Project

General

Profile

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

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

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

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

    
177
	$selectedAlgorithm = $array[$selected];
178

    
179
	if ($selectedAlgorithm) {
180
		$form .= '<span class="help-block">' . gettext($selectedAlgorithm['description']) . '</span><br/>';
181
	}
182

    
183
	$parameters = $selectedAlgorithm["parameters"];
184

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

    
200
		foreach ($parameters as $key => $value) {
201
			$form .= '<tr>';
202

    
203
			// get current value, or default.
204
			$currentValue = $params[$key];
205
			if (!$currentValue || $currentValue == '') {
206
				$currentValue = $value["default"]; // default to default
207
			}
208

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

    
214
			$form .= '</tr>';
215
		}
216

    
217
		$form .= "</tbody></table></div><br />";
218
	}
219

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

    
225
	return($form);
226
}
227
function FormatParameters($format_string, $params) {
228
	return vsprintf($format_string, $params);
229
}
230
/* End limiter patch */
231

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

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

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

    
310
/*
311
 * I admit :) this is derived from xmlparse.inc StartElement()
312
 */
313
function &get_reference_to_me_in_config(&$mypath) {
314
	global $config;
315

    
316
	init_config_arr(array('shaper'));
317
	$ptr = &$config['shaper'];
318
	foreach ($mypath as $indeks) {
319
		if (!is_array($ptr)) {
320
			$ptr = array();
321
		}
322
		if (!is_array($ptr['queue'])) {
323
			$ptr['queue'] = array();
324
		}
325
		if (!is_array($ptr['queue'][$indeks])) {
326
			$ptr['queue'][$indeks] = array();
327
		}
328
		$ptr = &$ptr['queue'][$indeks];
329
	}
330

    
331
	return $ptr;
332
}
333

    
334
function unset_object_by_reference(&$mypath) {
335
	global $config;
336

    
337
	init_config_arr(array('shaper'));
338
	$ptr = &$config['shaper'];
339
	for ($i = 0; $i < count($mypath) - 1; $i++) {
340
		$ptr = &$ptr['queue'][$mypath[$i]];
341
	}
342
	unset($ptr['queue'][$mypath[$i]]);
343
}
344

    
345
function &get_dn_reference_to_me_in_config(&$mypath) {
346
	global $config;
347

    
348
	init_config_arr(array('dnshaper'));
349
	$ptr = &$config['dnshaper'];
350
	foreach ($mypath as $indeks) {
351
		$ptr = &$ptr['queue'][$indeks];
352
	}
353

    
354
	return $ptr;
355
}
356

    
357
function unset_dn_object_by_reference(&$mypath) {
358
	global $config;
359

    
360
	init_config_arr(array('dnshaper'));
361
	$ptr = &$config['dnshaper'];
362
	for ($i = 0; $i < count($mypath) - 1; $i++) {
363
		$ptr = &$ptr['queue'][$mypath[$i]];
364
	}
365
	unset($ptr['queue'][$mypath[$i]]);
366
}
367

    
368
function clean_child_queues($type, $mypath) {
369
	$ref = &get_reference_to_me_in_config($mypath);
370

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

    
446
function get_bandwidthtype_scale($type) {
447
	switch ($type) {
448
		case "Gb":
449
			$factor = 1024 * 1024 * 1024;
450
			break;
451
		case "Mb":
452
			$factor = 1024 * 1024;
453
			break;
454
		case "Kb":
455
			$factor = 1024;
456
			break;
457
		case "b":
458
		default:
459
			$factor = 1;
460
			break;
461
	}
462
	return intval($factor);
463
}
464

    
465
function get_bandwidth($bw, $scale, $obj) {
466
	$bw = (int) $bw;
467
	$pattern= "/(b|Kb|Mb|Gb|%)/";
468
	if (!preg_match($pattern, $scale, $match))
469
		return 0;
470

    
471
	switch ($match[1]) {
472
		case '%':
473
			$objbw = ($bw / 100) * get_queue_bandwidth($obj);
474
			break;
475
		default:
476
			$objbw = $bw * get_bandwidthtype_scale($scale);
477
			break;
478
	}
479

    
480
	return floatval($objbw);
481
}
482

    
483
/*
484
 * XXX - unused
485
 *
486
function get_hfsc_bandwidth($object, $bw) {
487
	$pattern= "/[0-9]+/";
488
	if (preg_match($pattern, $bw, $match)) {
489
		$bw_1 = $match[1];
490
	} else {
491
		return 0;
492
	}
493
	$pattern= "/(b|Kb|Mb|Gb|%)/";
494
	if (preg_match($pattern, $bw, $match)) {
495
		switch ($match[1]) {
496
			case '%':
497
				$bw_1 = ($bw_1 / 100) * get_interface_bandwidth($object);
498
				break;
499
			default:
500
				$bw_1 = $bw_1 * get_bandwidthtype_scale($match[0]);
501
				break;
502
		}
503
		return floatval($bw_1);
504
	} else {
505
		return 0;
506
	}
507
}
508
*/
509

    
510
function get_queue_bandwidth($obj) {
511
	$bw = $obj->GetBandwidth();
512
	$scale = $obj->GetBwscale();
513

    
514
	$pattern= "/(b|Kb|Mb|Gb|%)/";
515
	if (!preg_match($pattern, $scale, $match))
516
		return 0;
517

    
518
	switch ($match[1]) {
519
		case '%':
520
			$objbw = ($bw / 100) * get_queue_bandwidth($obj->GetParent());
521
			break;
522
		default:
523
			$objbw = $bw * get_bandwidthtype_scale($scale);
524
			break;
525
	}
526

    
527
	return floatval($objbw);
528
}
529

    
530
function get_interface_bandwidth($object) {
531
	global $altq_list_queues;
532

    
533
	$int = $object->GetInterface();
534
	if (isset($altq_list_queues[$int])) {
535
		$altq = &$altq_list_queues[$int];
536
		$bw_3 = $altq->GetBandwidth();
537
		$bw_3 = $bw_3 * get_bandwidthtype_scale($altq->GetBwscale());
538
		return floatval($bw_3);
539
	} else {
540
		return 0;
541
	}
542
}
543

    
544
/*
545
 * This is duplicated here since we cannot include guiconfig.inc.
546
 * Including it makes all stuff break.
547
 */
548
function shaper_do_input_validation($postdata, $reqdfields, $reqdfieldsn, $input_errors) {
549

    
550
	/* check for bad control characters */
551
	foreach ($postdata as $pn => $pd) {
552
		if (is_string($pd) && preg_match("/[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f]/", $pd)) {
553
			$input_errors[] = sprintf(gettext("The field '%s' contains invalid characters."), $pn);
554
		}
555
	}
556

    
557
	for ($i = 0; $i < count($reqdfields); $i++) {
558
		if ($postdata[$reqdfields[$i]] == "") {
559
			$input_errors[] = sprintf(gettext("The field '%s' is required."), $reqdfieldsn[$i]);
560
		}
561
	}
562
}
563

    
564
function cleanup_queue_from_rules($queue) {
565
	global $config;
566

    
567
	foreach ($config['filter']['rule'] as $rule) {
568
		if ($rule['defaultqueue'] == $queue) {
569
			unset($rule['defaultqueue']);
570
		}
571
		if ($rule['ackqueue'] == $queue) {
572
			unset($rule['ackqueue']);
573
		}
574
	}
575
}
576

    
577
function cleanup_dnqueue_from_rules($queue) {
578
	global $config;
579

    
580
	foreach ($config['filter']['rule'] as $rule) {
581
		if ($rule['dnpipe'] == $queue) {
582
			unset($rule['dnpipe']);
583
		}
584
		if ($rule['pdnpipe'] == $queue) {
585
			unset($rule['pdnpipe']);
586
		}
587
	}
588
}
589

    
590
class altq_root_queue {
591
	var $interface;
592
	var $tbrconfig ;
593
	var $bandwidth;
594
	var $bandwidthtype; /* b, Kb, Mb, Gb, % */
595
	var $scheduler;
596
	var $qlimit;
597
	var $queues = array();
598
	var $qenabled = false;
599
	var $link;
600

    
601
	/* Accessor functions */
602
	function GetDefaultQueuePresent() {
603
		if (!empty($this->queues)) {
604
			foreach ($this->queues as $q) {
605
				if ($q->GetDefault()) {
606
					return true;
607
				}
608
			}
609
		}
610

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

    
678
	function GetBwscaleText() {
679
		switch ($this->bandwidthtype) {
680
			case "b":
681
				$bwscaletext = "Bit/s";
682
				break;
683
			case "Kb":
684
				$bwscaletext = "Kbit/s";
685
				break;
686
			case "Mb":
687
				$bwscaletext = "Mbit/s";
688
				break;
689
			case "Gb":
690
				$bwscaletext = "Gbit/s";
691
				break;
692
			default:
693
				/* For others that do not need translating like % */
694
				$bwscaletext = $this->bandwidthtype;
695
				break;
696
		}
697
		return $bwscaletext;
698
	}
699

    
700
	function CheckBandwidth($bw, $bwtype) {
701
		$sum = $this->GetTotalBw();
702
		if ($sum > $bw * get_bandwidthtype_scale($bwtype))
703
			return 1;
704
		foreach ($this->queues as $q) {
705
			if ($q->CheckBandwidth(0, ''))
706
				return 1;
707
		}
708

    
709
		return 0;
710
	}
711

    
712
	function GetTotalBw($qignore = NULL) {
713
		$sum = 0;
714
		foreach ($this->queues as $q) {
715
			if ($qignore != NULL && $qignore == $q)
716
				continue;
717
			$sum += get_bandwidth($q->GetBandwidth(), $q->GetBwscale(), $this);
718
		}
719

    
720
		return $sum;
721
	}
722

    
723
	function validate_input($data, &$input_errors) {
724

    
725
		$reqdfields[] = "bandwidth";
726
		$reqdfieldsn[] = gettext("Bandwidth");
727
		$reqdfields[] = "bandwidthtype";
728
		$reqdfieldsn[] = gettext("Bandwidthtype");
729

    
730
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
731

    
732
		if (!isset($data['bandwidth']) || strlen($data['bandwidth']) == 0) {
733
			$input_errors[] = gettext("Bandwidth must be set.  This is usually the interface speed.");
734
		}
735
		if ($data['bandwidth'] && (!is_numeric($data['bandwidth']))) {
736
			$input_errors[] = gettext("Bandwidth must be an integer.");
737
		}
738
		if ($data['bandwidth'] < 0) {
739
			$input_errors[] = gettext("Bandwidth cannot be negative.");
740
		}
741
		if ($data['bandwidthtype'] == "%") {
742
			if ($data['bandwidth'] > 100 || $data['bandwidth'] < 0) {
743
				$input_errors[] = gettext("Bandwidth in percentage should be between 1 and 100.");
744
			}
745
		}
746
		if ($this->CheckBandwidth($data['bandwidth'], $data['bandwidthtype']))
747
			$input_errors[] = "The sum of child bandwidth is higher than parent.";
748

    
749
		if ($data['qlimit'] && (!is_numeric($data['qlimit']))) {
750
			$input_errors[] = gettext("Qlimit must be an integer.");
751
		}
752
		if ($data['qlimit'] < 0) {
753
			$input_errors[] = gettext("Qlimit must be positive.");
754
		}
755
		if ($data['tbrconfig'] && (!is_numeric($data['tbrconfig']))) {
756
			$input_errors[] = gettext("Tbrsize must be an integer.");
757
		}
758
		if ($data['tbrconfig'] < 0) {
759
			$input_errors[] = gettext("Tbrsize must be positive.");
760
		}
761
	}
762

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

    
798
	function copy_queue($interface, &$cflink) {
799
		$cflink['interface'] = $interface;
800
		$cflink['name'] = $interface;
801
		$cflink['scheduler'] = $this->GetScheduler();
802
		$cflink['bandwidth'] = $this->GetBandwidth();
803
		$cflink['bandwidthtype'] = $this->GetBwscale();
804
		$cflink['qlimit'] = $this->GetQlimit();
805
		$cflink['tbrconfig'] = $this->GetTbrConfig();
806
		$cflink['enabled'] = $this->GetEnabled();
807
		if (is_array($this->queues)) {
808
			$cflink['queue'] = array();
809
			foreach ($this->queues as $q) {
810
				$cflink['queue'][$q->GetQname()] = array();
811
				$q->copy_queue($interface, $cflink['queue'][$q->GetQname()]);
812
			}
813
		}
814
	}
815

    
816
	function &get_queue_list(&$q = null) {
817
		$qlist = array();
818

    
819
		//$qlist[$this->GetQname()] = & $this;
820
		if (is_array($this->queues)) {
821
			foreach ($this->queues as $queue) {
822
				$queue->get_queue_list($qlist);
823
			}
824
		}
825
		return $qlist;
826
	}
827

    
828
	function &add_queue($interface, &$queue, &$path, &$input_errors) {
829

    
830
		if (!is_array($this->queues)) {
831
			$this->queues = array();
832
		}
833

    
834
		switch ($this->GetScheduler()) {
835
			case "PRIQ":
836
				$__tmp_q = new priq_queue(); $q =& $__tmp_q;
837
				break;
838
			case "HFSC":
839
				$__tmp_q = new hfsc_queue(); $q =& $__tmp_q;
840
				break;
841
			case "CBQ":
842
				$__tmp_q = new cbq_queue(); $q =& $__tmp_q;
843
				break;
844
			case "FAIRQ":
845
				$__tmp_q = new fairq_queue(); $q =& $__tmp_q;
846
				break;
847
			default:
848
				/* XXX: but should not happen anyway */
849
				return;
850
				break;
851
		}
852
		$q->SetLink($path);
853
		$q->SetInterface($this->GetInterface());
854
		$q->SetEnabled("on");
855
		$q->SetParent($this);
856
		$q->ReadConfig($queue);
857
		$q->validate_input($queue, $input_errors);
858

    
859
		$this->queues[$q->GetQname()] = &$q;
860
		ref_on_altq_queue_list($this->GetQname(), $q->GetQname());
861
		if (is_array($queue['queue'])) {
862
			foreach ($queue['queue'] as $key1 => $que) {
863
				array_push($path, $key1);
864
				$q->add_queue($q->GetInterface(), $que, $path, $input_errors);
865
				array_pop($path);
866
			}
867
		}
868

    
869
		return $q;
870
	}
871

    
872
	/* interface here might be optional */
873
	function &find_queue($interface, $qname) {
874
		if ($qname == $this->GetQname()) {
875
			return $this;
876
		}
877
		foreach ($this->queues as $q) {
878
			$result =& $q->find_queue("", $qname);
879
			if ($result) {
880
				return $result;
881
			}
882
		}
883
	}
884

    
885
	function &find_parentqueue($interface, $qname) {
886
		if ($qname == $interface) {
887
			$result = NULL;
888
		} else if ($this->queues[$qname]) {
889
			$result = $this;
890
		} else if ($this->GetScheduler() <> "PRIQ") {
891
			foreach ($this->queues as $q) {
892
				$result = $q->find_parentqueue("", $qname);
893
				if ($result) {
894
					return $result;
895
				}
896
			}
897
		}
898
	}
899

    
900
	function build_tree() {
901
		global $shaperIFlist;
902

    
903
		$tree = " <li><a href=\"firewall_shaper.php?interface=".$this->GetInterface()."&amp;queue=". $this->GetInterface()."&amp;action=show";
904
		$tree .= "\">" . $shaperIFlist[$this->GetInterface()] . "</a>";
905
		if (is_array($this->queues)) {
906
			$tree .= "<ul>";
907
			foreach ($this->queues as $q) {
908
				$tree .= $q->build_tree();
909
			}
910
			$tree .= "</ul>";
911
		}
912
		$tree .= "</li>";
913
		return $tree;
914
	}
915

    
916
	function delete_queue() {
917
		foreach ($this->queues as $q)
918
			$q->delete_queue();
919
		unset_object_by_reference($this->GetLink());
920
	}
921

    
922
	function delete_all() {
923
		if (count($this->queues)) {
924
			foreach ($this->queues as $q) {
925
				$q->delete_all();
926
				unset_object_by_reference($q->GetLink());
927
				unset($q);
928
			}
929
			unset($this->queues);
930
		}
931
	}
932

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

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

    
1002
			$rules .= " queue";
1003
		}
1004

    
1005
		$rules .= " \n";
1006
		return $rules;
1007
	}
1008

    
1009
	function build_javascript() {
1010
		$javascript = "<script type=\"text/javascript\">";
1011
		$javascript .= "//<![CDATA[\n";
1012
		$javascript .= "function mySuspend() {";
1013
		$javascript .= "if (document.layers && document.layers['shaperarea'] != null) ";
1014
		$javascript .= "document.layers['shaperarea'].visibility = 'hidden'; ";
1015
		$javascript .= "else if (document.all)";
1016
		$javascript .= "document.all['shaperarea'].style.visibility = 'hidden';";
1017
		$javascript .= "}";
1018

    
1019
		$javascript .= "function myResume() {";
1020
		$javascript .= "if (document.layers && document.layers['shaperarea'] != null) ";
1021
		$javascript .= "document.layers['shaperarea'].visibility = 'visible';";
1022
		$javascript .= "else if (document.all) ";
1023
		$javascript .= "document.all['shaperarea'].style.visibility = 'visible';";
1024
		$javascript .= "}";
1025
		$javascript .= "//]]>";
1026
		$javascript .= "</script>";
1027

    
1028
		return $javascript;
1029
	}
1030

    
1031
	function build_shortform() {
1032
		global $g;
1033

    
1034
		$altq =& $this;
1035

    
1036
		if ($altq) {
1037
			$scheduler = ": " . $altq->GetScheduler();
1038
		}
1039

    
1040
		$form = '<dl class="dl-horizontal">';
1041
		$form .= '	<dt>';
1042
		$form .= '		<a href="firewall_shaper.php?interface=' . $this->GetInterface() . '&amp;queue=' . $this->GetQname() . '&amp;action=show">' . $shaperIFlist[$this->GetInterface()] . '</a>';
1043
		$form .= '	</dt>';
1044
		$form .= '	<dd>';
1045
		$form .=		$scheduler;
1046
		$form .= '	</dd>';
1047

    
1048
		$form .= '	<dt>';
1049
		$form .=		'Bandwidth';
1050
		$form .= '	</dt>';
1051
		$form .= '	<dd>';
1052
		$form .=		$this->GetBandwidth() . '&nbsp;' . $this->GetBwscaleText();
1053
		$form .= '	</dd>';
1054

    
1055
		$form .= '	<dt>';
1056
		$form .= 'Disable';
1057
		$form .= '	<dt>';
1058
		$form .= '	<dd>';
1059

    
1060
		$form .= '<a class="btn btn-danger btn-xs" href="firewall_shaper_queues.php?interface=';
1061
		$form .= $this->GetInterface() . '&amp;queue=';
1062
		$form .= $this->GetQname() . '&amp;action=delete">';
1063
		$form .= '<i class="fa fa-trash icon-embed-btn"></i>';
1064
		$form .= gettext("Remove shaper from this interface") . '</a>';
1065

    
1066
		$form .= '	</dd>';
1067

    
1068
		$form .= '</dl>';
1069

    
1070
		return $form;
1071

    
1072
	}
1073

    
1074
	/*
1075
	 * For requesting the parameters of the root queues
1076
	 * to the user like the traffic wizard does.
1077
	 */
1078
	function build_form() {
1079

    
1080
		$sform = new Form();
1081

    
1082
		$sform->setAction("firewall_shaper.php");
1083

    
1084
		$section = new Form_Section(null);
1085

    
1086
		$section->addInput(new Form_Checkbox(
1087
			'enabled',
1088
			'Enable/Disable',
1089
			'Enable/disable discipline and its children',
1090
			($this->GetEnabled() == "on"),
1091
			'on'
1092
		));
1093

    
1094
		$section->addInput(new Form_StaticText(
1095
			'*Name',
1096
			$this->GetQname()
1097
		));
1098

    
1099
		$section->addInput(new Form_Select(
1100
			'scheduler',
1101
			'Scheduler Type',
1102
			$this->GetScheduler(),
1103
			array('HFSC' => 'HFSC',
1104
				  'CBQ' => 'CBQ',
1105
				  'FAIRQ' => 'FAIRQ',
1106
				  'CODELQ' => 'CODELQ',
1107
				  'PRIQ' => 'PRIQ')
1108
		))->setHelp('Changing this changes all child queues! Beware information can be lost.');
1109

    
1110
		$group = new Form_group('Bandwidth');
1111

    
1112
		$group->add(new Form_Input(
1113
			'bandwidth',
1114
			null,
1115
			'number',
1116
			$this->GetBandwidth()
1117
		));
1118

    
1119
		$group->add(new Form_Select(
1120
			'bandwidthtype',
1121
			null,
1122
			$this->GetBwscale(),
1123
			array('Kb' => 'Kbit/s',
1124
				  'Mb' => 'Mbit/s',
1125
				  'Gb' => 'Gbit/s',
1126
				  'b' => 'Bit/s',
1127
				  '%' => '%')
1128
		));
1129

    
1130
		$section->add($group);
1131

    
1132
		$section->addInput(new Form_Input(
1133
			'qlimit',
1134
			'Queue Limit',
1135
			'number',
1136
			$this->GetQlimit()
1137
		));
1138

    
1139
		$section->addInput(new Form_Input(
1140
			'tbrconfig',
1141
			'TBR Size',
1142
			'number',
1143
			$this->GetTbrConfig()
1144
		))->setHelp('Adjusts the size, in bytes, of the token bucket regulator. If not specified, heuristics based on the interface ' .
1145
					'bandwidth are used to determine the size.');
1146

    
1147
		$section->addInput(new Form_Input(
1148
			'interface',
1149
			null,
1150
			'hidden',
1151
			$this->GetInterface()
1152
		));
1153

    
1154
		$section->addInput(new Form_Input(
1155
			'name',
1156
			null,
1157
			'hidden',
1158
			$this->GetQname()
1159
		));
1160

    
1161
		$sform->add($section);
1162

    
1163
		return($sform);
1164
	}
1165

    
1166
	function update_altq_queue_data(&$data) {
1167
		$this->ReadConfig($data);
1168
	}
1169

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

    
1198
}
1199

    
1200
class priq_queue {
1201
	var $qname;
1202
	var $qinterface;
1203
	var $qlimit;
1204
	var $qpriority;
1205
	var $description;
1206
	var $isparent;
1207
	var $qbandwidth;
1208
	var $qbandwidthtype;
1209
	var $qdefault = "";
1210
	var $qrio = "";
1211
	var $qred = "";
1212
	var $qcodel = "";
1213
	var $qecn = "";
1214
	var $qack;
1215
	var $qenabled = "";
1216
	var $qparent;
1217
	var $link;
1218

    
1219
	/* This is here to help with form building and building rules/lists */
1220
	var $subqueues = array();
1221

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

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

    
1297
		if ($bwtype != "%") {
1298
			$sum = $parent->GetTotalBw(($bw > 0) ? $this : NULL);
1299

    
1300
			if ($bw > 0) {
1301
				$sum += get_bandwidth($bw, $bwtype, $parent);
1302
			}
1303

    
1304
			if ($sum > get_queue_bandwidth($parent)) {
1305
				return 1;
1306
			}
1307
		}
1308

    
1309
		foreach ($this->subqueues as $q) {
1310
			if ($q->CheckBandwidth(0, '')) {
1311
				return 1;
1312
			}
1313
		}
1314

    
1315
		return 0;
1316
	}
