[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