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-2019 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
				$newqueue["pkts"] = trim(substr($line, 10, 10));
288
				$newqueue["bytes"] = trim(substr($line, 29, 10));
289
				$newqueue["droppedpkts"] = trim(substr($line, 55, 6));
290
				$newqueue["droppedbytes"] = trim(substr($line, 69, 6));
291
			}
292
			if ($items[3] == "qlength:") {
293
				$subitems = explode("/", substr($line, 13, 8));
294
				$newqueue["qlengthitems"] = trim($subitems[0]);
295
				$newqueue["qlengthsize"] = trim($subitems[1]);
296
				if (substr($line, 22, 8) == "borrows:") {
297
					$newqueue["borrows"] = trim(substr($line, 31, 7));
298
				}
299
				if (substr($line, 39, 9) == "suspends:") {
300
					$newqueue["suspends"] = trim(substr($line, 49, 7));
301
				}
302
			}
303
		}
304
	}
305
	$result['interfacestats'] = $interfacestats;
306
	return $result;
307
}
308

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

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

    
330
	return $ptr;
331
}
332

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

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

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

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

    
353
	return $ptr;
354
}
355

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

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

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

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

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

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

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

    
479
	return floatval($objbw);
480
}
481

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

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

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

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

    
526
	return floatval($objbw);
527
}
528

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
708
		return 0;
709
	}
710

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

    
719
		return $sum;
720
	}
721

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

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

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

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

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

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

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

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

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

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

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

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

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

    
868
		return $q;
869
	}
870

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

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

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

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

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

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

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

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

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

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

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

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

    
1027
		return $javascript;
1028
	}
1029

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

    
1033
		$altq =& $this;
1034

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

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

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

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

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

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

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

    
1069
		return $form;
1070

    
1071
	}
1072

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

    
1079
		$sform = new Form();
1080

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

    
1083
		$section = new Form_Section(null);
1084

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

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

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

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

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

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

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

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

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

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

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

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

    
1162
		return($sform);
1163
	}
1164

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

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

    
1197
}
1198

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1424
		return $javascript;
1425
	}
1426

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

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

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

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

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

    
1465
	function &get_queue_list(&$qlist) {
1466

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

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

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

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

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

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

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

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

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

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

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

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

    
1625
		return $tree;
1626
	}
1627

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

    
1686
		$pfq_rule .= " \n";
1687

    
1688
		return $pfq_rule;
1689
	}
1690

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

    
1699
	function build_form() {
1700

    
1701
		$sform = new Form();
1702

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

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

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

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

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

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

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

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

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

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

    
1765
		$group->add(new Form_Checkbox(
1766
			'rio',
1767
			null,
1768
			null,
1769
			!empty($this->GetRio())
1770
		))->setHelp('%1$sRandom Early Detection In and Out%2$s', '<a target="_new" href="https://web.archive.org/web/20160404153707/http://www.openbsd.org/faq/pf/queueing.html#rio">', '</a>');
1771

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

    
1779
		$group->add(new Form_Checkbox(
1780
			'codel',
1781
			null,
1782
			null,
1783
			!empty($this->GetCodel())
1784
		))->setHelp('%1$sCodel Active Queue%2$s', '<a target="_new" href="https://web.archive.org/web/20160404153707/http://www.openbsd.org/faq/pf/queueing.html#ecn">', '</a>');
1785

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

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

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

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

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

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

    
1813
		return($sform);
1814
	}
1815

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

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

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

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

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

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

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

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

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

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

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

    
1877
		return $form;
1878

    
1879
	}
1880

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

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

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

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

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

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

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

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

    
2060
		return $q;
2061
	}
2062

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
2500
		$pfq_rule .= " \n";
2501

    
2502
		return $pfq_rule;
2503
	}
2504

    
2505
	function build_javascript() {
2506

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

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

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

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

    
2528
		enable_upperlimit();
2529

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

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

    
2541
		enable_realtime();
2542

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

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

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

    
2560
		return $javascript;
2561
	}
