[pve-devel] [PATCH ifupdown2 4/6] patch: add ipv6 slaac support upstream patch

Alexandre Derumier aderumier at odiso.com
Tue May 16 00:47:08 CEST 2023


Signed-off-by: Alexandre Derumier <aderumier at odiso.com>
---
 debian/patches/series                         |   2 +-
 ...6-slaac-support-inet6-auto-accept_ra.patch | 626 ++++++++++++++++++
 2 files changed, 627 insertions(+), 1 deletion(-)
 create mode 100644 debian/patches/upstream/0001-add-ipv6-slaac-support-inet6-auto-accept_ra.patch

diff --git a/debian/patches/series b/debian/patches/series
index d548398..8edf054 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -10,4 +10,4 @@ pve/0009-postinst-rm-update-network-config-compatibility.patch
 pve/0010-d-rules-drop-now-default-with-systemd.patch
 pve/0011-d-rules-add-dh_installsystemd-override-for-compat-12.patch
 pve/0012-postinst-reload-network-config-on-first-install.patch
-
+upstream/0001-add-ipv6-slaac-support-inet6-auto-accept_ra.patch
\ No newline at end of file
diff --git a/debian/patches/upstream/0001-add-ipv6-slaac-support-inet6-auto-accept_ra.patch b/debian/patches/upstream/0001-add-ipv6-slaac-support-inet6-auto-accept_ra.patch
new file mode 100644
index 0000000..1e7e126
--- /dev/null
+++ b/debian/patches/upstream/0001-add-ipv6-slaac-support-inet6-auto-accept_ra.patch
@@ -0,0 +1,626 @@
+From ac2462f9426fcfcecf3e9d9647c5bb128b44a111 Mon Sep 17 00:00:00 2001
+From: Alexandre Derumier <aderumier at odiso.com>
+Date: Tue, 9 May 2023 17:48:14 +0200
+Subject: [PATCH] add ipv6 slaac support (inet6 auto && accept_ra)
+
+This should fix a lot of users request in the forum,
+and also fix upgrade from ifupdown1 to ifupdown2 if user have "inet6 auto" in configuration.
+(default on stock debian install, this break pbs install on top of stock debian)
+
+upstream pull request:
+
+https://github.com/CumulusNetworks/ifupdown2/pull/259
+Signed-off-by: Alexandre Derumier <aderumier at odiso.com>
+---
+ etc/network/ifupdown2/addons.conf       |   2 +
+ ifupdown2/addons/address.py             | 104 +++++++++++++--
+ ifupdown2/addons/auto.py                | 168 ++++++++++++++++++++++++
+ ifupdown2/addons/dhcp.py                |  18 +--
+ ifupdown2/ifupdown/iface.py             |   4 +
+ ifupdown2/ifupdown/networkinterfaces.py |   2 +-
+ ifupdown2/lib/nlcache.py                |  63 ++++++++-
+ ifupdown2/man/interfaces.5.rst          |   9 ++
+ ifupdown2/nlmanager/nlpacket.py         |  24 +++-
+ 9 files changed, 368 insertions(+), 26 deletions(-)
+ create mode 100644 ifupdown2/addons/auto.py
+
+diff --git a/etc/network/ifupdown2/addons.conf b/etc/network/ifupdown2/addons.conf
+index 726d63a..67de25f 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 e71a26f..11e4512 100644
+--- a/ifupdown2/addons/address.py
++++ b/ifupdown2/addons/address.py
+@@ -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',
++                'validvals': ['0', '1'],
++                'default': '0',
++                'example': ['autoconf 1']
++            },
++
+         }
+     }
+ 
+@@ -256,6 +269,16 @@ class address(AddonWithIpBlackList, moduleBase):
+             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__,
+@@ -627,21 +650,31 @@ class address(AddonWithIpBlackList, moduleBase):
+                 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))
+@@ -872,7 +905,9 @@ class address(AddonWithIpBlackList, moduleBase):
+         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:
+@@ -979,6 +1014,41 @@ class address(AddonWithIpBlackList, moduleBase):
+                        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:
+@@ -1016,7 +1086,7 @@ class address(AddonWithIpBlackList, moduleBase):
+             # 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:
+@@ -1213,7 +1283,7 @@ class address(AddonWithIpBlackList, moduleBase):
+             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 []:
+@@ -1326,6 +1396,22 @@ class address(AddonWithIpBlackList, moduleBase):
+             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):
+@@ -1380,7 +1466,7 @@ class address(AddonWithIpBlackList, moduleBase):
+ 
+     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 0000000..02e6ca4
+--- /dev/null
++++ b/ifupdown2/addons/auto.py
+@@ -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)
+diff --git a/ifupdown2/addons/dhcp.py b/ifupdown2/addons/dhcp.py
+index a5bf860..22bbdb4 100644
+--- a/ifupdown2/addons/dhcp.py
++++ b/ifupdown2/addons/dhcp.py
+@@ -193,20 +193,10 @@ class dhcp(Addon, moduleBase):
+                     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 07bd067..325e6c3 100644
+--- a/ifupdown2/ifupdown/iface.py
++++ b/ifupdown2/ifupdown/iface.py
+@@ -289,6 +289,8 @@ class ifaceJsonEncoder(json.JSONEncoder):
+         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:
+@@ -843,6 +845,8 @@ class iface():
+                 # 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 2bebe39..3803590 100644
+--- a/ifupdown2/ifupdown/networkinterfaces.py
++++ b/ifupdown2/ifupdown/networkinterfaces.py
+@@ -30,7 +30,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 0b1c6d2..0d2f624 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 @@ class _NetlinkCache:
+         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 @@ class _NetlinkCache:
+         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 262d726..ca461ea 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 8972c76..0090529 100644
+--- a/ifupdown2/nlmanager/nlpacket.py
++++ b/ifupdown2/nlmanager/nlpacket.py
+@@ -1818,6 +1818,15 @@ class AttributeIFLA_AF_SPEC(Attribute):
+          */
+ 
+         """
++        #only first attributes used in any kernel.
++        ipv6_devconf = ['forwarding',
++                        'hop_limit',
++                        'mtu6',
++                        'accept_ra',
++                        'accept_redirects',
++                        'autoconf',
++        ]
++
+         self.decode_length_type(data)
+         self.value = {}
+ 
+@@ -1896,8 +1905,21 @@ class AttributeIFLA_AF_SPEC(Attribute):
+                         (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]
++                            index = 0
++                            result = {}
++                            while inet6conf_data:
++                                (value, undef) = unpack('=HH', inet6conf_data[:4])
++                                result[ipv6_devconf[index]] = value
++                                inet6conf_data = inet6conf_data[4:]
++                                index = index + 1
++                                if index >= len(ipv6_devconf):
++                                    inet6_attr[inet6_attr_type] = result
++                                    break
++
+                         # 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
+-- 
+2.30.2
+
-- 
2.30.2





More information about the pve-devel mailing list