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