Project

General

Profile

Download (18.2 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
		@unlink("{$g['tmp_path']}/apinger.status");
45
		return;
46
	}
47

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

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

    
58
# pfSense apinger configuration file. Automatically Generated!
59

    
60
## User and group the pinger should run as
61
user "root"
62
group "wheel"
63

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

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

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

    
73
status {
74
	## File where the status information whould be written to
75
	file "{$g['tmp_path']}/apinger.status"
76
	## Interval between file updates
77
	## when 0 or not set, file is written only when SIGUSR1 is received
78
	interval 5s
79
}
80

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

    
86
## These parameters can be overriden in a specific alarm configuration
87
alarm default { 
88
	command on "/usr/local/sbin/pfSctl -c 'filter reload'"
89
	command off "/usr/local/sbin/pfSctl -c 'filter reload'"
90
	combine 10s
91
}
92

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

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

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

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

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

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

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

    
137
EOD;
138

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

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

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

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

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

    
225
	killbypid("{$g['varrun_path']}/apinger.pid");
226
	if (is_dir("{$g['tmp_path']}"))
227
		chmod("{$g['tmp_path']}", 01777);
228
	if (!is_dir("{$g['vardb_path']}/rrd"))
229
		mkdir("{$g['vardb_path']}/rrd", 0775);
230

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

    
233
	/* start a new apinger process */
234
	@unlink("{$g['tmp_path']}/apinger.status");
235
	sleep(1);
236
	mwexec_bg("/usr/local/sbin/apinger -c {$g['varetc_path']}/apinger.conf");
237

    
238
	return 0;
239
}
240

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

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

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

    
266
	return($status);
267
}
268

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

    
273
	$gateways_arr = array();
274

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

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

    
302
			$gateways_arr[$gateway['name']] = $gateway;
303
			$i++;
304
		}
305
	} 
306

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

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

    
318
		if (empty($config['interfaces'][$ifname]))
319
			continue;
320

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

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

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

    
348
		if (is_ipaddr($gateway['gateway']))
349
			$gateway['monitor'] = $gateway['gateway'];
350

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

    
355
	return($gateways_arr);
356
}
357

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

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

    
370
	if (isset($config['system']['gw_switch_default'])) {
371
	/* 
372
	 * NOTE: The code below is meant to replace the default gateway when it goes down.
373
	 *	This facilitates services running on pfSense itself and are not handled by a PBR to continue working.
374
	 */
375
	$upgw = "";
376
	$dfltgwdown = false;
377
	$dfltgwfound = false;
378
	foreach ($gateways_arr as $gwname => $gwsttng) {
379
		if (isset($gwsttng['defaultgw'])) {
380
			$dfltgwfound = true;
381
			if (stristr($gateways_status[$gwname]['status'], "down"))
382
				$dfltgwdown = true;
383
		}
384
		/* Keep a record of the last up gateway */
385
		if (empty($upgw) && !stristr($gateways_status[$gwname]['status'], "down"))
386
			$upgw = $gwname;
387
		if ($dfltgwdown == true && !empty($upgw))
388
			break;
389
	}
390
	if ($dfltgwfound == false) {
391
		$gwname = convert_friendly_interface_to_friendly_descr("wan");
392
		if (stristr($gateways_status[$gwname]['status'], "down"))
393
			$dfltgwdown = true;
394
	}
395
	if ($dfltgwdown == true && !empty($upgw)) {
396
		if ($gateways_arr[$upgw]['gateway'] == "dynamic")
397
			$gateways_arr[$upgw]['gateway'] = get_interface_gateway($gateways_arr[$upgw]['friendlyiface']);
398
		if (is_ipaddr($gateways_arr[$upgw]['gateway'])) {
399
			log_error("Default gateway down setting {$upgw} as default!");
400
			mwexec("/sbin/route delete -inet default; /sbin/route add -inet default {$gateways_arr[$upgw]['gateway']}");
401
		}
402
	}
403
	unset($upgw, $dfltgwfound, $dfltgwdown, $gwname, $gwsttng);
404
	}
