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
		$t_ptr = $ptr['queue'][$indeks];
120
		$ptr =& $t_ptr;
121
	}
122

    
123
	return $ptr;
124
}
125

    
126
function unset_object_by_reference(&$mypath) {
127
	global $config;
128

    
129
	$ptr =& $config['shaper'];
130
	for ($i = 0; $i < count($mypath) - 1; $i++) {
131
		$ptr =& $ptr['queue'][$mypath[$i]];
132
	}
133
	unset($ptr['queue'][$mypath[$i]]);
134
}
135

    
136
function &get_dn_reference_to_me_in_config(&$mypath) {
137
	global $config;
138

    
139
	$ptr =& $config['dnshaper'];
140
	foreach ($mypath as $indeks) {
141
		$ptr =& $ptr['queue'][$indeks];
142
	}
143

    
144
	return $ptr;
145
}
146

    
147
function unset_dn_object_by_reference(&$mypath) {
148
	global $config;
149

    
150
	$ptr =& $config['dnshaper'];
151
	for ($i = 0; $i < count($mypath) - 1; $i++) {
152
		$ptr =& $ptr['queue'][$mypath[$i]];
153
	}
154
	unset($ptr['queue'][$mypath[$i]]);
155
}
156

    
157
function clean_child_queues($type, $mypath) {
158
	$ref = &get_reference_to_me_in_config($mypath);
159

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

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

    
254
function get_bandwidth($bw, $scale, $obj) {
255

    
256
	$pattern= "/(b|Kb|Mb|Gb|%)/";
257
	if (!preg_match($pattern, $scale, $match))
258
		return 0;
259

    
260
	switch ($match[1]) {
261
		case '%':
262
			$objbw = ($bw / 100) * get_queue_bandwidth($obj);
263
			break;
264
		default:
265
			$objbw = $bw * get_bandwidthtype_scale($scale);
266
			break;
267
	}
268

    
269
	return floatval($objbw);
270
}
271

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

    
299
function get_queue_bandwidth($obj) {
300
	$bw = $obj->GetBandwidth();
301
	$scale = $obj->GetBwscale();
302

    
303
	$pattern= "/(b|Kb|Mb|Gb|%)/";
304
	if (!preg_match($pattern, $scale, $match))
305
		return 0;
306

    
307
	switch ($match[1]) {
308
		case '%':
309
			$objbw = ($bw / 100) * get_queue_bandwidth($obj->GetParent());
310
			break;
311
		default:
312
			$objbw = $bw * get_bandwidthtype_scale($scale);
313
			break;
314
	}
315

    
316
	return floatval($objbw);
317
}
318

    
319
function get_interface_bandwidth($object) {
320
	global $altq_list_queues;
321

    
322
	$int = $object->GetInterface();
323
	$altq =& $altq_list_queues[$int];
324
	if ($altq) {
325
		$bw_3 = $altq->GetBandwidth();
326
		$bw_3 = $bw_3 * get_bandwidthtype_scale($altq->GetBwscale());
327
		return floatval($bw_3);
328
	} else {
329
		return 0;
330
	}
331
}
332

    
333
/*
334
 * This is duplicated here since we cannot include guiconfig.inc.
335
 * Including it makes all stuff break.
336
 */
337
function shaper_do_input_validation($postdata, $reqdfields, $reqdfieldsn, $input_errors) {
338

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

    
346
	for ($i = 0; $i < count($reqdfields); $i++) {
347
		if ($postdata[$reqdfields[$i]] == "") {
348
			$input_errors[] = sprintf(gettext("The field '%s' is required."), $reqdfieldsn[$i]);
349
		}
350
	}
351
}
352

    
353
function cleanup_queue_from_rules($queue) {
354
	global $config;
355

    
356
	foreach ($config['filter']['rule'] as $rule) {
357
		if ($rule['defaultqueue'] == $queue) {
358
			unset($rule['defaultqueue']);
359
		}
360
		if ($rule['ackqueue'] == $queue) {
361
			unset($rule['ackqueue']);
362
		}
363
	}
364
}
365

    
366
function cleanup_dnqueue_from_rules($queue) {
367
	global $config;
368

    
369
	foreach ($config['filter']['rule'] as $rule) {
370
		if ($rule['dnpipe'] == $queue) {
371
			unset($rule['dnpipe']);
372
		}
373
		if ($rule['pdnpipe'] == $queue) {
374
			unset($rule['pdnpipe']);
375
		}
376
	}
377
}
378

    
379
class altq_root_queue {
380
	var $interface;
381
	var $tbrconfig ;
382
	var $bandwidth;
383
	var $bandwidthtype; /* b, Kb, Mb, Gb, % */
384
	var $scheduler;
385
	var $qlimit;
386
	var $queues = array();
387
	var $qenabled = false;
388
	var $link;
389

    
390
	/* Accessor functions */
391
	function GetDefaultQueuePresent() {
392
		if (!empty($this->queues)) {
393
			foreach ($this->queues as $q) {
394
				if ($q->GetDefault()) {
395
					return true;
396
				}
397
			}
398
		}
399

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

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

    
489
	function CheckBandwidth($bw, $bwtype) {
490
		$sum = $this->GetTotalBw();
491
		if ($sum > $bw * get_bandwidthtype_scale($bwtype))
492
			return 1;
493
		foreach ($this->queues as $q) {
494
			if ($q->CheckBandwidth(0, ''))
495
				return 1;
496
		}
497

    
498
		return 0;
499
	}
500

    
501
	function GetTotalBw($qignore = NULL) {
502
		$sum = 0;
503
		foreach ($this->queues as $q) {
504
			if ($qignore != NULL && $qignore == $q)
505
				continue;
506
			$sum += get_bandwidth($q->GetBandwidth(), $q->GetBwscale(), $this);
507
		}
508

    
509
		return $sum;
510
	}
511

    
512
	function validate_input($data, &$input_errors) {
513

    
514
		$reqdfields[] = "bandwidth";
515
		$reqdfieldsn[] = gettext("Bandwidth");
516
		$reqdfields[] = "bandwidthtype";
517
		$reqdfieldsn[] = gettext("Bandwidthtype");
518

    
519
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
520

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

    
538
		if ($data['qlimit'] && (!is_numeric($data['qlimit']))) {
539
			$input_errors[] = gettext("Qlimit must be an integer.");
540
		}
541
		if ($data['qlimit'] < 0) {
542
			$input_errors[] = gettext("Qlimit must be positive.");
543
		}
544
		if ($data['tbrconfig'] && (!is_numeric($data['tbrconfig']))) {
545
			$input_errors[] = gettext("Tbrsize must be an integer.");
546
		}
547
		if ($data['tbrconfig'] < 0) {
548
			$input_errors[] = gettext("Tbrsize must be positive.");
549
		}
550
	}
551

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

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

    
605
	function &get_queue_list(&$q = null) {
606
		$qlist = array();
607

    
608
		//$qlist[$this->GetQname()] = & $this;
609
		if (is_array($this->queues)) {
610
			foreach ($this->queues as $queue) {
611
				$queue->get_queue_list($qlist);
612
			}
613
		}
614
		return $qlist;
615
	}
616

    
617
	function &add_queue($interface, &$queue, &$path, &$input_errors) {
618

    
619
		if (!is_array($this->queues)) {
620
			$this->queues = array();
621
		}
622

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

    
648
		$this->queues[$q->GetQname()] = &$q;
649
		ref_on_altq_queue_list($this->GetQname(), $q->GetQname());
650
		if (is_array($queue['queue'])) {
651
			foreach ($queue['queue'] as $key1 => $que) {
652
				array_push($path, $key1);
653
				$q->add_queue($q->GetInterface(), $que, $path, $input_errors);
654
				array_pop($path);
655
			}
656
		}
657

    
658
		return $q;
659
	}
660

    
661
	/* interface here might be optional */
662
	function &find_queue($interface, $qname) {
663
		if ($qname == $this->GetQname()) {
664
			return $this;
665
		}
666
		foreach ($this->queues as $q) {
667
			$result =& $q->find_queue("", $qname);
668
			if ($result) {
669
				return $result;
670
			}
671
		}
672
	}
673

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

    
689
	function build_tree() {
690
		global $shaperIFlist;
691

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

    
705
	function delete_queue() {
706
		foreach ($this->queues as $q)
707
			$q->delete_queue();
708
		unset_object_by_reference($this->GetLink());
709
	}
710

    
711
	function delete_all() {
712
		if (count($this->queues)) {
713
			foreach ($this->queues as $q) {
714
				$q->delete_all();
715
				unset_object_by_reference($q->GetLink());
716
				unset($q);
717
			}
718
			unset($this->queues);
719
		}
720
	}
721

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

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

    
791
			$rules .= " queue";
792
		}
793

    
794
		$rules .= " \n";
795
		return $rules;
796
	}
797

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

    
808
		$javascript .= "function myResume() {";
809
		$javascript .= "if (document.layers && document.layers['shaperarea'] != null) ";
810
		$javascript .= "document.layers['shaperarea'].visibility = 'visible';";
811
		$javascript .= "else if (document.all) ";
812
		$javascript .= "document.all['shaperarea'].style.visibility = 'visible';";
813
		$javascript .= "}";
814
		$javascript .= "//]]>";
815
		$javascript .= "</script>";
816

    
817
		return $javascript;
818
	}
819

    
820
	function build_shortform() {
821
		global $g;
822

    
823
		$altq =& $this;
824

    
825
		if ($altq) {
826
			$scheduler = ": " . $altq->GetScheduler();
827
		}
828

    
829
		$form = '<dl class="dl-horizontal">';
830
		$form .= '	<dt>';
831
		$form .= '		<a href="firewall_shaper.php?interface=' . $this->GetInterface() . '&amp;queue=' . $this->GetQname() . '&amp;action=show">' . $shaperIFlist[$this->GetInterface()] . '</a>';
832
		$form .= '	</dt>';
833
		$form .= '	<dd>';
834
		$form .=		$scheduler;
835
		$form .= '	</dd>';
836

    
837
		$form .= '	<dt>';
838
		$form .=		'Bandwidth';
839
		$form .= '	</dt>';
840
		$form .= '	<dd>';
841
		$form .=		$this->GetBandwidth() . '&nbsp;' . $this->GetBwscaleText();
842
		$form .= '	</dd>';
843

    
844
		$form .= '	<dt>';
845
		$form .= 'Disable';
846
		$form .= '	<dt>';
847
		$form .= '	<dd>';
848

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

    
855
		$form .= '	</dd>';
856

    
857
		$form .= '</dl>';
858

    
859
		return $form;
860

    
861
	}
862

    
863
	/*
864
	 * For requesting the parameters of the root queues
865
	 * to the user like the traffic wizard does.
866
	 */
867
	function build_form() {
868

    
869
		$sform = new Form();
870

    
871
		$sform->setAction("firewall_shaper.php");
872

    
873
		$section = new Form_Section(null);
874

    
875
		$section->addInput(new Form_Checkbox(
876
			'enabled',
877
			'Enable/Disable',
878
			'Enable/disable discipline and its children',
879
			($this->GetEnabled() == "on"),
880
			'on'
881
		));
882

    
883
		$section->addInput(new Form_StaticText(
884
			'*Name',
885
			$this->GetQname()
886
		));
887

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

    
899
		$group = new Form_group('Bandwidth');
900

    
901
		$group->add(new Form_Input(
902
			'bandwidth',
903
			null,
904
			'number',
905
			$this->GetBandwidth()
906
		));
907

    
908
		$group->add(new Form_Select(
909
			'bandwidthtype',
910
			null,
911
			$this->GetBwscale(),
912
			array('Kb' => 'Kbit/s',
913
				  'Mb' => 'Mbit/s',
914
				  'Gb' => 'Gbit/s',
915
				  'b' => 'Bit/s',
916
				  '%' => '%')
917
		));
918

    
919
		$section->add($group);
920

    
921
		$section->addInput(new Form_Input(
922
			'qlimit',
923
			'Queue Limit',
924
			'number',
925
			$this->GetQlimit()
926
		));
927

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

    
936
		$section->addInput(new Form_Input(
937
			'interface',
938
			null,
939
			'hidden',
940
			$this->GetInterface()
941
		));
942

    
943
		$section->addInput(new Form_Input(
944
			'name',
945
			null,
946
			'hidden',
947
			$this->GetQname()
948
		));
949

    
950
		$sform->add($section);
951

    
952
		return($sform);
953
	}
954

    
955
	function update_altq_queue_data(&$data) {
956
		$this->ReadConfig($data);
957
	}
958

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

    
987
}
988

    
989
class priq_queue {
990
	var $qname;
991
	var $qinterface;
992
	var $qlimit;
993
	var $qpriority;
994
	var $description;
995
	var $isparent;
996
	var $qbandwidth;
997
	var $qbandwidthtype;
998
	var $qdefault = "";
999
	var $qrio = "";
1000
	var $qred = "";
1001
	var $qcodel = "";
1002
	var $qecn = "";
1003
	var $qack;
1004
	var $qenabled = "";
1005
	var $qparent;
1006
	var $link;
1007

    
1008
	/* This is here to help with form building and building rules/lists */
1009
	var $subqueues = array();
1010

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

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

    
1086
		if ($bwtype != "%") {
1087
			$sum = $parent->GetTotalBw(($bw > 0) ? $this : NULL);
1088

    
1089
			if ($bw > 0) {
1090
				$sum += get_bandwidth($bw, $bwtype, $parent);
1091
			}
1092

    
1093
			if ($sum > get_queue_bandwidth($parent)) {
1094
				return 1;
1095
			}
1096
		}
1097

    
1098
		foreach ($this->subqueues as $q) {
1099
			if ($q->CheckBandwidth(0, '')) {
1100
				return 1;
1101
			}
1102
		}
1103

    
1104
		return 0;
1105
	}
