Project

General

Profile

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

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

    
34
/* Limiter patch */
35
// I need this crazy looking model/struct to specify various algos, ecn caps, and params.
36
// update as needed when dummynet changes
37

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

    
173
	$selectedAlgorithm = $array[$selected];
174

    
175
	if ($selectedAlgorithm) {
176
		$form .= '<span class="help-block">' . gettext($selectedAlgorithm['description']) . '</span><br/>';
177
	}
178

    
179
	$parameters = $selectedAlgorithm["parameters"];
180

    
181
	if (!$parameters || sizeof($parameters) <= 0) {
182
		if (!$selectedAlgorithm) {
183
			$form .= 'No parameters for selected algorithm.';
184
		} else {
185
			$form .= 'No parameters for ' . $selectedAlgorithm["name"] . '.';
186
		}
187
	} else {
188
		$form .= '<div class="table-responsive">';
189
		$form .= '<table id="maintable" class="table table-hover table-striped">';
190
		$form .= "<thead><tr>";
191
		$form .= "<th>Parameter</th>";
192
		$form .= "<th>Value</th>";
193
		$form .= "</tr></thead>";
194
		$form .= "<tbody>";
195

    
196
		foreach ($parameters as $key => $value) {
197
			$form .= '<tr>';
198

    
199
			// get current value, or default.
200
			$currentValue = $params[$key];
201
			if (!$currentValue || $currentValue == '') {
202
				$currentValue = $value["default"]; // default to default
203
			}
204

    
205
			$form .= "<td class=\"col-xs-4\">" . htmlspecialchars($key) . "</td>";
206
			$form .= "<td class=\"col-xs-8\"><input class=\"form-control\" type=" . gettext($value["type"])
207
					. " name=\"param_" . $selected . "_" . $key . "\" placeholder=\"" .
208
					htmlspecialchars($value["default"]) . "\" value=\"" . htmlspecialchars($currentValue) . "\"/></td>";
209

    
210
			$form .= '</tr>';
211
		}
212

    
213
		$form .= "</tbody></table></div><br />";
214
	}
215

    
216
	$form .= '</div>';
217
	$form .= '<script type="text/javascript">';
218
	$form .= 'events.push(function() {$("#' . $id . '").change(function() {$("#params_' .
219
		gettext($id) . '").html("Save this limiter to see algorithm parameters.");})});</script>';
220

    
221
	return($form);
222
}
223
function FormatParameters($format_string, $params) {
224
	return vsprintf($format_string, $params);
225
}
226
/* End limiter patch */
227

    
228
function get_queue_stats() {
229
	// due to sysutils\qstats not providing accurate stats with 'currently' valid numbers
230
	// in its current implementation this php function does the job for now..
231
	$result = array();
232
	$result['timestamp'] = gettimeofday(true);
233
	$r = exec("/sbin/pfctl -s queue -v", $output, $ret);
234
	$interfacestats = array();
235
	foreach($output as $line) {
236
		$partial = explode('{', $line);
237
		$items = explode(" ", $partial[0]);
238
		if (isset($partial[1])) {
239
			$contains = explode(", ", substr($partial[1], 0, strlen($partial[1]) - 1));
240
		} else {
241
			unset($contains);
242
		}
243
		if ($items[0] == "queue") {
244
			$level = 1;
245
			while (empty($items[$level])) {
246
				$level++;
247
			}
248
			unset($newqueue);
249
			$newqueue = array();
250
			$currentqueuename = $items[$level];
251
			$currentqueueif = $items[$level+2];
252
			$newqueue['name'] = $currentqueuename;
253
			$newqueue['interface'] = $items[$level+2];
254

    
255
			if ($items[$level+3] == "bandwidth") {
256
				$level += 2;
257
			}
258
			if ($items[$level+3] == "priority") {
259
				$level += 2;
260
			}
261
			if ($items[$level+3] == "qlimit") {
262
				$level += 2;
263
			}
264
			$tmp = $items[$level+3];
265
			if (substr($tmp,strlen($tmp)-1) == "(") {
266
				$newqueue['shapertype'] = substr($tmp, 0, strlen($tmp)-1);
267
			}
268

    
269
			$interfacestats[$currentqueueif][$currentqueuename] = &$newqueue;
270
			if (is_array($contains)) {
271
				$newqueue['contains'] = $contains;
272
			}
273
		} elseif ($items[0] == "altq") {
274
			unset($newqueue);
275
			$newqueue = array();
276
			$currentqueuename = convert_real_interface_to_friendly_interface_name($items[2]);
277
			$currentqueueif = $items[2];
278
			$newqueue['name'] = $currentqueuename;
279
			$newqueue['interface'] = $items[2];
280
			$interfacestats[$currentqueueif][$currentqueuename] = &$newqueue;
281
		} else {
282
			if ($items[3] == "pkts:") {
283
				$newqueue["pkts"] = trim(substr($line, 10, 10));
284
				$newqueue["bytes"] = trim(substr($line, 29, 10));
285
				$newqueue["droppedpkts"] = trim(substr($line, 55, 6));
286
				$newqueue["droppedbytes"] = trim(substr($line, 69, 6));
287
			}
288
			if ($items[3] == "qlength:") {
289
				$subitems = explode("/", substr($line, 13, 8));
290
				$newqueue["qlengthitems"] = trim($subitems[0]);
291
				$newqueue["qlengthsize"] = trim($subitems[1]);
292
				if (substr($line, 22, 8) == "borrows:") {
293
					$newqueue["borrows"] = trim(substr($line, 31, 7));
294
				}
295
				if (substr($line, 39, 9) == "suspends:") {
296
					$newqueue["suspends"] = trim(substr($line, 49, 7));
297
				}
298
			}
299
		}
300
	}
301
	$result['interfacestats'] = $interfacestats;
302
	return $result;
303
}
304

    
305
/*
306
 * I admit :) this is derived from xmlparse.inc StartElement()
307
 */
308
function &get_reference_to_me_in_config(&$mypath) {
309
	global $config;
310

    
311
	$ptr =& $config['shaper'];
312
	foreach ($mypath as $indeks) {
313
		if (!is_array($ptr)) {
314
			$ptr = array();
315
		}
316
		if (!is_array($ptr['queue'])) {
317
			$ptr['queue'] = array();
318
		}
319
		if (!is_array($ptr['queue'][$indeks])) {
320
			$ptr['queue'][$indeks] = array();
321
		}
322
		$ptr =& $ptr['queue'][$indeks];
323
	}
324

    
325
	return $ptr;
326
}
327

    
328
function unset_object_by_reference(&$mypath) {
329
	global $config;
330

    
331
	$ptr =& $config['shaper'];
332
	for ($i = 0; $i < count($mypath) - 1; $i++) {
333
		$ptr =& $ptr['queue'][$mypath[$i]];
334
	}
335
	unset($ptr['queue'][$mypath[$i]]);
336
}
337

    
338
function &get_dn_reference_to_me_in_config(&$mypath) {
339
	global $config;
340

    
341
	$ptr =& $config['dnshaper'];
342
	foreach ($mypath as $indeks) {
343
		$ptr =& $ptr['queue'][$indeks];
344
	}
345

    
346
	return $ptr;
347
}
348

    
349
function unset_dn_object_by_reference(&$mypath) {
350
	global $config;
351

    
352
	$ptr =& $config['dnshaper'];
353
	for ($i = 0; $i < count($mypath) - 1; $i++) {
354
		$ptr =& $ptr['queue'][$mypath[$i]];
355
	}
356
	unset($ptr['queue'][$mypath[$i]]);
357
}
358

    
359
function clean_child_queues($type, $mypath) {
360
	$ref = &get_reference_to_me_in_config($mypath);
361

    
362
	switch ($type) {
363
		case 'HFSC':
364
			if (isset($ref['borrow'])) {
365
				unset($ref['borrow']);
366
			}
367
			if (isset($ref['hogs'])) {
368
				unset($ref['hogs']);
369
			}
370
			if (isset($ref['buckets'])) {
371
				unset($ref['buckets']);
372
			}
373
			break;
374
		case 'PRIQ':
375
			if (isset($ref['borrow'])) {
376
				unset($ref['borrow']);
377
			}
378
			if (isset($ref['bandwidth'])) {
379
				unset($ref['bandwidth']);
380
			}
381
			if (isset($ref['bandwidthtype'])) {
382
				unset($ref['bandwidthtype']);
383
			}
384
			/* fall through */
385
		case 'FAIRQ':
386
			if (isset($ref['borrow'])) {
387
				unset($ref['borrow']);
388
			}
389
			/* fall through */
390
		case 'CBQ':
391
			if (isset($ref['realtime'])) {
392
				unset($ref['realtime']);
393
			}
394
			if (isset($ref['realtime1'])) {
395
				unset($ref['realtime1']);
396
			}
397
			if (isset($ref['realtime2'])) {
398
				unset($ref['realtime2']);
399
			}
400
			if (isset($ref['realtime3'])) {
401
				unset($ref['realtime3']);
402
			}
403
			if (isset($ref['upperlimit'])) {
404
				unset($ref['upperlimit']);
405
			}
406
			if (isset($ref['upperlimit1'])) {
407
				unset($ref['upperlimit1']);
408
			}
409
			if (isset($ref['upperlimit2'])) {
410
				unset($ref['upperlimit2']);
411
			}
412
			if (isset($ref['upperlimit3'])) {
413
				unset($ref['upperlimit3']);
414
			}
415
			if (isset($ref['linkshare'])) {
416
				unset($ref['linkshare']);
417
			}
418
			if (isset($ref['linkshare1'])) {
419
				unset($ref['linkshare1']);
420
			}
421
			if (isset($ref['linkshare2'])) {
422
				unset($ref['linkshare2']);
423
			}
424
			if (isset($ref['linkshare3'])) {
425
				unset($ref['linkshare3']);
426
			}
427
			if (isset($ref['hogs'])) {
428
				unset($ref['hogs']);
429
			}
430
			if (isset($ref['buckets'])) {
431
				unset($ref['buckets']);
432
			}
433
			break;
434
	}
435
}
436

    
437
function get_bandwidthtype_scale($type) {
438
	switch ($type) {
439
		case "Gb":
440
			$factor = 1024 * 1024 * 1024;
441
			break;
442
		case "Mb":
443
			$factor = 1024 * 1024;
444
			break;
445
		case "Kb":
446
			$factor = 1024;
447
			break;
448
		case "b":
449
		default:
450
			$factor = 1;
451
			break;
452
	}
453
	return intval($factor);
454
}
455

    
456
function get_bandwidth($bw, $scale, $obj) {
457
	$bw = (int) $bw;
458
	$pattern= "/(b|Kb|Mb|Gb|%)/";
459
	if (!preg_match($pattern, $scale, $match))
460
		return 0;
461

    
462
	switch ($match[1]) {
463
		case '%':
464
			$objbw = ($bw / 100) * get_queue_bandwidth($obj);
465
			break;
466
		default:
467
			$objbw = $bw * get_bandwidthtype_scale($scale);
468
			break;
469
	}
470

    
471
	return floatval($objbw);
472
}
473

    
474
/*
475
 * XXX - unused
476
 *
477
function get_hfsc_bandwidth($object, $bw) {
478
	$pattern= "/[0-9]+/";
479
	if (preg_match($pattern, $bw, $match)) {
480
		$bw_1 = $match[1];
481
	} else {
482
		return 0;
483
	}
484
	$pattern= "/(b|Kb|Mb|Gb|%)/";
485
	if (preg_match($pattern, $bw, $match)) {
486
		switch ($match[1]) {
487
			case '%':
488
				$bw_1 = ($bw_1 / 100) * get_interface_bandwidth($object);
489
				break;
490
			default:
491
				$bw_1 = $bw_1 * get_bandwidthtype_scale($match[0]);
492
				break;
493
		}
494
		return floatval($bw_1);
495
	} else {
496
		return 0;
497
	}
498
}
499
*/
500

    
501
function get_queue_bandwidth($obj) {
502
	$bw = $obj->GetBandwidth();
503
	$scale = $obj->GetBwscale();
504

    
505
	$pattern= "/(b|Kb|Mb|Gb|%)/";
506
	if (!preg_match($pattern, $scale, $match))
507
		return 0;
508

    
509
	switch ($match[1]) {
510
		case '%':
511
			$objbw = ($bw / 100) * get_queue_bandwidth($obj->GetParent());
512
			break;
513
		default:
514
			$objbw = $bw * get_bandwidthtype_scale($scale);
515
			break;
516
	}
517

    
518
	return floatval($objbw);
519
}
520

    
521
function get_interface_bandwidth($object) {
522
	global $altq_list_queues;
523

    
524
	$int = $object->GetInterface();
525
	$altq =& $altq_list_queues[$int];
526
	if ($altq) {
527
		$bw_3 = $altq->GetBandwidth();
528
		$bw_3 = $bw_3 * get_bandwidthtype_scale($altq->GetBwscale());
529
		return floatval($bw_3);
530
	} else {
531
		return 0;
532
	}
533
}
534

    
535
/*
536
 * This is duplicated here since we cannot include guiconfig.inc.
537
 * Including it makes all stuff break.
538
 */
