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
29NUM=3
30IFACES=""
31DRV_MODE="xdpgeneric xdpdrv xdpegress"
32PASS=0
33FAIL=0
34LOG_DIR=$(mktemp -d)
35declare -a NS
36NS[0]="ns0-$(mktemp -u XXXXXX)"
37NS[1]="ns1-$(mktemp -u XXXXXX)"
38NS[2]="ns2-$(mktemp -u XXXXXX)"
39NS[3]="ns3-$(mktemp -u XXXXXX)"
40
41test_pass()
42{
43	echo "Pass: $@"
44	PASS=$((PASS + 1))
45}
46
47test_fail()
48{
49	echo "fail: $@"
50	FAIL=$((FAIL + 1))
51}
52
53clean_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.
61check_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
76setup_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
104do_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
123do_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
174do_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
202check_env
203
204trap clean_up EXIT
205
206for mode in ${DRV_MODE}; do
207	setup_ns $mode
208	do_tests $mode
209	clean_up
210done
211rm -rf ${LOG_DIR}
212
213echo "Summary: PASS $PASS, FAIL $FAIL"
214[ $FAIL -eq 0 ] && exit 0 || exit 1
215