405

    
406
	if (is_array($config['gateways']['gateway_group'])) {
407
		foreach($config['gateways']['gateway_group'] as $group) {
408
			/* create array with group gateways members seperated by tier */
409
			$tiers = array();
410
			$backupplan = array();
411
			foreach($group['item'] as $item) {
412
				$itemsplit = explode("|", $item);
413
				$tier = $itemsplit[1];
414
				$gwname = $itemsplit[0];
415

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

    
419
				/* check if the gateway is available before adding it to the array */
420
				if (!empty($gateways_status[$gwname])) {
421
					$status = $gateways_status[$gwname];
422
					$gwdown = false;
423
					if (stristr($status['status'], "down")) {
424
						$msg = "MONITOR: {$gwname} is down, removing from routing group";
425
						$gwdown = true;
426
					} else if (stristr($status['status'], "loss") && strstr($group['trigger'], "loss")) {
427
						/* packet loss */
428
						$msg = "MONITOR: {$gwname} has packet loss, removing from routing group";
429
						$gwdown = true;
430
					} else if (stristr($status['status'], "delay") && strstr($group['trigger'] , "latency")) {
431
						/* high latency */
432
						$msg = "MONITOR: {$gwname} has high latency, removing from routing group";
433
						$gwdown = true;
434
					}
435
					if ($gwdown == true) {
436
						log_error($msg);
437
						notify_via_growl($msg);
438
						notify_via_smtp($msg);
439
					} else
440
						/* Online add member */
441
						$tiers[$tier][] = $gwname;
442
				}
443
			}
444
			$tiers_count = count($tiers);
445
			if($tiers_count == 0) {
446
				/* Oh dear, we have no members! Engage Plan B */
447
				if (!$g['booting']) {
448
					$msg = "Gateways status could not be determined, considering all as up/active.";
449
					log_error($msg);
450
					notify_via_growl($msg);
451
					notify_via_smtp($msg);
452
				}
453
				$tiers = $backupplan;
454
			}
455
			/* sort the tiers array by the tier key */
456
			ksort($tiers);
457

    
458
			/* we do not really foreach the tiers as we stop after the first tier */
459
			foreach($tiers as $tier) {
460
				/* process all gateways in this tier */
461
				foreach($tier as $member) {
462
					/* determine interface gateway */
463
					if (isset($gateways_arr[$member])) {
464
						$gateway = $gateways_arr[$member];
465
						$int = $gateway['interface'];
466
						$gatewayip = "";
467
						if(is_ipaddr($gateway['gateway'])) 
468
							$gatewayip = $gateway['gateway'];
469
						else if ($int <> "")
470
							$gatewayip = get_interface_gateway($gateway['friendlyiface']);
471
					
472
						if (($int <> "") && is_ipaddr($gatewayip)) {
473
							$groupmember = array();
474
							$groupmember['int']  = $int;
475
							$groupmember['gwip']  = $gatewayip;
476
							$groupmember['weight']  = isset($gateway['weight']) ? $gateway['weight'] : 1;
477
							$gateway_groups_array[$group['name']][] = $groupmember;
478
						}
479
					}
480
				}
481
				/* we should have the 1st available tier now, exit stage left */
482
				break;
483
			}
484
		}
485
	}
486
	return ($gateway_groups_array);
487
}
488

    
489
/* Update DHCP WAN Interface ip address in gateway group item */
490
function dhclient_update_gateway_groups_defaultroute($interface = "wan") {
491
	global $config, $g;
492
	foreach($config['gateways']['gateway_item'] as & $gw) {	
493
		if($gw['interface'] == $interface) {
494
			$current_gw = get_interface_gateway($interface);
495
			if($gw['gateway'] <> $current_gw) {
496
				$gw['gateway'] = $current_gw;
497
				$changed = true;
498
			}
499
		}
500
	}
501
	if($changed && $current_gw)
502
		write_config("Updating gateway group gateway for $interface - new gateway is $current_gw");
503
}
504

    
505
function lookup_gateway_ip_by_name($name) {
506

    
507
	$gateways_arr = return_gateways_array();
508
        foreach ($gateways_arr as $gname => $gw) {
509
                if ($gw['name'] == $name || $gname == $name)
510
                        return $gw['gateway'];
511
        }
512

    
513
	return false;
514
}
515

    
516
function lookup_gateway_monitor_ip_by_name($name) {
517

    
518
        $gateways_arr = return_gateways_array();
519
	if (!empty($gateways_arr[$name])) {
520
		$gateway = $gateways_arr[$name];
521
		if(!is_ipaddr($gateway['monitor']))
522
			return $gateway['gateway'];
523

    
524
		return $gateway['monitor'];
525
        }
526

    
527
        return (false);
528
}
529

    
530
function lookup_gateway_interface_by_name($name) {
531

    
532
        $gateways_arr = return_gateways_array();
533
	if (!empty($gateways_arr[$name])) {
534
		$interfacegw = $gateway['interface'];
535
		return ($interfacegw);
536
        }
537

    
538
        return (false);
539
}
540

    
541
function get_interface_gateway($interface, &$dynamic = false) {
542
        global $config, $g;
543

    
544
        $gw = NULL;
545

    
546
        $gwcfg = $config['interfaces'][$interface];
547
        if (!empty($gwcfg['gateway']) && is_array($config['gateways']['gateway_item'])) {
548
               	foreach($config['gateways']['gateway_item'] as $gateway) {
549
                        if ($gateway['name'] == $gwcfg['gateway']) {
550
                                $gw = $gateway['gateway'];
551
				break;
552
			}
553
                }
554
	}
555

    
556
        // for dynamic interfaces we handle them through the $interface_router file.
557
        if (!is_ipaddr($gw) && !is_ipaddr($gwcfg['ipaddr'])) {
558
                $realif = get_real_interface($interface);
559
                if (file_exists("{$g['tmp_path']}/{$realif}_router")) {
560
                        $gw = trim(file_get_contents("{$g['tmp_path']}/{$realif}_router"), " \n");
561
					$dynamic = true;
562
                }
563
                if (file_exists("{$g['tmp_path']}/{$realif}_defaultgw"))
564
					$dynamic = "default";
565
			
566
				
567
        }
568

    
569
        /* return gateway */
570
        return ($gw);
571
}
572

    
573
?>
(24-24/61)