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