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