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