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

add support for ipv6 slaac #259

Open
wants to merge 33 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
7908b36
fix: address: purge all ips only if primary ipv4 changed
aderumier May 9, 2023
ffb0086
netlink: decode IFLA_INET6_CONF
aderumier Apr 26, 2023
c2c7408
nlcache: add get_link_inet6_conf
aderumier Apr 26, 2023
559be58
nlcache: add Address.IFA_FLAGS && get_ip_addresses_flags
aderumier May 10, 2023
dac15f1
add method auto support for inet6
Apr 26, 2023
a1342c7
add auto addons
Apr 26, 2023
70e515d
address: handle auto
Apr 26, 2023
0c62f83
address: add support for accept_ra && autoconf
aderumier Apr 26, 2023
4dd993b
dhcp: remove accept_ra && autoconf
aderumier Apr 27, 2023
579ddc8
address: process addresses with static + slaac
aderumier Apr 26, 2023
c136347
man: interfaces : add inet6 auto method
Apr 26, 2023
2ced29c
fix sysctl accept_ra/autoconf with dotted vlan interfaces
aderumier May 9, 2023
e6e323d
address: move sysctl accept_ra/autoconf code to _sysctl_config def
aderumier May 9, 2023
81e8567
add cache.get_link_inet6_accept_ra && autoconf, and fix parsing of em…
aderumier May 9, 2023
40da419
inet6 auto : default to accept_ra=2 like ifupdown1
aderumier May 9, 2023
ccabcbe
address: skip purge dynamic ipv6 address for autoconf instead accept_ra
aderumier May 9, 2023
0c63c7a
address: use default accept_ra|autoconf from net.ipv6.conf.all. sysctl
aderumier May 9, 2023
e189f1f
update nlcache after sysctl_set accept_ra/autoconf
aderumier May 10, 2023
0a52252
Merge branch 'CumulusNetworks:master' into ipv6auto
aderumier Jun 27, 2023
9b6e3e8
address: fix accept_ra/autoconf sysctl on bridges
Jun 27, 2023
721b225
address: fix description typo
Jun 27, 2023
9a5c4b7
ifupdown.argv: replace lockfile global by an arg
sohorx Jun 6, 2023
b8d74b8
set lanes in ethtool
May 31, 2022
7955c7e
addons: ethtool: reset link-speed on downed swps
julienfortin Nov 30, 2022
73ae401
ifupdown.utils: simplify expand_iface_range
sohorx Jun 12, 2023
218691b
ifupdown.utils: fix itf range in argument
sohorx Jun 12, 2023
64bff8a
networkinterfaces: make auto alias of allow-auto
sohorx Jun 12, 2023
a3daa35
networkinterfaces: make allow/auto behave the same
sohorx Jun 12, 2023
b41fbed
networkinterfaces: clean process_iface/vlan code
sohorx Jun 12, 2023
6f55b37
networkinterfaces: fix bad allow keyword
sohorx Jun 12, 2023
4868f73
addons: ethtool: add rx-vlan-filter
aderumier Jun 23, 2023
a7dc236
scheduler: import traceback
Jun 26, 2023
697d5e9
Merge branch 'ipv6auto' of github.com:aderumier/ifupdown2 into ipv6auto
Jun 27, 2023
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
2 changes: 2 additions & 0 deletions etc/network/ifupdown2/addons.conf
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -28,6 +29,7 @@ pre-down,usercmds
pre-down,vxrd
pre-down,dhcp
down,ppp
down,auto
down,addressvirtual
down,address
down,usercmds
Expand Down
104 changes: 95 additions & 9 deletions ifupdown2/addons/address.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,19 @@ class address(AddonWithIpBlackList, moduleBase):
'default': 'off',
'example': ['arp-accept on']
},
'accept-ra': {
'help': 'accept ipv6 router advertisement',
'validvals': ['0', '1', '2'],
'default': '0',
'example': ['accept-ra 1']
},
'autoconf': {
'help': 'enable ipv6 slaac autoconfiguratoin',
aderumier marked this conversation as resolved.
Show resolved Hide resolved
'validvals': ['0', '1'],
'default': '0',
'example': ['autoconf 1']
},

}
}

Expand Down Expand Up @@ -256,6 +269,16 @@ def __init__(self, *args, **kargs):
attr="check_l3_svi_ip_forwarding")
)

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(
module_name=self.__class__.__name__,
Expand Down Expand Up @@ -627,21 +650,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))
Expand Down Expand Up @@ -872,7 +905,9 @@ def _set_bridge_forwarding(self, ifaceobj):
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:
Expand Down Expand Up @@ -979,6 +1014,41 @@ def _sysctl_config(self, ifaceobj):
ifaceobj.status = ifaceStatus.ERROR
self.logger.error('%s: %s' %(ifaceobj.name, str(e)))

addr_method = ifaceobj.addr_method
if addr_method not in ["auto"]:

try:
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('net.ipv6.conf.%s.accept_ra'
%('/'.join(ifaceobj.name.split("."))),
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('net.ipv6.conf.%s.autoconf'
%('/'.join(ifaceobj.name.split("."))),
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 process_mtu(self, ifaceobj, ifaceobj_getfunc):

if ifaceobj.link_privflags & ifaceLinkPrivFlags.OPENVSWITCH:
Expand Down Expand Up @@ -1016,7 +1086,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:
Expand Down Expand Up @@ -1213,7 +1283,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 []:
Expand Down Expand Up @@ -1326,6 +1396,22 @@ def _query_sysctl(self, ifaceobj, ifaceobjcurr):
ifaceobjcurr.update_config_with_status('mpls-enable',
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)
return

def query_check_ipv6_addrgen(self, ifaceobj, ifaceobjcurr):
Expand Down Expand Up @@ -1380,7 +1466,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:
Expand Down
168 changes: 168 additions & 0 deletions ifupdown2/addons/auto.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
#!/usr/bin/env python3
#

import re
import time
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 interface """

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("%s: skipping auto configuration: link-down yes" % ifaceobj.name)
return

try:
if 'inet6' in ifaceobj.addr_family:
running_accept_ra = self.cache.get_link_inet6_accept_ra(ifaceobj)
if running_accept_ra != '2':
accept_ra = '2'
self.sysctl_set('net.ipv6.conf.%s.accept_ra'
%('/'.join(ifaceobj.name.split("."))),
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('net.ipv6.conf.%s.autoconf'
%('/'.join(ifaceobj.name.split("."))),
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 running attribute values and its config
status. The modules can use it to return queried running state
of interfaces. status is success if the running state is 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("%s: enabling syslog for auto configuration" % ifaceobj.name)

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)
18 changes: 4 additions & 14 deletions ifupdown2/addons/dhcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,20 +193,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:
Expand Down
Loading