Project

General

Profile

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

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

    
34
function get_queue_stats() {
35
	// due to sysutils\qstats not providing accurate stats with 'currently' valid numbers
36
	// in its current implementation this php function does the job for now..
37
	$result = array();
38
	$result['timestamp'] = gettimeofday(true);
39
	$r = exec("/sbin/pfctl -s queue -v", $output, $ret);
40
	$interfacestats = array();
41
	foreach($output as $line) {
42
		$partial = explode('{', $line);
43
		$items = explode(" ", $partial[0]);
44
		if (isset($partial[1])) {
45
			$contains = explode(", ", substr($partial[1], 0, strlen($partial[1]) - 1));
46
		} else {
47
			unset($contains);
48
		}
49
		if ($items[0] == "queue") {
50
			$level = 1;
51
			while (empty($items[$level])) {
52
				$level++;
53
			}
54
			unset($newqueue);
55
			$newqueue = array();
56
			$currentqueuename = $items[$level];
57
			$currentqueueif = $items[$level+2];
58
			$newqueue['name'] = $currentqueuename;
59
			$newqueue['interface'] = $items[$level+2];
60

    
61
			if ($items[$level+3] == "bandwidth") {
62
				$level += 2;
63
			}
64
			if ($items[$level+3] == "priority") {
65
				$level += 2;
66
			}
67
			if ($items[$level+3] == "qlimit") {
68
				$level += 2;
69
			}
70
			$tmp = $items[$level+3];
71
			if (substr($tmp,strlen($tmp)-1) == "(") {
72
				$newqueue['shapertype'] = substr($tmp, 0, strlen($tmp)-1);
73
			}
74

    
75
			$interfacestats[$currentqueueif][$currentqueuename] = &$newqueue;
76
			if (is_array($contains)) {
77
				$newqueue['contains'] = $contains;
78
			}
79
		} elseif ($items[0] == "altq") {
80
			unset($newqueue);
81
			$newqueue = array();
82
			$currentqueuename = convert_real_interface_to_friendly_interface_name($items[2]);
83
			$currentqueueif = $items[2];
84
			$newqueue['name'] = $currentqueuename;
85
			$newqueue['interface'] = $items[2];
86
			$interfacestats[$currentqueueif][$currentqueuename] = &$newqueue;
87
		} else {
88
			if ($items[3] == "pkts:") {
89
				$newqueue["pkts"] = trim(substr($line, 10, 10));
90
				$newqueue["bytes"] = trim(substr($line, 29, 10));
91
				$newqueue["droppedpkts"] = trim(substr($line, 55, 6));
92
				$newqueue["droppedbytes"] = trim(substr($line, 69, 6));
93
			}
94
			if ($items[3] == "qlength:") {
95
				$subitems = explode("/", substr($line, 13, 8));
96
				$newqueue["qlengthitems"] = trim($subitems[0]);
97
				$newqueue["qlengthsize"] = trim($subitems[1]);
98
				if (substr($line, 22, 8) == "borrows:") {
99
					$newqueue["borrows"] = trim(substr($line, 31, 7));
100
				}
101
				if (substr($line, 39, 9) == "suspends:") {
102
					$newqueue["suspends"] = trim(substr($line, 49, 7));
103
				}
104
			}
105
		}
106
	}
107
	$result['interfacestats'] = $interfacestats;
108
	return $result;
109
}
110

    
111
/*
112
 * I admit :) this is derived from xmlparse.inc StartElement()
113
 */
114
function &get_reference_to_me_in_config(&$mypath) {
115
	global $config;
116

    
117
	$ptr =& $config['shaper'];
118
	foreach ($mypath as $indeks) {
119
		if (!is_array($ptr['queue'])) {
120
			$ptr['queue'] = array();
121
		}
122
		if (!is_array($ptr['queue'][$indeks])) {
123
			$ptr['queue'][$indeks] = array();
124
		}
125
		$ptr =& $ptr['queue'][$indeks];
126
	}
127

    
128
	return $ptr;
129
}
130

    
131
function unset_object_by_reference(&$mypath) {
132
	global $config;
133

    
134
	$ptr =& $config['shaper'];
135
	for ($i = 0; $i < count($mypath) - 1; $i++) {
136
		$ptr =& $ptr['queue'][$mypath[$i]];
137
	}
138
	unset($ptr['queue'][$mypath[$i]]);
139
}
140

    
141
function &get_dn_reference_to_me_in_config(&$mypath) {
142
	global $config;
143

    
144
	$ptr =& $config['dnshaper'];
145
	foreach ($mypath as $indeks) {
146
		$ptr =& $ptr['queue'][$indeks];
147
	}
148

    
149
	return $ptr;
150
}
151

    
152
function unset_dn_object_by_reference(&$mypath) {
153
	global $config;
154

    
155
	$ptr =& $config['dnshaper'];
156
	for ($i = 0; $i < count($mypath) - 1; $i++) {
157
		$ptr =& $ptr['queue'][$mypath[$i]];
158
	}
159
	unset($ptr['queue'][$mypath[$i]]);
160
}
161

    
162
function clean_child_queues($type, $mypath) {
163
	$ref = &get_reference_to_me_in_config($mypath);
164

    
165
	switch ($type) {
166
		case 'HFSC':
167
			if (isset($ref['borrow'])) {
168
				unset($ref['borrow']);
169
			}
170
			if (isset($ref['hogs'])) {
171
				unset($ref['hogs']);
172
			}
173
			if (isset($ref['buckets'])) {
174
				unset($ref['buckets']);
175
			}
176
			break;
177
		case 'PRIQ':
178
			if (isset($ref['borrow'])) {
179
				unset($ref['borrow']);
180
			}
181
			if (isset($ref['bandwidth'])) {
182
				unset($ref['bandwidth']);
183
			}
184
			if (isset($ref['bandwidthtype'])) {
185
				unset($ref['bandwidthtype']);
186
			}
187
			/* fall through */
188
		case 'FAIRQ':
189
			if (isset($ref['borrow'])) {
190
				unset($ref['borrow']);
191
			}
192
			/* fall through */
193
		case 'CBQ':
194
			if (isset($ref['realtime'])) {
195
				unset($ref['realtime']);
196
			}
197
			if (isset($ref['realtime1'])) {
198
				unset($ref['realtime1']);
199
			}
200
			if (isset($ref['realtime2'])) {
201
				unset($ref['realtime2']);
202
			}
203
			if (isset($ref['realtime3'])) {
204
				unset($ref['realtime3']);
205
			}
206
			if (isset($ref['upperlimit'])) {
207
				unset($ref['upperlimit']);
208
			}
209
			if (isset($ref['upperlimit1'])) {
210
				unset($ref['upperlimit1']);
211
			}
212
			if (isset($ref['upperlimit2'])) {
213
				unset($ref['upperlimit2']);
214
			}
215
			if (isset($ref['upperlimit3'])) {
216
				unset($ref['upperlimit3']);
217
			}
218
			if (isset($ref['linkshare'])) {
219
				unset($ref['linkshare']);
220
			}
221
			if (isset($ref['linkshare1'])) {
222
				unset($ref['linkshare1']);
223
			}
224
			if (isset($ref['linkshare2'])) {
225
				unset($ref['linkshare2']);
226
			}
227
			if (isset($ref['linkshare3'])) {
228
				unset($ref['linkshare3']);
229
			}
230
			if (isset($ref['hogs'])) {
231
				unset($ref['hogs']);
232
			}
233
			if (isset($ref['buckets'])) {
234
				unset($ref['buckets']);
235
			}
236
			break;
237
	}
238
}
239

    
240
function get_bandwidthtype_scale($type) {
241
	switch ($type) {
242
		case "Gb":
243
			$factor = 1024 * 1024 * 1024;
244
			break;
245
		case "Mb":
246
			$factor = 1024 * 1024;
247
			break;
248
		case "Kb":
249
			$factor = 1024;
250
			break;
251
		case "b":
252
		default:
253
			$factor = 1;
254
			break;
255
	}
256
	return intval($factor);
257
}
258

    
259
function get_bandwidth($bw, $scale, $obj) {
260

    
261
	$pattern= "/(b|Kb|Mb|Gb|%)/";
262
	if (!preg_match($pattern, $scale, $match))
263
		return 0;
264

    
265
	switch ($match[1]) {
266
		case '%':
267
			$objbw = ($bw / 100) * get_queue_bandwidth($obj);
268
			break;
269
		default:
270
			$objbw = $bw * get_bandwidthtype_scale($scale);
271
			break;
272
	}
273

    
274
	return floatval($objbw);
275
}
276

    
277
/*
278
 * XXX - unused
279
 *
280
function get_hfsc_bandwidth($object, $bw) {
281
	$pattern= "/[0-9]+/";
282
	if (preg_match($pattern, $bw, $match)) {
283
		$bw_1 = $match[1];
284
	} else {
285
		return 0;
286
	}
287
	$pattern= "/(b|Kb|Mb|Gb|%)/";
288
	if (preg_match($pattern, $bw, $match)) {
289
		switch ($match[1]) {
290
			case '%':
291
				$bw_1 = ($bw_1 / 100) * get_interface_bandwidth($object);
292
				break;
293
			default:
294
				$bw_1 = $bw_1 * get_bandwidthtype_scale($match[0]);
295
				break;
296
		}
297
		return floatval($bw_1);
298
	} else {
299
		return 0;
300
	}
301
}
302
*/
303

    
304
function get_queue_bandwidth($obj) {
305
	$bw = $obj->GetBandwidth();
306
	$scale = $obj->GetBwscale();
307

    
308
	$pattern= "/(b|Kb|Mb|Gb|%)/";
309
	if (!preg_match($pattern, $scale, $match))
310
		return 0;
311

    
312
	switch ($match[1]) {
313
		case '%':
314
			$objbw = ($bw / 100) * get_queue_bandwidth($obj->GetParent());
315
			break;
316
		default:
317
			$objbw = $bw * get_bandwidthtype_scale($scale);
318
			break;
319
	}
320

    
321
	return floatval($objbw);
322
}
323

    
324
function get_interface_bandwidth($object) {
325
	global $altq_list_queues;
326

    
327
	$int = $object->GetInterface();
328
	$altq =& $altq_list_queues[$int];
329
	if ($altq) {
330
		$bw_3 = $altq->GetBandwidth();
331
		$bw_3 = $bw_3 * get_bandwidthtype_scale($altq->GetBwscale());
332
		return floatval($bw_3);
333
	} else {
334
		return 0;
335
	}
336
}
337

    
338
/*
339
 * This is duplicated here since we cannot include guiconfig.inc.
340
 * Including it makes all stuff break.
341
 */
