Project

General

Profile

Download (17 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
	if (!is_array($config['gateways']['gateway_item']))
41
		$config['gateways']['gateway_item'] = array();
42

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

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

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

    
57
# pfSense apinger configuration file. Automatically Generated!
58

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
143
EOD;
144

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

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

    
211
			$apingercfg .= "	rrd file \"{$g['vardb_path']}/rrd/{$gateway['name']}-quality.rrd\"\n";
212
			$apingercfg .= "}\n";
213
			$apingercfg .= "\n";
214
			if($gateway['monitor'] == $gateway['gateway']) {
215
				/* if the gateway is the same as the monitor we do not add a
216
				 * route as this will break the routing table */
217
				continue;
218
			} else {
219
				if(($gateway['gateway'] != "dynamic") && (is_ipaddr($gateway['gateway']))) {
220
					mwexec("/sbin/route delete -host " . escapeshellarg($gateway['monitor']));
221
					mwexec("/sbin/route add -host " . escapeshellarg($gateway['monitor']) .
222
						" " . escapeshellarg($gateway['gateway']));
223
					log_error("Removing static route for monitor {$gateway['monitor']} and adding a new route through {$gateway['gateway']}");
224
				}
225
			}
226
		}
227
		$apingerconfig .= $alarmscfg;
228
		$apingerconfig .= $apingercfg;
229
	}
230
	fwrite($fd, $apingerconfig);
231
	fclose($fd);
