Project

General

Profile

Download (32.6 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

    
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['varrun_path']}/apinger.status");
57
		return;
58
	}
59

    
60
	$apinger_default = return_apinger_defaults();
61
	$apingerconfig = <<<EOD
62

    
63
# pfSense apinger configuration file. Automatically Generated!
64

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

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

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

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

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

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

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

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

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

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

    
120
target default {
121
	## How often the probe should be sent
122
	interval {$apinger_default['interval']}s
123

    
124
	## How many replies should be used to compute average delay
125
	## for controlling "delay" alarms
126
	avg_delay_samples 10
127

    
128
	## How many probes should be used to compute average loss
129
	avg_loss_samples 50
130

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

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

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

    
142
EOD;
143

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

    
156
		/* if the monitor address is already used before, skip */
157
		if(in_array($gateway['monitor'], $monitor_ips))
158
			continue;
159

    
160
		/* Interface ip is needed since apinger will bind a socket to it. */
161
		if (is_ipaddrv4($gateway['gateway'])) {
162
			$gwifip = find_interface_ip($gateway['interface'], true);
163
			if (!is_ipaddrv4($gwifip))
164
				continue; //Skip this target
165

    
166
			/*
167
			 * If the gateway is the same as the monitor we do not add a
168
			 * route as this will break the routing table.
169
			 * Add static routes for each gateway with their monitor IP
170
			 * not strictly necessary but is a added level of protection.
171
			 */
172
			if (is_ipaddrv4($gateway['gateway']) && $gateway['monitor'] != $gateway['gateway']) {
173
				log_error("Removing static route for monitor {$gateway['monitor']} and adding a new route through {$gateway['gateway']}");
174
				mwexec("/sbin/route change -host " . escapeshellarg($gateway['monitor']) .
175
					" " . escapeshellarg($gateway['gateway']), true);
176
			}
177
		} else if (is_ipaddrv6($gateway['gateway'])) {
178
			/* link locals really need a different src ip */
179
			if(is_linklocal($gateway['gateway'])) {
180
				$gwifip = find_interface_ipv6_ll($gateway['interface'], true);
181
			} else {
182
				$gwifip = find_interface_ipv6($gateway['interface'], true);
183
			}
184
			if (is_linklocal($gateway['monitor']) && !strstr($gateway['monitor'], '%'))
185
				$gateway['monitor'] .= "%{$gateway['interface']}";
186
			if (!is_ipaddrv6($gwifip))
187
				continue; //Skip this target
188

    
189
			/*
190
			 * If the gateway is the same as the monitor we do not add a
191
			 * route as this will break the routing table.
192
			 * Add static routes for each gateway with their monitor IP
193
			 * not strictly necessary but is a added level of protection.
194
			 */
195
			if (is_ipaddrv6($gateway['gateway']) && $gateway['monitor'] != $gateway['gateway']) {
196
				log_error("Removing static route for monitor {$gateway['monitor']} and adding a new route through {$gateway['gateway']}");
197
				mwexec("/sbin/route change -host -inet6 " . escapeshellarg($gateway['monitor']) .
198
					" " . escapeshellarg($gateway['gateway']), true);
199
			}
200
		} else
201
			continue;
202

    
203
		$monitor_ips[] = $gateway['monitor'];
204
		$apingercfg = "target \"{$gateway['monitor']}\" {\n";
205
		$apingercfg .= "	description \"{$name}\"\n";
206
		$apingercfg .= "	srcip \"{$gwifip}\"\n";
207
		if (!empty($gateway['interval']) && intval($gateway['interval']) > 1)
208
			$apingercfg .= "	interval " . intval($gateway['interval']) . "s\n";
209
		$alarms = "";
210
		$alarmscfg = "";
211
		$override = false;
212
		if (!empty($gateway['losslow'])) {
213
			$alarmscfg .= "alarm loss \"{$name}loss\" {\n";
214
			$alarmscfg .= "\tpercent_low {$gateway['losslow']}\n";
215
			$alarmscfg .= "\tpercent_high {$gateway['losshigh']}\n";
216
			$alarmscfg .= "}\n";
217
			$alarms .= "\"{$name}loss\"";
218
			$override = true;
219
		} else {
220
			if ($override == true)
221
				$alarms .= ",";
222
			$alarms .= "\"loss\"";
223
			$override = true;
224
		}
225
		if (!empty($gateway['latencylow'])) {
226
			$alarmscfg .= "alarm delay \"{$name}delay\" {\n";
227
			$alarmscfg .= "\tdelay_low {$gateway['latencylow']}ms\n";
228
			$alarmscfg .= "\tdelay_high {$gateway['latencyhigh']}ms\n";
229
			$alarmscfg .= "}\n";
230
			if ($override == true)
231
				$alarms .= ",";
232
			$alarms .= "\"{$name}delay\"";
233
			$override = true;
234
		} else {
235
			if ($override == true)
236
				$alarms .= ",";
237
			$alarms .= "\"delay\"";
238
			$override = true;
239
		}
240
		if (!empty($gateway['down'])) {
241
			$alarmscfg .= "alarm down \"{$name}down\" {\n";
242
			$alarmscfg .= "\ttime {$gateway['down']}s\n";
243
			$alarmscfg .= "}\n";
244
			if ($override == true)
245
				$alarms .= ",";
246
			$alarms .= "\"{$name}down\"";
247
			$override = true;
248
		} else {
249
			if ($override == true)
250
				$alarms .= ",";
251
			$alarms .= "\"down\"";
252
			$override = true;
253
		}
254
		if ($override == true)
255
			$apingercfg .= "\talarms override {$alarms};\n";
256

    
257
		$apingercfg .= "	rrd file \"{$g['vardb_path']}/rrd/{$gateway['name']}-quality.rrd\"\n";
258
		$apingercfg .= "}\n";
259
		$apingercfg .= "\n";
260

    
261
		$apingerconfig .= $alarmscfg;
262
		$apingerconfig .= $apingercfg;
263
	}
