xref: /linux/tools/testing/selftests/net/fib_rule_tests.sh (revision 550ee90ac61c1f0cd987c68a9ac6c4c9833925d7)
1#!/bin/bash
2# SPDX-License-Identifier: GPL-2.0
3
4# This test is for checking IPv4 and IPv6 FIB rules API
5
6source lib.sh
7ret=0
8PAUSE_ON_FAIL=${PAUSE_ON_FAIL:=no}
9
10RTABLE=100
11RTABLE_PEER=101
12RTABLE_VRF=102
13GW_IP4=192.51.100.2
14SRC_IP=192.51.100.3
15GW_IP6=2001:db8:1::2
16SRC_IP6=2001:db8:1::3
17
18DEV_ADDR=192.51.100.1
19DEV_ADDR6=2001:db8:1::1
20DEV=dummy0
21TESTS="
22	fib_rule6
23	fib_rule4
24	fib_rule6_connect
25	fib_rule4_connect
26	fib_rule6_vrf
27	fib_rule4_vrf
28"
29
30SELFTEST_PATH=""
31
32log_test()
33{
34	local rc=$1
35	local expected=$2
36	local msg="$3"
37
38	if [ ${rc} -eq ${expected} ]; then
39		nsuccess=$((nsuccess+1))
40		printf "    TEST: %-60s  [ OK ]\n" "${msg}"
41	else
42		ret=1
43		nfail=$((nfail+1))
44		printf "    TEST: %-60s  [FAIL]\n" "${msg}"
45		if [ "${PAUSE_ON_FAIL}" = "yes" ]; then
46			echo
47			echo "hit enter to continue, 'q' to quit"
48			read a
49			[ "$a" = "q" ] && exit 1
50		fi
51	fi
52}
53
54setup()
55{
56	set -e
57	setup_ns testns
58	IP="ip -netns $testns"
59
60	$IP link add dummy0 type dummy
61	$IP link set dev dummy0 up
62	$IP address add $DEV_ADDR/24 dev dummy0
63	$IP -6 address add $DEV_ADDR6/64 dev dummy0
64
65	set +e
66}
67
68cleanup()
69{
70	$IP link del dev dummy0 &> /dev/null
71	cleanup_ns $testns
72}
73
74setup_peer()
75{
76	set -e
77
78	setup_ns peerns
79	IP_PEER="ip -netns $peerns"
80	$IP_PEER link set dev lo up
81
82	ip link add name veth0 netns $testns type veth \
83		peer name veth1 netns $peerns
84	$IP link set dev veth0 up
85	$IP_PEER link set dev veth1 up
86
87	$IP address add 192.0.2.10 peer 192.0.2.11/32 dev veth0
88	$IP_PEER address add 192.0.2.11 peer 192.0.2.10/32 dev veth1
89
90	$IP address add 2001:db8::10 peer 2001:db8::11/128 dev veth0 nodad
91	$IP_PEER address add 2001:db8::11 peer 2001:db8::10/128 dev veth1 nodad
92
93	$IP_PEER address add 198.51.100.11/32 dev lo
94	$IP route add table $RTABLE_PEER 198.51.100.11/32 via 192.0.2.11
95
96	$IP_PEER address add 2001:db8::1:11/128 dev lo
97	$IP route add table $RTABLE_PEER 2001:db8::1:11/128 via 2001:db8::11
98
99	set +e
100}
101
102cleanup_peer()
103{
104	$IP link del dev veth0
105	ip netns del $peerns
106}
107
108setup_vrf()
109{
110	$IP link add name vrf0 up type vrf table $RTABLE_VRF
111	$IP link set dev $DEV master vrf0
112}
113
114cleanup_vrf()
115{
116	$IP link del dev vrf0
117}
118
119fib_check_iproute_support()
120{
121	ip rule help 2>&1 | grep -q $1
122	if [ $? -ne 0 ]; then
123		echo "SKIP: iproute2 iprule too old, missing $1 match"
124		return 1
125	fi
126
127	ip route get help 2>&1 | grep -q $2
128	if [ $? -ne 0 ]; then
129		echo "SKIP: iproute2 get route too old, missing $2 match"
130		return 1
131	fi
132
133	return 0
134}
135
136fib_rule6_del()
137{
138	$IP -6 rule del $1
139	log_test $? 0 "rule6 del $1"
140}
141
142fib_rule6_del_by_pref()
143{
144	pref=$($IP -6 rule show $1 table $RTABLE | cut -d ":" -f 1)
145	$IP -6 rule del pref $pref
146}
147
148fib_rule6_test_match_n_redirect()
149{
150	local match="$1"
151	local getmatch="$2"
152	local getnomatch="$3"
153	local description="$4"
154	local nomatch_description="$5"
155
156	$IP -6 rule add $match table $RTABLE
157	$IP -6 route get $GW_IP6 $getmatch | grep -q "table $RTABLE"
158	log_test $? 0 "rule6 check: $description"
159
160	$IP -6 route get $GW_IP6 $getnomatch 2>&1 | grep -q "table $RTABLE"
161	log_test $? 1 "rule6 check: $nomatch_description"
162
163	fib_rule6_del_by_pref "$match"
164	log_test $? 0 "rule6 del by pref: $description"
165}
166
167fib_rule6_test_reject()
168{
169	local match="$1"
170	local rc
171
172	$IP -6 rule add $match table $RTABLE 2>/dev/null
173	rc=$?
174	log_test $rc 2 "rule6 check: $match"
175
176	if [ $rc -eq 0 ]; then
177		$IP -6 rule del $match table $RTABLE
178	fi
179}
180
181fib_rule6_test()
182{
183	local ext_name=$1; shift
184	local getnomatch
185	local getmatch
186	local match
187	local cnt
188
189	echo
190	echo "IPv6 FIB rule tests $ext_name"
191
192	# setup the fib rule redirect route
193	$IP -6 route add table $RTABLE default via $GW_IP6 dev $DEV onlink
194
195	match="oif $DEV"
196	getnomatch="oif lo"
197	fib_rule6_test_match_n_redirect "$match" "$match" "$getnomatch" \
198		"oif redirect to table" "oif no redirect to table"
199
200	match="from $SRC_IP6 iif $DEV"
201	getnomatch="from $SRC_IP6 iif lo"
202	fib_rule6_test_match_n_redirect "$match" "$match" "$getnomatch" \
203		"iif redirect to table" "iif no redirect to table"
204
205	# Reject dsfield (tos) options which have ECN bits set
206	for cnt in $(seq 1 3); do
207		match="dsfield $cnt"
208		fib_rule6_test_reject "$match"
209	done
210
211	# Don't take ECN bits into account when matching on dsfield
212	match="tos 0x10"
213	for cnt in "0x10" "0x11" "0x12" "0x13"; do
214		# Using option 'tos' instead of 'dsfield' as old iproute2
215		# versions don't support 'dsfield' in ip rule show.
216		getmatch="tos $cnt"
217		getnomatch="tos 0x20"
218		fib_rule6_test_match_n_redirect "$match" "$getmatch" \
219			"$getnomatch" "$getmatch redirect to table" \
220			"$getnomatch no redirect to table"
221	done
222
223	# Re-test TOS matching, but with input routes since they are handled
224	# differently from output routes.
225	match="tos 0x10"
226	for cnt in "0x10" "0x11" "0x12" "0x13"; do
227		getmatch="tos $cnt"
228		getnomatch="tos 0x20"
229		fib_rule6_test_match_n_redirect "$match" \
230			"from $SRC_IP6 iif $DEV $getmatch" \
231			"from $SRC_IP6 iif $DEV $getnomatch" \
232			"iif $getmatch redirect to table" \
233			"iif $getnomatch no redirect to table"
234	done
235
236	match="fwmark 0x64"
237	getmatch="mark 0x64"
238	getnomatch="mark 0x63"
239	fib_rule6_test_match_n_redirect "$match" "$getmatch" "$getnomatch" \
240		"fwmark redirect to table" "fwmark no redirect to table"
241
242	fib_check_iproute_support "uidrange" "uid"
243	if [ $? -eq 0 ]; then
244		match="uidrange 100-100"
245		getmatch="uid 100"
246		getnomatch="uid 101"
247		fib_rule6_test_match_n_redirect "$match" "$getmatch" \
248			"$getnomatch" "uid redirect to table" \
249			"uid no redirect to table"
250	fi
251
252	fib_check_iproute_support "sport" "sport"
253	if [ $? -eq 0 ]; then
254		match="sport 666 dport 777"
255		getnomatch="sport 667 dport 778"
256		fib_rule6_test_match_n_redirect "$match" "$match" \
257			"$getnomatch" "sport and dport redirect to table" \
258			"sport and dport no redirect to table"
259	fi
260
261	fib_check_iproute_support "ipproto" "ipproto"
262	if [ $? -eq 0 ]; then
263		match="ipproto tcp"
264		getnomatch="ipproto udp"
265		fib_rule6_test_match_n_redirect "$match" "$match" \
266			"$getnomatch" "ipproto tcp match" "ipproto udp no match"
267	fi
268
269	fib_check_iproute_support "ipproto" "ipproto"
270	if [ $? -eq 0 ]; then
271		match="ipproto ipv6-icmp"
272		getnomatch="ipproto tcp"
273		fib_rule6_test_match_n_redirect "$match" "$match" \
274			"$getnomatch" "ipproto ipv6-icmp match" \
275			"ipproto ipv6-tcp no match"
276	fi
277}
278
279fib_rule6_vrf_test()
280{
281	setup_vrf
282	fib_rule6_test "- with VRF"
283	cleanup_vrf
284}
285
286# Verify that the IPV6_TCLASS option of UDPv6 and TCPv6 sockets is properly
287# taken into account when connecting the socket and when sending packets.
288fib_rule6_connect_test()
289{
290	local dsfield
291
292	echo
293	echo "IPv6 FIB rule connect tests"
294
295	setup_peer
296	$IP -6 rule add dsfield 0x04 table $RTABLE_PEER
297
298	# Combine the base DS Field value (0x04) with all possible ECN values
299	# (Not-ECT: 0, ECT(1): 1, ECT(0): 2, CE: 3).
300	# The ECN bits shouldn't influence the result of the test.
301	for dsfield in 0x04 0x05 0x06 0x07; do
302		nettest -q -6 -B -t 5 -N $testns -O $peerns -U -D \
303			-Q "${dsfield}" -l 2001:db8::1:11 -r 2001:db8::1:11
304		log_test $? 0 "rule6 dsfield udp connect (dsfield ${dsfield})"
305
306		nettest -q -6 -B -t 5 -N $testns -O $peerns -Q "${dsfield}" \
307			-l 2001:db8::1:11 -r 2001:db8::1:11
308		log_test $? 0 "rule6 dsfield tcp connect (dsfield ${dsfield})"
309	done
310
311	# Check that UDP and TCP connections fail when using a DS Field that
312	# does not match the previously configured FIB rule.
313	nettest -q -6 -B -t 5 -N $testns -O $peerns -U -D \
314		-Q 0x20 -l 2001:db8::1:11 -r 2001:db8::1:11
315	log_test $? 1 "rule6 dsfield udp no connect (dsfield 0x20)"
316
317	nettest -q -6 -B -t 5 -N $testns -O $peerns -Q 0x20 \
318		-l 2001:db8::1:11 -r 2001:db8::1:11
319	log_test $? 1 "rule6 dsfield tcp no connect (dsfield 0x20)"
320
321	$IP -6 rule del dsfield 0x04 table $RTABLE_PEER
322	cleanup_peer
323}
324
325fib_rule4_del()
326{
327	$IP rule del $1
328	log_test $? 0 "del $1"
329}
330
331fib_rule4_del_by_pref()
332{
333	pref=$($IP rule show $1 table $RTABLE | cut -d ":" -f 1)
334	$IP rule del pref $pref
335}
336
337fib_rule4_test_match_n_redirect()
338{
339	local match="$1"
340	local getmatch="$2"
341	local getnomatch="$3"
342	local description="$4"
343	local nomatch_description="$5"
344
345	$IP rule add $match table $RTABLE
346	$IP route get $GW_IP4 $getmatch | grep -q "table $RTABLE"
347	log_test $? 0 "rule4 check: $description"
348
349	$IP route get $GW_IP4 $getnomatch 2>&1 | grep -q "table $RTABLE"
350	log_test $? 1 "rule4 check: $nomatch_description"
351
352	fib_rule4_del_by_pref "$match"
353	log_test $? 0 "rule4 del by pref: $description"
354}
355
356fib_rule4_test_reject()
357{
358	local match="$1"
359	local rc
360
361	$IP rule add $match table $RTABLE 2>/dev/null
362	rc=$?
363	log_test $rc 2 "rule4 check: $match"
364
365	if [ $rc -eq 0 ]; then
366		$IP rule del $match table $RTABLE
367	fi
368}
369
370fib_rule4_test()
371{
372	local ext_name=$1; shift
373	local getnomatch
374	local getmatch
375	local match
376	local cnt
377
378	echo
379	echo "IPv4 FIB rule tests $ext_name"
380
381	# setup the fib rule redirect route
382	$IP route add table $RTABLE default via $GW_IP4 dev $DEV onlink
383
384	match="oif $DEV"
385	getnomatch="oif lo"
386	fib_rule4_test_match_n_redirect "$match" "$match" "$getnomatch" \
387		"oif redirect to table" "oif no redirect to table"
388
389	# Enable forwarding and disable rp_filter as all the addresses are in
390	# the same subnet and egress device == ingress device.
391	ip netns exec $testns sysctl -qw net.ipv4.ip_forward=1
392	ip netns exec $testns sysctl -qw net.ipv4.conf.$DEV.rp_filter=0
393	match="from $SRC_IP iif $DEV"
394	getnomatch="from $SRC_IP iif lo"
395	fib_rule4_test_match_n_redirect "$match" "$match" "$getnomatch" \
396		"iif redirect to table" "iif no redirect to table"
397
398	# Reject dsfield (tos) options which have ECN bits set
399	for cnt in $(seq 1 3); do
400		match="dsfield $cnt"
401		fib_rule4_test_reject "$match"
402	done
403
404	# Don't take ECN bits into account when matching on dsfield
405	match="tos 0x10"
406	for cnt in "0x10" "0x11" "0x12" "0x13"; do
407		# Using option 'tos' instead of 'dsfield' as old iproute2
408		# versions don't support 'dsfield' in ip rule show.
409		getmatch="tos $cnt"
410		getnomatch="tos 0x20"
411		fib_rule4_test_match_n_redirect "$match" "$getmatch" \
412			"$getnomatch" "$getmatch redirect to table" \
413			"$getnomatch no redirect to table"
414	done
415
416	# Re-test TOS matching, but with input routes since they are handled
417	# differently from output routes.
418	match="tos 0x10"
419	for cnt in "0x10" "0x11" "0x12" "0x13"; do
420		getmatch="tos $cnt"
421		getnomatch="tos 0x20"
422		fib_rule4_test_match_n_redirect "$match" \
423			"from $SRC_IP iif $DEV $getmatch" \
424			"from $SRC_IP iif $DEV $getnomatch" \
425			"iif $getmatch redirect to table" \
426			"iif $getnomatch no redirect to table"
427	done
428
429	match="fwmark 0x64"
430	getmatch="mark 0x64"
431	getnomatch="mark 0x63"
432	fib_rule4_test_match_n_redirect "$match" "$getmatch" "$getnomatch" \
433		"fwmark redirect to table" "fwmark no redirect to table"
434
435	fib_check_iproute_support "uidrange" "uid"
436	if [ $? -eq 0 ]; then
437		match="uidrange 100-100"
438		getmatch="uid 100"
439		getnomatch="uid 101"
440		fib_rule4_test_match_n_redirect "$match" "$getmatch" \
441			"$getnomatch" "uid redirect to table" \
442			"uid no redirect to table"
443	fi
444
445	fib_check_iproute_support "sport" "sport"
446	if [ $? -eq 0 ]; then
447		match="sport 666 dport 777"
448		getnomatch="sport 667 dport 778"
449		fib_rule4_test_match_n_redirect "$match" "$match" \
450			"$getnomatch" "sport and dport redirect to table" \
451			"sport and dport no redirect to table"
452	fi
453
454	fib_check_iproute_support "ipproto" "ipproto"
455	if [ $? -eq 0 ]; then
456		match="ipproto tcp"
457		getnomatch="ipproto udp"
458		fib_rule4_test_match_n_redirect "$match" "$match" \
459			"$getnomatch" "ipproto tcp match" \
460			"ipproto udp no match"
461	fi
462
463	fib_check_iproute_support "ipproto" "ipproto"
464	if [ $? -eq 0 ]; then
465		match="ipproto icmp"
466		getnomatch="ipproto tcp"
467		fib_rule4_test_match_n_redirect "$match" "$match" \
468			"$getnomatch" "ipproto icmp match" \
469			"ipproto tcp no match"
470	fi
471}
472
473fib_rule4_vrf_test()
474{
475	setup_vrf
476	fib_rule4_test "- with VRF"
477	cleanup_vrf
478}
479
480# Verify that the IP_TOS option of UDPv4 and TCPv4 sockets is properly taken
481# into account when connecting the socket and when sending packets.
482fib_rule4_connect_test()
483{
484	local dsfield
485
486	echo
487	echo "IPv4 FIB rule connect tests"
488
489	setup_peer
490	$IP -4 rule add dsfield 0x04 table $RTABLE_PEER
491
492	# Combine the base DS Field value (0x04) with all possible ECN values
493	# (Not-ECT: 0, ECT(1): 1, ECT(0): 2, CE: 3).
494	# The ECN bits shouldn't influence the result of the test.
495	for dsfield in 0x04 0x05 0x06 0x07; do
496		nettest -q -B -t 5 -N $testns -O $peerns -D -U -Q "${dsfield}" \
497			-l 198.51.100.11 -r 198.51.100.11
498		log_test $? 0 "rule4 dsfield udp connect (dsfield ${dsfield})"
499
500		nettest -q -B -t 5 -N $testns -O $peerns -Q "${dsfield}" \
501			-l 198.51.100.11 -r 198.51.100.11
502		log_test $? 0 "rule4 dsfield tcp connect (dsfield ${dsfield})"
503	done
504
505	# Check that UDP and TCP connections fail when using a DS Field that
506	# does not match the previously configured FIB rule.
507	nettest -q -B -t 5 -N $testns -O $peerns -D -U -Q 0x20 \
508		-l 198.51.100.11 -r 198.51.100.11
509	log_test $? 1 "rule4 dsfield udp no connect (dsfield 0x20)"
510
511	nettest -q -B -t 5 -N $testns -O $peerns -Q 0x20 \
512		-l 198.51.100.11 -r 198.51.100.11
513	log_test $? 1 "rule4 dsfield tcp no connect (dsfield 0x20)"
514
515	$IP -4 rule del dsfield 0x04 table $RTABLE_PEER
516	cleanup_peer
517}
518################################################################################
519# usage
520
521usage()
522{
523	cat <<EOF
524usage: ${0##*/} OPTS
525
526        -t <test>   Test(s) to run (default: all)
527                    (options: $TESTS)
528EOF
529}
530
531################################################################################
532# main
533
534while getopts ":t:h" opt; do
535	case $opt in
536		t) TESTS=$OPTARG;;
537		h) usage; exit 0;;
538		*) usage; exit 1;;
539	esac
540done
541
542if [ "$(id -u)" -ne 0 ];then
543	echo "SKIP: Need root privileges"
544	exit $ksft_skip
545fi
546
547if [ ! -x "$(command -v ip)" ]; then
548	echo "SKIP: Could not run test without ip tool"
549	exit $ksft_skip
550fi
551
552check_gen_prog "nettest"
553
554# start clean
555cleanup &> /dev/null
556setup
557for t in $TESTS
558do
559	case $t in
560	fib_rule6_test|fib_rule6)		fib_rule6_test;;
561	fib_rule4_test|fib_rule4)		fib_rule4_test;;
562	fib_rule6_connect_test|fib_rule6_connect)	fib_rule6_connect_test;;
563	fib_rule4_connect_test|fib_rule4_connect)	fib_rule4_connect_test;;
564	fib_rule6_vrf_test|fib_rule6_vrf)	fib_rule6_vrf_test;;
565	fib_rule4_vrf_test|fib_rule4_vrf)	fib_rule4_vrf_test;;
566
567	help) echo "Test names: $TESTS"; exit 0;;
568
569	esac
570done
571cleanup
572
573if [ "$TESTS" != "none" ]; then
574	printf "\nTests passed: %3d\n" ${nsuccess}
575	printf "Tests failed: %3d\n"   ${nfail}
576fi
577
578exit $ret
579