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