]>
Commit | Line | Data |
---|---|---|
02929181 | 1 | From 76721af329cab107e339fef5bc783dacb32132dc Mon Sep 17 00:00:00 2001 |
9e5b857e AD |
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 + | |
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 | ||
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 | |
02929181 | 48 | index 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: | |
247 | diff --git a/ifupdown2/addons/auto.py b/ifupdown2/addons/auto.py | |
248 | new file mode 100644 | |
249 | index 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) | |
421 | diff --git a/ifupdown2/addons/dhcp.py b/ifupdown2/addons/dhcp.py | |
422 | index 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: | |
450 | diff --git a/ifupdown2/ifupdown/iface.py b/ifupdown2/ifupdown/iface.py | |
451 | index 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, | |
472 | diff --git a/ifupdown2/ifupdown/networkinterfaces.py b/ifupdown2/ifupdown/networkinterfaces.py | |
473 | index 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', | |
485 | diff --git a/ifupdown2/lib/nlcache.py b/ifupdown2/lib/nlcache.py | |
486 | index 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: | |
573 | diff --git a/ifupdown2/man/interfaces.5.rst b/ifupdown2/man/interfaces.5.rst | |
574 | index 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 | ||
600 | diff --git a/ifupdown2/nlmanager/nlpacket.py b/ifupdown2/nlmanager/nlpacket.py | |
601 | index 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 | 644 | 2.39.2 |
9e5b857e | 645 |