1106
	function GetTotalBw($qignore = NULL) {
1107
		$sum = 0;
1108
		foreach ($this->subqueues as $q) {
1109
			if ($qignore != NULL && $qignore == $q)
1110
				continue;
1111
			$sum += get_bandwidth($q->GetBandwidth(), $q->GetBwscale(), $q->GetParent());
1112
		}
1113

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

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

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

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

    
1205
		$javascript .= "function myResume() {\n";
1206
		$javascript .= "if (document.layers && document.layers['shaperarea'] != null)\n";
1207
		$javascript .= "document.layers['shaperarea'].visibility = 'visible';\n";
1208
		$javascript .= "else if (document.all)\n";
1209
		$javascript .= "document.all['shaperarea'].style.visibility = 'visible';\n";
1210
		$javascript .= "}\n";
1211
		$javascript .= "//]]>";
1212
		$javascript .= "</script>";
1213

    
1214
		return $javascript;
1215
	}
1216

    
1217
	function &add_queue($interface, &$qname, &$path, &$input_errors) { return; }
1218

    
1219
	/*
1220
	 * Currently this will not be called unless we decide to clone a whole
1221
	 * queue tree on the 'By Queues' view or support drag&drop on the tree/list
1222
	 */
1223
	function copy_queue($interface, &$cflink) {
1224

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

    
1237
		if (is_array($this->subqueues)) {
1238
			$cflinkp['queue'] = array();
1239
			foreach ($this->subqueues as $q) {
1240
				$cflink['queue'][$q->GetQname()] = array();
1241
				$q->copy_queue($interface, $cflink['queue'][$q->GetQname()]);
1242
			}
1243
		}
1244
	}
1245

    
1246
	function clean_queue($sched) {
1247
		clean_child_queues($sched, $this->GetLink());
1248
		if (is_array($this->subqueues)) {
1249
			foreach ($this->subqueues as $q) {
1250
				$q->clean_queue($sched);
1251
			}
1252
		}
1253
	}
1254

    
1255
	function &get_queue_list(&$qlist) {
1256

    
1257
		$qlist[$this->GetQname()] = & $this;
1258
		if (is_array($this->subqueues)) {
1259
			foreach ($this->subqueues as $queue) {
1260
				$queue->get_queue_list($qlist);
1261
			}
1262
		}
1263
	}
1264

    
1265
	function delete_queue() {
1266
		unref_on_altq_queue_list($this->GetQname());
1267
		cleanup_queue_from_rules($this->GetQname());
1268
		unset_object_by_reference($this->GetLink());
1269
	}
1270

    
1271
	function delete_all() {
1272
		if (count($this->subqueues)) {
1273
			foreach ($this->subqueues as $q) {
1274
				$q->delete_all();
1275
				unset_object_by_reference($q->GetLink());
1276
				unset($q);
1277
			}
1278
			unset($this->subqueues);
1279
		}
1280
	}
1281

    
1282
	function &find_queue($interface, $qname) {
1283
		if ($qname == $this->GetQname()) {
1284
			return $this;
1285
		}
1286
	}
1287

    
1288
	function find_parentqueue($interface, $qname) { return; }
1289

    
1290
	function validate_input($data, &$input_errors) {
1291

    
1292
		$reqdfields[] = "name";
1293
		$reqdfieldsn[] = gettext("Name");
1294
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
1295

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

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

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

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

    
1413
		$tree .= "</li>";
1414

    
1415
		return $tree;
1416
	}
1417

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

    
1476
		$pfq_rule .= " \n";
1477

    
1478
		return $pfq_rule;
1479
	}
1480

    
1481
	/*
1482
	 * To return the html form to show to user
1483
	 * for getting the parameters.
1484
	 * Should do even for first time when the
1485
	 * object is created and later when we may
1486
	 * need to update it. (2)
1487
	 */
1488

    
1489
	function build_form() {
1490

    
1491
		$sform = new Form();
1492

    
1493
		$sform->setAction("firewall_shaper.php");
1494

    
1495
		$section = new Form_Section("");
1496

    
1497
		$section->addInput(new Form_Checkbox(
1498
			'enabled',
1499
			'Enable/Disable',
1500
			'Enable/disable discipline and its children',
1501
			($this->GetEnabled() == "on"),
1502
			'on'
1503
		));
1504

    
1505
		$section->addInput(new Form_Input(
1506
			'newname',
1507
			'*Name',
1508
			'text',
1509
			$this->GetQname()
1510
		))->setHelp('Enter the name of the queue here. Do not use spaces and limit the size to 15 characters.');
1511

    
1512
		$section->addInput(new Form_Input(
1513
			'name',
1514
			null,
1515
			'hidden',
1516
			$this->GetQname()
1517
		));
1518

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

    
1529
		$section->addInput(new Form_Input(
1530
			'qlimit',
1531
			'Queue Limit',
1532
			'number',
1533
			$this->GetQlimit()
1534
		))->setHelp('Queue limit in packets.');
1535

    
1536
		$group = new Form_Group('Scheduler options');
1537

    
1538
		if (empty($this->subqueues)) {
1539
			$group->add(new Form_Checkbox(
1540
				'default',
1541
				null,
1542
				null,
1543
				$this->GetDefault(),
1544
				'default'
1545
			))->setHelp('Default Queue');
1546
		}
1547

    
1548
		$group->add(new Form_Checkbox(
1549
			'red',
1550
			null,
1551
			null,
1552
			!empty($this->GetRed())
1553
		))->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>');
1554

    
1555
		$group->add(new Form_Checkbox(
1556
			'rio',
1557
			null,
1558
			null,
1559
			!empty($this->GetRio())
1560
		))->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>');
1561

    
1562
		$group->add(new Form_Checkbox(
1563
			'ecn',
1564
			null,
1565
			null,
1566
			!empty($this->GetEcn())
1567
		))->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>');
1568

    
1569
		$group->add(new Form_Checkbox(
1570
			'codel',
1571
			null,
1572
			null,
1573
			!empty($this->GetCodel())
1574
		))->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>');
1575

    
1576
		$group->setHelp('Select options for this queue');
1577

    
1578
		$section->add($group);
1579

    
1580
		$section->addInput(new Form_Input(
1581
			'description',
1582
			'Description',
1583
			'text',
1584
			$this->GetDescription()
1585
		));
1586

    
1587
		$sform->add($section);
1588

    
1589
		$sform->addGlobal(new Form_Input(
1590
			'interface',
1591
			null,
1592
			'hidden',
1593
			$this->GetInterface()
1594
		));
1595

    
1596
		$sform->addGlobal(new Form_Input(
1597
			'name',
1598
			null,
1599
			'hidden',
1600
			$this->GetQname()
1601
		));
1602

    
1603
		return($sform);
1604
	}
1605

    
1606
	function build_shortform() {
1607
		/* XXX: Hacks in sight. Mostly layer violations!  */
1608
		global $g, $altq_list_queues;
1609
		global $shaperIFlist;
1610

    
1611
		$altq =& $altq_list_queues[$this->GetInterface()];
1612

    
1613
		if ($altq) {
1614
			$scheduler = $altq->GetScheduler();
1615
		}
1616

    
1617
		$form = '<dl class="dl-horizontal">';
1618
		$form .= '	<dt>';
1619
		$form .= '		<a href="firewall_shaper.php?interface=' . $this->GetInterface() . '&amp;queue=' . $this->GetQname() . '&amp;action=show">' . $shaperIFlist[$this->GetInterface()] . '</a>';
1620
		$form .= '	</dt>';
1621
		$form .= '	<dd>';
1622
		$form .=		$scheduler;
1623
		$form .= '	</dd>';
1624

    
1625
		$form .= '	<dt>';
1626
		$form .=		'Bandwidth';
1627
		$form .= '	</dt>';
1628
		$form .= '	<dd>';
1629
		$form .=		$this->GetBandwidth() . '&nbsp;' . $this->GetBwscaleText();
1630
		$form .= '	</dd>';
1631

    
1632
		$tmpvalue = $this->GetQpriority();
1633
		if (!empty($tmpvalue)) {
1634
			$form .= '	<dt>';
1635
			$form .=		'Priority';
1636
			$form .= '	<dt>';
1637
			$form .= '	<dd>';
1638
			$form .=		'On';
1639
			$form .= '	</dd>';
1640
		}
1641

    
1642
		$tmpvalue = $this->GetDefault();
1643
		if (!empty($tmpvalue)) {
1644
			$form .= '	<dt>';
1645
			$form .=		'Default';
1646
			$form .= '	<dt>';
1647
			$form .= '	<dd>';
1648
			$form .=		'On';
1649
			$form .= '	</dd>';
1650
		}
1651

    
1652
			$form .= '	<dt>';
1653
			$form .= 'Delete';
1654
			$form .= '	<dt>';
1655
			$form .= '	<dd>';
1656

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

    
1663
			$form .= '	</dd>';
1664

    
1665
			$form .= '</dl>';
1666

    
1667
		return $form;
1668

    
1669
	}