342
function shaper_do_input_validation($postdata, $reqdfields, $reqdfieldsn, $input_errors) {
343

    
344
	/* check for bad control characters */
345
	foreach ($postdata as $pn => $pd) {
346
		if (is_string($pd) && preg_match("/[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f]/", $pd)) {
347
			$input_errors[] = sprintf(gettext("The field '%s' contains invalid characters."), $pn);
348
		}
349
	}
350

    
351
	for ($i = 0; $i < count($reqdfields); $i++) {
352
		if ($postdata[$reqdfields[$i]] == "") {
353
			$input_errors[] = sprintf(gettext("The field '%s' is required."), $reqdfieldsn[$i]);
354
		}
355
	}
356
}
357

    
358
function cleanup_queue_from_rules($queue) {
359
	global $config;
360

    
361
	foreach ($config['filter']['rule'] as $rule) {
362
		if ($rule['defaultqueue'] == $queue) {
363
			unset($rule['defaultqueue']);
364
		}
365
		if ($rule['ackqueue'] == $queue) {
366
			unset($rule['ackqueue']);
367
		}
368
	}
369
}
370

    
371
function cleanup_dnqueue_from_rules($queue) {
372
	global $config;
373

    
374
	foreach ($config['filter']['rule'] as $rule) {
375
		if ($rule['dnpipe'] == $queue) {
376
			unset($rule['dnpipe']);
377
		}
378
		if ($rule['pdnpipe'] == $queue) {
379
			unset($rule['pdnpipe']);
380
		}
381
	}
382
}
383

    
384
class altq_root_queue {
385
	var $interface;
386
	var $tbrconfig ;
387
	var $bandwidth;
388
	var $bandwidthtype; /* b, Kb, Mb, Gb, % */
389
	var $scheduler;
390
	var $qlimit;
391
	var $queues = array();
392
	var $qenabled = false;
393
	var $link;
394

    
395
	/* Accessor functions */
396
	function GetDefaultQueuePresent() {
397
		if (!empty($this->queues)) {
398
			foreach ($this->queues as $q) {
399
				if ($q->GetDefault()) {
400
					return true;
401
				}
402
			}
403
		}
404

    
405
		return false;
406
	}
407
	function SetLink($link) {
408
		$this->link = $link;
409
	}
410
	function GetLink() {
411
		return $this->link;
412
	}
413
	function GetEnabled() {
414
		return $this->qenabled;
415
	}
416
	function SetEnabled($value) {
417
		$this->qenabled = $value;
418
	}
419
	function CanHaveChildren() {
420
		if ($this->GetScheduler() == "CODELQ") {
421
			return false;
422
		} else {
423
			return true;
424
		}
425
	}
426
	function CanBeDeleted() {
427
		return false;
428
	}
429
	function GetQname() {
430
		return $this->interface;
431
	}
432
	function SetQname($name) {
433
		$this->interface = trim($name);
434
	}
435
	function GetInterface() {
436
		return $this->interface;
437
	}
438
	function SetInterface($name) {
439
		$this->interface = trim($name);
440
	}
441
	function GetTbrConfig() {
442
		return $this->tbrconfig;
443
	}
444
	function SetTbrConfig($tbrconfig) {
445
		$this->tbrconfig = $tbrconfig;
446
	}
447
	function GetBandwidth() {
448
		return $this->bandwidth;
449
	}
450
	function SetBandwidth($bw) {
451
		$this->bandwidth = $bw;
452
	}
453
	function GetBwscale() {
454
		return $this->bandwidthtype;
455
	}
456
	function SetBwscale($bwscale) {
457
		$this->bandwidthtype = $bwscale;
458
	}
459
	function GetScheduler() {
460
		return $this->scheduler;
461
	}
462
	function SetScheduler($scheduler) {
463
		$this->scheduler = trim($scheduler);
464
	}
465
	function GetQlimit() {
466
		return $this->qlimit;
467
	}
468
	function SetQlimit($limit) {
469
		$this->qlimit = $limit;
470
	}
471

    
472
	function GetBwscaleText() {
473
		switch ($this->bandwidthtype) {
474
			case "b":
475
				$bwscaletext = "Bit/s";
476
				break;
477
			case "Kb":
478
				$bwscaletext = "Kbit/s";
479
				break;
480
			case "Mb":
481
				$bwscaletext = "Mbit/s";
482
				break;
483
			case "Gb":
484
				$bwscaletext = "Gbit/s";
485
				break;
486
			default:
487
				/* For others that do not need translating like % */
488
				$bwscaletext = $this->bandwidthtype;
489
				break;
490
		}
491
		return $bwscaletext;
492
	}
493

    
494
	function CheckBandwidth($bw, $bwtype) {
495
		$sum = $this->GetTotalBw();
496
		if ($sum > $bw * get_bandwidthtype_scale($bwtype))
497
			return 1;
498
		foreach ($this->queues as $q) {
499
			if ($q->CheckBandwidth(0, ''))
500
				return 1;
501
		}
502

    
503
		return 0;
504
	}
505

    
506
	function GetTotalBw($qignore = NULL) {
507
		$sum = 0;
508
		foreach ($this->queues as $q) {
509
			if ($qignore != NULL && $qignore == $q)
510
				continue;
511
			$sum += get_bandwidth($q->GetBandwidth(), $q->GetBwscale(), $this);
512
		}
513

    
514
		return $sum;
515
	}
516

    
517
	function validate_input($data, &$input_errors) {
518

    
519
		$reqdfields[] = "bandwidth";
520
		$reqdfieldsn[] = gettext("Bandwidth");
521
		$reqdfields[] = "bandwidthtype";
522
		$reqdfieldsn[] = gettext("Bandwidthtype");
523

    
524
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
525

    
526
		if (!isset($data['bandwidth']) || strlen($data['bandwidth']) == 0) {
527
			$input_errors[] = gettext("Bandwidth must be set.  This is usually the interface speed.");
528
		}
529
		if ($data['bandwidth'] && (!is_numeric($data['bandwidth']))) {
530
			$input_errors[] = gettext("Bandwidth must be an integer.");
531
		}
532
		if ($data['bandwidth'] < 0) {
533
			$input_errors[] = gettext("Bandwidth cannot be negative.");
534
		}
535
		if ($data['bandwidthtype'] == "%") {
536
			if ($data['bandwidth'] > 100 || $data['bandwidth'] < 0) {
537
				$input_errors[] = gettext("Bandwidth in percentage should be between 1 and 100.");
538
			}
539
		}
540
		if ($this->CheckBandwidth($data['bandwidth'], $data['bandwidthtype']))
541
			$input_errors[] = "The sum of child bandwidth is higher than parent.";
542

    
543
		if ($data['qlimit'] && (!is_numeric($data['qlimit']))) {
544
			$input_errors[] = gettext("Qlimit must be an integer.");
545
		}
546
		if ($data['qlimit'] < 0) {
547
			$input_errors[] = gettext("Qlimit must be positive.");
548
		}
549
		if ($data['tbrconfig'] && (!is_numeric($data['tbrconfig']))) {
550
			$input_errors[] = gettext("Tbrsize must be an integer.");
551
		}
552
		if ($data['tbrconfig'] < 0) {
553
			$input_errors[] = gettext("Tbrsize must be positive.");
554
		}
555
	}
556

    
557
	/* Implement this to shorten some code on the frontend page */
558
	function ReadConfig(&$conf) {
559
		if (isset($conf['tbrconfig'])) {
560
			$this->SetTbrConfig($conf['tbrconfig']);
561
		} else {
562
			$this->SetTbrConfig($conf['tbrconfig']);
563
		}
564
		$this->SetBandwidth($conf['bandwidth']);
565
		if ($conf['bandwidthtype'] <> "") {
566
			$this->SetBwscale($conf['bandwidthtype']);
567
		}
568
		if (isset($conf['scheduler'])) {
569
			if ($this->GetScheduler() != $conf['scheduler']) {
570
				foreach ($this->queues as $q) {
571
					clean_child_queues($conf['scheduler'], $this->GetLink());
572
					$q->clean_queue($conf['scheduler']);
573
				}
574
			}
575
			$this->SetScheduler($conf['scheduler']);
576
		}
577
		if (isset($conf['qlimit']) && $conf['qlimit'] <> "") {
578
			$this->SetQlimit($conf['qlimit']);
579
		} else {
580
			$this->SetQlimit("");
581
		}
582
		if (isset($conf['name'])) {
583
			$this->SetQname($conf['name']);
584
		}
585
		if (!empty($conf['enabled'])) {
586
			$this->SetEnabled($conf['enabled']);
587
		} else {
588
			$this->SetEnabled("");
589
		}
590
	}
591

    
592
	function copy_queue($interface, &$cflink) {
593
		$cflink['interface'] = $interface;
594
		$cflink['name'] = $interface;
595
		$cflink['scheduler'] = $this->GetScheduler();
596
		$cflink['bandwidth'] = $this->GetBandwidth();
597
		$cflink['bandwidthtype'] = $this->GetBwscale();
598
		$cflink['qlimit'] = $this->GetQlimit();
599
		$cflink['tbrconfig'] = $this->GetTbrConfig();
600
		$cflink['enabled'] = $this->GetEnabled();
601
		if (is_array($this->queues)) {
602
			$cflink['queue'] = array();
603
			foreach ($this->queues as $q) {
604
				$cflink['queue'][$q->GetQname()] = array();
605
				$q->copy_queue($interface, $cflink['queue'][$q->GetQname()]);
606
			}
607
		}
608
	}
609

    
610
	function &get_queue_list(&$q = null) {
611
		$qlist = array();
612

    
613
		//$qlist[$this->GetQname()] = & $this;
614
		if (is_array($this->queues)) {
615
			foreach ($this->queues as $queue) {
616
				$queue->get_queue_list($qlist);
617
			}
618
		}
619
		return $qlist;
620
	}
621

    
622
	function &add_queue($interface, &$queue, &$path, &$input_errors) {
623

    
624
		if (!is_array($this->queues)) {
625
			$this->queues = array();
626
		}
627

    
628
		switch ($this->GetScheduler()) {
629
			case "PRIQ":
630
				$__tmp_q = new priq_queue(); $q =& $__tmp_q;
631
				break;
632
			case "HFSC":
633
				$__tmp_q = new hfsc_queue(); $q =& $__tmp_q;
634
				break;
635
			case "CBQ":
636
				$__tmp_q = new cbq_queue(); $q =& $__tmp_q;
637
				break;
638
			case "FAIRQ":
639
				$__tmp_q = new fairq_queue(); $q =& $__tmp_q;
640
				break;
641
			default:
642
				/* XXX: but should not happen anyway */
643
				return;
644
				break;
645
		}
646
		$q->SetLink($path);
647
		$q->SetInterface($this->GetInterface());
648
		$q->SetEnabled("on");
649
		$q->SetParent($this);
650
		$q->ReadConfig($queue);
651
		$q->validate_input($queue, $input_errors);
652

    
653
		$this->queues[$q->GetQname()] = &$q;
654
		ref_on_altq_queue_list($this->GetQname(), $q->GetQname());
655
		if (is_array($queue['queue'])) {
656
			foreach ($queue['queue'] as $key1 => $que) {
657
				array_push($path, $key1);
658
				$q->add_queue($q->GetInterface(), $que, $path, $input_errors);
659
				array_pop($path);
660
			}
661
		}
662

    
663
		return $q;
664
	}
665

    
666
	/* interface here might be optional */
667
	function &find_queue($interface, $qname) {
668
		if ($qname == $this->GetQname()) {
669
			return $this;
670
		}
671
		foreach ($this->queues as $q) {
672
			$result =& $q->find_queue("", $qname);
673
			if ($result) {
674
				return $result;
675
			}
676
		}
677
	}
678

    
679
	function &find_parentqueue($interface, $qname) {
680
		if ($qname == $interface) {
681
			$result = NULL;
682
		} else if ($this->queues[$qname]) {
683
			$result = $this;
684
		} else if ($this->GetScheduler() <> "PRIQ") {
685
			foreach ($this->queues as $q) {
686
				$result = $q->find_parentqueue("", $qname);
687
				if ($result) {
688
					return $result;
689
				}
690
			}
691
		}
692
	}
693

    
694
	function build_tree() {
695
		global $shaperIFlist;
696

    
697
		$tree = " <li><a href=\"firewall_shaper.php?interface=".$this->GetInterface()."&amp;queue=". $this->GetInterface()."&amp;action=show";
698
		$tree .= "\">" . $shaperIFlist[$this->GetInterface()] . "</a>";
699
		if (is_array($this->queues)) {
700
			$tree .= "<ul>";
701
			foreach ($this->queues as $q) {
702
				$tree .= $q->build_tree();
703
			}
704
			$tree .= "</ul>";
705
		}
706
		$tree .= "</li>";
707
		return $tree;
708
	}
709

    
710
	function delete_queue() {
711
		foreach ($this->queues as $q)
712
			$q->delete_queue();
713
		unset_object_by_reference($this->GetLink());
714
	}
715

    
716
	function delete_all() {
717
		if (count($this->queues)) {
718
			foreach ($this->queues as $q) {
719
				$q->delete_all();
720
				unset_object_by_reference($q->GetLink());
721
				unset($q);
722
			}
723
			unset($this->queues);
724
		}
725
	}
726

    
727
	/*
728
	 * First it spits:
729
	 * altq on $interface ..............
730
	 *	then it goes like
731
	 *	foreach ($queues as $qkey => $queue) {
732
	 *		this->queues[$qkey]->build_rule();
733
	 *	}
734
	 */
735
	function build_rules(&$default = false) {
736
		if (count($this->queues) > 0 && $this->GetEnabled() == "on") {
737
			$default = false;
738
			$rules = " altq on " . get_real_interface($this->GetInterface());
739
			if ($this->GetScheduler()) {
740
				$rules .= " ".strtolower($this->GetScheduler());
741
			}
742
			if ($this->GetQlimit() > 0) {
743
				$rules .= " qlimit " . $this->GetQlimit() . " ";
744
			}
745
			if ($this->GetBandwidth()) {
746
				$rules .= " bandwidth ".trim($this->GetBandwidth());
747
				if ($this->GetBwscale()) {
748
					$rules .= $this->GetBwscale();
749
				}
750
			}
751
			if ($this->GetTbrConfig()) {
752
				$rules .= " tbrsize ".$this->GetTbrConfig();
753
			}
754
			if (count($this->queues)) {
755
				$i = count($this->queues);
756
				$rules .= " queue { ";
757
				foreach ($this->queues as $qkey => $qnone) {
758
					if ($i > 1) {
759
						$i--;
760
						$rules .= " {$qkey}, ";
761
					} else {
762
						$rules .= " {$qkey} ";
763
					}
764
				}
765
				$rules .= " } \n";
766
				foreach ($this->queues as $q) {
767
					$rules .= $q->build_rules($default);
768
				}
769
			}
770

    
771
			if ($default == false) {
772
				$error = sprintf(gettext("SHAPER: no default queue specified for interface %s."), $this->GetInterface()) . " " . gettext("The interface queue will be enforced as default.");
773
				file_notice("Shaper", $error, "Error occurred", "");
774
				unset($error);
775
				return "\n";
776
			}
777
			$frule .= $rules;
778
		} else if ($this->GetEnabled() == "on" && $this->GetScheduler() == "CODELQ") {
779
			$rules = " altq on " . get_real_interface($this->GetInterface());
780
			if ($this->GetScheduler()) {
781
				$rules .= " ".strtolower($this->GetScheduler());
782
			}
783
			if ($this->GetQlimit() > 0) {
784
				$rules .= " ( qlimit " . $this->GetQlimit() . " ) ";
785
			}
786
			if ($this->GetBandwidth()) {
787
				$rules .= " bandwidth ".trim($this->GetBandwidth());
788
				if ($this->GetBwscale()) {
789
					$rules .= $this->GetBwscale();
790
				}
791
			}
792
			if ($this->GetTbrConfig()) {
793
				$rules .= " tbrsize ".$this->GetTbrConfig();
794
			}
795

    
796
			$rules .= " queue";
797
		}
798

    
799
		$rules .= " \n";
800
		return $rules;
801
	}
802

    
803
	function build_javascript() {
804
		$javascript = "<script type=\"text/javascript\">";
805
		$javascript .= "//<![CDATA[\n";
806
		$javascript .= "function mySuspend() {";
807
		$javascript .= "if (document.layers && document.layers['shaperarea'] != null) ";
808
		$javascript .= "document.layers['shaperarea'].visibility = 'hidden'; ";
809
		$javascript .= "else if (document.all)";
810
		$javascript .= "document.all['shaperarea'].style.visibility = 'hidden';";
811
		$javascript .= "}";
812

    
813
		$javascript .= "function myResume() {";
814
		$javascript .= "if (document.layers && document.layers['shaperarea'] != null) ";
815
		$javascript .= "document.layers['shaperarea'].visibility = 'visible';";
816
		$javascript .= "else if (document.all) ";
817
		$javascript .= "document.all['shaperarea'].style.visibility = 'visible';";
818
		$javascript .= "}";
819
		$javascript .= "//]]>";
820
		$javascript .= "</script>";
821

    
822
		return $javascript;
823
	}
824

    
825
	function build_shortform() {
826
		global $g;
827

    
828
		$altq =& $this;
829

    
830
		if ($altq) {
831
			$scheduler = ": " . $altq->GetScheduler();
832
		}
833

    
834
		$form = '<dl class="dl-horizontal">';
835
		$form .= '	<dt>';
836
		$form .= '		<a href="firewall_shaper.php?interface=' . $this->GetInterface() . '&amp;queue=' . $this->GetQname() . '&amp;action=show">' . $shaperIFlist[$this->GetInterface()] . '</a>';
837
		$form .= '	</dt>';
838
		$form .= '	<dd>';
839
		$form .=		$scheduler;
840
		$form .= '	</dd>';
841

    
842
		$form .= '	<dt>';
843
		$form .=		'Bandwidth';
844
		$form .= '	</dt>';
845
		$form .= '	<dd>';
846
		$form .=		$this->GetBandwidth() . '&nbsp;' . $this->GetBwscaleText();
847
		$form .= '	</dd>';
848

    
849
		$form .= '	<dt>';
850
		$form .= 'Disable';
851
		$form .= '	<dt>';
852
		$form .= '	<dd>';
853

    
854
		$form .= '<a class="btn btn-danger btn-xs" href="firewall_shaper_queues.php?interface=';
855
		$form .= $this->GetInterface() . '&amp;queue=';
856
		$form .= $this->GetQname() . '&amp;action=delete">';
857
		$form .= '<i class="fa fa-trash icon-embed-btn"></i>';
858
		$form .= gettext("Remove shaper from this interface") . '</a>';
859

    
860
		$form .= '	</dd>';
861

    
862
		$form .= '</dl>';
863

    
864
		return $form;
865

    
866
	}
867

    
868
	/*
869
	 * For requesting the parameters of the root queues
870
	 * to the user like the traffic wizard does.
871
	 */
872
	function build_form() {
873

    
874
		$sform = new Form();
875

    
876
		$sform->setAction("firewall_shaper.php");
877

    
878
		$section = new Form_Section(null);
879

    
880
		$section->addInput(new Form_Checkbox(
881
			'enabled',
882
			'Enable/Disable',
883
			'Enable/disable discipline and its children',
884
			($this->GetEnabled() == "on"),
885
			'on'
886
		));
887

    
888
		$section->addInput(new Form_StaticText(
889
			'*Name',
890
			$this->GetQname()
891
		));
892

    
893
		$section->addInput(new Form_Select(
894
			'scheduler',
895
			'Scheduler Type',
896
			$this->GetScheduler(),
897
			array('HFSC' => 'HFSC',
898
				  'CBQ' => 'CBQ',
899
				  'FAIRQ' => 'FAIRQ',
900
				  'CODELQ' => 'CODELQ',
901
				  'PRIQ' => 'PRIQ')
902
		))->setHelp('Changing this changes all child queues! Beware information can be lost.');
903

    
904
		$group = new Form_group('Bandwidth');
905

    
906
		$group->add(new Form_Input(
907
			'bandwidth',
908
			null,
909
			'number',
910
			$this->GetBandwidth()
911
		));
912

    
913
		$group->add(new Form_Select(
914
			'bandwidthtype',
915
			null,
916
			$this->GetBwscale(),
917
			array('Kb' => 'Kbit/s',
918
				  'Mb' => 'Mbit/s',
919
				  'Gb' => 'Gbit/s',
920
				  'b' => 'Bit/s',
921
				  '%' => '%')
922
		));
923

    
924
		$section->add($group);
925

    
926
		$section->addInput(new Form_Input(
927
			'qlimit',
928
			'Queue Limit',
929
			'number',
930
			$this->GetQlimit()
931
		));
932

    
933
		$section->addInput(new Form_Input(
934
			'tbrconfig',
935
			'TBR Size',
936
			'number',
937
			$this->GetTbrConfig()
938
		))->setHelp('Adjusts the size, in bytes, of the token bucket regulator. If not specified, heuristics based on the interface ' .
939
					'bandwidth are used to determine the size.');
940

    
941
		$section->addInput(new Form_Input(
942
			'interface',
943
			null,
944
			'hidden',
945
			$this->GetInterface()
946
		));
947

    
948
		$section->addInput(new Form_Input(
949
			'name',
950
			null,
951
			'hidden',
952
			$this->GetQname()
953
		));
954

    
955
		$sform->add($section);
956

    
957
		return($sform);
958
	}
959

    
960
	function update_altq_queue_data(&$data) {
961
		$this->ReadConfig($data);
962
	}
963

    
964
	/*
965
	 * Should call on each of it queues and subqueues
966
	 * the same function much like build_rules();
967
	 */
968
	function wconfig() {
969
		$cflink = &get_reference_to_me_in_config($this->GetLink());
970
		if (!is_array($cflink)) {
971
			$cflink = array();
972
		}
973
		$cflink['interface'] = $this->GetInterface();
974
		$cflink['name'] = $this->GetQname();
975
		$cflink['scheduler'] = $this->GetScheduler();
976
		$cflink['bandwidth'] = $this->GetBandwidth();
977
		$cflink['bandwidthtype'] = $this->GetBwscale();
978
		$cflink['qlimit'] = trim($this->GetQlimit());
979
		if (empty($cflink['qlimit'])) {
980
			unset($cflink['qlimit']);
981
		}
982
		$cflink['tbrconfig'] = trim($this->GetTbrConfig());
983
		if (empty($cflink['tbrconfig'])) {
984
			unset($cflink['tbrconfig']);
985
		}
986
		$cflink['enabled'] = $this->GetEnabled();
987
		if (empty($cflink['enabled'])) {
988
			unset($cflink['enabled']);
989
		}
990
	}
991

    
992
}
993

    
994
class priq_queue {
995
	var $qname;
996
	var $qinterface;
997
	var $qlimit;
998
	var $qpriority;
999
	var $description;
1000
	var $isparent;
1001
	var $qbandwidth;
1002
	var $qbandwidthtype;
1003
	var $qdefault = "";
1004
	var $qrio = "";
1005
	var $qred = "";
1006
	var $qcodel = "";
1007
	var $qecn = "";
1008
	var $qack;
1009
	var $qenabled = "";
1010
	var $qparent;
1011
	var $link;
1012

    
1013
	/* This is here to help with form building and building rules/lists */
1014
	var $subqueues = array();
1015

    
1016
	/* Accessor functions */
1017
	function SetLink($link) {
1018
		$this->link = $link;
1019
	}
1020
	function GetLink() {
1021
		return $this->link;
1022
	}
1023
	function &GetParent() {
1024
		return $this->qparent;
1025
	}
1026
	function SetParent(&$parent) {
1027
		$this->qparent = &$parent;
1028
	}
1029
	function GetEnabled() {
1030
		return $this->qenabled;
1031
	}
1032
	function SetEnabled($value) {
1033
		$this->qenabled = $value;
1034
	}
1035
	function CanHaveChildren() {
1036
		return false;
1037
	}
1038
	function CanBeDeleted() {
1039
		return true;
1040
	}
1041
	function GetQname() {
1042
		return $this->qname;
1043
	}
1044
	function SetQname($name) {
1045
		$this->qname = trim($name);
1046
	}
1047
	function GetBandwidth() {
1048
		return $this->qbandwidth;
1049
	}
1050
	function SetBandwidth($bandwidth) {
1051
		$this->qbandwidth = $bandwidth;
1052
	}
1053
	function GetInterface() {
1054
		return $this->qinterface;
1055
	}
1056
	function SetInterface($name) {
1057
		$this->qinterface = trim($name);
1058
	}
1059
	function GetQlimit() {
1060
		return $this->qlimit;
1061
	}
1062
	function SetQlimit($limit) {
1063
		$this->qlimit = $limit;
1064
	}
1065
	function GetQpriority() {
1066
		return $this->qpriority;
1067
	}
1068
	function SetQpriority($priority) {
1069
		$this->qpriority = $priority;
1070
	}
1071
	function GetDescription() {
1072
		return $this->description;
1073
	}
1074
	function SetDescription($str) {
1075
		$this->description = trim($str);
1076
	}
1077
	function GetFirstime() {
1078
		return $this->firsttime;
1079
	}
1080
	function SetFirsttime($number) {
1081
		$this->firsttime = $number;
1082
	}
1083
	function CheckBandwidth($bw, $bwtype) {
1084
		$parent = $this->GetParent();
1085

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

    
1091
		if ($bwtype != "%") {
1092
			$sum = $parent->GetTotalBw(($bw > 0) ? $this : NULL);
1093

    
1094
			if ($bw > 0) {
1095
				$sum += get_bandwidth($bw, $bwtype, $parent);
1096
			}
1097

    
1098
			if ($sum > get_queue_bandwidth($parent)) {
1099
				return 1;
1100
			}
1101
		}
1102

    
1103
		foreach ($this->subqueues as $q) {
1104
			if ($q->CheckBandwidth(0, '')) {
1105
				return 1;
1106
			}
1107
		}
1108

    
1109
		return 0;
1110
	}
1111
	function GetTotalBw($qignore = NULL) {
1112
		$sum = 0;
1113
		foreach ($this->subqueues as $q) {
1114
			if ($qignore != NULL && $qignore == $q)
1115
				continue;
1116
			$sum += get_bandwidth($q->GetBandwidth(), $q->GetBwscale(), $q->GetParent());
1117
		}
1118

    
1119
		return $sum;
1120
	}
1121
	function GetBwscale() {
1122
		return $this->qbandwidthtype;
1123
	}
1124
	function SetBwscale($scale) {
1125
		$this->qbandwidthtype = $scale;
1126
	}
1127
	function GetDefaultQueuePresent() {
1128
		if ($this->GetDefault()) {
1129
			return true;
1130
		}
1131
		if (!empty($this->subqueues)) {
1132
			foreach ($this->subqueues as $q) {
1133
				if ($q->GetDefault()) {
1134
					return true;
1135
				}
1136
			}
1137
		}
1138

    
1139
		return false;
1140
	}
1141
	function GetDefault() {
1142
		return $this->qdefault;
1143
	}
1144
	function SetDefault($value = false) {
1145
		$this->qdefault = $value;
1146
	}
1147
	function GetCodel() {
1148
		return $this->codel;
1149
	}
1150
	function SetCodel($codel = false) {
1151
		$this->codel = $codel;
1152
	}
1153
	function GetRed() {
1154
		return $this->qred;
1155
	}
1156
	function SetRed($red = false) {
1157
		$this->qred = $red;
1158
	}
1159
	function GetRio() {
1160
		return $this->qrio;
1161
	}
1162
	function SetRio($rio = false) {
1163
		$this->qrio = $rio;
1164
	}
1165
	function GetEcn() {
1166
		return $this->qecn;
1167
	}
1168
	function SetEcn($ecn = false) {
1169
		$this->qecn = $ecn;
1170
	}
1171
	function GetAck() {
1172
		return $this->qack;
1173
	}
1174
	function SetAck($ack = false) {
1175
		$this->qack = $ack;
1176
	}
1177

    
1178
	function GetBwscaleText() {
1179
		switch ($this->qbandwidthtype) {
1180
			case "b":
1181
				$bwscaletext = "Bit/s";
1182
				break;
1183
			case "Kb":
1184
				$bwscaletext = "Kbit/s";
1185
				break;
1186
			case "Mb":
1187
				$bwscaletext = "Mbit/s";
1188
				break;
1189
			case "Gb":
1190
				$bwscaletext = "Gbit/s";
1191
				break;
1192
			default:
1193
				/* For others that do not need translating like % */
1194
				$bwscaletext = $this->qbandwidthtype;
1195
				break;
1196
		}
1197
		return $bwscaletext;
1198
	}
1199

    
1200
	function build_javascript() {
1201
		$javascript = "<script type=\"text/javascript\">";
1202
		$javascript .= "//<![CDATA[\n";
1203
		$javascript .= "function mySuspend() { \n";
1204
		$javascript .= "if (document.layers && document.layers['shaperarea'] != null)\n";
1205
		$javascript .= "document.layers['shaperarea'].visibility = 'hidden';\n";
1206
		$javascript .= "else if (document.all)\n";
1207
		$javascript .= "document.all['shaperarea'].style.visibility = 'hidden';\n";
1208
		$javascript .= "}\n";
1209

    
1210
		$javascript .= "function myResume() {\n";
1211
		$javascript .= "if (document.layers && document.layers['shaperarea'] != null)\n";
1212
		$javascript .= "document.layers['shaperarea'].visibility = 'visible';\n";
1213
		$javascript .= "else if (document.all)\n";
1214
		$javascript .= "document.all['shaperarea'].style.visibility = 'visible';\n";
1215
		$javascript .= "}\n";
1216
		$javascript .= "//]]>";
1217
		$javascript .= "</script>";
1218

    
1219
		return $javascript;
1220
	}
1221

    
1222
	function &add_queue($interface, &$qname, &$path, &$input_errors) { return; }
1223

    
1224
	/*
1225
	 * Currently this will not be called unless we decide to clone a whole
1226
	 * queue tree on the 'By Queues' view or support drag&drop on the tree/list
1227
	 */
1228
	function copy_queue($interface, &$cflink) {
1229

    
1230
		$cflink['name'] = $this->GetQname();
1231
		$cflink['interface'] = $interface;
1232
		$cflink['qlimit'] = $this->GetQlimit();
1233
		$cflink['priority'] = $this->GetQpriority();
1234
		$cflink['description'] = $this->GetDescription();
1235
		$cflink['enabled'] = $this->GetEnabled();
1236
		$cflink['default'] = $this->GetDefault();
1237
		$cflink['red'] = $this->GetRed();
1238
		$cflink['codel'] = $this->GetCodel();
1239
		$cflink['rio'] = $this->GetRio();
1240
		$cflink['ecn'] = $this->GetEcn();
1241

    
1242
		if (is_array($this->subqueues)) {
1243
			$cflinkp['queue'] = array();
1244
			foreach ($this->subqueues as $q) {
1245
				$cflink['queue'][$q->GetQname()] = array();
1246
				$q->copy_queue($interface, $cflink['queue'][$q->GetQname()]);
1247
			}
1248
		}
1249
	}
1250

    
1251
	function clean_queue($sched) {
1252
		clean_child_queues($sched, $this->GetLink());
1253
		if (is_array($this->subqueues)) {
1254
			foreach ($this->subqueues as $q) {
1255
				$q->clean_queue($sched);
1256
			}
1257
		}
1258
	}
1259

    
1260
	function &get_queue_list(&$qlist) {
1261

    
1262
		$qlist[$this->GetQname()] = & $this;
1263
		if (is_array($this->subqueues)) {
1264
			foreach ($this->subqueues as $queue) {
1265
				$queue->get_queue_list($qlist);
1266
			}
1267
		}
1268
	}
1269

    
1270
	function delete_queue() {
1271
		unref_on_altq_queue_list($this->GetQname());
1272
		cleanup_queue_from_rules($this->GetQname());
1273
		unset_object_by_reference($this->GetLink());
1274
	}
1275

    
1276
	function delete_all() {
1277
		if (count($this->subqueues)) {
1278
			foreach ($this->subqueues as $q) {
1279
				$q->delete_all();
1280
				unset_object_by_reference($q->GetLink());
1281
				unset($q);
1282
			}
1283
			unset($this->subqueues);
1284
		}
1285
	}
1286

    
1287
	function &find_queue($interface, $qname) {
1288
		if ($qname == $this->GetQname()) {
1289
			return $this;
1290
		}
1291
	}
1292

    
1293
	function find_parentqueue($interface, $qname) { return; }
1294

    
1295
	function validate_input($data, &$input_errors) {
1296

    
1297
		$reqdfields[] = "name";
1298
		$reqdfieldsn[] = gettext("Name");
1299
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
1300

    
1301
		if ($data['bandwidth'] && (!is_numeric($data['bandwidth']))) {
1302
			$input_errors[] = gettext("Bandwidth must be an integer.");
1303
		}
1304
		if ($data['bandwidth'] < 0) {
1305
			$input_errors[] = gettext("Bandwidth cannot be negative.");
1306
		}
1307
		if ($data['bandwidthtype'] == "%") {
1308
			if ($data['bandwidth'] > 100 || $data['bandwidth'] < 0) {
1309
				$input_errors[] = gettext("Bandwidth in percentage should be between 1 and 100.");
1310
			}
1311
		}
1312
		if ($this->CheckBandwidth($data['bandwidth'], $data['bandwidthtype']))
1313
			$input_errors[] = "The sum of child bandwidth is higher than parent.";
1314

    
1315
		if (isset($data['priority']) && (!is_numeric($data['priority']) ||
1316
		    ($data['priority'] < 0) || ($data['priority'] > 15))) {
1317
			$input_errors[] = gettext("The priority must be an integer between 0 and 15.");
1318
		}
1319
		if ($data['qlimit'] && (!is_numeric($data['qlimit']))) {
1320
				$input_errors[] = gettext("Queue limit must be an integer");
1321
		}
1322
		if ($data['qlimit'] < 0) {
1323
				$input_errors[] = gettext("Queue limit must be positive");
1324
		}
1325
		if (!empty($data['newname']) && !preg_match("/^[a-zA-Z0-9_-]*$/", $data['newname'])) {
1326
			$input_errors[] = gettext("Queue names must be alphanumeric and _ or - only.");
1327
		}
1328
		if (!empty($data['name']) && !preg_match("/^[a-zA-Z0-9_-]*$/", $data['name'])) {
1329
			$input_errors[] = gettext("Queue names must be alphanumeric and _ or - only.");
1330
		}
1331
		$default = $this->GetDefault();
1332
		if (!empty($data['default']) && altq_get_default_queue($data['interface']) && empty($default)) {
1333
			$input_errors[] = gettext("Only one default queue per interface is allowed.");
1334
		}
1335
	}
1336

    
1337
	function ReadConfig(&$q) {
1338
		if (!empty($q['name']) && !empty($q['newname']) && $q['name'] != $q['newname']) {
1339
			$this->SetQname($q['newname']);
1340
		} else if (!empty($q['newname'])) {
1341
			$this->SetQname($q['newname']);
1342
		} else if (isset($q['name'])) {
1343
			$this->SetQname($q['name']);
1344
		}
1345
		if (isset($q['interface'])) {
1346
			$this->SetInterface($q['interface']);
1347
		}
1348
		$this->SetBandwidth($q['bandwidth']);
1349
		if ($q['bandwidthtype'] <> "") {
1350
			$this->SetBwscale($q['bandwidthtype']);
1351
		}
1352
		if (!empty($q['qlimit'])) {
1353
			$this->SetQlimit($q['qlimit']);
1354
		} else {
1355
			$this->SetQlimit(""); // Default
1356
		}
1357
		if (is_numeric($q['priority'])) {
1358
			$this->SetQPriority($q['priority']);
1359
		} else {
1360
			$this->SetQpriority("");
1361
		}
1362
		if (!empty($q['description'])) {
1363
			$this->SetDescription($q['description']);
1364
		} else {
1365
			$this->SetDescription("");
1366
		}
1367
		if (!empty($q['red'])) {
1368
			$this->SetRed($q['red']);
1369
		} else {
1370
			$this->SetRed();
1371
		}
1372
		if (!empty($q['codel'])) {
1373
			$this->SetCodel($q['codel']);
1374
		} else {
1375
			$this->SetCodel();
1376
		}
1377
		if (!empty($q['rio'])) {
1378
			$this->SetRio($q['rio']);
1379
		} else {
1380
			$this->SetRio();
1381
		}
1382
		if (!empty($q['ecn'])) {
1383
			$this->SetEcn($q['ecn']);
1384
		} else {
1385
			$this->SetEcn();
1386
		}
1387
		if (!empty($q['default'])) {
1388
			$this->SetDefault($q['default']);
1389
		} else {
1390
			$this->SetDefault();
1391
		}
1392
		if (!empty($q['enabled'])) {
1393
			$this->SetEnabled($q['enabled']);
1394
		} else {
1395
			$this->SetEnabled("");
1396
		}
1397
	}
1398

    
1399
	function build_tree() {
1400
		$tree = " <li><a href=\"firewall_shaper.php?interface=". $this->GetInterface()."&amp;queue=". $this->GetQname()."&amp;action=show";
1401
		$tree .= "\" ";
1402
		$tmpvalue = $this->GetDefault();
1403
		if (!empty($tmpvalue)) {
1404
			$tree .= " class=\"navlnk\"";
1405
		}
1406
		$tree .= " >" . $this->GetQname() . "</a>";
1407
		/*
1408
		 * Not needed here!
1409
		 * if (is_array($queues) {
1410
		 *	  $tree .= "<ul>";
1411
		 *	  foreach ($q as $queues)
1412
		 *		  $tree .= $queues['$q->GetName()']->build_tree();
1413
		 *	  endforeach
1414
		 *	  $tree .= "</ul>";
1415
		 * }
1416
		 */
1417

    
1418
		$tree .= "</li>";
1419

    
1420
		return $tree;
1421
	}
1422

    
1423
	/* Should return something like:
1424
	 * queue $qname on $qinterface bandwidth ....
1425
	 */
1426
	function build_rules(&$default = false) {
1427
		$pfq_rule = " queue ". $this->qname;
1428
		if ($this->GetInterface()) {
1429
			$pfq_rule .= " on ".get_real_interface($this->GetInterface());
1430
		}
1431
		$tmpvalue = $this->GetQpriority();
1432
		if (is_numeric($tmpvalue)) {
1433
			$pfq_rule .= " priority ".$this->GetQpriority();
1434
		}
1435
		$tmpvalue = $this->GetQlimit();
1436
		if (!empty($tmpvalue)) {
1437
			$pfq_rule .= " qlimit " . $this->GetQlimit();
1438
		}
1439
		if ($this->GetRed() || $this->GetRio() || $this->GetEcn() || $this->GetDefault() || $this->GetCodel()) {
1440
			$pfq_rule .= " priq ( ";
1441
			$tmpvalue = $this->GetRed();
1442
			if (!empty($tmpvalue)) {
1443
				$comma = 1;
1444
				$pfq_rule .= " red ";
1445
			}
1446
			$tmpvalue = $this->GetRio();
1447
			if (!empty($tmpvalue)) {
1448
				if ($comma) {
1449
					$pfq_rule .= " ,";
1450
				}
1451
				$comma = 1;
1452
				$pfq_rule .= " rio ";
1453
			}
1454
			$tmpvalue = $this->GetEcn();
1455
			if (!empty($tmpvalue)) {
1456
				if ($comma) {
1457
					$pfq_rule .= " ,";
1458
				}
1459
				$comma = 1;
1460
				$pfq_rule .= " ecn ";
1461
			}
1462
			$tmpvalue = $this->GetCodel();
1463
			if (!empty($tmpvalue)) {
1464
				if ($comma) {
1465
					$pfq_rule .= " ,";
1466
				}
1467
				$comma = 1;
1468
				$pfq_rule .= " codel ";
1469
			}
1470
			$tmpvalue = $this->GetDefault();
1471
			if (!empty($tmpvalue)) {
1472
				if ($comma) {
1473
					$pfq_rule .= " ,";
1474
				}
1475
				$pfq_rule .= " default ";
1476
				$default = true;
1477
			}
1478
			$pfq_rule .= " ) ";
1479
		}
1480

    
1481
		$pfq_rule .= " \n";
1482

    
1483
		return $pfq_rule;
1484
	}
1485

    
1486
	/*
1487
	 * To return the html form to show to user
1488
	 * for getting the parameters.
1489
	 * Should do even for first time when the
1490
	 * object is created and later when we may
1491
	 * need to update it. (2)
1492
	 */
1493

    
1494
	function build_form() {
1495

    
1496
		$sform = new Form();
1497

    
1498
		$sform->setAction("firewall_shaper.php");
1499

    
1500
		$section = new Form_Section("");
1501

    
1502
		$section->addInput(new Form_Checkbox(
1503
			'enabled',
1504
			'Enable/Disable',
1505
			'Enable/disable discipline and its children',
1506
			($this->GetEnabled() == "on"),
1507
			'on'
1508
		));
1509

    
1510
		$section->addInput(new Form_Input(
1511
			'newname',
1512
			'*Name',
1513
			'text',
1514
			$this->GetQname()
1515
		))->setHelp('Enter the name of the queue here. Do not use spaces and limit the size to 15 characters.');
1516

    
1517
		$section->addInput(new Form_Input(
1518
			'name',
1519
			null,
1520
			'hidden',
1521
			$this->GetQname()
1522
		));
1523

    
1524
		if (!is_a($this, "hfsc_queue")) {
1525
			$section->addInput(new Form_Input(
1526
				'priority',
1527
				'Priority',
1528
				'number',
1529
				$this->GetQpriority(),
1530
				['min' => '0', 'max'=> '']
1531
			))->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.');
1532
		}
1533

    
1534
		$section->addInput(new Form_Input(
1535
			'qlimit',
1536
			'Queue Limit',
1537
			'number',
1538
			$this->GetQlimit()
1539
		))->setHelp('Queue limit in packets.');
1540

    
1541
		$group = new Form_Group('Scheduler options');
1542

    
1543
		if (empty($this->subqueues)) {
1544
			$group->add(new Form_Checkbox(
1545
				'default',
1546
				null,
1547
				null,
1548
				$this->GetDefault(),
1549
				'default'
1550
			))->setHelp('Default Queue');
1551
		}
1552

    
1553
		$group->add(new Form_Checkbox(
1554
			'red',
1555
			null,
1556
			null,
1557
			!empty($this->GetRed())
1558
		))->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>');
1559

    
1560
		$group->add(new Form_Checkbox(
1561
			'rio',
1562
			null,
1563
			null,
1564
			!empty($this->GetRio())
1565
		))->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>');
1566

    
1567
		$group->add(new Form_Checkbox(
1568
			'ecn',
1569
			null,
1570
			null,
1571
			!empty($this->GetEcn())
1572
		))->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>');
1573

    
1574
		$group->add(new Form_Checkbox(
1575
			'codel',
1576
			null,
1577
			null,
1578
			!empty($this->GetCodel())
1579
		))->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>');
1580

    
1581
		$group->setHelp('Select options for this queue');
1582

    
1583
		$section->add($group);
1584

    
1585
		$section->addInput(new Form_Input(
1586
			'description',
1587
			'Description',
1588
			'text',
1589
			$this->GetDescription()
1590
		));
1591

    
1592
		$sform->add($section);
1593

    
1594
		$sform->addGlobal(new Form_Input(
1595
			'interface',
1596
			null,
1597
			'hidden',
1598
			$this->GetInterface()
1599
		));
1600

    
1601
		$sform->addGlobal(new Form_Input(
1602
			'name',
1603
			null,
1604
			'hidden',
1605
			$this->GetQname()
1606
		));
1607

    
1608
		return($sform);
1609
	}
1610

    
1611
	function build_shortform() {
1612
		/* XXX: Hacks in sight. Mostly layer violations!  */
1613
		global $g, $altq_list_queues;
1614
		global $shaperIFlist;
1615

    
1616
		$altq =& $altq_list_queues[$this->GetInterface()];
1617

    
1618
		if ($altq) {
1619
			$scheduler = $altq->GetScheduler();
1620
		}
1621

    
1622
		$form = '<dl class="dl-horizontal">';
1623
		$form .= '	<dt>';
1624
		$form .= '		<a href="firewall_shaper.php?interface=' . $this->GetInterface() . '&amp;queue=' . $this->GetQname() . '&amp;action=show">' . $shaperIFlist[$this->GetInterface()] . '</a>';
1625
		$form .= '	</dt>';
1626
		$form .= '	<dd>';
1627
		$form .=		$scheduler;
1628
		$form .= '	</dd>';
1629

    
1630
		$form .= '	<dt>';
1631
		$form .=		'Bandwidth';
1632
		$form .= '	</dt>';
1633
		$form .= '	<dd>';
1634
		$form .=		$this->GetBandwidth() . '&nbsp;' . $this->GetBwscaleText();
1635
		$form .= '	</dd>';
1636

    
1637
		$tmpvalue = $this->GetQpriority();
1638
		if (!empty($tmpvalue)) {
1639
			$form .= '	<dt>';
1640
			$form .=		'Priority';
1641
			$form .= '	<dt>';
1642
			$form .= '	<dd>';
1643
			$form .=		'On';
1644
			$form .= '	</dd>';
1645
		}
1646

    
1647
		$tmpvalue = $this->GetDefault();
1648
		if (!empty($tmpvalue)) {
1649
			$form .= '	<dt>';
1650
			$form .=		'Default';
1651
			$form .= '	<dt>';
1652
			$form .= '	<dd>';
1653
			$form .=		'On';
1654
			$form .= '	</dd>';
1655
		}
1656

    
1657
			$form .= '	<dt>';
1658
			$form .= 'Delete';
1659
			$form .= '	<dt>';
1660
			$form .= '	<dd>';
1661

    
1662
			$form .= '<a class="btn btn-danger btn-xs" href="firewall_shaper_queues.php?interface=';
1663
			$form .= $this->GetInterface() . '&amp;queue=';
1664
			$form .= $this->GetQname() . '&amp;action=delete">';
1665
			$form .= '<i class="fa fa-trash icon-embed-btn"></i>';
1666
			$form .= gettext("Delete Queue from this Interface") . '</a>';
1667

    
1668
			$form .= '	</dd>';
1669

    
1670
			$form .= '</dl>';
1671

    
1672
		return $form;
1673

    
1674
	}
1675

    
1676
	function update_altq_queue_data(&$q) {
1677
		$this->ReadConfig($q);
1678
	}
1679

    
1680
	function wconfig() {
1681
		$cflink =& get_reference_to_me_in_config($this->GetLink());
1682
		if (!is_array($cflink)) {
1683
			$cflink = array();
1684
		}
1685
		$cflink['name'] = $this->GetQname();
1686
		$cflink['interface'] = $this->GetInterface();
1687
		$cflink['qlimit'] = trim($this->GetQlimit());
1688
		if (empty($cflink['qlimit'])) {
1689
			unset($cflink['qlimit']);
1690
		}
1691
		$cflink['priority'] = trim($this->GetQpriority());
1692
		if (!is_numeric($cflink['priority'])) {
1693
			unset($cflink['priority']);
1694
		}
1695
		$cflink['description'] = trim($this->GetDescription());
1696
		if (empty($cflink['description'])) {
1697
			unset($cflink['description']);
1698
		}
1699
		$cflink['enabled'] = trim($this->GetEnabled());
1700
		if (empty($cflink['enabled'])) {
1701
			unset($cflink['enabled']);
1702
		}
1703
		$cflink['default'] = trim($this->GetDefault());
1704
		if (empty($cflink['default'])) {
1705
			unset($cflink['default']);
1706
		}
1707
		$cflink['red'] = trim($this->GetRed());
1708
		if (empty($cflink['red'])) {
1709
			unset($cflink['red']);
1710
		}
1711
		$cflink['codel'] = trim($this->GetCodel());
1712
		if (empty($cflink['codel'])) {
1713
			unset($cflink['codel']);
1714
		}
1715
		$cflink['rio'] = trim($this->GetRio());
1716
		if (empty($cflink['rio'])) {
1717
			unset($cflink['rio']);
1718
		}
1719
		$cflink['ecn'] = trim($this->GetEcn());
1720
		if (empty($cflink['ecn'])) {
1721
			unset($cflink['ecn']);
1722
		}
1723
	}
1724
}
1725

    
1726
class hfsc_queue extends priq_queue {
1727
	/* realtime */
1728
	var $realtime;
1729
	var $r_m1;
1730
	var $r_d;
1731
	var $r_m2;
1732
	/* linkshare */
1733
	var $linkshare;
1734
	var $l_m1;
1735
	var $l_d;
1736
	var $l_m2;
1737
	/* upperlimit */
1738
	var $upperlimit;
1739
	var $u_m1;
1740
	var $u_d;
1741
	var $u_m2;
1742

    
1743
	/*
1744
	 * HFSC can have nested queues.
1745
	 */
1746
	function CanHaveChildren() {
1747
		return true;
1748
	}
1749
	function GetRealtime() {
1750
		return $this->realtime;
1751
	}
1752
	function GetR_m1() {
1753
		return $this->r_m1;
1754
	}
1755
	function GetR_d() {
1756
		return $this->r_d;
1757
	}
1758
	function GetR_m2() {
1759
		return $this->r_m2;
1760
	}
1761
	function SetRealtime() {
1762
		$this->realtime = "on";
1763
	}
1764
	function DisableRealtime() {
1765
		$this->realtime = "";
1766
	}
1767
	function SetR_m1($value) {
1768
		$this->r_m1 = $value;
1769
	}
1770
	function SetR_d($value) {
1771
		$this->r_d = $value;
1772
	}
1773
	function SetR_m2($value) {
1774
		$this->r_m2 = $value;
1775
	}
1776
	function GetLinkshare() {
1777
		return $this->linkshare;
1778
	}
1779
	function DisableLinkshare() {
1780
		$this->linkshare = "";
1781
	}
1782
	function GetL_m1() {
1783
		return $this->l_m1;
1784
	}
1785
	function GetL_d() {
1786
		return $this->l_d;
1787
	}
1788
	function GetL_m2() {
1789
		return $this->l_m2;
1790
	}
1791
	function SetLinkshare() {
1792
		$this->linkshare = "on";
1793
	}
1794
	function SetL_m1($value) {
1795
		$this->l_m1 = $value;
1796
	}
1797
	function SetL_d($value) {
1798
		$this->l_d = $value;
1799
	}
1800
	function SetL_m2($value) {
1801
		$this->l_m2 = $value;
1802
	}
1803
	function GetUpperlimit() {
1804
		return $this->upperlimit;
1805
	}
1806
	function GetU_m1() {
1807
		return $this->u_m1;
1808
	}
1809
	function GetU_d() {
1810
		return $this->u_d;
1811
	}
1812
	function GetU_m2() {
1813
		return $this->u_m2;
1814
	}
1815
	function SetUpperlimit() {
1816
		$this->upperlimit = "on";
1817
	}
1818
	function DisableUpperlimit() {
1819
		$this->upperlimit = "";
1820
	}
1821
	function SetU_m1($value) {
1822
		$this->u_m1 = $value;
1823
	}
1824
	function SetU_d($value) {
1825
		$this->u_d = $value;
1826
	}
1827
	function SetU_m2($value) {
1828
		$this->u_m2 = $value;
1829
	}
1830

    
1831
	function &add_queue($interface, &$qname, &$path, &$input_errors) {
1832

    
1833
		if (!is_array($this->subqueues)) {
1834
			$this->subqueues = array();
1835
		}
1836
		$__tmp_q = new hfsc_queue(); $q =& $__tmp_q;
1837
		$q->SetInterface($this->GetInterface());
1838
		$q->SetParent($this);
1839
		$q->ReadConfig($qname);
1840
		$q->validate_input($qname, $input_errors);
1841

    
1842
		$q->SetEnabled("on");
1843
		$q->SetLink($path);
1844

    
1845
		$this->subqueues[$q->GetQname()] =& $q; //new hfsc_queue()
1846
		ref_on_altq_queue_list($this->GetQname(), $q->GetQname());
1847
		if (is_array($qname['queue'])) {
1848
			foreach ($qname['queue'] as $key1 => $que) {
1849
				array_push($path, $key1);
1850
				$q->add_queue($q->GetInterface(), $que, $path, $input_errors);
1851
				array_pop($path);
1852
			}
1853
		}
1854

    
1855
		return $q;
1856
	}
1857

    
1858
	function copy_queue($interface, &$cflink) {
1859

    
1860
		$cflink['name'] = $this->GetQname();
1861
		$cflink['interface'] = $interface;
1862
		$cflink['qlimit'] = trim($this->GetQlimit());
1863
		if (empty($cflink['qlimit'])) {
1864
			unset($cflink['qlimit']);
1865
		}
1866
		$cflink['priority'] = trim($this->GetQpriority());
1867
		if (empty($cflink['priority'])) {
1868
			unset($cflink['priority']);
1869
		}
1870
		$cflink['description'] = trim($this->GetDescription());
1871
		if (empty($cflink['description'])) {
1872
			unset($cflink['description']);
1873
		}
1874
		$cflink['bandwidth'] = $this->GetBandwidth();
1875
		$cflink['bandwidthtype'] = $this->GetBwscale();
1876
		$cflink['enabled'] = trim($this->GetEnabled());
1877
		if (empty($cflink['enabled'])) {
1878
			unset($cflink['enabled']);
1879
		}
1880
		$cflink['default'] = trim($this->GetDefault());
1881
		if (empty($cflink['default'])) {
1882
			unset($cflink['default']);
1883
		}
1884
		$cflink['red'] = trim($this->GetRed());
1885
		if (empty($cflink['red'])) {
1886
			unset($cflink['red']);
1887
		}
1888
		$cflink['rio'] = trim($this->GetRio());
1889
		if (empty($cflink['rio'])) {
1890
			unset($cflink['rio']);
1891
		}
1892
		$cflink['ecn'] = trim($this->GetEcn());
1893
		if (empty($cflink['ecn'])) {
1894
			unset($cflink['ecn']);
1895
		}
1896
		if ($this->GetLinkshare() <> "") {
1897
			if ($this->GetL_m1() <> "") {
1898
				$cflink['linkshare1'] = $this->GetL_m1();
1899
				$cflink['linkshare2'] = $this->GetL_d();
1900
				$cflink['linkshare'] = "on";
1901
			} else {
1902
				unset($cflink['linkshare1']);
1903
				unset($cflink['linkshare2']);
1904
				unset($cflink['linkshare']);
1905
			}
1906
			if ($this->GetL_m2() <> "") {
1907
				$cflink['linkshare3'] = $this->GetL_m2();
1908
				$cflink['linkshare'] = "on";
1909
			} else {
1910
				unset($cflink['linkshare3']);
1911
				unset($cflink['linkshare']);
1912
			}
1913
		}
1914
		if ($this->GetRealtime() <> "") {
1915
			if ($this->GetR_m1() <> "") {
1916
				$cflink['realtime1'] = $this->GetR_m1();
1917
				$cflink['realtime2'] = $this->GetR_d();
1918
				$cflink['realtime'] = "on";
1919
			} else {
1920
				unset($cflink['realtime1']);
1921
				unset($cflink['realtime2']);
1922
				unset($cflink['realtime']);
1923
			}
1924
			if ($this->GetR_m2() <> "") {
1925
				$cflink['realtime3'] = $this->GetR_m2();
1926
				$cflink['realtime'] = "on";
1927
			} else {
1928
				unset($cflink['realtime3']);
1929
				unset($cflink['realtime']);
1930
			}
1931
		}
1932
		if ($this->GetUpperlimit() <> "") {
1933
			if ($this->GetU_m1() <> "") {
1934
				$cflink['upperlimit1'] = $this->GetU_m1();
1935
				$cflink['upperlimit2'] = $this->GetU_d();
1936
				$cflink['upperlimit'] = "on";
1937
			} else {
1938
				unset($cflink['upperlimit']);
1939
				unset($cflink['upperlimit1']);
1940
				unset($cflink['upperlimit2']);
1941
			}
1942
			if ($this->GetU_m2() <> "") {
1943
				$cflink['upperlimit3'] = $this->GetU_m2();
1944
				$cflink['upperlimit'] = "on";
1945
			} else {
1946
				unset($cflink['upperlimit3']);
1947
				unset($cflink['upperlimit']);
1948
			}
1949
		}
1950

    
1951
		if (is_array($this->subqueues)) {
1952
			$cflinkp['queue'] = array();
1953
			foreach ($this->subqueues as $q) {
1954
				$cflink['queue'][$q->GetQname()] = array();
1955
				$q->copy_queue($interface, $cflink['queue'][$q->GetQname()]);
1956
			}
1957
		}
1958
	}
1959

    
1960
	function delete_queue() {
1961
		unref_on_altq_queue_list($this->GetQname());
1962
		cleanup_queue_from_rules($this->GetQname());
1963
		$parent =& $this->GetParent();
1964
		foreach ($this->subqueues as $q)
1965
			$q->delete_queue();
1966
		unset_object_by_reference($this->GetLink());
1967
	}
1968

    
1969
	/*
1970
	 * Should search even its children
1971
	 */
1972
	function &find_queue($interface, $qname) {
1973
		if ($qname == $this->GetQname()) {
1974
			return $this;
1975
		}
1976

    
1977
		foreach ($this->subqueues as $q) {
1978
			$result =& $q->find_queue("", $qname);
1979
			if ($result) {
1980
				return $result;
1981
			}
1982
		}
1983
	}
1984

    
1985
	function &find_parentqueue($interface, $qname) {
1986
		if ($this->subqueues[$qname]) {
1987
			return $this;
1988
		}
1989
		foreach ($this->subqueues as $q) {
1990
			$result = $q->find_parentqueue("", $qname);
1991
			if ($result) {
1992
				return $result;
1993
			}
1994
		}
1995
	}
1996

    
1997
	function validate_input($data, &$input_errors) {
1998
		parent::validate_input($data, $input_errors);
1999

    
2000
		$reqdfields[] = "bandwidth";
2001
		$reqdfieldsn[] = gettext("Bandwidth");
2002
		$reqdfields[] = "bandwidthtype";
2003
		$reqdfieldsn[] = gettext("Bandwidthtype");
2004

    
2005
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
2006

    
2007
		if (isset($data['linkshare3']) && $data['linkshare3'] <> "") {
2008
			if ($data['bandwidth'] && (!is_numeric($data['bandwidth']))) {
2009
				$input_errors[] = gettext("Bandwidth must be an integer.");
2010
			}
2011

    
2012
			if ($data['bandwidth'] < 0) {
2013
				$input_errors[] = gettext("Bandwidth cannot be negative.");
2014
			}
2015

    
2016
			if ($data['bandwidthtype'] == "%") {
2017
				if ($data['bandwidth'] > 100 || $data['bandwidth'] < 0) {
2018
					$input_errors[] = gettext("Bandwidth in percentage should be between 1 and 100.");
2019
				}
2020
			}
2021
		}
2022

    
2023
		if ($data['upperlimit1'] <> "" && $data['upperlimit2'] == "") {
2024
			$input_errors[] = gettext("upperlimit service curve defined but missing (d) value");
2025
		}
2026
		if ($data['upperlimit2'] <> "" && $data['upperlimit1'] == "") {
2027
			$input_errors[] = gettext("upperlimit service curve defined but missing initial bandwidth (m1) value");
2028
		}
2029
		if ($data['upperlimit1'] <> "" && !is_valid_shaperbw($data['upperlimit1'])) {
2030
			$input_errors[] = gettext("upperlimit m1 value needs to be Kb, Mb, Gb, or %");
2031
		}
2032
		if ($data['upperlimit2'] <> "" && !is_numeric($data['upperlimit2'])) {
2033
			$input_errors[] = gettext("upperlimit d value needs to be numeric");
2034
		}
2035
		if ($data['upperlimit3'] <> "" && !is_valid_shaperbw($data['upperlimit3'])) {
2036
			$input_errors[] = gettext("upperlimit m2 value needs to be Kb, Mb, Gb, or %");
2037
		}
2038

    
2039
		/*
2040
		if (isset($data['upperlimit']) && $data['upperlimit3'] <> "" && $data['upperlimit1'] <> "") {
2041
			$bw_1 = get_hfsc_bandwidth($this, $data['upperlimit1']);
2042
			$bw_2 = get_hfsc_bandwidth($this, $data['upperlimit3']);
2043
			if (floatval($bw_1) < floatval($bw_2)) {
2044
				$input_errors[] = ("upperlimit m1 cannot be smaller than m2");
2045
			}
2046

    
2047
			if (get_interface_bandwidth($this) < (0.8 * (floatval($bw_1) + floatval($bw_2)))) {
2048
				$input_errors[] = ("upperlimit specification exceeds 80% of allowable allocation.");
2049
			}
2050
		}
2051
		*/
2052
		if ($data['linkshare1'] <> "" && $data['linkshare2'] == "") {
2053
			$input_errors[] = gettext("linkshare service curve defined but missing (d) value");
2054
		}
2055
		if ($data['linkshare2'] <> "" && $data['linkshare1'] == "") {
2056
			$input_errors[] = gettext("linkshare service curve defined but missing initial bandwidth (m1) value");
2057
		}
2058
		if ($data['linkshare1'] <> "" && !is_valid_shaperbw($data['linkshare1'])) {
2059
			$input_errors[] = gettext("linkshare m1 value needs to be Kb, Mb, Gb, or %");
2060
		}
2061
		if ($data['linkshare2'] <> "" && !is_numeric($data['linkshare2'])) {
2062
			$input_errors[] = gettext("linkshare d value needs to be numeric");
2063
		}
2064
		if ($data['linkshare3'] <> "" && !is_valid_shaperbw($data['linkshare3'])) {
2065
			$input_errors[] = gettext("linkshare m2 value needs to be Kb, Mb, Gb, or %");
2066
		}
2067
		if ($data['realtime1'] <> "" && $data['realtime2'] == "") {
2068
			$input_errors[] = gettext("realtime service curve defined but missing (d) value");
2069
		}
2070
		if ($data['realtime2'] <> "" && $data['realtime1'] == "") {
2071
			$input_errors[] = gettext("realtime service curve defined but missing initial bandwidth (m1) value");
2072
		}
2073

    
2074
		/*
2075
		if (isset($data['linkshare']) && $data['linkshare3'] <> "" && $data['linkshare1'] <> "" && 0) {
2076
			$bw_1 = get_hfsc_bandwidth($this, $data['linkshare1']);
2077
			$bw_2 = get_hfsc_bandwidth($this, $data['linkshare3']);
2078
			if (floatval($bw_1) < floatval($bw_2)) {
2079
				$input_errors[] = ("linkshare m1 cannot be smaller than m2");
2080
			}
2081

    
2082
			if (get_interface_bandwidth($this) < (0.8 * (floatval($bw_1) + floatval($bw_2)))) {
2083
				$input_errors[] = ("linkshare specification exceeds 80% of allowable allocation.");
2084
			}
2085
		}
2086
		*/
2087

    
2088
		if ($data['realtime1'] <> "" && !is_valid_shaperbw($data['realtime1'])) {
2089
			$input_errors[] = gettext("realtime m1 value needs to be Kb, Mb, Gb, or %");
2090
		}
2091
		if ($data['realtime2'] <> "" && !is_numeric($data['realtime2'])) {
2092
			$input_errors[] = gettext("realtime d value needs to be numeric");
2093
		}
2094
		if ($data['realtime3'] <> "" && !is_valid_shaperbw($data['realtime3'])) {
2095
			$input_errors[] = gettext("realtime m2 value needs to be Kb, Mb, Gb, or %");
2096
		}
2097

    
2098
		/*
2099
		if (isset($data['realtime']) && $data['realtime3'] <> "" && $data['realtime1'] <> "" && 0) {
2100
			$bw_1 = get_hfsc_bandwidth($this, $data['realtime1']);
2101
			$bw_2 = get_hfsc_bandwidth($this, $data['realtime3']);
2102
			if (floatval($bw_1) < floatval($bw_2)) {
2103
				$input_errors[] = ("realtime m1 cannot be smaller than m2");
2104
			}
2105

    
2106
			if (get_interface_bandwidth($this) < (0.8 * (floatval($bw_1) + floatval($bw_2)))) {
2107
				$input_errors[] = ("realtime specification exceeds 80% of allowable allocation.");
2108
			}
2109
		}
2110
		*/
2111
	}
2112

    
2113
	function ReadConfig(&$cflink) {
2114
		if (!empty($cflink['linkshare'])) {
2115
			if (!empty($cflink['linkshare1'])) {
2116
				$this->SetL_m1($cflink['linkshare1']);
2117
				$this->SetL_d($cflink['linkshare2']);
2118
				$this->SetLinkshare();
2119
			} else {
2120
				$this->SetL_m1("");
2121
				$this->SetL_d("");
2122
				$this->DisableLinkshare();
2123
			}
2124
			if (!empty($cflink['linkshare3'])) {
2125
				$this->SetL_m2($cflink['linkshare3']);
2126
				$this->SetLinkshare();
2127
			}
2128
		} else {
2129
			$this->DisableLinkshare();
2130
		}
2131
		if (!empty($cflink['realtime'])) {
2132
			if (!empty($cflink['realtime1'])) {
2133
				$this->SetR_m1($cflink['realtime1']);
2134
				$this->SetR_d($cflink['realtime2']);
2135
				$this->SetRealtime();
2136
			} else {
2137
				$this->SetR_m1("");
2138
				$this->SetR_d("");
2139
				$this->DisableRealtime();
2140
			}
2141
			if (!empty($cflink['realtime3'])) {
2142
				$this->SetR_m2($cflink['realtime3']);
2143
				$this->SetRealtime();
2144
			}
2145
		} else {
2146
			$this->DisableRealtime();
2147
		}
2148
		if (!empty($cflink['upperlimit'])) {
2149
			if (!empty($cflink['upperlimit1'])) {
2150
				$this->SetU_m1($cflink['upperlimit1']);
2151
				$this->SetU_d($cflink['upperlimit2']);
2152
				$this->SetUpperlimit();
2153
			} else {
2154
				$this->SetU_m1("");
2155
				$this->SetU_d("");
2156
				$this->DisableUpperlimit();
2157
			}
2158
			if (!empty($cflink['upperlimit3'])) {
2159
				$this->SetU_m2($cflink['upperlimit3']);
2160
				$this->SetUpperlimit();
2161
			}
2162
		} else {
2163
			$this->DisableUpperlimit();
2164
		}
2165
		parent::ReadConfig($cflink);
2166
	}
2167

    
2168
	function build_tree() {
2169
		$tree = " <li><a href=\"firewall_shaper.php?interface=" . $this->GetInterface() ."&amp;queue=" . $this->GetQname()."&amp;action=show";
2170
		$tree .= "\" ";
2171
		$tmpvalue = $this->GetDefault();
2172
		if (!empty($tmpvalue)) {
2173
			$tree .= " class=\"navlnk\"";
2174
		}
2175
		$tree .= " >" . $this->GetQname() . "</a>";
2176
		if (is_array($this->subqueues)) {
2177
			$tree .= "<ul>";
2178
			foreach ($this->subqueues as $q) {
2179
				$tree .= $q->build_tree();
2180
			}
2181
			$tree .= "</ul>";
2182
		}
2183
		$tree .= "</li>";
2184
		return $tree;
2185
	}
2186

    
2187
	/* Even this should take children into consideration */
2188
	function build_rules(&$default = false) {
2189

    
2190
		$pfq_rule = " queue ". $this->qname;
2191
		if ($this->GetInterface()) {
2192
			$pfq_rule .= " on ".get_real_interface($this->GetInterface());
2193
		}
2194
		if ($this->GetBandwidth() && $this->GetBwscale()) {
2195
			$pfq_rule .= " bandwidth ".trim($this->GetBandwidth()).$this->GetBwscale();
2196
		}
2197

    
2198
		$tmpvalue = $this->GetQlimit();
2199
		if (!empty($tmpvalue)) {
2200
			$pfq_rule .= " qlimit " . $this->GetQlimit();
2201
		}
2202
		if ($this->GetDefault() || $this->GetRed() || $this->GetRio() || $this->GetEcn() || $this->GetCodel() || $this->GetRealtime() <> "" || $this->GetLinkshare() <> "" || $this->GetUpperlimit() <> "") {
2203
			$pfq_rule .= " hfsc ( ";
2204
			$tmpvalue = $this->GetRed();
2205
			if (!empty($tmpvalue)) {
2206
				$comma = 1;
2207
				$pfq_rule .= " red ";
2208
			}
2209

    
2210
			$tmpvalue = $this->GetRio();
2211
			if (!empty($tmpvalue)) {
2212
				if ($comma) {
2213
					$pfq_rule .= " ,";
2214
				}
2215
				$comma = 1;
2216
				$pfq_rule .= " rio ";
2217
			}
2218
			$tmpvalue = $this->GetEcn();
2219
			if (!empty($tmpvalue)) {
2220
				if ($comma) {
2221
					$pfq_rule .= " ,";
2222
				}
2223
				$comma = 1;
2224
				$pfq_rule .= " ecn ";
2225
			}
2226
			$tmpvalue = $this->GetCodel();
2227
			if (!empty($tmpvalue)) {
2228
				if ($comma) {
2229
					$pfq_rule .= " ,";
2230
				}
2231
				$comma = 1;
2232
				$pfq_rule .= " codel ";
2233
			}
2234
			$tmpvalue = $this->GetDefault();
2235
			if (!empty($tmpvalue)) {
2236
				if ($comma) {
2237
					$pfq_rule .= " ,";
2238
				}
2239
				$comma = 1;
2240
				$pfq_rule .= " default ";
2241
				$default = true;
2242
			}
2243

    
2244
			if ($this->GetRealtime() <> "") {
2245
				if ($comma) {
2246
					$pfq_rule .= " , ";
2247
				}
2248
				if ($this->GetR_m1() <> "" && $this->GetR_d() <> "" && $this->GetR_m2() <> "") {
2249
					$pfq_rule .= " realtime (".$this->GetR_m1() . ", " . $this->GetR_d().", ". $this->GetR_m2() .") ";
2250
				} else if ($this->GetR_m2() <> "") {
2251
					$pfq_rule .= " realtime " . $this->GetR_m2();
2252
				}
2253
				$comma = 1;
2254
			}
2255
			if ($this->GetLinkshare() <> "") {
2256
				if ($comma) {
2257
					$pfq_rule .= " ,";
2258
				}
2259
				if ($this->GetL_m1() <> "" && $this->GetL_d() <> "" && $this->GetL_m2() <> "") {
2260
					$pfq_rule .= " linkshare (".$this->GetL_m1(). ", ". $this->GetL_d(). ", ". $this->GetL_m2(). ") ";
2261
				} else if ($this->GetL_m2() <> "") {
2262
					$pfq_rule .= " linkshare " . $this->GetL_m2() . " ";
2263
				}
2264
				$comma = 1;
2265
			}
2266
			if ($this->GetUpperlimit() <> "") {
2267
				if ($comma) {
2268
					$pfq_rule .= " ,";
2269
				}
2270
				if ($this->GetU_m1() <> "" && $this->GetU_d() <> "" && $this->GetU_m2() <> "") {
2271
							$pfq_rule .= " upperlimit (".$this->GetU_m1().", ". $this->GetU_d().", ". $this->GetU_m2(). ") ";
2272
				} else if ($this->GetU_m2() <> "") {
2273
					$pfq_rule .= " upperlimit " . $this->GetU_m2() . " ";
2274
				}
2275
			}
2276
			$pfq_rule .= " ) ";
2277
		}
2278
		if (count($this->subqueues)) {
2279
			$i = count($this->subqueues);
2280
			$pfq_rule .= " { ";
2281
			foreach ($this->subqueues as $qkey => $qnone) {
2282
				if ($i > 1) {
2283
					$i--;
2284
					$pfq_rule .= " {$qkey}, ";
2285
				} else {
2286
					$pfq_rule .= " {$qkey} ";
2287
				}
2288
			}
2289
			$pfq_rule .= " } \n";
2290
			foreach ($this->subqueues as $q) {
2291
				$pfq_rule .= $q->build_rules($default);
2292
			}
2293
		}
2294

    
2295
		$pfq_rule .= " \n";
2296

    
2297
		return $pfq_rule;
2298
	}
2299

    
2300
	function build_javascript() {
2301

    
2302
		$javascript = <<<EOJS
2303
<script type="text/javascript">
2304
//<![CDATA[
2305
	events.push(function(){
2306

    
2307
		// Disables the specified input element
2308
		function disableInput(id, disable) {
2309
			$('#' + id).prop("disabled", disable);
2310
		}
2311

    
2312
		// Upperlimit
2313
		function enable_upperlimit() {
2314
			disableInput('upperlimit1', !$('#upperlimit').prop('checked'));
2315
			disableInput('upperlimit2', !$('#upperlimit').prop('checked'));
2316
			disableInput('upperlimit3', !$('#upperlimit').prop('checked'));
2317
		}
2318

    
2319
		$('#upperlimit').click(function () {
2320
			enable_upperlimit();
2321
		});
2322

    
2323
		enable_upperlimit();
2324

    
2325
		// realtime
2326
		function enable_realtime() {
2327
			disableInput('realtime1', !$('#realtime').prop('checked'));
2328
			disableInput('realtime2', !$('#realtime').prop('checked'));
2329
			disableInput('realtime3', !$('#realtime').prop('checked'));
2330
		}
2331

    
2332
		$('#realtime').click(function () {
2333
			enable_realtime();
2334
		});
2335

    
2336
		enable_realtime();
2337

    
2338
		// linkshare
2339
		function enable_linkshare() {
2340
			disableInput('linkshare1', !$('#linkshare').prop('checked'));
2341
			disableInput('linkshare2', !$('#linkshare').prop('checked'));
2342
			disableInput('linkshare3', !$('#linkshare').prop('checked'));
2343
		}
2344

    
2345
		$('#linkshare').click(function () {
2346
			enable_linkshare();
2347
		});
2348

    
2349
		enable_linkshare();
2350
	});
2351
//]]>
2352
</script>
2353
EOJS;
2354

    
2355
		return $javascript;
2356
	}
2357

    
2358
	function build_form() {
2359

    
2360
		$sform = parent::build_form();
2361

    
2362
		$section = new Form_Section('Service Curve (sc)');
2363

    
2364
		$group = new Form_Group('Bandwidth');
2365

    
2366
		$group->add(new Form_Input(
2367
			'bandwidth',
2368
			null,
2369
			'number',
2370
			$this->GetBandwidth(),
2371
			['step' => 'any', 'min' => '0.000']
2372
		));
2373

    
2374
		$group->add(new Form_Select(
2375
			'bandwidthtype',
2376
			null,
2377
			$this->GetBwscale(),
2378
			array('Kb' => 'Kbit/s',
2379
				  'Mb' => 'Mbit/s',
2380
				  'Gb' => 'Gbit/s',
2381
				  'b' => 'Bit/s',
2382
				  '%' => '%')
2383
		));
2384

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

    
2387
		$section->add($group);
2388

    
2389
		$group = new Form_Group('Max bandwidth for queue.');
2390

    
2391
		$group->add(new Form_Checkbox(
2392
			'upperlimit',
2393
			null,
2394
			'Upper Limit',
2395
			($this->GetUpperlimit()<> "")
2396
		));
2397

    
2398
		$group->add(new Form_Input(
2399
			'upperlimit1',
2400
			null,
2401
			'text',
2402
			$this->GetU_m1()
2403
		))->setHelp('m1');
2404

    
2405
		$group->add(new Form_Input(
2406
			'upperlimit2',
2407
			null,
2408
			'text',
2409
			$this->GetU_d()
2410
		))->setHelp('d');
2411

    
2412
		$group->add(new Form_Input(
2413
			'upperlimit3',
2414
			null,
2415
			'text',
2416
			$this->GetU_m2()
2417
		))->setHelp('m2');
2418

    
2419

    
2420
		$section->add($group);
2421

    
2422
		$group = new Form_Group('Min bandwidth for queue.');
2423

    
2424
		$group->add(new Form_Checkbox(
2425
			'realtime',
2426
			null,
2427
			'Real Time',
2428
			($this->GetRealtime()<> "")
2429
		));
2430

    
2431
		$group->add(new Form_Input(
2432
			'realtime1',
2433
			null,
2434
			'text',
2435
			$this->GetR_m1()
2436
		))->setHelp('m1');
2437

    
2438
		$group->add(new Form_Input(
2439
			'realtime2',
2440
			null,
2441
			'text',
2442
			$this->GetR_d()
2443
		))->setHelp('d');
2444

    
2445
		$group->add(new Form_Input(
2446
			'realtime3',
2447
			null,
2448
			'text',
2449
			$this->GetR_m2()
2450
		))->setHelp('m2');
2451

    
2452
		$section->add($group);
2453

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

    
2456
		$group->add(new Form_Checkbox(
2457
			'linkshare',
2458
			null,
2459
			'Link Share',
2460
			($this->GetLinkshare()<> "")
2461
		));
2462

    
2463
		$group->add(new Form_Input(
2464
			'linkshare1',
2465
			null,
2466
			'text',
2467
			$this->GetL_m1()
2468
		))->setHelp('m1');
2469

    
2470
		$group->add(new Form_Input(
2471
			'linkshare2',
2472
			null,
2473
			'text',
2474
			$this->GetL_d()
2475
		))->setHelp('d');
2476

    
2477
		$group->add(new Form_Input(
2478
			'linkshare3',
2479
			null,
2480
			'text',
2481
			$this->GetL_m2()
2482
		))->setHelp('m2');
2483

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

    
2490
		$section->add($group);
2491

    
2492
		$sform->add($section);
2493

    
2494
		return($sform);
2495
	}
2496

    
2497
	function update_altq_queue_data(&$data) {
2498
		$this->ReadConfig($data);
2499
	}
2500

    
2501
	function wconfig() {
2502
		$cflink =& get_reference_to_me_in_config($this->GetLink());
2503
		if (!is_array($cflink)) {
2504
			$cflink = array();
2505
		}
2506
		$cflink['name'] = $this->GetQname();
2507
		$cflink['interface'] = $this->GetInterface();
2508
		$cflink['qlimit'] = trim($this->GetQlimit());
2509
		if (empty($cflink['qlimit'])) {
2510
			unset($cflink['qlimit']);
2511
		}
2512
		$cflink['priority'] = $this->GetQpriority();
2513
		if (!is_numericint($cflink['priority'])) {
2514
			unset($cflink['priority']);
2515
		}
2516
		$cflink['description'] = $this->GetDescription();
2517
		if (empty($cflink['description'])) {
2518
			unset($cflink['description']);
2519
		}
2520
		$cflink['bandwidth'] = $this->GetBandwidth();
2521
		$cflink['bandwidthtype'] = $this->GetBwscale();
2522
		$cflink['enabled'] = $this->GetEnabled();
2523
		if (empty($cflink['enabled'])) {
2524
			unset($cflink['enabled']);
2525
		}
2526
		$cflink['default'] = $this->GetDefault();
2527
		if (empty($cflink['default'])) {
2528
			unset($cflink['default']);
2529
		}
2530
		$cflink['red'] = trim($this->GetRed());
2531
		if (empty($cflink['red'])) {
2532
			unset($cflink['red']);
2533
		}
2534
		$cflink['rio'] = $this->GetRio();
2535
		if (empty($cflink['rio'])) {
2536
			unset($cflink['rio']);
2537
		}
2538
		$cflink['ecn'] = trim($this->GetEcn());
2539
		if (empty($cflink['ecn'])) {
2540
			unset($cflink['ecn']);
2541
		}
2542
		$cflink['codel'] = trim($this->GetCodel());
2543
		if (empty($cflink['codel'])) {
2544
			unset($cflink['codel']);
2545
		}
2546
		if ($this->GetLinkshare() <> "") {
2547
			if ($this->GetL_m1() <> "") {
2548
				$cflink['linkshare1'] = $this->GetL_m1();
2549
				$cflink['linkshare2'] = $this->GetL_d();
2550
				$cflink['linkshare'] = "on";
2551
			} else {
2552
				unset($cflink['linkshare']);
2553
				unset($cflink['linkshare1']);
2554
				unset($cflink['linkshare2']);
2555
			}
2556
			if ($this->GetL_m2() <> "") {
2557
				$cflink['linkshare3'] = $this->GetL_m2();
2558
				$cflink['linkshare'] = "on";
2559
			} else {
2560
				unset($cflink['linkshare']);
2561
				unset($cflink['linkshare3']);
2562
			}
2563
		} else {
2564
			unset($cflink['linkshare']);
2565
			unset($cflink['linkshare1']);
2566
			unset($cflink['linkshare2']);
2567
			unset($cflink['linkshare3']);
2568
		}
2569
		if ($this->GetRealtime() <> "") {
2570
			if ($this->GetR_m1() <> "") {
2571
				$cflink['realtime1'] = $this->GetR_m1();
2572
				$cflink['realtime2'] = $this->GetR_d();
2573
				$cflink['realtime'] = "on";
2574
			} else {
2575
				unset($cflink['realtime']);
2576
				unset($cflink['realtime1']);
2577
				unset($cflink['realtime2']);
2578
			}
2579
			if ($this->GetR_m2() <> "") {
2580
				$cflink['realtime3'] = $this->GetR_m2();
2581
				$cflink['realtime'] = "on";
2582
			} else {
2583
				unset($cflink['realtime']);
2584
				unset($cflink['realtime3']);
2585
			}
2586
		} else {
2587
			unset($cflink['realtime']);
2588
			unset($cflink['realtime1']);
2589
			unset($cflink['realtime2']);
2590
			unset($cflink['realtime3']);
2591
		}
2592
		if ($this->GetUpperlimit() <> "") {
2593
			if ($this->GetU_m1() <> "") {
2594
				$cflink['upperlimit1'] = $this->GetU_m1();
2595
				$cflink['upperlimit2'] = $this->GetU_d();
2596
				$cflink['upperlimit'] = "on";
2597
			} else {
2598
				unset($cflink['upperlimit']);
2599
				unset($cflink['upperlimit1']);
2600
				unset($cflink['upperlimit2']);
2601
			}
2602
			if ($this->GetU_m2() <> "") {
2603
				$cflink['upperlimit3'] = $this->GetU_m2();
2604
				$cflink['upperlimit'] = "on";
2605
			} else {
2606
				unset($cflink['upperlimit']);
2607
				unset($cflink['upperlimit3']);
2608
			}
2609
		} else {
2610
			unset($cflink['upperlimit']);
2611
			unset($cflink['upperlimit1']);
2612
			unset($cflink['upperlimit2']);
2613
			unset($cflink['upperlimit3']);
2614
		}
2615
	}
2616
}
2617

    
2618
class cbq_queue extends priq_queue {
2619
	var $qborrow = "";
2620

    
2621
	function GetBorrow() {
2622
		return $this->qborrow;
2623
	}
2624
	function SetBorrow($borrow) {
2625
		$this->qborrow = $borrow;
2626
	}
2627
	function CanHaveChildren() {
2628
		return true;
2629
	}
2630

    
2631
	function &add_queue($interface, &$qname, &$path, &$input_errors) {
2632

    
2633
		if (!is_array($this->subqueues)) {
2634
			$this->subqueues = array();
2635
		}
2636
		$__tmp_q = new cbq_queue(); $q =& $__tmp_q;
2637
		$q->SetInterface($this->GetInterface());
2638
		$q->SetParent($this);
2639
		$q->ReadConfig($qname);
2640
		$q->validate_input($qname, $input_errors);
2641

    
2642
		$q->SetEnabled("on");
2643
		$q->SetLink($path);
2644
		$this->subqueues[$q->GetQName()] = &$q;
2645
		ref_on_altq_queue_list($this->GetQname(), $q->GetQname());
2646
		if (is_array($qname['queue'])) {
2647
			foreach ($qname['queue'] as $key1 => $que) {
2648
				array_push($path, $key1);
2649
				$q->add_queue($q->GetInterface(), $que, $path, $input_errors);
2650
				array_pop($path);
2651
			}
2652
		}
2653

    
2654
		return $q;
2655
	}
2656

    
2657
	function copy_queue($interface, &$cflink) {
2658

    
2659
		$cflink['interface'] = $interface;
2660
		$cflink['qlimit'] = trim($this->GetQlimit());
2661
		if (empty($clink['qlimit'])) {
2662
			unset($cflink['qlimit']);
2663
		}
2664
		$cflink['priority'] = trim($this->GetQpriority());
2665
		if (!is_numeric($cflink['priority'])) {
2666
			unset($cflink['priority']);
2667
		}
2668
		$cflink['name'] = $this->GetQname();
2669
		$cflink['description'] = trim($this->GetDescription());
2670
		if (empty($cflink['description'])) {
2671
			unset($cflink['description']);
2672
		}
2673
		$cflink['bandwidth'] = $this->GetBandwidth();
2674
		$cflink['bandwidthtype'] = $this->GetBwscale();
2675
		$cflink['enabled'] = trim($this->GetEnabled());
2676
		if (empty($cflink['enabled'])) {
2677
			unset($cflink['enabled']);
2678
		}
2679
		$cflink['default'] = trim($this->GetDefault());
2680
		if (empty($cflink['default'])) {
2681
			unset($cflink['default']);
2682
		}
2683
		$cflink['red'] = trim($this->GetRed());
2684
		if (empty($cflink['red'])) {
2685
			unset($cflink['red']);
2686
		}
2687
		$cflink['rio'] = trim($this->GetRio());
2688
		if (empty($cflink['rio'])) {
2689
			unset($cflink['rio']);
2690
		}
2691
		$cflink['ecn'] = trim($this->GetEcn());
2692
		if (empty($cflink['ecn'])) {
2693
			unset($cflink['ecn']);
2694
		}
2695
		$cflink['borrow'] = trim($this->GetBorrow());
2696
		if (empty($cflink['borrow'])) {
2697
			unset($cflink['borrow']);
2698
		}
2699
		if (is_array($this->queues)) {
2700
			$cflinkp['queue'] = array();
2701
			foreach ($this->subqueues as $q) {
2702
				$cflink['queue'][$q->GetQname()] = array();
2703
				$q->copy_queue($interface, $cflink['queue'][$q->GetQname()]);
2704
			}
2705
		}
2706
	}
2707

    
2708
	/*
2709
	 * Should search even its children
2710
	 */
2711
	function &find_queue($interface, $qname) {
2712
		if ($qname == $this->GetQname()) {
2713
			return $this;
2714
		}
2715
		foreach ($this->subqueues as $q) {
2716
			$result =& $q->find_queue("", $qname);
2717
			if ($result) {
2718
				return $result;
2719
			}
2720
		}
2721
	}
2722

    
2723
	function &find_parentqueue($interface, $qname) {
2724
		if ($this->subqueues[$qname]) {
2725
			return $this;
2726
		}
2727
		foreach ($this->subqueues as $q) {
2728
			$result = $q->find_parentqueue("", $qname);
2729
			if ($result) {
2730
				return $result;
2731
			}
2732
		}
2733
	}
2734

    
2735
	function delete_queue() {
2736
		unref_on_altq_queue_list($this->GetQname());
2737
		cleanup_queue_from_rules($this->GetQname());
2738
		foreach ($this->subqueues as $q)
2739
			$q->delete_queue();
2740
		unset_object_by_reference($this->GetLink());
2741
	}
2742

    
2743
	function validate_input($data, &$input_errors) {
2744
		parent::validate_input($data, $input_errors);
2745

    
2746
		if ($data['priority'] > 7) {
2747
				$input_errors[] = gettext("Priority must be an integer between 0 and 7.");
2748
		}
2749
		$reqdfields[] = "bandwidth";
2750
		$reqdfieldsn[] = gettext("Bandwidth");
2751
		$reqdfields[] = "bandwidthtype";
2752
		$reqdfieldsn[] = gettext("Bandwidthtype");
2753

    
2754
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
2755
	}
2756

    
2757
	function ReadConfig(&$q) {
2758
		parent::ReadConfig($q);
2759
		if (!empty($q['borrow'])) {
2760
			$this->SetBorrow("on");
2761
		} else {
2762
			$this->SetBorrow("");
2763
		}
2764
	}
2765

    
2766
	function build_javascript() {
2767
		return parent::build_javascript();
2768
	}
2769

    
2770
	function build_tree() {
2771
		$tree = " <li><a href=\"firewall_shaper.php?interface=" . $this->GetInterface()."&amp;queue=" . $this->GetQname()."&amp;action=show";
2772
		$tree .= "\" ";
2773
		$tmpvalue = trim($this->GetDefault());
2774
		if (!empty($tmpvalue)) {
2775
			$tree .= " class=\"navlnk\"";
2776
		}
2777
		$tree .= " >" . $this->GetQname() . "</a>";
2778
		if (is_array($this->subqueues)) {
2779
			$tree .= "<ul>";
2780
			foreach ($this->subqueues as $q) {
2781
				$tree .= $q->build_tree();
2782
			}
2783
			$tree .= "</ul>";
2784
		}
2785
		$tree .= "</li>";
2786
		return $tree;
2787
	}
2788

    
2789
	/* Even this should take children into consideration */
2790
	function build_rules(&$default = false) {
2791
		$pfq_rule = "queue ". $this->qname;
2792
		if ($this->GetInterface()) {
2793
			$pfq_rule .= " on ".get_real_interface($this->GetInterface());
2794
		}
2795
		if ($this->GetBandwidth() && $this->GetBwscale()) {
2796
			$pfq_rule .= " bandwidth ".trim($this->GetBandwidth()).$this->GetBwscale();
2797
		}
2798
		$tmpvalue = $this->GetQpriority();
2799
		if (is_numeric($tmpvalue)) {
2800
			$pfq_rule .= " priority " . $this->GetQpriority();
2801
		}
2802
		$tmpvalue = trim($this->GetQlimit());
2803
		if (!empty($tmpvalue)) {
2804
			$pfq_rule .= " qlimit " . $this->GetQlimit();
2805
		}
2806
		if ($this->GetDefault() || $this->GetRed() || $this->GetRio() || $this->GetEcn() || $this->GetBorrow() || $this->GetCodel()) {
2807
			$pfq_rule .= " cbq ( ";
2808
			$tmpvalue = trim($this->GetRed());
2809
			if (!empty($tmpvalue)) {
2810
				$comma = 1;
2811
				$pfq_rule .= " red ";
2812
			}
2813
			$tmpvalue = trim($this->GetCodel());
2814
			if (!empty($tmpvalue)) {
2815
				$comma = 1;
2816
				$pfq_rule .= " codel ";
2817
			}
2818
			$tmpvalue = trim($this->GetRio());
2819
			if (!empty($tmpvalue)) {
2820
				if ($comma) {
2821
					$pfq_rule .= " ,";
2822
				}
2823
				$comma = 1;
2824
				$pfq_rule .= " rio ";
2825
			}
2826
			$tmpvalue = trim($this->GetEcn());
2827
			if (!empty($tmpvalue)) {
2828
				if ($comma) {
2829
					$pfq_rule .= " ,";
2830
				}
2831
				$comma = 1;
2832
				$pfq_rule .= " ecn ";
2833
			}
2834
			$tmpvalue = trim($this->GetDefault());
2835
			if (!empty($tmpvalue)) {
2836
				if ($comma) {
2837
					$pfq_rule .= " ,";
2838
				}
2839
				$comma = 1;
2840
				$pfq_rule .= " default ";
2841
				$default = true;
2842
			}
2843
			$tmpvalue = trim($this->GetBorrow());
2844
			if (!empty($tmpvalue)) {
2845
				if ($comma) {
2846
					$pfq_rule .= ", ";
2847
				}
2848
				$pfq_rule .= " borrow ";
2849
			}
2850
			$pfq_rule .= " ) ";
2851
		}
2852
		if (count($this->subqueues)) {
2853
			$i = count($this->subqueues);
2854
			$pfq_rule .= " { ";
2855
			foreach ($this->subqueues as $qkey => $qnone) {
2856
				if ($i > 1) {
2857
					$i--;
2858
					$pfq_rule .= " {$qkey}, ";
2859
				} else {
2860
					$pfq_rule .= " {$qkey} ";
2861
				}
2862
			}
2863
			$pfq_rule .= " } \n";
2864
			foreach ($this->subqueues as $q) {
2865
				$pfq_rule .= $q->build_rules($default);
2866
			}
2867
		}
2868

    
2869
		$pfq_rule .= " \n";
2870
		return $pfq_rule;
2871
	}
2872

    
2873
	function build_form() {
2874
		$sform = parent::build_form();
2875

    
2876
		$section = new Form_Section('NOTITLE');
2877

    
2878
		$group = new Form_Group('Bandwidth');
2879

    
2880
		$group->add(new Form_Input(
2881
			'bandwidth',
2882
			null,
2883
			'number',
2884
			$this->GetBandwidth()
2885
		));
2886

    
2887
		$group->add(new Form_Select(
2888
			'bandwidthtype',
2889
			null,
2890
			$this->GetBwscale(),
2891
			array('Kb' => 'Kbit/s',
2892
				  'Mb' => 'Mbit/s',
2893
				  'Gb' => 'Gbit/s',
2894
				  'b' => 'Bit/s',
2895
				  '%' => '%')
2896
		));
2897

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

    
2900
		$section->add($group);
2901

    
2902
		$section->addInput(new Form_Checkbox(
2903
			'borrow',
2904
			'Scheduler option',
2905
			'Borrow from other queues when available',
2906
			($this->GetBorrow() == "on")
2907
		));
2908

    
2909
		$sform->add($section);
2910

    
2911
		return $sform;
2912
	}
2913

    
2914
	function update_altq_queue_data(&$data) {
2915
		$this->ReadConfig($data);
2916
	}
2917

    
2918
	function wconfig() {
2919
		$cflink =& get_reference_to_me_in_config($this->GetLink());
2920
		if (!is_array($cflink)) {
2921
			$cflink = array();
2922
		}
2923
		$cflink['interface'] = $this->GetInterface();
2924
		$cflink['qlimit'] = trim($this->GetQlimit());
2925
		if (empty($cflink['qlimit'])) {
2926
			unset($cflink['qlimit']);
2927
		}
2928
		$cflink['priority'] = $this->GetQpriority();
2929
		if (!is_numeric($cflink['priority'])) {
2930
			unset($cflink['priority']);
2931
		}
2932
		$cflink['name'] = $this->GetQname();
2933
		$cflink['description'] = $this->GetDescription();
2934
		if (empty($cflink['description'])) {
2935
			unset($cflink['description']);
2936
		}
2937
		$cflink['bandwidth'] = $this->GetBandwidth();
2938
		$cflink['bandwidthtype'] = $this->GetBwscale();
2939
		$cflink['enabled'] = trim($this->GetEnabled());
2940
		if (empty($cflink['enabled'])) {
2941
			unset($cflink['enabled']);
2942
		}
2943
		$cflink['default'] = trim($this->GetDefault());
2944
		if (empty($cflink['default'])) {
2945
			unset($cflink['default']);
2946
		}
2947
		$cflink['red'] = trim($this->GetRed());
2948
		if (empty($cflink['red'])) {
2949
			unset($cflink['red']);
2950
		}
2951
		$cflink['rio'] = trim($this->GetRio());
2952
		if (empty($cflink['rio'])) {
2953
			unset($cflink['rio']);
2954
		}
2955
		$cflink['ecn'] = trim($this->GetEcn());
2956
		if (empty($cflink['ecn'])) {
2957
			unset($cflink['ecn']);
2958
		}
2959
		$cflink['codel'] = trim($this->GetCodel());
2960
		if (empty($cflink['codel'])) {
2961
			unset($cflink['codel']);
2962
		}
2963
		$cflink['borrow'] = trim($this->GetBorrow());
2964
		if (empty($cflink['borrow'])) {
2965
			unset($cflink['borrow']);
2966
		}
2967
	}
2968
}
2969

    
2970
class fairq_queue extends priq_queue {
2971
	var $hogs;
2972
	var $buckets;
2973

    
2974
	function GetBuckets() {
2975
		return $this->buckets;
2976
	}
2977
	function SetBuckets($buckets) {
2978
		$this->buckets = $buckets;
2979
	}
2980
	function GetHogs() {
2981
		return $this->hogs;
2982
	}
2983
	function SetHogs($hogs) {
2984
		$this->hogs = $hogs;
2985
	}
2986
	function CanHaveChildren() {
2987
		return false;
2988
	}
2989

    
2990

    
2991
	function copy_queue($interface, &$cflink) {
2992
		$cflink['interface'] = $interface;
2993
		$cflink['qlimit'] = $this->GetQlimit();
2994
		$cflink['priority'] = $this->GetQpriority();
2995
		$cflink['name'] = $this->GetQname();
2996
		$cflink['description'] = $this->GetDescription();
2997
		$cflink['bandwidth'] = $this->GetBandwidth();
2998
		$cflink['bandwidthtype'] = $this->GetBwscale();
2999
		$cflink['enabled'] = $this->GetEnabled();
3000
		$cflink['default'] = $this->GetDefault();
3001
		$cflink['red'] = $this->GetRed();
3002
		$cflink['rio'] = $this->GetRio();
3003
		$cflink['ecn'] = $this->GetEcn();
3004
		$cflink['buckets'] = $this->GetBuckets();
3005
		$cflink['hogs'] = $this->GetHogs();
3006
	}
3007

    
3008
	/*
3009
	 * Should search even its children
3010
	 */
3011
	function &find_queue($interface, $qname) {
3012
		if ($qname == $this->GetQname()) {
3013
			return $this;
3014
		}
3015
	}
3016

    
3017
	function find_parentqueue($interface, $qname) { return; }
3018

    
3019
	function delete_queue() {
3020
		unref_on_altq_queue_list($this->GetQname());
3021
		cleanup_queue_from_rules($this->GetQname());
3022
		unset_object_by_reference($this->GetLink());
3023
	}
3024

    
3025
	function validate_input($data, &$input_errors) {
3026
		parent::validate_input($data, $input_errors);
3027

    
3028
		if ($data['priority'] > 7) {
3029
				$input_errors[] = gettext("Priority must be an integer between 0 and 7.");
3030
		}
3031
		$reqdfields[] = "bandwidth";
3032
		$reqdfieldsn[] = gettext("Bandwidth");
3033
		$reqdfields[] = "bandwidthtype";
3034
		$reqdfieldsn[] = gettext("Bandwidthtype");
3035

    
3036
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
3037
	}
3038

    
3039
	function ReadConfig(&$q) {
3040
		parent::ReadConfig($q);
3041
		if (!empty($q['buckets'])) {
3042
			$this->SetBuckets($q['buckets']);
3043
		} else {
3044
			$this->SetBuckets("");
3045
		}
3046
		if (!empty($q['hogs']) && is_valid_shaperbw($q['hogs'])) {
3047
			$this->SetHogs($q['hogs']);
3048
		} else {
3049
			$this->SetHogs("");
3050
		}
3051
	}
3052

    
3053
	function build_javascript() {
3054
		return parent::build_javascript();
3055
	}
3056

    
3057
	function build_tree() {
3058
		$tree = " <li><a href=\"firewall_shaper.php?interface=" .
3059
		$this->GetInterface()."&amp;queue=" . $this->GetQname()."&amp;action=show";
3060
		$tree .= "\" ";
3061
		$tmpvalue = trim($this->GetDefault());
3062
		if (!empty($tmpvalue)) {
3063
			$tree .= " class=\"navlnk\"";
3064
		}
3065
		$tree .= " >" . $this->GetQname() . "</a>";
3066
		$tree .= "</li>";
3067
		return $tree;
3068
	}
3069

    
3070
	/* Even this should take children into consideration */
3071
	function build_rules(&$default = false) {
3072
		$pfq_rule = "queue ". $this->qname;
3073
		if ($this->GetInterface()) {
3074
			$pfq_rule .= " on ".get_real_interface($this->GetInterface());
3075
		}
3076
		if ($this->GetBandwidth() && $this->GetBwscale()) {
3077
			$pfq_rule .= " bandwidth ".trim($this->GetBandwidth()).$this->GetBwscale();
3078
		}
3079
		$tmpvalue = trim($this->GetQpriority());
3080
		if (is_numeric($tmpvalue)) {
3081
			$pfq_rule .= " priority " . $this->GetQpriority();
3082
		}
3083
		$tmpvalue = trim($this->GetQlimit());
3084
		if (!empty($tmpvalue)) {
3085
			$pfq_rule .= " qlimit " . $this->GetQlimit();
3086
		}
3087
		if ($this->GetDefault() || $this->GetRed() || $this->GetRio() ||
3088
		    $this->GetEcn() || $this->GetBuckets() || $this->GetHogs() || $this->GetCodel()) {
3089
			$pfq_rule .= " fairq ( ";
3090
			$tmpvalue = trim($this->GetRed());
3091
			if (!empty($tmpvalue)) {
3092
				$comma = 1;
3093
				$pfq_rule .= " red ";
3094
			}
3095
			$tmpvalue = trim($this->GetCodel());
3096
			if (!empty($tmpvalue)) {
3097
				$comma = 1;
3098
				$pfq_rule .= " codel ";
3099
			}
3100
			$tmpvalue = trim($this->GetRio());
3101
			if (!empty($tmpvalue)) {
3102
				if ($comma) {
3103
					$pfq_rule .= " ,";
3104
				}
3105
				$comma = 1;
3106
				$pfq_rule .= " rio ";
3107
			}
3108
			$tmpvalue = trim($this->GetEcn());
3109
			if (!empty($tmpvalue)) {
3110
				if ($comma) {
3111
					$pfq_rule .= " ,";
3112
				}
3113
				$comma = 1;
3114
				$pfq_rule .= " ecn ";
3115
			}
3116
			$tmpvalue = trim($this->GetDefault());
3117
			if (!empty($tmpvalue)) {
3118
				if ($comma) {
3119
					$pfq_rule .= " ,";
3120
				}
3121
				$comma = 1;
3122
				$pfq_rule .= " default ";
3123
				$default = true;
3124
			}
3125
			$tmpvalue = trim($this->GetBuckets());
3126
			if (!empty($tmpvalue)) {
3127
				if ($comma) {
3128
					$pfq_rule .= ", ";
3129
				}
3130
				$pfq_rule .= " buckets " . $this->GetBuckets() . " ";
3131
			}
3132
			$tmpvalue = trim($this->GetHogs());
3133
			if (!empty($tmpvalue)) {
3134
				if ($comma) {
3135
					$pfq_rule .= ", ";
3136
				}
3137
				$pfq_rule .= " hogs " . $this->GetHogs() . " ";
3138
			}
3139
			$pfq_rule .= " ) ";
3140
		}
3141

    
3142
		$pfq_rule .= " \n";
3143
		return $pfq_rule;
3144
	}
3145

    
3146
	function build_form() {
3147
		$form = parent::build_form();
3148

    
3149
		$section = new Form_Section('');
3150

    
3151
		$group = new Form_Group('Bandwidth');
3152

    
3153
		$group->add(new Form_Input(
3154
			'bandwidth',
3155
			null,
3156
			'number',
3157
			$this->GetBandwidth()
3158
		));
3159

    
3160
		$group->add(new Form_Select(
3161
			'bandwidthtype',
3162
			null,
3163
			$this->GetBwscale(),
3164
			array('Kb' => 'Kbit/s',
3165
				  'Mb' => 'Mbit/s',
3166
				  'Gb' => 'Gbit/s',
3167
				  'b' => 'Bit/s',
3168
				  '%' => '%')
3169
		));
3170

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

    
3173
		$section->add($group);
3174

    
3175
		$section->addInput(new Form_Input(
3176
			'buckets',
3177
			'Scheduler specific options',
3178
			'text',
3179
			$this->GetBuckets()
3180
		))->setHelp('Number of buckets available');
3181

    
3182
		$section->addInput(new Form_Input(
3183
			'hogs',
3184
			'',
3185
			'text',
3186
			$this->GetHogs()
3187
			))->setHelp('Bandwidth limit for hosts to not saturate link');
3188

    
3189
		$form->add($section);
3190
		return $form;
3191
	}
3192

    
3193
	function update_altq_queue_data(&$data) {
3194
		$this->ReadConfig($data);
3195
	}
3196

    
3197
	function wconfig() {
3198
		$cflink =& get_reference_to_me_in_config($this->GetLink());
3199
		if (!is_array($cflink)) {
3200
			$cflink = array();
3201
		}
3202
		$cflink['interface'] = $this->GetInterface();
3203
		$cflink['qlimit'] = trim($this->GetQlimit());
3204
		if (empty($cflink['qlimit'])) {
3205
			unset($cflink['qlimit']);
3206
		}
3207
		$cflink['priority'] = trim($this->GetQpriority());
3208
		if (!is_numeric($cflink['priority'])) {
3209
			unset($cflink['priority']);
3210
		}
3211
		$cflink['name'] = $this->GetQname();
3212
		$cflink['description'] = trim($this->GetDescription());
3213
		if (empty($cflink['description'])) {
3214
			unset($cflink['description']);
3215
		}
3216
		$cflink['bandwidth'] = $this->GetBandwidth();
3217
		$cflink['bandwidthtype'] = $this->GetBwscale();
3218
		$cflink['enabled'] = $this->GetEnabled();
3219
		if (empty($cflink['enabled'])) {
3220
			unset($cflink['enabled']);
3221
		}
3222
		$cflink['default'] = trim($this->GetDefault());
3223
		if (empty($cflink['default'])) {
3224
			unset($cflink['default']);
3225
		}
3226
		$cflink['red'] = trim($this->GetRed());
3227
		if (empty($cflink['red'])) {
3228
			unset($cflink['red']);
3229
		}
3230
		$cflink['rio'] = trim($this->GetRio());
3231
		if (empty($cflink['rio'])) {
3232
			unset($cflink['rio']);
3233
		}
3234
		$cflink['ecn'] = trim($this->GetEcn());
3235
		if (empty($cflink['ecn'])) {
3236
			unset($cflink['ecn']);
3237
		}
3238
		$cflink['codel'] = trim($this->GetCodel());
3239
		if (empty($cflink['codel'])) {
3240
			unset($cflink['codel']);
3241
		}
3242
		$cflink['buckets'] = trim($this->GetBuckets());
3243
		if (empty($cflink['buckets'])) {
3244
			unset($cflink['buckets']);
3245
		}
3246
		$cflink['hogs'] = trim($this->GetHogs());
3247
		if (empty($cflink['hogs'])) {
3248
			unset($cflink['hogs']);
3249
		}
3250
	}
3251
}
3252

    
3253

    
3254
/*
3255
 * dummynet(4) wrappers.
3256
 */