232

    
233
	if(!is_process_running("apinger")) {
234
		if (is_dir("{$g['tmp_path']}"))
235
			chmod("{$g['tmp_path']}", 01777);
236
		if (is_dir("{$g['vardb_path']}/rrd"))
237
			chown("{$g['vardb_path']}/rrd", "nobody");
238
		/* start a new apinger process */
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() {
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
		$target = $info[0];
257
		$status[$target]['name'] = $info[1];
258
		$status[$target]['lastcheck'] = $info[4] ? date('r', $info[4]) : date('r');
259
		$status[$target]['delay'] = empty($info[5]) ? 0 : $info[5];
260
		$status[$target]['loss'] = empty($info[6]) ? "0.0%" : $info[6] . "";
261
		$status[$target]['status'] = trim($info[7]);
262
	}
263
	return($status);
264
}
265

    
266
/* Return all configured gateways on the system */
267
function return_gateways_array($disabled = false) {
268
	global $config;
269

    
270
	$gateways_arr = array();
271

    
272
	/* Loop through all interfaces with a gateway and add it to a array */
273
	if ($disabled == false) {
274
		$iflist = get_configured_interface_with_descr();
275
	} else {
276
		$iflist = get_configured_interface_with_descr(false, true);
277
	}
278

    
279
	$i = 0;
280
	/* tack on all the hard defined gateways as well */
281
	if(is_array($config['gateways']['gateway_item'])) {
282
		foreach($config['gateways']['gateway_item'] as $gateway) {
283
			if($gateway['gateway'] == "dynamic") {
284
				$gateway['gateway'] = get_interface_gateway($gateway['interface']);
285
				/* no IP address found, set to dynamic */
286
				if(! is_ipaddr($gateway['gateway'])) {
287
					$gateway['gateway'] = "dynamic";
288
				}
289
				$gateway['dynamic'] = true;
290
			}
291
			if($gateway['monitor'] == "") {
292
				$gateway['monitor'] = $gateway['gateway'];
293
			}
294
			/* include the gateway index as the attribute */
295
			$gateway['friendlyiface'] = $gateway['interface'];
296
			$gateway['interface'] = convert_friendly_interface_to_real_interface_name($gateway['interface']);
297
			$gateway['attribute'] = "$i";
298

    
299
			$gateways_arr[$gateway['name']] = $gateway;
300
			$i++;
301
		}
302
	} 
303

    
304
	foreach($iflist as $ifname => $friendly ) {
305
		if(! interface_has_gateway($ifname)) {
306
			continue;
307
		}
308
		$gateway = array();
309
		$gateway['dynamic'] = false;
310
		$gateway['gateway'] = get_interface_gateway($ifname, $gateway['dynamic']);
311
		$gateway['interface'] = get_real_interface($ifname);
312
		$gateway['friendlyiface'] = $ifname;
313
		$gateway['name'] = "{$friendly}";
314
		$gateway['attribute'] = "system";
315
	
316
		/* Loopback dummy for dynamic interfaces without a IP */
317
		if(!is_ipaddr(trim($gateway['gateway'])) && $gateway['dynamic'] == true) {
318
			 $gateway['gateway'] = "dynamic";
319
		}
320

    
321
		/* automatically skip known static and dynamic gateways we have a array entry for */
322
		foreach($gateways_arr as $gateway_item) {
323
			if ($ifname == $gateway_item['friendlyiface'] || $friendly == $gateway_item['name']) {
324
				if ($gateway_item['gateway'] == $gateway['gateway'])
325
					continue 2;
326
				if ($gateway_item['gateway'] == "dynamic")
327
					continue 2;
328
			}
329
		}
330

    
331
		/* retrieve a proper monitor IP? */
332
		$interface = $config['interfaces'][$ifname];
333
		if(is_ipaddr($interface['monitorip'])) {
334
			$gateway['monitor'] = $interface['monitorip'];
335
		} else {
336
			$gateway['monitor'] = $gateway['gateway'];
337
		}
338
		$gateway['descr'] = "Interface $ifname Dynamic Gateway";
339
		$gateways_arr[$ifname] = $gateway;
340
		$i++;
341
	}
342

    
343
	return($gateways_arr);
344
}
345

    
346

    
347
/* return a array with all gateway groups with name as key
348
 * All gateway groups will be processed before returning the array.
349
*/
350
function return_gateway_groups_array() {
351
	global $config, $g;
352

    
353
	/* fetch the current gateways status */
354
	$gateways_status = return_gateways_status();
355
	$gateways_arr = return_gateways_array();
356
	$gateway_groups_array = array();
357

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

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

    
460
function lookup_gateway_ip_by_name($name) {
461

    
462
	$gateways_arr = return_gateways_array();
463
	if (!empty($gateways_arr[$name]))
464
		return $gateways_arr[$name]['gateway'];
465

    
466
	return false;
467
}
468

    
469
function lookup_gateway_monitor_ip_by_name($name) {
470

    
471
        $gateways_arr = return_gateways_array();
472
	if (!empty($gateways_arr[$name])) {
473
		$gateway = $gateways_arr[$name];
474
		if ($gateway['gateway'] == "dynamic")
475
			$gateway['monitor'] = "127.0.0.2";
476

    
477
		$monitorip = $gateway['monitor'];
478
		if($monitorip == "")
479
			$monitorip = $gateway['gateway'];
480

    
481
		return ($monitorip);
482
        }
483

    
484
        return (false);
485
}
486

    
487
function lookup_gateway_interface_by_name($name) {
488

    
489
        $gateways_arr = return_gateways_array();
490
	if (!empty($gateways_arr[$name])) {
491
		//$gatewayip = $gateway['gateway'];
492
		$interfacegw = $gateway['interface'];
493
		return ($interfacegw);
494
        }
495

    
496
        return (false);
497
}
498

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

    
502
        $gw = NULL;
503

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

    
516
        // for dynamic interfaces we handle them through the $interface_router file.
517
        if (!is_ipaddr($gw) && !is_ipaddr($gwcfg['ipaddr'])) {
518
                $realif = get_real_interface($interface);
519
                if (file_exists("{$g['tmp_path']}/{$realif}_router")) {
520
                        $gw = file_get_contents("{$g['tmp_path']}/{$realif}_router");
521
                        $gw = rtrim($gw);
522
			$dynamic = true;
523
                }
524
        }
525

    
526
        /* return gateway */
527
        return ($gw);
528
}
529

    
530
?>
(20-20/50)