Project

General

Profile

Download (119 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
	shaper.inc
4
*/
5
/* ====================================================================
6
 *  Copyright (c)  2004-2015  Electric Sheep Fencing, LLC. All rights reserved.
7
 *
8
 *  Some or all of this file is based on the m0n0wall project which is
9
 *  Copyright (c)  2004 Manuel Kasper (BSD 2 clause)
10
 *
11
 *  Redistribution and use in source and binary forms, with or without modification,
12
 *  are permitted provided that the following conditions are met:
13
 *
14
 *  1. Redistributions of source code must retain the above copyright notice,
15
 *      this list of conditions and the following disclaimer.
16
 *
17
 *  2. Redistributions in binary form must reproduce the above copyright
18
 *      notice, this list of conditions and the following disclaimer in
19
 *      the documentation and/or other materials provided with the
20
 *      distribution.
21
 *
22
 *  3. All advertising materials mentioning features or use of this software
23
 *      must display the following acknowledgment:
24
 *      "This product includes software developed by the pfSense Project
25
 *       for use in the pfSense software distribution. (http://www.pfsense.org/).
26
 *
27
 *  4. The names "pfSense" and "pfSense Project" must not be used to
28
 *       endorse or promote products derived from this software without
29
 *       prior written permission. For written permission, please contact
30
 *       coreteam@pfsense.org.
31
 *
32
 *  5. Products derived from this software may not be called "pfSense"
33
 *      nor may "pfSense" appear in their names without prior written
34
 *      permission of the Electric Sheep Fencing, LLC.
35
 *
36
 *  6. Redistributions of any form whatsoever must retain the following
37
 *      acknowledgment:
38
 *
39
 *  "This product includes software developed by the pfSense Project
40
 *  for use in the pfSense software distribution (http://www.pfsense.org/).
41
 *
42
 *  THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY
43
 *  EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
44
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
45
 *  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR
46
 *  ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
47
 *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
48
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
49
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
50
 *  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
51
 *  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
52
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
53
 *  OF THE POSSIBILITY OF SUCH DAMAGE.
54
 *
55
 *  ====================================================================
56
 *
57
 */
58

    
59
/* XXX: needs some reducing on include. */
60
/* include all configuration functions. */
61
require_once("globals.inc");
62
require_once("functions.inc");
63
require_once("util.inc");
64
require_once("notices.inc");
65

    
66
/*
67
 * I admit :) this is derived from xmlparse.inc StartElement()
68
 */
69
function &get_reference_to_me_in_config(&$mypath) {
70
	global $config;
71

    
72
	$ptr =& $config['shaper'];
73
	foreach ($mypath as $indeks) {
74
		$ptr =& $ptr['queue'][$indeks];
75
	}
76

    
77
	return $ptr;
78
}
79

    
80
function unset_object_by_reference(&$mypath) {
81
	global $config;
82

    
83
	$ptr =& $config['shaper'];
84
	for ($i = 0; $i < count($mypath) - 1; $i++) {
85
		$ptr =& $ptr['queue'][$mypath[$i]];
86
	}
87
	unset($ptr['queue'][$mypath[$i]]);
88
}
89

    
90
function &get_dn_reference_to_me_in_config(&$mypath) {
91
	global $config;
92

    
93
	$ptr =& $config['dnshaper'];
94
	foreach ($mypath as $indeks) {
95
		$ptr =& $ptr['queue'][$indeks];
96
	}
97

    
98
	return $ptr;
99
}
100

    
101
function unset_dn_object_by_reference(&$mypath) {
102
	global $config;
103

    
104
	$ptr =& $config['dnshaper'];
105
	for ($i = 0; $i < count($mypath) - 1; $i++) {
106
		$ptr =& $ptr['queue'][$mypath[$i]];
107
	}
108
	unset($ptr['queue'][$mypath[$i]]);
109
}
110

    
111
function clean_child_queues($type, $mypath) {
112
	$ref = &get_reference_to_me_in_config($mypath);
113

    
114
	switch ($type) {
115
		case 'HFSC':
116
			if (isset($ref['borrow'])) {
117
				unset($ref['borrow']);
118
			}
119
			if (isset($ref['hogs'])) {
120
				unset($ref['hogs']);
121
			}
122
			if (isset($ref['buckets'])) {
123
				unset($ref['buckets']);
124
			}
125
			break;
126
		case 'PRIQ':
127
			if (isset($ref['borrow'])) {
128
				unset($ref['borrow']);
129
			}
130
			if (isset($ref['bandwidth'])) {
131
				unset($ref['bandwidth']);
132
			}
133
			if (isset($ref['bandwidthtype'])) {
134
				unset($ref['bandwidthtype']);
135
			}
136
			/* fall through */
137
		case 'FAIRQ':
138
			if (isset($ref['borrow'])) {
139
				unset($ref['borrow']);
140
			}
141
			/* fall through */
142
		case 'CBQ':
143
			if (isset($ref['realtime'])) {
144
				unset($ref['realtime']);
145
			}
146
			if (isset($ref['realtime1'])) {
147
				unset($ref['realtime1']);
148
			}
149
			if (isset($ref['realtime2'])) {
150
				unset($ref['realtime2']);
151
			}
152
			if (isset($ref['realtime3'])) {
153
				unset($ref['realtime3']);
154
			}
155
			if (isset($ref['upperlimit'])) {
156
				unset($ref['upperlimit']);
157
			}
158
			if (isset($ref['upperlimit1'])) {
159
				unset($ref['upperlimit1']);
160
			}
161
			if (isset($ref['upperlimit2'])) {
162
				unset($ref['upperlimit2']);
163
			}
164
			if (isset($ref['upperlimit3'])) {
165
				unset($ref['upperlimit3']);
166
			}
167
			if (isset($ref['linkshare'])) {
168
				unset($ref['linkshare']);
169
			}
170
			if (isset($ref['linkshare1'])) {
171
				unset($ref['linkshare1']);
172
			}
173
			if (isset($ref['linkshare2'])) {
174
				unset($ref['linkshare2']);
175
			}
176
			if (isset($ref['linkshare3'])) {
177
				unset($ref['linkshare3']);
178
			}
179
			if (isset($ref['hogs'])) {
180
				unset($ref['hogs']);
181
			}
182
			if (isset($ref['buckets'])) {
183
				unset($ref['buckets']);
184
			}
185
			break;
186
	}
187
}
188

    
189
function get_bandwidthtype_scale($type) {
190
	switch ($type) {
191
		case "Gb":
192
			$factor = 1024 * 1024 * 1024;
193
			break;
194
		case "Mb":
195
			$factor = 1024 * 1024;
196
			break;
197
		case "Kb":
198
			$factor = 1024;
199
			break;
200
		case "b":
201
		default:
202
			$factor = 1;
203
			break;
204
	}
205
	return intval($factor);
206
}
207

    
208
function get_hfsc_bandwidth($object, $bw) {
209
	$pattern= "/[0-9]+/";
210
	if (preg_match($pattern, $bw, $match)) {
211
		$bw_1 = $match[1];
212
	} else {
213
		return 0;
214
	}
215
	$pattern= "/(b|Kb|Mb|Gb|%)/";
216
	if (preg_match($pattern, $bw, $match)) {
217
		switch ($match[1]) {
218
			case '%':
219
				$bw_1 = $bw_1 / 100 * get_interface_bandwidth($object);
220
				break;
221
			default:
222
				$bw_1 = $bw_1 * get_bandwidthtype_scale($match[0]);
223
				break;
224
		}
225
		return floatval($bw_1);
226
	} else {
227
		return 0;
228
	}
229
}
230

    
231
function get_interface_bandwidth($object) {
232
	global $altq_list_queues;
233

    
234
	$int = $object->GetInterface();
235
	$altq =& $altq_list_queues[$int];
236
	if ($altq) {
237
		$bw_3 = $altq->GetBandwidth();
238
		$bw_3 = $bw_3 * get_bandwidthtype_scale($altq->GetBwscale());
239
		return floatval($bw_3);
240
	} else {
241
		return 0;
242
	}
243
}
244

    
245
/*
246
 * This is duplicated here since we cannot include guiconfig.inc.
247
 * Including it makes all stuff break.
248
 */
249
function shaper_do_input_validation($postdata, $reqdfields, $reqdfieldsn, $input_errors) {
250

    
251
	/* check for bad control characters */
252
	foreach ($postdata as $pn => $pd) {
253
		if (is_string($pd) && preg_match("/[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f]/", $pd)) {
254
			$input_errors[] = sprintf(gettext("The field '%s' contains invalid characters."), $pn);
255
		}
256
	}
257

    
258
	for ($i = 0; $i < count($reqdfields); $i++) {
259
		if ($postdata[$reqdfields[$i]] == "") {
260
			$input_errors[] = sprintf(gettext("The field '%s' is required."), $reqdfieldsn[$i]);
261
		}
262
	}
263
}
264

    
265
function cleanup_queue_from_rules($queue) {
266
	global $config;
267

    
268
	foreach ($config['filter']['rule'] as $rule) {
269
		if ($rule['defaultqueue'] == $queue) {
270
			unset($rule['defaultqueue']);
271
		}
272
		if ($rule['ackqueue'] == $queue) {
273
			unset($rule['ackqueue']);
274
		}
275
	}
276
}
277

    
278
function cleanup_dnqueue_from_rules($queue) {
279
	global $config;
280

    
281
	foreach ($config['filter']['rule'] as $rule) {
282
		if ($rule['dnpipe'] == $queue) {
283
			unset($rule['dnpipe']);
284
		}
285
		if ($rule['pdnpipe'] == $queue) {
286
			unset($rule['pdnpipe']);
287
		}
288
	}
289
}
290

    
291
class altq_root_queue {
292
	var $interface;
293
	var $tbrconfig ;
294
	var $bandwidth;
295
	var $bandwidthtype; /* b, Kb, Mb, Gb, % */
296
	var $scheduler;
297
	var $qlimit;
298
	var $queues = array();
299
	var $qenabled = false;
300
	var $link;
301
	var $available_bw; /* in b/s */
302

    
303
	/* Accessor functions */
304
	function GetAvailableBandwidth() {
305
		return $this->available_bw;
306
	}
307
	function SetAvailableBandwidth($bw) {
308
		$this->available_bw = $bw;
309
	}
310
	function GetDefaultQueuePresent() {
311
		if (!empty($this->queues)) {
312
			foreach ($this->queues as $q) {
313
				if ($q->GetDefault()) {
314
					return true;
315
				}
316
			}
317
		}
318

    
319
		return false;
320
	}
321
	function SetLink($link) {
322
		$this->link = $link;
323
	}
324
	function GetLink() {
325
		return $this->link;
326
	}
327
	function GetEnabled() {
328
		return $this->qenabled;
329
	}
330
	function SetEnabled($value) {
331
		$this->qenabled = $value;
332
	}
333
	function CanHaveChildren() {
334
		if ($this->GetScheduler() == "CODELQ") {
335
			return false;
336
		} else {
337
			return true;
338
		}
339
	}
340
	function CanBeDeleted() {
341
		return false;
342
	}
343
	function GetQname() {
344
		return $this->interface;
345
	}
346
	function SetQname($name) {
347
		$this->interface = trim($name);
348
	}
349
	function GetInterface() {
350
		return $this->interface;
351
	}
352
	function SetInterface($name) {
353
		$this->interface = trim($name);
354
	}
355
	function GetTbrConfig() {
356
		return $this->tbrconfig;
357
	}
358
	function SetTbrConfig($tbrconfig) {
359
		$this->tbrconfig = $tbrconfig;
360
	}
361
	function GetBandwidth() {
362
		return $this->bandwidth;
363
	}
364
	function SetBandwidth($bw) {
365
		$this->bandwidth = $bw;
366
	}
367
	function GetBwscale() {
368
		return $this->bandwidthtype;
369
	}
370
	function SetBwscale($bwscale) {
371
		$this->bandwidthtype = $bwscale;
372
	}
373
	function GetScheduler() {
374
		return $this->scheduler;
375
	}
376
	function SetScheduler($scheduler) {
377
		$this->scheduler = trim($scheduler);
378
	}
379
	function GetQlimit() {
380
		return $this->qlimit;
381
	}
382
	function SetQlimit($limit) {
383
		$this->qlimit = $limit;
384
	}
385

    
386
	function GetBwscaleText() {
387
		switch ($this->bandwidthtype) {
388
			case "b":
389
				$bwscaletext = "Bit/s";
390
				break;
391
			case "Kb":
392
				$bwscaletext = "Kbit/s";
393
				break;
394
			case "Mb":
395
				$bwscaletext = "Mbit/s";
396
				break;
397
			case "Gb":
398
				$bwscaletext = "Gbit/s";
399
				break;
400
			default:
401
				/* For others that do not need translating like % */
402
				$bwscaletext = $this->bandwidthtype;
403
				break;
404
		}
405
		return $bwscaletext;
406
	}
407

    
408
	function validate_input($data, &$input_errors) {
409

    
410
		$reqdfields[] = "bandwidth";
411
		$reqdfieldsn[] = gettext("Bandwidth");
412
		$reqdfields[] = "bandwidthtype";
413
		$reqdfieldsn[] = gettext("Bandwidthtype");
414

    
415
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
416

    
417
		if ($data['bandwidth'] && (!is_numeric($data['bandwidth']))) {
418
			$input_errors[] = gettext("Bandwidth must be an integer.");
419
		}
420
		if ($data['bandwidth'] < 0) {
421
			$input_errors[] = gettext("Bandwidth cannot be negative.");
422
		}
423
		if ($data['qlimit'] && (!is_numeric($data['qlimit']))) {
424
			$input_errors[] = gettext("Qlimit must be an integer.");
425
		}
426
		if ($data['qlimit'] < 0) {
427
			$input_errors[] = gettext("Qlimit must be positive.");
428
		}
429
		if ($data['tbrconfig'] && (!is_numeric($data['tbrconfig']))) {
430
			$input_errors[] = gettext("Tbrsize must be an integer.");
431
		}
432
		if ($data['tbrconfig'] < 0) {
433
			$input_errors[] = gettext("Tbrsize must be positive.");
434
		}
435
	}
436

    
437
	/* Implement this to shorten some code on the frontend page */
438
	function ReadConfig(&$conf) {
439
		if (isset($conf['tbrconfig'])) {
440
			$this->SetTbrConfig($conf['tbrconfig']);
441
		} else {
442
			$this->SetTbrConfig($conf['tbrconfig']);
443
		}
444
		$this->SetBandwidth($conf['bandwidth']);
445
		if ($conf['bandwidthtype'] <> "") {
446
			$this->SetBwscale($conf['bandwidthtype']);
447
		}
448
		if (isset($conf['scheduler'])) {
449
			if ($this->GetScheduler() != $conf['scheduler']) {
450
				foreach ($this->queues as $q) {
451
					clean_child_queues($conf['scheduler'], $this->GetLink());
452
					$q->clean_queue($conf['scheduler']);
453
				}
454
			}
455
			$this->SetScheduler($conf['scheduler']);
456
		}
457
		if (isset($conf['qlimit']) && $conf['qlimit'] <> "") {
458
			$this->SetQlimit($conf['qlimit']);
459
		} else {
460
			$this->SetQlimit("");
461
		}
462
		if (isset($conf['name'])) {
463
			$this->SetQname($conf['name']);
464
		}
465
		if (!empty($conf['enabled'])) {
466
			$this->SetEnabled($conf['enabled']);
467
		} else {
468
			$this->SetEnabled("");
469
		}
470
	}
471

    
472
	function copy_queue($interface, &$cflink) {
473
		$cflink['interface'] = $interface;
474
		$cflink['name'] = $interface;
475
		$cflink['scheduler'] = $this->GetScheduler();
476
		$cflink['bandwidth'] = $this->GetBandwidth();
477
		$cflink['bandwidthtype'] = $this->GetBwscale();
478
		$cflink['qlimit'] = $this->GetQlimit();
479
		$cflink['tbrconfig'] = $this->GetTbrConfig();
480
		$cflink['enabled'] = $this->GetEnabled();
481
		if (is_array($this->queues)) {
482
			$cflink['queue'] = array();
483
			foreach ($this->queues as $q) {
484
				$cflink['queue'][$q->GetQname()] = array();
485
				$q->copy_queue($interface, $cflink['queue'][$q->GetQname()]);
486
			}
487
		}
488
	}
489

    
490
	function &get_queue_list(&$q = null) {
491
		$qlist = array();
492

    
493
		//$qlist[$this->GetQname()] = & $this;
494
		if (is_array($this->queues)) {
495
			foreach ($this->queues as $queue) {
496
				$queue->get_queue_list($qlist);
497
			}
498
		}
499
		return $qlist;
500
	}
501

    
502
	function &add_queue($interface, &$queue, &$path, &$input_errors) {
503

    
504
		if (!is_array($this->queues)) {
505
			$this->queues = array();
506
		}
507

    
508
		switch ($this->GetScheduler()) {
509
			case "PRIQ":
510
				$q =& new priq_queue();
511
				break;
512
			case "HFSC":
513
				$q =& new hfsc_queue();
514
				break;
515
			case "CBQ":
516
				$q =& new cbq_queue();
517
				break;
518
			case "FAIRQ":
519
				$q =& new fairq_queue();
520
				break;
521
			default:
522
				/* XXX: but should not happen anyway */
523
				return;
524
				break;
525
		}
526
		$q->SetLink($path);
527
		$q->SetInterface($this->GetInterface());
528
		$q->SetEnabled("on");
529
		$q->SetParent($this);
530
		$q->ReadConfig($queue);
531
		$q->validate_input($queue, $input_errors);
532
		if (count($input_errors)) {
533
			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)));
534
			return $q;
535
		}
536

    
537
		if (isset($queue['bandwidth'])) {
538
			switch ($queue['bandwidthtype']) {
539
				case "%":
540
					$myBw = $this->GetAvailableBandwidth() * $queue['bandwidth'] / 100;
541
					break;
542
				default:
543
					$myBw = $queue['bandwidth'] * get_bandwidthtype_scale($queue['bandwidthtype']);
544
					break;
545
			}
546
		}
547
		$q->SetAvailableBandwidth($myBw);
548
		$this->SetAvailableBandwidth($this->GetAvailableBandwidth() - $myBw);
549
		$this->queues[$q->GetQname()] = &$q;
550
		ref_on_altq_queue_list($this->GetQname(), $q->GetQname());
551
		if (is_array($queue['queue'])) {
552
			foreach ($queue['queue'] as $key1 => $que) {
553
				array_push($path, $key1);
554
				$q->add_queue($q->GetInterface(), $que, $path, $input_errors);
555
				array_pop($path);
556
			}
557
		}
558

    
559
		return $q;
560
	}
561

    
562
	/* interface here might be optional */
563
	function &find_queue($interface, $qname) {
564
		if ($qname == $this->GetQname()) {
565
			return $this;
566
		}
567
		foreach ($this->queues as $q) {
568
			$result =& $q->find_queue("", $qname);
569
			if ($result) {
570
				return $result;
571
			}
572
		}
573
	}
574

    
575
	function &find_parentqueue($interface, $qname) {
576
		if ($qname == $interface) {
577
			$result = NULL;
578
		} else if ($this->queues[$qname]) {
579
			$result = $this;
580
		} else if ($this->GetScheduler() <> "PRIQ") {
581
			foreach ($this->queues as $q) {
582
				$result = $q->find_parentqueue("", $qname);
583
				if ($result) {
584
					return $result;
585
				}
586
			}
587
		}
588
	}
589

    
590
	function build_tree() {
591
		global $shaperIFlist;
592

    
593
		$tree = " <li><a href=\"firewall_shaper.php?interface=".$this->GetInterface()."&amp;queue=". $this->GetInterface()."&amp;action=show";
594
		$tree .= "\">" . $shaperIFlist[$this->GetInterface()] . "</a>";
595
		if (is_array($this->queues)) {
596
			$tree .= "<ul>";
597
			foreach ($this->queues as $q) {
598
				$tree .= $q->build_tree();
599
			}
600
			$tree .= "</ul>";
601
		}
602
		$tree .= "</li>";
603
		return $tree;
604
	}
605

    
606
	function delete_queue() {
607
		foreach ($this->queues as $q) {
608
			$this->SetAvailableBandwidth($this->GetAvailableBandwidth() + $q->GetAvailableBandwidth());
609
			$q->delete_queue();
610
		}
611
		unset_object_by_reference($this->GetLink());
612
	}
613

    
614
	function delete_all() {
615
		if (count($this->queues)) {
616
			foreach ($this->queues as $q) {
617
				$q->delete_all();
618
				unset_object_by_reference($q->GetLink());
619
				unset($q);
620
			}
621
			unset($this->queues);
622
		}
623
	}
624

    
625
	/*
626
	 * First it spits:
627
	 * altq on $interface ..............
628
	 *	then it goes like
629
	 *	foreach ($queues as $qkey => $queue) {
630
	 *		this->queues[$qkey]->build_rule();
631
	 *	}
632
	 */