1317
	function GetTotalBw($qignore = NULL) {
1318
		$sum = 0;
1319
		foreach ($this->subqueues as $q) {
1320
			if ($qignore != NULL && $qignore == $q)
1321
				continue;
1322
			$sum += get_bandwidth($q->GetBandwidth(), $q->GetBwscale(), $q->GetParent());
1323
		}
1324

    
1325
		return $sum;
1326
	}
1327
	function GetBwscale() {
1328
		return $this->qbandwidthtype;
1329
	}
1330
	function SetBwscale($scale) {
1331
		$this->qbandwidthtype = $scale;
1332
	}
1333
	function GetDefaultQueuePresent() {
1334
		if ($this->GetDefault()) {
1335
			return true;
1336
		}
1337
		if (!empty($this->subqueues)) {
1338
			foreach ($this->subqueues as $q) {
1339
				if ($q->GetDefault()) {
1340
					return true;
1341
				}
1342
			}
1343
		}
1344

    
1345
		return false;
1346
	}
1347
	function GetDefault() {
1348
		return $this->qdefault;
1349
	}
1350
	function SetDefault($value = false) {
1351
		$this->qdefault = $value;
1352
	}
1353
	function GetCodel() {
1354
		return $this->codel;
1355
	}
1356
	function SetCodel($codel = false) {
1357
		$this->codel = $codel;
1358
	}
1359
	function GetRed() {
1360
		return $this->qred;
1361
	}
1362
	function SetRed($red = false) {
1363
		$this->qred = $red;
1364
	}
1365
	function GetRio() {
1366
		return $this->qrio;
1367
	}
1368
	function SetRio($rio = false) {
1369
		$this->qrio = $rio;
1370
	}
1371
	function GetEcn() {
1372
		return $this->qecn;
1373
	}
1374
	function SetEcn($ecn = false) {
1375
		$this->qecn = $ecn;
1376
	}
1377
	function GetAck() {
1378
		return $this->qack;
1379
	}
1380
	function SetAck($ack = false) {
1381
		$this->qack = $ack;
1382
	}
1383

    
1384
	function GetBwscaleText() {
1385
		switch ($this->qbandwidthtype) {
1386
			case "b":
1387
				$bwscaletext = "Bit/s";
1388
				break;
1389
			case "Kb":
1390
				$bwscaletext = "Kbit/s";
1391
				break;
1392
			case "Mb":
1393
				$bwscaletext = "Mbit/s";
1394
				break;
1395
			case "Gb":
1396
				$bwscaletext = "Gbit/s";
1397
				break;
1398
			default:
1399
				/* For others that do not need translating like % */
1400
				$bwscaletext = $this->qbandwidthtype;
1401
				break;
1402
		}
1403
		return $bwscaletext;
1404
	}
1405

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

    
1416
		$javascript .= "function myResume() {\n";
1417
		$javascript .= "if (document.layers && document.layers['shaperarea'] != null)\n";
1418
		$javascript .= "document.layers['shaperarea'].visibility = 'visible';\n";
1419
		$javascript .= "else if (document.all)\n";
1420
		$javascript .= "document.all['shaperarea'].style.visibility = 'visible';\n";
1421
		$javascript .= "}\n";
1422
		$javascript .= "//]]>";
1423
		$javascript .= "</script>";
1424

    
1425
		return $javascript;
1426
	}
1427

    
1428
	function &add_queue($interface, &$qname, &$path, &$input_errors) { return; }
1429

    
1430
	/*
1431
	 * Currently this will not be called unless we decide to clone a whole
1432
	 * queue tree on the 'By Queues' view or support drag&drop on the tree/list
1433
	 */
1434
	function copy_queue($interface, &$cflink) {
1435

    
1436
		$cflink['name'] = $this->GetQname();
1437
		$cflink['interface'] = $interface;
1438
		$cflink['qlimit'] = $this->GetQlimit();
1439
		$cflink['priority'] = $this->GetQpriority();
1440
		$cflink['description'] = $this->GetDescription();
1441
		$cflink['enabled'] = $this->GetEnabled();
1442
		$cflink['default'] = $this->GetDefault();
1443
		$cflink['red'] = $this->GetRed();
1444
		$cflink['codel'] = $this->GetCodel();
1445
		$cflink['rio'] = $this->GetRio();
1446
		$cflink['ecn'] = $this->GetEcn();
1447

    
1448
		if (is_array($this->subqueues)) {
1449
			$cflinkp['queue'] = array();
1450
			foreach ($this->subqueues as $q) {
1451
				$cflink['queue'][$q->GetQname()] = array();
1452
				$q->copy_queue($interface, $cflink['queue'][$q->GetQname()]);
1453
			}
1454
		}
1455
	}
1456

    
1457
	function clean_queue($sched) {
1458
		clean_child_queues($sched, $this->GetLink());
1459
		if (is_array($this->subqueues)) {
1460
			foreach ($this->subqueues as $q) {
1461
				$q->clean_queue($sched);
1462
			}
1463
		}
1464
	}
1465

    
1466
	function &get_queue_list(&$qlist) {
1467

    
1468
		$qlist[$this->GetQname()] = & $this;
1469
		if (is_array($this->subqueues)) {
1470
			foreach ($this->subqueues as $queue) {
1471
				$queue->get_queue_list($qlist);
1472
			}
1473
		}
1474
	}
1475

    
1476
	function delete_queue() {
1477
		unref_on_altq_queue_list($this->GetQname());
1478
		cleanup_queue_from_rules($this->GetQname());
1479
		unset_object_by_reference($this->GetLink());
1480
	}
1481

    
1482
	function delete_all() {
1483
		if (count($this->subqueues)) {
1484
			foreach ($this->subqueues as $q) {
1485
				$q->delete_all();
1486
				unset_object_by_reference($q->GetLink());
1487
				unset($q);
1488
			}
1489
			unset($this->subqueues);
1490
		}
1491
	}
1492

    
1493
	function &find_queue($interface, $qname) {
1494
		if ($qname == $this->GetQname()) {
1495
			return $this;
1496
		}
1497
	}
1498

    
1499
	function find_parentqueue($interface, $qname) { return; }