1670

    
1671
	function update_altq_queue_data(&$q) {
1672
		$this->ReadConfig($q);
1673
	}
1674

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

    
1721
class hfsc_queue extends priq_queue {
1722
	/* realtime */
1723
	var $realtime;
1724
	var $r_m1;
1725
	var $r_d;
1726
	var $r_m2;
1727
	/* linkshare */
1728
	var $linkshare;
1729
	var $l_m1;
1730
	var $l_d;
1731
	var $l_m2;
1732
	/* upperlimit */
1733
	var $upperlimit;
1734
	var $u_m1;
1735
	var $u_d;
1736
	var $u_m2;
1737

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

    
1826
	function &add_queue($interface, &$qname, &$path, &$input_errors) {
1827

    
1828
		if (!is_array($this->subqueues)) {
1829
			$this->subqueues = array();
1830
		}
1831
		$__tmp_q = new hfsc_queue(); $q =& $__tmp_q;
1832
		$q->SetInterface($this->GetInterface());
1833
		$q->SetParent($this);
1834
		$q->ReadConfig($qname);
1835
		$q->validate_input($qname, $input_errors);
1836

    
1837
		$q->SetEnabled("on");
1838
		$q->SetLink($path);
1839

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

    
1850
		return $q;
1851
	}
1852

    
1853
	function copy_queue($interface, &$cflink) {
1854

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

    
1946
		if (is_array($this->subqueues)) {
1947
			$cflinkp['queue'] = array();
1948
			foreach ($this->subqueues as $q) {
1949
				$cflink['queue'][$q->GetQname()] = array();
1950
				$q->copy_queue($interface, $cflink['queue'][$q->GetQname()]);
1951
			}
1952
		}
1953
	}
1954

    
1955
	function delete_queue() {
1956
		unref_on_altq_queue_list($this->GetQname());
1957
		cleanup_queue_from_rules($this->GetQname());
1958
		$parent =& $this->GetParent();
1959
		foreach ($this->subqueues as $q)
1960
			$q->delete_queue();
1961
		unset_object_by_reference($this->GetLink());
1962
	}
1963

    
1964
	/*
1965
	 * Should search even its children
1966
	 */
1967
	function &find_queue($interface, $qname) {
1968
		if ($qname == $this->GetQname()) {
1969
			return $this;
1970
		}
1971

    
1972
		foreach ($this->subqueues as $q) {
1973
			$result =& $q->find_queue("", $qname);
1974
			if ($result) {
1975
				return $result;
1976
			}
1977
		}
1978
	}
1979

    
1980
	function &find_parentqueue($interface, $qname) {
1981
		if ($this->subqueues[$qname]) {
1982
			return $this;
1983
		}
1984
		foreach ($this->subqueues as $q) {
1985
			$result = $q->find_parentqueue("", $qname);
1986
			if ($result) {
1987
				return $result;
1988
			}
1989
		}
1990
	}
1991

    
1992
	function validate_input($data, &$input_errors) {
1993
		parent::validate_input($data, $input_errors);
1994

    
1995
		$reqdfields[] = "bandwidth";
1996
		$reqdfieldsn[] = gettext("Bandwidth");
1997
		$reqdfields[] = "bandwidthtype";
1998
		$reqdfieldsn[] = gettext("Bandwidthtype");
1999

    
2000
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
2001

    
2002
		if (isset($data['linkshare3']) && $data['linkshare3'] <> "") {
2003
			if ($data['bandwidth'] && (!is_numeric($data['bandwidth']))) {
2004
				$input_errors[] = gettext("Bandwidth must be an integer.");
2005
			}
2006

    
2007
			if ($data['bandwidth'] < 0) {
2008
				$input_errors[] = gettext("Bandwidth cannot be negative.");
2009
			}
2010

    
2011
			if ($data['bandwidthtype'] == "%") {
2012
				if ($data['bandwidth'] > 100 || $data['bandwidth'] < 0) {
2013
					$input_errors[] = gettext("Bandwidth in percentage should be between 1 and 100.");
2014
				}
2015
			}
2016
		}
2017

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

    
2034
		/*
2035
		if (isset($data['upperlimit']) && $data['upperlimit3'] <> "" && $data['upperlimit1'] <> "") {
2036
			$bw_1 = get_hfsc_bandwidth($this, $data['upperlimit1']);
2037
			$bw_2 = get_hfsc_bandwidth($this, $data['upperlimit3']);
2038
			if (floatval($bw_1) < floatval($bw_2)) {
2039
				$input_errors[] = ("upperlimit m1 cannot be smaller than m2");
2040
			}
2041

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

    
2069
		/*
2070
		if (isset($data['linkshare']) && $data['linkshare3'] <> "" && $data['linkshare1'] <> "" && 0) {
2071
			$bw_1 = get_hfsc_bandwidth($this, $data['linkshare1']);
2072
			$bw_2 = get_hfsc_bandwidth($this, $data['linkshare3']);
2073
			if (floatval($bw_1) < floatval($bw_2)) {
2074
				$input_errors[] = ("linkshare m1 cannot be smaller than m2");
2075
			}
2076

    
2077
			if (get_interface_bandwidth($this) < (0.8 * (floatval($bw_1) + floatval($bw_2)))) {
2078
				$input_errors[] = ("linkshare specification exceeds 80% of allowable allocation.");
2079
			}
2080
		}
2081
		*/
2082

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

    
2093
		/*
2094
		if (isset($data['realtime']) && $data['realtime3'] <> "" && $data['realtime1'] <> "" && 0) {
2095
			$bw_1 = get_hfsc_bandwidth($this, $data['realtime1']);
2096
			$bw_2 = get_hfsc_bandwidth($this, $data['realtime3']);
2097
			if (floatval($bw_1) < floatval($bw_2)) {
2098
				$input_errors[] = ("realtime m1 cannot be smaller than m2");
2099
			}
2100

    
2101
			if (get_interface_bandwidth($this) < (0.8 * (floatval($bw_1) + floatval($bw_2)))) {
2102
				$input_errors[] = ("realtime specification exceeds 80% of allowable allocation.");
2103
			}
2104
		}
2105
		*/
2106
	}
2107

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

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

    
2182
	/* Even this should take children into consideration */
2183
	function build_rules(&$default = false) {
2184

    
2185
		$pfq_rule = " queue ". $this->qname;
2186
		if ($this->GetInterface()) {
2187
			$pfq_rule .= " on ".get_real_interface($this->GetInterface());
2188
		}
2189
		if ($this->GetBandwidth() && $this->GetBwscale()) {
2190
			$pfq_rule .= " bandwidth ".trim($this->GetBandwidth()).$this->GetBwscale();
2191
		}
2192

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

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

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

    
2290
		$pfq_rule .= " \n";
2291

    
2292
		return $pfq_rule;
2293
	}
2294

    
2295
	function build_javascript() {
2296

    
2297
		$javascript = <<<EOJS
2298
<script type="text/javascript">
2299
//<![CDATA[
2300
	events.push(function(){
2301

    
2302
		// Disables the specified input element
2303
		function disableInput(id, disable) {
2304
			$('#' + id).prop("disabled", disable);
2305
		}
2306

    
2307
		// Upperlimit
2308
		function enable_upperlimit() {
2309
			disableInput('upperlimit1', !$('#upperlimit').prop('checked'));
2310
			disableInput('upperlimit2', !$('#upperlimit').prop('checked'));
2311
			disableInput('upperlimit3', !$('#upperlimit').prop('checked'));
2312
		}
2313

    
2314
		$('#upperlimit').click(function () {
2315
			enable_upperlimit();
2316
		});
2317

    
2318
		enable_upperlimit();
2319

    
2320
		// realtime
2321
		function enable_realtime() {
2322
			disableInput('realtime1', !$('#realtime').prop('checked'));
2323
			disableInput('realtime2', !$('#realtime').prop('checked'));
2324
			disableInput('realtime3', !$('#realtime').prop('checked'));
2325
		}
2326

    
2327
		$('#realtime').click(function () {
2328
			enable_realtime();
2329
		});
2330

    
2331
		enable_realtime();
2332

    
2333
		// linkshare
2334
		function enable_linkshare() {
2335
			disableInput('linkshare1', !$('#linkshare').prop('checked'));
2336
			disableInput('linkshare2', !$('#linkshare').prop('checked'));
2337
			disableInput('linkshare3', !$('#linkshare').prop('checked'));
2338
		}
2339

    
2340
		$('#linkshare').click(function () {
2341
			enable_linkshare();
2342
		});
2343

    
2344
		enable_linkshare();
2345
	});
2346
//]]>
2347
</script>
2348
EOJS;
2349

    
2350
		return $javascript;
2351
	}
2352

    
2353
	function build_form() {
2354

    
2355
		$sform = parent::build_form();
2356

    
2357
		$section = new Form_Section('Service Curve (sc)');
2358

    
2359
		$group = new Form_Group('Bandwidth');
2360

    
2361
		$group->add(new Form_Input(
2362
			'bandwidth',
2363
			null,
2364
			'number',
2365
			$this->GetBandwidth(),
2366
			['step' => 'any', 'min' => '0.000']
2367
		));
2368

    
2369
		$group->add(new Form_Select(
2370
			'bandwidthtype',
2371
			null,
2372
			$this->GetBwscale(),
2373
			array('Kb' => 'Kbit/s',
2374
				  'Mb' => 'Mbit/s',
2375
				  'Gb' => 'Gbit/s',
2376
				  'b' => 'Bit/s',
2377
				  '%' => '%')
2378
		));
2379

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

    
2382
		$section->add($group);
2383

    
2384
		$group = new Form_Group('Max bandwidth for queue.');
2385

    
2386
		$group->add(new Form_Checkbox(
2387
			'upperlimit',
2388
			null,
2389
			'Upper Limit',
2390
			($this->GetUpperlimit()<> "")
2391
		));
2392

    
2393
		$group->add(new Form_Input(
2394
			'upperlimit1',
2395
			null,
2396
			'text',
2397
			$this->GetU_m1()
2398
		))->setHelp('m1');
2399

    
2400
		$group->add(new Form_Input(
2401
			'upperlimit2',
2402
			null,
2403
			'text',
2404
			$this->GetU_d()
2405
		))->setHelp('d');
2406

    
2407
		$group->add(new Form_Input(
2408
			'upperlimit3',
2409
			null,
2410
			'text',
2411
			$this->GetU_m2()
2412
		))->setHelp('m2');
2413

    
2414

    
2415
		$section->add($group);
2416

    
2417
		$group = new Form_Group('Min bandwidth for queue.');
2418

    
2419
		$group->add(new Form_Checkbox(
2420
			'realtime',
2421
			null,
2422
			'Real Time',
2423
			($this->GetRealtime()<> "")
2424
		));
2425

    
2426
		$group->add(new Form_Input(
2427
			'realtime1',
2428
			null,
2429
			'text',
2430
			$this->GetR_m1()
2431
		))->setHelp('m1');
2432

    
2433
		$group->add(new Form_Input(
2434
			'realtime2',
2435
			null,
2436
			'text',
2437
			$this->GetR_d()
2438
		))->setHelp('d');
2439

    
2440
		$group->add(new Form_Input(
2441
			'realtime3',
2442
			null,
2443
			'text',
2444
			$this->GetR_m2()
2445
		))->setHelp('m2');
2446

    
2447
		$section->add($group);
2448

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

    
2451
		$group->add(new Form_Checkbox(
2452
			'linkshare',
2453
			null,
2454
			'Link Share',
2455
			($this->GetLinkshare()<> "")
2456
		));
2457

    
2458
		$group->add(new Form_Input(
2459
			'linkshare1',
2460
			null,
2461
			'text',
2462
			$this->GetL_m1()
2463
		))->setHelp('m1');
2464

    
2465
		$group->add(new Form_Input(
2466
			'linkshare2',
2467
			null,
2468
			'text',
2469
			$this->GetL_d()
2470
		))->setHelp('d');
2471

    
2472
		$group->add(new Form_Input(
2473
			'linkshare3',
2474
			null,
2475
			'text',
2476
			$this->GetL_m2()
2477
		))->setHelp('m2');
2478

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

    
2485
		$section->add($group);
2486

    
2487
		$sform->add($section);
2488

    
2489
		return($sform);
2490
	}
2491

    
2492
	function update_altq_queue_data(&$data) {
2493
		$this->ReadConfig($data);
2494
	}
2495

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

    
2613
class cbq_queue extends priq_queue {
2614
	var $qborrow = "";
2615

    
2616
	function GetBorrow() {
2617
		return $this->qborrow;
2618
	}
2619
	function SetBorrow($borrow) {
2620
		$this->qborrow = $borrow;
2621
	}
2622
	function CanHaveChildren() {
2623
		return true;
2624
	}
2625

    
2626
	function &add_queue($interface, &$qname, &$path, &$input_errors) {
2627

    
2628
		if (!is_array($this->subqueues)) {
2629
			$this->subqueues = array();
2630
		}
2631
		$__tmp_q = new cbq_queue(); $q =& $__tmp_q;
2632
		$q->SetInterface($this->GetInterface());
2633
		$q->SetParent($this);
2634
		$q->ReadConfig($qname);
2635
		$q->validate_input($qname, $input_errors);
2636

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

    
2649
		return $q;
2650
	}
2651

    
2652
	function copy_queue($interface, &$cflink) {
2653

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

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

    
2718
	function &find_parentqueue($interface, $qname) {
2719
		if ($this->subqueues[$qname]) {
2720
			return $this;
2721
		}
2722
		foreach ($this->subqueues as $q) {
2723
			$result = $q->find_parentqueue("", $qname);
2724
			if ($result) {
2725
				return $result;
2726
			}
2727
		}
2728
	}
2729

    
2730
	function delete_queue() {
2731
		unref_on_altq_queue_list($this->GetQname());
2732
		cleanup_queue_from_rules($this->GetQname());
2733
		foreach ($this->subqueues as $q)
2734
			$q->delete_queue();
2735
		unset_object_by_reference($this->GetLink());
2736
	}
2737

    
2738
	function validate_input($data, &$input_errors) {
2739
		parent::validate_input($data, $input_errors);
2740

    
2741
		if ($data['priority'] > 7) {
2742
				$input_errors[] = gettext("Priority must be an integer between 0 and 7.");
2743
		}
2744
		$reqdfields[] = "bandwidth";
2745
		$reqdfieldsn[] = gettext("Bandwidth");
2746
		$reqdfields[] = "bandwidthtype";
2747
		$reqdfieldsn[] = gettext("Bandwidthtype");
2748

    
2749
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
2750
	}
2751

    
2752
	function ReadConfig(&$q) {
2753
		parent::ReadConfig($q);
2754
		if (!empty($q['borrow'])) {
2755
			$this->SetBorrow("on");
2756
		} else {
2757
			$this->SetBorrow("");
2758
		}
2759
	}
2760

    
2761
	function build_javascript() {
2762
		return parent::build_javascript();
2763
	}
2764

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

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

    
2864
		$pfq_rule .= " \n";
2865
		return $pfq_rule;
2866
	}
2867

    
2868
	function build_form() {
2869
		$sform = parent::build_form();
2870

    
2871
		$section = new Form_Section('NOTITLE');
2872

    
2873
		$group = new Form_Group('Bandwidth');
2874

    
2875
		$group->add(new Form_Input(
2876
			'bandwidth',
2877
			null,
2878
			'number',
2879
			$this->GetBandwidth()
2880
		));
2881

    
2882
		$group->add(new Form_Select(
2883
			'bandwidthtype',
2884
			null,
2885
			$this->GetBwscale(),
2886
			array('Kb' => 'Kbit/s',
2887
				  'Mb' => 'Mbit/s',
2888
				  'Gb' => 'Gbit/s',
2889
				  'b' => 'Bit/s',
2890
				  '%' => '%')
2891
		));
2892

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

    
2895
		$section->add($group);
2896

    
2897
		$section->addInput(new Form_Checkbox(
2898
			'borrow',
2899
			'Scheduler option',
2900
			'Borrow from other queues when available',
2901
			($this->GetBorrow() == "on")
2902
		));
2903

    
2904
		$sform->add($section);
2905

    
2906
		return $sform;
2907
	}
2908

    
2909
	function update_altq_queue_data(&$data) {
2910
		$this->ReadConfig($data);
2911
	}
2912

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

    
2965
class fairq_queue extends priq_queue {
2966
	var $hogs;
2967
	var $buckets;
2968

    
2969
	function GetBuckets() {
2970
		return $this->buckets;
2971
	}
2972
	function SetBuckets($buckets) {
2973
		$this->buckets = $buckets;
2974
	}
2975
	function GetHogs() {
2976
		return $this->hogs;
2977
	}
2978
	function SetHogs($hogs) {
2979
		$this->hogs = $hogs;
2980
	}
2981
	function CanHaveChildren() {
2982
		return false;
2983
	}
2984

    
2985

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

    
3003
	/*
3004
	 * Should search even its children
3005
	 */
3006
	function &find_queue($interface, $qname) {
3007
		if ($qname == $this->GetQname()) {
3008
			return $this;
3009
		}
3010
	}
3011

    
3012
	function find_parentqueue($interface, $qname) { return; }
3013

    
3014
	function delete_queue() {
3015
		unref_on_altq_queue_list($this->GetQname());
3016
		cleanup_queue_from_rules($this->GetQname());
3017
		unset_object_by_reference($this->GetLink());
3018
	}
3019

    
3020
	function validate_input($data, &$input_errors) {
3021
		parent::validate_input($data, $input_errors);
3022

    
3023
		if ($data['priority'] > 7) {
3024
				$input_errors[] = gettext("Priority must be an integer between 0 and 7.");
3025
		}
3026
		$reqdfields[] = "bandwidth";
3027
		$reqdfieldsn[] = gettext("Bandwidth");
3028
		$reqdfields[] = "bandwidthtype";
3029
		$reqdfieldsn[] = gettext("Bandwidthtype");
3030

    
3031
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
3032
	}
3033

    
3034
	function ReadConfig(&$q) {
3035
		parent::ReadConfig($q);
3036
		if (!empty($q['buckets'])) {
3037
			$this->SetBuckets($q['buckets']);
3038
		} else {
3039
			$this->SetBuckets("");
3040
		}
3041
		if (!empty($q['hogs']) && is_valid_shaperbw($q['hogs'])) {
3042
			$this->SetHogs($q['hogs']);
3043
		} else {
3044
			$this->SetHogs("");
3045
		}
3046
	}
3047

    
3048
	function build_javascript() {
3049
		return parent::build_javascript();
3050
	}
3051

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

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

    
3137
		$pfq_rule .= " \n";
3138
		return $pfq_rule;
3139
	}
3140

    
3141
	function build_form() {
3142
		$form = parent::build_form();
3143

    
3144
		$section = new Form_Section('');
3145

    
3146
		$group = new Form_Group('Bandwidth');
3147

    
3148
		$group->add(new Form_Input(
3149
			'bandwidth',
3150
			null,
3151
			'number',
3152
			$this->GetBandwidth()
3153
		));
3154

    
3155
		$group->add(new Form_Select(
3156
			'bandwidthtype',
3157
			null,
3158
			$this->GetBwscale(),
3159
			array('Kb' => 'Kbit/s',
3160
				  'Mb' => 'Mbit/s',
3161
				  'Gb' => 'Gbit/s',
3162
				  'b' => 'Bit/s',
3163
				  '%' => '%')
3164
		));
3165

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

    
3168
		$section->add($group);
3169

    
3170
		$section->addInput(new Form_Input(
3171
			'buckets',
3172
			'Scheduler specific options',
3173
			'text',
3174
			$this->GetBuckets()
3175
		))->setHelp('Number of buckets available');
3176

    
3177
		$section->addInput(new Form_Input(
3178
			'hogs',
3179
			'',
3180
			'text',
3181
			$this->GetHogs()
3182
			))->setHelp('Bandwidth limit for hosts to not saturate link');
3183

    
3184
		$form->add($section);
3185
		return $form;
3186
	}
3187

    
3188
	function update_altq_queue_data(&$data) {
3189
		$this->ReadConfig($data);
3190
	}
3191

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

    
3248

    
3249
/*
3250
 * dummynet(4) wrappers.
3251
 */
3252

    
3253

    
3254
/*
3255
 * List of respective objects!
3256
 */
3257
$dummynet_pipe_list = array();
3258

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

    
3269
	var $buckets;
3270
	/* mask parameters */
3271
	var $mask;
3272
	var $noerror;
3273

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

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

    
3370
	function validate_input($data, &$input_errors) {
3371
		$reqdfields[] = "bandwidth";
3372
		$reqdfieldsn[] = gettext("Bandwidth");
3373
		/*$reqdfields[] = "burst";
3374
		$reqdfieldsn[] = gettext("Burst"); */
3375
		$reqdfields[] = "bandwidthtype";
3376
		$reqdfieldsn[] = gettext("Bandwidthtype");
3377
		$reqdfields[] = "newname";
3378
		$reqdfieldsn[] = gettext("Name");
3379

    
3380
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
3381

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

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

    
3448
}
3449

    
3450
class dnpipe_class extends dummynet_class {
3451
	var $delay;
3452
	var $qbandwidth = array();
3453
	var $qbandwidthtype;
3454

    
3455
		/* This is here to help on form building and building rules/lists */
3456
	var $subqueues = array();
3457

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

    
3488
	function &add_queue($interface, &$queue, &$path, &$input_errors) {
3489

    
3490
		if (!is_array($this->subqueues)) {
3491
			$this->subqueues = array();
3492
		}
3493

    
3494
		$__tmp_q = new dnqueue_class(); $q =& $__tmp_q;
3495
		$q->SetLink($path);
3496
		$q->SetEnabled("on");
3497
		$q->SetPipe($this->GetQname());
3498
		$q->SetParent($this);
3499
		$q->ReadConfig($queue);
3500
		$q->validate_input($queue, $input_errors);
3501
		if (count($input_errors)) {
3502
			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)));
3503
			return $q;
3504
		}
3505
		$number = dnqueue_find_nextnumber();
3506
		$q->SetNumber($number);
3507
		$this->subqueues[$q->GetQname()] = &$q;
3508

    
3509
		return $q;
3510
	}