539
function shaper_do_input_validation($postdata, $reqdfields, $reqdfieldsn, $input_errors) {
540

    
541
	/* check for bad control characters */
542
	foreach ($postdata as $pn => $pd) {
543
		if (is_string($pd) && preg_match("/[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f]/", $pd)) {
544
			$input_errors[] = sprintf(gettext("The field '%s' contains invalid characters."), $pn);
545
		}
546
	}
547

    
548
	for ($i = 0; $i < count($reqdfields); $i++) {
549
		if ($postdata[$reqdfields[$i]] == "") {
550
			$input_errors[] = sprintf(gettext("The field '%s' is required."), $reqdfieldsn[$i]);
551
		}
552
	}
553
}
554

    
555
function cleanup_queue_from_rules($queue) {
556
	global $config;
557

    
558
	foreach ($config['filter']['rule'] as $rule) {
559
		if ($rule['defaultqueue'] == $queue) {
560
			unset($rule['defaultqueue']);
561
		}
562
		if ($rule['ackqueue'] == $queue) {
563
			unset($rule['ackqueue']);
564
		}
565
	}
566
}
567

    
568
function cleanup_dnqueue_from_rules($queue) {
569
	global $config;
570

    
571
	foreach ($config['filter']['rule'] as $rule) {
572
		if ($rule['dnpipe'] == $queue) {
573
			unset($rule['dnpipe']);
574
		}
575
		if ($rule['pdnpipe'] == $queue) {
576
			unset($rule['pdnpipe']);
577
		}
578
	}
579
}
580

    
581
class altq_root_queue {
582
	var $interface;
583
	var $tbrconfig ;
584
	var $bandwidth;
585
	var $bandwidthtype; /* b, Kb, Mb, Gb, % */
586
	var $scheduler;
587
	var $qlimit;
588
	var $queues = array();
589
	var $qenabled = false;
590
	var $link;
591

    
592
	/* Accessor functions */
593
	function GetDefaultQueuePresent() {
594
		if (!empty($this->queues)) {
595
			foreach ($this->queues as $q) {
596
				if ($q->GetDefault()) {
597
					return true;
598
				}
599
			}
600
		}
601

    
602
		return false;
603
	}
604
	function SetLink($link) {
605
		$this->link = $link;
606
	}
607
	function GetLink() {
608
		return $this->link;
609
	}
610
	function GetEnabled() {
611
		return $this->qenabled;
612
	}
613
	function SetEnabled($value) {
614
		$this->qenabled = $value;
615
	}
616
	function CanHaveChildren() {
617
		if ($this->GetScheduler() == "CODELQ") {
618
			return false;
619
		} else {
620
			return true;
621
		}
622
	}
623
	function CanBeDeleted() {
624
		return false;
625
	}
626
	function GetQname() {
627
		return $this->interface;
628
	}
629
	function SetQname($name) {
630
		$this->interface = trim($name);
631
	}
632
	function GetInterface() {
633
		return $this->interface;
634
	}
635
	function SetInterface($name) {
636
		$this->interface = trim($name);
637
	}
638
	function GetTbrConfig() {
639
		return $this->tbrconfig;
640
	}
641
	function SetTbrConfig($tbrconfig) {
642
		$this->tbrconfig = $tbrconfig;
643
	}
644
	function GetBandwidth() {
645
		return $this->bandwidth;
646
	}
647
	function SetBandwidth($bw) {
648
		$this->bandwidth = $bw;
649
	}
650
	function GetBwscale() {
651
		return $this->bandwidthtype;
652
	}
653
	function SetBwscale($bwscale) {
654
		$this->bandwidthtype = $bwscale;
655
	}
656
	function GetScheduler() {
657
		return $this->scheduler;
658
	}
659
	function SetScheduler($scheduler) {
660
		$this->scheduler = trim($scheduler);
661
	}
662
	function GetQlimit() {
663
		return $this->qlimit;
664
	}
665
	function SetQlimit($limit) {
666
		$this->qlimit = $limit;
667
	}
668

    
669
	function GetBwscaleText() {
670
		switch ($this->bandwidthtype) {
671
			case "b":
672
				$bwscaletext = "Bit/s";
673
				break;
674
			case "Kb":
675
				$bwscaletext = "Kbit/s";
676
				break;
677
			case "Mb":
678
				$bwscaletext = "Mbit/s";
679
				break;
680
			case "Gb":
681
				$bwscaletext = "Gbit/s";
682
				break;
683
			default:
684
				/* For others that do not need translating like % */
685
				$bwscaletext = $this->bandwidthtype;
686
				break;
687
		}
688
		return $bwscaletext;
689
	}
690

    
691
	function CheckBandwidth($bw, $bwtype) {
692
		$sum = $this->GetTotalBw();
693
		if ($sum > $bw * get_bandwidthtype_scale($bwtype))
694
			return 1;
695
		foreach ($this->queues as $q) {
696
			if ($q->CheckBandwidth(0, ''))
697
				return 1;
698
		}
699

    
700
		return 0;
701
	}
702

    
703
	function GetTotalBw($qignore = NULL) {
704
		$sum = 0;
705
		foreach ($this->queues as $q) {
706
			if ($qignore != NULL && $qignore == $q)
707
				continue;
708
			$sum += get_bandwidth($q->GetBandwidth(), $q->GetBwscale(), $this);
709
		}
710

    
711
		return $sum;
712
	}
713

    
714
	function validate_input($data, &$input_errors) {
715

    
716
		$reqdfields[] = "bandwidth";
717
		$reqdfieldsn[] = gettext("Bandwidth");
718
		$reqdfields[] = "bandwidthtype";
719
		$reqdfieldsn[] = gettext("Bandwidthtype");
720

    
721
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
722

    
723
		if (!isset($data['bandwidth']) || strlen($data['bandwidth']) == 0) {
724
			$input_errors[] = gettext("Bandwidth must be set.  This is usually the interface speed.");
725
		}
726
		if ($data['bandwidth'] && (!is_numeric($data['bandwidth']))) {
727
			$input_errors[] = gettext("Bandwidth must be an integer.");
728
		}
729
		if ($data['bandwidth'] < 0) {
730
			$input_errors[] = gettext("Bandwidth cannot be negative.");
731
		}
732
		if ($data['bandwidthtype'] == "%") {
733
			if ($data['bandwidth'] > 100 || $data['bandwidth'] < 0) {
734
				$input_errors[] = gettext("Bandwidth in percentage should be between 1 and 100.");
735
			}
736
		}
737
		if ($this->CheckBandwidth($data['bandwidth'], $data['bandwidthtype']))
738
			$input_errors[] = "The sum of child bandwidth is higher than parent.";
739

    
740
		if ($data['qlimit'] && (!is_numeric($data['qlimit']))) {
741
			$input_errors[] = gettext("Qlimit must be an integer.");
742
		}
743
		if ($data['qlimit'] < 0) {
744
			$input_errors[] = gettext("Qlimit must be positive.");
745
		}
746
		if ($data['tbrconfig'] && (!is_numeric($data['tbrconfig']))) {
747
			$input_errors[] = gettext("Tbrsize must be an integer.");
748
		}
749
		if ($data['tbrconfig'] < 0) {
750
			$input_errors[] = gettext("Tbrsize must be positive.");
751
		}
752
	}
753

    
754
	/* Implement this to shorten some code on the frontend page */
755
	function ReadConfig(&$conf) {
756
		if (isset($conf['tbrconfig'])) {
757
			$this->SetTbrConfig($conf['tbrconfig']);
758
		} else {
759
			$this->SetTbrConfig($conf['tbrconfig']);
760
		}
761
		$this->SetBandwidth($conf['bandwidth']);
762
		if ($conf['bandwidthtype'] <> "") {
763
			$this->SetBwscale($conf['bandwidthtype']);
764
		}
765
		if (isset($conf['scheduler'])) {
766
			if ($this->GetScheduler() != $conf['scheduler']) {
767
				foreach ($this->queues as $q) {
768
					clean_child_queues($conf['scheduler'], $this->GetLink());
769
					$q->clean_queue($conf['scheduler']);
770
				}
771
			}
772
			$this->SetScheduler($conf['scheduler']);
773
		}
774
		if (isset($conf['qlimit']) && $conf['qlimit'] <> "") {
775
			$this->SetQlimit($conf['qlimit']);
776
		} else {
777
			$this->SetQlimit("");
778
		}
779
		if (isset($conf['name'])) {
780
			$this->SetQname($conf['name']);
781
		}
782
		if (!empty($conf['enabled'])) {
783
			$this->SetEnabled($conf['enabled']);
784
		} else {
785
			$this->SetEnabled("");
786
		}
787
	}
788

    
789
	function copy_queue($interface, &$cflink) {
790
		$cflink['interface'] = $interface;
791
		$cflink['name'] = $interface;
792
		$cflink['scheduler'] = $this->GetScheduler();
793
		$cflink['bandwidth'] = $this->GetBandwidth();
794
		$cflink['bandwidthtype'] = $this->GetBwscale();
795
		$cflink['qlimit'] = $this->GetQlimit();
796
		$cflink['tbrconfig'] = $this->GetTbrConfig();
797
		$cflink['enabled'] = $this->GetEnabled();
798
		if (is_array($this->queues)) {
799
			$cflink['queue'] = array();
800
			foreach ($this->queues as $q) {
801
				$cflink['queue'][$q->GetQname()] = array();
802
				$q->copy_queue($interface, $cflink['queue'][$q->GetQname()]);
803
			}
804
		}
805
	}
806

    
807
	function &get_queue_list(&$q = null) {
808
		$qlist = array();
809

    
810
		//$qlist[$this->GetQname()] = & $this;
811
		if (is_array($this->queues)) {
812
			foreach ($this->queues as $queue) {
813
				$queue->get_queue_list($qlist);
814
			}
815
		}
816
		return $qlist;
817
	}
818

    
819
	function &add_queue($interface, &$queue, &$path, &$input_errors) {
820

    
821
		if (!is_array($this->queues)) {
822
			$this->queues = array();
823
		}
824

    
825
		switch ($this->GetScheduler()) {
826
			case "PRIQ":
827
				$__tmp_q = new priq_queue(); $q =& $__tmp_q;
828
				break;
829
			case "HFSC":
830
				$__tmp_q = new hfsc_queue(); $q =& $__tmp_q;
831
				break;
832
			case "CBQ":
833
				$__tmp_q = new cbq_queue(); $q =& $__tmp_q;
834
				break;
835
			case "FAIRQ":
836
				$__tmp_q = new fairq_queue(); $q =& $__tmp_q;
837
				break;
838
			default:
839
				/* XXX: but should not happen anyway */
840
				return;
841
				break;
842
		}
843
		$q->SetLink($path);
844
		$q->SetInterface($this->GetInterface());
845
		$q->SetEnabled("on");
846
		$q->SetParent($this);
847
		$q->ReadConfig($queue);
848
		$q->validate_input($queue, $input_errors);
849

    
850
		$this->queues[$q->GetQname()] = &$q;
851
		ref_on_altq_queue_list($this->GetQname(), $q->GetQname());
852
		if (is_array($queue['queue'])) {
853
			foreach ($queue['queue'] as $key1 => $que) {
854
				array_push($path, $key1);
855
				$q->add_queue($q->GetInterface(), $que, $path, $input_errors);
856
				array_pop($path);
857
			}
858
		}
859

    
860
		return $q;
861
	}
862

    
863
	/* interface here might be optional */
864
	function &find_queue($interface, $qname) {
865
		if ($qname == $this->GetQname()) {
866
			return $this;
867
		}
868
		foreach ($this->queues as $q) {
869
			$result =& $q->find_queue("", $qname);
870
			if ($result) {
871
				return $result;
872
			}
873
		}
874
	}
875

    
876
	function &find_parentqueue($interface, $qname) {
877
		if ($qname == $interface) {
878
			$result = NULL;
879
		} else if ($this->queues[$qname]) {
880
			$result = $this;
881
		} else if ($this->GetScheduler() <> "PRIQ") {
882
			foreach ($this->queues as $q) {
883
				$result = $q->find_parentqueue("", $qname);
884
				if ($result) {
885
					return $result;
886
				}
887
			}
888
		}
889
	}
890

    
891
	function build_tree() {
892
		global $shaperIFlist;
893

    
894
		$tree = " <li><a href=\"firewall_shaper.php?interface=".$this->GetInterface()."&amp;queue=". $this->GetInterface()."&amp;action=show";
895
		$tree .= "\">" . $shaperIFlist[$this->GetInterface()] . "</a>";
896
		if (is_array($this->queues)) {
897
			$tree .= "<ul>";
898
			foreach ($this->queues as $q) {
899
				$tree .= $q->build_tree();
900
			}
901
			$tree .= "</ul>";
902
		}
903
		$tree .= "</li>";
904
		return $tree;
905
	}
906

    
907
	function delete_queue() {
908
		foreach ($this->queues as $q)
909
			$q->delete_queue();
910
		unset_object_by_reference($this->GetLink());
911
	}
912

    
913
	function delete_all() {
914
		if (count($this->queues)) {
915
			foreach ($this->queues as $q) {
916
				$q->delete_all();
917
				unset_object_by_reference($q->GetLink());
918
				unset($q);
919
			}
920
			unset($this->queues);
921
		}
922
	}
923

    
924
	/*
925
	 * First it spits:
926
	 * altq on $interface ..............
927
	 *	then it goes like
928
	 *	foreach ($queues as $qkey => $queue) {
929
	 *		this->queues[$qkey]->build_rule();
930
	 *	}
931
	 */
932
	function build_rules(&$default = false) {
933
		if (count($this->queues) > 0 && $this->GetEnabled() == "on") {
934
			$default = false;
935
			$rules = " altq on " . get_real_interface($this->GetInterface());
936
			if ($this->GetScheduler()) {
937
				$rules .= " ".strtolower($this->GetScheduler());
938
			}
939
			if ($this->GetQlimit() > 0) {
940
				$rules .= " qlimit " . $this->GetQlimit() . " ";
941
			}
942
			if ($this->GetBandwidth()) {
943
				$rules .= " bandwidth ".trim($this->GetBandwidth());
944
				if ($this->GetBwscale()) {
945
					$rules .= $this->GetBwscale();
946
				}
947
			}
948
			if ($this->GetTbrConfig()) {
949
				$rules .= " tbrsize ".$this->GetTbrConfig();
950
			}
951
			if (count($this->queues)) {
952
				$i = count($this->queues);
953
				$rules .= " queue { ";
954
				foreach ($this->queues as $qkey => $qnone) {
955
					if ($i > 1) {
956
						$i--;
957
						$rules .= " {$qkey}, ";
958
					} else {
959
						$rules .= " {$qkey} ";
960
					}
961
				}
962
				$rules .= " } \n";
963
				foreach ($this->queues as $q) {
964
					$rules .= $q->build_rules($default);
965
				}
966
			}
967

    
968
			if ($default == false) {
969
				$error = sprintf(gettext("SHAPER: no default queue specified for interface %s."), $this->GetInterface()) . " " . gettext("The interface queue will be enforced as default.");
970
				file_notice("Shaper", $error, "Error occurred", "");
971
				unset($error);
972
				return "\n";
973
			}
974
			$frule .= $rules;
975
		} else if ($this->GetEnabled() == "on" && $this->GetScheduler() == "CODELQ") {
976
			$rules = " altq on " . get_real_interface($this->GetInterface());
977
			if ($this->GetScheduler()) {
978
				$rules .= " ".strtolower($this->GetScheduler());
979
			}
980
			if ($this->GetQlimit() > 0) {
981
				$rules .= " ( qlimit " . $this->GetQlimit() . " ) ";
982
			}
983
			if ($this->GetBandwidth()) {
984
				$rules .= " bandwidth ".trim($this->GetBandwidth());
985
				if ($this->GetBwscale()) {
986
					$rules .= $this->GetBwscale();
987
				}
988
			}
989
			if ($this->GetTbrConfig()) {
990
				$rules .= " tbrsize ".$this->GetTbrConfig();
991
			}
992

    
993
			$rules .= " queue";
994
		}
995

    
996
		$rules .= " \n";
997
		return $rules;
998
	}
999

    
1000
	function build_javascript() {
1001
		$javascript = "<script type=\"text/javascript\">";
1002
		$javascript .= "//<![CDATA[\n";
1003
		$javascript .= "function mySuspend() {";
1004
		$javascript .= "if (document.layers && document.layers['shaperarea'] != null) ";
1005
		$javascript .= "document.layers['shaperarea'].visibility = 'hidden'; ";
1006
		$javascript .= "else if (document.all)";
1007
		$javascript .= "document.all['shaperarea'].style.visibility = 'hidden';";
1008
		$javascript .= "}";
1009

    
1010
		$javascript .= "function myResume() {";
1011
		$javascript .= "if (document.layers && document.layers['shaperarea'] != null) ";
1012
		$javascript .= "document.layers['shaperarea'].visibility = 'visible';";
1013
		$javascript .= "else if (document.all) ";
1014
		$javascript .= "document.all['shaperarea'].style.visibility = 'visible';";
1015
		$javascript .= "}";
1016
		$javascript .= "//]]>";
1017
		$javascript .= "</script>";
1018

    
1019
		return $javascript;
1020
	}
1021

    
1022
	function build_shortform() {
1023
		global $g;
1024

    
1025
		$altq =& $this;
1026

    
1027
		if ($altq) {
1028
			$scheduler = ": " . $altq->GetScheduler();
1029
		}
1030

    
1031
		$form = '<dl class="dl-horizontal">';
1032
		$form .= '	<dt>';
1033
		$form .= '		<a href="firewall_shaper.php?interface=' . $this->GetInterface() . '&amp;queue=' . $this->GetQname() . '&amp;action=show">' . $shaperIFlist[$this->GetInterface()] . '</a>';
1034
		$form .= '	</dt>';
1035
		$form .= '	<dd>';
1036
		$form .=		$scheduler;
1037
		$form .= '	</dd>';
1038

    
1039
		$form .= '	<dt>';
1040
		$form .=		'Bandwidth';
1041
		$form .= '	</dt>';
1042
		$form .= '	<dd>';
1043
		$form .=		$this->GetBandwidth() . '&nbsp;' . $this->GetBwscaleText();
1044
		$form .= '	</dd>';
1045

    
1046
		$form .= '	<dt>';
1047
		$form .= 'Disable';
1048
		$form .= '	<dt>';
1049
		$form .= '	<dd>';
1050

    
1051
		$form .= '<a class="btn btn-danger btn-xs" href="firewall_shaper_queues.php?interface=';
1052
		$form .= $this->GetInterface() . '&amp;queue=';
1053
		$form .= $this->GetQname() . '&amp;action=delete">';
1054
		$form .= '<i class="fa fa-trash icon-embed-btn"></i>';
1055
		$form .= gettext("Remove shaper from this interface") . '</a>';
1056

    
1057
		$form .= '	</dd>';
1058

    
1059
		$form .= '</dl>';
1060

    
1061
		return $form;
1062

    
1063
	}
1064

    
1065
	/*
1066
	 * For requesting the parameters of the root queues
1067
	 * to the user like the traffic wizard does.
1068
	 */
1069
	function build_form() {
1070

    
1071
		$sform = new Form();
1072

    
1073
		$sform->setAction("firewall_shaper.php");
1074

    
1075
		$section = new Form_Section(null);
1076

    
1077
		$section->addInput(new Form_Checkbox(
1078
			'enabled',
1079
			'Enable/Disable',
1080
			'Enable/disable discipline and its children',
1081
			($this->GetEnabled() == "on"),
1082
			'on'
1083
		));
1084

    
1085
		$section->addInput(new Form_StaticText(
1086
			'*Name',
1087
			$this->GetQname()
1088
		));
1089

    
1090
		$section->addInput(new Form_Select(
1091
			'scheduler',
1092
			'Scheduler Type',
1093
			$this->GetScheduler(),
1094
			array('HFSC' => 'HFSC',
1095
				  'CBQ' => 'CBQ',
1096
				  'FAIRQ' => 'FAIRQ',
1097
				  'CODELQ' => 'CODELQ',
1098
				  'PRIQ' => 'PRIQ')
1099
		))->setHelp('Changing this changes all child queues! Beware information can be lost.');
1100

    
1101
		$group = new Form_group('Bandwidth');
1102

    
1103
		$group->add(new Form_Input(
1104
			'bandwidth',
1105
			null,
1106
			'number',
1107
			$this->GetBandwidth()
1108
		));
1109

    
1110
		$group->add(new Form_Select(
1111
			'bandwidthtype',
1112
			null,
1113
			$this->GetBwscale(),
1114
			array('Kb' => 'Kbit/s',
1115
				  'Mb' => 'Mbit/s',
1116
				  'Gb' => 'Gbit/s',
1117
				  'b' => 'Bit/s',
1118
				  '%' => '%')
1119
		));
1120

    
1121
		$section->add($group);
1122

    
1123
		$section->addInput(new Form_Input(
1124
			'qlimit',
1125
			'Queue Limit',
1126
			'number',
1127
			$this->GetQlimit()
1128
		));
1129

    
1130
		$section->addInput(new Form_Input(
1131
			'tbrconfig',
1132
			'TBR Size',
1133
			'number',
1134
			$this->GetTbrConfig()
1135
		))->setHelp('Adjusts the size, in bytes, of the token bucket regulator. If not specified, heuristics based on the interface ' .
1136
					'bandwidth are used to determine the size.');
1137

    
1138
		$section->addInput(new Form_Input(
1139
			'interface',
1140
			null,
1141
			'hidden',
1142
			$this->GetInterface()
1143
		));
1144

    
1145
		$section->addInput(new Form_Input(
1146
			'name',
1147
			null,
1148
			'hidden',
1149
			$this->GetQname()
1150
		));
1151

    
1152
		$sform->add($section);
1153

    
1154
		return($sform);
1155
	}
1156

    
1157
	function update_altq_queue_data(&$data) {
1158
		$this->ReadConfig($data);
1159
	}
1160

    
1161
	/*
1162
	 * Should call on each of it queues and subqueues
1163
	 * the same function much like build_rules();
1164
	 */
1165
	function wconfig() {
1166
		$cflink = &get_reference_to_me_in_config($this->GetLink());
1167
		if (!is_array($cflink)) {
1168
			$cflink = array();
1169
		}
1170
		$cflink['interface'] = $this->GetInterface();
1171
		$cflink['name'] = $this->GetQname();
1172
		$cflink['scheduler'] = $this->GetScheduler();
1173
		$cflink['bandwidth'] = $this->GetBandwidth();
1174
		$cflink['bandwidthtype'] = $this->GetBwscale();
1175
		$cflink['qlimit'] = trim($this->GetQlimit());
1176
		if (empty($cflink['qlimit'])) {
1177
			unset($cflink['qlimit']);
1178
		}
1179
		$cflink['tbrconfig'] = trim($this->GetTbrConfig());
1180
		if (empty($cflink['tbrconfig'])) {
1181
			unset($cflink['tbrconfig']);
1182
		}
1183
		$cflink['enabled'] = $this->GetEnabled();
1184
		if (empty($cflink['enabled'])) {
1185
			unset($cflink['enabled']);
1186
		}
1187
	}
1188

    
1189
}
1190

    
1191
class priq_queue {
1192
	var $qname;
1193
	var $qinterface;
1194
	var $qlimit;
1195
	var $qpriority;
1196
	var $description;
1197
	var $isparent;
1198
	var $qbandwidth;
1199
	var $qbandwidthtype;
1200
	var $qdefault = "";
1201
	var $qrio = "";
1202
	var $qred = "";
1203
	var $qcodel = "";
1204
	var $qecn = "";
1205
	var $qack;
1206
	var $qenabled = "";
1207
	var $qparent;
1208
	var $link;
1209

    
1210
	/* This is here to help with form building and building rules/lists */
1211
	var $subqueues = array();
1212

    
1213
	/* Accessor functions */
1214
	function SetLink($link) {
1215
		$this->link = $link;
1216
	}
1217
	function GetLink() {
1218
		return $this->link;
1219
	}
1220
	function &GetParent() {
1221
		return $this->qparent;
1222
	}
1223
	function SetParent(&$parent) {
1224
		$this->qparent = &$parent;
1225
	}
1226
	function GetEnabled() {
1227
		return $this->qenabled;
1228
	}
1229
	function SetEnabled($value) {
1230
		$this->qenabled = $value;
1231
	}
1232
	function CanHaveChildren() {
1233
		return false;
1234
	}
1235
	function CanBeDeleted() {
1236
		return true;
1237
	}
1238
	function GetQname() {
1239
		return $this->qname;
1240
	}
1241
	function SetQname($name) {
1242
		$this->qname = trim($name);
1243
	}
1244
	function GetBandwidth() {
1245
		return $this->qbandwidth;
1246
	}
1247
	function SetBandwidth($bandwidth) {
1248
		$this->qbandwidth = $bandwidth;
1249
	}
1250
	function GetInterface() {
1251
		return $this->qinterface;
1252
	}
1253
	function SetInterface($name) {
1254
		$this->qinterface = trim($name);
1255
	}
1256
	function GetQlimit() {
1257
		return $this->qlimit;
1258
	}
1259
	function SetQlimit($limit) {
1260
		$this->qlimit = $limit;
1261
	}
1262
	function GetQpriority() {
1263
		return $this->qpriority;
1264
	}
1265
	function SetQpriority($priority) {
1266
		$this->qpriority = $priority;
1267
	}
1268
	function GetDescription() {
1269
		return $this->description;
1270
	}
1271
	function SetDescription($str) {
1272
		$this->description = trim($str);
1273
	}
1274
	function GetFirstime() {
1275
		return $this->firsttime;
1276
	}
1277
	function SetFirsttime($number) {
1278
		$this->firsttime = $number;
1279
	}
1280
	function CheckBandwidth($bw, $bwtype) {
1281
		$parent = $this->GetParent();
1282

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

    
1288
		if ($bwtype != "%") {
1289
			$sum = $parent->GetTotalBw(($bw > 0) ? $this : NULL);
1290

    
1291
			if ($bw > 0) {
1292
				$sum += get_bandwidth($bw, $bwtype, $parent);
1293
			}
1294

    
1295
			if ($sum > get_queue_bandwidth($parent)) {
1296
				return 1;
1297
			}
1298
		}
1299

    
1300
		foreach ($this->subqueues as $q) {
1301
			if ($q->CheckBandwidth(0, '')) {
1302
				return 1;
1303
			}
1304
		}
1305

    
1306
		return 0;
1307
	}
1308
	function GetTotalBw($qignore = NULL) {
1309
		$sum = 0;
1310
		foreach ($this->subqueues as $q) {
1311
			if ($qignore != NULL && $qignore == $q)
1312
				continue;
1313
			$sum += get_bandwidth($q->GetBandwidth(), $q->GetBwscale(), $q->GetParent());
1314
		}
1315

    
1316
		return $sum;
1317
	}
1318
	function GetBwscale() {
1319
		return $this->qbandwidthtype;
1320
	}
1321
	function SetBwscale($scale) {
1322
		$this->qbandwidthtype = $scale;
1323
	}
1324
	function GetDefaultQueuePresent() {
1325
		if ($this->GetDefault()) {
1326
			return true;
1327
		}
1328
		if (!empty($this->subqueues)) {
1329
			foreach ($this->subqueues as $q) {
1330
				if ($q->GetDefault()) {
1331
					return true;
1332
				}
1333
			}
1334
		}
1335

    
1336
		return false;
1337
	}
1338
	function GetDefault() {
1339
		return $this->qdefault;
1340
	}
1341
	function SetDefault($value = false) {
1342
		$this->qdefault = $value;
1343
	}
1344
	function GetCodel() {
1345
		return $this->codel;
1346
	}
1347
	function SetCodel($codel = false) {
1348
		$this->codel = $codel;
1349
	}
1350
	function GetRed() {
1351
		return $this->qred;
1352
	}
1353
	function SetRed($red = false) {
1354
		$this->qred = $red;
1355
	}
1356
	function GetRio() {
1357
		return $this->qrio;
1358
	}
1359
	function SetRio($rio = false) {
1360
		$this->qrio = $rio;
1361
	}
1362
	function GetEcn() {
1363
		return $this->qecn;
1364
	}
1365
	function SetEcn($ecn = false) {
1366
		$this->qecn = $ecn;
1367
	}
1368
	function GetAck() {
1369
		return $this->qack;
1370
	}
1371
	function SetAck($ack = false) {
1372
		$this->qack = $ack;
1373
	}
1374

    
1375
	function GetBwscaleText() {
1376
		switch ($this->qbandwidthtype) {
1377
			case "b":
1378
				$bwscaletext = "Bit/s";
1379
				break;
1380
			case "Kb":
1381
				$bwscaletext = "Kbit/s";
1382
				break;
1383
			case "Mb":
1384
				$bwscaletext = "Mbit/s";
1385
				break;
1386
			case "Gb":
1387
				$bwscaletext = "Gbit/s";
1388
				break;
1389
			default:
1390
				/* For others that do not need translating like % */
1391
				$bwscaletext = $this->qbandwidthtype;
1392
				break;
1393
		}
1394
		return $bwscaletext;
1395
	}
1396

    
1397
	function build_javascript() {
1398
		$javascript = "<script type=\"text/javascript\">";
1399
		$javascript .= "//<![CDATA[\n";
1400
		$javascript .= "function mySuspend() { \n";
1401
		$javascript .= "if (document.layers && document.layers['shaperarea'] != null)\n";
1402
		$javascript .= "document.layers['shaperarea'].visibility = 'hidden';\n";
1403
		$javascript .= "else if (document.all)\n";
1404
		$javascript .= "document.all['shaperarea'].style.visibility = 'hidden';\n";
1405
		$javascript .= "}\n";
1406

    
1407
		$javascript .= "function myResume() {\n";
1408
		$javascript .= "if (document.layers && document.layers['shaperarea'] != null)\n";
1409
		$javascript .= "document.layers['shaperarea'].visibility = 'visible';\n";
1410
		$javascript .= "else if (document.all)\n";
1411
		$javascript .= "document.all['shaperarea'].style.visibility = 'visible';\n";
1412
		$javascript .= "}\n";
1413
		$javascript .= "//]]>";
1414
		$javascript .= "</script>";
1415

    
1416
		return $javascript;
1417
	}
1418

    
1419
	function &add_queue($interface, &$qname, &$path, &$input_errors) { return; }
1420

    
1421
	/*
1422
	 * Currently this will not be called unless we decide to clone a whole
1423
	 * queue tree on the 'By Queues' view or support drag&drop on the tree/list
1424
	 */
1425
	function copy_queue($interface, &$cflink) {
1426

    
1427
		$cflink['name'] = $this->GetQname();
1428
		$cflink['interface'] = $interface;
1429
		$cflink['qlimit'] = $this->GetQlimit();
1430
		$cflink['priority'] = $this->GetQpriority();
1431
		$cflink['description'] = $this->GetDescription();
1432
		$cflink['enabled'] = $this->GetEnabled();
1433
		$cflink['default'] = $this->GetDefault();
1434
		$cflink['red'] = $this->GetRed();
1435
		$cflink['codel'] = $this->GetCodel();
1436
		$cflink['rio'] = $this->GetRio();
1437
		$cflink['ecn'] = $this->GetEcn();
1438

    
1439
		if (is_array($this->subqueues)) {
1440
			$cflinkp['queue'] = array();
1441
			foreach ($this->subqueues as $q) {
1442
				$cflink['queue'][$q->GetQname()] = array();
1443
				$q->copy_queue($interface, $cflink['queue'][$q->GetQname()]);
1444
			}
1445
		}
1446
	}
1447

    
1448
	function clean_queue($sched) {
1449
		clean_child_queues($sched, $this->GetLink());
1450
		if (is_array($this->subqueues)) {
1451
			foreach ($this->subqueues as $q) {
1452
				$q->clean_queue($sched);
1453
			}
1454
		}
1455
	}
1456

    
1457
	function &get_queue_list(&$qlist) {
1458

    
1459
		$qlist[$this->GetQname()] = & $this;
1460
		if (is_array($this->subqueues)) {
1461
			foreach ($this->subqueues as $queue) {
1462
				$queue->get_queue_list($qlist);
1463
			}
1464
		}
1465
	}
1466

    
1467
	function delete_queue() {
1468
		unref_on_altq_queue_list($this->GetQname());
1469
		cleanup_queue_from_rules($this->GetQname());
1470
		unset_object_by_reference($this->GetLink());
1471
	}
1472

    
1473
	function delete_all() {
1474
		if (count($this->subqueues)) {
1475
			foreach ($this->subqueues as $q) {
1476
				$q->delete_all();
1477
				unset_object_by_reference($q->GetLink());
1478
				unset($q);
1479
			}
1480
			unset($this->subqueues);
1481
		}
1482
	}
1483

    
1484
	function &find_queue($interface, $qname) {
1485
		if ($qname == $this->GetQname()) {
1486
			return $this;
1487
		}
1488
	}
1489

    
1490
	function find_parentqueue($interface, $qname) { return; }
1491

    
1492
	function validate_input($data, &$input_errors) {
1493

    
1494
		$reqdfields[] = "name";
1495
		$reqdfieldsn[] = gettext("Name");
1496
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
1497

    
1498
		if ($data['bandwidth'] && (!is_numeric($data['bandwidth']))) {
1499
			$input_errors[] = gettext("Bandwidth must be an integer.");
1500
		}
1501
		if ($data['bandwidth'] < 0) {
1502
			$input_errors[] = gettext("Bandwidth cannot be negative.");
1503
		}
1504
		if ($data['bandwidthtype'] == "%") {
1505
			if ($data['bandwidth'] > 100 || $data['bandwidth'] < 0) {
1506
				$input_errors[] = gettext("Bandwidth in percentage should be between 1 and 100.");
1507
			}
1508
		}
1509
		if ($this->CheckBandwidth($data['bandwidth'], $data['bandwidthtype']))
1510
			$input_errors[] = "The sum of child bandwidth is higher than parent.";
1511

    
1512
		if (isset($data['priority']) && (!is_numeric($data['priority']) ||
1513
		    ($data['priority'] < 0) || ($data['priority'] > 15))) {
1514
			$input_errors[] = gettext("The priority must be an integer between 0 and 15.");
1515
		}
1516
		if ($data['qlimit'] && (!is_numeric($data['qlimit']))) {
1517
				$input_errors[] = gettext("Queue limit must be an integer");
1518
		}
1519
		if ($data['qlimit'] < 0) {
1520
				$input_errors[] = gettext("Queue limit must be positive");
1521
		}
1522
		if (!empty($data['newname']) && !preg_match("/^[a-zA-Z0-9_-]*$/", $data['newname'])) {
1523
			$input_errors[] = gettext("Queue names must be alphanumeric and _ or - only.");
1524
		}
1525
		if (!empty($data['name']) && !preg_match("/^[a-zA-Z0-9_-]*$/", $data['name'])) {
1526
			$input_errors[] = gettext("Queue names must be alphanumeric and _ or - only.");
1527
		}
1528
		$default = $this->GetDefault();
1529
		if (!empty($data['default']) && altq_get_default_queue($data['interface']) && empty($default)) {
1530
			$input_errors[] = gettext("Only one default queue per interface is allowed.");
1531
		}
1532
	}
1533

    
1534
	function ReadConfig(&$q) {
1535
		if (!empty($q['name']) && !empty($q['newname']) && $q['name'] != $q['newname']) {
1536
			$this->SetQname($q['newname']);
1537
		} else if (!empty($q['newname'])) {
1538
			$this->SetQname($q['newname']);
1539
		} else if (isset($q['name'])) {
1540
			$this->SetQname($q['name']);
1541
		}
1542
		if (isset($q['interface'])) {
1543
			$this->SetInterface($q['interface']);
1544
		}
1545
		$this->SetBandwidth($q['bandwidth']);
1546
		if ($q['bandwidthtype'] <> "") {
1547
			$this->SetBwscale($q['bandwidthtype']);
1548
		}
1549
		if (!empty($q['qlimit'])) {
1550
			$this->SetQlimit($q['qlimit']);
1551
		} else {
1552
			$this->SetQlimit(""); // Default
1553
		}
1554
		if (is_numeric($q['priority'])) {
1555
			$this->SetQPriority($q['priority']);
1556
		} else {
1557
			$this->SetQpriority("");
1558
		}
1559
		if (!empty($q['description'])) {
1560
			$this->SetDescription($q['description']);
1561
		} else {
1562
			$this->SetDescription("");
1563
		}
1564
		if (!empty($q['red'])) {
1565
			$this->SetRed($q['red']);
1566
		} else {
1567
			$this->SetRed();
1568
		}
1569
		if (!empty($q['codel'])) {
1570
			$this->SetCodel($q['codel']);
1571
		} else {
1572
			$this->SetCodel();
1573
		}
1574
		if (!empty($q['rio'])) {
1575
			$this->SetRio($q['rio']);
1576
		} else {
1577
			$this->SetRio();
1578
		}
1579
		if (!empty($q['ecn'])) {
1580
			$this->SetEcn($q['ecn']);
1581
		} else {
1582
			$this->SetEcn();
1583
		}
1584
		if (!empty($q['default'])) {
1585
			$this->SetDefault($q['default']);
1586
		} else {
1587
			$this->SetDefault();
1588
		}
1589
		if (!empty($q['enabled'])) {
1590
			$this->SetEnabled($q['enabled']);
1591
		} else {
1592
			$this->SetEnabled("");
1593
		}
1594
	}
1595

    
1596
	function build_tree() {
1597
		$tree = " <li><a href=\"firewall_shaper.php?interface=". $this->GetInterface()."&amp;queue=". $this->GetQname()."&amp;action=show";
1598
		$tree .= "\" ";
1599
		$tmpvalue = $this->GetDefault();
1600
		if (!empty($tmpvalue)) {
1601
			$tree .= " class=\"navlnk\"";
1602
		}
1603
		$tree .= " >" . $this->GetQname() . "</a>";
1604
		/*
1605
		 * Not needed here!
1606
		 * if (is_array($queues) {
1607
		 *	  $tree .= "<ul>";
1608
		 *	  foreach ($q as $queues)
1609
		 *		  $tree .= $queues['$q->GetName()']->build_tree();
1610
		 *	  endforeach
1611
		 *	  $tree .= "</ul>";
1612
		 * }
1613
		 */
1614

    
1615
		$tree .= "</li>";
1616

    
1617
		return $tree;
1618
	}
1619

    
1620
	/* Should return something like:
1621
	 * queue $qname on $qinterface bandwidth ....
1622
	 */
1623
	function build_rules(&$default = false) {
1624
		$pfq_rule = " queue ". $this->qname;
1625
		if ($this->GetInterface()) {
1626
			$pfq_rule .= " on ".get_real_interface($this->GetInterface());
1627
		}
1628
		$tmpvalue = $this->GetQpriority();
1629
		if (is_numeric($tmpvalue)) {
1630
			$pfq_rule .= " priority ".$this->GetQpriority();
1631
		}
1632
		$tmpvalue = $this->GetQlimit();
1633
		if (!empty($tmpvalue)) {
1634
			$pfq_rule .= " qlimit " . $this->GetQlimit();
1635
		}
1636
		if ($this->GetRed() || $this->GetRio() || $this->GetEcn() || $this->GetDefault() || $this->GetCodel()) {
1637
			$pfq_rule .= " priq ( ";
1638
			$tmpvalue = $this->GetRed();
1639
			if (!empty($tmpvalue)) {
1640
				$comma = 1;
1641
				$pfq_rule .= " red ";
1642
			}
1643
			$tmpvalue = $this->GetRio();
1644
			if (!empty($tmpvalue)) {
1645
				if ($comma) {
1646
					$pfq_rule .= " ,";
1647
				}
1648
				$comma = 1;
1649
				$pfq_rule .= " rio ";
1650
			}
1651
			$tmpvalue = $this->GetEcn();
1652
			if (!empty($tmpvalue)) {
1653
				if ($comma) {
1654
					$pfq_rule .= " ,";
1655
				}
1656
				$comma = 1;
1657
				$pfq_rule .= " ecn ";
1658
			}
1659
			$tmpvalue = $this->GetCodel();
1660
			if (!empty($tmpvalue)) {
1661
				if ($comma) {
1662
					$pfq_rule .= " ,";
1663
				}
1664
				$comma = 1;
1665
				$pfq_rule .= " codel ";
1666
			}
1667
			$tmpvalue = $this->GetDefault();
1668
			if (!empty($tmpvalue)) {
1669
				if ($comma) {
1670
					$pfq_rule .= " ,";
1671
				}
1672
				$pfq_rule .= " default ";
1673
				$default = true;
1674
			}
1675
			$pfq_rule .= " ) ";
1676
		}
1677

    
1678
		$pfq_rule .= " \n";
1679

    
1680
		return $pfq_rule;
1681
	}
1682

    
1683
	/*
1684
	 * To return the html form to show to user
1685
	 * for getting the parameters.
1686
	 * Should do even for first time when the
1687
	 * object is created and later when we may
1688
	 * need to update it. (2)
1689
	 */
1690

    
1691
	function build_form() {
1692

    
1693
		$sform = new Form();
1694

    
1695
		$sform->setAction("firewall_shaper.php");
1696

    
1697
		$section = new Form_Section("");
1698

    
1699
		$section->addInput(new Form_Checkbox(
1700
			'enabled',
1701
			'Enable/Disable',
1702
			'Enable/disable discipline and its children',
1703
			($this->GetEnabled() == "on"),
1704
			'on'
1705
		));
1706

    
1707
		$section->addInput(new Form_Input(
1708
			'newname',
1709
			'*Name',
1710
			'text',
1711
			$this->GetQname()
1712
		))->setHelp('Enter the name of the queue here. Do not use spaces and limit the size to 15 characters.');
1713

    
1714
		$section->addInput(new Form_Input(
1715
			'name',
1716
			null,
1717
			'hidden',
1718
			$this->GetQname()
1719
		));
1720

    
1721
		if (!is_a($this, "hfsc_queue")) {
1722
			$section->addInput(new Form_Input(
1723
				'priority',
1724
				'Priority',
1725
				'number',
1726
				$this->GetQpriority(),
1727
				['min' => '0', 'max'=> '']
1728
			))->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.');
1729
		}
1730

    
1731
		$section->addInput(new Form_Input(
1732
			'qlimit',
1733
			'Queue Limit',
1734
			'number',
1735
			$this->GetQlimit()
1736
		))->setHelp('Queue limit in packets.');
1737

    
1738
		$group = new Form_Group('Scheduler options');
1739

    
1740
		if (empty($this->subqueues)) {
1741
			$group->add(new Form_Checkbox(
1742
				'default',
1743
				null,
1744
				null,
1745
				$this->GetDefault(),
1746
				'default'
1747
			))->setHelp('Default Queue');
1748
		}
1749

    
1750
		$group->add(new Form_Checkbox(
1751
			'red',
1752
			null,
1753
			null,
1754
			!empty($this->GetRed())
1755
		))->setHelp('%1$sRandom Early Detection%2$s', '<a target="_new" href="https://web.archive.org/web/20160404153707/http://www.openbsd.org/faq/pf/queueing.html#red">', '</a>');
1756

    
1757
		$group->add(new Form_Checkbox(
1758
			'rio',
1759
			null,
1760
			null,
1761
			!empty($this->GetRio())
1762
		))->setHelp('%1$sRandom Early Detection In and Out%2$s', '<a target="_new" href="https://web.archive.org/web/20160404153707/http://www.openbsd.org/faq/pf/queueing.html#rio">', '</a>');
1763

    
1764
		$group->add(new Form_Checkbox(
1765
			'ecn',
1766
			null,
1767
			null,
1768
			!empty($this->GetEcn())
1769
		))->setHelp('%1$sExplicit Congestion Notification%2$s', '<a target="_new" href="https://web.archive.org/web/20160404153707/http://www.openbsd.org/faq/pf/queueing.html#ecn">', '</a>');
1770

    
1771
		$group->add(new Form_Checkbox(
1772
			'codel',
1773
			null,
1774
			null,
1775
			!empty($this->GetCodel())
1776
		))->setHelp('%1$sCodel Active Queue%2$s', '<a target="_new" href="https://web.archive.org/web/20160404153707/http://www.openbsd.org/faq/pf/queueing.html#ecn">', '</a>');
1777

    
1778
		$group->setHelp('Select options for this queue');
1779

    
1780
		$section->add($group);
1781

    
1782
		$section->addInput(new Form_Input(
1783
			'description',
1784
			'Description',
1785
			'text',
1786
			$this->GetDescription()
1787
		));
1788

    
1789
		$sform->add($section);
1790

    
1791
		$sform->addGlobal(new Form_Input(
1792
			'interface',
1793
			null,
1794
			'hidden',
1795
			$this->GetInterface()
1796
		));
1797

    
1798
		$sform->addGlobal(new Form_Input(
1799
			'name',
1800
			null,
1801
			'hidden',
1802
			$this->GetQname()
1803
		));
1804

    
1805
		return($sform);
1806
	}
1807

    
1808
	function build_shortform() {
1809
		/* XXX: Hacks in sight. Mostly layer violations!  */
1810
		global $g, $altq_list_queues;
1811
		global $shaperIFlist;
1812

    
1813
		$altq =& $altq_list_queues[$this->GetInterface()];
1814

    
1815
		if ($altq) {
1816
			$scheduler = $altq->GetScheduler();
1817
		}
1818

    
1819
		$form = '<dl class="dl-horizontal">';
1820
		$form .= '	<dt>';
1821
		$form .= '		<a href="firewall_shaper.php?interface=' . $this->GetInterface() . '&amp;queue=' . $this->GetQname() . '&amp;action=show">' . $shaperIFlist[$this->GetInterface()] . '</a>';
1822
		$form .= '	</dt>';
1823
		$form .= '	<dd>';
1824
		$form .=		$scheduler;
1825
		$form .= '	</dd>';
1826

    
1827
		$form .= '	<dt>';
1828
		$form .=		'Bandwidth';
1829
		$form .= '	</dt>';
1830
		$form .= '	<dd>';
1831
		$form .=		$this->GetBandwidth() . '&nbsp;' . $this->GetBwscaleText();
1832
		$form .= '	</dd>';
1833

    
1834
		$tmpvalue = $this->GetQpriority();
1835
		if (!empty($tmpvalue)) {
1836
			$form .= '	<dt>';
1837
			$form .=		'Priority';
1838
			$form .= '	<dt>';
1839
			$form .= '	<dd>';
1840
			$form .=		'On';
1841
			$form .= '	</dd>';
1842
		}
1843

    
1844
		$tmpvalue = $this->GetDefault();
1845
		if (!empty($tmpvalue)) {
1846
			$form .= '	<dt>';
1847
			$form .=		'Default';
1848
			$form .= '	<dt>';
1849
			$form .= '	<dd>';
1850
			$form .=		'On';
1851
			$form .= '	</dd>';
1852
		}
1853

    
1854
			$form .= '	<dt>';
1855
			$form .= 'Delete';
1856
			$form .= '	<dt>';
1857
			$form .= '	<dd>';
1858

    
1859
			$form .= '<a class="btn btn-danger btn-xs" href="firewall_shaper_queues.php?interface=';
1860
			$form .= $this->GetInterface() . '&amp;queue=';
1861
			$form .= $this->GetQname() . '&amp;action=delete">';
1862
			$form .= '<i class="fa fa-trash icon-embed-btn"></i>';
1863
			$form .= gettext("Delete Queue from this Interface") . '</a>';
1864

    
1865
			$form .= '	</dd>';
1866

    
1867
			$form .= '</dl>';
1868

    
1869
		return $form;
1870

    
1871
	}
1872

    
1873
	function update_altq_queue_data(&$q) {
1874
		$this->ReadConfig($q);
1875
	}
1876

    
1877
	function wconfig() {
1878
		$cflink =& get_reference_to_me_in_config($this->GetLink());
1879
		if (!is_array($cflink)) {
1880
			$cflink = array();
1881
		}
1882
		$cflink['name'] = $this->GetQname();
1883
		$cflink['interface'] = $this->GetInterface();
1884
		$cflink['qlimit'] = trim($this->GetQlimit());
1885
		if (empty($cflink['qlimit'])) {
1886
			unset($cflink['qlimit']);
1887
		}
1888
		$cflink['priority'] = trim($this->GetQpriority());
1889
		if (!is_numeric($cflink['priority'])) {
1890
			unset($cflink['priority']);
1891
		}
1892
		$cflink['description'] = trim($this->GetDescription());
1893
		if (empty($cflink['description'])) {
1894
			unset($cflink['description']);
1895
		}
1896
		$cflink['enabled'] = trim($this->GetEnabled());
1897
		if (empty($cflink['enabled'])) {
1898
			unset($cflink['enabled']);
1899
		}
1900
		$cflink['default'] = trim($this->GetDefault());
1901
		if (empty($cflink['default'])) {
1902
			unset($cflink['default']);
1903
		}
1904
		$cflink['red'] = trim($this->GetRed());
1905
		if (empty($cflink['red'])) {
1906
			unset($cflink['red']);
1907
		}
1908
		$cflink['codel'] = trim($this->GetCodel());
1909
		if (empty($cflink['codel'])) {
1910
			unset($cflink['codel']);
1911
		}
1912
		$cflink['rio'] = trim($this->GetRio());
1913
		if (empty($cflink['rio'])) {
1914
			unset($cflink['rio']);
1915
		}
1916
		$cflink['ecn'] = trim($this->GetEcn());
1917
		if (empty($cflink['ecn'])) {
1918
			unset($cflink['ecn']);
1919
		}
1920
	}
1921
}
1922

    
1923
class hfsc_queue extends priq_queue {
1924
	/* realtime */
1925
	var $realtime;
1926
	var $r_m1;
1927
	var $r_d;
1928
	var $r_m2;
1929
	/* linkshare */
1930
	var $linkshare;
1931
	var $l_m1;
1932
	var $l_d;
1933
	var $l_m2;
1934
	/* upperlimit */
1935
	var $upperlimit;
1936
	var $u_m1;
1937
	var $u_d;
1938
	var $u_m2;
1939

    
1940
	/*
1941
	 * HFSC can have nested queues.
1942
	 */
1943
	function CanHaveChildren() {
1944
		return true;
1945
	}
1946
	function GetRealtime() {
1947
		return $this->realtime;
1948
	}
1949
	function GetR_m1() {
1950
		return $this->r_m1;
1951
	}
1952
	function GetR_d() {
1953
		return $this->r_d;
1954
	}
1955
	function GetR_m2() {
1956
		return $this->r_m2;
1957
	}
1958
	function SetRealtime() {
1959
		$this->realtime = "on";
1960
	}
1961
	function DisableRealtime() {
1962
		$this->realtime = "";
1963
	}
1964
	function SetR_m1($value) {
1965
		$this->r_m1 = $value;
1966
	}
1967
	function SetR_d($value) {
1968
		$this->r_d = $value;
1969
	}
1970
	function SetR_m2($value) {
1971
		$this->r_m2 = $value;
1972
	}
1973
	function GetLinkshare() {
1974
		return $this->linkshare;
1975
	}
1976
	function DisableLinkshare() {
1977
		$this->linkshare = "";
1978
	}
1979
	function GetL_m1() {
1980
		return $this->l_m1;
1981
	}
1982
	function GetL_d() {
1983
		return $this->l_d;
1984
	}
1985
	function GetL_m2() {
1986
		return $this->l_m2;
1987
	}
1988
	function SetLinkshare() {
1989
		$this->linkshare = "on";
1990
	}
1991
	function SetL_m1($value) {
1992
		$this->l_m1 = $value;
1993
	}
1994
	function SetL_d($value) {
1995
		$this->l_d = $value;
1996
	}
1997
	function SetL_m2($value) {
1998
		$this->l_m2 = $value;
1999
	}
2000
	function GetUpperlimit() {
2001
		return $this->upperlimit;
2002
	}
2003
	function GetU_m1() {
2004
		return $this->u_m1;
2005
	}
2006
	function GetU_d() {
2007
		return $this->u_d;
2008
	}
2009
	function GetU_m2() {
2010
		return $this->u_m2;
2011
	}
2012
	function SetUpperlimit() {
2013
		$this->upperlimit = "on";
2014
	}
2015
	function DisableUpperlimit() {
2016
		$this->upperlimit = "";
2017
	}
2018
	function SetU_m1($value) {
2019
		$this->u_m1 = $value;
2020
	}
2021
	function SetU_d($value) {
2022
		$this->u_d = $value;
2023
	}
2024
	function SetU_m2($value) {
2025
		$this->u_m2 = $value;
2026
	}
2027

    
2028
	function &add_queue($interface, &$qname, &$path, &$input_errors) {
2029

    
2030
		if (!is_array($this->subqueues)) {
2031
			$this->subqueues = array();
2032
		}
2033
		$__tmp_q = new hfsc_queue(); $q =& $__tmp_q;
2034
		$q->SetInterface($this->GetInterface());
2035
		$q->SetParent($this);
2036
		$q->ReadConfig($qname);
2037
		$q->validate_input($qname, $input_errors);
2038

    
2039
		$q->SetEnabled("on");
2040
		$q->SetLink($path);
2041

    
2042
		$this->subqueues[$q->GetQname()] =& $q; //new hfsc_queue()
2043
		ref_on_altq_queue_list($this->GetQname(), $q->GetQname());
2044
		if (is_array($qname['queue'])) {
2045
			foreach ($qname['queue'] as $key1 => $que) {
2046
				array_push($path, $key1);
2047
				$q->add_queue($q->GetInterface(), $que, $path, $input_errors);
2048
				array_pop($path);
2049
			}
2050
		}
2051

    
2052
		return $q;
2053
	}
2054

    
2055
	function copy_queue($interface, &$cflink) {
2056

    
2057
		$cflink['name'] = $this->GetQname();
2058
		$cflink['interface'] = $interface;
2059
		$cflink['qlimit'] = trim($this->GetQlimit());
2060
		if (empty($cflink['qlimit'])) {
2061
			unset($cflink['qlimit']);
2062
		}
2063
		$cflink['priority'] = trim($this->GetQpriority());
2064
		if (empty($cflink['priority'])) {
2065
			unset($cflink['priority']);
2066
		}
2067
		$cflink['description'] = trim($this->GetDescription());
2068
		if (empty($cflink['description'])) {
2069
			unset($cflink['description']);
2070
		}
2071
		$cflink['bandwidth'] = $this->GetBandwidth();
2072
		$cflink['bandwidthtype'] = $this->GetBwscale();
2073
		$cflink['enabled'] = trim($this->GetEnabled());
2074
		if (empty($cflink['enabled'])) {
2075
			unset($cflink['enabled']);
2076
		}
2077
		$cflink['default'] = trim($this->GetDefault());
2078
		if (empty($cflink['default'])) {
2079
			unset($cflink['default']);
2080
		}
2081
		$cflink['red'] = trim($this->GetRed());
2082
		if (empty($cflink['red'])) {
2083
			unset($cflink['red']);
2084
		}
2085
		$cflink['rio'] = trim($this->GetRio());
2086
		if (empty($cflink['rio'])) {
2087
			unset($cflink['rio']);
2088
		}
2089
		$cflink['ecn'] = trim($this->GetEcn());
2090
		if (empty($cflink['ecn'])) {
2091
			unset($cflink['ecn']);
2092
		}
2093
		if ($this->GetLinkshare() <> "") {
2094
			if ($this->GetL_m1() <> "") {
2095
				$cflink['linkshare1'] = $this->GetL_m1();
2096
				$cflink['linkshare2'] = $this->GetL_d();
2097
				$cflink['linkshare'] = "on";
2098
			} else {
2099
				unset($cflink['linkshare1']);
2100
				unset($cflink['linkshare2']);
2101
				unset($cflink['linkshare']);
2102
			}
2103
			if ($this->GetL_m2() <> "") {
2104
				$cflink['linkshare3'] = $this->GetL_m2();
2105
				$cflink['linkshare'] = "on";
2106
			} else {
2107
				unset($cflink['linkshare3']);
2108
				unset($cflink['linkshare']);
2109
			}
2110
		}
2111
		if ($this->GetRealtime() <> "") {
2112
			if ($this->GetR_m1() <> "") {
2113
				$cflink['realtime1'] = $this->GetR_m1();
2114
				$cflink['realtime2'] = $this->GetR_d();
2115
				$cflink['realtime'] = "on";
2116
			} else {
2117
				unset($cflink['realtime1']);
2118
				unset($cflink['realtime2']);
2119
				unset($cflink['realtime']);
2120
			}
2121
			if ($this->GetR_m2() <> "") {
2122
				$cflink['realtime3'] = $this->GetR_m2();
2123
				$cflink['realtime'] = "on";
2124
			} else {
2125
				unset($cflink['realtime3']);
2126
				unset($cflink['realtime']);
2127
			}
2128
		}
2129
		if ($this->GetUpperlimit() <> "") {
2130
			if ($this->GetU_m1() <> "") {
2131
				$cflink['upperlimit1'] = $this->GetU_m1();
2132
				$cflink['upperlimit2'] = $this->GetU_d();
2133
				$cflink['upperlimit'] = "on";
2134
			} else {
2135
				unset($cflink['upperlimit']);
2136
				unset($cflink['upperlimit1']);
2137
				unset($cflink['upperlimit2']);
2138
			}
2139
			if ($this->GetU_m2() <> "") {
2140
				$cflink['upperlimit3'] = $this->GetU_m2();
2141
				$cflink['upperlimit'] = "on";
2142
			} else {
2143
				unset($cflink['upperlimit3']);
2144
				unset($cflink['upperlimit']);
2145
			}
2146
		}
2147

    
2148
		if (is_array($this->subqueues)) {
2149
			$cflinkp['queue'] = array();
2150
			foreach ($this->subqueues as $q) {
2151
				$cflink['queue'][$q->GetQname()] = array();
2152
				$q->copy_queue($interface, $cflink['queue'][$q->GetQname()]);
2153
			}
2154
		}
2155
	}
2156

    
2157
	function delete_queue() {
2158
		unref_on_altq_queue_list($this->GetQname());
2159
		cleanup_queue_from_rules($this->GetQname());
2160
		$parent =& $this->GetParent();
2161
		foreach ($this->subqueues as $q)
2162
			$q->delete_queue();
2163
		unset_object_by_reference($this->GetLink());
2164
	}
2165

    
2166
	/*
2167
	 * Should search even its children
2168
	 */
2169
	function &find_queue($interface, $qname) {
2170
		if ($qname == $this->GetQname()) {
2171
			return $this;
2172
		}
2173

    
2174
		foreach ($this->subqueues as $q) {
2175
			$result =& $q->find_queue("", $qname);
2176
			if ($result) {
2177
				return $result;
2178
			}
2179
		}
2180
	}
2181

    
2182
	function &find_parentqueue($interface, $qname) {
2183
		if ($this->subqueues[$qname]) {
2184
			return $this;
2185
		}
2186
		foreach ($this->subqueues as $q) {
2187
			$result = $q->find_parentqueue("", $qname);
2188
			if ($result) {
2189
				return $result;
2190
			}
2191
		}
2192
	}
2193

    
2194
	function validate_input($data, &$input_errors) {
2195
		parent::validate_input($data, $input_errors);
2196

    
2197
		$reqdfields[] = "bandwidth";
2198
		$reqdfieldsn[] = gettext("Bandwidth");
2199
		$reqdfields[] = "bandwidthtype";
2200
		$reqdfieldsn[] = gettext("Bandwidthtype");
2201

    
2202
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
2203

    
2204
		if (isset($data['linkshare3']) && $data['linkshare3'] <> "") {
2205
			if ($data['bandwidth'] && (!is_numeric($data['bandwidth']))) {
2206
				$input_errors[] = gettext("Bandwidth must be an integer.");
2207
			}
2208

    
2209
			if ($data['bandwidth'] < 0) {
2210
				$input_errors[] = gettext("Bandwidth cannot be negative.");
2211
			}
2212

    
2213
			if ($data['bandwidthtype'] == "%") {
2214
				if ($data['bandwidth'] > 100 || $data['bandwidth'] < 0) {
2215
					$input_errors[] = gettext("Bandwidth in percentage should be between 1 and 100.");
2216
				}
2217
			}
2218
		}
2219

    
2220
		if ($data['upperlimit1'] <> "" && $data['upperlimit2'] == "") {
2221
			$input_errors[] = gettext("upperlimit service curve defined but missing (d) value");
2222
		}
2223
		if ($data['upperlimit2'] <> "" && $data['upperlimit1'] == "") {
2224
			$input_errors[] = gettext("upperlimit service curve defined but missing initial bandwidth (m1) value");
2225
		}
2226
		if ($data['upperlimit1'] <> "" && !is_valid_shaperbw($data['upperlimit1'])) {
2227
			$input_errors[] = gettext("upperlimit m1 value needs to be Kb, Mb, Gb, or %");
2228
		}
2229
		if ($data['upperlimit2'] <> "" && !is_numeric($data['upperlimit2'])) {
2230
			$input_errors[] = gettext("upperlimit d value needs to be numeric");
2231
		}
2232
		if ($data['upperlimit3'] <> "" && !is_valid_shaperbw($data['upperlimit3'])) {
2233
			$input_errors[] = gettext("upperlimit m2 value needs to be Kb, Mb, Gb, or %");
2234
		}
2235

    
2236
		/*
2237
		if (isset($data['upperlimit']) && $data['upperlimit3'] <> "" && $data['upperlimit1'] <> "") {
2238
			$bw_1 = get_hfsc_bandwidth($this, $data['upperlimit1']);
2239
			$bw_2 = get_hfsc_bandwidth($this, $data['upperlimit3']);
2240
			if (floatval($bw_1) < floatval($bw_2)) {
2241
				$input_errors[] = ("upperlimit m1 cannot be smaller than m2");
2242
			}
2243

    
2244
			if (get_interface_bandwidth($this) < (0.8 * (floatval($bw_1) + floatval($bw_2)))) {
2245
				$input_errors[] = ("upperlimit specification exceeds 80% of allowable allocation.");
2246
			}
2247
		}
2248
		*/
2249
		if ($data['linkshare1'] <> "" && $data['linkshare2'] == "") {
2250
			$input_errors[] = gettext("linkshare service curve defined but missing (d) value");
2251
		}
2252
		if ($data['linkshare2'] <> "" && $data['linkshare1'] == "") {
2253
			$input_errors[] = gettext("linkshare service curve defined but missing initial bandwidth (m1) value");
2254
		}
2255
		if ($data['linkshare1'] <> "" && !is_valid_shaperbw($data['linkshare1'])) {
2256
			$input_errors[] = gettext("linkshare m1 value needs to be Kb, Mb, Gb, or %");
2257
		}
2258
		if ($data['linkshare2'] <> "" && !is_numeric($data['linkshare2'])) {
2259
			$input_errors[] = gettext("linkshare d value needs to be numeric");
2260
		}
2261
		if ($data['linkshare3'] <> "" && !is_valid_shaperbw($data['linkshare3'])) {
2262
			$input_errors[] = gettext("linkshare m2 value needs to be Kb, Mb, Gb, or %");
2263
		}
2264
		if ($data['realtime1'] <> "" && $data['realtime2'] == "") {
2265
			$input_errors[] = gettext("realtime service curve defined but missing (d) value");
2266
		}
2267
		if ($data['realtime2'] <> "" && $data['realtime1'] == "") {
2268
			$input_errors[] = gettext("realtime service curve defined but missing initial bandwidth (m1) value");
2269
		}
2270

    
2271
		/*
2272
		if (isset($data['linkshare']) && $data['linkshare3'] <> "" && $data['linkshare1'] <> "" && 0) {
2273
			$bw_1 = get_hfsc_bandwidth($this, $data['linkshare1']);
2274
			$bw_2 = get_hfsc_bandwidth($this, $data['linkshare3']);
2275
			if (floatval($bw_1) < floatval($bw_2)) {
2276
				$input_errors[] = ("linkshare m1 cannot be smaller than m2");
2277
			}
2278

    
2279
			if (get_interface_bandwidth($this) < (0.8 * (floatval($bw_1) + floatval($bw_2)))) {
2280
				$input_errors[] = ("linkshare specification exceeds 80% of allowable allocation.");
2281
			}
2282
		}
2283
		*/
2284

    
2285
		if ($data['realtime1'] <> "" && !is_valid_shaperbw($data['realtime1'])) {
2286
			$input_errors[] = gettext("realtime m1 value needs to be Kb, Mb, Gb, or %");
2287
		}
2288
		if ($data['realtime2'] <> "" && !is_numeric($data['realtime2'])) {
2289
			$input_errors[] = gettext("realtime d value needs to be numeric");
2290
		}
2291
		if ($data['realtime3'] <> "" && !is_valid_shaperbw($data['realtime3'])) {
2292
			$input_errors[] = gettext("realtime m2 value needs to be Kb, Mb, Gb, or %");
2293
		}
2294

    
2295
		/*
2296
		if (isset($data['realtime']) && $data['realtime3'] <> "" && $data['realtime1'] <> "" && 0) {
2297
			$bw_1 = get_hfsc_bandwidth($this, $data['realtime1']);
2298
			$bw_2 = get_hfsc_bandwidth($this, $data['realtime3']);
2299
			if (floatval($bw_1) < floatval($bw_2)) {
2300
				$input_errors[] = ("realtime m1 cannot be smaller than m2");
2301
			}
2302

    
2303
			if (get_interface_bandwidth($this) < (0.8 * (floatval($bw_1) + floatval($bw_2)))) {
2304
				$input_errors[] = ("realtime specification exceeds 80% of allowable allocation.");
2305
			}
2306
		}
2307
		*/
2308
	}
2309

    
2310
	function ReadConfig(&$cflink) {
2311
		if (!empty($cflink['linkshare'])) {
2312
			if (!empty($cflink['linkshare1'])) {
2313
				$this->SetL_m1($cflink['linkshare1']);
2314
				$this->SetL_d($cflink['linkshare2']);
2315
				$this->SetLinkshare();
2316
			} else {
2317
				$this->SetL_m1("");
2318
				$this->SetL_d("");
2319
				$this->DisableLinkshare();
2320
			}
2321
			if (!empty($cflink['linkshare3'])) {
2322
				$this->SetL_m2($cflink['linkshare3']);
2323
				$this->SetLinkshare();
2324
			}
2325
		} else {
2326
			$this->DisableLinkshare();
2327
		}
2328
		if (!empty($cflink['realtime'])) {
2329
			if (!empty($cflink['realtime1'])) {
2330
				$this->SetR_m1($cflink['realtime1']);
2331
				$this->SetR_d($cflink['realtime2']);
2332
				$this->SetRealtime();
2333
			} else {
2334
				$this->SetR_m1("");
2335
				$this->SetR_d("");
2336
				$this->DisableRealtime();
2337
			}
2338
			if (!empty($cflink['realtime3'])) {
2339
				$this->SetR_m2($cflink['realtime3']);
2340
				$this->SetRealtime();
2341
			}
2342
		} else {
2343
			$this->DisableRealtime();
2344
		}
2345
		if (!empty($cflink['upperlimit'])) {
2346
			if (!empty($cflink['upperlimit1'])) {
2347
				$this->SetU_m1($cflink['upperlimit1']);
2348
				$this->SetU_d($cflink['upperlimit2']);
2349
				$this->SetUpperlimit();
2350
			} else {
2351
				$this->SetU_m1("");
2352
				$this->SetU_d("");
2353
				$this->DisableUpperlimit();
2354
			}
2355
			if (!empty($cflink['upperlimit3'])) {
2356
				$this->SetU_m2($cflink['upperlimit3']);
2357
				$this->SetUpperlimit();
2358
			}
2359
		} else {
2360
			$this->DisableUpperlimit();
2361
		}
2362
		parent::ReadConfig($cflink);
2363
	}
2364

    
2365
	function build_tree() {
2366
		$tree = " <li><a href=\"firewall_shaper.php?interface=" . $this->GetInterface() ."&amp;queue=" . $this->GetQname()."&amp;action=show";
2367
		$tree .= "\" ";
2368
		$tmpvalue = $this->GetDefault();
2369
		if (!empty($tmpvalue)) {
2370
			$tree .= " class=\"navlnk\"";
2371
		}
2372
		$tree .= " >" . $this->GetQname() . "</a>";
2373
		if (is_array($this->subqueues)) {
2374
			$tree .= "<ul>";
2375
			foreach ($this->subqueues as $q) {
2376
				$tree .= $q->build_tree();
2377
			}
2378
			$tree .= "</ul>";
2379
		}
2380
		$tree .= "</li>";
2381
		return $tree;
2382
	}
2383

    
2384
	/* Even this should take children into consideration */
2385
	function build_rules(&$default = false) {
2386

    
2387
		$pfq_rule = " queue ". $this->qname;
2388
		if ($this->GetInterface()) {
2389
			$pfq_rule .= " on ".get_real_interface($this->GetInterface());
2390
		}
2391
		if ($this->GetBandwidth() && $this->GetBwscale()) {
2392
			$pfq_rule .= " bandwidth ".trim($this->GetBandwidth()).$this->GetBwscale();
2393
		}
2394

    
2395
		$tmpvalue = $this->GetQlimit();
2396
		if (!empty($tmpvalue)) {
2397
			$pfq_rule .= " qlimit " . $this->GetQlimit();
2398
		}
2399
		if ($this->GetDefault() || $this->GetRed() || $this->GetRio() || $this->GetEcn() || $this->GetCodel() || $this->GetRealtime() <> "" || $this->GetLinkshare() <> "" || $this->GetUpperlimit() <> "") {
2400
			$pfq_rule .= " hfsc ( ";
2401
			$tmpvalue = $this->GetRed();
2402
			if (!empty($tmpvalue)) {
2403
				$comma = 1;
2404
				$pfq_rule .= " red ";
2405
			}
2406

    
2407
			$tmpvalue = $this->GetRio();
2408
			if (!empty($tmpvalue)) {
2409
				if ($comma) {
2410
					$pfq_rule .= " ,";
2411
				}
2412
				$comma = 1;
2413
				$pfq_rule .= " rio ";
2414
			}
2415
			$tmpvalue = $this->GetEcn();
2416
			if (!empty($tmpvalue)) {
2417
				if ($comma) {
2418
					$pfq_rule .= " ,";
2419
				}
2420
				$comma = 1;
2421
				$pfq_rule .= " ecn ";
2422
			}
2423
			$tmpvalue = $this->GetCodel();
2424
			if (!empty($tmpvalue)) {
2425
				if ($comma) {
2426
					$pfq_rule .= " ,";
2427
				}
2428
				$comma = 1;
2429
				$pfq_rule .= " codel ";
2430
			}
2431
			$tmpvalue = $this->GetDefault();
2432
			if (!empty($tmpvalue)) {
2433
				if ($comma) {
2434
					$pfq_rule .= " ,";
2435
				}
2436
				$comma = 1;
2437
				$pfq_rule .= " default ";
2438
				$default = true;
2439
			}
2440

    
2441
			if ($this->GetRealtime() <> "") {
2442
				if ($comma) {
2443
					$pfq_rule .= " , ";
2444
				}
2445
				if ($this->GetR_m1() <> "" && $this->GetR_d() <> "" && $this->GetR_m2() <> "") {
2446
					$pfq_rule .= " realtime (".$this->GetR_m1() . ", " . $this->GetR_d().", ". $this->GetR_m2() .") ";
2447
				} else if ($this->GetR_m2() <> "") {
2448
					$pfq_rule .= " realtime " . $this->GetR_m2();
2449
				}
2450
				$comma = 1;
2451
			}
2452
			if ($this->GetLinkshare() <> "") {
2453
				if ($comma) {
2454
					$pfq_rule .= " ,";
2455
				}
2456
				if ($this->GetL_m1() <> "" && $this->GetL_d() <> "" && $this->GetL_m2() <> "") {
2457
					$pfq_rule .= " linkshare (".$this->GetL_m1(). ", ". $this->GetL_d(). ", ". $this->GetL_m2(). ") ";
2458
				} else if ($this->GetL_m2() <> "") {
2459
					$pfq_rule .= " linkshare " . $this->GetL_m2() . " ";
2460
				}
2461
				$comma = 1;
2462
			}
2463
			if ($this->GetUpperlimit() <> "") {
2464
				if ($comma) {
2465
					$pfq_rule .= " ,";
2466
				}
2467
				if ($this->GetU_m1() <> "" && $this->GetU_d() <> "" && $this->GetU_m2() <> "") {
2468
							$pfq_rule .= " upperlimit (".$this->GetU_m1().", ". $this->GetU_d().", ". $this->GetU_m2(). ") ";
2469
				} else if ($this->GetU_m2() <> "") {
2470
					$pfq_rule .= " upperlimit " . $this->GetU_m2() . " ";
2471
				}
2472
			}
2473
			$pfq_rule .= " ) ";
2474
		}
2475
		if (count($this->subqueues)) {
2476
			$i = count($this->subqueues);
2477
			$pfq_rule .= " { ";
2478
			foreach ($this->subqueues as $qkey => $qnone) {
2479
				if ($i > 1) {
2480
					$i--;
2481
					$pfq_rule .= " {$qkey}, ";
2482
				} else {
2483
					$pfq_rule .= " {$qkey} ";
2484
				}
2485
			}
2486
			$pfq_rule .= " } \n";
2487
			foreach ($this->subqueues as $q) {
2488
				$pfq_rule .= $q->build_rules($default);
2489
			}
2490
		}
2491

    
2492
		$pfq_rule .= " \n";
2493

    
2494
		return $pfq_rule;
2495
	}
2496

    
2497
	function build_javascript() {
2498

    
2499
		$javascript = <<<EOJS
2500
<script type="text/javascript">
2501
//<![CDATA[
2502
	events.push(function(){
2503

    
2504
		// Disables the specified input element
2505
		function disableInput(id, disable) {
2506
			$('#' + id).prop("disabled", disable);
2507
		}
2508

    
2509
		// Upperlimit
2510
		function enable_upperlimit() {
2511
			disableInput('upperlimit1', !$('#upperlimit').prop('checked'));
2512
			disableInput('upperlimit2', !$('#upperlimit').prop('checked'));
2513
			disableInput('upperlimit3', !$('#upperlimit').prop('checked'));
2514
		}
2515

    
2516
		$('#upperlimit').click(function () {
2517
			enable_upperlimit();
2518
		});
2519

    
2520
		enable_upperlimit();
2521

    
2522
		// realtime
2523
		function enable_realtime() {
2524
			disableInput('realtime1', !$('#realtime').prop('checked'));
2525
			disableInput('realtime2', !$('#realtime').prop('checked'));
2526
			disableInput('realtime3', !$('#realtime').prop('checked'));
2527
		}
2528

    
2529
		$('#realtime').click(function () {
2530
			enable_realtime();
2531
		});
2532

    
2533
		enable_realtime();
2534

    
2535
		// linkshare
2536
		function enable_linkshare() {
2537
			disableInput('linkshare1', !$('#linkshare').prop('checked'));
2538
			disableInput('linkshare2', !$('#linkshare').prop('checked'));
2539
			disableInput('linkshare3', !$('#linkshare').prop('checked'));
2540
		}
2541

    
2542
		$('#linkshare').click(function () {
2543
			enable_linkshare();
2544
		});
2545

    
2546
		enable_linkshare();
2547
	});
2548
//]]>
2549
</script>
2550
EOJS;
2551

    
2552
		return $javascript;
2553
	}
2554

    
2555
	function build_form() {
2556

    
2557
		$sform = parent::build_form();
2558

    
2559
		$section = new Form_Section('Service Curve (sc)');
2560

    
2561
		$group = new Form_Group('Bandwidth');
2562

    
2563
		$group->add(new Form_Input(
2564
			'bandwidth',
2565
			null,
2566
			'number',
2567
			$this->GetBandwidth(),
2568
			['step' => 'any', 'min' => '0.000']
2569
		));
2570

    
2571
		$group->add(new Form_Select(
2572
			'bandwidthtype',
2573
			null,
2574
			$this->GetBwscale(),
2575
			array('Kb' => 'Kbit/s',
2576
				  'Mb' => 'Mbit/s',
2577
				  'Gb' => 'Gbit/s',
2578
				  'b' => 'Bit/s',
2579
				  '%' => '%')
2580
		));
2581

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

    
2584
		$section->add($group);
2585

    
2586
		$group = new Form_Group('Max bandwidth for queue.');
2587

    
2588
		$group->add(new Form_Checkbox(
2589
			'upperlimit',
2590
			null,
2591
			'Upper Limit',
2592
			($this->GetUpperlimit()<> "")
2593
		));
2594

    
2595
		$group->add(new Form_Input(
2596
			'upperlimit1',
2597
			null,
2598
			'text',
2599
			$this->GetU_m1()
2600
		))->setHelp('m1');
2601

    
2602
		$group->add(new Form_Input(
2603
			'upperlimit2',
2604
			null,
2605
			'text',
2606
			$this->GetU_d()
2607
		))->setHelp('d');
2608

    
2609
		$group->add(new Form_Input(
2610
			'upperlimit3',
2611
			null,
2612
			'text',
2613
			$this->GetU_m2()
2614
		))->setHelp('m2');
2615

    
2616

    
2617
		$section->add($group);
2618

    
2619
		$group = new Form_Group('Min bandwidth for queue.');
2620

    
2621
		$group->add(new Form_Checkbox(
2622
			'realtime',
2623
			null,
2624
			'Real Time',
2625
			($this->GetRealtime()<> "")
2626
		));
2627

    
2628
		$group->add(new Form_Input(
2629
			'realtime1',
2630
			null,
2631
			'text',
2632
			$this->GetR_m1()
2633
		))->setHelp('m1');
2634

    
2635
		$group->add(new Form_Input(
2636
			'realtime2',
2637
			null,
2638
			'text',
2639
			$this->GetR_d()
2640
		))->setHelp('d');
2641

    
2642
		$group->add(new Form_Input(
2643
			'realtime3',
2644
			null,
2645
			'text',
2646
			$this->GetR_m2()
2647
		))->setHelp('m2');
2648

    
2649
		$section->add($group);
2650

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

    
2653
		$group->add(new Form_Checkbox(
2654
			'linkshare',
2655
			null,
2656
			'Link Share',
2657
			($this->GetLinkshare()<> "")
2658
		));
2659

    
2660
		$group->add(new Form_Input(
2661
			'linkshare1',
2662
			null,
2663
			'text',
2664
			$this->GetL_m1()
2665
		))->setHelp('m1');
2666

    
2667
		$group->add(new Form_Input(
2668
			'linkshare2',
2669
			null,
2670
			'text',
2671
			$this->GetL_d()
2672
		))->setHelp('d');
2673

    
2674
		$group->add(new Form_Input(
2675
			'linkshare3',
2676
			null,
2677
			'text',
2678
			$this->GetL_m2()
2679
		))->setHelp('m2');
2680

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

    
2687
		$section->add($group);
2688

    
2689
		$sform->add($section);
2690

    
2691
		return($sform);
2692
	}
2693

    
2694
	function update_altq_queue_data(&$data) {
2695
		$this->ReadConfig($data);
2696
	}
2697

    
2698
	function wconfig() {
2699
		$cflink =& get_reference_to_me_in_config($this->GetLink());
2700
		if (!is_array($cflink)) {
2701
			$cflink = array();
2702
		}
2703
		$cflink['name'] = $this->GetQname();
2704
		$cflink['interface'] = $this->GetInterface();
2705
		$cflink['qlimit'] = trim($this->GetQlimit());
2706
		if (empty($cflink['qlimit'])) {
2707
			unset($cflink['qlimit']);
2708
		}
2709
		$cflink['priority'] = $this->GetQpriority();
2710
		if (!is_numericint($cflink['priority'])) {
2711
			unset($cflink['priority']);
2712
		}
2713
		$cflink['description'] = $this->GetDescription();
2714
		if (empty($cflink['description'])) {
2715
			unset($cflink['description']);
2716
		}
2717
		$cflink['bandwidth'] = $this->GetBandwidth();
2718
		$cflink['bandwidthtype'] = $this->GetBwscale();
2719
		$cflink['enabled'] = $this->GetEnabled();
2720
		if (empty($cflink['enabled'])) {
2721
			unset($cflink['enabled']);
2722
		}
2723
		$cflink['default'] = $this->GetDefault();
2724
		if (empty($cflink['default'])) {
2725
			unset($cflink['default']);
2726
		}
2727
		$cflink['red'] = trim($this->GetRed());
2728
		if (empty($cflink['red'])) {
2729
			unset($cflink['red']);
2730
		}
2731
		$cflink['rio'] = $this->GetRio();
2732
		if (empty($cflink['rio'])) {
2733
			unset($cflink['rio']);
2734
		}
2735
		$cflink['ecn'] = trim($this->GetEcn());
2736
		if (empty($cflink['ecn'])) {
2737
			unset($cflink['ecn']);
2738
		}
2739
		$cflink['codel'] = trim($this->GetCodel());
2740
		if (empty($cflink['codel'])) {
2741
			unset($cflink['codel']);
2742
		}
2743
		if ($this->GetLinkshare() <> "") {
2744
			if ($this->GetL_m1() <> "") {
2745
				$cflink['linkshare1'] = $this->GetL_m1();
2746
				$cflink['linkshare2'] = $this->GetL_d();
2747
				$cflink['linkshare'] = "on";
2748
			} else {
2749
				unset($cflink['linkshare']);
2750
				unset($cflink['linkshare1']);
2751
				unset($cflink['linkshare2']);
2752
			}
2753
			if ($this->GetL_m2() <> "") {
2754
				$cflink['linkshare3'] = $this->GetL_m2();
2755
				$cflink['linkshare'] = "on";
2756
			} else {
2757
				unset($cflink['linkshare']);
2758
				unset($cflink['linkshare3']);
2759
			}
2760
		} else {
2761
			unset($cflink['linkshare']);
2762
			unset($cflink['linkshare1']);
2763
			unset($cflink['linkshare2']);
2764
			unset($cflink['linkshare3']);
2765
		}
2766
		if ($this->GetRealtime() <> "") {
2767
			if ($this->GetR_m1() <> "") {
2768
				$cflink['realtime1'] = $this->GetR_m1();
2769
				$cflink['realtime2'] = $this->GetR_d();
2770
				$cflink['realtime'] = "on";
2771
			} else {
2772
				unset($cflink['realtime']);
2773
				unset($cflink['realtime1']);
2774
				unset($cflink['realtime2']);
2775
			}
2776
			if ($this->GetR_m2() <> "") {
2777
				$cflink['realtime3'] = $this->GetR_m2();
2778
				$cflink['realtime'] = "on";
2779
			} else {
2780
				unset($cflink['realtime']);
2781
				unset($cflink['realtime3']);
2782
			}
2783
		} else {
2784
			unset($cflink['realtime']);
2785
			unset($cflink['realtime1']);
2786
			unset($cflink['realtime2']);
2787
			unset($cflink['realtime3']);
2788
		}
2789
		if ($this->GetUpperlimit() <> "") {
2790
			if ($this->GetU_m1() <> "") {
2791
				$cflink['upperlimit1'] = $this->GetU_m1();
2792
				$cflink['upperlimit2'] = $this->GetU_d();
2793
				$cflink['upperlimit'] = "on";
2794
			} else {
2795
				unset($cflink['upperlimit']);
2796
				unset($cflink['upperlimit1']);
2797
				unset($cflink['upperlimit2']);
2798
			}
2799
			if ($this->GetU_m2() <> "") {
2800
				$cflink['upperlimit3'] = $this->GetU_m2();
2801
				$cflink['upperlimit'] = "on";
2802
			} else {
2803
				unset($cflink['upperlimit']);
2804
				unset($cflink['upperlimit3']);
2805
			}
2806
		} else {
2807
			unset($cflink['upperlimit']);
2808
			unset($cflink['upperlimit1']);
2809
			unset($cflink['upperlimit2']);
2810
			unset($cflink['upperlimit3']);
2811
		}
2812
	}
2813
}
2814

    
2815
class cbq_queue extends priq_queue {
2816
	var $qborrow = "";
2817

    
2818
	function GetBorrow() {
2819
		return $this->qborrow;
2820
	}
2821
	function SetBorrow($borrow) {
2822
		$this->qborrow = $borrow;
2823
	}
2824
	function CanHaveChildren() {
2825
		return true;
2826
	}
2827

    
2828
	function &add_queue($interface, &$qname, &$path, &$input_errors) {
2829

    
2830
		if (!is_array($this->subqueues)) {
2831
			$this->subqueues = array();
2832
		}
2833
		$__tmp_q = new cbq_queue(); $q =& $__tmp_q;
2834
		$q->SetInterface($this->GetInterface());
2835
		$q->SetParent($this);
2836
		$q->ReadConfig($qname);
2837
		$q->validate_input($qname, $input_errors);
2838

    
2839
		$q->SetEnabled("on");
2840
		$q->SetLink($path);
2841
		$this->subqueues[$q->GetQName()] = &$q;
2842
		ref_on_altq_queue_list($this->GetQname(), $q->GetQname());
2843
		if (is_array($qname['queue'])) {
2844
			foreach ($qname['queue'] as $key1 => $que) {
2845
				array_push($path, $key1);
2846
				$q->add_queue($q->GetInterface(), $que, $path, $input_errors);
2847
				array_pop($path);
2848
			}
2849
		}
2850

    
2851
		return $q;
2852
	}
2853

    
2854
	function copy_queue($interface, &$cflink) {
2855

    
2856
		$cflink['interface'] = $interface;
2857
		$cflink['qlimit'] = trim($this->GetQlimit());
2858
		if (empty($clink['qlimit'])) {
2859
			unset($cflink['qlimit']);
2860
		}
2861
		$cflink['priority'] = trim($this->GetQpriority());
2862
		if (!is_numeric($cflink['priority'])) {
2863
			unset($cflink['priority']);
2864
		}
2865
		$cflink['name'] = $this->GetQname();
2866
		$cflink['description'] = trim($this->GetDescription());
2867
		if (empty($cflink['description'])) {
2868
			unset($cflink['description']);
2869
		}
2870
		$cflink['bandwidth'] = $this->GetBandwidth();
2871
		$cflink['bandwidthtype'] = $this->GetBwscale();
2872
		$cflink['enabled'] = trim($this->GetEnabled());
2873
		if (empty($cflink['enabled'])) {
2874
			unset($cflink['enabled']);
2875
		}
2876
		$cflink['default'] = trim($this->GetDefault());
2877
		if (empty($cflink['default'])) {
2878
			unset($cflink['default']);
2879
		}
2880
		$cflink['red'] = trim($this->GetRed());
2881
		if (empty($cflink['red'])) {
2882
			unset($cflink['red']);
2883
		}
2884
		$cflink['rio'] = trim($this->GetRio());
2885
		if (empty($cflink['rio'])) {
2886
			unset($cflink['rio']);
2887
		}
2888
		$cflink['ecn'] = trim($this->GetEcn());
2889
		if (empty($cflink['ecn'])) {
2890
			unset($cflink['ecn']);
2891
		}
2892
		$cflink['borrow'] = trim($this->GetBorrow());
2893
		if (empty($cflink['borrow'])) {
2894
			unset($cflink['borrow']);
2895
		}
2896
		if (is_array($this->queues)) {
2897
			$cflinkp['queue'] = array();
2898
			foreach ($this->subqueues as $q) {
2899
				$cflink['queue'][$q->GetQname()] = array();
2900
				$q->copy_queue($interface, $cflink['queue'][$q->GetQname()]);
2901
			}
2902
		}
2903
	}
2904

    
2905
	/*
2906
	 * Should search even its children
2907
	 */
2908
	function &find_queue($interface, $qname) {
2909
		if ($qname == $this->GetQname()) {
2910
			return $this;
2911
		}
2912
		foreach ($this->subqueues as $q) {
2913
			$result =& $q->find_queue("", $qname);
2914
			if ($result) {
2915
				return $result;
2916
			}
2917
		}
2918
	}
2919

    
2920
	function &find_parentqueue($interface, $qname) {
2921
		if ($this->subqueues[$qname]) {
2922
			return $this;
2923
		}
2924
		foreach ($this->subqueues as $q) {
2925
			$result = $q->find_parentqueue("", $qname);
2926
			if ($result) {
2927
				return $result;
2928
			}
2929
		}
2930
	}
2931

    
2932
	function delete_queue() {
2933
		unref_on_altq_queue_list($this->GetQname());
2934
		cleanup_queue_from_rules($this->GetQname());
2935
		foreach ($this->subqueues as $q)
2936
			$q->delete_queue();
2937
		unset_object_by_reference($this->GetLink());
2938
	}
2939

    
2940
	function validate_input($data, &$input_errors) {
2941
		parent::validate_input($data, $input_errors);
2942

    
2943
		if ($data['priority'] > 7) {
2944
				$input_errors[] = gettext("Priority must be an integer between 0 and 7.");
2945
		}
2946
		$reqdfields[] = "bandwidth";
2947
		$reqdfieldsn[] = gettext("Bandwidth");
2948
		$reqdfields[] = "bandwidthtype";
2949
		$reqdfieldsn[] = gettext("Bandwidthtype");
2950

    
2951
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
2952
	}
2953

    
2954
	function ReadConfig(&$q) {
2955
		parent::ReadConfig($q);
2956
		if (!empty($q['borrow'])) {
2957
			$this->SetBorrow("on");
2958
		} else {
2959
			$this->SetBorrow("");
2960
		}
2961
	}
2962

    
2963
	function build_javascript() {
2964
		return parent::build_javascript();
2965
	}
2966

    
2967
	function build_tree() {
2968
		$tree = " <li><a href=\"firewall_shaper.php?interface=" . $this->GetInterface()."&amp;queue=" . $this->GetQname()."&amp;action=show";
2969
		$tree .= "\" ";
2970
		$tmpvalue = trim($this->GetDefault());
2971
		if (!empty($tmpvalue)) {
2972
			$tree .= " class=\"navlnk\"";
2973
		}
2974
		$tree .= " >" . $this->GetQname() . "</a>";
2975
		if (is_array($this->subqueues)) {
2976
			$tree .= "<ul>";
2977
			foreach ($this->subqueues as $q) {
2978
				$tree .= $q->build_tree();
2979
			}
2980
			$tree .= "</ul>";
2981
		}
2982
		$tree .= "</li>";
2983
		return $tree;
2984
	}
2985

    
2986
	/* Even this should take children into consideration */
2987
	function build_rules(&$default = false) {
2988
		$pfq_rule = "queue ". $this->qname;
2989
		if ($this->GetInterface()) {
2990
			$pfq_rule .= " on ".get_real_interface($this->GetInterface());
2991
		}
2992
		if ($this->GetBandwidth() && $this->GetBwscale()) {
2993
			$pfq_rule .= " bandwidth ".trim($this->GetBandwidth()).$this->GetBwscale();
2994
		}
2995
		$tmpvalue = $this->GetQpriority();
2996
		if (is_numeric($tmpvalue)) {
2997
			$pfq_rule .= " priority " . $this->GetQpriority();
2998
		}
2999
		$tmpvalue = trim($this->GetQlimit());
3000
		if (!empty($tmpvalue)) {
3001
			$pfq_rule .= " qlimit " . $this->GetQlimit();
3002
		}
3003
		if ($this->GetDefault() || $this->GetRed() || $this->GetRio() || $this->GetEcn() || $this->GetBorrow() || $this->GetCodel()) {
3004
			$pfq_rule .= " cbq ( ";
3005
			$tmpvalue = trim($this->GetRed());
3006
			if (!empty($tmpvalue)) {
3007
				$comma = 1;
3008
				$pfq_rule .= " red ";
3009
			}
3010
			$tmpvalue = trim($this->GetCodel());
3011
			if (!empty($tmpvalue)) {
3012
				$comma = 1;
3013
				$pfq_rule .= " codel ";
3014
			}
3015
			$tmpvalue = trim($this->GetRio());
3016
			if (!empty($tmpvalue)) {
3017
				if ($comma) {
3018
					$pfq_rule .= " ,";
3019
				}
3020
				$comma = 1;
3021
				$pfq_rule .= " rio ";
3022
			}
3023
			$tmpvalue = trim($this->GetEcn());
3024
			if (!empty($tmpvalue)) {
3025
				if ($comma) {
3026
					$pfq_rule .= " ,";
3027
				}
3028
				$comma = 1;
3029
				$pfq_rule .= " ecn ";
3030
			}
3031
			$tmpvalue = trim($this->GetDefault());
3032
			if (!empty($tmpvalue)) {
3033
				if ($comma) {
3034
					$pfq_rule .= " ,";
3035
				}
3036
				$comma = 1;
3037
				$pfq_rule .= " default ";
3038
				$default = true;
3039
			}
3040
			$tmpvalue = trim($this->GetBorrow());
3041
			if (!empty($tmpvalue)) {
3042
				if ($comma) {
3043
					$pfq_rule .= ", ";
3044
				}
3045
				$pfq_rule .= " borrow ";
3046
			}
3047
			$pfq_rule .= " ) ";
3048
		}
3049
		if (count($this->subqueues)) {
3050
			$i = count($this->subqueues);
3051
			$pfq_rule .= " { ";
3052
			foreach ($this->subqueues as $qkey => $qnone) {
3053
				if ($i > 1) {
3054
					$i--;
3055
					$pfq_rule .= " {$qkey}, ";
3056
				} else {
3057
					$pfq_rule .= " {$qkey} ";
3058
				}
3059
			}
3060
			$pfq_rule .= " } \n";
3061
			foreach ($this->subqueues as $q) {
3062
				$pfq_rule .= $q->build_rules($default);
3063
			}
3064
		}
3065

    
3066
		$pfq_rule .= " \n";
3067
		return $pfq_rule;
3068
	}
3069

    
3070
	function build_form() {
3071
		$sform = parent::build_form();
3072

    
3073
		$section = new Form_Section('NOTITLE');
3074

    
3075
		$group = new Form_Group('Bandwidth');
3076

    
3077
		$group->add(new Form_Input(
3078
			'bandwidth',
3079
			null,
3080
			'number',
3081
			$this->GetBandwidth()
3082
		));
3083

    
3084
		$group->add(new Form_Select(
3085
			'bandwidthtype',
3086
			null,
3087
			$this->GetBwscale(),
3088
			array('Kb' => 'Kbit/s',
3089
				  'Mb' => 'Mbit/s',
3090
				  'Gb' => 'Gbit/s',
3091
				  'b' => 'Bit/s',
3092
				  '%' => '%')
3093
		));
3094

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

    
3097
		$section->add($group);
3098

    
3099
		$section->addInput(new Form_Checkbox(
3100
			'borrow',
3101
			'Scheduler option',
3102
			'Borrow from other queues when available',
3103
			($this->GetBorrow() == "on")
3104
		));
3105

    
3106
		$sform->add($section);
3107

    
3108
		return $sform;
3109
	}
3110

    
3111
	function update_altq_queue_data(&$data) {
3112
		$this->ReadConfig($data);
3113
	}
3114

    
3115
	function wconfig() {
3116
		$cflink =& get_reference_to_me_in_config($this->GetLink());
3117
		if (!is_array($cflink)) {
3118
			$cflink = array();
3119
		}
3120
		$cflink['interface'] = $this->GetInterface();
3121
		$cflink['qlimit'] = trim($this->GetQlimit());
3122
		if (empty($cflink['qlimit'])) {
3123
			unset($cflink['qlimit']);
3124
		}
3125
		$cflink['priority'] = $this->GetQpriority();
3126
		if (!is_numeric($cflink['priority'])) {
3127
			unset($cflink['priority']);
3128
		}
3129
		$cflink['name'] = $this->GetQname();
3130
		$cflink['description'] = $this->GetDescription();
3131
		if (empty($cflink['description'])) {
3132
			unset($cflink['description']);
3133
		}
3134
		$cflink['bandwidth'] = $this->GetBandwidth();
3135
		$cflink['bandwidthtype'] = $this->GetBwscale();
3136
		$cflink['enabled'] = trim($this->GetEnabled());
3137
		if (empty($cflink['enabled'])) {
3138
			unset($cflink['enabled']);
3139
		}
3140
		$cflink['default'] = trim($this->GetDefault());
3141
		if (empty($cflink['default'])) {
3142
			unset($cflink['default']);
3143
		}
3144
		$cflink['red'] = trim($this->GetRed());
3145
		if (empty($cflink['red'])) {
3146
			unset($cflink['red']);
3147
		}
3148
		$cflink['rio'] = trim($this->GetRio());
3149
		if (empty($cflink['rio'])) {
3150
			unset($cflink['rio']);
3151
		}
3152
		$cflink['ecn'] = trim($this->GetEcn());
3153
		if (empty($cflink['ecn'])) {
3154
			unset($cflink['ecn']);
3155
		}
3156
		$cflink['codel'] = trim($this->GetCodel());
3157
		if (empty($cflink['codel'])) {
3158
			unset($cflink['codel']);
3159
		}
3160
		$cflink['borrow'] = trim($this->GetBorrow());
3161
		if (empty($cflink['borrow'])) {
3162
			unset($cflink['borrow']);
3163
		}
3164
	}
3165
}
3166

    
3167
class fairq_queue extends priq_queue {
3168
	var $hogs;
3169
	var $buckets;
3170

    
3171
	function GetBuckets() {
3172
		return $this->buckets;
3173
	}
3174
	function SetBuckets($buckets) {
3175
		$this->buckets = $buckets;
3176
	}
3177
	function GetHogs() {
3178
		return $this->hogs;
3179
	}
3180
	function SetHogs($hogs) {
3181
		$this->hogs = $hogs;
3182
	}
3183
	function CanHaveChildren() {
3184
		return false;
3185
	}
3186

    
3187

    
3188
	function copy_queue($interface, &$cflink) {
3189
		$cflink['interface'] = $interface;
3190
		$cflink['qlimit'] = $this->GetQlimit();
3191
		$cflink['priority'] = $this->GetQpriority();
3192
		$cflink['name'] = $this->GetQname();
3193
		$cflink['description'] = $this->GetDescription();
3194
		$cflink['bandwidth'] = $this->GetBandwidth();
3195
		$cflink['bandwidthtype'] = $this->GetBwscale();
3196
		$cflink['enabled'] = $this->GetEnabled();
3197
		$cflink['default'] = $this->GetDefault();
3198
		$cflink['red'] = $this->GetRed();
3199
		$cflink['rio'] = $this->GetRio();
3200
		$cflink['ecn'] = $this->GetEcn();
3201
		$cflink['buckets'] = $this->GetBuckets();
3202
		$cflink['hogs'] = $this->GetHogs();
3203
	}
3204

    
3205
	/*
3206
	 * Should search even its children
3207
	 */
3208
	function &find_queue($interface, $qname) {
3209
		if ($qname == $this->GetQname()) {
3210
			return $this;
3211
		}
3212
	}
3213

    
3214
	function find_parentqueue($interface, $qname) { return; }
3215

    
3216
	function delete_queue() {
3217
		unref_on_altq_queue_list($this->GetQname());
3218
		cleanup_queue_from_rules($this->GetQname());
3219
		unset_object_by_reference($this->GetLink());
3220
	}
3221

    
3222
	function validate_input($data, &$input_errors) {
3223
		parent::validate_input($data, $input_errors);
3224

    
3225
		if ($data['priority'] > 7) {
3226
				$input_errors[] = gettext("Priority must be an integer between 0 and 7.");
3227
		}
3228
		$reqdfields[] = "bandwidth";
3229
		$reqdfieldsn[] = gettext("Bandwidth");
3230
		$reqdfields[] = "bandwidthtype";
3231
		$reqdfieldsn[] = gettext("Bandwidthtype");
3232

    
3233
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
3234
	}
3235

    
3236
	function ReadConfig(&$q) {
3237
		parent::ReadConfig($q);
3238
		if (!empty($q['buckets'])) {
3239
			$this->SetBuckets($q['buckets']);
3240
		} else {
3241
			$this->SetBuckets("");
3242
		}
3243
		if (!empty($q['hogs']) && is_valid_shaperbw($q['hogs'])) {
3244
			$this->SetHogs($q['hogs']);
3245
		} else {
3246
			$this->SetHogs("");
3247
		}
3248
	}
3249

    
3250
	function build_javascript() {
3251
		return parent::build_javascript();
3252
	}
3253

    
3254
	function build_tree() {
3255
		$tree = " <li><a href=\"firewall_shaper.php?interface=" .
3256
		$this->GetInterface()."&amp;queue=" . $this->GetQname()."&amp;action=show";
3257
		$tree .= "\" ";
3258
		$tmpvalue = trim($this->GetDefault());
3259
		if (!empty($tmpvalue)) {
3260
			$tree .= " class=\"navlnk\"";
3261
		}
3262
		$tree .= " >" . $this->GetQname() . "</a>";
3263
		$tree .= "</li>";
3264
		return $tree;
3265
	}
3266

    
3267
	/* Even this should take children into consideration */
3268
	function build_rules(&$default = false) {
3269
		$pfq_rule = "queue ". $this->qname;
3270
		if ($this->GetInterface()) {
3271
			$pfq_rule .= " on ".get_real_interface($this->GetInterface());
3272
		}
3273
		if ($this->GetBandwidth() && $this->GetBwscale()) {
3274
			$pfq_rule .= " bandwidth ".trim($this->GetBandwidth()).$this->GetBwscale();
3275
		}
3276
		$tmpvalue = trim($this->GetQpriority());
3277
		if (is_numeric($tmpvalue)) {
3278
			$pfq_rule .= " priority " . $this->GetQpriority();
3279
		}
3280
		$tmpvalue = trim($this->GetQlimit());
3281
		if (!empty($tmpvalue)) {
3282
			$pfq_rule .= " qlimit " . $this->GetQlimit();
3283
		}
3284
		if ($this->GetDefault() || $this->GetRed() || $this->GetRio() ||
3285
		    $this->GetEcn() || $this->GetBuckets() || $this->GetHogs() || $this->GetCodel()) {
3286
			$pfq_rule .= " fairq ( ";
3287
			$tmpvalue = trim($this->GetRed());
3288
			if (!empty($tmpvalue)) {
3289
				$comma = 1;
3290
				$pfq_rule .= " red ";
3291
			}
3292
			$tmpvalue = trim($this->GetCodel());
3293
			if (!empty($tmpvalue)) {
3294
				$comma = 1;
3295
				$pfq_rule .= " codel ";
3296
			}
3297
			$tmpvalue = trim($this->GetRio());
3298
			if (!empty($tmpvalue)) {
3299
				if ($comma) {
3300
					$pfq_rule .= " ,";
3301
				}
3302
				$comma = 1;
3303
				$pfq_rule .= " rio ";
3304
			}
3305
			$tmpvalue = trim($this->GetEcn());
3306
			if (!empty($tmpvalue)) {
3307
				if ($comma) {
3308
					$pfq_rule .= " ,";
3309
				}
3310
				$comma = 1;
3311
				$pfq_rule .= " ecn ";
3312
			}
3313
			$tmpvalue = trim($this->GetDefault());
3314
			if (!empty($tmpvalue)) {
3315
				if ($comma) {
3316
					$pfq_rule .= " ,";
3317
				}
3318
				$comma = 1;
3319
				$pfq_rule .= " default ";
3320
				$default = true;
3321
			}
3322
			$tmpvalue = trim($this->GetBuckets());
3323
			if (!empty($tmpvalue)) {
3324
				if ($comma) {
3325
					$pfq_rule .= ", ";
3326
				}
3327
				$pfq_rule .= " buckets " . $this->GetBuckets() . " ";
3328
			}
3329
			$tmpvalue = trim($this->GetHogs());
3330
			if (!empty($tmpvalue)) {
3331
				if ($comma) {
3332
					$pfq_rule .= ", ";
3333
				}
3334
				$pfq_rule .= " hogs " . $this->GetHogs() . " ";
3335
			}
3336
			$pfq_rule .= " ) ";
3337
		}
3338

    
3339
		$pfq_rule .= " \n";
3340
		return $pfq_rule;
3341
	}
3342

    
3343
	function build_form() {
3344
		$form = parent::build_form();
3345

    
3346
		$section = new Form_Section('');
3347

    
3348
		$group = new Form_Group('Bandwidth');
3349

    
3350
		$group->add(new Form_Input(
3351
			'bandwidth',
3352
			null,
3353
			'number',
3354
			$this->GetBandwidth()
3355
		));
3356

    
3357
		$group->add(new Form_Select(
3358
			'bandwidthtype',
3359
			null,
3360
			$this->GetBwscale(),
3361
			array('Kb' => 'Kbit/s',
3362
				  'Mb' => 'Mbit/s',
3363
				  'Gb' => 'Gbit/s',
3364
				  'b' => 'Bit/s',
3365
				  '%' => '%')
3366
		));
3367

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

    
3370
		$section->add($group);
3371

    
3372
		$section->addInput(new Form_Input(
3373
			'buckets',
3374
			'Scheduler specific options',
3375
			'text',
3376
			$this->GetBuckets()
3377
		))->setHelp('Number of buckets available');
3378

    
3379
		$section->addInput(new Form_Input(
3380
			'hogs',
3381
			'',
3382
			'text',
3383
			$this->GetHogs()
3384
			))->setHelp('Bandwidth limit for hosts to not saturate link');
3385

    
3386
		$form->add($section);
3387
		return $form;
3388
	}
3389

    
3390
	function update_altq_queue_data(&$data) {
3391
		$this->ReadConfig($data);
3392
	}
3393

    
3394
	function wconfig() {
3395
		$cflink =& get_reference_to_me_in_config($this->GetLink());
3396
		if (!is_array($cflink)) {
3397
			$cflink = array();
3398
		}
3399
		$cflink['interface'] = $this->GetInterface();
3400
		$cflink['qlimit'] = trim($this->GetQlimit());
3401
		if (empty($cflink['qlimit'])) {
3402
			unset($cflink['qlimit']);
3403
		}
3404
		$cflink['priority'] = trim($this->GetQpriority());
3405
		if (!is_numeric($cflink['priority'])) {
3406
			unset($cflink['priority']);
3407
		}
3408
		$cflink['name'] = $this->GetQname();
3409
		$cflink['description'] = trim($this->GetDescription());
3410
		if (empty($cflink['description'])) {
3411
			unset($cflink['description']);
3412
		}
3413
		$cflink['bandwidth'] = $this->GetBandwidth();
3414
		$cflink['bandwidthtype'] = $this->GetBwscale();
3415
		$cflink['enabled'] = $this->GetEnabled();
3416
		if (empty($cflink['enabled'])) {
3417
			unset($cflink['enabled']);
3418
		}
3419
		$cflink['default'] = trim($this->GetDefault());
3420
		if (empty($cflink['default'])) {
3421
			unset($cflink['default']);
3422
		}
3423
		$cflink['red'] = trim($this->GetRed());
3424
		if (empty($cflink['red'])) {
3425
			unset($cflink['red']);
3426
		}
3427
		$cflink['rio'] = trim($this->GetRio());
3428
		if (empty($cflink['rio'])) {
3429
			unset($cflink['rio']);
3430
		}
3431
		$cflink['ecn'] = trim($this->GetEcn());
3432
		if (empty($cflink['ecn'])) {
3433
			unset($cflink['ecn']);
3434
		}
3435
		$cflink['codel'] = trim($this->GetCodel());
3436
		if (empty($cflink['codel'])) {
3437
			unset($cflink['codel']);
3438
		}
3439
		$cflink['buckets'] = trim($this->GetBuckets());
3440
		if (empty($cflink['buckets'])) {
3441
			unset($cflink['buckets']);
3442
		}
3443
		$cflink['hogs'] = trim($this->GetHogs());
3444
		if (empty($cflink['hogs'])) {
3445
			unset($cflink['hogs']);
3446
		}
3447
	}
3448
}
3449

    
3450

    
3451
/*
3452
 * dummynet(4) wrappers.
3453
 */
