Project

General

Profile

Download (17.3 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
  Copyright (C) 2008 Bill Marquette, Seth Mos
4
  Copyright (C) 2010 Ermal Lu?i
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
	$gateways_status = return_gateways_status();
41
	if (!is_array($config['gateways']['gateway_item']))
42
		$config['gateways']['gateway_item'] = array();
43

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

    
46
	$a_settings = array();
47
	$a_settings['latencylow'] = "200";
48
	$a_settings['latencyhigh'] = "500";
49
	$a_settings['losslow'] = "10";
50
	$a_settings['losshigh'] = "20";
51

    
52
	$fd = fopen("{$g['varetc_path']}/apinger.conf", "w");
53
	$apingerconfig = <<<EOD
54

    
55
# pfSense apinger configuration file. Automatically Generated!
56

    
57
## User and group the pinger should run as
58
user "root"
59
group "wheel"
60

    
61
## Mailer to use (default: "/usr/lib/sendmail -t")
62
#mailer "/var/qmail/bin/qmail-inject" 
63

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

    
67
## Format of timestamp (%s macro) (default: "%b %d %H:%M:%S")
68
#timestamp_format "%Y%m%d%H%M%S"
69

    
70
status {
71
	## File where the status information whould be written to
72
	file "/tmp/apinger.status"
73
	## Interval between file updates
74
	## when 0 or not set, file is written only when SIGUSR1 is received
75
	interval 10s
76
}
77

    
78
########################################
79
# RRDTool status gathering configuration
80
# Interval between RRD updates
81
rrd interval 60s;
82

    
83
## These parameters can be overriden in a specific alarm configuration
84
alarm default { 
85
	command on "/usr/bin/touch /tmp/filter_dirty"
86
	command off "/usr/bin/touch /tmp/filter_dirty"
87
	combine 10s
88
}
89

    
90
## "Down" alarm definition. 
91
## This alarm will be fired when target doesn't respond for 30 seconds.
92
alarm down "down" {
93
	time 10s
94
}
95

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

    
104
## "Loss" alarm definition. 
105
## This alarm will be fired when packet loss goes over 20%
106
## it will be canceled, when the loss drops below 10%
107
alarm loss "loss" {
108
	percent_low {$a_settings['losslow']}
109
	percent_high {$a_settings['losshigh']}
110
}
111

    
112
target default {
113
	## How often the probe should be sent	
114
	interval 1s
115
	
116
	## How many replies should be used to compute average delay 
117
	## for controlling "delay" alarms
118
	avg_delay_samples 10
119
	
120
	## How many probes should be used to compute average loss
121
	avg_loss_samples 50
122

    
123
	## The delay (in samples) after which loss is computed
124
	## without this delays larger than interval would be treated as loss
125
	avg_loss_delay_samples 20
126

    
127
	## Names of the alarms that may be generated for the target
128
	alarms "down","delay","loss"
129

    
130
	## Location of the RRD
131
	#rrd file "{$g['vardb_path']}/rrd/apinger-%t.rrd"
132
}
133

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

    
141
EOD;
142

    
143
	/* add static routes for each gateway with their monitor IP */
144
	if(is_array($gateways_arr)) {
145
		$i = 2;
146
		foreach($gateways_arr as $name => $gateway) {
147
			$gwref = $a_gateway_item[$gateway['attribute']];
148
			/* for dynamic gateways without an IP address we subtitute a local one */
149
			if(is_ipaddr($gwref['monitor'])) {
150
				$gateway['monitor'] = $gwref['monitor'];
151
			} else {
152
				if ($gateway['gateway'] == "dynamic") {
153
					$gateway['monitor'] = "127.0.0.{$i}";
154
					$i++;
155
				}
156
				if (!is_ipaddr($gateway['monitor']))
157
					$gateway['monitor'] = $gateway['gateway'];
158
			}
159

    
160
			if (!is_ipaddr($gateway['monitor']))
161
				continue;
162

    
163
			if($gateway['monitor'] == "127.0.0.{$i}") {
164
				$gwifip = "127.0.0.1";
165
			} else {
166
				$gwifip = find_interface_ip($gateway['interface']);
167
			}
168
			if (!is_ipaddr($gwifip))
169
				continue; //Skip this target
170

    
171
			$apingercfg .= "target \"{$gateway['monitor']}\" {\n";
172
			$apingercfg .= "	description \"{$gateway['name']}\"\n";
173
			$apingercfg .= "	srcip \"{$gwifip}\"\n";
174
			$alarms = "";
175
			$override = false;
176
			if (!empty($gwref['lowloss'])) {
177
				$alarmscfg .= "alarm loss \"{$gateway['name']}loss\" {\n";
178
				$alarmscfg .= "\tpercent_low {$gwref['losslow']}\n";
179
        			$alarmscfg .= "\tpercent_high {$gwref['losshigh']}\n";
180
				$alarmscfg .= "}\n";
181
				$alarms .= "\"{$gateway['name']}loss\"";
182
				$override = true;
183
			} else {
184
                                if ($override == true)
185
                                        $alarms .= ",";
186
                                $alarms .= "\"loss\"";
187
                                $override = true;
188
                        }
189
			if (!empty($gwref['latencylow'])) {
190
				$alarmscfg .= "alarm delay \"{$gateway['name']}delay\" {\n";
191
				$alarmscfg .= "\tdelay_low {$gwref['latencylow']}ms\n";
192
				$alarmscfg .= "\tdelay_high {$gwref['latencyhigh']}ms\n";
193
				$alarmscfg .= "}\n";
194
				if ($override == true)
195
					$alarms .= ",";
196
				$alarms .= "\"{$gateway['name']}delay\"";
197
				$override = true;
198
			} else {
199
				if ($override == true)
200
                                        $alarms .= ",";
201
				$alarms .= "\"delay\"";
202
				$override = true;
203
			}
204
			if (!empty($gwref['down'])) {
205
				$alarmscfg .= "alarm down \"{$gateway['name']}down\" {\n";
206
				$alarmscfg .= "\ttime {$gwref['down']}s\n";
207
				$alarmscfg .= "}\n";
208
				if ($override == true)
209
                                        $alarms .= ",";
210
				$alarms .= "\"{$gateway['name']}down\"";
211
				$override = true;
212
			} else {
213
                                if ($override == true)
214
                                        $alarms .= ",";
215
                                $alarms .= "\"down\"";
216
                                $override = true;
217
                        }
218
			if ($override == true)
219
				$apingercfg .= "\talarms override {$alarms};\n";
220

    
221
			$apingercfg .= "	rrd file \"{$g['vardb_path']}/rrd/{$gateway['name']}-quality.rrd\"\n";
222
			$apingercfg .= "}\n";
223
			$apingercfg .= "\n";
224
			if($gateway['monitor'] == $gateway['gateway']) {
225
				/* if the gateway is the same as the monitor we do not add a
226
				 * route as this will break the routing table */
227
				continue;
228
			} else {
229
				if ($gateway['gateway'] != "dynamic" && is_ipaddr($gateway['gateway'])) {
230
					mwexec("/sbin/route delete -host " . escapeshellarg($gateway['monitor']));
231
					mwexec("/sbin/route add -host " . escapeshellarg($gateway['monitor']) .
232
						" " . escapeshellarg($gateway['gateway']));
233
					log_error("Removing static route for monitor {$gateway['monitor']} and adding a new route through {$gateway['gateway']}");
234
				}
235
			}
236
		}
237
		$apingerconfig .= $alarmscfg;
238
		$apingerconfig .= $apingercfg;
239
	}
240
	fwrite($fd, $apingerconfig);
241
	fclose($fd);
242

    
243
	killbypid("{$g['varrun_path']}/apinger.pid");
244
	if (is_dir("{$g['tmp_path']}"))
245
		chmod("{$g['tmp_path']}", 01777);
246
	if (is_dir("{$g['vardb_path']}/rrd"))
247
		chown("{$g['vardb_path']}/rrd", "nobody");
248
	/* start a new apinger process */
249
	mwexec_bg("/usr/local/sbin/apinger -c {$g['varetc_path']}/apinger.conf");
250

    
251
	return 0;
252
}
253

    
254
/* return the status of the apinger targets as a array */
255
function return_gateways_status() {
256
	global $config, $g;
257

    
258
	$apingerstatus = array();
259
	if (file_exists("{$g['tmp_path']}/apinger.status")) {
260
		$apingerstatus = file("{$g['tmp_path']}/apinger.status");
261
	}
262

    
263
	$status = array();
264
	foreach($apingerstatus as $line) {
265
		$info = explode("|", $line);
266
		$target = $info[0];
267
		$status[$target]['name'] = $info[1];
268
		$status[$target]['lastcheck'] = $info[4] ? date('r', $info[4]) : date('r');
269
		$status[$target]['delay'] = empty($info[5]) ? 0 : $info[5];
270
		$status[$target]['loss'] = empty($info[6]) ? "0.0%" : $info[6] . "";
271
		$status[$target]['status'] = trim($info[7]);
272
	}
273
	return($status);
274
}
275

    
276
/* Return all configured gateways on the system */
277
function return_gateways_array($disabled = false) {
278
	global $config;
279

    
280
	$gateways_arr = array();
281

    
282
	/* Loop through all interfaces with a gateway and add it to a array */
283
	if ($disabled == false) {
284
		$iflist = get_configured_interface_with_descr();
285
	} else {
286
		$iflist = get_configured_interface_with_descr(false, true);
287
	}
288

    
289
	$i = 0;
290
	/* tack on all the hard defined gateways as well */
291
	if(is_array($config['gateways']['gateway_item'])) {
292
		foreach($config['gateways']['gateway_item'] as $gateway) {
293
			if($gateway['gateway'] == "dynamic") {
294
				$gateway['gateway'] = get_interface_gateway($gateway['interface']);
295
				/* no IP address found, set to dynamic */
296
				if(! is_ipaddr($gateway['gateway'])) {
297
					$gateway['gateway'] = "dynamic";
298
				}
299
				$gateway['dynamic'] = true;
300
			}
301
			if($gateway['monitor'] == "") {
302
				$gateway['monitor'] = $gateway['gateway'];
303
			}
304
			/* include the gateway index as the attribute */
305
			$gateway['friendlyiface'] = $gateway['interface'];
306
			$gateway['interface'] = convert_friendly_interface_to_real_interface_name($gateway['interface']);
307
			$gateway['attribute'] = "$i";
308

    
309
			$gateways_arr[$gateway['name']] = $gateway;
310
			$i++;
311
		}
312
	} 
313

    
314
	foreach($iflist as $ifname => $friendly ) {
315
		if(! interface_has_gateway($ifname)) {
316
			continue;
317
		}
318
		$gateway = array();
319
		$gateway['dynamic'] = false;
320
		$gateway['gateway'] = get_interface_gateway($ifname, $gateway['dynamic']);
321
		$gateway['interface'] = get_real_interface($ifname);
322
		$gateway['friendlyiface'] = $ifname;
323
		$gateway['name'] = "{$friendly}";
324
		$gateway['attribute'] = "system";
325
	
326
		/* Loopback dummy for dynamic interfaces without a IP */
327
		if(!is_ipaddr(trim($gateway['gateway'])) && $gateway['dynamic'] == true) {
328
			 $gateway['gateway'] = "dynamic";
329
		}
330

    
331
		/* automatically skip known static and dynamic gateways we have a array entry for */
332
		foreach($gateways_arr as $gateway_item) {
333
			if ($ifname == $gateway_item['friendlyiface'] || $friendly == $gateway_item['name']) {
334
				if ($gateway_item['gateway'] == $gateway['gateway'])
335
					continue 2;
336
				if ($gateway_item['gateway'] == "dynamic")
337
					continue 2;
338
			}
339
		}
340

    
341
		/* retrieve a proper monitor IP? */
342
		$interface = $config['interfaces'][$ifname];
343
		if(is_ipaddr($interface['monitorip'])) {
344
			$gateway['monitor'] = $interface['monitorip'];
345
		} else {
346
			$gateway['monitor'] = $gateway['gateway'];
347
		}
348
		$gateway['descr'] = "Interface $ifname Dynamic Gateway";
349
		$gateways_arr[$ifname] = $gateway;
350
		$i++;
351
	}
352

    
353
	return($gateways_arr);
354
}
355

    
356

    
357
/* return a array with all gateway groups with name as key
358
 * All gateway groups will be processed before returning the array.
359
*/
360
function return_gateway_groups_array() {
361
	global $config, $g;
362

    
363
	/* fetch the current gateways status */
364
	$gateways_status = return_gateways_status();
365
	$gateways_arr = return_gateways_array();
366
	$gateway_groups_array = array();
367

    
368
	if (is_array($config['gateways']['gateway_group'])) {
369
		foreach($config['gateways']['gateway_group'] as $group) {
370
			/* create array with group gateways members seperated by tier */
371
			$tiers = array();
372
			foreach($group['item'] as $item) {
373
				$itemsplit = explode("|", $item);
374
				$tier = $itemsplit[1];
375
				$gwname = $itemsplit[0];
376
				/* check if the gateway is available before adding it to the array */
377
				foreach($gateways_status as $status) {
378
					if ($status['name'] != $gwname) {
379
						continue;
380
					}
381
					if (stristr($status['status'], "down")) {
382
						$msg = "MONITOR: $gwname has high latency, removing from routing group";
383
						log_error($msg);
384
						notify_via_growl($msg);
385
					} elseif (stristr($status['status'], "loss")) {
386
						if (strstr($group['trigger'], "loss")) {
387
							/* packet loss */
388
							$msg = "MONITOR: $gwname has packet loss, removing from routing group";
389
							log_error($msg);
390
							notify_via_growl($msg);
391
						} else {
392
							$tiers[$tier][] = $gwname;
393
						}
394
					} elseif (stristr($status['status'], "delay")) {
395
						if (strstr($group['trigger'] , "latency")) {
396
							/* high latency */
397
							$msg = "MONITOR: $gwname has high latency, removing from routing group";
398
							log_error($msg);
399
							notify_via_growl($msg);
400
						} else {
401
							$tiers[$tier][] = $gwname;
402
						}
403
					} elseif ($status['status'] == "none") {
404
						/* Online add member */
405
						$tiers[$tier][] = $gwname;
406
					}
407
				}
408
			}
409
			$tiers_count = count($tiers);
410
			if($tiers_count == 0) {
411
				/* Oh dear, we have no members! Engage Plan B */
412
				$msg = "All gateways are unavailable, proceeding with configured XML settings!";
413
				log_error($msg);
414
				notify_via_growl($msg);
415
				foreach($group['item'] as $item) {
416
					$itemsplit = explode("|", $item);
417
					$tier = $itemsplit[1];
418
					$gwname = $itemsplit[0];
419
					$tiers[$tier][] = $gwname;
420
				}
421
			}
422
			/* sort the tiers array by the tier key */
423
			ksort($tiers);
424
			/* we do not really foreach the tiers as we stop after the first tier */
425
			foreach($tiers as $tier) {
426
				/* process all gateways in this tier */
427
				foreach($tier as $member) {
428
					/* determine interface gateway */
429
					if (isset($gateways_arr[$member])) {
430
						$gateway = $gateways_arr[$member];
431
						$int = $gateway['interface'];
432
						$gatewayip = "";
433
						if(is_ipaddr($gateway['gateway'])) 
434
							$gatewayip = $gateway['gateway'];
435
						else if ($int <> "")
436
							$gatewayip = get_interface_gateway($gateway['friendlyiface']);
437
					}
438
					if (($int <> "") && is_ipaddr($gatewayip)) {
439
						$groupmember = array();
440
						$groupmember['int']  = "$int";
441
						$groupmember['gwip']  = "$gatewayip";
442
						$groupmember['weight']  = isset($gateway['weight']) ? $gateway['weight'] : 1;
443
						$gateway_groups_array[$group['name']][] = $groupmember;
444
					}
445
				}
446
				/* we should have the 1st available tier now, exit stage left */
447
				break;
448
			}
449
		}
450
	}
451
	return ($gateway_groups_array);
452
}
453

    
454
/* Update DHCP WAN Interface ip address in gateway group item */
455
function dhclient_update_gateway_groups_defaultroute($interface = "wan") {
456
	global $config, $g;
457
	foreach($config['gateways']['gateway_item'] as & $gw) {	
458
		if($gw['interface'] == $interface) {
459
			$current_gw = get_interface_gateway($interface);
460
			if($gw['gateway'] <> $current_gw) {
461
				$gw['gateway'] = $current_gw;
462
				$changed = true;
463
			}
464
		}
465
	}
466
	if($changed && $current_gw)
467
		write_config("Updating gateway group gateway for $interface - new gateway is $current_gw");
468
}
469

    
470
function lookup_gateway_ip_by_name($name) {
471

    
472
	$gateways_arr = return_gateways_array();
473
        foreach ($gateways_arr as $gw) {
474
                if ($gw['name'] == $name)
475
                        return $gw['gateway'];
476
        }
477

    
478
	return false;
479
}
480

    
481
function lookup_gateway_monitor_ip_by_name($name) {
482

    
483
        $gateways_arr = return_gateways_array();
484
	if (!empty($gateways_arr[$name])) {
485
		$gateway = $gateways_arr[$name];
486
		if ($gateway['gateway'] == "dynamic")
487
			$gateway['monitor'] = "127.0.0.2";
488

    
489
		$monitorip = $gateway['monitor'];
490
		if(!is_ipaddr($monitorip))
491
			$monitorip = $gateway['gateway'];
492

    
493
		return ($monitorip);
494
        }
495

    
496
        return (false);
497
}
498

    
499
function lookup_gateway_interface_by_name($name) {
500

    
501
        $gateways_arr = return_gateways_array();
502
	if (!empty($gateways_arr[$name])) {
503
		//$gatewayip = $gateway['gateway'];
504
		$interfacegw = $gateway['interface'];
505
		return ($interfacegw);
506
        }
507

    
508
        return (false);
509
}
510

    
511
function get_interface_gateway($interface, &$dynamic = false) {
512
        global $config, $g;
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']) && is_array($config['gateways']['gateway_item'])) {
520
               	foreach($config['gateways']['gateway_item'] as $gateway) {
521
                        if ($gateway['name'] == $gwcfg['gateway']) {
522
                                $gw = $gateway['gateway'];
523
				break;
524
			}
525
                }
526
	}
527

    
528
        // for dynamic interfaces we handle them through the $interface_router file.
529
        if (!is_ipaddr($gw) && !is_ipaddr($gwcfg['ipaddr'])) {
530
                $realif = get_real_interface($interface);
531
                if (file_exists("{$g['tmp_path']}/{$realif}_router")) {
532
                        $gw = file_get_contents("{$g['tmp_path']}/{$realif}_router");
533
                        $gw = rtrim($gw);
534
			$dynamic = true;
535
                }
536
        }
537

    
538
        /* return gateway */
539
        return ($gw);
540
}
541

    
542
?>
(20-20/51)