3511

    
3512
	function &get_queue_list(&$q = null) {
3513
		$qlist = array();
3514

    
3515
		$qlist[$this->GetQname()] = $this->GetNumber();
3516
		if (is_array($this->subqueues)) {
3517
			foreach ($this->subqueues as $queue) {
3518
				$queue->get_queue_list($qlist);
3519
			}
3520
		}
3521
		return $qlist;
3522
	}
3523

    
3524
	/*
3525
	 * Should search even its children
3526
	 */
3527
	function &find_queue($pipe, $qname) {
3528
		if ($qname == $this->GetQname()) {
3529
			return $this;
3530
		}
3531
		foreach ($this->subqueues as $q) {
3532
			$result =& $q->find_queue("", $qname);
3533
			if ($result) {
3534
				return $result;
3535
			}
3536
		}
3537
	}
3538

    
3539
	function &find_parentqueue($pipe, $qname) {
3540
		return NULL;
3541
	}
3542

    
3543
	function validate_input($data, &$input_errors) {
3544
		parent::validate_input($data, $input_errors);
3545

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

    
3582
	function ReadConfig(&$q) {
3583
		if (!empty($q['name']) && !empty($q['newname']) && $q['name'] != $q['newname']) {
3584
			$this->SetQname($q['newname']);
3585
		} else if (!empty($q['newname'])) {
3586
			$this->SetQname($q['newname']);
3587
		} else {
3588
			$this->SetQname($q['name']);
3589
		}
3590
		$this->SetNumber($q['number']);
3591

    
3592
		if (!empty($_POST)) {
3593
			$bandwidth = array();
3594
			/* XXX: Really no better way? */
3595
			for ($i = 0; $i < 2900; $i++) {
3596
				if (isset($q["bandwidth{$i}"]) && $q["bandwidth{$i}"] <> "") {
3597
					$bw = array();
3598
					$bw['bw'] = $q["bandwidth{$i}"];
3599
					$bw['burst'] = $q["burst{$i}"];
3600
					if (isset($q["bwtype{$i}"]) && $q["bwtype{$i}"]) {
3601
						$bw['bwscale'] = $q["bwtype{$i}"];
3602
					}
3603
					if (isset($q["bwsched{$i}"]) && $q["bwsched{$i}"]) {
3604
						$bw['bwsched'] = $q["bwsched{$i}"];
3605
					}
3606
					$bandwidth[] = $bw;
3607
				}
3608
			}
3609
			$this->SetBandwidth($bandwidth);
3610
		}
3611

    
3612
		if (is_array($q['bandwidth']) && is_array($q['bandwidth']['item'])) {
3613
			$this->SetBandwidth($q['bandwidth']['item']);
3614
			$this->SetBurst($q['burst']['item']);
3615
		}
3616

    
3617
		if (isset($q['qlimit']) && $q['qlimit'] <> "") {
3618
			$this->SetQlimit($q['qlimit']);
3619
		} else {
3620
			$this->SetQlimit("");
3621
		}
3622
		if (isset($q['mask']) && $q['mask'] <> "") {
3623
			$masktype = $q['mask'];
3624
		} else {
3625
			$masktype = "";
3626
		}
3627
		if (isset($q['maskbits']) && $q['maskbits'] <> "") {
3628
			$maskbits = $q['maskbits'];
3629
		} else {
3630
			$maskbits = "";
3631
		}
3632
		if (isset($q['maskbitsv6']) && $q['maskbitsv6'] <> "") {
3633
			$maskbitsv6 = $q['maskbitsv6'];
3634
		} else {
3635
			$maskbitsv6 = "";
3636
		}
3637
		$this->SetMask(array("type" => $masktype, "bits" => $maskbits, "bitsv6" => $maskbitsv6));
3638
		if (isset($q['buckets']) && $q['buckets'] <> "") {
3639
			$this->SetBuckets($q['buckets']);
3640
		} else {
3641
			$this->SetBuckets("");
3642
		}
3643
		if (isset($q['plr']) && $q['plr'] <> "") {
3644
			$this->SetPlr($q['plr']);
3645
		} else {
3646
			$this->SetPlr("");
3647
		}
3648
		if (isset($q['delay']) && $q['delay'] <> "") {
3649
			$this->SetDelay($q['delay']);
3650
		} else {
3651
			$this->SetDelay(0);
3652
		}
3653
		if (isset($q['description']) && $q['description'] <> "") {
3654
			$this->SetDescription($q['description']);
3655
		} else {
3656
			$this->SetDescription("");
3657
		}
3658
		$this->SetEnabled($q['enabled']);
3659

    
3660
	}
3661

    
3662
	function build_tree() {
3663
		$tree = " <li><a href=\"firewall_shaper_vinterface.php?pipe=" . $this->GetQname() ."&amp;queue=".$this->GetQname() ."&amp;action=show\">";
3664
		$tree .= $this->GetQname() . "</a>";
3665
		if (is_array($this->subqueues)) {
3666
			$tree .= "<ul>";
3667
			foreach ($this->subqueues as $q) {
3668
				$tree .= $q->build_tree();
3669
			}
3670
			$tree .= "</ul>";
3671
		}
3672
		$tree .= "</li>";
3673

    
3674
		return $tree;
3675
	}
3676

    
3677
	function build_rules() {
3678
		global $config, $time_based_rules;
3679

    
3680
		if ($this->GetEnabled() == "") {
3681
			return;
3682
		}
3683

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

    
3727
		if ($this->GetQlimit()) {
3728
			$pfq_rule .= " queue " . $this->GetQlimit();
3729
		}
3730
		if ($this->GetPlr()) {
3731
			$pfq_rule .= " plr " . $this->GetPlr();
3732
		}
3733
		if ($this->GetBuckets()) {
3734
			$pfq_rule .= " buckets " . $this->GetBuckets();
3735
		}
3736
		if ($this->GetDelay()) {
3737
			$pfq_rule .= " delay " . $this->GetDelay();
3738
		}
3739
		$this->build_mask_rules($pfq_rule);
3740

    
3741
		$pfq_rule .= "\n";
3742

    
3743
		if (!empty($this->subqueues) && count($this->subqueues) > 0) {
3744
			foreach ($this->subqueues as $q) {
3745
				$pfq_rule .= $q->build_rules();
3746
			}
3747
		}
3748
		$pfq_rule .= " \n";
3749

    
3750
		return $pfq_rule;
3751
	}
3752

    
3753
	function update_dn_data(&$data) {
3754
		$this->ReadConfig($data);
3755
	}
3756

    
3757
	function build_javascript() {
3758
		global $g, $config;
3759

    
3760
		$javasr = parent::build_javascript();
3761

    
3762
		//build list of schedules
3763
		$schedules = "<option value='none'>none</option>";
3764
		if (is_array($config['schedules']) && is_array($config['schedules']['schedule'])) {
3765
			foreach ($config['schedules']['schedule'] as $schedule) {
3766
				if ($schedule['name'] <> "") {
3767
					$schedules .= "<option value='{$schedule['name']}'>{$schedule['name']}</option>";
3768
				}
3769
			}
3770
		}
3771
		$bwopt = "";
3772
		foreach (array("Kb" => "Kbit/s", "Mb" => "Mbit/s", "Gb" => "Gbit/s", "b" => "Bit/s") as $bwidx => $bw) {
3773
			$bwopt .= "<option value='{$bwidx}'>{$bw}</option>";
3774
		}
3775

    
3776
		$javasr .= <<<EOD
3777
<script type='text/javascript'>
3778
//<![CDATA[
3779
var addBwRowTo = (function() {
3780

    
3781
	return (function (tableId) {
3782

    
3783
	var table = document.getElementById(tableId);
3784
	var totalrows = table.rows.length -1;
3785

    
3786
	var row = table.insertRow(totalrows + 1);
3787
	var cell1 = row.insertCell(0);
3788
	var cell2 = row.insertCell(1);
3789
	var cell3 = row.insertCell(2);
3790
	var cell4 = row.insertCell(3);
3791

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

    
3797
	});
3798
})();
3799

    
3800
function removeBwRow(el) {
3801
	var d = el.parentNode.parentNode.rowIndex;
3802
	document.getElementById('maintable').deleteRow(d);
3803
}
3804

    
3805
function ceil_func(el){
3806
	el.value = Math.ceil(el.value);
3807

    
3808
}
3809
//]]>
3810
</script>
3811

    
3812
EOD;
3813

    
3814
		return $javasr;
3815
	}
3816

    
3817
	// Compose a table of bandwidths that can then be inserted into the form using a Form_StaticText
3818
	// The table has been "Bootstrapped" to match the web design while maintaining compatibility with
3819
	// with the javascript in this class
3820
	function build_bwtable() {
3821
		global $config;
3822

    
3823
		$bandwidth = $this->GetBandwidth();
3824
				//build list of schedules
3825
		$schedules = array();
3826
		$schedules[] = "none";//leave none to leave rule enabled all the time
3827
		if (is_array($config['schedules']) && is_array($config['schedules']['schedule'])) {
3828
			foreach ($config['schedules']['schedule'] as $schedule) {
3829
				if ($schedule['name'] != "") {
3830
					$schedules[] = $schedule['name'];
3831
				}
3832
			}
3833
		}
3834

    
3835
		$form = '<div class="table-responsive">';
3836
		$form .= '<table id="maintable" class="table table-hover table-striped">';
3837
		$form .= "<thead><tr>";
3838
		$form .= "<th>Bandwidth</th>";
3839
		//$form .= "<td width='35%'><div id='fifthcolumn'>Burst</div></td>";
3840
		$form .= "<th>Bw type</th>";
3841
		$form .= "<th>Schedule</th>";
3842
		$form .= "<th></th>";
3843
		$form .= "</tr></thead>";
3844
		$form .= "<tbody>";
3845

    
3846
		// If there are no bandwidths defined, make a blank one for convenience
3847
		if (empty($bandwidth)) {
3848
			$bandwidth = array(0 => array('bw' => '', 'bwscale' => 'Kb', 'bwsched' => 'none'));
3849
		}
3850

    
3851
		if (is_array($bandwidth)) {
3852
			foreach ($bandwidth as $bwidx => $bw) {
3853
				$form .= '<tr>';
3854
				$form .= '<td class="col-xs-4">';
3855
				$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\"/>";
3856
				//$form .= "</td><td width='20%'>";
3857
				//$form .= "<input class='formfld unknown' size='10' type=\"text\" id=\"burst{$bwidx}\" name=\"burst{$bwidx}\" value=\"{$bw['burst']}\" />";
3858
				$form .= "</td>";
3859
				$form .= '<td class="col-xs-4">';
3860
				$form .= "<select id=\"bwtype{$bwidx}\" name=\"bwtype{$bwidx}\" class=\"form-control\">";
3861

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

    
3865
					if ($bw['bwscale'] == $bwsidx) {
3866
						$form .= " selected";
3867
					}
3868

    
3869
					$form .= ">{$bwscale}</option>";
3870
				}
3871

    
3872
				$form .= "</select>";
3873
				$form .= "</td>";
3874
				$form .= '<td class="col-xs-4">';
3875
				$form .= "<select id=\"bwsched{$bwidx}\" name=\"bwsched{$bwidx}\" class=\"form-control\">";
3876

    
3877
				foreach ($schedules as $schd) {
3878
					$selected = "";
3879
					if ($bw['bwsched'] == $schd) {
3880
						$selected = "selected";
3881
					}
3882

    
3883
					$form .= "<option value='{$schd}' {$selected}>{$schd}</option>";
3884
				}
3885

    
3886
				$form .= "</select>";
3887
				$form .= "</td>";
3888
				$form .= '<td>';
3889
				$form .= '<a class="btn btn-warning" onclick="removeBwRow(this); return false;"><i class="fa fa-trash icon-embed-btn"></i>' . gettext('Delete') . '</a>';
3890
				$form .= "</td></tr>";
3891
			}
3892
		}
3893
		$form .= "</tbody></table></div><br />";
3894

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

    
3899
		return($form);
3900
	}
