diff options
-rw-r--r-- | python/vyos/configdict.py | 42 | ||||
-rw-r--r-- | python/vyos/ifconfig/interface.py | 70 | ||||
-rw-r--r-- | 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 <http://www.gnu.org/licenses/>. +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 |