633
	function build_rules(&$default = false) {
634
		if (count($this->queues) > 0 && $this->GetEnabled() == "on") {
635
			$default = false;
636
			$rules = " altq on " . get_real_interface($this->GetInterface());
637
			if ($this->GetScheduler()) {
638
				$rules .= " ".strtolower($this->GetScheduler());
639
			}
640
			if ($this->GetQlimit() > 0) {
641
				$rules .= " qlimit " . $this->GetQlimit() . " ";
642
			}
643
			if ($this->GetBandwidth()) {
644
				$rules .= " bandwidth ".trim($this->GetBandwidth());
645
				if ($this->GetBwscale()) {
646
					$rules .= $this->GetBwscale();
647
				}
648
			}
649
			if ($this->GetTbrConfig()) {
650
				$rules .= " tbrsize ".$this->GetTbrConfig();
651
			}
652
			if (count($this->queues)) {
653
				$i = count($this->queues);
654
				$rules .= " queue { ";
655
				foreach ($this->queues as $qkey => $qnone) {
656
					if ($i > 1) {
657
						$i--;
658
						$rules .= " {$qkey}, ";
659
					} else {
660
						$rules .= " {$qkey} ";
661
					}
662
				}
663
				$rules .= " } \n";
664
				foreach ($this->queues as $q) {
665
					$rules .= $q->build_rules($default);
666
				}
667
			}
668

    
669
			if ($default == false) {
670
				$error = sprintf(gettext("SHAPER: no default queue specified for interface %s."), $this->GetInterface()) . " " . gettext("The interface queue will be enforced as default.");
671
				file_notice("Shaper", $error, "Error occurred", "");
672
				unset($error);
673
				return "\n";
674
			}
675
			$frule .= $rules;
676
		} else if ($this->GetEnabled() == "on" && $this->GetScheduler() == "CODELQ") {
677
			$rules = " altq on " . get_real_interface($this->GetInterface());
678
			if ($this->GetScheduler()) {
679
				$rules .= " ".strtolower($this->GetScheduler());
680
			}
681
			if ($this->GetQlimit() > 0) {
682
				$rules .= " ( qlimit " . $this->GetQlimit() . " ) ";
683
			}
684
			if ($this->GetBandwidth()) {
685
				$rules .= " bandwidth ".trim($this->GetBandwidth());
686
				if ($this->GetBwscale()) {
687
					$rules .= $this->GetBwscale();
688
				}
689
			}
690
			if ($this->GetTbrConfig()) {
691
				$rules .= " tbrsize ".$this->GetTbrConfig();
692
			}
693

    
694
			$rules .= " queue";
695
		}
696

    
697
		$rules .= " \n";
698
		return $rules;
699
	}
700

    
701
	function build_javascript() {
702
		$javascript = "<script type=\"text/javascript\">";
703
		$javascript .= "//<![CDATA[\n";
704
		$javascript .= "function mySuspend() {";
705
		$javascript .= "if (document.layers && document.layers['shaperarea'] != null) ";
706
		$javascript .= "document.layers['shaperarea'].visibility = 'hidden'; ";
707
		$javascript .= "else if (document.all)";
708
		$javascript .= "document.all['shaperarea'].style.visibility = 'hidden';";
709
		$javascript .= "}";
710

    
711
		$javascript .= "function myResume() {";
712
		$javascript .= "if (document.layers && document.layers['shaperarea'] != null) ";
713
		$javascript .= "document.layers['shaperarea'].visibility = 'visible';";
714
		$javascript .= "else if (document.all) ";
715
		$javascript .= "document.all['shaperarea'].style.visibility = 'visible';";
716
		$javascript .= "}";
717
		$javascript .= "//]]>";
718
		$javascript .= "</script>";
719

    
720
		return $javascript;
721
	}
722

    
723
	function build_shortform() {
724
		global $g;
725

    
726
		$altq =& $this;
727

    
728
		if ($altq) {
729
			$scheduler = ": " . $altq->GetScheduler();
730
		}
731

    
732
		$form = '<dl class="dl-horizontal">';
733
		$form .= '	<dt>';
734
		$form .= '		<a href="firewall_shaper.php?interface=' . $this->GetInterface() . '&amp;queue=' . $this->GetQname() . '&amp;action=show">' . $shaperIFlist[$this->GetInterface()] . '</a>';
735
		$form .= '	</dt>';
736
		$form .= '	<dd>';
737
		$form .=		$scheduler;
738
		$form .= '	</dd>';
739

    
740
		$form .= '	<dt>';
741
		$form .=		'Bandwidth';
742
		$form .= '	</dt>';
743
		$form .= '	<dd>';
744
		$form .=		$this->GetBandwidth() . '&nbsp;' . $this->GetBwscaleText();
745
		$form .= '	</dd>';
746

    
747
		$form .= '	<dt>';
748
		$form .= 'Disable';
749
		$form .= '	<dt>';
750
		$form .= '	<dd>';
751

    
752
		$form .= '<a class="btn btn-danger btn-xs" href="firewall_shaper_queues.php?interface=';
753
		$form .= $this->GetInterface() . '&amp;queue=';
754
		$form .= $this->GetQname() . '&amp;action=delete">';
755
		$form .= '<i class="fa fa-trash icon-embed-btn"></i>';
756
		$form .= gettext("Remove shaper from this interface") . '</a>';
757

    
758
		$form .= '	</dd>';
759

    
760
		$form .= '</dl>';
761

    
762
		return $form;
763

    
764
	}
765

    
766
	/*
767
	 * For requesting the parameters of the root queues
768
	 * to the user like the traffic wizard does.
769
	 */
770
	function build_form() {
771

    
772
		$sform = new Form();
773

    
774
		$sform->setAction("firewall_shaper.php");
775

    
776
		$section = new Form_Section(null);
777

    
778
		$section->addInput(new Form_Checkbox(
779
			'enabled',
780
			'Enable/Disable',
781
			'Enable/disable discipline and its children',
782
			($this->GetEnabled() == "on"),
783
			'on'
784
		));
785

    
786
		$section->addInput(new Form_StaticText(
787
			'Name',
788
			$this->GetQname()
789
		));
790

    
791
		$section->addInput(new Form_Select(
792
			'scheduler',
793
			'Scheduler Type',
794
			$this->GetScheduler(),
795
			array('HFSC' => 'HFSC',
796
				  'CBQ' => 'CBQ',
797
				  'FAIRQ' => 'FAIRQ',
798
				  'CODELQ' => 'CODELQ',
799
				  'PRIQ' => 'PRIQ')
800
		))->setHelp('Changing this changes all child queues! Beware you can lose information.');
801

    
802
		$group = new Form_group('Bandwidth');
803

    
804
		$group->add(new Form_Input(
805
			'bandwidth',
806
			null,
807
			'number',
808
			$this->GetBandwidth()
809
		));
810

    
811
		$group->add(new Form_Select(
812
			'bandwidthtype',
813
			null,
814
			$this->GetBwscale(),
815
			array('Kb' => 'Kbit/s',
816
				  'Mb' => 'Mbit/s',
817
				  'Gb' => 'Gbit/s',
818
				  'b' => 'Bit/s',
819
				  '%' => '%')
820
		));
821

    
822
		$section->add($group);
823

    
824
		$section->addInput(new Form_Input(
825
			'qlimit',
826
			'Queue Limit',
827
			'number',
828
			$this->GetQlimit()
829
		));
830

    
831
		$section->addInput(new Form_Input(
832
			'tbrconfig',
833
			'TRB Size',
834
			'number',
835
			$this->GetTbrConfig()
836
		))->setHelp('Adjusts the size, in bytes, of the token bucket regulator. If not specified, heuristics based on the interface ' .
837
					'bandwidth are used to determine the size.');
838

    
839
		$section->addInput(new Form_Input(
840
			'interface',
841
			null,
842
			'hidden',
843
			$this->GetInterface()
844
		));
845

    
846
		$section->addInput(new Form_Input(
847
			'name',
848
			null,
849
			'hidden',
850
			$this->GetQname()
851
		));
852

    
853
		$sform->add($section);
854

    
855
		return($sform);
856
	}
857

    
858
	function update_altq_queue_data(&$data) {
859
		$this->ReadConfig($data);
860
	}
861

    
862
	/*
863
	 * Should call on each of it queues and subqueues
864
	 * the same function much like build_rules();
865
	 */
866
	function wconfig() {
867
		$cflink = &get_reference_to_me_in_config($this->GetLink());
868
		if (!is_array($cflink)) {
869
			$cflink = array();
870
		}
871
		$cflink['interface'] = $this->GetInterface();
872
		$cflink['name'] = $this->GetQname();
873
		$cflink['scheduler'] = $this->GetScheduler();
874
		$cflink['bandwidth'] = $this->GetBandwidth();
875
		$cflink['bandwidthtype'] = $this->GetBwscale();
876
		$cflink['qlimit'] = trim($this->GetQlimit());
877
		if (empty($cflink['qlimit'])) {
878
			unset($cflink['qlimit']);
879
		}
880
		$cflink['tbrconfig'] = trim($this->GetTbrConfig());
881
		if (empty($cflink['tbrconfig'])) {
882
			unset($cflink['tbrconfig']);
883
		}
884
		$cflink['enabled'] = $this->GetEnabled();
885
		if (empty($cflink['enabled'])) {
886
			unset($cflink['enabled']);
887
		}
888
	}
