Project

General

Profile

Download (31.8 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
  Copyright (C) 2008 Bill Marquette, Seth Mos
4
  Copyright (C) 2010 Ermal Luçi
5
  All rights reserved.
6

    
7
  Redistribution and use in source and binary forms, with or without
8
  modification, are permitted provided that the following conditions are met:
9

    
10
  1. Redistributions of source code must retain the above copyright notice,
11
  this list of conditions and the following disclaimer.
12

    
13
  2. Redistributions in binary form must reproduce the above copyright
14
  notice, this list of conditions and the following disclaimer in the
15
  documentation and/or other materials provided with the distribution.
16

    
17
  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
18
  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
19
  AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
20
  AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
21
  OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22
  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23
  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24
  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25
  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26
  POSSIBILITY OF SUCH DAMAGE.
27

    
28
	pfSense_BUILDER_BINARIES:	/usr/bin/killall	/sbin/route	/usr/local/sbin/apinger
29
	pfSense_MODULE:	routing
30

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

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

    
45
/*
46
 * Creates monitoring configuration file and
47
 * adds appropriate static routes.
48
 */
49
function setup_gateways_monitor() {
50
	global $config, $g;
51

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

    
60
	$apinger_default = return_apinger_defaults();
61
	$fd = fopen("{$g['varetc_path']}/apinger.conf", "w");
62
	$apingerconfig = <<<EOD
63

    
64
# pfSense apinger configuration file. Automatically Generated!
65

    
66
## User and group the pinger should run as
67
user "root"
68
group "wheel"
69

    
70
## Mailer to use (default: "/usr/lib/sendmail -t")
71
#mailer "/var/qmail/bin/qmail-inject" 
72

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

    
76
## Format of timestamp (%s macro) (default: "%b %d %H:%M:%S")
77
#timestamp_format "%Y%m%d%H%M%S"
78

    
79
status {
80
	## File where the status information should be written to
81
	file "{$g['tmp_path']}/apinger.status"
82
	## Interval between file updates
83
	## when 0 or not set, file is written only when SIGUSR1 is received
84
	interval 5s
85
}
86

    
87
########################################
88
# RRDTool status gathering configuration
89
# Interval between RRD updates
90
rrd interval 60s;
91

    
92
## These parameters can be overridden in a specific alarm configuration
93
alarm default { 
94
	command on "/usr/local/sbin/pfSctl -c 'service reload dyndnsall' -c 'service reload ipsecdns' -c 'service reload openvpn' -c 'filter reload' "
95
	command off "/usr/local/sbin/pfSctl -c 'service reload dyndnsall' -c 'service reload ipsecdns' -c 'service reload openvpn' -c 'filter reload' "
96
	combine 10s
97
}
98

    
99
## "Down" alarm definition. 
100
## This alarm will be fired when target doesn't respond for 30 seconds.
101
alarm down "down" {
102
	time {$apinger_default['down']}s
103
}
104

    
105
## "Delay" alarm definition. 
106
## This alarm will be fired when responses are delayed more than 200ms
107
## it will be canceled, when the delay drops below 100ms
108
alarm delay "delay" {
109
	delay_low {$apinger_default['latencylow']}ms
110
	delay_high {$apinger_default['latencyhigh']}ms
111
}
112

    
113
## "Loss" alarm definition. 
114
## This alarm will be fired when packet loss goes over 20%
115
## it will be canceled, when the loss drops below 10%
116
alarm loss "loss" {
117
	percent_low {$apinger_default['losslow']}
118
	percent_high {$apinger_default['losshigh']}
119
}
120

    
121
target default {
122
	## How often the probe should be sent	
123
	interval {$apinger_default['interval']}s
124
	
125
	## How many replies should be used to compute average delay 
126
	## for controlling "delay" alarms
127
	avg_delay_samples 10
128
	
129
	## How many probes should be used to compute average loss
130
	avg_loss_samples 50
131

    
132
	## The delay (in samples) after which loss is computed
133
	## without this delays larger than interval would be treated as loss
134
	avg_loss_delay_samples 20
135

    
136
	## Names of the alarms that may be generated for the target
137
	alarms "down","delay","loss"
138

    
139
	## Location of the RRD
140
	#rrd file "{$g['vardb_path']}/rrd/apinger-%t.rrd"
141
}
142

    
143
EOD;
144

    
145
	$monitor_ips = array();
146
	foreach($gateways_arr as $name => $gateway) {
147
		/* Do not monitor if such was requested */
148
		if (isset($gateway['monitor_disable']))
149
			continue;
150
		if (empty($gateway['monitor']) || !is_ipaddr($gateway['monitor'])) {
151
			if (is_ipaddr($gateway['gateway']))
152
				$gateway['monitor'] = $gateway['gateway'];
153
			else /* No chance to get an ip to monitor skip target. */
154
				continue;
155
		}
156

    
157
		/* if the monitor address is already used before, skip */ 
158
		if(in_array($gateway['monitor'], $monitor_ips))
159
			continue;
160
		
161
		/* Interface ip is needed since apinger will bind a socket to it. */
162
		if (is_ipaddrv4($gateway['gateway'])) {
163
			$gwifip = find_interface_ip($gateway['interface'], true);
164
		}
165
		if (is_ipaddrv6($gateway['gateway'])) {
166
			/* link locals really need a different src ip */
167
			if(preg_match("/fe80::/i", $gateway['gateway'])) {
168
				$linklocal = explode("%", find_interface_ipv6_ll($gateway['interface'], true));
169
				$gwifip = $linklocal[0];
170
				$ifscope = "%". $linklocal[1];
171
			} else {
172
				$gwifip = find_interface_ipv6($gateway['interface'], true);
173
			}
174
		}
175
		if (!is_ipaddr($gwifip))
176
			continue; //Skip this target
177

    
178
		$monitor_ips[] = monitor_ips;
179
		$apingercfg = "target \"{$gateway['monitor']}\" {\n";
180
		$apingercfg .= "	description \"{$name}\"\n";
181
		$apingercfg .= "	srcip \"{$gwifip}\"\n";
182
		if (!empty($gateway['interval']) && intval($gateway['interval']) > 1)
183
			$apingercfg .= "	interval " . intval($gateway['interval']) . "s\n";
184
		$alarms = "";
185
		$alarmscfg = "";
186
		$override = false;
187
		if (!empty($gateway['losslow'])) {
188
			$alarmscfg .= "alarm loss \"{$name}loss\" {\n";
189
			$alarmscfg .= "\tpercent_low {$gateway['losslow']}\n";
190
       			$alarmscfg .= "\tpercent_high {$gateway['losshigh']}\n";
191
			$alarmscfg .= "}\n";
192
			$alarms .= "\"{$name}loss\"";
193
			$override = true;
194
		} else {
195
			if ($override == true)
196
				$alarms .= ",";
197
			$alarms .= "\"loss\"";
198
			$override = true;
199
		}
200
		if (!empty($gateway['latencylow'])) {
201
			$alarmscfg .= "alarm delay \"{$name}delay\" {\n";
202
			$alarmscfg .= "\tdelay_low {$gateway['latencylow']}ms\n";
203
			$alarmscfg .= "\tdelay_high {$gateway['latencyhigh']}ms\n";
204
			$alarmscfg .= "}\n";
205
			if ($override == true)
206
				$alarms .= ",";
207
			$alarms .= "\"{$name}delay\"";
208
			$override = true;
209
		} else {
210
			if ($override == true)
211
				$alarms .= ",";
212
			$alarms .= "\"delay\"";
213
			$override = true;
214
		}
215
		if (!empty($gateway['down'])) {
216
			$alarmscfg .= "alarm down \"{$name}down\" {\n";
217
			$alarmscfg .= "\ttime {$gateway['down']}s\n";
218
			$alarmscfg .= "}\n";
219
			if ($override == true)
220
				$alarms .= ",";
221
			$alarms .= "\"{$name}down\"";
222
			$override = true;
223
		} else {
224
			if ($override == true)
225
				$alarms .= ",";
226
			$alarms .= "\"down\"";
227
			$override = true;
228
		}
229
		if ($override == true)
230
			$apingercfg .= "\talarms override {$alarms};\n";
231

    
232
		$apingercfg .= "	rrd file \"{$g['vardb_path']}/rrd/{$gateway['name']}-quality.rrd\"\n";
233
		$apingercfg .= "}\n";
234
		$apingercfg .= "\n";
235
		/*
236
		 * If the gateway is the same as the monitor we do not add a
237
		 * route as this will break the routing table.
238
		 * Add static routes for each gateway with their monitor IP
239
		 * not strictly necessary but is a added level of protection.
240
		 */
241
		if (is_ipaddr($gateway['gateway']) && $gateway['monitor'] != $gateway['gateway']) {
242
			log_error(sprintf(gettext('Removing static route for monitor %1$s and adding a new route through %2$s'), $gateway['monitor'], $gateway['gateway']));
243
			if(is_ipaddrv6($gateway['gateway'])) {
244
				$inetfamily = "-inet6";
245
			} else {
246
				$inetfamily = "-inet";
247
			}
248
			// mwexec("/sbin/route change {$inetfamily} -host " . escapeshellarg($gateway['monitor']) .
249
			//	" " . escapeshellarg($gateway['gateway']), true);
250
		}
251

    
252
		$apingerconfig .= $alarmscfg;
253
		$apingerconfig .= $apingercfg;
254
	}
255
	fwrite($fd, $apingerconfig);
256
	fclose($fd);
257

    
258
	killbypid("{$g['varrun_path']}/apinger.pid");
259
	if (is_dir("{$g['tmp_path']}"))
260
		chmod("{$g['tmp_path']}", 01777);
261
	if (!is_dir("{$g['vardb_path']}/rrd"))
262
		mkdir("{$g['vardb_path']}/rrd", 0775);
263

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

    
266
	/* start a new apinger process */
267
	@unlink("{$g['tmp_path']}/apinger.status");
268
	sleep(1);
269
	mwexec_bg("/usr/local/sbin/apinger -c {$g['varetc_path']}/apinger.conf");
270

    
271
	return 0;
272
}
273

    
274
/* return the status of the apinger targets as a array */
275
function return_gateways_status($byname = false) {
276
	global $config, $g;
277

    
278
	$apingerstatus = array();
279
	if (file_exists("{$g['tmp_path']}/apinger.status")) {
280
		$apingerstatus = file("{$g['tmp_path']}/apinger.status");
281
	}
282

    
283
	$status = array();
284
	foreach($apingerstatus as $line) {
285
		$info = explode("|", $line);
286
		if ($byname == false)
287
			$target = $info[0];
288
		else
289
			$target = $info[2];
290

    
291
		$status[$target]['monitorip'] = $info[0];
292
		$status[$target]['srcip'] = $info[1];
293
		$status[$target]['name'] = $info[2];
294
		$status[$target]['lastcheck'] = $info[5] ? date('r', $info[5]) : date('r');
295
		$status[$target]['delay'] = empty($info[6]) ? "0ms" : round($info[6], 1) ."ms" ;
296
		$status[$target]['loss'] = empty($info[7]) ? "0.0%" : round($info[7], 1) . "%";
297
		$status[$target]['status'] = trim($info[8]);
298
	}
299

    
300
	/* tack on any gateways that have monitoring disabled
301
	 * or are down, which could cause gateway groups to fail */
302
	$gateways_arr = return_gateways_array();
303
	foreach($gateways_arr as $gwitem) {
304
		if(isset($gwitem['monitor_disable'])) {
305
			if(!is_ipaddr($gwitem['monitorip'])) {
306
				$realif = $gwitem['interface'];
307
				$tgtip = get_interface_gateway($realif);
308
				$srcip = find_interface_ip($realif);
309
			} else {
310
				$tgtip = $gwitem['monitorip'];
311
				$srcip = find_interface_ip($realif);
312
			}
313
			if($byname == true)
314
				$target = $gwitem['name'];
315
			else
316
				$target = $tgtip;
317

    
318
			/* failsafe for down interfaces */
319
			if($target == "") {
320
				$target = $gwitem['name'];
321
				$status[$target]['name'] = $gwitem['name'];
322
				$status[$target]['lastcheck'] = date('r');
323
				$status[$target]['delay'] = "0.0ms";
324
				$status[$target]['loss'] = "100.0%";
325
				$status[$target]['status'] = "down";
326
			} else {
327
				$status[$target]['monitorip'] = $tgtip;
328
				$status[$target]['srcip'] = $srcip;
329
				$status[$target]['name'] = $gwitem['name'];
330
				$status[$target]['lastcheck'] = date('r');
331
				$status[$target]['delay'] = "0.0ms";
332
				$status[$target]['loss'] = "0.0%";
333
				$status[$target]['status'] = "none";
334
			}
335
		}
336
	}
337
	return($status);
338
}
339

    
340
/* Return all configured gateways on the system */
341
function return_gateways_array($disabled = false, $localhost = false) {
342
	global $config, $g;
343

    
344
	$gateways_arr = array();
345

    
346
	$found_defaultv4 = 0;
347
	$found_defaultv6 = 0;
348

    
349
	$interfaces_v4 = array();
350
	$interfaces_v6 = array();
351

    
352
	$i = 0;
353
	/* Process/add all the configured gateways. */
354
	if (is_array($config['gateways']['gateway_item'])) {
355
		foreach($config['gateways']['gateway_item'] as $gateway) {
356
			/* skip disabled interfaces */
357
			if(!isset($config['interfaces'][$gateway['interface']]['enable']))
358
				continue;
359

    
360
			$wancfg = $config['interfaces'][$gateway['interface']];
361
			/* getting this detection right is hard at this point because we still don't
362
			 * store the address family in the gateway item */
363
			if(is_ipaddrv4($gateway['gateway']))
364
				$gateway['ipprotocol'] = "inet";
365
			if(is_ipaddrv6($gateway['gateway']))
366
				$gateway['ipprotocol'] = "inet6";
367
			if((preg_match("/dynamic/i", $gateway['gateway'])) && (!isset($gateway['ipprotocol']))) {
368
				if(is_ipaddrv4($gateway['gateway']))
369
					$gateway['ipprotocol'] = "inet";
370
				if(is_ipaddrv6($gateway['gateway']))
371
					$gateway['ipprotocol'] = "inet6";
372
			}
373
			if((preg_match("/dynamic/i", $gateway['monitor'])) && (!isset($gateway['ipprotocol']))) {
374
				if(is_ipaddrv4($gateway['monitor']))
375
					$gateway['ipprotocol'] = "inet";
376
				if(is_ipaddrv6($gateway['monitor']))
377
					$gateway['ipprotocol'] = "inet6";
378
			}
379

    
380
			/* if the gateway is dynamic and we can find the IPv4, Great! */
381
			if((empty($gateway['gateway']) || ($gateway['gateway'] == "dynamic")) && ($gateway['ipprotocol'] == "inet")) {
382
				/* we know which interfaces is dynamic, this should be made a function */
383
				switch($wancfg['ipaddr']) {
384
					case "dhcp":
385
					case "pppoe":
386
					case "pptp":
387
					case "ppp":
388
						$gateway['ipprotocol'] = "inet";
389
						$gateway['gateway'] = get_interface_gateway($gateway['interface']);
390
						if($gateway['gateway'] == "dynamic") {
391
							$dynstr = $gateway['gateway'];
392
						}
393
						/* no IP address found, set to dynamic */
394
						if(! is_ipaddrv4($gateway['gateway'])) {
395
							$gateway['gateway'] = "{$dynstr}";
396
						}
397
						$gateway['dynamic'] = true;
398
						break;
399
				}
400
			}
401

    
402
			/* if the gateway is dynamic6 and we can find the IPv6, Great! */
403
			if((empty($gateway['gateway']) || ($gateway['gateway'] == "dynamic")) && ($gateway['ipprotocol'] == "inet6")) {
404
				/* we know which interfaces is dynamic, this should be made a function, and for v6 too */
405
				switch($wancfg['ipaddrv6']) {
406
					case "6rd":
407
					case "6to4":
408
					case "dhcp6":
409
						$gateway['ipprotocol'] = "inet6";
410
						$gateway['gateway'] = get_interface_gateway_v6($gateway['interface']);
411
						if($gateway['gateway'] == "dynamic6") {
412
							$dynstr = $gateway['gateway'];
413
						}
414
						/* no IPv6 address found, set to dynamic6 */
415
						if(! is_ipaddrv6($gateway['gateway'])) {
416
							$gateway['gateway'] = "{$dynstr}";
417
						}
418
						$gateway['dynamic'] = true;
419
						break;
420
				}
421
			}
422

    
423
			if (isset($gateway['monitor_disable']))
424
				$gateway['monitor_disable'] = true;
425
			else if (empty($gateway['monitor']))
426
				$gateway['monitor'] = $gateway['gateway'];
427

    
428
			$gateway['friendlyiface'] = $gateway['interface'];
429

    
430
			/* special treatment for tunnel interfaces */
431
			if($gateway['ipprotocol'] == "inet6") {
432
				switch($wancfg['ipaddrv6']) {
433
					case "6rd":
434
					case "6to4":
435
						$gateway['interface'] = "stf0";
436
						break;
437
					default:
438
						$gateway['interface'] = get_real_interface($gateway['interface']);
439
						break;
440
				}
441
			}
442
			if($gateway['ipprotocol'] == "inet") {
443
				$gateway['interface'] = get_real_interface($gateway['interface']);
444
			}
445

    
446
			/* entry has a default flag, use it */
447
			if (isset($gateway['defaultgw'])) {
448
				if($gateway['ipprotocol'] == "inet") {
449
					$gateway['defaultgw'] = true;
450
					$found_defaultv4 = 1;
451
				}
452
				if($gateway['ipprotocol'] == "inet6") {
453
					$gateway['defaultgw'] = true;
454
					$found_defaultv6 = 1;
455
				}
456
			}
457
			/* FIXME: Should this be enabled.
458
			 * Some interface like wan might be default but have no info recorded 
459
			 * the config. */
460
			/* this is a fallback if all else fails and we want to get packets out @smos */
461
			if (!isset($gateway['defaultgw'])) {
462
				if (($gateway['friendlyiface'] == "wan") && ($found_defaultv4 == 0)) {
463
					if (file_exists("{$g['tmp_path']}/{$gateway['interface']}_defaultgw")) {
464
						$gateway['defaultgw'] = true;
465
						$found_defaultv4 = 1;
466
					}
467
				}
468
				if (($gateway['friendlyiface'] == "wan") && ($found_defaultv6 == 0)) {
469
					if (file_exists("{$g['tmp_path']}/{$gateway['interface']}_defaultgwv6")) {
470
						$gateway['defaultgw'] = true;
471
						$found_defaultv6 = 1;
472
					}
473
				}
474
			}
475
			/* include the gateway index as the attribute */
476
			$gateway['attribute'] = $i;
477

    
478
			/* tack a item on the array to keep track of dynamic interfaces */
479
			if($gateway['ipprotocol'] == "inet")
480
				$interfaces_v4[] = $gateway['friendlyiface'];
481

    
482
			if($gateway['ipprotocol'] == "inet6")
483
				$interfaces_v6[] = $gateway['friendlyiface'];
484

    
485
			$gateways_arr[$gateway['name']] = $gateway;
486
			unset($gateway);
487
			$i++;
488
		}
489
	}
490

    
491
	/* Loop through all interfaces with a gateway and add it to a array */
492
	if ($disabled == false)
493
		$iflist = get_configured_interface_with_descr();
494
	else
495
		$iflist = get_configured_interface_with_descr(false, true);
496

    
497
	/* Process/add dynamic v4 gateways. */
498
	foreach($iflist as $ifname => $friendly ) {
499
		if(! interface_has_gateway($ifname))
500
			continue;
501

    
502
		if (empty($config['interfaces'][$ifname]))
503
			continue;
504

    
505
		$ifcfg = &$config['interfaces'][$ifname];
506
		if(!empty($ifcfg['ipaddr']) && is_ipaddrv4($ifcfg['ipaddr']))
507
			continue;
508

    
509
		if(!isset($ifcfg['enable']))
510
			continue;
511

    
512
		if(in_array($ifname, $interfaces_v4))
513
			continue;
514
			
515
		$ctype = "";
516
		switch($ifcfg['ipaddr']) {
517
			case "dhcp":
518
			case "pppoe":
519
			case "pptp":
520
			case "ppp":
521
				$ctype = strtoupper($ifcfg['ipaddr']);
522
				break;
523
			default:
524
				if (substr($ifcfg['if'], 0, 5) ==  "ovpnc")
525
					$ctype = "VPNv4";
526
				break;
527
		}
528
		$ctype = "_". strtoupper($ctype);
529

    
530
		$gateway = array();
531
		$gateway['dynamic'] = false;
532
		$gateway['ipprotocol'] = "inet";
533
		$gateway['gateway'] = get_interface_gateway($ifname, $gateway['dynamic']);
534
		$gateway['interface'] = get_real_interface($ifname);
535
		$gateway['friendlyiface'] = $ifname;
536
		$gateway['name'] = "{$friendly}{$ctype}";
537
		$gateway['attribute'] = "system";
538
	
539
		if (($gateway['dynamic'] === "default") && ($found_defaultv4 == 0)) {
540
			$gateway['defaultgw'] = true;
541
			$gateway['dynamic'] = true;
542
			$found_defaultv4 = 1;
543
		}
544
		/* Loopback dummy for dynamic interfaces without a IP */
545
		if (!is_ipaddrv4($gateway['gateway']) && $gateway['dynamic'] == true)
546
			$gateway['gateway'] = "dynamic";
547

    
548
		/* automatically skip known static and dynamic gateways we have a array entry for */
549
		foreach($gateways_arr as $gateway_item) {
550
			if ((($ifname == $gateway_item['friendlyiface'] && $friendly == $gateway_item['name'])&& ($gateway['ipprotocol'] == $gateway_item['ipprotocol'])) ||
551
				($ifname == $gateway_item['friendlyiface'] && $gateway_item['dynamic'] == true) && ($gateway['ipprotocol'] == $gateway_item['ipprotocol']))
552
					continue 2;
553
		}
554

    
555
		if (is_ipaddrv4($gateway['gateway']))
556
			$gateway['monitor'] = $gateway['gateway'];
557

    
558
		$gateway['descr'] = "Interface {$friendly}{$ctype} Gateway";
559
		$gateways_arr[$gateway['name']] = $gateway;
560
		unset($gateway);
561
	}
562

    
563
	/* Process/add dynamic v6 gateways. */
564
	foreach($iflist as $ifname => $friendly ) {
565
		if(! interface_has_gatewayv6($ifname))
566
			continue;
567

    
568
		if (empty($config['interfaces'][$ifname]))
569
			continue;
570

    
571
		$ifcfg = &$config['interfaces'][$ifname];
572
		if(!empty($ifcfg['ipaddrv6']) && is_ipaddrv6($ifcfg['ipaddrv6']))
573
			continue;
574
			
575
		if(!isset($ifcfg['enable']))
576
			continue;
577

    
578
		if(in_array($ifname, $interfaces_v6))
579
			continue;
580

    
581
		$ctype = "";
582
		switch($ifcfg['ipaddrv6']) {
583
			case "slaac":
584
			case "dhcp6":
585
			case "6to4":
586
			case "6rd":
587
				$ctype = strtoupper($ifcfg['ipaddrv6']);
588
				break;
589
			default:
590
				if (substr($ifcfg['if'], 0, 5) ==  "ovpnc")
591
					$ctype = "VPNv6";
592
				break;
593
		}
594
		$ctype = "_". strtoupper($ctype);
595

    
596
		$gateway = array();
597
		$gateway['dynamic'] = false;
598
		$gateway['ipprotocol'] = "inet6";
599
		$gateway['gateway'] = get_interface_gateway_v6($ifname, $gateway['dynamic']);
600
		switch($ifcfg['ipaddrv6']) {
601
			case "6to4":
602
				$gateway['interface'] = "stf0";
603
				$gateway['dynamic'] = "default";
604
				break;
605
			case "6rd":
606
				$gateway['interface'] = "stf0";
607
				$gateway['dynamic'] = "default";
608
				break;
609
			default:
610
				$gateway['interface'] = get_real_interface($ifname);
611
				break;
612
		}
613
		$gateway['friendlyiface'] = $ifname;
614
		$gateway['name'] = "{$friendly}{$ctype}";
615
		$gateway['attribute'] = "system";
616
	
617
		if (($gateway['dynamic'] === "default")  && ($found_defaultv6 == 0)) {
618
			$gateway['defaultgw'] = true;
619
			$gateway['dynamic'] = true;
620
			$found_defaultv6 = 1;
621
		}
622

    
623
		/* Loopback dummy for dynamic interfaces without a IP */
624
		if (!is_ipaddrv6($gateway['gateway']) && $gateway['dynamic'] == true)
625
			$gateway['gateway'] = "dynamic6";
626

    
627
		/* automatically skip known static and dynamic gateways we have a array entry for */
628
		foreach($gateways_arr as $gateway_item) {
629
			if ((($ifname == $gateway_item['friendlyiface'] && $friendly == $gateway_item['name']) && ($gateway['ipprotocol'] == $gateway_item['ipprotocol'])) ||
630
				($ifname == $gateway_item['friendlyiface'] && $gateway_item['dynamic'] == true) && ($gateway['ipprotocol'] == $gateway_item['ipprotocol']))
631
					continue 2;
632
		}
633

    
634
		if (is_ipaddrv6($gateway['gateway']))
635
			$gateway['monitor'] = $gateway['gateway'];
636

    
637
		$gateway['descr'] = "Interface {$friendly}{$ctype} Gateway";
638
		$gateways_arr[$gateway['name']] = $gateway;
639
		unset($gateway);
640
	}
641

    
642
	if($localhost === true) {
643
		/* attach localhost for Null routes */
644
		$gwlo4 = array();
645
		$gwlo4['name'] = "Null4";
646
		$gwlo4['interface'] = "lo0";
647
		$gwlo4['ipprotocol'] = "inet";
648
		$gwlo4['gateway'] = "127.0.0.1";
649
		$gwlo6 = array();
650
		$gwlo6['name'] = "Null6";
651
		$gwlo6['interface'] = "lo0";
652
		$gwlo6['ipprotocol'] = "inet6";
653
		$gwlo6['gateway'] = "::1";
654
		$gateways_arr['Null4'] = $gwlo4;
655
		$gateways_arr['Null6'] = $gwlo6;
656
	}
657
	return($gateways_arr);
658
}
659

    
660
function fixup_default_gateway($ipprotocol, $gateways_status, $gateways_arr) {
661
	global $config, $g;
662
	/*
663
	 * NOTE: The code below is meant to replace the default gateway when it goes down.
664
	 *	This facilitates services running on pfSense itself and are not handled by a PBR to continue working.
665
	 */
666
	$upgw = "";
667
	$dfltgwdown = false;
668
	$dfltgwfound = false;
669
	foreach ($gateways_arr as $gwname => $gwsttng) {
670
		if (($gwsttng['ipprotocol'] == $ipprotocol) && isset($gwsttng['defaultgw'])) {
671
			$dfltgwfound = true;
672
			$dfltgwname = $gwname;
673
			if (!isset($gwsttng['monitor_disable']) && stristr($gateways_status[$gwname]['status'], "down"))
674
				$dfltgwdown = true;
675
		}
676
		/* Keep a record of the last up gateway */
677
		/* XXX: Blacklist lan for now since it might cause issues to those who have a gateway set for it */
678
		if (empty($upgw) && ($gwsttng['ipprotocol'] == $ipprotocol) && (isset($gwsttng['monitor_disable']) || !stristr($gateways_status[$gwname]['status'], "down")) && $gwsttng[$gwname]['friendlyiface'] != "lan")
679
			$upgw = $gwname;
680
		if ($dfltgwdown == true && !empty($upgw))
681
			break;
682
	}
683
	if ($dfltgwfound == false) {
684
		$gwname = convert_friendly_interface_to_friendly_descr("wan");
685
		if (!empty($gateways_status[$gwname]) && stristr($gateways_status[$gwname]['status'], "down"))
686
			$dfltgwdown = true;
687
	}
688
	if ($dfltgwdown == true && !empty($upgw)) {
689
		if (preg_match("/dynamic/i", $gateways_arr[$upgw]['gateway']))
690
			$gateways_arr[$upgw]['gateway'] = get_interface_gateway($gateways_arr[$upgw]['friendlyiface']);
691
		if (is_ipaddr($gateways_arr[$upgw]['gateway'])) {
692
			log_error("Default gateway down setting {$upgw} as default!");
693
			if(is_ipaddrv6($gateways_arr[$upgw]['gateway'])) {
694
				$inetfamily = "-inet6";
695
			} else {
696
				$inetfamily = "-inet";
697
			}
698
			mwexec("/sbin/route change {$inetfamily} default {$gateways_arr[$upgw]['gateway']}");
699
		}
700
	} else {
701
		$defaultgw = trim(`/sbin/route -n get -{$ipprotocol} default | /usr/bin/grep gateway | /usr/bin/sed 's/gateway://g'`, " \n");
702
		if(is_ipaddrv6($gateways_arr[$dfltgwname]['gateway'])) {
703
			$inetfamily = "-inet6";
704
		} else {
705
			$inetfamily = "-inet";
706
		}
707
		if ($defaultgw != $gateways_arr[$dfltgwname]['gateway'])
708
			mwexec("/sbin/route change {$inetfamily} default {$gateways_arr[$dfltgwname]['gateway']}");
709
	}
710
}
711

    
712
/*
713
 * Return an array with all gateway groups with name as key
714
 * All gateway groups will be processed before returning the array.
715
 */
716
function return_gateway_groups_array() {
717
	global $config, $g;
718

    
719
	/* fetch the current gateways status */
720
	$gateways_status = return_gateways_status(true);
721
	$gateways_arr = return_gateways_array();
722
	$gateway_groups_array = array();
723
	$carplist = get_configured_carp_interface_list();
724

    
725
	if (isset($config['system']['gw_switch_default'])) {
726
		fixup_default_gateway("inet", $gateways_status, $gateways_arr);
727
		fixup_default_gateway("inet6", $gateways_status, $gateways_arr);
728
	}
729
	if (is_array($config['gateways']['gateway_group'])) {
730
		foreach($config['gateways']['gateway_group'] as $group) {
731
			/* create array with group gateways members seperated by tier */
732
			$tiers = array();
733
			$backupplan = array();
734
			foreach($group['item'] as $item) {
735
				$itemsplit = explode("|", $item);
736
				$tier = $itemsplit[1];
737
				$gwname = $itemsplit[0];
738
				$vipname = $itemsplit[2];
739
				if(is_ipaddr($carplist[$vipname]))
740
					$gwvip_arr[$group['name']][$gwname] = $vipname;
741

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

    
745
				/* check if the gateway is available before adding it to the array */
746
				if (!empty($gateways_status[$gwname])) {
747
					$status = $gateways_status[$gwname];
748
					$gwdown = false;
749
					if (stristr($status['status'], "down")) {
750
						$msg = sprintf(gettext("MONITOR: %s is down, removing from routing group {$group['name']}"), $gwname);
751
						$gwdown = true;
752
					} else if (stristr($status['status'], "loss") && strstr($group['trigger'], "loss")) {
753
						/* packet loss */
754
						$msg = sprintf(gettext("MONITOR: %s has packet loss, removing from routing group {$group['name']}"), $gwname);
755
						$gwdown = true;
756
					} else if (stristr($status['status'], "delay") && strstr($group['trigger'] , "latency")) {
757
						/* high latency */
758
						$msg = sprintf(gettext("MONITOR: %s has high latency, removing from routing group {$group['name']}"), $gwname);
759
						$gwdown = true;
760
					}
761
					if ($gwdown == true) {
762
						log_error($msg);
763
						notify_via_growl($msg);
764
						notify_via_smtp($msg);
765
					} else
766
						/* Online add member */
767
						$tiers[$tier][] = $gwname;
768
				} else if (isset($gateways_arr[$gwname]['monitor_disable']))
769
					$tiers[$tier][] = $gwname;
770
			}
771
			$tiers_count = count($tiers);
772
			if($tiers_count == 0) {
773
				/* Oh dear, we have no members! Engage Plan B */
774
				if (!$g['booting']) {
775
					$msg = gettext("Gateways status could not be determined, considering all as up/active. (Group: {$group['name']})");
776
					log_error($msg);
777
					notify_via_growl($msg);
778
					notify_via_smtp($msg);
779
				}
780
				$tiers = $backupplan;
781
			}
782
			/* sort the tiers array by the tier key */
783
			ksort($tiers);
784

    
785
			/* we do not really foreach the tiers as we stop after the first tier */
786
			foreach($tiers as $tier) {
787
				/* process all gateways in this tier */
788
				foreach($tier as $member) {
789
					/* determine interface gateway */
790
					if (isset($gateways_arr[$member])) {
791
						$gateway = $gateways_arr[$member];
792
						$int = $gateway['interface'];
793
						$gatewayip = "";
794
						if(is_ipaddr($gateway['gateway'])) 
795
							$gatewayip = $gateway['gateway'];
796
						else if ($int <> "")
797
							$gatewayip = get_interface_gateway($gateway['friendlyiface']);
798
					
799
						if (($int <> "") && is_ipaddr($gatewayip)) {
800
							$groupmember = array();
801
							$groupmember['int']  = $int;
802
							$groupmember['gwip']  = $gatewayip;
803
							$groupmember['weight']  = isset($gateway['weight']) ? $gateway['weight'] : 1;
804
							if($gwvip_arr[$group['name']][$gwname] <> "")
805
								$groupmember['vip'] = $gwvip_arr[$group['name']][$gwname];
806
							$gateway_groups_array[$group['name']]['ipprotocol'] = $gateway['ipprotocol'];
807
							$gateway_groups_array[$group['name']][] = $groupmember;
808
						}
809
					}
810
				}
811
				/* we should have the 1st available tier now, exit stage left */
812
				if(is_array($gateway_groups_array[$group['name']]))
813
					break;
814
				else
815
					log_error("GATEWAYS: We did not find the first tier of the gateway group {$group['name']}! That's odd.");
816
			}
817
		}
818
	}
819
	return ($gateway_groups_array);
820
}
821

    
822
/* Update DHCP WAN Interface ip address in gateway group item */
823
function dhclient_update_gateway_groups_defaultroute($interface = "wan") {
824
	global $config, $g;
825
	foreach($config['gateways']['gateway_item'] as & $gw) {	
826
		if($gw['interface'] == $interface) {
827
			$current_gw = get_interface_gateway($interface);
828
			if($gw['gateway'] <> $current_gw) {
829
				$gw['gateway'] = $current_gw;
830
				$changed = true;
831
			}
832
		}
833
	}
834
	if($changed && $current_gw)
835
		write_config(sprintf(gettext('Updating gateway group gateway for %1$s - new gateway is %2$s'), $interfac, $current_gw));
836
}
837

    
838
function lookup_gateway_ip_by_name($name) {
839

    
840
	$gateways_arr = return_gateways_array(false, true);
841
        foreach ($gateways_arr as $gname => $gw) {
842
                if ($gw['name'] == $name || $gname == $name)
843
                        return $gw['gateway'];
844
        }
845

    
846
	return false;
847
}
848

    
849
function lookup_gateway_monitor_ip_by_name($name) {
850

    
851
        $gateways_arr = return_gateways_array(false, true);
852
	if (!empty($gateways_arr[$name])) {
853
		$gateway = $gateways_arr[$name];
854
		if(!is_ipaddr($gateway['monitor']))
855
			return $gateway['gateway'];
856

    
857
		return $gateway['monitor'];
858
        }
859

    
860
        return (false);
861
}
862

    
863
function lookup_gateway_interface_by_name($name) {
864

    
865
        $gateways_arr = return_gateways_array(false, true);
866
	if (!empty($gateways_arr[$name])) {
867
		$interfacegw = $gateways_arr[$name]['interface'];
868
		return ($interfacegw);
869
        }
870

    
871
        return (false);
872
}
873

    
874
function get_interface_gateway($interface, &$dynamic = false) {
875
	global $config, $g;
876

    
877
	$gw = NULL;
878

    
879
	$gwcfg = $config['interfaces'][$interface];
880
	if (!empty($gwcfg['gateway']) && is_array($config['gateways']['gateway_item'])) {
881
		foreach($config['gateways']['gateway_item'] as $gateway) {
882
			if(($gateway['name'] == $gwcfg['gateway']) && (is_ipaddrv4($gateway['gateway']))) {
883
				$gw = $gateway['gateway'];
884
				break;
885
			}
886
		}
887
	}
888

    
889
	// for dynamic interfaces we handle them through the $interface_router file.
890
	if (!is_ipaddrv4($gw) && !is_ipaddrv4($gwcfg['ipaddr'])) {
891
		$realif = get_real_interface($interface);
892
		if (file_exists("{$g['tmp_path']}/{$realif}_router")) {
893
				$gw = trim(file_get_contents("{$g['tmp_path']}/{$realif}_router"), " \n");
894
			$dynamic = true;
895
		}
896
		if (file_exists("{$g['tmp_path']}/{$realif}_defaultgw"))
897
			$dynamic = "default";
898
			
899
	}
900

    
901
	/* return gateway */
902
	return ($gw);
903
}
904

    
905
function get_interface_gateway_v6($interface, &$dynamic = false) {
906
	global $config, $g;
907

    
908
	$gw = NULL;
909
	$gwcfg = $config['interfaces'][$interface];
910
	if (!empty($gwcfg['gatewayv6']) && is_array($config['gateways']['gateway_item'])) {
911
		foreach($config['gateways']['gateway_item'] as $gateway) {
912
			if(($gateway['name'] == $gwcfg['gatewayv6']) && (is_ipaddrv6($gateway['gateway']))) {
913
				$gw = $gateway['gateway'];
914
				break;
915
			}
916
		}
917
	}
918

    
919
	// for dynamic interfaces we handle them through the $interface_router file.
920
	if (!is_ipaddrv6($gw) && !is_ipaddrv6($gwcfg['ipaddrv6'])) {
921
			$realif = get_real_interface($interface);
922
			if (file_exists("{$g['tmp_path']}/{$realif}_routerv6")) {
923
				$gw = trim(file_get_contents("{$g['tmp_path']}/{$realif}_routerv6"), " \n");
924
				$dynamic = true;
925
			}
926
			if (file_exists("{$g['tmp_path']}/{$realif}_defaultgwv6"))
927
				$dynamic = "default";
928
			
929
	}
930
	/* return gateway */
931
	return ($gw);
932
}
933

    
934
/* Check a IP address against a gateway IP or name
935
 * to verify it's address family */
936
function validate_address_family($ipaddr, $gwname) {
937
	$v4ip = false;
938
	$v6ip = false;
939
	$v4gw = false;
940
	$v6gw = false;
941

    
942
	if(is_ipaddrv4($ipaddr))
943
		$v4ip = true;
944
	if(is_ipaddrv6($ipaddr))
945
		$v6ip = true;
946
	if(is_ipaddrv4($gwname))
947
		$v4gw = true;
948
	if(is_ipaddrv6($gwname))
949
		$v6gw = true;
950

    
951
	if($v4ip && $v4gw)
952
		return true;
953
	if($v6ip && $v6gw)
954
		return true;
955

    
956
	/* still no match, carry on, lookup gateways */
957
	if(is_ipaddrv4(lookup_gateway_ip_by_name($gwname)))
958
		$v4gw = true;
959
	if(is_ipaddrv6(lookup_gateway_ip_by_name($gwname)))
960
		$v6gw = true;
961

    
962
	$gw_array = return_gateways_array();
963
	if(is_array($gw_array[$gwname])) {
964
		switch($gw_array[$gwname]['ipprotocol']) {
965
			case "inet":
966
				$v4gw = true;
967
				break;
968
			case "inet6":
969
				$v6gw = true;
970
				break;
971
		}
972
	}
973

    
974
	if($v4ip && $v4gw)
975
		return true;
976
	if($v6ip && $v6gw)
977
		return true;
978
	
979
	return false;
980
}
981

    
982
/* check if a interface is part of a gateway group */
983
function interface_gateway_group_member($interface) {
984
	global $config;
985
	$realif = get_real_interface($interface);
986
	if(is_array($config['gateways']['gateway_group']))
987
		$groups = $config['gateways']['gateway_group'];
988
	else
989
		return false;
990

    
991
	foreach($groups as $group) {
992
		if(is_array($group['item'])) {
993
			foreach($group['item'] as $item) {
994
				$elements = explode("|", $item);
995
				$gwname = $elements[0];
996
				$gwif = get_real_interface(lookup_gateway_interface_by_name($gwname));
997
				if($gwif == $realif)
998
					return true;
999
			}
1000
		}
1001
	}
1002
	return false;
1003
}
1004

    
1005
?>
(25-25/68)