selftests: netfilter: query conntrack state to check for port clash resolution

Jakub reported this self test flaking occasionally (fails, but passes on
re-run) on debug kernels.

This is because the test checks for elapsed time to determine if both
connections were established in parallel.

Rework this to no longer depend on timing.
Use busywait helper to check that both sockets have moved to established
state and then query the conntrack engine for the two entries.

Reported-by: Jakub Kicinski <kuba@kernel.org>
Closes: https://lore.kernel.org/netfilter-devel/20250926163318.40d1a502@kernel.org/
Fixes: 117e149e26 ("selftests: netfilter: test nat source port clash resolution interaction with tcp early demux")
Signed-off-by: Florian Westphal <fw@strlen.de>
This commit is contained in:
Florian Westphal 2025-10-02 15:05:41 +02:00
parent a126ab6b26
commit e84945bdc6

View File

@ -17,9 +17,31 @@ cleanup()
checktool "socat -h" "run test without socat"
checktool "iptables --version" "run test without iptables"
checktool "conntrack --version" "run test without conntrack"
trap cleanup EXIT
connect_done()
{
local ns="$1"
local port="$2"
ip netns exec "$ns" ss -nt -o state established "dport = :$port" | grep -q "$port"
}
check_ctstate()
{
local ns="$1"
local dp="$2"
if ! ip netns exec "$ns" conntrack --get -s 192.168.1.2 -d 192.168.1.1 -p tcp \
--sport 10000 --dport "$dp" --state ESTABLISHED > /dev/null 2>&1;then
echo "FAIL: Did not find expected state for dport $2"
ip netns exec "$ns" bash -c 'conntrack -L; conntrack -S; ss -nt'
ret=1
fi
}
setup_ns ns1 ns2
# Connect the namespaces using a veth pair
@ -44,15 +66,18 @@ socatpid=$!
ip netns exec "$ns2" sysctl -q net.ipv4.ip_local_port_range="10000 10000"
# add a virtual IP using DNAT
ip netns exec "$ns2" iptables -t nat -A OUTPUT -d 10.96.0.1/32 -p tcp --dport 443 -j DNAT --to-destination 192.168.1.1:5201
ip netns exec "$ns2" iptables -t nat -A OUTPUT -d 10.96.0.1/32 -p tcp --dport 443 -j DNAT --to-destination 192.168.1.1:5201 || exit 1
# ... and route it to the other namespace
ip netns exec "$ns2" ip route add 10.96.0.1 via 192.168.1.1
# add a persistent connection from the other namespace
ip netns exec "$ns2" socat -t 10 - TCP:192.168.1.1:5201 > /dev/null &
# listener should be up by now, wait if it isn't yet.
wait_local_port_listen "$ns1" 5201 tcp
sleep 1
# add a persistent connection from the other namespace
sleep 10 | ip netns exec "$ns2" socat -t 10 - TCP:192.168.1.1:5201 > /dev/null &
cpid0=$!
busywait "$BUSYWAIT_TIMEOUT" connect_done "$ns2" "5201"
# ip daddr:dport will be rewritten to 192.168.1.1 5201
# NAT must reallocate source port 10000 because
@ -71,26 +96,25 @@ fi
ip netns exec "$ns1" iptables -t nat -A PREROUTING -p tcp --dport 5202 -j REDIRECT --to-ports 5201
ip netns exec "$ns1" iptables -t nat -A PREROUTING -p tcp --dport 5203 -j REDIRECT --to-ports 5201
sleep 5 | ip netns exec "$ns2" socat -t 5 -u STDIN TCP:192.168.1.1:5202,connect-timeout=5 >/dev/null &
sleep 5 | ip netns exec "$ns2" socat -T 5 -u STDIN TCP:192.168.1.1:5202,connect-timeout=5 >/dev/null &
cpid1=$!
# if connect succeeds, client closes instantly due to EOF on stdin.
# if connect hangs, it will time out after 5s.
echo | ip netns exec "$ns2" socat -t 3 -u STDIN TCP:192.168.1.1:5203,connect-timeout=5 >/dev/null &
sleep 5 | ip netns exec "$ns2" socat -T 5 -u STDIN TCP:192.168.1.1:5203,connect-timeout=5 >/dev/null &
cpid2=$!
time_then=$(date +%s)
wait $cpid2
rv=$?
time_now=$(date +%s)
busywait "$BUSYWAIT_TIMEOUT" connect_done "$ns2" 5202
busywait "$BUSYWAIT_TIMEOUT" connect_done "$ns2" 5203
# Check how much time has elapsed, expectation is for
# 'cpid2' to connect and then exit (and no connect delay).
delta=$((time_now - time_then))
check_ctstate "$ns1" 5202
check_ctstate "$ns1" 5203
if [ $delta -lt 2 ] && [ $rv -eq 0 ]; then
kill $socatpid $cpid0 $cpid1 $cpid2
socatpid=0
if [ $ret -eq 0 ]; then
echo "PASS: could connect to service via redirected ports"
else
echo "FAIL: socat cannot connect to service via redirect ($delta seconds elapsed, returned $rv)"
echo "FAIL: socat cannot connect to service via redirect"
ret=1
fi