Project

General

Profile

Download (31.5 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
			if (!is_ipaddrv4($gwifip))
165
				continue; //Skip this target
166
		} else if (is_ipaddrv6($gateway['gateway'])) {
167
			/* link locals really need a different src ip */
168
			if(preg_match("/fe80::/i", $gateway['gateway'])) {
169
				$linklocal = explode("%", find_interface_ipv6_ll($gateway['interface'], true));
170
				$gwifip = $linklocal[0];
171
				$ifscope = "%". $linklocal[1];
172
			} else {
173
				$gwifip = find_interface_ipv6($gateway['interface'], true);
174
			}
175
			if (!is_ipaddrv6($gwifip))
176
				continue; //Skip this target
177
		} else
178
			continue;
179

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

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

    
254
		$apingerconfig .= $alarmscfg;
255
		$apingerconfig .= $apingercfg;
256
	}
257
	fwrite($fd, $apingerconfig);
258
	fclose($fd);
259
	unset($apingerconfig);
260

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

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

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

    
274
	return 0;
275
}
276

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

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

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

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

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

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

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

    
347
	$gateways_arr = array();
348

    
349
	$found_defaultv4 = 0;
350
	$found_defaultv6 = 0;
351

    
352
	$interfaces_v4 = array();
353
	$interfaces_v6 = array();
354

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

    
363
			$wancfg = $config['interfaces'][$gateway['interface']];
364
			/* getting this detection right is hard at this point because we still don't
365
			 * store the address family in the gateway item */
366
			if(is_ipaddrv4($gateway['gateway']))
367
				$gateway['ipprotocol'] = "inet";
368
			else if(is_ipaddrv6($gateway['gateway']))
369
				$gateway['ipprotocol'] = "inet6";
370

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

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

    
417
			if (isset($gateway['monitor_disable']))
418
				$gateway['monitor_disable'] = true;
419
			else if (empty($gateway['monitor']))
420
				$gateway['monitor'] = $gateway['gateway'];
421

    
422
			$gateway['friendlyiface'] = $gateway['interface'];
423

    
424
			/* special treatment for tunnel interfaces */
425
			if ($gateway['ipprotocol'] == "inet6")
426
				$gateway['interface'] = get_real_interface($gateway['interface'], "inet6");
427
			else if ($gateway['ipprotocol'] == "inet")
428
				$gateway['interface'] = get_real_interface($gateway['interface']);
429

    
430
			/* entry has a default flag, use it */
431
			if (isset($gateway['defaultgw'])) {
432
				if($gateway['ipprotocol'] == "inet") {
433
					$gateway['defaultgw'] = true;
434
					$found_defaultv4 = 1;
435
				}
436
				if($gateway['ipprotocol'] == "inet6") {
437
					$gateway['defaultgw'] = true;
438
					$found_defaultv6 = 1;
439
				}
440
			}
441
			/* include the gateway index as the attribute */
442
			$gateway['attribute'] = $i;
443

    
444
			/* tack a item on the array to keep track of dynamic interfaces */
445
			if($gateway['ipprotocol'] == "inet")
446
				$interfaces_v4[] = $gateway['friendlyiface'];
447

    
448
			if($gateway['ipprotocol'] == "inet6")
449
				$interfaces_v6[] = $gateway['friendlyiface'];
450

    
451
			$gateways_arr[$gateway['name']] = $gateway;
452
			unset($gateway);
453
			$i++;
454
		}
455
	}
456

    
457
	/* Loop through all interfaces with a gateway and add it to a array */
458
	if ($disabled == false)
459
		$iflist = get_configured_interface_with_descr();
460
	else
461
		$iflist = get_configured_interface_with_descr(false, true);
462

    
463
	/* Process/add dynamic v4 gateways. */
