Project

General

Profile

Download (18.3 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/* $Id$ */
3
/*
4
	shaper.inc
5
	Copyright (C) 2004 Scott Ullrich
6
	Copyright (C) 2005 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) {
184
	global $config;
185
	$lconfig = $config;
186

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

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

    
201
	if(!is_array($config['shaper']['queue'])) return null;
202

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

    
209
		$queue_names = "";
210
		$is_first = "";
211

    
212
		$queue_names = find_root_queue($ifname);
213

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

    
223
	}
224
	return $altq_rules;
225
}
226

    
227

    
228

    
229
/* Find the root queue for an interface */
230
function find_root_queue($ifname) {
231
	global $config;
232

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

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

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

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

    
284
	/* unreachable */
285
	return null;
286
}
287

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

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

    
310
function filter_generate_pf_altq_rules() {
311
	/* I don't think we're in IPFW anymore Toto */
312
	$i = 0;
313
	
314
	global $config, $g, $tcpflags;
315

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

    
320
	$lanif = $lancfg['if'];
321
	$wanif = get_real_wan_interface();
322

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

    
326
	/* optional interfaces */
327
	$optcfg = array();
328
	generate_optcfg_array($optcfg);
329

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

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

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

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

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

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

    
370
				$ispptp = true;
371
				break;
372

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

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

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

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

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

    
423
		for ($iif = 0; $iif < $nif; $iif++) {
424
			$direction = 'in';
425
			$line = "pass {$direction} on ";
426

    
427
			if ($ispptp) {
428
				$line .= " ng" . ($iif+1);
429
			} 
430
			else if($ispppoe) {
431
				$line .= " ng" . ($iif+1);
432
			}			
433
			else {
434
				$friendly_desc = convert_friendly_interface_to_friendly_descr($rule['in-interface']);
435
				$line .= " \${$friendly_desc} ";
436
			}
437
			
438
			/* get protocol */
439
			$proto = $rule['protocol'];
440
			if (isset($proto)) {
441
				$line .= "proto {$proto} ";
442
			}
443

    
444
			/* get source address */
445
			if (isset($rule['source']['any'])) {
446
				$src = "any";
447
			} else if ($rule['source']['network']) {
448
				if (stristr($rule['source']['network'], "opt") == true) {
449
					$src = $optcfg[$rule['source']['network']]['sa'] . "/" .
450
						$optcfg[$rule['source']['network']]['sn'];
451
				} else {
452
					switch ($rule['source']['network']) {
453
						case 'lan':
454
							$src = "$lansa/$lansn";
455
							break;
456
						case 'pptp':
457
							$src = "$pptpsa/$pptpsn";
458
							break;
459
						case 'pppoe':
460
							$src = "$pppoesa/$pppoesn";
461
							break;
462
					}
463
				}
464
			} else if ($rule['source']['address']) {
465
				$src = alias_expand($rule['source']['address']);
466
				if(!$src)
467
					$src = $rule['source']['address'];
468
			}
469

    
470
			if (!$src) {
471
				printf("No source address found in rule $i\n");
472
				break;
473
			}
474

    
475
			if (isset($rule['source']['not'])) {
476
				
477
				/*pf is not really happy with this sexy ($src = "!{$src}";) approach of 
478
				 * negating a list or macro. So we have to write out a ! on each entry.
479
				 */				
480

    
481
				/* not very happy with this! but it beats copying this section to 
482
				 * several places.
483
				 */
484
				$alias = alias_expand(substr($src, 1));
485
				
486
				if(isset($alias) && stristr($alias, "$")) {
487
					$alias = alias_expand_value(substr($src, 1));
488
					$src = "{";
489
					foreach(preg_split("/[\s]+/", $alias) as $item) {
490
						if($item != "") {
491
							$src .= " !{$item}";
492
						}
493
					}
494
					$src .= " }";
495
				}
496
				else {
497
					$src = "!{$src}";
498
				}
499
			}
500
			$line .= "from {$src} ";
501
			
502
			/* get source port */
503
			if (!isset($rule['protocol']) || in_array($rule['protocol'], array("tcp","udp"))) {
504
				if ($rule['source']['port']) {
505
					/*
506
					* Check to see if port is a alias.  If so grab it and
507
					* enclose it in { } to pass to pf.
508
					*
509
					* Otherwise combine the portrange into one if its only
510
					* one item.
511
					*/
512
					$src = alias_expand($rule['source']['port']);
513
					if($src <> "") {
514
						$line .= "port {$src}";
515
					} else {
516
						$srcport = explode("-", $rule['source']['port']);
517
						if ((!$srcport[1]) || ($srcport[0] == $srcport[1])) {
518
							$line .= "port {$srcport[0]} ";
519
						} else {
520
							$line .= "port {$srcport[0]}:{$srcport[1]} ";
521
						}
522
					}
523
				}
524
			}
525

    
526
			/* destination address */
527
			if (isset($rule['destination']['any'])) {
528
				$dst = "any";
529
			} else if ($rule['destination']['network']) {
530
				if (stristr($rule['destination']['network'], "opt") == true) {
531
					$dst = $optcfg[$rule['destination']['network']]['sa'] . "/" .
532
						$optcfg[$rule['destination']['network']]['sn'];
533
				} else {
534
					switch ($rule['destination']['network']) {
535
						case 'lan':
536
							$dst = "$lansa/$lansn";
537
							break;
538
						case 'pptp':
539
							$dst = "$pptpsa/$pptpsn";
540
							break;
541
						case 'pppoe':
542
							$dst = "$pppoesa/$pppoesn";
543
							break;
544
					}
545
				}
546
			} else if ($rule['destination']['address']) {
547
				$dst = alias_expand($rule['destination']['address']);
548
				if(!$dst)
549
					$dst = $rule['destination']['address'];
550
			}
551

    
552
			if (!$dst) {
553
				printf("No destination address found in rule {$i}. \n");
554
				print_r($rule['destination']['network']);
555
				break;
556
			}
557

    
558
			if (isset($rule['destination']['not'])) {
559
				
560
				/*pf is not really happy with this sexy ($dst = "!{$dst}";) approach of 
561
				 * negating a list or macro. So we have to write out a ! on each entry.
562
				 */				
563

    
564
				/* not very happy with this! but it beats copying this section to 
565
				 * several places.
566
				 */
567
				$alias = alias_expand(substr($dst, 1));
568
				
569
				if(isset($alias) && stristr($alias, "$")) {
570
					$alias = alias_expand_value(substr($dst, 1));
571
					$dst = "{";
572
					foreach(preg_split("/[\s]+/", $alias) as $item) {
573
						if($item != "") {
574
							$dst .= " !{$item}";
575
						}
576
					}
577
					$dst .= " }";
578
				}
579
				else {
580
					$dst = "!{$dst}";
581
				}
582
			}
583
			$line .= "to {$dst} ";
584

    
585
			if (!isset($rule['protocol']) || in_array($rule['protocol'], array("tcp","udp"))) {
586
				if ($rule['destination']['port']) {
587
					$dstport = alias_expand($rule['destination']['port']);
588
					/*
589
					* Check to see if port is a alias.  If so grab it and
590
					* enclose it in { } to pass to pf.
591
					*
592
					* Otherwise combine the portrange into one if its only
593
					* one item.
594
					*/
595
					if($dstport <> "") {
596
						$line .= "port {$dstport}";
597
					} else {
598
						$dstport = explode("-", $rule['destination']['port']);
599
						if ((!$dstport[1]) || ($dstport[0] == $dstport[1])) {
600
							$dstport = $dstport[0];
601
							$line .= "port {$dstport} ";
602
						} else {
603
							$dstport = "{$dstport[0]}:{$dstport[1]}";
604
							$line .= "port {$dstport} ";
605
						}
606
					}
607
				}
608
			}
609

    
610
			if ($rule['iptos'])
611
				$line .= "tos {$rule['iptos']} ";
612

    
613
			$inflags = explode(",", $rule['tcpflags']);
614
			$flags = " flags ";
615
			foreach ($tcpflags as $tcpflag) {
616
				if (array_search($tcpflag, $inflags) !== false) {
617
					$flags .= strtoupper(substr($tcpflag, 0, 1));
618
				}
619
			}
620
			if($flags <> " flags ")
621
			$line .= "{$flags}/SAFRPU ";
622

    
623
			$qtag = "{$direction}queue";
624
			$line .= " keep state tagged unshaped tag {$rule[$qtag]} ";
625

    
626
			$line .= "\n";
627
			$shaperrules .= $line;
628
			
629
			/* setup the outbound queue on the other interface */
630
			$direction = 'out';
631
			$qouttag = "{$direction}queue";
632
			
633
			$friendly_desc = convert_friendly_interface_to_friendly_descr($rule['out-interface']);
634
			$shaperrules .= "pass out on \${$friendly_desc}";
635
			
636
			if(isset($proto) && $proto != "") {
637
				$shaperrules .= " proto {$proto}";
638
			}
639
			$shaperrules .= " from any to {$dst}";
640
			if(isset($dstport) && $dstport != "") {
641
				$shaperrules .= " port {$dstport}";
642
			}
643
			if ($rule['iptos']) {
644
				$shaperrules .= " tos {$rule['iptos']}";
645
			}
646
			if($flags <> " flags ") {
647
				$shaperrules .= "{$flags}/SAFRPU";
648
			}
649
			
650
			$shaperrules .= " keep state tagged {$rule[$qtag]} tag {$rule[$qouttag]}\n";
651
			
652
			unset($src);
653
			unset($dst);
654
			unset($srcport);
655
			unset($dstport);
656
		}
657

    
658
		$i++;
659
	}
660
	return $shaperrules;
661
}
662

    
663
?>
(19-19/27)