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