3454

    
3455

    
3456
/*
3457
 * List of respective objects!
3458
 */
3459
$dummynet_pipe_list = array();
3460

    
3461
class dummynet_class {
3462
	var $qname;
3463
	var $qnumber; /* dummynet(4) uses numbers instead of names; maybe integrate with pf the same as altq does?! */
3464
	var $qlimit;
3465
	var $description;
3466
	var $qenabled;
3467
	var $link;
3468
	var $qparent; /* link to upper class so we do things easily on WF2Q+ rule creation */
3469
	var $plr;
3470

    
3471
	var $buckets;
3472
	/* mask parameters */
3473
	var $mask;
3474
	var $noerror;
3475

    
3476
	/* Accessor functions */
3477
	function SetLink($link) {
3478
		$this->link = $link;
3479
	}
3480
	function GetLink() {
3481
		return $this->link;
3482
	}
3483
	function GetMask() {
3484
		if (!isset($this->mask["type"])) {
3485
			$this->mask["type"] = "none";
3486
		}
3487
		return $this->mask;
3488
	}
3489
	function SetMask($mask) {
3490
		$this->mask = $mask;
3491
	}
3492
	function &GetParent() {
3493
		return $this->qparent;
3494
	}
3495
	function SetParent(&$parent) {
3496
		$this->qparent = &$parent;
3497
	}
3498
	function GetEnabled() {
3499
		return $this->qenabled;
3500
	}
3501
	function SetEnabled($value) {
3502
		$this->qenabled = $value;
3503
	}
3504
	function CanHaveChildren() {
3505
		return false;
3506
	}
3507
	function CanBeDeleted() {
3508
		return true;
3509
	}
3510
	function GetQname() {
3511
		return $this->qname;
3512
	}
3513
	function SetQname($name) {
3514
		$this->qname = trim($name);
3515
	}
3516
	function GetQlimit() {
3517
		return $this->qlimit;
3518
	}
3519
	function SetQlimit($limit) {
3520
		$this->qlimit = $limit;
3521
	}
3522
	function GetDescription() {
3523
		return $this->description;
3524
	}
3525
	function SetDescription($str) {
3526
		$this->description = trim($str);
3527
	}
3528
	function GetFirstime() {
3529
		return $this->firsttime;
3530
	}
3531
	function SetFirsttime($number) {
3532
		$this->firsttime = $number;
3533
	}
3534
	function GetBuckets() {
3535
		return $this->buckets;
3536
	}
3537
	function SetBuckets($buckets) {
3538
		$this->buckets = $buckets;
3539
	}
3540
	function SetNumber($number) {
3541
		$this->qnumber = $number;
3542
	}
3543
	function GetNumber() {
3544
		return $this->qnumber;
3545
	}
3546
	function GetPlr() {
3547
		return $this->plr;
3548
	}
3549
	function SetPlr($plr) {
3550
		$this->plr = $plr;
3551
	}
3552

    
3553
	function build_javascript() {
3554
		$javascript .= "<script type=\"text/javascript\">\n";
3555
		$javascript .= "//<![CDATA[\n";
3556
		$javascript .= "function enable_maskbits(enable_over) {\n";
3557
		$javascript .= "var e = document.getElementById(\"mask\");\n";
3558
		$javascript .= "if ((e.options[e.selectedIndex].text == \"none\") || enable_over) {\n";
3559
		$javascript .= "document.iform.maskbits.disabled = 1;\n";
3560
		$javascript .= "document.iform.maskbits.value = \"\";\n";
3561
		$javascript .= "document.iform.maskbitsv6.disabled = 1;\n";
3562
		$javascript .= "document.iform.maskbitsv6.value = \"\";\n";
3563
		$javascript .= "} else {\n";
3564
		$javascript .= "document.iform.maskbits.disabled = 0;\n";
3565
		$javascript .= "document.iform.maskbitsv6.disabled = 0;\n";
3566
		$javascript .= "}}\n";
3567
		$javascript .= "//]]>\n";
3568
		$javascript .= "</script>\n";
3569
		return $javascript;
3570
	}
3571

    
3572
	function validate_input($data, &$input_errors) {
3573
		$reqdfields[] = "bandwidth";
3574
		$reqdfieldsn[] = gettext("Bandwidth");
3575
		/*$reqdfields[] = "burst";
3576
		$reqdfieldsn[] = gettext("Burst"); */
3577
		$reqdfields[] = "bandwidthtype";
3578
		$reqdfieldsn[] = gettext("Bandwidthtype");
3579
		$reqdfields[] = "newname";
3580
		$reqdfieldsn[] = gettext("Name");
3581

    
3582
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
3583

    
3584
		if ($data['plr'] && (!is_numeric($data['plr']) ||
3585
		    ($data['plr'] < 0) || ($data['plr'] > 1))) {
3586
			$input_errors[] = gettext("Packet Loss Rate must be a value between 0 and 1.");
3587
		}
3588
		if ($data['buckets'] && (!is_numeric($data['buckets']) ||
3589
		    ($data['buckets'] < 16) || ($data['buckets'] > 65535))) {
3590
			$input_errors[] = gettext("Buckets must be an integer between 16 and 65535.");
3591
		}
3592
		if ($data['qlimit'] && (!is_numeric($data['qlimit']))) {
3593
			$input_errors[] = gettext("Queue limit must be an integer");
3594
		}
3595
		if (!empty($data['newname']) && !preg_match("/^[a-zA-Z0-9_-]+$/", $data['newname'])) {
3596
			$input_errors[] = gettext("Queue names must be alphanumeric and _ or - only.");
3597
		}
3598
		if (!empty($data['name']) && !preg_match("/^[a-zA-Z0-9_-]+$/", $data['name'])) {
3599
			$input_errors[] = gettext("Queue names must be alphanumeric and _ or - only.");
3600
		}
3601
		if (isset($data['maskbits']) && ($data['maskbits'] <> "")) {
3602
			if ((!is_numeric($data['maskbits'])) || ($data['maskbits'] <= 0) || ($data['maskbits'] > 32)) {
3603
				$input_errors[] = gettext("IPv4 bit mask must be blank or numeric value between 1 and 32.");
3604
			}
3605
		}
3606
		if (isset($data['maskbitsv6']) && ($data['maskbitsv6'] <> "")) {
3607
			if ((!is_numeric($data['maskbitsv6'])) || ($data['maskbitsv6'] <= 0) || ($data['maskbitsv6'] > 128)) {
3608
				$input_errors[] = gettext("IPv6 bit mask must be blank or numeric value between 1 and 128.");
3609
			}
3610
		}
3611
	}
3612

    
3613
	function build_mask_rules(&$pfq_rule) {
3614
		$mask = $this->GetMask();
3615
		if (!empty($mask['type'])) {
3616
			if ($mask['type'] <> 'none') {
3617
				$pfq_rule .= " mask";
3618
			}
3619
			switch ($mask['type']) {
3620
				case 'srcaddress':
3621
					if (!empty($mask['bitsv6']) && ($mask['bitsv6'] <> "")) {
3622
						$pfq_rule .= " src-ip6 /" . $mask['bitsv6'];
3623
					} else {
3624
						$pfq_rule .= " src-ip6 /128";
3625
					}
3626
					if (!empty($mask['bits']) && ($mask['bits'] <> "")) {
3627
						$pfq_rule .= sprintf(" src-ip 0x%x", gen_subnet_mask_long($mask['bits']));
3628
					} else {
3629
						$pfq_rule .= " src-ip 0xffffffff";
3630
					}
3631
					break;
3632
				case 'dstaddress':
3633
					if (!empty($mask['bitsv6']) && ($mask['bitsv6'] <> "")) {
3634
						$pfq_rule .= " dst-ip6 /" . $mask['bitsv6'];
3635
					} else {
3636
						$pfq_rule .= " dst-ip6 /128";
3637
					}
3638
					if (!empty($mask['bits']) && ($mask['bits'] <> "")) {
3639
						$pfq_rule .= sprintf(" dst-ip 0x%x", gen_subnet_mask_long($mask['bits']));
3640
					} else {
3641
						$pfq_rule .= " dst-ip 0xffffffff";
3642
					}
3643
					break;
3644
				default:
3645
					break;
3646
			}
3647
		}
3648
	}
3649

    
3650
}
3651

    
3652
class dnpipe_class extends dummynet_class {
3653
	var $delay;
3654
	var $qbandwidth = array();
3655
	var $qbandwidthtype;
3656

    
3657
	/* Limiter queue patch */
3658
    var $ecn; // ecn 'on' or 'off'
3659
    var $aqm; // key to aqm_map
3660
    var $aqm_params = array(); // AQM params
3661
    var $scheduler;	// key to scheduler_map
3662
    var $scheduler_params = array(); // AQM params
3663
	function GetAQM() {
3664
			return $this->aqm;
3665
	}
3666
	function SetAQM($aqm) {
3667
			$this->aqm = $aqm;
3668
	}
3669
	function GetAQMParameters() {
3670
			return $this->aqm_params;
3671
	}
3672
	function GetAQMParameter($parameter) {
3673
			return $this->aqm_params[$parameter];
3674
	}
3675
	function SetAQMParameter($key, $value) {
3676
			return $this->aqm_params[$key] = $value;
3677
	}
3678
	function SetAQMParameters($params) {
3679
			$this->aqm_params = $params;
3680
	}
3681
	function GetECN() {
3682
			return $this->ecn;
3683
	}
3684
	function SetECN($ecn) {
3685
			$this->ecn = $ecn;
3686
	}
3687
	function GetScheduler() {
3688
			return $this->scheduler;
3689
	}
3690
	function SetScheduler($scheduler) {
3691
			$this->scheduler = $scheduler;
3692
	}
3693
	function GetSchedulerParameters() {
3694
			return $this->scheduler_params;
3695
	}
3696
	function SetSchedulerParameters($params) {
3697
			$this->scheduler_params = $params;
3698
	}
3699
	function SetSchedulerParameter($key, $value) {
3700
			$this->scheduler_params[$key] = $value;
3701
	}
3702
	function GetSchedulerParameter($key) {
3703
			return $this->scheduler_params[$key];
3704
	}
3705
	/* End limiter queue patch */
3706

    
3707
		/* This is here to help on form building and building rules/lists */
3708
	var $subqueues = array();
3709

    
3710
	function CanHaveChildren() {
3711
		return true;
3712
	}
3713
	function SetDelay($delay) {
3714
		$this->delay = $delay;
3715
	}
3716
	function GetDelay() {
3717
		return $this->delay;
3718
	}
3719
	function delete_queue() {
3720
		cleanup_dnqueue_from_rules($this->GetQname());
3721
		foreach ($this->subqueues as $q) {
3722
			$q->delete_queue();
3723
		}
3724
		unset_dn_object_by_reference($this->GetLink());
3725
		@pfSense_ipfw_pipe("pipe delete " . $this->GetNumber());
3726
		@pfSense_ipfw_pipe("sched delete " . $this->GetNumber());
3727
	}
3728
	function GetBandwidth() {
3729
		return $this->qbandwidth;
3730
	}
3731
	function SetBandwidth($bandwidth) {
3732
		$this->qbandwidth = $bandwidth;
3733
	}
3734
	function GetBurst() {
3735
		return $this->qburst;
3736
	}
3737
	function SetBurst($burst) {
3738
		$this->qburst = $burst;
3739
	}
3740

    
3741
	function &add_queue($interface, &$queue, &$path, &$input_errors) {
3742

    
3743
		if (!is_array($this->subqueues)) {
3744
			$this->subqueues = array();
3745
		}
3746

    
3747
		$__tmp_q = new dnqueue_class(); $q =& $__tmp_q;
3748
		$q->SetLink($path);
3749
		$q->SetEnabled("on");
3750
		$q->SetPipe($this->GetQname());
3751
		$q->SetParent($this);
3752
		$q->ReadConfig($queue);
3753
		$q->validate_input($queue, $input_errors);
3754

    
3755
		if (!is_array($input_errors)) {
3756
			$input_errors = array();
3757
		}
3758

    
3759
		if (count($input_errors)) {
3760
			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)));