3901

    
3902
	function build_form() {
3903
		global $g, $config, $pipe, $action, $qname;
3904

    
3905
		//build list of schedules
3906
		$schedules = array();
3907
		$schedules[] = "none";//leave none to leave rule enabled all the time
3908
		if (is_array($config['schedules']) && is_array($config['schedules']['schedule'])) {
3909
			foreach ($config['schedules']['schedule'] as $schedule) {
3910
				if ($schedule['name'] <> "") {
3911
					$schedules[] = $schedule['name'];
3912
				}
3913
			}
3914
		}
3915

    
3916

    
3917
		$sform = new Form();
3918
		$sform->setAction("firewall_shaper.php");
3919

    
3920
		$section = new Form_Section('Limiters');
3921

    
3922
		$section->addInput(new Form_Checkbox(
3923
			'enabled',
3924
			'Enable',
3925
			'Enable limiter and its children',
3926
			($this->GetEnabled() == "on"),
3927
			'on'
3928
		));
3929

    
3930
		$section->addInput(new Form_Input(
3931
			'newname',
3932
			'*Name',
3933
			'text',
3934
			$this->GetQname()
3935
		));
3936

    
3937
		$section->addInput(new Form_Input(
3938
			'name',
3939
			null,
3940
			'hidden',
3941
			$this->GetQname()
3942
		));
3943

    
3944
		if ($this->GetNumber() > 0) {
3945
			$section->addInput(new Form_Input(
3946
				'number',
3947
				null,
3948
				'hidden',
3949
				$this->GetNumber()
3950
			));
3951
		}
3952

    
3953
		$bandwidth = $this->GetBandwidth();
3954

    
3955
		if (is_array($bandwidth)) {
3956
				$section->addInput(new Form_StaticText(
3957
				'Bandwidth',
3958
				$this->build_bwtable()
3959
			));
3960
		}
3961

    
3962
		$mask = $this->GetMask();
3963

    
3964
		$section->addInput(new Form_Select(
3965
			'mask',
3966
			'Mask',
3967
			$mask['type'],
3968
			array('none' => gettext('None'), 'srcaddress' => gettext('Source addresses'), 'dstaddress' => gettext('Destination addresses'))
3969
		))->setHelp('If "source" or "destination" slots is chosen a dynamic pipe with the bandwidth, delay, packet loss ' .
3970
					'and queue size given above will be created for each source/destination IP address encountered, respectively. ' .
3971
					'This makes it possible to easily specify bandwidth limits per host.');
3972

    
3973
		$group = new Form_Group(null);
3974

    
3975
		$group->add(new Form_Select(
3976
			'maskbits',
3977
			null,
3978
			$mask['bits'],
3979
			array_combine(range(32, 1, -1), range(32, 1, -1))
3980
		))->setHelp('IPv4 mask bits%1$s%2$s', '<br />', '255.255.255.255/?');
3981

    
3982
		$group->add(new Form_Select(
3983
			'maskbitsv6',
3984
			null,
3985
			$mask['bitsv6'],
3986
			array_combine(range(128, 1, -1), range(128, 1, -1))
3987
		))->setHelp('IPv6 mask bits%1$s%2$s', '<br />', '<span style="font-family:consolas">ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/?</span>');
3988

    
3989
		$section->add($group);
3990

    
3991
		$section->addInput(new Form_Input(
3992
			'description',
3993
			'Description',
3994
			'text',
3995
			$this->GetDescription()
3996
		))->setHelp('A description may be entered here for administrative reference (not parsed).');
3997

    
3998
		$sform->add($section);
3999

    
4000
		$section = new Form_Section('Advanced Options');
4001

    
4002
		$section->addInput(new Form_Input(
4003
			'delay',
4004
			'Delay (ms)',
4005
			'text',
4006
			$this->GetDelay() > 0 ? $this->GetDelay():null
4007
		))->setHelp('In most cases, zero (0) should specified here (or leave the field empty).');
4008

    
4009
		$section->addInput(new Form_Input(
4010
			'plr',
4011
			'Packet Loss Rate',
4012
			'number',
4013
			$this->GetPlr(),
4014
			['step' => '0.001', 'min' => '0.000']
4015
		))->setHelp('In most cases, zero (0) should be specified here (or leave the field empty). ' .
4016
					'A value of 0.001 means one packet in 1000 gets dropped.');
4017

    
4018
		$section->addInput(new Form_Input(
4019
			'qlimit',
4020
			'Queue size (slots)',
4021
			'number',
4022
			$this->GetQlimit()
4023
		))->setHelp('In most cases, the field should be left empty. All packets in this pipe are placed into a fixed-size queue first, ' .
4024
					'then they are delayed by value specified in the Delay field, and then they are delivered to their destination.');
4025

    
4026
		$section->addInput(new Form_Input(
4027
			'buckets',
4028
			'Bucket size (slots)',
4029
			'number',
4030
			$this->GetBuckets()
4031
		))->setHelp('In most cases, this field should be left empty. It increases the hash size set.');
4032

    
4033
		$sform->add($section);
4034

    
4035
		return($sform);
4036
		}