889

    
890
}
891

    
892
class priq_queue {
893
	var $qname;
894
	var $qinterface;
895
	var $qlimit;
896
	var $qpriority;
897
	var $description;
898
	var $isparent;
899
	var $qbandwidth;
900
	var $qbandwidthtype;
901
	var $qdefault = "";
902
	var $qrio = "";
903
	var $qred = "";
904
	var $qcodel = "";
905
	var $qecn = "";
906
	var $qack;
907
	var $qenabled = "";
908
	var $qparent;
909
	var $link;
910
	var $available_bw; /* in b/s */
911

    
912
	/* This is here to help with form building and building rules/lists */
913
	var $subqueues = array();
914

    
915
	/* Accessor functions */
916
	function GetAvailableBandwidth() {
917
		return $this->available_bw;
918
	}
919
	function SetAvailableBandwidth($bw) {
920
		$this->available_bw = $bw;
921
	}
922
	function SetLink($link) {
923
		$this->link = $link;
924
	}
925
	function GetLink() {
926
		return $this->link;
927
	}
928
	function &GetParent() {
929
		return $this->qparent;
930
	}
931
	function SetParent(&$parent) {
932
		$this->qparent = &$parent;
933
	}
934
	function GetEnabled() {
935
		return $this->qenabled;
936
	}
937
	function SetEnabled($value) {
938
		$this->qenabled = $value;
939
	}
940
	function CanHaveChildren() {
941
		return false;
942
	}
943
	function CanBeDeleted() {
944
		return true;
945
	}
946
	function GetQname() {
947
		return $this->qname;
948
	}
949
	function SetQname($name) {
950
		$this->qname = trim($name);
951
	}
952
	function GetBandwidth() {
953
		return $this->qbandwidth;
954
	}
955
	function SetBandwidth($bandwidth) {
956
		$this->qbandwidth = $bandwidth;
957
	}
958
	function GetInterface() {
959
		return $this->qinterface;
960
	}
961
	function SetInterface($name) {
962
		$this->qinterface = trim($name);
963
	}
964
	function GetQlimit() {
965
		return $this->qlimit;
966
	}
967
	function SetQlimit($limit) {
968
		$this->qlimit = $limit;
969
	}
970
	function GetQpriority() {
971
		return $this->qpriority;
972
	}
973
	function SetQpriority($priority) {
974
		$this->qpriority = $priority;
975
	}
976
	function GetDescription() {
977
		return $this->description;
978
	}
979
	function SetDescription($str) {
980
		$this->description = trim($str);
981
	}
982
	function GetFirstime() {
983
		return $this->firsttime;
984
	}
985
	function SetFirsttime($number) {
986
		$this->firsttime = $number;
987
	}
988
	function GetBwscale() {
989
		return $this->qbandwidthtype;
990
	}
991
	function SetBwscale($scale) {
992
		$this->qbandwidthtype = $scale;
993
	}
994
	function GetDefaultQueuePresent() {
995
		if ($this->GetDefault()) {
996
			return true;
997
		}
998
		if (!empty($this->subqueues)) {
999
			foreach ($this->subqueues as $q) {
1000
				if ($q->GetDefault()) {
1001
					return true;
1002
				}
1003
			}
1004
		}
1005

    
1006
		return false;
1007
	}
1008
	function GetDefault() {
1009
		return $this->qdefault;
1010
	}
1011
	function SetDefault($value = false) {
1012
		$this->qdefault = $value;
1013
	}
1014
	function GetCodel() {
1015
		return $this->codel;
1016
	}
1017
	function SetCodel($codel = false) {
1018
		$this->codel = $codel;
1019
	}
1020
	function GetRed() {
1021
		return $this->qred;
1022
	}
1023
	function SetRed($red = false) {
1024
		$this->qred = $red;
1025
	}
1026
	function GetRio() {
1027
		return $this->qrio;
1028
	}
1029
	function SetRio($rio = false) {
1030
		$this->qrio = $rio;
1031
	}
1032
	function GetEcn() {
1033
		return $this->qecn;
1034
	}
1035
	function SetEcn($ecn = false) {
1036
		$this->qecn = $ecn;
1037
	}
1038
	function GetAck() {
1039
		return $this->qack;
1040
	}
1041
	function SetAck($ack = false) {
1042
		$this->qack = $ack;
1043
	}
1044

    
1045
	function GetBwscaleText() {
1046
		switch ($this->qbandwidthtype) {
1047
			case "b":
1048
				$bwscaletext = "Bit/s";
1049
				break;
1050
			case "Kb":
1051
				$bwscaletext = "Kbit/s";
1052
				break;
1053
			case "Mb":
1054
				$bwscaletext = "Mbit/s";
1055
				break;
1056
			case "Gb":
1057
				$bwscaletext = "Gbit/s";
1058
				break;
1059
			default:
1060
				/* For others that do not need translating like % */
1061
				$bwscaletext = $this->qbandwidthtype;
1062
				break;
1063
		}
1064
		return $bwscaletext;
1065
	}
1066

    
1067
	function build_javascript() {
1068
		$javascript = "<script type=\"text/javascript\">";
1069
		$javascript .= "//<![CDATA[\n";
1070
		$javascript .= "function mySuspend() { \n";
1071
		$javascript .= "if (document.layers && document.layers['shaperarea'] != null)\n";
1072
		$javascript .= "document.layers['shaperarea'].visibility = 'hidden';\n";
1073
		$javascript .= "else if (document.all)\n";
1074
		$javascript .= "document.all['shaperarea'].style.visibility = 'hidden';\n";
1075
		$javascript .= "}\n";
1076

    
1077
		$javascript .= "function myResume() {\n";
1078
		$javascript .= "if (document.layers && document.layers['shaperarea'] != null)\n";
1079
		$javascript .= "document.layers['shaperarea'].visibility = 'visible';\n";
1080
		$javascript .= "else if (document.all)\n";
1081
		$javascript .= "document.all['shaperarea'].style.visibility = 'visible';\n";
1082
		$javascript .= "}\n";
1083
		$javascript .= "//]]>";
1084
		$javascript .= "</script>";
1085

    
1086
		return $javascript;
1087
	}
1088

    
1089
	function &add_queue($interface, &$qname, &$path, &$input_errors) { return; }
1090

    
1091
	/*
1092
	 * Currently this will not be called unless we decide to clone a whole
1093
	 * queue tree on the 'By Queues' view or support drag&drop on the tree/list
1094
	 */
1095
	function copy_queue($interface, &$cflink) {
1096

    
1097
		$cflink['name'] = $this->GetQname();
1098
		$cflink['interface'] = $interface;
1099
		$cflink['qlimit'] = $this->GetQlimit();
1100
		$cflink['priority'] = $this->GetQpriority();
1101
		$cflink['description'] = $this->GetDescription();
1102
		$cflink['enabled'] = $this->GetEnabled();
1103
		$cflink['default'] = $this->GetDefault();
1104
		$cflink['red'] = $this->GetRed();
1105
		$cflink['codel'] = $this->GetCodel();
1106
		$cflink['rio'] = $this->GetRio();
1107
		$cflink['ecn'] = $this->GetEcn();
1108

    
1109
		if (is_array($this->subqueues)) {
1110
			$cflinkp['queue'] = array();
1111
			foreach ($this->subqueues as $q) {
1112
				$cflink['queue'][$q->GetQname()] = array();
1113
				$q->copy_queue($interface, $cflink['queue'][$q->GetQname()]);
1114
			}
1115
		}
1116
	}
1117

    
1118
	function clean_queue($sched) {
1119
		clean_child_queues($sched, $this->GetLink());
1120
		if (is_array($this->subqueues)) {
1121
			foreach ($this->subqueues as $q) {
1122
				$q->clean_queue($sched);
1123
			}
1124
		}
1125
	}
1126

    
1127
	function &get_queue_list(&$qlist) {
1128

    
1129
		$qlist[$this->GetQname()] = & $this;
1130
		if (is_array($this->subqueues)) {
1131
			foreach ($this->subqueues as $queue) {
1132
				$queue->get_queue_list($qlist);
1133
			}
1134
		}
1135
	}
1136

    
1137
	function delete_queue() {
1138
		unref_on_altq_queue_list($this->GetQname());
1139
		cleanup_queue_from_rules($this->GetQname());
1140
		unset_object_by_reference($this->GetLink());
1141
	}
1142

    
1143
	function delete_all() {
1144
		if (count($this->subqueues)) {
1145
			foreach ($this->subqueues as $q) {
1146
				$q->delete_all();
1147
				unset_object_by_reference($q->GetLink());
1148
				unset($q);
1149
			}
1150
			unset($this->subqueues);
1151
		}
1152
	}
1153

    
1154
	function &find_queue($interface, $qname) {
1155
		if ($qname == $this->GetQname()) {
1156
			return $this;
1157
		}
1158
	}
1159

    
1160
	function find_parentqueue($interface, $qname) { return; }
1161

    
1162
	function validate_input($data, &$input_errors) {
1163

    
1164
		$reqdfields[] = "name";
1165
		$reqdfieldsn[] = gettext("Name");
1166
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
1167

    
1168
		if ($data['bandwidth'] && (!is_numeric($data['bandwidth']))) {
1169
			$input_errors[] = gettext("Bandwidth must be an integer.");
1170
		}
1171
		if ($data['bandwidth'] < 0) {
1172
			$input_errors[] = gettext("Bandwidth cannot be negative.");
1173
		}
1174
		if ($data['priority'] && (!is_numeric($data['priority']) ||
1175
		    ($data['priority'] < 1) || ($data['priority'] > 15))) {
1176
			$input_errors[] = gettext("The priority must be an integer between 1 and 15.");
1177
		}
1178
		if ($data['qlimit'] && (!is_numeric($data['qlimit']))) {
1179
				$input_errors[] = gettext("Queue limit must be an integer");
1180
		}
1181
		if ($data['qlimit'] < 0) {
1182
				$input_errors[] = gettext("Queue limit must be positive");
1183
		}
1184
		if (!empty($data['newname']) && !preg_match("/^[a-zA-Z0-9_-]*$/", $data['newname'])) {
1185
			$input_errors[] = gettext("Queue names must be alphanumeric and _ or - only.");
1186
		}
1187
		if (!empty($data['name']) && !preg_match("/^[a-zA-Z0-9_-]*$/", $data['name'])) {
1188
			$input_errors[] = gettext("Queue names must be alphanumeric and _ or - only.");
1189
		}
1190
		$default = $this->GetDefault();
1191
		if (!empty($data['default']) && altq_get_default_queue($data['interface']) && empty($default)) {
1192
			$input_errors[] = gettext("Only one default queue per interface is allowed.");
1193
		}
1194
	}
1195

    
1196
	function ReadConfig(&$q) {
1197
		if (!empty($q['name']) && !empty($q['newname']) && $q['name'] != $q['newname']) {
1198
			$this->SetQname($q['newname']);
1199
		} else if (!empty($q['newname'])) {
1200
			$this->SetQname($q['newname']);
1201
		} else if (isset($q['name'])) {
1202
			$this->SetQname($q['name']);
1203
		}
1204
		if (isset($q['interface'])) {
1205
			$this->SetInterface($q['interface']);
1206
		}
1207
		$this->SetBandwidth($q['bandwidth']);
1208
		if ($q['bandwidthtype'] <> "") {
1209
			$this->SetBwscale($q['bandwidthtype']);
1210
		}
1211
		if (!empty($q['qlimit'])) {
1212
			$this->SetQlimit($q['qlimit']);
1213
		} else {
1214
			$this->SetQlimit(""); // Default
1215
		}
1216
		if (!empty($q['priority'])) {
1217
			$this->SetQPriority($q['priority']);
1218
		} else {
1219
			$this->SetQpriority("");
1220
		}
1221
		if (!empty($q['description'])) {
1222
			$this->SetDescription($q['description']);
1223
		} else {
1224
			$this->SetDescription("");
1225
		}
1226
		if (!empty($q['red'])) {
1227
			$this->SetRed($q['red']);
1228
		} else {
1229
			$this->SetRed();
1230
		}
1231
		if (!empty($q['codel'])) {
1232
			$this->SetCodel($q['codel']);
1233
		} else {
1234
			$this->SetCodel();
1235
		}
1236
		if (!empty($q['rio'])) {
1237
			$this->SetRio($q['rio']);
1238
		} else {
1239
			$this->SetRio();
1240
		}
1241
		if (!empty($q['ecn'])) {
1242
			$this->SetEcn($q['ecn']);
1243
		} else {
1244
			$this->SetEcn();
1245
		}
1246
		if (!empty($q['default'])) {
1247
			$this->SetDefault($q['default']);
1248
		} else {
1249
			$this->SetDefault();
1250
		}
1251
		if (!empty($q['enabled'])) {
1252
			$this->SetEnabled($q['enabled']);
1253
		} else {
1254
			$this->SetEnabled("");
1255
		}
1256
	}
1257

    
1258
	function build_tree() {
1259
		$tree = " <li><a href=\"firewall_shaper.php?interface=". $this->GetInterface()."&amp;queue=". $this->GetQname()."&amp;action=show";
1260
		$tree .= "\" ";
1261
		$tmpvalue = $this->GetDefault();
1262
		if (!empty($tmpvalue)) {
1263
			$tree .= " class=\"navlnk\"";
1264
		}
1265
		$tree .= " >" . $this->GetQname() . "</a>";
1266
		/*
1267
		 * Not needed here!
1268
		 * if (is_array($queues) {
1269
		 *	  $tree .= "<ul>";
1270
		 *	  foreach ($q as $queues)
1271
		 *		  $tree .= $queues['$q->GetName()']->build_tree();
1272
		 *	  endforeach
1273
		 *	  $tree .= "</ul>";
1274
		 * }
1275
		 */
1276

    
1277
		$tree .= "</li>";
1278

    
1279
		return $tree;
1280
	}
1281

    
1282
	/* Should return something like:
1283
	 * queue $qname on $qinterface bandwidth ....
1284
	 */
1285
	function build_rules(&$default = false) {
1286
		$pfq_rule = " queue ". $this->qname;
1287
		if ($this->GetInterface()) {
1288
			$pfq_rule .= " on ".get_real_interface($this->GetInterface());
1289
		}
1290
		$tmpvalue = $this->GetQpriority();
1291
		if (!empty($tmpvalue)) {
1292
			$pfq_rule .= " priority ".$this->GetQpriority();
1293
		}
1294
		$tmpvalue = $this->GetQlimit();
1295
		if (!empty($tmpvalue)) {
1296
			$pfq_rule .= " qlimit " . $this->GetQlimit();
1297
		}
1298
		if ($this->GetRed() || $this->GetRio() || $this->GetEcn() || $this->GetDefault() || $this->GetCodel()) {
1299
			$pfq_rule .= " priq ( ";
1300
			$tmpvalue = $this->GetRed();
1301
			if (!empty($tmpvalue)) {
1302
				$comma = 1;
1303
				$pfq_rule .= " red ";
1304
			}
1305
			$tmpvalue = $this->GetRio();
1306
			if (!empty($tmpvalue)) {
1307
				if ($comma) {
1308
					$pfq_rule .= " ,";
1309
				}
1310
				$comma = 1;
1311
				$pfq_rule .= " rio ";
1312
			}
1313
			$tmpvalue = $this->GetEcn();
1314
			if (!empty($tmpvalue)) {
1315
				if ($comma) {
1316
					$pfq_rule .= " ,";
1317
				}
1318
				$comma = 1;
1319
				$pfq_rule .= " ecn ";
1320
			}
1321
			$tmpvalue = $this->GetCodel();
1322
			if (!empty($tmpvalue)) {
1323
				if ($comma) {
1324
					$pfq_rule .= " ,";
1325
				}
1326
				$comma = 1;
1327
				$pfq_rule .= " codel ";
1328
			}
1329
			$tmpvalue = $this->GetDefault();
1330
			if (!empty($tmpvalue)) {
1331
				if ($comma) {
1332
					$pfq_rule .= " ,";
1333
				}
1334
				$pfq_rule .= " default ";
1335
				$default = true;
1336
			}
1337
			$pfq_rule .= " ) ";
1338
		}
1339

    
1340
		$pfq_rule .= " \n";
1341

    
1342
		return $pfq_rule;
1343
	}
1344

    
1345
	/*
1346
	 * To return the html form to show to user
1347
	 * for getting the parameters.
1348
	 * Should do even for first time when the
1349
	 * object is created and later when we may
1350
	 * need to update it. (2)
1351
	 */
1352

    
1353
	function build_form() {
1354

    
1355
		$sform = new Form();
1356

    
1357
		$sform->setAction("firewall_shaper.php");
1358

    
1359
		$section = new Form_Section("");
1360

    
1361
		$section->addInput(new Form_Checkbox(
1362
			'enabled',
1363
			'Enable/Disable',
1364
			'Enable/disable discipline and its children',
1365
			($this->GetEnabled() == "on"),
1366
			'on'
1367
		));
1368

    
1369
		$section->addInput(new Form_Input(
1370
			'newname',
1371
			'Name',
1372
			'text',
1373
			$this->GetQname()
1374
		))->setHelp('Enter the name of the queue here. Do not use spaces and limit the size to 15 characters.');
1375

    
1376
		$section->addInput(new Form_Input(
1377
			'name',
1378
			null,
1379
			'hidden',
1380
			$this->GetQname()
1381
		));
1382

    
1383
		$section->addInput(new Form_Input(
1384
			'priority',
1385
			'Priority',
1386
			'number',
1387
			$this->GetQpriority(),
1388
			['min' => '0', 'max'=> '7']
1389
		))->setHelp('For hfsc, the range is 0 to 7. The default is 1. Hfsc queues with a higher priority are preferred in the case of overload.');
1390

    
1391
		$section->addInput(new Form_Input(
1392
			'qlimit',
1393
			'Queue Limit',
1394
			'number',
1395
			$this->GetQlimit()
1396
		))->setHelp('Queue limit in packets.');
1397

    
1398
		$group = new Form_Group('Scheduler options');
1399

    
1400
		if (empty($this->subqueues)) {
1401
			$group->add(new Form_Checkbox(
1402
				'default',
1403
				null,
1404
				null,
1405
				$this->GetDefault(),
1406
				'default'
1407
			))->setHelp('Default Queue');
1408
		}
1409

    
1410
		$group->add(new Form_Checkbox(
1411
			'red',
1412
			null,
1413
			null,
1414
			!empty($this->GetRed())
1415
		))->setHelp('<a target="_new" href="http://www.openbsd.org/faq/pf/queueing.html#red">' . gettext('Random Early Detection') . '</a>');
1416

    
1417
		$group->add(new Form_Checkbox(
1418
			'rio',
1419
			null,
1420
			null,
1421
			!empty($this->GetRio())
1422
		))->setHelp('<a target="_new" href="http://www.openbsd.org/faq/pf/queueing.html#rio">' . gettext('Random Early Detection In and Out') . '</a>');
1423

    
1424
		$group->add(new Form_Checkbox(
1425
			'ecn',
1426
			null,
1427
			null,
1428
			!empty($this->GetEcn())
1429
		))->setHelp('<a target="_new" href="http://www.openbsd.org/faq/pf/queueing.html#ecn">' . gettext('Explicit Congestion Notification') . '</a>');
1430

    
1431
		$group->add(new Form_Checkbox(
1432
			'codel',
1433
			null,
1434
			null,
1435
			!empty($this->GetCodel())
1436
		))->setHelp('<a target="_new" href="http://www.openbsd.org/faq/pf/queueing.html#ecn">' . gettext('Codel Active Queue') . '</a>');
1437

    
1438
		$group->setHelp('Select options for this queue');
1439

    
1440
		$section->add($group);
1441

    
1442
		$section->addInput(new Form_Input(
1443
			'description',
1444
			'Description',
1445
			'text',
1446
			$this->GetDescription()
1447
		));
1448

    
1449
		$sform->add($section);
1450

    
1451
		$sform->addGlobal(new Form_Input(
1452
			'interface',
1453
			null,
1454
			'hidden',
1455
			$this->GetInterface()
1456
		));
1457

    
1458
		$sform->addGlobal(new Form_Input(
1459
			'name',
1460
			null,
1461
			'hidden',
1462
			$this->GetQname()
1463
		));
1464

    
1465
		return($sform);
1466
	}
1467

    
1468
	function build_shortform() {
1469
		/* XXX: Hacks in sight. Mostly layer violations!  */
1470
		global $g, $altq_list_queues;
1471
		global $shaperIFlist;
1472

    
1473
		$altq =& $altq_list_queues[$this->GetInterface()];
1474

    
1475
		if ($altq) {
1476
			$scheduler = $altq->GetScheduler();
1477
		}
1478

    
1479
		$form = '<dl class="dl-horizontal">';
1480
		$form .= '	<dt>';
1481
		$form .= '		<a href="firewall_shaper.php?interface=' . $this->GetInterface() . '&amp;queue=' . $this->GetQname() . '&amp;action=show">' . $shaperIFlist[$this->GetInterface()] . '</a>';
1482
		$form .= '	</dt>';
1483
		$form .= '	<dd>';
1484
		$form .=		$scheduler;
1485
		$form .= '	</dd>';
1486

    
1487
		$form .= '	<dt>';
1488
		$form .=		'Bandwidth';
1489
		$form .= '	</dt>';
1490
		$form .= '	<dd>';
1491
		$form .=		$this->GetBandwidth() . '&nbsp;' . $this->GetBwscaleText();
1492
		$form .= '	</dd>';
1493

    
1494
		$tmpvalue = $this->GetQpriority();
1495
		if (!empty($tmpvalue)) {
1496
			$form .= '	<dt>';
1497
			$form .=		'Priority';
1498
			$form .= '	<dt>';
1499
			$form .= '	<dd>';
1500
			$form .=		'On';
1501
			$form .= '	</dd>';
1502
		}
1503

    
1504
		$tmpvalue = $this->GetDefault();
1505
		if (!empty($tmpvalue)) {
1506
			$form .= '	<dt>';
1507
			$form .=		'Default';
1508
			$form .= '	<dt>';
1509
			$form .= '	<dd>';
1510
			$form .=		'On';
1511
			$form .= '	</dd>';
1512
		}
1513

    
1514
			$form .= '	<dt>';
1515
			$form .= 'Delete';
1516
			$form .= '	<dt>';
1517
			$form .= '	<dd>';
1518

    
1519
			$form .= '<a class="btn btn-danger btn-xs" href="firewall_shaper_queues.php?interface=';
1520
			$form .= $this->GetInterface() . '&amp;queue=';
1521
			$form .= $this->GetQname() . '&amp;action=delete">';
1522
			$form .= '<i class="fa fa-trash icon-embed-btn"></i>';
1523
			$form .= gettext("Delete Queue from this Interface") . '</a>';
1524

    
1525
			$form .= '	</dd>';
1526

    
1527
			$form .= '</dl>';
1528

    
1529
		return $form;
1530

    
1531
	}
1532

    
1533
	function update_altq_queue_data(&$q) {
1534
		$this->ReadConfig($q);
1535
	}
1536

    
1537
	function wconfig() {
1538
		$cflink =& get_reference_to_me_in_config($this->GetLink());
1539
		if (!is_array($cflink)) {
1540
			$cflink = array();
1541
		}
1542
		$cflink['name'] = $this->GetQname();
1543
		$cflink['interface'] = $this->GetInterface();
1544
		$cflink['qlimit'] = trim($this->GetQlimit());
1545
		if (empty($cflink['qlimit'])) {
1546
			unset($cflink['qlimit']);
1547
		}
1548
		$cflink['priority'] = trim($this->GetQpriority());
1549
		if (empty($cflink['priority'])) {
1550
			unset($cflink['priority']);
1551
		}
1552
		$cflink['description'] = trim($this->GetDescription());
1553
		if (empty($cflink['description'])) {
1554
			unset($cflink['description']);
1555
		}
1556
		$cflink['enabled'] = trim($this->GetEnabled());
1557
		if (empty($cflink['enabled'])) {
1558
			unset($cflink['enabled']);
1559
		}
1560
		$cflink['default'] = trim($this->GetDefault());
1561
		if (empty($cflink['default'])) {
1562
			unset($cflink['default']);
1563
		}
1564
		$cflink['red'] = trim($this->GetRed());
1565
		if (empty($cflink['red'])) {
1566
			unset($cflink['red']);
1567
		}
1568
		$cflink['codel'] = trim($this->GetCodel());
1569
		if (empty($cflink['codel'])) {
1570
			unset($cflink['codel']);
1571
		}
1572
		$cflink['rio'] = trim($this->GetRio());
1573
		if (empty($cflink['rio'])) {
1574
			unset($cflink['rio']);
1575
		}
1576
		$cflink['ecn'] = trim($this->GetEcn());
1577
		if (empty($cflink['ecn'])) {
1578
			unset($cflink['ecn']);
1579
		}
1580
	}
1581
}
1582

    
1583
class hfsc_queue extends priq_queue {
1584
	/* realtime */
1585
	var $realtime;
1586
	var $r_m1;
1587
	var $r_d;
1588
	var $r_m2;
1589
	/* linkshare */
1590
	var $linkshare;
1591
	var $l_m1;
1592
	var $l_d;
1593
	var $l_m2;
1594
	/* upperlimit */
1595
	var $upperlimit;
1596
	var $u_m1;
1597
	var $u_d;
1598
	var $u_m2;
1599

    
1600
	/*
1601
	 * HFSC can have nested queues.
1602
	 */
1603
	function CanHaveChildren() {
1604
		return true;
1605
	}
1606
	function GetRealtime() {
1607
		return $this->realtime;
1608
	}
1609
	function GetR_m1() {
1610
		return $this->r_m1;
1611
	}
1612
	function GetR_d() {
1613
		return $this->r_d;
1614
	}
1615
	function GetR_m2() {
1616
		return $this->r_m2;
1617
	}
1618
	function SetRealtime() {
1619
		$this->realtime = "on";
1620
	}
1621
	function DisableRealtime() {
1622
		$this->realtime = "";
1623
	}
1624
	function SetR_m1($value) {
1625
		$this->r_m1 = $value;
1626
	}
1627
	function SetR_d($value) {
1628
		$this->r_d = $value;
1629
	}
1630
	function SetR_m2($value) {
1631
		$this->r_m2 = $value;
1632
	}
1633
	function GetLinkshare() {
1634
		return $this->linkshare;
1635
	}
1636
	function DisableLinkshare() {
1637
		$this->linkshare = "";
1638
	}
1639
	function GetL_m1() {
1640
		return $this->l_m1;
1641
	}
1642
	function GetL_d() {
1643
		return $this->l_d;
1644
	}
1645
	function GetL_m2() {
1646
		return $this->l_m2;
1647
	}
1648
	function SetLinkshare() {
1649
		$this->linkshare = "on";
1650
	}
1651
	function SetL_m1($value) {
1652
		$this->l_m1 = $value;
1653
	}
1654
	function SetL_d($value) {
1655
		$this->l_d = $value;
1656
	}
1657
	function SetL_m2($value) {
1658
		$this->l_m2 = $value;
1659
	}
1660
	function GetUpperlimit() {
1661
		return $this->upperlimit;
1662
	}
1663
	function GetU_m1() {
1664
		return $this->u_m1;
1665
	}
1666
	function GetU_d() {
1667
		return $this->u_d;
1668
	}
1669
	function GetU_m2() {
1670
		return $this->u_m2;
1671
	}
1672
	function SetUpperlimit() {
1673
		$this->upperlimit = "on";
1674
	}
1675
	function DisableUpperlimit() {
1676
		$this->upperlimit = "";
1677
	}
1678
	function SetU_m1($value) {
1679
		$this->u_m1 = $value;
1680
	}
1681
	function SetU_d($value) {
1682
		$this->u_d = $value;
1683
	}
1684
	function SetU_m2($value) {
1685
		$this->u_m2 = $value;
1686
	}
1687

    
1688
	function &add_queue($interface, &$qname, &$path, &$input_errors) {
1689

    
1690
		if (!is_array($this->subqueues)) {
1691
			$this->subqueues = array();
1692
		}
1693
		$q =& new hfsc_queue();
1694
		$q->SetInterface($this->GetInterface());
1695
		$q->SetParent($this);
1696
		$q->ReadConfig($qname);
1697
		$q->validate_input($qname, $input_errors);
1698
		if (count($input_errors)) {
1699
			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)));
1700
			return $q;
1701
		}
1702

    
1703
		$q->SetEnabled("on");
1704
		$q->SetLink($path);
1705
		switch ($q->GetBwscale()) {
1706
			case "%":
1707
				$myBw = $this->GetAvailableBandwidth() * $qname['bandwidth'] / 100;
1708
				break;
1709
			default:
1710
				$myBw = $qname['bandwidth'] * get_bandwidthtype_scale($q->GetBwscale());
1711
				break;
1712
		}
1713
		$q->SetAvailableBandwidth($myBw);
1714
		$this->SetAvailableBandwidth($this->GetAvailableBandwidth() - $myBw);
1715

    
1716
		$this->subqueues[$q->GetQname()] =& $q; //new hfsc_queue()
1717
		ref_on_altq_queue_list($this->GetQname(), $q->GetQname());
1718
		if (is_array($qname['queue'])) {
1719
			foreach ($qname['queue'] as $key1 => $que) {
1720
				array_push($path, $key1);
1721
				$q->add_queue($q->GetInterface(), $que, $path, $input_errors);
1722
				array_pop($path);
1723
			}
1724
		}
1725

    
1726
		return $q;
1727
	}
1728

    
1729
	function copy_queue($interface, &$cflink) {
1730

    
1731
		$cflink['name'] = $this->GetQname();
1732
		$cflink['interface'] = $interface;
1733
		$cflink['qlimit'] = trim($this->GetQlimit());
1734
		if (empty($cflink['qlimit'])) {
1735
			unset($cflink['qlimit']);
1736
		}
1737
		$cflink['priority'] = trim($this->GetQpriority());
1738
		if (empty($cflink['priority'])) {
1739
			unset($cflink['priority']);
1740
		}
1741
		$cflink['description'] = trim($this->GetDescription());
1742
		if (empty($cflink['description'])) {
1743
			unset($cflink['description']);
1744
		}
1745
		$cflink['bandwidth'] = $this->GetBandwidth();
1746
		$cflink['bandwidthtype'] = $this->GetBwscale();
1747
		$cflink['enabled'] = trim($this->GetEnabled());
1748
		if (empty($cflink['enabled'])) {
1749
			unset($cflink['enabled']);
1750
		}
1751
		$cflink['default'] = trim($this->GetDefault());
1752
		if (empty($cflink['default'])) {
1753
			unset($cflink['default']);
1754
		}
1755
		$cflink['red'] = trim($this->GetRed());
1756
		if (empty($cflink['red'])) {
1757
			unset($cflink['red']);
1758
		}
1759
		$cflink['rio'] = trim($this->GetRio());
1760
		if (empty($cflink['rio'])) {
1761
			unset($cflink['rio']);
1762
		}
1763
		$cflink['ecn'] = trim($this->GetEcn());
1764
		if (empty($cflink['ecn'])) {
1765
			unset($cflink['ecn']);
1766
		}
1767
		if ($this->GetLinkshare() <> "") {
1768
			if ($this->GetL_m1() <> "") {
1769
				$cflink['linkshare1'] = $this->GetL_m1();
1770
				$cflink['linkshare2'] = $this->GetL_d();
1771
				$cflink['linkshare'] = "on";
1772
			} else {
1773
				unset($cflink['linkshare1']);
1774
				unset($cflink['linkshare2']);
1775
				unset($cflink['linkshare']);
1776
			}
1777
			if ($this->GetL_m2() <> "") {
1778
				$cflink['linkshare3'] = $this->GetL_m2();
1779
				$cflink['linkshare'] = "on";
1780
			} else {
1781
				unset($cflink['linkshare3']);
1782
				unset($cflink['linkshare']);
1783
			}
1784
		}
1785
		if ($this->GetRealtime() <> "") {
1786
			if ($this->GetR_m1() <> "") {
1787
				$cflink['realtime1'] = $this->GetR_m1();
1788
				$cflink['realtime2'] = $this->GetR_d();
1789
				$cflink['realtime'] = "on";
1790
			} else {
1791
				unset($cflink['realtime1']);
1792
				unset($cflink['realtime2']);
1793
				unset($cflink['realtime']);
1794
			}
1795
			if ($this->GetR_m2() <> "") {
1796
				$cflink['realtime3'] = $this->GetR_m2();
1797
				$cflink['realtime'] = "on";
1798
			} else {
1799
				unset($cflink['realtime3']);
1800
				unset($cflink['realtime']);
1801
			}
1802
		}
1803
		if ($this->GetUpperlimit() <> "") {
1804
			if ($this->GetU_m1() <> "") {
1805
				$cflink['upperlimit1'] = $this->GetU_m1();
1806
				$cflink['upperlimit2'] = $this->GetU_d();
1807
				$cflink['upperlimit'] = "on";
1808
			} else {
1809
				unset($cflink['upperlimit']);
1810
				unset($cflink['upperlimit1']);
1811
				unset($cflink['upperlimit2']);
1812
			}
1813
			if ($this->GetU_m2() <> "") {
1814
				$cflink['upperlimit3'] = $this->GetU_m2();
1815
				$cflink['upperlimit'] = "on";
1816
			} else {
1817
				unset($cflink['upperlimit3']);
1818
				unset($cflink['upperlimit']);
1819
			}
1820
		}
1821

    
1822
		if (is_array($this->subqueues)) {
1823
			$cflinkp['queue'] = array();
1824
			foreach ($this->subqueues as $q) {
1825
				$cflink['queue'][$q->GetQname()] = array();
1826
				$q->copy_queue($interface, $cflink['queue'][$q->GetQname()]);
1827
			}
1828
		}
1829
	}
1830

    
1831
	function delete_queue() {
1832
		unref_on_altq_queue_list($this->GetQname());
1833
		cleanup_queue_from_rules($this->GetQname());
1834
		$parent =& $this->GetParent();
1835
		foreach ($this->subqueues as $q) {
1836
			$this->SetAvailableBandwidth($this->GetAvailableBandwidth() + $q->GetAvailableBandwidth());
1837
				$q->delete_queue();
1838
		}
1839
		unset_object_by_reference($this->GetLink());
1840
	}
1841

    
1842
	/*
1843
	 * Should search even its children
1844
	 */
1845
	function &find_queue($interface, $qname) {
1846
		if ($qname == $this->GetQname()) {
1847
			return $this;
1848
		}
1849

    
1850
		foreach ($this->subqueues as $q) {
1851
			$result =& $q->find_queue("", $qname);
1852
			if ($result) {
1853
				return $result;
1854
			}
1855
		}
1856
	}
1857

    
1858
	function &find_parentqueue($interface, $qname) {
1859
		if ($this->subqueues[$qname]) {
1860
			return $this;
1861
		}
1862
		foreach ($this->subqueues as $q) {
1863
			$result = $q->find_parentqueue("", $qname);
1864
			if ($result) {
1865
				return $result;
1866
			}
1867
		}
1868
	}
