1
0
mirror of https://github.com/CumulusNetworks/ifupdown2.git synced 2024-05-06 15:54:50 +00:00
Files
CumulusNetworks-ifupdown2/ifupdown2/addons/addressvirtual.py
Julien Fortin 007cae3525 ipv6-addrgen: add ifquery check/running/ifreload support using netlink cache
[13:09:20] root:~ # ifquery -a
auto lo
iface lo inet loopback

auto eth0
iface eth0 inet dhcp

auto vlan1903
iface vlan1903
	vlan-id 1903
	vlan-raw-device bridge
	ipv6-addrgen no
	address-virtual-ipv6-addrgen no
	address-virtual 00:00:5e:00:01:a3 2a06:c01:1:1903::1/64 fe80::1/64 185.98.123.1/24

auto bridge
iface bridge
	bridge-ports swp1

[13:09:25] root:~ # ifup -a -v
info: loading builtin modules from ['/usr/share/ifupdown2/addons']
info: executing /var/lib/ifupdown2/hooks/get_reserved_vlan_range.sh
info: executing /sbin/sysctl net.bridge.bridge-allow-multiple-vlans
info: executing /bin/pidof mstpd
info: executing /bin/ip rule show
info: executing /bin/ip -6 rule show
info: address: using default mtu 1500
info: 'link_master_slave' is set. slave admin state changes will be delayed till the masters admin state change.
info: processing interfaces file /etc/network/interfaces
info: lo: running ops ...
info: netlink: ip link show
info: netlink: ip addr show
info: executing /bin/ip addr help
info: address metric support: KO
info: lo: netlink: ip link set dev lo up
info: reading '/proc/sys/net/mpls/conf/lo/input'
info: reading '/proc/sys/net/ipv4/conf/lo/forwarding'
info: reading '/proc/sys/net/ipv6/conf/lo/forwarding'
info: reading '/proc/sys/net/ipv4/conf/lo/accept_local'
info: executing /bin/systemctl is-enabled vxrd.service
info: eth0: running ops ...
info: executing /sbin/ethtool eth0
info: reading '/sys/class/net/eth0/speed'
info: reading '/sys/class/net/eth0/duplex'
info: eth0: netlink: ip link set dev eth0 up
info: dhclient4 already running on eth0. Not restarting.
info: reading '/proc/sys/net/mpls/conf/eth0/input'
info: reading '/proc/sys/net/ipv4/conf/eth0/forwarding'
info: reading '/proc/sys/net/ipv6/conf/eth0/forwarding'
info: reading '/proc/sys/net/ipv4/conf/eth0/accept_local'
info: swp1: running ops ...
info: executing /sbin/ethtool swp1
info: reading '/sys/class/net/swp1/speed'
info: reading '/sys/class/net/swp1/duplex'
info: executing /sbin/ethtool -s swp1  speed 1000 duplex full
info: reading '/proc/sys/net/mpls/conf/swp1/input'
info: reading '/proc/sys/net/ipv4/conf/swp1/accept_local'
info: bridge: running ops ...
info: bridge: netlink: ip link add bridge type bridge
info: bridge: apply bridge settings
info: bridge: set bridge-ageing 1800
info: bridge: set bridge-hashel 4096
info: bridge: set bridge-hashmax 4096
info: bridge: set bridge-mcstats on
info: reading '/sys/class/net/bridge/bridge/stp_state'
info: bridge: stp state reset, reapplying port settings
info: bridge: netlink: ip link set bridge type bridge with attributes
info: writing '1' to file /proc/sys/net/ipv6/conf/swp1/disable_ipv6
info: executing /bin/ip -force -batch - [link set dev swp1 master bridge
addr flush dev swp1
]
info: bridge: applying bridge port configuration: ['swp1']
info: bridge: swp1: set bridge-portprios 8
info: swp1: netlink: ip link set dev swp1: bridge slave attributes
info: executing /sbin/brctl showmcqv4src bridge
info: bridge: applying bridge configuration specific to ports
info: bridge: processing bridge config for port swp1
info: swp1: netlink: ip link set dev swp1 up
info: bridge: setting bridge mac to port swp1 mac
info: executing /bin/ip link set dev bridge address 90:e2:ba:2c:b1:96
info: executing /sbin/mstpctl showportdetail bridge json
info: executing /sbin/mstpctl showbridge json bridge
info: bridge: applying mstp configuration specific to ports
info: bridge: processing mstp config for port swp1
info: bridge: netlink: ip link set dev bridge up
info: reading '/proc/sys/net/mpls/conf/bridge/input'
info: executing /sbin/sysctl net.ipv4.conf.bridge.forwarding
info: executing /sbin/sysctl net.ipv6.conf.bridge.forwarding
info: executing /bin/ip -force -batch - [link set dev bridge down
link set dev bridge addrgenmode eui64
link set dev bridge up
]
info: reading '/proc/sys/net/ipv4/conf/bridge/accept_local'
info: vlan1903: running ops ...
info: vlan1903: netlink: ip link add link bridge name vlan1903 type vlan id 1903 protocol 802.1q
info: vlan1903: netlink: ip link set dev vlan1903 up
info: reading '/proc/sys/net/mpls/conf/vlan1903/input'
info: reading '/proc/sys/net/ipv4/conf/vlan1903/forwarding'
info: reading '/proc/sys/net/ipv6/conf/vlan1903/forwarding'
info: executing /bin/ip -force -batch - [link set dev vlan1903 down
link set dev vlan1903 addrgenmode none
link set dev vlan1903 up
]
info: vlan1903: netlink: ip link add link vlan1903 name vlan1903-v0 type macvlan mode private
info: executing /sbin/sysctl net.ipv6.conf.vlan1903-v0.accept_dad
info: executing /sbin/sysctl net.ipv6.conf.vlan1903-v0.accept_dad=0
info: executing /sbin/sysctl net.ipv6.conf.vlan1903-v0.dad_transmits
info: executing /sbin/sysctl net.ipv6.conf.vlan1903-v0.dad_transmits=0
info: executing /bin/ip -force -batch - [link set dev vlan1903-v0 addrgenmode none
link set dev vlan1903-v0 down
link set dev vlan1903-v0 address 00:00:5e:00:01:a3
link set dev vlan1903-v0 up
addr add 2a06:c01:1:1903::1/64 dev vlan1903-v0
addr add fe80::1/64 dev vlan1903-v0
addr add 185.98.123.1/24 dev vlan1903-v0
route del 2a06:c01:1:1903::/64 dev vlan1903-v0
route del fe80::/64 dev vlan1903-v0
route add 2a06:c01:1:1903::/64 dev vlan1903-v0 proto kernel metric 9999
route add fe80::/64 dev vlan1903-v0 proto kernel metric 9999
]
info: reading '/proc/sys/net/ipv4/conf/vlan1903/accept_local'
[13:09:29] root:~ #
[13:09:30] root:~ #
[13:09:30] root:~ # ifquery -a -c
auto lo
iface lo inet loopback

