1 #!/bin/bash
2 # SPDX-License-Identifier: GPL-2.0
3 #
4 # Test topology:
5 #    - - - - - - - - - - - - - - - - - - -
6 #    | veth1         veth2         veth3 |  ns0
7 #     - -| - - - - - - | - - - - - - | - -
8 #    ---------     ---------     ---------
9 #    | veth0 |     | veth0 |     | veth0 |
10 #    ---------     ---------     ---------
11 #       ns1           ns2           ns3
12 #
13 # Test modules:
14 # XDP modes: generic, native, native + egress_prog
15 #
16 # Test cases:
17 #   ARP: Testing BPF_F_BROADCAST, the ingress interface also should receive
18 #   the redirects.
19 #      ns1 -> gw: ns1, ns2, ns3, should receive the arp request
20 #   IPv4: Testing BPF_F_BROADCAST | BPF_F_EXCLUDE_INGRESS, the ingress
21 #   interface should not receive the redirects.
22 #      ns1 -> gw: ns1 should not receive, ns2, ns3 should receive redirects.
23 #   IPv6: Testing none flag, all the pkts should be redirected back
24 #      ping test: ns1 -> ns2 (block), echo requests will be redirect back
25 #   egress_prog:
26 #      all src mac should be egress interface's mac
27 
28 # netns numbers
29 NUM=3
30 IFACES=""
31 DRV_MODE="xdpgeneric xdpdrv xdpegress"
32 PASS=0
33 FAIL=0
34 LOG_DIR=$(mktemp -d)
35 declare -a NS
36 NS[0]="ns0-$(mktemp -u XXXXXX)"
37 NS[1]="ns1-$(mktemp -u XXXXXX)"
38 NS[2]="ns2-$(mktemp -u XXXXXX)"
39 NS[3]="ns3-$(mktemp -u XXXXXX)"
40 
41 test_pass()
42 {
43 	echo "Pass: $@"
44 	PASS=$((PASS + 1))
45 }
46 
47 test_fail()
48 {
49 	echo "fail: $@"
50 	FAIL=$((FAIL + 1))
51 }
52 
53 clean_up()
54 {
55 	for i in $(seq 0 $NUM); do
56 		ip netns del ${NS[$i]} 2> /dev/null
57 	done
58 }
59 
60 # Kselftest framework requirement - SKIP code is 4.
61 check_env()
62 {
63 	ip link set dev lo xdpgeneric off &>/dev/null
64 	if [ $? -ne 0 ];then
65 		echo "selftests: [SKIP] Could not run test without the ip xdpgeneric support"
66 		exit 4
67 	fi
68 
69 	which tcpdump &>/dev/null
70 	if [ $? -ne 0 ];then
71 		echo "selftests: [SKIP] Could not run test without tcpdump"
72 		exit 4
73 	fi
74 }
75 
76 setup_ns()
77 {
78 	local mode=$1
79 	IFACES=""
80 
81 	if [ "$mode" = "xdpegress" ]; then
82 		mode="xdpdrv"
83 	fi
84 
85 	ip netns add ${NS[0]}
86 	for i in $(seq $NUM); do
87 	        ip netns add ${NS[$i]}
88 		ip -n ${NS[$i]} link add veth0 type veth peer name veth$i netns ${NS[0]}
89 		ip -n ${NS[$i]} link set veth0 up
90 		ip -n ${NS[0]} link set veth$i up
91 
92 		ip -n ${NS[$i]} addr add 192.0.2.$i/24 dev veth0
93 		ip -n ${NS[$i]} addr add 2001:db8::$i/64 dev veth0
94 		# Add a neigh entry for IPv4 ping test
95 		ip -n ${NS[$i]} neigh add 192.0.2.253 lladdr 00:00:00:00:00:01 dev veth0
96 		ip -n ${NS[$i]} link set veth0 $mode obj \
97 			xdp_dummy.bpf.o sec xdp &> /dev/null || \
98 			{ test_fail "Unable to load dummy xdp" && exit 1; }
99 		IFACES="$IFACES veth$i"
100 		veth_mac[$i]=$(ip -n ${NS[0]} link show veth$i | awk '/link\/ether/ {print $2}')
101 	done
102 }
103 
104 do_egress_tests()
105 {
106 	local mode=$1
107 
108 	# mac test
109 	ip netns exec ${NS[2]} tcpdump -e -i veth0 -nn -l -e &> ${LOG_DIR}/mac_ns1-2_${mode}.log &
110 	ip netns exec ${NS[3]} tcpdump -e -i veth0 -nn -l -e &> ${LOG_DIR}/mac_ns1-3_${mode}.log &
111 	sleep 0.5
112 	ip netns exec ${NS[1]} ping 192.0.2.254 -i 0.1 -c 4 &> /dev/null
113 	sleep 0.5
114 	pkill tcpdump
115 
116 	# mac check
117 	grep -q "${veth_mac[2]} > ff:ff:ff:ff:ff:ff" ${LOG_DIR}/mac_ns1-2_${mode}.log && \
118 	       test_pass "$mode mac ns1-2" || test_fail "$mode mac ns1-2"
119 	grep -q "${veth_mac[3]} > ff:ff:ff:ff:ff:ff" ${LOG_DIR}/mac_ns1-3_${mode}.log && \
120 		test_pass "$mode mac ns1-3" || test_fail "$mode mac ns1-3"
121 }
122 
123 do_ping_tests()
124 {
125 	local mode=$1
126 
127 	# ping6 test: echo request should be redirect back to itself, not others
128 	ip netns exec ${NS[1]} ip neigh add 2001:db8::2 dev veth0 lladdr 00:00:00:00:00:02
129 
130 	ip netns exec ${NS[1]} tcpdump -i veth0 -nn -l -e &> ${LOG_DIR}/ns1-1_${mode}.log &
131 	ip netns exec ${NS[2]} tcpdump -i veth0 -nn -l -e &> ${LOG_DIR}/ns1-2_${mode}.log &
132 	ip netns exec ${NS[3]} tcpdump -i veth0 -nn -l -e &> ${LOG_DIR}/ns1-3_${mode}.log &
133 	sleep 0.5
134 	# ARP test
135 	ip netns exec ${NS[1]} arping -q -c 2 -I veth0 192.0.2.254
136 	# IPv4 test
137 	ip netns exec ${NS[1]} ping 192.0.2.253 -i 0.1 -c 4 &> /dev/null
138 	# IPv6 test
139 	ip netns exec ${NS[1]} ping6 2001:db8::2 -i 0.1 -c 2 &> /dev/null
140 	sleep 0.5
141 	pkill tcpdump
142 
143 	# All netns should receive the redirect arp requests
144 	[ $(grep -cF "who-has 192.0.2.254" ${LOG_DIR}/ns1-1_${mode}.log) -eq 4 ] && \
145 		test_pass "$mode arp(F_BROADCAST) ns1-1" || \
146 		test_fail "$mode arp(F_BROADCAST) ns1-1"
147 	[ $(grep -cF "who-has 192.0.2.254" ${LOG_DIR}/ns1-2_${mode}.log) -eq 2 ] && \
148 		test_pass "$mode arp(F_BROADCAST) ns1-2" || \
149 		test_fail "$mode arp(F_BROADCAST) ns1-2"
150 	[ $(grep -cF "who-has 192.0.2.254" ${LOG_DIR}/ns1-3_${mode}.log) -eq 2 ] && \
151 		test_pass "$mode arp(F_BROADCAST) ns1-3" || \
152 		test_fail "$mode arp(F_BROADCAST) ns1-3"
153 
154 	# ns1 should not receive the redirect echo request, others should
155 	[ $(grep -c "ICMP echo request" ${LOG_DIR}/ns1-1_${mode}.log) -eq 4 ] && \
156 		test_pass "$mode IPv4 (F_BROADCAST|F_EXCLUDE_INGRESS) ns1-1" || \
157 		test_fail "$mode IPv4 (F_BROADCAST|F_EXCLUDE_INGRESS) ns1-1"
158 	[ $(grep -c "ICMP echo request" ${LOG_DIR}/ns1-2_${mode}.log) -eq 4 ] && \
159 		test_pass "$mode IPv4 (F_BROADCAST|F_EXCLUDE_INGRESS) ns1-2" || \
160 		test_fail "$mode IPv4 (F_BROADCAST|F_EXCLUDE_INGRESS) ns1-2"
161 	[ $(grep -c "ICMP echo request" ${LOG_DIR}/ns1-3_${mode}.log) -eq 4 ] && \
162 		test_pass "$mode IPv4 (F_BROADCAST|F_EXCLUDE_INGRESS) ns1-3" || \
163 		test_fail "$mode IPv4 (F_BROADCAST|F_EXCLUDE_INGRESS) ns1-3"
164 
165 	# ns1 should receive the echo request, ns2 should not
166 	[ $(grep -c "ICMP6, echo request" ${LOG_DIR}/ns1-1_${mode}.log) -eq 4 ] && \
167 		test_pass "$mode IPv6 (no flags) ns1-1" || \
168 		test_fail "$mode IPv6 (no flags) ns1-1"
169 	[ $(grep -c "ICMP6, echo request" ${LOG_DIR}/ns1-2_${mode}.log) -eq 0 ] && \
170 		test_pass "$mode IPv6 (no flags) ns1-2" || \
171 		test_fail "$mode IPv6 (no flags) ns1-2"
172 }
173 
174 do_tests()
175 {
176 	local mode=$1
177 	local drv_p
178 
179 	case ${mode} in
180 		xdpdrv)  drv_p="-N";;
181 		xdpegress) drv_p="-X";;
182 		xdpgeneric) drv_p="-S";;
183 	esac
184 
185 	ip netns exec ${NS[0]} ./xdp_redirect_multi $drv_p $IFACES &> ${LOG_DIR}/xdp_redirect_${mode}.log &
186 	xdp_pid=$!
187 	sleep 1
188 	if ! ps -p $xdp_pid > /dev/null; then
189 		test_fail "$mode xdp_redirect_multi start failed"
190 		return 1
191 	fi
192 
193 	if [ "$mode" = "xdpegress" ]; then
194 		do_egress_tests $mode
195 	else
196 		do_ping_tests $mode
197 	fi
198 
199 	kill $xdp_pid
200 }
201 
202 check_env
203 
204 trap clean_up EXIT
205 
206 for mode in ${DRV_MODE}; do
207 	setup_ns $mode
208 	do_tests $mode
209 	clean_up
210 done
211 rm -rf ${LOG_DIR}
212 
213 echo "Summary: PASS $PASS, FAIL $FAIL"
214 [ $FAIL -eq 0 ] && exit 0 || exit 1
215