Project

General

Profile

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

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

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

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

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

    
29
	pfSense_MODULE:	routing
30

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

    
35
/* Returns an array of default values used for dpinger */
36
function return_dpinger_defaults() {
37
	return array(
38
		"latencylow" => "200",
39
		"latencyhigh" => "500",
40
		"losslow" => "10",
41
		"losshigh" => "20",
42
		"interval" => "250",
43
		"loss_interval" => "500",
44
		"time_period" => "25000",
45
		"alert_interval" => "1000");
46
}
47

    
48
function running_dpinger_processes() {
49
	global $g;
50

    
51
	$pidfiles = glob("{$g['varrun_path']}/dpinger_*.pid");
52

    
53
	$result = array();
54
	if ($pidfiles === FALSE) {
55
		return $result;
56
	}
57

    
58
	foreach ($pidfiles as $pidfile) {
59
		$result[] = preg_replace('/^dpinger_(\w+)\.pid$/', "$1",
60
		    basename($pidfile));
61
	}
62

    
63
	return $result;
64
}
65

    
66
/*
67
 * Stop one or more dpinger process
68
 * default parameter $gwname is '*' that will kill all running sessions
69
 * If a gateway name is passed, only this one will be killed
70
 */
71
function stop_dpinger($gwname = '*') {
72
	global $g;
73

    
74
	$pidfiles = glob("{$g['varrun_path']}/dpinger_{$gwname}.pid");
75

    
76
	if ($pidfiles === FALSE) {
77
		return;
78
	}
79

    
80
	foreach ($pidfiles as $pidfile) {
81
		if (isvalidpid($pidfile)) {
82
			killbypid($pidfile);
83
		} else {
84
			@unlink($pidfile);
85
		}
86
	}
87
}
88

    
89
function start_dpinger($gateway) {
90
	global $g;
91

    
92
	$dpinger_defaults = return_dpinger_defaults();
93

    
94
	$pidfile = "{$g['varrun_path']}/dpinger_{$gateway['name']}.pid";
95
	$socket = "{$g['varrun_path']}/dpinger_{$gateway['name']}.sock";
96
	$alarm_cmd = "{$g['etc_path']}/rc.gateway_alarm {$gateway['name']}";
97

    
98
	$params  = "-S "; /* Log warnings via syslog */
99
	$params .= "-B {$gateway['gwifip']} "; /* Bind src address */
100
	$params .= "-p {$pidfile} "; /* PID filename */
101
	$params .= "-U {$socket} "; /* Status Socket */
102
	$params .= "-C \"{$alarm_cmd}\" "; /* Command to run on alarm */
103

    
104
	$params .= "-s " .
105
	    (isset($gateway['interval']) && is_numeric($gateway['interval'])
106
	    ? $gateway['interval']
107
	    : $dpinger_defaults['interval']
108
	    ) . " ";
109

    
110
	$params .= "-l " .
111
	    (isset($gateway['loss_interval']) && is_numeric($gateway['loss_interval'])
112
	    ?  $gateway['loss_interval']
113
	    : $dpinger_defaults['loss_interval']
114
	    ) . " ";
115

    
116
	$params .= "-t " .
117
	    (isset($gateway['time_period']) && is_numeric($gateway['time_period'])
118
	    ?  $gateway['time_period']
119
	    : $dpinger_defaults['time_period']
120
	    ) . " ";
121

    
122
	$params .= "-A " .
123
	    (isset($gateway['alert_interval']) && is_numeric($gateway['alert_interval'])
124
	    ?  $gateway['alert_interval']
125
	    : $dpinger_defaults['alert_interval']
126
	    ) . " ";
127

    
128
	$params .= "-D " .
129
	    (isset($gateway['latencyhigh']) && is_numeric($gateway['latencyhigh'])
130
	    ?  $gateway['latencyhigh']
131
	    : $dpinger_defaults['latencyhigh']
132
	    ) . " ";
133

    
134
	$params .= "-L " .
135
	    (isset($gateway['losshigh']) && is_numeric($gateway['losshigh'])
136
	    ?  $gateway['losshigh']
137
	    : $dpinger_defaults['losshigh']
138
	    ) . " ";
139

    
140
	mwexec_bg("/usr/local/bin/dpinger {$params} {$gateway['monitor']}");
141
}
142

    
143
/*
144
 * Creates monitoring configuration file and
145
 * adds appropriate static routes.
146
 */
147
function setup_gateways_monitor() {
148
	global $config, $g;
149

    
150
	$gateways_arr = return_gateways_array();
151
	if (!is_array($gateways_arr)) {
152
		log_error("No gateways to monitor. dpinger will not run.");
153
		stop_dpinger();
154
		return;
155
	}
156

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

    
171
		/* if the monitor address is already used before, skip */
172
		if (in_array($gateway['monitor'], $monitor_ips)) {
173
			continue;
174
		}
175

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

    
189
			if ($gwifip == "0.0.0.0") {
190
				continue; //Skip this target - the gateway is still waiting for DHCP
191
			}
192

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

    
209
				pfSense_kill_states("0.0.0.0/0", $gateway['monitor'], $gateway['interface'], "icmp");
210
			}