4037

    
4038
	function wconfig() {
4039
		$cflink =& get_dn_reference_to_me_in_config($this->GetLink());
4040
		if (!is_array($cflink)) {
4041
			$cflink = array();
4042
		}
4043
		$cflink['name'] = $this->GetQname();
4044
		$cflink['number'] = $this->GetNumber();
4045
		$cflink['qlimit'] = $this->GetQlimit();
4046
		$cflink['plr'] = $this->GetPlr();
4047
		$cflink['description'] = $this->GetDescription();
4048

    
4049
		$bandwidth = $this->GetBandwidth();
4050
		if (is_array($bandwidth)) {
4051
			$cflink['bandwidth'] = array();
4052
			$cflink['bandwidth']['item'] = array();
4053
			foreach ($bandwidth as $bwidx => $bw) {
4054
				$cflink['bandwidth']['item'][] = $bw;
4055
			}
4056
		}
4057

    
4058
		$cflink['enabled'] = $this->GetEnabled();
4059
		$cflink['buckets'] = $this->GetBuckets();
4060
		$mask = $this->GetMask();
4061
		$cflink['mask'] = $mask['type'];
4062
		$cflink['maskbits'] = $mask['bits'];
4063
		$cflink['maskbitsv6'] = $mask['bitsv6'];
4064
		$cflink['delay'] = $this->GetDelay();
4065
	}
