diff --git a/.gitignore b/.gitignore index 23bc3fa4..d72cbc39 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,15 @@ wheels/ .installed.cfg *.egg MANIFEST +.pybuild/ +man/ + +# Debian packaging +debian/files +debian/.debhelper/ +debian/*.debhelper +debian/*.substvars +debian/ifupdown2 # PyInstaller # Usually these files are written by a python script from a template diff --git a/Makefile b/Makefile index e7e2bf4d..cf48426e 100644 --- a/Makefile +++ b/Makefile @@ -35,8 +35,12 @@ clean-build: ## remove build artifacts rm -fr build/ rm -fr dist/ rm -fr .eggs/ + rm -fr .pybuild/ + rm -fr man/ find . -name '*.egg-info' -exec rm -fr {} + find . -name '*.egg' -exec rm -f {} + + rm -fr debian/files debian/.debhelper debian/ifupdown2 debian/ifupdown2.substvars + find debian -name '*.debhelper' -exec rm -f {} + clean-pyc: ## remove Python file artifacts find . -name '*.pyc' -exec rm -f {} + diff --git a/etc/network/ifupdown2/addons.conf b/etc/network/ifupdown2/addons.conf index 726d63a0..67de25fe 100644 --- a/etc/network/ifupdown2/addons.conf +++ b/etc/network/ifupdown2/addons.conf @@ -15,6 +15,7 @@ pre-up,mstpctl pre-up,tunnel pre-up,vrf pre-up,ethtool +pre-up,auto pre-up,address up,dhcp up,address @@ -28,6 +29,7 @@ pre-down,usercmds pre-down,vxrd pre-down,dhcp down,ppp +down,auto down,addressvirtual down,address down,usercmds diff --git a/ifupdown2/addons/address.py b/ifupdown2/addons/address.py index 3d961032..132b1e79 100644 --- a/ifupdown2/addons/address.py +++ b/ifupdown2/addons/address.py @@ -194,7 +194,19 @@ class address(AddonWithIpBlackList, moduleBase): "validvals": ['on', 'off', 'yes', 'no', '0', '1'], "default": "no", "aliases": ["disable-ip6"] - } + }, + 'accept-ra': { + 'help': 'Accept IPv6 router advertisements', + 'validvals': ['0', '1', '2'], + 'default': '0', + 'example': ['accept-ra 1'] + }, + 'autoconf': { + 'help': 'Enable IPv6 slaac autoconfiguration', + 'validvals': ['0', '1'], + 'default': '0', + 'example': ['autoconf 1'] + }, } } @@ -272,6 +284,15 @@ def __init__(self, *args, **kargs): self.mac_regex = re.compile(r"^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$") + try: + self.default_accept_ra = str(self.sysctl_get('net.ipv6.conf.all.accept_ra')) + except Exception: + self.default_accept_ra = 1 + + try: + self.default_autoconf = str(self.sysctl_get('net.ipv6.conf.all.autoconf')) + except Exception: + self.default_autoconf = 1 def __policy_get_default_mtu(self): default_mtu = policymanager.policymanager_api.get_attr_default( @@ -682,21 +703,31 @@ def process_addresses(self, ifaceobj, ifaceobj_getfunc=None, force_reapply=False if force_reapply: self.__add_ip_addresses_with_attributes(ifaceobj, ifname, user_config_ip_addrs_list) return + + purge_dynamic_v6_addresses = True + running_autoconf = self.cache.get_link_inet6_autoconf(ifaceobj) + if running_autoconf == '1' and not squash_addr_config: + purge_dynamic_v6_addresses = False + try: - # if primary address is not same, there is no need to keep any, reset all addresses. - if ordered_user_configured_ips and running_ip_addrs and ordered_user_configured_ips[0] != running_ip_addrs[0]: - self.logger.info("%s: primary ip changed (from %s to %s) we need to purge all ip addresses and re-add them" - % (ifname, ordered_user_configured_ips[0], running_ip_addrs[0])) - skip_addrs = [] + # if primary ipv4 address is not same, there is no need to keep any, reset all ipv4 addresses. + if user_ip4 and running_ip_addrs and running_ip_addrs[0].version == 4 and user_ip4[0] != running_ip_addrs[0]: + self.logger.info("%s: primary ipv4 changed (from %s to %s) we need to purge all ipv4 addresses and re-add them" + % (ifname, user_ip4[0], running_ip_addrs[0])) + skip_addrs = user_ip6 else: skip_addrs = ordered_user_configured_ips if anycast_ip: skip_addrs.append(anycast_ip) + ip_flags = self.cache.get_ip_addresses_flags(ifname) for addr in running_ip_addrs: if addr in skip_addrs: continue + # don't purge dynamic ipv6 ip if autoconf is enabled + if addr.version == 6 and not purge_dynamic_v6_addresses and addr in ip_flags and not ip_flags[addr] & 0x80: + continue self.netlink.addr_del(ifname, addr) except Exception as e: self.log_warn(str(e)) @@ -923,14 +954,15 @@ def _process_mtu_config_mtu_none(self, ifaceobj, ifaceobj_getfunc): self._propagate_mtu_to_upper_devs(ifaceobj, self.default_mtu, self.default_mtu_int, ifaceobj_getfunc) def _set_bridge_forwarding(self, ifaceobj): - """ set ip forwarding to 0 if bridge interface does not have a - ip nor svi """ + """ Disable IP forwarding if bridge interface does not have a IP nor SVI. """ ifname = ifaceobj.name netconf_ipv4_forwarding = self.cache.get_netconf_forwarding(socket.AF_INET, ifname) netconf_ipv6_forwarding = self.cache.get_netconf_forwarding(socket.AF_INET6, ifname) - if not ifaceobj.upperifaces and not ifaceobj.get_attr_value('address') and (ifaceobj.addr_method and "dhcp" not in ifaceobj.addr_method): + if ( not ifaceobj.upperifaces and not ifaceobj.get_attr_value('address') and + ifaceobj.addr_method and "dhcp" not in ifaceobj.addr_method and "auto" not in ifaceobj.addr_method): + if netconf_ipv4_forwarding: self.sysctl_write_forwarding_value_to_proc(ifname, "ipv4", 0) if netconf_ipv6_forwarding: @@ -944,6 +976,41 @@ def _set_bridge_forwarding(self, ifaceobj): def sysctl_write_forwarding_value_to_proc(self, ifname, family, value): self.write_file("/proc/sys/net/%s/conf/%s/forwarding" % (family, ifname), "%s\n" % value) + def _sysctl_slaac(self, ifaceobj): + addr_method = ifaceobj.addr_method + if addr_method not in ["auto"]: + + try: + sysctl_ifname = '/'.join(ifaceobj.name.split(".")) + + running_accept_ra = self.cache.get_link_inet6_accept_ra(ifaceobj) + if running_accept_ra == '': + running_accept_ra = self.default_accept_ra + accept_ra = ifaceobj.get_attr_value_first('accept-ra') + if accept_ra is None: + accept_ra = self.default_accept_ra + + if running_accept_ra != accept_ra: + self.sysctl_set(f'net.ipv6.conf.{sysctl_ifname}.accept_ra', accept_ra) + self.cache.update_link_inet6_accept_ra(ifaceobj.name, accept_ra) + + running_autoconf = self.cache.get_link_inet6_autoconf(ifaceobj) + if running_autoconf == '': + running_autoconf = self.default_autoconf + autoconf = ifaceobj.get_attr_value_first('autoconf') + if autoconf is None: + autoconf = self.default_autoconf + + if running_autoconf != autoconf: + self.sysctl_set(f'net.ipv6.conf.{sysctl_ifname}.autoconf', autoconf) + self.cache.update_link_inet6_autoconf(ifaceobj.name, autoconf) + + except Exception as e: + if not setting_default_value: + ifaceobj.status = ifaceStatus.ERROR + self.logger.error('%s: %s' %(ifaceobj.name, str(e))) + + def _sysctl_config(self, ifaceobj): setting_default_value = False mpls_enable = ifaceobj.get_attr_value_first('mpls-enable'); @@ -970,7 +1037,7 @@ def _sysctl_config(self, ifaceobj): if (ifaceobj.link_kind & ifaceLinkKind.BRIDGE): self._set_bridge_forwarding(ifaceobj) - + self._sysctl_slaac(ifaceobj) if not self.syntax_check_sysctls(ifaceobj): return if not self.syntax_check_l3_svi_ip_forward(ifaceobj): @@ -1037,6 +1104,8 @@ def _sysctl_config(self, ifaceobj): ifaceobj.status = ifaceStatus.ERROR self.logger.error('%s: %s' %(ifaceobj.name, str(e))) + self._sysctl_slaac(ifaceobj) + def process_mtu(self, ifaceobj, ifaceobj_getfunc): if ifaceobj.link_privflags & ifaceLinkPrivFlags.OPENVSWITCH: @@ -1074,7 +1143,7 @@ def up_ipv6_addrgen(self, ifaceobj): # no need to go further during perfmode (boot) return - if not user_configured_ipv6_addrgen and ifaceobj.addr_method in ["dhcp", "ppp"]: + if not user_configured_ipv6_addrgen and ifaceobj.addr_method in ["dhcp", "ppp", "auto"]: return if not user_configured_ipv6_addrgen: @@ -1330,7 +1399,7 @@ def _down(self, ifaceobj, ifaceobj_getfunc=None): if not self.cache.link_exists(ifaceobj.name): return addr_method = ifaceobj.addr_method - if addr_method not in ["dhcp", "ppp"]: + if addr_method not in ["dhcp", "ppp", "auto"]: if ifaceobj.get_attr_value_first('address-purge')=='no': addrlist = ifaceobj.get_attr_value('address') for addr in addrlist or []: @@ -1451,6 +1520,22 @@ def _query_sysctl(self, ifaceobj, ifaceobjcurr): running_mpls_enable, mpls_enable != running_mpls_enable) + accept_ra = ifaceobj.get_attr_value_first('accept-ra') + if accept_ra: + running_accept_ra = self.cache.get_link_inet6_accept_ra(ifaceobj) + + ifaceobjcurr.update_config_with_status('accept_ra', + running_accept_ra, + accept_ra != running_accept_ra) + + autoconf = ifaceobj.get_attr_value_first('autoconf') + if autoconf: + running_autoconf = self.cache.get_link_inet6_autoconf(ifaceobj) + + ifaceobjcurr.update_config_with_status('autoconf', + running_autoconf, + autoconf != running_autoconf) + def query_check_ipv6_addrgen(self, ifaceobj, ifaceobjcurr): ipv6_addrgen = ifaceobj.get_attr_value_first('ipv6-addrgen') @@ -1517,7 +1602,7 @@ def _query_check(self, ifaceobj, ifaceobjcurr, ifaceobj_getfunc=None): def _query_check_address(self, ifaceobj, ifaceobjcurr, ifaceobj_getfunc): """ ifquery-check: attribute: "address" """ - if ifaceobj.addr_method in ["dhcp", "ppp"]: + if ifaceobj.addr_method in ["dhcp", "ppp", "auto"]: return if ifaceobj_getfunc: diff --git a/ifupdown2/addons/auto.py b/ifupdown2/addons/auto.py new file mode 100644 index 00000000..b54becdb --- /dev/null +++ b/ifupdown2/addons/auto.py @@ -0,0 +1,164 @@ +#!/usr/bin/env python3 +# + +import socket + +try: + from ifupdown2.lib.addon import Addon + from ifupdown2.lib.log import LogManager + + import ifupdown2.ifupdown.policymanager as policymanager + import ifupdown2.ifupdown.ifupdownflags as ifupdownflags + + from ifupdown2.ifupdown.iface import * + from ifupdown2.ifupdown.utils import utils + + from ifupdown2.ifupdownaddons.modulebase import moduleBase +except (ImportError, ModuleNotFoundError): + from lib.addon import Addon + from lib.log import LogManager + + import ifupdown.policymanager as policymanager + import ifupdown.ifupdownflags as ifupdownflags + + from ifupdown.iface import * + from ifupdown.utils import utils + + from ifupdownaddons.modulebase import moduleBase + + +class auto(Addon, moduleBase): + """ ifupdown2 addon module to configure SLAAC on inet6 interfaces """ + + def __init__(self, *args, **kargs): + Addon.__init__(self) + moduleBase.__init__(self, *args, **kargs) + + def syntax_check(self, ifaceobj, ifaceobj_getfunc): + return self.is_auto_allowed_on(ifaceobj, syntax_check=True) + + def is_auto_allowed_on(self, ifaceobj, syntax_check): + if ifaceobj.addr_method and 'auto' in ifaceobj.addr_method: + return utils.is_addr_ip_allowed_on(ifaceobj, syntax_check=True) + return True + + def _up(self, ifaceobj): + + if ifaceobj.link_privflags & ifaceLinkPrivFlags.KEEP_LINK_DOWN: + self.logger.info(f'{ifaceobj.name}: skipping auto configuration: link-down yes') + return + + try: + if 'inet6' in ifaceobj.addr_family: + sysctl_ifname = '/'.join(ifaceobj.name.split(".")) + + running_accept_ra = self.cache.get_link_inet6_accept_ra(ifaceobj) + if running_accept_ra != '2': + accept_ra = '2' + self.sysctl_set(f'net.ipv6.conf.{sysctl_ifname}.accept_ra', accept_ra) + self.cache.update_link_inet6_accept_ra(ifaceobj.name, accept_ra) + + running_autoconf = self.cache.get_link_inet6_autoconf(ifaceobj) + if running_autoconf != '1': + autoconf = '1' + self.sysctl_set(f'net.ipv6.conf.{sysctl_ifname}.autoconf', autoconf) + self.cache.update_link_inet6_autoconf(ifaceobj.name, autoconf) + + except Exception as e: + self.logger.error("%s: %s" % (ifaceobj.name, str(e))) + ifaceobj.set_status(ifaceStatus.ERROR) + + def _down(self, ifaceobj): + if 'inet6' in ifaceobj.addr_family: + self.cache.force_address_flush_family(ifaceobj.name, socket.AF_INET6) + self.netlink.link_down(ifaceobj.name) + + def _query_check(self, ifaceobj, ifaceobjcurr): + if not self.cache.link_exists(ifaceobj.name): + return + ifaceobjcurr.addr_family = ifaceobj.addr_family + ifaceobjcurr.addr_method = 'auto' + + inet6conf = self.cache.get_link_inet6_conf(ifaceobj.name) + if inet6conf['accept_ra'] == 2 and inet6conf['autoconf'] == 1: + ifaceobjcurr.status = ifaceStatus.SUCCESS + else: + ifaceobjcurr.status = ifaceStatus.ERROR + + def _query_running(self, ifaceobjrunning): + pass + + _run_ops = {'pre-up' : _up, + 'up' : _up, + 'down' : _down, + 'pre-down' : _down, + 'query-checkcurr' : _query_check, + 'query-running' : _query_running } + + def get_ops(self): + """ returns list of ops supported by this module """ + return list(self._run_ops.keys()) + + def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args): + """ Run DHCP configuration on the interface object passed as argument. + + Args: + **ifaceobj** (object): iface object + + **operation** (str): any of 'up', 'down', 'query-checkcurr', + 'query-running' + + Kwargs: + **query_ifaceobj** (object): Query check ifaceobject. This is only + valid when op is 'query-checkcurr'. It is an object same as + ifaceobj, but contains current attribute values and its config + status. The module can use it to return the queried running + state of interfaces. Returns 'SUCCESS' if the running state is + the same as user-required state in ifaceobj, 'ERROR' otherwise. + """ + op_handler = self._run_ops.get(operation) + if not op_handler: + return + try: + if (operation != 'query-running' and ifaceobj.addr_method != 'auto'): + return + except Exception: + return + if not self.is_auto_allowed_on(ifaceobj, syntax_check=False): + return + + log_manager = LogManager.get_instance() + + syslog_log_level = logging.INFO + disable_syslog_on_exit = None + + if operation in ["up", "down"]: + # if syslog is already enabled we shouldn't disable it + if log_manager.is_syslog_enabled(): + # save current syslog level + syslog_log_level = log_manager.get_syslog_log_level() + # prevent syslog from being disabled on exit + disable_syslog_on_exit = False + else: + # enabling syslog + log_manager.enable_syslog() + # syslog will be disabled once we are done + disable_syslog_on_exit = True + + # update the current syslog handler log level if higher than INFO + if syslog_log_level >= logging.INFO: + log_manager.set_level_syslog(logging.INFO) + + self.logger.info(f'{ifaceobj.name}: enabling syslog for auto configuration') + + try: + if operation == 'query-checkcurr': + op_handler(self, ifaceobj, query_ifaceobj) + else: + op_handler(self, ifaceobj) + finally: + # disable syslog handler or re-set the proper log-level + if disable_syslog_on_exit is True: + log_manager.get_instance().disable_syslog() + elif disable_syslog_on_exit is False: + log_manager.set_level_syslog(syslog_log_level) diff --git a/ifupdown2/addons/dhcp.py b/ifupdown2/addons/dhcp.py index 9b2f5f9b..cff81b40 100644 --- a/ifupdown2/addons/dhcp.py +++ b/ifupdown2/addons/dhcp.py @@ -204,20 +204,10 @@ def _up(self, ifaceobj): self.logger.info('dhclient6 already running on %s. ' 'Not restarting.' % ifaceobj.name) else: - accept_ra = ifaceobj.get_attr_value_first('accept_ra') - if accept_ra: - # XXX: Validate value - self.sysctl_set('net.ipv6.conf.%s' %ifaceobj.name + - '.accept_ra', accept_ra) - autoconf = ifaceobj.get_attr_value_first('autoconf') - if autoconf: - # XXX: Validate value - self.sysctl_set('net.ipv6.conf.%s' %ifaceobj.name + - '.autoconf', autoconf) - try: - self.dhclientcmd.stop6(ifaceobj.name, duid=dhcp6_duid) - except Exception: - pass + try: + self.dhclientcmd.stop6(ifaceobj.name, duid=dhcp6_duid) + except Exception: + pass #add delay before starting IPv6 dhclient to #make sure the configured interface/link is up. if timeout > 1: diff --git a/ifupdown2/ifupdown/iface.py b/ifupdown2/ifupdown/iface.py index 00725159..b60cb21f 100644 --- a/ifupdown2/ifupdown/iface.py +++ b/ifupdown2/ifupdown/iface.py @@ -289,6 +289,8 @@ def default(self, o): if o.addr_method: if 'inet' in o.addr_family and 'dhcp' in o.addr_method: retifacedict['addr_method'] = 'dhcp' + elif 'inet6' in o.addr_family and 'auto' in o.addr_method: + retifacedict['addr_method'] = 'auto' else: retifacedict['addr_method'] = o.addr_method if o.addr_family: @@ -851,6 +853,8 @@ def dump_pretty(self, with_status=False, use_realname=False): # both inet and inet6 addr_family if addr_method and family == 'inet' and 'dhcp' in addr_method: addr_method = 'dhcp' + elif addr_method and family == 'inet6' and 'auto' in addr_method: + addr_method = 'auto' self._dump_pretty(family, first, addr_method=addr_method, with_status=with_status, diff --git a/ifupdown2/ifupdown/networkinterfaces.py b/ifupdown2/ifupdown/networkinterfaces.py index c0c8ad96..37e73140 100644 --- a/ifupdown2/ifupdown/networkinterfaces.py +++ b/ifupdown2/ifupdown/networkinterfaces.py @@ -35,7 +35,7 @@ class networkInterfaces(): """ debian ifupdown /etc/network/interfaces file parser """ _addrfams = {'inet' : ['static', 'manual', 'loopback', 'dhcp', 'dhcp6', 'ppp', 'tunnel'], - 'inet6' : ['static', 'manual', 'loopback', 'dhcp', 'dhcp6', 'ppp', 'tunnel']} + 'inet6' : ['static', 'manual', 'loopback', 'dhcp', 'dhcp6', 'ppp', 'tunnel', 'auto']} # tunnel is part of the address family for backward compatibility but is not required. def __init__(self, interfacesfile='/etc/network/interfaces', diff --git a/ifupdown2/lib/nlcache.py b/ifupdown2/lib/nlcache.py index e2fd5681..e0235573 100644 --- a/ifupdown2/lib/nlcache.py +++ b/ifupdown2/lib/nlcache.py @@ -152,7 +152,7 @@ class _NetlinkCache: Address.IFA_ANYCAST, # Address.IFA_CACHEINFO, Address.IFA_MULTICAST, - # Address.IFA_FLAGS + Address.IFA_FLAGS ) def __init__(self): @@ -1179,6 +1179,52 @@ def get_link_ipv6_addrgen_mode(self, ifname): except TypeError as e: return self.__handle_type_error(inspect.currentframe().f_code.co_name, ifname, str(e), return_value=0) + def get_link_inet6_conf(self, ifname): + try: + with self._cache_lock: + return self._link_cache[ifname].attributes[Link.IFLA_AF_SPEC].value[socket.AF_INET6][Link.IFLA_INET6_CONF] + except (KeyError, AttributeError): + return False + except TypeError as e: + return self.__handle_type_error(inspect.currentframe().f_code.co_name, ifname, str(e), return_value=False) + + def get_link_inet6_accept_ra(self, ifaceobj): + inet6conf = self.get_link_inet6_conf(ifaceobj.name) + if inet6conf and 'accept_ra' in inet6conf: + accept_ra = str(inet6conf['accept_ra']) + else: + accept_ra = '' + return accept_ra + + def get_link_inet6_autoconf(self, ifaceobj): + inet6conf = self.get_link_inet6_conf(ifaceobj.name) + if inet6conf and 'autoconf' in inet6conf: + autoconf = str(inet6conf['autoconf']) + else: + autoconf = '' + return autoconf + + def update_link_inet6_accept_ra(self, ifname, accept_ra): + try: + with self._cache_lock: + try: + self._link_cache[ifname].attributes[Link.IFLA_AF_SPEC].value[socket.AF_INET6][Link.IFLA_INET6_CONF]['accept_ra'] = accept_ra + except Exception as e: + pass + except Exception: + pass + + def update_link_inet6_autoconf(self, ifname, autoconf): + try: + with self._cache_lock: + try: + self._link_cache[ifname].attributes[Link.IFLA_AF_SPEC].value[socket.AF_INET6][Link.IFLA_INET6_CONF]['autoconf'] = autoconf + except Exception as e: + pass + except Exception: + pass + + ##################################################### ##################################################### ##################################################### @@ -1745,6 +1791,21 @@ def get_ip_addresses(self, ifname: str) -> list: except (KeyError, AttributeError): return addresses + def get_ip_addresses_flags(self, ifname: str) -> dict: + addresses = {} + try: + with self._cache_lock: + intf_addresses = self._addr_cache[ifname] + for addr in intf_addresses.get(4, []): + addresses[addr.attributes[Address.IFA_ADDRESS].value] = addr.attributes[Address.IFA_FLAGS].value + + for addr in intf_addresses.get(6, []): + addresses[addr.attributes[Address.IFA_ADDRESS].value] = addr.attributes[Address.IFA_FLAGS].value + + return addresses + except (KeyError, AttributeError): + return addresses + def link_has_ip(self, ifname): try: with self._cache_lock: diff --git a/ifupdown2/man/interfaces.5.rst b/ifupdown2/man/interfaces.5.rst index 262d7265..ca461eae 100644 --- a/ifupdown2/man/interfaces.5.rst +++ b/ifupdown2/man/interfaces.5.rst @@ -106,6 +106,12 @@ METHODS The dhcp Method This method may be used to obtain an address via DHCP. + **inet6** address family interfaces can use the following method: + + The auto Method + This method may be used to obtain an address via SLAAC. + + BUILTIN INTERFACES ================== **iface** sections for some interfaces like physical interfaces or vlan @@ -131,6 +137,9 @@ EXAMPLES address 192.168.2.0/24 address 2001:dee:eeee:1::4/128 + auto eth3 + iface eth3 inet auto + # source files from a directory /etc/network/interfaces.d source /etc/network/interfaces.d/* diff --git a/ifupdown2/nlmanager/nlpacket.py b/ifupdown2/nlmanager/nlpacket.py index bf6c84fa..77afeb00 100644 --- a/ifupdown2/nlmanager/nlpacket.py +++ b/ifupdown2/nlmanager/nlpacket.py @@ -1691,6 +1691,71 @@ class AttributeIFLA_AF_SPEC(Attribute): Link.IFLA_BRIDGE_VLAN_INFO: (vflags, vlanid) } """ + + # from /usr/include/linux/ipv6.h + # index values for entries in Link.IFLA_INET6_CONF values + IPV6_DEVCONF_FORWARDING = 0 + IPV6_DEVCONF_HOPLIMIT = 1 + IPV6_DEVCONF_MTU6 = 2 + IPV6_DEVCONF_ACCEPT_RA = 3 + IPV6_DEVCONF_ACCEPT_REDIRECTS = 4 + IPV6_DEVCONF_AUTOCONF = 5 + IPV6_DEVCONF_DAD_TRANSMITS = 6 + IPV6_DEVCONF_RTR_SOLICITS = 7 + IPV6_DEVCONF_RTR_SOLICIT_INTERVAL = 8 + IPV6_DEVCONF_RTR_SOLICIT_DELAY = 9 + IPV6_DEVCONF_USE_TEMPADDR = 10 + IPV6_DEVCONF_TEMP_VALID_LFT = 11 + IPV6_DEVCONF_TEMP_PREFERED_LFT = 12 + IPV6_DEVCONF_REGEN_MAX_RETRY = 13 + IPV6_DEVCONF_MAX_DESYNC_FACTOR = 14 + IPV6_DEVCONF_MAX_ADDRESSES = 15 + IPV6_DEVCONF_FORCE_MLD_VERSION = 16 + IPV6_DEVCONF_ACCEPT_RA_DEFRTR = 17 + IPV6_DEVCONF_ACCEPT_RA_PINFO = 18 + IPV6_DEVCONF_ACCEPT_RA_RTR_PREF = 19 + IPV6_DEVCONF_RTR_PROBE_INTERVAL = 20 + IPV6_DEVCONF_ACCEPT_RA_RT_INFO_MAX_PLEN = 21 + IPV6_DEVCONF_PROXY_NDP = 22 + IPV6_DEVCONF_OPTIMISTIC_DAD = 23 + IPV6_DEVCONF_ACCEPT_SOURCE_ROUTE = 24 + IPV6_DEVCONF_MC_FORWARDING = 25 + IPV6_DEVCONF_DISABLE_IPV6 = 26 + IPV6_DEVCONF_ACCEPT_DAD = 27 + IPV6_DEVCONF_FORCE_TLLAO = 28 + IPV6_DEVCONF_NDISC_NOTIFY = 29 + IPV6_DEVCONF_MLDV1_UNSOLICITED_REPORT_INTERVAL = 30 + IPV6_DEVCONF_MLDV2_UNSOLICITED_REPORT_INTERVAL = 31 + IPV6_DEVCONF_SUPPRESS_FRAG_NDISC = 32 + IPV6_DEVCONF_ACCEPT_RA_FROM_LOCAL = 33 + IPV6_DEVCONF_USE_OPTIMISTIC = 34 + IPV6_DEVCONF_ACCEPT_RA_MTU = 35 + IPV6_DEVCONF_STABLE_SECRET = 36 + IPV6_DEVCONF_USE_OIF_ADDRS_ONLY = 37 + IPV6_DEVCONF_ACCEPT_RA_MIN_HOP_LIMIT = 38 + IPV6_DEVCONF_IGNORE_ROUTES_WITH_LINKDOWN = 39 + IPV6_DEVCONF_DROP_UNICAST_IN_L2_MULTICAST = 40 + IPV6_DEVCONF_DROP_UNSOLICITED_NA = 41 + IPV6_DEVCONF_KEEP_ADDR_ON_DOWN = 42 + IPV6_DEVCONF_RTR_SOLICIT_MAX_INTERVAL = 43 + IPV6_DEVCONF_SEG6_ENABLED = 44 + IPV6_DEVCONF_SEG6_REQUIRE_HMAC = 45 + IPV6_DEVCONF_ENHANCED_DAD = 46 + IPV6_DEVCONF_ADDR_GEN_MODE = 47 + IPV6_DEVCONF_DISABLE_POLICY = 48 + IPV6_DEVCONF_ACCEPT_RA_RT_INFO_MIN_PLEN = 49 + IPV6_DEVCONF_NDISC_TCLASS = 50 + IPV6_DEVCONF_RPL_SEG_ENABLED = 51 + IPV6_DEVCONF_RA_DEFRTR_METRIC = 52 + IPV6_DEVCONF_IOAM6_ENABLED = 53 + IPV6_DEVCONF_IOAM6_ID = 54 + IPV6_DEVCONF_IOAM6_ID_WIDE = 55 + IPV6_DEVCONF_NDISC_EVICT_NOCARRIER = 56 + IPV6_DEVCONF_ACCEPT_UNTRACKED_NA = 57 + IPV6_DEVCONF_ACCEPT_RA_MIN_LFT = 58 + IPV6_DEVCONF_FORCE_FORWARDING = 59 + + def __init__(self, atype, string, family, logger): Attribute.__init__(self, atype, string, logger) self.family = family @@ -1818,6 +1883,18 @@ def decode(self, parent_msg, data): */ """ + + # maps IFLA_INET6_CONF index to key name in result + ipv6_devconf_map = { + self.IPV6_DEVCONF_FORWARDING: 'forwarding', + self.IPV6_DEVCONF_HOPLIMIT: 'hop_limit', + self.IPV6_DEVCONF_MTU6: 'mtu6', + self.IPV6_DEVCONF_ACCEPT_RA: 'accept_ra', + self.IPV6_DEVCONF_ACCEPT_REDIRECTS: 'accept_redirects', + self.IPV6_DEVCONF_AUTOCONF: 'autoconf', + self.IPV6_DEVCONF_DISABLE_IPV6: 'disable_ipv6', + } + self.decode_length_type(data) self.value = {} @@ -1896,8 +1973,19 @@ def decode(self, parent_msg, data): (inet6_attr_length, inet6_attr_type) = unpack('=HH', sub_attr_data[:4]) inet6_attr_end = padded_length(inet6_attr_length) + if inet6_attr_type == Link.IFLA_INET6_CONF: + inet6conf_data = sub_attr_data[4:inet6_attr_end] + result = {} + + for index in ipv6_devconf_map: + offset = index * 4 + (value, undef) = unpack('=HH', inet6conf_data[offset:offset + 4]) + result[ipv6_devconf_map[index]] = value + + inet6_attr[inet6_attr_type] = result + # 1 byte attr - if inet6_attr_type == Link.IFLA_INET6_ADDR_GEN_MODE: + elif inet6_attr_type == Link.IFLA_INET6_ADDR_GEN_MODE: inet6_attr[inet6_attr_type] = self.decode_one_byte_attribute(sub_attr_data) # nlmanager doesn't support multiple kernel version