211
		} else if ($gateway['ipprotocol'] == "inet6") { // This is an IPv6 gateway...
212
			if ($gateway['monitor'] == $gateway['gateway']) {
213
				/* link locals really need a different src ip */
214
				if (is_linklocal($gateway['gateway'])) {
215
					if (!strpos($gateway['gateway'], '%')) {
216
						$gateway['gateway'] .= '%' . $gateway['interface'];
217
					}
218
					$gwifip = find_interface_ipv6_ll($gateway['interface'], true);
219
				} else {
220
					$gwifip = find_interface_ipv6($gateway['interface'], true);
221
				}
222
			} else {
223
				/* 'monitor' has been set, so makes sure it has precedence over
224
				 * 'gateway' in defining the source IP. Otherwise if 'gateway'
225
				 * is a local link and 'monitor' is global routable then the
226
				 * ICMP6 response would not find its way back home...
227
				 */
228
				$gwifip = find_interface_ipv6($gateway['interface'], true);
229
			}
230

    
231
			/* Make sure srcip and target have scope defined when they are ll */
232
			if (is_linklocal($gwifip) && !strpos($gwifip, '%')) {
233
				$gwifip .= '%' . $gateway['interface'];
234
			}
235
			if (is_linklocal($gateway['monitor']) && !strpos($gateway['monitor'], '%')) {
236
				$gateway['monitor'] .= "%{$gateway['interface']}";
237
			}
238

    
239
			if (!is_ipaddrv6($gwifip)) {
240
				continue; //Skip this target
241
			}
242

    
243
			/*
244
			 * If the gateway is the same as the monitor we do not add a
245
			 * route as this will break the routing table.
246
			 * Add static routes for each gateway with their monitor IP
247
			 * not strictly necessary but is a added level of protection.
248
			 */
249
			if ($gateway['gateway'] != $gateway['monitor']) {
250
				log_error("Removing static route for monitor {$gateway['monitor']} and adding a new route through {$gateway['gateway']}");
251
				if (interface_isppp_type($gateway['friendlyiface'])) {
252
					mwexec("/sbin/route change -host -inet6 " . escapeshellarg($gateway['monitor']) .
253
						" -iface " . escapeshellarg($gateway['interface']), true);
254
				} else {
255
					mwexec("/sbin/route change -host -inet6 " . escapeshellarg($gateway['monitor']) .
256
						" " . escapeshellarg($gateway['gateway']), true);
257
				}
258

    
259
				pfSense_kill_states("::0.0.0.0/0", $gateway['monitor'], $gateway['interface'], "icmpv6");
260
			}
261
		} else {
262
			continue;
263
		}
264

    
265
		$monitor_ips[] = $gateway['monitor'];
266
		$gateways_arr[$gwname]['enable_dpinger'] = true;
267
		$gateways_arr[$gwname]['gwifip'] = $gwifip;
268
	}
269

    
270
	stop_dpinger();
271

    
272
	/* Start new processes */
273
	foreach ($gateways_arr as $gateway) {
274
		if (isset($gateway['enable_dpinger'])) {
275
			start_dpinger($gateway);
276
		}
277
	}
278

    
279
	return 0;
280
}
281

    
282
function get_dpinger_status($gwname) {
283
	global $g;
284

    
285
	$socket = "{$g['varrun_path']}/dpinger_{$gwname}.sock";
286

    
287
	if (!file_exists($socket)) {
288
		log_error("dpinger: status socket {$socket} not found");
289
		return false;
290
	}
291

    
292
	$fp = stream_socket_client("unix://{$socket}", $errno, $errstr, 10);
293
	if (!$fp) {
294
		log_error("dpinger: cannot connect to status socket {$socket} - $errstr ($errno)");
295
		return false;
296
	}
297

    
298
	$status = '';
299
	while (!feof($fp)) {
300
		$status .= fgets($fp, 1024);
301
	}
302
	fclose($fp);
303

    
304
	$r = array();
305
	list(
306
	    $r['latency_avg'],
307
	    $r['latency_stddev'],
308
	    $r['loss'],
309
	    $r['alarm_on'],
310
	    $r['srcip'],
311
	    $r['targetip']
312
	) = explode(' ', preg_replace('/\n/', '', $status));
313

    
314
	$gateways_arr = return_gateways_array();
315
	unset($gw);
316
	if (isset($gateways_arr[$gwname])) {
317
		$gw = $gateways_arr[$gwname];
318
	}
319

    
320
	$r['latency_avg'] = round($r['latency_avg']/1000, 3);
321
	$r['latency_stddev'] = round($r['latency_stddev']/1000, 3);
322

    
323
	$r['status'] = "none";
324
	if (isset($gw) && isset($gw['force_down'])) {
325
		$r['status'] = "force_down";
326
	} else if ($r['alarm_on'] == 1) {
327
		$r['status'] = "down";
328
	} else if (isset($gw)) {
329
		$dpinger_defaults = return_dpinger_defaults();
330
		if (isset($gw['latencylow']) &&
331
		    is_numeric($gw['latencylow'])) {
332
			$latencylow = $gw['latencylow'];
333
		} else {
334
			$latencylow = $dpinger_defaults['latencylow'];
335
		}
336

    
337
		if ($r['latency_avg'] > $latencylow) {
338
			$r['status'] = "delay";
339
		} else {
340
			if (isset($gw['losslow']) &&
341
			    is_numeric($gw['losslow'])) {
342
				$losslow = $gw['losslow'];
343
			} else {
344
				$losslow = $dpinger_defaults['losslow'];
345
			}
346

    
347
			if ($r['loss'] > $losslow) {
348
				$r['status'] = "loss";
349
			}
350
		}
351
	}
352

    
353
	return $r;
354
}
355

    
356
/* return the status of the dpinger targets as an array */
357
function return_gateways_status($byname = false) {
358
	global $config, $g;
359

    
360
	$dpinger_gws = running_dpinger_processes();
361
	$status = array();
362

    
363
	$gateways_arr = return_gateways_array();
364

    
365
	foreach ($dpinger_gws as $gwname) {
366
		$dpinger_status = get_dpinger_status($gwname);
367
		if ($dpinger_status === false) {
368
			continue;
369
		}
370

    
371
		if ($byname == false) {
372
			$target = $dpinger_status['targetip'];
373
		} else {
374
			$target = $gwname;
375
		}
376

    
377
		$status[$target] = array();
378
		$status[$target]['monitorip'] = $dpinger_status['targetip'];
379
		$status[$target]['srcip'] = $dpinger_status['srcip'];
380
		$status[$target]['name'] = $gwname;
381
		$status[$target]['delay'] = empty($dpinger_status['latency_avg']) ? "0ms" : $dpinger_status['latency_avg'] . "ms";
382
		$status[$target]['loss'] = empty($dpinger_status['loss']) ? "0.0%" : round($dpinger_status['loss'], 1) . "%";
383
		$status[$target]['status'] = $dpinger_status['status'];
384
	}
385

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

    
410
		/* failsafe for down interfaces */
411
		if ($target == "none") {
412
			$target = $gwitem['name'];
413
			$status[$target]['name'] = $gwitem['name'];
414
			$status[$target]['delay'] = "0.0ms";
415
			$status[$target]['loss'] = "100.0%";
416
			$status[$target]['status'] = "down";
417
		} else {
418
			$status[$target]['monitorip'] = $tgtip;
419
			$status[$target]['srcip'] = $srcip;
420
			$status[$target]['name'] = $gwitem['name'];
421
			$status[$target]['delay'] = "0.0ms";
422
			$status[$target]['loss'] = "0.0%";
423
			$status[$target]['status'] = "none";
424
		}
425
	}
