diff options
Diffstat (limited to 'tests/monitor')
-rw-r--r-- | tests/monitor/README | 59 | ||||
-rwxr-xr-x | tests/monitor/run-tests.sh | 211 | ||||
-rw-r--r-- | tests/monitor/testcases/map-expr.t | 6 | ||||
-rw-r--r-- | tests/monitor/testcases/object.t | 46 | ||||
-rw-r--r-- | tests/monitor/testcases/set-interval.t | 30 | ||||
-rw-r--r-- | tests/monitor/testcases/set-maps.t | 14 | ||||
-rw-r--r-- | tests/monitor/testcases/set-mixed.t | 22 | ||||
-rw-r--r-- | tests/monitor/testcases/set-multiple.t | 15 | ||||
-rw-r--r-- | tests/monitor/testcases/set-simple.t | 61 | ||||
-rw-r--r-- | tests/monitor/testcases/simple.t | 28 |
10 files changed, 492 insertions, 0 deletions
diff --git a/tests/monitor/README b/tests/monitor/README new file mode 100644 index 0000000..39096a7 --- /dev/null +++ b/tests/monitor/README @@ -0,0 +1,59 @@ +Simple NFT MONITOR Testsuite +============================ + +The purpose of this suite of tests is to assert correct 'nft monitor' output for +known input. The suite consists of the single shell script 'run-tests.sh' which +performs the tests and a number of test definition files in 'testcases/'. The +latter have to be suffixed '.t' in order to be recognized as such. + +Test Case Syntax +---------------- + +Each testcase defines a number of commands to pass on to 'nft' binary and an +associated 'nft monitor' output definition. Prerequisites for each command have +to be established manually, i.e. in order to test monitor output when adding a +chain, the table containing it has to be created first. In between each +testcase, rule set is flushed completely. + +Input lines are prefixed by 'I'. Multiple consecutive input lines are passed to +'nft' together, hence lead to a single transaction. + +There are two types of output lines: Those for standard syntax, prefixed by 'O' +and those for JSON output, prefixed by 'J'. For standard syntax output lines, +there is a shortcut: If a line consists of 'O -' only, the test script uses all +previous input lines as expected output directly. Of course this is not +available for JSON output lines. + +Empty lines and those starting with '#' are ignored. + +Test Script Semantics +--------------------- + +The script iterates over all test case files, reading them line by line. It +assumes that sections of 'I' lines alternate with sections of 'O'/'J' lines. +After stripping the prefix, each line is appended to a temporary file. There are +separate files for input and output lines. + +If a set of input and output lines is complete (i.e. upon encountering either a +new input line or end of file), a testrun is performed: 'nft monitor' is run in +background, redirecting the output into a third file. The input file is passed +to 'nft -f'. Finally 'nft monitor' is killed and it's output compared to the +output file created earlier. If the files differ, a unified diff is printed and +test execution aborts. + +After each testrun, input and output files are cleared. + +Note: Running 'nft monitor' in background is prone to race conditions. Hence +an artificial delay is introduced before calling 'nft -f' to allow for 'nft +monitor' to complete initialization and another one before comparing the output +to allow for 'nft monitor' to process the netlink events. + +By default, only standard syntax is being tested for, i.e. 'J'-prefixed lines +are simply ignored. If JSON testing was requested (by passing '-j' flag to the +test script), 'O'-prefixed lines in turn are ignored. + +There is one caveat with regards to JSON output: Since it always contains handle +properties (if the given object possesses such) which is supposed to be +arbitrary, there is a filter script which normalizes all handle values in +monitor output to zero before comparison. Therefore expected output must have +all handle properties present but with a value of zero. diff --git a/tests/monitor/run-tests.sh b/tests/monitor/run-tests.sh new file mode 100755 index 0000000..f1ac790 --- /dev/null +++ b/tests/monitor/run-tests.sh @@ -0,0 +1,211 @@ +#!/bin/bash + +cd $(dirname $0) +nft=${NFT:-../../src/nft} +debug=false +test_json=false + +mydiff() { + diff -w -I '^# ' "$@" +} + +err() { + echo "$*" >&2 +} + +die() { + err "$*" + exit 1 +} + +if [ "$(id -u)" != "0" ] ; then + die "this requires root!" +fi + +testdir=$(mktemp -d) +if [ ! -d $testdir ]; then + die "Failed to create test directory" +fi +trap 'rm -rf $testdir; $nft flush ruleset' EXIT + +command_file=$(mktemp -p $testdir) +output_file=$(mktemp -p $testdir) + +cmd_append() { + echo "$*" >>$command_file +} +monitor_output_append() { + [[ "$*" == '-' ]] && { + cat $command_file >>$output_file + return + } + echo "$*" >>$output_file +} +echo_output_append() { + # this is a bit tricky: for replace commands, nft prints a delete + # command - so in case there is a replace command in $command_file, + # just assume any other commands in the same file are sane + grep -q '^replace' $command_file >/dev/null 2>&1 && { + monitor_output_append "$*" + return + } + [[ "$*" == '-' ]] && { + grep '^\(add\|replace\|insert\)' $command_file >>$output_file + return + } + [[ "$*" =~ ^add|replace|insert ]] && echo "$*" >>$output_file +} +json_output_filter() { # (filename) + # unify handle values + sed -i -e 's/\("handle":\) [0-9][0-9]*/\1 0/g' "$1" +} +monitor_run_test() { + monitor_output=$(mktemp -p $testdir) + monitor_args="" + $test_json && monitor_args="vm json" + local rc=0 + + $nft -nn monitor $monitor_args >$monitor_output & + monitor_pid=$! + + sleep 0.5 + + $debug && { + echo "command file:" + cat $command_file + } + $nft -f - <$command_file || { + err "nft command failed!" + rc=1 + } + sleep 0.5 + kill $monitor_pid + wait >/dev/null 2>&1 + $test_json && json_output_filter $monitor_output + mydiff -q $monitor_output $output_file >/dev/null 2>&1 + if [[ $rc == 0 && $? != 0 ]]; then + err "monitor output differs!" + mydiff -u $output_file $monitor_output >&2 + rc=1 + fi + rm $command_file + rm $output_file + touch $command_file + touch $output_file + return $rc +} + +echo_run_test() { + echo_output=$(mktemp -p $testdir) + local rc=0 + + $debug && { + echo "command file:" + cat $command_file + } + $nft -nn -e -f - <$command_file >$echo_output || { + err "nft command failed!" + rc=1 + } + mydiff -q $echo_output $output_file >/dev/null 2>&1 + if [[ $rc == 0 && $? != 0 ]]; then + err "echo output differs!" + mydiff -u $output_file $echo_output >&2 + rc=1 + fi + rm $command_file + rm $output_file + touch $command_file + touch $output_file + return $rc +} + +testcases="" +while [ -n "$1" ]; do + case "$1" in + -d|--debug) + debug=true + shift + ;; + -j|--json) + test_json=true + shift + ;; + -H|--host) + nft=nft + shift + ;; + testcases/*.t) + testcases+=" $1" + shift + ;; + *) + echo "unknown option '$1'" + ;& + -h|--help) + echo "Usage: $(basename $0) [-j|--json] [-d|--debug] [testcase ...]" + exit 1 + ;; + esac +done + +if $test_json; then + variants="monitor" +else + variants="monitor echo" +fi + +rc=0 +for variant in $variants; do + run_test=${variant}_run_test + output_append=${variant}_output_append + + for testcase in ${testcases:-testcases/*.t}; do + filename=$(basename $testcase) + echo "$variant: running tests from file $filename" + rc_start=$rc + + # files are like this: + # + # I add table ip t + # O add table ip t + # I add chain ip t c + # O add chain ip t c + + $nft flush ruleset + + input_complete=false + while read dir line; do + case $dir in + I) + $input_complete && { + $run_test + let "rc += $?" + } + input_complete=false + cmd_append "$line" + ;; + O) + input_complete=true + $test_json || $output_append "$line" + ;; + J) + input_complete=true + $test_json && $output_append "$line" + ;; + '#'|'') + # ignore comments and empty lines + ;; + esac + done <$testcase + $input_complete && { + $run_test + let "rc += $?" + } + + let "rc_diff = rc - rc_start" + [[ $rc_diff -ne 0 ]] && \ + echo "$variant: $rc_diff tests from file $filename failed" + done +done +exit $rc diff --git a/tests/monitor/testcases/map-expr.t b/tests/monitor/testcases/map-expr.t new file mode 100644 index 0000000..8729c0b --- /dev/null +++ b/tests/monitor/testcases/map-expr.t @@ -0,0 +1,6 @@ +# first the setup +I add table ip t +I add map ip t m { typeof meta day . meta hour : verdict; flags interval; counter; } +O - +J {"add": {"table": {"family": "ip", "name": "t", "handle": 0}}} +J {"add": {"map": {"family": "ip", "name": "m", "table": "t", "type": ["day", "hour"], "handle": 0, "map": "verdict", "flags": ["interval"], "stmt": [{"counter": null}]}}} diff --git a/tests/monitor/testcases/object.t b/tests/monitor/testcases/object.t new file mode 100644 index 0000000..53a9f8c --- /dev/null +++ b/tests/monitor/testcases/object.t @@ -0,0 +1,46 @@ +# first the setup +I add table ip t +O - +J {"add": {"table": {"family": "ip", "name": "t", "handle": 0}}} + +I add counter ip t c +O add counter ip t c { packets 0 bytes 0 } +J {"add": {"counter": {"family": "ip", "name": "c", "table": "t", "handle": 0, "packets": 0, "bytes": 0}}} + +I delete counter ip t c +O - +J {"delete": {"counter": {"family": "ip", "name": "c", "table": "t", "handle": 0, "packets": 0, "bytes": 0}}} + +# FIXME: input/output shouldn't be asynchronous here +I add quota ip t q 25 mbytes +O add quota ip t q { 25 mbytes } +J {"add": {"quota": {"family": "ip", "name": "q", "table": "t", "handle": 0, "bytes": 26214400, "used": 0, "inv": false}}} + +I delete quota ip t q +O - +J {"delete": {"quota": {"family": "ip", "name": "q", "table": "t", "handle": 0, "bytes": 26214400, "used": 0, "inv": false}}} + +# FIXME: input/output shouldn't be asynchronous here +I add limit ip t l rate 1/second +O add limit ip t l { rate 1/second } +J {"add": {"limit": {"family": "ip", "name": "l", "table": "t", "handle": 0, "rate": 1, "per": "second", "burst": 5}}} + +I delete limit ip t l +O - +J {"delete": {"limit": {"family": "ip", "name": "l", "table": "t", "handle": 0, "rate": 1, "per": "second", "burst": 5}}} + +I add ct helper ip t cth { type "sip" protocol tcp; l3proto ip; } +O - +J {"add": {"ct helper": {"family": "ip", "name": "cth", "table": "t", "handle": 0, "type": "sip", "protocol": "tcp", "l3proto": "ip"}}} + +I delete ct helper ip t cth +O - +J {"delete": {"ct helper": {"family": "ip", "name": "cth", "table": "t", "handle": 0, "type": "sip", "protocol": "tcp", "l3proto": "ip"}}} + +I add ct timeout ip t ctt { protocol udp; l3proto ip; policy = { unreplied : 15s, replied : 12s }; } +O - +J {"add": {"ct timeout": {"family": "ip", "name": "ctt", "table": "t", "handle": 0, "protocol": "udp", "l3proto": "ip", "policy": {"unreplied": 15, "replied": 12}}}} + +I delete ct timeout ip t ctt +O - +J {"delete": {"ct timeout": {"family": "ip", "name": "ctt", "table": "t", "handle": 0, "protocol": "udp", "l3proto": "ip", "policy": {"unreplied": 15, "replied": 12}}}} diff --git a/tests/monitor/testcases/set-interval.t b/tests/monitor/testcases/set-interval.t new file mode 100644 index 0000000..5053c59 --- /dev/null +++ b/tests/monitor/testcases/set-interval.t @@ -0,0 +1,30 @@ +# setup first +I add table ip t +I add chain ip t c +O - +J {"add": {"table": {"family": "ip", "name": "t", "handle": 0}}} +J {"add": {"chain": {"family": "ip", "table": "t", "name": "c", "handle": 0}}} + +# add set with elements, monitor output expectedly differs +I add set ip t s { type inet_service; flags interval; elements = { 20, 30-40 }; } +O add set ip t s { type inet_service; flags interval; } +O add element ip t s { 20 } +O add element ip t s { 30-40 } +J {"add": {"set": {"family": "ip", "name": "s", "table": "t", "type": "inet_service", "handle": 0, "flags": ["interval"]}}} +J {"add": {"element": {"family": "ip", "table": "t", "name": "s", "elem": {"set": [20]}}}} +J {"add": {"element": {"family": "ip", "table": "t", "name": "s", "elem": {"set": [{"range": [30, 40]}]}}}} + +# this would crash nft +I add rule ip t c tcp dport @s +O - +J {"add": {"rule": {"family": "ip", "table": "t", "chain": "c", "handle": 0, "expr": [{"match": {"op": "==", "left": {"payload": {"protocol": "tcp", "field": "dport"}}, "right": "@s"}}]}}} + +# test anonymous interval sets as well +I add rule ip t c tcp dport { 20, 30-40 } +O - +J {"add": {"rule": {"family": "ip", "table": "t", "chain": "c", "handle": 0, "expr": [{"match": {"op": "==", "left": {"payload": {"protocol": "tcp", "field": "dport"}}, "right": {"set": [20, {"range": [30, 40]}]}}}]}}} + +# ... and anon concat range +I add rule ip t c ether saddr . ip saddr { 08:00:27:40:f7:09 . 192.168.56.10-192.168.56.12 } +O - +J {"add": {"rule": {"family": "ip", "table": "t", "chain": "c", "handle": 0, "expr": [{"match": {"op": "==", "left": {"concat": [{"payload": {"protocol": "ether", "field": "saddr"}}, {"payload": {"protocol": "ip", "field": "saddr"}}]}, "right": {"set": [{"concat": ["08:00:27:40:f7:09", {"range": ["192.168.56.10", "192.168.56.12"]}]}]}}}]}}} diff --git a/tests/monitor/testcases/set-maps.t b/tests/monitor/testcases/set-maps.t new file mode 100644 index 0000000..acda480 --- /dev/null +++ b/tests/monitor/testcases/set-maps.t @@ -0,0 +1,14 @@ +# first the setup +I add table ip t +I add map ip t portip { type inet_service: ipv4_addr; flags interval; } +O - +J {"add": {"table": {"family": "ip", "name": "t", "handle": 0}}} +J {"add": {"map": {"family": "ip", "name": "portip", "table": "t", "type": "inet_service", "handle": 0, "map": "ipv4_addr", "flags": ["interval"]}}} + +I add element ip t portip { 80-100: 10.0.0.1 } +O - +J {"add": {"element": {"family": "ip", "table": "t", "name": "portip", "elem": {"set": [[{"range": [80, 100]}, "10.0.0.1"]]}}}} + +I add element ip t portip { 1024-65535: 10.0.0.1 } +O - +J {"add": {"element": {"family": "ip", "table": "t", "name": "portip", "elem": {"set": [[{"range": [1024, 65535]}, "10.0.0.1"]]}}}} diff --git a/tests/monitor/testcases/set-mixed.t b/tests/monitor/testcases/set-mixed.t new file mode 100644 index 0000000..08c2011 --- /dev/null +++ b/tests/monitor/testcases/set-mixed.t @@ -0,0 +1,22 @@ +# first the setup +I add table ip t +I add set ip t portrange { type inet_service; flags interval; } +I add set ip t ports { type inet_service; } +O - +J {"add": {"table": {"family": "ip", "name": "t", "handle": 0}}} +J {"add": {"set": {"family": "ip", "name": "portrange", "table": "t", "type": "inet_service", "handle": 0, "flags": ["interval"]}}} +J {"add": {"set": {"family": "ip", "name": "ports", "table": "t", "type": "inet_service", "handle": 0}}} + +# make sure concurrent adds work +I add element ip t portrange { 1024-65535 } +I add element ip t ports { 10 } +O - +J {"add": {"element": {"family": "ip", "table": "t", "name": "portrange", "elem": {"set": [{"range": [1024, 65535]}]}}}} +J {"add": {"element": {"family": "ip", "table": "t", "name": "ports", "elem": {"set": [10]}}}} + +# delete items again +I delete element ip t portrange { 1024-65535 } +I delete element ip t ports { 10 } +O - +J {"delete": {"element": {"family": "ip", "table": "t", "name": "portrange", "elem": {"set": [{"range": [1024, 65535]}]}}}} +J {"delete": {"element": {"family": "ip", "table": "t", "name": "ports", "elem": {"set": [10]}}}} diff --git a/tests/monitor/testcases/set-multiple.t b/tests/monitor/testcases/set-multiple.t new file mode 100644 index 0000000..bd7a624 --- /dev/null +++ b/tests/monitor/testcases/set-multiple.t @@ -0,0 +1,15 @@ +# first the setup +I add table ip t +I add set ip t portrange { type inet_service; flags interval; } +I add set ip t portrange2 { type inet_service; flags interval; } +O - +J {"add": {"table": {"family": "ip", "name": "t", "handle": 0}}} +J {"add": {"set": {"family": "ip", "name": "portrange", "table": "t", "type": "inet_service", "handle": 0, "flags": ["interval"]}}} +J {"add": {"set": {"family": "ip", "name": "portrange2", "table": "t", "type": "inet_service", "handle": 0, "flags": ["interval"]}}} + +# make sure concurrent adds work +I add element ip t portrange { 1024-65535 } +I add element ip t portrange2 { 10-20 } +O - +J {"add": {"element": {"family": "ip", "table": "t", "name": "portrange", "elem": {"set": [{"range": [1024, 65535]}]}}}} +J {"add": {"element": {"family": "ip", "table": "t", "name": "portrange2", "elem": {"set": [{"range": [10, 20]}]}}}} diff --git a/tests/monitor/testcases/set-simple.t b/tests/monitor/testcases/set-simple.t new file mode 100644 index 0000000..8ca4f32 --- /dev/null +++ b/tests/monitor/testcases/set-simple.t @@ -0,0 +1,61 @@ +# first the setup +I add table ip t +I add set ip t portrange { type inet_service; flags interval; } +O - +J {"add": {"table": {"family": "ip", "name": "t", "handle": 0}}} +J {"add": {"set": {"family": "ip", "name": "portrange", "table": "t", "type": "inet_service", "handle": 0, "flags": ["interval"]}}} + +# adding some ranges +I add element ip t portrange { 1-10 } +O - +J {"add": {"element": {"family": "ip", "table": "t", "name": "portrange", "elem": {"set": [{"range": [1, 10]}]}}}} +I add element ip t portrange { 1024-65535 } +O - +J {"add": {"element": {"family": "ip", "table": "t", "name": "portrange", "elem": {"set": [{"range": [1024, 65535]}]}}}} +I add element ip t portrange { 20-30, 40-50 } +O add element ip t portrange { 20-30 } +O add element ip t portrange { 40-50 } +J {"add": {"element": {"family": "ip", "table": "t", "name": "portrange", "elem": {"set": [{"range": [20, 30]}]}}}} +J {"add": {"element": {"family": "ip", "table": "t", "name": "portrange", "elem": {"set": [{"range": [40, 50]}]}}}} + +# test flushing -> elements are removed in reverse +I flush set ip t portrange +O delete element ip t portrange { 1024-65535 } +O delete element ip t portrange { 40-50 } +O delete element ip t portrange { 20-30 } +O delete element ip t portrange { 1-10 } +J {"delete": {"element": {"family": "ip", "table": "t", "name": "portrange", "elem": {"set": [{"range": [1024, 65535]}]}}}} +J {"delete": {"element": {"family": "ip", "table": "t", "name": "portrange", "elem": {"set": [{"range": [40, 50]}]}}}} +J {"delete": {"element": {"family": "ip", "table": "t", "name": "portrange", "elem": {"set": [{"range": [20, 30]}]}}}} +J {"delete": {"element": {"family": "ip", "table": "t", "name": "portrange", "elem": {"set": [{"range": [1, 10]}]}}}} + +# make sure lower scope boundary works +I add element ip t portrange { 0-10 } +O - +J {"add": {"element": {"family": "ip", "table": "t", "name": "portrange", "elem": {"set": [{"range": [0, 10]}]}}}} + +# make sure half open before other element works +I add element ip t portrange { 1024-65535 } +I add element ip t portrange { 100-200 } +O - +J {"add": {"element": {"family": "ip", "table": "t", "name": "portrange", "elem": {"set": [{"range": [1024, 65535]}]}}}} +J {"add": {"element": {"family": "ip", "table": "t", "name": "portrange", "elem": {"set": [{"range": [100, 200]}]}}}} + +# make sure deletion of elements works +I delete element ip t portrange { 0-10 } +O - +J {"delete": {"element": {"family": "ip", "table": "t", "name": "portrange", "elem": {"set": [{"range": [0, 10]}]}}}} +I delete element ip t portrange { 100-200 } +I delete element ip t portrange { 1024-65535 } +O - +J {"delete": {"element": {"family": "ip", "table": "t", "name": "portrange", "elem": {"set": [{"range": [100, 200]}]}}}} +J {"delete": {"element": {"family": "ip", "table": "t", "name": "portrange", "elem": {"set": [{"range": [1024, 65535]}]}}}} + +# make sure mixed add/delete works +I add element ip t portrange { 10-20 } +I add element ip t portrange { 1024-65535 } +I delete element ip t portrange { 10-20 } +O - +J {"add": {"element": {"family": "ip", "table": "t", "name": "portrange", "elem": {"set": [{"range": [10, 20]}]}}}} +J {"add": {"element": {"family": "ip", "table": "t", "name": "portrange", "elem": {"set": [{"range": [1024, 65535]}]}}}} +J {"delete": {"element": {"family": "ip", "table": "t", "name": "portrange", "elem": {"set": [{"range": [10, 20]}]}}}} diff --git a/tests/monitor/testcases/simple.t b/tests/monitor/testcases/simple.t new file mode 100644 index 0000000..67be5c8 --- /dev/null +++ b/tests/monitor/testcases/simple.t @@ -0,0 +1,28 @@ +# first the setup +I add table ip t +I add chain ip t c +O - +J {"add": {"table": {"family": "ip", "name": "t", "handle": 0}}} +J {"add": {"chain": {"family": "ip", "table": "t", "name": "c", "handle": 0}}} + +I add rule ip t c accept +O - +J {"add": {"rule": {"family": "ip", "table": "t", "chain": "c", "handle": 0, "expr": [{"accept": null}]}}} + +I add rule ip t c tcp dport { 22, 80, 443 } accept +O - +J {"add": {"rule": {"family": "ip", "table": "t", "chain": "c", "handle": 0, "expr": [{"match": {"op": "==", "left": {"payload": {"protocol": "tcp", "field": "dport"}}, "right": {"set": [22, 80, 443]}}}, {"accept": null}]}}} + +I insert rule ip t c counter accept +O insert rule ip t c counter packets 0 bytes 0 accept +J {"insert": {"rule": {"family": "ip", "table": "t", "chain": "c", "handle": 0, "expr": [{"counter": {"packets": 0, "bytes": 0}}, {"accept": null}]}}} + +I replace rule ip t c handle 2 accept comment "foo bar" +O delete rule ip t c handle 2 +O add rule ip t c handle 5 accept comment "foo bar" +J {"delete": {"rule": {"family": "ip", "table": "t", "chain": "c", "handle": 0, "expr": [{"accept": null}]}}} +J {"add": {"rule": {"family": "ip", "table": "t", "chain": "c", "handle": 0, "comment": "foo bar", "expr": [{"accept": null}]}}} + +I add counter ip t cnt +O add counter ip t cnt { packets 0 bytes 0 } +J {"add": {"counter": {"family": "ip", "name": "cnt", "table": "t", "handle": 0, "packets": 0, "bytes": 0}}} |