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

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

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

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

    
77
	return $ptr;
78
}
79

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

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

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

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

    
98
	return $ptr;
99
}
100

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
386
	function validate_input($data, &$input_errors) {
387

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

    
393
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
394

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

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

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

    
468
	function &get_queue_list(&$q = null) {
469
		$qlist = array();
470

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

    
480
	function &add_queue($interface, &$queue, &$path, &$input_errors) {
481

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

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

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

    
537
		return $q;
538
	}
539

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

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

    
568
	function build_tree() {
569
		global $shaperIFlist;
570

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

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

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

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

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

    
672
			$rules .= " queue";
673
		}
674

    
675
		$rules .= " \n";
676
		return $rules;
677
	}
678

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

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

    
698
		return $javascript;
699
	}
700

    
701
	function build_shortform() {
702
		global $g;
703

    
704
		$altq =& $this;
705

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

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

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

    
725
		$form .= '	<dt>';
726
		$form .= 'Disable';
727
		$form .= '	<dt>';
728
		$form .= '	<dd>';
729

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

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

    
737
		$form .= '</dl>';
738

    
739
		return $form;
740

    
741
	}
742

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

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

    
754
		$section = new Form_Section(null);
755

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

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

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

    
780
		$group = new Form_group('Bandwidth');
781

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

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

    
800
		$section->add($group);
801

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

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

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

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

    
831
		$sform->add($section);
832

    
833
		return($sform);
834
	}
835

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

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

    
868
}
869

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

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

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

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

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

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

    
1042
		return $javascript;
1043
	}
1044

    
1045
	function &add_queue($interface, &$qname, &$path, &$input_errors) { return; }
1046

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

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

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

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

    
1083
	function &get_queue_list(&$qlist) {
1084

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

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

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

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

    
1116
	function find_parentqueue($interface, $qname) { return; }
1117

    
1118
	function validate_input($data, &$input_errors) {
1119

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

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

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

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

    
1233
		$tree .= "</li>";
1234

    
1235
		return $tree;
1236
	}
1237

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

    
1296
		$pfq_rule .= " \n";
1297

    
1298
		return $pfq_rule;
1299
	}
1300

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

    
1309
	function build_form() {
1310

    
1311
		$sform = new Form();
1312

    
1313
		$section = new Form_Section(null);
1314

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

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

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

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

    
1343
		$group = new Form_Group('Scheduler options');
1344

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

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

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

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

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

    
1382
		$group->setHelp('Select options for this queue');
1383

    
1384
		$section->add($group);
1385

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

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

    
1400
		$sform->add($section);
1401

    
1402
		return($sform);
1403
	}
1404

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

    
1410
		$altq =& $altq_list_queues[$this->GetInterface()];
1411

    
1412
		if ($altq) {
1413
			$scheduler = $altq->GetScheduler();
1414
		}
1415

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

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

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

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

    
1451
			$form .= '	<dt>';
1452
			$form .= 'Delete';
1453
			$form .= '	<dt>';
1454
			$form .= '	<dd>';
1455

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

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

    
1463
			$form .= '</dl>';
1464

    
1465
		return $form;
1466

    
1467
	}
1468

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

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

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

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

    
1624
	function &add_queue($interface, &$qname, &$path, &$input_errors) {
1625

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

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

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

    
1662
		return $q;
1663
	}
1664

    
1665
	function copy_queue($interface, &$cflink) {
1666

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

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

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

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

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

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

    
1806
	function validate_input($data, &$input_errors) {
1807
		parent::validate_input($data, $input_errors);
1808

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

    
1814
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
1815

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
2117
		$pfq_rule .= " \n";
2118

    
2119
		return $pfq_rule;
2120
	}
2121

    
2122
	function build_javascript() {
2123

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

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

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

    
2141
		$('#upperlimit').click(function () {
2142
			enable_upperlimit();
2143
		});
2144

    
2145
		enable_upperlimit();
2146

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

    
2154
		$('#realtime').click(function () {
2155
			enable_realtime();
2156
		});
2157

    
2158
		enable_realtime();
2159

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

    
2167
		$('#linkshare').click(function () {
2168
			enable_linkshare();
2169
		});
2170

    
2171
		enable_linkshare();
2172
	});
