Project

General

Profile

Download (129 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
	shaper.inc
4
	Copyright (C) 2008 Ermal Luçi
5
	All rights reserved.
6

    
7
	Redistribution and use in source and binary forms, with or without
8
	modification, are permitted provided that the following conditions are met:
9

    
10
	1. Redistributions of source code must retain the above copyright notice,
11
	   this list of conditions and the following disclaimer.
12

    
13
	2. Redistributions in binary form must reproduce the above copyright
14
	   notice, this list of conditions and the following disclaimer in the
15
	   documentation and/or other materials provided with the distribution.
16

    
17
	THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
18
	INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
19
	AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
20
	AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
21
	OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22
	SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23
	INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24
	CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25
	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26
	POSSIBILITY OF SUCH DAMAGE.
27

    
28
	pfSense_BUILDER_BINARIES:	/bin/kill	/sbin/kldload	/bin/rm /bin/ps
29
	pfSense_MODULE: shaper
30
*/
31

    
32
/* XXX: needs some reducing on include. */
33
/* include all configuration functions. */
34
require_once("globals.inc");
35
require_once("functions.inc");
36
require_once("util.inc");
37
require_once("notices.inc");
38

    
39
/*
40
 * I admit :) this is derived from xmlparse.inc StartElement()
41
 */
42
function &get_reference_to_me_in_config(&$mypath) {
43
	global $config;
44

    
45
	$ptr =& $config['shaper'];
46
	foreach ($mypath as $indeks) {
47
		$ptr =& $ptr['queue'][$indeks];
48
	}
49

    
50
	return $ptr;
51
}
52

    
53
function unset_object_by_reference(&$mypath) {
54
	global $config;
55

    
56
	$ptr =& $config['shaper'];
57
	for ($i = 0; $i < count($mypath) - 1; $i++) {
58
		$ptr =& $ptr['queue'][$mypath[$i]];
59
	}
60
	unset($ptr['queue'][$mypath[$i]]);
61
}
62

    
63
function &get_dn_reference_to_me_in_config(&$mypath) {
64
	global $config;
65

    
66
	$ptr =& $config['dnshaper'];
67
	foreach ($mypath as $indeks) {
68
		$ptr =& $ptr['queue'][$indeks];
69
	}
70

    
71
	return $ptr;
72
}
73

    
74
function unset_dn_object_by_reference(&$mypath) {
75
	global $config;
76

    
77
	$ptr =& $config['dnshaper'];
78
	for ($i = 0; $i < count($mypath) - 1; $i++) {
79
		$ptr =& $ptr['queue'][$mypath[$i]];
80
	}
81
	unset($ptr['queue'][$mypath[$i]]);
82
}
83

    
84
function clean_child_queues($type, $mypath) {
85
	$ref = &get_reference_to_me_in_config($mypath);
86

    
87
	switch ($type) {
88
		case 'HFSC':
89
			if (isset($ref['borrow'])) {
90
				unset($ref['borrow']);
91
			}
92
			if (isset($ref['hogs'])) {
93
				unset($ref['hogs']);
94
			}
95
			if (isset($ref['buckets'])) {
96
				unset($ref['buckets']);
97
			}
98
			break;
99
		case 'PRIQ':
100
			if (isset($ref['borrow'])) {
101
				unset($ref['borrow']);
102
			}
103
			if (isset($ref['bandwidth'])) {
104
				unset($ref['bandwidth']);
105
			}
106
			if (isset($ref['bandwidthtype'])) {
107
				unset($ref['bandwidthtype']);
108
			}
109
			/* fall through */
110
		case 'FAIRQ':
111
			if (isset($ref['borrow'])) {
112
				unset($ref['borrow']);
113
			}
114
			/* fall through */
115
		case 'CBQ':
116
			if (isset($ref['realtime'])) {
117
				unset($ref['realtime']);
118
			}
119
			if (isset($ref['realtime1'])) {
120
				unset($ref['realtime1']);
121
			}
122
			if (isset($ref['realtime2'])) {
123
				unset($ref['realtime2']);
124
			}
125
			if (isset($ref['realtime3'])) {
126
				unset($ref['realtime3']);
127
			}
128
			if (isset($ref['upperlimit'])) {
129
				unset($ref['upperlimit']);
130
			}
131
			if (isset($ref['upperlimit1'])) {
132
				unset($ref['upperlimit1']);
133
			}
134
			if (isset($ref['upperlimit2'])) {
135
				unset($ref['upperlimit2']);
136
			}
137
			if (isset($ref['upperlimit3'])) {
138
				unset($ref['upperlimit3']);
139
			}
140
			if (isset($ref['linkshare'])) {
141
				unset($ref['linkshare']);
142
			}
143
			if (isset($ref['linkshare1'])) {
144
				unset($ref['linkshare1']);
145
			}
146
			if (isset($ref['linkshare2'])) {
147
				unset($ref['linkshare2']);
148
			}
149
			if (isset($ref['linkshare3'])) {
150
				unset($ref['linkshare3']);
151
			}
152
			if (isset($ref['hogs'])) {
153
				unset($ref['hogs']);
154
			}
155
			if (isset($ref['buckets'])) {
156
				unset($ref['buckets']);
157
			}
158
			break;
159
	}
160
}
161

    
162
function get_bandwidthtype_scale($type) {
163
	switch ($type) {
164
		case "Gb":
165
			$factor = 1024 * 1024 * 1024;
166
			break;
167
		case "Mb":
168
			$factor = 1024 * 1024;
169
			break;
170
		case "Kb":
171
			$factor = 1024;
172
			break;
173
		case "b":
174
		default:
175
			$factor = 1;
176
			break;
177
	}
178
	return intval($factor);
179
}
180

    
181
function get_hfsc_bandwidth($object, $bw) {
182
	$pattern= "/[0-9]+/";
183
	if (preg_match($pattern, $bw, $match)) {
184
		$bw_1 = $match[1];
185
	} else {
186
		return 0;
187
	}
188
	$pattern= "/(b|Kb|Mb|Gb|%)/";
189
	if (preg_match($pattern, $bw, $match)) {
190
		switch ($match[1]) {
191
			case '%':
192
				$bw_1 = $bw_1 / 100 * get_interface_bandwidth($object);
193
				break;
194
			default:
195
				$bw_1 = $bw_1 * get_bandwidthtype_scale($match[0]);
196
				break;
197
		}
198
		return floatval($bw_1);
199
	} else {
200
		return 0;
201
	}
202
}
203

    
204
function get_interface_bandwidth($object) {
205
	global $altq_list_queues;
206

    
207
	$int = $object->GetInterface();
208
	$altq =& $altq_list_queues[$int];
209
	if ($altq) {
210
		$bw_3 = $altq->GetBandwidth();
211
		$bw_3 = $bw_3 *	 get_bandwidthtype_scale($altq->GetBwscale());
212
		return floatval($bw_3);
213
	} else {
214
		return 0;
215
	}
216
}
217

    
218
/*
219
 * This is duplicated here since we cannot include guiconfig.inc.
220
 * Including it makes all stuff break.
221
 */
222
function shaper_do_input_validation($postdata, $reqdfields, $reqdfieldsn, $input_errors) {
223

    
224
	/* check for bad control characters */
225
	foreach ($postdata as $pn => $pd) {
226
		if (is_string($pd) && preg_match("/[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f]/", $pd)) {
227
			$input_errors[] = sprintf(gettext("The field '%s' contains invalid characters."), $pn);
228
		}
229
	}
230

    
231
	for ($i = 0; $i < count($reqdfields); $i++) {
232
		if ($postdata[$reqdfields[$i]] == "") {
233
			$input_errors[] = sprintf(gettext("The field '%s' is required."), $reqdfieldsn[$i]);
234
		}
235
	}
236
}
237

    
238
function cleanup_queue_from_rules($queue) {
239
	global $config;
240

    
241
	foreach ($config['filter']['rule'] as $rule) {
242
		if ($rule['defaultqueue'] == $queue) {
243
			unset($rule['defaultqueue']);
244
		}
245
		if ($rule['ackqueue'] == $queue) {
246
			unset($rule['ackqueue']);
247
		}
248
	}
249
}
250

    
251
function cleanup_dnqueue_from_rules($queue) {
252
	global $config;
253

    
254
	foreach ($config['filter']['rule'] as $rule) {
255
		if ($rule['dnpipe'] == $queue) {
256
			unset($rule['dnpipe']);
257
		}
258
		if ($rule['pdnpipe'] == $queue) {
259
			unset($rule['pdnpipe']);
260
		}
261
	}
262
}
263

    
264
class altq_root_queue {
265
	var $interface;
266
	var $tbrconfig ;
267
	var $bandwidth;
268
	var $bandwidthtype; /* b, Kb, Mb */
269
	var $scheduler;
270
	var $qlimit;
271
	var $queues = array();
272
	var $qenabled = false;
273
	var $link;
274
	var $available_bw; /* in b/s */
275

    
276
	/* Accesor functions */
277
	function GetAvailableBandwidth() {
278
		return $this->available_bw;
279
	}
280
	function SetAvailableBandwidth($bw) {
281
		$this->available_bw = $bw;
282
	}
283
	function GetDefaultQueuePresent() {
284
		if (!empty($this->queues)) {
285
			foreach ($this->queues as $q) {
286
				if ($q->GetDefault()) {
287
					return true;
288
				}
289
			}
290
		}
291

    
292
		return false;
293
	}
294
	function SetLink($link) {
295
		$this->link = $link;
296
	}
297
	function GetLink() {
298
		return $this->link;
299
	}
300
	function GetEnabled() {
301
		return $this->qenabled;
302
	}
303
	function SetEnabled($value) {
304
		$this->qenabled = $value;
305
	}
306
	function CanHaveChildren() {
307
		if ($this->GetScheduler() == "CODELQ") {
308
			return false;
309
		} else {
310
			return true;
311
		}
312
	}
313
	function CanBeDeleted() {
314
		return false;
315
	}
316
	function GetQname() {
317
		return $this->interface;
318
	}
319
	function SetQname($name) {
320
		$this->interface = trim($name);
321
	}
322
	function GetInterface() {
323
		return $this->interface;
324
	}
325
	function SetInterface($name) {
326
		$this->interface = trim($name);
327
	}
328
	function GetTbrConfig() {
329
		return $this->tbrconfig;
330
	}
331
	function SetTbrConfig($tbrconfig) {
332
		$this->tbrconfig = $tbrconfig;
333
	}
334
	function GetBandwidth() {
335
		return $this->bandwidth;
336
	}
337
	function SetBandwidth($bw) {
338
		$this->bandwidth = $bw;
339
	}
340
	function GetBwscale() {
341
		return $this->bandwidthtype;
342
	}
343
	function SetBwscale($bwscale) {
344
		$this->bandwidthtype = $bwscale;
345
	}
346
	function GetScheduler() {
347
		return $this->scheduler;
348
	}
349
	function SetScheduler($scheduler) {
350
		$this->scheduler = trim($scheduler);
351
	}
352
	function GetQlimit() {
353
		return $this->qlimit;
354
	}
355
	function SetQlimit($limit) {
356
		$this->qlimit = $limit;
357
	}
358

    
359
	function validate_input($data, &$input_errors) {
360

    
361
		$reqdfields[] = "bandwidth";
362
		$reqdfieldsn[] = gettext("Bandwidth");
363
		$reqdfields[] = "bandwidthtype";
364
		$reqdfieldsn[] = gettext("Bandwidthtype");
365

    
366
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
367

    
368
		if ($data['bandwidth'] && (!is_numeric($data['bandwidth']))) {
369
			$input_errors[] = gettext("Bandwidth must be an integer.");
370
		}
371
		if ($data['bandwidth'] < 0) {
372
			$input_errors[] = gettext("Bandwidth cannot be negative.");
373
		}
374
		if ($data['qlimit'] && (!is_numeric($data['qlimit']))) {
375
			$input_errors[] = gettext("Qlimit must be an integer.");
376
		}
377
		if ($data['qlimit'] < 0) {
378
			$input_errors[] = gettext("Qlimit must be positive.");
379
		}
380
		if ($data['tbrconfig'] && (!is_numeric($data['tbrconfig']))) {
381
			$input_errors[] = gettext("Tbrsize must be an integer.");
382
		}
383
		if ($data['tbrconfig'] < 0) {
384
			$input_errors[] = gettext("Tbrsize must be positive.");
385
		}
386
	}
387

    
388
	/* Implement this to shorten some code on the frontend page */
389
	function ReadConfig(&$conf) {
390
		if (isset($conf['tbrconfig'])) {
391
			$this->SetTbrConfig($conf['tbrconfig']);
392
		} else {
393
			$this->SetTbrConfig($conf['tbrconfig']);
394
		}
395
		$this->SetBandwidth($conf['bandwidth']);
396
		if ($conf['bandwidthtype'] <> "") {
397
			$this->SetBwscale($conf['bandwidthtype']);
398
		}
399
		if (isset($conf['scheduler'])) {
400
			if ($this->GetScheduler() != $conf['scheduler']) {
401
				foreach ($this->queues as $q) {
402
					clean_child_queues($conf['scheduler'], $this->GetLink());
403
					$q->clean_queue($conf['scheduler']);
404
				}
405
			}
406
			$this->SetScheduler($conf['scheduler']);
407
		}
408
		if (isset($conf['qlimit']) && $conf['qlimit'] <> "") {
409
			$this->SetQlimit($conf['qlimit']);
410
		} else {
411
			$this->SetQlimit("");
412
		}
413
		if (isset($conf['name'])) {
414
			$this->SetQname($conf['name']);
415
		}
416
		if (!empty($conf['enabled'])) {
417
			$this->SetEnabled($conf['enabled']);
418
		} else {
419
			$this->SetEnabled("");
420
		}
421
	}
422

    
423
	function copy_queue($interface, &$cflink) {
424
		$cflink['interface'] = $interface;
425
		$cflink['name'] = $interface;
426
		$cflink['scheduler'] = $this->GetScheduler();
427
		$cflink['bandwidth'] = $this->GetBandwidth();
428
		$cflink['bandwidthtype'] = $this->GetBwscale();
429
		$cflink['qlimit'] = $this->GetQlimit();
430
		$cflink['tbrconfig'] = $this->GetTbrConfig();
431
		$cflink['enabled'] = $this->GetEnabled();
432
		if (is_array($this->queues)) {
433
			$cflink['queue'] = array();
434
			foreach ($this->queues as $q) {
435
				$cflink['queue'][$q->GetQname()] = array();
436
				$q->copy_queue($interface, $cflink['queue'][$q->GetQname()]);
437
			}
438
		}
439
	}
440

    
441
	function &get_queue_list(&$q = null) {
442
		$qlist = array();
443

    
444
		//$qlist[$this->GetQname()] = & $this;
445
		if (is_array($this->queues)) {
446
			foreach ($this->queues as $queue) {
447
				$queue->get_queue_list($qlist);
448
			}
449
		}
450
		return $qlist;
451
	}
452

    
453
	function &add_queue($interface, &$queue, &$path, &$input_errors) {
454

    
455
		if (!is_array($this->queues)) {
456
			$this->queues = array();
457
		}
458

    
459
		switch ($this->GetScheduler()) {
460
			case "PRIQ":
461
				$q =& new priq_queue();
462
				break;
463
			case "HFSC":
464
				$q =& new hfsc_queue();
465
				break;
466
			case "CBQ":
467
				$q =& new cbq_queue();
468
				break;
469
			case "FAIRQ":
470
				$q =& new fairq_queue();
471
				break;
472
			default:
473
				/* XXX: but should not happen anyway */
474
				return;
475
				break;
476
		}
477
		$q->SetLink($path);
478
		$q->SetInterface($this->GetInterface());
479
		$q->SetEnabled("on");
480
		$q->SetParent($this);
481
		$q->ReadConfig($queue);
482
		$q->validate_input($queue, $input_errors);
483
		if (count($input_errors)) {
484
			log_error("SHAPER: could not create queue " . $q->GetQname() . " on interface {$interface} because: " . print_r($input_errors, true));
485
			return $q;
486
		}
487

    
488
		if (isset($queue['bandwidth'])) {
489
			switch ($queue['bandwidthtype']) {
490
				case "%":
491
					$myBw = $this->GetAvailableBandwidth() * $queue['bandwidth'] / 100;
492
					break;
493
				default:
494
					$myBw = $queue['bandwidth'] * get_bandwidthtype_scale($queue['bandwdithtype']);
495
					break;
496
			}
497
		}
498
		$q->SetAvailableBandwidth($myBw);
499
		$this->SetAvailableBandwidth($this->GetAvailableBandwidth() - $myBw);
500
		$this->queues[$q->GetQname()] = &$q;
501
		ref_on_altq_queue_list($this->GetQname(), $q->GetQname());
502
		if (is_array($queue['queue'])) {
503
			foreach ($queue['queue'] as $key1 => $que) {
504
				array_push($path, $key1);
505
				$q->add_queue($q->GetInterface(), $que, $path, $input_errors);
506
				array_pop($path);
507
			}
508
		}
509

    
510
		return $q;
511
	}
512

    
513
	/* interface here might be optional */
514
	function &find_queue($interface, $qname) {
515
		if ($qname == $this->GetQname()) {
516
			return $this;
517
		}
518
		foreach ($this->queues as $q) {
519
			$result =& $q->find_queue("", $qname);
520
			if ($result) {
521
				return $result;
522
			}
523
		}
524
	}
525

    
526
	function &find_parentqueue($interface, $qname) {
527
		if ($qname == $interface) {
528
			$result =  NULL;
529
		} else if ($this->queues[$qname]) {
530
			$result = $this;
531
		} else if ($this->GetScheduler() <> "PRIQ") {
532
			foreach ($this->queues as $q) {
533
				$result = $q->find_parentqueue("", $qname);
534
				if ($result) {
535
					return $result;
536
				}
537
			}
538
		}
539
	}
540

    
541
	function build_tree() {
542
		global $shaperIFlist;
543

    
544
		$tree = " <li><a href=\"firewall_shaper.php?interface=".$this->GetInterface()."&amp;queue=". $this->GetInterface()."&amp;action=show";
545
		$tree .= "\">" . $shaperIFlist[$this->GetInterface()] . "</a>";
546
		if (is_array($this->queues)) {
547
			$tree .= "<ul>";
548
			foreach ($this->queues as $q)  {
549
				$tree .= $q->build_tree();
550
			}
551
			$tree .= "</ul>";
552
		}
553
		$tree .= "</li>";
554
		return $tree;
555
	}
556

    
557
	function delete_queue() {
558
		foreach ($this->queues as $q) {
559
			$this->SetAvailableBandwidth($this->GetAvailableBandwidth() + $q->GetAvailableBandwidth());
560
			$q->delete_queue();
561
		}
562
		unset_object_by_reference($this->GetLink());
563
	}
564

    
565
	function delete_all() {
566
		if (count($this->queues)) {
567
			foreach ($this->queues as $q) {
568
				$q->delete_all();
569
				unset_object_by_reference($q->GetLink());
570
				unset($q);
571
			}
572
			unset($this->queues);
573
		}
574
	}
575

    
576
	/*
577
	 * First it spits:
578
	 * altq on $interface ..............
579
	 *		then it goes like
580
	 *		foreach ($queues as $qkey => $queue) {
581
	 *				this->queues[$qkey]->build_rule();
582
	 *		}
583
	 */
584
	function build_rules(&$default = false) {
585
		if (count($this->queues) > 0 && $this->GetEnabled() == "on") {
586
			$default = false;
587
			$rules = " altq on	" . get_real_interface($this->GetInterface());
588
			if ($this->GetScheduler()) {
589
				$rules .= " ".strtolower($this->GetScheduler());
590
			}
591
			if ($this->GetQlimit() > 0) {
592
				$rules .= " qlimit " . $this->GetQlimit() . " ";
593
			}
594
			if ($this->GetBandwidth()) {
595
				$rules .= " bandwidth ".trim($this->GetBandwidth());
596
				if ($this->GetBwscale()) {
597
					$rules .= $this->GetBwscale();
598
				}
599
			}
600
			if ($this->GetTbrConfig()) {
601
				$rules .= " tbrsize ".$this->GetTbrConfig();
602
			}
603
			if (count($this->queues)) {
604
				$i = count($this->queues);
605
				$rules .= " queue { ";
606
				foreach ($this->queues as $qkey => $qnone) {
607
					if ($i > 1) {
608
						$i--;
609
						$rules .= " {$qkey}, ";
610
					} else {
611
						$rules .= " {$qkey} ";
612
					}
613
				}
614
				$rules .= " } \n";
615
				foreach ($this->queues as $q) {
616
					$rules .= $q->build_rules($default);
617
				}
618
			}
619

    
620
			if ($default == false) {
621
				$error = "SHAPER: no default queue specified for interface ". $this->GetInterface() . ". The interface queue will be enforced as default.";
622
				file_notice("Shaper", $error, "Error occurred", "");
623
				unset($error);
624
				return "\n";
625
			}
626
			$frule .= $rules;
627
		} else if ($this->GetEnabled() == "on" && $this->GetScheduler() == "CODELQ") {
628
			$rules = " altq on	" . get_real_interface($this->GetInterface());
629
			if ($this->GetScheduler()) {
630
				$rules .= " ".strtolower($this->GetScheduler());
631
			}
632
			if ($this->GetQlimit() > 0) {
633
				$rules .= " ( qlimit " . $this->GetQlimit() . " ) ";
634
			}
635
			if ($this->GetBandwidth()) {
636
				$rules .= " bandwidth ".trim($this->GetBandwidth());
637
				if ($this->GetBwscale()) {
638
					$rules .= $this->GetBwscale();
639
				}
640
			}
641
			if ($this->GetTbrConfig()) {
642
				$rules .= " tbrsize ".$this->GetTbrConfig();
643
			}
644

    
645
			$rules .= " queue";
646
		}
647

    
648
		$rules .= " \n";
649
		return $rules;
650
	}
651

    
652
	function build_javascript() {
653
		$javascript = "<script type=\"text/javascript\">";
654
		$javascript .= "//<![CDATA[\n";
655
		$javascript .= "function mySuspend() {";
656
		$javascript .= "if (document.layers && document.layers['shaperarea'] != null) ";
657
		$javascript .= "document.layers['shaperarea'].visibility = 'hidden'; ";
658
		$javascript .= "else if (document.all)";
659
		$javascript .= "document.all['shaperarea'].style.visibility = 'hidden';";
660
		$javascript .= "}";
661

    
662
		$javascript .= "function myResume() {";
663
		$javascript .= "if (document.layers && document.layers['shaperarea'] != null) ";
664
		$javascript .= "document.layers['shaperarea'].visibility = 'visible';";
665
		$javascript .= "else if (document.all) ";
666
		$javascript .= "document.all['shaperarea'].style.visibility = 'visible';";
667
		$javascript .= "}";
668
		$javascript .= "//]]>";
669
		$javascript .= "</script>";
670

    
671
		return $javascript;
672
	}
673

    
674
	function build_shortform() {
675
		global $g;
676

    
677
		$altq =& $this;
678

    
679
		if ($altq) {
680
			$scheduler = ": " . $altq->GetScheduler();
681
		}
682

    
683
		$form = '<dl class="dl-horizontal">';
684
		$form .= '	<dt>';
685
		$form .= '		<a href="firewall_shaper.php?interface=' . $this->GetInterface() . '&amp;queue=' . $this->GetQname() . '&amp;action=show">' . $shaperIFlist[$this->GetInterface()] . '</a>';
686
		$form .= '	</dt>';
687
		$form .= '	<dd>';
688
		$form .=		$scheduler;
689
		$form .= '	</dd>';
690

    
691
		$form .= '	<dt>';
692
		$form .=		'Bandwidth';
693
		$form .= '	</dt>';
694
		$form .= '	<dd>';
695
		$form .=		$this->GetBandwidth() . '&nbsp;' . $this->GetBwscale();
696
		$form .= '	</dd>';
697

    
698
		$form .= '	<dt>';
699
		$form .= 'Disable';
700
		$form .= '	<dt>';
701
		$form .= '	<dd>';
702

    
703
		$form .= '<a class="btn btn-default btn-xs" href="firewall_shaper_queues.php?interface=';
704
		$form .= $this->GetInterface() . '&amp;queue=';
705
		$form .= $this->GetQname() . '&amp;action=delete">';
706
		$form .= gettext("Disable shaper on interface") . '</a>';
707

    
708
		$form .= '	</dd>';
709

    
710
		$form .= '</dl>';
711

    
712
		return $form;
713

    
714
	}
715

    
716
	/*
717
	 * For requesting the parameters of the root queues
718
	 * to the user like the traffic wizard does.
719
	 */
720
	function build_form() {
721

    
722
		$sform = new Form(new Form_Button(
723
			'Submit',
724
			'Save'
725
		));
726

    
727
		$section = new Form_Section(null);
728

    
729
		$section->addInput(new Form_Checkbox(
730
			'enabled',
731
			'Enable/Disable',
732
			'Enable/disable discipline and its children',
733
			($this->GetEnabled() == "on"),
734
			'on'
735
		));
736

    
737
		$section->addInput(new Form_StaticText(
738
			'Name',
739
			$this->GetQname()
740
		));
741

    
742
		$section->addInput(new Form_Select(
743
			'scheduler',
744
			'Scheduler Type',
745
			$this->GetScheduler(),
746
			array('HFSC' => 'HFSC',
747
				  'CBQ' => 'CBQ',
748
				  'FAIRQ' => 'FAIRQ',
749
				  'CODELQ' => 'CODELQ',
750
				  'PRIQ' => 'PRIQ')
751
		))->setHelp('Changing this changes all child queues! Beware you can lose information.');
752

    
753
		$group = new Form_group('Bandwidth');
754

    
755
		$group->add(new Form_Input(
756
			'bandwidth',
757
			null,
758
			'number',
759
			$this->GetBandwidth()
760
		));
761

    
762
		$group->add(new Form_Select(
763
			'bandwidthtype',
764
			null,
765
			$this->GetBwscale(),
766
			array('Kb' => 'Kb',
767
				  'Mb' => 'Mb',
768
				  'Gb' => 'Gb',
769
				  'b' => 'b')
770
		));
771

    
772
		$section->add($group);
773

    
774
		$section->addInput(new Form_Input(
775
			'qlimit',
776
			'Queue Limit',
777
			'number',
778
			$this->GetQlimit()
779
		));
780

    
781
		$section->addInput(new Form_Input(
782
			'tbrconfig',
783
			'TRB Size',
784
			'number',
785
			$this->GetTbrConfig()
786
		))->setHelp('Adjusts the size, in bytes, of the token bucket regulator. If not specified, heuristics based on the interface ' .
787
					'bandwidth are used to determine the size.');
788

    
789
		$section->addInput(new Form_Input(
790
			'interface',
791
			null,
792
			'hidden',
793
			$this->GetInterface()
794
		));
795

    
796
		$section->addInput(new Form_Input(
797
			'name',
798
			null,
799
			'hidden',
800
			$this->GetQname()
801
		));
802

    
803
		$sform->add($section);
804

    
805
		return($sform);
806
	}
807

    
808
	function update_altq_queue_data(&$data) {
809
		$this->ReadConfig($data);
810
	}
811

    
812
	/*
813
	 * Should call on each of it queues and subqueues
814
	 * the same function much like build_rules();
815
	 */
816
	function wconfig() {
817
		$cflink = &get_reference_to_me_in_config($this->GetLink());
818
		if (!is_array($cflink)) {
819
			$cflink = array();
820
		}
821
		$cflink['interface'] = $this->GetInterface();
822
		$cflink['name'] = $this->GetQname();
823
		$cflink['scheduler'] = $this->GetScheduler();
824
		$cflink['bandwidth'] = $this->GetBandwidth();
825
		$cflink['bandwidthtype'] = $this->GetBwscale();
826
		$cflink['qlimit'] = trim($this->GetQlimit());
827
		if (empty($cflink['qlimit'])) {
828
			unset($cflink['qlimit']);
829
		}
830
		$cflink['tbrconfig'] = trim($this->GetTbrConfig());
831
		if (empty($cflink['tbrconfig'])) {
832
			unset($cflink['tbrconfig']);
833
		}
834
		$cflink['enabled'] = $this->GetEnabled();
835
		if (empty($cflink['enabled'])) {
836
			unset($cflink['enabled']);
837
		}
838
	}
839

    
840
}
841

    
842
class priq_queue {
843
	var $qname;
844
	var $qinterface;
845
	var $qlimit;
846
	var $qpriority;
847
	var $description;
848
	var $isparent;
849
	var $qbandwidth;
850
	var $qbandwidthtype;
851
	var $qdefault = "";
852
	var $qrio = "";
853
	var $qred = "";
854
	var $qcodel = "";
855
	var $qecn = "";
856
	var $qack;
857
	var $qenabled = "";
858
	var $qparent;
859
	var $link;
860
	var $available_bw; /* in b/s */
861

    
862
	/* This is here to help with form building and building rules/lists */
863
	var $subqueues = array();
864

    
865
	/* Accesor functions */
866
	function GetAvailableBandwidth() {
867
		return $this->available_bw;
868
	}
869
	function SetAvailableBandwidth($bw) {
870
		$this->available_bw = $bw;
871
	}
872
	function SetLink($link) {
873
		$this->link = $link;
874
	}
875
	function GetLink() {
876
		return $this->link;
877
	}
878
	function &GetParent() {
879
		return $this->qparent;
880
	}
881
	function SetParent(&$parent) {
882
		$this->qparent = &$parent;
883
	}
884
	function GetEnabled() {
885
		return $this->qenabled;
886
	}
887
	function SetEnabled($value) {
888
		$this->qenabled = $value;
889
	}
890
	function CanHaveChildren() {
891
		return false;
892
	}
893
	function CanBeDeleted() {
894
		return true;
895
	}
896
	function GetQname() {
897
		return $this->qname;
898
	}
899
	function SetQname($name) {
900
		$this->qname = trim($name);
901
	}
902
	function GetBandwidth() {
903
		return $this->qbandwidth;
904
	}
905
	function SetBandwidth($bandwidth) {
906
		$this->qbandwidth = $bandwidth;
907
	}
908
	function GetInterface() {
909
		return $this->qinterface;
910
	}
911
	function SetInterface($name) {
912
		$this->qinterface = trim($name);
913
	}
914
	function GetQlimit() {
915
		return $this->qlimit;
916
	}
917
	function SetQlimit($limit) {
918
		$this->qlimit = $limit;
919
	}
920
	function GetQpriority() {
921
		return $this->qpriority;
922
	}
923
	function SetQpriority($priority) {
924
		$this->qpriority = $priority;
925
	}
926
	function GetDescription() {
927
		return $this->description;
928
	}
929
	function SetDescription($str) {
930
		$this->description = trim($str);
931
	}
932
	function GetFirstime() {
933
		return $this->firsttime;
934
	}
935
	function SetFirsttime($number) {
936
		$this->firsttime = $number;
937
	}
938
	function GetBwscale() {
939
		return $this->qbandwidthtype;
940
	}
941
	function SetBwscale($scale) {
942
		$this->qbandwidthtype = $scale;
943
	}
944
	function GetDefaultQueuePresent() {
945
		if ($this->GetDefault()) {
946
			return true;
947
		}
948
		if (!empty($this->subqueues)) {
949
			foreach ($this->subqueues as $q) {
950
				if ($q->GetDefault()) {
951
					return true;
952
				}
953
			}
954
		}
955

    
956
		return false;
957
	}
958
	function GetDefault() {
959
		return $this->qdefault;
960
	}
961
	function SetDefault($value = false) {
962
		$this->qdefault = $value;
963
	}
964
	function GetCodel() {
965
		return $this->codel;
966
	}
967
	function SetCodel($codel = false) {
968
		$this->codel = $codel;
969
	}
970
	function GetRed() {
971
		return $this->qred;
972
	}
973
	function SetRed($red = false) {
974
		$this->qred = $red;
975
	}
976
	function GetRio() {
977
		return $this->qrio;
978
	}
979
	function SetRio($rio = false) {
980
		$this->qrio = $rio;
981
	}
982
	function GetEcn() {
983
		return $this->qecn;
984
	}
985
	function SetEcn($ecn = false) {
986
		$this->qecn = $ecn;
987
	}
988
	function GetAck() {
989
		return $this->qack;
990
	}
991
	function SetAck($ack = false) {
992
		$this->qack = $ack;
993
	}
994

    
995
	function build_javascript() {
996
		$javascript = "<script type=\"text/javascript\">";
997
		$javascript .= "//<![CDATA[\n";
998
		$javascript .= "function mySuspend() { \n";
999
		$javascript .= "if (document.layers && document.layers['shaperarea'] != null)\n";
1000
		$javascript .= "document.layers['shaperarea'].visibility = 'hidden';\n";
1001
		$javascript .= "else if (document.all)\n";
1002
		$javascript .= "document.all['shaperarea'].style.visibility = 'hidden';\n";
1003
		$javascript .= "}\n";
1004

    
1005
		$javascript .= "function myResume() {\n";
1006
		$javascript .= "if (document.layers && document.layers['shaperarea'] != null)\n";
1007
		$javascript .= "document.layers['shaperarea'].visibility = 'visible';\n";
1008
		$javascript .= "else if (document.all)\n";
1009
		$javascript .= "document.all['shaperarea'].style.visibility = 'visible';\n";
1010
		$javascript .= "}\n";
1011
		$javascript .= "//]]>";
1012
		$javascript .= "</script>";
1013

    
1014
		return $javascript;
1015
	}
1016

    
1017
	function &add_queue($interface, &$qname, &$path, &$input_errors) { return; }
1018

    
1019
	/*
1020
	 * Currently this will not be called unless we decide to clone a whole
1021
	 * queue tree on the 'By Queues' view or support drag&drop on the tree/list
1022
	 */
1023
	function copy_queue($interface, &$cflink) {
1024

    
1025
		$cflink['name'] = $this->GetQname();
1026
		$cflink['interface'] = $interface;
1027
		$cflink['qlimit'] = $this->GetQlimit();
1028
		$cflink['priority'] = $this->GetQpriority();
1029
		$cflink['description'] = $this->GetDescription();
1030
		$cflink['enabled'] = $this->GetEnabled();
1031
		$cflink['default'] = $this->GetDefault();
1032
		$cflink['red'] = $this->GetRed();
1033
		$cflink['codel'] = $this->GetCodel();
1034
		$cflink['rio'] = $this->GetRio();
1035
		$cflink['ecn'] = $this->GetEcn();
1036

    
1037
		if (is_array($this->subqueues)) {
1038
			$cflinkp['queue'] = array();
1039
			foreach ($this->subqueues as $q) {
1040
				$cflink['queue'][$q->GetQname()] = array();
1041
				$q->copy_queue($interface, $cflink['queue'][$q->GetQname()]);
1042
			}
1043
		}
1044
	}
1045

    
1046
	function clean_queue($sched) {
1047
		clean_child_queues($sched, $this->GetLink());
1048
		if (is_array($this->subqueues)) {
1049
			foreach ($this->subqueues as $q) {
1050
				$q->clean_queue($sched);
1051
			}
1052
		}
1053
	}
1054

    
1055
	function &get_queue_list(&$qlist) {
1056

    
1057
		$qlist[$this->GetQname()] = & $this;
1058
		if (is_array($this->subqueues)) {
1059
			foreach ($this->subqueues as $queue) {
1060
				$queue->get_queue_list($qlist);
1061
			}
1062
		}
1063
	}
1064

    
1065
	function delete_queue() {
1066
		unref_on_altq_queue_list($this->GetQname());
1067
		cleanup_queue_from_rules($this->GetQname());
1068
		unset_object_by_reference($this->GetLink());
1069
	}
1070

    
1071
	function delete_all() {
1072
		if (count($this->subqueues)) {
1073
			foreach ($this->subqueues as $q) {
1074
				$q->delete_all();
1075
				unset_object_by_reference($q->GetLink());
1076
				unset($q);
1077
			}
1078
			unset($this->subqueues);
1079
		}
1080
	}
1081

    
1082
	function &find_queue($interface, $qname) {
1083
		if ($qname == $this->GetQname()) {
1084
			return $this;
1085
		}
1086
	}
1087

    
1088
	function find_parentqueue($interface, $qname) { return; }
1089

    
1090
	function validate_input($data, &$input_errors) {
1091

    
1092
		$reqdfields[] = "name";
1093
		$reqdfieldsn[] = gettext("Name");
1094
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
1095

    
1096
		if ($data['bandwidth'] && (!is_numeric($data['bandwidth']))) {
1097
			$input_errors[] = "Bandwidth must be an integer.";
1098
		}
1099
		if ($data['bandwidth'] < 0) {
1100
			$input_errors[] = "Bandwidth cannot be negative.";
1101
		}
1102
		if ($data['priority'] && (!is_numeric($data['priority']) ||
1103
			($data['priority'] < 1) || ($data['priority'] > 15))) {
1104
			$input_errors[] = gettext("The priority must be an integer between 1 and 15.");
1105
		}
1106
		if ($data['qlimit'] && (!is_numeric($data['qlimit']))) {
1107
				$input_errors[] = gettext("Queue limit must be an integer");
1108
		}
1109
		if ($data['qlimit'] < 0) {
1110
				$input_errors[] = gettext("Queue limit must be positive");
1111
		}
1112
		if (!empty($data['newname']) && !preg_match("/^[a-zA-Z0-9_-]*$/", $data['newname'])) {
1113
			$input_errors[] = gettext("Queue names must be alphanumeric and _ or - only.");
1114
		}
1115
		if (!empty($data['name']) && !preg_match("/^[a-zA-Z0-9_-]*$/", $data['name'])) {
1116
			$input_errors[] = gettext("Queue names must be alphanumeric and _ or - only.");
1117
		}
1118
		$default = $this->GetDefault();
1119
		if (!empty($data['default']) && altq_get_default_queue($data['interface']) && empty($default)) {
1120
			$input_errors[] = gettext("Only one default queue per interface is allowed.");
1121
		}
1122
	}
1123

    
1124
	function ReadConfig(&$q) {
1125
		if (!empty($q['name']) && !empty($q['newname']) && $q['name'] != $q['newname']) {
1126
			$this->SetQname($q['newname']);
1127
		} else if (!empty($q['newname'])) {
1128
			$this->SetQname($q['newname']);
1129
		} else if (isset($q['name'])) {
1130
			$this->SetQname($q['name']);
1131
		}
1132
		if (isset($q['interface'])) {
1133
			$this->SetInterface($q['interface']);
1134
		}
1135
		$this->SetBandwidth($q['bandwidth']);
1136
		if ($q['bandwidthtype'] <> "") {
1137
			$this->SetBwscale($q['bandwidthtype']);
1138
		}
1139
		if (!empty($q['qlimit'])) {
1140
			$this->SetQlimit($q['qlimit']);
1141
		} else {
1142
			$this->SetQlimit(""); // Default
1143
		}
1144
		if (!empty($q['priority'])) {
1145
			$this->SetQPriority($q['priority']);
1146
		} else {
1147
			$this->SetQpriority("");
1148
		}
1149
		if (!empty($q['description'])) {
1150
			$this->SetDescription($q['description']);
1151
		} else {
1152
			$this->SetDescription("");
1153
		}
1154
		if (!empty($q['red'])) {
1155
			$this->SetRed($q['red']);
1156
		} else {
1157
			$this->SetRed();
1158
		}
1159
		if (!empty($q['codel'])) {
1160
			$this->SetCodel($q['codel']);
1161
		} else {
1162
			$this->SetCodel();
1163
		}
1164
		if (!empty($q['rio'])) {
1165
			$this->SetRio($q['rio']);
1166
		} else {
1167
			$this->SetRio();
1168
		}
1169
		if (!empty($q['ecn'])) {
1170
			$this->SetEcn($q['ecn']);
1171
		} else {
1172
			$this->SetEcn();
1173
		}
1174
		if (!empty($q['default'])) {
1175
			$this->SetDefault($q['default']);
1176
		} else {
1177
			$this->SetDefault();
1178
		}
1179
		if (!empty($q['enabled'])) {
1180
			$this->SetEnabled($q['enabled']);
1181
		} else {
1182
			$this->SetEnabled("");
1183
		}
1184
	}
1185

    
1186
	function build_tree() {
1187
		$tree = " <li><a href=\"firewall_shaper.php?interface=". $this->GetInterface()."&amp;queue=". $this->GetQname()."&amp;action=show";
1188
		$tree .= "\" ";
1189
		$tmpvalue = $this->GetDefault();
1190
		if (!empty($tmpvalue)) {
1191
			$tree .= " class=\"navlnk\"";
1192
		}
1193
		$tree .= " >" . $this->GetQname() . "</a>";
1194
		/*
1195
		 * Not needed here!
1196
		 * if (is_array($queues) {
1197
		 *	  $tree .= "<ul>";
1198
		 *	  foreach ($q as $queues)
1199
		 *		  $tree .= $queues['$q->GetName()']->build_tree();
1200
		 *	  endforeach
1201
		 *	  $tree .= "</ul>";
1202
		 * }
1203
		 */
1204

    
1205
		$tree .= "</li>";
1206

    
1207
		return $tree;
1208
	}
1209

    
1210
	/* Should return something like:
1211
	 * queue $qname on $qinterface bandwidth ....
1212
	 */
1213
	function build_rules(&$default = false) {
1214
		$pfq_rule = " queue ". $this->qname;
1215
		if ($this->GetInterface()) {
1216
			$pfq_rule .= " on ".get_real_interface($this->GetInterface());
1217
		}
1218
		$tmpvalue = $this->GetQpriority();
1219
		if (!empty($tmpvalue)) {
1220
			$pfq_rule .= " priority ".$this->GetQpriority();
1221
		}
1222
		$tmpvalue = $this->GetQlimit();
1223
		if (!empty($tmpvalue)) {
1224
			$pfq_rule .= " qlimit " . $this->GetQlimit();
1225
		}
1226
		if ($this->GetRed() || $this->GetRio() || $this->GetEcn() || $this->GetDefault() || $this->GetCodel()) {
1227
			$pfq_rule .= " priq ( ";
1228
			$tmpvalue = $this->GetRed();
1229
			if (!empty($tmpvalue)) {
1230
				$comma = 1;
1231
				$pfq_rule .= " red ";
1232
			}
1233
			$tmpvalue = $this->GetRio();
1234
			if (!empty($tmpvalue)) {
1235
				if ($comma) {
1236
					$pfq_rule .= " ,";
1237
				}
1238
				$comma = 1;
1239
				$pfq_rule .= " rio ";
1240
			}
1241
			$tmpvalue = $this->GetEcn();
1242
			if (!empty($tmpvalue)) {
1243
				if ($comma) {
1244
					$pfq_rule .= " ,";
1245
				}
1246
				$comma = 1;
1247
				$pfq_rule .= " ecn ";
1248
			}
1249
			$tmpvalue = $this->GetCodel();
1250
			if (!empty($tmpvalue)) {
1251
				if ($comma) {
1252
					$pfq_rule .= " ,";
1253
				}
1254
				$comma = 1;
1255
				$pfq_rule .= " codel ";
1256
			}
1257
			$tmpvalue = $this->GetDefault();
1258
			if (!empty($tmpvalue)) {
1259
				if ($comma) {
1260
					$pfq_rule .= " ,";
1261
				}
1262
				$pfq_rule .= " default ";
1263
				$default = true;
1264
			}
1265
			$pfq_rule .= " ) ";
1266
		}
1267

    
1268
		$pfq_rule .= " \n";
1269

    
1270
		return $pfq_rule;
1271
	}
1272

    
1273
	/*
1274
	 * To return the html form to show to user
1275
	 * for getting the parameters.
1276
	 * Should do even for first time when the
1277
	 * object is created and later when we may
1278
	 * need to update it. (2)
1279
	 */
1280

    
1281
	function build_form() {
1282

    
1283
		$sform = new Form();
1284

    
1285
		$section = new Form_Section(null);
1286

    
1287
		$section->addInput(new Form_Checkbox(
1288
			'enabled',
1289
			'Enable/Disable',
1290
			'Enable/disable discipline and its children',
1291
			($this->GetEnabled() == "on"),
1292
			'on'
1293
		));
1294

    
1295
		$section->addInput(new Form_StaticText(
1296
			'Name',
1297
			$this->GetQname()
1298
		))->setHelp('Enter the name of the queue here. Do not use spaces and limit the size to 15 characters.');
1299

    
1300
		$section->addInput(new Form_Input(
1301
			'priority',
1302
			'Priority',
1303
			'number',
1304
			$this->GetQpriority(),
1305
			['min' => '0', 'max'=> '7']
1306
		))->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.');
1307

    
1308
		$section->addInput(new Form_Input(
1309
			'qlimit',
1310
			'Queue Limit',
1311
			'number',
1312
			$this->GetQlimit()
1313
		))->setHelp('Queue limit in packets.');
1314

    
1315
		$group = new Form_Group('Scheduler options');
1316

    
1317
		if (empty($this->subqueues)) {
1318
			$group->add(new Form_Checkbox(
1319
				'default',
1320
				null,
1321
				null,
1322
				$this->GetDefault()
1323
			))->setHelp('Default Queue');
1324
		}
1325

    
1326
		$group->add(new Form_Checkbox(
1327
			'red',
1328
			null,
1329
			null,
1330
			!empty($this->GetRed())
1331
		))->setHelp('<a target="_new" href="http://www.openbsd.org/faq/pf/queueing.html#red">' . gettext('Random Early Detection') . '</a>');
1332

    
1333
		$group->add(new Form_Checkbox(
1334
			'rio',
1335
			null,
1336
			null,
1337
			!empty($this->GetRio())
1338
		))->setHelp('<a target="_new" href="http://www.openbsd.org/faq/pf/queueing.html#rio">' . gettext('Random Early Detection In and Out') . '</a>');
1339

    
1340
		$group->add(new Form_Checkbox(
1341
			'ecn',
1342
			null,
1343
			null,
1344
			!empty($this->GetEcn())
1345
		))->setHelp('<a target="_new" href="http://www.openbsd.org/faq/pf/queueing.html#ecn">' . gettext('Explicit Congestion Notification') . '</a>');
1346

    
1347
		$group->add(new Form_Checkbox(
1348
			'codel',
1349
			null,
1350
			null,
1351
			!empty($this->GetCodel())
1352
		))->setHelp('<a target="_new" href="http://www.openbsd.org/faq/pf/queueing.html#ecn">' . gettext('Explicit Congestion Notification') . '</a>');
1353

    
1354
		$group->setHelp('Select options for this queue');
1355

    
1356
		$section->add($group);
1357

    
1358
		$section->addInput(new Form_Input(
1359
			'description',
1360
			'Description',
1361
			'text',
1362
			$this->GetDescription()
1363
		));
1364

    
1365
		$section->addInput(new Form_Input(
1366
			'interface',
1367
			null,
1368
			'hidden',
1369
			$this->GetInterface()
1370
		));
1371

    
1372
		$sform->add($section);
1373

    
1374
		return($sform);
1375
	}
1376

    
1377
	function build_shortform() {
1378
		/* XXX: Hacks in sight. Mostly layer violations!  */
1379
		global $g, $altq_list_queues;
1380
		global $shaperIFlist;
1381

    
1382
		$altq =& $altq_list_queues[$this->GetInterface()];
1383

    
1384
		if ($altq) {
1385
			$scheduler = $altq->GetScheduler();
1386
		}
1387

    
1388
		$form = '<dl class="dl-horizontal">';
1389
		$form .= '	<dt>';
1390
		$form .= '		<a href="firewall_shaper.php?interface=' . $this->GetInterface() . '&amp;queue=' . $this->GetQname() . '&amp;action=show">' . $shaperIFlist[$this->GetInterface()] . '</a>';
1391
		$form .= '	</dt>';
1392
		$form .= '	<dd>';
1393
		$form .=		$scheduler;
1394
		$form .= '	</dd>';
1395

    
1396
		$form .= '	<dt>';
1397
		$form .=		'Bandwidth';
1398
		$form .= '	</dt>';
1399
		$form .= '	<dd>';
1400
		$form .=		$this->GetBandwidth() . '&nbsp;' . $this->GetBwscale();
1401
		$form .= '	</dd>';
1402

    
1403
		$tmpvalue = $this->GetQpriority();
1404
		if (!empty($tmpvalue)) {
1405
			$form .= '	<dt>';
1406
			$form .=		'Priority';
1407
			$form .= '	<dt>';
1408
			$form .= '	<dd>';
1409
			$form .=		'On';
1410
			$form .= '	</dd>';
1411
		}
1412

    
1413
		$tmpvalue = $this->GetDefault();
1414
		if (!empty($tmpvalue)) {
1415
			$form .= '	<dt>';
1416
			$form .=		'Default';
1417
			$form .= '	<dt>';
1418
			$form .= '	<dd>';
1419
			$form .=		'On';
1420
			$form .= '	</dd>';
1421
		}
1422

    
1423
			$form .= '	<dt>';
1424
			$form .= 'Delete';
1425
			$form .= '	<dt>';
1426
			$form .= '	<dd>';
1427

    
1428
			$form .= '<a class="btn btn-danger btn-xs" href="firewall_shaper_queues.php?interface=';
1429
			$form .= $this->GetInterface() . '&amp;queue=';
1430
			$form .= $this->GetQname() . '&amp;action=delete">';
1431
			$form .= gettext("Delete queue from interface") . '</a>';
1432

    
1433
			$form .= '	</dd>';
1434

    
1435
			$form .= '</dl>';
1436

    
1437
		return $form;
1438

    
1439
	}
1440

    
1441
	function update_altq_queue_data(&$q) {
1442
		$this->ReadConfig($q);
1443
	}
1444

    
1445
	function wconfig() {
1446
		$cflink =& get_reference_to_me_in_config($this->GetLink());
1447
		if (!is_array($cflink)) {
1448
			$cflink = array();
1449
		}
1450
		$cflink['name'] = $this->GetQname();
1451
		$cflink['interface'] = $this->GetInterface();
1452
		$cflink['qlimit'] = trim($this->GetQlimit());
1453
		if (empty($cflink['qlimit'])) {
1454
			unset($cflink['qlimit']);
1455
		}
1456
		$cflink['priority'] = trim($this->GetQpriority());
1457
		if (empty($cflink['priority'])) {
1458
			unset($cflink['priority']);
1459
		}
1460
		$cflink['description'] = trim($this->GetDescription());
1461
		if (empty($cflink['description'])) {
1462
			unset($cflink['description']);
1463
		}
1464
		$cflink['enabled'] = trim($this->GetEnabled());
1465
		if (empty($cflink['enabled'])) {
1466
			unset($cflink['enabled']);
1467
		}
1468
		$cflink['default'] = trim($this->GetDefault());
1469
		if (empty($cflink['default'])) {
1470
			unset($cflink['default']);
1471
		}
1472
		$cflink['red'] = trim($this->GetRed());
1473
		if (empty($cflink['red'])) {
1474
			unset($cflink['red']);
1475
		}
1476
		$cflink['codel'] = trim($this->GetCodel());
1477
		if (empty($cflink['codel'])) {
1478
			unset($cflink['codel']);
1479
		}
1480
		$cflink['rio'] = trim($this->GetRio());
1481
		if (empty($cflink['rio'])) {
1482
			unset($cflink['rio']);
1483
		}
1484
		$cflink['ecn'] = trim($this->GetEcn());
1485
		if (empty($cflink['ecn'])) {
1486
			unset($cflink['ecn']);
1487
		}
1488
	}
1489
}
1490

    
1491
class hfsc_queue extends priq_queue {
1492
	/* realtime */
1493
	var $realtime;
1494
	var $r_m1;
1495
	var $r_d;
1496
	var $r_m2;
1497
	/* linkshare */
1498
	var $linkshare;
1499
	var $l_m1;
1500
	var $l_d;
1501
	var $l_m2;
1502
	/* upperlimit */
1503
	var $upperlimit;
1504
	var $u_m1;
1505
	var $u_d;
1506
	var $u_m2;
1507

    
1508
	/*
1509
	 * HFSC can have nested queues.
1510
	 */
1511
	function CanHaveChildren() {
1512
		return true;
1513
	}
1514
	function GetRealtime() {
1515
		return $this->realtime;
1516
	}
1517
	function GetR_m1() {
1518
		return $this->r_m1;
1519
	}
1520
	function GetR_d() {
1521
		return $this->r_d;
1522
	}
1523
	function GetR_m2() {
1524
		return $this->r_m2;
1525
	}
1526
	function SetRealtime() {
1527
		$this->realtime = "on";
1528
	}
1529
	function DisableRealtime() {
1530
		$this->realtime = "";
1531
	}
1532
	function SetR_m1($value) {
1533
		$this->r_m1 = $value;
1534
	}
1535
	function SetR_d($value) {
1536
		$this->r_d = $value;
1537
	}
1538
	function SetR_m2($value) {
1539
		$this->r_m2 = $value;
1540
	}
1541
	function GetLinkshare() {
1542
		return $this->linkshare;
1543
	}
1544
	function DisableLinkshare() {
1545
		$this->linkshare = "";
1546
	}
1547
	function GetL_m1() {
1548
		return $this->l_m1;
1549
	}
1550
	function GetL_d() {
1551
		return $this->l_d;
1552
	}
1553
	function GetL_m2() {
1554
		return $this->l_m2;
1555
	}
1556
	function SetLinkshare() {
1557
		$this->linkshare = "on";
1558
	}
1559
	function SetL_m1($value) {
1560
		$this->l_m1 = $value;
1561
	}
1562
	function SetL_d($value) {
1563
		$this->l_d = $value;
1564
	}
1565
	function SetL_m2($value) {
1566
		$this->l_m2 = $value;
1567
	}
1568
	function GetUpperlimit() {
1569
		return $this->upperlimit;
1570
	}
1571
	function GetU_m1() {
1572
		return $this->u_m1;
1573
	}
1574
	function GetU_d() {
1575
		return $this->u_d;
1576
	}
1577
	function GetU_m2() {
1578
		return $this->u_m2;
1579
	}
1580
	function SetUpperlimit() {
1581
		$this->upperlimit = "on";
1582
	}
1583
	function DisableUpperlimit() {
1584
		$this->upperlimit = "";
1585
	}
1586
	function SetU_m1($value) {
1587
		$this->u_m1 = $value;
1588
	}
1589
	function SetU_d($value) {
1590
		$this->u_d = $value;
1591
	}
1592
	function SetU_m2($value) {
1593
		$this->u_m2 = $value;
1594
	}
1595

    
1596
	function &add_queue($interface, &$qname, &$path, &$input_errors) {
1597

    
1598
		if (!is_array($this->subqueues)) {
1599
			$this->subqueues = array();
1600
		}
1601
		$q =& new hfsc_queue();
1602
		$q->SetInterface($this->GetInterface());
1603
		$q->SetParent($this);
1604
		$q->ReadConfig($qname);
1605
		$q->validate_input($qname, $input_errors);
1606
		if (count($input_errors)) {
1607
			log_error("SHAPER: could not create queue " . $q->GetQname() . " on interface {$interface} because: " . print_r($input_errors, true));
1608
			return $q;
1609
		}
1610

    
1611
		$q->SetEnabled("on");
1612
		$q->SetLink($path);
1613
		switch ($q->GetBwscale()) {
1614
			case "%":
1615
				$myBw = $this->GetAvailableBandwidth() * $qname['bandwidth'] / 100;
1616
				break;
1617
			default:
1618
				$myBw = $qname['bandwidth'] * get_bandwidthtype_scale($q->GetBwscale());
1619
				break;
1620
		}
1621
		$q->SetAvailableBandwidth($myBw);
1622
		$this->SetAvailableBandwidth($this->GetAvailableBandwidth() - $myBw);
1623

    
1624
		$this->subqueues[$q->GetQname()] =& $q; //new hfsc_queue()
1625
		ref_on_altq_queue_list($this->GetQname(), $q->GetQname());
1626
		if (is_array($qname['queue'])) {
1627
			foreach ($qname['queue'] as $key1 => $que) {
1628
				array_push($path, $key1);
1629
				$q->add_queue($q->GetInterface(), $que, $path, $input_errors);
1630
				array_pop($path);
1631
			}
1632
		}
1633

    
1634
		return $q;
1635
	}
1636

    
1637
	function copy_queue($interface, &$cflink) {
1638

    
1639
		$cflink['name'] = $this->GetQname();
1640
		$cflink['interface'] = $interface;
1641
		$cflink['qlimit'] = trim($this->GetQlimit());
1642
		if (empty($cflink['qlimit'])) {
1643
			unset($cflink['qlimit']);
1644
		}
1645
		$cflink['priority'] = trim($this->GetQpriority());
1646
		if (empty($cflink['priority'])) {
1647
			unset($cflink['priority']);
1648
		}
1649
		$cflink['description'] = trim($this->GetDescription());
1650
		if (empty($cflink['description'])) {
1651
			unset($cflink['description']);
1652
		}
1653
		$cflink['bandwidth'] = $this->GetBandwidth();
1654
		$cflink['bandwidthtype'] = $this->GetBwscale();
1655
		$cflink['enabled'] = trim($this->GetEnabled());
1656
		if (empty($cflink['enabled'])) {
1657
			unset($cflink['enabled']);
1658
		}
1659
		$cflink['default'] = trim($this->GetDefault());
1660
		if (empty($cflink['default'])) {
1661
			unset($cflink['default']);
1662
		}
1663
		$cflink['red'] = trim($this->GetRed());
1664
		if (empty($cflink['red'])) {
1665
			unset($cflink['red']);
1666
		}
1667
		$cflink['rio'] = trim($this->GetRio());
1668
		if (empty($cflink['rio'])) {
1669
			unset($cflink['rio']);
1670
		}
1671
		$cflink['ecn'] = trim($this->GetEcn());
1672
		if (empty($cflink['ecn'])) {
1673
			unset($cflink['ecn']);
1674
		}
1675
		if ($this->GetLinkshare() <> "") {
1676
			if ($this->GetL_m1() <> "") {
1677
				$cflink['linkshare1'] = $this->GetL_m1();
1678
				$cflink['linkshare2'] = $this->GetL_d();
1679
				$cflink['linkshare'] = "on";
1680
			} else {
1681
				unset($cflink['linkshare1']);
1682
				unset($cflink['linkshare2']);
1683
				unset($cflink['linkshare']);
1684
			}
1685
			if ($this->GetL_m2() <> "") {
1686
				$cflink['linkshare3'] = $this->GetL_m2();
1687
				$cflink['linkshare'] = "on";
1688
			} else {
1689
				unset($cflink['linkshare3']);
1690
				unset($cflink['linkshare']);
1691
			}
1692
		}
1693
		if ($this->GetRealtime() <> "") {
1694
			if ($this->GetR_m1() <> "") {
1695
				$cflink['realtime1'] = $this->GetR_m1();
1696
				$cflink['realtime2'] = $this->GetR_d();
1697
				$cflink['realtime'] = "on";
1698
			} else {
1699
				unset($cflink['realtime1']);
1700
				unset($cflink['realtime2']);
1701
				unset($cflink['realtime']);
1702
			}
1703
			if ($this->GetR_m2() <> "") {
1704
				$cflink['realtime3'] = $this->GetR_m2();
1705
				$cflink['realtime'] = "on";
1706
			} else {
1707
				unset($cflink['realtime3']);
1708
				unset($cflink['realtime']);
1709
			}
1710
		}
1711
		if ($this->GetUpperlimit() <> "") {
1712
			if ($this->GetU_m1() <> "") {
1713
				$cflink['upperlimit1'] = $this->GetU_m1();
1714
				$cflink['upperlimit2'] = $this->GetU_d();
1715
				$cflink['upperlimit'] = "on";
1716
			} else {
1717
				unset($cflink['upperlimit']);
1718
				unset($cflink['upperlimit1']);
1719
				unset($cflink['upperlimit2']);
1720
			}
1721
			if ($this->GetU_m2() <> "") {
1722
				$cflink['upperlimit3'] = $this->GetU_m2();
1723
				$cflink['upperlimit'] = "on";
1724
			} else {
1725
				unset($cflink['upperlimit3']);
1726
				unset($cflink['upperlimit']);
1727
			}
1728
		}
1729

    
1730
		if (is_array($this->subqueues)) {
1731
			$cflinkp['queue'] = array();
1732
			foreach ($this->subqueues as $q) {
1733
				$cflink['queue'][$q->GetQname()] = array();
1734
				$q->copy_queue($interface, $cflink['queue'][$q->GetQname()]);
1735
			}
1736
		}
1737
	}
1738

    
1739
	function delete_queue() {
1740
		unref_on_altq_queue_list($this->GetQname());
1741
		cleanup_queue_from_rules($this->GetQname());
1742
		$parent =& $this->GetParent();
1743
		foreach ($this->subqueues as $q)  {
1744
			$this->SetAvailableBandwidth($this->GetAvailableBandwidth() + $q->GetAvailableBandwidth());
1745
				$q->delete_queue();
1746
		}
1747
		unset_object_by_reference($this->GetLink());
1748
	}
1749

    
1750
	/*
1751
	 * Should search even its children
1752
	 */
1753
	function &find_queue($interface, $qname) {
1754
		if ($qname == $this->GetQname()) {
1755
			return $this;
1756
		}
1757

    
1758
		foreach ($this->subqueues as $q) {
1759
			$result =& $q->find_queue("", $qname);
1760
			if ($result) {
1761
				return $result;
1762
			}
1763
		}
1764
	}
1765

    
1766
	function &find_parentqueue($interface, $qname) {
1767
		if ($this->subqueues[$qname]) {
1768
			return $this;
1769
		}
1770
		foreach ($this->subqueues as $q) {
1771
			$result = $q->find_parentqueue("", $qname);
1772
			if ($result) {
1773
				return $result;
1774
			}
1775
		}
1776
	}
1777

    
1778
	function validate_input($data, &$input_errors) {
1779
		parent::validate_input($data, $input_errors);
1780

    
1781
		$reqdfields[] = "bandwidth";
1782
		$reqdfieldsn[] = gettext("Bandwidth");
1783
		$reqdfields[] = "bandwidthtype";
1784
		$reqdfieldsn[] = gettext("Bandwidthtype");
1785

    
1786
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
1787

    
1788
		if (isset($data['linkshare3']) && $data['linkshare3'] <> "") {
1789
			if ($data['bandwidth'] && (!is_numeric($data['bandwidth']))) {
1790
				$input_errors[] = gettext("Bandwidth must be an integer.");
1791
			}
1792

    
1793
			if ($data['bandwidth'] < 0) {
1794
				$input_errors[] = gettext("Bandwidth cannot be negative.");
1795
			}
1796

    
1797
			if ($data['bandwidthtype'] == "%") {
1798
				if ($data['bandwidth'] > 100 || $data['bandwidth'] < 0) {
1799
					$input_errors[] = gettext("Bandwidth in percentage should be between 1 and 100 bounds.");
1800
				}
1801
			}
1802
		/*
1803
			$parent =& $this->GetParent();
1804
			switch ($data['bandwidthtype']) {
1805
			case "%":
1806
				$myBw = $parent->GetAvailableBandwidth() * floatval($data['bandwidth']) / 100;
1807
			default:
1808
				$mybw = floatval($data['bandwidth']) * get_bandwidthtype_scale($data['bandwidthtype']);
1809
				break;
1810
			}
1811
			if ($parent->GetAvailableBandwidth() < $myBw) {
1812
				$input_errors[] = "The sum of children bandwidth exceeds that of the parent.";
1813
			}
1814
		*/
1815
		}
1816

    
1817
		if ($data['upperlimit1'] <> "" &&  $data['upperlimit2'] == "") {
1818
			$input_errors[] = gettext("upperlimit service curve defined but missing (d) value");
1819
		}
1820
		if ($data['upperlimit2'] <> "" &&  $data['upperlimit1'] == "") {
1821
			$input_errors[] = gettext("upperlimit service curve defined but missing initial bandwidth (m1) value");
1822
		}
1823
		if ($data['upperlimit1'] <> "" && !is_valid_shaperbw($data['upperlimit1'])) {
1824
			$input_errors[] = gettext("upperlimit m1 value needs to be Kb, Mb, Gb, or %");
1825
		}
1826
		if ($data['upperlimit2'] <> "" && !is_numeric($data['upperlimit2'])) {
1827
			$input_errors[] = gettext("upperlimit d value needs to be numeric");
1828
		}
1829
		if ($data['upperlimit3'] <> "" && !is_valid_shaperbw($data['upperlimit3'])) {
1830
			$input_errors[] = gettext("upperlimit m2 value needs to be Kb, Mb, Gb, or %");
1831
		}
1832

    
1833
		/*
1834
		if (isset($data['upperlimit']) && $data['upperlimit3'] <> "" && $data['upperlimit1'] <> "") {
1835
			$bw_1 = get_hfsc_bandwidth($this, $data['upperlimit1']);
1836
			$bw_2 = get_hfsc_bandwidth($this, $data['upperlimit3']);
1837
			if (floatval($bw_1) < floatval($bw_2)) {
1838
				$input_errors[] = ("upperlimit m1 cannot be smaller than m2");
1839
			}
1840

    
1841
			if (get_interface_bandwidth($this) < (0.8 * (floatval($bw_1) + floatval($bw_2)))) {
1842
				$input_errors[] = ("upperlimit specification exceeds 80% of allowable allocation.");
1843
			}
1844
		}
1845
		*/
1846
		if ($data['linkshare1'] <> "" &&  $data['linkshare2'] == "") {
1847
			$input_errors[] = gettext("linkshare service curve defined but missing (d) value");
1848
		}
1849
		if ($data['linkshare2'] <> "" &&  $data['linkshare1'] == "") {
1850
			$input_errors[] = gettext("linkshare service curve defined but missing initial bandwidth (m1) value");
1851
		}
1852
		if ($data['linkshare1'] <> "" && !is_valid_shaperbw($data['linkshare1'])) {
1853
			$input_errors[] = gettext("linkshare m1 value needs to be Kb, Mb, Gb, or %");
1854
		}
1855
		if ($data['linkshare2'] <> "" && !is_numeric($data['linkshare2'])) {
1856
			$input_errors[] = gettext("linkshare d value needs to be numeric");
1857
		}
1858
		if ($data['linkshare3'] <> "" && !is_valid_shaperbw($data['linkshare3'])) {
1859
			$input_errors[] = gettext("linkshare m2 value needs to be Kb, Mb, Gb, or %");
1860
		}
1861
		if ($data['realtime1'] <> "" &&	 $data['realtime2'] == "") {
1862
			$input_errors[] = gettext("realtime service curve defined but missing (d) value");
1863
		}
1864
		if ($data['realtime2'] <> "" &&	 $data['realtime1'] == "") {
1865
			$input_errors[] = gettext("realtime service curve defined but missing initial bandwidth (m1) value");
1866
		}
1867

    
1868
		/*
1869
		if (isset($data['linkshare']) && $data['linkshare3'] <> "" && $data['linkshare1'] <> "" && 0) {
1870
			$bw_1 = get_hfsc_bandwidth($this, $data['linkshare1']);
1871
			$bw_2 = get_hfsc_bandwidth($this, $data['linkshare3']);
1872
			if (floatval($bw_1) < floatval($bw_2)) {
1873
				$input_errors[] = ("linkshare m1 cannot be smaller than m2");
1874
			}
1875

    
1876
			if (get_interface_bandwidth($this) < (0.8 * (floatval($bw_1) + floatval($bw_2)))) {
1877
				$input_errors[] = ("linkshare specification exceeds 80% of allowable allocation.");
1878
			}
1879
		}
1880
		*/
1881

    
1882
		if ($data['realtime1'] <> "" && !is_valid_shaperbw($data['realtime1'])) {
1883
			$input_errors[] = gettext("realtime m1 value needs to be Kb, Mb, Gb, or %");
1884
		}
1885
		if ($data['realtime2'] <> "" && !is_numeric($data['realtime2'])) {
1886
			$input_errors[] = gettext("realtime d value needs to be numeric");
1887
		}
1888
		if ($data['realtime3'] <> "" && !is_valid_shaperbw($data['realtime3'])) {
1889
			$input_errors[] = gettext("realtime m2 value needs to be Kb, Mb, Gb, or %");
1890
		}
1891

    
1892
		/*
1893
		if (isset($data['realtime']) && $data['realtime3'] <> "" && $data['realtime1'] <> "" && 0) {
1894
			$bw_1 = get_hfsc_bandwidth($this, $data['realtime1']);
1895
			$bw_2 = get_hfsc_bandwidth($this, $data['realtime3']);
1896
			if (floatval($bw_1) < floatval($bw_2)) {
1897
				$input_errors[] = ("realtime m1 cannot be smaller than m2");
1898
			}
1899

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

    
1907
	function ReadConfig(&$cflink) {
1908
		if (!empty($cflink['linkshare'])) {
1909
			if (!empty($cflink['linkshare1'])) {
1910
				$this->SetL_m1($cflink['linkshare1']);
1911
				$this->SetL_d($cflink['linkshare2']);
1912
				$this->SetLinkshare();
1913
			} else {
1914
				$this->SetL_m1("");
1915
				$this->SetL_d("");
1916
				$this->DisableLinkshare();
1917
			}
1918
			if (!empty($cflink['linkshare3'])) {
1919
				$this->SetL_m2($cflink['linkshare3']);
1920
				$this->SetLinkshare();
1921
			}
1922
		} else {
1923
			$this->DisableLinkshare();
1924
		}
1925
		if (!empty($cflink['realtime'])) {
1926
			if (!empty($cflink['realtime1'])) {
1927
				$this->SetR_m1($cflink['realtime1']);
1928
				$this->SetR_d($cflink['realtime2']);
1929
				$this->SetRealtime();
1930
			} else {
1931
				$this->SetR_m1("");
1932
				$this->SetR_d("");
1933
				$this->DisableRealtime();
1934
			}
1935
			if (!empty($cflink['realtime3'])) {
1936
				$this->SetR_m2($cflink['realtime3']);
1937
				$this->SetRealtime();
1938
			}
1939
		} else {
1940
			$this->DisableRealtime();
1941
		}
1942
		if (!empty($cflink['upperlimit'])) {
1943
			if (!empty($cflink['upperlimit1'])) {
1944
				$this->SetU_m1($cflink['upperlimit1']);
1945
				$this->SetU_d($cflink['upperlimit2']);
1946
				$this->SetUpperlimit();
1947
			} else {
1948
				$this->SetU_m1("");
1949
				$this->SetU_d("");
1950
				$this->DisableUpperlimit();
1951
			}
1952
			if (!empty($cflink['upperlimit3'])) {
1953
				$this->SetU_m2($cflink['upperlimit3']);
1954
				$this->SetUpperlimit();
1955
			}
1956
		} else {
1957
			$this->DisableUpperlimit();
1958
		}
1959
		parent::ReadConfig($cflink);
1960
	}
1961

    
1962
	function build_tree() {
1963
		$tree = " <li><a href=\"firewall_shaper.php?interface=" . $this->GetInterface() ."&amp;queue=" . $this->GetQname()."&amp;action=show";
1964
		$tree .= "\" ";
1965
		$tmpvalue = $this->GetDefault();
1966
		if (!empty($tmpvalue)) {
1967
			$tree .= " class=\"navlnk\"";
1968
		}
1969
		$tree .= " >" . $this->GetQname() . "</a>";
1970
		if (is_array($this->subqueues)) {
1971
			$tree .= "<ul>";
1972
			foreach ($this->subqueues as $q)  {
1973
				$tree .= $q->build_tree();
1974
			}
1975
			$tree .= "</ul>";
1976
		}
1977
		$tree .= "</li>";
1978
		return $tree;
1979
	}
1980

    
1981
	/* Even this should take children into consideration */
1982
	function build_rules(&$default = false) {
1983

    
1984
		$pfq_rule = " queue ". $this->qname;
1985
		if ($this->GetInterface()) {
1986
			$pfq_rule .= " on ".get_real_interface($this->GetInterface());
1987
		}
1988
		if ($this->GetBandwidth() && $this->GetBwscale()) {
1989
			$pfq_rule .= " bandwidth ".trim($this->GetBandwidth()).$this->GetBwscale();
1990
		}
1991

    
1992
		$tmpvalue = $this->GetQlimit();
1993
		if (!empty($tmpvalue)) {
1994
			$pfq_rule .= " qlimit " . $this->GetQlimit();
1995
		}
1996
		if ($this->GetDefault() || $this->GetRed() || $this->GetRio() || $this->GetEcn() || $this->GetCodel() || $this->GetRealtime() <> "" || $this->GetLinkshare() <> "" || $this->GetUpperlimit() <> "") {
1997
			$pfq_rule .= " hfsc ( ";
1998
			$tmpvalue = $this->GetRed();
1999
			if (!empty($tmpvalue)) {
2000
				$comma = 1;
2001
				$pfq_rule .= " red ";
2002
			}
2003

    
2004
			$tmpvalue = $this->GetRio();
2005
			if (!empty($tmpvalue)) {
2006
				if ($comma) {
2007
					$pfq_rule .= " ,";
2008
				}
2009
				$comma = 1;
2010
				$pfq_rule .= " rio ";
2011
			}
2012
			$tmpvalue = $this->GetEcn();
2013
			if (!empty($tmpvalue)) {
2014
				if ($comma) {
2015
					$pfq_rule .= " ,";
2016
				}
2017
				$comma = 1;
2018
				$pfq_rule .= " ecn ";
2019
			}
2020
			$tmpvalue = $this->GetCodel();
2021
			if (!empty($tmpvalue)) {
2022
				if ($comma) {
2023
					$pfq_rule .= " ,";
2024
				}
2025
				$comma = 1;
2026
				$pfq_rule .= " codel ";
2027
			}
2028
			$tmpvalue = $this->GetDefault();
2029
			if (!empty($tmpvalue)) {
2030
				if ($comma) {
2031
					$pfq_rule .= " ,";
2032
				}
2033
				$comma = 1;
2034
				$pfq_rule .= " default ";
2035
				$default = true;
2036
			}
2037

    
2038
			if ($this->GetRealtime() <> "")	 {
2039
				if ($comma) {
2040
					$pfq_rule .= " , ";
2041
				}
2042
				if ($this->GetR_m1()  <> "" && $this->GetR_d() <> "" && $this->GetR_m2() <> "") {
2043
					$pfq_rule .= " realtime (".$this->GetR_m1() . ", " . $this->GetR_d().", ". $this->GetR_m2() .") ";
2044
				} else	if ($this->GetR_m2() <> "") {
2045
					$pfq_rule .= " realtime " . $this->GetR_m2();
2046
				}
2047
				$comma = 1;
2048
			}
2049
			if ($this->GetLinkshare() <> "") {
2050
				if ($comma) {
2051
					$pfq_rule .= " ,";
2052
				}
2053
				if ($this->GetL_m1() <> "" && $this->GetL_d() <> "" && $this->GetL_m2() <> "") {
2054
					$pfq_rule .= " linkshare (".$this->GetL_m1(). ", ". $this->GetL_d(). ", ". $this->GetL_m2(). ") ";
2055
				} else if ($this->GetL_m2() <> "") {
2056
					$pfq_rule .= " linkshare " . $this->GetL_m2() . " ";
2057
				}
2058
				$comma = 1;
2059
			}
2060
			if ($this->GetUpperlimit() <> "") {
2061
				if ($comma) {
2062
					$pfq_rule .= " ,";
2063
				}
2064
				if ($this->GetU_m1() <> "" && $this->GetU_d() <> "" && $this->GetU_m2() <> "") {
2065
							$pfq_rule .= " upperlimit (".$this->GetU_m1().", ". $this->GetU_d().", ". $this->GetU_m2(). ") ";
2066
				} else if ($this->GetU_m2() <> "") {
2067
					$pfq_rule .= " upperlimit " . $this->GetU_m2() . " ";
2068
				}
2069
			}
2070
			$pfq_rule .= " ) ";
2071
		}
2072
		if (count($this->subqueues)) {
2073
			$i = count($this->subqueues);
2074
			$pfq_rule .= " { ";
2075
			foreach ($this->subqueues as $qkey => $qnone) {
2076
				if ($i > 1) {
2077
					$i--;
2078
					$pfq_rule .= " {$qkey}, ";
2079
				} else {
2080
					$pfq_rule .= " {$qkey} ";
2081
				}
2082
			}
2083
			$pfq_rule .= " } \n";
2084
			foreach ($this->subqueues as $q) {
2085
				$pfq_rule .= $q->build_rules($default);
2086
			}
2087
		}
2088

    
2089
		$pfq_rule .= " \n";
2090

    
2091
		return $pfq_rule;
2092
	}
2093

    
2094
	function build_javascript() {
2095

    
2096
		$javascript = <<<EOJS
2097
<script type="text/javascript">
2098
//<![CDATA[
2099
	events.push(function(){
2100

    
2101
		// Disables the specified input element
2102
		function disableInput(id, disable) {
2103
			$('#' + id).prop("disabled", disable);
2104
		}
2105

    
2106
		// Upperlimit
2107
		function enable_upperlimit() {
2108
			disableInput('upperlimit1', !$('#upperlimit').prop('checked'));
2109
			disableInput('upperlimit2', !$('#upperlimit').prop('checked'));
2110
			disableInput('upperlimit3', !$('#upperlimit').prop('checked'));
2111
		}
2112

    
2113
		$('#upperlimit').click(function () {
2114
			enable_upperlimit();
2115
		});
2116

    
2117
		enable_upperlimit();
2118

    
2119
		// realtime
2120
		function enable_realtime() {
2121
			disableInput('realtime1', !$('#realtime').prop('checked'));
2122
			disableInput('realtime2', !$('#realtime').prop('checked'));
2123
			disableInput('realtime3', !$('#realtime').prop('checked'));
2124
		}
2125

    
2126
		$('#realtime').click(function () {
2127
			enable_realtime();
2128
		});
2129

    
2130
		enable_realtime();
2131

    
2132
		// linkshare
2133
		function enable_linkshare() {
2134
			disableInput('linkshare1', !$('#linkshare').prop('checked'));
2135
			disableInput('linkshare2', !$('#linkshare').prop('checked'));
2136
			disableInput('linkshare3', !$('#linkshare').prop('checked'));
2137
		}
2138

    
2139
		$('#linkshare').click(function () {
2140
			enable_linkshare();
2141
		});
2142

    
2143
		enable_linkshare();
2144
	});
2145
//]]>
2146
</script>
2147
EOJS;
2148

    
2149
		return $javascript;
2150
	}
2151

    
2152
	function build_form() {
2153

    
2154
		$sform = parent::build_form();
2155

    
2156
		$section = new Form_Section('Service Curve (sc)');
2157

    
2158
		$group = new Form_Group('Bandwidth');
2159

    
2160
		$group->add(new Form_Input(
2161
			'bandwidth',
2162
			null,
2163
			'number',
2164
			$this->GetBandwidth()
2165
		));
2166

    
2167
		$group->add(new Form_Select(
2168
			'bandwidthtype',
2169
			null,
2170
			$this->GetBwscale(),
2171
			array('Kb' => 'Kb',
2172
				  'Mb' => 'Mb',
2173
				  'Gb' => 'Gb',
2174
				  'b' => 'b')
2175
		));
2176

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

    
2179
		$section->add($group);
2180

    
2181
		$group = new Form_Group('Max bandwidth for queue.');
2182

    
2183
		$group->add(new Form_Checkbox(
2184
			'upperlimit',
2185
			null,
2186
			'Upper Limit',
2187
			($this->GetUpperlimit()<> "")
2188
		));
2189

    
2190
		$group->add(new Form_Input(
2191
			'upperlimit1',
2192
			null,
2193
			'text',
2194
			$this->GetU_m1()
2195
		));
2196

    
2197
		$group->add(new Form_Input(
2198
			'upperlimit2',
2199
			null,
2200
			'text',
2201
			$this->GetU_d()
2202
		));
2203
		$group->add(new Form_Input(
2204
			'upperlimit3',
2205
			null,
2206
			'text',
2207
			$this->GetU_m2()
2208
		));
2209

    
2210

    
2211
		$section->add($group);
2212

    
2213
		$group = new Form_Group('Min bandwidth for queue.');
2214

    
2215
		$group->add(new Form_Checkbox(
2216
			'realtime',
2217
			null,
2218
			'Real Time',
2219
			($this->GetRealtime()<> "")
2220
		));
2221

    
2222
		$group->add(new Form_Input(
2223
			'realtime1',
2224
			null,
2225
			'text',
2226
			$this->GetR_m1()
2227
		));
2228

    
2229
		$group->add(new Form_Input(
2230
			'realtime2',
2231
			null,
2232
			'text',
2233
			$this->GetR_d()
2234
		));
2235
		$group->add(new Form_Input(
2236
			'realtime3',
2237
			null,
2238
			'text',
2239
			$this->GetR_m2()
2240
		));
2241

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

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

    
2246
		$group->add(new Form_Checkbox(
2247
			'linkshare',
2248
			null,
2249
			'Link Share',
2250
			($this->GetLinkshare()<> "")
2251
		));
2252

    
2253
		$group->add(new Form_Input(
2254
			'linkshare1',
2255
			null,
2256
			'text',
2257
			$this->GetL_m1()
2258
		));
2259

    
2260
		$group->add(new Form_Input(
2261
			'linkshare2',
2262
			null,
2263
			'text',
2264
			$this->GetL_d()
2265
		));
2266
		$group->add(new Form_Input(
2267
			'linkshare3',
2268
			null,
2269
			'text',
2270
			$this->GetL_m2()
2271
		));
2272

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

    
2278
		$section->add($group);
2279

    
2280
		$sform->add($section);
2281

    
2282
		return($sform);
2283
	}
2284

    
2285
	function update_altq_queue_data(&$data) {
2286
		$this->ReadConfig($data);
2287
	}
2288

    
2289
	function wconfig() {
2290
		$cflink =& get_reference_to_me_in_config($this->GetLink());
2291
		if (!is_array($cflink)) {
2292
			$cflink = array();
2293
		}
2294
		$cflink['name'] = $this->GetQname();
2295
		$cflink['interface'] = $this->GetInterface();
2296
		$cflink['qlimit'] = trim($this->GetQlimit());
2297
		if (empty($cflink['qlimit'])) {
2298
			unset($cflink['qlimit']);
2299
		}
2300
		$cflink['priority'] = $this->GetQpriority();
2301
		if (empty($cflink['priority'])) {
2302
			unset($cflink['priority']);
2303
		}
2304
		$cflink['description'] = $this->GetDescription();
2305
		if (empty($cflink['description'])) {
2306
			unset($cflink['description']);
2307
		}
2308
		$cflink['bandwidth'] = $this->GetBandwidth();
2309
		$cflink['bandwidthtype'] = $this->GetBwscale();
2310
		$cflink['enabled'] = $this->GetEnabled();
2311
		if (empty($cflink['enabled'])) {
2312
			unset($cflink['enabled']);
2313
		}
2314
		$cflink['default'] = $this->GetDefault();
2315
		if (empty($cflink['default'])) {
2316
			unset($cflink['default']);
2317
		}
2318
		$cflink['red'] = trim($this->GetRed());
2319
		if (empty($cflink['red'])) {
2320
			unset($cflink['red']);
2321
		}
2322
		$cflink['rio'] = $this->GetRio();
2323
		if (empty($cflink['rio'])) {
2324
			unset($cflink['rio']);
2325
		}
2326
		$cflink['ecn'] = trim($this->GetEcn());
2327
		if (empty($cflink['ecn'])) {
2328
			unset($cflink['ecn']);
2329
		}
2330
		$cflink['codel'] = trim($this->GetCodel());
2331
		if (empty($cflink['codel'])) {
2332
			unset($cflink['codel']);
2333
		}
2334
		if ($this->GetLinkshare() <> "") {
2335
			if ($this->GetL_m1() <> "") {
2336
				$cflink['linkshare1'] = $this->GetL_m1();
2337
				$cflink['linkshare2'] = $this->GetL_d();
2338
				$cflink['linkshare'] = "on";
2339
			} else {
2340
				unset($cflink['linkshare']);
2341
				unset($cflink['linkshare1']);
2342
				unset($cflink['linkshare2']);
2343
			}
2344
			if ($this->GetL_m2() <> "") {
2345
				$cflink['linkshare3'] = $this->GetL_m2();
2346
				$cflink['linkshare'] = "on";
2347
			} else {
2348
				unset($cflink['linkshare']);
2349
				unset($cflink['linkshare3']);
2350
			}
2351
		} else {
2352
			unset($cflink['linkshare']);
2353
			unset($cflink['linkshare1']);
2354
			unset($cflink['linkshare2']);
2355
			unset($cflink['linkshare3']);
2356
		}
2357
		if ($this->GetRealtime() <> "") {
2358
			if ($this->GetR_m1() <> "") {
2359
				$cflink['realtime1'] = $this->GetR_m1();
2360
				$cflink['realtime2'] = $this->GetR_d();
2361
				$cflink['realtime'] = "on";
2362
			} else {
2363
				unset($cflink['realtime']);
2364
				unset($cflink['realtime1']);
2365
				unset($cflink['realtime2']);
2366
			}
2367
			if ($this->GetR_m2() <> "") {
2368
				$cflink['realtime3'] = $this->GetR_m2();
2369
				$cflink['realtime'] = "on";
2370
			} else {
2371
				unset($cflink['realtime']);
2372
				unset($cflink['realtime3']);
2373
			}
2374
		} else {
2375
			unset($cflink['realtime']);
2376
			unset($cflink['realtime1']);
2377
			unset($cflink['realtime2']);
2378
			unset($cflink['realtime3']);
2379
		}
2380
		if ($this->GetUpperlimit() <> "") {
2381
			if ($this->GetU_m1() <> "") {
2382
				$cflink['upperlimit1'] = $this->GetU_m1();
2383
				$cflink['upperlimit2'] = $this->GetU_d();
2384
				$cflink['upperlimit'] = "on";
2385
			} else {
2386
				unset($cflink['upperlimit']);
2387
				unset($cflink['upperlimit1']);
2388
				unset($cflink['upperlimit2']);
2389
			}
2390
			if ($this->GetU_m2() <> "") {
2391
				$cflink['upperlimit3'] = $this->GetU_m2();
2392
				$cflink['upperlimit'] = "on";
2393
			} else {
2394
				unset($cflink['upperlimit']);
2395
				unset($cflink['upperlimit3']);
2396
			}
2397
		} else {
2398
			unset($cflink['upperlimit']);
2399
			unset($cflink['upperlimit1']);
2400
			unset($cflink['upperlimit2']);
2401
			unset($cflink['upperlimit3']);
2402
		}
2403
	}
2404
}
2405

    
2406
class cbq_queue extends priq_queue {
2407
	var $qborrow = "";
2408

    
2409
	function GetBorrow() {
2410
		return $this->qborrow;
2411
	}
2412
	function SetBorrow($borrow) {
2413
		$this->qborrow = $borrow;
2414
	}
2415
	function CanHaveChildren() {
2416
		return true;
2417
	}
2418

    
2419
	function &add_queue($interface, &$qname, &$path, &$input_errors) {
2420

    
2421
		if (!is_array($this->subqueues)) {
2422
			$this->subqueues = array();
2423
		}
2424
		$q =& new cbq_queue();
2425
		$q->SetInterface($this->GetInterface());
2426
		$q->SetParent($this);
2427
		$q->ReadConfig($qname);
2428
		$q->validate_input($qname, $input_errors);
2429
		if (count($input_errors)) {
2430
			log_error("SHAPER: could not create queue " . $q->GetQname() . " on interface {$interface} because: " . print_r($input_errors, true));
2431
			return $q;
2432
		}
2433
		switch ($q->GetBwscale()) {
2434
			case "%":
2435
				$myBw = $this->GetAvailableBandwidth() * $qname['bandwidth'] / 100;
2436
				break;
2437
			default:
2438
				$myBw = $qname['bandwidth'] * get_bandwidthtype_scale($q->GetBwscale());
2439
				break;
2440
		}
2441
		$q->SetAvailableBandwidth($myBw);
2442
		$this->SetAvailableBandwidth($this->GetAvailableBandwidth() - $myBw);
2443

    
2444
		$q->SetEnabled("on");
2445
		$q->SetLink($path);
2446
		$this->subqueues[$q->GetQName()] = &$q;
2447
		ref_on_altq_queue_list($this->GetQname(), $q->GetQname());
2448
		if (is_array($qname['queue'])) {
2449
			foreach ($qname['queue'] as $key1 => $que) {
2450
				array_push($path, $key1);
2451
				$q->add_queue($q->GetInterface(), $que, $path, $input_errors);
2452
				array_pop($path);
2453
			}
2454
		}
2455

    
2456
		return $q;
2457
	}
2458

    
2459
	function copy_queue($interface, &$cflink) {
2460

    
2461
		$cflink['interface'] = $interface;
2462
		$cflink['qlimit'] = trim($this->GetQlimit());
2463
		if (empty($clink['qlimit'])) {
2464
			unset($cflink['qlimit']);
2465
		}
2466
		$cflink['priority'] = trim($this->GetQpriority());
2467
		if (empty($cflink['priority'])) {
2468
			unset($cflink['priority']);
2469
		}
2470
		$cflink['name'] = $this->GetQname();
2471
		$cflink['description'] = trim($this->GetDescription());
2472
		if (empty($cflink['description'])) {
2473
			unset($cflink['description']);
2474
		}
2475
		$cflink['bandwidth'] = $this->GetBandwidth();
2476
		$cflink['bandwidthtype'] = $this->GetBwscale();
2477
		$cflink['enabled'] = trim($this->GetEnabled());
2478
		if (empty($cflink['enabled'])) {
2479
			unset($cflink['enabled']);
2480
		}
2481
		$cflink['default'] = trim($this->GetDefault());
2482
		if (empty($cflink['default'])) {
2483
			unset($cflink['default']);
2484
		}
2485
		$cflink['red'] = trim($this->GetRed());
2486
		if (empty($cflink['red'])) {
2487
			unset($cflink['red']);
2488
		}
2489
		$cflink['rio'] = trim($this->GetRio());
2490
		if (empty($cflink['rio'])) {
2491
			unset($cflink['rio']);
2492
		}
2493
		$cflink['ecn'] = trim($this->GetEcn());
2494
		if (empty($cflink['ecn'])) {
2495
			unset($cflink['ecn']);
2496
		}
2497
		$cflink['borrow'] = trim($this->GetBorrow());
2498
		if (empty($cflink['borrow'])) {
2499
			unset($cflink['borrow']);
2500
		}
2501
		if (is_array($this->queues)) {
2502
			$cflinkp['queue'] = array();
2503
			foreach ($this->subqueues as $q) {
2504
				$cflink['queue'][$q->GetQname()] = array();
2505
				$q->copy_queue($interface, $cflink['queue'][$q->GetQname()]);
2506
			}
2507
		}
2508
	}
2509

    
2510
	/*
2511
	 * Should search even its children
2512
	 */
2513
	function &find_queue($interface, $qname) {
2514
		if ($qname == $this->GetQname()) {
2515
			return $this;
2516
		}
2517
		foreach ($this->subqueues as $q) {
2518
			$result =& $q->find_queue("", $qname);
2519
			if ($result) {
2520
				return $result;
2521
			}
2522
		}
2523
	}
2524

    
2525
	function &find_parentqueue($interface, $qname) {
2526
		if ($this->subqueues[$qname]) {
2527
			return $this;
2528
		}
2529
		foreach ($this->subqueues as $q) {
2530
			$result = $q->find_parentqueue("", $qname);
2531
			if ($result) {
2532
				return $result;
2533
			}
2534
		}
2535
	}
2536

    
2537
	function delete_queue() {
2538
		unref_on_altq_queue_list($this->GetQname());
2539
		cleanup_queue_from_rules($this->GetQname());
2540
		foreach ($this->subqueues as $q) {
2541
			$this->SetAvailableBandwidth($this->GetAvailableBandwidth() + $q->GetAvailableBandwidth());
2542
				$q->delete_queue();
2543
		}
2544
		unset_object_by_reference($this->GetLink());
2545
	}
2546

    
2547
	function validate_input($data, &$input_errors) {
2548
		parent::validate_input($data, $input_errors);
2549

    
2550
		if ($data['priority'] > 7) {
2551
				$input_errors[] = gettext("Priority must be an integer between 1 and 7.");
2552
		}
2553
		$reqdfields[] = "bandwidth";
2554
		$reqdfieldsn[] = gettext("Bandwidth");
2555
		$reqdfields[] = "bandwidthtype";
2556
		$reqdfieldsn[] = gettext("Bandwidthtype");
2557

    
2558
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
2559

    
2560
		if ($data['bandwidth'] && !is_numeric($data['bandwidth'])) {
2561
			$input_errors[] = gettext("Bandwidth must be an integer.");
2562
		}
2563

    
2564

    
2565
		if ($data['bandwidth'] < 0) {
2566
			$input_errors[] = gettext("Bandwidth cannot be negative.");
2567
		}
2568

    
2569
		if ($data['bandwidthtype'] == "%") {
2570
			if ($data['bandwidth'] > 100 || $data['bandwidth'] < 0) {
2571
				$input_errors[] = gettext("Bandwidth in percentage should be between 1 and 100 bounds.");
2572
			}
2573
		}
2574

    
2575
/*
2576
		$parent =& $this->GetParent();
2577
		switch ($data['bandwidthtype']) {
2578
		case "%":
2579
			$myBw = $parent->GetAvailableBandwidth() * floatval($data['bandwidth']) / 100;
2580
			break;
2581
		default:
2582
			$mybw = floatval($data['bandwidth']) * get_bandwidthtype_scale($data['bandwidthtype']);
2583
			break;
2584
		}
2585
		if ($parent->GetAvailableBandwidth() < floatval($myBw)) {
2586
			$input_errors[] = "The sum of the children bandwidth exceeds that of the parent.";
2587
		}
2588
 */
2589
	}
2590

    
2591
	function ReadConfig(&$q) {
2592
		parent::ReadConfig($q);
2593
		if (!empty($q['borrow'])) {
2594
			$this->SetBorrow("on");
2595
		} else {
2596
			$this->SetBorrow("");
2597
		}
2598
	}
2599

    
2600
	function build_javascript() {
2601
		return parent::build_javascript();
2602
	}
2603

    
2604
	function build_tree() {
2605
		$tree = " <li><a href=\"firewall_shaper.php?interface=" . $this->GetInterface()."&amp;queue=" . $this->GetQname()."&amp;action=show";
2606
		$tree .= "\" ";
2607
		$tmpvalue = trim($this->GetDefault());
2608
		if (!empty($tmpvalue)) {
2609
			$tree .= " class=\"navlnk\"";
2610
		}
2611
		$tree .= " >" . $this->GetQname() . "</a>";
2612
		if (is_array($this->subqueues)) {
2613
			$tree .= "<ul>";
2614
			foreach ($this->subqueues as $q)  {
2615
				$tree .= $q->build_tree();
2616
			}
2617
			$tree .= "</ul>";
2618
		}
2619
		$tree .= "</li>";
2620
		return $tree;
2621
	}
2622

    
2623
	/* Even this should take children into consideration */
2624
	function build_rules(&$default = false) {
2625
		$pfq_rule = "queue ". $this->qname;
2626
		if ($this->GetInterface()) {
2627
			$pfq_rule .= " on ".get_real_interface($this->GetInterface());
2628
		}
2629
		if ($this->GetBandwidth() && $this->GetBwscale()) {
2630
			$pfq_rule .= " bandwidth ".trim($this->GetBandwidth()).$this->GetBwscale();
2631
		}
2632
		$tmpvalue = $this->GetQpriority();
2633
		if (!empty($tmpvalue)) {
2634
			$pfq_rule .= " priority " . $this->GetQpriority();
2635
		}
2636
		$tmpvalue = trim($this->GetQlimit());
2637
		if (!empty($tmpvalue)) {
2638
			$pfq_rule .= " qlimit " . $this->GetQlimit();
2639
		}
2640
		if ($this->GetDefault() || $this->GetRed() || $this->GetRio() || $this->GetEcn() || $this->GetBorrow() || $this->GetCodel()) {
2641
			$pfq_rule .= " cbq ( ";
2642
			$tmpvalue = trim($this->GetRed());
2643
			if (!empty($tmpvalue)) {
2644
				$comma = 1;
2645
				$pfq_rule .= " red ";
2646
			}
2647
			$tmpvalue = trim($this->GetCodel());
2648
			if (!empty($tmpvalue)) {
2649
				$comma = 1;
2650
				$pfq_rule .= " codel ";
2651
			}
2652
			$tmpvalue = trim($this->GetRio());
2653
			if (!empty($tmpvalue)) {
2654
				if ($comma) {
2655
					$pfq_rule .= " ,";
2656
				}
2657
				$comma = 1;
2658
				$pfq_rule .= " rio ";
2659
			}
2660
			$tmpvalue = trim($this->GetEcn());
2661
			if (!empty($tmpvalue)) {
2662
				if ($comma) {
2663
					$pfq_rule .= " ,";
2664
				}
2665
				$comma = 1;
2666
				$pfq_rule .= " ecn ";
2667
			}
2668
			$tmpvalue = trim($this->GetDefault());
2669
			if (!empty($tmpvalue)) {
2670
				if ($comma) {
2671
					$pfq_rule .= " ,";
2672
				}
2673
				$comma = 1;
2674
				$pfq_rule .= " default ";
2675
				$default = true;
2676
			}
2677
			$tmpvalue = trim($this->GetBorrow());
2678
			if (!empty($tmpvalue)) {
2679
				if ($comma) {
2680
					$pfq_rule .= ", ";
2681
				}
2682
				$pfq_rule .= " borrow ";
2683
			}
2684
			$pfq_rule .= " ) ";
2685
		}
2686
		if (count($this->subqueues)) {
2687
			$i = count($this->subqueues);
2688
			$pfq_rule .= " { ";
2689
			foreach ($this->subqueues as $qkey => $qnone) {
2690
				if ($i > 1) {
2691
					$i--;
2692
					$pfq_rule .= " {$qkey}, ";
2693
				} else {
2694
					$pfq_rule .= " {$qkey} ";
2695
				}
2696
			}
2697
			$pfq_rule .= " } \n";
2698
			foreach ($this->subqueues as $q) {
2699
				$pfq_rule .= $q->build_rules($default);
2700
			}
2701
		}
2702

    
2703
		$pfq_rule .= " \n";
2704
		return $pfq_rule;
2705
	}
2706

    
2707
	function build_form() {
2708
		$sform = parent::build_form();
2709

    
2710
		$section = new Form_Section('');
2711

    
2712
		$group = new Form_Group('Bandwidth');
2713

    
2714
		$group->add(new Form_Input(
2715
			'bandwidth',
2716
			null,
2717
			'number',
2718
			$this->GetBandwidth()
2719
		));
2720

    
2721
		$group->add(new Form_Select(
2722
			'bandwidthtype',
2723
			null,
2724
			$this->GetBwscale(),
2725
			array('Kb' => 'Kb',
2726
				  'Mb' => 'Mb',
2727
				  'Gb' => 'Gb',
2728
				  'b' => 'b')
2729
		));
2730

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

    
2733
		$section->add($group);
2734

    
2735
		$section->addInput(new Form_Checkbox(
2736
			'borrow',
2737
			'Scheduler option',
2738
			'Borrow from other queues when available',
2739
			($this->GetBorrow() == "on")
2740
		));
2741

    
2742
		return $sform;
2743
	}
2744

    
2745
	function update_altq_queue_data(&$data) {
2746
		$this->ReadConfig($data);
2747
	}
2748

    
2749
	function wconfig() {
2750
		$cflink =& get_reference_to_me_in_config($this->GetLink());
2751
		if (!is_array($cflink)) {
2752
			$cflink = array();
2753
		}
2754
		$cflink['interface'] = $this->GetInterface();
2755
		$cflink['qlimit'] = trim($this->GetQlimit());
2756
		if (empty($cflink['qlimit'])) {
2757
			unset($cflink['qlimit']);
2758
		}
2759
		$cflink['priority'] = $this->GetQpriority();
2760
		if (empty($cflink['priority'])) {
2761
			unset($cflink['priority']);
2762
		}
2763
		$cflink['name'] = $this->GetQname();
2764
		$cflink['description'] = $this->GetDescription();
2765
		if (empty($cflink['description'])) {
2766
			unset($cflink['description']);
2767
		}
2768
		$cflink['bandwidth'] = $this->GetBandwidth();
2769
		$cflink['bandwidthtype'] = $this->GetBwscale();
2770
		$cflink['enabled'] = trim($this->GetEnabled());
2771
		if (empty($cflink['enabled'])) {
2772
			unset($cflink['enabled']);
2773
		}
2774
		$cflink['default'] = trim($this->GetDefault());
2775
		if (empty($cflink['default'])) {
2776
			unset($cflink['default']);
2777
		}
2778
		$cflink['red'] = trim($this->GetRed());
2779
		if (empty($cflink['red'])) {
2780
			unset($cflink['red']);
2781
		}
2782
		$cflink['rio'] = trim($this->GetRio());
2783
		if (empty($cflink['rio'])) {
2784
			unset($cflink['rio']);
2785
		}
2786
		$cflink['ecn'] = trim($this->GetEcn());
2787
		if (empty($cflink['ecn'])) {
2788
			unset($cflink['ecn']);
2789
		}
2790
		$cflink['codel'] = trim($this->GetCodel());
2791
		if (empty($cflink['codel'])) {
2792
			unset($cflink['codel']);
2793
		}
2794
		$cflink['borrow'] = trim($this->GetBorrow());
2795
		if (empty($cflink['borrow'])) {
2796
			unset($cflink['borrow']);
2797
		}
2798
	}
2799
}
2800

    
2801
class fairq_queue extends priq_queue {
2802
	var $hogs;
2803
	var $buckets;
2804

    
2805
	function GetBuckets() {
2806
		return $this->buckets;
2807
	}
2808
	function SetBuckets($buckets) {
2809
		$this->buckets = $buckets;
2810
	}
2811
	function GetHogs() {
2812
		return $this->hogs;
2813
	}
2814
	function SetHogs($hogs) {
2815
		$this->hogs = $hogs;
2816
	}
2817
	function CanHaveChildren() {
2818
		return false;
2819
	}
2820

    
2821

    
2822
	function copy_queue($interface, &$cflink) {
2823
		$cflink['interface'] = $interface;
2824
		$cflink['qlimit'] = $this->GetQlimit();
2825
		$cflink['priority'] = $this->GetQpriority();
2826
		$cflink['name'] = $this->GetQname();
2827
		$cflink['description'] = $this->GetDescription();
2828
		$cflink['bandwidth'] = $this->GetBandwidth();
2829
		$cflink['bandwidthtype'] = $this->GetBwscale();
2830
		$cflink['enabled'] = $this->GetEnabled();
2831
		$cflink['default'] = $this->GetDefault();
2832
		$cflink['red'] = $this->GetRed();
2833
		$cflink['rio'] = $this->GetRio();
2834
		$cflink['ecn'] = $this->GetEcn();
2835
		$cflink['buckets'] = $this->GetBuckets();
2836
		$cflink['hogs'] = $this->GetHogs();
2837
	}
2838

    
2839
	/*
2840
	 * Should search even its children
2841
	 */
2842
	function &find_queue($interface, $qname) {
2843
		if ($qname == $this->GetQname()) {
2844
			return $this;
2845
		}
2846
	}
2847

    
2848
	function find_parentqueue($interface, $qname) { return; }
2849

    
2850
	function delete_queue() {
2851
		unref_on_altq_queue_list($this->GetQname());
2852
		cleanup_queue_from_rules($this->GetQname());
2853
		unset_object_by_reference($this->GetLink());
2854
	}
2855

    
2856
	function validate_input($data, &$input_errors) {
2857
		parent::validate_input($data, $input_errors);
2858

    
2859
		if ($data['priority'] > 255) {
2860
				$input_errors[] = gettext("Priority must be an integer between 1 and 255.");
2861
		}
2862
		$reqdfields[] = "bandwidth";
2863
		$reqdfieldsn[] = gettext("Bandwidth");
2864
		$reqdfields[] = "bandwidthtype";
2865
		$reqdfieldsn[] = gettext("Bandwidthtype");
2866

    
2867
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
2868

    
2869
		if ($data['bandwidth'] && !is_numeric($data['bandwidth'])) {
2870
			$input_errors[] = gettext("Bandwidth must be an integer.");
2871
		}
2872

    
2873

    
2874
		if ($data['bandwidth'] < 0) {
2875
			$input_errors[] = gettext("Bandwidth cannot be negative.");
2876
		}
2877

    
2878

    
2879
		if ($data['bandwidthtype'] == "%") {
2880
			if ($data['bandwidth'] > 100 || $data['bandwidth'] < 0) {
2881
				$input_errors[] = gettext("Bandwidth in percentage should be between 1 and 100 bounds.");
2882
			}
2883
		}
2884

    
2885
/*
2886
		$parent =& $this->GetParent();
2887
		switch ($data['bandwidthtype']) {
2888
		case "%":
2889
			$myBw = $parent->GetAvailableBandwidth() * floatval($data['bandwidth']) / 100;
2890
		default:
2891
			$mybw = floatval($data['bandwidth']) * get_bandwidthtype_scale($data['bandwidthtype']);
2892
			break;
2893
		}
2894
		if ($parent->GetAvailableBandwidth() < floatval($myBw)) {
2895
			$input_errors[] = "The sum of children bandwidth exceeds that of the parent.";
2896
		}
2897
*/
2898
	}
2899

    
2900
	function ReadConfig(&$q) {
2901
		parent::ReadConfig($q);
2902
		if (!empty($q['buckets'])) {
2903
			$this->SetBuckets($q['buckets']);
2904
		} else {
2905
			$this->SetBuckets("");
2906
		}
2907
		if (!empty($q['hogs']) && is_valid_shaperbw($q['hogs'])) {
2908
			$this->SetHogs($q['hogs']);
2909
		} else {
2910
			$this->SetHogs("");
2911
		}
2912
	}
2913

    
2914
	function build_javascript() {
2915
		return parent::build_javascript();
2916
	}
2917

    
2918
	function build_tree() {
2919
		$tree = " <li><a href=\"firewall_shaper.php?interface=" .
2920
		$this->GetInterface()."&amp;queue=" . $this->GetQname()."&amp;action=show";
2921
		$tree .= "\" ";
2922
		$tmpvalue = trim($this->GetDefault());
2923
		if (!empty($tmpvalue)) {
2924
			$tree .= " class=\"navlnk\"";
2925
		}
2926
		$tree .= " >" . $this->GetQname() . "</a>";
2927
		$tree .= "</li>";
2928
		return $tree;
2929
	}
2930

    
2931
	/* Even this should take children into consideration */
2932
	function build_rules(&$default = false) {
2933
		$pfq_rule = "queue ". $this->qname;
2934
		if ($this->GetInterface()) {
2935
			$pfq_rule .= " on ".get_real_interface($this->GetInterface());
2936
		}
2937
		if ($this->GetBandwidth() && $this->GetBwscale()) {
2938
			$pfq_rule .= " bandwidth ".trim($this->GetBandwidth()).$this->GetBwscale();
2939
		}
2940
		$tmpvalue = trim($this->GetQpriority());
2941
		if (!empty($tmpvalue)) {
2942
			$pfq_rule .= " priority " . $this->GetQpriority();
2943
		}
2944
		$tmpvalue = trim($this->GetQlimit());
2945
		if (!empty($tmpvalue)) {
2946
			$pfq_rule .= " qlimit " . $this->GetQlimit();
2947
		}
2948
		if ($this->GetDefault() || $this->GetRed() || $this->GetRio() ||
2949
			$this->GetEcn() || $this->GetBuckets() || $this->GetHogs() || $this->GetCodel()) {
2950
			$pfq_rule .= " fairq ( ";
2951
			$tmpvalue = trim($this->GetRed());
2952
			if (!empty($tmpvalue)) {
2953
				$comma = 1;
2954
				$pfq_rule .= " red ";
2955
			}
2956
			$tmpvalue = trim($this->GetCodel());
2957
			if (!empty($tmpvalue)) {
2958
				$comma = 1;
2959
				$pfq_rule .= " codel ";
2960
			}
2961
			$tmpvalue = trim($this->GetRio());
2962
			if (!empty($tmpvalue)) {
2963
				if ($comma) {
2964
					$pfq_rule .= " ,";
2965
				}
2966
				$comma = 1;
2967
				$pfq_rule .= " rio ";
2968
			}
2969
			$tmpvalue = trim($this->GetEcn());
2970
			if (!empty($tmpvalue)) {
2971
				if ($comma) {
2972
					$pfq_rule .= " ,";
2973
				}
2974
				$comma = 1;
2975
				$pfq_rule .= " ecn ";
2976
			}
2977
			$tmpvalue = trim($this->GetDefault());
2978
			if (!empty($tmpvalue)) {
2979
				if ($comma) {
2980
					$pfq_rule .= " ,";
2981
				}
2982
				$comma = 1;
2983
				$pfq_rule .= " default ";
2984
				$default = true;
2985
			}
2986
			$tmpvalue = trim($this->GetBuckets());
2987
			if (!empty($tmpvalue)) {
2988
				if ($comma) {
2989
					$pfq_rule .= ", ";
2990
				}
2991
				$pfq_rule .= " buckets " . $this->GetBuckets() . " ";
2992
			}
2993
			$tmpvalue = trim($this->GetHogs());
2994
			if (!empty($tmpvalue)) {
2995
				if ($comma) {
2996
					$pfq_rule .= ", ";
2997
				}
2998
				$pfq_rule .= " hogs " . $this->GetHogs() . " ";
2999
			}
3000
			$pfq_rule .= " ) ";
3001
		}
3002

    
3003
		$pfq_rule .= " \n";
3004
		return $pfq_rule;
3005
	}
3006

    
3007
	function build_form() {
3008
		$form = parent::build_form();
3009

    
3010
		$section = new Form_Section('');
3011

    
3012
		$group = new Form_Group('Bandwidth');
3013

    
3014
		$group->add(new Form_Input(
3015
			'bandwidth',
3016
			null,
3017
			'number',
3018
			$this->GetBandwidth()
3019
		));
3020

    
3021
		$group->add(new Form_Select(
3022
			'bandwidthtype',
3023
			null,
3024
			$this->GetBwscale(),
3025
			array('Kb' => 'Kb',
3026
				  'Mb' => 'Mb',
3027
				  'Gb' => 'Gb',
3028
				  'b' => 'b')
3029
		));
3030

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

    
3033
		$section->add($group);
3034

    
3035

    
3036
		$form .= "<tr><td class=\"vncellreq\">" . gettext("Scheduler specific options") . "</td>";
3037
		$form .= "<td class=\"vtable\"><table><tr><td>";
3038
		$form .= "<input id=\"buckets\" name=\"buckets\" value=\"";
3039
		$tmpvalue = trim($this->GetBuckets());
3040
		if (!empty($tmpvalue)) {
3041
			$form .=  $this->GetBuckets();
3042
		}
3043
		$form .= "\" /> " . gettext("Number of buckets available.") . "<br /></td></tr>";
3044
		$form .= "<tr><td class=\"vtable\"><input id=\"hogs\" name=\"hogs\" value=\"";
3045
		$tmpvalue = trim($this->GetHogs());
3046
		if (!empty($tmpvalue)) {
3047
			$form .=  $this->GetHogs();
3048
		}
3049
		$form .= "\" /> " . gettext("Bandwidth limit for hosts to not saturate link.") . "<br /></td></tr>";
3050
		$form .= "</table></td></tr>";
3051
		return $form;
3052
	}
3053

    
3054
	function update_altq_queue_data(&$data) {
3055
		$this->ReadConfig($data);
3056
	}
3057

    
3058
	function wconfig() {
3059
		$cflink =& get_reference_to_me_in_config($this->GetLink());
3060
		if (!is_array($cflink)) {
3061
			$cflink = array();
3062
		}
3063
		$cflink['interface'] = $this->GetInterface();
3064
		$cflink['qlimit'] = trim($this->GetQlimit());
3065
		if (empty($cflink['qlimit'])) {
3066
			unset($cflink['qlimit']);
3067
		}
3068
		$cflink['priority'] = trim($this->GetQpriority());
3069
		if (empty($cflink['priority'])) {
3070
			unset($cflink['priority']);
3071
		}
3072
		$cflink['name'] = $this->GetQname();
3073
		$cflink['description'] = trim($this->GetDescription());
3074
		if (empty($cflink['description'])) {
3075
			unset($cflink['description']);
3076
		}
3077
		$cflink['bandwidth'] = $this->GetBandwidth();
3078
		$cflink['bandwidthtype'] = $this->GetBwscale();
3079
		$cflink['enabled'] = $this->GetEnabled();
3080
		if (empty($cflink['enabled'])) {
3081
			unset($cflink['enabled']);
3082
		}
3083
		$cflink['default'] = trim($this->GetDefault());
3084
		if (empty($cflink['default'])) {
3085
			unset($cflink['default']);
3086
		}
3087
		$cflink['red'] = trim($this->GetRed());
3088
		if (empty($cflink['red'])) {
3089
			unset($cflink['red']);
3090
		}
3091
		$cflink['rio'] = trim($this->GetRio());
3092
		if (empty($cflink['rio'])) {
3093
			unset($cflink['rio']);
3094
		}
3095
		$cflink['ecn'] = trim($this->GetEcn());
3096
		if (empty($cflink['ecn'])) {
3097
			unset($cflink['ecn']);
3098
		}
3099
		$cflink['codel'] = trim($this->GetCodel());
3100
		if (empty($cflink['codel'])) {
3101
			unset($cflink['codel']);
3102
		}
3103
		$cflink['buckets'] = trim($this->GetBuckets());
3104
		if (empty($cflink['buckets'])) {
3105
			unset($cflink['buckets']);
3106
		}
3107
		$cflink['hogs'] = trim($this->GetHogs());
3108
		if (empty($cflink['hogs'])) {
3109
			unset($cflink['hogs']);
3110
		}
3111
	}
3112
}
3113

    
3114

    
3115
/*
3116
 * dummynet(4) wrappers.
3117
 */
3118

    
3119

    
3120
/*
3121
 * List of respective objects!
3122
 */
3123
$dummynet_pipe_list = array();
3124

    
3125
class dummynet_class {
3126
	var $qname;
3127
	var $qnumber; /* dummynet(4) uses numbers instead of names; maybe integrate with pf the same as altq does?! */
3128
	var $qlimit;
3129
	var $description;
3130
	var $qenabled;
3131
	var $link;
3132
	var $qparent; /* link to upper class so we do things easily on WF2Q+ rule creation */
3133
	var $plr;
3134

    
3135
	var $buckets;
3136
	/* mask parameters */
3137
	var $mask;
3138
	var $noerror;
3139

    
3140
	/* Accessor functions */
3141
	function SetLink($link) {
3142
		$this->link = $link;
3143
	}
3144
	function GetLink() {
3145
		return $this->link;
3146
	}
3147
	function GetMask() {
3148
		if (!isset($this->mask["type"])) {
3149
			$this->mask["type"] = "none";
3150
		}
3151
		return $this->mask;
3152
	}
3153
	function SetMask($mask) {
3154
		$this->mask = $mask;
3155
	}
3156
	function &GetParent() {
3157
		return $this->qparent;
3158
	}
3159
	function SetParent(&$parent) {
3160
		$this->qparent = &$parent;
3161
	}
3162
	function GetEnabled() {
3163
		return $this->qenabled;
3164
	}
3165
	function SetEnabled($value) {
3166
		$this->qenabled = $value;
3167
	}
3168
	function CanHaveChildren() {
3169
		return false;
3170
	}
3171
	function CanBeDeleted() {
3172
		return true;
3173
	}
3174
	function GetQname() {
3175
		return $this->qname;
3176
	}
3177
	function SetQname($name) {
3178
		$this->qname = trim($name);
3179
	}
3180
	function GetQlimit() {
3181
		return $this->qlimit;
3182
	}
3183
	function SetQlimit($limit) {
3184
		$this->qlimit = $limit;
3185
	}
3186
	function GetDescription() {
3187
		return $this->description;
3188
	}
3189
	function SetDescription($str) {
3190
		$this->description = trim($str);
3191
	}
3192
	function GetFirstime() {
3193
		return $this->firsttime;
3194
	}
3195
	function SetFirsttime($number) {
3196
		$this->firsttime = $number;
3197
	}
3198
	function GetBuckets() {
3199
		return $this->buckets;
3200
	}
3201
	function SetBuckets($buckets) {
3202
		$this->buckets = $buckets;
3203
	}
3204
	function SetNumber($number) {
3205
		$this->qnumber = $number;
3206
	}
3207
	function GetNumber() {
3208
		return $this->qnumber;
3209
	}
3210
	function GetPlr() {
3211
		return $this->plr;
3212
	}
3213
	function SetPlr($plr) {
3214
		$this->plr = $plr;
3215
	}
3216

    
3217
	function build_javascript() {
3218
		$javascript .= "<script type=\"text/javascript\">\n";
3219
		$javascript .= "//<![CDATA[\n";
3220
		$javascript .= "function enable_maskbits(enable_over) {\n";
3221
		$javascript .= "var e = document.getElementById(\"mask\");\n";
3222
		$javascript .= "if ((e.options[e.selectedIndex].text == \"none\") || enable_over) {\n";
3223
		$javascript .= "document.iform.maskbits.disabled = 1;\n";
3224
		$javascript .= "document.iform.maskbits.value = \"\";\n";
3225
		$javascript .= "document.iform.maskbitsv6.disabled = 1;\n";
3226
		$javascript .= "document.iform.maskbitsv6.value = \"\";\n";
3227
		$javascript .= "} else {\n";
3228
		$javascript .= "document.iform.maskbits.disabled = 0;\n";
3229
		$javascript .= "document.iform.maskbitsv6.disabled = 0;\n";
3230
		$javascript .= "}}\n";
3231
		$javascript .= "//]]>\n";
3232
		$javascript .= "</script>\n";
3233
		return $javascript;
3234
	}
3235

    
3236
	function validate_input($data, &$input_errors) {
3237
		$reqdfields[] = "bandwidth";
3238
		$reqdfieldsn[] = gettext("Bandwidth");
3239
		/*$reqdfields[] = "burst";
3240
		$reqdfieldsn[] = gettext("Burst"); */
3241
		$reqdfields[] = "bandwidthtype";
3242
		$reqdfieldsn[] = gettext("Bandwidthtype");
3243
		$reqdfields[] = "newname";
3244
		$reqdfieldsn[] = gettext("Name");
3245

    
3246
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
3247

    
3248
		if ($data['plr'] && (!is_numeric($data['plr']) ||
3249
			($data['plr'] < 0) || ($data['plr'] > 1))) {
3250
				$input_errors[] = gettext("Plr must be a value between 0 and 1.");
3251
		}
3252
		if ($data['buckets'] && (!is_numeric($data['buckets']) ||
3253
			($data['buckets'] < 16) || ($data['buckets'] > 65535))) {
3254
				$input_errors[] = gettext("Buckets must be an integer between 16 and 65535.");
3255
		}
3256
		if ($data['qlimit'] && (!is_numeric($data['qlimit']))) {
3257
			$input_errors[] = gettext("Queue limit must be an integer");
3258
		}
3259
		if (!empty($data['newname']) && !preg_match("/^[a-zA-Z0-9_-]+$/", $data['newname'])) {
3260
			$input_errors[] = gettext("Queue names must be alphanumeric and _ or - only.");
3261
		}
3262
		if (!empty($data['name']) && !preg_match("/^[a-zA-Z0-9_-]+$/", $data['name'])) {
3263
			$input_errors[] = gettext("Queue names must be alphanumeric and _ or - only.");
3264
		}
3265
		if (isset($data['maskbits']) && ($data['maskbits'] <> "")) {
3266
			if ((!is_numeric($data['maskbits'])) || ($data['maskbits'] <= 0) || ($data['maskbits'] > 32)) {
3267
				$input_errors[] = gettext("IPV4 bit mask must be blank or numeric value between 1 and 32.");
3268
			}
3269
		}
3270
		if (isset($data['maskbitsv6']) && ($data['maskbitsv6'] <> "")) {
3271
			if ((!is_numeric($data['maskbitsv6'])) || ($data['maskbitsv6'] <= 0) || ($data['maskbitsv6'] > 128)) {
3272
				$input_errors[] = gettext("IPV6 bit mask must be blank or numeric value between 1 and 128.");
3273
			}
3274
		}
3275
	}
3276

    
3277
	function build_mask_rules(&$pfq_rule) {
3278
		$mask = $this->GetMask();
3279
		if (!empty($mask['type'])) {
3280
			if ($mask['type'] <> 'none') {
3281
				$pfq_rule .= " mask";
3282
			}
3283
			switch ($mask['type']) {
3284
				case 'srcaddress':
3285
					if (!empty($mask['bitsv6']) && ($mask['bitsv6'] <> "")) {
3286
						$pfq_rule .= " src-ip6 /" . $mask['bitsv6'];
3287
					} else {
3288
						$pfq_rule .= " src-ip6 /128";
3289
					}
3290
					if (!empty($mask['bits']) && ($mask['bits'] <> "")) {
3291
						$pfq_rule .= sprintf(" src-ip 0x%x", gen_subnet_mask_long($mask['bits']));
3292
					} else {
3293
						$pfq_rule .= " src-ip 0xffffffff";
3294
					}
3295
					break;
3296
				case 'dstaddress':
3297
					if (!empty($mask['bitsv6']) && ($mask['bitsv6'] <> "")) {
3298
						$pfq_rule .= " dst-ip6 /" . $mask['bitsv6'];
3299
					} else {
3300
						$pfq_rule .= " dst-ip6 /128";
3301
					}
3302
					if (!empty($mask['bits']) && ($mask['bits'] <> "")) {
3303
						$pfq_rule .= sprintf(" dst-ip 0x%x", gen_subnet_mask_long($mask['bits']));
3304
					} else {
3305
						$pfq_rule .= " dst-ip 0xffffffff";
3306
					}
3307
					break;
3308
				default:
3309
					break;
3310
			}
3311
		}
3312
	}
3313

    
3314
}
3315

    
3316
class dnpipe_class extends dummynet_class {
3317
	var $delay;
3318
	var $qbandwidth = array();
3319
	var $qbandwidthtype;
3320

    
3321
		/* This is here to help on form building and building rules/lists */
3322
	var $subqueues = array();
3323

    
3324
	function CanHaveChildren() {
3325
		return true;
3326
	}
3327
	function SetDelay($delay) {
3328
		$this->delay = $delay;
3329
	}
3330
	function GetDelay() {
3331
		return $this->delay;
3332
	}
3333
	function delete_queue() {
3334
		cleanup_dnqueue_from_rules($this->GetQname());
3335
		foreach ($this->subqueues as $q) {
3336
			$q->delete_queue();
3337
		}
3338
		unset_dn_object_by_reference($this->GetLink());
3339
		@pfSense_pipe_action("pipe delete " . $this->GetNumber());
3340
	}
3341
	function GetBandwidth() {
3342
		return $this->qbandwidth;
3343
	}
3344
	function SetBandwidth($bandwidth) {
3345
		$this->qbandwidth = $bandwidth;
3346
	}
3347
	function GetBurst() {
3348
		return $this->qburst;
3349
	}
3350
	function SetBurst($burst) {
3351
		$this->qburst = $burst;
3352
	}
3353

    
3354
	function &add_queue($interface, &$queue, &$path, &$input_errors) {
3355

    
3356
		if (!is_array($this->subqueues)) {
3357
			$this->subqueues = array();
3358
		}
3359

    
3360
		$q =& new dnqueue_class();
3361
		$q->SetLink($path);
3362
		$q->SetEnabled("on");
3363
		$q->SetPipe($this->GetQname());
3364
		$q->SetParent($this);
3365
		$q->ReadConfig($queue);
3366
		$q->validate_input($queue, $input_errors);
3367
		if (count($input_errors)) {
3368
			log_error("SHAPER: could not create queue " . $q->GetQname() . " on interface {$interface} because: " . print_r($input_errors, true));
3369
			return $q;
3370
		}
3371
		$number = dnqueue_find_nextnumber();
3372
		$q->SetNumber($number);
3373
		$this->subqueues[$q->GetQname()] = &$q;
3374

    
3375
		return $q;
3376
	}
3377

    
3378
	function &get_queue_list(&$q = null) {
3379
		$qlist = array();
3380

    
3381
		$qlist[$this->GetQname()] = $this->GetNumber();
3382
		if (is_array($this->subqueues)) {
3383
			foreach ($this->subqueues as $queue) {
3384
				$queue->get_queue_list($qlist);
3385
			}
3386
		}
3387
		return $qlist;
3388
	}
3389

    
3390
	/*
3391
	 * Should search even its children
3392
	 */
3393
	function &find_queue($pipe, $qname) {
3394
		if ($qname == $this->GetQname()) {
3395
			return $this;
3396
		}
3397
		foreach ($this->subqueues as $q) {
3398
			$result =& $q->find_queue("", $qname);
3399
			if ($result) {
3400
				return $result;
3401
			}
3402
		}
3403
	}
3404

    
3405
	function &find_parentqueue($pipe, $qname) {
3406
		return NULL;
3407
	}
3408

    
3409
	function validate_input($data, &$input_errors) {
3410
		parent::validate_input($data, $input_errors);
3411

    
3412
		$schedule = 0;
3413
		$schedulenone = 0;
3414
		$entries = 0;
3415
		/* XXX: Really no better way? */
3416
		for ($i = 0; $i < 2900; $i++) {
3417
			if (!empty($data["bwsched{$i}"])) {
3418
				if ($data["bwsched{$i}"] != "none") {
3419
					$schedule++;
3420
				} else {
3421
					$schedulenone++;
3422
				}
3423
			}
3424
			if (!empty($data["bandwidth{$i}"])) {
3425
				if (!is_numeric($data["bandwidth{$i}"])) {
3426
					$input_errors[] = sprintf(gettext("Bandwidth for schedule %s must be an integer."), $data["bwsched{$i}"]);
3427
				} else if (($data["burst{$i}"] != "") && (!is_numeric($data["burst{$i}"]))) {
3428
					$input_errors[] = sprintf(gettext("Burst for schedule %s must be an integer."), $data["bwsched{$i}"]);
3429
				} else {
3430
					$entries++;
3431
				}
3432
			}
3433
		}
3434
		if ($schedule == 0 && $entries > 1) {
3435
			$input_errors[] = gettext("You need to specify a schedule for every additional entry");
3436
		}
3437
		if ($schedulenone > 0 && $entries > 1) {
3438
			$input_errors[] = gettext("If more than one bandwidth configured all schedules need to be selected");
3439
		}
3440
		if ($entries == 0) {
3441
			$input_errors[] = gettext("At least one bw specification is necessary");
3442
		}
3443
		if ($data['delay'] && (!is_numeric($data['delay']))) {
3444
			$input_errors[] = gettext("Delay must be an integer.");
3445
		}
3446
	}
3447

    
3448
	function ReadConfig(&$q) {
3449
		if (!empty($q['name']) && !empty($q['newname']) && $q['name'] != $q['newname']) {
3450
			$this->SetQname($q['newname']);
3451
		} else if (!empty($q['newname'])) {
3452
			$this->SetQname($q['newname']);
3453
		} else {
3454
			$this->SetQname($q['name']);
3455
		}
3456
		$this->SetNumber($q['number']);
3457

    
3458
		if (!empty($_POST)) {
3459
			$bandwidth = array();
3460
			/* XXX: Really no better way? */
3461
			for ($i = 0; $i < 2900; $i++) {
3462
				if (isset($q["bandwidth{$i}"]) && $q["bandwidth{$i}"] <> "") {
3463
					$bw = array();
3464
					$bw['bw'] = $q["bandwidth{$i}"];
3465
					$bw['burst'] = $q["burst{$i}"];
3466
					if (isset($q["bwtype{$i}"]) && $q["bwtype{$i}"]) {
3467
						$bw['bwscale'] = $q["bwtype{$i}"];
3468
					}
3469
					if (isset($q["bwsched{$i}"]) && $q["bwsched{$i}"]) {
3470
						$bw['bwsched'] = $q["bwsched{$i}"];
3471
					}
3472
					$bandwidth[] = $bw;
3473
				}
3474
			}
3475
			$this->SetBandwidth($bandwidth);
3476
		}
3477

    
3478
		if (is_array($q['bandwidth']) && is_array($q['bandwidth']['item'])) {
3479
			$this->SetBandwidth($q['bandwidth']['item']);
3480
			$this->SetBurst($q['burst']['item']);
3481
		}
3482

    
3483
		if (isset($q['qlimit']) && $q['qlimit'] <> "") {
3484
			$this->SetQlimit($q['qlimit']);
3485
		} else {
3486
			$this->SetQlimit("");
3487
		}
3488
		if (isset($q['mask']) && $q['mask'] <> "") {
3489
			$masktype = $q['mask'];
3490
		} else {
3491
			$masktype = "";
3492
		}
3493
		if (isset($q['maskbits']) && $q['maskbits'] <> "") {
3494
			$maskbits = $q['maskbits'];
3495
		} else {
3496
			$maskbits = "";
3497
		}
3498
		if (isset($q['maskbitsv6']) && $q['maskbitsv6'] <> "") {
3499
			$maskbitsv6 = $q['maskbitsv6'];
3500
		} else {
3501
			$maskbitsv6 = "";
3502
		}
3503
		$this->SetMask(array("type" => $masktype, "bits" => $maskbits, "bitsv6" => $maskbitsv6));
3504
		if (isset($q['buckets']) && $q['buckets'] <> "") {
3505
			$this->SetBuckets($q['buckets']);
3506
		} else {
3507
			$this->SetBuckets("");
3508
		}
3509
		if (isset($q['plr']) && $q['plr'] <> "") {
3510
			$this->SetPlr($q['plr']);
3511
		} else {
3512
			$this->SetPlr("");
3513
		}
3514
		if (isset($q['delay']) && $q['delay'] <> "") {
3515
			$this->SetDelay($q['delay']);
3516
		} else {
3517
			$this->SetDelay(0);
3518
		}
3519
		if (isset($q['description']) && $q['description'] <> "") {
3520
			$this->SetDescription($q['description']);
3521
		} else {
3522
			$this->SetDescription("");
3523
		}
3524
		$this->SetEnabled($q['enabled']);
3525

    
3526
	}
3527

    
3528
	function build_tree() {
3529
		$tree = " <li><a href=\"firewall_shaper_vinterface.php?pipe=" . $this->GetQname() ."&amp;queue=".$this->GetQname() ."&amp;action=show\">";
3530
		$tree .= $this->GetQname() . "</a>";
3531
		if (is_array($this->subqueues)) {
3532
			$tree .= "<ul>";
3533
			foreach ($this->subqueues as $q)  {
3534
				$tree .= $q->build_tree();
3535
			}
3536
			$tree .= "</ul>";
3537
		}
3538
		$tree .= "</li>";
3539

    
3540
		return $tree;
3541
	}
3542

    
3543
	function build_rules() {
3544
		global $config, $time_based_rules;
3545

    
3546
		if ($this->GetEnabled() == "") {
3547
			return;
3548
		}
3549

    
3550
		$pfq_rule = "\npipe ". $this->GetNumber() . " config ";
3551
		$found = false;
3552
		$bandwidth = $this->GetBandwidth();
3553
		if (is_array($bandwidth)) {
3554
			foreach ($bandwidth as $bw) {
3555
				if ($bw['bwsched'] != "none") {
3556
					$time_based_rules = true;
3557
					if (is_array($config['schedules']) && is_array($config['schedules']['schedule'])) {
3558
						foreach ($config['schedules']['schedule'] as $schedule) {
3559
							if ($bw['bwsched'] == $schedule['name']) {
3560
								if (filter_get_time_based_rule_status($schedule)) {
3561
									$pfq_rule .= " bw ".trim($bw['bw']).$bw['bwscale'];
3562
									if (is_numeric($bw['burst']) && ($bw['burst'] > 0)) {
3563
										$pfq_rule .= " burst ".trim($bw['burst']);
3564
									}
3565
									$found = true;
3566
									break;
3567
								}
3568
							}
3569
						}
3570
					} else {
3571
						$pfq_rule .= " bw 0";
3572
						$found = true;
3573
						break;
3574
					}
3575
				} else {
3576
					$pfq_rule .= " bw ".trim($bw['bw']).$bw['bwscale'];
3577
					if (is_numeric($bw['burst']) && ($bw['burst'] > 0)) {
3578
						$pfq_rule .= " burst ".trim($bw['burst']);
3579
					}
3580
					$found = true;
3581
					break;
3582
				}
3583
			}
3584
			if ($found == false) {
3585
				$pfq_rule .= " bw 0";
3586
			}
3587
		} else {
3588
			$pfq_rule .= " bw 0";
3589
		}
3590

    
3591
		if ($this->GetQlimit()) {
3592
			$pfq_rule .= " queue " . $this->GetQlimit();
3593
		}
3594
		if ($this->GetPlr()) {
3595
			$pfq_rule .= " plr " . $this->GetPlr();
3596
		}
3597
		if ($this->GetBuckets()) {
3598
			$pfq_rule .= " buckets " . $this->GetBuckets();
3599
		}
3600
		if ($this->GetDelay()) {
3601
			$pfq_rule .= " delay " . $this->GetDelay();
3602
		}
3603
		$this->build_mask_rules($pfq_rule);
3604

    
3605
		$pfq_rule .= "\n";
3606

    
3607
		if (!empty($this->subqueues) && count($this->subqueues) > 0) {
3608
			foreach ($this->subqueues as $q) {
3609
				$pfq_rule .= $q->build_rules();
3610
			}
3611
		}
3612
		$pfq_rule .= " \n";
3613

    
3614
		return $pfq_rule;
3615
	}
3616

    
3617
	function update_dn_data(&$data) {
3618
		$this->ReadConfig($data);
3619
	}
3620

    
3621
	function build_javascript() {
3622
		global $g, $config;
3623

    
3624
		$javasr = parent::build_javascript();
3625

    
3626
		//build list of schedules
3627
		$schedules = "<option value='none'>none</option>";
3628
		if (is_array($config['schedules']) && is_array($config['schedules']['schedule'])) {
3629
			foreach ($config['schedules']['schedule'] as $schedule) {
3630
				if ($schedule['name'] <> "") {
3631
					$schedules .= "<option value='{$schedule['name']}'>{$schedule['name']}</option>";
3632
				}
3633
			}
3634
		}
3635
		$bwopt = "";
3636
		foreach (array("Kb" => "Kbit/s", "Mb" => "Mbit/s", "Gb" => "Gbit/s", "b" => "Bit/s") as $bwidx => $bw) {
3637
			$bwopt .= "<option value='{$bwidx}'>{$bw}</option>";
3638
		}
3639

    
3640
		$javasr .= <<<EOD
3641
<script type='text/javascript'>
3642
//<![CDATA[
3643
var addBwRowTo = (function() {
3644

    
3645
	return (function (tableId) {
3646

    
3647
	var table = document.getElementById(tableId);
3648
	var totalrows = table.rows.length -1;
3649

    
3650
	var row = table.insertRow(totalrows + 1);
3651
	var cell1 = row.insertCell(0);
3652
	var cell2 = row.insertCell(1);
3653
	var cell3 = row.insertCell(2);
3654
	var cell4 = row.insertCell(3);
3655

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

    
3661
	});
3662
})();
3663

    
3664
function removeBwRow(el) {
3665
	var d = el.parentNode.parentNode.rowIndex;
3666
	document.getElementById('maintable').deleteRow(d);
3667
}
3668
//]]>
3669
</script>
3670

    
3671
EOD;
3672

    
3673
		return $javasr;
3674
	}
3675

    
3676
	// Compose a table of bandwidths that can then be inserted into the form using a Form_StaticText
3677
	// THe table has been "Bootstrapped" to match the we b design while maintaining compatibility with
3678
	// with the javascript in this class
3679
	function build_bwtable() {
3680
		global $config;
3681

    
3682
		$bandwidth = $this->GetBandwidth();
3683
				//build list of schedules
3684
		$schedules = array();
3685
		$schedules[] = "none";//leave none to leave rule enabled all the time
3686
		if (is_array($config['schedules']) && is_array($config['schedules']['schedule'])) {
3687
			foreach ($config['schedules']['schedule'] as $schedule) {
3688
				if ($schedule['name'] <> "") {
3689
					$schedules[] = $schedule['name'];
3690
				}
3691
			}
3692
		}
3693

    
3694
		$form = '<table id="maintable" class="table table-hover table-condensed">';
3695
		$form .= "<thead><tr>";
3696
		$form .= "<th><div id='onecolumn'>Bandwidth</div></th>";
3697
		//$form .= "<td width='35%'><div id='fifthcolumn'>Burst</div></td>";
3698
		$form .= "<th><div id='twocolumn'>Bw type</div></th>";
3699
		$form .= "<th><div id='thirdcolumn'>Schedule</div></th>";
3700
		$form .= "<th><div id='fourthcolumn'></div></th>";
3701
		$form .= "</tr></thead>";
3702
		$form .= "<tbody>";
3703

    
3704
		if (is_array($bandwidth)) {
3705
			foreach ($bandwidth as $bwidx => $bw) {
3706
				$form .= '<tr><td>';
3707
				$form .= "<input class='form-control' type=\"text\" id=\"bandwidth{$bwidx}\" name=\"bandwidth{$bwidx}\" value=\"{$bw['bw']}\" />";
3708
				//$form .= "</td><td width='20%'>";
3709
				//$form .= "<input class='formfld unknown' size='10' type=\"text\" id=\"burst{$bwidx}\" name=\"burst{$bwidx}\" value=\"{$bw['burst']}\" />";
3710
				$form .= "</td><td>";
3711
				$form .= "<select id=\"bwtype{$bwidx}\" name=\"bwtype{$bwidx}\" class=\"form-control\">";
3712
				foreach (array("Kb" => "Kbit/s", "Mb" => "Mbit/s", "Gb" => "Gbit/s", "b" => "Bit/s") as $bwsidx => $bwscale) {
3713
					$form .= "<option value=\"{$bwsidx}\"";
3714
					if ($bw['bwscale'] == $bwsidx) {
3715
						$form .= " selected=\"selected\"";
3716
					}
3717
					$form .= ">{$bwscale}</option>";
3718
				}
3719
				$form .= "</select>";
3720
				$form .= "</td><td>";
3721
				$form .= "<select id=\"bwsched{$bwidx}\" name=\"bwsched{$bwidx}\" class=\"form-control\">";
3722
				foreach ($schedules as $schd) {
3723
					$selected = "";
3724
					if ($bw['bwsched'] == $schd) {
3725
						$selected = "selected=\"selected\"";
3726
					}
3727
					$form .= "<option value='{$schd}' {$selected}>{$schd}</option>";
3728
				}
3729
				$form .= "</select>";
3730
				$form .= "</td><td>";
3731
				$form .= '<a type="button" class="btn btn-default" onclick="removeBwRow(this); return false;">' . gettext('Remove') . '</a>';
3732
				$form .= "</td></tr>";
3733
			}
3734
		}
3735
		$form .= "</tbody></table>";
3736

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

    
3740
		return($form);
3741
	}
3742

    
3743
	function build_form() {
3744
		global $g, $config, $pipe, $action, $qname;
3745

    
3746
		//build list of schedules
3747
		$schedules = array();
3748
		$schedules[] = "none";//leave none to leave rule enabled all the time
3749
		if (is_array($config['schedules']) && is_array($config['schedules']['schedule'])) {
3750
			foreach ($config['schedules']['schedule'] as $schedule) {
3751
				if ($schedule['name'] <> "") {
3752
					$schedules[] = $schedule['name'];
3753
				}
3754
			}
3755
		}
3756

    
3757

    
3758
		$sform = new Form();
3759

    
3760
		$section = new Form_Section('Limiters');
3761

    
3762
		$section->addInput(new Form_Checkbox(
3763
			'enabled',
3764
			'Enable',
3765
			'Enable limiter and its children',
3766
			($this->GetEnabled() == "on"),
3767
			'on'
3768
		));
3769

    
3770
		$section->addInput(new Form_Input(
3771
			'newname',
3772
			'Name',
3773
			'text',
3774
			$this->GetQname()
3775
		));
3776

    
3777
		$section->addInput(new Form_Input(
3778
			'name',
3779
			null,
3780
			'hidden',
3781
			$this->GetQname()
3782
		));
3783

    
3784
		if ($this->GetNumber() > 0) {
3785
			$section->addInput(new Form_Input(
3786
				'number',
3787
				null,
3788
				'hidden',
3789
				$this->GetNumber()
3790
			));
3791
		}
3792

    
3793
		$bandwidth = $this->GetBandwidth();
3794

    
3795
		// Delete a row
3796
		if(isset($_GET['delbwrow']) && (count($bandwidth) > 0))
3797
			unset($bandwidth[$_GET['delbwrow']]);
3798

    
3799
		// Add a row
3800
		if($_GET['newbwrow']) {
3801
			array_push($bandwidth, array(count($bandwidth) => array('bw' => '', 'burst' => '', 'bwscale' => 'Kb', 'bwsched' => 'none') ));
3802

    
3803
		}
3804

    
3805
		if (is_array($bandwidth)) {
3806
			$section->addInput(new Form_StaticText(
3807
				'Bandwidth',
3808
				$this->build_bwtable()
3809
			));
3810
		}
3811

    
3812
		$mask = $this->GetMask();
3813

    
3814
		$section->addInput(new Form_Select(
3815
			'scheduler',
3816
			'Mask',
3817
			$mask['type'],
3818
			array('none' => 'None', 'srcaddress' => 'Source addresses', 'dstaddress' => 'Destination addresses')
3819
		))->setHelp('If "source" or "destination" slots is chosen a dynamic pipe with the bandwidth, delay, packet loss ' .
3820
					'and queue size given above will be created for each source/destination IP address encountered, respectively. ' .
3821
					'This makes it possible to easily specify bandwidth limits per host.');
3822

    
3823
		$group = new Form_Group(null);
3824

    
3825
		$group->add(new Form_Select(
3826
			'maskbits',
3827
			null,
3828
			$mask['bits'],
3829
			array_combine(range(32, 1, -1), range(32, 1, -1))
3830
		))->setHelp('IPV4 mask bits' . '<br />' . '255.255.255.255/?');
3831

    
3832
		$group->add(new Form_Select(
3833
			'maskbitsv6',
3834
			null,
3835
			$mask['bitsv6'],
3836
			array_combine(range(128, 1, -1), range(128, 1, -1))
3837
		))->setHelp('IPV6 mask bits' . '<br />' . '<font face="consolas">ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/?</font>');
3838

    
3839
		$section->add($group);
3840

    
3841
		$section->addInput(new Form_Input(
3842
			'description',
3843
			'Description',
3844
			'text',
3845
			$this->GetDescription()
3846
		))->setHelp('You may enter a description here for your reference (not parsed).');
3847

    
3848
		$sform->add($section);
3849

    
3850
		$section = new Form_Section('Advanced options');
3851

    
3852
		$section->addInput(new Form_Input(
3853
			'delay',
3854
			'Delay (mS)',
3855
			'number',
3856
			$this->GetDelay()
3857
		))->setHelp('In most cases, you should specify 0 here (or leave the field empty)');
3858

    
3859
		$section->addInput(new Form_Input(
3860
			'plr',
3861
			'Packet Loss Rate',
3862
			'number',
3863
			$this->GetPlr(),
3864
			['step' => '0.001', 'min' => '0.000']
3865
		))->setHelp('In most cases, you should specify 0 here (or leave the field empty). ' .
3866
					'A value of 0.001 means one packet in 1000 gets dropped');
3867

    
3868
		$section->addInput(new Form_Input(
3869
			'qlimit',
3870
			'Queue size (slots)',
3871
			'number',
3872
			$this->GetQlimit()
3873
		))->setHelp('In most cases, you should leave the field empty. All packets in this pipe are placed into a fixed-size queue first, ' .
3874
					'then they are delayed by value specified in the Delay field, and then they are delivered to their destination.');
3875

    
3876
		$section->addInput(new Form_Input(
3877
			'buckets',
3878
			'Bucket size (slots)',
3879
			'number',
3880
			$this->GetBuckets()
3881
		))->setHelp('In most cases, you should leave this field empty. It increases the hash size set');
3882

    
3883
		$sform->add($section);
3884

    
3885
		return($sform);
3886
		}
3887

    
3888
	function wconfig() {
3889
		$cflink =& get_dn_reference_to_me_in_config($this->GetLink());
3890
		if (!is_array($cflink)) {
3891
			$cflink = array();
3892
		}
3893
		$cflink['name'] = $this->GetQname();
3894
		$cflink['number'] = $this->GetNumber();
3895
		$cflink['qlimit'] = $this->GetQlimit();
3896
		$cflink['plr'] = $this->GetPlr();
3897
		$cflink['description'] = $this->GetDescription();
3898

    
3899
		$bandwidth = $this->GetBandwidth();
3900
		if (is_array($bandwidth)) {
3901
			$cflink['bandwidth'] = array();
3902
			$cflink['bandwidth']['item'] = array();
3903
			foreach ($bandwidth as $bwidx => $bw) {
3904
				$cflink['bandwidth']['item'][] = $bw;
3905
			}
3906
		}
3907

    
3908
		$cflink['enabled'] = $this->GetEnabled();
3909
		$cflink['buckets'] = $this->GetBuckets();
3910
		$mask = $this->GetMask();
3911
		$cflink['mask'] = $mask['type'];
3912
		$cflink['maskbits'] = $mask['bits'];
3913
		$cflink['maskbitsv6'] = $mask['bitsv6'];
3914
		$cflink['delay'] = $this->GetDelay();
3915
	}
3916

    
3917
}
3918

    
3919
class dnqueue_class extends dummynet_class {
3920
	var $pipeparent;
3921
	var $weight;
3922

    
3923
	function GetWeight() {
3924
		return $this->weight;
3925
	}
3926
	function SetWeight($weight) {
3927
		$this->weight = $weight;
3928
	}
3929
	function GetPipe() {
3930
		return $this->pipeparent;
3931
	}
3932
	function SetPipe($pipe) {
3933
		$this->pipeparent = $pipe;
3934
	}
3935

    
3936
	/* Just a stub in case we ever try to call this from the frontend. */
3937
	function &add_queue($interface, &$queue, &$path, &$input_errors) {
3938
		return;
3939
	}
3940

    
3941
	function delete_queue() {
3942
		cleanup_dnqueue_from_rules($this->GetQname());
3943
		unset_dn_object_by_reference($this->GetLink());
3944
		@pfSense_pipe_action("queue delete " . $this->GetNumber());
3945
	}
3946

    
3947
	function validate_input($data, &$input_errors) {
3948
		parent::validate_input($data, $input_errors);
3949

    
3950
		if ($data['weight'] && ((!is_numeric($data['weight'])) ||
3951
			($data['weight'] < 1 && $data['weight'] > 100))) {
3952
			$input_errors[] = gettext("Weight must be an integer between 1 and 100.");
3953
		}
3954
	}
3955

    
3956
	/*
3957
	 * Should search even its children
3958
	 */
3959
	function &find_queue($pipe, $qname) {
3960
		if ($qname == $this->GetQname()) {
3961
			return $this;
3962
		} else {
3963
			return NULL;
3964
		}
3965
	}
3966

    
3967
	function &find_parentqueue($pipe, $qname) {
3968
		return $this->qparent;
3969
	}
3970

    
3971
	function &get_queue_list(&$qlist) {
3972
		if ($this->GetEnabled() == "") {
3973
			return;
3974
		}
3975
		$qlist[$this->GetQname()] = "?" .$this->GetNumber();
3976
	}
3977

    
3978
	function ReadConfig(&$q) {
3979
		if (!empty($q['name']) && !empty($q['newname']) && $q['name'] != $q['newname']) {
3980
			$this->SetQname($q['newname']);
3981
		} else if (!empty($q['newname'])) {
3982
			$this->SetQname($q['newname']);
3983
		} else {
3984
			$this->SetQname($q['name']);
3985
		}
3986
		$this->SetNumber($q['number']);
3987
		if (isset($q['qlimit']) && $q['qlimit'] <> "") {
3988
			$this->SetQlimit($q['qlimit']);
3989
		} else {
3990
			$this->SetQlimit("");
3991
		}
3992
		if (isset($q['mask']) && $q['mask'] <> "") {
3993
			$masktype = $q['mask'];
3994
		} else {
3995
			$masktype = "";
3996
		}
3997
		if (isset($q['maskbits']) && $q['maskbits'] <> "") {
3998
			$maskbits = $q['maskbits'];
3999
		} else {
4000
			$maskbits = "";
4001
		}
4002
		if (isset($q['maskbitsv6']) && $q['maskbitsv6'] <> "") {
4003
			$maskbitsv6 = $q['maskbitsv6'];
4004
		} else {
4005
			$maskbitsv6 = "";
4006
		}
4007
		$this->SetMask(array("type" => $masktype, "bits" => $maskbits, "bitsv6" => $maskbitsv6));
4008
		if (isset($q['buckets']) && $q['buckets'] <> "") {
4009
			$this->SetBuckets($q['buckets']);
4010
		} else {
4011
			$this->SetBuckets("");
4012
		}
4013
		if (isset($q['plr']) && $q['plr'] <> "") {
4014
			$this->SetPlr($q['plr']);
4015
		} else {
4016
			$this->SetPlr("");
4017
		}
4018
		if (isset($q['weight']) && $q['weight'] <> "") {
4019
			$this->SetWeight($q['weight']);
4020
		} else {
4021
			$this->SetWeight("");
4022
		}
4023
		if (isset($q['description']) && $q['description'] <> "") {
4024
			$this->SetDescription($q['description']);
4025
		} else {
4026
			$this->SetDescription("");
4027
		}
4028
		$this->SetEnabled($q['enabled']);
4029
	}
4030

    
4031
	function build_tree() {
4032
		$parent =& $this->GetParent();
4033
		$tree = " <li><a href=\"firewall_shaper_vinterface.php?pipe=" . $parent->GetQname() ."&amp;queue=" . $this->GetQname() ."&amp;action=show\">";
4034
		$tree .= $this->GetQname() . "</a>";
4035
		$tree .= "</li>";
4036

    
4037
		return $tree;
4038
	}
4039

    
4040
	function build_rules() {
4041
		if ($this->GetEnabled() == "") {
4042
			return;
4043
		}
4044

    
4045
		$parent =& $this->GetParent();
4046
		$pfq_rule = "queue ". $this->GetNumber() . " config pipe " . $parent->GetNumber();
4047
		if ($this->GetQlimit()) {
4048
			$pfq_rule .= " queue " . $this->GetQlimit();
4049
		}
4050
		if ($this->GetWeight()) {
4051
			$pfq_rule .= " weight " . $this->GetWeight();
4052
		}
4053
		if ($this->GetBuckets()) {
4054
			$pfq_rule .= " buckets " . $this->GetBuckets();
4055
		}
4056
		$this->build_mask_rules($pfq_rule);
4057
		$pfq_rule .= "\n";
4058

    
4059
		return $pfq_rule;
4060
	}
4061

    
4062
	function build_javascript() {
4063
		return parent::build_javascript();
4064
	}
4065

    
4066
	function build_form() {
4067
		global $g, $config, $pipe, $action, $qname;
4068

    
4069
		//build list of schedules
4070
		$schedules = array();
4071
		$schedules[] = "none";//leave none to leave rule enabled all the time
4072
		if (is_array($config['schedules']) && is_array($config['schedules']['schedule'])) {
4073
			foreach ($config['schedules']['schedule'] as $schedule) {
4074
				if ($schedule['name'] <> "") {
4075
					$schedules[] = $schedule['name'];
4076
				}
4077
			}
4078
		}
4079

    
4080

    
4081
		$sform = new Form();
4082

    
4083
		$section = new Form_Section('Limiters');
4084

    
4085
		$section->addInput(new Form_Checkbox(
4086
			'enabled',
4087
			'Enable',
4088
			'Enable this queue',
4089
			($this->GetEnabled() == "on"),
4090
			'on'
4091
		));
4092

    
4093
		$section->addInput(new Form_Input(
4094
			'newname',
4095
			'Name',
4096
			'text',
4097
			$this->GetQname()
4098
		));
4099

    
4100
		$section->addInput(new Form_Input(
4101
			'name',
4102
			null,
4103
			'hidden',
4104
			$this->GetQname()
4105
		));
4106

    
4107
		if ($this->GetNumber() > 0) {
4108
			$section->addInput(new Form_Input(
4109
				'number',
4110
				null,
4111
				'hidden',
4112
				$this->GetNumber()
4113
			));
4114
		}
4115

    
4116
		$bandwidth = $this->GetBandwidth();
4117

    
4118
		// Delete a row
4119
		if(isset($_GET['delbwrow']) && (count($bandwidth) > 0))
4120
			unset($bandwidth[$_GET['delbwrow']]);
4121

    
4122
		// Add a row
4123
		if($_GET['newbwrow']) {
4124
			array_push($bandwidth, array(count($bandwidth) => array('bw' => '', 'burst' => '', 'bwscale' => 'Kb', 'bwsched' => 'none') ));
4125

    
4126
		}
4127

    
4128
		if (is_array($bandwidth)) {
4129
			$row = 0;
4130
			$numrows = count($bandwidth) - 1;
4131

    
4132
			if($numrows >= 0) {
4133
				foreach ($bandwidth as $bwidx => $bw) {
4134
					$group = new Form_Group($row == 0 ? 'Bandwidth':null);
4135

    
4136
					$group->add(new Form_Input(
4137
						'bandwidth' . $bwidx,
4138
						null,
4139
						'text',
4140
						$bw['bw']
4141
					))->setHelp($row == $numrows ? 'Bandwidth':null);
4142

    
4143
					$group->add(new Form_Select(
4144
						'bwtype' . $bwidx,
4145
						null,
4146
						$bw['bwscale'],
4147
						array('Kb' => 'Kbit/s', 'Mb' => 'Mbit/s', 'Gb' => 'Gbit/s', 'b' => 'Bit/s')
4148
					))->setHelp($row == $numrows ? 'Bw Type':null);;
4149

    
4150
					$group->add(new Form_Select(
4151
						'bwsched' . $bwidx,
4152
						null,
4153
						$bw['bwsched'],
4154
						$schedules
4155
					))->setHelp($row == $numrows ? 'Schedule':null);;
4156

    
4157
					$group->add(new Form_Button(
4158
						'delete' + $bwidx,
4159
						'Delete',
4160
						'firewall_shaper_vinterface.php?pipe=' . $pipe . '&queue=' . $qname . '&action=' . $action . '&delbwrow=' . $bwidx
4161
					))->removeClass('btn-primary')->addClass('btn-danger btn-sm');
4162

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

    
4166
					$section->add($group);
4167
					$row++;
4168
				}
4169
			}
4170
		else { // The $bandwidth array exists, but is empty
4171
			$section->addInput(new Form_StaticText(
4172
				'Bandwidth',
4173
				'No schedules configured for this limiter.'
4174
			));
4175
		}
4176

    
4177
		$section->addInput(new Form_Button(
4178
			'addsched',
4179
			'Add new schedule',
4180
			'firewall_shaper_vinterface.php?pipe=' . $pipe . '&queue=' . $qname . '&action=' . $action . '&newbwrow=yes'
4181
			))->removeClass('btn-primary')->addClass('btn-success btn-sm');
4182
		}
4183

    
4184
		$mask = $this->GetMask();
4185

    
4186
		$section->addInput(new Form_Select(
4187
			'scheduler',
4188
			'Mask',
4189
			$mask['type'],
4190
			array('none' => 'None', 'srcaddress' => 'Source addresses', 'dstaddress' => 'Destination addresses')
4191
		))->setHelp('If "source" or "destination" slots is chosen a dynamic pipe with the bandwidth, delay, packet loss ' .
4192
					'and queue size given above will be created for each source/destination IP address encountered, respectively. ' .
4193
					'This makes it possible to easily specify bandwidth limits per host.');
4194

    
4195
		$group = new Form_Group(null);
4196

    
4197
		$group->add(new Form_Select(
4198
			'maskbits',
4199
			null,
4200
			$mask['bits'],
4201
			array_combine(range(32, 1, -1), range(32, 1, -1))
4202
		))->setHelp('IPV4 mask bits' . '<br />' . '255.255.255.255/?');
4203

    
4204
		$group->add(new Form_Select(
4205
			'maskbitsv6',
4206
			null,
4207
			$mask['bitsv6'],
4208
			array_combine(range(128, 1, -1), range(128, 1, -1))
4209
		))->setHelp('IPV6 mask bits' . '<br />' . '<font face="consolas">ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/?</font>');
4210

    
4211
		$section->add($group);
4212

    
4213
		$section->addInput(new Form_Input(
4214
			'description',
4215
			'Description',
4216
			'text',
4217
			$this->GetDescription()
4218
		))->setHelp('You may enter a description here for your reference (not parsed).');
4219

    
4220
		$sform->add($section);
4221

    
4222
		$section = new Form_Section('Advanced options');
4223

    
4224
		$section->addInput(new Form_Input(
4225
			'weight',
4226
			'Weight',
4227
			'number',
4228
			$this->GetWeight(),
4229
			['min' => '1', 'max' => '100']
4230
		))->setHelp('For queues under the same parent this specifies the share that a queue gets(values range from 1 to 100),' .
4231
					' you can leave it blank otherwise');
4232

    
4233
		$section->addInput(new Form_Input(
4234
			'plr',
4235
			'Packet Loss Rate',
4236
			'number',
4237
			$this->GetPlr(),
4238
			['step' => '0.001', 'min' => '0.000']
4239
		))->setHelp('In most cases, you should specify 0 here (or leave the field empty). ' .
4240
					'A value of 0.001 means one packet in 1000 gets dropped');
4241

    
4242
		$section->addInput(new Form_Input(
4243
			'qlimit',
4244
			'Queue size (slots)',
4245
			'number',
4246
			$this->GetQlimit()
4247
		))->setHelp('In most cases, you should leave the field empty. All packets in this pipe are placed into a fixed-size queue first, ' .
4248
					'then they are delayed by value specified in the Delay field, and then they are delivered to their destination.');
4249

    
4250
		$section->addInput(new Form_Input(
4251
			'buckets',
4252
			'Bucket size (slots)',
4253
			'number',
4254
			$this->GetBuckets()
4255
		))->setHelp('In most cases, you should leave this field empty. It increases the hash size set');
4256

    
4257
		$section->addInput(new Form_Input(
4258
			'pipe',
4259
			null,
4260
			'hidden',
4261
			$this->GetPipe()
4262
		));
4263

    
4264
		$sform->add($section);
4265

    
4266
		return($sform);
4267
	}
4268

    
4269
	function update_dn_data(&$data) {
4270
		$this->ReadConfig($data);
4271
	}
4272

    
4273
	function wconfig() {
4274
		$cflink =& get_dn_reference_to_me_in_config($this->GetLink());
4275
		if (!is_array($cflink)) {
4276
			$cflink = array();
4277
		}
4278
		$cflink['name'] = $this->GetQname();
4279
		$cflink['number'] = $this->GetNumber();
4280
		$cflink['qlimit'] = $this->GetQlimit();
4281
		$cflink['description'] = $this->GetDescription();
4282
		$cflink['weight'] = $this->GetWeight();
4283
		$cflink['enabled'] = $this->GetEnabled();
4284
		$cflink['buckets'] = $this->GetBuckets();
4285
		$mask = $this->GetMask();
4286
		$cflink['mask'] = $mask['type'];
4287
		$cflink['maskbits'] = $mask['bits'];
4288
		$cflink['maskbitsv6'] = $mask['bitsv6'];
4289
	}
4290
}
4291

    
4292
// List of layer7 objects
4293
$layer7_rules_list = array();
4294

    
4295
class layer7 {
4296

    
4297
	var $rname; //alias
4298
	var $rdescription; //alias description
4299
	var $rport; //divert port
4300
	var $renabled; //rule enabled
4301
	var $rsets = array(); //array of l7 associations
4302

    
4303
	// Auxiliary functions
4304

    
4305
	function GetRName() {
4306
		return $this->rname;
4307
	}
4308
	function SetRName($rname) {
4309
		$this->rname = $rname;
4310
	}
4311
	function GetRDescription() {
4312
		return $this->rdescription;
4313
	}
4314
	function SetRDescription($rdescription) {
4315
		$this->rdescription = $rdescription;
4316
	}
4317
	function GetRPort() {
4318
		return $this->rport;
4319
	}
4320
	function SetRPort($rport) {
4321
		$this->rport = $rport;
4322
	}
4323
	function GetREnabled() {
4324
		return $this->renabled;
4325
	}
4326
	function SetREnabled($value) {
4327
		$this->renabled = $value;
4328
	}
4329
	function GetRl7() {
4330
		return $this->rsets;
4331
	}
4332
	function SetRl7($rsets) {
4333
		$this->rsets = $rsets;
4334
	}
4335

    
4336
	//Add a tuple (rule,structure,element) to the $rsets
4337

    
4338
	function add_rule($l7set) {
4339
		$this->rsets[] = $l7set;
4340
	}
4341

    
4342
	// Build the layer7 rules
4343
	function build_l7_rules() {
4344
		if ($this->GetREnabled() == "") {
4345
			return;
4346
		}
4347
		//$l7rules = "#" . $this->rdescription . "\n";
4348
		foreach ($this->rsets as $rl7) {
4349
			$l7rules .= $rl7->build_rules();
4350
		}
4351
		return $l7rules;
4352
	}
4353

    
4354
	// Read the config from array
4355
	function ReadConfig(&$qname, &$q) {
4356
		$this->SetRName($qname);
4357
		$this->SetREnabled($q['enabled']);
4358
		$this->SetRPort($q['divert_port']);
4359
		if (isset($q['description']) && $q['description'] <> "") {
4360
			$this->SetRDescription($q['description']);
4361
		}
4362
		$rsets = $q['l7rules'];
4363
		//Put individual rules in the array
4364
		if (is_array($rsets)) {
4365
			$this->rsets = array(); // XXX: ugly hack
4366
			foreach ($rsets as $l7r) {
4367
				$l7obj = new l7rule();
4368
				$l7obj->SetRProtocol($l7r['protocol']);
4369
				$l7obj->SetRStructure($l7r['structure']);
4370
				$l7obj->SetRBehaviour($l7r['behaviour']);
4371
				$this->add_rule($l7obj);
4372
			}
4373
		}
4374
	}
4375

    
4376
	//Generate a random port for the divert socket
4377
	function gen_divert_port() {
4378
		$dports = get_divert_ports(); //array of used ports
4379
		$divert_port = 1; // Initialize
4380
		while (($divert_port % 2) != 0 || in_array($divert_port, $dports)) {
4381
			$divert_port = rand(40000, 60000);
4382
		}
4383
		return $divert_port;
4384
	}
4385

    
4386
	//Helps building the left tree
4387
	function build_tree() {
4388
		$tree = " <li><a href=\"firewall_shaper_layer7.php?container=" . $this->GetRName() ."&amp;action=show\">";
4389
		$tree .= $this->GetRName() . "</a>";
4390
		$tree .= "</li>";
4391

    
4392
		return $tree;
4393
	}
4394

    
4395
	function build_form() {
4396

    
4397
		$form = new Form(new Form_Button(
4398
			'Submit',
4399
			'Save'
4400
		));
4401

    
4402
		$section = new Form_Section('Traffic Shaper');
4403

    
4404
		$section->addInput(new Form_Checkbox(
4405
			'enabled',
4406
			'Enable/Disable',
4407
			'Enable/disable discipline and its children',
4408
			($this->GetREnabled() == "on"),
4409
			'on'
4410
		));
4411

    
4412
		$section->addInput(new Form_Input(
4413
			'container',
4414
			'Name',
4415
			'text',
4416
			$this->GetRName()
4417
		));
4418

    
4419
		$section->addInput(new Form_Input(
4420
			'description',
4421
			'Description',
4422
			'text',
4423
			$this->GetRDescription()
4424
		))->setHelp('You may enter a description here for your reference (not parsed).');
4425

    
4426
		$form->add($section);
4427

    
4428
		return $form;
4429
	}
4430

    
4431
	//Write the setting to the $config array
4432
	function wconfig() {
4433
		global $config;
4434

    
4435
		if (!is_array($config['l7shaper']['container'])) {
4436
			$config['l7shaper']['container'] = array();
4437
		}
4438
		//
4439
		$cflink =& get_l7c_reference_to_me_in_config($this->GetRName());
4440
		// Test if this rule exists already
4441
		if (!$cflink) {
4442
			$cflink =& $config['l7shaper']['container'][];
4443
		}
4444
		$cflink['name'] = $this->GetRName();
4445
		$cflink['enabled'] = $this->GetREnabled();
4446
		$cflink['description'] = $this->GetRDescription();
4447
		$cflink['divert_port'] = $this->GetRPort();
4448

    
4449
		// Destroy previously existent rules
4450
		if (is_array($cflink['rules'])) {
4451
			unset($cflink['l7rules']);
4452
		}
4453

    
4454
		$cflink['l7rules'] = array();
4455

    
4456
		$i = 0;
4457
		foreach ($this->rsets as $rulel7) {
4458
			$cflink['l7rules'][$i]['protocol'] = $rulel7->GetRProtocol();
4459
			$cflink['l7rules'][$i]['structure'] = $rulel7->GetRStructure();
4460
			$cflink['l7rules'][$i]['behaviour'] = $rulel7->GetRBehaviour();
4461
			$i++;
4462
		}
4463
	}
4464

    
4465
	//This function is necessary to help producing the overload options for keep state
4466
	function get_unique_structures() {
4467

    
4468
		$unique_structures = array("action" => false, "dummynet" => false, "altq" => false);
4469
		foreach ($this->rsets as $l7rule) {
4470
			if ($l7rule->GetRStructure() == "action") {
4471
				$unique_structures['action'] = true;
4472
			} else if ($l7rule->GetRStructure() == "limiter") {
4473
				$unique_structures['dummynet'] = true;
4474
			} else {
4475
				$unique_structures['altq'] = true;
4476
			}
4477
		}
4478
		//Delete non used structures so we don't have to check this in filter.inc
4479
		foreach ($unique_structures as $key => $value) {
4480
			if (!$value) {
4481
				unset($unique_structures[$key]);
4482
			}
4483
		}
4484
		return $unique_structures;
4485
	}
4486

    
4487
	function validate_input($data, &$input_errors) {
4488
		$reqdfields[] = "container";
4489
		$reqdfieldsn[] = gettext("Name");
4490

    
4491
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
4492

    
4493
		if (!preg_match("/^[a-zA-Z0-9_-]+$/", $data['container'])) {
4494
			$input_errors[] = gettext("Queue names must be alphanumeric and _ or - only.");
4495
		}
4496
	}
4497

    
4498
	function delete_l7c() {
4499
		mwexec("/bin/pkill -f 'ipfw-classifyd .* -p ". $this->GetRPort() . "'", true);
4500
		unset_l7_object_by_reference($this->GetRName());
4501
		cleanup_l7_from_rules($this->GetRName());
4502
	}
4503
}
4504

    
4505
class l7rule {
4506

    
4507
	var $rprotocol; //protocol
4508
	var $rstructure; //action, limiter, queue
4509
	var $rbehaviour; //allow, block, queue_name, pipe_number ...
4510

    
4511
	//Auxiliary Functions
4512

    
4513
	function GetRProtocol() {
4514
		return $this->rprotocol;
4515
	}
4516
	function SetRProtocol($rprotocol) {
4517
		$this->rprotocol = $rprotocol;
4518
	}
4519
	function GetRStructure() {
4520
		return $this->rstructure;
4521
	}
4522
	function SetRStructure($rstructure) {
4523
		$this->rstructure = $rstructure;
4524
	}
4525
	function GetRBehaviour() {
4526
		return $this->rbehaviour;
4527
	}
4528
	function SetRBehaviour($rbehaviour) {
4529
		$this->rbehaviour = $rbehaviour;
4530
	}
4531

    
4532
	//XXX Do we need to test any particularity for AltQ queues?
4533
	function build_rules() {
4534
		global $dummynet_pipe_list;
4535
		switch ($this->GetRStructure()) {
4536
		case "limiter":
4537
			read_dummynet_config();
4538
			$dn_list =& get_unique_dnqueue_list();
4539
			$found = false;
4540
			if (is_array($dn_list)) {
4541
				foreach ($dn_list as $key => $value) {
4542
					if ($key == $this->GetRBehaviour()) {
4543
						if ($value[0] == "?") {
4544
							$l7rule = $this->GetRProtocol() . " = dnqueue " . substr($value, 1) . "\n";
4545
						} else {
4546
							$l7rule = $this->GetRProtocol() . " = dnpipe " . $value . "\n";
4547
						}
4548
						$found = true;
4549
					}
4550
					if ($found) {
4551
						break;
4552
					}
4553
				}
4554
			}
4555
			break;
4556
		default: //This is for action and for altq
4557
			$l7rule = $this->GetRProtocol() . " = " . $this->GetRStructure() . " " . $this->GetRBehaviour() . "\n";
4558
			break;
4559
		}
4560
		return $l7rule;
4561
	}
4562
}
4563

    
4564
/*
4565
 * This function allows to return an array with all the used divert socket ports
4566
 */
4567
function get_divert_ports() {
4568
	global $layer7_rules_list;
4569
	$dports = array();
4570

    
4571
	foreach ($layer7_rules_list as $l7r) {
4572
		$dports[] = $l7r->GetRPort();
4573
	}
4574

    
4575
	return $dports;
4576
}
4577

    
4578
function &get_l7c_reference_to_me_in_config(&$name) {
4579
	global $config;
4580

    
4581
	$ptr = NULL;
4582

    
4583
	if (is_array($config['l7shaper']['container'])) {
4584
		foreach ($config['l7shaper']['container'] as $key => $value) {
4585
			if ($value['name'] == $name) {
4586
				$ptr =& $config['l7shaper']['container'][$key];
4587
			}
4588
		}
4589
	}
4590
	return $ptr;
4591
	// $ptr can be null. has to be checked later
4592
}
4593

    
4594
function unset_l7_object_by_reference(&$name) {
4595
	global $config;
4596

    
4597
	if (is_array($config['l7shaper']['container'])) {
4598
		foreach ($config['l7shaper']['container'] as $key => $value) {
4599
			if ($value['name'] == $name) {
4600
				unset($config['l7shaper']['container'][$key]['l7rules']);
4601
				unset($config['l7shaper']['container'][$key]);
4602
				break;
4603
			}
4604
		}
4605
	}
4606
}
4607

    
4608
function read_layer7_config() {
4609
	global $layer7_rules_list, $config;
4610

    
4611
	if (!is_array($config['l7shaper']['container']) || !count($config['l7shaper']['container'])) {
4612
		$layer7_rules_list = array();
4613
		return;
4614
	}
4615

    
4616
	$l7cs = &$config['l7shaper']['container'];
4617

    
4618
	$layer7_rules_list = array();
4619

    
4620
	foreach ($l7cs as $conf) {
4621
		if (empty($conf['name'])) {
4622
			continue; /* XXX: grrrrrr at php */
4623
		}
4624
		$root =& new layer7();
4625
		$root->ReadConfig($conf['name'],$conf);
4626
		$layer7_rules_list[$root->GetRName()] = &$root;
4627
	}
4628
}
4629

    
4630
function update_layer7_custom_patterns() {
4631
	global $config;
4632

    
4633
	if (!is_array($config['l7shaper']['custom_pat'])) {
4634
		return;
4635
	}
4636

    
4637
	foreach ($config['l7shaper']['custom_pat'] as $filename => $filecontent) {
4638
		if (!file_exists("/usr/local/share/protocols/" . $filename)) {
4639
			@file_put_contents("/usr/local/share/protocols/" . $filename, base64_decode($filecontent));
4640
		}
4641
	}
4642
}
4643

    
4644
function generate_layer7_files() {
4645
	global $layer7_rules_list, $g;
4646

    
4647
	read_layer7_config();
4648

    
4649
	if (!empty($layer7_rules_list)) {
4650
		if (!is_module_loaded("ipdivert.ko")) {
4651
			mwexec("/sbin/kldload ipdivert.ko");
4652
		}
4653

    
4654
		array_map('unlink', glob("{$g['tmp_path']}/*.l7"));
4655
	}
4656

    
4657
	update_layer7_custom_patterns();
4658

    
4659
	foreach ($layer7_rules_list as $l7rules) {
4660
		if ($l7rules->GetREnabled()) {
4661
			$filename = $l7rules->GetRName() . ".l7";
4662
			$path = "{$g['tmp_path']}/" . $filename;
4663

    
4664
			$rules = $l7rules->build_l7_rules();
4665

    
4666
			$fp = fopen($path,'w');
4667
			fwrite($fp,$rules);
4668
			fclose($fp);
4669
		}
4670
	}
4671
}
4672

    
4673
function layer7_start_l7daemon() {
4674
	global $layer7_rules_list, $g;
4675

    
4676
	/*
4677
	 * XXX: ermal - Needed ?!
4678
	 * read_layer7_config();
4679
	 */
4680

    
4681
	foreach ($layer7_rules_list as $l7rules) {
4682
		if ($l7rules->GetREnabled()) {
4683
			$filename = $l7rules->GetRName() . ".l7";
4684
			$path = "{$g['tmp_path']}/" . $filename;
4685

    
4686
			unset($l7pid);
4687
			/* Only reread the configuration rather than restart to avoid losing information. */
4688
			exec("/bin/pgrep -f 'ipfw-classifyd .* -p ". $l7rules->GetRPort() . "'", $l7pid);
4689
			if (count($l7pid) > 0) {
4690
				log_error(sprintf(gettext("Sending HUP signal to %s"), $l7pid[0]));
4691
				mwexec("/bin/kill -HUP {$l7pid[0]}");
4692
			} else {
4693
				// XXX: Hardcoded number of packets to garbage collect and queue length.
4694
				$ipfw_classifyd_init = "/usr/local/sbin/ipfw-classifyd -n 8 -q 700 -c {$path} -p " . $l7rules->GetRPort() . " -P /usr/local/share/protocols";
4695
				mwexec_bg($ipfw_classifyd_init);
4696
			}
4697
		}
4698
	}
4699
}
4700

    
4701
// This function uses /usr/local/share/protocols as a default directory for searching .pat files
4702
function generate_protocols_array() {
4703

    
4704
	update_layer7_custom_patterns();
4705

    
4706
	$protocols = return_dir_as_array("/usr/local/share/protocols");
4707
	$protocols_new = array();
4708
	if (is_array($protocols)) {
4709
		foreach ($protocols as $key => $proto) {
4710
			if (strstr($proto, ".pat")) {
4711
				$protocols_new[$key] =& str_replace(".pat", "", $proto);
4712
			}
4713
		}
4714
		sort($protocols_new);
4715
	}
4716
	return $protocols_new;
4717
}
4718

    
4719
function get_l7_unique_list() {
4720
	global $layer7_rules_list;
4721

    
4722
	$l7list = array();
4723
	if (is_array($layer7_rules_list)) {
4724
		foreach ($layer7_rules_list as $l7c) {
4725
			if ($l7c->GetREnabled()) {
4726
				$l7list[] = $l7c->GetRName();
4727
			}
4728
		}
4729
	}
4730

    
4731
	return $l7list;
4732
}
4733

    
4734
// Disable a removed l7 container from the filter
4735
function cleanup_l7_from_rules(&$name) {
4736
	global $config;
4737

    
4738
	if (is_array($config['filter']['rule'])) {
4739
		foreach ($config['filter']['rule'] as $key => $rule) {
4740
			if ($rule['l7container'] == $name) {
4741
				unset($config['filter']['rule'][$key]['l7container']);
4742
			}
4743
		}
4744
	}
4745
}
4746

    
4747
function get_dummynet_name_list() {
4748

    
4749
	$dn_name_list =& get_unique_dnqueue_list();
4750
	$dn_name = array();
4751
	if (is_array($dn_name_list)) {
4752
		foreach ($dn_name_list as $key => $value) {
4753
			$dn_name[] = $key;
4754
		}
4755
	}
4756

    
4757
	return $dn_name;
4758

    
4759
}
4760

    
4761
function get_altq_name_list() {
4762
	$altq_name_list =& get_unique_queue_list();
4763
	$altq_name = array();
4764
	if (is_array($altq_name_list)) {
4765
		foreach ($altq_name_list as $key => $aqobj) {
4766
			$altq_name[] = $key;
4767
		}
4768
	}
4769

    
4770
	return $altq_name;
4771
}
4772

    
4773
/*
4774
 * XXX: TODO Make a class shaper to hide all these functions
4775
 * from the global namespace.
4776
 */
4777

    
4778
/*
4779
 * This is a layer violation but for now there is no way
4780
 * I can find to properly do this with PHP.
4781
 */
4782
function altq_get_default_queue($interface) {
4783
	global $altq_list_queues;
4784

    
4785
	$altq_tmp = $altq_list_queues[$interface];
4786
	if ($altq_tmp) {
4787
		return $altq_tmp->GetDefaultQueuePresent();
4788
	} else {
4789
		return false;
4790
	}
4791
}
4792

    
4793
function altq_check_default_queues() {
4794
	global $altq_list_queues;
4795

    
4796
	$count = 0;
4797
	if (is_array($altq_list_queues)) {
4798
		foreach ($altq_list_queues as $altq) {
4799
			if ($altq->GetDefaultQueuePresent()) {
4800
				$count++;
4801
			}
4802
		}
4803
	}
4804
	else {
4805
		$count++;
4806
	}
4807

    
4808
	return 0;
4809
}
4810

    
4811
function &get_unique_queue_list() {
4812
	global $altq_list_queues;
4813

    
4814
	$qlist = array();
4815
	if (is_array($altq_list_queues)) {
4816
		foreach ($altq_list_queues as $altq) {
4817
			if ($altq->GetEnabled() == "") {
4818
				continue;
4819
			}
4820
			$tmplist =& $altq->get_queue_list();
4821
			foreach ($tmplist as $qname => $link) {
4822
				if ($link->GetEnabled() <> "") {
4823
					$qlist[$qname] = $link;
4824
				}
4825
			}
4826
		}
4827
	}
4828
	return $qlist;
4829
}
4830

    
4831
function &get_unique_dnqueue_list() {
4832
	global $dummynet_pipe_list;
4833

    
4834
	$qlist = array();
4835
	if (is_array($dummynet_pipe_list)) {
4836
		foreach ($dummynet_pipe_list as $dn) {
4837
			if ($dn->GetEnabled() == "") {
4838
				continue;
4839
			}
4840
			$tmplist =& $dn->get_queue_list();
4841
			foreach ($tmplist as $qname => $link) {
4842
				$qlist[$qname] = $link;
4843
			}
4844
		}
4845
	}
4846
	return $qlist;
4847
}
4848

    
4849
function ref_on_altq_queue_list($parent, $qname) {
4850
	if (isset($GLOBALS['queue_list'][$qname])) {
4851
		$GLOBALS['queue_list'][$qname]++;
4852
	} else {
4853
		$GLOBALS['queue_list'][$qname] = 1;
4854
	}
4855

    
4856
	unref_on_altq_queue_list($parent);
4857
}
4858

    
4859
function unref_on_altq_queue_list($qname) {
4860
	$GLOBALS['queue_list'][$qname]--;
4861
	if ($GLOBALS['queue_list'][$qname] <= 1) {
4862
		unset($GLOBALS['queue_list'][$qname]);
4863
	}
4864
}
4865

    
4866
function read_altq_config() {
4867
	global $altq_list_queues, $config;
4868
	$path = array();
4869

    
4870
	if (!is_array($config['shaper'])) {
4871
		$config['shaper'] = array();
4872
	}
4873
	if (!is_array($config['shaper']['queue'])) {
4874
		$config['shaper']['queue'] = array();
4875
	}
4876
	$a_int = &$config['shaper']['queue'];
4877

    
4878
	$altq_list_queues = array();
4879

    
4880
	if (!is_array($config['shaper']['queue'])) {
4881
		return;
4882
	}
4883

    
4884
	foreach ($a_int as $key => $conf) {
4885
		$int = $conf['interface'];
4886
		$root =& new altq_root_queue();
4887
		$root->SetInterface($int);
4888
		$altq_list_queues[$root->GetInterface()] = &$root;
4889
		$root->ReadConfig($conf);
4890
		array_push($path, $key);
4891
		$root->SetLink($path);
4892
		if (is_array($conf['queue'])) {
4893
			foreach ($conf['queue'] as $key1 => $q) {
4894
				array_push($path, $key1);
4895
				/*
4896
				 * XXX: we completely ignore errors here but anyway we must have
4897
				 *	checked them before so no harm should be come from this.
4898
				 */
4899
				$root->add_queue($root->GetInterface(), $q, $path, $input_errors);
4900
				array_pop($path);
4901
			}
4902
		}
4903
		array_pop($path);
4904
	}
4905
}
4906

    
4907
function read_dummynet_config() {
4908
	global $dummynet_pipe_list, $config;
4909
	$path = array();
4910

    
4911
	if (!is_array($config['dnshaper'])) {
4912
		$config['dnshaper'] = array();
4913
	}
4914
	if (!is_array($config['dnshaper']['queue'])) {
4915
		$config['dnshaper']['queue'] = array();
4916
	}
4917
	$a_int = &$config['dnshaper']['queue'];
4918

    
4919
	$dummynet_pipe_list = array();
4920

    
4921
	if (!is_array($config['dnshaper']['queue']) ||
4922
		!count($config['dnshaper']['queue'])) {
4923
		return;
4924
	}
4925

    
4926
	foreach ($a_int as $key => $conf) {
4927
		if (empty($conf['name'])) {
4928
			continue; /* XXX: grrrrrr at php */
4929
		}
4930
		$root =& new dnpipe_class();
4931
		$root->ReadConfig($conf);
4932
		$dummynet_pipe_list[$root->GetQname()] = &$root;
4933
		array_push($path, $key);
4934
		$root->SetLink($path);
4935
		if (is_array($conf['queue'])) {
4936
			foreach ($conf['queue'] as $key1 => $q) {
4937
				array_push($path, $key1);
4938
				/*
4939
				 * XXX: we completely ignore errors here but anyway we must have
4940
				 *	checked them before so no harm should be come from this.
4941
				 */
4942
				$root->add_queue($root->GetQname(), $q, $path, $input_errors);
4943
				array_pop($path);
4944
			}
4945
		}
4946
		array_pop($path);
4947
	}
4948
}
4949

    
4950
function get_interface_list_to_show() {
4951
	global $altq_list_queues, $config;
4952
	global $shaperIFlist;
4953

    
4954
	$tree = "";
4955
	foreach ($shaperIFlist as $shif => $shDescr) {
4956
		if ($altq_list_queues[$shif]) {
4957
			continue;
4958
		} else	{
4959
			if (!is_altq_capable(get_real_interface($shif))) {
4960
				continue;
4961
			}
4962
			$tree .= " <li><a href=\"firewall_shaper.php?interface=".$shif."&amp;action=add\">".$shDescr."</a></li>";
4963
		}
4964
	}
4965

    
4966
	return $tree;
4967
}
4968

    
4969
function filter_generate_altq_queues() {
4970
	global $altq_list_queues;
4971

    
4972
	read_altq_config();
4973

    
4974
	$altq_rules = "";
4975
	foreach ($altq_list_queues as $altq) {
4976
		$altq_rules .= $altq->build_rules();
4977
	}
4978

    
4979
	return $altq_rules;
4980
}
4981

    
4982
function dnqueue_find_nextnumber() {
4983
	global $dummynet_pipe_list;
4984

    
4985
	$dnused = array();
4986
	if (is_array($dummynet_pipe_list)) {
4987
		foreach ($dummynet_pipe_list as $dn) {
4988
			$tmplist =& $dn->get_queue_list();
4989
			foreach ($tmplist as $qname => $link) {
4990
				if ($link[0] == "?") {
4991
					$dnused[$qname] = substr($link, 1);
4992
				}
4993
			}
4994
		}
4995
	}
4996

    
4997
	sort($dnused, SORT_NUMERIC);
4998
	$dnnumber = 0;
4999
	$found = false;
5000
	foreach ($dnused as $dnnum) {
5001
		if (($dnnum - $dnnumber) > 1) {
5002
			$dnnumber = $dnnum - 1;
5003
			$found = true;
5004
			break;
5005
		} else {
5006
			$dnnumber = $dnnum;
5007
		}
5008
	}
5009

    
5010
	if ($found == false) {
5011
		$dnnumber++;
5012
	}
5013

    
5014
	unset($dnused, $dnnum, $found);
5015
	return $dnnumber;
5016
}
5017

    
5018
function dnpipe_find_nextnumber() {
5019
	global $dummynet_pipe_list;
5020

    
5021
	$dnused = array();
5022
	foreach ($dummynet_pipe_list as $dn) {
5023
		$dnused[] = $dn->GetNumber();
5024
	}
5025

    
5026
	sort($dnused, SORT_NUMERIC);
5027
	$dnnumber = 0;
5028
	$found = false;
5029
	foreach ($dnused as $dnnum) {
5030
		if (($dnnum - $dnnumber) > 1) {
5031
			$dnnumber = $dnnum - 1;
5032
			$found = true;
5033
			break;
5034
		} else {
5035
			$dnnumber = $dnnum;
5036
		}
5037
	}
5038

    
5039
	if ($found == false) {
5040
		$dnnumber++;
5041
	}
5042

    
5043
	unset($dnused, $dnnum, $found);
5044
	return $dnnumber;
5045
}
5046

    
5047
function filter_generate_dummynet_rules() {
5048
	global $g, $dummynet_pipe_list;
5049

    
5050
	read_dummynet_config();
5051

    
5052
	$dn_rules = "";
5053
	foreach ($dummynet_pipe_list as $dn) {
5054
		$dn_rules .= $dn->build_rules();
5055
	}
5056

    
5057
	if (!empty($dn_rules)) {
5058
		if (!is_module_loaded("dummynet.ko")) {
5059
			mwexec("/sbin/kldload dummynet");
5060
			set_sysctl(array(
5061
				"net.inet.ip.dummynet.io_fast" => "1",
5062
				"net.inet.ip.dummynet.hash_size" => "256"
5063
			));
5064
		}
5065
		file_put_contents("{$g['tmp_path']}/rules.limiter", $dn_rules);
5066
		mwexec("/sbin/ipfw {$g['tmp_path']}/rules.limiter");
5067
	}
5068
}
5069

    
5070
function build_iface_without_this_queue($iface, $qname) {
5071
	global $g, $altq_list_queues;
5072
	global $shaperIFlist;
5073

    
5074
	$altq =& $altq_list_queues[$iface];
5075

    
5076
	if ($altq) {
5077
		$scheduler = $altq->GetScheduler();
5078
	}
5079

    
5080
	$form = '<dl class="dl-horizontal">';
5081

    
5082
	$form .= '	<dt>';
5083
	$form .= '		<a href="firewall_shaper.php?interface=' . $iface . '&amp;queue=' . $iface . '&amp;action=show">' . $shaperIFlist[$iface] . '</a>';
5084
	$form .= '	</dt>';
5085
	$form .= '	<dd>';
5086
	$form .=		$scheduler;
5087
	$form .= '	</dd>';
5088

    
5089
	$form .= '	<dt>';
5090
	$form .= 'Clone';
5091
	$form .= '	</dt>';
5092
	$form .= '	<dd>';
5093
	$form .= '<a class="btn btn-info btn-xs" href="firewall_shaper_queues.php?interface=';
5094
	$form .= $iface . '&amp;queue=';
5095
	$form .= $qname . '&amp;action=add">';
5096
	$form .= gettext("Clone shaper on the I/F") . '</a>';
5097
	$form .= '	</dd>';
5098

    
5099
	$form .= '</dl>';
5100

    
5101
	return $form;
5102

    
5103
}
5104

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

    
5109
$dn_default_shaper_msg = $default_shaper_msg;
5110

    
5111
?>
(50-50/67)