Project

General

Profile

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

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

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

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

    
182
function filter_is_queue_being_used_on_interface($queuename, $interface) {
183
	global $config;
184
	$lconfig = $config;
185

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

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

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

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

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

    
211
		$queue_names = find_root_queue($ifname);
212

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

    
222
	}
223
	return $altq_rules;
224
}
225

    
226

    
227

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

    
232
	foreach ($config['shaper']['queue'] as $queue) {
233
		$rule_interface = "";
234
		$q = $queue;
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
			/* Confirm that this is a valid queue for this interface */
238
			$rule_interface = is_subqueue_used_on_interface($q['name'], $ifname);
239
			if ($rule_interface == 1) {
240
				if (strlen($queue_names) > 0)
241
					$queue_names .= " ";
242
				$queue_names .= $q['name'];
243
			}
244
		}
245
	}
246
	return $queue_names;
247
}
248

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
363
				$nif = $g['n_pptp_units'];
364
				if($config['pptp']['n_pptp_units'] <> "")
365
					$nif = $config['pptp']['n_pptp_units'];
366

    
367
				$ispptp = true;
368
				break;
369

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

    
379
				$nif = $g['n_pppoe_units'];
380
				if($config['pppoe']['n_pppoe_units'] <> "")
381
					$nif = $config['pppoe']['n_pppoe_units'];
382

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

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

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

    
420
		for ($iif = 0; $iif < $nif; $iif++) {
421
			foreach ( array('in', 'out') as $direction) {
422

    
423
				$line = "pass {$direction} on ";
424

    
425
				if ($ispptp) {
426
					$line .= " ng" . ($iif+1);
427
				} else {
428
					if($ispppoe) {
429
						$line .= " ng" . ($iif+1);
430
					} else {
431
						$if = $config['interfaces'][$rule['in-interface']]['if'];
432
					}
433

    
434
					if ($rule['in-interface'] == "wan") {
435
						if($direction=="in") {
436
							$if = $wanif;
437
						} else {
438
							$if = $lanif;
439
						}
440
					} else {
441
						if($rule['in-interface'] == "lan") {
442
							if($direction=="in") {
443
								$if = $lanif;
444
							} else {
445
								$if = $wanif;
446
							}
447
						}
448
					}
449

    
450
					$line .= " {$if} ";
451
				}
452

    
453
				if (isset($rule['protocol'])) {
454
					$line .= "proto {$rule['protocol']} ";
455
				}
456

    
457
				/* source address */
458
				/* Using any for source on 'out' is due to not knowing what
459
				 * the packet looks like after NAT occurs
460
				 */
461
				if (isset($rule['source']['any']) || $direction == "out") {
462
					$src = "any";
463
				} else if ($rule['source']['network']) {
464
					if (strstr($rule['source']['network'], "opt")) {
465
						$src = $optcfg[$rule['source']['network']]['sa'] . "/" .
466
						$optcfg[$rule['source']['network']]['sn'];
467
					} else {
468
						switch ($rule['source']['network']) {
469
							case 'lan':
470
							$src = "$lansa/$lansn";
471
							break;
472
							case 'pptp':
473
							$src = "$pptpsa/$pptpsn";
474
							break;
475
							case 'pppoe':
476
							$src = "$pppoesa/$pppoesn";
477
							break;
478
						}
479
					}
480
				} else if ($rule['source']['address']) {
481
					$src = alias_expand($rule['source']['address']);
482
					if(!$src)
483
						$src = $rule['source']['address'];
484
				}
485

    
486
				if (!$src) {
487
					printf("No source address found in rule $i\n");
488
					break;
489
				}
490

    
491
				if (isset($rule['source']['not'])) {
492
					$line .= "from ! $src ";
493
				} else {
494
					$line .= "from $src ";
495
				}
496

    
497
				if (!isset($rule['protocol']) || in_array($rule['protocol'], array("tcp","udp"))) {
498
					if ($rule['source']['port']) {
499
						/*
500
						* Check to see if port is a alias.  If so grab it and
501
						* enclose it in { } to pass to pf.
502
						*
503
						* Otherwise combine the portrange into one if its only
504
						* one item.
505
						*/
506
						$src = alias_expand($rule['source']['port']);
507
						if($src <> "") {
508
							$line .= "port {$src}";
509
						} else {
510
							$srcport = explode("-", $rule['source']['port']);
511
							if ((!$srcport[1]) || ($srcport[0] == $srcport[1])) {
512
								$line .= "port {$srcport[0]} ";
513
							} else {
514
								$line .= "port {$srcport[0]}:{$srcport[1]} ";
515
							}
516
						}
517
					}
518
				}
519

    
520
				/* destination address */
521
				if (isset($rule['destination']['any'])) {
522
					$dst = "any";
523
				} else if ($rule['destination']['network']) {
524

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

    
547
				if (!$dst) {
548
					printf("No destination address found in rule $i\n");
549
					break;
550
				}
551

    
552
				if (isset($rule['destination']['not'])) {
553
					$line .= "to ! $dst ";
554
				} else {
555
					$line .= "to $dst ";
556
				}
557

    
558
				if (!isset($rule['protocol']) || in_array($rule['protocol'], array("tcp","udp"))) {
559
					if ($rule['destination']['port']) {
560
						$dst = alias_expand($rule['destination']['port']);
561
						/*
562
						* Check to see if port is a alias.  If so grab it and
563
						* enclose it in { } to pass to pf.
564
						*
565
						* Otherwise combine the portrange into one if its only
566
						* one item.
567
						*/
568
						if($dst <> "") {
569
							$line .= "port {$dst}";
570
						} else {
571
							$dstport = explode("-", $rule['destination']['port']);
572
							if ((!$dstport[1]) || ($dstport[0] == $dstport[1])) {
573
								$line .= "port {$dstport[0]} ";
574
							} else {
575
								$line .= "port {$dstport[0]}:{$dstport[1]} ";
576
							}
577
						}
578
					}
579
				}
580

    
581
				if ($rule['iptos'])
582
				$line .= "tos {$rule['iptos']} ";
583

    
584
				$inflags = explode(",", $rule['tcpflags']);
585
				$flags = " flags ";
586
				foreach ($tcpflags as $tcpflag) {
587
					if (array_search($tcpflag, $inflags) !== false) {
588
						$flags .= strtoupper(substr($tcpflag, 0, 1));
589
					}
590
				}
591
				if($flags <> " flags ")
592
				$line .= "{$flags}/SAFRPU ";
593

    
594
				$qtag = "{$direction}queue";
595
				$line .= " keep state tagged unshaped tag {$rule[$qtag]} ";
596

    
597
				$line .= "\n";
598
				$shaperrules .= $line;
599
				/* setup the outbound queue on the other interface */
600
				switch($direction) {
601
					case 'in':
602
						$qouttag = "outqueue";
603
						break;
604
					case 'out':
605
						$qouttag = "inqueue";
606
						break;
607
				}
608
				$shaperrules .= "pass out on {$rule['out-interface']} all keep state tagged {$rule[$qtag]} tag {$rule[$qouttag]}\n";
609
			}
610
		}
611

    
612
		$i++;
613
	}
614

    
615
	return $shaperrules;
616
}
617

    
618
?>
(19-19/27)