Project

General

Profile

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

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

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

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

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

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

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

    
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
		"avg_delay_samples" => "10",
44
		"avg_loss_samples" => "50",
45
		"avg_loss_delay_samples" => "20");
46
	}
47

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

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

    
63
	$apinger_default = return_apinger_defaults();
64
	$apingerconfig = <<<EOD
65

    
66
# pfSense apinger configuration file. Automatically Generated!
67

    
68
## User and group the pinger should run as
69
user "root"
70
group "wheel"
71

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

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

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

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

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

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

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

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

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

    
123
target default {
124
	## How often the probe should be sent
125
	interval {$apinger_default['interval']}s
126

    
127
	## How many replies should be used to compute average delay
128
	## for controlling "delay" alarms
129
	avg_delay_samples {$apinger_default['avg_delay_samples']}
130

    
131
	## How many probes should be used to compute average loss
132
	avg_loss_samples {$apinger_default['avg_loss_samples']}
133

    
134
	## The delay (in samples) after which loss is computed
135
	## without this delays larger than interval would be treated as loss
136
	avg_loss_delay_samples {$apinger_default['avg_loss_delay_samples']}
137

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

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

    
145
EOD;
146

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

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

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

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

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

    
206
		$monitor_ips[] = $gateway['monitor'];
207
		$apingercfg = "target \"{$gateway['monitor']}\" {\n";
208
		$apingercfg .= "	description \"{$name}\"\n";
209
		$apingercfg .= "	srcip \"{$gwifip}\"\n";
210

    
211
		## How often the probe should be sent
212
		if (!empty($gateway['interval']) &&  is_numeric($gateway['interval'])) {
213
			$interval = intval($gateway['interval']);	# Restrict to Integer
214
			if ($interval <  1) $interval =  1;	# Minimum
215
			if ($interval != $apinger_default['interval'])	# If not default value
216
				$apingercfg .= "	interval " . $interval . "s\n";
217
		}
218

    
219
		## How many replies should be used to compute average delay 
220
		## for controlling "delay" alarms
221
		if (!empty($gateway['avg_delay_samples']) && is_numeric($gateway['avg_delay_samples'])) {
222
			$avg_delay_samples = intval($gateway['avg_delay_samples']);	# Restrict to Integer
223
			if ($avg_delay_samples <  1) $avg_delay_samples =  1;	# Minimum
224
			if ($avg_delay_samples != $apinger_default['avg_delay_samples'])	# If not default value
225
				$apingercfg .= "	avg_delay_samples " . $avg_delay_samples . "\n";
226
		}
227

    
228
		## How many probes should be used to compute average loss
229
		if (!empty($gateway['avg_loss_samples']) && is_numeric($gateway['avg_loss_samples'])) {
230
			$avg_loss_samples = intval($gateway['avg_loss_samples']);	# Restrict to Integer
231
			if ($avg_loss_samples < 1) $avg_loss_samples = 1;	# Minimum
232
			if ($avg_loss_samples != $apinger_default['avg_loss_samples'])	# If not default value
233
				$apingercfg .= "	avg_loss_samples " . $avg_loss_samples . "\n";
234
		}
235

    
236
		## The delay (in samples) after which loss is computed
237
		## without this delays larger than interval would be treated as loss
238
		if (!empty($gateway['avg_loss_delay_samples']) && is_numeric($gateway['avg_loss_delay_samples'])) {
239
			$avg_loss_delay_samples = intval($gateway['avg_loss_delay_samples']);	# Restrict to Integer
240
			if ($avg_loss_delay_samples < 1) $avg_loss_delay_samples = 1;	# Minimum
241
			if ($avg_loss_delay_samples != $apinger_default['avg_loss_delay_samples'])	# If not default value
242
				$apingercfg .= "	avg_loss_delay_samples " . $avg_loss_delay_samples . "\n";
243
		}
244

    
245
		$alarms = "";
246
		$alarmscfg = "";
247
		$override = false;
248
		if (!empty($gateway['losslow'])) {
249
			$alarmscfg .= "alarm loss \"{$name}loss\" {\n";
250
			$alarmscfg .= "\tpercent_low {$gateway['losslow']}\n";
251
			$alarmscfg .= "\tpercent_high {$gateway['losshigh']}\n";
252
			$alarmscfg .= "}\n";
253
			$alarms .= "\"{$name}loss\"";
254
			$override = true;
255
		} else {
256
			if ($override == true)
257
				$alarms .= ",";
258
			$alarms .= "\"loss\"";
259
			$override = true;
260
		}
261
		if (!empty($gateway['latencylow'])) {
262
			$alarmscfg .= "alarm delay \"{$name}delay\" {\n";
263
			$alarmscfg .= "\tdelay_low {$gateway['latencylow']}ms\n";
264
			$alarmscfg .= "\tdelay_high {$gateway['latencyhigh']}ms\n";
265
			$alarmscfg .= "}\n";
266
			if ($override == true)
267
				$alarms .= ",";
268
			$alarms .= "\"{$name}delay\"";
269
			$override = true;
270
		} else {
271
			if ($override == true)
272
				$alarms .= ",";
273
			$alarms .= "\"delay\"";
274
			$override = true;
275
		}
276
		if (!empty($gateway['down'])) {
277
			$alarmscfg .= "alarm down \"{$name}down\" {\n";
278
			$alarmscfg .= "\ttime {$gateway['down']}s\n";
279
			$alarmscfg .= "}\n";
280
			if ($override == true)
281
				$alarms .= ",";
282
			$alarms .= "\"{$name}down\"";
283
			$override = true;
284
		} else {
285
			if ($override == true)
286
				$alarms .= ",";
287
			$alarms .= "\"down\"";
288
			$override = true;
289
		}
290
		if ($override == true)
291
			$apingercfg .= "\talarms override {$alarms};\n";
292

    
293
		if (isset($gateway['force_down']))
294
			$apingercfg .= "\tforce_down on\n";
295

    
296
		$apingercfg .= "	rrd file \"{$g['vardb_path']}/rrd/{$gateway['name']}-quality.rrd\"\n";
297
		$apingercfg .= "}\n";
298
		$apingercfg .= "\n";
299

    
300
		$apingerconfig .= $alarmscfg;
301
		$apingerconfig .= $apingercfg;
302
	}