1500

    
1501
	function validate_input($data, &$input_errors) {
1502

    
1503
		$reqdfields[] = "name";
1504
		$reqdfieldsn[] = gettext("Name");
1505
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
1506

    
1507
		if ($data['bandwidth'] && (!is_numeric($data['bandwidth']))) {
1508
			$input_errors[] = gettext("Bandwidth must be an integer.");
1509
		}
1510
		if ($data['bandwidth'] < 0) {
1511
			$input_errors[] = gettext("Bandwidth cannot be negative.");
1512
		}
1513
		if ($data['bandwidthtype'] == "%") {
1514
			if ($data['bandwidth'] > 100 || $data['bandwidth'] < 0) {
1515
				$input_errors[] = gettext("Bandwidth in percentage should be between 1 and 100.");
1516
			}
1517
		}
1518
		if ($this->CheckBandwidth($data['bandwidth'], $data['bandwidthtype']))
1519
			$input_errors[] = "The sum of child bandwidth is higher than parent.";
1520

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

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

    
1605
	function build_tree() {
1606
		$tree = " <li><a href=\"firewall_shaper.php?interface=". $this->GetInterface()."&amp;queue=". htmlspecialchars($this->GetQname())."&amp;action=show";
1607
		$tree .= "\" ";
1608
		$tmpvalue = $this->GetDefault();
1609
		if (!empty($tmpvalue)) {
1610
			$tree .= " class=\"navlnk\"";
1611
		}
1612
		$tree .= " >" . htmlspecialchars($this->GetQname()) . "</a>";
1613
		/*
1614
		 * Not needed here!
1615
		 * if (is_array($queues) {
1616
		 *	  $tree .= "<ul>";
1617
		 *	  foreach ($q as $queues)
1618
		 *		  $tree .= $queues['$q->GetName()']->build_tree();
1619
		 *	  endforeach
1620
		 *	  $tree .= "</ul>";
1621
		 * }
1622
		 */
1623

    
1624
		$tree .= "</li>";
1625

    
1626
		return $tree;
1627
	}
1628

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

    
1687
		$pfq_rule .= " \n";
1688

    
1689
		return $pfq_rule;
1690
	}
1691

    
1692
	/*
1693
	 * To return the html form to show to user
1694
	 * for getting the parameters.
1695
	 * Should do even for first time when the
1696
	 * object is created and later when we may
1697
	 * need to update it. (2)
1698
	 */
1699

    
1700
	function build_form() {
1701

    
1702
		$sform = new Form();
1703

    
1704
		$sform->setAction("firewall_shaper.php");
1705

    
1706
		$section = new Form_Section("");
1707

    
1708
		$section->addInput(new Form_Checkbox(
1709
			'enabled',
1710
			'Enable/Disable',
1711
			'Enable/disable discipline and its children',
1712
			($this->GetEnabled() == "on"),
1713
			'on'
1714
		));
1715

    
1716
		$section->addInput(new Form_Input(
1717
			'newname',
1718
			'*Name',
1719
			'text',
1720
			$this->GetQname()
1721
		))->setHelp('Enter the name of the queue here. Do not use spaces and limit the size to 15 characters.');
1722

    
1723
		$section->addInput(new Form_Input(
1724
			'name',
1725
			null,
1726
			'hidden',
1727
			$this->GetQname()
1728
		));
1729

    
1730
		if (!is_a($this, "hfsc_queue")) {
1731
			$section->addInput(new Form_Input(
1732
				'priority',
1733
				'Priority',
1734
				'number',
1735
				$this->GetQpriority(),
1736
				['min' => '0', 'max'=> '']
1737
			))->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.');
1738
		}
1739

    
1740
		$section->addInput(new Form_Input(
1741
			'qlimit',
1742
			'Queue Limit',
1743
			'number',
1744
			$this->GetQlimit()
1745
		))->setHelp('Queue limit in packets.');
1746

    
1747
		$group = new Form_Group('Scheduler options');
1748

    
1749
		if (empty($this->subqueues)) {
1750
			$group->add(new Form_Checkbox(
1751
				'default',
1752
				null,
1753
				null,
1754
				$this->GetDefault(),
1755
				'default'
1756
			))->setHelp('Default Queue');
1757
		}
1758

    
1759
		$group->add(new Form_Checkbox(
1760
			'red',
1761
			null,
1762
			null,
1763
			!empty($this->GetRed())
1764
		))->setHelp('%1$sRandom Early Detection%2$s', '<a target="_new" href="https://docs.netgate.com/pfsense/en/latest/book/trafficshaper/advanced-customization.html#editing-shaper-queues">', '</a>');
1765

    
1766
		$group->add(new Form_Checkbox(
1767
			'rio',
1768
			null,
1769
			null,
1770
			!empty($this->GetRio())
1771
		))->setHelp('%1$sRandom Early Detection In and Out%2$s', '<a target="_new" href="https://docs.netgate.com/pfsense/en/latest/book/trafficshaper/advanced-customization.html#editing-shaper-queues">', '</a>');
1772

    
1773
		$group->add(new Form_Checkbox(
1774
			'ecn',
1775
			null,
1776
			null,
1777
			!empty($this->GetEcn())
1778
		))->setHelp('%1$sExplicit Congestion Notification%2$s', '<a target="_new" href="https://docs.netgate.com/pfsense/en/latest/book/trafficshaper/advanced-customization.html#editing-shaper-queues">', '</a>');
1779

    
1780
		$group->add(new Form_Checkbox(
1781
			'codel',
1782
			null,
1783
			null,
1784
			!empty($this->GetCodel())
1785
		))->setHelp('%1$sCodel Active Queue%2$s', '<a target="_new" href="https://docs.netgate.com/pfsense/en/latest/book/trafficshaper/altq-scheduler-types.html#codel-active-queue-management">', '</a>');
1786

    
1787
		$group->setHelp('Select options for this queue');
1788

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

    
1791
		$section->addInput(new Form_Input(
1792
			'description',
1793
			'Description',
1794
			'text',
1795
			$this->GetDescription()
1796
		));
1797

    
1798
		$sform->add($section);
1799

    
1800
		$sform->addGlobal(new Form_Input(
1801
			'interface',
1802
			null,
1803
			'hidden',
1804
			$this->GetInterface()
1805
		));
1806

    
1807
		$sform->addGlobal(new Form_Input(
1808
			'name',
1809
			null,
1810
			'hidden',
1811
			$this->GetQname()
1812
		));
1813

    
1814
		return($sform);
1815
	}
1816

    
1817
	function build_shortform() {
1818
		/* XXX: Hacks in sight. Mostly layer violations!  */
1819
		global $g, $altq_list_queues;
1820
		global $shaperIFlist;
1821

    
1822
		$altq =& $altq_list_queues[$this->GetInterface()];
1823

    
1824
		if ($altq) {
1825
			$scheduler = $altq->GetScheduler();
1826
		}
1827

    
1828
		$form = '<dl class="dl-horizontal">';
1829
		$form .= '	<dt>';
1830
		$form .= '		<a href="firewall_shaper.php?interface=' . $this->GetInterface() . '&amp;queue=' . $this->GetQname() . '&amp;action=show">' . $shaperIFlist[$this->GetInterface()] . '</a>';
1831
		$form .= '	</dt>';
1832
		$form .= '	<dd>';
1833
		$form .=		$scheduler;
1834
		$form .= '	</dd>';
1835

    
1836
		$form .= '	<dt>';
1837
		$form .=		'Bandwidth';
1838
		$form .= '	</dt>';
1839
		$form .= '	<dd>';
1840
		$form .=		$this->GetBandwidth() . '&nbsp;' . $this->GetBwscaleText();
1841
		$form .= '	</dd>';
1842

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

    
1853
		$tmpvalue = $this->GetDefault();
1854
		if (!empty($tmpvalue)) {
1855
			$form .= '	<dt>';
1856
			$form .=		'Default';
1857
			$form .= '	<dt>';
1858
			$form .= '	<dd>';
1859
			$form .=		'On';
1860
			$form .= '	</dd>';
1861
		}
1862

    
1863
			$form .= '	<dt>';
1864
			$form .= 'Delete';
1865
			$form .= '	<dt>';
1866
			$form .= '	<dd>';
1867

    
1868
			$form .= '<a class="btn btn-danger btn-xs" href="firewall_shaper_queues.php?interface=';
1869
			$form .= $this->GetInterface() . '&amp;queue=';
1870
			$form .= $this->GetQname() . '&amp;action=delete">';
1871
			$form .= '<i class="fa fa-trash icon-embed-btn"></i>';
1872
			$form .= gettext("Delete Queue from this Interface") . '</a>';
1873

    
1874
			$form .= '	</dd>';
1875

    
1876
			$form .= '</dl>';
1877

    
1878
		return $form;
1879

    
1880
	}
1881

    
1882
	function update_altq_queue_data(&$q) {
1883
		$this->ReadConfig($q);
1884
	}
