Project

General

Profile

Download (18.7 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/* $Id$ */
3
/*
4
	shaper.inc
5
	Copyright (C) 2004-2006 Scott Ullrich
6
	Copyright (C) 2005-2006 Bill Marquette
7
	All rights reserved.
8

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

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

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

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

    
30
*/
31

    
32
/* include all configuration functions */
33
require_once("functions.inc");
34
require_once("pkg-utils.inc");
35
require_once("notices.inc");
36

    
37
function find_default_queue($interface) {
38
	global $config, $queue_cache;
39
	$qconfig = $config;
40

    
41
	/* quick return if we've already seen the default queue for this interface */
42
	if (isset($queue_cache['defq'][$interface]))
43
	return $queue_cache['defq'][$interface];
44

    
45
	if (is_array($qconfig['shaper']['queue'])) {
46
		foreach ($qconfig['shaper']['queue'] as $queue) {
47
			if(isset($queue['defaultqueue']) and ($queue['defaultqueue'] <> "")) {
48
				/* If this is a child queue */
49
				if(isset($queue['attachtoqueue'])) {
50
					if(is_subqueue_used_on_interface($queue['attachtoqueue'], $interface)) {
51
						$queue_cache['defq'][$interface] = $queue['name'];
52
						return $queue['name'];
53
					}
54
				} else {
55
					$queue_cache['defq'][$interface] = $queue['name'];
56
					return $queue['name'];
57
				}
58
			}
59
		}
60
	}
61

    
62
	/* unreachable */
63
	return null;
64
}
65

    
66
function get_ack_queue($interface) {
67
	global $config, $queue_cache;
68

    
69
	/* quick return if we've already seen the ack queue for this interface */
70
	if (isset($queue_cache['ackq'][$interface]))
71
	return $queue_cache['ackq'][$interface];
72

    
73
	$qconfig = $config;
74

    
75
	if (is_array($qconfig['shaper']['queue'])) {
76
		foreach ($qconfig['shaper']['queue'] as $queue) {
77
			if(isset($queue['ack']))
78
			if(isset($queue['attachtoqueue']))
79
			if(is_subqueue_used_on_interface($queue['attachtoqueue'], $interface)) {
80
				/* Add to cache */
81
				$queue_cache['ackq'][$interface] = $queue['name'];
82
				return $queue['name'];
83
			}
84
		}
85
	}
86
	/* unreachable */
87
	return null;
88
}
89

    
90
function filter_generate_altq_queues($altq_ints) {
91
	global $config;
92
	$altq_rules = "";
93
	if (is_array($config['shaper']['queue'])) {
94
		foreach ($config['shaper']['queue'] as $rule) {
95
			update_filter_reload_status("Generating ALTQ queue {$rule['descr']}...");
96
			$options = "";
97
			// check to make sure we're actually using this queue.
98
			//if(stristr($altq_ints, $rule['name']) !== FALSE) {
99
			$altq_rules .= "queue {$rule['name']} ";
100
			if (isset($rule['bandwidth']) and $rule['bandwidth'] <> "")
101
			$altq_rules .= "bandwidth {$rule['bandwidth']}{$rule['bandwidthtype']} ";
102
			if (isset($rule['priority']) and $rule['priority'] <> "")
103
			$altq_rules .= "priority {$rule['priority']} ";
104
			if (isset($rule['qlimit']) and $rule['qlimit'] <> "")
105
			$altq_rules .= "qlimit {$rule['qlimit']} ";
106
			if(isset($rule['red']) and $rule['red'] <> "")
107
			$options .= " red";
108
			if(isset($rule['borrow']) and $rule['borrow'] <> "")
109
			$options .= " borrow";
110
			if(isset($rule['ecn']) and $rule['ecn'] <> "")
111
			$options .= " ecn";
112
			if(isset($rule['rio']) and $rule['rio'] <> "")
113
			$options .= " rio";
114
			if(isset($rule['defaultqueue']) and $rule['defaultqueue'] <> "")
115
			$options .= " default";
116
			if(isset($rule['upperlimit']) and $rule['upperlimit'] <> "") {
117
				if ($rule['upperlimit1'] <> "")
118
					$options .= " upperlimit({$rule['upperlimit1']} {$rule['upperlimit2']} {$rule['upperlimit3']})";
119
				else
120
					$options .= " upperlimit {$rule['upperlimit3']}";
121
			}
122
			if(isset($rule['linkshare']) and $rule['linkshare'] <> "") {
123
				if ($rule['linkshare1'] <> "")
124
					$options .= " linkshare({$rule['linkshare1']} {$rule['linkshare2']} {$rule['linkshare3']})";
125
				else
126
					$options .= " linkshare {$rule['linkshare3']}";
127
			}
128
			if(isset($rule['realtime']) and $rule['realtime'] <> "") {
129
				if ($rule['realtime1'] <> "")
130
					$options .= " realtime({$rule['realtime1']} {$rule['realtime2']} {$rule['realtime3']})";
131
				else
132
					 $options .= " realtime {$rule['realtime3']}";
133
			}
134
			$scheduler_type = $config['shaper']['schedulertype'];
135
			$altq_rules .= "{$scheduler_type} ";
136
			if($options)
137
				$altq_rules .= "( {$options} )";
138
			$fsq="";
139
			foreach($config['shaper']['queue'] as $q) {
140
				if($q['attachtoqueue'] == $rule['name']) {
141
					if($fsq == "") {
142
						$altq_rules .= "{ ";
143
					}
144
					else if($fsq == "1") {
145
						$altq_rules .= ", ";
146
					}
147
					$altq_rules .= $q['name'];
148
					$fsq = "1";
149
				}
150
			}
151
			if($fsq == "1")
152
			$altq_rules .= " }";
153
			$altq_rules .= "\n";
154
			//}
155
		}
156
	}
157
	return $altq_rules;
158
}
159

    
160
/* Find a queue that's attached to this one and see if that queue is used on this interface */
161
function is_subqueue_used_on_interface($queuename, $interface) {
162
	global $config;
163
	$qconfig = $config;
164
	if (!is_array($qconfig['shaper']['queue'])) return 0;
165

    
166
	foreach ($qconfig['shaper']['queue'] as $queue) {
167
		if($queue['attachtoqueue'] == $queuename) {
168
			/* recurse if we're a parent queue */
169
			if ($queue['parentqueue'] == "on") {
170
				return is_subqueue_used_on_interface($queue['name'], $interface);
171
			}
172

    
173
			/* If we're not a parent check to see if the queue is used on this interface */
174
			$subqueue_interface = filter_is_queue_being_used_on_interface($queue['name'], $interface);
175
			if ($subqueue_interface != ""){
176
				return 1;
177
			}
178
		}
179
	}
180
	return 0;
181
}
182

    
183
function filter_is_queue_being_used_on_interface($queuename, $interface, $direction = 'in') {
184
	global $config;
185
	$lconfig = $config;
186

    
187
	if(!is_array($lconfig['shaper']['rule'])) return null;
188
	foreach($lconfig['shaper']['rule'] as $rule) {
189
		$q  = $direction . 'queue';
190
		$if = $direction . '-interface';
191
		if(($rule[$q] == $queuename && $rule[$if] == $interface))
192
		return $interface;
193
	}
194
	return null;
195
}
196

    
197
function filter_setup_altq_interfaces() {
198
	global $config;
199
	$altq_rules  = "";
200
	$queue_names = "";
201
	$is_first = "";
202

    
203
	if(!is_array($config['shaper']['queue'])) return null;
204

    
205
	$ifdescrs = array('wan', 'lan');
206
	for ($j = 1; isset($config['interfaces']['opt' . $j]); $j++) {
207
		$ifdescrs[] = "opt" . $j;
208
	}
209
	foreach ($ifdescrs as $ifdescr => $ifname) {
210

    
211
		$queue_names = "";
212
		$is_first = "";
213

    
214
		$queue_names = find_root_queue($ifname);
215

    
216
		if($queue_names <> ""){
217
			$altq_rules .= "altq on {$config['interfaces'][$ifname]['if']} ";
218
			$bandwidth_arr = get_queue_bandwidth($queue_names);
219
			$bandwidth = "bandwidth {$bandwidth_arr['bandwidth']}{$bandwidth_arr['bandwidthtype']}";
220
			$altq_rules .= "{$config['shaper']['schedulertype']} {$bandwidth} ";
221
			$altq_rules .= "queue { {$queue_names} }";
222
		}
223
		$altq_rules .= "\n";
224

    
225
	}
226
	return $altq_rules;
227
}
228

    
229

    
230

    
231
/* Find the root queue for an interface */
232
function find_root_queue($ifname) {
233
	global $config;
234
	$queue_names = "";
235
	foreach ($config['shaper']['queue'] as $queue) {
236
		$rule_interface = "";
237
		$q = $queue;
238
		/* if we're a parentqueue and aren't attached to another queue we're probably a root */
239
		if ((isset($q['parentqueue']) && $q['parentqueue'] <> "") && (!isset($q['attachtoqueue']) || $q['attachtoqueue'] == "")) {
240
			/* Confirm that this is a valid queue for this interface */
241
			$rule_interface = is_subqueue_used_on_interface($q['name'], $ifname);
242
			if ($rule_interface == 1) {
243
				if (strlen($queue_names) > 0)
244
					$queue_names .= " ";
245
				$queue_names .= $q['name'];
246
			}
247
		}
248
	}
249
	return $queue_names;
250
}
251

    
252
function get_queue_bandwidth($name) {
253
	global $config;
254
	foreach ($config['shaper']['queue'] as $queue) {
255
		if ($queue['name'] == $name) {
256
			return array(
257
				'bandwidth' => $queue['bandwidth'],
258
				'bandwidthtype' => $queue['bandwidthtype']
259
			);
260
		}
261
	}
262
}
263

    
264
function is_queue_attached_children($name) {
265
	global $config;
266
	if (!is_array($config['shaper']['queue'])) return 0;
267
	foreach ($config['shaper']['queue'] as $queue) {
268
		if($queue['attachtoqueue'] == $name) return 1;
269
	}
270
	return 0;
271
}
272

    
273
function queue_interface_recursive($queuename) {
274
	global $config;
275
	foreach($config['shaper']['queue'] as $queue) {
276
		if($queue['attachtoqueue'] == $queuename) {
277
			$status = queue_interface_recursive($queue['name']);
278
			if($status <> "") return $status;
279
		}
280
		foreach($config['shaper']['rule'] as $rule) {
281
			if($rule['inqueue'] == $queuename)
282
			return $rule['in-interface'];
283
		}
284
	}
285

    
286
	/* unreachable */
287
	return null;
288
}
289

    
290
function is_subqueue($name) {
291
	global $config;
292
	$queues = $config['shaper']['queue']; /* must assign to keep from corrupting in memory $config */
293
	if (!is_array($queues)) return 0;
294
	foreach ($queues as $queue) {
295
		if($queue['attachtoqueue'] == $name) return 1;
296
	}
297
	return 0;
298
}
299

    
300
function filter_altq_get_queuename($queuenum) {
301
	global $config;
302
	$x=0;
303
	foreach($config['shaper']['queue'] as $rule) {
304
		if($x == $queuenum)
305
		return $rule['name'];
306
		$x++;
307
	}
308
	/* unreachable */
309
	return null;
310
}
311

    
312
function filter_generate_pf_altq_rules() {
313
	/* I don't think we're in IPFW anymore Toto */
314
	$i = 0;
315

    
316
	global $config, $g, $tcpflags;
317

    
318
	$lancfg = $config['interfaces']['lan'];
319
	$pptpdcfg = $config['pptpd'];
320
	$pppoecfg = $config['pppoe'];
321

    
322
	$lanif = $lancfg['if'];
323
	$wanif = get_real_wan_interface();
324

    
325
	$lansa = gen_subnet($lancfg['ipaddr'], $lancfg['subnet']);
326
	$lansn = $lancfg['subnet'];
327

    
328
	/* optional interfaces */
329
	$optcfg = array();
330
	generate_optcfg_array($optcfg);
331

    
332
	if ($pptpdcfg['mode'] == "server") {
333
		$pptpsa = $pptpdcfg['remoteip'];
334
		$pptpsn = $g['pptp_subnet'];
335
		if($config['pptp']['pptp_subnet'] <> "")
336
		$pptpsn = $config['pptp']['pptp_subnet'];
337
	}
338

    
339
	if ($pppoecfg['mode'] == "server") {
340
		$pppoesa = $pppoecfg['remoteip'];
341
		$pppoesn = $g['pppoe_subnet'];
342
		if($config['pppoe']['pppoe_subnet'] <> "")
343
		$pppoesn = $config['pppoe']['pppoe_subnet'];
344
	}
345

    
346
	/* generate rules */
347
	if (isset($config['shaper']['rule']))
348
	foreach ($config['shaper']['rule'] as $rule) {
349

    
350
		/* don't include disabled rules */
351
		if (isset($rule['disabled'])) {
352
			$i++;
353
			continue;
354
		}
355

    
356
		update_filter_reload_status("Generating ALTQ rule {$rule['descr']}...");
357

    
358
		switch($rule['in-interface']) {
359
			case "pptp": /* does the rule deal with a PPTP interface? */
360
				if ($pptpdcfg['mode'] != "server") {
361
					if (($rule['source']['network'] == "pptp") ||
362
					($rule['destination']['network'] == "pptp")) {
363
						$i++;
364
						continue;
365
					}
366
				}
367

    
368
				$nif = $g['n_pptp_units'];
369
				if($config['pptp']['n_pptp_units'] <> "")
370
					$nif = $config['pptp']['n_pptp_units'];
371

    
372
				$ispptp = true;
373
				break;
374

    
375
			case "pppoe": /* does the rule deal with a PPPOE interface? */
376
				if ($pppoecfg['mode'] != "server") {
377
					if (($rule['source']['network'] == "pppoe") ||
378
					($rule['destination']['network'] == "pppoe")) {
379
						$i++;
380
						continue;
381
					}
382
				}
383

    
384
				$nif = $g['n_pppoe_units'];
385
				if($config['pppoe']['n_pppoe_units'] <> "")
386
					$nif = $config['pppoe']['n_pppoe_units'];
387

    
388
				$ispppoe = true;
389
				break;
390
			default:
391
				if (strstr($rule['in-interface'], "opt")) {
392
					if (!array_key_exists($rule['in-interface'], $optcfg)) {
393
						$i++;
394
						continue;
395
					}
396
				}
397
				$nif = 1;
398
				$ispptp = false;
399
				$ispppoe = false;
400
		}
401

    
402
		if (strstr($rule['source']['network'], "opt")) {
403
			if (!array_key_exists($rule['source']['network'], $optcfg)) {
404
				$i++;
405
				continue;
406
			}
407
		}
408
		if (strstr($rule['destination']['network'], "opt")) {
409
			if (!array_key_exists($rule['destination']['network'], $optcfg)) {
410
				$i++;
411
				continue;
412
			}
413
		}
414

    
415
		/* check for unresolvable aliases */
416
		if ($rule['source']['address'] && !alias_expand($rule['source']['address'])) {
417
			$i++;
418
			continue;
419
		}
420
		if ($rule['destination']['address'] && !alias_expand($rule['destination']['address'])) {
421
			$i++;
422
			continue;
423
		}
424

    
425
		$lanip = find_interface_ip($config['interfaces']['lan']['if']);
426
		$wanip = find_interface_ip(get_real_wan_interface());
427

    
428
		for ($iif = 0; $iif < $nif; $iif++) {
429
			$direction = 'in';
430
			$line = "pass {$direction} on ";
431

    
432
			if ($ispptp) {
433
				$line .= " ng" . ($iif+1);
434
			}
435
			else if($ispppoe) {
436
				$line .= " ng" . ($iif+1);
437
			}
438
			else {
439
				$friendly_desc = convert_friendly_interface_to_friendly_descr($rule['in-interface']);
440
				$line .= " \${$friendly_desc} ";
441
			}
442

    
443
			/* get protocol */
444
			$proto = $rule['protocol'];
445
			if (isset($proto)) {
446
				$line .= "proto {$proto} ";
447
			}
448

    
449
			/* get source address */
450
			if (isset($rule['source']['any'])) {
451
				$src = "any";
452
			} else if ($rule['source']['network']) {
453
				if (stristr($rule['source']['network'], "opt") == true) {
454
					$src = $optcfg[$rule['source']['network']]['sa'] . "/" .
455
						$optcfg[$rule['source']['network']]['sn'];
456
				} else {
457
					switch ($rule['source']['network']) {
458
						case 'wanip':
459
							$src = $wanip;
460
							break;
461
						case 'lanip':
462
							$src = $lanip;
463
							break;
464
						case 'lan':
465
							$src = "$lansa/$lansn";
466
							break;
467
						case 'pptp':
468
							$src = "$pptpsa/$pptpsn";
469
							break;
470
						case 'pppoe':
471
							$src = "$pppoesa/$pppoesn";
472
							break;
473
					}
474
				}
475
			} else if ($rule['source']['address']) {
476
				$src = alias_expand($rule['source']['address']);
477
				if(!$src)
478
					$src = $rule['source']['address'];
479
			}
480

    
481
			if (!$src) {
482
				printf("No source address found in rule $i\n");
483
				break;
484
			}
485

    
486
			if (isset($rule['source']['not'])) {
487

    
488
				/*pf is not really happy with this sexy ($src = "!{$src}";) approach of
489
				 * negating a list or macro. So we have to write out a ! on each entry.
490
				 */
491

    
492
				/* not very happy with this! but it beats copying this section to
493
				 * several places.
494
				 */
495
				$alias = alias_expand(substr($src, 1));
496

    
497
				if(isset($alias) && stristr($alias, "$")) {
498
					$alias = alias_expand_value(substr($src, 1));
499
					$src = "{";
500
					foreach(preg_split("/[\s]+/", $alias) as $item) {
501
						if($item != "") {
502
							$src .= " !{$item}";
503
						}
504
					}
505
					$src .= " }";
506
				}
507
				else {
508
					$src = "!{$src}";
509
				}
510
			}
511
			$line .= "from {$src} ";
512

    
513
			/* get source port */
514
			if (!isset($rule['protocol']) || in_array($rule['protocol'], array("tcp","udp"))) {
515
				if ($rule['source']['port']) {
516
					/*
517
					* Check to see if port is a alias.  If so grab it and
518
					* enclose it in { } to pass to pf.
519
					*
520
					* Otherwise combine the portrange into one if its only
521
					* one item.
522
					*/
523
					$src = alias_expand($rule['source']['port']);
524
					if($src <> "") {
525
						$line .= "port {$src}";
526
					} else {
527
						$srcport = explode("-", $rule['source']['port']);
528
						if ((!$srcport[1]) || ($srcport[0] == $srcport[1])) {
529
							$line .= "port {$srcport[0]} ";
530
						} else {
531
							$line .= "port {$srcport[0]}:{$srcport[1]} ";
532
						}
533
					}
534
				}
535
			}
536

    
537
			/* destination address */
538
			if (isset($rule['destination']['any'])) {
539
				$dst = "any";
540
			} else if ($rule['destination']['network']) {
541
				if (stristr($rule['destination']['network'], "opt") == true) {
542
					$dst = $optcfg[$rule['destination']['network']]['sa'] . "/" .
543
						$optcfg[$rule['destination']['network']]['sn'];
544
				} else {
545
					switch ($rule['destination']['network']) {
546
						case 'wanip':
547
							$dst = $wanip;
548
							break;
549
						case 'lanip':
550
							$dst = $lanip;
551
							break;
552
						case 'lan':
553
							$dst = "$lansa/$lansn";
554
							break;
555
						case 'pptp':
556
							$dst = "$pptpsa/$pptpsn";
557
							break;
558
						case 'pppoe':
559
							$dst = "$pppoesa/$pppoesn";
560
							break;
561
					}
562
				}
563
			} else if ($rule['destination']['address']) {
564
				$dst = alias_expand($rule['destination']['address']);
565
				if(!$dst)
566
					$dst = $rule['destination']['address'];
567
			}
568

    
569
			if (!$dst) {
570
				printf("No destination address found in rule {$i}. \n");
571
				print_r($rule['destination']['network']);
572
				break;
573
			}
574

    
575
			if (isset($rule['destination']['not'])) {
576

    
577
				/*pf is not really happy with this sexy ($dst = "!{$dst}";) approach of
578
				 * negating a list or macro. So we have to write out a ! on each entry.
579
				 */
580

    
581
				/* not very happy with this! but it beats copying this section to
582
				 * several places.
583
				 */
584
				$alias = alias_expand(substr($dst, 1));
585

    
586
				if(isset($alias) && stristr($alias, "$")) {
587
					$alias = alias_expand_value(substr($dst, 1));
588
					$dst = "{";
589
					foreach(preg_split("/[\s]+/", $alias) as $item) {
590
						if($item != "") {
591
							$dst .= " !{$item}";
592
						}
593
					}
594
					$dst .= " }";
595
				}
596
				else {
597
					$dst = "!{$dst}";
598
				}
599
			}
600
			$line .= " to {$dst} ";
601

    
602
			if (!isset($rule['protocol']) || in_array($rule['protocol'], array("tcp","udp"))) {
603
				if ($rule['destination']['port']) {
604
					$dstport = alias_expand($rule['destination']['port']);
605
					/*
606
					* Check to see if port is a alias.  If so grab it and
607
					* enclose it in { } to pass to pf.
608
					*
609
					* Otherwise combine the portrange into one if its only
610
					* one item.
611
					*/
612
					if($dstport <> "") {
613
						$line .= "port {$dstport}";
614
					} else {
615
						$dstport = explode("-", $rule['destination']['port']);
616
						if ((!$dstport[1]) || ($dstport[0] == $dstport[1])) {
617
							$dstport = $dstport[0];
618
							$line .= "port {$dstport} ";
619
						} else {
620
							$dstport = "{$dstport[0]}:{$dstport[1]}";
621
							$line .= "port {$dstport} ";
622
						}
623
					}
624
				}
625
			}
626

    
627
			if ($rule['iptos'])
628
				$line .= "tos {$rule['iptos']} ";
629

    
630
			$inflags = explode(",", $rule['tcpflags']);
631
			$flags = " flags ";
632
			foreach ($tcpflags as $tcpflag) {
633
				if (array_search($tcpflag, $inflags) !== false) {
634
					$flags .= strtoupper(substr($tcpflag, 0, 1));
635
				}
636
			}
637
			if($flags <> " flags ")
638
			$line .= "{$flags}/SAFRPU ";
639

    
640
			$qtag = "{$direction}queue";
641
			$line .= " keep state tagged unshaped tag {$rule[$qtag]} ";
642

    
643
			$line .= "\n";
644
			$shaperrules .= $line;
645

    
646
			/* setup the outbound queue on the other interface */
647
			$direction = 'out';
648
			$qouttag = "{$direction}queue";
649

    
650
			$friendly_desc = convert_friendly_interface_to_friendly_descr($rule['out-interface']);
651
			$shaperrules .= "pass out on \${$friendly_desc}";
652

    
653
			if(isset($proto) && $proto != "") {
654
				$shaperrules .= " proto {$proto}";
655
			}
656
			$shaperrules .= " from any to {$dst}";
657
			if(isset($dstport) && $dstport != "") {
658
				$shaperrules .= " port {$dstport}";
659
			}
660
			if ($rule['iptos']) {
661
				$shaperrules .= " tos {$rule['iptos']}";
662
			}
663
			if($flags <> " flags ") {
664
				$shaperrules .= "{$flags}/SAFRPU";
665
			}
666

    
667
			$shaperrules .= " keep state tagged {$rule[$qtag]} tag {$rule[$qouttag]}\n";
668

    
669
			unset($src);
670
			unset($dst);
671
			unset($srcport);
672
			unset($dstport);
673
		}
674

    
675
		$i++;
676
	}
677
	return $shaperrules;
678
}
679

    
680
?>
(19-19/27)