2562

    
2563
	function build_form() {
2564

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

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

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

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

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

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

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

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

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

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

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

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

    
2624

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
2699
		return($sform);
2700
	}
2701

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

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

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

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

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

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

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

    
2859
		return $q;
2860
	}
2861

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
3116
		return $sform;
3117
	}
3118

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

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

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

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

    
3195

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
3458

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

    
3463

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

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

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

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

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

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

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

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

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

    
3658
}
3659

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

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

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

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

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

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

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

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

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

    
3775
		return $q;
3776
	}
3777

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

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

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

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

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

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

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

    
3862
	function ReadConfig(&$q) {
3863
		if (!empty($q['name']) && !empty($q['newname']) && $q['name'] != $q['newname']) {
3864
			$this->SetQname($q['newname']);
3865
		} else if (!empty($q['newname'])) {
3866
			$this->SetQname($q['newname']);
3867
		} else {
3868
			$this->SetQname($q['name']);
3869
		}
3870
		$this->SetNumber($q['number']);
3871

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

    
3892
		if (is_array($q['bandwidth']) && is_array($q['bandwidth']['item'])) {
3893
			$this->SetBandwidth($q['bandwidth']['item']);
3894
			$this->SetBurst($q['burst']['item']);
3895
		}
3896

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

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

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

    
3978
		// ecn flag
3979
		$this->SetECN($q['ecn']);
3980
		/* End limiter patch */
3981
	}
3982

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

    
3995
		return $tree;
3996
	}
3997

    
3998
	function build_rules() {
3999
		global $config, $time_based_rules;
4000

    
4001
		if ($this->GetEnabled() == "") {
4002
			return;
4003
		}
4004

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

    
4048
		if ($this->GetQlimit()) {
4049
			$pfq_rule .= " queue " . $this->GetQlimit();
4050
		}
4051
		if ($this->GetPlr()) {
4052
			$pfq_rule .= " plr " . $this->GetPlr();
4053
		}
4054
		if ($this->GetBuckets()) {
4055
			$pfq_rule .= " buckets " . $this->GetBuckets();
4056
		}
4057
		if ($this->GetDelay()) {
4058
			$pfq_rule .= " delay " . $this->GetDelay();
4059
		}
4060
		$this->build_mask_rules($pfq_rule);
4061

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

    
4088
		if (!empty($this->subqueues) && count($this->subqueues) > 0) {
4089
			foreach ($this->subqueues as $q) {
4090
				$pfq_rule .= $q->build_rules();
4091
			}
4092
		}
4093

    
4094
		$pfq_rule .= " \n";
4095

    
4096
		return $pfq_rule;
4097
	}
4098

    
4099
	function update_dn_data(&$data) {
4100
		$this->ReadConfig($data);
4101
	}
4102

    
4103
	function build_javascript() {
4104
		global $g, $config;
4105

    
4106
		$javasr = parent::build_javascript();
4107

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

    
4122
		$javasr .= <<<EOD
4123
<script type='text/javascript'>
4124
//<![CDATA[
4125
var addBwRowTo = (function() {
4126

    
4127
	return (function (tableId) {
4128

    
4129
	var table = document.getElementById(tableId);
4130
	var totalrows = table.rows.length -1;
4131

    
4132
	var row = table.insertRow(totalrows + 1);
4133
	var cell1 = row.insertCell(0);
4134
	var cell2 = row.insertCell(1);
4135
	var cell3 = row.insertCell(2);
4136
	var cell4 = row.insertCell(3);
4137

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

    
4143
	});
4144
})();
4145

    
4146
function removeBwRow(el) {
4147
	var d = el.parentNode.parentNode.rowIndex;
4148
	document.getElementById('maintable').deleteRow(d);
4149
}
4150

    
4151
function ceil_func(el){
4152
	el.value = Math.ceil(el.value);
4153

    
4154
}
4155
//]]>
4156
</script>
4157

    
4158
EOD;
4159

    
4160
		return $javasr;
4161
	}
4162

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

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

    
4181
		$form = '<div class="table-responsive">';
