xref: /linux/tools/testing/selftests/net/netfilter/conntrack_clash.sh (revision d69eb204c255c35abd9e8cb621484e8074c75eaa)
1#!/bin/bash
2# SPDX-License-Identifier: GPL-2.0
3
4source lib.sh
5
6clash_resolution_active=0
7dport=22111
8ret=0
9
10cleanup()
11{
12	# netns cleanup also zaps any remaining socat echo server.
13	cleanup_all_ns
14}
15
16checktool "nft --version" "run test without nft"
17checktool "conntrack --version" "run test without conntrack"
18checktool "socat -h" "run test without socat"
19
20trap cleanup EXIT
21
22setup_ns nsclient1 nsclient2 nsrouter
23
24ip netns exec "$nsrouter" nft -f -<<EOF
25table ip t {
26	chain lb {
27		meta l4proto udp dnat to numgen random mod 3 map { 0 : 10.0.2.1 . 9000, 1 : 10.0.2.1 . 9001, 2 : 10.0.2.1 . 9002 }
28	}
29
30	chain prerouting {
31		type nat hook prerouting priority dstnat
32
33		udp dport $dport counter jump lb
34	}
35
36	chain output {
37		type nat hook output priority dstnat
38
39		udp dport $dport counter jump lb
40	}
41}
42EOF
43
44load_simple_ruleset()
45{
46ip netns exec "$1" nft -f -<<EOF
47table ip t {
48	chain forward {
49		type filter hook forward priority 0
50
51		ct state new counter
52	}
53}
54EOF
55}
56
57spawn_servers()
58{
59	local ns="$1"
60	local ports="9000 9001 9002"
61
62	for port in $ports; do
63		ip netns exec "$ns" socat UDP-RECVFROM:$port,fork PIPE 2>/dev/null &
64	done
65
66	for port in $ports; do
67		wait_local_port_listen "$ns" $port udp
68	done
69}
70
71add_addr()
72{
73	local ns="$1"
74	local dev="$2"
75	local i="$3"
76	local j="$4"
77
78	ip -net "$ns" link set "$dev" up
79	ip -net "$ns" addr add "10.0.$i.$j/24" dev "$dev"
80}
81
82ping_test()
83{
84	local ns="$1"
85	local daddr="$2"
86
87	if ! ip netns exec "$ns" ping -q -c 1 $daddr > /dev/null;then
88		echo "FAIL: ping from $ns to $daddr"
89		exit 1
90	fi
91}
92
93run_one_clash_test()
94{
95	local ns="$1"
96	local ctns="$2"
97	local daddr="$3"
98	local dport="$4"
99	local entries
100	local cre
101
102	if ! ip netns exec "$ns" timeout 30 ./udpclash $daddr $dport;then
103		echo "INFO: did not receive expected number of replies for $daddr:$dport"
104		ip netns exec "$ctns" conntrack -S
105		# don't fail: check if clash resolution triggered after all.
106	fi
107
108	entries=$(ip netns exec "$ctns" conntrack -S | wc -l)
109	cre=$(ip netns exec "$ctns" conntrack -S | grep "clash_resolve=0" | wc -l)
110
111	if [ "$cre" -ne "$entries" ];then
112		clash_resolution_active=1
113		return 0
114	fi
115
116	# not a failure: clash resolution logic did not trigger.
117	# With right timing, xmit completed sequentially and
118	# no parallel insertion occurs.
119	return $ksft_skip
120}
121
122run_clash_test()
123{
124	local ns="$1"
125	local ctns="$2"
126	local daddr="$3"
127	local dport="$4"
128	local softerr=0
129
130	for i in $(seq 1 10);do
131		run_one_clash_test "$ns" "$ctns" "$daddr" "$dport"
132		local rv=$?
133		if [ $rv -eq 0 ];then
134			echo "PASS: clash resolution test for $daddr:$dport on attempt $i"
135			return 0
136		elif [ $rv -eq $ksft_skip ]; then
137			softerr=1
138		fi
139	done
140
141	[ $softerr -eq 1 ] && echo "SKIP: clash resolution for $daddr:$dport did not trigger"
142}
143
144ip link add veth0 netns "$nsclient1" type veth peer name veth0 netns "$nsrouter"
145ip link add veth0 netns "$nsclient2" type veth peer name veth1 netns "$nsrouter"
146add_addr "$nsclient1" veth0 1 1
147add_addr "$nsclient2" veth0 2 1
148add_addr "$nsrouter" veth0 1 99
149add_addr "$nsrouter" veth1 2 99
150
151ip -net "$nsclient1" route add default via 10.0.1.99
152ip -net "$nsclient2" route add default via 10.0.2.99
153ip netns exec "$nsrouter" sysctl -q net.ipv4.ip_forward=1
154
155ping_test "$nsclient1" 10.0.1.99
156ping_test "$nsclient1" 10.0.2.1
157ping_test "$nsclient2" 10.0.1.1
158
159spawn_servers "$nsclient2"
160
161# exercise clash resolution with nat:
162# nsrouter is supposed to dnat to 10.0.2.1:900{0,1,2,3}.
163run_clash_test "$nsclient1" "$nsrouter" 10.0.1.99 "$dport"
164
165# exercise clash resolution without nat.
166load_simple_ruleset "$nsclient2"
167run_clash_test "$nsclient2" "$nsclient2" 127.0.0.1 9001
168
169if [ $clash_resolution_active -eq 0 ];then
170	[ "$ret" -eq 0 ] && ret=$ksft_skip
171	echo "SKIP: Clash resolution did not trigger"
172fi
173
174exit $ret
175