2173
//]]>
2174
</script>
2175
EOJS;
2176

    
2177
		return $javascript;
2178
	}
2179

    
2180
	function build_form() {
2181

    
2182
		$sform = parent::build_form();
2183

    
2184
		$section = new Form_Section('Service Curve (sc)');
2185

    
2186
		$group = new Form_Group('Bandwidth');
2187

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

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

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

    
2208
		$section->add($group);
2209

    
2210
		$group = new Form_Group('Max bandwidth for queue.');
2211

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

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

    
2226
		$group->add(new Form_Input(
2227
			'upperlimit2',
2228
			null,
2229
			'text',
2230
			$this->GetU_d()
2231
		))->setHelp('d');
2232

    
2233
		$group->add(new Form_Input(
2234
			'upperlimit3',
2235
			null,
2236
			'text',
2237
			$this->GetU_m2()
2238
		))->setHelp('m2');
2239

    
2240

    
2241
		$section->add($group);
2242

    
2243
		$group = new Form_Group('Min bandwidth for queue.');
2244

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

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

    
2259
		$group->add(new Form_Input(
2260
			'realtime2',
2261
			null,
2262
			'text',
2263
			$this->GetR_d()
2264
		))->setHelp('d');
2265

    
2266
		$group->add(new Form_Input(
2267
			'realtime3',
2268
			null,
2269
			'text',
2270
			$this->GetR_m2()
2271
		))->setHelp('m2');
2272

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

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

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

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

    
2291
		$group->add(new Form_Input(
2292
			'linkshare2',
2293
			null,
2294
			'text',
2295
			$this->GetL_d()
2296
		))->setHelp('d');
2297

    
2298
		$group->add(new Form_Input(
2299
			'linkshare3',
2300
			null,
2301
			'text',
2302
			$this->GetL_m2()
2303
		))->setHelp('m2');
2304

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

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

    
2312
		$sform->add($section);
2313

    
2314
		return($sform);
2315
	}
