summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--python/vyos/configdict.py42
-rw-r--r--python/vyos/ifconfig/interface.py48
-rw-r--r--smoketest/scripts/cli/base_interfaces_test.py18
3 files changed, 101 insertions, 7 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..7026223b1 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
@@ -1043,7 +1044,9 @@ class Interface(Control):
# Setting up packet mirroring
ingress_mirror = dict_search('mirror.ingress', self._config)
- if ingress_mirror:
+ # if interface does yet not exist bail out early and
+ # add it later
+ if ingress_mirror and ingress_mirror in interfaces():
# Mirror ingress traffic
mirror_cmd = f'tc qdisc add dev {ifname} handle ffff: ingress'
self._cmd(mirror_cmd)
@@ -1052,7 +1055,9 @@ class Interface(Control):
self._cmd(mirror_cmd)
egress_mirror = dict_search('mirror.egress', self._config)
- if egress_mirror:
+ # if interface does yet not exist bail out early and
+ # add it later
+ if egress_mirror and egress_mirror in interfaces():
# Mirror egress traffic
mirror_cmd = f'tc qdisc add dev {ifname} handle 1: root prio'
self._cmd(mirror_cmd)
@@ -1060,6 +1065,39 @@ class Interface(Control):
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):
+ # 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']
+ mirror_rules = self._config.get('is_monitor_intf')
+
+ # 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)
+ # Export the mirrored traffic to the interface
+ 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
get_config_dict(). It's main intention is to consolidate the scattered
@@ -1227,7 +1265,11 @@ 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:
+ self.apply_mirror_of_monitor()
+
# 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