3257

    
3258

    
3259
/*
3260
 * List of respective objects!
3261
 */
3262
$dummynet_pipe_list = array();
3263

    
3264
class dummynet_class {
3265
	var $qname;
3266
	var $qnumber; /* dummynet(4) uses numbers instead of names; maybe integrate with pf the same as altq does?! */
3267
	var $qlimit;
3268
	var $description;
3269
	var $qenabled;
3270
	var $link;
3271
	var $qparent; /* link to upper class so we do things easily on WF2Q+ rule creation */
3272
	var $plr;
3273

    
3274
	var $buckets;
3275
	/* mask parameters */
3276
	var $mask;
3277
	var $noerror;
3278

    
3279
	/* Accessor functions */
3280
	function SetLink($link) {
3281
		$this->link = $link;
3282
	}
3283
	function GetLink() {
3284
		return $this->link;
3285
	}
3286
	function GetMask() {
3287
		if (!isset($this->mask["type"])) {
3288
			$this->mask["type"] = "none";
3289
		}
3290
		return $this->mask;
3291
	}
3292
	function SetMask($mask) {
3293
		$this->mask = $mask;
3294
	}
3295
	function &GetParent() {
3296
		return $this->qparent;
3297
	}
3298
	function SetParent(&$parent) {
3299
		$this->qparent = &$parent;
3300
	}
3301
	function GetEnabled() {
3302
		return $this->qenabled;
3303
	}
3304
	function SetEnabled($value) {
3305
		$this->qenabled = $value;
3306
	}
3307
	function CanHaveChildren() {
3308
		return false;
3309
	}
3310
	function CanBeDeleted() {
3311
		return true;
3312
	}
3313
	function GetQname() {
3314
		return $this->qname;
3315
	}
3316
	function SetQname($name) {
3317
		$this->qname = trim($name);
3318
	}
3319
	function GetQlimit() {
3320
		return $this->qlimit;
3321
	}
3322
	function SetQlimit($limit) {
3323
		$this->qlimit = $limit;
3324
	}
3325
	function GetDescription() {
3326
		return $this->description;
3327
	}
3328
	function SetDescription($str) {
3329
		$this->description = trim($str);
3330
	}
3331
	function GetFirstime() {
3332
		return $this->firsttime;
3333
	}
3334
	function SetFirsttime($number) {
3335
		$this->firsttime = $number;
3336
	}
3337
	function GetBuckets() {
3338
		return $this->buckets;
3339
	}
3340
	function SetBuckets($buckets) {
3341
		$this->buckets = $buckets;
3342
	}
3343
	function SetNumber($number) {
3344
		$this->qnumber = $number;
3345
	}
3346
	function GetNumber() {
3347
		return $this->qnumber;
3348
	}
3349
	function GetPlr() {
3350
		return $this->plr;
3351
	}
3352
	function SetPlr($plr) {
3353
		$this->plr = $plr;
3354
	}
3355

    
3356
	function build_javascript() {
3357
		$javascript .= "<script type=\"text/javascript\">\n";
3358
		$javascript .= "//<![CDATA[\n";
3359
		$javascript .= "function enable_maskbits(enable_over) {\n";
3360
		$javascript .= "var e = document.getElementById(\"mask\");\n";
3361
		$javascript .= "if ((e.options[e.selectedIndex].text == \"none\") || enable_over) {\n";
3362
		$javascript .= "document.iform.maskbits.disabled = 1;\n";
3363
		$javascript .= "document.iform.maskbits.value = \"\";\n";
3364
		$javascript .= "document.iform.maskbitsv6.disabled = 1;\n";
3365
		$javascript .= "document.iform.maskbitsv6.value = \"\";\n";
3366
		$javascript .= "} else {\n";
3367
		$javascript .= "document.iform.maskbits.disabled = 0;\n";
3368
		$javascript .= "document.iform.maskbitsv6.disabled = 0;\n";
3369
		$javascript .= "}}\n";
3370
		$javascript .= "//]]>\n";
3371
		$javascript .= "</script>\n";
3372
		return $javascript;
3373
	}
3374

    
3375
	function validate_input($data, &$input_errors) {
3376
		$reqdfields[] = "bandwidth";
3377
		$reqdfieldsn[] = gettext("Bandwidth");
3378
		/*$reqdfields[] = "burst";
3379
		$reqdfieldsn[] = gettext("Burst"); */
3380
		$reqdfields[] = "bandwidthtype";
3381
		$reqdfieldsn[] = gettext("Bandwidthtype");
3382
		$reqdfields[] = "newname";
3383
		$reqdfieldsn[] = gettext("Name");
3384

    
3385
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
3386

    
3387
		if ($data['plr'] && (!is_numeric($data['plr']) ||
3388
		    ($data['plr'] < 0) || ($data['plr'] > 1))) {
3389
			$input_errors[] = gettext("Packet Loss Rate must be a value between 0 and 1.");
3390
		}
3391
		if ($data['buckets'] && (!is_numeric($data['buckets']) ||
3392
		    ($data['buckets'] < 16) || ($data['buckets'] > 65535))) {
3393
			$input_errors[] = gettext("Buckets must be an integer between 16 and 65535.");
3394
		}
3395
		if ($data['qlimit'] && (!is_numeric($data['qlimit']))) {
3396
			$input_errors[] = gettext("Queue limit must be an integer");
3397
		}
3398
		if (!empty($data['newname']) && !preg_match("/^[a-zA-Z0-9_-]+$/", $data['newname'])) {
3399
			$input_errors[] = gettext("Queue names must be alphanumeric and _ or - only.");
3400
		}
3401
		if (!empty($data['name']) && !preg_match("/^[a-zA-Z0-9_-]+$/", $data['name'])) {
3402
			$input_errors[] = gettext("Queue names must be alphanumeric and _ or - only.");
3403
		}
3404
		if (isset($data['maskbits']) && ($data['maskbits'] <> "")) {
3405
			if ((!is_numeric($data['maskbits'])) || ($data['maskbits'] <= 0) || ($data['maskbits'] > 32)) {
3406
				$input_errors[] = gettext("IPv4 bit mask must be blank or numeric value between 1 and 32.");
3407
			}
3408
		}
3409
		if (isset($data['maskbitsv6']) && ($data['maskbitsv6'] <> "")) {
3410
			if ((!is_numeric($data['maskbitsv6'])) || ($data['maskbitsv6'] <= 0) || ($data['maskbitsv6'] > 128)) {
3411
				$input_errors[] = gettext("IPv6 bit mask must be blank or numeric value between 1 and 128.");
3412
			}
3413
		}
3414
	}
3415

    
3416
	function build_mask_rules(&$pfq_rule) {
3417
		$mask = $this->GetMask();
3418
		if (!empty($mask['type'])) {
3419
			if ($mask['type'] <> 'none') {
3420
				$pfq_rule .= " mask";
3421
			}
3422
			switch ($mask['type']) {
3423
				case 'srcaddress':
3424
					if (!empty($mask['bitsv6']) && ($mask['bitsv6'] <> "")) {
3425
						$pfq_rule .= " src-ip6 /" . $mask['bitsv6'];
3426
					} else {
3427
						$pfq_rule .= " src-ip6 /128";
3428
					}
3429
					if (!empty($mask['bits']) && ($mask['bits'] <> "")) {
3430
						$pfq_rule .= sprintf(" src-ip 0x%x", gen_subnet_mask_long($mask['bits']));
3431
					} else {
3432
						$pfq_rule .= " src-ip 0xffffffff";
3433
					}
3434
					break;
3435
				case 'dstaddress':
3436
					if (!empty($mask['bitsv6']) && ($mask['bitsv6'] <> "")) {
3437
						$pfq_rule .= " dst-ip6 /" . $mask['bitsv6'];
3438
					} else {
3439
						$pfq_rule .= " dst-ip6 /128";
3440
					}
3441
					if (!empty($mask['bits']) && ($mask['bits'] <> "")) {
3442
						$pfq_rule .= sprintf(" dst-ip 0x%x", gen_subnet_mask_long($mask['bits']));
3443
					} else {
3444
						$pfq_rule .= " dst-ip 0xffffffff";
3445
					}
3446
					break;
3447
				default:
3448
					break;
3449
			}
3450
		}
3451
	}
3452

    
3453
}
3454

    
3455
class dnpipe_class extends dummynet_class {
3456
	var $delay;
3457
	var $qbandwidth = array();
3458
	var $qbandwidthtype;
3459

    
3460
		/* This is here to help on form building and building rules/lists */
3461
	var $subqueues = array();
3462

    
3463
	function CanHaveChildren() {
3464
		return true;
3465
	}
3466
	function SetDelay($delay) {
3467
		$this->delay = $delay;
3468
	}
3469
	function GetDelay() {
3470
		return $this->delay;
3471
	}
3472
	function delete_queue() {
3473
		cleanup_dnqueue_from_rules($this->GetQname());
3474
		foreach ($this->subqueues as $q) {
3475
			$q->delete_queue();
3476
		}
3477
		unset_dn_object_by_reference($this->GetLink());
3478
		@pfSense_ipfw_pipe("pipe delete " . $this->GetNumber());
3479
	}
3480
	function GetBandwidth() {
3481
		return $this->qbandwidth;
3482
	}
3483
	function SetBandwidth($bandwidth) {
3484
		$this->qbandwidth = $bandwidth;
3485
	}
3486
	function GetBurst() {
3487
		return $this->qburst;
3488
	}
3489
	function SetBurst($burst) {
3490
		$this->qburst = $burst;
3491
	}
3492

    
3493
	function &add_queue($interface, &$queue, &$path, &$input_errors) {
3494

    
3495
		if (!is_array($this->subqueues)) {
3496
			$this->subqueues = array();
3497
		}
3498

    
3499
		$__tmp_q = new dnqueue_class(); $q =& $__tmp_q;
3500
		$q->SetLink($path);
3501
		$q->SetEnabled("on");
3502
		$q->SetPipe($this->GetQname());
3503
		$q->SetParent($this);
3504
		$q->ReadConfig($queue);
3505
		$q->validate_input($queue, $input_errors);
3506

    
3507
		if (!is_array($input_errors)) {
3508
			$input_errors = array();
3509
		}
3510

    
3511
		if (count($input_errors)) {
3512
			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)));
