]> git.proxmox.com Git - mirror_ifupdown2.git/blob - ifupdown2/addons/addressvirtual.py
python3: logging: the 'warn' method is deprecated, use warning instead
[mirror_ifupdown2.git] / ifupdown2 / addons / addressvirtual.py
1 #!/usr/bin/env python3
2 #
3 # Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
4 # Author: Roopa Prabhu, roopa@cumulusnetworks.com
5 #
6
7 import os
8 import glob
9 import subprocess
10
11 from collections import deque
12 from ipaddr import IPNetwork, IPv6Network
13
14 try:
15 from ifupdown2.lib.addon import Addon
16 from ifupdown2.ifupdown.iface import *
17 from ifupdown2.ifupdown.utils import utils
18
19 from ifupdown2.nlmanager.nlpacket import Link
20
21 from ifupdown2.ifupdownaddons.modulebase import moduleBase
22
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
27 except ImportError:
28 from lib.addon import Addon
29 from ifupdown.iface import *
30 from ifupdown.utils import utils
31
32 from nlmanager.nlpacket import Link
33
34 from ifupdownaddons.modulebase import moduleBase
35
36 import ifupdown.statemanager as statemanager
37 import ifupdown.policymanager as policymanager
38 import ifupdown.ifupdownflags as ifupdownflags
39 import ifupdown.ifupdownconfig as ifupdownconfig
40
41
42 class addressvirtual(Addon, moduleBase):
43 """ ifupdown2 addon module to configure virtual addresses """
44
45 _modinfo = {
46 "mhelp": "address module configures virtual addresses for interfaces. "
47 "It creates a macvlan interface for every mac ip address-virtual line",
48 "attrs": {
49 "address-virtual": {
50 "help": "bridge router virtual mac and ips",
51 "multivalue": True,
52 "validvals": ["<mac-ip/prefixlen-list>", ],
53 "example": ["address-virtual 00:11:22:33:44:01 11.0.1.1/24 11.0.1.2/24"]
54 },
55 "address-virtual-ipv6-addrgen": {
56 "help": "enable disable ipv6 link addrgenmode",
57 "validvals": ["on", "off"],
58 "default": "on",
59 "example": [
60 "address-virtual-ipv6-addrgen on",
61 "address-virtual-ipv6-addrgen off"
62 ]
63 },
64 "vrrp": {
65 "help": "VRRP support",
66 "multivalue": True,
67 "example": [
68 "vrrp 1 10.0.0.15/24 2001:0db8::0370:7334/64",
69 "vrrp 42 10.0.0.42/24"
70 ]
71 }
72 }
73 }
74
75 DEFAULT_IP_METRIC = 1024
76 ADDR_METRIC_SUPPORT = None
77
78 def __init__(self, *args, **kargs):
79 Addon.__init__(self)
80 moduleBase.__init__(self, *args, **kargs)
81 self._bridge_fdb_query_cache = {}
82 self.addressvirtual_with_route_metric = utils.get_boolean_from_string(
83 policymanager.policymanager_api.get_module_globals(
84 module_name=self.__class__.__name__,
85 attr='addressvirtual_with_route_metric'
86 ),
87 default=True
88 )
89
90 self.address_virtual_ipv6_addrgen_value_dict = {'on': 0, 'yes': 0, '0': 0, 'off': 1, 'no': 1, '1': 1}
91
92 if addressvirtual.ADDR_METRIC_SUPPORT is None:
93 try:
94 cmd = [utils.ip_cmd, 'addr', 'help']
95 self.logger.info('executing %s addr help' % utils.ip_cmd)
96
97 process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
98 stdout, stderr = process.communicate()
99 addressvirtual.ADDR_METRIC_SUPPORT = '[ metric METRIC ]' in stderr or ''
100 self.logger.info('address metric support: %s' % ('OK' if addressvirtual.ADDR_METRIC_SUPPORT else 'KO'))
101 except Exception:
102 addressvirtual.ADDR_METRIC_SUPPORT = False
103 self.logger.info('address metric support: KO')
104
105 @classmethod
106 def addr_metric_support(cls):
107 return cls.ADDR_METRIC_SUPPORT
108
109 @classmethod
110 def get_default_ip_metric(cls):
111 return cls.DEFAULT_IP_METRIC
112
113 def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None):
114 if ifaceobj.get_attr_value('address-virtual') or ifaceobj.get_attr_value("vrrp"):
115 ifaceobj.link_privflags |= ifaceLinkPrivFlags.ADDRESS_VIRTUAL_SLAVE
116
117 def _get_macvlan_prefix(self, ifaceobj):
118 return '%s-v' %ifaceobj.name[0:13].replace('.', '-')
119
120 def get_vrrp_prefix(self, ifname, family):
121 return "vrrp%s-%s-" % (family, self.cache.get_ifindex(ifname))
122
123 def _add_addresses_to_bridge(self, ifaceobj, hwaddress):
124 # XXX: batch the addresses
125 if ifaceobj.link_kind & ifaceLinkKind.VLAN:
126 bridgename = ifaceobj.lowerifaces[0]
127 vlan = self._get_vlan_id(ifaceobj)
128 if self.cache.bridge_is_vlan_aware(bridgename):
129 [self.iproute2.bridge_fdb_add(bridgename, addr,
130 vlan) for addr in hwaddress]
131 elif self.cache.link_is_bridge(ifaceobj.name):
132 [self.iproute2.bridge_fdb_add(ifaceobj.name, addr)
133 for addr in hwaddress]
134
135 def _remove_addresses_from_bridge(self, ifaceobj, hwaddress):
136 # XXX: batch the addresses
137 if ifaceobj.link_kind & ifaceLinkKind.VLAN:
138 bridgename = ifaceobj.lowerifaces[0]
139 vlan = self._get_vlan_id(ifaceobj)
140 if self.cache.bridge_is_vlan_aware(bridgename):
141 for addr in hwaddress:
142 try:
143 self.iproute2.bridge_fdb_del(bridgename, addr, vlan)
144 except Exception as e:
145 self.logger.debug("%s: %s" %(ifaceobj.name, str(e)))
146 pass
147 elif self.cache.link_is_bridge(ifaceobj.name):
148 for addr in hwaddress:
149 try:
150 self.iproute2.bridge_fdb_del(ifaceobj.name, addr)
151 except Exception as e:
152 self.logger.debug("%s: %s" %(ifaceobj.name, str(e)))
153 pass
154
155 def _get_bridge_fdbs(self, bridgename, vlan):
156 fdbs = self._bridge_fdb_query_cache.get(bridgename)
157 if not fdbs:
158 fdbs = self.iproute2.bridge_fdb_show_dev(bridgename)
159 if not fdbs:
160 return
161 self._bridge_fdb_query_cache[bridgename] = fdbs
162 return fdbs.get(vlan)
163
164 def _check_addresses_in_bridge(self, ifaceobj, hwaddress):
165 """ If the device is a bridge, make sure the addresses
166 are in the bridge """
167 if ifaceobj.link_kind & ifaceLinkKind.VLAN:
168 bridgename = ifaceobj.lowerifaces[0]
169 vlan = self._get_vlan_id(ifaceobj)
170 if self.cache.bridge_is_vlan_aware(bridgename):
171 fdb_addrs = self._get_bridge_fdbs(bridgename, str(vlan))
172 if not fdb_addrs:
173 return False
174 hwaddress_int = utils.mac_str_to_int(hwaddress)
175 for mac in fdb_addrs:
176 if utils.mac_str_to_int(mac) == hwaddress_int:
177 return True
178 return False
179 return True
180
181 def _fix_connected_route(self, ifaceobj, vifacename, addr):
182 #
183 # XXX: Hack to make sure the primary address
184 # is the first in the routing table.
185 #
186 # We use `ip route get` on the vrr network to see which
187 # device the kernel returns. if it is the mac vlan device,
188 # flap the macvlan device to adjust the routing table entry.
189 #
190 # flapping the macvlan device makes sure the macvlan
191 # connected route goes through delete + add, hence adjusting
192 # the order in the routing table.
193 #
194 try:
195 self.logger.info('%s: checking route entry ...' %ifaceobj.name)
196 ip = IPNetwork(addr)
197
198 # we don't support ip6 route fix yet
199 if type(ip) == IPv6Network:
200 return
201
202 route_prefix = '%s/%d' %(ip.network, ip.prefixlen)
203
204 if ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE:
205 vrf_master = self.cache.get_master(ifaceobj.name)
206 else:
207 vrf_master = None
208
209 dev = self.iproute2.ip_route_get_dev(route_prefix, vrf_master=vrf_master)
210
211 if dev and dev != ifaceobj.name:
212 self.logger.info('%s: preferred routing entry ' %ifaceobj.name +
213 'seems to be of the macvlan dev %s'
214 %vifacename +
215 ' .. flapping macvlan dev to fix entry.')
216 self.iproute2.link_down(vifacename)
217 self.iproute2.link_up(vifacename)
218 except Exception as e:
219 self.logger.debug('%s: fixing route entry failed (%s)'
220 % (ifaceobj.name, str(e)))
221 pass
222
223 def _get_macs_from_old_config(self, ifaceobj=None):
224 """ This method returns a list of the mac addresses
225 in the address-virtual attribute for the bridge. """
226 maclist = []
227 saved_ifaceobjs = statemanager.statemanager_api.get_ifaceobjs(ifaceobj.name)
228 if not saved_ifaceobjs:
229 return maclist
230 # we need the old saved configs from the statemanager
231 for oldifaceobj in saved_ifaceobjs:
232 if not oldifaceobj.get_attr_value('address-virtual'):
233 continue
234 for av in oldifaceobj.get_attr_value('address-virtual'):
235 macip = av.split()
236 if len(macip) < 2:
237 self.logger.debug("%s: incorrect old address-virtual attrs '%s'"
238 %(oldifaceobj.name, av))
239 continue
240 maclist.append(macip[0])
241 return maclist
242
243 def get_addressvirtual_ipv6_addrgen_user_conf(self, ifaceobj):
244 ipv6_addrgen = ifaceobj.get_attr_value_first('address-virtual-ipv6-addrgen')
245
246 if ipv6_addrgen:
247 # IFLA_INET6_ADDR_GEN_MODE values:
248 # 0 = eui64
249 # 1 = none
250 ipv6_addrgen_nl = self.address_virtual_ipv6_addrgen_value_dict.get(ipv6_addrgen.lower(), None)
251
252 if ipv6_addrgen_nl is None:
253 self.logger.warning('%s: invalid value "%s" for attribute address-virtual-ipv6-addrgen' % (ifaceobj.name, ipv6_addrgen))
254 else:
255 return True, ipv6_addrgen_nl
256
257 else:
258 # if user didn't configure ipv6-addrgen, should we reset to default?
259 ipv6_addrgen_nl = self.address_virtual_ipv6_addrgen_value_dict.get(
260 self.get_attr_default_value('address-virtual-ipv6-addrgen'),
261 None
262 )
263 if ipv6_addrgen_nl is not None:
264 return True, ipv6_addrgen_nl
265
266 return False, None
267
268 def _remove_running_address_config(self, ifaceobj):
269 if not self.cache.link_exists(ifaceobj.name):
270 return
271 hwaddress = []
272
273 for macvlan_prefix in [
274 self._get_macvlan_prefix(ifaceobj),
275 self.get_vrrp_prefix(ifaceobj.name, "4"),
276 self.get_vrrp_prefix(ifaceobj.name, "6")
277 ]:
278 for macvlan_ifacename in glob.glob("/sys/class/net/%s*" % macvlan_prefix):
279 macvlan_ifacename = os.path.basename(macvlan_ifacename)
280 if not self.cache.link_exists(macvlan_ifacename):
281 continue
282 hwaddress.append(self.cache.get_link_address(macvlan_ifacename))
283 self.netlink.link_del(os.path.basename(macvlan_ifacename))
284 # XXX: Also delete any fdb addresses. This requires, checking mac address
285 # on individual macvlan interfaces and deleting the vlan from that.
286
287 if any(hwaddress):
288 self._remove_addresses_from_bridge(ifaceobj, hwaddress)
289
290 def _remove_address_config(self, ifaceobj, address_virtual_list=None):
291 if not address_virtual_list:
292 self._remove_running_address_config(ifaceobj)
293 return
294
295 if not self.cache.link_exists(ifaceobj.name):
296 return
297 hwaddress = []
298 av_idx = 0
299 macvlan_prefix = self._get_macvlan_prefix(ifaceobj)
300 for av in address_virtual_list:
301 av_attrs = av.split()
302
303 # Delete the macvlan device on this device
304 macvlan_ifacename = '%s%d' %(macvlan_prefix, av_idx)
305 self.netlink.link_del(os.path.basename(macvlan_ifacename))
306 if av_attrs[0] != 'None':
307 hwaddress.append(av_attrs[0])
308 av_idx += 1
309 self._remove_addresses_from_bridge(ifaceobj, hwaddress)
310
311 def check_mac_address(self, ifaceobj, mac):
312 if mac == 'none':
313 return True
314 try:
315 if int(mac.split(":")[0], 16) & 1 :
316 self.log_error("%s: Multicast bit is set in the virtual mac address '%s'"
317 % (ifaceobj.name, mac), ifaceobj=ifaceobj)
318 return False
319 return True
320 except ValueError:
321 return False
322
323 def _fixup_vrf_enslavements(self, ifaceobj, ifaceobj_getfunc=None):
324 """ This function fixes up address virtual interfaces
325 (macvlans) on vrf slaves. Since this fixup is an overhead,
326 this must be called only in cases when ifupdown2 is
327 called on the vrf device or its slave and not when
328 ifupdown2 is called for all devices. When all
329 interfaces are brought up, the expectation is that
330 the normal path will fix up a vrf device or its slaves"""
331
332 if not ifaceobj_getfunc:
333 return
334 if ((ifaceobj.link_kind & ifaceLinkKind.VRF) and
335 self.cache.link_exists(ifaceobj.name)):
336 # if I am a vrf device and I have slaves
337 # that have address virtual config,
338 # enslave the slaves 'address virtual
339 # interfaces (macvlans)' to myself:
340 running_slaves = self.sysfs.link_get_lowers(ifaceobj.name)
341 if running_slaves:
342 # pick up any existing slaves of a vrf device and
343 # look for their upperdevices and enslave them to the
344 # vrf device:
345 for s in running_slaves:
346 sobjs = ifaceobj_getfunc(s)
347 if (sobjs and
348 (sobjs[0].link_privflags & ifaceLinkPrivFlags.ADDRESS_VIRTUAL_SLAVE)):
349 # enslave all its upper devices to
350 # the vrf device
351 upperdevs = self.sysfs.link_get_uppers(sobjs[0].name)
352 if not upperdevs:
353 continue
354 for u in upperdevs:
355 # skip vrf device which
356 # will also show up in the
357 # upper device list
358 if u == ifaceobj.name:
359 continue
360 self.netlink.link_set_master(u, ifaceobj.name)
361 self.netlink.link_up(u)
362 elif ((ifaceobj.link_privflags & ifaceLinkPrivFlags.ADDRESS_VIRTUAL_SLAVE) and
363 (ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE) and
364 self.cache.link_exists(ifaceobj.name)):
365 # If I am a vrf slave and I have 'address virtual'
366 # config, make sure my addrress virtual interfaces
367 # (macvlans) are also enslaved to the vrf device
368 vrfname = ifaceobj.get_attr_value_first('vrf')
369 if not vrfname or not self.cache.link_exists(vrfname):
370 return
371 running_uppers = self.sysfs.link_get_uppers(ifaceobj.name)
372 if not running_uppers:
373 return
374 macvlan_prefix = self._get_macvlan_prefix(ifaceobj)
375 if not macvlan_prefix:
376 return
377 for u in running_uppers:
378 if u == vrfname:
379 continue
380 if u.startswith(macvlan_prefix):
381 self.netlink.link_set_master(u, vrfname)
382 self.netlink.link_up(u)
383
384 def create_macvlan_and_apply_config(self, ifaceobj, intf_config_list, vrrp=False):
385
386 """
387 intf_config_list = [
388 {
389 "ifname": "macvlan_ifname",
390 "hwaddress": "macvlan_hwaddress",
391 "ips": [str(IPNetwork), ]
392 },
393 ]
394 """
395 hw_address_list = []
396
397 if not intf_config_list:
398 return hw_address_list
399
400 user_configured_ipv6_addrgenmode, ipv6_addrgen_user_value = self.get_addressvirtual_ipv6_addrgen_user_conf(ifaceobj)
401 purge_existing = False if ifupdownflags.flags.PERFMODE else True
402 ifname = ifaceobj.name
403
404 update_mtu = lower_iface_mtu = lower_iface_mtu_str = None
405 if ifupdownconfig.config.get("adjust_logical_dev_mtu", "1") != "0":
406 if ifaceobj.lowerifaces and intf_config_list:
407 update_mtu = True
408
409 if update_mtu:
410 lower_iface_mtu = self.cache.get_link_mtu(ifaceobj.name)
411 lower_iface_mtu_str = str(lower_iface_mtu)
412
413 self.iproute2.batch_start() # TODO: make sure we only do 1 ip link set down and set up (only one flap in the batch)
414
415 for intf_config_dict in intf_config_list:
416 link_created = False
417 macvlan_ifname = intf_config_dict.get("ifname")
418 macvlan_hwaddr = intf_config_dict.get("hwaddress")
419 macvlan_mode = intf_config_dict.get("mode")
420 ips = intf_config_dict.get("ips")
421
422 if not self.cache.link_exists(macvlan_ifname):
423 # When creating VRRP macvlan with bridge mode, the kernel
424 # return an error: 'Invalid argument' (22)
425 # so for now we should only use the iproute2 API.
426 # try:
427 # self.netlink.link_add_macvlan(ifname, macvlan_ifname)
428 # except:
429 self.iproute2.link_add_macvlan(ifname, macvlan_ifname, macvlan_mode)
430 link_created = True
431
432 # first thing we need to handle vrf enslavement
433 if ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE:
434 vrf_ifname = self.cache.get_master(ifaceobj.name)
435 if vrf_ifname:
436 self.iproute2.link_set_master(macvlan_ifname, vrf_ifname)
437
438 # if we are dealing with a VRRP macvlan we need to set addrgenmode
439 # to RANDOM, and protodown on
440 if vrrp:
441 try:
442 self.iproute2.link_set_ipv6_addrgen(
443 macvlan_ifname,
444 Link.IN6_ADDR_GEN_MODE_RANDOM,
445 link_created
446 )
447 except Exception as e:
448 self.logger.warning("%s: %s: ip link set dev %s addrgenmode random: "
449 "operation not supported: %s" % (ifname, macvlan_ifname, macvlan_ifname, str(e)))
450 try:
451 if link_created:
452 self.netlink.link_set_protodown_on(macvlan_ifname)
453 except Exception as e:
454 self.logger.warning("%s: %s: ip link set dev %s protodown on: operation not supported: %s" % (ifname, macvlan_ifname, macvlan_ifname, str(e)))
455 elif user_configured_ipv6_addrgenmode:
456 self.iproute2.link_set_ipv6_addrgen(macvlan_ifname, ipv6_addrgen_user_value, link_created)
457
458 if macvlan_hwaddr:
459 self.iproute2.link_set_address_and_keep_down(
460 macvlan_ifname,
461 macvlan_hwaddr,
462 keep_down=ifaceobj.link_privflags & ifaceLinkPrivFlags.KEEP_LINK_DOWN
463 )
464 hw_address_list.append(macvlan_hwaddr)
465
466 if self.addressvirtual_with_route_metric and self.addr_metric_support():
467 metric = self.get_default_ip_metric()
468 else:
469 metric = None
470
471 self.iproute2.add_addresses(
472 ifaceobj,
473 macvlan_ifname,
474 ips,
475 purge_existing,
476 metric=metric
477 )
478
479 if ifaceobj.link_privflags & ifaceLinkPrivFlags.KEEP_LINK_DOWN:
480 self.logger.info("%s: keeping macvlan down - link-down yes on lower device %s" % (macvlan_ifname, ifname))
481 self.netlink.link_down(macvlan_ifname)
482
483 # If link existed before, flap the link
484 if not link_created:
485
486 if not self.addressvirtual_with_route_metric or not self.addr_metric_support():
487 # if the system doesn't support ip addr set METRIC
488 # we need to do manually check the ordering of the ip4 routes
489 self._fix_connected_route(ifaceobj, macvlan_ifname, ips[0])
490
491 if update_mtu:
492 update_mtu = False
493
494 try:
495 self.sysfs.link_set_mtu(macvlan_ifname, mtu_str=lower_iface_mtu_str, mtu_int=lower_iface_mtu)
496 except Exception as e:
497 self.logger.info('%s: failed to set mtu %s: %s' % (macvlan_ifname, lower_iface_mtu, e))
498
499 # set macvlan device to up in anycase.
500 # since we auto create them here..we are responsible
501 # to bring them up here in the case they were brought down
502 # by some other entity in the system.
503 if not ifaceobj.link_privflags & ifaceLinkPrivFlags.KEEP_LINK_DOWN:
504 self.netlink.link_up(macvlan_ifname)
505 else:
506 try:
507 if not self.addressvirtual_with_route_metric or not self.addr_metric_support():
508 # if the system doesn't support ip addr set METRIC
509 # we need to do manually check the ordering of the ip6 routes
510 self.iproute2.fix_ipv6_route_metric(ifaceobj, macvlan_ifname, ips)
511 except Exception as e:
512 self.logger.debug('fix_vrf_slave_ipv6_route_metric: failed: %s' % e)
513
514 # Disable IPv6 duplicate address detection on VRR interfaces
515 sysctl_prefix = "net.ipv6.conf.%s" % macvlan_ifname
516
517 try:
518 syskey = "%s.%s" % (sysctl_prefix, "enhanced_dad")
519 if self.sysctl_get(syskey) != "0":
520 self.sysctl_set(syskey, "0")
521 except Exception as e:
522 self.logger.info("sysctl failure: operation not supported: %s" % str(e))
523
524 for key, sysval in {
525 "accept_dad": "0",
526 "dad_transmits": "0"
527 }.items():
528 syskey = "%s.%s" % (sysctl_prefix, key)
529 if self.sysctl_get(syskey) != sysval:
530 self.sysctl_set(syskey, sysval)
531
532 self.iproute2.batch_commit()
533 return hw_address_list
534
535 def _up(self, ifaceobj, ifaceobj_getfunc=None):
536 if not ifupdownflags.flags.ALL:
537 self._fixup_vrf_enslavements(ifaceobj, ifaceobj_getfunc)
538
539 address_virtual_list = ifaceobj.get_attr_value('address-virtual')
540 vrr_config_list = ifaceobj.get_attr_value("vrrp")
541
542 if not address_virtual_list and not vrr_config_list:
543 # XXX: address virtual is not present. In which case,
544 # delete stale macvlan devices.
545 self._remove_running_address_config(ifaceobj)
546 return
547
548 if ifaceobj.upperifaces and not ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE:
549 self.log_error("%s: invalid placement of address-virtual/vrrp lines "
550 "(must be configured under an interface "
551 "with no upper interfaces or parent interfaces)"
552 % ifaceobj.name, ifaceobj)
553
554 if not self.cache.link_exists(ifaceobj.name):
555 return
556
557 addr_virtual_macs = self.create_macvlan_and_apply_config(
558 ifaceobj,
559 self.translate_addrvirtual_user_config_to_list(
560 ifaceobj,
561 address_virtual_list
562 )
563 )
564
565 vrr_macs = self.create_macvlan_and_apply_config(
566 ifaceobj,
567 self.translate_vrr_user_config_to_list(
568 ifaceobj,
569 vrr_config_list
570 ),
571 vrrp=True
572 )
573
574 hw_address_list = addr_virtual_macs + vrr_macs
575
576 # check the statemanager for old configs.
577 # We need to remove only the previously configured FDB entries
578 oldmacs = self._get_macs_from_old_config(ifaceobj)
579 # get a list of fdbs in old that are not in new config meaning they should
580 # be removed since they are gone from the config
581 removed_macs = [mac for mac in oldmacs if mac.lower() not in hw_address_list]
582 self._remove_addresses_from_bridge(ifaceobj, removed_macs)
583 # if ifaceobj is a bridge and bridge is a vlan aware bridge
584 # add the vid to the bridge
585 self._add_addresses_to_bridge(ifaceobj, hw_address_list)
586
587 def translate_vrr_user_config_to_list(self, ifaceobj, vrr_config_list, ifquery=False):
588 """
589 If (IPv4 addresses provided):
590 00:00:5e:00:01:<V>
591 else if (IPv6 addresses provided):
592 00:00:5e:00:02:<V>
593
594 vrrp 1 10.0.0.15/24
595 vrrp 1 2001:0db8::0370:7334/64
596
597 # Translate:
598 # vrrp 255 10.0.0.15/24 10.0.0.2/1
599 # To:
600 # [
601 # {
602 # "ifname": "macvlan_ifname",
603 # "hwaddress": "macvlan_hwaddress",
604 # "mode": "macvlan_mode",
605 # "ips": [str(IPNetwork), ]
606 # },
607 # ]
608 """
609 ifname = ifaceobj.name
610 user_config_list = []
611
612 for index, config in enumerate(vrr_config_list or []):
613 vrrp_id, ip_addrs = config.split(" ", 1)
614 hex_id = '%02x' % int(vrrp_id)
615 ip4 = []
616 ip6 = []
617
618 for ip_addr in ip_addrs.split():
619 ip_network_obj = IPNetwork(ip_addr)
620 is_ip6 = isinstance(ip_network_obj, IPv6Network)
621
622 if is_ip6:
623 ip6.append(ip_addr)
624 else:
625 ip4.append(ip_addr)
626
627 macvlan_ip4_ifname = "%s%s" % (self.get_vrrp_prefix(ifname, "4"), vrrp_id)
628 macvlan_ip6_ifname = "%s%s" % (self.get_vrrp_prefix(ifname, "6"), vrrp_id)
629
630 if ip4 or ifquery:
631 merged_with_existing_obj = False
632 macvlan_ip4_mac = "00:00:5e:00:01:%s" % hex_id
633 macvlan_ip4_mac_int = utils.mac_str_to_int(macvlan_ip4_mac)
634 # if the vrr config is defined in different lines for the same ID
635 # we need to save the ip4 and ip6 in the objects we previously
636 # created, example:
637 # vrrp 255 10.0.0.15/24 10.0.0.2/15
638 # vrrp 255 fe80::a00:27ff:fe04:42/64
639 for obj in user_config_list:
640 if obj.get("hwaddress_int") == macvlan_ip4_mac_int:
641 obj["ips"] += ip4
642 merged_with_existing_obj = True
643
644 if not merged_with_existing_obj:
645 # if ip4 config wasn't merge with an existing object
646 # we need to insert it in our list
647 user_config_list.append({
648 "ifname": macvlan_ip4_ifname,
649 "hwaddress": macvlan_ip4_mac,
650 "hwaddress_int": macvlan_ip4_mac_int,
651 "mode": "bridge",
652 "ips": ip4,
653 "id": vrrp_id
654 })
655 elif not ip4 and not ifquery:
656 # special check to see if all ipv4 were removed from the vrrp
657 # configuration, if so we need to remove the associated macvlan
658 if self.cache.link_exists(macvlan_ip4_ifname):
659 self.netlink.link_del(macvlan_ip4_ifname)
660
661 if ip6 or ifquery:
662 merged_with_existing_obj = False
663 macvlan_ip6_mac = "00:00:5e:00:02:%s" % hex_id
664 macvlan_ip6_mac_int = utils.mac_str_to_int(macvlan_ip6_mac)
665 # if the vrr config is defined in different lines for the same ID
666 # we need to save the ip4 and ip6 in the objects we previously
667 # created, example:
668 # vrrp 255 10.0.0.15/24 10.0.0.2/15
669 # vrrp 255 fe80::a00:27ff:fe04:42/64
670
671 for obj in user_config_list:
672 if obj.get("hwaddress_int") == macvlan_ip6_mac_int:
673 obj["ips"] += ip6
674 merged_with_existing_obj = True
675
676 if not merged_with_existing_obj:
677 # if ip6 config wasn't merge with an existing object
678 # we need to insert it in our list
679 user_config_list.append({
680 "ifname": macvlan_ip6_ifname,
681 "hwaddress": macvlan_ip6_mac,
682 "hwaddress_int": macvlan_ip6_mac_int,
683 "mode": "bridge",
684 "ips": ip6,
685 "id": vrrp_id
686 })
687 elif not ip6 and not ifquery:
688 # special check to see if all ipv6 were removed from the vrrp
689 # configuration, if so we need to remove the associated macvlan
690 if self.cache.link_exists(macvlan_ip6_ifname):
691 self.netlink.link_del(macvlan_ip6_ifname)
692
693 if not ifquery:
694 # check if vrrp attribute was removed/re-assigned
695 old_vrr_ids = set()
696
697 try:
698 for old_ifaceobj in statemanager.statemanager_api.get_ifaceobjs(ifname) or []:
699 for vrr_config in old_ifaceobj.get_attr_value("vrrp") or []:
700 try:
701 old_vrr_ids.add(vrr_config.split()[0])
702 except:
703 continue
704
705 if old_vrr_ids:
706
707 for config in user_config_list:
708 try:
709 old_vrr_ids.remove(config["id"])
710 except KeyError:
711 pass
712
713 for id_to_remove in old_vrr_ids:
714 macvlan_ip4_ifname = "%s%s" % (self.get_vrrp_prefix(ifname, "4"), id_to_remove)
715 macvlan_ip6_ifname = "%s%s" % (self.get_vrrp_prefix(ifname, "6"), id_to_remove)
716
717 if self.cache.link_exists(macvlan_ip4_ifname):
718 self.netlink.link_del(macvlan_ip4_ifname)
719
720 if self.cache.link_exists(macvlan_ip6_ifname):
721 self.netlink.link_del(macvlan_ip6_ifname)
722
723 except Exception as e:
724 self.logger.debug("%s: vrrp: failure while removing unused macvlan(s)" % ifname)
725
726 return user_config_list
727
728 def translate_addrvirtual_user_config_to_list(self, ifaceobj, address_virtual_list):
729 """
730 # Translate:
731 # address-virtual 00:11:22:33:44:01 2001:0db8::0370:7334/64 11.0.1.1/24 11.0.1.2/24
732 # To:
733 # [
734 # {
735 # "ifname": "macvlan_ifname",
736 # "hwaddress": "macvlan_hwaddress",
737 # "ips": [str(IPNetwork), ]
738 # },
739 # ]
740 """
741 user_config_list = []
742
743 if not address_virtual_list:
744 return user_config_list
745
746 macvlan_prefix = self._get_macvlan_prefix(ifaceobj)
747
748 for index, addr_virtual in enumerate(address_virtual_list):
749 av_attrs = addr_virtual.split()
750 mac = av_attrs[0]
751 if mac:
752 mac = mac.lower()
753
754 if not self.check_mac_address(ifaceobj, mac):
755 continue
756
757 config = {
758 "ifname": "%s%d" % (macvlan_prefix, index),
759 "mode": "private"
760 }
761
762 if mac != "none":
763 config["hwaddress"] = mac
764 config["hwaddress_int"] = utils.mac_str_to_int(mac)
765
766 ip_network_obj_list = []
767 for ip in av_attrs[1:]:
768 ip_network_obj_list.append(str(IPNetwork(ip)))
769
770 config["ips"] = ip_network_obj_list
771 user_config_list.append(config)
772
773 return user_config_list
774
775 def _down(self, ifaceobj, ifaceobj_getfunc=None):
776 try:
777 self._remove_address_config(ifaceobj,
778 ifaceobj.get_attr_value('address-virtual'))
779
780 #### VRR
781 hwaddress = []
782 for vrr_prefix in [self.get_vrrp_prefix(ifaceobj.name, "4"), self.get_vrrp_prefix(ifaceobj.name, "6")]:
783 for macvlan_ifacename in glob.glob("/sys/class/net/%s*" % vrr_prefix):
784 macvlan_ifacename = os.path.basename(macvlan_ifacename)
785 if not self.cache.link_exists(macvlan_ifacename):
786 continue
787 hwaddress.append(self.cache.get_link_address(macvlan_ifacename))
788 self.netlink.link_del(macvlan_ifacename)
789 # XXX: Also delete any fdb addresses. This requires, checking mac address
790 # on individual macvlan interfaces and deleting the vlan from that.
791 if any(hwaddress):
792 self._remove_addresses_from_bridge(ifaceobj, hwaddress)
793 except Exception as e:
794 import traceback
795 traceback.print_exc()
796 self.log_warn(str(e))
797
798 def _query_check(self, ifaceobj, ifaceobjcurr):
799
800 if not self.cache.link_exists(ifaceobj.name):
801 return
802
803 user_config_address_virtual_ipv6_addr = ifaceobj.get_attr_value_first('address-virtual-ipv6-addrgen')
804 if user_config_address_virtual_ipv6_addr and user_config_address_virtual_ipv6_addr not in utils._string_values:
805 ifaceobjcurr.update_config_with_status('address-virtual-ipv6-addrgen', user_config_address_virtual_ipv6_addr, 1)
806 user_config_address_virtual_ipv6_addr = None
807
808 address_virtual_list = ifaceobj.get_attr_value('address-virtual')
809
810 macvlans_running_ipv6_addr_virtual = self.query_check_macvlan_config(
811 ifaceobj,
812 ifaceobjcurr,
813 "address-virtual",
814 user_config_address_virtual_ipv6_addr,
815 virtual_addr_list_raw=address_virtual_list,
816 macvlan_config_list=self.translate_addrvirtual_user_config_to_list(
817 ifaceobj,
818 address_virtual_list
819 )
820 )
821
822 vrr_config_list = ifaceobj.get_attr_value("vrrp")
823
824 macvlans_running_ipv6_addr_vrr = self.query_check_macvlan_config(
825 ifaceobj,
826 ifaceobjcurr,
827 "vrrp",
828 user_config_address_virtual_ipv6_addr,
829 virtual_addr_list_raw=vrr_config_list,
830 macvlan_config_list=self.translate_vrr_user_config_to_list(
831 ifaceobj,
832 vrr_config_list,
833 ifquery=True
834 )
835 )
836
837 macvlans_running_ipv6_addr = macvlans_running_ipv6_addr_virtual + macvlans_running_ipv6_addr_vrr
838 if user_config_address_virtual_ipv6_addr:
839 bool_user_ipv6_addrgen = utils.get_boolean_from_string(user_config_address_virtual_ipv6_addr)
840 for running_ipv6_addrgen in macvlans_running_ipv6_addr:
841 if (not bool_user_ipv6_addrgen) != running_ipv6_addrgen:
842 ifaceobjcurr.update_config_with_status('address-virtual-ipv6-addrgen', user_config_address_virtual_ipv6_addr, 1)
843 return
844 ifaceobjcurr.update_config_with_status('address-virtual-ipv6-addrgen', user_config_address_virtual_ipv6_addr, 0)
845
846 @staticmethod
847 def compare_user_config_vs_running_state(running_addrs, user_addrs):
848 ip4 = []
849 ip6 = []
850
851 for ip in user_addrs or []:
852 obj = IPNetwork(ip)
853
854 if type(obj) == IPv6Network:
855 ip6.append(obj)
856 else:
857 ip4.append(obj)
858
859 running_ipobj = []
860 for ip in running_addrs or []:
861 running_ipobj.append(IPNetwork(ip))
862
863 return running_ipobj == (ip4 + ip6)
864
865 def query_check_macvlan_config(self, ifaceobj, ifaceobjcurr, attr_name, user_config_address_virtual_ipv6_addr, virtual_addr_list_raw, macvlan_config_list):
866 """
867 macvlan_config_list = [
868 {
869 "ifname": "macvlan_ifname",
870 "hwaddress": "macvlan_hwaddress",
871 "ips": [str(IPNetwork), ]
872 },
873 ]
874 """
875 is_vrr = attr_name == "vrrp"
876 macvlans_running_ipv6_addr = []
877
878 if not virtual_addr_list_raw:
879 return macvlans_running_ipv6_addr
880
881 macvlan_config_queue = deque(macvlan_config_list)
882
883 while macvlan_config_queue:
884
885 ip4_config = None
886 ip6_config = None
887
888 config = macvlan_config_queue.popleft()
889
890 if is_vrr:
891 ip4_config = config
892 ip6_config = macvlan_config_queue.popleft()
893
894 macvlan_ifacename = config.get("ifname")
895
896 if not self.cache.link_exists(macvlan_ifacename):
897 ifaceobjcurr.update_config_with_status(attr_name, "", 1)
898 continue
899
900 macvlan_hwaddress = config.get("hwaddress")
901 macvlan_hwaddress_int = config.get("hwaddress_int")
902
903 if user_config_address_virtual_ipv6_addr:
904 macvlans_running_ipv6_addr.append(self.cache.get_link_ipv6_addrgen_mode(macvlan_ifacename))
905
906 # Check mac and ip address
907 rhwaddress = ip4_macvlan_hwaddress = self.cache.get_link_address(macvlan_ifacename)
908 raddrs = ip4_running_addrs =[str(ip) for ip in self.cache.get_ifupdown2_addresses_list(
909 ifname=macvlan_ifacename,
910 ifaceobj_list=[ifaceobj],
911 with_address_virtual=True
912 )]
913
914 if not is_vrr:
915 ips = config.get("ips")
916
917 if not rhwaddress:
918 ifaceobjcurr.update_config_with_status(attr_name, "", 1)
919 continue
920
921 try:
922 if utils.mac_str_to_int(rhwaddress) == macvlan_hwaddress_int \
923 and self.compare_user_config_vs_running_state(raddrs, ips) \
924 and self._check_addresses_in_bridge(ifaceobj, macvlan_hwaddress):
925 ifaceobjcurr.update_config_with_status(
926 attr_name,
927 " ".join(virtual_addr_list_raw),
928 0
929 )
930 else:
931 ifaceobjcurr.update_config_with_status(
932 attr_name,
933 '%s %s' % (rhwaddress, ' '.join(raddrs)),
934 1
935 )
936 except:
937 ifaceobjcurr.update_config_with_status(
938 attr_name,
939 '%s %s' % (rhwaddress, ' '.join(raddrs)),
940 1
941 )
942 else:
943 # VRRP
944
945 ok = False
946 # check macvlan ip4 hwaddress (only if ip4 were provided by the user)
947 if not ip4_config.get("ips") or ip4_macvlan_hwaddress == ip4_config.get("hwaddress"):
948 ip6_macvlan_ifname = ip6_config.get("ifname")
949 ip6_macvlan_hwaddress = ip6_config.get("hwaddress")
950
951 # check macvlan ip6 hwaddress (only if ip6 were provided by the user)
952 if not ip6_config.get("ips") or self.cache.get_link_address_raw(ip6_macvlan_ifname) == ip6_config.get("hwaddress_int"):
953
954 # check all ip4
955 if self.compare_user_config_vs_running_state(
956 ip4_running_addrs,
957 ip4_config.get("ips")
958 ) and self._check_addresses_in_bridge(ifaceobj, ip4_macvlan_hwaddress):
959 ip6_running_addrs = [str(ip) for ip in self.cache.get_ifupdown2_addresses_list(
960 ifname=ip6_macvlan_ifname,
961 details=False,
962 ifaceobj_list=[ifaceobj],
963 with_address_virtual=True
964 )]
965
966 # check all ip6
967 if self.compare_user_config_vs_running_state(
968 ip6_running_addrs,
969 ip6_config.get("ips")
970 ) and self._check_addresses_in_bridge(ifaceobj, ip6_macvlan_hwaddress):
971 ifaceobjcurr.update_config_with_status(
972 attr_name,
973 "%s %s" % (ip4_config.get("id"), " ".join(ip4_config.get("ips") + ip6_config.get("ips"))),
974 0
975 )
976 ok = True
977
978 if not ok:
979 ifaceobjcurr.update_config_with_status(
980 attr_name,
981 "%s %s" % (ip4_config.get("id"), " ".join(ip4_config.get("ips") + ip6_config.get("ips"))),
982 1
983 )
984
985 return macvlans_running_ipv6_addr
986
987 def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None):
988 macvlan_prefix = self._get_macvlan_prefix(ifaceobjrunning)
989 address_virtuals = glob.glob("/sys/class/net/%s*" %macvlan_prefix)
990 macvlans_ipv6_addrgen_list = []
991 for av in address_virtuals:
992 macvlan_ifacename = os.path.basename(av)
993 rhwaddress = self.cache.get_link_address(macvlan_ifacename)
994 raddress = [str(ip) for ip in self.cache.get_ifupdown2_addresses_list(
995 ifaceobj_list=ifaceobj_getfunc(ifaceobjrunning.name) or [],
996 ifname=ifaceobjrunning.name,
997 with_address_virtual=True
998 )]
999
1000 raddress = list(set(raddress))
1001
1002 if not raddress:
1003 self.logger.warning('%s: no running addresses'
1004 %ifaceobjrunning.name)
1005 raddress = []
1006 ifaceobjrunning.update_config('address-virtual',
1007 '%s %s' %(rhwaddress, ' '.join(raddress)))
1008
1009 macvlans_ipv6_addrgen_list.append((macvlan_ifacename, self.cache.get_link_ipv6_addrgen_mode(macvlan_ifacename)))
1010
1011 macvlan_count = len(address_virtuals)
1012 if not macvlan_count:
1013 return
1014 ipv6_addrgen = macvlans_ipv6_addrgen_list[0][1]
1015
1016 for macvlan_ifname, macvlan_ipv6_addrgen in macvlans_ipv6_addrgen_list:
1017 if macvlan_ipv6_addrgen != ipv6_addrgen:
1018 # one macvlan has a different ipv6-addrgen configuration
1019 # we simply return, ifquery-running will print the macvlan
1020 # stanzas with the ipv6-addrgen on/off attribute
1021 return
1022 ifaceobjrunning.update_config('address-virtual-ipv6-addrgen', 'off' if ipv6_addrgen else 'on')
1023
1024 _run_ops = {
1025 'up': _up,
1026 'down': _down,
1027 'query-checkcurr': _query_check,
1028 'query-running': _query_running
1029 }
1030
1031 def get_ops(self):
1032 """ returns list of ops supported by this module """
1033 return list(self._run_ops.keys())
1034
1035
1036 def run(self, ifaceobj, operation, query_ifaceobj=None,
1037 ifaceobj_getfunc=None, **extra_args):
1038 """ run vlan configuration on the interface object passed as argument
1039
1040 Args:
1041 **ifaceobj** (object): iface object
1042
1043 **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
1044 'query-running'
1045 Kwargs:
1046 **query_ifaceobj** (object): query check ifaceobject. This is only
1047 valid when op is 'query-checkcurr'. It is an object same as
1048 ifaceobj, but contains running attribute values and its config
1049 status. The modules can use it to return queried running state
1050 of interfaces. status is success if the running state is same
1051 as user required state in ifaceobj. error otherwise.
1052 """
1053 if ifaceobj.type == ifaceType.BRIDGE_VLAN:
1054 return
1055 op_handler = self._run_ops.get(operation)
1056 if not op_handler:
1057 return
1058
1059 if operation == 'query-checkcurr':
1060 op_handler(self, ifaceobj, query_ifaceobj)
1061 else:
1062 op_handler(self, ifaceobj, ifaceobj_getfunc=ifaceobj_getfunc)