1#!/bin/bash
2# SPDX-License-Identifier: GPL-2.0
3
4# This test is for checking IPv4 and IPv6 FIB rules API
5
6# Kselftest framework requirement - SKIP code is 4.
7ksft_skip=4
8
9ret=0
10
11PAUSE_ON_FAIL=${PAUSE_ON_FAIL:=no}
12IP="ip -netns testns"
13
14RTABLE=100
15GW_IP4=192.51.100.2
16SRC_IP=192.51.100.3
17GW_IP6=2001:db8:1::2
18SRC_IP6=2001:db8:1::3
19
20DEV_ADDR=192.51.100.1
21DEV_ADDR6=2001:db8:1::1
22DEV=dummy0
23
24log_test()
25{
26	local rc=$1
27	local expected=$2
28	local msg="$3"
29
30	if [ ${rc} -eq ${expected} ]; then
31		nsuccess=$((nsuccess+1))
32		printf "\n    TEST: %-50s  [ OK ]\n" "${msg}"
33	else
34		ret=1
35		nfail=$((nfail+1))
36		printf "\n    TEST: %-50s  [FAIL]\n" "${msg}"
37		if [ "${PAUSE_ON_FAIL}" = "yes" ]; then
38			echo
39			echo "hit enter to continue, 'q' to quit"
40			read a
41			[ "$a" = "q" ] && exit 1
42		fi
43	fi
44}
45
46log_section()
47{
48	echo
49	echo "######################################################################"
50	echo "TEST SECTION: $*"
51	echo "######################################################################"
52}
53
54setup()
55{
56	set -e
57	ip netns add testns
58	$IP link set dev lo up
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	ip netns del testns
72}
73
74fib_check_iproute_support()
75{
76	ip rule help 2>&1 | grep -q $1
77	if [ $? -ne 0 ]; then
78		echo "SKIP: iproute2 iprule too old, missing $1 match"
79		return 1
80	fi
81
82	ip route get help 2>&1 | grep -q $2
83	if [ $? -ne 0 ]; then
84		echo "SKIP: iproute2 get route too old, missing $2 match"
85		return 1
86	fi
87
88	return 0
89}
90
91fib_rule6_del()
92{
93	$IP -6 rule del $1
94	log_test $? 0 "rule6 del $1"
95}
96
97fib_rule6_del_by_pref()
98{
99	pref=$($IP -6 rule show $1 table $RTABLE | cut -d ":" -f 1)
100	$IP -6 rule del pref $pref
101}
102
103fib_rule6_test_match_n_redirect()
104{
105	local match="$1"
106	local getmatch="$2"
107	local description="$3"
108
109	$IP -6 rule add $match table $RTABLE
110	$IP -6 route get $GW_IP6 $getmatch | grep -q "table $RTABLE"
111	log_test $? 0 "rule6 check: $description"
112
113	fib_rule6_del_by_pref "$match"
114	log_test $? 0 "rule6 del by pref: $description"
115}
116
117fib_rule6_test_reject()
118{
119	local match="$1"
120	local rc
121
122	$IP -6 rule add $match table $RTABLE 2>/dev/null
123	rc=$?
124	log_test $rc 2 "rule6 check: $match"
125
126	if [ $rc -eq 0 ]; then
127		$IP -6 rule del $match table $RTABLE
128	fi
129}
130
131fib_rule6_test()
132{
133	local getmatch
134	local match
135	local cnt
136
137	# setup the fib rule redirect route
138	$IP -6 route add table $RTABLE default via $GW_IP6 dev $DEV onlink
139
140	match="oif $DEV"
141	fib_rule6_test_match_n_redirect "$match" "$match" "oif redirect to table"
142
143	match="from $SRC_IP6 iif $DEV"
144	fib_rule6_test_match_n_redirect "$match" "$match" "iif redirect to table"
145
146	# Reject dsfield (tos) options which have ECN bits set
147	for cnt in $(seq 1 3); do
148		match="dsfield $cnt"
149		fib_rule6_test_reject "$match"
150	done
151
152	# Don't take ECN bits into account when matching on dsfield
153	match="tos 0x10"
154	for cnt in "0x10" "0x11" "0x12" "0x13"; do
155		# Using option 'tos' instead of 'dsfield' as old iproute2
156		# versions don't support 'dsfield' in ip rule show.
157		getmatch="tos $cnt"
158		fib_rule6_test_match_n_redirect "$match" "$getmatch" \
159						"$getmatch redirect to table"
160	done
161
162	match="fwmark 0x64"
163	getmatch="mark 0x64"
164	fib_rule6_test_match_n_redirect "$match" "$getmatch" "fwmark redirect to table"
165
166	fib_check_iproute_support "uidrange" "uid"
167	if [ $? -eq 0 ]; then
168		match="uidrange 100-100"
169		getmatch="uid 100"
170		fib_rule6_test_match_n_redirect "$match" "$getmatch" "uid redirect to table"
171	fi
172
173	fib_check_iproute_support "sport" "sport"
174	if [ $? -eq 0 ]; then
175		match="sport 666 dport 777"
176		fib_rule6_test_match_n_redirect "$match" "$match" "sport and dport redirect to table"
177	fi
178
179	fib_check_iproute_support "ipproto" "ipproto"
180	if [ $? -eq 0 ]; then
181		match="ipproto tcp"
182		fib_rule6_test_match_n_redirect "$match" "$match" "ipproto match"
183	fi
184
185	fib_check_iproute_support "ipproto" "ipproto"
186	if [ $? -eq 0 ]; then
187		match="ipproto ipv6-icmp"
188		fib_rule6_test_match_n_redirect "$match" "$match" "ipproto ipv6-icmp match"
189	fi
190}
191
192fib_rule4_del()
193{
194	$IP rule del $1
195	log_test $? 0 "del $1"
196}
197
198fib_rule4_del_by_pref()
199{
200	pref=$($IP rule show $1 table $RTABLE | cut -d ":" -f 1)
201	$IP rule del pref $pref
202}
203
204fib_rule4_test_match_n_redirect()
205{
206	local match="$1"
207	local getmatch="$2"
208	local description="$3"
209
210	$IP rule add $match table $RTABLE
211	$IP route get $GW_IP4 $getmatch | grep -q "table $RTABLE"
212	log_test $? 0 "rule4 check: $description"
213
214	fib_rule4_del_by_pref "$match"
215	log_test $? 0 "rule4 del by pref: $description"
216}
217
218fib_rule4_test_reject()
219{
220	local match="$1"
221	local rc
222
223	$IP rule add $match table $RTABLE 2>/dev/null
224	rc=$?
225	log_test $rc 2 "rule4 check: $match"
226
227	if [ $rc -eq 0 ]; then
228		$IP rule del $match table $RTABLE
229	fi
230}
231
232fib_rule4_test()
233{
234	local getmatch
235	local match
236	local cnt
237
238	# setup the fib rule redirect route
239	$IP route add table $RTABLE default via $GW_IP4 dev $DEV onlink
240
241	match="oif $DEV"
242	fib_rule4_test_match_n_redirect "$match" "$match" "oif redirect to table"
243
244	# need enable forwarding and disable rp_filter temporarily as all the
245	# addresses are in the same subnet and egress device == ingress device.
246	ip netns exec testns sysctl -qw net.ipv4.ip_forward=1
247	ip netns exec testns sysctl -qw net.ipv4.conf.$DEV.rp_filter=0
248	match="from $SRC_IP iif $DEV"
249	fib_rule4_test_match_n_redirect "$match" "$match" "iif redirect to table"
250	ip netns exec testns sysctl -qw net.ipv4.ip_forward=0
251
252	# Reject dsfield (tos) options which have ECN bits set
253	for cnt in $(seq 1 3); do
254		match="dsfield $cnt"
255		fib_rule4_test_reject "$match"
256	done
257
258	# Don't take ECN bits into account when matching on dsfield
259	match="tos 0x10"
260	for cnt in "0x10" "0x11" "0x12" "0x13"; do
261		# Using option 'tos' instead of 'dsfield' as old iproute2
262		# versions don't support 'dsfield' in ip rule show.
263		getmatch="tos $cnt"
264		fib_rule4_test_match_n_redirect "$match" "$getmatch" \
265						"$getmatch redirect to table"
266	done
267
268	match="fwmark 0x64"
269	getmatch="mark 0x64"
270	fib_rule4_test_match_n_redirect "$match" "$getmatch" "fwmark redirect to table"
271
272	fib_check_iproute_support "uidrange" "uid"
273	if [ $? -eq 0 ]; then
274		match="uidrange 100-100"
275		getmatch="uid 100"
276		fib_rule4_test_match_n_redirect "$match" "$getmatch" "uid redirect to table"
277	fi
278
279	fib_check_iproute_support "sport" "sport"
280	if [ $? -eq 0 ]; then
281		match="sport 666 dport 777"
282		fib_rule4_test_match_n_redirect "$match" "$match" "sport and dport redirect to table"
283	fi
284
285	fib_check_iproute_support "ipproto" "ipproto"
286	if [ $? -eq 0 ]; then
287		match="ipproto tcp"
288		fib_rule4_test_match_n_redirect "$match" "$match" "ipproto tcp match"
289	fi
290
291	fib_check_iproute_support "ipproto" "ipproto"
292	if [ $? -eq 0 ]; then
293		match="ipproto icmp"
294		fib_rule4_test_match_n_redirect "$match" "$match" "ipproto icmp match"
295	fi
296}
297
298run_fibrule_tests()
299{
300	log_section "IPv4 fib rule"
301	fib_rule4_test
302	log_section "IPv6 fib rule"
303	fib_rule6_test
304}
305
306if [ "$(id -u)" -ne 0 ];then
307	echo "SKIP: Need root privileges"
308	exit $ksft_skip
309fi
310
311if [ ! -x "$(command -v ip)" ]; then
312	echo "SKIP: Could not run test without ip tool"
313	exit $ksft_skip
314fi
315
316# start clean
317cleanup &> /dev/null
318setup
319run_fibrule_tests
320cleanup
321
322if [ "$TESTS" != "none" ]; then
323	printf "\nTests passed: %3d\n" ${nsuccess}
324	printf "Tests failed: %3d\n"   ${nfail}
325fi
326
327exit $ret
328