Project

General

Profile

Download (16.5 KB) Statistics
| Branch: | Tag: | Revision:
1 061f78b1 Bill Marquette
<?php
2
/* $Id$ */
3
/*
4 afb5973f Bill Marquette
	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 061f78b1 Bill Marquette
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
38
function find_default_queue($interface) {
39
	global $config, $queue_cache;
40
	$qconfig = $config;
41
42
	/* quick return if we've already seen the default queue for this interface */
43
	if (isset($queue_cache['defq'][$interface]))
44
	return $queue_cache['defq'][$interface];
45
46
47
	if (is_array($qconfig['shaper']['queue'])) {
48
		foreach ($qconfig['shaper']['queue'] as $queue) {
49
			if(isset($queue['defaultqueue']) and ($queue['defaultqueue'] <> "")) {
50
				/* If this is a child queue */
51
				if(isset($queue['attachtoqueue'])) {
52
					if(is_subqueue_used_on_interface($queue['attachtoqueue'], $interface)) {
53
						$queue_cache['defq'][$interface] = $queue['name'];
54
						return $queue['name'];
55
					}
56
				} else {
57
					$queue_cache['defq'][$interface] = $queue['name'];
58
					return $queue['name'];
59
				}
60
			}
61
		}
62
	}
63
64
	/* unreachable */
65
	return null;
