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