1869

    
1870
	function validate_input($data, &$input_errors) {
1871
		parent::validate_input($data, $input_errors);
1872

    
1873
		$reqdfields[] = "bandwidth";
1874
		$reqdfieldsn[] = gettext("Bandwidth");
1875
		$reqdfields[] = "bandwidthtype";
1876
		$reqdfieldsn[] = gettext("Bandwidthtype");
1877

    
1878
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
1879

    
1880
		if (isset($data['linkshare3']) && $data['linkshare3'] <> "") {
1881
			if ($data['bandwidth'] && (!is_numeric($data['bandwidth']))) {
1882
				$input_errors[] = gettext("Bandwidth must be an integer.");
1883
			}
1884

    
1885
			if ($data['bandwidth'] < 0) {
1886
				$input_errors[] = gettext("Bandwidth cannot be negative.");
1887
			}
1888

    
1889
			if ($data['bandwidthtype'] == "%") {
1890
				if ($data['bandwidth'] > 100 || $data['bandwidth'] < 0) {
1891
					$input_errors[] = gettext("Bandwidth in percentage should be between 1 and 100 bounds.");
1892
				}
1893
			}
1894
		/*
1895
			$parent =& $this->GetParent();
1896
			switch ($data['bandwidthtype']) {
1897
			case "%":
1898
				$myBw = $parent->GetAvailableBandwidth() * floatval($data['bandwidth']) / 100;
1899
			default:
1900
				$mybw = floatval($data['bandwidth']) * get_bandwidthtype_scale($data['bandwidthtype']);
1901
				break;
1902
			}
1903
			if ($parent->GetAvailableBandwidth() < $myBw) {
1904
				$input_errors[] = "The sum of children bandwidth exceeds that of the parent.";
1905
			}
1906
		*/
1907
		}
1908

    
1909
		if ($data['upperlimit1'] <> "" && $data['upperlimit2'] == "") {
1910
			$input_errors[] = gettext("upperlimit service curve defined but missing (d) value");
1911
		}
1912
		if ($data['upperlimit2'] <> "" && $data['upperlimit1'] == "") {
1913
			$input_errors[] = gettext("upperlimit service curve defined but missing initial bandwidth (m1) value");
1914
		}
1915
		if ($data['upperlimit1'] <> "" && !is_valid_shaperbw($data['upperlimit1'])) {
1916
			$input_errors[] = gettext("upperlimit m1 value needs to be Kb, Mb, Gb, or %");
1917
		}
1918
		if ($data['upperlimit2'] <> "" && !is_numeric($data['upperlimit2'])) {
1919
			$input_errors[] = gettext("upperlimit d value needs to be numeric");
1920
		}
1921
		if ($data['upperlimit3'] <> "" && !is_valid_shaperbw($data['upperlimit3'])) {
1922
			$input_errors[] = gettext("upperlimit m2 value needs to be Kb, Mb, Gb, or %");
1923
		}
1924

    
1925
		/*
1926
		if (isset($data['upperlimit']) && $data['upperlimit3'] <> "" && $data['upperlimit1'] <> "") {
1927
			$bw_1 = get_hfsc_bandwidth($this, $data['upperlimit1']);
1928
			$bw_2 = get_hfsc_bandwidth($this, $data['upperlimit3']);
1929
			if (floatval($bw_1) < floatval($bw_2)) {
1930
				$input_errors[] = ("upperlimit m1 cannot be smaller than m2");
1931
			}
1932

    
1933
			if (get_interface_bandwidth($this) < (0.8 * (floatval($bw_1) + floatval($bw_2)))) {
1934
				$input_errors[] = ("upperlimit specification exceeds 80% of allowable allocation.");
1935
			}
1936
		}
1937
		*/
1938
		if ($data['linkshare1'] <> "" && $data['linkshare2'] == "") {
1939
			$input_errors[] = gettext("linkshare service curve defined but missing (d) value");
1940
		}
1941
		if ($data['linkshare2'] <> "" && $data['linkshare1'] == "") {
1942
			$input_errors[] = gettext("linkshare service curve defined but missing initial bandwidth (m1) value");
1943
		}
1944
		if ($data['linkshare1'] <> "" && !is_valid_shaperbw($data['linkshare1'])) {
1945
			$input_errors[] = gettext("linkshare m1 value needs to be Kb, Mb, Gb, or %");
1946
		}
1947
		if ($data['linkshare2'] <> "" && !is_numeric($data['linkshare2'])) {
1948
			$input_errors[] = gettext("linkshare d value needs to be numeric");
1949
		}
1950
		if ($data['linkshare3'] <> "" && !is_valid_shaperbw($data['linkshare3'])) {
1951
			$input_errors[] = gettext("linkshare m2 value needs to be Kb, Mb, Gb, or %");
1952
		}
1953
		if ($data['realtime1'] <> "" && $data['realtime2'] == "") {
1954
			$input_errors[] = gettext("realtime service curve defined but missing (d) value");
1955
		}
1956
		if ($data['realtime2'] <> "" && $data['realtime1'] == "") {
1957
			$input_errors[] = gettext("realtime service curve defined but missing initial bandwidth (m1) value");
1958
		}
1959

    
1960
		/*
1961
		if (isset($data['linkshare']) && $data['linkshare3'] <> "" && $data['linkshare1'] <> "" && 0) {
1962
			$bw_1 = get_hfsc_bandwidth($this, $data['linkshare1']);
1963
			$bw_2 = get_hfsc_bandwidth($this, $data['linkshare3']);
1964
			if (floatval($bw_1) < floatval($bw_2)) {
1965
				$input_errors[] = ("linkshare m1 cannot be smaller than m2");
1966
			}
1967

    
1968
			if (get_interface_bandwidth($this) < (0.8 * (floatval($bw_1) + floatval($bw_2)))) {
1969
				$input_errors[] = ("linkshare specification exceeds 80% of allowable allocation.");
1970
			}
1971
		}
1972
		*/
1973

    
1974
		if ($data['realtime1'] <> "" && !is_valid_shaperbw($data['realtime1'])) {
1975
			$input_errors[] = gettext("realtime m1 value needs to be Kb, Mb, Gb, or %");
1976
		}
1977
		if ($data['realtime2'] <> "" && !is_numeric($data['realtime2'])) {
1978
			$input_errors[] = gettext("realtime d value needs to be numeric");
1979
		}
1980
		if ($data['realtime3'] <> "" && !is_valid_shaperbw($data['realtime3'])) {
1981
			$input_errors[] = gettext("realtime m2 value needs to be Kb, Mb, Gb, or %");
1982
		}
1983

    
1984
		/*
1985
		if (isset($data['realtime']) && $data['realtime3'] <> "" && $data['realtime1'] <> "" && 0) {
1986
			$bw_1 = get_hfsc_bandwidth($this, $data['realtime1']);
1987
			$bw_2 = get_hfsc_bandwidth($this, $data['realtime3']);
1988
			if (floatval($bw_1) < floatval($bw_2)) {
1989
				$input_errors[] = ("realtime m1 cannot be smaller than m2");
1990
			}
1991

    
1992
			if (get_interface_bandwidth($this) < (0.8 * (floatval($bw_1) + floatval($bw_2)))) {
1993
				$input_errors[] = ("realtime specification exceeds 80% of allowable allocation.");
1994
			}
1995
		}
1996
		*/
1997
	}
1998

    
1999
	function ReadConfig(&$cflink) {
2000
		if (!empty($cflink['linkshare'])) {
2001
			if (!empty($cflink['linkshare1'])) {
2002
				$this->SetL_m1($cflink['linkshare1']);
2003
				$this->SetL_d($cflink['linkshare2']);
2004
				$this->SetLinkshare();
2005
			} else {
2006
				$this->SetL_m1("");
2007
				$this->SetL_d("");
2008
				$this->DisableLinkshare();
2009
			}
2010
			if (!empty($cflink['linkshare3'])) {
2011
				$this->SetL_m2($cflink['linkshare3']);
2012
				$this->SetLinkshare();
2013
			}
2014
		} else {
2015
			$this->DisableLinkshare();
2016
		}
2017
		if (!empty($cflink['realtime'])) {
2018
			if (!empty($cflink['realtime1'])) {
2019
				$this->SetR_m1($cflink['realtime1']);
2020
				$this->SetR_d($cflink['realtime2']);
2021
				$this->SetRealtime();
2022
			} else {
2023
				$this->SetR_m1("");
2024
				$this->SetR_d("");
2025
				$this->DisableRealtime();
2026
			}
2027
			if (!empty($cflink['realtime3'])) {
2028
				$this->SetR_m2($cflink['realtime3']);
2029
				$this->SetRealtime();
2030
			}
2031
		} else {
2032
			$this->DisableRealtime();
2033
		}
2034
		if (!empty($cflink['upperlimit'])) {
2035
			if (!empty($cflink['upperlimit1'])) {
2036
				$this->SetU_m1($cflink['upperlimit1']);
2037
				$this->SetU_d($cflink['upperlimit2']);
2038
				$this->SetUpperlimit();
2039
			} else {
2040
				$this->SetU_m1("");
2041
				$this->SetU_d("");
2042
				$this->DisableUpperlimit();
2043
			}
2044
			if (!empty($cflink['upperlimit3'])) {
2045
				$this->SetU_m2($cflink['upperlimit3']);
2046
				$this->SetUpperlimit();
2047
			}
2048
		} else {
2049
			$this->DisableUpperlimit();
2050
		}
2051
		parent::ReadConfig($cflink);
2052
	}
2053

    
2054
	function build_tree() {
2055
		$tree = " <li><a href=\"firewall_shaper.php?interface=" . $this->GetInterface() ."&amp;queue=" . $this->GetQname()."&amp;action=show";
2056
		$tree .= "\" ";
2057
		$tmpvalue = $this->GetDefault();
2058
		if (!empty($tmpvalue)) {
2059
			$tree .= " class=\"navlnk\"";
2060
		}
2061
		$tree .= " >" . $this->GetQname() . "</a>";
2062
		if (is_array($this->subqueues)) {
2063
			$tree .= "<ul>";
2064
			foreach ($this->subqueues as $q) {
2065
				$tree .= $q->build_tree();
2066
			}
2067
			$tree .= "</ul>";
2068
		}
2069
		$tree .= "</li>";
2070
		return $tree;
2071
	}
2072

    
2073
	/* Even this should take children into consideration */
2074
	function build_rules(&$default = false) {
2075

    
2076
		$pfq_rule = " queue ". $this->qname;
2077
		if ($this->GetInterface()) {
2078
			$pfq_rule .= " on ".get_real_interface($this->GetInterface());
2079
		}
2080
		if ($this->GetBandwidth() && $this->GetBwscale()) {
2081
			$pfq_rule .= " bandwidth ".trim($this->GetBandwidth()).$this->GetBwscale();
2082
		}
2083

    
2084
		$tmpvalue = $this->GetQlimit();
2085
		if (!empty($tmpvalue)) {
2086
			$pfq_rule .= " qlimit " . $this->GetQlimit();
2087
		}
2088
		if ($this->GetDefault() || $this->GetRed() || $this->GetRio() || $this->GetEcn() || $this->GetCodel() || $this->GetRealtime() <> "" || $this->GetLinkshare() <> "" || $this->GetUpperlimit() <> "") {
2089
			$pfq_rule .= " hfsc ( ";
2090
			$tmpvalue = $this->GetRed();
2091
			if (!empty($tmpvalue)) {
2092
				$comma = 1;
2093
				$pfq_rule .= " red ";
2094
			}
2095

    
2096
			$tmpvalue = $this->GetRio();
2097
			if (!empty($tmpvalue)) {
2098
				if ($comma) {
2099
					$pfq_rule .= " ,";
2100
				}
2101
				$comma = 1;
2102
				$pfq_rule .= " rio ";
2103
			}
2104
			$tmpvalue = $this->GetEcn();
2105
			if (!empty($tmpvalue)) {
2106
				if ($comma) {
2107
					$pfq_rule .= " ,";
2108
				}
2109
				$comma = 1;
2110
				$pfq_rule .= " ecn ";
2111
			}
2112
			$tmpvalue = $this->GetCodel();
2113
			if (!empty($tmpvalue)) {
2114
				if ($comma) {
2115
					$pfq_rule .= " ,";
2116
				}
2117
				$comma = 1;
2118
				$pfq_rule .= " codel ";
2119
			}
2120
			$tmpvalue = $this->GetDefault();
2121
			if (!empty($tmpvalue)) {
2122
				if ($comma) {
2123
					$pfq_rule .= " ,";
2124
				}
2125
				$comma = 1;
2126
				$pfq_rule .= " default ";
2127
				$default = true;
2128
			}
2129

    
2130
			if ($this->GetRealtime() <> "") {
2131
				if ($comma) {
2132
					$pfq_rule .= " , ";
2133
				}
2134
				if ($this->GetR_m1() <> "" && $this->GetR_d() <> "" && $this->GetR_m2() <> "") {
2135
					$pfq_rule .= " realtime (".$this->GetR_m1() . ", " . $this->GetR_d().", ". $this->GetR_m2() .") ";
2136
				} else if ($this->GetR_m2() <> "") {
2137
					$pfq_rule .= " realtime " . $this->GetR_m2();
2138
				}
2139
				$comma = 1;
2140
			}
2141
			if ($this->GetLinkshare() <> "") {
2142
				if ($comma) {
2143
					$pfq_rule .= " ,";
2144
				}
2145
				if ($this->GetL_m1() <> "" && $this->GetL_d() <> "" && $this->GetL_m2() <> "") {
2146
					$pfq_rule .= " linkshare (".$this->GetL_m1(). ", ". $this->GetL_d(). ", ". $this->GetL_m2(). ") ";
2147
				} else if ($this->GetL_m2() <> "") {
2148
					$pfq_rule .= " linkshare " . $this->GetL_m2() . " ";
2149
				}
2150
				$comma = 1;
2151
			}
2152
			if ($this->GetUpperlimit() <> "") {
2153
				if ($comma) {
2154
					$pfq_rule .= " ,";
2155
				}
2156
				if ($this->GetU_m1() <> "" && $this->GetU_d() <> "" && $this->GetU_m2() <> "") {
2157
							$pfq_rule .= " upperlimit (".$this->GetU_m1().", ". $this->GetU_d().", ". $this->GetU_m2(). ") ";
2158
				} else if ($this->GetU_m2() <> "") {
2159
					$pfq_rule .= " upperlimit " . $this->GetU_m2() . " ";
2160
				}
2161
			}
2162
			$pfq_rule .= " ) ";
2163
		}
2164
		if (count($this->subqueues)) {
2165
			$i = count($this->subqueues);
2166
			$pfq_rule .= " { ";
2167
			foreach ($this->subqueues as $qkey => $qnone) {
2168
				if ($i > 1) {
2169
					$i--;
2170
					$pfq_rule .= " {$qkey}, ";
2171
				} else {
2172
					$pfq_rule .= " {$qkey} ";
2173
				}
2174
			}
2175
			$pfq_rule .= " } \n";
2176
			foreach ($this->subqueues as $q) {
2177
				$pfq_rule .= $q->build_rules($default);
2178
			}
2179
		}
2180

    
2181
		$pfq_rule .= " \n";
2182

    
2183
		return $pfq_rule;
2184
	}
2185

    
2186
	function build_javascript() {
2187

    
2188
		$javascript = <<<EOJS
2189
<script type="text/javascript">
2190
//<![CDATA[
2191
	events.push(function(){
2192

    
2193
		// Disables the specified input element
2194
		function disableInput(id, disable) {
2195
			$('#' + id).prop("disabled", disable);
2196
		}
2197

    
2198
		// Upperlimit
2199
		function enable_upperlimit() {
2200
			disableInput('upperlimit1', !$('#upperlimit').prop('checked'));
2201
			disableInput('upperlimit2', !$('#upperlimit').prop('checked'));
2202
			disableInput('upperlimit3', !$('#upperlimit').prop('checked'));
2203
		}
2204

    
2205
		$('#upperlimit').click(function () {
2206
			enable_upperlimit();
2207
		});
2208

    
2209
		enable_upperlimit();
2210

    
2211
		// realtime
2212
		function enable_realtime() {
2213
			disableInput('realtime1', !$('#realtime').prop('checked'));
2214
			disableInput('realtime2', !$('#realtime').prop('checked'));
2215
			disableInput('realtime3', !$('#realtime').prop('checked'));
2216
		}
2217

    
2218
		$('#realtime').click(function () {
2219
			enable_realtime();
2220
		});
2221

    
2222
		enable_realtime();
2223

    
2224
		// linkshare
2225
		function enable_linkshare() {
2226
			disableInput('linkshare1', !$('#linkshare').prop('checked'));
2227
			disableInput('linkshare2', !$('#linkshare').prop('checked'));
2228
			disableInput('linkshare3', !$('#linkshare').prop('checked'));
2229
		}
2230

    
2231
		$('#linkshare').click(function () {
2232
			enable_linkshare();
2233
		});
2234

    
2235
		enable_linkshare();
2236
	});
2237
//]]>
2238
</script>
2239
EOJS;
2240

    
2241
		return $javascript;
2242
	}
2243

    
2244
	function build_form() {
2245

    
2246
		$sform = parent::build_form();
2247

    
2248
		$section = new Form_Section('Service Curve (sc)');
2249

    
2250
		$group = new Form_Group('Bandwidth');
2251

    
2252
		$group->add(new Form_Input(
2253
			'bandwidth',
2254
			null,
2255
			'number',
2256
			$this->GetBandwidth()
2257
		));
2258

    
2259
		$group->add(new Form_Select(
2260
			'bandwidthtype',
2261
			null,
2262
			$this->GetBwscale(),
2263
			array('Kb' => 'Kbit/s',
2264
				  'Mb' => 'Mbit/s',
2265
				  'Gb' => 'Gbit/s',
2266
				  'b' => 'Bit/s',
2267
				  '%' => '%')
2268
		));
2269

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

    
2272
		$section->add($group);
2273

    
2274
		$group = new Form_Group('Max bandwidth for queue.');
2275

    
2276
		$group->add(new Form_Checkbox(
2277
			'upperlimit',
2278
			null,
2279
			'Upper Limit',
2280
			($this->GetUpperlimit()<> "")
2281
		));
2282

    
2283
		$group->add(new Form_Input(
2284
			'upperlimit1',
2285
			null,
2286
			'text',
2287
			$this->GetU_m1()
2288
		))->setHelp('m1');
2289

    
2290
		$group->add(new Form_Input(
2291
			'upperlimit2',
2292
			null,
2293
			'text',
2294
			$this->GetU_d()
2295
		))->setHelp('d');
2296

    
2297
		$group->add(new Form_Input(
2298
			'upperlimit3',
2299
			null,
2300
			'text',
2301
			$this->GetU_m2()
2302
		))->setHelp('m2');
2303

    
2304

    
2305
		$section->add($group);
2306

    
2307
		$group = new Form_Group('Min bandwidth for queue.');
2308

    
2309
		$group->add(new Form_Checkbox(
2310
			'realtime',
2311
			null,
2312
			'Real Time',
2313
			($this->GetRealtime()<> "")
2314
		));
2315

    
2316
		$group->add(new Form_Input(
2317
			'realtime1',
2318
			null,
2319
			'text',
2320
			$this->GetR_m1()
2321
		))->setHelp('m1');
2322

    
2323
		$group->add(new Form_Input(
2324
			'realtime2',
2325
			null,
2326
			'text',
2327
			$this->GetR_d()
2328
		))->setHelp('d');
2329

    
2330
		$group->add(new Form_Input(
2331
			'realtime3',
2332
			null,
2333
			'text',
2334
			$this->GetR_m2()
2335
		))->setHelp('m2');
2336

    
2337
		$section->add($group);
2338

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

    
2341
		$group->add(new Form_Checkbox(
2342
			'linkshare',
2343
			null,
2344
			'Link Share',
2345
			($this->GetLinkshare()<> "")
2346
		));
2347

    
2348
		$group->add(new Form_Input(
2349
			'linkshare1',
2350
			null,
2351
			'text',
2352
			$this->GetL_m1()
2353
		))->setHelp('m1');
2354

    
2355
		$group->add(new Form_Input(
2356
			'linkshare2',
2357
			null,
2358
			'text',
2359
			$this->GetL_d()
2360
		))->setHelp('d');
2361

    
2362
		$group->add(new Form_Input(
2363
			'linkshare3',
2364
			null,
2365
			'text',
2366
			$this->GetL_m2()
2367
		))->setHelp('m2');
2368

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

    
2374
		$section->add($group);
2375

    
2376
		$sform->add($section);
2377

    
2378
		return($sform);
2379
	}
2380

    
2381
	function update_altq_queue_data(&$data) {
2382
		$this->ReadConfig($data);
2383
	}