auto eth0
iface eth0 inet dhcp                                                [pass]

auto vlan1903
iface vlan1903                                                      [pass]
	vlan-raw-device bridge                                      [pass]
	vlan-id 1903                                                [pass]
	ipv6-addrgen no                                             [pass]
	address-virtual 00:00:5e:00:01:a3 2a06:c01:1:1903::1/64 fe80::1/64 185.98.123.1/24     [pass]
	address-virtual-ipv6-addrgen no                             [pass]

auto bridge
iface bridge                                                        [pass]
	bridge-ports swp1                                           [pass]

[13:09:33] root:~ #
[13:09:35] root:~ #
[13:09:35] root:~ # ifquery -a -r
auto vlan1903-v0
iface vlan1903-v0
	ipv6-addrgen off
	address 185.98.123.1/24
	address 2a06:c01:1:1903::1/64

auto vlan1903
iface vlan1903
	vlan-id 1903
	vlan-protocol 802.1Q
	vlan-raw-device bridge
	ipv6-addrgen off
	address-virtual 00:00:5e:00:01:a3 185.98.123.1/242a06:c01:1:1903::1/64
	address-virtual-ipv6-addrgen off

auto bridge
iface bridge
	bridge-vlan-stats off
	bridge-mcstats 1
	bridge-ports swp1
	bridge-stp yes
	mstpctl-portp2p  swp1=auto
	mstpctl-treeportcost  swp1=20000
	mstpctl-portautoedge  swp1=yes

auto swp1
iface swp1

auto eth0
iface eth0 inet dhcp

auto lo
iface lo inet loopback
	mtu 65536

[13:09:38] root:~ # ip -d link show vlan1903
20: vlan1903@bridge: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default
    link/ether 90:e2:ba:2c:b1:96 brd ff:ff:ff:ff:ff:ff promiscuity 1
    vlan protocol 802.1Q id 1903 <REORDER_HDR> addrgenmode none
[13:09:50] root:~ # ip -d link show vlan1903-v0
21: vlan1903-v0@vlan1903: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default
    link/ether 00:00:5e:00:01:a3 brd ff:ff:ff:ff:ff:ff promiscuity 0
    macvlan  mode private addrgenmode none
