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