303
	@file_put_contents("{$g['varetc_path']}/apinger.conf", $apingerconfig);
304
	unset($apingerconfig);
305

    
306
	if (is_dir("{$g['tmp_path']}"))
307
		chmod("{$g['tmp_path']}", 01777);
308
	if (!is_dir("{$g['vardb_path']}/rrd"))
309
		mkdir("{$g['vardb_path']}/rrd", 0775);
310

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

    
313
	if (isvalidpid("{$g['varrun_path']}/apinger.pid"))
314
		sigkillbypid("{$g['varrun_path']}/apinger.pid", "HUP");
315
	else {
316
		/* start a new apinger process */
317
		@unlink("{$g['varrun_path']}/apinger.status");
318
		sleep(1);
319
		mwexec_bg("/usr/local/sbin/apinger -c {$g['varetc_path']}/apinger.conf");
320
		sleep(1);
321
		sigkillbypid("{$g['varrun_path']}/apinger.pid", "USR1");
322
	}
323

    
324
	return 0;
325
}
326

    
327
/* return the status of the apinger targets as a array */
328
function return_gateways_status($byname = false) {
329
	global $config, $g;
330

    
331
	$apingerstatus = array();
332
	/* Always get the latest status from apinger */
333
	if (file_exists("{$g['varrun_path']}/apinger.pid"))
334
                sigkillbypid("{$g['varrun_path']}/apinger.pid", "USR1");
335
	if (file_exists("{$g['varrun_path']}/apinger.status")) {
336
		$apingerstatus = file("{$g['varrun_path']}/apinger.status");
337
	} else
338
		$apingerstatus = array();
339

    
340
	$status = array();
341
	foreach($apingerstatus as $line) {
342
		$info = explode("|", $line);
343
		if ($byname == false)
344
			$target = $info[0];
345
		else
346
			$target = $info[2];
347

    
348
		$status[$target] = array();
349
		$status[$target]['monitorip'] = $info[0];
350
		$status[$target]['srcip'] = $info[1];
351
		$status[$target]['name'] = $info[2];
352
		$status[$target]['lastcheck'] = $info[5] ? date('r', $info[5]) : date('r');
353
		$status[$target]['delay'] = empty($info[6]) ? "0ms" : round($info[6], 1) ."ms" ;
354
		$status[$target]['loss'] = empty($info[7]) ? "0.0%" : round($info[7], 1) . "%";
355
		$status[$target]['status'] = trim($info[8]);
356
	}
357

    
358
	/* tack on any gateways that have monitoring disabled
359
	 * or are down, which could cause gateway groups to fail */
360
	$gateways_arr = return_gateways_array();
361
	foreach($gateways_arr as $gwitem) {
362
		if(!isset($gwitem['monitor_disable']))
363
			continue;
364
		if(!is_ipaddr($gwitem['monitorip'])) {
365
			$realif = $gwitem['interface'];
366
			$tgtip = get_interface_gateway($realif);
367
			if (!is_ipaddr($tgtip))
368
				$tgtip = "none";
369
			$srcip = find_interface_ip($realif);
370
		} else {
371
			$tgtip = $gwitem['monitorip'];
372
			$srcip = find_interface_ip($realif);
373
		}
374
		if($byname == true)
375
			$target = $gwitem['name'];
376
		else
377
			$target = $tgtip;
378

    
379
		/* failsafe for down interfaces */
380
		if($target == "none") {
381
			$target = $gwitem['name'];
382
			$status[$target]['name'] = $gwitem['name'];
383
			$status[$target]['lastcheck'] = date('r');
384
			$status[$target]['delay'] = "0.0ms";
385
			$status[$target]['loss'] = "100.0%";
386
			$status[$target]['status'] = "down";
387
		} else {
388
			$status[$target]['monitorip'] = $tgtip;
389
			$status[$target]['srcip'] = $srcip;
390
			$status[$target]['name'] = $gwitem['name'];
391
			$status[$target]['lastcheck'] = date('r');
392
			$status[$target]['delay'] = "0.0ms";
393
			$status[$target]['loss'] = "0.0%";
394
			$status[$target]['status'] = "none";
395
		}
396
	}
397
	return($status);
398
}
399

    
400
/* Return all configured gateways on the system */
401
function return_gateways_array($disabled = false, $localhost = false, $inactive = false) {
402
	global $config, $g;
403

    
404
	$gateways_arr = array();
405

    
406
	$found_defaultv4 = 0;
407
	$found_defaultv6 = 0;
408

    
409
	// Ensure the interface cache is up to date first
410
	$interfaces = get_interface_arr(true);
411
	$interfaces_v4 = array();
412
	$interfaces_v6 = array();
413

    
414
	$i = -1;
415
	/* Process/add all the configured gateways. */
416
	if (is_array($config['gateways']['gateway_item'])) {
417
		foreach ($config['gateways']['gateway_item'] as $gateway) {
418
			/* Increment it here to do not skip items */
419
			$i++;
420

    
421
			if (empty($config['interfaces'][$gateway['interface']])) {
422
				if ($inactive === false)
423
					continue;
424
				else
425
					$gateway['inactive'] = true;
426
			}
427
			$wancfg = $config['interfaces'][$gateway['interface']];
428

    
429
			/* skip disabled interfaces */
430
			if ($disabled === false && (!isset($wancfg['enable']) || isset($gateway['disabled'])))
431
				continue;
432

    
433
			/* if the gateway is dynamic and we can find the IPv4, Great! */
434
			if (empty($gateway['gateway']) || $gateway['gateway'] == "dynamic" || $gateway['gateway'] == "dynamic6") {
435
				if ($gateway['ipprotocol'] == "inet") {
436
					/* we know which interfaces is dynamic, this should be made a function */
437
					$gateway['gateway'] = get_interface_gateway($gateway['interface']);
438
					/* no IP address found, set to dynamic */
439
					if (!is_ipaddrv4($gateway['gateway']))
440
						$gateway['gateway'] = "dynamic";
441
					$gateway['dynamic'] = true;
442
				}
443

    
444
				/* if the gateway is dynamic6 and we can find the IPv6, Great! */
445
				else if ($gateway['ipprotocol'] == "inet6") {
446
					/* we know which interfaces is dynamic, this should be made a function, and for v6 too */
447
					$gateway['gateway'] = get_interface_gateway_v6($gateway['interface']);
448
					/* no IPv6 address found, set to dynamic6 */
449
					if (!is_ipaddrv6($gateway['gateway']))
450
						$gateway['gateway'] = "dynamic6";
451
					$gateway['dynamic'] = true;
452
				}
453
			} else {
454
				/* getting this detection right is hard at this point because we still don't
455
				 * store the address family in the gateway item */
456
				if (is_ipaddrv4($gateway['gateway']))
457
					$gateway['ipprotocol'] = "inet";
458
				else if(is_ipaddrv6($gateway['gateway']))
459
					$gateway['ipprotocol'] = "inet6";
460
			}
461

    
462
			if (isset($gateway['monitor_disable']))
463
				$gateway['monitor_disable'] = true;
464
			else if (empty($gateway['monitor']))
465
				$gateway['monitor'] = $gateway['gateway'];
466

    
467
			$gateway['friendlyiface'] = $gateway['interface'];
468

    
469
			/* special treatment for tunnel interfaces */
470
			if ($gateway['ipprotocol'] == "inet6") {
471
				$gateway['interface'] = get_real_interface($gateway['interface'], "inet6", false, false);
472
				$interfaces_v6[$gateway['friendlyiface']] = $gateway['friendlyiface'];
473
			} else {
474
				$gateway['interface'] = get_real_interface($gateway['interface'], "all", false, false);
475
				$interfaces_v4[$gateway['friendlyiface']] = $gateway['friendlyiface'];
476
			}
477

    
478
			/* entry has a default flag, use it */
479
			if (isset($gateway['defaultgw'])) {
480
				if ($gateway['ipprotocol'] == "inet") {
481
					$gateway['defaultgw'] = true;
482
					$found_defaultv4 = 1;
483
				} else if ($gateway['ipprotocol'] == "inet6") {
484
					$gateway['defaultgw'] = true;
485
					$found_defaultv6 = 1;
486
				}
487
			}
488
			/* include the gateway index as the attribute */
489
			$gateway['attribute'] = $i;
490

    
491
			$gateways_arr[$gateway['name']] = $gateway;
492
		}
493
	}
494
	unset($gateway);
495

    
496
	/* Loop through all interfaces with a gateway and add it to a array */
497
	if ($disabled == false)
498
		$iflist = get_configured_interface_with_descr();
499
	else
500
		$iflist = get_configured_interface_with_descr(false, true);
501

    
502
	/* Process/add dynamic v4 gateways. */
503
	foreach($iflist as $ifname => $friendly ) {
504
		if(! interface_has_gateway($ifname))
505
			continue;
506

    
507
		if (empty($config['interfaces'][$ifname]))
508
			continue;
509

    
510
		$ifcfg = &$config['interfaces'][$ifname];
511
		if(!isset($ifcfg['enable']))
512
			continue;
513

    
514
		if(!empty($ifcfg['ipaddr']) && is_ipaddrv4($ifcfg['ipaddr']))
515
			continue;
516

    
517
		if (isset($interfaces_v4[$ifname]))
518
			continue;
519

    
520
		$ctype = "";
521
		switch($ifcfg['ipaddr']) {
522
			case "dhcp":
523
			case "pppoe":
524
			case "pptp":
525
			case "ppp":
526
				$ctype = strtoupper($ifcfg['ipaddr']);
527
				break;
528
			default:
529
				if (substr($ifcfg['if'], 0, 4) ==  "ovpn")
530
					$ctype = "VPNv4";
531
				break;
532
		}
533
		$ctype = "_". strtoupper($ctype);
534

    
535
		$gateway = array();
536
		$gateway['dynamic'] = false;
537
		$gateway['ipprotocol'] = "inet";
538
		$gateway['gateway'] = get_interface_gateway($ifname, $gateway['dynamic']);
539
		$gateway['interface'] = get_real_interface($ifname);
540
		$gateway['friendlyiface'] = $ifname;
541
		$gateway['name'] = "{$friendly}{$ctype}";
542
		$gateway['attribute'] = "system";
543

    
544
		if (($gateway['dynamic'] === "default") && ($found_defaultv4 == 0)) {
545
			$gateway['defaultgw'] = true;
546
			$gateway['dynamic'] = true;
547
			$found_defaultv4 = 1;
548
		}
549
		/* Loopback dummy for dynamic interfaces without a IP */
550
		if (!is_ipaddrv4($gateway['gateway']) && $gateway['dynamic'] == true)
551
			$gateway['gateway'] = "dynamic";
552

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

    
560
		if (is_ipaddrv4($gateway['gateway']))
561
			$gateway['monitor'] = $gateway['gateway'];
562

    
563
		$gateway['descr'] = "Interface {$friendly}{$ctype} Gateway";
564
		$gateways_arr[$gateway['name']] = $gateway;
565
	}
566
	unset($gateway);
567

    
568
	/* Process/add dynamic v6 gateways. */
569
	foreach($iflist as $ifname => $friendly ) {
570
		/* If the user has disabled IPv6, they probably don't want any IPv6 gateways. */
571
		if (!isset($config['system']['ipv6allow']))
572
			break;
573

    
574
		if(! interface_has_gatewayv6($ifname))
575
			continue;
576

    
577
		if (empty($config['interfaces'][$ifname]))
578
			continue;
579

    
580
		$ifcfg = &$config['interfaces'][$ifname];
581
		if(!isset($ifcfg['enable']))
582
			continue;
583

    
584
		if(!empty($ifcfg['ipaddrv6']) && is_ipaddrv6($ifcfg['ipaddrv6']))
585
			continue;
586

    
587
		if(isset($interfaces_v6[$ifname]))
588
			continue;
589

    
590
		$ctype = "";
591
		switch($ifcfg['ipaddrv6']) {
592
			case "slaac":
593
			case "dhcp6":
594
			case "6to4":
595
			case "6rd":
596
				$ctype = strtoupper($ifcfg['ipaddrv6']);
597
				break;
598
			default:
599
				$tunnelif = substr($ifname['if'], 0, 3);
600
				if (substr($ifcfg['if'], 0, 4) ==  "ovpn")
601
					$ctype = "VPNv6";
602
				else if ($tunnelif == "gif" || $tunnelif == "gre")
603
					$ctype = "TUNNELv6";
604
				break;
605
		}
606
		$ctype = "_". strtoupper($ctype);
607

    
608
		$gateway = array();
609
		$gateway['dynamic'] = false;
610
		$gateway['ipprotocol'] = "inet6";
611
		$gateway['gateway'] = get_interface_gateway_v6($ifname, $gateway['dynamic']);
612
		$gateway['interface'] = get_real_interface($ifname, "inet6");
613
		switch($ifcfg['ipaddrv6']) {
614
			case "6rd":
615
			case "6to4":
616
				$gateway['dynamic'] = "default";
617
				break;
618
		}
619
		$gateway['friendlyiface'] = $ifname;
620
		$gateway['name'] = "{$friendly}{$ctype}";
621
		$gateway['attribute'] = "system";
622

    
623
		if (($gateway['dynamic'] === "default")  && ($found_defaultv6 == 0)) {
624
			$gateway['defaultgw'] = true;
625
			$gateway['dynamic'] = true;
626
			$found_defaultv6 = 1;
627
		}
628

    
629
		/* Loopback dummy for dynamic interfaces without a IP */
630
		if (!is_ipaddrv6($gateway['gateway']) && $gateway['dynamic'] == true)
631
			$gateway['gateway'] = "dynamic6";
632

    
633
		/* automatically skip known static and dynamic gateways we have a array entry for */
634
		foreach($gateways_arr as $gateway_item) {
635
			if ((($ifname == $gateway_item['friendlyiface'] && $friendly == $gateway_item['name']) && ($gateway['ipprotocol'] == $gateway_item['ipprotocol'])) ||
636
				($ifname == $gateway_item['friendlyiface'] && $gateway_item['dynamic'] == true) && ($gateway['ipprotocol'] == $gateway_item['ipprotocol']))
637
					continue 2;
638
		}
639

    
640
		if (is_ipaddrv6($gateway['gateway']))
641
			$gateway['monitor'] = $gateway['gateway'];
642

    
643
		$gateway['descr'] = "Interface {$friendly}{$ctype} Gateway";
644
		$gateways_arr[$gateway['name']] = $gateway;
645
	}
646
	unset($gateway);
647

    
648
	/* FIXME: Should this be enabled.
649
	 * Some interface like wan might be default but have no info recorded
650
	 * the config. */
651
	/* this is a fallback if all else fails and we want to get packets out @smos */
652
	if ($found_defaultv4 == 0 || $found_defaultv6 == 0) {
653
		foreach ($gateways_arr as &$gateway) {
654
			if (($gateway['friendlyiface'] == "wan") && ($found_defaultv4 == 0) && (!isset($gateway['ipprotocol']) || ($gateway['ipprotocol'] == "inet"))) {
655
				if (file_exists("{$g['tmp_path']}/{$gateway['interface']}_defaultgw")) {
656
					$gateway['defaultgw'] = true;
657
					$found_defaultv4 = 1;
658
				}
659
			}
660
			if (($gateway['friendlyiface'] == "wan") && ($found_defaultv6 == 0) && ($gateway['ipprotocol'] == "inet6")) {
661
				if (file_exists("{$g['tmp_path']}/{$gateway['interface']}_defaultgwv6")) {
662
					$gateway['defaultgw'] = true;
663
					$found_defaultv6 = 1;
664
				}
665
			}
666
		}
667
	}
668

    
669
	if($localhost === true) {
670
		/* attach localhost for Null routes */
671
		$gwlo4 = array();
672
		$gwlo4['name'] = "Null4";
673
		$gwlo4['interface'] = "lo0";
674
		$gwlo4['ipprotocol'] = "inet";
675
		$gwlo4['gateway'] = "127.0.0.1";
676
		$gwlo6 = array();
677
		$gwlo6['name'] = "Null6";
678
		$gwlo6['interface'] = "lo0";
679
		$gwlo6['ipprotocol'] = "inet6";
680
		$gwlo6['gateway'] = "::1";
681
		$gateways_arr['Null4'] = $gwlo4;
682
		$gateways_arr['Null6'] = $gwlo6;
683
	}
684
	return($gateways_arr);
685
}
686

    
687
function fixup_default_gateway($ipprotocol, $gateways_status, $gateways_arr) {
688
	global $config, $g;
689
	/*
690
	 * NOTE: The code below is meant to replace the default gateway when it goes down.
691
	 *	This facilitates services running on pfSense itself and are not handled by a PBR to continue working.
692
	 */
693
	$upgw = "";
694
	$dfltgwdown = false;
695
	$dfltgwfound = false;
696
	foreach ($gateways_arr as $gwname => $gwsttng) {
697
		if (($gwsttng['ipprotocol'] == $ipprotocol) && isset($gwsttng['defaultgw'])) {
698
			$dfltgwfound = true;
699
			$dfltgwname = $gwname;
700
			if (!isset($gwsttng['monitor_disable']) && stristr($gateways_status[$gwname]['status'], "down"))
701
				$dfltgwdown = true;
702
		}
703
		/* Keep a record of the last up gateway */
704
		/* XXX: Blacklist lan for now since it might cause issues to those who have a gateway set for it */
705
		if (empty($upgw) && ($gwsttng['ipprotocol'] == $ipprotocol) && (isset($gwsttng['monitor_disable']) || !stristr($gateways_status[$gwname]['status'], "down")) && $gwsttng[$gwname]['friendlyiface'] != "lan")
706
			$upgw = $gwname;
707
		if ($dfltgwdown == true && !empty($upgw))
708
			break;
709
	}
710
	if ($dfltgwfound == false) {
711
		$gwname = convert_friendly_interface_to_friendly_descr("wan");
712
		if (!empty($gateways_status[$gwname]) && stristr($gateways_status[$gwname]['status'], "down"))
713
			$dfltgwdown = true;
714
	}
715
	if ($dfltgwdown == true && !empty($upgw)) {
716
		if (preg_match("/dynamic/i", $gateways_arr[$upgw]['gateway']))
717
			$gateways_arr[$upgw]['gateway'] = get_interface_gateway($gateways_arr[$upgw]['friendlyiface']);
718
		if (is_ipaddr($gateways_arr[$upgw]['gateway'])) {
719
			log_error("Default gateway down setting {$upgw} as default!");
720
			if(is_ipaddrv6($gateways_arr[$upgw]['gateway'])) {
721
				$inetfamily = "-inet6";
722
			} else {
723
				$inetfamily = "-inet";
724
			}
725
			mwexec("/sbin/route change {$inetfamily} default {$gateways_arr[$upgw]['gateway']}");
726
		}
727
	} else {
728
		$defaultgw = trim(`/sbin/route -n get -{$ipprotocol} default | /usr/bin/grep gateway | /usr/bin/sed 's/gateway://g'`, " \n");
729
		if(is_ipaddrv6($gateways_arr[$dfltgwname]['gateway'])) {
730
			$inetfamily = "-inet6";
731
		} else {
732
			$inetfamily = "-inet";
733
		}
734
		if ($defaultgw != $gateways_arr[$dfltgwname]['gateway'])
735
			mwexec("/sbin/route change {$inetfamily} default {$gateways_arr[$dfltgwname]['gateway']}");
736
	}
737
}
738

    
739
/*
740
 * Return an array with all gateway groups with name as key
741
 * All gateway groups will be processed before returning the array.
742
 */
