3 # Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
4 # Author: Roopa Prabhu, roopa@cumulusnetworks.com
10 from collections
import deque
11 from ipaddr
import IPNetwork
, IPv6Network
14 from ifupdown2
.ifupdown
.iface
import *
15 from ifupdown2
.ifupdown
.utils
import utils
16 from ifupdown2
.ifupdown
.netlink
import netlink
18 from ifupdown2
.nlmanager
.nlpacket
import Link
20 from ifupdown2
.ifupdownaddons
.cache
import *
21 from ifupdown2
.ifupdownaddons
.LinkUtils
import LinkUtils
22 from ifupdown2
.ifupdownaddons
.modulebase
import moduleBase
24 import ifupdown2
.ifupdown
.statemanager
as statemanager
25 import ifupdown2
.ifupdown
.policymanager
as policymanager
26 import ifupdown2
.ifupdown
.ifupdownflags
as ifupdownflags
27 import ifupdown2
.ifupdown
.ifupdownconfig
as ifupdownconfig
29 from ifupdown
.iface
import *
30 from ifupdown
.utils
import utils
31 from ifupdown
.netlink
import netlink
33 from nlmanager
.nlpacket
import Link
35 from ifupdownaddons
.cache
import *
36 from ifupdownaddons
.LinkUtils
import LinkUtils
37 from ifupdownaddons
.modulebase
import moduleBase
39 import ifupdown
.statemanager
as statemanager
40 import ifupdown
.policymanager
as policymanager
41 import ifupdown
.ifupdownflags
as ifupdownflags
42 import ifupdown
.ifupdownconfig
as ifupdownconfig
45 class addressvirtual(moduleBase
):
46 """ ifupdown2 addon module to configure virtual addresses """
48 _modinfo
= {'mhelp' : 'address module configures virtual addresses for ' +
49 'interfaces. It creates a macvlan interface for ' +
50 'every mac ip address-virtual line',
53 { 'help' : 'bridge router virtual mac and ips',
55 'validvals' : ['<mac-ip/prefixlen-list>',],
56 'example': ['address-virtual 00:11:22:33:44:01 11.0.1.1/24 11.0.1.2/24']
58 'address-virtual-ipv6-addrgen': {
59 'help': 'enable disable ipv6 link addrgenmode',
60 'validvals': ['on', 'off'],
63 'address-virtual-ipv6-addrgen on',
64 'address-virtual-ipv6-addrgen off'
68 "help": "VRRP support",
71 "vrrp 1 10.0.0.15/24 2001:0db8::0370:7334/64",
72 "vrrp 42 10.0.0.42/24"
78 def __init__(self
, *args
, **kargs
):
79 moduleBase
.__init
__(self
, *args
, **kargs
)
81 self
._bridge
_fdb
_query
_cache
= {}
82 self
.addressvirtual_with_route_metric
= utils
.get_boolean_from_string(
83 policymanager
.policymanager_api
.get_module_globals(
84 module_name
=self
.__class
__.__name
__,
85 attr
='addressvirtual_with_route_metric'
90 self
.address_virtual_ipv6_addrgen_value_dict
= {'on': 0, 'yes': 0, '0': 0, 'off': 1, 'no': 1, '1': 1}
92 def get_dependent_ifacenames(self
, ifaceobj
, ifacenames_all
=None):
93 if ifaceobj
.get_attr_value('address-virtual') or ifaceobj
.get_attr_value("vrrp"):
94 ifaceobj
.link_privflags |
= ifaceLinkPrivFlags
.ADDRESS_VIRTUAL_SLAVE
96 def _get_macvlan_prefix(self
, ifaceobj
):
97 return '%s-v' %ifaceobj
.name
[0:13].replace('.', '-')
100 def get_vrrp_prefix(ifname
, family
):
101 return "vrrp%s-%s-" % (family
, netlink
.get_iface_index(ifname
))
103 def _add_addresses_to_bridge(self
, ifaceobj
, hwaddress
):
104 # XXX: batch the addresses
105 if ifaceobj
.link_kind
& ifaceLinkKind
.VLAN
:
106 bridgename
= ifaceobj
.lowerifaces
[0]
107 vlan
= self
._get
_vlan
_id
(ifaceobj
)
108 if self
.ipcmd
.bridge_is_vlan_aware(bridgename
):
109 [self
.ipcmd
.bridge_fdb_add(bridgename
, addr
,
110 vlan
) for addr
in hwaddress
]
111 elif self
.ipcmd
.is_bridge(ifaceobj
.name
):
112 [self
.ipcmd
.bridge_fdb_add(ifaceobj
.name
, addr
)
113 for addr
in hwaddress
]
115 def _remove_addresses_from_bridge(self
, ifaceobj
, hwaddress
):
116 # XXX: batch the addresses
117 if ifaceobj
.link_kind
& ifaceLinkKind
.VLAN
:
118 bridgename
= ifaceobj
.lowerifaces
[0]
119 vlan
= self
._get
_vlan
_id
(ifaceobj
)
120 if self
.ipcmd
.bridge_is_vlan_aware(bridgename
):
121 for addr
in hwaddress
:
123 self
.ipcmd
.bridge_fdb_del(bridgename
, addr
, vlan
)
125 self
.logger
.debug("%s: %s" %(ifaceobj
.name
, str(e
)))
127 elif self
.ipcmd
.is_bridge(ifaceobj
.name
):
128 for addr
in hwaddress
:
130 self
.ipcmd
.bridge_fdb_del(ifaceobj
.name
, addr
)
132 self
.logger
.debug("%s: %s" %(ifaceobj
.name
, str(e
)))
135 def _get_bridge_fdbs(self
, bridgename
, vlan
):
136 fdbs
= self
._bridge
_fdb
_query
_cache
.get(bridgename
)
138 fdbs
= self
.ipcmd
.bridge_fdb_show_dev(bridgename
)
141 self
._bridge
_fdb
_query
_cache
[bridgename
] = fdbs
142 return fdbs
.get(vlan
)
144 def _check_addresses_in_bridge(self
, ifaceobj
, hwaddress
):
145 """ If the device is a bridge, make sure the addresses
146 are in the bridge """
147 if ifaceobj
.link_kind
& ifaceLinkKind
.VLAN
:
148 bridgename
= ifaceobj
.lowerifaces
[0]
149 vlan
= self
._get
_vlan
_id
(ifaceobj
)
150 if self
.ipcmd
.bridge_is_vlan_aware(bridgename
):
151 fdb_addrs
= self
._get
_bridge
_fdbs
(bridgename
, str(vlan
))
154 hwaddress_int
= self
.ipcmd
.mac_str_to_int(hwaddress
)
155 for mac
in fdb_addrs
:
156 if self
.ipcmd
.mac_str_to_int(mac
) == hwaddress_int
:
161 def _fix_connected_route(self
, ifaceobj
, vifacename
, addr
):
163 # XXX: Hack to make sure the primary address
164 # is the first in the routing table.
166 # We use `ip route get` on the vrr network to see which
167 # device the kernel returns. if it is the mac vlan device,
168 # flap the macvlan device to adjust the routing table entry.
170 # flapping the macvlan device makes sure the macvlan
171 # connected route goes through delete + add, hence adjusting
172 # the order in the routing table.
175 self
.logger
.info('%s: checking route entry ...' %ifaceobj
.name
)
178 # we don't support ip6 route fix yet
179 if type(ip
) == IPv6Network
:
182 route_prefix
= '%s/%d' %(ip
.network
, ip
.prefixlen
)
184 if ifaceobj
.link_privflags
& ifaceLinkPrivFlags
.VRF_SLAVE
:
185 vrf_master
= self
.ipcmd
.link_get_master(ifaceobj
.name
)
189 dev
= self
.ipcmd
.ip_route_get_dev(route_prefix
, vrf_master
=vrf_master
)
191 if dev
and dev
!= ifaceobj
.name
:
192 self
.logger
.info('%s: preferred routing entry ' %ifaceobj
.name
+
193 'seems to be of the macvlan dev %s'
195 ' .. flapping macvlan dev to fix entry.')
196 self
.ipcmd
.link_down(vifacename
)
197 self
.ipcmd
.link_up(vifacename
)
199 self
.logger
.debug('%s: fixing route entry failed (%s)'
200 % (ifaceobj
.name
, str(e
)))
203 def _handle_vrf_slaves(self
, macvlan_ifacename
, ifaceobj
):
204 vrfname
= self
.ipcmd
.link_get_master(ifaceobj
.name
)
206 self
.ipcmd
.link_set(macvlan_ifacename
, 'master', vrfname
)
208 def _get_macs_from_old_config(self
, ifaceobj
=None):
209 """ This method returns a list of the mac addresses
210 in the address-virtual attribute for the bridge. """
212 saved_ifaceobjs
= statemanager
.statemanager_api
.get_ifaceobjs(ifaceobj
.name
)
213 if not saved_ifaceobjs
:
215 # we need the old saved configs from the statemanager
216 for oldifaceobj
in saved_ifaceobjs
:
217 if not oldifaceobj
.get_attr_value('address-virtual'):
219 for av
in oldifaceobj
.get_attr_value('address-virtual'):
222 self
.logger
.debug("%s: incorrect old address-virtual attrs '%s'"
223 %(oldifaceobj
.name
, av
))
225 maclist
.append(macip
[0])
228 def get_addressvirtual_ipv6_addrgen_user_conf(self
, ifaceobj
):
229 ipv6_addrgen
= ifaceobj
.get_attr_value_first('address-virtual-ipv6-addrgen')
232 # IFLA_INET6_ADDR_GEN_MODE values:
235 ipv6_addrgen_nl
= self
.address_virtual_ipv6_addrgen_value_dict
.get(ipv6_addrgen
.lower(), None)
237 if ipv6_addrgen_nl
is None:
238 self
.logger
.warning('%s: invalid value "%s" for attribute address-virtual-ipv6-addrgen' % (ifaceobj
.name
, ipv6_addrgen
))
240 return True, ipv6_addrgen_nl
243 # if user didn't configure ipv6-addrgen, should we reset to default?
244 ipv6_addrgen_nl
= self
.address_virtual_ipv6_addrgen_value_dict
.get(
245 self
.get_attr_default_value('address-virtual-ipv6-addrgen'),
248 if ipv6_addrgen_nl
is not None:
249 return True, ipv6_addrgen_nl
253 def _remove_running_address_config(self
, ifaceobj
):
254 if not self
.ipcmd
.link_exists(ifaceobj
.name
):
257 self
.ipcmd
.batch_start()
258 for macvlan_prefix
in [
259 self
._get
_macvlan
_prefix
(ifaceobj
),
260 self
.get_vrrp_prefix(ifaceobj
.name
, "4"),
261 self
.get_vrrp_prefix(ifaceobj
.name
, "6")
263 for macvlan_ifacename
in glob
.glob("/sys/class/net/%s*" % macvlan_prefix
):
264 macvlan_ifacename
= os
.path
.basename(macvlan_ifacename
)
265 if not self
.ipcmd
.link_exists(macvlan_ifacename
):
267 hwaddress
.append(self
.ipcmd
.link_get_hwaddress(macvlan_ifacename
))
268 self
.ipcmd
.link_delete(os
.path
.basename(macvlan_ifacename
))
269 # XXX: Also delete any fdb addresses. This requires, checking mac address
270 # on individual macvlan interfaces and deleting the vlan from that.
271 self
.ipcmd
.batch_commit()
273 self
._remove
_addresses
_from
_bridge
(ifaceobj
, hwaddress
)
275 def _remove_address_config(self
, ifaceobj
, address_virtual_list
=None):
276 if not address_virtual_list
:
277 self
._remove
_running
_address
_config
(ifaceobj
)
280 if not self
.ipcmd
.link_exists(ifaceobj
.name
):
283 self
.ipcmd
.batch_start()
285 macvlan_prefix
= self
._get
_macvlan
_prefix
(ifaceobj
)
286 for av
in address_virtual_list
:
287 av_attrs
= av
.split()
288 if len(av_attrs
) < 2:
289 self
.log_error("%s: incorrect address-virtual attrs '%s'"
290 %(ifaceobj
.name
, av
), ifaceobj
,
295 # Delete the macvlan device on this device
296 macvlan_ifacename
= '%s%d' %(macvlan_prefix
, av_idx
)
297 self
.ipcmd
.link_delete(os
.path
.basename(macvlan_ifacename
))
298 if av_attrs
[0] != 'None':
299 hwaddress
.append(av_attrs
[0])
301 self
.ipcmd
.batch_commit()
302 self
._remove
_addresses
_from
_bridge
(ifaceobj
, hwaddress
)
304 def check_mac_address(self
, ifaceobj
, mac
):
308 if int(mac
.split(":")[0], 16) & 1 :
309 self
.log_error("%s: Multicast bit is set in the virtual mac address '%s'"
310 % (ifaceobj
.name
, mac
), ifaceobj
=ifaceobj
)
316 def _fixup_vrf_enslavements(self
, ifaceobj
, ifaceobj_getfunc
=None):
317 """ This function fixes up address virtual interfaces
318 (macvlans) on vrf slaves. Since this fixup is an overhead,
319 this must be called only in cases when ifupdown2 is
320 called on the vrf device or its slave and not when
321 ifupdown2 is called for all devices. When all
322 interfaces are brought up, the expectation is that
323 the normal path will fix up a vrf device or its slaves"""
325 if not ifaceobj_getfunc
:
327 if ((ifaceobj
.link_kind
& ifaceLinkKind
.VRF
) and
328 self
.ipcmd
.link_exists(ifaceobj
.name
)):
329 # if I am a vrf device and I have slaves
330 # that have address virtual config,
331 # enslave the slaves 'address virtual
332 # interfaces (macvlans)' to myself:
333 running_slaves
= self
.ipcmd
.link_get_lowers(ifaceobj
.name
)
335 # pick up any existing slaves of a vrf device and
336 # look for their upperdevices and enslave them to the
338 for s
in running_slaves
:
339 sobjs
= ifaceobj_getfunc(s
)
341 (sobjs
[0].link_privflags
& ifaceLinkPrivFlags
.ADDRESS_VIRTUAL_SLAVE
)):
342 # enslave all its upper devices to
344 upperdevs
= self
.ipcmd
.link_get_uppers(sobjs
[0].name
)
348 # skip vrf device which
349 # will also show up in the
351 if u
== ifaceobj
.name
:
353 self
.ipcmd
.link_set(u
, 'master', ifaceobj
.name
,
355 elif ((ifaceobj
.link_privflags
& ifaceLinkPrivFlags
.ADDRESS_VIRTUAL_SLAVE
) and
356 (ifaceobj
.link_privflags
& ifaceLinkPrivFlags
.VRF_SLAVE
) and
357 self
.ipcmd
.link_exists(ifaceobj
.name
)):
358 # If I am a vrf slave and I have 'address virtual'
359 # config, make sure my addrress virtual interfaces
360 # (macvlans) are also enslaved to the vrf device
361 vrfname
= ifaceobj
.get_attr_value_first('vrf')
362 if not vrfname
or not self
.ipcmd
.link_exists(vrfname
):
364 running_uppers
= self
.ipcmd
.link_get_uppers(ifaceobj
.name
)
365 if not running_uppers
:
367 macvlan_prefix
= self
._get
_macvlan
_prefix
(ifaceobj
)
368 if not macvlan_prefix
:
370 for u
in running_uppers
:
373 if u
.startswith(macvlan_prefix
):
374 self
.ipcmd
.link_set(u
, 'master', vrfname
,
377 def create_macvlan_and_apply_config(self
, ifaceobj
, intf_config_list
, vrrp
=False):
381 "ifname": "macvlan_ifname",
382 "hwaddress": "macvlan_hwaddress",
383 "ips": [str(IPNetwork), ]
387 user_configured_ipv6_addrgenmode
, ipv6_addrgen_user_value
= self
.get_addressvirtual_ipv6_addrgen_user_conf(ifaceobj
)
388 purge_existing
= False if ifupdownflags
.flags
.PERFMODE
else True
390 ifname
= ifaceobj
.name
392 lower_iface_mtu
= update_mtu
= None
393 if ifupdownconfig
.config
.get("adjust_logical_dev_mtu", "1") != "0":
394 if ifaceobj
.lowerifaces
and intf_config_list
:
397 self
.ipcmd
.batch_start()
399 for intf_config_dict
in intf_config_list
:
401 macvlan_ifname
= intf_config_dict
.get("ifname")
402 macvlan_hwaddr
= intf_config_dict
.get("hwaddress")
403 macvlan_mode
= intf_config_dict
.get("mode")
404 ips
= intf_config_dict
.get("ips")
406 if not self
.ipcmd
.link_exists(macvlan_ifname
):
407 self
.ipcmd
.link_add_macvlan(ifname
, macvlan_ifname
, macvlan_mode
)
410 # first thing we need to handle vrf enslavement
411 if ifaceobj
.link_privflags
& ifaceLinkPrivFlags
.VRF_SLAVE
:
412 self
._handle
_vrf
_slaves
(macvlan_ifname
, ifaceobj
)
414 # if we are dealing with a VRRP macvlan we need to set addrgenmode
415 # to RANDOM, and protodown on
418 self
.ipcmd
.ipv6_addrgen(
420 Link
.IN6_ADDR_GEN_MODE_RANDOM
,
423 except Exception as e
:
424 self
.logger
.warning("%s: %s: ip link set dev %s addrgenmode random: "
425 "operation not supported: %s" % (ifname
, macvlan_ifname
, macvlan_ifname
, str(e
)))
428 netlink
.link_set_protodown(macvlan_ifname
, "on")
429 except Exception as e
:
430 self
.logger
.warning("%s: %s: ip link set dev %s protodown on: operation not supported: %s" % (ifname
, macvlan_ifname
, macvlan_ifname
, str(e
)))
431 elif user_configured_ipv6_addrgenmode
:
432 self
.ipcmd
.ipv6_addrgen(macvlan_ifname
, ipv6_addrgen_user_value
, link_created
)
435 self
.ipcmd
.link_set_hwaddress(macvlan_ifname
, macvlan_hwaddr
)
436 hw_address_list
.append(macvlan_hwaddr
)
438 if self
.addressvirtual_with_route_metric
and self
.ipcmd
.addr_metric_support():
439 metric
= self
.ipcmd
.get_default_ip_metric()
443 self
.ipcmd
.addr_add_multiple(
451 # If link existed before, flap the link
454 if not self
.addressvirtual_with_route_metric
or not self
.ipcmd
.addr_metric_support():
455 # if the system doesn't support ip addr set METRIC
456 # we need to do manually check the ordering of the ip4 routes
457 self
._fix
_connected
_route
(ifaceobj
, macvlan_ifname
, ips
[0])
460 lower_iface_mtu
= self
.ipcmd
.link_get_mtu(ifname
, refresh
=True)
463 if lower_iface_mtu
and lower_iface_mtu
!= self
.ipcmd
.link_get_mtu(macvlan_ifname
, refresh
=True):
465 self
.ipcmd
.link_set_mtu(macvlan_ifname
,
467 except Exception as e
:
468 self
.logger
.info('%s: failed to set mtu %s: %s' %
469 (macvlan_ifname
, lower_iface_mtu
, e
))
471 # set macvlan device to up in anycase.
472 # since we auto create them here..we are responsible
473 # to bring them up here in the case they were brought down
474 # by some other entity in the system.
475 netlink
.link_set_updown(macvlan_ifname
, "up")
478 if not self
.addressvirtual_with_route_metric
or not self
.ipcmd
.addr_metric_support():
479 # if the system doesn't support ip addr set METRIC
480 # we need to do manually check the ordering of the ip6 routes
481 self
.ipcmd
.fix_ipv6_route_metric(ifaceobj
, macvlan_ifname
, ips
)
482 except Exception as e
:
483 self
.logger
.debug('fix_vrf_slave_ipv6_route_metric: failed: %s' % e
)
485 # Disable IPv6 duplicate address detection on VRR interfaces
490 syskey
= "net.ipv6.conf.%s.%s" % (macvlan_ifname
, key
)
491 if self
.sysctl_get(syskey
) != sysval
:
492 self
.sysctl_set(syskey
, sysval
)
494 self
.ipcmd
.batch_commit()
495 return hw_address_list
497 def _up(self
, ifaceobj
, ifaceobj_getfunc
=None):
498 if not ifupdownflags
.flags
.ALL
:
499 self
._fixup
_vrf
_enslavements
(ifaceobj
, ifaceobj_getfunc
)
501 address_virtual_list
= ifaceobj
.get_attr_value('address-virtual')
502 vrr_config_list
= ifaceobj
.get_attr_value("vrrp")
504 if not address_virtual_list
and not vrr_config_list
:
505 # XXX: address virtual is not present. In which case,
506 # delete stale macvlan devices.
507 self
._remove
_running
_address
_config
(ifaceobj
)
510 if ifaceobj
.upperifaces
and not ifaceobj
.link_privflags
& ifaceLinkPrivFlags
.VRF_SLAVE
:
511 self
.log_error("%s: invalid placement of address-virtual/vrrp lines "
512 "(must be configured under an interface "
513 "with no upper interfaces or parent interfaces)"
514 % ifaceobj
.name
, ifaceobj
)
516 if not self
.ipcmd
.link_exists(ifaceobj
.name
):
519 addr_virtual_macs
= self
.create_macvlan_and_apply_config(
521 self
.translate_addrvirtual_user_config_to_list(
527 vrr_macs
= self
.create_macvlan_and_apply_config(
529 self
.translate_vrr_user_config_to_list(
536 hw_address_list
= addr_virtual_macs
+ vrr_macs
538 # check the statemanager for old configs.
539 # We need to remove only the previously configured FDB entries
540 oldmacs
= self
._get
_macs
_from
_old
_config
(ifaceobj
)
541 # get a list of fdbs in old that are not in new config meaning they should
542 # be removed since they are gone from the config
543 removed_macs
= [mac
for mac
in oldmacs
if mac
.lower() not in hw_address_list
]
544 self
._remove
_addresses
_from
_bridge
(ifaceobj
, removed_macs
)
545 # if ifaceobj is a bridge and bridge is a vlan aware bridge
546 # add the vid to the bridge
547 self
._add
_addresses
_to
_bridge
(ifaceobj
, hw_address_list
)
549 def translate_vrr_user_config_to_list(self
, ifaceobj
, vrr_config_list
, ifquery
=False):
551 If (IPv4 addresses provided):
553 else if (IPv6 addresses provided):
557 vrrp 1 2001:0db8::0370:7334/64
560 # vrrp 255 10.0.0.15/24 10.0.0.2/1
564 # "ifname": "macvlan_ifname",
565 # "hwaddress": "macvlan_hwaddress",
566 # "mode": "macvlan_mode",
567 # "ips": [str(IPNetwork), ]
571 ifname
= ifaceobj
.name
572 user_config_list
= []
574 for index
, config
in enumerate(vrr_config_list
or []):
575 vrrp_id
, ip_addrs
= config
.split(" ", 1)
576 hex_id
= '%02x' % int(vrrp_id
)
580 for ip_addr
in ip_addrs
.split():
581 ip_network_obj
= IPNetwork(ip_addr
)
582 is_ip6
= isinstance(ip_network_obj
, IPv6Network
)
589 macvlan_ip4_ifname
= "%s%s" % (self
.get_vrrp_prefix(ifname
, "4"), vrrp_id
)
590 macvlan_ip6_ifname
= "%s%s" % (self
.get_vrrp_prefix(ifname
, "6"), vrrp_id
)
593 merged_with_existing_obj
= False
594 macvlan_ip4_mac
= "00:00:5e:00:01:%s" % hex_id
595 macvlan_ip4_mac_int
= self
.ipcmd
.mac_str_to_int(macvlan_ip4_mac
)
596 # if the vrr config is defined in different lines for the same ID
597 # we need to save the ip4 and ip6 in the objects we previously
599 # vrrp 255 10.0.0.15/24 10.0.0.2/15
600 # vrrp 255 fe80::a00:27ff:fe04:42/64
601 for obj
in user_config_list
:
602 if obj
.get("hwaddress_int") == macvlan_ip4_mac_int
:
604 merged_with_existing_obj
= True
606 if not merged_with_existing_obj
:
607 # if ip4 config wasn't merge with an existing object
608 # we need to insert it in our list
609 user_config_list
.append({
610 "ifname": macvlan_ip4_ifname
,
611 "hwaddress": macvlan_ip4_mac
,
612 "hwaddress_int": macvlan_ip4_mac_int
,
617 elif not ip4
and not ifquery
:
618 # special check to see if all ipv4 were removed from the vrrp
619 # configuration, if so we need to remove the associated macvlan
620 if self
.ipcmd
.link_exists(macvlan_ip4_ifname
):
621 netlink
.link_del(macvlan_ip4_ifname
)
624 merged_with_existing_obj
= False
625 macvlan_ip6_mac
= "00:00:5e:00:02:%s" % hex_id
626 macvlan_ip6_mac_int
= self
.ipcmd
.mac_str_to_int(macvlan_ip6_mac
)
627 # if the vrr config is defined in different lines for the same ID
628 # we need to save the ip4 and ip6 in the objects we previously
630 # vrrp 255 10.0.0.15/24 10.0.0.2/15
631 # vrrp 255 fe80::a00:27ff:fe04:42/64
633 for obj
in user_config_list
:
634 if obj
.get("hwaddress_int") == macvlan_ip6_mac_int
:
636 merged_with_existing_obj
= True
638 if not merged_with_existing_obj
:
639 # if ip6 config wasn't merge with an existing object
640 # we need to insert it in our list
641 user_config_list
.append({
642 "ifname": macvlan_ip6_ifname
,
643 "hwaddress": macvlan_ip6_mac
,
644 "hwaddress_int": macvlan_ip6_mac_int
,
649 elif not ip6
and not ifquery
:
650 # special check to see if all ipv6 were removed from the vrrp
651 # configuration, if so we need to remove the associated macvlan
652 if self
.ipcmd
.link_exists(macvlan_ip6_ifname
):
653 netlink
.link_del(macvlan_ip6_ifname
)
656 # check if vrrp attribute was removed/re-assigned
660 for old_ifaceobj
in statemanager
.statemanager_api
.get_ifaceobjs(ifname
) or []:
661 for vrr_config
in old_ifaceobj
.get_attr_value("vrrp") or []:
663 old_vrr_ids
.add(vrr_config
.split()[0])
669 for config
in user_config_list
:
671 old_vrr_ids
.remove(config
["id"])
675 for id_to_remove
in old_vrr_ids
:
676 macvlan_ip4_ifname
= "%s%s" % (self
.get_vrrp_prefix(ifname
, "4"), id_to_remove
)
677 macvlan_ip6_ifname
= "%s%s" % (self
.get_vrrp_prefix(ifname
, "6"), id_to_remove
)
679 if self
.ipcmd
.link_exists(macvlan_ip4_ifname
):
680 netlink
.link_del(macvlan_ip4_ifname
)
682 if self
.ipcmd
.link_exists(macvlan_ip6_ifname
):
683 netlink
.link_del(macvlan_ip6_ifname
)
685 except Exception as e
:
686 self
.logger
.debug("%s: vrrp: failure while removing unused macvlan(s)" % ifname
)
688 return user_config_list
690 def translate_addrvirtual_user_config_to_list(self
, ifaceobj
, address_virtual_list
):
693 # address-virtual 00:11:22:33:44:01 2001:0db8::0370:7334/64 11.0.1.1/24 11.0.1.2/24
697 # "ifname": "macvlan_ifname",
698 # "hwaddress": "macvlan_hwaddress",
699 # "ips": [str(IPNetwork), ]
703 user_config_list
= []
705 if not address_virtual_list
:
706 return user_config_list
708 macvlan_prefix
= self
._get
_macvlan
_prefix
(ifaceobj
)
710 for index
, addr_virtual
in enumerate(address_virtual_list
):
711 av_attrs
= addr_virtual
.split()
713 if len(av_attrs
) < 2:
714 self
.log_error("%s: incorrect address-virtual attrs '%s'"
715 % (ifaceobj
.name
, addr_virtual
), ifaceobj
,
723 if not self
.check_mac_address(ifaceobj
, mac
):
727 "ifname": "%s%d" % (macvlan_prefix
, index
),
732 config
["hwaddress"] = mac
733 config
["hwaddress_int"] = self
.ipcmd
.mac_str_to_int(mac
)
735 ip_network_obj_list
= []
736 for ip
in av_attrs
[1:]:
737 ip_network_obj_list
.append(str(IPNetwork(ip
)))
739 config
["ips"] = ip_network_obj_list
740 user_config_list
.append(config
)
742 return user_config_list
744 def process_macvlans_config(self
, ifaceobj
, attr_name
, virtual_addr_list_raw
, macvlan_config_list
):
745 return self
.create_macvlan_and_apply_config(ifaceobj
, macvlan_config_list
)
747 def _down(self
, ifaceobj
, ifaceobj_getfunc
=None):
749 self
._remove
_address
_config
(ifaceobj
,
750 ifaceobj
.get_attr_value('address-virtual'))
754 self
.ipcmd
.batch_start()
755 for vrr_prefix
in [self
.get_vrrp_prefix(ifaceobj
.name
, "4"), self
.get_vrrp_prefix(ifaceobj
.name
, "6")]:
756 for macvlan_ifacename
in glob
.glob("/sys/class/net/%s*" % vrr_prefix
):
757 macvlan_ifacename
= os
.path
.basename(macvlan_ifacename
)
758 if not self
.ipcmd
.link_exists(macvlan_ifacename
):
760 hwaddress
.append(self
.ipcmd
.link_get_hwaddress(macvlan_ifacename
))
761 self
.ipcmd
.link_delete(os
.path
.basename(macvlan_ifacename
))
762 # XXX: Also delete any fdb addresses. This requires, checking mac address
763 # on individual macvlan interfaces and deleting the vlan from that.
764 self
.ipcmd
.batch_commit()
766 self
._remove
_addresses
_from
_bridge
(ifaceobj
, hwaddress
)
769 traceback
.print_exc()
770 self
.log_warn(str(e
))
772 def _query_check(self
, ifaceobj
, ifaceobjcurr
):
774 if not self
.ipcmd
.link_exists(ifaceobj
.name
):
777 user_config_address_virtual_ipv6_addr
= ifaceobj
.get_attr_value_first('address-virtual-ipv6-addrgen')
778 if user_config_address_virtual_ipv6_addr
and user_config_address_virtual_ipv6_addr
not in utils
._string
_values
:
779 ifaceobjcurr
.update_config_with_status('address-virtual-ipv6-addrgen', user_config_address_virtual_ipv6_addr
, 1)
780 user_config_address_virtual_ipv6_addr
= None
782 address_virtual_list
= ifaceobj
.get_attr_value('address-virtual')
784 macvlans_running_ipv6_addr_virtual
= self
.query_check_macvlan_config(
788 user_config_address_virtual_ipv6_addr
,
789 virtual_addr_list_raw
=address_virtual_list
,
790 macvlan_config_list
=self
.translate_addrvirtual_user_config_to_list(
796 vrr_config_list
= ifaceobj
.get_attr_value("vrrp")
798 macvlans_running_ipv6_addr_vrr
= self
.query_check_macvlan_config(
802 user_config_address_virtual_ipv6_addr
,
803 virtual_addr_list_raw
=vrr_config_list
,
804 macvlan_config_list
=self
.translate_vrr_user_config_to_list(
811 macvlans_running_ipv6_addr
= macvlans_running_ipv6_addr_virtual
+ macvlans_running_ipv6_addr_vrr
812 if user_config_address_virtual_ipv6_addr
:
813 bool_user_ipv6_addrgen
= utils
.get_boolean_from_string(user_config_address_virtual_ipv6_addr
)
814 for running_ipv6_addrgen
in macvlans_running_ipv6_addr
:
815 if (not bool_user_ipv6_addrgen
) != running_ipv6_addrgen
:
816 ifaceobjcurr
.update_config_with_status('address-virtual-ipv6-addrgen', user_config_address_virtual_ipv6_addr
, 1)
818 ifaceobjcurr
.update_config_with_status('address-virtual-ipv6-addrgen', user_config_address_virtual_ipv6_addr
, 0)
820 def query_check_macvlan_config(self
, ifaceobj
, ifaceobjcurr
, attr_name
, user_config_address_virtual_ipv6_addr
, virtual_addr_list_raw
, macvlan_config_list
):
822 macvlan_config_list = [
824 "ifname": "macvlan_ifname",
825 "hwaddress": "macvlan_hwaddress",
826 "ips": [str(IPNetwork), ]
830 is_vrr
= attr_name
== "vrrp"
831 macvlans_running_ipv6_addr
= []
833 if not virtual_addr_list_raw
:
834 return macvlans_running_ipv6_addr
836 macvlan_config_queue
= deque(macvlan_config_list
)
838 while macvlan_config_queue
:
843 config
= macvlan_config_queue
.popleft()
847 ip6_config
= macvlan_config_queue
.popleft()
849 macvlan_ifacename
= config
.get("ifname")
851 if not self
.ipcmd
.link_exists(macvlan_ifacename
):
852 ifaceobjcurr
.update_config_with_status(attr_name
, "", 1)
855 macvlan_hwaddress
= config
.get("hwaddress")
856 macvlan_hwaddress_int
= config
.get("hwaddress_int")
858 if user_config_address_virtual_ipv6_addr
:
859 macvlans_running_ipv6_addr
.append(self
.ipcmd
.get_ipv6_addrgen_mode(macvlan_ifacename
))
861 # Check mac and ip address
862 rhwaddress
= ip4_macvlan_hwaddress
= self
.ipcmd
.link_get_hwaddress(macvlan_ifacename
)
863 raddrs
= ip4_running_addrs
= self
.ipcmd
.get_running_addrs(
864 ifname
=macvlan_ifacename
,
866 addr_virtual_ifaceobj
=ifaceobj
870 ips
= config
.get("ips")
872 if not raddrs
or not rhwaddress
:
873 ifaceobjcurr
.update_config_with_status(attr_name
, "", 1)
877 if self
.ipcmd
.mac_str_to_int(rhwaddress
) == macvlan_hwaddress_int \
878 and self
.ipcmd
.compare_user_config_vs_running_state(raddrs
, ips
) \
879 and self
._check
_addresses
_in
_bridge
(ifaceobj
, macvlan_hwaddress
):
880 ifaceobjcurr
.update_config_with_status(
882 " ".join(virtual_addr_list_raw
),
886 ifaceobjcurr
.update_config_with_status(
888 '%s %s' % (rhwaddress
, ' '.join(raddrs
)),
892 ifaceobjcurr
.update_config_with_status(
894 '%s %s' % (rhwaddress
, ' '.join(raddrs
)),
901 # check macvlan ip4 hwaddress (only if ip4 were provided by the user)
902 if not ip4_config
.get("ips") or ip4_macvlan_hwaddress
== ip4_config
.get("hwaddress"):
903 ip6_macvlan_ifname
= ip6_config
.get("ifname")
904 ip6_macvlan_hwaddress
= ip6_config
.get("hwaddress")
906 # check macvlan ip6 hwaddress (only if ip6 were provided by the user)
907 if not ip6_config
.get("ips") or self
.ipcmd
.link_get_hwaddress(ip6_macvlan_ifname
) == ip6_macvlan_hwaddress
:
910 if self
.ipcmd
.compare_user_config_vs_running_state(
912 ip4_config
.get("ips")
913 ) and self
._check
_addresses
_in
_bridge
(ifaceobj
, ip4_macvlan_hwaddress
):
914 ip6_running_addrs
= self
.ipcmd
.get_running_addrs(
915 ifname
=ip6_macvlan_ifname
,
917 addr_virtual_ifaceobj
=ifaceobj
921 if self
.ipcmd
.compare_user_config_vs_running_state(
923 ip6_config
.get("ips")
924 ) and self
._check
_addresses
_in
_bridge
(ifaceobj
, ip4_macvlan_hwaddress
):
925 ifaceobjcurr
.update_config_with_status(
927 "%s %s" % (ip4_config
.get("id"), " ".join(ip4_config
.get("ips") + ip6_config
.get("ips"))),
933 ifaceobjcurr
.update_config_with_status(
935 "%s %s" % (ip4_config
.get("id"), " ".join(ip4_config
.get("ips") + ip6_config
.get("ips"))),
939 return macvlans_running_ipv6_addr
941 def _query_running(self
, ifaceobjrunning
, ifaceobj_getfunc
=None):
942 macvlan_prefix
= self
._get
_macvlan
_prefix
(ifaceobjrunning
)
943 address_virtuals
= []
944 for av
in linkCache
.links
:
945 if av
.startswith(macvlan_prefix
):
946 address_virtuals
.append(av
)
948 macvlans_ipv6_addrgen_list
= []
949 for av
in address_virtuals
:
950 macvlan_ifacename
= os
.path
.basename(av
)
951 rhwaddress
= self
.ipcmd
.link_get_hwaddress(macvlan_ifacename
)
954 for obj
in ifaceobj_getfunc(ifaceobjrunning
.name
) or []:
955 raddress
.extend(self
.ipcmd
.get_running_addrs(None, macvlan_ifacename
, addr_virtual_ifaceobj
=obj
) or [])
957 raddress
= list(set(raddress
))
960 self
.logger
.warn('%s: no running addresses'
961 %ifaceobjrunning
.name
)
963 ifaceobjrunning
.update_config('address-virtual',
964 '%s %s' %(rhwaddress
, ' '.join(raddress
)))
966 macvlans_ipv6_addrgen_list
.append((macvlan_ifacename
, self
.ipcmd
.get_ipv6_addrgen_mode(macvlan_ifacename
)))
968 macvlan_count
= len(address_virtuals
)
969 if not macvlan_count
:
971 ipv6_addrgen
= macvlans_ipv6_addrgen_list
[0][1]
973 for macvlan_ifname
, macvlan_ipv6_addrgen
in macvlans_ipv6_addrgen_list
:
974 if macvlan_ipv6_addrgen
!= ipv6_addrgen
:
975 # one macvlan has a different ipv6-addrgen configuration
976 # we simply return, ifquery-running will print the macvlan
977 # stanzas with the ipv6-addrgen on/off attribute
979 ifaceobjrunning
.update_config('address-virtual-ipv6-addrgen', 'off' if ipv6_addrgen
else 'on')
982 _run_ops
= {'up' : _up
,
984 'query-checkcurr' : _query_check
,
985 'query-running' : _query_running
}
988 """ returns list of ops supported by this module """
989 return self
._run
_ops
.keys()
991 def _init_command_handlers(self
):
993 self
.ipcmd
= LinkUtils()
995 def run(self
, ifaceobj
, operation
, query_ifaceobj
=None,
996 ifaceobj_getfunc
=None, **extra_args
):
997 """ run vlan configuration on the interface object passed as argument
1000 **ifaceobj** (object): iface object
1002 **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
1005 **query_ifaceobj** (object): query check ifaceobject. This is only
1006 valid when op is 'query-checkcurr'. It is an object same as
1007 ifaceobj, but contains running attribute values and its config
1008 status. The modules can use it to return queried running state
1009 of interfaces. status is success if the running state is same
1010 as user required state in ifaceobj. error otherwise.
1012 if ifaceobj
.type == ifaceType
.BRIDGE_VLAN
:
1014 op_handler
= self
._run
_ops
.get(operation
)
1017 self
._init
_command
_handlers
()
1018 if operation
== 'query-checkcurr':
1019 op_handler(self
, ifaceobj
, query_ifaceobj
)
1021 op_handler(self
, ifaceobj
, ifaceobj_getfunc
=ifaceobj_getfunc
)