diff options
Diffstat (limited to 'python/vyos/ifconfig/interface.py')
-rw-r--r-- | python/vyos/ifconfig/interface.py | 166 |
1 files changed, 89 insertions, 77 deletions
diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py index 32ce1a80c..de5ca369f 100644 --- a/python/vyos/ifconfig/interface.py +++ b/python/vyos/ifconfig/interface.py @@ -1,4 +1,4 @@ -# Copyright 2019 VyOS maintainers and contributors <maintainers@vyos.io> +# Copyright 2019-2020 VyOS maintainers and contributors <maintainers@vyos.io> # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -14,6 +14,7 @@ # License along with this library. If not, see <http://www.gnu.org/licenses/>. import os +import re import json from copy import deepcopy @@ -49,7 +50,7 @@ class Interface(Control): # WireGuard to modify their display behaviour OperationalClass = Operational - options = [] + options = ['debug', 'create',] required = [] default = { 'type': '', @@ -63,6 +64,7 @@ class Interface(Control): 'bondable': False, 'broadcast': False, 'bridgeable': False, + 'eternal': '', } _command_get = { @@ -217,7 +219,7 @@ class Interface(Control): else: raise Exception('interface "{}" not found'.format(self.config['ifname'])) - # list of assigned IP addresses + # temporary list of assigned IP addresses self._addr = [] self.operational = self.OperationalClass(ifname) @@ -238,39 +240,21 @@ class Interface(Control): >>> i = Interface('eth0') >>> i.remove() """ - # stop DHCP(v6) if running - self.dhcp.v4.delete() - self.dhcp.v6.delete() # remove all assigned IP addresses from interface - this is a bit redundant # as the kernel will remove all addresses on interface deletion, but we # can not delete ALL interfaces, see below - for addr in self.get_addr(): - self.del_addr(addr) + self.flush_addrs() # --------------------------------------------------------------------- - # A code refactoring is required as this type check is present as - # Interface implement behaviour for one of it's sub-class. - - # It is required as the current pattern for vlan is: - # Interface('name').remove() to delete an interface - # The code should be modified to have a class method called connect and - # have Interface.connect('name').remove() + # Any class can define an eternal regex in its definition + # interface matching the regex will not be deleted - # each subclass should register within Interface the pattern for that - # interface ie: (ethX, etc.) and use this to create an instance of - # the right class (EthernetIf, ...) - - # Ethernet interfaces can not be removed - - # Commented out as nowhere in the code do we call Interface() - # This would also cause an import loop - # if self.__class__ == EthernetIf: - # return - - # --------------------------------------------------------------------- - - self._delete() + eternal = self.definition['eternal'] + if not eternal: + self._delete() + elif not re.match(eternal, self.ifname): + self._delete() def _delete(self): # NOTE (Improvement): @@ -431,39 +415,28 @@ class Interface(Control): """ return self.set_interface('ipv6_autoconf', autoconf) - def set_ipv6_eui64_address(self, prefix): + def add_ipv6_eui64_address(self, prefix): """ Extended Unique Identifier (EUI), as per RFC2373, allows a host to - assign iteslf a unique IPv6 address based on a given IPv6 prefix. + assign itself a unique IPv6 address based on a given IPv6 prefix. - If prefix is passed address is assigned, if prefix is '' address is - removed from interface. + Calculate the EUI64 from the interface's MAC, then assign it + with the given prefix to the interface. """ - # if prefix is an empty string convert it to None so mac2eui64 works - # as expected - if not prefix: - prefix = None eui64 = mac2eui64(self.get_mac(), prefix) + prefixlen = prefix.split('/')[1] + self.add_addr(f'{eui64}/{prefixlen}') - if not prefix: - # if prefix is empty - thus removed - we need to walk through all - # interface IPv6 addresses and find the one with the calculated - # EUI-64 identifier. The address is then removed - for addr in self.get_addr(): - addr_wo_prefix = addr.split('/')[0] - if is_ipv6(addr_wo_prefix): - if eui64 in IPv6Address(addr_wo_prefix).exploded: - self.del_addr(addr) - - return None + def del_ipv6_eui64_address(self, prefix): + """ + Delete the address based on the interface's MAC-based EUI64 + combined with the prefix address. + """ + eui64 = mac2eui64(self.get_mac(), prefix) + prefixlen = prefix.split('/')[1] + self.del_addr(f'{eui64}/{prefixlen}') - # calculate and add EUI-64 IPv6 address - if IPv6Network(prefix): - # we also need to take the subnet length into account - prefix = prefix.split('/')[1] - eui64 = f'{eui64}/{prefix}' - self.add_addr(eui64 ) def set_ipv6_forwarding(self, forwarding): """ @@ -644,7 +617,8 @@ class Interface(Control): def add_addr(self, addr): """ Add IP(v6) address to interface. Address is only added if it is not - already assigned to that interface. + already assigned to that interface. Address format must be validated + and compressed/normalized before calling this function. addr: can be an IPv4 address, IPv6 address, dhcp or dhcpv6! IPv4: add IPv4 address to interface @@ -652,6 +626,7 @@ class Interface(Control): dhcp: start dhclient (IPv4) on interface dhcpv6: start dhclient (IPv6) on interface + Returns False if address is already assigned and wasn't re-added. Example: >>> from vyos.ifconfig import Interface >>> j = Interface('eth0') @@ -660,32 +635,44 @@ class Interface(Control): >>> j.get_addr() ['192.0.2.1/24', '2001:db8::ffff/64'] """ + # XXX: normalize/compress with ipaddress if calling functions don't? + # is subnet mask always passed, and in the same way? - # cache new IP address which is assigned to interface - self._addr.append(addr) + # we can't have both DHCP and static IPv4 addresses assigned + for a in self._addr: + if ( ( addr == 'dhcp' and a != 'dhcpv6' and is_ipv4(a) ) or + ( a == 'dhcp' and addr != 'dhcpv6' and is_ipv4(addr) ) ): + raise ConfigError(( + "Can't configure both static IPv4 and DHCP address " + "on the same interface")) - # we can not have both DHCP and static IPv4 addresses assigned to an interface - if 'dhcp' in self._addr: - for addr in self._addr: - # do not change below 'if' ordering esle you will get an exception as: - # ValueError: 'dhcp' does not appear to be an IPv4 or IPv6 address - if addr != 'dhcp' and is_ipv4(addr): - raise ConfigError( - "Can't configure both static IPv4 and DHCP address on the same interface") + # do not add same address twice + if addr in self._addr: + return False + # add to interface if addr == 'dhcp': + self._addr.append(addr) self.dhcp.v4.set() - elif addr == 'dhcpv6': + return True + + if addr == 'dhcpv6': + self._addr.append(addr) self.dhcp.v6.set() - else: - if not is_intf_addr_assigned(self.config['ifname'], addr): - cmd = 'ip addr add "{}" dev "{}"'.format(addr, self.config['ifname']) - return self._cmd(cmd) + return True + + if not is_intf_addr_assigned(self.ifname, addr): + self._addr.append(addr) + self._cmd(f'ip addr add "{addr}" dev "{self.ifname}"') + return True + + return False def del_addr(self, addr): """ - Delete IP(v6) address to interface. Address is only added if it is - assigned to that interface. + Delete IP(v6) address from interface. Address is only deleted if it is + assigned to that interface. Address format must be exactly the same as + was used when adding the address. addr: can be an IPv4 address, IPv6 address, dhcp or dhcpv6! IPv4: delete IPv4 address from interface @@ -693,6 +680,7 @@ class Interface(Control): dhcp: stop dhclient (IPv4) on interface dhcpv6: stop dhclient (IPv6) on interface + Returns False if address isn't already assigned and wasn't deleted. Example: >>> from vyos.ifconfig import Interface >>> j = Interface('eth0') @@ -704,11 +692,35 @@ class Interface(Control): >>> j.get_addr() ['2001:db8::ffff/64'] """ + + # remove from cache (dhcp, and dhcpv6 can not be in it) + if addr in self._addr: + self._addr.remove(addr) + + # remove from interface if addr == 'dhcp': self.dhcp.v4.delete() - elif addr == 'dhcpv6': + return True + + if addr == 'dhcpv6': self.dhcp.v6.delete() - else: - if is_intf_addr_assigned(self.config['ifname'], addr): - cmd = 'ip addr del "{}" dev "{}"'.format(addr, self.config['ifname']) - return self._cmd(cmd) + return True + + if is_intf_addr_assigned(self.ifname, addr): + self._cmd(f'ip addr del "{addr}" dev "{self.ifname}"') + return True + + return False + + def flush_addrs(self): + """ + Flush all addresses from an interface, including DHCP. + + Will raise an exception on error. + """ + # stop DHCP(v6) if running + self.dhcp.v4.delete() + self.dhcp.v6.delete() + + # flush all addresses + self._cmd(f'ip addr flush dev "{self.ifname}"') |