2384

    
2385
	function wconfig() {
2386
		$cflink =& get_reference_to_me_in_config($this->GetLink());
2387
		if (!is_array($cflink)) {
2388
			$cflink = array();
2389
		}
2390
		$cflink['name'] = $this->GetQname();
2391
		$cflink['interface'] = $this->GetInterface();
2392
		$cflink['qlimit'] = trim($this->GetQlimit());
2393
		if (empty($cflink['qlimit'])) {
2394
			unset($cflink['qlimit']);
2395
		}
2396
		$cflink['priority'] = $this->GetQpriority();
2397
		if (empty($cflink['priority'])) {
2398
			unset($cflink['priority']);
2399
		}
2400
		$cflink['description'] = $this->GetDescription();
2401
		if (empty($cflink['description'])) {
2402
			unset($cflink['description']);
2403
		}
2404
		$cflink['bandwidth'] = $this->GetBandwidth();
2405
		$cflink['bandwidthtype'] = $this->GetBwscale();
2406
		$cflink['enabled'] = $this->GetEnabled();
2407
		if (empty($cflink['enabled'])) {
2408
			unset($cflink['enabled']);
2409
		}
2410
		$cflink['default'] = $this->GetDefault();
2411
		if (empty($cflink['default'])) {
2412
			unset($cflink['default']);
2413
		}
2414
		$cflink['red'] = trim($this->GetRed());
2415
		if (empty($cflink['red'])) {
2416
			unset($cflink['red']);
2417
		}
2418
		$cflink['rio'] = $this->GetRio();
2419
		if (empty($cflink['rio'])) {
2420
			unset($cflink['rio']);
2421
		}
2422
		$cflink['ecn'] = trim($this->GetEcn());
2423
		if (empty($cflink['ecn'])) {
2424
			unset($cflink['ecn']);
2425
		}
2426
		$cflink['codel'] = trim($this->GetCodel());
2427
		if (empty($cflink['codel'])) {
2428
			unset($cflink['codel']);
2429
		}
2430
		if ($this->GetLinkshare() <> "") {
2431
			if ($this->GetL_m1() <> "") {
2432
				$cflink['linkshare1'] = $this->GetL_m1();
2433
				$cflink['linkshare2'] = $this->GetL_d();
2434
				$cflink['linkshare'] = "on";
2435
			} else {
2436
				unset($cflink['linkshare']);
2437
				unset($cflink['linkshare1']);
2438
				unset($cflink['linkshare2']);
2439
			}
2440
			if ($this->GetL_m2() <> "") {
2441
				$cflink['linkshare3'] = $this->GetL_m2();
2442
				$cflink['linkshare'] = "on";
2443
			} else {
2444
				unset($cflink['linkshare']);
2445
				unset($cflink['linkshare3']);
2446
			}
2447
		} else {
2448
			unset($cflink['linkshare']);
2449
			unset($cflink['linkshare1']);
2450
			unset($cflink['linkshare2']);
2451
			unset($cflink['linkshare3']);
2452
		}
2453
		if ($this->GetRealtime() <> "") {
2454
			if ($this->GetR_m1() <> "") {
2455
				$cflink['realtime1'] = $this->GetR_m1();
2456
				$cflink['realtime2'] = $this->GetR_d();
2457
				$cflink['realtime'] = "on";
2458
			} else {
2459
				unset($cflink['realtime']);
2460
				unset($cflink['realtime1']);
2461
				unset($cflink['realtime2']);
2462
			}
2463
			if ($this->GetR_m2() <> "") {
2464
				$cflink['realtime3'] = $this->GetR_m2();
2465
				$cflink['realtime'] = "on";
2466
			} else {
2467
				unset($cflink['realtime']);
2468
				unset($cflink['realtime3']);
2469
			}
2470
		} else {
2471
			unset($cflink['realtime']);
2472
			unset($cflink['realtime1']);
2473
			unset($cflink['realtime2']);
2474
			unset($cflink['realtime3']);
2475
		}
2476
		if ($this->GetUpperlimit() <> "") {
2477
			if ($this->GetU_m1() <> "") {
2478
				$cflink['upperlimit1'] = $this->GetU_m1();
2479
				$cflink['upperlimit2'] = $this->GetU_d();
2480
				$cflink['upperlimit'] = "on";
2481
			} else {
2482
				unset($cflink['upperlimit']);
2483
				unset($cflink['upperlimit1']);
2484
				unset($cflink['upperlimit2']);
2485
			}
2486
			if ($this->GetU_m2() <> "") {
2487
				$cflink['upperlimit3'] = $this->GetU_m2();
2488
				$cflink['upperlimit'] = "on";
2489
			} else {
2490
				unset($cflink['upperlimit']);
2491
				unset($cflink['upperlimit3']);
2492
			}
2493
		} else {
2494
			unset($cflink['upperlimit']);
2495
			unset($cflink['upperlimit1']);
2496
			unset($cflink['upperlimit2']);
2497
			unset($cflink['upperlimit3']);
2498
		}
2499
	}
2500
}
2501

    
2502
class cbq_queue extends priq_queue {
2503
	var $qborrow = "";
2504

    
2505
	function GetBorrow() {
2506
		return $this->qborrow;
2507
	}
2508
	function SetBorrow($borrow) {
2509
		$this->qborrow = $borrow;
2510
	}
2511
	function CanHaveChildren() {
2512
		return true;
2513
	}
2514

    
2515
	function &add_queue($interface, &$qname, &$path, &$input_errors) {
2516

    
2517
		if (!is_array($this->subqueues)) {
2518
			$this->subqueues = array();
2519
		}
2520
		$q =& new cbq_queue();
2521
		$q->SetInterface($this->GetInterface());
2522
		$q->SetParent($this);
2523
		$q->ReadConfig($qname);
2524
		$q->validate_input($qname, $input_errors);
2525
		if (count($input_errors)) {
2526
			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)));
2527
			return $q;
2528
		}
2529
		switch ($q->GetBwscale()) {
2530
			case "%":
2531
				$myBw = $this->GetAvailableBandwidth() * $qname['bandwidth'] / 100;
2532
				break;
2533
			default:
2534
				$myBw = $qname['bandwidth'] * get_bandwidthtype_scale($q->GetBwscale());
2535
				break;
2536
		}
2537
		$q->SetAvailableBandwidth($myBw);
2538
		$this->SetAvailableBandwidth($this->GetAvailableBandwidth() - $myBw);
2539

    
2540
		$q->SetEnabled("on");
2541
		$q->SetLink($path);
2542
		$this->subqueues[$q->GetQName()] = &$q;
2543
		ref_on_altq_queue_list($this->GetQname(), $q->GetQname());
2544
		if (is_array($qname['queue'])) {
2545
			foreach ($qname['queue'] as $key1 => $que) {
2546
				array_push($path, $key1);
2547
				$q->add_queue($q->GetInterface(), $que, $path, $input_errors);
2548
				array_pop($path);
2549
			}
2550
		}
2551

    
2552
		return $q;
2553
	}
2554

    
2555
	function copy_queue($interface, &$cflink) {
2556

    
2557
		$cflink['interface'] = $interface;
2558
		$cflink['qlimit'] = trim($this->GetQlimit());
2559
		if (empty($clink['qlimit'])) {
2560
			unset($cflink['qlimit']);
2561
		}
2562
		$cflink['priority'] = trim($this->GetQpriority());
2563
		if (empty($cflink['priority'])) {
2564
			unset($cflink['priority']);
2565
		}
2566
		$cflink['name'] = $this->GetQname();
2567
		$cflink['description'] = trim($this->GetDescription());
2568
		if (empty($cflink['description'])) {
2569
			unset($cflink['description']);
2570
		}
2571
		$cflink['bandwidth'] = $this->GetBandwidth();
2572
		$cflink['bandwidthtype'] = $this->GetBwscale();
2573
		$cflink['enabled'] = trim($this->GetEnabled());
2574
		if (empty($cflink['enabled'])) {
2575
			unset($cflink['enabled']);
2576
		}
2577
		$cflink['default'] = trim($this->GetDefault());
2578
		if (empty($cflink['default'])) {
2579
			unset($cflink['default']);
2580
		}
2581
		$cflink['red'] = trim($this->GetRed());
2582
		if (empty($cflink['red'])) {
2583
			unset($cflink['red']);
2584
		}
2585
		$cflink['rio'] = trim($this->GetRio());
2586
		if (empty($cflink['rio'])) {
2587
			unset($cflink['rio']);
2588
		}
2589
		$cflink['ecn'] = trim($this->GetEcn());
2590
		if (empty($cflink['ecn'])) {
2591
			unset($cflink['ecn']);
2592
		}
2593
		$cflink['borrow'] = trim($this->GetBorrow());
2594
		if (empty($cflink['borrow'])) {
2595
			unset($cflink['borrow']);
2596
		}
2597
		if (is_array($this->queues)) {
2598
			$cflinkp['queue'] = array();
2599
			foreach ($this->subqueues as $q) {
2600
				$cflink['queue'][$q->GetQname()] = array();
2601
				$q->copy_queue($interface, $cflink['queue'][$q->GetQname()]);
2602
			}
2603
		}
2604
	}
2605

    
2606
	/*
2607
	 * Should search even its children
2608
	 */
2609
	function &find_queue($interface, $qname) {
2610
		if ($qname == $this->GetQname()) {
2611
			return $this;
2612
		}
2613
		foreach ($this->subqueues as $q) {
2614
			$result =& $q->find_queue("", $qname);
2615
			if ($result) {
2616
				return $result;
2617
			}
2618
		}
2619
	}
2620

    
2621
	function &find_parentqueue($interface, $qname) {
2622
		if ($this->subqueues[$qname]) {
2623
			return $this;
2624
		}
2625
		foreach ($this->subqueues as $q) {
2626
			$result = $q->find_parentqueue("", $qname);
2627
			if ($result) {
2628
				return $result;
2629
			}
2630
		}
2631
	}
2632

    
2633
	function delete_queue() {
2634
		unref_on_altq_queue_list($this->GetQname());
2635
		cleanup_queue_from_rules($this->GetQname());
2636
		foreach ($this->subqueues as $q) {
2637
			$this->SetAvailableBandwidth($this->GetAvailableBandwidth() + $q->GetAvailableBandwidth());
2638
				$q->delete_queue();
2639
		}
2640
		unset_object_by_reference($this->GetLink());
2641
	}
2642

    
2643
	function validate_input($data, &$input_errors) {
2644
		parent::validate_input($data, $input_errors);
2645

    
2646
		if ($data['priority'] > 7) {
2647
				$input_errors[] = gettext("Priority must be an integer between 1 and 7.");
2648
		}
2649
		$reqdfields[] = "bandwidth";
2650
		$reqdfieldsn[] = gettext("Bandwidth");
2651
		$reqdfields[] = "bandwidthtype";
2652
		$reqdfieldsn[] = gettext("Bandwidthtype");
2653

    
2654
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
2655

    
2656
		if ($data['bandwidth'] && !is_numeric($data['bandwidth'])) {
2657
			$input_errors[] = gettext("Bandwidth must be an integer.");
2658
		}
2659

    
2660

    
2661
		if ($data['bandwidth'] < 0) {
2662
			$input_errors[] = gettext("Bandwidth cannot be negative.");
2663
		}
2664

    
2665
		if ($data['bandwidthtype'] == "%") {
2666
			if ($data['bandwidth'] > 100 || $data['bandwidth'] < 0) {
2667
				$input_errors[] = gettext("Bandwidth in percentage should be between 1 and 100 bounds.");
2668
			}
2669
		}
2670

    
2671
/*
2672
		$parent =& $this->GetParent();
2673
		switch ($data['bandwidthtype']) {
2674
		case "%":
2675
			$myBw = $parent->GetAvailableBandwidth() * floatval($data['bandwidth']) / 100;
2676
			break;
2677
		default:
2678
			$mybw = floatval($data['bandwidth']) * get_bandwidthtype_scale($data['bandwidthtype']);
2679
			break;
2680
		}
2681
		if ($parent->GetAvailableBandwidth() < floatval($myBw)) {
2682
			$input_errors[] = "The sum of the children bandwidth exceeds that of the parent.";
2683
		}
2684
 */
2685
	}
2686

    
2687
	function ReadConfig(&$q) {
2688
		parent::ReadConfig($q);
2689
		if (!empty($q['borrow'])) {
2690
			$this->SetBorrow("on");
2691
		} else {
2692
			$this->SetBorrow("");
2693
		}
2694
	}
2695

    
2696
	function build_javascript() {
2697
		return parent::build_javascript();
2698
	}
2699

    
2700
	function build_tree() {
2701
		$tree = " <li><a href=\"firewall_shaper.php?interface=" . $this->GetInterface()."&amp;queue=" . $this->GetQname()."&amp;action=show";
2702
		$tree .= "\" ";
2703
		$tmpvalue = trim($this->GetDefault());
2704
		if (!empty($tmpvalue)) {
2705
			$tree .= " class=\"navlnk\"";
2706
		}
2707
		$tree .= " >" . $this->GetQname() . "</a>";
2708
		if (is_array($this->subqueues)) {
2709
			$tree .= "<ul>";
2710
			foreach ($this->subqueues as $q) {
2711
				$tree .= $q->build_tree();
2712
			}
2713
			$tree .= "</ul>";
2714
		}
2715
		$tree .= "</li>";
2716
		return $tree;
2717
	}
2718

    
2719
	/* Even this should take children into consideration */
2720
	function build_rules(&$default = false) {
2721
		$pfq_rule = "queue ". $this->qname;
2722
		if ($this->GetInterface()) {
2723
			$pfq_rule .= " on ".get_real_interface($this->GetInterface());
2724
		}
2725
		if ($this->GetBandwidth() && $this->GetBwscale()) {
2726
			$pfq_rule .= " bandwidth ".trim($this->GetBandwidth()).$this->GetBwscale();
2727
		}
2728
		$tmpvalue = $this->GetQpriority();
2729
		if (!empty($tmpvalue)) {
2730
			$pfq_rule .= " priority " . $this->GetQpriority();
2731
		}
2732
		$tmpvalue = trim($this->GetQlimit());
2733
		if (!empty($tmpvalue)) {
2734
			$pfq_rule .= " qlimit " . $this->GetQlimit();
2735
		}
2736
		if ($this->GetDefault() || $this->GetRed() || $this->GetRio() || $this->GetEcn() || $this->GetBorrow() || $this->GetCodel()) {
2737
			$pfq_rule .= " cbq ( ";
2738
			$tmpvalue = trim($this->GetRed());
2739
			if (!empty($tmpvalue)) {
2740
				$comma = 1;
2741
				$pfq_rule .= " red ";
2742
			}
2743
			$tmpvalue = trim($this->GetCodel());
2744
			if (!empty($tmpvalue)) {
2745
				$comma = 1;
2746
				$pfq_rule .= " codel ";
2747
			}
2748
			$tmpvalue = trim($this->GetRio());
2749
			if (!empty($tmpvalue)) {
2750
				if ($comma) {
2751
					$pfq_rule .= " ,";
2752
				}
2753
				$comma = 1;
2754
				$pfq_rule .= " rio ";
2755
			}
2756
			$tmpvalue = trim($this->GetEcn());
2757
			if (!empty($tmpvalue)) {
2758
				if ($comma) {
2759
					$pfq_rule .= " ,";
2760
				}
2761
				$comma = 1;
2762
				$pfq_rule .= " ecn ";
2763
			}
2764
			$tmpvalue = trim($this->GetDefault());
2765
			if (!empty($tmpvalue)) {
2766
				if ($comma) {
2767
					$pfq_rule .= " ,";
2768
				}
2769
				$comma = 1;
2770
				$pfq_rule .= " default ";
2771
				$default = true;
2772
			}
2773
			$tmpvalue = trim($this->GetBorrow());
2774
			if (!empty($tmpvalue)) {
2775
				if ($comma) {
2776
					$pfq_rule .= ", ";
2777
				}
2778
				$pfq_rule .= " borrow ";
2779
			}
2780
			$pfq_rule .= " ) ";
2781
		}
2782
		if (count($this->subqueues)) {
2783
			$i = count($this->subqueues);
2784
			$pfq_rule .= " { ";
2785
			foreach ($this->subqueues as $qkey => $qnone) {
2786
				if ($i > 1) {
2787
					$i--;
2788
					$pfq_rule .= " {$qkey}, ";
2789
				} else {
2790
					$pfq_rule .= " {$qkey} ";
2791
				}
2792
			}
2793
			$pfq_rule .= " } \n";
2794
			foreach ($this->subqueues as $q) {
2795
				$pfq_rule .= $q->build_rules($default);
2796
			}
2797
		}
2798

    
2799
		$pfq_rule .= " \n";
2800
		return $pfq_rule;
2801
	}
2802

    
2803
	function build_form() {
2804
		$sform = parent::build_form();
2805

    
2806
		$section = new Form_Section('');
2807

    
2808
		$group = new Form_Group('Bandwidth');
2809

    
2810
		$group->add(new Form_Input(
2811
			'bandwidth',
2812
			null,
2813
			'number',
2814
			$this->GetBandwidth()
2815
		));
2816

    
2817
		$group->add(new Form_Select(
2818
			'bandwidthtype',
2819
			null,
2820
			$this->GetBwscale(),
2821
			array('Kb' => 'Kbit/s',
2822
				  'Mb' => 'Mbit/s',
2823
				  'Gb' => 'Gbit/s',
2824
				  'b' => 'Bit/s',
2825
				  '%' => '%')
2826
		));
2827

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

    
2830
		$section->add($group);
2831

    
2832
		$section->addInput(new Form_Checkbox(
2833
			'borrow',
2834
			'Scheduler option',
2835
			'Borrow from other queues when available',
2836
			($this->GetBorrow() == "on")
2837
		));
2838

    
2839
		return $sform;
2840
	}
2841

    
2842
	function update_altq_queue_data(&$data) {
2843
		$this->ReadConfig($data);
2844
	}
2845

    
2846
	function wconfig() {
2847
		$cflink =& get_reference_to_me_in_config($this->GetLink());
2848
		if (!is_array($cflink)) {
2849
			$cflink = array();
2850
		}
2851
		$cflink['interface'] = $this->GetInterface();
2852
		$cflink['qlimit'] = trim($this->GetQlimit());
2853
		if (empty($cflink['qlimit'])) {
2854
			unset($cflink['qlimit']);
2855
		}
2856
		$cflink['priority'] = $this->GetQpriority();
2857
		if (empty($cflink['priority'])) {
2858
			unset($cflink['priority']);
2859
		}
2860
		$cflink['name'] = $this->GetQname();
2861
		$cflink['description'] = $this->GetDescription();
2862
		if (empty($cflink['description'])) {
2863
			unset($cflink['description']);
2864
		}
2865
		$cflink['bandwidth'] = $this->GetBandwidth();
2866
		$cflink['bandwidthtype'] = $this->GetBwscale();
2867
		$cflink['enabled'] = trim($this->GetEnabled());
2868
		if (empty($cflink['enabled'])) {
2869
			unset($cflink['enabled']);
2870
		}
2871
		$cflink['default'] = trim($this->GetDefault());
2872
		if (empty($cflink['default'])) {
2873
			unset($cflink['default']);
2874
		}
2875
		$cflink['red'] = trim($this->GetRed());
2876
		if (empty($cflink['red'])) {
2877
			unset($cflink['red']);
2878
		}
2879
		$cflink['rio'] = trim($this->GetRio());
2880
		if (empty($cflink['rio'])) {
2881
			unset($cflink['rio']);
2882
		}
2883
		$cflink['ecn'] = trim($this->GetEcn());
2884
		if (empty($cflink['ecn'])) {
2885
			unset($cflink['ecn']);
2886
		}
2887
		$cflink['codel'] = trim($this->GetCodel());
2888
		if (empty($cflink['codel'])) {
2889
			unset($cflink['codel']);
2890
		}
2891
		$cflink['borrow'] = trim($this->GetBorrow());
2892
		if (empty($cflink['borrow'])) {
2893
			unset($cflink['borrow']);
2894
		}
2895
	}
