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