464
	foreach($iflist as $ifname => $friendly ) {
465
		if(! interface_has_gateway($ifname))
466
			continue;
467

    
468
		if (empty($config['interfaces'][$ifname]))
469
			continue;
470

    
471
		$ifcfg = &$config['interfaces'][$ifname];
472
		if(!empty($ifcfg['ipaddr']) && is_ipaddrv4($ifcfg['ipaddr']))
473
			continue;
474

    
475
		if(!isset($ifcfg['enable']))
476
			continue;
477

    
478
		if(in_array($ifname, $interfaces_v4))
479
			continue;
480
			
481
		$ctype = "";
482
		switch($ifcfg['ipaddr']) {
483
			case "dhcp":
484
			case "pppoe":
485
			case "pptp":
486
			case "ppp":
487
				$ctype = strtoupper($ifcfg['ipaddr']);
488
				break;
489
			default:
490
				if (substr($ifcfg['if'], 0, 4) ==  "ovpn")
491
					$ctype = "VPNv4";
492
				break;
493
		}
494
		$ctype = "_". strtoupper($ctype);
495

    
496
		$gateway = array();
497
		$gateway['dynamic'] = false;
498
		$gateway['ipprotocol'] = "inet";
499
		$gateway['gateway'] = get_interface_gateway($ifname, $gateway['dynamic']);
500
		$gateway['interface'] = get_real_interface($ifname);
501
		$gateway['friendlyiface'] = $ifname;
502
		$gateway['name'] = "{$friendly}{$ctype}";
503
		$gateway['attribute'] = "system";
504
	
505
		if (($gateway['dynamic'] === "default") && ($found_defaultv4 == 0)) {
506
			$gateway['defaultgw'] = true;
507
			$gateway['dynamic'] = true;
508
			$found_defaultv4 = 1;
509
		}
510
		/* Loopback dummy for dynamic interfaces without a IP */
511
		if (!is_ipaddrv4($gateway['gateway']) && $gateway['dynamic'] == true)
512
			$gateway['gateway'] = "dynamic";
513

    
514
		/* automatically skip known static and dynamic gateways we have a array entry for */
515
		foreach($gateways_arr as $gateway_item) {
516
			if ((($ifname == $gateway_item['friendlyiface'] && $friendly == $gateway_item['name'])&& ($gateway['ipprotocol'] == $gateway_item['ipprotocol'])) ||
517
				($ifname == $gateway_item['friendlyiface'] && $gateway_item['dynamic'] == true) && ($gateway['ipprotocol'] == $gateway_item['ipprotocol']))
518
					continue 2;
519
		}
520

    
521
		if (is_ipaddrv4($gateway['gateway']))
522
			$gateway['monitor'] = $gateway['gateway'];
523

    
524
		$gateway['descr'] = "Interface {$friendly}{$ctype} Gateway";
525
		$gateways_arr[$gateway['name']] = $gateway;
526
		unset($gateway);
527
	}
528

    
529
	/* Process/add dynamic v6 gateways. */
530
	foreach($iflist as $ifname => $friendly ) {
531
		/* If the user has disabled IPv6, they probably don't want any IPv6 gateways. */
532
		if (!isset($config['system']['ipv6allow']))
533
			break;
534

    
535
		if(! interface_has_gatewayv6($ifname))
536
			continue;
537

    
538
		if (empty($config['interfaces'][$ifname]))
539
			continue;
540

    
541
		$ifcfg = &$config['interfaces'][$ifname];
542
		if(!empty($ifcfg['ipaddrv6']) && is_ipaddrv6($ifcfg['ipaddrv6']))
543
			continue;
544
			
545
		if(!isset($ifcfg['enable']))
546
			continue;
547

    
548
		if(in_array($ifname, $interfaces_v6))
549
			continue;
550

    
551
		$ctype = "";
552
		switch($ifcfg['ipaddrv6']) {
553
			case "slaac":
554
			case "dhcp6":
555
			case "6to4":
556
			case "6rd":
557
				$ctype = strtoupper($ifcfg['ipaddrv6']);
558
				break;
559
			default:
560
				if (substr($ifcfg['if'], 0, 4) ==  "ovpn")
561
					$ctype = "VPNv6";
562
				break;
563
		}
564
		$ctype = "_". strtoupper($ctype);
565

    
566
		$gateway = array();
567
		$gateway['dynamic'] = false;
568
		$gateway['ipprotocol'] = "inet6";
569
		$gateway['gateway'] = get_interface_gateway_v6($ifname, $gateway['dynamic']);
570
		$gateway['interface'] = get_real_interface($ifname, "inet6");
571
		switch($ifcfg['ipaddrv6']) {
572
			case "6rd":
573
			case "6to4":
574
				$gateway['dynamic'] = "default";
575
				break;
576
		}
577
		$gateway['friendlyiface'] = $ifname;
578
		$gateway['name'] = "{$friendly}{$ctype}";
579
		$gateway['attribute'] = "system";
580
	
581
		if (($gateway['dynamic'] === "default")  && ($found_defaultv6 == 0)) {
582
			$gateway['defaultgw'] = true;
583
			$gateway['dynamic'] = true;
584
			$found_defaultv6 = 1;
585
		}
586

    
587
		/* Loopback dummy for dynamic interfaces without a IP */
588
		if (!is_ipaddrv6($gateway['gateway']) && $gateway['dynamic'] == true)
589
			$gateway['gateway'] = "dynamic6";
590

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

    
598
		if (is_ipaddrv6($gateway['gateway']))
599
			$gateway['monitor'] = $gateway['gateway'];
600

    
601
		$gateway['descr'] = "Interface {$friendly}{$ctype} Gateway";
602
		$gateways_arr[$gateway['name']] = $gateway;
603
		unset($gateway);
604
	}
