xref: /linux/tools/testing/selftests/net/netfilter/nft_fib.sh (revision ab93e0dd72c37d378dd936f031ffb83ff2bd87ce)
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 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 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
441	[ -z "$count" ] && count=1
442
443	if ! ip netns exec "$nsrouter" nft get element inet t "$setname" { "$iifname" . "$addr" . "$type" } |grep -q "counter packets $count";then
444		echo "FAIL: did not find $iifname . $addr . $type in $setname"
445		ip netns exec "$nsrouter" nft list set inet t "$setname"
446		ret=1
447		return 1
448	fi
449
450	# delete the entry, this allows to check if anything unexpected appeared
451	# at the end of the test run: all dynamic sets should be empty by then.
452	if ! ip netns exec "$nsrouter" nft delete element inet t "$setname" { "$iifname" . "$addr" . "$type" } ; then
453		echo "FAIL: can't delete $iifname . $addr . $type in $setname"
454		ip netns exec "$nsrouter" nft list set inet t "$setname"
455		ret=1
456		return 1
457	fi
458
459	return 0
460}
461
462check_local()
463{
464	check_type $@ "local" 1
465}
466
467check_unicast()
468{
469	check_type $@ "unicast" 1
470}
471
472check_rpf()
473{
474	check_type $@
475}
476
477check_fib_vrf_sets_empty()
478{
479	local setname=""
480	local lret=0
481
482	# A non-empty set means that we have seen unexpected packets OR
483	# that a fib lookup provided unexpected results.
484	for setname in "fibif4" "fibif4iif" "fibif6" "fibif6iif" \
485		       "fibtype4" "fibtype4iif" "fibtype6" "fibtype6iif";do
486		if ip netns exec "$nsrouter" nft list set inet t "$setname" | grep -q elements;then
487			echo "FAIL: $setname not empty"
488	                ip netns exec "$nsrouter" nft list set inet t "$setname"
489			ret=1
490			lret=1
491		fi
492	done
493
494	return $lret
495}
496
497check_fib_vrf_type()
498{
499	local msg="$1"
500
501	local addr
502	# the incoming interface is always veth0.  As its not linked to a VRF,
503	# the 'tvrf' device should NOT show up anywhere.
504	local ifname="veth0"
505	local lret=0
506
507	# local_veth0, local_veth1
508	for addr in "10.0.1.1" "10.0.2.1"; do
509		check_local fibtype4  "$ifname" "$addr" || lret=1
510		check_type  fibif4    "$ifname" "$addr" "0" || lret=1
511	done
512	for addr in "dead:1::1" "dead:2::1";do
513		check_local fibtype6  "$ifname" "$addr" || lret=1
514		check_type  fibif6    "$ifname" "$addr" "0" || lret=1
515	done
516
517	# when restricted to the incoming interface, 10.0.1.1 should
518	# be 'local', but 10.0.2.1 unicast.
519	check_local fibtype4iif   "$ifname" "10.0.1.1" || lret=1
520	check_unicast fibtype4iif "$ifname" "10.0.2.1" || lret=1
521
522	# same for the ipv6 addresses.
523	check_local fibtype6iif   "$ifname" "dead:1::1" || lret=1
524	check_unicast fibtype6iif "$ifname" "dead:2::1" || lret=1
525
526	# None of these addresses should find a valid route when restricting
527	# to the incoming interface (we ask for daddr - 10.0.1.1/2.1 are
528	# reachable via 'lo'.
529	for addr in "10.0.1.1" "10.0.2.1" "10.9.9.1" "10.9.9.2";do
530		check_type fibif4iif "$ifname" "$addr" "0" || lret=1
531	done
532
533	# expect default route (veth1), dummy0 is part of VRF but iif isn't.
534	for addr in "10.9.9.1" "10.9.9.2";do
535		check_unicast fibtype4    "$ifname" "$addr" || lret=1
536		check_unicast fibtype4iif "$ifname" "$addr" || lret=1
537		check_type fibif4 "$ifname" "$addr" "veth1" || lret=1
538	done
539	for addr in "dead:9::1" "dead:9::2";do
540		check_unicast fibtype6    "$ifname" "$addr" || lret=1
541		check_unicast fibtype6iif "$ifname" "$addr" || lret=1
542		check_type fibif6 "$ifname" "$addr" "veth1" || lret=1
543	done
544
545	# same for the IPv6 equivalent addresses.
546	for addr in "dead:1::1" "dead:2::1" "dead:9::1" "dead:9::2";do
547		check_type  fibif6iif "$ifname" "$addr" "0" || lret=1
548	done
549
550	check_unicast fibtype4    "$ifname" "10.0.2.99" || lret=1
551	check_unicast fibtype4iif "$ifname" "10.0.2.99" || lret=1
552	check_unicast fibtype6    "$ifname" "dead:2::99" || lret=1
553	check_unicast fibtype6iif "$ifname" "dead:2::99" || lret=1
554
555	check_type fibif4 "$ifname" "10.0.2.99" "veth1" || lret=1
556	check_type fibif4iif "$ifname" "10.0.2.99" 0 || lret=1
557	check_type fibif6 "$ifname" "dead:2::99" "veth1" || lret=1
558	check_type fibif6iif "$ifname" "dead:2::99" 0 || lret=1
559
560	check_rpf  fibif4    "$ifname" "10.0.1.99" "veth0" 5 || lret=1
561	check_rpf  fibif4iif "$ifname" "10.0.1.99" "veth0" 5 || lret=1
562	check_rpf  fibif6    "$ifname" "dead:1::99" "veth0" 5 || lret=1
563	check_rpf  fibif6iif "$ifname" "dead:1::99" "veth0" 5 || lret=1
564
565	check_fib_vrf_sets_empty || lret=1
566
567	if [ $lret -eq 0 ];then
568		echo "PASS: $msg"
569	else
570		echo "FAIL: $msg"
571		ret=1
572	fi
573}
574
575check_fib_veth_vrf_type()
576{
577	local msg="$1"
578
579	local addr
580	local ifname
581	local setname
582	local lret=0
583
584	# as veth0 is now part of tvrf interface, packets will be seen
585	# twice, once with iif veth0, then with iif tvrf.
586
587	for ifname in "veth0" "tvrf"; do
588		for addr in "10.0.1.1" "10.9.9.1"; do
589			check_local fibtype4  "$ifname" "$addr" || lret=1
590			# addr local, but nft_fib doesn't return routes with RTN_LOCAL.
591			check_type  fibif4    "$ifname" "$addr" 0 || lret=1
592			check_type  fibif4iif "$ifname" "$addr" 0 || lret=1
593		done
594
595		for addr in "dead:1::1" "dead:9::1"; do
596			check_local fibtype6 "$ifname" "$addr" || lret=1
597			# same, address is local but no route is returned for lo.
598			check_type  fibif6    "$ifname" "$addr" 0 || lret=1
599			check_type  fibif6iif "$ifname" "$addr" 0 || lret=1
600		done
601
602		for t in fibtype4 fibtype4iif; do
603			check_unicast "$t" "$ifname" 10.9.9.2 || lret=1
604		done
605		for t in fibtype6 fibtype6iif; do
606			check_unicast "$t" "$ifname" dead:9::2 || lret=1
607		done
608
609		check_unicast fibtype4iif "$ifname" "10.9.9.1" || lret=1
610		check_unicast fibtype6iif "$ifname" "dead:9::1" || lret=1
611
612		check_unicast fibtype4    "$ifname" "10.0.2.99" || lret=1
613		check_unicast fibtype4iif "$ifname" "10.0.2.99" || lret=1
614
615		check_unicast fibtype6    "$ifname" "dead:2::99" || lret=1
616		check_unicast fibtype6iif "$ifname" "dead:2::99" || lret=1
617
618		check_type fibif4    "$ifname"  "10.0.2.99" "veth1" || lret=1
619		check_type fibif6    "$ifname" "dead:2::99" "veth1" || lret=1
620		check_type fibif4    "$ifname"   "10.9.9.2" "dummy0" || lret=1
621		check_type fibif6    "$ifname"  "dead:9::2" "dummy0" || lret=1
622
623		# restricted to iif -- MUST NOT provide result, its != $ifname.
624		check_type fibif4iif "$ifname"  "10.0.2.99" 0 || lret=1
625		check_type fibif6iif "$ifname" "dead:2::99" 0 || lret=1
626
627		check_rpf  fibif4 "$ifname" "10.0.1.99" "veth0" 4 || lret=1
628		check_rpf  fibif6 "$ifname" "dead:1::99" "veth0" 4 || lret=1
629		check_rpf  fibif4iif "$ifname" "10.0.1.99" "$ifname" 4 || lret=1
630		check_rpf  fibif6iif "$ifname" "dead:1::99" "$ifname" 4 || lret=1
631	done
632
633	check_local fibtype4iif "veth0" "10.0.1.1" || lret=1
634	check_local fibtype6iif "veth0" "dead:1::1" || lret=1
635
636	check_unicast fibtype4iif "tvrf" "10.0.1.1" || lret=1
637	check_unicast fibtype6iif "tvrf" "dead:1::1" || lret=1
638
639	# 10.9.9.2 should not provide a result for iif veth, but
640	# should when iif is tvrf.
641	# This is because its reachable via dummy0 which is part of
642	# tvrf.  iif veth0 MUST conceal the dummy0 result (i.e. return oif 0).
643	check_type fibif4iif "veth0" "10.9.9.2" 0 || lret=1
644	check_type fibif6iif "veth0"  "dead:9::2" 0 || lret=1
645
646	check_type fibif4iif "tvrf" "10.9.9.2" "tvrf" || lret=1
647	check_type fibif6iif "tvrf" "dead:9::2" "tvrf" || lret=1
648
649	check_fib_vrf_sets_empty || lret=1
650
651	if [ $lret -eq 0 ];then
652		echo "PASS: $msg"
653	else
654		echo "FAIL: $msg"
655		ret=1
656	fi
657}
658
659# Extends nsrouter config by adding dummy0+vrf.
660#
661#  10.0.1.99     10.0.1.1           10.0.2.1         10.0.2.99
662# dead:1::99    dead:1::1          dead:2::1        dead:2::99
663# ns1 <-------> [ veth0 ] nsrouter [veth1] <-------> ns2
664#                         [dummy0]
665#                         10.9.9.1
666#                        dead:9::1
667#                          [tvrf]
668test_fib_vrf()
669{
670	local cntname=""
671
672	if ! test_fib_vrf_dev_add_dummy; then
673		[ $ret -eq 0 ] && ret=$ksft_skip
674		return
675	fi
676
677	ip -net "$nsrouter" addr add "10.9.9.1"/24 dev dummy0
678	ip -net "$nsrouter" addr add "dead:9::1"/64 dev dummy0 nodad
679
680	ip -net "$nsrouter" route add default via 10.0.2.99
681	ip -net "$nsrouter" route add default via dead:2::99
682
683	load_ruleset_vrf || return
684
685	# no echo reply for these addresses: The dummy interface is part of tvrf,
686	# but veth0 (incoming interface) isn't linked to it.
687	test_ping_unreachable "10.9.9.1" "dead:9::1" &
688	test_ping_unreachable "10.9.9.2" "dead:9::2" &
689
690	# expect replies from these.
691	test_ping "10.0.1.1" "dead:1::1"
692	test_ping "10.0.2.1" "dead:2::1"
693	test_ping "10.0.2.99" "dead:2::99"
694
695	wait
696
697	check_fib_vrf_type "fib expression address types match (iif not in vrf)"
698
699	# second round: this time, make veth0 (rx interface) part of the vrf.
700	# 10.9.9.1 / dead:9::1 become reachable from ns1, while ns2
701	# becomes unreachable.
702	ip -net "$nsrouter" link set veth0 master tvrf
703	ip -net "$nsrouter" addr add dead:1::1/64 dev veth0 nodad
704
705	# this reload should not be needed, but in case
706	# there is some error (missing or unexpected entry) this will prevent them
707	# from leaking into round 2.
708	load_ruleset_vrf || return
709
710	test_ping "10.0.1.1" "dead:1::1"
711	test_ping "10.9.9.1" "dead:9::1"
712
713	# ns2 should no longer be reachable (veth1 not in vrf)
714	test_ping_unreachable "10.0.2.99" "dead:2::99" &
715
716	# vrf via dummy0, but host doesn't exist
717	test_ping_unreachable "10.9.9.2" "dead:9::2" &
718
719	wait
720
721	check_fib_veth_vrf_type "fib expression address types match (iif in vrf)"
722}
723
724ip netns exec "$nsrouter" sysctl net.ipv6.conf.all.forwarding=1 > /dev/null
725ip netns exec "$nsrouter" sysctl net.ipv4.conf.veth0.forwarding=1 > /dev/null
726ip netns exec "$nsrouter" sysctl net.ipv4.conf.veth1.forwarding=1 > /dev/null
727
728test_ping 10.0.2.1 dead:2::1 || exit 1
729check_drops
730
731test_ping 10.0.2.99 dead:2::99 || exit 1
732check_drops
733
734[ $ret -eq 0 ] && echo "PASS: fib expression did not cause unwanted packet drops"
735
736load_input_ruleset "$ns1"
737
738test_ping 127.0.0.1 ::1
739check_drops
740
741test_ping 10.0.1.99 dead:1::99
742check_drops
743
744[ $ret -eq 0 ] && echo "PASS: fib expression did not discard loopback packets"
745
746load_input_ruleset "$ns1"
747
748test_ping 127.0.0.1 ::1 || exit 1
749check_drops || exit 1
750
751test_ping 10.0.1.99 dead:1::99 || exit 1
752check_drops || exit 1
753
754echo "PASS: fib expression did not discard loopback packets"
755
756ip netns exec "$nsrouter" nft flush table inet filter
757
758ip -net "$ns1" route del default
759ip -net "$ns1" -6 route del default
760
761ip -net "$ns1" addr del 10.0.1.99/24 dev eth0
762ip -net "$ns1" addr del dead:1::99/64 dev eth0
763
764ip -net "$ns1" addr add 10.0.2.99/24 dev eth0
765ip -net "$ns1" addr add dead:2::99/64 dev eth0 nodad
766
767ip -net "$ns1" route add default via 10.0.2.1
768ip -net "$ns1" -6 route add default via dead:2::1
769
770ip -net "$nsrouter" addr add dead:2::1/64 dev veth0 nodad
771
772# switch to ruleset that doesn't log, this time
773# its expected that this does drop the packets.
774load_ruleset_count "$nsrouter"
775
776# ns1 has a default route, but nsrouter does not.
777# must not check return value, ping to 1.1.1.1 will
778# fail.
779check_fib_counter 0 "$nsrouter" 1.1.1.1 || exit 1
780check_fib_counter 0 "$nsrouter" 1c3::c01d || exit 1
781
782ip netns exec "$ns1" ping -W 0.5 -c 1 -q 1.1.1.1 > /dev/null
783check_fib_counter 1 "$nsrouter" 1.1.1.1 || exit 1
784
785ip netns exec "$ns1" ping -W 0.5 -i 0.1 -c 3 -q 1c3::c01d > /dev/null
786check_fib_counter 3 "$nsrouter" 1c3::c01d || exit 1
787
788# delete all rules
789ip netns exec "$ns1" nft flush ruleset
790ip netns exec "$ns2" nft flush ruleset
791ip netns exec "$nsrouter" nft flush ruleset
792
793ip -net "$ns1" addr add 10.0.1.99/24 dev eth0
794ip -net "$ns1" addr add dead:1::99/64 dev eth0 nodad
795
796ip -net "$ns1" addr del 10.0.2.99/24 dev eth0
797ip -net "$ns1" addr del dead:2::99/64 dev eth0
798
799ip -net "$nsrouter" addr del dead:2::1/64 dev veth0
800
801# ... pbr ruleset for the router, check iif+oif.
802if ! load_pbr_ruleset "$nsrouter";then
803	echo "SKIP: Could not load fib forward ruleset"
804	[ "$ret" -eq 0 ] && ret=$ksft_skip
805fi
806
807ip -net "$nsrouter" rule add from all table 128
808ip -net "$nsrouter" rule add from all iif veth0 table 129
809ip -net "$nsrouter" route add table 128 to 10.0.1.0/24 dev veth0
810ip -net "$nsrouter" route add table 129 to 10.0.2.0/24 dev veth1
811
812# drop main ipv4 table
813ip -net "$nsrouter" -4 rule delete table main
814
815if test_ping 10.0.2.99 dead:2::99;then
816	echo "PASS: fib expression forward check with policy based routing"
817else
818	echo "FAIL: fib expression forward check with policy based routing"
819	ret=1
820fi
821
822test_fib_type "policy routing"
823ip netns exec "$nsrouter" nft delete table ip filter
824ip netns exec "$nsrouter" nft delete table ip6 filter
825
826# Un-do policy routing changes
827ip -net "$nsrouter" rule del from all table 128
828ip -net "$nsrouter" rule del from all iif veth0 table 129
829
830ip -net "$nsrouter" route del table 128 to 10.0.1.0/24 dev veth0
831ip -net "$nsrouter" route del table 129 to 10.0.2.0/24 dev veth1
832
833ip -net "$ns1" -4 route del default
834ip -net "$ns1" -6 route del default
835
836ip -net "$ns1" -4 route add default via 10.0.1.1
837ip -net "$ns1" -6 route add default via dead:1::1
838
839ip -net "$nsrouter" -4 rule add from all table main priority 32766
840
841test_fib_type "default table"
842ip netns exec "$nsrouter" nft delete table ip filter
843ip netns exec "$nsrouter" nft delete table ip6 filter
844
845test_fib_vrf
846
847exit $ret
848