Use more route information to improve default_ipvX facts on BSDs (#64172)

* Use "default" route info to help pick the default address.

Before this change, the address information used for the "default_ipv4"
and "default_ipv6" information is whatever is first on the interface
identified by the looking up the "default" route. On OpenBSD at
least, the first IPv6 address tends to be a link-local address,
which is not useful if you want to try and put a globally routable
v6 address in a template somewhere.

OpenBSD and NetBSD list the local address used for the default
route, so we can then use that to filter the addresses on the
interface and use the right one when it is available. This should
also help in situations where the interface has a lot of aliases,
or if you're doing IP multipath.

Thanks to John-Mark Gurney and Jared McNeill for providing me output
from the route command on FreeBSD and NetBSD respectively.

* Use "route get default" to get default route information.

Using some other arbitrary address makes these facts produce
unexpected results in some situations.
This commit is contained in:
David Gwynne 2019-11-06 10:36:04 +10:00 committed by Brian Coca
parent adcf9458f1
commit 8c0ab43f05

View file

@ -73,12 +73,12 @@ class GenericBsdIfconfigNetwork(Network):
def get_default_interfaces(self, route_path): def get_default_interfaces(self, route_path):
# Use the commands: # Use the commands:
# route -n get 8.8.8.8 -> Google public DNS # route -n get default
# route -n get -inet6 2404:6800:400a:800::1012 -> ipv6.google.com # route -n get -inet6 default
# to find out the default outgoing interface, address, and gateway # to find out the default outgoing interface, address, and gateway
command = dict(v4=[route_path, '-n', 'get', '8.8.8.8'], command = dict(v4=[route_path, '-n', 'get', 'default'],
v6=[route_path, '-n', 'get', '-inet6', '2404:6800:400a:800::1012']) v6=[route_path, '-n', 'get', '-inet6', 'default'])
interface = dict(v4={}, v6={}) interface = dict(v4={}, v6={})
@ -92,13 +92,19 @@ class GenericBsdIfconfigNetwork(Network):
# RTNETLINK answers: Invalid argument # RTNETLINK answers: Invalid argument
continue continue
for line in out.splitlines(): for line in out.splitlines():
words = line.split() words = line.strip().split(': ')
# Collect output from route command # Collect output from route command
if len(words) > 1: if len(words) > 1:
if words[0] == 'interface:': if words[0] == 'interface':
interface[v]['interface'] = words[1] interface[v]['interface'] = words[1]
if words[0] == 'gateway:': if words[0] == 'gateway':
interface[v]['gateway'] = words[1] interface[v]['gateway'] = words[1]
# help pick the right interface address on OpenBSD
if words[0] == 'if address':
interface[v]['address'] = words[1]
# help pick the right interface address on NetBSD
if words[0] == 'local addr':
interface[v]['address'] = words[1]
return interface['v4'], interface['v6'] return interface['v4'], interface['v6']
@ -291,6 +297,14 @@ class GenericBsdIfconfigNetwork(Network):
for item in ifinfo: for item in ifinfo:
if item != 'ipv4' and item != 'ipv6': if item != 'ipv4' and item != 'ipv6':
defaults[item] = ifinfo[item] defaults[item] = ifinfo[item]
if len(ifinfo[ip_type]) > 0:
for item in ifinfo[ip_type][0]: ipinfo = []
defaults[item] = ifinfo[ip_type][0][item] if 'address' in defaults:
ipinfo = [x for x in ifinfo[ip_type] if x['address'] == defaults['address']]
if len(ipinfo) == 0:
ipinfo = ifinfo[ip_type]
if len(ipinfo) > 0:
for item in ipinfo[0]:
defaults[item] = ipinfo[0][item]