3513
			return $q;
3514
		}
3515
		$number = dnqueue_find_nextnumber();
3516
		$q->SetNumber($number);
3517
		$this->subqueues[$q->GetQname()] = &$q;
3518

    
3519
		return $q;
3520
	}
3521

    
3522
	function &get_queue_list(&$q = null) {
3523
		$qlist = array();
3524

    
3525
		$qlist[$this->GetQname()] = $this->GetNumber();
3526
		if (is_array($this->subqueues)) {
3527
			foreach ($this->subqueues as $queue) {
3528
				$queue->get_queue_list($qlist);
3529
			}
3530
		}
3531
		return $qlist;
3532
	}
3533

    
3534
	/*
3535
	 * Should search even its children
3536
	 */
3537
	function &find_queue($pipe, $qname) {
3538
		if ($qname == $this->GetQname()) {
3539
			return $this;
3540
		}
3541
		foreach ($this->subqueues as $q) {
3542
			$result =& $q->find_queue("", $qname);
3543
			if ($result) {
3544
				return $result;
3545
			}
3546
		}
3547
	}
3548

    
3549
	function &find_parentqueue($pipe, $qname) {
3550
		return NULL;
3551
	}
3552

    
3553
	function validate_input($data, &$input_errors) {
3554
		parent::validate_input($data, $input_errors);
3555

    
3556
		$schedule = 0;
3557
		$schedulenone = 0;
3558
		$entries = 0;
3559
		/* XXX: Really no better way? */
3560
		for ($i = 0; $i < 2900; $i++) {
3561
			if (!empty($data["bwsched{$i}"])) {
3562
				if ($data["bwsched{$i}"] != "none") {
3563
					$schedule++;
3564
				} else {
3565
					$schedulenone++;
3566
				}
3567
			}
3568
			if (!empty($data["bandwidth{$i}"])) {
3569
				if (!is_numeric($data["bandwidth{$i}"])) {
3570
					$input_errors[] = sprintf(gettext("Bandwidth for schedule %s must be an integer."), $data["bwsched{$i}"]);
3571
				} else if (($data["burst{$i}"] != "") && (!is_numeric($data["burst{$i}"]))) {
3572
					$input_errors[] = sprintf(gettext("Burst for schedule %s must be an integer."), $data["bwsched{$i}"]);
3573
				} else {
3574
					$entries++;
3575
				}
3576
			}
3577
		}
3578
		if ($schedule == 0 && $entries > 1) {
3579
			$input_errors[] = gettext("A schedule needs to be specified for every additional entry.");
3580
		}
3581
		if ($schedulenone > 0 && $entries > 1) {
3582
			$input_errors[] = gettext("If more than one bandwidth configured all schedules need to be selected.");
3583
		}
3584
		if ($entries == 0) {
3585
			$input_errors[] = gettext("At least one bw specification is necessary.");
3586
		}
3587
		if ($data['delay'] && (!is_numeric($data['delay']))) {
3588
			$input_errors[] = gettext("Delay must be an integer.");
3589
		}
3590
	}
