system: rewrite the default route handling

With this, there is consistency in setting the default route
and associated marker file.  Nothing is done before the decision
is final, so we may not end up with a missing route after an
attempt to reconfigure for something that doesn't work anyway.

DHCPv4 and PPP still do this manually, but we will have to route
them through the same logic instead of them manually handling their
routes.  6rd and 6to4 may have a similar issue as well.

While here, swap the system_routing_configure() arguments to
align with the way other configure functions present their
arguments (verbose print flag first).

Gateway monitoring and default switching improvement since 2016.
Push $keep into default handling, it may be useful in general to
avoid spurious switches.  It could also be a default probably.
This commit is contained in:
Franco Fichtner 2018-03-15 11:11:31 +00:00
parent 9ca61d8b3e
commit 09f45fbdbf
7 changed files with 88 additions and 89 deletions

View File

@ -1,6 +1,7 @@
<?php
/*
Copyright (C) 2016-2018 Franco Fichtner <franco@opnsense.org>
Copyright (C) 2008 Bill Marquette <bill.marquette@gmail.com>
Copyright (C) 2008 Seth Mos <seth.mos@dds.nl>
Copyright (C) 2010 Ermal Luçi
@ -769,25 +770,8 @@ function fixup_default_gateway($gateways_status, $gateways_arr)
continue;
}
$defaultif = get_real_interface($gateways_arr[$dfltgwname]['friendlyiface']);
$gwipmatch = $dfltgwip;
if (is_linklocal($dfltgwip)) {
/* correct match in IPv6 case */
$gwipmatch .= "%{$defaultif}";
}
$tmpcmd = "/sbin/route -n get -{$ipprotocol} default 2>/dev/null | /usr/bin/awk '/gateway:/ {print $2}'";
$defaultgw = trim(exec($tmpcmd), " \n");
if ($defaultgw != $gwipmatch) {
foreach (glob('/tmp/*_defaultgw' . ($ipprotocol == 'inet' ? '' : 'v6')) as $to_delete) {
log_error("GATEWAYS: removing {$to_delete}");
@unlink($to_delete);
}
log_error("Switching default gateway to $dfltgwname ($dfltgwip on $defaultif)");
@file_put_contents("/tmp/{$defaultif}_defaultgw" . ($ipprotocol == 'inet' ? '' : 'v6'), $dfltgwip);
/* XXX fargw and IPv6 should be cleaned up to make it easier to read */
system_default_route($dfltgwip, !isset($gateways_arr[$dfltgwname]['fargw']) && $ipprotocol == 'inet' ? null : $defaultif);
}
/* switch only if another gateway is set aa the default */
system_default_route($dfltgwip, $ipprotocol, $gateways_arr[$dfltgwname]['friendlyiface'], isset($gateways_arr[$dfltgwname]['fargw']), true);
}
}

View File