426
	return($status);
427
}
428

    
429
/* Return all configured gateways on the system */
430
function return_gateways_array($disabled = false, $localhost = false, $inactive = false) {
431
	global $config, $g;
432

    
433
	$gateways_arr = array();
434
	$gateways_arr_temp = array();
435

    
436
	$found_defaultv4 = 0;
437
	$found_defaultv6 = 0;
438

    
439
	// Ensure the interface cache is up to date first
440
	$interfaces = get_interface_arr(true);
441
	$interfaces_v4 = array();
442
	$interfaces_v6 = array();
443

    
444
	$i = -1;
445
	/* Process/add all the configured gateways. */
446
	if (is_array($config['gateways']['gateway_item'])) {
447
		foreach ($config['gateways']['gateway_item'] as $gateway) {
448
			/* Increment it here to do not skip items */
449
			$i++;
450

    
451
			if (empty($config['interfaces'][$gateway['interface']])) {
452
				if ($inactive === false) {
453
					continue;
454
				} else {
455
					$gateway['inactive'] = true;
456
				}
457
			}
458
			$wancfg = $config['interfaces'][$gateway['interface']];
459

    
460
			/* skip disabled interfaces */
461
			if ($disabled === false && (!isset($wancfg['enable']))) {
462
				continue;
463
			}
464

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

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

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

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

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

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

    
527
			/* Remember all the gateway names, even ones to be skipped because they are disabled. */
528
			/* Then we can easily know and match them later when attempting to add dynamic gateways to the list. */
529
			$gateways_arr_temp[$gateway['name']] = $gateway;
530

    
531
			/* skip disabled gateways if the caller has not asked for them to be returned. */
532
			if (!($disabled === false && isset($gateway['disabled']))) {
533
				$gateways_arr[$gateway['name']] = $gateway;
534
			}
535
		}
536
	}
537
	unset($gateway);
538

    
539
	/* Loop through all interfaces with a gateway and add it to a array */
540
	if ($disabled == false) {
541
		$iflist = get_configured_interface_with_descr();
542
	} else {
543
		$iflist = get_configured_interface_with_descr(false, true);
544
	}
545

    
546
	/* Process/add dynamic v4 gateways. */
547
	foreach ($iflist as $ifname => $friendly) {
548
		if (!interface_has_gateway($ifname)) {
549
			continue;
550
		}
551

    
552
		if (empty($config['interfaces'][$ifname])) {
553
			continue;
554
		}
555

    
556
		$ifcfg = &$config['interfaces'][$ifname];
557
		if (!isset($ifcfg['enable'])) {
558
			continue;
559
		}
560

    
561
		if (!empty($ifcfg['ipaddr']) && is_ipaddrv4($ifcfg['ipaddr'])) {
562
			continue;
563
		}
564

    
565
		if (isset($interfaces_v4[$ifname])) {
566
			continue;
567
		}
568

    
569
		$ctype = "";
570
		switch ($ifcfg['ipaddr']) {
571
			case "dhcp":
572
			case "pppoe":
573
			case "pptp":
574
			case "ppp":
575
				$ctype = strtoupper($ifcfg['ipaddr']);
576
				break;
577
			default:
578
				$tunnelif = substr($ifcfg['if'], 0, 3);
579
				if (substr($ifcfg['if'], 0, 4) == "ovpn") {
580
					// if current iface is an ovpn server endpoint then check its type, skip tap only
581
					if (substr($ifcfg['if'], 4, 1) == 's') {
582
						$ovpnid = substr($ifcfg['if'], 5);
583
						if (is_array($config['openvpn']['openvpn-server'])) {
584
							foreach ($config['openvpn']['openvpn-server'] as & $ovpnserverconf) {
585
								if ($ovpnserverconf['vpnid'] == $ovpnid) {
586
									if ($ovpnserverconf['dev_mode'] == "tap") {
587
										continue 3;
588
									}
589
								}
590
							}
591
						}
592
					}
593
					$ctype = "VPNv4";
594
				} else if ($tunnelif == "gif" || $tunnelif == "gre") {
595
					$ctype = "TUNNELv4";
596
				}
597
				break;
598
		}
599
		$ctype = "_". strtoupper($ctype);
600

    
601
		$gateway = array();
602
		$gateway['dynamic'] = false;
603
		$gateway['ipprotocol'] = "inet";
604
		$gateway['gateway'] = get_interface_gateway($ifname, $gateway['dynamic']);
605
		$gateway['interface'] = get_real_interface($ifname);
606
		$gateway['friendlyiface'] = $ifname;
607
		$gateway['name'] = "{$friendly}{$ctype}";
608
		$gateway['attribute'] = "system";
609

    
610
		if (($gateway['dynamic'] === "default") && ($found_defaultv4 == 0)) {
611
			$gateway['defaultgw'] = true;
612
			$gateway['dynamic'] = true;
613
			$found_defaultv4 = 1;
614
		}
615
		/* Loopback dummy for dynamic interfaces without a IP */
616
		if (!is_ipaddrv4($gateway['gateway']) && $gateway['dynamic'] == true) {
617
			$gateway['gateway'] = "dynamic";
618
		}
619

    
620
		/* automatically skip known static and dynamic gateways that were previously processed */
621
		foreach ($gateways_arr_temp as $gateway_item) {
622
			if ((($ifname == $gateway_item['friendlyiface'] && $friendly == $gateway_item['name'])&& ($gateway['ipprotocol'] == $gateway_item['ipprotocol'])) ||
623
			    (($ifname == $gateway_item['friendlyiface'] && $gateway_item['dynamic'] == true) && ($gateway['ipprotocol'] == $gateway_item['ipprotocol']))) {
624
				continue 2;
625
			}
626
		}
627

    
628
		if (is_ipaddrv4($gateway['gateway'])) {
629
			$gateway['monitor'] = $gateway['gateway'];
630
		}
631

    
632
		$gateway['descr'] = "Interface {$friendly}{$ctype} Gateway";
633
		$gateways_arr[$gateway['name']] = $gateway;
634
	}
635
	unset($gateway);
636

    
637
	/* Process/add dynamic v6 gateways. */
638
	foreach ($iflist as $ifname => $friendly) {
639
		/* If the user has disabled IPv6, they probably don't want any IPv6 gateways. */
640
		if (!isset($config['system']['ipv6allow'])) {
641
			break;
642
		}
643

    
644
		if (!interface_has_gatewayv6($ifname)) {
645
			continue;
646
		}
647

    
648
		if (empty($config['interfaces'][$ifname])) {
649
			continue;
650
		}
651

    
652
		$ifcfg = &$config['interfaces'][$ifname];
653
		if (!isset($ifcfg['enable'])) {
654
			continue;
655
		}
656

    
657
		if (!empty($ifcfg['ipaddrv6']) && is_ipaddrv6($ifcfg['ipaddrv6'])) {
658
			continue;
659
		}
660

    
661
		if (isset($interfaces_v6[$ifname])) {
662
			continue;
663
		}
664

    
665
		$ctype = "";
666
		switch ($ifcfg['ipaddrv6']) {
667
			case "slaac":
668
			case "dhcp6":
669
			case "6to4":
670
			case "6rd":
671
				$ctype = strtoupper($ifcfg['ipaddrv6']);
672
				break;
673
			default:
674
				$tunnelif = substr($ifcfg['if'], 0, 3);
675
				if (substr($ifcfg['if'], 0, 4) == "ovpn") {
676
					// if current iface is an ovpn server endpoint then check its type, skip tap only
677
					if (substr($ifcfg['if'], 4, 1) == 's') {
678
						$ovpnid = substr($ifcfg['if'], 5);
679
						if (is_array($config['openvpn']['openvpn-server'])) {
680
							foreach ($config['openvpn']['openvpn-server'] as & $ovpnserverconf) {
681
								if ($ovpnserverconf['vpnid'] == $ovpnid) {
682
									if ($ovpnserverconf['dev_mode'] == "tap") {
683
										continue 3;
684
									}
685
								}
686
							}
687
						}
688
					}
689
					$ctype = "VPNv6";
690
				} else if ($tunnelif == "gif" || $tunnelif == "gre") {
691
					$ctype = "TUNNELv6";
692
				}
693
				break;
694
		}
695
		$ctype = "_". strtoupper($ctype);
696

    
697
		$gateway = array();
698
		$gateway['dynamic'] = false;
699
		$gateway['ipprotocol'] = "inet6";
700
		$gateway['gateway'] = get_interface_gateway_v6($ifname, $gateway['dynamic']);
701
		$gateway['interface'] = get_real_interface($ifname, "inet6");
702
		switch ($ifcfg['ipaddrv6']) {
703
			case "6rd":
704
			case "6to4":
705
				$gateway['dynamic'] = "default";
706
				break;
707
		}
708
		$gateway['friendlyiface'] = $ifname;
709
		$gateway['name'] = "{$friendly}{$ctype}";
710
		$gateway['attribute'] = "system";
711

    
712
		if (($gateway['dynamic'] === "default") && ($found_defaultv6 == 0)) {
713
			$gateway['defaultgw'] = true;
714
			$gateway['dynamic'] = true;
715
			$found_defaultv6 = 1;
716
		}
717

    
718
		/* Loopback dummy for dynamic interfaces without a IP */
719
		if (!is_ipaddrv6($gateway['gateway']) && $gateway['dynamic'] == true) {
720
			$gateway['gateway'] = "dynamic";
721
		}
722

    
723
		/* automatically skip known static and dynamic gateways that were previously processed */
724
		foreach ($gateways_arr_temp as $gateway_item) {
725
			if ((($ifname == $gateway_item['friendlyiface'] && $friendly == $gateway_item['name']) && ($gateway['ipprotocol'] == $gateway_item['ipprotocol'])) ||
726
			    (($ifname == $gateway_item['friendlyiface'] && $gateway_item['dynamic'] == true) && ($gateway['ipprotocol'] == $gateway_item['ipprotocol']))) {
727
				continue 2;
728
			}
729
		}
730

    
731
		if (is_ipaddrv6($gateway['gateway'])) {
732
			$gateway['monitor'] = $gateway['gateway'];
733
		}
734

    
735
		$gateway['descr'] = "Interface {$friendly}{$ctype} Gateway";
736
		$gateways_arr[$gateway['name']] = $gateway;
737
	}
738
	unset($gateway);
739

    
740
	/* FIXME: Should this be enabled.
741
	 * Some interface like wan might be default but have no info recorded
742
	 * the config. */
743
	/* this is a fallback if all else fails and we want to get packets out @smos */
744
	if ($found_defaultv4 == 0 || $found_defaultv6 == 0) {
745
		foreach ($gateways_arr as &$gateway) {
746
			if (($gateway['friendlyiface'] == "wan") && ($found_defaultv4 == 0) && (!isset($gateway['ipprotocol']) || ($gateway['ipprotocol'] == "inet"))) {
747
				if (file_exists("{$g['tmp_path']}/{$gateway['interface']}_defaultgw")) {
748
					$gateway['defaultgw'] = true;
749
					$found_defaultv4 = 1;
750
				}
751
			}
752
			else if (($gateway['friendlyiface'] == "wan") && ($found_defaultv6 == 0) && ($gateway['ipprotocol'] == "inet6")) {
753
				if (file_exists("{$g['tmp_path']}/{$gateway['interface']}_defaultgwv6")) {
754
					$gateway['defaultgw'] = true;
755
					$found_defaultv6 = 1;
756
				}
757
			}
758
		}
759
	}
760

    
761
	if ($localhost === true) {
762
		/* attach localhost for Null routes */
763
		$gwlo4 = array();
764
		$gwlo4['name'] = "Null4";
765
		$gwlo4['interface'] = "lo0";
766
		$gwlo4['ipprotocol'] = "inet";
767
		$gwlo4['gateway'] = "127.0.0.1";
768
		$gwlo6 = array();
769
		$gwlo6['name'] = "Null6";
770
		$gwlo6['interface'] = "lo0";
771
		$gwlo6['ipprotocol'] = "inet6";
772
		$gwlo6['gateway'] = "::1";
773
		$gateways_arr['Null4'] = $gwlo4;
774
		$gateways_arr['Null6'] = $gwlo6;
775
	}
776
	return($gateways_arr);
777
}
778

    
779
function fixup_default_gateway($ipprotocol, $gateways_status, $gateways_arr) {
780
	global $config, $g;
781
	/*
782
	 * NOTE: The code below is meant to replace the default gateway when it goes down.
783
	 *	This facilitates services running on pfSense itself and are not handled by a PBR to continue working.
784
	 */
785
	$upgw = '';
786
	$dfltgwname = '';
787
	$dfltgwdown = false;
788
	$dfltgwfound = false;
789
	foreach ($gateways_arr as $gwname => $gwsttng) {
790
		if (($gwsttng['ipprotocol'] == $ipprotocol) && isset($gwsttng['defaultgw'])) {
791
			$dfltgwfound = true;
792
			$dfltgwname = $gwname;
793
			if (!isset($gwsttng['monitor_disable']) && $gateways_status[$gwname]['status'] != "none") {
794
				$dfltgwdown = true;
795
			}
796
		}
797
		/* Keep a record of the last up gateway */
798
		/* XXX: Blacklist lan for now since it might cause issues to those who have a gateway set for it */
799
		if (empty($upgw) && ($gwsttng['ipprotocol'] == $ipprotocol) && (isset($gwsttng['monitor_disable']) || $gateways_status[$gwname]['status'] == "none") && $gwsttng[$gwname]['friendlyiface'] != "lan") {
800
			$upgw = $gwname;
801
		}
802
		if ($dfltgwdown == true && !empty($upgw)) {
803
			break;
804
		}
805
	}
806
	if ($dfltgwfound == false) {
807
		$gwname = convert_friendly_interface_to_friendly_descr("wan");
808
		if (!empty($gateways_status[$gwname]) && stristr($gateways_status[$gwname]['status'], "down")) {
809
			$dfltgwdown = true;
810
		}
811
	}
812
	if ($dfltgwdown == true && !empty($upgw)) {
813
		if ($gateways_arr[$upgw]['gateway'] == "dynamic") {
814
			$gateways_arr[$upgw]['gateway'] = get_interface_gateway($gateways_arr[$upgw]['friendlyiface']);
815
		}
816
		if (is_ipaddr($gateways_arr[$upgw]['gateway'])) {
817
			log_error("Default gateway down setting {$upgw} as default!");
818
			if (is_ipaddrv6($gateways_arr[$upgw]['gateway'])) {
819
				$inetfamily = "-inet6";
820
			} else {
821
				$inetfamily = "-inet";
822
			}
823
			mwexec("/sbin/route change {$inetfamily} default {$gateways_arr[$upgw]['gateway']}");
824
		}
825
	} else if (!empty($dfltgwname)) {
826
		$defaultgw = trim(exec("/sbin/route -n get -{$ipprotocol} default | /usr/bin/awk '/gateway:/ {print $2}'"), " \n");
827
		if ($ipprotocol == 'inet6' && !is_ipaddrv6($gateways_arr[$dfltgwname]['gateway'])) {
828
			return;
829
		}
830
		if ($ipprotocol == 'inet' && !is_ipaddrv4($gateways_arr[$dfltgwname]['gateway'])) {
831
			return;
832
		}
833
		if ($defaultgw != $gateways_arr[$dfltgwname]['gateway']) {
834
			mwexec("/sbin/route change -{$ipprotocol} default {$gateways_arr[$dfltgwname]['gateway']}");
835
		}
836
	}
837
}
838

    
839
/*
840
 * Return an array with all gateway groups with name as key
841
 * All gateway groups will be processed before returning the array.
842
 */