1885

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

    
1932
class hfsc_queue extends priq_queue {
1933
	/* realtime */
1934
	var $realtime;
1935
	var $r_m1;
1936
	var $r_d;
1937
	var $r_m2;
1938
	/* linkshare */
1939
	var $linkshare;
1940
	var $l_m1;
1941
	var $l_d;
1942
	var $l_m2;
1943
	/* upperlimit */
1944
	var $upperlimit;
1945
	var $u_m1;
1946
	var $u_d;
1947
	var $u_m2;
1948

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

    
2037
	function &add_queue($interface, &$qname, &$path, &$input_errors) {
2038

    
2039
		if (!is_array($this->subqueues)) {
2040
			$this->subqueues = array();
2041
		}
2042
		$__tmp_q = new hfsc_queue(); $q =& $__tmp_q;
2043
		$q->SetInterface($this->GetInterface());
2044
		$q->SetParent($this);
2045
		$q->ReadConfig($qname);
2046
		$q->validate_input($qname, $input_errors);
2047

    
2048
		$q->SetEnabled("on");
2049
		$q->SetLink($path);
2050

    
2051
		$this->subqueues[$q->GetQname()] =& $q; //new hfsc_queue()
2052
		ref_on_altq_queue_list($this->GetQname(), $q->GetQname());
2053
		if (is_array($qname['queue'])) {
2054
			foreach ($qname['queue'] as $key1 => $que) {
2055
				array_push($path, $key1);
2056
				$q->add_queue($q->GetInterface(), $que, $path, $input_errors);
2057
				array_pop($path);
2058
			}
2059
		}
2060

    
2061
		return $q;
2062
	}
2063

    
2064
	function copy_queue($interface, &$cflink) {
2065

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

    
2157
		if (is_array($this->subqueues)) {
2158
			$cflinkp['queue'] = array();
2159
			foreach ($this->subqueues as $q) {
2160
				$cflink['queue'][$q->GetQname()] = array();
2161
				$q->copy_queue($interface, $cflink['queue'][$q->GetQname()]);
2162
			}
2163
		}
2164
	}
2165

    
2166
	function delete_queue() {
2167
		unref_on_altq_queue_list($this->GetQname());
2168
		cleanup_queue_from_rules($this->GetQname());
2169
		$parent =& $this->GetParent();
2170
		foreach ($this->subqueues as $q)
2171
			$q->delete_queue();
2172
		unset_object_by_reference($this->GetLink());
2173
	}
2174

    
2175
	/*
2176
	 * Should search even its children
2177
	 */
2178
	function &find_queue($interface, $qname) {
2179
		if ($qname == $this->GetQname()) {
2180
			return $this;
2181
		}
2182

    
2183
		foreach ($this->subqueues as $q) {
2184
			$result =& $q->find_queue("", $qname);
2185
			if ($result) {
2186
				return $result;
2187
			}
2188
		}
2189
	}
2190

    
2191
	function &find_parentqueue($interface, $qname) {
2192
		if ($this->subqueues[$qname]) {
2193
			return $this;
2194
		}
2195
		foreach ($this->subqueues as $q) {
2196
			$result = $q->find_parentqueue("", $qname);
2197
			if ($result) {
2198
				return $result;
2199
			}
2200
		}
2201
	}
2202

    
2203
	function validate_input($data, &$input_errors) {
2204
		parent::validate_input($data, $input_errors);
2205

    
2206
		$reqdfields[] = "bandwidth";
2207
		$reqdfieldsn[] = gettext("Bandwidth");
2208
		$reqdfields[] = "bandwidthtype";
2209
		$reqdfieldsn[] = gettext("Bandwidthtype");
2210

    
2211
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
2212

    
2213
		if (isset($data['linkshare3']) && $data['linkshare3'] <> "") {
2214
			if ($data['bandwidth'] && (!is_numeric($data['bandwidth']))) {
2215
				$input_errors[] = gettext("Bandwidth must be an integer.");
2216
			}
2217

    
2218
			if ($data['bandwidth'] < 0) {
2219
				$input_errors[] = gettext("Bandwidth cannot be negative.");
2220
			}
2221

    
2222
			if ($data['bandwidthtype'] == "%") {
2223
				if ($data['bandwidth'] > 100 || $data['bandwidth'] < 0) {
2224
					$input_errors[] = gettext("Bandwidth in percentage should be between 1 and 100.");
2225
				}
2226
			}
2227
		}
2228

    
2229
		if ($data['upperlimit1'] <> "" && $data['upperlimit2'] == "") {
2230
			$input_errors[] = gettext("upperlimit service curve defined but missing (d) value");
2231
		}
2232
		if ($data['upperlimit2'] <> "" && $data['upperlimit1'] == "") {
2233
			$input_errors[] = gettext("upperlimit service curve defined but missing initial bandwidth (m1) value");
2234
		}
2235
		if ($data['upperlimit1'] <> "" && !is_valid_shaperbw($data['upperlimit1'])) {
2236
			$input_errors[] = gettext("upperlimit m1 value needs to be Kb, Mb, Gb, or %");
2237
		}
2238
		if ($data['upperlimit2'] <> "" && !is_numeric($data['upperlimit2'])) {
2239
			$input_errors[] = gettext("upperlimit d value needs to be numeric");
2240
		}
2241
		if ($data['upperlimit3'] <> "" && !is_valid_shaperbw($data['upperlimit3'])) {
2242
			$input_errors[] = gettext("upperlimit m2 value needs to be Kb, Mb, Gb, or %");
2243
		}
2244

    
2245
		/*
2246
		if (isset($data['upperlimit']) && $data['upperlimit3'] <> "" && $data['upperlimit1'] <> "") {
2247
			$bw_1 = get_hfsc_bandwidth($this, $data['upperlimit1']);
2248
			$bw_2 = get_hfsc_bandwidth($this, $data['upperlimit3']);
2249
			if (floatval($bw_1) < floatval($bw_2)) {
2250
				$input_errors[] = ("upperlimit m1 cannot be smaller than m2");
2251
			}
2252

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

    
2280
		/*
2281
		if (isset($data['linkshare']) && $data['linkshare3'] <> "" && $data['linkshare1'] <> "" && 0) {
2282
			$bw_1 = get_hfsc_bandwidth($this, $data['linkshare1']);
2283
			$bw_2 = get_hfsc_bandwidth($this, $data['linkshare3']);
2284
			if (floatval($bw_1) < floatval($bw_2)) {
2285
				$input_errors[] = ("linkshare m1 cannot be smaller than m2");
2286
			}
2287

    
2288
			if (get_interface_bandwidth($this) < (0.8 * (floatval($bw_1) + floatval($bw_2)))) {
2289
				$input_errors[] = ("linkshare specification exceeds 80% of allowable allocation.");
2290
			}
2291
		}
2292
		*/
2293

    
2294
		if ($data['realtime1'] <> "" && !is_valid_shaperbw($data['realtime1'])) {
2295
			$input_errors[] = gettext("realtime m1 value needs to be Kb, Mb, Gb, or %");
2296
		}
2297
		if ($data['realtime2'] <> "" && !is_numeric($data['realtime2'])) {
2298
			$input_errors[] = gettext("realtime d value needs to be numeric");
2299
		}
2300
		if ($data['realtime3'] <> "" && !is_valid_shaperbw($data['realtime3'])) {
2301
			$input_errors[] = gettext("realtime m2 value needs to be Kb, Mb, Gb, or %");
2302
		}
2303

    
2304
		/*
2305
		if (isset($data['realtime']) && $data['realtime3'] <> "" && $data['realtime1'] <> "" && 0) {
2306
			$bw_1 = get_hfsc_bandwidth($this, $data['realtime1']);
2307
			$bw_2 = get_hfsc_bandwidth($this, $data['realtime3']);
2308
			if (floatval($bw_1) < floatval($bw_2)) {
2309
				$input_errors[] = ("realtime m1 cannot be smaller than m2");
2310
			}
2311

    
2312
			if (get_interface_bandwidth($this) < (0.8 * (floatval($bw_1) + floatval($bw_2)))) {
2313
				$input_errors[] = ("realtime specification exceeds 80% of allowable allocation.");
2314
			}
2315
		}
2316
		*/
2317
	}
2318

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

    
2374
	function build_tree() {
2375
		$tree = " <li><a href=\"firewall_shaper.php?interface=" . $this->GetInterface() ."&amp;queue=" . htmlspecialchars($this->GetQname())."&amp;action=show";
2376
		$tree .= "\" ";
2377
		$tmpvalue = $this->GetDefault();
2378
		if (!empty($tmpvalue)) {
2379
			$tree .= " class=\"navlnk\"";
2380
		}
2381
		$tree .= " >" . htmlspecialchars($this->GetQname()) . "</a>";
2382
		if (is_array($this->subqueues)) {
2383
			$tree .= "<ul>";
2384
			foreach ($this->subqueues as $q) {
2385
				$tree .= $q->build_tree();
2386
			}
2387
			$tree .= "</ul>";
2388
		}
2389
		$tree .= "</li>";
2390
		return $tree;
2391
	}
2392

    
2393
	/* Even this should take children into consideration */
2394
	function build_rules(&$default = false) {
2395

    
2396
		$pfq_rule = " queue ". $this->qname;
2397
		if ($this->GetInterface()) {
2398
			$pfq_rule .= " on ".get_real_interface($this->GetInterface());
2399
		}
2400
		if ($this->GetBandwidth() && $this->GetBwscale()) {
2401
			$pfq_rule .= " bandwidth ".trim($this->GetBandwidth()).$this->GetBwscale();
2402
		}
2403

    
2404
		$tmpvalue = $this->GetQlimit();
2405
		if (!empty($tmpvalue)) {
2406
			$pfq_rule .= " qlimit " . $this->GetQlimit();
2407
		}
2408
		if ($this->GetDefault() || $this->GetRed() || $this->GetRio() || $this->GetEcn() || $this->GetCodel() || $this->GetRealtime() <> "" || $this->GetLinkshare() <> "" || $this->GetUpperlimit() <> "") {
2409
			$pfq_rule .= " hfsc ( ";
2410
			$tmpvalue = $this->GetRed();
2411
			if (!empty($tmpvalue)) {
2412
				$comma = 1;
2413
				$pfq_rule .= " red ";
2414
			}
2415

    
2416
			$tmpvalue = $this->GetRio();
2417
			if (!empty($tmpvalue)) {
2418
				if ($comma) {
2419
					$pfq_rule .= " ,";
2420
				}
2421
				$comma = 1;
2422
				$pfq_rule .= " rio ";
2423
			}
2424
			$tmpvalue = $this->GetEcn();
2425
			if (!empty($tmpvalue)) {
2426
				if ($comma) {
2427
					$pfq_rule .= " ,";
2428
				}
2429
				$comma = 1;
2430
				$pfq_rule .= " ecn ";
2431
			}
2432
			$tmpvalue = $this->GetCodel();
2433
			if (!empty($tmpvalue)) {
2434
				if ($comma) {
2435
					$pfq_rule .= " ,";
2436
				}
2437
				$comma = 1;
2438
				$pfq_rule .= " codel ";
2439
			}
2440
			$tmpvalue = $this->GetDefault();
2441
			if (!empty($tmpvalue)) {
2442
				if ($comma) {
2443
					$pfq_rule .= " ,";
2444
				}
2445
				$comma = 1;
2446
				$pfq_rule .= " default ";
2447
				$default = true;
2448
			}
2449

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

    
2501
		$pfq_rule .= " \n";
2502

    
2503
		return $pfq_rule;
2504
	}
2505

    
2506
	function build_javascript() {
2507

    
2508
		$javascript = <<<EOJS
2509
<script type="text/javascript">
2510
//<![CDATA[
2511
	events.push(function(){
2512

    
2513
		// Disables the specified input element
2514
		function disableInput(id, disable) {
2515
			$('#' + id).prop("disabled", disable);
2516
		}
2517

    
2518
		// Upperlimit
2519
		function enable_upperlimit() {
2520
			disableInput('upperlimit1', !$('#upperlimit').prop('checked'));
2521
			disableInput('upperlimit2', !$('#upperlimit').prop('checked'));
2522
			disableInput('upperlimit3', !$('#upperlimit').prop('checked'));
2523
		}
2524

    
2525
		$('#upperlimit').click(function () {
2526
			enable_upperlimit();
2527
		});
2528

    
2529
		enable_upperlimit();
2530

    
2531
		// realtime
2532
		function enable_realtime() {
2533
			disableInput('realtime1', !$('#realtime').prop('checked'));
2534
			disableInput('realtime2', !$('#realtime').prop('checked'));
2535
			disableInput('realtime3', !$('#realtime').prop('checked'));
2536
		}
2537

    
2538
		$('#realtime').click(function () {
2539
			enable_realtime();
2540
		});
2541

    
2542
		enable_realtime();
2543

    
2544
		// linkshare
2545
		function enable_linkshare() {
2546
			disableInput('linkshare1', !$('#linkshare').prop('checked'));
2547
			disableInput('linkshare2', !$('#linkshare').prop('checked'));
2548
			disableInput('linkshare3', !$('#linkshare').prop('checked'));
2549
		}
2550

    
2551
		$('#linkshare').click(function () {
2552
			enable_linkshare();
2553
		});
2554

    
2555
		enable_linkshare();
2556
	});
2557
//]]>
2558
</script>
2559
EOJS;
2560

    
2561
		return $javascript;
2562
	}
2563

    
2564
	function build_form() {
2565

    
2566
		$sform = parent::build_form();
2567

    
2568
		$section = new Form_Section('Service Curve (sc)');
2569

    
2570
		$group = new Form_Group('Bandwidth');
2571

    
2572
		$group->add(new Form_Input(
2573
			'bandwidth',
2574
			null,
2575
			'number',
2576
			$this->GetBandwidth(),
2577
			['step' => 'any', 'min' => '0.000']
2578
		));
2579

    
2580
		$group->add(new Form_Select(
2581
			'bandwidthtype',
2582
			null,
2583
			$this->GetBwscale(),
2584
			array('Kb' => 'Kbit/s',
2585
				  'Mb' => 'Mbit/s',
2586
				  'Gb' => 'Gbit/s',
2587
				  'b' => 'Bit/s',
2588
				  '%' => '%')
2589
		));
2590

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

    
2593
		$section->add($group);
2594

    
2595
		$group = new Form_Group('Max bandwidth for queue.');
2596

    
2597
		$group->add(new Form_Checkbox(
2598
			'upperlimit',
2599
			null,
2600
			'Upper Limit',
2601
			($this->GetUpperlimit()<> "")
2602
		));
2603

    
2604
		$group->add(new Form_Input(
2605
			'upperlimit1',
2606
			null,
2607
			'text',
2608
			$this->GetU_m1()
2609
		))->setHelp('m1');
2610

    
2611
		$group->add(new Form_Input(
2612
			'upperlimit2',
2613
			null,
2614
			'text',
2615
			$this->GetU_d()
2616
		))->setHelp('d');
2617

    
2618
		$group->add(new Form_Input(
2619
			'upperlimit3',
2620
			null,
2621
			'text',
2622
			$this->GetU_m2()
2623
		))->setHelp('m2');
2624

    
2625

    
2626
		$section->add($group);
2627

    
2628
		$group = new Form_Group('Min bandwidth for queue.');
2629

    
2630
		$group->add(new Form_Checkbox(
2631
			'realtime',
2632
			null,
2633
			'Real Time',
2634
			($this->GetRealtime()<> "")
2635
		));
2636

    
2637
		$group->add(new Form_Input(
2638
			'realtime1',
2639
			null,
2640
			'text',
2641
			$this->GetR_m1()
2642
		))->setHelp('m1');
2643

    
2644
		$group->add(new Form_Input(
2645
			'realtime2',
2646
			null,
2647
			'text',
2648
			$this->GetR_d()
2649
		))->setHelp('d');
2650

    
2651
		$group->add(new Form_Input(
2652
			'realtime3',
2653
			null,
2654
			'text',
2655
			$this->GetR_m2()
2656
		))->setHelp('m2');
2657

    
2658
		$section->add($group);
2659

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

    
2662
		$group->add(new Form_Checkbox(
2663
			'linkshare',
2664
			null,
2665
			'Link Share',
2666
			($this->GetLinkshare()<> "")
2667
		));
2668

    
2669
		$group->add(new Form_Input(
2670
			'linkshare1',
2671
			null,
2672
			'text',
2673
			$this->GetL_m1()
2674
		))->setHelp('m1');
2675

    
2676
		$group->add(new Form_Input(
2677
			'linkshare2',
2678
			null,
2679
			'text',
2680
			$this->GetL_d()
2681
		))->setHelp('d');
2682

    
2683
		$group->add(new Form_Input(
2684
			'linkshare3',
2685
			null,
2686
			'text',
2687
			$this->GetL_m2()
2688
		))->setHelp('m2');
2689

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

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

    
2698
		$sform->add($section);
2699

    
2700
		return($sform);
2701
	}
2702

    
2703
	function update_altq_queue_data(&$data) {
2704
		$this->ReadConfig($data);
2705
	}
2706

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

    
2824
class cbq_queue extends priq_queue {
2825
	var $qborrow = "";
2826

    
2827
	function GetBorrow() {
2828
		return $this->qborrow;
2829
	}
2830
	function SetBorrow($borrow) {
2831
		$this->qborrow = $borrow;
2832
	}
2833
	function CanHaveChildren() {
2834
		return true;
2835
	}
2836

    
2837
	function &add_queue($interface, &$qname, &$path, &$input_errors) {
2838

    
2839
		if (!is_array($this->subqueues)) {
2840
			$this->subqueues = array();
2841
		}
2842
		$__tmp_q = new cbq_queue(); $q =& $__tmp_q;
2843
		$q->SetInterface($this->GetInterface());
2844
		$q->SetParent($this);
2845
		$q->ReadConfig($qname);
2846
		$q->validate_input($qname, $input_errors);
2847

    
2848
		$q->SetEnabled("on");
2849
		$q->SetLink($path);
2850
		$this->subqueues[$q->GetQName()] = &$q;
2851
		ref_on_altq_queue_list($this->GetQname(), $q->GetQname());
2852
		if (is_array($qname['queue'])) {
2853
			foreach ($qname['queue'] as $key1 => $que) {
2854
				array_push($path, $key1);
2855
				$q->add_queue($q->GetInterface(), $que, $path, $input_errors);
2856
				array_pop($path);
2857
			}
2858
		}
2859

    
2860
		return $q;
2861
	}
2862

    
2863
	function copy_queue($interface, &$cflink) {
2864

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

    
2914
	/*
2915
	 * Should search even its children
2916
	 */
2917
	function &find_queue($interface, $qname) {
2918
		if ($qname == $this->GetQname()) {
2919
			return $this;
2920
		}
2921
		foreach ($this->subqueues as $q) {
2922
			$result =& $q->find_queue("", $qname);
2923
			if ($result) {
2924
				return $result;
2925
			}
2926
		}
2927
	}
2928

    
2929
	function &find_parentqueue($interface, $qname) {
2930
		if ($this->subqueues[$qname]) {
2931
			return $this;
2932
		}
2933
		foreach ($this->subqueues as $q) {
2934
			$result = $q->find_parentqueue("", $qname);
2935
			if ($result) {
2936
				return $result;
2937
			}
2938
		}
2939
	}
2940

    
2941
	function delete_queue() {
2942
		unref_on_altq_queue_list($this->GetQname());
2943
		cleanup_queue_from_rules($this->GetQname());
2944
		foreach ($this->subqueues as $q)
2945
			$q->delete_queue();
2946
		unset_object_by_reference($this->GetLink());
2947
	}
2948

    
2949
	function validate_input($data, &$input_errors) {
2950
		parent::validate_input($data, $input_errors);
2951

    
2952
		if ($data['priority'] > 7) {
2953
				$input_errors[] = gettext("Priority must be an integer between 0 and 7.");
2954
		}
2955
		$reqdfields[] = "bandwidth";
2956
		$reqdfieldsn[] = gettext("Bandwidth");
2957
		$reqdfields[] = "bandwidthtype";
2958
		$reqdfieldsn[] = gettext("Bandwidthtype");
2959

    
2960
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
2961
	}
2962

    
2963
	function ReadConfig(&$q) {
2964
		parent::ReadConfig($q);
2965
		if (!empty($q['borrow'])) {
2966
			$this->SetBorrow("on");
2967
		} else {
2968
			$this->SetBorrow("");
2969
		}
2970
	}
2971

    
2972
	function build_javascript() {
2973
		return parent::build_javascript();
2974
	}
2975

    
2976
	function build_tree() {
2977
		$tree = " <li><a href=\"firewall_shaper.php?interface=" . $this->GetInterface()."&amp;queue=" . htmlspecialchars($this->GetQname())."&amp;action=show";
2978
		$tree .= "\" ";
2979
		$tmpvalue = trim($this->GetDefault());
2980
		if (!empty($tmpvalue)) {
2981
			$tree .= " class=\"navlnk\"";
2982
		}
2983
		$tree .= " >" . htmlspecialchars($this->GetQname()) . "</a>";
2984
		if (is_array($this->subqueues)) {
2985
			$tree .= "<ul>";
2986
			foreach ($this->subqueues as $q) {
2987
				$tree .= $q->build_tree();
2988
			}
2989
			$tree .= "</ul>";
2990
		}
2991
		$tree .= "</li>";
2992
		return $tree;
2993
	}
2994

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

    
3075
		$pfq_rule .= " \n";
3076
		return $pfq_rule;
3077
	}
3078

    
3079
	function build_form() {
3080
		$sform = parent::build_form();
3081

    
3082
		$section = new Form_Section('NOTITLE');
3083

    
3084
		$group = new Form_Group('Bandwidth');
3085

    
3086
		$group->add(new Form_Input(
3087
			'bandwidth',
3088
			null,
3089
			'number',
3090
			$this->GetBandwidth()
3091
		));
3092

    
3093
		$group->add(new Form_Select(
3094
			'bandwidthtype',
3095
			null,
3096
			$this->GetBwscale(),
3097
			array('Kb' => 'Kbit/s',
3098
				  'Mb' => 'Mbit/s',
3099
				  'Gb' => 'Gbit/s',
3100
				  'b' => 'Bit/s',
3101
				  '%' => '%')
3102
		));
3103

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

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

    
3108
		$section->addInput(new Form_Checkbox(
3109
			'borrow',
3110
			'Scheduler option',
3111
			'Borrow from other queues when available',
3112
			($this->GetBorrow() == "on")
3113
		));
3114

    
3115
		$sform->add($section);
3116

    
3117
		return $sform;
3118
	}
3119

    
3120
	function update_altq_queue_data(&$data) {
3121
		$this->ReadConfig($data);
3122
	}
3123

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

    
3176
class fairq_queue extends priq_queue {
3177
	var $hogs;
3178
	var $buckets;
3179

    
3180
	function GetBuckets() {
3181
		return $this->buckets;
3182
	}
3183
	function SetBuckets($buckets) {
3184
		$this->buckets = $buckets;
3185
	}
3186
	function GetHogs() {
3187
		return $this->hogs;
3188
	}
3189
	function SetHogs($hogs) {
3190
		$this->hogs = $hogs;
3191
	}
3192
	function CanHaveChildren() {
3193
		return false;
3194
	}
3195

    
3196

    
3197
	function copy_queue($interface, &$cflink) {
3198
		$cflink['interface'] = $interface;
3199
		$cflink['qlimit'] = $this->GetQlimit();
3200
		$cflink['priority'] = $this->GetQpriority();
3201
		$cflink['name'] = $this->GetQname();
3202
		$cflink['description'] = $this->GetDescription();
3203
		$cflink['bandwidth'] = $this->GetBandwidth();
3204
		$cflink['bandwidthtype'] = $this->GetBwscale();
3205
		$cflink['enabled'] = $this->GetEnabled();
3206
		$cflink['default'] = $this->GetDefault();
3207
		$cflink['red'] = $this->GetRed();
3208
		$cflink['rio'] = $this->GetRio();
3209
		$cflink['ecn'] = $this->GetEcn();
3210
		$cflink['buckets'] = $this->GetBuckets();
3211
		$cflink['hogs'] = $this->GetHogs();
3212
	}
3213

    
3214
	/*
3215
	 * Should search even its children
3216
	 */
3217
	function &find_queue($interface, $qname) {
3218
		if ($qname == $this->GetQname()) {
3219
			return $this;
3220
		}
3221
	}
3222

    
3223
	function find_parentqueue($interface, $qname) { return; }
3224

    
3225
	function delete_queue() {
3226
		unref_on_altq_queue_list($this->GetQname());
3227
		cleanup_queue_from_rules($this->GetQname());
3228
		unset_object_by_reference($this->GetLink());
3229
	}
3230

    
3231
	function validate_input($data, &$input_errors) {
3232
		parent::validate_input($data, $input_errors);
3233

    
3234
		if ($data['priority'] > 7) {
3235
				$input_errors[] = gettext("Priority must be an integer between 0 and 7.");
3236
		}
3237
		$reqdfields[] = "bandwidth";
3238
		$reqdfieldsn[] = gettext("Bandwidth");
3239
		$reqdfields[] = "bandwidthtype";
3240
		$reqdfieldsn[] = gettext("Bandwidthtype");
3241

    
3242
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
3243
	}
3244

    
3245
	function ReadConfig(&$q) {
3246
		parent::ReadConfig($q);
3247
		if (!empty($q['buckets'])) {
3248
			$this->SetBuckets($q['buckets']);
3249
		} else {
3250
			$this->SetBuckets("");
3251
		}
3252
		if (!empty($q['hogs']) && is_valid_shaperbw($q['hogs'])) {
3253
			$this->SetHogs($q['hogs']);
3254
		} else {
3255
			$this->SetHogs("");
3256
		}
3257
	}
3258

    
3259
	function build_javascript() {
3260
		return parent::build_javascript();
3261
	}
3262

    
3263
	function build_tree() {
3264
		$tree = " <li><a href=\"firewall_shaper.php?interface=" .
3265
		$this->GetInterface()."&amp;queue=" . htmlspecialchars($this->GetQname())."&amp;action=show";
3266
		$tree .= "\" ";
3267
		$tmpvalue = trim($this->GetDefault());
3268
		if (!empty($tmpvalue)) {
3269
			$tree .= " class=\"navlnk\"";
3270
		}
3271
		$tree .= " >" . htmlspecialchars($this->GetQname()) . "</a>";
3272
		$tree .= "</li>";
3273
		return $tree;
3274
	}
3275

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

    
3348
		$pfq_rule .= " \n";
3349
		return $pfq_rule;
3350
	}
3351

    
3352
	function build_form() {
3353
		$form = parent::build_form();
3354

    
3355
		$section = new Form_Section('');
3356

    
3357
		$group = new Form_Group('Bandwidth');
3358

    
3359
		$group->add(new Form_Input(
3360
			'bandwidth',
3361
			null,
3362
			'number',
3363
			$this->GetBandwidth()
3364
		));
3365

    
3366
		$group->add(new Form_Select(
3367
			'bandwidthtype',
3368
			null,
3369
			$this->GetBwscale(),
3370
			array('Kb' => 'Kbit/s',
3371
				  'Mb' => 'Mbit/s',
3372
				  'Gb' => 'Gbit/s',
3373
				  'b' => 'Bit/s',
3374
				  '%' => '%')
3375
		));
3376

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

    
3379
		$section->add($group);
3380

    
3381
		$section->addInput(new Form_Input(
3382
			'buckets',
3383
			'Scheduler specific options',
3384
			'text',
3385
			$this->GetBuckets()
3386
		))->setHelp('Number of buckets available');
3387

    
3388
		$section->addInput(new Form_Input(
3389
			'hogs',
3390
			'',
3391
			'text',
3392
			$this->GetHogs()
3393
			))->setHelp('Bandwidth limit for hosts to not saturate link');
3394

    
3395
		$form->add($section);
3396
		return $form;
3397
	}
3398

    
3399
	function update_altq_queue_data(&$data) {
3400
		$this->ReadConfig($data);
3401
	}
3402

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

    
3459

    
3460
/*
3461
 * dummynet(4) wrappers.
3462
 */
3463

    
3464

    
3465
/*
3466
 * List of respective objects!
3467
 */
3468
$dummynet_pipe_list = array();
3469

    
3470
class dummynet_class {
3471
	var $qname;
3472
	var $qnumber; /* dummynet(4) uses numbers instead of names; maybe integrate with pf the same as altq does?! */
3473
	var $qlimit;
3474
	var $description;
3475
	var $qenabled;
3476
	var $link;
3477
	var $qparent; /* link to upper class so we do things easily on WF2Q+ rule creation */
3478
	var $plr;
3479

    
3480
	var $buckets;
3481
	/* mask parameters */
3482
	var $mask;
3483
	var $noerror;
3484

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

    
3562
	function build_javascript() {
3563
		$javascript .= "<script type=\"text/javascript\">\n";
3564
		$javascript .= "//<![CDATA[\n";
3565
		$javascript .= "function enable_maskbits(enable_over) {\n";
3566
		$javascript .= "var e = document.getElementById(\"mask\");\n";
3567
		$javascript .= "if ((e.options[e.selectedIndex].text == \"none\") || enable_over) {\n";
3568
		$javascript .= "document.iform.maskbits.disabled = 1;\n";
3569
		$javascript .= "document.iform.maskbits.value = \"\";\n";
3570
		$javascript .= "document.iform.maskbitsv6.disabled = 1;\n";
3571
		$javascript .= "document.iform.maskbitsv6.value = \"\";\n";
3572
		$javascript .= "} else {\n";
3573
		$javascript .= "document.iform.maskbits.disabled = 0;\n";
3574
		$javascript .= "document.iform.maskbitsv6.disabled = 0;\n";
3575
		$javascript .= "}}\n";
3576
		$javascript .= "//]]>\n";
3577
		$javascript .= "</script>\n";
3578
		return $javascript;
3579
	}
3580

    
3581
	function validate_input($data, &$input_errors) {
3582
		$reqdfields[] = "bandwidth";
3583
		$reqdfieldsn[] = gettext("Bandwidth");
3584
		/*$reqdfields[] = "burst";
3585
		$reqdfieldsn[] = gettext("Burst"); */
3586
		$reqdfields[] = "bandwidthtype";
3587
		$reqdfieldsn[] = gettext("Bandwidthtype");
3588
		$reqdfields[] = "newname";
3589
		$reqdfieldsn[] = gettext("Name");
3590

    
3591
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
3592

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

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

    
3659
}
3660

    
3661
class dnpipe_class extends dummynet_class {
3662
	var $delay;
3663
	var $qbandwidth = array();
3664
	var $qbandwidthtype;
3665

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

    
3716
		/* This is here to help on form building and building rules/lists */
3717
	var $subqueues = array();
3718

    
3719
	function CanHaveChildren() {
3720
		return true;
3721
	}
3722
	function SetDelay($delay) {
3723
		$this->delay = $delay;
3724
	}
3725
	function GetDelay() {
3726
		return $this->delay;
3727
	}
3728
	function delete_queue() {
3729
		cleanup_dnqueue_from_rules($this->GetQname());
3730
		foreach ($this->subqueues as $q) {
3731
			$q->delete_queue();
3732
		}
3733
		unset_dn_object_by_reference($this->GetLink());
3734
		@pfSense_ipfw_pipe("pipe delete " . $this->GetNumber());
3735
		@pfSense_ipfw_pipe("sched delete " . $this->GetNumber());
3736
	}
3737
	function GetBandwidth() {
3738
		return $this->qbandwidth;
3739
	}
3740
	function SetBandwidth($bandwidth) {
3741
		$this->qbandwidth = $bandwidth;
3742
	}
3743
	function GetBurst() {
3744
		return $this->qburst;
3745
	}
3746
	function SetBurst($burst) {
3747
		$this->qburst = $burst;
3748
	}
3749

    
3750
	function &add_queue($interface, &$queue, &$path, &$input_errors) {
3751

    
3752
		if (!is_array($this->subqueues)) {
3753
			$this->subqueues = array();
3754
		}
3755

    
3756
		$__tmp_q = new dnqueue_class(); $q =& $__tmp_q;
3757
		$q->SetLink($path);
3758
		$q->SetEnabled("on");
3759
		$q->SetPipe($this->GetQname());
3760
		$q->SetParent($this);
3761
		$q->ReadConfig($queue);
3762
		$q->validate_input($queue, $input_errors);
3763

    
3764
		if (!is_array($input_errors)) {
3765
			$input_errors = array();
3766
		}
3767

    
3768
		if (count($input_errors)) {
3769
			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)));
3770
			return $q;
3771
		}
3772
		$number = dnqueue_find_nextnumber();
3773
		$q->SetNumber($number);
3774
		$this->subqueues[$q->GetQname()] = &$q;
3775

    
3776
		return $q;
3777
	}
3778

    
3779
	function &get_queue_list(&$q = null) {
3780
		$qlist = array();
3781

    
3782
		$qlist[$this->GetQname()] = $this->GetNumber();
3783
		if (is_array($this->subqueues)) {
3784
			foreach ($this->subqueues as $queue) {
3785
				$queue->get_queue_list($qlist);
3786
			}
3787
		}
3788
		return $qlist;
3789
	}
3790

    
3791
	/*
3792
	 * Should search even its children
3793
	 */
3794
	function &find_queue($pipe, $qname) {
3795
		if ($qname == $this->GetQname()) {
3796
			return $this;
3797
		}
3798
		foreach ($this->subqueues as $q) {
3799
			$result =& $q->find_queue("", $qname);
3800
			if ($result) {
3801
				return $result;
3802
			}
3803
		}
3804
	}
3805

    
3806
	function &find_parentqueue($pipe, $qname) {
3807
		return NULL;
3808
	}
3809

    
3810
	function validate_input($data, &$input_errors) {
3811
		parent::validate_input($data, $input_errors);
3812

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

    
3852
		/* Limiter patch */
3853
		$selectedScheduler = getSchedulers()[$data['sched']];
3854
		if (!$selectedScheduler) {
3855
			$input_errors[] = gettext("Selected scheduler not recognized.");
3856
		}
3857
		$selectedAqm = getAQMs()[$data['aqm']];
3858
		if (!empty($data['aqm']) && !$selectedAqm) {
3859
			$input_errors[] = gettext("Selected AQM not recognized.");
3860
		}
3861
		if ($data['ecn'] && $data['ecn'] == 'on' && !$selectedScheduler["ecn"] && !$selectedAqm["ecn"]) {
3862
			$input_errors[] = gettext("Explicit Congestion Notification is selected, but neither " . $selectedAqm["name"] . " nor " . $selectedScheduler["name"] . " support it.");
3863
		}
3864
		/* End limiter patch */
3865
	}
3866

    
3867
	function ReadConfig(&$q) {
3868
		if (!empty($q['name']) && !empty($q['newname']) && $q['name'] != $q['newname']) {
3869
			$this->SetQname($q['newname']);
3870
		} else if (!empty($q['newname'])) {
3871
			$this->SetQname($q['newname']);
3872
		} else {
3873
			$this->SetQname($q['name']);
3874
		}
3875
		$this->SetNumber($q['number']);
3876

    
3877
		if (!empty($_POST)) {
3878
			$bandwidth = array();
3879
			/* XXX: Really no better way? */
3880
			for ($i = 0; $i < 2900; $i++) {
3881
				if (isset($q["bandwidth{$i}"]) && $q["bandwidth{$i}"] <> "") {
3882
					$bw = array();
3883
					$bw['bw'] = $q["bandwidth{$i}"];
3884
					$bw['burst'] = $q["burst{$i}"];
3885
					if (isset($q["bwtype{$i}"]) && $q["bwtype{$i}"]) {
3886
						$bw['bwscale'] = $q["bwtype{$i}"];
3887
					}
3888
					if (isset($q["bwsched{$i}"]) && $q["bwsched{$i}"]) {
3889
						$bw['bwsched'] = $q["bwsched{$i}"];
3890
					}
3891
					$bandwidth[] = $bw;
3892
				}
3893
			}
3894
			$this->SetBandwidth($bandwidth);
3895
		}
3896

    
3897
		if (is_array($q['bandwidth']) && is_array($q['bandwidth']['item'])) {
3898
			$this->SetBandwidth($q['bandwidth']['item']);
3899
			$this->SetBurst($q['burst']['item']);
3900
		}
3901

    
3902
		if (isset($q['qlimit']) && $q['qlimit'] <> "") {
3903
			$this->SetQlimit($q['qlimit']);
3904
		} else {
3905
			$this->SetQlimit("");
3906
		}
3907
		if (isset($q['mask']) && $q['mask'] <> "") {
3908
			$masktype = $q['mask'];
3909
		} else {
3910
			$masktype = "";
3911
		}
3912
		if (isset($q['maskbits']) && $q['maskbits'] <> "") {
3913
			$maskbits = $q['maskbits'];
3914
		} else {
3915
			$maskbits = "";
3916
		}
3917
		if (isset($q['maskbitsv6']) && $q['maskbitsv6'] <> "") {
3918
			$maskbitsv6 = $q['maskbitsv6'];
3919
		} else {
3920
			$maskbitsv6 = "";
3921
		}
3922
		$this->SetMask(array("type" => $masktype, "bits" => $maskbits, "bitsv6" => $maskbitsv6));
3923
		if (isset($q['buckets']) && $q['buckets'] <> "") {
3924
			$this->SetBuckets($q['buckets']);
3925
		} else {
3926
			$this->SetBuckets("");
3927
		}
3928
		if (isset($q['plr']) && $q['plr'] <> "") {
3929
			$this->SetPlr($q['plr']);
3930
		} else {
3931
			$this->SetPlr("");
3932
		}
3933
		if (isset($q['delay']) && $q['delay'] <> "") {
3934
			$this->SetDelay($q['delay']);
3935
		} else {
3936
			$this->SetDelay(0);
3937
		}
3938
		if (isset($q['description']) && $q['description'] <> "") {
3939
			$this->SetDescription($q['description']);
3940
		} else {
3941
			$this->SetDescription("");
3942
		}
3943
		$this->SetEnabled($q['enabled']);
3944

    
3945
		/* Limiter patch */
3946
		if (isset($q['aqm']) && $q['aqm'] <> "") {
3947
				$this->SetAQM($q['aqm']);
3948
		} else {
3949
				$this->SetAQM('droptail');
3950
		}
3951
		// parse AQM arguments
3952
		$aqm_map = getAQMs();
3953
		$selectedParameters = $aqm_map[$this->getAQM()]["parameters"];
3954
		if (is_array($selectedParameters)) {
3955
			foreach ($selectedParameters as $key => $value) {
3956
				$config_key = 'param_' . $this->GetAQM() . '_' . $key;
3957
				if (isset($q[$config_key]) && $q[$config_key] <> "") {
3958
					$this->SetAQMParameter($key, $q[$config_key]);
3959
				} else {
3960
					$this->SetAQMParameter($key, $value["default"]);
3961
				}
3962
			}
3963
		}
3964

    
3965
		if (isset($q['sched']) && $q['sched'] <> "") {
3966
				$this->SetScheduler($q['sched']);
3967
		} else {
3968
				$this->SetScheduler('fifo');
3969
		}
3970
		$scheduler_map = getSchedulers();
3971
		$selectedParameters = $scheduler_map[$this->getScheduler()]["parameters"];
3972
		if (is_array($selectedParameters)) {
3973
			foreach ($selectedParameters as $key => $value) {
3974
				$config_key = 'param_' . $this->GetScheduler() . '_' . $key;
3975
				if (isset($q[$config_key]) && $q[$config_key] <> "") {
3976
					$this->SetSchedulerParameter($key, $q[$config_key]);
3977
				} else {
3978
					$this->SetSchedulerParameter($key, $value["default"]);
3979
				}
3980
			}
3981
		}
3982

    
3983
		// ecn flag
3984
		$this->SetECN($q['ecn']);
3985
		/* End limiter patch */
3986
	}
3987

    
3988
	function build_tree() {
3989
		$tree = " <li><a href=\"firewall_shaper_vinterface.php?pipe=" . htmlspecialchars($this->GetQname()) ."&amp;queue=".htmlspecialchars($this->GetQname()) ."&amp;action=show\">";
3990
		$tree .= htmlspecialchars($this->GetQname()) . "</a>";
3991
		if (is_array($this->subqueues)) {
3992
			$tree .= "<ul>";
3993
			foreach ($this->subqueues as $q) {
3994
				$tree .= $q->build_tree();
3995
			}
3996
			$tree .= "</ul>";
3997
		}
3998
		$tree .= "</li>";
3999

    
4000
		return $tree;
4001
	}
4002

    
4003
	function build_rules() {
4004
		global $config, $time_based_rules;
4005

    
4006
		if ($this->GetEnabled() == "") {
4007
			return;
4008
		}
4009

    
4010
		$pfq_rule = "\npipe ". $this->GetNumber() . " config ";
4011
		$found = false;
4012
		$bandwidth = $this->GetBandwidth();
4013
		if (is_array($bandwidth)) {
4014
			foreach ($bandwidth as $bw) {
4015
				if ($bw['bwsched'] != "none") {
4016
					$time_based_rules = true;
4017
					if (is_array($config['schedules']) && is_array($config['schedules']['schedule'])) {
4018
						foreach ($config['schedules']['schedule'] as $schedule) {
4019
							if ($bw['bwsched'] == $schedule['name']) {
4020
								if (filter_get_time_based_rule_status($schedule)) {
4021
									/* pipe throughputs must always be an integer, enforce that restriction again here. */
4022
									$pfq_rule .= " bw ".round(trim($bw['bw']),0).$bw['bwscale'];
4023
									if (is_numeric($bw['burst']) && ($bw['burst'] > 0)) {
4024
										$pfq_rule .= " burst ".trim($bw['burst']);
4025
									}
4026
									$found = true;
4027
									break;
4028
								}
4029
							}
4030
						}
4031
					} else {
4032
						$pfq_rule .= " bw 0";
4033
						$found = true;
4034
						break;
4035
					}
4036
				} else {
4037
					/* pipe throughputs must always be an integer, enforce that restriction again here. */
4038
					$pfq_rule .= " bw ".round(trim($bw['bw']), 0).$bw['bwscale'];
4039
					if (is_numeric($bw['burst']) && ($bw['burst'] > 0)) {
4040
						$pfq_rule .= " burst ".trim($bw['burst']);
4041
					}
4042
					$found = true;
4043
					break;
4044
				}
4045
			}
4046
			if ($found == false) {
4047
				$pfq_rule .= " bw 0";
4048
			}
4049
		} else {
4050
			$pfq_rule .= " bw 0";
4051
		}
4052

    
4053
		if ($this->GetQlimit()) {
4054
			$pfq_rule .= " queue " . $this->GetQlimit();
4055
		}
4056
		if ($this->GetPlr()) {
4057
			$pfq_rule .= " plr " . $this->GetPlr();
4058
		}
4059
		if ($this->GetBuckets()) {
4060
			$pfq_rule .= " buckets " . $this->GetBuckets();
4061
		}
4062
		if ($this->GetDelay()) {
4063
			$pfq_rule .= " delay " . $this->GetDelay();
4064
		}
4065
		$this->build_mask_rules($pfq_rule);
4066

    
4067
		/* Limiter patch */
4068
		$selectedAQM = getAQMs()[$this->getAQM()];
4069
		if ($selectedAQM) {
4070
			$pfq_rule .= " " . FormatParameters($selectedAQM["parameter_format"], $this->GetAQMParameters());
4071
			if ($selectedAQM["ecn"]) {
4072
				if ($this->getECN() == 'on') {
4073
					$pfq_rule .= ' ecn';
4074
				} else {
4075
					$pfq_rule .= ' noecn';
4076
				}
4077
			}
4078
		}
4079
		$selectedScheduler = getSchedulers()[$this->getScheduler()];
4080
		if ($selectedScheduler) {
4081
			$pfq_rule .= "\nsched ". $this->GetNumber() . " config pipe " . $this->GetNumber() . " " . FormatParameters($selectedScheduler["parameter_format"], $this->GetSchedulerParameters());
4082
			if ($selectedScheduler["ecn"]) {
4083
				if ($this->getECN() == 'on') {
4084
					$pfq_rule .= ' ecn';
4085
				} else {
4086
					$pfq_rule .= ' noecn';
4087
				}
4088
			}
4089
		}
4090
		$pfq_rule .= "\n";
4091
		/* End patch */
4092

    
4093
		if (!empty($this->subqueues) && count($this->subqueues) > 0) {
4094
			foreach ($this->subqueues as $q) {
4095
				$pfq_rule .= $q->build_rules();
4096
			}
4097
		}
4098

    
4099
		$pfq_rule .= " \n";
4100

    
4101
		return $pfq_rule;
4102
	}
4103

    
4104
	function update_dn_data(&$data) {
4105
		$this->ReadConfig($data);
4106
	}
4107

    
4108
	function build_javascript() {
4109
		global $g, $config;
4110

    
4111
		$javasr = parent::build_javascript();
4112

    
4113
		//build list of schedules
4114
		$schedules = "<option value='none'>none</option>";
4115
		if (is_array($config['schedules']) && is_array($config['schedules']['schedule'])) {
4116
			foreach ($config['schedules']['schedule'] as $schedule) {
4117
				if ($schedule['name'] <> "") {
4118
					$schedules .= "<option value='{$schedule['name']}'>{$schedule['name']}</option>";
4119
				}
4120
			}
4121
		}
4122
		$bwopt = "";
4123
		foreach (array("Kb" => "Kbit/s", "Mb" => "Mbit/s", "Gb" => "Gbit/s", "b" => "Bit/s") as $bwidx => $bw) {
4124
			$bwopt .= "<option value='{$bwidx}'>{$bw}</option>";
4125
		}
4126

    
4127
		$javasr .= <<<EOD
4128
<script type='text/javascript'>
4129
//<![CDATA[
4130
var addBwRowTo = (function() {
4131

    
4132
	return (function (tableId) {
4133

    
4134
	var table = document.getElementById(tableId);
4135
	var totalrows = table.rows.length -1;
4136

    
4137
	var row = table.insertRow(totalrows + 1);
4138
	var cell1 = row.insertCell(0);
4139
	var cell2 = row.insertCell(1);
4140
	var cell3 = row.insertCell(2);
4141
	var cell4 = row.insertCell(3);
4142

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

    
4148
	});
4149
})();
4150

    
4151
function removeBwRow(el) {
4152
	var d = el.parentNode.parentNode.rowIndex;
4153
	document.getElementById('maintable').deleteRow(d);
4154
}
4155

    
4156
function ceil_func(el){
4157
	el.value = Math.ceil(el.value);
4158

    
4159
}
4160
//]]>
4161
</script>
4162

    
4163
EOD;
4164

    
4165
		return $javasr;
4166
	}
4167

    
4168
	// Compose a table of bandwidths that can then be inserted into the form using a Form_StaticText
4169
	// The table has been "Bootstrapped" to match the web design while maintaining compatibility with
4170
	// with the javascript in this class
4171
	function build_bwtable() {
4172
		global $config;
4173

    
4174
		$bandwidth = $this->GetBandwidth();
4175
				//build list of schedules
4176
		$schedules = array();
4177
		$schedules[] = "none";//leave none to leave rule enabled all the time
4178
		if (is_array($config['schedules']) && is_array($config['schedules']['schedule'])) {
4179
			foreach ($config['schedules']['schedule'] as $schedule) {
4180
				if ($schedule['name'] != "") {
4181
					$schedules[] = $schedule['name'];
4182
				}
4183
			}
4184
		}
4185

    
4186
		$form = '<div class="table-responsive">';
4187
		$form .= '<table id="maintable" class="table table-hover table-striped">';
4188
		$form .= "<thead><tr>";
4189
		$form .= "<th>Bandwidth</th>";
4190
		//$form .= "<td width='35%'><div id='fifthcolumn'>Burst</div></td>";
4191
		$form .= "<th>Bw type</th>";
4192
		$form .= "<th>Schedule</th>";
4193
		$form .= "<th></th>";
4194
		$form .= "</tr></thead>";
4195
		$form .= "<tbody>";
4196

    
4197
		// If there are no bandwidths defined, make a blank one for convenience
4198
		if (empty($bandwidth)) {
4199
			$bandwidth = array(0 => array('bw' => '', 'bwscale' => 'Kb', 'bwsched' => 'none'));
4200
		}
4201

    
4202
		if (is_array($bandwidth)) {
4203
			foreach ($bandwidth as $bwidx => $bw) {
4204
				$form .= '<tr>';
4205
				$form .= '<td class="col-xs-4">';
4206
				$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\"/>";
4207
				//$form .= "</td><td width='20%'>";
4208
				//$form .= "<input class='formfld unknown' size='10' type=\"text\" id=\"burst{$bwidx}\" name=\"burst{$bwidx}\" value=\"{$bw['burst']}\" />";
4209
				$form .= "</td>";
4210
				$form .= '<td class="col-xs-4">';
4211
				$form .= "<select id=\"bwtype{$bwidx}\" name=\"bwtype{$bwidx}\" class=\"form-control\">";
4212

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

    
4216
					if ($bw['bwscale'] == $bwsidx) {
4217
						$form .= " selected";
4218
					}
4219

    
4220
					$form .= ">{$bwscale}</option>";
4221
				}
4222

    
4223
				$form .= "</select>";
4224
				$form .= "</td>";
4225
				$form .= '<td class="col-xs-4">';
4226
				$form .= "<select id=\"bwsched{$bwidx}\" name=\"bwsched{$bwidx}\" class=\"form-control\">";
4227

    
4228
				foreach ($schedules as $schd) {
4229
					$selected = "";
4230
					if ($bw['bwsched'] == $schd) {
4231
						$selected = "selected";
4232
					}
4233

    
4234
					$form .= "<option value='{$schd}' {$selected}>{$schd}</option>";
4235
				}
4236

    
4237
				$form .= "</select>";
4238
				$form .= "</td>";
4239
				$form .= '<td>';
4240
				$form .= '<a class="btn btn-warning" onclick="removeBwRow(this); return false;"><i class="fa fa-trash icon-embed-btn"></i>' . gettext('Delete') . '</a>';
4241
				$form .= "</td></tr>";
4242
			}
4243
		}
4244
		$form .= "</tbody></table></div><br />";
4245

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

    
4250
		return($form);
4251
	}
4252

    
4253
	function build_form() {
4254
		global $g, $config, $pipe, $action, $qname;
4255

    
4256
		//build list of schedules
4257
		$schedules = array();
4258
		$schedules[] = "none";//leave none to leave rule enabled all the time
4259
		if (is_array($config['schedules']) && is_array($config['schedules']['schedule'])) {
4260
			foreach ($config['schedules']['schedule'] as $schedule) {
4261
				if ($schedule['name'] <> "") {
4262
					$schedules[] = $schedule['name'];
4263
				}
4264
			}
4265
		}
4266

    
4267

    
4268
		$sform = new Form();
4269
		$sform->setAction("firewall_shaper.php");
4270

    
4271
		$section = new Form_Section('Limiters');
4272

    
4273
		$section->addInput(new Form_Checkbox(
4274
			'enabled',
4275
			'Enable',
4276
			'Enable limiter and its children',
4277
			($this->GetEnabled() == "on"),
4278
			'on'
4279
		));
4280

    
4281
		$section->addInput(new Form_Input(
4282
			'newname',
4283
			'*Name',
4284
			'text',
4285
			$this->GetQname()
4286
		));
4287

    
4288
		$section->addInput(new Form_Input(
4289
			'name',
4290
			null,
4291
			'hidden',
4292
			$this->GetQname()
4293
		));
4294

    
4295
		if ($this->GetNumber() > 0) {
4296
			$section->addInput(new Form_Input(
4297
				'number',
4298
				null,
4299
				'hidden',
4300
				$this->GetNumber()
4301
			));
4302
		}
4303

    
4304
		$bandwidth = $this->GetBandwidth();
4305

    
4306
		if (is_array($bandwidth)) {
4307
				$section->addInput(new Form_StaticText(
4308
				'Bandwidth',
4309
				$this->build_bwtable()
4310
			));
4311
		}
4312

    
4313
		$mask = $this->GetMask();
4314

    
4315
		$section->addInput(new Form_Select(
4316
			'mask',
4317
			'Mask',
4318
			$mask['type'],
4319
			array('none' => gettext('None'), 'srcaddress' => gettext('Source addresses'), 'dstaddress' => gettext('Destination addresses'))
4320
		))->setHelp('If "source" or "destination" slots is chosen a dynamic pipe with the bandwidth, delay, packet loss ' .
4321
					'and queue size given above will be created for each source/destination IP address encountered, respectively. ' .
4322
					'This makes it possible to easily specify bandwidth limits per host.');
4323

    
4324
		$group = new Form_Group(null);
4325

    
4326
		$group->add(new Form_Select(
4327
			'maskbits',
4328
			null,
4329
			$mask['bits'],
4330
			array_combine(range(32, 1, -1), range(32, 1, -1))
4331
		))->setHelp('IPv4 mask bits%1$s%2$s', '<br />', '255.255.255.255/?');
4332

    
4333
		$group->add(new Form_Select(
4334
			'maskbitsv6',
4335
			null,
4336
			$mask['bitsv6'],
4337
			array_combine(range(128, 1, -1), range(128, 1, -1))
4338
		))->setHelp('IPv6 mask bits%1$s%2$s', '<br />', '<span style="font-family:consolas">ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/?</span>');
4339

    
4340
		$section->add($group);
4341

    
4342
		$section->addInput(new Form_Input(
4343
			'description',
4344
			'Description',
4345
			'text',
4346
			$this->GetDescription()
4347
		))->setHelp('A description may be entered here for administrative reference (not parsed).');
4348

    
4349
		$sform->add($section);
4350

    
4351
		/* Begin limiter patch */
4352
		$aqm_map = getAQMs();
4353
		$scheduler_map = getSchedulers();
4354

    
4355
		$section = new Form_Section('Queue');
4356
		$section->addInput(new Form_Select(
4357
				'aqm',
4358
				'Queue Management Algorithm',
4359
				$this->GetAQM(),
4360
				array_map_assoc(function ($k, $v) {
4361
					return [$k, $v["name"]];
4362
				}, $aqm_map)
4363
		))->setHelp('Active queue management (AQM) is the intelligent drop of network packets inside the limiter, ' .
4364
						'when it becomes full or gets close to becoming full, with the goal of reducing ' .
4365
						'network congestion.');
4366

    
4367
		$section->addInput(new Form_StaticText(
4368
			'',
4369
			build_queue_params($aqm_map, $this->GetAQM(), $this->GetAQMParameters(), "aqm")
4370
		))->setHelp('Specifies the queue management algorithm parameters.');
4371

    
4372
		$section->addInput(new Form_Select(
4373
				'sched',
4374
				'Scheduler',
4375
				$this->GetScheduler(),
4376
				array_map_assoc(function ($k, $v) {
4377
					return [$k, $v["name"]];
4378
				}, $scheduler_map)
4379
		))->setHelp('The scheduler manages the sequence of network packets in the limiter\'s queue.');
4380

    
4381
		$section->addInput(new Form_StaticText(
4382
			'',
4383
			build_queue_params($scheduler_map, $this->GetScheduler(), $this->GetSchedulerParameters(), "sched")
4384
		))->setHelp('Specifies the scheduler parameters.');
4385

    
4386
		$section->addInput(new Form_Input(
4387
				'qlimit',
4388
				'Queue length',
4389
				'number',
4390
				$this->GetQlimit()
4391
		))->setHelp('Specifies the length of the limiter\'s queue, which the scheduler and AQM are responsible for. ' .
4392
			'This field may be left empty.');
4393

    
4394
		$section->addInput(new Form_Checkbox(
4395
			'ecn',
4396
			'ECN',
4397
			'Enable Explicit Congestion Notification (ECN)',
4398
			($this->GetECN() == "on"),
4399
			'on'
4400
		))->setHelp('ECN sets a reserved TCP flag when the queue is nearing or exceeding capacity. Not all AQMs or schedulers support this.');
4401

    
4402
		$sform->add($section);
4403
		/* End limiter patch */
4404

    
4405
		$section = new Form_Section('Advanced Options');
4406

    
4407
		$section->addInput(new Form_Input(
4408
			'delay',
4409
			'Delay (ms)',
4410
			'text',
4411
			$this->GetDelay() > 0 ? $this->GetDelay():null
4412
		))->setHelp('In most cases, zero (0) should specified here (or leave the field empty).');
4413

    
4414
		$section->addInput(new Form_Input(
4415
			'plr',
4416
			'Packet Loss Rate',
4417
			'number',
4418
			$this->GetPlr(),
4419
			['step' => '0.001', 'min' => '0.000']
4420
		))->setHelp('In most cases, zero (0) should be specified here (or leave the field empty). ' .
4421
					'A value of 0.001 means one packet in 1000 gets dropped.');
4422

    
4423
		$section->addInput(new Form_Input(
4424
			'buckets',
4425
			'Bucket size (slots)',
4426
			'number',
4427
			$this->GetBuckets()
4428
		))->setHelp('In most cases, this field should be left empty. It increases the hash size set.');
4429

    
4430
		$sform->add($section);
4431

    
4432
		return($sform);
4433
		}
4434

    
4435
	function wconfig() {
4436
		$cflink =& get_dn_reference_to_me_in_config($this->GetLink());
4437
		if (!is_array($cflink)) {
4438
			$cflink = array();
4439
		}
4440
		$cflink['name'] = $this->GetQname();
4441
		$cflink['number'] = $this->GetNumber();
4442
		$cflink['qlimit'] = $this->GetQlimit();
4443
		$cflink['plr'] = $this->GetPlr();
4444
		$cflink['description'] = $this->GetDescription();
4445

    
4446
		$bandwidth = $this->GetBandwidth();
4447
		if (is_array($bandwidth)) {
4448
			$cflink['bandwidth'] = array();
4449
			$cflink['bandwidth']['item'] = array();
4450
			foreach ($bandwidth as $bwidx => $bw) {
4451
				$cflink['bandwidth']['item'][] = $bw;
4452
			}
4453
		}
4454

    
4455
		$cflink['enabled'] = $this->GetEnabled();
4456
		$cflink['buckets'] = $this->GetBuckets();
4457
		$mask = $this->GetMask();
4458
		$cflink['mask'] = $mask['type'];
4459
		$cflink['maskbits'] = $mask['bits'];
4460
		$cflink['maskbitsv6'] = $mask['bitsv6'];
4461
		$cflink['delay'] = $this->GetDelay();
4462

    
4463
		/* Limiter queue patch */
4464
		$cflink['sched'] = $this->GetScheduler();
4465
		$scheduler_map = getSchedulers();
4466
		$selectedParameters = $scheduler_map[$this->getScheduler()]["parameters"];
4467
		foreach ($selectedParameters as $key => $value) {
4468
			$config_key = 'param_' . $this->GetScheduler() . '_' . $key;
4469
			$cflink[$config_key] = $this->GetSchedulerParameter($key);
4470
		}
4471
		$cflink['aqm'] = $this->GetAQM();
4472
		$aqm_map = GetAQMs();
4473
		$selectedParameters = $aqm_map[$this->getAQM()]["parameters"];
4474
		foreach ($selectedParameters as $key => $value) {
4475
			$config_key = 'param_' . $this->GetAQM() . '_' . $key;
4476
			$cflink[$config_key] = $this->GetAQMParameter($key);
4477
		}
4478
		$cflink['ecn'] = $this->GetECN();
4479
		/* End limiter queue patch */
4480
	}
4481

    
4482
}
4483

    
4484
class dnqueue_class extends dummynet_class {
4485
	var $pipeparent;
4486
	var $weight;
4487
	/* Limiter queue patch */
4488
    var $ecn; // ecn 'on' or 'off'
4489
    var $aqm; // key to aqm_map
4490
    var $aqm_params = array(); // AQM params
4491
	function GetAQM() {
4492
			return $this->aqm;
4493
	}
4494
	function SetAQM($aqm) {
4495
			$this->aqm = $aqm;
4496
	}
4497
	function GetAQMParameters() {
4498
			return $this->aqm_params;
4499
	}
4500
	function GetAQMParameter($parameter) {
4501
			return $this->aqm_params[$parameter];
4502
	}
4503
	function SetAQMParameter($key, $value) {
4504
			return $this->aqm_params[$key] = $value;
4505
	}
4506
	function SetAQMParameters($params) {
4507
			$this->aqm_params = $params;
4508
	}
4509
	function GetECN() {
4510
			return $this->ecn;
4511
	}
4512
	function SetECN($ecn) {
4513
			$this->ecn = $ecn;
4514
	}
4515
	/* End limiter queue patch */
4516

    
4517
	function GetWeight() {
4518
		return $this->weight;
4519
	}
4520
	function SetWeight($weight) {
4521
		$this->weight = $weight;
4522
	}
4523
	function GetPipe() {
4524
		return $this->pipeparent;
4525
	}
4526
	function SetPipe($pipe) {
4527
		$this->pipeparent = $pipe;
4528
	}
4529

    
4530
	/* Just a stub in case we ever try to call this from the frontend. */
4531
	function &add_queue($interface, &$queue, &$path, &$input_errors) {
4532
		return;
4533
	}
4534

    
4535
	function delete_queue() {
4536
		cleanup_dnqueue_from_rules($this->GetQname());
4537
		unset_dn_object_by_reference($this->GetLink());
4538
		@pfSense_ipfw_pipe("queue delete " . $this->GetNumber());
4539
	}
4540

    
4541
	function validate_input($data, &$input_errors) {
4542
		parent::validate_input($data, $input_errors);
4543

    
4544

    
4545
		/* Limiter patch */
4546
		$selectedAqm = getAQMs()[$data['aqm']];
4547
		if (!empty($data['aqm']) && !$selectedAqm) {
4548
			$input_errors[] = gettext("Selected AQM not recognized.");
4549
		}
4550
		if ($data['ecn'] && $data['ecn'] == 'on' && !$selectedAqm["ecn"]) {
4551
			$input_errors[] = gettext("Explicit Congestion Notification is selected, but " . $selectedAqm["name"] . " does not support it.");
4552
		}
4553
		/* End limiter patch */
4554

    
4555
		if ($data['weight'] && ((!is_numeric($data['weight'])) ||
4556
		    ($data['weight'] < 1 && $data['weight'] > 100))) {
4557
			$input_errors[] = gettext("Weight must be an integer between 1 and 100.");
4558
		}
4559
	}
4560

    
4561
	/*
4562
	 * Should search even its children
4563
	 */
4564
	function &find_queue($pipe, $qname) {
4565
		if ($qname == $this->GetQname()) {
4566
			return $this;
4567
		} else {
4568
			return NULL;
4569
		}
4570
	}
4571

    
4572
	function &find_parentqueue($pipe, $qname) {
4573
		return $this->qparent;
4574
	}
4575

    
4576
	function &get_queue_list(&$qlist) {
4577
		if ($this->GetEnabled() == "") {
4578
			return;
4579
		}
4580
		$qlist[$this->GetQname()] = "?" .$this->GetNumber();
4581
	}
4582

    
4583
	function ReadConfig(&$q) {
4584
		if (!empty($q['name']) && !empty($q['newname']) && $q['name'] != $q['newname']) {
4585
			$this->SetQname($q['newname']);
4586
		} else if (!empty($q['newname'])) {
4587
			$this->SetQname($q['newname']);
4588
		} else {
4589
			$this->SetQname($q['name']);
4590
		}
4591
		$this->SetNumber($q['number']);
4592
		if (isset($q['qlimit']) && $q['qlimit'] <> "") {
4593
			$this->SetQlimit($q['qlimit']);
4594
		} else {
4595
			$this->SetQlimit("");
4596
		}
4597
		if (isset($q['mask']) && $q['mask'] <> "") {
4598
			$masktype = $q['mask'];
4599
		} else {
4600
			$masktype = "";
4601
		}
4602
		if (isset($q['maskbits']) && $q['maskbits'] <> "") {
4603
			$maskbits = $q['maskbits'];
4604
		} else {
4605
			$maskbits = "";
4606
		}
4607
		if (isset($q['maskbitsv6']) && $q['maskbitsv6'] <> "") {
4608
			$maskbitsv6 = $q['maskbitsv6'];
4609
		} else {
4610
			$maskbitsv6 = "";
4611
		}
4612
		$this->SetMask(array("type" => $masktype, "bits" => $maskbits, "bitsv6" => $maskbitsv6));
4613
		if (isset($q['buckets']) && $q['buckets'] <> "") {
4614
			$this->SetBuckets($q['buckets']);
4615
		} else {
4616
			$this->SetBuckets("");
4617
		}
4618
		if (isset($q['plr']) && $q['plr'] <> "") {
4619
			$this->SetPlr($q['plr']);
4620
		} else {
4621
			$this->SetPlr("");
4622
		}
4623
		if (isset($q['weight']) && $q['weight'] <> "") {
4624
			$this->SetWeight($q['weight']);
4625
		} else {
4626
			$this->SetWeight("");
4627
		}
4628
		if (isset($q['description']) && $q['description'] <> "") {
4629
			$this->SetDescription($q['description']);
4630
		} else {
4631
			$this->SetDescription("");
4632
		}
4633
		$this->SetEnabled($q['enabled']);
4634

    
4635
		/* Limiter patch */
4636
		if (isset($q['aqm']) && $q['aqm'] <> "") {
4637
				$this->SetAQM($q['aqm']);
4638
		} else {
4639
				$this->SetAQM('droptail');
4640
		}
4641
		// parse AQM arguments
4642
		$aqm_map = getAQMs();
4643
		$selectedParameters = $aqm_map[$this->getAQM()]["parameters"];
4644
		foreach ($selectedParameters as $key => $value) {
4645
			$config_key = 'param_' . $this->GetAQM() . '_' . $key;
4646
			if (isset($q[$config_key]) && $q[$config_key] <> "") {
4647
				$this->SetAQMParameter($key, $q[$config_key]);
4648
			} else {
4649
				$this->SetAQMParameter($key, $value["default"]);
4650
			}
4651
		}
4652

    
4653
		// ecn flag
4654
		$this->SetECN($q['ecn']);
4655
		/* End limiter patch */
4656
	}
4657

    
4658
	function build_tree() {
4659
		$parent =& $this->GetParent();
4660
		$tree = " <li><a href=\"firewall_shaper_vinterface.php?pipe=" . htmlspecialchars($parent->GetQname()) ."&amp;queue=" . htmlspecialchars($this->GetQname()) ."&amp;action=show\">";
4661
		$tree .= htmlspecialchars($this->GetQname()) . "</a>";
4662
		$tree .= "</li>";
4663

    
4664
		return $tree;
4665
	}
4666

    
4667
	function build_rules() {
4668
		if ($this->GetEnabled() == "") {
4669
			return;
4670
		}
4671

    
4672
		$parent =& $this->GetParent();
4673
		$pfq_rule = "queue ". $this->GetNumber() . " config pipe " . $parent->GetNumber();
4674
		if ($this->GetQlimit()) {
4675
			$pfq_rule .= " queue " . $this->GetQlimit();
4676
		}
4677
		if ($this->GetWeight()) {
4678
			$pfq_rule .= " weight " . $this->GetWeight();
4679
		}
4680
		if ($this->GetBuckets()) {
4681
			$pfq_rule .= " buckets " . $this->GetBuckets();
4682
		}
4683
		$this->build_mask_rules($pfq_rule);
4684

    
4685
		/* Limiter patch */
4686
		$selectedAQM = getAQMs()[$this->getAQM()];
4687
		if ($selectedAQM) {
4688
			$pfq_rule .= " " . FormatParameters($selectedAQM["parameter_format"], $this->GetAQMParameters());
4689
			if ($selectedAQM["ecn"]) {
4690
				if ($this->getECN() == 'on') {
4691
					$pfq_rule .= ' ecn';
4692
				} else {
4693
					$pfq_rule .= ' noecn';
4694
				}
4695
			}
4696
		}
4697
		/* End patch */
4698

    
4699
		$pfq_rule .= "\n";
4700

    
4701
		return $pfq_rule;
4702
	}
4703

    
4704
	function build_javascript() {
4705
		return parent::build_javascript();
4706
	}
4707

    
4708
	function build_form() {
4709
		global $g, $config, $pipe, $action, $qname;
4710

    
4711
		//build list of schedules
4712
		$schedules = array();
4713
		$schedules[] = "none";//leave none to leave rule enabled all the time
4714
		if (is_array($config['schedules']) && is_array($config['schedules']['schedule'])) {
4715
			foreach ($config['schedules']['schedule'] as $schedule) {
4716
				if ($schedule['name'] <> "") {
4717
					$schedules[] = $schedule['name'];
4718
				}
4719
			}
4720
		}
4721

    
4722

    
4723
		$sform = new Form();
4724
		$sform->setAction("firewall_shaper.php");
4725
		$section = new Form_Section('Limiters');
4726

    
4727
		$section->addInput(new Form_Checkbox(
4728
			'enabled',
4729
			'Enable',
4730
			'Enable this queue',
4731
			($this->GetEnabled() == "on"),
4732
			'on'
4733
		));
4734

    
4735
		$section->addInput(new Form_Input(
4736
			'newname',
4737
			'*Name',
4738
			'text',
4739
			$this->GetQname()
4740
		));
4741

    
4742
		$section->addInput(new Form_Input(
4743
			'name',
4744
			null,
4745
			'hidden',
4746
			$this->GetQname()
4747
		));
4748

    
4749
		if ($this->GetNumber() > 0) {
4750
			$section->addInput(new Form_Input(
4751
				'number',
4752
				null,
4753
				'hidden',
4754
				$this->GetNumber()
4755
			));
4756
		}
4757

    
4758
		$mask = $this->GetMask();
4759

    
4760
		$section->addInput(new Form_Select(
4761
			'mask',
4762
			'Mask',
4763
			$mask['type'],
4764
			array('none' => gettext('None'), 'srcaddress' => gettext('Source addresses'), 'dstaddress' => gettext('Destination addresses'))
4765
		))->setHelp('If "source" or "destination" slots is chosen a dynamic pipe with the bandwidth, delay, packet loss ' .
4766
					'and queue size given above will be created for each source/destination IP address encountered, respectively. ' .
4767
					'This makes it possible to easily specify bandwidth limits per host.');
4768

    
4769
		$group = new Form_Group(null);
4770

    
4771
		$group->add(new Form_Select(
4772
			'maskbits',
4773
			null,
4774
			$mask['bits'],
4775
			array_combine(range(32, 1, -1), range(32, 1, -1))
4776
		))->setHelp('IPv4 mask bits%1$s%2$s', '<br />', '255.255.255.255/?');
4777

    
4778
		$group->add(new Form_Select(
4779
			'maskbitsv6',
4780
			null,
4781
			$mask['bitsv6'],
4782
			array_combine(range(128, 1, -1), range(128, 1, -1))
4783
		))->setHelp('IPv6 mask bits%1$s%2$s', '<br />', '<span style="font-family:consolas">ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/?</span>');
4784

    
4785
		$section->add($group);
4786

    
4787
		$section->addInput(new Form_Input(
4788
			'description',
4789
			'Description',
4790
			'text',
4791
			$this->GetDescription()
4792
		))->setHelp('A description may be entered here for administrative reference (not parsed).');
4793

    
4794
		$sform->add($section);
4795

    
4796
		/* Begin limiter patch */
4797
		$aqm_map = getAQMs();
4798

    
4799
		$section = new Form_Section('Queue');
4800
		$section->addInput(new Form_Select(
4801
				'aqm',
4802
				'Queue Management Algorithm',
4803
				$this->GetAQM(),
4804
				array_map_assoc(function ($k, $v) {
4805
					return [$k, $v["name"]];
4806
				}, $aqm_map)
4807
		))->setHelp('Active queue management (AQM) is the intelligent drop of network packets inside this limiter\'s queue, ' .
4808
						'when it becomes full or gets close to becoming full, with the goal of reducing ' .
4809
						'network congestion.');
4810

    
4811
		$section->addInput(new Form_StaticText(
4812
			'',
4813
			build_queue_params($aqm_map, $this->GetAQM(), $this->GetAQMParameters(), "aqm")
4814
		))->setHelp('Specifies the queue management algorithm parameters.');
4815

    
4816
		$section->addInput(new Form_Input(
4817
				'qlimit',
4818
				'Queue length',
4819
				'number',
4820
				$this->GetQlimit()
4821
		))->setHelp('Specifies the length of this queue, which the AQM is responsible for.  This field may be left empty.');
4822

    
4823
		$section->addInput(new Form_Checkbox(
4824
			'ecn',
4825
			'ECN',
4826
			'Enable Explicit Congestion Notification (ECN)',
4827
			($this->GetECN() == "on"),
4828
			'on'
4829
		))->setHelp('ECN sets a reserved TCP flag when the queue is nearing or exceeding capacity. Not all AQMs or schedulers support this.');
4830

    
4831
		$sform->add($section);
4832
		/* End limiter patch */
4833

    
4834
		$section = new Form_Section('Advanced Options');
4835

    
4836
		$section->addInput(new Form_Input(
4837
			'weight',
4838
			'Weight',
4839
			'number',
4840
			$this->GetWeight(),
4841
			['min' => '1', 'max' => '100']
4842
		))->setHelp('For queues under the same parent this specifies the share that a queue gets(values range from 1 to 100),' .
4843
					' it can be left blank otherwise.');
4844

    
4845
		$section->addInput(new Form_Input(
4846
			'plr',
4847
			'Packet Loss Rate',
4848
			'number',
4849
			$this->GetPlr(),
4850
			['step' => '0.001', 'min' => '0.000']
4851
		))->setHelp('In most cases, zero (0) should be specified here (or leave the field empty). ' .
4852
					'A value of 0.001 means one packet in 1000 gets dropped');
4853

    
4854
		$section->addInput(new Form_Input(
4855
			'buckets',
4856
			'Bucket size (slots)',
4857
			'number',
4858
			$this->GetBuckets()
4859
		))->setHelp('In most cases, this field should be left empty. It increases the hash size set');
4860

    
4861
		$section->addInput(new Form_Input(
4862
			'pipe',
4863
			null,
4864
			'hidden',
4865
			$this->GetPipe()
4866
		));
4867

    
4868
		$sform->add($section);
4869

    
4870
		return($sform);
4871
	}
4872

    
4873
	function update_dn_data(&$data) {
4874
		$this->ReadConfig($data);
4875
	}
4876

    
4877
	function wconfig() {
4878
		$cflink =& get_dn_reference_to_me_in_config($this->GetLink());
4879
		if (!is_array($cflink)) {
4880
			$cflink = array();
4881
		}
4882
		$cflink['name'] = $this->GetQname();
4883
		$cflink['number'] = $this->GetNumber();
4884
		$cflink['qlimit'] = $this->GetQlimit();
4885
		$cflink['description'] = $this->GetDescription();
4886
		$cflink['weight'] = $this->GetWeight();
4887
		$cflink['enabled'] = $this->GetEnabled();
4888
		$cflink['buckets'] = $this->GetBuckets();
4889
		$mask = $this->GetMask();
4890
		$cflink['mask'] = $mask['type'];
4891
		$cflink['maskbits'] = $mask['bits'];
4892
		$cflink['maskbitsv6'] = $mask['bitsv6'];
4893

    
4894
		/* Limiter queue patch */
4895
		$cflink['aqm'] = $this->GetAQM();
4896
		$aqm_map = GetAQMs();
4897
		$selectedParameters = $aqm_map[$this->getAQM()]["parameters"];
4898
		foreach ($selectedParameters as $key => $value) {
4899
			$config_key = 'param_' . $this->GetAQM() . '_' . $key;
4900
			$cflink[$config_key] = $this->GetAQMParameter($key);
4901
		}
4902
		$cflink['ecn'] = $this->GetECN();
4903
		/* End limiter queue patch */
4904
	}
4905
}
4906

    
4907
function get_dummynet_name_list() {
4908

    
4909
	$dn_name_list =& get_unique_dnqueue_list();
4910
	$dn_name = array();
4911
	if (is_array($dn_name_list)) {
4912
		foreach ($dn_name_list as $key => $value) {
4913
			$dn_name[] = $key;
4914
		}
4915
	}
4916

    
4917
	return $dn_name;
4918

    
4919
}
4920

    
4921
function get_altq_name_list() {
4922
	$altq_name_list =& get_unique_queue_list();
4923
	$altq_name = array();
4924
	if (is_array($altq_name_list)) {
4925
		foreach ($altq_name_list as $key => $aqobj) {
4926
			$altq_name[] = $key;
4927
		}
4928
	}
4929

    
4930
	return $altq_name;
4931
}
4932

    
4933
/*
4934
 * XXX: TODO Make a class shaper to hide all these functions
4935
 * from the global namespace.
4936
 */
