[PATCH] selftests: netfilter: conntrack_sctp_collision.sh: Introduce SCTP INIT collision test

Yi Chen posted 1 patch 2 days, 13 hours ago
.../net/netfilter/conntrack_sctp_collision.sh | 84 ++++++++++++++-----
1 file changed, 65 insertions(+), 19 deletions(-)
[PATCH] selftests: netfilter: conntrack_sctp_collision.sh: Introduce SCTP INIT collision test
Posted by Yi Chen 2 days, 13 hours ago
The existing test covered a scenario where a delayed INIT_ACK chunk
updates the vtag in conntrack after the association has already been
established.

A similar issue can occur with a delayed SCTP INIT chunk.

Add a new simultaneous-open test case where the client's INIT is
delayed, allowing conntrack to establish the association based on
the server-initiated handshake.

When the stale INIT arrives later, it may overwirte the vtag in
conntrack, causing subsequent SCTP DATA chunks to be considered
as invalid and then dropped by nft rules matching on ct state invalid.

This test verifies such stale INIT chunks do not corrupt conntrack
state.

Signed-off-by: Yi Chen <yiche@redhat.com>
---
 .../net/netfilter/conntrack_sctp_collision.sh | 84 ++++++++++++++-----
 1 file changed, 65 insertions(+), 19 deletions(-)

diff --git a/tools/testing/selftests/net/netfilter/conntrack_sctp_collision.sh b/tools/testing/selftests/net/netfilter/conntrack_sctp_collision.sh
index d860f7d9744b..7f8f1b6b746a 100755
--- a/tools/testing/selftests/net/netfilter/conntrack_sctp_collision.sh
+++ b/tools/testing/selftests/net/netfilter/conntrack_sctp_collision.sh
@@ -23,8 +23,19 @@ SERVER_PORT=1234
 CLIENT_GW="198.51.200.2"
 SERVER_GW="198.51.100.2"
 
+assert_pass()
+{
+	local ret=$?
+	if [ $ret != 0 ]; then
+		echo "FAIL: ${@}"
+		exit $ksft_fail
+	else
+		echo "PASS: ${@}"
+	fi
+}
+
 # setup the topo