843
function return_gateway_groups_array() {
844
	global $config, $g;
845

    
846
	/* fetch the current gateways status */
847
	$gateways_status = return_gateways_status(true);
848
	$gateways_arr = return_gateways_array();
849
	$gateway_groups_array = array();
850

    
851
	if (isset($config['system']['gw_switch_default'])) {
852
		fixup_default_gateway("inet", $gateways_status, $gateways_arr);
853
		fixup_default_gateway("inet6", $gateways_status, $gateways_arr);
854
	}
855
	if (is_array($config['gateways']['gateway_group'])) {
856
		$carplist = get_configured_carp_interface_list();
857
		foreach ($config['gateways']['gateway_group'] as $group) {
858
			/* create array with group gateways members separated by tier */
859
			$tiers = array();
860
			$backupplan = array();
861
			$gwvip_arr = array();
862
			foreach ($group['item'] as $item) {
863
				list($gwname, $tier, $vipname) = explode("|", $item);
864

    
865
				if (is_ipaddr($carplist[$vipname])) {
866
					if (!is_array($gwvip_arr[$group['name']])) {
867
						$gwvip_arr[$group['name']] = array();
868
					}
869
					$gwvip_arr[$group['name']][$gwname] = $vipname;
870
				}
871

    
872
				/* Do it here rather than reiterating again the group in case no member is up. */
873
				if (!is_array($backupplan[$tier])) {
874
					$backupplan[$tier] = array();
875
				}
876
				$backupplan[$tier][] = $gwname;
877

    
878
				/* check if the gateway is available before adding it to the array */
879
				if (is_array($gateways_status[$gwname])) {
880
					$status = $gateways_status[$gwname];
881
					$gwdown = false;
882
					if (stristr($status['status'], "down")) {
883
						$msg = sprintf(gettext("MONITOR: %s is down, omitting from routing group {$group['name']}"), $gwname);
884
						$gwdown = true;
885
					} else if (stristr($status['status'], "loss") && strstr($group['trigger'], "loss")) {
886
						/* packet loss */
887
						$msg = sprintf(gettext("MONITOR: %s has packet loss, omitting from routing group {$group['name']}"), $gwname);
888
						$gwdown = true;
889
					} else if (stristr($status['status'], "delay") && strstr($group['trigger'] , "latency")) {
890
						/* high latency */
891
						$msg = sprintf(gettext("MONITOR: %s has high latency, omitting from routing group {$group['name']}"), $gwname);
892
						$gwdown = true;
893
					}
894
					if ($gwdown == true) {
895
						log_error($msg);
896
						notify_via_growl($msg);
897
						notify_via_smtp($msg);
898
					} else {
899
						/* Online add member */
900
						if (!is_array($tiers[$tier])) {
901
							$tiers[$tier] = array();
902
						}
903
						$tiers[$tier][] = $gwname;
904
					}
905
				} else if (isset($gateways_arr[$gwname]['monitor_disable'])) {
906
					$tiers[$tier][] = $gwname;
907
				}
908
			}
909
			$tiers_count = count($tiers);
910
			if ($tiers_count == 0) {
911
				/* Oh dear, we have no members! Engage Plan B */
912
				if (!platform_booting()) {
913
					$msg = gettext("Gateways status could not be determined, considering all as up/active. (Group: {$group['name']})");
914
					log_error($msg);
915
					notify_via_growl($msg);
916
					//notify_via_smtp($msg);
917
				}
918
				$tiers = $backupplan;
919
			}
920
			/* sort the tiers array by the tier key */
921
			ksort($tiers);
922

    
923
			/* we do not really foreach the tiers as we stop after the first tier */
924
			foreach ($tiers as $tieridx => $tier) {
925
				/* process all gateways in this tier */
926
				foreach ($tier as $member) {
927
					/* determine interface gateway */
928
					if (isset($gateways_arr[$member])) {
929
						$gateway = $gateways_arr[$member];
930
						$int = $gateway['interface'];
931
						$gatewayip = "";
932
						if (is_ipaddr($gateway['gateway'])) {
933
							$gatewayip = $gateway['gateway'];
934
						} else if (!empty($int)) {
935
							$gatewayip = get_interface_gateway($gateway['friendlyiface']);
936
						}
937

    
938
						if (!empty($int)) {
939
							$gateway_groups_array[$group['name']]['ipprotocol'] = $gateway['ipprotocol'];
940
							if (is_ipaddr($gatewayip)) {
941
								$groupmember = array();
942
								$groupmember['int'] = $int;
943
								$groupmember['gwip'] = $gatewayip;
944
								$groupmember['weight'] = isset($gateway['weight']) ? $gateway['weight'] : 1;
945
								if (is_array($gwvip_arr[$group['name']])&& !empty($gwvip_arr[$group['name']][$member])) {
946
									$groupmember['vip'] = $gwvip_arr[$group['name']][$member];
947
								}
948
								$gateway_groups_array[$group['name']][] = $groupmember;
949
							}
950
						}
951
					}
952
				}
953
				/* we should have the 1st available tier now, exit stage left */
954
				if (count($gateway_groups_array[$group['name']]) > 0) {
955
					break;
956
				} else {
957
					log_error("GATEWAYS: Group {$group['name']} did not have any gateways up on tier {$tieridx}!");
958
				}
959
			}
960
		}
961
	}
962

    
963
	return ($gateway_groups_array);
964
}
965

    
966
/* Update DHCP WAN Interface ip address in gateway group item */
967
function dhclient_update_gateway_groups_defaultroute($interface = "wan") {
968
	global $config, $g;
969
	foreach ($config['gateways']['gateway_item'] as & $gw) {
970
		if ($gw['interface'] == $interface) {
971
			$current_gw = get_interface_gateway($interface);
972
			if ($gw['gateway'] <> $current_gw) {
973
				$gw['gateway'] = $current_gw;
974
				$changed = true;
975
			}
976
		}
977
	}
978
	if ($changed && $current_gw) {
979
		write_config(sprintf(gettext('Updating gateway group gateway for %1$s - new gateway is %2$s'), $interfac, $current_gw));
980
	}
981
}
982

    
983
function lookup_gateway_ip_by_name($name, $disabled = false) {
984

    
985
	$gateways_arr = return_gateways_array($disabled, true);
986
	foreach ($gateways_arr as $gname => $gw) {
987
		if ($gw['name'] === $name || $gname === $name) {
988
			return $gw['gateway'];
989
		}
990
	}
991

    
992
	return false;
993
}
994

    
995
function lookup_gateway_monitor_ip_by_name($name) {
996

    
997
	$gateways_arr = return_gateways_array(false, true);
998
	if (!empty($gateways_arr[$name])) {
999
		$gateway = $gateways_arr[$name];
1000
		if (!is_ipaddr($gateway['monitor'])) {
1001
			return $gateway['gateway'];
1002
		}
1003

    
1004
		return $gateway['monitor'];
1005
	}
1006

    
1007
	return (false);
1008
}
1009

    
1010
function lookup_gateway_interface_by_name($name) {
1011

    
1012
	$gateways_arr = return_gateways_array(false, true);
1013
	if (!empty($gateways_arr[$name])) {
1014
		$interfacegw = $gateways_arr[$name]['friendlyiface'];
1015
		return ($interfacegw);
1016
	}
1017

    
1018
	return (false);
1019
}
1020

    
1021
function get_interface_gateway($interface, &$dynamic = false) {
1022
	global $config, $g;
1023

    
1024
	if (substr($interface, 0, 4) == '_vip') {
1025
		$interface = get_configured_carp_interface_list($interface, 'inet', 'iface');
1026
	}
1027

    
1028
	$gw = NULL;
1029
	$gwcfg = $config['interfaces'][$interface];
1030
	if (!empty($gwcfg['gateway']) && is_array($config['gateways']['gateway_item'])) {
1031
		foreach ($config['gateways']['gateway_item'] as $gateway) {
1032
			if (($gateway['name'] == $gwcfg['gateway']) && (is_ipaddrv4($gateway['gateway']))) {
1033
				$gw = $gateway['gateway'];
1034
				break;
1035
			}
1036
		}
1037
	}
1038

    
1039
	// for dynamic interfaces we handle them through the $interface_router file.
1040
	if (($gw == NULL || !is_ipaddrv4($gw)) && !is_ipaddrv4($gwcfg['ipaddr'])) {
1041
		$realif = get_real_interface($interface);
1042
		if (file_exists("{$g['tmp_path']}/{$realif}_router")) {
1043
			$gw = trim(file_get_contents("{$g['tmp_path']}/{$realif}_router"), " \n");
1044
			$dynamic = true;
1045
		}
1046
		if (file_exists("{$g['tmp_path']}/{$realif}_defaultgw")) {
1047
			$dynamic = "default";
1048
		}
1049

    
1050
	}
1051

    
1052
	/* return gateway */
1053
	return ($gw);
1054
}
1055

    
1056
function get_interface_gateway_v6($interface, &$dynamic = false) {
1057
	global $config, $g;
1058

    
1059
	if (substr($interface, 0, 4) == '_vip') {
1060
		$interface = get_configured_carp_interface_list($interface, 'inet6', 'iface');
1061
	}
1062

    
1063
	$gw = NULL;
1064
	$gwcfg = $config['interfaces'][$interface];
1065
	if (!empty($gwcfg['gatewayv6']) && is_array($config['gateways']['gateway_item'])) {
1066
		foreach ($config['gateways']['gateway_item'] as $gateway) {
1067
			if (($gateway['name'] == $gwcfg['gatewayv6']) && (is_ipaddrv6($gateway['gateway']))) {
1068
				$gw = $gateway['gateway'];
1069
				break;
1070
			}
1071
		}
1072
	}
1073

    
1074
	// for dynamic interfaces we handle them through the $interface_router file.
1075
	if (($gw == NULL || !is_ipaddrv6($gw)) && !is_ipaddrv6($gwcfg['ipaddrv6'])) {
1076
		$realif = get_real_interface($interface);
1077
		if (file_exists("{$g['tmp_path']}/{$realif}_routerv6")) {
1078
			$gw = trim(file_get_contents("{$g['tmp_path']}/{$realif}_routerv6"), " \n");
1079
			$dynamic = true;
1080
		}
1081
		if (file_exists("{$g['tmp_path']}/{$realif}_defaultgwv6")) {
1082
			$dynamic = "default";
1083
		}
1084
	}
1085
	/* return gateway */
1086
	return ($gw);
1087
}
1088

    
1089
/* Check a IP address against a gateway IP or name
1090
 * to verify it's address family */
