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