4066

    
4067
}
4068

    
4069
class dnqueue_class extends dummynet_class {
4070
	var $pipeparent;
4071
	var $weight;
4072

    
4073
	function GetWeight() {
4074
		return $this->weight;
4075
	}
4076
	function SetWeight($weight) {
4077
		$this->weight = $weight;
4078
	}
4079
	function GetPipe() {
4080
		return $this->pipeparent;
4081
	}
4082
	function SetPipe($pipe) {
4083
		$this->pipeparent = $pipe;
4084
	}
4085

    
4086
	/* Just a stub in case we ever try to call this from the frontend. */
4087
	function &add_queue($interface, &$queue, &$path, &$input_errors) {
4088
		return;
4089
	}
4090

    
4091
	function delete_queue() {
4092
		cleanup_dnqueue_from_rules($this->GetQname());
4093
		unset_dn_object_by_reference($this->GetLink());
4094
		@pfSense_ipfw_pipe("queue delete " . $this->GetNumber());
4095
	}
4096

    
4097
	function validate_input($data, &$input_errors) {
4098
		parent::validate_input($data, $input_errors);
4099

    
4100
		if ($data['weight'] && ((!is_numeric($data['weight'])) ||
4101
		    ($data['weight'] < 1 && $data['weight'] > 100))) {
4102
			$input_errors[] = gettext("Weight must be an integer between 1 and 100.");
4103
		}
4104
	}
4105

    
4106
	/*
4107
	 * Should search even its children
4108
	 */
4109
	function &find_queue($pipe, $qname) {
4110
		if ($qname == $this->GetQname()) {
4111
			return $this;
4112
		} else {
4113
			return NULL;
4114
		}
4115
	}
4116

    
4117
	function &find_parentqueue($pipe, $qname) {
4118
		return $this->qparent;
4119
	}
4120

    
4121
	function &get_queue_list(&$qlist) {
4122
		if ($this->GetEnabled() == "") {
4123
			return;
4124
		}
4125
		$qlist[$this->GetQname()] = "?" .$this->GetNumber();
4126
	}
4127

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

    
4181
	function build_tree() {
4182
		$parent =& $this->GetParent();
4183
		$tree = " <li><a href=\"firewall_shaper_vinterface.php?pipe=" . $parent->GetQname() ."&amp;queue=" . $this->GetQname() ."&amp;action=show\">";
4184
		$tree .= $this->GetQname() . "</a>";
4185
		$tree .= "</li>";
4186

    
4187
		return $tree;
4188
	}
4189

    
4190
	function build_rules() {
4191
		if ($this->GetEnabled() == "") {
4192
			return;
4193
		}
4194

    
4195
		$parent =& $this->GetParent();
4196
		$pfq_rule = "queue ". $this->GetNumber() . " config pipe " . $parent->GetNumber();
4197
		if ($this->GetQlimit()) {
4198
			$pfq_rule .= " queue " . $this->GetQlimit();
4199
		}
4200
		if ($this->GetWeight()) {
4201
			$pfq_rule .= " weight " . $this->GetWeight();
4202
		}
4203
		if ($this->GetBuckets()) {
4204
			$pfq_rule .= " buckets " . $this->GetBuckets();
4205
		}
4206
		$this->build_mask_rules($pfq_rule);
4207
		$pfq_rule .= "\n";
4208

    
4209
		return $pfq_rule;
4210
	}
4211

    
4212
	function build_javascript() {
4213
		return parent::build_javascript();
4214
	}
4215

    
4216
	function build_form() {
4217
		global $g, $config, $pipe, $action, $qname;
4218

    
4219
		//build list of schedules
4220
		$schedules = array();
4221
		$schedules[] = "none";//leave none to leave rule enabled all the time
4222
		if (is_array($config['schedules']) && is_array($config['schedules']['schedule'])) {
4223
			foreach ($config['schedules']['schedule'] as $schedule) {
4224
				if ($schedule['name'] <> "") {
4225
					$schedules[] = $schedule['name'];
4226
				}
4227
			}
4228
		}
4229

    
4230

    
4231
		$sform = new Form();
4232
		$sform->setAction("firewall_shaper.php");
4233
		$section = new Form_Section('Limiters');
4234

    
4235
		$section->addInput(new Form_Checkbox(
4236
			'enabled',
4237
			'Enable',
4238
			'Enable this queue',
4239
			($this->GetEnabled() == "on"),
4240
			'on'
4241
		));
4242

    
4243
		$section->addInput(new Form_Input(
4244
			'newname',
4245
			'*Name',
4246
			'text',
4247
			$this->GetQname()
4248
		));
4249

    
4250
		$section->addInput(new Form_Input(
4251
			'name',
4252
			null,
4253
			'hidden',
4254
			$this->GetQname()
4255
		));
4256

    
4257
		if ($this->GetNumber() > 0) {
4258
			$section->addInput(new Form_Input(
4259
				'number',
4260
				null,
4261
				'hidden',
4262
				$this->GetNumber()
4263
			));
4264
		}
4265

    
4266
		$mask = $this->GetMask();
4267

    
4268
		$section->addInput(new Form_Select(
4269
			'mask',
4270
			'Mask',
4271
			$mask['type'],
4272
			array('none' => gettext('None'), 'srcaddress' => gettext('Source addresses'), 'dstaddress' => gettext('Destination addresses'))
4273
		))->setHelp('If "source" or "destination" slots is chosen a dynamic pipe with the bandwidth, delay, packet loss ' .
4274
					'and queue size given above will be created for each source/destination IP address encountered, respectively. ' .
4275
					'This makes it possible to easily specify bandwidth limits per host.');
4276

    
4277
		$group = new Form_Group(null);
4278

    
4279
		$group->add(new Form_Select(
4280
			'maskbits',
4281
			null,
4282
			$mask['bits'],
4283
			array_combine(range(32, 1, -1), range(32, 1, -1))
4284
		))->setHelp('IPv4 mask bits%1$s%2$s', '<br />', '255.255.255.255/?');
4285

    
4286
		$group->add(new Form_Select(
4287
			'maskbitsv6',
4288
			null,
4289
			$mask['bitsv6'],
4290
			array_combine(range(128, 1, -1), range(128, 1, -1))
4291
		))->setHelp('IPv6 mask bits%1$s%2$s', '<br />', '<span style="font-family:consolas">ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/?</span>');
4292

    
4293
		$section->add($group);
4294

    
4295
		$section->addInput(new Form_Input(
4296
			'description',
4297
			'Description',
4298
			'text',
4299
			$this->GetDescription()
4300
		))->setHelp('A description may be entered here for administrative reference (not parsed).');
4301

    
4302
		$sform->add($section);
4303

    
4304
		$section = new Form_Section('Advanced Options');
4305

    
4306
		$section->addInput(new Form_Input(
4307
			'weight',
4308
			'Weight',
4309
			'number',
4310
			$this->GetWeight(),
4311
			['min' => '1', 'max' => '100']
4312
		))->setHelp('For queues under the same parent this specifies the share that a queue gets(values range from 1 to 100),' .
4313
					' it can be left blank otherwise.');
4314

    
4315
		$section->addInput(new Form_Input(
4316
			'plr',
4317
			'Packet Loss Rate',
4318
			'number',
4319
			$this->GetPlr(),
4320
			['step' => '0.001', 'min' => '0.000']
4321
		))->setHelp('In most cases, zero (0) should be specified here (or leave the field empty). ' .
4322
					'A value of 0.001 means one packet in 1000 gets dropped');
4323

    
4324
		$section->addInput(new Form_Input(
4325
			'qlimit',
4326
			'Queue size (slots)',
4327
			'number',
4328
			$this->GetQlimit()
4329
		))->setHelp('In most cases, the field should be left empty. All packets in this pipe are placed into a fixed-size queue first, ' .
4330
					'then they are delayed by value specified in the Delay field, and then they are delivered to their destination.');
4331

    
4332
		$section->addInput(new Form_Input(
4333
			'buckets',
4334
			'Bucket size (slots)',
4335
			'number',
4336
			$this->GetBuckets()
4337
		))->setHelp('In most cases, this field should be left empty. It increases the hash size set');
4338

    
4339
		$section->addInput(new Form_Input(
4340
			'pipe',
4341
			null,
4342
			'hidden',
4343
			$this->GetPipe()
4344
		));
4345

    
4346
		$sform->add($section);
4347

    
4348
		return($sform);
4349
	}
4350

    
4351
	function update_dn_data(&$data) {
4352
		$this->ReadConfig($data);
4353
	}
4354

    
4355
	function wconfig() {
4356
		$cflink =& get_dn_reference_to_me_in_config($this->GetLink());
4357
		if (!is_array($cflink)) {
4358
			$cflink = array();
4359
		}
4360
		$cflink['name'] = $this->GetQname();
4361
		$cflink['number'] = $this->GetNumber();
4362
		$cflink['qlimit'] = $this->GetQlimit();
4363
		$cflink['description'] = $this->GetDescription();
4364
		$cflink['weight'] = $this->GetWeight();
4365
		$cflink['enabled'] = $this->GetEnabled();
4366
		$cflink['buckets'] = $this->GetBuckets();
4367
		$mask = $this->GetMask();
4368
		$cflink['mask'] = $mask['type'];
4369
		$cflink['maskbits'] = $mask['bits'];
4370
		$cflink['maskbitsv6'] = $mask['bitsv6'];
4371
	}
4372
}
4373

    
4374
function get_dummynet_name_list() {
4375

    
4376
	$dn_name_list =& get_unique_dnqueue_list();
4377
	$dn_name = array();
4378
	if (is_array($dn_name_list)) {
4379
		foreach ($dn_name_list as $key => $value) {
4380
			$dn_name[] = $key;
4381
		}
4382
	}
4383

    
4384
	return $dn_name;
4385

    
4386
}
4387

    
4388
function get_altq_name_list() {
4389
	$altq_name_list =& get_unique_queue_list();
4390
	$altq_name = array();
4391
	if (is_array($altq_name_list)) {
4392
		foreach ($altq_name_list as $key => $aqobj) {
4393
			$altq_name[] = $key;
4394
		}
4395
	}
4396

    
4397
	return $altq_name;
4398
}
4399

    
4400
/*
4401
 * XXX: TODO Make a class shaper to hide all these functions
4402
 * from the global namespace.
4403
 */
4404

    
4405
/*
4406
 * This is a layer violation but for now there is no way
4407
 * I can find to properly do this with PHP.
4408
 */