2316

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

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

    
2438
class cbq_queue extends priq_queue {
2439
	var $qborrow = "";
2440

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

    
2451
	function &add_queue($interface, &$qname, &$path, &$input_errors) {
2452

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

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

    
2488
		return $q;
2489
	}
2490

    
2491
	function copy_queue($interface, &$cflink) {
2492

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

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

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

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

    
2579
	function validate_input($data, &$input_errors) {
2580
		parent::validate_input($data, $input_errors);
2581

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

    
2590
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
2591

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

    
2596

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

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

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

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

    
2632
	function build_javascript() {
2633
		return parent::build_javascript();
2634
	}
2635

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

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

    
2735
		$pfq_rule .= " \n";
2736
		return $pfq_rule;
2737
	}
2738

    
2739
	function build_form() {
2740
		$sform = parent::build_form();
2741

    
2742
		$section = new Form_Section('');
2743

    
2744
		$group = new Form_Group('Bandwidth');
2745

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

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

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

    
2766
		$section->add($group);
2767

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

    
2775
		return $sform;
2776
	}
2777

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

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

    
2834
class fairq_queue extends priq_queue {
2835
	var $hogs;
2836
	var $buckets;
2837

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

    
2854

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

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

    
2881
	function find_parentqueue($interface, $qname) { return; }
2882

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

    
2889
	function validate_input($data, &$input_errors) {
2890
		parent::validate_input($data, $input_errors);
2891

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

    
2900
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
2901

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

    
2906

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

    
2911

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

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

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

    
2947
	function build_javascript() {
2948
		return parent::build_javascript();
2949
	}
2950

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

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

    
3036
		$pfq_rule .= " \n";
3037
		return $pfq_rule;
3038
	}
3039

    
3040
	function build_form() {
3041
		$form = parent::build_form();
3042

    
3043
		$section = new Form_Section('');
3044

    
3045
		$group = new Form_Group('Bandwidth');
3046

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

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

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

    
3067
		$section->add($group);
3068

    
3069

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

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

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

    
3148

    
3149
/*
3150
 * dummynet(4) wrappers.
3151
 */
3152

    
3153

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

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

    
3169
	var $buckets;
3170
	/* mask parameters */
3171
	var $mask;
3172
	var $noerror;
3173

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

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

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

    
3280
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
3281

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

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

    
3348
}
3349

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

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

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

    
3388
	function &add_queue($interface, &$queue, &$path, &$input_errors) {
3389

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

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

    
3409
		return $q;
3410
	}
3411

    
3412
	function &get_queue_list(&$q = null) {
3413
		$qlist = array();
3414

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

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

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

    
3443
	function validate_input($data, &$input_errors) {
3444
		parent::validate_input($data, $input_errors);
3445

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

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

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

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

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

    
3560
	}
3561

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

    
3574
		return $tree;
3575
	}
3576

    
3577
	function build_rules() {
3578
		global $config, $time_based_rules;
3579

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

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

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

    
3639
		$pfq_rule .= "\n";
3640

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

    
3648
		return $pfq_rule;
3649
	}
3650

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

    
3655
	function build_javascript() {
3656
		global $g, $config;
3657

    
3658
		$javasr = parent::build_javascript();
3659

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

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

    
3679
	return (function (tableId) {
3680

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

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

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

    
3695
	});
3696
})();
3697

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

    
3705
EOD;
3706

    
3707
		return $javasr;
3708
	}
3709

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

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

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

    
3739
		// If there are no bandwidths defined, make a blank one for convenience
3740
		if(empty($bandwidth))
3741
			$bandwidth = array(0 => array('bw' => '', 'bwscale' => 'Kb', 'bwsched' => 'none'));
3742

    
3743
		if (is_array($bandwidth)) {
3744
			foreach ($bandwidth as $bwidx => $bw) {
3745
				$form .= '<tr>';
3746
				$form .= '<td class="col-xs-4">';
3747
				$form .= "<input class='form-control' type=\"number\" id=\"bandwidth{$bwidx}\" name=\"bandwidth{$bwidx}\" value=\"{$bw['bw']}\" />";
3748
				//$form .= "</td><td width='20%'>";
3749
				//$form .= "<input class='formfld unknown' size='10' type=\"text\" id=\"burst{$bwidx}\" name=\"burst{$bwidx}\" value=\"{$bw['burst']}\" />";
3750
				$form .= "</td>";
3751
				$form .= '<td class="col-xs-4">';
3752
				$form .= "<select id=\"bwtype{$bwidx}\" name=\"bwtype{$bwidx}\" class=\"form-control\">";
3753

    
3754
				foreach (array("Kb" => "Kbit/s", "Mb" => "Mbit/s", "Gb" => "Gbit/s", "b" => "Bit/s") as $bwsidx => $bwscale) {
3755
					$form .= "<option value=\"{$bwsidx}\"";
3756

    
3757
					if ($bw['bwscale'] == $bwsidx) {
3758
						$form .= " selected=\"selected\"";
3759
					}
3760

    
3761
					$form .= ">{$bwscale}</option>";
3762
				}
3763

    
3764
				$form .= "</select>";
3765
				$form .= "</td>";
3766
				$form .= '<td class="col-xs-4">';
3767
				$form .= "<select id=\"bwsched{$bwidx}\" name=\"bwsched{$bwidx}\" class=\"form-control\">";
3768

    
3769
				foreach ($schedules as $schd) {
3770
					$selected = "";
3771
					if ($bw['bwsched'] == $schd) {
3772
						$selected = "selected=\"selected\"";
3773
					}
3774

    
3775
					$form .= "<option value='{$schd}' {$selected}>{$schd}</option>";
3776
				}
3777

    
3778
				$form .= "</select>";
3779
				$form .= "</td>";
3780
				$form .= '<td>';
3781
				$form .= '<a type="button" class="btn btn-default" onclick="removeBwRow(this); return false;">' . gettext('Remove') . '</a>';
3782
				$form .= "</td></tr>";
3783
			}
3784
		}
3785
		$form .= "</tbody></table></div><br />";
3786

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

    
3790
		return($form);
3791
	}
