1#!/bin/bash
2# SPDX-License-Identifier: GPL-2.0
3
4# IPv4 and IPv6 onlink tests
5
6PAUSE_ON_FAIL=${PAUSE_ON_FAIL:=no}
7
8# Network interfaces
9# - odd in current namespace; even in peer ns
10declare -A NETIFS
11# default VRF
12NETIFS[p1]=veth1
13NETIFS[p2]=veth2
14NETIFS[p3]=veth3
15NETIFS[p4]=veth4
16# VRF
17NETIFS[p5]=veth5
18NETIFS[p6]=veth6
19NETIFS[p7]=veth7
20NETIFS[p8]=veth8
21
22# /24 network
23declare -A V4ADDRS
24V4ADDRS[p1]=169.254.1.1
25V4ADDRS[p2]=169.254.1.2
26V4ADDRS[p3]=169.254.3.1
27V4ADDRS[p4]=169.254.3.2
28V4ADDRS[p5]=169.254.5.1
29V4ADDRS[p6]=169.254.5.2
30V4ADDRS[p7]=169.254.7.1
31V4ADDRS[p8]=169.254.7.2
32
33# /64 network
34declare -A V6ADDRS
35V6ADDRS[p1]=2001:db8:101::1
36V6ADDRS[p2]=2001:db8:101::2
37V6ADDRS[p3]=2001:db8:301::1
38V6ADDRS[p4]=2001:db8:301::2
39V6ADDRS[p5]=2001:db8:501::1
40V6ADDRS[p6]=2001:db8:501::2
41V6ADDRS[p7]=2001:db8:701::1
42V6ADDRS[p8]=2001:db8:701::2
43
44# Test networks:
45# [1] = default table
46# [2] = VRF
47#
48# /32 host routes
49declare -A TEST_NET4
50TEST_NET4[1]=169.254.101
51TEST_NET4[2]=169.254.102
52# /128 host routes
53declare -A TEST_NET6
54TEST_NET6[1]=2001:db8:101
55TEST_NET6[2]=2001:db8:102
56
57# connected gateway
58CONGW[1]=169.254.1.254
59CONGW[2]=169.254.3.254
60CONGW[3]=169.254.5.254
61
62# recursive gateway
63RECGW4[1]=169.254.11.254
64RECGW4[2]=169.254.12.254
65RECGW6[1]=2001:db8:11::64
66RECGW6[2]=2001:db8:12::64
67
68# for v4 mapped to v6
69declare -A TEST_NET4IN6IN6
70TEST_NET4IN6[1]=10.1.1.254
71TEST_NET4IN6[2]=10.2.1.254
72
73# mcast address
74MCAST6=ff02::1
75
76
77PEER_NS=bart
78PEER_CMD="ip netns exec ${PEER_NS}"
79VRF=lisa
80VRF_TABLE=1101
81PBR_TABLE=101
82
83################################################################################
84# utilities
85
86log_test()
87{
88	local rc=$1
89	local expected=$2
90	local msg="$3"
91
92	if [ ${rc} -eq ${expected} ]; then
93		nsuccess=$((nsuccess+1))
94		printf "\n    TEST: %-50s  [ OK ]\n" "${msg}"
95	else
96		nfail=$((nfail+1))
97		printf "\n    TEST: %-50s  [FAIL]\n" "${msg}"
98		if [ "${PAUSE_ON_FAIL}" = "yes" ]; then
99			echo
100			echo "hit enter to continue, 'q' to quit"
101			read a
102			[ "$a" = "q" ] && exit 1
103		fi
104	fi
105}
106
107log_section()
108{
109	echo
110	echo "######################################################################"
111	echo "TEST SECTION: $*"
112	echo "######################################################################"
113}
114
115log_subsection()
116{
117	echo
118	echo "#########################################"
119	echo "TEST SUBSECTION: $*"
120}
121
122run_cmd()
123{
124	echo
125	echo "COMMAND: $*"
126	eval $*
127}
128
129get_linklocal()
130{
131	local dev=$1
132	local pfx
133	local addr
134
135	addr=$(${pfx} ip -6 -br addr show dev ${dev} | \
136	awk '{
137		for (i = 3; i <= NF; ++i) {
138			if ($i ~ /^fe80/)
139				print $i
140		}
141	}'
142	)
143	addr=${addr/\/*}
144
145	[ -z "$addr" ] && return 1
146
147	echo $addr
148
149	return 0
150}
151
152################################################################################
153#
154
155setup()
156{
157	echo
158	echo "########################################"
159	echo "Configuring interfaces"
160
161	set -e
162
163	# create namespace
164	ip netns add ${PEER_NS}
165	ip -netns ${PEER_NS} li set lo up
166
167	# add vrf table
168	ip li add ${VRF} type vrf table ${VRF_TABLE}
169	ip li set ${VRF} up
170	ip ro add table ${VRF_TABLE} unreachable default
171	ip -6 ro add table ${VRF_TABLE} unreachable default
172
173	# create test interfaces
174	ip li add ${NETIFS[p1]} type veth peer name ${NETIFS[p2]}
175	ip li add ${NETIFS[p3]} type veth peer name ${NETIFS[p4]}
176	ip li add ${NETIFS[p5]} type veth peer name ${NETIFS[p6]}
177	ip li add ${NETIFS[p7]} type veth peer name ${NETIFS[p8]}
178
179	# enslave vrf interfaces
180	for n in 5 7; do
181		ip li set ${NETIFS[p${n}]} vrf ${VRF}
182	done
183
184	# add addresses
185	for n in 1 3 5 7; do
186		ip li set ${NETIFS[p${n}]} up
187		ip addr add ${V4ADDRS[p${n}]}/24 dev ${NETIFS[p${n}]}
188		ip addr add ${V6ADDRS[p${n}]}/64 dev ${NETIFS[p${n}]}
189	done
190
191	# move peer interfaces to namespace and add addresses
192	for n in 2 4 6 8; do
193		ip li set ${NETIFS[p${n}]} netns ${PEER_NS} up
194		ip -netns ${PEER_NS} addr add ${V4ADDRS[p${n}]}/24 dev ${NETIFS[p${n}]}
195		ip -netns ${PEER_NS} addr add ${V6ADDRS[p${n}]}/64 dev ${NETIFS[p${n}]}
196	done
197
198	set +e
199
200	# let DAD complete - assume default of 1 probe
201	sleep 1
202}
203
204cleanup()
205{
206	# make sure we start from a clean slate
207	ip netns del ${PEER_NS} 2>/dev/null
208	for n in 1 3 5 7; do
209		ip link del ${NETIFS[p${n}]} 2>/dev/null
210	done
211	ip link del ${VRF} 2>/dev/null
212	ip ro flush table ${VRF_TABLE}
213	ip -6 ro flush table ${VRF_TABLE}
214}
215
216################################################################################
217# IPv4 tests
218#
219
220run_ip()
221{
222	local table="$1"
223	local prefix="$2"
224	local gw="$3"
225	local dev="$4"
226	local exp_rc="$5"
227	local desc="$6"
228
229	# dev arg may be empty
230	[ -n "${dev}" ] && dev="dev ${dev}"
231
232	run_cmd ip ro add table "${table}" "${prefix}"/32 via "${gw}" "${dev}" onlink
233	log_test $? ${exp_rc} "${desc}"
234}
235
236run_ip_mpath()
237{
238	local table="$1"
239	local prefix="$2"
240	local nh1="$3"
241	local nh2="$4"
242	local exp_rc="$5"
243	local desc="$6"
244
245	# dev arg may be empty
246	[ -n "${dev}" ] && dev="dev ${dev}"
247
248	run_cmd ip ro add table "${table}" "${prefix}"/32 \
249		nexthop via ${nh1} nexthop via ${nh2}
250	log_test $? ${exp_rc} "${desc}"
251}
252
253valid_onlink_ipv4()
254{
255	# - unicast connected, unicast recursive
256	#
257	log_subsection "default VRF - main table"
258
259	run_ip 254 ${TEST_NET4[1]}.1 ${CONGW[1]} ${NETIFS[p1]} 0 "unicast connected"
260	run_ip 254 ${TEST_NET4[1]}.2 ${RECGW4[1]} ${NETIFS[p1]} 0 "unicast recursive"
261
262	log_subsection "VRF ${VRF}"
263
264	run_ip ${VRF_TABLE} ${TEST_NET4[2]}.1 ${CONGW[3]} ${NETIFS[p5]} 0 "unicast connected"
265	run_ip ${VRF_TABLE} ${TEST_NET4[2]}.2 ${RECGW4[2]} ${NETIFS[p5]} 0 "unicast recursive"
266
267	log_subsection "VRF device, PBR table"
268
269	run_ip ${PBR_TABLE} ${TEST_NET4[2]}.3 ${CONGW[3]} ${NETIFS[p5]} 0 "unicast connected"
270	run_ip ${PBR_TABLE} ${TEST_NET4[2]}.4 ${RECGW4[2]} ${NETIFS[p5]} 0 "unicast recursive"
271
272	# multipath version
273	#
274	log_subsection "default VRF - main table - multipath"
275
276	run_ip_mpath 254 ${TEST_NET4[1]}.5 \
277		"${CONGW[1]} dev ${NETIFS[p1]} onlink" \
278		"${CONGW[2]} dev ${NETIFS[p3]} onlink" \
279		0 "unicast connected - multipath"
280
281	run_ip_mpath 254 ${TEST_NET4[1]}.6 \
282		"${RECGW4[1]} dev ${NETIFS[p1]} onlink" \
283		"${RECGW4[2]} dev ${NETIFS[p3]} onlink" \
284		0 "unicast recursive - multipath"
285
286	run_ip_mpath 254 ${TEST_NET4[1]}.7 \
287		"${CONGW[1]} dev ${NETIFS[p1]}"        \
288		"${CONGW[2]} dev ${NETIFS[p3]} onlink" \
289		0 "unicast connected - multipath onlink first only"
290
291	run_ip_mpath 254 ${TEST_NET4[1]}.8 \
292		"${CONGW[1]} dev ${NETIFS[p1]} onlink" \
293		"${CONGW[2]} dev ${NETIFS[p3]}"        \
294		0 "unicast connected - multipath onlink second only"
295}
296
297invalid_onlink_ipv4()
298{
299	run_ip 254 ${TEST_NET4[1]}.11 ${V4ADDRS[p1]} ${NETIFS[p1]} 2 \
300		"Invalid gw - local unicast address"
301
302	run_ip ${VRF_TABLE} ${TEST_NET4[2]}.11 ${V4ADDRS[p5]} ${NETIFS[p5]} 2 \
303		"Invalid gw - local unicast address, VRF"
304
305	run_ip 254 ${TEST_NET4[1]}.101 ${V4ADDRS[p1]} "" 2 "No nexthop device given"
306
307	run_ip 254 ${TEST_NET4[1]}.102 ${V4ADDRS[p3]} ${NETIFS[p1]} 2 \
308		"Gateway resolves to wrong nexthop device"
309
310	run_ip ${VRF_TABLE} ${TEST_NET4[2]}.103 ${V4ADDRS[p7]} ${NETIFS[p5]} 2 \
311		"Gateway resolves to wrong nexthop device - VRF"
312}
313
314################################################################################
315# IPv6 tests
316#
317
318run_ip6()
319{
320	local table="$1"
321	local prefix="$2"
322	local gw="$3"
323	local dev="$4"
324	local exp_rc="$5"
325	local desc="$6"
326
327	# dev arg may be empty
328	[ -n "${dev}" ] && dev="dev ${dev}"
329
330	run_cmd ip -6 ro add table "${table}" "${prefix}"/128 via "${gw}" "${dev}" onlink
331	log_test $? ${exp_rc} "${desc}"
332}
333
334run_ip6_mpath()
335{
336	local table="$1"
337	local prefix="$2"
338	local opts="$3"
339	local nh1="$4"
340	local nh2="$5"
341	local exp_rc="$6"
342	local desc="$7"
343
344	run_cmd ip -6 ro add table "${table}" "${prefix}"/128 "${opts}" \
345		nexthop via ${nh1} nexthop via ${nh2}
346	log_test $? ${exp_rc} "${desc}"
347}
348
349valid_onlink_ipv6()
350{
351	# - unicast connected, unicast recursive, v4-mapped
352	#
353	log_subsection "default VRF - main table"
354
355	run_ip6 254 ${TEST_NET6[1]}::1 ${V6ADDRS[p1]/::*}::64 ${NETIFS[p1]} 0 "unicast connected"
356	run_ip6 254 ${TEST_NET6[1]}::2 ${RECGW6[1]} ${NETIFS[p1]} 0 "unicast recursive"
357	run_ip6 254 ${TEST_NET6[1]}::3 ::ffff:${TEST_NET4IN6[1]} ${NETIFS[p1]} 0 "v4-mapped"
358
359	log_subsection "VRF ${VRF}"
360
361	run_ip6 ${VRF_TABLE} ${TEST_NET6[2]}::1 ${V6ADDRS[p5]/::*}::64 ${NETIFS[p5]} 0 "unicast connected"
362	run_ip6 ${VRF_TABLE} ${TEST_NET6[2]}::2 ${RECGW6[2]} ${NETIFS[p5]} 0 "unicast recursive"
363	run_ip6 ${VRF_TABLE} ${TEST_NET6[2]}::3 ::ffff:${TEST_NET4IN6[2]} ${NETIFS[p5]} 0 "v4-mapped"
364
365	log_subsection "VRF device, PBR table"
366
367	run_ip6 ${PBR_TABLE} ${TEST_NET6[2]}::4 ${V6ADDRS[p5]/::*}::64 ${NETIFS[p5]} 0 "unicast connected"
368	run_ip6 ${PBR_TABLE} ${TEST_NET6[2]}::5 ${RECGW6[2]} ${NETIFS[p5]} 0 "unicast recursive"
369	run_ip6 ${PBR_TABLE} ${TEST_NET6[2]}::6 ::ffff:${TEST_NET4IN6[2]} ${NETIFS[p5]} 0 "v4-mapped"
370
371	# multipath version
372	#
373	log_subsection "default VRF - main table - multipath"
374
375	run_ip6_mpath 254 ${TEST_NET6[1]}::4 "onlink" \
376		"${V6ADDRS[p1]/::*}::64 dev ${NETIFS[p1]}" \
377		"${V6ADDRS[p3]/::*}::64 dev ${NETIFS[p3]}" \
378		0 "unicast connected - multipath onlink"
379
380	run_ip6_mpath 254 ${TEST_NET6[1]}::5 "onlink" \
381		"${RECGW6[1]} dev ${NETIFS[p1]}" \
382		"${RECGW6[2]} dev ${NETIFS[p3]}" \
383		0 "unicast recursive - multipath onlink"
384
385	run_ip6_mpath 254 ${TEST_NET6[1]}::6 "onlink" \
386		"::ffff:${TEST_NET4IN6[1]} dev ${NETIFS[p1]}" \
387		"::ffff:${TEST_NET4IN6[2]} dev ${NETIFS[p3]}" \
388		0 "v4-mapped - multipath onlink"
389
390	run_ip6_mpath 254 ${TEST_NET6[1]}::7 "" \
391		"${V6ADDRS[p1]/::*}::64 dev ${NETIFS[p1]} onlink" \
392		"${V6ADDRS[p3]/::*}::64 dev ${NETIFS[p3]} onlink" \
393		0 "unicast connected - multipath onlink both nexthops"
394
395	run_ip6_mpath 254 ${TEST_NET6[1]}::8 "" \
396		"${V6ADDRS[p1]/::*}::64 dev ${NETIFS[p1]} onlink" \
397		"${V6ADDRS[p3]/::*}::64 dev ${NETIFS[p3]}" \
398		0 "unicast connected - multipath onlink first only"
399
400	run_ip6_mpath 254 ${TEST_NET6[1]}::9 "" \
401		"${V6ADDRS[p1]/::*}::64 dev ${NETIFS[p1]}"        \
402		"${V6ADDRS[p3]/::*}::64 dev ${NETIFS[p3]} onlink" \
403		0 "unicast connected - multipath onlink second only"
404}
405
406invalid_onlink_ipv6()
407{
408	local lladdr
409
410	lladdr=$(get_linklocal ${NETIFS[p1]}) || return 1
411
412	run_ip6 254 ${TEST_NET6[1]}::11 ${V6ADDRS[p1]} ${NETIFS[p1]} 2 \
413		"Invalid gw - local unicast address"
414	run_ip6 254 ${TEST_NET6[1]}::12 ${lladdr} ${NETIFS[p1]} 2 \
415		"Invalid gw - local linklocal address"
416	run_ip6 254 ${TEST_NET6[1]}::12 ${MCAST6} ${NETIFS[p1]} 2 \
417		"Invalid gw - multicast address"
418
419	lladdr=$(get_linklocal ${NETIFS[p5]}) || return 1
420	run_ip6 ${VRF_TABLE} ${TEST_NET6[2]}::11 ${V6ADDRS[p5]} ${NETIFS[p5]} 2 \
421		"Invalid gw - local unicast address, VRF"
422	run_ip6 ${VRF_TABLE} ${TEST_NET6[2]}::12 ${lladdr} ${NETIFS[p5]} 2 \
423		"Invalid gw - local linklocal address, VRF"
424	run_ip6 ${VRF_TABLE} ${TEST_NET6[2]}::12 ${MCAST6} ${NETIFS[p5]} 2 \
425		"Invalid gw - multicast address, VRF"
426
427	run_ip6 254 ${TEST_NET6[1]}::101 ${V6ADDRS[p1]} "" 2 \
428		"No nexthop device given"
429
430	# default VRF validation is done against LOCAL table
431	# run_ip6 254 ${TEST_NET6[1]}::102 ${V6ADDRS[p3]/::[0-9]/::64} ${NETIFS[p1]} 2 \
432	#	"Gateway resolves to wrong nexthop device"
433
434	run_ip6 ${VRF_TABLE} ${TEST_NET6[2]}::103 ${V6ADDRS[p7]/::[0-9]/::64} ${NETIFS[p5]} 2 \
435		"Gateway resolves to wrong nexthop device - VRF"
436}
437
438run_onlink_tests()
439{
440	log_section "IPv4 onlink"
441	log_subsection "Valid onlink commands"
442	valid_onlink_ipv4
443	log_subsection "Invalid onlink commands"
444	invalid_onlink_ipv4
445
446	log_section "IPv6 onlink"
447	log_subsection "Valid onlink commands"
448	valid_onlink_ipv6
449	log_subsection "Invalid onlink commands"
450	invalid_onlink_ipv6
451}
452
453################################################################################
454# main
455
456nsuccess=0
457nfail=0
458
459cleanup
460setup
461run_onlink_tests
462cleanup
463
464if [ "$TESTS" != "none" ]; then
465	printf "\nTests passed: %3d\n" ${nsuccess}
466	printf "Tests failed: %3d\n"   ${nfail}
467fi
468