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