Project

General

Profile

Bug #3607 » gwlb.inc

Gilles Compienne, 04/17/2014 08:21 AM

 
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
		print "Testing gateway: " . $name . "\n";
148
		if (isset($gateway['monitor_disable']))
149
			continue;
150
		if (empty($gateway['monitor']) || !is_ipaddr($gateway['monitor'])) {
151
			if (is_ipaddr($gateway['gateway']))
152
				$gateway['monitor'] = $gateway['gateway'];
153
			else /* No chance to get an ip to monitor skip target. */
154
				{
155
				print "Skipping1: " . $name . "\n";
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 ($gateway['ipprotocol'] == "inet") {
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 ($gateway['ipprotocol'] == "inet6") {
181
			if ($gateway['monitor'] == $gateway['gateway']) {
182
				/* link locals really need a different src ip */
183
				if (is_linklocal($gateway['gateway'])) {
184
					$gwifip = find_interface_ipv6_ll($gateway['interface'], true);
185
				} else {
186
					$gwifip = find_interface_ipv6($gateway['interface'], true);
187
				}
188
			} else {
189
				$gwifip = find_interface_ipv6($gateway['interface'], true);
190
				if (is_linklocal($gateway['monitor'])) {
191
					if (!strstr($gateway['monitor'], '%')) {
192
						$gateway['monitor'] .= "%{$gateway['interface']}";
193
					}
194
				} else {
195
					// Monitor is a routable address, so use a routable address for the "src" part
196
					$gwifip = find_interface_ipv6($gateway['interface'], true);
197
				}
198
			}
199
			if (!is_ipaddrv6($gwifip))
200
				continue; //Skip this target
201

    
202
			/*
203
			 * If the gateway is the same as the monitor we do not add a
204
			 * route as this will break the routing table.
205
			 * Add static routes for each gateway with their monitor IP
206
			 * not strictly necessary but is a added level of protection.
207
			 */
208
			if (is_ipaddrv6($gateway['gateway']) && $gateway['monitor'] != $gateway['gateway']) {
209
				log_error("Removing static route for monitor {$gateway['monitor']} and adding a new route through {$gateway['gateway']}");
210
				mwexec("/sbin/route change -host -inet6 " . escapeshellarg($gateway['monitor']) .
211
					" " . escapeshellarg($gateway['gateway']), true);
212
			}
213
		} else { 
214
			continue;
215
		}
216

    
217
		$monitor_ips[] = $gateway['monitor'];
218
		$apingercfg = "target \"{$gateway['monitor']}\" {\n";
219
		$apingercfg .= "	description \"{$name}\"\n";
220
		$apingercfg .= "	srcip \"{$gwifip}\"\n";
221
		if (!empty($gateway['interval']) && intval($gateway['interval']) > 1)
222
			$apingercfg .= "	interval " . intval($gateway['interval']) . "s\n";
223
		$alarms = "";
224
		$alarmscfg = "";
225
		$override = false;
226
		if (!empty($gateway['losslow'])) {
227
			$alarmscfg .= "alarm loss \"{$name}loss\" {\n";
228
			$alarmscfg .= "\tpercent_low {$gateway['losslow']}\n";
229
			$alarmscfg .= "\tpercent_high {$gateway['losshigh']}\n";
230
			$alarmscfg .= "}\n";
231
			$alarms .= "\"{$name}loss\"";
232
			$override = true;
233
		} else {
234
			if ($override == true)
235
				$alarms .= ",";
236
			$alarms .= "\"loss\"";
237
			$override = true;
238
		}
239
		if (!empty($gateway['latencylow'])) {
240
			$alarmscfg .= "alarm delay \"{$name}delay\" {\n";
241
			$alarmscfg .= "\tdelay_low {$gateway['latencylow']}ms\n";
242
			$alarmscfg .= "\tdelay_high {$gateway['latencyhigh']}ms\n";
243
			$alarmscfg .= "}\n";
244
			if ($override == true)
245
				$alarms .= ",";
246
			$alarms .= "\"{$name}delay\"";
247
			$override = true;
248
		} else {
249
			if ($override == true)
250
				$alarms .= ",";
251
			$alarms .= "\"delay\"";
252
			$override = true;
253
		}
254
		if (!empty($gateway['down'])) {
255
			$alarmscfg .= "alarm down \"{$name}down\" {\n";
256
			$alarmscfg .= "\ttime {$gateway['down']}s\n";
257
			$alarmscfg .= "}\n";
258
			if ($override == true)
259
				$alarms .= ",";
260
			$alarms .= "\"{$name}down\"";
261
			$override = true;
262
		} else {
263
			if ($override == true)
264
				$alarms .= ",";
265
			$alarms .= "\"down\"";
266
			$override = true;
267
		}
268
		if ($override == true)
269
			$apingercfg .= "\talarms override {$alarms};\n";
270

    
271
		$apingercfg .= "	rrd file \"{$g['vardb_path']}/rrd/{$gateway['name']}-quality.rrd\"\n";
272
		$apingercfg .= "}\n";
273
		$apingercfg .= "\n";
274

    
275
		$apingerconfig .= $alarmscfg;
276
		$apingerconfig .= $apingercfg;
277
	}
278
	@file_put_contents("{$g['varetc_path']}/apinger.conf", $apingerconfig);
279
	unset($apingerconfig);
280

    
281
	if (is_dir("{$g['tmp_path']}"))
282
		chmod("{$g['tmp_path']}", 01777);
283
	if (!is_dir("{$g['vardb_path']}/rrd"))
284
		mkdir("{$g['vardb_path']}/rrd", 0775);
285

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

    
288
	if (isvalidpid("{$g['varrun_path']}/apinger.pid"))
289
		sigkillbypid("{$g['varrun_path']}/apinger.pid", "HUP");
290
	else {
291
		/* start a new apinger process */
292
		@unlink("{$g['varrun_path']}/apinger.status");
293
		sleep(1);
294
		mwexec_bg("/usr/local/sbin/apinger -c {$g['varetc_path']}/apinger.conf");
295
		sleep(1);
296
		sigkillbypid("{$g['varrun_path']}/apinger.pid", "USR1");
297
	}
298

    
299
	return 0;
300
}
301

    
302
/* return the status of the apinger targets as a array */
303
function return_gateways_status($byname = false) {
304
	global $config, $g;
305

    
306
	$apingerstatus = array();
307
	/* Always get the latest status from apinger */
308
	if (file_exists("{$g['varrun_path']}/apinger.pid"))
309
                sigkillbypid("{$g['varrun_path']}/apinger.pid", "USR1");
310
	if (file_exists("{$g['varrun_path']}/apinger.status")) {
311
		$apingerstatus = file("{$g['varrun_path']}/apinger.status");
312
	} else
313
		$apingerstatus = array();
314

    
315
	$status = array();
316
	foreach($apingerstatus as $line) {
317
		$info = explode("|", $line);
318
		if ($byname == false)
319
			$target = $info[0];
320
		else
321
			$target = $info[2];
322

    
323
		$status[$target] = array();
324
		$status[$target]['monitorip'] = $info[0];
325
		$status[$target]['srcip'] = $info[1];
326
		$status[$target]['name'] = $info[2];
327
		$status[$target]['lastcheck'] = $info[5] ? date('r', $info[5]) : date('r');
328
		$status[$target]['delay'] = empty($info[6]) ? "0ms" : round($info[6], 1) ."ms" ;
329
		$status[$target]['loss'] = empty($info[7]) ? "0.0%" : round($info[7], 1) . "%";
330
		$status[$target]['status'] = trim($info[8]);
331
	}
332

    
333
	/* tack on any gateways that have monitoring disabled
334
	 * or are down, which could cause gateway groups to fail */
335
	$gateways_arr = return_gateways_array();
336
	foreach($gateways_arr as $gwitem) {
337
		if(!isset($gwitem['monitor_disable']))
338
			continue;
339
		if(!is_ipaddr($gwitem['monitorip'])) {
340
			$realif = $gwitem['interface'];
341
			$tgtip = get_interface_gateway($realif);
342
			if (!is_ipaddr($tgtip))
343
				$tgtip = "none";
344
			$srcip = find_interface_ip($realif);
345
		} else {
346
			$tgtip = $gwitem['monitorip'];
347
			$srcip = find_interface_ip($realif);
348
		}
349
		if($byname == true)
350
			$target = $gwitem['name'];
351
		else
352
			$target = $tgtip;
353

    
354
		/* failsafe for down interfaces */
355
		if($target == "none") {
356
			$target = $gwitem['name'];
357
			$status[$target]['name'] = $gwitem['name'];
358
			$status[$target]['lastcheck'] = date('r');
359
			$status[$target]['delay'] = "0.0ms";
360
			$status[$target]['loss'] = "100.0%";
361
			$status[$target]['status'] = "down";
362
		} else {
363
			$status[$target]['monitorip'] = $tgtip;
364
			$status[$target]['srcip'] = $srcip;
365
			$status[$target]['name'] = $gwitem['name'];
366
			$status[$target]['lastcheck'] = date('r');
367
			$status[$target]['delay'] = "0.0ms";
368
			$status[$target]['loss'] = "0.0%";
369
			$status[$target]['status'] = "none";
370
		}
371
	}
372
	return($status);
373
}
374

    
375
/* Return all configured gateways on the system */
376
function return_gateways_array($disabled = false, $localhost = false) {
377
	global $config, $g;
378

    
379
	$gateways_arr = array();
380

    
381
	$found_defaultv4 = 0;
382
	$found_defaultv6 = 0;
383

    
384
	// Ensure the interface cache is up to date first
385
	$interfaces = get_interface_arr(true);
386
	$interfaces_v4 = array();
387
	$interfaces_v6 = array();
388

    
389
	$i = -1;
390
	/* Process/add all the configured gateways. */
391
	if (is_array($config['gateways']['gateway_item'])) {
392
		foreach ($config['gateways']['gateway_item'] as $gateway) {
393
			/* Increment it here to do not skip items */
394
			$i++;
395

    
396
			if (empty($config['interfaces'][$gateway['interface']]))
397
				continue;
398
			$wancfg = $config['interfaces'][$gateway['interface']];
399

    
400
			/* skip disabled interfaces */
401
			if ($disabled === false && !isset($wancfg['enable']))
402
				continue;
403

    
404
			/* if the gateway is dynamic and we can find the IPv4, Great! */
405
			if (empty($gateway['gateway']) || $gateway['gateway'] == "dynamic") {
406
				if ($gateway['ipprotocol'] == "inet") {
407
					/* we know which interfaces is dynamic, this should be made a function */
408
					$gateway['gateway'] = get_interface_gateway($gateway['interface']);
409
					/* no IP address found, set to dynamic */
410
					if (!is_ipaddrv4($gateway['gateway']))
411
						$gateway['gateway'] = "dynamic";
412
					$gateway['dynamic'] = true;
413
				}
414

    
415
				/* if the gateway is dynamic and we can find the IPv6, Great! */
416
				else if ($gateway['ipprotocol'] == "inet6") {
417
					/* we know which interfaces is dynamic, this should be made a function, and for v6 too */
418
					$gateway['gateway'] = get_interface_gateway_v6($gateway['interface']);
419
					/* no IPv6 address found, set to dynamic */
420
					if (!is_ipaddrv6($gateway['gateway']))
421
						$gateway['gateway'] = "dynamic";
422
					$gateway['dynamic'] = true;
423
				}
424
			} else {
425
				/* getting this detection right is hard at this point because we still don't
426
				 * store the address family in the gateway item */
427
				if (is_ipaddrv4($gateway['gateway']))
428
					$gateway['ipprotocol'] = "inet";
429
				else if(is_ipaddrv6($gateway['gateway']))
430
					$gateway['ipprotocol'] = "inet6";
431
			}
432

    
433
			if (isset($gateway['monitor_disable']))
434
				$gateway['monitor_disable'] = true;
435
			else if (empty($gateway['monitor']))
436
				$gateway['monitor'] = $gateway['gateway'];
437

    
438
			$gateway['friendlyiface'] = $gateway['interface'];
439

    
440
			/* special treatment for tunnel interfaces */
441
			if ($gateway['ipprotocol'] == "inet6") {
442
				$gateway['interface'] = get_real_interface($gateway['interface'], "inet6", false, false);
443
				$interfaces_v6[$gateway['friendlyiface']] = $gateway['friendlyiface'];
444
			} else {
445
				$gateway['interface'] = get_real_interface($gateway['interface'], "all", false, false);
446
				$interfaces_v4[$gateway['friendlyiface']] = $gateway['friendlyiface'];
447
			}
448

    
449
			/* entry has a default flag, use it */
450
			if (isset($gateway['defaultgw'])) {
451
				if ($gateway['ipprotocol'] == "inet") {
452
					$gateway['defaultgw'] = true;
453
					$found_defaultv4 = 1;
454
				} else if ($gateway['ipprotocol'] == "inet6") {
455
					$gateway['defaultgw'] = true;
456
					$found_defaultv6 = 1;
457
				}
458
			}
459
			/* include the gateway index as the attribute */
460
			$gateway['attribute'] = $i;
461

    
462
			$gateways_arr[$gateway['name']] = $gateway;
463
		}
464
	}
465
	unset($gateway);
466

    
467
	/* Loop through all interfaces with a gateway and add it to a array */
468
	if ($disabled == false)
469
		$iflist = get_configured_interface_with_descr();
470
	else
471
		$iflist = get_configured_interface_with_descr(false, true);
472

    
473
	/* Process/add dynamic v4 gateways. */
474
	foreach($iflist as $ifname => $friendly ) {
475
		if(! interface_has_gateway($ifname))
476
			continue;
477

    
478
		if (empty($config['interfaces'][$ifname]))
479
			continue;
480

    
481
		$ifcfg = &$config['interfaces'][$ifname];
482
		if(!isset($ifcfg['enable']))
483
			continue;
484

    
485
		if(!empty($ifcfg['ipaddr']) && is_ipaddrv4($ifcfg['ipaddr']))
486
			continue;
487

    
488
		if (isset($interfaces_v4[$ifname]))
489
			continue;
490

    
491
		$ctype = "";
492
		switch($ifcfg['ipaddr']) {
493
			case "dhcp":
494
			case "pppoe":
495
			case "pptp":
496
			case "ppp":
497
				$ctype = strtoupper($ifcfg['ipaddr']);
498
				break;
499
			default:
500
				if (substr($ifcfg['if'], 0, 4) ==  "ovpn")
501
					$ctype = "VPNv4";
502
				break;
503
		}
504
		$ctype = "_". strtoupper($ctype);
505

    
506
		$gateway = array();
507
		$gateway['dynamic'] = false;
508
		$gateway['ipprotocol'] = "inet";
509
		$gateway['gateway'] = get_interface_gateway($ifname, $gateway['dynamic']);
510
		$gateway['interface'] = get_real_interface($ifname);
511
		$gateway['friendlyiface'] = $ifname;
512
		$gateway['name'] = "{$friendly}{$ctype}";
513
		$gateway['attribute'] = "system";
514

    
515
		if (($gateway['dynamic'] === "default") && ($found_defaultv4 == 0)) {
516
			$gateway['defaultgw'] = true;
517
			$gateway['dynamic'] = true;
518
			$found_defaultv4 = 1;
519
		}
520
		/* Loopback dummy for dynamic interfaces without a IP */
521
		if (!is_ipaddrv4($gateway['gateway']) && $gateway['dynamic'] == true)
522
			$gateway['gateway'] = "dynamic";
523

    
524
		/* automatically skip known static and dynamic gateways we have a array entry for */
525
		foreach($gateways_arr as $gateway_item) {
526
			if ((($ifname == $gateway_item['friendlyiface'] && $friendly == $gateway_item['name'])&& ($gateway['ipprotocol'] == $gateway_item['ipprotocol'])) ||
527
				($ifname == $gateway_item['friendlyiface'] && $gateway_item['dynamic'] == true) && ($gateway['ipprotocol'] == $gateway_item['ipprotocol']))
528
					continue 2;
529
		}
530

    
531
		if (is_ipaddrv4($gateway['gateway']))
532
			$gateway['monitor'] = $gateway['gateway'];
533

    
534
		$gateway['descr'] = "Interface {$friendly}{$ctype} Gateway";
535
		$gateways_arr[$gateway['name']] = $gateway;
536
	}
537
	unset($gateway);
538

    
539
	/* Process/add dynamic v6 gateways. */
540
	foreach($iflist as $ifname => $friendly ) {
541
		/* If the user has disabled IPv6, they probably don't want any IPv6 gateways. */
542
		if (!isset($config['system']['ipv6allow']))
543
			break;
544

    
545
		if(! interface_has_gatewayv6($ifname))
546
			continue;
547

    
548
		if (empty($config['interfaces'][$ifname]))
549
			continue;
550

    
551
		$ifcfg = &$config['interfaces'][$ifname];
552
		if(!isset($ifcfg['enable']))
553
			continue;
554

    
555
		if(!empty($ifcfg['ipaddrv6']) && is_ipaddrv6($ifcfg['ipaddrv6']))
556
			continue;
557

    
558
		if(isset($interfaces_v6[$ifname]))
559
			continue;
560

    
561
		$ctype = "";
562
		switch($ifcfg['ipaddrv6']) {
563
			case "slaac":
564
			case "dhcp6":
565
			case "6to4":
566
			case "6rd":
567
				$ctype = strtoupper($ifcfg['ipaddrv6']);
568
				break;
569
			default:
570
				$tunnelif = substr($ifcfg['if'], 0, 3);
571
				if (substr($ifcfg['if'], 0, 4) ==  "ovpn")
572
					$ctype = "VPNv6";
573
				else if ($tunnelif == "gif" || $tunnelif == "gre")
574
					$ctype = "TUNNELv6";
575
				break;
576
		}
577
		$ctype = "_". strtoupper($ctype);
578

    
579
		$gateway = array();
580
		$gateway['dynamic'] = false;
581
		$gateway['ipprotocol'] = "inet6";
582
		$gateway['gateway'] = get_interface_gateway_v6($ifname, $gateway['dynamic']);
583
		$gateway['interface'] = get_real_interface($ifname, "inet6");
584
		switch($ifcfg['ipaddrv6']) {
585
			case "6rd":
586
			case "6to4":
587
				$gateway['dynamic'] = "default";
588
				break;
589
		}
590
		$gateway['friendlyiface'] = $ifname;
591
		$gateway['name'] = "{$friendly}{$ctype}";
592
		$gateway['attribute'] = "system";
593

    
594
		if (($gateway['dynamic'] === "default")  && ($found_defaultv6 == 0)) {
595
			$gateway['defaultgw'] = true;
596
			$gateway['dynamic'] = true;
597
			$found_defaultv6 = 1;
598
		}
599

    
600
		/* Loopback dummy for dynamic interfaces without a IP */
601
		if (!is_ipaddrv6($gateway['gateway']) && $gateway['dynamic'] == true)
602
			$gateway['gateway'] = "dynamic";
603

    
604
		/* automatically skip known static and dynamic gateways we have a array entry for */
605
		foreach($gateways_arr as $gateway_item) {
606
			if ((($ifname == $gateway_item['friendlyiface'] && $friendly == $gateway_item['name']) && ($gateway['ipprotocol'] == $gateway_item['ipprotocol'])) ||
607
				($ifname == $gateway_item['friendlyiface'] && $gateway_item['dynamic'] == true) && ($gateway['ipprotocol'] == $gateway_item['ipprotocol']))
608
					continue 2;
609
		}
610

    
611
		if (is_ipaddrv6($gateway['gateway']))
612
			$gateway['monitor'] = $gateway['gateway'];
613

    
614
		$gateway['descr'] = "Interface {$friendly}{$ctype} Gateway";
615
		$gateways_arr[$gateway['name']] = $gateway;
616
	}
617
	unset($gateway);
618

    
619
	/* FIXME: Should this be enabled.
620
	 * Some interface like wan might be default but have no info recorded
621
	 * the config. */
622
	/* this is a fallback if all else fails and we want to get packets out @smos */
623
	if ($found_defaultv4 == 0 || $found_defaultv6 == 0) {
624
		foreach ($gateways_arr as &$gateway) {
625
			if (($gateway['friendlyiface'] == "wan") && ($found_defaultv4 == 0) && (!isset($gateway['ipprotocol']) || ($gateway['ipprotocol'] == "inet"))) {
626
				if (file_exists("{$g['tmp_path']}/{$gateway['interface']}_defaultgw")) {
627
					$gateway['defaultgw'] = true;
628
					$found_defaultv4 = 1;
629
				}
630
			}
631
			if (($gateway['friendlyiface'] == "wan") && ($found_defaultv6 == 0) && ($gateway['ipprotocol'] == "inet6")) {
632
				if (file_exists("{$g['tmp_path']}/{$gateway['interface']}_defaultgwv6")) {
633
					$gateway['defaultgw'] = true;
634
					$found_defaultv6 = 1;
635
				}
636
			}
637
		}
638
	}
639

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

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

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

    
717
	/* fetch the current gateways status */
718
	$gateways_status = return_gateways_status(true);
719
	$gateways_arr = return_gateways_array();
720
	$gateway_groups_array = array();
721

    
722
	if (isset($config['system']['gw_switch_default'])) {
723
		fixup_default_gateway("inet", $gateways_status, $gateways_arr);
724
		fixup_default_gateway("inet6", $gateways_status, $gateways_arr);
725
	}
726
	if (is_array($config['gateways']['gateway_group'])) {
727
		$carplist = get_configured_carp_interface_list();
728
		foreach ($config['gateways']['gateway_group'] as $group) {
729
			/* create array with group gateways members separated by tier */
730
			$tiers = array();
731
			$backupplan = array();
732
			$gwvip_arr = array();
733
			foreach ($group['item'] as $item) {
734
				list($gwname, $tier, $vipname) = explode("|", $item);
735

    
736
				if (is_ipaddr($carplist[$vipname])) {
737
					if (!is_array($gwvip_arr[$group['name']]))
738
						$gwvip_arr[$group['name']] = array();
739
					$gwvip_arr[$group['name']][$gwname] = $vipname;
740
				}
741

    
742
				/* Do it here rather than reiterating again the group in case no member is up. */
743
				if (!is_array($backupplan[$tier]))
744
					$backupplan[$tier] = array();
745
				$backupplan[$tier][] = $gwname;
746

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

    
790
			/* we do not really foreach the tiers as we stop after the first tier */
791
			foreach ($tiers as $tieridx => $tier) {
792
				/* process all gateways in this tier */
793
				foreach ($tier as $member) {
794
					/* determine interface gateway */
795
					if (isset($gateways_arr[$member])) {
796
						$gateway = $gateways_arr[$member];
797
						$int = $gateway['interface'];
798
						$gatewayip = "";
799
						if(is_ipaddr($gateway['gateway']))
800
							$gatewayip = $gateway['gateway'];
801
						else if (!empty($int))
802
							$gatewayip = get_interface_gateway($gateway['friendlyiface']);
803

    
804
						if (!empty($int)) {
805
							$gateway_groups_array[$group['name']]['ipprotocol'] = $gateway['ipprotocol'];
806
							if (is_ipaddr($gatewayip)) {
807
								$groupmember = array();
808
								$groupmember['int']  = $int;
809
								$groupmember['gwip']  = $gatewayip;
810
								$groupmember['weight']  = isset($gateway['weight']) ? $gateway['weight'] : 1;
811
								if (is_array($gwvip_arr[$group['name']])&& !empty($gwvip_arr[$group['name']][$member]))
812
									$groupmember['vip'] = $gwvip_arr[$group['name']][$member];
813
								$gateway_groups_array[$group['name']][] = $groupmember;
814
							}
815
						}
816
					}
817
				}
818
				/* we should have the 1st available tier now, exit stage left */
819
				if (count($gateway_groups_array[$group['name']]) > 0)
820
					break;
821
				else
822
					log_error("GATEWAYS: Group {$group['name']} did not have any gateways up on tier {$tieridx}!");
823
			}
824
		}
825
	}
826

    
827
	return ($gateway_groups_array);
828
}
829

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

    
846
function lookup_gateway_ip_by_name($name) {
847

    
848
	$gateways_arr = return_gateways_array(false, true);
849
	foreach ($gateways_arr as $gname => $gw) {
850
		if ($gw['name'] === $name || $gname === $name)
851
			return $gw['gateway'];
852
	}
853

    
854
	return false;
855
}
856

    
857
function lookup_gateway_monitor_ip_by_name($name) {
858

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

    
865
		return $gateway['monitor'];
866
	}
867

    
868
	return (false);
869
}
870

    
871
function lookup_gateway_interface_by_name($name) {
872

    
873
	$gateways_arr = return_gateways_array(false, true);
874
	if (!empty($gateways_arr[$name])) {
875
		$interfacegw = $gateways_arr[$name]['friendlyiface'];
876
		return ($interfacegw);
877
	}
878

    
879
	return (false);
880
}
881

    
882
function get_interface_gateway($interface, &$dynamic = false) {
883
	global $config, $g;
884

    
885
	$gw = NULL;
886

    
887
	$gwcfg = $config['interfaces'][$interface];
888
	if (!empty($gwcfg['gateway']) && is_array($config['gateways']['gateway_item'])) {
889
		foreach($config['gateways']['gateway_item'] as $gateway) {
890
			if(($gateway['name'] == $gwcfg['gateway']) && (is_ipaddrv4($gateway['gateway']))) {
891
				$gw = $gateway['gateway'];
892
				break;
893
			}
894
		}
895
	}
896

    
897
	// for dynamic interfaces we handle them through the $interface_router file.
898
	if (!is_ipaddrv4($gw) && !is_ipaddrv4($gwcfg['ipaddr'])) {
899
		$realif = get_real_interface($interface);
900
		if (file_exists("{$g['tmp_path']}/{$realif}_router")) {
901
			$gw = trim(file_get_contents("{$g['tmp_path']}/{$realif}_router"), " \n");
902
			$dynamic = true;
903
		}
904
		if (file_exists("{$g['tmp_path']}/{$realif}_defaultgw"))
905
			$dynamic = "default";
906

    
907
	}
908

    
909
	/* return gateway */
910
	return ($gw);
911
}
912

    
913
function get_interface_gateway_v6($interface, &$dynamic = false) {
914
	global $config, $g;
915

    
916
	$gw = NULL;
917
	$gwcfg = $config['interfaces'][$interface];
918
	if (!empty($gwcfg['gatewayv6']) && is_array($config['gateways']['gateway_item'])) {
919
		foreach($config['gateways']['gateway_item'] as $gateway) {
920
			if(($gateway['name'] == $gwcfg['gatewayv6']) && (is_ipaddrv6($gateway['gateway']))) {
921
				$gw = $gateway['gateway'];
922
				break;
923
			}
924
		}
925
	}
926

    
927
	// for dynamic interfaces we handle them through the $interface_router file.
928
	if (!is_ipaddrv6($gw) && !is_ipaddrv6($gwcfg['ipaddrv6'])) {
929
			$realif = get_real_interface($interface);
930
			if (file_exists("{$g['tmp_path']}/{$realif}_routerv6")) {
931
				$gw = trim(file_get_contents("{$g['tmp_path']}/{$realif}_routerv6"), " \n");
932
				$dynamic = true;
933
			}
934
			if (file_exists("{$g['tmp_path']}/{$realif}_defaultgwv6"))
935
				$dynamic = "default";
936

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

    
942
/* Check a IP address against a gateway IP or name
943
 * to verify it's address family */
944
function validate_address_family($ipaddr, $gwname) {
945
	$v4ip = false;
946
	$v6ip = false;
947
	$v4gw = false;
948
	$v6gw = false;
949

    
950
	if(is_ipaddrv4($ipaddr))
951
		$v4ip = true;
952
	if(is_ipaddrv6($ipaddr))
953
		$v6ip = true;
954
	if(is_ipaddrv4($gwname))
955
		$v4gw = true;
956
	if(is_ipaddrv6($gwname))
957
		$v6gw = true;
958

    
959
	if($v4ip && $v4gw)
960
		return true;
961
	if($v6ip && $v6gw)
962
		return true;
963

    
964
	/* still no match, carry on, lookup gateways */
965
	if(is_ipaddrv4(lookup_gateway_ip_by_name($gwname)))
966
		$v4gw = true;
967
	if(is_ipaddrv6(lookup_gateway_ip_by_name($gwname)))
968
		$v6gw = true;
969

    
970
	$gw_array = return_gateways_array();
971
	if(is_array($gw_array[$gwname])) {
972
		switch($gw_array[$gwname]['ipprotocol']) {
973
			case "inet":
974
				$v4gw = true;
975
				break;
976
			case "inet6":
977
				$v6gw = true;
978
				break;
979
		}
980
	}
981

    
982
	if($v4ip && $v4gw)
983
		return true;
984
	if($v6ip && $v6gw)
985
		return true;
986

    
987
	return false;
988
}
989

    
990
/* check if a interface is part of a gateway group */
991
function interface_gateway_group_member($interface) {
992
	global $config;
993

    
994
	if (is_array($config['gateways']['gateway_group']))
995
		$groups = $config['gateways']['gateway_group'];
996
	else
997
		return false;
998

    
999
	$gateways_arr = return_gateways_array(false, true);
1000
	foreach($groups as $group) {
1001
		if(is_array($group['item'])) {
1002
			foreach($group['item'] as $item) {
1003
				$elements = explode("|", $item);
1004
				$gwname = $elements[0];
1005
				if ($interface == $gateways_arr[$gwname]['interface']) {
1006
					unset($gateways_arr);
1007
					return true;
1008
				}
1009
			}
1010
		}
1011
	}
1012
	unset($gateways_arr);
1013

    
1014
	return false;
1015
}
1016

    
1017
function gateway_is_gwgroup_member($name) {
1018
	global $config;
1019

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

    
1025
	$members = array();
1026
	foreach($groups as $group) {
1027
		if (is_array($group['item'])) {
1028
			foreach($group['item'] as $item) {
1029
				$elements = explode("|", $item);
1030
				$gwname = $elements[0];
1031
				if ($name == $elements[0])
1032
					$members[] = $group['name'];
1033
			}
1034
		}
1035
	}
1036

    
1037
	return $members;
1038
}
1039
?>
    (1-1/1)