Project

General

Profile

Download (116 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
 * shaper.inc
4
 *
5
 * part of pfSense (https://www.pfsense.org)
6
 * Copyright (c) 2004-2016 Rubicon Communications, LLC (Netgate)
7
 * All rights reserved.
8
 *
9
 * originally based on m0n0wall (http://m0n0.ch/wall)
10
 * Copyright (c) 2003-2004 Manuel Kasper <mk@neon1.net>.
11
 * All rights reserved.
12
 *
13
 * Licensed under the Apache License, Version 2.0 (the "License");
14
 * you may not use this file except in compliance with the License.
15
 * You may obtain a copy of the License at
16
 *
17
 * http://www.apache.org/licenses/LICENSE-2.0
18
 *
19
 * Unless required by applicable law or agreed to in writing, software
20
 * distributed under the License is distributed on an "AS IS" BASIS,
21
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22
 * See the License for the specific language governing permissions and
23
 * limitations under the License.
24
 */
25

    
26
/* XXX: needs some reducing on include. */
27
/* include all configuration functions. */
28
require_once("globals.inc");
29
require_once("functions.inc");
30
require_once("util.inc");
31
require_once("notices.inc");
32

    
33
/*
34
 * I admit :) this is derived from xmlparse.inc StartElement()
35
 */
36
function &get_reference_to_me_in_config(&$mypath) {
37
	global $config;
38

    
39
	$ptr =& $config['shaper'];
40
	foreach ($mypath as $indeks) {
41
		$ptr =& $ptr['queue'][$indeks];
42
	}
43

    
44
	return $ptr;
45
}
46

    
47
function unset_object_by_reference(&$mypath) {
48
	global $config;
49

    
50
	$ptr =& $config['shaper'];
51
	for ($i = 0; $i < count($mypath) - 1; $i++) {
52
		$ptr =& $ptr['queue'][$mypath[$i]];
53
	}
54
	unset($ptr['queue'][$mypath[$i]]);
55
}
56

    
57
function &get_dn_reference_to_me_in_config(&$mypath) {
58
	global $config;
59

    
60
	$ptr =& $config['dnshaper'];
61
	foreach ($mypath as $indeks) {
62
		$ptr =& $ptr['queue'][$indeks];
63
	}
64

    
65
	return $ptr;
66
}
67

    
68
function unset_dn_object_by_reference(&$mypath) {
69
	global $config;
70

    
71
	$ptr =& $config['dnshaper'];
72
	for ($i = 0; $i < count($mypath) - 1; $i++) {
73
		$ptr =& $ptr['queue'][$mypath[$i]];
74
	}
75
	unset($ptr['queue'][$mypath[$i]]);
76
}
77

    
78
function clean_child_queues($type, $mypath) {
79
	$ref = &get_reference_to_me_in_config($mypath);
80

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

    
156
function get_bandwidthtype_scale($type) {
157
	switch ($type) {
158
		case "Gb":
159
			$factor = 1024 * 1024 * 1024;
160
			break;
161
		case "Mb":
162
			$factor = 1024 * 1024;
163
			break;
164
		case "Kb":
165
			$factor = 1024;
166
			break;
167
		case "b":
168
		default:
169
			$factor = 1;
170
			break;
171
	}
172
	return intval($factor);
173
}
174

    
175
function get_bandwidth($bw, $scale, $obj) {
176

    
177
	$pattern= "/(b|Kb|Mb|Gb|%)/";
178
	if (!preg_match($pattern, $scale, $match))
179
		return 0;
180

    
181
	switch ($match[1]) {
182
		case '%':
183
			$objbw = ($bw / 100) * get_queue_bandwidth($obj);
184
			break;
185
		default:
186
			$objbw = $bw * get_bandwidthtype_scale($scale);
187
			break;
188
	}
189

    
190
	return floatval($objbw);
191
}
192

    
193
/*
194
 * XXX - unused
195
 *
196
function get_hfsc_bandwidth($object, $bw) {
197
	$pattern= "/[0-9]+/";
198
	if (preg_match($pattern, $bw, $match)) {
199
		$bw_1 = $match[1];
200
	} else {
201
		return 0;
202
	}
203
	$pattern= "/(b|Kb|Mb|Gb|%)/";
204
	if (preg_match($pattern, $bw, $match)) {
205
		switch ($match[1]) {
206
			case '%':
207
				$bw_1 = ($bw_1 / 100) * get_interface_bandwidth($object);
208
				break;
209
			default:
210
				$bw_1 = $bw_1 * get_bandwidthtype_scale($match[0]);
211
				break;
212
		}
213
		return floatval($bw_1);
214
	} else {
215
		return 0;
216
	}
217
}
218
*/
219

    
220
function get_queue_bandwidth($obj) {
221
	$bw = $obj->GetBandwidth();
222
	$scale = $obj->GetBwscale();
223

    
224
	$pattern= "/(b|Kb|Mb|Gb|%)/";
225
	if (!preg_match($pattern, $scale, $match))
226
		return 0;
227

    
228
	switch ($match[1]) {
229
		case '%':
230
			$objbw = ($bw / 100) * get_queue_bandwidth($obj->GetParent());
231
			break;
232
		default:
233
			$objbw = $bw * get_bandwidthtype_scale($scale);
234
			break;
235
	}
236

    
237
	return floatval($objbw);
238
}
239

    
240
function get_interface_bandwidth($object) {
241
	global $altq_list_queues;
242

    
243
	$int = $object->GetInterface();
244
	$altq =& $altq_list_queues[$int];
245
	if ($altq) {
246
		$bw_3 = $altq->GetBandwidth();
247
		$bw_3 = $bw_3 * get_bandwidthtype_scale($altq->GetBwscale());
248
		return floatval($bw_3);
249
	} else {
250
		return 0;
251
	}
252
}
253

    
254
/*
255
 * This is duplicated here since we cannot include guiconfig.inc.
256
 * Including it makes all stuff break.
257
 */
258
function shaper_do_input_validation($postdata, $reqdfields, $reqdfieldsn, $input_errors) {
259

    
260
	/* check for bad control characters */
261
	foreach ($postdata as $pn => $pd) {
262
		if (is_string($pd) && preg_match("/[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f]/", $pd)) {
263
			$input_errors[] = sprintf(gettext("The field '%s' contains invalid characters."), $pn);
264
		}
265
	}
266

    
267
	for ($i = 0; $i < count($reqdfields); $i++) {
268
		if ($postdata[$reqdfields[$i]] == "") {
269
			$input_errors[] = sprintf(gettext("The field '%s' is required."), $reqdfieldsn[$i]);
270
		}
271
	}
272
}
273

    
274
function cleanup_queue_from_rules($queue) {
275
	global $config;
276

    
277
	foreach ($config['filter']['rule'] as $rule) {
278
		if ($rule['defaultqueue'] == $queue) {
279
			unset($rule['defaultqueue']);
280
		}
281
		if ($rule['ackqueue'] == $queue) {
282
			unset($rule['ackqueue']);
283
		}
284
	}
285
}
286

    
287
function cleanup_dnqueue_from_rules($queue) {
288
	global $config;
289

    
290
	foreach ($config['filter']['rule'] as $rule) {
291
		if ($rule['dnpipe'] == $queue) {
292
			unset($rule['dnpipe']);
293
		}
294
		if ($rule['pdnpipe'] == $queue) {
295
			unset($rule['pdnpipe']);
296
		}
297
	}
298
}
299

    
300
class altq_root_queue {
301
	var $interface;
302
	var $tbrconfig ;
303
	var $bandwidth;
304
	var $bandwidthtype; /* b, Kb, Mb, Gb, % */
305
	var $scheduler;
306
	var $qlimit;
307
	var $queues = array();
308
	var $qenabled = false;
309
	var $link;
310

    
311
	/* Accessor functions */
312
	function GetDefaultQueuePresent() {
313
		if (!empty($this->queues)) {
314
			foreach ($this->queues as $q) {
315
				if ($q->GetDefault()) {
316
					return true;
317
				}
318
			}
319
		}
320

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

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

    
410
	function CheckBandwidth($bw, $bwtype) {
411
		$sum = $this->GetTotalBw();
412
		if ($sum > $bw * get_bandwidthtype_scale($bwtype))
413
			return 1;
414
		foreach ($this->queues as $q) {
415
			if ($q->CheckBandwidth(0, ''))
416
				return 1;
417
		}
418

    
419
		return 0;
420
	}
421

    
422
	function GetTotalBw($qignore = NULL) {
423
		$sum = 0;
424
		foreach ($this->queues as $q) {
425
			if ($qignore != NULL && $qignore == $q)
426
				continue;
427
			$sum += get_bandwidth($q->GetBandwidth(), $q->GetBwscale(), $this);
428
		}
429

    
430
		return $sum;
431
	}
432

    
433
	function validate_input($data, &$input_errors) {
434

    
435
		$reqdfields[] = "bandwidth";
436
		$reqdfieldsn[] = gettext("Bandwidth");
437
		$reqdfields[] = "bandwidthtype";
438
		$reqdfieldsn[] = gettext("Bandwidthtype");
439

    
440
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
441

    
442
		if (!isset($data['bandwidth']) || strlen($data['bandwidth']) == 0) {
443
			$input_errors[] = gettext("Bandwidth must be set.  This is usually the interface speed.");
444
		}
445
		if ($data['bandwidth'] && (!is_numeric($data['bandwidth']))) {
446
			$input_errors[] = gettext("Bandwidth must be an integer.");
447
		}
448
		if ($data['bandwidth'] < 0) {
449
			$input_errors[] = gettext("Bandwidth cannot be negative.");
450
		}
451
		if ($data['bandwidthtype'] == "%") {
452
			if ($data['bandwidth'] > 100 || $data['bandwidth'] < 0) {
453
				$input_errors[] = gettext("Bandwidth in percentage should be between 1 and 100.");
454
			}
455
		}
456
		if ($this->CheckBandwidth($data['bandwidth'], $data['bandwidthtype']))
457
			$input_errors[] = "The sum of child bandwidth is higher than parent.";
458

    
459
		if ($data['qlimit'] && (!is_numeric($data['qlimit']))) {
460
			$input_errors[] = gettext("Qlimit must be an integer.");
461
		}
462
		if ($data['qlimit'] < 0) {
463
			$input_errors[] = gettext("Qlimit must be positive.");
464
		}
465
		if ($data['tbrconfig'] && (!is_numeric($data['tbrconfig']))) {
466
			$input_errors[] = gettext("Tbrsize must be an integer.");
467
		}
468
		if ($data['tbrconfig'] < 0) {
469
			$input_errors[] = gettext("Tbrsize must be positive.");
470
		}
471
	}
472

    
473
	/* Implement this to shorten some code on the frontend page */
474
	function ReadConfig(&$conf) {
475
		if (isset($conf['tbrconfig'])) {
476
			$this->SetTbrConfig($conf['tbrconfig']);
477
		} else {
478
			$this->SetTbrConfig($conf['tbrconfig']);
479
		}
480
		$this->SetBandwidth($conf['bandwidth']);
481
		if ($conf['bandwidthtype'] <> "") {
482
			$this->SetBwscale($conf['bandwidthtype']);
483
		}
484
		if (isset($conf['scheduler'])) {
485
			if ($this->GetScheduler() != $conf['scheduler']) {
486
				foreach ($this->queues as $q) {
487
					clean_child_queues($conf['scheduler'], $this->GetLink());
488
					$q->clean_queue($conf['scheduler']);
489
				}
490
			}
491
			$this->SetScheduler($conf['scheduler']);
492
		}
493
		if (isset($conf['qlimit']) && $conf['qlimit'] <> "") {
494
			$this->SetQlimit($conf['qlimit']);
495
		} else {
496
			$this->SetQlimit("");
497
		}
498
		if (isset($conf['name'])) {
499
			$this->SetQname($conf['name']);
500
		}
501
		if (!empty($conf['enabled'])) {
502
			$this->SetEnabled($conf['enabled']);
503
		} else {
504
			$this->SetEnabled("");
505
		}
506
	}
507

    
508
	function copy_queue($interface, &$cflink) {
509
		$cflink['interface'] = $interface;
510
		$cflink['name'] = $interface;
511
		$cflink['scheduler'] = $this->GetScheduler();
512
		$cflink['bandwidth'] = $this->GetBandwidth();
513
		$cflink['bandwidthtype'] = $this->GetBwscale();
514
		$cflink['qlimit'] = $this->GetQlimit();
515
		$cflink['tbrconfig'] = $this->GetTbrConfig();
516
		$cflink['enabled'] = $this->GetEnabled();
517
		if (is_array($this->queues)) {
518
			$cflink['queue'] = array();
519
			foreach ($this->queues as $q) {
520
				$cflink['queue'][$q->GetQname()] = array();
521
				$q->copy_queue($interface, $cflink['queue'][$q->GetQname()]);
522
			}
523
		}
524
	}
525

    
526
	function &get_queue_list(&$q = null) {
527
		$qlist = array();
528

    
529
		//$qlist[$this->GetQname()] = & $this;
530
		if (is_array($this->queues)) {
531
			foreach ($this->queues as $queue) {
532
				$queue->get_queue_list($qlist);
533
			}
534
		}
535
		return $qlist;
536
	}
537

    
538
	function &add_queue($interface, &$queue, &$path, &$input_errors) {
539

    
540
		if (!is_array($this->queues)) {
541
			$this->queues = array();
542
		}
543

    
544
		switch ($this->GetScheduler()) {
545
			case "PRIQ":
546
				$q =& new priq_queue();
547
				break;
548
			case "HFSC":
549
				$q =& new hfsc_queue();
550
				break;
551
			case "CBQ":
552
				$q =& new cbq_queue();
553
				break;
554
			case "FAIRQ":
555
				$q =& new fairq_queue();
556
				break;
557
			default:
558
				/* XXX: but should not happen anyway */
559
				return;
560
				break;
561
		}
562
		$q->SetLink($path);
563
		$q->SetInterface($this->GetInterface());
564
		$q->SetEnabled("on");
565
		$q->SetParent($this);
566
		$q->ReadConfig($queue);
567
		$q->validate_input($queue, $input_errors);
568

    
569
		$this->queues[$q->GetQname()] = &$q;
570
		ref_on_altq_queue_list($this->GetQname(), $q->GetQname());
571
		if (is_array($queue['queue'])) {
572
			foreach ($queue['queue'] as $key1 => $que) {
573
				array_push($path, $key1);
574
				$q->add_queue($q->GetInterface(), $que, $path, $input_errors);
575
				array_pop($path);
576
			}
577
		}
578

    
579
		return $q;
580
	}
581

    
582
	/* interface here might be optional */
583
	function &find_queue($interface, $qname) {
584
		if ($qname == $this->GetQname()) {
585
			return $this;
586
		}
587
		foreach ($this->queues as $q) {
588
			$result =& $q->find_queue("", $qname);
589
			if ($result) {
590
				return $result;
591
			}
592
		}
593
	}
594

    
595
	function &find_parentqueue($interface, $qname) {
596
		if ($qname == $interface) {
597
			$result = NULL;
598
		} else if ($this->queues[$qname]) {
599
			$result = $this;
600
		} else if ($this->GetScheduler() <> "PRIQ") {
601
			foreach ($this->queues as $q) {
602
				$result = $q->find_parentqueue("", $qname);
603
				if ($result) {
604
					return $result;
605
				}
606
			}
607
		}
608
	}
609

    
610
	function build_tree() {
611
		global $shaperIFlist;
612

    
613
		$tree = " <li><a href=\"firewall_shaper.php?interface=".$this->GetInterface()."&amp;queue=". $this->GetInterface()."&amp;action=show";
614
		$tree .= "\">" . $shaperIFlist[$this->GetInterface()] . "</a>";
615
		if (is_array($this->queues)) {
616
			$tree .= "<ul>";
617
			foreach ($this->queues as $q) {
618
				$tree .= $q->build_tree();
619
			}
620
			$tree .= "</ul>";
621
		}
622
		$tree .= "</li>";
623
		return $tree;
624
	}
625

    
626
	function delete_queue() {
627
		foreach ($this->queues as $q)
628
			$q->delete_queue();
629
		unset_object_by_reference($this->GetLink());
630
	}
631

    
632
	function delete_all() {
633
		if (count($this->queues)) {
634
			foreach ($this->queues as $q) {
635
				$q->delete_all();
636
				unset_object_by_reference($q->GetLink());
637
				unset($q);
638
			}
639
			unset($this->queues);
640
		}
641
	}
642

    
643
	/*
644
	 * First it spits:
645
	 * altq on $interface ..............
646
	 *	then it goes like
647
	 *	foreach ($queues as $qkey => $queue) {
648
	 *		this->queues[$qkey]->build_rule();
649
	 *	}
650
	 */
651
	function build_rules(&$default = false) {
652
		if (count($this->queues) > 0 && $this->GetEnabled() == "on") {
653
			$default = false;
654
			$rules = " altq on " . get_real_interface($this->GetInterface());
655
			if ($this->GetScheduler()) {
656
				$rules .= " ".strtolower($this->GetScheduler());
657
			}
658
			if ($this->GetQlimit() > 0) {
659
				$rules .= " qlimit " . $this->GetQlimit() . " ";
660
			}
661
			if ($this->GetBandwidth()) {
662
				$rules .= " bandwidth ".trim($this->GetBandwidth());
663
				if ($this->GetBwscale()) {
664
					$rules .= $this->GetBwscale();
665
				}
666
			}
667
			if ($this->GetTbrConfig()) {
668
				$rules .= " tbrsize ".$this->GetTbrConfig();
669
			}
670
			if (count($this->queues)) {
671
				$i = count($this->queues);
672
				$rules .= " queue { ";
673
				foreach ($this->queues as $qkey => $qnone) {
674
					if ($i > 1) {
675
						$i--;
676
						$rules .= " {$qkey}, ";
677
					} else {
678
						$rules .= " {$qkey} ";
679
					}
680
				}
681
				$rules .= " } \n";
682
				foreach ($this->queues as $q) {
683
					$rules .= $q->build_rules($default);
684
				}
685
			}
686

    
687
			if ($default == false) {
688
				$error = sprintf(gettext("SHAPER: no default queue specified for interface %s."), $this->GetInterface()) . " " . gettext("The interface queue will be enforced as default.");
689
				file_notice("Shaper", $error, "Error occurred", "");
690
				unset($error);
691
				return "\n";
692
			}
693
			$frule .= $rules;
694
		} else if ($this->GetEnabled() == "on" && $this->GetScheduler() == "CODELQ") {
695
			$rules = " altq on " . get_real_interface($this->GetInterface());
696
			if ($this->GetScheduler()) {
697
				$rules .= " ".strtolower($this->GetScheduler());
698
			}
699
			if ($this->GetQlimit() > 0) {
700
				$rules .= " ( qlimit " . $this->GetQlimit() . " ) ";
701
			}
702
			if ($this->GetBandwidth()) {
703
				$rules .= " bandwidth ".trim($this->GetBandwidth());
704
				if ($this->GetBwscale()) {
705
					$rules .= $this->GetBwscale();
706
				}
707
			}
708
			if ($this->GetTbrConfig()) {
709
				$rules .= " tbrsize ".$this->GetTbrConfig();
710
			}
711

    
712
			$rules .= " queue";
713
		}
714

    
715
		$rules .= " \n";
716
		return $rules;
717
	}
718

    
719
	function build_javascript() {
720
		$javascript = "<script type=\"text/javascript\">";
721
		$javascript .= "//<![CDATA[\n";
722
		$javascript .= "function mySuspend() {";
723
		$javascript .= "if (document.layers && document.layers['shaperarea'] != null) ";
724
		$javascript .= "document.layers['shaperarea'].visibility = 'hidden'; ";
725
		$javascript .= "else if (document.all)";
726
		$javascript .= "document.all['shaperarea'].style.visibility = 'hidden';";
727
		$javascript .= "}";
728

    
729
		$javascript .= "function myResume() {";
730
		$javascript .= "if (document.layers && document.layers['shaperarea'] != null) ";
731
		$javascript .= "document.layers['shaperarea'].visibility = 'visible';";
732
		$javascript .= "else if (document.all) ";
733
		$javascript .= "document.all['shaperarea'].style.visibility = 'visible';";
734
		$javascript .= "}";
735
		$javascript .= "//]]>";
736
		$javascript .= "</script>";
737

    
738
		return $javascript;
739
	}
740

    
741
	function build_shortform() {
742
		global $g;
743

    
744
		$altq =& $this;
745

    
746
		if ($altq) {
747
			$scheduler = ": " . $altq->GetScheduler();
748
		}
749

    
750
		$form = '<dl class="dl-horizontal">';
751
		$form .= '	<dt>';
752
		$form .= '		<a href="firewall_shaper.php?interface=' . $this->GetInterface() . '&amp;queue=' . $this->GetQname() . '&amp;action=show">' . $shaperIFlist[$this->GetInterface()] . '</a>';
753
		$form .= '	</dt>';
754
		$form .= '	<dd>';
755
		$form .=		$scheduler;
756
		$form .= '	</dd>';
757

    
758
		$form .= '	<dt>';
759
		$form .=		'Bandwidth';
760
		$form .= '	</dt>';
761
		$form .= '	<dd>';
762
		$form .=		$this->GetBandwidth() . '&nbsp;' . $this->GetBwscaleText();
763
		$form .= '	</dd>';
764

    
765
		$form .= '	<dt>';
766
		$form .= 'Disable';
767
		$form .= '	<dt>';
768
		$form .= '	<dd>';
769

    
770
		$form .= '<a class="btn btn-danger btn-xs" href="firewall_shaper_queues.php?interface=';
771
		$form .= $this->GetInterface() . '&amp;queue=';
772
		$form .= $this->GetQname() . '&amp;action=delete">';
773
		$form .= '<i class="fa fa-trash icon-embed-btn"></i>';
774
		$form .= gettext("Remove shaper from this interface") . '</a>';
775

    
776
		$form .= '	</dd>';
777

    
778
		$form .= '</dl>';
779

    
780
		return $form;
781

    
782
	}
783

    
784
	/*
785
	 * For requesting the parameters of the root queues
786
	 * to the user like the traffic wizard does.
787
	 */
788
	function build_form() {
789

    
790
		$sform = new Form();
791

    
792
		$sform->setAction("firewall_shaper.php");
793

    
794
		$section = new Form_Section(null);
795

    
796
		$section->addInput(new Form_Checkbox(
797
			'enabled',
798
			'Enable/Disable',
799
			'Enable/disable discipline and its children',
800
			($this->GetEnabled() == "on"),
801
			'on'
802
		));
803

    
804
		$section->addInput(new Form_StaticText(
805
			'*Name',
806
			$this->GetQname()
807
		));
808

    
809
		$section->addInput(new Form_Select(
810
			'scheduler',
811
			'Scheduler Type',
812
			$this->GetScheduler(),
813
			array('HFSC' => 'HFSC',
814
				  'CBQ' => 'CBQ',
815
				  'FAIRQ' => 'FAIRQ',
816
				  'CODELQ' => 'CODELQ',
817
				  'PRIQ' => 'PRIQ')
818
		))->setHelp('Changing this changes all child queues! Beware information can be lost.');
819

    
820
		$group = new Form_group('Bandwidth');
821

    
822
		$group->add(new Form_Input(
823
			'bandwidth',
824
			null,
825
			'number',
826
			$this->GetBandwidth()
827
		));
828

    
829
		$group->add(new Form_Select(
830
			'bandwidthtype',
831
			null,
832
			$this->GetBwscale(),
833
			array('Kb' => 'Kbit/s',
834
				  'Mb' => 'Mbit/s',
835
				  'Gb' => 'Gbit/s',
836
				  'b' => 'Bit/s',
837
				  '%' => '%')
838
		));
839

    
840
		$section->add($group);
841

    
842
		$section->addInput(new Form_Input(
843
			'qlimit',
844
			'Queue Limit',
845
			'number',
846
			$this->GetQlimit()
847
		));
848

    
849
		$section->addInput(new Form_Input(
850
			'tbrconfig',
851
			'TBR Size',
852
			'number',
853
			$this->GetTbrConfig()
854
		))->setHelp('Adjusts the size, in bytes, of the token bucket regulator. If not specified, heuristics based on the interface ' .
855
					'bandwidth are used to determine the size.');
856

    
857
		$section->addInput(new Form_Input(
858
			'interface',
859
			null,
860
			'hidden',
861
			$this->GetInterface()
862
		));
863

    
864
		$section->addInput(new Form_Input(
865
			'name',
866
			null,
867
			'hidden',
868
			$this->GetQname()
869
		));
870

    
871
		$sform->add($section);
872

    
873
		return($sform);
874
	}
875

    
876
	function update_altq_queue_data(&$data) {
877
		$this->ReadConfig($data);
878
	}
879

    
880
	/*
881
	 * Should call on each of it queues and subqueues
882
	 * the same function much like build_rules();
883
	 */
884
	function wconfig() {
885
		$cflink = &get_reference_to_me_in_config($this->GetLink());
886
		if (!is_array($cflink)) {
887
			$cflink = array();
888
		}
889
		$cflink['interface'] = $this->GetInterface();
890
		$cflink['name'] = $this->GetQname();
891
		$cflink['scheduler'] = $this->GetScheduler();
892
		$cflink['bandwidth'] = $this->GetBandwidth();
893
		$cflink['bandwidthtype'] = $this->GetBwscale();
894
		$cflink['qlimit'] = trim($this->GetQlimit());
895
		if (empty($cflink['qlimit'])) {
896
			unset($cflink['qlimit']);
897
		}
898
		$cflink['tbrconfig'] = trim($this->GetTbrConfig());
899
		if (empty($cflink['tbrconfig'])) {
900
			unset($cflink['tbrconfig']);
901
		}
902
		$cflink['enabled'] = $this->GetEnabled();
903
		if (empty($cflink['enabled'])) {
904
			unset($cflink['enabled']);
905
		}
906
	}
907

    
908
}
909

    
910
class priq_queue {
911
	var $qname;
912
	var $qinterface;
913
	var $qlimit;
914
	var $qpriority;
915
	var $description;
916
	var $isparent;
917
	var $qbandwidth;
918
	var $qbandwidthtype;
919
	var $qdefault = "";
920
	var $qrio = "";
921
	var $qred = "";
922
	var $qcodel = "";
923
	var $qecn = "";
924
	var $qack;
925
	var $qenabled = "";
926
	var $qparent;
927
	var $link;
928

    
929
	/* This is here to help with form building and building rules/lists */
930
	var $subqueues = array();
931

    
932
	/* Accessor functions */
933
	function SetLink($link) {
934
		$this->link = $link;
935
	}
936
	function GetLink() {
937
		return $this->link;
938
	}
939
	function &GetParent() {
940
		return $this->qparent;
941
	}
942
	function SetParent(&$parent) {
943
		$this->qparent = &$parent;
944
	}
945
	function GetEnabled() {
946
		return $this->qenabled;
947
	}
948
	function SetEnabled($value) {
949
		$this->qenabled = $value;
950
	}
951
	function CanHaveChildren() {
952
		return false;
953
	}
954
	function CanBeDeleted() {
955
		return true;
956
	}
957
	function GetQname() {
958
		return $this->qname;
959
	}
960
	function SetQname($name) {
961
		$this->qname = trim($name);
962
	}
963
	function GetBandwidth() {
964
		return $this->qbandwidth;
965
	}
966
	function SetBandwidth($bandwidth) {
967
		$this->qbandwidth = $bandwidth;
968
	}
969
	function GetInterface() {
970
		return $this->qinterface;
971
	}
972
	function SetInterface($name) {
973
		$this->qinterface = trim($name);
974
	}
975
	function GetQlimit() {
976
		return $this->qlimit;
977
	}
978
	function SetQlimit($limit) {
979
		$this->qlimit = $limit;
980
	}
981
	function GetQpriority() {
982
		return $this->qpriority;
983
	}
984
	function SetQpriority($priority) {
985
		$this->qpriority = $priority;
986
	}
987
	function GetDescription() {
988
		return $this->description;
989
	}
990
	function SetDescription($str) {
991
		$this->description = trim($str);
992
	}
993
	function GetFirstime() {
994
		return $this->firsttime;
995
	}
996
	function SetFirsttime($number) {
997
		$this->firsttime = $number;
998
	}
999
	function CheckBandwidth($bw, $bwtype) {
1000
		$parent = $this->GetParent();
1001
		$sum = $parent->GetTotalBw(($bw > 0) ? $this : NULL);
1002
		if ($bw > 0)
1003
			$sum += get_bandwidth($bw, $bwtype, $parent);
1004
		if ($sum > get_queue_bandwidth($parent))
1005
			return 1;
1006

    
1007
		foreach ($this->subqueues as $q) {
1008
			if ($q->CheckBandwidth(0, ''))
1009
				return 1;
1010
		}
1011

    
1012
		return 0;
1013
	}
1014
	function GetTotalBw($qignore = NULL) {
1015
		$sum = 0;
1016
		foreach ($this->subqueues as $q) {
1017
			if ($qignore != NULL && $qignore == $q)
1018
				continue;
1019
			$sum += get_bandwidth($q->GetBandwidth(), $q->GetBwscale(), $q->GetParent());
1020
		}
1021

    
1022
		return $sum;
1023
	}
1024
	function GetBwscale() {
1025
		return $this->qbandwidthtype;
1026
	}
1027
	function SetBwscale($scale) {
1028
		$this->qbandwidthtype = $scale;
1029
	}
1030
	function GetDefaultQueuePresent() {
1031
		if ($this->GetDefault()) {
1032
			return true;
1033
		}
1034
		if (!empty($this->subqueues)) {
1035
			foreach ($this->subqueues as $q) {
1036
				if ($q->GetDefault()) {
1037
					return true;
1038
				}
1039
			}
1040
		}
1041

    
1042
		return false;
1043
	}
1044
	function GetDefault() {
1045
		return $this->qdefault;
1046
	}
1047
	function SetDefault($value = false) {
1048
		$this->qdefault = $value;
1049
	}
1050
	function GetCodel() {
1051
		return $this->codel;
1052
	}
1053
	function SetCodel($codel = false) {
1054
		$this->codel = $codel;
1055
	}
1056
	function GetRed() {
1057
		return $this->qred;
1058
	}
1059
	function SetRed($red = false) {
1060
		$this->qred = $red;
1061
	}
1062
	function GetRio() {
1063
		return $this->qrio;
1064
	}
1065
	function SetRio($rio = false) {
1066
		$this->qrio = $rio;
1067
	}
1068
	function GetEcn() {
1069
		return $this->qecn;
1070
	}
1071
	function SetEcn($ecn = false) {
1072
		$this->qecn = $ecn;
1073
	}
1074
	function GetAck() {
1075
		return $this->qack;
1076
	}
1077
	function SetAck($ack = false) {
1078
		$this->qack = $ack;
1079
	}
1080

    
1081
	function GetBwscaleText() {
1082
		switch ($this->qbandwidthtype) {
1083
			case "b":
1084
				$bwscaletext = "Bit/s";
1085
				break;
1086
			case "Kb":
1087
				$bwscaletext = "Kbit/s";
1088
				break;
1089
			case "Mb":
1090
				$bwscaletext = "Mbit/s";
1091
				break;
1092
			case "Gb":
1093
				$bwscaletext = "Gbit/s";
1094
				break;
1095
			default:
1096
				/* For others that do not need translating like % */
1097
				$bwscaletext = $this->qbandwidthtype;
1098
				break;
1099
		}
1100
		return $bwscaletext;
1101
	}
1102

    
1103
	function build_javascript() {
1104
		$javascript = "<script type=\"text/javascript\">";
1105
		$javascript .= "//<![CDATA[\n";
1106
		$javascript .= "function mySuspend() { \n";
1107
		$javascript .= "if (document.layers && document.layers['shaperarea'] != null)\n";
1108
		$javascript .= "document.layers['shaperarea'].visibility = 'hidden';\n";
1109
		$javascript .= "else if (document.all)\n";
1110
		$javascript .= "document.all['shaperarea'].style.visibility = 'hidden';\n";
1111
		$javascript .= "}\n";
1112

    
1113
		$javascript .= "function myResume() {\n";
1114
		$javascript .= "if (document.layers && document.layers['shaperarea'] != null)\n";
1115
		$javascript .= "document.layers['shaperarea'].visibility = 'visible';\n";
1116
		$javascript .= "else if (document.all)\n";
1117
		$javascript .= "document.all['shaperarea'].style.visibility = 'visible';\n";
1118
		$javascript .= "}\n";
1119
		$javascript .= "//]]>";
1120
		$javascript .= "</script>";
1121

    
1122
		return $javascript;
1123
	}
1124

    
1125
	function &add_queue($interface, &$qname, &$path, &$input_errors) { return; }
1126

    
1127
	/*
1128
	 * Currently this will not be called unless we decide to clone a whole
1129
	 * queue tree on the 'By Queues' view or support drag&drop on the tree/list
1130
	 */
1131
	function copy_queue($interface, &$cflink) {
1132

    
1133
		$cflink['name'] = $this->GetQname();
1134
		$cflink['interface'] = $interface;
1135
		$cflink['qlimit'] = $this->GetQlimit();
1136
		$cflink['priority'] = $this->GetQpriority();
1137
		$cflink['description'] = $this->GetDescription();
1138
		$cflink['enabled'] = $this->GetEnabled();
1139
		$cflink['default'] = $this->GetDefault();
1140
		$cflink['red'] = $this->GetRed();
1141
		$cflink['codel'] = $this->GetCodel();
1142
		$cflink['rio'] = $this->GetRio();
1143
		$cflink['ecn'] = $this->GetEcn();
1144

    
1145
		if (is_array($this->subqueues)) {
1146
			$cflinkp['queue'] = array();
1147
			foreach ($this->subqueues as $q) {
1148
				$cflink['queue'][$q->GetQname()] = array();
1149
				$q->copy_queue($interface, $cflink['queue'][$q->GetQname()]);
1150
			}
1151
		}
1152
	}
1153

    
1154
	function clean_queue($sched) {
1155
		clean_child_queues($sched, $this->GetLink());
1156
		if (is_array($this->subqueues)) {
1157
			foreach ($this->subqueues as $q) {
1158
				$q->clean_queue($sched);
1159
			}
1160
		}
1161
	}
1162

    
1163
	function &get_queue_list(&$qlist) {
1164

    
1165
		$qlist[$this->GetQname()] = & $this;
1166
		if (is_array($this->subqueues)) {
1167
			foreach ($this->subqueues as $queue) {
1168
				$queue->get_queue_list($qlist);
1169
			}
1170
		}
1171
	}
1172

    
1173
	function delete_queue() {
1174
		unref_on_altq_queue_list($this->GetQname());
1175
		cleanup_queue_from_rules($this->GetQname());
1176
		unset_object_by_reference($this->GetLink());
1177
	}
1178

    
1179
	function delete_all() {
1180
		if (count($this->subqueues)) {
1181
			foreach ($this->subqueues as $q) {
1182
				$q->delete_all();
1183
				unset_object_by_reference($q->GetLink());
1184
				unset($q);
1185
			}
1186
			unset($this->subqueues);
1187
		}
1188
	}
1189

    
1190
	function &find_queue($interface, $qname) {
1191
		if ($qname == $this->GetQname()) {
1192
			return $this;
1193
		}
1194
	}
1195

    
1196
	function find_parentqueue($interface, $qname) { return; }
1197

    
1198
	function validate_input($data, &$input_errors) {
1199

    
1200
		$reqdfields[] = "name";
1201
		$reqdfieldsn[] = gettext("Name");
1202
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
1203

    
1204
		if ($data['bandwidth'] && (!is_numeric($data['bandwidth']))) {
1205
			$input_errors[] = gettext("Bandwidth must be an integer.");
1206
		}
1207
		if ($data['bandwidth'] < 0) {
1208
			$input_errors[] = gettext("Bandwidth cannot be negative.");
1209
		}
1210
		if ($data['bandwidthtype'] == "%") {
1211
			if ($data['bandwidth'] > 100 || $data['bandwidth'] < 0) {
1212
				$input_errors[] = gettext("Bandwidth in percentage should be between 1 and 100.");
1213
			}
1214
		}
1215
		if ($this->CheckBandwidth($data['bandwidth'], $data['bandwidthtype']))
1216
			$input_errors[] = "The sum of child bandwidth is higher than parent.";
1217
		
1218
		if (isset($data['priority']) && (!is_numeric($data['priority']) ||
1219
		    ($data['priority'] < 0) || ($data['priority'] > 15))) {
1220
			$input_errors[] = gettext("The priority must be an integer between 1 and 15.");
1221
		}
1222
		if ($data['qlimit'] && (!is_numeric($data['qlimit']))) {
1223
				$input_errors[] = gettext("Queue limit must be an integer");
1224
		}
1225
		if ($data['qlimit'] < 0) {
1226
				$input_errors[] = gettext("Queue limit must be positive");
1227
		}
1228
		if (!empty($data['newname']) && !preg_match("/^[a-zA-Z0-9_-]*$/", $data['newname'])) {
1229
			$input_errors[] = gettext("Queue names must be alphanumeric and _ or - only.");
1230
		}
1231
		if (!empty($data['name']) && !preg_match("/^[a-zA-Z0-9_-]*$/", $data['name'])) {
1232
			$input_errors[] = gettext("Queue names must be alphanumeric and _ or - only.");
1233
		}
1234
		$default = $this->GetDefault();
1235
		if (!empty($data['default']) && altq_get_default_queue($data['interface']) && empty($default)) {
1236
			$input_errors[] = gettext("Only one default queue per interface is allowed.");
1237
		}
1238
	}
1239

    
1240
	function ReadConfig(&$q) {
1241
		if (!empty($q['name']) && !empty($q['newname']) && $q['name'] != $q['newname']) {
1242
			$this->SetQname($q['newname']);
1243
		} else if (!empty($q['newname'])) {
1244
			$this->SetQname($q['newname']);
1245
		} else if (isset($q['name'])) {
1246
			$this->SetQname($q['name']);
1247
		}
1248
		if (isset($q['interface'])) {
1249
			$this->SetInterface($q['interface']);
1250
		}
1251
		$this->SetBandwidth($q['bandwidth']);
1252
		if ($q['bandwidthtype'] <> "") {
1253
			$this->SetBwscale($q['bandwidthtype']);
1254
		}
1255
		if (!empty($q['qlimit'])) {
1256
			$this->SetQlimit($q['qlimit']);
1257
		} else {
1258
			$this->SetQlimit(""); // Default
1259
		}
1260
		if (is_numeric($q['priority'])) {
1261
			$this->SetQPriority($q['priority']);
1262
		} else {
1263
			$this->SetQpriority("");
1264
		}
1265
		if (!empty($q['description'])) {
1266
			$this->SetDescription($q['description']);
1267
		} else {
1268
			$this->SetDescription("");
1269
		}
1270
		if (!empty($q['red'])) {
1271
			$this->SetRed($q['red']);
1272
		} else {
1273
			$this->SetRed();
1274
		}
1275
		if (!empty($q['codel'])) {
1276
			$this->SetCodel($q['codel']);
1277
		} else {
1278
			$this->SetCodel();
1279
		}
1280
		if (!empty($q['rio'])) {
1281
			$this->SetRio($q['rio']);
1282
		} else {
1283
			$this->SetRio();
1284
		}
1285
		if (!empty($q['ecn'])) {
1286
			$this->SetEcn($q['ecn']);
1287
		} else {
1288
			$this->SetEcn();
1289
		}
1290
		if (!empty($q['default'])) {
1291
			$this->SetDefault($q['default']);
1292
		} else {
1293
			$this->SetDefault();
1294
		}
1295
		if (!empty($q['enabled'])) {
1296
			$this->SetEnabled($q['enabled']);
1297
		} else {
1298
			$this->SetEnabled("");
1299
		}
1300
	}
1301

    
1302
	function build_tree() {
1303
		$tree = " <li><a href=\"firewall_shaper.php?interface=". $this->GetInterface()."&amp;queue=". $this->GetQname()."&amp;action=show";
1304
		$tree .= "\" ";
1305
		$tmpvalue = $this->GetDefault();
1306
		if (!empty($tmpvalue)) {
1307
			$tree .= " class=\"navlnk\"";
1308
		}
1309
		$tree .= " >" . $this->GetQname() . "</a>";
1310
		/*
1311
		 * Not needed here!
1312
		 * if (is_array($queues) {
1313
		 *	  $tree .= "<ul>";
1314
		 *	  foreach ($q as $queues)
1315
		 *		  $tree .= $queues['$q->GetName()']->build_tree();
1316
		 *	  endforeach
1317
		 *	  $tree .= "</ul>";
1318
		 * }
1319
		 */
1320

    
1321
		$tree .= "</li>";
1322

    
1323
		return $tree;
1324
	}
1325

    
1326
	/* Should return something like:
1327
	 * queue $qname on $qinterface bandwidth ....
1328
	 */
1329
	function build_rules(&$default = false) {
1330
		$pfq_rule = " queue ". $this->qname;
1331
		if ($this->GetInterface()) {
1332
			$pfq_rule .= " on ".get_real_interface($this->GetInterface());
1333
		}
1334
		$tmpvalue = $this->GetQpriority();
1335
		if (is_numeric($tmpvalue)) {
1336
			$pfq_rule .= " priority ".$this->GetQpriority();
1337
		}
1338
		$tmpvalue = $this->GetQlimit();
1339
		if (!empty($tmpvalue)) {
1340
			$pfq_rule .= " qlimit " . $this->GetQlimit();
1341
		}
1342
		if ($this->GetRed() || $this->GetRio() || $this->GetEcn() || $this->GetDefault() || $this->GetCodel()) {
1343
			$pfq_rule .= " priq ( ";
1344
			$tmpvalue = $this->GetRed();
1345
			if (!empty($tmpvalue)) {
1346
				$comma = 1;
1347
				$pfq_rule .= " red ";
1348
			}
1349
			$tmpvalue = $this->GetRio();
1350
			if (!empty($tmpvalue)) {
1351
				if ($comma) {
1352
					$pfq_rule .= " ,";
1353
				}
1354
				$comma = 1;
1355
				$pfq_rule .= " rio ";
1356
			}
1357
			$tmpvalue = $this->GetEcn();
1358
			if (!empty($tmpvalue)) {
1359
				if ($comma) {
1360
					$pfq_rule .= " ,";
1361
				}
1362
				$comma = 1;
1363
				$pfq_rule .= " ecn ";
1364
			}
1365
			$tmpvalue = $this->GetCodel();
1366
			if (!empty($tmpvalue)) {
1367
				if ($comma) {
1368
					$pfq_rule .= " ,";
1369
				}
1370
				$comma = 1;
1371
				$pfq_rule .= " codel ";
1372
			}
1373
			$tmpvalue = $this->GetDefault();
1374
			if (!empty($tmpvalue)) {
1375
				if ($comma) {
1376
					$pfq_rule .= " ,";
1377
				}
1378
				$pfq_rule .= " default ";
1379
				$default = true;
1380
			}
1381
			$pfq_rule .= " ) ";
1382
		}
1383

    
1384
		$pfq_rule .= " \n";
1385

    
1386
		return $pfq_rule;
1387
	}
1388

    
1389
	/*
1390
	 * To return the html form to show to user
1391
	 * for getting the parameters.
1392
	 * Should do even for first time when the
1393
	 * object is created and later when we may
1394
	 * need to update it. (2)
1395
	 */
1396

    
1397
	function build_form() {
1398

    
1399
		$sform = new Form();
1400

    
1401
		$sform->setAction("firewall_shaper.php");
1402

    
1403
		$section = new Form_Section("");
1404

    
1405
		$section->addInput(new Form_Checkbox(
1406
			'enabled',
1407
			'Enable/Disable',
1408
			'Enable/disable discipline and its children',
1409
			($this->GetEnabled() == "on"),
1410
			'on'
1411
		));
1412

    
1413
		$section->addInput(new Form_Input(
1414
			'newname',
1415
			'*Name',
1416
			'text',
1417
			$this->GetQname()
1418
		))->setHelp('Enter the name of the queue here. Do not use spaces and limit the size to 15 characters.');
1419

    
1420
		$section->addInput(new Form_Input(
1421
			'name',
1422
			null,
1423
			'hidden',
1424
			$this->GetQname()
1425
		));
1426

    
1427
		if (!is_a($this, "hfsc_queue")) {
1428
			$section->addInput(new Form_Input(
1429
				'priority',
1430
				'Priority',
1431
				'number',
1432
				$this->GetQpriority(),
1433
				['min' => '0', 'max'=> '']
1434
			))->setHelp('For cbq and fairq the range is 0 to 7. The default is 1. For priq the range is 0 to 15, queues with a higher priority are preferred in the case of overload.');
1435
		}
1436

    
1437
		$section->addInput(new Form_Input(
1438
			'qlimit',
1439
			'Queue Limit',
1440
			'number',
1441
			$this->GetQlimit()
1442
		))->setHelp('Queue limit in packets.');
1443

    
1444
		$group = new Form_Group('Scheduler options');
1445

    
1446
		if (empty($this->subqueues)) {
1447
			$group->add(new Form_Checkbox(
1448
				'default',
1449
				null,
1450
				null,
1451
				$this->GetDefault(),
1452
				'default'
1453
			))->setHelp('Default Queue');
1454
		}
1455

    
1456
		$group->add(new Form_Checkbox(
1457
			'red',
1458
			null,
1459
			null,
1460
			!empty($this->GetRed())
1461
		))->setHelp('%1$sRandom Early Detection%2$s', '<a target="_new" href="https://web.archive.org/web/20160404153707/http://www.openbsd.org/faq/pf/queueing.html#red">', '</a>');
1462

    
1463
		$group->add(new Form_Checkbox(
1464
			'rio',
1465
			null,
1466
			null,
1467
			!empty($this->GetRio())
1468
		))->setHelp('%1$sRandom Early Detection In and Out%2$s', '<a target="_new" href="https://web.archive.org/web/20160404153707/http://www.openbsd.org/faq/pf/queueing.html#rio">', '</a>');
1469

    
1470
		$group->add(new Form_Checkbox(
1471
			'ecn',
1472
			null,
1473
			null,
1474
			!empty($this->GetEcn())
1475
		))->setHelp('%1$sExplicit Congestion Notification%2$s', '<a target="_new" href="https://web.archive.org/web/20160404153707/http://www.openbsd.org/faq/pf/queueing.html#ecn">', '</a>');
1476

    
1477
		$group->add(new Form_Checkbox(
1478
			'codel',
1479
			null,
1480
			null,
1481
			!empty($this->GetCodel())
1482
		))->setHelp('%1$sCodel Active Queue%2$s', '<a target="_new" href="https://web.archive.org/web/20160404153707/http://www.openbsd.org/faq/pf/queueing.html#ecn">', '</a>');
1483

    
1484
		$group->setHelp('Select options for this queue');
1485

    
1486
		$section->add($group);
1487

    
1488
		$section->addInput(new Form_Input(
1489
			'description',
1490
			'Description',
1491
			'text',
1492
			$this->GetDescription()
1493
		));
1494

    
1495
		$sform->add($section);
1496

    
1497
		$sform->addGlobal(new Form_Input(
1498
			'interface',
1499
			null,
1500
			'hidden',
1501
			$this->GetInterface()
1502
		));
1503

    
1504
		$sform->addGlobal(new Form_Input(
1505
			'name',
1506
			null,
1507
			'hidden',
1508
			$this->GetQname()
1509
		));
1510

    
1511
		return($sform);
1512
	}
1513

    
1514
	function build_shortform() {
1515
		/* XXX: Hacks in sight. Mostly layer violations!  */
1516
		global $g, $altq_list_queues;
1517
		global $shaperIFlist;
1518

    
1519
		$altq =& $altq_list_queues[$this->GetInterface()];
1520

    
1521
		if ($altq) {
1522
			$scheduler = $altq->GetScheduler();
1523
		}
1524

    
1525
		$form = '<dl class="dl-horizontal">';
1526
		$form .= '	<dt>';
1527
		$form .= '		<a href="firewall_shaper.php?interface=' . $this->GetInterface() . '&amp;queue=' . $this->GetQname() . '&amp;action=show">' . $shaperIFlist[$this->GetInterface()] . '</a>';
1528
		$form .= '	</dt>';
1529
		$form .= '	<dd>';
1530
		$form .=		$scheduler;
1531
		$form .= '	</dd>';
1532

    
1533
		$form .= '	<dt>';
1534
		$form .=		'Bandwidth';
1535
		$form .= '	</dt>';
1536
		$form .= '	<dd>';
1537
		$form .=		$this->GetBandwidth() . '&nbsp;' . $this->GetBwscaleText();
1538
		$form .= '	</dd>';
1539

    
1540
		$tmpvalue = $this->GetQpriority();
1541
		if (!empty($tmpvalue)) {
1542
			$form .= '	<dt>';
1543
			$form .=		'Priority';
1544
			$form .= '	<dt>';
1545
			$form .= '	<dd>';
1546
			$form .=		'On';
1547
			$form .= '	</dd>';
1548
		}
1549

    
1550
		$tmpvalue = $this->GetDefault();
1551
		if (!empty($tmpvalue)) {
1552
			$form .= '	<dt>';
1553
			$form .=		'Default';
1554
			$form .= '	<dt>';
1555
			$form .= '	<dd>';
1556
			$form .=		'On';
1557
			$form .= '	</dd>';
1558
		}
1559

    
1560
			$form .= '	<dt>';
1561
			$form .= 'Delete';
1562
			$form .= '	<dt>';
1563
			$form .= '	<dd>';
1564

    
1565
			$form .= '<a class="btn btn-danger btn-xs" href="firewall_shaper_queues.php?interface=';
1566
			$form .= $this->GetInterface() . '&amp;queue=';
1567
			$form .= $this->GetQname() . '&amp;action=delete">';
1568
			$form .= '<i class="fa fa-trash icon-embed-btn"></i>';
1569
			$form .= gettext("Delete Queue from this Interface") . '</a>';
1570

    
1571
			$form .= '	</dd>';
1572

    
1573
			$form .= '</dl>';
1574

    
1575
		return $form;
1576

    
1577
	}
1578

    
1579
	function update_altq_queue_data(&$q) {
1580
		$this->ReadConfig($q);
1581
	}
1582

    
1583
	function wconfig() {
1584
		$cflink =& get_reference_to_me_in_config($this->GetLink());
1585
		if (!is_array($cflink)) {
1586
			$cflink = array();
1587
		}
1588
		$cflink['name'] = $this->GetQname();
1589
		$cflink['interface'] = $this->GetInterface();
1590
		$cflink['qlimit'] = trim($this->GetQlimit());
1591
		if (empty($cflink['qlimit'])) {
1592
			unset($cflink['qlimit']);
1593
		}
1594
		$cflink['priority'] = trim($this->GetQpriority());
1595
		if (!is_numeric($cflink['priority'])) {
1596
			unset($cflink['priority']);
1597
		}
1598
		$cflink['description'] = trim($this->GetDescription());
1599
		if (empty($cflink['description'])) {
1600
			unset($cflink['description']);
1601
		}
1602
		$cflink['enabled'] = trim($this->GetEnabled());
1603
		if (empty($cflink['enabled'])) {
1604
			unset($cflink['enabled']);
1605
		}
1606
		$cflink['default'] = trim($this->GetDefault());
1607
		if (empty($cflink['default'])) {
1608
			unset($cflink['default']);
1609
		}
1610
		$cflink['red'] = trim($this->GetRed());
1611
		if (empty($cflink['red'])) {
1612
			unset($cflink['red']);
1613
		}
1614
		$cflink['codel'] = trim($this->GetCodel());
1615
		if (empty($cflink['codel'])) {
1616
			unset($cflink['codel']);
1617
		}
1618
		$cflink['rio'] = trim($this->GetRio());
1619
		if (empty($cflink['rio'])) {
1620
			unset($cflink['rio']);
1621
		}
1622
		$cflink['ecn'] = trim($this->GetEcn());
1623
		if (empty($cflink['ecn'])) {
1624
			unset($cflink['ecn']);
1625
		}
1626
	}
1627
}
1628

    
1629
class hfsc_queue extends priq_queue {
1630
	/* realtime */
1631
	var $realtime;
1632
	var $r_m1;
1633
	var $r_d;
1634
	var $r_m2;
1635
	/* linkshare */
1636
	var $linkshare;
1637
	var $l_m1;
1638
	var $l_d;
1639
	var $l_m2;
1640
	/* upperlimit */
1641
	var $upperlimit;
1642
	var $u_m1;
1643
	var $u_d;
1644
	var $u_m2;
1645

    
1646
	/*
1647
	 * HFSC can have nested queues.
1648
	 */
1649
	function CanHaveChildren() {
1650
		return true;
1651
	}
1652
	function GetRealtime() {
1653
		return $this->realtime;
1654
	}
1655
	function GetR_m1() {
1656
		return $this->r_m1;
1657
	}
1658
	function GetR_d() {
1659
		return $this->r_d;
1660
	}
1661
	function GetR_m2() {
1662
		return $this->r_m2;
1663
	}
1664
	function SetRealtime() {
1665
		$this->realtime = "on";
1666
	}
1667
	function DisableRealtime() {
1668
		$this->realtime = "";
1669
	}
1670
	function SetR_m1($value) {
1671
		$this->r_m1 = $value;
1672
	}
1673
	function SetR_d($value) {
1674
		$this->r_d = $value;
1675
	}
1676
	function SetR_m2($value) {
1677
		$this->r_m2 = $value;
1678
	}
1679
	function GetLinkshare() {
1680
		return $this->linkshare;
1681
	}
1682
	function DisableLinkshare() {
1683
		$this->linkshare = "";
1684
	}
1685
	function GetL_m1() {
1686
		return $this->l_m1;
1687
	}
1688
	function GetL_d() {
1689
		return $this->l_d;
1690
	}
1691
	function GetL_m2() {
1692
		return $this->l_m2;
1693
	}
1694
	function SetLinkshare() {
1695
		$this->linkshare = "on";
1696
	}
1697
	function SetL_m1($value) {
1698
		$this->l_m1 = $value;
1699
	}
1700
	function SetL_d($value) {
1701
		$this->l_d = $value;
1702
	}
1703
	function SetL_m2($value) {
1704
		$this->l_m2 = $value;
1705
	}
1706
	function GetUpperlimit() {
1707
		return $this->upperlimit;
1708
	}
1709
	function GetU_m1() {
1710
		return $this->u_m1;
1711
	}
1712
	function GetU_d() {
1713
		return $this->u_d;
1714
	}
1715
	function GetU_m2() {
1716
		return $this->u_m2;
1717
	}
1718
	function SetUpperlimit() {
1719
		$this->upperlimit = "on";
1720
	}
1721
	function DisableUpperlimit() {
1722
		$this->upperlimit = "";
1723
	}
1724
	function SetU_m1($value) {
1725
		$this->u_m1 = $value;
1726
	}
1727
	function SetU_d($value) {
1728
		$this->u_d = $value;
1729
	}
1730
	function SetU_m2($value) {
1731
		$this->u_m2 = $value;
1732
	}
1733

    
1734
	function &add_queue($interface, &$qname, &$path, &$input_errors) {
1735

    
1736
		if (!is_array($this->subqueues)) {
1737
			$this->subqueues = array();
1738
		}
1739
		$q =& new hfsc_queue();
1740
		$q->SetInterface($this->GetInterface());
1741
		$q->SetParent($this);
1742
		$q->ReadConfig($qname);
1743
		$q->validate_input($qname, $input_errors);
1744

    
1745
		$q->SetEnabled("on");
1746
		$q->SetLink($path);
1747

    
1748
		$this->subqueues[$q->GetQname()] =& $q; //new hfsc_queue()
1749
		ref_on_altq_queue_list($this->GetQname(), $q->GetQname());
1750
		if (is_array($qname['queue'])) {
1751
			foreach ($qname['queue'] as $key1 => $que) {
1752
				array_push($path, $key1);
1753
				$q->add_queue($q->GetInterface(), $que, $path, $input_errors);
1754
				array_pop($path);
1755
			}
1756
		}
1757

    
1758
		return $q;
1759
	}
1760

    
1761
	function copy_queue($interface, &$cflink) {
1762

    
1763
		$cflink['name'] = $this->GetQname();
1764
		$cflink['interface'] = $interface;
1765
		$cflink['qlimit'] = trim($this->GetQlimit());
1766
		if (empty($cflink['qlimit'])) {
1767
			unset($cflink['qlimit']);
1768
		}
1769
		$cflink['priority'] = trim($this->GetQpriority());
1770
		if (empty($cflink['priority'])) {
1771
			unset($cflink['priority']);
1772
		}
1773
		$cflink['description'] = trim($this->GetDescription());
1774
		if (empty($cflink['description'])) {
1775
			unset($cflink['description']);
1776
		}
1777
		$cflink['bandwidth'] = $this->GetBandwidth();
1778
		$cflink['bandwidthtype'] = $this->GetBwscale();
1779
		$cflink['enabled'] = trim($this->GetEnabled());
1780
		if (empty($cflink['enabled'])) {
1781
			unset($cflink['enabled']);
1782
		}
1783
		$cflink['default'] = trim($this->GetDefault());
1784
		if (empty($cflink['default'])) {
1785
			unset($cflink['default']);
1786
		}
1787
		$cflink['red'] = trim($this->GetRed());
1788
		if (empty($cflink['red'])) {
1789
			unset($cflink['red']);
1790
		}
1791
		$cflink['rio'] = trim($this->GetRio());
1792
		if (empty($cflink['rio'])) {
1793
			unset($cflink['rio']);
1794
		}
1795
		$cflink['ecn'] = trim($this->GetEcn());
1796
		if (empty($cflink['ecn'])) {
1797
			unset($cflink['ecn']);
1798
		}
1799
		if ($this->GetLinkshare() <> "") {
1800
			if ($this->GetL_m1() <> "") {
1801
				$cflink['linkshare1'] = $this->GetL_m1();
1802
				$cflink['linkshare2'] = $this->GetL_d();
1803
				$cflink['linkshare'] = "on";
1804
			} else {
1805
				unset($cflink['linkshare1']);
1806
				unset($cflink['linkshare2']);
1807
				unset($cflink['linkshare']);
1808
			}
1809
			if ($this->GetL_m2() <> "") {
1810
				$cflink['linkshare3'] = $this->GetL_m2();
1811
				$cflink['linkshare'] = "on";
1812
			} else {
1813
				unset($cflink['linkshare3']);
1814
				unset($cflink['linkshare']);
1815
			}
1816
		}
1817
		if ($this->GetRealtime() <> "") {
1818
			if ($this->GetR_m1() <> "") {
1819
				$cflink['realtime1'] = $this->GetR_m1();
1820
				$cflink['realtime2'] = $this->GetR_d();
1821
				$cflink['realtime'] = "on";
1822
			} else {
1823
				unset($cflink['realtime1']);
1824
				unset($cflink['realtime2']);
1825
				unset($cflink['realtime']);
1826
			}
1827
			if ($this->GetR_m2() <> "") {
1828
				$cflink['realtime3'] = $this->GetR_m2();
1829
				$cflink['realtime'] = "on";
1830
			} else {
1831
				unset($cflink['realtime3']);
1832
				unset($cflink['realtime']);
1833
			}
1834
		}
1835
		if ($this->GetUpperlimit() <> "") {
1836
			if ($this->GetU_m1() <> "") {
1837
				$cflink['upperlimit1'] = $this->GetU_m1();
1838
				$cflink['upperlimit2'] = $this->GetU_d();
1839
				$cflink['upperlimit'] = "on";
1840
			} else {
1841
				unset($cflink['upperlimit']);
1842
				unset($cflink['upperlimit1']);
1843
				unset($cflink['upperlimit2']);
1844
			}
1845
			if ($this->GetU_m2() <> "") {
1846
				$cflink['upperlimit3'] = $this->GetU_m2();
1847
				$cflink['upperlimit'] = "on";
1848
			} else {
1849
				unset($cflink['upperlimit3']);
1850
				unset($cflink['upperlimit']);
1851
			}
1852
		}
1853

    
1854
		if (is_array($this->subqueues)) {
1855
			$cflinkp['queue'] = array();
1856
			foreach ($this->subqueues as $q) {
1857
				$cflink['queue'][$q->GetQname()] = array();
1858
				$q->copy_queue($interface, $cflink['queue'][$q->GetQname()]);
1859
			}
1860
		}
1861
	}
1862

    
1863
	function delete_queue() {
1864
		unref_on_altq_queue_list($this->GetQname());
1865
		cleanup_queue_from_rules($this->GetQname());
1866
		$parent =& $this->GetParent();
1867
		foreach ($this->subqueues as $q)
1868
			$q->delete_queue();
1869
		unset_object_by_reference($this->GetLink());
1870
	}
1871

    
1872
	/*
1873
	 * Should search even its children
1874
	 */
1875
	function &find_queue($interface, $qname) {
1876
		if ($qname == $this->GetQname()) {
1877
			return $this;
1878
		}
1879

    
1880
		foreach ($this->subqueues as $q) {
1881
			$result =& $q->find_queue("", $qname);
1882
			if ($result) {
1883
				return $result;
1884
			}
1885
		}
1886
	}
1887

    
1888
	function &find_parentqueue($interface, $qname) {
1889
		if ($this->subqueues[$qname]) {
1890
			return $this;
1891
		}
1892
		foreach ($this->subqueues as $q) {
1893
			$result = $q->find_parentqueue("", $qname);
1894
			if ($result) {
1895
				return $result;
1896
			}
1897
		}
1898
	}
1899

    
1900
	function validate_input($data, &$input_errors) {
1901
		parent::validate_input($data, $input_errors);
1902

    
1903
		$reqdfields[] = "bandwidth";
1904
		$reqdfieldsn[] = gettext("Bandwidth");
1905
		$reqdfields[] = "bandwidthtype";
1906
		$reqdfieldsn[] = gettext("Bandwidthtype");
1907

    
1908
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
1909

    
1910
		if (isset($data['linkshare3']) && $data['linkshare3'] <> "") {
1911
			if ($data['bandwidth'] && (!is_numeric($data['bandwidth']))) {
1912
				$input_errors[] = gettext("Bandwidth must be an integer.");
1913
			}
1914

    
1915
			if ($data['bandwidth'] < 0) {
1916
				$input_errors[] = gettext("Bandwidth cannot be negative.");
1917
			}
1918

    
1919
			if ($data['bandwidthtype'] == "%") {
1920
				if ($data['bandwidth'] > 100 || $data['bandwidth'] < 0) {
1921
					$input_errors[] = gettext("Bandwidth in percentage should be between 1 and 100.");
1922
				}
1923
			}
1924
		}
1925

    
1926
		if ($data['upperlimit1'] <> "" && $data['upperlimit2'] == "") {
1927
			$input_errors[] = gettext("upperlimit service curve defined but missing (d) value");
1928
		}
1929
		if ($data['upperlimit2'] <> "" && $data['upperlimit1'] == "") {
1930
			$input_errors[] = gettext("upperlimit service curve defined but missing initial bandwidth (m1) value");
1931
		}
1932
		if ($data['upperlimit1'] <> "" && !is_valid_shaperbw($data['upperlimit1'])) {
1933
			$input_errors[] = gettext("upperlimit m1 value needs to be Kb, Mb, Gb, or %");
1934
		}
1935
		if ($data['upperlimit2'] <> "" && !is_numeric($data['upperlimit2'])) {
1936
			$input_errors[] = gettext("upperlimit d value needs to be numeric");
1937
		}
1938
		if ($data['upperlimit3'] <> "" && !is_valid_shaperbw($data['upperlimit3'])) {
1939
			$input_errors[] = gettext("upperlimit m2 value needs to be Kb, Mb, Gb, or %");
1940
		}
1941

    
1942
		/*
1943
		if (isset($data['upperlimit']) && $data['upperlimit3'] <> "" && $data['upperlimit1'] <> "") {
1944
			$bw_1 = get_hfsc_bandwidth($this, $data['upperlimit1']);
1945
			$bw_2 = get_hfsc_bandwidth($this, $data['upperlimit3']);
1946
			if (floatval($bw_1) < floatval($bw_2)) {
1947
				$input_errors[] = ("upperlimit m1 cannot be smaller than m2");
1948
			}
1949

    
1950
			if (get_interface_bandwidth($this) < (0.8 * (floatval($bw_1) + floatval($bw_2)))) {
1951
				$input_errors[] = ("upperlimit specification exceeds 80% of allowable allocation.");
1952
			}
1953
		}
1954
		*/
1955
		if ($data['linkshare1'] <> "" && $data['linkshare2'] == "") {
1956
			$input_errors[] = gettext("linkshare service curve defined but missing (d) value");
1957
		}
1958
		if ($data['linkshare2'] <> "" && $data['linkshare1'] == "") {
1959
			$input_errors[] = gettext("linkshare service curve defined but missing initial bandwidth (m1) value");
1960
		}
1961
		if ($data['linkshare1'] <> "" && !is_valid_shaperbw($data['linkshare1'])) {
1962
			$input_errors[] = gettext("linkshare m1 value needs to be Kb, Mb, Gb, or %");
1963
		}
1964
		if ($data['linkshare2'] <> "" && !is_numeric($data['linkshare2'])) {
1965
			$input_errors[] = gettext("linkshare d value needs to be numeric");
1966
		}
1967
		if ($data['linkshare3'] <> "" && !is_valid_shaperbw($data['linkshare3'])) {
1968
			$input_errors[] = gettext("linkshare m2 value needs to be Kb, Mb, Gb, or %");
1969
		}
1970
		if ($data['realtime1'] <> "" && $data['realtime2'] == "") {
1971
			$input_errors[] = gettext("realtime service curve defined but missing (d) value");
1972
		}
1973
		if ($data['realtime2'] <> "" && $data['realtime1'] == "") {
1974
			$input_errors[] = gettext("realtime service curve defined but missing initial bandwidth (m1) value");
1975
		}
1976

    
1977
		/*
1978
		if (isset($data['linkshare']) && $data['linkshare3'] <> "" && $data['linkshare1'] <> "" && 0) {
1979
			$bw_1 = get_hfsc_bandwidth($this, $data['linkshare1']);
1980
			$bw_2 = get_hfsc_bandwidth($this, $data['linkshare3']);
1981
			if (floatval($bw_1) < floatval($bw_2)) {
1982
				$input_errors[] = ("linkshare m1 cannot be smaller than m2");
1983
			}
1984

    
1985
			if (get_interface_bandwidth($this) < (0.8 * (floatval($bw_1) + floatval($bw_2)))) {
1986
				$input_errors[] = ("linkshare specification exceeds 80% of allowable allocation.");
1987
			}
1988
		}
1989
		*/
1990

    
1991
		if ($data['realtime1'] <> "" && !is_valid_shaperbw($data['realtime1'])) {
1992
			$input_errors[] = gettext("realtime m1 value needs to be Kb, Mb, Gb, or %");
1993
		}
1994
		if ($data['realtime2'] <> "" && !is_numeric($data['realtime2'])) {
1995
			$input_errors[] = gettext("realtime d value needs to be numeric");
1996
		}
1997
		if ($data['realtime3'] <> "" && !is_valid_shaperbw($data['realtime3'])) {
1998
			$input_errors[] = gettext("realtime m2 value needs to be Kb, Mb, Gb, or %");
1999
		}
2000

    
2001
		/*
2002
		if (isset($data['realtime']) && $data['realtime3'] <> "" && $data['realtime1'] <> "" && 0) {
2003
			$bw_1 = get_hfsc_bandwidth($this, $data['realtime1']);
2004
			$bw_2 = get_hfsc_bandwidth($this, $data['realtime3']);
2005
			if (floatval($bw_1) < floatval($bw_2)) {
2006
				$input_errors[] = ("realtime m1 cannot be smaller than m2");
2007
			}
2008

    
2009
			if (get_interface_bandwidth($this) < (0.8 * (floatval($bw_1) + floatval($bw_2)))) {
2010
				$input_errors[] = ("realtime specification exceeds 80% of allowable allocation.");
2011
			}
2012
		}
2013
		*/
2014
	}
2015

    
2016
	function ReadConfig(&$cflink) {
2017
		if (!empty($cflink['linkshare'])) {
2018
			if (!empty($cflink['linkshare1'])) {
2019
				$this->SetL_m1($cflink['linkshare1']);
2020
				$this->SetL_d($cflink['linkshare2']);
2021
				$this->SetLinkshare();
2022
			} else {
2023
				$this->SetL_m1("");
2024
				$this->SetL_d("");
2025
				$this->DisableLinkshare();
2026
			}
2027
			if (!empty($cflink['linkshare3'])) {
2028
				$this->SetL_m2($cflink['linkshare3']);
2029
				$this->SetLinkshare();
2030
			}
2031
		} else {
2032
			$this->DisableLinkshare();
2033
		}
2034
		if (!empty($cflink['realtime'])) {
2035
			if (!empty($cflink['realtime1'])) {
2036
				$this->SetR_m1($cflink['realtime1']);
2037
				$this->SetR_d($cflink['realtime2']);
2038
				$this->SetRealtime();
2039
			} else {
2040
				$this->SetR_m1("");
2041
				$this->SetR_d("");
2042
				$this->DisableRealtime();
2043
			}
2044
			if (!empty($cflink['realtime3'])) {
2045
				$this->SetR_m2($cflink['realtime3']);
2046
				$this->SetRealtime();
2047
			}
2048
		} else {
2049
			$this->DisableRealtime();
2050
		}
2051
		if (!empty($cflink['upperlimit'])) {
2052
			if (!empty($cflink['upperlimit1'])) {
2053
				$this->SetU_m1($cflink['upperlimit1']);
2054
				$this->SetU_d($cflink['upperlimit2']);
2055
				$this->SetUpperlimit();
2056
			} else {
2057
				$this->SetU_m1("");
2058
				$this->SetU_d("");
2059
				$this->DisableUpperlimit();
2060
			}
2061
			if (!empty($cflink['upperlimit3'])) {
2062
				$this->SetU_m2($cflink['upperlimit3']);
2063
				$this->SetUpperlimit();
2064
			}
2065
		} else {
2066
			$this->DisableUpperlimit();
2067
		}
2068
		parent::ReadConfig($cflink);
2069
	}
2070

    
2071
	function build_tree() {
2072
		$tree = " <li><a href=\"firewall_shaper.php?interface=" . $this->GetInterface() ."&amp;queue=" . $this->GetQname()."&amp;action=show";
2073
		$tree .= "\" ";
2074
		$tmpvalue = $this->GetDefault();
2075
		if (!empty($tmpvalue)) {
2076
			$tree .= " class=\"navlnk\"";
2077
		}
2078
		$tree .= " >" . $this->GetQname() . "</a>";
2079
		if (is_array($this->subqueues)) {
2080
			$tree .= "<ul>";
2081
			foreach ($this->subqueues as $q) {
2082
				$tree .= $q->build_tree();
2083
			}
2084
			$tree .= "</ul>";
2085
		}
2086
		$tree .= "</li>";
2087
		return $tree;
2088
	}
2089

    
2090
	/* Even this should take children into consideration */
2091
	function build_rules(&$default = false) {
2092

    
2093
		$pfq_rule = " queue ". $this->qname;
2094
		if ($this->GetInterface()) {
2095
			$pfq_rule .= " on ".get_real_interface($this->GetInterface());
2096
		}
2097
		if ($this->GetBandwidth() && $this->GetBwscale()) {
2098
			$pfq_rule .= " bandwidth ".trim($this->GetBandwidth()).$this->GetBwscale();
2099
		}
2100

    
2101
		$tmpvalue = $this->GetQlimit();
2102
		if (!empty($tmpvalue)) {
2103
			$pfq_rule .= " qlimit " . $this->GetQlimit();
2104
		}
2105
		if ($this->GetDefault() || $this->GetRed() || $this->GetRio() || $this->GetEcn() || $this->GetCodel() || $this->GetRealtime() <> "" || $this->GetLinkshare() <> "" || $this->GetUpperlimit() <> "") {
2106
			$pfq_rule .= " hfsc ( ";
2107
			$tmpvalue = $this->GetRed();
2108
			if (!empty($tmpvalue)) {
2109
				$comma = 1;
2110
				$pfq_rule .= " red ";
2111
			}
2112

    
2113
			$tmpvalue = $this->GetRio();
2114
			if (!empty($tmpvalue)) {
2115
				if ($comma) {
2116
					$pfq_rule .= " ,";
2117
				}
2118
				$comma = 1;
2119
				$pfq_rule .= " rio ";
2120
			}
2121
			$tmpvalue = $this->GetEcn();
2122
			if (!empty($tmpvalue)) {
2123
				if ($comma) {
2124
					$pfq_rule .= " ,";
2125
				}
2126
				$comma = 1;
2127
				$pfq_rule .= " ecn ";
2128
			}
2129
			$tmpvalue = $this->GetCodel();
2130
			if (!empty($tmpvalue)) {
2131
				if ($comma) {
2132
					$pfq_rule .= " ,";
2133
				}
2134
				$comma = 1;
2135
				$pfq_rule .= " codel ";
2136
			}
2137
			$tmpvalue = $this->GetDefault();
2138
			if (!empty($tmpvalue)) {
2139
				if ($comma) {
2140
					$pfq_rule .= " ,";
2141
				}
2142
				$comma = 1;
2143
				$pfq_rule .= " default ";
2144
				$default = true;
2145
			}
2146

    
2147
			if ($this->GetRealtime() <> "") {
2148
				if ($comma) {
2149
					$pfq_rule .= " , ";
2150
				}
2151
				if ($this->GetR_m1() <> "" && $this->GetR_d() <> "" && $this->GetR_m2() <> "") {
2152
					$pfq_rule .= " realtime (".$this->GetR_m1() . ", " . $this->GetR_d().", ". $this->GetR_m2() .") ";
2153
				} else if ($this->GetR_m2() <> "") {
2154
					$pfq_rule .= " realtime " . $this->GetR_m2();
2155
				}
2156
				$comma = 1;
2157
			}
2158
			if ($this->GetLinkshare() <> "") {
2159
				if ($comma) {
2160
					$pfq_rule .= " ,";
2161
				}
2162
				if ($this->GetL_m1() <> "" && $this->GetL_d() <> "" && $this->GetL_m2() <> "") {
2163
					$pfq_rule .= " linkshare (".$this->GetL_m1(). ", ". $this->GetL_d(). ", ". $this->GetL_m2(). ") ";
2164
				} else if ($this->GetL_m2() <> "") {
2165
					$pfq_rule .= " linkshare " . $this->GetL_m2() . " ";
2166
				}
2167
				$comma = 1;
2168
			}
2169
			if ($this->GetUpperlimit() <> "") {
2170
				if ($comma) {
2171
					$pfq_rule .= " ,";
2172
				}
2173
				if ($this->GetU_m1() <> "" && $this->GetU_d() <> "" && $this->GetU_m2() <> "") {
2174
							$pfq_rule .= " upperlimit (".$this->GetU_m1().", ". $this->GetU_d().", ". $this->GetU_m2(). ") ";
2175
				} else if ($this->GetU_m2() <> "") {
2176
					$pfq_rule .= " upperlimit " . $this->GetU_m2() . " ";
2177
				}
2178
			}
2179
			$pfq_rule .= " ) ";
2180
		}
2181
		if (count($this->subqueues)) {
2182
			$i = count($this->subqueues);
2183
			$pfq_rule .= " { ";
2184
			foreach ($this->subqueues as $qkey => $qnone) {
2185
				if ($i > 1) {
2186
					$i--;
2187
					$pfq_rule .= " {$qkey}, ";
2188
				} else {
2189
					$pfq_rule .= " {$qkey} ";
2190
				}
2191
			}
2192
			$pfq_rule .= " } \n";
2193
			foreach ($this->subqueues as $q) {
2194
				$pfq_rule .= $q->build_rules($default);
2195
			}
2196
		}
2197

    
2198
		$pfq_rule .= " \n";
2199

    
2200
		return $pfq_rule;
2201
	}
2202

    
2203
	function build_javascript() {
2204

    
2205
		$javascript = <<<EOJS
2206
<script type="text/javascript">
2207
//<![CDATA[
2208
	events.push(function(){
2209

    
2210
		// Disables the specified input element
2211
		function disableInput(id, disable) {
2212
			$('#' + id).prop("disabled", disable);
2213
		}
2214

    
2215
		// Upperlimit
2216
		function enable_upperlimit() {
2217
			disableInput('upperlimit1', !$('#upperlimit').prop('checked'));
2218
			disableInput('upperlimit2', !$('#upperlimit').prop('checked'));
2219
			disableInput('upperlimit3', !$('#upperlimit').prop('checked'));
2220
		}
2221

    
2222
		$('#upperlimit').click(function () {
2223
			enable_upperlimit();
2224
		});
2225

    
2226
		enable_upperlimit();
2227

    
2228
		// realtime
2229
		function enable_realtime() {
2230
			disableInput('realtime1', !$('#realtime').prop('checked'));
2231
			disableInput('realtime2', !$('#realtime').prop('checked'));
2232
			disableInput('realtime3', !$('#realtime').prop('checked'));
2233
		}
2234

    
2235
		$('#realtime').click(function () {
2236
			enable_realtime();
2237
		});
2238

    
2239
		enable_realtime();
2240

    
2241
		// linkshare
2242
		function enable_linkshare() {
2243
			disableInput('linkshare1', !$('#linkshare').prop('checked'));
2244
			disableInput('linkshare2', !$('#linkshare').prop('checked'));
2245
			disableInput('linkshare3', !$('#linkshare').prop('checked'));
2246
		}
2247

    
2248
		$('#linkshare').click(function () {
2249
			enable_linkshare();
2250
		});
2251

    
2252
		enable_linkshare();
2253
	});
2254
//]]>
2255
</script>
2256
EOJS;
2257

    
2258
		return $javascript;
2259
	}
2260

    
2261
	function build_form() {
2262

    
2263
		$sform = parent::build_form();
2264

    
2265
		$section = new Form_Section('Service Curve (sc)');
2266

    
2267
		$group = new Form_Group('Bandwidth');
2268

    
2269
		$group->add(new Form_Input(
2270
			'bandwidth',
2271
			null,
2272
			'number',
2273
			$this->GetBandwidth(),
2274
			['step' => 'any', 'min' => '0.000']
2275
		));
2276

    
2277
		$group->add(new Form_Select(
2278
			'bandwidthtype',
2279
			null,
2280
			$this->GetBwscale(),
2281
			array('Kb' => 'Kbit/s',
2282
				  'Mb' => 'Mbit/s',
2283
				  'Gb' => 'Gbit/s',
2284
				  'b' => 'Bit/s',
2285
				  '%' => '%')
2286
		));
2287

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

    
2290
		$section->add($group);
2291

    
2292
		$group = new Form_Group('Max bandwidth for queue.');
2293

    
2294
		$group->add(new Form_Checkbox(
2295
			'upperlimit',
2296
			null,
2297
			'Upper Limit',
2298
			($this->GetUpperlimit()<> "")
2299
		));
2300

    
2301
		$group->add(new Form_Input(
2302
			'upperlimit1',
2303
			null,
2304
			'text',
2305
			$this->GetU_m1()
2306
		))->setHelp('m1');
2307

    
2308
		$group->add(new Form_Input(
2309
			'upperlimit2',
2310
			null,
2311
			'text',
2312
			$this->GetU_d()
2313
		))->setHelp('d');
2314

    
2315
		$group->add(new Form_Input(
2316
			'upperlimit3',
2317
			null,
2318
			'text',
2319
			$this->GetU_m2()
2320
		))->setHelp('m2');
2321

    
2322

    
2323
		$section->add($group);
2324

    
2325
		$group = new Form_Group('Min bandwidth for queue.');
2326

    
2327
		$group->add(new Form_Checkbox(
2328
			'realtime',
2329
			null,
2330
			'Real Time',
2331
			($this->GetRealtime()<> "")
2332
		));
2333

    
2334
		$group->add(new Form_Input(
2335
			'realtime1',
2336
			null,
2337
			'text',
2338
			$this->GetR_m1()
2339
		))->setHelp('m1');
2340

    
2341
		$group->add(new Form_Input(
2342
			'realtime2',
2343
			null,
2344
			'text',
2345
			$this->GetR_d()
2346
		))->setHelp('d');
2347

    
2348
		$group->add(new Form_Input(
2349
			'realtime3',
2350
			null,
2351
			'text',
2352
			$this->GetR_m2()
2353
		))->setHelp('m2');
2354

    
2355
		$section->add($group);
2356

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

    
2359
		$group->add(new Form_Checkbox(
2360
			'linkshare',
2361
			null,
2362
			'Link Share',
2363
			($this->GetLinkshare()<> "")
2364
		));
2365

    
2366
		$group->add(new Form_Input(
2367
			'linkshare1',
2368
			null,
2369
			'text',
2370
			$this->GetL_m1()
2371
		))->setHelp('m1');
2372

    
2373
		$group->add(new Form_Input(
2374
			'linkshare2',
2375
			null,
2376
			'text',
2377
			$this->GetL_d()
2378
		))->setHelp('d');
2379

    
2380
		$group->add(new Form_Input(
2381
			'linkshare3',
2382
			null,
2383
			'text',
2384
			$this->GetL_m2()
2385
		))->setHelp('m2');
2386

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

    
2393
		$section->add($group);
2394

    
2395
		$sform->add($section);
2396

    
2397
		return($sform);
2398
	}
2399

    
2400
	function update_altq_queue_data(&$data) {
2401
		$this->ReadConfig($data);
2402
	}
2403

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

    
2521
class cbq_queue extends priq_queue {
2522
	var $qborrow = "";
2523

    
2524
	function GetBorrow() {
2525
		return $this->qborrow;
2526
	}
2527
	function SetBorrow($borrow) {
2528
		$this->qborrow = $borrow;
2529
	}
2530
	function CanHaveChildren() {
2531
		return true;
2532
	}
2533

    
2534
	function &add_queue($interface, &$qname, &$path, &$input_errors) {
2535

    
2536
		if (!is_array($this->subqueues)) {
2537
			$this->subqueues = array();
2538
		}
2539
		$q =& new cbq_queue();
2540
		$q->SetInterface($this->GetInterface());
2541
		$q->SetParent($this);
2542
		$q->ReadConfig($qname);
2543
		$q->validate_input($qname, $input_errors);
2544

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

    
2557
		return $q;
2558
	}
2559

    
2560
	function copy_queue($interface, &$cflink) {
2561

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

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

    
2626
	function &find_parentqueue($interface, $qname) {
2627
		if ($this->subqueues[$qname]) {
2628
			return $this;
2629
		}
2630
		foreach ($this->subqueues as $q) {
2631
			$result = $q->find_parentqueue("", $qname);
2632
			if ($result) {
2633
				return $result;
2634
			}
2635
		}
2636
	}
2637

    
2638
	function delete_queue() {
2639
		unref_on_altq_queue_list($this->GetQname());
2640
		cleanup_queue_from_rules($this->GetQname());
2641
		foreach ($this->subqueues as $q)
2642
			$q->delete_queue();
2643
		unset_object_by_reference($this->GetLink());
2644
	}
2645

    
2646
	function validate_input($data, &$input_errors) {
2647
		parent::validate_input($data, $input_errors);
2648

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

    
2657
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
2658
	}
2659

    
2660
	function ReadConfig(&$q) {
2661
		parent::ReadConfig($q);
2662
		if (!empty($q['borrow'])) {
2663
			$this->SetBorrow("on");
2664
		} else {
2665
			$this->SetBorrow("");
2666
		}
2667
	}
2668

    
2669
	function build_javascript() {
2670
		return parent::build_javascript();
2671
	}
2672

    
2673
	function build_tree() {
2674
		$tree = " <li><a href=\"firewall_shaper.php?interface=" . $this->GetInterface()."&amp;queue=" . $this->GetQname()."&amp;action=show";
2675
		$tree .= "\" ";
2676
		$tmpvalue = trim($this->GetDefault());
2677
		if (!empty($tmpvalue)) {
2678
			$tree .= " class=\"navlnk\"";
2679
		}
2680
		$tree .= " >" . $this->GetQname() . "</a>";
2681
		if (is_array($this->subqueues)) {
2682
			$tree .= "<ul>";
2683
			foreach ($this->subqueues as $q) {
2684
				$tree .= $q->build_tree();
2685
			}
2686
			$tree .= "</ul>";
2687
		}
2688
		$tree .= "</li>";
2689
		return $tree;
2690
	}
2691

    
2692
	/* Even this should take children into consideration */
2693
	function build_rules(&$default = false) {
2694
		$pfq_rule = "queue ". $this->qname;
2695
		if ($this->GetInterface()) {
2696
			$pfq_rule .= " on ".get_real_interface($this->GetInterface());
2697
		}
2698
		if ($this->GetBandwidth() && $this->GetBwscale()) {
2699
			$pfq_rule .= " bandwidth ".trim($this->GetBandwidth()).$this->GetBwscale();
2700
		}
2701
		$tmpvalue = $this->GetQpriority();
2702
		if (is_numeric($tmpvalue)) {
2703
			$pfq_rule .= " priority " . $this->GetQpriority();
2704
		}
2705
		$tmpvalue = trim($this->GetQlimit());
2706
		if (!empty($tmpvalue)) {
2707
			$pfq_rule .= " qlimit " . $this->GetQlimit();
2708
		}
2709
		if ($this->GetDefault() || $this->GetRed() || $this->GetRio() || $this->GetEcn() || $this->GetBorrow() || $this->GetCodel()) {
2710
			$pfq_rule .= " cbq ( ";
2711
			$tmpvalue = trim($this->GetRed());
2712
			if (!empty($tmpvalue)) {
2713
				$comma = 1;
2714
				$pfq_rule .= " red ";
2715
			}
2716
			$tmpvalue = trim($this->GetCodel());
2717
			if (!empty($tmpvalue)) {
2718
				$comma = 1;
2719
				$pfq_rule .= " codel ";
2720
			}
2721
			$tmpvalue = trim($this->GetRio());
2722
			if (!empty($tmpvalue)) {
2723
				if ($comma) {
2724
					$pfq_rule .= " ,";
2725
				}
2726
				$comma = 1;
2727
				$pfq_rule .= " rio ";
2728
			}
2729
			$tmpvalue = trim($this->GetEcn());
2730
			if (!empty($tmpvalue)) {
2731
				if ($comma) {
2732
					$pfq_rule .= " ,";
2733
				}
2734
				$comma = 1;
2735
				$pfq_rule .= " ecn ";
2736
			}
2737
			$tmpvalue = trim($this->GetDefault());
2738
			if (!empty($tmpvalue)) {
2739
				if ($comma) {
2740
					$pfq_rule .= " ,";
2741
				}
2742
				$comma = 1;
2743
				$pfq_rule .= " default ";
2744
				$default = true;
2745
			}
2746
			$tmpvalue = trim($this->GetBorrow());
2747
			if (!empty($tmpvalue)) {
2748
				if ($comma) {
2749
					$pfq_rule .= ", ";
2750
				}
2751
				$pfq_rule .= " borrow ";
2752
			}
2753
			$pfq_rule .= " ) ";
2754
		}
2755
		if (count($this->subqueues)) {
2756
			$i = count($this->subqueues);
2757
			$pfq_rule .= " { ";
2758
			foreach ($this->subqueues as $qkey => $qnone) {
2759
				if ($i > 1) {
2760
					$i--;
2761
					$pfq_rule .= " {$qkey}, ";
2762
				} else {
2763
					$pfq_rule .= " {$qkey} ";
2764
				}
2765
			}
2766
			$pfq_rule .= " } \n";
2767
			foreach ($this->subqueues as $q) {
2768
				$pfq_rule .= $q->build_rules($default);
2769
			}
2770
		}
2771

    
2772
		$pfq_rule .= " \n";
2773
		return $pfq_rule;
2774
	}
2775

    
2776
	function build_form() {
2777
		$sform = parent::build_form();
2778

    
2779
		$section = new Form_Section('NOTITLE');
2780

    
2781
		$group = new Form_Group('Bandwidth');
2782

    
2783
		$group->add(new Form_Input(
2784
			'bandwidth',
2785
			null,
2786
			'number',
2787
			$this->GetBandwidth()
2788
		));
2789

    
2790
		$group->add(new Form_Select(
2791
			'bandwidthtype',
2792
			null,
2793
			$this->GetBwscale(),
2794
			array('Kb' => 'Kbit/s',
2795
				  'Mb' => 'Mbit/s',
2796
				  'Gb' => 'Gbit/s',
2797
				  'b' => 'Bit/s',
2798
				  '%' => '%')
2799
		));
2800

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

    
2803
		$section->add($group);
2804

    
2805
		$section->addInput(new Form_Checkbox(
2806
			'borrow',
2807
			'Scheduler option',
2808
			'Borrow from other queues when available',
2809
			($this->GetBorrow() == "on")
2810
		));
2811

    
2812
		$sform->add($section);
2813

    
2814
		return $sform;
2815
	}
2816

    
2817
	function update_altq_queue_data(&$data) {
2818
		$this->ReadConfig($data);
2819
	}
2820

    
2821
	function wconfig() {
2822
		$cflink =& get_reference_to_me_in_config($this->GetLink());
2823
		if (!is_array($cflink)) {
2824
			$cflink = array();
2825
		}
2826
		$cflink['interface'] = $this->GetInterface();
2827
		$cflink['qlimit'] = trim($this->GetQlimit());
2828
		if (empty($cflink['qlimit'])) {
2829
			unset($cflink['qlimit']);
2830
		}
2831
		$cflink['priority'] = $this->GetQpriority();
2832
		if (!is_numeric($cflink['priority'])) {
2833
			unset($cflink['priority']);
2834
		}
2835
		$cflink['name'] = $this->GetQname();
2836
		$cflink['description'] = $this->GetDescription();
2837
		if (empty($cflink['description'])) {
2838
			unset($cflink['description']);
2839
		}
2840
		$cflink['bandwidth'] = $this->GetBandwidth();
2841
		$cflink['bandwidthtype'] = $this->GetBwscale();
2842
		$cflink['enabled'] = trim($this->GetEnabled());
2843
		if (empty($cflink['enabled'])) {
2844
			unset($cflink['enabled']);
2845
		}
2846
		$cflink['default'] = trim($this->GetDefault());
2847
		if (empty($cflink['default'])) {
2848
			unset($cflink['default']);
2849
		}
2850
		$cflink['red'] = trim($this->GetRed());
2851
		if (empty($cflink['red'])) {
2852
			unset($cflink['red']);
2853
		}
2854
		$cflink['rio'] = trim($this->GetRio());
2855
		if (empty($cflink['rio'])) {
2856
			unset($cflink['rio']);
2857
		}
2858
		$cflink['ecn'] = trim($this->GetEcn());
2859
		if (empty($cflink['ecn'])) {
2860
			unset($cflink['ecn']);
2861
		}
2862
		$cflink['codel'] = trim($this->GetCodel());
2863
		if (empty($cflink['codel'])) {
2864
			unset($cflink['codel']);
2865
		}
2866
		$cflink['borrow'] = trim($this->GetBorrow());
2867
		if (empty($cflink['borrow'])) {
2868
			unset($cflink['borrow']);
2869
		}
2870
	}
2871
}
2872

    
2873
class fairq_queue extends priq_queue {
2874
	var $hogs;
2875
	var $buckets;
2876

    
2877
	function GetBuckets() {
2878
		return $this->buckets;
2879
	}
2880
	function SetBuckets($buckets) {
2881
		$this->buckets = $buckets;
2882
	}
2883
	function GetHogs() {
2884
		return $this->hogs;
2885
	}
2886
	function SetHogs($hogs) {
2887
		$this->hogs = $hogs;
2888
	}
2889
	function CanHaveChildren() {
2890
		return false;
2891
	}
2892

    
2893

    
2894
	function copy_queue($interface, &$cflink) {
2895
		$cflink['interface'] = $interface;
2896
		$cflink['qlimit'] = $this->GetQlimit();
2897
		$cflink['priority'] = $this->GetQpriority();
2898
		$cflink['name'] = $this->GetQname();
2899
		$cflink['description'] = $this->GetDescription();
2900
		$cflink['bandwidth'] = $this->GetBandwidth();
2901
		$cflink['bandwidthtype'] = $this->GetBwscale();
2902
		$cflink['enabled'] = $this->GetEnabled();
2903
		$cflink['default'] = $this->GetDefault();
2904
		$cflink['red'] = $this->GetRed();
2905
		$cflink['rio'] = $this->GetRio();
2906
		$cflink['ecn'] = $this->GetEcn();
2907
		$cflink['buckets'] = $this->GetBuckets();
2908
		$cflink['hogs'] = $this->GetHogs();
2909
	}
2910

    
2911
	/*
2912
	 * Should search even its children
2913
	 */
2914
	function &find_queue($interface, $qname) {
2915
		if ($qname == $this->GetQname()) {
2916
			return $this;
2917
		}
2918
	}
2919

    
2920
	function find_parentqueue($interface, $qname) { return; }
2921

    
2922
	function delete_queue() {
2923
		unref_on_altq_queue_list($this->GetQname());
2924
		cleanup_queue_from_rules($this->GetQname());
2925
		unset_object_by_reference($this->GetLink());
2926
	}
2927

    
2928
	function validate_input($data, &$input_errors) {
2929
		parent::validate_input($data, $input_errors);
2930

    
2931
		if ($data['priority'] > 7) {
2932
				$input_errors[] = gettext("Priority must be an integer between 0 and 7.");
2933
		}
2934
		$reqdfields[] = "bandwidth";
2935
		$reqdfieldsn[] = gettext("Bandwidth");
2936
		$reqdfields[] = "bandwidthtype";
2937
		$reqdfieldsn[] = gettext("Bandwidthtype");
2938

    
2939
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
2940
	}
2941

    
2942
	function ReadConfig(&$q) {
2943
		parent::ReadConfig($q);
2944
		if (!empty($q['buckets'])) {
2945
			$this->SetBuckets($q['buckets']);
2946
		} else {
2947
			$this->SetBuckets("");
2948
		}
2949
		if (!empty($q['hogs']) && is_valid_shaperbw($q['hogs'])) {
2950
			$this->SetHogs($q['hogs']);
2951
		} else {
2952
			$this->SetHogs("");
2953
		}
2954
	}
2955

    
2956
	function build_javascript() {
2957
		return parent::build_javascript();
2958
	}
2959

    
2960
	function build_tree() {
2961
		$tree = " <li><a href=\"firewall_shaper.php?interface=" .
2962
		$this->GetInterface()."&amp;queue=" . $this->GetQname()."&amp;action=show";
2963
		$tree .= "\" ";
2964
		$tmpvalue = trim($this->GetDefault());
2965
		if (!empty($tmpvalue)) {
2966
			$tree .= " class=\"navlnk\"";
2967
		}
2968
		$tree .= " >" . $this->GetQname() . "</a>";
2969
		$tree .= "</li>";
2970
		return $tree;
2971
	}
2972

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

    
3045
		$pfq_rule .= " \n";
3046
		return $pfq_rule;
3047
	}
3048

    
3049
	function build_form() {
3050
		$form = parent::build_form();
3051

    
3052
		$section = new Form_Section('');
3053

    
3054
		$group = new Form_Group('Bandwidth');
3055

    
3056
		$group->add(new Form_Input(
3057
			'bandwidth',
3058
			null,
3059
			'number',
3060
			$this->GetBandwidth()
3061
		));
3062

    
3063
		$group->add(new Form_Select(
3064
			'bandwidthtype',
3065
			null,
3066
			$this->GetBwscale(),
3067
			array('Kb' => 'Kbit/s',
3068
				  'Mb' => 'Mbit/s',
3069
				  'Gb' => 'Gbit/s',
3070
				  'b' => 'Bit/s',
3071
				  '%' => '%')
3072
		));
3073

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

    
3076
		$section->add($group);
3077

    
3078
		$section->addInput(new Form_Input(
3079
			'buckets',
3080
			'Scheduler specific options',
3081
			'text',
3082
			$this->GetBuckets()
3083
		))->setHelp('Number of buckets available');
3084

    
3085
		$section->addInput(new Form_Input(
3086
			'hogs',
3087
			'',
3088
			'text',
3089
			$this->GetHogs()
3090
			))->setHelp('Bandwidth limit for hosts to not saturate link');
3091

    
3092
		$form->add($section);
3093
		return $form;
3094
	}
3095

    
3096
	function update_altq_queue_data(&$data) {
3097
		$this->ReadConfig($data);
3098
	}
3099

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

    
3156

    
3157
/*
3158
 * dummynet(4) wrappers.
3159
 */
3160

    
3161

    
3162
/*
3163
 * List of respective objects!
3164
 */
3165
$dummynet_pipe_list = array();
3166

    
3167
class dummynet_class {
3168
	var $qname;
3169
	var $qnumber; /* dummynet(4) uses numbers instead of names; maybe integrate with pf the same as altq does?! */
3170
	var $qlimit;
3171
	var $description;
3172
	var $qenabled;
3173
	var $link;
3174
	var $qparent; /* link to upper class so we do things easily on WF2Q+ rule creation */
3175
	var $plr;
3176

    
3177
	var $buckets;
3178
	/* mask parameters */
3179
	var $mask;
3180
	var $noerror;
3181

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

    
3259
	function build_javascript() {
3260
		$javascript .= "<script type=\"text/javascript\">\n";
3261
		$javascript .= "//<![CDATA[\n";
3262
		$javascript .= "function enable_maskbits(enable_over) {\n";
3263
		$javascript .= "var e = document.getElementById(\"mask\");\n";
3264
		$javascript .= "if ((e.options[e.selectedIndex].text == \"none\") || enable_over) {\n";
3265
		$javascript .= "document.iform.maskbits.disabled = 1;\n";
3266
		$javascript .= "document.iform.maskbits.value = \"\";\n";
3267
		$javascript .= "document.iform.maskbitsv6.disabled = 1;\n";
3268
		$javascript .= "document.iform.maskbitsv6.value = \"\";\n";
3269
		$javascript .= "} else {\n";
3270
		$javascript .= "document.iform.maskbits.disabled = 0;\n";
3271
		$javascript .= "document.iform.maskbitsv6.disabled = 0;\n";
3272
		$javascript .= "}}\n";
3273
		$javascript .= "//]]>\n";
3274
		$javascript .= "</script>\n";
3275
		return $javascript;
3276
	}
3277

    
3278
	function validate_input($data, &$input_errors) {
3279
		$reqdfields[] = "bandwidth";
3280
		$reqdfieldsn[] = gettext("Bandwidth");
3281
		/*$reqdfields[] = "burst";
3282
		$reqdfieldsn[] = gettext("Burst"); */
3283
		$reqdfields[] = "bandwidthtype";
3284
		$reqdfieldsn[] = gettext("Bandwidthtype");
3285
		$reqdfields[] = "newname";
3286
		$reqdfieldsn[] = gettext("Name");
3287

    
3288
		shaper_do_input_validation($data, $reqdfields, $reqdfieldsn, $input_errors);
3289

    
3290
		if ($data['plr'] && (!is_numeric($data['plr']) ||
3291
		    ($data['plr'] < 0) || ($data['plr'] > 1))) {
3292
			$input_errors[] = gettext("Packet Loss Rate must be a value between 0 and 1.");
3293
		}
3294
		if ($data['buckets'] && (!is_numeric($data['buckets']) ||
3295
		    ($data['buckets'] < 16) || ($data['buckets'] > 65535))) {
3296
			$input_errors[] = gettext("Buckets must be an integer between 16 and 65535.");
3297
		}
3298
		if ($data['qlimit'] && (!is_numeric($data['qlimit']))) {
3299
			$input_errors[] = gettext("Queue limit must be an integer");
3300
		}
3301
		if (!empty($data['newname']) && !preg_match("/^[a-zA-Z0-9_-]+$/", $data['newname'])) {
3302
			$input_errors[] = gettext("Queue names must be alphanumeric and _ or - only.");
3303
		}
3304
		if (!empty($data['name']) && !preg_match("/^[a-zA-Z0-9_-]+$/", $data['name'])) {
3305
			$input_errors[] = gettext("Queue names must be alphanumeric and _ or - only.");
3306
		}
3307
		if (isset($data['maskbits']) && ($data['maskbits'] <> "")) {
3308
			if ((!is_numeric($data['maskbits'])) || ($data['maskbits'] <= 0) || ($data['maskbits'] > 32)) {
3309
				$input_errors[] = gettext("IPv4 bit mask must be blank or numeric value between 1 and 32.");
3310
			}
3311
		}
3312
		if (isset($data['maskbitsv6']) && ($data['maskbitsv6'] <> "")) {
3313
			if ((!is_numeric($data['maskbitsv6'])) || ($data['maskbitsv6'] <= 0) || ($data['maskbitsv6'] > 128)) {
3314
				$input_errors[] = gettext("IPv6 bit mask must be blank or numeric value between 1 and 128.");
3315
			}
3316
		}
3317
	}
3318

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

    
3356
}
3357

    
3358
class dnpipe_class extends dummynet_class {
3359
	var $delay;
3360
	var $qbandwidth = array();
3361
	var $qbandwidthtype;
3362

    
3363
		/* This is here to help on form building and building rules/lists */
3364
	var $subqueues = array();
3365

    
3366
	function CanHaveChildren() {
3367
		return true;
3368
	}
3369
	function SetDelay($delay) {
3370
		$this->delay = $delay;
3371
	}
3372
	function GetDelay() {
3373
		return $this->delay;
3374
	}
3375
	function delete_queue() {
3376
		cleanup_dnqueue_from_rules($this->GetQname());
3377
		foreach ($this->subqueues as $q) {
3378
			$q->delete_queue();
3379
		}
3380
		unset_dn_object_by_reference($this->GetLink());
3381
		@pfSense_ipfw_pipe("pipe delete " . $this->GetNumber());
3382
	}
3383
	function GetBandwidth() {
3384
		return $this->qbandwidth;
3385
	}
3386
	function SetBandwidth($bandwidth) {
3387
		$this->qbandwidth = $bandwidth;
3388
	}
3389
	function GetBurst() {
3390
		return $this->qburst;
3391
	}
3392
	function SetBurst($burst) {
3393
		$this->qburst = $burst;
3394
	}
3395

    
3396
	function &add_queue($interface, &$queue, &$path, &$input_errors) {
3397

    
3398
		if (!is_array($this->subqueues)) {
3399
			$this->subqueues = array();
3400
		}
3401

    
3402
		$q =& new dnqueue_class();
3403
		$q->SetLink($path);
3404
		$q->SetEnabled("on");
3405
		$q->SetPipe($this->GetQname());
3406
		$q->SetParent($this);
3407
		$q->ReadConfig($queue);
3408
		$q->validate_input($queue, $input_errors);
3409
		if (count($input_errors)) {
3410
			log_error(sprintf(gettext('SHAPER: Could not create queue %1$s on interface %2$s because: %3$s'), $q->GetQname(), $interface, print_r($input_errors, true)));
3411
			return $q;
3412
		}
3413
		$number = dnqueue_find_nextnumber();
3414
		$q->SetNumber($number);
3415
		$this->subqueues[$q->GetQname()] = &$q;
3416

    
3417
		return $q;
3418
	}
3419

    
3420
	function &get_queue_list(&$q = null) {
3421
		$qlist = array();
3422

    
3423
		$qlist[$this->GetQname()] = $this->GetNumber();
3424
		if (is_array($this->subqueues)) {
3425
			foreach ($this->subqueues as $queue) {
3426
				$queue->get_queue_list($qlist);
3427
			}
3428
		}
3429
		return $qlist;
3430
	}
3431

    
3432
	/*
3433
	 * Should search even its children
3434
	 */
3435
	function &find_queue($pipe, $qname) {
3436
		if ($qname == $this->GetQname()) {
3437
			return $this;
3438
		}
3439
		foreach ($this->subqueues as $q) {
3440
			$result =& $q->find_queue("", $qname);
3441
			if ($result) {
3442
				return $result;
3443
			}
3444
		}
3445
	}
3446

    
3447
	function &find_parentqueue($pipe, $qname) {
3448
		return NULL;
3449
	}
3450

    
3451
	function validate_input($data, &$input_errors) {
3452
		parent::validate_input($data, $input_errors);
3453

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

    
3490
	function ReadConfig(&$q) {
3491
		if (!empty($q['name']) && !empty($q['newname']) && $q['name'] != $q['newname']) {
3492
			$this->SetQname($q['newname']);
3493
		} else if (!empty($q['newname'])) {
3494
			$this->SetQname($q['newname']);
3495
		} else {
3496
			$this->SetQname($q['name']);
3497
		}
3498
		$this->SetNumber($q['number']);
3499

    
3500
		if (!empty($_POST)) {
3501
			$bandwidth = array();
3502
			/* XXX: Really no better way? */
3503
			for ($i = 0; $i < 2900; $i++) {
3504
				if (isset($q["bandwidth{$i}"]) && $q["bandwidth{$i}"] <> "") {
3505
					$bw = array();
3506
					$bw['bw'] = $q["bandwidth{$i}"];
3507
					$bw['burst'] = $q["burst{$i}"];
3508
					if (isset($q["bwtype{$i}"]) && $q["bwtype{$i}"]) {
3509
						$bw['bwscale'] = $q["bwtype{$i}"];
3510
					}
3511
					if (isset($q["bwsched{$i}"]) && $q["bwsched{$i}"]) {
3512
						$bw['bwsched'] = $q["bwsched{$i}"];
3513
					}
3514
					$bandwidth[] = $bw;
3515
				}
3516
			}
3517
			$this->SetBandwidth($bandwidth);
3518
		}
3519

    
3520
		if (is_array($q['bandwidth']) && is_array($q['bandwidth']['item'])) {
3521
			$this->SetBandwidth($q['bandwidth']['item']);
3522
			$this->SetBurst($q['burst']['item']);
3523
		}
3524

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

    
3568
	}
3569

    
3570
	function build_tree() {
3571
		$tree = " <li><a href=\"firewall_shaper_vinterface.php?pipe=" . $this->GetQname() ."&amp;queue=".$this->GetQname() ."&amp;action=show\">";
3572
		$tree .= $this->GetQname() . "</a>";
3573
		if (is_array($this->subqueues)) {
3574
			$tree .= "<ul>";
3575
			foreach ($this->subqueues as $q) {
3576
				$tree .= $q->build_tree();
3577
			}
3578
			$tree .= "</ul>";
3579
		}
3580
		$tree .= "</li>";
3581

    
3582
		return $tree;
3583
	}
3584

    
3585
	function build_rules() {
3586
		global $config, $time_based_rules;
3587

    
3588
		if ($this->GetEnabled() == "") {
3589
			return;
3590
		}
3591

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

    
3633
		if ($this->GetQlimit()) {
3634
			$pfq_rule .= " queue " . $this->GetQlimit();
3635
		}
3636
		if ($this->GetPlr()) {
3637
			$pfq_rule .= " plr " . $this->GetPlr();
3638
		}
3639
		if ($this->GetBuckets()) {
3640
			$pfq_rule .= " buckets " . $this->GetBuckets();
3641
		}
3642
		if ($this->GetDelay()) {
3643
			$pfq_rule .= " delay " . $this->GetDelay();
3644
		}
3645
		$this->build_mask_rules($pfq_rule);
3646

    
3647
		$pfq_rule .= "\n";
3648

    
3649
		if (!empty($this->subqueues) && count($this->subqueues) > 0) {
3650
			foreach ($this->subqueues as $q) {
3651
				$pfq_rule .= $q->build_rules();
3652
			}
3653
		}
3654
		$pfq_rule .= " \n";
3655

    
3656
		return $pfq_rule;
3657
	}
3658

    
3659
	function update_dn_data(&$data) {
3660
		$this->ReadConfig($data);
3661
	}
3662

    
3663
	function build_javascript() {
3664
		global $g, $config;
3665

    
3666
		$javasr = parent::build_javascript();
3667

    
3668
		//build list of schedules
3669
		$schedules = "<option value='none'>none</option>";
3670
		if (is_array($config['schedules']) && is_array($config['schedules']['schedule'])) {
3671
			foreach ($config['schedules']['schedule'] as $schedule) {
3672
				if ($schedule['name'] <> "") {
3673
					$schedules .= "<option value='{$schedule['name']}'>{$schedule['name']}</option>";
3674
				}
3675
			}
3676
		}
3677
		$bwopt = "";
3678
		foreach (array("Kb" => "Kbit/s", "Mb" => "Mbit/s", "Gb" => "Gbit/s", "b" => "Bit/s") as $bwidx => $bw) {
3679
			$bwopt .= "<option value='{$bwidx}'>{$bw}</option>";
3680
		}
3681

    
3682
		$javasr .= <<<EOD
3683
<script type='text/javascript'>
3684
//<![CDATA[
3685
var addBwRowTo = (function() {
3686

    
3687
	return (function (tableId) {
3688

    
3689
	var table = document.getElementById(tableId);
3690
	var totalrows = table.rows.length -1;
3691

    
3692
	var row = table.insertRow(totalrows + 1);
3693
	var cell1 = row.insertCell(0);
3694
	var cell2 = row.insertCell(1);
3695
	var cell3 = row.insertCell(2);
3696
	var cell4 = row.insertCell(3);
3697

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

    
3703
	});
3704
})();
3705

    
3706
function removeBwRow(el) {
3707
	var d = el.parentNode.parentNode.rowIndex;
3708
	document.getElementById('maintable').deleteRow(d);
3709
}
3710
//]]>
3711
</script>
3712

    
3713
EOD;
3714

    
3715
		return $javasr;
3716
	}
3717

    
3718
	// Compose a table of bandwidths that can then be inserted into the form using a Form_StaticText
3719
	// The table has been "Bootstrapped" to match the web design while maintaining compatibility with
3720
	// with the javascript in this class
3721
	function build_bwtable() {
3722
		global $config;
3723

    
3724
		$bandwidth = $this->GetBandwidth();
3725
				//build list of schedules
3726
		$schedules = array();
3727
		$schedules[] = "none";//leave none to leave rule enabled all the time
3728
		if (is_array($config['schedules']) && is_array($config['schedules']['schedule'])) {
3729
			foreach ($config['schedules']['schedule'] as $schedule) {
3730
				if ($schedule['name'] != "") {
3731
					$schedules[] = $schedule['name'];
3732
				}
3733
			}
3734
		}
3735

    
3736
		$form = '<div class="table-responsive">';
3737
		$form .= '<table id="maintable" class="table table-hover table-striped">';
3738
		$form .= "<thead><tr>";
3739
		$form .= "<th>Bandwidth</th>";
3740
		//$form .= "<td width='35%'><div id='fifthcolumn'>Burst</div></td>";
3741
		$form .= "<th>Bw type</th>";
3742
		$form .= "<th>Schedule</th>";
3743
		$form .= "<th></th>";
3744
		$form .= "</tr></thead>";
3745
		$form .= "<tbody>";
3746

    
3747
		// If there are no bandwidths defined, make a blank one for convenience
3748
		if (empty($bandwidth)) {
3749
			$bandwidth = array(0 => array('bw' => '', 'bwscale' => 'Kb', 'bwsched' => 'none'));
3750
		}
3751

    
3752
		if (is_array($bandwidth)) {
3753
			foreach ($bandwidth as $bwidx => $bw) {
3754
				$form .= '<tr>';
3755
				$form .= '<td class="col-xs-4">';
3756
				$form .= "<input class='form-control' type=\"number\" id=\"bandwidth{$bwidx}\" name=\"bandwidth{$bwidx}\" value=\"{$bw['bw']}\" />";
3757
				//$form .= "</td><td width='20%'>";
3758
				//$form .= "<input class='formfld unknown' size='10' type=\"text\" id=\"burst{$bwidx}\" name=\"burst{$bwidx}\" value=\"{$bw['burst']}\" />";
3759
				$form .= "</td>";
3760
				$form .= '<td class="col-xs-4">';
3761
				$form .= "<select id=\"bwtype{$bwidx}\" name=\"bwtype{$bwidx}\" class=\"form-control\">";
3762

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

    
3766
					if ($bw['bwscale'] == $bwsidx) {
3767
						$form .= " selected";
3768
					}
3769

    
3770
					$form .= ">{$bwscale}</option>";
3771
				}
3772

    
3773
				$form .= "</select>";
3774
				$form .= "</td>";
3775
				$form .= '<td class="col-xs-4">';
3776
				$form .= "<select id=\"bwsched{$bwidx}\" name=\"bwsched{$bwidx}\" class=\"form-control\">";
3777

    
3778
				foreach ($schedules as $schd) {
3779
					$selected = "";
3780
					if ($bw['bwsched'] == $schd) {
3781
						$selected = "selected";
3782
					}
3783

    
3784
					$form .= "<option value='{$schd}' {$selected}>{$schd}</option>";
3785
				}
3786

    
3787
				$form .= "</select>";
3788
				$form .= "</td>";
3789
				$form .= '<td>';
3790
				$form .= '<a class="btn btn-warning" onclick="removeBwRow(this); return false;"><i class="fa fa-trash icon-embed-btn"></i>' . gettext('Delete') . '</a>';
3791
				$form .= "</td></tr>";
3792
			}
3793
		}
3794
		$form .= "</tbody></table></div><br />";
3795

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

    
3800
		return($form);
3801
	}
3802

    
3803
	function build_form() {
3804
		global $g, $config, $pipe, $action, $qname;
3805

    
3806
		//build list of schedules
3807
		$schedules = array();
3808
		$schedules[] = "none";//leave none to leave rule enabled all the time
3809
		if (is_array($config['schedules']) && is_array($config['schedules']['schedule'])) {
3810
			foreach ($config['schedules']['schedule'] as $schedule) {
3811
				if ($schedule['name'] <> "") {
3812
					$schedules[] = $schedule['name'];
3813
				}
3814
			}
3815
		}
3816

    
3817

    
3818
		$sform = new Form();
3819
		$sform->setAction("firewall_shaper.php");
3820

    
3821
		$section = new Form_Section('Limiters');
3822

    
3823
		$section->addInput(new Form_Checkbox(
3824
			'enabled',
3825
			'Enable',
3826
			'Enable limiter and its children',
3827
			($this->GetEnabled() == "on"),
3828
			'on'
3829
		));
3830

    
3831
		$section->addInput(new Form_Input(
3832
			'newname',
3833
			'*Name',
3834
			'text',
3835
			$this->GetQname()
3836
		));
3837

    
3838
		$section->addInput(new Form_Input(
3839
			'name',
3840
			null,
3841
			'hidden',
3842
			$this->GetQname()
3843
		));
3844

    
3845
		if ($this->GetNumber() > 0) {
3846
			$section->addInput(new Form_Input(
3847
				'number',
3848
				null,
3849
				'hidden',
3850
				$this->GetNumber()
3851
			));
3852
		}
3853

    
3854
		$bandwidth = $this->GetBandwidth();
3855

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

    
3863
		$mask = $this->GetMask();
3864

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

    
3874
		$group = new Form_Group(null);
3875

    
3876
		$group->add(new Form_Select(
3877
			'maskbits',
3878
			null,
3879
			$mask['bits'],
3880
			array_combine(range(32, 1, -1), range(32, 1, -1))
3881
		))->setHelp('IPv4 mask bits%1$s%2$s', '<br />', '255.255.255.255/?');
3882

    
3883
		$group->add(new Form_Select(
3884
			'maskbitsv6',
3885
			null,
3886
			$mask['bitsv6'],
3887
			array_combine(range(128, 1, -1), range(128, 1, -1))
3888
		))->setHelp('IPv6 mask bits%1$s%2$s', '<br />', '<span style="font-family:consolas">ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/?</span>');
3889

    
3890
		$section->add($group);
3891

    
3892
		$section->addInput(new Form_Input(
3893
			'description',
3894
			'Description',
3895
			'text',
3896
			$this->GetDescription()
3897
		))->setHelp('A description may be entered here for administrative reference (not parsed).');
3898

    
3899
		$sform->add($section);
3900

    
3901
		$section = new Form_Section('Advanced Options');
3902

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

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

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

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

    
3934
		$sform->add($section);
3935

    
3936
		return($sform);
3937
		}
3938

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

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

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

    
3968
}
3969

    
3970
class dnqueue_class extends dummynet_class {
3971
	var $pipeparent;
3972
	var $weight;
3973

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

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

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

    
3998
	function validate_input($data, &$input_errors) {
3999
		parent::validate_input($data, $input_errors);
4000

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

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

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

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

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

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

    
4088
		return $tree;
4089
	}
4090

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

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

    
4110
		return $pfq_rule;
4111
	}
4112

    
4113
	function build_javascript() {
4114
		return parent::build_javascript();
4115
	}
4116

    
4117
	function build_form() {
4118
		global $g, $config, $pipe, $action, $qname;
4119

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

    
4131

    
4132
		$sform = new Form();
4133
		$sform->setAction("firewall_shaper.php");
4134
		$section = new Form_Section('Limiters');
4135

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

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

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

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

    
4167
		$mask = $this->GetMask();
4168

    
4169
		$section->addInput(new Form_Select(
4170
			'mask',
4171
			'Mask',
4172
			$mask['type'],
4173
			array('none' => gettext('None'), 'srcaddress' => gettext('Source addresses'), 'dstaddress' => gettext('Destination addresses'))
4174
		))->setHelp('If "source" or "destination" slots is chosen a dynamic pipe with the bandwidth, delay, packet loss ' .
4175
					'and queue size given above will be created for each source/destination IP address encountered, respectively. ' .
4176
					'This makes it possible to easily specify bandwidth limits per host.');
4177

    
4178
		$group = new Form_Group(null);
4179

    
4180
		$group->add(new Form_Select(
4181
			'maskbits',
4182
			null,
4183
			$mask['bits'],
4184
			array_combine(range(32, 1, -1), range(32, 1, -1))
4185
		))->setHelp('IPv4 mask bits%1$s%2$s', '<br />', '255.255.255.255/?');
4186

    
4187
		$group->add(new Form_Select(
4188
			'maskbitsv6',
4189
			null,
4190
			$mask['bitsv6'],
4191
			array_combine(range(128, 1, -1), range(128, 1, -1))
4192
		))->setHelp('IPv6 mask bits%1$s%2$s', '<br />', '<span style="font-family:consolas">ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/?</span>');
4193

    
4194
		$section->add($group);
4195

    
4196
		$section->addInput(new Form_Input(
4197
			'description',
4198
			'Description',
4199
			'text',
4200
			$this->GetDescription()
4201
		))->setHelp('A description may be entered here for administrative reference (not parsed).');
4202

    
4203
		$sform->add($section);
4204

    
4205
		$section = new Form_Section('Advanced Options');
4206

    
4207
		$section->addInput(new Form_Input(
4208
			'weight',
4209
			'Weight',
4210
			'number',
4211
			$this->GetWeight(),
4212
			['min' => '1', 'max' => '100']
4213
		))->setHelp('For queues under the same parent this specifies the share that a queue gets(values range from 1 to 100),' .
4214
					' it can be left blank otherwise.');
4215

    
4216
		$section->addInput(new Form_Input(
4217
			'plr',
4218
			'Packet Loss Rate',
4219
			'number',
4220
			$this->GetPlr(),
4221
			['step' => '0.001', 'min' => '0.000']
4222
		))->setHelp('In most cases, zero (0) should be specified here (or leave the field empty). ' .
4223
					'A value of 0.001 means one packet in 1000 gets dropped');
4224

    
4225
		$section->addInput(new Form_Input(
4226
			'qlimit',
4227
			'Queue size (slots)',
4228
			'number',
4229
			$this->GetQlimit()
4230
		))->setHelp('In most cases, the field should be left empty. All packets in this pipe are placed into a fixed-size queue first, ' .
4231
					'then they are delayed by value specified in the Delay field, and then they are delivered to their destination.');
4232

    
4233
		$section->addInput(new Form_Input(
4234
			'buckets',
4235
			'Bucket size (slots)',
4236
			'number',
4237
			$this->GetBuckets()
4238
		))->setHelp('In most cases, this field should be left empty. It increases the hash size set');
4239

    
4240
		$section->addInput(new Form_Input(
4241
			'pipe',
4242
			null,
4243
			'hidden',
4244
			$this->GetPipe()
4245
		));
4246

    
4247
		$sform->add($section);
4248

    
4249
		return($sform);
4250
	}
4251

    
4252
	function update_dn_data(&$data) {
4253
		$this->ReadConfig($data);
4254
	}
4255

    
4256
	function wconfig() {
4257
		$cflink =& get_dn_reference_to_me_in_config($this->GetLink());
4258
		if (!is_array($cflink)) {
4259
			$cflink = array();
4260
		}
4261
		$cflink['name'] = $this->GetQname();
4262
		$cflink['number'] = $this->GetNumber();
4263
		$cflink['qlimit'] = $this->GetQlimit();
4264
		$cflink['description'] = $this->GetDescription();
4265
		$cflink['weight'] = $this->GetWeight();
4266
		$cflink['enabled'] = $this->GetEnabled();
4267
		$cflink['buckets'] = $this->GetBuckets();
4268
		$mask = $this->GetMask();
4269
		$cflink['mask'] = $mask['type'];
4270
		$cflink['maskbits'] = $mask['bits'];
4271
		$cflink['maskbitsv6'] = $mask['bitsv6'];
4272
	}
4273
}
4274

    
4275
function get_dummynet_name_list() {
4276

    
4277
	$dn_name_list =& get_unique_dnqueue_list();
4278
	$dn_name = array();
4279
	if (is_array($dn_name_list)) {
4280
		foreach ($dn_name_list as $key => $value) {
4281
			$dn_name[] = $key;
4282
		}
4283
	}
4284

    
4285
	return $dn_name;
4286

    
4287
}
4288

    
4289
function get_altq_name_list() {
4290
	$altq_name_list =& get_unique_queue_list();
4291
	$altq_name = array();
4292
	if (is_array($altq_name_list)) {
4293
		foreach ($altq_name_list as $key => $aqobj) {
4294
			$altq_name[] = $key;
4295
		}
4296
	}
4297

    
4298
	return $altq_name;
4299
}
4300

    
4301
/*
4302
 * XXX: TODO Make a class shaper to hide all these functions
4303
 * from the global namespace.
4304
 */
4305

    
4306
/*
4307
 * This is a layer violation but for now there is no way
4308
 * I can find to properly do this with PHP.
4309
 */
4310
function altq_get_default_queue($interface) {
4311
	global $altq_list_queues;
4312

    
4313
	$altq_tmp = $altq_list_queues[$interface];
4314
	if ($altq_tmp) {
4315
		return $altq_tmp->GetDefaultQueuePresent();
4316
	} else {
4317
		return false;
4318
	}
4319
}
4320

    
4321
function altq_check_default_queues() {
4322
	global $altq_list_queues;
4323

    
4324
	$count = 0;
4325
	if (is_array($altq_list_queues)) {
4326
		foreach ($altq_list_queues as $altq) {
4327
			if ($altq->GetDefaultQueuePresent()) {
4328
				$count++;
4329
			}
4330
		}
4331
	}
4332
	else {
4333
		$count++;
4334
	}
4335

    
4336
	return 0;
4337
}
4338

    
4339
function &get_unique_queue_list() {
4340
	global $altq_list_queues;
4341

    
4342
	$qlist = array();
4343
	if (is_array($altq_list_queues)) {
4344
		foreach ($altq_list_queues as $altq) {
4345
			if ($altq->GetEnabled() == "") {
4346
				continue;
4347
			}
4348
			$tmplist =& $altq->get_queue_list();
4349
			foreach ($tmplist as $qname => $link) {
4350
				if ($link->GetEnabled() <> "") {
4351
					$qlist[$qname] = $link;
4352
				}
4353
			}
4354
		}
4355
	}
4356
	return $qlist;
4357
}
4358

    
4359
function &get_unique_dnqueue_list() {
4360
	global $dummynet_pipe_list;
4361

    
4362
	$qlist = array();
4363
	if (is_array($dummynet_pipe_list)) {
4364
		foreach ($dummynet_pipe_list as $dn) {
4365
			if ($dn->GetEnabled() == "") {
4366
				continue;
4367
			}
4368
			$tmplist =& $dn->get_queue_list();
4369
			foreach ($tmplist as $qname => $link) {
4370
				$qlist[$qname] = $link;
4371
			}
4372
		}
4373
	}
4374
	return $qlist;
4375
}
4376

    
4377
function ref_on_altq_queue_list($parent, $qname) {
4378
	if (isset($GLOBALS['queue_list'][$qname])) {
4379
		$GLOBALS['queue_list'][$qname]++;
4380
	} else {
4381
		$GLOBALS['queue_list'][$qname] = 1;
4382
	}
4383

    
4384
	unref_on_altq_queue_list($parent);
4385
}
4386

    
4387
function unref_on_altq_queue_list($qname) {
4388
	$GLOBALS['queue_list'][$qname]--;
4389
	if ($GLOBALS['queue_list'][$qname] <= 1) {
4390
		unset($GLOBALS['queue_list'][$qname]);
4391
	}
4392
}
4393

    
4394
function read_altq_config() {
4395
	global $altq_list_queues, $config;
4396
	$path = array();
4397

    
4398
	if (!is_array($config['shaper'])) {
4399
		$config['shaper'] = array();
4400
	}
4401
	if (!is_array($config['shaper']['queue'])) {
4402
		$config['shaper']['queue'] = array();
4403
	}
4404
	$a_int = &$config['shaper']['queue'];
4405

    
4406
	$altq_list_queues = array();
4407

    
4408
	if (!is_array($config['shaper']['queue'])) {
4409
		return;
4410
	}
4411

    
4412
	foreach ($a_int as $key => $conf) {
4413
		$int = $conf['interface'];
4414
		$root =& new altq_root_queue();
4415
		$root->SetInterface($int);
4416
		$altq_list_queues[$root->GetInterface()] = &$root;
4417
		$root->ReadConfig($conf);
4418
		array_push($path, $key);
4419
		$root->SetLink($path);
4420
		if (is_array($conf['queue'])) {
4421
			foreach ($conf['queue'] as $key1 => $q) {
4422
				array_push($path, $key1);
4423
				/*
4424
				 * XXX: we completely ignore errors here but anyway we must have
4425
				 *	checked them before so no harm should be come from this.
4426
				 */
4427
				$root->add_queue($root->GetInterface(), $q, $path, $input_errors);
4428
				array_pop($path);
4429
			}
4430
		}
4431
		array_pop($path);
4432
	}
4433
}
4434

    
4435
function read_dummynet_config() {
4436
	global $dummynet_pipe_list, $config;
4437
	$path = array();
4438

    
4439
	if (!is_array($config['dnshaper'])) {
4440
		$config['dnshaper'] = array();
4441
	}
4442
	if (!is_array($config['dnshaper']['queue'])) {
4443
		$config['dnshaper']['queue'] = array();
4444
	}
4445
	$a_int = &$config['dnshaper']['queue'];
4446

    
4447
	$dummynet_pipe_list = array();
4448

    
4449
	if (!is_array($config['dnshaper']['queue']) ||
4450
	    !count($config['dnshaper']['queue'])) {
4451
		return;
4452
	}
4453

    
4454
	foreach ($a_int as $key => $conf) {
4455
		if (empty($conf['name'])) {
4456
			continue; /* XXX: grrrrrr at php */
4457
		}
4458
		$root =& new dnpipe_class();
4459
		$root->ReadConfig($conf);
4460
		$dummynet_pipe_list[$root->GetQname()] = &$root;
4461
		array_push($path, $key);
4462
		$root->SetLink($path);
4463
		if (is_array($conf['queue'])) {
4464
			foreach ($conf['queue'] as $key1 => $q) {
4465
				array_push($path, $key1);
4466
				/*
4467
				 * XXX: we completely ignore errors here but anyway we must have
4468
				 *	checked them before so no harm should be come from this.
4469
				 */
4470
				$root->add_queue($root->GetQname(), $q, $path, $input_errors);
4471
				array_pop($path);
4472
			}
4473
		}
4474
		array_pop($path);
4475
	}
4476
}
4477

    
4478
function get_interface_list_to_show() {
4479
	global $altq_list_queues, $config;
4480
	global $shaperIFlist;
4481

    
4482
	$tree = "";
4483
	foreach ($shaperIFlist as $shif => $shDescr) {
4484
		if ($altq_list_queues[$shif]) {
4485
			continue;
4486
		} else {
4487
			if (!is_altq_capable(get_real_interface($shif))) {
4488
				continue;
4489
			}
4490
			$tree .= " <li><a href=\"firewall_shaper.php?interface=".$shif."&amp;action=add\">".$shDescr."</a></li>";
4491
		}
4492
	}
4493

    
4494
	return $tree;
4495
}
4496

    
4497
function filter_generate_altq_queues() {
4498
	global $altq_list_queues;
4499

    
4500
	read_altq_config();
4501

    
4502
	$altq_rules = "";
4503
	foreach ($altq_list_queues as $altq) {
4504
		$altq_rules .= $altq->build_rules();
4505
	}
4506

    
4507
	return $altq_rules;
4508
}
4509

    
4510
function dnqueue_find_nextnumber() {
4511
	global $dummynet_pipe_list;
4512

    
4513
	$dnused = array();
4514
	if (is_array($dummynet_pipe_list)) {
4515
		foreach ($dummynet_pipe_list as $dn) {
4516
			$tmplist =& $dn->get_queue_list();
4517
			foreach ($tmplist as $qname => $link) {
4518
				if ($link[0] == "?") {
4519
					$dnused[$qname] = substr($link, 1);
4520
				}
4521
			}
4522
		}
4523
	}
4524

    
4525
	sort($dnused, SORT_NUMERIC);
4526
	$dnnumber = 0;
4527
	$found = false;
4528
	foreach ($dnused as $dnnum) {
4529
		if (($dnnum - $dnnumber) > 1) {
4530
			$dnnumber = $dnnum - 1;
4531
			$found = true;
4532
			break;
4533
		} else {
4534
			$dnnumber = $dnnum;
4535
		}
4536
	}
4537

    
4538
	if ($found == false) {
4539
		$dnnumber++;
4540
	}
4541

    
4542
	unset($dnused, $dnnum, $found);
4543
	return $dnnumber;
4544
}
4545

    
4546
function dnpipe_find_nextnumber() {
4547
	global $dummynet_pipe_list;
4548

    
4549
	$dnused = array();
4550
	foreach ($dummynet_pipe_list as $dn) {
4551
		$dnused[] = $dn->GetNumber();
4552
	}
4553

    
4554
	sort($dnused, SORT_NUMERIC);
4555
	$dnnumber = 0;
4556
	$found = false;
4557
	foreach ($dnused as $dnnum) {
4558
		if (($dnnum - $dnnumber) > 1) {
4559
			$dnnumber = $dnnum - 1;
4560
			$found = true;
4561
			break;
4562
		} else {
4563
			$dnnumber = $dnnum;
4564
		}
4565
	}
4566

    
4567
	if ($found == false) {
4568
		$dnnumber++;
4569
	}
4570

    
4571
	unset($dnused, $dnnum, $found);
4572
	return $dnnumber;
4573
}
4574

    
4575
function filter_generate_dummynet_rules() {
4576
	global $g, $dummynet_pipe_list;
4577

    
4578
	read_dummynet_config();
4579

    
4580
	$dn_rules = "";
4581
	$max_qlimit = "100"; // OS default
4582
	foreach ($dummynet_pipe_list as $dn) {
4583
		$dn_rules .= $dn->build_rules();
4584
		$this_qlimit = $dn->GetQlimit();
4585
		if ($this_qlimit > $max_qlimit) {
4586
			$max_qlimit = $this_qlimit;
4587
		}
4588
	}
4589
	if (!is_numericint($max_qlimit)) {
4590
		$max_qlimit = "100";
4591
	}
4592
	if (!empty($dn_rules)) {
4593
		if (!is_module_loaded("dummynet.ko")) {
4594
			mwexec("/sbin/kldload dummynet");
4595
		}
4596
		set_sysctl(array(
4597
				"net.inet.ip.dummynet.io_fast" => "1",
4598
				"net.inet.ip.dummynet.hash_size" => "256",
4599
				"net.inet.ip.dummynet.pipe_slot_limit" => $max_qlimit
4600
		));
4601
		file_put_contents("{$g['tmp_path']}/rules.limiter", $dn_rules);
4602
		mwexec("/sbin/ipfw {$g['tmp_path']}/rules.limiter");
4603
	}
4604
}
4605

    
4606
function build_iface_without_this_queue($iface, $qname) {
4607
	global $g, $altq_list_queues;
4608
	global $shaperIFlist;
4609

    
4610
	$altq =& $altq_list_queues[$iface];
4611

    
4612
	if ($altq) {
4613
		$scheduler = $altq->GetScheduler();
4614
	}
4615

    
4616
	$form = '<dl class="dl-horizontal">';
4617

    
4618
	$form .= '	<dt>';
4619
	$form .= '		<a href="firewall_shaper.php?interface=' . $iface . '&amp;queue=' . $iface . '&amp;action=show">' . $shaperIFlist[$iface] . '</a>';
4620
	$form .= '	</dt>';
4621
	$form .= '	<dd>';
4622
	$form .=		$scheduler;
4623
	$form .= '	</dd>';
4624

    
4625
	$form .= '	<dt>';
4626
	$form .= 'Clone';
4627
	$form .= '	</dt>';
4628
	$form .= '	<dd>';
4629
	$form .= '<a class="btn btn-info btn-xs" href="firewall_shaper_queues.php?interface=';
4630
	$form .= $iface . '&amp;queue=';
4631
	$form .= $qname . '&amp;action=add">';
4632
	$form .= '<i class="fa fa-clone icon-embed-btn"></i>';
4633
	$form .= gettext("Clone Shaper to this Interface") . '</a>';
4634
	$form .= '	</dd>';
4635

    
4636
	$form .= '</dl>';
4637

    
4638
	return $form;
4639

    
4640
}
4641

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

    
4645
$shaper_msg = gettext("The tree on the left navigates through the %s.");
4646
$default_shaper_msg .= sprintf($shaper_msg, gettext("queues")) . "<br />";
4647
$dn_default_shaper_msg .= sprintf($shaper_msg, gettext("limiters")) . "<br />";
4648

    
4649
$shaper_msg = gettext("Buttons at the bottom represent %s actions and are activated accordingly.");
4650
$default_shaper_msg .= sprintf($shaper_msg, gettext("queue"));
4651
$dn_default_shaper_msg .= sprintf($shaper_msg, gettext("limiter"));
4652

    
4653
// Check to see if the specified interface has a queue configured
4654
function interface_has_queue($if) {
4655
	global $config;
4656

    
4657
	if ($config['shaper']) {
4658
		foreach ($config['shaper']['queue'] as $queue) {
4659
			if ($queue['interface'] === $if) {
4660
				return true;
4661
			}
4662
		}
4663
	}
4664

    
4665
	return false;
4666
}
4667
?>
(42-42/54)