selftests: netfilter: add zone stress test with colliding tuples
authorFlorian Westphal <fw@strlen.de>
Wed, 8 Sep 2021 12:28:39 +0000 (14:28 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Tue, 21 Sep 2021 01:46:55 +0000 (03:46 +0200)
Add 20k entries to the connection tracking table, once from the
data plane, once via ctnetlink.

In both cases, each entry lives in a different conntrack zone
and addresses/ports are identical.

Expectation is that insertions work and occurs in constant time:

PASS: added 10000 entries in 1215 ms (now 10000 total, loop 1)
PASS: added 10000 entries in 1214 ms (now 20000 total, loop 2)
PASS: inserted 20000 entries from packet path in 2434 ms total
PASS: added 10000 entries in 57631 ms (now 10000 total)
PASS: added 10000 entries in 58572 ms (now 20000 total)
PASS: inserted 20000 entries via ctnetlink in 116205 ms

Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
tools/testing/selftests/netfilter/nft_zones_many.sh [new file with mode: 0755]

diff --git a/tools/testing/selftests/netfilter/nft_zones_many.sh b/tools/testing/selftests/netfilter/nft_zones_many.sh
new file mode 100755 (executable)
index 0000000..ac64637
--- /dev/null
@@ -0,0 +1,156 @@
+#!/bin/bash
+
+# Test insertion speed for packets with identical addresses/ports
+# that are all placed in distinct conntrack zones.
+
+sfx=$(mktemp -u "XXXXXXXX")
+ns="ns-$sfx"
+
+# Kselftest framework requirement - SKIP code is 4.
+ksft_skip=4
+
+zones=20000
+have_ct_tool=0
+ret=0
+
+cleanup()
+{
+       ip netns del $ns
+}
+
+ip netns add $ns
+if [ $? -ne 0 ];then
+       echo "SKIP: Could not create net namespace $gw"
+       exit $ksft_skip
+fi
+
+trap cleanup EXIT
+
+conntrack -V > /dev/null 2>&1
+if [ $? -eq 0 ];then
+       have_ct_tool=1
+fi
+
+ip -net "$ns" link set lo up
+
+test_zones() {
+       local max_zones=$1
+
+ip netns exec $ns sysctl -q net.netfilter.nf_conntrack_udp_timeout=3600
+ip netns exec $ns nft -f /dev/stdin<<EOF
+flush ruleset
+table inet raw {
+       map rndzone {
+               typeof numgen inc mod $max_zones : ct zone
+       }
+
+       chain output {
+               type filter hook output priority -64000; policy accept;
+               udp dport 12345  ct zone set numgen inc mod 65536 map @rndzone
+       }
+}
+EOF
+       (
+               echo "add element inet raw rndzone {"
+       for i in $(seq 1 $max_zones);do
+               echo -n "$i : $i"
+               if [ $i -lt $max_zones ]; then
+                       echo ","
+               else
+                       echo "}"
+               fi
+       done
+       ) | ip netns exec $ns nft -f /dev/stdin
+
+       local i=0
+       local j=0
+       local outerstart=$(date +%s%3N)
+       local stop=$outerstart
+
+       while [ $i -lt $max_zones ]; do
+               local start=$(date +%s%3N)
+               i=$((i + 10000))
+               j=$((j + 1))
+               dd if=/dev/zero of=/dev/stdout bs=8k count=10000 2>/dev/null | ip netns exec "$ns" nc -w 1 -q 1 -u -p 12345 127.0.0.1 12345 > /dev/null
+               if [ $? -ne 0 ] ;then
+                       ret=1
+                       break
+               fi
+
+               stop=$(date +%s%3N)
+               local duration=$((stop-start))
+               echo "PASS: added 10000 entries in $duration ms (now $i total, loop $j)"
+       done
+
+       if [ $have_ct_tool -eq 1 ]; then
+               local count=$(ip netns exec "$ns" conntrack -C)
+               local duration=$((stop-outerstart))
+
+               if [ $count -eq $max_zones ]; then
+                       echo "PASS: inserted $count entries from packet path in $duration ms total"
+               else
+                       ip netns exec $ns conntrack -S 1>&2
+                       echo "FAIL: inserted $count entries from packet path in $duration ms total, expected $max_zones entries"
+                       ret=1
+               fi
+       fi
+
+       if [ $ret -ne 0 ];then
+               echo "FAIL: insert $max_zones entries from packet path" 1>&2
+       fi
+}
+
+test_conntrack_tool() {
+       local max_zones=$1
+
+       ip netns exec $ns conntrack -F >/dev/null 2>/dev/null
+
+       local outerstart=$(date +%s%3N)
+       local start=$(date +%s%3N)
+       local stop=$start
+       local i=0
+       while [ $i -lt $max_zones ]; do
+               i=$((i + 1))
+               ip netns exec "$ns" conntrack -I -s 1.1.1.1 -d 2.2.2.2 --protonum 6 \
+                        --timeout 3600 --state ESTABLISHED --sport 12345 --dport 1000 --zone $i >/dev/null 2>&1
+               if [ $? -ne 0 ];then
+                       ip netns exec "$ns" conntrack -I -s 1.1.1.1 -d 2.2.2.2 --protonum 6 \
+                        --timeout 3600 --state ESTABLISHED --sport 12345 --dport 1000 --zone $i > /dev/null
+                       echo "FAIL: conntrack -I returned an error"
+                       ret=1
+                       break
+               fi
+
+               if [ $((i%10000)) -eq 0 ];then
+                       stop=$(date +%s%3N)
+
+                       local duration=$((stop-start))
+                       echo "PASS: added 10000 entries in $duration ms (now $i total)"
+                       start=$stop
+               fi
+       done
+
+       local count=$(ip netns exec "$ns" conntrack -C)
+       local duration=$((stop-outerstart))
+
+       if [ $count -eq $max_zones ]; then
+               echo "PASS: inserted $count entries via ctnetlink in $duration ms"
+       else
+               ip netns exec $ns conntrack -S 1>&2
+               echo "FAIL: inserted $count entries via ctnetlink in $duration ms, expected $max_zones entries ($duration ms)"
+               ret=1
+       fi
+}
+
+test_zones $zones
+
+if [ $have_ct_tool -eq 1 ];then
+       test_conntrack_tool $zones
+else
+       echo "SKIP: Could not run ctnetlink insertion test without conntrack tool"
+       if [ $ret -eq 0 ];then
+               exit $ksft_skip
+       fi
+fi
+
+exit $ret