summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjack9603301 <jack9603301@163.com>2020-12-13 17:03:01 +0800
committerjack9603301 <jack9603301@163.com>2020-12-13 17:03:01 +0800
commite08b381d07b322b8bf5daebbf2ca1c977c14d408 (patch)
tree184366bd656c34fb4c27510f8b0f6f08b8743c3c
parent505c5efaf1e852a3e90276baacdb235c4c3e41b2 (diff)
downloadvyos-1x-e08b381d07b322b8bf5daebbf2ca1c977c14d408.tar.gz
vyos-1x-e08b381d07b322b8bf5daebbf2ca1c977c14d408.zip
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
-rw-r--r--python/vyos/configdict.py42
-rw-r--r--python/vyos/ifconfig/interface.py70
-rw-r--r--smoketest/scripts/cli/base_interfaces_test.py18
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