From 007cae3525b2c43b7d114f2e471f2f01e86ffe5b Mon Sep 17 00:00:00 2001 From: Julien Fortin Date: Tue, 10 Jul 2018 00:18:39 +0200 Subject: [PATCH] ipv6-addrgen: add ifquery check/running/ifreload support using netlink cache [13:09:20] root:~ # ifquery -a auto lo iface lo inet loopback auto eth0 iface eth0 inet dhcp auto vlan1903 iface vlan1903 vlan-id 1903 vlan-raw-device bridge ipv6-addrgen no address-virtual-ipv6-addrgen no address-virtual 00:00:5e:00:01:a3 2a06:c01:1:1903::1/64 fe80::1/64 185.98.123.1/24 auto bridge iface bridge bridge-ports swp1 [13:09:25] root:~ # ifup -a -v info: loading builtin modules from ['/usr/share/ifupdown2/addons'] info: executing /var/lib/ifupdown2/hooks/get_reserved_vlan_range.sh info: executing /sbin/sysctl net.bridge.bridge-allow-multiple-vlans info: executing /bin/pidof mstpd info: executing /bin/ip rule show info: executing /bin/ip -6 rule show info: address: using default mtu 1500 info: 'link_master_slave' is set. slave admin state changes will be delayed till the masters admin state change. info: processing interfaces file /etc/network/interfaces info: lo: running ops ... info: netlink: ip link show info: netlink: ip addr show info: executing /bin/ip addr help info: address metric support: KO info: lo: netlink: ip link set dev lo up info: reading '/proc/sys/net/mpls/conf/lo/input' info: reading '/proc/sys/net/ipv4/conf/lo/forwarding' info: reading '/proc/sys/net/ipv6/conf/lo/forwarding' info: reading '/proc/sys/net/ipv4/conf/lo/accept_local' info: executing /bin/systemctl is-enabled vxrd.service info: eth0: running ops ... info: executing /sbin/ethtool eth0 info: reading '/sys/class/net/eth0/speed' info: reading '/sys/class/net/eth0/duplex' info: eth0: netlink: ip link set dev eth0 up info: dhclient4 already running on eth0. Not restarting. info: reading '/proc/sys/net/mpls/conf/eth0/input' info: reading '/proc/sys/net/ipv4/conf/eth0/forwarding' info: reading '/proc/sys/net/ipv6/conf/eth0/forwarding' info: reading '/proc/sys/net/ipv4/conf/eth0/accept_local' info: swp1: running ops ... info: executing /sbin/ethtool swp1 info: reading '/sys/class/net/swp1/speed' info: reading '/sys/class/net/swp1/duplex' info: executing /sbin/ethtool -s swp1 speed 1000 duplex full info: reading '/proc/sys/net/mpls/conf/swp1/input' info: reading '/proc/sys/net/ipv4/conf/swp1/accept_local' info: bridge: running ops ... info: bridge: netlink: ip link add bridge type bridge info: bridge: apply bridge settings info: bridge: set bridge-ageing 1800 info: bridge: set bridge-hashel 4096 info: bridge: set bridge-hashmax 4096 info: bridge: set bridge-mcstats on info: reading '/sys/class/net/bridge/bridge/stp_state' info: bridge: stp state reset, reapplying port settings info: bridge: netlink: ip link set bridge type bridge with attributes info: writing '1' to file /proc/sys/net/ipv6/conf/swp1/disable_ipv6 info: executing /bin/ip -force -batch - [link set dev swp1 master bridge addr flush dev swp1 ] info: bridge: applying bridge port configuration: ['swp1'] info: bridge: swp1: set bridge-portprios 8 info: swp1: netlink: ip link set dev swp1: bridge slave attributes info: executing /sbin/brctl showmcqv4src bridge info: bridge: applying bridge configuration specific to ports info: bridge: processing bridge config for port swp1 info: swp1: netlink: ip link set dev swp1 up info: bridge: setting bridge mac to port swp1 mac info: executing /bin/ip link set dev bridge address 90:e2:ba:2c:b1:96 info: executing /sbin/mstpctl showportdetail bridge json info: executing /sbin/mstpctl showbridge json bridge info: bridge: applying mstp configuration specific to ports info: bridge: processing mstp config for port swp1 info: bridge: netlink: ip link set dev bridge up info: reading '/proc/sys/net/mpls/conf/bridge/input' info: executing /sbin/sysctl net.ipv4.conf.bridge.forwarding info: executing /sbin/sysctl net.ipv6.conf.bridge.forwarding info: executing /bin/ip -force -batch - [link set dev bridge down link set dev bridge addrgenmode eui64 link set dev bridge up ] info: reading '/proc/sys/net/ipv4/conf/bridge/accept_local' info: vlan1903: running ops ... info: vlan1903: netlink: ip link add link bridge name vlan1903 type vlan id 1903 protocol 802.1q info: vlan1903: netlink: ip link set dev vlan1903 up info: reading '/proc/sys/net/mpls/conf/vlan1903/input' info: reading '/proc/sys/net/ipv4/conf/vlan1903/forwarding' info: reading '/proc/sys/net/ipv6/conf/vlan1903/forwarding' info: executing /bin/ip -force -batch - [link set dev vlan1903 down link set dev vlan1903 addrgenmode none link set dev vlan1903 up ] info: vlan1903: netlink: ip link add link vlan1903 name vlan1903-v0 type macvlan mode private info: executing /sbin/sysctl net.ipv6.conf.vlan1903-v0.accept_dad info: executing /sbin/sysctl net.ipv6.conf.vlan1903-v0.accept_dad=0 info: executing /sbin/sysctl net.ipv6.conf.vlan1903-v0.dad_transmits info: executing /sbin/sysctl net.ipv6.conf.vlan1903-v0.dad_transmits=0 info: executing /bin/ip -force -batch - [link set dev vlan1903-v0 addrgenmode none link set dev vlan1903-v0 down link set dev vlan1903-v0 address 00:00:5e:00:01:a3 link set dev vlan1903-v0 up addr add 2a06:c01:1:1903::1/64 dev vlan1903-v0 addr add fe80::1/64 dev vlan1903-v0 addr add 185.98.123.1/24 dev vlan1903-v0 route del 2a06:c01:1:1903::/64 dev vlan1903-v0 route del fe80::/64 dev vlan1903-v0 route add 2a06:c01:1:1903::/64 dev vlan1903-v0 proto kernel metric 9999 route add fe80::/64 dev vlan1903-v0 proto kernel metric 9999 ] info: reading '/proc/sys/net/ipv4/conf/vlan1903/accept_local' [13:09:29] root:~ # [13:09:30] root:~ # [13:09:30] root:~ # ifquery -a -c auto lo iface lo inet loopback auto eth0 iface eth0 inet dhcp [pass] auto vlan1903 iface vlan1903 [pass] vlan-raw-device bridge [pass] vlan-id 1903 [pass] ipv6-addrgen no [pass] address-virtual 00:00:5e:00:01:a3 2a06:c01:1:1903::1/64 fe80::1/64 185.98.123.1/24 [pass] address-virtual-ipv6-addrgen no [pass] auto bridge iface bridge [pass] bridge-ports swp1 [pass] [13:09:33] root:~ # [13:09:35] root:~ # [13:09:35] root:~ # ifquery -a -r auto vlan1903-v0 iface vlan1903-v0 ipv6-addrgen off address 185.98.123.1/24 address 2a06:c01:1:1903::1/64 auto vlan1903 iface vlan1903 vlan-id 1903 vlan-protocol 802.1Q vlan-raw-device bridge ipv6-addrgen off address-virtual 00:00:5e:00:01:a3 185.98.123.1/242a06:c01:1:1903::1/64 address-virtual-ipv6-addrgen off auto bridge iface bridge bridge-vlan-stats off bridge-mcstats 1 bridge-ports swp1 bridge-stp yes mstpctl-portp2p swp1=auto mstpctl-treeportcost swp1=20000 mstpctl-portautoedge swp1=yes auto swp1 iface swp1 auto eth0 iface eth0 inet dhcp auto lo iface lo inet loopback mtu 65536 [13:09:38] root:~ # ip -d link show vlan1903 20: vlan1903@bridge: mtu 1500 qdisc noqueue state UP mode DEFAULT group default link/ether 90:e2:ba:2c:b1:96 brd ff:ff:ff:ff:ff:ff promiscuity 1 vlan protocol 802.1Q id 1903 addrgenmode none [13:09:50] root:~ # ip -d link show vlan1903-v0 21: vlan1903-v0@vlan1903: mtu 1500 qdisc noqueue state UP mode DEFAULT group default link/ether 00:00:5e:00:01:a3 brd ff:ff:ff:ff:ff:ff promiscuity 0 macvlan mode private addrgenmode none [13:09:53] root:~ # [13:09:56] root:~ # ip link set dev vlan1903-v0 addrgenmode eui64 [13:10:23] root:~ # ifquery -a -c auto lo iface lo inet loopback auto eth0 iface eth0 inet dhcp [pass] auto vlan1903 iface vlan1903 [fail] vlan-raw-device bridge [pass] vlan-id 1903 [pass] ipv6-addrgen no [pass] address-virtual 00:00:5e:00:01:a3 2a06:c01:1:1903::1/64 fe80::1/64 185.98.123.1/24 [pass] address-virtual-ipv6-addrgen no [fail] auto bridge iface bridge [pass] bridge-ports swp1 [pass] [13:10:29] root:~ # ifquery -a -r auto vlan1903-v0 iface vlan1903-v0 address 185.98.123.1/24 address 2a06:c01:1:1903::1/64 auto vlan1903 iface vlan1903 vlan-id 1903 vlan-protocol 802.1Q vlan-raw-device bridge ipv6-addrgen off address-virtual 00:00:5e:00:01:a3 185.98.123.1/242a06:c01:1:1903::1/64 address-virtual-ipv6-addrgen on auto bridge iface bridge bridge-vlan-stats off bridge-mcstats 1 bridge-ports swp1 bridge-stp yes mstpctl-portp2p swp1=auto mstpctl-treeportcost swp1=20000 mstpctl-portautoedge swp1=yes auto swp1 iface swp1 auto eth0 iface eth0 inet dhcp auto lo iface lo inet loopback mtu 65536 [13:10:33] root:~ # Signed-off-by: Julien Fortin --- ifupdown2/addons/address.py | 60 +++++++++++++++++++++++-- ifupdown2/addons/addressvirtual.py | 64 +++++++++++++++++++++------ ifupdown2/ifupdown/netlink.py | 5 +++ ifupdown2/ifupdownaddons/LinkUtils.py | 29 +++++++++++- 4 files changed, 140 insertions(+), 18 deletions(-) diff --git a/ifupdown2/addons/address.py b/ifupdown2/addons/address.py index 6aea606..e723d8b 100644 --- a/ifupdown2/addons/address.py +++ b/ifupdown2/addons/address.py @@ -4,6 +4,8 @@ # Author: Roopa Prabhu, roopa@cumulusnetworks.com # +import socket + from ipaddr import IPNetwork, IPv4Network, IPv6Network, _BaseV6 try: @@ -751,9 +753,34 @@ class address(moduleBase): self._process_mtu_config(ifaceobj, ifaceobj_getfunc, mtu) def up_ipv6_addrgen(self, ifaceobj): - ipv6_addrgen = ifaceobj.get_attr_value_first('ipv6-addrgen') - if ipv6_addrgen: - self.ipcmd.ipv6_addrgen(ifaceobj.name, utils.get_boolean_from_string(ipv6_addrgen)) + user_configured_ipv6_addrgen = ifaceobj.get_attr_value_first('ipv6-addrgen') + + if not user_configured_ipv6_addrgen: + for old_ifaceobj in statemanager.statemanager_api.get_ifaceobjs(ifaceobj.name) or []: + old_config_ipv6_addrgen = old_ifaceobj.get_attr_value_first('ipv6-addrgen') + + if old_config_ipv6_addrgen: + user_configured_ipv6_addrgen = self.get_attr_default_value('ipv6-addrgen') + break + + if not user_configured_ipv6_addrgen: + # no previous config detected we dont have to configure ipv6-addrgen + return + + ipv6_addrgen_nl = { + 'on': 0, + 'yes': 0, + '0': 0, + 'off': 1, + 'no': 1, + '1': 1 + }.get(user_configured_ipv6_addrgen.lower(), None) + + if ipv6_addrgen_nl is not None: + self.ipcmd.ipv6_addrgen(ifaceobj.name, ipv6_addrgen_nl, link_created=True) + # link_create=False will flush the addr cache of that intf + else: + self.logger.warning('%s: invalid value "%s" for attribute ipv6-addrgen' % (ifaceobj.name, user_configured_ipv6_addrgen)) def _up(self, ifaceobj, ifaceobj_getfunc=None): if not self.ipcmd.link_exists(ifaceobj.name): @@ -958,11 +985,29 @@ class address(moduleBase): mpls_enable != running_mpls_enable) return + def query_check_ipv6_addrgen(self, ifaceobj, ifaceobjcurr): + ipv6_addrgen = ifaceobj.get_attr_value_first('ipv6-addrgen') + + if not ipv6_addrgen: + return + + if ipv6_addrgen in utils._string_values: + ifaceobjcurr.update_config_with_status( + 'ipv6-addrgen', + ipv6_addrgen, + utils.get_boolean_from_string(ipv6_addrgen) == self.ipcmd.get_ipv6_addrgen_mode(ifaceobj.name) + ) + else: + ifaceobjcurr.update_config_with_status('ipv6-addrgen', ipv6_addrgen, 1) + def _query_check(self, ifaceobj, ifaceobjcurr, ifaceobj_getfunc=None): runningaddrsdict = None if not self.ipcmd.link_exists(ifaceobj.name): self.logger.debug('iface %s not found' %ifaceobj.name) return + + self.query_check_ipv6_addrgen(ifaceobj, ifaceobjcurr) + addr_method = ifaceobj.addr_method self.query_n_update_ifaceobjcurr_attr(ifaceobj, ifaceobjcurr, 'mtu', self.ipcmd.link_get_mtu) @@ -1036,10 +1081,19 @@ class address(moduleBase): #XXXX Check broadcast address, scope, etc return + def query_running_ipv6_addrgen(self, ifaceobjrunning): + ipv6_addrgen = self.ipcmd.get_ipv6_addrgen_mode(ifaceobjrunning.name) + + if ipv6_addrgen: + ifaceobjrunning.update_config('ipv6-addrgen', 'off') + def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None): if not self.ipcmd.link_exists(ifaceobjrunning.name): self.logger.debug('iface %s not found' %ifaceobjrunning.name) return + + self.query_running_ipv6_addrgen(ifaceobjrunning) + dhclientcmd = dhclient() if (dhclientcmd.is_running(ifaceobjrunning.name) or dhclientcmd.is_running6(ifaceobjrunning.name)): diff --git a/ifupdown2/addons/addressvirtual.py b/ifupdown2/addons/addressvirtual.py index c5a7c2f..e997415 100644 --- a/ifupdown2/addons/addressvirtual.py +++ b/ifupdown2/addons/addressvirtual.py @@ -6,6 +6,7 @@ import os import glob +import socket from ipaddr import IPNetwork, IPv6Network @@ -203,7 +204,17 @@ class addressvirtual(moduleBase): ipv6_addrgen = ifaceobj.get_attr_value_first('address-virtual-ipv6-addrgen') if ipv6_addrgen: - return True, utils.get_boolean_from_string(ipv6_addrgen) + # IFLA_INET6_ADDR_GEN_MODE values: + # 0 = eui64 + # 1 = none + ipv6_addrgen_nl = {'on': 0, 'yes': 0, '0': 0, 'off': 1, 'no': 1, '1': 1}.get(ipv6_addrgen.lower(), None) + + if ipv6_addrgen_nl is None: + self.logger.warning('%s: value "%s" not allowed for attribute "ipv6-addrgen"' % (ifaceobj.name, user_configured_ipv6_addrgen)) + + self.logger.warning('%s: invalid value "%s" for attribute address-virtual-ipv6-addrgen' % (ifaceobj.name, ipv6_addrgen)) + else: + return True, ipv6_addrgen_nl return False, None @@ -215,7 +226,7 @@ class addressvirtual(moduleBase): if ifaceobj.lowerifaces and address_virtual_list: update_mtu = True - should_configure_ipv6_addrgen, ipv6_addrgen_user_value = self.get_addressvirtual_ipv6_addrgen_user_conf(ifaceobj) + user_configured_ipv6_addrgenmode, ipv6_addrgen_user_value = self.get_addressvirtual_ipv6_addrgen_user_conf(ifaceobj) hwaddress = [] self.ipcmd.batch_start() @@ -243,20 +254,13 @@ class addressvirtual(moduleBase): except: self.ipcmd.link_add_macvlan(ifaceobj.name, macvlan_ifacename) link_created = True - else: - if should_configure_ipv6_addrgen: - # When setting addrgenmode it is necessary to flap the macvlan - # device. After flapping the device we also need to re-add all - # the user configuration. The best way to add the user config - # is to flush our internal address cache - self.ipcmd.reset_addr_cache(macvlan_ifacename) # first thing we need to handle vrf enslavement if (ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE): self._handle_vrf_slaves(macvlan_ifacename, ifaceobj) - if should_configure_ipv6_addrgen: - self.ipcmd.ipv6_addrgen(macvlan_ifacename, ipv6_addrgen_user_value) + if user_configured_ipv6_addrgenmode: + self.ipcmd.ipv6_addrgen(macvlan_ifacename, ipv6_addrgen_user_value, link_created) ips = av_attrs[1:] if mac != 'None': @@ -486,6 +490,13 @@ class addressvirtual(moduleBase): return if not self.ipcmd.link_exists(ifaceobj.name): return + + user_config_address_virtual_ipv6_addr = ifaceobj.get_attr_value_first('address-virtual-ipv6-addrgen') + if user_config_address_virtual_ipv6_addr and user_config_address_virtual_ipv6_addr not in utils._string_values: + ifaceobjcurr.update_config_with_status('address-virtual-ipv6-addrgen', user_config_address_virtual_ipv6_addr, 1) + user_config_address_virtual_ipv6_addr = None + macvlans_running_ipv6_addr = [] + av_idx = 0 macvlan_prefix = self._get_macvlan_prefix(ifaceobj) for address_virtual in address_virtual_list: @@ -503,6 +514,10 @@ class addressvirtual(moduleBase): '', 1) av_idx += 1 continue + + if user_config_address_virtual_ipv6_addr: + macvlans_running_ipv6_addr.append(self.ipcmd.get_ipv6_addrgen_mode(macvlan_ifacename)) + # Check mac and ip address rhwaddress = self.ipcmd.link_get_hwaddress(macvlan_ifacename) raddrs = self.ipcmd.get_running_addrs( @@ -537,11 +552,19 @@ class addressvirtual(moduleBase): ifaceobjcurr.update_config_with_status('address-virtual', raddress_virtual, 1) av_idx += 1 - return + + if user_config_address_virtual_ipv6_addr: + bool_user_ipv6_addrgen = utils.get_boolean_from_string(user_config_address_virtual_ipv6_addr) + for running_ipv6_addrgen in macvlans_running_ipv6_addr: + if (not bool_user_ipv6_addrgen) != running_ipv6_addrgen: + ifaceobjcurr.update_config_with_status('address-virtual-ipv6-addrgen', user_config_address_virtual_ipv6_addr, 1) + return + ifaceobjcurr.update_config_with_status('address-virtual-ipv6-addrgen', user_config_address_virtual_ipv6_addr, 0) def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None): macvlan_prefix = self._get_macvlan_prefix(ifaceobjrunning) address_virtuals = glob.glob("/sys/class/net/%s*" %macvlan_prefix) + macvlans_ipv6_addrgen_list = [] for av in address_virtuals: macvlan_ifacename = os.path.basename(av) rhwaddress = self.ipcmd.link_get_hwaddress(macvlan_ifacename) @@ -552,7 +575,22 @@ class addressvirtual(moduleBase): raddress = [] ifaceobjrunning.update_config('address-virtual', '%s %s' %(rhwaddress, ''.join(raddress))) - return + + macvlans_ipv6_addrgen_list.append((macvlan_ifacename, self.ipcmd.get_ipv6_addrgen_mode(macvlan_ifacename))) + + macvlan_count = len(address_virtuals) + if not macvlan_count: + return + ipv6_addrgen = macvlans_ipv6_addrgen_list[0][1] + + for macvlan_ifname, macvlan_ipv6_addrgen in macvlans_ipv6_addrgen_list: + if macvlan_ipv6_addrgen != ipv6_addrgen: + # one macvlan has a different ipv6-addrgen configuration + # we simply return, ifquery-running will print the macvlan + # stanzas with the ipv6-addrgen on/off attribute + return + ifaceobjrunning.update_config('address-virtual-ipv6-addrgen', 'off' if ipv6_addrgen else 'on') + _run_ops = {'up' : _up, 'down' : _down, diff --git a/ifupdown2/ifupdown/netlink.py b/ifupdown2/ifupdown/netlink.py index 65ce356..c7b0b76 100644 --- a/ifupdown2/ifupdown/netlink.py +++ b/ifupdown2/ifupdown/netlink.py @@ -335,6 +335,11 @@ class Netlink(utilsBase): 'attr': Link.IFLA_OPERSTATE, 'name': 'state', 'func': lambda x: '0%x' % int(x) if x > len(Link.oper_to_string) else Link.oper_to_string[x][8:] + }, + { + 'attr': Link.IFLA_AF_SPEC, + 'name': 'af_spec', + 'func': dict } ] diff --git a/ifupdown2/ifupdownaddons/LinkUtils.py b/ifupdown2/ifupdownaddons/LinkUtils.py index 6fb42e2..1b886e2 100644 --- a/ifupdown2/ifupdownaddons/LinkUtils.py +++ b/ifupdown2/ifupdownaddons/LinkUtils.py @@ -10,6 +10,7 @@ import re import glob import shlex import signal +import socket import subprocess from ipaddr import IPNetwork, IPv6Network @@ -2645,8 +2646,32 @@ class LinkUtils(utilsBase): except: pass - def ipv6_addrgen(self, ifname, addrgen): - cmd = 'link set dev %s addrgenmode %s' % (ifname, 'eui64' if addrgen else 'none') + def get_ipv6_addrgen_mode(self, ifname): + try: + return self._cache_get('link', [ifname, 'af_spec', socket.AF_INET6])[Link.IFLA_INET6_ADDR_GEN_MODE] + except: + # default to 0 (eui64) + return 0 + + def ipv6_addrgen(self, ifname, addrgen, link_created): + try: + # IFLA_INET6_ADDR_GEN_MODE values: + # 0 = eui64 + # 1 = none + if self._link_cache_get([ifname, 'af_spec', socket.AF_INET6])[Link.IFLA_INET6_ADDR_GEN_MODE] == addrgen: + self.logger.debug('%s: ipv6 addrgen already %s' % (ifname, 'off' if addrgen else 'on')) + return + except: + pass + + if not link_created: + # When setting addrgenmode it is necessary to flap the macvlan + # device. After flapping the device we also need to re-add all + # the user configuration. The best way to add the user config + # is to flush our internal address cache + self.reset_addr_cache(ifname) + + cmd = 'link set dev %s addrgenmode %s' % (ifname, 'none' if addrgen else 'eui64') is_link_up = self.is_link_up(ifname)