]>
Commit | Line | Data |
---|---|---|
35681c06 | 1 | #!/usr/bin/env python3 |
15ef32ea | 2 | # |
d486dd0d | 3 | # Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved. |
15ef32ea RP |
4 | # Author: Roopa Prabhu, roopa@cumulusnetworks.com |
5 | # | |
6 | ||
007cae35 | 7 | import socket |
b99c724a AB |
8 | import json |
9 | import time | |
10 | import subprocess | |
007cae35 | 11 | |
8126ef0c JF |
12 | from distutils.util import strtobool |
13 | ||
15ef32ea | 14 | try: |
3eb08b79 | 15 | from ifupdown2.lib.addon import AddonWithIpBlackList |
223ba5af JF |
16 | from ifupdown2.nlmanager.nlmanager import Link |
17 | ||
d486dd0d JF |
18 | from ifupdown2.ifupdown.iface import * |
19 | from ifupdown2.ifupdown.utils import utils | |
d486dd0d JF |
20 | |
21 | from ifupdown2.ifupdownaddons.dhclient import dhclient | |
d486dd0d JF |
22 | from ifupdown2.ifupdownaddons.modulebase import moduleBase |
23 | ||
0e936c3f JF |
24 | import ifupdown2.nlmanager.ipnetwork as ipnetwork |
25 | ||
d486dd0d JF |
26 | import ifupdown2.ifupdown.statemanager as statemanager |
27 | import ifupdown2.ifupdown.policymanager as policymanager | |
28 | import ifupdown2.ifupdown.ifupdownflags as ifupdownflags | |
29 | import ifupdown2.ifupdown.ifupdownconfig as ifupdownconfig | |
bd441a51 | 30 | except (ImportError, ModuleNotFoundError): |
3eb08b79 | 31 | from lib.addon import AddonWithIpBlackList |
223ba5af JF |
32 | from nlmanager.nlmanager import Link |
33 | ||
15ef32ea | 34 | from ifupdown.iface import * |
82908a2d | 35 | from ifupdown.utils import utils |
d486dd0d | 36 | |
15ef32ea | 37 | from ifupdownaddons.dhclient import dhclient |
d486dd0d JF |
38 | from ifupdownaddons.modulebase import moduleBase |
39 | ||
0e936c3f JF |
40 | import nlmanager.ipnetwork as ipnetwork |
41 | ||
d486dd0d | 42 | import ifupdown.statemanager as statemanager |
84f33af6 | 43 | import ifupdown.policymanager as policymanager |
fc5e1735 | 44 | import ifupdown.ifupdownflags as ifupdownflags |
d486dd0d JF |
45 | import ifupdown.ifupdownconfig as ifupdownconfig |
46 | ||
15ef32ea | 47 | |
3eb08b79 | 48 | class address(AddonWithIpBlackList, moduleBase): |
15ef32ea RP |
49 | """ ifupdown2 addon module to configure address, mtu, hwaddress, alias |
50 | (description) on an interface """ | |
51 | ||
223ba5af JF |
52 | _modinfo = { |
53 | 'mhelp': 'address configuration module for interfaces', | |
54 | 'attrs': { | |
55 | 'address': { | |
56 | 'help': 'The address of the interface. The format of the ' | |
57 | 'address depends on the protocol. It is a dotted ' | |
58 | 'quad for IP and a sequence of hexadecimal halfwords ' | |
59 | 'separated by colons for IPv6. The ADDRESS may be ' | |
60 | 'followed by a slash and a decimal number which ' | |
61 | 'encodes the network prefix length.', | |
62 | 'validvals': ['<ipv4/prefixlen>', '<ipv6/prefixlen>'], | |
63 | 'multiline': True, | |
64 | 'example': [ | |
65 | 'address 10.0.12.3/24', | |
66 | 'address 2000:1000:1000:1000:3::5/128' | |
67 | ] | |
68 | }, | |
69 | 'netmask': { | |
70 | 'help': 'Address netmask', | |
71 | 'example': ['netmask 255.255.255.0'], | |
72 | 'compat': True | |
73 | }, | |
eb6ad1f7 AB |
74 | 'dad-attempts': { |
75 | 'help': 'Number of attempts to settle DAD (0 to disable DAD). ' | |
76 | 'To use this feature, the ipv6_dad_handling_enabled ' | |
77 | 'module global must be set to true', | |
78 | 'example': ['dad-attempts 0'], | |
79 | 'default': '60', | |
80 | }, | |
81 | 'dad-interval': { | |
82 | 'help': 'DAD state polling interval in seconds. ' | |
83 | 'To use this feature, the ipv6_dad_handling_enabled ' | |
84 | 'module global must be set to true', | |
85 | 'example': ['dad-interval 0.5'], | |
86 | 'default': '0.1', | |
87 | }, | |
223ba5af JF |
88 | 'broadcast': { |
89 | 'help': 'The broadcast address on the interface.', | |
90 | 'validvals': ['<ipv4>'], | |
91 | 'example': ['broadcast 10.0.1.255'] | |
92 | }, | |
93 | 'scope': { | |
94 | 'help': 'The scope of the area where this address is valid. ' | |
95 | 'The available scopes are listed in file /etc/iproute2/rt_scopes. ' | |
96 | 'Predefined scope values are: ' | |
97 | 'global - the address is globally valid. ' | |
98 | 'site - (IPv6 only, deprecated) the address is site local, i.e. it is valid inside this site. ' | |
99 | 'link - the address is link local, i.e. it is valid only on this device. ' | |
100 | 'host - the address is valid only inside this host.', | |
101 | 'validvals': ['universe', 'site', 'link', 'host', 'nowhere'], | |
102 | 'example': ['scope host'] | |
103 | }, | |
104 | 'preferred-lifetime': { | |
105 | 'help': 'The preferred lifetime of this address; see section ' | |
106 | '5.5.4 of RFC 4862. When it expires, the address is ' | |
107 | 'no longer used for new outgoing connections. ' | |
108 | 'Defaults to forever.', | |
109 | 'validrange': ['0', '65535'], | |
110 | 'example': [ | |
111 | 'preferred-lifetime forever', | |
112 | 'preferred-lifetime 10' | |
113 | ] | |
114 | }, | |
115 | 'pointopoint': { | |
116 | 'help': 'Set the remote IP address for a point-to-point link', | |
117 | 'validvals': ['<ipv4/prefixlen>', '<ipv6/prefixlen>'], | |
118 | 'example': [ | |
119 | 'pointopoint 10.10.10.42/32' | |
120 | ] | |
121 | }, | |
122 | 'gateway': { | |
123 | 'help': 'Default gateway', | |
124 | 'validvals': ['<ipv4>', '<ipv6>'], | |
125 | 'multiline': True, | |
126 | 'example': ['gateway 255.255.255.0'] | |
127 | }, | |
128 | 'mtu': { | |
129 | 'help': 'Interface MTU (maximum transmission unit)', | |
130 | 'validrange': ['552', '9216'], | |
131 | 'example': ['mtu 1600'], | |
132 | 'default': '1500' | |
133 | }, | |
134 | 'hwaddress': { | |
135 | 'help': 'Hardware address (mac)', | |
136 | 'validvals': ['<mac>'], | |
137 | 'example': ['hwaddress 44:38:39:00:27:b8'] | |
138 | }, | |
139 | 'alias': { | |
140 | 'help': 'description/alias: give the device a symbolic name for easy reference.', | |
141 | 'example': ['alias testnetwork'] | |
142 | }, | |
143 | 'address-purge': { | |
144 | 'help': 'Purge existing addresses. By default any existing ' | |
145 | 'ip addresses on an interface are purged to match ' | |
146 | 'persistant addresses in the interfaces file. Set ' | |
147 | 'this attribute to \'no\' if you want to preserve ' | |
148 | 'existing addresses', | |
149 | 'validvals': ['yes', 'no'], | |
150 | 'default': 'yes', | |
151 | 'example': ['address-purge yes/no'] | |
152 | }, | |
153 | 'clagd-vxlan-anycast-ip': { | |
154 | 'help': 'Anycast local IP address for dual connected VxLANs', | |
155 | 'validvals': ['<ipv4>'], | |
156 | 'example': ['clagd-vxlan-anycast-ip 36.0.0.11'] | |
157 | }, | |
158 | 'ip-forward': { | |
159 | 'help': 'ip forwarding flag', | |
160 | 'validvals': ['on', 'off', 'yes', 'no', '0', '1'], | |
161 | 'default': 'off', | |
162 | 'example': ['ip-forward off'] | |
163 | }, | |
164 | 'ip6-forward': { | |
165 | 'help': 'ipv6 forwarding flag', | |
166 | 'validvals': ['on', 'off', 'yes', 'no', '0', '1'], | |
167 | 'default': 'off', | |
168 | 'example': ['ip6-forward off'] | |
169 | }, | |
170 | 'mpls-enable': { | |
171 | 'help': 'mpls enable flag', | |
172 | 'validvals': ['yes', 'no'], | |
173 | 'default': 'no', | |
174 | 'example': ['mpls-enable yes'] | |
175 | }, | |
176 | 'ipv6-addrgen': { | |
177 | 'help': 'enable disable ipv6 link addrgenmode', | |
178 | 'validvals': ['on', 'off'], | |
179 | 'default': 'on', | |
180 | 'example': [ | |
181 | 'ipv6-addrgen on', | |
182 | 'ipv6-addrgen off' | |
183 | ] | |
184 | }, | |
185 | 'arp-accept': { | |
186 | 'help': 'Allow gratuitous arp to update arp table', | |
187 | 'validvals': ['on', 'off', 'yes', 'no', '0', '1'], | |
188 | 'default': 'off', | |
189 | 'example': ['arp-accept on'] | |
190 | }, | |
191 | } | |
192 | } | |
193 | ||
194 | DEFAULT_MTU_STRING = "1500" | |
15ef32ea RP |
195 | |
196 | def __init__(self, *args, **kargs): | |
3eb08b79 | 197 | AddonWithIpBlackList.__init__(self) |
15ef32ea | 198 | moduleBase.__init__(self, *args, **kargs) |
8e113d63 | 199 | self._bridge_fdb_query_cache = {} |
d486dd0d JF |
200 | self.ipforward = policymanager.policymanager_api.get_attr_default(module_name=self.__class__.__name__, attr='ip-forward') |
201 | self.ip6forward = policymanager.policymanager_api.get_attr_default(module_name=self.__class__.__name__, attr='ip6-forward') | |
202 | self.ifaces_defaults = policymanager.policymanager_api.get_iface_defaults(module_name=self.__class__.__name__) | |
203 | self.enable_l3_iface_forwarding_checks = utils.get_boolean_from_string( | |
204 | policymanager.policymanager_api.get_module_globals( | |
205 | self.__class__.__name__, | |
206 | 'enable_l3_iface_forwarding_checks' | |
207 | ) | |
208 | ) | |
f4764e0f AB |
209 | self.ipv6_dad_handling_enabled = utils.get_boolean_from_string( |
210 | policymanager.policymanager_api.get_module_globals( | |
211 | self.__class__.__name__, | |
212 | 'ipv6_dad_handling_enabled' | |
213 | ), | |
214 | default=False | |
215 | ) | |
9f30b2cc | 216 | |
90937759 | 217 | self.default_mtu = str(self.__policy_get_default_mtu()) |
cbda6dda JF |
218 | self.default_mgmt_intf_mtu = self.__policy_get_mgmt_intf_mtu() |
219 | if not self.default_mgmt_intf_mtu: | |
220 | self.default_mgmt_intf_mtu = self.default_mtu | |
221 | self.default_mgmt_intf_mtu_int = self.default_mtu_int | |
222 | self.max_mtu = self.__policy_get_max_mtu() | |
9f30b2cc | 223 | |
0e936c3f | 224 | self.default_loopback_addresses = (ipnetwork.IPNetwork('127.0.0.1/8'), ipnetwork.IPNetwork('::1/128')) |
d486dd0d | 225 | |
8b57a467 JF |
226 | self.l3_intf_arp_accept = utils.get_boolean_from_string( |
227 | policymanager.policymanager_api.get_module_globals( | |
228 | module_name=self.__class__.__name__, | |
229 | attr='l3_intf_arp_accept' | |
230 | ), | |
8126ef0c | 231 | default=0 |
8b57a467 JF |
232 | ) |
233 | ||
8126ef0c JF |
234 | try: |
235 | l3_intf_arp_accept_str = policymanager.policymanager_api.get_module_globals( | |
236 | module_name=self.__class__.__name__, | |
237 | attr="l3_intf_arp_accept" | |
238 | ) | |
239 | try: | |
240 | self.l3_intf_arp_accept = int(l3_intf_arp_accept_str) | |
241 | except: | |
242 | self.l3_intf_arp_accept = int(strtobool(l3_intf_arp_accept_str)) | |
243 | except: | |
244 | self.l3_intf_arp_accept = 0 | |
245 | ||
9d505185 JF |
246 | self.l3_intf_default_gateway_set_onlink = utils.get_boolean_from_string( |
247 | policymanager.policymanager_api.get_module_globals( | |
248 | module_name=self.__class__.__name__, | |
249 | attr='l3_intf_default_gateway_set_onlink' | |
250 | ), | |
251 | default=True | |
252 | ) | |
253 | ||
20eab2b1 JF |
254 | self.check_l3_svi_ip_forwarding = utils.get_boolean_from_string(policymanager.policymanager_api.get_module_globals( |
255 | module_name=self.__class__.__name__, | |
256 | attr="check_l3_svi_ip_forwarding") | |
257 | ) | |
258 | ||
223ba5af JF |
259 | def __policy_get_default_mtu(self): |
260 | default_mtu = policymanager.policymanager_api.get_attr_default( | |
261 | module_name=self.__class__.__name__, | |
262 | attr="mtu" | |
263 | ) | |
264 | ||
265 | if not default_mtu: | |
266 | default_mtu = self.DEFAULT_MTU_STRING | |
267 | ||
268 | try: | |
269 | self.default_mtu_int = int(default_mtu) | |
270 | except ValueError as e: | |
271 | self.logger.error("address: invalid default mtu \"%s\" set via policy: %s" % (default_mtu, str(e))) | |
272 | default_mtu = self.DEFAULT_MTU_STRING | |
273 | self.default_mtu_int = int(self.DEFAULT_MTU_STRING) | |
274 | ||
275 | self.logger.info("address: using default mtu %s" % default_mtu) | |
276 | ||
277 | return default_mtu | |
278 | ||
279 | def __policy_get_max_mtu(self): | |
280 | max_mtu = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr="max_mtu") | |
281 | if max_mtu: | |
282 | try: | |
283 | max_mtu_int = int(max_mtu) | |
284 | self.logger.info("address: using max mtu %s" % self.max_mtu) | |
285 | return max_mtu_int | |
286 | except ValueError as e: | |
287 | self.logger.warning("address: policy max_mtu: %s" % str(e)) | |
288 | else: | |
289 | self.logger.info("address: max_mtu undefined") | |
290 | return 0 | |
291 | ||
cbda6dda JF |
292 | def __policy_get_mgmt_intf_mtu(self): |
293 | default_mgmt_mtu = policymanager.policymanager_api.get_module_globals( | |
294 | module_name=self.__class__.__name__, | |
295 | attr="mgmt_intf_mtu") | |
296 | self.default_mgmt_mtu_int = None | |
297 | ||
298 | if not default_mgmt_mtu: | |
299 | return None | |
300 | ||
301 | try: | |
302 | self.default_mgmt_mtu_int = int(default_mgmt_mtu) | |
303 | except ValueError as e: | |
304 | self.logger.error("address: invalid default mgmt mtu \"%s\" set via policy: %s" % (default_mgmt_mtu, str(e))) | |
305 | default_mgmt_mtu = self.DEFAULT_MTU_STRING | |
306 | self.default_mgmt_mtu_int = int(self.DEFAULT_MTU_STRING) | |
307 | ||
308 | self.logger.info("address: using default mgmt interface mtu %s" % default_mgmt_mtu) | |
309 | ||
310 | return default_mgmt_mtu | |
311 | ||
09096420 | 312 | def syntax_check(self, ifaceobj, ifaceobj_getfunc=None): |
20eab2b1 | 313 | self.syntax_check_l3_svi_ip_forward(ifaceobj) |
22b49c28 | 314 | return (self.syntax_check_multiple_gateway(ifaceobj) |
09096420 | 315 | and self.syntax_check_addr_allowed_on(ifaceobj, True) |
d486dd0d JF |
316 | and self.syntax_check_mtu(ifaceobj, ifaceobj_getfunc) |
317 | and self.syntax_check_sysctls(ifaceobj) | |
20eab2b1 | 318 | and self.syntax_check_enable_l3_iface_forwardings(ifaceobj, ifaceobj_getfunc, syntax_check=True)) |
0b34071b JF |
319 | |
320 | def syntax_check_l3_svi_ip_forward(self, ifaceobj): | |
20eab2b1 JF |
321 | """ enabled via policy: 'check_l3_svi_ip_forwarding' """ |
322 | ||
323 | if not self.check_l3_svi_ip_forwarding: | |
324 | return True | |
325 | ||
0b34071b JF |
326 | if ifaceobj.link_kind & ifaceLinkKind.VLAN and ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE: |
327 | ip_forward = ifaceobj.get_attr_value_first("ip-forward") | |
328 | ||
329 | if ip_forward and not utils.get_boolean_from_string(ip_forward): | |
20eab2b1 | 330 | self.logger.error("%s: misconfiguration: disabling ip4 forwarding on an l3-svi is not allowed" % ifaceobj.name) |
0b34071b JF |
331 | return False |
332 | ||
333 | ip6_forward = ifaceobj.get_attr_value_first("ip6-forward") | |
334 | ||
335 | if ip6_forward and not utils.get_boolean_from_string(ip6_forward): | |
20eab2b1 | 336 | self.logger.error("%s: misconfiguration: disabling ip6 forwarding on an l3-svi is not allowed" % ifaceobj.name) |
0b34071b JF |
337 | return False |
338 | ||
339 | return True | |
d486dd0d JF |
340 | |
341 | def syntax_check_enable_l3_iface_forwardings(self, ifaceobj, ifaceobj_getfunc, syntax_check=False): | |
342 | if (self.enable_l3_iface_forwarding_checks | |
343 | and (ifaceobj.link_kind & ifaceLinkKind.VLAN | |
344 | or ifaceobj.link_kind & ifaceLinkKind.BRIDGE) | |
345 | and not ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT): | |
346 | ||
347 | ifname = ifaceobj.name | |
348 | vlan_addr = None | |
349 | vlan_ipforward_off = None | |
350 | ||
351 | for obj in ifaceobj_getfunc(ifname) or [ifaceobj]: | |
352 | if not vlan_addr: | |
353 | vlan_addr = obj.get_attr_value('address') | |
354 | ||
355 | if not vlan_ipforward_off: | |
356 | ip_forward_value = obj.get_attr_value_first('ip-forward') | |
357 | ||
358 | if ip_forward_value and not utils.get_boolean_from_string(ip_forward_value): | |
359 | vlan_ipforward_off = True | |
360 | ||
361 | if vlan_addr and vlan_ipforward_off: | |
362 | if syntax_check: | |
363 | raise Exception( | |
364 | 'configuring ip-forward off and ip address(es) (%s) is not compatible' | |
365 | % (', '.join(vlan_addr)) | |
366 | ) | |
367 | else: | |
368 | raise Exception( | |
369 | '%s: configuring ip-forward off and ip address(es) (%s) is not compatible' | |
370 | % (ifname, ', '.join(vlan_addr)) | |
371 | ) | |
372 | ||
373 | return True | |
374 | ||
375 | def syntax_check_sysctls(self, ifaceobj): | |
376 | result = True | |
377 | bridge_port = (ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT) | |
378 | ipforward = ifaceobj.get_attr_value_first('ip-forward') | |
379 | if bridge_port and ipforward: | |
380 | result = False | |
381 | self.log_error('%s: \'ip-forward\' is not supported for ' | |
382 | 'bridge port' %ifaceobj.name) | |
383 | ip6forward = ifaceobj.get_attr_value_first('ip6-forward') | |
384 | if bridge_port and ip6forward: | |
385 | result = False | |
386 | self.log_error('%s: \'ip6-forward\' is not supported for ' | |
387 | 'bridge port' %ifaceobj.name) | |
388 | return result | |
09096420 RP |
389 | |
390 | def syntax_check_mtu(self, ifaceobj, ifaceobj_getfunc): | |
223ba5af JF |
391 | mtu_str = ifaceobj.get_attr_value_first('mtu') |
392 | if mtu_str: | |
393 | try: | |
394 | mtu_int = int(mtu_str) | |
395 | except ValueError as e: | |
396 | self.logger.warning("%s: invalid mtu %s: %s" % (ifaceobj.name, mtu_str, str(e))) | |
397 | return False | |
398 | return self._check_mtu_config(ifaceobj, mtu_str, mtu_int, ifaceobj_getfunc, syntaxcheck=True) | |
09096420 | 399 | return True |
22b49c28 JF |
400 | |
401 | def syntax_check_addr_allowed_on(self, ifaceobj, syntax_check=False): | |
402 | if ifaceobj.get_attr_value('address'): | |
403 | return utils.is_addr_ip_allowed_on(ifaceobj, syntax_check=syntax_check) | |
404 | return True | |
405 | ||
0e936c3f JF |
406 | def _syntax_check_multiple_gateway(self, family, found, addr, version): |
407 | if ipnetwork.IPNetwork(addr).version == version: | |
38365d4a JF |
408 | if found: |
409 | raise Exception('%s: multiple gateways for %s family' | |
410 | % (addr, family)) | |
411 | return True | |
412 | return False | |
413 | ||
22b49c28 | 414 | def syntax_check_multiple_gateway(self, ifaceobj): |
38365d4a JF |
415 | result = True |
416 | inet = False | |
417 | inet6 = False | |
418 | gateways = ifaceobj.get_attr_value('gateway') | |
419 | for addr in gateways if gateways else []: | |
420 | try: | |
0e936c3f | 421 | if self._syntax_check_multiple_gateway('inet', inet, addr, 4): |
38365d4a | 422 | inet = True |
0e936c3f | 423 | if self._syntax_check_multiple_gateway('inet6', inet6, addr, 6): |
38365d4a JF |
424 | inet6 = True |
425 | except Exception as e: | |
426 | self.logger.warning('%s: address: %s' % (ifaceobj.name, str(e))) | |
427 | result = False | |
428 | return result | |
429 | ||
75afe2a7 RP |
430 | def _address_valid(self, addrs): |
431 | if not addrs: | |
432 | return False | |
3b01ed76 JF |
433 | if any([True if a[:7] != '0.0.0.0' |
434 | else False for a in addrs]): | |
75afe2a7 RP |
435 | return True |
436 | return False | |
437 | ||
428206bf | 438 | def _get_hwaddress(self, ifaceobj): |
d486dd0d | 439 | return utils.strip_hwaddress(ifaceobj.get_attr_value_first('hwaddress')) |
428206bf | 440 | |
4e0f16d0 | 441 | def _process_bridge(self, ifaceobj, up, hwaddress, old_mac_addr=None): |
75afe2a7 | 442 | addrs = ifaceobj.get_attr_value_first('address') |
45db39f6 AD |
443 | arp_accept = ifaceobj.get_attr_value_first('arp-accept') |
444 | arp_accept = utils.boolean_support_binary(arp_accept) | |
75afe2a7 | 445 | is_vlan_dev_on_vlan_aware_bridge = False |
223ba5af | 446 | is_bridge = self.cache.get_link_kind(ifaceobj.name) == 'bridge' |
75afe2a7 | 447 | if not is_bridge: |
d486dd0d JF |
448 | if ifaceobj.link_kind & ifaceLinkKind.VLAN: |
449 | bridgename = ifaceobj.lowerifaces[0] | |
450 | vlan = self._get_vlan_id(ifaceobj) | |
223ba5af JF |
451 | is_vlan_dev_on_vlan_aware_bridge = self.cache.bridge_is_vlan_aware(bridgename) |
452 | if ((is_bridge and not self.cache.bridge_is_vlan_aware(ifaceobj.name)) | |
8c2c9f26 | 453 | or is_vlan_dev_on_vlan_aware_bridge): |
8b57a467 JF |
454 | if self._address_valid(addrs): |
455 | if self.l3_intf_arp_accept: | |
456 | if up: | |
457 | self.write_file('/proc/sys/net/ipv4/conf/%s' % ifaceobj.name + | |
8126ef0c | 458 | '/arp_accept', str(self.l3_intf_arp_accept)) |
8b57a467 JF |
459 | else: |
460 | self.write_file('/proc/sys/net/ipv4/conf/%s' % ifaceobj.name + | |
461 | '/arp_accept', '0') | |
462 | else: | |
45db39f6 | 463 | self.write_file('/proc/sys/net/ipv4/conf/%s/arp_accept' % ifaceobj.name, arp_accept) |
75afe2a7 | 464 | if hwaddress and is_vlan_dev_on_vlan_aware_bridge: |
1db0cb7a JF |
465 | if old_mac_addr: |
466 | # corner case, first the user's /e/n/i is configured without 'hwaddress', then it is used to fix the svi | |
467 | # mac address. The current code only checks for the statemanager for old 'hwaddress' attribute but | |
468 | # couldn't find any. Now we save the mac addr before updating it, so we can later clear it from the fdb. | |
469 | try: | |
4e0f16d0 JF |
470 | if utils.mac_str_to_int(old_mac_addr) != utils.mac_str_to_int(hwaddress): |
471 | self.iproute2.bridge_fdb_del(bridgename, old_mac_addr, vlan) | |
1db0cb7a JF |
472 | except: |
473 | pass | |
15897163 JF |
474 | if up: |
475 | # check statemanager to delete the old entry if necessary | |
476 | try: | |
477 | for old_obj in statemanager.statemanager_api.get_ifaceobjs(ifaceobj.name) or []: | |
478 | old_hwaddress = old_obj.get_attr_value_first("hwaddress") | |
223ba5af | 479 | if old_hwaddress and utils.mac_str_to_int(old_hwaddress) != utils.mac_str_to_int(hwaddress): |
1db0cb7a JF |
480 | if old_hwaddress != old_mac_addr: |
481 | self.iproute2.bridge_fdb_del(bridgename, old_hwaddress, vlan) | |
15897163 | 482 | break |
3218f49d | 483 | except Exception: |
15897163 | 484 | pass |
223ba5af | 485 | self.iproute2.bridge_fdb_add(bridgename, hwaddress, vlan) |
15897163 | 486 | else: |
223ba5af | 487 | self.iproute2.bridge_fdb_del(bridgename, hwaddress, vlan) |
cb46a208 | 488 | |
223ba5af | 489 | def __get_ip_addr_with_attributes(self, ifaceobj_list, ifname): |
0e936c3f | 490 | user_config_ip_addrs_list = [] |
0582f185 | 491 | |
223ba5af | 492 | try: |
01a65536 | 493 | for ifaceobj in ifaceobj_list or []: |
0582f185 | 494 | |
223ba5af | 495 | user_addrs = ifaceobj.get_attr_value("address") |
0582f185 | 496 | |
223ba5af JF |
497 | if not user_addrs: |
498 | continue | |
499 | ||
500 | if not self.syntax_check_addr_allowed_on(ifaceobj, syntax_check=False): | |
501 | return False, None | |
502 | ||
503 | for index, addr in enumerate(user_addrs): | |
504 | addr_attributes = {} | |
505 | addr_obj = None | |
506 | ||
507 | # convert the ip from string to IPNetwork object | |
508 | if "/" in addr: | |
0e936c3f | 509 | addr_obj = ipnetwork.IPNetwork(addr) |
8de397ef | 510 | else: |
223ba5af JF |
511 | netmask = ifaceobj.get_attr_value_n("netmask", index) |
512 | ||
513 | if netmask: | |
0e936c3f | 514 | addr_obj = ipnetwork.IPNetwork(addr, netmask) |
223ba5af | 515 | else: |
0e936c3f | 516 | addr_obj = ipnetwork.IPNetwork(addr) |
223ba5af | 517 | |
3fd6c201 | 518 | for attr_name in ("broadcast", "scope", "preferred-lifetime"): |
223ba5af JF |
519 | attr_value = ifaceobj.get_attr_value_n(attr_name, index) |
520 | if attr_value: | |
521 | addr_attributes[attr_name] = attr_value | |
522 | ||
523 | pointopoint = ifaceobj.get_attr_value_n("pointopoint", index) | |
524 | try: | |
525 | if pointopoint: | |
0e936c3f | 526 | addr_attributes["pointopoint"] = ipnetwork.IPNetwork(pointopoint) |
223ba5af JF |
527 | except Exception as e: |
528 | self.logger.warning("%s: pointopoint %s: %s" % (ifaceobj.name, pointopoint, str(e))) | |
529 | ||
530 | user_config_ip_addrs_list.append((addr_obj, addr_attributes)) | |
531 | except Exception as e: | |
01a65536 | 532 | self.log_error("%s: convert string ip address into IPNetwork object: %s" % (ifname, str(e)), ifaceobj) |
223ba5af JF |
533 | return False, None |
534 | ||
535 | return True, user_config_ip_addrs_list | |
536 | ||
537 | def __add_ip_addresses_with_attributes(self, ifaceobj, ifname, user_config_ip_addrs): | |
190cf3e6 | 538 | ipv6_is_disabled = None |
beaffab6 AB |
539 | nodad = False |
540 | if self.ipv6_dad_handling_enabled: | |
541 | nodad = ifaceobj.get_attr_value_first('dad-attempts') == '0' | |
190cf3e6 | 542 | |
e90c33ca JF |
543 | for ip, attributes in user_config_ip_addrs: |
544 | try: | |
190cf3e6 JF |
545 | if ip.version == 6 and ipv6_is_disabled is None: |
546 | # check (only once) if ipv6 is disabled on this device | |
547 | proc_path = "/proc/sys/net/ipv6/conf/%s/disable_ipv6" % ifname | |
548 | ipv6_is_disabled = utils.get_boolean_from_string(self.read_file_oneline(proc_path)) | |
549 | ||
550 | if ipv6_is_disabled: | |
551 | # enable ipv6 | |
552 | self.write_file(proc_path, "0") | |
553 | ||
3eb08b79 JF |
554 | # check if ip is not blacklisted |
555 | self.ip_blacklist_check(ifname, ip) | |
556 | ||
223ba5af JF |
557 | if attributes: |
558 | self.netlink.addr_add( | |
559 | ifname, ip, | |
560 | scope=attributes.get("scope"), | |
561 | peer=attributes.get("pointopoint"), | |
562 | broadcast=attributes.get("broadcast"), | |
beaffab6 AB |
563 | preferred_lifetime=attributes.get("preferred-lifetime"), |
564 | nodad=nodad | |
223ba5af | 565 | ) |
77021aa1 | 566 | else: |
beaffab6 | 567 | self.netlink.addr_add(ifname, ip, nodad=nodad) |
e90c33ca JF |
568 | except Exception as e: |
569 | self.log_error(str(e), ifaceobj, raise_error=False) | |
77021aa1 | 570 | |
223ba5af JF |
571 | @staticmethod |
572 | def __add_loopback_anycast_ip_to_running_ip_addr_list(ifaceobjlist): | |
573 | """ | |
574 | if anycast address is configured on 'lo' and is in running | |
575 | config add it to newaddrs so that ifreload doesn't wipe it out | |
576 | :param ifaceobjlist: | |
577 | :param running_ip_addrs: | |
578 | """ | |
579 | anycast_ip_addr = None | |
0582f185 | 580 | |
223ba5af JF |
581 | for ifaceobj in ifaceobjlist: |
582 | anycast_addr = ifaceobj.get_attr_value_first("clagd-vxlan-anycast-ip") | |
583 | if anycast_addr: | |
0e936c3f | 584 | anycast_ip_addr = ipnetwork.IPNetwork(anycast_addr) |
223ba5af | 585 | |
0e936c3f | 586 | return anycast_ip_addr |
223ba5af JF |
587 | |
588 | def process_addresses(self, ifaceobj, ifaceobj_getfunc=None, force_reapply=False): | |
589 | squash_addr_config = ifupdownconfig.config.get("addr_config_squash", "0") == "1" | |
590 | ||
591 | if squash_addr_config and not ifaceobj.flags & ifaceobj.YOUNGEST_SIBLING: | |
0582f185 RP |
592 | return |
593 | ||
223ba5af JF |
594 | ifname = ifaceobj.name |
595 | purge_addresses = utils.get_boolean_from_string(ifaceobj.get_attr_value_first("address-purge"), default=True) | |
596 | ||
597 | if not squash_addr_config and ifaceobj.flags & iface.HAS_SIBLINGS: | |
598 | # if youngest sibling and squash addr is not set | |
599 | # print a warning that addresses will not be purged | |
600 | if ifaceobj.flags & iface.YOUNGEST_SIBLING: | |
601 | self.logger.warning("%s: interface has multiple iface stanzas, skip purging existing addresses" % ifname) | |
602 | purge_addresses = False | |
0582f185 RP |
603 | |
604 | if squash_addr_config and ifaceobj.flags & iface.HAS_SIBLINGS: | |
223ba5af | 605 | ifaceobj_list = ifaceobj_getfunc(ifname) |
0582f185 | 606 | else: |
223ba5af | 607 | ifaceobj_list = [ifaceobj] |
d486dd0d | 608 | |
223ba5af | 609 | addr_supported, user_config_ip_addrs_list = self.__get_ip_addr_with_attributes(ifaceobj_list, ifname) |
d486dd0d | 610 | |
0582f185 RP |
611 | if not addr_supported: |
612 | return | |
0582f185 | 613 | |
223ba5af JF |
614 | if not ifupdownflags.flags.PERFMODE and purge_addresses: |
615 | # if perfmode is not set and purge addresses is set to True | |
0582f185 | 616 | # lets purge addresses not in the config |
223ba5af JF |
617 | anycast_ip = None |
618 | ||
0e936c3f | 619 | running_ip_addrs = self.cache.get_managed_ip_addresses(ifname, ifaceobj_list) |
0582f185 | 620 | |
223ba5af JF |
621 | if ifaceobj.link_privflags & ifaceLinkPrivFlags.LOOPBACK: |
622 | anycast_ip = self.__add_loopback_anycast_ip_to_running_ip_addr_list(ifaceobj_list) | |
0582f185 | 623 | |
223ba5af | 624 | user_ip4, user_ip6, ordered_user_configured_ips = self.order_user_configured_addrs(user_config_ip_addrs_list) |
d486dd0d | 625 | |
223ba5af | 626 | if ordered_user_configured_ips == running_ip_addrs or self.compare_running_ips_and_user_config(user_ip4, user_ip6, running_ip_addrs): |
77021aa1 | 627 | if force_reapply: |
223ba5af | 628 | self.__add_ip_addresses_with_attributes(ifaceobj, ifname, user_config_ip_addrs_list) |
15ef32ea RP |
629 | return |
630 | try: | |
223ba5af | 631 | # if primary address is not same, there is no need to keep any, reset all addresses. |
0e936c3f | 632 | if ordered_user_configured_ips and running_ip_addrs and ordered_user_configured_ips[0] != running_ip_addrs[0]: |
223ba5af | 633 | self.logger.info("%s: primary ip changed (from %s to %s) we need to purge all ip addresses and re-add them" |
0e936c3f | 634 | % (ifname, ordered_user_configured_ips[0], running_ip_addrs[0])) |
d486dd0d | 635 | skip_addrs = [] |
15ef32ea | 636 | else: |
223ba5af JF |
637 | skip_addrs = ordered_user_configured_ips |
638 | ||
639 | if anycast_ip: | |
640 | skip_addrs.append(anycast_ip) | |
641 | ||
0e936c3f | 642 | for addr in running_ip_addrs: |
d486dd0d JF |
643 | if addr in skip_addrs: |
644 | continue | |
0e936c3f | 645 | self.netlink.addr_del(ifname, addr) |
3b01ed76 | 646 | except Exception as e: |
15ef32ea | 647 | self.log_warn(str(e)) |
223ba5af | 648 | if not user_config_ip_addrs_list: |
15ef32ea | 649 | return |
223ba5af JF |
650 | self.__add_ip_addresses_with_attributes(ifaceobj, ifname, user_config_ip_addrs_list) |
651 | ||
d486dd0d JF |
652 | def compare_running_ips_and_user_config(self, user_ip4, user_ip6, running_addrs): |
653 | """ | |
654 | We need to compare the user config ips and the running ips. | |
655 | ip4 ordering matters (primary etc) but ip6 order doesn't matter | |
656 | ||
657 | this function replaces the strict comparison previously in place | |
658 | if newaddrs == running_addrs ? | |
659 | ||
660 | We will compare if the ip4 ordering is correct, then check if all | |
661 | ip6 are present in the list (without checking the ordering) | |
662 | """ | |
663 | if (user_ip4 or user_ip6) and not running_addrs: | |
664 | return False | |
665 | elif running_addrs and not user_ip4 and not user_ip6: | |
666 | return False | |
667 | elif not running_addrs and not user_ip4 and not user_ip6: | |
668 | return True | |
669 | ||
670 | len_ip4 = len(user_ip4) | |
671 | len_running_addrs = len(running_addrs) | |
672 | ||
673 | if len_ip4 > len_running_addrs: | |
674 | return False | |
675 | ||
676 | i = 0 | |
677 | while i < len_ip4: | |
678 | if user_ip4[i] != running_addrs[i]: | |
679 | return False | |
680 | i += 1 | |
681 | ||
682 | if len_ip4 > 0: | |
683 | running_ip6 = running_addrs[len_ip4:] | |
684 | else: | |
685 | running_ip6 = running_addrs | |
686 | ||
687 | i = 0 | |
688 | len_ip6 = len(user_ip6) | |
689 | ||
690 | for ip6 in running_ip6: | |
691 | if ip6 not in user_ip6: | |
692 | return False | |
693 | i += 1 | |
694 | ||
695 | return i == len_ip6 | |
696 | ||
223ba5af JF |
697 | @staticmethod |
698 | def order_user_configured_addrs(user_config_addrs): | |
d486dd0d JF |
699 | ip4 = [] |
700 | ip6 = [] | |
701 | ||
223ba5af JF |
702 | for a, _ in user_config_addrs: |
703 | if a.version == 6: | |
0e936c3f | 704 | ip6.append(a) |
d486dd0d | 705 | else: |
0e936c3f | 706 | ip4.append(a) |
d486dd0d JF |
707 | |
708 | return ip4, ip6, ip4 + ip6 | |
709 | ||
710 | def _delete_gateway(self, ifaceobj, gateways, vrf, metric): | |
711 | for del_gw in gateways: | |
0232d1bb | 712 | try: |
223ba5af | 713 | self.iproute2.route_del_gateway(ifaceobj.name, del_gw, vrf, metric) |
5b666749 JF |
714 | except Exception as e: |
715 | self.logger.debug('%s: %s' % (ifaceobj.name, str(e))) | |
d486dd0d JF |
716 | |
717 | def _add_delete_gateway(self, ifaceobj, gateways=[], prev_gw=[]): | |
718 | vrf = ifaceobj.get_attr_value_first('vrf') | |
719 | metric = ifaceobj.get_attr_value_first('metric') | |
720 | self._delete_gateway(ifaceobj, list(set(prev_gw) - set(gateways)), | |
721 | vrf, metric) | |
4b875c17 | 722 | for add_gw in gateways: |
0232d1bb | 723 | try: |
223ba5af | 724 | self.iproute2.route_add_gateway(ifaceobj.name, add_gw, vrf, metric, onlink=self.l3_intf_default_gateway_set_onlink) |
5b666749 JF |
725 | except Exception as e: |
726 | self.log_error('%s: %s' % (ifaceobj.name, str(e))) | |
0232d1bb N |
727 | |
728 | def _get_prev_gateway(self, ifaceobj, gateways): | |
729 | ipv = [] | |
730 | saved_ifaceobjs = statemanager.statemanager_api.get_ifaceobjs(ifaceobj.name) | |
731 | if not saved_ifaceobjs: | |
732 | return ipv | |
733 | prev_gateways = saved_ifaceobjs[0].get_attr_value('gateway') | |
734 | if not prev_gateways: | |
735 | return ipv | |
736 | return prev_gateways | |
737 | ||
223ba5af | 738 | def _check_mtu_config(self, ifaceobj, mtu_str, mtu_int, ifaceobj_getfunc, syntaxcheck=False): |
8d1e346f RP |
739 | retval = True |
740 | if (ifaceobj.link_kind & ifaceLinkKind.BRIDGE): | |
741 | if syntaxcheck: | |
c46af1c9 | 742 | self.logger.warning('%s: bridge inherits mtu from its ports. There is no need to assign mtu on a bridge' %ifaceobj.name) |
8d1e346f RP |
743 | retval = False |
744 | else: | |
9f30b2cc | 745 | self.logger.info('%s: bridge inherits mtu from its ports. There is no need to assign mtu on a bridge' %ifaceobj.name) |
8d1e346f RP |
746 | elif ifaceobj_getfunc: |
747 | if ((ifaceobj.link_privflags & ifaceLinkPrivFlags.BOND_SLAVE) and | |
748 | ifaceobj.upperifaces): | |
9f30b2cc RP |
749 | masterobj = ifaceobj_getfunc(ifaceobj.upperifaces[0]) |
750 | if masterobj: | |
751 | master_mtu = masterobj[0].get_attr_value_first('mtu') | |
223ba5af JF |
752 | if master_mtu and master_mtu != mtu_str: |
753 | log_msg = ("%s: bond slave mtu %s is different from bond master %s mtu %s. " | |
754 | "There is no need to configure mtu on a bond slave." % | |
755 | (ifaceobj.name, mtu_str, masterobj[0].name, master_mtu)) | |
8d1e346f | 756 | if syntaxcheck: |
c46af1c9 | 757 | self.logger.warning(log_msg) |
8d1e346f RP |
758 | retval = False |
759 | else: | |
223ba5af | 760 | self.logger.info(log_msg) |
8d1e346f RP |
761 | elif ((ifaceobj.link_kind & ifaceLinkKind.VLAN) and |
762 | ifaceobj.lowerifaces): | |
763 | lowerobj = ifaceobj_getfunc(ifaceobj.lowerifaces[0]) | |
764 | if lowerobj: | |
765 | if syntaxcheck: | |
223ba5af | 766 | lowerdev_mtu = int(lowerobj[0].get_attr_value_first('mtu') or 0) |
8d1e346f | 767 | else: |
223ba5af JF |
768 | lowerdev_mtu = self.cache.get_link_mtu(lowerobj[0].name) # return type: int |
769 | if lowerdev_mtu and mtu_int > lowerdev_mtu: | |
c46af1c9 | 770 | self.logger.warning('%s: vlan dev mtu %s is greater than lower realdev %s mtu %s' |
223ba5af | 771 | %(ifaceobj.name, mtu_str, lowerobj[0].name, lowerdev_mtu)) |
8d1e346f RP |
772 | retval = False |
773 | elif (not lowerobj[0].link_kind and | |
774 | not (lowerobj[0].link_privflags & ifaceLinkPrivFlags.LOOPBACK) and | |
223ba5af | 775 | not lowerdev_mtu and self.default_mtu and (mtu_int > self.default_mtu_int)): |
8d1e346f | 776 | # only check default mtu on lower device which is a physical interface |
c46af1c9 | 777 | self.logger.warning('%s: vlan dev mtu %s is greater than lower realdev %s mtu %s' |
223ba5af | 778 | %(ifaceobj.name, mtu_str, lowerobj[0].name, self.default_mtu)) |
8d1e346f | 779 | retval = False |
223ba5af | 780 | if self.max_mtu and mtu_int > self.max_mtu: |
c46af1c9 | 781 | self.logger.warning('%s: specified mtu %s is greater than max mtu %s' |
223ba5af | 782 | %(ifaceobj.name, mtu_str, self.max_mtu)) |
8d1e346f RP |
783 | retval = False |
784 | return retval | |
785 | ||
223ba5af | 786 | def _propagate_mtu_to_upper_devs(self, ifaceobj, mtu_str, mtu_int, ifaceobj_getfunc): |
8d1e346f RP |
787 | if (not ifaceobj.upperifaces or |
788 | (ifaceobj.link_privflags & ifaceLinkPrivFlags.BOND_SLAVE) or | |
789 | (ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE) or | |
790 | (ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT)): | |
791 | return | |
792 | for u in ifaceobj.upperifaces: | |
793 | upperobjs = ifaceobj_getfunc(u) | |
794 | if (not upperobjs or | |
795 | not (upperobjs[0].link_kind & ifaceLinkKind.VLAN)): | |
796 | continue | |
797 | # only adjust mtu for vlan devices on ifaceobj | |
798 | umtu = upperobjs[0].get_attr_value_first('mtu') | |
799 | if not umtu: | |
223ba5af JF |
800 | running_mtu = self.cache.get_link_mtu(upperobjs[0].name) |
801 | if not running_mtu or running_mtu != mtu_int: | |
802 | self.sysfs.link_set_mtu(u, mtu_str=mtu_str, mtu_int=mtu_int) | |
8d1e346f | 803 | |
223ba5af JF |
804 | def _process_mtu_config_mtu_valid(self, ifaceobj, ifaceobj_getfunc, mtu_str, mtu_int): |
805 | if not self._check_mtu_config(ifaceobj, mtu_str, mtu_int, ifaceobj_getfunc): | |
806 | return | |
807 | ||
808 | if mtu_int != self.cache.get_link_mtu(ifaceobj.name): | |
809 | self.sysfs.link_set_mtu(ifaceobj.name, mtu_str=mtu_str, mtu_int=mtu_int) | |
810 | ||
811 | if (not ifupdownflags.flags.ALL and | |
8d1e346f | 812 | not ifaceobj.link_kind and |
d486dd0d | 813 | ifupdownconfig.config.get('adjust_logical_dev_mtu', '1') != '0'): |
223ba5af JF |
814 | # This is additional cost to us, so do it only when |
815 | # ifupdown2 is called on a particular interface and | |
816 | # it is a physical interface | |
817 | self._propagate_mtu_to_upper_devs(ifaceobj, mtu_str, mtu_int, ifaceobj_getfunc) | |
818 | return | |
819 | ||
820 | def _process_mtu_config_mtu_none(self, ifaceobj): | |
cbda6dda JF |
821 | |
822 | if (ifaceobj.link_privflags & ifaceLinkPrivFlags.MGMT_INTF): | |
823 | return | |
824 | ||
223ba5af | 825 | cached_link_mtu = self.cache.get_link_mtu(ifaceobj.name) |
9f30b2cc RP |
826 | |
827 | if ifaceobj.link_kind: | |
19ee2b11 | 828 | # bonds, vxlan and custom devices (like dummy) need an explicit set of mtu. |
9f30b2cc | 829 | # bridges don't need mtu set |
223ba5af JF |
830 | if ifaceobj.link_kind & ifaceLinkKind.BOND \ |
831 | or ifaceobj.link_kind & ifaceLinkKind.VXLAN \ | |
a3df9e69 | 832 | or ifaceobj.link_kind & ifaceLinkKind.BRIDGE \ |
223ba5af JF |
833 | or ifaceobj.link_kind & ifaceLinkKind.OTHER: |
834 | if cached_link_mtu != self.default_mtu_int: | |
835 | self.sysfs.link_set_mtu(ifaceobj.name, mtu_str=self.default_mtu, mtu_int=self.default_mtu_int) | |
9f30b2cc | 836 | return |
223ba5af | 837 | |
d486dd0d | 838 | if (ifupdownconfig.config.get('adjust_logical_dev_mtu', '1') != '0' |
9f30b2cc RP |
839 | and ifaceobj.lowerifaces): |
840 | # set vlan interface mtu to lower device mtu | |
841 | if (ifaceobj.link_kind & ifaceLinkKind.VLAN): | |
d486dd0d | 842 | lower_iface = ifaceobj.lowerifaces[0] |
223ba5af | 843 | lower_iface_mtu_int = self.cache.get_link_mtu(lower_iface) |
d486dd0d | 844 | |
223ba5af JF |
845 | if lower_iface_mtu_int != cached_link_mtu: |
846 | self.sysfs.link_set_mtu(ifaceobj.name, mtu_str=str(lower_iface_mtu_int), mtu_int=lower_iface_mtu_int) | |
9f30b2cc RP |
847 | |
848 | elif (not (ifaceobj.name == 'lo') and not ifaceobj.link_kind and | |
849 | not (ifaceobj.link_privflags & ifaceLinkPrivFlags.BOND_SLAVE) and | |
850 | self.default_mtu): | |
851 | # logical devices like bridges and vlan devices rely on mtu | |
852 | # from their lower devices. ie mtu travels from | |
853 | # lower devices to upper devices. For bonds mtu travels from | |
854 | # upper to lower devices. running mtu depends on upper and | |
855 | # lower device mtu. With all this implicit mtu | |
856 | # config by the kernel in play, we try to be cautious here | |
857 | # on which devices we want to reset mtu to default. | |
858 | # essentially only physical interfaces which are not bond slaves | |
223ba5af JF |
859 | if cached_link_mtu != self.default_mtu_int: |
860 | self.sysfs.link_set_mtu(ifaceobj.name, mtu_str=self.default_mtu, mtu_int=self.default_mtu_int) | |
9f30b2cc | 861 | |
d486dd0d JF |
862 | def _set_bridge_forwarding(self, ifaceobj): |
863 | """ set ip forwarding to 0 if bridge interface does not have a | |
864 | ip nor svi """ | |
2185a108 | 865 | ifname = ifaceobj.name |
223ba5af JF |
866 | |
867 | netconf_ipv4_forwarding = self.cache.get_netconf_forwarding(socket.AF_INET, ifname) | |
868 | netconf_ipv6_forwarding = self.cache.get_netconf_forwarding(socket.AF_INET6, ifname) | |
869 | ||
69825bb4 | 870 | if not ifaceobj.upperifaces and not ifaceobj.get_attr_value('address') and (ifaceobj.addr_method and "dhcp" not in ifaceobj.addr_method): |
223ba5af | 871 | if netconf_ipv4_forwarding: |
2185a108 | 872 | self.sysctl_write_forwarding_value_to_proc(ifname, "ipv4", 0) |
223ba5af | 873 | if netconf_ipv6_forwarding: |
2185a108 | 874 | self.sysctl_write_forwarding_value_to_proc(ifname, "ipv6", 0) |
d486dd0d | 875 | else: |
223ba5af | 876 | if not netconf_ipv4_forwarding: |
2185a108 | 877 | self.sysctl_write_forwarding_value_to_proc(ifname, "ipv4", 1) |
223ba5af | 878 | if not netconf_ipv6_forwarding: |
2185a108 JF |
879 | self.sysctl_write_forwarding_value_to_proc(ifname, "ipv6", 1) |
880 | ||
2185a108 JF |
881 | def sysctl_write_forwarding_value_to_proc(self, ifname, family, value): |
882 | self.write_file("/proc/sys/net/%s/conf/%s/forwarding" % (family, ifname), "%s\n" % value) | |
d486dd0d JF |
883 | |
884 | def _sysctl_config(self, ifaceobj): | |
885 | setting_default_value = False | |
886 | mpls_enable = ifaceobj.get_attr_value_first('mpls-enable'); | |
887 | if not mpls_enable: | |
888 | setting_default_value = True | |
889 | mpls_enable = self.get_mod_subattr('mpls-enable', 'default') | |
890 | mpls_enable = utils.boolean_support_binary(mpls_enable) | |
891 | # File read has been used for better performance | |
892 | # instead of using sysctl command | |
893 | if ifupdownflags.flags.PERFMODE: | |
894 | running_mpls_enable = '0' | |
895 | else: | |
223ba5af | 896 | running_mpls_enable = str(self.cache.get_netconf_mpls_input(ifaceobj.name)) |
d486dd0d JF |
897 | |
898 | if mpls_enable != running_mpls_enable: | |
899 | try: | |
900 | self.sysctl_set('net.mpls.conf.%s.input' | |
901 | %('/'.join(ifaceobj.name.split("."))), | |
902 | mpls_enable) | |
903 | except Exception as e: | |
904 | if not setting_default_value: | |
905 | ifaceobj.status = ifaceStatus.ERROR | |
906 | self.logger.error('%s: %s' %(ifaceobj.name, str(e))) | |
907 | ||
908 | if (ifaceobj.link_kind & ifaceLinkKind.BRIDGE): | |
909 | self._set_bridge_forwarding(ifaceobj) | |
910 | return | |
911 | if not self.syntax_check_sysctls(ifaceobj): | |
912 | return | |
0b34071b JF |
913 | if not self.syntax_check_l3_svi_ip_forward(ifaceobj): |
914 | return | |
915 | ||
d486dd0d JF |
916 | ipforward = ifaceobj.get_attr_value_first('ip-forward') |
917 | ip6forward = ifaceobj.get_attr_value_first('ip6-forward') | |
918 | if ifupdownflags.flags.PERFMODE: | |
919 | if ipforward: | |
920 | self.sysctl_set('net.ipv4.conf.%s.forwarding' | |
921 | %('/'.join(ifaceobj.name.split("."))), | |
922 | utils.boolean_support_binary(ipforward)) | |
923 | if ip6forward: | |
924 | self.sysctl_set('net.ipv6.conf.%s.forwarding' | |
925 | %('/'.join(ifaceobj.name.split("."))), | |
926 | utils.boolean_support_binary(ip6forward)) | |
927 | return | |
928 | bridge_port = ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT | |
929 | if bridge_port: | |
930 | if ipforward: | |
931 | self.log_error('%s: \'ip-forward\' is not supported for ' | |
932 | 'bridge port' %ifaceobj.name) | |
933 | if ip6forward: | |
934 | self.log_error('%s: \'ip6-forward\' is not supported for ' | |
935 | 'bridge port' %ifaceobj.name) | |
936 | return | |
52712b1a AD |
937 | setting_default_value = False |
938 | if not ipforward: | |
939 | setting_default_value = True | |
34de8712 AD |
940 | ipforward = self.ipforward |
941 | if ipforward: | |
942 | ipforward = int(utils.get_boolean_from_string(ipforward)) | |
943 | running_ipforward = self.cache.get_netconf_forwarding(socket.AF_INET, ifaceobj.name) | |
944 | if ipforward != running_ipforward: | |
945 | try: | |
946 | self.sysctl_set('net.ipv4.conf.%s.forwarding' | |
947 | %('/'.join(ifaceobj.name.split("."))), | |
948 | ipforward) | |
949 | except Exception as e: | |
950 | if not setting_default_value: | |
951 | ifaceobj.status = ifaceStatus.ERROR | |
952 | self.logger.error('%s: %s' %(ifaceobj.name, str(e))) | |
52712b1a AD |
953 | |
954 | setting_default_value = False | |
34de8712 | 955 | |
52712b1a AD |
956 | if not ip6forward: |
957 | setting_default_value = True | |
34de8712 AD |
958 | ip6forward = self.ip6forward |
959 | ||
960 | if ip6forward: | |
961 | ip6forward = int(utils.get_boolean_from_string(ip6forward)) | |
962 | running_ip6forward = self.cache.get_netconf_forwarding(socket.AF_INET6, ifaceobj.name) | |
963 | if ip6forward != running_ip6forward: | |
964 | try: | |
965 | self.sysctl_set('net.ipv6.conf.%s.forwarding' | |
966 | %('/'.join(ifaceobj.name.split("."))), | |
967 | ip6forward) | |
968 | except Exception as e: | |
969 | # There is chance of ipv6 being removed because of, | |
970 | # for example, setting mtu < 1280 | |
971 | # In such cases, log error only if user has configured | |
972 | # ip6-forward | |
973 | if not setting_default_value: | |
974 | ifaceobj.status = ifaceStatus.ERROR | |
975 | self.logger.error('%s: %s' %(ifaceobj.name, str(e))) | |
d486dd0d JF |
976 | |
977 | def process_mtu(self, ifaceobj, ifaceobj_getfunc): | |
8994bdd3 AD |
978 | |
979 | if ifaceobj.link_privflags & ifaceLinkPrivFlags.OPENVSWITCH: | |
980 | return | |
981 | ||
223ba5af JF |
982 | mtu_str = ifaceobj.get_attr_value_first('mtu') |
983 | mtu_from_policy = False | |
d486dd0d | 984 | |
223ba5af | 985 | if not mtu_str: |
cbda6dda JF |
986 | if (ifaceobj.link_privflags & ifaceLinkPrivFlags.MGMT_INTF): |
987 | mtu_str = self.default_mgmt_intf_mtu | |
988 | if not mtu_str: | |
989 | mtu_str = self.ifaces_defaults.get(ifaceobj.name, {}).get('mtu') | |
990 | ||
223ba5af | 991 | mtu_from_policy = True |
d486dd0d | 992 | |
223ba5af JF |
993 | if mtu_str: |
994 | try: | |
995 | mtu_int = int(mtu_str) | |
996 | except Exception as e: | |
997 | if mtu_from_policy: | |
998 | self.logger.warning("%s: invalid MTU value from policy file (iface_defaults): %s" % (ifaceobj.name, str(e))) | |
999 | else: | |
1000 | self.logger.warning("%s: invalid MTU value: %s" % (ifaceobj.name, str(e))) | |
1001 | return | |
d486dd0d | 1002 | |
223ba5af JF |
1003 | self._process_mtu_config_mtu_valid(ifaceobj, ifaceobj_getfunc, mtu_str, mtu_int) |
1004 | else: | |
1005 | self._process_mtu_config_mtu_none(ifaceobj) | |
d486dd0d | 1006 | |
3fc54eef | 1007 | def up_ipv6_addrgen(self, ifaceobj): |
007cae35 JF |
1008 | user_configured_ipv6_addrgen = ifaceobj.get_attr_value_first('ipv6-addrgen') |
1009 | ||
b306a8b6 JF |
1010 | if not user_configured_ipv6_addrgen and ifupdownflags.flags.PERFMODE: |
1011 | # no need to go further during perfmode (boot) | |
1012 | return | |
1013 | ||
223ba5af | 1014 | if not user_configured_ipv6_addrgen and ifaceobj.addr_method in ["dhcp", "ppp"]: |
cd890b06 JF |
1015 | return |
1016 | ||
007cae35 | 1017 | if not user_configured_ipv6_addrgen: |
17da0561 JF |
1018 | # if user didn't configure ipv6-addrgen, should we reset to default? |
1019 | user_configured_ipv6_addrgen = self.get_attr_default_value('ipv6-addrgen') | |
007cae35 JF |
1020 | |
1021 | ipv6_addrgen_nl = { | |
1022 | 'on': 0, | |
1023 | 'yes': 0, | |
1024 | '0': 0, | |
1025 | 'off': 1, | |
1026 | 'no': 1, | |
1027 | '1': 1 | |
1028 | }.get(user_configured_ipv6_addrgen.lower(), None) | |
1029 | ||
1030 | if ipv6_addrgen_nl is not None: | |
223ba5af JF |
1031 | self.iproute2.batch_start() |
1032 | self.iproute2.link_set_ipv6_addrgen(ifaceobj.name, ipv6_addrgen_nl, link_created=True) | |
1033 | self.iproute2.batch_commit() | |
007cae35 JF |
1034 | # link_create=False will flush the addr cache of that intf |
1035 | else: | |
1036 | self.logger.warning('%s: invalid value "%s" for attribute ipv6-addrgen' % (ifaceobj.name, user_configured_ipv6_addrgen)) | |
3fc54eef | 1037 | |
223ba5af JF |
1038 | def _pre_up(self, ifaceobj, ifaceobj_getfunc=None): |
1039 | if not self.cache.link_exists(ifaceobj.name): | |
15ef32ea | 1040 | return |
2ff98bb9 | 1041 | |
d486dd0d JF |
1042 | if not self.syntax_check_enable_l3_iface_forwardings(ifaceobj, ifaceobj_getfunc): |
1043 | return | |
1044 | ||
223ba5af JF |
1045 | # |
1046 | # alias | |
1047 | # | |
1048 | self.sysfs.link_set_alias(ifaceobj.name, ifaceobj.get_attr_value_first("alias")) | |
2ff98bb9 | 1049 | |
d486dd0d JF |
1050 | self._sysctl_config(ifaceobj) |
1051 | ||
68d9fee0 | 1052 | addr_method = ifaceobj.addr_method |
77021aa1 | 1053 | force_reapply = False |
15ef32ea RP |
1054 | try: |
1055 | # release any stale dhcp addresses if present | |
223ba5af | 1056 | if (addr_method not in ["dhcp", "ppp"] and not ifupdownflags.flags.PERFMODE and |
15ef32ea RP |
1057 | not (ifaceobj.flags & iface.HAS_SIBLINGS)): |
1058 | # if not running in perf mode and ifaceobj does not have | |
1059 | # any sibling iface objects, kill any stale dhclient | |
1060 | # processes | |
75afe2a7 | 1061 | dhclientcmd = dhclient() |
7c1135ea | 1062 | if dhclientcmd.is_running(ifaceobj.name): |
15ef32ea RP |
1063 | # release any dhcp leases |
1064 | dhclientcmd.release(ifaceobj.name) | |
223ba5af | 1065 | self.cache.force_address_flush_family(ifaceobj.name, socket.AF_INET) |
77021aa1 | 1066 | force_reapply = True |
7c1135ea | 1067 | elif dhclientcmd.is_running6(ifaceobj.name): |
15ef32ea | 1068 | dhclientcmd.release6(ifaceobj.name) |
223ba5af | 1069 | self.cache.force_address_flush_family(ifaceobj.name, socket.AF_INET6) |
77021aa1 | 1070 | force_reapply = True |
3218f49d | 1071 | except Exception: |
15ef32ea | 1072 | pass |
8e113d63 | 1073 | |
2c152f83 | 1074 | self.process_mtu(ifaceobj, ifaceobj_getfunc) |
3fc54eef JF |
1075 | self.up_ipv6_addrgen(ifaceobj) |
1076 | ||
77054f7f | 1077 | if addr_method not in ["dhcp", "ppp"]: |
223ba5af | 1078 | self.process_addresses(ifaceobj, ifaceobj_getfunc, force_reapply) |
2e2dcdaf JF |
1079 | else: |
1080 | # remove old addresses added by ifupdown2 | |
1081 | # (if intf was moved from static config to dhcp) | |
1082 | for old_ifaceobj in statemanager.statemanager_api.get_ifaceobjs(ifaceobj.name) or []: | |
1083 | for addr in old_ifaceobj.get_attr_value("address") or []: | |
a3f9506e | 1084 | self.netlink.addr_del(ifaceobj.name, ipnetwork.IPNetwork(addr)) |
d486dd0d | 1085 | |
13e22530 | 1086 | |
093ffa00 | 1087 | try: |
223ba5af | 1088 | # Handle special things on a bridge |
4e0f16d0 | 1089 | self._process_bridge(ifaceobj, True, *self.process_hwaddress(ifaceobj)) |
3b01ed76 | 1090 | except Exception as e: |
223ba5af | 1091 | self.log_error('%s: %s' % (ifaceobj.name, str(e)), ifaceobj) |
d1477c4b | 1092 | |
b99c724a AB |
1093 | def _settle_dad(self, ifaceobj, ips): |
1094 | """ Settle dad for any given ips """ | |
1095 | def ip_addr_list(what): | |
1096 | raw = json.loads(utils.exec_commandl([ | |
1097 | 'ip', '-j', '-o', '-6', 'address', 'list', 'dev', | |
1098 | ifaceobj.name, what | |
1099 | ])) | |
1100 | addr_infos = (x for t in raw for x in t.get('addr_info', [])) | |
1101 | ip_list = [f'{x["local"]}/{x["prefixlen"]}' for x in addr_infos if x] | |
1102 | return ip_list | |
1103 | ||
1104 | def get_param(key, default=None): | |
1105 | return (ifaceobj.get_attr_value_first(key) | |
1106 | or policymanager.policymanager_api.get_iface_default( | |
1107 | self.__class__.__name__, ifaceobj.name, key) | |
1108 | or default) | |
1109 | ||
1110 | interval = float(get_param('dad-interval', '0.1')) # 0.1: ifupdown default value | |
1111 | attempts = int(get_param('dad-attempts', '60')) # 60: ifupdown default value | |
1112 | if not attempts or not ips: | |
1113 | return | |
1114 | try: | |
1115 | for _attempt in range(0, attempts): | |
1116 | tentative = ip_addr_list('tentative') | |
1117 | if all(str(ip) not in tentative for ip in ips): | |
1118 | break | |
cccf76e4 | 1119 | self.logger.info("%s: dad-interval: sleeping for %s" % (ifaceobj.name, interval)) |
b99c724a AB |
1120 | time.sleep(interval) |
1121 | else: | |
1122 | timeout = ','.join(ip for ip in ips if str(ip) not in tentative) | |
1123 | self.logger.warning('address: %s: dad timeout "%s"', ifaceobj.name, timeout) | |
1124 | return | |
1125 | failure = ip_addr_list('dadfailed') | |
1126 | if failure: | |
1127 | self.logger.warning('address: %s: dad failure "%s"', ifaceobj.name, ','.join(failure)) | |
1128 | except subprocess.CalledProcessError as exc: | |
1129 | self.logger.error('address: %s: could not settle dad %s', ifaceobj.name, str(exc)) | |
1130 | ||
d594fb86 AB |
1131 | def _get_ifaceobjs(self, ifaceobj, ifaceobj_getfunc): |
1132 | squash_addr_config = ifupdownconfig.config.get("addr_config_squash", "0") == "1" | |
1133 | if not squash_addr_config: | |
1134 | return [ifaceobj] # no squash, returns current ifaceobj | |
1135 | if not ifaceobj.flags & ifaceobj.YOUNGEST_SIBLING: | |
1136 | return [] # when squash is present, work only on the youngest sibling | |
1137 | if ifaceobj.flags & iface.HAS_SIBLINGS: | |
1138 | return ifaceobj_getfunc(ifaceobj.name) # get sibling interfaces | |
1139 | return [ifaceobj] | |
1140 | ||
223ba5af | 1141 | def _up(self, ifaceobj, ifaceobj_getfunc=None): |
d1477c4b JF |
1142 | gateways = ifaceobj.get_attr_value('gateway') |
1143 | if not gateways: | |
1144 | gateways = [] | |
1145 | prev_gw = self._get_prev_gateway(ifaceobj, gateways) | |
1146 | self._add_delete_gateway(ifaceobj, gateways, prev_gw) | |
d594fb86 AB |
1147 | # settle dad |
1148 | if not self.ipv6_dad_handling_enabled: | |
1149 | return | |
21a7bd2d AB |
1150 | if not self.cache.link_exists(ifaceobj.name): |
1151 | return | |
d594fb86 AB |
1152 | ifname = ifaceobj.name |
1153 | ifaceobjs = self._get_ifaceobjs(ifaceobj, ifaceobj_getfunc) | |
1154 | addr_supported, user_addrs_list = self.__get_ip_addr_with_attributes(ifaceobjs, ifname) | |
1155 | if not addr_supported: | |
1156 | return | |
1157 | self._settle_dad(ifaceobj, [ip for ip, _ in user_addrs_list if ip.version == 6]) | |
d1477c4b | 1158 | |
223ba5af JF |
1159 | def process_hwaddress(self, ifaceobj): |
1160 | hwaddress = self._get_hwaddress(ifaceobj) | |
d1477c4b | 1161 | |
223ba5af JF |
1162 | if not hwaddress: |
1163 | if ifaceobj.link_kind & ifaceLinkKind.VLAN: | |
1164 | # When hwaddress is removed from vlan config | |
1165 | # we should go back to system or bridge mac | |
1166 | for lower in ifaceobj.lowerifaces: | |
1167 | if self.cache.get_link_kind(lower) == "bridge": | |
1168 | hwaddress = self.cache.get_link_address(lower) | |
1169 | break | |
1170 | if not hwaddress: | |
4e0f16d0 | 1171 | return None, None |
223ba5af | 1172 | else: |
4e0f16d0 | 1173 | return None, None |
707aeb73 | 1174 | |
223ba5af JF |
1175 | if not ifupdownflags.flags.PERFMODE: # system is clean |
1176 | running_hwaddress = self.cache.get_link_address(ifaceobj.name) | |
1177 | else: | |
1178 | running_hwaddress = None | |
1179 | ||
1db0cb7a JF |
1180 | old_mac_addr = None |
1181 | ||
859b8643 JF |
1182 | hwaddress_int = utils.mac_str_to_int(hwaddress) |
1183 | ||
1184 | if hwaddress_int != utils.mac_str_to_int(running_hwaddress): | |
223ba5af JF |
1185 | slave_down = False |
1186 | if ifaceobj.link_kind & ifaceLinkKind.BOND: | |
1187 | # if bond, down all the slaves | |
1188 | if ifaceobj.lowerifaces: | |
1189 | for l in ifaceobj.lowerifaces: | |
1190 | self.netlink.link_down(l) | |
1191 | slave_down = True | |
1192 | try: | |
859b8643 | 1193 | self.netlink.link_set_address(ifaceobj.name, hwaddress, hwaddress_int) |
1db0cb7a | 1194 | old_mac_addr = running_hwaddress |
223ba5af JF |
1195 | finally: |
1196 | if slave_down: | |
1197 | for l in ifaceobj.lowerifaces: | |
1198 | self.netlink.link_up(l) | |
15ef32ea | 1199 | |
4e0f16d0 | 1200 | return hwaddress, old_mac_addr |
1db0cb7a | 1201 | |
0582f185 | 1202 | def _down(self, ifaceobj, ifaceobj_getfunc=None): |
15ef32ea | 1203 | try: |
223ba5af | 1204 | if not self.cache.link_exists(ifaceobj.name): |
15ef32ea | 1205 | return |
68d9fee0 | 1206 | addr_method = ifaceobj.addr_method |
77054f7f | 1207 | if addr_method not in ["dhcp", "ppp"]: |
aa052170 N |
1208 | if ifaceobj.get_attr_value_first('address-purge')=='no': |
1209 | addrlist = ifaceobj.get_attr_value('address') | |
223ba5af JF |
1210 | for addr in addrlist or []: |
1211 | self.netlink.addr_del(ifaceobj.name, addr) | |
d486dd0d JF |
1212 | elif not ifaceobj.link_kind: |
1213 | # for logical interfaces we don't need to remove the ip addresses | |
1214 | # kernel will do it for us on 'ip link del' | |
223ba5af JF |
1215 | if ifaceobj_getfunc: |
1216 | ifaceobj_list = ifaceobj_getfunc(ifaceobj.name) or [ifaceobj] | |
1217 | else: | |
1218 | ifaceobj_list = [ifaceobj] | |
1219 | ||
0e936c3f | 1220 | for addr in self.cache.get_managed_ip_addresses(ifaceobj.name, ifaceobj_list): |
223ba5af JF |
1221 | self.netlink.addr_del(ifaceobj.name, addr) |
1222 | ||
d486dd0d JF |
1223 | gateways = ifaceobj.get_attr_value('gateway') |
1224 | if gateways: | |
1225 | self._delete_gateway(ifaceobj, gateways, | |
1226 | ifaceobj.get_attr_value_first('vrf'), | |
1227 | ifaceobj.get_attr_value_first('metric')) | |
223ba5af JF |
1228 | |
1229 | # | |
1230 | # mtu -- | |
1231 | # If device is not a logical intf and has its MTU configured by | |
1232 | # ifupdown2. If this MTU is different from our default mtu, | |
1233 | # if so we need to reset it back to default. | |
1234 | if not ifaceobj.link_kind and self.default_mtu and ifaceobj.get_attr_value_first('mtu') and self.cache.get_link_mtu(ifaceobj.name) != self.default_mtu_int: | |
1235 | self.sysfs.link_set_mtu(ifaceobj.name, mtu_str=self.default_mtu, mtu_int=self.default_mtu_int) | |
1236 | ||
1237 | # | |
1238 | # alias | |
1239 | # only reset alias on non-logical device | |
1240 | if not ifaceobj.link_kind: | |
1241 | alias = ifaceobj.get_attr_value_first("alias") | |
1242 | if alias: | |
1243 | self.sysfs.link_set_alias(ifaceobj.name, None) # None to reset alias. | |
1244 | ||
75afe2a7 RP |
1245 | # XXX hwaddress reset cannot happen because we dont know last |
1246 | # address. | |
1247 | ||
1248 | # Handle special things on a bridge | |
4e0f16d0 JF |
1249 | hwaddress = self._get_hwaddress(ifaceobj) |
1250 | if not hwaddress: | |
1251 | hwaddress = self.cache.get_link_address(ifaceobj.name) | |
1252 | self._process_bridge(ifaceobj, False, hwaddress, None) | |
3b01ed76 | 1253 | except Exception as e: |
bcf11b14 RP |
1254 | self.logger.debug('%s : %s' %(ifaceobj.name, str(e))) |
1255 | pass | |
15ef32ea | 1256 | |
8e113d63 RP |
1257 | def _get_bridge_fdbs(self, bridgename, vlan): |
1258 | fdbs = self._bridge_fdb_query_cache.get(bridgename) | |
1259 | if not fdbs: | |
223ba5af | 1260 | fdbs = self.iproute2.bridge_fdb_show_dev(bridgename) |
8e113d63 RP |
1261 | if not fdbs: |
1262 | return | |
1263 | self._bridge_fdb_query_cache[bridgename] = fdbs | |
1264 | return fdbs.get(vlan) | |
1265 | ||
1266 | def _check_addresses_in_bridge(self, ifaceobj, hwaddress): | |
1267 | """ If the device is a bridge, make sure the addresses | |
1268 | are in the bridge """ | |
d486dd0d JF |
1269 | if ifaceobj.link_kind & ifaceLinkKind.VLAN: |
1270 | bridgename = ifaceobj.lowerifaces[0] | |
1271 | vlan = self._get_vlan_id(ifaceobj) | |
223ba5af JF |
1272 | if self.cache.bridge_is_vlan_aware(bridgename): |
1273 | fdb_addrs = [utils.mac_str_to_int(fdb_addr) for fdb_addr in self._get_bridge_fdbs(bridgename, str(vlan))] | |
d1477c4b | 1274 | if not fdb_addrs: |
223ba5af JF |
1275 | return False |
1276 | hwaddress_int = utils.mac_str_to_int(hwaddress) | |
d1477c4b JF |
1277 | if hwaddress_int not in fdb_addrs: |
1278 | return False | |
8e113d63 RP |
1279 | return True |
1280 | ||
d486dd0d JF |
1281 | def _query_sysctl(self, ifaceobj, ifaceobjcurr): |
1282 | bridge_port = ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT | |
1283 | ipforward = ifaceobj.get_attr_value_first('ip-forward') | |
1284 | if ipforward: | |
1285 | if bridge_port: | |
1286 | ifaceobjcurr.status = ifaceStatus.ERROR | |
1287 | ifaceobjcurr.status_str = ('\'ip-forward\' not supported ' + | |
1288 | 'for bridge port') | |
1289 | ifaceobjcurr.update_config_with_status('ip-forward', 1, None) | |
1290 | else: | |
223ba5af | 1291 | running_ipforward = self.cache.get_netconf_forwarding(socket.AF_INET, ifaceobj.name) |
307e814c JF |
1292 | config_ipforward = utils.get_boolean_from_string(ipforward) |
1293 | ifaceobjcurr.update_config_with_status( | |
1294 | 'ip-forward', | |
1295 | 'on' if running_ipforward else 'off', | |
1296 | running_ipforward != config_ipforward | |
1297 | ) | |
d486dd0d JF |
1298 | |
1299 | ip6forward = ifaceobj.get_attr_value_first('ip6-forward') | |
1300 | if ip6forward: | |
1301 | if bridge_port: | |
1302 | ifaceobjcurr.status = ifaceStatus.ERROR | |
1303 | ifaceobjcurr.status_str = ('\'ip6-forward\' not supported ' + | |
1304 | 'for bridge port') | |
1305 | ifaceobjcurr.update_config_with_status('ip6-forward', 1, None) | |
1306 | else: | |
223ba5af | 1307 | running_ip6forward = self.cache.get_netconf_forwarding(socket.AF_INET6, ifaceobj.name) |
307e814c JF |
1308 | config_ip6forward = utils.get_boolean_from_string(ip6forward) |
1309 | ifaceobjcurr.update_config_with_status( | |
1310 | 'ip6-forward', | |
1311 | 'on' if running_ip6forward else 'off', | |
1312 | running_ip6forward != config_ip6forward | |
1313 | ) | |
d486dd0d JF |
1314 | mpls_enable = ifaceobj.get_attr_value_first('mpls-enable'); |
1315 | if mpls_enable: | |
223ba5af | 1316 | running_mpls_enable = utils.get_yesno_from_onezero(str(self.cache.get_netconf_mpls_input(ifaceobj.name))) |
d486dd0d JF |
1317 | ifaceobjcurr.update_config_with_status('mpls-enable', |
1318 | running_mpls_enable, | |
1319 | mpls_enable != running_mpls_enable) | |
1320 | return | |
1321 | ||
007cae35 JF |
1322 | def query_check_ipv6_addrgen(self, ifaceobj, ifaceobjcurr): |
1323 | ipv6_addrgen = ifaceobj.get_attr_value_first('ipv6-addrgen') | |
1324 | ||
1325 | if not ipv6_addrgen: | |
1326 | return | |
1327 | ||
1328 | if ipv6_addrgen in utils._string_values: | |
1329 | ifaceobjcurr.update_config_with_status( | |
1330 | 'ipv6-addrgen', | |
1331 | ipv6_addrgen, | |
223ba5af | 1332 | utils.get_boolean_from_string(ipv6_addrgen) == self.cache.get_link_ipv6_addrgen_mode(ifaceobj.name) |
007cae35 JF |
1333 | ) |
1334 | else: | |
1335 | ifaceobjcurr.update_config_with_status('ipv6-addrgen', ipv6_addrgen, 1) | |
1336 | ||
0582f185 | 1337 | def _query_check(self, ifaceobj, ifaceobjcurr, ifaceobj_getfunc=None): |
223ba5af JF |
1338 | """ |
1339 | TODO: Check broadcast address, scope, etc | |
1340 | """ | |
15ef32ea | 1341 | runningaddrsdict = None |
223ba5af | 1342 | if not self.cache.link_exists(ifaceobj.name): |
15ef32ea RP |
1343 | self.logger.debug('iface %s not found' %ifaceobj.name) |
1344 | return | |
007cae35 JF |
1345 | |
1346 | self.query_check_ipv6_addrgen(ifaceobj, ifaceobjcurr) | |
1347 | ||
16d854b4 | 1348 | addr_method = ifaceobj.addr_method |
15ef32ea | 1349 | self.query_n_update_ifaceobjcurr_attr(ifaceobj, ifaceobjcurr, |
223ba5af | 1350 | 'mtu', self.cache.get_link_mtu_str) |
428206bf | 1351 | hwaddress = self._get_hwaddress(ifaceobj) |
8e113d63 | 1352 | if hwaddress: |
223ba5af JF |
1353 | rhwaddress = self.cache.get_link_address(ifaceobj.name) |
1354 | if not rhwaddress or utils.mac_str_to_int(rhwaddress) != utils.mac_str_to_int(hwaddress): | |
8e113d63 RP |
1355 | ifaceobjcurr.update_config_with_status('hwaddress', rhwaddress, |
1356 | 1) | |
1357 | elif not self._check_addresses_in_bridge(ifaceobj, hwaddress): | |
1358 | # XXX: hw address is not in bridge | |
1359 | ifaceobjcurr.update_config_with_status('hwaddress', rhwaddress, | |
1360 | 1) | |
1361 | ifaceobjcurr.status_str = 'bridge fdb error' | |
1362 | else: | |
1363 | ifaceobjcurr.update_config_with_status('hwaddress', rhwaddress, | |
1364 | 0) | |
15ef32ea | 1365 | self.query_n_update_ifaceobjcurr_attr(ifaceobj, ifaceobjcurr, |
223ba5af | 1366 | 'alias', self.cache.get_link_alias) |
0e936c3f | 1367 | |
d486dd0d | 1368 | self._query_sysctl(ifaceobj, ifaceobjcurr) |
9a6a3050 | 1369 | |
0e936c3f JF |
1370 | self._query_check_address(ifaceobj, ifaceobjcurr, ifaceobj_getfunc) |
1371 | ||
1372 | def _query_check_address(self, ifaceobj, ifaceobjcurr, ifaceobj_getfunc): | |
1373 | """ ifquery-check: attribute: "address" """ | |
1374 | if ifaceobj.addr_method in ["dhcp", "ppp"]: | |
1375 | return | |
223ba5af JF |
1376 | |
1377 | if ifaceobj_getfunc: | |
1378 | ifaceobj_list = ifaceobj_getfunc(ifaceobj.name) | |
1379 | else: | |
1380 | ifaceobj_list = [ifaceobj] | |
1381 | ||
0e936c3f JF |
1382 | intf_running_addrs = self.cache.get_managed_ip_addresses(ifaceobj.name, ifaceobj_list) |
1383 | user_config_addrs = self.cache.get_user_configured_addresses([ifaceobj]) | |
223ba5af JF |
1384 | |
1385 | try: | |
0e936c3f | 1386 | clagd_vxlan_anycast_ip = ipnetwork.IPNetwork(ifaceobj.get_attr_value_first("clagd-vxlan-anycast-ip")) |
223ba5af JF |
1387 | |
1388 | if clagd_vxlan_anycast_ip in intf_running_addrs: | |
1389 | user_config_addrs.append(clagd_vxlan_anycast_ip) | |
3218f49d | 1390 | except Exception: |
223ba5af | 1391 | pass |
15ef32ea RP |
1392 | |
1393 | # Set ifaceobjcurr method and family | |
1394 | ifaceobjcurr.addr_method = ifaceobj.addr_method | |
1395 | ifaceobjcurr.addr_family = ifaceobj.addr_family | |
223ba5af JF |
1396 | |
1397 | if not intf_running_addrs and not user_config_addrs: | |
1398 | # The device doesn't have any ips configured and the | |
1399 | # the user didn't specify any ip in the configuration file | |
15ef32ea | 1400 | return |
223ba5af JF |
1401 | |
1402 | for address in user_config_addrs: | |
1403 | ifaceobjcurr.update_config_with_status('address', str(address), address not in intf_running_addrs) | |
1404 | try: | |
1405 | intf_running_addrs.remove(address) | |
3218f49d | 1406 | except Exception: |
223ba5af JF |
1407 | pass |
1408 | ||
0e936c3f JF |
1409 | # if any ip address is left in 'intf_running_addrs' it means that they |
1410 | # used to be configured by ifupdown2 but not anymore. The entry was | |
1411 | # removed from the configuration file but the IP is still configured on | |
1412 | # the device, so we need to mark them as FAIL (we will only mark them | |
1413 | # as failure on the first sibling). | |
223ba5af JF |
1414 | if ifaceobj.flags & iface.HAS_SIBLINGS: |
1415 | if not ifaceobj.flags & iface.YOUNGEST_SIBLING: | |
1416 | return | |
1417 | ||
0e936c3f | 1418 | all_stanza_user_config_ip = self.cache.get_user_configured_addresses(ifaceobj_list) |
223ba5af JF |
1419 | |
1420 | for address in intf_running_addrs: | |
1421 | if address not in all_stanza_user_config_ip: | |
1422 | ifaceobjcurr.update_config_with_status('address', str(address), 1) | |
1423 | ||
007cae35 | 1424 | def query_running_ipv6_addrgen(self, ifaceobjrunning): |
223ba5af | 1425 | ipv6_addrgen = self.cache.get_link_ipv6_addrgen_mode(ifaceobjrunning.name) |
007cae35 JF |
1426 | |
1427 | if ipv6_addrgen: | |
1428 | ifaceobjrunning.update_config('ipv6-addrgen', 'off') | |
1429 | ||
0582f185 | 1430 | def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None): |
223ba5af | 1431 | if not self.cache.link_exists(ifaceobjrunning.name): |
15ef32ea | 1432 | self.logger.debug('iface %s not found' %ifaceobjrunning.name) |
15ef32ea | 1433 | return |
007cae35 JF |
1434 | |
1435 | self.query_running_ipv6_addrgen(ifaceobjrunning) | |
1436 | ||
15ef32ea RP |
1437 | dhclientcmd = dhclient() |
1438 | if (dhclientcmd.is_running(ifaceobjrunning.name) or | |
1439 | dhclientcmd.is_running6(ifaceobjrunning.name)): | |
1440 | # If dhcp is configured on the interface, we skip it | |
84f33af6 | 1441 | return |
223ba5af | 1442 | |
0e936c3f | 1443 | intf_running_addrs = self.cache.get_ip_addresses(ifaceobjrunning.name) or [] |
223ba5af JF |
1444 | |
1445 | if self.cache.link_is_loopback(ifaceobjrunning.name): | |
1446 | for default_addr in self.default_loopback_addresses: | |
1447 | try: | |
1448 | intf_running_addrs.remove(default_addr) | |
3218f49d | 1449 | except Exception: |
223ba5af | 1450 | pass |
004d1e65 | 1451 | ifaceobjrunning.addr_family.append('inet') |
15ef32ea | 1452 | ifaceobjrunning.addr_method = 'loopback' |
223ba5af JF |
1453 | |
1454 | for addr in intf_running_addrs: | |
580a567b | 1455 | ifaceobjrunning.update_config('address', str(addr)) |
223ba5af JF |
1456 | |
1457 | mtu = self.cache.get_link_mtu_str(ifaceobjrunning.name) | |
15ef32ea RP |
1458 | if (mtu and |
1459 | (ifaceobjrunning.name == 'lo' and mtu != '16436') or | |
1460 | (ifaceobjrunning.name != 'lo' and | |
1461 | mtu != self.get_mod_subattr('mtu', 'default'))): | |
1462 | ifaceobjrunning.update_config('mtu', mtu) | |
223ba5af JF |
1463 | |
1464 | alias = self.cache.get_link_alias(ifaceobjrunning.name) | |
84f33af6 | 1465 | if alias: |
15ef32ea RP |
1466 | ifaceobjrunning.update_config('alias', alias) |
1467 | ||
63155c61 JF |
1468 | ifaceobjrunning.update_config("hwaddress", self.cache.get_link_address(ifaceobjrunning.name)) |
1469 | ||
223ba5af JF |
1470 | _run_ops = { |
1471 | 'pre-up': _pre_up, | |
1472 | 'up': _up, | |
1473 | 'down': _down, | |
1474 | 'query-checkcurr': _query_check, | |
1475 | 'query-running': _query_running | |
1476 | } | |
15ef32ea RP |
1477 | |
1478 | def get_ops(self): | |
1479 | """ returns list of ops supported by this module """ | |
3b01ed76 | 1480 | return list(self._run_ops.keys()) |
15ef32ea | 1481 | |
6e16e5ae | 1482 | def run(self, ifaceobj, operation, query_ifaceobj=None, ifaceobj_getfunc=None): |
15ef32ea RP |
1483 | """ run address configuration on the interface object passed as argument |
1484 | ||
1485 | Args: | |
1486 | **ifaceobj** (object): iface object | |
1487 | ||
1488 | **operation** (str): any of 'up', 'down', 'query-checkcurr', | |
1489 | 'query-running' | |
1490 | Kwargs: | |
1491 | query_ifaceobj (object): query check ifaceobject. This is only | |
1492 | valid when op is 'query-checkcurr'. It is an object same as | |
1493 | ifaceobj, but contains running attribute values and its config | |
1494 | status. The modules can use it to return queried running state | |
1495 | of interfaces. status is success if the running state is same | |
1496 | as user required state in ifaceobj. error otherwise. | |
1497 | """ | |
8e113d63 RP |
1498 | if ifaceobj.type == ifaceType.BRIDGE_VLAN: |
1499 | return | |
15ef32ea RP |
1500 | op_handler = self._run_ops.get(operation) |
1501 | if not op_handler: | |
1502 | return | |
15ef32ea | 1503 | if operation == 'query-checkcurr': |
0582f185 RP |
1504 | op_handler(self, ifaceobj, query_ifaceobj, |
1505 | ifaceobj_getfunc=ifaceobj_getfunc) | |
15ef32ea | 1506 | else: |
0582f185 RP |
1507 | op_handler(self, ifaceobj, |
1508 | ifaceobj_getfunc=ifaceobj_getfunc) |