2896
}
2897

    
2898
class fairq_queue extends priq_queue {
2899
	var $hogs;
2900
	var $buckets;
2901

    
2902
	function GetBuckets() {
2903
		return $this->buckets;
2904
	}
2905
	function SetBuckets($buckets) {
2906
		$this->buckets = $buckets;
2907
	}
2908
	function GetHogs() {
2909
		return $this->hogs;
2910
	}
2911
	function SetHogs($hogs) {
2912
		$this->hogs = $hogs;
2913
	}
2914
	function CanHaveChildren() {
2915
		return false;
2916
	}
2917

    
2918

    
2919
	function copy_queue($interface, &$cflink) {
2920
		$cflink['interface'] = $interface;
2921
		$cflink['qlimit'] = $this->GetQlimit();
2922
		$cflink['priority'] = $this->GetQpriority();
2923
		$cflink['name'] = $this->GetQname();
2924
		$cflink['description'] = $this->GetDescription();
2925
		$cflink['bandwidth'] = $this->GetBandwidth();
2926
		$cflink['bandwidthtype'] = $this->GetBwscale();
2927
		$cflink['enabled'] = $this->GetEnabled();
2928
		$cflink['default'] = $this->GetDefault();
2929
		$cflink['red'] = $this->GetRed();
2930
		$cflink['rio'] = $this->GetRio();
2931
		$cflink['ecn'] = $this->GetEcn();
2932
		$cflink['buckets'] = $this->GetBuckets();
2933
		$cflink['hogs'] = $this->GetHogs();
2934
	}
2935

    
2936
	/*
2937
	 * Should search even its children
2938
	 */
2939
	function &find_queue($interface, $qname) {
2940
		if ($qname == $this->GetQname()) {
2941
			return $this;
2942
		}
2943
	}
2944

    
2945
	function find_parentqueue($interface, $qname) { return; }
2946

    
2947
	function delete_queue() {
2948
		unref_on_altq_queue_list($this->GetQname());
2949
		cleanup_queue_from_rules($this->GetQname());
2950
		unset_object_by_reference($this->GetLink());
2951
	}
2952

    
2953
	function validate_input($data, &$input_errors) {
2954
		parent::validate_input($data, $input_errors);
2955

    
2956
		if ($data['priority'] > 255) {
2957
				$input_errors[] = gettext("Priority must be an integer between 1 and 255.");
2958
		}
2959
		$reqdfields[] = "bandwidth";
2960
		$reqdfieldsn[] = gettext("Bandwidth");
2961
		$reqdfields[] = "bandwidthtype";
2962
		$reqdfieldsn[] = gettext("Bandwidthtype");
2963

    
2964
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
2965

    
2966
		if ($data['bandwidth'] && !is_numeric($data['bandwidth'])) {
2967
			$input_errors[] = gettext("Bandwidth must be an integer.");
2968
		}
2969

    
2970

    
2971
		if ($data['bandwidth'] < 0) {
2972
			$input_errors[] = gettext("Bandwidth cannot be negative.");
2973
		}
2974

    
2975

    
2976
		if ($data['bandwidthtype'] == "%") {
2977
			if ($data['bandwidth'] > 100 || $data['bandwidth'] < 0) {
2978
				$input_errors[] = gettext("Bandwidth in percentage should be between 1 and 100 bounds.");
2979
			}
2980
		}
2981

    
2982
/*
2983
		$parent =& $this->GetParent();
2984
		switch ($data['bandwidthtype']) {
2985
		case "%":
2986
			$myBw = $parent->GetAvailableBandwidth() * floatval($data['bandwidth']) / 100;
2987
		default:
2988
			$mybw = floatval($data['bandwidth']) * get_bandwidthtype_scale($data['bandwidthtype']);
2989
			break;
2990
		}
2991
		if ($parent->GetAvailableBandwidth() < floatval($myBw)) {
2992
			$input_errors[] = "The sum of children bandwidth exceeds that of the parent.";
2993
		}
2994
*/
2995
	}
2996

    
2997
	function ReadConfig(&$q) {
2998
		parent::ReadConfig($q);
2999
		if (!empty($q['buckets'])) {
3000
			$this->SetBuckets($q['buckets']);
3001
		} else {
3002
			$this->SetBuckets("");
3003
		}
3004
		if (!empty($q['hogs']) && is_valid_shaperbw($q['hogs'])) {
3005
			$this->SetHogs($q['hogs']);
3006
		} else {
3007
			$this->SetHogs("");
3008
		}
3009
	}
3010

    
3011
	function build_javascript() {
3012
		return parent::build_javascript();
3013
	}
3014

    
3015
	function build_tree() {
3016
		$tree = " <li><a href=\"firewall_shaper.php?interface=" .
3017
		$this->GetInterface()."&amp;queue=" . $this->GetQname()."&amp;action=show";
3018
		$tree .= "\" ";
3019
		$tmpvalue = trim($this->GetDefault());
3020
		if (!empty($tmpvalue)) {
3021
			$tree .= " class=\"navlnk\"";
3022
		}
3023
		$tree .= " >" . $this->GetQname() . "</a>";
3024
		$tree .= "</li>";
3025
		return $tree;
3026
	}
3027

    
3028
	/* Even this should take children into consideration */
3029
	function build_rules(&$default = false) {
3030
		$pfq_rule = "queue ". $this->qname;
3031
		if ($this->GetInterface()) {
3032
			$pfq_rule .= " on ".get_real_interface($this->GetInterface());
3033
		}
3034
		if ($this->GetBandwidth() && $this->GetBwscale()) {
3035
			$pfq_rule .= " bandwidth ".trim($this->GetBandwidth()).$this->GetBwscale();
3036
		}
3037
		$tmpvalue = trim($this->GetQpriority());
3038
		if (!empty($tmpvalue)) {
3039
			$pfq_rule .= " priority " . $this->GetQpriority();
3040
		}
3041
		$tmpvalue = trim($this->GetQlimit());
3042
		if (!empty($tmpvalue)) {
3043
			$pfq_rule .= " qlimit " . $this->GetQlimit();
3044
		}
3045
		if ($this->GetDefault() || $this->GetRed() || $this->GetRio() ||
3046
		    $this->GetEcn() || $this->GetBuckets() || $this->GetHogs() || $this->GetCodel()) {
3047
			$pfq_rule .= " fairq ( ";
3048
			$tmpvalue = trim($this->GetRed());
3049
			if (!empty($tmpvalue)) {
3050
				$comma = 1;
3051
				$pfq_rule .= " red ";
3052
			}
3053
			$tmpvalue = trim($this->GetCodel());
3054
			if (!empty($tmpvalue)) {
3055
				$comma = 1;
3056
				$pfq_rule .= " codel ";
3057
			}
3058
			$tmpvalue = trim($this->GetRio());
3059
			if (!empty($tmpvalue)) {
3060
				if ($comma) {
3061
					$pfq_rule .= " ,";
3062
				}
3063
				$comma = 1;
3064
				$pfq_rule .= " rio ";
3065
			}
3066
			$tmpvalue = trim($this->GetEcn());
3067
			if (!empty($tmpvalue)) {
3068
				if ($comma) {
3069
					$pfq_rule .= " ,";
3070
				}
3071
				$comma = 1;
3072
				$pfq_rule .= " ecn ";
3073
			}
3074
			$tmpvalue = trim($this->GetDefault());
3075
			if (!empty($tmpvalue)) {
3076
				if ($comma) {
3077
					$pfq_rule .= " ,";
3078
				}
3079
				$comma = 1;
3080
				$pfq_rule .= " default ";
3081
				$default = true;
3082
			}
3083
			$tmpvalue = trim($this->GetBuckets());
3084
			if (!empty($tmpvalue)) {
3085
				if ($comma) {
3086
					$pfq_rule .= ", ";
3087
				}
3088
				$pfq_rule .= " buckets " . $this->GetBuckets() . " ";
3089
			}
3090
			$tmpvalue = trim($this->GetHogs());
3091
			if (!empty($tmpvalue)) {
3092
				if ($comma) {
3093
					$pfq_rule .= ", ";
3094
				}
3095
				$pfq_rule .= " hogs " . $this->GetHogs() . " ";
3096
			}
3097
			$pfq_rule .= " ) ";
3098
		}
3099

    
3100
		$pfq_rule .= " \n";
3101
		return $pfq_rule;
3102
	}
3103

    
3104
	function build_form() {
3105
		$form = parent::build_form();
3106

    
3107
		$section = new Form_Section('');
3108

    
3109
		$group = new Form_Group('Bandwidth');
3110

    
3111
		$group->add(new Form_Input(
3112
			'bandwidth',
3113
			null,
3114
			'number',
3115
			$this->GetBandwidth()
3116
		));
3117

    
3118
		$group->add(new Form_Select(
3119
			'bandwidthtype',
3120
			null,
3121
			$this->GetBwscale(),
3122
			array('Kb' => 'Kbit/s',
3123
				  'Mb' => 'Mbit/s',
3124
				  'Gb' => 'Gbit/s',
3125
				  'b' => 'Bit/s',
3126
				  '%' => '%')
3127
		));
3128

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

    
3131
		$section->add($group);
3132

    
3133
		$section->addInput(new Form_Input(
3134
			'buckets',
3135
			'Scheduler specific options',
3136
			'text',
3137
			$this->GetBuckets()
3138
		))->setHelp('Number of buckets available');
3139

    
3140
		$section->addInput(new Form_Input(
3141
			'hogs',
3142
			'',
3143
			'text',
3144
			$this->GetHogs()
3145
			))->setHelp('Bandwidth limit for hosts to not saturate link');
3146

    
3147
		$form->add($section);
3148
		return $form;
3149
	}
3150

    
3151
	function update_altq_queue_data(&$data) {
3152
		$this->ReadConfig($data);
3153
	}
3154

    
3155
	function wconfig() {
3156
		$cflink =& get_reference_to_me_in_config($this->GetLink());
3157
		if (!is_array($cflink)) {
3158
			$cflink = array();
3159
		}
3160
		$cflink['interface'] = $this->GetInterface();
3161
		$cflink['qlimit'] = trim($this->GetQlimit());
3162
		if (empty($cflink['qlimit'])) {
3163
			unset($cflink['qlimit']);
3164
		}
3165
		$cflink['priority'] = trim($this->GetQpriority());
3166
		if (empty($cflink['priority'])) {
3167
			unset($cflink['priority']);
3168
		}
3169
		$cflink['name'] = $this->GetQname();
3170
		$cflink['description'] = trim($this->GetDescription());
3171
		if (empty($cflink['description'])) {
3172
			unset($cflink['description']);
3173
		}
3174
		$cflink['bandwidth'] = $this->GetBandwidth();
3175
		$cflink['bandwidthtype'] = $this->GetBwscale();
3176
		$cflink['enabled'] = $this->GetEnabled();
3177
		if (empty($cflink['enabled'])) {
3178
			unset($cflink['enabled']);
3179
		}
3180
		$cflink['default'] = trim($this->GetDefault());
3181
		if (empty($cflink['default'])) {
3182
			unset($cflink['default']);
3183
		}
3184
		$cflink['red'] = trim($this->GetRed());
3185
		if (empty($cflink['red'])) {
3186
			unset($cflink['red']);
3187
		}
3188
		$cflink['rio'] = trim($this->GetRio());
3189
		if (empty($cflink['rio'])) {
3190
			unset($cflink['rio']);
3191
		}
3192
		$cflink['ecn'] = trim($this->GetEcn());
3193
		if (empty($cflink['ecn'])) {
3194
			unset($cflink['ecn']);
3195
		}
3196
		$cflink['codel'] = trim($this->GetCodel());
3197
		if (empty($cflink['codel'])) {
3198
			unset($cflink['codel']);
3199
		}
3200
		$cflink['buckets'] = trim($this->GetBuckets());
3201
		if (empty($cflink['buckets'])) {
3202
			unset($cflink['buckets']);
3203
		}
3204
		$cflink['hogs'] = trim($this->GetHogs());
3205
		if (empty($cflink['hogs'])) {
3206
			unset($cflink['hogs']);
3207
		}
3208
	}
3209
}
3210

    
3211

    
3212
/*
3213
 * dummynet(4) wrappers.
3214
 */
3215

    
3216

    
3217
/*
3218
 * List of respective objects!
3219
 */
3220
$dummynet_pipe_list = array();
3221

    
3222
class dummynet_class {
3223
	var $qname;
3224
	var $qnumber; /* dummynet(4) uses numbers instead of names; maybe integrate with pf the same as altq does?! */
3225
	var $qlimit;
3226
	var $description;
3227
	var $qenabled;
3228
	var $link;
3229
	var $qparent; /* link to upper class so we do things easily on WF2Q+ rule creation */
3230
	var $plr;
3231

    
3232
	var $buckets;
3233
	/* mask parameters */
3234
	var $mask;
3235
	var $noerror;
3236

    
3237
	/* Accessor functions */
3238
	function SetLink($link) {
3239
		$this->link = $link;
3240
	}
3241
	function GetLink() {
3242
		return $this->link;
3243
	}
3244
	function GetMask() {
3245
		if (!isset($this->mask["type"])) {
3246
			$this->mask["type"] = "none";
3247
		}
3248
		return $this->mask;
3249
	}
3250
	function SetMask($mask) {
3251
		$this->mask = $mask;
3252
	}
3253
	function &GetParent() {
3254
		return $this->qparent;
3255
	}
3256
	function SetParent(&$parent) {
3257
		$this->qparent = &$parent;
3258
	}
3259
	function GetEnabled() {
3260
		return $this->qenabled;
3261
	}
3262
	function SetEnabled($value) {
3263
		$this->qenabled = $value;
3264
	}
3265
	function CanHaveChildren() {
3266
		return false;
3267
	}
3268
	function CanBeDeleted() {
3269
		return true;
3270
	}
3271
	function GetQname() {
3272
		return $this->qname;
3273
	}
3274
	function SetQname($name) {
3275
		$this->qname = trim($name);
3276
	}
3277
	function GetQlimit() {
3278
		return $this->qlimit;
3279
	}
3280
	function SetQlimit($limit) {
3281
		$this->qlimit = $limit;
3282
	}
3283
	function GetDescription() {
3284
		return $this->description;
3285
	}
3286
	function SetDescription($str) {
3287
		$this->description = trim($str);
3288
	}
3289
	function GetFirstime() {
3290
		return $this->firsttime;
3291
	}
3292
	function SetFirsttime($number) {
3293
		$this->firsttime = $number;
3294
	}
3295
	function GetBuckets() {
3296
		return $this->buckets;
3297
	}
3298
	function SetBuckets($buckets) {
3299
		$this->buckets = $buckets;
3300
	}
3301
	function SetNumber($number) {
3302
		$this->qnumber = $number;
3303
	}
3304
	function GetNumber() {
3305
		return $this->qnumber;
3306
	}
3307
	function GetPlr() {
3308
		return $this->plr;
3309
	}
3310
	function SetPlr($plr) {
3311
		$this->plr = $plr;
3312
	}
3313

    
3314
	function build_javascript() {
3315
		$javascript .= "<script type=\"text/javascript\">\n";
3316
		$javascript .= "//<![CDATA[\n";
3317
		$javascript .= "function enable_maskbits(enable_over) {\n";
3318
		$javascript .= "var e = document.getElementById(\"mask\");\n";
3319
		$javascript .= "if ((e.options[e.selectedIndex].text == \"none\") || enable_over) {\n";
3320
		$javascript .= "document.iform.maskbits.disabled = 1;\n";
3321
		$javascript .= "document.iform.maskbits.value = \"\";\n";
3322
		$javascript .= "document.iform.maskbitsv6.disabled = 1;\n";
3323
		$javascript .= "document.iform.maskbitsv6.value = \"\";\n";
3324
		$javascript .= "} else {\n";
3325
		$javascript .= "document.iform.maskbits.disabled = 0;\n";
3326
		$javascript .= "document.iform.maskbitsv6.disabled = 0;\n";
3327
		$javascript .= "}}\n";
3328
		$javascript .= "//]]>\n";
3329
		$javascript .= "</script>\n";
3330
		return $javascript;
3331
	}
3332

    
3333
	function validate_input($data, &$input_errors) {
3334
		$reqdfields[] = "bandwidth";
3335
		$reqdfieldsn[] = gettext("Bandwidth");
3336
		/*$reqdfields[] = "burst";
3337
		$reqdfieldsn[] = gettext("Burst"); */
3338
		$reqdfields[] = "bandwidthtype";
3339
		$reqdfieldsn[] = gettext("Bandwidthtype");
3340
		$reqdfields[] = "newname";
3341
		$reqdfieldsn[] = gettext("Name");
3342

    
3343
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
3344

    
3345
		if ($data['plr'] && (!is_numeric($data['plr']) ||
3346
		    ($data['plr'] < 0) || ($data['plr'] > 1))) {
3347
			$input_errors[] = gettext("Plr must be a value between 0 and 1.");
3348
		}
3349
		if ($data['buckets'] && (!is_numeric($data['buckets']) ||
3350
		    ($data['buckets'] < 16) || ($data['buckets'] > 65535))) {
3351
			$input_errors[] = gettext("Buckets must be an integer between 16 and 65535.");
3352
		}
3353
		if ($data['qlimit'] && (!is_numeric($data['qlimit']))) {
3354
			$input_errors[] = gettext("Queue limit must be an integer");
3355
		}
3356
		if (!empty($data['newname']) && !preg_match("/^[a-zA-Z0-9_-]+$/", $data['newname'])) {
3357
			$input_errors[] = gettext("Queue names must be alphanumeric and _ or - only.");
3358
		}
3359
		if (!empty($data['name']) && !preg_match("/^[a-zA-Z0-9_-]+$/", $data['name'])) {
3360
			$input_errors[] = gettext("Queue names must be alphanumeric and _ or - only.");
3361
		}
3362
		if (isset($data['maskbits']) && ($data['maskbits'] <> "")) {
3363
			if ((!is_numeric($data['maskbits'])) || ($data['maskbits'] <= 0) || ($data['maskbits'] > 32)) {
3364
				$input_errors[] = gettext("IPV4 bit mask must be blank or numeric value between 1 and 32.");
3365
			}
3366
		}
3367
		if (isset($data['maskbitsv6']) && ($data['maskbitsv6'] <> "")) {
3368
			if ((!is_numeric($data['maskbitsv6'])) || ($data['maskbitsv6'] <= 0) || ($data['maskbitsv6'] > 128)) {
3369
				$input_errors[] = gettext("IPV6 bit mask must be blank or numeric value between 1 and 128.");
3370
			}
3371
		}
3372
	}
3373

    
3374
	function build_mask_rules(&$pfq_rule) {
3375
		$mask = $this->GetMask();
3376
		if (!empty($mask['type'])) {
3377
			if ($mask['type'] <> 'none') {
3378
				$pfq_rule .= " mask";
3379
			}
3380
			switch ($mask['type']) {
3381
				case 'srcaddress':
3382
					if (!empty($mask['bitsv6']) && ($mask['bitsv6'] <> "")) {
3383
						$pfq_rule .= " src-ip6 /" . $mask['bitsv6'];
3384
					} else {
3385
						$pfq_rule .= " src-ip6 /128";
3386
					}
3387
					if (!empty($mask['bits']) && ($mask['bits'] <> "")) {
3388
						$pfq_rule .= sprintf(" src-ip 0x%x", gen_subnet_mask_long($mask['bits']));
3389
					} else {
3390
						$pfq_rule .= " src-ip 0xffffffff";
3391
					}
3392
					break;
3393
				case 'dstaddress':
3394
					if (!empty($mask['bitsv6']) && ($mask['bitsv6'] <> "")) {
3395
						$pfq_rule .= " dst-ip6 /" . $mask['bitsv6'];
3396
					} else {
3397
						$pfq_rule .= " dst-ip6 /128";
3398
					}
3399
					if (!empty($mask['bits']) && ($mask['bits'] <> "")) {
3400
						$pfq_rule .= sprintf(" dst-ip 0x%x", gen_subnet_mask_long($mask['bits']));
3401
					} else {
3402
						$pfq_rule .= " dst-ip 0xffffffff";
3403
					}
3404
					break;
3405
				default:
3406
					break;
3407
			}
3408
		}
3409
	}
3410

    
3411
}
3412

    
3413
class dnpipe_class extends dummynet_class {
3414
	var $delay;
3415
	var $qbandwidth = array();
3416
	var $qbandwidthtype;
3417

    
3418
		/* This is here to help on form building and building rules/lists */
3419
	var $subqueues = array();
3420

    
3421
	function CanHaveChildren() {
3422
		return true;
3423
	}
3424
	function SetDelay($delay) {
3425
		$this->delay = $delay;
3426
	}
3427
	function GetDelay() {
3428
		return $this->delay;
3429
	}
3430
	function delete_queue() {
3431
		cleanup_dnqueue_from_rules($this->GetQname());
3432
		foreach ($this->subqueues as $q) {
3433
			$q->delete_queue();
3434
		}
3435
		unset_dn_object_by_reference($this->GetLink());
3436
		@pfSense_pipe_action("pipe delete " . $this->GetNumber());
3437
	}
3438
	function GetBandwidth() {
3439
		return $this->qbandwidth;
3440
	}
3441
	function SetBandwidth($bandwidth) {
3442
		$this->qbandwidth = $bandwidth;
3443
	}
3444
	function GetBurst() {
3445
		return $this->qburst;
3446
	}
3447
	function SetBurst($burst) {
3448
		$this->qburst = $burst;
3449
	}
3450

    
3451
	function &add_queue($interface, &$queue, &$path, &$input_errors) {
3452

    
3453
		if (!is_array($this->subqueues)) {
3454
			$this->subqueues = array();
3455
		}
3456

    
3457
		$q =& new dnqueue_class();
3458
		$q->SetLink($path);
3459
		$q->SetEnabled("on");
3460
		$q->SetPipe($this->GetQname());
3461
		$q->SetParent($this);
3462
		$q->ReadConfig($queue);
3463
		$q->validate_input($queue, $input_errors);
3464
		if (count($input_errors)) {
3465
			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)));
3466
			return $q;
3467
		}
3468
		$number = dnqueue_find_nextnumber();
3469
		$q->SetNumber($number);
3470
		$this->subqueues[$q->GetQname()] = &$q;
3471

    
3472
		return $q;
3473
	}
3474

    
3475
	function &get_queue_list(&$q = null) {
3476
		$qlist = array();
3477

    
3478
		$qlist[$this->GetQname()] = $this->GetNumber();
3479
		if (is_array($this->subqueues)) {
3480
			foreach ($this->subqueues as $queue) {
3481
				$queue->get_queue_list($qlist);
3482
			}
3483
		}
3484
		return $qlist;
3485
	}
3486

    
3487
	/*
3488
	 * Should search even its children
3489
	 */
3490
	function &find_queue($pipe, $qname) {
3491
		if ($qname == $this->GetQname()) {
3492
			return $this;
3493
		}
3494
		foreach ($this->subqueues as $q) {
3495
			$result =& $q->find_queue("", $qname);
3496
			if ($result) {
3497
				return $result;
3498
			}
3499
		}
3500
	}
3501

    
3502
	function &find_parentqueue($pipe, $qname) {
3503
		return NULL;
3504
	}
3505

    
3506
	function validate_input($data, &$input_errors) {
3507
		parent::validate_input($data, $input_errors);
3508

    
3509
		$schedule = 0;
3510
		$schedulenone = 0;
3511
		$entries = 0;
3512
		/* XXX: Really no better way? */
3513
		for ($i = 0; $i < 2900; $i++) {
3514
			if (!empty($data["bwsched{$i}"])) {
3515
				if ($data["bwsched{$i}"] != "none") {
3516
					$schedule++;
3517
				} else {
3518
					$schedulenone++;
3519
				}
3520
			}
3521
			if (!empty($data["bandwidth{$i}"])) {
3522
				if (!is_numeric($data["bandwidth{$i}"])) {
3523
					$input_errors[] = sprintf(gettext("Bandwidth for schedule %s must be an integer."), $data["bwsched{$i}"]);
3524
				} else if (($data["burst{$i}"] != "") && (!is_numeric($data["burst{$i}"]))) {
3525
					$input_errors[] = sprintf(gettext("Burst for schedule %s must be an integer."), $data["bwsched{$i}"]);
3526
				} else {
3527
					$entries++;
3528
				}
3529
			}
3530
		}
3531
		if ($schedule == 0 && $entries > 1) {
3532
			$input_errors[] = gettext("You need to specify a schedule for every additional entry");
3533
		}
3534
		if ($schedulenone > 0 && $entries > 1) {
3535
			$input_errors[] = gettext("If more than one bandwidth configured all schedules need to be selected");
3536
		}
3537
		if ($entries == 0) {
3538
			$input_errors[] = gettext("At least one bw specification is necessary");
3539
		}
3540
		if ($data['delay'] && (!is_numeric($data['delay']))) {
3541
			$input_errors[] = gettext("Delay must be an integer.");
3542
		}
3543
	}