3761
			return $q;
3762
		}
3763
		$number = dnqueue_find_nextnumber();
3764
		$q->SetNumber($number);
3765
		$this->subqueues[$q->GetQname()] = &$q;
3766

    
3767
		return $q;
3768
	}
3769

    
3770
	function &get_queue_list(&$q = null) {
3771
		$qlist = array();
3772

    
3773
		$qlist[$this->GetQname()] = $this->GetNumber();
3774
		if (is_array($this->subqueues)) {
3775
			foreach ($this->subqueues as $queue) {
3776
				$queue->get_queue_list($qlist);
3777
			}
3778
		}
3779
		return $qlist;
3780
	}
3781

    
3782
	/*
3783
	 * Should search even its children
3784
	 */
3785
	function &find_queue($pipe, $qname) {
3786
		if ($qname == $this->GetQname()) {
3787
			return $this;
3788
		}
3789
		foreach ($this->subqueues as $q) {
3790
			$result =& $q->find_queue("", $qname);
3791
			if ($result) {
3792
				return $result;
3793
			}
3794
		}
3795
	}
3796

    
3797
	function &find_parentqueue($pipe, $qname) {
3798
		return NULL;
3799
	}
3800

    
3801
	function validate_input($data, &$input_errors) {
3802
		parent::validate_input($data, $input_errors);
3803

    
3804
		$schedule = 0;
3805
		$schedulenone = 0;
3806
		$entries = 0;
3807
		/* XXX: Really no better way? */
3808
		for ($i = 0; $i < 2900; $i++) {
3809
			if (!empty($data["bwsched{$i}"])) {
3810
				if ($data["bwsched{$i}"] != "none") {
3811
					$schedule++;
3812
				} else {
3813
					$schedulenone++;
3814
				}
3815
			}
3816
			if (!empty($data["bandwidth{$i}"])) {
3817
				if (!is_numeric($data["bandwidth{$i}"])) {
3818
					$input_errors[] = sprintf(gettext("Bandwidth for schedule %s must be an integer."), $data["bwsched{$i}"]);
3819
				} else if (($data["burst{$i}"] != "") && (!is_numeric($data["burst{$i}"]))) {
3820
					$input_errors[] = sprintf(gettext("Burst for schedule %s must be an integer."), $data["bwsched{$i}"]);
3821
				} else {
3822
					$entries++;
3823
				}
3824
			}
3825
		}
3826
		if ($schedule == 0 && $entries > 1) {
3827
			$input_errors[] = gettext("A schedule needs to be specified for every additional entry.");
3828
		}
3829
		if ($schedulenone > 0 && $entries > 1) {
3830
			$input_errors[] = gettext("If more than one bandwidth configured all schedules need to be selected.");
3831
		}
3832
		if ($entries == 0) {
3833
			$input_errors[] = gettext("At least one bw specification is necessary.");
3834
		}
3835
		if ($data['delay'] && (!is_numeric($data['delay']))) {
3836
			$input_errors[] = gettext("Delay must be an integer.");
3837
		}
3838

    
3839
		/* Limiter patch */
3840
		$selectedScheduler = getSchedulers()[$data['sched']];
3841
		if (!$selectedScheduler) {
3842
			$input_errors[] = gettext("Selected scheduler not recognized.");
3843
		}
3844
		$selectedAqm = getAQMs()[$data['aqm']];
3845
		if (!empty($data['aqm']) && !$selectedAqm) {
3846
			$input_errors[] = gettext("Selected AQM not recognized.");
3847
		}
3848
		if ($data['ecn'] && $data['ecn'] == 'on' && !$selectedScheduler["ecn"] && !$selectedAqm["ecn"]) {
3849
			$input_errors[] = gettext("Explicit Congestion Notification is selected, but neither " . $selectedAqm["name"] . " nor " . $selectedScheduler["name"] . " support it.");
3850
		}
3851
		/* End limiter patch */
3852
	}