4182
		$form .= '<table id="maintable" class="table table-hover table-striped">';
4183
		$form .= "<thead><tr>";
4184
		$form .= "<th>Bandwidth</th>";
4185
		//$form .= "<td width='35%'><div id='fifthcolumn'>Burst</div></td>";
4186
		$form .= "<th>Bw type</th>";
4187
		$form .= "<th>Schedule</th>";
4188
		$form .= "<th></th>";
4189
		$form .= "</tr></thead>";
4190
		$form .= "<tbody>";
4191

    
4192
		// If there are no bandwidths defined, make a blank one for convenience
4193
		if (empty($bandwidth)) {
4194
			$bandwidth = array(0 => array('bw' => '', 'bwscale' => 'Kb', 'bwsched' => 'none'));
4195
		}
4196

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

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

    
4211
					if ($bw['bwscale'] == $bwsidx) {
4212
						$form .= " selected";
4213
					}
4214

    
4215
					$form .= ">{$bwscale}</option>";
4216
				}
4217

    
4218
				$form .= "</select>";
4219
				$form .= "</td>";
4220
				$form .= '<td class="col-xs-4">';
4221
				$form .= "<select id=\"bwsched{$bwidx}\" name=\"bwsched{$bwidx}\" class=\"form-control\">";
4222

    
4223
				foreach ($schedules as $schd) {
4224
					$selected = "";
4225
					if ($bw['bwsched'] == $schd) {
4226
						$selected = "selected";
4227
					}
4228

    
4229
					$form .= "<option value='{$schd}' {$selected}>{$schd}</option>";
4230
				}
4231

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

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

    
4245
		return($form);
4246
	}
4247

    
4248
	function build_form() {
4249
		global $g, $config, $pipe, $action, $qname;
4250

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

    
4262

    
4263
		$sform = new Form();
4264
		$sform->setAction("firewall_shaper.php");
4265

    
4266
		$section = new Form_Section('Limiters');
4267

    
4268
		$section->addInput(new Form_Checkbox(
4269
			'enabled',
4270
			'Enable',
4271
			'Enable limiter and its children',
4272
			($this->GetEnabled() == "on"),
4273
			'on'
4274
		));
4275

    
4276
		$section->addInput(new Form_Input(
4277
			'newname',
4278
			'*Name',
4279
			'text',
4280
			$this->GetQname()
4281
		));
4282

    
4283
		$section->addInput(new Form_Input(
4284
			'name',
4285
			null,
4286
			'hidden',
4287
			$this->GetQname()
4288
		));
4289

    
4290
		if ($this->GetNumber() > 0) {
4291
			$section->addInput(new Form_Input(
4292
				'number',
4293
				null,
4294
				'hidden',
4295
				$this->GetNumber()
4296
			));
4297
		}
4298

    
4299
		$bandwidth = $this->GetBandwidth();
4300

    
4301
		if (is_array($bandwidth)) {
4302
				$section->addInput(new Form_StaticText(
4303
				'Bandwidth',
4304
				$this->build_bwtable()
4305
			));
4306
		}
4307

    
4308
		$mask = $this->GetMask();
4309

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

    
4319
		$group = new Form_Group(null);
4320

    
4321
		$group->add(new Form_Select(
4322
			'maskbits',
4323
			null,
4324
			$mask['bits'],
4325
			array_combine(range(32, 1, -1), range(32, 1, -1))
4326
		))->setHelp('IPv4 mask bits%1$s%2$s', '<br />', '255.255.255.255/?');
4327

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

    
4335
		$section->add($group);
4336

    
4337
		$section->addInput(new Form_Input(
4338
			'description',
4339
			'Description',
4340
			'text',
4341
			$this->GetDescription()
4342
		))->setHelp('A description may be entered here for administrative reference (not parsed).');
4343

    
4344
		$sform->add($section);
4345

    
4346
		/* Begin limiter patch */
4347
		$aqm_map = getAQMs();
4348
		$scheduler_map = getSchedulers();
4349

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

    
4362
		$section->addInput(new Form_StaticText(
4363
			'',
4364
			build_queue_params($aqm_map, $this->GetAQM(), $this->GetAQMParameters(), "aqm")
4365
		))->setHelp('Specifies the queue management algorithm parameters.');
4366

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

    
4376
		$section->addInput(new Form_StaticText(
4377
			'',
4378
			build_queue_params($scheduler_map, $this->GetScheduler(), $this->GetSchedulerParameters(), "sched")
4379
		))->setHelp('Specifies the scheduler parameters.');
4380

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

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

    
4397
		$sform->add($section);
4398
		/* End limiter patch */
4399

    
4400
		$section = new Form_Section('Advanced Options');
4401

    
4402
		$section->addInput(new Form_Input(
4403
			'delay',
4404
			'Delay (ms)',
4405
			'text',
4406
			$this->GetDelay() > 0 ? $this->GetDelay():null
4407
		))->setHelp('In most cases, zero (0) should specified here (or leave the field empty).');
4408

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

    
4418
		$section->addInput(new Form_Input(
4419
			'buckets',
4420
			'Bucket size (slots)',
4421
			'number',
4422
			$this->GetBuckets()
4423
		))->setHelp('In most cases, this field should be left empty. It increases the hash size set.');
4424

    
4425
		$sform->add($section);
4426

    
4427
		return($sform);
4428
		}