264
	@file_put_contents("{$g['varetc_path']}/apinger.conf", $apingerconfig);
265
	unset($apingerconfig);
266

    
267
	if (is_dir("{$g['tmp_path']}"))
268
		chmod("{$g['tmp_path']}", 01777);
269
	if (!is_dir("{$g['vardb_path']}/rrd"))
270
		mkdir("{$g['vardb_path']}/rrd", 0775);
271

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

    
274
	if (isvalidpid("{$g['varrun_path']}/apinger.pid"))
275
		sigkillbypid("{$g['varrun_path']}/apinger.pid", "HUP");
276
	else {
277
		/* start a new apinger process */
278
		@unlink("{$g['varrun_path']}/apinger.status");
279
		sleep(1);
280
		mwexec_bg("/usr/local/sbin/apinger -c {$g['varetc_path']}/apinger.conf");
281
		sleep(1);
282
		sigkillbypid("{$g['varrun_path']}/apinger.pid", "USR1");
283
	}
284

    
285
	return 0;
286
}
287

    
288
/* return the status of the apinger targets as a array */
289
function return_gateways_status($byname = false) {
290
	global $config, $g;
291

    
292
	$apingerstatus = array();
293
	/* Always get the latest status from apinger */
294
	if (file_exists("{$g['varrun_path']}/apinger.pid"))
295
                sigkillbypid("{$g['varrun_path']}/apinger.pid", "USR1");
296
	if (file_exists("{$g['varrun_path']}/apinger.status")) {
297
		$apingerstatus = file("{$g['varrun_path']}/apinger.status");
298
	} else
299
		$apingerstatus = array();
300

    
301
	$status = array();
302
	foreach($apingerstatus as $line) {
303
		$info = explode("|", $line);
304
		if ($byname == false)
305
			$target = $info[0];
306
		else
307
			$target = $info[2];
308

    
309
		$status[$target] = array();
310
		$status[$target]['monitorip'] = $info[0];
311
		$status[$target]['srcip'] = $info[1];
312
		$status[$target]['name'] = $info[2];
313
		$status[$target]['lastcheck'] = $info[5] ? date('r', $info[5]) : date('r');
314
		$status[$target]['delay'] = empty($info[6]) ? "0ms" : round($info[6], 1) ."ms" ;
315
		$status[$target]['loss'] = empty($info[7]) ? "0.0%" : round($info[7], 1) . "%";
316
		$status[$target]['status'] = trim($info[8]);
317
	}
318

    
319
	/* tack on any gateways that have monitoring disabled
320
	 * or are down, which could cause gateway groups to fail */
321
	$gateways_arr = return_gateways_array();
322
	foreach($gateways_arr as $gwitem) {
323
		if(!isset($gwitem['monitor_disable']))
324
			continue;
325
		if(!is_ipaddr($gwitem['monitorip'])) {
326
			$realif = $gwitem['interface'];
327
			$tgtip = get_interface_gateway($realif);
328
			if (!is_ipaddr($tgtip))
329
				$tgtip = "none";
330
			$srcip = find_interface_ip($realif);
331
		} else {
332
			$tgtip = $gwitem['monitorip'];
333
			$srcip = find_interface_ip($realif);
334
		}
335
		if($byname == true)
336
			$target = $gwitem['name'];
337
		else
338
			$target = $tgtip;
339

    
340
		/* failsafe for down interfaces */
341
		if($target == "none") {
342
			$target = $gwitem['name'];
343
			$status[$target]['name'] = $gwitem['name'];
344
			$status[$target]['lastcheck'] = date('r');
345
			$status[$target]['delay'] = "0.0ms";
346
			$status[$target]['loss'] = "100.0%";
347
			$status[$target]['status'] = "down";
348
		} else {
349
			$status[$target]['monitorip'] = $tgtip;
350
			$status[$target]['srcip'] = $srcip;
351
			$status[$target]['name'] = $gwitem['name'];
352
			$status[$target]['lastcheck'] = date('r');
353
			$status[$target]['delay'] = "0.0ms";
354
			$status[$target]['loss'] = "0.0%";
355
			$status[$target]['status'] = "none";
356
		}
357
	}
358
	return($status);
359
}
360

    
361
/* Return all configured gateways on the system */
362
function return_gateways_array($disabled = false, $localhost = false) {
363
	global $config, $g;
364

    
365
	$gateways_arr = array();
366

    
367
	$found_defaultv4 = 0;
368
	$found_defaultv6 = 0;
369

    
370
	// Ensure the interface cache is up to date first
371
	$interfaces = get_interface_arr(true);
372
	$interfaces_v4 = array();
373
	$interfaces_v6 = array();
374

    
375
	$i = -1;
376
	/* Process/add all the configured gateways. */
377
	if (is_array($config['gateways']['gateway_item'])) {
378
		foreach ($config['gateways']['gateway_item'] as $gateway) {
379
			/* Increment it here to do not skip items */
380
			$i++;
381

    
382
			if (empty($config['interfaces'][$gateway['interface']]))
383
				continue;
384
			$wancfg = $config['interfaces'][$gateway['interface']];
385

    
386
			/* skip disabled interfaces */
387
			if ($disabled === false && !isset($wancfg['enable']))
388
				continue;
389

    
390
			/* if the gateway is dynamic and we can find the IPv4, Great! */
391
			if (empty($gateway['gateway']) || $gateway['gateway'] == "dynamic") {
392
				if ($gateway['ipprotocol'] == "inet") {
393
					/* we know which interfaces is dynamic, this should be made a function */
394
					$gateway['gateway'] = get_interface_gateway($gateway['interface']);
395
					/* no IP address found, set to dynamic */
396
					if (!is_ipaddrv4($gateway['gateway']))
397
						$gateway['gateway'] = "dynamic";
398
					$gateway['dynamic'] = true;
399
				}
400

    
401
				/* if the gateway is dynamic and we can find the IPv6, Great! */
402
				else if ($gateway['ipprotocol'] == "inet6") {
403
					/* we know which interfaces is dynamic, this should be made a function, and for v6 too */
404
					$gateway['gateway'] = get_interface_gateway_v6($gateway['interface']);
405
					/* no IPv6 address found, set to dynamic */
406
					if (!is_ipaddrv6($gateway['gateway']))
407
						$gateway['gateway'] = "dynamic";
408
					$gateway['dynamic'] = true;
409
				}
410
			} else {
411
				/* getting this detection right is hard at this point because we still don't
412
				 * store the address family in the gateway item */
413
				if (is_ipaddrv4($gateway['gateway']))
414
					$gateway['ipprotocol'] = "inet";
415
				else if(is_ipaddrv6($gateway['gateway']))
416
					$gateway['ipprotocol'] = "inet6";
417
			}
418

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

    
424
			$gateway['friendlyiface'] = $gateway['interface'];
425

    
426
			/* special treatment for tunnel interfaces */
427
			if ($gateway['ipprotocol'] == "inet6") {
428
				$gateway['interface'] = get_real_interface($gateway['interface'], "inet6", false, false);
429
				$interfaces_v6[$gateway['friendlyiface']] = $gateway['friendlyiface'];
430
			} else {
431
				$gateway['interface'] = get_real_interface($gateway['interface'], "all", false, false);
432
				$interfaces_v4[$gateway['friendlyiface']] = $gateway['friendlyiface'];
433
			}
434

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

    
448
			$gateways_arr[$gateway['name']] = $gateway;
449
		}
450
	}
451
	unset($gateway);
452

    
453
	/* Loop through all interfaces with a gateway and add it to a array */
454
	if ($disabled == false)
455
		$iflist = get_configured_interface_with_descr();
456
	else
457
		$iflist = get_configured_interface_with_descr(false, true);
458

    
459
	/* Process/add dynamic v4 gateways. */
460
	foreach($iflist as $ifname => $friendly ) {
461
		if(! interface_has_gateway($ifname))
462
			continue;
463

    
464
		if (empty($config['interfaces'][$ifname]))
465
			continue;
466

    
467
		$ifcfg = &$config['interfaces'][$ifname];
468
		if(!isset($ifcfg['enable']))
469
			continue;
470

    
471
		if(!empty($ifcfg['ipaddr']) && is_ipaddrv4($ifcfg['ipaddr']))
472
			continue;
473

    
474
		if (isset($interfaces_v4[$ifname]))
475
			continue;
476

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

    
492
		$gateway = array();
493
		$gateway['dynamic'] = false;
494
		$gateway['ipprotocol'] = "inet";
495
		$gateway['gateway'] = get_interface_gateway($ifname, $gateway['dynamic']);
496
		$gateway['interface'] = get_real_interface($ifname);
497
		$gateway['friendlyiface'] = $ifname;
498
		$gateway['name'] = "{$friendly}{$ctype}";
499
		$gateway['attribute'] = "system";
500

    
501
		if (($gateway['dynamic'] === "default") && ($found_defaultv4 == 0)) {
502
			$gateway['defaultgw'] = true;
503
			$gateway['dynamic'] = true;
504
			$found_defaultv4 = 1;
505
		}
506
		/* Loopback dummy for dynamic interfaces without a IP */
507
		if (!is_ipaddrv4($gateway['gateway']) && $gateway['dynamic'] == true)
508
			$gateway['gateway'] = "dynamic";
509

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

    
517
		if (is_ipaddrv4($gateway['gateway']))
518
			$gateway['monitor'] = $gateway['gateway'];
519

    
520
		$gateway['descr'] = "Interface {$friendly}{$ctype} Gateway";
521
		$gateways_arr[$gateway['name']] = $gateway;
522
	}
523
	unset($gateway);
524

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

    
531
		if(! interface_has_gatewayv6($ifname))
532
			continue;
533

    
534
		if (empty($config['interfaces'][$ifname]))
535
			continue;
536

    
537
		$ifcfg = &$config['interfaces'][$ifname];
538
		if(!isset($ifcfg['enable']))
539
			continue;
540

    
541
		if(!empty($ifcfg['ipaddrv6']) && is_ipaddrv6($ifcfg['ipaddrv6']))
542
			continue;
543

    
544
		if(isset($interfaces_v6[$ifname]))
545
			continue;
546

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

    
565
		$gateway = array();
566
		$gateway['dynamic'] = false;
567
		$gateway['ipprotocol'] = "inet6";
568
		$gateway['gateway'] = get_interface_gateway_v6($ifname, $gateway['dynamic']);
569
		$gateway['interface'] = get_real_interface($ifname, "inet6");
570
		switch($ifcfg['ipaddrv6']) {
571
			case "6rd":
572
			case "6to4":
573
				$gateway['dynamic'] = "default";
574
				break;
575
		}
576
		$gateway['friendlyiface'] = $ifname;
577
		$gateway['name'] = "{$friendly}{$ctype}";
578
		$gateway['attribute'] = "system";
579

    
580
		if (($gateway['dynamic'] === "default")  && ($found_defaultv6 == 0)) {
581
			$gateway['defaultgw'] = true;
582
			$gateway['dynamic'] = true;
583
			$found_defaultv6 = 1;
584
		}
585

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

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

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

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

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

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

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

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

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

    
708
	if (isset($config['system']['gw_switch_default'])) {
709
		fixup_default_gateway("inet", $gateways_status, $gateways_arr);
710
		fixup_default_gateway("inet6", $gateways_status, $gateways_arr);
711
	}
712
	if (is_array($config['gateways']['gateway_group'])) {
713
		$carplist = get_configured_carp_interface_list();
714
		foreach ($config['gateways']['gateway_group'] as $group) {
715
			/* create array with group gateways members separated by tier */
716
			$tiers = array();
717
			$backupplan = array();
718
			$gwvip_arr = array();
719
			foreach ($group['item'] as $item) {
720
				list($gwname, $tier, $vipname) = explode("|", $item);
721

    
722
				if (is_ipaddr($carplist[$vipname])) {
723
					if (!is_array($gwvip_arr[$group['name']]))
724
						$gwvip_arr[$group['name']] = array();
725
					$gwvip_arr[$group['name']][$gwname] = $vipname;
726
				}
727

    
728
				/* Do it here rather than reiterating again the group in case no member is up. */
729
				if (!is_array($backupplan[$tier]))
730
					$backupplan[$tier] = array();
731
				$backupplan[$tier][] = $gwname;
732

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

    
776
			/* we do not really foreach the tiers as we stop after the first tier */
777
			foreach ($tiers as $tieridx => $tier) {
778
				/* process all gateways in this tier */
779
				foreach ($tier as $member) {
780
					/* determine interface gateway */
781
					if (isset($gateways_arr[$member])) {
782
						$gateway = $gateways_arr[$member];
783
						$int = $gateway['interface'];
784
						$gatewayip = "";
785
						if(is_ipaddr($gateway['gateway']))
786
							$gatewayip = $gateway['gateway'];
787
						else if (!empty($int))
788
							$gatewayip = get_interface_gateway($gateway['friendlyiface']);
789

    
790
						if (!empty($int)) {
791
							$gateway_groups_array[$group['name']]['ipprotocol'] = $gateway['ipprotocol'];
792
							if (is_ipaddr($gatewayip)) {
793
								$groupmember = array();
794
								$groupmember['int']  = $int;
795
								$groupmember['gwip']  = $gatewayip;
796
								$groupmember['weight']  = isset($gateway['weight']) ? $gateway['weight'] : 1;
797
								if (is_array($gwvip_arr[$group['name']])&& !empty($gwvip_arr[$group['name']][$member]))
798
									$groupmember['vip'] = $gwvip_arr[$group['name']][$member];
799
								$gateway_groups_array[$group['name']][] = $groupmember;
800
							}
801
						}
802
					}
803
				}
804
				/* we should have the 1st available tier now, exit stage left */
805
				if (count($gateway_groups_array[$group['name']]) > 0)
806
					break;
807
				else
808
					log_error("GATEWAYS: Group {$group['name']} did not have any gateways up on tier {$tieridx}!");
809
			}
810
		}
811
	}
812

    
813
	return ($gateway_groups_array);
814
}
815

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

    
832
function lookup_gateway_ip_by_name($name) {
833

    
834
	$gateways_arr = return_gateways_array(false, true);
835
	foreach ($gateways_arr as $gname => $gw) {
836
		if ($gw['name'] === $name || $gname === $name)
837
			return $gw['gateway'];
838
	}
839

    
840
	return false;
841
}
842

    
843
function lookup_gateway_monitor_ip_by_name($name) {
844

    
845
	$gateways_arr = return_gateways_array(false, true);
846
	if (!empty($gateways_arr[$name])) {
847
		$gateway = $gateways_arr[$name];
848
		if(!is_ipaddr($gateway['monitor']))
849
			return $gateway['gateway'];
850

    
851
		return $gateway['monitor'];
852
	}
853

    
854
	return (false);
855
}
856

    
857
function lookup_gateway_interface_by_name($name) {
858

    
859
	$gateways_arr = return_gateways_array(false, true);
860
	if (!empty($gateways_arr[$name])) {
861
		$interfacegw = $gateways_arr[$name]['friendlyiface'];
862
		return ($interfacegw);
863
	}
864

    
865
	return (false);
866
}
867

    
868
function get_interface_gateway($interface, &$dynamic = false) {
869
	global $config, $g;
870

    
871
	$gw = NULL;
872

    
873
	$gwcfg = $config['interfaces'][$interface];
874
	if (!empty($gwcfg['gateway']) && is_array($config['gateways']['gateway_item'])) {
875
		foreach($config['gateways']['gateway_item'] as $gateway) {
876
			if(($gateway['name'] == $gwcfg['gateway']) && (is_ipaddrv4($gateway['gateway']))) {
877
				$gw = $gateway['gateway'];
878
				break;
879
			}
880
		}
881
	}
882

    
883
	// for dynamic interfaces we handle them through the $interface_router file.
884
	if (!is_ipaddrv4($gw) && !is_ipaddrv4($gwcfg['ipaddr'])) {
885
		$realif = get_real_interface($interface);
886
		if (file_exists("{$g['tmp_path']}/{$realif}_router")) {
887
			$gw = trim(file_get_contents("{$g['tmp_path']}/{$realif}_router"), " \n");
888
			$dynamic = true;
889
		}
890
		if (file_exists("{$g['tmp_path']}/{$realif}_defaultgw"))
891
			$dynamic = "default";
892

    
893
	}
894

    
895
	/* return gateway */
896
	return ($gw);
897
}
898

    
899
function get_interface_gateway_v6($interface, &$dynamic = false) {
900
	global $config, $g;
901

    
902
	$gw = NULL;
903
	$gwcfg = $config['interfaces'][$interface];
904
	if (!empty($gwcfg['gatewayv6']) && is_array($config['gateways']['gateway_item'])) {
905
		foreach($config['gateways']['gateway_item'] as $gateway) {
906
			if(($gateway['name'] == $gwcfg['gatewayv6']) && (is_ipaddrv6($gateway['gateway']))) {
907
				$gw = $gateway['gateway'];
908
				break;
909
			}
910
		}
911
	}
912

    
913
	// for dynamic interfaces we handle them through the $interface_router file.
914
	if (!is_ipaddrv6($gw) && !is_ipaddrv6($gwcfg['ipaddrv6'])) {
915
			$realif = get_real_interface($interface);
916
			if (file_exists("{$g['tmp_path']}/{$realif}_routerv6")) {
917
				$gw = trim(file_get_contents("{$g['tmp_path']}/{$realif}_routerv6"), " \n");
918
				$dynamic = true;
919
			}
920
			if (file_exists("{$g['tmp_path']}/{$realif}_defaultgwv6"))
921
				$dynamic = "default";
922

    
923
	}
924
	/* return gateway */
925
	return ($gw);
926
}
927

    
928
/* Check a IP address against a gateway IP or name
929
 * to verify it's address family */
930
function validate_address_family($ipaddr, $gwname) {
931
	$v4ip = false;
932
	$v6ip = false;
933
	$v4gw = false;
934
	$v6gw = false;
935

    
936
	if(is_ipaddrv4($ipaddr))
937
		$v4ip = true;
938
	if(is_ipaddrv6($ipaddr))
939
		$v6ip = true;
940
	if(is_ipaddrv4($gwname))
941
		$v4gw = true;
942
	if(is_ipaddrv6($gwname))
943
		$v6gw = true;
944

    
945
	if($v4ip && $v4gw)
946
		return true;
947
	if($v6ip && $v6gw)
948
		return true;
949

    
950
	/* still no match, carry on, lookup gateways */
951
	if(is_ipaddrv4(lookup_gateway_ip_by_name($gwname)))
952
		$v4gw = true;
953
	if(is_ipaddrv6(lookup_gateway_ip_by_name($gwname)))
954
		$v6gw = true;
955

    
956
	$gw_array = return_gateways_array();
957
	if(is_array($gw_array[$gwname])) {
958
		switch($gw_array[$gwname]['ipprotocol']) {
959
			case "inet":
960
				$v4gw = true;
961
				break;
962
			case "inet6":
963
				$v6gw = true;
964
				break;
965
		}
966
	}
967

    
968
	if($v4ip && $v4gw)
969
		return true;
970
	if($v6ip && $v6gw)
971
		return true;
972

    
973
	return false;
974
}
975

    
976
/* check if a interface is part of a gateway group */
977
function interface_gateway_group_member($interface) {
978
	global $config;
979

    
980
	if (is_array($config['gateways']['gateway_group']))
981
		$groups = $config['gateways']['gateway_group'];
982
	else
983
		return false;
984

    
985
	$gateways_arr = return_gateways_array(false, true);
986
	foreach($groups as $group) {
987
		if(is_array($group['item'])) {
988
			foreach($group['item'] as $item) {
989
				$elements = explode("|", $item);
990
				$gwname = $elements[0];
991
				if ($interface == $gateways_arr[$gwname]['interface']) {
992
					unset($gateways_arr);
993
					return true;
994
				}
995
			}
996
		}
997
	}
998
	unset($gateways_arr);
999

    
1000
	return false;
1001
}
1002

    
1003
function gateway_is_gwgroup_member($name) {
1004
	global $config;
1005

    
1006
	if (is_array($config['gateways']['gateway_group']))
1007
		$groups = $config['gateways']['gateway_group'];
1008
	else
1009
		return false;
1010

    
1011
	$members = array();
1012
	foreach($groups as $group) {
1013
		if (is_array($group['item'])) {
1014
			foreach($group['item'] as $item) {
1015
				$elements = explode("|", $item);
1016
				$gwname = $elements[0];
1017
				if ($name == $elements[0])
1018
					$members[] = $group['name'];
1019
			}
1020
		}
1021
	}
1022

    
1023
	return $members;
1024
}
1025
?>
(24-24/66)