3853

    
3854
	function ReadConfig(&$q) {
3855
		if (!empty($q['name']) && !empty($q['newname']) && $q['name'] != $q['newname']) {
3856
			$this->SetQname($q['newname']);
3857
		} else if (!empty($q['newname'])) {
3858
			$this->SetQname($q['newname']);
3859
		} else {
3860
			$this->SetQname($q['name']);
3861
		}
3862
		$this->SetNumber($q['number']);
3863

    
3864
		if (!empty($_POST)) {
3865
			$bandwidth = array();
3866
			/* XXX: Really no better way? */
3867
			for ($i = 0; $i < 2900; $i++) {
3868
				if (isset($q["bandwidth{$i}"]) && $q["bandwidth{$i}"] <> "") {
3869
					$bw = array();
3870
					$bw['bw'] = $q["bandwidth{$i}"];
3871
					$bw['burst'] = $q["burst{$i}"];
3872
					if (isset($q["bwtype{$i}"]) && $q["bwtype{$i}"]) {
3873
						$bw['bwscale'] = $q["bwtype{$i}"];
3874
					}
3875
					if (isset($q["bwsched{$i}"]) && $q["bwsched{$i}"]) {
3876
						$bw['bwsched'] = $q["bwsched{$i}"];
3877
					}
3878
					$bandwidth[] = $bw;
3879
				}
3880
			}
3881
			$this->SetBandwidth($bandwidth);
3882
		}
3883

    
3884
		if (is_array($q['bandwidth']) && is_array($q['bandwidth']['item'])) {
3885
			$this->SetBandwidth($q['bandwidth']['item']);
3886
			$this->SetBurst($q['burst']['item']);
3887
		}
3888

    
3889
		if (isset($q['qlimit']) && $q['qlimit'] <> "") {
3890
			$this->SetQlimit($q['qlimit']);
3891
		} else {
3892
			$this->SetQlimit("");
3893
		}
3894
		if (isset($q['mask']) && $q['mask'] <> "") {
3895
			$masktype = $q['mask'];
3896
		} else {
3897
			$masktype = "";
3898
		}
3899
		if (isset($q['maskbits']) && $q['maskbits'] <> "") {
3900
			$maskbits = $q['maskbits'];
3901
		} else {
3902
			$maskbits = "";
3903
		}
3904
		if (isset($q['maskbitsv6']) && $q['maskbitsv6'] <> "") {
3905
			$maskbitsv6 = $q['maskbitsv6'];
3906
		} else {
3907
			$maskbitsv6 = "";
3908
		}
3909
		$this->SetMask(array("type" => $masktype, "bits" => $maskbits, "bitsv6" => $maskbitsv6));
3910
		if (isset($q['buckets']) && $q['buckets'] <> "") {
3911
			$this->SetBuckets($q['buckets']);
3912
		} else {
3913
			$this->SetBuckets("");
3914
		}
3915
		if (isset($q['plr']) && $q['plr'] <> "") {
3916
			$this->SetPlr($q['plr']);
3917
		} else {
3918
			$this->SetPlr("");
3919
		}
3920
		if (isset($q['delay']) && $q['delay'] <> "") {
3921
			$this->SetDelay($q['delay']);
3922
		} else {
3923
			$this->SetDelay(0);
3924
		}
3925
		if (isset($q['description']) && $q['description'] <> "") {
3926
			$this->SetDescription($q['description']);
3927
		} else {
3928
			$this->SetDescription("");
3929
		}
3930
		$this->SetEnabled($q['enabled']);
3931

    
3932
		/* Limiter patch */
3933
		if (isset($q['aqm']) && $q['aqm'] <> "") {
3934
				$this->SetAQM($q['aqm']);
3935
		} else {
3936
				$this->SetAQM('droptail');
3937
		}
3938
		// parse AQM arguments
3939
		$aqm_map = getAQMs();
3940
		$selectedParameters = $aqm_map[$this->getAQM()]["parameters"];
3941
		if (is_array($selectedParameters)) {
3942
			foreach ($selectedParameters as $key => $value) {
3943
				$config_key = 'param_' . $this->GetAQM() . '_' . $key;
3944
				if (isset($q[$config_key]) && $q[$config_key] <> "") {
3945
					$this->SetAQMParameter($key, $q[$config_key]);
3946
				} else {
3947
					$this->SetAQMParameter($key, $value["default"]);
3948
				}
3949
			}
3950
		}
3951

    
3952
		if (isset($q['sched']) && $q['sched'] <> "") {
3953
				$this->SetScheduler($q['sched']);
3954
		} else {
3955
				$this->SetScheduler('fifo');
3956
		}
3957
		$scheduler_map = getSchedulers();
3958
		$selectedParameters = $scheduler_map[$this->getScheduler()]["parameters"];
3959
		if (is_array($selectedParameters)) {
3960
			foreach ($selectedParameters as $key => $value) {
3961
				$config_key = 'param_' . $this->GetScheduler() . '_' . $key;
3962
				if (isset($q[$config_key]) && $q[$config_key] <> "") {
3963
					$this->SetSchedulerParameter($key, $q[$config_key]);
3964
				} else {
3965
					$this->SetSchedulerParameter($key, $value["default"]);
3966
				}
3967
			}
3968
		}
3969

    
3970
		// ecn flag
3971
		$this->SetECN($q['ecn']);
3972
		/* End limiter patch */
3973
	}
