[PATCH net-next v9] selftests/net/openvswitch: add SCTP flow key support and test
From: Minxi Hou <hidden>
Date: 2026-08-05 03:45:10
Also in:
linux-kselftest, lkml
Subsystem:
kernel selftest framework, networking [general], openvswitch, the rest · Maintainers:
Shuah Khan, Shuah Khan, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Aaron Conole, Eelco Chaudron, Ilya Maximets, Linus Torvalds
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.
A forking daemon outlives the pid ovs_netns_spawn_daemon captures,
because the child handling an association is not signalled when only
that pid is killed. Start spawned daemons in their own session so
each leads its own process group, and signal the group on cleanup.
setsid ships in util-linux-core; selftests/vsock/vmtest.sh already
calls it unconditionally in this same tree, so this isn't a new class
of dependency for a kselftest shell script.
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 <redacted>
---
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
One property of the test worth stating, found by removing the
parse-table hunk and re-running. The probe at the top of
test_sctp_connect_v4 adds a flow with an sctp() key and skips when
that fails. A parser that drops the key gives the same -EINVAL as a
kernel without SCTP flow key support, so a missing parse-table entry
makes this test skip rather than fail: v5 dropped the ovs-dpctl.py
hunk while keeping the test, and that gap is exactly why the loss
did not show up as a failure. dec_ttl, icmpv6, psample and
drop_reason probe the same way, so I kept the pattern rather than
diverge from them here.
v8: https://lore.kernel.org/netdev/20260731062655.4088575-1-houminxi@gmail.com/ (local)
v7: https://lore.kernel.org/netdev/20260729064549.3647518-1-houminxi@gmail.com/ (local)
v6: https://lore.kernel.org/netdev/20260724150624.3457427-1-houminxi@gmail.com/ (local)
v5: https://lore.kernel.org/netdev/20260723084203.3483560-1-houminxi@gmail.com/ (local)
v4: https://lore.kernel.org/netdev/20260719162657.3263089-1-houminxi@gmail.com/ (local)
v3: https://lore.kernel.org/netdev/20260715015446.530018-1-houminxi@gmail.com/ (local)
v2: https://lore.kernel.org/netdev/20260707034718.2717982-1-houminxi@gmail.com/ (local)
v1: https://lore.kernel.org/netdev/20260702090908.1253688-1-houminxi@gmail.com/ (local)
---
.../testing/selftests/net/openvswitch/config | 1 +
.../selftests/net/openvswitch/openvswitch.sh | 110 +++++++++++++++++-
.../selftests/net/openvswitch/ovs-dpctl.py | 5 +
3 files changed, 113 insertions(+), 3 deletions(-)
diff --git a/tools/testing/selftests/net/openvswitch/config b/tools/testing/selftests/net/openvswitch/config
index c659749cd086..1c8f0a51905c 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 853dbc1b00d7..d11e89b54312 100755
--- a/tools/testing/selftests/net/openvswitch/openvswitch.sh
+++ b/tools/testing/selftests/net/openvswitch/openvswitch.sh@@ -34,6 +34,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() {
@@ -143,13 +144,17 @@ ovs_netns_spawn_daemon() { shift netns=$1 shift + # Give the daemon its own process group. A daemon that forks a + # child per connection leaves those children running when only the + # pid captured here is signalled. if [ "$netns" == "_default" ]; then - $* >> $ovs_dir/stdout 2>> $ovs_dir/stderr & + setsid $* >> $ovs_dir/stdout 2>> $ovs_dir/stderr & else - ip netns exec $netns $* >> $ovs_dir/stdout 2>> $ovs_dir/stderr & + setsid ip netns exec $netns $* \ + >> $ovs_dir/stdout 2>> $ovs_dir/stderr & fi pid=$! - ovs_sbx "$sbx" on_exit "kill -TERM $pid 2>/dev/null" + ovs_sbx "$sbx" on_exit "kill -TERM -$pid 2>/dev/null" } ovs_spawn_daemon() {
@@ -611,6 +616,105 @@ 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 + + # Probe: check if kernel supports sctp flow key. + ovs_add_flow "$t" sctp4 \ + 'in_port(1),eth(),eth_type(0x0800),ipv4(proto=132),sctp(dst=4443)' \ + '2' &>/dev/null + if [ $? -ne 0 ]; then + info "no support for sctp key - skipping" + ovs_exit_sig + return $ksft_skip + fi + ovs_del_flows "$t" sctp4 + + # 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. + ovs_netns_spawn_daemon "$t" "server" \ + socat -u 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 f3edd198223f..ce790d936832 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