From: Minxi Hou <houminxi@gmail.com>
To: netdev@vger.kernel.org
Cc: aconole@redhat.com, davem@davemloft.net, dev@openvswitch.org,
echaudro@redhat.com, edumazet@google.com, horms@kernel.org,
i.maximets@ovn.org, i.maximets@redhat.com, kuba@kernel.org,
linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org,
pabeni@redhat.com, shuah@kernel.org,
Minxi Hou <houminxi@gmail.com>
Subject: [PATCH net-next v10] selftests/net/openvswitch: add SCTP flow key support and test
Date: Tue, 11 Aug 2026 14:16:45 -0400 [thread overview]
Message-ID: <20260811181645.1918420-1-houminxi@gmail.com> (raw)
The ovskey flow-string parser has no OVS_KEY_ATTR_SCTP entry, so a
flow string containing sctp(src=.../dst=...) parses without error but
silently drops the L4 key. The resulting flow carries only
ipv4(proto=132), and the kernel rejects it: match_validate() in
flow_netlink.c requires OVS_KEY_ATTR_SCTP when the IP protocol is
IPPROTO_SCTP and returns -EINVAL for the missing key.
Register OVS_KEY_ATTR_SCTP in the parse table and add a matching
selftest that verifies SCTP flow key matching (sctp src/dst port).
One listener serves the whole test. socat's fork option handles each
association in a child, so the flow rules are the only thing that
changes between the three phases and the listener is never restarted
underneath them. -t 1 bounds how long a forked child lingers after
its association closes, and the existing kill -TERM of the captured
pid on teardown removes the listener itself.
Also enable CONFIG_IP_SCTP in the selftest kernel config. The config
checker strips underscores before comparing keys, so the entry sorts
before CONFIG_IPV6 rather than after it.
Signed-off-by: Minxi Hou <houminxi@gmail.com>
---
v10:
- drop the sctp() key probe. OVS_KEY_ATTR_SCTP is unconditional in
the kernel datapath (no kconfig gates it in flow_netlink.c), so
the probe only ever fired for a missing ovs-dpctl.py parse-table
entry and turned that regression into a skip: the same gap that
hid the v5 loss. A parse-table regression now fails the test
instead of skipping it (Jakub)
- drop setsid. The forked children exit when their association
closes, socat -t 1 bounds how long they linger, and the existing
kill -TERM of the captured pid removes the listener itself (Jakub)
- bound listener child lifetime with socat -t 1 (Jakub)
v9:
- one forking listener for the whole test instead of restarting it
between phases (Aaron)
- signal the daemon's process group on cleanup so the children a
forking listener leaves behind are reaped
- sort CONFIG_IP_SCTP the way the config checker compares keys
v8: https://lore.kernel.org/netdev/20260731062655.4088575-1-houminxi@gmail.com/
v7: https://lore.kernel.org/netdev/20260729064549.3647518-1-houminxi@gmail.com/
v6: https://lore.kernel.org/netdev/20260724150624.3457427-1-houminxi@gmail.com/
v5: https://lore.kernel.org/netdev/20260723084203.3483560-1-houminxi@gmail.com/
v4: https://lore.kernel.org/netdev/20260719162657.3263089-1-houminxi@gmail.com/
v3: https://lore.kernel.org/netdev/20260715015446.530018-1-houminxi@gmail.com/
v2: https://lore.kernel.org/netdev/20260707034718.2717982-1-houminxi@gmail.com/
v1: https://lore.kernel.org/netdev/20260702090908.1253688-1-houminxi@gmail.com/
---
.../testing/selftests/net/openvswitch/config | 1 +
.../selftests/net/openvswitch/openvswitch.sh | 90 +++++++++++++++++++
.../selftests/net/openvswitch/ovs-dpctl.py | 5 ++
3 files changed, 96 insertions(+)
diff --git a/tools/testing/selftests/net/openvswitch/config b/tools/testing/selftests/net/openvswitch/config
index 05ca6affb510..a825e0b5c88e 100644
--- a/tools/testing/selftests/net/openvswitch/config
+++ b/tools/testing/selftests/net/openvswitch/config
@@ -1,5 +1,6 @@
CONFIG_GENEVE=m
CONFIG_INET_DIAG=y
+CONFIG_IP_SCTP=y
CONFIG_IPV6=y
CONFIG_NETFILTER=y
CONFIG_NET_IPGRE=m
diff --git a/tools/testing/selftests/net/openvswitch/openvswitch.sh b/tools/testing/selftests/net/openvswitch/openvswitch.sh
index f63001dc2510..a31f7fb6882d 100755
--- a/tools/testing/selftests/net/openvswitch/openvswitch.sh
+++ b/tools/testing/selftests/net/openvswitch/openvswitch.sh
@@ -33,6 +33,7 @@ tests="
action_set set: SET action rewrites fields
trunc trunc: output truncation
icmpv6 icmpv6: ICMPv6 echo type match
+ sctp_connect_v4 sctp: SCTP flow key matching
psample psample: Sampling packets with psample"
info() {
@@ -610,6 +611,95 @@ test_icmpv6() {
return 0
}
+# Check for an SCTP endpoint via /proc, which works without sctp_diag.
+sctp_eps_has() {
+ ip netns exec "$1" awk -v p="$2" '$6==p' /proc/net/sctp/eps | grep -q .
+}
+
+# sctp_connect_v4 test
+# - sctp(dst=4443) matches client-to-server INIT
+# - sctp(src=4443) matches server-to-client INIT-ACK
+# - remove flows and verify connection fails, reinstall and recover
+test_sctp_connect_v4() {
+ local t="test_sctp_connect_v4"
+ local srv_ip=172.31.110.20
+
+ modprobe -q sctp 2>/dev/null || return "$ksft_skip"
+ socat -V 2>&1 | grep -q "define WITH_SCTP" || return "$ksft_skip"
+
+ sbx_add "$t" || return $?
+ ovs_add_dp "$t" sctp4 || return 1
+
+ info "create namespaces"
+ for ns in client server; do
+ ovs_add_netns_and_veths "$t" "sctp4" "$ns" \
+ "${ns:0:1}0" "${ns:0:1}1" || return 1
+ done
+
+ ip netns exec client ip addr add 172.31.110.10/24 dev c1
+ ip netns exec client ip link set c1 up
+ ip netns exec server ip addr add "${srv_ip}/24" dev s1
+ ip netns exec server ip link set s1 up
+
+ # ARP forwarding
+ ovs_add_flow "$t" sctp4 \
+ 'in_port(1),eth(),eth_type(0x0806),arp()' \
+ '2' || return 1
+ ovs_add_flow "$t" sctp4 \
+ 'in_port(2),eth(),eth_type(0x0806),arp()' \
+ '1' || return 1
+
+ # SCTP port matching: dst for request, src for reply
+ ovs_add_flow "$t" sctp4 \
+ 'in_port(1),eth(),eth_type(0x0800),ipv4(proto=132),sctp(dst=4443)' \
+ '2' || return 1
+ ovs_add_flow "$t" sctp4 \
+ 'in_port(2),eth(),eth_type(0x0800),ipv4(proto=132),sctp(src=4443)' \
+ '1' || return 1
+
+ # The listener forks a child per association, so one instance serves
+ # the whole test and the flows stay the only variable. -t 1 bounds
+ # how long a child lingers after its association closes.
+ ovs_netns_spawn_daemon "$t" "server" \
+ socat -u -t 1 SCTP4-LISTEN:4443,fork STDOUT
+ ovs_wait sctp_eps_has server 4443 || return 1
+
+ info "verify SCTP association with port-keyed flows"
+ ovs_sbx "$t" ip netns exec client \
+ timeout 3 socat -u STDIN "SCTP4-CONNECT:${srv_ip}:4443" </dev/null \
+ || return 1
+
+ ovs_del_flows "$t" sctp4
+
+ info "verify connection fails without flows"
+ ovs_add_flow "$t" sctp4 \
+ 'in_port(1),eth(),eth_type(0x0806),arp()' \
+ '2' || return 1
+ ovs_add_flow "$t" sctp4 \
+ 'in_port(2),eth(),eth_type(0x0806),arp()' \
+ '1' || return 1
+
+ ovs_sbx "$t" ip netns exec client \
+ timeout 3 socat -u STDIN "SCTP4-CONNECT:${srv_ip}:4443" </dev/null \
+ >/dev/null 2>&1 \
+ && { info "connection should fail without flows"
+ return 1; }
+
+ info "reinstall flows and verify recovery"
+ ovs_add_flow "$t" sctp4 \
+ 'in_port(1),eth(),eth_type(0x0800),ipv4(proto=132),sctp(dst=4443)' \
+ '2' || return 1
+ ovs_add_flow "$t" sctp4 \
+ 'in_port(2),eth(),eth_type(0x0800),ipv4(proto=132),sctp(src=4443)' \
+ '1' || return 1
+
+ ovs_sbx "$t" ip netns exec client \
+ timeout 3 socat -u STDIN "SCTP4-CONNECT:${srv_ip}:4443" </dev/null \
+ || return 1
+
+ return 0
+}
+
# psample test
# - use psample to observe packets
test_psample() {
diff --git a/tools/testing/selftests/net/openvswitch/ovs-dpctl.py b/tools/testing/selftests/net/openvswitch/ovs-dpctl.py
index 3ece07d47281..1615843c225e 100644
--- a/tools/testing/selftests/net/openvswitch/ovs-dpctl.py
+++ b/tools/testing/selftests/net/openvswitch/ovs-dpctl.py
@@ -1984,6 +1984,11 @@ class ovskey(nla):
"udp",
ovskey.ovs_key_udp,
),
+ (
+ "OVS_KEY_ATTR_SCTP",
+ "sctp",
+ ovskey.ovs_key_sctp,
+ ),
(
"OVS_KEY_ATTR_ICMP",
"icmp",
--
2.55.0
next reply other threads:[~2026-08-11 18:16 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-11 18:16 Minxi Hou [this message]
2026-08-13 11:15 ` [PATCH net-next v10] selftests/net/openvswitch: add SCTP flow key support and test Aaron Conole
2026-08-14 3:24 ` patchwork-bot+netdevbpf
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=20260811181645.1918420-1-houminxi@gmail.com \
--to=houminxi@gmail.com \
--cc=aconole@redhat.com \
--cc=davem@davemloft.net \
--cc=dev@openvswitch.org \
--cc=echaudro@redhat.com \
--cc=edumazet@google.com \
--cc=horms@kernel.org \
--cc=i.maximets@ovn.org \
--cc=i.maximets@redhat.com \
--cc=kuba@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=shuah@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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.