selftests/net/openvswitch: add SCTP flow key support and test

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>
Reviewed-by: Aaron Conole <aconole@redhat.com>
Link: https://patch.msgid.link/20260811181645.1918420-1-houminxi@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Minxi Hou 2026-08-11 14:16:45 -04:00 committed by Jakub Kicinski
parent 5ba017f9ef
commit 07a9e39750
3 changed files with 96 additions and 0 deletions

View File

@ -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

View File

@ -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() {

View File

@ -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",