1#!/bin/bash
2# SPDX-License-Identifier: GPL-2.0
3
4# Uncomment to see generated bytecode
5#VERBOSE=verbose
6
7NS1=lwt_ns1
8NS2=lwt_ns2
9VETH0=tst_lwt1a
10VETH1=tst_lwt1b
11VETH2=tst_lwt2a
12VETH3=tst_lwt2b
13IPVETH0="192.168.254.1"
14IPVETH1="192.168.254.2"
15IPVETH1b="192.168.254.3"
16
17IPVETH2="192.168.111.1"
18IPVETH3="192.168.111.2"
19
20IP_LOCAL="192.168.99.1"
21
22TRACE_ROOT=/sys/kernel/debug/tracing
23
24function lookup_mac()
25{
26	set +x
27	if [ ! -z "$2" ]; then
28		MAC=$(ip netns exec $2 ip link show $1 | grep ether | awk '{print $2}')
29	else
30		MAC=$(ip link show $1 | grep ether | awk '{print $2}')
31	fi
32	MAC="${MAC//:/}"
33	echo "0x${MAC:10:2}${MAC:8:2}${MAC:6:2}${MAC:4:2}${MAC:2:2}${MAC:0:2}"
34	set -x
35}
36
37function cleanup {
38	set +ex
39	rm test_lwt_bpf.o 2> /dev/null
40	ip link del $VETH0 2> /dev/null
41	ip link del $VETH1 2> /dev/null
42	ip link del $VETH2 2> /dev/null
43	ip link del $VETH3 2> /dev/null
44	ip netns exec $NS1 killall netserver
45	ip netns delete $NS1 2> /dev/null
46	ip netns delete $NS2 2> /dev/null
47	set -ex
48}
49
50function setup_one_veth {
51	ip netns add $1
52	ip link add $2 type veth peer name $3
53	ip link set dev $2 up
54	ip addr add $4/24 dev $2
55	ip link set $3 netns $1
56	ip netns exec $1 ip link set dev $3 up
57	ip netns exec $1 ip addr add $5/24 dev $3
58
59	if [ "$6" ]; then
60		ip netns exec $1 ip addr add $6/32 dev $3
61	fi
62}
63
64function get_trace {
65	set +x
66	cat ${TRACE_ROOT}/trace | grep -v '^#'
67	set -x
68}
69
70function cleanup_routes {
71	ip route del ${IPVETH1}/32 dev $VETH0 2> /dev/null || true
72	ip route del table local local ${IP_LOCAL}/32 dev lo 2> /dev/null || true
73}
74
75function install_test {
76	cleanup_routes
77	cp /dev/null ${TRACE_ROOT}/trace
78
79	OPTS="encap bpf headroom 14 $1 obj test_lwt_bpf.o section $2 $VERBOSE"
80
81	if [ "$1" == "in" ];  then
82		ip route add table local local ${IP_LOCAL}/32 $OPTS dev lo
83	else
84		ip route add ${IPVETH1}/32 $OPTS dev $VETH0
85	fi
86}
87
88function remove_prog {
89	if [ "$1" == "in" ];  then
90		ip route del table local local ${IP_LOCAL}/32 dev lo
91	else
92		ip route del ${IPVETH1}/32 dev $VETH0
93	fi
94}
95
96function filter_trace {
97	# Add newline to allow starting EXPECT= variables on newline
98	NL=$'\n'
99	echo "${NL}$*" | sed -e 's/^.*: : //g'
100}
101
102function expect_fail {
103	set +x
104	echo "FAIL:"
105	echo "Expected: $1"
106	echo "Got: $2"
107	set -x
108	exit 1
109}
110
111function match_trace {
112	set +x
113	RET=0
114	TRACE=$1
115	EXPECT=$2
116	GOT="$(filter_trace "$TRACE")"
117
118	[ "$GOT" != "$EXPECT" ] && {
119		expect_fail "$EXPECT" "$GOT"
120		RET=1
121	}
122	set -x
123	return $RET
124}
125
126function test_start {
127	set +x
128	echo "----------------------------------------------------------------"
129	echo "Starting test: $*"
130	echo "----------------------------------------------------------------"
131	set -x
132}
133
134function failure {
135	get_trace
136	echo "FAIL: $*"
137	exit 1
138}
139
140function test_ctx_xmit {
141	test_start "test_ctx on lwt xmit"
142	install_test xmit test_ctx
143	ping -c 3 $IPVETH1 || {
144		failure "test_ctx xmit: packets are dropped"
145	}
146	match_trace "$(get_trace)" "
147len 84 hash 0 protocol 8
148cb 1234 ingress_ifindex 0 ifindex $DST_IFINDEX
149len 84 hash 0 protocol 8
150cb 1234 ingress_ifindex 0 ifindex $DST_IFINDEX
151len 84 hash 0 protocol 8
152cb 1234 ingress_ifindex 0 ifindex $DST_IFINDEX" || exit 1
153	remove_prog xmit
154}
155
156function test_ctx_out {
157	test_start "test_ctx on lwt out"
158	install_test out test_ctx
159	ping -c 3 $IPVETH1 || {
160		failure "test_ctx out: packets are dropped"
161	}
162	match_trace "$(get_trace)" "
163len 84 hash 0 protocol 0
164cb 1234 ingress_ifindex 0 ifindex 0
165len 84 hash 0 protocol 0
166cb 1234 ingress_ifindex 0 ifindex 0
167len 84 hash 0 protocol 0
168cb 1234 ingress_ifindex 0 ifindex 0" || exit 1
169	remove_prog out
170}
171
172function test_ctx_in {
173	test_start "test_ctx on lwt in"
174	install_test in test_ctx
175	ping -c 3 $IP_LOCAL || {
176		failure "test_ctx out: packets are dropped"
177	}
178	# We will both request & reply packets as the packets will
179	# be from $IP_LOCAL => $IP_LOCAL
180	match_trace "$(get_trace)" "
181len 84 hash 0 protocol 8
182cb 1234 ingress_ifindex 1 ifindex 1
183len 84 hash 0 protocol 8
184cb 1234 ingress_ifindex 1 ifindex 1
185len 84 hash 0 protocol 8
186cb 1234 ingress_ifindex 1 ifindex 1
187len 84 hash 0 protocol 8
188cb 1234 ingress_ifindex 1 ifindex 1
189len 84 hash 0 protocol 8
190cb 1234 ingress_ifindex 1 ifindex 1
191len 84 hash 0 protocol 8
192cb 1234 ingress_ifindex 1 ifindex 1" || exit 1
193	remove_prog in
194}
195
196function test_data {
197	test_start "test_data on lwt $1"
198	install_test $1 test_data
199	ping -c 3 $IPVETH1 || {
200		failure "test_data ${1}: packets are dropped"
201	}
202	match_trace "$(get_trace)" "
203src: 1fea8c0 dst: 2fea8c0
204src: 1fea8c0 dst: 2fea8c0
205src: 1fea8c0 dst: 2fea8c0" || exit 1
206	remove_prog $1
207}
208
209function test_data_in {
210	test_start "test_data on lwt in"
211	install_test in test_data
212	ping -c 3 $IP_LOCAL || {
213		failure "test_data in: packets are dropped"
214	}
215	# We will both request & reply packets as the packets will
216	# be from $IP_LOCAL => $IP_LOCAL
217	match_trace "$(get_trace)" "
218src: 163a8c0 dst: 163a8c0
219src: 163a8c0 dst: 163a8c0
220src: 163a8c0 dst: 163a8c0
221src: 163a8c0 dst: 163a8c0
222src: 163a8c0 dst: 163a8c0
223src: 163a8c0 dst: 163a8c0" || exit 1
224	remove_prog in
225}
226
227function test_cb {
228	test_start "test_cb on lwt $1"
229	install_test $1 test_cb
230	ping -c 3 $IPVETH1 || {
231		failure "test_cb ${1}: packets are dropped"
232	}
233	match_trace "$(get_trace)" "
234cb0: 0 cb1: 0 cb2: 0
235cb3: 0 cb4: 0
236cb0: 0 cb1: 0 cb2: 0
237cb3: 0 cb4: 0
238cb0: 0 cb1: 0 cb2: 0
239cb3: 0 cb4: 0" || exit 1
240	remove_prog $1
241}
242
243function test_cb_in {
244	test_start "test_cb on lwt in"
245	install_test in test_cb
246	ping -c 3 $IP_LOCAL || {
247		failure "test_cb in: packets are dropped"
248	}
249	# We will both request & reply packets as the packets will
250	# be from $IP_LOCAL => $IP_LOCAL
251	match_trace "$(get_trace)" "
252cb0: 0 cb1: 0 cb2: 0
253cb3: 0 cb4: 0
254cb0: 0 cb1: 0 cb2: 0
255cb3: 0 cb4: 0
256cb0: 0 cb1: 0 cb2: 0
257cb3: 0 cb4: 0
258cb0: 0 cb1: 0 cb2: 0
259cb3: 0 cb4: 0
260cb0: 0 cb1: 0 cb2: 0
261cb3: 0 cb4: 0
262cb0: 0 cb1: 0 cb2: 0
263cb3: 0 cb4: 0" || exit 1
264	remove_prog in
265}
266
267function test_drop_all {
268	test_start "test_drop_all on lwt $1"
269	install_test $1 drop_all
270	ping -c 3 $IPVETH1 && {
271		failure "test_drop_all ${1}: Unexpected success of ping"
272	}
273	match_trace "$(get_trace)" "
274dropping with: 2
275dropping with: 2
276dropping with: 2" || exit 1
277	remove_prog $1
278}
279
280function test_drop_all_in {
281	test_start "test_drop_all on lwt in"
282	install_test in drop_all
283	ping -c 3 $IP_LOCAL && {
284		failure "test_drop_all in: Unexpected success of ping"
285	}
286	match_trace "$(get_trace)" "
287dropping with: 2
288dropping with: 2
289dropping with: 2" || exit 1
290	remove_prog in
291}
292
293function test_push_ll_and_redirect {
294	test_start "test_push_ll_and_redirect on lwt xmit"
295	install_test xmit push_ll_and_redirect
296	ping -c 3 $IPVETH1 || {
297		failure "Redirected packets appear to be dropped"
298	}
299	match_trace "$(get_trace)" "
300redirected to $DST_IFINDEX
301redirected to $DST_IFINDEX
302redirected to $DST_IFINDEX" || exit 1
303	remove_prog xmit
304}
305
306function test_no_l2_and_redirect {
307	test_start "test_no_l2_and_redirect on lwt xmit"
308	install_test xmit fill_garbage_and_redirect
309	ping -c 3 $IPVETH1 && {
310		failure "Unexpected success despite lack of L2 header"
311	}
312	match_trace "$(get_trace)" "
313redirected to $DST_IFINDEX
314redirected to $DST_IFINDEX
315redirected to $DST_IFINDEX" || exit 1
316	remove_prog xmit
317}
318
319function test_rewrite {
320	test_start "test_rewrite on lwt xmit"
321	install_test xmit test_rewrite
322	ping -c 3 $IPVETH1 || {
323		failure "Rewritten packets appear to be dropped"
324	}
325	match_trace "$(get_trace)" "
326out: rewriting from 2fea8c0 to 3fea8c0
327out: rewriting from 2fea8c0 to 3fea8c0
328out: rewriting from 2fea8c0 to 3fea8c0" || exit 1
329	remove_prog out
330}
331
332function test_fill_garbage {
333	test_start "test_fill_garbage on lwt xmit"
334	install_test xmit fill_garbage
335	ping -c 3 $IPVETH1 && {
336		failure "test_drop_all ${1}: Unexpected success of ping"
337	}
338	match_trace "$(get_trace)" "
339Set initial 96 bytes of header to FF
340Set initial 96 bytes of header to FF
341Set initial 96 bytes of header to FF" || exit 1
342	remove_prog xmit
343}
344
345function test_netperf_nop {
346	test_start "test_netperf_nop on lwt xmit"
347	install_test xmit nop
348	netperf -H $IPVETH1 -t TCP_STREAM || {
349		failure "packets appear to be dropped"
350	}
351	match_trace "$(get_trace)" ""|| exit 1
352	remove_prog xmit
353}
354
355function test_netperf_redirect {
356	test_start "test_netperf_redirect on lwt xmit"
357	install_test xmit push_ll_and_redirect_silent
358	netperf -H $IPVETH1 -t TCP_STREAM || {
359		failure "Rewritten packets appear to be dropped"
360	}
361	match_trace "$(get_trace)" ""|| exit 1
362	remove_prog xmit
363}
364
365cleanup
366setup_one_veth $NS1 $VETH0 $VETH1 $IPVETH0 $IPVETH1 $IPVETH1b
367setup_one_veth $NS2 $VETH2 $VETH3 $IPVETH2 $IPVETH3
368ip netns exec $NS1 netserver
369echo 1 > ${TRACE_ROOT}/tracing_on
370
371DST_MAC=$(lookup_mac $VETH1 $NS1)
372SRC_MAC=$(lookup_mac $VETH0)
373DST_IFINDEX=$(cat /sys/class/net/$VETH0/ifindex)
374
375CLANG_OPTS="-O2 -target bpf -I ../include/"
376CLANG_OPTS+=" -DSRC_MAC=$SRC_MAC -DDST_MAC=$DST_MAC -DDST_IFINDEX=$DST_IFINDEX"
377clang $CLANG_OPTS -c test_lwt_bpf.c -o test_lwt_bpf.o
378
379test_ctx_xmit
380test_ctx_out
381test_ctx_in
382test_data "xmit"
383test_data "out"
384test_data_in
385test_cb "xmit"
386test_cb "out"
387test_cb_in
388test_drop_all "xmit"
389test_drop_all "out"
390test_drop_all_in
391test_rewrite
392test_push_ll_and_redirect
393test_no_l2_and_redirect
394test_fill_garbage
395test_netperf_nop
396test_netperf_redirect
397
398cleanup
399echo 0 > ${TRACE_ROOT}/tracing_on
400exit 0
401