]> git.proxmox.com Git - mirror_ifupdown2.git/blame - ifupdown2/addons/addressvirtual.py
addons: addressvirtual: create VRRP macvlans in bridge mode
[mirror_ifupdown2.git] / ifupdown2 / addons / addressvirtual.py
CommitLineData
15ef32ea
RP
1#!/usr/bin/python
2#
d486dd0d 3# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
15ef32ea
RP
4# Author: Roopa Prabhu, roopa@cumulusnetworks.com
5#
6
15ef32ea
RP
7import os
8import glob
9
5bc963f0
JF
10from string import maketrans
11from collections import deque
d486dd0d
JF
12from ipaddr import IPNetwork, IPv6Network
13
14try:
15 from ifupdown2.ifupdown.iface import *
7b711dc5 16 from ifupdown2.ifupdown.utils import utils
d486dd0d
JF
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
23e8546d 23 import ifupdown2.ifupdown.policymanager as policymanager
d486dd0d
JF
24 import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
25 import ifupdown2.ifupdown.ifupdownconfig as ifupdownconfig
26except ImportError:
27 from ifupdown.iface import *
7b711dc5 28 from ifupdown.utils import utils
d486dd0d
JF
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
23e8546d 35 import ifupdown.policymanager as policymanager
d486dd0d
JF
36 import ifupdown.ifupdownflags as ifupdownflags
37 import ifupdown.ifupdownconfig as ifupdownconfig
38
39
15ef32ea
RP
40class 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' :
482b2fab 48 { 'help' : 'bridge router virtual mac and ips',
100ab3ea 49 'multivalue' : True,
2c592263 50 'validvals' : ['<mac-ip/prefixlen-list>',],
7b711dc5
JF
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': [
b478792b
JF
58 'address-virtual-ipv6-addrgen on',
59 'address-virtual-ipv6-addrgen off'
7b711dc5 60 ]
5bc963f0
JF
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 ]
7b711dc5
JF
69 }
70 }}
15ef32ea 71
8e113d63 72
15ef32ea
RP
73 def __init__(self, *args, **kargs):
74 moduleBase.__init__(self, *args, **kargs)
75 self.ipcmd = None
8e113d63 76 self._bridge_fdb_query_cache = {}
23e8546d
JF
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 )
15ef32ea 84
17da0561 85 self.address_virtual_ipv6_addrgen_value_dict = {'on': 0, 'yes': 0, '0': 0, 'off': 1, 'no': 1, '1': 1}
5bc963f0 86 self.mac_translate_tab = maketrans(":.-,", " ")
17da0561 87
42e85fc8 88 def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None):
5bc963f0 89 if ifaceobj.get_attr_value('address-virtual') or ifaceobj.get_attr_value("vrrp"):
42e85fc8 90 ifaceobj.link_privflags |= ifaceLinkPrivFlags.ADDRESS_VIRTUAL_SLAVE
15ef32ea 91
aaef0a79
RP
92 def _get_macvlan_prefix(self, ifaceobj):
93 return '%s-v' %ifaceobj.name[0:13].replace('.', '-')
94
8fb6dd67 95 @staticmethod
68c8d699
JF
96 def get_vrrp_prefix(ifname, family):
97 return "vrrp%s-if%s-v" % (family, netlink.get_iface_index(ifname))
8fb6dd67 98
e1601369 99 def _add_addresses_to_bridge(self, ifaceobj, hwaddress):
8e113d63 100 # XXX: batch the addresses
d486dd0d
JF
101 if ifaceobj.link_kind & ifaceLinkKind.VLAN:
102 bridgename = ifaceobj.lowerifaces[0]
103 vlan = self._get_vlan_id(ifaceobj)
e1601369
RP
104 if self.ipcmd.bridge_is_vlan_aware(bridgename):
105 [self.ipcmd.bridge_fdb_add(bridgename, addr,
106 vlan) for addr in hwaddress]
8e113d63
RP
107 elif self.ipcmd.is_bridge(ifaceobj.name):
108 [self.ipcmd.bridge_fdb_add(ifaceobj.name, addr)
109 for addr in hwaddress]
e1601369
RP
110
111 def _remove_addresses_from_bridge(self, ifaceobj, hwaddress):
8e113d63 112 # XXX: batch the addresses
d486dd0d
JF
113 if ifaceobj.link_kind & ifaceLinkKind.VLAN:
114 bridgename = ifaceobj.lowerifaces[0]
115 vlan = self._get_vlan_id(ifaceobj)
e1601369 116 if self.ipcmd.bridge_is_vlan_aware(bridgename):
55072bd1
ST
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
8e113d63 123 elif self.ipcmd.is_bridge(ifaceobj.name):
55072bd1
ST
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
8e113d63
RP
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 """
d486dd0d
JF
143 if ifaceobj.link_kind & ifaceLinkKind.VLAN:
144 bridgename = ifaceobj.lowerifaces[0]
145 vlan = self._get_vlan_id(ifaceobj)
8e113d63 146 if self.ipcmd.bridge_is_vlan_aware(bridgename):
d486dd0d 147 fdb_addrs = self._get_bridge_fdbs(bridgename, str(vlan))
5bc963f0 148 if not fdb_addrs:
8e113d63 149 return False
5bc963f0
JF
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
8e113d63 155 return True
e1601369 156
00f6105d
RP
157 def _fix_connected_route(self, ifaceobj, vifacename, addr):
158 #
d486dd0d 159 # XXX: Hack to make sure the primary address
00f6105d
RP
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.
d486dd0d 165 #
00f6105d
RP
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)
d486dd0d
JF
173
174 # we don't support ip6 route fix yet
175 if type(ip) == IPv6Network:
176 return
177
00f6105d
RP
178 route_prefix = '%s/%d' %(ip.network, ip.prefixlen)
179
aa895ecd
JF
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
d486dd0d 187 if dev and dev != ifaceobj.name:
00f6105d
RP
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)'
d486dd0d 196 % (ifaceobj.name, str(e)))
00f6105d
RP
197 pass
198
1b284018
RP
199 def _handle_vrf_slaves(self, macvlan_ifacename, ifaceobj):
200 vrfname = self.ipcmd.link_get_master(ifaceobj.name)
201 if vrfname:
d486dd0d 202 self.ipcmd.link_set(macvlan_ifacename, 'master', vrfname)
1b284018 203
55072bd1
ST
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
7b711dc5
JF
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:
007cae35
JF
228 # IFLA_INET6_ADDR_GEN_MODE values:
229 # 0 = eui64
230 # 1 = none
17da0561 231 ipv6_addrgen_nl = self.address_virtual_ipv6_addrgen_value_dict.get(ipv6_addrgen.lower(), None)
007cae35
JF
232
233 if ipv6_addrgen_nl is None:
007cae35
JF
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
7b711dc5 237
17da0561
JF
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
7b711dc5
JF
247 return False, None
248
e1601369
RP
249 def _remove_running_address_config(self, ifaceobj):
250 if not self.ipcmd.link_exists(ifaceobj.name):
15ef32ea 251 return
e1601369 252 hwaddress = []
15ef32ea 253 self.ipcmd.batch_start()
709f7942
JF
254 for macvlan_prefix in [
255 self._get_macvlan_prefix(ifaceobj),
68c8d699
JF
256 self.get_vrrp_prefix(ifaceobj.name, "4"),
257 self.get_vrrp_prefix(ifaceobj.name, "6")
709f7942
JF
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.
15ef32ea 267 self.ipcmd.batch_commit()
e1601369
RP
268 if any(hwaddress):
269 self._remove_addresses_from_bridge(ifaceobj, hwaddress)
15ef32ea 270
e1601369
RP
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
aaef0a79 281 macvlan_prefix = self._get_macvlan_prefix(ifaceobj)
e1601369
RP
282 for av in address_virtual_list:
283 av_attrs = av.split()
284 if len(av_attrs) < 2:
bf3eda91
RP
285 self.log_error("%s: incorrect address-virtual attrs '%s'"
286 %(ifaceobj.name, av), ifaceobj,
287 raise_error=False)
e1601369
RP
288 av_idx += 1
289 continue
290
291 # Delete the macvlan device on this device
cb46a208 292 macvlan_ifacename = '%s%d' %(macvlan_prefix, av_idx)
e1601369
RP
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)
15ef32ea 299
4d3dc0f7 300 def check_mac_address(self, ifaceobj, mac):
5bc963f0 301 if mac == 'none':
d486dd0d 302 return True
4d3dc0f7 303 try:
586535e8 304 if int(mac.split(":")[0], 16) & 1 :
5b1fffaf
JF
305 self.log_error("%s: Multicast bit is set in the virtual mac address '%s'"
306 % (ifaceobj.name, mac), ifaceobj=ifaceobj)
4d3dc0f7
N
307 return False
308 return True
5b1fffaf 309 except ValueError:
4d3dc0f7
N
310 return False
311
42e85fc8
RP
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
d486dd0d
JF
349 self.ipcmd.link_set(u, 'master', ifaceobj.name,
350 state='up')
42e85fc8
RP
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):
d486dd0d
JF
370 self.ipcmd.link_set(u, 'master', vrfname,
371 state='up')
42e85fc8 372
5bc963f0
JF
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")
e588acb7 405 macvlan_mode = intf_config_dict.get("mode")
5bc963f0
JF
406 ips = intf_config_dict.get("ips")
407
408 if not self.ipcmd.link_exists(macvlan_ifname):
e588acb7 409 self.ipcmd.link_add_macvlan(ifname, macvlan_ifname, macvlan_mode)
5bc963f0
JF
410 link_created = True
411
412 # first thing we need to handle vrf enslavement
413 if ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE:
414 self._handle_vrf_slaves(macvlan_ifname, ifaceobj)
415
416 if user_configured_ipv6_addrgenmode:
417 self.ipcmd.ipv6_addrgen(macvlan_ifname, ipv6_addrgen_user_value, link_created)
418
419 if macvlan_hwaddr:
420 self.ipcmd.link_set_hwaddress(macvlan_ifname, macvlan_hwaddr)
421 hw_address_list.append(macvlan_hwaddr)
422
423 if self.addressvirtual_with_route_metric and self.ipcmd.addr_metric_support():
424 metric = self.ipcmd.get_default_ip_metric()
425 else:
426 metric = None
427
428 self.ipcmd.addr_add_multiple(
429 ifaceobj,
430 macvlan_ifname,
431 ips,
432 purge_existing,
433 metric=metric
434 )
435
436 # If link existed before, flap the link
437 if not link_created:
438
439 if not self.addressvirtual_with_route_metric or not self.ipcmd.addr_metric_support():
440 # if the system doesn't support ip addr set METRIC
441 # we need to do manually check the ordering of the ip4 routes
442 self._fix_connected_route(ifaceobj, macvlan_ifname, ips[0])
443
444 if update_mtu:
445 lower_iface_mtu = self.ipcmd.link_get_mtu(ifname, refresh=True)
446 update_mtu = False
447
448 if lower_iface_mtu and lower_iface_mtu != self.ipcmd.link_get_mtu(macvlan_ifname, refresh=True):
449 try:
450 self.ipcmd.link_set_mtu(macvlan_ifname,
451 lower_iface_mtu)
452 except Exception as e:
453 self.logger.info('%s: failed to set mtu %s: %s' %
454 (macvlan_ifname, lower_iface_mtu, e))
455
456 # set macvlan device to up in anycase.
457 # since we auto create them here..we are responsible
458 # to bring them up here in the case they were brought down
459 # by some other entity in the system.
460 netlink.link_set_updown(macvlan_ifname, "up")
461 else:
462 try:
463 if not self.addressvirtual_with_route_metric or not self.ipcmd.addr_metric_support():
464 # if the system doesn't support ip addr set METRIC
465 # we need to do manually check the ordering of the ip6 routes
466 self.ipcmd.fix_ipv6_route_metric(ifaceobj, macvlan_ifname, ips)
467 except Exception as e:
468 self.logger.debug('fix_vrf_slave_ipv6_route_metric: failed: %s' % e)
469
470 # Disable IPv6 duplicate address detection on VRR interfaces
471 for key, sysval in {
472 "accept_dad": "0",
473 "dad_transmits": "0"
474 }.iteritems():
475 syskey = "net.ipv6.conf.%s.%s" % (macvlan_ifname, key)
476 if self.sysctl_get(syskey) != sysval:
477 self.sysctl_set(syskey, sysval)
478
479 self.ipcmd.batch_commit()
480 return hw_address_list
481
42e85fc8
RP
482 def _up(self, ifaceobj, ifaceobj_getfunc=None):
483 if not ifupdownflags.flags.ALL:
484 self._fixup_vrf_enslavements(ifaceobj, ifaceobj_getfunc)
5bc963f0 485
15ef32ea 486 address_virtual_list = ifaceobj.get_attr_value('address-virtual')
5bc963f0
JF
487 vrr_config_list = ifaceobj.get_attr_value("vrrp")
488
489 if not address_virtual_list and not vrr_config_list:
15ef32ea 490 # XXX: address virtual is not present. In which case,
00f6105d 491 # delete stale macvlan devices.
5bc963f0 492 self._remove_running_address_config(ifaceobj)
15ef32ea
RP
493 return
494
5bc963f0
JF
495 if ifaceobj.upperifaces and not ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE:
496 self.log_error("%s: invalid placement of address-virtual/vrrp lines "
497 "(must be configured under an interface "
498 "with no upper interfaces or parent interfaces)"
499 % ifaceobj.name, ifaceobj)
f466af7a 500
e1601369 501 if not self.ipcmd.link_exists(ifaceobj.name):
15ef32ea 502 return
5bc963f0
JF
503
504 addr_virtual_macs = self.create_macvlan_and_apply_config(
505 ifaceobj,
506 self.translate_addrvirtual_user_config_to_list(
507 ifaceobj,
508 address_virtual_list
509 )
510 )
511
512 vrr_macs = self.create_macvlan_and_apply_config(
513 ifaceobj,
514 self.translate_vrr_user_config_to_list(
515 ifaceobj,
516 vrr_config_list
517 )
518 )
519
520 hw_address_list = addr_virtual_macs + vrr_macs
521
522 # check the statemanager for old configs.
523 # We need to remove only the previously configured FDB entries
524 oldmacs = self._get_macs_from_old_config(ifaceobj)
525 # get a list of fdbs in old that are not in new config meaning they should
526 # be removed since they are gone from the config
527 removed_macs = [mac for mac in oldmacs if mac.lower() not in hw_address_list]
528 self._remove_addresses_from_bridge(ifaceobj, removed_macs)
529 # if ifaceobj is a bridge and bridge is a vlan aware bridge
530 # add the vid to the bridge
531 self._add_addresses_to_bridge(ifaceobj, hw_address_list)
532
5bc963f0
JF
533 def translate_vrr_user_config_to_list(self, ifaceobj, vrr_config_list, ifquery=False):
534 """
535 If (IPv4 addresses provided):
536 00:00:5e:00:01:<V>
537 else if (IPv6 addresses provided):
538 00:00:5e:00:02:<V>
539
540 vrrp 1 10.0.0.15/24
541 vrrp 1 2001:0db8::0370:7334/64
542
543 # Translate:
544 # vrrp 255 10.0.0.15/24 10.0.0.2/1
545 # To:
546 # [
547 # {
548 # "ifname": "macvlan_ifname",
549 # "hwaddress": "macvlan_hwaddress",
e588acb7 550 # "mode": "macvlan_mode",
5bc963f0
JF
551 # "ips": [str(IPNetwork), ]
552 # },
553 # ]
554 """
555 ifname = ifaceobj.name
556 user_config_list = []
557
8fb6dd67 558 for index, config in enumerate(vrr_config_list or []):
5bc963f0
JF
559 vrrp_id, ip_addrs = config.split(" ", 1)
560 hex_id = '%02x' % int(vrrp_id)
561 ip4 = []
562 ip6 = []
563
564 for ip_addr in ip_addrs.split():
565 ip_network_obj = IPNetwork(ip_addr)
566 is_ip6 = isinstance(ip_network_obj, IPv6Network)
567
568 if is_ip6:
569 ip6.append(ip_addr)
570 else:
571 ip4.append(ip_addr)
572
68c8d699
JF
573 macvlan_ip4_ifname = "%s%s" % (self.get_vrrp_prefix(ifname, "4"), index)
574 macvlan_ip6_ifname = "%s%s" % (self.get_vrrp_prefix(ifname, "6"), index)
5bc963f0
JF
575
576 merged_with_existing_obj = False
577 # if the vrr config is defined in different lines for the same ID
578 # we need to save the ip4 and ip6 in the objects we previously
579 # created, example:
580 # vrrp 255 10.0.0.15/24 10.0.0.2/15
581 # vrrp 255 fe80::a00:27ff:fe04:42/64
582 for obj in user_config_list:
583 if obj.get("ifname") == macvlan_ip4_ifname:
584 obj["ips"] += ip4
585 merged_with_existing_obj = True
586 elif obj.get("ifname") == macvlan_ip6_ifname:
587 obj["ips"] += ip6
588 merged_with_existing_obj = True
589
590 if merged_with_existing_obj:
591 continue
592
593 if ip4 or ifquery:
594 # config_ip4
595 macvlan_ip4_mac = "00:00:5e:00:01:%s" % hex_id
596 user_config_list.append({
597 "ifname": macvlan_ip4_ifname,
598 "hwaddress": macvlan_ip4_mac,
599 "hwaddress_int": self.mac_str_to_int(macvlan_ip4_mac),
e588acb7 600 "mode": "bridge",
5bc963f0
JF
601 "ips": ip4,
602 "id": vrrp_id
603 })
604
605 if ip6 or ifquery:
606 # config_ip6
607 macvlan_ip6_mac = "00:00:5e:00:02:%s" % hex_id
608 user_config_list.append({
609 "ifname": macvlan_ip6_ifname,
610 "hwaddress": macvlan_ip6_mac,
611 "hwaddress_int": self.mac_str_to_int(macvlan_ip6_mac),
e588acb7 612 "mode": "bridge",
5bc963f0
JF
613 "ips": ip6,
614 "id": vrrp_id
615 })
616
617 return user_config_list
618
619 def translate_addrvirtual_user_config_to_list(self, ifaceobj, address_virtual_list):
620 """
621 # Translate:
622 # address-virtual 00:11:22:33:44:01 2001:0db8::0370:7334/64 11.0.1.1/24 11.0.1.2/24
623 # To:
624 # [
625 # {
626 # "ifname": "macvlan_ifname",
627 # "hwaddress": "macvlan_hwaddress",
628 # "ips": [str(IPNetwork), ]
629 # },
630 # ]
631 """
632 user_config_list = []
633
634 if not address_virtual_list:
635 return user_config_list
636
637 macvlan_prefix = self._get_macvlan_prefix(ifaceobj)
638
639 for index, addr_virtual in enumerate(address_virtual_list):
640 av_attrs = addr_virtual.split()
641
642 if len(av_attrs) < 2:
643 self.log_error("%s: incorrect address-virtual attrs '%s'"
644 % (ifaceobj.name, addr_virtual), ifaceobj,
645 raise_error=False)
646 continue
647
648 mac = av_attrs[0]
649 if mac:
650 mac = mac.lower()
651
652 if not self.check_mac_address(ifaceobj, mac):
653 continue
654
e588acb7
JF
655 config = {
656 "ifname": "%s%d" % (macvlan_prefix, index),
657 "mode": "private"
658 }
5bc963f0
JF
659
660 if mac != "none":
661 config["hwaddress"] = mac
662 config["hwaddress_int"] = self.mac_str_to_int(mac)
663
664 ip_network_obj_list = []
665 for ip in av_attrs[1:]:
666 ip_network_obj_list.append(str(IPNetwork(ip)))
667
668 config["ips"] = ip_network_obj_list
669 user_config_list.append(config)
670
671 return user_config_list
672
673 def process_macvlans_config(self, ifaceobj, attr_name, virtual_addr_list_raw, macvlan_config_list):
674 return self.create_macvlan_and_apply_config(ifaceobj, macvlan_config_list)
15ef32ea 675
42e85fc8 676 def _down(self, ifaceobj, ifaceobj_getfunc=None):
15ef32ea 677 try:
cb46a208
RP
678 self._remove_address_config(ifaceobj,
679 ifaceobj.get_attr_value('address-virtual'))
5bc963f0
JF
680
681 #### VRR
682 hwaddress = []
683 self.ipcmd.batch_start()
68c8d699 684 for vrr_prefix in [self.get_vrrp_prefix(ifaceobj.name, "4"), self.get_vrrp_prefix(ifaceobj.name, "6")]:
5bc963f0
JF
685 for macvlan_ifacename in glob.glob("/sys/class/net/%s*" % vrr_prefix):
686 macvlan_ifacename = os.path.basename(macvlan_ifacename)
687 if not self.ipcmd.link_exists(macvlan_ifacename):
688 continue
689 hwaddress.append(self.ipcmd.link_get_hwaddress(macvlan_ifacename))
690 self.ipcmd.link_delete(os.path.basename(macvlan_ifacename))
691 # XXX: Also delete any fdb addresses. This requires, checking mac address
692 # on individual macvlan interfaces and deleting the vlan from that.
693 self.ipcmd.batch_commit()
694 if any(hwaddress):
695 self._remove_addresses_from_bridge(ifaceobj, hwaddress)
15ef32ea 696 except Exception, e:
5bc963f0
JF
697 import traceback
698 traceback.print_exc()
15ef32ea
RP
699 self.log_warn(str(e))
700
701 def _query_check(self, ifaceobj, ifaceobjcurr):
5bc963f0 702
cb46a208
RP
703 if not self.ipcmd.link_exists(ifaceobj.name):
704 return
007cae35
JF
705
706 user_config_address_virtual_ipv6_addr = ifaceobj.get_attr_value_first('address-virtual-ipv6-addrgen')
707 if user_config_address_virtual_ipv6_addr and user_config_address_virtual_ipv6_addr not in utils._string_values:
708 ifaceobjcurr.update_config_with_status('address-virtual-ipv6-addrgen', user_config_address_virtual_ipv6_addr, 1)
709 user_config_address_virtual_ipv6_addr = None
5bc963f0
JF
710
711 address_virtual_list = ifaceobj.get_attr_value('address-virtual')
712
713 macvlans_running_ipv6_addr_virtual = self.query_check_macvlan_config(
714 ifaceobj,
715 ifaceobjcurr,
716 "address-virtual",
717 user_config_address_virtual_ipv6_addr,
718 virtual_addr_list_raw=address_virtual_list,
719 macvlan_config_list=self.translate_addrvirtual_user_config_to_list(
720 ifaceobj,
721 address_virtual_list
722 )
723 )
724
725 vrr_config_list = ifaceobj.get_attr_value("vrrp")
726
727 macvlans_running_ipv6_addr_vrr = self.query_check_macvlan_config(
728 ifaceobj,
729 ifaceobjcurr,
730 "vrrp",
731 user_config_address_virtual_ipv6_addr,
732 virtual_addr_list_raw=vrr_config_list,
733 macvlan_config_list=self.translate_vrr_user_config_to_list(
734 ifaceobj,
735 vrr_config_list,
736 ifquery=True
737 )
738 )
739
740 macvlans_running_ipv6_addr = macvlans_running_ipv6_addr_virtual + macvlans_running_ipv6_addr_vrr
741 if user_config_address_virtual_ipv6_addr:
742 bool_user_ipv6_addrgen = utils.get_boolean_from_string(user_config_address_virtual_ipv6_addr)
743 for running_ipv6_addrgen in macvlans_running_ipv6_addr:
744 if (not bool_user_ipv6_addrgen) != running_ipv6_addrgen:
745 ifaceobjcurr.update_config_with_status('address-virtual-ipv6-addrgen', user_config_address_virtual_ipv6_addr, 1)
746 return
747 ifaceobjcurr.update_config_with_status('address-virtual-ipv6-addrgen', user_config_address_virtual_ipv6_addr, 0)
748
749 def query_check_macvlan_config(self, ifaceobj, ifaceobjcurr, attr_name, user_config_address_virtual_ipv6_addr, virtual_addr_list_raw, macvlan_config_list):
750 """
751 macvlan_config_list = [
752 {
753 "ifname": "macvlan_ifname",
754 "hwaddress": "macvlan_hwaddress",
755 "ips": [str(IPNetwork), ]
756 },
757 ]
758 """
759 is_vrr = attr_name == "vrrp"
007cae35
JF
760 macvlans_running_ipv6_addr = []
761
5bc963f0
JF
762 if not virtual_addr_list_raw:
763 return macvlans_running_ipv6_addr
764
765 macvlan_config_queue = deque(macvlan_config_list)
766
767 while macvlan_config_queue:
768
769 ip4_config = None
770 ip6_config = None
771
772 config = macvlan_config_queue.popleft()
773
774 if is_vrr:
775 ip4_config = config
776 ip6_config = macvlan_config_queue.popleft()
777
778 macvlan_ifacename = config.get("ifname")
15ef32ea 779
cb46a208 780 if not self.ipcmd.link_exists(macvlan_ifacename):
5bc963f0 781 ifaceobjcurr.update_config_with_status(attr_name, "", 1)
cb46a208 782 continue
007cae35 783
5bc963f0
JF
784 macvlan_hwaddress = config.get("hwaddress")
785 macvlan_hwaddress_int = config.get("hwaddress_int")
786
007cae35
JF
787 if user_config_address_virtual_ipv6_addr:
788 macvlans_running_ipv6_addr.append(self.ipcmd.get_ipv6_addrgen_mode(macvlan_ifacename))
789
cb46a208 790 # Check mac and ip address
5bc963f0
JF
791 rhwaddress = ip4_macvlan_hwaddress = self.ipcmd.link_get_hwaddress(macvlan_ifacename)
792 raddrs = ip4_running_addrs = self.ipcmd.get_running_addrs(
d486dd0d
JF
793 ifname=macvlan_ifacename,
794 details=False,
795 addr_virtual_ifaceobj=ifaceobj
796 )
007cae35 797
5bc963f0
JF
798 if not is_vrr:
799 ips = config.get("ips")
800
801 if not raddrs or not rhwaddress:
802 ifaceobjcurr.update_config_with_status(attr_name, "", 1)
803 continue
804
805 try:
806 if self.mac_str_to_int(rhwaddress) == macvlan_hwaddress_int \
807 and self.ipcmd.compare_user_config_vs_running_state(raddrs, ips) \
808 and self._check_addresses_in_bridge(ifaceobj, macvlan_hwaddress):
809 ifaceobjcurr.update_config_with_status(
810 attr_name,
811 " ".join(virtual_addr_list_raw),
812 0
813 )
814 else:
815 ifaceobjcurr.update_config_with_status(
816 attr_name,
817 '%s %s' % (rhwaddress, ' '.join(raddrs)),
818 1
819 )
820 except:
821 ifaceobjcurr.update_config_with_status(
822 attr_name,
823 '%s %s' % (rhwaddress, ' '.join(raddrs)),
824 1
825 )
826 else:
827 # VRRP
828
829 ok = False
830 # check macvlan ip4 hwaddress (only if ip4 were provided by the user)
831 if not ip4_config.get("ips") or ip4_macvlan_hwaddress == ip4_config.get("hwaddress"):
832 ip6_macvlan_ifname = ip6_config.get("ifname")
833 ip6_macvlan_hwaddress = ip6_config.get("hwaddress")
834
835 # check macvlan ip6 hwaddress (only if ip6 were provided by the user)
836 if not ip6_config.get("ips") or self.ipcmd.link_get_hwaddress(ip6_macvlan_ifname) == ip6_macvlan_hwaddress:
837
838 # check all ip4
839 if self.ipcmd.compare_user_config_vs_running_state(
840 ip4_running_addrs,
841 ip4_config.get("ips")
842 ) and self._check_addresses_in_bridge(ifaceobj, ip4_macvlan_hwaddress):
843 ip6_running_addrs = self.ipcmd.get_running_addrs(
844 ifname=ip6_macvlan_ifname,
845 details=False,
846 addr_virtual_ifaceobj=ifaceobj
847 )
848
849 # check all ip6
850 if self.ipcmd.compare_user_config_vs_running_state(
851 ip6_running_addrs,
852 ip6_config.get("ips")
853 ) and self._check_addresses_in_bridge(ifaceobj, ip4_macvlan_hwaddress):
854 ifaceobjcurr.update_config_with_status(
855 attr_name,
856 "%s %s" % (ip4_config.get("id"), " ".join(ip4_config.get("ips") + ip6_config.get("ips"))),
857 0
858 )
859 ok = True
860
861 if not ok:
862 ifaceobjcurr.update_config_with_status(
863 attr_name,
864 "%s %s" % (ip4_config.get("id"), " ".join(ip4_config.get("ips") + ip6_config.get("ips"))),
865 1
866 )
867
868 return macvlans_running_ipv6_addr
15ef32ea 869
42e85fc8 870 def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None):
aaef0a79 871 macvlan_prefix = self._get_macvlan_prefix(ifaceobjrunning)
8e113d63 872 address_virtuals = glob.glob("/sys/class/net/%s*" %macvlan_prefix)
007cae35 873 macvlans_ipv6_addrgen_list = []
8e113d63
RP
874 for av in address_virtuals:
875 macvlan_ifacename = os.path.basename(av)
876 rhwaddress = self.ipcmd.link_get_hwaddress(macvlan_ifacename)
c3175b31
JF
877
878 raddress = []
879 for obj in ifaceobj_getfunc(ifaceobjrunning.name) or []:
880 raddress.extend(self.ipcmd.get_running_addrs(None, macvlan_ifacename, addr_virtual_ifaceobj=obj) or [])
881
882 raddress = list(set(raddress))
883
8e113d63
RP
884 if not raddress:
885 self.logger.warn('%s: no running addresses'
886 %ifaceobjrunning.name)
887 raddress = []
888 ifaceobjrunning.update_config('address-virtual',
c3175b31 889 '%s %s' %(rhwaddress, ' '.join(raddress)))
007cae35
JF
890
891 macvlans_ipv6_addrgen_list.append((macvlan_ifacename, self.ipcmd.get_ipv6_addrgen_mode(macvlan_ifacename)))
892
893 macvlan_count = len(address_virtuals)
894 if not macvlan_count:
895 return
896 ipv6_addrgen = macvlans_ipv6_addrgen_list[0][1]
897
898 for macvlan_ifname, macvlan_ipv6_addrgen in macvlans_ipv6_addrgen_list:
899 if macvlan_ipv6_addrgen != ipv6_addrgen:
900 # one macvlan has a different ipv6-addrgen configuration
901 # we simply return, ifquery-running will print the macvlan
902 # stanzas with the ipv6-addrgen on/off attribute
903 return
904 ifaceobjrunning.update_config('address-virtual-ipv6-addrgen', 'off' if ipv6_addrgen else 'on')
905
15ef32ea
RP
906
907 _run_ops = {'up' : _up,
908 'down' : _down,
909 'query-checkcurr' : _query_check,
910 'query-running' : _query_running}
911
912 def get_ops(self):
913 """ returns list of ops supported by this module """
914 return self._run_ops.keys()
915
916 def _init_command_handlers(self):
917 if not self.ipcmd:
d486dd0d 918 self.ipcmd = LinkUtils()
15ef32ea 919
42e85fc8
RP
920 def run(self, ifaceobj, operation, query_ifaceobj=None,
921 ifaceobj_getfunc=None, **extra_args):
15ef32ea
RP
922 """ run vlan configuration on the interface object passed as argument
923
924 Args:
925 **ifaceobj** (object): iface object
926
927 **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
928 'query-running'
929 Kwargs:
930 **query_ifaceobj** (object): query check ifaceobject. This is only
931 valid when op is 'query-checkcurr'. It is an object same as
932 ifaceobj, but contains running attribute values and its config
933 status. The modules can use it to return queried running state
934 of interfaces. status is success if the running state is same
935 as user required state in ifaceobj. error otherwise.
936 """
84ca006f
RP
937 if ifaceobj.type == ifaceType.BRIDGE_VLAN:
938 return
15ef32ea
RP
939 op_handler = self._run_ops.get(operation)
940 if not op_handler:
941 return
942 self._init_command_handlers()
943 if operation == 'query-checkcurr':
944 op_handler(self, ifaceobj, query_ifaceobj)
945 else:
42e85fc8 946 op_handler(self, ifaceobj, ifaceobj_getfunc=ifaceobj_getfunc)