xref: /linux/tools/testing/selftests/net/netfilter/nft_fib.sh (revision 18a7e218cfcdca6666e1f7356533e4c988780b57)
1#!/bin/bash
2#
3# This tests the fib expression.
4#
5# Kselftest framework requirement - SKIP code is 4.
6#
7#  10.0.1.99     10.0.1.1           10.0.2.1         10.0.2.99
8# dead:1::99    dead:1::1          dead:2::1        dead:2::99
9# ns1 <-------> [ veth0 ] nsrouter [veth1] <-------> ns2
10
11source lib.sh
12
13ret=0
14
15timeout=4
16
17log_netns=$(sysctl -n net.netfilter.nf_log_all_netns)
18
19cleanup()
20{
21	cleanup_all_ns
22
23	[ "$log_netns" -eq 0 ] && sysctl -q net.netfilter.nf_log_all_netns=$log_netns
24}
25
26checktool "nft --version" "run test without nft"
27
28setup_ns nsrouter ns1 ns2
29
30trap cleanup EXIT
31
32if dmesg | grep -q ' nft_rpfilter: ';then
33	dmesg -c | grep ' nft_rpfilter: '
34	echo "WARN: a previous test run has failed" 1>&2
35fi
36
37sysctl -q net.netfilter.nf_log_all_netns=1
38
39load_ruleset() {
40	local netns=$1
41
42ip netns exec "$netns" nft -f /dev/stdin <<EOF
43table inet filter {
44	chain prerouting {
45		type filter hook prerouting priority 0; policy accept;
46	        fib saddr . iif oif missing counter log prefix "$netns nft_rpfilter: " drop
47	}
48}
49EOF
50}
51
52load_input_ruleset() {
53	local netns=$1
54
55ip netns exec "$netns" nft -f /dev/stdin <<EOF
56table inet filter {
57	chain input {
58		type filter hook input priority 0; policy accept;
59	        fib saddr . iif oif missing counter log prefix "$netns nft_rpfilter: " drop
60	}
61}
62EOF
63}
64
65load_pbr_ruleset() {
66	local netns=$1
67
68ip netns exec "$netns" nft -f /dev/stdin <<EOF
69table inet filter {
70	chain forward {
71		type filter hook forward priority raw;
72		fib saddr . iif oif gt 0 accept
73		log drop
74	}
75}
76EOF
77}
78
79load_type_ruleset() {
80	local netns=$1
81
82	for family in ip ip6;do
83ip netns exec "$netns" nft -f /dev/stdin <<EOF
84table $family filter {
85	chain type_match_in {
86		fib daddr type local counter comment "daddr configured on other iface"
87		fib daddr . iif type local counter comment "daddr configured on iif"
88		fib daddr type unicast counter comment "daddr not local"
89		fib daddr . iif type unicast counter comment "daddr not configured on iif"
90	}
91
92	chain type_match_out {
93		fib daddr type unicast counter
94		fib daddr . oif type unicast counter
95		fib daddr type local counter
96		fib daddr . oif type local counter
97	}
98
99	chain prerouting {
100		type filter hook prerouting priority 0;
101		icmp type echo-request counter jump type_match_in
102		icmpv6 type echo-request counter jump type_match_in
103	}
104
105	chain input {
106		type filter hook input priority 0;
107		icmp type echo-request counter jump type_match_in
108		icmpv6 type echo-request counter jump type_match_in
109	}
110
111	chain forward {
112		type filter hook forward priority 0;
113		icmp type echo-request counter jump type_match_in
114		icmpv6 type echo-request counter jump type_match_in
115	}
116
117	chain output {
118		type filter hook output priority 0;
119		icmp type echo-request counter jump type_match_out
120		icmpv6 type echo-request counter jump type_match_out
121	}
122
123	chain postrouting {
124		type filter hook postrouting priority 0;
125		icmp type echo-request counter jump type_match_out
126		icmpv6 type echo-request counter jump type_match_out
127	}
128}
129EOF
130done
131}
132
133reload_type_ruleset() {
134	ip netns exec "$1" nft flush table ip filter
135	ip netns exec "$1" nft flush table ip6 filter
136	load_type_ruleset "$1"
137}
138
139check_fib_type_counter_family() {
140	local family="$1"
141	local want="$2"
142	local ns="$3"
143	local chain="$4"
144	local what="$5"
145	local errmsg="$6"
146
147	if ! ip netns exec "$ns" nft list chain "$family" filter "$chain" | grep "$what" | grep -q "packets $want";then
148		echo "Netns $ns $family fib type counter doesn't match expected packet count of $want for $what $errmsg" 1>&2
149		ip netns exec "$ns" nft list chain "$family" filter "$chain"
150		ret=1
151		return 1
152	fi
153
154	return 0
155}
156
157check_fib_type_counter() {
158	check_fib_type_counter_family "ip" "$@" || return 1
159	check_fib_type_counter_family "ip6" "$@" || return 1
160}
161
162load_ruleset_count() {
163	local netns=$1
164
165ip netns exec "$netns" nft -f /dev/stdin <<EOF
166table inet filter {
167	chain prerouting {
168		type filter hook prerouting priority 0; policy accept;
169		ip daddr 1.1.1.1 fib saddr . iif oif missing counter drop
170		ip6 daddr 1c3::c01d fib saddr . iif oif missing counter drop
171	}
172}
173EOF
174}
175
176check_drops() {
177	if dmesg | grep -q ' nft_rpfilter: ';then
178		dmesg | grep ' nft_rpfilter: '
179		echo "FAIL: rpfilter did drop packets"
180		ret=1
181		return 1
182	fi
183
184	return 0
185}
186
187check_fib_counter() {
188	local want=$1
189	local ns=$2
190	local address=$3
191
192	if ! ip netns exec "$ns" nft list table inet filter | grep 'fib saddr . iif' | grep "$address" | grep -q "packets $want";then
193		echo "Netns $ns fib counter doesn't match expected packet count of $want for $address" 1>&2
194		ip netns exec "$ns" nft list table inet filter
195		return 1
196	fi
197
198	if [ "$want" -gt 0 ]; then
199		echo "PASS: fib expression did drop packets for $address"
200	fi
201
202	return 0
203}
204
205load_ruleset "$nsrouter"
206load_ruleset "$ns1"
207load_ruleset "$ns2"
208
209if ! ip link add veth0 netns "$nsrouter" type veth peer name eth0 netns "$ns1" > /dev/null 2>&1; then
210    echo "SKIP: No virtual ethernet pair device support in kernel"
211    exit $ksft_skip
212fi
213ip link add veth1 netns "$nsrouter" type veth peer name eth0 netns "$ns2"
214
215ip -net "$nsrouter" link set veth0 up
216ip -net "$nsrouter" addr add 10.0.1.1/24 dev veth0
217ip -net "$nsrouter" addr add dead:1::1/64 dev veth0 nodad
218
219ip -net "$nsrouter" link set veth1 up
220ip -net "$nsrouter" addr add 10.0.2.1/24 dev veth1
221ip -net "$nsrouter" addr add dead:2::1/64 dev veth1 nodad
222
223ip -net "$ns1" link set eth0 up
224ip -net "$ns2" link set eth0 up
225
226ip -net "$ns1" addr add 10.0.1.99/24 dev eth0
227ip -net "$ns1" addr add dead:1::99/64 dev eth0 nodad
228ip -net "$ns1" route add default via 10.0.1.1
229ip -net "$ns1" route add default via dead:1::1
230
231ip -net "$ns2" addr add 10.0.2.99/24 dev eth0
232ip -net "$ns2" addr add dead:2::99/64 dev eth0 nodad
233ip -net "$ns2" route add default via 10.0.2.1
234ip -net "$ns2" route add default via dead:2::1
235
236test_ping() {
237  local daddr4=$1
238  local daddr6=$2
239
240  if ! ip netns exec "$ns1" ping -c 1 -q "$daddr4" > /dev/null; then
241	check_drops
242	echo "FAIL: ${ns1} cannot reach $daddr4, ret $ret" 1>&2
243	return 1
244  fi
245
246  if ! ip netns exec "$ns1" ping -c 1 -q "$daddr6" > /dev/null; then
247	check_drops
248	echo "FAIL: ${ns1} cannot reach $daddr6, ret $ret" 1>&2
249	return 1
250  fi
251
252  return 0
253}
254
255test_ping_unreachable() {
256  local daddr4=$1
257  local daddr6=$2
258
259  if ip netns exec "$ns1" ping -c 1 -W 0.1 -q "$daddr4" > /dev/null; then
260	echo "FAIL: ${ns1} could reach $daddr4" 1>&2
261	return 1
262  fi
263
264  if ip netns exec "$ns1" ping -c 1 -W 0.1 -q "$daddr6" > /dev/null; then
265	echo "FAIL: ${ns1} could reach $daddr6" 1>&2
266	return 1
267  fi
268
269  return 0
270}
271
272test_fib_type() {
273	local notice="$1"
274	local errmsg="addr-on-if"
275	local lret=0
276
277	if ! load_type_ruleset "$nsrouter";then
278		echo "SKIP: Could not load fib type ruleset"
279		[ $ret -eq 0 ] && ret=$ksft_skip
280		return
281	fi
282
283	# makes router receive packet for addresses configured on incoming
284	# interface.
285	test_ping 10.0.1.1 dead:1::1 || return 1
286
287	# expectation: triggers all 'local' in prerouting/input.
288	check_fib_type_counter 2 "$nsrouter" "type_match_in" "fib daddr type local" "$errmsg" || lret=1
289	check_fib_type_counter 2 "$nsrouter" "type_match_in" "fib daddr . iif type local" "$errmsg" || lret=1
290
291	reload_type_ruleset "$nsrouter"
292	# makes router receive packet for address configured on a different (but local)
293	# interface.
294	test_ping 10.0.2.1 dead:2::1 || return 1
295
296	# expectation: triggers 'unicast' in prerouting/input for daddr . iif and local for 'daddr'.
297	errmsg="addr-on-host"
298	check_fib_type_counter 2 "$nsrouter" "type_match_in" "fib daddr type local" "$errmsg" || lret=1
299	check_fib_type_counter 2 "$nsrouter" "type_match_in" "fib daddr . iif type unicast" "$errmsg" || lret=1
300
301	reload_type_ruleset "$nsrouter"
302	test_ping 10.0.2.99 dead:2::99 || return 1
303	errmsg="addr-on-otherhost"
304	check_fib_type_counter 2 "$nsrouter" "type_match_in" "fib daddr type unicast" "$errmsg" || lret=1
305	check_fib_type_counter 2 "$nsrouter" "type_match_in" "fib daddr . iif type unicast" "$errmsg" || lret=1
306
307	if [ $lret -eq 0 ];then
308		echo "PASS: fib expression address types match ($notice)"
309	else
310		echo "FAIL: fib expression address types match ($notice)"
311		ret=1
312	fi
313}
314
315test_fib_vrf_dev_add_dummy()
316{
317	if ! ip -net "$nsrouter" link add dummy0 type dummy ;then
318		echo "SKIP: VRF tests: dummy device type not supported"
319		return 1
320	fi
321
322	if ! ip -net "$nsrouter" link add tvrf type vrf table 9876;then
323		echo "SKIP: VRF tests: vrf device type not supported"
324		return 1
325	fi
326
327	ip -net "$nsrouter" link set dummy0 master tvrf
328	ip -net "$nsrouter" link set dummy0 up
329	ip -net "$nsrouter" link set tvrf up
330}
331
332load_ruleset_vrf()
333{
334# Due to the many different possible combinations using named counters
335# or one-rule-per-expected-result is complex.
336#
337# Instead, add dynamic sets for the fib modes
338# (fib address type, fib output interface lookup .. ),
339# and then add the obtained fib results to them.
340#
341# The test is successful if the sets contain the expected results
342# and no unexpected extra entries existed.
343ip netns exec "$nsrouter" nft -f - <<EOF
344flush ruleset
345table inet t {
346	set fibif4 {
347		typeof meta iif . ip daddr . fib daddr oif
348		flags dynamic
349		counter
350	}
351
352	set fibif4iif {
353		typeof meta iif . ip daddr . fib daddr . iif oif
354		flags dynamic
355		counter
356	}
357
358	set fibif6 {
359		typeof meta iif . ip6 daddr . fib daddr oif
360		flags dynamic
361		counter
362	}
363
364	set fibif6iif {
365		typeof meta iif . ip6 daddr . fib daddr . iif oif
366		flags dynamic
367		counter
368	}
369
370	set fibtype4 {
371		typeof meta iif . ip daddr . fib daddr type
372		flags dynamic
373		counter
374	}
375
376	set fibtype4iif {
377		typeof meta iif . ip daddr . fib daddr . iif type
378		flags dynamic
379		counter
380	}
381
382	set fibtype6 {
383		typeof meta iif . ip6 daddr . fib daddr type
384		flags dynamic
385		counter
386	}
387
388	set fibtype6iif {
389		typeof meta iif . ip6 daddr . fib daddr . iif type
390		flags dynamic
391		counter
392	}
393
394	chain fib_test {
395		meta nfproto ipv4 jump {
396			add @fibif4 { meta iif . ip daddr . fib daddr oif }
397			add @fibif4iif { meta iif . ip daddr . fib daddr . iif oif }
398			add @fibtype4 { meta iif . ip daddr . fib daddr type }
399			add @fibtype4iif { meta iif . ip daddr . fib daddr . iif type }
400
401			add @fibif4 { meta iif . ip saddr . fib saddr oif }
402			add @fibif4iif { meta iif . ip saddr . fib saddr . iif oif }
403		}
404
405		meta nfproto ipv6 jump {
406			add @fibif6    { meta iif . ip6 daddr . fib daddr oif }
407			add @fibif6iif { meta iif . ip6 daddr . fib daddr . iif oif }
408			add @fibtype6    { meta iif . ip6 daddr . fib daddr type }
409			add @fibtype6iif { meta iif . ip6 daddr . fib daddr . iif type }
410
411			add @fibif6 { meta iif . ip6 saddr . fib saddr oif }
412			add @fibif6iif { meta iif . ip6 saddr . fib saddr . iif oif }
413		}
414	}
415
416	chain prerouting {
417		type filter hook prerouting priority 0;
418		icmp type echo-request counter jump fib_test
419
420		# neighbour discovery to be ignored.
421		icmpv6 type echo-request counter jump fib_test
422	}
423}
424EOF
425
426if [ $? -ne 0 ] ;then
427	echo "SKIP: Could not load ruleset for fib vrf test"
428	[ $ret -eq 0 ] && ret=$ksft_skip
429	return 1
430fi
431}
432
433check_type()
434{
435	local setname="$1"
436	local iifname="$2"
437	local addr="$3"
438	local type="$4"
439	local count="$5"
440	local lret=0
441
442	[ -z "$count" ] && count=1
443
444	if ! ip netns exec "$nsrouter" nft get element inet t "$setname" { "$iifname" . "$addr" . "$type" } |grep -q "counter packets $count";then
445		echo "FAIL: did not find $iifname . $addr . $type in $setname with $count packets"
446		ip netns exec "$nsrouter" nft list set inet t "$setname"
447		ret=1
448		# do not fail right away, delete entry if it exists so later test that
449		# checks for unwanted keys don't get confused by this *expected* key.
450		lret=1
451	fi
452
453	# delete the entry, this allows to check if anything unexpected appeared
454	# at the end of the test run: all dynamic sets should be empty by then.
455	if ! ip netns exec "$nsrouter" nft delete element inet t "$setname" { "$iifname" . "$addr" . "$type" } ; then
456		echo "FAIL: can't delete $iifname . $addr . $type in $setname"
457		ip netns exec "$nsrouter" nft list set inet t "$setname"
458		ret=1
459		return 1
460	fi
461
462	return $lret
463}
464
465check_local()
466{
467	check_type $@ "local" 1
468}
469
470check_unicast()
471{
472	check_type $@ "unicast" 1
473}
474
475check_rpf()
476{
477	check_type $@
478}
479
480check_fib_vrf_sets_empty()
481{
482	local setname=""
483	local lret=0
484
485	# A non-empty set means that we have seen unexpected packets OR
486	# that a fib lookup provided unexpected results.
487	for setname in "fibif4" "fibif4iif" "fibif6" "fibif6iif" \
488		       "fibtype4" "fibtype4iif" "fibtype6" "fibtype6iif";do
489		if ip netns exec "$nsrouter" nft list set inet t "$setname" | grep -q elements;then
490			echo "FAIL: $setname not empty"
491	                ip netns exec "$nsrouter" nft list set inet t "$setname"
492			ret=1
493			lret=1
494		fi
495	done
496
497	return $lret
498}
499
500check_fib_vrf_type()
501{
502	local msg="$1"
503
504	local addr
505	# the incoming interface is always veth0.  As its not linked to a VRF,
506	# the 'tvrf' device should NOT show up anywhere.
507	local ifname="veth0"
508	local lret=0
509
510	# local_veth0, local_veth1
511	for addr in "10.0.1.1" "10.0.2.1"; do
512		check_local fibtype4  "$ifname" "$addr" || lret=1
513		check_type  fibif4    "$ifname" "$addr" "0" || lret=1
514	done
515	for addr in "dead:1::1" "dead:2::1";do
516		check_local fibtype6  "$ifname" "$addr" || lret=1
517		check_type  fibif6    "$ifname" "$addr" "0" || lret=1
518	done
519
520	# when restricted to the incoming interface, 10.0.1.1 should
521	# be 'local', but 10.0.2.1 unicast.
522	check_local fibtype4iif   "$ifname" "10.0.1.1" || lret=1
523	check_unicast fibtype4iif "$ifname" "10.0.2.1" || lret=1
524
525	# same for the ipv6 addresses.
526	check_local fibtype6iif   "$ifname" "dead:1::1" || lret=1
527	check_unicast fibtype6iif "$ifname" "dead:2::1" || lret=1
528
529	# None of these addresses should find a valid route when restricting
530	# to the incoming interface (we ask for daddr - 10.0.1.1/2.1 are
531	# reachable via 'lo'.
532	for addr in "10.0.1.1" "10.0.2.1" "10.9.9.1" "10.9.9.2";do
533		check_type fibif4iif "$ifname" "$addr" "0" || lret=1
534	done
535
536	# expect default route (veth1), dummy0 is part of VRF but iif isn't.
537	for addr in "10.9.9.1" "10.9.9.2";do
538		check_unicast fibtype4    "$ifname" "$addr" || lret=1
539		check_unicast fibtype4iif "$ifname" "$addr" || lret=1
540		check_type fibif4 "$ifname" "$addr" "veth1" || lret=1
541	done
542	for addr in "dead:9::1" "dead:9::2";do
543		check_unicast fibtype6    "$ifname" "$addr" || lret=1
544		check_unicast fibtype6iif "$ifname" "$addr" || lret=1
545		check_type fibif6 "$ifname" "$addr" "veth1" || lret=1
546	done
547
548	# same for the IPv6 equivalent addresses.
549	for addr in "dead:1::1" "dead:2::1" "dead:9::1" "dead:9::2";do
550		check_type  fibif6iif "$ifname" "$addr" "0" || lret=1
551	done
552
553	check_unicast fibtype4    "$ifname" "10.0.2.99" || lret=1
554	check_unicast fibtype4iif "$ifname" "10.0.2.99" || lret=1
555	check_unicast fibtype6    "$ifname" "dead:2::99" || lret=1
556	check_unicast fibtype6iif "$ifname" "dead:2::99" || lret=1
557
558	check_type fibif4 "$ifname" "10.0.2.99" "veth1" || lret=1
559	check_type fibif4iif "$ifname" "10.0.2.99" 0 || lret=1
560	check_type fibif6 "$ifname" "dead:2::99" "veth1" || lret=1
561	check_type fibif6iif "$ifname" "dead:2::99" 0 || lret=1
562
563	check_rpf  fibif4    "$ifname" "10.0.1.99" "veth0" 5 || lret=1
564	check_rpf  fibif4iif "$ifname" "10.0.1.99" "veth0" 5 || lret=1
565	check_rpf  fibif6    "$ifname" "dead:1::99" "veth0" 5 || lret=1
566	check_rpf  fibif6iif "$ifname" "dead:1::99" "veth0" 5 || lret=1
567
568	check_fib_vrf_sets_empty || lret=1
569
570	if [ $lret -eq 0 ];then
571		echo "PASS: $msg"
572	else
573		echo "FAIL: $msg"
574		ret=1
575	fi
576}
577
578check_fib_veth_vrf_type()
579{
580	local msg="$1"
581
582	local addr
583	local ifname
584	local setname
585	local lret=0
586
587	# as veth0 is now part of tvrf interface, packets will be seen
588	# twice, once with iif veth0, then with iif tvrf.
589
590	for ifname in "veth0" "tvrf"; do
591		for addr in "10.0.1.1" "10.9.9.1"; do
592			check_local fibtype4  "$ifname" "$addr" || lret=1
593			# addr local, but nft_fib doesn't return routes with RTN_LOCAL.
594			check_type  fibif4    "$ifname" "$addr" 0 || lret=1
595			check_type  fibif4iif "$ifname" "$addr" 0 || lret=1
596		done
597
598		for addr in "dead:1::1" "dead:9::1"; do
599			check_local fibtype6 "$ifname" "$addr" || lret=1
600			# same, address is local but no route is returned for lo.
601			check_type  fibif6    "$ifname" "$addr" 0 || lret=1
602			check_type  fibif6iif "$ifname" "$addr" 0 || lret=1
603		done
604
605		for t in fibtype4 fibtype4iif; do
606			check_unicast "$t" "$ifname" 10.9.9.2 || lret=1
607		done
608		for t in fibtype6 fibtype6iif; do
609			check_unicast "$t" "$ifname" dead:9::2 || lret=1
610		done
611
612		check_unicast fibtype4iif "$ifname" "10.9.9.1" || lret=1
613		check_unicast fibtype6iif "$ifname" "dead:9::1" || lret=1
614
615		check_unicast fibtype4    "$ifname" "10.0.2.99" || lret=1
616		check_unicast fibtype4iif "$ifname" "10.0.2.99" || lret=1
617
618		check_unicast fibtype6    "$ifname" "dead:2::99" || lret=1
619		check_unicast fibtype6iif "$ifname" "dead:2::99" || lret=1
620
621		check_type fibif4    "$ifname"  "10.0.2.99" "veth1" || lret=1
622		check_type fibif6    "$ifname" "dead:2::99" "veth1" || lret=1
623		check_type fibif4    "$ifname"   "10.9.9.2" "dummy0" || lret=1
624		check_type fibif6    "$ifname"  "dead:9::2" "dummy0" || lret=1
625
626		# restricted to iif -- MUST NOT provide result, its != $ifname.
627		check_type fibif4iif "$ifname"  "10.0.2.99" 0 || lret=1
628		check_type fibif6iif "$ifname" "dead:2::99" 0 || lret=1
629
630		check_rpf  fibif4 "$ifname" "10.0.1.99" "veth0" 4 || lret=1
631		check_rpf  fibif6 "$ifname" "dead:1::99" "veth0" 4 || lret=1
632		check_rpf  fibif4iif "$ifname" "10.0.1.99" "$ifname" 4 || lret=1
633		check_rpf  fibif6iif "$ifname" "dead:1::99" "$ifname" 4 || lret=1
634	done
635
636	check_local fibtype4iif "veth0" "10.0.1.1" || lret=1
637	check_local fibtype6iif "veth0" "dead:1::1" || lret=1
638
639	check_unicast fibtype4iif "tvrf" "10.0.1.1" || lret=1
640	check_unicast fibtype6iif "tvrf" "dead:1::1" || lret=1
641
642	# 10.9.9.2 should not provide a result for iif veth, but
643	# should when iif is tvrf.
644	# This is because its reachable via dummy0 which is part of
645	# tvrf.  iif veth0 MUST conceal the dummy0 result (i.e. return oif 0).
646	check_type fibif4iif "veth0" "10.9.9.2" 0 || lret=1
647	check_type fibif6iif "veth0"  "dead:9::2" 0 || lret=1
648
649	check_type fibif4iif "tvrf" "10.9.9.2" "tvrf" || lret=1
650	check_type fibif6iif "tvrf" "dead:9::2" "tvrf" || lret=1
651
652	check_fib_vrf_sets_empty || lret=1
653
654	if [ $lret -eq 0 ];then
655		echo "PASS: $msg"
656	else
657		echo "FAIL: $msg"
658		ret=1
659	fi
660}
661
662# Extends nsrouter config by adding dummy0+vrf.
663#
664#  10.0.1.99     10.0.1.1           10.0.2.1         10.0.2.99
665# dead:1::99    dead:1::1          dead:2::1        dead:2::99
666# ns1 <-------> [ veth0 ] nsrouter [veth1] <-------> ns2
667#                         [dummy0]
668#                         10.9.9.1
669#                        dead:9::1
670#                          [tvrf]
671test_fib_vrf()
672{
673	local cntname=""
674
675	if ! test_fib_vrf_dev_add_dummy; then
676		[ $ret -eq 0 ] && ret=$ksft_skip
677		return
678	fi
679
680	ip -net "$nsrouter" addr add "10.9.9.1"/24 dev dummy0
681	ip -net "$nsrouter" addr add "dead:9::1"/64 dev dummy0 nodad
682
683	ip -net "$nsrouter" route add default via 10.0.2.99
684	ip -net "$nsrouter" route add default via dead:2::99
685
686	load_ruleset_vrf || return
687
688	# no echo reply for these addresses: The dummy interface is part of tvrf,
689	# but veth0 (incoming interface) isn't linked to it.
690	test_ping_unreachable "10.9.9.1" "dead:9::1" &
691	test_ping_unreachable "10.9.9.2" "dead:9::2" &
692
693	# expect replies from these.
694	test_ping "10.0.1.1" "dead:1::1"
695	test_ping "10.0.2.1" "dead:2::1"
696	test_ping "10.0.2.99" "dead:2::99"
697
698	wait
699
700	check_fib_vrf_type "fib expression address types match (iif not in vrf)"
701
702	# second round: this time, make veth0 (rx interface) part of the vrf.
703	# 10.9.9.1 / dead:9::1 become reachable from ns1, while ns2
704	# becomes unreachable.
705	ip -net "$nsrouter" link set veth0 master tvrf
706	ip -net "$nsrouter" addr add dead:1::1/64 dev veth0 nodad
707
708	# this reload should not be needed, but in case
709	# there is some error (missing or unexpected entry) this will prevent them
710	# from leaking into round 2.
711	load_ruleset_vrf || return
712
713	test_ping "10.0.1.1" "dead:1::1"
714	test_ping "10.9.9.1" "dead:9::1"
715
716	# ns2 should no longer be reachable (veth1 not in vrf)
717	test_ping_unreachable "10.0.2.99" "dead:2::99" &
718
719	# vrf via dummy0, but host doesn't exist
720	test_ping_unreachable "10.9.9.2" "dead:9::2" &
721
722	wait
723
724	check_fib_veth_vrf_type "fib expression address types match (iif in vrf)"
725}
726
727ip netns exec "$nsrouter" sysctl net.ipv6.conf.all.forwarding=1 > /dev/null
728ip netns exec "$nsrouter" sysctl net.ipv4.conf.veth0.forwarding=1 > /dev/null
729ip netns exec "$nsrouter" sysctl net.ipv4.conf.veth1.forwarding=1 > /dev/null
730
731test_ping 10.0.2.1 dead:2::1 || exit 1
732check_drops
733
734test_ping 10.0.2.99 dead:2::99 || exit 1
735check_drops
736
737[ $ret -eq 0 ] && echo "PASS: fib expression did not cause unwanted packet drops"
738
739load_input_ruleset "$ns1"
740
741test_ping 127.0.0.1 ::1
742check_drops
743
744test_ping 10.0.1.99 dead:1::99
745check_drops
746
747[ $ret -eq 0 ] && echo "PASS: fib expression did not discard loopback packets"
748
749load_input_ruleset "$ns1"
750
751test_ping 127.0.0.1 ::1 || exit 1
752check_drops || exit 1
753
754test_ping 10.0.1.99 dead:1::99 || exit 1
755check_drops || exit 1
756
757echo "PASS: fib expression did not discard loopback packets"
758
759ip netns exec "$nsrouter" nft flush table inet filter
760
761ip -net "$ns1" route del default
762ip -net "$ns1" -6 route del default
763
764ip -net "$ns1" addr del 10.0.1.99/24 dev eth0
765ip -net "$ns1" addr del dead:1::99/64 dev eth0
766
767ip -net "$ns1" addr add 10.0.2.99/24 dev eth0
768ip -net "$ns1" addr add dead:2::99/64 dev eth0 nodad
769
770ip -net "$ns1" route add default via 10.0.2.1
771ip -net "$ns1" -6 route add default via dead:2::1
772
773ip -net "$nsrouter" addr add dead:2::1/64 dev veth0 nodad
774
775# switch to ruleset that doesn't log, this time
776# its expected that this does drop the packets.
777load_ruleset_count "$nsrouter"
778
779# ns1 has a default route, but nsrouter does not.
780# must not check return value, ping to 1.1.1.1 will
781# fail.
782check_fib_counter 0 "$nsrouter" 1.1.1.1 || exit 1
783check_fib_counter 0 "$nsrouter" 1c3::c01d || exit 1
784
785ip netns exec "$ns1" ping -W 0.5 -c 1 -q 1.1.1.1 > /dev/null
786check_fib_counter 1 "$nsrouter" 1.1.1.1 || exit 1
787
788ip netns exec "$ns1" ping -W 0.5 -i 0.1 -c 3 -q 1c3::c01d > /dev/null
789check_fib_counter 3 "$nsrouter" 1c3::c01d || exit 1
790
791# delete all rules
792ip netns exec "$ns1" nft flush ruleset
793ip netns exec "$ns2" nft flush ruleset
794ip netns exec "$nsrouter" nft flush ruleset
795
796ip -net "$ns1" addr add 10.0.1.99/24 dev eth0
797ip -net "$ns1" addr add dead:1::99/64 dev eth0 nodad
798
799ip -net "$ns1" addr del 10.0.2.99/24 dev eth0
800ip -net "$ns1" addr del dead:2::99/64 dev eth0
801
802ip -net "$nsrouter" addr del dead:2::1/64 dev veth0
803
804# ... pbr ruleset for the router, check iif+oif.
805if ! load_pbr_ruleset "$nsrouter";then
806	echo "SKIP: Could not load fib forward ruleset"
807	[ "$ret" -eq 0 ] && ret=$ksft_skip
808fi
809
810ip -net "$nsrouter" rule add from all table 128
811ip -net "$nsrouter" rule add from all iif veth0 table 129
812ip -net "$nsrouter" route add table 128 to 10.0.1.0/24 dev veth0
813ip -net "$nsrouter" route add table 129 to 10.0.2.0/24 dev veth1
814
815# drop main ipv4 table
816ip -net "$nsrouter" -4 rule delete table main
817
818if test_ping 10.0.2.99 dead:2::99;then
819	echo "PASS: fib expression forward check with policy based routing"
820else
821	echo "FAIL: fib expression forward check with policy based routing"
822	ret=1
823fi
824
825test_fib_type "policy routing"
826ip netns exec "$nsrouter" nft delete table ip filter
827ip netns exec "$nsrouter" nft delete table ip6 filter
828
829# Un-do policy routing changes
830ip -net "$nsrouter" rule del from all table 128
831ip -net "$nsrouter" rule del from all iif veth0 table 129
832
833ip -net "$nsrouter" route del table 128 to 10.0.1.0/24 dev veth0
834ip -net "$nsrouter" route del table 129 to 10.0.2.0/24 dev veth1
835
836ip -net "$ns1" -4 route del default
837ip -net "$ns1" -6 route del default
838
839ip -net "$ns1" -4 route add default via 10.0.1.1
840ip -net "$ns1" -6 route add default via dead:1::1
841
842ip -net "$nsrouter" -4 rule add from all table main priority 32766
843
844test_fib_type "default table"
845ip netns exec "$nsrouter" nft delete table ip filter
846ip netns exec "$nsrouter" nft delete table ip6 filter
847
848test_fib_vrf
849
850exit $ret
851