]> git.proxmox.com Git - ifupdown2.git/blame - debian/patches/upstream/0001-add-ipv6-slaac-support-inet6-auto-accept_ra.patch
patch: fix ipv6 slaac on bridge
[ifupdown2.git] / debian / patches / upstream / 0001-add-ipv6-slaac-support-inet6-auto-accept_ra.patch
CommitLineData
02929181 1From 76721af329cab107e339fef5bc783dacb32132dc Mon Sep 17 00:00:00 2001
9e5b857e
AD
2From: Alexandre Derumier <aderumier@odiso.com>
3Date: Tue, 9 May 2023 17:48:14 +0200
4Subject: [PATCH] add ipv6 slaac support (inet6 auto && accept_ra)
5
6This should fix a lot of users request in the forum,
7and also fix upgrade from ifupdown1 to ifupdown2 if user have "inet6 auto" in configuration.
8(default on stock debian install, this break pbs install on top of stock debian)
9
10upstream pull request:
11
12https://github.com/CumulusNetworks/ifupdown2/pull/259
13Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
14---
15 etc/network/ifupdown2/addons.conf | 2 +
02929181 16 ifupdown2/addons/address.py | 109 +++++++++++++--
9e5b857e
AD
17 ifupdown2/addons/auto.py | 168 ++++++++++++++++++++++++
18 ifupdown2/addons/dhcp.py | 18 +--
19 ifupdown2/ifupdown/iface.py | 4 +
20 ifupdown2/ifupdown/networkinterfaces.py | 2 +-
21 ifupdown2/lib/nlcache.py | 63 ++++++++-
22 ifupdown2/man/interfaces.5.rst | 9 ++
23 ifupdown2/nlmanager/nlpacket.py | 24 +++-
02929181 24 9 files changed, 373 insertions(+), 26 deletions(-)
9e5b857e
AD
25 create mode 100644 ifupdown2/addons/auto.py
26
27diff --git a/etc/network/ifupdown2/addons.conf b/etc/network/ifupdown2/addons.conf
28index 726d63a..67de25f 100644
29--- a/etc/network/ifupdown2/addons.conf
30+++ b/etc/network/ifupdown2/addons.conf
31@@ -15,6 +15,7 @@ pre-up,mstpctl
32 pre-up,tunnel
33 pre-up,vrf
34 pre-up,ethtool
35+pre-up,auto
36 pre-up,address
37 up,dhcp
38 up,address
39@@ -28,6 +29,7 @@ pre-down,usercmds
40 pre-down,vxrd
41 pre-down,dhcp
42 down,ppp
43+down,auto
44 down,addressvirtual
45 down,address
46 down,usercmds
47diff --git a/ifupdown2/addons/address.py b/ifupdown2/addons/address.py
02929181 48index e71a26f..473a089 100644
9e5b857e
AD
49--- a/ifupdown2/addons/address.py
50+++ b/ifupdown2/addons/address.py
51@@ -188,6 +188,19 @@ class address(AddonWithIpBlackList, moduleBase):
52 'default': 'off',
53 'example': ['arp-accept on']
54 },
55+ 'accept-ra': {
56+ 'help': 'accept ipv6 router advertisement',
57+ 'validvals': ['0', '1', '2'],
58+ 'default': '0',
59+ 'example': ['accept-ra 1']
60+ },
61+ 'autoconf': {
02929181 62+ 'help': 'enable ipv6 slaac autoconfiguration',
9e5b857e
AD
63+ 'validvals': ['0', '1'],
64+ 'default': '0',
65+ 'example': ['autoconf 1']
66+ },
67+
68 }
69 }
70
71@@ -256,6 +269,16 @@ class address(AddonWithIpBlackList, moduleBase):
72 attr="check_l3_svi_ip_forwarding")
73 )
74
75+ try:
76+ self.default_accept_ra = str(self.sysctl_get('net.ipv6.conf.all.accept_ra'))
77+ except Exception:
78+ self.default_accept_ra = 1
79+
80+ try:
81+ self.default_autoconf = str(self.sysctl_get('net.ipv6.conf.all.autoconf'))
82+ except Exception:
83+ self.default_autoconf = 1
84+
85 def __policy_get_default_mtu(self):
86 default_mtu = policymanager.policymanager_api.get_attr_default(
87 module_name=self.__class__.__name__,
88@@ -627,21 +650,31 @@ class address(AddonWithIpBlackList, moduleBase):
89 if force_reapply:
90 self.__add_ip_addresses_with_attributes(ifaceobj, ifname, user_config_ip_addrs_list)
91 return
92+
93+ purge_dynamic_v6_addresses = True
94+ running_autoconf = self.cache.get_link_inet6_autoconf(ifaceobj)
95+ if running_autoconf == '1' and not squash_addr_config:
96+ purge_dynamic_v6_addresses = False
97+
98 try:
99- # if primary address is not same, there is no need to keep any, reset all addresses.
100- if ordered_user_configured_ips and running_ip_addrs and ordered_user_configured_ips[0] != running_ip_addrs[0]:
101- self.logger.info("%s: primary ip changed (from %s to %s) we need to purge all ip addresses and re-add them"
102- % (ifname, ordered_user_configured_ips[0], running_ip_addrs[0]))
103- skip_addrs = []
104+ # if primary ipv4 address is not same, there is no need to keep any, reset all ipv4 addresses.
105+ if user_ip4 and running_ip_addrs and running_ip_addrs[0].version == 4 and user_ip4[0] != running_ip_addrs[0]:
106+ self.logger.info("%s: primary ipv4 changed (from %s to %s) we need to purge all ipv4 addresses and re-add them"
107+ % (ifname, user_ip4[0], running_ip_addrs[0]))
108+ skip_addrs = user_ip6
109 else:
110 skip_addrs = ordered_user_configured_ips
111
112 if anycast_ip:
113 skip_addrs.append(anycast_ip)
114
115+ ip_flags = self.cache.get_ip_addresses_flags(ifname)
116 for addr in running_ip_addrs:
117 if addr in skip_addrs:
118 continue
119+ # don't purge dynamic ipv6 ip if autoconf is enabled
120+ if addr.version == 6 and not purge_dynamic_v6_addresses and addr in ip_flags and not ip_flags[addr] & 0x80:
121+ continue
122 self.netlink.addr_del(ifname, addr)
123 except Exception as e:
124 self.log_warn(str(e))
125@@ -872,7 +905,9 @@ class address(AddonWithIpBlackList, moduleBase):
126 netconf_ipv4_forwarding = self.cache.get_netconf_forwarding(socket.AF_INET, ifname)
127 netconf_ipv6_forwarding = self.cache.get_netconf_forwarding(socket.AF_INET6, ifname)
128
129- if not ifaceobj.upperifaces and not ifaceobj.get_attr_value('address') and (ifaceobj.addr_method and "dhcp" not in ifaceobj.addr_method):
130+ if ( not ifaceobj.upperifaces and not ifaceobj.get_attr_value('address') and
131+ ifaceobj.addr_method and "dhcp" not in ifaceobj.addr_method and "auto" not in ifaceobj.addr_method):
132+
133 if netconf_ipv4_forwarding:
134 self.sysctl_write_forwarding_value_to_proc(ifname, "ipv4", 0)
135 if netconf_ipv6_forwarding:
02929181
AD
136@@ -886,6 +921,43 @@ class address(AddonWithIpBlackList, moduleBase):
137 def sysctl_write_forwarding_value_to_proc(self, ifname, family, value):
138 self.write_file("/proc/sys/net/%s/conf/%s/forwarding" % (family, ifname), "%s\n" % value)
9e5b857e 139
02929181 140+ def _sysctl_slaac(self, ifaceobj):
9e5b857e
AD
141+ addr_method = ifaceobj.addr_method
142+ if addr_method not in ["auto"]:
143+
144+ try:
145+ running_accept_ra = self.cache.get_link_inet6_accept_ra(ifaceobj)
146+ if running_accept_ra == '':
147+ running_accept_ra = self.default_accept_ra
148+ accept_ra = ifaceobj.get_attr_value_first('accept-ra')
149+ if accept_ra is None:
150+ accept_ra = self.default_accept_ra
151+
152+ if running_accept_ra != accept_ra:
153+ self.sysctl_set('net.ipv6.conf.%s.accept_ra'
154+ %('/'.join(ifaceobj.name.split("."))),
155+ accept_ra)
156+ self.cache.update_link_inet6_accept_ra(ifaceobj.name, accept_ra)
157+
158+ running_autoconf = self.cache.get_link_inet6_autoconf(ifaceobj)
159+ if running_autoconf == '':
160+ running_autoconf = self.default_autoconf
161+ autoconf = ifaceobj.get_attr_value_first('autoconf')
162+ if autoconf is None:
163+ autoconf = self.default_autoconf
164+
165+ if running_autoconf != autoconf:
166+ self.sysctl_set('net.ipv6.conf.%s.autoconf'
167+ %('/'.join(ifaceobj.name.split("."))),
168+ autoconf)
169+ self.cache.update_link_inet6_autoconf(ifaceobj.name, autoconf)
170+
171+ except Exception as e:
172+ if not setting_default_value:
173+ ifaceobj.status = ifaceStatus.ERROR
174+ self.logger.error('%s: %s' %(ifaceobj.name, str(e)))
02929181
AD
175+
176+
177 def _sysctl_config(self, ifaceobj):
178 setting_default_value = False
179 mpls_enable = ifaceobj.get_attr_value_first('mpls-enable');
180@@ -912,6 +984,7 @@ class address(AddonWithIpBlackList, moduleBase):
181
182 if (ifaceobj.link_kind & ifaceLinkKind.BRIDGE):
183 self._set_bridge_forwarding(ifaceobj)
184+ self._sysctl_slaac(ifaceobj)
185 return
186 if not self.syntax_check_sysctls(ifaceobj):
187 return
188@@ -979,6 +1052,8 @@ class address(AddonWithIpBlackList, moduleBase):
189 ifaceobj.status = ifaceStatus.ERROR
190 self.logger.error('%s: %s' %(ifaceobj.name, str(e)))
191
192+ self._sysctl_slaac(ifaceobj)
9e5b857e
AD
193+
194 def process_mtu(self, ifaceobj, ifaceobj_getfunc):
195
196 if ifaceobj.link_privflags & ifaceLinkPrivFlags.OPENVSWITCH:
02929181 197@@ -1016,7 +1091,7 @@ class address(AddonWithIpBlackList, moduleBase):
9e5b857e
AD
198 # no need to go further during perfmode (boot)
199 return
200
201- if not user_configured_ipv6_addrgen and ifaceobj.addr_method in ["dhcp", "ppp"]:
202+ if not user_configured_ipv6_addrgen and ifaceobj.addr_method in ["dhcp", "ppp", "auto"]:
203 return
204
205 if not user_configured_ipv6_addrgen:
02929181 206@@ -1213,7 +1288,7 @@ class address(AddonWithIpBlackList, moduleBase):
9e5b857e
AD
207 if not self.cache.link_exists(ifaceobj.name):
208 return
209 addr_method = ifaceobj.addr_method
210- if addr_method not in ["dhcp", "ppp"]:
211+ if addr_method not in ["dhcp", "ppp", "auto"]:
212 if ifaceobj.get_attr_value_first('address-purge')=='no':
213 addrlist = ifaceobj.get_attr_value('address')
214 for addr in addrlist or []:
02929181 215@@ -1326,6 +1401,22 @@ class address(AddonWithIpBlackList, moduleBase):
9e5b857e
AD
216 ifaceobjcurr.update_config_with_status('mpls-enable',
217 running_mpls_enable,
218 mpls_enable != running_mpls_enable)
219+
220+ accept_ra = ifaceobj.get_attr_value_first('accept-ra')
221+ if accept_ra:
222+ running_accept_ra = self.cache.get_link_inet6_accept_ra(ifaceobj)
223+
224+ ifaceobjcurr.update_config_with_status('accept_ra',
225+ running_accept_ra,
226+ accept_ra != running_accept_ra)
227+
228+ autoconf = ifaceobj.get_attr_value_first('autoconf')
229+ if autoconf:
230+ running_autoconf = self.cache.get_link_inet6_autoconf(ifaceobj)
231+
232+ ifaceobjcurr.update_config_with_status('autoconf',
233+ running_autoconf,
234+ autoconf != running_autoconf)
235 return
236
237 def query_check_ipv6_addrgen(self, ifaceobj, ifaceobjcurr):
02929181 238@@ -1380,7 +1471,7 @@ class address(AddonWithIpBlackList, moduleBase):
9e5b857e
AD
239
240 def _query_check_address(self, ifaceobj, ifaceobjcurr, ifaceobj_getfunc):
241 """ ifquery-check: attribute: "address" """
242- if ifaceobj.addr_method in ["dhcp", "ppp"]:
243+ if ifaceobj.addr_method in ["dhcp", "ppp", "auto"]:
244 return
245
246 if ifaceobj_getfunc:
247diff --git a/ifupdown2/addons/auto.py b/ifupdown2/addons/auto.py
248new file mode 100644
249index 0000000..02e6ca4
250--- /dev/null
251+++ b/ifupdown2/addons/auto.py
252@@ -0,0 +1,168 @@
253+#!/usr/bin/env python3
254+#
255+
256+import re
257+import time
258+import socket
259+
260+try:
261+ from ifupdown2.lib.addon import Addon
262+ from ifupdown2.lib.log import LogManager
263+
264+ import ifupdown2.ifupdown.policymanager as policymanager
265+ import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
266+
267+ from ifupdown2.ifupdown.iface import *
268+ from ifupdown2.ifupdown.utils import utils
269+
270+ from ifupdown2.ifupdownaddons.modulebase import moduleBase
271+except (ImportError, ModuleNotFoundError):
272+ from lib.addon import Addon
273+ from lib.log import LogManager
274+
275+ import ifupdown.policymanager as policymanager
276+ import ifupdown.ifupdownflags as ifupdownflags
277+
278+ from ifupdown.iface import *
279+ from ifupdown.utils import utils
280+
281+ from ifupdownaddons.modulebase import moduleBase
282+
283+
284+class auto(Addon, moduleBase):
285+ """ ifupdown2 addon module to configure slaac on inet6 interface """
286+
287+ def __init__(self, *args, **kargs):
288+ Addon.__init__(self)
289+ moduleBase.__init__(self, *args, **kargs)
290+
291+ def syntax_check(self, ifaceobj, ifaceobj_getfunc):
292+ return self.is_auto_allowed_on(ifaceobj, syntax_check=True)
293+
294+ def is_auto_allowed_on(self, ifaceobj, syntax_check):
295+ if ifaceobj.addr_method and 'auto' in ifaceobj.addr_method:
296+ return utils.is_addr_ip_allowed_on(ifaceobj, syntax_check=True)
297+ return True
298+
299+ def _up(self, ifaceobj):
300+
301+ if ifaceobj.link_privflags & ifaceLinkPrivFlags.KEEP_LINK_DOWN:
302+ self.logger.info("%s: skipping auto configuration: link-down yes" % ifaceobj.name)
303+ return
304+
305+ try:
306+ if 'inet6' in ifaceobj.addr_family:
307+ running_accept_ra = self.cache.get_link_inet6_accept_ra(ifaceobj)
308+ if running_accept_ra != '2':
309+ accept_ra = '2'
310+ self.sysctl_set('net.ipv6.conf.%s.accept_ra'
311+ %('/'.join(ifaceobj.name.split("."))),
312+ accept_ra)
313+ self.cache.update_link_inet6_accept_ra(ifaceobj.name, accept_ra)
314+
315+ running_autoconf = self.cache.get_link_inet6_autoconf(ifaceobj)
316+ if running_autoconf != '1':
317+ autoconf = '1'
318+ self.sysctl_set('net.ipv6.conf.%s.autoconf'
319+ %('/'.join(ifaceobj.name.split("."))),
320+ autoconf)
321+ self.cache.update_link_inet6_autoconf(ifaceobj.name, autoconf)
322+
323+ except Exception as e:
324+ self.logger.error("%s: %s" % (ifaceobj.name, str(e)))
325+ ifaceobj.set_status(ifaceStatus.ERROR)
326+
327+ def _down(self, ifaceobj):
328+ if 'inet6' in ifaceobj.addr_family:
329+ self.cache.force_address_flush_family(ifaceobj.name, socket.AF_INET6)
330+ self.netlink.link_down(ifaceobj.name)
331+
332+ def _query_check(self, ifaceobj, ifaceobjcurr):
333+ if not self.cache.link_exists(ifaceobj.name):
334+ return
335+ ifaceobjcurr.addr_family = ifaceobj.addr_family
336+ ifaceobjcurr.addr_method = 'auto'
337+
338+ inet6conf = self.cache.get_link_inet6_conf(ifaceobj.name)
339+ if inet6conf['accept_ra'] == 2 and inet6conf['autoconf'] == 1:
340+ ifaceobjcurr.status = ifaceStatus.SUCCESS
341+ else:
342+ ifaceobjcurr.status = ifaceStatus.ERROR
343+
344+ def _query_running(self, ifaceobjrunning):
345+ pass
346+
347+ _run_ops = {'pre-up' : _up,
348+ 'up' : _up,
349+ 'down' : _down,
350+ 'pre-down' : _down,
351+ 'query-checkcurr' : _query_check,
352+ 'query-running' : _query_running }
353+
354+ def get_ops(self):
355+ """ returns list of ops supported by this module """
356+ return list(self._run_ops.keys())
357+
358+ def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
359+ """ run dhcp configuration on the interface object passed as argument
360+
361+ Args:
362+ **ifaceobj** (object): iface object
363+
364+ **operation** (str): any of 'up', 'down', 'query-checkcurr',
365+ 'query-running'
366+
367+ Kwargs:
368+ **query_ifaceobj** (object): query check ifaceobject. This is only
369+ valid when op is 'query-checkcurr'. It is an object same as
370+ ifaceobj, but contains running attribute values and its config
371+ status. The modules can use it to return queried running state
372+ of interfaces. status is success if the running state is same
373+ as user required state in ifaceobj. error otherwise.
374+ """
375+ op_handler = self._run_ops.get(operation)
376+ if not op_handler:
377+ return
378+ try:
379+ if (operation != 'query-running' and ifaceobj.addr_method != 'auto'):
380+ return
381+ except Exception:
382+ return
383+ if not self.is_auto_allowed_on(ifaceobj, syntax_check=False):
384+ return
385+
386+ log_manager = LogManager.get_instance()
387+
388+ syslog_log_level = logging.INFO
389+ disable_syslog_on_exit = None
390+
391+ if operation in ["up", "down"]:
392+ # if syslog is already enabled we shouldn't disable it
393+ if log_manager.is_syslog_enabled():
394+ # save current syslog level
395+ syslog_log_level = log_manager.get_syslog_log_level()
396+ # prevent syslog from being disabled on exit
397+ disable_syslog_on_exit = False
398+ else:
399+ # enabling syslog
400+ log_manager.enable_syslog()
401+ # syslog will be disabled once we are done
402+ disable_syslog_on_exit = True
403+
404+ # update the current syslog handler log level if higher than INFO
405+ if syslog_log_level >= logging.INFO:
406+ log_manager.set_level_syslog(logging.INFO)
407+
408+ self.logger.info("%s: enabling syslog for auto configuration" % ifaceobj.name)
409+
410+ try:
411+ if operation == 'query-checkcurr':
412+ op_handler(self, ifaceobj, query_ifaceobj)
413+ else:
414+ op_handler(self, ifaceobj)
415+ finally:
416+ # disable syslog handler or re-set the proper log-level
417+ if disable_syslog_on_exit is True:
418+ log_manager.get_instance().disable_syslog()
419+ elif disable_syslog_on_exit is False:
420+ log_manager.set_level_syslog(syslog_log_level)
421diff --git a/ifupdown2/addons/dhcp.py b/ifupdown2/addons/dhcp.py
422index a5bf860..22bbdb4 100644
423--- a/ifupdown2/addons/dhcp.py
424+++ b/ifupdown2/addons/dhcp.py
425@@ -193,20 +193,10 @@ class dhcp(Addon, moduleBase):
426 self.logger.info('dhclient6 already running on %s. '
427 'Not restarting.' % ifaceobj.name)
428 else:
429- accept_ra = ifaceobj.get_attr_value_first('accept_ra')
430- if accept_ra:
431- # XXX: Validate value
432- self.sysctl_set('net.ipv6.conf.%s' %ifaceobj.name +
433- '.accept_ra', accept_ra)
434- autoconf = ifaceobj.get_attr_value_first('autoconf')
435- if autoconf:
436- # XXX: Validate value
437- self.sysctl_set('net.ipv6.conf.%s' %ifaceobj.name +
438- '.autoconf', autoconf)
439- try:
440- self.dhclientcmd.stop6(ifaceobj.name, duid=dhcp6_duid)
441- except Exception:
442- pass
443+ try:
444+ self.dhclientcmd.stop6(ifaceobj.name, duid=dhcp6_duid)
445+ except Exception:
446+ pass
447 #add delay before starting IPv6 dhclient to
448 #make sure the configured interface/link is up.
449 if timeout > 1:
450diff --git a/ifupdown2/ifupdown/iface.py b/ifupdown2/ifupdown/iface.py
451index 07bd067..325e6c3 100644
452--- a/ifupdown2/ifupdown/iface.py
453+++ b/ifupdown2/ifupdown/iface.py
454@@ -289,6 +289,8 @@ class ifaceJsonEncoder(json.JSONEncoder):
455 if o.addr_method:
456 if 'inet' in o.addr_family and 'dhcp' in o.addr_method:
457 retifacedict['addr_method'] = 'dhcp'
458+ elif 'inet6' in o.addr_family and 'auto' in o.addr_method:
459+ retifacedict['addr_method'] = 'auto'
460 else:
461 retifacedict['addr_method'] = o.addr_method
462 if o.addr_family:
463@@ -843,6 +845,8 @@ class iface():
464 # both inet and inet6 addr_family
465 if addr_method and family == 'inet' and 'dhcp' in addr_method:
466 addr_method = 'dhcp'
467+ elif addr_method and family == 'inet6' and 'auto' in addr_method:
468+ addr_method = 'auto'
469 self._dump_pretty(family, first,
470 addr_method=addr_method,
471 with_status=with_status,
472diff --git a/ifupdown2/ifupdown/networkinterfaces.py b/ifupdown2/ifupdown/networkinterfaces.py
473index 2bebe39..3803590 100644
474--- a/ifupdown2/ifupdown/networkinterfaces.py
475+++ b/ifupdown2/ifupdown/networkinterfaces.py
476@@ -30,7 +30,7 @@ class networkInterfaces():
477 """ debian ifupdown /etc/network/interfaces file parser """
478
479 _addrfams = {'inet' : ['static', 'manual', 'loopback', 'dhcp', 'dhcp6', 'ppp', 'tunnel'],
480- 'inet6' : ['static', 'manual', 'loopback', 'dhcp', 'dhcp6', 'ppp', 'tunnel']}
481+ 'inet6' : ['static', 'manual', 'loopback', 'dhcp', 'dhcp6', 'ppp', 'tunnel', 'auto']}
482 # tunnel is part of the address family for backward compatibility but is not required.
483
484 def __init__(self, interfacesfile='/etc/network/interfaces',
485diff --git a/ifupdown2/lib/nlcache.py b/ifupdown2/lib/nlcache.py
486index 0b1c6d2..0d2f624 100644
487--- a/ifupdown2/lib/nlcache.py
488+++ b/ifupdown2/lib/nlcache.py
489@@ -152,7 +152,7 @@ class _NetlinkCache:
490 Address.IFA_ANYCAST,
491 # Address.IFA_CACHEINFO,
492 Address.IFA_MULTICAST,
493- # Address.IFA_FLAGS
494+ Address.IFA_FLAGS
495 )
496
497 def __init__(self):
498@@ -1179,6 +1179,52 @@ class _NetlinkCache:
499 except TypeError as e:
500 return self.__handle_type_error(inspect.currentframe().f_code.co_name, ifname, str(e), return_value=0)
501
502+ def get_link_inet6_conf(self, ifname):
503+ try:
504+ with self._cache_lock:
505+ return self._link_cache[ifname].attributes[Link.IFLA_AF_SPEC].value[socket.AF_INET6][Link.IFLA_INET6_CONF]
506+ except (KeyError, AttributeError):
507+ return False
508+ except TypeError as e:
509+ return self.__handle_type_error(inspect.currentframe().f_code.co_name, ifname, str(e), return_value=False)
510+
511+ def get_link_inet6_accept_ra(self, ifaceobj):
512+ inet6conf = self.get_link_inet6_conf(ifaceobj.name)
513+ if inet6conf and 'accept_ra' in inet6conf:
514+ accept_ra = str(inet6conf['accept_ra'])
515+ else:
516+ accept_ra = ''
517+ return accept_ra
518+
519+ def get_link_inet6_autoconf(self, ifaceobj):
520+ inet6conf = self.get_link_inet6_conf(ifaceobj.name)
521+ if inet6conf and 'autoconf' in inet6conf:
522+ autoconf = str(inet6conf['autoconf'])
523+ else:
524+ autoconf = ''
525+ return autoconf
526+
527+ def update_link_inet6_accept_ra(self, ifname, accept_ra):
528+ try:
529+ with self._cache_lock:
530+ try:
531+ self._link_cache[ifname].attributes[Link.IFLA_AF_SPEC].value[socket.AF_INET6][Link.IFLA_INET6_CONF]['accept_ra'] = accept_ra
532+ except Exception as e:
533+ pass
534+ except Exception:
535+ pass
536+
537+ def update_link_inet6_autoconf(self, ifname, autoconf):
538+ try:
539+ with self._cache_lock:
540+ try:
541+ self._link_cache[ifname].attributes[Link.IFLA_AF_SPEC].value[socket.AF_INET6][Link.IFLA_INET6_CONF]['autoconf'] = autoconf
542+ except Exception as e:
543+ pass
544+ except Exception:
545+ pass
546+
547+
548 #####################################################
549 #####################################################
550 #####################################################
551@@ -1745,6 +1791,21 @@ class _NetlinkCache:
552 except (KeyError, AttributeError):
553 return addresses
554
555+ def get_ip_addresses_flags(self, ifname: str) -> dict:
556+ addresses = {}
557+ try:
558+ with self._cache_lock:
559+ intf_addresses = self._addr_cache[ifname]
560+ for addr in intf_addresses.get(4, []):
561+ addresses[addr.attributes[Address.IFA_ADDRESS].value] = addr.attributes[Address.IFA_FLAGS].value
562+
563+ for addr in intf_addresses.get(6, []):
564+ addresses[addr.attributes[Address.IFA_ADDRESS].value] = addr.attributes[Address.IFA_FLAGS].value
565+
566+ return addresses
567+ except (KeyError, AttributeError):
568+ return addresses
569+
570 def link_has_ip(self, ifname):
571 try:
572 with self._cache_lock:
573diff --git a/ifupdown2/man/interfaces.5.rst b/ifupdown2/man/interfaces.5.rst
574index 262d726..ca461ea 100644
575--- a/ifupdown2/man/interfaces.5.rst
576+++ b/ifupdown2/man/interfaces.5.rst
577@@ -106,6 +106,12 @@ METHODS
578 The dhcp Method
579 This method may be used to obtain an address via DHCP.
580
581+ **inet6** address family interfaces can use the following method:
582+
583+ The auto Method
584+ This method may be used to obtain an address via SLAAC.
585+
586+
587 BUILTIN INTERFACES
588 ==================
589 **iface** sections for some interfaces like physical interfaces or vlan
590@@ -131,6 +137,9 @@ EXAMPLES
591 address 192.168.2.0/24
592 address 2001:dee:eeee:1::4/128
593
594+ auto eth3
595+ iface eth3 inet auto
596+
597 # source files from a directory /etc/network/interfaces.d
598 source /etc/network/interfaces.d/*
599
600diff --git a/ifupdown2/nlmanager/nlpacket.py b/ifupdown2/nlmanager/nlpacket.py
601index 8972c76..0090529 100644
602--- a/ifupdown2/nlmanager/nlpacket.py
603+++ b/ifupdown2/nlmanager/nlpacket.py
604@@ -1818,6 +1818,15 @@ class AttributeIFLA_AF_SPEC(Attribute):
605 */
606
607 """
608+ #only first attributes used in any kernel.
609+ ipv6_devconf = ['forwarding',
610+ 'hop_limit',
611+ 'mtu6',
612+ 'accept_ra',
613+ 'accept_redirects',
614+ 'autoconf',
615+ ]
616+
617 self.decode_length_type(data)
618 self.value = {}
619
620@@ -1896,8 +1905,21 @@ class AttributeIFLA_AF_SPEC(Attribute):
621 (inet6_attr_length, inet6_attr_type) = unpack('=HH', sub_attr_data[:4])
622 inet6_attr_end = padded_length(inet6_attr_length)
623
624+ if inet6_attr_type == Link.IFLA_INET6_CONF:
625+ inet6conf_data = sub_attr_data[4:inet6_attr_end]
626+ index = 0
627+ result = {}
628+ while inet6conf_data:
629+ (value, undef) = unpack('=HH', inet6conf_data[:4])
630+ result[ipv6_devconf[index]] = value
631+ inet6conf_data = inet6conf_data[4:]
632+ index = index + 1
633+ if index >= len(ipv6_devconf):
634+ inet6_attr[inet6_attr_type] = result
635+ break
636+
637 # 1 byte attr
638- if inet6_attr_type == Link.IFLA_INET6_ADDR_GEN_MODE:
639+ elif inet6_attr_type == Link.IFLA_INET6_ADDR_GEN_MODE:
640 inet6_attr[inet6_attr_type] = self.decode_one_byte_attribute(sub_attr_data)
641
642 # nlmanager doesn't support multiple kernel version
643--
02929181 6442.39.2
9e5b857e 645