3544

    
3545
	function ReadConfig(&$q) {
3546
		if (!empty($q['name']) && !empty($q['newname']) && $q['name'] != $q['newname']) {
3547
			$this->SetQname($q['newname']);
3548
		} else if (!empty($q['newname'])) {
3549
			$this->SetQname($q['newname']);
3550
		} else {
3551
			$this->SetQname($q['name']);
3552
		}
3553
		$this->SetNumber($q['number']);
3554

    
3555
		if (!empty($_POST)) {
3556
			$bandwidth = array();
3557
			/* XXX: Really no better way? */
3558
			for ($i = 0; $i < 2900; $i++) {
3559
				if (isset($q["bandwidth{$i}"]) && $q["bandwidth{$i}"] <> "") {
3560
					$bw = array();
3561
					$bw['bw'] = $q["bandwidth{$i}"];
3562
					$bw['burst'] = $q["burst{$i}"];
3563
					if (isset($q["bwtype{$i}"]) && $q["bwtype{$i}"]) {
3564
						$bw['bwscale'] = $q["bwtype{$i}"];
3565
					}
3566
					if (isset($q["bwsched{$i}"]) && $q["bwsched{$i}"]) {
3567
						$bw['bwsched'] = $q["bwsched{$i}"];
3568
					}
3569
					$bandwidth[] = $bw;
3570
				}
3571
			}
3572
			$this->SetBandwidth($bandwidth);
3573
		}
3574

    
3575
		if (is_array($q['bandwidth']) && is_array($q['bandwidth']['item'])) {
3576
			$this->SetBandwidth($q['bandwidth']['item']);
3577
			$this->SetBurst($q['burst']['item']);
3578
		}
3579

    
3580
		if (isset($q['qlimit']) && $q['qlimit'] <> "") {
3581
			$this->SetQlimit($q['qlimit']);
3582
		} else {
3583
			$this->SetQlimit("");
3584
		}
3585
		if (isset($q['mask']) && $q['mask'] <> "") {
3586
			$masktype = $q['mask'];
3587
		} else {
3588
			$masktype = "";
3589
		}
3590
		if (isset($q['maskbits']) && $q['maskbits'] <> "") {
3591
			$maskbits = $q['maskbits'];
3592
		} else {
3593
			$maskbits = "";
3594
		}
3595
		if (isset($q['maskbitsv6']) && $q['maskbitsv6'] <> "") {
3596
			$maskbitsv6 = $q['maskbitsv6'];
3597
		} else {
3598
			$maskbitsv6 = "";
3599
		}
3600
		$this->SetMask(array("type" => $masktype, "bits" => $maskbits, "bitsv6" => $maskbitsv6));
3601
		if (isset($q['buckets']) && $q['buckets'] <> "") {
3602
			$this->SetBuckets($q['buckets']);
3603
		} else {
3604
			$this->SetBuckets("");
3605
		}
3606
		if (isset($q['plr']) && $q['plr'] <> "") {
3607
			$this->SetPlr($q['plr']);
3608
		} else {
3609
			$this->SetPlr("");
3610
		}
3611
		if (isset($q['delay']) && $q['delay'] <> "") {
3612
			$this->SetDelay($q['delay']);
3613
		} else {
3614
			$this->SetDelay(0);
3615
		}
3616
		if (isset($q['description']) && $q['description'] <> "") {
3617
			$this->SetDescription($q['description']);
3618
		} else {
3619
			$this->SetDescription("");
3620
		}
3621
		$this->SetEnabled($q['enabled']);
3622

    
3623
	}
3624

    
3625
	function build_tree() {
3626
		$tree = " <li><a href=\"firewall_shaper_vinterface.php?pipe=" . $this->GetQname() ."&amp;queue=".$this->GetQname() ."&amp;action=show\">";
3627
		$tree .= $this->GetQname() . "</a>";
3628
		if (is_array($this->subqueues)) {
3629
			$tree .= "<ul>";
3630
			foreach ($this->subqueues as $q) {
3631
				$tree .= $q->build_tree();
3632
			}
3633
			$tree .= "</ul>";
3634
		}
3635
		$tree .= "</li>";
3636

    
3637
		return $tree;
3638
	}
3639

    
3640
	function build_rules() {
3641
		global $config, $time_based_rules;
3642

    
3643
		if ($this->GetEnabled() == "") {
3644
			return;
3645
		}
3646

    
3647
		$pfq_rule = "\npipe ". $this->GetNumber() . " config ";
3648
		$found = false;
3649
		$bandwidth = $this->GetBandwidth();
3650
		if (is_array($bandwidth)) {
3651
			foreach ($bandwidth as $bw) {
3652
				if ($bw['bwsched'] != "none") {
3653
					$time_based_rules = true;
3654
					if (is_array($config['schedules']) && is_array($config['schedules']['schedule'])) {
3655
						foreach ($config['schedules']['schedule'] as $schedule) {
3656
							if ($bw['bwsched'] == $schedule['name']) {
3657
								if (filter_get_time_based_rule_status($schedule)) {
3658
									$pfq_rule .= " bw ".trim($bw['bw']).$bw['bwscale'];
3659
									if (is_numeric($bw['burst']) && ($bw['burst'] > 0)) {
3660
										$pfq_rule .= " burst ".trim($bw['burst']);
3661
									}
3662
									$found = true;
3663
									break;
3664
								}
3665
							}
3666
						}
3667
					} else {
3668
						$pfq_rule .= " bw 0";
3669
						$found = true;
3670
						break;
3671
					}
3672
				} else {
3673
					$pfq_rule .= " bw ".trim($bw['bw']).$bw['bwscale'];
3674
					if (is_numeric($bw['burst']) && ($bw['burst'] > 0)) {
3675
						$pfq_rule .= " burst ".trim($bw['burst']);
3676
					}
3677
					$found = true;
3678
					break;
3679
				}
3680
			}
3681
			if ($found == false) {
3682
				$pfq_rule .= " bw 0";
3683
			}
3684
		} else {
3685
			$pfq_rule .= " bw 0";
3686
		}
3687

    
3688
		if ($this->GetQlimit()) {
3689
			$pfq_rule .= " queue " . $this->GetQlimit();
3690
		}
3691
		if ($this->GetPlr()) {
3692
			$pfq_rule .= " plr " . $this->GetPlr();
3693
		}
3694
		if ($this->GetBuckets()) {
3695
			$pfq_rule .= " buckets " . $this->GetBuckets();
3696
		}
3697
		if ($this->GetDelay()) {
3698
			$pfq_rule .= " delay " . $this->GetDelay();
3699
		}
3700
		$this->build_mask_rules($pfq_rule);
3701

    
3702
		$pfq_rule .= "\n";
3703

    
3704
		if (!empty($this->subqueues) && count($this->subqueues) > 0) {
3705
			foreach ($this->subqueues as $q) {
3706
				$pfq_rule .= $q->build_rules();
3707
			}
3708
		}
3709
		$pfq_rule .= " \n";
3710

    
3711
		return $pfq_rule;
3712
	}
3713

    
3714
	function update_dn_data(&$data) {
3715
		$this->ReadConfig($data);
3716
	}
3717

    
3718
	function build_javascript() {
3719
		global $g, $config;
3720

    
3721
		$javasr = parent::build_javascript();
3722

    
3723
		//build list of schedules
3724
		$schedules = "<option value='none'>none</option>";
3725
		if (is_array($config['schedules']) && is_array($config['schedules']['schedule'])) {
3726
			foreach ($config['schedules']['schedule'] as $schedule) {
3727
				if ($schedule['name'] <> "") {
3728
					$schedules .= "<option value='{$schedule['name']}'>{$schedule['name']}</option>";
3729
				}
3730
			}
3731
		}
3732
		$bwopt = "";
3733
		foreach (array("Kb" => "Kbit/s", "Mb" => "Mbit/s", "Gb" => "Gbit/s", "b" => "Bit/s") as $bwidx => $bw) {
3734
			$bwopt .= "<option value='{$bwidx}'>{$bw}</option>";
3735
		}
3736

    
3737
		$javasr .= <<<EOD
3738
<script type='text/javascript'>
3739
//<![CDATA[
3740
var addBwRowTo = (function() {
3741

    
3742
	return (function (tableId) {
3743

    
3744
	var table = document.getElementById(tableId);
3745
	var totalrows = table.rows.length -1;
3746

    
3747
	var row = table.insertRow(totalrows + 1);
3748
	var cell1 = row.insertCell(0);
3749
	var cell2 = row.insertCell(1);
3750
	var cell3 = row.insertCell(2);
3751
	var cell4 = row.insertCell(3);
3752

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

    
3758
	});
3759
})();
3760

    
3761
function removeBwRow(el) {
3762
	var d = el.parentNode.parentNode.rowIndex;
3763
	document.getElementById('maintable').deleteRow(d);
3764
}
3765
//]]>
3766
</script>
3767

    
3768
EOD;
3769

    
3770
		return $javasr;
3771
	}
3772

    
3773
	// Compose a table of bandwidths that can then be inserted into the form using a Form_StaticText
3774
	// The table has been "Bootstrapped" to match the web design while maintaining compatibility with
3775
	// with the javascript in this class
3776
	function build_bwtable() {
3777
		global $config;
3778

    
3779
		$bandwidth = $this->GetBandwidth();
3780
				//build list of schedules
3781
		$schedules = array();
3782
		$schedules[] = "none";//leave none to leave rule enabled all the time
3783
		if (is_array($config['schedules']) && is_array($config['schedules']['schedule'])) {
3784
			foreach ($config['schedules']['schedule'] as $schedule) {
3785
				if ($schedule['name'] != "") {
3786
					$schedules[] = $schedule['name'];
3787
				}
3788
			}
3789
		}
3790

    
3791
		$form = '<div class="table-responsive">';
3792
		$form .= '<table id="maintable" class="table table-hover table-striped">';
3793
		$form .= "<thead><tr>";
3794
		$form .= "<th>Bandwidth</th>";
3795
		//$form .= "<td width='35%'><div id='fifthcolumn'>Burst</div></td>";
3796
		$form .= "<th>Bw type</th>";
3797
		$form .= "<th>Schedule</th>";
3798
		$form .= "<th></th>";
3799
		$form .= "</tr></thead>";
3800
		$form .= "<tbody>";
3801

    
3802
		// If there are no bandwidths defined, make a blank one for convenience
3803
		if (empty($bandwidth)) {
3804
			$bandwidth = array(0 => array('bw' => '', 'bwscale' => 'Kb', 'bwsched' => 'none'));
3805
		}
3806

    
3807
		if (is_array($bandwidth)) {
3808
			foreach ($bandwidth as $bwidx => $bw) {
3809
				$form .= '<tr>';
3810
				$form .= '<td class="col-xs-4">';
3811
				$form .= "<input class='form-control' type=\"number\" id=\"bandwidth{$bwidx}\" name=\"bandwidth{$bwidx}\" value=\"{$bw['bw']}\" />";
3812
				//$form .= "</td><td width='20%'>";
3813
				//$form .= "<input class='formfld unknown' size='10' type=\"text\" id=\"burst{$bwidx}\" name=\"burst{$bwidx}\" value=\"{$bw['burst']}\" />";
3814
				$form .= "</td>";
3815
				$form .= '<td class="col-xs-4">';
3816
				$form .= "<select id=\"bwtype{$bwidx}\" name=\"bwtype{$bwidx}\" class=\"form-control\">";
3817

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

    
3821
					if ($bw['bwscale'] == $bwsidx) {
3822
						$form .= " selected";
3823
					}
3824

    
3825
					$form .= ">{$bwscale}</option>";
3826
				}
3827

    
3828
				$form .= "</select>";
3829
				$form .= "</td>";
3830
				$form .= '<td class="col-xs-4">';
3831
				$form .= "<select id=\"bwsched{$bwidx}\" name=\"bwsched{$bwidx}\" class=\"form-control\">";
3832

    
3833
				foreach ($schedules as $schd) {
3834
					$selected = "";
3835
					if ($bw['bwsched'] == $schd) {
3836
						$selected = "selected";
3837
					}
3838

    
3839
					$form .= "<option value='{$schd}' {$selected}>{$schd}</option>";
3840
				}
3841

    
3842
				$form .= "</select>";
3843
				$form .= "</td>";
3844
				$form .= '<td>';
3845
				$form .= '<a class="btn btn-warning" onclick="removeBwRow(this); return false;"><i class="fa fa-trash icon-embed-btn"></i>' . gettext('Delete') . '</a>';
3846
				$form .= "</td></tr>";
3847
			}
3848
		}
3849
		$form .= "</tbody></table></div><br />";
3850

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

    
3855
		return($form);
3856
	}
3857

    
3858
	function build_form() {
3859
		global $g, $config, $pipe, $action, $qname;
3860

    
3861
		//build list of schedules
3862
		$schedules = array();
3863
		$schedules[] = "none";//leave none to leave rule enabled all the time
3864
		if (is_array($config['schedules']) && is_array($config['schedules']['schedule'])) {
3865
			foreach ($config['schedules']['schedule'] as $schedule) {
3866
				if ($schedule['name'] <> "") {
3867
					$schedules[] = $schedule['name'];
3868
				}
3869
			}
3870
		}
3871

    
3872

    
3873
		$sform = new Form();
3874
		$sform->setAction("firewall_shaper.php");
3875

    
3876
		$section = new Form_Section('Limiters');
3877

    
3878
		$section->addInput(new Form_Checkbox(
3879
			'enabled',
3880
			'Enable',
3881
			'Enable limiter and its children',
3882
			($this->GetEnabled() == "on"),
3883
			'on'
3884
		));
3885

    
3886
		$section->addInput(new Form_Input(
3887
			'newname',
3888
			'Name',
3889
			'text',
3890
			$this->GetQname()
3891
		));
3892

    
3893
		$section->addInput(new Form_Input(
3894
			'name',
3895
			null,
3896
			'hidden',
3897
			$this->GetQname()
3898
		));
3899

    
3900
		if ($this->GetNumber() > 0) {
3901
			$section->addInput(new Form_Input(
3902
				'number',
3903
				null,
3904
				'hidden',
3905
				$this->GetNumber()
3906
			));
3907
		}
3908

    
3909
		$bandwidth = $this->GetBandwidth();
3910

    
3911
		// Delete a row
3912
//		if(isset($_GET['delbwrow']) && (count($bandwidth) > 0))
3913
//			unset($bandwidth[$_GET['delbwrow']]);
3914

    
3915
		// Add a row
3916
//		if($_GET['newbwrow']) {
3917
//			array_push($bandwidth, array(count($bandwidth) => array('bw' => '', 'burst' => '', 'bwscale' => 'Kb', 'bwsched' => 'none') ));
3918
//		}
3919

    
3920
		if (is_array($bandwidth)) {
3921
				$section->addInput(new Form_StaticText(
3922
				'Bandwidth',
3923
				$this->build_bwtable()
3924
			));
3925
		}
3926

    
3927
		$mask = $this->GetMask();
3928

    
3929
		$section->addInput(new Form_Select(
3930
			'mask',
3931
			'Mask',
3932
			$mask['type'],
3933
			array('none' => gettext('None'), 'srcaddress' => gettext('Source addresses'), 'dstaddress' => gettext('Destination addresses'))
3934
		))->setHelp('If "source" or "destination" slots is chosen a dynamic pipe with the bandwidth, delay, packet loss ' .
3935
					'and queue size given above will be created for each source/destination IP address encountered, respectively. ' .
3936
					'This makes it possible to easily specify bandwidth limits per host.');
3937

    
3938
		$group = new Form_Group(null);
3939

    
3940
		$group->add(new Form_Select(
3941
			'maskbits',
3942
			null,
3943
			$mask['bits'],
3944
			array_combine(range(32, 1, -1), range(32, 1, -1))
3945
		))->setHelp('IPV4 mask bits' . '<br />' . '255.255.255.255/?');
3946

    
3947
		$group->add(new Form_Select(
3948
			'maskbitsv6',
3949
			null,
3950
			$mask['bitsv6'],
3951
			array_combine(range(128, 1, -1), range(128, 1, -1))
3952
		))->setHelp('IPV6 mask bits' . '<br />' . '<span style="font-family:consolas">ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/?</span>');
3953

    
3954
		$section->add($group);
3955

    
3956
		$section->addInput(new Form_Input(
3957
			'description',
3958
			'Description',
3959
			'text',
3960
			$this->GetDescription()
3961
		))->setHelp('You may enter a description here for your reference (not parsed).');
3962

    
3963
		$sform->add($section);
3964

    
3965
		$section = new Form_Section('Advanced Options');
3966

    
3967
		$section->addInput(new Form_Input(
3968
			'delay',
3969
			'Delay (ms)',
3970
			'text',
3971
			$this->GetDelay() > 0 ? $this->GetDelay():null
3972
		))->setHelp('In most cases, you should specify 0 here (or leave the field empty)');
3973

    
3974
		$section->addInput(new Form_Input(
3975
			'plr',
3976
			'Packet Loss Rate',
3977
			'number',
3978
			$this->GetPlr(),
3979
			['step' => '0.001', 'min' => '0.000']
3980
		))->setHelp('In most cases, you should specify 0 here (or leave the field empty). ' .
3981
					'A value of 0.001 means one packet in 1000 gets dropped');
3982

    
3983
		$section->addInput(new Form_Input(
3984
			'qlimit',
3985
			'Queue size (slots)',
3986
			'number',
3987
			$this->GetQlimit()
3988
		))->setHelp('In most cases, you should leave the field empty. All packets in this pipe are placed into a fixed-size queue first, ' .
3989
					'then they are delayed by value specified in the Delay field, and then they are delivered to their destination.');
3990

    
3991
		$section->addInput(new Form_Input(
3992
			'buckets',
3993
			'Bucket size (slots)',
3994
			'number',
3995
			$this->GetBuckets()
3996
		))->setHelp('In most cases, you should leave this field empty. It increases the hash size set');
3997

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

    
4000
		return($sform);
4001
		}
4002

    
4003
	function wconfig() {
4004
		$cflink =& get_dn_reference_to_me_in_config($this->GetLink());
4005
		if (!is_array($cflink)) {
4006
			$cflink = array();
4007
		}
4008
		$cflink['name'] = $this->GetQname();
4009
		$cflink['number'] = $this->GetNumber();
4010
		$cflink['qlimit'] = $this->GetQlimit();
4011
		$cflink['plr'] = $this->GetPlr();
4012
		$cflink['description'] = $this->GetDescription();
4013

    
4014
		$bandwidth = $this->GetBandwidth();
4015
		if (is_array($bandwidth)) {
4016
			$cflink['bandwidth'] = array();
4017
			$cflink['bandwidth']['item'] = array();
4018
			foreach ($bandwidth as $bwidx => $bw) {
4019
				$cflink['bandwidth']['item'][] = $bw;
4020
			}
4021
		}
4022

    
4023
		$cflink['enabled'] = $this->GetEnabled();
4024
		$cflink['buckets'] = $this->GetBuckets();
4025
		$mask = $this->GetMask();
4026
		$cflink['mask'] = $mask['type'];
4027
		$cflink['maskbits'] = $mask['bits'];
4028
		$cflink['maskbitsv6'] = $mask['bitsv6'];
4029
		$cflink['delay'] = $this->GetDelay();
4030
	}
