diff options
-rw-r--r-- | python/vyos/ifconfig/interface.py | 47 |
1 files changed, 45 insertions, 2 deletions
diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py index 85de0947a..baa45f5bd 100644 --- a/python/vyos/ifconfig/interface.py +++ b/python/vyos/ifconfig/interface.py @@ -22,6 +22,7 @@ from copy import deepcopy from glob import glob from ipaddress import IPv4Network +from ipaddress import IPv6Interface from netifaces import ifaddresses # this is not the same as socket.AF_INET/INET6 from netifaces import AF_INET @@ -911,7 +912,8 @@ class Interface(Control): return None rc = self.set_interface('ipv6_autoconf', autoconf) if autoconf == '0': - self.flush_ipv6_slaac_addrs() + flushed = self.flush_ipv6_slaac_addrs() + self.flush_ipv6_slaac_routes(flushed) return rc def add_ipv6_eui64_address(self, prefix): @@ -1313,12 +1315,13 @@ class Interface(Control): # flush all addresses self._cmd(cmd) - def flush_ipv6_slaac_addrs(self): + def flush_ipv6_slaac_addrs(self) -> list: """ Flush all IPv6 addresses installed in response to router advertisement messages from this interface. Will raise an exception on error. + Will return a list of flushed IPv6 addresses. """ netns = get_interface_namespace(self.ifname) netns_cmd = f'ip netns exec {netns}' if netns else '' @@ -1331,6 +1334,7 @@ class Interface(Control): # 'prefixlen': 64, 'scope': 'global', 'dynamic': True, # 'mngtmpaddr': True, 'protocol': 'kernel_ra', # 'valid_life_time': 2591987, 'preferred_life_time': 14387} + flushed = [] for addr_info in tmp['addr_info']: if 'protocol' not in addr_info: continue @@ -1338,8 +1342,47 @@ class Interface(Control): addr_info['scope'] == 'global'): # Flush IPv6 addresses installed by router advertisement ra_addr = f"{addr_info['local']}/{addr_info['prefixlen']}" + flushed.append(ra_addr) cmd = f'{netns_cmd} ip -6 addr del dev {self.ifname} {ra_addr}' self._cmd(cmd) + return flushed + + def flush_ipv6_slaac_routes(self, ra_addrs: list=[]) -> None: + """ + Flush IPv6 default routes installed in response to router advertisement + messages from this interface. + + Will raise an exception on error. + """ + # Do not flush default route if interface uses DHCPv6 in addition to SLAAC + if 'address' in self.config and 'dhcpv6' in self.config['address']: + return None + + # Find IPv6 connected prefixes for flushed SLAAC addresses + connected = [] + for addr in ra_addrs: + connected.append(str(IPv6Interface(addr).network)) + + netns = get_interface_namespace(self.ifname) + netns_cmd = f'ip netns exec {netns}' if netns else '' + + tmp = self._cmd(f'{netns_cmd} ip -j -6 route show dev {self.ifname}') + tmp = json.loads(tmp) + # Parse interface routes. Example data: + # {'dst': 'default', 'gateway': 'fe80::250:56ff:feb3:cdba', + # 'protocol': 'ra', 'metric': 1024, 'flags': [], 'expires': 1398, + # 'metrics': [{'hoplimit': 64}], 'pref': 'medium'} + for route in tmp: + # If it's a default route received from RA, delete it + if (dict_search('dst', route) == 'default' and + dict_search('protocol', route) == 'ra'): + self._cmd(f'{netns_cmd} ip -6 route del default via {route["gateway"]} dev {self.ifname}') + # Remove connected prefixes received from RA + if dict_search('dst', route) in connected: + # If it's a connected prefix, delete it + self._cmd(f'{netns_cmd} ip -6 route del {route["dst"]} dev {self.ifname}') + + return None def add_to_bridge(self, bridge_dict): """ |