4429

    
4430
	function wconfig() {
4431
		$cflink =& get_dn_reference_to_me_in_config($this->GetLink());
4432
		if (!is_array($cflink)) {
4433
			$cflink = array();
4434
		}
4435
		$cflink['name'] = $this->GetQname();
4436
		$cflink['number'] = $this->GetNumber();
4437
		$cflink['qlimit'] = $this->GetQlimit();
4438
		$cflink['plr'] = $this->GetPlr();
4439
		$cflink['description'] = $this->GetDescription();
4440

    
4441
		$bandwidth = $this->GetBandwidth();
4442
		if (is_array($bandwidth)) {
4443
			$cflink['bandwidth'] = array();
4444
			$cflink['bandwidth']['item'] = array();
4445
			foreach ($bandwidth as $bwidx => $bw) {
4446
				$cflink['bandwidth']['item'][] = $bw;
4447
			}
4448
		}
4449

    
4450
		$cflink['enabled'] = $this->GetEnabled();
4451
		$cflink['buckets'] = $this->GetBuckets();
4452
		$mask = $this->GetMask();
4453
		$cflink['mask'] = $mask['type'];
4454
		$cflink['maskbits'] = $mask['bits'];
4455
		$cflink['maskbitsv6'] = $mask['bitsv6'];
4456
		$cflink['delay'] = $this->GetDelay();
4457

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

    
4477
}
4478

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

    
4512
	function GetWeight() {
4513
		return $this->weight;
4514
	}
4515
	function SetWeight($weight) {
4516
		$this->weight = $weight;
4517
	}
4518
	function GetPipe() {
4519
		return $this->pipeparent;
4520
	}
4521
	function SetPipe($pipe) {
4522
		$this->pipeparent = $pipe;
4523
	}
4524

    
4525
	/* Just a stub in case we ever try to call this from the frontend. */
4526
	function &add_queue($interface, &$queue, &$path, &$input_errors) {
4527
		return;
4528
	}
4529

    
4530
	function delete_queue() {
4531
		cleanup_dnqueue_from_rules($this->GetQname());
4532
		unset_dn_object_by_reference($this->GetLink());
4533
		@pfSense_ipfw_pipe("queue delete " . $this->GetNumber());
4534
	}
4535

    
4536
	function validate_input($data, &$input_errors) {
4537
		parent::validate_input($data, $input_errors);
4538

    
4539

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

    
4550
		if ($data['weight'] && ((!is_numeric($data['weight'])) ||
4551
		    ($data['weight'] < 1 && $data['weight'] > 100))) {
4552
			$input_errors[] = gettext("Weight must be an integer between 1 and 100.");
4553
		}
4554
	}
4555

    
4556
	/*
4557
	 * Should search even its children
4558
	 */