743
function return_gateway_groups_array() {
744
	global $config, $g;
745

    
746
	/* fetch the current gateways status */
747
	$gateways_status = return_gateways_status(true);
748
	$gateways_arr = return_gateways_array();
749
	$gateway_groups_array = array();
750

    
751
	if (isset($config['system']['gw_switch_default'])) {
752
		fixup_default_gateway("inet", $gateways_status, $gateways_arr);
753
		fixup_default_gateway("inet6", $gateways_status, $gateways_arr);
754
	}
755
	if (is_array($config['gateways']['gateway_group'])) {
756
		$carplist = get_configured_carp_interface_list();
757
		foreach ($config['gateways']['gateway_group'] as $group) {
758
			/* create array with group gateways members separated by tier */
759
			$tiers = array();
760
			$backupplan = array();
761
			$gwvip_arr = array();
762
			foreach ($group['item'] as $item) {
763
				list($gwname, $tier, $vipname) = explode("|", $item);
764

    
765
				if (is_ipaddr($carplist[$vipname])) {
766
					if (!is_array($gwvip_arr[$group['name']]))
767
						$gwvip_arr[$group['name']] = array();
768
					$gwvip_arr[$group['name']][$gwname] = $vipname;
769
				}
770

    
771
				/* Do it here rather than reiterating again the group in case no member is up. */
772
				if (!is_array($backupplan[$tier]))
773
					$backupplan[$tier] = array();
774
				$backupplan[$tier][] = $gwname;
775

    
776
				/* check if the gateway is available before adding it to the array */
777
				if (is_array($gateways_status[$gwname])) {
778
					$status = $gateways_status[$gwname];
779
					$gwdown = false;
780
					if (stristr($status['status'], "down")) {
781
						$msg = sprintf(gettext("MONITOR: %s is down, removing from routing group {$group['name']}"), $gwname);
782
						$gwdown = true;
783
					} else if (stristr($status['status'], "loss") && strstr($group['trigger'], "loss")) {
784
						/* packet loss */
785
						$msg = sprintf(gettext("MONITOR: %s has packet loss, removing from routing group {$group['name']}"), $gwname);
786
						$gwdown = true;
787
					} else if (stristr($status['status'], "delay") && strstr($group['trigger'] , "latency")) {
788
						/* high latency */
789
						$msg = sprintf(gettext("MONITOR: %s has high latency, removing from routing group {$group['name']}"), $gwname);
790
						$gwdown = true;
791
					}
792
					if ($gwdown == true) {
793
						log_error($msg);
794
						notify_via_growl($msg);
795
						notify_via_smtp($msg);
796
					} else {
797
						/* Online add member */
798
						if (!is_array($tiers[$tier]))
799
							$tiers[$tier] = array();
800
						$tiers[$tier][] = $gwname;
801
					}
802
				} else if (isset($gateways_arr[$gwname]['monitor_disable']))
803
					$tiers[$tier][] = $gwname;
804
			}
805
			$tiers_count = count($tiers);
806
			if ($tiers_count == 0) {
807
				/* Oh dear, we have no members! Engage Plan B */
808
				if (!$g['booting']) {
809
					$msg = gettext("Gateways status could not be determined, considering all as up/active. (Group: {$group['name']})");
810
					log_error($msg);
811
					notify_via_growl($msg);
812
					//notify_via_smtp($msg);
813
				}
814
				$tiers = $backupplan;
815
			}
816
			/* sort the tiers array by the tier key */
817
			ksort($tiers);
818

    
819
			/* we do not really foreach the tiers as we stop after the first tier */
820
			foreach ($tiers as $tieridx => $tier) {
821
				/* process all gateways in this tier */
822
				foreach ($tier as $member) {
823
					/* determine interface gateway */
824
					if (isset($gateways_arr[$member])) {
825
						$gateway = $gateways_arr[$member];
826
						$int = $gateway['interface'];
827
						$gatewayip = "";
828
						if(is_ipaddr($gateway['gateway']))
829
							$gatewayip = $gateway['gateway'];
830
						else if (!empty($int))
831
							$gatewayip = get_interface_gateway($gateway['friendlyiface']);
832

    
833
						if (!empty($int) && is_ipaddr($gatewayip)) {
834
							$groupmember = array();
835
							$groupmember['int']  = $int;
836
							$groupmember['gwip']  = $gatewayip;
837
							$groupmember['weight']  = isset($gateway['weight']) ? $gateway['weight'] : 1;
838
							if (is_array($gwvip_arr[$group['name']])&& !empty($gwvip_arr[$group['name']][$member]))
839
								$groupmember['vip'] = $gwvip_arr[$group['name']][$member];
840
							$gateway_groups_array[$group['name']]['ipprotocol'] = $gateway['ipprotocol'];
841
							$gateway_groups_array[$group['name']][] = $groupmember;
842
						}
843
					}
844
				}
845
				/* we should have the 1st available tier now, exit stage left */
846
				if (count($gateway_groups_array[$group['name']]) > 0)
847
					break;
848
				else
849
					log_error("GATEWAYS: Group {$group['name']} did not have any gateways up on tier {$tieridx}!");
850
			}
851
		}
852
	}
853

    
854
	return ($gateway_groups_array);
855
}
856

    
857
/* Update DHCP WAN Interface ip address in gateway group item */
858
function dhclient_update_gateway_groups_defaultroute($interface = "wan") {
859
	global $config, $g;
860
	foreach($config['gateways']['gateway_item'] as & $gw) {
861
		if($gw['interface'] == $interface) {
862
			$current_gw = get_interface_gateway($interface);
863
			if($gw['gateway'] <> $current_gw) {
864
				$gw['gateway'] = $current_gw;
865
				$changed = true;
866
			}
867
		}
868
	}
869
	if($changed && $current_gw)
870
		write_config(sprintf(gettext('Updating gateway group gateway for %1$s - new gateway is %2$s'), $interfac, $current_gw));
871
}
872

    
873
function lookup_gateway_ip_by_name($name) {
874

    
875
	$gateways_arr = return_gateways_array(false, true);
876
	foreach ($gateways_arr as $gname => $gw) {
877
		if ($gw['name'] === $name || $gname === $name)
878
			return $gw['gateway'];
879
	}
880

    
881
	return false;
882
}
883

    
884
function lookup_gateway_monitor_ip_by_name($name) {
885

    
886
	$gateways_arr = return_gateways_array(false, true);
887
	if (!empty($gateways_arr[$name])) {
888
		$gateway = $gateways_arr[$name];
889
		if(!is_ipaddr($gateway['monitor']))
890
			return $gateway['gateway'];
891

    
892
		return $gateway['monitor'];
893
	}
894

    
895
	return (false);
896
}
897

    
898
function lookup_gateway_interface_by_name($name) {
899

    
900
	$gateways_arr = return_gateways_array(false, true);
901
	if (!empty($gateways_arr[$name])) {
902
		$interfacegw = $gateways_arr[$name]['friendlyiface'];
903
		return ($interfacegw);
904
	}
905

    
906
	return (false);
907
}
908

    
909
function get_interface_gateway($interface, &$dynamic = false) {
910
	global $config, $g;
911

    
912
	$gw = NULL;
913

    
914
	$gwcfg = $config['interfaces'][$interface];
915
	if (!empty($gwcfg['gateway']) && is_array($config['gateways']['gateway_item'])) {
916
		foreach($config['gateways']['gateway_item'] as $gateway) {
917
			if(($gateway['name'] == $gwcfg['gateway']) && (is_ipaddrv4($gateway['gateway']))) {
918
				$gw = $gateway['gateway'];
919
				break;
920
			}
921
		}
922
	}
923

    
924
	// for dynamic interfaces we handle them through the $interface_router file.
925
	if (!is_ipaddrv4($gw) && !is_ipaddrv4($gwcfg['ipaddr'])) {
926
		$realif = get_real_interface($interface);
927
		if (file_exists("{$g['tmp_path']}/{$realif}_router")) {
928
				$gw = trim(file_get_contents("{$g['tmp_path']}/{$realif}_router"), " \n");
929
			$dynamic = true;
930
		}
931
		if (file_exists("{$g['tmp_path']}/{$realif}_defaultgw"))
932
			$dynamic = "default";
933

    
934
	}
935

    
936
	/* return gateway */
937
	return ($gw);
938
}
939

    
940
function get_interface_gateway_v6($interface, &$dynamic = false) {
941
	global $config, $g;
942

    
943
	$gw = NULL;
944
	$gwcfg = $config['interfaces'][$interface];
945
	if (!empty($gwcfg['gatewayv6']) && is_array($config['gateways']['gateway_item'])) {
946
		foreach($config['gateways']['gateway_item'] as $gateway) {
947
			if(($gateway['name'] == $gwcfg['gatewayv6']) && (is_ipaddrv6($gateway['gateway']))) {
948
				$gw = $gateway['gateway'];
949
				break;
950
			}
951
		}
952
	}
953

    
954
	// for dynamic interfaces we handle them through the $interface_router file.
955
	if (!is_ipaddrv6($gw) && !is_ipaddrv6($gwcfg['ipaddrv6'])) {
956
			$realif = get_real_interface($interface);
957
			if (file_exists("{$g['tmp_path']}/{$realif}_routerv6")) {
958
				$gw = trim(file_get_contents("{$g['tmp_path']}/{$realif}_routerv6"), " \n");
959
				$dynamic = true;
960
			}
961
			if (file_exists("{$g['tmp_path']}/{$realif}_defaultgwv6"))
962
				$dynamic = "default";
963

    
964
	}
965
	/* return gateway */
966
	return ($gw);
967
}
968

    
969
/* Check a IP address against a gateway IP or name
970
 * to verify it's address family */