605

    
606
	/* FIXME: Should this be enabled.
607
	 * Some interface like wan might be default but have no info recorded
608
	 * the config. */
609
	/* this is a fallback if all else fails and we want to get packets out @smos */
610
	if ($found_defaultv4 == 0 || $found_defaultv6 == 0) {
611
		foreach ($gateways_arr as &$gateway) {
612
			if (($gateway['friendlyiface'] == "wan") && ($found_defaultv4 == 0) && (!isset($gateway['ipprotocol']) || ($gateway['ipprotocol'] == "inet"))) {
613
				if (file_exists("{$g['tmp_path']}/{$gateway['interface']}_defaultgw")) {
614
					$gateway['defaultgw'] = true;
615
					$found_defaultv4 = 1;
616
				}
617
			}
618
			if (($gateway['friendlyiface'] == "wan") && ($found_defaultv6 == 0) && ($gateway['ipprotocol'] == "inet6")) {
619
				if (file_exists("{$g['tmp_path']}/{$gateway['interface']}_defaultgwv6")) {
620
					$gateway['defaultgw'] = true;
621
					$found_defaultv6 = 1;
622
				}
623
			}
624
		}
625
	}
626

    
627
	if($localhost === true) {
628
		/* attach localhost for Null routes */
629
		$gwlo4 = array();
630
		$gwlo4['name'] = "Null4";
631
		$gwlo4['interface'] = "lo0";
632
		$gwlo4['ipprotocol'] = "inet";
633
		$gwlo4['gateway'] = "127.0.0.1";
634
		$gwlo6 = array();
635
		$gwlo6['name'] = "Null6";
636
		$gwlo6['interface'] = "lo0";
637
		$gwlo6['ipprotocol'] = "inet6";
638
		$gwlo6['gateway'] = "::1";
639
		$gateways_arr['Null4'] = $gwlo4;
640
		$gateways_arr['Null6'] = $gwlo6;
641
	}
642
	return($gateways_arr);
643
}
644

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

    
697
/*
698
 * Return an array with all gateway groups with name as key
699
 * All gateway groups will be processed before returning the array.
700
 */
