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