4031

    
4032
}
4033

    
4034
class dnqueue_class extends dummynet_class {
4035
	var $pipeparent;
4036
	var $weight;
4037

    
4038
	function GetWeight() {
4039
		return $this->weight;
4040
	}
4041
	function SetWeight($weight) {
4042
		$this->weight = $weight;
4043
	}
4044
	function GetPipe() {
4045
		return $this->pipeparent;
4046
	}
4047
	function SetPipe($pipe) {
4048
		$this->pipeparent = $pipe;
4049
	}
4050

    
4051
	/* Just a stub in case we ever try to call this from the frontend. */
4052
	function &add_queue($interface, &$queue, &$path, &$input_errors) {
4053
		return;
4054
	}
4055

    
4056
	function delete_queue() {
4057
		cleanup_dnqueue_from_rules($this->GetQname());
4058
		unset_dn_object_by_reference($this->GetLink());
4059
		@pfSense_pipe_action("queue delete " . $this->GetNumber());
4060
	}
4061

    
4062
	function validate_input($data, &$input_errors) {
4063
		parent::validate_input($data, $input_errors);
4064

    
4065
		if ($data['weight'] && ((!is_numeric($data['weight'])) ||
4066
		    ($data['weight'] < 1 && $data['weight'] > 100))) {
4067
			$input_errors[] = gettext("Weight must be an integer between 1 and 100.");
4068
		}
4069
	}
4070

    
4071
	/*
4072
	 * Should search even its children
4073
	 */
4074
	function &find_queue($pipe, $qname) {
4075
		if ($qname == $this->GetQname()) {
4076
			return $this;
4077
		} else {
4078
			return NULL;
4079
		}
4080
	}
4081

    
4082
	function &find_parentqueue($pipe, $qname) {
4083
		return $this->qparent;
4084
	}
4085

    
4086
	function &get_queue_list(&$qlist) {
4087
		if ($this->GetEnabled() == "") {
4088
			return;
4089
		}
4090
		$qlist[$this->GetQname()] = "?" .$this->GetNumber();
4091
	}
4092

    
4093
	function ReadConfig(&$q) {
4094
		if (!empty($q['name']) && !empty($q['newname']) && $q['name'] != $q['newname']) {
4095
			$this->SetQname($q['newname']);
4096
		} else if (!empty($q['newname'])) {
4097
			$this->SetQname($q['newname']);
4098
		} else {
4099
			$this->SetQname($q['name']);
4100
		}
4101
		$this->SetNumber($q['number']);
4102
		if (isset($q['qlimit']) && $q['qlimit'] <> "") {
4103
			$this->SetQlimit($q['qlimit']);
4104
		} else {
4105
			$this->SetQlimit("");
4106
		}
4107
		if (isset($q['mask']) && $q['mask'] <> "") {
4108
			$masktype = $q['mask'];
4109
		} else {
4110
			$masktype = "";
4111
		}
4112
		if (isset($q['maskbits']) && $q['maskbits'] <> "") {
4113
			$maskbits = $q['maskbits'];
4114
		} else {
4115
			$maskbits = "";
4116
		}
4117
		if (isset($q['maskbitsv6']) && $q['maskbitsv6'] <> "") {
4118
			$maskbitsv6 = $q['maskbitsv6'];
4119
		} else {
4120
			$maskbitsv6 = "";
4121
		}
4122
		$this->SetMask(array("type" => $masktype, "bits" => $maskbits, "bitsv6" => $maskbitsv6));
4123
		if (isset($q['buckets']) && $q['buckets'] <> "") {
4124
			$this->SetBuckets($q['buckets']);
4125
		} else {
4126
			$this->SetBuckets("");
4127
		}
4128
		if (isset($q['plr']) && $q['plr'] <> "") {
4129
			$this->SetPlr($q['plr']);
4130
		} else {
4131
			$this->SetPlr("");
4132
		}
4133
		if (isset($q['weight']) && $q['weight'] <> "") {
4134
			$this->SetWeight($q['weight']);
4135
		} else {
4136
			$this->SetWeight("");
4137
		}
4138
		if (isset($q['description']) && $q['description'] <> "") {
4139
			$this->SetDescription($q['description']);
4140
		} else {
4141
			$this->SetDescription("");
4142
		}
4143
		$this->SetEnabled($q['enabled']);
4144
	}
4145

    
4146
	function build_tree() {
4147
		$parent =& $this->GetParent();
4148
		$tree = " <li><a href=\"firewall_shaper_vinterface.php?pipe=" . $parent->GetQname() ."&amp;queue=" . $this->GetQname() ."&amp;action=show\">";
4149
		$tree .= $this->GetQname() . "</a>";
4150
		$tree .= "</li>";
4151

    
4152
		return $tree;
4153
	}
4154

    
4155
	function build_rules() {
4156
		if ($this->GetEnabled() == "") {
4157
			return;
4158
		}
4159

    
4160
		$parent =& $this->GetParent();
4161
		$pfq_rule = "queue ". $this->GetNumber() . " config pipe " . $parent->GetNumber();
4162
		if ($this->GetQlimit()) {
4163
			$pfq_rule .= " queue " . $this->GetQlimit();
4164
		}
4165
		if ($this->GetWeight()) {
4166
			$pfq_rule .= " weight " . $this->GetWeight();
4167
		}
4168
		if ($this->GetBuckets()) {
4169
			$pfq_rule .= " buckets " . $this->GetBuckets();
4170
		}
4171
		$this->build_mask_rules($pfq_rule);
4172
		$pfq_rule .= "\n";
4173

    
4174
		return $pfq_rule;
4175
	}
4176

    
4177
	function build_javascript() {
4178
		return parent::build_javascript();
4179
	}
4180

    
4181
	function build_form() {
4182
		global $g, $config, $pipe, $action, $qname;
4183

    
4184
		//build list of schedules
4185
		$schedules = array();
4186
		$schedules[] = "none";//leave none to leave rule enabled all the time
4187
		if (is_array($config['schedules']) && is_array($config['schedules']['schedule'])) {
4188
			foreach ($config['schedules']['schedule'] as $schedule) {
4189
				if ($schedule['name'] <> "") {
4190
					$schedules[] = $schedule['name'];
4191
				}
4192
			}
4193
		}
4194

    
4195

    
4196
		$sform = new Form();
4197
		$sform->setAction("firewall_shaper.php");
4198
		$section = new Form_Section('Limiters');
4199

    
4200
		$section->addInput(new Form_Checkbox(
4201
			'enabled',
4202
			'Enable',
4203
			'Enable this queue',
4204
			($this->GetEnabled() == "on"),
4205
			'on'
4206
		));
4207

    
4208
		$section->addInput(new Form_Input(
4209
			'newname',
4210
			'Name',
4211
			'text',
4212
			$this->GetQname()
4213
		));
4214

    
4215
		$section->addInput(new Form_Input(
4216
			'name',
4217
			null,
4218
			'hidden',
4219
			$this->GetQname()
4220
		));
4221

    
4222
		if ($this->GetNumber() > 0) {
4223
			$section->addInput(new Form_Input(
4224
				'number',
4225
				null,
4226
				'hidden',
4227
				$this->GetNumber()
4228
			));
4229
		}
4230

    
4231
		$mask = $this->GetMask();
4232

    
4233
		$section->addInput(new Form_Select(
4234
			'mask',
4235
			'Mask',
4236
			$mask['type'],
4237
			array('none' => gettext('None'), 'srcaddress' => gettext('Source addresses'), 'dstaddress' => gettext('Destination addresses'))
4238
		))->setHelp('If "source" or "destination" slots is chosen a dynamic pipe with the bandwidth, delay, packet loss ' .
4239
					'and queue size given above will be created for each source/destination IP address encountered, respectively. ' .
4240
					'This makes it possible to easily specify bandwidth limits per host.');
4241

    
4242
		$group = new Form_Group(null);
4243

    
4244
		$group->add(new Form_Select(
4245
			'maskbits',
4246
			null,
4247
			$mask['bits'],
4248
			array_combine(range(32, 1, -1), range(32, 1, -1))
4249
		))->setHelp('IPV4 mask bits' . '<br />' . '255.255.255.255/?');
4250

    
4251
		$group->add(new Form_Select(
4252
			'maskbitsv6',
4253
			null,
4254
			$mask['bitsv6'],
4255
			array_combine(range(128, 1, -1), range(128, 1, -1))
4256
		))->setHelp('IPV6 mask bits' . '<br />' . '<span style="font-family:consolas">ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/?</span>');
4257

    
4258
		$section->add($group);
4259

    
4260
		$section->addInput(new Form_Input(
4261
			'description',
4262
			'Description',
4263
			'text',
4264
			$this->GetDescription()
4265
		))->setHelp('You may enter a description here for your reference (not parsed).');
4266

    
4267
		$sform->add($section);
4268

    
4269
		$section = new Form_Section('Advanced Options');
4270

    
4271
		$section->addInput(new Form_Input(
4272
			'weight',
4273
			'Weight',
4274
			'number',
4275
			$this->GetWeight(),
4276
			['min' => '1', 'max' => '100']
4277
		))->setHelp('For queues under the same parent this specifies the share that a queue gets(values range from 1 to 100),' .
4278
					' you can leave it blank otherwise');
4279

    
4280
		$section->addInput(new Form_Input(
4281
			'plr',
4282
			'Packet Loss Rate',
4283
			'number',
4284
			$this->GetPlr(),
4285
			['step' => '0.001', 'min' => '0.000']
4286
		))->setHelp('In most cases, you should specify 0 here (or leave the field empty). ' .
4287
					'A value of 0.001 means one packet in 1000 gets dropped');
4288

    
4289
		$section->addInput(new Form_Input(
4290
			'qlimit',
4291
			'Queue size (slots)',
4292
			'number',
4293
			$this->GetQlimit()
4294
		))->setHelp('In most cases, you should leave the field empty. All packets in this pipe are placed into a fixed-size queue first, ' .
4295
					'then they are delayed by value specified in the Delay field, and then they are delivered to their destination.');
4296

    
4297
		$section->addInput(new Form_Input(
4298
			'buckets',
4299
			'Bucket size (slots)',
4300
			'number',
4301
			$this->GetBuckets()
4302
		))->setHelp('In most cases, you should leave this field empty. It increases the hash size set');
4303

    
4304
		$section->addInput(new Form_Input(
4305
			'pipe',
4306
			null,
4307
			'hidden',
4308
			$this->GetPipe()
4309
		));
4310

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

    
4313
		return($sform);
4314
	}
4315

    
4316
	function update_dn_data(&$data) {
4317
		$this->ReadConfig($data);
4318
	}
4319

    
4320
	function wconfig() {
4321
		$cflink =& get_dn_reference_to_me_in_config($this->GetLink());
4322
		if (!is_array($cflink)) {
4323
			$cflink = array();
4324
		}
4325
		$cflink['name'] = $this->GetQname();
4326
		$cflink['number'] = $this->GetNumber();
4327
		$cflink['qlimit'] = $this->GetQlimit();
4328
		$cflink['description'] = $this->GetDescription();
4329
		$cflink['weight'] = $this->GetWeight();
4330
		$cflink['enabled'] = $this->GetEnabled();
4331
		$cflink['buckets'] = $this->GetBuckets();
4332
		$mask = $this->GetMask();
4333
		$cflink['mask'] = $mask['type'];
4334
		$cflink['maskbits'] = $mask['bits'];
4335
		$cflink['maskbitsv6'] = $mask['bitsv6'];
4336
	}
4337
}
4338

    
4339
function get_dummynet_name_list() {
4340

    
4341
	$dn_name_list =& get_unique_dnqueue_list();
4342
	$dn_name = array();
4343
	if (is_array($dn_name_list)) {
4344
		foreach ($dn_name_list as $key => $value) {
4345
			$dn_name[] = $key;
4346
		}
4347
	}
4348

    
4349
	return $dn_name;
4350

    
4351
}
4352

    
4353
function get_altq_name_list() {
4354
	$altq_name_list =& get_unique_queue_list();
4355
	$altq_name = array();
4356
	if (is_array($altq_name_list)) {
4357
		foreach ($altq_name_list as $key => $aqobj) {
4358
			$altq_name[] = $key;
4359
		}
4360
	}
4361

    
4362
	return $altq_name;
4363
}
4364

    
4365
/*
4366
 * XXX: TODO Make a class shaper to hide all these functions
4367
 * from the global namespace.
4368
 */
4369

    
4370
/*
4371
 * This is a layer violation but for now there is no way
4372
 * I can find to properly do this with PHP.
4373
 */
4374
function altq_get_default_queue($interface) {
4375
	global $altq_list_queues;
4376

    
4377
	$altq_tmp = $altq_list_queues[$interface];
4378
	if ($altq_tmp) {
4379
		return $altq_tmp->GetDefaultQueuePresent();
4380
	} else {
4381
		return false;
4382
	}
4383
}
4384

    
4385
function altq_check_default_queues() {
4386
	global $altq_list_queues;
4387

    
4388
	$count = 0;
4389
	if (is_array($altq_list_queues)) {
4390
		foreach ($altq_list_queues as $altq) {
4391
			if ($altq->GetDefaultQueuePresent()) {
4392
				$count++;
4393
			}
4394
		}
4395
	}
4396
	else {
4397
		$count++;
4398
	}
4399

    
4400
	return 0;
4401
}
4402

    
4403
function &get_unique_queue_list() {
4404
	global $altq_list_queues;
4405

    
4406
	$qlist = array();
4407
	if (is_array($altq_list_queues)) {
4408
		foreach ($altq_list_queues as $altq) {
4409
			if ($altq->GetEnabled() == "") {
4410
				continue;
4411
			}
4412
			$tmplist =& $altq->get_queue_list();
4413
			foreach ($tmplist as $qname => $link) {
4414
				if ($link->GetEnabled() <> "") {
4415
					$qlist[$qname] = $link;
4416
				}
4417
			}
4418
		}
4419
	}
4420
	return $qlist;
4421
}
4422

    
4423
function &get_unique_dnqueue_list() {
4424
	global $dummynet_pipe_list;
4425

    
4426
	$qlist = array();
4427
	if (is_array($dummynet_pipe_list)) {
4428
		foreach ($dummynet_pipe_list as $dn) {
4429
			if ($dn->GetEnabled() == "") {
4430
				continue;
4431
			}
4432
			$tmplist =& $dn->get_queue_list();
4433
			foreach ($tmplist as $qname => $link) {
4434
				$qlist[$qname] = $link;
4435
			}
4436
		}
4437
	}
4438
	return $qlist;
4439
}
4440

    
4441
function ref_on_altq_queue_list($parent, $qname) {
4442
	if (isset($GLOBALS['queue_list'][$qname])) {
4443
		$GLOBALS['queue_list'][$qname]++;
4444
	} else {
4445
		$GLOBALS['queue_list'][$qname] = 1;
4446
	}
4447

    
4448
	unref_on_altq_queue_list($parent);
4449
}
4450

    
4451
function unref_on_altq_queue_list($qname) {
4452
	$GLOBALS['queue_list'][$qname]--;
4453
	if ($GLOBALS['queue_list'][$qname] <= 1) {
4454
		unset($GLOBALS['queue_list'][$qname]);
4455
	}
4456
}
4457

    
4458
function read_altq_config() {
4459
	global $altq_list_queues, $config;
4460
	$path = array();
4461

    
4462
	if (!is_array($config['shaper'])) {
4463
		$config['shaper'] = array();
4464
	}
4465
	if (!is_array($config['shaper']['queue'])) {
4466
		$config['shaper']['queue'] = array();
4467
	}
4468
	$a_int = &$config['shaper']['queue'];
4469

    
4470
	$altq_list_queues = array();
4471

    
4472
	if (!is_array($config['shaper']['queue'])) {
4473
		return;
4474
	}
4475

    
4476
	foreach ($a_int as $key => $conf) {
4477
		$int = $conf['interface'];
4478
		$root =& new altq_root_queue();
4479
		$root->SetInterface($int);
4480
		$altq_list_queues[$root->GetInterface()] = &$root;
4481
		$root->ReadConfig($conf);
4482
		array_push($path, $key);
4483
		$root->SetLink($path);
4484
		if (is_array($conf['queue'])) {
4485
			foreach ($conf['queue'] as $key1 => $q) {
4486
				array_push($path, $key1);
4487
				/*
4488
				 * XXX: we completely ignore errors here but anyway we must have
4489
				 *	checked them before so no harm should be come from this.
4490
				 */
4491
				$root->add_queue($root->GetInterface(), $q, $path, $input_errors);
4492
				array_pop($path);
4493
			}
4494
		}
4495
		array_pop($path);
4496
	}
4497
}
4498

    
4499
function read_dummynet_config() {
4500
	global $dummynet_pipe_list, $config;
4501
	$path = array();
4502

    
4503
	if (!is_array($config['dnshaper'])) {
4504
		$config['dnshaper'] = array();
4505
	}
4506
	if (!is_array($config['dnshaper']['queue'])) {
4507
		$config['dnshaper']['queue'] = array();
4508
	}
4509
	$a_int = &$config['dnshaper']['queue'];
4510

    
4511
	$dummynet_pipe_list = array();
4512

    
4513
	if (!is_array($config['dnshaper']['queue']) ||
4514
	    !count($config['dnshaper']['queue'])) {
4515
		return;
4516
	}
4517

    
4518
	foreach ($a_int as $key => $conf) {
4519
		if (empty($conf['name'])) {
4520
			continue; /* XXX: grrrrrr at php */
4521
		}
4522
		$root =& new dnpipe_class();
4523
		$root->ReadConfig($conf);
4524
		$dummynet_pipe_list[$root->GetQname()] = &$root;
4525
		array_push($path, $key);
4526
		$root->SetLink($path);
4527
		if (is_array($conf['queue'])) {
4528
			foreach ($conf['queue'] as $key1 => $q) {
4529
				array_push($path, $key1);
4530
				/*
4531
				 * XXX: we completely ignore errors here but anyway we must have
4532
				 *	checked them before so no harm should be come from this.
4533
				 */
4534
				$root->add_queue($root->GetQname(), $q, $path, $input_errors);
4535
				array_pop($path);
4536
			}
4537
		}
4538
		array_pop($path);
4539
	}
4540
}
4541

    
4542
function get_interface_list_to_show() {
4543
	global $altq_list_queues, $config;
4544
	global $shaperIFlist;
4545

    
4546
	$tree = "";
4547
	foreach ($shaperIFlist as $shif => $shDescr) {
4548
		if ($altq_list_queues[$shif]) {
4549
			continue;
4550
		} else {
4551
			if (!is_altq_capable(get_real_interface($shif))) {
4552
				continue;
4553
			}
4554
			$tree .= " <li><a href=\"firewall_shaper.php?interface=".$shif."&amp;action=add\">".$shDescr."</a></li>";
4555
		}
4556
	}
4557

    
4558
	return $tree;
4559
}
4560

    
4561
function filter_generate_altq_queues() {
4562
	global $altq_list_queues;
4563

    
4564
	read_altq_config();
4565

    
4566
	$altq_rules = "";
4567
	foreach ($altq_list_queues as $altq) {
4568
		$altq_rules .= $altq->build_rules();
4569
	}
4570

    
4571
	return $altq_rules;
4572
}
4573

    
4574
function dnqueue_find_nextnumber() {
4575
	global $dummynet_pipe_list;
4576

    
4577
	$dnused = array();
4578
	if (is_array($dummynet_pipe_list)) {
4579
		foreach ($dummynet_pipe_list as $dn) {
4580
			$tmplist =& $dn->get_queue_list();
4581
			foreach ($tmplist as $qname => $link) {
4582
				if ($link[0] == "?") {
4583
					$dnused[$qname] = substr($link, 1);
4584
				}
4585
			}
4586
		}
4587
	}
4588

    
4589
	sort($dnused, SORT_NUMERIC);
4590
	$dnnumber = 0;
4591
	$found = false;
4592
	foreach ($dnused as $dnnum) {
4593
		if (($dnnum - $dnnumber) > 1) {
4594
			$dnnumber = $dnnum - 1;
4595
			$found = true;
4596
			break;
4597
		} else {
4598
			$dnnumber = $dnnum;
4599
		}
4600
	}
4601

    
4602
	if ($found == false) {
4603
		$dnnumber++;
4604
	}
4605

    
4606
	unset($dnused, $dnnum, $found);
4607
	return $dnnumber;
4608
}
4609

    
4610
function dnpipe_find_nextnumber() {
4611
	global $dummynet_pipe_list;
4612

    
4613
	$dnused = array();
4614
	foreach ($dummynet_pipe_list as $dn) {
4615
		$dnused[] = $dn->GetNumber();
4616
	}
4617

    
4618
	sort($dnused, SORT_NUMERIC);
4619
	$dnnumber = 0;
4620
	$found = false;
4621
	foreach ($dnused as $dnnum) {
4622
		if (($dnnum - $dnnumber) > 1) {
4623
			$dnnumber = $dnnum - 1;
4624
			$found = true;
4625
			break;
4626
		} else {
4627
			$dnnumber = $dnnum;
4628
		}
4629
	}
4630

    
4631
	if ($found == false) {
4632
		$dnnumber++;
4633
	}
4634

    
4635
	unset($dnused, $dnnum, $found);
4636
	return $dnnumber;
4637
}
4638

    
4639
function filter_generate_dummynet_rules() {
4640
	global $g, $dummynet_pipe_list;
4641

    
4642
	read_dummynet_config();
4643

    
4644
	$dn_rules = "";
4645
	foreach ($dummynet_pipe_list as $dn) {
4646
		$dn_rules .= $dn->build_rules();
4647
	}
4648

    
4649
	if (!empty($dn_rules)) {
4650
		if (!is_module_loaded("dummynet.ko")) {
4651
			mwexec("/sbin/kldload dummynet");
4652
			set_sysctl(array(
4653
				"net.inet.ip.dummynet.io_fast" => "1",
4654
				"net.inet.ip.dummynet.hash_size" => "256"
4655
			));
4656
		}
4657
		file_put_contents("{$g['tmp_path']}/rules.limiter", $dn_rules);
4658
		mwexec("/sbin/ipfw {$g['tmp_path']}/rules.limiter");
4659
	}
4660
}
4661

    
4662
function build_iface_without_this_queue($iface, $qname) {
4663
	global $g, $altq_list_queues;
4664
	global $shaperIFlist;
4665

    
4666
	$altq =& $altq_list_queues[$iface];
4667

    
4668
	if ($altq) {
4669
		$scheduler = $altq->GetScheduler();
4670
	}
4671

    
4672
	$form = '<dl class="dl-horizontal">';
4673

    
4674
	$form .= '	<dt>';
4675
	$form .= '		<a href="firewall_shaper.php?interface=' . $iface . '&amp;queue=' . $iface . '&amp;action=show">' . $shaperIFlist[$iface] . '</a>';
4676
	$form .= '	</dt>';
4677
	$form .= '	<dd>';
4678
	$form .=		$scheduler;
4679
	$form .= '	</dd>';
4680

    
4681
	$form .= '	<dt>';
4682
	$form .= 'Clone';
4683
	$form .= '	</dt>';
4684
	$form .= '	<dd>';
4685
	$form .= '<a class="btn btn-info btn-xs" href="firewall_shaper_queues.php?interface=';
4686
	$form .= $iface . '&amp;queue=';
4687
	$form .= $qname . '&amp;action=add">';
4688
	$form .= '<i class="fa fa-clone icon-embed-btn"></i>';
4689
	$form .= gettext("Clone Shaper to this Interface") . '</a>';
4690
	$form .= '	</dd>';
4691

    
4692
	$form .= '</dl>';
4693

    
4694
	return $form;
4695

    
4696
}
4697

    
4698
$default_shaper_msg = sprintf(gettext("Welcome to the %s Traffic Shaper."), $g['product_name']) . "<br />";
4699
$default_shaper_msg .= gettext("The tree on the left helps you navigate through the queues.<br />"
4700
	. "Buttons at the bottom represent queue actions and are activated accordingly.");
4701

    
4702
$dn_default_shaper_msg = $default_shaper_msg;
4703

    
4704
?>
(50-50/65)