3974

    
3975
	function build_tree() {
3976
		$tree = " <li><a href=\"firewall_shaper_vinterface.php?pipe=" . $this->GetQname() ."&amp;queue=".$this->GetQname() ."&amp;action=show\">";
3977
		$tree .= $this->GetQname() . "</a>";
3978
		if (is_array($this->subqueues)) {
3979
			$tree .= "<ul>";
3980
			foreach ($this->subqueues as $q) {
3981
				$tree .= $q->build_tree();
3982
			}
3983
			$tree .= "</ul>";
3984
		}
3985
		$tree .= "</li>";
3986

    
3987
		return $tree;
3988
	}
3989

    
3990
	function build_rules() {
3991
		global $config, $time_based_rules;
3992

    
3993
		if ($this->GetEnabled() == "") {
3994
			return;
3995
		}
3996

    
3997
		$pfq_rule = "\npipe ". $this->GetNumber() . " config ";
3998
		$found = false;
3999
		$bandwidth = $this->GetBandwidth();
4000
		if (is_array($bandwidth)) {
4001
			foreach ($bandwidth as $bw) {
4002
				if ($bw['bwsched'] != "none") {
4003
					$time_based_rules = true;
4004
					if (is_array($config['schedules']) && is_array($config['schedules']['schedule'])) {
4005
						foreach ($config['schedules']['schedule'] as $schedule) {
4006
							if ($bw['bwsched'] == $schedule['name']) {
4007
								if (filter_get_time_based_rule_status($schedule)) {
4008
									/* pipe throughputs must always be an integer, enforce that restriction again here. */
4009
									$pfq_rule .= " bw ".round(trim($bw['bw']),0).$bw['bwscale'];
4010
									if (is_numeric($bw['burst']) && ($bw['burst'] > 0)) {
4011
										$pfq_rule .= " burst ".trim($bw['burst']);
4012
									}
4013
									$found = true;
4014
									break;
4015
								}
4016
							}
4017
						}
4018
					} else {
4019
						$pfq_rule .= " bw 0";
4020
						$found = true;
4021
						break;
4022
					}
4023
				} else {
4024
					/* pipe throughputs must always be an integer, enforce that restriction again here. */
4025
					$pfq_rule .= " bw ".round(trim($bw['bw']), 0).$bw['bwscale'];
4026
					if (is_numeric($bw['burst']) && ($bw['burst'] > 0)) {
4027
						$pfq_rule .= " burst ".trim($bw['burst']);
4028
					}
4029
					$found = true;
4030
					break;
4031
				}
4032
			}
4033
			if ($found == false) {
4034
				$pfq_rule .= " bw 0";
4035
			}
4036
		} else {
4037
			$pfq_rule .= " bw 0";
4038
		}
4039

    
4040
		if ($this->GetQlimit()) {
4041
			$pfq_rule .= " queue " . $this->GetQlimit();
4042
		}
4043
		if ($this->GetPlr()) {
4044
			$pfq_rule .= " plr " . $this->GetPlr();
4045
		}
4046
		if ($this->GetBuckets()) {
4047
			$pfq_rule .= " buckets " . $this->GetBuckets();
4048
		}
4049
		if ($this->GetDelay()) {
4050
			$pfq_rule .= " delay " . $this->GetDelay();
4051
		}
4052
		$this->build_mask_rules($pfq_rule);
4053

    
4054
		/* Limiter patch */
4055
		$selectedAQM = getAQMs()[$this->getAQM()];
4056
		if ($selectedAQM) {
4057
			$pfq_rule .= " " . FormatParameters($selectedAQM["parameter_format"], $this->GetAQMParameters());
4058
			if ($selectedAQM["ecn"]) {
4059
				if ($this->getECN() == 'on') {
4060
					$pfq_rule .= ' ecn';
4061
				} else {
4062
					$pfq_rule .= ' noecn';
4063
				}
4064
			}
4065
		}
4066
		$selectedScheduler = getSchedulers()[$this->getScheduler()];
4067
		if ($selectedScheduler) {
4068
			$pfq_rule .= "\nsched ". $this->GetNumber() . " config pipe " . $this->GetNumber() . " " . FormatParameters($selectedScheduler["parameter_format"], $this->GetSchedulerParameters());
4069
			if ($selectedScheduler["ecn"]) {
4070
				if ($this->getECN() == 'on') {
4071
					$pfq_rule .= ' ecn';
4072
				} else {
4073
					$pfq_rule .= ' noecn';
4074
				}
4075
			}
4076
		}
4077
		$pfq_rule .= "\n";
4078
		/* End patch */
4079

    
4080
		if (!empty($this->subqueues) && count($this->subqueues) > 0) {
4081
			foreach ($this->subqueues as $q) {
4082
				$pfq_rule .= $q->build_rules();
4083
			}
4084
		}
4085

    
4086
		$pfq_rule .= " \n";
4087

    
4088
		return $pfq_rule;
4089
	}
