]> git.proxmox.com Git - mirror_ifupdown2.git/blame - ifupdown2/addons/addressvirtual.py
addons: addressvirtual: vrrp: fix macvlan name to include vrrp id
[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 96 def get_vrrp_prefix(ifname, family):
3e112a1c 97 return "vrrp%s-%s-" % (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
3e112a1c
JF
573 macvlan_ip4_ifname = "%s%s" % (self.get_vrrp_prefix(ifname, "4"), vrrp_id)
574 macvlan_ip6_ifname = "%s%s" % (self.get_vrrp_prefix(ifname, "6"), vrrp_id)
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 })
bd451a48
JF
604 elif not ip4 and not ifquery:
605 # special check to see if all ipv4 were removed from the vrrp
606 # configuration, if so we need to remove the associated macvlan
607 if self.ipcmd.link_exists(macvlan_ip4_ifname):
608 netlink.link_del(macvlan_ip4_ifname)
5bc963f0
JF
609
610 if ip6 or ifquery:
611 # config_ip6
612 macvlan_ip6_mac = "00:00:5e:00:02:%s" % hex_id
613 user_config_list.append({
614 "ifname": macvlan_ip6_ifname,
615 "hwaddress": macvlan_ip6_mac,
616 "hwaddress_int": self.mac_str_to_int(macvlan_ip6_mac),
e588acb7 617 "mode": "bridge",
5bc963f0
JF
618 "ips": ip6,
619 "id": vrrp_id
620 })
bd451a48
JF
621 elif not ip6 and not ifquery:
622 # special check to see if all ipv6 were removed from the vrrp
623 # configuration, if so we need to remove the associated macvlan
624 if self.ipcmd.link_exists(macvlan_ip6_ifname):
625 netlink.link_del(macvlan_ip6_ifname)
5bc963f0
JF
626
627 return user_config_list
628
629 def translate_addrvirtual_user_config_to_list(self, ifaceobj, address_virtual_list):
630 """
631 # Translate:
632 # address-virtual 00:11:22:33:44:01 2001:0db8::0370:7334/64 11.0.1.1/24 11.0.1.2/24
633 # To:
634 # [
635 # {
636 # "ifname": "macvlan_ifname",
637 # "hwaddress": "macvlan_hwaddress",
638 # "ips": [str(IPNetwork), ]
639 # },
640 # ]
641 """
642 user_config_list = []
643
644 if not address_virtual_list:
645 return user_config_list
646
647 macvlan_prefix = self._get_macvlan_prefix(ifaceobj)
648
649 for index, addr_virtual in enumerate(address_virtual_list):
650 av_attrs = addr_virtual.split()
651
652 if len(av_attrs) < 2:
653 self.log_error("%s: incorrect address-virtual attrs '%s'"
654 % (ifaceobj.name, addr_virtual), ifaceobj,
655 raise_error=False)
656 continue
657
658 mac = av_attrs[0]
659 if mac:
660 mac = mac.lower()
661
662 if not self.check_mac_address(ifaceobj, mac):
663 continue
664
e588acb7
JF
665 config = {
666 "ifname": "%s%d" % (macvlan_prefix, index),
667 "mode": "private"
668 }
5bc963f0
JF
669
670 if mac != "none":
671 config["hwaddress"] = mac
672 config["hwaddress_int"] = self.mac_str_to_int(mac)
673
674 ip_network_obj_list = []
675 for ip in av_attrs[1:]:
676 ip_network_obj_list.append(str(IPNetwork(ip)))
677
678 config["ips"] = ip_network_obj_list
679 user_config_list.append(config)
680
681 return user_config_list
682
683 def process_macvlans_config(self, ifaceobj, attr_name, virtual_addr_list_raw, macvlan_config_list):
684 return self.create_macvlan_and_apply_config(ifaceobj, macvlan_config_list)
15ef32ea 685
42e85fc8 686 def _down(self, ifaceobj, ifaceobj_getfunc=None):
15ef32ea 687 try:
cb46a208
RP
688 self._remove_address_config(ifaceobj,
689 ifaceobj.get_attr_value('address-virtual'))
5bc963f0
JF
690
691 #### VRR
692 hwaddress = []
693 self.ipcmd.batch_start()
68c8d699 694 for vrr_prefix in [self.get_vrrp_prefix(ifaceobj.name, "4"), self.get_vrrp_prefix(ifaceobj.name, "6")]:
5bc963f0
JF
695 for macvlan_ifacename in glob.glob("/sys/class/net/%s*" % vrr_prefix):
696 macvlan_ifacename = os.path.basename(macvlan_ifacename)
697 if not self.ipcmd.link_exists(macvlan_ifacename):
698 continue
699 hwaddress.append(self.ipcmd.link_get_hwaddress(macvlan_ifacename))
700 self.ipcmd.link_delete(os.path.basename(macvlan_ifacename))
701 # XXX: Also delete any fdb addresses. This requires, checking mac address
702 # on individual macvlan interfaces and deleting the vlan from that.
703 self.ipcmd.batch_commit()
704 if any(hwaddress):
705 self._remove_addresses_from_bridge(ifaceobj, hwaddress)
15ef32ea 706 except Exception, e:
5bc963f0
JF
707 import traceback
708 traceback.print_exc()
15ef32ea
RP
709 self.log_warn(str(e))
710
711 def _query_check(self, ifaceobj, ifaceobjcurr):
5bc963f0 712
cb46a208
RP
713 if not self.ipcmd.link_exists(ifaceobj.name):
714 return
007cae35
JF
715
716 user_config_address_virtual_ipv6_addr = ifaceobj.get_attr_value_first('address-virtual-ipv6-addrgen')
717 if user_config_address_virtual_ipv6_addr and user_config_address_virtual_ipv6_addr not in utils._string_values:
718 ifaceobjcurr.update_config_with_status('address-virtual-ipv6-addrgen', user_config_address_virtual_ipv6_addr, 1)
719 user_config_address_virtual_ipv6_addr = None
5bc963f0
JF
720
721 address_virtual_list = ifaceobj.get_attr_value('address-virtual')
722
723 macvlans_running_ipv6_addr_virtual = self.query_check_macvlan_config(
724 ifaceobj,
725 ifaceobjcurr,
726 "address-virtual",
727 user_config_address_virtual_ipv6_addr,
728 virtual_addr_list_raw=address_virtual_list,
729 macvlan_config_list=self.translate_addrvirtual_user_config_to_list(
730 ifaceobj,
731 address_virtual_list
732 )
733 )
734
735 vrr_config_list = ifaceobj.get_attr_value("vrrp")
736
737 macvlans_running_ipv6_addr_vrr = self.query_check_macvlan_config(
738 ifaceobj,
739 ifaceobjcurr,
740 "vrrp",
741 user_config_address_virtual_ipv6_addr,
742 virtual_addr_list_raw=vrr_config_list,
743 macvlan_config_list=self.translate_vrr_user_config_to_list(
744 ifaceobj,
745 vrr_config_list,
746 ifquery=True
747 )
748 )
749
750 macvlans_running_ipv6_addr = macvlans_running_ipv6_addr_virtual + macvlans_running_ipv6_addr_vrr
751 if user_config_address_virtual_ipv6_addr:
752 bool_user_ipv6_addrgen = utils.get_boolean_from_string(user_config_address_virtual_ipv6_addr)
753 for running_ipv6_addrgen in macvlans_running_ipv6_addr:
754 if (not bool_user_ipv6_addrgen) != running_ipv6_addrgen:
755 ifaceobjcurr.update_config_with_status('address-virtual-ipv6-addrgen', user_config_address_virtual_ipv6_addr, 1)
756 return
757 ifaceobjcurr.update_config_with_status('address-virtual-ipv6-addrgen', user_config_address_virtual_ipv6_addr, 0)
758
759 def query_check_macvlan_config(self, ifaceobj, ifaceobjcurr, attr_name, user_config_address_virtual_ipv6_addr, virtual_addr_list_raw, macvlan_config_list):
760 """
761 macvlan_config_list = [
762 {
763 "ifname": "macvlan_ifname",
764 "hwaddress": "macvlan_hwaddress",
765 "ips": [str(IPNetwork), ]
766 },
767 ]
768 """
769 is_vrr = attr_name == "vrrp"
007cae35
JF
770 macvlans_running_ipv6_addr = []
771
5bc963f0
JF
772 if not virtual_addr_list_raw:
773 return macvlans_running_ipv6_addr
774
775 macvlan_config_queue = deque(macvlan_config_list)
776
777 while macvlan_config_queue:
778
779 ip4_config = None
780 ip6_config = None
781
782 config = macvlan_config_queue.popleft()
783
784 if is_vrr:
785 ip4_config = config
786 ip6_config = macvlan_config_queue.popleft()
787
788 macvlan_ifacename = config.get("ifname")
15ef32ea 789
cb46a208 790 if not self.ipcmd.link_exists(macvlan_ifacename):
5bc963f0 791 ifaceobjcurr.update_config_with_status(attr_name, "", 1)
cb46a208 792 continue
007cae35 793
5bc963f0
JF
794 macvlan_hwaddress = config.get("hwaddress")
795 macvlan_hwaddress_int = config.get("hwaddress_int")
796
007cae35
JF
797 if user_config_address_virtual_ipv6_addr:
798 macvlans_running_ipv6_addr.append(self.ipcmd.get_ipv6_addrgen_mode(macvlan_ifacename))
799
cb46a208 800 # Check mac and ip address
5bc963f0
JF
801 rhwaddress = ip4_macvlan_hwaddress = self.ipcmd.link_get_hwaddress(macvlan_ifacename)
802 raddrs = ip4_running_addrs = self.ipcmd.get_running_addrs(
d486dd0d
JF
803 ifname=macvlan_ifacename,
804 details=False,
805 addr_virtual_ifaceobj=ifaceobj
806 )
007cae35 807
5bc963f0
JF
808 if not is_vrr:
809 ips = config.get("ips")
810
811 if not raddrs or not rhwaddress:
812 ifaceobjcurr.update_config_with_status(attr_name, "", 1)
813 continue
814
815 try:
816 if self.mac_str_to_int(rhwaddress) == macvlan_hwaddress_int \
817 and self.ipcmd.compare_user_config_vs_running_state(raddrs, ips) \
818 and self._check_addresses_in_bridge(ifaceobj, macvlan_hwaddress):
819 ifaceobjcurr.update_config_with_status(
820 attr_name,
821 " ".join(virtual_addr_list_raw),
822 0
823 )
824 else:
825 ifaceobjcurr.update_config_with_status(
826 attr_name,
827 '%s %s' % (rhwaddress, ' '.join(raddrs)),
828 1
829 )
830 except:
831 ifaceobjcurr.update_config_with_status(
832 attr_name,
833 '%s %s' % (rhwaddress, ' '.join(raddrs)),
834 1
835 )
836 else:
837 # VRRP
838
839 ok = False
840 # check macvlan ip4 hwaddress (only if ip4 were provided by the user)
841 if not ip4_config.get("ips") or ip4_macvlan_hwaddress == ip4_config.get("hwaddress"):
842 ip6_macvlan_ifname = ip6_config.get("ifname")
843 ip6_macvlan_hwaddress = ip6_config.get("hwaddress")
844
845 # check macvlan ip6 hwaddress (only if ip6 were provided by the user)
846 if not ip6_config.get("ips") or self.ipcmd.link_get_hwaddress(ip6_macvlan_ifname) == ip6_macvlan_hwaddress:
847
848 # check all ip4
849 if self.ipcmd.compare_user_config_vs_running_state(
850 ip4_running_addrs,
851 ip4_config.get("ips")
852 ) and self._check_addresses_in_bridge(ifaceobj, ip4_macvlan_hwaddress):
853 ip6_running_addrs = self.ipcmd.get_running_addrs(
854 ifname=ip6_macvlan_ifname,
855 details=False,
856 addr_virtual_ifaceobj=ifaceobj
857 )
858
859 # check all ip6
860 if self.ipcmd.compare_user_config_vs_running_state(
861 ip6_running_addrs,
862 ip6_config.get("ips")
863 ) and self._check_addresses_in_bridge(ifaceobj, ip4_macvlan_hwaddress):
864 ifaceobjcurr.update_config_with_status(
865 attr_name,
866 "%s %s" % (ip4_config.get("id"), " ".join(ip4_config.get("ips") + ip6_config.get("ips"))),
867 0
868 )
869 ok = True
870
871 if not ok:
872 ifaceobjcurr.update_config_with_status(
873 attr_name,
874 "%s %s" % (ip4_config.get("id"), " ".join(ip4_config.get("ips") + ip6_config.get("ips"))),
875 1
876 )
877
878 return macvlans_running_ipv6_addr
15ef32ea 879
42e85fc8 880 def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None):
aaef0a79 881 macvlan_prefix = self._get_macvlan_prefix(ifaceobjrunning)
8e113d63 882 address_virtuals = glob.glob("/sys/class/net/%s*" %macvlan_prefix)
007cae35 883 macvlans_ipv6_addrgen_list = []
8e113d63
RP
884 for av in address_virtuals:
885 macvlan_ifacename = os.path.basename(av)
886 rhwaddress = self.ipcmd.link_get_hwaddress(macvlan_ifacename)
c3175b31
JF
887
888 raddress = []
889 for obj in ifaceobj_getfunc(ifaceobjrunning.name) or []:
890 raddress.extend(self.ipcmd.get_running_addrs(None, macvlan_ifacename, addr_virtual_ifaceobj=obj) or [])
891
892 raddress = list(set(raddress))
893
8e113d63
RP
894 if not raddress:
895 self.logger.warn('%s: no running addresses'
896 %ifaceobjrunning.name)
897 raddress = []
898 ifaceobjrunning.update_config('address-virtual',
c3175b31 899 '%s %s' %(rhwaddress, ' '.join(raddress)))
007cae35
JF
900
901 macvlans_ipv6_addrgen_list.append((macvlan_ifacename, self.ipcmd.get_ipv6_addrgen_mode(macvlan_ifacename)))
902
903 macvlan_count = len(address_virtuals)
904 if not macvlan_count:
905 return
906 ipv6_addrgen = macvlans_ipv6_addrgen_list[0][1]
907
908 for macvlan_ifname, macvlan_ipv6_addrgen in macvlans_ipv6_addrgen_list:
909 if macvlan_ipv6_addrgen != ipv6_addrgen:
910 # one macvlan has a different ipv6-addrgen configuration
911 # we simply return, ifquery-running will print the macvlan
912 # stanzas with the ipv6-addrgen on/off attribute
913 return
914 ifaceobjrunning.update_config('address-virtual-ipv6-addrgen', 'off' if ipv6_addrgen else 'on')
915
15ef32ea
RP
916
917 _run_ops = {'up' : _up,
918 'down' : _down,
919 'query-checkcurr' : _query_check,
920 'query-running' : _query_running}
921
922 def get_ops(self):
923 """ returns list of ops supported by this module """
924 return self._run_ops.keys()
925
926 def _init_command_handlers(self):
927 if not self.ipcmd:
d486dd0d 928 self.ipcmd = LinkUtils()
15ef32ea 929
42e85fc8
RP
930 def run(self, ifaceobj, operation, query_ifaceobj=None,
931 ifaceobj_getfunc=None, **extra_args):
15ef32ea
RP
932 """ run vlan configuration on the interface object passed as argument
933
934 Args:
935 **ifaceobj** (object): iface object
936
937 **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
938 'query-running'
939 Kwargs:
940 **query_ifaceobj** (object): query check ifaceobject. This is only
941 valid when op is 'query-checkcurr'. It is an object same as
942 ifaceobj, but contains running attribute values and its config
943 status. The modules can use it to return queried running state
944 of interfaces. status is success if the running state is same
945 as user required state in ifaceobj. error otherwise.
946 """
84ca006f
RP
947 if ifaceobj.type == ifaceType.BRIDGE_VLAN:
948 return
15ef32ea
RP
949 op_handler = self._run_ops.get(operation)
950 if not op_handler:
951 return
952 self._init_command_handlers()
953 if operation == 'query-checkcurr':
954 op_handler(self, ifaceobj, query_ifaceobj)
955 else:
42e85fc8 956 op_handler(self, ifaceobj, ifaceobj_getfunc=ifaceobj_getfunc)