701
function return_gateway_groups_array() {
702
	global $config, $g;
703

    
704
	/* fetch the current gateways status */
705
	$gateways_status = return_gateways_status(true);
706
	$gateways_arr = return_gateways_array();
707
	$gateway_groups_array = array();
708
	$carplist = get_configured_carp_interface_list();
709

    
710
	if (isset($config['system']['gw_switch_default'])) {
711
		fixup_default_gateway("inet", $gateways_status, $gateways_arr);
712
		fixup_default_gateway("inet6", $gateways_status, $gateways_arr);
713
	}
714
	if (is_array($config['gateways']['gateway_group'])) {
715
		foreach($config['gateways']['gateway_group'] as $group) {
716
			/* create array with group gateways members seperated by tier */
717
			$tiers = array();
718
			$backupplan = array();
719
			foreach($group['item'] as $item) {
720
				$itemsplit = explode("|", $item);
721
				$tier = $itemsplit[1];
722
				$gwname = $itemsplit[0];
723
				$vipname = $itemsplit[2];
724
				if(is_ipaddr($carplist[$vipname]))
725
					$gwvip_arr[$group['name']][$gwname] = $vipname;
726

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

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

    
771
			/* we do not really foreach the tiers as we stop after the first tier */
772
			foreach($tiers as $tier) {
773
				/* process all gateways in this tier */
774
				foreach($tier as $member) {
775
					/* determine interface gateway */
776
					if (isset($gateways_arr[$member])) {
777
						$gateway = $gateways_arr[$member];
778
						$int = $gateway['interface'];
779
						$gatewayip = "";
780
						if(is_ipaddr($gateway['gateway'])) 
781
							$gatewayip = $gateway['gateway'];
782
						else if ($int <> "")
783
							$gatewayip = get_interface_gateway($gateway['friendlyiface']);
784
					
785
						if (($int <> "") && is_ipaddr($gatewayip)) {
786
							$groupmember = array();
787
							$groupmember['int']  = $int;
788
							$groupmember['gwip']  = $gatewayip;
789
							$groupmember['weight']  = isset($gateway['weight']) ? $gateway['weight'] : 1;
790
							if($gwvip_arr[$group['name']][$gwname] <> "")
791
								$groupmember['vip'] = $gwvip_arr[$group['name']][$gwname];
792
							$gateway_groups_array[$group['name']]['ipprotocol'] = $gateway['ipprotocol'];
793
							$gateway_groups_array[$group['name']][] = $groupmember;
794
						}
795
					}
796
				}
797
				/* we should have the 1st available tier now, exit stage left */
798
				if(is_array($gateway_groups_array[$group['name']]))
799
					break;
800
				else
801
					log_error("GATEWAYS: We did not find the first tier of the gateway group {$group['name']}! That's odd.");
802
			}
803
		}
804
	}
805
	return ($gateway_groups_array);
806
}
807

    
808
/* Update DHCP WAN Interface ip address in gateway group item */
809
function dhclient_update_gateway_groups_defaultroute($interface = "wan") {
810
	global $config, $g;
811
	foreach($config['gateways']['gateway_item'] as & $gw) {	
812
		if($gw['interface'] == $interface) {
813
			$current_gw = get_interface_gateway($interface);
814
			if($gw['gateway'] <> $current_gw) {
815
				$gw['gateway'] = $current_gw;
816
				$changed = true;
817
			}
818
		}
819
	}
820
	if($changed && $current_gw)
821
		write_config(sprintf(gettext('Updating gateway group gateway for %1$s - new gateway is %2$s'), $interfac, $current_gw));
822
}
823

    
824
function lookup_gateway_ip_by_name($name) {
825

    
826
	$gateways_arr = return_gateways_array(false, true);
827
        foreach ($gateways_arr as $gname => $gw) {
828
                if ($gw['name'] == $name || $gname == $name)
829
                        return $gw['gateway'];
830
        }
831

    
832
	return false;
833
}
834

    
835
function lookup_gateway_monitor_ip_by_name($name) {
836

    
837
        $gateways_arr = return_gateways_array(false, true);
838
	if (!empty($gateways_arr[$name])) {
839
		$gateway = $gateways_arr[$name];
840
		if(!is_ipaddr($gateway['monitor']))
841
			return $gateway['gateway'];
842

    
843
		return $gateway['monitor'];
844
        }
845

    
846
        return (false);
847
}
848

    
849
function lookup_gateway_interface_by_name($name) {
850

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

    
857
        return (false);
858
}
859

    
860
function get_interface_gateway($interface, &$dynamic = false) {
861
	global $config, $g;
862

    
863
	$gw = NULL;
864

    
865
	$gwcfg = $config['interfaces'][$interface];
866
	if (!empty($gwcfg['gateway']) && is_array($config['gateways']['gateway_item'])) {
867
		foreach($config['gateways']['gateway_item'] as $gateway) {
868
			if(($gateway['name'] == $gwcfg['gateway']) && (is_ipaddrv4($gateway['gateway']))) {
869
				$gw = $gateway['gateway'];
870
				break;
871
			}
872
		}
873
	}
874

    
875
	// for dynamic interfaces we handle them through the $interface_router file.
876
	if (!is_ipaddrv4($gw) && !is_ipaddrv4($gwcfg['ipaddr'])) {
877
		$realif = get_real_interface($interface);
878
		if (file_exists("{$g['tmp_path']}/{$realif}_router")) {
879
				$gw = trim(file_get_contents("{$g['tmp_path']}/{$realif}_router"), " \n");
880
			$dynamic = true;
881
		}
882
		if (file_exists("{$g['tmp_path']}/{$realif}_defaultgw"))
883
			$dynamic = "default";
884
			
885
	}
886

    
887
	/* return gateway */
888
	return ($gw);
889
}
890

    
891
function get_interface_gateway_v6($interface, &$dynamic = false) {
892
	global $config, $g;
893

    
894
	$gw = NULL;
895
	$gwcfg = $config['interfaces'][$interface];
896
	if (!empty($gwcfg['gatewayv6']) && is_array($config['gateways']['gateway_item'])) {
897
		foreach($config['gateways']['gateway_item'] as $gateway) {
898
			if(($gateway['name'] == $gwcfg['gatewayv6']) && (is_ipaddrv6($gateway['gateway']))) {
899
				$gw = $gateway['gateway'];
900
				break;
901
			}
902
		}
903
	}
904

    
905
	// for dynamic interfaces we handle them through the $interface_router file.
906
	if (!is_ipaddrv6($gw) && !is_ipaddrv6($gwcfg['ipaddrv6'])) {
907
			$realif = get_real_interface($interface);
908
			if (file_exists("{$g['tmp_path']}/{$realif}_routerv6")) {
909
				$gw = trim(file_get_contents("{$g['tmp_path']}/{$realif}_routerv6"), " \n");
910
				$dynamic = true;
911
			}
912
			if (file_exists("{$g['tmp_path']}/{$realif}_defaultgwv6"))
913
				$dynamic = "default";
914
			
915
	}
916
	/* return gateway */
917
	return ($gw);
918
}
919

    
920
/* Check a IP address against a gateway IP or name
921
 * to verify it's address family */
922
function validate_address_family($ipaddr, $gwname) {
923
	$v4ip = false;
924
	$v6ip = false;
925
	$v4gw = false;
926
	$v6gw = false;
927

    
928
	if(is_ipaddrv4($ipaddr))
929
		$v4ip = true;
930
	if(is_ipaddrv6($ipaddr))
931
		$v6ip = true;
932
	if(is_ipaddrv4($gwname))
933
		$v4gw = true;
934
	if(is_ipaddrv6($gwname))
935
		$v6gw = true;
936

    
937
	if($v4ip && $v4gw)
938
		return true;
939
	if($v6ip && $v6gw)
940
		return true;
941

    
942
	/* still no match, carry on, lookup gateways */
943
	if(is_ipaddrv4(lookup_gateway_ip_by_name($gwname)))
944
		$v4gw = true;
945
	if(is_ipaddrv6(lookup_gateway_ip_by_name($gwname)))
946
		$v6gw = true;
947

    
948
	$gw_array = return_gateways_array();
949
	if(is_array($gw_array[$gwname])) {
950
		switch($gw_array[$gwname]['ipprotocol']) {
951
			case "inet":
952
				$v4gw = true;
953
				break;
954
			case "inet6":
955
				$v6gw = true;
956
				break;
957
		}
958
	}
959

    
960
	if($v4ip && $v4gw)
961
		return true;
962
	if($v6ip && $v6gw)
963
		return true;
964
	
965
	return false;
966
}
967

    
968
/* check if a interface is part of a gateway group */
969
function interface_gateway_group_member($interface) {
970
	global $config;
971
	$realif = get_real_interface($interface);
972
	if(is_array($config['gateways']['gateway_group']))
973
		$groups = $config['gateways']['gateway_group'];
974
	else
975
		return false;
976

    
977
	foreach($groups as $group) {
978
		if(is_array($group['item'])) {
979
			foreach($group['item'] as $item) {
980
				$elements = explode("|", $item);
981
				$gwname = $elements[0];
982
				$gwif = get_real_interface(lookup_gateway_interface_by_name($gwname));
983
				if($gwif == $realif)
984
					return true;
985
			}
986
		}
987
	}
988
	return false;
989
}
990

    
991
?>
(24-24/66)