1#!/bin/bash
2#
3# This tests the fib expression.
4#
5# Kselftest framework requirement - SKIP code is 4.
6
7source lib.sh
8
9ret=0
10
11timeout=4
12
13log_netns=$(sysctl -n net.netfilter.nf_log_all_netns)
14
15cleanup()
16{
17	cleanup_all_ns
18
19	[ "$log_netns" -eq 0 ] && sysctl -q net.netfilter.nf_log_all_netns=$log_netns
20}
21
22checktool "nft --version" "run test without nft"
23
24setup_ns nsrouter ns1 ns2
25
26trap cleanup EXIT
27
28if dmesg | grep -q ' nft_rpfilter: ';then
29	dmesg -c | grep ' nft_rpfilter: '
30	echo "WARN: a previous test run has failed" 1>&2
31fi
32
33sysctl -q net.netfilter.nf_log_all_netns=1
34
35load_ruleset() {
36	local netns=$1
37
38ip netns exec "$netns" nft -f /dev/stdin <<EOF
39table inet filter {
40	chain prerouting {
41		type filter hook prerouting priority 0; policy accept;
42	        fib saddr . iif oif missing counter log prefix "$netns nft_rpfilter: " drop
43	}
44}
45EOF
46}
47
48load_pbr_ruleset() {
49	local netns=$1
50
51ip netns exec "$netns" nft -f /dev/stdin <<EOF
52table inet filter {
53	chain forward {
54		type filter hook forward priority raw;
55		fib saddr . iif oif gt 0 accept
56		log drop
57	}
58}
59EOF
60}
61
62load_ruleset_count() {
63	local netns=$1
64
65ip netns exec "$netns" nft -f /dev/stdin <<EOF
66table inet filter {
67	chain prerouting {
68		type filter hook prerouting priority 0; policy accept;
69		ip daddr 1.1.1.1 fib saddr . iif oif missing counter drop
70		ip6 daddr 1c3::c01d fib saddr . iif oif missing counter drop
71	}
72}
73EOF
74}
75
76check_drops() {
77	if dmesg | grep -q ' nft_rpfilter: ';then
78		dmesg | grep ' nft_rpfilter: '
79		echo "FAIL: rpfilter did drop packets"
80		return 1
81	fi
82
83	return 0
84}
85
86check_fib_counter() {
87	local want=$1
88	local ns=$2
89	local address=$3
90
91	if ! ip netns exec "$ns" nft list table inet filter | grep 'fib saddr . iif' | grep "$address" | grep -q "packets $want";then
92		echo "Netns $ns fib counter doesn't match expected packet count of $want for $address" 1>&2
93		ip netns exec "$ns" nft list table inet filter
94		return 1
95	fi
96
97	if [ "$want" -gt 0 ]; then
98		echo "PASS: fib expression did drop packets for $address"
99	fi
100
101	return 0
102}
103
104load_ruleset "$nsrouter"
105load_ruleset "$ns1"
106load_ruleset "$ns2"
107
108if ! ip link add veth0 netns "$nsrouter" type veth peer name eth0 netns "$ns1" > /dev/null 2>&1; then
109    echo "SKIP: No virtual ethernet pair device support in kernel"
110    exit $ksft_skip
111fi
112ip link add veth1 netns "$nsrouter" type veth peer name eth0 netns "$ns2"
113
114ip -net "$nsrouter" link set veth0 up
115ip -net "$nsrouter" addr add 10.0.1.1/24 dev veth0
116ip -net "$nsrouter" addr add dead:1::1/64 dev veth0 nodad
117
118ip -net "$nsrouter" link set veth1 up
119ip -net "$nsrouter" addr add 10.0.2.1/24 dev veth1
120ip -net "$nsrouter" addr add dead:2::1/64 dev veth1 nodad
121
122ip -net "$ns1" link set eth0 up
123ip -net "$ns2" link set eth0 up
124
125ip -net "$ns1" addr add 10.0.1.99/24 dev eth0
126ip -net "$ns1" addr add dead:1::99/64 dev eth0 nodad
127ip -net "$ns1" route add default via 10.0.1.1
128ip -net "$ns1" route add default via dead:1::1
129
130ip -net "$ns2" addr add 10.0.2.99/24 dev eth0
131ip -net "$ns2" addr add dead:2::99/64 dev eth0 nodad
132ip -net "$ns2" route add default via 10.0.2.1
133ip -net "$ns2" route add default via dead:2::1
134
135test_ping() {
136  local daddr4=$1
137  local daddr6=$2
138
139  if ! ip netns exec "$ns1" ping -c 1 -q "$daddr4" > /dev/null; then
140	check_drops
141	echo "FAIL: ${ns1} cannot reach $daddr4, ret $ret" 1>&2
142	return 1
143  fi
144
145  if ! ip netns exec "$ns1" ping -c 1 -q "$daddr6" > /dev/null; then
146	check_drops
147	echo "FAIL: ${ns1} cannot reach $daddr6, ret $ret" 1>&2
148	return 1
149  fi
150
151  return 0
152}
153
154ip netns exec "$nsrouter" sysctl net.ipv6.conf.all.forwarding=1 > /dev/null
155ip netns exec "$nsrouter" sysctl net.ipv4.conf.veth0.forwarding=1 > /dev/null
156ip netns exec "$nsrouter" sysctl net.ipv4.conf.veth1.forwarding=1 > /dev/null
157ip netns exec "$nsrouter" sysctl net.ipv4.conf.all.rp_filter=0 > /dev/null
158ip netns exec "$nsrouter" sysctl net.ipv4.conf.veth0.rp_filter=0 > /dev/null
159
160test_ping 10.0.2.1 dead:2::1 || exit 1
161check_drops || exit 1
162
163test_ping 10.0.2.99 dead:2::99 || exit 1
164check_drops || exit 1
165
166echo "PASS: fib expression did not cause unwanted packet drops"
167
168ip netns exec "$nsrouter" nft flush table inet filter
169
170ip -net "$ns1" route del default
171ip -net "$ns1" -6 route del default
172
173ip -net "$ns1" addr del 10.0.1.99/24 dev eth0
174ip -net "$ns1" addr del dead:1::99/64 dev eth0
175
176ip -net "$ns1" addr add 10.0.2.99/24 dev eth0
177ip -net "$ns1" addr add dead:2::99/64 dev eth0 nodad
178
179ip -net "$ns1" route add default via 10.0.2.1
180ip -net "$ns1" -6 route add default via dead:2::1
181
182ip -net "$nsrouter" addr add dead:2::1/64 dev veth0 nodad
183
184# switch to ruleset that doesn't log, this time
185# its expected that this does drop the packets.
186load_ruleset_count "$nsrouter"
187
188# ns1 has a default route, but nsrouter does not.
189# must not check return value, ping to 1.1.1.1 will
190# fail.
191check_fib_counter 0 "$nsrouter" 1.1.1.1 || exit 1
192check_fib_counter 0 "$nsrouter" 1c3::c01d || exit 1
193
194ip netns exec "$ns1" ping -W 0.5 -c 1 -q 1.1.1.1 > /dev/null
195check_fib_counter 1 "$nsrouter" 1.1.1.1 || exit 1
196
197ip netns exec "$ns1" ping -W 0.5 -i 0.1 -c 3 -q 1c3::c01d > /dev/null
198check_fib_counter 3 "$nsrouter" 1c3::c01d || exit 1
199
200# delete all rules
201ip netns exec "$ns1" nft flush ruleset
202ip netns exec "$ns2" nft flush ruleset
203ip netns exec "$nsrouter" nft flush ruleset
204
205ip -net "$ns1" addr add 10.0.1.99/24 dev eth0
206ip -net "$ns1" addr add dead:1::99/64 dev eth0 nodad
207
208ip -net "$ns1" addr del 10.0.2.99/24 dev eth0
209ip -net "$ns1" addr del dead:2::99/64 dev eth0
210
211ip -net "$nsrouter" addr del dead:2::1/64 dev veth0
212
213# ... pbr ruleset for the router, check iif+oif.
214if ! load_pbr_ruleset "$nsrouter";then
215	echo "SKIP: Could not load fib forward ruleset"
216	exit $ksft_skip
217fi
218
219ip -net "$nsrouter" rule add from all table 128
220ip -net "$nsrouter" rule add from all iif veth0 table 129
221ip -net "$nsrouter" route add table 128 to 10.0.1.0/24 dev veth0
222ip -net "$nsrouter" route add table 129 to 10.0.2.0/24 dev veth1
223
224# drop main ipv4 table
225ip -net "$nsrouter" -4 rule delete table main
226
227if ! test_ping 10.0.2.99 dead:2::99;then
228	ip -net "$nsrouter" nft list ruleset
229	echo "FAIL: fib mismatch in pbr setup"
230	exit 1
231fi
232
233echo "PASS: fib expression forward check with policy based routing"
234exit 0
235