3 # Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
4 # Author: Roopa Prabhu, roopa@cumulusnetworks.com
12 from collections
import deque
15 from ifupdown2
.lib
.addon
import AddonWithIpBlackList
16 from ifupdown2
.ifupdown
.iface
import *
17 from ifupdown2
.ifupdown
.utils
import utils
19 from ifupdown2
.nlmanager
.nlpacket
import Link
21 from ifupdown2
.ifupdownaddons
.modulebase
import moduleBase
23 import ifupdown2
.nlmanager
.ipnetwork
as ipnetwork
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
34 from nlmanager
.nlpacket
import Link
36 from ifupdownaddons
.modulebase
import moduleBase
38 import nlmanager
.ipnetwork
as ipnetwork
40 import ifupdown
.statemanager
as statemanager
41 import ifupdown
.policymanager
as policymanager
42 import ifupdown
.ifupdownflags
as ifupdownflags
43 import ifupdown
.ifupdownconfig
as ifupdownconfig
46 class addressvirtual(AddonWithIpBlackList
, moduleBase
):
47 """ ifupdown2 addon module to configure virtual addresses """
50 "mhelp": "address module configures virtual addresses for interfaces. "
51 "It creates a macvlan interface for every mac ip address-virtual line",
54 "help": "bridge router virtual mac and ips",
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"]
59 "address-virtual-ipv6-addrgen": {
60 "help": "enable disable ipv6 link addrgenmode",
61 "validvals": ["on", "off"],
64 "address-virtual-ipv6-addrgen on",
65 "address-virtual-ipv6-addrgen off"
69 "help": "VRRP support",
72 "vrrp 1 10.0.0.15/24 2001:0db8::0370:7334/64",
73 "vrrp 42 10.0.0.42/24"
79 DEFAULT_IP_METRIC
= 1024
80 ADDR_METRIC_SUPPORT
= None
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'
94 self
.address_virtual_ipv6_addrgen_value_dict
= {'on': 0, 'yes': 0, '0': 0, 'off': 1, 'no': 1, '1': 1}
96 if addressvirtual
.ADDR_METRIC_SUPPORT
is None:
98 cmd
= [utils
.ip_cmd
, 'addr', 'help']
99 self
.logger
.info('executing %s addr help' % utils
.ip_cmd
)
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'))
106 addressvirtual
.ADDR_METRIC_SUPPORT
= False
107 self
.logger
.info('address metric support: KO')
110 def get_addr_metric_support(cls
):
111 return cls
.ADDR_METRIC_SUPPORT
114 def get_default_ip_metric(cls
):
115 return cls
.DEFAULT_IP_METRIC
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
121 def _get_macvlan_prefix(self
, ifaceobj
):
122 return '%s-v' %ifaceobj
.name
[0:13].replace('.', '-')
124 def get_vrrp_prefix(self
, ifname
, family
):
125 return "vrrp%s-%s-" % (family
, self
.cache
.get_ifindex(ifname
))
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
]
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
:
147 self
.iproute2
.bridge_fdb_del(bridgename
, addr
, vlan
)
148 except Exception as e
:
149 self
.logger
.debug("%s: %s" %(ifaceobj
.name
, str(e
)))
151 elif self
.cache
.link_is_bridge(ifaceobj
.name
):
152 for addr
in hwaddress
:
154 self
.iproute2
.bridge_fdb_del(ifaceobj
.name
, addr
)
155 except Exception as e
:
156 self
.logger
.debug("%s: %s" %(ifaceobj
.name
, str(e
)))
159 def _get_bridge_fdbs(self
, bridgename
, vlan
):
160 fdbs
= self
._bridge
_fdb
_query
_cache
.get(bridgename
)
162 fdbs
= self
.iproute2
.bridge_fdb_show_dev(bridgename
)
165 self
._bridge
_fdb
_query
_cache
[bridgename
] = fdbs
166 return fdbs
.get(vlan
)
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
))
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
:
185 def _fix_connected_route(self
, ifaceobj
, vifacename
, addr
):
187 # XXX: Hack to make sure the primary address
188 # is the first in the routing table.
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.
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.
199 self
.logger
.info('%s: checking route entry ...' %ifaceobj
.name
)
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)
209 # we don't support ip6 route fix yet
213 if ifaceobj
.link_privflags
& ifaceLinkPrivFlags
.VRF_SLAVE
:
214 vrf_master
= self
.cache
.get_master(ifaceobj
.name
)
218 dev
= self
.iproute2
.ip_route_get_dev(ip
.with_prefixlen
, vrf_master
=vrf_master
)
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'
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
)))
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. """
236 saved_ifaceobjs
= statemanager
.statemanager_api
.get_ifaceobjs(ifaceobj
.name
)
237 if not saved_ifaceobjs
:
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'):
243 for av
in oldifaceobj
.get_attr_value('address-virtual'):
246 self
.logger
.debug("%s: incorrect old address-virtual attrs '%s'"
247 %(oldifaceobj
.name
, av
))
249 maclist
.append(macip
[0])
252 def get_addressvirtual_ipv6_addrgen_user_conf(self
, ifaceobj
):
253 ipv6_addrgen
= ifaceobj
.get_attr_value_first('address-virtual-ipv6-addrgen')
256 # IFLA_INET6_ADDR_GEN_MODE values:
259 ipv6_addrgen_nl
= self
.address_virtual_ipv6_addrgen_value_dict
.get(ipv6_addrgen
.lower(), None)
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
))
264 return True, ipv6_addrgen_nl
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'),
272 if ipv6_addrgen_nl
is not None:
273 return True, ipv6_addrgen_nl
277 def _remove_running_address_config(self
, ifaceobj
):
278 if not self
.cache
.link_exists(ifaceobj
.name
):
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")
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":
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.
297 self
._remove
_addresses
_from
_bridge
(ifaceobj
, hwaddress
)
299 def _remove_address_config(self
, ifaceobj
, address_virtual_list
=None):
300 if not address_virtual_list
:
301 self
._remove
_running
_address
_config
(ifaceobj
)
304 if not self
.cache
.link_exists(ifaceobj
.name
):
308 macvlan_prefix
= self
._get
_macvlan
_prefix
(ifaceobj
)
309 for av
in address_virtual_list
:
310 av_attrs
= av
.split()
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])
318 self
._remove
_addresses
_from
_bridge
(ifaceobj
, hwaddress
)
320 def check_mac_address(self
, ifaceobj
, mac
):
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
)
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"""
341 if not ifaceobj_getfunc
:
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
)
351 # pick up any existing slaves of a vrf device and
352 # look for their upperdevices and enslave them to the
354 for s
in running_slaves
:
355 sobjs
= ifaceobj_getfunc(s
)
357 (sobjs
[0].link_privflags
& ifaceLinkPrivFlags
.ADDRESS_VIRTUAL_SLAVE
)):
358 # enslave all its upper devices to
360 upperdevs
= self
.sysfs
.link_get_uppers(sobjs
[0].name
)
364 # skip vrf device which
365 # will also show up in the
367 if u
== ifaceobj
.name
:
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
):
380 running_uppers
= self
.sysfs
.link_get_uppers(ifaceobj
.name
)
381 if not running_uppers
:
383 macvlan_prefix
= self
._get
_macvlan
_prefix
(ifaceobj
)
384 if not macvlan_prefix
:
386 for u
in running_uppers
:
389 if u
.startswith(macvlan_prefix
):
390 self
.netlink
.link_set_master(u
, vrfname
)
391 self
.netlink
.link_up(u
)
393 def sync_macvlan_forwarding_state(self
, ifname
, macvlan_ifname
):
396 "/proc/sys/net/ipv4/conf/%s/forwarding" % macvlan_ifname
,
397 self
.read_file_oneline("/proc/sys/net/ipv4/conf/%s/forwarding" % ifname
)
399 except Exception as e
:
400 self
.logger
.info("%s: syncing macvlan forwarding with lower device forwarding state failed: %s" % (ifname
, str(e
)))
402 def create_macvlan_and_apply_config(self
, ifaceobj
, intf_config_list
, vrrp
=False):
407 "ifname": "macvlan_ifname",
408 "hwaddress": "macvlan_hwaddress",
409 "ips": [str(IPNetwork), ]
415 if not intf_config_list
:
416 return hw_address_list
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
422 update_mtu
= lower_iface_mtu
= lower_iface_mtu_str
= None
423 if ifupdownconfig
.config
.get("adjust_logical_dev_mtu", "1") != "0" and ifaceobj
.lowerifaces
and intf_config_list
:
427 lower_iface_mtu
= self
.cache
.get_link_mtu(ifaceobj
.name
)
428 lower_iface_mtu_str
= str(lower_iface_mtu
)
430 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 for intf_config_dict
in intf_config_list
:
434 macvlan_ifname
= intf_config_dict
.get("ifname")
435 macvlan_hwaddr
= intf_config_dict
.get("hwaddress")
436 macvlan_mode
= intf_config_dict
.get("mode")
437 ips
= intf_config_dict
.get("ips")
439 if len(macvlan_ifname
) > 15:
440 self
.logger
.error("%s: macvlan name will exceed the 15 chars limitation - please rename the underlying interface (%s)" % (macvlan_ifname
, ifname
))
441 ifaceobj
.set_status(ifaceStatus
.ERROR
)
446 self
.ip_blacklist_check(ifname
, ip
)
447 ip_network_obj
= ipnetwork
.IPNetwork(ip
)
448 is_ip6 |
= ip_network_obj
.version
== 6
450 if not self
.cache
.link_exists(macvlan_ifname
):
451 # When creating VRRP macvlan with bridge mode, the kernel
452 # return an error: 'Invalid argument' (22)
453 # so for now we should only use the iproute2 API.
455 # self.netlink.link_add_macvlan(ifname, macvlan_ifname)
457 self
.iproute2
.link_add_macvlan(ifname
, macvlan_ifname
, macvlan_mode
)
458 self
.sync_macvlan_forwarding_state(ifname
, macvlan_ifname
)
461 # first thing we need to handle vrf enslavement
462 if ifaceobj
.link_privflags
& ifaceLinkPrivFlags
.VRF_SLAVE
:
463 vrf_ifname
= self
.cache
.get_master(ifaceobj
.name
)
465 self
.iproute2
.link_set_master(macvlan_ifname
, vrf_ifname
)
467 # If we are dealing with a VRRP macvlan we need to set protodown on
468 # and set addrgenmode appropriately. For IPv4, a VRRP user only
469 # needs the VIP (which is explicitly configured) so addrgenmode
470 # should be NONE. For IPv6, a unique link-local address is needed
471 # as the SIP for vrrp6 hellos, so addrgenmode should be RANDOM.
474 v6_ag_mode
= Link
.IN6_ADDR_GEN_MODE_RANDOM
if is_ip6
else Link
.IN6_ADDR_GEN_MODE_NONE
475 self
.iproute2
.link_set_ipv6_addrgen(
480 except Exception as e
:
481 self
.logger
.warning("%s: %s: ip link set dev %s addrgenmode none: "
482 "operation not supported: %s" % (ifname
, macvlan_ifname
, macvlan_ifname
, str(e
)))
485 self
.netlink
.link_set_protodown_on(macvlan_ifname
)
486 except Exception as e
:
487 self
.logger
.warning("%s: %s: ip link set dev %s protodown on: operation not supported: %s" % (ifname
, macvlan_ifname
, macvlan_ifname
, str(e
)))
488 elif user_configured_ipv6_addrgenmode
:
489 self
.iproute2
.link_set_ipv6_addrgen(macvlan_ifname
, ipv6_addrgen_user_value
, link_created
)
492 self
.iproute2
.link_set_address_and_keep_down(
495 keep_down
=ifaceobj
.link_privflags
& ifaceLinkPrivFlags
.KEEP_LINK_DOWN
497 hw_address_list
.append(macvlan_hwaddr
)
499 if self
.addressvirtual_with_route_metric
and self
.get_addr_metric_support():
500 metric
= self
.get_default_ip_metric()
504 self
.iproute2
.add_addresses(
512 if ifaceobj
.link_privflags
& ifaceLinkPrivFlags
.KEEP_LINK_DOWN
:
513 self
.logger
.info("%s: keeping macvlan down - link-down yes on lower device %s" % (macvlan_ifname
, ifname
))
514 self
.netlink
.link_down(macvlan_ifname
)
516 # If link existed before, flap the link
519 if not self
.addressvirtual_with_route_metric
or not self
.get_addr_metric_support():
520 # if the system doesn't support ip addr set METRIC
521 # we need to do manually check the ordering of the ip4 routes
522 self
._fix
_connected
_route
(ifaceobj
, macvlan_ifname
, ips
[0])
528 self
.sysfs
.link_set_mtu(macvlan_ifname
, mtu_str
=lower_iface_mtu_str
, mtu_int
=lower_iface_mtu
)
529 except Exception as e
:
530 self
.logger
.info('%s: failed to set mtu %s: %s' % (macvlan_ifname
, lower_iface_mtu
, e
))
532 # set macvlan device to up in anycase.
533 # since we auto create them here..we are responsible
534 # to bring them up here in the case they were brought down
535 # by some other entity in the system.
536 if not ifaceobj
.link_privflags
& ifaceLinkPrivFlags
.KEEP_LINK_DOWN
:
537 self
.netlink
.link_up(macvlan_ifname
)
540 if not self
.addressvirtual_with_route_metric
or not self
.get_addr_metric_support():
541 # if the system doesn't support ip addr set METRIC
542 # we need to do manually check the ordering of the ip6 routes
543 self
.iproute2
.fix_ipv6_route_metric(ifaceobj
, macvlan_ifname
, ips
)
544 except Exception as e
:
545 self
.logger
.debug('fix_vrf_slave_ipv6_route_metric: failed: %s' % e
)
547 # Disable IPv6 duplicate address detection on VRR interfaces
548 sysctl_prefix
= "net.ipv6.conf.%s" % macvlan_ifname
551 syskey
= "%s.%s" % (sysctl_prefix
, "enhanced_dad")
552 if self
.sysctl_get(syskey
) != "0":
553 self
.sysctl_set(syskey
, "0")
554 except Exception as e
:
555 self
.logger
.info("sysctl failure: operation not supported: %s" % str(e
))
561 syskey
= "%s.%s" % (sysctl_prefix
, key
)
562 if self
.sysctl_get(syskey
) != sysval
:
563 self
.sysctl_set(syskey
, sysval
)
565 self
.iproute2
.batch_commit()
566 return hw_address_list
568 def _up(self
, ifaceobj
, ifaceobj_getfunc
=None):
569 if not ifupdownflags
.flags
.ALL
:
570 self
._fixup
_vrf
_enslavements
(ifaceobj
, ifaceobj_getfunc
)
572 address_virtual_list
= ifaceobj
.get_attr_value('address-virtual')
573 vrr_config_list
= ifaceobj
.get_attr_value("vrrp")
575 if not address_virtual_list
and not vrr_config_list
:
576 # XXX: address virtual is not present. In which case,
577 # delete stale macvlan devices.
578 self
._remove
_running
_address
_config
(ifaceobj
)
581 if ifaceobj
.upperifaces
and not ifaceobj
.link_privflags
& ifaceLinkPrivFlags
.VRF_SLAVE
:
582 self
.log_error("%s: invalid placement of address-virtual/vrrp lines "
583 "(must be configured under an interface "
584 "with no upper interfaces or parent interfaces)"
585 % ifaceobj
.name
, ifaceobj
)
587 if not self
.cache
.link_exists(ifaceobj
.name
):
590 addr_virtual_macs
= self
.create_macvlan_and_apply_config(
592 self
.translate_addrvirtual_user_config_to_list(
598 vrr_macs
= self
.create_macvlan_and_apply_config(
600 self
.translate_vrr_user_config_to_list(
607 hw_address_list
= addr_virtual_macs
+ vrr_macs
609 # check the statemanager for old configs.
610 # We need to remove only the previously configured FDB entries
611 oldmacs
= self
._get
_macs
_from
_old
_config
(ifaceobj
)
612 # get a list of fdbs in old that are not in new config meaning they should
613 # be removed since they are gone from the config
614 removed_macs
= [mac
for mac
in oldmacs
if mac
.lower() not in hw_address_list
]
615 self
._remove
_addresses
_from
_bridge
(ifaceobj
, removed_macs
)
616 # if ifaceobj is a bridge and bridge is a vlan aware bridge
617 # add the vid to the bridge
618 self
._add
_addresses
_to
_bridge
(ifaceobj
, hw_address_list
)
620 def translate_vrr_user_config_to_list(self
, ifaceobj
, vrr_config_list
, ifquery
=False):
622 If (IPv4 addresses provided):
624 else if (IPv6 addresses provided):
628 vrrp 1 2001:0db8::0370:7334/64
631 # vrrp 255 10.0.0.15/24 10.0.0.2/1
635 # "ifname": "macvlan_ifname",
636 # "hwaddress": "macvlan_hwaddress",
637 # "mode": "macvlan_mode",
638 # "ips": [str(IPNetwork), ]
642 ifname
= ifaceobj
.name
643 user_config_list
= []
645 for index
, config
in enumerate(vrr_config_list
or []):
646 vrrp_id
, ip_addrs
= config
.split(" ", 1)
647 hex_id
= '%02x' % int(vrrp_id
)
651 for ip_addr
in ip_addrs
.split():
652 ip_network_obj
= ipnetwork
.IPNetwork(ip_addr
)
653 is_ip6
= ip_network_obj
.version
== 6
656 ip6
.append(ip_network_obj
)
658 ip4
.append(ip_network_obj
)
660 macvlan_ip4_ifname
= "%s%s" % (self
.get_vrrp_prefix(ifname
, "4"), vrrp_id
)
661 macvlan_ip6_ifname
= "%s%s" % (self
.get_vrrp_prefix(ifname
, "6"), vrrp_id
)
664 merged_with_existing_obj
= False
665 macvlan_ip4_mac
= "00:00:5e:00:01:%s" % hex_id
666 macvlan_ip4_mac_int
= utils
.mac_str_to_int(macvlan_ip4_mac
)
667 # if the vrr config is defined in different lines for the same ID
668 # we need to save the ip4 and ip6 in the objects we previously
670 # vrrp 255 10.0.0.15/24 10.0.0.2/15
671 # vrrp 255 fe80::a00:27ff:fe04:42/64
672 for obj
in user_config_list
:
673 if obj
.get("hwaddress_int") == macvlan_ip4_mac_int
:
675 merged_with_existing_obj
= True
677 if not merged_with_existing_obj
:
678 # if ip4 config wasn't merge with an existing object
679 # we need to insert it in our list
680 user_config_list
.append({
681 "ifname": macvlan_ip4_ifname
,
682 "hwaddress": macvlan_ip4_mac
,
683 "hwaddress_int": macvlan_ip4_mac_int
,
688 elif not ip4
and not ifquery
and self
.cache
.link_exists(macvlan_ip4_ifname
):
689 # special check to see if all ipv4 were removed from the vrrp
690 # configuration, if so we need to remove the associated macvlan
691 self
.netlink
.link_del(macvlan_ip4_ifname
)
694 merged_with_existing_obj
= False
695 macvlan_ip6_mac
= "00:00:5e:00:02:%s" % hex_id
696 macvlan_ip6_mac_int
= utils
.mac_str_to_int(macvlan_ip6_mac
)
697 # if the vrr config is defined in different lines for the same ID
698 # we need to save the ip4 and ip6 in the objects we previously
700 # vrrp 255 10.0.0.15/24 10.0.0.2/15
701 # vrrp 255 fe80::a00:27ff:fe04:42/64
703 for obj
in user_config_list
:
704 if obj
.get("hwaddress_int") == macvlan_ip6_mac_int
:
706 merged_with_existing_obj
= True
708 if not merged_with_existing_obj
:
709 # if ip6 config wasn't merge with an existing object
710 # we need to insert it in our list
711 user_config_list
.append({
712 "ifname": macvlan_ip6_ifname
,
713 "hwaddress": macvlan_ip6_mac
,
714 "hwaddress_int": macvlan_ip6_mac_int
,
719 elif not ip6
and not ifquery
and self
.cache
.link_exists(macvlan_ip6_ifname
):
720 # special check to see if all ipv6 were removed from the vrrp
721 # configuration, if so we need to remove the associated macvlan
722 self
.netlink
.link_del(macvlan_ip6_ifname
)
725 # check if vrrp attribute was removed/re-assigned
729 for old_ifaceobj
in statemanager
.statemanager_api
.get_ifaceobjs(ifname
) or []:
730 for vrr_config
in old_ifaceobj
.get_attr_value("vrrp") or []:
732 old_vrr_ids
.add(vrr_config
.split()[0])
738 for config
in user_config_list
:
740 old_vrr_ids
.remove(config
["id"])
744 for id_to_remove
in old_vrr_ids
:
745 macvlan_ip4_ifname
= "%s%s" % (self
.get_vrrp_prefix(ifname
, "4"), id_to_remove
)
746 macvlan_ip6_ifname
= "%s%s" % (self
.get_vrrp_prefix(ifname
, "6"), id_to_remove
)
748 if self
.cache
.link_exists(macvlan_ip4_ifname
):
749 self
.netlink
.link_del(macvlan_ip4_ifname
)
751 if self
.cache
.link_exists(macvlan_ip6_ifname
):
752 self
.netlink
.link_del(macvlan_ip6_ifname
)
754 except Exception as e
:
755 self
.logger
.debug("%s: vrrp: failure while removing unused macvlan(s)" % ifname
)
757 return user_config_list
759 def translate_addrvirtual_user_config_to_list(self
, ifaceobj
, address_virtual_list
):
762 # address-virtual 00:11:22:33:44:01 2001:0db8::0370:7334/64 11.0.1.1/24 11.0.1.2/24
766 # "ifname": "macvlan_ifname",
767 # "hwaddress": "macvlan_hwaddress",
768 # "ips": [str(IPNetwork), ]
772 user_config_list
= []
774 if not address_virtual_list
:
775 return user_config_list
777 macvlan_prefix
= self
._get
_macvlan
_prefix
(ifaceobj
)
779 for index
, addr_virtual
in enumerate(address_virtual_list
):
780 av_attrs
= addr_virtual
.split()
785 if not self
.check_mac_address(ifaceobj
, mac
):
789 "ifname": "%s%d" % (macvlan_prefix
, index
),
794 config
["hwaddress"] = mac
795 config
["hwaddress_int"] = utils
.mac_str_to_int(mac
)
797 ip_network_obj_list
= []
798 for ip
in av_attrs
[1:]:
799 ip_network_obj_list
.append(ipnetwork
.IPNetwork(ip
))
801 config
["ips"] = ip_network_obj_list
802 user_config_list
.append(config
)
804 return user_config_list
806 def _down(self
, ifaceobj
, ifaceobj_getfunc
=None):
808 self
._remove
_address
_config
(ifaceobj
,
809 ifaceobj
.get_attr_value('address-virtual'))
813 for vrr_prefix
in [self
.get_vrrp_prefix(ifaceobj
.name
, "4"), self
.get_vrrp_prefix(ifaceobj
.name
, "6")]:
814 for macvlan_ifacename
in glob
.glob("/sys/class/net/%s*" % vrr_prefix
):
815 macvlan_ifacename
= os
.path
.basename(macvlan_ifacename
)
816 if not self
.cache
.link_exists(macvlan_ifacename
):
818 hwaddress
.append(self
.cache
.get_link_address(macvlan_ifacename
))
819 self
.netlink
.link_del(macvlan_ifacename
)
820 # XXX: Also delete any fdb addresses. This requires, checking mac address
821 # on individual macvlan interfaces and deleting the vlan from that.
823 self
._remove
_addresses
_from
_bridge
(ifaceobj
, hwaddress
)
824 except Exception as e
:
826 traceback
.print_exc()
827 self
.log_warn(str(e
))
829 def _query_check(self
, ifaceobj
, ifaceobjcurr
):
831 if not self
.cache
.link_exists(ifaceobj
.name
):
834 user_config_address_virtual_ipv6_addr
= ifaceobj
.get_attr_value_first('address-virtual-ipv6-addrgen')
835 if user_config_address_virtual_ipv6_addr
and user_config_address_virtual_ipv6_addr
not in utils
._string
_values
:
836 ifaceobjcurr
.update_config_with_status('address-virtual-ipv6-addrgen', user_config_address_virtual_ipv6_addr
, 1)
837 user_config_address_virtual_ipv6_addr
= None
839 address_virtual_list
= ifaceobj
.get_attr_value('address-virtual')
841 macvlans_running_ipv6_addr_virtual
= self
.query_check_macvlan_config(
845 user_config_address_virtual_ipv6_addr
,
846 virtual_addr_list_raw
=address_virtual_list
,
847 macvlan_config_list
=self
.translate_addrvirtual_user_config_to_list(
853 vrr_config_list
= ifaceobj
.get_attr_value("vrrp")
855 macvlans_running_ipv6_addr_vrr
= self
.query_check_macvlan_config(
859 user_config_address_virtual_ipv6_addr
,
860 virtual_addr_list_raw
=vrr_config_list
,
861 macvlan_config_list
=self
.translate_vrr_user_config_to_list(
868 macvlans_running_ipv6_addr
= macvlans_running_ipv6_addr_virtual
+ macvlans_running_ipv6_addr_vrr
869 if user_config_address_virtual_ipv6_addr
:
870 bool_user_ipv6_addrgen
= utils
.get_boolean_from_string(user_config_address_virtual_ipv6_addr
)
871 for running_ipv6_addrgen
in macvlans_running_ipv6_addr
:
872 if (not bool_user_ipv6_addrgen
) != running_ipv6_addrgen
:
873 ifaceobjcurr
.update_config_with_status('address-virtual-ipv6-addrgen', user_config_address_virtual_ipv6_addr
, 1)
875 ifaceobjcurr
.update_config_with_status('address-virtual-ipv6-addrgen', user_config_address_virtual_ipv6_addr
, 0)
878 def compare_user_config_vs_running_state(running_addrs
, user_addrs
):
882 for ip
in user_addrs
or []:
889 for ip
in running_addrs
or []:
890 running_ipobj
.append(ip
)
892 return running_ipobj
== (ip4
+ ip6
)
894 def query_check_macvlan_config(self
, ifaceobj
, ifaceobjcurr
, attr_name
, user_config_address_virtual_ipv6_addr
, virtual_addr_list_raw
, macvlan_config_list
):
896 macvlan_config_list = [
898 "ifname": "macvlan_ifname",
899 "hwaddress": "macvlan_hwaddress",
900 "ips": [str(IPNetwork), ]
904 is_vrr
= attr_name
== "vrrp"
905 macvlans_running_ipv6_addr
= []
907 if not virtual_addr_list_raw
:
908 return macvlans_running_ipv6_addr
910 macvlan_config_queue
= deque(macvlan_config_list
)
912 while macvlan_config_queue
:
917 config
= macvlan_config_queue
.popleft()
921 ip6_config
= macvlan_config_queue
.popleft()
923 macvlan_ifacename
= config
.get("ifname")
925 if not self
.cache
.link_exists(macvlan_ifacename
):
926 ifaceobjcurr
.update_config_with_status(attr_name
, "", 1)
929 macvlan_hwaddress
= config
.get("hwaddress")
930 macvlan_hwaddress_int
= config
.get("hwaddress_int")
932 if user_config_address_virtual_ipv6_addr
:
933 macvlans_running_ipv6_addr
.append(self
.cache
.get_link_ipv6_addrgen_mode(macvlan_ifacename
))
935 # Check mac and ip address
936 rhwaddress
= ip4_macvlan_hwaddress
= self
.cache
.get_link_address(macvlan_ifacename
)
937 raddrs
= ip4_running_addrs
= self
.cache
.get_managed_ip_addresses(
938 ifname
=macvlan_ifacename
,
939 ifaceobj_list
=[ifaceobj
],
940 with_address_virtual
=True
944 ips
= config
.get("ips")
947 ifaceobjcurr
.update_config_with_status(attr_name
, "", 1)
951 if utils
.mac_str_to_int(rhwaddress
) == macvlan_hwaddress_int \
952 and self
.compare_user_config_vs_running_state(raddrs
, ips
) \
953 and self
._check
_addresses
_in
_bridge
(ifaceobj
, macvlan_hwaddress
):
954 ifaceobjcurr
.update_config_with_status(
956 " ".join(virtual_addr_list_raw
),
961 address_virtual_value
= "%s %s" % (rhwaddress
, " ".join(raddrs
))
963 address_virtual_value
= rhwaddress
964 ifaceobjcurr
.update_config_with_status(attr_name
, address_virtual_value
, 1)
965 except Exception as e
:
966 self
.logger
.debug("addressvirtual: %s" % str(e
))
968 address_virtual_value
= "%s %s" % (rhwaddress
, " ".join(raddrs
))
970 address_virtual_value
= rhwaddress
972 ifaceobjcurr
.update_config_with_status(attr_name
, address_virtual_value
, 1)
977 # check macvlan ip4 hwaddress (only if ip4 were provided by the user)
978 if not ip4_config
.get("ips") or ip4_macvlan_hwaddress
== ip4_config
.get("hwaddress"):
979 ip6_macvlan_ifname
= ip6_config
.get("ifname")
980 ip6_macvlan_hwaddress
= ip6_config
.get("hwaddress")
982 # check macvlan ip6 hwaddress (only if ip6 were provided by the user)
983 if not ip6_config
.get("ips") or self
.cache
.get_link_address_raw(ip6_macvlan_ifname
) == ip6_config
.get("hwaddress_int"):
986 if self
.compare_user_config_vs_running_state(
988 ip4_config
.get("ips")
989 ) and self
._check
_addresses
_in
_bridge
(ifaceobj
, ip4_macvlan_hwaddress
):
990 ip6_running_addrs
= self
.cache
.get_managed_ip_addresses(
991 ifname
=ip6_macvlan_ifname
,
992 ifaceobj_list
=[ifaceobj
],
993 with_address_virtual
=True
997 if self
.compare_user_config_vs_running_state(
999 ip6_config
.get("ips")
1000 ) and self
._check
_addresses
_in
_bridge
(ifaceobj
, ip6_macvlan_hwaddress
):
1001 ifaceobjcurr
.update_config_with_status(
1003 "%s %s" % (ip4_config
.get("id"), " ".join(ip4_config
.get("ips") + ip6_config
.get("ips"))),
1009 ifaceobjcurr
.update_config_with_status(
1011 "%s %s" % (ip4_config
.get("id"), " ".join(ip4_config
.get("ips") + ip6_config
.get("ips"))),
1015 return macvlans_running_ipv6_addr
1017 def _query_running(self
, ifaceobjrunning
, ifaceobj_getfunc
=None):
1018 macvlan_prefix
= self
._get
_macvlan
_prefix
(ifaceobjrunning
)
1019 address_virtuals
= glob
.glob("/sys/class/net/%s*" %macvlan_prefix
)
1020 macvlans_ipv6_addrgen_list
= []
1021 for av
in address_virtuals
:
1022 macvlan_ifacename
= os
.path
.basename(av
)
1023 rhwaddress
= self
.cache
.get_link_address(macvlan_ifacename
)
1024 raddress
= self
.cache
.get_managed_ip_addresses(
1025 ifname
=ifaceobjrunning
.name
,
1026 ifaceobj_list
=ifaceobj_getfunc(ifaceobjrunning
.name
) or [],
1027 with_address_virtual
=True
1030 raddress
= list(set(raddress
))
1033 self
.logger
.warning('%s: no running addresses'
1034 %ifaceobjrunning
.name
)
1037 ifaceobjrunning
.update_config('address-virtual', '%s %s' %(rhwaddress
, ' '.join([str(a
) for a
in raddress
])))
1039 macvlans_ipv6_addrgen_list
.append((macvlan_ifacename
, self
.cache
.get_link_ipv6_addrgen_mode(macvlan_ifacename
)))
1041 macvlan_count
= len(address_virtuals
)
1042 if not macvlan_count
:
1044 ipv6_addrgen
= macvlans_ipv6_addrgen_list
[0][1]
1046 for macvlan_ifname
, macvlan_ipv6_addrgen
in macvlans_ipv6_addrgen_list
:
1047 if macvlan_ipv6_addrgen
!= ipv6_addrgen
:
1048 # one macvlan has a different ipv6-addrgen configuration
1049 # we simply return, ifquery-running will print the macvlan
1050 # stanzas with the ipv6-addrgen on/off attribute
1052 ifaceobjrunning
.update_config('address-virtual-ipv6-addrgen', 'off' if ipv6_addrgen
else 'on')
1057 'query-checkcurr': _query_check
,
1058 'query-running': _query_running
1062 """ returns list of ops supported by this module """
1063 return list(self
._run
_ops
.keys())
1066 def run(self
, ifaceobj
, operation
, query_ifaceobj
=None,
1067 ifaceobj_getfunc
=None, **extra_args
):
1068 """ run vlan configuration on the interface object passed as argument
1071 **ifaceobj** (object): iface object
1073 **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
1076 **query_ifaceobj** (object): query check ifaceobject. This is only
1077 valid when op is 'query-checkcurr'. It is an object same as
1078 ifaceobj, but contains running attribute values and its config
1079 status. The modules can use it to return queried running state
1080 of interfaces. status is success if the running state is same
1081 as user required state in ifaceobj. error otherwise.
1083 if ifaceobj
.type == ifaceType
.BRIDGE_VLAN
:
1085 op_handler
= self
._run
_ops
.get(operation
)
1089 if operation
== 'query-checkcurr':
1090 op_handler(self
, ifaceobj
, query_ifaceobj
)
1092 op_handler(self
, ifaceobj
, ifaceobj_getfunc
=ifaceobj_getfunc
)