From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by lists.proxmox.com (Postfix) with ESMTPS id 9AEF399F54 for ; Tue, 16 May 2023 00:47:54 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 77A7A102DF for ; Tue, 16 May 2023 00:47:24 +0200 (CEST) Received: from bastionodiso.odiso.net (bastionodiso.odiso.net [IPv6:2a0a:1580:2000::2d]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by firstgate.proxmox.com (Proxmox) with ESMTPS for ; Tue, 16 May 2023 00:47:19 +0200 (CEST) Received: from kvmformation3.odiso.net (formationkvm3.odiso.net [10.3.94.12]) by bastionodiso.odiso.net (Postfix) with ESMTP id 3A2F07B01; Tue, 16 May 2023 00:47:12 +0200 (CEST) Received: by kvmformation3.odiso.net (Postfix, from userid 0) id 20F57255EFB; Tue, 16 May 2023 00:47:12 +0200 (CEST) From: Alexandre Derumier To: pve-devel@lists.proxmox.com Date: Tue, 16 May 2023 00:47:08 +0200 Message-Id: <20230515224710.1331004-5-aderumier@odiso.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20230515224710.1331004-1-aderumier@odiso.com> References: <20230515224710.1331004-1-aderumier@odiso.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 0 AWL -0.066 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DMARC_MISSING 0.1 Missing DMARC policy HEADER_FROM_DIFFERENT_DOMAINS 0.249 From and EnvelopeFrom 2nd level mail domains are different KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment KAM_LAZY_DOMAIN_SECURITY 1 Sending domain does not have any anti-forgery methods PROLO_LEO1 0.1 Meta Catches all Leo drug variations so far SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_NONE 0.001 SPF: sender does not publish an SPF Record T_SCC_BODY_TEXT_LINE -0.01 - Subject: [pve-devel] [PATCH ifupdown2 4/6] patch: add ipv6 slaac support upstream patch X-BeenThere: pve-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox VE development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 15 May 2023 22:47:54 -0000 Signed-off-by: Alexandre Derumier --- 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 +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 +--- + 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