Project

General

Profile

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

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

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

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

    
75
	return $ptr;
76
}
77

    
78
function unset_object_by_reference(&$mypath) {
79
	global $config;
80

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

    
88
function &get_dn_reference_to_me_in_config(&$mypath) {
89
	global $config;
90

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

    
96
	return $ptr;
97
}
98

    
99
function unset_dn_object_by_reference(&$mypath) {
100
	global $config;
101

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

    
109
function clean_child_queues($type, $mypath) {
110
	$ref = &get_reference_to_me_in_config($mypath);
111

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

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

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

    
229
function get_interface_bandwidth($object) {
230
	global $altq_list_queues;
231

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

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

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

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

    
263
function cleanup_queue_from_rules($queue) {
264
	global $config;
265

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

    
276
function cleanup_dnqueue_from_rules($queue) {
277
	global $config;
278

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

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

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

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

    
384
	function validate_input($data, &$input_errors) {
385

    
386
		$reqdfields[] = "bandwidth";
387
		$reqdfieldsn[] = gettext("Bandwidth");
388
		$reqdfields[] = "bandwidthtype";
389
		$reqdfieldsn[] = gettext("Bandwidthtype");
390

    
391
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
392

    
393
		if ($data['bandwidth'] && (!is_numeric($data['bandwidth']))) {
394
			$input_errors[] = gettext("Bandwidth must be an integer.");
395
		}
396
		if ($data['bandwidth'] < 0) {
397
			$input_errors[] = gettext("Bandwidth cannot be negative.");
398
		}
399
		if ($data['qlimit'] && (!is_numeric($data['qlimit']))) {
400
			$input_errors[] = gettext("Qlimit must be an integer.");
401
		}
402
		if ($data['qlimit'] < 0) {
403
			$input_errors[] = gettext("Qlimit must be positive.");
404
		}
405
		if ($data['tbrconfig'] && (!is_numeric($data['tbrconfig']))) {
406
			$input_errors[] = gettext("Tbrsize must be an integer.");
407
		}
408
		if ($data['tbrconfig'] < 0) {
409
			$input_errors[] = gettext("Tbrsize must be positive.");
410
		}
411
	}
412

    
413
	/* Implement this to shorten some code on the frontend page */
414
	function ReadConfig(&$conf) {
415
		if (isset($conf['tbrconfig'])) {
416
			$this->SetTbrConfig($conf['tbrconfig']);
417
		} else {
418
			$this->SetTbrConfig($conf['tbrconfig']);
419
		}
420
		$this->SetBandwidth($conf['bandwidth']);
421
		if ($conf['bandwidthtype'] <> "") {
422
			$this->SetBwscale($conf['bandwidthtype']);
423
		}
424
		if (isset($conf['scheduler'])) {
425
			if ($this->GetScheduler() != $conf['scheduler']) {
426
				foreach ($this->queues as $q) {
427
					clean_child_queues($conf['scheduler'], $this->GetLink());
428
					$q->clean_queue($conf['scheduler']);
429
				}
430
			}
431
			$this->SetScheduler($conf['scheduler']);
432
		}
433
		if (isset($conf['qlimit']) && $conf['qlimit'] <> "") {
434
			$this->SetQlimit($conf['qlimit']);
435
		} else {
436
			$this->SetQlimit("");
437
		}
438
		if (isset($conf['name'])) {
439
			$this->SetQname($conf['name']);
440
		}
441
		if (!empty($conf['enabled'])) {
442
			$this->SetEnabled($conf['enabled']);
443
		} else {
444
			$this->SetEnabled("");
445
		}
446
	}
447

    
448
	function copy_queue($interface, &$cflink) {
449
		$cflink['interface'] = $interface;
450
		$cflink['name'] = $interface;
451
		$cflink['scheduler'] = $this->GetScheduler();
452
		$cflink['bandwidth'] = $this->GetBandwidth();
453
		$cflink['bandwidthtype'] = $this->GetBwscale();
454
		$cflink['qlimit'] = $this->GetQlimit();
455
		$cflink['tbrconfig'] = $this->GetTbrConfig();
456
		$cflink['enabled'] = $this->GetEnabled();
457
		if (is_array($this->queues)) {
458
			$cflink['queue'] = array();
459
			foreach ($this->queues as $q) {
460
				$cflink['queue'][$q->GetQname()] = array();
461
				$q->copy_queue($interface, $cflink['queue'][$q->GetQname()]);
462
			}
463
		}
464
	}
465

    
466
	function &get_queue_list(&$q = null) {
467
		$qlist = array();
468

    
469
		//$qlist[$this->GetQname()] = & $this;
470
		if (is_array($this->queues)) {
471
			foreach ($this->queues as $queue) {
472
				$queue->get_queue_list($qlist);
473
			}
474
		}
475
		return $qlist;
476
	}
477

    
478
	function &add_queue($interface, &$queue, &$path, &$input_errors) {
479

    
480
		if (!is_array($this->queues)) {
481
			$this->queues = array();
482
		}
483

    
484
		switch ($this->GetScheduler()) {
485
			case "PRIQ":
486
				$q =& new priq_queue();
487
				break;
488
			case "HFSC":
489
				$q =& new hfsc_queue();
490
				break;
491
			case "CBQ":
492
				$q =& new cbq_queue();
493
				break;
494
			case "FAIRQ":
495
				$q =& new fairq_queue();
496
				break;
497
			default:
498
				/* XXX: but should not happen anyway */
499
				return;
500
				break;
501
		}
502
		$q->SetLink($path);
503
		$q->SetInterface($this->GetInterface());
504
		$q->SetEnabled("on");
505
		$q->SetParent($this);
506
		$q->ReadConfig($queue);
507
		$q->validate_input($queue, $input_errors);
508
		if (count($input_errors)) {
509
			log_error("SHAPER: could not create queue " . $q->GetQname() . " on interface {$interface} because: " . print_r($input_errors, true));
510
			return $q;
511
		}
512

    
513
		if (isset($queue['bandwidth'])) {
514
			switch ($queue['bandwidthtype']) {
515
				case "%":
516
					$myBw = $this->GetAvailableBandwidth() * $queue['bandwidth'] / 100;
517
					break;
518
				default:
519
					$myBw = $queue['bandwidth'] * get_bandwidthtype_scale($queue['bandwidthtype']);
520
					break;
521
			}
522
		}
523
		$q->SetAvailableBandwidth($myBw);
524
		$this->SetAvailableBandwidth($this->GetAvailableBandwidth() - $myBw);
525
		$this->queues[$q->GetQname()] = &$q;
526
		ref_on_altq_queue_list($this->GetQname(), $q->GetQname());
527
		if (is_array($queue['queue'])) {
528
			foreach ($queue['queue'] as $key1 => $que) {
529
				array_push($path, $key1);
530
				$q->add_queue($q->GetInterface(), $que, $path, $input_errors);
531
				array_pop($path);
532
			}
533
		}
534

    
535
		return $q;
536
	}
537

    
538
	/* interface here might be optional */
539
	function &find_queue($interface, $qname) {
540
		if ($qname == $this->GetQname()) {
541
			return $this;
542
		}
543
		foreach ($this->queues as $q) {
544
			$result =& $q->find_queue("", $qname);
545
			if ($result) {
546
				return $result;
547
			}
548
		}
549
	}
550

    
551
	function &find_parentqueue($interface, $qname) {
552
		if ($qname == $interface) {
553
			$result = NULL;
554
		} else if ($this->queues[$qname]) {
555
			$result = $this;
556
		} else if ($this->GetScheduler() <> "PRIQ") {
557
			foreach ($this->queues as $q) {
558
				$result = $q->find_parentqueue("", $qname);
559
				if ($result) {
560
					return $result;
561
				}
562
			}
563
		}
564
	}
565

    
566
	function build_tree() {
567
		global $shaperIFlist;
568

    
569
		$tree = " <li><a href=\"firewall_shaper.php?interface=".$this->GetInterface()."&amp;queue=". $this->GetInterface()."&amp;action=show";
570
		$tree .= "\">" . $shaperIFlist[$this->GetInterface()] . "</a>";
571
		if (is_array($this->queues)) {
572
			$tree .= "<ul>";
573
			foreach ($this->queues as $q) {
574
				$tree .= $q->build_tree();
575
			}
576
			$tree .= "</ul>";
577
		}
578
		$tree .= "</li>";
579
		return $tree;
580
	}
581

    
582
	function delete_queue() {
583
		foreach ($this->queues as $q) {
584
			$this->SetAvailableBandwidth($this->GetAvailableBandwidth() + $q->GetAvailableBandwidth());
585
			$q->delete_queue();
586
		}
587
		unset_object_by_reference($this->GetLink());
588
	}
589

    
590
	function delete_all() {
591
		if (count($this->queues)) {
592
			foreach ($this->queues as $q) {
593
				$q->delete_all();
594
				unset_object_by_reference($q->GetLink());
595
				unset($q);
596
			}
597
			unset($this->queues);
598
		}
599
	}
600

    
601
	/*
602
	 * First it spits:
603
	 * altq on $interface ..............
604
	 *	then it goes like
605
	 *	foreach ($queues as $qkey => $queue) {
606
	 *		this->queues[$qkey]->build_rule();
607
	 *	}
608
	 */
609
	function build_rules(&$default = false) {
610
		if (count($this->queues) > 0 && $this->GetEnabled() == "on") {
611
			$default = false;
612
			$rules = " altq on " . get_real_interface($this->GetInterface());
613
			if ($this->GetScheduler()) {
614
				$rules .= " ".strtolower($this->GetScheduler());
615
			}
616
			if ($this->GetQlimit() > 0) {
617
				$rules .= " qlimit " . $this->GetQlimit() . " ";
618
			}
619
			if ($this->GetBandwidth()) {
620
				$rules .= " bandwidth ".trim($this->GetBandwidth());
621
				if ($this->GetBwscale()) {
622
					$rules .= $this->GetBwscale();
623
				}
624
			}
625
			if ($this->GetTbrConfig()) {
626
				$rules .= " tbrsize ".$this->GetTbrConfig();
627
			}
628
			if (count($this->queues)) {
629
				$i = count($this->queues);
630
				$rules .= " queue { ";
631
				foreach ($this->queues as $qkey => $qnone) {
632
					if ($i > 1) {
633
						$i--;
634
						$rules .= " {$qkey}, ";
635
					} else {
636
						$rules .= " {$qkey} ";
637
					}
638
				}
639
				$rules .= " } \n";
640
				foreach ($this->queues as $q) {
641
					$rules .= $q->build_rules($default);
642
				}
643
			}
644

    
645
			if ($default == false) {
646
				$error = "SHAPER: no default queue specified for interface ". $this->GetInterface() . ". The interface queue will be enforced as default.";
647
				file_notice("Shaper", $error, "Error occurred", "");
648
				unset($error);
649
				return "\n";
650
			}
651
			$frule .= $rules;
652
		} else if ($this->GetEnabled() == "on" && $this->GetScheduler() == "CODELQ") {
653
			$rules = " altq on " . get_real_interface($this->GetInterface());
654
			if ($this->GetScheduler()) {
655
				$rules .= " ".strtolower($this->GetScheduler());
656
			}
657
			if ($this->GetQlimit() > 0) {
658
				$rules .= " ( qlimit " . $this->GetQlimit() . " ) ";
659
			}
660
			if ($this->GetBandwidth()) {
661
				$rules .= " bandwidth ".trim($this->GetBandwidth());
662
				if ($this->GetBwscale()) {
663
					$rules .= $this->GetBwscale();
664
				}
665
			}
666
			if ($this->GetTbrConfig()) {
667
				$rules .= " tbrsize ".$this->GetTbrConfig();
668
			}
669

    
670
			$rules .= " queue";
671
		}
672

    
673
		$rules .= " \n";
674
		return $rules;
675
	}
676

    
677
	function build_javascript() {
678
		$javascript = "<script type=\"text/javascript\">";
679
		$javascript .= "//<![CDATA[\n";
680
		$javascript .= "function mySuspend() {";
681
		$javascript .= "if (document.layers && document.layers['shaperarea'] != null) ";
682
		$javascript .= "document.layers['shaperarea'].visibility = 'hidden'; ";
683
		$javascript .= "else if (document.all)";
684
		$javascript .= "document.all['shaperarea'].style.visibility = 'hidden';";
685
		$javascript .= "}";
686

    
687
		$javascript .= "function myResume() {";
688
		$javascript .= "if (document.layers && document.layers['shaperarea'] != null) ";
689
		$javascript .= "document.layers['shaperarea'].visibility = 'visible';";
690
		$javascript .= "else if (document.all) ";
691
		$javascript .= "document.all['shaperarea'].style.visibility = 'visible';";
692
		$javascript .= "}";
693
		$javascript .= "//]]>";
694
		$javascript .= "</script>";
695

    
696
		return $javascript;
697
	}
698

    
699
	function build_shortform() {
700
		global $g;
701

    
702
		$altq =& $this;
703

    
704
		if ($altq) {
705
			$scheduler = ": " . $altq->GetScheduler();
706
		}
707

    
708
		$form = '<dl class="dl-horizontal">';
709
		$form .= '	<dt>';
710
		$form .= '		<a href="firewall_shaper.php?interface=' . $this->GetInterface() . '&amp;queue=' . $this->GetQname() . '&amp;action=show">' . $shaperIFlist[$this->GetInterface()] . '</a>';
711
		$form .= '	</dt>';
712
		$form .= '	<dd>';
713
		$form .=		$scheduler;
714
		$form .= '	</dd>';
715

    
716
		$form .= '	<dt>';
717
		$form .=		'Bandwidth';
718
		$form .= '	</dt>';
719
		$form .= '	<dd>';
720
		$form .=		$this->GetBandwidth() . '&nbsp;' . $this->GetBwscale();
721
		$form .= '	</dd>';
722

    
723
		$form .= '	<dt>';
724
		$form .= 'Disable';
725
		$form .= '	<dt>';
726
		$form .= '	<dd>';
727

    
728
		$form .= '<a class="btn btn-default btn-xs" href="firewall_shaper_queues.php?interface=';
729
		$form .= $this->GetInterface() . '&amp;queue=';
730
		$form .= $this->GetQname() . '&amp;action=delete">';
731
		$form .= gettext("Disable shaper on interface") . '</a>';
732

    
733
		$form .= '	</dd>';
734

    
735
		$form .= '</dl>';
736

    
737
		return $form;
738

    
739
	}
740

    
741
	/*
742
	 * For requesting the parameters of the root queues
743
	 * to the user like the traffic wizard does.
744
	 */
745
	function build_form() {
746

    
747
		$sform = new Form(new Form_Button(
748
			'Submit',
749
			'Save'
750
		));
751

    
752
		$section = new Form_Section(null);
753

    
754
		$section->addInput(new Form_Checkbox(
755
			'enabled',
756
			'Enable/Disable',
757
			'Enable/disable discipline and its children',
758
			($this->GetEnabled() == "on"),
759
			'on'
760
		));
761

    
762
		$section->addInput(new Form_StaticText(
763
			'Name',
764
			$this->GetQname()
765
		));
766

    
767
		$section->addInput(new Form_Select(
768
			'scheduler',
769
			'Scheduler Type',
770
			$this->GetScheduler(),
771
			array('HFSC' => 'HFSC',
772
				  'CBQ' => 'CBQ',
773
				  'FAIRQ' => 'FAIRQ',
774
				  'CODELQ' => 'CODELQ',
775
				  'PRIQ' => 'PRIQ')
776
		))->setHelp('Changing this changes all child queues! Beware you can lose information.');
777

    
778
		$group = new Form_group('Bandwidth');
779

    
780
		$group->add(new Form_Input(
781
			'bandwidth',
782
			null,
783
			'number',
784
			$this->GetBandwidth()
785
		));
786

    
787
		$group->add(new Form_Select(
788
			'bandwidthtype',
789
			null,
790
			$this->GetBwscale(),
791
			array('Kb' => 'Kb',
792
				  'Mb' => 'Mb',
793
				  'Gb' => 'Gb',
794
				  'b' => 'b',
795
				  '%' => '%')
796
		));
797

    
798
		$section->add($group);
799

    
800
		$section->addInput(new Form_Input(
801
			'qlimit',
802
			'Queue Limit',
803
			'number',
804
			$this->GetQlimit()
805
		));
806

    
807
		$section->addInput(new Form_Input(
808
			'tbrconfig',
809
			'TRB Size',
810
			'number',
811
			$this->GetTbrConfig()
812
		))->setHelp('Adjusts the size, in bytes, of the token bucket regulator. If not specified, heuristics based on the interface ' .
813
					'bandwidth are used to determine the size.');
814

    
815
		$section->addInput(new Form_Input(
816
			'interface',
817
			null,
818
			'hidden',
819
			$this->GetInterface()
820
		));
821

    
822
		$section->addInput(new Form_Input(
823
			'name',
824
			null,
825
			'hidden',
826
			$this->GetQname()
827
		));
828

    
829
		$sform->add($section);
830

    
831
		return($sform);
832
	}
833

    
834
	function update_altq_queue_data(&$data) {
835
		$this->ReadConfig($data);
836
	}
837

    
838
	/*
839
	 * Should call on each of it queues and subqueues
840
	 * the same function much like build_rules();
841
	 */
842
	function wconfig() {
843
		$cflink = &get_reference_to_me_in_config($this->GetLink());
844
		if (!is_array($cflink)) {
845
			$cflink = array();
846
		}
847
		$cflink['interface'] = $this->GetInterface();
848
		$cflink['name'] = $this->GetQname();
849
		$cflink['scheduler'] = $this->GetScheduler();
850
		$cflink['bandwidth'] = $this->GetBandwidth();
851
		$cflink['bandwidthtype'] = $this->GetBwscale();
852
		$cflink['qlimit'] = trim($this->GetQlimit());
853
		if (empty($cflink['qlimit'])) {
854
			unset($cflink['qlimit']);
855
		}
856
		$cflink['tbrconfig'] = trim($this->GetTbrConfig());
857
		if (empty($cflink['tbrconfig'])) {
858
			unset($cflink['tbrconfig']);
859
		}
860
		$cflink['enabled'] = $this->GetEnabled();
861
		if (empty($cflink['enabled'])) {
862
			unset($cflink['enabled']);
863
		}
864
	}
865

    
866
}
867

    
868
class priq_queue {
869
	var $qname;
870
	var $qinterface;
871
	var $qlimit;
872
	var $qpriority;
873
	var $description;
874
	var $isparent;
875
	var $qbandwidth;
876
	var $qbandwidthtype;
877
	var $qdefault = "";
878
	var $qrio = "";
879
	var $qred = "";
880
	var $qcodel = "";
881
	var $qecn = "";
882
	var $qack;
883
	var $qenabled = "";
884
	var $qparent;
885
	var $link;
886
	var $available_bw; /* in b/s */
887

    
888
	/* This is here to help with form building and building rules/lists */
889
	var $subqueues = array();
890

    
891
	/* Accessor functions */
892
	function GetAvailableBandwidth() {
893
		return $this->available_bw;
894
	}
895
	function SetAvailableBandwidth($bw) {
896
		$this->available_bw = $bw;
897
	}
898
	function SetLink($link) {
899
		$this->link = $link;
900
	}
901
	function GetLink() {
902
		return $this->link;
903
	}
904
	function &GetParent() {
905
		return $this->qparent;
906
	}
907
	function SetParent(&$parent) {
908
		$this->qparent = &$parent;
909
	}
910
	function GetEnabled() {
911
		return $this->qenabled;
912
	}
913
	function SetEnabled($value) {
914
		$this->qenabled = $value;
915
	}
916
	function CanHaveChildren() {
917
		return false;
918
	}
919
	function CanBeDeleted() {
920
		return true;
921
	}
922
	function GetQname() {
923
		return $this->qname;
924
	}
925
	function SetQname($name) {
926
		$this->qname = trim($name);
927
	}
928
	function GetBandwidth() {
929
		return $this->qbandwidth;
930
	}
931
	function SetBandwidth($bandwidth) {
932
		$this->qbandwidth = $bandwidth;
933
	}
934
	function GetInterface() {
935
		return $this->qinterface;
936
	}
937
	function SetInterface($name) {
938
		$this->qinterface = trim($name);
939
	}
940
	function GetQlimit() {
941
		return $this->qlimit;
942
	}
943
	function SetQlimit($limit) {
944
		$this->qlimit = $limit;
945
	}
946
	function GetQpriority() {
947
		return $this->qpriority;
948
	}
949
	function SetQpriority($priority) {
950
		$this->qpriority = $priority;
951
	}
952
	function GetDescription() {
953
		return $this->description;
954
	}
955
	function SetDescription($str) {
956
		$this->description = trim($str);
957
	}
958
	function GetFirstime() {
959
		return $this->firsttime;
960
	}
961
	function SetFirsttime($number) {
962
		$this->firsttime = $number;
963
	}
964
	function GetBwscale() {
965
		return $this->qbandwidthtype;
966
	}
967
	function SetBwscale($scale) {
968
		$this->qbandwidthtype = $scale;
969
	}
970
	function GetDefaultQueuePresent() {
971
		if ($this->GetDefault()) {
972
			return true;
973
		}
974
		if (!empty($this->subqueues)) {
975
			foreach ($this->subqueues as $q) {
976
				if ($q->GetDefault()) {
977
					return true;
978
				}
979
			}
980
		}
981

    
982
		return false;
983
	}
984
	function GetDefault() {
985
		return $this->qdefault;
986
	}
987
	function SetDefault($value = false) {
988
		$this->qdefault = $value;
989
	}
990
	function GetCodel() {
991
		return $this->codel;
992
	}
993
	function SetCodel($codel = false) {
994
		$this->codel = $codel;
995
	}
996
	function GetRed() {
997
		return $this->qred;
998
	}
999
	function SetRed($red = false) {
1000
		$this->qred = $red;
1001
	}
1002
	function GetRio() {
1003
		return $this->qrio;
1004
	}
1005
	function SetRio($rio = false) {
1006
		$this->qrio = $rio;
1007
	}
1008
	function GetEcn() {
1009
		return $this->qecn;
1010
	}
1011
	function SetEcn($ecn = false) {
1012
		$this->qecn = $ecn;
1013
	}
1014
	function GetAck() {
1015
		return $this->qack;
1016
	}
1017
	function SetAck($ack = false) {
1018
		$this->qack = $ack;
1019
	}
1020

    
1021
	function build_javascript() {
1022
		$javascript = "<script type=\"text/javascript\">";
1023
		$javascript .= "//<![CDATA[\n";
1024
		$javascript .= "function mySuspend() { \n";
1025
		$javascript .= "if (document.layers && document.layers['shaperarea'] != null)\n";
1026
		$javascript .= "document.layers['shaperarea'].visibility = 'hidden';\n";
1027
		$javascript .= "else if (document.all)\n";
1028
		$javascript .= "document.all['shaperarea'].style.visibility = 'hidden';\n";
1029
		$javascript .= "}\n";
1030

    
1031
		$javascript .= "function myResume() {\n";
1032
		$javascript .= "if (document.layers && document.layers['shaperarea'] != null)\n";
1033
		$javascript .= "document.layers['shaperarea'].visibility = 'visible';\n";
1034
		$javascript .= "else if (document.all)\n";
1035
		$javascript .= "document.all['shaperarea'].style.visibility = 'visible';\n";
1036
		$javascript .= "}\n";
1037
		$javascript .= "//]]>";
1038
		$javascript .= "</script>";
1039

    
1040
		return $javascript;
1041
	}
1042

    
1043
	function &add_queue($interface, &$qname, &$path, &$input_errors) { return; }
1044

    
1045
	/*
1046
	 * Currently this will not be called unless we decide to clone a whole
1047
	 * queue tree on the 'By Queues' view or support drag&drop on the tree/list
1048
	 */
1049
	function copy_queue($interface, &$cflink) {
1050

    
1051
		$cflink['name'] = $this->GetQname();
1052
		$cflink['interface'] = $interface;
1053
		$cflink['qlimit'] = $this->GetQlimit();
1054
		$cflink['priority'] = $this->GetQpriority();
1055
		$cflink['description'] = $this->GetDescription();
1056
		$cflink['enabled'] = $this->GetEnabled();
1057
		$cflink['default'] = $this->GetDefault();
1058
		$cflink['red'] = $this->GetRed();
1059
		$cflink['codel'] = $this->GetCodel();
1060
		$cflink['rio'] = $this->GetRio();
1061
		$cflink['ecn'] = $this->GetEcn();
1062

    
1063
		if (is_array($this->subqueues)) {
1064
			$cflinkp['queue'] = array();
1065
			foreach ($this->subqueues as $q) {
1066
				$cflink['queue'][$q->GetQname()] = array();
1067
				$q->copy_queue($interface, $cflink['queue'][$q->GetQname()]);
1068
			}
1069
		}
1070
	}
1071

    
1072
	function clean_queue($sched) {
1073
		clean_child_queues($sched, $this->GetLink());
1074
		if (is_array($this->subqueues)) {
1075
			foreach ($this->subqueues as $q) {
1076
				$q->clean_queue($sched);
1077
			}
1078
		}
1079
	}
1080

    
1081
	function &get_queue_list(&$qlist) {
1082

    
1083
		$qlist[$this->GetQname()] = & $this;
1084
		if (is_array($this->subqueues)) {
1085
			foreach ($this->subqueues as $queue) {
1086
				$queue->get_queue_list($qlist);
1087
			}
1088
		}
1089
	}
1090

    
1091
	function delete_queue() {
1092
		unref_on_altq_queue_list($this->GetQname());
1093
		cleanup_queue_from_rules($this->GetQname());
1094
		unset_object_by_reference($this->GetLink());
1095
	}
1096

    
1097
	function delete_all() {
1098
		if (count($this->subqueues)) {
1099
			foreach ($this->subqueues as $q) {
1100
				$q->delete_all();
1101
				unset_object_by_reference($q->GetLink());
1102
				unset($q);
1103
			}
1104
			unset($this->subqueues);
1105
		}
1106
	}
1107

    
1108
	function &find_queue($interface, $qname) {
1109
		if ($qname == $this->GetQname()) {
1110
			return $this;
1111
		}
1112
	}
1113

    
1114
	function find_parentqueue($interface, $qname) { return; }
1115

    
1116
	function validate_input($data, &$input_errors) {
1117

    
1118
		$reqdfields[] = "name";
1119
		$reqdfieldsn[] = gettext("Name");
1120
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
1121

    
1122
		if ($data['bandwidth'] && (!is_numeric($data['bandwidth']))) {
1123
			$input_errors[] = "Bandwidth must be an integer.";
1124
		}
1125
		if ($data['bandwidth'] < 0) {
1126
			$input_errors[] = "Bandwidth cannot be negative.";
1127
		}
1128
		if ($data['priority'] && (!is_numeric($data['priority']) ||
1129
		    ($data['priority'] < 1) || ($data['priority'] > 15))) {
1130
			$input_errors[] = gettext("The priority must be an integer between 1 and 15.");
1131
		}
1132
		if ($data['qlimit'] && (!is_numeric($data['qlimit']))) {
1133
				$input_errors[] = gettext("Queue limit must be an integer");
1134
		}
1135
		if ($data['qlimit'] < 0) {
1136
				$input_errors[] = gettext("Queue limit must be positive");
1137
		}
1138
		if (!empty($data['newname']) && !preg_match("/^[a-zA-Z0-9_-]*$/", $data['newname'])) {
1139
			$input_errors[] = gettext("Queue names must be alphanumeric and _ or - only.");
1140
		}
1141
		if (!empty($data['name']) && !preg_match("/^[a-zA-Z0-9_-]*$/", $data['name'])) {
1142
			$input_errors[] = gettext("Queue names must be alphanumeric and _ or - only.");
1143
		}
1144
		$default = $this->GetDefault();
1145
		if (!empty($data['default']) && altq_get_default_queue($data['interface']) && empty($default)) {
1146
			$input_errors[] = gettext("Only one default queue per interface is allowed.");
1147
		}
1148
	}
1149

    
1150
	function ReadConfig(&$q) {
1151
		if (!empty($q['name']) && !empty($q['newname']) && $q['name'] != $q['newname']) {
1152
			$this->SetQname($q['newname']);
1153
		} else if (!empty($q['newname'])) {
1154
			$this->SetQname($q['newname']);
1155
		} else if (isset($q['name'])) {
1156
			$this->SetQname($q['name']);
1157
		}
1158
		if (isset($q['interface'])) {
1159
			$this->SetInterface($q['interface']);
1160
		}
1161
		$this->SetBandwidth($q['bandwidth']);
1162
		if ($q['bandwidthtype'] <> "") {
1163
			$this->SetBwscale($q['bandwidthtype']);
1164
		}
1165
		if (!empty($q['qlimit'])) {
1166
			$this->SetQlimit($q['qlimit']);
1167
		} else {
1168
			$this->SetQlimit(""); // Default
1169
		}
1170
		if (!empty($q['priority'])) {
1171
			$this->SetQPriority($q['priority']);
1172
		} else {
1173
			$this->SetQpriority("");
1174
		}
1175
		if (!empty($q['description'])) {
1176
			$this->SetDescription($q['description']);
1177
		} else {
1178
			$this->SetDescription("");
1179
		}
1180
		if (!empty($q['red'])) {
1181
			$this->SetRed($q['red']);
1182
		} else {
1183
			$this->SetRed();
1184
		}
1185
		if (!empty($q['codel'])) {
1186
			$this->SetCodel($q['codel']);
1187
		} else {
1188
			$this->SetCodel();
1189
		}
1190
		if (!empty($q['rio'])) {
1191
			$this->SetRio($q['rio']);
1192
		} else {
1193
			$this->SetRio();
1194
		}
1195
		if (!empty($q['ecn'])) {
1196
			$this->SetEcn($q['ecn']);
1197
		} else {
1198
			$this->SetEcn();
1199
		}
1200
		if (!empty($q['default'])) {
1201
			$this->SetDefault($q['default']);
1202
		} else {
1203
			$this->SetDefault();
1204
		}
1205
		if (!empty($q['enabled'])) {
1206
			$this->SetEnabled($q['enabled']);
1207
		} else {
1208
			$this->SetEnabled("");
1209
		}
1210
	}
1211

    
1212
	function build_tree() {
1213
		$tree = " <li><a href=\"firewall_shaper.php?interface=". $this->GetInterface()."&amp;queue=". $this->GetQname()."&amp;action=show";
1214
		$tree .= "\" ";
1215
		$tmpvalue = $this->GetDefault();
1216
		if (!empty($tmpvalue)) {
1217
			$tree .= " class=\"navlnk\"";
1218
		}
1219
		$tree .= " >" . $this->GetQname() . "</a>";
1220
		/*
1221
		 * Not needed here!
1222
		 * if (is_array($queues) {
1223
		 *	  $tree .= "<ul>";
1224
		 *	  foreach ($q as $queues)
1225
		 *		  $tree .= $queues['$q->GetName()']->build_tree();
1226
		 *	  endforeach
1227
		 *	  $tree .= "</ul>";
1228
		 * }
1229
		 */
1230

    
1231
		$tree .= "</li>";
1232

    
1233
		return $tree;
1234
	}
1235

    
1236
	/* Should return something like:
1237
	 * queue $qname on $qinterface bandwidth ....
1238
	 */
1239
	function build_rules(&$default = false) {
1240
		$pfq_rule = " queue ". $this->qname;
1241
		if ($this->GetInterface()) {
1242
			$pfq_rule .= " on ".get_real_interface($this->GetInterface());
1243
		}
1244
		$tmpvalue = $this->GetQpriority();
1245
		if (!empty($tmpvalue)) {
1246
			$pfq_rule .= " priority ".$this->GetQpriority();
1247
		}
1248
		$tmpvalue = $this->GetQlimit();
1249
		if (!empty($tmpvalue)) {
1250
			$pfq_rule .= " qlimit " . $this->GetQlimit();
1251
		}
1252
		if ($this->GetRed() || $this->GetRio() || $this->GetEcn() || $this->GetDefault() || $this->GetCodel()) {
1253
			$pfq_rule .= " priq ( ";
1254
			$tmpvalue = $this->GetRed();
1255
			if (!empty($tmpvalue)) {
1256
				$comma = 1;
1257
				$pfq_rule .= " red ";
1258
			}
1259
			$tmpvalue = $this->GetRio();
1260
			if (!empty($tmpvalue)) {
1261
				if ($comma) {
1262
					$pfq_rule .= " ,";
1263
				}
1264
				$comma = 1;
1265
				$pfq_rule .= " rio ";
1266
			}
1267
			$tmpvalue = $this->GetEcn();
1268
			if (!empty($tmpvalue)) {
1269
				if ($comma) {
1270
					$pfq_rule .= " ,";
1271
				}
1272
				$comma = 1;
1273
				$pfq_rule .= " ecn ";
1274
			}
1275
			$tmpvalue = $this->GetCodel();
1276
			if (!empty($tmpvalue)) {
1277
				if ($comma) {
1278
					$pfq_rule .= " ,";
1279
				}
1280
				$comma = 1;
1281
				$pfq_rule .= " codel ";
1282
			}
1283
			$tmpvalue = $this->GetDefault();
1284
			if (!empty($tmpvalue)) {
1285
				if ($comma) {
1286
					$pfq_rule .= " ,";
1287
				}
1288
				$pfq_rule .= " default ";
1289
				$default = true;
1290
			}
1291
			$pfq_rule .= " ) ";
1292
		}
1293

    
1294
		$pfq_rule .= " \n";
1295

    
1296
		return $pfq_rule;
1297
	}
1298

    
1299
	/*
1300
	 * To return the html form to show to user
1301
	 * for getting the parameters.
1302
	 * Should do even for first time when the
1303
	 * object is created and later when we may
1304
	 * need to update it. (2)
1305
	 */
1306

    
1307
	function build_form() {
1308

    
1309
		$sform = new Form();
1310

    
1311
		$section = new Form_Section(null);
1312

    
1313
		$section->addInput(new Form_Checkbox(
1314
			'enabled',
1315
			'Enable/Disable',
1316
			'Enable/disable discipline and its children',
1317
			($this->GetEnabled() == "on"),
1318
			'on'
1319
		));
1320

    
1321
		$section->addInput(new Form_StaticText(
1322
			'Name',
1323
			$this->GetQname()
1324
		))->setHelp('Enter the name of the queue here. Do not use spaces and limit the size to 15 characters.');
1325

    
1326
		$section->addInput(new Form_Input(
1327
			'priority',
1328
			'Priority',
1329
			'number',
1330
			$this->GetQpriority(),
1331
			['min' => '0', 'max'=> '7']
1332
		))->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.');
1333

    
1334
		$section->addInput(new Form_Input(
1335
			'qlimit',
1336
			'Queue Limit',
1337
			'number',
1338
			$this->GetQlimit()
1339
		))->setHelp('Queue limit in packets.');
1340

    
1341
		$group = new Form_Group('Scheduler options');
1342

    
1343
		if (empty($this->subqueues)) {
1344
			$group->add(new Form_Checkbox(
1345
				'default',
1346
				null,
1347
				null,
1348
				$this->GetDefault()
1349
			))->setHelp('Default Queue');
1350
		}
1351

    
1352
		$group->add(new Form_Checkbox(
1353
			'red',
1354
			null,
1355
			null,
1356
			!empty($this->GetRed())
1357
		))->setHelp('<a target="_new" href="http://www.openbsd.org/faq/pf/queueing.html#red">' . gettext('Random Early Detection') . '</a>');
1358

    
1359
		$group->add(new Form_Checkbox(
1360
			'rio',
1361
			null,
1362
			null,
1363
			!empty($this->GetRio())
1364
		))->setHelp('<a target="_new" href="http://www.openbsd.org/faq/pf/queueing.html#rio">' . gettext('Random Early Detection In and Out') . '</a>');
1365

    
1366
		$group->add(new Form_Checkbox(
1367
			'ecn',
1368
			null,
1369
			null,
1370
			!empty($this->GetEcn())
1371
		))->setHelp('<a target="_new" href="http://www.openbsd.org/faq/pf/queueing.html#ecn">' . gettext('Explicit Congestion Notification') . '</a>');
1372

    
1373
		$group->add(new Form_Checkbox(
1374
			'codel',
1375
			null,
1376
			null,
1377
			!empty($this->GetCodel())
1378
		))->setHelp('<a target="_new" href="http://www.openbsd.org/faq/pf/queueing.html#ecn">' . gettext('Explicit Congestion Notification') . '</a>');
1379

    
1380
		$group->setHelp('Select options for this queue');
1381

    
1382
		$section->add($group);
1383

    
1384
		$section->addInput(new Form_Input(
1385
			'description',
1386
			'Description',
1387
			'text',
1388
			$this->GetDescription()
1389
		));
1390

    
1391
		$section->addInput(new Form_Input(
1392
			'interface',
1393
			null,
1394
			'hidden',
1395
			$this->GetInterface()
1396
		));
1397

    
1398
		$sform->add($section);
1399

    
1400
		return($sform);
1401
	}
1402

    
1403
	function build_shortform() {
1404
		/* XXX: Hacks in sight. Mostly layer violations!  */
1405
		global $g, $altq_list_queues;
1406
		global $shaperIFlist;
1407

    
1408
		$altq =& $altq_list_queues[$this->GetInterface()];
1409

    
1410
		if ($altq) {
1411
			$scheduler = $altq->GetScheduler();
1412
		}
1413

    
1414
		$form = '<dl class="dl-horizontal">';
1415
		$form .= '	<dt>';
1416
		$form .= '		<a href="firewall_shaper.php?interface=' . $this->GetInterface() . '&amp;queue=' . $this->GetQname() . '&amp;action=show">' . $shaperIFlist[$this->GetInterface()] . '</a>';
1417
		$form .= '	</dt>';
1418
		$form .= '	<dd>';
1419
		$form .=		$scheduler;
1420
		$form .= '	</dd>';
1421

    
1422
		$form .= '	<dt>';
1423
		$form .=		'Bandwidth';
1424
		$form .= '	</dt>';
1425
		$form .= '	<dd>';
1426
		$form .=		$this->GetBandwidth() . '&nbsp;' . $this->GetBwscale();
1427
		$form .= '	</dd>';
1428

    
1429
		$tmpvalue = $this->GetQpriority();
1430
		if (!empty($tmpvalue)) {
1431
			$form .= '	<dt>';
1432
			$form .=		'Priority';
1433
			$form .= '	<dt>';
1434
			$form .= '	<dd>';
1435
			$form .=		'On';
1436
			$form .= '	</dd>';
1437
		}
1438

    
1439
		$tmpvalue = $this->GetDefault();
1440
		if (!empty($tmpvalue)) {
1441
			$form .= '	<dt>';
1442
			$form .=		'Default';
1443
			$form .= '	<dt>';
1444
			$form .= '	<dd>';
1445
			$form .=		'On';
1446
			$form .= '	</dd>';
1447
		}
1448

    
1449
			$form .= '	<dt>';
1450
			$form .= 'Delete';
1451
			$form .= '	<dt>';
1452
			$form .= '	<dd>';
1453

    
1454
			$form .= '<a class="btn btn-danger btn-xs" href="firewall_shaper_queues.php?interface=';
1455
			$form .= $this->GetInterface() . '&amp;queue=';
1456
			$form .= $this->GetQname() . '&amp;action=delete">';
1457
			$form .= gettext("Delete queue from interface") . '</a>';
1458

    
1459
			$form .= '	</dd>';
1460

    
1461
			$form .= '</dl>';
1462

    
1463
		return $form;
1464

    
1465
	}
1466

    
1467
	function update_altq_queue_data(&$q) {
1468
		$this->ReadConfig($q);
1469
	}
1470

    
1471
	function wconfig() {
1472
		$cflink =& get_reference_to_me_in_config($this->GetLink());
1473
		if (!is_array($cflink)) {
1474
			$cflink = array();
1475
		}
1476
		$cflink['name'] = $this->GetQname();
1477
		$cflink['interface'] = $this->GetInterface();
1478
		$cflink['qlimit'] = trim($this->GetQlimit());
1479
		if (empty($cflink['qlimit'])) {
1480
			unset($cflink['qlimit']);
1481
		}
1482
		$cflink['priority'] = trim($this->GetQpriority());
1483
		if (empty($cflink['priority'])) {
1484
			unset($cflink['priority']);
1485
		}
1486
		$cflink['description'] = trim($this->GetDescription());
1487
		if (empty($cflink['description'])) {
1488
			unset($cflink['description']);
1489
		}
1490
		$cflink['enabled'] = trim($this->GetEnabled());
1491
		if (empty($cflink['enabled'])) {
1492
			unset($cflink['enabled']);
1493
		}
1494
		$cflink['default'] = trim($this->GetDefault());
1495
		if (empty($cflink['default'])) {
1496
			unset($cflink['default']);
1497
		}
1498
		$cflink['red'] = trim($this->GetRed());
1499
		if (empty($cflink['red'])) {
1500
			unset($cflink['red']);
1501
		}
1502
		$cflink['codel'] = trim($this->GetCodel());
1503
		if (empty($cflink['codel'])) {
1504
			unset($cflink['codel']);
1505
		}
1506
		$cflink['rio'] = trim($this->GetRio());
1507
		if (empty($cflink['rio'])) {
1508
			unset($cflink['rio']);
1509
		}
1510
		$cflink['ecn'] = trim($this->GetEcn());
1511
		if (empty($cflink['ecn'])) {
1512
			unset($cflink['ecn']);
1513
		}
1514
	}
1515
}
1516

    
1517
class hfsc_queue extends priq_queue {
1518
	/* realtime */
1519
	var $realtime;
1520
	var $r_m1;
1521
	var $r_d;
1522
	var $r_m2;
1523
	/* linkshare */
1524
	var $linkshare;
1525
	var $l_m1;
1526
	var $l_d;
1527
	var $l_m2;
1528
	/* upperlimit */
1529
	var $upperlimit;
1530
	var $u_m1;
1531
	var $u_d;
1532
	var $u_m2;
1533

    
1534
	/*
1535
	 * HFSC can have nested queues.
1536
	 */
1537
	function CanHaveChildren() {
1538
		return true;
1539
	}
1540
	function GetRealtime() {
1541
		return $this->realtime;
1542
	}
1543
	function GetR_m1() {
1544
		return $this->r_m1;
1545
	}
1546
	function GetR_d() {
1547
		return $this->r_d;
1548
	}
1549
	function GetR_m2() {
1550
		return $this->r_m2;
1551
	}
1552
	function SetRealtime() {
1553
		$this->realtime = "on";
1554
	}
1555
	function DisableRealtime() {
1556
		$this->realtime = "";
1557
	}
1558
	function SetR_m1($value) {
1559
		$this->r_m1 = $value;
1560
	}
1561
	function SetR_d($value) {
1562
		$this->r_d = $value;
1563
	}
1564
	function SetR_m2($value) {
1565
		$this->r_m2 = $value;
1566
	}
1567
	function GetLinkshare() {
1568
		return $this->linkshare;
1569
	}
1570
	function DisableLinkshare() {
1571
		$this->linkshare = "";
1572
	}
1573
	function GetL_m1() {
1574
		return $this->l_m1;
1575
	}
1576
	function GetL_d() {
1577
		return $this->l_d;
1578
	}
1579
	function GetL_m2() {
1580
		return $this->l_m2;
1581
	}
1582
	function SetLinkshare() {
1583
		$this->linkshare = "on";
1584
	}
1585
	function SetL_m1($value) {
1586
		$this->l_m1 = $value;
1587
	}
1588
	function SetL_d($value) {
1589
		$this->l_d = $value;
1590
	}
1591
	function SetL_m2($value) {
1592
		$this->l_m2 = $value;
1593
	}
1594
	function GetUpperlimit() {
1595
		return $this->upperlimit;
1596
	}
1597
	function GetU_m1() {
1598
		return $this->u_m1;
1599
	}
1600
	function GetU_d() {
1601
		return $this->u_d;
1602
	}
1603
	function GetU_m2() {
1604
		return $this->u_m2;
1605
	}
1606
	function SetUpperlimit() {
1607
		$this->upperlimit = "on";
1608
	}
1609
	function DisableUpperlimit() {
1610
		$this->upperlimit = "";
1611
	}
1612
	function SetU_m1($value) {
1613
		$this->u_m1 = $value;
1614
	}
1615
	function SetU_d($value) {
1616
		$this->u_d = $value;
1617
	}
1618
	function SetU_m2($value) {
1619
		$this->u_m2 = $value;
1620
	}
1621

    
1622
	function &add_queue($interface, &$qname, &$path, &$input_errors) {
1623

    
1624
		if (!is_array($this->subqueues)) {
1625
			$this->subqueues = array();
1626
		}
1627
		$q =& new hfsc_queue();
1628
		$q->SetInterface($this->GetInterface());
1629
		$q->SetParent($this);
1630
		$q->ReadConfig($qname);
1631
		$q->validate_input($qname, $input_errors);
1632
		if (count($input_errors)) {
1633
			log_error("SHAPER: could not create queue " . $q->GetQname() . " on interface {$interface} because: " . print_r($input_errors, true));
1634
			return $q;
1635
		}
1636

    
1637
		$q->SetEnabled("on");
1638
		$q->SetLink($path);
1639
		switch ($q->GetBwscale()) {
1640
			case "%":
1641
				$myBw = $this->GetAvailableBandwidth() * $qname['bandwidth'] / 100;
1642
				break;
1643
			default:
1644
				$myBw = $qname['bandwidth'] * get_bandwidthtype_scale($q->GetBwscale());
1645
				break;
1646
		}
1647
		$q->SetAvailableBandwidth($myBw);
1648
		$this->SetAvailableBandwidth($this->GetAvailableBandwidth() - $myBw);
1649

    
1650
		$this->subqueues[$q->GetQname()] =& $q; //new hfsc_queue()
1651
		ref_on_altq_queue_list($this->GetQname(), $q->GetQname());
1652
		if (is_array($qname['queue'])) {
1653
			foreach ($qname['queue'] as $key1 => $que) {
1654
				array_push($path, $key1);
1655
				$q->add_queue($q->GetInterface(), $que, $path, $input_errors);
1656
				array_pop($path);
1657
			}
1658
		}
1659

    
1660
		return $q;
1661
	}
1662

    
1663
	function copy_queue($interface, &$cflink) {
1664

    
1665
		$cflink['name'] = $this->GetQname();
1666
		$cflink['interface'] = $interface;
1667
		$cflink['qlimit'] = trim($this->GetQlimit());
1668
		if (empty($cflink['qlimit'])) {
1669
			unset($cflink['qlimit']);
1670
		}
1671
		$cflink['priority'] = trim($this->GetQpriority());
1672
		if (empty($cflink['priority'])) {
1673
			unset($cflink['priority']);
1674
		}
1675
		$cflink['description'] = trim($this->GetDescription());
1676
		if (empty($cflink['description'])) {
1677
			unset($cflink['description']);
1678
		}
1679
		$cflink['bandwidth'] = $this->GetBandwidth();
1680
		$cflink['bandwidthtype'] = $this->GetBwscale();
1681
		$cflink['enabled'] = trim($this->GetEnabled());
1682
		if (empty($cflink['enabled'])) {
1683
			unset($cflink['enabled']);
1684
		}
1685
		$cflink['default'] = trim($this->GetDefault());
1686
		if (empty($cflink['default'])) {
1687
			unset($cflink['default']);
1688
		}
1689
		$cflink['red'] = trim($this->GetRed());
1690
		if (empty($cflink['red'])) {
1691
			unset($cflink['red']);
1692
		}
1693
		$cflink['rio'] = trim($this->GetRio());
1694
		if (empty($cflink['rio'])) {
1695
			unset($cflink['rio']);
1696
		}
1697
		$cflink['ecn'] = trim($this->GetEcn());
1698
		if (empty($cflink['ecn'])) {
1699
			unset($cflink['ecn']);
1700
		}
1701
		if ($this->GetLinkshare() <> "") {
1702
			if ($this->GetL_m1() <> "") {
1703
				$cflink['linkshare1'] = $this->GetL_m1();
1704
				$cflink['linkshare2'] = $this->GetL_d();
1705
				$cflink['linkshare'] = "on";
1706
			} else {
1707
				unset($cflink['linkshare1']);
1708
				unset($cflink['linkshare2']);
1709
				unset($cflink['linkshare']);
1710
			}
1711
			if ($this->GetL_m2() <> "") {
1712
				$cflink['linkshare3'] = $this->GetL_m2();
1713
				$cflink['linkshare'] = "on";
1714
			} else {
1715
				unset($cflink['linkshare3']);
1716
				unset($cflink['linkshare']);
1717
			}
1718
		}
1719
		if ($this->GetRealtime() <> "") {
1720
			if ($this->GetR_m1() <> "") {
1721
				$cflink['realtime1'] = $this->GetR_m1();
1722
				$cflink['realtime2'] = $this->GetR_d();
1723
				$cflink['realtime'] = "on";
1724
			} else {
1725
				unset($cflink['realtime1']);
1726
				unset($cflink['realtime2']);
1727
				unset($cflink['realtime']);
1728
			}
1729
			if ($this->GetR_m2() <> "") {
1730
				$cflink['realtime3'] = $this->GetR_m2();
1731
				$cflink['realtime'] = "on";
1732
			} else {
1733
				unset($cflink['realtime3']);
1734
				unset($cflink['realtime']);
1735
			}
1736
		}
1737
		if ($this->GetUpperlimit() <> "") {
1738
			if ($this->GetU_m1() <> "") {
1739
				$cflink['upperlimit1'] = $this->GetU_m1();
1740
				$cflink['upperlimit2'] = $this->GetU_d();
1741
				$cflink['upperlimit'] = "on";
1742
			} else {
1743
				unset($cflink['upperlimit']);
1744
				unset($cflink['upperlimit1']);
1745
				unset($cflink['upperlimit2']);
1746
			}
1747
			if ($this->GetU_m2() <> "") {
1748
				$cflink['upperlimit3'] = $this->GetU_m2();
1749
				$cflink['upperlimit'] = "on";
1750
			} else {
1751
				unset($cflink['upperlimit3']);
1752
				unset($cflink['upperlimit']);
1753
			}
1754
		}
1755

    
1756
		if (is_array($this->subqueues)) {
1757
			$cflinkp['queue'] = array();
1758
			foreach ($this->subqueues as $q) {
1759
				$cflink['queue'][$q->GetQname()] = array();
1760
				$q->copy_queue($interface, $cflink['queue'][$q->GetQname()]);
1761
			}
1762
		}
1763
	}
1764

    
1765
	function delete_queue() {
1766
		unref_on_altq_queue_list($this->GetQname());
1767
		cleanup_queue_from_rules($this->GetQname());
1768
		$parent =& $this->GetParent();
1769
		foreach ($this->subqueues as $q) {
1770
			$this->SetAvailableBandwidth($this->GetAvailableBandwidth() + $q->GetAvailableBandwidth());
1771
				$q->delete_queue();
1772
		}
1773
		unset_object_by_reference($this->GetLink());
1774
	}
1775

    
1776
	/*
1777
	 * Should search even its children
1778
	 */
1779
	function &find_queue($interface, $qname) {
1780
		if ($qname == $this->GetQname()) {
1781
			return $this;
1782
		}
1783

    
1784
		foreach ($this->subqueues as $q) {
1785
			$result =& $q->find_queue("", $qname);
1786
			if ($result) {
1787
				return $result;
1788
			}
1789
		}
1790
	}
1791

    
1792
	function &find_parentqueue($interface, $qname) {
1793
		if ($this->subqueues[$qname]) {
1794
			return $this;
1795
		}
1796
		foreach ($this->subqueues as $q) {
1797
			$result = $q->find_parentqueue("", $qname);
1798
			if ($result) {
1799
				return $result;
1800
			}
1801
		}
1802
	}
1803

    
1804
	function validate_input($data, &$input_errors) {
1805
		parent::validate_input($data, $input_errors);
1806

    
1807
		$reqdfields[] = "bandwidth";
1808
		$reqdfieldsn[] = gettext("Bandwidth");
1809
		$reqdfields[] = "bandwidthtype";
1810
		$reqdfieldsn[] = gettext("Bandwidthtype");
1811

    
1812
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
1813

    
1814
		if (isset($data['linkshare3']) && $data['linkshare3'] <> "") {
1815
			if ($data['bandwidth'] && (!is_numeric($data['bandwidth']))) {
1816
				$input_errors[] = gettext("Bandwidth must be an integer.");
1817
			}
1818

    
1819
			if ($data['bandwidth'] < 0) {
1820
				$input_errors[] = gettext("Bandwidth cannot be negative.");
1821
			}
1822

    
1823
			if ($data['bandwidthtype'] == "%") {
1824
				if ($data['bandwidth'] > 100 || $data['bandwidth'] < 0) {
1825
					$input_errors[] = gettext("Bandwidth in percentage should be between 1 and 100 bounds.");
1826
				}
1827
			}
1828
		/*
1829
			$parent =& $this->GetParent();
1830
			switch ($data['bandwidthtype']) {
1831
			case "%":
1832
				$myBw = $parent->GetAvailableBandwidth() * floatval($data['bandwidth']) / 100;
1833
			default:
1834
				$mybw = floatval($data['bandwidth']) * get_bandwidthtype_scale($data['bandwidthtype']);
1835
				break;
1836
			}
1837
			if ($parent->GetAvailableBandwidth() < $myBw) {
1838
				$input_errors[] = "The sum of children bandwidth exceeds that of the parent.";
1839
			}
1840
		*/
1841
		}
1842

    
1843
		if ($data['upperlimit1'] <> "" && $data['upperlimit2'] == "") {
1844
			$input_errors[] = gettext("upperlimit service curve defined but missing (d) value");
1845
		}
1846
		if ($data['upperlimit2'] <> "" && $data['upperlimit1'] == "") {
1847
			$input_errors[] = gettext("upperlimit service curve defined but missing initial bandwidth (m1) value");
1848
		}
1849
		if ($data['upperlimit1'] <> "" && !is_valid_shaperbw($data['upperlimit1'])) {
1850
			$input_errors[] = gettext("upperlimit m1 value needs to be Kb, Mb, Gb, or %");
1851
		}
1852
		if ($data['upperlimit2'] <> "" && !is_numeric($data['upperlimit2'])) {
1853
			$input_errors[] = gettext("upperlimit d value needs to be numeric");
1854
		}
1855
		if ($data['upperlimit3'] <> "" && !is_valid_shaperbw($data['upperlimit3'])) {
1856
			$input_errors[] = gettext("upperlimit m2 value needs to be Kb, Mb, Gb, or %");
1857
		}
1858

    
1859
		/*
1860
		if (isset($data['upperlimit']) && $data['upperlimit3'] <> "" && $data['upperlimit1'] <> "") {
1861
			$bw_1 = get_hfsc_bandwidth($this, $data['upperlimit1']);
1862
			$bw_2 = get_hfsc_bandwidth($this, $data['upperlimit3']);
1863
			if (floatval($bw_1) < floatval($bw_2)) {
1864
				$input_errors[] = ("upperlimit m1 cannot be smaller than m2");
1865
			}
1866

    
1867
			if (get_interface_bandwidth($this) < (0.8 * (floatval($bw_1) + floatval($bw_2)))) {
1868
				$input_errors[] = ("upperlimit specification exceeds 80% of allowable allocation.");
1869
			}
1870
		}
1871
		*/
1872
		if ($data['linkshare1'] <> "" && $data['linkshare2'] == "") {
1873
			$input_errors[] = gettext("linkshare service curve defined but missing (d) value");
1874
		}
1875
		if ($data['linkshare2'] <> "" && $data['linkshare1'] == "") {
1876
			$input_errors[] = gettext("linkshare service curve defined but missing initial bandwidth (m1) value");
1877
		}
1878
		if ($data['linkshare1'] <> "" && !is_valid_shaperbw($data['linkshare1'])) {
1879
			$input_errors[] = gettext("linkshare m1 value needs to be Kb, Mb, Gb, or %");
1880
		}
1881
		if ($data['linkshare2'] <> "" && !is_numeric($data['linkshare2'])) {
1882
			$input_errors[] = gettext("linkshare d value needs to be numeric");
1883
		}
1884
		if ($data['linkshare3'] <> "" && !is_valid_shaperbw($data['linkshare3'])) {
1885
			$input_errors[] = gettext("linkshare m2 value needs to be Kb, Mb, Gb, or %");
1886
		}
1887
		if ($data['realtime1'] <> "" && $data['realtime2'] == "") {
1888
			$input_errors[] = gettext("realtime service curve defined but missing (d) value");
1889
		}
1890
		if ($data['realtime2'] <> "" && $data['realtime1'] == "") {
1891
			$input_errors[] = gettext("realtime service curve defined but missing initial bandwidth (m1) value");
1892
		}
1893

    
1894
		/*
1895
		if (isset($data['linkshare']) && $data['linkshare3'] <> "" && $data['linkshare1'] <> "" && 0) {
1896
			$bw_1 = get_hfsc_bandwidth($this, $data['linkshare1']);
1897
			$bw_2 = get_hfsc_bandwidth($this, $data['linkshare3']);
1898
			if (floatval($bw_1) < floatval($bw_2)) {
1899
				$input_errors[] = ("linkshare m1 cannot be smaller than m2");
1900
			}
1901

    
1902
			if (get_interface_bandwidth($this) < (0.8 * (floatval($bw_1) + floatval($bw_2)))) {
1903
				$input_errors[] = ("linkshare specification exceeds 80% of allowable allocation.");
1904
			}
1905
		}
1906
		*/
1907

    
1908
		if ($data['realtime1'] <> "" && !is_valid_shaperbw($data['realtime1'])) {
1909
			$input_errors[] = gettext("realtime m1 value needs to be Kb, Mb, Gb, or %");
1910
		}
1911
		if ($data['realtime2'] <> "" && !is_numeric($data['realtime2'])) {
1912
			$input_errors[] = gettext("realtime d value needs to be numeric");
1913
		}
1914
		if ($data['realtime3'] <> "" && !is_valid_shaperbw($data['realtime3'])) {
1915
			$input_errors[] = gettext("realtime m2 value needs to be Kb, Mb, Gb, or %");
1916
		}
1917

    
1918
		/*
1919
		if (isset($data['realtime']) && $data['realtime3'] <> "" && $data['realtime1'] <> "" && 0) {
1920
			$bw_1 = get_hfsc_bandwidth($this, $data['realtime1']);
1921
			$bw_2 = get_hfsc_bandwidth($this, $data['realtime3']);
1922
			if (floatval($bw_1) < floatval($bw_2)) {
1923
				$input_errors[] = ("realtime m1 cannot be smaller than m2");
1924
			}
1925

    
1926
			if (get_interface_bandwidth($this) < (0.8 * (floatval($bw_1) + floatval($bw_2)))) {
1927
				$input_errors[] = ("realtime specification exceeds 80% of allowable allocation.");
1928
			}
1929
		}
1930
		*/
1931
	}
1932

    
1933
	function ReadConfig(&$cflink) {
1934
		if (!empty($cflink['linkshare'])) {
1935
			if (!empty($cflink['linkshare1'])) {
1936
				$this->SetL_m1($cflink['linkshare1']);
1937
				$this->SetL_d($cflink['linkshare2']);
1938
				$this->SetLinkshare();
1939
			} else {
1940
				$this->SetL_m1("");
1941
				$this->SetL_d("");
1942
				$this->DisableLinkshare();
1943
			}
1944
			if (!empty($cflink['linkshare3'])) {
1945
				$this->SetL_m2($cflink['linkshare3']);
1946
				$this->SetLinkshare();
1947
			}
1948
		} else {
1949
			$this->DisableLinkshare();
1950
		}
1951
		if (!empty($cflink['realtime'])) {
1952
			if (!empty($cflink['realtime1'])) {
1953
				$this->SetR_m1($cflink['realtime1']);
1954
				$this->SetR_d($cflink['realtime2']);
1955
				$this->SetRealtime();
1956
			} else {
1957
				$this->SetR_m1("");
1958
				$this->SetR_d("");
1959
				$this->DisableRealtime();
1960
			}
1961
			if (!empty($cflink['realtime3'])) {
1962
				$this->SetR_m2($cflink['realtime3']);
1963
				$this->SetRealtime();
1964
			}
1965
		} else {
1966
			$this->DisableRealtime();
1967
		}
1968
		if (!empty($cflink['upperlimit'])) {
1969
			if (!empty($cflink['upperlimit1'])) {
1970
				$this->SetU_m1($cflink['upperlimit1']);
1971
				$this->SetU_d($cflink['upperlimit2']);
1972
				$this->SetUpperlimit();
1973
			} else {
1974
				$this->SetU_m1("");
1975
				$this->SetU_d("");
1976
				$this->DisableUpperlimit();
1977
			}
1978
			if (!empty($cflink['upperlimit3'])) {
1979
				$this->SetU_m2($cflink['upperlimit3']);
1980
				$this->SetUpperlimit();
1981
			}
1982
		} else {
1983
			$this->DisableUpperlimit();
1984
		}
1985
		parent::ReadConfig($cflink);
1986
	}
1987

    
1988
	function build_tree() {
1989
		$tree = " <li><a href=\"firewall_shaper.php?interface=" . $this->GetInterface() ."&amp;queue=" . $this->GetQname()."&amp;action=show";
1990
		$tree .= "\" ";
1991
		$tmpvalue = $this->GetDefault();
1992
		if (!empty($tmpvalue)) {
1993
			$tree .= " class=\"navlnk\"";
1994
		}
1995
		$tree .= " >" . $this->GetQname() . "</a>";
1996
		if (is_array($this->subqueues)) {
1997
			$tree .= "<ul>";
1998
			foreach ($this->subqueues as $q) {
1999
				$tree .= $q->build_tree();
2000
			}
2001
			$tree .= "</ul>";
2002
		}
2003
		$tree .= "</li>";
2004
		return $tree;
2005
	}
2006

    
2007
	/* Even this should take children into consideration */
2008
	function build_rules(&$default = false) {
2009

    
2010
		$pfq_rule = " queue ". $this->qname;
2011
		if ($this->GetInterface()) {
2012
			$pfq_rule .= " on ".get_real_interface($this->GetInterface());
2013
		}
2014
		if ($this->GetBandwidth() && $this->GetBwscale()) {
2015
			$pfq_rule .= " bandwidth ".trim($this->GetBandwidth()).$this->GetBwscale();
2016
		}
2017

    
2018
		$tmpvalue = $this->GetQlimit();
2019
		if (!empty($tmpvalue)) {
2020
			$pfq_rule .= " qlimit " . $this->GetQlimit();
2021
		}
2022
		if ($this->GetDefault() || $this->GetRed() || $this->GetRio() || $this->GetEcn() || $this->GetCodel() || $this->GetRealtime() <> "" || $this->GetLinkshare() <> "" || $this->GetUpperlimit() <> "") {
2023
			$pfq_rule .= " hfsc ( ";
2024
			$tmpvalue = $this->GetRed();
2025
			if (!empty($tmpvalue)) {
2026
				$comma = 1;
2027
				$pfq_rule .= " red ";
2028
			}
2029

    
2030
			$tmpvalue = $this->GetRio();
2031
			if (!empty($tmpvalue)) {
2032
				if ($comma) {
2033
					$pfq_rule .= " ,";
2034
				}
2035
				$comma = 1;
2036
				$pfq_rule .= " rio ";
2037
			}
2038
			$tmpvalue = $this->GetEcn();
2039
			if (!empty($tmpvalue)) {
2040
				if ($comma) {
2041
					$pfq_rule .= " ,";
2042
				}
2043
				$comma = 1;
2044
				$pfq_rule .= " ecn ";
2045
			}
2046
			$tmpvalue = $this->GetCodel();
2047
			if (!empty($tmpvalue)) {
2048
				if ($comma) {
2049
					$pfq_rule .= " ,";
2050
				}
2051
				$comma = 1;
2052
				$pfq_rule .= " codel ";
2053
			}
2054
			$tmpvalue = $this->GetDefault();
2055
			if (!empty($tmpvalue)) {
2056
				if ($comma) {
2057
					$pfq_rule .= " ,";
2058
				}
2059
				$comma = 1;
2060
				$pfq_rule .= " default ";
2061
				$default = true;
2062
			}
2063

    
2064
			if ($this->GetRealtime() <> "") {
2065
				if ($comma) {
2066
					$pfq_rule .= " , ";
2067
				}
2068
				if ($this->GetR_m1() <> "" && $this->GetR_d() <> "" && $this->GetR_m2() <> "") {
2069
					$pfq_rule .= " realtime (".$this->GetR_m1() . ", " . $this->GetR_d().", ". $this->GetR_m2() .") ";
2070
				} else if ($this->GetR_m2() <> "") {
2071
					$pfq_rule .= " realtime " . $this->GetR_m2();
2072
				}
2073
				$comma = 1;
2074
			}
2075
			if ($this->GetLinkshare() <> "") {
2076
				if ($comma) {
2077
					$pfq_rule .= " ,";
2078
				}
2079
				if ($this->GetL_m1() <> "" && $this->GetL_d() <> "" && $this->GetL_m2() <> "") {
2080
					$pfq_rule .= " linkshare (".$this->GetL_m1(). ", ". $this->GetL_d(). ", ". $this->GetL_m2(). ") ";
2081
				} else if ($this->GetL_m2() <> "") {
2082
					$pfq_rule .= " linkshare " . $this->GetL_m2() . " ";
2083
				}
2084
				$comma = 1;
2085
			}
2086
			if ($this->GetUpperlimit() <> "") {
2087
				if ($comma) {
2088
					$pfq_rule .= " ,";
2089
				}
2090
				if ($this->GetU_m1() <> "" && $this->GetU_d() <> "" && $this->GetU_m2() <> "") {
2091
							$pfq_rule .= " upperlimit (".$this->GetU_m1().", ". $this->GetU_d().", ". $this->GetU_m2(). ") ";
2092
				} else if ($this->GetU_m2() <> "") {
2093
					$pfq_rule .= " upperlimit " . $this->GetU_m2() . " ";
2094
				}
2095
			}
2096
			$pfq_rule .= " ) ";
2097
		}
2098
		if (count($this->subqueues)) {
2099
			$i = count($this->subqueues);
2100
			$pfq_rule .= " { ";
2101
			foreach ($this->subqueues as $qkey => $qnone) {
2102
				if ($i > 1) {
2103
					$i--;
2104
					$pfq_rule .= " {$qkey}, ";
2105
				} else {
2106
					$pfq_rule .= " {$qkey} ";
2107
				}
2108
			}
2109
			$pfq_rule .= " } \n";
2110
			foreach ($this->subqueues as $q) {
2111
				$pfq_rule .= $q->build_rules($default);
2112
			}
2113
		}
2114

    
2115
		$pfq_rule .= " \n";
2116

    
2117
		return $pfq_rule;
2118
	}
2119

    
2120
	function build_javascript() {
2121

    
2122
		$javascript = <<<EOJS
2123
<script type="text/javascript">
2124
//<![CDATA[
2125
	events.push(function(){
2126

    
2127
		// Disables the specified input element
2128
		function disableInput(id, disable) {
2129
			$('#' + id).prop("disabled", disable);
2130
		}
2131

    
2132
		// Upperlimit
2133
		function enable_upperlimit() {
2134
			disableInput('upperlimit1', !$('#upperlimit').prop('checked'));
2135
			disableInput('upperlimit2', !$('#upperlimit').prop('checked'));
2136
			disableInput('upperlimit3', !$('#upperlimit').prop('checked'));
2137
		}
2138

    
2139
		$('#upperlimit').click(function () {
2140
			enable_upperlimit();
2141
		});
2142

    
2143
		enable_upperlimit();
2144

    
2145
		// realtime
2146
		function enable_realtime() {
2147
			disableInput('realtime1', !$('#realtime').prop('checked'));
2148
			disableInput('realtime2', !$('#realtime').prop('checked'));
2149
			disableInput('realtime3', !$('#realtime').prop('checked'));
2150
		}
2151

    
2152
		$('#realtime').click(function () {
2153
			enable_realtime();
2154
		});
2155

    
2156
		enable_realtime();
2157

    
2158
		// linkshare
2159
		function enable_linkshare() {
2160
			disableInput('linkshare1', !$('#linkshare').prop('checked'));
2161
			disableInput('linkshare2', !$('#linkshare').prop('checked'));
2162
			disableInput('linkshare3', !$('#linkshare').prop('checked'));
2163
		}
2164

    
2165
		$('#linkshare').click(function () {
2166
			enable_linkshare();
2167
		});
2168

    
2169
		enable_linkshare();
2170
	});
2171
//]]>
2172
</script>
2173
EOJS;
2174

    
2175
		return $javascript;
2176
	}
2177

    
2178
	function build_form() {
2179

    
2180
		$sform = parent::build_form();
2181

    
2182
		$section = new Form_Section('Service Curve (sc)');
2183

    
2184
		$group = new Form_Group('Bandwidth');
2185

    
2186
		$group->add(new Form_Input(
2187
			'bandwidth',
2188
			null,
2189
			'number',
2190
			$this->GetBandwidth()
2191
		));
2192

    
2193
		$group->add(new Form_Select(
2194
			'bandwidthtype',
2195
			null,
2196
			$this->GetBwscale(),
2197
			array('Kb' => 'Kb',
2198
				  'Mb' => 'Mb',
2199
				  'Gb' => 'Gb',
2200
				  'b' => 'b',
2201
				  '%' => '%')
2202
		));
2203

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

    
2206
		$section->add($group);
2207

    
2208
		$group = new Form_Group('Max bandwidth for queue.');
2209

    
2210
		$group->add(new Form_Checkbox(
2211
			'upperlimit',
2212
			null,
2213
			'Upper Limit',
2214
			($this->GetUpperlimit()<> "")
2215
		));
2216

    
2217
		$group->add(new Form_Input(
2218
			'upperlimit1',
2219
			null,
2220
			'text',
2221
			$this->GetU_m1()
2222
		))->setHelp('m1');
2223

    
2224
		$group->add(new Form_Input(
2225
			'upperlimit2',
2226
			null,
2227
			'text',
2228
			$this->GetU_d()
2229
		))->setHelp('d');
2230
		
2231
		$group->add(new Form_Input(
2232
			'upperlimit3',
2233
			null,
2234
			'text',
2235
			$this->GetU_m2()
2236
		))->setHelp('m2');
2237

    
2238

    
2239
		$section->add($group);
2240

    
2241
		$group = new Form_Group('Min bandwidth for queue.');
2242

    
2243
		$group->add(new Form_Checkbox(
2244
			'realtime',
2245
			null,
2246
			'Real Time',
2247
			($this->GetRealtime()<> "")
2248
		));
2249

    
2250
		$group->add(new Form_Input(
2251
			'realtime1',
2252
			null,
2253
			'text',
2254
			$this->GetR_m1()
2255
		))->setHelp('m1');
2256

    
2257
		$group->add(new Form_Input(
2258
			'realtime2',
2259
			null,
2260
			'text',
2261
			$this->GetR_d()
2262
		))->setHelp('d');
2263
		
2264
		$group->add(new Form_Input(
2265
			'realtime3',
2266
			null,
2267
			'text',
2268
			$this->GetR_m2()
2269
		))->setHelp('m2');
2270

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

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

    
2275
		$group->add(new Form_Checkbox(
2276
			'linkshare',
2277
			null,
2278
			'Link Share',
2279
			($this->GetLinkshare()<> "")
2280
		));
2281

    
2282
		$group->add(new Form_Input(
2283
			'linkshare1',
2284
			null,
2285
			'text',
2286
			$this->GetL_m1()
2287
		))->setHelp('m1');
2288

    
2289
		$group->add(new Form_Input(
2290
			'linkshare2',
2291
			null,
2292
			'text',
2293
			$this->GetL_d()
2294
		))->setHelp('d');
2295
		
2296
		$group->add(new Form_Input(
2297
			'linkshare3',
2298
			null,
2299
			'text',
2300
			$this->GetL_m2()
2301
		))->setHelp('m2');
2302

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

    
2308
		$section->add($group);
2309

    
2310
		$sform->add($section);
2311

    
2312
		return($sform);
2313
	}
2314

    
2315
	function update_altq_queue_data(&$data) {
2316
		$this->ReadConfig($data);
2317
	}
2318

    
2319
	function wconfig() {
2320
		$cflink =& get_reference_to_me_in_config($this->GetLink());
2321
		if (!is_array($cflink)) {
2322
			$cflink = array();
2323
		}
2324
		$cflink['name'] = $this->GetQname();
2325
		$cflink['interface'] = $this->GetInterface();
2326
		$cflink['qlimit'] = trim($this->GetQlimit());
2327
		if (empty($cflink['qlimit'])) {
2328
			unset($cflink['qlimit']);
2329
		}
2330
		$cflink['priority'] = $this->GetQpriority();
2331
		if (empty($cflink['priority'])) {
2332
			unset($cflink['priority']);
2333
		}
2334
		$cflink['description'] = $this->GetDescription();
2335
		if (empty($cflink['description'])) {
2336
			unset($cflink['description']);
2337
		}
2338
		$cflink['bandwidth'] = $this->GetBandwidth();
2339
		$cflink['bandwidthtype'] = $this->GetBwscale();
2340
		$cflink['enabled'] = $this->GetEnabled();
2341
		if (empty($cflink['enabled'])) {
2342
			unset($cflink['enabled']);
2343
		}
2344
		$cflink['default'] = $this->GetDefault();
2345
		if (empty($cflink['default'])) {
2346
			unset($cflink['default']);
2347
		}
2348
		$cflink['red'] = trim($this->GetRed());
2349
		if (empty($cflink['red'])) {
2350
			unset($cflink['red']);
2351
		}
2352
		$cflink['rio'] = $this->GetRio();
2353
		if (empty($cflink['rio'])) {
2354
			unset($cflink['rio']);
2355
		}
2356
		$cflink['ecn'] = trim($this->GetEcn());
2357
		if (empty($cflink['ecn'])) {
2358
			unset($cflink['ecn']);
2359
		}
2360
		$cflink['codel'] = trim($this->GetCodel());
2361
		if (empty($cflink['codel'])) {
2362
			unset($cflink['codel']);
2363
		}
2364
		if ($this->GetLinkshare() <> "") {
2365
			if ($this->GetL_m1() <> "") {
2366
				$cflink['linkshare1'] = $this->GetL_m1();
2367
				$cflink['linkshare2'] = $this->GetL_d();
2368
				$cflink['linkshare'] = "on";
2369
			} else {
2370
				unset($cflink['linkshare']);
2371
				unset($cflink['linkshare1']);
2372
				unset($cflink['linkshare2']);
2373
			}
2374
			if ($this->GetL_m2() <> "") {
2375
				$cflink['linkshare3'] = $this->GetL_m2();
2376
				$cflink['linkshare'] = "on";
2377
			} else {
2378
				unset($cflink['linkshare']);
2379
				unset($cflink['linkshare3']);
2380
			}
2381
		} else {
2382
			unset($cflink['linkshare']);
2383
			unset($cflink['linkshare1']);
2384
			unset($cflink['linkshare2']);
2385
			unset($cflink['linkshare3']);
2386
		}
2387
		if ($this->GetRealtime() <> "") {
2388
			if ($this->GetR_m1() <> "") {
2389
				$cflink['realtime1'] = $this->GetR_m1();
2390
				$cflink['realtime2'] = $this->GetR_d();
2391
				$cflink['realtime'] = "on";
2392
			} else {
2393
				unset($cflink['realtime']);
2394
				unset($cflink['realtime1']);
2395
				unset($cflink['realtime2']);
2396
			}
2397
			if ($this->GetR_m2() <> "") {
2398
				$cflink['realtime3'] = $this->GetR_m2();
2399
				$cflink['realtime'] = "on";
2400
			} else {
2401
				unset($cflink['realtime']);
2402
				unset($cflink['realtime3']);
2403
			}
2404
		} else {
2405
			unset($cflink['realtime']);
2406
			unset($cflink['realtime1']);
2407
			unset($cflink['realtime2']);
2408
			unset($cflink['realtime3']);
2409
		}
2410
		if ($this->GetUpperlimit() <> "") {
2411
			if ($this->GetU_m1() <> "") {
2412
				$cflink['upperlimit1'] = $this->GetU_m1();
2413
				$cflink['upperlimit2'] = $this->GetU_d();
2414
				$cflink['upperlimit'] = "on";
2415
			} else {
2416
				unset($cflink['upperlimit']);
2417
				unset($cflink['upperlimit1']);
2418
				unset($cflink['upperlimit2']);
2419
			}
2420
			if ($this->GetU_m2() <> "") {
2421
				$cflink['upperlimit3'] = $this->GetU_m2();
2422
				$cflink['upperlimit'] = "on";
2423
			} else {
2424
				unset($cflink['upperlimit']);
2425
				unset($cflink['upperlimit3']);
2426
			}
2427
		} else {
2428
			unset($cflink['upperlimit']);
2429
			unset($cflink['upperlimit1']);
2430
			unset($cflink['upperlimit2']);
2431
			unset($cflink['upperlimit3']);
2432
		}
2433
	}
2434
}
2435

    
2436
class cbq_queue extends priq_queue {
2437
	var $qborrow = "";
2438

    
2439
	function GetBorrow() {
2440
		return $this->qborrow;
2441
	}
2442
	function SetBorrow($borrow) {
2443
		$this->qborrow = $borrow;
2444
	}
2445
	function CanHaveChildren() {
2446
		return true;
2447
	}
2448

    
2449
	function &add_queue($interface, &$qname, &$path, &$input_errors) {
2450

    
2451
		if (!is_array($this->subqueues)) {
2452
			$this->subqueues = array();
2453
		}
2454
		$q =& new cbq_queue();
2455
		$q->SetInterface($this->GetInterface());
2456
		$q->SetParent($this);
2457
		$q->ReadConfig($qname);
2458
		$q->validate_input($qname, $input_errors);
2459
		if (count($input_errors)) {
2460
			log_error("SHAPER: could not create queue " . $q->GetQname() . " on interface {$interface} because: " . print_r($input_errors, true));
2461
			return $q;
2462
		}
2463
		switch ($q->GetBwscale()) {
2464
			case "%":
2465
				$myBw = $this->GetAvailableBandwidth() * $qname['bandwidth'] / 100;
2466
				break;
2467
			default:
2468
				$myBw = $qname['bandwidth'] * get_bandwidthtype_scale($q->GetBwscale());
2469
				break;
2470
		}
2471
		$q->SetAvailableBandwidth($myBw);
2472
		$this->SetAvailableBandwidth($this->GetAvailableBandwidth() - $myBw);
2473

    
2474
		$q->SetEnabled("on");
2475
		$q->SetLink($path);
2476
		$this->subqueues[$q->GetQName()] = &$q;
2477
		ref_on_altq_queue_list($this->GetQname(), $q->GetQname());
2478
		if (is_array($qname['queue'])) {
2479
			foreach ($qname['queue'] as $key1 => $que) {
2480
				array_push($path, $key1);
2481
				$q->add_queue($q->GetInterface(), $que, $path, $input_errors);
2482
				array_pop($path);
2483
			}
2484
		}
2485

    
2486
		return $q;
2487
	}
2488

    
2489
	function copy_queue($interface, &$cflink) {
2490

    
2491
		$cflink['interface'] = $interface;
2492
		$cflink['qlimit'] = trim($this->GetQlimit());
2493
		if (empty($clink['qlimit'])) {
2494
			unset($cflink['qlimit']);
2495
		}
2496
		$cflink['priority'] = trim($this->GetQpriority());
2497
		if (empty($cflink['priority'])) {
2498
			unset($cflink['priority']);
2499
		}
2500
		$cflink['name'] = $this->GetQname();
2501
		$cflink['description'] = trim($this->GetDescription());
2502
		if (empty($cflink['description'])) {
2503
			unset($cflink['description']);
2504
		}
2505
		$cflink['bandwidth'] = $this->GetBandwidth();
2506
		$cflink['bandwidthtype'] = $this->GetBwscale();
2507
		$cflink['enabled'] = trim($this->GetEnabled());
2508
		if (empty($cflink['enabled'])) {
2509
			unset($cflink['enabled']);
2510
		}
2511
		$cflink['default'] = trim($this->GetDefault());
2512
		if (empty($cflink['default'])) {
2513
			unset($cflink['default']);
2514
		}
2515
		$cflink['red'] = trim($this->GetRed());
2516
		if (empty($cflink['red'])) {
2517
			unset($cflink['red']);
2518
		}
2519
		$cflink['rio'] = trim($this->GetRio());
2520
		if (empty($cflink['rio'])) {
2521
			unset($cflink['rio']);
2522
		}
2523
		$cflink['ecn'] = trim($this->GetEcn());
2524
		if (empty($cflink['ecn'])) {
2525
			unset($cflink['ecn']);
2526
		}
2527
		$cflink['borrow'] = trim($this->GetBorrow());
2528
		if (empty($cflink['borrow'])) {
2529
			unset($cflink['borrow']);
2530
		}
2531
		if (is_array($this->queues)) {
2532
			$cflinkp['queue'] = array();
2533
			foreach ($this->subqueues as $q) {
2534
				$cflink['queue'][$q->GetQname()] = array();
2535
				$q->copy_queue($interface, $cflink['queue'][$q->GetQname()]);
2536
			}
2537
		}
2538
	}
2539

    
2540
	/*
2541
	 * Should search even its children
2542
	 */
2543
	function &find_queue($interface, $qname) {
2544
		if ($qname == $this->GetQname()) {
2545
			return $this;
2546
		}
2547
		foreach ($this->subqueues as $q) {
2548
			$result =& $q->find_queue("", $qname);
2549
			if ($result) {
2550
				return $result;
2551
			}
2552
		}
2553
	}
2554

    
2555
	function &find_parentqueue($interface, $qname) {
2556
		if ($this->subqueues[$qname]) {
2557
			return $this;
2558
		}
2559
		foreach ($this->subqueues as $q) {
2560
			$result = $q->find_parentqueue("", $qname);
2561
			if ($result) {
2562
				return $result;
2563
			}
2564
		}
2565
	}
2566

    
2567
	function delete_queue() {
2568
		unref_on_altq_queue_list($this->GetQname());
2569
		cleanup_queue_from_rules($this->GetQname());
2570
		foreach ($this->subqueues as $q) {
2571
			$this->SetAvailableBandwidth($this->GetAvailableBandwidth() + $q->GetAvailableBandwidth());
2572
				$q->delete_queue();
2573
		}
2574
		unset_object_by_reference($this->GetLink());
2575
	}
2576

    
2577
	function validate_input($data, &$input_errors) {
2578
		parent::validate_input($data, $input_errors);
2579

    
2580
		if ($data['priority'] > 7) {
2581
				$input_errors[] = gettext("Priority must be an integer between 1 and 7.");
2582
		}
2583
		$reqdfields[] = "bandwidth";
2584
		$reqdfieldsn[] = gettext("Bandwidth");
2585
		$reqdfields[] = "bandwidthtype";
2586
		$reqdfieldsn[] = gettext("Bandwidthtype");
2587

    
2588
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
2589

    
2590
		if ($data['bandwidth'] && !is_numeric($data['bandwidth'])) {
2591
			$input_errors[] = gettext("Bandwidth must be an integer.");
2592
		}
2593

    
2594

    
2595
		if ($data['bandwidth'] < 0) {
2596
			$input_errors[] = gettext("Bandwidth cannot be negative.");
2597
		}
2598

    
2599
		if ($data['bandwidthtype'] == "%") {
2600
			if ($data['bandwidth'] > 100 || $data['bandwidth'] < 0) {
2601
				$input_errors[] = gettext("Bandwidth in percentage should be between 1 and 100 bounds.");
2602
			}
2603
		}
2604

    
2605
/*
2606
		$parent =& $this->GetParent();
2607
		switch ($data['bandwidthtype']) {
2608
		case "%":
2609
			$myBw = $parent->GetAvailableBandwidth() * floatval($data['bandwidth']) / 100;
2610
			break;
2611
		default:
2612
			$mybw = floatval($data['bandwidth']) * get_bandwidthtype_scale($data['bandwidthtype']);
2613
			break;
2614
		}
2615
		if ($parent->GetAvailableBandwidth() < floatval($myBw)) {
2616
			$input_errors[] = "The sum of the children bandwidth exceeds that of the parent.";
2617
		}
2618
 */
2619
	}
2620

    
2621
	function ReadConfig(&$q) {
2622
		parent::ReadConfig($q);
2623
		if (!empty($q['borrow'])) {
2624
			$this->SetBorrow("on");
2625
		} else {
2626
			$this->SetBorrow("");
2627
		}
2628
	}
2629

    
2630
	function build_javascript() {
2631
		return parent::build_javascript();
2632
	}
2633

    
2634
	function build_tree() {
2635
		$tree = " <li><a href=\"firewall_shaper.php?interface=" . $this->GetInterface()."&amp;queue=" . $this->GetQname()."&amp;action=show";
2636
		$tree .= "\" ";
2637
		$tmpvalue = trim($this->GetDefault());
2638
		if (!empty($tmpvalue)) {
2639
			$tree .= " class=\"navlnk\"";
2640
		}
2641
		$tree .= " >" . $this->GetQname() . "</a>";
2642
		if (is_array($this->subqueues)) {
2643
			$tree .= "<ul>";
2644
			foreach ($this->subqueues as $q) {
2645
				$tree .= $q->build_tree();
2646
			}
2647
			$tree .= "</ul>";
2648
		}
2649
		$tree .= "</li>";
2650
		return $tree;
2651
	}
2652

    
2653
	/* Even this should take children into consideration */
2654
	function build_rules(&$default = false) {
2655
		$pfq_rule = "queue ". $this->qname;
2656
		if ($this->GetInterface()) {
2657
			$pfq_rule .= " on ".get_real_interface($this->GetInterface());
2658
		}
2659
		if ($this->GetBandwidth() && $this->GetBwscale()) {
2660
			$pfq_rule .= " bandwidth ".trim($this->GetBandwidth()).$this->GetBwscale();
2661
		}
2662
		$tmpvalue = $this->GetQpriority();
2663
		if (!empty($tmpvalue)) {
2664
			$pfq_rule .= " priority " . $this->GetQpriority();
2665
		}
2666
		$tmpvalue = trim($this->GetQlimit());
2667
		if (!empty($tmpvalue)) {
2668
			$pfq_rule .= " qlimit " . $this->GetQlimit();
2669
		}
2670
		if ($this->GetDefault() || $this->GetRed() || $this->GetRio() || $this->GetEcn() || $this->GetBorrow() || $this->GetCodel()) {
2671
			$pfq_rule .= " cbq ( ";
2672
			$tmpvalue = trim($this->GetRed());
2673
			if (!empty($tmpvalue)) {
2674
				$comma = 1;
2675
				$pfq_rule .= " red ";
2676
			}
2677
			$tmpvalue = trim($this->GetCodel());
2678
			if (!empty($tmpvalue)) {
2679
				$comma = 1;
2680
				$pfq_rule .= " codel ";
2681
			}
2682
			$tmpvalue = trim($this->GetRio());
2683
			if (!empty($tmpvalue)) {
2684
				if ($comma) {
2685
					$pfq_rule .= " ,";
2686
				}
2687
				$comma = 1;
2688
				$pfq_rule .= " rio ";
2689
			}
2690
			$tmpvalue = trim($this->GetEcn());
2691
			if (!empty($tmpvalue)) {
2692
				if ($comma) {
2693
					$pfq_rule .= " ,";
2694
				}
2695
				$comma = 1;
2696
				$pfq_rule .= " ecn ";
2697
			}
2698
			$tmpvalue = trim($this->GetDefault());
2699
			if (!empty($tmpvalue)) {
2700
				if ($comma) {
2701
					$pfq_rule .= " ,";
2702
				}
2703
				$comma = 1;
2704
				$pfq_rule .= " default ";
2705
				$default = true;
2706
			}
2707
			$tmpvalue = trim($this->GetBorrow());
2708
			if (!empty($tmpvalue)) {
2709
				if ($comma) {
2710
					$pfq_rule .= ", ";
2711
				}
2712
				$pfq_rule .= " borrow ";
2713
			}
2714
			$pfq_rule .= " ) ";
2715
		}
2716
		if (count($this->subqueues)) {
2717
			$i = count($this->subqueues);
2718
			$pfq_rule .= " { ";
2719
			foreach ($this->subqueues as $qkey => $qnone) {
2720
				if ($i > 1) {
2721
					$i--;
2722
					$pfq_rule .= " {$qkey}, ";
2723
				} else {
2724
					$pfq_rule .= " {$qkey} ";
2725
				}
2726
			}
2727
			$pfq_rule .= " } \n";
2728
			foreach ($this->subqueues as $q) {
2729
				$pfq_rule .= $q->build_rules($default);
2730
			}
2731
		}
2732

    
2733
		$pfq_rule .= " \n";
2734
		return $pfq_rule;
2735
	}
2736

    
2737
	function build_form() {
2738
		$sform = parent::build_form();
2739

    
2740
		$section = new Form_Section('');
2741

    
2742
		$group = new Form_Group('Bandwidth');
2743

    
2744
		$group->add(new Form_Input(
2745
			'bandwidth',
2746
			null,
2747
			'number',
2748
			$this->GetBandwidth()
2749
		));
2750

    
2751
		$group->add(new Form_Select(
2752
			'bandwidthtype',
2753
			null,
2754
			$this->GetBwscale(),
2755
			array('Kb' => 'Kb',
2756
				  'Mb' => 'Mb',
2757
				  'Gb' => 'Gb',
2758
				  'b' => 'b',
2759
				  '%' => '%')
2760
		));
2761

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

    
2764
		$section->add($group);
2765

    
2766
		$section->addInput(new Form_Checkbox(
2767
			'borrow',
2768
			'Scheduler option',
2769
			'Borrow from other queues when available',
2770
			($this->GetBorrow() == "on")
2771
		));
2772

    
2773
		return $sform;
2774
	}
2775

    
2776
	function update_altq_queue_data(&$data) {
2777
		$this->ReadConfig($data);
2778
	}
2779

    
2780
	function wconfig() {
2781
		$cflink =& get_reference_to_me_in_config($this->GetLink());
2782
		if (!is_array($cflink)) {
2783
			$cflink = array();
2784
		}
2785
		$cflink['interface'] = $this->GetInterface();
2786
		$cflink['qlimit'] = trim($this->GetQlimit());
2787
		if (empty($cflink['qlimit'])) {
2788
			unset($cflink['qlimit']);
2789
		}
2790
		$cflink['priority'] = $this->GetQpriority();
2791
		if (empty($cflink['priority'])) {
2792
			unset($cflink['priority']);
2793
		}
2794
		$cflink['name'] = $this->GetQname();
2795
		$cflink['description'] = $this->GetDescription();
2796
		if (empty($cflink['description'])) {
2797
			unset($cflink['description']);
2798
		}
2799
		$cflink['bandwidth'] = $this->GetBandwidth();
2800
		$cflink['bandwidthtype'] = $this->GetBwscale();
2801
		$cflink['enabled'] = trim($this->GetEnabled());
2802
		if (empty($cflink['enabled'])) {
2803
			unset($cflink['enabled']);
2804
		}
2805
		$cflink['default'] = trim($this->GetDefault());
2806
		if (empty($cflink['default'])) {
2807
			unset($cflink['default']);
2808
		}
2809
		$cflink['red'] = trim($this->GetRed());
2810
		if (empty($cflink['red'])) {
2811
			unset($cflink['red']);
2812
		}
2813
		$cflink['rio'] = trim($this->GetRio());
2814
		if (empty($cflink['rio'])) {
2815
			unset($cflink['rio']);
2816
		}
2817
		$cflink['ecn'] = trim($this->GetEcn());
2818
		if (empty($cflink['ecn'])) {
2819
			unset($cflink['ecn']);
2820
		}
2821
		$cflink['codel'] = trim($this->GetCodel());
2822
		if (empty($cflink['codel'])) {
2823
			unset($cflink['codel']);
2824
		}
2825
		$cflink['borrow'] = trim($this->GetBorrow());
2826
		if (empty($cflink['borrow'])) {
2827
			unset($cflink['borrow']);
2828
		}
2829
	}
2830
}
2831

    
2832
class fairq_queue extends priq_queue {
2833
	var $hogs;
2834
	var $buckets;
2835

    
2836
	function GetBuckets() {
2837
		return $this->buckets;
2838
	}
2839
	function SetBuckets($buckets) {
2840
		$this->buckets = $buckets;
2841
	}
2842
	function GetHogs() {
2843
		return $this->hogs;
2844
	}
2845
	function SetHogs($hogs) {
2846
		$this->hogs = $hogs;
2847
	}
2848
	function CanHaveChildren() {
2849
		return false;
2850
	}
2851

    
2852

    
2853
	function copy_queue($interface, &$cflink) {
2854
		$cflink['interface'] = $interface;
2855
		$cflink['qlimit'] = $this->GetQlimit();
2856
		$cflink['priority'] = $this->GetQpriority();
2857
		$cflink['name'] = $this->GetQname();
2858
		$cflink['description'] = $this->GetDescription();
2859
		$cflink['bandwidth'] = $this->GetBandwidth();
2860
		$cflink['bandwidthtype'] = $this->GetBwscale();
2861
		$cflink['enabled'] = $this->GetEnabled();
2862
		$cflink['default'] = $this->GetDefault();
2863
		$cflink['red'] = $this->GetRed();
2864
		$cflink['rio'] = $this->GetRio();
2865
		$cflink['ecn'] = $this->GetEcn();
2866
		$cflink['buckets'] = $this->GetBuckets();
2867
		$cflink['hogs'] = $this->GetHogs();
2868
	}
2869

    
2870
	/*
2871
	 * Should search even its children
2872
	 */
2873
	function &find_queue($interface, $qname) {
2874
		if ($qname == $this->GetQname()) {
2875
			return $this;
2876
		}
2877
	}
2878

    
2879
	function find_parentqueue($interface, $qname) { return; }
2880

    
2881
	function delete_queue() {
2882
		unref_on_altq_queue_list($this->GetQname());
2883
		cleanup_queue_from_rules($this->GetQname());
2884
		unset_object_by_reference($this->GetLink());
2885
	}
2886

    
2887
	function validate_input($data, &$input_errors) {
2888
		parent::validate_input($data, $input_errors);
2889

    
2890
		if ($data['priority'] > 255) {
2891
				$input_errors[] = gettext("Priority must be an integer between 1 and 255.");
2892
		}
2893
		$reqdfields[] = "bandwidth";
2894
		$reqdfieldsn[] = gettext("Bandwidth");
2895
		$reqdfields[] = "bandwidthtype";
2896
		$reqdfieldsn[] = gettext("Bandwidthtype");
2897

    
2898
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
2899

    
2900
		if ($data['bandwidth'] && !is_numeric($data['bandwidth'])) {
2901
			$input_errors[] = gettext("Bandwidth must be an integer.");
2902
		}
2903

    
2904

    
2905
		if ($data['bandwidth'] < 0) {
2906
			$input_errors[] = gettext("Bandwidth cannot be negative.");
2907
		}
2908

    
2909

    
2910
		if ($data['bandwidthtype'] == "%") {
2911
			if ($data['bandwidth'] > 100 || $data['bandwidth'] < 0) {
2912
				$input_errors[] = gettext("Bandwidth in percentage should be between 1 and 100 bounds.");
2913
			}
2914
		}
2915

    
2916
/*
2917
		$parent =& $this->GetParent();
2918
		switch ($data['bandwidthtype']) {
2919
		case "%":
2920
			$myBw = $parent->GetAvailableBandwidth() * floatval($data['bandwidth']) / 100;
2921
		default:
2922
			$mybw = floatval($data['bandwidth']) * get_bandwidthtype_scale($data['bandwidthtype']);
2923
			break;
2924
		}
2925
		if ($parent->GetAvailableBandwidth() < floatval($myBw)) {
2926
			$input_errors[] = "The sum of children bandwidth exceeds that of the parent.";
2927
		}
2928
*/
2929
	}
2930

    
2931
	function ReadConfig(&$q) {
2932
		parent::ReadConfig($q);
2933
		if (!empty($q['buckets'])) {
2934
			$this->SetBuckets($q['buckets']);
2935
		} else {
2936
			$this->SetBuckets("");
2937
		}
2938
		if (!empty($q['hogs']) && is_valid_shaperbw($q['hogs'])) {
2939
			$this->SetHogs($q['hogs']);
2940
		} else {
2941
			$this->SetHogs("");
2942
		}
2943
	}
2944

    
2945
	function build_javascript() {
2946
		return parent::build_javascript();
2947
	}
2948

    
2949
	function build_tree() {
2950
		$tree = " <li><a href=\"firewall_shaper.php?interface=" .
2951
		$this->GetInterface()."&amp;queue=" . $this->GetQname()."&amp;action=show";
2952
		$tree .= "\" ";
2953
		$tmpvalue = trim($this->GetDefault());
2954
		if (!empty($tmpvalue)) {
2955
			$tree .= " class=\"navlnk\"";
2956
		}
2957
		$tree .= " >" . $this->GetQname() . "</a>";
2958
		$tree .= "</li>";
2959
		return $tree;
2960
	}
2961

    
2962
	/* Even this should take children into consideration */
2963
	function build_rules(&$default = false) {
2964
		$pfq_rule = "queue ". $this->qname;
2965
		if ($this->GetInterface()) {
2966
			$pfq_rule .= " on ".get_real_interface($this->GetInterface());
2967
		}
2968
		if ($this->GetBandwidth() && $this->GetBwscale()) {
2969
			$pfq_rule .= " bandwidth ".trim($this->GetBandwidth()).$this->GetBwscale();
2970
		}
2971
		$tmpvalue = trim($this->GetQpriority());
2972
		if (!empty($tmpvalue)) {
2973
			$pfq_rule .= " priority " . $this->GetQpriority();
2974
		}
2975
		$tmpvalue = trim($this->GetQlimit());
2976
		if (!empty($tmpvalue)) {
2977
			$pfq_rule .= " qlimit " . $this->GetQlimit();
2978
		}
2979
		if ($this->GetDefault() || $this->GetRed() || $this->GetRio() ||
2980
		    $this->GetEcn() || $this->GetBuckets() || $this->GetHogs() || $this->GetCodel()) {
2981
			$pfq_rule .= " fairq ( ";
2982
			$tmpvalue = trim($this->GetRed());
2983
			if (!empty($tmpvalue)) {
2984
				$comma = 1;
2985
				$pfq_rule .= " red ";
2986
			}
2987
			$tmpvalue = trim($this->GetCodel());
2988
			if (!empty($tmpvalue)) {
2989
				$comma = 1;
2990
				$pfq_rule .= " codel ";
2991
			}
2992
			$tmpvalue = trim($this->GetRio());
2993
			if (!empty($tmpvalue)) {
2994
				if ($comma) {
2995
					$pfq_rule .= " ,";
2996
				}
2997
				$comma = 1;
2998
				$pfq_rule .= " rio ";
2999
			}
3000
			$tmpvalue = trim($this->GetEcn());
3001
			if (!empty($tmpvalue)) {
3002
				if ($comma) {
3003
					$pfq_rule .= " ,";
3004
				}
3005
				$comma = 1;
3006
				$pfq_rule .= " ecn ";
3007
			}
3008
			$tmpvalue = trim($this->GetDefault());
3009
			if (!empty($tmpvalue)) {
3010
				if ($comma) {
3011
					$pfq_rule .= " ,";
3012
				}
3013
				$comma = 1;
3014
				$pfq_rule .= " default ";
3015
				$default = true;
3016
			}
3017
			$tmpvalue = trim($this->GetBuckets());
3018
			if (!empty($tmpvalue)) {
3019
				if ($comma) {
3020
					$pfq_rule .= ", ";
3021
				}
3022
				$pfq_rule .= " buckets " . $this->GetBuckets() . " ";
3023
			}
3024
			$tmpvalue = trim($this->GetHogs());
3025
			if (!empty($tmpvalue)) {
3026
				if ($comma) {
3027
					$pfq_rule .= ", ";
3028
				}
3029
				$pfq_rule .= " hogs " . $this->GetHogs() . " ";
3030
			}
3031
			$pfq_rule .= " ) ";
3032
		}
3033

    
3034
		$pfq_rule .= " \n";
3035
		return $pfq_rule;
3036
	}
3037

    
3038
	function build_form() {
3039
		$form = parent::build_form();
3040

    
3041
		$section = new Form_Section('');
3042

    
3043
		$group = new Form_Group('Bandwidth');
3044

    
3045
		$group->add(new Form_Input(
3046
			'bandwidth',
3047
			null,
3048
			'number',
3049
			$this->GetBandwidth()
3050
		));
3051

    
3052
		$group->add(new Form_Select(
3053
			'bandwidthtype',
3054
			null,
3055
			$this->GetBwscale(),
3056
			array('Kb' => 'Kb',
3057
				  'Mb' => 'Mb',
3058
				  'Gb' => 'Gb',
3059
				  'b' => 'b',
3060
				  '%' => '%')
3061
		));
3062

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

    
3065
		$section->add($group);
3066

    
3067

    
3068
		$form .= "<tr><td class=\"vncellreq\">" . gettext("Scheduler specific options") . "</td>";
3069
		$form .= "<td class=\"vtable\"><table><tr><td>";
3070
		$form .= "<input id=\"buckets\" name=\"buckets\" value=\"";
3071
		$tmpvalue = trim($this->GetBuckets());
3072
		if (!empty($tmpvalue)) {
3073
			$form .= $this->GetBuckets();
3074
		}
3075
		$form .= "\" /> " . gettext("Number of buckets available.") . "<br /></td></tr>";
3076
		$form .= "<tr><td class=\"vtable\"><input id=\"hogs\" name=\"hogs\" value=\"";
3077
		$tmpvalue = trim($this->GetHogs());
3078
		if (!empty($tmpvalue)) {
3079
			$form .= $this->GetHogs();
3080
		}
3081
		$form .= "\" /> " . gettext("Bandwidth limit for hosts to not saturate link.") . "<br /></td></tr>";
3082
		$form .= "</table></td></tr>";
3083
		return $form;
3084
	}
3085

    
3086
	function update_altq_queue_data(&$data) {
3087
		$this->ReadConfig($data);
3088
	}
3089

    
3090
	function wconfig() {
3091
		$cflink =& get_reference_to_me_in_config($this->GetLink());
3092
		if (!is_array($cflink)) {
3093
			$cflink = array();
3094
		}
3095
		$cflink['interface'] = $this->GetInterface();
3096
		$cflink['qlimit'] = trim($this->GetQlimit());
3097
		if (empty($cflink['qlimit'])) {
3098
			unset($cflink['qlimit']);
3099
		}
3100
		$cflink['priority'] = trim($this->GetQpriority());
3101
		if (empty($cflink['priority'])) {
3102
			unset($cflink['priority']);
3103
		}
3104
		$cflink['name'] = $this->GetQname();
3105
		$cflink['description'] = trim($this->GetDescription());
3106
		if (empty($cflink['description'])) {
3107
			unset($cflink['description']);
3108
		}
3109
		$cflink['bandwidth'] = $this->GetBandwidth();
3110
		$cflink['bandwidthtype'] = $this->GetBwscale();
3111
		$cflink['enabled'] = $this->GetEnabled();
3112
		if (empty($cflink['enabled'])) {
3113
			unset($cflink['enabled']);
3114
		}
3115
		$cflink['default'] = trim($this->GetDefault());
3116
		if (empty($cflink['default'])) {
3117
			unset($cflink['default']);
3118
		}
3119
		$cflink['red'] = trim($this->GetRed());
3120
		if (empty($cflink['red'])) {
3121
			unset($cflink['red']);
3122
		}
3123
		$cflink['rio'] = trim($this->GetRio());
3124
		if (empty($cflink['rio'])) {
3125
			unset($cflink['rio']);
3126
		}
3127
		$cflink['ecn'] = trim($this->GetEcn());
3128
		if (empty($cflink['ecn'])) {
3129
			unset($cflink['ecn']);
3130
		}
3131
		$cflink['codel'] = trim($this->GetCodel());
3132
		if (empty($cflink['codel'])) {
3133
			unset($cflink['codel']);
3134
		}
3135
		$cflink['buckets'] = trim($this->GetBuckets());
3136
		if (empty($cflink['buckets'])) {
3137
			unset($cflink['buckets']);
3138
		}
3139
		$cflink['hogs'] = trim($this->GetHogs());
3140
		if (empty($cflink['hogs'])) {
3141
			unset($cflink['hogs']);
3142
		}
3143
	}
3144
}
3145

    
3146

    
3147
/*
3148
 * dummynet(4) wrappers.
3149
 */
3150

    
3151

    
3152
/*
3153
 * List of respective objects!
3154
 */
3155
$dummynet_pipe_list = array();
3156

    
3157
class dummynet_class {
3158
	var $qname;
3159
	var $qnumber; /* dummynet(4) uses numbers instead of names; maybe integrate with pf the same as altq does?! */
3160
	var $qlimit;
3161
	var $description;
3162
	var $qenabled;
3163
	var $link;
3164
	var $qparent; /* link to upper class so we do things easily on WF2Q+ rule creation */
3165
	var $plr;
3166

    
3167
	var $buckets;
3168
	/* mask parameters */
3169
	var $mask;
3170
	var $noerror;
3171

    
3172
	/* Accessor functions */
3173
	function SetLink($link) {
3174
		$this->link = $link;
3175
	}
3176
	function GetLink() {
3177
		return $this->link;
3178
	}
3179
	function GetMask() {
3180
		if (!isset($this->mask["type"])) {
3181
			$this->mask["type"] = "none";
3182
		}
3183
		return $this->mask;
3184
	}
3185
	function SetMask($mask) {
3186
		$this->mask = $mask;
3187
	}
3188
	function &GetParent() {
3189
		return $this->qparent;
3190
	}
3191
	function SetParent(&$parent) {
3192
		$this->qparent = &$parent;
3193
	}
3194
	function GetEnabled() {
3195
		return $this->qenabled;
3196
	}
3197
	function SetEnabled($value) {
3198
		$this->qenabled = $value;
3199
	}
3200
	function CanHaveChildren() {
3201
		return false;
3202
	}
3203
	function CanBeDeleted() {
3204
		return true;
3205
	}
3206
	function GetQname() {
3207
		return $this->qname;
3208
	}
3209
	function SetQname($name) {
3210
		$this->qname = trim($name);
3211
	}
3212
	function GetQlimit() {
3213
		return $this->qlimit;
3214
	}
3215
	function SetQlimit($limit) {
3216
		$this->qlimit = $limit;
3217
	}
3218
	function GetDescription() {
3219
		return $this->description;
3220
	}
3221
	function SetDescription($str) {
3222
		$this->description = trim($str);
3223
	}
3224
	function GetFirstime() {
3225
		return $this->firsttime;
3226
	}
3227
	function SetFirsttime($number) {
3228
		$this->firsttime = $number;
3229
	}
3230
	function GetBuckets() {
3231
		return $this->buckets;
3232
	}
3233
	function SetBuckets($buckets) {
3234
		$this->buckets = $buckets;
3235
	}
3236
	function SetNumber($number) {
3237
		$this->qnumber = $number;
3238
	}
3239
	function GetNumber() {
3240
		return $this->qnumber;
3241
	}
3242
	function GetPlr() {
3243
		return $this->plr;
3244
	}
3245
	function SetPlr($plr) {
3246
		$this->plr = $plr;
3247
	}
3248

    
3249
	function build_javascript() {
3250
		$javascript .= "<script type=\"text/javascript\">\n";
3251
		$javascript .= "//<![CDATA[\n";
3252
		$javascript .= "function enable_maskbits(enable_over) {\n";
3253
		$javascript .= "var e = document.getElementById(\"mask\");\n";
3254
		$javascript .= "if ((e.options[e.selectedIndex].text == \"none\") || enable_over) {\n";
3255
		$javascript .= "document.iform.maskbits.disabled = 1;\n";
3256
		$javascript .= "document.iform.maskbits.value = \"\";\n";
3257
		$javascript .= "document.iform.maskbitsv6.disabled = 1;\n";
3258
		$javascript .= "document.iform.maskbitsv6.value = \"\";\n";
3259
		$javascript .= "} else {\n";
3260
		$javascript .= "document.iform.maskbits.disabled = 0;\n";
3261
		$javascript .= "document.iform.maskbitsv6.disabled = 0;\n";
3262
		$javascript .= "}}\n";
3263
		$javascript .= "//]]>\n";
3264
		$javascript .= "</script>\n";
3265
		return $javascript;
3266
	}
3267

    
3268
	function validate_input($data, &$input_errors) {
3269
		$reqdfields[] = "bandwidth";
3270
		$reqdfieldsn[] = gettext("Bandwidth");
3271
		/*$reqdfields[] = "burst";
3272
		$reqdfieldsn[] = gettext("Burst"); */
3273
		$reqdfields[] = "bandwidthtype";
3274
		$reqdfieldsn[] = gettext("Bandwidthtype");
3275
		$reqdfields[] = "newname";
3276
		$reqdfieldsn[] = gettext("Name");
3277

    
3278
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
3279

    
3280
		if ($data['plr'] && (!is_numeric($data['plr']) ||
3281
		    ($data['plr'] < 0) || ($data['plr'] > 1))) {
3282
			$input_errors[] = gettext("Plr must be a value between 0 and 1.");
3283
		}
3284
		if ($data['buckets'] && (!is_numeric($data['buckets']) ||
3285
		    ($data['buckets'] < 16) || ($data['buckets'] > 65535))) {
3286
			$input_errors[] = gettext("Buckets must be an integer between 16 and 65535.");
3287
		}
3288
		if ($data['qlimit'] && (!is_numeric($data['qlimit']))) {
3289
			$input_errors[] = gettext("Queue limit must be an integer");
3290
		}
3291
		if (!empty($data['newname']) && !preg_match("/^[a-zA-Z0-9_-]+$/", $data['newname'])) {
3292
			$input_errors[] = gettext("Queue names must be alphanumeric and _ or - only.");
3293
		}
3294
		if (!empty($data['name']) && !preg_match("/^[a-zA-Z0-9_-]+$/", $data['name'])) {
3295
			$input_errors[] = gettext("Queue names must be alphanumeric and _ or - only.");
3296
		}
3297
		if (isset($data['maskbits']) && ($data['maskbits'] <> "")) {
3298
			if ((!is_numeric($data['maskbits'])) || ($data['maskbits'] <= 0) || ($data['maskbits'] > 32)) {
3299
				$input_errors[] = gettext("IPV4 bit mask must be blank or numeric value between 1 and 32.");
3300
			}
3301
		}
3302
		if (isset($data['maskbitsv6']) && ($data['maskbitsv6'] <> "")) {
3303
			if ((!is_numeric($data['maskbitsv6'])) || ($data['maskbitsv6'] <= 0) || ($data['maskbitsv6'] > 128)) {
3304
				$input_errors[] = gettext("IPV6 bit mask must be blank or numeric value between 1 and 128.");
3305
			}
3306
		}
3307
	}
3308

    
3309
	function build_mask_rules(&$pfq_rule) {
3310
		$mask = $this->GetMask();
3311
		if (!empty($mask['type'])) {
3312
			if ($mask['type'] <> 'none') {
3313
				$pfq_rule .= " mask";
3314
			}
3315
			switch ($mask['type']) {
3316
				case 'srcaddress':
3317
					if (!empty($mask['bitsv6']) && ($mask['bitsv6'] <> "")) {
3318
						$pfq_rule .= " src-ip6 /" . $mask['bitsv6'];
3319
					} else {
3320
						$pfq_rule .= " src-ip6 /128";
3321
					}
3322
					if (!empty($mask['bits']) && ($mask['bits'] <> "")) {
3323
						$pfq_rule .= sprintf(" src-ip 0x%x", gen_subnet_mask_long($mask['bits']));
3324
					} else {
3325
						$pfq_rule .= " src-ip 0xffffffff";
3326
					}
3327
					break;
3328
				case 'dstaddress':
3329
					if (!empty($mask['bitsv6']) && ($mask['bitsv6'] <> "")) {
3330
						$pfq_rule .= " dst-ip6 /" . $mask['bitsv6'];
3331
					} else {
3332
						$pfq_rule .= " dst-ip6 /128";
3333
					}
3334
					if (!empty($mask['bits']) && ($mask['bits'] <> "")) {
3335
						$pfq_rule .= sprintf(" dst-ip 0x%x", gen_subnet_mask_long($mask['bits']));
3336
					} else {
3337
						$pfq_rule .= " dst-ip 0xffffffff";
3338
					}
3339
					break;
3340
				default:
3341
					break;
3342
			}
3343
		}
3344
	}
3345

    
3346
}
3347

    
3348
class dnpipe_class extends dummynet_class {
3349
	var $delay;
3350
	var $qbandwidth = array();
3351
	var $qbandwidthtype;
3352

    
3353
		/* This is here to help on form building and building rules/lists */
3354
	var $subqueues = array();
3355

    
3356
	function CanHaveChildren() {
3357
		return true;
3358
	}
3359
	function SetDelay($delay) {
3360
		$this->delay = $delay;
3361
	}
3362
	function GetDelay() {
3363
		return $this->delay;
3364
	}
3365
	function delete_queue() {
3366
		cleanup_dnqueue_from_rules($this->GetQname());
3367
		foreach ($this->subqueues as $q) {
3368
			$q->delete_queue();
3369
		}
3370
		unset_dn_object_by_reference($this->GetLink());
3371
		@pfSense_pipe_action("pipe delete " . $this->GetNumber());
3372
	}
3373
	function GetBandwidth() {
3374
		return $this->qbandwidth;
3375
	}
3376
	function SetBandwidth($bandwidth) {
3377
		$this->qbandwidth = $bandwidth;
3378
	}
3379
	function GetBurst() {
3380
		return $this->qburst;
3381
	}
3382
	function SetBurst($burst) {
3383
		$this->qburst = $burst;
3384
	}
3385

    
3386
	function &add_queue($interface, &$queue, &$path, &$input_errors) {
3387

    
3388
		if (!is_array($this->subqueues)) {
3389
			$this->subqueues = array();
3390
		}
3391

    
3392
		$q =& new dnqueue_class();
3393
		$q->SetLink($path);
3394
		$q->SetEnabled("on");
3395
		$q->SetPipe($this->GetQname());
3396
		$q->SetParent($this);
3397
		$q->ReadConfig($queue);
3398
		$q->validate_input($queue, $input_errors);
3399
		if (count($input_errors)) {
3400
			log_error("SHAPER: could not create queue " . $q->GetQname() . " on interface {$interface} because: " . print_r($input_errors, true));
3401
			return $q;
3402
		}
3403
		$number = dnqueue_find_nextnumber();
3404
		$q->SetNumber($number);
3405
		$this->subqueues[$q->GetQname()] = &$q;
3406

    
3407
		return $q;
3408
	}
3409

    
3410
	function &get_queue_list(&$q = null) {
3411
		$qlist = array();
3412

    
3413
		$qlist[$this->GetQname()] = $this->GetNumber();
3414
		if (is_array($this->subqueues)) {
3415
			foreach ($this->subqueues as $queue) {
3416
				$queue->get_queue_list($qlist);
3417
			}
3418
		}
3419
		return $qlist;
3420
	}
3421

    
3422
	/*
3423
	 * Should search even its children
3424
	 */
3425
	function &find_queue($pipe, $qname) {
3426
		if ($qname == $this->GetQname()) {
3427
			return $this;
3428
		}
3429
		foreach ($this->subqueues as $q) {
3430
			$result =& $q->find_queue("", $qname);
3431
			if ($result) {
3432
				return $result;
3433
			}
3434
		}
3435
	}
3436

    
3437
	function &find_parentqueue($pipe, $qname) {
3438
		return NULL;
3439
	}
3440

    
3441
	function validate_input($data, &$input_errors) {
3442
		parent::validate_input($data, $input_errors);
3443

    
3444
		$schedule = 0;
3445
		$schedulenone = 0;
3446
		$entries = 0;
3447
		/* XXX: Really no better way? */
3448
		for ($i = 0; $i < 2900; $i++) {
3449
			if (!empty($data["bwsched{$i}"])) {
3450
				if ($data["bwsched{$i}"] != "none") {
3451
					$schedule++;
3452
				} else {
3453
					$schedulenone++;
3454
				}
3455
			}
3456
			if (!empty($data["bandwidth{$i}"])) {
3457
				if (!is_numeric($data["bandwidth{$i}"])) {
3458
					$input_errors[] = sprintf(gettext("Bandwidth for schedule %s must be an integer."), $data["bwsched{$i}"]);
3459
				} else if (($data["burst{$i}"] != "") && (!is_numeric($data["burst{$i}"]))) {
3460
					$input_errors[] = sprintf(gettext("Burst for schedule %s must be an integer."), $data["bwsched{$i}"]);
3461
				} else {
3462
					$entries++;
3463
				}
3464
			}
3465
		}
3466
		if ($schedule == 0 && $entries > 1) {
3467
			$input_errors[] = gettext("You need to specify a schedule for every additional entry");
3468
		}
3469
		if ($schedulenone > 0 && $entries > 1) {
3470
			$input_errors[] = gettext("If more than one bandwidth configured all schedules need to be selected");
3471
		}
3472
		if ($entries == 0) {
3473
			$input_errors[] = gettext("At least one bw specification is necessary");
3474
		}
3475
		if ($data['delay'] && (!is_numeric($data['delay']))) {
3476
			$input_errors[] = gettext("Delay must be an integer.");
3477
		}
3478
	}
3479

    
3480
	function ReadConfig(&$q) {
3481
		if (!empty($q['name']) && !empty($q['newname']) && $q['name'] != $q['newname']) {
3482
			$this->SetQname($q['newname']);
3483
		} else if (!empty($q['newname'])) {
3484
			$this->SetQname($q['newname']);
3485
		} else {
3486
			$this->SetQname($q['name']);
3487
		}
3488
		$this->SetNumber($q['number']);
3489

    
3490
		if (!empty($_POST)) {
3491
			$bandwidth = array();
3492
			/* XXX: Really no better way? */
3493
			for ($i = 0; $i < 2900; $i++) {
3494
				if (isset($q["bandwidth{$i}"]) && $q["bandwidth{$i}"] <> "") {
3495
					$bw = array();
3496
					$bw['bw'] = $q["bandwidth{$i}"];
3497
					$bw['burst'] = $q["burst{$i}"];
3498
					if (isset($q["bwtype{$i}"]) && $q["bwtype{$i}"]) {
3499
						$bw['bwscale'] = $q["bwtype{$i}"];
3500
					}
3501
					if (isset($q["bwsched{$i}"]) && $q["bwsched{$i}"]) {
3502
						$bw['bwsched'] = $q["bwsched{$i}"];
3503
					}
3504
					$bandwidth[] = $bw;
3505
				}
3506
			}
3507
			$this->SetBandwidth($bandwidth);
3508
		}
3509

    
3510
		if (is_array($q['bandwidth']) && is_array($q['bandwidth']['item'])) {
3511
			$this->SetBandwidth($q['bandwidth']['item']);
3512
			$this->SetBurst($q['burst']['item']);
3513
		}
3514

    
3515
		if (isset($q['qlimit']) && $q['qlimit'] <> "") {
3516
			$this->SetQlimit($q['qlimit']);
3517
		} else {
3518
			$this->SetQlimit("");
3519
		}
3520
		if (isset($q['mask']) && $q['mask'] <> "") {
3521
			$masktype = $q['mask'];
3522
		} else {
3523
			$masktype = "";
3524
		}
3525
		if (isset($q['maskbits']) && $q['maskbits'] <> "") {
3526
			$maskbits = $q['maskbits'];
3527
		} else {
3528
			$maskbits = "";
3529
		}
3530
		if (isset($q['maskbitsv6']) && $q['maskbitsv6'] <> "") {
3531
			$maskbitsv6 = $q['maskbitsv6'];
3532
		} else {
3533
			$maskbitsv6 = "";
3534
		}
3535
		$this->SetMask(array("type" => $masktype, "bits" => $maskbits, "bitsv6" => $maskbitsv6));
3536
		if (isset($q['buckets']) && $q['buckets'] <> "") {
3537
			$this->SetBuckets($q['buckets']);
3538
		} else {
3539
			$this->SetBuckets("");
3540
		}
3541
		if (isset($q['plr']) && $q['plr'] <> "") {
3542
			$this->SetPlr($q['plr']);
3543
		} else {
3544
			$this->SetPlr("");
3545
		}
3546
		if (isset($q['delay']) && $q['delay'] <> "") {
3547
			$this->SetDelay($q['delay']);
3548
		} else {
3549
			$this->SetDelay(0);
3550
		}
3551
		if (isset($q['description']) && $q['description'] <> "") {
3552
			$this->SetDescription($q['description']);
3553
		} else {
3554
			$this->SetDescription("");
3555
		}
3556
		$this->SetEnabled($q['enabled']);
3557

    
3558
	}
3559

    
3560
	function build_tree() {
3561
		$tree = " <li><a href=\"firewall_shaper_vinterface.php?pipe=" . $this->GetQname() ."&amp;queue=".$this->GetQname() ."&amp;action=show\">";
3562
		$tree .= $this->GetQname() . "</a>";
3563
		if (is_array($this->subqueues)) {
3564
			$tree .= "<ul>";
3565
			foreach ($this->subqueues as $q) {
3566
				$tree .= $q->build_tree();
3567
			}
3568
			$tree .= "</ul>";
3569
		}
3570
		$tree .= "</li>";
3571

    
3572
		return $tree;
3573
	}
3574

    
3575
	function build_rules() {
3576
		global $config, $time_based_rules;
3577

    
3578
		if ($this->GetEnabled() == "") {
3579
			return;
3580
		}
3581

    
3582
		$pfq_rule = "\npipe ". $this->GetNumber() . " config ";
3583
		$found = false;
3584
		$bandwidth = $this->GetBandwidth();
3585
		if (is_array($bandwidth)) {
3586
			foreach ($bandwidth as $bw) {
3587
				if ($bw['bwsched'] != "none") {
3588
					$time_based_rules = true;
3589
					if (is_array($config['schedules']) && is_array($config['schedules']['schedule'])) {
3590
						foreach ($config['schedules']['schedule'] as $schedule) {
3591
							if ($bw['bwsched'] == $schedule['name']) {
3592
								if (filter_get_time_based_rule_status($schedule)) {
3593
									$pfq_rule .= " bw ".trim($bw['bw']).$bw['bwscale'];
3594
									if (is_numeric($bw['burst']) && ($bw['burst'] > 0)) {
3595
										$pfq_rule .= " burst ".trim($bw['burst']);
3596
									}
3597
									$found = true;
3598
									break;
3599
								}
3600
							}
3601
						}
3602
					} else {
3603
						$pfq_rule .= " bw 0";
3604
						$found = true;
3605
						break;
3606
					}
3607
				} else {
3608
					$pfq_rule .= " bw ".trim($bw['bw']).$bw['bwscale'];
3609
					if (is_numeric($bw['burst']) && ($bw['burst'] > 0)) {
3610
						$pfq_rule .= " burst ".trim($bw['burst']);
3611
					}
3612
					$found = true;
3613
					break;
3614
				}
3615
			}
3616
			if ($found == false) {
3617
				$pfq_rule .= " bw 0";
3618
			}
3619
		} else {
3620
			$pfq_rule .= " bw 0";
3621
		}
3622

    
3623
		if ($this->GetQlimit()) {
3624
			$pfq_rule .= " queue " . $this->GetQlimit();
3625
		}
3626
		if ($this->GetPlr()) {
3627
			$pfq_rule .= " plr " . $this->GetPlr();
3628
		}
3629
		if ($this->GetBuckets()) {
3630
			$pfq_rule .= " buckets " . $this->GetBuckets();
3631
		}
3632
		if ($this->GetDelay()) {
3633
			$pfq_rule .= " delay " . $this->GetDelay();
3634
		}
3635
		$this->build_mask_rules($pfq_rule);
3636

    
3637
		$pfq_rule .= "\n";
3638

    
3639
		if (!empty($this->subqueues) && count($this->subqueues) > 0) {
3640
			foreach ($this->subqueues as $q) {
3641
				$pfq_rule .= $q->build_rules();
3642
			}
3643
		}
3644
		$pfq_rule .= " \n";
3645

    
3646
		return $pfq_rule;
3647
	}
3648

    
3649
	function update_dn_data(&$data) {
3650
		$this->ReadConfig($data);
3651
	}
3652

    
3653
	function build_javascript() {
3654
		global $g, $config;
3655

    
3656
		$javasr = parent::build_javascript();
3657

    
3658
		//build list of schedules
3659
		$schedules = "<option value='none'>none</option>";
3660
		if (is_array($config['schedules']) && is_array($config['schedules']['schedule'])) {
3661
			foreach ($config['schedules']['schedule'] as $schedule) {
3662
				if ($schedule['name'] <> "") {
3663
					$schedules .= "<option value='{$schedule['name']}'>{$schedule['name']}</option>";
3664
				}
3665
			}
3666
		}
3667
		$bwopt = "";
3668
		foreach (array("Kb" => "Kbit/s", "Mb" => "Mbit/s", "Gb" => "Gbit/s", "b" => "Bit/s") as $bwidx => $bw) {
3669
			$bwopt .= "<option value='{$bwidx}'>{$bw}</option>";
3670
		}
3671

    
3672
		$javasr .= <<<EOD
3673
<script type='text/javascript'>
3674
//<![CDATA[
3675
var addBwRowTo = (function() {
3676

    
3677
	return (function (tableId) {
3678

    
3679
	var table = document.getElementById(tableId);
3680
	var totalrows = table.rows.length -1;
3681

    
3682
	var row = table.insertRow(totalrows + 1);
3683
	var cell1 = row.insertCell(0);
3684
	var cell2 = row.insertCell(1);
3685
	var cell3 = row.insertCell(2);
3686
	var cell4 = row.insertCell(3);
3687

    
3688
	cell1.innerHTML = "<input type='hidden' value='" + totalrows +"' name='bandwidth_row-" + totalrows + "' /><input type='text' class='form-control' name='bandwidth" + totalrows + "' id='bandwidth" + totalrows + "' />";
3689
	cell2.innerHTML = "<input type='hidden' value='" + totalrows +"' name='bwtype_row-" + totalrows + "' /><select class='form-control' name='bwtype" + totalrows + "'>{$bwopt}</select>";
3690
	cell3.innerHTML = "<input type='hidden' value='" + totalrows +"' name='bwsched_row-" + totalrows + "' /><select class='form-control' name='bwsched" + totalrows + "'>{$schedules}</select>";
3691
	cell4.innerHTML = '<a class="btn btn-default" onclick="removeBwRow(this); return false;" href="#">Remove</a>';
3692

    
3693
	});
3694
})();
3695

    
3696
function removeBwRow(el) {
3697
	var d = el.parentNode.parentNode.rowIndex;
3698
	document.getElementById('maintable').deleteRow(d);
3699
}
3700
//]]>
3701
</script>
3702

    
3703
EOD;
3704

    
3705
		return $javasr;
3706
	}
3707

    
3708
	// Compose a table of bandwidths that can then be inserted into the form using a Form_StaticText
3709
	// The table has been "Bootstrapped" to match the web design while maintaining compatibility with
3710
	// with the javascript in this class
3711
	function build_bwtable() {
3712
		global $config;
3713

    
3714
		$bandwidth = $this->GetBandwidth();
3715
				//build list of schedules
3716
		$schedules = array();
3717
		$schedules[] = "none";//leave none to leave rule enabled all the time
3718
		if (is_array($config['schedules']) && is_array($config['schedules']['schedule'])) {
3719
			foreach ($config['schedules']['schedule'] as $schedule) {
3720
				if ($schedule['name'] != "") {
3721
					$schedules[] = $schedule['name'];
3722
				}
3723
			}
3724
		}
3725

    
3726
		$form = '<div class="table-responsive">';
3727
		$form .= '<table id="maintable" class="table table-hover table-striped">';
3728
		$form .= "<thead><tr>";
3729
		$form .= "<th>Bandwidth</th>";
3730
		//$form .= "<td width='35%'><div id='fifthcolumn'>Burst</div></td>";
3731
		$form .= "<th>Bw type</th>";
3732
		$form .= "<th>Schedule</th>";
3733
		$form .= "<th></th>";
3734
		$form .= "</tr></thead>";
3735
		$form .= "<tbody>";
3736

    
3737
		// If there are no bandwidths defined, make a blank one for convenience
3738
		if(empty($bandwidth))
3739
			$bandwidth = array(0 => array('bw' => '', 'bwscale' => 'Kb', 'bwsched' => 'none'));
3740
			
3741
		if (is_array($bandwidth)) {
3742
			foreach ($bandwidth as $bwidx => $bw) {
3743
				$form .= '<tr>';
3744
				$form .= '<td class="col-xs-4">';
3745
				$form .= "<input class='form-control' type=\"number\" id=\"bandwidth{$bwidx}\" name=\"bandwidth{$bwidx}\" value=\"{$bw['bw']}\" />";
3746
				//$form .= "</td><td width='20%'>";
3747
				//$form .= "<input class='formfld unknown' size='10' type=\"text\" id=\"burst{$bwidx}\" name=\"burst{$bwidx}\" value=\"{$bw['burst']}\" />";
3748
				$form .= "</td>";
3749
				$form .= '<td class="col-xs-4">';
3750
				$form .= "<select id=\"bwtype{$bwidx}\" name=\"bwtype{$bwidx}\" class=\"form-control\">";
3751
				
3752
				foreach (array("Kb" => "Kbit/s", "Mb" => "Mbit/s", "Gb" => "Gbit/s", "b" => "Bit/s") as $bwsidx => $bwscale) {
3753
					$form .= "<option value=\"{$bwsidx}\"";
3754
					
3755
					if ($bw['bwscale'] == $bwsidx) {
3756
						$form .= " selected=\"selected\"";
3757
					}
3758
					
3759
					$form .= ">{$bwscale}</option>";
3760
				}
3761
				
3762
				$form .= "</select>";
3763
				$form .= "</td>";
3764
				$form .= '<td class="col-xs-4">';
3765
				$form .= "<select id=\"bwsched{$bwidx}\" name=\"bwsched{$bwidx}\" class=\"form-control\">";
3766
				
3767
				foreach ($schedules as $schd) {
3768
					$selected = "";
3769
					if ($bw['bwsched'] == $schd) {
3770
						$selected = "selected=\"selected\"";
3771
					}
3772
					
3773
					$form .= "<option value='{$schd}' {$selected}>{$schd}</option>";
3774
				}
3775
				
3776
				$form .= "</select>";
3777
				$form .= "</td>";
3778
				$form .= '<td>';
3779
				$form .= '<a type="button" class="btn btn-default" onclick="removeBwRow(this); return false;">' . gettext('Remove') . '</a>';
3780
				$form .= "</td></tr>";
3781
			}
3782
		}
3783
		$form .= "</tbody></table></div><br />";
3784

    
3785
		$form .= '<a type="button" class="btn btn-sm btn-success" onclick="javascript:addBwRowTo(\'maintable\'); return false;" >';
3786
		$form .= gettext("Add another schedule") . "</a>";
3787

    
3788
		return($form);
3789
	}
3790

    
3791
	function build_form() {
3792
		global $g, $config, $pipe, $action, $qname;
3793

    
3794
		//build list of schedules
3795
		$schedules = array();
3796
		$schedules[] = "none";//leave none to leave rule enabled all the time
3797
		if (is_array($config['schedules']) && is_array($config['schedules']['schedule'])) {
3798
			foreach ($config['schedules']['schedule'] as $schedule) {
3799
				if ($schedule['name'] <> "") {
3800
					$schedules[] = $schedule['name'];
3801
				}
3802
			}
3803
		}
3804

    
3805

    
3806
		$sform = new Form();
3807

    
3808
		$section = new Form_Section('Limiters');
3809

    
3810
		$section->addInput(new Form_Checkbox(
3811
			'enabled',
3812
			'Enable',
3813
			'Enable limiter and its children',
3814
			($this->GetEnabled() == "on"),
3815
			'on'
3816
		));
3817

    
3818
		$section->addInput(new Form_Input(
3819
			'newname',
3820
			'Name',
3821
			'text',
3822
			$this->GetQname()
3823
		));
3824

    
3825
		$section->addInput(new Form_Input(
3826
			'name',
3827
			null,
3828
			'hidden',
3829
			$this->GetQname()
3830
		));
3831

    
3832
		if ($this->GetNumber() > 0) {
3833
			$section->addInput(new Form_Input(
3834
				'number',
3835
				null,
3836
				'hidden',
3837
				$this->GetNumber()
3838
			));
3839
		}
3840

    
3841
		$bandwidth = $this->GetBandwidth();
3842

    
3843
		// Delete a row
3844
//		if(isset($_GET['delbwrow']) && (count($bandwidth) > 0))
3845
//			unset($bandwidth[$_GET['delbwrow']]);
3846

    
3847
		// Add a row
3848
//		if($_GET['newbwrow']) {
3849
//			array_push($bandwidth, array(count($bandwidth) => array('bw' => '', 'burst' => '', 'bwscale' => 'Kb', 'bwsched' => 'none') ));
3850
//		}
3851
		
3852
		if (is_array($bandwidth)) {
3853
				$section->addInput(new Form_StaticText(
3854
				'Bandwidth',
3855
				$this->build_bwtable()
3856
			));
3857
		}
3858

    
3859
		$mask = $this->GetMask();
3860

    
3861
		$section->addInput(new Form_Select(
3862
			'scheduler',
3863
			'Mask',
3864
			$mask['type'],
3865
			array('none' => 'None', 'srcaddress' => 'Source addresses', 'dstaddress' => 'Destination addresses')
3866
		))->setHelp('If "source" or "destination" slots is chosen a dynamic pipe with the bandwidth, delay, packet loss ' .
3867
					'and queue size given above will be created for each source/destination IP address encountered, respectively. ' .
3868
					'This makes it possible to easily specify bandwidth limits per host.');
3869

    
3870
		$group = new Form_Group(null);
3871

    
3872
		$group->add(new Form_Select(
3873
			'maskbits',
3874
			null,
3875
			$mask['bits'],
3876
			array_combine(range(32, 1, -1), range(32, 1, -1))
3877
		))->setHelp('IPV4 mask bits' . '<br />' . '255.255.255.255/?');
3878

    
3879
		$group->add(new Form_Select(
3880
			'maskbitsv6',
3881
			null,
3882
			$mask['bitsv6'],
3883
			array_combine(range(128, 1, -1), range(128, 1, -1))
3884
		))->setHelp('IPV6 mask bits' . '<br />' . '<font face="consolas">ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/?</font>');
3885

    
3886
		$section->add($group);
3887

    
3888
		$section->addInput(new Form_Input(
3889
			'description',
3890
			'Description',
3891
			'text',
3892
			$this->GetDescription()
3893
		))->setHelp('You may enter a description here for your reference (not parsed).');
3894

    
3895
		$sform->add($section);
3896

    
3897
		$section = new Form_Section('Advanced options');
3898

    
3899
		$section->addInput(new Form_Input(
3900
			'delay',
3901
			'Delay (ms)',
3902
			'text',
3903
			$this->GetDelay() > 0 ? $this->GetDelay():null
3904
		))->setHelp('In most cases, you should specify 0 here (or leave the field empty)');
3905

    
3906
		$section->addInput(new Form_Input(
3907
			'plr',
3908
			'Packet Loss Rate',
3909
			'number',
3910
			$this->GetPlr(),
3911
			['step' => '0.001', 'min' => '0.000']
3912
		))->setHelp('In most cases, you should specify 0 here (or leave the field empty). ' .
3913
					'A value of 0.001 means one packet in 1000 gets dropped');
3914

    
3915
		$section->addInput(new Form_Input(
3916
			'qlimit',
3917
			'Queue size (slots)',
3918
			'number',
3919
			$this->GetQlimit()
3920
		))->setHelp('In most cases, you should leave the field empty. All packets in this pipe are placed into a fixed-size queue first, ' .
3921
					'then they are delayed by value specified in the Delay field, and then they are delivered to their destination.');
3922

    
3923
		$section->addInput(new Form_Input(
3924
			'buckets',
3925
			'Bucket size (slots)',
3926
			'number',
3927
			$this->GetBuckets()
3928
		))->setHelp('In most cases, you should leave this field empty. It increases the hash size set');
3929

    
3930
		$sform->add($section);
3931

    
3932
		return($sform);
3933
		}
3934

    
3935
	function wconfig() {
3936
		$cflink =& get_dn_reference_to_me_in_config($this->GetLink());
3937
		if (!is_array($cflink)) {
3938
			$cflink = array();
3939
		}
3940
		$cflink['name'] = $this->GetQname();
3941
		$cflink['number'] = $this->GetNumber();
3942
		$cflink['qlimit'] = $this->GetQlimit();
3943
		$cflink['plr'] = $this->GetPlr();
3944
		$cflink['description'] = $this->GetDescription();
3945

    
3946
		$bandwidth = $this->GetBandwidth();
3947
		if (is_array($bandwidth)) {
3948
			$cflink['bandwidth'] = array();
3949
			$cflink['bandwidth']['item'] = array();
3950
			foreach ($bandwidth as $bwidx => $bw) {
3951
				$cflink['bandwidth']['item'][] = $bw;
3952
			}
3953
		}
3954

    
3955
		$cflink['enabled'] = $this->GetEnabled();
3956
		$cflink['buckets'] = $this->GetBuckets();
3957
		$mask = $this->GetMask();
3958
		$cflink['mask'] = $mask['type'];
3959
		$cflink['maskbits'] = $mask['bits'];
3960
		$cflink['maskbitsv6'] = $mask['bitsv6'];
3961
		$cflink['delay'] = $this->GetDelay();
3962
	}
3963

    
3964
}
3965

    
3966
class dnqueue_class extends dummynet_class {
3967
	var $pipeparent;
3968
	var $weight;
3969

    
3970
	function GetWeight() {
3971
		return $this->weight;
3972
	}
3973
	function SetWeight($weight) {
3974
		$this->weight = $weight;
3975
	}
3976
	function GetPipe() {
3977
		return $this->pipeparent;
3978
	}
3979
	function SetPipe($pipe) {
3980
		$this->pipeparent = $pipe;
3981
	}
3982

    
3983
	/* Just a stub in case we ever try to call this from the frontend. */
3984
	function &add_queue($interface, &$queue, &$path, &$input_errors) {
3985
		return;
3986
	}
3987

    
3988
	function delete_queue() {
3989
		cleanup_dnqueue_from_rules($this->GetQname());
3990
		unset_dn_object_by_reference($this->GetLink());
3991
		@pfSense_pipe_action("queue delete " . $this->GetNumber());
3992
	}
3993

    
3994
	function validate_input($data, &$input_errors) {
3995
		parent::validate_input($data, $input_errors);
3996

    
3997
		if ($data['weight'] && ((!is_numeric($data['weight'])) ||
3998
		    ($data['weight'] < 1 && $data['weight'] > 100))) {
3999
			$input_errors[] = gettext("Weight must be an integer between 1 and 100.");
4000
		}
4001
	}
4002

    
4003
	/*
4004
	 * Should search even its children
4005
	 */
4006
	function &find_queue($pipe, $qname) {
4007
		if ($qname == $this->GetQname()) {
4008
			return $this;
4009
		} else {
4010
			return NULL;
4011
		}
4012
	}
4013

    
4014
	function &find_parentqueue($pipe, $qname) {
4015
		return $this->qparent;
4016
	}
4017

    
4018
	function &get_queue_list(&$qlist) {
4019
		if ($this->GetEnabled() == "") {
4020
			return;
4021
		}
4022
		$qlist[$this->GetQname()] = "?" .$this->GetNumber();
4023
	}
4024

    
4025
	function ReadConfig(&$q) {
4026
		if (!empty($q['name']) && !empty($q['newname']) && $q['name'] != $q['newname']) {
4027
			$this->SetQname($q['newname']);
4028
		} else if (!empty($q['newname'])) {
4029
			$this->SetQname($q['newname']);
4030
		} else {
4031
			$this->SetQname($q['name']);
4032
		}
4033
		$this->SetNumber($q['number']);
4034
		if (isset($q['qlimit']) && $q['qlimit'] <> "") {
4035
			$this->SetQlimit($q['qlimit']);
4036
		} else {
4037
			$this->SetQlimit("");
4038
		}
4039
		if (isset($q['mask']) && $q['mask'] <> "") {
4040
			$masktype = $q['mask'];
4041
		} else {
4042
			$masktype = "";
4043
		}
4044
		if (isset($q['maskbits']) && $q['maskbits'] <> "") {
4045
			$maskbits = $q['maskbits'];
4046
		} else {
4047
			$maskbits = "";
4048
		}
4049
		if (isset($q['maskbitsv6']) && $q['maskbitsv6'] <> "") {
4050
			$maskbitsv6 = $q['maskbitsv6'];
4051
		} else {
4052
			$maskbitsv6 = "";
4053
		}
4054
		$this->SetMask(array("type" => $masktype, "bits" => $maskbits, "bitsv6" => $maskbitsv6));
4055
		if (isset($q['buckets']) && $q['buckets'] <> "") {
4056
			$this->SetBuckets($q['buckets']);
4057
		} else {
4058
			$this->SetBuckets("");
4059
		}
4060
		if (isset($q['plr']) && $q['plr'] <> "") {
4061
			$this->SetPlr($q['plr']);
4062
		} else {
4063
			$this->SetPlr("");
4064
		}
4065
		if (isset($q['weight']) && $q['weight'] <> "") {
4066
			$this->SetWeight($q['weight']);
4067
		} else {
4068
			$this->SetWeight("");
4069
		}
4070
		if (isset($q['description']) && $q['description'] <> "") {
4071
			$this->SetDescription($q['description']);
4072
		} else {
4073
			$this->SetDescription("");
4074
		}
4075
		$this->SetEnabled($q['enabled']);
4076
	}
4077

    
4078
	function build_tree() {
4079
		$parent =& $this->GetParent();
4080
		$tree = " <li><a href=\"firewall_shaper_vinterface.php?pipe=" . $parent->GetQname() ."&amp;queue=" . $this->GetQname() ."&amp;action=show\">";
4081
		$tree .= $this->GetQname() . "</a>";
4082
		$tree .= "</li>";
4083

    
4084
		return $tree;
4085
	}
4086

    
4087
	function build_rules() {
4088
		if ($this->GetEnabled() == "") {
4089
			return;
4090
		}
4091

    
4092
		$parent =& $this->GetParent();
4093
		$pfq_rule = "queue ". $this->GetNumber() . " config pipe " . $parent->GetNumber();
4094
		if ($this->GetQlimit()) {
4095
			$pfq_rule .= " queue " . $this->GetQlimit();
4096
		}
4097
		if ($this->GetWeight()) {
4098
			$pfq_rule .= " weight " . $this->GetWeight();
4099
		}
4100
		if ($this->GetBuckets()) {
4101
			$pfq_rule .= " buckets " . $this->GetBuckets();
4102
		}
4103
		$this->build_mask_rules($pfq_rule);
4104
		$pfq_rule .= "\n";
4105

    
4106
		return $pfq_rule;
4107
	}
4108

    
4109
	function build_javascript() {
4110
		return parent::build_javascript();
4111
	}
4112

    
4113
	function build_form() {
4114
		global $g, $config, $pipe, $action, $qname;
4115

    
4116
		//build list of schedules
4117
		$schedules = array();
4118
		$schedules[] = "none";//leave none to leave rule enabled all the time
4119
		if (is_array($config['schedules']) && is_array($config['schedules']['schedule'])) {
4120
			foreach ($config['schedules']['schedule'] as $schedule) {
4121
				if ($schedule['name'] <> "") {
4122
					$schedules[] = $schedule['name'];
4123
				}
4124
			}
4125
		}
4126

    
4127

    
4128
		$sform = new Form();
4129

    
4130
		$section = new Form_Section('Limiters');
4131

    
4132
		$section->addInput(new Form_Checkbox(
4133
			'enabled',
4134
			'Enable',
4135
			'Enable this queue',
4136
			($this->GetEnabled() == "on"),
4137
			'on'
4138
		));
4139

    
4140
		$section->addInput(new Form_Input(
4141
			'newname',
4142
			'Name',
4143
			'text',
4144
			$this->GetQname()
4145
		));
4146

    
4147
		$section->addInput(new Form_Input(
4148
			'name',
4149
			null,
4150
			'hidden',
4151
			$this->GetQname()
4152
		));
4153

    
4154
		if ($this->GetNumber() > 0) {
4155
			$section->addInput(new Form_Input(
4156
				'number',
4157
				null,
4158
				'hidden',
4159
				$this->GetNumber()
4160
			));
4161
		}
4162

    
4163
		$bandwidth = $this->GetBandwidth();
4164

    
4165
		// Delete a row
4166
		if(isset($_GET['delbwrow']) && (count($bandwidth) > 0))
4167
			unset($bandwidth[$_GET['delbwrow']]);
4168

    
4169
		// Add a row
4170
		if($_GET['newbwrow']) {
4171
			array_push($bandwidth, array(count($bandwidth) => array('bw' => '', 'burst' => '', 'bwscale' => 'Kb', 'bwsched' => 'none') ));
4172

    
4173
		}
4174

    
4175
		if (is_array($bandwidth)) {
4176
			$row = 0;
4177
			$numrows = count($bandwidth) - 1;
4178

    
4179
			if($numrows >= 0) {
4180
				foreach ($bandwidth as $bwidx => $bw) {
4181
					$group = new Form_Group($row == 0 ? 'Bandwidth':null);
4182

    
4183
					$group->add(new Form_Input(
4184
						'bandwidth' . $bwidx,
4185
						null,
4186
						'text',
4187
						$bw['bw']
4188
					))->setHelp($row == $numrows ? 'Bandwidth':null);
4189

    
4190
					$group->add(new Form_Select(
4191
						'bwtype' . $bwidx,
4192
						null,
4193
						$bw['bwscale'],
4194
						array('Kb' => 'Kbit/s', 'Mb' => 'Mbit/s', 'Gb' => 'Gbit/s', 'b' => 'Bit/s')
4195
					))->setHelp($row == $numrows ? 'Bw Type':null);;
4196

    
4197
					$group->add(new Form_Select(
4198
						'bwsched' . $bwidx,
4199
						null,
4200
						$bw['bwsched'],
4201
						$schedules
4202
					))->setHelp($row == $numrows ? 'Schedule':null);;
4203

    
4204
					$group->add(new Form_Button(
4205
						'delete' + $bwidx,
4206
						'Delete',
4207
						'firewall_shaper_vinterface.php?pipe=' . $pipe . '&queue=' . $qname . '&action=' . $action . '&delbwrow=' . $bwidx
4208
					))->removeClass('btn-primary')->addClass('btn-danger btn-sm');
4209

    
4210
					if($row == $numrows)
4211
						$group->setHelp('Bandwidth is the rate (e.g. Mbit/s) to which traffic in this limiter will be restricted.');
4212

    
4213
					$section->add($group);
4214
					$row++;
4215
				}
4216
			}
4217
		else { // The $bandwidth array exists, but is empty
4218
			$section->addInput(new Form_StaticText(
4219
				'Bandwidth',
4220
				'No schedules configured for this limiter.'
4221
			));
4222
		}
4223

    
4224
		$section->addInput(new Form_Button(
4225
			'addsched',
4226
			'Add new schedule',
4227
			'firewall_shaper_vinterface.php?pipe=' . $pipe . '&queue=' . $qname . '&action=' . $action . '&newbwrow=yes'
4228
			))->removeClass('btn-primary')->addClass('btn-success btn-sm');
4229
		}
4230

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

    
4233
		$section->addInput(new Form_Select(
4234
			'scheduler',
4235
			'Mask',
4236
			$mask['type'],
4237
			array('none' => 'None', 'srcaddress' => 'Source addresses', 'dstaddress' => '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 />' . '<font face="consolas">ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/?</font>');
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
// List of layer7 objects
4340
$layer7_rules_list = array();
4341

    
4342
class layer7 {
4343

    
4344
	var $rname; //alias
4345
	var $rdescription; //alias description
4346
	var $rport; //divert port
4347
	var $renabled; //rule enabled
4348
	var $rsets = array(); //array of l7 associations
4349

    
4350
	// Auxiliary functions
4351

    
4352
	function GetRName() {
4353
		return $this->rname;
4354
	}
4355
	function SetRName($rname) {
4356
		$this->rname = $rname;
4357
	}
4358
	function GetRDescription() {
4359
		return $this->rdescription;
4360
	}
4361
	function SetRDescription($rdescription) {
4362
		$this->rdescription = $rdescription;
4363
	}
4364
	function GetRPort() {
4365
		return $this->rport;
4366
	}
4367
	function SetRPort($rport) {
4368
		$this->rport = $rport;
4369
	}
4370
	function GetREnabled() {
4371
		return $this->renabled;
4372
	}
4373
	function SetREnabled($value) {
4374
		$this->renabled = $value;
4375
	}
4376
	function GetRl7() {
4377
		return $this->rsets;
4378
	}
4379
	function SetRl7($rsets) {
4380
		$this->rsets = $rsets;
4381
	}
4382

    
4383
	//Add a tuple (rule,structure,element) to the $rsets
4384

    
4385
	function add_rule($l7set) {
4386
		$this->rsets[] = $l7set;
4387
	}
4388

    
4389
	// Build the layer7 rules
4390
	function build_l7_rules() {
4391
		if ($this->GetREnabled() == "") {
4392
			return;
4393
		}
4394
		//$l7rules = "#" . $this->rdescription . "\n";
4395
		foreach ($this->rsets as $rl7) {
4396
			$l7rules .= $rl7->build_rules();
4397
		}
4398
		return $l7rules;
4399
	}
4400

    
4401
	// Read the config from array
4402
	function ReadConfig(&$qname, &$q) {
4403
		$this->SetRName($qname);
4404
		$this->SetREnabled($q['enabled']);
4405
		$this->SetRPort($q['divert_port']);
4406
		if (isset($q['description']) && $q['description'] <> "") {
4407
			$this->SetRDescription($q['description']);
4408
		}
4409
		$rsets = $q['l7rules'];
4410
		//Put individual rules in the array
4411
		if (is_array($rsets)) {
4412
			$this->rsets = array(); // XXX: ugly hack
4413
			foreach ($rsets as $l7r) {
4414
				$l7obj = new l7rule();
4415
				$l7obj->SetRProtocol($l7r['protocol']);
4416
				$l7obj->SetRStructure($l7r['structure']);
4417
				$l7obj->SetRBehaviour($l7r['behaviour']);
4418
				$this->add_rule($l7obj);
4419
			}
4420
		}
4421
	}
4422

    
4423
	//Generate a random port for the divert socket
4424
	function gen_divert_port() {
4425
		$dports = get_divert_ports(); //array of used ports
4426
		$divert_port = 1; // Initialize
4427
		while (($divert_port % 2) != 0 || in_array($divert_port, $dports)) {
4428
			$divert_port = rand(40000, 60000);
4429
		}
4430
		return $divert_port;
4431
	}
4432

    
4433
	//Helps building the left tree
4434
	function build_tree() {
4435
		$tree = " <li><a href=\"firewall_shaper_layer7.php?container=" . $this->GetRName() ."&amp;action=show\">";
4436
		$tree .= $this->GetRName() . "</a>";
4437
		$tree .= "</li>";
4438

    
4439
		return $tree;
4440
	}
4441

    
4442
	function build_form() {
4443

    
4444
		$form = new Form(new Form_Button(
4445
			'Submit',
4446
			'Save'
4447
		));
4448

    
4449
		$section = new Form_Section('Traffic Shaper');
4450

    
4451
		$section->addInput(new Form_Checkbox(
4452
			'enabled',
4453
			'Enable/Disable',
4454
			'Enable/disable discipline and its children',
4455
			($this->GetREnabled() == "on"),
4456
			'on'
4457
		));
4458

    
4459
		$section->addInput(new Form_Input(
4460
			'container',
4461
			'Name',
4462
			'text',
4463
			$this->GetRName()
4464
		));
4465

    
4466
		$section->addInput(new Form_Input(
4467
			'description',
4468
			'Description',
4469
			'text',
4470
			$this->GetRDescription()
4471
		))->setHelp('You may enter a description here for your reference (not parsed).');
4472

    
4473
		$form->add($section);
4474

    
4475
		return $form;
4476
	}
4477

    
4478
	//Write the setting to the $config array
4479
	function wconfig() {
4480
		global $config;
4481

    
4482
		if (!is_array($config['l7shaper']['container'])) {
4483
			$config['l7shaper']['container'] = array();
4484
		}
4485
		//
4486
		$cflink =& get_l7c_reference_to_me_in_config($this->GetRName());
4487
		// Test if this rule exists already
4488
		if (!$cflink) {
4489
			$cflink =& $config['l7shaper']['container'][];
4490
		}
4491
		$cflink['name'] = $this->GetRName();
4492
		$cflink['enabled'] = $this->GetREnabled();
4493
		$cflink['description'] = $this->GetRDescription();
4494
		$cflink['divert_port'] = $this->GetRPort();
4495

    
4496
		// Destroy previously existent rules
4497
		if (is_array($cflink['rules'])) {
4498
			unset($cflink['l7rules']);
4499
		}
4500

    
4501
		$cflink['l7rules'] = array();
4502

    
4503
		$i = 0;
4504
		foreach ($this->rsets as $rulel7) {
4505
			$cflink['l7rules'][$i]['protocol'] = $rulel7->GetRProtocol();
4506
			$cflink['l7rules'][$i]['structure'] = $rulel7->GetRStructure();
4507
			$cflink['l7rules'][$i]['behaviour'] = $rulel7->GetRBehaviour();
4508
			$i++;
4509
		}
4510
	}
4511

    
4512
	//This function is necessary to help producing the overload options for keep state
4513
	function get_unique_structures() {
4514

    
4515
		$unique_structures = array("action" => false, "dummynet" => false, "altq" => false);
4516
		foreach ($this->rsets as $l7rule) {
4517
			if ($l7rule->GetRStructure() == "action") {
4518
				$unique_structures['action'] = true;
4519
			} else if ($l7rule->GetRStructure() == "limiter") {
4520
				$unique_structures['dummynet'] = true;
4521
			} else {
4522
				$unique_structures['altq'] = true;
4523
			}
4524
		}
4525
		//Delete non used structures so we don't have to check this in filter.inc
4526
		foreach ($unique_structures as $key => $value) {
4527
			if (!$value) {
4528
				unset($unique_structures[$key]);
4529
			}
4530
		}
4531
		return $unique_structures;
4532
	}
4533

    
4534
	function validate_input($data, &$input_errors) {
4535
		$reqdfields[] = "container";
4536
		$reqdfieldsn[] = gettext("Name");
4537

    
4538
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
4539

    
4540
		if (!preg_match("/^[a-zA-Z0-9_-]+$/", $data['container'])) {
4541
			$input_errors[] = gettext("Queue names must be alphanumeric and _ or - only.");
4542
		}
4543
	}
4544

    
4545
	function delete_l7c() {
4546
		mwexec("/bin/pkill -f 'ipfw-classifyd .* -p ". $this->GetRPort() . "'", true);
4547
		unset_l7_object_by_reference($this->GetRName());
4548
		cleanup_l7_from_rules($this->GetRName());
4549
	}
4550
}
4551

    
4552
class l7rule {
4553

    
4554
	var $rprotocol; //protocol
4555
	var $rstructure; //action, limiter, queue
4556
	var $rbehaviour; //allow, block, queue_name, pipe_number ...
4557

    
4558
	//Auxiliary Functions
4559

    
4560
	function GetRProtocol() {
4561
		return $this->rprotocol;
4562
	}
4563
	function SetRProtocol($rprotocol) {
4564
		$this->rprotocol = $rprotocol;
4565
	}
4566
	function GetRStructure() {
4567
		return $this->rstructure;
4568
	}
4569
	function SetRStructure($rstructure) {
4570
		$this->rstructure = $rstructure;
4571
	}
4572
	function GetRBehaviour() {
4573
		return $this->rbehaviour;
4574
	}
4575
	function SetRBehaviour($rbehaviour) {
4576
		$this->rbehaviour = $rbehaviour;
4577
	}
4578

    
4579
	//XXX Do we need to test any particularity for AltQ queues?
4580
	function build_rules() {
4581
		global $dummynet_pipe_list;
4582
		switch ($this->GetRStructure()) {
4583
		case "limiter":
4584
			read_dummynet_config();
4585
			$dn_list =& get_unique_dnqueue_list();
4586
			$found = false;
4587
			if (is_array($dn_list)) {
4588
				foreach ($dn_list as $key => $value) {
4589
					if ($key == $this->GetRBehaviour()) {
4590
						if ($value[0] == "?") {
4591
							$l7rule = $this->GetRProtocol() . " = dnqueue " . substr($value, 1) . "\n";
4592
						} else {
4593
							$l7rule = $this->GetRProtocol() . " = dnpipe " . $value . "\n";
4594
						}
4595
						$found = true;
4596
					}
4597
					if ($found) {
4598
						break;
4599
					}
4600
				}
4601
			}
4602
			break;
4603
		default: //This is for action and for altq
4604
			$l7rule = $this->GetRProtocol() . " = " . $this->GetRStructure() . " " . $this->GetRBehaviour() . "\n";
4605
			break;
4606
		}
4607
		return $l7rule;
4608
	}
4609
}
4610

    
4611
/*
4612
 * This function allows to return an array with all the used divert socket ports
4613
 */
4614
function get_divert_ports() {
4615
	global $layer7_rules_list;
4616
	$dports = array();
4617

    
4618
	foreach ($layer7_rules_list as $l7r) {
4619
		$dports[] = $l7r->GetRPort();
4620
	}
4621

    
4622
	return $dports;
4623
}
4624

    
4625
function &get_l7c_reference_to_me_in_config(&$name) {
4626
	global $config;
4627

    
4628
	$ptr = NULL;
4629

    
4630
	if (is_array($config['l7shaper']['container'])) {
4631
		foreach ($config['l7shaper']['container'] as $key => $value) {
4632
			if ($value['name'] == $name) {
4633
				$ptr =& $config['l7shaper']['container'][$key];
4634
			}
4635
		}
4636
	}
4637
	return $ptr;
4638
	// $ptr can be null. has to be checked later
4639
}
4640

    
4641
function unset_l7_object_by_reference(&$name) {
4642
	global $config;
4643

    
4644
	if (is_array($config['l7shaper']['container'])) {
4645
		foreach ($config['l7shaper']['container'] as $key => $value) {
4646
			if ($value['name'] == $name) {
4647
				unset($config['l7shaper']['container'][$key]['l7rules']);
4648
				unset($config['l7shaper']['container'][$key]);
4649
				break;
4650
			}
4651
		}
4652
	}
4653
}
4654

    
4655
function read_layer7_config() {
4656
	global $layer7_rules_list, $config;
4657

    
4658
	if (!is_array($config['l7shaper']['container']) || !count($config['l7shaper']['container'])) {
4659
		$layer7_rules_list = array();
4660
		return;
4661
	}
4662

    
4663
	$l7cs = &$config['l7shaper']['container'];
4664

    
4665
	$layer7_rules_list = array();
4666

    
4667
	foreach ($l7cs as $conf) {
4668
		if (empty($conf['name'])) {
4669
			continue; /* XXX: grrrrrr at php */
4670
		}
4671
		$root =& new layer7();
4672
		$root->ReadConfig($conf['name'], $conf);
4673
		$layer7_rules_list[$root->GetRName()] = &$root;
4674
	}
4675
}
4676

    
4677
function update_layer7_custom_patterns() {
4678
	global $config;
4679

    
4680
	if (!is_array($config['l7shaper']['custom_pat'])) {
4681
		return;
4682
	}
4683

    
4684
	foreach ($config['l7shaper']['custom_pat'] as $filename => $filecontent) {
4685
		if (!file_exists("/usr/local/share/protocols/" . $filename)) {
4686
			@file_put_contents("/usr/local/share/protocols/" . $filename, base64_decode($filecontent));
4687
		}
4688
	}
4689
}
4690

    
4691
function generate_layer7_files() {
4692
	global $layer7_rules_list, $g;
4693

    
4694
	read_layer7_config();
4695

    
4696
	if (!empty($layer7_rules_list)) {
4697
		if (!is_module_loaded("ipdivert.ko")) {
4698
			mwexec("/sbin/kldload ipdivert.ko");
4699
		}
4700

    
4701
		array_map('unlink', glob("{$g['tmp_path']}/*.l7"));
4702
	}
4703

    
4704
	update_layer7_custom_patterns();
4705

    
4706
	foreach ($layer7_rules_list as $l7rules) {
4707
		if ($l7rules->GetREnabled()) {
4708
			$filename = $l7rules->GetRName() . ".l7";
4709
			$path = "{$g['tmp_path']}/" . $filename;
4710

    
4711
			$rules = $l7rules->build_l7_rules();
4712

    
4713
			$fp = fopen($path, 'w');
4714
			fwrite($fp, $rules);
4715
			fclose($fp);
4716
		}
4717
	}
4718
}
4719

    
4720
function layer7_start_l7daemon() {
4721
	global $layer7_rules_list, $g;
4722

    
4723
	/*
4724
	 * XXX: ermal - Needed ?!
4725
	 * read_layer7_config();
4726
	 */
4727

    
4728
	foreach ($layer7_rules_list as $l7rules) {
4729
		if ($l7rules->GetREnabled()) {
4730
			$filename = $l7rules->GetRName() . ".l7";
4731
			$path = "{$g['tmp_path']}/" . $filename;
4732

    
4733
			unset($l7pid);
4734
			/* Only reread the configuration rather than restart to avoid losing information. */
4735
			exec("/bin/pgrep -f 'ipfw-classifyd .* -p ". $l7rules->GetRPort() . "'", $l7pid);
4736
			if (count($l7pid) > 0) {
4737
				log_error(sprintf(gettext("Sending HUP signal to %s"), $l7pid[0]));
4738
				mwexec("/bin/kill -HUP {$l7pid[0]}");
4739
			} else {
4740
				// XXX: Hardcoded number of packets to garbage collect and queue length.
4741
				$ipfw_classifyd_init = "/usr/local/sbin/ipfw-classifyd -n 8 -q 700 -c {$path} -p " . $l7rules->GetRPort() . " -P /usr/local/share/protocols";
4742
				mwexec_bg($ipfw_classifyd_init);
4743
			}
4744
		}
4745
	}
4746
}
4747

    
4748
// This function uses /usr/local/share/protocols as a default directory for searching .pat files
4749
function generate_protocols_array() {
4750

    
4751
	update_layer7_custom_patterns();
4752

    
4753
	$protocols = return_dir_as_array("/usr/local/share/protocols");
4754
	$protocols_new = array();
4755
	if (is_array($protocols)) {
4756
		foreach ($protocols as $key => $proto) {
4757
			if (strstr($proto, ".pat")) {
4758
				$protocols_new[$key] =& str_replace(".pat", "", $proto);
4759
			}
4760
		}
4761
		sort($protocols_new);
4762
	}
4763
	return $protocols_new;
4764
}
4765

    
4766
function get_l7_unique_list() {
4767
	global $layer7_rules_list;
4768

    
4769
	$l7list = array();
4770
	if (is_array($layer7_rules_list)) {
4771
		foreach ($layer7_rules_list as $l7c) {
4772
			if ($l7c->GetREnabled()) {
4773
				$l7list[] = $l7c->GetRName();
4774
			}
4775
		}
4776
	}
4777

    
4778
	return $l7list;
4779
}
4780

    
4781
// Disable a removed l7 container from the filter
4782
function cleanup_l7_from_rules(&$name) {
4783
	global $config;
4784

    
4785
	if (is_array($config['filter']['rule'])) {
4786
		foreach ($config['filter']['rule'] as $key => $rule) {
4787
			if ($rule['l7container'] == $name) {
4788
				unset($config['filter']['rule'][$key]['l7container']);
4789
			}
4790
		}
4791
	}
4792
}
4793

    
4794
function get_dummynet_name_list() {
4795

    
4796
	$dn_name_list =& get_unique_dnqueue_list();
4797
	$dn_name = array();
4798
	if (is_array($dn_name_list)) {
4799
		foreach ($dn_name_list as $key => $value) {
4800
			$dn_name[] = $key;
4801
		}
4802
	}
4803

    
4804
	return $dn_name;
4805

    
4806
}
4807

    
4808
function get_altq_name_list() {
4809
	$altq_name_list =& get_unique_queue_list();
4810
	$altq_name = array();
4811
	if (is_array($altq_name_list)) {
4812
		foreach ($altq_name_list as $key => $aqobj) {
4813
			$altq_name[] = $key;
4814
		}
4815
	}
4816

    
4817
	return $altq_name;
4818
}
4819

    
4820
/*
4821
 * XXX: TODO Make a class shaper to hide all these functions
4822
 * from the global namespace.
4823
 */
4824

    
4825
/*
4826
 * This is a layer violation but for now there is no way
4827
 * I can find to properly do this with PHP.
4828
 */
4829
function altq_get_default_queue($interface) {
4830
	global $altq_list_queues;
4831

    
4832
	$altq_tmp = $altq_list_queues[$interface];
4833
	if ($altq_tmp) {
4834
		return $altq_tmp->GetDefaultQueuePresent();
4835
	} else {
4836
		return false;
4837
	}
4838
}
4839

    
4840
function altq_check_default_queues() {
4841
	global $altq_list_queues;
4842

    
4843
	$count = 0;
4844
	if (is_array($altq_list_queues)) {
4845
		foreach ($altq_list_queues as $altq) {
4846
			if ($altq->GetDefaultQueuePresent()) {
4847
				$count++;
4848
			}
4849
		}
4850
	}
4851
	else {
4852
		$count++;
4853
	}
4854

    
4855
	return 0;
4856
}
4857

    
4858
function &get_unique_queue_list() {
4859
	global $altq_list_queues;
4860

    
4861
	$qlist = array();
4862
	if (is_array($altq_list_queues)) {
4863
		foreach ($altq_list_queues as $altq) {
4864
			if ($altq->GetEnabled() == "") {
4865
				continue;
4866
			}
4867
			$tmplist =& $altq->get_queue_list();
4868
			foreach ($tmplist as $qname => $link) {
4869
				if ($link->GetEnabled() <> "") {
4870
					$qlist[$qname] = $link;
4871
				}
4872
			}
4873
		}
4874
	}
4875
	return $qlist;
4876
}
4877

    
4878
function &get_unique_dnqueue_list() {
4879
	global $dummynet_pipe_list;
4880

    
4881
	$qlist = array();
4882
	if (is_array($dummynet_pipe_list)) {
4883
		foreach ($dummynet_pipe_list as $dn) {
4884
			if ($dn->GetEnabled() == "") {
4885
				continue;
4886
			}
4887
			$tmplist =& $dn->get_queue_list();
4888
			foreach ($tmplist as $qname => $link) {
4889
				$qlist[$qname] = $link;
4890
			}
4891
		}
4892
	}
4893
	return $qlist;
4894
}
4895

    
4896
function ref_on_altq_queue_list($parent, $qname) {
4897
	if (isset($GLOBALS['queue_list'][$qname])) {
4898
		$GLOBALS['queue_list'][$qname]++;
4899
	} else {
4900
		$GLOBALS['queue_list'][$qname] = 1;
4901
	}
4902

    
4903
	unref_on_altq_queue_list($parent);
4904
}
4905

    
4906
function unref_on_altq_queue_list($qname) {
4907
	$GLOBALS['queue_list'][$qname]--;
4908
	if ($GLOBALS['queue_list'][$qname] <= 1) {
4909
		unset($GLOBALS['queue_list'][$qname]);
4910
	}
4911
}
4912

    
4913
function read_altq_config() {
4914
	global $altq_list_queues, $config;
4915
	$path = array();
4916

    
4917
	if (!is_array($config['shaper'])) {
4918
		$config['shaper'] = array();
4919
	}
4920
	if (!is_array($config['shaper']['queue'])) {
4921
		$config['shaper']['queue'] = array();
4922
	}
4923
	$a_int = &$config['shaper']['queue'];
4924

    
4925
	$altq_list_queues = array();
4926

    
4927
	if (!is_array($config['shaper']['queue'])) {
4928
		return;
4929
	}
4930

    
4931
	foreach ($a_int as $key => $conf) {
4932
		$int = $conf['interface'];
4933
		$root =& new altq_root_queue();
4934
		$root->SetInterface($int);
4935
		$altq_list_queues[$root->GetInterface()] = &$root;
4936
		$root->ReadConfig($conf);
4937
		array_push($path, $key);
4938
		$root->SetLink($path);
4939
		if (is_array($conf['queue'])) {
4940
			foreach ($conf['queue'] as $key1 => $q) {
4941
				array_push($path, $key1);
4942
				/*
4943
				 * XXX: we completely ignore errors here but anyway we must have
4944
				 *	checked them before so no harm should be come from this.
4945
				 */
4946
				$root->add_queue($root->GetInterface(), $q, $path, $input_errors);
4947
				array_pop($path);
4948
			}
4949
		}
4950
		array_pop($path);
4951
	}
4952
}
4953

    
4954
function read_dummynet_config() {
4955
	global $dummynet_pipe_list, $config;
4956
	$path = array();
4957

    
4958
	if (!is_array($config['dnshaper'])) {
4959
		$config['dnshaper'] = array();
4960
	}
4961
	if (!is_array($config['dnshaper']['queue'])) {
4962
		$config['dnshaper']['queue'] = array();
4963
	}
4964
	$a_int = &$config['dnshaper']['queue'];
4965

    
4966
	$dummynet_pipe_list = array();
4967

    
4968
	if (!is_array($config['dnshaper']['queue']) ||
4969
	    !count($config['dnshaper']['queue'])) {
4970
		return;
4971
	}
4972

    
4973
	foreach ($a_int as $key => $conf) {
4974
		if (empty($conf['name'])) {
4975
			continue; /* XXX: grrrrrr at php */
4976
		}
4977
		$root =& new dnpipe_class();
4978
		$root->ReadConfig($conf);
4979
		$dummynet_pipe_list[$root->GetQname()] = &$root;
4980
		array_push($path, $key);
4981
		$root->SetLink($path);
4982
		if (is_array($conf['queue'])) {
4983
			foreach ($conf['queue'] as $key1 => $q) {
4984
				array_push($path, $key1);
4985
				/*
4986
				 * XXX: we completely ignore errors here but anyway we must have
4987
				 *	checked them before so no harm should be come from this.
4988
				 */
4989
				$root->add_queue($root->GetQname(), $q, $path, $input_errors);
4990
				array_pop($path);
4991
			}
4992
		}
4993
		array_pop($path);
4994
	}
4995
}
4996

    
4997
function get_interface_list_to_show() {
4998
	global $altq_list_queues, $config;
4999
	global $shaperIFlist;
5000

    
5001
	$tree = "";
5002
	foreach ($shaperIFlist as $shif => $shDescr) {
5003
		if ($altq_list_queues[$shif]) {
5004
			continue;
5005
		} else {
5006
			if (!is_altq_capable(get_real_interface($shif))) {
5007
				continue;
5008
			}
5009
			$tree .= " <li><a href=\"firewall_shaper.php?interface=".$shif."&amp;action=add\">".$shDescr."</a></li>";
5010
		}
5011
	}
5012

    
5013
	return $tree;
5014
}
5015

    
5016
function filter_generate_altq_queues() {
5017
	global $altq_list_queues;
5018

    
5019
	read_altq_config();
5020

    
5021
	$altq_rules = "";
5022
	foreach ($altq_list_queues as $altq) {
5023
		$altq_rules .= $altq->build_rules();
5024
	}
5025

    
5026
	return $altq_rules;
5027
}
5028

    
5029
function dnqueue_find_nextnumber() {
5030
	global $dummynet_pipe_list;
5031

    
5032
	$dnused = array();
5033
	if (is_array($dummynet_pipe_list)) {
5034
		foreach ($dummynet_pipe_list as $dn) {
5035
			$tmplist =& $dn->get_queue_list();
5036
			foreach ($tmplist as $qname => $link) {
5037
				if ($link[0] == "?") {
5038
					$dnused[$qname] = substr($link, 1);
5039
				}
5040
			}
5041
		}
5042
	}
5043

    
5044
	sort($dnused, SORT_NUMERIC);
5045
	$dnnumber = 0;
5046
	$found = false;
5047
	foreach ($dnused as $dnnum) {
5048
		if (($dnnum - $dnnumber) > 1) {
5049
			$dnnumber = $dnnum - 1;
5050
			$found = true;
5051
			break;
5052
		} else {
5053
			$dnnumber = $dnnum;
5054
		}
5055
	}
5056

    
5057
	if ($found == false) {
5058
		$dnnumber++;
5059
	}
5060

    
5061
	unset($dnused, $dnnum, $found);
5062
	return $dnnumber;
5063
}
5064

    
5065
function dnpipe_find_nextnumber() {
5066
	global $dummynet_pipe_list;
5067

    
5068
	$dnused = array();
5069
	foreach ($dummynet_pipe_list as $dn) {
5070
		$dnused[] = $dn->GetNumber();
5071
	}
5072

    
5073
	sort($dnused, SORT_NUMERIC);
5074
	$dnnumber = 0;
5075
	$found = false;
5076
	foreach ($dnused as $dnnum) {
5077
		if (($dnnum - $dnnumber) > 1) {
5078
			$dnnumber = $dnnum - 1;
5079
			$found = true;
5080
			break;
5081
		} else {
5082
			$dnnumber = $dnnum;
5083
		}
5084
	}
5085

    
5086
	if ($found == false) {
5087
		$dnnumber++;
5088
	}
5089

    
5090
	unset($dnused, $dnnum, $found);
5091
	return $dnnumber;
5092
}
5093

    
5094
function filter_generate_dummynet_rules() {
5095
	global $g, $dummynet_pipe_list;
5096

    
5097
	read_dummynet_config();
5098

    
5099
	$dn_rules = "";
5100
	foreach ($dummynet_pipe_list as $dn) {
5101
		$dn_rules .= $dn->build_rules();
5102
	}
5103

    
5104
	if (!empty($dn_rules)) {
5105
		if (!is_module_loaded("dummynet.ko")) {
5106
			mwexec("/sbin/kldload dummynet");
5107
			set_sysctl(array(
5108
				"net.inet.ip.dummynet.io_fast" => "1",
5109
				"net.inet.ip.dummynet.hash_size" => "256"
5110
			));
5111
		}
5112
		file_put_contents("{$g['tmp_path']}/rules.limiter", $dn_rules);
5113
		mwexec("/sbin/ipfw {$g['tmp_path']}/rules.limiter");
5114
	}
5115
}
5116

    
5117
function build_iface_without_this_queue($iface, $qname) {
5118
	global $g, $altq_list_queues;
5119
	global $shaperIFlist;
5120

    
5121
	$altq =& $altq_list_queues[$iface];
5122

    
5123
	if ($altq) {
5124
		$scheduler = $altq->GetScheduler();
5125
	}
5126

    
5127
	$form = '<dl class="dl-horizontal">';
5128

    
5129
	$form .= '	<dt>';
5130
	$form .= '		<a href="firewall_shaper.php?interface=' . $iface . '&amp;queue=' . $iface . '&amp;action=show">' . $shaperIFlist[$iface] . '</a>';
5131
	$form .= '	</dt>';
5132
	$form .= '	<dd>';
5133
	$form .=		$scheduler;
5134
	$form .= '	</dd>';
5135

    
5136
	$form .= '	<dt>';
5137
	$form .= 'Clone';
5138
	$form .= '	</dt>';
5139
	$form .= '	<dd>';
5140
	$form .= '<a class="btn btn-info btn-xs" href="firewall_shaper_queues.php?interface=';
5141
	$form .= $iface . '&amp;queue=';
5142
	$form .= $qname . '&amp;action=add">';
5143
	$form .= gettext("Clone shaper on the I/F") . '</a>';
5144
	$form .= '	</dd>';
5145

    
5146
	$form .= '</dl>';
5147

    
5148
	return $form;
5149

    
5150
}
5151

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

    
5156
$dn_default_shaper_msg = $default_shaper_msg;
5157

    
5158
?>
(51-51/68)