summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Poessinger <christian@poessinger.com>2020-12-08 11:27:49 +0100
committerGitHub <noreply@github.com>2020-12-08 11:27:49 +0100
commit2a25efce5f3e03385a4fa1a412d6f41a07e9d483 (patch)
treec1d55aa093c69fd92e934e26bca6f8a7d5caff61
parent7ef0840d464205964314c97e4335a2fcf0ca0532 (diff)
parente8957b575b050b075b74c94c3352d253414f4a6f (diff)
downloadvyos-1x-2a25efce5f3e03385a4fa1a412d6f41a07e9d483.tar.gz
vyos-1x-2a25efce5f3e03385a4fa1a412d6f41a07e9d483.zip
Merge pull request #633 from jack9603301/T3089
mirror: T3089: support two-way traffic mirroring
-rw-r--r--interface-definitions/include/interface-mirror.xml.i26
-rw-r--r--python/vyos/ifconfig/interface.py49
-rw-r--r--smoketest/scripts/cli/base_interfaces_test.py34
-rwxr-xr-xsrc/migration-scripts/interfaces/16-to-1752
4 files changed, 126 insertions, 35 deletions
diff --git a/interface-definitions/include/interface-mirror.xml.i b/interface-definitions/include/interface-mirror.xml.i
index e3720cde7..d34132a9c 100644
--- a/interface-definitions/include/interface-mirror.xml.i
+++ b/interface-definitions/include/interface-mirror.xml.i
@@ -1,11 +1,25 @@
<!-- included start from interface-mirror.xml.i -->
-<leafNode name="mirror">
+<node name="mirror">
<properties>
<help>Incoming/outgoing packet mirroring destination</help>
- <completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
- </completionHelp>
- <multi/>
</properties>
-</leafNode>
+ <children>
+ <leafNode name="ingress">
+ <properties>
+ <help>Mirror the ingress traffic of the interface to the destination interface</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_interfaces.py</script>
+ </completionHelp>
+ </properties>
+ </leafNode>
+ <leafNode name="egress">
+ <properties>
+ <help>Mirror the egress traffic of the interface to the destination interface</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_interfaces.py</script>
+ </completionHelp>
+ </properties>
+ </leafNode>
+ </children>
+</node>
<!-- included end -->
diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py
index 24f60efb8..6e6a83f36 100644
--- a/python/vyos/ifconfig/interface.py
+++ b/python/vyos/ifconfig/interface.py
@@ -977,25 +977,40 @@ class Interface(Control):
old_handle = rule['handle']
old_kind = rule['kind']
if old_dev == dev and old_handle == handle and old_kind == kind:
- delete_tc_cmd = f'tc qdisc del dev {dev} handle {handle} {kind}'
- self._cmd(delete_tc_cmd)
-
-
-
- def apply_mirror(self,config):
- ifname = config['ifname']
-
+ if 'root' in rule and rule['root']:
+ delete_tc_cmd = f'tc qdisc del dev {dev} handle {handle} root {kind}'
+ self._cmd(delete_tc_cmd)
+ else:
+ delete_tc_cmd = f'tc qdisc del dev {dev} handle {handle} {kind}'
+ self._cmd(delete_tc_cmd)
+
+ def apply_mirror(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']
# Remove existing mirroring rules
self.del_tc_qdisc(ifname,'ingress','ffff:')
-
+ self.del_tc_qdisc(ifname,'prio','1:')
+
# Setting up packet mirroring
- mirror = dict_search('mirror', config)
- if mirror:
- for interface in mirror:
- mirror_cmd = f'tc qdisc add dev {ifname} handle ffff: ingress'
- self._cmd(mirror_cmd)
- 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 {interface}'
- self._cmd(mirror_cmd)
+ 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)
+
+ 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)
def update(self, config):
""" General helper function which works on a dictionary retrived by
@@ -1215,7 +1230,7 @@ class Interface(Control):
vlan = VLANIf(vif_ifname, **tmp)
vlan.update(vif_config)
- self.apply_mirror(config)
+ self.apply_mirror()
diff --git a/smoketest/scripts/cli/base_interfaces_test.py b/smoketest/scripts/cli/base_interfaces_test.py
index 4187fd77c..d1bb9c3fe 100644
--- a/smoketest/scripts/cli/base_interfaces_test.py
+++ b/smoketest/scripts/cli/base_interfaces_test.py
@@ -28,6 +28,22 @@ from vyos.util import dict_search
from vyos.validate import is_intf_addr_assigned
from vyos.validate import is_ipv6_link_local
+def read_mirror_rule(interfaces):
+ Success = 0
+ for interface in interfaces:
+ get_tc_cmd = 'tc -j qdisc'
+ tmp = cmd(get_tc_cmd, shell=True)
+ data = json.loads(tmp)
+ for rule in data:
+ dev = rule['dev']
+ handle = rule['handle']
+ kind = rule['kind']
+ if dev == interface and handle == "ffff:" and kind == "ingress":
+ Success+=1
+ elif dev == interface and handle == "1:" and kind == "prio":
+ Success+=1
+ return Success
+
class BasicInterfaceTest:
class BaseTest(unittest.TestCase):
_test_ip = False
@@ -73,25 +89,19 @@ class BasicInterfaceTest:
Success = 0
i = 0
if self._test_mirror:
+ # Check the two-way mirror rules of ingress and egress
for interface in self._interfaces:
- self.session.set(self._base_path + [interface, 'mirror', 'lo'])
+ self.session.set(self._base_path + [interface, 'mirror', 'ingress', 'lo'])
+ self.session.set(self._base_path + [interface, 'mirror', 'egress', 'lo'])
i+=1
self.session.commit()
# Parse configuration
- for interface in self._interfaces:
- get_tc_cmd = 'tc -j qdisc'
- tmp = cmd(get_tc_cmd, shell=True)
- data = json.loads(tmp)
- for rule in data:
- dev = rule['dev']
- handle = rule['handle']
- kind = rule['kind']
- if dev == interface and handle == "ffff:" and kind == "ingress":
- Success+=1
- if Success == i:
+ Success = read_mirror_rule(self._interfaces)
+ if Success == i*2:
self.assertTrue(True)
else:
self.assertTrue(False)
+ i=0
else:
return None
diff --git a/src/migration-scripts/interfaces/16-to-17 b/src/migration-scripts/interfaces/16-to-17
new file mode 100755
index 000000000..a6b4c7663
--- /dev/null
+++ b/src/migration-scripts/interfaces/16-to-17
@@ -0,0 +1,52 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2020 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# 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, see <http://www.gnu.org/licenses/>.
+
+# Command line migration of port mirroring
+# https://phabricator.vyos.net/T3089
+
+import sys
+from vyos.configtree import ConfigTree
+
+if __name__ == '__main__':
+ if (len(sys.argv) < 1):
+ print("Must specify file name!")
+ sys.exit(1)
+
+ file_name = sys.argv[1]
+
+ with open(file_name, 'r') as f:
+ config_file = f.read()
+
+ config = ConfigTree(config_file)
+ base = ['interfaces', 'ethernet']
+ if not config.exists(base):
+ # Nothing to do
+ sys.exit(0)
+
+ for interface in config.list_nodes(base):
+ mirror_old_base = base + [interface, 'mirror']
+ if config.exists(mirror_old_base):
+ intf = config.return_values(mirror_old_base)
+ if config.exists(mirror_old_base):
+ config.delete(mirror_old_base)
+ config.set(mirror_old_base + ['ingress'],intf[0])
+
+ try:
+ with open(file_name, 'w') as f:
+ f.write(config.to_string())
+ except OSError as e:
+ print("Failed to save the modified config: {}".format(e))
+ sys.exit(1)