mirror of
https://github.com/CumulusNetworks/ifupdown2.git
synced 2024-05-06 15:54:50 +00:00
783 lines
27 KiB
Python
783 lines
27 KiB
Python
# Copyright (C) 2017, 2018, 2019 Cumulus Networks, Inc. all rights reserved
|
|
#
|
|
# This program is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU General Public License as
|
|
# published by the Free Software Foundation; version 2.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
# General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
# 02110-1301, USA.
|
|
#
|
|
# https://www.gnu.org/licenses/gpl-2.0-standalone.html
|
|
#
|
|
# Author:
|
|
# Julien Fortin, julien@cumulusnetworks.com
|
|
#
|
|
# iproute2 -- contains all iproute2 related operation
|
|
#
|
|
|
|
import re
|
|
import shlex
|
|
import signal
|
|
import ipaddress
|
|
import subprocess
|
|
|
|
|
|
try:
|
|
from ifupdown2.lib.sysfs import Sysfs
|
|
from ifupdown2.lib.base_objects import Cache, Requirements
|
|
|
|
import ifupdown2.nlmanager.ipnetwork as ipnetwork
|
|
|
|
from ifupdown2.ifupdown.utils import utils
|
|
from ifupdown2.ifupdown.iface import ifaceLinkPrivFlags
|
|
from ifupdown2.nlmanager.nlpacket import Link
|
|
except (ImportError, ModuleNotFoundError):
|
|
from lib.sysfs import Sysfs
|
|
from lib.base_objects import Cache, Requirements
|
|
|
|
import nlmanager.ipnetwork as ipnetwork
|
|
|
|
from ifupdown.utils import utils
|
|
from ifupdown.iface import ifaceLinkPrivFlags
|
|
from nlmanager.nlpacket import Link
|
|
|
|
# WORK AROUND - Tunnel creation should be done via netlink and not iproute2 ####
|
|
import struct #
|
|
import socket #
|
|
#
|
|
try: #
|
|
import ifupdown2.nlmanager.nlpacket as nlpacket #
|
|
except: #
|
|
import nlmanager.nlpacket as nlpacket #
|
|
################################################################################
|
|
|
|
|
|
class IPRoute2(Cache, Requirements):
|
|
|
|
VXLAN_UDP_PORT = 4789
|
|
VXLAN_PEER_REGEX_PATTERN = re.compile("\s+dst\s+(\d+.\d+.\d+.\d+)\s+")
|
|
|
|
def __init__(self):
|
|
Cache.__init__(self)
|
|
Requirements.__init__(self)
|
|
|
|
self.sysfs = Sysfs
|
|
|
|
self.__batch = {}
|
|
self.__batch_mode = False
|
|
|
|
# if bridge utils is not installed overrrides specific functions to
|
|
# avoid constantly checking bridge_utils_is_installed
|
|
if not Requirements.bridge_utils_is_installed:
|
|
self.bridge_set_stp = lambda _, __: None
|
|
self.bridge_del_mcqv4src = lambda _, __: None
|
|
self.bridge_set_mcqv4src = lambda _, __, ___: None
|
|
|
|
############################################################################
|
|
# WORK-AROUND
|
|
############################################################################
|
|
|
|
def __update_cache_after_link_creation(self, ifname, kind):
|
|
"""
|
|
WORK AROUND - when creating tunnel via iproute2 we still need to fill
|
|
our internal cache to keep track of this interface until we receive the
|
|
NEWLINK notification. This code is a copy-paste from:
|
|
nlcache.tx_nlpacket_get_response_with_error_and_cache_on_ack
|
|
|
|
:param ifname:
|
|
:param kind:
|
|
:return:
|
|
"""
|
|
packet = nlpacket.Link(nlpacket.RTM_NEWLINK, False, use_color=False)
|
|
packet.flags = nlpacket.NLM_F_CREATE | nlpacket.NLM_F_REQUEST | nlpacket.NLM_F_ACK
|
|
packet.body = struct.pack('Bxxxiii', socket.AF_UNSPEC, 0, 0, 0)
|
|
packet.add_attribute(nlpacket.Link.IFLA_IFNAME, ifname)
|
|
packet.add_attribute(nlpacket.Link.IFLA_LINKINFO, {
|
|
nlpacket.Link.IFLA_INFO_KIND: kind,
|
|
nlpacket.Link.IFLA_INFO_DATA: {}
|
|
})
|
|
packet.build_message(0, 0)
|
|
# When creating a new link via netlink, we don't always wait for the kernel
|
|
# NEWLINK notification to be cached to continue. If our request is ACKed by
|
|
# the OS we assume that the link was successfully created. Since we aren't
|
|
# waiting for the kernel notification to continue we need to manually fill
|
|
# our cache with the packet we just TX'ed. Once the NEWLINK notification
|
|
# is received it will simply override the previous entry.
|
|
# We need to keep track of those manually cached packets. We set a private
|
|
# flag on the objects via the attribute priv_flags
|
|
packet.priv_flags |= nlpacket.NLM_F_REQUEST
|
|
try:
|
|
# we need to decode the service header so all the attribute are properly
|
|
# filled in the packet object that we are about to store in cache.
|
|
# i.e.: packet.flags shouldn't contain NLM_F_* values but IFF_* (in case of Link object)
|
|
# otherwise call to cache.link_is_up() will probably return True
|
|
packet.decode_service_header()
|
|
except:
|
|
# we can ignore all errors
|
|
pass
|
|
|
|
# Then we can use our normal "add_link" API call to cache the packet
|
|
# and fill up our additional internal data structures.
|
|
self.cache.add_link(packet)
|
|
|
|
############################################################################
|
|
# BATCH
|
|
############################################################################
|
|
|
|
def __add_to_batch(self, prefix, cmd):
|
|
if prefix in self.__batch:
|
|
self.__batch[prefix].append(cmd)
|
|
else:
|
|
self.__batch[prefix] = [cmd]
|
|
|
|
def __execute_or_batch(self, prefix, cmd):
|
|
if self.__batch_mode:
|
|
self.__add_to_batch(prefix, cmd)
|
|
else:
|
|
utils.exec_command("%s %s" % (prefix, cmd))
|
|
|
|
def __execute_or_batch_dry_run(self, prefix, cmd):
|
|
"""
|
|
The batch function has it's own dryrun handler so we only handle
|
|
dryrun for non-batch mode. Which will be removed once the "utils"
|
|
module has it's own dryrun handlers
|
|
"""
|
|
if self.__batch_mode:
|
|
self.__add_to_batch(prefix, cmd)
|
|
else:
|
|
self.log_info_dry_run("executing: %s %s" % (prefix, cmd))
|
|
|
|
def batch_start(self):
|
|
if not self.__batch_mode:
|
|
self.__batch_mode = True
|
|
self.__batch = {}
|
|
|
|
def batch_commit(self):
|
|
try:
|
|
if not self.__batch_mode or not self.__batch:
|
|
return
|
|
for prefix, commands in self.__batch.items():
|
|
utils.exec_command(
|
|
"%s -force -batch -" % prefix,
|
|
stdin="\n".join(commands)
|
|
)
|
|
except:
|
|
raise
|
|
finally:
|
|
self.__batch_mode = False
|
|
del self.__batch
|
|
self.__batch = None
|
|
|
|
############################################################################
|
|
# LINK
|
|
############################################################################
|
|
|
|
def link_up(self, ifname):
|
|
if not self.cache.link_is_up(ifname):
|
|
self.link_up_force(ifname)
|
|
|
|
def link_down(self, ifname):
|
|
if self.cache.link_is_up(ifname):
|
|
self.link_down_force(ifname)
|
|
|
|
def link_up_dry_run(self, ifname):
|
|
self.link_up_force(ifname)
|
|
|
|
def link_down_dry_run(self, ifname):
|
|
self.link_down_force(ifname)
|
|
|
|
def link_up_force(self, ifname):
|
|
self.__execute_or_batch(utils.ip_cmd, "link set dev %s up" % ifname)
|
|
|
|
def link_down_force(self, ifname):
|
|
self.__execute_or_batch(utils.ip_cmd, "link set dev %s down" % ifname)
|
|
|
|
###
|
|
|
|
def link_set_master(self, ifname, master):
|
|
if master != self.cache.get_master(ifname):
|
|
self.__execute_or_batch(
|
|
utils.ip_cmd,
|
|
"link set dev %s master %s" % (ifname, master)
|
|
)
|
|
|
|
def link_set_master_dry_run(self, ifname, master):
|
|
self.__execute_or_batch(
|
|
utils.ip_cmd,
|
|
"link set dev %s master %s" % (ifname, master)
|
|
)
|
|
|
|
###
|
|
|
|
def link_set_address(self, ifname, address):
|
|
if utils.mac_str_to_int(address) != self.cache.get_link_address_raw(ifname):
|
|
self.link_down(ifname)
|
|
self.__execute_or_batch(
|
|
utils.ip_cmd,
|
|
"link set dev %s address %s" % (ifname, address)
|
|
)
|
|
self.link_up(ifname)
|
|
|
|
def link_set_address_dry_run(self, ifname, address):
|
|
self.link_down(ifname)
|
|
self.__execute_or_batch(
|
|
utils.ip_cmd,
|
|
"link set dev %s address %s" % (ifname, address)
|
|
)
|
|
self.link_up(ifname)
|
|
|
|
def link_set_address_and_keep_down(self, ifname, address, keep_down=False):
|
|
if utils.mac_str_to_int(address) != self.cache.get_link_address_raw(ifname):
|
|
self.link_down(ifname)
|
|
self.__execute_or_batch(
|
|
utils.ip_cmd,
|
|
"link set dev %s address %s" % (ifname, address)
|
|
)
|
|
if not keep_down:
|
|
self.link_up(ifname)
|
|
|
|
def link_set_address_and_keep_down_dry_run(self, ifname, address, keep_down=False):
|
|
self.link_down(ifname)
|
|
self.__execute_or_batch(
|
|
utils.ip_cmd,
|
|
"link set dev %s address %s" % (ifname, address)
|
|
)
|
|
if not keep_down:
|
|
self.link_up(ifname)
|
|
|
|
###
|
|
|
|
def link_add(self, ifname, link_type):
|
|
utils.exec_command(
|
|
"%s link add %s type %s"
|
|
% (utils.ip_cmd, ifname, link_type)
|
|
)
|
|
|
|
###
|
|
|
|
def link_add_macvlan(self, ifname, macvlan_ifname, macvlan_mode):
|
|
utils.exec_command(
|
|
"%s link add link %s name %s type macvlan mode %s"
|
|
% (utils.ip_cmd, ifname, macvlan_ifname, macvlan_mode)
|
|
)
|
|
|
|
def link_add_macvlan_dry_run(self, ifname, macvlan_ifname, macvlan_mode):
|
|
# this dryrun method can be removed once dryrun handlers
|
|
# are added to the utils module
|
|
self.log_info_ifname_dry_run(ifname, "executing %s link add link %s name %s type macvlan mode %s"
|
|
% (utils.ip_cmd, ifname, macvlan_ifname, macvlan_mode)
|
|
)
|
|
|
|
###
|
|
|
|
def link_add_veth(self, ifname, peer_name):
|
|
utils.exec_command(
|
|
"%s link add %s type veth peer name %s"
|
|
% (utils.ip_cmd, ifname, peer_name)
|
|
)
|
|
|
|
###
|
|
|
|
def link_add_single_vxlan(self, ifname, ip, port):
|
|
self.logger.info("creating single vxlan device: %s" % ifname)
|
|
|
|
cmd = ["link add dev %s type vxlan external" % ifname]
|
|
|
|
if ip:
|
|
cmd.append("local %s" % ip)
|
|
|
|
if port:
|
|
cmd.append("dstport %s" % port)
|
|
|
|
self.__execute_or_batch(utils.ip_cmd, " ".join(cmd))
|
|
self.__update_cache_after_link_creation(ifname, "vxlan")
|
|
|
|
def link_create_vxlan(self, name, vxlanid, localtunnelip=None, svcnodeip=None,
|
|
remoteips=None, learning='on', ageing=None, ttl=None, physdev=None):
|
|
if svcnodeip and remoteips:
|
|
raise Exception("svcnodeip and remoteip are mutually exclusive")
|
|
|
|
if self.cache.link_exists(name):
|
|
cmd = [
|
|
"link set dev %s type vxlan dstport %d"
|
|
% (name, self.VXLAN_UDP_PORT)
|
|
]
|
|
else:
|
|
cmd = [
|
|
"link add dev %s type vxlan id %s dstport %d"
|
|
% (name, vxlanid, self.VXLAN_UDP_PORT)
|
|
]
|
|
|
|
if svcnodeip:
|
|
if svcnodeip.ip.is_multicast:
|
|
cmd.append("group %s" % svcnodeip)
|
|
else:
|
|
cmd.append("remote %s" % svcnodeip)
|
|
|
|
if ageing:
|
|
cmd.append("ageing %s" % ageing)
|
|
|
|
if learning == 'off':
|
|
cmd.append("nolearning")
|
|
|
|
if ttl is not None:
|
|
cmd.append("ttl %s" % ttl)
|
|
|
|
if physdev:
|
|
cmd.append("dev %s" % physdev)
|
|
|
|
if localtunnelip:
|
|
cmd.append("local %s" % localtunnelip)
|
|
|
|
self.__execute_or_batch(utils.ip_cmd, " ".join(cmd))
|
|
|
|
def get_vxlan_peers(self, dev, svcnodeip):
|
|
cmd = "%s fdb show brport %s" % (utils.bridge_cmd, dev)
|
|
cur_peers = []
|
|
try:
|
|
ps = subprocess.Popen(shlex.split(cmd), stdout=subprocess.PIPE, close_fds=False)
|
|
utils.enable_subprocess_signal_forwarding(ps, signal.SIGINT)
|
|
output = subprocess.check_output(("grep", "00:00:00:00:00:00"), stdin=ps.stdout).decode()
|
|
ps.wait()
|
|
utils.disable_subprocess_signal_forwarding(signal.SIGINT)
|
|
try:
|
|
for l in output.split('\n'):
|
|
m = self.VXLAN_PEER_REGEX_PATTERN.search(l)
|
|
if m and m.group(1) != svcnodeip:
|
|
cur_peers.append(m.group(1))
|
|
except:
|
|
self.logger.warning('error parsing ip link output')
|
|
except subprocess.CalledProcessError as e:
|
|
if e.returncode != 1:
|
|
self.logger.error(str(e))
|
|
finally:
|
|
utils.disable_subprocess_signal_forwarding(signal.SIGINT)
|
|
return cur_peers
|
|
|
|
###
|
|
|
|
def link_add_xfrm(self, ifname, xfrm_name, xfrm_id):
|
|
utils.exec_commandl(['ip', 'link', 'add', xfrm_name, 'type', 'xfrm', 'dev', ifname, 'if_id', xfrm_id])
|
|
self.__update_cache_after_link_creation(xfrm_name, "xfrm")
|
|
|
|
def link_add_openvswitch(self, ifname, kind):
|
|
self.__update_cache_after_link_creation(ifname, kind)
|
|
|
|
############################################################################
|
|
# TUNNEL
|
|
############################################################################
|
|
|
|
def tunnel_create(self, tunnelname, mode, attrs=None):
|
|
if self.cache.link_exists(tunnelname):
|
|
return
|
|
|
|
cmd = []
|
|
if "6" in mode:
|
|
cmd.append("-6")
|
|
|
|
if mode in ["gretap"]:
|
|
cmd.append("link add %s type %s" % (tunnelname, mode))
|
|
else:
|
|
cmd.append("tunnel add %s mode %s" % (tunnelname, mode))
|
|
|
|
if attrs:
|
|
for k, v in attrs.items():
|
|
cmd.append(k)
|
|
if v:
|
|
cmd.append(v)
|
|
|
|
utils.exec_command("%s %s" % (utils.ip_cmd, " ".join(cmd)))
|
|
self.__update_cache_after_link_creation(tunnelname, mode)
|
|
|
|
def tunnel_change(self, tunnelname, attrs=None):
|
|
""" tunnel change function """
|
|
if not self.cache.link_exists(tunnelname):
|
|
return
|
|
cmd = ["tunnel change %s" % tunnelname]
|
|
if attrs:
|
|
for k, v in attrs.items():
|
|
cmd.append(k)
|
|
if v:
|
|
cmd.append(v)
|
|
self.__execute_or_batch(utils.ip_cmd, " ".join(cmd))
|
|
|
|
############################################################################
|
|
# ADDRESS
|
|
############################################################################
|
|
|
|
def addr_flush(self, ifname):
|
|
if self.cache.link_has_ip(ifname):
|
|
self.__execute_or_batch(utils.ip_cmd, "addr flush dev %s" % ifname)
|
|
|
|
def link_set_ipv6_addrgen_dry_run(self, ifname, addrgen, link_created):
|
|
addrgen_str = "none" if addrgen else "eui64"
|
|
self.link_down(ifname)
|
|
self.__execute_or_batch(utils.ip_cmd, "link set dev %s addrgenmode %s" % (ifname, addrgen_str))
|
|
self.link_up(ifname)
|
|
|
|
def link_set_ipv6_addrgen(self, ifname, addrgen, link_created):
|
|
"""
|
|
IFLA_INET6_ADDR_GEN_MODE values:
|
|
0 = eui64
|
|
1 = none
|
|
|
|
:param ifname:
|
|
:param addrgen:
|
|
:param link_created:
|
|
:return:
|
|
"""
|
|
cached_ipv6_addr_gen_mode = self.cache.get_link_ipv6_addrgen_mode(ifname)
|
|
|
|
if cached_ipv6_addr_gen_mode == addrgen:
|
|
return True
|
|
|
|
disabled_ipv6 = self.sysfs.get_ipv6_conf_disable_ipv6(ifname)
|
|
|
|
if disabled_ipv6:
|
|
self.logger.info("%s: cannot set addrgen: ipv6 is disabled on this device" % ifname)
|
|
return False
|
|
|
|
if link_created:
|
|
link_mtu = self.sysfs.link_get_mtu(ifname)
|
|
else:
|
|
link_mtu = self.cache.get_link_mtu(ifname)
|
|
|
|
if link_mtu < 1280:
|
|
self.logger.info("%s: ipv6 addrgen is disabled on device with MTU "
|
|
"lower than 1280 (current mtu %s): cannot set addrgen %s"
|
|
% (ifname, link_mtu, "off" if addrgen else "on"))
|
|
return False
|
|
|
|
if not link_created:
|
|
# When setting addrgenmode it is necessary to flap the macvlan
|
|
# device. After flapping the device we also need to re-add all
|
|
# the user configuration. The best way to add the user config
|
|
# is to flush our internal address cache
|
|
self.cache.address_flush_link(ifname)
|
|
|
|
is_link_up = self.cache.link_is_up(ifname)
|
|
|
|
if is_link_up:
|
|
self.link_down_force(ifname)
|
|
|
|
self.__execute_or_batch(
|
|
utils.ip_cmd,
|
|
"link set dev %s addrgenmode %s" % (ifname, Link.ifla_inet6_addr_gen_mode_dict.get(addrgen))
|
|
)
|
|
|
|
if is_link_up:
|
|
self.link_up_force(ifname)
|
|
|
|
return True
|
|
|
|
@staticmethod
|
|
def __compare_user_config_vs_running_state(running_addrs, user_addrs):
|
|
ip4 = []
|
|
ip6 = []
|
|
|
|
for ip in user_addrs or []:
|
|
if ip.version == 6:
|
|
ip6.append(ip)
|
|
else:
|
|
ip4.append(ip)
|
|
|
|
running_ipobj = []
|
|
for ip in running_addrs or []:
|
|
running_ipobj.append(ip)
|
|
|
|
return running_ipobj == (ip4 + ip6)
|
|
|
|
def add_addresses(self, ifacobj, ifname, address_list, purge_existing=False, metric=None, with_address_virtual=False):
|
|
if purge_existing:
|
|
running_address_list = self.cache.get_managed_ip_addresses(
|
|
ifname,
|
|
[ifacobj],
|
|
with_address_virtual=with_address_virtual
|
|
)
|
|
|
|
if self.__compare_user_config_vs_running_state(running_address_list, address_list):
|
|
return
|
|
|
|
try:
|
|
self.__execute_or_batch(utils.ip_cmd, "addr flush dev %s" % ifname)
|
|
except Exception as e:
|
|
self.logger.warning("%s: flushing all ip address failed: %s" % (ifname, str(e)))
|
|
for addr in address_list:
|
|
try:
|
|
if metric:
|
|
self.__execute_or_batch(utils.ip_cmd, "addr add %s dev %s metric %s" % (addr, ifname, metric))
|
|
else:
|
|
self.__execute_or_batch(utils.ip_cmd, "addr add %s dev %s" % (addr, ifname))
|
|
except Exception as e:
|
|
self.logger.error("%s: add_address: %s" % (ifname, str(e)))
|
|
|
|
############################################################################
|
|
# BRIDGE
|
|
############################################################################
|
|
|
|
@staticmethod
|
|
def bridge_set_stp(bridge, stp_state):
|
|
utils.exec_command("%s stp %s %s" % (utils.brctl_cmd, bridge, stp_state))
|
|
|
|
@staticmethod
|
|
def bridge_fdb_show_dev(dev):
|
|
try:
|
|
fdbs = {}
|
|
output = utils.exec_command("%s fdb show dev %s" % (utils.bridge_cmd, dev))
|
|
if output:
|
|
for fdb_entry in output.splitlines():
|
|
try:
|
|
entries = fdb_entry.split()
|
|
fdbs.setdefault(entries[2], []).append(entries[0])
|
|
except:
|
|
pass
|
|
return fdbs
|
|
except Exception:
|
|
return None
|
|
|
|
@staticmethod
|
|
def bridge_fdb_add(dev, address, vlan=None, bridge=True, remote=None):
|
|
target = "self" if bridge else ""
|
|
vlan_str = "vlan %s " % vlan if vlan else ""
|
|
dst_str = "dst %s " % remote if remote else ""
|
|
|
|
utils.exec_command(
|
|
"%s fdb replace %s dev %s %s %s %s"
|
|
% (
|
|
utils.bridge_cmd,
|
|
address,
|
|
dev,
|
|
vlan_str,
|
|
target,
|
|
dst_str
|
|
)
|
|
)
|
|
|
|
@staticmethod
|
|
def bridge_fdb_append(dev, address, vlan=None, bridge=True, remote=None):
|
|
target = "self" if bridge else ""
|
|
vlan_str = "vlan %s " % vlan if vlan else ""
|
|
dst_str = "dst %s " % remote if remote else ""
|
|
|
|
utils.exec_command(
|
|
"%s fdb append %s dev %s %s %s %s"
|
|
% (
|
|
utils.bridge_cmd,
|
|
address,
|
|
dev,
|
|
vlan_str,
|
|
target,
|
|
dst_str
|
|
)
|
|
)
|
|
|
|
@staticmethod
|
|
def bridge_fdb_del(dev, address, vlan=None, bridge=True, remote=None):
|
|
target = "self" if bridge else ""
|
|
vlan_str = "vlan %s " % vlan if vlan else ""
|
|
dst_str = "dst %s " % remote if remote else ""
|
|
|
|
utils.exec_command(
|
|
"%s fdb del %s dev %s %s %s %s"
|
|
% (
|
|
utils.bridge_cmd,
|
|
address,
|
|
dev,
|
|
vlan_str,
|
|
target,
|
|
dst_str
|
|
)
|
|
)
|
|
|
|
@staticmethod
|
|
def bridge_vlan_del_vid_list(ifname, vids):
|
|
if not vids:
|
|
return
|
|
for v in vids:
|
|
utils.exec_command(
|
|
"%s vlan del vid %s dev %s" % (utils.bridge_cmd, v, ifname)
|
|
)
|
|
|
|
def bridge_vlan_del_vid_list_self(self, ifname, vids, is_bridge=True):
|
|
target = "self" if is_bridge else ""
|
|
for v in vids:
|
|
self.__execute_or_batch(
|
|
utils.bridge_cmd,
|
|
"vlan del vid %s dev %s %s" % (v, ifname, target)
|
|
)
|
|
|
|
def bridge_vlan_add_vlan_tunnel_info(self, ifname, vids, vnis):
|
|
for i in range(0, len(vids)):
|
|
try:
|
|
self.__execute_or_batch(
|
|
utils.bridge_cmd,
|
|
"vlan add dev %s vid %s tunnel_info id %s" % (
|
|
ifname, vids[i], vnis[i]
|
|
)
|
|
)
|
|
except Exception as e:
|
|
if "exists" not in str(e).lower():
|
|
self.logger.error(e)
|
|
|
|
def bridge_vlan_add_vid_list_self(self, ifname, vids, is_bridge=True):
|
|
target = "self" if is_bridge else ""
|
|
for v in vids:
|
|
self.__execute_or_batch(
|
|
utils.bridge_cmd,
|
|
"vlan add vid %s dev %s %s" % (v, ifname, target)
|
|
)
|
|
|
|
def bridge_vlan_del_pvid(self, ifname, pvid):
|
|
self.__execute_or_batch(
|
|
utils.bridge_cmd,
|
|
"vlan del vid %s untagged pvid dev %s" % (pvid, ifname)
|
|
)
|
|
|
|
def bridge_vlan_add_pvid(self, ifname, pvid):
|
|
self.__execute_or_batch(
|
|
utils.bridge_cmd,
|
|
"vlan add vid %s untagged pvid dev %s" % (pvid, ifname)
|
|
)
|
|
|
|
def bridge_del_mcqv4src(self, bridge, vlan):
|
|
try:
|
|
vlan = int(vlan)
|
|
except Exception as e:
|
|
self.logger.info("%s: del mcqv4src vlan: invalid parameter %s: %s"
|
|
% (bridge, vlan, str(e)))
|
|
return
|
|
utils.exec_command("%s delmcqv4src %s %d" % (utils.brctl_cmd, bridge, vlan))
|
|
|
|
def bridge_set_mcqv4src(self, bridge, vlan, mcquerier):
|
|
try:
|
|
vlan = int(vlan)
|
|
except Exception as e:
|
|
self.logger.info("%s: set mcqv4src vlan: invalid parameter %s: %s" % (bridge, vlan, str(e)))
|
|
return
|
|
if vlan == 0 or vlan > 4095:
|
|
self.logger.warning("mcqv4src vlan '%d' invalid range" % vlan)
|
|
return
|
|
|
|
ip = mcquerier.split(".")
|
|
if len(ip) != 4:
|
|
self.logger.warning("mcqv4src '%s' invalid IPv4 address" % mcquerier)
|
|
return
|
|
for k in ip:
|
|
if not k.isdigit() or int(k, 10) < 0 or int(k, 10) > 255:
|
|
self.logger.warning("mcqv4src '%s' invalid IPv4 address" % mcquerier)
|
|
return
|
|
|
|
utils.exec_command("%s setmcqv4src %s %d %s" % (utils.brctl_cmd, bridge, vlan, mcquerier))
|
|
|
|
############################################################################
|
|
# ROUTE
|
|
############################################################################
|
|
|
|
@staticmethod
|
|
def route_add_gateway(ifname, gateway, vrf=None, metric=None, onlink=True):
|
|
if not gateway:
|
|
return
|
|
|
|
if not vrf:
|
|
cmd = "%s route add default via %s proto kernel" % (utils.ip_cmd, gateway)
|
|
else:
|
|
cmd = "%s route add table %s default via %s proto kernel" % (utils.ip_cmd, vrf, gateway)
|
|
|
|
if metric:
|
|
cmd += " metric %s" % metric
|
|
|
|
cmd += " dev %s" % ifname
|
|
|
|
if onlink:
|
|
cmd += " onlink"
|
|
|
|
utils.exec_command(cmd)
|
|
|
|
@staticmethod
|
|
def route_del_gateway(ifname, gateway, vrf=None, metric=None):
|
|
"""
|
|
delete default gw
|
|
we don't need a DRYRUN handler here as utils.exec_command should have one
|
|
"""
|
|
if not gateway:
|
|
return
|
|
|
|
if not vrf:
|
|
cmd = "%s route del default via %s proto kernel" % (utils.ip_cmd, gateway)
|
|
else:
|
|
cmd = "%s route del table %s default via %s proto kernel" % (utils.ip_cmd, vrf, gateway)
|
|
|
|
if metric:
|
|
cmd += " metric %s" % metric
|
|
|
|
cmd += " dev %s" % ifname
|
|
utils.exec_command(cmd)
|
|
|
|
def fix_ipv6_route_metric(self, ifaceobj, macvlan_ifacename, ips):
|
|
vrf_table = None
|
|
|
|
if ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE:
|
|
try:
|
|
for upper_iface in ifaceobj.upperifaces:
|
|
vrf_table = self.cache.get_vrf_table(upper_iface)
|
|
if vrf_table:
|
|
break
|
|
except:
|
|
pass
|
|
|
|
ip_route_del = []
|
|
for ip in ips:
|
|
ip_network_obj = ipaddress.ip_network(ip)
|
|
|
|
if ip_network_obj.version == 6:
|
|
route_prefix = '%s/%d' % (ip_network_obj.network, ip_network_obj.prefixlen)
|
|
|
|
if vrf_table:
|
|
self.__execute_or_batch(
|
|
utils.ip_cmd,
|
|
"route del %s table %s dev %s" % (route_prefix, vrf_table, macvlan_ifacename)
|
|
)
|
|
else:
|
|
self.__execute_or_batch(
|
|
utils.ip_cmd,
|
|
"route del %s dev %s" % (route_prefix, macvlan_ifacename)
|
|
)
|
|
|
|
ip_route_del.append((route_prefix, vrf_table))
|
|
|
|
for ip, vrf_table in ip_route_del:
|
|
if vrf_table:
|
|
self.__execute_or_batch(
|
|
utils.ip_cmd,
|
|
"route add %s table %s dev %s proto kernel metric 9999" % (ip, vrf_table, macvlan_ifacename)
|
|
)
|
|
else:
|
|
self.__execute_or_batch(
|
|
utils.ip_cmd,
|
|
"route add %s dev %s proto kernel metric 9999" % (ip, macvlan_ifacename)
|
|
)
|
|
|
|
def ip_route_get_dev(self, prefix, vrf_master=None):
|
|
try:
|
|
if vrf_master:
|
|
cmd = "%s route get %s vrf %s" % (utils.ip_cmd, prefix, vrf_master)
|
|
else:
|
|
cmd = "%s route get %s" % (utils.ip_cmd, prefix)
|
|
|
|
output = utils.exec_command(cmd)
|
|
if output:
|
|
rline = output.splitlines()[0]
|
|
if rline:
|
|
rattrs = rline.split()
|
|
return rattrs[rattrs.index("dev") + 1]
|
|
except Exception as e:
|
|
self.logger.debug("ip_route_get_dev: failed .. %s" % str(e))
|
|
return None
|