4090

    
4091
	function update_dn_data(&$data) {
4092
		$this->ReadConfig($data);
4093
	}
4094

    
4095
	function build_javascript() {
4096
		global $g, $config;
4097

    
4098
		$javasr = parent::build_javascript();
4099

    
4100
		//build list of schedules
4101
		$schedules = "<option value='none'>none</option>";
4102
		if (is_array($config['schedules']) && is_array($config['schedules']['schedule'])) {
4103
			foreach ($config['schedules']['schedule'] as $schedule) {
4104
				if ($schedule['name'] <> "") {
4105
					$schedules .= "<option value='{$schedule['name']}'>{$schedule['name']}</option>";
4106
				}
4107
			}
4108
		}
4109
		$bwopt = "";
4110
		foreach (array("Kb" => "Kbit/s", "Mb" => "Mbit/s", "Gb" => "Gbit/s", "b" => "Bit/s") as $bwidx => $bw) {
4111
			$bwopt .= "<option value='{$bwidx}'>{$bw}</option>";
4112
		}
4113

    
4114
		$javasr .= <<<EOD
4115
<script type='text/javascript'>
4116
//<![CDATA[
4117
var addBwRowTo = (function() {
4118

    
4119
	return (function (tableId) {
4120

    
4121
	var table = document.getElementById(tableId);
4122
	var totalrows = table.rows.length -1;
4123

    
4124
	var row = table.insertRow(totalrows + 1);
4125
	var cell1 = row.insertCell(0);
4126
	var cell2 = row.insertCell(1);
4127
	var cell3 = row.insertCell(2);
4128
	var cell4 = row.insertCell(3);
4129

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

    
4135
	});
4136
})();
4137

    
4138
function removeBwRow(el) {
4139
	var d = el.parentNode.parentNode.rowIndex;
4140
	document.getElementById('maintable').deleteRow(d);
4141
}
4142

    
4143
function ceil_func(el){
4144
	el.value = Math.ceil(el.value);
4145

    
4146
}
4147
//]]>
4148
</script>
4149

    
4150
EOD;
4151

    
4152
		return $javasr;
4153
	}
4154

    
4155
	// Compose a table of bandwidths that can then be inserted into the form using a Form_StaticText
4156
	// The table has been "Bootstrapped" to match the web design while maintaining compatibility with
4157
	// with the javascript in this class
4158
	function build_bwtable() {
4159
		global $config;
4160

    
4161
		$bandwidth = $this->GetBandwidth();
4162
				//build list of schedules
4163
		$schedules = array();
4164
		$schedules[] = "none";//leave none to leave rule enabled all the time
4165
		if (is_array($config['schedules']) && is_array($config['schedules']['schedule'])) {
4166
			foreach ($config['schedules']['schedule'] as $schedule) {
4167
				if ($schedule['name'] != "") {
4168
					$schedules[] = $schedule['name'];
4169
				}
4170
			}
4171
		}
4172

    
4173
		$form = '<div class="table-responsive">';
4174
		$form .= '<table id="maintable" class="table table-hover table-striped">';
4175
		$form .= "<thead><tr>";
4176
		$form .= "<th>Bandwidth</th>";
4177
		//$form .= "<td width='35%'><div id='fifthcolumn'>Burst</div></td>";
4178
		$form .= "<th>Bw type</th>";
4179
		$form .= "<th>Schedule</th>";
4180
		$form .= "<th></th>";
4181
		$form .= "</tr></thead>";
4182
		$form .= "<tbody>";
4183

    
4184
		// If there are no bandwidths defined, make a blank one for convenience
4185
		if (empty($bandwidth)) {
4186
			$bandwidth = array(0 => array('bw' => '', 'bwscale' => 'Kb', 'bwsched' => 'none'));
4187
		}
4188

    
4189
		if (is_array($bandwidth)) {
4190
			foreach ($bandwidth as $bwidx => $bw) {
4191
				$form .= '<tr>';
4192
				$form .= '<td class="col-xs-4">';
4193
				$form .= "<input class='form-control' onchange=\"ceil_func(this)\" type=\"number\" id=\"bandwidth{$bwidx}\" name=\"bandwidth{$bwidx}\" value=\"" . ceil($bw['bw']) ."\" min=\"0\" step=\"1\"/>";
4194
				//$form .= "</td><td width='20%'>";
4195
				//$form .= "<input class='formfld unknown' size='10' type=\"text\" id=\"burst{$bwidx}\" name=\"burst{$bwidx}\" value=\"{$bw['burst']}\" />";
4196
				$form .= "</td>";
4197
				$form .= '<td class="col-xs-4">';
4198
				$form .= "<select id=\"bwtype{$bwidx}\" name=\"bwtype{$bwidx}\" class=\"form-control\">";
4199

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

    
4203
					if ($bw['bwscale'] == $bwsidx) {
4204
						$form .= " selected";
4205
					}
4206

    
4207
					$form .= ">{$bwscale}</option>";
4208
				}
4209

    
4210
				$form .= "</select>";
4211
				$form .= "</td>";
4212
				$form .= '<td class="col-xs-4">';
4213
				$form .= "<select id=\"bwsched{$bwidx}\" name=\"bwsched{$bwidx}\" class=\"form-control\">";
4214

    
4215
				foreach ($schedules as $schd) {
4216
					$selected = "";
4217
					if ($bw['bwsched'] == $schd) {
4218
						$selected = "selected";
4219
					}
4220

    
4221
					$form .= "<option value='{$schd}' {$selected}>{$schd}</option>";
4222
				}
4223

    
4224
				$form .= "</select>";
4225
				$form .= "</td>";
4226
				$form .= '<td>';
4227
				$form .= '<a class="btn btn-warning" onclick="removeBwRow(this); return false;"><i class="fa fa-trash icon-embed-btn"></i>' . gettext('Delete') . '</a>';
4228
				$form .= "</td></tr>";
4229
			}
4230
		}
4231
		$form .= "</tbody></table></div><br />";
4232

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

    
4237
		return($form);
4238
	}
4239

    
4240
	function build_form() {
4241
		global $g, $config, $pipe, $action, $qname;
4242

    
4243
		//build list of schedules
4244
		$schedules = array();
4245
		$schedules[] = "none";//leave none to leave rule enabled all the time
4246
		if (is_array($config['schedules']) && is_array($config['schedules']['schedule'])) {
4247
			foreach ($config['schedules']['schedule'] as $schedule) {
4248
				if ($schedule['name'] <> "") {
4249
					$schedules[] = $schedule['name'];
4250
				}
4251
			}
4252
		}
4253

    
4254

    
4255
		$sform = new Form();
4256
		$sform->setAction("firewall_shaper.php");
4257

    
4258
		$section = new Form_Section('Limiters');
4259

    
4260
		$section->addInput(new Form_Checkbox(
4261
			'enabled',
4262
			'Enable',
4263
			'Enable limiter and its children',
4264
			($this->GetEnabled() == "on"),
4265
			'on'
4266
		));
4267

    
4268
		$section->addInput(new Form_Input(
4269
			'newname',
4270
			'*Name',
4271
			'text',
4272
			$this->GetQname()
4273
		));
4274

    
4275
		$section->addInput(new Form_Input(
4276
			'name',
4277
			null,
4278
			'hidden',
4279
			$this->GetQname()
4280
		));
4281

    
4282
		if ($this->GetNumber() > 0) {
4283
			$section->addInput(new Form_Input(
4284
				'number',
4285
				null,
4286
				'hidden',
4287
				$this->GetNumber()
4288
			));
4289
		}
4290

    
4291
		$bandwidth = $this->GetBandwidth();
4292

    
4293
		if (is_array($bandwidth)) {
4294
				$section->addInput(new Form_StaticText(
4295
				'Bandwidth',
4296
				$this->build_bwtable()
4297
			));
4298
		}
4299

    
4300
		$mask = $this->GetMask();
4301

    
4302
		$section->addInput(new Form_Select(
4303
			'mask',
4304
			'Mask',
4305
			$mask['type'],
4306
			array('none' => gettext('None'), 'srcaddress' => gettext('Source addresses'), 'dstaddress' => gettext('Destination addresses'))
4307
		))->setHelp('If "source" or "destination" slots is chosen a dynamic pipe with the bandwidth, delay, packet loss ' .
4308
					'and queue size given above will be created for each source/destination IP address encountered, respectively. ' .
4309
					'This makes it possible to easily specify bandwidth limits per host.');
4310

    
4311
		$group = new Form_Group(null);
4312

    
4313
		$group->add(new Form_Select(
4314
			'maskbits',
4315
			null,
4316
			$mask['bits'],
4317
			array_combine(range(32, 1, -1), range(32, 1, -1))
4318
		))->setHelp('IPv4 mask bits%1$s%2$s', '<br />', '255.255.255.255/?');
4319

    
4320
		$group->add(new Form_Select(
4321
			'maskbitsv6',
4322
			null,
4323
			$mask['bitsv6'],
4324
			array_combine(range(128, 1, -1), range(128, 1, -1))
4325
		))->setHelp('IPv6 mask bits%1$s%2$s', '<br />', '<span style="font-family:consolas">ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/?</span>');
4326

    
4327
		$section->add($group);
4328

    
4329
		$section->addInput(new Form_Input(
4330
			'description',
4331
			'Description',
4332
			'text',
4333
			$this->GetDescription()
4334
		))->setHelp('A description may be entered here for administrative reference (not parsed).');
4335

    
4336
		$sform->add($section);
4337

    
4338
		/* Begin limiter patch */
4339
		$aqm_map = getAQMs();
4340
		$scheduler_map = getSchedulers();
4341

    
4342
		$section = new Form_Section('Queue');
4343
		$section->addInput(new Form_Select(
4344
				'aqm',
4345
				'Queue Management Algorithm',
4346
				$this->GetAQM(),
4347
				array_map_assoc(function ($k, $v) {
4348
					return [$k, $v["name"]];
4349
				}, $aqm_map)
4350
		))->setHelp('Active queue management (AQM) is the intelligent drop of network packets inside the limiter, ' .
4351
						'when it becomes full or gets close to becoming full, with the goal of reducing ' .
4352
						'network congestion.');
4353

    
4354
		$section->addInput(new Form_StaticText(
4355
			'',
4356
			build_queue_params($aqm_map, $this->GetAQM(), $this->GetAQMParameters(), "aqm")
4357
		))->setHelp('Specifies the queue management algorithm parameters.');
4358

    
4359
		$section->addInput(new Form_Select(
4360
				'sched',
4361
				'Scheduler',
4362
				$this->GetScheduler(),
4363
				array_map_assoc(function ($k, $v) {
4364
					return [$k, $v["name"]];
4365
				}, $scheduler_map)
4366
		))->setHelp('The scheduler manages the sequence of network packets in the limiter\'s queue.');
4367

    
4368
		$section->addInput(new Form_StaticText(
4369
			'',
4370
			build_queue_params($scheduler_map, $this->GetScheduler(), $this->GetSchedulerParameters(), "sched")
4371
		))->setHelp('Specifies the scheduler parameters.');
4372

    
4373
		$section->addInput(new Form_Input(
4374
				'qlimit',
4375
				'Queue length',
4376
				'number',
4377
				$this->GetQlimit()
4378
		))->setHelp('Specifies the length of the limiter\'s queue, which the scheduler and AQM are responsible for. ' .
4379
			'This field may be left empty.');
4380

    
4381
		$section->addInput(new Form_Checkbox(
4382
			'ecn',
4383
			'ECN',
4384
			'Enable Explicit Congestion Notification (ECN)',
4385
			($this->GetECN() == "on"),
4386
			'on'
4387
		))->setHelp('ECN sets a reserved TCP flag when the queue is nearing or exceeding capacity. Not all AQMs or schedulers support this.');
4388

    
4389
		$sform->add($section);
4390
		/* End limiter patch */
4391

    
4392
		$section = new Form_Section('Advanced Options');
4393

    
4394
		$section->addInput(new Form_Input(
4395
			'delay',
4396
			'Delay (ms)',
4397
			'text',
4398
			$this->GetDelay() > 0 ? $this->GetDelay():null
4399
		))->setHelp('In most cases, zero (0) should specified here (or leave the field empty).');
4400

    
4401
		$section->addInput(new Form_Input(
4402
			'plr',
4403
			'Packet Loss Rate',
4404
			'number',
4405
			$this->GetPlr(),
4406
			['step' => '0.001', 'min' => '0.000']
4407
		))->setHelp('In most cases, zero (0) should be specified here (or leave the field empty). ' .
4408
					'A value of 0.001 means one packet in 1000 gets dropped.');
4409

    
4410
		$section->addInput(new Form_Input(
4411
			'buckets',
4412
			'Bucket size (slots)',
4413
			'number',
4414
			$this->GetBuckets()
4415
		))->setHelp('In most cases, this field should be left empty. It increases the hash size set.');
4416

    
4417
		$sform->add($section);
4418

    
4419
		return($sform);
4420
		}
4421

    
4422
	function wconfig() {
4423
		$cflink =& get_dn_reference_to_me_in_config($this->GetLink());
4424
		if (!is_array($cflink)) {
4425
			$cflink = array();
4426
		}
4427
		$cflink['name'] = $this->GetQname();
4428
		$cflink['number'] = $this->GetNumber();
4429
		$cflink['qlimit'] = $this->GetQlimit();
4430
		$cflink['plr'] = $this->GetPlr();
4431
		$cflink['description'] = $this->GetDescription();
4432

    
4433
		$bandwidth = $this->GetBandwidth();
4434
		if (is_array($bandwidth)) {
4435
			$cflink['bandwidth'] = array();
4436
			$cflink['bandwidth']['item'] = array();
4437
			foreach ($bandwidth as $bwidx => $bw) {
4438
				$cflink['bandwidth']['item'][] = $bw;
4439
			}
4440
		}
4441

    
4442
		$cflink['enabled'] = $this->GetEnabled();
4443
		$cflink['buckets'] = $this->GetBuckets();
4444
		$mask = $this->GetMask();
4445
		$cflink['mask'] = $mask['type'];
4446
		$cflink['maskbits'] = $mask['bits'];
4447
		$cflink['maskbitsv6'] = $mask['bitsv6'];
4448
		$cflink['delay'] = $this->GetDelay();
4449

    
4450
		/* Limiter queue patch */
4451
		$cflink['sched'] = $this->GetScheduler();
4452
		$scheduler_map = getSchedulers();
4453
		$selectedParameters = $scheduler_map[$this->getScheduler()]["parameters"];
4454
		foreach ($selectedParameters as $key => $value) {
4455
			$config_key = 'param_' . $this->GetScheduler() . '_' . $key;
4456
			$cflink[$config_key] = $this->GetSchedulerParameter($key);
4457
		}
4458
		$cflink['aqm'] = $this->GetAQM();
4459
		$aqm_map = GetAQMs();
4460
		$selectedParameters = $aqm_map[$this->getAQM()]["parameters"];
4461
		foreach ($selectedParameters as $key => $value) {
4462
			$config_key = 'param_' . $this->GetAQM() . '_' . $key;
4463
			$cflink[$config_key] = $this->GetAQMParameter($key);
4464
		}
4465
		$cflink['ecn'] = $this->GetECN();
4466
		/* End limiter queue patch */
4467
	}
