Project

General

Profile

Download (16.8 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
/*
34
 * Creates monitoring configuration file and
35
 * adds apropriate static routes.
36
 */
37
function setup_gateways_monitor() {
38
	global $config, $g;
39

    
40
	$gateways_arr = return_gateways_array();
41
	if (!is_array($gateways_arr)) {
42
		log_error("No gateways to monitor. Apinger will not be run.");
43
		killbypid("{$g['varrun_path']}/apinger.pid");
44
		// TEMPORARY XXX
45
		exec("/usr/bin/killall -9 apinger");
46
		@unlink("{$g['tmp_path']}/apinger.status");
47
		return;
48
	}
49

    
50
	/* Default settings. Probably should move to globals.inc? */
51
	$a_settings = array();
52
	$a_settings['latencylow'] = "200";
53
	$a_settings['latencyhigh'] = "500";
54
	$a_settings['losslow'] = "10";
55
	$a_settings['losshigh'] = "20";
56

    
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 "root"
64
group "wheel"
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 "{$g['tmp_path']}/apinger.status"
78
	## Interval between file updates
79
	## when 0 or not set, file is written only when SIGUSR1 is received
80
	interval 5s
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/local/sbin/pfSctl -c 'filter reload'"
91
	command off "/usr/local/sbin/pfSctl -c 'filter reload'"
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
EOD;
140

    
141
	foreach($gateways_arr as $name => $gateway) {
142
		if (empty($gateway['monitor']) || !is_ipaddr($gateway['monitor'])) {
143
			if (is_ipaddr($gateway['gateway']))
144
				$gateway['monitor'] = $gateway['gateway'];
145
			else /* No chance to get an ip to monitor skip target. */
146
				continue;
147
		}
148

    
149
		/* Interface ip is needed since apinger will bind a socket to it. */
150
		$gwifip = find_interface_ip($gateway['interface'], true);
151
		if (!is_ipaddr($gwifip))
152
			continue; //Skip this target
153

    
154
		$apingercfg = "target \"{$gateway['monitor']}\" {\n";
155
		$apingercfg .= "	description \"{$name}\"\n";
156
		$apingercfg .= "	srcip \"{$gwifip}\"\n";
157
		$alarms = "";
158
		$alarmscfg = "";
159
		$override = false;
160
		if (!empty($gateway['lowloss'])) {
161
			$alarmscfg .= "alarm loss \"{$name}loss\" {\n";
162
			$alarmscfg .= "\tpercent_low {$gateway['losslow']}\n";
163
       			$alarmscfg .= "\tpercent_high {$gateway['losshigh']}\n";
164
			$alarmscfg .= "}\n";
165
			$alarms .= "\"{$name}loss\"";
166
			$override = true;
167
		} else {
168
			if ($override == true)
169
				$alarms .= ",";
170
			$alarms .= "\"loss\"";
171
			$override = true;
172
		}
173
		if (!empty($gateway['latencylow'])) {
174
			$alarmscfg .= "alarm delay \"{$name}delay\" {\n";
175
			$alarmscfg .= "\tdelay_low {$gateway['latencylow']}ms\n";
176
			$alarmscfg .= "\tdelay_high {$gateway['latencyhigh']}ms\n";
177
			$alarmscfg .= "}\n";
178
			if ($override == true)
179
				$alarms .= ",";
180
			$alarms .= "\"{$name}delay\"";
181
			$override = true;
182
		} else {
183
			if ($override == true)
184
				$alarms .= ",";
185
			$alarms .= "\"delay\"";
186
			$override = true;
187
		}
188
		if (!empty($gateway['down'])) {
189
			$alarmscfg .= "alarm down \"{$name}down\" {\n";
190
			$alarmscfg .= "\ttime {$gateway['down']}s\n";
191
			$alarmscfg .= "}\n";
192
			if ($override == true)
193
				$alarms .= ",";
194
			$alarms .= "\"{$name}down\"";
195
			$override = true;
196
		} else {
197
			if ($override == true)
198
				$alarms .= ",";
199
			$alarms .= "\"down\"";
200
			$override = true;
201
		}
202
		if ($override == true)
203
			$apingercfg .= "\talarms override {$alarms};\n";
204

    
205
		$apingercfg .= "	rrd file \"{$g['vardb_path']}/rrd/{$gateway['name']}-quality.rrd\"\n";
206
		$apingercfg .= "}\n";
207
		$apingercfg .= "\n";
208
		/*
209
		 * If the gateway is the same as the monitor we do not add a
210
		 * route as this will break the routing table.
211
		 * Add static routes for each gateway with their monitor IP
212
		 * not strictly necessary but is a added level of protection.
213
		 */
214
		if (is_ipaddr($gateway['gateway']) && $gateway['monitor'] != $gateway['gateway']) {
215
			log_error("Removing static route for monitor {$gateway['monitor']} and adding a new route through {$gateway['gateway']}");
216
			mwexec("/sbin/route delete -host " . escapeshellarg($gateway['monitor']), true);
217
			mwexec("/sbin/route add -host " . escapeshellarg($gateway['monitor']) .
218
				" " . escapeshellarg($gateway['gateway']));
219
		}
220

    
221
		$apingerconfig .= $alarmscfg;
222
		$apingerconfig .= $apingercfg;
223
	}
224
	fwrite($fd, $apingerconfig);
225
	fclose($fd);
226

    
227
	killbypid("{$g['varrun_path']}/apinger.pid");
228
	// TEMPORARY XXX
229
	exec("/usr/bin/killall -9 apinger");
230
	if (is_dir("{$g['tmp_path']}"))
231
		chmod("{$g['tmp_path']}", 01777);
232
	if (!is_dir("{$g['vardb_path']}/rrd"))
233
		mkdir("{$g['vardb_path']}/rrd", 0775);
234

    
235
	@chown("{$g['vardb_path']}/rrd", "nobody");
236

    
237
	/* start a new apinger process */
238
	@unlink("{$g['tmp_path']}/apinger.status");
239
	mwexec_bg("/usr/local/sbin/apinger -c {$g['varetc_path']}/apinger.conf");
240

    
241
	return 0;
242
}
243

    
244
/* return the status of the apinger targets as a array */
245
function return_gateways_status($byname = false) {
246
	global $config, $g;
247

    
248
	$apingerstatus = array();
249
	if (file_exists("{$g['tmp_path']}/apinger.status")) {
250
		$apingerstatus = file("{$g['tmp_path']}/apinger.status");
251
	}
252

    
253
	$status = array();
254
	foreach($apingerstatus as $line) {
255
		$info = explode("|", $line);
256
		if ($byname == false)
257
			$target = $info[0];
258
		else
259
			$target = $info[2];
260
		$status[$target]['monitorip'] = $info[0];
261
		$status[$target]['srcip'] = $info[1];
262
		$status[$target]['name'] = $info[2];
263
		$status[$target]['lastcheck'] = $info[5] ? date('r', $info[5]) : date('r');
264
		$status[$target]['delay'] = empty($info[6]) ? 0 : $info[6];
265
		$status[$target]['loss'] = empty($info[7]) ? "0.0%" : $info[7] . "";
266
		$status[$target]['status'] = trim($info[8]);
267
	}
268

    
269
	return($status);
270
}
271

    
272
/* Return all configured gateways on the system */
273
function return_gateways_array($disabled = false) {
274
	global $config, $g;
275

    
276
	$gateways_arr = array();
277

    
278
	$i = 0;
279
	/* Process/add all the configured gateways. */
280
	if (is_array($config['gateways']['gateway_item'])) {
281
		foreach($config['gateways']['gateway_item'] as $gateway) {
282
			if(empty($gateway['gateway']) || $gateway['gateway'] == "dynamic") {
283
				$gateway['gateway'] = get_interface_gateway($gateway['interface']);
284
				/* no IP address found, set to dynamic */
285
				if(! is_ipaddr($gateway['gateway']))
286
					$gateway['gateway'] = "dynamic";
287
				$gateway['dynamic'] = true;
288
			}
289
			if(empty($gateway['monitor']))
290
				$gateway['monitor'] = $gateway['gateway'];
291

    
292
			$gateway['friendlyiface'] = $gateway['interface'];
293
			$gateway['interface'] = get_real_interface($gateway['interface']);
294
			/* Some interface like wan might be default but have no info recorded 
295
			 * the config.
296
			 */
297
			if ($gateway['friendlyiface'] == "wan" && !isset($gateway['defaultgw'])) {
298
				if (file_exists("{$g['tmp_path']}/{$gateway['interface']}_defaultgw"))
299
					$gateway['defaultgw'] = true;
300
			}
301
			/* include the gateway index as the attribute */
302
			$gateway['attribute'] = $i;
303

    
304
			$gateways_arr[$gateway['name']] = $gateway;
305
			$i++;
306
		}
307
	} 
308

    
309
	/* Loop through all interfaces with a gateway and add it to a array */
310
	if ($disabled == false)
311
		$iflist = get_configured_interface_with_descr();
312
	else
313
		$iflist = get_configured_interface_with_descr(false, true);
314

    
315
	/* Process/add dynamic gateways. */
316
	foreach($iflist as $ifname => $friendly ) {
317
		if(! interface_has_gateway($ifname))
318
			continue;
319

    
320
		if (empty($config['interfaces'][$ifname]))
321
			continue;
322

    
323
		$ifcfg = &$config['interfaces'][$ifname];
324
		if (!empty($ifcfg['ipaddr']) && is_ipaddr($ifcfg['ipaddr']))
325
			continue;
326

    
327
		$gateway = array();
328
		$gateway['dynamic'] = false;
329
		$gateway['gateway'] = get_interface_gateway($ifname, $gateway['dynamic']);
330
		$gateway['interface'] = get_real_interface($ifname);
331
		$gateway['friendlyiface'] = $ifname;
332
		$gateway['name'] = $friendly;
333
		$gateway['attribute'] = "system";
334
	
335
		if ($gateway['dynamic'] === "default") {
336
			$gateway['defaultgw'] = true;
337
			$gateway['dynamic'] = true;
338
		}
339
		/* Loopback dummy for dynamic interfaces without a IP */
340
		if (!is_ipaddr($gateway['gateway']) && $gateway['dynamic'] == true)
341
			$gateway['gateway'] = "dynamic";
342

    
343
		/* automatically skip known static and dynamic gateways we have a array entry for */
344
		foreach($gateways_arr as $gateway_item) {
345
			if (($ifname == $gateway_item['friendlyiface'] && $friendly == $gateway_item['name']) ||
346
				($ifname == $gateway_item['friendlyiface'] && $gateway_item['dynamic'] == true))
347
					continue 2;
348
		}
349

    
350
		if (is_ipaddr($gateway['gateway']))
351
			$gateway['monitor'] = $gateway['gateway'];
352

    
353
		$gateway['descr'] = "Interface {$friendly} Dynamic Gateway";
354
		$gateways_arr[$friendly] = $gateway;
355
	}
356

    
357
	return($gateways_arr);
358
}
359

    
360
/*
361
 * Return an array with all gateway groups with name as key
362
 * All gateway groups will be processed before returning the array.
363
 */
364
function return_gateway_groups_array() {
365
	global $config, $g;
366

    
367
	/* fetch the current gateways status */
368
	$gateways_status = return_gateways_status(true);
369
	$gateways_arr = return_gateways_array();
370
	$gateway_groups_array = array();
371

    
372
	if (is_array($config['gateways']['gateway_group'])) {
373
		foreach($config['gateways']['gateway_group'] as $group) {
374
			/* create array with group gateways members seperated by tier */
375
			$tiers = array();
376
			$backupplan = array();
377
			foreach($group['item'] as $item) {
378
				$itemsplit = explode("|", $item);
379
				$tier = $itemsplit[1];
380
				$gwname = $itemsplit[0];
381

    
382
				/* Do it here rather than reiterating again the group in case no member is up. */
383
				$backupplan[$tier][] = $gwname;
384

    
385
				/* check if the gateway is available before adding it to the array */
386
				if (!empty($gateways_status[$gwname])) {
387
					$status = $gateways_status[$gwname];
388
					$gwdown = false;
389
					if (stristr($status['status'], "down")) {
390
						$msg = "MONITOR: {$gwname} has high latency, removing from routing group";
391
						$gwdown = true;
392
					} else if (stristr($status['status'], "loss") && strstr($group['trigger'], "loss")) {
393
						/* packet loss */
394
						$msg = "MONITOR: {$gwname} has packet loss, removing from routing group";
395
						$gwdown = true;
396
					} else if (stristr($status['status'], "delay") && strstr($group['trigger'] , "latency")) {
397
						/* high latency */
398
						$msg = "MONITOR: {$gwname} has high latency, removing from routing group";
399
						$gwdown = true;
400
					}
401
					if ($gwdown == true) {
402
						log_error($msg);
403
						notify_via_growl($msg);
404
					} else
405
						/* Online add member */
406
						$tiers[$tier][] = $gwname;
407
				}
408
			}
409
			$tiers_count = count($tiers);
410
			if($tiers_count == 0) {
411
				/* Oh dear, we have no members! Engage Plan B */
412
				$msg = "Gateways status could not be determined, considering all as up/active.";
413
				log_error($msg);
414
				notify_via_growl($msg);
415
				$tiers = $backupplan;
416
			}
417
			/* sort the tiers array by the tier key */
418
			ksort($tiers);
419

    
420
			/* we do not really foreach the tiers as we stop after the first tier */
421
			foreach($tiers as $tier) {
422
				/* process all gateways in this tier */
423
				foreach($tier as $member) {
424
					/* determine interface gateway */
425
					if (isset($gateways_arr[$member])) {
426
						$gateway = $gateways_arr[$member];
427
						$int = $gateway['interface'];
428
						$gatewayip = "";
429
						if(is_ipaddr($gateway['gateway'])) 
430
							$gatewayip = $gateway['gateway'];
431
						else if ($int <> "")
432
							$gatewayip = get_interface_gateway($gateway['friendlyiface']);
433
					
434
						if (($int <> "") && is_ipaddr($gatewayip)) {
435
							$groupmember = array();
436
							$groupmember['int']  = $int;
437
							$groupmember['gwip']  = $gatewayip;
438
							$groupmember['weight']  = isset($gateway['weight']) ? $gateway['weight'] : 1;
439
							$gateway_groups_array[$group['name']][] = $groupmember;
440
						}
441
					}
442
				}
443
				/* we should have the 1st available tier now, exit stage left */
444
				break;
445
			}
446
		}
447
	}
448
	return ($gateway_groups_array);
449
}
450

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

    
467
function lookup_gateway_ip_by_name($name) {
468

    
469
	$gateways_arr = return_gateways_array();
470
        foreach ($gateways_arr as $gname => $gw) {
471
                if ($gw['name'] == $name || $gname == $name)
472
                        return $gw['gateway'];
473
        }
474

    
475
	return false;
476
}
477

    
478
function lookup_gateway_monitor_ip_by_name($name) {
479

    
480
        $gateways_arr = return_gateways_array();
481
	if (!empty($gateways_arr[$name])) {
482
		$gateway = $gateways_arr[$name];
483
		if(!is_ipaddr($gateway['monitor']))
484
			return $gateway['gateway'];
485

    
486
		return $gateway['monitor'];
487
        }
488

    
489
        return (false);
490
}
491

    
492
function lookup_gateway_interface_by_name($name) {
493

    
494
        $gateways_arr = return_gateways_array();
495
	if (!empty($gateways_arr[$name])) {
496
		$interfacegw = $gateway['interface'];
497
		return ($interfacegw);
498
        }
499

    
500
        return (false);
501
}
502

    
503
function get_interface_gateway($interface, &$dynamic = false) {
504
        global $config, $g;
505

    
506
        $gw = NULL;
507

    
508
        $gwcfg = $config['interfaces'][$interface];
509
        if (!empty($gwcfg['gateway']) && is_array($config['gateways']['gateway_item'])) {
510
               	foreach($config['gateways']['gateway_item'] as $gateway) {
511
                        if ($gateway['name'] == $gwcfg['gateway']) {
512
                                $gw = $gateway['gateway'];
513
				break;
514
			}
515
                }
516
	}
517

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

    
531
        /* return gateway */
532
        return ($gw);
533
}
534

    
535
?>
(21-21/54)