]> git.proxmox.com Git - mirror_ifupdown2.git/blob - ifupdown2/addons/addressvirtual.py
addons: addressvirtual: keep macvlan down if link-down specified on lower device
[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, keep_down=ifaceobj.link_privflags & ifaceLinkPrivFlags.KEEP_LINK_DOWN)
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 ifaceobj.link_privflags & ifaceLinkPrivFlags.KEEP_LINK_DOWN:
452 self.logger.info("%s: keeping macvlan down - link-down yes on lower device %s" % (macvlan_ifname, ifname))
453 netlink.link_set_updown(macvlan_ifname, "down")
454
455 # If link existed before, flap the link
456 if not link_created:
457
458 if not self.addressvirtual_with_route_metric or not self.ipcmd.addr_metric_support():
459 # if the system doesn't support ip addr set METRIC
460 # we need to do manually check the ordering of the ip4 routes
461 self._fix_connected_route(ifaceobj, macvlan_ifname, ips[0])
462
463 if update_mtu:
464 lower_iface_mtu = self.ipcmd.link_get_mtu(ifname, refresh=True)
465 update_mtu = False
466
467 if lower_iface_mtu and lower_iface_mtu != self.ipcmd.link_get_mtu(macvlan_ifname, refresh=True):
468 try:
469 self.ipcmd.link_set_mtu(macvlan_ifname,
470 lower_iface_mtu)
471 except Exception as e:
472 self.logger.info('%s: failed to set mtu %s: %s' %
473 (macvlan_ifname, lower_iface_mtu, e))
474
475 # set macvlan device to up in anycase.
476 # since we auto create them here..we are responsible
477 # to bring them up here in the case they were brought down
478 # by some other entity in the system.
479 if not ifaceobj.link_privflags & ifaceLinkPrivFlags.KEEP_LINK_DOWN:
480 netlink.link_set_updown(macvlan_ifname, "up")
481 else:
482 try:
483 if not self.addressvirtual_with_route_metric or not self.ipcmd.addr_metric_support():
484 # if the system doesn't support ip addr set METRIC
485 # we need to do manually check the ordering of the ip6 routes
486 self.ipcmd.fix_ipv6_route_metric(ifaceobj, macvlan_ifname, ips)
487 except Exception as e:
488 self.logger.debug('fix_vrf_slave_ipv6_route_metric: failed: %s' % e)
489
490 # Disable IPv6 duplicate address detection on VRR interfaces
491 for key, sysval in {
492 "accept_dad": "0",
493 "dad_transmits": "0"
494 }.iteritems():
495 syskey = "net.ipv6.conf.%s.%s" % (macvlan_ifname, key)
496 if self.sysctl_get(syskey) != sysval:
497 self.sysctl_set(syskey, sysval)
498
499 self.ipcmd.batch_commit()
500 return hw_address_list
501
502 def _up(self, ifaceobj, ifaceobj_getfunc=None):
503 if not ifupdownflags.flags.ALL:
504 self._fixup_vrf_enslavements(ifaceobj, ifaceobj_getfunc)
505
506 address_virtual_list = ifaceobj.get_attr_value('address-virtual')
507 vrr_config_list = ifaceobj.get_attr_value("vrrp")
508
509 if not address_virtual_list and not vrr_config_list:
510 # XXX: address virtual is not present. In which case,
511 # delete stale macvlan devices.
512 self._remove_running_address_config(ifaceobj)
513 return
514
515 if ifaceobj.upperifaces and not ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE:
516 self.log_error("%s: invalid placement of address-virtual/vrrp lines "
517 "(must be configured under an interface "
518 "with no upper interfaces or parent interfaces)"
519 % ifaceobj.name, ifaceobj)
520
521 if not self.ipcmd.link_exists(ifaceobj.name):
522 return
523
524 addr_virtual_macs = self.create_macvlan_and_apply_config(
525 ifaceobj,
526 self.translate_addrvirtual_user_config_to_list(
527 ifaceobj,
528 address_virtual_list
529 )
530 )
531
532 vrr_macs = self.create_macvlan_and_apply_config(
533 ifaceobj,
534 self.translate_vrr_user_config_to_list(
535 ifaceobj,
536 vrr_config_list
537 ),
538 vrrp=True
539 )
540
541 hw_address_list = addr_virtual_macs + vrr_macs
542
543 # check the statemanager for old configs.
544 # We need to remove only the previously configured FDB entries
545 oldmacs = self._get_macs_from_old_config(ifaceobj)
546 # get a list of fdbs in old that are not in new config meaning they should
547 # be removed since they are gone from the config
548 removed_macs = [mac for mac in oldmacs if mac.lower() not in hw_address_list]
549 self._remove_addresses_from_bridge(ifaceobj, removed_macs)
550 # if ifaceobj is a bridge and bridge is a vlan aware bridge
551 # add the vid to the bridge
552 self._add_addresses_to_bridge(ifaceobj, hw_address_list)
553
554 def translate_vrr_user_config_to_list(self, ifaceobj, vrr_config_list, ifquery=False):
555 """
556 If (IPv4 addresses provided):
557 00:00:5e:00:01:<V>
558 else if (IPv6 addresses provided):
559 00:00:5e:00:02:<V>
560
561 vrrp 1 10.0.0.15/24
562 vrrp 1 2001:0db8::0370:7334/64
563
564 # Translate:
565 # vrrp 255 10.0.0.15/24 10.0.0.2/1
566 # To:
567 # [
568 # {
569 # "ifname": "macvlan_ifname",
570 # "hwaddress": "macvlan_hwaddress",
571 # "mode": "macvlan_mode",
572 # "ips": [str(IPNetwork), ]
573 # },
574 # ]
575 """
576 ifname = ifaceobj.name
577 user_config_list = []
578
579 for index, config in enumerate(vrr_config_list or []):
580 vrrp_id, ip_addrs = config.split(" ", 1)
581 hex_id = '%02x' % int(vrrp_id)
582 ip4 = []
583 ip6 = []
584
585 for ip_addr in ip_addrs.split():
586 ip_network_obj = IPNetwork(ip_addr)
587 is_ip6 = isinstance(ip_network_obj, IPv6Network)
588
589 if is_ip6:
590 ip6.append(ip_addr)
591 else:
592 ip4.append(ip_addr)
593
594 macvlan_ip4_ifname = "%s%s" % (self.get_vrrp_prefix(ifname, "4"), vrrp_id)
595 macvlan_ip6_ifname = "%s%s" % (self.get_vrrp_prefix(ifname, "6"), vrrp_id)
596
597 if ip4 or ifquery:
598 merged_with_existing_obj = False
599 macvlan_ip4_mac = "00:00:5e:00:01:%s" % hex_id
600 macvlan_ip4_mac_int = self.ipcmd.mac_str_to_int(macvlan_ip4_mac)
601 # if the vrr config is defined in different lines for the same ID
602 # we need to save the ip4 and ip6 in the objects we previously
603 # created, example:
604 # vrrp 255 10.0.0.15/24 10.0.0.2/15
605 # vrrp 255 fe80::a00:27ff:fe04:42/64
606 for obj in user_config_list:
607 if obj.get("hwaddress_int") == macvlan_ip4_mac_int:
608 obj["ips"] += ip4
609 merged_with_existing_obj = True
610
611 if not merged_with_existing_obj:
612 # if ip4 config wasn't merge with an existing object
613 # we need to insert it in our list
614 user_config_list.append({
615 "ifname": macvlan_ip4_ifname,
616 "hwaddress": macvlan_ip4_mac,
617 "hwaddress_int": macvlan_ip4_mac_int,
618 "mode": "bridge",
619 "ips": ip4,
620 "id": vrrp_id
621 })
622 elif not ip4 and not ifquery:
623 # special check to see if all ipv4 were removed from the vrrp
624 # configuration, if so we need to remove the associated macvlan
625 if self.ipcmd.link_exists(macvlan_ip4_ifname):
626 netlink.link_del(macvlan_ip4_ifname)
627
628 if ip6 or ifquery:
629 merged_with_existing_obj = False
630 macvlan_ip6_mac = "00:00:5e:00:02:%s" % hex_id
631 macvlan_ip6_mac_int = self.ipcmd.mac_str_to_int(macvlan_ip6_mac)
632 # if the vrr config is defined in different lines for the same ID
633 # we need to save the ip4 and ip6 in the objects we previously
634 # created, example:
635 # vrrp 255 10.0.0.15/24 10.0.0.2/15
636 # vrrp 255 fe80::a00:27ff:fe04:42/64
637
638 for obj in user_config_list:
639 if obj.get("hwaddress_int") == macvlan_ip6_mac_int:
640 obj["ips"] += ip6
641 merged_with_existing_obj = True
642
643 if not merged_with_existing_obj:
644 # if ip6 config wasn't merge with an existing object
645 # we need to insert it in our list
646 user_config_list.append({
647 "ifname": macvlan_ip6_ifname,
648 "hwaddress": macvlan_ip6_mac,
649 "hwaddress_int": macvlan_ip6_mac_int,
650 "mode": "bridge",
651 "ips": ip6,
652 "id": vrrp_id
653 })
654 elif not ip6 and not ifquery:
655 # special check to see if all ipv6 were removed from the vrrp
656 # configuration, if so we need to remove the associated macvlan
657 if self.ipcmd.link_exists(macvlan_ip6_ifname):
658 netlink.link_del(macvlan_ip6_ifname)
659
660 if not ifquery:
661 # check if vrrp attribute was removed/re-assigned
662 old_vrr_ids = set()
663
664 try:
665 for old_ifaceobj in statemanager.statemanager_api.get_ifaceobjs(ifname) or []:
666 for vrr_config in old_ifaceobj.get_attr_value("vrrp") or []:
667 try:
668 old_vrr_ids.add(vrr_config.split()[0])
669 except:
670 continue
671
672 if old_vrr_ids:
673
674 for config in user_config_list:
675 try:
676 old_vrr_ids.remove(config["id"])
677 except KeyError:
678 pass
679
680 for id_to_remove in old_vrr_ids:
681 macvlan_ip4_ifname = "%s%s" % (self.get_vrrp_prefix(ifname, "4"), id_to_remove)
682 macvlan_ip6_ifname = "%s%s" % (self.get_vrrp_prefix(ifname, "6"), id_to_remove)
683
684 if self.ipcmd.link_exists(macvlan_ip4_ifname):
685 netlink.link_del(macvlan_ip4_ifname)
686
687 if self.ipcmd.link_exists(macvlan_ip6_ifname):
688 netlink.link_del(macvlan_ip6_ifname)
689
690 except Exception as e:
691 self.logger.debug("%s: vrrp: failure while removing unused macvlan(s)" % ifname)
692
693 return user_config_list
694
695 def translate_addrvirtual_user_config_to_list(self, ifaceobj, address_virtual_list):
696 """
697 # Translate:
698 # address-virtual 00:11:22:33:44:01 2001:0db8::0370:7334/64 11.0.1.1/24 11.0.1.2/24
699 # To:
700 # [
701 # {
702 # "ifname": "macvlan_ifname",
703 # "hwaddress": "macvlan_hwaddress",
704 # "ips": [str(IPNetwork), ]
705 # },
706 # ]
707 """
708 user_config_list = []
709
710 if not address_virtual_list:
711 return user_config_list
712
713 macvlan_prefix = self._get_macvlan_prefix(ifaceobj)
714
715 for index, addr_virtual in enumerate(address_virtual_list):
716 av_attrs = addr_virtual.split()
717
718 if len(av_attrs) < 2:
719 self.log_error("%s: incorrect address-virtual attrs '%s'"
720 % (ifaceobj.name, addr_virtual), ifaceobj,
721 raise_error=False)
722 continue
723
724 mac = av_attrs[0]
725 if mac:
726 mac = mac.lower()
727
728 if not self.check_mac_address(ifaceobj, mac):
729 continue
730
731 config = {
732 "ifname": "%s%d" % (macvlan_prefix, index),
733 "mode": "private"
734 }
735
736 if mac != "none":
737 config["hwaddress"] = mac
738 config["hwaddress_int"] = self.ipcmd.mac_str_to_int(mac)
739
740 ip_network_obj_list = []
741 for ip in av_attrs[1:]:
742 ip_network_obj_list.append(str(IPNetwork(ip)))
743
744 config["ips"] = ip_network_obj_list
745 user_config_list.append(config)
746
747 return user_config_list
748
749 def process_macvlans_config(self, ifaceobj, attr_name, virtual_addr_list_raw, macvlan_config_list):
750 return self.create_macvlan_and_apply_config(ifaceobj, macvlan_config_list)
751
752 def _down(self, ifaceobj, ifaceobj_getfunc=None):
753 try:
754 self._remove_address_config(ifaceobj,
755 ifaceobj.get_attr_value('address-virtual'))
756
757 #### VRR
758 hwaddress = []
759 self.ipcmd.batch_start()
760 for vrr_prefix in [self.get_vrrp_prefix(ifaceobj.name, "4"), self.get_vrrp_prefix(ifaceobj.name, "6")]:
761 for macvlan_ifacename in glob.glob("/sys/class/net/%s*" % vrr_prefix):
762 macvlan_ifacename = os.path.basename(macvlan_ifacename)
763 if not self.ipcmd.link_exists(macvlan_ifacename):
764 continue
765 hwaddress.append(self.ipcmd.link_get_hwaddress(macvlan_ifacename))
766 self.ipcmd.link_delete(os.path.basename(macvlan_ifacename))
767 # XXX: Also delete any fdb addresses. This requires, checking mac address
768 # on individual macvlan interfaces and deleting the vlan from that.
769 self.ipcmd.batch_commit()
770 if any(hwaddress):
771 self._remove_addresses_from_bridge(ifaceobj, hwaddress)
772 except Exception, e:
773 import traceback
774 traceback.print_exc()
775 self.log_warn(str(e))
776
777 def _query_check(self, ifaceobj, ifaceobjcurr):
778
779 if not self.ipcmd.link_exists(ifaceobj.name):
780 return
781
782 user_config_address_virtual_ipv6_addr = ifaceobj.get_attr_value_first('address-virtual-ipv6-addrgen')
783 if user_config_address_virtual_ipv6_addr and user_config_address_virtual_ipv6_addr not in utils._string_values:
784 ifaceobjcurr.update_config_with_status('address-virtual-ipv6-addrgen', user_config_address_virtual_ipv6_addr, 1)
785 user_config_address_virtual_ipv6_addr = None
786
787 address_virtual_list = ifaceobj.get_attr_value('address-virtual')
788
789 macvlans_running_ipv6_addr_virtual = self.query_check_macvlan_config(
790 ifaceobj,
791 ifaceobjcurr,
792 "address-virtual",
793 user_config_address_virtual_ipv6_addr,
794 virtual_addr_list_raw=address_virtual_list,
795 macvlan_config_list=self.translate_addrvirtual_user_config_to_list(
796 ifaceobj,
797 address_virtual_list
798 )
799 )
800
801 vrr_config_list = ifaceobj.get_attr_value("vrrp")
802
803 macvlans_running_ipv6_addr_vrr = self.query_check_macvlan_config(
804 ifaceobj,
805 ifaceobjcurr,
806 "vrrp",
807 user_config_address_virtual_ipv6_addr,
808 virtual_addr_list_raw=vrr_config_list,
809 macvlan_config_list=self.translate_vrr_user_config_to_list(
810 ifaceobj,
811 vrr_config_list,
812 ifquery=True
813 )
814 )
815
816 macvlans_running_ipv6_addr = macvlans_running_ipv6_addr_virtual + macvlans_running_ipv6_addr_vrr
817 if user_config_address_virtual_ipv6_addr:
818 bool_user_ipv6_addrgen = utils.get_boolean_from_string(user_config_address_virtual_ipv6_addr)
819 for running_ipv6_addrgen in macvlans_running_ipv6_addr:
820 if (not bool_user_ipv6_addrgen) != running_ipv6_addrgen:
821 ifaceobjcurr.update_config_with_status('address-virtual-ipv6-addrgen', user_config_address_virtual_ipv6_addr, 1)
822 return
823 ifaceobjcurr.update_config_with_status('address-virtual-ipv6-addrgen', user_config_address_virtual_ipv6_addr, 0)
824
825 def query_check_macvlan_config(self, ifaceobj, ifaceobjcurr, attr_name, user_config_address_virtual_ipv6_addr, virtual_addr_list_raw, macvlan_config_list):
826 """
827 macvlan_config_list = [
828 {
829 "ifname": "macvlan_ifname",
830 "hwaddress": "macvlan_hwaddress",
831 "ips": [str(IPNetwork), ]
832 },
833 ]
834 """
835 is_vrr = attr_name == "vrrp"
836 macvlans_running_ipv6_addr = []
837
838 if not virtual_addr_list_raw:
839 return macvlans_running_ipv6_addr
840
841 macvlan_config_queue = deque(macvlan_config_list)
842
843 while macvlan_config_queue:
844
845 ip4_config = None
846 ip6_config = None
847
848 config = macvlan_config_queue.popleft()
849
850 if is_vrr:
851 ip4_config = config
852 ip6_config = macvlan_config_queue.popleft()
853
854 macvlan_ifacename = config.get("ifname")
855
856 if not self.ipcmd.link_exists(macvlan_ifacename):
857 ifaceobjcurr.update_config_with_status(attr_name, "", 1)
858 continue
859
860 macvlan_hwaddress = config.get("hwaddress")
861 macvlan_hwaddress_int = config.get("hwaddress_int")
862
863 if user_config_address_virtual_ipv6_addr:
864 macvlans_running_ipv6_addr.append(self.ipcmd.get_ipv6_addrgen_mode(macvlan_ifacename))
865
866 # Check mac and ip address
867 rhwaddress = ip4_macvlan_hwaddress = self.ipcmd.link_get_hwaddress(macvlan_ifacename)
868 raddrs = ip4_running_addrs = self.ipcmd.get_running_addrs(
869 ifname=macvlan_ifacename,
870 details=False,
871 addr_virtual_ifaceobj=ifaceobj
872 )
873
874 if not is_vrr:
875 ips = config.get("ips")
876
877 if not raddrs or not rhwaddress:
878 ifaceobjcurr.update_config_with_status(attr_name, "", 1)
879 continue
880
881 try:
882 if self.ipcmd.mac_str_to_int(rhwaddress) == macvlan_hwaddress_int \
883 and self.ipcmd.compare_user_config_vs_running_state(raddrs, ips) \
884 and self._check_addresses_in_bridge(ifaceobj, macvlan_hwaddress):
885 ifaceobjcurr.update_config_with_status(
886 attr_name,
887 " ".join(virtual_addr_list_raw),
888 0
889 )
890 else:
891 ifaceobjcurr.update_config_with_status(
892 attr_name,
893 '%s %s' % (rhwaddress, ' '.join(raddrs)),
894 1
895 )
896 except:
897 ifaceobjcurr.update_config_with_status(
898 attr_name,
899 '%s %s' % (rhwaddress, ' '.join(raddrs)),
900 1
901 )
902 else:
903 # VRRP
904
905 ok = False
906 # check macvlan ip4 hwaddress (only if ip4 were provided by the user)
907 if not ip4_config.get("ips") or ip4_macvlan_hwaddress == ip4_config.get("hwaddress"):
908 ip6_macvlan_ifname = ip6_config.get("ifname")
909 ip6_macvlan_hwaddress = ip6_config.get("hwaddress")
910
911 # check macvlan ip6 hwaddress (only if ip6 were provided by the user)
912 if not ip6_config.get("ips") or self.ipcmd.link_get_hwaddress(ip6_macvlan_ifname) == ip6_macvlan_hwaddress:
913
914 # check all ip4
915 if self.ipcmd.compare_user_config_vs_running_state(
916 ip4_running_addrs,
917 ip4_config.get("ips")
918 ) and self._check_addresses_in_bridge(ifaceobj, ip4_macvlan_hwaddress):
919 ip6_running_addrs = self.ipcmd.get_running_addrs(
920 ifname=ip6_macvlan_ifname,
921 details=False,
922 addr_virtual_ifaceobj=ifaceobj
923 )
924
925 # check all ip6
926 if self.ipcmd.compare_user_config_vs_running_state(
927 ip6_running_addrs,
928 ip6_config.get("ips")
929 ) and self._check_addresses_in_bridge(ifaceobj, ip4_macvlan_hwaddress):
930 ifaceobjcurr.update_config_with_status(
931 attr_name,
932 "%s %s" % (ip4_config.get("id"), " ".join(ip4_config.get("ips") + ip6_config.get("ips"))),
933 0
934 )
935 ok = True
936
937 if not ok:
938 ifaceobjcurr.update_config_with_status(
939 attr_name,
940 "%s %s" % (ip4_config.get("id"), " ".join(ip4_config.get("ips") + ip6_config.get("ips"))),
941 1
942 )
943
944 return macvlans_running_ipv6_addr
945
946 def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None):
947 macvlan_prefix = self._get_macvlan_prefix(ifaceobjrunning)
948 address_virtuals = []
949 for av in linkCache.links:
950 if av.startswith(macvlan_prefix):
951 address_virtuals.append(av)
952
953 macvlans_ipv6_addrgen_list = []
954 for av in address_virtuals:
955 macvlan_ifacename = os.path.basename(av)
956 rhwaddress = self.ipcmd.link_get_hwaddress(macvlan_ifacename)
957
958 raddress = []
959 for obj in ifaceobj_getfunc(ifaceobjrunning.name) or []:
960 raddress.extend(self.ipcmd.get_running_addrs(None, macvlan_ifacename, addr_virtual_ifaceobj=obj) or [])
961
962 raddress = list(set(raddress))
963
964 if not raddress:
965 self.logger.warn('%s: no running addresses'
966 %ifaceobjrunning.name)
967 raddress = []
968 ifaceobjrunning.update_config('address-virtual',
969 '%s %s' %(rhwaddress, ' '.join(raddress)))
970
971 macvlans_ipv6_addrgen_list.append((macvlan_ifacename, self.ipcmd.get_ipv6_addrgen_mode(macvlan_ifacename)))
972
973 macvlan_count = len(address_virtuals)
974 if not macvlan_count:
975 return
976 ipv6_addrgen = macvlans_ipv6_addrgen_list[0][1]
977
978 for macvlan_ifname, macvlan_ipv6_addrgen in macvlans_ipv6_addrgen_list:
979 if macvlan_ipv6_addrgen != ipv6_addrgen:
980 # one macvlan has a different ipv6-addrgen configuration
981 # we simply return, ifquery-running will print the macvlan
982 # stanzas with the ipv6-addrgen on/off attribute
983 return
984 ifaceobjrunning.update_config('address-virtual-ipv6-addrgen', 'off' if ipv6_addrgen else 'on')
985
986
987 _run_ops = {'up' : _up,
988 'down' : _down,
989 'query-checkcurr' : _query_check,
990 'query-running' : _query_running}
991
992 def get_ops(self):
993 """ returns list of ops supported by this module """
994 return self._run_ops.keys()
995
996 def _init_command_handlers(self):
997 if not self.ipcmd:
998 self.ipcmd = LinkUtils()
999
1000 def run(self, ifaceobj, operation, query_ifaceobj=None,
1001 ifaceobj_getfunc=None, **extra_args):
1002 """ run vlan configuration on the interface object passed as argument
1003
1004 Args:
1005 **ifaceobj** (object): iface object
1006
1007 **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
1008 'query-running'
1009 Kwargs:
1010 **query_ifaceobj** (object): query check ifaceobject. This is only
1011 valid when op is 'query-checkcurr'. It is an object same as
1012 ifaceobj, but contains running attribute values and its config
1013 status. The modules can use it to return queried running state
1014 of interfaces. status is success if the running state is same
1015 as user required state in ifaceobj. error otherwise.
1016 """
1017 if ifaceobj.type == ifaceType.BRIDGE_VLAN:
1018 return
1019 op_handler = self._run_ops.get(operation)
1020 if not op_handler:
1021 return
1022 self._init_command_handlers()
1023 if operation == 'query-checkcurr':
1024 op_handler(self, ifaceobj, query_ifaceobj)
1025 else:
1026 op_handler(self, ifaceobj, ifaceobj_getfunc=ifaceobj_getfunc)