This series extends the Open vSwitch SCTP selftests: patch 1 adds the
IPv6 flow-key test (mirroring the merged IPv4 test), and patch 2 adds an
SCTP NAT test exercising port translation across conntrack.
v3 -> v4:
- test_sctp_connect_v6 now also asserts that installing a keyless
ipv6(proto=132) flow is refused with EINVAL, pinning the reject side
of the match_validate() sctp() key requirement (pointed out in
review: only the accept side was exercised, so a regression dropping
the requirement would have passed unnoticed). The refusal is checked
to be errno 22 specifically, not a flow-string parse error.
- Wrap a few over-80-column lines in the NAT test; checkpatch strict
is now clean for the series.
- Rebased onto 548b86839f7f.
Signed-off-by: Minxi Hou <redacted>
Minxi Hou (2):
selftests/net/openvswitch: add SCTP flow key test over IPv6
selftests/net/openvswitch: add SCTP NAT test with port translation
.../testing/selftests/net/openvswitch/config | 1 +
.../selftests/net/openvswitch/openvswitch.sh | 201 ++++++++++++++++++
2 files changed, 202 insertions(+)
base-commit: 548b86839f7fb819a4d6c83b71c73ec378d24275
--
2.55.0
The merged SCTP test covers only IPv4. The SCTP branch of the IPv6
extractor (the proto=132 walk after parse_ipv6hdr) and the v6 side of
the SCTP netlink validation (match_validate() requires the sctp() key
whenever ipv6(proto=132) is matched) have no selftest coverage.
Add test_sctp_connect_v6 mirroring the v4 test: bare icmpv6() flows
forward NS/NA, and ipv6(proto=132),sctp(dst=4443)/sctp(src=4443)
flows gate the association in the same three phases (flows installed,
removed, reinstalled). A keyless ipv6(proto=132) install must be
refused with EINVAL, pinning the reject side of the match_validate()
rule; without it a regression dropping the requirement would pass
unnoticed. The refusal is asserted to be EINVAL specifically, not a
parse error of the flow string. After the association succeeds the
test also pushes a known payload across and waits for the listener
to log it, proving the datapath carries the association's traffic
end to end, not only its handshake. Skips when the sctp module is
missing, socat lacks SCTP support, or IPv6 is unavailable; an
association or payload failure with the flows installed fails the
test.
Signed-off-by: Minxi Hou <redacted>
---
.../selftests/net/openvswitch/openvswitch.sh | 108 ++++++++++++++++++
1 file changed, 108 insertions(+)
@@ -700,6 +701,113 @@ test_sctp_connect_v4() {return0}+# sctp_connect_v6 test+# - sctp(dst=4443) matches client-to-server INIT+# - sctp(src=4443) matches server-to-client INIT-ACK+# - icmpv6 NS/NA flows forward neighbour discovery+# - remove flows and verify connection fails, reinstall and recover+test_sctp_connect_v6(){+localt="test_sctp_connect_v6"+localv6="eth_type(0x86dd),ipv6(proto=132)"+localpayload="SCTP6_DATA_OK"+localrxfile="${ovs_base}/${t}/sctp-rx.txt"++modprobe-qsctp2>/dev/null||return"$ksft_skip"+socat-V2>&1|grep-q"define WITH_SCTP"||return"$ksft_skip"+[-e/proc/sys/net/ipv6]||return"$ksft_skip"++sbx_add"$t"||return$?+ovs_add_dp"$t"sctp6||return1++info"create namespaces"+fornsinclientserver;do+ovs_add_netns_and_veths"$t""sctp6""$ns"\+"${ns:0:1}0""${ns:0:1}1"||return1+done++ipnetnsexecclientipaddraddfd00::1/64devc1nodad+ipnetnsexecclientiplinksetc1up+ipnetnsexecserveripaddraddfd00::2/64devs1nodad+ipnetnsexecserveriplinksets1up++# NS/NA forwarding+ovs_add_flow"$t"sctp6\+'in_port(1),eth(),eth_type(0x86dd),ipv6(proto=58),icmpv6()'\+'2'||return1+ovs_add_flow"$t"sctp6\+'in_port(2),eth(),eth_type(0x86dd),ipv6(proto=58),icmpv6()'\+'1'||return1++# SCTP port matching: dst for request, src for reply+ovs_add_flow"$t"sctp6\+"in_port(1),eth(),$v6,sctp(dst=4443)"\+'2'||return1+ovs_add_flow"$t"sctp6\+"in_port(2),eth(),$v6,sctp(src=4443)"\+'1'||return1++# A keyless ipv6(proto=132) install must be refused (EINVAL):+# match_validate() requires the sctp() key. Pin the reject side+# of that rule; the keyed installs above cover the accept side.+# Verify the refusal is EINVAL (missing key), not a parse error.+err=$(ovs_sbx"$t"python3$ovs_base/ovs-dpctl.pyadd-flowsctp6\+"in_port(1),eth(),$v6"'2'2>&1>/dev/null)\+&&{info"keyless SCTP flow should be refused"+return1;}+echo"$err"|grep-q"(22,"||{+info"keyless SCTP flow refused for wrong reason: $err"+return1+}++ovs_netns_spawn_daemon"$t""server"\+socat-u-t1SCTP6-LISTEN:4443,fork\+OPEN:"$rxfile",creat,append+ovs_waitsctp_eps_hasserver4443||return1++info"verify SCTP association with port-keyed flows"+ovs_sbx"$t"ipnetnsexecclient\+timeout3socat-uSTDIN"SCTP6-CONNECT:[fd00::2]:4443"</dev/null\+||return1++info"verify SCTP DATA chunk crosses the datapath"+ovs_sbx"$t"ipnetnsexecclient\+timeout3socat-uSTDIN"SCTP6-CONNECT:[fd00::2]:4443"\+<<<"$payload"||return1+ovs_waitgrep-q"$payload""$rxfile"\+||{info"server did not receive SCTP DATA payload"+return1;}++ovs_del_flows"$t"sctp6++info"verify connection fails without flows"+ovs_add_flow"$t"sctp6\+'in_port(1),eth(),eth_type(0x86dd),ipv6(proto=58),icmpv6()'\+'2'||return1+ovs_add_flow"$t"sctp6\+'in_port(2),eth(),eth_type(0x86dd),ipv6(proto=58),icmpv6()'\+'1'||return1++ovs_sbx"$t"ipnetnsexecclient\+timeout3socat-uSTDIN"SCTP6-CONNECT:[fd00::2]:4443"</dev/null\+>/dev/null2>&1\+&&{info"connection should fail without flows"+return1;}++info"reinstall flows and verify recovery"+ovs_add_flow"$t"sctp6\+"in_port(1),eth(),$v6,sctp(dst=4443)"\+'2'||return1+ovs_add_flow"$t"sctp6\+"in_port(2),eth(),$v6,sctp(src=4443)"\+'1'||return1++ovs_sbx"$t"ipnetnsexecclient\+timeout3socat-uSTDIN"SCTP6-CONNECT:[fd00::2]:4443"</dev/null\+||return1++return0+}+# psample test# - use psample to observe packets test_psample(){
After conntrack NAT rewrites a packet, OVS refreshes the cached flow
key in ovs_nat_update_key(), which has a per-protocol branch for the
L4 ports (UDP/TCP/SCTP, conntrack.c). Address-only NAT cannot tell a
working SCTP branch from a missing one: the ports survive unchanged
either way, so a post-recirc match on the original port stays green
even with the branch deleted. The suite's NAT coverage drives TCP
over nc, and the merged SCTP test has no conntrack in the path, so
the SCTP branch goes unexercised.
Add test_sctp_nat_connect_v4: untracked client traffic to
192.168.0.20:4443 hits ct(commit,nat(dst=172.31.110.20:5555)),recirc,
and the post-recirc flows match the translated tuple,
ipv4(dst=172.31.110.20),sctp(dst=5555). Reply traffic is matched on
the restored original tuple, sctp(src=4443). With the SCTP branch
broken the translated port never reaches the key, no post-recirc
flow matches, and the association fails. The probe flow uses the
same ct+nat action as the real flows, so a kernel without
CONFIG_NF_NAT rejects it at flow-add time and the test skips instead
of failing; CONFIG_NF_CT_PROTO_SCTP=y is added to the config fragment
for the same reason (the symbol is user-visible only with
NETFILTER_ADVANCED=y). After the association succeeds the test pushes
a known payload across and waits for the listener to log it.
Signed-off-by: Minxi Hou <redacted>
---
.../testing/selftests/net/openvswitch/config | 1 +
.../selftests/net/openvswitch/openvswitch.sh | 93 +++++++++++++++++++
2 files changed, 94 insertions(+)
@@ -1199,6 +1200,98 @@ test_nat_connect_v4 () {return0}+# sctp_nat_connect_v4 test+# - SCTP association crosses a ct(commit,nat(dst=ip:port)) DNAT+# - post-recirc flows match the translated address and port, so the+# SCTP branch of the post-NAT flow key update is load-bearing+test_sctp_nat_connect_v4(){+localt="test_sctp_nat_connect_v4"+localpayload="SCTP_NAT_DATA_OK"+localrxfile="${ovs_base}/${t}/sctp-rx.txt"++modprobe-qsctp2>/dev/null||return"$ksft_skip"+socat-V2>&1|grep-q"define WITH_SCTP"||return"$ksft_skip"+# SCTP conntrack is compiled into nf_conntrack.ko, so check that+# loading it actually exposed the SCTP conntrack sysctls.+modprobe-qnf_conntrack2>/dev/null||return"$ksft_skip"+[-e/proc/sys/net/netfilter/nf_conntrack_sctp_timeout_established]\+||{info"no SCTP conntrack support - skipping"+return"$ksft_skip";}++sbx_add"test_sctp_nat_connect_v4"||return$?++ovs_add_dp"test_sctp_nat_connect_v4"sctpnat4||return1+info"create namespaces"+fornsinclientserver;do+ovs_add_netns_and_veths"test_sctp_nat_connect_v4""sctpnat4"\+"$ns""${ns:0:1}0""${ns:0:1}1"||return1+done++ipnetnsexecclientipaddradd172.31.110.10/24devc1+ipnetnsexecclientiplinksetc1up+ipnetnsexecserveripaddradd172.31.110.20/24devs1+ipnetnsexecserveriplinksets1up++ipnetnsexecclientiprouteadddefaultvia172.31.110.20++# Check if the ct and nat actions can be configured.+ovs_add_flow"test_sctp_nat_connect_v4"sctpnat4\+'in_port(1),eth(),eth_type(0x0800),ipv4()'\+'ct(commit,nat(dst=172.31.110.20:5555)),recirc(0x1)'\+&>/dev/null+if[$?==1];then+info"no support for ct/nat actions - skipping"+ovs_exit_sig+return$ksft_skip+fi++ovs_del_flows"test_sctp_nat_connect_v4"sctpnat4++ovs_add_flow"test_sctp_nat_connect_v4"sctpnat4\+'in_port(1),eth(),eth_type(0x0806),arp()''2'||return1+ovs_add_flow"test_sctp_nat_connect_v4"sctpnat4\+'in_port(2),eth(),eth_type(0x0806),arp()''1'||return1+ovs_add_flow"test_sctp_nat_connect_v4"sctpnat4\+"ct_state(-trk),in_port(1),eth(),eth_type(0x0800),"\+"ipv4(dst=192.168.0.20)"\+"ct(commit,nat(dst=172.31.110.20:5555)),recirc(0x1)"||return1+ovs_add_flow"test_sctp_nat_connect_v4"sctpnat4\+"ct_state(-trk),in_port(2),eth(),eth_type(0x0800),ipv4()"\+"ct(commit,nat),recirc(0x2)"||return1++ovs_add_flow"test_sctp_nat_connect_v4"sctpnat4\+"recirc_id(0x1),ct_state(+trk-inv),in_port(1),eth(),"\+"eth_type(0x0800),ipv4(dst=172.31.110.20,proto=132),"\+"sctp(dst=5555)"\+"2"||return1+ovs_add_flow"test_sctp_nat_connect_v4"sctpnat4\+"recirc_id(0x2),ct_state(+trk-inv),in_port(2),eth(),"\+"eth_type(0x0800),ipv4(src=192.168.0.20,proto=132),"\+"sctp(src=4443)"\+"1"||return1++ovs_netns_spawn_daemon"test_sctp_nat_connect_v4""server"\+socat-u-t1SCTP4-LISTEN:5555,fork\+OPEN:"$rxfile",creat,append+ovs_waitsctp_eps_hasserver5555||return1++info"verify SCTP association across NAT"+ovs_sbx"test_sctp_nat_connect_v4"ipnetnsexecclient\+timeout3socat-uSTDIN"SCTP4-CONNECT:192.168.0.20:4443"\+</dev/null||return1++info"verify SCTP DATA chunk crosses NAT"+ovs_sbx"test_sctp_nat_connect_v4"ipnetnsexecclient\+timeout3socat-uSTDIN"SCTP4-CONNECT:192.168.0.20:4443"\+<<<"$payload"||return1+ovs_waitgrep-q"$payload""$rxfile"\+||{info"server did not receive SCTP DATA payload"+return1;}++info"done..."+return0+}+# nat_related_v4 test# - client->server ip packets go via SNAT# - client solicits ICMP destination unreachable packet from server
This series extends the Open vSwitch SCTP selftests: patch 1 adds the
IPv6 flow-key test (mirroring the merged IPv4 test), and patch 2 adds an
SCTP NAT test exercising port translation across conntrack.
v3 -> v4:
- test_sctp_connect_v6 now also asserts that installing a keyless
ipv6(proto=132) flow is refused with EINVAL, pinning the reject side
of the match_validate() sctp() key requirement (pointed out in
review: only the accept side was exercised, so a regression dropping
the requirement would have passed unnoticed). The refusal is checked
to be errno 22 specifically, not a flow-string parse error.
- Wrap a few over-80-column lines in the NAT test; checkpatch strict
is now clean for the series.
- Rebased onto 548b86839f7f.
Signed-off-by: Minxi Hou <redacted>
Minxi Hou (2):
selftests/net/openvswitch: add SCTP flow key test over IPv6
selftests/net/openvswitch: add SCTP NAT test with port translation
.../testing/selftests/net/openvswitch/config | 1 +
.../selftests/net/openvswitch/openvswitch.sh | 201 ++++++++++++++++++
2 files changed, 202 insertions(+)
Beside the Aaron's comment on v3, please, avoid re-naming patches and
the series while sending new versions. This confuses both the automation
and humans trying to follow the history of the change.
Also, keep the full changelog from previous versions, and include links
to lore for the previous versions.
BTW, it seems like due to a volume of patches and the speed you're
re-sending them over and over, email providers started to reject your
submissions again due to spam detection. For example, while I'm on
CC list for this patch set, it didn't reach my inbox so far. So, once
again, please, try not to have too many patches on the list at the same
time and try to avoid re-spinning them too often.
Best regards, Ilya Maximets.
Thank you for your contribution! Sashiko AI review found 1 potential
issue(s) to consider:
Critical: 0 · High: 0 · Medium: 0 · Low: 1
- [Low] test_sctp_connect_v6() gates itself on `[ -e /proc/sys/net/ipv6
]` (line 717) and on socat's `WITH_SCTP` define (line 716) only, and…
--
selftests/net/openvswitch: add SCTP flow key test over IPv6
Adds test_sctp_connect_v6 to the openvswitch selftest, mirroring the
existing IPv4 SCTP test: icmpv6() flows forward neighbour discovery,
ipv6(proto=132),sctp() flows gate an SCTP association over IPv6, a keyless
ipv6(proto=132) install is expected to be refused with EINVAL, and a payload
is pushed across the datapath.
@@ -700,6 +701,113 @@ test_sctp_connect_v4() { return 0 }+# sctp_connect_v6 test+# - sctp(dst=4443) matches client-to-server INIT+# - sctp(src=4443) matches server-to-client INIT-ACK+# - icmpv6 NS/NA flows forward neighbour discovery+# - remove flows and verify connection fails, reinstall and recover+test_sctp_connect_v6() {+ local t="test_sctp_connect_v6"+ local v6="eth_type(0x86dd),ipv6(proto=132)"+ local payload="SCTP6_DATA_OK"+ local rxfile="${ovs_base}/${t}/sctp-rx.txt"++ modprobe -q sctp 2>/dev/null || return "$ksft_skip"+ socat -V 2>&1 | grep -q "define WITH_SCTP" || return "$ksft_skip"+ [ -e /proc/sys/net/ipv6 ] || return "$ksft_skip"++ sbx_add "$t" || return $?+ ovs_add_dp "$t" sctp6 || return 1++ info "create namespaces"+ for ns in client server; do+ ovs_add_netns_and_veths "$t" "sctp6" "$ns" \+ "${ns:0:1}0" "${ns:0:1}1" || return 1+ done++ ip netns exec client ip addr add fd00::1/64 dev c1 nodad+ ip netns exec client ip link set c1 up+ ip netns exec server ip addr add fd00::2/64 dev s1 nodad+ ip netns exec server ip link set s1 up
[Severity: Low]
Two questions about the skip/setup handling in test_sctp_connect_v6().
First, the capability probe only greps socat -V for "define WITH_SCTP", while
this test uses the SCTP6-LISTEN and SCTP6-CONNECT addresses, which also need
socat's IPv6 support (WITH_IP6). On a socat built with SCTP but without IPv6,
the probe passes, the listener never comes up, and:
ovs_wait sctp_eps_has server 4443 || return 1
times out and reports FAIL for the SCTP flow-key path rather than skipping for
a missing userspace prerequisite. Would it make sense to also grep for the
IPv6 define here?
Second, the four addressing commands above discard their exit status. The rest
of the test does not depend on addressing until the association attempt, so if
"ip addr add fd00::1/64 ... nodad" fails (for example an iproute2 that rejects
nodad, or IPv6 disabled per-device in the fresh netns via
net.core.devconf_inherit_init_net), the first visible error is:
ovs_sbx "$t" ip netns exec client \
timeout 3 socat -u STDIN "SCTP6-CONNECT:[fd00::2]:4443" </dev/null \
|| return 1
which attributes a setup failure to the SCTP flow-key path. The file-level
"trap ovs_exit_sig EXIT TERM INT ERR" does not catch these, since set -e/set -E
are not in use and run_test() runs the body in a subshell via
"eval test_${tname}". Should these four commands carry "|| return 1" so the
failure is reported where it happens? The pre-existing test_icmpv6() and
test_sctp_connect_v4() use the same unchecked pattern, so this is the current
convention in the file rather than something new here.
Thank you for your contribution! Sashiko AI review found 1 potential
issue(s) to consider:
Critical: 0 · High: 0 · Medium: 1 · Low: 0
- [Medium] tools/testing/selftests/net/openvswitch/config gains
CONFIG_NF_CT_PROTO_SCTP=y for the new test_sctp_nat_connect_v4 test…
--
selftests/net/openvswitch: add SCTP NAT test with port translation
Adds test_sctp_nat_connect_v4, which runs an SCTP association through a
ct(commit,nat(dst=ip:port)) DNAT and matches the translated tuple after
recirculation, plus CONFIG_NF_CT_PROTO_SCTP=y in the config fragment.
[Severity: Medium]
Should this fragment also request CONFIG_NF_NAT=m (and
CONFIG_NETFILTER_ADVANCED=y, which the changelog says the SCTP conntrack
symbol needs)?
The new test's probe uses a nat() action, and net/openvswitch/Kconfig only
has:
select NF_NAT_OVS if NF_NAT
so it never turns NF_NAT on itself. In net/netfilter/Kconfig, NF_NAT is:
config NF_NAT
tristate "Network Address Translation support"
depends on NF_CONNTRACK
default m if NETFILTER_ADVANCED=n
With NETFILTER_ADVANCED=y and no iptables/nftables NAT option selected,
NF_NAT ends up n, and in net/openvswitch/conntrack.c the nat attribute is
compiled out:
#if IS_ENABLED(CONFIG_NF_NAT)
case OVS_CT_ATTR_NAT: {
+ # Check if the ct and nat actions can be configured.+ ovs_add_flow "test_sctp_nat_connect_v4" sctpnat4 \+ 'in_port(1),eth(),eth_type(0x0800),ipv4()' \+ 'ct(commit,nat(dst=172.31.110.20:5555)),recirc(0x1)' \+ &> /dev/null+ if [ $? == 1 ]; then+ info "no support for ct/nat actions - skipping"+ ovs_exit_sig+ return $ksft_skip+ fi
[Severity: Medium]
Following on from the config question above: when NF_NAT is n, does this
probe in test_sctp_nat_connect_v4() always fail so the test permanently
returns $ksft_skip?
That would leave the SCTP arm of ovs_nat_update_key() with no coverage,
which is the path the changelog says is currently unexercised, and the loss
would be silent. The existing test_nat_connect_v4() fails loudly instead:
ovs_sbx "test_nat_connect_v4" ip netns exec client ping -c 1 -W 1 \
172.31.110.20 || return 1
Would adding CONFIG_NF_NAT to the fragment keep this test running on the
reference config?
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260909122521.2687193-1-houminxi%40gmail.com