4937

    
4938
/*
4939
 * This is a layer violation but for now there is no way
4940
 * I can find to properly do this with PHP.
4941
 */
4942
function altq_get_default_queue($interface) {
4943
	global $altq_list_queues;
4944

    
4945
	$altq_tmp = $altq_list_queues[$interface];
4946
	if ($altq_tmp) {
4947
		return $altq_tmp->GetDefaultQueuePresent();
4948
	} else {
4949
		return false;
4950
	}
4951
}
4952

    
4953
function altq_check_default_queues() {
4954
	global $altq_list_queues;
4955

    
4956
	$count = 0;
4957
	if (is_array($altq_list_queues)) {
4958
		foreach ($altq_list_queues as $altq) {
4959
			if ($altq->GetDefaultQueuePresent()) {
4960
				$count++;
4961
			}
4962
		}
4963
	}
4964
	else {
4965
		$count++;
4966
	}
4967

    
4968
	return 0;
4969
}
4970

    
4971
function &get_unique_queue_list() {
4972
	global $altq_list_queues;
4973

    
4974
	$qlist = array();
4975
	if (is_array($altq_list_queues)) {
4976
		foreach ($altq_list_queues as $altq) {
4977
			if ($altq->GetEnabled() == "") {
4978
				continue;
4979
			}
4980
			$tmplist =& $altq->get_queue_list();
4981
			foreach ($tmplist as $qname => $link) {
4982
				if ($link->GetEnabled() <> "") {
4983
					$qlist[$qname] = $link;
4984
				}
4985
			}
4986
		}
4987
	}
4988
	return $qlist;
4989
}
4990

    
4991
function &get_unique_dnqueue_list() {
4992
	global $dummynet_pipe_list;
4993

    
4994
	$qlist = array();
4995
	if (is_array($dummynet_pipe_list)) {
4996
		foreach ($dummynet_pipe_list as $dn) {
4997
			if ($dn->GetEnabled() == "") {
4998
				continue;
4999
			}
5000
			$tmplist =& $dn->get_queue_list();
5001
			foreach ($tmplist as $qname => $link) {
5002
				$qlist[$qname] = $link;
5003
			}
5004
		}
5005
	}
5006
	return $qlist;
5007
}
5008

    
5009
function ref_on_altq_queue_list($parent, $qname) {
5010
	if (isset($GLOBALS['queue_list'][$qname])) {
5011
		$GLOBALS['queue_list'][$qname]++;
5012
	} else {
5013
		$GLOBALS['queue_list'][$qname] = 1;
5014
	}
5015

    
5016
	unref_on_altq_queue_list($parent);
5017
}
5018

    
5019
function unref_on_altq_queue_list($qname) {
5020
	$GLOBALS['queue_list'][$qname]--;
5021
	if ($GLOBALS['queue_list'][$qname] <= 1) {
5022
		unset($GLOBALS['queue_list'][$qname]);
5023
	}
5024
}
5025

    
5026
function read_altq_config() {
5027
	global $altq_list_queues, $config;
5028
	$path = array();
5029

    
5030
	init_config_arr(array('shaper', 'queue'));
5031
	$a_int = &$config['shaper']['queue'];
5032

    
5033
	$altq_list_queues = array();
5034

    
5035
	if (!is_array($config['shaper']['queue'])) {
5036
		return;
5037
	}
5038

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

    
5062
function read_dummynet_config() {
5063
	global $dummynet_pipe_list, $config;
5064
	$path = array();
5065

    
5066
	init_config_arr(array('dnshaper', 'queue'));
5067
	$a_int = &$config['dnshaper']['queue'];
5068

    
5069
	$dummynet_pipe_list = array();
5070

    
5071
	if (!count($config['dnshaper']['queue'])) {
5072
		return;
5073
	}
5074

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

    
5099
function get_interface_list_to_show() {
5100
	global $altq_list_queues, $config;
5101
	global $shaperIFlist;
5102

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

    
5115
	return $tree;
5116
}
5117

    
5118
function filter_generate_altq_queues() {
5119
	global $altq_list_queues;
5120

    
5121
	read_altq_config();
5122

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

    
5128
	return $altq_rules;
5129
}
5130

    
5131
function dnqueue_find_nextnumber() {
5132
	global $dummynet_pipe_list;
5133

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

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

    
5159
	if ($found == false) {
5160
		$dnnumber++;
5161
	}
5162

    
5163
	unset($dnused, $dnnum, $found);
5164
	return $dnnumber;
5165
}
5166

    
5167
function dnpipe_find_nextnumber() {
5168
	global $dummynet_pipe_list;
5169

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

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

    
5188
	if ($found == false) {
5189
		$dnnumber++;
5190
	}
5191

    
5192
	unset($dnused, $dnnum, $found);
5193
	return $dnnumber;
5194
}
5195

    
5196
function filter_generate_dummynet_rules() {
5197
	global $g, $dummynet_pipe_list;
5198

    
5199
	read_dummynet_config();
5200

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

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

    
5231
	$altq =& $altq_list_queues[$iface];
5232

    
5233
	if ($altq) {
5234
		$scheduler = $altq->GetScheduler();
5235
	}
5236

    
5237
	$form = '<dl class="dl-horizontal">';
5238

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

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

    
5257
	$form .= '</dl>';
5258

    
5259
	return $form;
5260

    
5261
}
5262

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

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

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

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

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

    
5286
	return false;
5287
}
5288
?>
(47-47/60)