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_ruleset_count() {
70	local netns=$1
71
72ip netns exec ${netns} nft -f /dev/stdin <<EOF
73table inet filter {
74	chain prerouting {
75		type filter hook prerouting priority 0; policy accept;
76		ip daddr 1.1.1.1 fib saddr . iif oif missing counter drop
77		ip6 daddr 1c3::c01d fib saddr . iif oif missing counter drop
78	}
79}
80EOF
81}
82
83check_drops() {
84	dmesg | grep -q ' nft_rpfilter: '
85	if [ $? -eq 0 ]; then
86		dmesg | grep ' nft_rpfilter: '
87		echo "FAIL: rpfilter did drop packets"
88		return 1
89	fi
90
91	return 0
92}
93
94check_fib_counter() {
95	local want=$1
96	local ns=$2
97	local address=$3
98
99	line=$(ip netns exec ${ns} nft list table inet filter | grep 'fib saddr . iif' | grep $address | grep "packets $want" )
100	ret=$?
101
102	if [ $ret -ne 0 ];then
103		echo "Netns $ns fib counter doesn't match expected packet count of $want for $address" 1>&2
104		ip netns exec ${ns} nft list table inet filter
105		return 1
106	fi
107
108	if [ $want -gt 0 ]; then
109		echo "PASS: fib expression did drop packets for $address"
110	fi
111
112	return 0
113}
114
115load_ruleset ${nsrouter}
116load_ruleset ${ns1}
117load_ruleset ${ns2}
118
119ip link add veth0 netns ${nsrouter} type veth peer name eth0 netns ${ns1} > /dev/null 2>&1
120if [ $? -ne 0 ];then
121    echo "SKIP: No virtual ethernet pair device support in kernel"
122    exit $ksft_skip
123fi
124ip link add veth1 netns ${nsrouter} type veth peer name eth0 netns ${ns2}
125
126ip -net ${nsrouter} link set lo up
127ip -net ${nsrouter} link set veth0 up
128ip -net ${nsrouter} addr add 10.0.1.1/24 dev veth0
129ip -net ${nsrouter} addr add dead:1::1/64 dev veth0
130
131ip -net ${nsrouter} link set veth1 up
132ip -net ${nsrouter} addr add 10.0.2.1/24 dev veth1
133ip -net ${nsrouter} addr add dead:2::1/64 dev veth1
134
135ip -net ${ns1} link set lo up
136ip -net ${ns1} link set eth0 up
137
138ip -net ${ns2} link set lo up
139ip -net ${ns2} link set eth0 up
140
141ip -net ${ns1} addr add 10.0.1.99/24 dev eth0
142ip -net ${ns1} addr add dead:1::99/64 dev eth0
143ip -net ${ns1} route add default via 10.0.1.1
144ip -net ${ns1} route add default via dead:1::1
145
146ip -net ${ns2} addr add 10.0.2.99/24 dev eth0
147ip -net ${ns2} addr add dead:2::99/64 dev eth0
148ip -net ${ns2} route add default via 10.0.2.1
149ip -net ${ns2} route add default via dead:2::1
150
151test_ping() {
152  local daddr4=$1
153  local daddr6=$2
154
155  ip netns exec ${ns1} ping -c 1 -q $daddr4 > /dev/null
156  ret=$?
157  if [ $ret -ne 0 ];then
158	check_drops
159	echo "FAIL: ${ns1} cannot reach $daddr4, ret $ret" 1>&2
160	return 1
161  fi
162
163  ip netns exec ${ns1} ping -c 3 -q $daddr6 > /dev/null
164  ret=$?
165  if [ $ret -ne 0 ];then
166	check_drops
167	echo "FAIL: ${ns1} cannot reach $daddr6, ret $ret" 1>&2
168	return 1
169  fi
170
171  return 0
172}
173
174ip netns exec ${nsrouter} sysctl net.ipv6.conf.all.forwarding=1 > /dev/null
175ip netns exec ${nsrouter} sysctl net.ipv4.conf.veth0.forwarding=1 > /dev/null
176ip netns exec ${nsrouter} sysctl net.ipv4.conf.veth1.forwarding=1 > /dev/null
177
178sleep 3
179
180test_ping 10.0.2.1 dead:2::1 || exit 1
181check_drops || exit 1
182
183test_ping 10.0.2.99 dead:2::99 || exit 1
184check_drops || exit 1
185
186echo "PASS: fib expression did not cause unwanted packet drops"
187
188ip netns exec ${nsrouter} nft flush table inet filter
189
190ip -net ${ns1} route del default
191ip -net ${ns1} -6 route del default
192
193ip -net ${ns1} addr del 10.0.1.99/24 dev eth0
194ip -net ${ns1} addr del dead:1::99/64 dev eth0
195
196ip -net ${ns1} addr add 10.0.2.99/24 dev eth0
197ip -net ${ns1} addr add dead:2::99/64 dev eth0
198
199ip -net ${ns1} route add default via 10.0.2.1
200ip -net ${ns1} -6 route add default via dead:2::1
201
202ip -net ${nsrouter} addr add dead:2::1/64 dev veth0
203
204# switch to ruleset that doesn't log, this time
205# its expected that this does drop the packets.
206load_ruleset_count ${nsrouter}
207
208# ns1 has a default route, but nsrouter does not.
209# must not check return value, ping to 1.1.1.1 will
210# fail.
211check_fib_counter 0 ${nsrouter} 1.1.1.1 || exit 1
212check_fib_counter 0 ${nsrouter} 1c3::c01d || exit 1
213
214ip netns exec ${ns1} ping -c 1 -W 1 -q 1.1.1.1 > /dev/null
215check_fib_counter 1 ${nsrouter} 1.1.1.1 || exit 1
216
217sleep 2
218ip netns exec ${ns1} ping -c 3 -q 1c3::c01d > /dev/null
219check_fib_counter 3 ${nsrouter} 1c3::c01d || exit 1
220
221exit 0
222