4559
	function &find_queue($pipe, $qname) {
4560
		if ($qname == $this->GetQname()) {
4561
			return $this;
4562
		} else {
4563
			return NULL;
4564
		}
4565
	}
4566

    
4567
	function &find_parentqueue($pipe, $qname) {
4568
		return $this->qparent;
4569
	}
4570

    
4571
	function &get_queue_list(&$qlist) {
4572
		if ($this->GetEnabled() == "") {
4573
			return;
4574
		}
4575
		$qlist[$this->GetQname()] = "?" .$this->GetNumber();
4576
	}
4577

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

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

    
4648
		// ecn flag
4649
		$this->SetECN($q['ecn']);
4650
		/* End limiter patch */
4651
	}
4652

    
4653
	function build_tree() {
4654
		$parent =& $this->GetParent();
4655
		$tree = " <li><a href=\"firewall_shaper_vinterface.php?pipe=" . htmlspecialchars($parent->GetQname()) ."&amp;queue=" . htmlspecialchars($this->GetQname()) ."&amp;action=show\">";
4656
		$tree .= htmlspecialchars($this->GetQname()) . "</a>";
4657
		$tree .= "</li>";
4658

    
4659
		return $tree;
4660
	}
4661

    
4662
	function build_rules() {
4663
		if ($this->GetEnabled() == "") {
4664
			return;
4665
		}
4666

    
4667
		$parent =& $this->GetParent();
4668
		$pfq_rule = "queue ". $this->GetNumber() . " config pipe " . $parent->GetNumber();
4669
		if ($this->GetQlimit()) {
4670
			$pfq_rule .= " queue " . $this->GetQlimit();
4671
		}
4672
		if ($this->GetWeight()) {
4673
			$pfq_rule .= " weight " . $this->GetWeight();
4674
		}
4675
		if ($this->GetBuckets()) {
4676
			$pfq_rule .= " buckets " . $this->GetBuckets();
4677
		}
4678
		$this->build_mask_rules($pfq_rule);
4679

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

    
4694
		$pfq_rule .= "\n";
4695

    
4696
		return $pfq_rule;
4697
	}
4698

    
4699
	function build_javascript() {
4700
		return parent::build_javascript();
4701
	}
4702

    
4703
	function build_form() {
4704
		global $g, $config, $pipe, $action, $qname;
4705

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

    
4717

    
4718
		$sform = new Form();
4719
		$sform->setAction("firewall_shaper.php");
4720
		$section = new Form_Section('Limiters');
4721

    
4722
		$section->addInput(new Form_Checkbox(
4723
			'enabled',
4724
			'Enable',
4725
			'Enable this queue',
4726
			($this->GetEnabled() == "on"),
4727
			'on'
4728
		));
4729

    
4730
		$section->addInput(new Form_Input(
4731
			'newname',
4732
			'*Name',
4733
			'text',
4734
			$this->GetQname()
4735
		));
4736

    
4737
		$section->addInput(new Form_Input(
4738
			'name',
4739
			null,
4740
			'hidden',
4741
			$this->GetQname()
4742
		));
4743

    
4744
		if ($this->GetNumber() > 0) {
4745
			$section->addInput(new Form_Input(
4746
				'number',
4747
				null,
4748
				'hidden',
4749
				$this->GetNumber()
4750
			));
4751
		}
4752

    
4753
		$mask = $this->GetMask();
4754

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

    
4764
		$group = new Form_Group(null);
4765

    
4766
		$group->add(new Form_Select(
4767
			'maskbits',
4768
			null,
4769
			$mask['bits'],
4770
			array_combine(range(32, 1, -1), range(32, 1, -1))
4771
		))->setHelp('IPv4 mask bits%1$s%2$s', '<br />', '255.255.255.255/?');
4772

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

    
4780
		$section->add($group);
4781

    
4782
		$section->addInput(new Form_Input(
4783
			'description',
4784
			'Description',
4785
			'text',
4786
			$this->GetDescription()
4787
		))->setHelp('A description may be entered here for administrative reference (not parsed).');
4788

    
4789
		$sform->add($section);
4790

    
4791
		/* Begin limiter patch */
4792
		$aqm_map = getAQMs();
4793

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

    
4806
		$section->addInput(new Form_StaticText(
4807
			'',
4808
			build_queue_params($aqm_map, $this->GetAQM(), $this->GetAQMParameters(), "aqm")
4809
		))->setHelp('Specifies the queue management algorithm parameters.');
4810

    
4811
		$section->addInput(new Form_Input(
4812
				'qlimit',
4813
				'Queue length',
4814
				'number',
4815
				$this->GetQlimit()
4816
		))->setHelp('Specifies the length of this queue, which the AQM is responsible for.  This field may be left empty.');
4817

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

    
4826
		$sform->add($section);
4827
		/* End limiter patch */
4828

    
4829
		$section = new Form_Section('Advanced Options');
4830

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

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

    
4849
		$section->addInput(new Form_Input(
4850
			'buckets',
4851
			'Bucket size (slots)',
4852
			'number',
4853
			$this->GetBuckets()
4854
		))->setHelp('In most cases, this field should be left empty. It increases the hash size set');
4855

    
4856
		$section->addInput(new Form_Input(
4857
			'pipe',
4858
			null,
4859
			'hidden',
4860
			$this->GetPipe()
4861
		));
4862

    
4863
		$sform->add($section);
4864

    
4865
		return($sform);
4866
	}
