From 14e5175e9e04a982ef044a0dcdf1484643df1d3a Mon Sep 17 00:00:00 2001 From: Lucas Bates Date: Mon, 8 Jul 2019 21:34:27 -0400 Subject: [PATCH] tc-testing: introduce scapyPlugin for basic traffic The scapyPlugin allows for simple traffic generation in tdc to test various tc features. It was tested with scapy v2.4.2, but should work with any successive version. In order to use the plugin's functionality, scapy must be installed. This can be done with: pip3 install scapy or to install 2.4.2: pip3 install scapy==2.4.2 If the plugin is unable to import the scapy module, it will terminate the tdc run. The plugin makes use of a new key in the test case data, 'scapy'. This block contains three other elements: 'iface', 'count', and 'packet': "scapy": { "iface": "$DEV0", "count": 1, "packet": "Ether(type=0x800)/IP(src='16.61.16.61')/ICMP()" }, * iface is the name of the device on the host machine from which the packet(s) will be sent. Values contained within tdc_config.py's NAMES dict can be used here - this is useful if paired with nsPlugin * count is the number of copies of this packet to be sent * packet is a string detailing the different layers of the packet to be sent. If a property isn't explicitly set, scapy will set default values for you. Layers in the packet info are separated by slashes. For info about common TCP and IP properties, see: https://blogs.sans.org/pen-testing/files/2016/04/ScapyCheatSheet_v0.2.pdf Caution is advised when running tests using the scapy functionality, since the plugin blindly sends the packet as defined in the test case data. See creating-testcases/scapy-example.json for sample test cases; the first test is intended to pass while the second is intended to fail. Signed-off-by: Lucas Bates Signed-off-by: David S. Miller --- .../creating-testcases/scapy-example.json | 98 ++++++++++++++++++++++ .../selftests/tc-testing/plugin-lib/scapyPlugin.py | 50 +++++++++++ 2 files changed, 148 insertions(+) create mode 100644 tools/testing/selftests/tc-testing/creating-testcases/scapy-example.json create mode 100644 tools/testing/selftests/tc-testing/plugin-lib/scapyPlugin.py diff --git a/tools/testing/selftests/tc-testing/creating-testcases/scapy-example.json b/tools/testing/selftests/tc-testing/creating-testcases/scapy-example.json new file mode 100644 index 0000000..5a9377b --- /dev/null +++ b/tools/testing/selftests/tc-testing/creating-testcases/scapy-example.json @@ -0,0 +1,98 @@ +[ + { + "id": "b1e9", + "name": "Test matching of source IP", + "category": [ + "actions", + "scapy" + ], + "plugins": { + "requires": [ + "nsPlugin", + "scapyPlugin" + ] + }, + "setup": [ + [ + "$TC qdisc del dev $DEV1 ingress", + 0, + 1, + 2, + 255 + ], + "$TC qdisc add dev $DEV1 ingress" + ], + "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: prio 3 protocol ip flower src_ip 16.61.16.61 flowid 1:1 action ok", + "scapy": { + "iface": "$DEV0", + "count": 1, + "packet": "Ether(type=0x800)/IP(src='16.61.16.61')/ICMP()" + }, + "expExitCode": "0", + "verifyCmd": "$TC -s -j filter ls dev $DEV1 ingress prio 3", + "matchJSON": [ + { + "path": [ + 1, + "options", + "actions", + 0, + "stats", + "packets" + ], + "value": 1 + } + ], + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "e9c4", + "name": "Test matching of source IP with wrong count", + "category": [ + "actions", + "scapy" + ], + "plugins": { + "requires": [ + "nsPlugin", + "scapyPlugin" + ] + }, + "setup": [ + [ + "$TC qdisc del dev $DEV1 ingress", + 0, + 1, + 2, + 255 + ], + "$TC qdisc add dev $DEV1 ingress" + ], + "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: prio 3 protocol ip flower src_ip 16.61.16.61 flowid 1:1 action ok", + "scapy": { + "iface": "$DEV0", + "count": 3, + "packet": "Ether(type=0x800)/IP(src='16.61.16.61')/ICMP()" + }, + "expExitCode": "0", + "verifyCmd": "$TC -s -j filter ls dev $DEV1 parent ffff:", + "matchJSON": [ + { + "path": [ + 1, + "options", + "actions", + 0, + "stats", + "packets" + ], + "value": 1 + } + ], + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + } +] diff --git a/tools/testing/selftests/tc-testing/plugin-lib/scapyPlugin.py b/tools/testing/selftests/tc-testing/plugin-lib/scapyPlugin.py new file mode 100644 index 0000000..229ee18 --- /dev/null +++ b/tools/testing/selftests/tc-testing/plugin-lib/scapyPlugin.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 + +import os +import signal +from string import Template +import subprocess +import time +from TdcPlugin import TdcPlugin + +from tdc_config import * + +try: + from scapy.all import * +except ImportError: + print("Unable to import the scapy python module.") + print("\nIf not already installed, you may do so with:") + print("\t\tpip3 install scapy==2.4.2") + exit(1) + +class SubPlugin(TdcPlugin): + def __init__(self): + self.sub_class = 'scapy/SubPlugin' + super().__init__() + + def post_execute(self): + if 'scapy' not in self.args.caseinfo: + if self.args.verbose: + print('{}.post_execute: no scapy info in test case'.format(self.sub_class)) + return + + # Check for required fields + scapyinfo = self.args.caseinfo['scapy'] + scapy_keys = ['iface', 'count', 'packet'] + missing_keys = [] + keyfail = False + for k in scapy_keys: + if k not in scapyinfo: + keyfail = True + missing_keys.add(k) + if keyfail: + print('{}: Scapy block present in the test, but is missing info:' + .format(self.sub_class)) + print('{}'.format(missing_keys)) + + pkt = eval(scapyinfo['packet']) + if '$' in scapyinfo['iface']: + tpl = Template(scapyinfo['iface']) + scapyinfo['iface'] = tpl.safe_substitute(NAMES) + for count in range(scapyinfo['count']): + sendp(pkt, iface=scapyinfo['iface']) -- 2.7.4