3591

    
3592
	function ReadConfig(&$q) {
3593
		if (!empty($q['name']) && !empty($q['newname']) && $q['name'] != $q['newname']) {
3594
			$this->SetQname($q['newname']);
3595
		} else if (!empty($q['newname'])) {
3596
			$this->SetQname($q['newname']);
3597
		} else {
3598
			$this->SetQname($q['name']);
3599
		}
3600
		$this->SetNumber($q['number']);
3601

    
3602
		if (!empty($_POST)) {
3603
			$bandwidth = array();
3604
			/* XXX: Really no better way? */
3605
			for ($i = 0; $i < 2900; $i++) {
3606
				if (isset($q["bandwidth{$i}"]) && $q["bandwidth{$i}"] <> "") {
3607
					$bw = array();
3608
					$bw['bw'] = $q["bandwidth{$i}"];
3609
					$bw['burst'] = $q["burst{$i}"];
3610
					if (isset($q["bwtype{$i}"]) && $q["bwtype{$i}"]) {
3611
						$bw['bwscale'] = $q["bwtype{$i}"];
3612
					}
3613
					if (isset($q["bwsched{$i}"]) && $q["bwsched{$i}"]) {
3614
						$bw['bwsched'] = $q["bwsched{$i}"];
3615
					}
3616
					$bandwidth[] = $bw;
3617
				}
3618
			}
3619
			$this->SetBandwidth($bandwidth);
3620
		}
3621

    
3622
		if (is_array($q['bandwidth']) && is_array($q['bandwidth']['item'])) {
3623
			$this->SetBandwidth($q['bandwidth']['item']);
3624
			$this->SetBurst($q['burst']['item']);
3625
		}
3626

    
3627
		if (isset($q['qlimit']) && $q['qlimit'] <> "") {
3628
			$this->SetQlimit($q['qlimit']);
3629
		} else {
3630
			$this->SetQlimit("");
3631
		}
3632
		if (isset($q['mask']) && $q['mask'] <> "") {
3633
			$masktype = $q['mask'];
3634
		} else {
3635
			$masktype = "";
3636
		}
3637
		if (isset($q['maskbits']) && $q['maskbits'] <> "") {
3638
			$maskbits = $q['maskbits'];
3639
		} else {
3640
			$maskbits = "";
3641
		}
3642
		if (isset($q['maskbitsv6']) && $q['maskbitsv6'] <> "") {
3643
			$maskbitsv6 = $q['maskbitsv6'];
3644
		} else {
3645
			$maskbitsv6 = "";
3646
		}
3647
		$this->SetMask(array("type" => $masktype, "bits" => $maskbits, "bitsv6" => $maskbitsv6));
3648
		if (isset($q['buckets']) && $q['buckets'] <> "") {
3649
			$this->SetBuckets($q['buckets']);
3650
		} else {
3651
			$this->SetBuckets("");
3652
		}
3653
		if (isset($q['plr']) && $q['plr'] <> "") {
3654
			$this->SetPlr($q['plr']);
3655
		} else {
3656
			$this->SetPlr("");
3657
		}
3658
		if (isset($q['delay']) && $q['delay'] <> "") {
3659
			$this->SetDelay($q['delay']);
3660
		} else {
3661
			$this->SetDelay(0);
3662
		}
3663
		if (isset($q['description']) && $q['description'] <> "") {
3664
			$this->SetDescription($q['description']);
3665
		} else {
3666
			$this->SetDescription("");
3667
		}
3668
		$this->SetEnabled($q['enabled']);
3669

    
3670
	}
