1#!/bin/bash
2# SPDX-License-Identifier: GPL-2.0
3# Author: Jesper Dangaard Brouer <hawk@kernel.org>
4
5# Kselftest framework requirement - SKIP code is 4.
6readonly KSFT_SKIP=4
7
8# Allow wrapper scripts to name test
9if [ -z "$TESTNAME" ]; then
10    TESTNAME=xdp_vlan
11fi
12
13# Default XDP mode
14XDP_MODE=xdpgeneric
15
16usage() {
17  echo "Testing XDP + TC eBPF VLAN manipulations: $TESTNAME"
18  echo ""
19  echo "Usage: $0 [-vfh]"
20  echo "  -v | --verbose : Verbose"
21  echo "  --flush        : Flush before starting (e.g. after --interactive)"
22  echo "  --interactive  : Keep netns setup running after test-run"
23  echo "  --mode=XXX     : Choose XDP mode (xdp | xdpgeneric | xdpdrv)"
24  echo ""
25}
26
27valid_xdp_mode()
28{
29	local mode=$1
30
31	case "$mode" in
32		xdpgeneric | xdpdrv | xdp)
33			return 0
34			;;
35		*)
36			return 1
37	esac
38}
39
40cleanup()
41{
42	local status=$?
43
44	if [ "$status" = "0" ]; then
45		echo "selftests: $TESTNAME [PASS]";
46	else
47		echo "selftests: $TESTNAME [FAILED]";
48	fi
49
50	if [ -n "$INTERACTIVE" ]; then
51		echo "Namespace setup still active explore with:"
52		echo " ip netns exec ns1 bash"
53		echo " ip netns exec ns2 bash"
54		exit $status
55	fi
56
57	set +e
58	ip link del veth1 2> /dev/null
59	ip netns del ns1 2> /dev/null
60	ip netns del ns2 2> /dev/null
61}
62
63# Using external program "getopt" to get --long-options
64OPTIONS=$(getopt -o hvfi: \
65    --long verbose,flush,help,interactive,debug,mode: -- "$@")
66if (( $? != 0 )); then
67    usage
68    echo "selftests: $TESTNAME [FAILED] Error calling getopt, unknown option?"
69    exit 2
70fi
71eval set -- "$OPTIONS"
72
73##  --- Parse command line arguments / parameters ---
74while true; do
75	case "$1" in
76	    -v | --verbose)
77		export VERBOSE=yes
78		shift
79		;;
80	    -i | --interactive | --debug )
81		INTERACTIVE=yes
82		shift
83		;;
84	    -f | --flush )
85		cleanup
86		shift
87		;;
88	    --mode )
89		shift
90		XDP_MODE=$1
91		shift
92		;;
93	    -- )
94		shift
95		break
96		;;
97	    -h | --help )
98		usage;
99		echo "selftests: $TESTNAME [SKIP] usage help info requested"
100		exit $KSFT_SKIP
101		;;
102	    * )
103		shift
104		break
105		;;
106	esac
107done
108
109if [ "$EUID" -ne 0 ]; then
110	echo "selftests: $TESTNAME [FAILED] need root privileges"
111	exit 1
112fi
113
114valid_xdp_mode $XDP_MODE
115if [ $? -ne 0 ]; then
116	echo "selftests: $TESTNAME [FAILED] unknown XDP mode ($XDP_MODE)"
117	exit 1
118fi
119
120ip link set dev lo xdpgeneric off 2>/dev/null > /dev/null
121if [ $? -ne 0 ]; then
122	echo "selftests: $TESTNAME [SKIP] need ip xdp support"
123	exit $KSFT_SKIP
124fi
125
126# Interactive mode likely require us to cleanup netns
127if [ -n "$INTERACTIVE" ]; then
128	ip link del veth1 2> /dev/null
129	ip netns del ns1 2> /dev/null
130	ip netns del ns2 2> /dev/null
131fi
132
133# Exit on failure
134set -e
135
136# Some shell-tools dependencies
137which ip > /dev/null
138which tc > /dev/null
139which ethtool > /dev/null
140
141# Make rest of shell verbose, showing comments as doc/info
142if [ -n "$VERBOSE" ]; then
143    set -v
144fi
145
146# Create two namespaces
147ip netns add ns1
148ip netns add ns2
149
150# Run cleanup if failing or on kill
151trap cleanup 0 2 3 6 9
152
153# Create veth pair
154ip link add veth1 type veth peer name veth2
155
156# Move veth1 and veth2 into the respective namespaces
157ip link set veth1 netns ns1
158ip link set veth2 netns ns2
159
160# NOTICE: XDP require VLAN header inside packet payload
161#  - Thus, disable VLAN offloading driver features
162#  - For veth REMEMBER TX side VLAN-offload
163#
164# Disable rx-vlan-offload (mostly needed on ns1)
165ip netns exec ns1 ethtool -K veth1 rxvlan off
166ip netns exec ns2 ethtool -K veth2 rxvlan off
167#
168# Disable tx-vlan-offload (mostly needed on ns2)
169ip netns exec ns2 ethtool -K veth2 txvlan off
170ip netns exec ns1 ethtool -K veth1 txvlan off
171
172export IPADDR1=100.64.41.1
173export IPADDR2=100.64.41.2
174
175# In ns1/veth1 add IP-addr on plain net_device
176ip netns exec ns1 ip addr add ${IPADDR1}/24 dev veth1
177ip netns exec ns1 ip link set veth1 up
178
179# In ns2/veth2 create VLAN device
180export VLAN=4011
181export DEVNS2=veth2
182ip netns exec ns2 ip link add link $DEVNS2 name $DEVNS2.$VLAN type vlan id $VLAN
183ip netns exec ns2 ip addr add ${IPADDR2}/24 dev $DEVNS2.$VLAN
184ip netns exec ns2 ip link set $DEVNS2 up
185ip netns exec ns2 ip link set $DEVNS2.$VLAN up
186
187# Bringup lo in netns (to avoids confusing people using --interactive)
188ip netns exec ns1 ip link set lo up
189ip netns exec ns2 ip link set lo up
190
191# At this point, the hosts cannot reach each-other,
192# because ns2 are using VLAN tags on the packets.
193
194ip netns exec ns2 sh -c 'ping -W 1 -c 1 100.64.41.1 || echo "Success: First ping must fail"'
195
196
197# Now we can use the test_xdp_vlan.c program to pop/push these VLAN tags
198# ----------------------------------------------------------------------
199# In ns1: ingress use XDP to remove VLAN tags
200export DEVNS1=veth1
201export FILE=test_xdp_vlan.o
202
203# First test: Remove VLAN by setting VLAN ID 0, using "xdp_vlan_change"
204export XDP_PROG=xdp_vlan_change
205ip netns exec ns1 ip link set $DEVNS1 $XDP_MODE object $FILE section $XDP_PROG
206
207# In ns1: egress use TC to add back VLAN tag 4011
208#  (del cmd)
209#  tc qdisc del dev $DEVNS1 clsact 2> /dev/null
210#
211ip netns exec ns1 tc qdisc add dev $DEVNS1 clsact
212ip netns exec ns1 tc filter add dev $DEVNS1 egress \
213  prio 1 handle 1 bpf da obj $FILE sec tc_vlan_push
214
215# Now the namespaces can reach each-other, test with ping:
216ip netns exec ns2 ping -i 0.2 -W 2 -c 2 $IPADDR1
217ip netns exec ns1 ping -i 0.2 -W 2 -c 2 $IPADDR2
218
219# Second test: Replace xdp prog, that fully remove vlan header
220#
221# Catch kernel bug for generic-XDP, that does didn't allow us to
222# remove a VLAN header, because skb->protocol still contain VLAN
223# ETH_P_8021Q indication, and this cause overwriting of our changes.
224#
225export XDP_PROG=xdp_vlan_remove_outer2
226ip netns exec ns1 ip link set $DEVNS1 $XDP_MODE off
227ip netns exec ns1 ip link set $DEVNS1 $XDP_MODE object $FILE section $XDP_PROG
228
229# Now the namespaces should still be able reach each-other, test with ping:
230ip netns exec ns2 ping -i 0.2 -W 2 -c 2 $IPADDR1
231ip netns exec ns1 ping -i 0.2 -W 2 -c 2 $IPADDR2
232