-setup() {
+topo_setup() {
 	setup_ns CLIENT_NS SERVER_NS ROUTER_NS
 	ip -n "$SERVER_NS" link add link0 type veth peer name link1 netns "$ROUTER_NS"
 	ip -n "$CLIENT_NS" link add link3 type veth peer name link2 netns "$ROUTER_NS"
@@ -42,21 +53,51 @@ setup() {
 	ip -n "$CLIENT_NS" link set link3 up
 	ip -n "$CLIENT_NS" addr add $CLIENT_IP/24 dev link3
 	ip -n "$CLIENT_NS" route add $SERVER_IP dev link3 via $CLIENT_GW
+}
 
-	# simulate the delay on OVS upcall by setting up a delay for INIT_ACK with
-	# tc on $SERVER_NS side
-	tc -n "$SERVER_NS" qdisc add dev link0 root handle 1: htb r2q 64
-	tc -n "$SERVER_NS" class add dev link0 parent 1: classid 1:1 htb rate 100mbit
-	tc -n "$SERVER_NS" filter add dev link0 parent 1: protocol ip u32 match ip protocol 132 \
-		0xff match u8 2 0xff at 32 flowid 1:1
-	if ! tc -n "$SERVER_NS" qdisc add dev link0 parent 1:1 handle 10: netem delay 1200ms; then
-		echo "SKIP: Cannot add netem qdisc"
-		exit $ksft_skip
-	fi
+conf_delay()
+{
+	# simulate the delay on OVS upcall by setting up a delay for INIT_ACK/INIT with
+	case $1 in
+	"INIT") chunk_type=1
+		# tc on $CLIENT_NS side
+		tc -n "$CLIENT_NS" qdisc add dev link3 root handle 1: htb r2q 64
+		tc -n "$CLIENT_NS" class add dev link3 parent 1: classid 1:1 htb rate 100mbit
+		tc -n "$CLIENT_NS" filter add dev link3 parent 1: protocol ip \
+			u32 match ip protocol 132 0xff match u8 $chunk_type 0xff at 32 flowid 1:1
+		if ! tc -n "$CLIENT_NS" qdisc add dev link3 parent 1:1 handle 10: \
+			netem delay 1200ms; then
+			echo "SKIP: Cannot add netem qdisc"
+			exit $ksft_skip
+		fi
+		;;
+	"INIT_ACK") chunk_type=2
+		# tc on $SERVER_NS side
+		tc -n "$SERVER_NS" qdisc add dev link0 root handle 1: htb r2q 64
+		tc -n "$SERVER_NS" class add dev link0 parent 1: classid 1:1 htb rate 100mbit
+		tc -n "$SERVER_NS" filter add dev link0 parent 1: protocol ip \
+			u32 match ip protocol 132 0xff match u8 $chunk_type 0xff at 32 flowid 1:1
+		if ! tc -n "$SERVER_NS" qdisc add dev link0 parent 1:1 handle 10: \
+			netem delay 1200ms; then
+			echo "SKIP: Cannot add netem qdisc"
+			exit $ksft_skip
+		fi
+		;;
+	esac
 
 	# simulate the ctstate check on OVS nf_conntrack
-	ip net exec "$ROUTER_NS" iptables -A FORWARD -m state --state INVALID,UNTRACKED -j DROP
-	ip net exec "$ROUTER_NS" iptables -A INPUT -p sctp -j DROP
+	ip net exec "$ROUTER_NS" nft -f - <<-EOF
+	table ip t {
+	        chain forward {
+	                type filter hook forward priority filter; policy accept;
+	                meta l4proto { icmp, icmpv6 } accept
+	                ct state new counter accept
+	                ct state established,related counter accept
+	                ct state invalid log flags all counter drop
+	                counter
+	        }
+	}
+	EOF
 
 	# use a smaller number for assoc's max_retrans to reproduce the issue
 	modprobe -q sctp
@@ -64,8 +105,6 @@ setup() {
 }
 
 cleanup() {
-	ip net exec "$CLIENT_NS" pkill sctp_collision >/dev/null 2>&1
-	ip net exec "$SERVER_NS" pkill sctp_collision >/dev/null 2>&1
 	cleanup_all_ns
 }
 
@@ -81,7 +120,14 @@ do_test() {
 
 # run the test case
 trap cleanup EXIT
-setup && \
-echo "Test for SCTP Collision in nf_conntrack:" && \
-do_test && echo "PASS!"
-exit $?
+
+echo "Test for SCTP INIT_ACK Collision in nf_conntrack:"
+topo_setup && conf_delay INIT_ACK
+do_test
+assert_pass "The delayed INIT_ACK chunk did not disrupt sctp ct tracking."
+
+echo "Test for SCTP INIT Collision in nf_conntrack:"
+
+topo_setup && conf_delay INIT
+do_test
+assert_pass "The delayed INIT chunk did not disrupt sctp ct tracking."
-- 
2.53.0
Re: [PATCH] selftests: netfilter: conntrack_sctp_collision.sh: Introduce SCTP INIT collision test
Posted by Long Xin 2 days, 11 hours ago
On Mon, Mar 30, 2026 at 7:35 AM Yi Chen <yiche@redhat.com> wrote:
>
> The existing test covered a scenario where a delayed INIT_ACK chunk
> updates the vtag in conntrack after the association has already been
> established.
>
> A similar issue can occur with a delayed SCTP INIT chunk.
>
> Add a new simultaneous-open test case where the client's INIT is
> delayed, allowing conntrack to establish the association based on
> the server-initiated handshake.
>
> When the stale INIT arrives later, it may overwirte the vtag in
> conntrack, causing subsequent SCTP DATA chunks to be considered
> as invalid and then dropped by nft rules matching on ct state invalid.
>
> This test verifies such stale INIT chunks do not corrupt conntrack
> state.
>
> Signed-off-by: Yi Chen <yiche@redhat.com>
Thanks for the patch, plase see comment below.

> ---
>  .../net/netfilter/conntrack_sctp_collision.sh | 84 ++++++++++++++-----
>  1 file changed, 65 insertions(+), 19 deletions(-)
>
> diff --git a/tools/testing/selftests/net/netfilter/conntrack_sctp_collision.sh b/tools/testing/selftests/net/netfilter/conntrack_sctp_collision.sh
> index d860f7d9744b..7f8f1b6b746a 100755
> --- a/tools/testing/selftests/net/netfilter/conntrack_sctp_collision.sh
> +++ b/tools/testing/selftests/net/netfilter/conntrack_sctp_collision.sh
> @@ -23,8 +23,19 @@ SERVER_PORT=1234
>  CLIENT_GW="198.51.200.2"
>  SERVER_GW="198.51.100.2"
>
> +assert_pass()
> +{
> +       local ret=$?
> +       if [ $ret != 0 ]; then
> +               echo "FAIL: ${@}"
> +               exit $ksft_fail
You should not print FAIL and exit $ksft_fail if ret is $ksft_skip
(The test case was skipped).

> +       else
> +               echo "PASS: ${@}"
> +       fi
> +}
> +
>  # setup the topo
> -setup() {
> +topo_setup() {
>         setup_ns CLIENT_NS SERVER_NS ROUTER_NS
>         ip -n "$SERVER_NS" link add link0 type veth peer name link1 netns "$ROUTER_NS"
>         ip -n "$CLIENT_NS" link add link3 type veth peer name link2 netns "$ROUTER_NS"
> @@ -42,21 +53,51 @@ setup() {
>         ip -n "$CLIENT_NS" link set link3 up
>         ip -n "$CLIENT_NS" addr add $CLIENT_IP/24 dev link3
>         ip -n "$CLIENT_NS" route add $SERVER_IP dev link3 via $CLIENT_GW
> +}
>
> -       # simulate the delay on OVS upcall by setting up a delay for INIT_ACK with
> -       # tc on $SERVER_NS side
> -       tc -n "$SERVER_NS" qdisc add dev link0 root handle 1: htb r2q 64
> -       tc -n "$SERVER_NS" class add dev link0 parent 1: classid 1:1 htb rate 100mbit
> -       tc -n "$SERVER_NS" filter add dev link0 parent 1: protocol ip u32 match ip protocol 132 \
> -               0xff match u8 2 0xff at 32 flowid 1:1
> -       if ! tc -n "$SERVER_NS" qdisc add dev link0 parent 1:1 handle 10: netem delay 1200ms; then
> -               echo "SKIP: Cannot add netem qdisc"
> -               exit $ksft_skip
> -       fi
> +conf_delay()
> +{
> +       # simulate the delay on OVS upcall by setting up a delay for INIT_ACK/INIT with
> +       case $1 in
> +       "INIT") chunk_type=1
> +               # tc on $CLIENT_NS side
> +               tc -n "$CLIENT_NS" qdisc add dev link3 root handle 1: htb r2q 64
> +               tc -n "$CLIENT_NS" class add dev link3 parent 1: classid 1:1 htb rate 100mbit
> +               tc -n "$CLIENT_NS" filter add dev link3 parent 1: protocol ip \
> +                       u32 match ip protocol 132 0xff match u8 $chunk_type 0xff at 32 flowid 1:1
> +               if ! tc -n "$CLIENT_NS" qdisc add dev link3 parent 1:1 handle 10: \
> +                       netem delay 1200ms; then
> +                       echo "SKIP: Cannot add netem qdisc"
> +                       exit $ksft_skip
> +               fi
> +               ;;
> +       "INIT_ACK") chunk_type=2
> +               # tc on $SERVER_NS side
> +               tc -n "$SERVER_NS" qdisc add dev link0 root handle 1: htb r2q 64
> +               tc -n "$SERVER_NS" class add dev link0 parent 1: classid 1:1 htb rate 100mbit
> +               tc -n "$SERVER_NS" filter add dev link0 parent 1: protocol ip \
> +                       u32 match ip protocol 132 0xff match u8 $chunk_type 0xff at 32 flowid 1:1
> +               if ! tc -n "$SERVER_NS" qdisc add dev link0 parent 1:1 handle 10: \
> +                       netem delay 1200ms; then
> +                       echo "SKIP: Cannot add netem qdisc"
> +                       exit $ksft_skip
> +               fi
> +               ;;
> +       esac
The code of case "INIT" and "INIT_ACK" are pretty simliar, please try
to move the tc command out of the case statement with $ns and $link.

>
>         # simulate the ctstate check on OVS nf_conntrack
> -       ip net exec "$ROUTER_NS" iptables -A FORWARD -m state --state INVALID,UNTRACKED -j DROP
> -       ip net exec "$ROUTER_NS" iptables -A INPUT -p sctp -j DROP
> +       ip net exec "$ROUTER_NS" nft -f - <<-EOF
> +       table ip t {
> +               chain forward {
> +                       type filter hook forward priority filter; policy accept;
> +                       meta l4proto { icmp, icmpv6 } accept
> +                       ct state new counter accept
> +                       ct state established,related counter accept
> +                       ct state invalid log flags all counter drop
> +                       counter
> +               }
> +       }
> +       EOF
I think you need to check if 'nft' is available
Either in here by:

if [ $? -ne 0 ]; then
        echo -n "SKIP: Could not load ruleset: "
        nft --version
        exit $ksft_skip
fi

Or at the beginning by:

checktool "nft --version" "run test without nft tool"

>
>         # use a smaller number for assoc's max_retrans to reproduce the issue
>         modprobe -q sctp
> @@ -64,8 +105,6 @@ setup() {
>  }
>
>  cleanup() {
> -       ip net exec "$CLIENT_NS" pkill sctp_collision >/dev/null 2>&1
> -       ip net exec "$SERVER_NS" pkill sctp_collision >/dev/null 2>&1
>         cleanup_all_ns
>  }
>
> @@ -81,7 +120,14 @@ do_test() {
>
>  # run the test case
>  trap cleanup EXIT
> -setup && \
> -echo "Test for SCTP Collision in nf_conntrack:" && \
> -do_test && echo "PASS!"
> -exit $?
> +
> +echo "Test for SCTP INIT_ACK Collision in nf_conntrack:"
> +topo_setup && conf_delay INIT_ACK
How will the error returned from conf_delay() be handled?

> +do_test
> +assert_pass "The delayed INIT_ACK chunk did not disrupt sctp ct tracking."
> +
> +echo "Test for SCTP INIT Collision in nf_conntrack:"
> +
> +topo_setup && conf_delay INIT
> +do_test
> +assert_pass "The delayed INIT chunk did not disrupt sctp ct tracking."
> --
> 2.53.0
>
The current kernel will fail with this test case, please hold the patch
until the fix is submitted. It is still in testing, as you already know.

thanks.
Re: [PATCH] selftests: netfilter: conntrack_sctp_collision.sh: Introduce SCTP INIT collision test
Posted by Jakub Kicinski 2 days, 11 hours ago
On Mon, 30 Mar 2026 19:35:09 +0800 Yi Chen wrote:
> The existing test covered a scenario where a delayed INIT_ACK chunk
> updates the vtag in conntrack after the association has already been
> established.
> 
> A similar issue can occur with a delayed SCTP INIT chunk.
> 
> Add a new simultaneous-open test case where the client's INIT is
> delayed, allowing conntrack to establish the association based on
> the server-initiated handshake.
> 
> When the stale INIT arrives later, it may overwirte the vtag in
> conntrack, causing subsequent SCTP DATA chunks to be considered
> as invalid and then dropped by nft rules matching on ct state invalid.
> 
> This test verifies such stale INIT chunks do not corrupt conntrack
> state.

Now it fails in NIPA:

TAP version 13
1..1
# timeout set to 1800
# selftests: net/netfilter: conntrack_sctp_collision.sh
# Test for SCTP INIT_ACK Collision in nf_conntrack:
# Client: rcvd! 6
# Server: sent! 6
# PASS: The delayed INIT_ACK chunk did not disrupt sctp ct tracking.
# Test for SCTP INIT Collision in nf_conntrack:
# Failed to recv msg -1
# Failed to recv msg -1
# FAIL: The delayed INIT chunk did not disrupt sctp ct tracking.
not ok 1 selftests: net/netfilter: conntrack_sctp_collision.sh # exit=1