@ -995,7 +995,7 @@ function interface_gif_configure(&$gif, $gifkey = "")
foreach ($iflist as $ifname) {
if ($config['interfaces'][$ifname]['if'] == $gifif) {
if (get_interface_gateway($ifname) || get_interface_gateway_v6($ifname)) {
system_routing_configure($ifname);
system_routing_configure(false, $ifname);
break;
}
}
@ -1076,7 +1076,7 @@ function interfaces_configure($verbose = false)
interfaces_group_setup();
if ($reload) {
system_routing_configure('', $verbose);
system_routing_configure($verbose);
ipsec_configure_do($verbose);
plugins_configure('dns', $verbose);
services_dhcpd_configure('all', array(), $verbose);
@ -2713,7 +2713,7 @@ function interface_configure($interface = 'wan', $reloadall = false, $linkupeven
}
if ($reloadall == true) {
system_routing_configure($interface);
system_routing_configure(false, $interface);
ipsec_configure_do();
plugins_configure('dns');
services_dhcpd_configure();

View File

@ -417,30 +417,67 @@ function system_host_route($host, $gateway, $delete = true, $add = true)
}
}
function system_default_route($gateway, $interface = null)
function system_default_route($gateway, $family, $interface, $far = false, $keep = false)
{
if (is_ipaddrv4($gateway)) {
$family = 'inet';
} elseif (is_ipaddrv6($gateway)) {
$family = 'inet6';
if (is_linklocal($gateway)) {
$gateway .= "%{$interface}";
$realif = get_real_interface($interface);
switch ($family) {
case 'inet':
break;
case 'inet6':
if (is_linklocal($gateway)) {
$gateway .= "%{$realif}";
}
break;
default:
log_error("ROUTING: unknown address family '{$family}'");
return;
}
if ($keep) {
$tmpcmd = "/sbin/route -n get -{$family} default 2>/dev/null | /usr/bin/awk '/gateway:/ {print $2}'";
$current = trim(exec($tmpcmd), " \n");
if ($current == $gateway) {
log_error("ROUTING: keeping current default gateway '{$gateway}'");
return;
}
}
if ($family == 'inet') {
foreach (glob('/tmp/*_defaultgw', GLOB_BRACE) as $to_delete) {
log_error("ROUTING: removing {$to_delete}");
@unlink($to_delete);
}
log_error("ROUTING: creating /tmp/{$realif}_defaultgw using '{$gateway}'");
@file_put_contents("/tmp/{$realif}_defaultgw", $gateway);
if (!$far) {
$realif = null;
}
/* IPv6 does not support far gateway notion */
$interface = null;
} else {
return;
foreach (glob('/tmp/*_defaultgwv6', GLOB_BRACE) as $to_delete) {
log_error("ROUTING: removing {$to_delete}");
@unlink($to_delete);
}
log_error("ROUTING: creating /tmp/{$realif}_defaultgwv6 using '{$gateway}'");
@file_put_contents("/tmp/{$realif}_defaultgwv6", $gateway);
/* IPv6 does not support far gateway notion */
$realif = null;
$far = false;
}
mwexecf('/sbin/route delete -%s default', array($family), true);
if (!empty($interface)) {
mwexecf('/sbin/route delete -%s %s -interface %s', array($family, $gateway, $interface), true);
mwexecf('/sbin/route add -%s %s -interface %s', array($family, $gateway, $interface));
if (!empty($realif)) {
mwexecf('/sbin/route delete -%s %s -interface %s', array($family, $gateway, $realif), true);
mwexecf('/sbin/route add -%s %s -interface %s', array($family, $gateway, $realif));
}
mwexecf('/sbin/route add -%s default %s', array($family, $gateway));
}
function system_routing_configure($interface = '', $verbose = false)
function system_routing_configure($verbose = false, $interface = '')
{
global $config;
@ -449,58 +486,45 @@ function system_routing_configure($interface = '', $verbose = false)
flush();
}
$gatewayip = "";
$interfacegw = "";
$interfacegw = '';
$foundgw = false;
$gatewayipv6 = "";
$interfacegwv6 = "";
$foundgwv6 = false;
$gatewayip = '';
$fargw = false;
$interfacegwv6 = '';
$foundgwv6 = false;
$gatewayipv6 = '';
if (!empty($interface)) {
log_error("ROUTING: entering configure using '${interface}'");
} else {
log_error("ROUTING: entering configure using defaults");
}
/* tack on all the hard defined gateways as well */
if (isset($config['gateways']['gateway_item'])) {
foreach (glob('/tmp/*_defaultgw{,v6}', GLOB_BRACE) as $to_delete) {
log_error("ROUTING: removing {$to_delete}");
@unlink($to_delete);
}
foreach ($config['gateways']['gateway_item'] as $gateway) {
if (isset($gateway['defaultgw'])) {
if ($foundgw == false && $gateway['ipprotocol'] != "inet6" && (is_ipaddrv4($gateway['gateway']) || $gateway['gateway'] == "dynamic")) {
if ($gateway['gateway'] == "dynamic") {
if ($foundgw == false && $gateway['ipprotocol'] != 'inet6') {
if ($gateway['gateway'] == 'dynamic') {
$gateway['gateway'] = get_interface_gateway($gateway['interface']);
}
$interfacegw = $gateway['interface'];
$fargw = isset($gateway['fargw']);
$gatewayip = $gateway['gateway'];
$interfacegw = $gateway['interface'];
if (!empty($gateway['interface'])) {
$defaultif = get_real_interface($gateway['interface']);
if ($defaultif && is_ipaddrv4($gatewayip)) {
log_error("ROUTING: creating /tmp/{$defaultif}_defaultgw");
@file_put_contents("/tmp/{$defaultif}_defaultgw", $gatewayip);
}
}
$foundgw = true;
} elseif ($foundgwv6 == false && $gateway['ipprotocol'] == "inet6" && (is_ipaddrv6($gateway['gateway']) || $gateway['gateway'] == "dynamic")) {
if ($gateway['gateway'] == "dynamic") {
log_error("ROUTING: IPv4 default gateway set to {$interfacegw}");
} elseif ($foundgwv6 == false && $gateway['ipprotocol'] == 'inet6') {
if ($gateway['gateway'] == 'dynamic') {
$gateway['gateway'] = get_interface_gateway_v6($gateway['interface']);
}
$gatewayipv6 = $gateway['gateway'];
$interfacegwv6 = $gateway['interface'];
if (!empty($gateway['interface'])) {
$defaultifv6 = get_real_interface($gateway['interface']);
if ($defaultifv6 && is_ipaddrv6($gatewayipv6)) {
log_error("ROUTING: creating /tmp/{$defaultifv6}_defaultgwv6");
@file_put_contents("/tmp/{$defaultifv6}_defaultgwv6", $gatewayipv6);
}
}
$gatewayipv6 = $gateway['gateway'];
$foundgwv6 = true;
log_error("ROUTING: IPv6 default gateway set to {$interfacegwv6}");
}
}
}
@ -516,43 +540,34 @@ function system_routing_configure($interface = '', $verbose = false)
* In fact the following code is a mini gateway switcher
* facility which can only switch one hardcoded gateway and
* may obscure a problem with the setup for a long time... :(
*
* XXX Find a way to infer an upstream-capable interface or
* maybe make gateway switching the hardcoded behaviour, or
* at least the new default.
*/
if (!$foundgw) {
$interfacegw = 'wan';
$defaultif = get_real_interface($interfacegw);
log_error("ROUTING: no IPv4 default gateway set, assuming {$interfacegw}");
$gatewayip = get_interface_gateway($interfacegw);
if (is_ipaddrv4($gatewayip)) {
log_error("ROUTING: no IPv4 default gateway set, assuming {$interfacegw} on '{$defaultif}' ({$gatewayip})");
@file_put_contents("/tmp/{$defaultif}_defaultgw", $gatewayip);
} else {
log_error("ROUTING: no IPv4 default gateway set, skipping {$interfacegw} on '{$defaultif}'");
}
}
if (!$foundgwv6) {
$interfacegwv6 = 'wan';
$defaultifv6 = get_real_interface($interfacegwv6);
log_error("ROUTING: no IPv6 default gateway set, assuming {$interfacegwv6}");
$gatewayipv6 = get_interface_gateway_v6($interfacegwv6);
if (is_ipaddrv6($gatewayipv6)) {
log_error("ROUTING: no IPv6 default gateway set, trying {$interfacegwv6} on '{$defaultifv6}' ({$gatewayipv6})");
@file_put_contents("/tmp/{$defaultifv6}_defaultgwv6", $gatewayipv6);
} else {
log_error("ROUTING: no IPv6 default gateway set, skipping {$interfacegwv6} on '{$defaultifv6}'");
$interfacegwv6 = '';
}
}
if ((empty($interface) || $interface == $interfacegw) && is_ipaddrv4($gatewayip)) {
log_error("ROUTING: setting IPv4 default route to {$gatewayip}");
system_default_route($gatewayip, $fargw ? $defaultif : null);
system_default_route($gatewayip, 'inet', $interfacegw, $fargw);
} else {
log_error("ROUTING: skipping IPv4 default route");
}
if ((empty($interface) || $interface == $interfacegwv6) && is_ipaddrv6($gatewayipv6)) {
log_error("ROUTING: setting IPv6 default route to {$gatewayipv6}");
system_default_route($gatewayipv6, $defaultifv6);
system_default_route($gatewayipv6, 'inet6', $interfacegwv6, false);
} else {
log_error("ROUTING: skipping IPv6 default route");
}

View File

@ -104,7 +104,7 @@ filter_pflog_start(true);
setup_gateways_monitor(true);
plugins_configure('earlybootup', true);
system_cron_configure(true, true);
system_routing_configure('', true);
system_routing_configure(true);
plugins_configure('dns', true);
services_dhcpd_configure('all', array(), true);

View File

@ -144,7 +144,7 @@ if (isset($config['gifs']['gif']) && is_array($config['gifs']['gif'])){
if (!empty($confif)) {
interface_configure($confif);
}
system_routing_configure($ifname);
system_routing_configure(false, $ifname);
}
}
}
@ -161,7 +161,7 @@ $cacheip = @file_get_contents($cacheip_file);
if (!is_ipaddr($cacheip) || $ip != $cacheip || !is_ipaddr($configip)) {
@unlink($cacheip_file);
system_routing_configure($interface);
system_routing_configure(false, $interface);
setup_gateways_monitor();
plugins_configure('vpn', false, array($interface));
filter_configure_sync();

View File

@ -142,7 +142,7 @@ if (!is_ipaddr($cacheip) || $ip != $cacheip || !is_ipaddr($configip)) {
@unlink($cacheip_file);
system_routing_configure($interface);
system_routing_configure(false, $interface);
setup_gateways_monitor();
plugins_configure('vpn', false, array($interface));
filter_configure_sync();

View File

@ -42,6 +42,6 @@ foreach (glob("/tmp/delete_route_*.todo") as $filename) {
unlink($filename);
}
system_routing_configure('', true);
system_routing_configure(true);
setup_gateways_monitor(true);
filter_configure_sync(true);