diff options
Diffstat (limited to 'tests/conntrackd')
-rwxr-xr-x | tests/conntrackd/conntrackd-tests.py | 263 | ||||
-rw-r--r-- | tests/conntrackd/env.yaml | 2 | ||||
-rwxr-xr-x | tests/conntrackd/netns/conntrackd-netns-test.sh | 66 | ||||
-rw-r--r-- | tests/conntrackd/netns/conntrackd-nsr1.conf | 37 | ||||
-rw-r--r-- | tests/conntrackd/netns/conntrackd-nsr2.conf | 37 | ||||
-rw-r--r-- | tests/conntrackd/netns/ruleset-nsr1.nft | 6 | ||||
-rw-r--r-- | tests/conntrackd/scenarios.yaml | 100 | ||||
-rwxr-xr-x | tests/conntrackd/scenarios/basic/network-setup.sh | 59 | ||||
-rw-r--r-- | tests/conntrackd/tests.yaml | 83 |
9 files changed, 653 insertions, 0 deletions
diff --git a/tests/conntrackd/conntrackd-tests.py b/tests/conntrackd/conntrackd-tests.py new file mode 100755 index 0000000..f760351 --- /dev/null +++ b/tests/conntrackd/conntrackd-tests.py @@ -0,0 +1,263 @@ +#!/usr/bin/env python3 + +# (C) 2021 by Arturo Borrero Gonzalez <arturo@netfilter.org> + +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# + +# tests.yaml file format: +# - name: "test 1" +# scenario: scenario1 +# test: +# - test1 cmd1 +# - test1 cmd2 + +# scenarios.yaml file format: +# - name: scenario1 +# start: +# - cmd1 +# - cmd2 +# stop: +# - cmd1 +# - cmd2 + +# env.yaml file format: +# - VAR1: value1 +# - VAR2: value2 + +import os +import sys +import argparse +import subprocess +import yaml +import logging + + +def read_yaml_file(file): + try: + with open(file, "r") as stream: + try: + return yaml.safe_load(stream) + except yaml.YAMLError as e: + logging.error(e) + exit(2) + except FileNotFoundError as e: + logging.error(e) + exit(2) + + +def validate_dictionary(dictionary, keys): + if not isinstance(dictionary, dict): + logging.error("not a dictionary:\n{}".format(dictionary)) + return False + for key in keys: + if dictionary.get(key) is None: + logging.error("missing key {} in dictionary:\n{}".format(key, dictionary)) + return False + return True + + +def stage_validate_config(args): + scenarios_dict = read_yaml_file(args.scenarios_file) + for definition in scenarios_dict: + if not validate_dictionary(definition, ["name", "start", "stop"]): + logging.error("couldn't validate file {}".format(args.scenarios_file)) + return False + + logging.debug("{} seems valid".format(args.scenarios_file)) + ctx.scenarios_dict = scenarios_dict + + tests_dict = read_yaml_file(args.tests_file) + for definition in tests_dict: + if not validate_dictionary(definition, ["name", "scenario", "test"]): + logging.error("couldn't validate file {}".format(args.tests_file)) + return False + + logging.debug("{} seems valid".format(args.tests_file)) + ctx.tests_dict = tests_dict + + env_list = read_yaml_file(args.env_file) + if not isinstance(env_list, list): + logging.error("couldn't validate file {}".format(args.env_file)) + return False + + # set env to default values if not overridden when calling this very script + for entry in env_list: + for key in entry: + os.environ[key] = os.getenv(key, entry[key]) + + +def cmd_run(cmd): + logging.debug("running command: {}".format(cmd)) + r = subprocess.run(cmd, shell=True) + if r.returncode != 0: + logging.warning("failed command: {}".format(cmd)) + return r.returncode + + +def scenario_get(name): + for n in ctx.scenarios_dict: + if n["name"] == name: + return n + + logging.error("couldn't find a definition for scenario '{}'".format(name)) + exit(1) + + +def scenario_start(scenario): + for cmd in scenario["start"]: + if cmd_run(cmd) == 0: + continue + + logging.warning("--- failed scenario: {}".format(scenario["name"])) + ctx.counter_scenario_failed += 1 + ctx.skip_current_test = True + return + + +def scenario_stop(scenario): + for cmd in scenario["stop"]: + cmd_run(cmd) + + +def test_get(name): + for n in ctx.tests_dict: + if n["name"] == name: + return n + + logging.error("couldn't find a definition for test '{}'".format(name)) + exit(1) + + +def _test_run(test_definition): + if ctx.skip_current_test: + return + + for cmd in test_definition["test"]: + if cmd_run(cmd) == 0: + continue + + logging.warning("--- failed test: {}".format(test_definition["name"])) + ctx.counter_test_failed += 1 + return + + logging.info("--- passed test: {}".format(test_definition["name"])) + ctx.counter_test_ok += 1 + + +def test_run(test_definition): + scenario = scenario_get(test_definition["scenario"]) + + logging.info("--- running test: {}".format(test_definition["name"])) + + scenario_start(scenario) + _test_run(test_definition) + scenario_stop(scenario) + + +def stage_run_tests(args): + if args.start_scenario: + scenario_start(scenario_get(args.start_scenario)) + return + + if args.stop_scenario: + scenario_stop(scenario_get(args.stop_scenario)) + return + + if args.single: + test_run(test_get(args.single)) + return + + for test_definition in ctx.tests_dict: + ctx.skip_current_test = False + test_run(test_definition) + + +def stage_report(): + logging.info("---") + logging.info("--- finished") + total = ctx.counter_test_ok + ctx.counter_test_failed + ctx.counter_scenario_failed + logging.info("--- passed tests: {}".format(ctx.counter_test_ok)) + logging.info("--- failed tests: {}".format(ctx.counter_test_failed)) + logging.info("--- scenario failure: {}".format(ctx.counter_scenario_failed)) + logging.info("--- total tests: {}".format(total)) + + +def parse_args(): + description = "Utility to run tests for conntrack-tools" + parser = argparse.ArgumentParser(description=description) + parser.add_argument( + "--tests-file", + default="tests.yaml", + help="File with testcase definitions. Defaults to '%(default)s'", + ) + parser.add_argument( + "--scenarios-file", + default="scenarios.yaml", + help="File with configuration scenarios for tests. Defaults to '%(default)s'", + ) + parser.add_argument( + "--env-file", + default="env.yaml", + help="File with environment variables for scenarios/tests. Defaults to '%(default)s'", + ) + parser.add_argument( + "--single", + help="Execute a single testcase and exit. Use this for developing testcases", + ) + parser.add_argument( + "--start-scenario", + help="Execute scenario start commands and exit. Use this for developing testcases", + ) + parser.add_argument( + "--stop-scenario", + help="Execute scenario stop commands and exit. Use this for cleanup", + ) + parser.add_argument( + "--debug", + action="store_true", + help="debug mode", + ) + + return parser.parse_args() + + +class Context: + def __init__(self): + self.scenarios_dict = None + self.tests_dict = None + self.counter_test_failed = 0 + self.counter_test_ok = 0 + self.counter_scenario_failed = 0 + self.skip_current_test = False + + +# global data +ctx = Context() + + +def main(): + args = parse_args() + + logging_format = "[%(filename)s] %(levelname)s: %(message)s" + if args.debug: + logging_level = logging.DEBUG + else: + logging_level = logging.INFO + logging.basicConfig(format=logging_format, level=logging_level, stream=sys.stdout) + + if os.geteuid() != 0: + logging.error("root required") + exit(1) + + stage_validate_config(args) + stage_run_tests(args) + stage_report() + + +if __name__ == "__main__": + main() diff --git a/tests/conntrackd/env.yaml b/tests/conntrackd/env.yaml new file mode 100644 index 0000000..daf8ea1 --- /dev/null +++ b/tests/conntrackd/env.yaml @@ -0,0 +1,2 @@ +- CONNTRACKD: ../../src/conntrackd +- CONNTRACK: ../../src/conntrack diff --git a/tests/conntrackd/netns/conntrackd-netns-test.sh b/tests/conntrackd/netns/conntrackd-netns-test.sh new file mode 100755 index 0000000..6f16587 --- /dev/null +++ b/tests/conntrackd/netns/conntrackd-netns-test.sh @@ -0,0 +1,66 @@ +#!/bin/bash + +if [ $UID -ne 0 ] +then + echo "You must be root to run this test script" + exit 0 +fi + +start () { + ip netns add ns1 + ip netns add ns2 + ip netns add nsr1 + ip netns add nsr2 + + ip link add veth0 netns ns1 type veth peer name veth1 netns nsr1 + ip link add veth0 netns nsr1 type veth peer name veth0 netns ns2 + ip link add veth2 netns nsr1 type veth peer name veth0 netns nsr2 + + ip -net ns1 addr add 192.168.10.2/24 dev veth0 + ip -net ns1 link set up dev veth0 + ip -net ns1 ro add 10.0.1.0/24 via 192.168.10.1 dev veth0 + + ip -net nsr1 addr add 10.0.1.1/24 dev veth0 + ip -net nsr1 addr add 192.168.10.1/24 dev veth1 + ip -net nsr1 link set up dev veth0 + ip -net nsr1 link set up dev veth1 + ip -net nsr1 route add default via 192.168.10.2 + ip netns exec nsr1 sysctl net.ipv4.ip_forward=1 + + ip -net nsr1 addr add 192.168.100.2/24 dev veth2 + ip -net nsr1 link set up dev veth2 + ip -net nsr2 addr add 192.168.100.3/24 dev veth0 + ip -net nsr2 link set up dev veth0 + + ip -net ns2 addr add 10.0.1.2/24 dev veth0 + ip -net ns2 link set up dev veth0 + ip -net ns2 route add default via 10.0.1.1 + + echo 1 > /proc/sys/net/netfilter/nf_log_all_netns + + ip netns exec nsr1 nft -f ruleset-nsr1.nft + ip netns exec nsr1 conntrackd -C conntrackd-nsr1.conf -d + ip netns exec nsr2 conntrackd -C conntrackd-nsr2.conf -d +} + +stop () { + ip netns del ns1 + ip netns del ns2 + ip netns del nsr1 + ip netns del nsr2 + killall -15 conntrackd +} + +case $1 in +start) + start + ;; +stop) + stop + ;; +*) + echo "$0 [start|stop]" + ;; +esac + +exit 0 diff --git a/tests/conntrackd/netns/conntrackd-nsr1.conf b/tests/conntrackd/netns/conntrackd-nsr1.conf new file mode 100644 index 0000000..c79eff5 --- /dev/null +++ b/tests/conntrackd/netns/conntrackd-nsr1.conf @@ -0,0 +1,37 @@ +Sync { + Mode FTFW { + } + Multicast { + IPv4_address 225.0.0.50 + Group 3780 + IPv4_interface 192.168.100.2 + Interface veth2 + SndSocketBuffer 1249280 + RcvSocketBuffer 1249280 + Checksum on + } +} +General { + HashSize 32768 + HashLimit 131072 + LogFile on + LockFile /var/lock/conntrack-nsr1.lock + UNIX { + Path /var/run/conntrackd-nsr1.ctl + } + NetlinkBufferSize 2097152 + NetlinkBufferSizeMaxGrowth 8388608 + Filter From Userspace { + Protocol Accept { + TCP + SCTP + DCCP + } + Address Ignore { + IPv4_address 127.0.0.1 + IPv4_address 192.168.10.1 + IPv4_address 10.0.10.1 + IPv4_address 192.168.100.2 + } + } +} diff --git a/tests/conntrackd/netns/conntrackd-nsr2.conf b/tests/conntrackd/netns/conntrackd-nsr2.conf new file mode 100644 index 0000000..65fa0d6 --- /dev/null +++ b/tests/conntrackd/netns/conntrackd-nsr2.conf @@ -0,0 +1,37 @@ +Sync { + Mode FTFW { + } + Multicast { + IPv4_address 225.0.0.50 + Group 3780 + IPv4_interface 192.168.100.3 + Interface veth0 + SndSocketBuffer 1249280 + RcvSocketBuffer 1249280 + Checksum on + } +} +General { + HashSize 32768 + HashLimit 131072 + LogFile on + LockFile /var/lock/conntrack-nsr2.lock + UNIX { + Path /var/run/conntrackd-nsr2.ctl + } + NetlinkBufferSize 2097152 + NetlinkBufferSizeMaxGrowth 8388608 + Filter From Userspace { + Protocol Accept { + TCP + SCTP + DCCP + } + Address Ignore { + IPv4_address 127.0.0.1 + IPv4_address 192.168.10.1 + IPv4_address 10.0.10.1 + IPv4_address 192.168.100.2 + } + } +} diff --git a/tests/conntrackd/netns/ruleset-nsr1.nft b/tests/conntrackd/netns/ruleset-nsr1.nft new file mode 100644 index 0000000..bd6f1b4 --- /dev/null +++ b/tests/conntrackd/netns/ruleset-nsr1.nft @@ -0,0 +1,6 @@ +table ip filter { + chain postrouting { + type nat hook postrouting priority srcnat; policy accept; + oif veth0 masquerade + } +} diff --git a/tests/conntrackd/scenarios.yaml b/tests/conntrackd/scenarios.yaml new file mode 100644 index 0000000..65d6fa4 --- /dev/null +++ b/tests/conntrackd/scenarios.yaml @@ -0,0 +1,100 @@ +- name: empty + start: + - ":" + stop: + - ":" +- name: simple_stats + start: + - rm -f /var/lock/conntrack.lock + - | + cat << EOF > /tmp/conntrackd_test_simple_stats + General { + HashSize 8192 + LockFile /var/lock/conntrack.lock + UNIX { Path /var/run/conntrackd.ctl } + } + Stats { + LogFile on + } + EOF + - $CONNTRACKD -C /tmp/conntrackd_test_simple_stats -d + stop: + - $CONNTRACKD -C /tmp/conntrackd_test_simple_stats -k + - rm -f /var/lock/conntrack.lock + - rm -f /tmp/conntrackd_test_simple_stats + +- name: basic_2_peer_network_tcp_notrack + start: + - scenarios/basic/./network-setup.sh start + - | + cat << EOF > /tmp/ruleset.nft + table ip filter { + chain postrouting { + type nat hook postrouting priority srcnat; policy accept; + oif veth0 masquerade + } + } + EOF + - ip netns exec nsr1 nft -f /tmp/ruleset.nft + - | + cat << EOF > /tmp/nsr1.conf + Sync { + Mode NOTRACK { + DisableExternalCache on + DisableInternalCache on + } + TCP { + IPv4_address 192.168.100.2 + IPv4_Destination_Address 192.168.100.3 + Interface veth2 + Port 3780 + } + } + General { + LogFile on + LockFile /var/lock/conntrack-nsr1.lock + UNIX { Path /var/run/conntrackd-nsr1.ctl } + } + EOF + - | + cat << EOF > /tmp/nsr2.conf + Sync { + Mode NOTRACK { + DisableExternalCache on + DisableInternalCache on + } + TCP { + IPv4_address 192.168.100.3 + IPv4_Destination_Address 192.168.100.2 + Interface veth0 + Port 3780 + } + } + General { + LogFile on + LockFile /var/lock/conntrack-nsr2.lock + UNIX { Path /var/run/conntrackd-nsr2.ctl } + } + EOF + # finally run the daemons + - ip netns exec nsr1 $CONNTRACKD -C /tmp/nsr1.conf -d + - ip netns exec nsr2 $CONNTRACKD -C /tmp/nsr2.conf -d + # make sure they are alive and connected before considering the scenario started + - timeout 5 bash -c -- ' + while ! ip netns exec nsr1 $CONNTRACKD -C /tmp/nsr1.conf -s | grep -q "server=connected" + ; do sleep 0.5 ; done' + - timeout 5 bash -c -- ' + while ! ip netns exec nsr1 $CONNTRACKD -C /tmp/nsr1.conf -s | grep -q "client=connected" + ; do sleep 0.5 ; done' + - timeout 5 bash -c -- ' + while ! ip netns exec nsr2 $CONNTRACKD -C /tmp/nsr2.conf -s | grep -q "server=connected" + ; do sleep 0.5 ; done' + - timeout 5 bash -c -- ' + while ! ip netns exec nsr2 $CONNTRACKD -C /tmp/nsr2.conf -s | grep -q "client=connected" + ; do sleep 0.5 ; done' + stop: + - $CONNTRACKD -C /tmp/nsr1.conf -k 2>/dev/null + - $CONNTRACKD -C /tmp/nsr2.conf -k 2>/dev/null + - rm -f /tmp/ruleset.nft /tmp/nsr2.conf /tmp/nsr1.conf + - rm -f /var/lock/conntrack-nsr1.lock /var/lock/conntrack-nsr2.lock + - scenarios/basic/./network-setup.sh stop diff --git a/tests/conntrackd/scenarios/basic/network-setup.sh b/tests/conntrackd/scenarios/basic/network-setup.sh new file mode 100755 index 0000000..7f2f78a --- /dev/null +++ b/tests/conntrackd/scenarios/basic/network-setup.sh @@ -0,0 +1,59 @@ +#!/bin/bash + +if [ $UID -ne 0 ] +then + echo "You must be root to run this test script" + exit 0 +fi + +start () { + ip netns add ns1 + ip netns add ns2 + ip netns add nsr1 + ip netns add nsr2 + + ip link add veth0 netns ns1 type veth peer name veth1 netns nsr1 + ip link add veth0 netns nsr1 type veth peer name veth0 netns ns2 + ip link add veth2 netns nsr1 type veth peer name veth0 netns nsr2 + + ip -net ns1 addr add 192.168.10.2/24 dev veth0 + ip -net ns1 link set up dev veth0 + ip -net ns1 ro add 10.0.1.0/24 via 192.168.10.1 dev veth0 + + ip -net nsr1 addr add 10.0.1.1/24 dev veth0 + ip -net nsr1 addr add 192.168.10.1/24 dev veth1 + ip -net nsr1 link set up dev veth0 + ip -net nsr1 link set up dev veth1 + ip -net nsr1 route add default via 192.168.10.2 + ip netns exec nsr1 sysctl -q net.ipv4.ip_forward=1 + + ip -net nsr1 addr add 192.168.100.2/24 dev veth2 + ip -net nsr1 link set up dev veth2 + ip -net nsr2 addr add 192.168.100.3/24 dev veth0 + ip -net nsr2 link set up dev veth0 + + ip -net ns2 addr add 10.0.1.2/24 dev veth0 + ip -net ns2 link set up dev veth0 + ip -net ns2 route add default via 10.0.1.1 +} + +stop () { + ip netns del ns1 + ip netns del ns2 + ip netns del nsr1 + ip netns del nsr2 +} + +case $1 in +start) + start + ;; +stop) + stop + ;; +*) + echo "$0 [start|stop]" + ;; +esac + +exit 0 diff --git a/tests/conntrackd/tests.yaml b/tests/conntrackd/tests.yaml new file mode 100644 index 0000000..307f38f --- /dev/null +++ b/tests/conntrackd/tests.yaml @@ -0,0 +1,83 @@ +- name: stats_general + scenario: simple_stats + # check that we can obtain stats via unix socket: general + test: + - $CONNTRACKD -C /tmp/conntrackd_test_simple_stats -s | grep -q "cache stats" + +- name: stats_network + scenario: simple_stats + # check that we can obtain stats via unix socket: network (no output) + test: + - $CONNTRACKD -C /tmp/conntrackd_test_simple_stats -s network + +- name: stats_runtime + scenario: simple_stats + # check that we can obtain stats via unix socket: runtime + test: + - $CONNTRACKD -C /tmp/conntrackd_test_simple_stats -s runtime | grep -q uptime + +- name: stats_process + scenario: simple_stats + # check that we can obtain stats via unix socket: process (no output) + test: + - $CONNTRACKD -C /tmp/conntrackd_test_simple_stats -s process + +- name: stats_queue + scenario: simple_stats + # check that we can obtain stats via unix socket: queue (no output) + test: + - $CONNTRACKD -C /tmp/conntrackd_test_simple_stats -s queue + +- name: stats_ct + scenario: simple_stats + # check that we can obtain stats via unix socket: ct + test: + - $CONNTRACKD -C /tmp/conntrackd_test_simple_stats -s ct | grep -q traffic + +- name: stats_expect + scenario: simple_stats + # check that we can obtain stats via unix socket: expect (no output) + test: + - $CONNTRACKD -C /tmp/conntrackd_test_simple_stats -s expect + +- name: tcp_notrack_replicate_icmp + scenario: basic_2_peer_network_tcp_notrack + # check that we can replicate a ICMP conntrack entry in a 2 conntrackd TCP/NOTRACK setup + test: + # PING should inject an ICMP conntrack entry in nsr1 + - ip netns exec ns1 ping -c1 10.0.1.2 >/dev/null + # verify conntrack entry is then replicated to nsr2, wait up to 5 seconds + - timeout 5 bash -c -- ' + while ! ip netns exec nsr2 $CONNTRACK -L -p icmp 2>/dev/null | grep -q icmp + ; do sleep 0.5 ; done' + +- name: hash_defaults_segfault + scenario: empty + test: + - rm -f /var/lock/conntrack.lock + - | + cat << EOF > /tmp/conntrackd_notrack_hash_defaults + Sync { + Mode NOTRACK { } + Multicast { + IPv4_address 225.0.0.50 + Group 3780 + IPv4_interface 127.0.0.1 + Interface lo + SndSocketBuffer 1249280 + RcvSocketBuffer 1249280 + Checksum on + } + } + General { + LogFile on + Syslog on + LockFile /var/lock/conntrackd.lock + UNIX { Path /var/run/conntrackd.sock } + NetlinkBufferSize 2097152 + NetlinkBufferSizeMaxGrowth 8388608 + } + EOF + - $CONNTRACKD -C /tmp/conntrackd_notrack_hash_defaults -d + - $CONNTRACKD -C /tmp/conntrackd_notrack_hash_defaults -s | grep -q "cache" + - $CONNTRACKD -C /tmp/conntrackd_notrack_hash_defaults -k |