[13:09:53] root:~ #
[13:09:56] root:~ # ip link set dev vlan1903-v0 addrgenmode eui64
[13:10:23] root:~ # ifquery -a -c
auto lo
iface lo inet loopback

auto eth0
iface eth0 inet dhcp                                                [pass]

auto vlan1903
iface vlan1903                                                      [fail]
	vlan-raw-device bridge                                      [pass]
	vlan-id 1903                                                [pass]
	ipv6-addrgen no                                             [pass]
	address-virtual 00:00:5e:00:01:a3 2a06:c01:1:1903::1/64 fe80::1/64 185.98.123.1/24     [pass]
	address-virtual-ipv6-addrgen no                             [fail]

auto bridge
iface bridge                                                        [pass]
	bridge-ports swp1                                           [pass]

[13:10:29] root:~ # ifquery -a -r
auto vlan1903-v0
iface vlan1903-v0
	address 185.98.123.1/24
	address 2a06:c01:1:1903::1/64

auto vlan1903
iface vlan1903
	vlan-id 1903
	vlan-protocol 802.1Q
	vlan-raw-device bridge
	ipv6-addrgen off
	address-virtual 00:00:5e:00:01:a3 185.98.123.1/242a06:c01:1:1903::1/64
	address-virtual-ipv6-addrgen on

auto bridge
iface bridge
	bridge-vlan-stats off
	bridge-mcstats 1
	bridge-ports swp1
	bridge-stp yes
	mstpctl-portp2p  swp1=auto
	mstpctl-treeportcost  swp1=20000
	mstpctl-portautoedge  swp1=yes

auto swp1
iface swp1

auto eth0
iface eth0 inet dhcp

auto lo
iface lo inet loopback
	mtu 65536

[13:10:33] root:~ #

Signed-off-by: Julien Fortin <julien@cumulusnetworks.com>
2018-12-13 14:43:57 -08:00

635 lines
28 KiB
Python

