Project

General

Profile

Download (70.1 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
 * gwlb.inc
4
 *
5
 * part of pfSense (https://www.pfsense.org)
6
 * Copyright (c) 2008 Bill Marquette, Seth Mos
7
 * Copyright (c) 2008-2013 BSD Perimeter
8
 * Copyright (c) 2013-2016 Electric Sheep Fencing
9
 * Copyright (c) 2014-2020 Rubicon Communications, LLC (Netgate)
10
 * All rights reserved.
11
 *
12
 * Licensed under the Apache License, Version 2.0 (the "License");
13
 * you may not use this file except in compliance with the License.
14
 * You may obtain a copy of the License at
15
 *
16
 * http://www.apache.org/licenses/LICENSE-2.0
17
 *
18
 * Unless required by applicable law or agreed to in writing, software
19
 * distributed under the License is distributed on an "AS IS" BASIS,
20
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21
 * See the License for the specific language governing permissions and
22
 * limitations under the License.
23
 */
24

    
25
require_once("config.inc");
26
require_once("rrd.inc");
27
require_once("ipsec.inc");
28

    
29
/* Returns an array of default values used for dpinger */
30
function return_dpinger_defaults() {
31
	return array(
32
		"latencylow" => "200",
33
		"latencyhigh" => "500",
34
		"losslow" => "10",
35
		"losshigh" => "20",
36
		"interval" => "500",
37
		"loss_interval" => "2000",
38
		"time_period" => "60000",
39
		"alert_interval" => "1000",
40
		"data_payload" => "0");
41
}
42

    
43
function running_dpinger_processes() {
44
	global $g;
45

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

    
48
	$result = array();
49
	if ($pidfiles === FALSE) {
50
		return $result;
51
	}
52

    
53
	foreach ($pidfiles as $pidfile) {
54
		if (preg_match('/^dpinger_(.+)~([^~]+)~([^~]+)\.pid$/',
55
		    basename($pidfile), $matches)) {
56
			$socket_file = preg_replace('/\.pid$/', '.sock',
57
			    $pidfile);
58
			$result[$matches[1]] = array(
59
			    'srcip'    => $matches[2],
60
			    'targetip' => $matches[3],
61
			    'pidfile'  => $pidfile,
62
			    'socket'   => $socket_file
63
			);
64
			unset($gwinfo);
65
		}
66
	}
67

    
68
	return $result;
69
}
70

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

    
79
	$running_processes = running_dpinger_processes();
80

    
81
	foreach ($running_processes as $running_gwname => $process) {
82
		if ($gwname != '' && $running_gwname != $gwname) {
83
			continue;
84
		}
85

    
86
		if (isvalidpid($process['pidfile'])) {
87
			killbypid($process['pidfile'], 3);
88
		} else {
89
			@unlink($process['pidfile']);
90
		}
91
	}
92
}
93

    
94
function start_dpinger($gateway) {
95
	global $g;
96

    
97
	if (!isset($gateway['gwifip'])) {
98
		return (false);
99
	}
100

    
101
	$dpinger_defaults = return_dpinger_defaults();
102

    
103
	$prefix = "{$g['varrun_path']}/dpinger_{$gateway['name']}~" .
104
	    "{$gateway['gwifip']}~{$gateway['monitor']}";
105
	# dpinger socket path should not be longer then uaddr.sun_path
106
	if (strlen($prefix) > 95) {
107
		$prefix = "{$g['varrun_path']}/dpinger_{$gateway['name']}~" .
108
		    substr(md5($gateway['gwifip']),0,8) . "~" .
109
		    $gateway['monitor'];
110
	}
111
	$pidfile = $prefix . ".pid";
112
	$socket = $prefix . ".sock";
113
	$alarm_cmd = "{$g['etc_path']}/rc.gateway_alarm";
114

    
115
	$params  = "-S ";			/* Log warnings via syslog */
116
	$params .= "-r 0 ";			/* Disable unused reporting thread */
117
	$params .= "-i {$gateway['name']} ";	/* Identifier */
118
	$params .= "-B {$gateway['gwifip']} ";	/* Bind src address */
119
	$params .= "-p {$pidfile} ";		/* PID filename */
120
	$params .= "-u {$socket} ";		/* Status Socket */
121
	if (!$gateway['action_disable']) {
122
		$params .= "-C \"{$alarm_cmd}\" ";	/* Command to run on alarm */
123
	}
124

    
125
	$params .= "-d " .
126
	    (isset($gateway['data_payload']) && is_numeric($gateway['data_payload'])
127
	    ? $gateway['data_payload']
128
	    : $dpinger_defaults['data_payload']
129
	    ) . " ";
130

    
131
	$params .= "-s " .
132
	    (isset($gateway['interval']) && is_numeric($gateway['interval'])
133
	    ? $gateway['interval']
134
	    : $dpinger_defaults['interval']
135
	    ) . " ";
136

    
137
	$params .= "-l " .
138
	    (isset($gateway['loss_interval']) && is_numeric($gateway['loss_interval'])
139
	    ?  $gateway['loss_interval']
140
	    : $dpinger_defaults['loss_interval']
141
	    ) . " ";
142

    
143
	$params .= "-t " .
144
	    (isset($gateway['time_period']) && is_numeric($gateway['time_period'])
145
	    ?  $gateway['time_period']
146
	    : $dpinger_defaults['time_period']
147
	    ) . " ";
148

    
149
	$params .= "-A " .
150
	    (isset($gateway['alert_interval']) && is_numeric($gateway['alert_interval'])
151
	    ?  $gateway['alert_interval']
152
	    : $dpinger_defaults['alert_interval']
153
	    ) . " ";
154

    
155
	$params .= "-D " .
156
	    (isset($gateway['latencyhigh']) && is_numeric($gateway['latencyhigh'])
157
	    ?  $gateway['latencyhigh']
158
	    : $dpinger_defaults['latencyhigh']
159
	    ) . " ";
160

    
161
	$params .= "-L " .
162
	    (isset($gateway['losshigh']) && is_numeric($gateway['losshigh'])
163
	    ?  $gateway['losshigh']
164
	    : $dpinger_defaults['losshigh']
165
	    ) . " ";
166

    
167
	/* Make sure we don't end up with 2 process for the same GW */
168
	stop_dpinger($gateway['name']);
169

    
170
	/* Do not try to bind IPv6 where interface is in tentative state */
171
	if (is_ipaddrv6($gateway['gwifip'])) {
172
		$err = interface_wait_tentative(get_real_interface(
173
		    $gateway['interface']));
174
		if ($err == false) {
175
			log_error(gettext("Timeout waiting for IPv6 address in tentative state.  dpinger will not run."));
176
			return (false);
177
		}
178
	}
179

    
180
	/* Redirect stdout to /dev/null to avoid exec() to wait for dpinger */
181
	return mwexec("/usr/local/bin/dpinger {$params} {$gateway['monitor']} >/dev/null");
182
}
183

    
184
/*
185
 * Starts dpinger processes and adds appropriate static routes for monitor IPs
186
 */
187
function setup_gateways_monitor() {
188
	global $config, $g;
189

    
190
	$gateways_arr = return_gateways_array();
191
	if (!is_array($gateways_arr)) {
192
		log_error(gettext("No gateways to monitor. dpinger will not run."));
193
		stop_dpinger();
194
		return;
195
	}
196

    
197
	$monitor_ips = array();
198
	foreach ($gateways_arr as $gwname => $gateway) {
199
		/* Do not monitor if such was requested */
200
		if (isset($gateway['monitor_disable'])) {
201
			continue;
202
		}
203
		if (empty($gateway['monitor']) || !is_ipaddr($gateway['monitor'])) {
204
			if (is_ipaddr($gateway['gateway'])) {
205
				$gateways_arr[$gwname]['monitor'] = $gateway['gateway'];
206
			} else { /* No chance to get an ip to monitor skip target. */
207
				continue;
208
			}
209
		}
210

    
211
		/* if the monitor address is already used before, skip */
212
		if (in_array($gateway['monitor'], $monitor_ips)) {
213
			continue;
214
		}
215

    
216
		/* Interface ip is needed since dpinger will bind a socket to it.
217
		 * However the config GUI should already have checked this and when
218
		 * PPPoE is used the IP address is set to "dynamic". So using is_ipaddrv4
219
		 * or is_ipaddrv6 to identify packet type would be wrong, especially as
220
		 * further checks (that can cope with the "dynamic" case) are present inside
221
		 * the if block. So using $gateway['ipprotocol'] is the better option.
222
		 */
223
		if ($gateway['ipprotocol'] == "inet") { // This is an IPv4 gateway...
224
			$gwifip = find_interface_ip($gateway['interface'], true);
225
			if (!is_ipaddrv4($gwifip)) {
226
				continue; //Skip this target
227
			}
228

    
229
			if ($gwifip == "0.0.0.0") {
230
				continue; //Skip this target - the gateway is still waiting for DHCP
231
			}
232

    
233
			/*
234
			 * If the gateway is the same as the monitor we do not add a
235
			 * route as this will break the routing table.
236
			 * Add static routes for each gateway with their monitor IP
237
			 * not strictly necessary but is a added level of protection.
238
			 */
239
			if (is_ipaddrv4($gateway['gateway']) && $gateway['monitor'] != $gateway['gateway']) {
240
				log_error(sprintf(gettext('Removing static route for monitor %1$s and adding a new route through %2$s'), $gateway['monitor'], $gateway['gateway']));
241
				$route_to = "-host {$gateway['monitor']}";
242
				if (interface_isppp_type($gateway['friendlyiface'])) {
243
					route_add_or_change("{$route_to} -iface {$gateway['interface']}");
244
				} else {
245
					route_add_or_change("{$route_to} {$gateway['gateway']}");
246
				}
247

    
248
				pfSense_kill_states("0.0.0.0/0", $gateway['monitor'], $gateway['interface'], "icmp");
249
			}
250
		} else if ($gateway['ipprotocol'] == "inet6") { // This is an IPv6 gateway...
251
			if (is_linklocal($gateway['gateway']) &&
252
			    get_ll_scope($gateway['gateway']) == '') {
253
				$gateway['gateway'] .= '%' . $gateway['interface'];
254
			}
255

    
256
			if (is_linklocal($gateway['monitor'])) {
257
				if (get_ll_scope($gateway['monitor']) == '') {
258
					$gateways_arr[$gwname]['monitor'] .= '%' . $gateway['interface'];
259
				}
260

    
261
				$gwifip = find_interface_ipv6_ll($gateway['interface'], true);
262

    
263
				if (get_ll_scope($gwifip) == '') {
264
					$gwifip .= '%' . $gateway['interface'];
265
				}
266
			} else {
267
				$gwifip = find_interface_ipv6($gateway['interface'], true);
268
			}
269

    
270
			if (!is_ipaddrv6($gwifip)) {
271
				continue; //Skip this target
272
			}
273

    
274
			/*
275
			 * If the gateway is the same as the monitor we do not add a
276
			 * route as this will break the routing table.
277
			 * Add static routes for each gateway with their monitor IP
278
			 * not strictly necessary but is a added level of protection.
279
			 */
280
			if ($gateway['gateway'] != $gateway['monitor']) {
281
				log_error(sprintf(gettext('Removing static route for monitor %1$s and adding a new route through %2$s'), $gateway['monitor'], $gateway['gateway']));
282
				$route_to = "-host -inet6 {$gateway['monitor']}";
283
				if (interface_isppp_type($gateway['friendlyiface'])) {
284
					route_add_or_change("{$route_to} -iface {$gateway['interface']}");
285
				} else {
286
					route_add_or_change("{$route_to} {$gateway['gateway']}");
287
				}
288

    
289
				pfSense_kill_states("::0.0.0.0/0", $gateway['monitor'], $gateway['interface'], "icmpv6");
290
			}
291
		} else {
292
			continue;
293
		}
294

    
295
		$monitor_ips[] = $gateway['monitor'];
296
		$gateways_arr[$gwname]['enable_dpinger'] = true;
297
		$gateways_arr[$gwname]['gwifip'] = $gwifip;
298
	}
299

    
300
	stop_dpinger();
301

    
302
	/* Start new processes */
303
	foreach ($gateways_arr as $gateway) {
304
		if (!isset($gateway['enable_dpinger'])) {
305
			continue;
306
		}
307

    
308
		if (start_dpinger($gateway) != 0) {
309
			log_error(sprintf(gettext("Error starting gateway monitor for %s"), $gateway['name']));
310
		}
311
	}
312

    
313
	return;
314
}
315

    
316
function get_dpinger_status($gwname, $detailed = false) {
317
	global $g;
318

    
319
	$running_processes = running_dpinger_processes();
320

    
321
	if (!isset($running_processes[$gwname])) {
322
		log_error(sprintf(gettext('dpinger: No dpinger session running for gateway %s'), $gwname));
323
		return false;
324
	}
325

    
326
	$proc = $running_processes[$gwname];
327
	unset($running_processes);
328

    
329
	$timeoutcounter = 0;
330
	while (true) {
331
		if (!file_exists($proc['socket'])) {
332
			log_error("dpinger: status socket {$proc['socket']} not found");
333
			return false;
334
		}
335
		$fp = @stream_socket_client("unix://{$proc['socket']}", $errno, $errstr, 10);
336
		if (!$fp) {
337
			log_error(sprintf(gettext('dpinger: cannot connect to status socket %1$s - %2$s (%3$s)'), $proc['socket'], $errstr, $errno));
338
			return false;
339
		}
340

    
341
		$status = '';
342
		while (!feof($fp)) {
343
			$status .= fgets($fp, 1024);
344
		}
345
		fclose($fp);
346

    
347
		$r = array();
348
		list(
349
			$r['gwname'],
350
			$r['latency_avg'],
351
			$r['latency_stddev'],
352
			$r['loss']
353
		) = explode(' ', preg_replace('/\n/', '', $status));
354

    
355
		// dpinger returns '<gwname> 0 0 0' when queried directly after it starts.
356
		// while a latency of 0 and a loss of 0 would be perfect, in a real world it doesnt happen.
357
		// or does it, anyone? if so we must 'detect' the initialization period differently..
358
		$ready = $r['latency_stddev'] != '0' || $r['loss'] != '0';
359

    
360
		if ($ready) {
361
			break;
362
		} else {
363
			$timeoutcounter++;
364
			if ($timeoutcounter > 300) {
365
				log_error(sprintf(gettext('dpinger: timeout while retrieving status for gateway %s'), $gwname));
366
				return false;
367
			}
368
			usleep(10000);
369
		}
370
	}
371

    
372
	$r['srcip'] = $proc['srcip'];
373
	$r['targetip'] = $proc['targetip'];
374

    
375
	$gateways_arr = return_gateways_array();
376
	unset($gw);
377
	if (isset($gateways_arr[$gwname])) {
378
		$gw = $gateways_arr[$gwname];
379
	}
380

    
381
	$r['latency_avg'] = round($r['latency_avg']/1000, 3);
382
	$r['latency_stddev'] = round($r['latency_stddev']/1000, 3);
383

    
384
	$r['status'] = "none";
385
	if (isset($gw) && isset($gw['force_down'])) {
386
		$r['status'] = "force_down";
387
	} else if (isset($gw)) {
388
		$settings = return_dpinger_defaults();
389

    
390
		$keys = array(
391
		    'latencylow',
392
		    'latencyhigh',
393
		    'losslow',
394
		    'losshigh'
395
		);
396

    
397
		/* Replace default values by user-defined */
398
		foreach ($keys as $key) {
399
			if (isset($gw[$key]) && is_numeric($gw[$key])) {
400
				$settings[$key] = $gw[$key];
401
			}
402
		}
403

    
404
		if ($r['latency_avg'] > $settings['latencyhigh']) {
405
			if ($detailed) {
406
				$r['status'] = "highdelay";
407
			} else {
408
				$r['status'] = "down";
409
			}
410
		} else if ($r['loss'] > $settings['losshigh']) {
411
			if ($detailed) {
412
				$r['status'] = "highloss";
413
			} else {
414
				$r['status'] = "down";
415
			}
416
		} else if ($r['latency_avg'] > $settings['latencylow']) {
417
			$r['status'] = "delay";
418
		} else if ($r['loss'] > $settings['losslow']) {
419
			$r['status'] = "loss";
420
		}
421
	}
422

    
423
	return $r;
424
}
425

    
426
/* return the status of the dpinger targets as an array */
427
function return_gateways_status($byname = false) {
428
	global $config, $g;
429

    
430
	$dpinger_gws = running_dpinger_processes();
431
	$status = array();
432

    
433
	$gateways_arr = return_gateways_array();
434

    
435
	foreach ($dpinger_gws as $gwname => $gwdata) {
436
		// If action is disabled for this gateway, then we want a detailed status.
437
		// That reports "highdelay" or "highloss" rather than just "down".
438
		// Because reporting the gateway down would be misleading (gateway action is disabled)
439
		$detailed = $gateways_arr[$gwname]['action_disable'];
440
		$dpinger_status = get_dpinger_status($gwname, $detailed);
441
		if ($dpinger_status === false) {
442
			continue;
443
		}
444

    
445
		if ($byname == false) {
446
			$target = $dpinger_status['targetip'];
447
		} else {
448
			$target = $gwname;
449
		}
450

    
451
		$status[$target] = array();
452
		$status[$target]['monitorip'] = $dpinger_status['targetip'];
453
		$status[$target]['srcip'] = $dpinger_status['srcip'];
454
		$status[$target]['name'] = $gwname;
455
		$status[$target]['delay'] = empty($dpinger_status['latency_avg']) ? "0ms" : $dpinger_status['latency_avg'] . "ms";
456
		$status[$target]['stddev'] = empty($dpinger_status['latency_stddev']) ? "0ms" : $dpinger_status['latency_stddev'] . "ms";
457
		$status[$target]['loss'] = empty($dpinger_status['loss']) ? "0.0%" : round($dpinger_status['loss'], 1) . "%";
458
		$status[$target]['status'] = $dpinger_status['status'];
459
	}
460

    
461
	/* tack on any gateways that have monitoring disabled
462
	 * or are down, which could cause gateway groups to fail */
463
	$gateways_arr = return_gateways_array();
464
	foreach ($gateways_arr as $gwitem) {
465
		if (!isset($gwitem['monitor_disable'])) {
466
			continue;
467
		}
468
		if (!is_ipaddr($gwitem['monitor'])) {
469
			$realif = $gwitem['interface'];
470
			$tgtip = get_interface_gateway($realif);
471
			if (!is_ipaddr($tgtip)) {
472
				$tgtip = "none";
473
			}
474
			$srcip = find_interface_ip($realif);
475
		} else {
476
			$tgtip = $gwitem['monitor'];
477
			$srcip = find_interface_ip($realif);
478
		}
479
		if ($byname == true) {
480
			$target = $gwitem['name'];
481
		} else {
482
			$target = $tgtip;
483
		}
484

    
485
		/* failsafe for down interfaces */
486
		if ($target == "none") {
487
			$target = $gwitem['name'];
488
			$status[$target]['name'] = $gwitem['name'];
489
			$status[$target]['delay'] = "0.0ms";
490
			$status[$target]['loss'] = "100.0%";
491
			$status[$target]['status'] = "down";
492
		} else {
493
			$status[$target]['monitorip'] = $tgtip;
494
			$status[$target]['srcip'] = $srcip;
495
			$status[$target]['name'] = $gwitem['name'];
496
			$status[$target]['delay'] = "";
497
			$status[$target]['loss'] = "";
498
			$status[$target]['status'] = "none";
499
		}
500

    
501
		$status[$target]['monitor_disable'] = true;
502
	}
503
	return($status);
504
}
505

    
506
function return_gateways_status_text($byname = false, $brief = false) {
507
	$gwstat = return_gateways_status($byname);
508
	$output = "";
509
	$widths = array();
510
	$col_sep = 2;
511
	if ($brief) {
512
		$collist = array('status' => "Status");
513
	} else {
514
		$collist = array('monitorip' => "Monitor",
515
				'srcip' => "Source",
516
				'delay' => "Delay",
517
				'stddev' => "StdDev",
518
				'loss' => "Loss",
519
				'status' => "Status");
520
	}
521
	foreach ($gwstat as $gw) {
522
		foreach ($gw as $gwdidx => $gwdata) {
523
			if (strlen($gwdata) > $widths[$gwdidx]) {
524
				$widths[$gwdidx] = strlen($gwdata);
525
			}
526
		}
527
	}
528

    
529
	$output .= str_pad("Name", $widths['name'] + $col_sep, " ", STR_PAD_RIGHT);
530
	foreach ($collist as $hdrcol => $hdrdesc) {
531
		if (strlen($hdrdesc) > $widths[$hdrcol]) {
532
			$widths[$hdrcol] = strlen($hdrdesc);
533
		}
534
		$output .= str_pad($hdrdesc, $widths[$hdrcol] + $col_sep, " ", (substr($hdrcol, -2, 2) == "ip") ? STR_PAD_RIGHT : STR_PAD_LEFT);
535
	}
536
	$output .= "\n";
537

    
538
	foreach ($gwstat as $idx => $gw) {
539
		$output .= str_pad($gw['name'], $widths['name'] + $col_sep, " ", STR_PAD_RIGHT);
540
		foreach (array_keys($collist) as $col) {
541
			$output .= str_pad($gw[$col], $widths[$col] + $col_sep, " ", (substr($col, -2, 2) == "ip") ? STR_PAD_RIGHT : STR_PAD_LEFT);
542
		}
543
		$output .= "\n";
544
	}
545

    
546
	return $output;
547
}
548

    
549
function compare_gateway_order_configured($a, $b) {
550
	/* XXX WAN always has precedence */
551
	if ($a['friendlyiface'] == "wan") {
552
		return -1;
553
	} elseif ($b['friendlyiface'] == "wan") {
554
		return 1;
555
	}
556

    
557
	if ($a['attribute'] === $b['attribute']) {
558
		if ($a['attribute'] === 'system') {
559
			$res = (($a['name'] < $b['name'])) ? -1 : 1;
560
			return $res;
561
		}
562
		return 0;
563
	}
564
	if ($a['attribute'] === 'system' || $b['attribute'] === 'system') {
565
		$res = (($b['attribute'] === 'system')) ? -1 : 1;
566
		return $res;
567
	}
568
	$res = ($a['attribute'] < $b['attribute']) ? -1 : 1;
569
	return $res;
570
}
571

    
572
function order_gateways_as_configured($gateways_arr) {
573
	uasort($gateways_arr, 'compare_gateway_order_configured');
574
	return $gateways_arr;
575
}
576

    
577
/* Return all configured gateways on the system
578
   $disabled = true - include gateways that are disabled
579
   $localhost = true - include "Null" entries for localhost IP addresses
580
   $inactive = true - include gateways on inactive interfaces
581
   $integer_index = true - index the returned array by integers 0,1,2,... instead of by GW name
582
*/
583
function return_gateways_array($disabled = false, $localhost = false, $inactive = false, $integer_index = false) {
584
	global $config, $g;
585

    
586
	$gateways_arr = array();
587
	$gateways_arr_temp = array();
588
	$cgw4 = getcurrentdefaultgatewayip('inet');
589
	$cgw6 = getcurrentdefaultgatewayip('inet6');
590
	$found_defaultv4 = 0;
591
	$found_defaultv6 = 0;
592

    
593
	// Ensure the interface cache is up to date first
594
	$interfaces = get_interface_arr(true);
595

    
596
	$i = -1;
597
	/* Process/add all the configured gateways. */
598
	if (is_array($config['gateways']) && is_array($config['gateways']['gateway_item'])) {
599
		foreach ($config['gateways']['gateway_item'] as $gateway) {
600
			if (!is_array($gateway) || empty($gateway)) {
601
				continue;
602
			}
603
			
604
			/* Increment it here to do not skip items */
605
			$i++;
606
			if (isset($gateway['defaultgw'])) {
607
				unset($gateway['defaultgw']);
608
			}
609

    
610
			if (empty($config['interfaces'][$gateway['interface']])) {
611
				if ($inactive === false) {
612
					continue;
613
				} else {
614
					$gateway['inactive'] = true;
615
				}
616
			}
617
			$wancfg = $config['interfaces'][$gateway['interface']];
618

    
619
			/* skip disabled interfaces */
620
			if ($disabled === false && (!isset($wancfg['enable']))) {
621
				continue;
622
			}
623

    
624
			/* if the gateway is dynamic and we can find the IPv4, Great! */
625
			if (empty($gateway['gateway']) || $gateway['gateway'] == "dynamic") {
626
				if ($gateway['ipprotocol'] == "inet") {
627
					/* we know which interfaces is dynamic, this should be made a function */
628
					$gateway['gateway'] = get_interface_gateway($gateway['interface']);
629
					/* no IP address found, set to dynamic */
630
					if (!is_ipaddrv4($gateway['gateway'])) {
631
						$gateway['gateway'] = "dynamic";
632
					}
633
					$gateway['dynamic'] = true;
634
				}
635

    
636
				/* if the gateway is dynamic and we can find the IPv6, Great! */
637
				else if ($gateway['ipprotocol'] == "inet6") {
638
					/* we know which interfaces is dynamic, this should be made a function, and for v6 too */
639
					$gateway['gateway'] = get_interface_gateway_v6($gateway['interface']);
640
					/* no IPv6 address found, set to dynamic */
641
					if (!is_ipaddrv6($gateway['gateway'])) {
642
						$gateway['gateway'] = "dynamic";
643
					}
644
					$gateway['dynamic'] = true;
645
				}
646
			} else {
647
				/* getting this detection right is hard at this point because we still don't
648
				 * store the address family in the gateway item */
649
				if (is_ipaddrv4($gateway['gateway'])) {
650
					$gateway['ipprotocol'] = "inet";
651
				} else if (is_ipaddrv6($gateway['gateway'])) {
652
					$gateway['ipprotocol'] = "inet6";
653
				}
654
			}
655

    
656
			if (isset($gateway['monitor_disable'])) {
657
				$gateway['monitor_disable'] = true;
658
			} else if (empty($gateway['monitor'])) {
659
				$gateway['monitor'] = $gateway['gateway'];
660
			}
661

    
662
			if (isset($gateway['action_disable'])) {
663
				$gateway['action_disable'] = true;
664
			}
665

    
666
			$gateway['friendlyiface'] = $gateway['interface'];
667

    
668
			/* special treatment for tunnel interfaces */
669
			if ($gateway['ipprotocol'] == "inet6") {
670
				$gateway['interface'] = get_real_interface($gateway['interface'], "inet6", false, false);
671
			} else {
672
				$gateway['interface'] = get_real_interface($gateway['interface'], "inet", false, false);
673
			}
674

    
675
			if ($gateway['ipprotocol'] == "inet" &&
676
					($gateway['gateway'] == $cgw4)) {
677
				$gateway['isdefaultgw'] = true;
678
				$found_defaultv4 = 1;
679
			} else if ($gateway['ipprotocol'] == "inet6" &&
680
					($gateway['gateway'] == $cgw6)) {
681
				$gateway['isdefaultgw'] = true;
682
				$found_defaultv6 = 1;
683
			}
684
			/* include the gateway index as the attribute */
685
			$gateway['attribute'] = $i;
686

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

    
691
			/* skip disabled gateways if the caller has not asked for them to be returned. */
692
			if (!($disabled === false && isset($gateway['disabled']))) {
693
				$gateways_arr[$gateway['name']] = $gateway;
694
			}
695
		}
696
	}
697
	unset($gateway);
698

    
699
	//Sort the array by GW name before moving on.
700
	ksort($gateways_arr, SORT_STRING | SORT_FLAG_CASE);
701

    
702
	/* Loop through all interfaces with a gateway and add it to a array */
703
	if ($disabled == false) {
704
		$iflist = get_configured_interface_with_descr();
705
	} else {
706
		$iflist = get_configured_interface_with_descr(true);
707
	}
708

    
709
	/* Process/add dynamic v4 gateways. */
710
	foreach ($iflist as $ifname => $friendly) {
711
		if (!interface_has_gateway($ifname)) {
712
			continue;
713
		}
714

    
715
		if (empty($config['interfaces'][$ifname])) {
716
			continue;
717
		}
718

    
719
		$ifcfg = &$config['interfaces'][$ifname];
720
		if (!isset($ifcfg['enable'])) {
721
			continue;
722
		}
723

    
724
		if (!empty($ifcfg['ipaddr']) && is_ipaddrv4($ifcfg['ipaddr'])) {
725
			continue;
726
		}
727

    
728
		$ctype = "";
729
		switch ($ifcfg['ipaddr']) {
730
			case "dhcp":
731
			case "pppoe":
732
			case "l2tp":
733
			case "pptp":
734
			case "ppp":
735
				$ctype = strtoupper($ifcfg['ipaddr']);
736
				break;
737
			default:
738
				$tunnelif = substr($ifcfg['if'], 0, 3);
739
				if (substr($ifcfg['if'], 0, 4) == "ovpn") {
740
					switch (substr($ifcfg['if'], 4, 1)) {
741
						case "c":
742
							$ovpntype = "openvpn-client";
743
							break;
744
						case "s":
745
							$ovpntype = "openvpn-server";
746
							break;
747
						default:
748
							// unknown ovpn type
749
							continue 3;
750
					}
751
					$ovpnid = substr($ifcfg['if'], 5);
752
					if (is_array($config['openvpn'][$ovpntype])) {
753
						foreach ($config['openvpn'][$ovpntype] as & $ovpnconf) {
754
							if ($ovpnconf['vpnid'] == $ovpnid) {
755
								// skip IPv6-only interfaces
756
								if ($ovpnconf['create_gw'] == "v6only") {
757
									continue 3;
758
								}
759
								// skip tap interfaces
760
								if ($ovpnconf['dev_mode'] == "tap") {
761
									continue 3;
762
								}
763
							}
764
						}
765
					}
766
					$ctype = "VPNv4";
767
				} elseif (substr($ifcfg['if'], 0, 5) == "ipsec") {
768
					$ikeid = substr($ifcfg['if'], 5);
769
					if (is_array($config['ipsec']) && is_array($config['ipsec']['phase1']) && is_array($config['ipsec']['phase2'])) {
770
						foreach ($config['ipsec']['phase1'] as $ph1ent) {
771
							if ($ph1ent['disabled']) {
772
								continue;
773
							}
774
							$vtisubnet_spec = ipsec_vti($ph1ent, true);
775
							// Skip non-VTI tunnels
776
							if (!$vtisubnet_spec || !is_array($vtisubnet_spec)) {
777
								continue;
778
							}
779
							if (!isset($ph1ent['mobile']) && ($keyexchange == 'ikev1' || isset($ph1ent['splitconn']))) {
780
								foreach ($vtisubnet_spec as $idx => $vtisub) {
781
									if ($ifcfg['if'] == "ipsec{$ph1ent['ikeid']}00{$idx}") {
782
										// If this specific VTI remote is v4, then we can make a v4 gw
783
										if (is_ipaddrv4($vtisub['right'])) {
784
											$ctype = "VTIv4";
785
										}
786
									}
787
								}
788
							} else {
789
								if ($ifcfg['if'] == "ipsec{$ph1ent['ikeid']}000") {
790
									// If any of the VTI remotes are v4, then we can make a v4 gw
791
									foreach ($vtisubnet_spec as $vtisub) {
792
										if (is_ipaddrv4($vtisub['right'])) {
793
											$ctype = "VTIv4";
794
										}
795
									}
796
								}
797
							}
798
						}
799
						if (empty($ctype)) {
800
							continue 2;
801
						}
802
					}
803
				} elseif ($tunnelif == "gif" || $tunnelif == "gre") {
804
					$ctype = "TUNNELv4";
805
				}
806
				break;
807
		}
808
		$ctype = "_". strtoupper($ctype);
809

    
810
		$gateway = array();
811
		$gateway['dynamic'] = false;
812
		$gateway['ipprotocol'] = "inet";
813
		$gateway['gateway'] = get_interface_gateway($ifname, $gateway['dynamic']);
814
		$gateway['interface'] = get_real_interface($ifname);
815
		$gateway['friendlyiface'] = $ifname;
816
		$gateway['name'] = "{$friendly}{$ctype}";
817
		$gateway['attribute'] = "system";
818

    
819
		if (($gateway['dynamic'] === "default") && ($found_defaultv4 == 0)) {
820
			$gateway['isdefaultgw'] = true;
821
			$gateway['dynamic'] = true;
822
			$found_defaultv4 = 1;
823
		}
824

    
825
		/* Loopback dummy for dynamic interfaces without a IP */
826
		if (!is_ipaddrv4($gateway['gateway']) && $gateway['dynamic'] == true) {
827
			$gateway['gateway'] = "dynamic";
828
		}
829

    
830
		/* automatically skip known static and dynamic gateways that were previously processed */
831
		foreach ($gateways_arr_temp as $gateway_item) {
832
			if ((($ifname == $gateway_item['friendlyiface'] && $friendly == $gateway_item['name'])&& ($gateway['ipprotocol'] == $gateway_item['ipprotocol'])) ||
833
			    (($ifname == $gateway_item['friendlyiface'] && $gateway_item['dynamic'] == true) && ($gateway['ipprotocol'] == $gateway_item['ipprotocol']))) {
834
				continue 2;
835
			}
836
		}
837

    
838
		if (is_ipaddrv4($gateway['gateway'])) {
839
			$gateway['monitor'] = $gateway['gateway'];
840
		}
841

    
842
		$gateway['descr'] = "Interface {$friendly}{$ctype} Gateway";
843
		$gateways_arr[$gateway['name']] = $gateway;
844
	}
845
	unset($gateway);
846

    
847
	/* Process/add dynamic v6 gateways. */
848
	foreach ($iflist as $ifname => $friendly) {
849
		/* If the user has disabled IPv6, they probably don't want any IPv6 gateways. */
850
		if (!isset($config['system']['ipv6allow'])) {
851
			break;
852
		}
853

    
854
		if (!interface_has_gatewayv6($ifname)) {
855
			continue;
856
		}
857

    
858
		if (empty($config['interfaces'][$ifname])) {
859
			continue;
860
		}
861

    
862
		$ifcfg = &$config['interfaces'][$ifname];
863
		if (!isset($ifcfg['enable'])) {
864
			continue;
865
		}
866

    
867
		if (!empty($ifcfg['ipaddrv6']) && is_ipaddrv6($ifcfg['ipaddrv6'])) {
868
			continue;
869
		}
870

    
871
		$ctype = "";
872
		switch ($ifcfg['ipaddrv6']) {
873
			case "slaac":
874
			case "dhcp6":
875
			case "6to4":
876
			case "6rd":
877
				$ctype = strtoupper($ifcfg['ipaddrv6']);
878
				break;
879
			default:
880
				$tunnelif = substr($ifcfg['if'], 0, 3);
881
				if (substr($ifcfg['if'], 0, 4) == "ovpn") {
882
					switch (substr($ifcfg['if'], 4, 1)) {
883
						case "c":
884
							$ovpntype = "openvpn-client";
885
							break;
886
						case "s":
887
							$ovpntype = "openvpn-server";
888
							break;
889
						default:
890
							// unknown ovpn type
891
							continue 3;
892
					}
893
					$ovpnid = substr($ifcfg['if'], 5);
894
					if (is_array($config['openvpn'][$ovpntype])) {
895
						foreach ($config['openvpn'][$ovpntype] as & $ovpnconf) {
896
							if ($ovpnconf['vpnid'] == $ovpnid) {
897
								// skip IPv4-only interfaces
898
								if ($ovpnconf['create_gw'] == "v4only") {
899
									continue 3;
900
								}
901
								// skip tap interfaces
902
								if ($ovpnconf['dev_mode'] == "tap") {
903
									continue 3;
904
								}
905
							}
906
						}
907
					}
908
					$ctype = "VPNv6";
909
				} elseif (substr($ifcfg['if'], 0, 5) == "ipsec") {
910
					$ikeid = substr($ifcfg['if'], 5);
911
					if (is_array($config['ipsec']) && is_array($config['ipsec']['phase1']) && is_array($config['ipsec']['phase2'])) {
912
						foreach ($config['ipsec']['phase1'] as $ph1ent) {
913
							if ($ph1ent['disabled']) {
914
								continue;
915
							}
916
							$vtisubnet_spec = ipsec_vti($ph1ent, true);
917
							// Skip non-VTI tunnels
918
							if (!$vtisubnet_spec || !is_array($vtisubnet_spec)) {
919
								continue;
920
							}
921
							if (!isset($ph1ent['mobile']) && ($keyexchange == 'ikev1' || isset($ph1ent['splitconn']))) {
922
								foreach ($vtisubnet_spec as $idx => $vtisub) {
923
									if ($ifcfg['if'] == "ipsec{$ph1ent['ikeid']}00{$idx}") {
924
										// If this specific VTI remote is v6, then we can make a v6 gw
925
										if (is_ipaddrv6($vtisub['right'])) {
926
											$ctype = "VTIv6";
927
										}
928
									}
929
								}
930
							} else {
931
								if ($ifcfg['if'] == "ipsec{$ph1ent['ikeid']}000") {
932
									// If any of the VTI remotes are v6, then we can make a v6 gw
933
									foreach ($vtisubnet_spec as $vtisub) {
934
										if (is_ipaddrv6($vtisub['right'])) {
935
											$ctype = "VTIv6";
936
										}
937
									}
938
								}
939
							}
940
						}
941
						if (empty($ctype)) {
942
							continue 2;
943
						}
944
					}
945
				} else if ($tunnelif == "gif" || $tunnelif == "gre") {
946
					$ctype = "TUNNELv6";
947
				}
948
				break;
949
		}
950
		$ctype = "_". strtoupper($ctype);
951

    
952
		$gateway = array();
953
		$gateway['dynamic'] = false;
954
		$gateway['ipprotocol'] = "inet6";
955
		$gateway['gateway'] = get_interface_gateway_v6($ifname, $gateway['dynamic']);
956
		$gateway['interface'] = get_real_interface($ifname, "inet6");
957
		switch ($ifcfg['ipaddrv6']) {
958
			case "6rd":
959
			case "6to4":
960
				$gateway['dynamic'] = "default";
961
				break;
962
		}
963
		$gateway['friendlyiface'] = $ifname;
964
		$gateway['name'] = "{$friendly}{$ctype}";
965
		$gateway['attribute'] = "system";
966

    
967
		if (($gateway['dynamic'] === "default") && ($found_defaultv6 == 0)) {
968
			$gateway['isdefaultgw'] = true;
969
			$gateway['dynamic'] = true;
970
			$found_defaultv6 = 1;
971
		}
972

    
973
		/* Loopback dummy for dynamic interfaces without a IP */
974
		if (!is_ipaddrv6($gateway['gateway']) && $gateway['dynamic'] == true) {
975
			$gateway['gateway'] = "dynamic";
976
		}
977

    
978
		/* automatically skip known static and dynamic gateways that were previously processed */
979
		foreach ($gateways_arr_temp as $gateway_item) {
980
			if ((($ifname == $gateway_item['friendlyiface'] && $friendly == $gateway_item['name']) && ($gateway['ipprotocol'] == $gateway_item['ipprotocol'])) ||
981
			    (($ifname == $gateway_item['friendlyiface'] && $gateway_item['dynamic'] == true) && ($gateway['ipprotocol'] == $gateway_item['ipprotocol']))) {
982
				continue 2;
983
			}
984
		}
985

    
986
		if (is_ipaddrv6($gateway['gateway'])) {
987
			$gateway['monitor'] = $gateway['gateway'];
988
		}
989

    
990
		$gateway['descr'] = "Interface {$friendly}{$ctype} Gateway";
991
		$gateways_arr[$gateway['name']] = $gateway;
992
	}
993
	unset($gateway);
994

    
995
	/* FIXME: Should this be enabled.
996
	 * Some interface like wan might be default but have no info recorded
997
	 * the config. */
998
	/* this is a fallback if all else fails and we want to get packets out @smos */
999
	if ($found_defaultv4 == 0 || $found_defaultv6 == 0) {
1000
		foreach ($gateways_arr as &$gateway) {
1001
			if (($gateway['friendlyiface'] == "wan") && ($found_defaultv4 == 0) && (!isset($gateway['ipprotocol']) || ($gateway['ipprotocol'] == "inet"))) {
1002
				if (file_exists("{$g['tmp_path']}/{$gateway['interface']}_defaultgw")) {
1003
					$gateway['isdefaultgw'] = true;
1004
					$found_defaultv4 = 1;
1005
				}
1006
			}
1007
			else if (($gateway['friendlyiface'] == "wan") && ($found_defaultv6 == 0) && ($gateway['ipprotocol'] == "inet6")) {
1008
				if (file_exists("{$g['tmp_path']}/{$gateway['interface']}_defaultgwv6")) {
1009
					$gateway['isdefaultgw'] = true;
1010
					$found_defaultv6 = 1;
1011
				}
1012
			}
1013
		}
1014
	}
1015

    
1016
	if ($localhost === true) {
1017
		/* attach localhost for Null routes */
1018
		$gwlo4 = array();
1019
		$gwlo4['name'] = "Null4";
1020
		$gwlo4['interface'] = "lo0";
1021
		$gwlo4['ipprotocol'] = "inet";
1022
		$gwlo4['gateway'] = "127.0.0.1";
1023
		$gwlo4['attribute'] = "system";
1024
		$gwlo6 = array();
1025
		$gwlo6['name'] = "Null6";
1026
		$gwlo6['interface'] = "lo0";
1027
		$gwlo6['ipprotocol'] = "inet6";
1028
		$gwlo6['gateway'] = "::1";
1029
		$gwlo6['attribute'] = "system";
1030
		$gateways_arr['Null4'] = $gwlo4;
1031
		$gateways_arr['Null6'] = $gwlo6;
1032
	}
1033

    
1034
	if ($integer_index) {
1035
		$gateways_arr = array_values($gateways_arr);
1036
	}
1037

    
1038
	if ($found_defaultv4 != 1 && is_ipaddr($cgw4)) {
1039
		foreach($gateways_arr as &$gw) {
1040
			if ($gw['gateway'] == $cgw4) {
1041
				$gw['isdefaultgw'] = true;
1042
			}
1043
		}
1044
	}
1045
	if ($found_defaultv6 != 1 && is_ipaddr($cgw6)) {
1046
		foreach($gateways_arr as &$gw) {
1047
			if ($gw['gateway'] == $cgw6) {
1048
				$gw['isdefaultgw'] = true;
1049
			}
1050
		}
1051
	}
1052
	return order_gateways_as_configured($gateways_arr);
1053
}
1054

    
1055
function fixup_default_gateway($ipprotocol, $gateways_status, $gateways_arr) {
1056
	global $config, $g;
1057
	/*
1058
	 * NOTE: The code below is meant to replace the default gateway when it goes down.
1059
	 *	This facilitates services running on pfSense itself and are not handled by a PBR to continue working.
1060
	 */
1061
	$set_dfltgwname = '';
1062

    
1063
	if ($ipprotocol == 'inet') {
1064
		$gwdefault = $config['gateways']['defaultgw4'];
1065
	} else {
1066
		$gwdefault = $config['gateways']['defaultgw6'];
1067
	}
1068
	if ($gwdefault == "-") {
1069
		// 'none', dont set the default gateway, useful if routes are managed by frr/bgp/ospf or similar
1070
		return;
1071
	}
1072
	if (isset($gateways_arr[$gwdefault])) {
1073
		// the configured gateway is a regular one. (not a gwgroup) use it as is..
1074
		$set_dfltgwname = $gwdefault;
1075
	} elseif (empty($gwdefault)) {
1076
		// 'automatic' mode, pick the first one thats 'up' or 'unmonitored' which is always considered up
1077
		$gateways_arr = order_gateways_as_configured($gateways_arr);
1078
		$fallback = "";
1079
		foreach($gateways_arr as $gwname => $gwsttng) {
1080
			if ($gwsttng['ipprotocol'] != $ipprotocol) {
1081
				continue;
1082
			}
1083
			
1084
			if ((isset($gwsttng['monitor_disable']) || isset($gwsttng['action_disable']) || $gateways_status[$gwname]['status'] == "none")) {
1085
				$set_dfltgwname = $gwname;
1086
				break;
1087
			}
1088
			if (empty($fallback) && $gwsttng['interface'] != 'lo0') {
1089
				$fallback = $gwname;
1090
			}
1091
		}
1092
		if (empty($set_dfltgwname)) {
1093
			log_error(sprintf("Gateway, none 'available' for %s, use the first one configured. '%s'", $ipprotocol, $fallback));
1094
			$set_dfltgwname = $fallback;
1095
		}
1096
	} else {
1097
		// a gwgroup is selected
1098
		// find the best available gateway given options available..
1099
		$gwg_members = array();
1100
		$viplist = get_configured_vip_list();
1101
		if (is_array($config['gateways']['gateway_group'])) {
1102
			foreach ($config['gateways']['gateway_group'] as $group) {
1103
				if ($group['name'] == $gwdefault) {
1104
					// finds the gw members of the best available tier for this group.
1105
					$gwg_members = get_gwgroup_members_inner($group, $gateways_status, $gateways_arr, $viplist);
1106
				}
1107
			}
1108
		}
1109

    
1110
		if (count($gwg_members) > 0) {
1111
			$currentdefaultgwip = getcurrentdefaultgatewayip($ipprotocol);
1112
			$found_current = false;
1113
			foreach($gwg_members as $gwgroupitem) {
1114
				if (!empty($currentdefaultgwip) && ($gwgroupitem['gwip'] == $currentdefaultgwip)) {
1115
					$set_dfltgwname = $gwgroupitem['gw'];
1116
					$found_current = true;
1117
					if (isset($config['system']['gw-debug'])) {
1118
						log_error("Keep current gateway, its already part of the group members.");
1119
					}
1120
					break;
1121
				}
1122
			}
1123
			if (!$found_current) {
1124
				$set_dfltgwname = $gwg_members[0]['gw'];
1125
				log_error(sprintf("Gateway, switch to: %s", $set_dfltgwname));
1126
			}
1127
		} else {
1128
			log_error("Gateway, NONE AVAILABLE");
1129
		}
1130
	}
1131
	if (!empty($set_dfltgwname) && isset($gateways_arr[$set_dfltgwname])) {
1132
		setdefaultgateway($gateways_arr[$set_dfltgwname]);
1133
	}
1134
}
1135

    
1136
function getcurrentdefaultgatewayip($ipprotocol) {
1137
	return trim(exec("/sbin/route -n get -{$ipprotocol} default 2>/dev/null | /usr/bin/awk '/gateway:/ {print $2}'"), " \n");
1138
}
1139

    
1140
function setdefaultgateway($gw) {
1141
	global $g, $config;
1142
	if (isset($config['system']['route-debug'])) {
1143
		file_put_contents("/dev/console", "\n[".getmypid()."] SET DEF GW: {$gw['name']}");
1144
	}
1145
	$ipprotocol = $gw['ipprotocol'];
1146
	if ($gw['gateway'] == "dynamic") {
1147
		if ($ipprotocol == 'inet') {
1148
			$gw['gateway'] = get_interface_gateway($gw['friendlyiface']);
1149
		} else {
1150
			$gw['gateway'] = get_interface_gateway_v6($$gw['friendlyiface']);
1151
		}
1152
	}
1153
	if ($ipprotocol == 'inet6' && !is_ipaddrv6($gw['gateway'])) {
1154
		return;
1155
	}
1156
	if ($ipprotocol == 'inet' && !is_ipaddrv4($gw['gateway'])) {
1157
		return;
1158
	}
1159
	if ($ipprotocol == 'inet6') {
1160
		if (is_linklocal($gw['gateway']) && get_ll_scope($gw['gateway']) == '') {
1161
			$gw['gateway'] .= "%" . $gw['interface'];
1162
		}
1163
	}
1164
	$currentdefaultgwip = getcurrentdefaultgatewayip($ipprotocol);
1165
	if ($currentdefaultgwip != $gw['gateway']) {
1166
		log_error("Default gateway setting {$gw['descr']} as default.");
1167

    
1168
		if ($ipprotocol == 'inet') {
1169
			array_map('unlink', glob("{$g['tmp_path']}/*_defaultgw", GLOB_BRACE));
1170
		} else {
1171
			array_map('unlink', glob("{$g['tmp_path']}/*_defaultgwv6", GLOB_BRACE));
1172
		}
1173
		$defaultif = get_real_interface($gw['interface']);
1174
		if ($defaultif) {
1175
			@file_put_contents("{$g['tmp_path']}/{$defaultif}_defaultgw", $gw['gateway']);
1176
		}
1177

    
1178
		if (isset($gw["nonlocalgateway"])) {
1179
			if (is_ipaddr($gw['gateway']) && !empty($gw['interface'])) {
1180
				route_add_or_change("-{$ipprotocol} {$gw['gateway']} -iface {$gw['interface']}");
1181
			}
1182
		}
1183
		if (isset($config['system']['route-debug'])) {
1184
			file_put_contents("/dev/console", "\n[".getmypid()."] SET DEF GW: {$gw['name']} ({$gw['gateway']})");
1185
		}
1186
		route_add_or_change("-{$ipprotocol} default {$gw['gateway']}");
1187
		return true;
1188
	}
1189
}
1190

    
1191
function get_gwgroup_members_inner($group, $gateways_status, $gateways_arr, $viplist){
1192
	$result = array();
1193
	/* create array with group gateways members separated by tier */
1194
	$tiers = array();
1195
	$backupplan = array();
1196
	$gwvip_arr = array();
1197
	foreach ($group['item'] as $item) {
1198
		list($gwname, $tier, $vipname) = explode("|", $item);
1199

    
1200
		if (is_ipaddr($viplist[$vipname])) {
1201
			if (!is_array($gwvip_arr[$group['name']])) {
1202
				$gwvip_arr[$group['name']] = array();
1203
			}
1204
			$gwvip_arr[$group['name']][$gwname] = $vipname;
1205
		}
1206

    
1207
		/* Do it here rather than reiterating again the group in case no member is up. */
1208
		if (!is_array($backupplan[$tier])) {
1209
			$backupplan[$tier] = array();
1210
		}
1211
		$backupplan[$tier][] = $gwname;
1212

    
1213
		/* check if the gateway is available before adding it to the array */
1214
		if (is_array($gateways_status[$gwname])) {
1215
			$status = $gateways_status[$gwname];
1216
			$gwdown = false;
1217
			if (stristr($status['status'], "down")) {
1218
				$msg = sprintf(gettext('MONITOR: %1$s is down, omitting from routing group %2$s'), $gwname, $group['name']);
1219
				$gwdown = true;
1220
			} else if (stristr($status['status'], "loss") && strstr($group['trigger'], "loss")) {
1221
				/* packet loss */
1222
				$msg = sprintf(gettext('MONITOR: %1$s has packet loss, omitting from routing group %2$s'), $gwname, $group['name']);
1223
				$gwdown = true;
1224
			} else if (stristr($status['status'], "delay") && strstr($group['trigger'] , "latency")) {
1225
				/* high latency */
1226
				$msg = sprintf(gettext('MONITOR: %1$s has high latency, omitting from routing group %2$s'), $gwname, $group['name']);
1227
				$gwdown = true;
1228
			}
1229
			$statuschanged = false;
1230
			$pluginparams = array();
1231
			$pluginparams['type'] = 'gateway';
1232
			$pluginparams['name'] = $gwname;
1233
			if ($gwdown == true) {
1234
				if (!file_exists("/tmp/.down.{$gwname}")) {
1235
					@touch("/tmp/.down.{$gwname}");
1236
					$msg .= "\n".implode("|", $status);
1237
					$pluginparams['event'] = 'gateway.down';
1238
					$statuschanged = true;
1239
				}
1240
			} else {
1241
				/* Online add member */
1242
				if (!is_array($tiers[$tier])) {
1243
					$tiers[$tier] = array();
1244
				}
1245
				$tiers[$tier][] = $gwname;
1246
				if (unlink_if_exists("/tmp/.down.{$gwname}")) {
1247
					$msg = sprintf(getmypid () . gettext('MONITOR: %1$s is available now, adding to routing group %2$s'), $gwname, $group['name']);
1248
					$msg .= "\n".implode("|", $status);
1249
					$pluginparams['event'] = 'gateway.up';
1250
					$statuschanged = true;
1251
				}
1252
			}
1253
			if ($statuschanged) {
1254
				log_error($msg);
1255
				notify_via_smtp($msg);
1256
				if (isset($gateways_arr[$gwname]['interface'])) {
1257
					$pluginparams['interface'] = $gateways_arr[$gwname]['interface'];
1258
				}
1259
				pkg_call_plugins('plugin_gateway', $pluginparams);
1260
			}
1261
		} else if (isset($gateways_arr[$gwname]['monitor_disable']) || isset($gateways_arr[$gwname]['action_disable'])) {
1262
			$tiers[$tier][] = $gwname;
1263
		}
1264
	}
1265
	$tiers_count = count($tiers);
1266
	if ($tiers_count == 0) {
1267
		/* Oh dear, we have no members! Engage Plan B */
1268
		if (isset($config['system']['gw-debug']) && (!platform_booting())) {
1269
			$msg = sprintf(gettext('Gateways status could not be determined, considering all as up/active. (Group: %s)'), $group['name']);
1270
			log_error($msg);
1271
		}
1272
		$tiers = $backupplan;
1273
	}
1274
	/* sort the tiers array by the tier key */
1275
	ksort($tiers);
1276

    
1277
	/* we do not really foreach the tiers as we stop after the first tier */
1278
	foreach ($tiers as $tieridx => $tier) {
1279
		/* process all gateways in this tier */
1280
		foreach ($tier as $member) {
1281
			/* determine interface gateway */
1282
			if (isset($gateways_arr[$member])) {
1283
				$gateway = $gateways_arr[$member];
1284
				$int = $gateway['interface'];
1285
				$gatewayip = "";
1286
				if (is_ipaddr($gateway['gateway'])) {
1287
					$gatewayip = $gateway['gateway'];
1288
				} else if (!empty($int)) {
1289
					$gatewayip = get_interface_gateway($gateway['friendlyiface']);
1290
				}
1291

    
1292
				if (!empty($int)) {
1293
					$result['ipprotocol'] = $gateway['ipprotocol'];
1294
					if (is_ipaddr($gatewayip)) {
1295
						$groupmember = array();
1296
						$groupmember['gw'] = $member;
1297
						$groupmember['int'] = $int;
1298
						$groupmember['gwip'] = $gatewayip;
1299
						$groupmember['weight'] = isset($gateway['weight']) ? $gateway['weight'] : 1;
1300
						if (is_array($gwvip_arr[$group['name']]) && !empty($gwvip_arr[$group['name']][$member])) {
1301
							$groupmember['vip'] = $gwvip_arr[$group['name']][$member];
1302
						}
1303
						$result[] = $groupmember;
1304
					}
1305
				}
1306
			}
1307
		}
1308
		/* we should have the 1st available tier now, exit stage left */
1309
		if (count($result) > 0) {
1310
			break;
1311
		} else {
1312
			log_error(sprintf(gettext('GATEWAYS: Group %1$s did not have any gateways up on tier %2$s!'), $group['name'], $tieridx));
1313
		}
1314
	}
1315
	// Add description field last to not influence the count() above
1316
	$result['descr'] = $group['descr'];
1317
	return $result;
1318
}
1319

    
1320
function get_gwgroup_members($groupname) {
1321
	global $config;
1322
	$gateways_status = return_gateways_status(true);
1323
	$gateways_arr = return_gateways_array();
1324
	$viplist = get_configured_vip_list();
1325
	foreach ($config['gateways']['gateway_group'] as $group) {
1326
		if ($group['name'] == $groupname) {
1327
			return get_gwgroup_members_inner($group, $gateways_status, $gateways_arr, $viplist);
1328
		}
1329
	}
1330
	return array();
1331
}
1332

    
1333
/*
1334
 * Return an array with all gateway groups with name as key
1335
 * All gateway groups will be processed before returning the array.
1336
 */
1337
function return_gateway_groups_array($fixup = false) {
1338
	global $config;
1339

    
1340
	/* fetch the current gateways status */
1341
	$gateways_status = return_gateways_status(true);
1342
	$gateways_arr = return_gateways_array();
1343
	$gateway_groups_array = array();
1344
	if ($fixup == true) {
1345
		$gw4 = lookup_gateway_or_group_by_name($config['gateways']['defaultgw4']);
1346
		$gw6 = lookup_gateway_or_group_by_name($config['gateways']['defaultgw6']);
1347
		if ($gw4 && $gw4['type'] == 'gatewaygroup') {
1348
			fixup_default_gateway("inet", $gateways_status, $gateways_arr);
1349
		}
1350
		if ($gw6 && $gw6['type'] == 'gatewaygroup') {
1351
			fixup_default_gateway("inet6", $gateways_status, $gateways_arr);
1352
		}
1353
	}
1354
	if (is_array($config['gateways']['gateway_group'])) {
1355
		$viplist = get_configured_vip_list();
1356
		foreach ($config['gateways']['gateway_group'] as $group) {
1357
			$gateway_groups_array[$group['name']] = get_gwgroup_members_inner($group, $gateways_status, $gateways_arr, $viplist);
1358
		}
1359
	}
1360

    
1361
	return ($gateway_groups_array);
1362
}
1363

    
1364
/* Update DHCP WAN Interface ip address in gateway group item */
1365
function dhclient_update_gateway_groups_defaultroute($interface = "wan") {
1366
	global $config;
1367

    
1368
	if (is_array($config['gateways']['gateway_item'])) {
1369
		foreach ($config['gateways']['gateway_item'] as & $gw) {
1370
			if ($gw['interface'] != $interface) {
1371
				continue;
1372
			}
1373

    
1374
			$current_gw = get_interface_gateway($interface);
1375
			if ($gw['gateway'] <> $current_gw) {
1376
				$gw['gateway'] = $current_gw;
1377
				$changed = true;
1378
			}
1379
		}
1380
	}
1381

    
1382
	if ($changed && $current_gw) {
1383
		write_config(sprintf(gettext(
1384
		    'Updating gateway group gateway for %1$s - new gateway is %2$s'),
1385
		    $interface, $current_gw));
1386
	}
1387
}
1388

    
1389
function lookup_gateway_or_group_by_name($gwname) {
1390
	global $config;
1391

    
1392
	$gateways_arr = return_gateways_array();
1393
	foreach ($gateways_arr as $gw) {
1394
		if ($gw['name'] == $gwname) {
1395
			$gw['type'] = 'gateway';
1396
			return $gw;
1397
		}
1398
	}
1399

    
1400
	if (is_array($config['gateways']['gateway_group'])) {
1401
		foreach ($config['gateways']['gateway_group'] as $gwg) {
1402
			if ($gwg['name'] == $gwname) {
1403
				$gwg['type'] = 'gatewaygroup';
1404
				return $gwg;
1405
			}
1406
		}
1407
	}
1408

    
1409
	return false;
1410
}
1411

    
1412
function lookup_gateway_ip_by_name($name, $disabled = false) {
1413

    
1414
	$gateways_arr = return_gateways_array($disabled, true);
1415
	foreach ($gateways_arr as $gname => $gw) {
1416
		if ($gw['name'] === $name || $gname === $name) {
1417
			return $gw['gateway'];
1418
		}
1419
	}
1420

    
1421
	return false;
1422
}
1423

    
1424
function lookup_gateway_monitor_ip_by_name($name) {
1425

    
1426
	$gateways_arr = return_gateways_array(false, true);
1427
	if (!empty($gateways_arr[$name])) {
1428
		$gateway = $gateways_arr[$name];
1429
		if (!is_ipaddr($gateway['monitor'])) {
1430
			return $gateway['gateway'];
1431
		}
1432

    
1433
		return $gateway['monitor'];
1434
	}
1435

    
1436
	return (false);
1437
}
1438

    
1439
function lookup_gateway_interface_by_name($name) {
1440

    
1441
	$gateways_arr = return_gateways_array(false, true);
1442
	if (!empty($gateways_arr[$name])) {
1443
		$interfacegw = $gateways_arr[$name]['friendlyiface'];
1444
		return ($interfacegw);
1445
	}
1446

    
1447
	return (false);
1448
}
1449

    
1450
function get_root_interface($interface) {
1451
	if (substr($interface, 0, 4) == '_vip') {
1452
		$interface = get_configured_vip_interface($interface);
1453
		if (substr($interface, 0, 4) == '_vip') {
1454
			$interface = get_configured_vip_interface($interface);
1455
		}
1456
	}
1457
	return $interface;
1458
}
1459

    
1460
function get_interface_gateway($interface, &$dynamic = false) {
1461
	global $config, $g;
1462
	
1463
	$interface = get_root_interface($interface);
1464

    
1465
	$gw = NULL;
1466
	$gwcfg = $config['interfaces'][$interface];
1467
	if (!empty($gwcfg['gateway']) && is_array($config['gateways']['gateway_item'])) {
1468
		foreach ($config['gateways']['gateway_item'] as $gateway) {
1469
			if (($gateway['name'] == $gwcfg['gateway']) && (is_ipaddrv4($gateway['gateway']))) {
1470
				$gw = $gateway['gateway'];
1471
				break;
1472
			}
1473
		}
1474
	}
1475

    
1476
	// for dynamic interfaces we handle them through the $interface_router file.
1477
	if (($gw == NULL || !is_ipaddrv4($gw)) && !is_ipaddrv4($gwcfg['ipaddr'])) {
1478
		$realif = get_real_interface($interface);
1479
		if (file_exists("{$g['tmp_path']}/{$realif}_router")) {
1480
			$gw = trim(file_get_contents("{$g['tmp_path']}/{$realif}_router"), " \n");
1481
			$dynamic = true;
1482
		}
1483
		if (file_exists("{$g['tmp_path']}/{$realif}_defaultgw")) {
1484
			$dynamic = "default";
1485
		}
1486

    
1487
	}
1488

    
1489
	/* return gateway */
1490
	return ($gw);
1491
}
1492

    
1493
function get_interface_gateway_v6($interface, &$dynamic = false) {
1494
	global $config, $g;
1495

    
1496
	$interface = get_root_interface($interface);
1497

    
1498
	$gw = NULL;
1499
	$gwcfg = $config['interfaces'][$interface];
1500
	if (!empty($gwcfg['gatewayv6']) && is_array($config['gateways']['gateway_item'])) {
1501
		foreach ($config['gateways']['gateway_item'] as $gateway) {
1502
			if (($gateway['name'] == $gwcfg['gatewayv6']) && (is_ipaddrv6($gateway['gateway']))) {
1503
				$gw = $gateway['gateway'];
1504
				break;
1505
			}
1506
		}
1507
	}
1508

    
1509
	// for dynamic interfaces we handle them through the $interface_router file.
1510
	if (($gw == NULL || !is_ipaddrv6($gw)) && !is_ipaddrv6($gwcfg['ipaddrv6'])) {
1511
		$realif = get_real_interface($interface);
1512
		if (file_exists("{$g['tmp_path']}/{$realif}_routerv6")) {
1513
			$gw = trim(file_get_contents("{$g['tmp_path']}/{$realif}_routerv6"), " \n");
1514
			$dynamic = true;
1515
		}
1516
		if (file_exists("{$g['tmp_path']}/{$realif}_defaultgwv6")) {
1517
			$dynamic = "default";
1518
		}
1519
	}
1520
	/* return gateway */
1521
	return ($gw);
1522
}
1523

    
1524
/* Check a IP address against a gateway IP or name
1525
 * to verify it's address family */
1526
function validate_address_family($ipaddr, $gwname, $disabled = false) {
1527
	$v4ip = false;
1528
	$v6ip = false;
1529
	$v4gw = false;
1530
	$v6gw = false;
1531

    
1532
	if (is_ipaddrv4($ipaddr)) {
1533
		$v4ip = true;
1534
	}
1535
	if (is_ipaddrv6($ipaddr)) {
1536
		$v6ip = true;
1537
	}
1538
	if (is_ipaddrv4($gwname)) {
1539
		$v4gw = true;
1540
	}
1541
	if (is_ipaddrv6($gwname)) {
1542
		$v6gw = true;
1543
	}
1544

    
1545
	if ($v4ip && $v4gw) {
1546
		return true;
1547
	}
1548
	if ($v6ip && $v6gw) {
1549
		return true;
1550
	}
1551

    
1552
	/* still no match, carry on, lookup gateways */
1553
	if (is_ipaddrv4(lookup_gateway_ip_by_name($gwname, $disabled))) {
1554
		$v4gw = true;
1555
	}
1556
	if (is_ipaddrv6(lookup_gateway_ip_by_name($gwname, $disabled))) {
1557
		$v6gw = true;
1558
	}
1559

    
1560
	$gw_array = return_gateways_array();
1561
	if (is_array($gw_array[$gwname])) {
1562
		switch ($gw_array[$gwname]['ipprotocol']) {
1563
			case "inet":
1564
				$v4gw = true;
1565
				break;
1566
			case "inet6":
1567
				$v6gw = true;
1568
				break;
1569
		}
1570
	}
1571

    
1572
	if ($v4ip && $v4gw) {
1573
		return true;
1574
	}
1575
	if ($v6ip && $v6gw) {
1576
		return true;
1577
	}
1578

    
1579
	return false;
1580
}
1581

    
1582
/* check if a interface is part of a gateway group */
1583
function interface_gateway_group_member($interface, $gwgroup_name = "") {
1584
	global $config;
1585

    
1586
	if (is_array($config['gateways']['gateway_group'])) {
1587
		$groups = $config['gateways']['gateway_group'];
1588
	} else {
1589
		return false;
1590
	}
1591

    
1592
	$gateways_arr = return_gateways_array(false, true);
1593
	foreach ($groups as $group) {
1594
		if (is_array($group['item'])) {
1595
			foreach ($group['item'] as $item) {
1596
				$elements = explode("|", $item);
1597
				$gwname = $elements[0];
1598
				if ($interface == $gateways_arr[$gwname]['interface'] &&
1599
				    (empty($gwgroup_name) || $gwgroup_name == $group['name'])) {
1600
					unset($gateways_arr);
1601
					return true;
1602
				}
1603
			}
1604
		}
1605
	}
1606
	unset($gateways_arr);
1607

    
1608
	return false;
1609
}
1610

    
1611
function gateway_is_gwgroup_member($name, $detail=false) {
1612
	global $config;
1613

    
1614
	if (is_array($config['gateways']['gateway_group'])) {
1615
		$groups = $config['gateways']['gateway_group'];
1616
	} else {
1617
		return false;
1618
	}
1619

    
1620
	$members = array();
1621
	foreach ($groups as $group) {
1622
		if (is_array($group['item'])) {
1623
			foreach ($group['item'] as $item) {
1624
				list($gwname, $tier, $vipname) = explode("|", $item);
1625
				if ($name == $gwname) {
1626
					if ($detail) {
1627
						$newitem = array();
1628
						$newitem['name'] = $group['name'];
1629
						$newitem['tier'] = $tier;
1630
						$newitem['vipname'] = $vipname;
1631
						$members[] = $newitem;
1632
					} else {
1633
						$members[] = $group['name'];
1634
					}
1635
				}
1636
			}
1637
		}
1638
	}
1639

    
1640
	return $members;
1641
}
1642
/*
1643
  Check the proposed gateway settings to see if they are valid.
1644
  $gateway_settings - the proposed array of proposed gateway settings
1645
  $id - the index of the gateway proposed to be modified (otherwise "" if adding a new gateway)
1646
  $parent_ip - the IP (v4 or v6) address about to be set on the corresponding interface (if any)
1647
  $parent_sn - the subnet about to be set on the corresponding interface (if any)
1648
  (Note: the above 2 parameters allow gateway parameters to be validated concurrently with saving
1649
   an interface, before the new interface parameters are actually saved in the config.)
1650
  Return completed $input_errors array if there is any problem.
1651
  Otherwise return an empty $input_errors array
1652
*/
1653
function validate_gateway($gateway_settings, $id = "", $parent_ip = "", $parent_sn = "") {
1654
	global $config;
1655

    
1656
	$a_gateways = return_gateways_array(true, false, true, true);
1657
	$input_errors = array();
1658

    
1659
	/* input validation */
1660
	$reqdfields = explode(" ", "name interface");
1661
	$reqdfieldsn = array(gettext("Name"), gettext("Interface"));
1662

    
1663
	do_input_validation($gateway_settings, $reqdfields, $reqdfieldsn, $input_errors);
1664

    
1665
	if (!isset($gateway_settings['name'])) {
1666
		$input_errors[] = "A valid gateway name must be specified.";
1667
	}
1668
	if (!is_validaliasname($gateway_settings['name'])) {
1669
		$input_errors[] = invalidaliasnamemsg($gateway_settings['name'], gettext("gateway"));
1670
	} else if (isset($gateway_settings['disabled'])) {
1671
		// We have a valid gateway name that the user wants to mark as disabled.
1672
		// Check if the gateway name is used in any gateway group.
1673
		if (is_array($config['gateways']['gateway_group'])) {
1674
			foreach ($config['gateways']['gateway_group'] as $group) {
1675
				foreach ($group['item'] as $item) {
1676
					$items = explode("|", $item);
1677
					if ($items[0] == $gateway_settings['name']) {
1678
						$input_errors[] = sprintf(gettext('Gateway "%1$s" cannot be disabled because it is in use on Gateway Group "%2$s"'), $gateway_settings['name'], $group['name']);
1679
					}
1680
				}
1681
			}
1682
		}
1683

    
1684
		// Check if the gateway name is used in any enabled Static Route.
1685
		if (is_array($config['staticroutes']['route'])) {
1686
			foreach ($config['staticroutes']['route'] as $route) {
1687
				if ($route['gateway'] == $gateway_settings['name']) {
1688
					if (!isset($route['disabled'])) {
1689
						// There is a static route that uses this gateway and is enabled (not disabled).
1690
						$input_errors[] = sprintf(gettext('Gateway "%1$s" cannot be disabled because it is in use on Static Route "%2$s"'), $gateway_settings['name'], $route['network']);
1691
					}
1692
				}
1693
			}
1694
		}
1695
	}
1696
	/* skip system gateways which have been automatically added */
1697
	if (($gateway_settings['gateway'] && (!is_ipaddr($gateway_settings['gateway'])) && ($gateway_settings['attribute'] !== "system")) && ($gateway_settings['gateway'] != "dynamic")) {
1698
		$input_errors[] = gettext("A valid gateway IP address must be specified.");
1699
	}
1700

    
1701
	if ($gateway_settings['gateway'] && is_ipaddr($gateway_settings['gateway'])) {
1702
		if (is_ipaddrv4($gateway_settings['gateway'])) {
1703
			if ($parent_ip == '') {
1704
				$parent_ip = get_interface_ip($gateway_settings['interface']);
1705
				$parent_sn = get_interface_subnet($gateway_settings['interface']);
1706
			}
1707
			if (empty($parent_ip) || empty($parent_sn)) {
1708
				$input_errors[] = gettext("Cannot add IPv4 Gateway Address because no IPv4 address could be found on the interface.");
1709
			} elseif (!isset($gateway_settings["nonlocalgateway"])) {
1710
				$subnets = array(gen_subnet($parent_ip, $parent_sn) . "/" . $parent_sn);
1711
				$vips = link_interface_to_vips($gateway_settings['interface']);
1712
				if (is_array($vips)) {
1713
					foreach ($vips as $vip) {
1714
						if (!is_ipaddrv4($vip['subnet'])) {
1715
							continue;
1716
						}
1717
						$subnets[] = gen_subnet($vip['subnet'], $vip['subnet_bits']) . "/" . $vip['subnet_bits'];
1718
					}
1719
				}
1720

    
1721
				$found = false;
1722
				foreach ($subnets as $subnet) {
1723
					if (ip_in_subnet($gateway_settings['gateway'], $subnet)) {
1724
						$found = true;
1725
						break;
1726
					}
1727
				}
1728

    
1729
				if ($found === false) {
1730
					$input_errors[] = sprintf(gettext("The gateway address %s does not lie within one of the chosen interface's subnets."), $gateway_settings['gateway']);
1731
				}
1732
			}
1733
		} else if (is_ipaddrv6($gateway_settings['gateway'])) {
1734
			/* do not do a subnet match on a link local address, it's valid */
1735
			if (!is_linklocal($gateway_settings['gateway'])) {
1736
				if ($parent_ip == '') {
1737
					$parent_ip = get_interface_ipv6($gateway_settings['interface']);
1738
					$parent_sn = get_interface_subnetv6($gateway_settings['interface']);
1739
				}
1740
				if (empty($parent_ip) || empty($parent_sn)) {
1741
					$input_errors[] = gettext("Cannot add IPv6 Gateway Address because no IPv6 address could be found on the interface.");
1742
				} elseif (!isset($gateway_settings["nonlocalgateway"])) {
1743
					$subnets = array(gen_subnetv6($parent_ip, $parent_sn) . "/" . $parent_sn);
1744
					$vips = link_interface_to_vips($gateway_settings['interface']);
1745
					if (is_array($vips)) {
1746
						foreach ($vips as $vip) {
1747
							if (!is_ipaddrv6($vip['subnet'])) {
1748
								continue;
1749
							}
1750
							$subnets[] = gen_subnetv6($vip['subnet'], $vip['subnet_bits']) . "/" . $vip['subnet_bits'];
1751
						}
1752
					}
1753

    
1754
					$found = false;
1755
					foreach ($subnets as $subnet) {
1756
						if (ip_in_subnet($gateway_settings['gateway'], $subnet)) {
1757
							$found = true;
1758
							break;
1759
						}
1760
					}
1761

    
1762
					if ($found === false) {
1763
						$input_errors[] = sprintf(gettext("The gateway address %s does not lie within one of the chosen interface's subnets."), $gateway_settings['gateway']);
1764
					}
1765
				}
1766
			}
1767
		}
1768

    
1769
		if (!empty($config['interfaces'][$gateway_settings['interface']]['ipaddr'])) {
1770
			if (is_ipaddr($config['interfaces'][$gateway_settings['interface']]['ipaddr']) && (empty($gateway_settings['gateway']) || $gateway_settings['gateway'] == "dynamic")) {
1771
				$input_errors[] = gettext("Dynamic gateway values cannot be specified for interfaces with a static IPv4 configuration.");
1772
			}
1773
		}
1774
		if (!empty($config['interfaces'][$gateway_settings['interface']]['ipaddrv6'])) {
1775
			if (is_ipaddr($config['interfaces'][$gateway_settings['interface']]['ipaddrv6']) && (empty($gateway_settings['gateway']) || $gateway_settings['gateway'] == "dynamic")) {
1776
				$input_errors[] = gettext("Dynamic gateway values cannot be specified for interfaces with a static IPv6 configuration.");
1777
			}
1778
		}
1779
	}
1780
	if (($gateway_settings['monitor'] != "") && ($gateway_settings['monitor'] != "dynamic")) {
1781
		validateipaddr($gateway_settings['monitor'], IPV4V6, "Monitor IP", $input_errors, false);
1782
	}
1783
	if (isset($gateway_settings['data_payload']) && is_numeric($gateway_settings['data_payload']) && $gateway_settings['data_payload'] < 0) {
1784
		$input_errors[] = gettext("A valid data payload must be specified.");
1785
	}
1786
	/* only allow correct IPv4 and IPv6 gateway addresses */
1787
	if (($gateway_settings['gateway'] <> "") && is_ipaddr($gateway_settings['gateway']) && $gateway_settings['gateway'] != "dynamic") {
1788
		if (is_ipaddrv6($gateway_settings['gateway']) && ($gateway_settings['ipprotocol'] == "inet")) {
1789
			$input_errors[] = sprintf(gettext("The IPv6 gateway address '%s' can not be used as a IPv4 gateway."), $gateway_settings['gateway']);
1790
		}
1791
		if (is_ipaddrv4($gateway_settings['gateway']) && ($gateway_settings['ipprotocol'] == "inet6")) {
1792
			$input_errors[] = sprintf(gettext("The IPv4 gateway address '%s' can not be used as a IPv6 gateway."), $gateway_settings['gateway']);
1793
		}
1794
	}
1795
	/* only allow correct IPv4 and IPv6 monitor addresses */
1796
	if (($gateway_settings['monitor'] <> "") && is_ipaddr($gateway_settings['monitor']) && $gateway_settings['monitor'] != "dynamic") {
1797
		if (is_ipaddrv6($gateway_settings['monitor']) && ($gateway_settings['ipprotocol'] == "inet")) {
1798
			$input_errors[] = sprintf(gettext("The IPv6 monitor address '%s' can not be used on a IPv4 gateway."), $gateway_settings['monitor']);
1799
		}
1800
		if (is_ipaddrv4($gateway_settings['monitor']) && ($gateway_settings['ipprotocol'] == "inet6")) {
1801
			$input_errors[] = sprintf(gettext("The IPv4 monitor address '%s' can not be used on a IPv6 gateway."), $gateway_settings['monitor']);
1802
		}
1803
	}
1804

    
1805
	if (isset($gateway_settings['name'])) {
1806
		/* check for overlaps */
1807
		foreach ($a_gateways as $gateway) {
1808
			if (isset($id) && ($a_gateways[$id]) && ($a_gateways[$id] === $gateway)) {
1809
				if ($gateway['name'] != $gateway_settings['name']) {
1810
					$input_errors[] = gettext("Changing name on a gateway is not allowed.");
1811
				}
1812
				continue;
1813
			}
1814
			if ($gateway_settings['name'] <> "") {
1815
				if (($gateway['name'] <> "") && ($gateway_settings['name'] == $gateway['name']) && ($gateway['attribute'] !== "system")) {
1816
					$input_errors[] = sprintf(gettext('The gateway name "%s" already exists.'), $gateway_settings['name']);
1817
					break;
1818
				}
1819
			}
1820
			if (is_ipaddr($gateway_settings['gateway'])) {
1821
				if (($gateway['gateway'] <> "") && ($gateway_settings['gateway'] == $gateway['gateway']) && ($gateway['attribute'] !== "system")) {
1822
					$input_errors[] = sprintf(gettext('The gateway IP address "%s" already exists.'), $gateway_settings['gateway']);
1823
					break;
1824
				}
1825
			}
1826
			if (is_ipaddr($gateway_settings['monitor'])) {
1827
				if (($gateway['monitor'] <> "") && ($gateway_settings['monitor'] == $gateway['monitor']) && ($gateway['attribute'] !== "system")) {
1828
					$input_errors[] = sprintf(gettext('The monitor IP address "%s" is already in use. A different monitor IP must be chosen.'), $gateway_settings['monitor']);
1829
					break;
1830
				}
1831
			}
1832
		}
1833
	}
1834

    
1835
	/* input validation of dpinger advanced parameters */
1836

    
1837
	$dpinger_default = return_dpinger_defaults();
1838
	$latencylow = $dpinger_default['latencylow'];
1839
	if ($gateway_settings['latencylow']) {
1840
		if (!is_numeric($gateway_settings['latencylow'])) {
1841
			$input_errors[] = gettext("The low latency threshold needs to be a numeric value.");
1842
		} else if ($gateway_settings['latencylow'] < 1) {
1843
			$input_errors[] = gettext("The low latency threshold needs to be positive.");
1844
		} else {
1845
			$latencylow = $gateway_settings['latencylow'];
1846
		}
1847
	}
1848

    
1849
	$latencyhigh = $dpinger_default['latencyhigh'];
1850
	if ($gateway_settings['latencyhigh']) {
1851
		if (!is_numeric($gateway_settings['latencyhigh'])) {
1852
			$input_errors[] = gettext("The high latency threshold needs to be a numeric value.");
1853
		} else if ($gateway_settings['latencyhigh'] < 1) {
1854
			$input_errors[] = gettext("The high latency threshold needs to be positive.");
1855
		} else {
1856
			$latencyhigh = $gateway_settings['latencyhigh'];
1857
		}
1858
	}
1859

    
1860
	$losslow = $dpinger_default['losslow'];
1861
	if ($gateway_settings['losslow']) {
1862
		if (!is_numeric($gateway_settings['losslow'])) {
1863
			$input_errors[] = gettext("The low Packet Loss threshold needs to be a numeric value.");
1864
		} else if ($gateway_settings['losslow'] < 1) {
1865
			$input_errors[] = gettext("The low Packet Loss threshold needs to be positive.");
1866
		} else if ($gateway_settings['losslow'] >= 100) {
1867
			$input_errors[] = gettext("The low Packet Loss threshold needs to be less than 100.");
1868
		} else {
1869
			$losslow = $gateway_settings['losslow'];
1870
		}
1871
	}
1872

    
1873
	$losshigh = $dpinger_default['losshigh'];
1874
	if ($gateway_settings['losshigh']) {
1875
		if (!is_numeric($gateway_settings['losshigh'])) {
1876
			$input_errors[] = gettext("The high Packet Loss threshold needs to be a numeric value.");
1877
		} else if ($gateway_settings['losshigh'] < 1) {
1878
			$input_errors[] = gettext("The high Packet Loss threshold needs to be positive.");
1879
		} else if ($gateway_settings['losshigh'] > 100) {
1880
			$input_errors[] = gettext("The high Packet Loss threshold needs to be 100 or less.");
1881
		} else {
1882
			$losshigh = $gateway_settings['losshigh'];
1883
		}
1884
	}
1885

    
1886
	$time_period = $dpinger_default['time_period'];
1887
	if ($gateway_settings['time_period']) {
1888
		if (!is_numeric($gateway_settings['time_period'])) {
1889
			$input_errors[] = gettext("The time period over which results are averaged needs to be a numeric value.");
1890
		} else if ($gateway_settings['time_period'] < 1) {
1891
			$input_errors[] = gettext("The time period over which results are averaged needs to be positive.");
1892
		} else {
1893
			$time_period = $gateway_settings['time_period'];
1894
		}
1895
	}
1896

    
1897
	$interval = $dpinger_default['interval'];
1898
	if ($gateway_settings['interval']) {
1899
		if (!is_numeric($gateway_settings['interval'])) {
1900
			$input_errors[] = gettext("The probe interval needs to be a numeric value.");
1901
		} else if ($gateway_settings['interval'] < 1) {
1902
			$input_errors[] = gettext("The probe interval needs to be positive.");
1903
		} else {
1904
			$interval = $gateway_settings['interval'];
1905
		}
1906
	}
1907

    
1908
	$loss_interval = $dpinger_default['loss_interval'];
1909
	if ($gateway_settings['loss_interval']) {
1910
		if (!is_numeric($gateway_settings['loss_interval'])) {
1911
			$input_errors[] = gettext("The loss interval needs to be a numeric value.");
1912
		} else if ($gateway_settings['loss_interval'] < 1) {
1913
			$input_errors[] = gettext("The loss interval setting needs to be positive.");
1914
		} else {
1915
			$loss_interval = $gateway_settings['loss_interval'];
1916
		}
1917
	}
1918

    
1919
	$alert_interval = $dpinger_default['alert_interval'];
1920
	if ($gateway_settings['alert_interval']) {
1921
		if (!is_numeric($gateway_settings['alert_interval'])) {
1922
			$input_errors[] = gettext("The alert interval needs to be a numeric value.");
1923
		} else if ($gateway_settings['alert_interval'] < 1) {
1924
			$input_errors[] = gettext("The alert interval setting needs to be positive.");
1925
		} else {
1926
			$alert_interval = $gateway_settings['alert_interval'];
1927
		}
1928
	}
1929

    
1930
	if ($latencylow >= $latencyhigh) {
1931
		$input_errors[] = gettext(
1932
		    "The high latency threshold needs to be greater than the low latency threshold");
1933
	}
1934

    
1935
	if ($losslow >= $losshigh) {
1936
		$input_errors[] = gettext(
1937
		    "The high packet loss threshold needs to be higher than the low packet loss threshold");
1938
	}
1939

    
1940
	// If the loss interval is less than latencyhigh, then high latency could never be recorded
1941
	// because those high latency packets would be considered as lost. So do not allow that.
1942
	if ($latencyhigh > $loss_interval) {
1943
		$input_errors[] = gettext("The loss interval needs to be greater than or equal to the high latency threshold.");
1944
	}
1945

    
1946
	// Ensure that the time period is greater than 2 times the probe interval plus the loss interval.
1947
	if (($interval * 2 + $loss_interval) >= $time_period) {
1948
		$input_errors[] = gettext("The time period needs to be greater than twice the probe interval plus the loss interval.");
1949
	}
1950

    
1951
	// There is no point recalculating the average latency and loss more often than the probe interval.
1952
	// So the alert interval needs to be >= probe interval.
1953
	if ($interval > $alert_interval) {
1954
		$input_errors[] = gettext("The alert interval needs to be greater than or equal to the probe interval.");
1955
	}
1956

    
1957
	return $input_errors;
1958
}
1959

    
1960
// Save gateway settings.
1961
// $gateway_settings - the array of gateway setting parameters
1962
// $realid - the index of the gateway to be modified (otherwise "" if adding a new gateway)
1963

    
1964
// This function is responsible to:
1965
//   Setup the gateway parameter structure from the gateway settings input parameter
1966
//   Save the structure into the config
1967
//   Remove any run-time settings from gateway parameters that are changed (e.g. remove routes to addresses that are changing)
1968

    
1969
// A subsequent "apply" step will implement the added/changed gateway.
1970

    
1971
function save_gateway($gateway_settings, $realid = "") {
1972
	global $config;
1973

    
1974
	init_config_arr(array('gateways', 'gateway_item'));
1975
	$a_gateway_item = &$config['gateways']['gateway_item'];
1976
	$reloadif = "";
1977
	$gateway = array();
1978

    
1979
	if (empty($gateway_settings['interface'])) {
1980
		$gateway['interface'] = $gateway_settings['friendlyiface'];
1981
	} else {
1982
		$gateway['interface'] = $gateway_settings['interface'];
1983
	}
1984
	if (is_ipaddr($gateway_settings['gateway'])) {
1985
		$gateway['gateway'] = $gateway_settings['gateway'];
1986
	} else {
1987
		$gateway['gateway'] = "dynamic";
1988
	}
1989
	$gateway['name'] = $gateway_settings['name'];
1990
	$gateway['weight'] = $gateway_settings['weight'];
1991
	$gateway['ipprotocol'] = $gateway_settings['ipprotocol'];
1992
	if ($gateway_settings['interval']) {
1993
		$gateway['interval'] = $gateway_settings['interval'];
1994
	}
1995

    
1996
	if ($gateway_settings['time_period']) {
1997
		$gateway['time_period'] = $gateway_settings['time_period'];
1998
	}
1999
	if ($gateway_settings['alert_interval']) {
2000
		$gateway['alert_interval'] = $gateway_settings['alert_interval'];
2001
	}
2002

    
2003
	$gateway['descr'] = $gateway_settings['descr'];
2004
	if ($gateway_settings['monitor_disable'] == "yes") {
2005
		$gateway['monitor_disable'] = true;
2006
	}
2007
	if ($gateway_settings['action_disable'] == "yes") {
2008
		$gateway['action_disable'] = true;
2009
	}
2010
	if ($gateway_settings['nonlocalgateway'] == "yes") {
2011
		$gateway['nonlocalgateway'] = true;
2012
	}
2013
	if ($gateway_settings['force_down'] == "yes") {
2014
		$gateway['force_down'] = true;
2015
	}
2016
	if (is_ipaddr($gateway_settings['monitor'])) {
2017
		$gateway['monitor'] = $gateway_settings['monitor'];
2018
	}
2019
	if (isset($gateway_settings['data_payload']) && $gateway_settings['data_payload'] > 0) {
2020
		$gateway['data_payload'] = $gateway_settings['data_payload'];
2021
	}
2022

    
2023
	/* NOTE: If gateway ip is changed need to cleanup the old static interface route */
2024
	if ($gateway_settings['monitor'] != "dynamic" && !empty($a_gateway_item[$realid]) && is_ipaddr($a_gateway_item[$realid]['gateway']) &&
2025
		$gateway['gateway'] != $a_gateway_item[$realid]['gateway'] &&
2026
		isset($a_gateway_item[$realid]["nonlocalgateway"])) {
2027
		$realif = get_real_interface($a_gateway_item[$realid]['interface']);
2028
		$inet = (!is_ipaddrv4($a_gateway_item[$realid]['gateway']) ? "-inet6" : "-inet");
2029
		$rgateway = $a_gateway_item[$realid]['gateway'];
2030
		$cmd = "/sbin/route delete $inet " . escapeshellarg($a_gateway_item[$realid]['gateway']) . " -iface " . escapeshellarg($realif) . " " . escapeshellarg($rgateway);
2031
		mwexec($cmd);
2032
	}
2033

    
2034
	/* NOTE: If monitor ip is changed need to cleanup the old static route */
2035
	if ($gateway_settings['monitor'] != "dynamic" && !empty($a_gateway_item[$realid]) && is_ipaddr($a_gateway_item[$realid]['monitor']) &&
2036
		$gateway_settings['monitor'] != $a_gateway_item[$realid]['monitor'] && $gateway['gateway'] != $a_gateway_item[$realid]['monitor']) {
2037
		$rgateway = $a_gateway_item[$realid]['gateway'];
2038
		if (is_ipaddrv4($a_gateway_item[$realid]['monitor'])) {
2039
			mwexec("/sbin/route delete " . escapeshellarg($a_gateway_item[$realid]['monitor']) . " " . escapeshellarg($rgateway));
2040
		} else {
2041
			mwexec("/sbin/route delete -inet6 " . escapeshellarg($a_gateway_item[$realid]['monitor']) . " " . escapeshellarg($rgateway));
2042
		}
2043
	}
2044

    
2045
	if ($gateway_settings['defaultgw'] == "yes" || $gateway_settings['defaultgw'] == "on") {
2046
		// a new default gateway is being saved.
2047
		$i = 0;
2048
		/* remove the default gateway bits for all gateways with the same address family */
2049
		if (is_array($a_gateway_item)) {
2050
			foreach ($a_gateway_item as $gw) {
2051
				if ($gateway['ipprotocol'] == $gw['ipprotocol']) {
2052
					if ($gw['interface'] != $gateway_settings['interface'] &&
2053
						($gw['name'] == $config['gateways']['defaultgw4'] || $gw['name'] == $config['gateways']['defaultgw6'])) {
2054
						// remember the old default gateway interface to call with a "interface reconfigure" event.
2055
						$reloadif = $gw['interface'];
2056
					}
2057
				}
2058
				$i++;
2059
			}
2060
		}
2061
		if ($gateway['ipprotocol'] == "inet") {
2062
			$config['gateways']['defaultgw4'] = $gateway['name'];
2063
		} elseif ($gateway['ipprotocol'] == "inet6") {
2064
			$config['gateways']['defaultgw6'] = $gateway['name'];
2065
		}
2066
	}
2067

    
2068
	if ($gateway_settings['latencylow']) {
2069
		$gateway['latencylow'] = $gateway_settings['latencylow'];
2070
	}
2071
	if ($gateway_settings['latencyhigh']) {
2072
		$gateway['latencyhigh'] = $gateway_settings['latencyhigh'];
2073
	}
2074
	if ($gateway_settings['losslow']) {
2075
		$gateway['losslow'] = $gateway_settings['losslow'];
2076
	}
2077
	if ($gateway_settings['losshigh']) {
2078
		$gateway['losshigh'] = $gateway_settings['losshigh'];
2079
	}
2080
	if ($gateway_settings['loss_interval']) {
2081
		$gateway['loss_interval'] = $gateway_settings['loss_interval'];
2082
	}
2083
	/* when saving the manual gateway we use the attribute which has the corresponding id */
2084
	if (isset($realid) && $a_gateway_item[$realid]) {
2085
		$preserve_disabled = isset($a_gateway_item[$realid]['disabled']);
2086
		$a_gateway_item[$realid] = $gateway;
2087
		if ($preserve_disabled) {
2088
			$a_gateway_item[$realid]['disabled'] = true;
2089
		}
2090
	} else {
2091
		$a_gateway_item[] = $gateway;
2092
	}
2093
	gateway_set_enabled($gateway_settings['name'], !isset($gateway_settings['disabled']));
2094

    
2095
	mark_subsystem_dirty('staticroutes');
2096

    
2097
	write_config();
2098

    
2099
	if (!empty($reloadif)) {
2100
		send_event("interface reconfigure {$reloadif}");
2101
	}
2102
}
2103

    
2104
function gateway_set_enabled($name, $enabled) {
2105
	global $config;
2106
	if (is_array($config['gateways']['gateway_item'])) {
2107
		foreach($config['gateways']['gateway_item'] as &$item) {
2108
			if ($item['name'] == $name) {
2109
				$gateway = &$item;
2110
			}
2111
		}
2112
	}
2113
	if (!isset($gateway)) {
2114
		return;
2115
	}
2116
	if ($enabled) {
2117
		unset($gateway['disabled']);
2118
	} else {
2119
		/* Check if the gateway was enabled but changed to disabled. */
2120
		if (!isset($gateway['disabled'])) {
2121
			/*  If the disabled gateway was the default route, remove the default route */
2122
			if (is_ipaddr($gateway['gateway'])) {
2123
				$inet = (!is_ipaddrv4($gateway['gateway']) ? 'inet6' : 'inet');
2124
				if ($inet == 'inet') {
2125
					$cgw = getcurrentdefaultgatewayip('inet');
2126
				} else {
2127
					$cgw = getcurrentdefaultgatewayip('inet6');
2128
				}
2129
				if ($gateway['gateway'] == $cgw) {
2130
					mwexec("/sbin/route delete -{$inet} default " . escapeshellarg($cgw));
2131
				}
2132
			}
2133
			$gateway['disabled'] = true;
2134
		}
2135
	}
2136
}
2137

    
2138
function gateway_or_gwgroup_exists($name) {
2139
	global $config;
2140
	if (is_array($config['gateways']['gateway_item'])) {
2141
		foreach($config['gateways']['gateway_item'] as $item) {
2142
			if ($item['name'] == $name) {
2143
				return true;
2144
			}
2145
		}
2146
	}
2147
	if (is_array($config['gateways']['gateway_group'])) {
2148
		foreach($config['gateways']['gateway_group'] as $item) {
2149
			if ($item['name'] == $name) {
2150
				return true;
2151
			}
2152
		}
2153
	}
2154
	return false;
2155
}
2156

    
2157
?>
(21-21/60)