Project

General

Profile

Download (36.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:	/sbin/route	/usr/local/sbin/apinger
29
	pfSense_MODULE:	routing
30

    
31
 */
32
require_once("config.inc");
33
require_once("rrd.inc");
34

    
35
/* Returns an array of default values used for apinger.conf */
36
function return_apinger_defaults() {
37
	return array(
38
		"latencylow" => "200",
39
		"latencyhigh" => "500",
40
		"losslow" => "10",
41
		"losshigh" => "20",
42
		"interval" => "1",
43
		"down" => "10",
44
		"avg_delay_samples" => "10",
45
		"avg_loss_samples" => "50",
46
		"avg_loss_delay_samples" => "20");
47
}
48

    
49
/*
50
 * Creates monitoring configuration file and
51
 * adds appropriate static routes.
52
 */
53
function setup_gateways_monitor() {
54
	global $config, $g;
55

    
56
	$gateways_arr = return_gateways_array();
57
	if (!is_array($gateways_arr)) {
58
		log_error("No gateways to monitor. Apinger will not be run.");
59
		killbypid("{$g['varrun_path']}/apinger.pid");
60
		@unlink("{$g['varrun_path']}/apinger.status");
61
		return;
62
	}
63

    
64
	$apinger_debug = "";
65
	if (isset($config['system']['apinger_debug']))
66
		$apinger_debug = "debug on";
67

    
68
	$apinger_default = return_apinger_defaults();
69
	$apingerconfig = <<<EOD
70

    
71
# pfSense apinger configuration file. Automatically Generated!
72

    
73
{$apinger_debug}
74

    
75
## User and group the pinger should run as
76
user "root"
77
group "wheel"
78

    
79
## Mailer to use (default: "/usr/lib/sendmail -t")
80
#mailer "/var/qmail/bin/qmail-inject"
81

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

    
85
## Format of timestamp (%s macro) (default: "%b %d %H:%M:%S")
86
#timestamp_format "%Y%m%d%H%M%S"
87

    
88
status {
89
	## File where the status information should be written to
90
	file "{$g['varrun_path']}/apinger.status"
91
	## Interval between file updates
92
	## when 0 or not set, file is written only when SIGUSR1 is received
93
	interval 5s
94
}
95

    
96
########################################
97
# RRDTool status gathering configuration
98
# Interval between RRD updates
99
rrd interval 60s;
100

    
101
## These parameters can be overridden in a specific alarm configuration
102
alarm default {
103
	command on "/usr/local/sbin/pfSctl -c 'service reload dyndns %T' -c 'service reload ipsecdns' -c 'service reload openvpn %T' -c 'filter reload' "
104
	command off "/usr/local/sbin/pfSctl -c 'service reload dyndns %T' -c 'service reload ipsecdns' -c 'service reload openvpn %T' -c 'filter reload' "
105
	combine 10s
106
}
107

    
108
## "Down" alarm definition.
109
## This alarm will be fired when target doesn't respond for 30 seconds.
110
alarm down "down" {
111
	time {$apinger_default['down']}s
112
}
113

    
114
## "Delay" alarm definition.
115
## This alarm will be fired when responses are delayed more than 200ms
116
## it will be canceled, when the delay drops below 100ms
117
alarm delay "delay" {
118
	delay_low {$apinger_default['latencylow']}ms
119
	delay_high {$apinger_default['latencyhigh']}ms
120
}
121

    
122
## "Loss" alarm definition.
123
## This alarm will be fired when packet loss goes over 20%
124
## it will be canceled, when the loss drops below 10%
125
alarm loss "loss" {
126
	percent_low {$apinger_default['losslow']}
127
	percent_high {$apinger_default['losshigh']}
128
}
129

    
130
target default {
131
	## How often the probe should be sent
132
	interval {$apinger_default['interval']}s
133

    
134
	## How many replies should be used to compute average delay
135
	## for controlling "delay" alarms
136
	avg_delay_samples {$apinger_default['avg_delay_samples']}
137

    
138
	## How many probes should be used to compute average loss
139
	avg_loss_samples {$apinger_default['avg_loss_samples']}
140

    
141
	## The delay (in samples) after which loss is computed
142
	## without this delays larger than interval would be treated as loss
143
	avg_loss_delay_samples {$apinger_default['avg_loss_delay_samples']}
144

    
145
	## Names of the alarms that may be generated for the target
146
	alarms "down","delay","loss"
147

    
148
	## Location of the RRD
149
	#rrd file "{$g['vardb_path']}/rrd/apinger-%t.rrd"
150
}
151

    
152
EOD;
153

    
154
	$monitor_ips = array();
155
	foreach($gateways_arr as $name => $gateway) {
156
		/* Do not monitor if such was requested */
157
		if (isset($gateway['monitor_disable']))
158
			continue;
159
		if (empty($gateway['monitor']) || !is_ipaddr($gateway['monitor'])) {
160
			if (is_ipaddr($gateway['gateway']))
161
				$gateway['monitor'] = $gateway['gateway'];
162
			else /* No chance to get an ip to monitor skip target. */
163
				continue;
164
		}
165

    
166
		/* if the monitor address is already used before, skip */
167
		if(in_array($gateway['monitor'], $monitor_ips))
168
			continue;
169

    
170
		/* Interface ip is needed since apinger will bind a socket to it. 
171
		 * However the config GUI should already have checked this and when 
172
		 * PPoE is used the IP address is set to "dynamic". So using is_ipaddrv4
173
		 * or is_ipaddrv6 to identify packet type would be wrong, especially as
174
		 * further checks (that can cope with the "dynamic" case) are present inside
175
		 * the if block. So using $gateway['ipprotocol'] is the better option.
176
		 */
177
		if ($gateway['ipprotocol'] == "inet") { // This is an IPv4 gateway...
178
			$gwifip = find_interface_ip($gateway['interface'], true);
179
			if (!is_ipaddrv4($gwifip))
180
				continue; //Skip this target
181

    
182
			/*
183
			 * If the gateway is the same as the monitor we do not add a
184
			 * route as this will break the routing table.
185
			 * Add static routes for each gateway with their monitor IP
186
			 * not strictly necessary but is a added level of protection.
187
			 */
188
			if (is_ipaddrv4($gateway['gateway']) && $gateway['monitor'] != $gateway['gateway']) {
189
				log_error("Removing static route for monitor {$gateway['monitor']} and adding a new route through {$gateway['gateway']}");
190
				mwexec("/sbin/route change -host " . escapeshellarg($gateway['monitor']) .
191
					" " . escapeshellarg($gateway['gateway']), true);
192
			}
193
		} else if ($gateway['ipprotocol'] == "inet6") { // This is an IPv6 gateway...
194
			if ($gateway['monitor'] == $gateway['gateway']) {
195
				/* link locals really need a different src ip */
196
				if (is_linklocal($gateway['gateway'])) {
197
					if (!strpos($gateway['gateway'], '%'))
198
						$gateway['gateway'] .= '%' . $gateway['interface'];
199
					$gwifip = find_interface_ipv6_ll($gateway['interface'], true);
200
				} else {
201
					$gwifip = find_interface_ipv6($gateway['interface'], true);
202
				}
203
			} else {
204
				/* 'monitor' has been set, so makes sure it has precedence over
205
				 * 'gateway' in defining the source IP. Otherwise if 'gateway'
206
				 * is a local link and 'monitor' is global routable then the
207
				 * ICMP6 response would not find its way back home...
208
				 */
209
				$gwifip = find_interface_ipv6($gateway['interface'], true);
210
			}
211

    
212
			/* Make sure srcip and target have scope defined when they are ll */
213
			if (is_linklocal($gwifip) && !strpos($gwifip, '%'))
214
				$gwifip .= '%' . $gateway['interface'];
215
			if (is_linklocal($gateway['monitor']) && !strpos($gateway['monitor'], '%'))
216
				$gateway['monitor'] .= "%{$gateway['interface']}";
217

    
218
			if (!is_ipaddrv6($gwifip))
219
				continue; //Skip this target
220

    
221
			/*
222
			 * If the gateway is the same as the monitor we do not add a
223
			 * route as this will break the routing table.
224
			 * Add static routes for each gateway with their monitor IP
225
			 * not strictly necessary but is a added level of protection.
226
			 */
227
			if ($gateway['gateway'] != $gateway['monitor']) {
228
				log_error("Removing static route for monitor {$gateway['monitor']} and adding a new route through {$gateway['gateway']}");
229
				mwexec("/sbin/route change -host -inet6 " . escapeshellarg($gateway['monitor']) .
230
					" " . escapeshellarg($gateway['gateway']), true);
231
			}
232
		} else
233
			continue;
234

    
235
		$monitor_ips[] = $gateway['monitor'];
236
		$apingercfg = "target \"{$gateway['monitor']}\" {\n";
237
		$apingercfg .= "	description \"{$name}\"\n";
238
		$apingercfg .= "	srcip \"{$gwifip}\"\n";
239

    
240
		## How often the probe should be sent
241
		if (!empty($gateway['interval']) &&  is_numeric($gateway['interval'])) {
242
			$interval = intval($gateway['interval']);	# Restrict to Integer
243
			if ($interval <  1) $interval =  1;	# Minimum
244
			if ($interval != $apinger_default['interval'])	# If not default value
245
				$apingercfg .= "	interval " . $interval . "s\n";
246
		}
247

    
248
		## How many replies should be used to compute average delay 
249
		## for controlling "delay" alarms
250
		if (!empty($gateway['avg_delay_samples']) && is_numeric($gateway['avg_delay_samples'])) {
251
			$avg_delay_samples = intval($gateway['avg_delay_samples']);	# Restrict to Integer
252
			if ($avg_delay_samples <  1) $avg_delay_samples =  1;	# Minimum
253
			if ($avg_delay_samples != $apinger_default['avg_delay_samples'])	# If not default value
254
				$apingercfg .= "	avg_delay_samples " . $avg_delay_samples . "\n";
255
		}
256

    
257
		## How many probes should be used to compute average loss
258
		if (!empty($gateway['avg_loss_samples']) && is_numeric($gateway['avg_loss_samples'])) {
259
			$avg_loss_samples = intval($gateway['avg_loss_samples']);	# Restrict to Integer
260
			if ($avg_loss_samples < 1) $avg_loss_samples = 1;	# Minimum
261
			if ($avg_loss_samples != $apinger_default['avg_loss_samples'])	# If not default value
262
				$apingercfg .= "	avg_loss_samples " . $avg_loss_samples . "\n";
263
		}
264

    
265
		## The delay (in samples) after which loss is computed
266
		## without this delays larger than interval would be treated as loss
267
		if (!empty($gateway['avg_loss_delay_samples']) && is_numeric($gateway['avg_loss_delay_samples'])) {
268
			$avg_loss_delay_samples = intval($gateway['avg_loss_delay_samples']);	# Restrict to Integer
269
			if ($avg_loss_delay_samples < 1) $avg_loss_delay_samples = 1;	# Minimum
270
			if ($avg_loss_delay_samples != $apinger_default['avg_loss_delay_samples'])	# If not default value
271
				$apingercfg .= "	avg_loss_delay_samples " . $avg_loss_delay_samples . "\n";
272
		}
273

    
274
		$alarms = "";
275
		$alarmscfg = "";
276
		$override = false;
277
		if (!empty($gateway['losslow'])) {
278
			$alarmscfg .= "alarm loss \"{$name}loss\" {\n";
279
			$alarmscfg .= "\tpercent_low {$gateway['losslow']}\n";
280
			$alarmscfg .= "\tpercent_high {$gateway['losshigh']}\n";
281
			$alarmscfg .= "}\n";
282
			$alarms .= "\"{$name}loss\"";
283
			$override = true;
284
		} else {
285
			if ($override == true)
286
				$alarms .= ",";
287
			$alarms .= "\"loss\"";
288
			$override = true;
289
		}
290
		if (!empty($gateway['latencylow'])) {
291
			$alarmscfg .= "alarm delay \"{$name}delay\" {\n";
292
			$alarmscfg .= "\tdelay_low {$gateway['latencylow']}ms\n";
293
			$alarmscfg .= "\tdelay_high {$gateway['latencyhigh']}ms\n";
294
			$alarmscfg .= "}\n";
295
			if ($override == true)
296
				$alarms .= ",";
297
			$alarms .= "\"{$name}delay\"";
298
			$override = true;
299
		} else {
300
			if ($override == true)
301
				$alarms .= ",";
302
			$alarms .= "\"delay\"";
303
			$override = true;
304
		}
305
		if (!empty($gateway['down'])) {
306
			$alarmscfg .= "alarm down \"{$name}down\" {\n";
307
			$alarmscfg .= "\ttime {$gateway['down']}s\n";
308
			$alarmscfg .= "}\n";
309
			if ($override == true)
310
				$alarms .= ",";
311
			$alarms .= "\"{$name}down\"";
312
			$override = true;
313
		} else {
314
			if ($override == true)
315
				$alarms .= ",";
316
			$alarms .= "\"down\"";
317
			$override = true;
318
		}
319
		if ($override == true)
320
			$apingercfg .= "\talarms override {$alarms};\n";
321

    
322
		if (isset($gateway['force_down']))
323
			$apingercfg .= "\tforce_down on\n";
324

    
325
		$apingercfg .= "	rrd file \"{$g['vardb_path']}/rrd/{$gateway['name']}-quality.rrd\"\n";
326
		$apingercfg .= "}\n";
327
		$apingercfg .= "\n";
328

    
329
		$apingerconfig .= $alarmscfg;
330
		$apingerconfig .= $apingercfg;
331

    
332
		# Create gateway quality RRD with settings more suitable for pfSense graph set, 
333
		# since apinger uses default step (300; 5 minutes) and other settings that don't 
334
		# match the pfSense gateway quality graph set.
335
		create_gateway_quality_rrd("{$g['vardb_path']}/rrd/{$gateway['name']}-quality.rrd");
336
	}
337
	@file_put_contents("{$g['varetc_path']}/apinger.conf", $apingerconfig);
338
	unset($apingerconfig);
339

    
340
	if (is_dir("{$g['tmp_path']}"))
341
		chmod("{$g['tmp_path']}", 01777);
342
	if (!is_dir("{$g['vardb_path']}/rrd"))
343
		mkdir("{$g['vardb_path']}/rrd", 0775);
344

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

    
347
 	/* Restart apinger process */
348
	if (isvalidpid("{$g['varrun_path']}/apinger.pid"))
349
		sigkillbypid("{$g['varrun_path']}/apinger.pid", "HUP");
350
	else {
351
		/* start a new apinger process */
352
		@unlink("{$g['varrun_path']}/apinger.status");
353
		sleep(1);
354
		mwexec_bg("/usr/local/sbin/apinger -c {$g['varetc_path']}/apinger.conf");
355
		sleep(1);
356
		sigkillbypid("{$g['varrun_path']}/apinger.pid", "USR1");
357
	}
358

    
359
	return 0;
360
}
361

    
362
/* return the status of the apinger targets as a array */
363
function return_gateways_status($byname = false) {
364
	global $config, $g;
365

    
366
	$apingerstatus = array();
367
	/* Always get the latest status from apinger */
368
	if (file_exists("{$g['varrun_path']}/apinger.pid"))
369
                sigkillbypid("{$g['varrun_path']}/apinger.pid", "USR1");
370
	if (file_exists("{$g['varrun_path']}/apinger.status")) {
371
		$apingerstatus = file("{$g['varrun_path']}/apinger.status");
372
	} else
373
		$apingerstatus = array();
374

    
375
	$status = array();
376
	foreach($apingerstatus as $line) {
377
		$info = explode("|", $line);
378
		if ($byname == false)
379
			$target = $info[0];
380
		else
381
			$target = $info[2];
382

    
383
		$status[$target] = array();
384
		$status[$target]['monitorip'] = $info[0];
385
		$status[$target]['srcip'] = $info[1];
386
		$status[$target]['name'] = $info[2];
387
		$status[$target]['lastcheck'] = $info[5] ? date('r', $info[5]) : date('r');
388
		$status[$target]['delay'] = empty($info[6]) ? "0ms" : round($info[6], 1) ."ms" ;
389
		$status[$target]['loss'] = empty($info[7]) ? "0.0%" : round($info[7], 1) . "%";
390
		$status[$target]['status'] = trim($info[8]);
391
	}
392

    
393
	/* tack on any gateways that have monitoring disabled
394
	 * or are down, which could cause gateway groups to fail */
395
	$gateways_arr = return_gateways_array();
396
	foreach($gateways_arr as $gwitem) {
397
		if(!isset($gwitem['monitor_disable']))
398
			continue;
399
		if(!is_ipaddr($gwitem['monitorip'])) {
400
			$realif = $gwitem['interface'];
401
			$tgtip = get_interface_gateway($realif);
402
			if (!is_ipaddr($tgtip))
403
				$tgtip = "none";
404
			$srcip = find_interface_ip($realif);
405
		} else {
406
			$tgtip = $gwitem['monitorip'];
407
			$srcip = find_interface_ip($realif);
408
		}
409
		if($byname == true)
410
			$target = $gwitem['name'];
411
		else
412
			$target = $tgtip;
413

    
414
		/* failsafe for down interfaces */
415
		if($target == "none") {
416
			$target = $gwitem['name'];
417
			$status[$target]['name'] = $gwitem['name'];
418
			$status[$target]['lastcheck'] = date('r');
419
			$status[$target]['delay'] = "0.0ms";
420
			$status[$target]['loss'] = "100.0%";
421
			$status[$target]['status'] = "down";
422
		} else {
423
			$status[$target]['monitorip'] = $tgtip;
424
			$status[$target]['srcip'] = $srcip;
425
			$status[$target]['name'] = $gwitem['name'];
426
			$status[$target]['lastcheck'] = date('r');
427
			$status[$target]['delay'] = "0.0ms";
428
			$status[$target]['loss'] = "0.0%";
429
			$status[$target]['status'] = "none";
430
		}
431
	}
432
	return($status);
433
}
434

    
435
/* Return all configured gateways on the system */
436
function return_gateways_array($disabled = false, $localhost = false, $inactive = false) {
437
	global $config, $g;
438

    
439
	$gateways_arr = array();
440

    
441
	$found_defaultv4 = 0;
442
	$found_defaultv6 = 0;
443

    
444
	// Ensure the interface cache is up to date first
445
	$interfaces = get_interface_arr(true);
446
	$interfaces_v4 = array();
447
	$interfaces_v6 = array();
448

    
449
	$i = -1;
450
	/* Process/add all the configured gateways. */
451
	if (is_array($config['gateways']['gateway_item'])) {
452
		foreach ($config['gateways']['gateway_item'] as $gateway) {
453
			/* Increment it here to do not skip items */
454
			$i++;
455

    
456
			if (empty($config['interfaces'][$gateway['interface']])) {
457
				if ($inactive === false)
458
					continue;
459
				else
460
					$gateway['inactive'] = true;
461
			}
462
			$wancfg = $config['interfaces'][$gateway['interface']];
463

    
464
			/* skip disabled interfaces */
465
			if ($disabled === false && (!isset($wancfg['enable']) || isset($gateway['disabled'])))
466
				continue;
467

    
468
			/* if the gateway is dynamic and we can find the IPv4, Great! */
469
			if (empty($gateway['gateway']) || $gateway['gateway'] == "dynamic") {
470
				if ($gateway['ipprotocol'] == "inet") {
471
					/* we know which interfaces is dynamic, this should be made a function */
472
					$gateway['gateway'] = get_interface_gateway($gateway['interface']);
473
					/* no IP address found, set to dynamic */
474
					if (!is_ipaddrv4($gateway['gateway']))
475
						$gateway['gateway'] = "dynamic";
476
					$gateway['dynamic'] = true;
477
				}
478

    
479
				/* if the gateway is dynamic and we can find the IPv6, Great! */
480
				else if ($gateway['ipprotocol'] == "inet6") {
481
					/* we know which interfaces is dynamic, this should be made a function, and for v6 too */
482
					$gateway['gateway'] = get_interface_gateway_v6($gateway['interface']);
483
					/* no IPv6 address found, set to dynamic */
484
					if (!is_ipaddrv6($gateway['gateway']))
485
						$gateway['gateway'] = "dynamic";
486
					$gateway['dynamic'] = true;
487
				}
488
			} else {
489
				/* getting this detection right is hard at this point because we still don't
490
				 * store the address family in the gateway item */
491
				if (is_ipaddrv4($gateway['gateway']))
492
					$gateway['ipprotocol'] = "inet";
493
				else if(is_ipaddrv6($gateway['gateway']))
494
					$gateway['ipprotocol'] = "inet6";
495
			}
496

    
497
			if (isset($gateway['monitor_disable']))
498
				$gateway['monitor_disable'] = true;
499
			else if (empty($gateway['monitor']))
500
				$gateway['monitor'] = $gateway['gateway'];
501

    
502
			$gateway['friendlyiface'] = $gateway['interface'];
503

    
504
			/* special treatment for tunnel interfaces */
505
			if ($gateway['ipprotocol'] == "inet6") {
506
				$gateway['interface'] = get_real_interface($gateway['interface'], "inet6", false, false);
507
				$interfaces_v6[$gateway['friendlyiface']] = $gateway['friendlyiface'];
508
			} else {
509
				$gateway['interface'] = get_real_interface($gateway['interface'], "all", false, false);
510
				$interfaces_v4[$gateway['friendlyiface']] = $gateway['friendlyiface'];
511
			}
512

    
513
			/* entry has a default flag, use it */
514
			if (isset($gateway['defaultgw'])) {
515
				if ($gateway['ipprotocol'] == "inet") {
516
					$gateway['defaultgw'] = true;
517
					$found_defaultv4 = 1;
518
				} else if ($gateway['ipprotocol'] == "inet6") {
519
					$gateway['defaultgw'] = true;
520
					$found_defaultv6 = 1;
521
				}
522
			}
523
			/* include the gateway index as the attribute */
524
			$gateway['attribute'] = $i;
525

    
526
			$gateways_arr[$gateway['name']] = $gateway;
527
		}
528
	}
529
	unset($gateway);
530

    
531
	/* Loop through all interfaces with a gateway and add it to a array */
532
	if ($disabled == false)
533
		$iflist = get_configured_interface_with_descr();
534
	else
535
		$iflist = get_configured_interface_with_descr(false, true);
536

    
537
	/* Process/add dynamic v4 gateways. */
538
	foreach($iflist as $ifname => $friendly ) {
539
		if(! interface_has_gateway($ifname))
540
			continue;
541

    
542
		if (empty($config['interfaces'][$ifname]))
543
			continue;
544

    
545
		$ifcfg = &$config['interfaces'][$ifname];
546
		if(!isset($ifcfg['enable']))
547
			continue;
548

    
549
		if(!empty($ifcfg['ipaddr']) && is_ipaddrv4($ifcfg['ipaddr']))
550
			continue;
551

    
552
		if (isset($interfaces_v4[$ifname]))
553
			continue;
554

    
555
		$ctype = "";
556
		switch($ifcfg['ipaddr']) {
557
			case "dhcp":
558
			case "pppoe":
559
			case "pptp":
560
			case "ppp":
561
				$ctype = strtoupper($ifcfg['ipaddr']);
562
				break;
563
			default:
564
				$tunnelif = substr($ifcfg['if'], 0, 3);
565
				if (substr($ifcfg['if'], 0, 4) ==  "ovpn") {
566
					// if current iface is an ovpn server endpoint then skip it
567
					if (substr($ifcfg['if'], 4, 1) == 's')
568
						continue 2;							
569

    
570
					$ctype = "VPNv4";
571
				} else if ($tunnelif == "gif" || $tunnelif == "gre")
572
					$ctype = "TUNNELv4";
573
				break;
574
		}
575
		$ctype = "_". strtoupper($ctype);
576

    
577
		$gateway = array();
578
		$gateway['dynamic'] = false;
579
		$gateway['ipprotocol'] = "inet";
580
		$gateway['gateway'] = get_interface_gateway($ifname, $gateway['dynamic']);
581
		$gateway['interface'] = get_real_interface($ifname);
582
		$gateway['friendlyiface'] = $ifname;
583
		$gateway['name'] = "{$friendly}{$ctype}";
584
		$gateway['attribute'] = "system";
585

    
586
		if (($gateway['dynamic'] === "default") && ($found_defaultv4 == 0)) {
587
			$gateway['defaultgw'] = true;
588
			$gateway['dynamic'] = true;
589
			$found_defaultv4 = 1;
590
		}
591
		/* Loopback dummy for dynamic interfaces without a IP */
592
		if (!is_ipaddrv4($gateway['gateway']) && $gateway['dynamic'] == true)
593
			$gateway['gateway'] = "dynamic";
594

    
595
		/* automatically skip known static and dynamic gateways we have a array entry for */
596
		foreach($gateways_arr as $gateway_item) {
597
			if ((($ifname == $gateway_item['friendlyiface'] && $friendly == $gateway_item['name'])&& ($gateway['ipprotocol'] == $gateway_item['ipprotocol'])) ||
598
				($ifname == $gateway_item['friendlyiface'] && $gateway_item['dynamic'] == true) && ($gateway['ipprotocol'] == $gateway_item['ipprotocol']))
599
					continue 2;
600
		}
601

    
602
		if (is_ipaddrv4($gateway['gateway']))
603
			$gateway['monitor'] = $gateway['gateway'];
604

    
605
		$gateway['descr'] = "Interface {$friendly}{$ctype} Gateway";
606
		$gateways_arr[$gateway['name']] = $gateway;
607
	}
608
	unset($gateway);
609

    
610
	/* Process/add dynamic v6 gateways. */
611
	foreach($iflist as $ifname => $friendly ) {
612
		/* If the user has disabled IPv6, they probably don't want any IPv6 gateways. */
613
		if (!isset($config['system']['ipv6allow']))
614
			break;
615

    
616
		if(! interface_has_gatewayv6($ifname))
617
			continue;
618

    
619
		if (empty($config['interfaces'][$ifname]))
620
			continue;
621

    
622
		$ifcfg = &$config['interfaces'][$ifname];
623
		if(!isset($ifcfg['enable']))
624
			continue;
625

    
626
		if(!empty($ifcfg['ipaddrv6']) && is_ipaddrv6($ifcfg['ipaddrv6']))
627
			continue;
628

    
629
		if(isset($interfaces_v6[$ifname]))
630
			continue;
631

    
632
		$ctype = "";
633
		switch($ifcfg['ipaddrv6']) {
634
			case "slaac":
635
			case "dhcp6":
636
			case "6to4":
637
			case "6rd":
638
				$ctype = strtoupper($ifcfg['ipaddrv6']);
639
				break;
640
			default:
641
				$tunnelif = substr($ifcfg['if'], 0, 3);
642
				if (substr($ifcfg['if'], 0, 4) ==  "ovpn") {
643
					// if current iface is an ovpn server endpoint then skip it
644
					if (substr($ifcfg['if'], 4, 1) == 's') 
645
						continue 2;							
646

    
647
					$ctype = "VPNv6";
648
				} else if ($tunnelif == "gif" || $tunnelif == "gre")
649
					$ctype = "TUNNELv6";
650
				break;
651
		}
652
		$ctype = "_". strtoupper($ctype);
653

    
654
		$gateway = array();
655
		$gateway['dynamic'] = false;
656
		$gateway['ipprotocol'] = "inet6";
657
		$gateway['gateway'] = get_interface_gateway_v6($ifname, $gateway['dynamic']);
658
		$gateway['interface'] = get_real_interface($ifname, "inet6");
659
		switch($ifcfg['ipaddrv6']) {
660
			case "6rd":
661
			case "6to4":
662
				$gateway['dynamic'] = "default";
663
				break;
664
		}
665
		$gateway['friendlyiface'] = $ifname;
666
		$gateway['name'] = "{$friendly}{$ctype}";
667
		$gateway['attribute'] = "system";
668

    
669
		if (($gateway['dynamic'] === "default")  && ($found_defaultv6 == 0)) {
670
			$gateway['defaultgw'] = true;
671
			$gateway['dynamic'] = true;
672
			$found_defaultv6 = 1;
673
		}
674

    
675
		/* Loopback dummy for dynamic interfaces without a IP */
676
		if (!is_ipaddrv6($gateway['gateway']) && $gateway['dynamic'] == true)
677
			$gateway['gateway'] = "dynamic";
678

    
679
		/* automatically skip known static and dynamic gateways we have a array entry for */
680
		foreach($gateways_arr as $gateway_item) {
681
			if ((($ifname == $gateway_item['friendlyiface'] && $friendly == $gateway_item['name']) && ($gateway['ipprotocol'] == $gateway_item['ipprotocol'])) ||
682
				($ifname == $gateway_item['friendlyiface'] && $gateway_item['dynamic'] == true) && ($gateway['ipprotocol'] == $gateway_item['ipprotocol']))
683
					continue 2;
684
		}
685

    
686
		if (is_ipaddrv6($gateway['gateway']))
687
			$gateway['monitor'] = $gateway['gateway'];
688

    
689
		$gateway['descr'] = "Interface {$friendly}{$ctype} Gateway";
690
		$gateways_arr[$gateway['name']] = $gateway;
691
	}
692
	unset($gateway);
693

    
694
	/* FIXME: Should this be enabled.
695
	 * Some interface like wan might be default but have no info recorded
696
	 * the config. */
697
	/* this is a fallback if all else fails and we want to get packets out @smos */
698
	if ($found_defaultv4 == 0 || $found_defaultv6 == 0) {
699
		foreach ($gateways_arr as &$gateway) {
700
			if (($gateway['friendlyiface'] == "wan") && ($found_defaultv4 == 0) && (!isset($gateway['ipprotocol']) || ($gateway['ipprotocol'] == "inet"))) {
701
				if (file_exists("{$g['tmp_path']}/{$gateway['interface']}_defaultgw")) {
702
					$gateway['defaultgw'] = true;
703
					$found_defaultv4 = 1;
704
				}
705
			}
706
			else if (($gateway['friendlyiface'] == "wan") && ($found_defaultv6 == 0) && ($gateway['ipprotocol'] == "inet6")) {
707
				if (file_exists("{$g['tmp_path']}/{$gateway['interface']}_defaultgwv6")) {
708
					$gateway['defaultgw'] = true;
709
					$found_defaultv6 = 1;
710
				}
711
			}
712
		}
713
	}
714

    
715
	if($localhost === true) {
716
		/* attach localhost for Null routes */
717
		$gwlo4 = array();
718
		$gwlo4['name'] = "Null4";
719
		$gwlo4['interface'] = "lo0";
720
		$gwlo4['ipprotocol'] = "inet";
721
		$gwlo4['gateway'] = "127.0.0.1";
722
		$gwlo6 = array();
723
		$gwlo6['name'] = "Null6";
724
		$gwlo6['interface'] = "lo0";
725
		$gwlo6['ipprotocol'] = "inet6";
726
		$gwlo6['gateway'] = "::1";
727
		$gateways_arr['Null4'] = $gwlo4;
728
		$gateways_arr['Null6'] = $gwlo6;
729
	}
730
	return($gateways_arr);
731
}
732

    
733
function fixup_default_gateway($ipprotocol, $gateways_status, $gateways_arr) {
734
	global $config, $g;
735
	/*
736
	 * NOTE: The code below is meant to replace the default gateway when it goes down.
737
	 *	This facilitates services running on pfSense itself and are not handled by a PBR to continue working.
738
	 */
739
	$upgw = '';
740
	$dfltgwname = '';
741
	$dfltgwdown = false;
742
	$dfltgwfound = false;
743
	foreach ($gateways_arr as $gwname => $gwsttng) {
744
		if (($gwsttng['ipprotocol'] == $ipprotocol) && isset($gwsttng['defaultgw'])) {
745
			$dfltgwfound = true;
746
			$dfltgwname = $gwname;
747
			if (!isset($gwsttng['monitor_disable']) && stristr($gateways_status[$gwname]['status'], "down"))
748
				$dfltgwdown = true;
749
		}
750
		/* Keep a record of the last up gateway */
751
		/* XXX: Blacklist lan for now since it might cause issues to those who have a gateway set for it */
752
		if (empty($upgw) && ($gwsttng['ipprotocol'] == $ipprotocol) && (isset($gwsttng['monitor_disable']) || !stristr($gateways_status[$gwname]['status'], "down")) && $gwsttng[$gwname]['friendlyiface'] != "lan")
753
			$upgw = $gwname;
754
		if ($dfltgwdown == true && !empty($upgw))
755
			break;
756
	}
757
	if ($dfltgwfound == false) {
758
		$gwname = convert_friendly_interface_to_friendly_descr("wan");
759
		if (!empty($gateways_status[$gwname]) && stristr($gateways_status[$gwname]['status'], "down"))
760
			$dfltgwdown = true;
761
	}
762
	if ($dfltgwdown == true && !empty($upgw)) {
763
		if ($gateways_arr[$upgw]['gateway'] == "dynamic")
764
			$gateways_arr[$upgw]['gateway'] = get_interface_gateway($gateways_arr[$upgw]['friendlyiface']);
765
		if (is_ipaddr($gateways_arr[$upgw]['gateway'])) {
766
			log_error("Default gateway down setting {$upgw} as default!");
767
			if(is_ipaddrv6($gateways_arr[$upgw]['gateway'])) {
768
				$inetfamily = "-inet6";
769
			} else {
770
				$inetfamily = "-inet";
771
			}
772
			mwexec("/sbin/route change {$inetfamily} default {$gateways_arr[$upgw]['gateway']}");
773
		}
774
	} else if (!empty($dftgwname)) {
775
		$defaultgw = trim(exec("/sbin/route -n get -{$ipprotocol} default | /usr/bin/awk '/gateway:/ {print $2}'"), " \n");
776
		if ($ipprotocol == 'inet6' && !is_ipaddrv6($gateways_arr[$dfltgwname]['gateway']))
777
			return;
778
		if ($ipprotocol == 'inet' && !is_ipaddrv4($gateways_arr[$dfltgwname]['gateway']))
779
			return;
780
		if ($defaultgw != $gateways_arr[$dfltgwname]['gateway'])
781
			mwexec("/sbin/route change -{$ipprotocol} default {$gateways_arr[$dfltgwname]['gateway']}");
782
	}
783
}
784

    
785
/*
786
 * Return an array with all gateway groups with name as key
787
 * All gateway groups will be processed before returning the array.
788
 */
789
function return_gateway_groups_array() {
790
	global $config, $g;
791

    
792
	/* fetch the current gateways status */
793
	$gateways_status = return_gateways_status(true);
794
	$gateways_arr = return_gateways_array();
795
	$gateway_groups_array = array();
796

    
797
	if (isset($config['system']['gw_switch_default'])) {
798
		fixup_default_gateway("inet", $gateways_status, $gateways_arr);
799
		fixup_default_gateway("inet6", $gateways_status, $gateways_arr);
800
	}
801
	if (is_array($config['gateways']['gateway_group'])) {
802
		$carplist = get_configured_carp_interface_list();
803
		foreach ($config['gateways']['gateway_group'] as $group) {
804
			/* create array with group gateways members separated by tier */
805
			$tiers = array();
806
			$backupplan = array();
807
			$gwvip_arr = array();
808
			foreach ($group['item'] as $item) {
809
				list($gwname, $tier, $vipname) = explode("|", $item);
810

    
811
				if (is_ipaddr($carplist[$vipname])) {
812
					if (!is_array($gwvip_arr[$group['name']]))
813
						$gwvip_arr[$group['name']] = array();
814
					$gwvip_arr[$group['name']][$gwname] = $vipname;
815
				}
816

    
817
				/* Do it here rather than reiterating again the group in case no member is up. */
818
				if (!is_array($backupplan[$tier]))
819
					$backupplan[$tier] = array();
820
				$backupplan[$tier][] = $gwname;
821

    
822
				/* check if the gateway is available before adding it to the array */
823
				if (is_array($gateways_status[$gwname])) {
824
					$status = $gateways_status[$gwname];
825
					$gwdown = false;
826
					if (stristr($status['status'], "down")) {
827
						$msg = sprintf(gettext("MONITOR: %s is down, omitting from routing group {$group['name']}"), $gwname);
828
						$gwdown = true;
829
					} else if (stristr($status['status'], "loss") && strstr($group['trigger'], "loss")) {
830
						/* packet loss */
831
						$msg = sprintf(gettext("MONITOR: %s has packet loss, omitting from routing group {$group['name']}"), $gwname);
832
						$gwdown = true;
833
					} else if (stristr($status['status'], "delay") && strstr($group['trigger'] , "latency")) {
834
						/* high latency */
835
						$msg = sprintf(gettext("MONITOR: %s has high latency, omitting from routing group {$group['name']}"), $gwname);
836
						$gwdown = true;
837
					}
838
					if ($gwdown == true) {
839
						log_error($msg);
840
						notify_via_growl($msg);
841
						notify_via_smtp($msg);
842
					} else {
843
						/* Online add member */
844
						if (!is_array($tiers[$tier]))
845
							$tiers[$tier] = array();
846
						$tiers[$tier][] = $gwname;
847
					}
848
				} else if (isset($gateways_arr[$gwname]['monitor_disable']))
849
					$tiers[$tier][] = $gwname;
850
			}
851
			$tiers_count = count($tiers);
852
			if ($tiers_count == 0) {
853
				/* Oh dear, we have no members! Engage Plan B */
854
				if (!$g['booting']) {
855
					$msg = gettext("Gateways status could not be determined, considering all as up/active. (Group: {$group['name']})");
856
					log_error($msg);
857
					notify_via_growl($msg);
858
					//notify_via_smtp($msg);
859
				}
860
				$tiers = $backupplan;
861
			}
862
			/* sort the tiers array by the tier key */
863
			ksort($tiers);
864

    
865
			/* we do not really foreach the tiers as we stop after the first tier */
866
			foreach ($tiers as $tieridx => $tier) {
867
				/* process all gateways in this tier */
868
				foreach ($tier as $member) {
869
					/* determine interface gateway */
870
					if (isset($gateways_arr[$member])) {
871
						$gateway = $gateways_arr[$member];
872
						$int = $gateway['interface'];
873
						$gatewayip = "";
874
						if(is_ipaddr($gateway['gateway']))
875
							$gatewayip = $gateway['gateway'];
876
						else if (!empty($int))
877
							$gatewayip = get_interface_gateway($gateway['friendlyiface']);
878

    
879
						if (!empty($int)) {
880
							$gateway_groups_array[$group['name']]['ipprotocol'] = $gateway['ipprotocol'];
881
							if (is_ipaddr($gatewayip)) {
882
								$groupmember = array();
883
								$groupmember['int']  = $int;
884
								$groupmember['gwip']  = $gatewayip;
885
								$groupmember['weight']  = isset($gateway['weight']) ? $gateway['weight'] : 1;
886
								if (is_array($gwvip_arr[$group['name']])&& !empty($gwvip_arr[$group['name']][$member]))
887
									$groupmember['vip'] = $gwvip_arr[$group['name']][$member];
888
								$gateway_groups_array[$group['name']][] = $groupmember;
889
							}
890
						}
891
					}
892
				}
893
				/* we should have the 1st available tier now, exit stage left */
894
				if (count($gateway_groups_array[$group['name']]) > 0)
895
					break;
896
				else
897
					log_error("GATEWAYS: Group {$group['name']} did not have any gateways up on tier {$tieridx}!");
898
			}
899
		}
900
	}
901

    
902
	return ($gateway_groups_array);
903
}
904

    
905
/* Update DHCP WAN Interface ip address in gateway group item */
906
function dhclient_update_gateway_groups_defaultroute($interface = "wan") {
907
	global $config, $g;
908
	foreach($config['gateways']['gateway_item'] as & $gw) {
909
		if($gw['interface'] == $interface) {
910
			$current_gw = get_interface_gateway($interface);
911
			if($gw['gateway'] <> $current_gw) {
912
				$gw['gateway'] = $current_gw;
913
				$changed = true;
914
			}
915
		}
916
	}
917
	if($changed && $current_gw)
918
		write_config(sprintf(gettext('Updating gateway group gateway for %1$s - new gateway is %2$s'), $interfac, $current_gw));
919
}
920

    
921
function lookup_gateway_ip_by_name($name) {
922

    
923
	$gateways_arr = return_gateways_array(false, true);
924
	foreach ($gateways_arr as $gname => $gw) {
925
		if ($gw['name'] === $name || $gname === $name)
926
			return $gw['gateway'];
927
	}
928

    
929
	return false;
930
}
931

    
932
function lookup_gateway_monitor_ip_by_name($name) {
933

    
934
	$gateways_arr = return_gateways_array(false, true);
935
	if (!empty($gateways_arr[$name])) {
936
		$gateway = $gateways_arr[$name];
937
		if(!is_ipaddr($gateway['monitor']))
938
			return $gateway['gateway'];
939

    
940
		return $gateway['monitor'];
941
	}
942

    
943
	return (false);
944
}
945

    
946
function lookup_gateway_interface_by_name($name) {
947

    
948
	$gateways_arr = return_gateways_array(false, true);
949
	if (!empty($gateways_arr[$name])) {
950
		$interfacegw = $gateways_arr[$name]['friendlyiface'];
951
		return ($interfacegw);
952
	}
953

    
954
	return (false);
955
}
956

    
957
function get_interface_gateway($interface, &$dynamic = false) {
958
	global $config, $g;
959

    
960
	$gw = NULL;
961

    
962
	$gwcfg = $config['interfaces'][$interface];
963
	if (!empty($gwcfg['gateway']) && is_array($config['gateways']['gateway_item'])) {
964
		foreach($config['gateways']['gateway_item'] as $gateway) {
965
			if(($gateway['name'] == $gwcfg['gateway']) && (is_ipaddrv4($gateway['gateway']))) {
966
				$gw = $gateway['gateway'];
967
				break;
968
			}
969
		}
970
	}
971

    
972
	// for dynamic interfaces we handle them through the $interface_router file.
973
	if (($gw == NULL || !is_ipaddrv4($gw)) && !is_ipaddrv4($gwcfg['ipaddr'])) {
974
		$realif = get_real_interface($interface);
975
		if (file_exists("{$g['tmp_path']}/{$realif}_router")) {
976
				$gw = trim(file_get_contents("{$g['tmp_path']}/{$realif}_router"), " \n");
977
			$dynamic = true;
978
		}
979
		if (file_exists("{$g['tmp_path']}/{$realif}_defaultgw"))
980
			$dynamic = "default";
981

    
982
	}
983

    
984
	/* return gateway */
985
	return ($gw);
986
}
987

    
988
function get_interface_gateway_v6($interface, &$dynamic = false) {
989
	global $config, $g;
990

    
991
	$gw = NULL;
992
	$gwcfg = $config['interfaces'][$interface];
993
	if (!empty($gwcfg['gatewayv6']) && is_array($config['gateways']['gateway_item'])) {
994
		foreach($config['gateways']['gateway_item'] as $gateway) {
995
			if(($gateway['name'] == $gwcfg['gatewayv6']) && (is_ipaddrv6($gateway['gateway']))) {
996
				$gw = $gateway['gateway'];
997
				break;
998
			}
999
		}
1000
	}
1001

    
1002
	// for dynamic interfaces we handle them through the $interface_router file.
1003
	if (($gw == NULL || !is_ipaddrv6($gw)) && !is_ipaddrv6($gwcfg['ipaddrv6'])) {
1004
			$realif = get_real_interface($interface);
1005
			if (file_exists("{$g['tmp_path']}/{$realif}_routerv6")) {
1006
				$gw = trim(file_get_contents("{$g['tmp_path']}/{$realif}_routerv6"), " \n");
1007
				$dynamic = true;
1008
			}
1009
			if (file_exists("{$g['tmp_path']}/{$realif}_defaultgwv6"))
1010
				$dynamic = "default";
1011

    
1012
	}
1013
	/* return gateway */
1014
	return ($gw);
1015
}
1016

    
1017
/* Check a IP address against a gateway IP or name
1018
 * to verify it's address family */
1019
function validate_address_family($ipaddr, $gwname) {
1020
	$v4ip = false;
1021
	$v6ip = false;
1022
	$v4gw = false;
1023
	$v6gw = false;
1024

    
1025
	if(is_ipaddrv4($ipaddr))
1026
		$v4ip = true;
1027
	if(is_ipaddrv6($ipaddr))
1028
		$v6ip = true;
1029
	if(is_ipaddrv4($gwname))
1030
		$v4gw = true;
1031
	if(is_ipaddrv6($gwname))
1032
		$v6gw = true;
1033

    
1034
	if($v4ip && $v4gw)
1035
		return true;
1036
	if($v6ip && $v6gw)
1037
		return true;
1038

    
1039
	/* still no match, carry on, lookup gateways */
1040
	if(is_ipaddrv4(lookup_gateway_ip_by_name($gwname)))
1041
		$v4gw = true;
1042
	if(is_ipaddrv6(lookup_gateway_ip_by_name($gwname)))
1043
		$v6gw = true;
1044

    
1045
	$gw_array = return_gateways_array();
1046
	if(is_array($gw_array[$gwname])) {
1047
		switch($gw_array[$gwname]['ipprotocol']) {
1048
			case "inet":
1049
				$v4gw = true;
1050
				break;
1051
			case "inet6":
1052
				$v6gw = true;
1053
				break;
1054
		}
1055
	}
1056

    
1057
	if($v4ip && $v4gw)
1058
		return true;
1059
	if($v6ip && $v6gw)
1060
		return true;
1061

    
1062
	return false;
1063
}
1064

    
1065
/* check if a interface is part of a gateway group */
1066
function interface_gateway_group_member($interface) {
1067
	global $config;
1068

    
1069
	if (is_array($config['gateways']['gateway_group']))
1070
		$groups = $config['gateways']['gateway_group'];
1071
	else
1072
		return false;
1073

    
1074
	$gateways_arr = return_gateways_array(false, true);
1075
	foreach($groups as $group) {
1076
		if(is_array($group['item'])) {
1077
			foreach($group['item'] as $item) {
1078
				$elements = explode("|", $item);
1079
				$gwname = $elements[0];
1080
				if ($interface == $gateways_arr[$gwname]['interface']) {
1081
					unset($gateways_arr);
1082
					return true;
1083
				}
1084
			}
1085
		}
1086
	}
1087
	unset($gateways_arr);
1088

    
1089
	return false;
1090
}
1091

    
1092
function gateway_is_gwgroup_member($name) {
1093
	global $config;
1094

    
1095
	if (is_array($config['gateways']['gateway_group']))
1096
		$groups = $config['gateways']['gateway_group'];
1097
	else
1098
		return false;
1099

    
1100
	$members = array();
1101
	foreach($groups as $group) {
1102
		if (is_array($group['item'])) {
1103
			foreach($group['item'] as $item) {
1104
				$elements = explode("|", $item);
1105
				$gwname = $elements[0];
1106
				if ($name == $elements[0])
1107
					$members[] = $group['name'];
1108
			}
1109
		}
1110
	}
1111

    
1112
	return $members;
1113
}
1114
?>
(25-25/68)