xref: /linux/tools/testing/selftests/net/netfilter/nft_queue.sh (revision 2ed2e359dea752e7758d29a423033031a0b96584)
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# shellcheck disable=SC2162,SC2317
8
9source lib.sh
10ret=0
11timeout=5
12
13SCTP_TEST_TIMEOUT=60
14STRESS_TEST_TIMEOUT=30
15
16cleanup()
17{
18	ip netns pids "$ns1" | xargs kill 2>/dev/null
19	ip netns pids "$ns2" | xargs kill 2>/dev/null
20	ip netns pids "$nsrouter" | xargs kill 2>/dev/null
21
22	cleanup_all_ns
23
24	rm -f "$TMPINPUT"
25	rm -f "$TMPFILE0"
26	rm -f "$TMPFILE1"
27	rm -f "$TMPFILE2" "$TMPFILE3"
28}
29
30checktool "nft --version" "test without nft tool"
31checktool "socat -h" "run test without socat"
32
33modprobe -q sctp
34
35trap cleanup EXIT
36
37setup_ns ns1 ns2 ns3 nsrouter
38
39TMPFILE0=$(mktemp)
40TMPFILE1=$(mktemp)
41TMPFILE2=$(mktemp)
42TMPFILE3=$(mktemp)
43
44TMPINPUT=$(mktemp)
45COUNT=200
46[ "$KSFT_MACHINE_SLOW" = "yes" ] && COUNT=$((COUNT/8))
47dd conv=sparse status=none if=/dev/zero bs=1M count=$COUNT of="$TMPINPUT"
48
49if ! ip link add veth0 netns "$nsrouter" type veth peer name eth0 netns "$ns1" > /dev/null 2>&1; then
50    echo "SKIP: No virtual ethernet pair device support in kernel"
51    exit $ksft_skip
52fi
53ip link add veth1 netns "$nsrouter" type veth peer name eth0 netns "$ns2"
54ip link add veth2 netns "$nsrouter" type veth peer name eth0 netns "$ns3"
55
56ip -net "$nsrouter" link set veth0 up
57ip -net "$nsrouter" addr add 10.0.1.1/24 dev veth0
58ip -net "$nsrouter" addr add dead:1::1/64 dev veth0 nodad
59
60ip -net "$nsrouter" link set veth1 up
61ip -net "$nsrouter" addr add 10.0.2.1/24 dev veth1
62ip -net "$nsrouter" addr add dead:2::1/64 dev veth1 nodad
63
64ip -net "$nsrouter" link set veth2 up
65ip -net "$nsrouter" addr add 10.0.3.1/24 dev veth2
66ip -net "$nsrouter" addr add dead:3::1/64 dev veth2 nodad
67
68ip -net "$ns1" link set eth0 up
69ip -net "$ns2" link set eth0 up
70ip -net "$ns3" link set eth0 up
71
72ip -net "$ns1" addr add 10.0.1.99/24 dev eth0
73ip -net "$ns1" addr add dead:1::99/64 dev eth0 nodad
74ip -net "$ns1" route add default via 10.0.1.1
75ip -net "$ns1" route add default via dead:1::1
76
77ip -net "$ns2" addr add 10.0.2.99/24 dev eth0
78ip -net "$ns2" addr add dead:2::99/64 dev eth0 nodad
79ip -net "$ns2" route add default via 10.0.2.1
80ip -net "$ns2" route add default via dead:2::1
81
82ip -net "$ns3" addr add 10.0.3.99/24 dev eth0
83ip -net "$ns3" addr add dead:3::99/64 dev eth0 nodad
84ip -net "$ns3" route add default via 10.0.3.1
85ip -net "$ns3" route add default via dead:3::1
86
87load_ruleset() {
88	local family=$1
89	local name=$2
90	local prio=$3
91
92ip netns exec "$nsrouter" nft -f /dev/stdin <<EOF
93table $family $name {
94	chain nfq {
95		ip protocol icmp queue bypass
96		icmpv6 type { "echo-request", "echo-reply" } queue num 1 bypass
97	}
98	chain pre {
99		type filter hook prerouting priority $prio; policy accept;
100		jump nfq
101	}
102	chain input {
103		type filter hook input priority $prio; policy accept;
104		jump nfq
105	}
106	chain forward {
107		type filter hook forward priority $prio; policy accept;
108		tcp dport 12345 queue num 2
109		jump nfq
110	}
111	chain output {
112		type filter hook output priority $prio; policy accept;
113		tcp dport 12345 queue num 3
114		tcp sport 23456 queue num 3
115		jump nfq
116	}
117	chain post {
118		type filter hook postrouting priority $prio; policy accept;
119		jump nfq
120	}
121}
122EOF
123}
124
125load_counter_ruleset() {
126	local prio=$1
127
128ip netns exec "$nsrouter" nft -f /dev/stdin <<EOF
129table inet countrules {
130	chain pre {
131		type filter hook prerouting priority $prio; policy accept;
132		counter
133	}
134	chain input {
135		type filter hook input priority $prio; policy accept;
136		counter
137	}
138	chain forward {
139		type filter hook forward priority $prio; policy accept;
140		counter
141	}
142	chain output {
143		type filter hook output priority $prio; policy accept;
144		counter
145	}
146	chain post {
147		type filter hook postrouting priority $prio; policy accept;
148		counter
149	}
150}
151EOF
152}
153
154test_ping() {
155  if ! ip netns exec "$ns1" ping -c 1 -q 10.0.2.99 > /dev/null; then
156	return 1
157  fi
158
159  if ! ip netns exec "$ns1" ping -c 1 -q dead:2::99 > /dev/null; then
160	return 2
161  fi
162
163  return 0
164}
165
166test_ping_router() {
167  if ! ip netns exec "$ns1" ping -c 1 -q 10.0.2.1 > /dev/null; then
168	return 3
169  fi
170
171  if ! ip netns exec "$ns1" ping -c 1 -q dead:2::1 > /dev/null; then
172	return 4
173  fi
174
175  return 0
176}
177
178test_queue_blackhole() {
179	local proto=$1
180
181ip netns exec "$nsrouter" nft -f /dev/stdin <<EOF
182table $proto blackh {
183	chain forward {
184	type filter hook forward priority 0; policy accept;
185		queue num 600
186	}
187}
188EOF
189	if [ "$proto" = "ip" ] ;then
190		ip netns exec "$ns1" ping -W 2 -c 1 -q 10.0.2.99 > /dev/null
191		lret=$?
192	elif [ "$proto" = "ip6" ]; then
193		ip netns exec "$ns1" ping -W 2 -c 1 -q dead:2::99 > /dev/null
194		lret=$?
195	else
196		lret=111
197	fi
198
199	# queue without bypass keyword should drop traffic if no listener exists.
200	if [ "$lret" -eq 0 ];then
201		echo "FAIL: $proto expected failure, got $lret" 1>&2
202		exit 1
203	fi
204
205	if ! ip netns exec "$nsrouter" nft delete table "$proto" blackh; then
206	        echo "FAIL: $proto: Could not delete blackh table"
207	        exit 1
208	fi
209
210        echo "PASS: $proto: statement with no listener results in packet drop"
211}
212
213nf_queue_wait()
214{
215	local procfile="/proc/self/net/netfilter/nfnetlink_queue"
216	local netns id
217
218	netns="$1"
219	id="$2"
220
221	# if this file doesn't exist, nfnetlink_module isn't loaded.
222	# rather than loading it ourselves, wait for kernel module autoload
223	# completion, nfnetlink should do so automatically because nf_queue
224	# helper program, spawned in the background, asked for this functionality.
225	test -f "$procfile" &&
226		ip netns exec "$netns" cat "$procfile" | grep -q "^ *$id "
227}
228
229test_queue()
230{
231	local expected="$1"
232	local family="$2"
233	local last=""
234
235	# spawn nf_queue listeners
236	ip netns exec "$nsrouter" ./nf_queue -c -q 0 -t $timeout > "$TMPFILE0" &
237	ip netns exec "$nsrouter" ./nf_queue -c -q 1 -t $timeout > "$TMPFILE1" &
238
239	busywait "$BUSYWAIT_TIMEOUT" nf_queue_wait "$nsrouter" 0
240	busywait "$BUSYWAIT_TIMEOUT" nf_queue_wait "$nsrouter" 1
241
242	if ! test_ping;then
243		echo "FAIL: netns routing/connectivity with active listener on queues 0 and 1: $ret" 1>&2
244		exit $ret
245	fi
246
247	if ! test_ping_router;then
248		echo "FAIL: netns router unreachable listener on queue 0 and 1: $ret" 1>&2
249		exit $ret
250	fi
251
252	wait
253	ret=$?
254
255	for file in $TMPFILE0 $TMPFILE1; do
256		last=$(tail -n1 "$file")
257		if [ x"$last" != x"$expected packets total" ]; then
258			echo "FAIL: Expected $expected packets total, but got $last" 1>&2
259			ip netns exec "$nsrouter" nft list ruleset
260			echo -n "$TMPFILE0: ";cat "$TMPFILE0"
261			echo -n "$TMPFILE1: ";cat "$TMPFILE1"
262			exit 1
263		fi
264	done
265
266	echo "PASS: Expected and received $last ($family)"
267}
268
269listener_ready()
270{
271	ss -N "$1" -lnt -o "sport = :12345" | grep -q 12345
272}
273
274test_tcp_forward()
275{
276	ip netns exec "$nsrouter" ./nf_queue -q 2 &
277	local nfqpid=$!
278
279	timeout 5 ip netns exec "$ns2" socat -u TCP-LISTEN:12345 STDOUT >/dev/null &
280	local rpid=$!
281
282	busywait "$BUSYWAIT_TIMEOUT" listener_ready "$ns2"
283	busywait "$BUSYWAIT_TIMEOUT" nf_queue_wait "$nsrouter" 2
284
285	local tthen=$(date +%s)
286
287	ip netns exec "$ns1" socat -u STDIN TCP:10.0.2.99:12345 <"$TMPINPUT" >/dev/null
288
289	wait_and_check_retval "$rpid" "tcp and nfqueue in forward chain" "$tthen"
290	kill "$nfqpid"
291}
292
293test_tcp_localhost()
294{
295	timeout 5 ip netns exec "$nsrouter" socat -u TCP-LISTEN:12345 STDOUT >/dev/null &
296	local rpid=$!
297
298	ip netns exec "$nsrouter" ./nf_queue -q 3 &
299	local nfqpid=$!
300	local tthen=$(date +%s)
301
302	busywait "$BUSYWAIT_TIMEOUT" listener_ready "$nsrouter"
303	busywait "$BUSYWAIT_TIMEOUT" nf_queue_wait "$nsrouter" 3
304
305	ip netns exec "$nsrouter" socat -u STDIN TCP:127.0.0.1:12345 <"$TMPINPUT" >/dev/null
306
307	wait_and_check_retval "$rpid" "tcp via loopback" "$tthen"
308	kill "$nfqpid"
309}
310
311test_tcp_localhost_connectclose()
312{
313	ip netns exec "$nsrouter" ./nf_queue -q 3 &
314	local nfqpid=$!
315
316	busywait "$BUSYWAIT_TIMEOUT" nf_queue_wait "$nsrouter" 3
317
318	timeout 10 ip netns exec "$nsrouter" ./connect_close -p 23456 -t 3
319
320	kill "$nfqpid"
321	wait && echo "PASS: tcp via loopback with connect/close"
322}
323
324test_tcp_localhost_requeue()
325{
326ip netns exec "$nsrouter" nft -f /dev/stdin <<EOF
327flush ruleset
328table inet filter {
329	chain output {
330		type filter hook output priority 0; policy accept;
331		tcp dport 12345 limit rate 1/second burst 1 packets counter queue num 0
332	}
333	chain post {
334		type filter hook postrouting priority 0; policy accept;
335		tcp dport 12345 limit rate 1/second burst 1 packets counter queue num 0
336	}
337}
338EOF
339	timeout 5 ip netns exec "$nsrouter" socat -u TCP-LISTEN:12345 STDOUT >/dev/null &
340	local rpid=$!
341
342	ip netns exec "$nsrouter" ./nf_queue -c -q 1 -t "$timeout" > "$TMPFILE2" &
343
344	# nfqueue 1 will be called via output hook.  But this time,
345        # re-queue the packet to nfqueue program on queue 2.
346	ip netns exec "$nsrouter" ./nf_queue -G -d 150 -c -q 0 -Q 1 -t "$timeout" > "$TMPFILE3" &
347
348	busywait "$BUSYWAIT_TIMEOUT" listener_ready "$nsrouter"
349	ip netns exec "$nsrouter" socat -u STDIN TCP:127.0.0.1:12345 <"$TMPINPUT" > /dev/null
350
351	wait
352
353	if ! diff -u "$TMPFILE2" "$TMPFILE3" ; then
354		echo "FAIL: lost packets during requeue?!" 1>&2
355		return
356	fi
357
358	echo "PASS: tcp via loopback and re-queueing"
359}
360
361test_icmp_vrf() {
362	if ! ip -net "$ns1" link add tvrf type vrf table 9876;then
363		echo "SKIP: Could not add vrf device"
364		return
365	fi
366
367	ip -net "$ns1" li set eth0 master tvrf
368	ip -net "$ns1" li set tvrf up
369
370	ip -net "$ns1" route add 10.0.2.0/24 via 10.0.1.1 dev eth0 table 9876
371ip netns exec "$ns1" nft -f /dev/stdin <<EOF
372flush ruleset
373table inet filter {
374	chain output {
375		type filter hook output priority 0; policy accept;
376		meta oifname "tvrf" icmp type echo-request counter queue num 1
377		meta oifname "eth0" icmp type echo-request counter queue num 1
378	}
379	chain post {
380		type filter hook postrouting priority 0; policy accept;
381		meta oifname "tvrf" icmp type echo-request counter queue num 1
382		meta oifname "eth0" icmp type echo-request counter queue num 1
383	}
384}
385EOF
386	ip netns exec "$ns1" ./nf_queue -q 1 &
387	local nfqpid=$!
388
389	busywait "$BUSYWAIT_TIMEOUT" nf_queue_wait "$ns1" 1
390
391	ip netns exec "$ns1" ip vrf exec tvrf ping -c 1 10.0.2.99 > /dev/null
392
393	for n in output post; do
394		for d in tvrf eth0; do
395			if ! ip netns exec "$ns1" nft list chain inet filter "$n" | grep -q "oifname \"$d\" icmp type echo-request counter packets 1"; then
396				kill "$nfqpid"
397				echo "FAIL: chain $n: icmp packet counter mismatch for device $d" 1>&2
398				ip netns exec "$ns1" nft list ruleset
399				ret=1
400				return
401			fi
402		done
403	done
404
405	kill "$nfqpid"
406	echo "PASS: icmp+nfqueue via vrf"
407	ip -net "$ns1" link del tvrf
408	ip netns exec "$ns1" nft flush ruleset
409}
410
411sctp_listener_ready()
412{
413	ss -S -N "$1" -ln -o "sport = :12345" | grep -q 12345
414}
415
416check_output_files()
417{
418	local f1="$1"
419	local f2="$2"
420	local err="$3"
421
422	if ! cmp "$f1" "$f2" ; then
423		echo "FAIL: $err: input and output file differ" 1>&2
424		echo -n " Input file" 1>&2
425		ls -l "$f1" 1>&2
426		echo -n "Output file" 1>&2
427		ls -l "$f2" 1>&2
428		ret=1
429	fi
430}
431
432wait_and_check_retval()
433{
434	local rpid="$1"
435	local msg="$2"
436	local tthen="$3"
437	local tnow=$(date +%s)
438
439	if wait "$rpid";then
440		echo -n "PASS: "
441	else
442		echo -n "FAIL: "
443		ret=1
444	fi
445
446	printf "%s (duration: %ds)\n" "$msg" $((tnow-tthen))
447}
448
449test_sctp_forward()
450{
451	ip netns exec "$nsrouter" nft -f /dev/stdin <<EOF
452flush ruleset
453table inet sctpq {
454        chain forward {
455        type filter hook forward priority 0; policy accept;
456                sctp dport 12345 queue num 10
457        }
458}
459EOF
460	timeout "$SCTP_TEST_TIMEOUT" ip netns exec "$ns2" socat -u SCTP-LISTEN:12345 STDOUT > "$TMPFILE1" &
461	local rpid=$!
462
463	busywait "$BUSYWAIT_TIMEOUT" sctp_listener_ready "$ns2"
464
465	ip netns exec "$nsrouter" ./nf_queue -q 10 -G &
466	local nfqpid=$!
467	local tthen=$(date +%s)
468
469	ip netns exec "$ns1" socat -u STDIN SCTP:10.0.2.99:12345 <"$TMPINPUT" >/dev/null
470
471	if ! ip netns exec "$nsrouter" nft delete table inet sctpq; then
472		echo "FAIL:  Could not delete sctpq table"
473		exit 1
474	fi
475
476	wait_and_check_retval "$rpid" "sctp and nfqueue in forward chain" "$tthen"
477	kill "$nfqpid"
478
479	check_output_files "$TMPINPUT" "$TMPFILE1" "sctp forward"
480}
481
482test_sctp_output()
483{
484        ip netns exec "$ns1" nft -f /dev/stdin <<EOF
485table inet sctpq {
486        chain output {
487        type filter hook output priority 0; policy accept;
488                sctp dport 12345 queue num 11
489        }
490}
491EOF
492	# reduce test file size, software segmentation causes sk wmem increase.
493	dd conv=sparse status=none if=/dev/zero bs=1M count=$((COUNT/2)) of="$TMPINPUT"
494
495	timeout "$SCTP_TEST_TIMEOUT" ip netns exec "$ns2" socat -u SCTP-LISTEN:12345 STDOUT > "$TMPFILE1" &
496	local rpid=$!
497
498	busywait "$BUSYWAIT_TIMEOUT" sctp_listener_ready "$ns2"
499
500	ip netns exec "$ns1" ./nf_queue -q 11 &
501	local nfqpid=$!
502	local tthen=$(date +%s)
503
504	ip netns exec "$ns1" socat -u STDIN SCTP:10.0.2.99:12345 <"$TMPINPUT" >/dev/null
505
506	if ! ip netns exec "$ns1" nft delete table inet sctpq; then
507		echo "FAIL:  Could not delete sctpq table"
508		exit 1
509	fi
510
511	# must wait before checking completeness of output file.
512	wait_and_check_retval "$rpid" "sctp and nfqueue in output chain with GSO" "$tthen"
513	kill "$nfqpid"
514
515	check_output_files "$TMPINPUT" "$TMPFILE1" "sctp output"
516}
517
518udp_listener_ready()
519{
520	ss -S -N "$1" -uln -o "sport = :$2" | grep -q "$2"
521}
522
523output_files_written()
524{
525	test -s "$1" && test -s "$2"
526}
527
528test_udp_nat_race()
529{
530        ip netns exec "$nsrouter" nft -f /dev/stdin <<EOF
531flush ruleset
532table inet udpq {
533	chain prerouting {
534		type nat hook prerouting priority dstnat - 5; policy accept;
535		ip daddr 10.6.6.6 udp dport 12345 counter dnat to numgen inc mod 2 map { 0 : 10.0.2.99, 1 : 10.0.3.99 }
536	}
537        chain postrouting {
538		type filter hook postrouting priority srcnat - 5; policy accept;
539		udp dport 12345 counter queue num 12
540        }
541}
542EOF
543	:> "$TMPFILE1"
544	:> "$TMPFILE2"
545
546	timeout 10 ip netns exec "$ns2" socat UDP-LISTEN:12345,fork,pf=ipv4 OPEN:"$TMPFILE1",trunc &
547	local rpid1=$!
548
549	timeout 10 ip netns exec "$ns3" socat UDP-LISTEN:12345,fork,pf=ipv4 OPEN:"$TMPFILE2",trunc &
550	local rpid2=$!
551
552	ip netns exec "$nsrouter" ./nf_queue -q 12 -d 1000 &
553	local nfqpid=$!
554
555	busywait "$BUSYWAIT_TIMEOUT" udp_listener_ready "$ns2" 12345
556	busywait "$BUSYWAIT_TIMEOUT" udp_listener_ready "$ns3" 12345
557	busywait "$BUSYWAIT_TIMEOUT" nf_queue_wait "$nsrouter" 12
558
559	# Send two packets, one should end up in ns1, other in ns2.
560	# This is because nfqueue will delay packet for long enough so that
561	# second packet will not find existing conntrack entry.
562	echo "Packet 1" | ip netns exec "$ns1" socat -u STDIN UDP-DATAGRAM:10.6.6.6:12345,bind=0.0.0.0:55221
563	echo "Packet 2" | ip netns exec "$ns1" socat -u STDIN UDP-DATAGRAM:10.6.6.6:12345,bind=0.0.0.0:55221
564
565	busywait 10000 output_files_written "$TMPFILE1" "$TMPFILE2"
566
567	kill "$nfqpid" "$rpid1" "$rpid2"
568
569	if ! ip netns exec "$nsrouter" bash -c 'conntrack -L -p udp --dport 12345 2>/dev/null | wc -l | grep -q "^1"'; then
570		echo "FAIL: Expected One udp conntrack entry"
571		ip netns exec "$nsrouter" conntrack -L -p udp --dport 12345
572		ret=1
573	fi
574
575	if ! ip netns exec "$nsrouter" nft delete table inet udpq; then
576		echo "FAIL: Could not delete udpq table"
577		ret=1
578		return
579	fi
580
581	NUMLINES1=$(wc -l < "$TMPFILE1")
582	NUMLINES2=$(wc -l < "$TMPFILE2")
583
584	if [ "$NUMLINES1" -ne 1 ] || [ "$NUMLINES2" -ne 1 ]; then
585		ret=1
586		echo "FAIL: uneven udp packet distribution: $NUMLINES1 $NUMLINES2"
587		echo -n "$TMPFILE1: ";cat "$TMPFILE1"
588		echo -n "$TMPFILE2: ";cat "$TMPFILE2"
589		return
590	fi
591
592	echo "PASS: both udp receivers got one packet each"
593}
594
595# Make sure UDPGRO aggregated packets don't lose
596# their skb->nfct entry when nfqueue passes the
597# skb to userspace with software gso segmentation on.
598test_udp_gro_ct()
599{
600	local errprefix="FAIL: test_udp_gro_ct:"
601	local timeout=5
602
603	ip netns exec "$nsrouter" conntrack -F 2>/dev/null
604
605        ip netns exec "$nsrouter" nft -f /dev/stdin <<EOF
606flush ruleset
607table inet udpq {
608	# Number of packets/bytes queued to userspace
609	counter toqueue { }
610	# Number of packets/bytes reinjected from userspace with 'ct new' intact
611	counter fromqueue { }
612	# These two counters should be identical and not 0.
613
614	chain prerouting {
615		type filter hook prerouting priority -300; policy accept;
616
617		# userspace sends small packets, if < 1000, UDPGRO did
618		# not kick in, but test needs a 'new' conntrack with udpgro skb.
619		meta iifname veth0 meta l4proto udp meta length > 1000 accept
620
621		# don't pick up non-gso packets and don't queue them to
622		# userspace.
623		notrack
624	}
625
626        chain postrouting {
627		type filter hook postrouting priority 0; policy accept;
628
629		# Only queue unconfirmed fraglist gro skbs to userspace.
630		udp dport 12346 ct status ! confirmed counter name "toqueue" mark set 1 queue num 1
631        }
632
633	chain validate {
634		type filter hook postrouting priority 1; policy accept;
635		# ... and only count those that were reinjected with the
636		# skb->nfct intact.
637		mark 1 counter name "fromqueue"
638	}
639}
640EOF
641	timeout "$timeout" ip netns exec "$ns2" socat UDP-LISTEN:12346,fork,pf=ipv4 OPEN:"$TMPFILE1",trunc &
642	local rpid=$!
643
644	ip netns exec "$nsrouter" nice -n -19 ./nf_queue -G -c -q 1 -o -t 2 > "$TMPFILE2" &
645	local nfqpid=$!
646
647	ip netns exec "$nsrouter" ethtool -K "veth0" rx-udp-gro-forwarding on rx-gro-list on generic-receive-offload on
648
649	busywait "$BUSYWAIT_TIMEOUT" udp_listener_ready "$ns2" 12346
650	busywait "$BUSYWAIT_TIMEOUT" nf_queue_wait "$nsrouter" 1
651
652	local bs=512
653	local count=$(((32 * 1024 * 1024) / bs))
654
655	local nprocs=$(nproc)
656	[ $nprocs -gt 1 ] && nprocs=$((nprocs - 1))
657
658	dd if=/dev/zero bs="$bs" count="$count" 2>/dev/null | for i in $(seq 1 $nprocs); do
659		timeout "$timeout" nice -n 19 ip netns exec "$ns1" \
660			socat -u -b 512 STDIN UDP-DATAGRAM:10.0.2.99:12346,reuseport,bind=0.0.0.0:55221 &
661	done
662
663	busywait 10000 test -s "$TMPFILE1"
664
665	kill "$rpid"
666
667	wait
668
669	local p
670	local b
671	local pqueued
672	local bqueued
673
674	c=$(ip netns exec "$nsrouter" nft list counter inet udpq "toqueue" | grep packets)
675	read p pqueued b bqueued <<EOF
676$c
677EOF
678	local preinject
679	local breinject
680	c=$(ip netns exec "$nsrouter" nft list counter inet udpq "fromqueue" | grep packets)
681	read p preinject b breinject <<EOF
682$c
683EOF
684	ip netns exec "$nsrouter" ethtool -K "veth0" rx-udp-gro-forwarding off
685	ip netns exec "$nsrouter" ethtool -K "veth1" rx-udp-gro-forwarding off
686
687	if [ "$pqueued" -eq 0 ];then
688		# happens when gro did not build at least on aggregate
689		echo "SKIP: No packets were queued"
690		return
691	fi
692
693	local saw_ct_entry=0
694	if ip netns exec "$nsrouter" bash -c 'conntrack -L -p udp --dport 12346 2>/dev/null | wc -l | grep -q "^1"'; then
695		saw_ct_entry=1
696	else
697		echo "$errprefix Expected udp conntrack entry"
698		ip netns exec "$nsrouter" conntrack -L
699		ret=1
700	fi
701
702	if [ "$pqueued" -ge "$preinject" ] ;then
703		echo "$errprefix Expected software segmentation to occur, had $pqueued and $preinject"
704		ret=1
705		return
706	fi
707
708	# sw segmentation adds extra udp and ip headers.
709	local breinject_expect=$((preinject * (512 + 20 + 8)))
710
711	if [ "$breinject" -eq "$breinject_expect" ]; then
712		if [ "$saw_ct_entry" -eq 1 ];then
713			echo "PASS: fraglist gro skb passed with conntrack entry"
714		else
715			echo "$errprefix fraglist gro skb passed without conntrack entry"
716			ret=1
717		fi
718	else
719		echo "$errprefix Counter mismatch, conntrack entry dropped by nfqueue? Queued: $pqueued, $bqueued. Post-queue: $preinject, $breinject. Expected $breinject_expect"
720		ret=1
721	fi
722
723	if ! ip netns exec "$nsrouter" nft delete table inet udpq; then
724		echo "$errprefix: Could not delete udpq table"
725		ret=1
726	fi
727}
728
729check_tainted()
730{
731	local msg="$1"
732
733	if [ "$tainted_then" -ne 0 ];then
734		return
735	fi
736
737	read tainted_now < /proc/sys/kernel/tainted
738	if [ "$tainted_now" -eq 0 ];then
739		echo "PASS: $msg"
740	else
741		echo "TAINT: $msg"
742		dmesg
743		ret=1
744	fi
745}
746
747test_queue_stress()
748{
749	read tainted_then < /proc/sys/kernel/tainted
750	local i
751
752        ip netns exec "$nsrouter" nft -f /dev/stdin <<EOF
753flush ruleset
754table inet t {
755	chain forward {
756		type filter hook forward priority 0; policy accept;
757
758		queue flags bypass to numgen random mod 8
759	}
760}
761EOF
762	timeout "$STRESS_TEST_TIMEOUT" ip netns exec "$ns2" \
763		socat -u UDP-LISTEN:12345,fork,pf=ipv4 STDOUT > /dev/null &
764
765	timeout "$STRESS_TEST_TIMEOUT" ip netns exec "$ns3" \
766		socat -u UDP-LISTEN:12345,fork,pf=ipv4 STDOUT > /dev/null &
767
768	for i in $(seq 0 7); do
769		ip netns exec "$nsrouter" timeout "$STRESS_TEST_TIMEOUT" \
770			./nf_queue -q $i -t 2 -O -b > /dev/null &
771	done
772
773	ip netns exec "$ns1" timeout "$STRESS_TEST_TIMEOUT" \
774		ping -q -f 10.0.2.99 > /dev/null 2>&1 &
775	ip netns exec "$ns1" timeout "$STRESS_TEST_TIMEOUT" \
776		ping -q -f 10.0.3.99 > /dev/null 2>&1 &
777	ip netns exec "$ns1" timeout "$STRESS_TEST_TIMEOUT" \
778		ping -q -f "dead:2::99" > /dev/null 2>&1 &
779	ip netns exec "$ns1" timeout "$STRESS_TEST_TIMEOUT" \
780		ping -q -f "dead:3::99" > /dev/null 2>&1 &
781
782	busywait "$BUSYWAIT_TIMEOUT" udp_listener_ready "$ns2" 12345
783	busywait "$BUSYWAIT_TIMEOUT" udp_listener_ready "$ns3" 12345
784
785	for i in $(seq 1 4);do
786		ip netns exec "$ns1" timeout "$STRESS_TEST_TIMEOUT" \
787			socat -u STDIN UDP-DATAGRAM:10.0.2.99:12345 < /dev/zero > /dev/null &
788		ip netns exec "$ns1" timeout "$STRESS_TEST_TIMEOUT" \
789			socat -u STDIN UDP-DATAGRAM:10.0.3.99:12345 < /dev/zero > /dev/null &
790	done
791
792	wait
793
794	check_tainted "concurrent queueing"
795}
796
797test_queue_removal()
798{
799	read tainted_then < /proc/sys/kernel/tainted
800
801	ip netns exec "$ns1" nft -f - <<EOF
802flush ruleset
803table ip filter {
804	chain output {
805		type filter hook output priority 0; policy accept;
806		ip protocol icmp queue num 0
807	}
808}
809EOF
810	ip netns exec "$ns1" ./nf_queue -q 0 -d 30000 &
811	local nfqpid=$!
812
813	busywait "$BUSYWAIT_TIMEOUT" nf_queue_wait "$ns1" 0
814
815	ip netns exec "$ns1" ping -w 2 -f -c 10 127.0.0.1 -q >/dev/null
816	kill $nfqpid
817
818	ip netns exec "$ns1" nft flush ruleset
819
820	check_tainted "queue program exiting while packets queued"
821}
822
823test_queue_bridge()
824{
825	ip -net "$nsrouter" addr flush dev veth0
826	ip -net "$nsrouter" addr flush dev veth1
827
828	ip -net "$nsrouter" link add br0 type bridge
829	ip -net "$nsrouter" link set veth0 master br0
830	ip -net "$nsrouter" link set veth1 master br0
831
832	ip -net "$nsrouter" link set br0 up
833
834	ip -net "$nsrouter" addr add 10.0.2.1/16 dev br0
835	ip -net "$nsrouter" addr add dead:2::1/64 dev br0 nodad
836
837	ip -net "$ns1" addr flush dev eth0
838	ip -net "$ns2" addr flush dev eth0
839
840	ip -net "$ns1" addr add 10.0.1.1/16 dev eth0
841	ip -net "$ns1" addr add dead:2::2/64 dev eth0 nodad
842
843	ip -net "$ns2" addr add 10.0.2.99/16 dev eth0
844	ip -net "$ns2" addr add dead:2::99/64 dev eth0 nodad
845
846	ip netns exec "$nsrouter" nft flush ruleset
847
848	ip netns exec "$nsrouter" sysctl net.ipv6.conf.all.forwarding=0 > /dev/null
849	ip netns exec "$nsrouter" sysctl net.ipv4.conf.veth0.forwarding=0 > /dev/null
850	ip netns exec "$nsrouter" sysctl net.ipv4.conf.veth1.forwarding=0 > /dev/null
851
852	if ! test_ping;then
853		echo "FAIL: netns bridge connectivity" 1>&2
854		exit $ret
855	fi
856
857	load_ruleset "bridge" "filter" 10
858	test_queue 10 "bridge"
859
860	load_ruleset "bridge" "filter2" 20
861	test_queue 20 "bridge"
862}
863
864ip netns exec "$nsrouter" sysctl net.ipv6.conf.all.forwarding=1 > /dev/null
865ip netns exec "$nsrouter" sysctl net.ipv4.conf.veth0.forwarding=1 > /dev/null
866ip netns exec "$nsrouter" sysctl net.ipv4.conf.veth1.forwarding=1 > /dev/null
867ip netns exec "$nsrouter" sysctl net.ipv4.conf.veth2.forwarding=1 > /dev/null
868
869load_ruleset "inet" "filter" 0
870
871if test_ping; then
872	# queue bypass works (rules were skipped, no listener)
873	echo "PASS: ${ns1} can reach ${ns2}"
874else
875	echo "FAIL: ${ns1} cannot reach ${ns2}: $ret" 1>&2
876	exit $ret
877fi
878
879test_queue_blackhole ip
880test_queue_blackhole ip6
881
882# dummy ruleset to add base chains between the
883# queueing rules.  We don't want the second reinject
884# to re-execute the old hooks.
885load_counter_ruleset 10
886
887# we are hooking all: prerouting/input/forward/output/postrouting.
888# we ping ${ns2} from ${ns1} via ${nsrouter} using ipv4 and ipv6, so:
889# 1x icmp prerouting,forward,postrouting -> 3 queue events (6 incl. reply).
890# 1x icmp prerouting,input,output postrouting -> 4 queue events incl. reply.
891# so we expect that userspace program receives 10 packets.
892test_queue 10 "inet"
893
894# same.  We queue to a second program as well.
895load_ruleset "inet" "filter2" 20
896test_queue 20 "inet"
897ip netns exec "$ns1" nft flush ruleset
898
899test_tcp_forward
900test_tcp_localhost
901test_tcp_localhost_connectclose
902test_tcp_localhost_requeue
903test_sctp_forward
904test_sctp_output
905test_udp_nat_race
906test_udp_gro_ct
907test_queue_stress
908
909# should be last, adds vrf device in ns1 and changes routes
910test_icmp_vrf
911test_queue_removal
912
913# turns router into a bridge
914test_queue_bridge
915
916exit $ret
917