1#!/bin/bash
2# SPDX-License-Identifier: GPL-2.0
3#
4# Run a series of udpgro functional tests.
5
6readonly PEER_NS="ns-peer-$(mktemp -u XXXXXX)"
7
8# set global exit status, but never reset nonzero one.
9check_err()
10{
11	if [ $ret -eq 0 ]; then
12		ret=$1
13	fi
14}
15
16cleanup() {
17	local -r jobs="$(jobs -p)"
18	local -r ns="$(ip netns list|grep $PEER_NS)"
19
20	[ -n "${jobs}" ] && kill -1 ${jobs} 2>/dev/null
21	[ -n "$ns" ] && ip netns del $ns 2>/dev/null
22}
23trap cleanup EXIT
24
25cfg_veth() {
26	ip netns add "${PEER_NS}"
27	ip -netns "${PEER_NS}" link set lo up
28	ip link add type veth
29	ip link set dev veth0 up
30	ip addr add dev veth0 192.168.1.2/24
31	ip addr add dev veth0 2001:db8::2/64 nodad
32
33	ip link set dev veth1 netns "${PEER_NS}"
34	ip -netns "${PEER_NS}" addr add dev veth1 192.168.1.1/24
35	ip -netns "${PEER_NS}" addr add dev veth1 2001:db8::1/64 nodad
36	ip -netns "${PEER_NS}" link set dev veth1 up
37	ip -n "${PEER_NS}" link set veth1 xdp object ../bpf/xdp_dummy.o section xdp
38}
39
40run_one() {
41	# use 'rx' as separator between sender args and receiver args
42	local -r all="$@"
43	local -r tx_args=${all%rx*}
44	local -r rx_args=${all#*rx}
45
46	cfg_veth
47
48	ip netns exec "${PEER_NS}" ./udpgso_bench_rx -C 1000 -R 10 ${rx_args} && \
49		echo "ok" || \
50		echo "failed" &
51
52	# Hack: let bg programs complete the startup
53	sleep 0.2
54	./udpgso_bench_tx ${tx_args}
55	ret=$?
56	wait $(jobs -p)
57	return $ret
58}
59
60run_test() {
61	local -r args=$@
62
63	printf " %-40s" "$1"
64	./in_netns.sh $0 __subprocess $2 rx -G -r $3
65}
66
67run_one_nat() {
68	# use 'rx' as separator between sender args and receiver args
69	local addr1 addr2 pid family="" ipt_cmd=ip6tables
70	local -r all="$@"
71	local -r tx_args=${all%rx*}
72	local -r rx_args=${all#*rx}
73
74	if [[ ${tx_args} = *-4* ]]; then
75		ipt_cmd=iptables
76		family=-4
77		addr1=192.168.1.1
78		addr2=192.168.1.3/24
79	else
80		addr1=2001:db8::1
81		addr2="2001:db8::3/64 nodad"
82	fi
83
84	cfg_veth
85	ip -netns "${PEER_NS}" addr add dev veth1 ${addr2}
86
87	# fool the GRO engine changing the destination address ...
88	ip netns exec "${PEER_NS}" $ipt_cmd -t nat -I PREROUTING -d ${addr1} -j DNAT --to-destination ${addr2%/*}
89
90	# ... so that GRO will match the UDP_GRO enabled socket, but packets
91	# will land on the 'plain' one
92	ip netns exec "${PEER_NS}" ./udpgso_bench_rx -G ${family} -b ${addr1} -n 0 &
93	pid=$!
94	ip netns exec "${PEER_NS}" ./udpgso_bench_rx -C 1000 -R 10 ${family} -b ${addr2%/*} ${rx_args} && \
95		echo "ok" || \
96		echo "failed"&
97
98	sleep 0.1
99	./udpgso_bench_tx ${tx_args}
100	ret=$?
101	kill -INT $pid
102	wait $(jobs -p)
103	return $ret
104}
105
106run_one_2sock() {
107	# use 'rx' as separator between sender args and receiver args
108	local -r all="$@"
109	local -r tx_args=${all%rx*}
110	local -r rx_args=${all#*rx}
111
112	cfg_veth
113
114	ip netns exec "${PEER_NS}" ./udpgso_bench_rx -C 1000 -R 10 ${rx_args} -p 12345 &
115	ip netns exec "${PEER_NS}" ./udpgso_bench_rx -C 2000 -R 10 ${rx_args} && \
116		echo "ok" || \
117		echo "failed" &
118
119	# Hack: let bg programs complete the startup
120	sleep 0.2
121	./udpgso_bench_tx ${tx_args} -p 12345
122	sleep 0.1
123	# first UDP GSO socket should be closed at this point
124	./udpgso_bench_tx ${tx_args}
125	ret=$?
126	wait $(jobs -p)
127	return $ret
128}
129
130run_nat_test() {
131	local -r args=$@
132
133	printf " %-40s" "$1"
134	./in_netns.sh $0 __subprocess_nat $2 rx -r $3
135}
136
137run_2sock_test() {
138	local -r args=$@
139
140	printf " %-40s" "$1"
141	./in_netns.sh $0 __subprocess_2sock $2 rx -G -r $3
142}
143
144run_all() {
145	local -r core_args="-l 4"
146	local -r ipv4_args="${core_args} -4 -D 192.168.1.1"
147	local -r ipv6_args="${core_args} -6 -D 2001:db8::1"
148	ret=0
149
150	echo "ipv4"
151	run_test "no GRO" "${ipv4_args} -M 10 -s 1400" "-4 -n 10 -l 1400"
152	check_err $?
153
154	# explicitly check we are not receiving UDP_SEGMENT cmsg (-S -1)
155	# when GRO does not take place
156	run_test "no GRO chk cmsg" "${ipv4_args} -M 10 -s 1400" "-4 -n 10 -l 1400 -S -1"
157	check_err $?
158
159	# the GSO packets are aggregated because:
160	# * veth schedule napi after each xmit
161	# * segmentation happens in BH context, veth napi poll is delayed after
162	#   the transmission of the last segment
163	run_test "GRO" "${ipv4_args} -M 1 -s 14720 -S 0 " "-4 -n 1 -l 14720"
164	check_err $?
165	run_test "GRO chk cmsg" "${ipv4_args} -M 1 -s 14720 -S 0 " "-4 -n 1 -l 14720 -S 1472"
166	check_err $?
167	run_test "GRO with custom segment size" "${ipv4_args} -M 1 -s 14720 -S 500 " "-4 -n 1 -l 14720"
168	check_err $?
169	run_test "GRO with custom segment size cmsg" "${ipv4_args} -M 1 -s 14720 -S 500 " "-4 -n 1 -l 14720 -S 500"
170	check_err $?
171
172	run_nat_test "bad GRO lookup" "${ipv4_args} -M 1 -s 14720 -S 0" "-n 10 -l 1472"
173	check_err $?
174	run_2sock_test "multiple GRO socks" "${ipv4_args} -M 1 -s 14720 -S 0 " "-4 -n 1 -l 14720 -S 1472"
175	check_err $?
176
177	echo "ipv6"
178	run_test "no GRO" "${ipv6_args} -M 10 -s 1400" "-n 10 -l 1400"
179	check_err $?
180	run_test "no GRO chk cmsg" "${ipv6_args} -M 10 -s 1400" "-n 10 -l 1400 -S -1"
181	check_err $?
182	run_test "GRO" "${ipv6_args} -M 1 -s 14520 -S 0" "-n 1 -l 14520"
183	check_err $?
184	run_test "GRO chk cmsg" "${ipv6_args} -M 1 -s 14520 -S 0" "-n 1 -l 14520 -S 1452"
185	check_err $?
186	run_test "GRO with custom segment size" "${ipv6_args} -M 1 -s 14520 -S 500" "-n 1 -l 14520"
187	check_err $?
188	run_test "GRO with custom segment size cmsg" "${ipv6_args} -M 1 -s 14520 -S 500" "-n 1 -l 14520 -S 500"
189	check_err $?
190
191	run_nat_test "bad GRO lookup" "${ipv6_args} -M 1 -s 14520 -S 0" "-n 10 -l 1452"
192	check_err $?
193	run_2sock_test "multiple GRO socks" "${ipv6_args} -M 1 -s 14520 -S 0 " "-n 1 -l 14520 -S 1452"
194	check_err $?
195	return $ret
196}
197
198if [ ! -f ../bpf/xdp_dummy.o ]; then
199	echo "Missing xdp_dummy helper. Build bpf selftest first"
200	exit -1
201fi
202
203if [[ $# -eq 0 ]]; then
204	run_all
205elif [[ $1 == "__subprocess" ]]; then
206	shift
207	run_one $@
208elif [[ $1 == "__subprocess_nat" ]]; then
209	shift
210	run_one_nat $@
211elif [[ $1 == "__subprocess_2sock" ]]; then
212	shift
213	run_one_2sock $@
214fi
215
216exit $?
217