Merge tag 'io_uring-5.13-2021-05-07' of git://git.kernel.dk/linux-block
[linux-2.6-microblaze.git] / tools / testing / selftests / netfilter / nft_queue.sh
1 #!/bin/bash
2 #
3 # This tests nf_queue:
4 # 1. can process packets from all hooks
5 # 2. support running nfqueue from more than one base chain
6 #
7 # Kselftest framework requirement - SKIP code is 4.
8 ksft_skip=4
9 ret=0
10
11 sfx=$(mktemp -u "XXXXXXXX")
12 ns1="ns1-$sfx"
13 ns2="ns2-$sfx"
14 nsrouter="nsrouter-$sfx"
15 timeout=4
16
17 cleanup()
18 {
19         ip netns del ${ns1}
20         ip netns del ${ns2}
21         ip netns del ${nsrouter}
22         rm -f "$TMPFILE0"
23         rm -f "$TMPFILE1"
24         rm -f "$TMPFILE2" "$TMPFILE3"
25 }
26
27 nft --version > /dev/null 2>&1
28 if [ $? -ne 0 ];then
29         echo "SKIP: Could not run test without nft tool"
30         exit $ksft_skip
31 fi
32
33 ip -Version > /dev/null 2>&1
34 if [ $? -ne 0 ];then
35         echo "SKIP: Could not run test without ip tool"
36         exit $ksft_skip
37 fi
38
39 ip netns add ${nsrouter}
40 if [ $? -ne 0 ];then
41         echo "SKIP: Could not create net namespace"
42         exit $ksft_skip
43 fi
44
45 TMPFILE0=$(mktemp)
46 TMPFILE1=$(mktemp)
47 TMPFILE2=$(mktemp)
48 TMPFILE3=$(mktemp)
49 trap cleanup EXIT
50
51 ip netns add ${ns1}
52 ip netns add ${ns2}
53
54 ip link add veth0 netns ${nsrouter} type veth peer name eth0 netns ${ns1} > /dev/null 2>&1
55 if [ $? -ne 0 ];then
56     echo "SKIP: No virtual ethernet pair device support in kernel"
57     exit $ksft_skip
58 fi
59 ip link add veth1 netns ${nsrouter} type veth peer name eth0 netns ${ns2}
60
61 ip -net ${nsrouter} link set lo up
62 ip -net ${nsrouter} link set veth0 up
63 ip -net ${nsrouter} addr add 10.0.1.1/24 dev veth0
64 ip -net ${nsrouter} addr add dead:1::1/64 dev veth0
65
66 ip -net ${nsrouter} link set veth1 up
67 ip -net ${nsrouter} addr add 10.0.2.1/24 dev veth1
68 ip -net ${nsrouter} addr add dead:2::1/64 dev veth1
69
70 ip -net ${ns1} link set lo up
71 ip -net ${ns1} link set eth0 up
72
73 ip -net ${ns2} link set lo up
74 ip -net ${ns2} link set eth0 up
75
76 ip -net ${ns1} addr add 10.0.1.99/24 dev eth0
77 ip -net ${ns1} addr add dead:1::99/64 dev eth0
78 ip -net ${ns1} route add default via 10.0.1.1
79 ip -net ${ns1} route add default via dead:1::1
80
81 ip -net ${ns2} addr add 10.0.2.99/24 dev eth0
82 ip -net ${ns2} addr add dead:2::99/64 dev eth0
83 ip -net ${ns2} route add default via 10.0.2.1
84 ip -net ${ns2} route add default via dead:2::1
85
86 load_ruleset() {
87         local name=$1
88         local prio=$2
89
90 ip netns exec ${nsrouter} nft -f /dev/stdin <<EOF
91 table inet $name {
92         chain nfq {
93                 ip protocol icmp queue bypass
94                 icmpv6 type { "echo-request", "echo-reply" } queue num 1 bypass
95         }
96         chain pre {
97                 type filter hook prerouting priority $prio; policy accept;
98                 jump nfq
99         }
100         chain input {
101                 type filter hook input priority $prio; policy accept;
102                 jump nfq
103         }
104         chain forward {
105                 type filter hook forward priority $prio; policy accept;
106                 tcp dport 12345 queue num 2
107                 jump nfq
108         }
109         chain output {
110                 type filter hook output priority $prio; policy accept;
111                 tcp dport 12345 queue num 3
112                 jump nfq
113         }
114         chain post {
115                 type filter hook postrouting priority $prio; policy accept;
116                 jump nfq
117         }
118 }
119 EOF
120 }
121
122 load_counter_ruleset() {
123         local prio=$1
124
125 ip netns exec ${nsrouter} nft -f /dev/stdin <<EOF
126 table inet countrules {
127         chain pre {
128                 type filter hook prerouting priority $prio; policy accept;
129                 counter
130         }
131         chain input {
132                 type filter hook input priority $prio; policy accept;
133                 counter
134         }
135         chain forward {
136                 type filter hook forward priority $prio; policy accept;
137                 counter
138         }
139         chain output {
140                 type filter hook output priority $prio; policy accept;
141                 counter
142         }
143         chain post {
144                 type filter hook postrouting priority $prio; policy accept;
145                 counter
146         }
147 }
148 EOF
149 }
150
151 test_ping() {
152   ip netns exec ${ns1} ping -c 1 -q 10.0.2.99 > /dev/null
153   if [ $? -ne 0 ];then
154         return 1
155   fi
156
157   ip netns exec ${ns1} ping -c 1 -q dead:2::99 > /dev/null
158   if [ $? -ne 0 ];then
159         return 1
160   fi
161
162   return 0
163 }
164
165 test_ping_router() {
166   ip netns exec ${ns1} ping -c 1 -q 10.0.2.1 > /dev/null
167   if [ $? -ne 0 ];then
168         return 1
169   fi
170
171   ip netns exec ${ns1} ping -c 1 -q dead:2::1 > /dev/null
172   if [ $? -ne 0 ];then
173         return 1
174   fi
175
176   return 0
177 }
178
179 test_queue_blackhole() {
180         local proto=$1
181
182 ip netns exec ${nsrouter} nft -f /dev/stdin <<EOF
183 table $proto blackh {
184         chain forward {
185         type filter hook forward priority 0; policy accept;
186                 queue num 600
187         }
188 }
189 EOF
190         if [ $proto = "ip" ] ;then
191                 ip netns exec ${ns1} ping -W 2 -c 1 -q 10.0.2.99 > /dev/null
192                 lret=$?
193         elif [ $proto = "ip6" ]; then
194                 ip netns exec ${ns1} ping -W 2 -c 1 -q dead:2::99 > /dev/null
195                 lret=$?
196         else
197                 lret=111
198         fi
199
200         # queue without bypass keyword should drop traffic if no listener exists.
201         if [ $lret -eq 0 ];then
202                 echo "FAIL: $proto expected failure, got $lret" 1>&2
203                 exit 1
204         fi
205
206         ip netns exec ${nsrouter} nft delete table $proto blackh
207         if [ $? -ne 0 ] ;then
208                 echo "FAIL: $proto: Could not delete blackh table"
209                 exit 1
210         fi
211
212         echo "PASS: $proto: statement with no listener results in packet drop"
213 }
214
215 test_queue()
216 {
217         local expected=$1
218         local last=""
219
220         # spawn nf-queue listeners
221         ip netns exec ${nsrouter} ./nf-queue -c -q 0 -t $timeout > "$TMPFILE0" &
222         ip netns exec ${nsrouter} ./nf-queue -c -q 1 -t $timeout > "$TMPFILE1" &
223         sleep 1
224         test_ping
225         ret=$?
226         if [ $ret -ne 0 ];then
227                 echo "FAIL: netns routing/connectivity with active listener on queue $queue: $ret" 1>&2
228                 exit $ret
229         fi
230
231         test_ping_router
232         ret=$?
233         if [ $ret -ne 0 ];then
234                 echo "FAIL: netns router unreachable listener on queue $queue: $ret" 1>&2
235                 exit $ret
236         fi
237
238         wait
239         ret=$?
240
241         for file in $TMPFILE0 $TMPFILE1; do
242                 last=$(tail -n1 "$file")
243                 if [ x"$last" != x"$expected packets total" ]; then
244                         echo "FAIL: Expected $expected packets total, but got $last" 1>&2
245                         cat "$file" 1>&2
246
247                         ip netns exec ${nsrouter} nft list ruleset
248                         exit 1
249                 fi
250         done
251
252         echo "PASS: Expected and received $last"
253 }
254
255 test_tcp_forward()
256 {
257         ip netns exec ${nsrouter} ./nf-queue -q 2 -t $timeout &
258         local nfqpid=$!
259
260         tmpfile=$(mktemp) || exit 1
261         dd conv=sparse status=none if=/dev/zero bs=1M count=200 of=$tmpfile
262         ip netns exec ${ns2} nc -w 5 -l -p 12345 <"$tmpfile" >/dev/null &
263         local rpid=$!
264
265         sleep 1
266         ip netns exec ${ns1} nc -w 5 10.0.2.99 12345 <"$tmpfile" >/dev/null &
267
268         rm -f "$tmpfile"
269
270         wait $rpid
271         wait $lpid
272         [ $? -eq 0 ] && echo "PASS: tcp and nfqueue in forward chain"
273 }
274
275 test_tcp_localhost()
276 {
277         tmpfile=$(mktemp) || exit 1
278
279         dd conv=sparse status=none if=/dev/zero bs=1M count=200 of=$tmpfile
280         ip netns exec ${nsrouter} nc -w 5 -l -p 12345 <"$tmpfile" >/dev/null &
281         local rpid=$!
282
283         ip netns exec ${nsrouter} ./nf-queue -q 3 -t $timeout &
284         local nfqpid=$!
285
286         sleep 1
287         ip netns exec ${nsrouter} nc -w 5 127.0.0.1 12345 <"$tmpfile" > /dev/null
288         rm -f "$tmpfile"
289
290         wait $rpid
291         [ $? -eq 0 ] && echo "PASS: tcp via loopback"
292         wait 2>/dev/null
293 }
294
295 test_tcp_localhost_requeue()
296 {
297 ip netns exec ${nsrouter} nft -f /dev/stdin <<EOF
298 flush ruleset
299 table inet filter {
300         chain output {
301                 type filter hook output priority 0; policy accept;
302                 tcp dport 12345 limit rate 1/second burst 1 packets counter queue num 0
303         }
304         chain post {
305                 type filter hook postrouting priority 0; policy accept;
306                 tcp dport 12345 limit rate 1/second burst 1 packets counter queue num 0
307         }
308 }
309 EOF
310         tmpfile=$(mktemp) || exit 1
311         dd conv=sparse status=none if=/dev/zero bs=1M count=200 of=$tmpfile
312         ip netns exec ${nsrouter} nc -w 5 -l -p 12345 <"$tmpfile" >/dev/null &
313         local rpid=$!
314
315         ip netns exec ${nsrouter} ./nf-queue -c -q 1 -t $timeout > "$TMPFILE2" &
316
317         # nfqueue 1 will be called via output hook.  But this time,
318         # re-queue the packet to nfqueue program on queue 2.
319         ip netns exec ${nsrouter} ./nf-queue -G -d 150 -c -q 0 -Q 1 -t $timeout > "$TMPFILE3" &
320
321         sleep 1
322         ip netns exec ${nsrouter} nc -w 5 127.0.0.1 12345 <"$tmpfile" > /dev/null
323         rm -f "$tmpfile"
324
325         wait
326
327         if ! diff -u "$TMPFILE2" "$TMPFILE3" ; then
328                 echo "FAIL: lost packets during requeue?!" 1>&2
329                 return
330         fi
331
332         echo "PASS: tcp via loopback and re-queueing"
333 }
334
335 ip netns exec ${nsrouter} sysctl net.ipv6.conf.all.forwarding=1 > /dev/null
336 ip netns exec ${nsrouter} sysctl net.ipv4.conf.veth0.forwarding=1 > /dev/null
337 ip netns exec ${nsrouter} sysctl net.ipv4.conf.veth1.forwarding=1 > /dev/null
338
339 load_ruleset "filter" 0
340
341 sleep 3
342
343 test_ping
344 ret=$?
345 if [ $ret -eq 0 ];then
346         # queue bypass works (rules were skipped, no listener)
347         echo "PASS: ${ns1} can reach ${ns2}"
348 else
349         echo "FAIL: ${ns1} cannot reach ${ns2}: $ret" 1>&2
350         exit $ret
351 fi
352
353 test_queue_blackhole ip
354 test_queue_blackhole ip6
355
356 # dummy ruleset to add base chains between the
357 # queueing rules.  We don't want the second reinject
358 # to re-execute the old hooks.
359 load_counter_ruleset 10
360
361 # we are hooking all: prerouting/input/forward/output/postrouting.
362 # we ping ${ns2} from ${ns1} via ${nsrouter} using ipv4 and ipv6, so:
363 # 1x icmp prerouting,forward,postrouting -> 3 queue events (6 incl. reply).
364 # 1x icmp prerouting,input,output postrouting -> 4 queue events incl. reply.
365 # so we expect that userspace program receives 10 packets.
366 test_queue 10
367
368 # same.  We queue to a second program as well.
369 load_ruleset "filter2" 20
370 test_queue 20
371
372 test_tcp_forward
373 test_tcp_localhost
374 test_tcp_localhost_requeue
375
376 exit $ret