Project

General

Profile

Download (16.5 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

    
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
			$scheduler_type = $config['shaper']['schedulertype'];
127
			$altq_rules .= "{$scheduler_type} ";
128
			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
		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
		}
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
		$queue_names = find_root_queue($ifname);
207

    
208
		if($queue_names <> ""){
209
			$altq_rules .= "altq on {$config['interfaces'][$ifname]['if']} ";
210
			/* Default bandwidth to 10Mbit - this handles virtual interfaces (vlan) that have
211
			 * no bandwidth associated with them.
212
			 */
213
			$bandwidth = "bandwidth 10Mb";
214
			if($config['interfaces'][$ifname]['bandwidth'] <> "" and $config['interfaces'][$ifname]['bandwidthtype'] <> "" )
215
				$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
/* 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

    
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
				/* Using any for source on 'out' is due to not knowing what
457
				 * the packet looks like after NAT occurs
458
				 */
459
				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
					$src = alias_expand($rule['source']['address']);
480
					if(!$src)
481
						$src = $rule['source']['address'];
482
				}
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
					$dst = alias_expand($rule['destination']['address']);
541
					if(!$dst)
542
						$dst = $rule['destination']['address'];
543
				}
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
				$line .= " keep state tagged unshaped tag {$rule[$qtag]} ";
594

    
595
				$line .= "\n";
596
				$shaperrules .= $line;
597
			}
598
		}
599

    
600
		$i++;
601
	}
602

    
603
	return $shaperrules;
604
}
605

    
606
?>
(18-18/26)