Project

General

Profile

Download (149 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
 * shaper.inc
4
 *
5
 * part of pfSense (https://www.pfsense.org)
6
 * Copyright (c) 2004-2013 BSD Perimeter
7
 * Copyright (c) 2013-2016 Electric Sheep Fencing
8
 * Copyright (c) 2014-2022 Rubicon Communications, LLC (Netgate)
9
 * All rights reserved.
10
 *
11
 * originally based on m0n0wall (http://m0n0.ch/wall)
12
 * Copyright (c) 2003-2004 Manuel Kasper <mk@neon1.net>.
13
 * All rights reserved.
14
 *
15
 * Licensed under the Apache License, Version 2.0 (the "License");
16
 * you may not use this file except in compliance with the License.
17
 * You may obtain a copy of the License at
18
 *
19
 * http://www.apache.org/licenses/LICENSE-2.0
20
 *
21
 * Unless required by applicable law or agreed to in writing, software
22
 * distributed under the License is distributed on an "AS IS" BASIS,
23
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
24
 * See the License for the specific language governing permissions and
25
 * limitations under the License.
26
 */
27

    
28
/* XXX: needs some reducing on include. */
29
/* include all configuration functions. */
30
require_once("globals.inc");
31
require_once("functions.inc");
32
require_once("util.inc");
33
require_once("notices.inc");
34
include_once("interfaces.inc");//required for convert_real_interface_to_friendly_interface_name() in get_queue_stats() to get altq interface name.
35

    
36
// Try to ceil $value, otherwise return $default is unable to do so
37
function ceil2($value, $default = NULL)
38
{
39
    return (is_numeric($value) ? ceil($value) : $default);
40
}
41

    
42
/* Limiter patch */
43
// I need this crazy looking model/struct to specify various algos, ecn caps, and params.
44
// update as needed when dummynet changes
45

    
46
// List of schedulers ('type' command on scheduler/pipe) that dummynet/ipfw is supposed to support
47
function getSchedulers() {
48
	return array(
49
		"wf2q+" => array(
50
			"name" => "Worst-case Weighted fair Queueing (default)",
51
			"parameter_format" => "type wf2q+",
52
			"description" => "Worst-case Weighted fair Queueing (WF2Q+) schedules flows with an associated weight. ".
53
					 "WF2Q+ is the default algorithm used by previous versions.",
54
			"parameters" => array(),
55
			"ecn" => false
56
		),
57
		"fifo" => array(
58
			"name" => "FIFO",
59
			"parameter_format" => "type fifo",
60
			"description" => "First-in-First-out is a fundamental queueing discipline which ensures packet sequence. ".
61
					 "Dynamic queues are not supported with the FIFO scheduler (all packets are stored in a single queue).",
62
			"parameters" => array(),
63
			"ecn" => false
64
		),
65
		"qfq" => array(
66
			"name" => "Quick Fair Queueing",
67
			"parameter_format" => "type qfq",
68
			"description" => "QFQ is a fast, low-complexity Approximated Fair Queueing scheduler.",
69
			"parameters" => array(),
70
			"ecn" => false
71
		),
72
		"rr" => array(
73
			"name" => "Round Robin",
74
			"parameter_format" => "type rr",
75
			"description" => "Round Robin (RR) schedules packets in a round-robin fashion.",
76
			"parameters" => array(),
77
			"ecn" => false
78
		),
79
		"prio" => array(
80
			"name" => "PRIO",
81
			"parameter_format" => "type prio",
82
			"description" => "PRIO schedules packets by their corresponding priority.",
83
			"parameters" => array(),
84
			"ecn" => false
85
		),
86
		"fq_codel" => array(
87
			"name" => "FQ_CODEL",
88
			"parameter_format" => "type fq_codel target %sms interval %sms quantum %s limit %s flows %s",
89
			"description" => "CoDel is a novel \"no knobs\", \"just works\", \"handles variable bandwidth and RTT\", and simple AQM algorithm."
90
							. " As a scheduler, FQ_CODEL implements several flows (defined below), each having their own CoDel AQM.",
91
			"parameters" => array(
92
				"target" => array("name" => "Target Delay (ms)", "type" => "number", "default" => intval(get_single_sysctl("net.inet.ip.dummynet.fqcodel.target")) / 1000),
93
				"interval" => array("name" => "Interval (ms)", "type" => "number", "default" => intval(get_single_sysctl("net.inet.ip.dummynet.fqcodel.interval")) / 1000),
94
				"quantum" => array("name" => "quantum", "type" => "number", "default" => get_single_sysctl("net.inet.ip.dummynet.fqcodel.quantum")),
95
				"limit" => array("name" => "limit", "type" => "number", "default" => get_single_sysctl("net.inet.ip.dummynet.fqcodel.limit")),
96
				"flows" => array("name" => "flows", "type" => "number", "default" => get_single_sysctl("net.inet.ip.dummynet.fqcodel.flows"))
97
			),
98
			"ecn" => true
99
		),
100
		"fq_pie" => array(
101
			"name" => "FQ_PIE",
102
			"parameter_format" => "type fq_pie target %sms tupdate %sms alpha %s beta %s max_burst %s max_ecnth %s quantum %s limit %s flows %s",
103
			"description" => "Fair Queue Proportional Integral controller Enhanced AQM (FQ_PIE) is similar to FQ_CODEL but uses a slightly different algorithm.",
104
			"parameters" => array(
105
				"target" => array("name" => "Target Delay (ms)", "type" => "number", "default" => intval(get_single_sysctl("net.inet.ip.dummynet.fqpie.target")) / 1000),
106
				"tupdate" => array("name" => "Interval (ms)", "type" => "number", "default" => intval(get_single_sysctl("net.inet.ip.dummynet.fqpie.tupdate")) / 1000),
107
				"alpha" => array("name" => "alpha", "type" => "number step=any", "default" => intval(get_single_sysctl("net.inet.ip.dummynet.fqpie.alpha")) / 1000),
108
				"beta" => array("name" => "beta", "type" => "number step=any", "default" => intval(get_single_sysctl("net.inet.ip.dummynet.fqpie.beta")) / 1000),
109
				"max_burst" => array("name" => "max_burst", "type" => "number", "default" => intval(get_single_sysctl("net.inet.ip.dummynet.fqpie.max_burst")) / 1000),
110
				"max_ecnth" => array("name" => "max_ecnth", "type" => "number step=any", "default" => intval(get_single_sysctl("net.inet.ip.dummynet.fqpie.max_ecnth")) / 1000),
111
				"quantum" => array("name" => "quantum", "type" => "number", "default" => get_single_sysctl("net.inet.ip.dummynet.fqpie.quantum")),
112
				"limit" => array("name" => "limit", "type" => "number", "default" => get_single_sysctl("net.inet.ip.dummynet.fqpie.limit")),
113
				"flows" => array("name" => "flows", "type" => "number", "default" => get_single_sysctl("net.inet.ip.dummynet.fqpie.flows"))
114
			),
115
			"ecn" => true,
116
			"pie_onoff" => true,
117
			"pie_capdrop" => true,
118
			"pie_qdelay" => true,
119
			"pie_pderand" => true
120
		)
121
	);
122
}
123
// list of AQMs that dummynet supports
124
function getAQMs() {
125
	return array(
126
		"droptail" => array(
127
			"name" => "Tail Drop",
128
			"description" => "Tail Drop is a fundamental queue management algorithm which drops inbound packets once the queue is full.",
129
			"parameter_format" => "droptail",
130
			"parameters" => array(),
131
			"ecn" => false
132
		),
133
		"codel" => array(
134
			"name" => "CoDel",
135
			"description" => "CoDel is a novel \"no knobs\", \"just works\", \"handles variable bandwidth and RTT\", and simple AQM algorithm.",
136
			"parameter_format" => "codel target %sms interval %sms",
137
			"parameters" => array(
138
				"target" => array("name" => "Target Delay (ms)", "type" => "number", "default" => intval(get_single_sysctl("net.inet.ip.dummynet.codel.target")) / 1000),
139
				"interval" => array("name" => "Interval (ms)", "type" => "number", "default" => intval(get_single_sysctl("net.inet.ip.dummynet.codel.interval")) / 1000),
140
			),
141
			"ecn" => true
142
		),
143
		"pie" => array(
144
			"name" => "PIE",
145
			"description" => "Proportional Integral controller Enhanced AQM (PIE) is similar to CoDel but uses a slightly different algorithm.",
146
			"parameter_format" => "pie target %sms tupdate %sms alpha %s beta %s max_burst %s max_ecnth %s",
147
			"parameters" => array(
148
				"target" => array("name" => "Target Delay (ms)", "type" => "number", "default" => intval(get_single_sysctl("net.inet.ip.dummynet.fqpie.target")) / 1000),
149
				"tupdate" => array("name" => "Interval (ms)", "type" => "number", "default" => intval(get_single_sysctl("net.inet.ip.dummynet.fqpie.tupdate")) / 1000),
150
				"alpha" => array("name" => "alpha", "type" => "number step=any", "default" => intval(get_single_sysctl("net.inet.ip.dummynet.fqpie.alpha")) / 1000),
151
				"beta" => array("name" => "beta", "type" => "number step=any", "default" => intval(get_single_sysctl("net.inet.ip.dummynet.fqpie.beta")) / 1000),
152
				"max_burst" => array("name" => "max_burst", "type" => "number", "default" => intval(get_single_sysctl("net.inet.ip.dummynet.fqpie.max_burst")) / 1000),
153
				"max_ecnth" => array("name" => "max_ecnth", "type" => "number step=any", "default" => intval(get_single_sysctl("net.inet.ip.dummynet.fqpie.max_ecnth")) / 1000)
154
			),
155
			"ecn" => true,
156
			"pie_onoff" => true,
157
			"pie_capdrop" => true,
158
			"pie_qdelay" => true,
159
			"pie_pderand" => true
160
		),
161
		"red" => array(
162
			"name" => "Random Early Detection (RED)",
163
			"description" => "Random Early Detection (RED) drops packets based on probability, which increases as the queue increases in size.",
164
			"parameter_format" => "red %s/%s/%s/%s",
165
			"parameters" => array(
166
				"w_q" => array("name" => "w_q", "type" => "number", "default" => 1),
167
				"min_th" => array("name" => "min_th", "type" => "number", "default" => 0),
168
				"max_th" => array("name" => "max_th", "type" => "number", "default" => 1),
169
				"max_p" => array("name" => "max_p", "type" => "number", "default" => 1)
170
			),
171
			"ecn" => true
172
		),
173
		"gred" => array(
174
			"name" => "Gentle Random Early Detection (GRED)",
175
			"description" => "Gentle Random Early Detection (GRED) drops packets based on probability, which increases as the queue increases in size.",
176
			"parameter_format" => "gred %s/%s/%s/%s",
177
			"parameters" => array(
178
				"w_q" => array("name" => "w_q", "type" => "number", "default" => 1),
179
				"min_th" => array("name" => "min_th", "type" => "number", "default" => 0),
180
				"max_th" => array("name" => "max_th", "type" => "number", "default" => 1),
181
				"max_p" => array("name" => "max_p", "type" => "number", "default" => 1)
182
			),
183
			"ecn" => true
184
		)
185
	);
186
}
187
// used to map above
188
function array_map_assoc(callable $f, array $a) {
189
	return array_column(array_map($f, array_keys($a), $a), 1, 0);
190
}
191
function build_queue_params($array, $selected, $params, $id) {
192
	$form .= '<div id="params_' . $id . '">';
193

    
194
	$selectedAlgorithm = $array[$selected];
195

    
196
	if ($selectedAlgorithm) {
197
		$form .= '<span class="help-block">' . gettext($selectedAlgorithm['description']) . '</span><br/>';
198
	}
199

    
200
	$parameters = $selectedAlgorithm["parameters"];
201

    
202
	if (!$parameters || sizeof($parameters) <= 0) {
203
		if (!$selectedAlgorithm) {
204
			$form .= 'No parameters for selected algorithm.';
205
		} else {
206
			$form .= 'No parameters for ' . $selectedAlgorithm["name"] . '.';
207
		}
208
	} else {
209
		$form .= '<div class="table-responsive">';
210
		$form .= '<table id="maintable" class="table table-hover table-striped">';
211
		$form .= "<thead><tr>";
212
		$form .= "<th>Parameter</th>";
213
		$form .= "<th>Value</th>";
214
		$form .= "</tr></thead>";
215
		$form .= "<tbody>";
216

    
217
		foreach ($parameters as $key => $value) {
218
			$form .= '<tr>';
219

    
220
			// get current value, or default.
221
			$currentValue = $params[$key];
222
			// check if param key is set. using isset allows the param key to be zero.
223
			if (! isset($currentValue)) {
224
				$currentValue = $value["default"]; // default to default
225
			}
226

    
227
			$form .= "<td class=\"col-xs-4\">" . htmlspecialchars($key) . "</td>";
228
			$form .= "<td class=\"col-xs-8\"><input class=\"form-control\" type=" . gettext($value["type"])
229
					. " name=\"param_" . $selected . "_" . $key . "\" placeholder=\"" .
230
					htmlspecialchars($value["default"]) . "\" value=\"" . htmlspecialchars($currentValue) . "\"/></td>";
231

    
232
			$form .= '</tr>';
233
		}
234

    
235
		$form .= "</tbody></table></div><br />";
236
	}
237

    
238
	$form .= '</div>';
239
	$form .= '<script type="text/javascript">';
240
	$form .= 'events.push(function() {$("#' . $id . '").change(function() {$("#params_' .
241
		gettext($id) . '").html("Save this limiter to see algorithm parameters.");})});</script>';
242

    
243
	return($form);
244
}
245
function FormatParameters($format_string, $params) {
246
	return vsprintf($format_string, $params);
247
}
248
/* End limiter patch */
249

    
250
function get_queue_stats() {
251
	// due to sysutils\qstats not providing accurate stats with 'currently' valid numbers
252
	// in its current implementation this php function does the job for now..
253
	$result = array();
254
	$result['timestamp'] = gettimeofday(true);
255
	$r = exec("/sbin/pfctl -s queue -v", $output, $ret);
256
	$interfacestats = array();
257
	foreach($output as $line) {
258
		$partial = explode('{', $line);
259
		$items = explode(" ", $partial[0]);
260
		if (isset($partial[1])) {
261
			$contains = explode(", ", substr($partial[1], 0, strlen($partial[1]) - 1));
262
		} else {
263
			unset($contains);
264
		}
265
		if ($items[0] == "queue") {
266
			$level = 1;
267
			while (empty($items[$level])) {
268
				$level++;
269
			}
270
			unset($newqueue);
271
			$newqueue = array();
272
			$currentqueuename = $items[$level];
273
			$currentqueueif = $items[$level+2];
274
			$newqueue['name'] = $currentqueuename;
275
			$newqueue['interface'] = $items[$level+2];
276

    
277
			if ($items[$level+3] == "bandwidth") {
278
				$level += 2;
279
			}
280
			if ($items[$level+3] == "priority") {
281
				$level += 2;
282
			}
283
			if ($items[$level+3] == "qlimit") {
284
				$level += 2;
285
			}
286
			$tmp = $items[$level+3];
287
			if (substr($tmp,strlen($tmp)-1) == "(") {
288
				$newqueue['shapertype'] = substr($tmp, 0, strlen($tmp)-1);
289
			}
290

    
291
			$interfacestats[$currentqueueif][$currentqueuename] = &$newqueue;
292
			if (is_array($contains)) {
293
				$newqueue['contains'] = $contains;
294
			}
295
		} elseif ($items[0] == "altq") {
296
			unset($newqueue);
297
			$newqueue = array();
298
			$currentqueuename = convert_real_interface_to_friendly_interface_name($items[2]);
299
			$currentqueueif = $items[2];
300
			$newqueue['name'] = $currentqueuename;
301
			$newqueue['interface'] = $items[2];
302
			$interfacestats[$currentqueueif][$currentqueuename] = &$newqueue;
303
		} else {
304
			if ($items[3] == "pkts:") {
305
				preg_match('/pkts:\s+(\d+)\s+bytes:\s+(\d+)\s+dropped pkts:\s+(\d+)\s+bytes:\s+(\d+)/', $line, $matches);
306
				$newqueue["pkts"] = $matches[1];
307
				$newqueue["bytes"] = $matches[2];
308
				$newqueue["droppedpkts"] = $matches[3];
309
				$newqueue["droppedbytes"] = $matches[4];
310
			}
311
			if ($items[3] == "qlength:") {
312
				$subitems = explode("/", substr($line, 13, 8));
313
				$newqueue["qlengthitems"] = trim($subitems[0]);
314
				$newqueue["qlengthsize"] = trim($subitems[1]);
315
				if (substr($line, 22, 8) == "borrows:") {
316
					$newqueue["borrows"] = trim(substr($line, 31, 7));
317
				}
318
				if (substr($line, 39, 9) == "suspends:") {
319
					$newqueue["suspends"] = trim(substr($line, 49, 7));
320
				}
321
			}
322
		}
323
	}
324
	$result['interfacestats'] = $interfacestats;
325
	return $result;
326
}
327

    
328
/*
329
 * I admit :) this is derived from xmlparse.inc StartElement()
330
 */
331
function &get_reference_to_me_in_config(&$mypath) {
332
	global $config;
333

    
334
	init_config_arr(array('shaper'));
335
	$ptr = &$config['shaper'];
336
	foreach ($mypath as $indeks) {
337
		if (!is_array($ptr)) {
338
			$ptr = array();
339
		}
340
		if (!is_array($ptr['queue'])) {
341
			$ptr['queue'] = array();
342
		}
343
		if (!is_array($ptr['queue'][$indeks])) {
344
			$ptr['queue'][$indeks] = array();
345
		}
346
		$ptr = &$ptr['queue'][$indeks];
347
	}
348

    
349
	return $ptr;
350
}
351

    
352
function unset_object_by_reference(&$mypath) {
353
	global $config;
354

    
355
	init_config_arr(array('shaper'));
356
	$ptr = &$config['shaper'];
357
	for ($i = 0; $i < count($mypath) - 1; $i++) {
358
		$ptr = &$ptr['queue'][$mypath[$i]];
359
	}
360
	unset($ptr['queue'][$mypath[$i]]);
361
}
362

    
363
function &get_dn_reference_to_me_in_config(&$mypath) {
364
	global $config;
365

    
366
	init_config_arr(array('dnshaper'));
367
	$ptr = &$config['dnshaper'];
368
	foreach ($mypath as $indeks) {
369
		$ptr = &$ptr['queue'][$indeks];
370
	}
371

    
372
	return $ptr;
373
}
374

    
375
function unset_dn_object_by_reference(&$mypath) {
376
	global $config;
377

    
378
	init_config_arr(array('dnshaper'));
379
	$ptr = &$config['dnshaper'];
380
	for ($i = 0; $i < count($mypath) - 1; $i++) {
381
		$ptr = &$ptr['queue'][$mypath[$i]];
382
	}
383
	unset($ptr['queue'][$mypath[$i]]);
384
}
385

    
386
function clean_child_queues($type, $mypath) {
387
	$ref = &get_reference_to_me_in_config($mypath);
388

    
389
	switch ($type) {
390
		case 'HFSC':
391
			if (isset($ref['borrow'])) {
392
				unset($ref['borrow']);
393
			}
394
			if (isset($ref['hogs'])) {
395
				unset($ref['hogs']);
396
			}
397
			if (isset($ref['buckets'])) {
398
				unset($ref['buckets']);
399
			}
400
			break;
401
		case 'PRIQ':
402
			if (isset($ref['borrow'])) {
403
				unset($ref['borrow']);
404
			}
405
			if (isset($ref['bandwidth'])) {
406
				unset($ref['bandwidth']);
407
			}
408
			if (isset($ref['bandwidthtype'])) {
409
				unset($ref['bandwidthtype']);
410
			}
411
			/* fall through */
412
		case 'FAIRQ':
413
			if (isset($ref['borrow'])) {
414
				unset($ref['borrow']);
415
			}
416
			/* fall through */
417
		case 'CBQ':
418
			if (isset($ref['realtime'])) {
419
				unset($ref['realtime']);
420
			}
421
			if (isset($ref['realtime1'])) {
422
				unset($ref['realtime1']);
423
			}
424
			if (isset($ref['realtime2'])) {
425
				unset($ref['realtime2']);
426
			}
427
			if (isset($ref['realtime3'])) {
428
				unset($ref['realtime3']);
429
			}
430
			if (isset($ref['upperlimit'])) {
431
				unset($ref['upperlimit']);
432
			}
433
			if (isset($ref['upperlimit1'])) {
434
				unset($ref['upperlimit1']);
435
			}
436
			if (isset($ref['upperlimit2'])) {
437
				unset($ref['upperlimit2']);
438
			}
439
			if (isset($ref['upperlimit3'])) {
440
				unset($ref['upperlimit3']);
441
			}
442
			if (isset($ref['linkshare'])) {
443
				unset($ref['linkshare']);
444
			}
445
			if (isset($ref['linkshare1'])) {
446
				unset($ref['linkshare1']);
447
			}
448
			if (isset($ref['linkshare2'])) {
449
				unset($ref['linkshare2']);
450
			}
451
			if (isset($ref['linkshare3'])) {
452
				unset($ref['linkshare3']);
453
			}
454
			if (isset($ref['hogs'])) {
455
				unset($ref['hogs']);
456
			}
457
			if (isset($ref['buckets'])) {
458
				unset($ref['buckets']);
459
			}
460
			break;
461
	}
462
}
463

    
464
function get_bandwidthtype_scale($type) {
465
	switch ($type) {
466
		case "Gb":
467
			$factor = 1024 * 1024 * 1024;
468
			break;
469
		case "Mb":
470
			$factor = 1024 * 1024;
471
			break;
472
		case "Kb":
473
			$factor = 1024;
474
			break;
475
		case "b":
476
		default:
477
			$factor = 1;
478
			break;
479
	}
480
	return intval($factor);
481
}
482

    
483
function get_bandwidth($bw, $scale, $obj) {
484
	$bw = (int) $bw;
485
	$pattern= "/(b|Kb|Mb|Gb|%)/";
486
	if (!preg_match($pattern, $scale, $match))
487
		return 0;
488

    
489
	switch ($match[1]) {
490
		case '%':
491
			$objbw = ($bw / 100) * get_queue_bandwidth($obj);
492
			break;
493
		default:
494
			$objbw = $bw * get_bandwidthtype_scale($scale);
495
			break;
496
	}
497

    
498
	return floatval($objbw);
499
}
500

    
501
/*
502
 * XXX - unused
503
 *
504
function get_hfsc_bandwidth($object, $bw) {
505
	$pattern= "/[0-9]+/";
506
	if (preg_match($pattern, $bw, $match)) {
507
		$bw_1 = $match[1];
508
	} else {
509
		return 0;
510
	}
511
	$pattern= "/(b|Kb|Mb|Gb|%)/";
512
	if (preg_match($pattern, $bw, $match)) {
513
		switch ($match[1]) {
514
			case '%':
515
				$bw_1 = ($bw_1 / 100) * get_interface_bandwidth($object);
516
				break;
517
			default:
518
				$bw_1 = $bw_1 * get_bandwidthtype_scale($match[0]);
519
				break;
520
		}
521
		return floatval($bw_1);
522
	} else {
523
		return 0;
524
	}
525
}
526
*/
527

    
528
function get_queue_bandwidth($obj) {
529
	$bw = (int)$obj->GetBandwidth();
530
	$scale = $obj->GetBwscale();
531

    
532
	$pattern= "/(b|Kb|Mb|Gb|%)/";
533
	if (!preg_match($pattern, $scale, $match))
534
		return 0;
535

    
536
	switch ($match[1]) {
537
		case '%':
538
			if (method_exists($obj, 'GetParent')) {
539
				$getobjbw = get_queue_bandwidth($obj->GetParent());
540
			} else {
541
				$getobjbw = $obj->bandwidth;
542
			}
543
			$objbw = ($bw / 100) * $getobjbw;
544
			break;
545
		default:
546
			$objbw = $bw * get_bandwidthtype_scale($scale);
547
			break;
548
	}
549

    
550
	return floatval($objbw);
551
}
552

    
553
function get_interface_bandwidth($object) {
554
	global $altq_list_queues;
555

    
556
	$int = $object->GetInterface();
557
	if (isset($altq_list_queues[$int])) {
558
		$altq = &$altq_list_queues[$int];
559
		$bw_3 = (int)$altq->GetBandwidth();
560
		$bw_3 = $bw_3 * get_bandwidthtype_scale($altq->GetBwscale());
561
		return floatval($bw_3);
562
	} else {
563
		return 0;
564
	}
565
}
566

    
567
/*
568
 * This is duplicated here since we cannot include guiconfig.inc.
569
 * Including it makes all stuff break.
570
 */
571
function shaper_do_input_validation($postdata, $reqdfields, $reqdfieldsn, $input_errors) {
572

    
573
	/* check for bad control characters */
574
	foreach ($postdata as $pn => $pd) {
575
		if (is_string($pd) && preg_match("/[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f]/", $pd)) {
576
			$input_errors[] = sprintf(gettext("The field '%s' contains invalid characters."), $pn);
577
		}
578
	}
579

    
580
	for ($i = 0; $i < count($reqdfields); $i++) {
581
		if ($postdata[$reqdfields[$i]] == "") {
582
			$input_errors[] = sprintf(gettext("The field '%s' is required."), $reqdfieldsn[$i]);
583
		}
584
	}
585
}
586

    
587
function cleanup_queue_from_rules($queue) {
588
	global $config;
589

    
590
	foreach ($config['filter']['rule'] as & $rule) {
591
		if ($rule['defaultqueue'] == $queue) {
592
			unset($rule['defaultqueue']);
593
			if (isset($rule['ackqueue'])) {
594
				unset($rule['ackqueue']);
595
			}
596
		}
597
		if ($rule['ackqueue'] == $queue) {
598
			unset($rule['ackqueue']);
599
		}
600
	}
601
}
602

    
603
function cleanup_dnqueue_from_rules($queue) {
604
	global $config;
605

    
606
	foreach ($config['filter']['rule'] as & $rule) {
607
		if ($rule['dnpipe'] == $queue) {
608
			unset($rule['dnpipe']);
609
		}
610
		if ($rule['pdnpipe'] == $queue) {
611
			unset($rule['pdnpipe']);
612
		}
613
	}
614
}
615

    
616
function rename_queue_in_rules($name, $newname) {
617
	global $config;
618

    
619
	foreach ($config['filter']['rule'] as & $rule) {
620
		if ($rule['defaultqueue'] == $name) {
621
			$rule['defaultqueue'] = $newname;
622
		}
623
		if ($rule['ackqueue'] == $name) {
624
			$rule['ackqueue'] = $newname;
625
		}
626
	}
627
}
628

    
629
function rename_dnqueue_in_rules($name, $newname) {
630
	global $config;
631

    
632
	foreach ($config['filter']['rule'] as & $rule) {
633
		if ($rule['dnpipe'] == $name) {
634
			$rule['dnpipe'] = $newname;
635
		}
636
		if ($rule['pdnpipe'] == $name) {
637
			$rule['pdnpipe'] = $newname;
638
		}
639
	}
640
}
641

    
642
class altq_root_queue {
643
	var $interface;
644
	var $tbrconfig ;
645
	var $bandwidth;
646
	var $bandwidthtype; /* b, Kb, Mb, Gb, % */
647
	var $scheduler;
648
	var $qlimit;
649
	var $queues = array();
650
	var $qenabled = false;
651
	var $link;
652

    
653
	/* Accessor functions */
654
	function GetDefaultQueuePresent() {
655
		if (!empty($this->queues)) {
656
			foreach ($this->queues as $q) {
657
				if ($q->GetDefault()) {
658
					return true;
659
				}
660
			}
661
		}
662

    
663
		return false;
664
	}
665
	function SetLink($link) {
666
		$this->link = $link;
667
	}
668
	function GetLink() {
669
		return $this->link;
670
	}
671
	function GetEnabled() {
672
		return $this->qenabled;
673
	}
674
	function SetEnabled($value) {
675
		$this->qenabled = $value;
676
	}
677
	function CanHaveChildren() {
678
		if ($this->GetScheduler() == "CODELQ") {
679
			return false;
680
		} else {
681
			return true;
682
		}
683
	}
684
	function CanBeDeleted() {
685
		return false;
686
	}
687
	function GetQname() {
688
		return $this->interface;
689
	}
690
	function SetQname($name) {
691
		$this->interface = trim($name);
692
	}
693
	function GetInterface() {
694
		return $this->interface;
695
	}
696
	function SetInterface($name) {
697
		$this->interface = trim($name);
698
	}
699
	function GetTbrConfig() {
700
		return $this->tbrconfig;
701
	}
702
	function SetTbrConfig($tbrconfig) {
703
		$this->tbrconfig = $tbrconfig;
704
	}
705
	function GetBandwidth() {
706
		return $this->bandwidth;
707
	}
708
	function SetBandwidth($bw) {
709
		$this->bandwidth = $bw;
710
	}
711
	function GetBwscale() {
712
		return $this->bandwidthtype;
713
	}
714
	function FormGetBwscale() {
715
		if ($this->GetBwscale()) {
716
			$bwscale = $this->GetBwscale();
717
		} else {
718
			$bwscale = 'Mb';
719
		}
720
		return $bwscale;
721
	}
722
	function SetBwscale($bwscale) {
723
		$this->bandwidthtype = $bwscale;
724
	}
725
	function GetScheduler() {
726
		return $this->scheduler;
727
	}
728
	function SetScheduler($scheduler) {
729
		$this->scheduler = trim($scheduler);
730
	}
731
	function GetQlimit() {
732
		return $this->qlimit;
733
	}
734
	function SetQlimit($limit) {
735
		$this->qlimit = $limit;
736
	}
737

    
738
	function GetBwscaleText() {
739
		switch ($this->bandwidthtype) {
740
			case "b":
741
				$bwscaletext = "Bit/s";
742
				break;
743
			case "Kb":
744
				$bwscaletext = "Kbit/s";
745
				break;
746
			case "Mb":
747
				$bwscaletext = "Mbit/s";
748
				break;
749
			case "Gb":
750
				$bwscaletext = "Gbit/s";
751
				break;
752
			default:
753
				/* For others that do not need translating like % */
754
				$bwscaletext = $this->bandwidthtype;
755
				break;
756
		}
757
		return $bwscaletext;
758
	}
759

    
760
	function CheckBandwidth($bw, $bwtype) {
761
		$bw = (int)$bw;
762
		$sum = $this->GetTotalBw();
763
		if ($sum > ($bw * get_bandwidthtype_scale($bwtype))) {
764
			return 1;
765
		}
766
		foreach ($this->queues as $q) {
767
			if ($q->CheckBandwidth(0, '')) {
768
				return 1;
769
			}
770
		}
771

    
772
		return 0;
773
	}
774

    
775
	function GetTotalBw($qignore = NULL) {
776
		$sum = 0;
777
		foreach ($this->queues as $q) {
778
			if (($qignore != NULL) && ($qignore == $q)) {
779
				continue;
780
			}
781
			$sum += get_bandwidth($q->GetBandwidth(), $q->GetBwscale(), $this);
782
		}
783

    
784
		return $sum;
785
	}
786

    
787
	function validate_input($data, &$input_errors) {
788

    
789
		$reqdfields[] = "bandwidth";
790
		$reqdfieldsn[] = gettext("Bandwidth");
791
		$reqdfields[] = "bandwidthtype";
792
		$reqdfieldsn[] = gettext("Bandwidthtype");
793

    
794
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
795

    
796
		if ($data['bandwidth'] == null) {
797
			$input_errors[] = gettext("Bandwidth must be set.  This is usually the interface speed.");
798
		} else {
799
			if ((!is_numeric($data['bandwidth']))) {
800
				$input_errors[] = gettext("Bandwidth must be an integer.");
801
			}
802
			if ((int)$data['bandwidth'] < 0) {
803
				$input_errors[] = gettext("Bandwidth cannot be negative.");
804
			}
805
		}
806
		if ($data['bandwidthtype'] == "%") {
807
			if ($data['bandwidth'] > 100 || $data['bandwidth'] < 0) {
808
				$input_errors[] = gettext("Bandwidth in percentage should be between 1 and 100.");
809
			}
810
		}
811
		if ($this->CheckBandwidth($data['bandwidth'], $data['bandwidthtype'])) {
812
			$input_errors[] = "The sum of child bandwidth is higher than parent.";
813
		}
814
		if (($data['qlimit'] != null) && ($data['scheduler'] == 'CODELQ')) {
815
			$input_errors[] = gettext("CODELQ scheduler doesn't support Qlimit parameter.");
816
		}
817
		if (($data['qlimit'] != null) && (!is_numeric($data['qlimit']))) {
818
			$input_errors[] = gettext("Qlimit must be an integer.");
819
		}
820
		if (($data['qlimit'] != null) && (int)$data['qlimit'] < 1) {
821
			$input_errors[] = gettext("Qlimit must be positive.");
822
		}
823
		if (($data['tbrconfig'] != null) && (!is_numeric($data['tbrconfig']))) {
824
			$input_errors[] = gettext("Tbrsize must be an integer.");
825
		}
826
		if (($data['tbrconfig'] != null) && (int)$data['tbrconfig'] < 1) {
827
			$input_errors[] = gettext("Tbrsize must be positive.");
828
		}
829
	}
830

    
831
	/* Implement this to shorten some code on the frontend page */
832
	function ReadConfig(&$conf) {
833
		if (isset($conf['tbrconfig'])) {
834
			$this->SetTbrConfig($conf['tbrconfig']);
835
		} else {
836
			$this->SetTbrConfig($conf['tbrconfig']);
837
		}
838
		$this->SetBandwidth($conf['bandwidth']);
839
		if ($conf['bandwidthtype'] <> "") {
840
			$this->SetBwscale($conf['bandwidthtype']);
841
		}
842
		if (isset($conf['scheduler'])) {
843
			if ($this->GetScheduler() != $conf['scheduler']) {
844
				foreach ($this->queues as $q) {
845
					clean_child_queues($conf['scheduler'], $this->GetLink());
846
					$q->clean_queue($conf['scheduler']);
847
				}
848
			}
849
			$this->SetScheduler($conf['scheduler']);
850
		}
851
		if (isset($conf['qlimit']) && $conf['qlimit'] <> "") {
852
			$this->SetQlimit($conf['qlimit']);
853
		} else {
854
			$this->SetQlimit("");
855
		}
856
		if (isset($conf['name'])) {
857
			$this->SetQname($conf['name']);
858
		}
859
		if (!empty($conf['enabled'])) {
860
			$this->SetEnabled($conf['enabled']);
861
		} else {
862
			$this->SetEnabled("");
863
		}
864
	}
865

    
866
	function copy_queue($interface, &$cflink) {
867
		$cflink['interface'] = $interface;
868
		$cflink['name'] = $interface;
869
		$cflink['scheduler'] = $this->GetScheduler();
870
		$cflink['bandwidth'] = $this->GetBandwidth();
871
		$cflink['bandwidthtype'] = $this->GetBwscale();
872
		$cflink['qlimit'] = $this->GetQlimit();
873
		$cflink['tbrconfig'] = $this->GetTbrConfig();
874
		$cflink['enabled'] = $this->GetEnabled();
875
		if (is_array($this->queues)) {
876
			$cflink['queue'] = array();
877
			foreach ($this->queues as $q) {
878
				$cflink['queue'][$q->GetQname()] = array();
879
				$q->copy_queue($interface, $cflink['queue'][$q->GetQname()]);
880
			}
881
		}
882
	}
883

    
884
	function &get_queue_list(&$q = null) {
885
		$qlist = array();
886

    
887
		//$qlist[$this->GetQname()] = & $this;
888
		if (is_array($this->queues)) {
889
			foreach ($this->queues as $queue) {
890
				$queue->get_queue_list($qlist);
891
			}
892
		}
893
		return $qlist;
894
	}
895

    
896
	function &add_queue($interface, &$queue, &$path, &$input_errors) {
897

    
898
		if (!is_array($this->queues)) {
899
			$this->queues = array();
900
		}
901

    
902
		switch ($this->GetScheduler()) {
903
			case "PRIQ":
904
				$__tmp_q = new priq_queue(); $q =& $__tmp_q;
905
				break;
906
			case "HFSC":
907
				$__tmp_q = new hfsc_queue(); $q =& $__tmp_q;
908
				break;
909
			case "CBQ":
910
				$__tmp_q = new cbq_queue(); $q =& $__tmp_q;
911
				break;
912
			case "FAIRQ":
913
				$__tmp_q = new fairq_queue(); $q =& $__tmp_q;
914
				break;
915
			default:
916
				/* XXX: but should not happen anyway */
917
				return;
918
				break;
919
		}
920
		$q->SetLink($path);
921
		$q->SetInterface($this->GetInterface());
922
		$q->SetEnabled("on");
923
		$q->SetParent($this);
924
		$q->ReadConfig($queue);
925
		$q->validate_input($queue, $input_errors);
926

    
927
		$this->queues[$q->GetQname()] = &$q;
928
		ref_on_altq_queue_list($this->GetQname(), $q->GetQname());
929
		if (is_array($queue['queue'])) {
930
			foreach ($queue['queue'] as $key1 => $que) {
931
				array_push($path, $key1);
932
				$q->add_queue($q->GetInterface(), $que, $path, $input_errors);
933
				array_pop($path);
934
			}
935
		}
936

    
937
		return $q;
938
	}
939

    
940
	/* interface here might be optional */
941
	function &find_queue($interface, $qname) {
942
		if ($qname == $this->GetQname()) {
943
			return $this;
944
		}
945
		foreach ($this->queues as $q) {
946
			$result =& $q->find_queue("", $qname);
947
			if ($result) {
948
				return $result;
949
			}
950
		}
951
	}
952

    
953
	function &find_parentqueue($interface, $qname) {
954
		if ($qname == $interface) {
955
			$result = NULL;
956
		} else if ($this->queues[$qname]) {
957
			$result = $this;
958
		} else if ($this->GetScheduler() <> "PRIQ") {
959
			foreach ($this->queues as $q) {
960
				$result = $q->find_parentqueue("", $qname);
961
				if ($result) {
962
					return $result;
963
				}
964
			}
965
		}
966
	}
967

    
968
	function build_tree() {
969
		global $shaperIFlist;
970

    
971
		$tree = " <li><a href=\"firewall_shaper.php?interface=".$this->GetInterface()."&amp;queue=". $this->GetInterface()."&amp;action=show";
972
		$tree .= "\">" . $shaperIFlist[$this->GetInterface()] . "</a>";
973
		if (is_array($this->queues)) {
974
			$tree .= "<ul>";
975
			foreach ($this->queues as $q) {
976
				$tree .= $q->build_tree();
977
			}
978
			$tree .= "</ul>";
979
		}
980
		$tree .= "</li>";
981
		return $tree;
982
	}
983

    
984
	function delete_queue() {
985
		foreach ($this->queues as $q) {
986
			$q->delete_queue();
987
		}
988
		unset_object_by_reference($this->GetLink());
989
	}
990

    
991
	function delete_all() {
992
		if (count($this->queues)) {
993
			foreach ($this->queues as $q) {
994
				$q->delete_all();
995
				unset_object_by_reference($q->GetLink());
996
				unset($q);
997
			}
998
			unset($this->queues);
999
		}
1000
	}
1001

    
1002
	/*
1003
	 * First it spits:
1004
	 * altq on $interface ..............
1005
	 *	then it goes like
1006
	 *	foreach ($queues as $qkey => $queue) {
1007
	 *		this->queues[$qkey]->build_rule();
1008
	 *	}
1009
	 */
1010
	function build_rules(&$default = false) {
1011
		if (count($this->queues) > 0 && $this->GetEnabled() == "on") {
1012
			$default = false;
1013
			$rules = " altq on " . get_real_interface($this->GetInterface());
1014
			if ($this->GetScheduler()) {
1015
				$rules .= " ".strtolower($this->GetScheduler());
1016
			}
1017
			if ($this->GetQlimit() > 0) {
1018
				$rules .= " qlimit " . $this->GetQlimit() . " ";
1019
			}
1020
			if ($this->GetBandwidth()) {
1021
				$rules .= " bandwidth ".trim($this->GetBandwidth());
1022
				if ($this->GetBwscale()) {
1023
					$rules .= $this->GetBwscale();
1024
				}
1025
			}
1026
			if ($this->GetTbrConfig()) {
1027
				$rules .= " tbrsize ".$this->GetTbrConfig();
1028
			}
1029
			if (count($this->queues)) {
1030
				$i = count($this->queues);
1031
				$rules .= " queue { ";
1032
				foreach ($this->queues as $qkey => $qnone) {
1033
					if ($i > 1) {
1034
						$i--;
1035
						$rules .= " {$qkey}, ";
1036
					} else {
1037
						$rules .= " {$qkey} ";
1038
					}
1039
				}
1040
				$rules .= " } \n";
1041
				foreach ($this->queues as $q) {
1042
					$rules .= $q->build_rules($default);
1043
				}
1044
			}
1045

    
1046
			if ($default == false) {
1047
				$error = sprintf(gettext("SHAPER: no default queue specified for interface %s."), $this->GetInterface()) . " " . gettext("The interface queue will be enforced as default.");
1048
				file_notice("Shaper", $error, "Error occurred", "");
1049
				unset($error);
1050
				return "\n";
1051
			}
1052
			$frule .= $rules;
1053
		} else if ($this->GetEnabled() == "on" && $this->GetScheduler() == "CODELQ") {
1054
			$rules = " altq on " . get_real_interface($this->GetInterface());
1055
			if ($this->GetScheduler()) {
1056
				$rules .= " ".strtolower($this->GetScheduler());
1057
			}
1058
			if ($this->GetQlimit() > 0) {
1059
				$rules .= " ( qlimit " . $this->GetQlimit() . " ) ";
1060
			}
1061
			if ($this->GetBandwidth()) {
1062
				$rules .= " bandwidth ".trim($this->GetBandwidth());
1063
				if ($this->GetBwscale()) {
1064
					$rules .= $this->GetBwscale();
1065
				}
1066
			}
1067
			if ($this->GetTbrConfig()) {
1068
				$rules .= " tbrsize ".$this->GetTbrConfig();
1069
			}
1070

    
1071
			$rules .= " queue";
1072
		}
1073

    
1074
		$rules .= " \n";
1075
		return $rules;
1076
	}
1077

    
1078
	function build_javascript() {
1079
		$javascript = "<script type=\"text/javascript\">";
1080
		$javascript .= "//<![CDATA[\n";
1081
		$javascript .= "function mySuspend() {";
1082
		$javascript .= "if (document.layers && document.layers['shaperarea'] != null) ";
1083
		$javascript .= "document.layers['shaperarea'].visibility = 'hidden'; ";
1084
		$javascript .= "else if (document.all)";
1085
		$javascript .= "document.all['shaperarea'].style.visibility = 'hidden';";
1086
		$javascript .= "}";
1087

    
1088
		$javascript .= "function myResume() {";
1089
		$javascript .= "if (document.layers && document.layers['shaperarea'] != null) ";
1090
		$javascript .= "document.layers['shaperarea'].visibility = 'visible';";
1091
		$javascript .= "else if (document.all) ";
1092
		$javascript .= "document.all['shaperarea'].style.visibility = 'visible';";
1093
		$javascript .= "}";
1094
		$javascript .= "//]]>";
1095
		$javascript .= "</script>";
1096

    
1097
		return $javascript;
1098
	}
1099

    
1100
	function build_shortform() {
1101
		global $g;
1102

    
1103
		$altq =& $this;
1104

    
1105
		if ($altq) {
1106
			$scheduler = ": " . $altq->GetScheduler();
1107
		}
1108

    
1109
		$form = '<dl class="dl-horizontal">';
1110
		$form .= '	<dt>';
1111
		$form .= '		<a href="firewall_shaper.php?interface=' . $this->GetInterface() . '&amp;queue=' . $this->GetQname() . '&amp;action=show">' . $shaperIFlist[$this->GetInterface()] . '</a>';
1112
		$form .= '	</dt>';
1113
		$form .= '	<dd>';
1114
		$form .=		$scheduler;
1115
		$form .= '	</dd>';
1116

    
1117
		$form .= '	<dt>';
1118
		$form .=		'Bandwidth';
1119
		$form .= '	</dt>';
1120
		$form .= '	<dd>';
1121
		$form .=		$this->GetBandwidth() . '&nbsp;' . $this->GetBwscaleText();
1122
		$form .= '	</dd>';
1123

    
1124
		$form .= '	<dt>';
1125
		$form .= 'Disable';
1126
		$form .= '	<dt>';
1127
		$form .= '	<dd>';
1128

    
1129
		$form .= '<a class="btn btn-danger btn-xs" href="firewall_shaper_queues.php?interface=';
1130
		$form .= $this->GetInterface() . '&amp;queue=';
1131
		$form .= $this->GetQname() . '&amp;action=delete">';
1132
		$form .= '<i class="fa fa-trash icon-embed-btn"></i>';
1133
		$form .= gettext("Remove shaper from this interface") . '</a>';
1134

    
1135
		$form .= '	</dd>';
1136

    
1137
		$form .= '</dl>';
1138

    
1139
		return $form;
1140

    
1141
	}
1142

    
1143
	/*
1144
	 * For requesting the parameters of the root queues
1145
	 * to the user like the traffic wizard does.
1146
	 */
1147
	function build_form() {
1148

    
1149
		$sform = new Form();
1150

    
1151
		$sform->setAction("firewall_shaper.php");
1152

    
1153
		$section = new Form_Section(null);
1154

    
1155
		$section->addInput(new Form_Checkbox(
1156
			'enabled',
1157
			'Enable/Disable',
1158
			'Enable/disable discipline and its children',
1159
			($this->GetEnabled() == "on"),
1160
			'on'
1161
		));
1162

    
1163
		$section->addInput(new Form_StaticText(
1164
			'*Name',
1165
			$this->GetQname()
1166
		));
1167

    
1168
		$section->addInput(new Form_Select(
1169
			'scheduler',
1170
			'Scheduler Type',
1171
			$this->GetScheduler(),
1172
			array('HFSC' => 'HFSC',
1173
				  'CBQ' => 'CBQ',
1174
				  'FAIRQ' => 'FAIRQ',
1175
				  'CODELQ' => 'CODELQ',
1176
				  'PRIQ' => 'PRIQ')
1177
		))->setHelp('Changing this changes all child queues! Beware information can be lost.');
1178

    
1179
		$group = new Form_group('Bandwidth');
1180

    
1181
		$group->add(new Form_Input(
1182
			'bandwidth',
1183
			null,
1184
			'number',
1185
			$this->GetBandwidth()
1186
		));
1187

    
1188
		$group->add(new Form_Select(
1189
			'bandwidthtype',
1190
			null,
1191
			$this->FormGetBwscale(),
1192
			array('Kb' => 'Kbit/s',
1193
				  'Mb' => 'Mbit/s',
1194
				  'Gb' => 'Gbit/s',
1195
				  'b' => 'Bit/s',
1196
				  '%' => '%')
1197
		));
1198

    
1199
		$section->add($group);
1200

    
1201
		$section->addInput(new Form_Input(
1202
			'qlimit',
1203
			'Queue Limit',
1204
			'number',
1205
			$this->GetQlimit()
1206
		));
1207

    
1208
		$section->addInput(new Form_Input(
1209
			'tbrconfig',
1210
			'TBR Size',
1211
			'number',
1212
			$this->GetTbrConfig()
1213
		))->setHelp('Adjusts the size, in bytes, of the token bucket regulator. If not specified, heuristics based on the interface ' .
1214
					'bandwidth are used to determine the size.');
1215

    
1216
		$section->addInput(new Form_Input(
1217
			'interface',
1218
			null,
1219
			'hidden',
1220
			$this->GetInterface()
1221
		));
1222

    
1223
		$section->addInput(new Form_Input(
1224
			'name',
1225
			null,
1226
			'hidden',
1227
			$this->GetQname()
1228
		));
1229

    
1230
		$sform->add($section);
1231

    
1232
		return($sform);
1233
	}
1234

    
1235
	function update_altq_queue_data(&$data) {
1236
		$this->ReadConfig($data);
1237
	}
1238

    
1239
	/*
1240
	 * Should call on each of it queues and subqueues
1241
	 * the same function much like build_rules();
1242
	 */
1243
	function wconfig() {
1244
		$cflink = &get_reference_to_me_in_config($this->GetLink());
1245
		if (!is_array($cflink)) {
1246
			$cflink = array();
1247
		}
1248
		$cflink['interface'] = $this->GetInterface();
1249
		$cflink['name'] = $this->GetQname();
1250
		$cflink['scheduler'] = $this->GetScheduler();
1251
		$cflink['bandwidth'] = $this->GetBandwidth();
1252
		$cflink['bandwidthtype'] = $this->GetBwscale();
1253
		$cflink['qlimit'] = trim($this->GetQlimit());
1254
		if (empty($cflink['qlimit'])) {
1255
			unset($cflink['qlimit']);
1256
		}
1257
		$cflink['tbrconfig'] = trim($this->GetTbrConfig());
1258
		if (empty($cflink['tbrconfig'])) {
1259
			unset($cflink['tbrconfig']);
1260
		}
1261
		$cflink['enabled'] = $this->GetEnabled();
1262
		if (empty($cflink['enabled'])) {
1263
			unset($cflink['enabled']);
1264
		}
1265
	}
1266

    
1267
}
1268

    
1269
class priq_queue {
1270
	var $qname;
1271
	var $qinterface;
1272
	var $qlimit;
1273
	var $qpriority;
1274
	var $description;
1275
	var $isparent;
1276
	var $qbandwidth;
1277
	var $qbandwidthtype;
1278
	var $qdefault = "";
1279
	var $qrio = "";
1280
	var $qred = "";
1281
	var $qcodel = "";
1282
	var $qecn = "";
1283
	var $qack;
1284
	var $qenabled = "";
1285
	var $qparent;
1286
	var $link;
1287

    
1288
	/* This is here to help with form building and building rules/lists */
1289
	var $subqueues = array();
1290

    
1291
	/* Accessor functions */
1292
	function SetLink($link) {
1293
		$this->link = $link;
1294
	}
1295
	function GetLink() {
1296
		return $this->link;
1297
	}
1298
	function &GetParent() {
1299
		return $this->qparent;
1300
	}
1301
	function SetParent(&$parent) {
1302
		$this->qparent = &$parent;
1303
	}
1304
	function GetEnabled() {
1305
		return $this->qenabled;
1306
	}
1307
	function SetEnabled($value) {
1308
		$this->qenabled = $value;
1309
	}
1310
	function CanHaveChildren() {
1311
		return false;
1312
	}
1313
	function CanBeDeleted() {
1314
		return true;
1315
	}
1316
	function GetQname() {
1317
		return $this->qname;
1318
	}
1319
	function SetQname($name) {
1320
		$this->qname = trim($name);
1321
	}
1322
	function GetBandwidth() {
1323
		return $this->qbandwidth;
1324
	}
1325
	function SetBandwidth($bandwidth) {
1326
		$this->qbandwidth = $bandwidth;
1327
	}
1328
	function GetInterface() {
1329
		return $this->qinterface;
1330
	}
1331
	function SetInterface($name) {
1332
		$this->qinterface = trim($name);
1333
	}
1334
	function GetQlimit() {
1335
		return $this->qlimit;
1336
	}
1337
	function SetQlimit($limit) {
1338
		$this->qlimit = $limit;
1339
	}
1340
	function GetQpriority() {
1341
		if (is_numeric($this->qpriority)) {
1342
			return $this->qpriority;
1343
		} else {
1344
			return '1';
1345
		}
1346
	}
1347
	function SetQpriority($priority) {
1348
		$this->qpriority = $priority;
1349
	}
1350
	function GetDescription() {
1351
		return $this->description;
1352
	}
1353
	function SetDescription($str) {
1354
		$this->description = trim($str);
1355
	}
1356
	function GetFirstime() {
1357
		return $this->firsttime;
1358
	}
1359
	function SetFirsttime($number) {
1360
		$this->firsttime = $number;
1361
	}
1362
	function CheckBandwidth($bw, $bwtype) {
1363
		$parent = $this->GetParent();
1364

    
1365
		// If the child queues have bandwidth specified in %, there is no point trying
1366
		// to compare the total to the parent's bandwidth, which is defined in Kb/S or Mb/S etc.
1367
		// ToDo: Add check for total % > 100
1368
		//		 If one child is in %, they must all be
1369

    
1370
		if ($bwtype != "%") {
1371
			$sum = $parent->GetTotalBw(($bw > 0) ? $this : NULL);
1372

    
1373
			if ($bw > 0) {
1374
				$sum += get_bandwidth($bw, $bwtype, $parent);
1375
			}
1376

    
1377
			if ($sum > get_queue_bandwidth($parent)) {
1378
				return 1;
1379
			}
1380
		}
1381

    
1382
		foreach ($this->subqueues as $q) {
1383
			if ($q->CheckBandwidth(0, '')) {
1384
				return 1;
1385
			}
1386
		}
1387

    
1388
		return 0;
1389
	}
1390
	function GetTotalBw($qignore = NULL) {
1391
		$sum = 0;
1392
		foreach ($this->subqueues as $q) {
1393
			if ($qignore != NULL && $qignore == $q)
1394
				continue;
1395
			$sum += get_bandwidth($q->GetBandwidth(), $q->GetBwscale(), $q->GetParent());
1396
		}
1397

    
1398
		return $sum;
1399
	}
1400
	function GetBwscale() {
1401
		return $this->qbandwidthtype;
1402
	}
1403
	function FormGetBwscale() {
1404
		if ($this->GetBwscale()) {
1405
			$bwscale = $this->GetBwscale();
1406
		} else {
1407
			$bwscale = 'Mb';
1408
		}
1409
		return $bwscale;
1410
	}
1411
	function SetBwscale($scale) {
1412
		$this->qbandwidthtype = $scale;
1413
	}
1414
	function GetDefaultQueuePresent() {
1415
		if ($this->GetDefault()) {
1416
			return true;
1417
		}
1418
		if (!empty($this->subqueues)) {
1419
			foreach ($this->subqueues as $q) {
1420
				if ($q->GetDefault()) {
1421
					return true;
1422
				}
1423
			}
1424
		}
1425

    
1426
		return false;
1427
	}
1428
	function GetDefault() {
1429
		return $this->qdefault;
1430
	}
1431
	function SetDefault($value = false) {
1432
		$this->qdefault = $value;
1433
	}
1434
	function GetCodel() {
1435
		return $this->codel;
1436
	}
1437
	function SetCodel($codel = false) {
1438
		$this->codel = $codel;
1439
	}
1440
	function GetRed() {
1441
		return $this->qred;
1442
	}
1443
	function SetRed($red = false) {
1444
		$this->qred = $red;
1445
	}
1446
	function GetRio() {
1447
		return $this->qrio;
1448
	}
1449
	function SetRio($rio = false) {
1450
		$this->qrio = $rio;
1451
	}
1452
	function GetEcn() {
1453
		return $this->qecn;
1454
	}
1455
	function SetEcn($ecn = false) {
1456
		$this->qecn = $ecn;
1457
	}
1458
	function GetAck() {
1459
		return $this->qack;
1460
	}
1461
	function SetAck($ack = false) {
1462
		$this->qack = $ack;
1463
	}
1464

    
1465
	function GetBwscaleText() {
1466
		switch ($this->qbandwidthtype) {
1467
			case "b":
1468
				$bwscaletext = "Bit/s";
1469
				break;
1470
			case "Kb":
1471
				$bwscaletext = "Kbit/s";
1472
				break;
1473
			case "Mb":
1474
				$bwscaletext = "Mbit/s";
1475
				break;
1476
			case "Gb":
1477
				$bwscaletext = "Gbit/s";
1478
				break;
1479
			default:
1480
				/* For others that do not need translating like % */
1481
				$bwscaletext = $this->qbandwidthtype;
1482
				break;
1483
		}
1484
		return $bwscaletext;
1485
	}
1486

    
1487
	function build_javascript() {
1488
		$javascript = "<script type=\"text/javascript\">";
1489
		$javascript .= "//<![CDATA[\n";
1490
		$javascript .= "function mySuspend() { \n";
1491
		$javascript .= "if (document.layers && document.layers['shaperarea'] != null)\n";
1492
		$javascript .= "document.layers['shaperarea'].visibility = 'hidden';\n";
1493
		$javascript .= "else if (document.all)\n";
1494
		$javascript .= "document.all['shaperarea'].style.visibility = 'hidden';\n";
1495
		$javascript .= "}\n";
1496

    
1497
		$javascript .= "function myResume() {\n";
1498
		$javascript .= "if (document.layers && document.layers['shaperarea'] != null)\n";
1499
		$javascript .= "document.layers['shaperarea'].visibility = 'visible';\n";
1500
		$javascript .= "else if (document.all)\n";
1501
		$javascript .= "document.all['shaperarea'].style.visibility = 'visible';\n";
1502
		$javascript .= "}\n";
1503
		$javascript .= "//]]>";
1504
		$javascript .= "</script>";
1505

    
1506
		return $javascript;
1507
	}
1508

    
1509
	function &add_queue($interface, &$qname, &$path, &$input_errors) { return; }
1510

    
1511
	/*
1512
	 * Currently this will not be called unless we decide to clone a whole
1513
	 * queue tree on the 'By Queues' view or support drag&drop on the tree/list
1514
	 */
1515
	function copy_queue($interface, &$cflink) {
1516

    
1517
		$cflink['name'] = $this->GetQname();
1518
		$cflink['interface'] = $interface;
1519
		$cflink['qlimit'] = $this->GetQlimit();
1520
		$cflink['priority'] = $this->GetQpriority();
1521
		$cflink['description'] = $this->GetDescription();
1522
		$cflink['enabled'] = $this->GetEnabled();
1523
		$cflink['default'] = $this->GetDefault();
1524
		$cflink['red'] = $this->GetRed();
1525
		$cflink['codel'] = $this->GetCodel();
1526
		$cflink['rio'] = $this->GetRio();
1527
		$cflink['ecn'] = $this->GetEcn();
1528

    
1529
		if (is_array($this->subqueues)) {
1530
			$cflinkp['queue'] = array();
1531
			foreach ($this->subqueues as $q) {
1532
				$cflink['queue'][$q->GetQname()] = array();
1533
				$q->copy_queue($interface, $cflink['queue'][$q->GetQname()]);
1534
			}
1535
		}
1536
	}
1537

    
1538
	function clean_queue($sched) {
1539
		clean_child_queues($sched, $this->GetLink());
1540
		if (is_array($this->subqueues)) {
1541
			foreach ($this->subqueues as $q) {
1542
				$q->clean_queue($sched);
1543
			}
1544
		}
1545
	}
1546

    
1547
	function &get_queue_list(&$qlist) {
1548

    
1549
		$qlist[$this->GetQname()] = & $this;
1550
		if (is_array($this->subqueues)) {
1551
			foreach ($this->subqueues as $queue) {
1552
				$queue->get_queue_list($qlist);
1553
			}
1554
		}
1555
	}
1556

    
1557
	function delete_queue() {
1558
		unref_on_altq_queue_list($this->GetQname());
1559
		cleanup_queue_from_rules($this->GetQname());
1560
		unset_object_by_reference($this->GetLink());
1561
	}
1562

    
1563
	function delete_all() {
1564
		if (count($this->subqueues)) {
1565
			foreach ($this->subqueues as $q) {
1566
				$q->delete_all();
1567
				unset_object_by_reference($q->GetLink());
1568
				unset($q);
1569
			}
1570
			unset($this->subqueues);
1571
		}
1572
	}
1573

    
1574
	function &find_queue($interface, $qname) {
1575
		if ($qname == $this->GetQname()) {
1576
			return $this;
1577
		}
1578
	}
1579

    
1580
	function find_parentqueue($interface, $qname) { return; }
1581

    
1582
	function validate_input($data, &$input_errors) {
1583
		global $altq_list_queues;
1584

    
1585
		$reqdfields[] = "name";
1586
		$reqdfieldsn[] = gettext("Name");
1587
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
1588
		$parent = $altq_list_queues[$this->GetInterface()];
1589

    
1590
		if ($data['bandwidth'] && !is_numeric($data['bandwidth'])) {
1591
			$input_errors[] = gettext("Bandwidth must be an integer.");
1592
		}
1593
		if ($data['bandwidth'] < 0) {
1594
			$input_errors[] = gettext("Bandwidth cannot be negative.");
1595
		}
1596
		if ($data['bandwidthtype'] == "%") {
1597
			if (($data['bandwidth'] > 100) || ($data['bandwidth'] < 0)) {
1598
				$input_errors[] = gettext("Bandwidth in percentage should be between 1 and 100.");
1599
			}
1600
		}
1601
		if ($this->CheckBandwidth($data['bandwidth'], $data['bandwidthtype']))
1602
			$input_errors[] = "The sum of child bandwidth is higher than parent.";
1603

    
1604
		if ($parent) {
1605
			if (($parent->GetScheduler() == 'PRIQ') && (!is_numeric($data['priority']) ||
1606
			    ($data['priority'] < 0) || ($data['priority'] > 15))) {
1607
				$input_errors[] = gettext("The priority must be an integer between 0 and 15.");
1608
			} elseif (in_array($parent->GetScheduler(), array('FAIRQ','CBQ')) && (!is_numeric($data['priority']) ||
1609
			    ($data['priority'] < 0) || ($data['priority'] > 7))) {
1610
				$input_errors[] = gettext("The priority must be an integer between 0 and 7.");
1611
			}
1612
			if (is_array($parent->queues)) {
1613
				foreach ($parent->queues as $q) {
1614
					if (($data['newname'] == $q->GetQname()) && ($data['name'] != $data['newname'])) { 
1615
						$input_errors[] = gettext("A queue with the same name already exists.");
1616
					}
1617
					if (in_array($parent->GetScheduler(), array('FAIRQ','PRIQ')) &&
1618
					    ($data['priority'] == $q->GetQpriority()) && ($data['name'] != $q->GetQname())) { 
1619
						$input_errors[] = sprintf(gettext("The priority %d is already used by queue %s."),
1620
								$data['priority'], $q->GetQname());
1621
					}
1622
				}
1623
			}
1624
		}
1625

    
1626
		if ($data['qlimit'] && (!is_numeric($data['qlimit']))) {
1627
				$input_errors[] = gettext("Queue limit must be an integer");
1628
		}
1629
		if ($data['qlimit'] < 0) {
1630
				$input_errors[] = gettext("Queue limit must be positive");
1631
		}
1632
		if (!empty($data['newname']) && !preg_match("/^[a-zA-Z0-9_-]*$/", $data['newname'])) {
1633
			$input_errors[] = gettext("Queue names must be alphanumeric and _ or - only.");
1634
		}
1635
		if (!empty($data['name']) && !preg_match("/^[a-zA-Z0-9_-]*$/", $data['name'])) {
1636
			$input_errors[] = gettext("Queue names must be alphanumeric and _ or - only.");
1637
		}
1638
		$default = $this->GetDefault();
1639
		if (!empty($data['default']) && altq_get_default_queue($data['interface']) && empty($default)) {
1640
			$input_errors[] = gettext("Only one default queue per interface is allowed.");
1641
		}
1642
	}
1643

    
1644
	function ReadConfig(&$q) {
1645
		if (!empty($q['name']) && !empty($q['newname']) && ($q['name'] != $q['newname'])) {
1646
			$this->SetQname($q['newname']);
1647
			rename_queue_in_rules($q['name'], $q['newname']);
1648
		} else if (!empty($q['newname'])) {
1649
			$this->SetQname($q['newname']);
1650
		} elseif (isset($q['name'])) {
1651
			$this->SetQname($q['name']);
1652
		}
1653
		if (isset($q['interface'])) {
1654
			$this->SetInterface($q['interface']);
1655
		}
1656
		$this->SetBandwidth($q['bandwidth']);
1657
		if (!empty($q['bandwidthtype'])) {
1658
			$this->SetBwscale($q['bandwidthtype']);
1659
		}
1660
		if (!empty($q['qlimit'])) {
1661
			$this->SetQlimit($q['qlimit']);
1662
		} else {
1663
			$this->SetQlimit(""); // Default
1664
		}
1665
		if (is_numeric($q['priority'])) {
1666
			$this->SetQPriority($q['priority']);
1667
		} else {
1668
			$this->SetQpriority("");
1669
		}
1670
		if (!empty($q['description'])) {
1671
			$this->SetDescription($q['description']);
1672
		} else {
1673
			$this->SetDescription("");
1674
		}
1675
		if (!empty($q['red'])) {
1676
			$this->SetRed($q['red']);
1677
		} else {
1678
			$this->SetRed();
1679
		}
1680
		if (!empty($q['codel'])) {
1681
			$this->SetCodel($q['codel']);
1682
		} else {
1683
			$this->SetCodel();
1684
		}
1685
		if (!empty($q['rio'])) {
1686
			$this->SetRio($q['rio']);
1687
		} else {
1688
			$this->SetRio();
1689
		}
1690
		if (!empty($q['ecn'])) {
1691
			$this->SetEcn($q['ecn']);
1692
		} else {
1693
			$this->SetEcn();
1694
		}
1695
		if (!empty($q['default'])) {
1696
			$this->SetDefault($q['default']);
1697
		} else {
1698
			$this->SetDefault();
1699
		}
1700
		if (!empty($q['enabled'])) {
1701
			$this->SetEnabled($q['enabled']);
1702
		} else {
1703
			$this->SetEnabled("");
1704
		}
1705
	}
1706

    
1707
	function build_tree() {
1708
		$tree = " <li><a href=\"firewall_shaper.php?interface=". $this->GetInterface()."&amp;queue=". htmlspecialchars($this->GetQname())."&amp;action=show";
1709
		$tree .= "\" ";
1710
		$tmpvalue = $this->GetDefault();
1711
		if (!empty($tmpvalue)) {
1712
			$tree .= " class=\"navlnk\"";
1713
		}
1714
		$tree .= " >" . htmlspecialchars($this->GetQname()) . "</a>";
1715
		/*
1716
		 * Not needed here!
1717
		 * if (is_array($queues) {
1718
		 *	  $tree .= "<ul>";
1719
		 *	  foreach ($q as $queues)
1720
		 *		  $tree .= $queues['$q->GetName()']->build_tree();
1721
		 *	  endforeach
1722
		 *	  $tree .= "</ul>";
1723
		 * }
1724
		 */
1725

    
1726
		$tree .= "</li>";
1727

    
1728
		return $tree;
1729
	}
1730

    
1731
	/* Should return something like:
1732
	 * queue $qname on $qinterface bandwidth ....
1733
	 */
1734
	function build_rules(&$default = false) {
1735
		$pfq_rule = " queue ". $this->qname;
1736
		if ($this->GetInterface()) {
1737
			$pfq_rule .= " on ".get_real_interface($this->GetInterface());
1738
		}
1739
		$tmpvalue = $this->GetQpriority();
1740
		if (is_numeric($tmpvalue)) {
1741
			$pfq_rule .= " priority ".$this->GetQpriority();
1742
		}
1743
		$tmpvalue = $this->GetQlimit();
1744
		if (!empty($tmpvalue)) {
1745
			$pfq_rule .= " qlimit " . $this->GetQlimit();
1746
		}
1747
		if ($this->GetRed() || $this->GetRio() || $this->GetEcn() || $this->GetDefault() || $this->GetCodel()) {
1748
			$pfq_rule .= " priq ( ";
1749
			$tmpvalue = $this->GetRed();
1750
			if (!empty($tmpvalue)) {
1751
				$comma = 1;
1752
				$pfq_rule .= " red ";
1753
			}
1754
			$tmpvalue = $this->GetRio();
1755
			if (!empty($tmpvalue)) {
1756
				if ($comma) {
1757
					$pfq_rule .= " ,";
1758
				}
1759
				$comma = 1;
1760
				$pfq_rule .= " rio ";
1761
			}
1762
			$tmpvalue = $this->GetEcn();
1763
			if (!empty($tmpvalue)) {
1764
				if ($comma) {
1765
					$pfq_rule .= " ,";
1766
				}
1767
				$comma = 1;
1768
				$pfq_rule .= " ecn ";
1769
			}
1770
			$tmpvalue = $this->GetCodel();
1771
			if (!empty($tmpvalue)) {
1772
				if ($comma) {
1773
					$pfq_rule .= " ,";
1774
				}
1775
				$comma = 1;
1776
				$pfq_rule .= " codel ";
1777
			}
1778
			$tmpvalue = $this->GetDefault();
1779
			if (!empty($tmpvalue)) {
1780
				if ($comma) {
1781
					$pfq_rule .= " ,";
1782
				}
1783
				$pfq_rule .= " default ";
1784
				$default = true;
1785
			}
1786
			$pfq_rule .= " ) ";
1787
		}
1788

    
1789
		$pfq_rule .= " \n";
1790

    
1791
		return $pfq_rule;
1792
	}
1793

    
1794
	/*
1795
	 * To return the html form to show to user
1796
	 * for getting the parameters.
1797
	 * Should do even for first time when the
1798
	 * object is created and later when we may
1799
	 * need to update it. (2)
1800
	 */
1801

    
1802
	function build_form() {
1803

    
1804
		$sform = new Form();
1805

    
1806
		$sform->setAction("firewall_shaper.php");
1807

    
1808
		$section = new Form_Section("");
1809

    
1810
		$section->addInput(new Form_Checkbox(
1811
			'enabled',
1812
			'Enable/Disable',
1813
			'Enable/disable discipline and its children',
1814
			($this->GetEnabled() == "on"),
1815
			'on'
1816
		));
1817

    
1818
		$section->addInput(new Form_Input(
1819
			'newname',
1820
			'*Name',
1821
			'text',
1822
			$this->GetQname()
1823
		))->setHelp('Enter the name of the queue here. Do not use spaces and limit the size to 15 characters.');
1824

    
1825
		$section->addInput(new Form_Input(
1826
			'name',
1827
			null,
1828
			'hidden',
1829
			$this->GetQname()
1830
		));
1831

    
1832
		if (!is_a($this, "hfsc_queue")) {
1833
			$section->addInput(new Form_Input(
1834
				'priority',
1835
				'Priority',
1836
				'number',
1837
				$this->GetQpriority(),
1838
				['min' => '0', 'max'=> '']
1839
			))->setHelp('For cbq and fairq the range is 0 to 7. The default is 1. For priq the range is 0 to 15, queues with a higher priority are preferred in the case of overload.');
1840
		}
1841

    
1842
		$section->addInput(new Form_Input(
1843
			'qlimit',
1844
			'Queue Limit',
1845
			'number',
1846
			$this->GetQlimit()
1847
		))->setHelp('Queue limit in packets.');
1848

    
1849
		$group = new Form_Group('Scheduler options');
1850

    
1851
		if (empty($this->subqueues)) {
1852
			$group->add(new Form_Checkbox(
1853
				'default',
1854
				null,
1855
				null,
1856
				$this->GetDefault(),
1857
				'default'
1858
			))->setHelp('Default Queue');
1859
		}
1860

    
1861
		$group->add(new Form_Checkbox(
1862
			'red',
1863
			null,
1864
			null,
1865
			!empty($this->GetRed())
1866
		))->setHelp('%1$sRandom Early Detection%2$s', '<a target="_new" href="https://docs.netgate.com/pfsense/en/latest/trafficshaper/advanced.html#editing-shaper-queues">', '</a>');
1867

    
1868
		$group->add(new Form_Checkbox(
1869
			'rio',
1870
			null,
1871
			null,
1872
			!empty($this->GetRio())
1873
		))->setHelp('%1$sRandom Early Detection In and Out%2$s', '<a target="_new" href="https://docs.netgate.com/pfsense/en/latest/trafficshaper/advanced.html#editing-shaper-queues">', '</a>');
1874

    
1875
		$group->add(new Form_Checkbox(
1876
			'ecn',
1877
			null,
1878
			null,
1879
			!empty($this->GetEcn())
1880
		))->setHelp('%1$sExplicit Congestion Notification%2$s', '<a target="_new" href="https://docs.netgate.com/pfsense/en/latest/trafficshaper/advanced.html#editing-shaper-queues">', '</a>');
1881

    
1882
		$group->add(new Form_Checkbox(
1883
			'codel',
1884
			null,
1885
			null,
1886
			!empty($this->GetCodel())
1887
		))->setHelp('%1$sCodel Active Queue%2$s', '<a target="_new" href="https://docs.netgate.com/pfsense/en/latest/trafficshaper/altq-scheduler-types.html#codel-active-queue-management">', '</a>');
1888

    
1889
		$group->setHelp('Select options for this queue');
1890

    
1891
		$section->add($group);
1892

    
1893
		$section->addInput(new Form_Input(
1894
			'description',
1895
			'Description',
1896
			'text',
1897
			$this->GetDescription()
1898
		));
1899

    
1900
		$sform->add($section);
1901

    
1902
		$sform->addGlobal(new Form_Input(
1903
			'interface',
1904
			null,
1905
			'hidden',
1906
			$this->GetInterface()
1907
		));
1908

    
1909
		$sform->addGlobal(new Form_Input(
1910
			'name',
1911
			null,
1912
			'hidden',
1913
			$this->GetQname()
1914
		));
1915

    
1916
		return($sform);
1917
	}
1918

    
1919
	function build_shortform() {
1920
		/* XXX: Hacks in sight. Mostly layer violations!  */
1921
		global $g, $altq_list_queues;
1922
		global $shaperIFlist;
1923

    
1924
		$altq =& $altq_list_queues[$this->GetInterface()];
1925

    
1926
		if ($altq) {
1927
			$scheduler = $altq->GetScheduler();
1928
		}
1929

    
1930
		$form = '<dl class="dl-horizontal">';
1931
		$form .= '	<dt>';
1932
		$form .= '		<a href="firewall_shaper.php?interface=' . $this->GetInterface() . '&amp;queue=' . $this->GetQname() . '&amp;action=show">' . $shaperIFlist[$this->GetInterface()] . '</a>';
1933
		$form .= '	</dt>';
1934
		$form .= '	<dd>';
1935
		$form .=		$scheduler;
1936
		$form .= '	</dd>';
1937

    
1938
		$form .= '	<dt>';
1939
		$form .=		'Bandwidth';
1940
		$form .= '	</dt>';
1941
		$form .= '	<dd>';
1942
		$form .=		$this->GetBandwidth() . '&nbsp;' . $this->GetBwscaleText();
1943
		$form .= '	</dd>';
1944

    
1945
		$tmpvalue = $this->GetQpriority();
1946
		if (!empty($tmpvalue)) {
1947
			$form .= '	<dt>';
1948
			$form .=		'Priority';
1949
			$form .= '	<dt>';
1950
			$form .= '	<dd>';
1951
			$form .=		'On';
1952
			$form .= '	</dd>';
1953
		}
1954

    
1955
		$tmpvalue = $this->GetDefault();
1956
		if (!empty($tmpvalue)) {
1957
			$form .= '	<dt>';
1958
			$form .=		'Default';
1959
			$form .= '	<dt>';
1960
			$form .= '	<dd>';
1961
			$form .=		'On';
1962
			$form .= '	</dd>';
1963
		}
1964

    
1965
			$form .= '	<dt>';
1966
			$form .= 'Delete';
1967
			$form .= '	<dt>';
1968
			$form .= '	<dd>';
1969

    
1970
			$form .= '<a class="btn btn-danger btn-xs" href="firewall_shaper_queues.php?interface=';
1971
			$form .= $this->GetInterface() . '&amp;queue=';
1972
			$form .= $this->GetQname() . '&amp;action=delete">';
1973
			$form .= '<i class="fa fa-trash icon-embed-btn"></i>';
1974
			$form .= gettext("Delete Queue from this Interface") . '</a>';
1975

    
1976
			$form .= '	</dd>';
1977

    
1978
			$form .= '</dl>';
1979

    
1980
		return $form;
1981

    
1982
	}
1983

    
1984
	function update_altq_queue_data(&$q) {
1985
		$this->ReadConfig($q);
1986
	}
1987

    
1988
	function wconfig() {
1989
		$cflink =& get_reference_to_me_in_config($this->GetLink());
1990
		if (!is_array($cflink)) {
1991
			$cflink = array();
1992
		}
1993
		$cflink['name'] = $this->GetQname();
1994
		$cflink['interface'] = $this->GetInterface();
1995
		$cflink['qlimit'] = trim($this->GetQlimit());
1996
		if (empty($cflink['qlimit'])) {
1997
			unset($cflink['qlimit']);
1998
		}
1999
		$cflink['priority'] = trim($this->GetQpriority());
2000
		if (!is_numeric($cflink['priority'])) {
2001
			unset($cflink['priority']);
2002
		}
2003
		$cflink['description'] = trim($this->GetDescription());
2004
		if (empty($cflink['description'])) {
2005
			unset($cflink['description']);
2006
		}
2007
		$cflink['enabled'] = trim($this->GetEnabled());
2008
		if (empty($cflink['enabled'])) {
2009
			unset($cflink['enabled']);
2010
		}
2011
		$cflink['default'] = trim($this->GetDefault());
2012
		if (empty($cflink['default'])) {
2013
			unset($cflink['default']);
2014
		}
2015
		$cflink['red'] = trim($this->GetRed());
2016
		if (empty($cflink['red'])) {
2017
			unset($cflink['red']);
2018
		}
2019
		$cflink['codel'] = trim($this->GetCodel());
2020
		if (empty($cflink['codel'])) {
2021
			unset($cflink['codel']);
2022
		}
2023
		$cflink['rio'] = trim($this->GetRio());
2024
		if (empty($cflink['rio'])) {
2025
			unset($cflink['rio']);
2026
		}
2027
		$cflink['ecn'] = trim($this->GetEcn());
2028
		if (empty($cflink['ecn'])) {
2029
			unset($cflink['ecn']);
2030
		}
2031
	}
2032
}
2033

    
2034
class hfsc_queue extends priq_queue {
2035
	/* realtime */
2036
	var $realtime;
2037
	var $r_m1;
2038
	var $r_d;
2039
	var $r_m2;
2040
	/* linkshare */
2041
	var $linkshare;
2042
	var $l_m1;
2043
	var $l_d;
2044
	var $l_m2;
2045
	/* upperlimit */
2046
	var $upperlimit;
2047
	var $u_m1;
2048
	var $u_d;
2049
	var $u_m2;
2050

    
2051
	/*
2052
	 * HFSC can have nested queues.
2053
	 */
2054
	function CanHaveChildren() {
2055
		return true;
2056
	}
2057
	function GetRealtime() {
2058
		return $this->realtime;
2059
	}
2060
	function GetR_m1() {
2061
		return $this->r_m1;
2062
	}
2063
	function GetR_d() {
2064
		return $this->r_d;
2065
	}
2066
	function GetR_m2() {
2067
		return $this->r_m2;
2068
	}
2069
	function SetRealtime() {
2070
		$this->realtime = "on";
2071
	}
2072
	function DisableRealtime() {
2073
		$this->realtime = "";
2074
	}
2075
	function SetR_m1($value) {
2076
		$this->r_m1 = $value;
2077
	}
2078
	function SetR_d($value) {
2079
		$this->r_d = $value;
2080
	}
2081
	function SetR_m2($value) {
2082
		$this->r_m2 = $value;
2083
	}
2084
	function GetLinkshare() {
2085
		return $this->linkshare;
2086
	}
2087
	function DisableLinkshare() {
2088
		$this->linkshare = "";
2089
	}
2090
	function GetL_m1() {
2091
		return $this->l_m1;
2092
	}
2093
	function GetL_d() {
2094
		return $this->l_d;
2095
	}
2096
	function GetL_m2() {
2097
		return $this->l_m2;
2098
	}
2099
	function SetLinkshare() {
2100
		$this->linkshare = "on";
2101
	}
2102
	function SetL_m1($value) {
2103
		$this->l_m1 = $value;
2104
	}
2105
	function SetL_d($value) {
2106
		$this->l_d = $value;
2107
	}
2108
	function SetL_m2($value) {
2109
		$this->l_m2 = $value;
2110
	}
2111
	function GetUpperlimit() {
2112
		return $this->upperlimit;
2113
	}
2114
	function GetU_m1() {
2115
		return $this->u_m1;
2116
	}
2117
	function GetU_d() {
2118
		return $this->u_d;
2119
	}
2120
	function GetU_m2() {
2121
		return $this->u_m2;
2122
	}
2123
	function SetUpperlimit() {
2124
		$this->upperlimit = "on";
2125
	}
2126
	function DisableUpperlimit() {
2127
		$this->upperlimit = "";
2128
	}
2129
	function SetU_m1($value) {
2130
		$this->u_m1 = $value;
2131
	}
2132
	function SetU_d($value) {
2133
		$this->u_d = $value;
2134
	}
2135
	function SetU_m2($value) {
2136
		$this->u_m2 = $value;
2137
	}
2138

    
2139
	function &add_queue($interface, &$qname, &$path, &$input_errors) {
2140

    
2141
		if (!is_array($this->subqueues)) {
2142
			$this->subqueues = array();
2143
		}
2144
		$__tmp_q = new hfsc_queue(); $q =& $__tmp_q;
2145
		$q->SetInterface($this->GetInterface());
2146
		$q->SetParent($this);
2147
		$q->ReadConfig($qname);
2148
		$q->validate_input($qname, $input_errors);
2149

    
2150
		$q->SetEnabled("on");
2151
		$q->SetLink($path);
2152

    
2153
		$this->subqueues[$q->GetQname()] =& $q; //new hfsc_queue()
2154
		ref_on_altq_queue_list($this->GetQname(), $q->GetQname());
2155
		if (is_array($qname['queue'])) {
2156
			foreach ($qname['queue'] as $key1 => $que) {
2157
				array_push($path, $key1);
2158
				$q->add_queue($q->GetInterface(), $que, $path, $input_errors);
2159
				array_pop($path);
2160
			}
2161
		}
2162

    
2163
		return $q;
2164
	}
2165

    
2166
	function copy_queue($interface, &$cflink) {
2167

    
2168
		$cflink['name'] = $this->GetQname();
2169
		$cflink['interface'] = $interface;
2170
		$cflink['qlimit'] = trim($this->GetQlimit());
2171
		if (empty($cflink['qlimit'])) {
2172
			unset($cflink['qlimit']);
2173
		}
2174
		$cflink['priority'] = trim($this->GetQpriority());
2175
		if (empty($cflink['priority'])) {
2176
			unset($cflink['priority']);
2177
		}
2178
		$cflink['description'] = trim($this->GetDescription());
2179
		if (empty($cflink['description'])) {
2180
			unset($cflink['description']);
2181
		}
2182
		$cflink['bandwidth'] = $this->GetBandwidth();
2183
		$cflink['bandwidthtype'] = $this->GetBwscale();
2184
		$cflink['enabled'] = trim($this->GetEnabled());
2185
		if (empty($cflink['enabled'])) {
2186
			unset($cflink['enabled']);
2187
		}
2188
		$cflink['default'] = trim($this->GetDefault());
2189
		if (empty($cflink['default'])) {
2190
			unset($cflink['default']);
2191
		}
2192
		$cflink['red'] = trim($this->GetRed());
2193
		if (empty($cflink['red'])) {
2194
			unset($cflink['red']);
2195
		}
2196
		$cflink['rio'] = trim($this->GetRio());
2197
		if (empty($cflink['rio'])) {
2198
			unset($cflink['rio']);
2199
		}
2200
		$cflink['ecn'] = trim($this->GetEcn());
2201
		if (empty($cflink['ecn'])) {
2202
			unset($cflink['ecn']);
2203
		}
2204
		if ($this->GetLinkshare() <> "") {
2205
			if ($this->GetL_m1() <> "") {
2206
				$cflink['linkshare1'] = $this->GetL_m1();
2207
				$cflink['linkshare2'] = $this->GetL_d();
2208
				$cflink['linkshare'] = "on";
2209
			} else {
2210
				unset($cflink['linkshare1']);
2211
				unset($cflink['linkshare2']);
2212
				unset($cflink['linkshare']);
2213
			}
2214
			if ($this->GetL_m2() <> "") {
2215
				$cflink['linkshare3'] = $this->GetL_m2();
2216
				$cflink['linkshare'] = "on";
2217
			} else {
2218
				unset($cflink['linkshare3']);
2219
				unset($cflink['linkshare']);
2220
			}
2221
		}
2222
		if ($this->GetRealtime() <> "") {
2223
			if ($this->GetR_m1() <> "") {
2224
				$cflink['realtime1'] = $this->GetR_m1();
2225
				$cflink['realtime2'] = $this->GetR_d();
2226
				$cflink['realtime'] = "on";
2227
			} else {
2228
				unset($cflink['realtime1']);
2229
				unset($cflink['realtime2']);
2230
				unset($cflink['realtime']);
2231
			}
2232
			if ($this->GetR_m2() <> "") {
2233
				$cflink['realtime3'] = $this->GetR_m2();
2234
				$cflink['realtime'] = "on";
2235
			} else {
2236
				unset($cflink['realtime3']);
2237
				unset($cflink['realtime']);
2238
			}
2239
		}
2240
		if ($this->GetUpperlimit() <> "") {
2241
			if ($this->GetU_m1() <> "") {
2242
				$cflink['upperlimit1'] = $this->GetU_m1();
2243
				$cflink['upperlimit2'] = $this->GetU_d();
2244
				$cflink['upperlimit'] = "on";
2245
			} else {
2246
				unset($cflink['upperlimit']);
2247
				unset($cflink['upperlimit1']);
2248
				unset($cflink['upperlimit2']);
2249
			}
2250
			if ($this->GetU_m2() <> "") {
2251
				$cflink['upperlimit3'] = $this->GetU_m2();
2252
				$cflink['upperlimit'] = "on";
2253
			} else {
2254
				unset($cflink['upperlimit3']);
2255
				unset($cflink['upperlimit']);
2256
			}
2257
		}
2258

    
2259
		if (is_array($this->subqueues)) {
2260
			$cflinkp['queue'] = array();
2261
			foreach ($this->subqueues as $q) {
2262
				$cflink['queue'][$q->GetQname()] = array();
2263
				$q->copy_queue($interface, $cflink['queue'][$q->GetQname()]);
2264
			}
2265
		}
2266
	}
2267

    
2268
	function delete_queue() {
2269
		unref_on_altq_queue_list($this->GetQname());
2270
		cleanup_queue_from_rules($this->GetQname());
2271
		$parent =& $this->GetParent();
2272
		foreach ($this->subqueues as $q) {
2273
			$q->delete_queue();
2274
		}
2275
		unset_object_by_reference($this->GetLink());
2276
	}
2277

    
2278
	/*
2279
	 * Should search even its children
2280
	 */
2281
	function &find_queue($interface, $qname) {
2282
		if ($qname == $this->GetQname()) {
2283
			return $this;
2284
		}
2285

    
2286
		foreach ($this->subqueues as $q) {
2287
			$result =& $q->find_queue("", $qname);
2288
			if ($result) {
2289
				return $result;
2290
			}
2291
		}
2292
	}
2293

    
2294
	function &find_parentqueue($interface, $qname) {
2295
		if ($this->subqueues[$qname]) {
2296
			return $this;
2297
		}
2298
		foreach ($this->subqueues as $q) {
2299
			$result = $q->find_parentqueue("", $qname);
2300
			if ($result) {
2301
				return $result;
2302
			}
2303
		}
2304
	}
2305

    
2306
	function validate_input($data, &$input_errors) {
2307
		parent::validate_input($data, $input_errors);
2308

    
2309
		$reqdfields[] = "bandwidth";
2310
		$reqdfieldsn[] = gettext("Bandwidth");
2311
		$reqdfields[] = "bandwidthtype";
2312
		$reqdfieldsn[] = gettext("Bandwidthtype");
2313

    
2314
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
2315

    
2316
		if (isset($data['linkshare3']) && !empty($data['linkshare3'])) {
2317
			if ($data['bandwidth'] && !is_numeric($data['bandwidth'])) {
2318
				$input_errors[] = gettext("Bandwidth must be an integer.");
2319
			}
2320

    
2321
			if ($data['bandwidth'] < 0) {
2322
				$input_errors[] = gettext("Bandwidth cannot be negative.");
2323
			}
2324

    
2325
			if ($data['bandwidthtype'] == "%") {
2326
				if (($data['bandwidth'] > 100) || ($data['bandwidth'] < 0)) {
2327
					$input_errors[] = gettext("Bandwidth in percentage should be between 1 and 100.");
2328
				}
2329
			}
2330
		}
2331

    
2332
		if (!empty($data['upperlimit1']) && empty($data['upperlimit2'])) {
2333
			$input_errors[] = gettext("upperlimit service curve defined but missing (d) value");
2334
		}
2335
		if (!empty($data['upperlimit2']) && empty($data['upperlimit1'])) {
2336
			$input_errors[] = gettext("upperlimit service curve defined but missing initial bandwidth (m1) value");
2337
		}
2338
		if (!empty($data['upperlimit1']) && !is_valid_shaperbw($data['upperlimit1'])) {
2339
			$input_errors[] = gettext("upperlimit m1 value needs to be Kb, Mb, Gb, or %");
2340
		}
2341
		if (!empty($data['upperlimit2']) && !is_numeric($data['upperlimit2'])) {
2342
			$input_errors[] = gettext("upperlimit d value needs to be numeric");
2343
		}
2344
		if (!empty($data['upperlimit3']) && !is_valid_shaperbw($data['upperlimit3'])) {
2345
			$input_errors[] = gettext("upperlimit m2 value needs to be Kb, Mb, Gb, or %");
2346
		}
2347

    
2348
		/*
2349
		if (isset($data['upperlimit']) && $data['upperlimit3'] <> "" && $data['upperlimit1'] <> "") {
2350
			$bw_1 = get_hfsc_bandwidth($this, $data['upperlimit1']);
2351
			$bw_2 = get_hfsc_bandwidth($this, $data['upperlimit3']);
2352
			if (floatval($bw_1) < floatval($bw_2)) {
2353
				$input_errors[] = ("upperlimit m1 cannot be smaller than m2");
2354
			}
2355

    
2356
			if (get_interface_bandwidth($this) < (0.8 * (floatval($bw_1) + floatval($bw_2)))) {
2357
				$input_errors[] = ("upperlimit specification exceeds 80% of allowable allocation.");
2358
			}
2359
		}
2360
		*/
2361
		if ($data['linkshare1'] <> "" && $data['linkshare2'] == "") {
2362
			$input_errors[] = gettext("linkshare service curve defined but missing (d) value");
2363
		}
2364
		if ($data['linkshare2'] <> "" && $data['linkshare1'] == "") {
2365
			$input_errors[] = gettext("linkshare service curve defined but missing initial bandwidth (m1) value");
2366
		}
2367
		if ($data['linkshare1'] <> "" && !is_valid_shaperbw($data['linkshare1'])) {
2368
			$input_errors[] = gettext("linkshare m1 value needs to be Kb, Mb, Gb, or %");
2369
		}
2370
		if ($data['linkshare2'] <> "" && !is_numeric($data['linkshare2'])) {
2371
			$input_errors[] = gettext("linkshare d value needs to be numeric");
2372
		}
2373
		if ($data['linkshare3'] <> "" && !is_valid_shaperbw($data['linkshare3'])) {
2374
			$input_errors[] = gettext("linkshare m2 value needs to be Kb, Mb, Gb, or %");
2375
		}
2376
		if ($data['realtime1'] <> "" && $data['realtime2'] == "") {
2377
			$input_errors[] = gettext("realtime service curve defined but missing (d) value");
2378
		}
2379
		if ($data['realtime2'] <> "" && $data['realtime1'] == "") {
2380
			$input_errors[] = gettext("realtime service curve defined but missing initial bandwidth (m1) value");
2381
		}
2382

    
2383
		/*
2384
		if (isset($data['linkshare']) && $data['linkshare3'] <> "" && $data['linkshare1'] <> "" && 0) {
2385
			$bw_1 = get_hfsc_bandwidth($this, $data['linkshare1']);
2386
			$bw_2 = get_hfsc_bandwidth($this, $data['linkshare3']);
2387
			if (floatval($bw_1) < floatval($bw_2)) {
2388
				$input_errors[] = ("linkshare m1 cannot be smaller than m2");
2389
			}
2390

    
2391
			if (get_interface_bandwidth($this) < (0.8 * (floatval($bw_1) + floatval($bw_2)))) {
2392
				$input_errors[] = ("linkshare specification exceeds 80% of allowable allocation.");
2393
			}
2394
		}
2395
		*/
2396

    
2397
		if ($data['realtime1'] <> "" && !is_valid_shaperbw($data['realtime1'])) {
2398
			$input_errors[] = gettext("realtime m1 value needs to be Kb, Mb, Gb, or %");
2399
		}
2400
		if ($data['realtime2'] <> "" && !is_numeric($data['realtime2'])) {
2401
			$input_errors[] = gettext("realtime d value needs to be numeric");
2402
		}
2403
		if ($data['realtime3'] <> "" && !is_valid_shaperbw($data['realtime3'])) {
2404
			$input_errors[] = gettext("realtime m2 value needs to be Kb, Mb, Gb, or %");
2405
		}
2406

    
2407
		/*
2408
		if (isset($data['realtime']) && $data['realtime3'] <> "" && $data['realtime1'] <> "" && 0) {
2409
			$bw_1 = get_hfsc_bandwidth($this, $data['realtime1']);
2410
			$bw_2 = get_hfsc_bandwidth($this, $data['realtime3']);
2411
			if (floatval($bw_1) < floatval($bw_2)) {
2412
				$input_errors[] = ("realtime m1 cannot be smaller than m2");
2413
			}
2414

    
2415
			if (get_interface_bandwidth($this) < (0.8 * (floatval($bw_1) + floatval($bw_2)))) {
2416
				$input_errors[] = ("realtime specification exceeds 80% of allowable allocation.");
2417
			}
2418
		}
2419
		*/
2420
	}
2421

    
2422
	function ReadConfig(&$cflink) {
2423
		if (!empty($cflink['linkshare'])) {
2424
			if (!empty($cflink['linkshare1'])) {
2425
				$this->SetL_m1($cflink['linkshare1']);
2426
				$this->SetL_d($cflink['linkshare2']);
2427
				$this->SetLinkshare();
2428
			} else {
2429
				$this->SetL_m1("");
2430
				$this->SetL_d("");
2431
				$this->DisableLinkshare();
2432
			}
2433
			if (!empty($cflink['linkshare3'])) {
2434
				$this->SetL_m2($cflink['linkshare3']);
2435
				$this->SetLinkshare();
2436
			}
2437
		} else {
2438
			$this->DisableLinkshare();
2439
		}
2440
		if (!empty($cflink['realtime'])) {
2441
			if (!empty($cflink['realtime1'])) {
2442
				$this->SetR_m1($cflink['realtime1']);
2443
				$this->SetR_d($cflink['realtime2']);
2444
				$this->SetRealtime();
2445
			} else {
2446
				$this->SetR_m1("");
2447
				$this->SetR_d("");
2448
				$this->DisableRealtime();
2449
			}
2450
			if (!empty($cflink['realtime3'])) {
2451
				$this->SetR_m2($cflink['realtime3']);
2452
				$this->SetRealtime();
2453
			}
2454
		} else {
2455
			$this->DisableRealtime();
2456
		}
2457
		if (!empty($cflink['upperlimit'])) {
2458
			if (!empty($cflink['upperlimit1'])) {
2459
				$this->SetU_m1($cflink['upperlimit1']);
2460
				$this->SetU_d($cflink['upperlimit2']);
2461
				$this->SetUpperlimit();
2462
			} else {
2463
				$this->SetU_m1("");
2464
				$this->SetU_d("");
2465
				$this->DisableUpperlimit();
2466
			}
2467
			if (!empty($cflink['upperlimit3'])) {
2468
				$this->SetU_m2($cflink['upperlimit3']);
2469
				$this->SetUpperlimit();
2470
			}
2471
		} else {
2472
			$this->DisableUpperlimit();
2473
		}
2474
		parent::ReadConfig($cflink);
2475
	}
2476

    
2477
	function build_tree() {
2478
		$tree = " <li><a href=\"firewall_shaper.php?interface=" . $this->GetInterface() ."&amp;queue=" . htmlspecialchars($this->GetQname())."&amp;action=show";
2479
		$tree .= "\" ";
2480
		$tmpvalue = $this->GetDefault();
2481
		if (!empty($tmpvalue)) {
2482
			$tree .= " class=\"navlnk\"";
2483
		}
2484
		$tree .= " >" . htmlspecialchars($this->GetQname()) . "</a>";
2485
		if (is_array($this->subqueues)) {
2486
			$tree .= "<ul>";
2487
			foreach ($this->subqueues as $q) {
2488
				$tree .= $q->build_tree();
2489
			}
2490
			$tree .= "</ul>";
2491
		}
2492
		$tree .= "</li>";
2493
		return $tree;
2494
	}
2495

    
2496
	/* Even this should take children into consideration */
2497
	function build_rules(&$default = false) {
2498

    
2499
		$pfq_rule = " queue ". $this->qname;
2500
		if ($this->GetInterface()) {
2501
			$pfq_rule .= " on ".get_real_interface($this->GetInterface());
2502
		}
2503
		if ($this->GetBandwidth() && $this->GetBwscale()) {
2504
			$pfq_rule .= " bandwidth ".trim($this->GetBandwidth()).$this->GetBwscale();
2505
		}
2506

    
2507
		$tmpvalue = $this->GetQlimit();
2508
		if (!empty($tmpvalue)) {
2509
			$pfq_rule .= " qlimit " . $this->GetQlimit();
2510
		}
2511
		if ($this->GetDefault() || $this->GetRed() || $this->GetRio() || $this->GetEcn() || $this->GetCodel() || $this->GetRealtime() <> "" || $this->GetLinkshare() <> "" || $this->GetUpperlimit() <> "") {
2512
			$pfq_rule .= " hfsc ( ";
2513
			$tmpvalue = $this->GetRed();
2514
			if (!empty($tmpvalue)) {
2515
				$comma = 1;
2516
				$pfq_rule .= " red ";
2517
			}
2518

    
2519
			$tmpvalue = $this->GetRio();
2520
			if (!empty($tmpvalue)) {
2521
				if ($comma) {
2522
					$pfq_rule .= " ,";
2523
				}
2524
				$comma = 1;
2525
				$pfq_rule .= " rio ";
2526
			}
2527
			$tmpvalue = $this->GetEcn();
2528
			if (!empty($tmpvalue)) {
2529
				if ($comma) {
2530
					$pfq_rule .= " ,";
2531
				}
2532
				$comma = 1;
2533
				$pfq_rule .= " ecn ";
2534
			}
2535
			$tmpvalue = $this->GetCodel();
2536
			if (!empty($tmpvalue)) {
2537
				if ($comma) {
2538
					$pfq_rule .= " ,";
2539
				}
2540
				$comma = 1;
2541
				$pfq_rule .= " codel ";
2542
			}
2543
			$tmpvalue = $this->GetDefault();
2544
			if (!empty($tmpvalue)) {
2545
				if ($comma) {
2546
					$pfq_rule .= " ,";
2547
				}
2548
				$comma = 1;
2549
				$pfq_rule .= " default ";
2550
				$default = true;
2551
			}
2552

    
2553
			if ($this->GetRealtime() <> "") {
2554
				if ($comma) {
2555
					$pfq_rule .= " , ";
2556
				}
2557
				if ($this->GetR_m1() <> "" && $this->GetR_d() <> "" && $this->GetR_m2() <> "") {
2558
					$pfq_rule .= " realtime (".$this->GetR_m1() . ", " . $this->GetR_d().", ". $this->GetR_m2() .") ";
2559
				} else if ($this->GetR_m2() <> "") {
2560
					$pfq_rule .= " realtime " . $this->GetR_m2();
2561
				}
2562
				$comma = 1;
2563
			}
2564
			if ($this->GetLinkshare() <> "") {
2565
				if ($comma) {
2566
					$pfq_rule .= " ,";
2567
				}
2568
				if ($this->GetL_m1() <> "" && $this->GetL_d() <> "" && $this->GetL_m2() <> "") {
2569
					$pfq_rule .= " linkshare (".$this->GetL_m1(). ", ". $this->GetL_d(). ", ". $this->GetL_m2(). ") ";
2570
				} else if ($this->GetL_m2() <> "") {
2571
					$pfq_rule .= " linkshare " . $this->GetL_m2() . " ";
2572
				}
2573
				$comma = 1;
2574
			}
2575
			if ($this->GetUpperlimit() <> "") {
2576
				if ($comma) {
2577
					$pfq_rule .= " ,";
2578
				}
2579
				if ($this->GetU_m1() <> "" && $this->GetU_d() <> "" && $this->GetU_m2() <> "") {
2580
							$pfq_rule .= " upperlimit (".$this->GetU_m1().", ". $this->GetU_d().", ". $this->GetU_m2(). ") ";
2581
				} else if ($this->GetU_m2() <> "") {
2582
					$pfq_rule .= " upperlimit " . $this->GetU_m2() . " ";
2583
				}
2584
			}
2585
			$pfq_rule .= " ) ";
2586
		}
2587
		if (count($this->subqueues)) {
2588
			$i = count($this->subqueues);
2589
			$pfq_rule .= " { ";
2590
			foreach ($this->subqueues as $qkey => $qnone) {
2591
				if ($i > 1) {
2592
					$i--;
2593
					$pfq_rule .= " {$qkey}, ";
2594
				} else {
2595
					$pfq_rule .= " {$qkey} ";
2596
				}
2597
			}
2598
			$pfq_rule .= " } \n";
2599
			foreach ($this->subqueues as $q) {
2600
				$pfq_rule .= $q->build_rules($default);
2601
			}
2602
		}
2603

    
2604
		$pfq_rule .= " \n";
2605

    
2606
		return $pfq_rule;
2607
	}
2608

    
2609
	function build_javascript() {
2610

    
2611
		$javascript = <<<EOJS
2612
<script type="text/javascript">
2613
//<![CDATA[
2614
	events.push(function(){
2615

    
2616
		// Disables the specified input element
2617
		function disableInput(id, disable) {
2618
			$('#' + id).prop("disabled", disable);
2619
		}
2620

    
2621
		// Upperlimit
2622
		function enable_upperlimit() {
2623
			disableInput('upperlimit1', !$('#upperlimit').prop('checked'));
2624
			disableInput('upperlimit2', !$('#upperlimit').prop('checked'));
2625
			disableInput('upperlimit3', !$('#upperlimit').prop('checked'));
2626
		}
2627

    
2628
		$('#upperlimit').click(function () {
2629
			enable_upperlimit();
2630
		});
2631

    
2632
		enable_upperlimit();
2633

    
2634
		// realtime
2635
		function enable_realtime() {
2636
			disableInput('realtime1', !$('#realtime').prop('checked'));
2637
			disableInput('realtime2', !$('#realtime').prop('checked'));
2638
			disableInput('realtime3', !$('#realtime').prop('checked'));
2639
		}
2640

    
2641
		$('#realtime').click(function () {
2642
			enable_realtime();
2643
		});
2644

    
2645
		enable_realtime();
2646

    
2647
		// linkshare
2648
		function enable_linkshare() {
2649
			disableInput('linkshare1', !$('#linkshare').prop('checked'));
2650
			disableInput('linkshare2', !$('#linkshare').prop('checked'));
2651
			disableInput('linkshare3', !$('#linkshare').prop('checked'));
2652
		}
2653

    
2654
		$('#linkshare').click(function () {
2655
			enable_linkshare();
2656
		});
2657

    
2658
		enable_linkshare();
2659
	});
2660
//]]>
2661
</script>
2662
EOJS;
2663

    
2664
		return $javascript;
2665
	}
2666

    
2667
	function build_form() {
2668

    
2669
		$sform = parent::build_form();
2670

    
2671
		$section = new Form_Section('Service Curve (sc)');
2672

    
2673
		$group = new Form_Group('Bandwidth');
2674

    
2675
		$group->add(new Form_Input(
2676
			'bandwidth',
2677
			null,
2678
			'number',
2679
			$this->GetBandwidth(),
2680
			['step' => 'any', 'min' => '0.000']
2681
		));
2682

    
2683
		$group->add(new Form_Select(
2684
			'bandwidthtype',
2685
			null,
2686
			$this->FormGetBwscale(),
2687
			array('Kb' => 'Kbit/s',
2688
				  'Mb' => 'Mbit/s',
2689
				  'Gb' => 'Gbit/s',
2690
				  'b' => 'Bit/s',
2691
				  '%' => '%')
2692
		));
2693

    
2694
		$group->setHelp('Choose the amount of bandwidth for this queue');
2695

    
2696
		$section->add($group);
2697

    
2698
		$group = new Form_Group('Max bandwidth for queue.');
2699

    
2700
		$group->add(new Form_Checkbox(
2701
			'upperlimit',
2702
			null,
2703
			'Upper Limit',
2704
			($this->GetUpperlimit()<> "")
2705
		));
2706

    
2707
		$group->add(new Form_Input(
2708
			'upperlimit1',
2709
			null,
2710
			'text',
2711
			$this->GetU_m1()
2712
		))->setHelp('m1');
2713

    
2714
		$group->add(new Form_Input(
2715
			'upperlimit2',
2716
			null,
2717
			'text',
2718
			$this->GetU_d()
2719
		))->setHelp('d');
2720

    
2721
		$group->add(new Form_Input(
2722
			'upperlimit3',
2723
			null,
2724
			'text',
2725
			$this->GetU_m2()
2726
		))->setHelp('m2');
2727

    
2728

    
2729
		$section->add($group);
2730

    
2731
		$group = new Form_Group('Min bandwidth for queue.');
2732

    
2733
		$group->add(new Form_Checkbox(
2734
			'realtime',
2735
			null,
2736
			'Real Time',
2737
			($this->GetRealtime()<> "")
2738
		));
2739

    
2740
		$group->add(new Form_Input(
2741
			'realtime1',
2742
			null,
2743
			'text',
2744
			$this->GetR_m1()
2745
		))->setHelp('m1');
2746

    
2747
		$group->add(new Form_Input(
2748
			'realtime2',
2749
			null,
2750
			'text',
2751
			$this->GetR_d()
2752
		))->setHelp('d');
2753

    
2754
		$group->add(new Form_Input(
2755
			'realtime3',
2756
			null,
2757
			'text',
2758
			$this->GetR_m2()
2759
		))->setHelp('m2');
2760

    
2761
		$section->add($group);
2762

    
2763
		$group = new Form_Group('B/W share of a backlogged queue.');
2764

    
2765
		$group->add(new Form_Checkbox(
2766
			'linkshare',
2767
			null,
2768
			'Link Share',
2769
			($this->GetLinkshare()<> "")
2770
		));
2771

    
2772
		$group->add(new Form_Input(
2773
			'linkshare1',
2774
			null,
2775
			'text',
2776
			$this->GetL_m1()
2777
		))->setHelp('m1');
2778

    
2779
		$group->add(new Form_Input(
2780
			'linkshare2',
2781
			null,
2782
			'text',
2783
			$this->GetL_d()
2784
		))->setHelp('d');
2785

    
2786
		$group->add(new Form_Input(
2787
			'linkshare3',
2788
			null,
2789
			'text',
2790
			$this->GetL_m2()
2791
		))->setHelp('m2');
2792

    
2793
		$group->sethelp('Bandwidth share overrides priority.%s' .
2794
						'The format for service curve specifications is (m1, d, m2). m2 controls the bandwidth assigned to the queue. ' .
2795
						'm1 and d are optional and can be used to control the initial bandwidth assignment. ' .
2796
						'For the first d milliseconds the queue gets the bandwidth given as m1, afterwards the value given in m2.',
2797
						'<br />');
2798

    
2799
		$section->add($group);
2800

    
2801
		$sform->add($section);
2802

    
2803
		return($sform);
2804
	}
2805

    
2806
	function update_altq_queue_data(&$data) {
2807
		$this->ReadConfig($data);
2808
	}
2809

    
2810
	function wconfig() {
2811
		$cflink =& get_reference_to_me_in_config($this->GetLink());
2812
		if (!is_array($cflink)) {
2813
			$cflink = array();
2814
		}
2815
		$cflink['name'] = $this->GetQname();
2816
		$cflink['interface'] = $this->GetInterface();
2817
		$cflink['qlimit'] = trim($this->GetQlimit());
2818
		if (empty($cflink['qlimit'])) {
2819
			unset($cflink['qlimit']);
2820
		}
2821
		$cflink['priority'] = $this->GetQpriority();
2822
		if (!is_numericint($cflink['priority'])) {
2823
			unset($cflink['priority']);
2824
		}
2825
		$cflink['description'] = $this->GetDescription();
2826
		if (empty($cflink['description'])) {
2827
			unset($cflink['description']);
2828
		}
2829
		$cflink['bandwidth'] = $this->GetBandwidth();
2830
		$cflink['bandwidthtype'] = $this->GetBwscale();
2831
		$cflink['enabled'] = $this->GetEnabled();
2832
		if (empty($cflink['enabled'])) {
2833
			unset($cflink['enabled']);
2834
		}
2835
		$cflink['default'] = $this->GetDefault();
2836
		if (empty($cflink['default'])) {
2837
			unset($cflink['default']);
2838
		}
2839
		$cflink['red'] = trim($this->GetRed());
2840
		if (empty($cflink['red'])) {
2841
			unset($cflink['red']);
2842
		}
2843
		$cflink['rio'] = $this->GetRio();
2844
		if (empty($cflink['rio'])) {
2845
			unset($cflink['rio']);
2846
		}
2847
		$cflink['ecn'] = trim($this->GetEcn());
2848
		if (empty($cflink['ecn'])) {
2849
			unset($cflink['ecn']);
2850
		}
2851
		$cflink['codel'] = trim($this->GetCodel());
2852
		if (empty($cflink['codel'])) {
2853
			unset($cflink['codel']);
2854
		}
2855
		if ($this->GetLinkshare() <> "") {
2856
			if ($this->GetL_m1() <> "") {
2857
				$cflink['linkshare1'] = $this->GetL_m1();
2858
				$cflink['linkshare2'] = $this->GetL_d();
2859
				$cflink['linkshare'] = "on";
2860
			} else {
2861
				unset($cflink['linkshare']);
2862
				unset($cflink['linkshare1']);
2863
				unset($cflink['linkshare2']);
2864
			}
2865
			if ($this->GetL_m2() <> "") {
2866
				$cflink['linkshare3'] = $this->GetL_m2();
2867
				$cflink['linkshare'] = "on";
2868
			} else {
2869
				unset($cflink['linkshare']);
2870
				unset($cflink['linkshare3']);
2871
			}
2872
		} else {
2873
			unset($cflink['linkshare']);
2874
			unset($cflink['linkshare1']);
2875
			unset($cflink['linkshare2']);
2876
			unset($cflink['linkshare3']);
2877
		}
2878
		if ($this->GetRealtime() <> "") {
2879
			if ($this->GetR_m1() <> "") {
2880
				$cflink['realtime1'] = $this->GetR_m1();
2881
				$cflink['realtime2'] = $this->GetR_d();
2882
				$cflink['realtime'] = "on";
2883
			} else {
2884
				unset($cflink['realtime']);
2885
				unset($cflink['realtime1']);
2886
				unset($cflink['realtime2']);
2887
			}
2888
			if ($this->GetR_m2() <> "") {
2889
				$cflink['realtime3'] = $this->GetR_m2();
2890
				$cflink['realtime'] = "on";
2891
			} else {
2892
				unset($cflink['realtime']);
2893
				unset($cflink['realtime3']);
2894
			}
2895
		} else {
2896
			unset($cflink['realtime']);
2897
			unset($cflink['realtime1']);
2898
			unset($cflink['realtime2']);
2899
			unset($cflink['realtime3']);
2900
		}
2901
		if ($this->GetUpperlimit() <> "") {
2902
			if ($this->GetU_m1() <> "") {
2903
				$cflink['upperlimit1'] = $this->GetU_m1();
2904
				$cflink['upperlimit2'] = $this->GetU_d();
2905
				$cflink['upperlimit'] = "on";
2906
			} else {
2907
				unset($cflink['upperlimit']);
2908
				unset($cflink['upperlimit1']);
2909
				unset($cflink['upperlimit2']);
2910
			}
2911
			if ($this->GetU_m2() <> "") {
2912
				$cflink['upperlimit3'] = $this->GetU_m2();
2913
				$cflink['upperlimit'] = "on";
2914
			} else {
2915
				unset($cflink['upperlimit']);
2916
				unset($cflink['upperlimit3']);
2917
			}
2918
		} else {
2919
			unset($cflink['upperlimit']);
2920
			unset($cflink['upperlimit1']);
2921
			unset($cflink['upperlimit2']);
2922
			unset($cflink['upperlimit3']);
2923
		}
2924
	}
2925
}
2926

    
2927
class cbq_queue extends priq_queue {
2928
	var $qborrow = "";
2929

    
2930
	function GetBorrow() {
2931
		return $this->qborrow;
2932
	}
2933
	function SetBorrow($borrow) {
2934
		$this->qborrow = $borrow;
2935
	}
2936
	function CanHaveChildren() {
2937
		return true;
2938
	}
2939

    
2940
	function &add_queue($interface, &$qname, &$path, &$input_errors) {
2941

    
2942
		if (!is_array($this->subqueues)) {
2943
			$this->subqueues = array();
2944
		}
2945
		$__tmp_q = new cbq_queue(); $q =& $__tmp_q;
2946
		$q->SetInterface($this->GetInterface());
2947
		$q->SetParent($this);
2948
		$q->ReadConfig($qname);
2949
		$q->validate_input($qname, $input_errors);
2950

    
2951
		$q->SetEnabled("on");
2952
		$q->SetLink($path);
2953
		$this->subqueues[$q->GetQName()] = &$q;
2954
		ref_on_altq_queue_list($this->GetQname(), $q->GetQname());
2955
		if (is_array($qname['queue'])) {
2956
			foreach ($qname['queue'] as $key1 => $que) {
2957
				array_push($path, $key1);
2958
				$q->add_queue($q->GetInterface(), $que, $path, $input_errors);
2959
				array_pop($path);
2960
			}
2961
		}
2962

    
2963
		return $q;
2964
	}
2965

    
2966
	function copy_queue($interface, &$cflink) {
2967

    
2968
		$cflink['interface'] = $interface;
2969
		$cflink['qlimit'] = trim($this->GetQlimit());
2970
		if (empty($clink['qlimit'])) {
2971
			unset($cflink['qlimit']);
2972
		}
2973
		$cflink['priority'] = trim($this->GetQpriority());
2974
		if (!is_numeric($cflink['priority'])) {
2975
			unset($cflink['priority']);
2976
		}
2977
		$cflink['name'] = $this->GetQname();
2978
		$cflink['description'] = trim($this->GetDescription());
2979
		if (empty($cflink['description'])) {
2980
			unset($cflink['description']);
2981
		}
2982
		$cflink['bandwidth'] = $this->GetBandwidth();
2983
		$cflink['bandwidthtype'] = $this->GetBwscale();
2984
		$cflink['enabled'] = trim($this->GetEnabled());
2985
		if (empty($cflink['enabled'])) {
2986
			unset($cflink['enabled']);
2987
		}
2988
		$cflink['default'] = trim($this->GetDefault());
2989
		if (empty($cflink['default'])) {
2990
			unset($cflink['default']);
2991
		}
2992
		$cflink['red'] = trim($this->GetRed());
2993
		if (empty($cflink['red'])) {
2994
			unset($cflink['red']);
2995
		}
2996
		$cflink['rio'] = trim($this->GetRio());
2997
		if (empty($cflink['rio'])) {
2998
			unset($cflink['rio']);
2999
		}
3000
		$cflink['ecn'] = trim($this->GetEcn());
3001
		if (empty($cflink['ecn'])) {
3002
			unset($cflink['ecn']);
3003
		}
3004
		$cflink['borrow'] = trim($this->GetBorrow());
3005
		if (empty($cflink['borrow'])) {
3006
			unset($cflink['borrow']);
3007
		}
3008
		if (is_array($this->queues)) {
3009
			$cflinkp['queue'] = array();
3010
			foreach ($this->subqueues as $q) {
3011
				$cflink['queue'][$q->GetQname()] = array();
3012
				$q->copy_queue($interface, $cflink['queue'][$q->GetQname()]);
3013
			}
3014
		}
3015
	}
3016

    
3017
	/*
3018
	 * Should search even its children
3019
	 */
3020
	function &find_queue($interface, $qname) {
3021
		if ($qname == $this->GetQname()) {
3022
			return $this;
3023
		}
3024
		foreach ($this->subqueues as $q) {
3025
			$result =& $q->find_queue("", $qname);
3026
			if ($result) {
3027
				return $result;
3028
			}
3029
		}
3030
	}
3031

    
3032
	function &find_parentqueue($interface, $qname) {
3033
		if ($this->subqueues[$qname]) {
3034
			return $this;
3035
		}
3036
		foreach ($this->subqueues as $q) {
3037
			$result = $q->find_parentqueue("", $qname);
3038
			if ($result) {
3039
				return $result;
3040
			}
3041
		}
3042
	}
3043

    
3044
	function delete_queue() {
3045
		unref_on_altq_queue_list($this->GetQname());
3046
		cleanup_queue_from_rules($this->GetQname());
3047
		foreach ($this->subqueues as $q) {
3048
			$q->delete_queue();
3049
		}
3050
		unset_object_by_reference($this->GetLink());
3051
	}
3052

    
3053
	function validate_input($data, &$input_errors) {
3054
		parent::validate_input($data, $input_errors);
3055

    
3056
		$reqdfields[] = "bandwidth";
3057
		$reqdfieldsn[] = gettext("Bandwidth");
3058
		$reqdfields[] = "bandwidthtype";
3059
		$reqdfieldsn[] = gettext("Bandwidthtype");
3060

    
3061
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
3062

    
3063
		if ($data['priority'] > 7) {
3064
			$input_errors[] = gettext("Priority must be an integer between 0 and 7.");
3065
		}
3066

    
3067
		$parent = $this->GetParent();
3068
		if (method_exists($parent, 'GetParent') && ($parent->GetBorrow() != "on") &&
3069
		    ($data['borrow'] == 'yes')) {
3070
			$input_errors[] = gettext("You cannot set 'Borrow' if the parent queue also does not borrow.");
3071
		}
3072
	}
3073

    
3074
	function ReadConfig(&$q) {
3075
		parent::ReadConfig($q);
3076
		if (!empty($q['borrow'])) {
3077
			$this->SetBorrow("on");
3078
		} else {
3079
			$this->SetBorrow("");
3080
		}
3081
	}
3082

    
3083
	function build_javascript() {
3084
		return parent::build_javascript();
3085
	}
3086

    
3087
	function build_tree() {
3088
		$tree = " <li><a href=\"firewall_shaper.php?interface=" . $this->GetInterface()."&amp;queue=" . htmlspecialchars($this->GetQname())."&amp;action=show";
3089
		$tree .= "\" ";
3090
		$tmpvalue = trim($this->GetDefault());
3091
		if (!empty($tmpvalue)) {
3092
			$tree .= " class=\"navlnk\"";
3093
		}
3094
		$tree .= " >" . htmlspecialchars($this->GetQname()) . "</a>";
3095
		if (is_array($this->subqueues)) {
3096
			$tree .= "<ul>";
3097
			foreach ($this->subqueues as $q) {
3098
				$tree .= $q->build_tree();
3099
			}
3100
			$tree .= "</ul>";
3101
		}
3102
		$tree .= "</li>";
3103
		return $tree;
3104
	}
3105

    
3106
	/* Even this should take children into consideration */
3107
	function build_rules(&$default = false) {
3108
		$pfq_rule = "queue ". $this->qname;
3109
		if ($this->GetInterface()) {
3110
			$pfq_rule .= " on ".get_real_interface($this->GetInterface());
3111
		}
3112
		if ($this->GetBandwidth() && $this->GetBwscale()) {
3113
			$pfq_rule .= " bandwidth ".trim($this->GetBandwidth()).$this->GetBwscale();
3114
		}
3115
		$tmpvalue = $this->GetQpriority();
3116
		if (is_numeric($tmpvalue)) {
3117
			$pfq_rule .= " priority " . $this->GetQpriority();
3118
		}
3119
		$tmpvalue = trim($this->GetQlimit());
3120
		if (!empty($tmpvalue)) {
3121
			$pfq_rule .= " qlimit " . $this->GetQlimit();
3122
		}
3123
		if ($this->GetDefault() || $this->GetRed() || $this->GetRio() || $this->GetEcn() || $this->GetBorrow() || $this->GetCodel()) {
3124
			$pfq_rule .= " cbq ( ";
3125
			$tmpvalue = trim($this->GetRed());
3126
			if (!empty($tmpvalue)) {
3127
				$comma = 1;
3128
				$pfq_rule .= " red ";
3129
			}
3130
			$tmpvalue = trim($this->GetCodel());
3131
			if (!empty($tmpvalue)) {
3132
				$comma = 1;
3133
				$pfq_rule .= " codel ";
3134
			}
3135
			$tmpvalue = trim($this->GetRio());
3136
			if (!empty($tmpvalue)) {
3137
				if ($comma) {
3138
					$pfq_rule .= " ,";
3139
				}
3140
				$comma = 1;
3141
				$pfq_rule .= " rio ";
3142
			}
3143
			$tmpvalue = trim($this->GetEcn());
3144
			if (!empty($tmpvalue)) {
3145
				if ($comma) {
3146
					$pfq_rule .= " ,";
3147
				}
3148
				$comma = 1;
3149
				$pfq_rule .= " ecn ";
3150
			}
3151
			$tmpvalue = trim($this->GetDefault());
3152
			if (!empty($tmpvalue)) {
3153
				if ($comma) {
3154
					$pfq_rule .= " ,";
3155
				}
3156
				$comma = 1;
3157
				$pfq_rule .= " default ";
3158
				$default = true;
3159
			}
3160
			$tmpvalue = trim($this->GetBorrow());
3161
			if (!empty($tmpvalue)) {
3162
				if ($comma) {
3163
					$pfq_rule .= ", ";
3164
				}
3165
				$pfq_rule .= " borrow ";
3166
			}
3167
			$pfq_rule .= " ) ";
3168
		}
3169
		if (count($this->subqueues)) {
3170
			$i = count($this->subqueues);
3171
			$pfq_rule .= " { ";
3172
			foreach ($this->subqueues as $qkey => $qnone) {
3173
				if ($i > 1) {
3174
					$i--;
3175
					$pfq_rule .= " {$qkey}, ";
3176
				} else {
3177
					$pfq_rule .= " {$qkey} ";
3178
				}
3179
			}
3180
			$pfq_rule .= " } \n";
3181
			foreach ($this->subqueues as $q) {
3182
				$pfq_rule .= $q->build_rules($default);
3183
			}
3184
		}
3185

    
3186
		$pfq_rule .= " \n";
3187
		return $pfq_rule;
3188
	}
3189

    
3190
	function build_form() {
3191
		$sform = parent::build_form();
3192

    
3193
		$section = new Form_Section('NOTITLE');
3194

    
3195
		$group = new Form_Group('Bandwidth');
3196

    
3197
		$group->add(new Form_Input(
3198
			'bandwidth',
3199
			null,
3200
			'number',
3201
			$this->GetBandwidth()
3202
		));
3203

    
3204
		$group->add(new Form_Select(
3205
			'bandwidthtype',
3206
			null,
3207
			$this->FormGetBwscale(),
3208
			array('Kb' => 'Kbit/s',
3209
				  'Mb' => 'Mbit/s',
3210
				  'Gb' => 'Gbit/s',
3211
				  'b' => 'Bit/s',
3212
				  '%' => '%')
3213
		));
3214

    
3215
		$group->setHelp('Choose the amount of bandwidth for this queue');
3216

    
3217
		$section->add($group);
3218

    
3219
		$section->addInput(new Form_Checkbox(
3220
			'borrow',
3221
			'Scheduler option',
3222
			'Borrow from other queues when available',
3223
			($this->GetBorrow() == "on")
3224
		));
3225

    
3226
		$sform->add($section);
3227

    
3228
		return $sform;
3229
	}
3230

    
3231
	function update_altq_queue_data(&$data) {
3232
		$this->ReadConfig($data);
3233
	}
3234

    
3235
	function wconfig() {
3236
		$cflink =& get_reference_to_me_in_config($this->GetLink());
3237
		if (!is_array($cflink)) {
3238
			$cflink = array();
3239
		}
3240
		$cflink['interface'] = $this->GetInterface();
3241
		$cflink['qlimit'] = trim($this->GetQlimit());
3242
		if (empty($cflink['qlimit'])) {
3243
			unset($cflink['qlimit']);
3244
		}
3245
		$cflink['priority'] = $this->GetQpriority();
3246
		if (!is_numeric($cflink['priority'])) {
3247
			unset($cflink['priority']);
3248
		}
3249
		$cflink['name'] = $this->GetQname();
3250
		$cflink['description'] = $this->GetDescription();
3251
		if (empty($cflink['description'])) {
3252
			unset($cflink['description']);
3253
		}
3254
		$cflink['bandwidth'] = $this->GetBandwidth();
3255
		$cflink['bandwidthtype'] = $this->GetBwscale();
3256
		$cflink['enabled'] = trim($this->GetEnabled());
3257
		if (empty($cflink['enabled'])) {
3258
			unset($cflink['enabled']);
3259
		}
3260
		$cflink['default'] = trim($this->GetDefault());
3261
		if (empty($cflink['default'])) {
3262
			unset($cflink['default']);
3263
		}
3264
		$cflink['red'] = trim($this->GetRed());
3265
		if (empty($cflink['red'])) {
3266
			unset($cflink['red']);
3267
		}
3268
		$cflink['rio'] = trim($this->GetRio());
3269
		if (empty($cflink['rio'])) {
3270
			unset($cflink['rio']);
3271
		}
3272
		$cflink['ecn'] = trim($this->GetEcn());
3273
		if (empty($cflink['ecn'])) {
3274
			unset($cflink['ecn']);
3275
		}
3276
		$cflink['codel'] = trim($this->GetCodel());
3277
		if (empty($cflink['codel'])) {
3278
			unset($cflink['codel']);
3279
		}
3280
		$cflink['borrow'] = trim($this->GetBorrow());
3281
		if (empty($cflink['borrow'])) {
3282
			unset($cflink['borrow']);
3283
		}
3284
	}
3285
}
3286

    
3287
class fairq_queue extends priq_queue {
3288
	var $hogs;
3289
	var $buckets;
3290

    
3291
	function GetBuckets() {
3292
		return $this->buckets;
3293
	}
3294
	function SetBuckets($buckets) {
3295
		$this->buckets = $buckets;
3296
	}
3297
	function GetHogs() {
3298
		return $this->hogs;
3299
	}
3300
	function SetHogs($hogs) {
3301
		$this->hogs = $hogs;
3302
	}
3303
	function CanHaveChildren() {
3304
		return false;
3305
	}
3306

    
3307

    
3308
	function copy_queue($interface, &$cflink) {
3309
		$cflink['interface'] = $interface;
3310
		$cflink['qlimit'] = $this->GetQlimit();
3311
		$cflink['priority'] = $this->GetQpriority();
3312
		$cflink['name'] = $this->GetQname();
3313
		$cflink['description'] = $this->GetDescription();
3314
		$cflink['bandwidth'] = $this->GetBandwidth();
3315
		$cflink['bandwidthtype'] = $this->GetBwscale();
3316
		$cflink['enabled'] = $this->GetEnabled();
3317
		$cflink['default'] = $this->GetDefault();
3318
		$cflink['red'] = $this->GetRed();
3319
		$cflink['rio'] = $this->GetRio();
3320
		$cflink['ecn'] = $this->GetEcn();
3321
		$cflink['buckets'] = $this->GetBuckets();
3322
		$cflink['hogs'] = $this->GetHogs();
3323
	}
3324

    
3325
	/*
3326
	 * Should search even its children
3327
	 */
3328
	function &find_queue($interface, $qname) {
3329
		if ($qname == $this->GetQname()) {
3330
			return $this;
3331
		}
3332
	}
3333

    
3334
	function find_parentqueue($interface, $qname) { return; }
3335

    
3336
	function delete_queue() {
3337
		unref_on_altq_queue_list($this->GetQname());
3338
		cleanup_queue_from_rules($this->GetQname());
3339
		unset_object_by_reference($this->GetLink());
3340
	}
3341

    
3342
	function validate_input($data, &$input_errors) {
3343
		parent::validate_input($data, $input_errors);
3344

    
3345
		if ($data['priority'] > 7) {
3346
				$input_errors[] = gettext("Priority must be an integer between 0 and 7.");
3347
		}
3348
		$reqdfields[] = "bandwidth";
3349
		$reqdfieldsn[] = gettext("Bandwidth");
3350
		$reqdfields[] = "bandwidthtype";
3351
		$reqdfieldsn[] = gettext("Bandwidthtype");
3352

    
3353
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
3354
	}
3355

    
3356
	function ReadConfig(&$q) {
3357
		parent::ReadConfig($q);
3358
		if (!empty($q['buckets'])) {
3359
			$this->SetBuckets($q['buckets']);
3360
		} else {
3361
			$this->SetBuckets("");
3362
		}
3363
		if (!empty($q['hogs']) && is_valid_shaperbw($q['hogs'])) {
3364
			$this->SetHogs($q['hogs']);
3365
		} else {
3366
			$this->SetHogs("");
3367
		}
3368
	}
3369

    
3370
	function build_javascript() {
3371
		return parent::build_javascript();
3372
	}
3373

    
3374
	function build_tree() {
3375
		$tree = " <li><a href=\"firewall_shaper.php?interface=" .
3376
		$this->GetInterface()."&amp;queue=" . htmlspecialchars($this->GetQname())."&amp;action=show";
3377
		$tree .= "\" ";
3378
		$tmpvalue = trim($this->GetDefault());
3379
		if (!empty($tmpvalue)) {
3380
			$tree .= " class=\"navlnk\"";
3381
		}
3382
		$tree .= " >" . htmlspecialchars($this->GetQname()) . "</a>";
3383
		$tree .= "</li>";
3384
		return $tree;
3385
	}
3386

    
3387
	/* Even this should take children into consideration */
3388
	function build_rules(&$default = false) {
3389
		$pfq_rule = "queue ". $this->qname;
3390
		if ($this->GetInterface()) {
3391
			$pfq_rule .= " on ".get_real_interface($this->GetInterface());
3392
		}
3393
		if ($this->GetBandwidth() && $this->GetBwscale()) {
3394
			$pfq_rule .= " bandwidth ".trim($this->GetBandwidth()).$this->GetBwscale();
3395
		}
3396
		$tmpvalue = trim($this->GetQpriority());
3397
		if (is_numeric($tmpvalue)) {
3398
			$pfq_rule .= " priority " . $this->GetQpriority();
3399
		}
3400
		$tmpvalue = trim($this->GetQlimit());
3401
		if (!empty($tmpvalue)) {
3402
			$pfq_rule .= " qlimit " . $this->GetQlimit();
3403
		}
3404
		if ($this->GetDefault() || $this->GetRed() || $this->GetRio() ||
3405
		    $this->GetEcn() || $this->GetBuckets() || $this->GetHogs() || $this->GetCodel()) {
3406
			$pfq_rule .= " fairq ( ";
3407
			$tmpvalue = trim($this->GetRed());
3408
			if (!empty($tmpvalue)) {
3409
				$comma = 1;
3410
				$pfq_rule .= " red ";
3411
			}
3412
			$tmpvalue = trim($this->GetCodel());
3413
			if (!empty($tmpvalue)) {
3414
				$comma = 1;
3415
				$pfq_rule .= " codel ";
3416
			}
3417
			$tmpvalue = trim($this->GetRio());
3418
			if (!empty($tmpvalue)) {
3419
				if ($comma) {
3420
					$pfq_rule .= " ,";
3421
				}
3422
				$comma = 1;
3423
				$pfq_rule .= " rio ";
3424
			}
3425
			$tmpvalue = trim($this->GetEcn());
3426
			if (!empty($tmpvalue)) {
3427
				if ($comma) {
3428
					$pfq_rule .= " ,";
3429
				}
3430
				$comma = 1;
3431
				$pfq_rule .= " ecn ";
3432
			}
3433
			$tmpvalue = trim($this->GetDefault());
3434
			if (!empty($tmpvalue)) {
3435
				if ($comma) {
3436
					$pfq_rule .= " ,";
3437
				}
3438
				$comma = 1;
3439
				$pfq_rule .= " default ";
3440
				$default = true;
3441
			}
3442
			$tmpvalue = trim($this->GetBuckets());
3443
			if (!empty($tmpvalue)) {
3444
				if ($comma) {
3445
					$pfq_rule .= ", ";
3446
				}
3447
				$pfq_rule .= " buckets " . $this->GetBuckets() . " ";
3448
			}
3449
			$tmpvalue = trim($this->GetHogs());
3450
			if (!empty($tmpvalue)) {
3451
				if ($comma) {
3452
					$pfq_rule .= ", ";
3453
				}
3454
				$pfq_rule .= " hogs " . $this->GetHogs() . " ";
3455
			}
3456
			$pfq_rule .= " ) ";
3457
		}
3458

    
3459
		$pfq_rule .= " \n";
3460
		return $pfq_rule;
3461
	}
3462

    
3463
	function build_form() {
3464
		$form = parent::build_form();
3465

    
3466
		$section = new Form_Section('');
3467

    
3468
		$group = new Form_Group('Bandwidth');
3469

    
3470
		$group->add(new Form_Input(
3471
			'bandwidth',
3472
			null,
3473
			'number',
3474
			$this->GetBandwidth()
3475
		));
3476

    
3477
		$group->add(new Form_Select(
3478
			'bandwidthtype',
3479
			null,
3480
			$this->FormGetBwscale(),
3481
			array('Kb' => 'Kbit/s',
3482
				  'Mb' => 'Mbit/s',
3483
				  'Gb' => 'Gbit/s',
3484
				  'b' => 'Bit/s',
3485
				  '%' => '%')
3486
		));
3487

    
3488
		$group->setHelp('Choose the amount of bandwidth for this queue');
3489

    
3490
		$section->add($group);
3491

    
3492
		$section->addInput(new Form_Input(
3493
			'buckets',
3494
			'Scheduler specific options',
3495
			'text',
3496
			$this->GetBuckets()
3497
		))->setHelp('Number of buckets available');
3498

    
3499
		$section->addInput(new Form_Input(
3500
			'hogs',
3501
			'',
3502
			'text',
3503
			$this->GetHogs()
3504
			))->setHelp('Bandwidth limit for hosts to not saturate link');
3505

    
3506
		$form->add($section);
3507
		return $form;
3508
	}
3509

    
3510
	function update_altq_queue_data(&$data) {
3511
		$this->ReadConfig($data);
3512
	}
3513

    
3514
	function wconfig() {
3515
		$cflink =& get_reference_to_me_in_config($this->GetLink());
3516
		if (!is_array($cflink)) {
3517
			$cflink = array();
3518
		}
3519
		$cflink['interface'] = $this->GetInterface();
3520
		$cflink['qlimit'] = trim($this->GetQlimit());
3521
		if (empty($cflink['qlimit'])) {
3522
			unset($cflink['qlimit']);
3523
		}
3524
		$cflink['priority'] = trim($this->GetQpriority());
3525
		if (!is_numeric($cflink['priority'])) {
3526
			unset($cflink['priority']);
3527
		}
3528
		$cflink['name'] = $this->GetQname();
3529
		$cflink['description'] = trim($this->GetDescription());
3530
		if (empty($cflink['description'])) {
3531
			unset($cflink['description']);
3532
		}
3533
		$cflink['bandwidth'] = $this->GetBandwidth();
3534
		$cflink['bandwidthtype'] = $this->GetBwscale();
3535
		$cflink['enabled'] = $this->GetEnabled();
3536
		if (empty($cflink['enabled'])) {
3537
			unset($cflink['enabled']);
3538
		}
3539
		$cflink['default'] = trim($this->GetDefault());
3540
		if (empty($cflink['default'])) {
3541
			unset($cflink['default']);
3542
		}
3543
		$cflink['red'] = trim($this->GetRed());
3544
		if (empty($cflink['red'])) {
3545
			unset($cflink['red']);
3546
		}
3547
		$cflink['rio'] = trim($this->GetRio());
3548
		if (empty($cflink['rio'])) {
3549
			unset($cflink['rio']);
3550
		}
3551
		$cflink['ecn'] = trim($this->GetEcn());
3552
		if (empty($cflink['ecn'])) {
3553
			unset($cflink['ecn']);
3554
		}
3555
		$cflink['codel'] = trim($this->GetCodel());
3556
		if (empty($cflink['codel'])) {
3557
			unset($cflink['codel']);
3558
		}
3559
		$cflink['buckets'] = trim($this->GetBuckets());
3560
		if (empty($cflink['buckets'])) {
3561
			unset($cflink['buckets']);
3562
		}
3563
		$cflink['hogs'] = trim($this->GetHogs());
3564
		if (empty($cflink['hogs'])) {
3565
			unset($cflink['hogs']);
3566
		}
3567
	}
3568
}
3569

    
3570

    
3571
/*
3572
 * dummynet(4) wrappers.
3573
 */
3574

    
3575

    
3576
/*
3577
 * List of respective objects!
3578
 */
3579
$dummynet_pipe_list = array();
3580

    
3581
class dummynet_class {
3582
	var $qname;
3583
	var $qnumber; /* dummynet(4) uses numbers instead of names; maybe integrate with pf the same as altq does?! */
3584
	var $qlimit;
3585
	var $description;
3586
	var $qenabled;
3587
	var $link;
3588
	var $qparent; /* link to upper class so we do things easily on WF2Q+ rule creation */
3589
	var $plr;
3590

    
3591
	var $buckets;
3592
	/* mask parameters */
3593
	var $mask;
3594
	var $noerror;
3595

    
3596
	/* Accessor functions */
3597
	function SetLink($link) {
3598
		$this->link = $link;
3599
	}
3600
	function GetLink() {
3601
		return $this->link;
3602
	}
3603
	function GetMask() {
3604
		if (!isset($this->mask["type"])) {
3605
			$this->mask["type"] = "none";
3606
		}
3607
		return $this->mask;
3608
	}
3609
	function SetMask($mask) {
3610
		$this->mask = $mask;
3611
	}
3612
	function &GetParent() {
3613
		return $this->qparent;
3614
	}
3615
	function SetParent(&$parent) {
3616
		$this->qparent = &$parent;
3617
	}
3618
	function GetEnabled() {
3619
		return $this->qenabled;
3620
	}
3621
	function SetEnabled($value) {
3622
		$this->qenabled = $value;
3623
	}
3624
	function CanHaveChildren() {
3625
		return false;
3626
	}
3627
	function CanBeDeleted() {
3628
		return true;
3629
	}
3630
	function GetQname() {
3631
		return $this->qname;
3632
	}
3633
	function SetQname($name) {
3634
		$this->qname = trim($name);
3635
	}
3636
	function GetQlimit() {
3637
		return $this->qlimit;
3638
	}
3639
	function SetQlimit($limit) {
3640
		$this->qlimit = $limit;
3641
	}
3642
	function GetDescription() {
3643
		return $this->description;
3644
	}
3645
	function SetDescription($str) {
3646
		$this->description = trim($str);
3647
	}
3648
	function GetFirstime() {
3649
		return $this->firsttime;
3650
	}
3651
	function SetFirsttime($number) {
3652
		$this->firsttime = $number;
3653
	}
3654
	function GetBuckets() {
3655
		return $this->buckets;
3656
	}
3657
	function SetBuckets($buckets) {
3658
		$this->buckets = $buckets;
3659
	}
3660
	function SetNumber($number) {
3661
		$this->qnumber = $number;
3662
	}
3663
	function GetNumber() {
3664
		return $this->qnumber;
3665
	}
3666
	function GetPlr() {
3667
		return $this->plr;
3668
	}
3669
	function SetPlr($plr) {
3670
		$this->plr = $plr;
3671
	}
3672

    
3673
	function build_javascript() {
3674
		$javascript .= "<script type=\"text/javascript\">\n";
3675
		$javascript .= "//<![CDATA[\n";
3676
		$javascript .= "function enable_maskbits(enable_over) {\n";
3677
		$javascript .= "var e = document.getElementById(\"mask\");\n";
3678
		$javascript .= "if ((e.options[e.selectedIndex].text == \"none\") || enable_over) {\n";
3679
		$javascript .= "document.iform.maskbits.disabled = 1;\n";
3680
		$javascript .= "document.iform.maskbits.value = \"\";\n";
3681
		$javascript .= "document.iform.maskbitsv6.disabled = 1;\n";
3682
		$javascript .= "document.iform.maskbitsv6.value = \"\";\n";
3683
		$javascript .= "} else {\n";
3684
		$javascript .= "document.iform.maskbits.disabled = 0;\n";
3685
		$javascript .= "document.iform.maskbitsv6.disabled = 0;\n";
3686
		$javascript .= "}}\n";
3687
		$javascript .= "//]]>\n";
3688
		$javascript .= "</script>\n";
3689
		return $javascript;
3690
	}
3691

    
3692
	function validate_input($data, &$input_errors) {
3693
		$reqdfields[] = "bandwidth";
3694
		$reqdfieldsn[] = gettext("Bandwidth");
3695
		/*$reqdfields[] = "burst";
3696
		$reqdfieldsn[] = gettext("Burst"); */
3697
		$reqdfields[] = "bandwidthtype";
3698
		$reqdfieldsn[] = gettext("Bandwidthtype");
3699
		$reqdfields[] = "newname";
3700
		$reqdfieldsn[] = gettext("Name");
3701

    
3702
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
3703

    
3704
		if ($data['plr'] && (!is_numeric($data['plr']) ||
3705
		    ($data['plr'] < 0) || ($data['plr'] > 1))) {
3706
			$input_errors[] = gettext("Packet Loss Rate must be a value between 0 and 1.");
3707
		}
3708
		if ($data['buckets'] && (!is_numeric($data['buckets']) ||
3709
		    ($data['buckets'] < 16) || ($data['buckets'] > 65535))) {
3710
			$input_errors[] = gettext("Buckets must be an integer between 16 and 65535.");
3711
		}
3712
		if ($data['qlimit'] && (!is_numeric($data['qlimit']))) {
3713
			$input_errors[] = gettext("Queue limit must be an integer");
3714
		}
3715
		if (!empty($data['newname']) && !preg_match("/^[a-zA-Z0-9_-]+$/", $data['newname'])) {
3716
			$input_errors[] = gettext("Queue names must be alphanumeric and _ or - only.");
3717
		}
3718
		if (!empty($data['name']) && !preg_match("/^[a-zA-Z0-9_-]+$/", $data['name'])) {
3719
			$input_errors[] = gettext("Queue names must be alphanumeric and _ or - only.");
3720
		}
3721
		if (isset($data['maskbits']) && ($data['maskbits'] <> "")) {
3722
			if ((!is_numeric($data['maskbits'])) || ($data['maskbits'] <= 0) || ($data['maskbits'] > 32)) {
3723
				$input_errors[] = gettext("IPv4 bit mask must be blank or numeric value between 1 and 32.");
3724
			}
3725
		}
3726
		if (isset($data['maskbitsv6']) && ($data['maskbitsv6'] <> "")) {
3727
			if ((!is_numeric($data['maskbitsv6'])) || ($data['maskbitsv6'] <= 0) || ($data['maskbitsv6'] > 128)) {
3728
				$input_errors[] = gettext("IPv6 bit mask must be blank or numeric value between 1 and 128.");
3729
			}
3730
		}
3731
	}
3732

    
3733
	function build_mask_rules(&$pfq_rule) {
3734
		$mask = $this->GetMask();
3735
		if (!empty($mask['type'])) {
3736
			if ($mask['type'] <> 'none') {
3737
				$pfq_rule .= " mask";
3738
			}
3739
			switch ($mask['type']) {
3740
				case 'srcaddress':
3741
					if (!empty($mask['bitsv6']) && ($mask['bitsv6'] <> "")) {
3742
						$pfq_rule .= " src-ip6 /" . $mask['bitsv6'];
3743
					} else {
3744
						$pfq_rule .= " src-ip6 /128";
3745
					}
3746
					if (!empty($mask['bits']) && ($mask['bits'] <> "")) {
3747
						$pfq_rule .= sprintf(" src-ip 0x%x", gen_subnet_mask_long($mask['bits']));
3748
					} else {
3749
						$pfq_rule .= " src-ip 0xffffffff";
3750
					}
3751
					break;
3752
				case 'dstaddress':
3753
					if (!empty($mask['bitsv6']) && ($mask['bitsv6'] <> "")) {
3754
						$pfq_rule .= " dst-ip6 /" . $mask['bitsv6'];
3755
					} else {
3756
						$pfq_rule .= " dst-ip6 /128";
3757
					}
3758
					if (!empty($mask['bits']) && ($mask['bits'] <> "")) {
3759
						$pfq_rule .= sprintf(" dst-ip 0x%x", gen_subnet_mask_long($mask['bits']));
3760
					} else {
3761
						$pfq_rule .= " dst-ip 0xffffffff";
3762
					}
3763
					break;
3764
				default:
3765
					break;
3766
			}
3767
		}
3768
	}
3769

    
3770
}
3771

    
3772
class dnpipe_class extends dummynet_class {
3773
	var $delay;
3774
	var $qbandwidth = array();
3775
	var $qbandwidthtype;
3776

    
3777
	/* Limiter queue patch */
3778
	var $ecn; // ecn 'on' or 'off'
3779
	var $pie_onoff;
3780
	var $pie_capdrop;
3781
	var $pie_qdelay;
3782
	var $pie_pderand;
3783
	var $aqm; // key to aqm_map
3784
	var $aqm_params = array(); // AQM params
3785
	var $scheduler;	// key to scheduler_map
3786
	var $scheduler_params = array(); // AQM params
3787
	function GetAQM() {
3788
			return $this->aqm;
3789
	}
3790
	function SetAQM($aqm) {
3791
			$this->aqm = $aqm;
3792
	}
3793
	function GetAQMParameters() {
3794
			return $this->aqm_params;
3795
	}
3796
	function GetAQMParameter($parameter) {
3797
			return $this->aqm_params[$parameter];
3798
	}
3799
	function SetAQMParameter($key, $value) {
3800
			return $this->aqm_params[$key] = $value;
3801
	}
3802
	function SetAQMParameters($params) {
3803
			$this->aqm_params = $params;
3804
	}
3805
	function GetECN() {
3806
			return $this->ecn;
3807
	}
3808
	function SetECN($ecn) {
3809
			$this->ecn = $ecn;
3810
	}
3811
	function GetPIE_ONOFF() {
3812
			return $this->pie_onoff;
3813
	}
3814
	function SetPIE_ONOFF($pie_onoff) {
3815
			$this->pie_onoff = $pie_onoff;
3816
	}
3817
	function GetPIE_CAPDROP() {
3818
			return $this->pie_capdrop;
3819
	}
3820
	function SetPIE_CAPDROP($pie_capdrop) {
3821
			$this->pie_capdrop = $pie_capdrop;
3822
	}
3823
	function GetPIE_QDELAY() {
3824
			return $this->pie_qdelay;
3825
	}
3826
	function SetPIE_QDELAY($pie_qdelay) {
3827
			$this->pie_qdelay = $pie_qdelay;
3828
	}
3829
	function GetPIE_PDERAND() {
3830
			return $this->pie_pderand;
3831
	}
3832
	function SetPIE_PDERAND($pie_pderand) {
3833
			$this->pie_pderand = $pie_pderand;
3834
	}
3835
	function GetScheduler() {
3836
			return $this->scheduler;
3837
	}
3838
	function SetScheduler($scheduler) {
3839
			$this->scheduler = $scheduler;
3840
	}
3841
	function GetSchedulerParameters() {
3842
			return $this->scheduler_params;
3843
	}
3844
	function SetSchedulerParameters($params) {
3845
			$this->scheduler_params = $params;
3846
	}
3847
	function SetSchedulerParameter($key, $value) {
3848
			$this->scheduler_params[$key] = $value;
3849
	}
3850
	function GetSchedulerParameter($key) {
3851
			return $this->scheduler_params[$key];
3852
	}
3853
	/* End limiter queue patch */
3854

    
3855
		/* This is here to help on form building and building rules/lists */
3856
	var $subqueues = array();
3857

    
3858
	function CanHaveChildren() {
3859
		return true;
3860
	}
3861
	function SetDelay($delay) {
3862
		$this->delay = $delay;
3863
	}
3864
	function GetDelay() {
3865
		return $this->delay;
3866
	}
3867
	function delete_queue() {
3868
		cleanup_dnqueue_from_rules($this->GetQname());
3869
		foreach ($this->subqueues as $q) {
3870
			$q->delete_queue();
3871
		}
3872
		unset_dn_object_by_reference($this->GetLink());
3873
		mwexec("/sbin/dnctl pipe delete " . $this->GetNumber());
3874
		mwexec("/sbin/dnctl sched delete " . $this->GetNumber());
3875
	}
3876
	function GetBandwidth() {
3877
		return $this->qbandwidth;
3878
	}
3879
	function SetBandwidth($bandwidth) {
3880
		$this->qbandwidth = $bandwidth;
3881
	}
3882
	function GetBurst() {
3883
		return $this->qburst;
3884
	}
3885
	function SetBurst($burst) {
3886
		$this->qburst = $burst;
3887
	}
3888

    
3889
	function &add_queue($interface, &$queue, &$path, &$input_errors) {
3890

    
3891
		if (!is_array($this->subqueues)) {
3892
			$this->subqueues = array();
3893
		}
3894

    
3895
		$__tmp_q = new dnqueue_class(); $q =& $__tmp_q;
3896
		$q->SetLink($path);
3897
		$q->SetEnabled("on");
3898
		$q->SetPipe($this->GetQname());
3899
		$q->SetParent($this);
3900
		$q->ReadConfig($queue);
3901
		$q->validate_input($queue, $input_errors);
3902

    
3903
		if (!is_array($input_errors)) {
3904
			$input_errors = array();
3905
		}
3906

    
3907
		if (count($input_errors)) {
3908
			log_error(sprintf(gettext('SHAPER: Could not create queue %1$s on interface %2$s because: %3$s'), $q->GetQname(), $interface, print_r($input_errors, true)));
3909
			return $q;
3910
		}
3911
		$number = dnqueue_find_nextnumber();
3912
		$q->SetNumber($number);
3913
		$this->subqueues[$q->GetQname()] = &$q;
3914

    
3915
		return $q;
3916
	}
3917

    
3918
	function &get_queue_list(&$q = null) {
3919
		$qlist = array();
3920

    
3921
		$qlist[$this->GetQname()] = $this->GetNumber();
3922
		if (is_array($this->subqueues)) {
3923
			foreach ($this->subqueues as $queue) {
3924
				$queue->get_queue_list($qlist);
3925
			}
3926
		}
3927
		return $qlist;
3928
	}
3929

    
3930
	/*
3931
	 * Should search even its children
3932
	 */
3933
	function &find_queue($pipe, $qname) {
3934
		if ($qname == $this->GetQname()) {
3935
			return $this;
3936
		}
3937
		foreach ($this->subqueues as $q) {
3938
			$result =& $q->find_queue("", $qname);
3939
			if ($result) {
3940
				return $result;
3941
			}
3942
		}
3943
	}
3944

    
3945
	function &find_parentqueue($pipe, $qname) {
3946
		return NULL;
3947
	}
3948

    
3949
	function validate_input($data, &$input_errors) {
3950
		parent::validate_input($data, $input_errors);
3951

    
3952
		$schedule = 0;
3953
		$schedulenone = 0;
3954
		$entries = 0;
3955
		/* XXX: Really no better way? */
3956
		for ($i = 0; $i < 2900; $i++) {
3957
			if (!empty($data["bwsched{$i}"])) {
3958
				if ($data["bwsched{$i}"] != "none") {
3959
					$schedule++;
3960
				} else {
3961
					$schedulenone++;
3962
				}
3963
			}
3964
			if (!empty($data["bandwidth{$i}"])) {
3965
				if (!is_numeric($data["bandwidth{$i}"])) {
3966
					$input_errors[] = sprintf(gettext("Bandwidth for schedule %s must be an integer."), $data["bwsched{$i}"]);
3967
				} else if (($data["burst{$i}"] != "") && (!is_numeric($data["burst{$i}"]))) {
3968
					$input_errors[] = sprintf(gettext("Burst for schedule %s must be an integer."), $data["bwsched{$i}"]);
3969
				} else {
3970
					$entries++;
3971
				}
3972
			}
3973
		}
3974
		if ($schedule == 0 && $entries > 1) {
3975
			$input_errors[] = gettext("A schedule needs to be specified for every additional entry.");
3976
		}
3977
		if ($schedulenone > 0 && $entries > 1) {
3978
			$input_errors[] = gettext("If more than one bandwidth configured all schedules need to be selected.");
3979
		}
3980
		if ($entries == 0) {
3981
			$input_errors[] = gettext("At least one bw specification is necessary.");
3982
		}
3983
		if ($data['delay'] && (!is_numeric($data['delay']))) {
3984
			$input_errors[] = gettext("Delay must be an integer.");
3985
		}
3986
		if ($data['delay'] && is_numeric($data['delay']) &&
3987
		    (($data['delay'] < 0) || ($data['delay'] > 10000))) {
3988
			$input_errors[] = gettext("Delay must be an integer between 0 and 10000.");
3989
		}
3990

    
3991
		/* Limiter patch */
3992
		$selectedScheduler = getSchedulers()[$data['sched']];
3993
		if (!$selectedScheduler) {
3994
			$input_errors[] = gettext("Selected scheduler not recognized.");
3995
		}
3996
		$selectedAqm = getAQMs()[$data['aqm']];
3997
		if (!empty($data['aqm']) && !$selectedAqm) {
3998
			$input_errors[] = gettext("Selected AQM not recognized.");
3999
		}
4000
		/* End limiter patch */
4001
	}
4002

    
4003
	function ReadConfig(&$q) {
4004
		if (!empty($q['name']) && !empty($q['newname']) && $q['name'] != $q['newname']) {
4005
			$this->SetQname($q['newname']);
4006
			rename_dnqueue_in_rules($q['name'], $q['newname']);
4007
		} else if (!empty($q['newname'])) {
4008
			$this->SetQname($q['newname']);
4009
		} else {
4010
			$this->SetQname($q['name']);
4011
		}
4012
		$this->SetNumber($q['number']);
4013

    
4014
		if (!empty($_POST)) {
4015
			$bandwidth = array();
4016
			/* XXX: Really no better way? */
4017
			for ($i = 0; $i < 2900; $i++) {
4018
				if (isset($q["bandwidth{$i}"]) && $q["bandwidth{$i}"] <> "") {
4019
					$bw = array();
4020
					$bw['bw'] = $q["bandwidth{$i}"];
4021
					$bw['burst'] = $q["burst{$i}"];
4022
					if (isset($q["bwtype{$i}"]) && $q["bwtype{$i}"]) {
4023
						$bw['bwscale'] = $q["bwtype{$i}"];
4024
					}
4025
					if (isset($q["bwsched{$i}"]) && $q["bwsched{$i}"]) {
4026
						$bw['bwsched'] = $q["bwsched{$i}"];
4027
					}
4028
					$bandwidth[] = $bw;
4029
				}
4030
			}
4031
			$this->SetBandwidth($bandwidth);
4032
		}
4033

    
4034
		if (is_array($q['bandwidth']) && is_array($q['bandwidth']['item'])) {
4035
			$this->SetBandwidth($q['bandwidth']['item']);
4036
			$this->SetBurst($q['burst']['item']);
4037
		}
4038

    
4039
		if (isset($q['qlimit']) && $q['qlimit'] <> "") {
4040
			$this->SetQlimit($q['qlimit']);
4041
		} else {
4042
			$this->SetQlimit("");
4043
		}
4044
		if (isset($q['mask']) && $q['mask'] <> "") {
4045
			$masktype = $q['mask'];
4046
		} else {
4047
			$masktype = "";
4048
		}
4049
		if (isset($q['maskbits']) && $q['maskbits'] <> "") {
4050
			$maskbits = $q['maskbits'];
4051
		} else {
4052
			$maskbits = "";
4053
		}
4054
		if (isset($q['maskbitsv6']) && $q['maskbitsv6'] <> "") {
4055
			$maskbitsv6 = $q['maskbitsv6'];
4056
		} else {
4057
			$maskbitsv6 = "";
4058
		}
4059
		$this->SetMask(array("type" => $masktype, "bits" => $maskbits, "bitsv6" => $maskbitsv6));
4060
		if (isset($q['buckets']) && $q['buckets'] <> "") {
4061
			$this->SetBuckets($q['buckets']);
4062
		} else {
4063
			$this->SetBuckets("");
4064
		}
4065
		if (isset($q['plr']) && $q['plr'] <> "") {
4066
			$this->SetPlr($q['plr']);
4067
		} else {
4068
			$this->SetPlr("");
4069
		}
4070
		if (isset($q['delay']) && $q['delay'] <> "") {
4071
			$this->SetDelay($q['delay']);
4072
		} else {
4073
			$this->SetDelay(0);
4074
		}
4075
		if (isset($q['description']) && $q['description'] <> "") {
4076
			$this->SetDescription($q['description']);
4077
		} else {
4078
			$this->SetDescription("");
4079
		}
4080
		$this->SetEnabled($q['enabled']);
4081

    
4082
		/* Limiter patch */
4083
		if (isset($q['aqm']) && $q['aqm'] <> "") {
4084
				$this->SetAQM($q['aqm']);
4085
		} else {
4086
				$this->SetAQM('droptail');
4087
		}
4088
		// parse AQM arguments
4089
		$aqm_map = getAQMs();
4090
		$selectedParameters = $aqm_map[$this->getAQM()]["parameters"];
4091
		if (is_array($selectedParameters)) {
4092
			foreach ($selectedParameters as $key => $value) {
4093
				$config_key = 'param_' . $this->GetAQM() . '_' . $key;
4094
				if (isset($q[$config_key]) && $q[$config_key] <> "") {
4095
					$this->SetAQMParameter($key, $q[$config_key]);
4096
				} else {
4097
					$this->SetAQMParameter($key, $value["default"]);
4098
				}
4099
			}
4100
		}
4101

    
4102
		if (isset($q['sched']) && $q['sched'] <> "") {
4103
				$this->SetScheduler($q['sched']);
4104
		} else {
4105
				$this->SetScheduler('fifo');
4106
		}
4107
		$scheduler_map = getSchedulers();
4108
		$selectedParameters = $scheduler_map[$this->getScheduler()]["parameters"];
4109
		if (is_array($selectedParameters)) {
4110
			foreach ($selectedParameters as $key => $value) {
4111
				$config_key = 'param_' . $this->GetScheduler() . '_' . $key;
4112
				if (isset($q[$config_key]) && $q[$config_key] <> "") {
4113
					$this->SetSchedulerParameter($key, $q[$config_key]);
4114
				} else {
4115
					$this->SetSchedulerParameter($key, $value["default"]);
4116
				}
4117
			}
4118
		}
4119

    
4120
		// ecn flag
4121
		$this->SetECN($q['ecn']);
4122
		// PIE Flags.
4123
		$this->SetPIE_ONOFF($q['pie_onoff']);
4124
		$this->SetPIE_CAPDROP($q['pie_capdrop']);
4125
		$this->SetPIE_QDELAY($q['pie_qdelay']);
4126
		$this->SetPIE_PDERAND($q['pie_pderand']);
4127
		/* End limiter patch */
4128
	}
4129

    
4130
	function build_tree() {
4131
		$tree = " <li><a href=\"firewall_shaper_vinterface.php?pipe=" . htmlspecialchars($this->GetQname()) ."&amp;queue=".htmlspecialchars($this->GetQname()) ."&amp;action=show\">";
4132
		$tree .= htmlspecialchars($this->GetQname()) . "</a>";
4133
		if (is_array($this->subqueues)) {
4134
			$tree .= "<ul>";
4135
			foreach ($this->subqueues as $q) {
4136
				$tree .= $q->build_tree();
4137
			}
4138
			$tree .= "</ul>";
4139
		}
4140
		$tree .= "</li>";
4141

    
4142
		return $tree;
4143
	}
4144

    
4145
	function build_rules() {
4146
		global $config, $time_based_rules;
4147

    
4148
		if ($this->GetEnabled() == "") {
4149
			return;
4150
		}
4151

    
4152
		$pfq_rule = "\npipe ". $this->GetNumber() . " config ";
4153
		$found = false;
4154
		$bandwidth = $this->GetBandwidth();
4155
		if (is_array($bandwidth)) {
4156
			foreach ($bandwidth as $bw) {
4157
				if ($bw['bwsched'] != "none") {
4158
					if (is_array($config['schedules']) && is_array($config['schedules']['schedule'])) {
4159
						foreach ($config['schedules']['schedule'] as $schedule) {
4160
							if ($bw['bwsched'] == $schedule['name']) {
4161
								if (filter_get_time_based_rule_status($schedule)) {
4162
									/* pipe throughputs must always be an integer, enforce that restriction again here. */
4163
									$pfq_rule .= " bw ".round(trim($bw['bw']),0).$bw['bwscale'];
4164
									if (is_numeric($bw['burst']) && ($bw['burst'] > 0)) {
4165
										$pfq_rule .= " burst ".trim($bw['burst']);
4166
									}
4167
									$found = true;
4168
									break;
4169
								}
4170
							}
4171
						}
4172
					} else {
4173
						$pfq_rule .= " bw 0";
4174
						$found = true;
4175
						break;
4176
					}
4177
				} else {
4178
					/* pipe throughputs must always be an integer, enforce that restriction again here. */
4179
					$pfq_rule .= " bw ".round(trim($bw['bw']), 0).$bw['bwscale'];
4180
					if (is_numeric($bw['burst']) && ($bw['burst'] > 0)) {
4181
						$pfq_rule .= " burst ".trim($bw['burst']);
4182
					}
4183
					$found = true;
4184
					break;
4185
				}
4186
			}
4187
			if ($found == false) {
4188
				$pfq_rule .= " bw 0";
4189
			}
4190
		} else {
4191
			$pfq_rule .= " bw 0";
4192
		}
4193

    
4194
		if ($this->GetQlimit()) {
4195
			$pfq_rule .= " queue " . $this->GetQlimit();
4196
		}
4197
		if ($this->GetPlr()) {
4198
			$pfq_rule .= " plr " . $this->GetPlr();
4199
		}
4200
		if ($this->GetBuckets()) {
4201
			$pfq_rule .= " buckets " . $this->GetBuckets();
4202
		}
4203
		if ($this->GetDelay()) {
4204
			$pfq_rule .= " delay " . $this->GetDelay();
4205
		}
4206
		$this->build_mask_rules($pfq_rule);
4207

    
4208
		/* Limiter patch */
4209
		$selectedAQM = getAQMs()[$this->getAQM()];
4210
		if ($selectedAQM) {
4211
			$pfq_rule .= " " . FormatParameters($selectedAQM["parameter_format"], $this->GetAQMParameters());
4212
			if ($selectedAQM["ecn"]) {
4213
				if ($this->getECN() == 'on') {
4214
					$pfq_rule .= ' ecn';
4215
				} elseif (($this->getAQM() != 'red') && ($this->getAQM() != 'gred')) {
4216
					$pfq_rule .= ' noecn';
4217
				}
4218
			}
4219
		}
4220
		$selectedScheduler = getSchedulers()[$this->getScheduler()];
4221
		if ($selectedScheduler) {
4222
			$pfq_rule .= "\nsched ". $this->GetNumber() . " config ";
4223
			$pfq_rule .= "pipe ". $this->GetNumber();
4224
			$this->build_mask_rules($pfq_rule);
4225
			$pfq_rule .= " " . FormatParameters($selectedScheduler["parameter_format"], $this->GetSchedulerParameters());			
4226
			if ($selectedScheduler["ecn"]) {
4227
				if ($this->getECN() == 'on') {
4228
					$pfq_rule .= ' ecn';
4229
				} else {
4230
					$pfq_rule .= ' noecn';
4231
				}
4232
			}
4233
			if ($selectedScheduler["pie_onoff"]) {
4234
				if ($this->getPIE_ONOFF() == 'on') {
4235
					$pfq_rule .= 'onoff';
4236
				} else {
4237
					$pfq_rule .= '';
4238
				}
4239
			}
4240
			if ($selectedScheduler["pie_capdrop"]) {
4241
				if ($this->getPIE_CAPDROP() == 'on') {
4242
					$pfq_rule .= ' capdrop';
4243
				} else {
4244
					$pfq_rule .= ' nocapdrop';
4245
				}
4246
			}
4247
			if ($selectedScheduler["pie_qdelay"]) {
4248
				if ($this->getPIE_QDELAY() == 'on') {
4249
					$pfq_rule .= ' ts';
4250
				} else {
4251
					$pfq_rule .= ' dre';
4252
				}
4253
			}
4254
			if ($selectedScheduler["pie_pderand"]) {
4255
				if ($this->getPIE_PDERAND() == 'on') {
4256
					$pfq_rule .= ' derand';
4257
				} else {
4258
					$pfq_rule .= ' noderand';
4259
				}
4260
			}
4261
		}
4262
		$pfq_rule .= "\n";
4263
		/* End patch */
4264

    
4265
		if (!empty($this->subqueues) && count($this->subqueues) > 0) {
4266
			foreach ($this->subqueues as $q) {
4267
				$pfq_rule .= $q->build_rules();
4268
			}
4269
		}
4270

    
4271
		$pfq_rule .= " \n";
4272

    
4273
		return $pfq_rule;
4274
	}
4275

    
4276
	function update_dn_data(&$data) {
4277
		$this->ReadConfig($data);
4278
	}
4279

    
4280
	function build_javascript() {
4281
		global $g, $config;
4282

    
4283
		$javasr = parent::build_javascript();
4284

    
4285
		//build list of schedules
4286
		$schedules = "<option value='none'>none</option>";
4287
		if (is_array($config['schedules']) && is_array($config['schedules']['schedule'])) {
4288
			foreach ($config['schedules']['schedule'] as $schedule) {
4289
				if ($schedule['name'] <> "") {
4290
					$schedules .= "<option value='{$schedule['name']}'>{$schedule['name']}</option>";
4291
				}
4292
			}
4293
		}
4294
		$bwopt = "";
4295
		foreach (array("Kb" => "Kbit/s", "Mb" => "Mbit/s", "Gb" => "Gbit/s", "b" => "Bit/s") as $bwidx => $bw) {
4296
			$bwopt .= "<option value='{$bwidx}'>{$bw}</option>";
4297
		}
4298

    
4299
		$javasr .= <<<EOD
4300
<script type='text/javascript'>
4301
//<![CDATA[
4302
var addBwRowTo = (function() {
4303

    
4304
	return (function (tableId) {
4305

    
4306
	var table = document.getElementById(tableId);
4307
	var totalrows = table.rows.length -1;
4308

    
4309
	var row = table.insertRow(totalrows + 1);
4310
	var cell1 = row.insertCell(0);
4311
	var cell2 = row.insertCell(1);
4312
	var cell3 = row.insertCell(2);
4313
	var cell4 = row.insertCell(3);
4314

    
4315
	cell1.innerHTML = "<input type='hidden' value='" + totalrows +"' name='bandwidth_row-" + totalrows + "' /><input type='text' class='form-control' name='bandwidth" + totalrows + "' id='bandwidth" + totalrows + "' />";
4316
	cell2.innerHTML = "<input type='hidden' value='" + totalrows +"' name='bwtype_row-" + totalrows + "' /><select class='form-control' name='bwtype" + totalrows + "'>{$bwopt}</select>";
4317
	cell3.innerHTML = "<input type='hidden' value='" + totalrows +"' name='bwsched_row-" + totalrows + "' /><select class='form-control' name='bwsched" + totalrows + "'>{$schedules}</select>";
4318
	cell4.innerHTML = '<a class="btn btn-warning" onclick="removeBwRow(this); return false;" href="#"><i class="fa fa-trash icon-embed-btn"></i>Delete</a>';
4319

    
4320
	});
4321
})();
4322

    
4323
function removeBwRow(el) {
4324
	var d = el.parentNode.parentNode.rowIndex;
4325
	document.getElementById('maintable').deleteRow(d);
4326
}
4327

    
4328
function ceil_func(el){
4329
	el.value = Math.ceil(el.value);
4330

    
4331
}
4332
//]]>
4333
</script>
4334

    
4335
EOD;
4336

    
4337
		return $javasr;
4338
	}
4339

    
4340
	// Compose a table of bandwidths that can then be inserted into the form using a Form_StaticText
4341
	// The table has been "Bootstrapped" to match the web design while maintaining compatibility with
4342
	// with the javascript in this class
4343
	function build_bwtable() {
4344
		global $config;
4345

    
4346
		$bandwidth = $this->GetBandwidth();
4347
				//build list of schedules
4348
		$schedules = array();
4349
		$schedules[] = "none";//leave none to leave rule enabled all the time
4350
		if (is_array($config['schedules']) && is_array($config['schedules']['schedule'])) {
4351
			foreach ($config['schedules']['schedule'] as $schedule) {
4352
				if ($schedule['name'] != "") {
4353
					$schedules[] = $schedule['name'];
4354
				}
4355
			}
4356
		}
4357

    
4358
		$form = '<div class="table-responsive">';
4359
		$form .= '<table id="maintable" class="table table-hover table-striped">';
4360
		$form .= "<thead><tr>";
4361
		$form .= "<th>Bandwidth</th>";
4362
		//$form .= "<td width='35%'><div id='fifthcolumn'>Burst</div></td>";
4363
		$form .= "<th>Bw type</th>";
4364
		$form .= "<th>Schedule</th>";
4365
		$form .= "<th></th>";
4366
		$form .= "</tr></thead>";
4367
		$form .= "<tbody>";
4368

    
4369
		// If there are no bandwidths defined, make a blank one for convenience
4370
		if (empty($bandwidth)) {
4371
			$bandwidth = array(0 => array('bw' => '', 'bwscale' => 'Mb', 'bwsched' => 'none'));
4372
		}
4373

    
4374
		if (is_array($bandwidth)) {
4375
			foreach ($bandwidth as $bwidx => $bw) {
4376
				$form .= '<tr>';
4377
				$form .= '<td class="col-xs-4">';
4378
				$form .= "<input class='form-control' onchange=\"ceil_func(this)\" type=\"number\" id=\"bandwidth{$bwidx}\" name=\"bandwidth{$bwidx}\" value=\"" . ceil2($bw['bw']) ."\" min=\"0\" step=\"1\"/>";
4379
				//$form .= "</td><td width='20%'>";
4380
				//$form .= "<input class='formfld unknown' size='10' type=\"text\" id=\"burst{$bwidx}\" name=\"burst{$bwidx}\" value=\"{$bw['burst']}\" />";
4381
				$form .= "</td>";
4382
				$form .= '<td class="col-xs-4">';
4383
				$form .= "<select id=\"bwtype{$bwidx}\" name=\"bwtype{$bwidx}\" class=\"form-control\">";
4384

    
4385
				foreach (array("Kb" => "Kbit/s", "Mb" => "Mbit/s", "b" => "Bit/s") as $bwsidx => $bwscale) {
4386
					$form .= "<option value=\"{$bwsidx}\"";
4387

    
4388
					if ($bw['bwscale'] == $bwsidx) {
4389
						$form .= " selected";
4390
					}
4391

    
4392
					$form .= ">{$bwscale}</option>";
4393
				}
4394

    
4395
				$form .= "</select>";
4396
				$form .= "</td>";
4397
				$form .= '<td class="col-xs-4">';
4398
				$form .= "<select id=\"bwsched{$bwidx}\" name=\"bwsched{$bwidx}\" class=\"form-control\">";
4399

    
4400
				foreach ($schedules as $schd) {
4401
					$selected = "";
4402
					if ($bw['bwsched'] == $schd) {
4403
						$selected = "selected";
4404
					}
4405

    
4406
					$form .= "<option value='{$schd}' {$selected}>{$schd}</option>";
4407
				}
4408

    
4409
				$form .= "</select>";
4410
				$form .= "</td>";
4411
				$form .= '<td>';
4412
				$form .= '<a class="btn btn-warning" onclick="removeBwRow(this); return false;"><i class="fa fa-trash icon-embed-btn"></i>' . gettext('Delete') . '</a>';
4413
				$form .= "</td></tr>";
4414
			}
4415
		}
4416
		$form .= "</tbody></table></div><br />";
4417

    
4418
		$form .= '<a class="btn btn-sm btn-success" onclick="javascript:addBwRowTo(\'maintable\'); return false;" >';
4419
		$form .= '<i class="fa fa-plus icon-embed-btn"></i>';
4420
		$form .= gettext("Add Schedule") . "</a>";
4421

    
4422
		return($form);
4423
	}
4424

    
4425
	function build_form() {
4426
		global $g, $config, $pipe, $action, $qname;
4427

    
4428
		//build list of schedules
4429
		$schedules = array();
4430
		$schedules[] = "none";//leave none to leave rule enabled all the time
4431
		if (is_array($config['schedules']) && is_array($config['schedules']['schedule'])) {
4432
			foreach ($config['schedules']['schedule'] as $schedule) {
4433
				if ($schedule['name'] <> "") {
4434
					$schedules[] = $schedule['name'];
4435
				}
4436
			}
4437
		}
4438

    
4439

    
4440
		$sform = new Form();
4441
		$sform->setAction("firewall_shaper.php");
4442

    
4443
		$section = new Form_Section('Limiters');
4444

    
4445
		$section->addInput(new Form_Checkbox(
4446
			'enabled',
4447
			'Enable',
4448
			'Enable limiter and its children',
4449
			($this->GetEnabled() == "on"),
4450
			'on'
4451
		));
4452

    
4453
		$section->addInput(new Form_Input(
4454
			'newname',
4455
			'*Name',
4456
			'text',
4457
			$this->GetQname()
4458
		));
4459

    
4460
		$section->addInput(new Form_Input(
4461
			'name',
4462
			null,
4463
			'hidden',
4464
			$this->GetQname()
4465
		));
4466

    
4467
		if ($this->GetNumber() > 0) {
4468
			$section->addInput(new Form_Input(
4469
				'number',
4470
				null,
4471
				'hidden',
4472
				$this->GetNumber()
4473
			));
4474
		}
4475

    
4476
		$bandwidth = $this->GetBandwidth();
4477

    
4478
		if (is_array($bandwidth)) {
4479
				$section->addInput(new Form_StaticText(
4480
				'Bandwidth',
4481
				$this->build_bwtable()
4482
			));
4483
		}
4484

    
4485
		$mask = $this->GetMask();
4486

    
4487
		$section->addInput(new Form_Select(
4488
			'mask',
4489
			'Mask',
4490
			$mask['type'],
4491
			array('none' => gettext('None'), 'srcaddress' => gettext('Source addresses'), 'dstaddress' => gettext('Destination addresses'))
4492
		))->setHelp('If "source" or "destination" slots is chosen a dynamic pipe with the bandwidth, delay, packet loss ' .
4493
					'and queue size given above will be created for each source/destination IP address encountered, respectively. ' .
4494
					'This makes it possible to easily specify bandwidth limits per host or subnet.');
4495

    
4496
		$group = new Form_Group(null);
4497

    
4498
		$group->add(new Form_Select(
4499
			'maskbits',
4500
			null,
4501
			$mask['bits'],
4502
			array_combine(range(32, 1, -1), range(32, 1, -1))
4503
		))->setHelp('IPv4 mask bits%1$s%2$s', '<br />', '255.255.255.255/?');
4504

    
4505
		$group->add(new Form_Select(
4506
			'maskbitsv6',
4507
			null,
4508
			$mask['bitsv6'],
4509
			array_combine(range(128, 1, -1), range(128, 1, -1))
4510
		))->setHelp('IPv6 mask bits%1$s%2$s', '<br />', '<span style="font-family:consolas">ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/?</span>');
4511

    
4512
		$section->add($group);
4513

    
4514
		$section->addInput(new Form_Input(
4515
			'description',
4516
			'Description',
4517
			'text',
4518
			$this->GetDescription()
4519
		))->setHelp('A description may be entered here for administrative reference (not parsed).');
4520

    
4521
		$sform->add($section);
4522

    
4523
		/* Begin limiter patch */
4524
		$aqm_map = getAQMs();
4525
		$scheduler_map = getSchedulers();
4526

    
4527
		$section = new Form_Section('Queue');
4528
		$section->addInput(new Form_Select(
4529
				'aqm',
4530
				'Queue Management Algorithm',
4531
				$this->GetAQM(),
4532
				array_map_assoc(function ($k, $v) {
4533
					return [$k, $v["name"]];
4534
				}, $aqm_map)
4535
		))->setHelp('Active queue management (AQM) is the intelligent drop of network packets inside the limiter, ' .
4536
						'when it becomes full or gets close to becoming full, with the goal of reducing ' .
4537
						'network congestion.');
4538

    
4539
		$section->addInput(new Form_StaticText(
4540
			'',
4541
			build_queue_params($aqm_map, $this->GetAQM(), $this->GetAQMParameters(), "aqm")
4542
		))->setHelp('Specifies the queue management algorithm parameters.');
4543

    
4544
		$section->addInput(new Form_Select(
4545
				'sched',
4546
				'Scheduler',
4547
				$this->GetScheduler(),
4548
				array_map_assoc(function ($k, $v) {
4549
					return [$k, $v["name"]];
4550
				}, $scheduler_map)
4551
		))->setHelp('The scheduler manages the sequence of network packets in the limiter\'s queue.');
4552

    
4553
		$section->addInput(new Form_StaticText(
4554
			'',
4555
			build_queue_params($scheduler_map, $this->GetScheduler(), $this->GetSchedulerParameters(), "sched")
4556
		))->setHelp('Specifies the scheduler parameters.');
4557

    
4558
		$section->addInput(new Form_Input(
4559
				'qlimit',
4560
				'Queue length',
4561
				'number',
4562
				$this->GetQlimit()
4563
		))->setHelp('Specifies the length of the limiter\'s queue, which the scheduler and AQM are responsible for. ' .
4564
			'This field may be left empty.');
4565

    
4566
		$selectedScheduler = getSchedulers()[$this->getScheduler()];
4567

    
4568
		if ($selectedScheduler["ecn"]) {
4569
			$section->addInput(new Form_Checkbox(
4570
				'ecn',
4571
				'ECN',
4572
				'Enable Explicit Congestion Notification (ECN)',
4573
				($this->GetECN() == "on"),
4574
				'on'
4575
			))->setHelp('ECN sets a reserved TCP flag when the queue is nearing or exceeding capacity. Not all AQMs or schedulers support this.');
4576
		}
4577

    
4578
		if ($selectedScheduler["pie_onoff"]) {
4579
			$section->addInput(new Form_Checkbox(
4580
				'pie_onoff',
4581
				'ONOFF',
4582
				'Enable Onoff (onoff,)',
4583
				($this->GetPIE_ONOFF() == "on"),
4584
				'on'
4585
                	))->setHelp('Enable turning PIE on and off depending on queue load.');
4586
		}
4587

    
4588
		if ($selectedScheduler["pie_capdrop"]) {
4589
			$section->addInput(new Form_Checkbox(
4590
				'pie_capdrop',
4591
				'CAPDROP',
4592
				'Enable Capdrop (capdrop,nocapdrop)',
4593
				($this->GetPIE_CAPDROP() == "on"),
4594
				'on'
4595
			))->setHelp('Enable cap drop adjustment.');
4596
		}
4597

    
4598
		if ($selectedScheduler["pie_qdelay"]) {
4599
                        $section->addInput(new Form_Checkbox(
4600
                                'pie_qdelay',
4601
                                'QUEUE DELAY TYPE',
4602
                                'Enable Type Of Qdelay (ts,dre)',
4603
                                ($this->GetPIE_QDELAY() == "on"),
4604
                                'on'
4605
                        ))->setHelp('Set queue delay type to timestamps (checked) or departure rate estimation (unchecked).');
4606
                }
4607

    
4608
		if ($selectedScheduler["pie_pderand"]) {
4609
			$section->addInput(new Form_Checkbox(
4610
				'pie_pderand',
4611
				'PROB DERAND',
4612
				'Enable Drop Probability De-randomisation (derand,noderand)',
4613
				($this->GetPIE_PDERAND() == "on"),
4614
				'on'
4615
			))->setHelp('Enable (checked) or disable (unchecked) drop probability de-randomisation.');
4616
		}
4617

    
4618
		$sform->add($section);
4619
		/* End limiter patch */
4620

    
4621
		$section = new Form_Section('Advanced Options');
4622

    
4623
		$section->addInput(new Form_Input(
4624
			'delay',
4625
			'Delay (ms)',
4626
			'text',
4627
			$this->GetDelay() > 0 ? $this->GetDelay():null
4628
		))->setHelp('In most cases, zero (0) should specified here (or leave the field empty).');
4629

    
4630
		$section->addInput(new Form_Input(
4631
			'plr',
4632
			'Packet Loss Rate',
4633
			'number',
4634
			$this->GetPlr(),
4635
			['step' => '0.001', 'min' => '0.000']
4636
		))->setHelp('In most cases, zero (0) should be specified here (or leave the field empty). ' .
4637
					'A value of 0.001 means one packet in 1000 gets dropped.');
4638

    
4639
		$section->addInput(new Form_Input(
4640
			'buckets',
4641
			'Bucket size (slots)',
4642
			'number',
4643
			$this->GetBuckets()
4644
		))->setHelp('In most cases, this field should be left empty. It increases the hash size set.');
4645

    
4646
		$sform->add($section);
4647

    
4648
		return($sform);
4649
		}
4650

    
4651
	function wconfig() {
4652
		$cflink =& get_dn_reference_to_me_in_config($this->GetLink());
4653
		if (!is_array($cflink)) {
4654
			$cflink = array();
4655
		}
4656
		$cflink['name'] = $this->GetQname();
4657
		$cflink['number'] = $this->GetNumber();
4658
		$cflink['qlimit'] = $this->GetQlimit();
4659
		$cflink['plr'] = $this->GetPlr();
4660
		$cflink['description'] = $this->GetDescription();
4661

    
4662
		$bandwidth = $this->GetBandwidth();
4663
		if (is_array($bandwidth)) {
4664
			$cflink['bandwidth'] = array();
4665
			$cflink['bandwidth']['item'] = array();
4666
			foreach ($bandwidth as $bwidx => $bw) {
4667
				$cflink['bandwidth']['item'][] = $bw;
4668
			}
4669
		}
4670

    
4671
		$cflink['enabled'] = $this->GetEnabled();
4672
		$cflink['buckets'] = $this->GetBuckets();
4673
		$mask = $this->GetMask();
4674
		$cflink['mask'] = $mask['type'];
4675
		$cflink['maskbits'] = $mask['bits'];
4676
		$cflink['maskbitsv6'] = $mask['bitsv6'];
4677
		$cflink['delay'] = $this->GetDelay();
4678

    
4679
		/* Limiter queue patch */
4680
		$cflink['sched'] = $this->GetScheduler();
4681
		$scheduler_map = getSchedulers();
4682
		$selectedParameters = $scheduler_map[$this->getScheduler()]["parameters"];
4683
		foreach ($selectedParameters as $key => $value) {
4684
			$config_key = 'param_' . $this->GetScheduler() . '_' . $key;
4685
			$cflink[$config_key] = $this->GetSchedulerParameter($key);
4686
		}
4687
		$cflink['aqm'] = $this->GetAQM();
4688
		$aqm_map = GetAQMs();
4689
		$selectedParameters = $aqm_map[$this->getAQM()]["parameters"];
4690
		foreach ($selectedParameters as $key => $value) {
4691
			$config_key = 'param_' . $this->GetAQM() . '_' . $key;
4692
			$cflink[$config_key] = $this->GetAQMParameter($key);
4693
		}
4694
		$cflink['ecn'] = $this->GetECN();
4695

    
4696
		$selectedScheduler = getSchedulers()[$this->getScheduler()];
4697
		if ($selectedScheduler["pie_onoff"]) {
4698
			$cflink['pie_onoff'] = $this->GetPIE_ONOFF();
4699
		}
4700
		if ($selectedScheduler["pie_capdrop"]) {
4701
			$cflink['pie_capdrop'] = $this->GetPIE_CAPDROP();
4702
		}
4703
		if ($selectedScheduler["pie_qdelay"]) {
4704
			$cflink['pie_qdelay'] = $this->GetPIE_QDELAY();
4705
		}
4706
		if ($selectedScheduler["pie_pderand"]) {
4707
			$cflink['pie_pderand'] = $this->GetPIE_PDERAND();
4708
		}
4709
		/* End limiter queue patch */
4710
	}
4711

    
4712
}
4713

    
4714
class dnqueue_class extends dummynet_class {
4715
	var $pipeparent;
4716
	var $weight;
4717
	/* Limiter queue patch */
4718
    var $ecn; // ecn 'on' or 'off'
4719
	var $pie_onoff;
4720
	var $pie_capdrop;
4721
	var $pie_qdelay;
4722
	var $pie_pderand;
4723
    var $aqm; // key to aqm_map
4724
    var $aqm_params = array(); // AQM params
4725
	function GetAQM() {
4726
			return $this->aqm;
4727
	}
4728
	function SetAQM($aqm) {
4729
			$this->aqm = $aqm;
4730
	}
4731
	function GetAQMParameters() {
4732
			return $this->aqm_params;
4733
	}
4734
	function GetAQMParameter($parameter) {
4735
			return $this->aqm_params[$parameter];
4736
	}
4737
	function SetAQMParameter($key, $value) {
4738
			return $this->aqm_params[$key] = $value;
4739
	}
4740
	function SetAQMParameters($params) {
4741
			$this->aqm_params = $params;
4742
	}
4743
	function GetECN() {
4744
			return $this->ecn;
4745
	}
4746
	function SetECN($ecn) {
4747
			$this->ecn = $ecn;
4748
	}
4749
	function GetPIE_ONOFF() {
4750
			return $this->pie_onoff;
4751
	}
4752
	function SetPIE_ONOFF($pie_onoff) {
4753
			$this->pie_onoff = $pie_onoff;
4754
	}
4755
	function GetPIE_CAPDROP() {
4756
			return $this->pie_capdrop;
4757
	}
4758
	function SetPIE_CAPDROP($pie_capdrop) {
4759
			$this->pie_capdrop = $pie_capdrop;
4760
	}
4761
	function GetPIE_QDELAY() {
4762
			return $this->pie_qdelay;
4763
	}
4764
	function SetPIE_QDELAY($pie_qdelay) {
4765
			$this->pie_qdelay = $pie_qdelay;
4766
	}
4767
	function GetPIE_PDERAND() {
4768
			return $this->pie_pderand;
4769
	}
4770
	function SetPIE_PDERAND($pie_pderand) {
4771
			$this->pie_pderand = $pie_pderand;
4772
	}
4773
	/* End limiter queue patch */
4774

    
4775
	function GetWeight() {
4776
		return $this->weight;
4777
	}
4778
	function SetWeight($weight) {
4779
		$this->weight = $weight;
4780
	}
4781
	function GetPipe() {
4782
		return $this->pipeparent;
4783
	}
4784
	function SetPipe($pipe) {
4785
		$this->pipeparent = $pipe;
4786
	}
4787

    
4788
	/* Just a stub in case we ever try to call this from the frontend. */
4789
	function &add_queue($interface, &$queue, &$path, &$input_errors) {
4790
		return;
4791
	}
4792

    
4793
	function delete_queue() {
4794
		cleanup_dnqueue_from_rules($this->GetQname());
4795
		unset_dn_object_by_reference($this->GetLink());
4796
		mwexec("/sbin/dnctl queue delete " . $this->GetNumber());
4797
	}
4798

    
4799
	function validate_input($data, &$input_errors) {
4800
		parent::validate_input($data, $input_errors);
4801

    
4802

    
4803
		/* Limiter patch */
4804
		$selectedAqm = getAQMs()[$data['aqm']];
4805
		if (!empty($data['aqm']) && !$selectedAqm) {
4806
			$input_errors[] = gettext("Selected AQM not recognized.");
4807
		}
4808
		/* End limiter patch */
4809

    
4810
		if ($data['weight'] && ((!is_numeric($data['weight'])) ||
4811
		    ($data['weight'] < 1 && $data['weight'] > 100))) {
4812
			$input_errors[] = gettext("Weight must be an integer between 1 and 100.");
4813
		}
4814
	}
4815

    
4816
	/*
4817
	 * Should search even its children
4818
	 */
4819
	function &find_queue($pipe, $qname) {
4820
		if ($qname == $this->GetQname()) {
4821
			return $this;
4822
		} else {
4823
			return NULL;
4824
		}
4825
	}
4826

    
4827
	function &find_parentqueue($pipe, $qname) {
4828
		return $this->qparent;
4829
	}
4830

    
4831
	function &get_queue_list(&$qlist) {
4832
		if ($this->GetEnabled() == "") {
4833
			return;
4834
		}
4835
		$qlist[$this->GetQname()] = "?" .$this->GetNumber();
4836
	}
4837

    
4838
	function ReadConfig(&$q) {
4839
		if (!empty($q['name']) && !empty($q['newname']) && $q['name'] != $q['newname']) {
4840
			$this->SetQname($q['newname']);
4841
		} else if (!empty($q['newname'])) {
4842
			$this->SetQname($q['newname']);
4843
		} else {
4844
			$this->SetQname($q['name']);
4845
		}
4846
		$this->SetNumber($q['number']);
4847
		if (isset($q['qlimit']) && $q['qlimit'] <> "") {
4848
			$this->SetQlimit($q['qlimit']);
4849
		} else {
4850
			$this->SetQlimit("");
4851
		}
4852
		if (isset($q['mask']) && $q['mask'] <> "") {
4853
			$masktype = $q['mask'];
4854
		} else {
4855
			$masktype = "";
4856
		}
4857
		if (isset($q['maskbits']) && $q['maskbits'] <> "") {
4858
			$maskbits = $q['maskbits'];
4859
		} else {
4860
			$maskbits = "";
4861
		}
4862
		if (isset($q['maskbitsv6']) && $q['maskbitsv6'] <> "") {
4863
			$maskbitsv6 = $q['maskbitsv6'];
4864
		} else {
4865
			$maskbitsv6 = "";
4866
		}
4867
		$this->SetMask(array("type" => $masktype, "bits" => $maskbits, "bitsv6" => $maskbitsv6));
4868
		if (isset($q['buckets']) && $q['buckets'] <> "") {
4869
			$this->SetBuckets($q['buckets']);
4870
		} else {
4871
			$this->SetBuckets("");
4872
		}
4873
		if (isset($q['plr']) && $q['plr'] <> "") {
4874
			$this->SetPlr($q['plr']);
4875
		} else {
4876
			$this->SetPlr("");
4877
		}
4878
		if (isset($q['weight']) && $q['weight'] <> "") {
4879
			$this->SetWeight($q['weight']);
4880
		} else {
4881
			$this->SetWeight("");
4882
		}
4883
		if (isset($q['description']) && $q['description'] <> "") {
4884
			$this->SetDescription($q['description']);
4885
		} else {
4886
			$this->SetDescription("");
4887
		}
4888
		$this->SetEnabled($q['enabled']);
4889

    
4890
		/* Limiter patch */
4891
		if (isset($q['aqm']) && $q['aqm'] <> "") {
4892
				$this->SetAQM($q['aqm']);
4893
		} else {
4894
				$this->SetAQM('droptail');
4895
		}
4896
		// parse AQM arguments
4897
		$aqm_map = getAQMs();
4898
		$selectedParameters = $aqm_map[$this->getAQM()]["parameters"];
4899
		foreach ($selectedParameters as $key => $value) {
4900
			$config_key = 'param_' . $this->GetAQM() . '_' . $key;
4901
			if (isset($q[$config_key]) && $q[$config_key] <> "") {
4902
				$this->SetAQMParameter($key, $q[$config_key]);
4903
			} else {
4904
				$this->SetAQMParameter($key, $value["default"]);
4905
			}
4906
		}
4907

    
4908
		// ecn flag
4909
		$this->SetECN($q['ecn']);
4910
		// PIE Flags.
4911
		$this->SetPIE_ONOFF($q['pie_onoff']);
4912
		$this->SetPIE_CAPDROP($q['pie_capdrop']);
4913
		$this->SetPIE_QDELAY($q['pie_qdelay']);
4914
		$this->SetPIE_PDERAND($q['pie_pderand']);
4915
		/* End limiter patch */
4916
	}
4917

    
4918
	function build_tree() {
4919
		$parent =& $this->GetParent();
4920
		$tree = " <li><a href=\"firewall_shaper_vinterface.php?pipe=" . htmlspecialchars($parent->GetQname()) ."&amp;queue=" . htmlspecialchars($this->GetQname()) ."&amp;action=show\">";
4921
		$tree .= htmlspecialchars($this->GetQname()) . "</a>";
4922
		$tree .= "</li>";
4923

    
4924
		return $tree;
4925
	}
4926

    
4927
	function build_rules() {
4928
		if ($this->GetEnabled() == "") {
4929
			return;
4930
		}
4931

    
4932
		$parent =& $this->GetParent();
4933
		$pfq_rule = "queue ". $this->GetNumber() . " config pipe " . $parent->GetNumber();
4934
		if ($this->GetQlimit()) {
4935
			$pfq_rule .= " queue " . $this->GetQlimit();
4936
		}
4937
		if ($this->GetWeight()) {
4938
			$pfq_rule .= " weight " . $this->GetWeight();
4939
		}
4940
		if ($this->GetBuckets()) {
4941
			$pfq_rule .= " buckets " . $this->GetBuckets();
4942
		}
4943
		$this->build_mask_rules($pfq_rule);
4944

    
4945
		/* Limiter patch */
4946
		$selectedAQM = getAQMs()[$this->getAQM()];
4947
		if ($selectedAQM) {
4948
			$pfq_rule .= " " . FormatParameters($selectedAQM["parameter_format"], $this->GetAQMParameters());
4949
			if ($selectedAQM["ecn"]) {
4950
				if ($this->getECN() == 'on') {
4951
					$pfq_rule .= ' ecn';
4952
				} elseif (($this->getAQM() != 'red') && ($this->getAQM() != 'gred')) {
4953
					$pfq_rule .= ' noecn';
4954
				}
4955
			}
4956
			if ($selectedAQM["pie_onoff"]) {
4957
				if ($this->getPIE_ONOFF() == 'on') {
4958
				$pfq_rule .= 'onoff';
4959
				} else {
4960
					$pfq_rule .= '';
4961
				}
4962
			}
4963
			if ($selectedAQM["pie_capdrop"]) {
4964
				if ($this->getPIE_CAPDROP() == 'on') {
4965
					$pfq_rule .= ' capdrop';
4966
				} else {
4967
					$pfq_rule .= ' nocapdrop';
4968
				}
4969
			}
4970
			if ($selectedAQM["pie_qdelay"]) {
4971
				if ($this->getPIE_QDELAY() == 'on') {
4972
					$pfq_rule .= ' ts';
4973
				} else {
4974
					$pfq_rule .= ' dre';
4975
				}
4976
			}
4977
			if ($selectedAQM["pie_pderand"]) {
4978
				if ($this->getPIE_PDERAND() == 'on') {
4979
					$pfq_rule .= ' derand';
4980
				} else {
4981
					$pfq_rule .= ' noderand';
4982
				}
4983
			}
4984
		}
4985
		/* End patch */
4986

    
4987
		$pfq_rule .= "\n";
4988

    
4989
		return $pfq_rule;
4990
	}
4991

    
4992
	function build_javascript() {
4993
		return parent::build_javascript();
4994
	}
4995

    
4996
	function build_form() {
4997
		global $g, $config, $pipe, $action, $qname;
4998

    
4999
		//build list of schedules
5000
		$schedules = array();
5001
		$schedules[] = "none";//leave none to leave rule enabled all the time
5002
		if (is_array($config['schedules']) && is_array($config['schedules']['schedule'])) {
5003
			foreach ($config['schedules']['schedule'] as $schedule) {
5004
				if ($schedule['name'] <> "") {
5005
					$schedules[] = $schedule['name'];
5006
				}
5007
			}
5008
		}
5009

    
5010

    
5011
		$sform = new Form();
5012
		$sform->setAction("firewall_shaper.php");
5013
		$section = new Form_Section('Limiters');
5014

    
5015
		$section->addInput(new Form_Checkbox(
5016
			'enabled',
5017
			'Enable',
5018
			'Enable this queue',
5019
			($this->GetEnabled() == "on"),
5020
			'on'
5021
		));
5022

    
5023
		$section->addInput(new Form_Input(
5024
			'newname',
5025
			'*Name',
5026
			'text',
5027
			$this->GetQname()
5028
		));
5029

    
5030
		$section->addInput(new Form_Input(
5031
			'name',
5032
			null,
5033
			'hidden',
5034
			$this->GetQname()
5035
		));
5036

    
5037
		if ($this->GetNumber() > 0) {
5038
			$section->addInput(new Form_Input(
5039
				'number',
5040
				null,
5041
				'hidden',
5042
				$this->GetNumber()
5043
			));
5044
		}
5045

    
5046
		$mask = $this->GetMask();
5047

    
5048
		$section->addInput(new Form_Select(
5049
			'mask',
5050
			'Mask',
5051
			$mask['type'],
5052
			array('none' => gettext('None'), 'srcaddress' => gettext('Source addresses'), 'dstaddress' => gettext('Destination addresses'))
5053
		))->setHelp('If "source" or "destination" slots is chosen a dynamic queue with the bandwidth, delay, packet loss ' .
5054
					'and queue size given above will be created for each source/destination IP address encountered, respectively. ' .
5055
 					'This makes it possible to easily specify bandwidth limits per ' .
5056
 					'host or subnet, usually capped by the bandwidth of the parent ' .
5057
 					'limiter.');
5058

    
5059
		$group = new Form_Group(null);
5060

    
5061
		$group->add(new Form_Select(
5062
			'maskbits',
5063
			null,
5064
			$mask['bits'],
5065
			array_combine(range(32, 1, -1), range(32, 1, -1))
5066
		))->setHelp('IPv4 mask bits%1$s%2$s', '<br />', '255.255.255.255/?');
5067

    
5068
		$group->add(new Form_Select(
5069
			'maskbitsv6',
5070
			null,
5071
			$mask['bitsv6'],
5072
			array_combine(range(128, 1, -1), range(128, 1, -1))
5073
		))->setHelp('IPv6 mask bits%1$s%2$s', '<br />', '<span style="font-family:consolas">ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/?</span>');
5074

    
5075
		$section->add($group);
5076

    
5077
		$section->addInput(new Form_Input(
5078
			'description',
5079
			'Description',
5080
			'text',
5081
			$this->GetDescription()
5082
		))->setHelp('A description may be entered here for administrative reference (not parsed).');
5083

    
5084
		$sform->add($section);
5085

    
5086
		/* Begin limiter patch */
5087
		$aqm_map = getAQMs();
5088

    
5089
		$section = new Form_Section('Queue');
5090
		$section->addInput(new Form_Select(
5091
				'aqm',
5092
				'Queue Management Algorithm',
5093
				$this->GetAQM(),
5094
				array_map_assoc(function ($k, $v) {
5095
					return [$k, $v["name"]];
5096
				}, $aqm_map)
5097
		))->setHelp('Active queue management (AQM) is the intelligent drop of network packets inside this limiter\'s queue, ' .
5098
						'when it becomes full or gets close to becoming full, with the goal of reducing ' .
5099
						'network congestion.');
5100

    
5101
		$section->addInput(new Form_StaticText(
5102
			'',
5103
			build_queue_params($aqm_map, $this->GetAQM(), $this->GetAQMParameters(), "aqm")
5104
		))->setHelp('Specifies the queue management algorithm parameters.');
5105

    
5106
		$section->addInput(new Form_Input(
5107
				'qlimit',
5108
				'Queue length',
5109
				'number',
5110
				$this->GetQlimit()
5111
		))->setHelp('Specifies the length of this queue, which the AQM is responsible for.  This field may be left empty.');
5112

    
5113
		$selectedAQM = getAQMs()[$this->getAQM()];
5114

    
5115
		if ($selectedAQM["ecn"]) {
5116
			$section->addInput(new Form_Checkbox(
5117
				'ecn',
5118
				'ECN',
5119
				'Enable Explicit Congestion Notification (ECN)',
5120
				($this->GetECN() == "on"),
5121
				'on'
5122
			))->setHelp('ECN sets a reserved TCP flag when the queue is nearing or exceeding capacity. Not all AQMs or schedulers support this.');
5123
		}
5124

    
5125
		if ($selectedAQM["pie_onoff"]) {
5126
			$section->addInput(new Form_Checkbox(
5127
				'pie_onoff',
5128
				'ONOFF',
5129
				'Enable Onoff (onoff,)',
5130
				($this->GetPIE_ONOFF() == "on"),
5131
				'on'
5132
			))->setHelp('Enable turning PIE on and off depending on queue load.');
5133
		}
5134
                                 
5135
		if ($selectedAQM["pie_capdrop"]) {
5136
			$section->addInput(new Form_Checkbox(
5137
				'pie_capdrop',
5138
				'CAPDROP',
5139
				'Enable Capdrop (capdrop,nocapdrop)',
5140
				($this->GetPIE_CAPDROP() == "on"),
5141
				'on'
5142
			))->setHelp('Enable cap drop adjustment.');
5143
		}
5144
                 
5145
		if ($selectedAQM["pie_qdelay"]) {
5146
			$section->addInput(new Form_Checkbox(
5147
				'pie_qdelay',
5148
				'QUEUE DELAY TYPE',
5149
				'Enable Type Of Qdelay (ts,dre)',
5150
				($this->GetPIE_QDELAY() == "on"),
5151
				'on'
5152
			))->setHelp('Set queue delay type to timestamps (checked) or departure rate estimation (unchecked).');
5153
		}
5154

    
5155
		if ($selectedAQM["pie_pderand"]) {
5156
			$section->addInput(new Form_Checkbox(
5157
				'pie_pderand',
5158
				'PROB DERAND',
5159
				'Enable Drop Probability De-randomisation (derand,noderand)',
5160
				($this->GetPIE_PDERAND() == "on"),
5161
				'on'
5162
			))->setHelp('Enable (checked) or disable (unchecked) drop probability de-randomisation.');
5163
		}
5164
		$sform->add($section);
5165
		/* End limiter patch */
5166

    
5167
		$section = new Form_Section('Advanced Options');
5168

    
5169
		$section->addInput(new Form_Input(
5170
			'weight',
5171
			'Weight',
5172
			'number',
5173
			$this->GetWeight(),
5174
			['min' => '1', 'max' => '100']
5175
		))->setHelp('For queues under the same parent this specifies the share that a queue gets(values range from 1 to 100),' .
5176
					' it can be left blank otherwise.');
5177

    
5178
		$section->addInput(new Form_Input(
5179
			'plr',
5180
			'Packet Loss Rate',
5181
			'number',
5182
			$this->GetPlr(),
5183
			['step' => '0.001', 'min' => '0.000']
5184
		))->setHelp('In most cases, zero (0) should be specified here (or leave the field empty). ' .
5185
					'A value of 0.001 means one packet in 1000 gets dropped');
5186

    
5187
		$section->addInput(new Form_Input(
5188
			'buckets',
5189
			'Bucket size (slots)',
5190
			'number',
5191
			$this->GetBuckets()
5192
		))->setHelp('In most cases, this field should be left empty. It increases the hash size set');
5193

    
5194
		$section->addInput(new Form_Input(
5195
			'pipe',
5196
			null,
5197
			'hidden',
5198
			$this->GetPipe()
5199
		));
5200

    
5201
		$sform->add($section);
5202

    
5203
		return($sform);
5204
	}
5205

    
5206
	function update_dn_data(&$data) {
5207
		$this->ReadConfig($data);
5208
	}
5209

    
5210
	function wconfig() {
5211
		$cflink =& get_dn_reference_to_me_in_config($this->GetLink());
5212
		if (!is_array($cflink)) {
5213
			$cflink = array();
5214
		}
5215
		$cflink['name'] = $this->GetQname();
5216
		$cflink['number'] = $this->GetNumber();
5217
		$cflink['qlimit'] = $this->GetQlimit();
5218
		$cflink['description'] = $this->GetDescription();
5219
		$cflink['weight'] = $this->GetWeight();
5220
		$cflink['enabled'] = $this->GetEnabled();
5221
		$cflink['buckets'] = $this->GetBuckets();
5222
		$mask = $this->GetMask();
5223
		$cflink['mask'] = $mask['type'];
5224
		$cflink['maskbits'] = $mask['bits'];
5225
		$cflink['maskbitsv6'] = $mask['bitsv6'];
5226

    
5227
		/* Limiter queue patch */
5228
		$cflink['aqm'] = $this->GetAQM();
5229
		$aqm_map = GetAQMs();
5230
		$selectedParameters = $aqm_map[$this->getAQM()]["parameters"];
5231
		foreach ($selectedParameters as $key => $value) {
5232
			$config_key = 'param_' . $this->GetAQM() . '_' . $key;
5233
			$cflink[$config_key] = $this->GetAQMParameter($key);
5234
		}
5235
		$cflink['ecn'] = $this->GetECN();
5236

    
5237
		$selectedAQM = getAQMs()[$this->getAQM()];
5238
		if ($selectedAQM["pie_onoff"]) {
5239
			$cflink['pie_onoff'] = $this->GetPIE_ONOFF();
5240
		}
5241
		if ($selectedAQM["pie_capdrop"]) {
5242
			$cflink['pie_capdrop'] = $this->GetPIE_CAPDROP();
5243
		}
5244
		if ($selectedAQM["pie_qdelay"]) {
5245
			$cflink['pie_qdelay'] = $this->GetPIE_QDELAY();
5246
		}
5247
		if ($selectedAQM["pie_pderand"]) {
5248
			$cflink['pie_pderand'] = $this->GetPIE_PDERAND();
5249
		}
5250
		/* End limiter queue patch */
5251
	}
5252
}
5253

    
5254
function get_dummynet_name_list() {
5255

    
5256
	$dn_name_list =& get_unique_dnqueue_list();
5257
	$dn_name = array();
5258
	if (is_array($dn_name_list)) {
5259
		foreach ($dn_name_list as $key => $value) {
5260
			$dn_name[] = $key;
5261
		}
5262
	}
5263

    
5264
	return $dn_name;
5265

    
5266
}
5267

    
5268
function get_altq_name_list() {
5269
	$altq_name_list =& get_unique_queue_list();
5270
	$altq_name = array();
5271
	if (is_array($altq_name_list)) {
5272
		foreach ($altq_name_list as $key => $aqobj) {
5273
			$altq_name[] = $key;
5274
		}
5275
	}
5276

    
5277
	return $altq_name;
5278
}
5279

    
5280
/*
5281
 * XXX: TODO Make a class shaper to hide all these functions
5282
 * from the global namespace.
5283
 */
5284

    
5285
/*
5286
 * This is a layer violation but for now there is no way
5287
 * I can find to properly do this with PHP.
5288
 */
5289
function altq_get_default_queue($interface) {
5290
	global $altq_list_queues;
5291

    
5292
	$altq_tmp = $altq_list_queues[$interface];
5293
	if ($altq_tmp) {
5294
		return $altq_tmp->GetDefaultQueuePresent();
5295
	} else {
5296
		return false;
5297
	}
5298
}
5299

    
5300
function altq_check_default_queues() {
5301
	global $altq_list_queues;
5302

    
5303
	$count = 0;
5304
	if (is_array($altq_list_queues)) {
5305
		foreach ($altq_list_queues as $altq) {
5306
			if ($altq->GetDefaultQueuePresent()) {
5307
				$count++;
5308
			}
5309
		}
5310
	}
5311
	else {
5312
		$count++;
5313
	}
5314

    
5315
	return 0;
5316
}
5317

    
5318
function &get_unique_queue_list() {
5319
	global $altq_list_queues;
5320

    
5321
	$qlist = array();
5322
	if (is_array($altq_list_queues)) {
5323
		foreach ($altq_list_queues as $altq) {
5324
			if ($altq->GetEnabled() == "") {
5325
				continue;
5326
			}
5327
			$tmplist =& $altq->get_queue_list();
5328
			foreach ($tmplist as $qname => $link) {
5329
				if ($link->GetEnabled() <> "") {
5330
					$qlist[$qname] = $link;
5331
				}
5332
			}
5333
		}
5334
	}
5335
	return $qlist;
5336
}
5337

    
5338
function &get_unique_dnqueue_list() {
5339
	global $dummynet_pipe_list;
5340

    
5341
	$qlist = array();
5342
	if (is_array($dummynet_pipe_list)) {
5343
		foreach ($dummynet_pipe_list as $dn) {
5344
			if ($dn->GetEnabled() == "") {
5345
				continue;
5346
			}
5347
			$tmplist =& $dn->get_queue_list();
5348
			foreach ($tmplist as $qname => $link) {
5349
				$qlist[$qname] = $link;
5350
			}
5351
		}
5352
	}
5353
	return $qlist;
5354
}
5355

    
5356
function ref_on_altq_queue_list($parent, $qname) {
5357
	if (isset($GLOBALS['queue_list'][$qname])) {
5358
		$GLOBALS['queue_list'][$qname]++;
5359
	} else {
5360
		$GLOBALS['queue_list'][$qname] = 1;
5361
	}
5362

    
5363
	unref_on_altq_queue_list($parent);
5364
}
5365

    
5366
function unref_on_altq_queue_list($qname) {
5367
	$GLOBALS['queue_list'][$qname]--;
5368
	if ($GLOBALS['queue_list'][$qname] <= 1) {
5369
		unset($GLOBALS['queue_list'][$qname]);
5370
	}
5371
}
5372

    
5373
function read_altq_config() {
5374
	global $altq_list_queues, $config;
5375
	$path = array();
5376

    
5377
	init_config_arr(array('shaper', 'queue'));
5378
	$a_int = &$config['shaper']['queue'];
5379

    
5380
	$altq_list_queues = array();
5381

    
5382
	if (!is_array($config['shaper']['queue'])) {
5383
		return;
5384
	}
5385

    
5386
	foreach ($a_int as $key => $conf) {
5387
		$int = $conf['interface'];
5388
		$root = new altq_root_queue();
5389
		$root->SetInterface($int);
5390
		$altq_list_queues[$root->GetInterface()] = $root;
5391
		$root->ReadConfig($conf);
5392
		array_push($path, $key);
5393
		$root->SetLink($path);
5394
		if (is_array($conf['queue'])) {
5395
			foreach ($conf['queue'] as $key1 => $q) {
5396
				array_push($path, $key1);
5397
				/*
5398
				 * XXX: we completely ignore errors here but anyway we must have
5399
				 *	checked them before so no harm should be come from this.
5400
				 */
5401
				$root->add_queue($root->GetInterface(), $q, $path, $input_errors);
5402
				array_pop($path);
5403
			}
5404
		}
5405
		array_pop($path);
5406
	}
5407
}
5408

    
5409
function read_dummynet_config() {
5410
	global $dummynet_pipe_list, $config;
5411
	$path = array();
5412

    
5413
	init_config_arr(array('dnshaper', 'queue'));
5414
	$a_int = &$config['dnshaper']['queue'];
5415

    
5416
	$dummynet_pipe_list = array();
5417

    
5418
	if (!count($config['dnshaper']['queue'])) {
5419
		return;
5420
	}
5421

    
5422
	foreach ($a_int as $key => $conf) {
5423
		if (empty($conf['name'])) {
5424
			continue; /* XXX: grrrrrr at php */
5425
		}
5426
		$root = new dnpipe_class();
5427
		$root->ReadConfig($conf);
5428
		$dummynet_pipe_list[$root->GetQname()] = $root;
5429
		array_push($path, $key);
5430
		$root->SetLink($path);
5431
		if (is_array($conf['queue'])) {
5432
			foreach ($conf['queue'] as $key1 => $q) {
5433
				array_push($path, $key1);
5434
				/*
5435
				 * XXX: we completely ignore errors here but anyway we must have
5436
				 *	checked them before so no harm should be come from this.
5437
				 */
5438
				$root->add_queue($root->GetQname(), $q, $path, $input_errors);
5439
				array_pop($path);
5440
			}
5441
		}
5442
		array_pop($path);
5443
	}
5444
}
5445

    
5446
function get_interface_list_to_show() {
5447
	global $altq_list_queues, $config;
5448
	global $shaperIFlist;
5449

    
5450
	$tree = "";
5451
	foreach ($shaperIFlist as $shif => $shDescr) {
5452
		if ($altq_list_queues[$shif]) {
5453
			continue;
5454
		} else {
5455
			if (!is_altq_capable(get_real_interface($shif))) {
5456
				continue;
5457
			}
5458
			$tree .= " <li><a href=\"firewall_shaper.php?interface=".$shif."&amp;action=add\">".$shDescr."</a></li>";
5459
		}
5460
	}
5461

    
5462
	return $tree;
5463
}
5464

    
5465
function filter_generate_altq_queues() {
5466
	global $altq_list_queues;
5467

    
5468
	read_altq_config();
5469

    
5470
	$altq_rules = "";
5471
	foreach ($altq_list_queues as $altq) {
5472
		$altq_rules .= $altq->build_rules();
5473
	}
5474

    
5475
	return $altq_rules;
5476
}
5477

    
5478
function dnqueue_find_nextnumber() {
5479
	global $dummynet_pipe_list;
5480

    
5481
	$dnused = array();
5482
	if (is_array($dummynet_pipe_list)) {
5483
		foreach ($dummynet_pipe_list as $dn) {
5484
			$tmplist =& $dn->get_queue_list();
5485
			foreach ($tmplist as $qname => $link) {
5486
				if ($link[0] == "?") {
5487
					$dnused[$qname] = substr($link, 1);
5488
				}
5489
			}
5490
		}
5491
	}
5492

    
5493
	sort($dnused, SORT_NUMERIC);
5494
	$dnnumber = 0;
5495
	$found = false;
5496
	foreach ($dnused as $dnnum) {
5497
		if (($dnnum - $dnnumber) > 1) {
5498
			$dnnumber = $dnnum - 1;
5499
			$found = true;
5500
			break;
5501
		} else {
5502
			$dnnumber = $dnnum;
5503
		}
5504
	}
5505

    
5506
	if ($found == false) {
5507
		$dnnumber++;
5508
	}
5509

    
5510
	unset($dnused, $dnnum, $found);
5511
	return $dnnumber;
5512
}
5513

    
5514
function dnpipe_find_nextnumber() {
5515
	global $dummynet_pipe_list;
5516

    
5517
	$dnused = array();
5518
	foreach ($dummynet_pipe_list as $dn) {
5519
		$dnused[] = $dn->GetNumber();
5520
	}
5521

    
5522
	sort($dnused, SORT_NUMERIC);
5523
	$dnnumber = 0;
5524
	$found = false;
5525
	foreach ($dnused as $dnnum) {
5526
		if (($dnnum - $dnnumber) > 1) {
5527
			$dnnumber = $dnnum - 1;
5528
			$found = true;
5529
			break;
5530
		} else {
5531
			$dnnumber = $dnnum;
5532
		}
5533
	}
5534

    
5535
	if ($found == false) {
5536
		$dnnumber++;
5537
	}
5538

    
5539
	unset($dnused, $dnnum, $found);
5540
	return $dnnumber;
5541
}
5542

    
5543
function filter_generate_dummynet_rules() {
5544
	global $config, $g, $dummynet_pipe_list;
5545

    
5546
	read_dummynet_config();
5547

    
5548
	$dn_rules = "";
5549
	$max_qlimit = "100"; // OS default
5550
	foreach ($dummynet_pipe_list as $dn) {
5551
		$dn_rules .= $dn->build_rules();
5552
		$this_qlimit = $dn->GetQlimit();
5553
		if ($this_qlimit > $max_qlimit) {
5554
			$max_qlimit = $this_qlimit;
5555
		}
5556
	}
5557
	if (!is_numericint($max_qlimit)) {
5558
		$max_qlimit = "100";
5559
	}
5560
	if (!empty($dn_rules)) {
5561
		dummynet_load_module($max_qlimit);
5562
		file_put_contents("{$g['tmp_path']}/rules.limiter", $dn_rules);
5563
		mwexec("/sbin/dnctl {$g['tmp_path']}/rules.limiter");
5564
	}
5565
}
5566

    
5567
function build_iface_without_this_queue($iface, $qname) {
5568
	global $g, $altq_list_queues;
5569
	global $shaperIFlist;
5570

    
5571
	$altq =& $altq_list_queues[$iface];
5572

    
5573
	if ($altq) {
5574
		$scheduler = $altq->GetScheduler();
5575
	}
5576

    
5577
	$form = '<dl class="dl-horizontal">';
5578

    
5579
	$form .= '	<dt>';
5580
	$form .= '		<a href="firewall_shaper.php?interface=' . $iface . '&amp;queue=' . $iface . '&amp;action=show">' . $shaperIFlist[$iface] . '</a>';
5581
	$form .= '	</dt>';
5582
	$form .= '	<dd>';
5583
	$form .=		$scheduler;
5584
	$form .= '	</dd>';
5585

    
5586
	$form .= '	<dt>';
5587
	$form .= 'Clone';
5588
	$form .= '	</dt>';
5589
	$form .= '	<dd>';
5590
	$form .= '<a class="btn btn-info btn-xs" href="firewall_shaper_queues.php?interface=';
5591
	$form .= $iface . '&amp;queue=';
5592
	$form .= $qname . '&amp;action=add">';
5593
	$form .= '<i class="fa fa-clone icon-embed-btn"></i>';
5594
	$form .= gettext("Clone Shaper to this Interface") . '</a>';
5595
	$form .= '	</dd>';
5596

    
5597
	$form .= '</dl>';
5598

    
5599
	return $form;
5600

    
5601
}
5602

    
5603
$default_shaper_msg = sprintf(gettext("Welcome to the %s Traffic Shaper."), g_get('product_label')) . "<br />";
5604
$dn_default_shaper_msg = $default_shaper_msg;
5605

    
5606
$shaper_msg = gettext("The tree on the left navigates through the %s.");
5607
$default_shaper_msg .= sprintf($shaper_msg, gettext("queues")) . "<br />";
5608
$dn_default_shaper_msg .= sprintf($shaper_msg, gettext("limiters")) . "<br />";
5609

    
5610
$shaper_msg = gettext("Buttons at the bottom represent %s actions and are activated accordingly.");
5611
$default_shaper_msg .= sprintf($shaper_msg, gettext("queue"));
5612
$dn_default_shaper_msg .= sprintf($shaper_msg, gettext("limiter"));
5613

    
5614
// Check to see if the specified interface has a queue configured
5615
function interface_has_queue($if) {
5616
	global $config;
5617

    
5618
	if ($config['shaper']) {
5619
		foreach ($config['shaper']['queue'] as $queue) {
5620
			if ($queue['interface'] === $if) {
5621
				return true;
5622
			}
5623
		}
5624
	}
5625

    
5626
	return false;
5627
}
5628
?>
(48-48/61)