66
}
67
68
69
function get_ack_queue($interface) {
70
	global $config, $queue_cache;
71
72
	/* quick return if we've already seen the ack queue for this interface */
73
	if (isset($queue_cache['ackq'][$interface]))
74
	return $queue_cache['ackq'][$interface];
75
76
	$qconfig = $config;
77
78
	if (is_array($qconfig['shaper']['queue'])) {
79
		foreach ($qconfig['shaper']['queue'] as $queue) {
80
			if(isset($queue['ack']))
81
			if(isset($queue['attachtoqueue']))
82
			if(is_subqueue_used_on_interface($queue['attachtoqueue'], $interface)) {
83
				/* Add to cache */
84
				$queue_cache['ackq'][$interface] = $queue['name'];
85
				return $queue['name'];
86
			}
87
		}
88
	}
89
	/* unreachable */
90
	return null;
91
}
92
93
94
function filter_generate_altq_queues($altq_ints) {
95
	global $config;
96
	$altq_rules = "";
97
	if (is_array($config['shaper']['queue'])) {
98
		foreach ($config['shaper']['queue'] as $rule) {
99
			$options = "";
100
			// check to make sure we're actually using this queue.
101
			//if(stristr($altq_ints, $rule['name']) !== FALSE) {
102
			$altq_rules .= "queue {$rule['name']} ";
103
			if (isset($rule['bandwidth']) and $rule['bandwidth'] <> "")
104
			$altq_rules .= "bandwidth {$rule['bandwidth']}{$rule['bandwidthtype']} ";
105
			if (isset($rule['priority']) and $rule['priority'] <> "")
106
			$altq_rules .= "priority {$rule['priority']} ";
107
			if(isset($rule['red']) and $rule['red'] <> "")
108
			$options .= " red";
109
			if(isset($rule['borrow']) and $rule['borrow'] <> "")
110
			$options .= " borrow";
111
			if(isset($rule['ecn']) and $rule['ecn'] <> "")
112
			$options .= " ecn";
113
			if(isset($rule['rio']) and $rule['rio'] <> "")
114
			$options .= " rio";
115
			if(isset($rule['defaultqueue']) and $rule['defaultqueue'] <> "")
116
			$options .= " default";
117
			if(isset($rule['upperlimit']) and $rule['upperlimit'] <> "") {
118
				$options .= " upperlimit({$rule['upperlimit1']} {$rule['upperlimit2']} {$rule['upperlimit3']})";
119
			}
120
			if(isset($rule['linkshare']) and $rule['linkshare'] <> "") {
121
				$options .= " linkshare({$rule['linkshare1']} {$rule['linkshare2']} {$rule['linkshare3']})";
122
			}
123
			if(isset($rule['realtime']) and $rule['realtime'] <> "") {
124
				$options .= " realtime({$rule['realtime1']} {$rule['realtime2']} {$rule['realtime3']})";
125
			}
126 7bdf53c9 Scott Ullrich
			$scheduler_type = $config['shaper']['schedulertype'];
127 4f9a3d3b Scott Ullrich
			$altq_rules .= "{$scheduler_type} ";
128 061f78b1 Bill Marquette
			if($options)
129
				$altq_rules .= "( {$options} )";
130
			$fsq="";
131
			foreach($config['shaper']['queue'] as $q) {
132
				if($q['attachtoqueue'] == $rule['name']) {
133
					if($fsq == "") {
134
						$altq_rules .= "{ ";
135
					}
136
					else if($fsq == "1") {
137
						$altq_rules .= ", ";
138
					}
139
					$altq_rules .= $q['name'];
140
					$fsq = "1";
141
				}
142
			}
143
			if($fsq == "1")
144
			$altq_rules .= " }";
145
			$altq_rules .= "\n";
146
			//}
147
		}
148
	}
149
	return $altq_rules;
150
}
151
152
/* Find a queue that's attached to this one and see if that queue is used on this interface */
153
function is_subqueue_used_on_interface($queuename, $interface) {
154
	global $config;
155
	$qconfig = $config;
156
	if (!is_array($qconfig['shaper']['queue'])) return 0;
157
158
	foreach ($qconfig['shaper']['queue'] as $queue) {
159 b7ff5e40 Scott Ullrich
		if($queue['attachtoqueue'] == $queuename) {
160
			/* recurse if we're a parent queue */
161
			if ($queue['parentqueue'] == "on") {
162
				return is_subqueue_used_on_interface($queue['name'], $interface);
163
			}
164
165
			/* If we're not a parent check to see if the queue is used on this interface */
166
			$subqueue_interface = filter_is_queue_being_used_on_interface($queue['name'], $interface);
167
			if ($subqueue_interface != ""){
168
				return 1;
169
			}
170 061f78b1 Bill Marquette
		}
171
	}
172
	return 0;
173
}
174
175
176
function filter_is_queue_being_used_on_interface($queuename, $interface) {
177
	global $config;
178
	$lconfig = $config;
179
180
	if(!is_array($lconfig['shaper']['rule'])) return null;
181
	foreach($lconfig['shaper']['rule'] as $rule) {
182
		if(($rule['inqueue'] == $queuename && $rule['interface'] == $interface))
183
		return $interface;
184
	}
185
	return null;
186
}
187
188
189
function filter_setup_altq_interfaces() {
190
	global $config;
191
	$altq_rules  = "";
192
	$queue_names = "";
193
	$is_first = "";
194
195
	if(!is_array($config['shaper']['queue'])) return null;
196
197
	$ifdescrs = array('wan', 'lan');
198
	for ($j = 1; isset($config['interfaces']['opt' . $j]); $j++) {
199
		$ifdescrs[] = "opt" . $j;
200
	}
201
	foreach ($ifdescrs as $ifdescr => $ifname) {
202
203
		$queue_names = "";
204
		$is_first = "";
205
206 b7ff5e40 Scott Ullrich
		$queue_names = find_root_queue($ifname);
207 061f78b1 Bill Marquette
208
		if($queue_names <> ""){
209
			$altq_rules .= "altq on {$config['interfaces'][$ifname]['if']} ";
210 95904542 Scott Ullrich
			/* Default bandwidth to 10Mbit - this handles virtual interfaces (vlan) that have
211
			 * no bandwidth associated with them.
212
			 */
213
			$bandwidth = "bandwidth 10Mb";
214 7bdf53c9 Scott Ullrich
			if($config['interfaces'][$ifname]['bandwidth'] <> "" and $config['interfaces'][$ifname]['bandwidthtype'] <> "" )
215 061f78b1 Bill Marquette
				$bandwidth = "bandwidth {$config['interfaces'][$ifname]['bandwidth']}{$config['interfaces'][$ifname]['bandwidthtype']}";	
216
			$altq_rules .= "{$config['shaper']['schedulertype']} {$bandwidth} ";
217
			$altq_rules .= "queue { {$queue_names} }";
218
		}
219
		$altq_rules .= "\n";
220
221
	}
222
	return $altq_rules;
223
}
224
225 b7ff5e40 Scott Ullrich
/* Find the root queue for an interface */
226
function find_root_queue($ifname) {
227
	global $config;
228
229
	$dbg = fopen("/tmp/debug", 'a');
230
	foreach ($config['shaper']['queue'] as $queue) {
231
		$rule_interface = "";
232
		$q = $queue;
233
		fwrite($dbg, "interface: {$ifname}\n");
234
		fwrite($dbg, "queue: {$q['name']} parent: {$q['parentqueue']} attached: {$q['attachtoqueue']}\n");
235
		/* if we're a parentqueue and aren't attached to another queue we're probably a root */
236
		if ((isset($q['parentqueue']) && $q['parentqueue'] <> "") && (!isset($q['attachtoqueue']) || $q['attachtoqueue'] == "")) {
237
			fwrite($dbg, "queue: {$q['name']} is a parent\n");
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
				fwrite($dbg, "queue: {$q['name']} is a parent on {$ifname}\n");
242
				$queue_names .= " ";
243
				$queue_names .= $q['name'];
244
			}
245
		}
246
	}
