Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Backport DAD handling to the stable branch #300

Open
wants to merge 7 commits into
base: stable
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 86 additions & 2 deletions ifupdown2/addons/address.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
#

import socket
import json
import time
import subprocess

from ipaddr import IPNetwork, IPv4Network, IPv6Network, _BaseV6

Expand Down Expand Up @@ -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'],
Expand All @@ -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'
Expand Down Expand Up @@ -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
Expand All @@ -351,6 +375,11 @@ 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')
if dad_attempts == "0":
nodad = True
raddessi marked this conversation as resolved.
Show resolved Hide resolved
self.ipcmd.addr_add(ifaceobj.name, newaddrs[addr_index],
newaddr_attrs.get(newaddrs[addr_index],
{}).get('broadcast'),
Expand All @@ -359,7 +388,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:
Expand Down Expand Up @@ -866,6 +896,12 @@ 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):
addrlist = ifaceobj.get_attr_value('address')
if any((":"in ip for ip in addrlist)):
self._settle_dad(ifaceobj, [ip for ip in addrlist if ":" in ip])
raddessi marked this conversation as resolved.
Show resolved Hide resolved

gateways = ifaceobj.get_attr_value('gateway')
if not gateways:
gateways = []
Expand Down Expand Up @@ -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 = ['%s/%s' % (x["local"], x["prefixlen"]) for x in addr_infos if x]
raddessi marked this conversation as resolved.
Show resolved Hide resolved
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]
1 change: 0 additions & 1 deletion ifupdown2/ifupdown/policymanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 7 additions & 3 deletions ifupdown2/ifupdownaddons/LinkUtils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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:
Expand Down Expand Up @@ -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
Expand All @@ -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))

Expand Down