3792

    
3793
	function build_form() {
3794
		global $g, $config, $pipe, $action, $qname;
3795

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

    
3807

    
3808
		$sform = new Form();
3809

    
3810
		$section = new Form_Section('Limiters');
3811

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

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

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

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

    
3843
		$bandwidth = $this->GetBandwidth();
3844

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

    
3849
		// Add a row
3850
//		if($_GET['newbwrow']) {
3851
//			array_push($bandwidth, array(count($bandwidth) => array('bw' => '', 'burst' => '', 'bwscale' => 'Kb', 'bwsched' => 'none') ));
3852
//		}
3853

    
3854
		if (is_array($bandwidth)) {
3855
				$section->addInput(new Form_StaticText(
3856
				'Bandwidth',
3857
				$this->build_bwtable()
3858
			));
3859
		}
3860

    
3861
		$mask = $this->GetMask();
3862

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

    
3872
		$group = new Form_Group(null);
3873

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

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

    
3888
		$section->add($group);
3889

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

    
3897
		$sform->add($section);
3898

    
3899
		$section = new Form_Section('Advanced options');
3900

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

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

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

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

    
3932
		$sform->add($section);
3933

    
3934
		return($sform);
3935
		}
3936

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

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

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

    
3966
}
3967

    
3968
class dnqueue_class extends dummynet_class {
3969
	var $pipeparent;
3970
	var $weight;
3971

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

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

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

    
3996
	function validate_input($data, &$input_errors) {
3997
		parent::validate_input($data, $input_errors);
3998

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

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

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

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

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

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

    
4086
		return $tree;
4087
	}
4088

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

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

    
4108
		return $pfq_rule;
4109
	}
4110

    
4111
	function build_javascript() {
4112
		return parent::build_javascript();
4113
	}
4114

    
4115
	function build_form() {
4116
		global $g, $config, $pipe, $action, $qname;
4117

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

    
4129

    
4130
		$sform = new Form();
4131

    
4132
		$section = new Form_Section('Limiters');
4133

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

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

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

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

    
4165
		$bandwidth = $this->GetBandwidth();
4166

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

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

    
4175
		}
4176

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

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

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

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

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

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

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

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

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

    
4233
		$mask = $this->GetMask();
4234

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

    
4244
		$group = new Form_Group(null);
4245

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

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

    
4260
		$section->add($group);
4261

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

    
4269
		$sform->add($section);
4270

    
4271
		$section = new Form_Section('Advanced options');
4272

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

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

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

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

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

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

    
4315
		return($sform);
4316
	}
4317

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

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

    
4341
// List of layer7 objects
4342
$layer7_rules_list = array();
4343

    
4344
class layer7 {
4345

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

    
4352
	// Auxiliary functions
4353

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

    
4385
	//Add a tuple (rule,structure,element) to the $rsets
4386

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

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

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

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

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

    
4441
		return $tree;
4442
	}
4443

    
4444
	function build_form() {
4445

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

    
4451
		$section = new Form_Section('Traffic Shaper');
4452

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

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

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

    
4475
		$form->add($section);
4476

    
4477
		return $form;
4478
	}
4479

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

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

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

    
4503
		$cflink['l7rules'] = array();