971
function validate_address_family($ipaddr, $gwname) {
972
	$v4ip = false;
973
	$v6ip = false;
974
	$v4gw = false;
975
	$v6gw = false;
976

    
977
	if(is_ipaddrv4($ipaddr))
978
		$v4ip = true;
979
	if(is_ipaddrv6($ipaddr))
980
		$v6ip = true;
981
	if(is_ipaddrv4($gwname))
982
		$v4gw = true;
983
	if(is_ipaddrv6($gwname))
984
		$v6gw = true;
985

    
986
	if($v4ip && $v4gw)
987
		return true;
988
	if($v6ip && $v6gw)
989
		return true;
990

    
991
	/* still no match, carry on, lookup gateways */
992
	if(is_ipaddrv4(lookup_gateway_ip_by_name($gwname)))
993
		$v4gw = true;
994
	if(is_ipaddrv6(lookup_gateway_ip_by_name($gwname)))
995
		$v6gw = true;
996

    
997
	$gw_array = return_gateways_array();
998
	if(is_array($gw_array[$gwname])) {
999
		switch($gw_array[$gwname]['ipprotocol']) {
1000
			case "inet":
1001
				$v4gw = true;
1002
				break;
1003
			case "inet6":
1004
				$v6gw = true;
1005
				break;
1006
		}
1007
	}
1008

    
1009
	if($v4ip && $v4gw)
1010
		return true;
1011
	if($v6ip && $v6gw)
1012
		return true;
1013

    
1014
	return false;
1015
}
1016

    
1017
/* check if a interface is part of a gateway group */
1018
function interface_gateway_group_member($interface) {
1019
	global $config;
1020

    
1021
	if (is_array($config['gateways']['gateway_group']))
1022
		$groups = $config['gateways']['gateway_group'];
1023
	else
1024
		return false;
1025

    
1026
	$gateways_arr = return_gateways_array(false, true);
1027
	foreach($groups as $group) {
1028
		if(is_array($group['item'])) {
1029
			foreach($group['item'] as $item) {
1030
				$elements = explode("|", $item);
1031
				$gwname = $elements[0];
1032
				if ($interface == $gateways_arr[$gwname]['interface']) {
1033
					unset($gateways_arr);
1034
					return true;
1035
				}
1036
			}
1037
		}
1038
	}
1039
	unset($gateways_arr);
1040

    
1041
	return false;
1042
}
1043

    
1044
function gateway_is_gwgroup_member($name) {
1045
	global $config;
1046

    
1047
	if (is_array($config['gateways']['gateway_group']))
1048
		$groups = $config['gateways']['gateway_group'];
1049
	else
1050
		return false;
1051

    
1052
	$members = array();
1053
	foreach($groups as $group) {
1054
		if (is_array($group['item'])) {
1055
			foreach($group['item'] as $item) {
1056
				$elements = explode("|", $item);
1057
				$gwname = $elements[0];
1058
				if ($name == $elements[0])
1059
					$members[] = $group['name'];
1060
			}
1061
		}
1062
	}
1063

    
1064
	return $members;
1065
}
1066
?>
(24-24/67)