From: Florian Westphal <fw@strlen.de>
To: <netfilter-devel@vger.kernel.org>
Cc: Florian Westphal <fw@strlen.de>
Subject: [PATCH nft] tests: shell: add packetpath test for reject statement
Date: Wed, 8 Oct 2025 11:16:19 +0200 [thread overview]
Message-ID: <20251008091621.23597-1-fw@strlen.de> (raw)
Test case for:
91a79b792204 ("netfilter: nf_reject: don't leak dst refcount for loopback packets")
and
db99b2f2b3e2 ("netfilter: nf_reject: don't reply to icmp error messages")
Signed-off-by: Florian Westphal <fw@strlen.de>
---
.../testcases/packetpath/reject_loopback | 223 ++++++++++++++++++
1 file changed, 223 insertions(+)
create mode 100755 tests/shell/testcases/packetpath/reject_loopback
diff --git a/tests/shell/testcases/packetpath/reject_loopback b/tests/shell/testcases/packetpath/reject_loopback
new file mode 100755
index 000000000000..d199b1275f3f
--- /dev/null
+++ b/tests/shell/testcases/packetpath/reject_loopback
@@ -0,0 +1,223 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_socat)
+
+# Tests reject functionality for both IPv4 and IPv6 with TCP and ICMP traffic on
+# the loopback interface.
+#
+# - check reject works, i.e. ping and connect fail
+# - check we don't reply to tcp resets with another tcp reset
+# - check we don't reply to icmp error with another icmp error
+
+ret=0
+port=14512
+
+ip link set lo up
+
+load_ruleset_netdev()
+{
+echo load netdev test ruleset
+$NFT -f -<<EOF
+table netdev t {
+ chain in {
+ type filter hook ingress device lo priority 0
+
+ ip protocol icmp counter reject
+ ip6 nexthdr icmpv6 counter reject
+ meta l4proto tcp counter reject with tcp reset
+ }
+}
+EOF
+}
+
+load_ruleset_inet()
+{
+echo load inet test ruleset
+$NFT -f -<<EOF
+table inet t {
+ chain in {
+ type filter hook prerouting priority filter; policy accept;
+ iifname lo jump {
+ icmp type echo-request counter reject with icmp port-unreachable
+ icmpv6 type echo-request counter reject with icmpv6 port-unreachable
+ meta l4proto tcp tcp flags syn counter reject with tcp reset
+ }
+ }
+}
+EOF
+}
+
+# try to get nf_tables to reset tcp rest with tcp reset and
+# reject icmp port-unreach with port-unreach.
+# Should NOT be possible.
+# Note that this ruleset is nonsensical:
+# meta l4proto tcp ... reject with tcp reset, on loopback,
+# will drop the reset packet so the client times out.
+#
+# This isn't an issue for remote clients, as the reset
+# won't appear in prerouting.
+load_ruleset_inet_loop()
+{
+echo load inet loop ruleset
+$NFT -f -<<EOF
+table inet t {
+ counter tcprstc { }
+ counter icmp4c { }
+ counter icmp6c { }
+
+ chain in {
+ type filter hook prerouting priority filter; policy accept;
+ iifname lo jump {
+ ip protocol icmp counter name icmp4c reject with icmp port-unreachable
+ ip6 nexthdr icmpv6 counter name icmp6c reject with icmpv6 port-unreachable
+ meta l4proto tcp counter name tcprstc reject with tcp reset
+ }
+ }
+}
+EOF
+}
+
+load_ruleset_netdev_loop()
+{
+echo load netdev loop ruleset
+$NFT -f -<<EOF
+table netdev t {
+ counter tcprstc { }
+ counter icmp4c { }
+ counter icmp6c { }
+
+ chain in {
+ type filter hook ingress device lo priority 0
+ ip protocol icmp counter name icmp4c reject with icmp port-unreachable
+ ip6 nexthdr icmpv6 counter name icmp6c reject with icmpv6 port-unreachable
+ meta l4proto tcp counter name tcprstc reject with tcp reset
+ }
+}
+EOF
+}
+
+check_counter()
+{
+ local family="$1"
+ local countername="$2"
+ local wanted_packetcount="$3"
+ local max_packetcount="$4"
+
+ echo "counter $family t $countername has $pcount packets"
+ if $NFT list counter "$family" "t" "$countername" | grep packets\ $wanted_packetcount;then
+ return
+ fi
+
+ # the _loop rulesets drop tcp resets, so we must tolerate retransmitted syns.
+ if [ "$max_packetcount" -gt 0 ];then
+ local pcount=$($NFT list counter "$family" "t" "$countername" | grep packets)
+
+ pcount=${pcount%bytes*}
+ pcount=${pcount#*packets}
+
+ if [ "$pcount" -gt 0 ] && [ "$pcount" -le "$max_packetcount" ];then
+ echo "Tolerated $pcount packets (max $max_packetcount)"
+ return
+ fi
+ fi
+
+ echo "Unexpected packetcount, expected $wanted_packetcount / max $max_packetcount"
+ $NFT list counter "$family" "t" "$countername"
+ ret=1
+}
+
+check_counters()
+{
+ local family="$1"
+
+ # one syn, one rst
+ check_counter "$family" tcprstc 4 16
+
+ # one for echo, one for dst-unreach
+ check_counter "$family" icmp4c 2 2
+ check_counter "$family" icmp6c 2 2
+}
+
+maybe_error()
+{
+ local ret="$1"
+ shift
+ local err_wanted="$1"
+ shift
+
+ local errmsg="$@"
+
+
+ if [ $ret -eq 0 ];then
+ errmsg="$errmsg succeeded"
+
+ if [ $err_wanted -ne 0 ]; then
+ echo "$errmsg but expected to fail"
+ ret=1
+ return
+ fi
+ else
+ errmsg="$errmsg failed ($ret)"
+
+ if [ $err_wanted -eq 0 ]; then
+ echo "$errmsg but expected to work"
+ ret=1
+ return
+ fi
+
+ fi
+}
+
+test_all()
+{
+ local err_wanted="$1"
+
+ ping -W 1 -q -c 1 127.0.0.1 > /dev/null
+ maybe_error $? "$err_wanted" "ping 127.0.0.1"
+
+ socat -u STDIN TCP-CONNECT:127.0.0.1:$port,connect-timeout=1 < /dev/null 2>/dev/null
+ maybe_error $? "$err_wanted" "connect 127.0.0.1"
+
+ ping -W 1 -q -c 1 ::1 > /dev/null
+ maybe_error $? "$err_wanted" "connect 127.0.0.1"
+
+ socat -u STDIN TCP-CONNECT:[::1]:$port,connect-timeout=1 < /dev/null 2>/dev/null
+ maybe_error $? "$err_wanted" "connect ::1"
+}
+
+# Start socat listeners in background
+timeout 10 socat TCP-LISTEN:$port,bind=127.0.0.1,reuseaddr PIPE &
+SOCAT_PID4=$!
+
+timeout 10 socat TCP6-LISTEN:$port,bind=::1,reuseaddr PIPE &
+SOCAT_PID6=$!
+
+# Give listeners time to start
+sleep 1
+
+# empty ruleset
+test_all 0
+
+load_ruleset_inet
+test_all 1
+$NFT delete table inet t
+
+load_ruleset_netdev
+test_all 1
+$NFT delete table netdev t
+
+load_ruleset_inet_loop
+test_all 1
+check_counters inet
+$NFT delete table inet t
+
+load_ruleset_netdev_loop
+test_all 1
+check_counters netdev
+$NFT delete table netdev t
+
+# Clean up listeners
+kill $SOCAT_PID4 $SOCAT_PID6 2>/dev/null
+
+echo "Exiting with $ret"
+exit $ret
--
2.49.1
reply other threads:[~2025-10-08 9:16 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20251008091621.23597-1-fw@strlen.de \
--to=fw@strlen.de \
--cc=netfilter-devel@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).