4409
function altq_get_default_queue($interface) {
4410
	global $altq_list_queues;
4411

    
4412
	$altq_tmp = $altq_list_queues[$interface];
4413
	if ($altq_tmp) {
4414
		return $altq_tmp->GetDefaultQueuePresent();
4415
	} else {
4416
		return false;
4417
	}
4418
}
4419

    
4420
function altq_check_default_queues() {
4421
	global $altq_list_queues;
4422

    
4423
	$count = 0;
4424
	if (is_array($altq_list_queues)) {
4425
		foreach ($altq_list_queues as $altq) {
4426
			if ($altq->GetDefaultQueuePresent()) {
4427
				$count++;
4428
			}
4429
		}
4430
	}
4431
	else {
4432
		$count++;
4433
	}
4434

    
4435
	return 0;
4436
}
4437

    
4438
function &get_unique_queue_list() {
4439
	global $altq_list_queues;
4440

    
4441
	$qlist = array();
4442
	if (is_array($altq_list_queues)) {
4443
		foreach ($altq_list_queues as $altq) {
4444
			if ($altq->GetEnabled() == "") {
4445
				continue;
4446
			}
4447
			$tmplist =& $altq->get_queue_list();
4448
			foreach ($tmplist as $qname => $link) {
4449
				if ($link->GetEnabled() <> "") {
4450
					$qlist[$qname] = $link;
4451
				}
4452
			}
4453
		}
4454
	}
4455
	return $qlist;
4456
}
4457

    
4458
function &get_unique_dnqueue_list() {
4459
	global $dummynet_pipe_list;
4460

    
4461
	$qlist = array();
4462
	if (is_array($dummynet_pipe_list)) {
4463
		foreach ($dummynet_pipe_list as $dn) {
4464
			if ($dn->GetEnabled() == "") {
4465
				continue;
4466
			}
4467
			$tmplist =& $dn->get_queue_list();
4468
			foreach ($tmplist as $qname => $link) {
4469
				$qlist[$qname] = $link;
4470
			}
4471
		}
4472
	}
4473
	return $qlist;
4474
}
4475

    
4476
function ref_on_altq_queue_list($parent, $qname) {
4477
	if (isset($GLOBALS['queue_list'][$qname])) {
4478
		$GLOBALS['queue_list'][$qname]++;
4479
	} else {
4480
		$GLOBALS['queue_list'][$qname] = 1;
4481
	}
4482

    
4483
	unref_on_altq_queue_list($parent);
4484
}
4485

    
4486
function unref_on_altq_queue_list($qname) {
4487
	$GLOBALS['queue_list'][$qname]--;
4488
	if ($GLOBALS['queue_list'][$qname] <= 1) {
4489
		unset($GLOBALS['queue_list'][$qname]);
4490
	}
4491
}
4492

    
4493
function read_altq_config() {
4494
	global $altq_list_queues, $config;
4495
	$path = array();
4496

    
4497
	if (!is_array($config['shaper'])) {
4498
		$config['shaper'] = array();
4499
	}
4500
	if (!is_array($config['shaper']['queue'])) {
4501
		$config['shaper']['queue'] = array();
4502
	}
4503
	$a_int = &$config['shaper']['queue'];
4504

    
4505
	$altq_list_queues = array();
4506

    
4507
	if (!is_array($config['shaper']['queue'])) {
4508
		return;
4509
	}
4510

    
4511
	foreach ($a_int as $key => $conf) {
4512
		$int = $conf['interface'];
4513
		$__tmp_root = new altq_root_queue(); $root =& $__tmp_root;
4514
		$root->SetInterface($int);
4515
		$altq_list_queues[$root->GetInterface()] = &$root;
4516
		$root->ReadConfig($conf);
4517
		array_push($path, $key);
4518
		$root->SetLink($path);
4519
		if (is_array($conf['queue'])) {
4520
			foreach ($conf['queue'] as $key1 => $q) {
4521
				array_push($path, $key1);
4522
				/*
4523
				 * XXX: we completely ignore errors here but anyway we must have
4524
				 *	checked them before so no harm should be come from this.
4525
				 */
4526
				$root->add_queue($root->GetInterface(), $q, $path, $input_errors);
4527
				array_pop($path);
4528
			}
4529
		}
4530
		array_pop($path);
4531
	}
4532
}
4533

    
4534
function read_dummynet_config() {
4535
	global $dummynet_pipe_list, $config;
4536
	$path = array();
4537

    
4538
	if (!is_array($config['dnshaper'])) {
4539
		$config['dnshaper'] = array();
4540
	}
4541
	if (!is_array($config['dnshaper']['queue'])) {
4542
		$config['dnshaper']['queue'] = array();
4543
	}
4544
	$a_int = &$config['dnshaper']['queue'];
4545

    
4546
	$dummynet_pipe_list = array();
4547

    
4548
	if (!is_array($config['dnshaper']['queue']) ||
4549
	    !count($config['dnshaper']['queue'])) {
4550
		return;
4551
	}
4552

    
4553
	foreach ($a_int as $key => $conf) {
4554
		if (empty($conf['name'])) {
4555
			continue; /* XXX: grrrrrr at php */
4556
		}
4557
		$__tmp_root = new dnpipe_class(); $root =& $__tmp_root;
4558
		$root->ReadConfig($conf);
4559
		$dummynet_pipe_list[$root->GetQname()] = &$root;
4560
		array_push($path, $key);
4561
		$root->SetLink($path);
4562
		if (is_array($conf['queue'])) {
4563
			foreach ($conf['queue'] as $key1 => $q) {
4564
				array_push($path, $key1);
4565
				/*
4566
				 * XXX: we completely ignore errors here but anyway we must have
4567
				 *	checked them before so no harm should be come from this.
4568
				 */
4569
				$root->add_queue($root->GetQname(), $q, $path, $input_errors);
4570
				array_pop($path);
4571
			}
4572
		}
4573
		array_pop($path);
4574
	}
4575
}
4576

    
4577
function get_interface_list_to_show() {
4578
	global $altq_list_queues, $config;
4579
	global $shaperIFlist;
4580

    
4581
	$tree = "";
4582
	foreach ($shaperIFlist as $shif => $shDescr) {
4583
		if ($altq_list_queues[$shif]) {
4584
			continue;
4585
		} else {
4586
			if (!is_altq_capable(get_real_interface($shif))) {
4587
				continue;
4588
			}
4589
			$tree .= " <li><a href=\"firewall_shaper.php?interface=".$shif."&amp;action=add\">".$shDescr."</a></li>";
4590
		}
4591
	}
4592

    
4593
	return $tree;
4594
}
4595

    
4596
function filter_generate_altq_queues() {
4597
	global $altq_list_queues;
4598

    
4599
	read_altq_config();
4600

    
4601
	$altq_rules = "";
4602
	foreach ($altq_list_queues as $altq) {
4603
		$altq_rules .= $altq->build_rules();
4604
	}
4605

    
4606
	return $altq_rules;
4607
}
4608

    
4609
function dnqueue_find_nextnumber() {
4610
	global $dummynet_pipe_list;
4611

    
4612
	$dnused = array();
4613
	if (is_array($dummynet_pipe_list)) {
4614
		foreach ($dummynet_pipe_list as $dn) {
4615
			$tmplist =& $dn->get_queue_list();
4616
			foreach ($tmplist as $qname => $link) {
4617
				if ($link[0] == "?") {
4618
					$dnused[$qname] = substr($link, 1);
4619
				}
4620
			}
4621
		}
4622
	}
4623

    
4624
	sort($dnused, SORT_NUMERIC);
4625
	$dnnumber = 0;
4626
	$found = false;
4627
	foreach ($dnused as $dnnum) {
4628
		if (($dnnum - $dnnumber) > 1) {
4629
			$dnnumber = $dnnum - 1;
4630
			$found = true;
4631
			break;
4632
		} else {
4633
			$dnnumber = $dnnum;
4634
		}
4635
	}
4636

    
4637
	if ($found == false) {
4638
		$dnnumber++;
4639
	}
4640

    
4641
	unset($dnused, $dnnum, $found);
4642
	return $dnnumber;
4643
}
4644

    
4645
function dnpipe_find_nextnumber() {
4646
	global $dummynet_pipe_list;
4647

    
4648
	$dnused = array();
4649
	foreach ($dummynet_pipe_list as $dn) {
4650
		$dnused[] = $dn->GetNumber();
4651
	}
4652

    
4653
	sort($dnused, SORT_NUMERIC);
4654
	$dnnumber = 0;
4655
	$found = false;
4656
	foreach ($dnused as $dnnum) {
4657
		if (($dnnum - $dnnumber) > 1) {
4658
			$dnnumber = $dnnum - 1;
4659
			$found = true;
4660
			break;
4661
		} else {
4662
			$dnnumber = $dnnum;
4663
		}
4664
	}
4665

    
4666
	if ($found == false) {
4667
		$dnnumber++;
4668
	}
4669

    
4670
	unset($dnused, $dnnum, $found);
4671
	return $dnnumber;
4672
}
4673

    
4674
function filter_generate_dummynet_rules() {
4675
	global $g, $dummynet_pipe_list;
4676

    
4677
	read_dummynet_config();
4678

    
4679
	$dn_rules = "";
4680
	$max_qlimit = "100"; // OS default
4681
	foreach ($dummynet_pipe_list as $dn) {
4682
		$dn_rules .= $dn->build_rules();
4683
		$this_qlimit = $dn->GetQlimit();
4684
		if ($this_qlimit > $max_qlimit) {
4685
			$max_qlimit = $this_qlimit;
4686
		}
4687
	}
4688
	if (!is_numericint($max_qlimit)) {
4689
		$max_qlimit = "100";
4690
	}
4691
	if (!empty($dn_rules)) {
4692
		if (!is_module_loaded("dummynet.ko")) {
4693
			mwexec("/sbin/kldload dummynet");
4694
		}
4695
		set_sysctl(array(
4696
				"net.inet.ip.dummynet.io_fast" => "1",
4697
				"net.inet.ip.dummynet.hash_size" => "256",
4698
				"net.inet.ip.dummynet.pipe_slot_limit" => $max_qlimit
4699
		));
4700
		file_put_contents("{$g['tmp_path']}/rules.limiter", $dn_rules);
4701
		mwexec("/sbin/ipfw {$g['tmp_path']}/rules.limiter");
4702
	}
4703
}
4704

    
4705
function build_iface_without_this_queue($iface, $qname) {
4706
	global $g, $altq_list_queues;
4707
	global $shaperIFlist;
4708

    
4709
	$altq =& $altq_list_queues[$iface];
4710

    
4711
	if ($altq) {
4712
		$scheduler = $altq->GetScheduler();
4713
	}
4714

    
4715
	$form = '<dl class="dl-horizontal">';
4716

    
4717
	$form .= '	<dt>';
4718
	$form .= '		<a href="firewall_shaper.php?interface=' . $iface . '&amp;queue=' . $iface . '&amp;action=show">' . $shaperIFlist[$iface] . '</a>';
4719
	$form .= '	</dt>';
4720
	$form .= '	<dd>';
4721
	$form .=		$scheduler;
4722
	$form .= '	</dd>';
4723

    
4724
	$form .= '	<dt>';
4725
	$form .= 'Clone';
4726
	$form .= '	</dt>';
4727
	$form .= '	<dd>';
4728
	$form .= '<a class="btn btn-info btn-xs" href="firewall_shaper_queues.php?interface=';
4729
	$form .= $iface . '&amp;queue=';
4730
	$form .= $qname . '&amp;action=add">';
4731
	$form .= '<i class="fa fa-clone icon-embed-btn"></i>';
4732
	$form .= gettext("Clone Shaper to this Interface") . '</a>';
4733
	$form .= '	</dd>';
4734

    
4735
	$form .= '</dl>';
4736

    
4737
	return $form;
4738

    
4739
}
4740

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

    
4744
$shaper_msg = gettext("The tree on the left navigates through the %s.");
4745
$default_shaper_msg .= sprintf($shaper_msg, gettext("queues")) . "<br />";
4746
$dn_default_shaper_msg .= sprintf($shaper_msg, gettext("limiters")) . "<br />";
4747

    
4748
$shaper_msg = gettext("Buttons at the bottom represent %s actions and are activated accordingly.");
4749
$default_shaper_msg .= sprintf($shaper_msg, gettext("queue"));
4750
$dn_default_shaper_msg .= sprintf($shaper_msg, gettext("limiter"));
4751

    
4752
// Check to see if the specified interface has a queue configured
4753
function interface_has_queue($if) {
4754
	global $config;
4755

    
4756
	if ($config['shaper']) {
4757
		foreach ($config['shaper']['queue'] as $queue) {
4758
			if ($queue['interface'] === $if) {
4759
				return true;
4760
			}
4761
		}
4762
	}
4763

    
4764
	return false;
4765
}
4766
?>
(47-47/60)