1091
function validate_address_family($ipaddr, $gwname, $disabled = false) {
1092
	$v4ip = false;
1093
	$v6ip = false;
1094
	$v4gw = false;
1095
	$v6gw = false;
1096

    
1097
	if (is_ipaddrv4($ipaddr)) {
1098
		$v4ip = true;
1099
	}
1100
	if (is_ipaddrv6($ipaddr)) {
1101
		$v6ip = true;
1102
	}
1103
	if (is_ipaddrv4($gwname)) {
1104
		$v4gw = true;
1105
	}
1106
	if (is_ipaddrv6($gwname)) {
1107
		$v6gw = true;
1108
	}
1109

    
1110
	if ($v4ip && $v4gw) {
1111
		return true;
1112
	}
1113
	if ($v6ip && $v6gw) {
1114
		return true;
1115
	}
1116

    
1117
	/* still no match, carry on, lookup gateways */
1118
	if (is_ipaddrv4(lookup_gateway_ip_by_name($gwname, $disabled))) {
1119
		$v4gw = true;
1120
	}
1121
	if (is_ipaddrv6(lookup_gateway_ip_by_name($gwname, $disabled))) {
1122
		$v6gw = true;
1123
	}
1124

    
1125
	$gw_array = return_gateways_array();
1126
	if (is_array($gw_array[$gwname])) {
1127
		switch ($gw_array[$gwname]['ipprotocol']) {
1128
			case "inet":
1129
				$v4gw = true;
1130
				break;
1131
			case "inet6":
1132
				$v6gw = true;
1133
				break;
1134
		}
1135
	}
1136

    
1137
	if ($v4ip && $v4gw) {
1138
		return true;
1139
	}
1140
	if ($v6ip && $v6gw) {
1141
		return true;
1142
	}
1143

    
1144
	return false;
1145
}
1146

    
1147
/* check if a interface is part of a gateway group */
1148
function interface_gateway_group_member($interface) {
1149
	global $config;
1150

    
1151
	if (is_array($config['gateways']['gateway_group'])) {
1152
		$groups = $config['gateways']['gateway_group'];
1153
	} else {
1154
		return false;
1155
	}
1156

    
1157
	$gateways_arr = return_gateways_array(false, true);
1158
	foreach ($groups as $group) {
1159
		if (is_array($group['item'])) {
1160
			foreach ($group['item'] as $item) {
1161
				$elements = explode("|", $item);
1162
				$gwname = $elements[0];
1163
				if ($interface == $gateways_arr[$gwname]['interface']) {
1164
					unset($gateways_arr);
1165
					return true;
1166
				}
1167
			}
1168
		}
1169
	}
1170
	unset($gateways_arr);
1171

    
1172
	return false;
1173
}
1174

    
1175
function gateway_is_gwgroup_member($name) {
1176
	global $config;
1177

    
1178
	if (is_array($config['gateways']['gateway_group'])) {
1179
		$groups = $config['gateways']['gateway_group'];
1180
	} else {
1181
		return false;
1182
	}
1183

    
1184
	$members = array();
1185
	foreach ($groups as $group) {
1186
		if (is_array($group['item'])) {
1187
			foreach ($group['item'] as $item) {
1188
				$elements = explode("|", $item);
1189
				$gwname = $elements[0];
1190
				if ($name == $elements[0]) {
1191
					$members[] = $group['name'];
1192
				}
1193
			}
1194
		}
1195
	}
1196

    
1197
	return $members;
1198
}
1199
?>
(24-24/65)