diff --git a/ifupdown2/addons/address.py b/ifupdown2/addons/address.py index f576dc02..5d6393ca 100644 --- a/ifupdown2/addons/address.py +++ b/ifupdown2/addons/address.py @@ -5,6 +5,9 @@ # import socket +import json +import time +import subprocess from ipaddr import IPNetwork, IPv4Network, IPv6Network, _BaseV6 @@ -111,6 +114,20 @@ class address(moduleBase): 'validvals': ['yes', 'no'], 'default' : 'no', 'example' : ['mpls-enable yes']}, + 'dad-attempts': { + 'help': 'Number of attempts to settle DAD (0 to disable DAD). ' + 'To use this feature, the ipv6_dad_handling_enabled ' + 'module global must be set to true', + 'example': ['dad-attempts 0'], + 'default': '60', + }, + 'dad-interval': { + 'help': 'DAD state polling interval in seconds. ' + 'To use this feature, the ipv6_dad_handling_enabled ' + 'module global must be set to true', + 'example': ['dad-interval 0.5'], + 'default': '0.1', + }, 'ipv6-addrgen': { 'help': 'enable disable ipv6 link addrgenmode', 'validvals': ['on', 'off'], @@ -137,6 +154,13 @@ def __init__(self, *args, **kargs): 'enable_l3_iface_forwarding_checks' ) ) + self.ipv6_dad_handling_enabled = utils.get_boolean_from_string( + policymanager.policymanager_api.get_module_globals( + self.__class__.__name__, + 'ipv6_dad_handling_enabled' + ), + default=False + ) if not self.default_mtu: self.default_mtu = '1500' @@ -338,7 +362,7 @@ def _inet_address_convert_to_cidr(self, ifaceobjlist): attrs = {} for a in ['broadcast', 'pointopoint', 'scope', - 'preferred-lifetime']: + 'preferred-lifetime', 'dad-attempts', 'dad-interval']: aval = ifaceobj.get_attr_value_n(a, addr_index) if aval: attrs[a] = aval @@ -351,6 +375,10 @@ def _inet_address_list_config(self, ifaceobj, newaddrs, newaddr_attrs): for addr_index in range(0, len(newaddrs)): try: if newaddr_attrs: + nodad = False + if self.ipv6_dad_handling_enabled and ifaceobj.addr_family[addr_index] == "inet6": + dad_attempts = newaddr_attrs.get(newaddrs[addr_index], {}).get('dad-attempts') + nodad = dad_attempts == "0" self.ipcmd.addr_add(ifaceobj.name, newaddrs[addr_index], newaddr_attrs.get(newaddrs[addr_index], {}).get('broadcast'), @@ -359,7 +387,8 @@ def _inet_address_list_config(self, ifaceobj, newaddrs, newaddr_attrs): newaddr_attrs.get(newaddrs[addr_index], {}).get('scope'), newaddr_attrs.get(newaddrs[addr_index], - {}).get('preferred-lifetime')) + {}).get('preferred-lifetime'), + nodad=nodad) else: self.ipcmd.addr_add(ifaceobj.name, newaddrs[addr_index]) except Exception, e: @@ -866,6 +895,13 @@ def _up(self, ifaceobj, ifaceobj_getfunc=None): self.up_hwaddress(ifaceobj) + # settle dad + if self.ipv6_dad_handling_enabled and self.ipcmd.link_exists(ifaceobj.name): + self._settle_dad( + ifaceobj, + [a for a in ifaceobj.get_attr_value('address') if ':' in a] + ) + gateways = ifaceobj.get_attr_value('gateway') if not gateways: gateways = [] @@ -1212,3 +1248,51 @@ def run(self, ifaceobj, operation, query_ifaceobj=None, ifaceobj_getfunc=None): else: op_handler(self, ifaceobj, ifaceobj_getfunc=ifaceobj_getfunc) + + def _settle_dad(self, ifaceobj, ips): + """ Settle dad for any given ips """ + def ip_addr_list(what): + raw = json.loads(utils.exec_commandl([ + 'ip', '-j', '-o', '-6', 'address', 'list', 'dev', + ifaceobj.name, what + ])) + addr_infos = (x for t in raw for x in t.get('addr_info', [])) + ip_list = ['{local}/{prefixlen}'.format(**x) for x in addr_infos if x] + return ip_list + + def get_param(key, default=None): + return (ifaceobj.get_attr_value_first(key) + or policymanager.policymanager_api.get_iface_default( + self.__class__.__name__, ifaceobj.name, key) + or default) + + interval = float(get_param('dad-interval', '0.1')) # 0.1: ifupdown default value + attempts = int(get_param('dad-attempts', '60')) # 60: ifupdown default value + if not attempts or not ips: + return + try: + + for _attempt in range(0, attempts): + tentative = ip_addr_list('tentative') + if all(str(ip) not in tentative for ip in ips): + break + time.sleep(interval) + else: + timeout = ','.join(ip for ip in ips if str(ip) not in tentative) + self.logger.warning('address: %s: dad timeout "%s"', ifaceobj.name, timeout) + return + failure = ip_addr_list('dadfailed') + if failure: + self.logger.warning('address: %s: dad failure "%s"', ifaceobj.name, ','.join(failure)) + except subprocess.CalledProcessError as exc: + self.logger.error('address: %s: could not settle dad %s', ifaceobj.name, str(exc)) + + def _get_ifaceobjs(self, ifaceobj, ifaceobj_getfunc): + squash_addr_config = ifupdownconfig.config.get("addr_config_squash", "0") == "1" + if not squash_addr_config: + return [ifaceobj] # no squash, returns current ifaceobj + if not ifaceobj.flags & ifaceobj.YOUNGEST_SIBLING: + return [] # when squash is present, work only on the youngest sibling + if ifaceobj.flags & iface.HAS_SIBLINGS: + return ifaceobj_getfunc(ifaceobj.name) # get sibling interfaces + return [ifaceobj] diff --git a/ifupdown2/ifupdown/policymanager.py b/ifupdown2/ifupdown/policymanager.py index 4dadc0d8..d7507457 100644 --- a/ifupdown2/ifupdown/policymanager.py +++ b/ifupdown2/ifupdown/policymanager.py @@ -177,7 +177,6 @@ def get_module_globals(self,module_name=None,attr=None): We first check the user_policy_array and return that value. But if the user did not specify an override, we use the system_policy_array. ''' - if (not attr or not module_name): return None # users can specify defaults to override the systemwide settings diff --git a/ifupdown2/ifupdownaddons/LinkUtils.py b/ifupdown2/ifupdownaddons/LinkUtils.py index 2b29bfeb..5195eab6 100644 --- a/ifupdown2/ifupdownaddons/LinkUtils.py +++ b/ifupdown2/ifupdownaddons/LinkUtils.py @@ -852,7 +852,8 @@ def link_show(ifacename=None): '-o', '-d', 'link', 'show']) def addr_add(self, ifacename, address, broadcast=None, - peer=None, scope=None, preferred_lifetime=None, metric=None): + peer=None, scope=None, preferred_lifetime=None, metric=None, + nodad=None): if not address: return cmd = 'addr add %s' % address @@ -869,6 +870,9 @@ def addr_add(self, ifacename, address, broadcast=None, if metric: cmd += ' metric %s' % metric + if nodad: + cmd += ' nodad' + if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause: self.add_to_batch(cmd) else: @@ -1022,7 +1026,7 @@ def compare_user_config_vs_running_state(running_addrs, user_addrs): return running_ipobj == (ip4 + ip6) - def addr_add_multiple(self, ifaceobj, ifacename, addrs, purge_existing=False, metric=None): + def addr_add_multiple(self, ifaceobj, ifacename, addrs, purge_existing=False, metric=None, nodad=False): # purges address if purge_existing: # if perfmode is not set and also if iface has no sibling @@ -1049,7 +1053,7 @@ def addr_add_multiple(self, ifaceobj, ifacename, addrs, purge_existing=False, me self.logger.warning('%s: %s' % (ifacename, str(e))) for a in addrs: try: - self.addr_add(ifacename, a, metric=metric) + self.addr_add(ifacename, a, metric=metric, nodad=nodad) except Exception, e: self.logger.error(str(e))