4504

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

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

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

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

    
4540
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
4541

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

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

    
4554
class l7rule {
4555

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

    
4560
	//Auxiliary Functions
4561

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

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

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

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

    
4624
	return $dports;
4625
}
4626

    
4627
function &get_l7c_reference_to_me_in_config(&$name) {
4628
	global $config;
4629

    
4630
	$ptr = NULL;
4631

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

    
4643
function unset_l7_object_by_reference(&$name) {
4644
	global $config;
4645

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

    
4657
function read_layer7_config() {
4658
	global $layer7_rules_list, $config;
4659

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

    
4665
	$l7cs = &$config['l7shaper']['container'];
4666

    
4667
	$layer7_rules_list = array();
4668

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

    
4679
function update_layer7_custom_patterns() {
4680
	global $config;
4681

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

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

    
4693
function generate_layer7_files() {
4694
	global $layer7_rules_list, $g;
4695

    
4696
	read_layer7_config();
4697

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

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

    
4706
	update_layer7_custom_patterns();
4707

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

    
4713
			$rules = $l7rules->build_l7_rules();
4714

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

    
4722
function layer7_start_l7daemon() {
4723
	global $layer7_rules_list, $g;
4724

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

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

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

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

    
4753
	update_layer7_custom_patterns();
4754

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

    
4768
function get_l7_unique_list() {
4769
	global $layer7_rules_list;
4770

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

    
4780
	return $l7list;
4781
}
4782

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

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

    
4796
function get_dummynet_name_list() {
4797

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

    
4806
	return $dn_name;
4807

    
4808
}
4809

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

    
4819
	return $altq_name;
4820
}
4821

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

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

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

    
4842
function altq_check_default_queues() {
4843
	global $altq_list_queues;
4844

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

    
4857
	return 0;
4858
}
4859

    
4860
function &get_unique_queue_list() {
4861
	global $altq_list_queues;
4862

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

    
4880
function &get_unique_dnqueue_list() {
4881
	global $dummynet_pipe_list;
4882

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

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

    
4905
	unref_on_altq_queue_list($parent);
4906
}
4907

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

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

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

    
4927
	$altq_list_queues = array();
4928

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

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

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

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

    
4968
	$dummynet_pipe_list = array();
4969

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

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

    
4999
function get_interface_list_to_show() {
5000
	global $altq_list_queues, $config;
5001
	global $shaperIFlist;
5002

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

    
5015
	return $tree;
5016
}
5017

    
5018
function filter_generate_altq_queues() {
5019
	global $altq_list_queues;
5020

    
5021
	read_altq_config();
5022

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

    
5028
	return $altq_rules;
5029
}
5030

    
5031
function dnqueue_find_nextnumber() {
5032
	global $dummynet_pipe_list;
5033

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

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

    
5059
	if ($found == false) {
5060
		$dnnumber++;
5061
	}
5062

    
5063
	unset($dnused, $dnnum, $found);
5064
	return $dnnumber;
5065
}
5066

    
5067
function dnpipe_find_nextnumber() {
5068
	global $dummynet_pipe_list;
5069

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

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

    
5088
	if ($found == false) {
5089
		$dnnumber++;
5090
	}
5091

    
5092
	unset($dnused, $dnnum, $found);
5093
	return $dnnumber;
5094
}
5095

    
5096
function filter_generate_dummynet_rules() {
5097
	global $g, $dummynet_pipe_list;
5098

    
5099
	read_dummynet_config();
5100

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

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

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

    
5123
	$altq =& $altq_list_queues[$iface];
5124

    
5125
	if ($altq) {
5126
		$scheduler = $altq->GetScheduler();
5127
	}
5128

    
5129
	$form = '<dl class="dl-horizontal">';
5130

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

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

    
5148
	$form .= '</dl>';
5149

    
5150
	return $form;
5151

    
5152
}
5153

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

    
5158
$dn_default_shaper_msg = $default_shaper_msg;
5159

    
5160
?>
(51-51/67)