4468

    
4469
}
4470

    
4471
class dnqueue_class extends dummynet_class {
4472
	var $pipeparent;
4473
	var $weight;
4474
	/* Limiter queue patch */
4475
    var $ecn; // ecn 'on' or 'off'
4476
    var $aqm; // key to aqm_map
4477
    var $aqm_params = array(); // AQM params
4478
	function GetAQM() {
4479
			return $this->aqm;
4480
	}
4481
	function SetAQM($aqm) {
4482
			$this->aqm = $aqm;
4483
	}
4484
	function GetAQMParameters() {
4485
			return $this->aqm_params;
4486
	}
4487
	function GetAQMParameter($parameter) {
4488
			return $this->aqm_params[$parameter];
4489
	}
4490
	function SetAQMParameter($key, $value) {
4491
			return $this->aqm_params[$key] = $value;
4492
	}
4493
	function SetAQMParameters($params) {
4494
			$this->aqm_params = $params;
4495
	}
4496
	function GetECN() {
4497
			return $this->ecn;
4498
	}
4499
	function SetECN($ecn) {
4500
			$this->ecn = $ecn;
4501
	}
4502
	/* End limiter queue patch */
4503

    
4504
	function GetWeight() {
4505
		return $this->weight;
4506
	}
4507
	function SetWeight($weight) {
4508
		$this->weight = $weight;
4509
	}
4510
	function GetPipe() {
4511
		return $this->pipeparent;
4512
	}
4513
	function SetPipe($pipe) {
4514
		$this->pipeparent = $pipe;
4515
	}
4516

    
4517
	/* Just a stub in case we ever try to call this from the frontend. */
4518
	function &add_queue($interface, &$queue, &$path, &$input_errors) {
4519
		return;
4520
	}
4521

    
4522
	function delete_queue() {
4523
		cleanup_dnqueue_from_rules($this->GetQname());
4524
		unset_dn_object_by_reference($this->GetLink());
4525
		@pfSense_ipfw_pipe("queue delete " . $this->GetNumber());
4526
	}
4527

    
4528
	function validate_input($data, &$input_errors) {
4529
		parent::validate_input($data, $input_errors);
4530

    
4531

    
4532
		/* Limiter patch */
4533
		$selectedAqm = getAQMs()[$data['aqm']];
4534
		if (!empty($data['aqm']) && !$selectedAqm) {
4535
			$input_errors[] = gettext("Selected AQM not recognized.");
4536
		}
4537
		if ($data['ecn'] && $data['ecn'] == 'on' && !$selectedAqm["ecn"]) {
4538
			$input_errors[] = gettext("Explicit Congestion Notification is selected, but " . $selectedAqm["name"] . " does not support it.");
4539
		}
4540
		/* End limiter patch */
4541

    
4542
		if ($data['weight'] && ((!is_numeric($data['weight'])) ||
4543
		    ($data['weight'] < 1 && $data['weight'] > 100))) {
4544
			$input_errors[] = gettext("Weight must be an integer between 1 and 100.");
4545
		}
4546
	}
4547

    
4548
	/*
4549
	 * Should search even its children
4550
	 */
4551
	function &find_queue($pipe, $qname) {
4552
		if ($qname == $this->GetQname()) {
4553
			return $this;
4554
		} else {
4555
			return NULL;
4556
		}
4557
	}
4558

    
4559
	function &find_parentqueue($pipe, $qname) {
4560
		return $this->qparent;
4561
	}
4562

    
4563
	function &get_queue_list(&$qlist) {
4564
		if ($this->GetEnabled() == "") {
4565
			return;
4566
		}
4567
		$qlist[$this->GetQname()] = "?" .$this->GetNumber();
4568
	}
4569

    
4570
	function ReadConfig(&$q) {
4571
		if (!empty($q['name']) && !empty($q['newname']) && $q['name'] != $q['newname']) {
4572
			$this->SetQname($q['newname']);
4573
		} else if (!empty($q['newname'])) {
4574
			$this->SetQname($q['newname']);
4575
		} else {
4576
			$this->SetQname($q['name']);
4577
		}
4578
		$this->SetNumber($q['number']);
4579
		if (isset($q['qlimit']) && $q['qlimit'] <> "") {
4580
			$this->SetQlimit($q['qlimit']);
4581
		} else {
4582
			$this->SetQlimit("");
4583
		}
4584
		if (isset($q['mask']) && $q['mask'] <> "") {
4585
			$masktype = $q['mask'];
4586
		} else {
4587
			$masktype = "";
4588
		}
4589
		if (isset($q['maskbits']) && $q['maskbits'] <> "") {
4590
			$maskbits = $q['maskbits'];
4591
		} else {
4592
			$maskbits = "";
4593
		}
4594
		if (isset($q['maskbitsv6']) && $q['maskbitsv6'] <> "") {
4595
			$maskbitsv6 = $q['maskbitsv6'];
4596
		} else {
4597
			$maskbitsv6 = "";
4598
		}
4599
		$this->SetMask(array("type" => $masktype, "bits" => $maskbits, "bitsv6" => $maskbitsv6));
4600
		if (isset($q['buckets']) && $q['buckets'] <> "") {
4601
			$this->SetBuckets($q['buckets']);
4602
		} else {
4603
			$this->SetBuckets("");
4604
		}
4605
		if (isset($q['plr']) && $q['plr'] <> "") {
4606
			$this->SetPlr($q['plr']);
4607
		} else {
4608
			$this->SetPlr("");
4609
		}
4610
		if (isset($q['weight']) && $q['weight'] <> "") {
4611
			$this->SetWeight($q['weight']);
4612
		} else {
4613
			$this->SetWeight("");
4614
		}
4615
		if (isset($q['description']) && $q['description'] <> "") {
4616
			$this->SetDescription($q['description']);
4617
		} else {
4618
			$this->SetDescription("");
4619
		}
4620
		$this->SetEnabled($q['enabled']);
4621

    
4622
		/* Limiter patch */
4623
		if (isset($q['aqm']) && $q['aqm'] <> "") {
4624
				$this->SetAQM($q['aqm']);
4625
		} else {
4626
				$this->SetAQM('droptail');
4627
		}
4628
		// parse AQM arguments
4629
		$aqm_map = getAQMs();
4630
		$selectedParameters = $aqm_map[$this->getAQM()]["parameters"];
4631
		foreach ($selectedParameters as $key => $value) {
4632
			$config_key = 'param_' . $this->GetAQM() . '_' . $key;
4633
			if (isset($q[$config_key]) && $q[$config_key] <> "") {
4634
				$this->SetAQMParameter($key, $q[$config_key]);
4635
			} else {
4636
				$this->SetAQMParameter($key, $value["default"]);
4637
			}
4638
		}
4639

    
4640
		// ecn flag
4641
		$this->SetECN($q['ecn']);
4642
		/* End limiter patch */
4643
	}
4644

    
4645
	function build_tree() {
4646
		$parent =& $this->GetParent();
4647
		$tree = " <li><a href=\"firewall_shaper_vinterface.php?pipe=" . $parent->GetQname() ."&amp;queue=" . $this->GetQname() ."&amp;action=show\">";
4648
		$tree .= $this->GetQname() . "</a>";
4649
		$tree .= "</li>";
4650

    
4651
		return $tree;
4652
	}
4653

    
4654
	function build_rules() {
4655
		if ($this->GetEnabled() == "") {
4656
			return;
4657
		}
4658

    
4659
		$parent =& $this->GetParent();
4660
		$pfq_rule = "queue ". $this->GetNumber() . " config pipe " . $parent->GetNumber();
4661
		if ($this->GetQlimit()) {
4662
			$pfq_rule .= " queue " . $this->GetQlimit();
4663
		}
4664
		if ($this->GetWeight()) {
4665
			$pfq_rule .= " weight " . $this->GetWeight();
4666
		}
4667
		if ($this->GetBuckets()) {
4668
			$pfq_rule .= " buckets " . $this->GetBuckets();
4669
		}
4670
		$this->build_mask_rules($pfq_rule);
4671

    
4672
		/* Limiter patch */
4673
		$selectedAQM = getAQMs()[$this->getAQM()];
4674
		if ($selectedAQM) {
4675
			$pfq_rule .= " " . FormatParameters($selectedAQM["parameter_format"], $this->GetAQMParameters());
4676
			if ($selectedAQM["ecn"]) {
4677
				if ($this->getECN() == 'on') {
4678
					$pfq_rule .= ' ecn';
4679
				} else {
4680
					$pfq_rule .= ' noecn';
4681
				}
4682
			}
4683
		}
4684
		/* End patch */
4685

    
4686
		$pfq_rule .= "\n";
4687

    
4688
		return $pfq_rule;
4689
	}
4690

    
4691
	function build_javascript() {
4692
		return parent::build_javascript();
4693
	}
4694

    
4695
	function build_form() {
4696
		global $g, $config, $pipe, $action, $qname;
4697

    
4698
		//build list of schedules
4699
		$schedules = array();
4700
		$schedules[] = "none";//leave none to leave rule enabled all the time
4701
		if (is_array($config['schedules']) && is_array($config['schedules']['schedule'])) {
4702
			foreach ($config['schedules']['schedule'] as $schedule) {
4703
				if ($schedule['name'] <> "") {
4704
					$schedules[] = $schedule['name'];
4705
				}
4706
			}
4707
		}
4708

    
4709

    
4710
		$sform = new Form();
4711
		$sform->setAction("firewall_shaper.php");
4712
		$section = new Form_Section('Limiters');
4713

    
4714
		$section->addInput(new Form_Checkbox(
4715
			'enabled',
4716
			'Enable',
4717
			'Enable this queue',
4718
			($this->GetEnabled() == "on"),
4719
			'on'
4720
		));
4721

    
4722
		$section->addInput(new Form_Input(
4723
			'newname',
4724
			'*Name',
4725
			'text',
4726
			$this->GetQname()
4727
		));
4728

    
4729
		$section->addInput(new Form_Input(
4730
			'name',
4731
			null,
4732
			'hidden',
4733
			$this->GetQname()
4734
		));
4735

    
4736
		if ($this->GetNumber() > 0) {
4737
			$section->addInput(new Form_Input(
4738
				'number',
4739
				null,
4740
				'hidden',
4741
				$this->GetNumber()
4742
			));
4743
		}
4744

    
4745
		$mask = $this->GetMask();
4746

    
4747
		$section->addInput(new Form_Select(
4748
			'mask',
4749
			'Mask',
4750
			$mask['type'],
4751
			array('none' => gettext('None'), 'srcaddress' => gettext('Source addresses'), 'dstaddress' => gettext('Destination addresses'))
4752
		))->setHelp('If "source" or "destination" slots is chosen a dynamic pipe with the bandwidth, delay, packet loss ' .
4753
					'and queue size given above will be created for each source/destination IP address encountered, respectively. ' .
4754
					'This makes it possible to easily specify bandwidth limits per host.');
4755

    
4756
		$group = new Form_Group(null);
4757

    
4758
		$group->add(new Form_Select(
4759
			'maskbits',
4760
			null,
4761
			$mask['bits'],
4762
			array_combine(range(32, 1, -1), range(32, 1, -1))
4763
		))->setHelp('IPv4 mask bits%1$s%2$s', '<br />', '255.255.255.255/?');
4764

    
4765
		$group->add(new Form_Select(
4766
			'maskbitsv6',
4767
			null,
4768
			$mask['bitsv6'],
4769
			array_combine(range(128, 1, -1), range(128, 1, -1))
4770
		))->setHelp('IPv6 mask bits%1$s%2$s', '<br />', '<span style="font-family:consolas">ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/?</span>');
4771

    
4772
		$section->add($group);
4773

    
4774
		$section->addInput(new Form_Input(
4775
			'description',
4776
			'Description',
4777
			'text',
4778
			$this->GetDescription()
4779
		))->setHelp('A description may be entered here for administrative reference (not parsed).');
4780

    
4781
		$sform->add($section);
4782

    
4783
		/* Begin limiter patch */
4784
		$aqm_map = getAQMs();
4785

    
4786
		$section = new Form_Section('Queue');
4787
		$section->addInput(new Form_Select(
4788
				'aqm',
4789
				'Queue Management Algorithm',
4790
				$this->GetAQM(),
4791
				array_map_assoc(function ($k, $v) {
4792
					return [$k, $v["name"]];
4793
				}, $aqm_map)
4794
		))->setHelp('Active queue management (AQM) is the intelligent drop of network packets inside this limiter\'s queue, ' .
4795
						'when it becomes full or gets close to becoming full, with the goal of reducing ' .
4796
						'network congestion.');
4797

    
4798
		$section->addInput(new Form_StaticText(
4799
			'',
4800
			build_queue_params($aqm_map, $this->GetAQM(), $this->GetAQMParameters(), "aqm")
4801
		))->setHelp('Specifies the queue management algorithm parameters.');
4802

    
4803
		$section->addInput(new Form_Input(
4804
				'qlimit',
4805
				'Queue length',
4806
				'number',
4807
				$this->GetQlimit()
4808
		))->setHelp('Specifies the length of this queue, which the AQM is responsible for.  This field may be left empty.');
4809

    
4810
		$section->addInput(new Form_Checkbox(
4811
			'ecn',
4812
			'ECN',
4813
			'Enable Explicit Congestion Notification (ECN)',
4814
			($this->GetECN() == "on"),
4815
			'on'
4816
		))->setHelp('ECN sets a reserved TCP flag when the queue is nearing or exceeding capacity. Not all AQMs or schedulers support this.');
4817

    
4818
		$sform->add($section);
4819
		/* End limiter patch */
4820

    
4821
		$section = new Form_Section('Advanced Options');
4822

    
4823
		$section->addInput(new Form_Input(
4824
			'weight',
4825
			'Weight',
4826
			'number',
4827
			$this->GetWeight(),
4828
			['min' => '1', 'max' => '100']
4829
		))->setHelp('For queues under the same parent this specifies the share that a queue gets(values range from 1 to 100),' .
4830
					' it can be left blank otherwise.');
4831

    
4832
		$section->addInput(new Form_Input(
4833
			'plr',
4834
			'Packet Loss Rate',
4835
			'number',
4836
			$this->GetPlr(),
4837
			['step' => '0.001', 'min' => '0.000']
4838
		))->setHelp('In most cases, zero (0) should be specified here (or leave the field empty). ' .
4839
					'A value of 0.001 means one packet in 1000 gets dropped');
4840

    
4841
		$section->addInput(new Form_Input(
4842
			'buckets',
4843
			'Bucket size (slots)',
4844
			'number',
4845
			$this->GetBuckets()
4846
		))->setHelp('In most cases, this field should be left empty. It increases the hash size set');
4847

    
4848
		$section->addInput(new Form_Input(
4849
			'pipe',
4850
			null,
4851
			'hidden',
4852
			$this->GetPipe()
4853
		));
4854

    
4855
		$sform->add($section);
4856

    
4857
		return($sform);
4858
	}
4859

    
4860
	function update_dn_data(&$data) {
4861
		$this->ReadConfig($data);
4862
	}
4863

    
4864
	function wconfig() {
4865
		$cflink =& get_dn_reference_to_me_in_config($this->GetLink());
4866
		if (!is_array($cflink)) {
4867
			$cflink = array();
4868
		}
4869
		$cflink['name'] = $this->GetQname();
4870
		$cflink['number'] = $this->GetNumber();
4871
		$cflink['qlimit'] = $this->GetQlimit();
4872
		$cflink['description'] = $this->GetDescription();
4873
		$cflink['weight'] = $this->GetWeight();
4874
		$cflink['enabled'] = $this->GetEnabled();
4875
		$cflink['buckets'] = $this->GetBuckets();
4876
		$mask = $this->GetMask();
4877
		$cflink['mask'] = $mask['type'];
4878
		$cflink['maskbits'] = $mask['bits'];
4879
		$cflink['maskbitsv6'] = $mask['bitsv6'];
4880

    
4881
		/* Limiter queue patch */
4882
		$cflink['aqm'] = $this->GetAQM();
4883
		$aqm_map = GetAQMs();
4884
		$selectedParameters = $aqm_map[$this->getAQM()]["parameters"];
4885
		foreach ($selectedParameters as $key => $value) {
4886
			$config_key = 'param_' . $this->GetAQM() . '_' . $key;
4887
			$cflink[$config_key] = $this->GetAQMParameter($key);
4888
		}
4889
		$cflink['ecn'] = $this->GetECN();
4890
		/* End limiter queue patch */
4891
	}
4892
}
4893

    
4894
function get_dummynet_name_list() {
4895

    
4896
	$dn_name_list =& get_unique_dnqueue_list();
4897
	$dn_name = array();
4898
	if (is_array($dn_name_list)) {
4899
		foreach ($dn_name_list as $key => $value) {
4900
			$dn_name[] = $key;
4901
		}
4902
	}
4903

    
4904
	return $dn_name;
4905

    
4906
}
4907

    
4908
function get_altq_name_list() {
4909
	$altq_name_list =& get_unique_queue_list();
4910
	$altq_name = array();
4911
	if (is_array($altq_name_list)) {
4912
		foreach ($altq_name_list as $key => $aqobj) {
4913
			$altq_name[] = $key;
4914
		}
4915
	}
4916

    
4917
	return $altq_name;
4918
}
4919

    
4920
/*
4921
 * XXX: TODO Make a class shaper to hide all these functions
4922
 * from the global namespace.
4923
 */
4924

    
4925
/*
4926
 * This is a layer violation but for now there is no way
4927
 * I can find to properly do this with PHP.
4928
 */
4929
function altq_get_default_queue($interface) {
4930
	global $altq_list_queues;
4931

    
4932
	$altq_tmp = $altq_list_queues[$interface];
4933
	if ($altq_tmp) {
4934
		return $altq_tmp->GetDefaultQueuePresent();
4935
	} else {
4936
		return false;
4937
	}
4938
}
4939

    
4940
function altq_check_default_queues() {
4941
	global $altq_list_queues;
4942

    
4943
	$count = 0;
4944
	if (is_array($altq_list_queues)) {
4945
		foreach ($altq_list_queues as $altq) {
4946
			if ($altq->GetDefaultQueuePresent()) {
4947
				$count++;
4948
			}
4949
		}
4950
	}
4951
	else {
4952
		$count++;
4953
	}
4954

    
4955
	return 0;
4956
}
4957

    
4958
function &get_unique_queue_list() {
4959
	global $altq_list_queues;
4960

    
4961
	$qlist = array();
4962
	if (is_array($altq_list_queues)) {
4963
		foreach ($altq_list_queues as $altq) {
4964
			if ($altq->GetEnabled() == "") {
4965
				continue;
4966
			}
4967
			$tmplist =& $altq->get_queue_list();
4968
			foreach ($tmplist as $qname => $link) {
4969
				if ($link->GetEnabled() <> "") {
4970
					$qlist[$qname] = $link;
4971
				}
4972
			}
4973
		}
4974
	}
4975
	return $qlist;
4976
}
4977

    
4978
function &get_unique_dnqueue_list() {
4979
	global $dummynet_pipe_list;
4980

    
4981
	$qlist = array();
4982
	if (is_array($dummynet_pipe_list)) {
4983
		foreach ($dummynet_pipe_list as $dn) {
4984
			if ($dn->GetEnabled() == "") {
4985
				continue;
4986
			}
4987
			$tmplist =& $dn->get_queue_list();
4988
			foreach ($tmplist as $qname => $link) {
4989
				$qlist[$qname] = $link;
4990
			}
4991
		}
4992
	}
4993
	return $qlist;
4994
}
4995

    
4996
function ref_on_altq_queue_list($parent, $qname) {
4997
	if (isset($GLOBALS['queue_list'][$qname])) {
4998
		$GLOBALS['queue_list'][$qname]++;
4999
	} else {
5000
		$GLOBALS['queue_list'][$qname] = 1;
5001
	}
5002

    
5003
	unref_on_altq_queue_list($parent);
5004
}
5005

    
5006
function unref_on_altq_queue_list($qname) {
5007
	$GLOBALS['queue_list'][$qname]--;
5008
	if ($GLOBALS['queue_list'][$qname] <= 1) {
5009
		unset($GLOBALS['queue_list'][$qname]);
5010
	}
5011
}
5012

    
5013
function read_altq_config() {
5014
	global $altq_list_queues, $config;
5015
	$path = array();
5016

    
5017
	if (!is_array($config['shaper'])) {
5018
		$config['shaper'] = array();
5019
	}
5020
	if (!is_array($config['shaper']['queue'])) {
5021
		$config['shaper']['queue'] = array();
5022
	}
5023
	$a_int = &$config['shaper']['queue'];
5024

    
5025
	$altq_list_queues = array();
5026

    
5027
	if (!is_array($config['shaper']['queue'])) {
5028
		return;
5029
	}
5030

    
5031
	foreach ($a_int as $key => $conf) {
5032
		$int = $conf['interface'];
5033
		$root = new altq_root_queue();
5034
		$root->SetInterface($int);
5035
		$altq_list_queues[$root->GetInterface()] = $root;
5036
		$root->ReadConfig($conf);
5037
		array_push($path, $key);
5038
		$root->SetLink($path);
5039
		if (is_array($conf['queue'])) {
5040
			foreach ($conf['queue'] as $key1 => $q) {
5041
				array_push($path, $key1);
5042
				/*
5043
				 * XXX: we completely ignore errors here but anyway we must have
5044
				 *	checked them before so no harm should be come from this.
5045
				 */
5046
				$root->add_queue($root->GetInterface(), $q, $path, $input_errors);
5047
				array_pop($path);
5048
			}
5049
		}
5050
		array_pop($path);
5051
	}
5052
}
5053

    
5054
function read_dummynet_config() {
5055
	global $dummynet_pipe_list, $config;
5056
	$path = array();
5057

    
5058
	if (!is_array($config['dnshaper'])) {
5059
		$config['dnshaper'] = array();
5060
	}
5061
	if (!is_array($config['dnshaper']['queue'])) {
5062
		$config['dnshaper']['queue'] = array();
5063
	}
5064
	$a_int = &$config['dnshaper']['queue'];
5065

    
5066
	$dummynet_pipe_list = array();
5067

    
5068
	if (!is_array($config['dnshaper']['queue']) ||
5069
	    !count($config['dnshaper']['queue'])) {
5070
		return;
5071
	}
5072

    
5073
	foreach ($a_int as $key => $conf) {
5074
		if (empty($conf['name'])) {
5075
			continue; /* XXX: grrrrrr at php */
5076
		}
5077
		$root = new dnpipe_class();
5078
		$root->ReadConfig($conf);
5079
		$dummynet_pipe_list[$root->GetQname()] = $root;
5080
		array_push($path, $key);
5081
		$root->SetLink($path);
5082
		if (is_array($conf['queue'])) {
5083
			foreach ($conf['queue'] as $key1 => $q) {
5084
				array_push($path, $key1);
5085
				/*
5086
				 * XXX: we completely ignore errors here but anyway we must have
5087
				 *	checked them before so no harm should be come from this.
5088
				 */
5089
				$root->add_queue($root->GetQname(), $q, $path, $input_errors);
5090
				array_pop($path);
5091
			}
5092
		}
5093
		array_pop($path);
5094
	}
5095
}
5096

    
5097
function get_interface_list_to_show() {
5098
	global $altq_list_queues, $config;
5099
	global $shaperIFlist;
5100

    
5101
	$tree = "";
5102
	foreach ($shaperIFlist as $shif => $shDescr) {
5103
		if ($altq_list_queues[$shif]) {
5104
			continue;
5105
		} else {
5106
			if (!is_altq_capable(get_real_interface($shif))) {
5107
				continue;
5108
			}
5109
			$tree .= " <li><a href=\"firewall_shaper.php?interface=".$shif."&amp;action=add\">".$shDescr."</a></li>";
5110
		}
5111
	}
5112

    
5113
	return $tree;
5114
}
5115

    
5116
function filter_generate_altq_queues() {
5117
	global $altq_list_queues;
5118

    
5119
	read_altq_config();
5120

    
5121
	$altq_rules = "";
5122
	foreach ($altq_list_queues as $altq) {
5123
		$altq_rules .= $altq->build_rules();
5124
	}
5125

    
5126
	return $altq_rules;
5127
}
5128

    
5129
function dnqueue_find_nextnumber() {
5130
	global $dummynet_pipe_list;
5131

    
5132
	$dnused = array();
5133
	if (is_array($dummynet_pipe_list)) {
5134
		foreach ($dummynet_pipe_list as $dn) {
5135
			$tmplist =& $dn->get_queue_list();
5136
			foreach ($tmplist as $qname => $link) {
5137
				if ($link[0] == "?") {
5138
					$dnused[$qname] = substr($link, 1);
5139
				}
5140
			}
5141
		}
5142
	}
5143

    
5144
	sort($dnused, SORT_NUMERIC);
5145
	$dnnumber = 0;
5146
	$found = false;
5147
	foreach ($dnused as $dnnum) {
5148
		if (($dnnum - $dnnumber) > 1) {
5149
			$dnnumber = $dnnum - 1;
5150
			$found = true;
5151
			break;
5152
		} else {
5153
			$dnnumber = $dnnum;
5154
		}
5155
	}
5156

    
5157
	if ($found == false) {
5158
		$dnnumber++;
5159
	}
5160

    
5161
	unset($dnused, $dnnum, $found);
5162
	return $dnnumber;
5163
}
5164

    
5165
function dnpipe_find_nextnumber() {
5166
	global $dummynet_pipe_list;
5167

    
5168
	$dnused = array();
5169
	foreach ($dummynet_pipe_list as $dn) {
5170
		$dnused[] = $dn->GetNumber();
5171
	}
5172

    
5173
	sort($dnused, SORT_NUMERIC);
5174
	$dnnumber = 0;
5175
	$found = false;
5176
	foreach ($dnused as $dnnum) {
5177
		if (($dnnum - $dnnumber) > 1) {
5178
			$dnnumber = $dnnum - 1;
5179
			$found = true;
5180
			break;
5181
		} else {
5182
			$dnnumber = $dnnum;
5183
		}
5184
	}
5185

    
5186
	if ($found == false) {
5187
		$dnnumber++;
5188
	}
5189

    
5190
	unset($dnused, $dnnum, $found);
5191
	return $dnnumber;
5192
}
5193

    
5194
function filter_generate_dummynet_rules() {
5195
	global $g, $dummynet_pipe_list;
5196

    
5197
	read_dummynet_config();
5198

    
5199
	$dn_rules = "";
5200
	$max_qlimit = "100"; // OS default
5201
	foreach ($dummynet_pipe_list as $dn) {
5202
		$dn_rules .= $dn->build_rules();
5203
		$this_qlimit = $dn->GetQlimit();
5204
		if ($this_qlimit > $max_qlimit) {
5205
			$max_qlimit = $this_qlimit;
5206
		}
5207
	}
5208
	if (!is_numericint($max_qlimit)) {
5209
		$max_qlimit = "100";
5210
	}
5211
	if (!empty($dn_rules)) {
5212
		if (!is_module_loaded("dummynet.ko")) {
5213
			mwexec("/sbin/kldload dummynet");
5214
		}
5215
		set_sysctl(array(
5216
				"net.inet.ip.dummynet.io_fast" => "1",
5217
				"net.inet.ip.dummynet.hash_size" => "256",
5218
				"net.inet.ip.dummynet.pipe_slot_limit" => $max_qlimit
5219
		));
5220
		file_put_contents("{$g['tmp_path']}/rules.limiter", $dn_rules);
5221
		mwexec("/sbin/ipfw {$g['tmp_path']}/rules.limiter");
5222
	}
5223
}
5224

    
5225
function build_iface_without_this_queue($iface, $qname) {
5226
	global $g, $altq_list_queues;
5227
	global $shaperIFlist;
5228

    
5229
	$altq =& $altq_list_queues[$iface];
5230

    
5231
	if ($altq) {
5232
		$scheduler = $altq->GetScheduler();
5233
	}
5234

    
5235
	$form = '<dl class="dl-horizontal">';
5236

    
5237
	$form .= '	<dt>';
5238
	$form .= '		<a href="firewall_shaper.php?interface=' . $iface . '&amp;queue=' . $iface . '&amp;action=show">' . $shaperIFlist[$iface] . '</a>';
5239
	$form .= '	</dt>';
5240
	$form .= '	<dd>';
5241
	$form .=		$scheduler;
5242
	$form .= '	</dd>';
5243

    
5244
	$form .= '	<dt>';
5245
	$form .= 'Clone';
5246
	$form .= '	</dt>';
5247
	$form .= '	<dd>';
5248
	$form .= '<a class="btn btn-info btn-xs" href="firewall_shaper_queues.php?interface=';
5249
	$form .= $iface . '&amp;queue=';
5250
	$form .= $qname . '&amp;action=add">';
5251
	$form .= '<i class="fa fa-clone icon-embed-btn"></i>';
5252
	$form .= gettext("Clone Shaper to this Interface") . '</a>';
5253
	$form .= '	</dd>';
5254

    
5255
	$form .= '</dl>';
5256

    
5257
	return $form;
5258

    
5259
}
5260

    
5261
$default_shaper_msg = sprintf(gettext("Welcome to the %s Traffic Shaper."), $g['product_name']) . "<br />";
5262
$dn_default_shaper_msg = $default_shaper_msg;
5263

    
5264
$shaper_msg = gettext("The tree on the left navigates through the %s.");
5265
$default_shaper_msg .= sprintf($shaper_msg, gettext("queues")) . "<br />";
5266
$dn_default_shaper_msg .= sprintf($shaper_msg, gettext("limiters")) . "<br />";
5267

    
5268
$shaper_msg = gettext("Buttons at the bottom represent %s actions and are activated accordingly.");
5269
$default_shaper_msg .= sprintf($shaper_msg, gettext("queue"));
5270
$dn_default_shaper_msg .= sprintf($shaper_msg, gettext("limiter"));
5271

    
5272
// Check to see if the specified interface has a queue configured
5273
function interface_has_queue($if) {
5274
	global $config;
5275

    
5276
	if ($config['shaper']) {
5277
		foreach ($config['shaper']['queue'] as $queue) {
5278
			if ($queue['interface'] === $if) {
5279
				return true;
5280
			}
5281
		}
5282
	}
5283

    
5284
	return false;
5285
}
5286
?>
(47-47/60)