3671

    
3672
	function build_tree() {
3673
		$tree = " <li><a href=\"firewall_shaper_vinterface.php?pipe=" . $this->GetQname() ."&amp;queue=".$this->GetQname() ."&amp;action=show\">";
3674
		$tree .= $this->GetQname() . "</a>";
3675
		if (is_array($this->subqueues)) {
3676
			$tree .= "<ul>";
3677
			foreach ($this->subqueues as $q) {
3678
				$tree .= $q->build_tree();
3679
			}
3680
			$tree .= "</ul>";
3681
		}
3682
		$tree .= "</li>";
3683

    
3684
		return $tree;
3685
	}
3686

    
3687
	function build_rules() {
3688
		global $config, $time_based_rules;
3689

    
3690
		if ($this->GetEnabled() == "") {
3691
			return;
3692
		}
3693

    
3694
		$pfq_rule = "\npipe ". $this->GetNumber() . " config ";
3695
		$found = false;
3696
		$bandwidth = $this->GetBandwidth();
3697
		if (is_array($bandwidth)) {
3698
			foreach ($bandwidth as $bw) {
3699
				if ($bw['bwsched'] != "none") {
3700
					$time_based_rules = true;
3701
					if (is_array($config['schedules']) && is_array($config['schedules']['schedule'])) {
3702
						foreach ($config['schedules']['schedule'] as $schedule) {
3703
							if ($bw['bwsched'] == $schedule['name']) {
3704
								if (filter_get_time_based_rule_status($schedule)) {
3705
									/* pipe throughputs must always be an integer, enforce that restriction again here. */
3706
									$pfq_rule .= " bw ".round(trim($bw['bw']),0).$bw['bwscale'];
3707
									if (is_numeric($bw['burst']) && ($bw['burst'] > 0)) {
3708
										$pfq_rule .= " burst ".trim($bw['burst']);
3709
									}
3710
									$found = true;
3711
									break;
3712
								}
3713
							}
3714
						}
3715
					} else {
3716
						$pfq_rule .= " bw 0";
3717
						$found = true;
3718
						break;
3719
					}
3720
				} else {
3721
					/* pipe throughputs must always be an integer, enforce that restriction again here. */
3722
					$pfq_rule .= " bw ".round(trim($bw['bw']), 0).$bw['bwscale'];
3723
					if (is_numeric($bw['burst']) && ($bw['burst'] > 0)) {
3724
						$pfq_rule .= " burst ".trim($bw['burst']);
3725
					}
3726
					$found = true;
3727
					break;
3728
				}
3729
			}
3730
			if ($found == false) {
3731
				$pfq_rule .= " bw 0";
3732
			}
3733
		} else {
3734
			$pfq_rule .= " bw 0";
3735
		}
3736

    
3737
		if ($this->GetQlimit()) {
3738
			$pfq_rule .= " queue " . $this->GetQlimit();
3739
		}
3740
		if ($this->GetPlr()) {
3741
			$pfq_rule .= " plr " . $this->GetPlr();
3742
		}
3743
		if ($this->GetBuckets()) {
3744
			$pfq_rule .= " buckets " . $this->GetBuckets();
3745
		}
3746
		if ($this->GetDelay()) {
3747
			$pfq_rule .= " delay " . $this->GetDelay();
3748
		}
3749
		$this->build_mask_rules($pfq_rule);
3750

    
3751
		$pfq_rule .= "\n";
3752

    
3753
		if (!empty($this->subqueues) && count($this->subqueues) > 0) {
3754
			foreach ($this->subqueues as $q) {
3755
				$pfq_rule .= $q->build_rules();
3756
			}
3757
		}
3758
		$pfq_rule .= " \n";
3759

    
3760
		return $pfq_rule;
3761
	}