247
	fclose($dbg);
248
	return $queue_names;
249
}
250
251
	
252
253 061f78b1 Bill Marquette
254
function is_queue_attached_children($name) {
255
	global $config;
256
	if (!is_array($config['shaper']['queue'])) return 0;
257
	foreach ($config['shaper']['queue'] as $queue) {
258
		if($queue['attachtoqueue'] == $name) return 1;
259
	}
260
	return 0;
261
}
262
263
264
function queue_interface_recursive($queuename) {
265
	global $config;
266
	foreach($config['shaper']['queue'] as $queue) {
267
		if($queue['attachtoqueue'] == $queuename) {
268
			$status = queue_interface_recursive($queue['name']);
269
			if($status <> "") return $status;
270
		}
271
		foreach($config['shaper']['rule'] as $rule) {
272
			if($rule['inqueue'] == $queuename)
273
			return $rule['interface'];
274
		}
275
	}
276
277
	/* unreachable */
278
	return null;
279
}
280
281
282
function is_subqueue($name) {
283
	global $config;
284
	$queues = $config['shaper']['queue']; /* must assign to keep from corrupting in memory $config */
285
	if (!is_array($queues)) return 0;
286
	foreach ($queues as $queue) {
287
		if($queue['attachtoqueue'] == $name) return 1;
288
	}
289
	return 0;
290
}
291
292
293
function filter_altq_get_queuename($queuenum) {
294
	global $config;
295
	$x=0;
296
	foreach($config['shaper']['queue'] as $rule) {
297
		if($x == $queuenum)
298
		return $rule['name'];
299
		$x++;
300
	}
301
	/* unreachable */
302
	return null;
303
}
304
305
306
function filter_generate_pf_altq_rules() {
307
	/* I don't think we're in IPFW anymore Toto */
308
309
	global $config, $g, $tcpflags;
310
311
	$lancfg = $config['interfaces']['lan'];
312
	$pptpdcfg = $config['pptpd'];
313
	$pppoecfg = $config['pppoe'];
314
315
	$lanif = $lancfg['if'];
316
	$wanif = get_real_wan_interface();
317
318
	$lansa = gen_subnet($lancfg['ipaddr'], $lancfg['subnet']);
319
	$lansn = $lancfg['subnet'];
320
321
	/* optional interfaces */
322
	$optcfg = array();
323
	generate_optcfg_array($optcfg);
324
325
	if ($pptpdcfg['mode'] == "server") {
326
		$pptpsa = $pptpdcfg['remoteip'];
327
		$pptpsn = $g['pptp_subnet'];
328
		if($config['pptp']['pptp_subnet'] <> "")
329
		$pptpsn = $config['pptp']['pptp_subnet'];
330
	}
331
332
	if ($pppoecfg['mode'] == "server") {
333
		$pppoesa = $pppoecfg['remoteip'];
334
		$pppoesn = $g['pppoe_subnet'];
335
		if($config['pppoe']['pppoe_subnet'] <> "")
336
		$pppoesn = $config['pppoe']['pppoe_subnet'];
337
	}
338
339
	/* generate rules */
340
	if (isset($config['shaper']['rule']))
341
	foreach ($config['shaper']['rule'] as $rule) {
342
343
		/* don't include disabled rules */
344
		if (isset($rule['disabled'])) {
345
			$i++;
346
			continue;
347
		}
348
349
		switch($rule['interface']) {
350
			case "pptp": /* does the rule deal with a PPTP interface? */
351
				if ($pptpdcfg['mode'] != "server") {
352
					if (($rule['source']['network'] == "pptp") ||
353
					($rule['destination']['network'] == "pptp")) {
354
						$i++;
355
						continue;
356
					}
357
				}
358
359
				$nif = $g['n_pptp_units'];
360
				if($config['pptp']['n_pptp_units'] <> "")
361
					$nif = $config['pptp']['n_pptp_units'];
362
363
				$ispptp = true;
364
				break;
365
366
			case "pppoe": /* does the rule deal with a PPPOE interface? */
367
				if ($pppoecfg['mode'] != "server") {
368
					if (($rule['source']['network'] == "pppoe") ||
369
					($rule['destination']['network'] == "pppoe")) {
370
						$i++;
371
						continue;
372
					}
373
				}
374
375
				$nif = $g['n_pppoe_units'];
376
				if($config['pppoe']['n_pppoe_units'] <> "")
377
					$nif = $config['pppoe']['n_pppoe_units'];
378
379
				$ispppoe = true;
380
				break;
381
			default:
382
				if (strstr($rule['interface'], "opt")) {
383
					if (!array_key_exists($rule['interface'], $optcfg)) {
384
						$i++;
385
						continue;
386
					}
387
				}
388
				$nif = 1;
389
				$ispptp = false;
390
				$ispppoe = false;
391
		}
392
393
394
395
		if (strstr($rule['source']['network'], "opt")) {
396
			if (!array_key_exists($rule['source']['network'], $optcfg)) {
397
				$i++;
398
				continue;
399
			}
400
		}
401
		if (strstr($rule['destination']['network'], "opt")) {
402
			if (!array_key_exists($rule['destination']['network'], $optcfg)) {
403
				$i++;
404
				continue;
405
			}
406
		}
407
408
		/* check for unresolvable aliases */
409
		if ($rule['source']['address'] && !alias_expand($rule['source']['address'])) {
410
			$i++;
411
			continue;
412
		}
413
		if ($rule['destination']['address'] && !alias_expand($rule['destination']['address'])) {
414
			$i++;
415
			continue;
416
		}
417
418
		for ($iif = 0; $iif < $nif; $iif++) {
419
			foreach ( array('in', 'out') as $direction) {
420
421
				$line = "pass {$direction} on ";
422
423
				if ($ispptp) {
424
					$line .= " ng" . ($iif+1);
425
				} else {
426
					if($ispppoe) {
427
						$line .= " ng" . ($iif+1);
428
					} else {
429
						$if = $config['interfaces'][$rule['interface']]['if'];
430
					}
431
432
					if ($rule['interface'] == "wan") {
433
						if($direction=="in") {
434
							$if = $wanif;
435
						} else {
436
							$if = $lanif;
437
						}
438
					} else {
439
						if($rule['interface'] == "lan") {
440
							if($direction=="in") {
441
								$if = $lanif;
442
							} else {
443
								$if = $wanif;
444
							}
445
						}
446
					}
447
448
					$line .= " {$if} ";
449
				}
450
451
				if (isset($rule['protocol'])) {
452
					$line .= "proto {$rule['protocol']} ";
453
				}
454
455
				/* source address */
456 7bdf53c9 Scott Ullrich
				/* Using any for source on 'out' is due to not knowing what
457
				 * the packet looks like after NAT occurs
458
				 */
459 061f78b1 Bill Marquette
				if (isset($rule['source']['any']) || $direction == "out") {
460
					$src = "any";
461
				} else if ($rule['source']['network']) {
462
					if (strstr($rule['source']['network'], "opt")) {
463
						$src = $optcfg[$rule['source']['network']]['sa'] . "/" .
464
						$optcfg[$rule['source']['network']]['sn'];
465
					} else {
466
						switch ($rule['source']['network']) {
467
							case 'lan':
468
							$src = "$lansa/$lansn";
469
							break;
470
							case 'pptp':
471
							$src = "$pptpsa/$pptpsn";
472
							break;
473
							case 'pppoe':
474
							$src = "$pppoesa/$pppoesn";
475
							break;
476
						}
477
					}
478
				} else if ($rule['source']['address']) {
479 4bae67f6 Scott Ullrich
					$src = alias_expand($rule['source']['address']);
480
					if(!$src)
481
						$src = $rule['source']['address'];
482 061f78b1 Bill Marquette
				}
483
484
				if (!$src) {
485
					printf("No source address found in rule $i\n");
486
					break;
487
				}
488
489
				if (isset($rule['source']['not'])) {
490
					$line .= "from ! $src ";
491
				} else {
492
					$line .= "from $src ";
493
				}
494
495
				if (!isset($rule['protocol']) || in_array($rule['protocol'], array("tcp","udp"))) {
496
					if ($rule['source']['port']) {
497
						/*
498
						* Check to see if port is a alias.  If so grab it and
499
						* enclose it in { } to pass to pf.
500
						*
501
						* Otherwise combine the portrange into one if its only
502
						* one item.
503
						*/
504
						$src = alias_expand($rule['source']['port']);
505
						if($src <> "") {
506
							$line .= "port {$rule['destination']['port']}";
507
						} else {
508
							$srcport = explode("-", $rule['source']['port']);
509
							if ((!$srcport[1]) || ($srcport[0] == $srcport[1])) {
510
								$line .= "port {$srcport[0]} ";
511
							} else {
512
								$line .= "port {$srcport[0]}:{$srcport[1]} ";
513
							}
514
						}
515
					}
516
				}
517
518
				/* destination address */
519
				if (isset($rule['destination']['any'])) {
520
					$dst = "any";
521
				} else if ($rule['destination']['network']) {
522
523
					if (strstr($rule['destination']['network'], "opt")) {
524
						$dst = $optcfg[$rule['destination']['network']]['sa'] . "/" .
525
						$optcfg[$rule['destination']['network']]['sn'];
526
					} else {
527
						switch ($rule['destination']['network']) {
528
							case 'lan':
529
							$dst = "$lansa/$lansn";
530
							break;
531
							case 'pptp':
532
							$dst = "$pptpsa/$pptpsn";
533
							break;
534
							case 'pppoe':
535
							$dst = "$pppoesa/$pppoesn";
536
							break;
537
						}
538
					}
539
				} else if ($rule['destination']['address']) {
540 4bae67f6 Scott Ullrich
					$dst = alias_expand($rule['destination']['address']);
541
					if(!$dst)
542
						$dst = $rule['destination']['address'];
543 061f78b1 Bill Marquette
				}
544
545
				if (!$dst) {
546
					printf("No destination address found in rule $i\n");
547
					break;
548
				}
549
550
				if (isset($rule['destination']['not'])) {
551
					$line .= "to ! $dst ";
552
				} else {
553
					$line .= "to $dst ";
554
				}
555
556
				if (!isset($rule['protocol']) || in_array($rule['protocol'], array("tcp","udp"))) {
557
					if ($rule['destination']['port']) {
558
						$dst = alias_expand($rule['destination']['port']);
559
						/*
560
						* Check to see if port is a alias.  If so grab it and
561
						* enclose it in { } to pass to pf.
562
						*
563
						* Otherwise combine the portrange into one if its only
564
						* one item.
565
						*/
566
						if($dst <> "") {
567
							$line .= "port {$rule['destination']['port']}";
568
						} else {
569
							$dstport = explode("-", $rule['destination']['port']);
570
							if ((!$dstport[1]) || ($dstport[0] == $dstport[1])) {
571
								$line .= "port {$dstport[0]} ";
572
							} else {
573
								$line .= "port {$dstport[0]}:{$dstport[1]} ";
574
							}
575
						}
576
					}
577
				}
578
579
				if ($rule['iptos'])
580
				$line .= "tos {$rule['iptos']} ";
581
582
				$inflags = explode(",", $rule['tcpflags']);
583
				$flags = " flags ";
584
				foreach ($tcpflags as $tcpflag) {
585
					if (array_search($tcpflag, $inflags) !== false) {
586
						$flags .= strtoupper(substr($tcpflag, 0, 1));
587
					}
588
				}
589
				if($flags <> " flags ")
590
				$line .= "{$flags}/SAFRPU ";
591
592
				$qtag = "{$direction}queue";
593 3cd71852 Scott Ullrich
				$line .= " keep state tagged unshaped tag {$rule[$qtag]} ";
594 061f78b1 Bill Marquette
595
				$line .= "\n";
596
				$shaperrules .= $line;
597
			}
598
		}
599
600
		$i++;
601
	}
602
603
	return $shaperrules;
604
}
605
606
?>