Project

General

Profile

Download (17.2 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/* $Id$ */
3
/*
4
  Copyright (C) 2008 Bill Marquette, Seth Mos
5
  All rights reserved.
6

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

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

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

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

    
28
	pfSense_BUILDER_BINARIES:	/usr/bin/killall	/sbin/route	/usr/local/sbin/apinger
29
	pfSense_MODULE:	routing
30

    
31
  */
32

    
33
/* add static routes for monitor IP addresse
34
 * creates monitoring configuration file
35
 */
36
function setup_gateways_monitor() {
37
	global $config;
38
	global $g;
39
	$gateways_arr = return_gateways_array();
40
	if (!is_array($config['gateways']['gateway_item']))
41
		$config['gateways']['gateway_item'] = array();
42

    
43
	$a_gateway_item = &$config['gateways']['gateway_item'];
44

    
45
	if (is_array($config['gateways']['settings'])) {
46
		$a_settings = &$config['gateways']['settings'];
47
	} else {
48
		$a_settings['latencylow'] = "200";
49
		$a_settings['latencyhigh'] = "500";
50
		$a_settings['losslow'] = "10";
51
		$a_settings['losshigh'] = "20";
52
	}
53

    
54
	/* kill apinger process */
55
	if(is_process_running("apinger"))
56
		mwexec("/usr/bin/killall apinger", true);
57
	$fd = fopen("{$g['varetc_path']}/apinger.conf", "w");
58
	$apingerconfig = <<<EOD
59

    
60
# pfSense apinger configuration file. Automatically Generated!
61

    
62
## User and group the pinger should run as
63
user "nobody"
64
group "nobody"
65

    
66
## Mailer to use (default: "/usr/lib/sendmail -t")
67
#mailer "/var/qmail/bin/qmail-inject" 
68

    
69
## Location of the pid-file (default: "/var/run/apinger.pid")
70
pid_file "{$g['varrun_path']}/apinger.pid"
71

    
72
## Format of timestamp (%s macro) (default: "%b %d %H:%M:%S")
73
#timestamp_format "%Y%m%d%H%M%S"
74

    
75
status {
76
	## File where the status information whould be written to
77
	file "/tmp/apinger.status"
78
	## Interval between file updates
79
	## when 0 or not set, file is written only when SIGUSR1 is received
80
	interval 10s
81
}
82

    
83
########################################
84
# RRDTool status gathering configuration
85
# Interval between RRD updates
86
rrd interval 60s;
87

    
88
## These parameters can be overriden in a specific alarm configuration
89
alarm default { 
90
	command on "/usr/bin/touch /tmp/filter_dirty"
91
	command off "/usr/bin/touch /tmp/filter_dirty"
92
	combine 10s
93
}
94

    
95
## "Down" alarm definition. 
96
## This alarm will be fired when target doesn't respond for 30 seconds.
97
alarm down "down" {
98
	time 10s
99
}
100

    
101
## "Delay" alarm definition. 
102
## This alarm will be fired when responses are delayed more than 200ms
103
## it will be canceled, when the delay drops below 100ms
104
alarm delay "delay" {
105
	delay_low {$a_settings['latencylow']}ms
106
	delay_high {$a_settings['latencyhigh']}ms
107
}
108

    
109
## "Loss" alarm definition. 
110
## This alarm will be fired when packet loss goes over 20%
111
## it will be canceled, when the loss drops below 10%
112
alarm loss "loss" {
113
	percent_low {$a_settings['losslow']}
114
	percent_high {$a_settings['losshigh']}
115
}
116

    
117
target default {
118
	## How often the probe should be sent	
119
	interval 1s
120
	
121
	## How many replies should be used to compute average delay 
122
	## for controlling "delay" alarms
123
	avg_delay_samples 10
124
	
125
	## How many probes should be used to compute average loss
126
	avg_loss_samples 50
127

    
128
	## The delay (in samples) after which loss is computed
129
	## without this delays larger than interval would be treated as loss
130
	avg_loss_delay_samples 20
131

    
132
	## Names of the alarms that may be generated for the target
133
	alarms "down","delay","loss"
134

    
135
	## Location of the RRD
136
	#rrd file "{$g['vardb_path']}/rrd/apinger-%t.rrd"
137
}
138

    
139
## Targets to probe
140
## Each one defined with:
141
## target <address> { <parameter>... }
142
## The parameters are those described above in the "target default" section
143
## plus the "description" parameter.
144
## the <address> should be IPv4 or IPv6 address (not hostname!)
145

    
146
EOD;
147

    
148
	/* add static routes for each gateway with their monitor IP */
149
	if(is_array($gateways_arr)) {
150
		$i = 2;
151
		foreach($gateways_arr as $name => $gateway) {
152
			/* for dynamic gateways without an IP address we subtitute a local one */
153
			if((is_numeric($gateway['attribute'])) && ($a_gateway_item[$gateway['attribute']]['monitor'] <> "")) {
154
				$gateway['monitor'] = $a_gateway_item[$gateway['attribute']]['monitor'];
155
			} else {
156
				if(($gateway['gateway'] == "dynamic") && ($gateway['monitor'])) {
157
					$gateway['monitor'] = "127.0.0.{$i}";
158
					$i++;
159
				}
160
				if($gateway['monitor'] == "") {
161
					$gateway['monitor'] = $gateway['gateway'];
162
				}
163
			}
164
			$apingerconfig .= "target \"{$gateway['monitor']}\" {\n";
165
			$apingerconfig .= "	description \"{$gateway['name']}\"\n";
166
			$apingerconfig .= "	rrd file \"{$g['vardb_path']}/rrd/{$gateway['name']}-quality.rrd\"\n";
167
			$apingerconfig .= "}\n";
168
			$apingerconfig .= "\n";
169
			if($gateway['monitor'] == $gateway['gateway']) {
170
				/* if the gateway is the same as the monitor we do not add a
171
				 * route as this will break the routing table */
172
				continue;
173
			} else {
174
				if(($gateway['gateway'] != "dynamic") && (is_ipaddr($gateway['gateway']))) {
175
					mwexec("/sbin/route delete -host " . escapeshellarg($gateway['monitor']));
176
					mwexec("/sbin/route add -host " . escapeshellarg($gateway['monitor']) .
177
						" " . escapeshellarg($gateway['gateway']));
178
					log_error("Removing static route for monitor {$gateway['monitor']} and adding a new route through {$gateway['gateway']}");
179
				}
180
			}
181
		}
182
	}
183
	fwrite($fd, $apingerconfig);
184
	fclose($fd);
185

    
186
	if(!is_process_running("apinger")) {
187
		if (is_dir("{$g['tmp_path']}"))
188
			chmod("{$g['tmp_path']}", 01777);
189
		if (is_dir("{$g['vardb_path']}/rrd"))
190
			chown("{$g['vardb_path']}/rrd", "nobody");
191
		/* start a new apinger process */
192
		mwexec_bg("/usr/local/sbin/apinger -c {$g['varetc_path']}/apinger.conf");
193
	}
194
	return 0;
195
}
196

    
197
/* return the status of the apinger targets as a array */
198
function return_gateways_status() {
199
	global $config;
200
	global $g;
201
	$gateways_arr = return_gateways_array();
202

    
203
	$apingerstatus = array();
204
	if(is_readable("{$g['tmp_path']}/apinger.status")) {
205
		$apingerstatus = file("{$g['tmp_path']}/apinger.status");
206
	}
207

    
208
	$status = array();
209
	$i = 2;
210
	/* assign a dummy IP address for dynamic interfaces in case we need it */
211
	$monitor = array();
212
	foreach($gateways_arr as $name => $gateway) {
213
		if($gateway['monitor'] == "dynamic") {
214
			$gateway['monitor'] = "127.0.0.{$i}";
215
			$i++;
216
		}
217
		$status[$gateway['monitor']]['monitor'] = $gateway['monitor'];
218
		$status[$gateway['monitor']]['interface'] = $gateway['interface'];
219
		$status[$gateway['monitor']]['gateway'] = $gateway['gateway'];
220
		$status[$gateway['monitor']]['name'] = $gateway['name'];
221
		$status[$gateway['monitor']]['status'] = "down";
222

    
223
	}
224
	foreach($apingerstatus as $line) {
225
		$fields = explode(":", $line);
226
		switch($fields[0]) {
227
			case "Target":
228
				$target = trim($fields[1]);
229
				break;
230
			case "Description":
231
				if($target)
232
		 			$status[$target]['name'] = trim($fields[1]);
233
				break;
234
			case "Last reply received":
235
				if($target)
236
					$status[$target]['lastcheck'] = trim($fields[1]) .":". trim($fields[2]) .":". trim($fields[3]);
237
				break;
238
			case "Average delay":
239
				if($target)
240
					$status[$target]['delay'] = trim($fields[1]);
241
				break;
242
			case "Average packet loss":
243
				if($target)
244
					$status[$target]['loss'] = trim($fields[1]);
245
				break;
246
			case "Active alarms":
247
				if($target)
248
					$status[$target]['status'] = trim($fields[1]);
249
				break;
250
		}
251
	}
252
	return($status);
253
}
254

    
255
/* Return all configured gateways on the system */
256
function return_gateways_array($disabled = false) {
257
	global $config;
258

    
259
	$gateways_arr = array();
260

    
261
	/* Loop through all interfaces with a gateway and add it to a array */
262
	if ($disabled == false) {
263
		$iflist = get_configured_interface_with_descr();
264
	} else {
265
		$iflist = get_configured_interface_with_descr(false, true);
266
	}
267
		
268
	/* tack on all the hard defined gateways as well */
269
	if(is_array($config['gateways']['gateway_item'])) {
270
		$i = 0;
271
		foreach($config['gateways']['gateway_item'] as $gateway) {
272
			if($gateway['gateway'] == "dynamic") {
273
				$gateway['gateway'] = get_interface_gateway($gateway['interface']);
274
				/* no IP address found, set to dynamic */
275
				if(! is_ipaddr($gateway['gateway'])) {
276
					$gateway['gateway'] = "dynamic";
277
				}
278
				$gateway['dynamic'] = true;
279
			}
280
			if($gateway['monitor'] == "") {
281
				$gateway['monitor'] = $gateway['gateway'];
282
			}
283
			/* include the gateway index as the attribute */
284
			$gateway['friendlyiface'] = $gateway['interface'];
285
			$gateway['interface'] = convert_friendly_interface_to_real_interface_name($gateway['interface']);
286
			$gateway['attribute'] = "$i";
287

    
288
			$gateways_arr[$gateway['name']] = $gateway;
289
			$i++;
290
		}
291
	} 
292

    
293
	foreach($iflist as $ifname => $friendly ) {
294
		if(! interface_has_gateway($ifname)) {
295
			continue;
296
		}
297
		$gateway = array();
298
		$gateway['dynamic'] = false;
299
		$gateway['gateway'] = get_interface_gateway($ifname, $gateway['dynamic']);
300
		$gateway['interface'] = get_real_interface($ifname);
301
		$gateway['friendlyiface'] = $ifname;
302
		$gateway['name'] = "{$ifname}";
303
		$gateway['attribute'] = "system";
304
	
305
		/* Loopback dummy for dynamic interfaces without a IP */
306
		if(!is_ipaddr(trim($gateway['gateway'])) && $gateway['dynamic'] == true) {
307
			 $gateway['gateway'] = "dynamic";
308
		}
309

    
310
		/* automatically skip known static and dynamic gateways we have a array entry for */
311
		foreach($gateways_arr as $gateway_item) {
312
			if($gateway_item['gateway'] == $gateway['gateway'] && ($ifname == $gateway_item['friendlyiface']))
313
				continue 2;
314
			if(($gateway_item['gateway'] == "dynamic") && ($ifname == $gateway_item['friendlyiface']))
315
				continue 2;
316
		}
317

    
318
		/* retrieve a proper monitor IP? */
319
		$interface = $config['interfaces'][$ifname];
320
		if(is_ipaddr($interface['monitorip'])) {
321
			$gateway['monitor'] = $interface['monitorip'];
322
		} else {
323
			$gateway['monitor'] = $gateway['gateway'];
324
		}
325
		$gateway['descr'] = "Interface $ifname Dynamic Gateway";
326
		$gateways_arr[$ifname] = $gateway;
327
		$i++;
328
	}
329

    
330
	return($gateways_arr);
331
}
332

    
333

    
334
/* return a array with all gateway groups with name as key
335
 * All gateway groups will be processed before returning the array.
336
*/
337
function return_gateway_groups_array() {
338
	global $config, $g;
339

    
340
	/* fetch the current gateways status */
341
	$gateways_status = return_gateways_status();
342
	$gateways_arr = return_gateways_array();
343
	$gateway_groups_array = array();
344

    
345
	if (is_array($config['gateways']['gateway_group'])) {
346
		foreach($config['gateways']['gateway_group'] as $group) {
347
			/* create array with group gateways members seperated by tier */
348
			$tiers = array();
349
			foreach($group['item'] as $item) {
350
				$itemsplit = explode("|", $item);
351
				$tier = $itemsplit[1];
352
				$gwname = $itemsplit[0];
353
				/* check if the gateway is available before adding it to the array */
354
				foreach($gateways_status as $status) {
355
					if(($status['name'] != $gwname)) {
356
						continue;
357
					}
358
					if (preg_match("/down/i", $status['status'])) {
359
						$msg = "MONITOR: $gwname has high latency, removing from routing group";
360
						log_error($msg);
361
						notify_via_growl($msg);
362
					} elseif (preg_match("/loss/i", $status['status'])) {
363
						if (strstr($group['trigger'], "loss")) {
364
							/* packet loss */
365
							$msg = "MONITOR: $gwname has packet loss, removing from routing group";
366
							log_error($msg);
367
							notify_via_growl($msg);
368
						} else {
369
							$tiers[$tier][] = $gwname;
370
						}
371
					} elseif (preg_match("/delay/i", $status['status'])) {
372
						if (strstr($group['trigger'] , "latency")) {
373
							/* high latency */
374
							$msg = "MONITOR: $gwname has high latency, removing from routing group";
375
							log_error($msg);
376
							notify_via_growl($msg);
377
						} else {
378
							$tiers[$tier][] = $gwname;
379
						}
380
					} elseif (preg_match("/none/i", $status['status'])) {
381
						/* Online add member */
382
						$tiers[$tier][] = $gwname;
383
					}
384
				}
385
			}
386
			$tiers_count = count($tiers);
387
			if($tiers_count == 0) {
388
				/* Oh dear, we have no members! Engage Plan B */
389
				$msg = "All gateways are unavailable, proceeding with configured XML settings!";
390
				log_error($msg);
391
				notify_via_growl($msg);
392
				foreach($group['item'] as $item) {
393
					foreach($group['item'] as $item) {
394
						$itemsplit = explode("|", $item);
395
						$tier = $itemsplit[1];
396
						$gwname = $itemsplit[0];
397
						$tiers[$tier][] = $gwname;
398
					}
399
				}
400
			}
401
			/* sort the tiers array by the tier key */
402
			ksort($tiers);
403
			/* we do not really foreach the tiers as we stop after the first tier */
404
			foreach($tiers as $tiernr => $tier) {
405
				/* process all gateways in this tier */
406
				$member_count = count($tier);
407
				foreach($tier as $tiernr => $member) {
408
					/* determine interface gateway */
409
					foreach($gateways_arr as $name => $gateway) {
410
						if($gateway['name'] == $member) {
411
							$int = $gateway['interface'];
412
							if(is_ipaddr($gateway['gateway'])) 
413
								$gatewayip = $gateway['gateway'];
414
							else 
415
								$gatewayip = lookup_gateway_ip_by_name($gateway['gateway']);
416
							break;
417
						}
418
					}
419
					if (($int <> "") && is_ipaddr($gatewayip)) {
420
						$gateway_groups_array[$group['name']][$tiernr]['int']  = "$int";
421
						$gateway_groups_array[$group['name']][$tiernr]['gwip']  = "$gatewayip";
422
					}
423
				}
424
				/* we should have the 1st available tier now, exit stage left */
425
				break;
426
			}
427
		}
428
	}
429
	return($gateway_groups_array);
430
}
431

    
432
/* Update DHCP WAN Interface ip address in gateway group item */
433
function dhclient_update_gateway_groups_defaultroute($interface = "wan") {
434
	global $config, $g;
435
	foreach($config['gateways']['gateway_item'] as & $gw) {	
436
		if($gw['interface'] == $interface) {
437
			$current_gw = get_interface_gateway($interface);
438
			if($gw['gateway'] <> $current_gw) {
439
				$gw['gateway'] = $current_gw;
440
				$changed = true;
441
			}
442
		}
443
	}
444
	if($changed && $current_gw)
445
		write_config("Updating gateway group gateway for $interface - new gateway is $current_gw");
446
}
447

    
448
function lookup_gateway_ip_by_name($name) {
449
        global $config;
450
        if(is_array($config['gateways']['gateway_item'])) {
451
                foreach($config['gateways']['gateway_item'] as $gateway) {
452
                        if($gateway['name'] == $name) {
453
                                $gatewayip = $gateway['gateway'];
454
                                //$interfacegw = $gateway['interface'];
455
                                return($gatewayip);
456
                        }
457
                }
458
        } else {
459
                return(false);
460
        }
461
}
462

    
463
function lookup_gateway_monitor_ip_by_name($name) {
464
        global $config;
465
        $gateways_arr = return_gateways_array();
466

    
467
	$i = 2;
468
        foreach($gateways_arr as $gateway) {
469
		if($gateway['gateway'] == "dynamic") {
470
			$gateway['monitor'] = "127.0.0.{$i}";
471
			$i++;
472
		}
473
                if($gateway['name'] == "$name") {
474
                        $monitorip = $gateway['monitor'];
475
                        if($monitorip == "")
476
                                $monitorip = $gateway['gateway'];
477

    
478
                        return($monitorip);
479
                }
480
        }
481
        return(false);
482
}
483

    
484
function lookup_gateway_interface_by_name($name) {
485
        global $config;
486
        $gateways_arr = return_gateways_array();
487

    
488
        foreach($gateways_arr as $gateway) {
489
                if($gateway['name'] == "$name") {
490
                        $gatewayip = $gateway['gateway'];
491
                        $interfacegw = $gateway['interface'];
492
                        return($interfacegw);
493
                }
494
        }
495
        return(false);
496
}
497

    
498
function get_interface_gateway($interface, &$dynamic = false) {
499
        global $config, $g;
500

    
501
        $iflist = get_configured_interface_with_descr();
502
        /*
503
         * XXX: BUG: This is silly at first, but we may be called with the interface
504
         *      descr for no apparent reason!!!
505
         *      Probably one of those silly strtoupper() legacy stuff!
506
         */
507
        foreach ($iflist as $ifent => $ifdesc) {
508
                if ($ifent == $interface || $ifdesc == $interface) {
509
                        $interface = $ifent;
510
                        break;
511
                }
512
        }
513

    
514
        $gw = NULL;
515

    
516
        $gwcfg = $config['interfaces'][$interface];
517
        if (is_ipaddr($gwcfg['gateway']))
518
                $gw = $gwcfg['gateway'];
519
        else if (!empty($gwcfg['gateway']))
520
                $gw = lookup_gateway_ip_by_name($gwcfg['gateway']);
521

    
522
        // for dynamic interfaces we handle them through the $interface_router file.
523
        if (!is_ipaddr($gw) && !is_ipaddr($gwcfg['ipaddr'])) {
524
                $realif = get_real_interface($interface);
525
                if (file_exists("{$g['tmp_path']}/{$realif}_router")) {
526
                        $gw = file_get_contents("{$g['tmp_path']}/{$realif}_router");
527
                        $gw = rtrim($gw);
528
			$dynamic = true;
529
                }
530
        }
531

    
532
        /* return gateway */
533
        return $gw;
534
}
535

    
536
?>
(20-20/52)