3762

    
3763
	function update_dn_data(&$data) {
3764
		$this->ReadConfig($data);
3765
	}
3766

    
3767
	function build_javascript() {
3768
		global $g, $config;
3769

    
3770
		$javasr = parent::build_javascript();
3771

    
3772
		//build list of schedules
3773
		$schedules = "<option value='none'>none</option>";
3774
		if (is_array($config['schedules']) && is_array($config['schedules']['schedule'])) {
3775
			foreach ($config['schedules']['schedule'] as $schedule) {
3776
				if ($schedule['name'] <> "") {
3777
					$schedules .= "<option value='{$schedule['name']}'>{$schedule['name']}</option>";
3778
				}
3779
			}
3780
		}
3781
		$bwopt = "";
3782
		foreach (array("Kb" => "Kbit/s", "Mb" => "Mbit/s", "Gb" => "Gbit/s", "b" => "Bit/s") as $bwidx => $bw) {
3783
			$bwopt .= "<option value='{$bwidx}'>{$bw}</option>";
3784
		}
3785

    
3786
		$javasr .= <<<EOD
3787
<script type='text/javascript'>
3788
//<![CDATA[
3789
var addBwRowTo = (function() {
3790

    
3791
	return (function (tableId) {
3792

    
3793
	var table = document.getElementById(tableId);
3794
	var totalrows = table.rows.length -1;
3795

    
3796
	var row = table.insertRow(totalrows + 1);
3797
	var cell1 = row.insertCell(0);
3798
	var cell2 = row.insertCell(1);
3799
	var cell3 = row.insertCell(2);
3800
	var cell4 = row.insertCell(3);
3801

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

    
3807
	});
3808
})();
3809

    
3810
function removeBwRow(el) {
3811
	var d = el.parentNode.parentNode.rowIndex;
3812
	document.getElementById('maintable').deleteRow(d);
3813
}
3814

    
3815
function ceil_func(el){
3816
	el.value = Math.ceil(el.value);
3817

    
3818
}
3819
//]]>
3820
</script>
3821

    
3822
EOD;
3823

    
3824
		return $javasr;
3825
	}
3826

    
3827
	// Compose a table of bandwidths that can then be inserted into the form using a Form_StaticText
3828
	// The table has been "Bootstrapped" to match the web design while maintaining compatibility with
3829
	// with the javascript in this class
3830
	function build_bwtable() {
3831
		global $config;
3832

    
3833
		$bandwidth = $this->GetBandwidth();
3834
				//build list of schedules
3835
		$schedules = array();
3836
		$schedules[] = "none";//leave none to leave rule enabled all the time
3837
		if (is_array($config['schedules']) && is_array($config['schedules']['schedule'])) {
3838
			foreach ($config['schedules']['schedule'] as $schedule) {
3839
				if ($schedule['name'] != "") {
3840
					$schedules[] = $schedule['name'];
3841
				}
3842
			}
3843
		}
3844

    
3845
		$form = '<div class="table-responsive">';
3846
		$form .= '<table id="maintable" class="table table-hover table-striped">';
3847
		$form .= "<thead><tr>";
3848
		$form .= "<th>Bandwidth</th>";
3849
		//$form .= "<td width='35%'><div id='fifthcolumn'>Burst</div></td>";
3850
		$form .= "<th>Bw type</th>";
3851
		$form .= "<th>Schedule</th>";
3852
		$form .= "<th></th>";
3853
		$form .= "</tr></thead>";
3854
		$form .= "<tbody>";
3855

    
3856
		// If there are no bandwidths defined, make a blank one for convenience
3857
		if (empty($bandwidth)) {
3858
			$bandwidth = array(0 => array('bw' => '', 'bwscale' => 'Kb', 'bwsched' => 'none'));
3859
		}
3860

    
3861
		if (is_array($bandwidth)) {
3862
			foreach ($bandwidth as $bwidx => $bw) {
3863
				$form .= '<tr>';
3864
				$form .= '<td class="col-xs-4">';
3865
				$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\"/>";
3866
				//$form .= "</td><td width='20%'>";
3867
				//$form .= "<input class='formfld unknown' size='10' type=\"text\" id=\"burst{$bwidx}\" name=\"burst{$bwidx}\" value=\"{$bw['burst']}\" />";
3868
				$form .= "</td>";
3869
				$form .= '<td class="col-xs-4">';
3870
				$form .= "<select id=\"bwtype{$bwidx}\" name=\"bwtype{$bwidx}\" class=\"form-control\">";
3871

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

    
3875
					if ($bw['bwscale'] == $bwsidx) {
3876
						$form .= " selected";
3877
					}
3878

    
3879
					$form .= ">{$bwscale}</option>";
3880
				}
3881

    
3882
				$form .= "</select>";
3883
				$form .= "</td>";
3884
				$form .= '<td class="col-xs-4">';
3885
				$form .= "<select id=\"bwsched{$bwidx}\" name=\"bwsched{$bwidx}\" class=\"form-control\">";
3886

    
3887
				foreach ($schedules as $schd) {
3888
					$selected = "";
3889
					if ($bw['bwsched'] == $schd) {
3890
						$selected = "selected";
3891
					}
3892

    
3893
					$form .= "<option value='{$schd}' {$selected}>{$schd}</option>";
3894
				}
3895

    
3896
				$form .= "</select>";
3897
				$form .= "</td>";
3898
				$form .= '<td>';
3899
				$form .= '<a class="btn btn-warning" onclick="removeBwRow(this); return false;"><i class="fa fa-trash icon-embed-btn"></i>' . gettext('Delete') . '</a>';
3900
				$form .= "</td></tr>";
3901
			}
3902
		}
3903
		$form .= "</tbody></table></div><br />";
3904

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

    
3909
		return($form);
3910
	}
3911

    
3912
	function build_form() {
3913
		global $g, $config, $pipe, $action, $qname;
3914

    
3915
		//build list of schedules
3916
		$schedules = array();
3917
		$schedules[] = "none";//leave none to leave rule enabled all the time
3918
		if (is_array($config['schedules']) && is_array($config['schedules']['schedule'])) {
3919
			foreach ($config['schedules']['schedule'] as $schedule) {
3920
				if ($schedule['name'] <> "") {
3921
					$schedules[] = $schedule['name'];
3922
				}
3923
			}
3924
		}
3925

    
3926

    
3927
		$sform = new Form();
3928
		$sform->setAction("firewall_shaper.php");
3929

    
3930
		$section = new Form_Section('Limiters');
3931

    
3932
		$section->addInput(new Form_Checkbox(
3933
			'enabled',
3934
			'Enable',
3935
			'Enable limiter and its children',
3936
			($this->GetEnabled() == "on"),
3937
			'on'
3938
		));
3939

    
3940
		$section->addInput(new Form_Input(
3941
			'newname',
3942
			'*Name',
3943
			'text',
3944
			$this->GetQname()
3945
		));
3946

    
3947
		$section->addInput(new Form_Input(
3948
			'name',
3949
			null,
3950
			'hidden',
3951
			$this->GetQname()
3952
		));
3953

    
3954
		if ($this->GetNumber() > 0) {
3955
			$section->addInput(new Form_Input(
3956
				'number',
3957
				null,
3958
				'hidden',
3959
				$this->GetNumber()
3960
			));
3961
		}
3962

    
3963
		$bandwidth = $this->GetBandwidth();
3964

    
3965
		if (is_array($bandwidth)) {
3966
				$section->addInput(new Form_StaticText(
3967
				'Bandwidth',
3968
				$this->build_bwtable()
3969
			));
3970
		}
3971

    
3972
		$mask = $this->GetMask();
3973

    
3974
		$section->addInput(new Form_Select(
3975
			'mask',
3976
			'Mask',
3977
			$mask['type'],
3978
			array('none' => gettext('None'), 'srcaddress' => gettext('Source addresses'), 'dstaddress' => gettext('Destination addresses'))
3979
		))->setHelp('If "source" or "destination" slots is chosen a dynamic pipe with the bandwidth, delay, packet loss ' .
3980
					'and queue size given above will be created for each source/destination IP address encountered, respectively. ' .
3981
					'This makes it possible to easily specify bandwidth limits per host.');
3982

    
3983
		$group = new Form_Group(null);
3984

    
3985
		$group->add(new Form_Select(
3986
			'maskbits',
3987
			null,
3988
			$mask['bits'],
3989
			array_combine(range(32, 1, -1), range(32, 1, -1))
3990
		))->setHelp('IPv4 mask bits%1$s%2$s', '<br />', '255.255.255.255/?');
3991

    
3992
		$group->add(new Form_Select(
3993
			'maskbitsv6',
3994
			null,
3995
			$mask['bitsv6'],
3996
			array_combine(range(128, 1, -1), range(128, 1, -1))
3997
		))->setHelp('IPv6 mask bits%1$s%2$s', '<br />', '<span style="font-family:consolas">ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/?</span>');
3998

    
3999
		$section->add($group);
4000

    
4001
		$section->addInput(new Form_Input(
4002
			'description',
4003
			'Description',
4004
			'text',
4005
			$this->GetDescription()
4006
		))->setHelp('A description may be entered here for administrative reference (not parsed).');
4007

    
4008
		$sform->add($section);
4009

    
4010
		$section = new Form_Section('Advanced Options');
4011

    
4012
		$section->addInput(new Form_Input(
4013
			'delay',
4014
			'Delay (ms)',
4015
			'text',
4016
			$this->GetDelay() > 0 ? $this->GetDelay():null
4017
		))->setHelp('In most cases, zero (0) should specified here (or leave the field empty).');
4018

    
4019
		$section->addInput(new Form_Input(
4020
			'plr',
4021
			'Packet Loss Rate',
4022
			'number',
4023
			$this->GetPlr(),
4024
			['step' => '0.001', 'min' => '0.000']
4025
		))->setHelp('In most cases, zero (0) should be specified here (or leave the field empty). ' .
4026
					'A value of 0.001 means one packet in 1000 gets dropped.');
4027

    
4028
		$section->addInput(new Form_Input(
4029
			'qlimit',
4030
			'Queue size (slots)',
4031
			'number',
4032
			$this->GetQlimit()
4033
		))->setHelp('In most cases, the field should be left empty. All packets in this pipe are placed into a fixed-size queue first, ' .
4034
					'then they are delayed by value specified in the Delay field, and then they are delivered to their destination.');
4035

    
4036
		$section->addInput(new Form_Input(
4037
			'buckets',
4038
			'Bucket size (slots)',
4039
			'number',
4040
			$this->GetBuckets()
4041
		))->setHelp('In most cases, this field should be left empty. It increases the hash size set.');
4042

    
4043
		$sform->add($section);
4044

    
4045
		return($sform);
4046
		}
4047

    
4048
	function wconfig() {
4049
		$cflink =& get_dn_reference_to_me_in_config($this->GetLink());
4050
		if (!is_array($cflink)) {
4051
			$cflink = array();
4052
		}
4053
		$cflink['name'] = $this->GetQname();
4054
		$cflink['number'] = $this->GetNumber();
4055
		$cflink['qlimit'] = $this->GetQlimit();
4056
		$cflink['plr'] = $this->GetPlr();
4057
		$cflink['description'] = $this->GetDescription();
4058

    
4059
		$bandwidth = $this->GetBandwidth();
4060
		if (is_array($bandwidth)) {
4061
			$cflink['bandwidth'] = array();
4062
			$cflink['bandwidth']['item'] = array();
4063
			foreach ($bandwidth as $bwidx => $bw) {
4064
				$cflink['bandwidth']['item'][] = $bw;
4065
			}
4066
		}
4067

    
4068
		$cflink['enabled'] = $this->GetEnabled();
4069
		$cflink['buckets'] = $this->GetBuckets();
4070
		$mask = $this->GetMask();
4071
		$cflink['mask'] = $mask['type'];
4072
		$cflink['maskbits'] = $mask['bits'];
4073
		$cflink['maskbitsv6'] = $mask['bitsv6'];
4074
		$cflink['delay'] = $this->GetDelay();
4075
	}
