[PATCH nft] tests: shell: add packetpath test for nft ct expectation support
Florian Westphal <[email protected]> Mon, 3 Aug 2026 18:43:45 +0200
| Newsgroups | gmane.comp.security.firewalls.netfilter.devel |
|---|---|
| Message-ID | <[email protected]> |
This will fail on kernels that lack
6fb421bd07f1 ("netfilter: nft_ct: expectation timeouts are passed in milliseconds").
On kernels that lack NAT support the test will indicate SKIP.
Signed-off-by: Florian Westphal <[email protected]>
---
tests/shell/testcases/packetpath/ct_expect | 261 ++++++++++++++++++
.../packetpath/dumps/ct_expect.nodump | 0
2 files changed, 261 insertions(+)
create mode 100755 tests/shell/testcases/packetpath/ct_expect
create mode 100644 tests/shell/testcases/packetpath/dumps/ct_expect.nodump
diff --git a/tests/shell/testcases/packetpath/ct_expect b/tests/shell/testcases/packetpath/ct_expect
new file mode 100755
index 000000000000..d9824ca3cdaf
--- /dev/null
+++ b/tests/shell/testcases/packetpath/ct_expect
@@ -0,0 +1,261 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_socat)
+
+. $NFT_TEST_LIBRARY_FILE
+
+set -x
+
+rc=1
+
+cleanup()
+{
+ for i in $R $C $S;do
+ kill $(ip netns pid $i) 2>/dev/null
+ ip netns del $i
+ done
+
+ exit $rc
+}
+trap cleanup EXIT
+
+dump_expect_table() {
+ ip netns exec "$R" conntrack --family ipv6 -L expect
+ ip netns exec "$R" conntrack --family ipv4 -L expect
+}
+
+assert_failout()
+{
+ ip netns exec $R $NFT list ruleset
+ ip netns exec $R conntrack -L
+ dump_expect_table
+}
+
+do_connect() {
+ local ns="$1"
+ local addr="$2"
+ local port="$3"
+
+ ip netns exec "$ns" socat -u STDIN TCP-CONNECT:$addr:$port,connect-timeout=1 < /dev/null
+}
+
+connect_fail() {
+ local ns="$1"
+ local addr="$2"
+ local port="$3"
+ local msg="$4"
+
+ do_connect "$ns" "$addr" "$port"
+ assert_fail "connect from $ns to $addr:$port $msg"
+}
+
+connect_ok() {
+ local ns="$1"
+ local addr="$2"
+ local port="$3"
+ local msg="$4"
+
+ do_connect "$ns" "$addr" "$port"
+ assert_pass "connect from $ns to $addr:$port $msg"
+}
+
+test_expect() {
+ local ip_cr="$1"
+ local ip_sr="$2"
+ local msg="$3"
+
+ echo check port is closed from outside.
+ connect_fail "$S" "$ip_cr" 1234
+
+ echo "Test: expectation via ruleset$msg"
+
+ echo control port should be open.
+ connect_ok "$C" "$ip_sr" 2222 "$msg"
+
+ dump_expect_table
+
+ echo expected port. Should now pass.
+ connect_ok "$S" "$ip_cr" 1234 "$msg"
+
+ echo one expectation max, expect fail.
+ connect_fail "$S" "$ip_cr" 1234 "$msg"
+}
+
+test_timeout() {
+ echo "Test: expectation auto-timeout"
+ # control port - this should be open.
+ connect_ok "$C" "$ip4_sr" 2222
+ connect_ok "$C" "[$ip6_sr]" 2222
+
+ dump_expect_table
+
+ # timeout. We 'bundle' ipv4 and ipv6 test here.
+ sleep 6
+
+ dump_expect_table
+
+ echo "expect failures"
+ connect_fail "$S" "$ip4_cr" 1234
+ connect_fail "$S" "[$ip6_cr]" 1234
+}
+
+rnd=$(mktemp -u XXXXXXXX)
+R="ns-router-$rnd"
+C="ns-client-$rnd"
+S="ns-server-$rnd"
+
+ip6_sr=dead:d8:ff:22::1
+ip6_cr=dead:d8:ff:21::2
+ip6_rs=dead:d8:ff:22::fffe
+ip6_rc=dead:d8:ff:21::fffe
+
+ip4_sr=10.1.22.1
+ip4_cr=10.1.21.2
+ip4_rs=10.1.22.99
+ip4_rc=10.1.21.99
+
+set -e
+ip netns add $R
+ip netns add $S
+ip netns add $C
+ip -net $S link set lo up
+ip -net $R link set lo up
+ip -net $C link set lo up
+ip netns exec $R sysctl -wq net.ipv4.ip_forward=1
+ip netns exec $R sysctl -wq net.ipv6.conf.all.forwarding=1
+
+ip link add s_r netns $S type veth peer name r_s netns $R
+ip link add c_r netns $C type veth peer name r_c netns $R
+ip -net $S link set s_r up
+ip -net $R link set r_s up
+ip -net $R link set r_c up
+ip -net $C link set c_r up
+
+ip -net $S addr add ${ip6_sr}/64 dev s_r nodad
+ip -net $C addr add ${ip6_cr}/64 dev c_r nodad
+ip -net $R addr add ${ip6_rs}/64 dev r_s nodad
+ip -net $R addr add ${ip6_rc}/64 dev r_c nodad
+ip -net $C route add ${ip6_rs}/64 via ${ip6_rc} dev c_r
+ip -net $S route add ${ip6_rc}/64 via ${ip6_rs} dev s_r
+assert_pass "topo initialization"
+
+ip -net $S addr add ${ip4_sr}/24 dev s_r
+ip -net $C addr add ${ip4_cr}/24 dev c_r
+ip -net $R addr add ${ip4_rs}/24 dev r_s
+ip -net $R addr add ${ip4_rc}/24 dev r_c
+
+ip -net $C route add 10.1.22.0/24 via ${ip4_rc} dev c_r
+ip -net $S route add 10.1.21.0/24 via ${ip4_rs} dev s_r
+
+ip netns exec "$C" ping -q -c 1 "$ip4_sr"
+ip netns exec "$C" ping -q -c 1 "$ip6_sr"
+
+timeout 20 ip netns exec "$S" socat TCP6-LISTEN:2222,ipv6only=1,fork,reuseaddr PIPE &
+timeout 20 ip netns exec "$S" socat TCP4-LISTEN:2222,fork,reuseaddr PIPE &
+timeout 20 ip netns exec "$C" socat TCP6-LISTEN:1234,ipv6only=1,fork,reuseaddr PIPE &
+timeout 20 ip netns exec "$C" socat TCP4-LISTEN:1234,fork,reuseaddr PIPE &
+wait_local_port_listen $S 2222 tcp
+wait_local_port_listen $C 1234 tcp
+
+ip netns exec $R $NFT -f - <<-EOF
+flush ruleset
+table inet t {
+ ct expectation ctexpect4 {
+ protocol tcp
+ dport 1234
+ timeout 5s
+ size 1
+ l3proto ip
+ }
+
+ ct expectation ctexpect6 {
+ protocol tcp
+ dport 1234
+ timeout 5s
+ size 1
+ l3proto ip6
+ }
+
+ chain c {
+ type filter hook forward priority filter; policy drop;
+ ct state established accept
+ meta l4proto tcp ct state related counter accept
+ meta iifname r_c ct state new jump {
+ tcp dport 2222 counter ct expectation set meta nfproto map { ipv4 : ctexpect4, ipv6 : ctexpect6 } counter accept
+ counter accept
+ }
+ meta l4proto tcp counter reject with tcp reset
+ meta l4proto { icmp, icmpv6 } accept
+ }
+}
+EOF
+
+ip netns exec "$C" ping -q -c 1 "$ip4_sr"
+ip netns exec "$C" ping -q -c 1 "$ip6_sr"
+
+set +e
+echo check port is closed from outside.
+connect_fail "$S" "$ip4_cr" 1234
+connect_fail "$S" "[$ip6_cr]" 1234
+
+test_expect "$ip4_cr" "$ip4_sr" ""
+test_expect "[$ip6_cr]" "[$ip6_sr]" ""
+
+ip netns exec $R conntrack -F
+test_timeout
+
+set -e
+ip netns exec $R $NFT -f - <<-EOF
+flush ruleset
+table inet t {
+ ct expectation ctexpect4 {
+ protocol tcp
+ dport 1234
+ timeout 5s
+ size 1
+ l3proto ip
+ }
+
+ ct expectation ctexpect6 {
+ protocol tcp
+ dport 1234
+ timeout 5s
+ size 1
+ l3proto ip6
+ }
+ chain c {
+ type filter hook forward priority filter; policy drop;
+ ct state established accept
+ meta l4proto tcp ct state related counter accept
+ meta iifname r_c ct state new accept
+ meta l4proto tcp counter reject with tcp reset
+ meta l4proto { icmp, icmpv6 } accept
+ }
+
+ chain masq {
+ type nat hook postrouting priority srcnat; policy accept;
+ meta oifname "r_s" counter masquerade
+ }
+
+ chain e {
+ type filter hook postrouting priority 50000 ; policy accept;
+ meta iifname r_c ct state new jump {
+ tcp dport 2222 counter ct expectation set meta nfproto map { ipv4 : ctexpect4, ipv6 : ctexpect6 } counter accept
+ counter accept
+ }
+ }
+}
+EOF
+
+ip netns exec $R conntrack -F
+echo "Test: conntrack expectation with masquerade"
+set +e
+
+# Skip here, this is a kernel bug / missing feature we can't
+# test for.
+rc=77
+test_expect "$ip4_rs" "$ip4_sr" " with masquerade"
+test_expect "[$ip6_rs]" "[$ip6_sr]" " with masquerade"
+
+ip netns exec "$R" conntrack -L
+rc=0
diff --git a/tests/shell/testcases/packetpath/dumps/ct_expect.nodump b/tests/shell/testcases/packetpath/dumps/ct_expect.nodump
new file mode 100644
index 000000000000..e69de29bb2d1
--
2.54.0