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