3 # Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
4 # Author: Roopa Prabhu, roopa@cumulusnetworks.com
10 from string
import maketrans
11 from collections
import deque
12 from ipaddr
import IPNetwork
, IPv6Network
15 from ifupdown2
.ifupdown
.iface
import *
16 from ifupdown2
.ifupdown
.utils
import utils
17 from ifupdown2
.ifupdown
.netlink
import netlink
19 from ifupdown2
.nlmanager
.nlpacket
import Link
21 from ifupdown2
.ifupdownaddons
.cache
import *
22 from ifupdown2
.ifupdownaddons
.LinkUtils
import LinkUtils
23 from ifupdown2
.ifupdownaddons
.modulebase
import moduleBase
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
30 from ifupdown
.iface
import *
31 from ifupdown
.utils
import utils
32 from ifupdown
.netlink
import netlink
34 from nlmanager
.nlpacket
import Link
36 from ifupdownaddons
.cache
import *
37 from ifupdownaddons
.LinkUtils
import LinkUtils
38 from ifupdownaddons
.modulebase
import moduleBase
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(moduleBase
):
47 """ ifupdown2 addon module to configure virtual addresses """
49 _modinfo
= {'mhelp' : 'address module configures virtual addresses for ' +
50 'interfaces. It creates a macvlan interface for ' +
51 '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 def __init__(self
, *args
, **kargs
):
80 moduleBase
.__init
__(self
, *args
, **kargs
)
82 self
._bridge
_fdb
_query
_cache
= {}
83 self
.addressvirtual_with_route_metric
= utils
.get_boolean_from_string(
84 policymanager
.policymanager_api
.get_module_globals(
85 module_name
=self
.__class
__.__name
__,
86 attr
='addressvirtual_with_route_metric'
91 self
.address_virtual_ipv6_addrgen_value_dict
= {'on': 0, 'yes': 0, '0': 0, 'off': 1, 'no': 1, '1': 1}
92 self
.mac_translate_tab
= maketrans(":.-,", " ")
94 def get_dependent_ifacenames(self
, ifaceobj
, ifacenames_all
=None):
95 if ifaceobj
.get_attr_value('address-virtual') or ifaceobj
.get_attr_value("vrrp"):
96 ifaceobj
.link_privflags |
= ifaceLinkPrivFlags
.ADDRESS_VIRTUAL_SLAVE
98 def _get_macvlan_prefix(self
, ifaceobj
):
99 return '%s-v' %ifaceobj
.name
[0:13].replace('.', '-')
102 def get_vrrp_prefix(ifname
, family
):
103 return "vrrp%s-%s-" % (family
, netlink
.get_iface_index(ifname
))
105 def _add_addresses_to_bridge(self
, ifaceobj
, hwaddress
):
106 # XXX: batch the addresses
107 if ifaceobj
.link_kind
& ifaceLinkKind
.VLAN
:
108 bridgename
= ifaceobj
.lowerifaces
[0]
109 vlan
= self
._get
_vlan
_id
(ifaceobj
)
110 if self
.ipcmd
.bridge_is_vlan_aware(bridgename
):
111 [self
.ipcmd
.bridge_fdb_add(bridgename
, addr
,
112 vlan
) for addr
in hwaddress
]
113 elif self
.ipcmd
.is_bridge(ifaceobj
.name
):
114 [self
.ipcmd
.bridge_fdb_add(ifaceobj
.name
, addr
)
115 for addr
in hwaddress
]
117 def _remove_addresses_from_bridge(self
, ifaceobj
, hwaddress
):
118 # XXX: batch the addresses
119 if ifaceobj
.link_kind
& ifaceLinkKind
.VLAN
:
120 bridgename
= ifaceobj
.lowerifaces
[0]
121 vlan
= self
._get
_vlan
_id
(ifaceobj
)
122 if self
.ipcmd
.bridge_is_vlan_aware(bridgename
):
123 for addr
in hwaddress
:
125 self
.ipcmd
.bridge_fdb_del(bridgename
, addr
, vlan
)
127 self
.logger
.debug("%s: %s" %(ifaceobj
.name
, str(e
)))
129 elif self
.ipcmd
.is_bridge(ifaceobj
.name
):
130 for addr
in hwaddress
:
132 self
.ipcmd
.bridge_fdb_del(ifaceobj
.name
, addr
)
134 self
.logger
.debug("%s: %s" %(ifaceobj
.name
, str(e
)))
137 def _get_bridge_fdbs(self
, bridgename
, vlan
):
138 fdbs
= self
._bridge
_fdb
_query
_cache
.get(bridgename
)
140 fdbs
= self
.ipcmd
.bridge_fdb_show_dev(bridgename
)
143 self
._bridge
_fdb
_query
_cache
[bridgename
] = fdbs
144 return fdbs
.get(vlan
)
146 def _check_addresses_in_bridge(self
, ifaceobj
, hwaddress
):
147 """ If the device is a bridge, make sure the addresses
148 are in the bridge """
149 if ifaceobj
.link_kind
& ifaceLinkKind
.VLAN
:
150 bridgename
= ifaceobj
.lowerifaces
[0]
151 vlan
= self
._get
_vlan
_id
(ifaceobj
)
152 if self
.ipcmd
.bridge_is_vlan_aware(bridgename
):
153 fdb_addrs
= self
._get
_bridge
_fdbs
(bridgename
, str(vlan
))
156 hwaddress_int
= self
.mac_str_to_int(hwaddress
)
157 for mac
in fdb_addrs
:
158 if self
.mac_str_to_int(mac
) == hwaddress_int
:
163 def _fix_connected_route(self
, ifaceobj
, vifacename
, addr
):
165 # XXX: Hack to make sure the primary address
166 # is the first in the routing table.
168 # We use `ip route get` on the vrr network to see which
169 # device the kernel returns. if it is the mac vlan device,
170 # flap the macvlan device to adjust the routing table entry.
172 # flapping the macvlan device makes sure the macvlan
173 # connected route goes through delete + add, hence adjusting
174 # the order in the routing table.
177 self
.logger
.info('%s: checking route entry ...' %ifaceobj
.name
)
180 # we don't support ip6 route fix yet
181 if type(ip
) == IPv6Network
:
184 route_prefix
= '%s/%d' %(ip
.network
, ip
.prefixlen
)
186 if ifaceobj
.link_privflags
& ifaceLinkPrivFlags
.VRF_SLAVE
:
187 vrf_master
= self
.ipcmd
.link_get_master(ifaceobj
.name
)
191 dev
= self
.ipcmd
.ip_route_get_dev(route_prefix
, vrf_master
=vrf_master
)
193 if dev
and dev
!= ifaceobj
.name
:
194 self
.logger
.info('%s: preferred routing entry ' %ifaceobj
.name
+
195 'seems to be of the macvlan dev %s'
197 ' .. flapping macvlan dev to fix entry.')
198 self
.ipcmd
.link_down(vifacename
)
199 self
.ipcmd
.link_up(vifacename
)
201 self
.logger
.debug('%s: fixing route entry failed (%s)'
202 % (ifaceobj
.name
, str(e
)))
205 def _handle_vrf_slaves(self
, macvlan_ifacename
, ifaceobj
):
206 vrfname
= self
.ipcmd
.link_get_master(ifaceobj
.name
)
208 self
.ipcmd
.link_set(macvlan_ifacename
, 'master', vrfname
)
210 def _get_macs_from_old_config(self
, ifaceobj
=None):
211 """ This method returns a list of the mac addresses
212 in the address-virtual attribute for the bridge. """
214 saved_ifaceobjs
= statemanager
.statemanager_api
.get_ifaceobjs(ifaceobj
.name
)
215 if not saved_ifaceobjs
:
217 # we need the old saved configs from the statemanager
218 for oldifaceobj
in saved_ifaceobjs
:
219 if not oldifaceobj
.get_attr_value('address-virtual'):
221 for av
in oldifaceobj
.get_attr_value('address-virtual'):
224 self
.logger
.debug("%s: incorrect old address-virtual attrs '%s'"
225 %(oldifaceobj
.name
, av
))
227 maclist
.append(macip
[0])
230 def get_addressvirtual_ipv6_addrgen_user_conf(self
, ifaceobj
):
231 ipv6_addrgen
= ifaceobj
.get_attr_value_first('address-virtual-ipv6-addrgen')
234 # IFLA_INET6_ADDR_GEN_MODE values:
237 ipv6_addrgen_nl
= self
.address_virtual_ipv6_addrgen_value_dict
.get(ipv6_addrgen
.lower(), None)
239 if ipv6_addrgen_nl
is None:
240 self
.logger
.warning('%s: invalid value "%s" for attribute address-virtual-ipv6-addrgen' % (ifaceobj
.name
, ipv6_addrgen
))
242 return True, ipv6_addrgen_nl
245 # if user didn't configure ipv6-addrgen, should we reset to default?
246 ipv6_addrgen_nl
= self
.address_virtual_ipv6_addrgen_value_dict
.get(
247 self
.get_attr_default_value('address-virtual-ipv6-addrgen'),
250 if ipv6_addrgen_nl
is not None:
251 return True, ipv6_addrgen_nl
255 def _remove_running_address_config(self
, ifaceobj
):
256 if not self
.ipcmd
.link_exists(ifaceobj
.name
):
259 self
.ipcmd
.batch_start()
260 for macvlan_prefix
in [
261 self
._get
_macvlan
_prefix
(ifaceobj
),
262 self
.get_vrrp_prefix(ifaceobj
.name
, "4"),
263 self
.get_vrrp_prefix(ifaceobj
.name
, "6")
265 for macvlan_ifacename
in glob
.glob("/sys/class/net/%s*" % macvlan_prefix
):
266 macvlan_ifacename
= os
.path
.basename(macvlan_ifacename
)
267 if not self
.ipcmd
.link_exists(macvlan_ifacename
):
269 hwaddress
.append(self
.ipcmd
.link_get_hwaddress(macvlan_ifacename
))
270 self
.ipcmd
.link_delete(os
.path
.basename(macvlan_ifacename
))
271 # XXX: Also delete any fdb addresses. This requires, checking mac address
272 # on individual macvlan interfaces and deleting the vlan from that.
273 self
.ipcmd
.batch_commit()
275 self
._remove
_addresses
_from
_bridge
(ifaceobj
, hwaddress
)
277 def _remove_address_config(self
, ifaceobj
, address_virtual_list
=None):
278 if not address_virtual_list
:
279 self
._remove
_running
_address
_config
(ifaceobj
)
282 if not self
.ipcmd
.link_exists(ifaceobj
.name
):
285 self
.ipcmd
.batch_start()
287 macvlan_prefix
= self
._get
_macvlan
_prefix
(ifaceobj
)
288 for av
in address_virtual_list
:
289 av_attrs
= av
.split()
290 if len(av_attrs
) < 2:
291 self
.log_error("%s: incorrect address-virtual attrs '%s'"
292 %(ifaceobj
.name
, av
), ifaceobj
,
297 # Delete the macvlan device on this device
298 macvlan_ifacename
= '%s%d' %(macvlan_prefix
, av_idx
)
299 self
.ipcmd
.link_delete(os
.path
.basename(macvlan_ifacename
))
300 if av_attrs
[0] != 'None':
301 hwaddress
.append(av_attrs
[0])
303 self
.ipcmd
.batch_commit()
304 self
._remove
_addresses
_from
_bridge
(ifaceobj
, hwaddress
)
306 def check_mac_address(self
, ifaceobj
, mac
):
310 if int(mac
.split(":")[0], 16) & 1 :
311 self
.log_error("%s: Multicast bit is set in the virtual mac address '%s'"
312 % (ifaceobj
.name
, mac
), ifaceobj
=ifaceobj
)
318 def _fixup_vrf_enslavements(self
, ifaceobj
, ifaceobj_getfunc
=None):
319 """ This function fixes up address virtual interfaces
320 (macvlans) on vrf slaves. Since this fixup is an overhead,
321 this must be called only in cases when ifupdown2 is
322 called on the vrf device or its slave and not when
323 ifupdown2 is called for all devices. When all
324 interfaces are brought up, the expectation is that
325 the normal path will fix up a vrf device or its slaves"""
327 if not ifaceobj_getfunc
:
329 if ((ifaceobj
.link_kind
& ifaceLinkKind
.VRF
) and
330 self
.ipcmd
.link_exists(ifaceobj
.name
)):
331 # if I am a vrf device and I have slaves
332 # that have address virtual config,
333 # enslave the slaves 'address virtual
334 # interfaces (macvlans)' to myself:
335 running_slaves
= self
.ipcmd
.link_get_lowers(ifaceobj
.name
)
337 # pick up any existing slaves of a vrf device and
338 # look for their upperdevices and enslave them to the
340 for s
in running_slaves
:
341 sobjs
= ifaceobj_getfunc(s
)
343 (sobjs
[0].link_privflags
& ifaceLinkPrivFlags
.ADDRESS_VIRTUAL_SLAVE
)):
344 # enslave all its upper devices to
346 upperdevs
= self
.ipcmd
.link_get_uppers(sobjs
[0].name
)
350 # skip vrf device which
351 # will also show up in the
353 if u
== ifaceobj
.name
:
355 self
.ipcmd
.link_set(u
, 'master', ifaceobj
.name
,
357 elif ((ifaceobj
.link_privflags
& ifaceLinkPrivFlags
.ADDRESS_VIRTUAL_SLAVE
) and
358 (ifaceobj
.link_privflags
& ifaceLinkPrivFlags
.VRF_SLAVE
) and
359 self
.ipcmd
.link_exists(ifaceobj
.name
)):
360 # If I am a vrf slave and I have 'address virtual'
361 # config, make sure my addrress virtual interfaces
362 # (macvlans) are also enslaved to the vrf device
363 vrfname
= ifaceobj
.get_attr_value_first('vrf')
364 if not vrfname
or not self
.ipcmd
.link_exists(vrfname
):
366 running_uppers
= self
.ipcmd
.link_get_uppers(ifaceobj
.name
)
367 if not running_uppers
:
369 macvlan_prefix
= self
._get
_macvlan
_prefix
(ifaceobj
)
370 if not macvlan_prefix
:
372 for u
in running_uppers
:
375 if u
.startswith(macvlan_prefix
):
376 self
.ipcmd
.link_set(u
, 'master', vrfname
,
379 def mac_str_to_int(self
, mac
):
381 for n
in mac
.translate(self
.mac_translate_tab
).split():
382 mac_int
+= int(n
, 16)
385 def create_macvlan_and_apply_config(self
, ifaceobj
, intf_config_list
, vrrp
=False):
389 "ifname": "macvlan_ifname",
390 "hwaddress": "macvlan_hwaddress",
391 "ips": [str(IPNetwork), ]
395 user_configured_ipv6_addrgenmode
, ipv6_addrgen_user_value
= self
.get_addressvirtual_ipv6_addrgen_user_conf(ifaceobj
)
396 purge_existing
= False if ifupdownflags
.flags
.PERFMODE
else True
398 ifname
= ifaceobj
.name
400 lower_iface_mtu
= update_mtu
= None
401 if ifupdownconfig
.config
.get("adjust_logical_dev_mtu", "1") != "0":
402 if ifaceobj
.lowerifaces
and intf_config_list
:
405 self
.ipcmd
.batch_start()
407 for intf_config_dict
in intf_config_list
:
409 macvlan_ifname
= intf_config_dict
.get("ifname")
410 macvlan_hwaddr
= intf_config_dict
.get("hwaddress")
411 macvlan_mode
= intf_config_dict
.get("mode")
412 ips
= intf_config_dict
.get("ips")
414 if not self
.ipcmd
.link_exists(macvlan_ifname
):
415 self
.ipcmd
.link_add_macvlan(ifname
, macvlan_ifname
, macvlan_mode
)
418 # first thing we need to handle vrf enslavement
419 if ifaceobj
.link_privflags
& ifaceLinkPrivFlags
.VRF_SLAVE
:
420 self
._handle
_vrf
_slaves
(macvlan_ifname
, ifaceobj
)
422 # if we are dealing with a VRRP macvlan we need to set addrgenmode
423 # to RANDOM, and protodown on
426 self
.ipcmd
.ipv6_addrgen(
428 Link
.IN6_ADDR_GEN_MODE_RANDOM
,
431 except Exception as e
:
432 self
.logger
.warning("%s: %s: ip link set dev %s addrgenmode random: "
433 "operation not supported: %s" % (ifname
, macvlan_ifname
, macvlan_ifname
, str(e
)))
436 netlink
.link_set_protodown(macvlan_ifname
, "on")
437 except Exception as e
:
438 self
.logger
.warning("%s: %s: ip link set dev %s protodown on: operation not supported: %s" % (ifname
, macvlan_ifname
, macvlan_ifname
, str(e
)))
439 elif user_configured_ipv6_addrgenmode
:
440 self
.ipcmd
.ipv6_addrgen(macvlan_ifname
, ipv6_addrgen_user_value
, link_created
)
443 self
.ipcmd
.link_set_hwaddress(macvlan_ifname
, macvlan_hwaddr
)
444 hw_address_list
.append(macvlan_hwaddr
)
446 if self
.addressvirtual_with_route_metric
and self
.ipcmd
.addr_metric_support():
447 metric
= self
.ipcmd
.get_default_ip_metric()
451 self
.ipcmd
.addr_add_multiple(
459 # If link existed before, flap the link
462 if not self
.addressvirtual_with_route_metric
or not self
.ipcmd
.addr_metric_support():
463 # if the system doesn't support ip addr set METRIC
464 # we need to do manually check the ordering of the ip4 routes
465 self
._fix
_connected
_route
(ifaceobj
, macvlan_ifname
, ips
[0])
468 lower_iface_mtu
= self
.ipcmd
.link_get_mtu(ifname
, refresh
=True)
471 if lower_iface_mtu
and lower_iface_mtu
!= self
.ipcmd
.link_get_mtu(macvlan_ifname
, refresh
=True):
473 self
.ipcmd
.link_set_mtu(macvlan_ifname
,
475 except Exception as e
:
476 self
.logger
.info('%s: failed to set mtu %s: %s' %
477 (macvlan_ifname
, lower_iface_mtu
, e
))
479 # set macvlan device to up in anycase.
480 # since we auto create them here..we are responsible
481 # to bring them up here in the case they were brought down
482 # by some other entity in the system.
483 netlink
.link_set_updown(macvlan_ifname
, "up")
486 if not self
.addressvirtual_with_route_metric
or not self
.ipcmd
.addr_metric_support():
487 # if the system doesn't support ip addr set METRIC
488 # we need to do manually check the ordering of the ip6 routes
489 self
.ipcmd
.fix_ipv6_route_metric(ifaceobj
, macvlan_ifname
, ips
)
490 except Exception as e
:
491 self
.logger
.debug('fix_vrf_slave_ipv6_route_metric: failed: %s' % e
)
493 # Disable IPv6 duplicate address detection on VRR interfaces
498 syskey
= "net.ipv6.conf.%s.%s" % (macvlan_ifname
, key
)
499 if self
.sysctl_get(syskey
) != sysval
:
500 self
.sysctl_set(syskey
, sysval
)
502 self
.ipcmd
.batch_commit()
503 return hw_address_list
505 def _up(self
, ifaceobj
, ifaceobj_getfunc
=None):
506 if not ifupdownflags
.flags
.ALL
:
507 self
._fixup
_vrf
_enslavements
(ifaceobj
, ifaceobj_getfunc
)
509 address_virtual_list
= ifaceobj
.get_attr_value('address-virtual')
510 vrr_config_list
= ifaceobj
.get_attr_value("vrrp")
512 if not address_virtual_list
and not vrr_config_list
:
513 # XXX: address virtual is not present. In which case,
514 # delete stale macvlan devices.
515 self
._remove
_running
_address
_config
(ifaceobj
)
518 if ifaceobj
.upperifaces
and not ifaceobj
.link_privflags
& ifaceLinkPrivFlags
.VRF_SLAVE
:
519 self
.log_error("%s: invalid placement of address-virtual/vrrp lines "
520 "(must be configured under an interface "
521 "with no upper interfaces or parent interfaces)"
522 % ifaceobj
.name
, ifaceobj
)
524 if not self
.ipcmd
.link_exists(ifaceobj
.name
):
527 addr_virtual_macs
= self
.create_macvlan_and_apply_config(
529 self
.translate_addrvirtual_user_config_to_list(
535 vrr_macs
= self
.create_macvlan_and_apply_config(
537 self
.translate_vrr_user_config_to_list(
544 hw_address_list
= addr_virtual_macs
+ vrr_macs
546 # check the statemanager for old configs.
547 # We need to remove only the previously configured FDB entries
548 oldmacs
= self
._get
_macs
_from
_old
_config
(ifaceobj
)
549 # get a list of fdbs in old that are not in new config meaning they should
550 # be removed since they are gone from the config
551 removed_macs
= [mac
for mac
in oldmacs
if mac
.lower() not in hw_address_list
]
552 self
._remove
_addresses
_from
_bridge
(ifaceobj
, removed_macs
)
553 # if ifaceobj is a bridge and bridge is a vlan aware bridge
554 # add the vid to the bridge
555 self
._add
_addresses
_to
_bridge
(ifaceobj
, hw_address_list
)
557 def translate_vrr_user_config_to_list(self
, ifaceobj
, vrr_config_list
, ifquery
=False):
559 If (IPv4 addresses provided):
561 else if (IPv6 addresses provided):
565 vrrp 1 2001:0db8::0370:7334/64
568 # vrrp 255 10.0.0.15/24 10.0.0.2/1
572 # "ifname": "macvlan_ifname",
573 # "hwaddress": "macvlan_hwaddress",
574 # "mode": "macvlan_mode",
575 # "ips": [str(IPNetwork), ]
579 ifname
= ifaceobj
.name
580 user_config_list
= []
582 for index
, config
in enumerate(vrr_config_list
or []):
583 vrrp_id
, ip_addrs
= config
.split(" ", 1)
584 hex_id
= '%02x' % int(vrrp_id
)
588 for ip_addr
in ip_addrs
.split():
589 ip_network_obj
= IPNetwork(ip_addr
)
590 is_ip6
= isinstance(ip_network_obj
, IPv6Network
)
597 macvlan_ip4_ifname
= "%s%s" % (self
.get_vrrp_prefix(ifname
, "4"), vrrp_id
)
598 macvlan_ip6_ifname
= "%s%s" % (self
.get_vrrp_prefix(ifname
, "6"), vrrp_id
)
601 merged_with_existing_obj
= False
602 macvlan_ip4_mac
= "00:00:5e:00:01:%s" % hex_id
603 macvlan_ip4_mac_int
= self
.mac_str_to_int(macvlan_ip4_mac
)
604 # if the vrr config is defined in different lines for the same ID
605 # we need to save the ip4 and ip6 in the objects we previously
607 # vrrp 255 10.0.0.15/24 10.0.0.2/15
608 # vrrp 255 fe80::a00:27ff:fe04:42/64
609 for obj
in user_config_list
:
610 if obj
.get("hwaddress_int") == macvlan_ip4_mac_int
:
612 merged_with_existing_obj
= True
614 if not merged_with_existing_obj
:
615 # if ip4 config wasn't merge with an existing object
616 # we need to insert it in our list
617 user_config_list
.append({
618 "ifname": macvlan_ip4_ifname
,
619 "hwaddress": macvlan_ip4_mac
,
620 "hwaddress_int": macvlan_ip4_mac_int
,
625 elif not ip4
and not ifquery
:
626 # special check to see if all ipv4 were removed from the vrrp
627 # configuration, if so we need to remove the associated macvlan
628 if self
.ipcmd
.link_exists(macvlan_ip4_ifname
):
629 netlink
.link_del(macvlan_ip4_ifname
)
632 merged_with_existing_obj
= False
633 macvlan_ip6_mac
= "00:00:5e:00:02:%s" % hex_id
634 macvlan_ip6_mac_int
= self
.mac_str_to_int(macvlan_ip6_mac
)
635 # if the vrr config is defined in different lines for the same ID
636 # we need to save the ip4 and ip6 in the objects we previously
638 # vrrp 255 10.0.0.15/24 10.0.0.2/15
639 # vrrp 255 fe80::a00:27ff:fe04:42/64
641 for obj
in user_config_list
:
642 if obj
.get("hwaddress_int") == macvlan_ip6_mac_int
:
644 merged_with_existing_obj
= True
646 if not merged_with_existing_obj
:
647 # if ip6 config wasn't merge with an existing object
648 # we need to insert it in our list
649 user_config_list
.append({
650 "ifname": macvlan_ip6_ifname
,
651 "hwaddress": macvlan_ip6_mac
,
652 "hwaddress_int": macvlan_ip6_mac_int
,
657 elif not ip6
and not ifquery
:
658 # special check to see if all ipv6 were removed from the vrrp
659 # configuration, if so we need to remove the associated macvlan
660 if self
.ipcmd
.link_exists(macvlan_ip6_ifname
):
661 netlink
.link_del(macvlan_ip6_ifname
)
664 # check if vrrp attribute was removed/re-assigned
668 for old_ifaceobj
in statemanager
.statemanager_api
.get_ifaceobjs(ifname
) or []:
669 for vrr_config
in old_ifaceobj
.get_attr_value("vrrp") or []:
671 old_vrr_ids
.add(vrr_config
.split()[0])
677 for config
in user_config_list
:
679 old_vrr_ids
.remove(config
["id"])
683 for id_to_remove
in old_vrr_ids
:
684 macvlan_ip4_ifname
= "%s%s" % (self
.get_vrrp_prefix(ifname
, "4"), id_to_remove
)
685 macvlan_ip6_ifname
= "%s%s" % (self
.get_vrrp_prefix(ifname
, "6"), id_to_remove
)
687 if self
.ipcmd
.link_exists(macvlan_ip4_ifname
):
688 netlink
.link_del(macvlan_ip4_ifname
)
690 if self
.ipcmd
.link_exists(macvlan_ip6_ifname
):
691 netlink
.link_del(macvlan_ip6_ifname
)
693 except Exception as e
:
694 self
.logger
.debug("%s: vrrp: failure while removing unused macvlan(s)" % ifname
)
696 return user_config_list
698 def translate_addrvirtual_user_config_to_list(self
, ifaceobj
, address_virtual_list
):
701 # address-virtual 00:11:22:33:44:01 2001:0db8::0370:7334/64 11.0.1.1/24 11.0.1.2/24
705 # "ifname": "macvlan_ifname",
706 # "hwaddress": "macvlan_hwaddress",
707 # "ips": [str(IPNetwork), ]
711 user_config_list
= []
713 if not address_virtual_list
:
714 return user_config_list
716 macvlan_prefix
= self
._get
_macvlan
_prefix
(ifaceobj
)
718 for index
, addr_virtual
in enumerate(address_virtual_list
):
719 av_attrs
= addr_virtual
.split()
721 if len(av_attrs
) < 2:
722 self
.log_error("%s: incorrect address-virtual attrs '%s'"
723 % (ifaceobj
.name
, addr_virtual
), ifaceobj
,
731 if not self
.check_mac_address(ifaceobj
, mac
):
735 "ifname": "%s%d" % (macvlan_prefix
, index
),
740 config
["hwaddress"] = mac
741 config
["hwaddress_int"] = self
.mac_str_to_int(mac
)
743 ip_network_obj_list
= []
744 for ip
in av_attrs
[1:]:
745 ip_network_obj_list
.append(str(IPNetwork(ip
)))
747 config
["ips"] = ip_network_obj_list
748 user_config_list
.append(config
)
750 return user_config_list
752 def process_macvlans_config(self
, ifaceobj
, attr_name
, virtual_addr_list_raw
, macvlan_config_list
):
753 return self
.create_macvlan_and_apply_config(ifaceobj
, macvlan_config_list
)
755 def _down(self
, ifaceobj
, ifaceobj_getfunc
=None):
757 self
._remove
_address
_config
(ifaceobj
,
758 ifaceobj
.get_attr_value('address-virtual'))
762 self
.ipcmd
.batch_start()
763 for vrr_prefix
in [self
.get_vrrp_prefix(ifaceobj
.name
, "4"), self
.get_vrrp_prefix(ifaceobj
.name
, "6")]:
764 for macvlan_ifacename
in glob
.glob("/sys/class/net/%s*" % vrr_prefix
):
765 macvlan_ifacename
= os
.path
.basename(macvlan_ifacename
)
766 if not self
.ipcmd
.link_exists(macvlan_ifacename
):
768 hwaddress
.append(self
.ipcmd
.link_get_hwaddress(macvlan_ifacename
))
769 self
.ipcmd
.link_delete(os
.path
.basename(macvlan_ifacename
))
770 # XXX: Also delete any fdb addresses. This requires, checking mac address
771 # on individual macvlan interfaces and deleting the vlan from that.
772 self
.ipcmd
.batch_commit()
774 self
._remove
_addresses
_from
_bridge
(ifaceobj
, hwaddress
)
777 traceback
.print_exc()
778 self
.log_warn(str(e
))
780 def _query_check(self
, ifaceobj
, ifaceobjcurr
):
782 if not self
.ipcmd
.link_exists(ifaceobj
.name
):
785 user_config_address_virtual_ipv6_addr
= ifaceobj
.get_attr_value_first('address-virtual-ipv6-addrgen')
786 if user_config_address_virtual_ipv6_addr
and user_config_address_virtual_ipv6_addr
not in utils
._string
_values
:
787 ifaceobjcurr
.update_config_with_status('address-virtual-ipv6-addrgen', user_config_address_virtual_ipv6_addr
, 1)
788 user_config_address_virtual_ipv6_addr
= None
790 address_virtual_list
= ifaceobj
.get_attr_value('address-virtual')
792 macvlans_running_ipv6_addr_virtual
= self
.query_check_macvlan_config(
796 user_config_address_virtual_ipv6_addr
,
797 virtual_addr_list_raw
=address_virtual_list
,
798 macvlan_config_list
=self
.translate_addrvirtual_user_config_to_list(
804 vrr_config_list
= ifaceobj
.get_attr_value("vrrp")
806 macvlans_running_ipv6_addr_vrr
= self
.query_check_macvlan_config(
810 user_config_address_virtual_ipv6_addr
,
811 virtual_addr_list_raw
=vrr_config_list
,
812 macvlan_config_list
=self
.translate_vrr_user_config_to_list(
819 macvlans_running_ipv6_addr
= macvlans_running_ipv6_addr_virtual
+ macvlans_running_ipv6_addr_vrr
820 if user_config_address_virtual_ipv6_addr
:
821 bool_user_ipv6_addrgen
= utils
.get_boolean_from_string(user_config_address_virtual_ipv6_addr
)
822 for running_ipv6_addrgen
in macvlans_running_ipv6_addr
:
823 if (not bool_user_ipv6_addrgen
) != running_ipv6_addrgen
:
824 ifaceobjcurr
.update_config_with_status('address-virtual-ipv6-addrgen', user_config_address_virtual_ipv6_addr
, 1)
826 ifaceobjcurr
.update_config_with_status('address-virtual-ipv6-addrgen', user_config_address_virtual_ipv6_addr
, 0)
828 def query_check_macvlan_config(self
, ifaceobj
, ifaceobjcurr
, attr_name
, user_config_address_virtual_ipv6_addr
, virtual_addr_list_raw
, macvlan_config_list
):
830 macvlan_config_list = [
832 "ifname": "macvlan_ifname",
833 "hwaddress": "macvlan_hwaddress",
834 "ips": [str(IPNetwork), ]
838 is_vrr
= attr_name
== "vrrp"
839 macvlans_running_ipv6_addr
= []
841 if not virtual_addr_list_raw
:
842 return macvlans_running_ipv6_addr
844 macvlan_config_queue
= deque(macvlan_config_list
)
846 while macvlan_config_queue
:
851 config
= macvlan_config_queue
.popleft()
855 ip6_config
= macvlan_config_queue
.popleft()
857 macvlan_ifacename
= config
.get("ifname")
859 if not self
.ipcmd
.link_exists(macvlan_ifacename
):
860 ifaceobjcurr
.update_config_with_status(attr_name
, "", 1)
863 macvlan_hwaddress
= config
.get("hwaddress")
864 macvlan_hwaddress_int
= config
.get("hwaddress_int")
866 if user_config_address_virtual_ipv6_addr
:
867 macvlans_running_ipv6_addr
.append(self
.ipcmd
.get_ipv6_addrgen_mode(macvlan_ifacename
))
869 # Check mac and ip address
870 rhwaddress
= ip4_macvlan_hwaddress
= self
.ipcmd
.link_get_hwaddress(macvlan_ifacename
)
871 raddrs
= ip4_running_addrs
= self
.ipcmd
.get_running_addrs(
872 ifname
=macvlan_ifacename
,
874 addr_virtual_ifaceobj
=ifaceobj
878 ips
= config
.get("ips")
880 if not raddrs
or not rhwaddress
:
881 ifaceobjcurr
.update_config_with_status(attr_name
, "", 1)
885 if self
.mac_str_to_int(rhwaddress
) == macvlan_hwaddress_int \
886 and self
.ipcmd
.compare_user_config_vs_running_state(raddrs
, ips
) \
887 and self
._check
_addresses
_in
_bridge
(ifaceobj
, macvlan_hwaddress
):
888 ifaceobjcurr
.update_config_with_status(
890 " ".join(virtual_addr_list_raw
),
894 ifaceobjcurr
.update_config_with_status(
896 '%s %s' % (rhwaddress
, ' '.join(raddrs
)),
900 ifaceobjcurr
.update_config_with_status(
902 '%s %s' % (rhwaddress
, ' '.join(raddrs
)),
909 # check macvlan ip4 hwaddress (only if ip4 were provided by the user)
910 if not ip4_config
.get("ips") or ip4_macvlan_hwaddress
== ip4_config
.get("hwaddress"):
911 ip6_macvlan_ifname
= ip6_config
.get("ifname")
912 ip6_macvlan_hwaddress
= ip6_config
.get("hwaddress")
914 # check macvlan ip6 hwaddress (only if ip6 were provided by the user)
915 if not ip6_config
.get("ips") or self
.ipcmd
.link_get_hwaddress(ip6_macvlan_ifname
) == ip6_macvlan_hwaddress
:
918 if self
.ipcmd
.compare_user_config_vs_running_state(
920 ip4_config
.get("ips")
921 ) and self
._check
_addresses
_in
_bridge
(ifaceobj
, ip4_macvlan_hwaddress
):
922 ip6_running_addrs
= self
.ipcmd
.get_running_addrs(
923 ifname
=ip6_macvlan_ifname
,
925 addr_virtual_ifaceobj
=ifaceobj
929 if self
.ipcmd
.compare_user_config_vs_running_state(
931 ip6_config
.get("ips")
932 ) and self
._check
_addresses
_in
_bridge
(ifaceobj
, ip4_macvlan_hwaddress
):
933 ifaceobjcurr
.update_config_with_status(
935 "%s %s" % (ip4_config
.get("id"), " ".join(ip4_config
.get("ips") + ip6_config
.get("ips"))),
941 ifaceobjcurr
.update_config_with_status(
943 "%s %s" % (ip4_config
.get("id"), " ".join(ip4_config
.get("ips") + ip6_config
.get("ips"))),
947 return macvlans_running_ipv6_addr
949 def _query_running(self
, ifaceobjrunning
, ifaceobj_getfunc
=None):
950 macvlan_prefix
= self
._get
_macvlan
_prefix
(ifaceobjrunning
)
951 address_virtuals
= []
952 for av
in linkCache
.links
:
953 if av
.startswith(macvlan_prefix
):
954 address_virtuals
.append(av
)
956 macvlans_ipv6_addrgen_list
= []
957 for av
in address_virtuals
:
958 macvlan_ifacename
= os
.path
.basename(av
)
959 rhwaddress
= self
.ipcmd
.link_get_hwaddress(macvlan_ifacename
)
962 for obj
in ifaceobj_getfunc(ifaceobjrunning
.name
) or []:
963 raddress
.extend(self
.ipcmd
.get_running_addrs(None, macvlan_ifacename
, addr_virtual_ifaceobj
=obj
) or [])
965 raddress
= list(set(raddress
))
968 self
.logger
.warn('%s: no running addresses'
969 %ifaceobjrunning
.name
)
971 ifaceobjrunning
.update_config('address-virtual',
972 '%s %s' %(rhwaddress
, ' '.join(raddress
)))
974 macvlans_ipv6_addrgen_list
.append((macvlan_ifacename
, self
.ipcmd
.get_ipv6_addrgen_mode(macvlan_ifacename
)))
976 macvlan_count
= len(address_virtuals
)
977 if not macvlan_count
:
979 ipv6_addrgen
= macvlans_ipv6_addrgen_list
[0][1]
981 for macvlan_ifname
, macvlan_ipv6_addrgen
in macvlans_ipv6_addrgen_list
:
982 if macvlan_ipv6_addrgen
!= ipv6_addrgen
:
983 # one macvlan has a different ipv6-addrgen configuration
984 # we simply return, ifquery-running will print the macvlan
985 # stanzas with the ipv6-addrgen on/off attribute
987 ifaceobjrunning
.update_config('address-virtual-ipv6-addrgen', 'off' if ipv6_addrgen
else 'on')
990 _run_ops
= {'up' : _up
,
992 'query-checkcurr' : _query_check
,
993 'query-running' : _query_running
}
996 """ returns list of ops supported by this module """
997 return self
._run
_ops
.keys()
999 def _init_command_handlers(self
):
1001 self
.ipcmd
= LinkUtils()
1003 def run(self
, ifaceobj
, operation
, query_ifaceobj
=None,
1004 ifaceobj_getfunc
=None, **extra_args
):
1005 """ run vlan configuration on the interface object passed as argument
1008 **ifaceobj** (object): iface object
1010 **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
1013 **query_ifaceobj** (object): query check ifaceobject. This is only
1014 valid when op is 'query-checkcurr'. It is an object same as
1015 ifaceobj, but contains running attribute values and its config
1016 status. The modules can use it to return queried running state
1017 of interfaces. status is success if the running state is same
1018 as user required state in ifaceobj. error otherwise.
1020 if ifaceobj
.type == ifaceType
.BRIDGE_VLAN
:
1022 op_handler
= self
._run
_ops
.get(operation
)
1025 self
._init
_command
_handlers
()
1026 if operation
== 'query-checkcurr':
1027 op_handler(self
, ifaceobj
, query_ifaceobj
)
1029 op_handler(self
, ifaceobj
, ifaceobj_getfunc
=ifaceobj_getfunc
)