Project

General

Profile

Download (17.7 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
		if (is_ipaddrv4($gateway['gateway'])) {
151
			$gwifip = find_interface_ip($gateway['interface'], true);
152
		}
153
		if (is_ipaddrv6($gateway['gateway'])) {
154
			$gwifip = find_interface_ipv6($gateway['interface'], true);
155
		}
156
		if (!is_ipaddr($gwifip))
157
			continue; //Skip this target
158

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

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

    
226
		$apingerconfig .= $alarmscfg;
227
		$apingerconfig .= $apingercfg;
228
	}
229
	fwrite($fd, $apingerconfig);
230
	fclose($fd);
231

    
232
	killbypid("{$g['varrun_path']}/apinger.pid");
233
	// TEMPORARY XXX
234
	exec("/usr/bin/killall -9 apinger");
235
	if (is_dir("{$g['tmp_path']}"))
236
		chmod("{$g['tmp_path']}", 01777);
237
	if (!is_dir("{$g['vardb_path']}/rrd"))
238
		mkdir("{$g['vardb_path']}/rrd", 0775);
239

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

    
242
	/* start a new apinger process */
243
	@unlink("{$g['tmp_path']}/apinger.status");
244
	mwexec_bg("/usr/local/sbin/apinger -c {$g['varetc_path']}/apinger.conf");
245

    
246
	return 0;
247
}
248

    
249
/* return the status of the apinger targets as a array */
250
function return_gateways_status($byname = false) {
251
	global $config, $g;
252

    
253
	$apingerstatus = array();
254
	if (file_exists("{$g['tmp_path']}/apinger.status")) {
255
		$apingerstatus = file("{$g['tmp_path']}/apinger.status");
256
	}
257

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

    
274
	return($status);
275
}
276

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

    
281
	$gateways_arr = array();
282

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

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

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

    
314
	/* Loop through all interfaces with a gateway and add it to a array */
315
	if ($disabled == false)
316
		$iflist = get_configured_interface_with_descr();
317
	else
318
		$iflist = get_configured_interface_with_descr(false, true);
319

    
320
	/* Process/add dynamic gateways. */
321
	foreach($iflist as $ifname => $friendly ) {
322
		if(! interface_has_gateway($ifname))
323
			continue;
324

    
325
		if (empty($config['interfaces'][$ifname]))
326
			continue;
327

    
328
		$ifcfg = &$config['interfaces'][$ifname];
329
		if (!empty($ifcfg['ipaddr']) && is_ipaddr($ifcfg['ipaddr']))
330
			continue;
331

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

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

    
355
		if (is_ipaddr($gateway['gateway']))
356
			$gateway['monitor'] = $gateway['gateway'];
357

    
358
		$gateway['descr'] = "Interface {$friendly} Dynamic Gateway";
359
		$gateways_arr[$friendly] = $gateway;
360
	}
361

    
362
	return($gateways_arr);
363
}
364

    
365
/*
366
 * Return an array with all gateway groups with name as key
367
 * All gateway groups will be processed before returning the array.
368
 */
369
function return_gateway_groups_array() {
370
	global $config, $g;
371

    
372
	/* fetch the current gateways status */
373
	$gateways_status = return_gateways_status(true);
374
	$gateways_arr = return_gateways_array();
375
	$gateway_groups_array = array();
376

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

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

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

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

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

    
472
function lookup_gateway_ip_by_name($name) {
473

    
474
	$gateways_arr = return_gateways_array();
475
        foreach ($gateways_arr as $gname => $gw) {
476
                if ($gw['name'] == $name || $gname == $name)
477
                        return $gw['gateway'];
478
        }
479

    
480
	return false;
481
}
482

    
483
function lookup_gateway_monitor_ip_by_name($name) {
484

    
485
        $gateways_arr = return_gateways_array();
486
	if (!empty($gateways_arr[$name])) {
487
		$gateway = $gateways_arr[$name];
488
		if(!is_ipaddr($gateway['monitor']))
489
			return $gateway['gateway'];
490

    
491
		return $gateway['monitor'];
492
        }
493

    
494
        return (false);
495
}
496

    
497
function lookup_gateway_interface_by_name($name) {
498

    
499
        $gateways_arr = return_gateways_array();
500
	if (!empty($gateways_arr[$name])) {
501
		$interfacegw = $gateway['interface'];
502
		return ($interfacegw);
503
        }
504

    
505
        return (false);
506
}
507

    
508
function get_interface_gateway($interface, &$dynamic = false) {
509
	global $config, $g;
510

    
511
	$gw = NULL;
512

    
513
	$gwcfg = $config['interfaces'][$interface];
514
	if (!empty($gwcfg['gateway']) && is_array($config['gateways']['gateway_item'])) {
515
		foreach($config['gateways']['gateway_item'] as $gateway) {
516
			if(($gateway['name'] == $gwcfg['gateway']) && (is_ipaddrv4($gateway['gateway']))) {
517
				$gw = $gateway['gateway'];
518
				break;
519
			}
520
		}
521
	}
522

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

    
535
	/* return gateway */
536
	return ($gw);
537
}
538

    
539
function get_interface_gateway_v6($interface, &$dynamic = false) {
540
	global $config, $g;
541

    
542
	$gw = NULL;
543
	$gwcfg = $config['interfaces'][$interface];
544
	if (!empty($gwcfg['gateway']) && is_array($config['gateways']['gateway_item'])) {
545
		foreach($config['gateways']['gateway_item'] as $gateway) {
546
			if(($gateway['name'] == $gwcfg['gatewayv6']) && (is_ipaddrv6($gateway['gateway']))) {
547
				$gw = $gateway['gateway'];
548
				break;
549
			}
550
		}
551
	}
552

    
553
	// for dynamic interfaces we handle them through the $interface_router file.
554
	if (!is_ipaddrv6($gw) && !is_ipaddr($gwcfg['ipaddrv6'])) {
555
			$realif = get_real_interface($interface);
556
			if (file_exists("{$g['tmp_path']}/{$realif}_routerv6")) {
557
				$gw = trim(file_get_contents("{$g['tmp_path']}/{$realif}_routerv6"), " \n");
558
				$dynamic = true;
559
			}
560
			if (file_exists("{$g['tmp_path']}/{$realif}_defaultgwv6"))
561
				$dynamic = "default";
562
			
563
	}
564
	/* return gateway */
565
	return ($gw);
566
}
567

    
568
?>
(21-21/54)