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
, keep_down
=ifaceobj
.link_privflags
& ifaceLinkPrivFlags
.KEEP_LINK_DOWN
)
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 ifaceobj
.link_privflags
& ifaceLinkPrivFlags
.KEEP_LINK_DOWN
:
452 self
.logger
.info("%s: keeping macvlan down - link-down yes on lower device %s" % (macvlan_ifname
, ifname
))
453 netlink
.link_set_updown(macvlan_ifname
, "down")
455 # If link existed before, flap the link
458 if not self
.addressvirtual_with_route_metric
or not self
.ipcmd
.addr_metric_support():
459 # if the system doesn't support ip addr set METRIC
460 # we need to do manually check the ordering of the ip4 routes
461 self
._fix
_connected
_route
(ifaceobj
, macvlan_ifname
, ips
[0])
464 lower_iface_mtu
= self
.ipcmd
.link_get_mtu(ifname
, refresh
=True)
467 if lower_iface_mtu
and lower_iface_mtu
!= self
.ipcmd
.link_get_mtu(macvlan_ifname
, refresh
=True):
469 self
.ipcmd
.link_set_mtu(macvlan_ifname
,
471 except Exception as e
:
472 self
.logger
.info('%s: failed to set mtu %s: %s' %
473 (macvlan_ifname
, lower_iface_mtu
, e
))
475 # set macvlan device to up in anycase.
476 # since we auto create them here..we are responsible
477 # to bring them up here in the case they were brought down
478 # by some other entity in the system.
479 if not ifaceobj
.link_privflags
& ifaceLinkPrivFlags
.KEEP_LINK_DOWN
:
480 netlink
.link_set_updown(macvlan_ifname
, "up")
483 if not self
.addressvirtual_with_route_metric
or not self
.ipcmd
.addr_metric_support():
484 # if the system doesn't support ip addr set METRIC
485 # we need to do manually check the ordering of the ip6 routes
486 self
.ipcmd
.fix_ipv6_route_metric(ifaceobj
, macvlan_ifname
, ips
)
487 except Exception as e
:
488 self
.logger
.debug('fix_vrf_slave_ipv6_route_metric: failed: %s' % e
)
490 # Disable IPv6 duplicate address detection on VRR interfaces
495 syskey
= "net.ipv6.conf.%s.%s" % (macvlan_ifname
, key
)
496 if self
.sysctl_get(syskey
) != sysval
:
497 self
.sysctl_set(syskey
, sysval
)
499 self
.ipcmd
.batch_commit()
500 return hw_address_list
502 def _up(self
, ifaceobj
, ifaceobj_getfunc
=None):
503 if not ifupdownflags
.flags
.ALL
:
504 self
._fixup
_vrf
_enslavements
(ifaceobj
, ifaceobj_getfunc
)
506 address_virtual_list
= ifaceobj
.get_attr_value('address-virtual')
507 vrr_config_list
= ifaceobj
.get_attr_value("vrrp")
509 if not address_virtual_list
and not vrr_config_list
:
510 # XXX: address virtual is not present. In which case,
511 # delete stale macvlan devices.
512 self
._remove
_running
_address
_config
(ifaceobj
)
515 if ifaceobj
.upperifaces
and not ifaceobj
.link_privflags
& ifaceLinkPrivFlags
.VRF_SLAVE
:
516 self
.log_error("%s: invalid placement of address-virtual/vrrp lines "
517 "(must be configured under an interface "
518 "with no upper interfaces or parent interfaces)"
519 % ifaceobj
.name
, ifaceobj
)
521 if not self
.ipcmd
.link_exists(ifaceobj
.name
):
524 addr_virtual_macs
= self
.create_macvlan_and_apply_config(
526 self
.translate_addrvirtual_user_config_to_list(
532 vrr_macs
= self
.create_macvlan_and_apply_config(
534 self
.translate_vrr_user_config_to_list(
541 hw_address_list
= addr_virtual_macs
+ vrr_macs
543 # check the statemanager for old configs.
544 # We need to remove only the previously configured FDB entries
545 oldmacs
= self
._get
_macs
_from
_old
_config
(ifaceobj
)
546 # get a list of fdbs in old that are not in new config meaning they should
547 # be removed since they are gone from the config
548 removed_macs
= [mac
for mac
in oldmacs
if mac
.lower() not in hw_address_list
]
549 self
._remove
_addresses
_from
_bridge
(ifaceobj
, removed_macs
)
550 # if ifaceobj is a bridge and bridge is a vlan aware bridge
551 # add the vid to the bridge
552 self
._add
_addresses
_to
_bridge
(ifaceobj
, hw_address_list
)
554 def translate_vrr_user_config_to_list(self
, ifaceobj
, vrr_config_list
, ifquery
=False):
556 If (IPv4 addresses provided):
558 else if (IPv6 addresses provided):
562 vrrp 1 2001:0db8::0370:7334/64
565 # vrrp 255 10.0.0.15/24 10.0.0.2/1
569 # "ifname": "macvlan_ifname",
570 # "hwaddress": "macvlan_hwaddress",
571 # "mode": "macvlan_mode",
572 # "ips": [str(IPNetwork), ]
576 ifname
= ifaceobj
.name
577 user_config_list
= []
579 for index
, config
in enumerate(vrr_config_list
or []):
580 vrrp_id
, ip_addrs
= config
.split(" ", 1)
581 hex_id
= '%02x' % int(vrrp_id
)
585 for ip_addr
in ip_addrs
.split():
586 ip_network_obj
= IPNetwork(ip_addr
)
587 is_ip6
= isinstance(ip_network_obj
, IPv6Network
)
594 macvlan_ip4_ifname
= "%s%s" % (self
.get_vrrp_prefix(ifname
, "4"), vrrp_id
)
595 macvlan_ip6_ifname
= "%s%s" % (self
.get_vrrp_prefix(ifname
, "6"), vrrp_id
)
598 merged_with_existing_obj
= False
599 macvlan_ip4_mac
= "00:00:5e:00:01:%s" % hex_id
600 macvlan_ip4_mac_int
= self
.ipcmd
.mac_str_to_int(macvlan_ip4_mac
)
601 # if the vrr config is defined in different lines for the same ID
602 # we need to save the ip4 and ip6 in the objects we previously
604 # vrrp 255 10.0.0.15/24 10.0.0.2/15
605 # vrrp 255 fe80::a00:27ff:fe04:42/64
606 for obj
in user_config_list
:
607 if obj
.get("hwaddress_int") == macvlan_ip4_mac_int
:
609 merged_with_existing_obj
= True
611 if not merged_with_existing_obj
:
612 # if ip4 config wasn't merge with an existing object
613 # we need to insert it in our list
614 user_config_list
.append({
615 "ifname": macvlan_ip4_ifname
,
616 "hwaddress": macvlan_ip4_mac
,
617 "hwaddress_int": macvlan_ip4_mac_int
,
622 elif not ip4
and not ifquery
:
623 # special check to see if all ipv4 were removed from the vrrp
624 # configuration, if so we need to remove the associated macvlan
625 if self
.ipcmd
.link_exists(macvlan_ip4_ifname
):
626 netlink
.link_del(macvlan_ip4_ifname
)
629 merged_with_existing_obj
= False
630 macvlan_ip6_mac
= "00:00:5e:00:02:%s" % hex_id
631 macvlan_ip6_mac_int
= self
.ipcmd
.mac_str_to_int(macvlan_ip6_mac
)
632 # if the vrr config is defined in different lines for the same ID
633 # we need to save the ip4 and ip6 in the objects we previously
635 # vrrp 255 10.0.0.15/24 10.0.0.2/15
636 # vrrp 255 fe80::a00:27ff:fe04:42/64
638 for obj
in user_config_list
:
639 if obj
.get("hwaddress_int") == macvlan_ip6_mac_int
:
641 merged_with_existing_obj
= True
643 if not merged_with_existing_obj
:
644 # if ip6 config wasn't merge with an existing object
645 # we need to insert it in our list
646 user_config_list
.append({
647 "ifname": macvlan_ip6_ifname
,
648 "hwaddress": macvlan_ip6_mac
,
649 "hwaddress_int": macvlan_ip6_mac_int
,
654 elif not ip6
and not ifquery
:
655 # special check to see if all ipv6 were removed from the vrrp
656 # configuration, if so we need to remove the associated macvlan
657 if self
.ipcmd
.link_exists(macvlan_ip6_ifname
):
658 netlink
.link_del(macvlan_ip6_ifname
)
661 # check if vrrp attribute was removed/re-assigned
665 for old_ifaceobj
in statemanager
.statemanager_api
.get_ifaceobjs(ifname
) or []:
666 for vrr_config
in old_ifaceobj
.get_attr_value("vrrp") or []:
668 old_vrr_ids
.add(vrr_config
.split()[0])
674 for config
in user_config_list
:
676 old_vrr_ids
.remove(config
["id"])
680 for id_to_remove
in old_vrr_ids
:
681 macvlan_ip4_ifname
= "%s%s" % (self
.get_vrrp_prefix(ifname
, "4"), id_to_remove
)
682 macvlan_ip6_ifname
= "%s%s" % (self
.get_vrrp_prefix(ifname
, "6"), id_to_remove
)
684 if self
.ipcmd
.link_exists(macvlan_ip4_ifname
):
685 netlink
.link_del(macvlan_ip4_ifname
)
687 if self
.ipcmd
.link_exists(macvlan_ip6_ifname
):
688 netlink
.link_del(macvlan_ip6_ifname
)
690 except Exception as e
:
691 self
.logger
.debug("%s: vrrp: failure while removing unused macvlan(s)" % ifname
)
693 return user_config_list
695 def translate_addrvirtual_user_config_to_list(self
, ifaceobj
, address_virtual_list
):
698 # address-virtual 00:11:22:33:44:01 2001:0db8::0370:7334/64 11.0.1.1/24 11.0.1.2/24
702 # "ifname": "macvlan_ifname",
703 # "hwaddress": "macvlan_hwaddress",
704 # "ips": [str(IPNetwork), ]
708 user_config_list
= []
710 if not address_virtual_list
:
711 return user_config_list
713 macvlan_prefix
= self
._get
_macvlan
_prefix
(ifaceobj
)
715 for index
, addr_virtual
in enumerate(address_virtual_list
):
716 av_attrs
= addr_virtual
.split()
718 if len(av_attrs
) < 2:
719 self
.log_error("%s: incorrect address-virtual attrs '%s'"
720 % (ifaceobj
.name
, addr_virtual
), ifaceobj
,
728 if not self
.check_mac_address(ifaceobj
, mac
):
732 "ifname": "%s%d" % (macvlan_prefix
, index
),
737 config
["hwaddress"] = mac
738 config
["hwaddress_int"] = self
.ipcmd
.mac_str_to_int(mac
)
740 ip_network_obj_list
= []
741 for ip
in av_attrs
[1:]:
742 ip_network_obj_list
.append(str(IPNetwork(ip
)))
744 config
["ips"] = ip_network_obj_list
745 user_config_list
.append(config
)
747 return user_config_list
749 def process_macvlans_config(self
, ifaceobj
, attr_name
, virtual_addr_list_raw
, macvlan_config_list
):
750 return self
.create_macvlan_and_apply_config(ifaceobj
, macvlan_config_list
)
752 def _down(self
, ifaceobj
, ifaceobj_getfunc
=None):
754 self
._remove
_address
_config
(ifaceobj
,
755 ifaceobj
.get_attr_value('address-virtual'))
759 self
.ipcmd
.batch_start()
760 for vrr_prefix
in [self
.get_vrrp_prefix(ifaceobj
.name
, "4"), self
.get_vrrp_prefix(ifaceobj
.name
, "6")]:
761 for macvlan_ifacename
in glob
.glob("/sys/class/net/%s*" % vrr_prefix
):
762 macvlan_ifacename
= os
.path
.basename(macvlan_ifacename
)
763 if not self
.ipcmd
.link_exists(macvlan_ifacename
):
765 hwaddress
.append(self
.ipcmd
.link_get_hwaddress(macvlan_ifacename
))
766 self
.ipcmd
.link_delete(os
.path
.basename(macvlan_ifacename
))
767 # XXX: Also delete any fdb addresses. This requires, checking mac address
768 # on individual macvlan interfaces and deleting the vlan from that.
769 self
.ipcmd
.batch_commit()
771 self
._remove
_addresses
_from
_bridge
(ifaceobj
, hwaddress
)
774 traceback
.print_exc()
775 self
.log_warn(str(e
))
777 def _query_check(self
, ifaceobj
, ifaceobjcurr
):
779 if not self
.ipcmd
.link_exists(ifaceobj
.name
):
782 user_config_address_virtual_ipv6_addr
= ifaceobj
.get_attr_value_first('address-virtual-ipv6-addrgen')
783 if user_config_address_virtual_ipv6_addr
and user_config_address_virtual_ipv6_addr
not in utils
._string
_values
:
784 ifaceobjcurr
.update_config_with_status('address-virtual-ipv6-addrgen', user_config_address_virtual_ipv6_addr
, 1)
785 user_config_address_virtual_ipv6_addr
= None
787 address_virtual_list
= ifaceobj
.get_attr_value('address-virtual')
789 macvlans_running_ipv6_addr_virtual
= self
.query_check_macvlan_config(
793 user_config_address_virtual_ipv6_addr
,
794 virtual_addr_list_raw
=address_virtual_list
,
795 macvlan_config_list
=self
.translate_addrvirtual_user_config_to_list(
801 vrr_config_list
= ifaceobj
.get_attr_value("vrrp")
803 macvlans_running_ipv6_addr_vrr
= self
.query_check_macvlan_config(
807 user_config_address_virtual_ipv6_addr
,
808 virtual_addr_list_raw
=vrr_config_list
,
809 macvlan_config_list
=self
.translate_vrr_user_config_to_list(
816 macvlans_running_ipv6_addr
= macvlans_running_ipv6_addr_virtual
+ macvlans_running_ipv6_addr_vrr
817 if user_config_address_virtual_ipv6_addr
:
818 bool_user_ipv6_addrgen
= utils
.get_boolean_from_string(user_config_address_virtual_ipv6_addr
)
819 for running_ipv6_addrgen
in macvlans_running_ipv6_addr
:
820 if (not bool_user_ipv6_addrgen
) != running_ipv6_addrgen
:
821 ifaceobjcurr
.update_config_with_status('address-virtual-ipv6-addrgen', user_config_address_virtual_ipv6_addr
, 1)
823 ifaceobjcurr
.update_config_with_status('address-virtual-ipv6-addrgen', user_config_address_virtual_ipv6_addr
, 0)
825 def query_check_macvlan_config(self
, ifaceobj
, ifaceobjcurr
, attr_name
, user_config_address_virtual_ipv6_addr
, virtual_addr_list_raw
, macvlan_config_list
):
827 macvlan_config_list = [
829 "ifname": "macvlan_ifname",
830 "hwaddress": "macvlan_hwaddress",
831 "ips": [str(IPNetwork), ]
835 is_vrr
= attr_name
== "vrrp"
836 macvlans_running_ipv6_addr
= []
838 if not virtual_addr_list_raw
:
839 return macvlans_running_ipv6_addr
841 macvlan_config_queue
= deque(macvlan_config_list
)
843 while macvlan_config_queue
:
848 config
= macvlan_config_queue
.popleft()
852 ip6_config
= macvlan_config_queue
.popleft()
854 macvlan_ifacename
= config
.get("ifname")
856 if not self
.ipcmd
.link_exists(macvlan_ifacename
):
857 ifaceobjcurr
.update_config_with_status(attr_name
, "", 1)
860 macvlan_hwaddress
= config
.get("hwaddress")
861 macvlan_hwaddress_int
= config
.get("hwaddress_int")
863 if user_config_address_virtual_ipv6_addr
:
864 macvlans_running_ipv6_addr
.append(self
.ipcmd
.get_ipv6_addrgen_mode(macvlan_ifacename
))
866 # Check mac and ip address
867 rhwaddress
= ip4_macvlan_hwaddress
= self
.ipcmd
.link_get_hwaddress(macvlan_ifacename
)
868 raddrs
= ip4_running_addrs
= self
.ipcmd
.get_running_addrs(
869 ifname
=macvlan_ifacename
,
871 addr_virtual_ifaceobj
=ifaceobj
875 ips
= config
.get("ips")
877 if not raddrs
or not rhwaddress
:
878 ifaceobjcurr
.update_config_with_status(attr_name
, "", 1)
882 if self
.ipcmd
.mac_str_to_int(rhwaddress
) == macvlan_hwaddress_int \
883 and self
.ipcmd
.compare_user_config_vs_running_state(raddrs
, ips
) \
884 and self
._check
_addresses
_in
_bridge
(ifaceobj
, macvlan_hwaddress
):
885 ifaceobjcurr
.update_config_with_status(
887 " ".join(virtual_addr_list_raw
),
891 ifaceobjcurr
.update_config_with_status(
893 '%s %s' % (rhwaddress
, ' '.join(raddrs
)),
897 ifaceobjcurr
.update_config_with_status(
899 '%s %s' % (rhwaddress
, ' '.join(raddrs
)),
906 # check macvlan ip4 hwaddress (only if ip4 were provided by the user)
907 if not ip4_config
.get("ips") or ip4_macvlan_hwaddress
== ip4_config
.get("hwaddress"):
908 ip6_macvlan_ifname
= ip6_config
.get("ifname")
909 ip6_macvlan_hwaddress
= ip6_config
.get("hwaddress")
911 # check macvlan ip6 hwaddress (only if ip6 were provided by the user)
912 if not ip6_config
.get("ips") or self
.ipcmd
.link_get_hwaddress(ip6_macvlan_ifname
) == ip6_macvlan_hwaddress
:
915 if self
.ipcmd
.compare_user_config_vs_running_state(
917 ip4_config
.get("ips")
918 ) and self
._check
_addresses
_in
_bridge
(ifaceobj
, ip4_macvlan_hwaddress
):
919 ip6_running_addrs
= self
.ipcmd
.get_running_addrs(
920 ifname
=ip6_macvlan_ifname
,
922 addr_virtual_ifaceobj
=ifaceobj
926 if self
.ipcmd
.compare_user_config_vs_running_state(
928 ip6_config
.get("ips")
929 ) and self
._check
_addresses
_in
_bridge
(ifaceobj
, ip4_macvlan_hwaddress
):
930 ifaceobjcurr
.update_config_with_status(
932 "%s %s" % (ip4_config
.get("id"), " ".join(ip4_config
.get("ips") + ip6_config
.get("ips"))),
938 ifaceobjcurr
.update_config_with_status(
940 "%s %s" % (ip4_config
.get("id"), " ".join(ip4_config
.get("ips") + ip6_config
.get("ips"))),
944 return macvlans_running_ipv6_addr
946 def _query_running(self
, ifaceobjrunning
, ifaceobj_getfunc
=None):
947 macvlan_prefix
= self
._get
_macvlan
_prefix
(ifaceobjrunning
)
948 address_virtuals
= []
949 for av
in linkCache
.links
:
950 if av
.startswith(macvlan_prefix
):
951 address_virtuals
.append(av
)
953 macvlans_ipv6_addrgen_list
= []
954 for av
in address_virtuals
:
955 macvlan_ifacename
= os
.path
.basename(av
)
956 rhwaddress
= self
.ipcmd
.link_get_hwaddress(macvlan_ifacename
)
959 for obj
in ifaceobj_getfunc(ifaceobjrunning
.name
) or []:
960 raddress
.extend(self
.ipcmd
.get_running_addrs(None, macvlan_ifacename
, addr_virtual_ifaceobj
=obj
) or [])
962 raddress
= list(set(raddress
))
965 self
.logger
.warn('%s: no running addresses'
966 %ifaceobjrunning
.name
)
968 ifaceobjrunning
.update_config('address-virtual',
969 '%s %s' %(rhwaddress
, ' '.join(raddress
)))
971 macvlans_ipv6_addrgen_list
.append((macvlan_ifacename
, self
.ipcmd
.get_ipv6_addrgen_mode(macvlan_ifacename
)))
973 macvlan_count
= len(address_virtuals
)
974 if not macvlan_count
:
976 ipv6_addrgen
= macvlans_ipv6_addrgen_list
[0][1]
978 for macvlan_ifname
, macvlan_ipv6_addrgen
in macvlans_ipv6_addrgen_list
:
979 if macvlan_ipv6_addrgen
!= ipv6_addrgen
:
980 # one macvlan has a different ipv6-addrgen configuration
981 # we simply return, ifquery-running will print the macvlan
982 # stanzas with the ipv6-addrgen on/off attribute
984 ifaceobjrunning
.update_config('address-virtual-ipv6-addrgen', 'off' if ipv6_addrgen
else 'on')
987 _run_ops
= {'up' : _up
,
989 'query-checkcurr' : _query_check
,
990 'query-running' : _query_running
}
993 """ returns list of ops supported by this module """
994 return self
._run
_ops
.keys()
996 def _init_command_handlers(self
):
998 self
.ipcmd
= LinkUtils()
1000 def run(self
, ifaceobj
, operation
, query_ifaceobj
=None,
1001 ifaceobj_getfunc
=None, **extra_args
):
1002 """ run vlan configuration on the interface object passed as argument
1005 **ifaceobj** (object): iface object
1007 **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
1010 **query_ifaceobj** (object): query check ifaceobject. This is only
1011 valid when op is 'query-checkcurr'. It is an object same as
1012 ifaceobj, but contains running attribute values and its config
1013 status. The modules can use it to return queried running state
1014 of interfaces. status is success if the running state is same
1015 as user required state in ifaceobj. error otherwise.
1017 if ifaceobj
.type == ifaceType
.BRIDGE_VLAN
:
1019 op_handler
= self
._run
_ops
.get(operation
)
1022 self
._init
_command
_handlers
()
1023 if operation
== 'query-checkcurr':
1024 op_handler(self
, ifaceobj
, query_ifaceobj
)
1026 op_handler(self
, ifaceobj
, ifaceobj_getfunc
=ifaceobj_getfunc
)