4076

    
4077
}
4078

    
4079
class dnqueue_class extends dummynet_class {
4080
	var $pipeparent;
4081
	var $weight;
4082

    
4083
	function GetWeight() {
4084
		return $this->weight;
4085
	}
4086
	function SetWeight($weight) {
4087
		$this->weight = $weight;
4088
	}
4089
	function GetPipe() {
4090
		return $this->pipeparent;
4091
	}
4092
	function SetPipe($pipe) {
4093
		$this->pipeparent = $pipe;
4094
	}
4095

    
4096
	/* Just a stub in case we ever try to call this from the frontend. */
4097
	function &add_queue($interface, &$queue, &$path, &$input_errors) {
4098
		return;
4099
	}
4100

    
4101
	function delete_queue() {
4102
		cleanup_dnqueue_from_rules($this->GetQname());
4103
		unset_dn_object_by_reference($this->GetLink());
4104
		@pfSense_ipfw_pipe("queue delete " . $this->GetNumber());
4105
	}
4106

    
4107
	function validate_input($data, &$input_errors) {
4108
		parent::validate_input($data, $input_errors);
4109

    
4110
		if ($data['weight'] && ((!is_numeric($data['weight'])) ||
4111
		    ($data['weight'] < 1 && $data['weight'] > 100))) {
4112
			$input_errors[] = gettext("Weight must be an integer between 1 and 100.");
4113
		}
4114
	}
4115

    
4116
	/*
4117
	 * Should search even its children
4118
	 */
4119
	function &find_queue($pipe, $qname) {
4120
		if ($qname == $this->GetQname()) {
4121
			return $this;
4122
		} else {
4123
			return NULL;
4124
		}
4125
	}
4126

    
4127
	function &find_parentqueue($pipe, $qname) {
4128
		return $this->qparent;
4129
	}
4130

    
4131
	function &get_queue_list(&$qlist) {
4132
		if ($this->GetEnabled() == "") {
4133
			return;
4134
		}
4135
		$qlist[$this->GetQname()] = "?" .$this->GetNumber();
4136
	}
4137

    
4138
	function ReadConfig(&$q) {
4139
		if (!empty($q['name']) && !empty($q['newname']) && $q['name'] != $q['newname']) {
4140
			$this->SetQname($q['newname']);
4141
		} else if (!empty($q['newname'])) {
4142
			$this->SetQname($q['newname']);
4143
		} else {
4144
			$this->SetQname($q['name']);
4145
		}
4146
		$this->SetNumber($q['number']);
4147
		if (isset($q['qlimit']) && $q['qlimit'] <> "") {
4148
			$this->SetQlimit($q['qlimit']);
4149
		} else {
4150
			$this->SetQlimit("");
4151
		}
4152
		if (isset($q['mask']) && $q['mask'] <> "") {
4153
			$masktype = $q['mask'];
4154
		} else {
4155
			$masktype = "";
4156
		}
4157
		if (isset($q['maskbits']) && $q['maskbits'] <> "") {
4158
			$maskbits = $q['maskbits'];
4159
		} else {
4160
			$maskbits = "";
4161
		}
4162
		if (isset($q['maskbitsv6']) && $q['maskbitsv6'] <> "") {
4163
			$maskbitsv6 = $q['maskbitsv6'];
4164
		} else {
4165
			$maskbitsv6 = "";
4166
		}
4167
		$this->SetMask(array("type" => $masktype, "bits" => $maskbits, "bitsv6" => $maskbitsv6));
4168
		if (isset($q['buckets']) && $q['buckets'] <> "") {
4169
			$this->SetBuckets($q['buckets']);
4170
		} else {
4171
			$this->SetBuckets("");
4172
		}
4173
		if (isset($q['plr']) && $q['plr'] <> "") {
4174
			$this->SetPlr($q['plr']);
4175
		} else {
4176
			$this->SetPlr("");
4177
		}
4178
		if (isset($q['weight']) && $q['weight'] <> "") {
4179
			$this->SetWeight($q['weight']);
4180
		} else {
4181
			$this->SetWeight("");
4182
		}
4183
		if (isset($q['description']) && $q['description'] <> "") {
4184
			$this->SetDescription($q['description']);
4185
		} else {
4186
			$this->SetDescription("");
4187
		}
4188
		$this->SetEnabled($q['enabled']);
4189
	}
4190

    
4191
	function build_tree() {
4192
		$parent =& $this->GetParent();
4193
		$tree = " <li><a href=\"firewall_shaper_vinterface.php?pipe=" . $parent->GetQname() ."&amp;queue=" . $this->GetQname() ."&amp;action=show\">";
4194
		$tree .= $this->GetQname() . "</a>";
4195
		$tree .= "</li>";
4196

    
4197
		return $tree;
4198
	}
4199

    
4200
	function build_rules() {
4201
		if ($this->GetEnabled() == "") {
4202
			return;
4203
		}
4204

    
4205
		$parent =& $this->GetParent();
4206
		$pfq_rule = "queue ". $this->GetNumber() . " config pipe " . $parent->GetNumber();
4207
		if ($this->GetQlimit()) {
4208
			$pfq_rule .= " queue " . $this->GetQlimit();
4209
		}
4210
		if ($this->GetWeight()) {
4211
			$pfq_rule .= " weight " . $this->GetWeight();
4212
		}
4213
		if ($this->GetBuckets()) {
4214
			$pfq_rule .= " buckets " . $this->GetBuckets();
4215
		}
4216
		$this->build_mask_rules($pfq_rule);
4217
		$pfq_rule .= "\n";
4218

    
4219
		return $pfq_rule;
4220
	}
4221

    
4222
	function build_javascript() {
4223
		return parent::build_javascript();
4224
	}
4225

    
4226
	function build_form() {
4227
		global $g, $config, $pipe, $action, $qname;
4228

    
4229
		//build list of schedules
4230
		$schedules = array();
4231
		$schedules[] = "none";//leave none to leave rule enabled all the time
4232
		if (is_array($config['schedules']) && is_array($config['schedules']['schedule'])) {
4233
			foreach ($config['schedules']['schedule'] as $schedule) {
4234
				if ($schedule['name'] <> "") {
4235
					$schedules[] = $schedule['name'];
4236
				}
4237
			}
4238
		}
4239

    
4240

    
4241
		$sform = new Form();
4242
		$sform->setAction("firewall_shaper.php");
4243
		$section = new Form_Section('Limiters');
4244

    
4245
		$section->addInput(new Form_Checkbox(
4246
			'enabled',
4247
			'Enable',
4248
			'Enable this queue',
4249
			($this->GetEnabled() == "on"),
4250
			'on'
4251
		));
4252

    
4253
		$section->addInput(new Form_Input(
4254
			'newname',
4255
			'*Name',
4256
			'text',
4257
			$this->GetQname()
4258
		));
4259

    
4260
		$section->addInput(new Form_Input(
4261
			'name',
4262
			null,
4263
			'hidden',
4264
			$this->GetQname()
4265
		));
4266

    
4267
		if ($this->GetNumber() > 0) {
4268
			$section->addInput(new Form_Input(
4269
				'number',
4270
				null,
4271
				'hidden',
4272
				$this->GetNumber()
4273
			));
4274
		}
4275

    
4276
		$mask = $this->GetMask();
4277

    
4278
		$section->addInput(new Form_Select(
4279
			'mask',
4280
			'Mask',
4281
			$mask['type'],
4282
			array('none' => gettext('None'), 'srcaddress' => gettext('Source addresses'), 'dstaddress' => gettext('Destination addresses'))
4283
		))->setHelp('If "source" or "destination" slots is chosen a dynamic pipe with the bandwidth, delay, packet loss ' .
4284
					'and queue size given above will be created for each source/destination IP address encountered, respectively. ' .
4285
					'This makes it possible to easily specify bandwidth limits per host.');
4286

    
4287
		$group = new Form_Group(null);
4288

    
4289
		$group->add(new Form_Select(
4290
			'maskbits',
4291
			null,
4292
			$mask['bits'],
4293
			array_combine(range(32, 1, -1), range(32, 1, -1))
4294
		))->setHelp('IPv4 mask bits%1$s%2$s', '<br />', '255.255.255.255/?');
4295

    
4296
		$group->add(new Form_Select(
4297
			'maskbitsv6',
4298
			null,
4299
			$mask['bitsv6'],
4300
			array_combine(range(128, 1, -1), range(128, 1, -1))
4301
		))->setHelp('IPv6 mask bits%1$s%2$s', '<br />', '<span style="font-family:consolas">ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/?</span>');
4302

    
4303
		$section->add($group);
4304

    
4305
		$section->addInput(new Form_Input(
4306
			'description',
4307
			'Description',
4308
			'text',
4309
			$this->GetDescription()
4310
		))->setHelp('A description may be entered here for administrative reference (not parsed).');
4311

    
4312
		$sform->add($section);
4313

    
4314
		$section = new Form_Section('Advanced Options');
4315

    
4316
		$section->addInput(new Form_Input(
4317
			'weight',
4318
			'Weight',
4319
			'number',
4320
			$this->GetWeight(),
4321
			['min' => '1', 'max' => '100']
4322
		))->setHelp('For queues under the same parent this specifies the share that a queue gets(values range from 1 to 100),' .
4323
					' it can be left blank otherwise.');
4324

    
4325
		$section->addInput(new Form_Input(
4326
			'plr',
4327
			'Packet Loss Rate',
4328
			'number',
4329
			$this->GetPlr(),
4330
			['step' => '0.001', 'min' => '0.000']
4331
		))->setHelp('In most cases, zero (0) should be specified here (or leave the field empty). ' .
4332
					'A value of 0.001 means one packet in 1000 gets dropped');
4333

    
4334
		$section->addInput(new Form_Input(
4335
			'qlimit',
4336
			'Queue size (slots)',
4337
			'number',
4338
			$this->GetQlimit()
4339
		))->setHelp('In most cases, the field should be left empty. All packets in this pipe are placed into a fixed-size queue first, ' .
4340
					'then they are delayed by value specified in the Delay field, and then they are delivered to their destination.');
4341

    
4342
		$section->addInput(new Form_Input(
4343
			'buckets',
4344
			'Bucket size (slots)',
4345
			'number',
4346
			$this->GetBuckets()
4347
		))->setHelp('In most cases, this field should be left empty. It increases the hash size set');
4348

    
4349
		$section->addInput(new Form_Input(
4350
			'pipe',
4351
			null,
4352
			'hidden',
4353
			$this->GetPipe()
4354
		));
4355

    
4356
		$sform->add($section);
4357

    
4358
		return($sform);
4359
	}
4360

    
4361
	function update_dn_data(&$data) {
4362
		$this->ReadConfig($data);
4363
	}
4364

    
4365
	function wconfig() {
4366
		$cflink =& get_dn_reference_to_me_in_config($this->GetLink());
4367
		if (!is_array($cflink)) {
4368
			$cflink = array();
4369
		}
4370
		$cflink['name'] = $this->GetQname();
4371
		$cflink['number'] = $this->GetNumber();
4372
		$cflink['qlimit'] = $this->GetQlimit();
4373
		$cflink['description'] = $this->GetDescription();
4374
		$cflink['weight'] = $this->GetWeight();
4375
		$cflink['enabled'] = $this->GetEnabled();
4376
		$cflink['buckets'] = $this->GetBuckets();
4377
		$mask = $this->GetMask();
4378
		$cflink['mask'] = $mask['type'];
4379
		$cflink['maskbits'] = $mask['bits'];
4380
		$cflink['maskbitsv6'] = $mask['bitsv6'];
4381
	}
4382
}
4383

    
4384
function get_dummynet_name_list() {
4385

    
4386
	$dn_name_list =& get_unique_dnqueue_list();
4387
	$dn_name = array();
4388
	if (is_array($dn_name_list)) {
4389
		foreach ($dn_name_list as $key => $value) {
4390
			$dn_name[] = $key;
4391
		}
4392
	}
4393

    
4394
	return $dn_name;
4395

    
4396
}
4397

    
4398
function get_altq_name_list() {
4399
	$altq_name_list =& get_unique_queue_list();
4400
	$altq_name = array();
4401
	if (is_array($altq_name_list)) {
4402
		foreach ($altq_name_list as $key => $aqobj) {
4403
			$altq_name[] = $key;
4404
		}
4405
	}
4406

    
4407
	return $altq_name;
4408
}
4409

    
4410
/*
4411
 * XXX: TODO Make a class shaper to hide all these functions
4412
 * from the global namespace.
4413
 */
4414

    
4415
/*
4416
 * This is a layer violation but for now there is no way
4417
 * I can find to properly do this with PHP.
4418
 */
4419
function altq_get_default_queue($interface) {
4420
	global $altq_list_queues;
4421

    
4422
	$altq_tmp = $altq_list_queues[$interface];
4423
	if ($altq_tmp) {
4424
		return $altq_tmp->GetDefaultQueuePresent();
4425
	} else {
4426
		return false;
4427
	}
4428
}
4429

    
4430
function altq_check_default_queues() {
4431
	global $altq_list_queues;
4432

    
4433
	$count = 0;
4434
	if (is_array($altq_list_queues)) {
4435
		foreach ($altq_list_queues as $altq) {
4436
			if ($altq->GetDefaultQueuePresent()) {
4437
				$count++;
4438
			}
4439
		}
4440
	}
4441
	else {
4442
		$count++;
4443
	}
4444

    
4445
	return 0;
4446
}
4447

    
4448
function &get_unique_queue_list() {
4449
	global $altq_list_queues;
4450

    
4451
	$qlist = array();
4452
	if (is_array($altq_list_queues)) {
4453
		foreach ($altq_list_queues as $altq) {
4454
			if ($altq->GetEnabled() == "") {
4455
				continue;
4456
			}
4457
			$tmplist =& $altq->get_queue_list();
4458
			foreach ($tmplist as $qname => $link) {
4459
				if ($link->GetEnabled() <> "") {
4460
					$qlist[$qname] = $link;
4461
				}
4462
			}
4463
		}
4464
	}
4465
	return $qlist;
4466
}
4467

    
4468
function &get_unique_dnqueue_list() {
4469
	global $dummynet_pipe_list;
4470

    
4471
	$qlist = array();
4472
	if (is_array($dummynet_pipe_list)) {
4473
		foreach ($dummynet_pipe_list as $dn) {
4474
			if ($dn->GetEnabled() == "") {
4475
				continue;
4476
			}
4477
			$tmplist =& $dn->get_queue_list();
4478
			foreach ($tmplist as $qname => $link) {
4479
				$qlist[$qname] = $link;
4480
			}
4481
		}
4482
	}
4483
	return $qlist;
4484
}
4485

    
4486
function ref_on_altq_queue_list($parent, $qname) {
4487
	if (isset($GLOBALS['queue_list'][$qname])) {
4488
		$GLOBALS['queue_list'][$qname]++;
4489
	} else {
4490
		$GLOBALS['queue_list'][$qname] = 1;
4491
	}
4492

    
4493
	unref_on_altq_queue_list($parent);
4494
}
4495

    
4496
function unref_on_altq_queue_list($qname) {
4497
	$GLOBALS['queue_list'][$qname]--;
4498
	if ($GLOBALS['queue_list'][$qname] <= 1) {
4499
		unset($GLOBALS['queue_list'][$qname]);
4500
	}
4501
}
4502

    
4503
function read_altq_config() {
4504
	global $altq_list_queues, $config;
4505
	$path = array();
4506

    
4507
	if (!is_array($config['shaper'])) {
4508
		$config['shaper'] = array();
4509
	}
4510
	if (!is_array($config['shaper']['queue'])) {
4511
		$config['shaper']['queue'] = array();
4512
	}
4513
	$a_int = &$config['shaper']['queue'];
4514

    
4515
	$altq_list_queues = array();
4516

    
4517
	if (!is_array($config['shaper']['queue'])) {
4518
		return;
4519
	}
4520

    
4521
	foreach ($a_int as $key => $conf) {
4522
		$int = $conf['interface'];
4523
		$__tmp_root = new altq_root_queue(); $root =& $__tmp_root;
4524
		$root->SetInterface($int);
4525
		$altq_list_queues[$root->GetInterface()] = &$root;
4526
		$root->ReadConfig($conf);
4527
		array_push($path, $key);
4528
		$root->SetLink($path);
4529
		if (is_array($conf['queue'])) {
4530
			foreach ($conf['queue'] as $key1 => $q) {
4531
				array_push($path, $key1);
4532
				/*
4533
				 * XXX: we completely ignore errors here but anyway we must have
4534
				 *	checked them before so no harm should be come from this.
4535
				 */
4536
				$root->add_queue($root->GetInterface(), $q, $path, $input_errors);
4537
				array_pop($path);
4538
			}
4539
		}
4540
		array_pop($path);
4541
	}
4542
}
4543

    
4544
function read_dummynet_config() {
4545
	global $dummynet_pipe_list, $config;
4546
	$path = array();
4547

    
4548
	if (!is_array($config['dnshaper'])) {
4549
		$config['dnshaper'] = array();
4550
	}
4551
	if (!is_array($config['dnshaper']['queue'])) {
4552
		$config['dnshaper']['queue'] = array();
4553
	}
4554
	$a_int = &$config['dnshaper']['queue'];
4555

    
4556
	$dummynet_pipe_list = array();
4557

    
4558
	if (!is_array($config['dnshaper']['queue']) ||
4559
	    !count($config['dnshaper']['queue'])) {
4560
		return;
4561
	}
4562

    
4563
	foreach ($a_int as $key => $conf) {
4564
		if (empty($conf['name'])) {
4565
			continue; /* XXX: grrrrrr at php */
4566
		}
4567
		$root = new dnpipe_class();
4568
		$root->ReadConfig($conf);
4569
		$dummynet_pipe_list[$root->GetQname()] = $root;
4570
		array_push($path, $key);
4571
		$root->SetLink($path);
4572
		if (is_array($conf['queue'])) {
4573
			foreach ($conf['queue'] as $key1 => $q) {
4574
				array_push($path, $key1);
4575
				/*
4576
				 * XXX: we completely ignore errors here but anyway we must have
4577
				 *	checked them before so no harm should be come from this.
4578
				 */
4579
				$root->add_queue($root->GetQname(), $q, $path, $input_errors);
4580
				array_pop($path);
4581
			}
4582
		}
4583
		array_pop($path);
4584
	}
4585
}
4586

    
4587
function get_interface_list_to_show() {
4588
	global $altq_list_queues, $config;
4589
	global $shaperIFlist;
4590

    
4591
	$tree = "";
4592
	foreach ($shaperIFlist as $shif => $shDescr) {
4593
		if ($altq_list_queues[$shif]) {
4594
			continue;
4595
		} else {
4596
			if (!is_altq_capable(get_real_interface($shif))) {
4597
				continue;
4598
			}
4599
			$tree .= " <li><a href=\"firewall_shaper.php?interface=".$shif."&amp;action=add\">".$shDescr."</a></li>";
4600
		}
4601
	}
4602

    
4603
	return $tree;
4604
}
4605

    
4606
function filter_generate_altq_queues() {
4607
	global $altq_list_queues;
4608

    
4609
	read_altq_config();
4610

    
4611
	$altq_rules = "";
4612
	foreach ($altq_list_queues as $altq) {
4613
		$altq_rules .= $altq->build_rules();
4614
	}
4615

    
4616
	return $altq_rules;
4617
}
4618

    
4619
function dnqueue_find_nextnumber() {
4620
	global $dummynet_pipe_list;
4621

    
4622
	$dnused = array();
4623
	if (is_array($dummynet_pipe_list)) {
4624
		foreach ($dummynet_pipe_list as $dn) {
4625
			$tmplist =& $dn->get_queue_list();
4626
			foreach ($tmplist as $qname => $link) {
4627
				if ($link[0] == "?") {
4628
					$dnused[$qname] = substr($link, 1);
4629
				}
4630
			}
4631
		}
4632
	}
4633

    
4634
	sort($dnused, SORT_NUMERIC);
4635
	$dnnumber = 0;
4636
	$found = false;
4637
	foreach ($dnused as $dnnum) {
4638
		if (($dnnum - $dnnumber) > 1) {
4639
			$dnnumber = $dnnum - 1;
4640
			$found = true;
4641
			break;
4642
		} else {
4643
			$dnnumber = $dnnum;
4644
		}
4645
	}
4646

    
4647
	if ($found == false) {
4648
		$dnnumber++;
4649
	}
4650

    
4651
	unset($dnused, $dnnum, $found);
4652
	return $dnnumber;
4653
}
4654

    
4655
function dnpipe_find_nextnumber() {
4656
	global $dummynet_pipe_list;
4657

    
4658
	$dnused = array();
4659
	foreach ($dummynet_pipe_list as $dn) {
4660
		$dnused[] = $dn->GetNumber();
4661
	}
4662

    
4663
	sort($dnused, SORT_NUMERIC);
4664
	$dnnumber = 0;
4665
	$found = false;
4666
	foreach ($dnused as $dnnum) {
4667
		if (($dnnum - $dnnumber) > 1) {
4668
			$dnnumber = $dnnum - 1;
4669
			$found = true;
4670
			break;
4671
		} else {
4672
			$dnnumber = $dnnum;
4673
		}
4674
	}
4675

    
4676
	if ($found == false) {
4677
		$dnnumber++;
4678
	}
4679

    
4680
	unset($dnused, $dnnum, $found);
4681
	return $dnnumber;
4682
}
4683

    
4684
function filter_generate_dummynet_rules() {
4685
	global $g, $dummynet_pipe_list;
4686

    
4687
	read_dummynet_config();
4688

    
4689
	$dn_rules = "";
4690
	$max_qlimit = "100"; // OS default
4691
	foreach ($dummynet_pipe_list as $dn) {
4692
		$dn_rules .= $dn->build_rules();
4693
		$this_qlimit = $dn->GetQlimit();
4694
		if ($this_qlimit > $max_qlimit) {
4695
			$max_qlimit = $this_qlimit;
4696
		}
4697
	}
4698
	if (!is_numericint($max_qlimit)) {
4699
		$max_qlimit = "100";
4700
	}
4701
	if (!empty($dn_rules)) {
4702
		if (!is_module_loaded("dummynet.ko")) {
4703
			mwexec("/sbin/kldload dummynet");
4704
		}
4705
		set_sysctl(array(
4706
				"net.inet.ip.dummynet.io_fast" => "1",
4707
				"net.inet.ip.dummynet.hash_size" => "256",
4708
				"net.inet.ip.dummynet.pipe_slot_limit" => $max_qlimit
4709
		));
4710
		file_put_contents("{$g['tmp_path']}/rules.limiter", $dn_rules);
4711
		mwexec("/sbin/ipfw {$g['tmp_path']}/rules.limiter");
4712
	}
4713
}
4714

    
4715
function build_iface_without_this_queue($iface, $qname) {
4716
	global $g, $altq_list_queues;
4717
	global $shaperIFlist;
4718

    
4719
	$altq =& $altq_list_queues[$iface];
4720

    
4721
	if ($altq) {
4722
		$scheduler = $altq->GetScheduler();
4723
	}
4724

    
4725
	$form = '<dl class="dl-horizontal">';
4726

    
4727
	$form .= '	<dt>';
4728
	$form .= '		<a href="firewall_shaper.php?interface=' . $iface . '&amp;queue=' . $iface . '&amp;action=show">' . $shaperIFlist[$iface] . '</a>';
4729
	$form .= '	</dt>';
4730
	$form .= '	<dd>';
4731
	$form .=		$scheduler;
4732
	$form .= '	</dd>';
4733

    
4734
	$form .= '	<dt>';
4735
	$form .= 'Clone';
4736
	$form .= '	</dt>';
4737
	$form .= '	<dd>';
4738
	$form .= '<a class="btn btn-info btn-xs" href="firewall_shaper_queues.php?interface=';
4739
	$form .= $iface . '&amp;queue=';
4740
	$form .= $qname . '&amp;action=add">';
4741
	$form .= '<i class="fa fa-clone icon-embed-btn"></i>';
4742
	$form .= gettext("Clone Shaper to this Interface") . '</a>';
4743
	$form .= '	</dd>';
4744

    
4745
	$form .= '</dl>';
4746

    
4747
	return $form;
4748

    
4749
}
4750

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

    
4754
$shaper_msg = gettext("The tree on the left navigates through the %s.");
4755
$default_shaper_msg .= sprintf($shaper_msg, gettext("queues")) . "<br />";
4756
$dn_default_shaper_msg .= sprintf($shaper_msg, gettext("limiters")) . "<br />";
4757

    
4758
$shaper_msg = gettext("Buttons at the bottom represent %s actions and are activated accordingly.");
4759
$default_shaper_msg .= sprintf($shaper_msg, gettext("queue"));
4760
$dn_default_shaper_msg .= sprintf($shaper_msg, gettext("limiter"));
4761

    
4762
// Check to see if the specified interface has a queue configured
4763
function interface_has_queue($if) {
4764
	global $config;
4765

    
4766
	if ($config['shaper']) {
4767
		foreach ($config['shaper']['queue'] as $queue) {
4768
			if ($queue['interface'] === $if) {
4769
				return true;
4770
			}
4771
		}
4772
	}
4773

    
4774
	return false;
4775
}
4776
?>
(47-47/60)