#!/usr/bin/python
#
# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
# Author: Roopa Prabhu, roopa@cumulusnetworks.com
#
import os
import glob
import socket
from ipaddr import IPNetwork, IPv6Network
try:
from ifupdown2.ifupdown.iface import *
from ifupdown2.ifupdown.utils import utils
from ifupdown2.ifupdown.netlink import netlink
from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
from ifupdown2.ifupdownaddons.modulebase import moduleBase
import ifupdown2.ifupdown.statemanager as statemanager
import ifupdown2.ifupdown.policymanager as policymanager
import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
import ifupdown2.ifupdown.ifupdownconfig as ifupdownconfig
except ImportError:
from ifupdown.iface import *
from ifupdown.utils import utils
from ifupdown.netlink import netlink
from ifupdownaddons.LinkUtils import LinkUtils
from ifupdownaddons.modulebase import moduleBase
import ifupdown.statemanager as statemanager
import ifupdown.policymanager as policymanager
import ifupdown.ifupdownflags as ifupdownflags
import ifupdown.ifupdownconfig as ifupdownconfig
class addressvirtual(moduleBase):
""" ifupdown2 addon module to configure virtual addresses """
_modinfo = {'mhelp' : 'address module configures virtual addresses for ' +
'interfaces. It creates a macvlan interface for ' +
'every mac ip address-virtual line',
'attrs' : {
'address-virtual' :
{ 'help' : 'bridge router virtual mac and ips',
'multivalue' : True,
'validvals' : ['<mac-ip/prefixlen-list>',],
'example': ['address-virtual 00:11:22:33:44:01 11.0.1.1/24 11.0.1.2/24']
},
'address-virtual-ipv6-addrgen': {
'help': 'enable disable ipv6 link addrgenmode',
'validvals': ['on', 'off'],
'default': 'on',
'example': [
'address-virtual-ipv6-addrgen on',
'address-virtual-ipv6-addrgen off'
]
}
}}
def __init__(self, *args, **kargs):
moduleBase.__init__(self, *args, **kargs)
self.ipcmd = None
self._bridge_fdb_query_cache = {}
self.addressvirtual_with_route_metric = utils.get_boolean_from_string(
policymanager.policymanager_api.get_module_globals(
module_name=self.__class__.__name__,
attr='addressvirtual_with_route_metric'
),
default=True
)
def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None):
if ifaceobj.get_attr_value('address-virtual'):
ifaceobj.link_privflags |= ifaceLinkPrivFlags.ADDRESS_VIRTUAL_SLAVE
def _get_macvlan_prefix(self, ifaceobj):
return '%s-v' %ifaceobj.name[0:13].replace('.', '-')
def _add_addresses_to_bridge(self, ifaceobj, hwaddress):
# XXX: batch the addresses
if ifaceobj.link_kind & ifaceLinkKind.VLAN:
bridgename = ifaceobj.lowerifaces[0]
vlan = self._get_vlan_id(ifaceobj)
if self.ipcmd.bridge_is_vlan_aware(bridgename):
[self.ipcmd.bridge_fdb_add(bridgename, addr,
vlan) for addr in hwaddress]
elif self.ipcmd.is_bridge(ifaceobj.name):
[self.ipcmd.bridge_fdb_add(ifaceobj.name, addr)
for addr in hwaddress]
def _remove_addresses_from_bridge(self, ifaceobj, hwaddress):
# XXX: batch the addresses
if ifaceobj.link_kind & ifaceLinkKind.VLAN:
bridgename = ifaceobj.lowerifaces[0]
vlan = self._get_vlan_id(ifaceobj)
if self.ipcmd.bridge_is_vlan_aware(bridgename):
for addr in hwaddress:
try:
self.ipcmd.bridge_fdb_del(bridgename, addr, vlan)
except Exception, e:
self.logger.debug("%s: %s" %(ifaceobj.name, str(e)))
pass
elif self.ipcmd.is_bridge(ifaceobj.name):
for addr in hwaddress:
try:
self.ipcmd.bridge_fdb_del(ifaceobj.name, addr)
except Exception, e:
self.logger.debug("%s: %s" %(ifaceobj.name, str(e)))
pass
def _get_bridge_fdbs(self, bridgename, vlan):
fdbs = self._bridge_fdb_query_cache.get(bridgename)
if not fdbs:
fdbs = self.ipcmd.bridge_fdb_show_dev(bridgename)
if not fdbs:
return
self._bridge_fdb_query_cache[bridgename] = fdbs
return fdbs.get(vlan)
def _check_addresses_in_bridge(self, ifaceobj, hwaddress):
""" If the device is a bridge, make sure the addresses
are in the bridge """
if ifaceobj.link_kind & ifaceLinkKind.VLAN:
bridgename = ifaceobj.lowerifaces[0]
vlan = self._get_vlan_id(ifaceobj)
if self.ipcmd.bridge_is_vlan_aware(bridgename):
fdb_addrs = self._get_bridge_fdbs(bridgename, str(vlan))
if not fdb_addrs or hwaddress not in fdb_addrs:
return False
return True
def _fix_connected_route(self, ifaceobj, vifacename, addr):
#
# XXX: Hack to make sure the primary address
# is the first in the routing table.
#
# We use `ip route get` on the vrr network to see which
# device the kernel returns. if it is the mac vlan device,
# flap the macvlan device to adjust the routing table entry.
#
# flapping the macvlan device makes sure the macvlan
# connected route goes through delete + add, hence adjusting
# the order in the routing table.
#
try:
self.logger.info('%s: checking route entry ...' %ifaceobj.name)
ip = IPNetwork(addr)
# we don't support ip6 route fix yet
if type(ip) == IPv6Network:
return
route_prefix = '%s/%d' %(ip.network, ip.prefixlen)
if ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE:
vrf_master = self.ipcmd.link_get_master(ifaceobj.name)
else:
vrf_master = None
dev = self.ipcmd.ip_route_get_dev(route_prefix, vrf_master=vrf_master)
if dev and dev != ifaceobj.name:
self.logger.info('%s: preferred routing entry ' %ifaceobj.name +
'seems to be of the macvlan dev %s'
%vifacename +
' .. flapping macvlan dev to fix entry.')
self.ipcmd.link_down(vifacename)
self.ipcmd.link_up(vifacename)
except Exception, e:
self.logger.debug('%s: fixing route entry failed (%s)'
% (ifaceobj.name, str(e)))
pass
def _handle_vrf_slaves(self, macvlan_ifacename, ifaceobj):
vrfname = self.ipcmd.link_get_master(ifaceobj.name)
if vrfname:
self.ipcmd.link_set(macvlan_ifacename, 'master', vrfname)
def _get_macs_from_old_config(self, ifaceobj=None):
""" This method returns a list of the mac addresses
in the address-virtual attribute for the bridge. """
maclist = []
saved_ifaceobjs = statemanager.statemanager_api.get_ifaceobjs(ifaceobj.name)
if not saved_ifaceobjs:
return maclist
# we need the old saved configs from the statemanager
for oldifaceobj in saved_ifaceobjs:
if not oldifaceobj.get_attr_value('address-virtual'):
continue
for av in oldifaceobj.get_attr_value('address-virtual'):
macip = av.split()
if len(macip) < 2:
self.logger.debug("%s: incorrect old address-virtual attrs '%s'"
%(oldifaceobj.name, av))
continue
maclist.append(macip[0])
return maclist
def get_addressvirtual_ipv6_addrgen_user_conf(self, ifaceobj):
ipv6_addrgen = ifaceobj.get_attr_value_first('address-virtual-ipv6-addrgen')
if ipv6_addrgen:
# IFLA_INET6_ADDR_GEN_MODE values:
# 0 = eui64
# 1 = none
ipv6_addrgen_nl = {'on': 0, 'yes': 0, '0': 0, 'off': 1, 'no': 1, '1': 1}.get(ipv6_addrgen.lower(), None)
if ipv6_addrgen_nl is None:
self.logger.warning('%s: value "%s" not allowed for attribute "ipv6-addrgen"' % (ifaceobj.name, user_configured_ipv6_addrgen))
self.logger.warning('%s: invalid value "%s" for attribute address-virtual-ipv6-addrgen' % (ifaceobj.name, ipv6_addrgen))
else:
return True, ipv6_addrgen_nl
return False, None
def _apply_address_config(self, ifaceobj, address_virtual_list):
purge_existing = False if ifupdownflags.flags.PERFMODE else True
lower_iface_mtu = update_mtu = None
if ifupdownconfig.config.get('adjust_logical_dev_mtu', '1') != '0':
if ifaceobj.lowerifaces and address_virtual_list:
update_mtu = True
user_configured_ipv6_addrgenmode, ipv6_addrgen_user_value = self.get_addressvirtual_ipv6_addrgen_user_conf(ifaceobj)
hwaddress = []
self.ipcmd.batch_start()
av_idx = 0
macvlan_prefix = self._get_macvlan_prefix(ifaceobj)
for av in address_virtual_list:
av_attrs = av.split()
if len(av_attrs) < 2:
self.log_error("%s: incorrect address-virtual attrs '%s'"
%(ifaceobj.name, av), ifaceobj,
raise_error=False)
av_idx += 1
continue
mac = av_attrs[0]
if not self.check_mac_address(ifaceobj, mac):
continue
# Create a macvlan device on this device and set the virtual
# router mac and ip on it
link_created = False
macvlan_ifacename = '%s%d' %(macvlan_prefix, av_idx)
if not self.ipcmd.link_exists(macvlan_ifacename):
try:
netlink.link_add_macvlan(ifaceobj.name, macvlan_ifacename)
except:
self.ipcmd.link_add_macvlan(ifaceobj.name, macvlan_ifacename)
link_created = True
# first thing we need to handle vrf enslavement
if (ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE):
self._handle_vrf_slaves(macvlan_ifacename, ifaceobj)
if user_configured_ipv6_addrgenmode:
self.ipcmd.ipv6_addrgen(macvlan_ifacename, ipv6_addrgen_user_value, link_created)
ips = av_attrs[1:]
if mac != 'None':
mac = mac.lower()
# customer could have used UPPERCASE for MAC
self.ipcmd.link_set_hwaddress(macvlan_ifacename, mac)
hwaddress.append(mac)
if self.addressvirtual_with_route_metric and self.ipcmd.addr_metric_support():
metric = self.ipcmd.get_default_ip_metric()
else:
metric = None
self.ipcmd.addr_add_multiple(
ifaceobj,
macvlan_ifacename,
ips,
purge_existing,
metric=metric
)
# If link existed before, flap the link
if not link_created:
if not self.addressvirtual_with_route_metric or not self.ipcmd.addr_metric_support():
# if the system doesn't support ip addr set METRIC
# we need to do manually check the ordering of the ip4 routes
self._fix_connected_route(ifaceobj, macvlan_ifacename, ips[0])
if update_mtu:
lower_iface_mtu = self.ipcmd.link_get_mtu(ifaceobj.name, refresh=True)
update_mtu = False
if lower_iface_mtu and lower_iface_mtu != self.ipcmd.link_get_mtu(macvlan_ifacename, refresh=True):
try:
self.ipcmd.link_set_mtu(macvlan_ifacename,
lower_iface_mtu)
except Exception as e:
self.logger.info('%s: failed to set mtu %s: %s' %
(macvlan_ifacename, lower_iface_mtu, e))
# set macvlan device to up in anycase.
# since we auto create them here..we are responsible
# to bring them up here in the case they were brought down
# by some other entity in the system.
netlink.link_set_updown(macvlan_ifacename, "up")
else:
try:
if not self.addressvirtual_with_route_metric or not self.ipcmd.addr_metric_support():
# if the system doesn't support ip addr set METRIC
# we need to do manually check the ordering of the ip6 routes
self.ipcmd.fix_ipv6_route_metric(ifaceobj, macvlan_ifacename, ips)
except Exception as e:
self.logger.debug('fix_vrf_slave_ipv6_route_metric: failed: %s' % e)
# Disable IPv6 duplicate address detection on VRR interfaces
for key, sysval in { 'accept_dad' : '0', 'dad_transmits' : '0' }.iteritems():
syskey = 'net.ipv6.conf.%s.%s' % (macvlan_ifacename, key)
if self.sysctl_get(syskey) != sysval:
self.sysctl_set(syskey, sysval)
av_idx += 1
self.ipcmd.batch_commit()
# check the statemanager for old configs.
# We need to remove only the previously configured FDB entries
oldmacs = self._get_macs_from_old_config(ifaceobj)
# get a list of fdbs in old that are not in new config meaning they should
# be removed since they are gone from the config
removed_macs = [mac for mac in oldmacs if mac.lower() not in hwaddress]
self._remove_addresses_from_bridge(ifaceobj, removed_macs)
# if ifaceobj is a bridge and bridge is a vlan aware bridge
# add the vid to the bridge
self._add_addresses_to_bridge(ifaceobj, hwaddress)
def _remove_running_address_config(self, ifaceobj):
if not self.ipcmd.link_exists(ifaceobj.name):
return
hwaddress = []
self.ipcmd.batch_start()
macvlan_prefix = self._get_macvlan_prefix(ifaceobj)
for macvlan_ifacename in glob.glob("/sys/class/net/%s*" %macvlan_prefix):
macvlan_ifacename = os.path.basename(macvlan_ifacename)
if not self.ipcmd.link_exists(macvlan_ifacename):
continue
hwaddress.append(self.ipcmd.link_get_hwaddress(macvlan_ifacename))
self.ipcmd.link_delete(os.path.basename(macvlan_ifacename))
# XXX: Also delete any fdb addresses. This requires, checking mac address
# on individual macvlan interfaces and deleting the vlan from that.
self.ipcmd.batch_commit()
if any(hwaddress):
self._remove_addresses_from_bridge(ifaceobj, hwaddress)
def _remove_address_config(self, ifaceobj, address_virtual_list=None):
if not address_virtual_list:
self._remove_running_address_config(ifaceobj)
return
if not self.ipcmd.link_exists(ifaceobj.name):
return
hwaddress = []
self.ipcmd.batch_start()
av_idx = 0
macvlan_prefix = self._get_macvlan_prefix(ifaceobj)
for av in address_virtual_list:
av_attrs = av.split()
if len(av_attrs) < 2:
self.log_error("%s: incorrect address-virtual attrs '%s'"
%(ifaceobj.name, av), ifaceobj,
raise_error=False)
av_idx += 1
continue
# Delete the macvlan device on this device
macvlan_ifacename = '%s%d' %(macvlan_prefix, av_idx)
self.ipcmd.link_delete(os.path.basename(macvlan_ifacename))
if av_attrs[0] != 'None':
hwaddress.append(av_attrs[0])
av_idx += 1
self.ipcmd.batch_commit()
self._remove_addresses_from_bridge(ifaceobj, hwaddress)
def check_mac_address(self, ifaceobj, mac):
if mac == 'None':
return True
mac = mac.lower()
try:
if int(mac.split(":")[0], 16) & 1 :
self.log_error("%s: Multicast bit is set in the virtual mac address '%s'"
% (ifaceobj.name, mac), ifaceobj=ifaceobj)
return False
return True
except ValueError:
return False
def _fixup_vrf_enslavements(self, ifaceobj, ifaceobj_getfunc=None):
""" This function fixes up address virtual interfaces
(macvlans) on vrf slaves. Since this fixup is an overhead,
this must be called only in cases when ifupdown2 is
called on the vrf device or its slave and not when
ifupdown2 is called for all devices. When all
interfaces are brought up, the expectation is that
the normal path will fix up a vrf device or its slaves"""
if not ifaceobj_getfunc:
return
if ((ifaceobj.link_kind & ifaceLinkKind.VRF) and
self.ipcmd.link_exists(ifaceobj.name)):
# if I am a vrf device and I have slaves
# that have address virtual config,
# enslave the slaves 'address virtual
# interfaces (macvlans)' to myself:
running_slaves = self.ipcmd.link_get_lowers(ifaceobj.name)
if running_slaves:
# pick up any existing slaves of a vrf device and
# look for their upperdevices and enslave them to the
# vrf device:
for s in running_slaves:
sobjs = ifaceobj_getfunc(s)
if (sobjs and
(sobjs[0].link_privflags & ifaceLinkPrivFlags.ADDRESS_VIRTUAL_SLAVE)):
# enslave all its upper devices to
# the vrf device
upperdevs = self.ipcmd.link_get_uppers(sobjs[0].name)
if not upperdevs:
continue
for u in upperdevs:
# skip vrf device which
# will also show up in the
# upper device list
if u == ifaceobj.name:
continue
self.ipcmd.link_set(u, 'master', ifaceobj.name,
state='up')
elif ((ifaceobj.link_privflags & ifaceLinkPrivFlags.ADDRESS_VIRTUAL_SLAVE) and
(ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE) and
self.ipcmd.link_exists(ifaceobj.name)):
# If I am a vrf slave and I have 'address virtual'
# config, make sure my addrress virtual interfaces
# (macvlans) are also enslaved to the vrf device
vrfname = ifaceobj.get_attr_value_first('vrf')
if not vrfname or not self.ipcmd.link_exists(vrfname):
return
running_uppers = self.ipcmd.link_get_uppers(ifaceobj.name)
if not running_uppers:
return
macvlan_prefix = self._get_macvlan_prefix(ifaceobj)
if not macvlan_prefix:
return
for u in running_uppers:
if u == vrfname:
continue
if u.startswith(macvlan_prefix):
self.ipcmd.link_set(u, 'master', vrfname,
state='up')
def _up(self, ifaceobj, ifaceobj_getfunc=None):
if not ifupdownflags.flags.ALL:
self._fixup_vrf_enslavements(ifaceobj, ifaceobj_getfunc)
address_virtual_list = ifaceobj.get_attr_value('address-virtual')
if not address_virtual_list:
# XXX: address virtual is not present. In which case,
# delete stale macvlan devices.
self._remove_address_config(ifaceobj, address_virtual_list)
return
if (ifaceobj.upperifaces and
not ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE):
self.log_error('%s: invalid placement of address-virtual lines (must be configured under an interface with no upper interfaces or parent interfaces)'
% (ifaceobj.name), ifaceobj)
return
if not self.ipcmd.link_exists(ifaceobj.name):
return
self._apply_address_config(ifaceobj, address_virtual_list)
def _down(self, ifaceobj, ifaceobj_getfunc=None):
try:
self._remove_address_config(ifaceobj,
ifaceobj.get_attr_value('address-virtual'))
except Exception, e:
self.log_warn(str(e))
def _query_check(self, ifaceobj, ifaceobjcurr):
address_virtual_list = ifaceobj.get_attr_value('address-virtual')
if not address_virtual_list:
return
if not self.ipcmd.link_exists(ifaceobj.name):
return
user_config_address_virtual_ipv6_addr = ifaceobj.get_attr_value_first('address-virtual-ipv6-addrgen')
if user_config_address_virtual_ipv6_addr and user_config_address_virtual_ipv6_addr not in utils._string_values:
ifaceobjcurr.update_config_with_status('address-virtual-ipv6-addrgen', user_config_address_virtual_ipv6_addr, 1)
user_config_address_virtual_ipv6_addr = None
macvlans_running_ipv6_addr = []
av_idx = 0
macvlan_prefix = self._get_macvlan_prefix(ifaceobj)
for address_virtual in address_virtual_list:
av_attrs = address_virtual.split()
if len(av_attrs) < 2:
self.logger.warn("%s: incorrect address-virtual attrs '%s'"
%(ifaceobj.name, address_virtual))
av_idx += 1
continue
# Check if the macvlan device on this interface
macvlan_ifacename = '%s%d' %(macvlan_prefix, av_idx)
if not self.ipcmd.link_exists(macvlan_ifacename):
ifaceobjcurr.update_config_with_status('address-virtual',
'', 1)
av_idx += 1
continue
if user_config_address_virtual_ipv6_addr:
macvlans_running_ipv6_addr.append(self.ipcmd.get_ipv6_addrgen_mode(macvlan_ifacename))
# Check mac and ip address
rhwaddress = self.ipcmd.link_get_hwaddress(macvlan_ifacename)
raddrs = self.ipcmd.get_running_addrs(
ifname=macvlan_ifacename,
details=False,
addr_virtual_ifaceobj=ifaceobj
)
if not raddrs or not rhwaddress:
ifaceobjcurr.update_config_with_status('address-virtual', '', 1)
av_idx += 1
continue
try:
av_attrs[0] = ':'.join([i if len(i) == 2 else '0%s' % i
for i in av_attrs[0].split(':')])
except:
self.logger.info('%s: %s: invalid value for address-virtual (%s)'
% (ifaceobj.name,
macvlan_ifacename,
' '.join(av_attrs)))
try:
if (rhwaddress == av_attrs[0].lower() and
self.ipcmd.compare_user_config_vs_running_state(raddrs, av_attrs[1:]) and
self._check_addresses_in_bridge(ifaceobj, av_attrs[0].lower())):
ifaceobjcurr.update_config_with_status('address-virtual',
address_virtual, 0)
else:
raddress_virtual = '%s %s' % (rhwaddress, ' '.join(raddrs))
ifaceobjcurr.update_config_with_status('address-virtual',
raddress_virtual, 1)
except:
raddress_virtual = '%s %s' % (rhwaddress, ' '.join(raddrs))
ifaceobjcurr.update_config_with_status('address-virtual',
raddress_virtual, 1)
av_idx += 1
if user_config_address_virtual_ipv6_addr:
bool_user_ipv6_addrgen = utils.get_boolean_from_string(user_config_address_virtual_ipv6_addr)
for running_ipv6_addrgen in macvlans_running_ipv6_addr:
if (not bool_user_ipv6_addrgen) != running_ipv6_addrgen:
ifaceobjcurr.update_config_with_status('address-virtual-ipv6-addrgen', user_config_address_virtual_ipv6_addr, 1)
return
ifaceobjcurr.update_config_with_status('address-virtual-ipv6-addrgen', user_config_address_virtual_ipv6_addr, 0)
def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None):
macvlan_prefix = self._get_macvlan_prefix(ifaceobjrunning)
address_virtuals = glob.glob("/sys/class/net/%s*" %macvlan_prefix)
macvlans_ipv6_addrgen_list = []
for av in address_virtuals:
macvlan_ifacename = os.path.basename(av)
rhwaddress = self.ipcmd.link_get_hwaddress(macvlan_ifacename)
raddress = self.ipcmd.get_running_addrs(None, macvlan_ifacename)
if not raddress:
self.logger.warn('%s: no running addresses'
%ifaceobjrunning.name)
raddress = []
ifaceobjrunning.update_config('address-virtual',
'%s %s' %(rhwaddress, ''.join(raddress)))
macvlans_ipv6_addrgen_list.append((macvlan_ifacename, self.ipcmd.get_ipv6_addrgen_mode(macvlan_ifacename)))
macvlan_count = len(address_virtuals)
if not macvlan_count:
return
ipv6_addrgen = macvlans_ipv6_addrgen_list[0][1]
for macvlan_ifname, macvlan_ipv6_addrgen in macvlans_ipv6_addrgen_list:
if macvlan_ipv6_addrgen != ipv6_addrgen:
# one macvlan has a different ipv6-addrgen configuration
# we simply return, ifquery-running will print the macvlan
# stanzas with the ipv6-addrgen on/off attribute
return
ifaceobjrunning.update_config('address-virtual-ipv6-addrgen', 'off' if ipv6_addrgen else 'on')
_run_ops = {'up' : _up,
'down' : _down,
'query-checkcurr' : _query_check,
'query-running' : _query_running}
def get_ops(self):
""" returns list of ops supported by this module """
return self._run_ops.keys()
def _init_command_handlers(self):
if not self.ipcmd:
self.ipcmd = LinkUtils()
def run(self, ifaceobj, operation, query_ifaceobj=None,
ifaceobj_getfunc=None, **extra_args):
""" run vlan configuration on the interface object passed as argument
Args:
**ifaceobj** (object): iface object
**operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
'query-running'
Kwargs:
**query_ifaceobj** (object): query check ifaceobject. This is only
valid when op is 'query-checkcurr'. It is an object same as
ifaceobj, but contains running attribute values and its config
status. The modules can use it to return queried running state
of interfaces. status is success if the running state is same
as user required state in ifaceobj. error otherwise.
"""
if ifaceobj.type == ifaceType.BRIDGE_VLAN:
return
op_handler = self._run_ops.get(operation)
if not op_handler:
return
self._init_command_handlers()
if operation == 'query-checkcurr':
op_handler(self, ifaceobj, query_ifaceobj)
else:
op_handler(self, ifaceobj, ifaceobj_getfunc=ifaceobj_getfunc)