4867

    
4868
	function update_dn_data(&$data) {
4869
		$this->ReadConfig($data);
4870
	}
4871

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

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

    
4902
function get_dummynet_name_list() {
4903

    
4904
	$dn_name_list =& get_unique_dnqueue_list();
4905
	$dn_name = array();
4906
	if (is_array($dn_name_list)) {
4907
		foreach ($dn_name_list as $key => $value) {
4908
			$dn_name[] = $key;
4909
		}
4910
	}
4911

    
4912
	return $dn_name;
4913

    
4914
}
4915

    
4916
function get_altq_name_list() {
4917
	$altq_name_list =& get_unique_queue_list();
4918
	$altq_name = array();
4919
	if (is_array($altq_name_list)) {
4920
		foreach ($altq_name_list as $key => $aqobj) {
4921
			$altq_name[] = $key;
4922
		}
4923
	}
4924

    
4925
	return $altq_name;
4926
}
4927

    
4928
/*
4929
 * XXX: TODO Make a class shaper to hide all these functions
4930
 * from the global namespace.
4931
 */
4932

    
4933
/*
4934
 * This is a layer violation but for now there is no way
4935
 * I can find to properly do this with PHP.
4936
 */
4937
function altq_get_default_queue($interface) {
4938
	global $altq_list_queues;
4939

    
4940
	$altq_tmp = $altq_list_queues[$interface];
4941
	if ($altq_tmp) {
4942
		return $altq_tmp->GetDefaultQueuePresent();
4943
	} else {
4944
		return false;
4945
	}
4946
}
4947

    
4948
function altq_check_default_queues() {
4949
	global $altq_list_queues;
4950

    
4951
	$count = 0;
4952
	if (is_array($altq_list_queues)) {
4953
		foreach ($altq_list_queues as $altq) {
4954
			if ($altq->GetDefaultQueuePresent()) {
4955
				$count++;
4956
			}
4957
		}
4958
	}
4959
	else {
4960
		$count++;
4961
	}
4962

    
4963
	return 0;
4964
}
4965

    
4966
function &get_unique_queue_list() {
4967
	global $altq_list_queues;
4968

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

    
4986
function &get_unique_dnqueue_list() {
4987
	global $dummynet_pipe_list;
4988

    
4989
	$qlist = array();
4990
	if (is_array($dummynet_pipe_list)) {
4991
		foreach ($dummynet_pipe_list as $dn) {
4992
			if ($dn->GetEnabled() == "") {
4993
				continue;
4994
			}
4995
			$tmplist =& $dn->get_queue_list();
4996
			foreach ($tmplist as $qname => $link) {
4997
				$qlist[$qname] = $link;
4998
			}
4999
		}
5000
	}
5001
	return $qlist;
5002
}
5003

    
5004
function ref_on_altq_queue_list($parent, $qname) {
5005
	if (isset($GLOBALS['queue_list'][$qname])) {
5006
		$GLOBALS['queue_list'][$qname]++;
5007
	} else {
5008
		$GLOBALS['queue_list'][$qname] = 1;
5009
	}
5010

    
5011
	unref_on_altq_queue_list($parent);
5012
}
5013

    
5014
function unref_on_altq_queue_list($qname) {
5015
	$GLOBALS['queue_list'][$qname]--;
5016
	if ($GLOBALS['queue_list'][$qname] <= 1) {
5017
		unset($GLOBALS['queue_list'][$qname]);
5018
	}
5019
}
5020

    
5021
function read_altq_config() {
5022
	global $altq_list_queues, $config;
5023
	$path = array();
5024

    
5025
	init_config_arr(array('shaper', 'queue'));
5026
	$a_int = &$config['shaper']['queue'];
5027

    
5028
	$altq_list_queues = array();
5029

    
5030
	if (!is_array($config['shaper']['queue'])) {
5031
		return;
5032
	}
5033

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

    
5057
function read_dummynet_config() {
5058
	global $dummynet_pipe_list, $config;
5059
	$path = array();
5060

    
5061
	init_config_arr(array('dnshaper', 'queue'));
5062
	$a_int = &$config['dnshaper']['queue'];
5063

    
5064
	$dummynet_pipe_list = array();
5065

    
5066
	if (!count($config['dnshaper']['queue'])) {
5067
		return;
5068
	}
5069

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

    
5094
function get_interface_list_to_show() {
5095
	global $altq_list_queues, $config;
5096
	global $shaperIFlist;
5097

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

    
5110
	return $tree;
5111
}
5112

    
5113
function filter_generate_altq_queues() {
5114
	global $altq_list_queues;
5115

    
5116
	read_altq_config();
5117

    
5118
	$altq_rules = "";
5119
	foreach ($altq_list_queues as $altq) {
5120
		$altq_rules .= $altq->build_rules();
5121
	}
5122

    
5123
	return $altq_rules;
5124
}
5125

    
5126
function dnqueue_find_nextnumber() {
5127
	global $dummynet_pipe_list;
5128

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

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

    
5154
	if ($found == false) {
5155
		$dnnumber++;
5156
	}
5157

    
5158
	unset($dnused, $dnnum, $found);
5159
	return $dnnumber;
5160
}
5161

    
5162
function dnpipe_find_nextnumber() {
5163
	global $dummynet_pipe_list;
5164

    
5165
	$dnused = array();
5166
	foreach ($dummynet_pipe_list as $dn) {
5167
		$dnused[] = $dn->GetNumber();
5168
	}
5169

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

    
5183
	if ($found == false) {
5184
		$dnnumber++;
5185
	}
5186

    
5187
	unset($dnused, $dnnum, $found);
5188
	return $dnnumber;
5189
}
5190

    
5191
function filter_generate_dummynet_rules() {
5192
	global $g, $dummynet_pipe_list;
5193

    
5194
	read_dummynet_config();
5195

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

    
5222
function build_iface_without_this_queue($iface, $qname) {
5223
	global $g, $altq_list_queues;
5224
	global $shaperIFlist;
5225

    
5226
	$altq =& $altq_list_queues[$iface];
5227

    
5228
	if ($altq) {
5229
		$scheduler = $altq->GetScheduler();
5230
	}
5231

    
5232
	$form = '<dl class="dl-horizontal">';
5233

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

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

    
5252
	$form .= '</dl>';
5253

    
5254
	return $form;
5255

    
5256
}
5257

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

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

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

    
5269
// Check to see if the specified interface has a queue configured
5270
function interface_has_queue($if) {
5271
	global $config;
5272

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

    
5281
	return false;
5282
}
5283
?>
(47-47/59)