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