From e08b381d07b322b8bf5daebbf2ca1c977c14d408 Mon Sep 17 00:00:00 2001 From: jack9603301 Date: Sun, 13 Dec 2020 17:03:01 +0800 Subject: interfaces: mirror: T3089: Fix the dependency problem between interfaces Since the dependency problem has not been solved before, if the monitoring interface does not exist when the mirror rule is created, the execution will be abnormal --- python/vyos/configdict.py | 42 ++++++++++++++++ python/vyos/ifconfig/interface.py | 70 ++++++++++++++++++++++----- smoketest/scripts/cli/base_interfaces_test.py | 18 +++++-- 3 files changed, 113 insertions(+), 17 deletions(-) diff --git a/python/vyos/configdict.py b/python/vyos/configdict.py index 99c1ae2e4..b299f4a18 100644 --- a/python/vyos/configdict.py +++ b/python/vyos/configdict.py @@ -221,6 +221,44 @@ def is_member(conf, interface, intftype=None): old_level = conf.set_level(old_level) return ret_val +def is_monitor_intf(conf,interface,direction=None): + """ + Check whether the passed interface is the mirror monitoring interface of + other interfaces of the specified type. + direction is optional, if not passed it will search all known direction + (currently ingress and egress) + + Returns: + None -> Interface is not a monitor interface + Array() -> This interface is a monitor interface of interfaces + """ + + directions = ['ingress', 'egress'] + if direction not in directions + [None]: + raise ValueError(f'unknown interface mirror direction "{direction}"') + + direction = directions if direction == None else [direction] + + ret_val = [] + old_level = conf.get_level() + conf.set_level([]) + base = ['interfaces'] + + for dire in direction: + for iftype in conf.list_nodes(base): + iftype_base = base + [iftype] + for intf in conf.list_nodes(iftype_base): + mirror = iftype_base + [intf, 'mirror', dire, interface] + if conf.exists(mirror): + ret_val.append({intf : dire}) + + old_level = conf.set_level(old_level) + if len(ret_val) == 0: + return None + else: + return ret_val + + def has_vlan_subinterface_configured(conf, intf): """ Checks if interface has an VLAN subinterface configured. @@ -334,6 +372,10 @@ def get_interface_dict(config, base, ifname=''): # Check if we are a member of a bridge device bridge = is_member(config, ifname, 'bridge') if bridge: dict.update({'is_bridge_member' : bridge}) + + # Check if it is a monitor interface + mirror = is_monitor_intf(config, ifname) + if mirror: dict.update({'is_monitor_intf' : mirror}) # Check if we are a member of a bond device bond = is_member(config, ifname, 'bonding') diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py index e3c6beb8f..bf10b4440 100644 --- a/python/vyos/ifconfig/interface.py +++ b/python/vyos/ifconfig/interface.py @@ -13,6 +13,7 @@ # You should have received a copy of the GNU Lesser General Public # License along with this library. If not, see . +from netifaces import interfaces import os import re import json @@ -1044,21 +1045,59 @@ class Interface(Control): # Setting up packet mirroring ingress_mirror = dict_search('mirror.ingress', self._config) if ingress_mirror: - # Mirror ingress traffic - mirror_cmd = f'tc qdisc add dev {ifname} handle ffff: ingress' - self._cmd(mirror_cmd) - # Export the mirrored traffic to the interface - mirror_cmd = f'tc filter add dev {ifname} parent ffff: protocol all prio 10 u32 match u32 0 0 flowid 1:1 action mirred egress mirror dev {ingress_mirror}' - self._cmd(mirror_cmd) + # if interface does yet not exist bail out early and + # add it later + if ingress_mirror in interfaces(): + # Mirror ingress traffic + mirror_cmd = f'tc qdisc add dev {ifname} handle ffff: ingress' + self._cmd(mirror_cmd) + # Export the mirrored traffic to the interface + mirror_cmd = f'tc filter add dev {ifname} parent ffff: protocol all prio 10 u32 match u32 0 0 flowid 1:1 action mirred egress mirror dev {ingress_mirror}' + self._cmd(mirror_cmd) egress_mirror = dict_search('mirror.egress', self._config) if egress_mirror: - # Mirror egress traffic - mirror_cmd = f'tc qdisc add dev {ifname} handle 1: root prio' - self._cmd(mirror_cmd) - # Export the mirrored traffic to the interface - mirror_cmd = f'tc filter add dev {ifname} parent 1: protocol all prio 10 u32 match u32 0 0 flowid 1:1 action mirred egress mirror dev {egress_mirror}' - self._cmd(mirror_cmd) + # if interface does yet not exist bail out early and + # add it later + if egress_mirror in interfaces(): + # Mirror egress traffic + mirror_cmd = f'tc qdisc add dev {ifname} handle 1: root prio' + self._cmd(mirror_cmd) + # Export the mirrored traffic to the interface + mirror_cmd = f'tc filter add dev {ifname} parent 1: protocol all prio 10 u32 match u32 0 0 flowid 1:1 action mirred egress mirror dev {egress_mirror}' + self._cmd(mirror_cmd) + + def apply_mirror_of_monitor(self,mirror_rules): + # Please refer to the document for details + # https://man7.org/linux/man-pages/man8/tc.8.html + # https://man7.org/linux/man-pages/man8/tc-mirred.8.html + ifname = self._config['ifname'] + + # Remove existing mirroring rules + # The rule must be completely deleted first + for rule in mirror_rules: + for intf, dire in rule.items(): + self.del_tc_qdisc(intf,'ingress','ffff:') + self.del_tc_qdisc(intf,'prio','1:') + + # Setting mirror rules + for rule in mirror_rules: + for intf, dire in rule.items(): + # Setting up packet mirroring + if dire == "ingress": + # Mirror ingress traffic + mirror_cmd = f'tc qdisc add dev {intf} handle ffff: ingress' + self._cmd(mirror_cmd) + # Mirror ingress traffic + mirror_cmd = f'tc filter add dev {intf} parent ffff: protocol all prio 10 u32 match u32 0 0 flowid 1:1 action mirred egress mirror dev {ifname}' + self._cmd(mirror_cmd) + elif dire == "egress": + # Mirror egress traffic + mirror_cmd = f'tc qdisc add dev {intf} handle 1: root prio' + self._cmd(mirror_cmd) + # Export the mirrored traffic to the interface + mirror_cmd = f'tc filter add dev {intf} parent 1: protocol all prio 10 u32 match u32 0 0 flowid 1:1 action mirred egress mirror dev {ifname}' + self._cmd(mirror_cmd) def update(self, config): """ General helper function which works on a dictionary retrived by @@ -1227,7 +1266,12 @@ class Interface(Control): if 'is_bridge_member' in config: bridge_dict = config.get('is_bridge_member') self.add_to_bridge(bridge_dict) - + + # Re-set rules for the mirror monitoring interface + if 'is_monitor_intf' in config: + mirror_rules = config.get('is_monitor_intf') + self.apply_mirror_of_monitor(mirror_rules) + # remove no longer required 802.1ad (Q-in-Q VLANs) ifname = config['ifname'] for vif_s_id in config.get('vif_s_remove', {}): diff --git a/smoketest/scripts/cli/base_interfaces_test.py b/smoketest/scripts/cli/base_interfaces_test.py index d1bb9c3fe..cef920f04 100644 --- a/smoketest/scripts/cli/base_interfaces_test.py +++ b/smoketest/scripts/cli/base_interfaces_test.py @@ -86,13 +86,21 @@ class BasicInterfaceTest: del self.session def test_mirror(self): - Success = 0 - i = 0 + if self._test_mirror: + + # Create test dependency interface + self.session.set(['interfaces','dummy','dum0']) + self.session.set(['interfaces','dummy','dum1']) + self.session.set(['interfaces','bonding','bond1','member','interface','dum0']) + self.session.set(['interfaces','bonding','bond1','member','interface','dum1']) + + Success = 0 + i = 0 # Check the two-way mirror rules of ingress and egress for interface in self._interfaces: - self.session.set(self._base_path + [interface, 'mirror', 'ingress', 'lo']) - self.session.set(self._base_path + [interface, 'mirror', 'egress', 'lo']) + self.session.set(self._base_path + [interface, 'mirror', 'ingress', 'bond1']) + self.session.set(self._base_path + [interface, 'mirror', 'egress', 'bond1']) i+=1 self.session.commit() # Parse configuration @@ -102,6 +110,8 @@ class BasicInterfaceTest: else: self.assertTrue(False) i=0 + self.session.delete(['interfaces','dummy']) + self.session.delete(['interfaces','bonding']) else: return None -- cgit v1.2.3