xref: /linux/tools/testing/selftests/net/netfilter/nft_concat_range.sh (revision 07fdad3a93756b872da7b53647715c48d0f4a2d0)
1#!/bin/bash
2# SPDX-License-Identifier: GPL-2.0
3#
4# nft_concat_range.sh - Tests for sets with concatenation of ranged fields
5#
6# Copyright (c) 2019 Red Hat GmbH
7#
8# Author: Stefano Brivio <sbrivio@redhat.com>
9#
10# shellcheck disable=SC2154,SC2034,SC2016,SC2030,SC2031,SC2317
11# ^ Configuration and templates sourced with eval, counters reused in subshells
12
13source lib.sh
14
15# Available test groups:
16# - reported_issues: check for issues that were reported in the past
17# - correctness: check that packets match given entries, and only those
18# - correctness_large: same but with additional non-matching entries
19# - concurrency: attempt races between insertion, deletion and lookup
20# - timeout: check that packets match entries until they expire
21# - performance: estimate matching rate, compare with rbtree and hash baselines
22TESTS="reported_issues correctness correctness_large concurrency timeout"
23
24[ -n "$NFT_CONCAT_RANGE_TESTS" ] && TESTS="${NFT_CONCAT_RANGE_TESTS}"
25
26# Set types, defined by TYPE_ variables below
27TYPES="net_port port_net net6_port port_proto net6_port_mac net6_port_mac_proto
28       net_port_net net_mac mac_net net_mac_icmp net6_mac_icmp
29       net6_port_net6_port net_port_mac_proto_net"
30
31# Reported bugs, also described by TYPE_ variables below
32BUGS="flush_remove_add reload net_port_proto_match avx2_mismatch doublecreate"
33
34# List of possible paths to pktgen script from kernel tree for performance tests
35PKTGEN_SCRIPT_PATHS="
36	../../../../../samples/pktgen/pktgen_bench_xmit_mode_netif_receive.sh
37	pktgen/pktgen_bench_xmit_mode_netif_receive.sh"
38
39# Definition of set types:
40# display	display text for test report
41# type_spec	nftables set type specifier
42# chain_spec	nftables type specifier for rules mapping to set
43# dst		call sequence of format_*() functions for destination fields
44# src		call sequence of format_*() functions for source fields
45# start		initial integer used to generate addresses and ports
46# count		count of entries to generate and match
47# src_delta	number summed to destination generator for source fields
48# tools		list of tools for correctness and timeout tests, any can be used
49# proto		L4 protocol of test packets
50#
51# race_repeat	race attempts per thread, 0 disables concurrency test for type
52# flood_tools	list of tools for concurrency tests, any can be used
53# flood_proto	L4 protocol of test packets for concurrency tests
54# flood_spec	nftables type specifier for concurrency tests
55#
56# perf_duration	duration of single pktgen injection test
57# perf_spec	nftables type specifier for performance tests
58# perf_dst	format_*() functions for destination fields in performance test
59# perf_src	format_*() functions for source fields in performance test
60# perf_entries	number of set entries for performance test
61# perf_proto	L3 protocol of test packets
62TYPE_net_port="
63display		net,port
64type_spec	ipv4_addr . inet_service
65chain_spec	ip daddr . udp dport
66dst		addr4 port
67src
68start		1
69count		5
70src_delta	2000
71tools		sendip bash
72proto		udp
73
74race_repeat	3
75flood_tools	iperf3 iperf netperf
76flood_proto	udp
77flood_spec	ip daddr . udp dport
78
79perf_duration	5
80perf_spec	ip daddr . udp dport
81perf_dst	addr4 port
82perf_src
83perf_entries	1000
84perf_proto	ipv4
85"
86
87TYPE_port_net="
88display		port,net
89type_spec	inet_service . ipv4_addr
90chain_spec	udp dport . ip daddr
91dst		port addr4
92src
93start		1
94count		5
95src_delta	2000
96tools		sendip socat bash
97proto		udp
98
99race_repeat	3
100flood_tools	iperf3 iperf netperf
101flood_proto	udp
102flood_spec	udp dport . ip daddr
103
104perf_duration	5
105perf_spec	udp dport . ip daddr
106perf_dst	port addr4
107perf_src
108perf_entries	100
109perf_proto	ipv4
110"
111
112TYPE_net6_port="
113display		net6,port
114type_spec	ipv6_addr . inet_service
115chain_spec	ip6 daddr . udp dport
116dst		addr6 port
117src
118start		10
119count		5
120src_delta	2000
121tools		sendip socat bash
122proto		udp6
123
124race_repeat	3
125flood_tools	iperf3 iperf netperf
126flood_proto	tcp6
127flood_spec	ip6 daddr . udp dport
128
129perf_duration	5
130perf_spec	ip6 daddr . udp dport
131perf_dst	addr6 port
132perf_src
133perf_entries	1000
134perf_proto	ipv6
135"
136
137TYPE_port_proto="
138display		port,proto
139type_spec	inet_service . inet_proto
140chain_spec	udp dport . meta l4proto
141dst		port proto
142src
143start		1
144count		5
145src_delta	2000
146tools		sendip socat bash
147proto		udp
148
149race_repeat	0
150
151perf_duration	5
152perf_spec	udp dport . meta l4proto
153perf_dst	port proto
154perf_src
155perf_entries	30000
156perf_proto	ipv4
157"
158
159TYPE_net6_port_mac="
160display		net6,port,mac
161type_spec	ipv6_addr . inet_service . ether_addr
162chain_spec	ip6 daddr . udp dport . ether saddr
163dst		addr6 port
164src		mac
165start		10
166count		5
167src_delta	2000
168tools		sendip socat bash
169proto		udp6
170
171race_repeat	0
172
173perf_duration	5
174perf_spec	ip6 daddr . udp dport . ether daddr
175perf_dst	addr6 port mac
176perf_src
177perf_entries	10
178perf_proto	ipv6
179"
180
181TYPE_net6_port_mac_proto="
182display		net6,port,mac,proto
183type_spec	ipv6_addr . inet_service . ether_addr . inet_proto
184chain_spec	ip6 daddr . udp dport . ether saddr . meta l4proto
185dst		addr6 port
186src		mac proto
187start		10
188count		5
189src_delta	2000
190tools		sendip socat bash
191proto		udp6
192
193race_repeat	0
194
195perf_duration	5
196perf_spec	ip6 daddr . udp dport . ether daddr . meta l4proto
197perf_dst	addr6 port mac proto
198perf_src
199perf_entries	1000
200perf_proto	ipv6
201"
202
203TYPE_net_port_net="
204display		net,port,net
205type_spec	ipv4_addr . inet_service . ipv4_addr
206chain_spec	ip daddr . udp dport . ip saddr
207dst		addr4 port
208src		addr4
209start		1
210count		5
211src_delta	2000
212tools		sendip socat bash
213proto		udp
214
215race_repeat	3
216flood_tools	iperf3 iperf netperf
217flood_proto	tcp
218flood_spec	ip daddr . udp dport . ip saddr
219
220perf_duration	0
221"
222
223TYPE_net6_port_net6_port="
224display		net6,port,net6,port
225type_spec	ipv6_addr . inet_service . ipv6_addr . inet_service
226chain_spec	ip6 daddr . udp dport . ip6 saddr . udp sport
227dst		addr6 port
228src		addr6 port
229start		10
230count		5
231src_delta	2000
232tools		sendip socat
233proto		udp6
234
235race_repeat	3
236flood_tools	iperf3 iperf netperf
237flood_proto	tcp6
238flood_spec	ip6 daddr . tcp dport . ip6 saddr . tcp sport
239
240perf_duration	0
241"
242
243TYPE_net_port_mac_proto_net="
244display		net,port,mac,proto,net
245type_spec	ipv4_addr . inet_service . ether_addr . inet_proto . ipv4_addr
246chain_spec	ip daddr . udp dport . ether saddr . meta l4proto . ip saddr
247dst		addr4 port
248src		mac proto addr4
249start		1
250count		5
251src_delta	2000
252tools		sendip socat bash
253proto		udp
254
255race_repeat	0
256
257perf_duration	0
258"
259
260TYPE_net_mac="
261display		net,mac
262type_spec	ipv4_addr . ether_addr
263chain_spec	ip daddr . ether saddr
264dst		addr4
265src		mac
266start		1
267count		5
268src_delta	2000
269tools		sendip socat bash
270proto		udp
271
272race_repeat	0
273
274perf_duration	5
275perf_spec	ip daddr . ether daddr
276perf_dst	addr4 mac
277perf_src
278perf_entries	1000
279perf_proto	ipv4
280"
281
282TYPE_mac_net="
283display		mac,net
284type_spec	ether_addr . ipv4_addr
285chain_spec	ether saddr . ip saddr
286dst
287src		mac addr4
288start		1
289count		5
290src_delta	2000
291tools		sendip socat bash
292proto		udp
293
294race_repeat	0
295
296perf_duration	0
297"
298
299TYPE_net_mac_icmp="
300display		net,mac - ICMP
301type_spec	ipv4_addr . ether_addr
302chain_spec	ip daddr . ether saddr
303dst		addr4
304src		mac
305start		1
306count		5
307src_delta	2000
308tools		ping
309proto		icmp
310
311race_repeat	0
312
313perf_duration	0
314"
315
316TYPE_net6_mac_icmp="
317display		net6,mac - ICMPv6
318type_spec	ipv6_addr . ether_addr
319chain_spec	ip6 daddr . ether saddr
320dst		addr6
321src		mac
322start		10
323count		50
324src_delta	2000
325tools		ping
326proto		icmp6
327
328race_repeat	0
329
330perf_duration	0
331"
332
333TYPE_net_port_proto_net="
334display		net,port,proto,net
335type_spec	ipv4_addr . inet_service . inet_proto . ipv4_addr
336chain_spec	ip daddr . udp dport . meta l4proto . ip saddr
337dst		addr4 port proto
338src		addr4
339start		1
340count		5
341src_delta	2000
342tools		sendip socat
343proto		udp
344
345race_repeat	3
346flood_tools	iperf3 iperf netperf
347flood_proto	tcp
348flood_spec	ip daddr . tcp dport . meta l4proto . ip saddr
349
350perf_duration	0
351"
352
353# Definition of tests for bugs reported in the past:
354# display	display text for test report
355TYPE_flush_remove_add="
356display		Add two elements, flush, re-add
357"
358
359TYPE_reload="
360display		net,mac with reload
361type_spec	ipv4_addr . ether_addr
362chain_spec	ip daddr . ether saddr
363dst		addr4
364src		mac
365start		1
366count		1
367src_delta	2000
368tools		sendip socat bash
369proto		udp
370
371race_repeat	0
372
373perf_duration	0
374"
375
376TYPE_net_port_proto_match="
377display		net,port,proto
378type_spec	ipv4_addr . inet_service . inet_proto
379chain_spec	ip daddr . udp dport . meta l4proto
380dst		addr4 port proto
381src
382start		1
383count		9
384src_delta	9
385tools		sendip bash
386proto		udp
387
388race_repeat	0
389
390perf_duration	0
391"
392
393TYPE_avx2_mismatch="
394display		avx2 false match
395type_spec	inet_proto . ipv6_addr
396chain_spec	meta l4proto . ip6 daddr
397dst		proto addr6
398src
399start		1
400count		1
401src_delta	1
402tools		ping
403proto		icmp6
404
405race_repeat	0
406
407perf_duration	0
408"
409
410
411TYPE_doublecreate="
412display		cannot create same element twice
413type_spec	ipv4_addr . ipv4_addr
414chain_spec	ip saddr . ip daddr
415dst		addr4
416proto		icmp
417
418race_repeat	0
419
420perf_duration	0
421"
422
423# Set template for all tests, types and rules are filled in depending on test
424set_template='
425flush ruleset
426
427table inet filter {
428	counter test {
429		packets 0 bytes 0
430	}
431
432	set test {
433		type ${type_spec}
434		counter
435		flags interval,timeout
436	}
437
438	chain input {
439		type filter hook prerouting priority 0; policy accept;
440		${chain_spec} @test counter name \"test\"
441	}
442}
443
444table netdev perf {
445	counter test {
446		packets 0 bytes 0
447	}
448
449	counter match {
450		packets 0 bytes 0
451	}
452
453	set test {
454		type ${type_spec}
455		flags interval
456	}
457
458	set norange {
459		type ${type_spec}
460	}
461
462	set noconcat {
463		type ${type_spec%% *}
464		flags interval
465	}
466
467	chain test {
468		type filter hook ingress device veth_a priority 0;
469	}
470}
471'
472
473err_buf=
474info_buf=
475
476# Append string to error buffer
477err() {
478	err_buf="${err_buf}${1}
479"
480}
481
482# Append string to information buffer
483info() {
484	info_buf="${info_buf}${1}
485"
486}
487
488# Flush error buffer to stdout
489err_flush() {
490	printf "%s" "${err_buf}"
491	err_buf=
492}
493
494# Flush information buffer to stdout
495info_flush() {
496	printf "%s" "${info_buf}"
497	info_buf=
498}
499
500# Setup veth pair: this namespace receives traffic, B generates it
501setup_veth() {
502	ip netns add B
503	ip link add veth_a type veth peer name veth_b || return 1
504
505	ip link set veth_a up
506	ip link set veth_b netns B
507
508	ip -n B link set veth_b up
509
510	ip addr add dev veth_a 10.0.0.1
511	ip route add default dev veth_a
512
513	ip -6 addr add fe80::1/64 dev veth_a nodad
514	ip -6 addr add 2001:db8::1/64 dev veth_a nodad
515	ip -6 route add default dev veth_a
516
517	ip -n B route add default dev veth_b
518
519	ip -6 -n B addr add fe80::2/64 dev veth_b nodad
520	ip -6 -n B addr add 2001:db8::2/64 dev veth_b nodad
521	ip -6 -n B route add default dev veth_b
522
523	B() {
524		ip netns exec B "$@" >/dev/null 2>&1
525	}
526}
527
528# Fill in set template and initialise set
529setup_set() {
530	eval "echo \"${set_template}\"" | nft -f -
531}
532
533# Check that at least one of the needed tools is available
534check_tools() {
535	[ -z "${tools}" ] && return 0
536
537	__tools=
538	for tool in ${tools}; do
539		__tools="${__tools} ${tool}"
540
541		command -v "${tool}" >/dev/null && return 0
542	done
543	err "need one of:${__tools}, skipping" && return 1
544}
545
546# Set up function to send ICMP packets
547setup_send_icmp() {
548	send_icmp() {
549		B ping -c1 -W1 "${dst_addr4}" >/dev/null 2>&1
550	}
551}
552
553# Set up function to send ICMPv6 packets
554setup_send_icmp6() {
555	if command -v ping6 >/dev/null; then
556		send_icmp6() {
557			ip -6 addr add "${dst_addr6}" dev veth_a nodad \
558				2>/dev/null
559			B ping6 -q -c1 -W1 "${dst_addr6}"
560		}
561	else
562		send_icmp6() {
563			ip -6 addr add "${dst_addr6}" dev veth_a nodad \
564				2>/dev/null
565			B ping -q -6 -c1 -W1 "${dst_addr6}"
566		}
567	fi
568}
569
570# Set up function to send single UDP packets on IPv4
571setup_send_udp() {
572	if command -v sendip >/dev/null; then
573		send_udp() {
574			[ -n "${src_port}" ] && src_port="-us ${src_port}"
575			[ -n "${dst_port}" ] && dst_port="-ud ${dst_port}"
576			[ -n "${src_addr4}" ] && src_addr4="-is ${src_addr4}"
577
578			# shellcheck disable=SC2086 # sendip needs split options
579			B sendip -p ipv4 -p udp ${src_addr4} ${src_port} \
580						${dst_port} "${dst_addr4}"
581
582			src_port=
583			dst_port=
584			src_addr4=
585		}
586	elif command -v socat -v >/dev/null; then
587		send_udp() {
588			if [ -n "${src_addr4}" ]; then
589				B ip addr add "${src_addr4}" dev veth_b
590				__socatbind=",bind=${src_addr4}"
591				if [ -n "${src_port}" ];then
592					__socatbind="${__socatbind}:${src_port}"
593				fi
594			fi
595
596			ip addr add "${dst_addr4}" dev veth_a 2>/dev/null
597			[ -z "${dst_port}" ] && dst_port=12345
598
599			echo "test4" | B socat -t 0.01 STDIN UDP4-DATAGRAM:"$dst_addr4":"$dst_port""${__socatbind}"
600
601			src_addr4=
602			src_port=
603		}
604	elif [ -z "$(bash -c 'type -p')" ]; then
605		send_udp() {
606			ip addr add "${dst_addr4}" dev veth_a 2>/dev/null
607			if [ -n "${src_addr4}" ]; then
608				B ip addr add "${src_addr4}/16" dev veth_b
609				B ip route add default dev veth_b
610			fi
611
612			B bash -c "echo > /dev/udp/${dst_addr4}/${dst_port}"
613
614			if [ -n "${src_addr4}" ]; then
615				B ip addr del "${src_addr4}/16" dev veth_b
616			fi
617			src_addr4=
618		}
619	else
620		return 1
621	fi
622}
623
624# Set up function to send single UDP packets on IPv6
625setup_send_udp6() {
626	if command -v sendip >/dev/null; then
627		send_udp6() {
628			[ -n "${src_port}" ] && src_port="-us ${src_port}"
629			[ -n "${dst_port}" ] && dst_port="-ud ${dst_port}"
630			if [ -n "${src_addr6}" ]; then
631				src_addr6="-6s ${src_addr6}"
632			else
633				src_addr6="-6s 2001:db8::2"
634			fi
635			ip -6 addr add "${dst_addr6}" dev veth_a nodad \
636				2>/dev/null
637
638			# shellcheck disable=SC2086 # this needs split options
639			B sendip -p ipv6 -p udp ${src_addr6} ${src_port} \
640						${dst_port} "${dst_addr6}"
641
642			src_port=
643			dst_port=
644			src_addr6=
645		}
646	elif command -v socat -v >/dev/null; then
647		send_udp6() {
648			ip -6 addr add "${dst_addr6}" dev veth_a nodad \
649				2>/dev/null
650
651			__socatbind6=
652
653			if [ -n "${src_addr6}" ]; then
654				B ip addr add "${src_addr6}" dev veth_b nodad
655
656				__socatbind6=",bind=[${src_addr6}]"
657
658				if [ -n "${src_port}" ] ;then
659					__socatbind6="${__socatbind6}:${src_port}"
660				fi
661			fi
662
663			echo "test6" | B socat -t 0.01 STDIN UDP6-DATAGRAM:["$dst_addr6"]:"$dst_port""${__socatbind6}"
664		}
665	elif [ -z "$(bash -c 'type -p')" ]; then
666		send_udp6() {
667			ip -6 addr add "${dst_addr6}" dev veth_a nodad \
668				2>/dev/null
669			B ip addr add "${src_addr6}" dev veth_b nodad
670			B bash -c "echo > /dev/udp/${dst_addr6}/${dst_port}"
671			ip -6 addr del "${dst_addr6}" dev veth_a 2>/dev/null
672		}
673	else
674		return 1
675	fi
676}
677
678listener_ready()
679{
680	port="$1"
681	ss -lnt -o "sport = :$port" | grep -q "$port"
682}
683
684# Set up function to send TCP traffic on IPv4
685setup_flood_tcp() {
686	if command -v iperf3 >/dev/null; then
687		flood_tcp() {
688			local n_port="${dst_port}"
689			[ -n "${dst_port}" ] && dst_port="-p ${dst_port}"
690			if [ -n "${src_addr4}" ]; then
691				B ip addr add "${src_addr4}/16" dev veth_b
692				src_addr4="-B ${src_addr4}"
693			else
694				B ip addr add dev veth_b 10.0.0.2
695				src_addr4="-B 10.0.0.2"
696			fi
697			if [ -n "${src_port}" ]; then
698				src_port="--cport ${src_port}"
699			fi
700			B ip route add default dev veth_b 2>/dev/null
701			ip addr add "${dst_addr4}" dev veth_a 2>/dev/null
702
703			# shellcheck disable=SC2086 # this needs split options
704			iperf3 -s -DB "${dst_addr4}" ${dst_port} >/dev/null 2>&1
705			busywait "$BUSYWAIT_TIMEOUT" listener_ready "$n_port"
706
707			# shellcheck disable=SC2086 # this needs split options
708			B iperf3 -c "${dst_addr4}" ${dst_port} ${src_port} \
709				${src_addr4} -l16 -t 1000
710
711			src_addr4=
712			src_port=
713			dst_port=
714		}
715	elif command -v iperf >/dev/null; then
716		flood_tcp() {
717			local n_port="${dst_port}"
718			[ -n "${dst_port}" ] && dst_port="-p ${dst_port}"
719			if [ -n "${src_addr4}" ]; then
720				B ip addr add "${src_addr4}/16" dev veth_b
721				src_addr4="-B ${src_addr4}"
722			else
723				B ip addr add dev veth_b 10.0.0.2 2>/dev/null
724				src_addr4="-B 10.0.0.2"
725			fi
726			if [ -n "${src_port}" ]; then
727				src_addr4="${src_addr4}:${src_port}"
728			fi
729			B ip route add default dev veth_b
730			ip addr add "${dst_addr4}" dev veth_a 2>/dev/null
731
732			# shellcheck disable=SC2086 # this needs split options
733			iperf -s -DB "${dst_addr4}" ${dst_port} >/dev/null 2>&1
734			busywait "$BUSYWAIT_TIMEOUT" listener_ready "$n_port"
735
736			# shellcheck disable=SC2086 # this needs split options
737			B iperf -c "${dst_addr4}" ${dst_port} ${src_addr4} \
738				-l20 -t 1000
739
740			src_addr4=
741			src_port=
742			dst_port=
743		}
744	elif command -v netperf >/dev/null; then
745		flood_tcp() {
746			local n_port="${dst_port}"
747			[ -n "${dst_port}" ] && dst_port="-p ${dst_port}"
748			if [ -n "${src_addr4}" ]; then
749				B ip addr add "${src_addr4}/16" dev veth_b
750			else
751				B ip addr add dev veth_b 10.0.0.2
752				src_addr4="10.0.0.2"
753			fi
754			if [ -n "${src_port}" ]; then
755				dst_port="${dst_port},${src_port}"
756			fi
757			B ip route add default dev veth_b
758			ip addr add "${dst_addr4}" dev veth_a 2>/dev/null
759
760			# shellcheck disable=SC2086 # this needs split options
761			netserver -4 ${dst_port} -L "${dst_addr4}" \
762				>/dev/null 2>&1
763			busywait "$BUSYWAIT_TIMEOUT" listener_ready "${n_port}"
764
765			# shellcheck disable=SC2086 # this needs split options
766			B netperf -4 -H "${dst_addr4}" ${dst_port} \
767				-L "${src_addr4}" -l 1000 -t TCP_STREAM
768
769			src_addr4=
770			src_port=
771			dst_port=
772		}
773	else
774		return 1
775	fi
776}
777
778# Set up function to send TCP traffic on IPv6
779setup_flood_tcp6() {
780	if command -v iperf3 >/dev/null; then
781		flood_tcp6() {
782			local n_port="${dst_port}"
783			[ -n "${dst_port}" ] && dst_port="-p ${dst_port}"
784			if [ -n "${src_addr6}" ]; then
785				B ip addr add "${src_addr6}" dev veth_b nodad
786				src_addr6="-B ${src_addr6}"
787			else
788				src_addr6="-B 2001:db8::2"
789			fi
790			if [ -n "${src_port}" ]; then
791				src_port="--cport ${src_port}"
792			fi
793			B ip route add default dev veth_b
794			ip -6 addr add "${dst_addr6}" dev veth_a nodad \
795				2>/dev/null
796
797			# shellcheck disable=SC2086 # this needs split options
798			iperf3 -s -DB "${dst_addr6}" ${dst_port} >/dev/null 2>&1
799			busywait "$BUSYWAIT_TIMEOUT" listener_ready "${n_port}"
800
801			# shellcheck disable=SC2086 # this needs split options
802			B iperf3 -c "${dst_addr6}" ${dst_port} \
803				${src_port} ${src_addr6} -l16 -t 1000
804
805			src_addr6=
806			src_port=
807			dst_port=
808		}
809	elif command -v iperf >/dev/null; then
810		flood_tcp6() {
811			local n_port="${dst_port}"
812			[ -n "${dst_port}" ] && dst_port="-p ${dst_port}"
813			if [ -n "${src_addr6}" ]; then
814				B ip addr add "${src_addr6}" dev veth_b nodad
815				src_addr6="-B ${src_addr6}"
816			else
817				src_addr6="-B 2001:db8::2"
818			fi
819			if [ -n "${src_port}" ]; then
820				src_addr6="${src_addr6}:${src_port}"
821			fi
822			B ip route add default dev veth_b
823			ip -6 addr add "${dst_addr6}" dev veth_a nodad \
824				2>/dev/null
825
826			# shellcheck disable=SC2086 # this needs split options
827			iperf -s -VDB "${dst_addr6}" ${dst_port} >/dev/null 2>&1
828			busywait "$BUSYWAIT_TIMEOUT" listener_ready "$n_port"
829
830			# shellcheck disable=SC2086 # this needs split options
831			B iperf -c "${dst_addr6}" -V ${dst_port} \
832				${src_addr6} -l1 -t 1000
833
834			src_addr6=
835			src_port=
836			dst_port=
837		}
838	elif command -v netperf >/dev/null; then
839		flood_tcp6() {
840			local n_port="${dst_port}"
841			[ -n "${dst_port}" ] && dst_port="-p ${dst_port}"
842			if [ -n "${src_addr6}" ]; then
843				B ip addr add "${src_addr6}" dev veth_b nodad
844			else
845				src_addr6="2001:db8::2"
846			fi
847			if [ -n "${src_port}" ]; then
848				dst_port="${dst_port},${src_port}"
849			fi
850			B ip route add default dev veth_b
851			ip -6 addr add "${dst_addr6}" dev veth_a nodad \
852				2>/dev/null
853
854			# shellcheck disable=SC2086 # this needs split options
855			netserver -6 ${dst_port} -L "${dst_addr6}" \
856				>/dev/null 2>&1
857			busywait "$BUSYWAIT_TIMEOUT" listener_ready "$n_port"
858
859			# shellcheck disable=SC2086 # this needs split options
860			B netperf -6 -H "${dst_addr6}" ${dst_port} \
861				-L "${src_addr6}" -l 1000 -t TCP_STREAM
862
863			src_addr6=
864			src_port=
865			dst_port=
866		}
867	else
868		return 1
869	fi
870}
871
872# Set up function to send UDP traffic on IPv4
873setup_flood_udp() {
874	if command -v iperf3 >/dev/null; then
875		flood_udp() {
876			local n_port="${dst_port}"
877			[ -n "${dst_port}" ] && dst_port="-p ${dst_port}"
878			if [ -n "${src_addr4}" ]; then
879				B ip addr add "${src_addr4}/16" dev veth_b
880				src_addr4="-B ${src_addr4}"
881			else
882				B ip addr add dev veth_b 10.0.0.2 2>/dev/null
883				src_addr4="-B 10.0.0.2"
884			fi
885			if [ -n "${src_port}" ]; then
886				src_port="--cport ${src_port}"
887			fi
888			B ip route add default dev veth_b
889			ip addr add "${dst_addr4}" dev veth_a 2>/dev/null
890
891			# shellcheck disable=SC2086 # this needs split options
892			iperf3 -s -DB "${dst_addr4}" ${dst_port}
893			busywait "$BUSYWAIT_TIMEOUT" listener_ready "$n_port"
894
895			# shellcheck disable=SC2086 # this needs split options
896			B iperf3 -u -c "${dst_addr4}" -Z -b 100M -l16 -t1000 \
897				${dst_port} ${src_port} ${src_addr4}
898
899			src_addr4=
900			src_port=
901			dst_port=
902		}
903	elif command -v iperf >/dev/null; then
904		flood_udp() {
905			local n_port="${dst_port}"
906			[ -n "${dst_port}" ] && dst_port="-p ${dst_port}"
907			if [ -n "${src_addr4}" ]; then
908				B ip addr add "${src_addr4}/16" dev veth_b
909				src_addr4="-B ${src_addr4}"
910			else
911				B ip addr add dev veth_b 10.0.0.2
912				src_addr4="-B 10.0.0.2"
913			fi
914			if [ -n "${src_port}" ]; then
915				src_addr4="${src_addr4}:${src_port}"
916			fi
917			B ip route add default dev veth_b
918			ip addr add "${dst_addr4}" dev veth_a 2>/dev/null
919
920			# shellcheck disable=SC2086 # this needs split options
921			iperf -u -sDB "${dst_addr4}" ${dst_port} >/dev/null 2>&1
922			busywait "$BUSYWAIT_TIMEOUT" listener_ready "$n_port"
923
924			# shellcheck disable=SC2086 # this needs split options
925			B iperf -u -c "${dst_addr4}" -b 100M -l1 -t1000 \
926				${dst_port} ${src_addr4}
927
928			src_addr4=
929			src_port=
930			dst_port=
931		}
932	elif command -v netperf >/dev/null; then
933		flood_udp() {
934			local n_port="${dst_port}"
935			[ -n "${dst_port}" ] && dst_port="-p ${dst_port}"
936			if [ -n "${src_addr4}" ]; then
937				B ip addr add "${src_addr4}/16" dev veth_b
938			else
939				B ip addr add dev veth_b 10.0.0.2
940				src_addr4="10.0.0.2"
941			fi
942			if [ -n "${src_port}" ]; then
943				dst_port="${dst_port},${src_port}"
944			fi
945			B ip route add default dev veth_b
946			ip addr add "${dst_addr4}" dev veth_a 2>/dev/null
947
948			# shellcheck disable=SC2086 # this needs split options
949			netserver -4 ${dst_port} -L "${dst_addr4}" \
950				>/dev/null 2>&1
951			busywait "$BUSYWAIT_TIMEOUT" listener_ready "$n_port"
952
953			# shellcheck disable=SC2086 # this needs split options
954			B netperf -4 -H "${dst_addr4}" ${dst_port} \
955				-L "${src_addr4}" -l 1000 -t UDP_STREAM
956
957			src_addr4=
958			src_port=
959			dst_port=
960		}
961	else
962		return 1
963	fi
964}
965
966# Find pktgen script and set up function to start pktgen injection
967setup_perf() {
968	for pktgen_script_path in ${PKTGEN_SCRIPT_PATHS} __notfound; do
969		command -v "${pktgen_script_path}" >/dev/null && break
970	done
971	[ "${pktgen_script_path}" = "__notfound" ] && return 1
972
973	perf_ipv4() {
974		${pktgen_script_path} -s80 \
975			-i veth_a -d "${dst_addr4}" -p "${dst_port}" \
976			-m "${dst_mac}" \
977			-t $(($(nproc) / 5 + 1)) -b10000 -n0 2>/dev/null &
978		perf_pid=$!
979	}
980	perf_ipv6() {
981		IP6=6 ${pktgen_script_path} -s100 \
982			-i veth_a -d "${dst_addr6}" -p "${dst_port}" \
983			-m "${dst_mac}" \
984			-t $(($(nproc) / 5 + 1)) -b10000 -n0 2>/dev/null &
985		perf_pid=$!
986	}
987}
988
989# Clean up before each test
990cleanup() {
991	nft reset counter inet filter test	>/dev/null 2>&1
992	nft flush ruleset			>/dev/null 2>&1
993	ip link del dummy0			2>/dev/null
994	ip route del default			2>/dev/null
995	ip -6 route del default			2>/dev/null
996	ip netns pids B				2>/dev/null | xargs kill 2>/dev/null
997	ip netns del B				2>/dev/null
998	ip link del veth_a			2>/dev/null
999	timeout=
1000	killall iperf3				2>/dev/null
1001	killall iperf				2>/dev/null
1002	killall netperf				2>/dev/null
1003	killall netserver			2>/dev/null
1004}
1005
1006cleanup_exit() {
1007	cleanup
1008	rm -f "$tmp"
1009}
1010
1011# Entry point for setup functions
1012setup() {
1013	if [ "$(id -u)" -ne 0 ]; then
1014		echo "  need to run as root"
1015		exit ${ksft_skip}
1016	fi
1017
1018	cleanup
1019	check_tools || return 1
1020	for arg do
1021		if ! eval setup_"${arg}"; then
1022			err "  ${arg} not supported"
1023			return 1
1024		fi
1025	done
1026}
1027
1028# Format integer into IPv4 address, summing 10.0.0.5 (arbitrary) to it
1029format_addr4() {
1030	a=$((${1} + 16777216 * 10 + 5))
1031	printf "%i.%i.%i.%i"						\
1032	       "$((a / 16777216))" "$((a % 16777216 / 65536))"	\
1033	       "$((a % 65536 / 256))" "$((a % 256))"
1034}
1035
1036# Format integer into IPv6 address, summing 2001:db8:: to it
1037format_addr6() {
1038	printf "2001:db8::%04x:%04x" "$((${1} / 65536))" "$((${1} % 65536))"
1039}
1040
1041# Format integer into EUI-48 address, summing 00:01:00:00:00:00 to it
1042format_mac() {
1043	printf "00:01:%02x:%02x:%02x:%02x" \
1044	       "$((${1} / 16777216))" "$((${1} % 16777216 / 65536))"	\
1045	       "$((${1} % 65536 / 256))" "$((${1} % 256))"
1046}
1047
1048# Format integer into port, avoid 0 port
1049format_port() {
1050	printf "%i" "$((${1} % 65534 + 1))"
1051}
1052
1053# Drop suffixed '6' from L4 protocol, if any
1054format_proto() {
1055	printf "%s" "${proto}" | tr -d 6
1056}
1057
1058# Format destination and source fields into nft concatenated type
1059format() {
1060	__start=
1061	__end=
1062	__expr="{ "
1063
1064	for f in ${dst}; do
1065		[ "${__expr}" != "{ " ] && __expr="${__expr} . "
1066
1067		__start="$(eval format_"${f}" "${start}")"
1068		__end="$(eval format_"${f}" "${end}")"
1069
1070		if [ "${f}" = "proto" ]; then
1071			__expr="${__expr}${__start}"
1072		else
1073			__expr="${__expr}${__start}-${__end}"
1074		fi
1075	done
1076	for f in ${src}; do
1077		[ "${__expr}" != "{ " ] && __expr="${__expr} . "
1078
1079		__start="$(eval format_"${f}" "${srcstart}")"
1080		__end="$(eval format_"${f}" "${srcend}")"
1081
1082		if [ "${f}" = "proto" ]; then
1083			__expr="${__expr}${__start}"
1084		else
1085			__expr="${__expr}${__start}-${__end}"
1086		fi
1087	done
1088
1089	if [ -n "${timeout}" ]; then
1090		echo "${__expr} timeout ${timeout}s }"
1091	else
1092		echo "${__expr} }"
1093	fi
1094}
1095
1096# Format destination and source fields into nft type, start element only
1097format_norange() {
1098	__expr="{ "
1099
1100	for f in ${dst}; do
1101		[ "${__expr}" != "{ " ] && __expr="${__expr} . "
1102
1103		__expr="${__expr}$(eval format_"${f}" "${start}")"
1104	done
1105	for f in ${src}; do
1106		__expr="${__expr} . $(eval format_"${f}" "${start}")"
1107	done
1108
1109	echo "${__expr} }"
1110}
1111
1112# Format first destination field into nft type
1113format_noconcat() {
1114	for f in ${dst}; do
1115		__start="$(eval format_"${f}" "${start}")"
1116		__end="$(eval format_"${f}" "${end}")"
1117
1118		if [ "${f}" = "proto" ]; then
1119			echo "{ ${__start} }"
1120		else
1121			echo "{ ${__start}-${__end} }"
1122		fi
1123		return
1124	done
1125}
1126
1127# Add single entry to 'test' set in 'inet filter' table
1128add() {
1129	if ! nft add element inet filter test "${1}"; then
1130		err "Failed to add ${1} given ruleset:"
1131		err "$(nft -a list ruleset)"
1132		return 1
1133	fi
1134}
1135
1136# Format and output entries for sets in 'netdev perf' table
1137add_perf() {
1138	if [ "${1}" = "test" ]; then
1139		echo "add element netdev perf test $(format)"
1140	elif [ "${1}" = "norange" ]; then
1141		echo "add element netdev perf norange $(format_norange)"
1142	elif [ "${1}" = "noconcat" ]; then
1143		echo "add element netdev perf noconcat $(format_noconcat)"
1144	fi
1145}
1146
1147# Add single entry to 'norange' set in 'netdev perf' table
1148add_perf_norange() {
1149	if ! nft add element netdev perf norange "${1}"; then
1150		err "Failed to add ${1} given ruleset:"
1151		err "$(nft -a list ruleset)"
1152		return 1
1153	fi
1154}
1155
1156# Add single entry to 'noconcat' set in 'netdev perf' table
1157add_perf_noconcat() {
1158	if ! nft add element netdev perf noconcat "${1}"; then
1159		err "Failed to add ${1} given ruleset:"
1160		err "$(nft -a list ruleset)"
1161		return 1
1162	fi
1163}
1164
1165# Delete single entry from set
1166del() {
1167	if ! nft delete element inet filter test "${1}"; then
1168		err "Failed to delete ${1} given ruleset:"
1169		err "$(nft -a list ruleset)"
1170		return 1
1171	fi
1172}
1173
1174# Return packet count for elem $1 from 'test' counter in 'inet filter' table
1175count_packets() {
1176	found=0
1177	for token in $(nft reset element inet filter test "${1}" ); do
1178		[ ${found} -eq 1 ] && echo "${token}" && return
1179		[ "${token}" = "packets" ] && found=1
1180	done
1181}
1182
1183# Return packet count from 'test' counter in 'inet filter' table
1184count_packets_nomatch() {
1185	found=0
1186	for token in $(nft list counter inet filter test); do
1187		[ ${found} -eq 1 ] && echo "${token}" && return
1188		[ "${token}" = "packets" ] && found=1
1189	done
1190}
1191
1192# Return packet count from 'test' counter in 'netdev perf' table
1193count_perf_packets() {
1194	found=0
1195	for token in $(nft list counter netdev perf test); do
1196		[ ${found} -eq 1 ] && echo "${token}" && return
1197		[ "${token}" = "packets" ] && found=1
1198	done
1199}
1200
1201# Set MAC addresses, send traffic according to specifier
1202flood() {
1203	ip link set veth_a address "$(format_mac "${1}")"
1204	ip -n B link set veth_b address "$(format_mac "${2}")"
1205
1206	for f in ${dst}; do
1207		eval dst_"$f"=\$\(format_\$f "${1}"\)
1208	done
1209	for f in ${src}; do
1210		eval src_"$f"=\$\(format_\$f "${2}"\)
1211	done
1212	eval flood_\$proto
1213}
1214
1215# Set MAC addresses, start pktgen injection
1216perf() {
1217	dst_mac="$(format_mac "${1}")"
1218	ip link set veth_a address "${dst_mac}"
1219
1220	for f in ${dst}; do
1221		eval dst_"$f"=\$\(format_\$f "${1}"\)
1222	done
1223	for f in ${src}; do
1224		eval src_"$f"=\$\(format_\$f "${2}"\)
1225	done
1226	eval perf_\$perf_proto
1227}
1228
1229# Set MAC addresses, send single packet, check that it matches, reset counter
1230send_match() {
1231	local elem="$1"
1232
1233	shift
1234
1235	ip link set veth_a address "$(format_mac "${1}")"
1236	ip -n B link set veth_b address "$(format_mac "${2}")"
1237
1238	for f in ${dst}; do
1239		eval dst_"$f"=\$\(format_\$f "${1}"\)
1240	done
1241	for f in ${src}; do
1242		eval src_"$f"=\$\(format_\$f "${2}"\)
1243	done
1244	eval send_\$proto
1245	if [ "$(count_packets "$elem")" != "1" ]; then
1246		err "${proto} packet to:"
1247		err "  $(for f in ${dst}; do
1248			 eval format_\$f "${1}"; printf ' '; done)"
1249		err "from:"
1250		err "  $(for f in ${src}; do
1251			 eval format_\$f "${2}"; printf ' '; done)"
1252		err "should have matched ruleset:"
1253		err "$(nft -a list ruleset)"
1254		return 1
1255	fi
1256	nft reset counter inet filter test >/dev/null
1257}
1258
1259# Set MAC addresses, send single packet, check that it doesn't match
1260send_nomatch() {
1261	ip link set veth_a address "$(format_mac "${1}")"
1262	ip -n B link set veth_b address "$(format_mac "${2}")"
1263
1264	for f in ${dst}; do
1265		eval dst_"$f"=\$\(format_\$f "${1}"\)
1266	done
1267	for f in ${src}; do
1268		eval src_"$f"=\$\(format_\$f "${2}"\)
1269	done
1270	eval send_\$proto
1271	if [ "$(count_packets_nomatch)" != "0" ]; then
1272		err "${proto} packet to:"
1273		err "  $(for f in ${dst}; do
1274			 eval format_\$f "${1}"; printf ' '; done)"
1275		err "from:"
1276		err "  $(for f in ${src}; do
1277			 eval format_\$f "${2}"; printf ' '; done)"
1278		err "should not have matched ruleset:"
1279		err "$(nft -a list ruleset)"
1280		return 1
1281	fi
1282}
1283
1284maybe_send_nomatch() {
1285	local elem="$1"
1286	local what="$4"
1287
1288	[ $((RANDOM%20)) -gt 0 ] && return
1289
1290	dst_addr4="$2"
1291	dst_port="$3"
1292	send_udp
1293
1294	if [ "$(count_packets_nomatch)" != "0" ]; then
1295		err "Packet to $dst_addr4:$dst_port did match $what"
1296		err "$(nft -a list ruleset)"
1297		return 1
1298	fi
1299}
1300
1301maybe_send_match() {
1302	local elem="$1"
1303	local what="$4"
1304
1305	[ $((RANDOM%20)) -gt 0 ] && return
1306
1307	dst_addr4="$2"
1308	dst_port="$3"
1309	send_udp
1310
1311	if [ "$(count_packets "{ $elem }")" != "1" ]; then
1312		err "Packet to $dst_addr4:$dst_port did not match $what"
1313		err "$(nft -a list ruleset)"
1314		return 1
1315	fi
1316	nft reset counter inet filter test >/dev/null
1317	nft reset element inet filter test "{ $elem }" >/dev/null
1318}
1319
1320# Correctness test template:
1321# - add ranged element, check that packets match it
1322# - check that packets outside range don't match it
1323# - remove some elements, check that packets don't match anymore
1324test_correctness_main() {
1325	range_size=1
1326
1327	send_nomatch $((end + 1)) $((end + 1 + src_delta)) || return 1
1328
1329	for i in $(seq "${start}" $((start + count))); do
1330		local elem=""
1331
1332		end=$((start + range_size))
1333
1334		# Avoid negative or zero-sized port ranges
1335		if [ $((end / 65534)) -gt $((start / 65534)) ]; then
1336			start=${end}
1337			end=$((end + 1))
1338		fi
1339		srcstart=$((start + src_delta))
1340		srcend=$((end + src_delta))
1341
1342		elem="$(format)"
1343		add "$elem" || return 1
1344		for j in $(seq "$start" $((range_size / 2 + 1)) ${end}); do
1345			send_match "$elem" "${j}" $((j + src_delta)) || return 1
1346		done
1347		send_nomatch $((end + 1)) $((end + 1 + src_delta)) || return 1
1348
1349		# Delete elements now and then
1350		if [ $((i % 3)) -eq 0 ]; then
1351			del "$elem" || return 1
1352			for j in $(seq "$start" \
1353				   $((range_size / 2 + 1)) ${end}); do
1354				send_nomatch "${j}" $((j + src_delta)) \
1355					|| return 1
1356			done
1357		fi
1358
1359		range_size=$((range_size + 1))
1360		start=$((end + range_size))
1361	done
1362}
1363
1364test_correctness() {
1365	setup veth send_"${proto}" set || return ${ksft_skip}
1366
1367	test_correctness_main
1368}
1369
1370# Repeat the correctness tests, but add extra non-matching entries.
1371# This exercises the more compact '4 bit group' representation that
1372# gets picked when the default 8-bit representation exceed
1373# NFT_PIPAPO_LT_SIZE_HIGH bytes of memory.
1374# See usage of NFT_PIPAPO_LT_SIZE_HIGH in pipapo_lt_bits_adjust().
1375#
1376# The format() helper is way too slow when generating lots of
1377# entries so its not used here.
1378test_correctness_large() {
1379	setup veth send_"${proto}" set || return ${ksft_skip}
1380	# number of dummy (filler) entries to add.
1381	local dcount=16385
1382
1383	(
1384	echo -n "add element inet filter test { "
1385
1386	case "$type_spec" in
1387	"ether_addr . ipv4_addr")
1388		for i in $(seq 1 $dcount); do
1389			[ $i -gt 1 ] && echo ", "
1390			format_mac $((1000000 + i))
1391			printf ". 172.%i.%i.%i " $((RANDOM%256)) $((RANDOM%256)) $((i%256))
1392		done
1393		;;
1394	"inet_proto . ipv6_addr")
1395		for i in $(seq 1 $dcount); do
1396			[ $i -gt 1 ] && echo ", "
1397			printf "%i . " $((RANDOM%256))
1398			format_addr6 $((1000000 + i))
1399		done
1400		;;
1401	"inet_service . inet_proto")
1402		# smaller key sizes, need more entries to hit the
1403		# 4-bit threshold.
1404		dcount=65536
1405		for i in $(seq 1 $dcount); do
1406			local proto=$((RANDOM%256))
1407
1408			# Test uses UDP to match, as it also fails when matching
1409			# an entry that doesn't exist, so skip 'udp' entries
1410			# to not trigger a wrong failure.
1411			[ $proto -eq 17 ] && proto=18
1412			[ $i -gt 1 ] && echo ", "
1413			printf "%i . %i " $(((i%65534) + 1)) $((proto))
1414		done
1415		;;
1416	"inet_service . ipv4_addr")
1417		dcount=32768
1418		for i in $(seq 1 $dcount); do
1419			[ $i -gt 1 ] && echo ", "
1420			printf "%i . 172.%i.%i.%i " $(((RANDOM%65534) + 1)) $((RANDOM%256)) $((RANDOM%256)) $((i%256))
1421		done
1422		;;
1423	"ipv4_addr . ether_addr")
1424		for i in $(seq 1 $dcount); do
1425			[ $i -gt 1 ] && echo ", "
1426			printf "172.%i.%i.%i . " $((RANDOM%256)) $((RANDOM%256)) $((i%256))
1427			format_mac $((1000000 + i))
1428		done
1429		;;
1430	"ipv4_addr . inet_service")
1431		dcount=32768
1432		for i in $(seq 1 $dcount); do
1433			[ $i -gt 1 ] && echo ", "
1434			printf "172.%i.%i.%i . %i" $((RANDOM%256)) $((RANDOM%256)) $((i%256)) $(((RANDOM%65534) + 1))
1435		done
1436		;;
1437	"ipv4_addr . inet_service . ether_addr . inet_proto . ipv4_addr")
1438		dcount=65536
1439		for i in $(seq 1 $dcount); do
1440			[ $i -gt 1 ] && echo ", "
1441			printf "172.%i.%i.%i . %i . " $((RANDOM%256)) $((RANDOM%256)) $((i%256)) $(((RANDOM%65534) + 1))
1442			format_mac $((1000000 + i))
1443			printf ". %i . 192.168.%i.%i" $((RANDOM%256)) $((RANDOM%256)) $((i%256))
1444		done
1445		;;
1446	"ipv4_addr . inet_service . inet_proto")
1447		for i in $(seq 1 $dcount); do
1448			[ $i -gt 1 ] && echo ", "
1449			printf "172.%i.%i.%i . %i . %i " $((RANDOM%256)) $((RANDOM%256)) $((i%256)) $(((RANDOM%65534) + 1)) $((RANDOM%256))
1450		done
1451		;;
1452	"ipv4_addr . inet_service . inet_proto . ipv4_addr")
1453		for i in $(seq 1 $dcount); do
1454			[ $i -gt 1 ] && echo ", "
1455			printf "172.%i.%i.%i . %i . %i . 192.168.%i.%i " $((RANDOM%256)) $((RANDOM%256)) $((i%256)) $(((RANDOM%65534) + 1)) $((RANDOM%256)) $((RANDOM%256)) $((RANDOM%256))
1456		done
1457		;;
1458	"ipv4_addr . inet_service . ipv4_addr")
1459		dcount=32768
1460		for i in $(seq 1 $dcount); do
1461			[ $i -gt 1 ] && echo ", "
1462			printf "172.%i.%i.%i . %i . 192.168.%i.%i " $((RANDOM%256)) $((RANDOM%256)) $((i%256)) $(((RANDOM%65534) + 1)) $((RANDOM%256)) $((RANDOM%256))
1463		done
1464		;;
1465	"ipv6_addr . ether_addr")
1466		for i in $(seq 1 $dcount); do
1467			[ $i -gt 1 ] && echo ", "
1468			format_addr6 $((i + 1000000))
1469			echo -n " . "
1470			format_mac $((1000000 + i))
1471		done
1472		;;
1473	"ipv6_addr . inet_service")
1474		dcount=32768
1475		for i in $(seq 1 $dcount); do
1476			[ $i -gt 1 ] && echo ", "
1477			format_addr6 $((i + 1000000))
1478			echo -n " .  $(((RANDOM%65534) + 1))"
1479		done
1480		;;
1481	"ipv6_addr . inet_service . ether_addr")
1482		dcount=32768
1483		for i in $(seq 1 $dcount); do
1484			[ $i -gt 1 ] && echo ", "
1485			format_addr6 $((i + 1000000))
1486			echo -n " .  $(((RANDOM%65534) + 1)) . "
1487			format_mac $((i + 1000000))
1488		done
1489		;;
1490	"ipv6_addr . inet_service . ether_addr . inet_proto")
1491		dcount=65536
1492		for i in $(seq 1 $dcount); do
1493			[ $i -gt 1 ] && echo ", "
1494			format_addr6 $((i + 1000000))
1495			echo -n " .  $(((RANDOM%65534) + 1)) . "
1496			format_mac $((i + 1000000))
1497			echo -n " .  $((RANDOM%256))"
1498		done
1499		;;
1500	"ipv6_addr . inet_service . ipv6_addr . inet_service")
1501		dcount=32768
1502		for i in $(seq 1 $dcount); do
1503			[ $i -gt 1 ] && echo ", "
1504			format_addr6 $((i + 1000000))
1505			echo -n " .  $(((RANDOM%65534) + 1)) . "
1506			format_addr6 $((i + 2123456))
1507			echo -n " .  $((RANDOM%256))"
1508		done
1509		;;
1510	*)
1511		"Unhandled $type_spec"
1512		return 1
1513	esac
1514	echo -n "}"
1515
1516	) | nft -f - || return 1
1517
1518	test_correctness_main
1519}
1520
1521# Concurrency test template:
1522# - add all the elements
1523# - start a thread for each physical thread that:
1524#   - adds all the elements
1525#   - flushes the set
1526#   - adds all the elements
1527#   - flushes the entire ruleset
1528#   - adds the set back
1529#   - adds all the elements
1530#   - delete all the elements
1531test_concurrency() {
1532	proto=${flood_proto}
1533	tools=${flood_tools}
1534	chain_spec=${flood_spec}
1535	setup veth flood_"${proto}" set || return ${ksft_skip}
1536
1537	range_size=1
1538	cstart=${start}
1539	flood_pids=
1540	for i in $(seq "$start" $((start + count))); do
1541		end=$((start + range_size))
1542		srcstart=$((start + src_delta))
1543		srcend=$((end + src_delta))
1544
1545		add "$(format)" || return 1
1546
1547		flood "${i}" $((i + src_delta)) & flood_pids="${flood_pids} $!"
1548
1549		range_size=$((range_size + 1))
1550		start=$((end + range_size))
1551	done
1552
1553	sleep $((RANDOM%10))
1554
1555	pids=
1556	for c in $(seq 1 "$(nproc)"); do (
1557		for r in $(seq 1 "${race_repeat}"); do
1558			range_size=1
1559
1560			# $start needs to be local to this subshell
1561			# shellcheck disable=SC2030
1562			start=${cstart}
1563			for i in $(seq "$start" $((start + count))); do
1564				end=$((start + range_size))
1565				srcstart=$((start + src_delta))
1566				srcend=$((end + src_delta))
1567
1568				add "$(format)" 2>/dev/null
1569
1570				range_size=$((range_size + 1))
1571				start=$((end + range_size))
1572			done
1573
1574			nft flush inet filter test 2>/dev/null
1575
1576			range_size=1
1577			start=${cstart}
1578			for i in $(seq "$start" $((start + count))); do
1579				end=$((start + range_size))
1580				srcstart=$((start + src_delta))
1581				srcend=$((end + src_delta))
1582
1583				add "$(format)" 2>/dev/null
1584
1585				range_size=$((range_size + 1))
1586				start=$((end + range_size))
1587			done
1588
1589			nft flush ruleset
1590			setup set 2>/dev/null
1591
1592			range_size=1
1593			start=${cstart}
1594			for i in $(seq "$start" $((start + count))); do
1595				end=$((start + range_size))
1596				srcstart=$((start + src_delta))
1597				srcend=$((end + src_delta))
1598
1599				add "$(format)" 2>/dev/null
1600
1601				range_size=$((range_size + 1))
1602				start=$((end + range_size))
1603			done
1604
1605			range_size=1
1606			start=${cstart}
1607			for i in $(seq "$start" $((start + count))); do
1608				end=$((start + range_size))
1609				srcstart=$((start + src_delta))
1610				srcend=$((end + src_delta))
1611
1612				del "$(format)" 2>/dev/null
1613
1614				range_size=$((range_size + 1))
1615				start=$((end + range_size))
1616			done
1617		done
1618	) & pids="${pids} $!"
1619	done
1620
1621	# shellcheck disable=SC2046,SC2086 # word splitting wanted here
1622	wait $(for pid in ${pids}; do echo ${pid}; done)
1623	# shellcheck disable=SC2046,SC2086
1624	kill $(for pid in ${flood_pids}; do echo ${pid}; done) 2>/dev/null
1625	# shellcheck disable=SC2046,SC2086
1626	wait $(for pid in ${flood_pids}; do echo ${pid}; done) 2>/dev/null
1627
1628	return 0
1629}
1630
1631# Timeout test template:
1632# - add all the elements with 3s timeout while checking that packets match
1633# - wait 3s after the last insertion, check that packets don't match any entry
1634test_timeout() {
1635	setup veth send_"${proto}" set || return ${ksft_skip}
1636
1637	timeout=3
1638
1639	[ "$KSFT_MACHINE_SLOW" = "yes" ] && timeout=8
1640
1641	range_size=1
1642	for i in $(seq "$start" $((start + count))); do
1643		local elem=""
1644
1645		end=$((start + range_size))
1646		srcstart=$((start + src_delta))
1647		srcend=$((end + src_delta))
1648
1649		elem="$(format)"
1650		add "$elem" || return 1
1651
1652		for j in $(seq "$start" $((range_size / 2 + 1)) ${end}); do
1653			send_match "$elem" "${j}" $((j + src_delta)) || return 1
1654		done
1655
1656		range_size=$((range_size + 1))
1657		start=$((end + range_size))
1658	done
1659	sleep $timeout
1660	for i in $(seq "$start" $((start + count))); do
1661		end=$((start + range_size))
1662		srcstart=$((start + src_delta))
1663		srcend=$((end + src_delta))
1664
1665		for j in $(seq "$start" $((range_size / 2 + 1)) ${end}); do
1666			send_nomatch "${j}" $((j + src_delta)) || return 1
1667		done
1668
1669		range_size=$((range_size + 1))
1670		start=$((end + range_size))
1671	done
1672}
1673
1674# Performance test template:
1675# - add concatenated ranged entries
1676# - add non-ranged concatenated entries (for hash set matching rate baseline)
1677# - add ranged entries with first field only (for rbhash baseline)
1678# - start pktgen injection directly on device rx path of this namespace
1679# - measure drop only rate, hash and rbtree baselines, then matching rate
1680test_performance() {
1681	chain_spec=${perf_spec}
1682	dst="${perf_dst}"
1683	src="${perf_src}"
1684	setup veth perf set || return ${ksft_skip}
1685
1686	first=${start}
1687	range_size=1
1688	for set in test norange noconcat; do
1689		start=${first}
1690		for i in $(seq "$start" $((start + perf_entries))); do
1691			end=$((start + range_size))
1692			srcstart=$((start + src_delta))
1693			srcend=$((end + src_delta))
1694
1695			if [ $((end / 65534)) -gt $((start / 65534)) ]; then
1696				start=${end}
1697				end=$((end + 1))
1698			elif [ "$start" -eq "$end" ]; then
1699				end=$((start + 1))
1700			fi
1701
1702			add_perf ${set}
1703
1704			start=$((end + range_size))
1705		done > "${tmp}"
1706		nft -f "${tmp}"
1707	done
1708
1709	perf $((end - 1)) "$srcstart"
1710
1711	sleep 2
1712
1713	nft add rule netdev perf test counter name \"test\" drop
1714	nft reset counter netdev perf test >/dev/null 2>&1
1715	sleep "${perf_duration}"
1716	pps="$(printf %10s $(($(count_perf_packets) / perf_duration)))"
1717	info "    baseline (drop from netdev hook):            ${pps}pps"
1718	handle="$(nft -a list chain netdev perf test | grep counter)"
1719	handle="${handle##* }"
1720	nft delete rule netdev perf test handle "${handle}"
1721
1722	nft add rule "netdev perf test ${chain_spec} @norange \
1723		counter name \"test\" drop"
1724	nft reset counter netdev perf test >/dev/null 2>&1
1725	sleep "${perf_duration}"
1726	pps="$(printf %10s $(($(count_perf_packets) / perf_duration)))"
1727	info "    baseline hash (non-ranged entries):          ${pps}pps"
1728	handle="$(nft -a list chain netdev perf test | grep counter)"
1729	handle="${handle##* }"
1730	nft delete rule netdev perf test handle "${handle}"
1731
1732	nft add rule "netdev perf test ${chain_spec%%. *} @noconcat \
1733		counter name \"test\" drop"
1734	nft reset counter netdev perf test >/dev/null 2>&1
1735	sleep "${perf_duration}"
1736	pps="$(printf %10s $(($(count_perf_packets) / perf_duration)))"
1737	info "    baseline rbtree (match on first field only): ${pps}pps"
1738	handle="$(nft -a list chain netdev perf test | grep counter)"
1739	handle="${handle##* }"
1740	nft delete rule netdev perf test handle "${handle}"
1741
1742	nft add rule "netdev perf test ${chain_spec} @test \
1743		counter name \"test\" drop"
1744	nft reset counter netdev perf test >/dev/null 2>&1
1745	sleep "${perf_duration}"
1746	pps="$(printf %10s $(($(count_perf_packets) / perf_duration)))"
1747	p5="$(printf %5s "${perf_entries}")"
1748	info "    set with ${p5} full, ranged entries:         ${pps}pps"
1749	kill "${perf_pid}"
1750}
1751
1752test_bug_flush_remove_add() {
1753	rounds=100
1754	[ "$KSFT_MACHINE_SLOW" = "yes" ] && rounds=10
1755
1756	set_cmd='{ set s { type ipv4_addr . inet_service; flags interval; }; }'
1757	elem1='{ 10.0.0.1 . 22-25, 10.0.0.1 . 10-20 }'
1758	elem2='{ 10.0.0.1 . 10-20, 10.0.0.1 . 22-25 }'
1759	for i in $(seq 1 $rounds); do
1760		nft add table t "$set_cmd"	|| return ${ksft_skip}
1761		nft add element t s "$elem1"	2>/dev/null || return 1
1762		nft flush set t s		2>/dev/null || return 1
1763		nft add element t s "$elem2"	2>/dev/null || return 1
1764	done
1765	nft flush ruleset
1766}
1767
1768# - add ranged element, check that packets match it
1769# - reload the set, check packets still match
1770test_bug_reload() {
1771	setup veth send_"${proto}" set || return ${ksft_skip}
1772	rstart=${start}
1773
1774	range_size=1
1775	for i in $(seq "${start}" $((start + count))); do
1776		end=$((start + range_size))
1777
1778		# Avoid negative or zero-sized port ranges
1779		if [ $((end / 65534)) -gt $((start / 65534)) ]; then
1780			start=${end}
1781			end=$((end + 1))
1782		fi
1783		srcstart=$((start + src_delta))
1784		srcend=$((end + src_delta))
1785
1786		add "$(format)" || return 1
1787		range_size=$((range_size + 1))
1788		start=$((end + range_size))
1789	done
1790
1791	# check kernel does allocate pcpu sctrach map
1792	# for reload with no elemet add/delete
1793	( echo flush set inet filter test ;
1794	  nft list set inet filter test ) | nft -f -
1795
1796	start=${rstart}
1797	range_size=1
1798
1799	for i in $(seq "${start}" $((start + count))); do
1800		end=$((start + range_size))
1801
1802		# Avoid negative or zero-sized port ranges
1803		if [ $((end / 65534)) -gt $((start / 65534)) ]; then
1804			start=${end}
1805			end=$((end + 1))
1806		fi
1807		srcstart=$((start + src_delta))
1808		srcend=$((end + src_delta))
1809
1810		for j in $(seq "$start" $((range_size / 2 + 1)) ${end}); do
1811			send_match "$(format)" "${j}" $((j + src_delta)) || return 1
1812		done
1813
1814		range_size=$((range_size + 1))
1815		start=$((end + range_size))
1816	done
1817
1818	nft flush ruleset
1819}
1820
1821# - add ranged element, check that packets match it
1822# - delete element again, check it is gone
1823test_bug_net_port_proto_match() {
1824	setup veth send_"${proto}" set || return ${ksft_skip}
1825	rstart=${start}
1826
1827	range_size=1
1828	for i in $(seq 1 10); do
1829		for j in $(seq 1 20) ; do
1830			local dport=$j
1831
1832			elem=$(printf "10.%d.%d.0/24 . %d-%d0 . 6-17 " ${i} ${j} ${dport} "$((dport+1))")
1833
1834			# too slow, do not test all addresses
1835			maybe_send_nomatch "$elem" $(printf "10.%d.%d.1" $i $j) $(printf "%d1" $((dport+1))) "before add" || return 1
1836
1837			nft "add element inet filter test { $elem }" || return 1
1838
1839			maybe_send_match "$elem" $(printf "10.%d.%d.1" $i $j) $(printf "%d" $dport) "after add" || return 1
1840
1841			nft "get element inet filter test { $elem }" | grep -q "$elem"
1842			if [ $? -ne 0 ];then
1843				local got=$(nft "get element inet filter test { $elem }")
1844				err "post-add: should have returned $elem but got $got"
1845				return 1
1846			fi
1847
1848			maybe_send_nomatch "$elem" $(printf "10.%d.%d.1" $i $j) $(printf "%d1" $((dport+1))) "out-of-range" || return 1
1849		done
1850	done
1851
1852	# recheck after set was filled
1853	for i in $(seq 1 10); do
1854		for j in $(seq 1 20) ; do
1855			local dport=$j
1856
1857			elem=$(printf "10.%d.%d.0/24 . %d-%d0 . 6-17 " ${i} ${j} ${dport} "$((dport+1))")
1858
1859			nft "get element inet filter test { $elem }" | grep -q "$elem"
1860			if [ $? -ne 0 ];then
1861				local got=$(nft "get element inet filter test { $elem }")
1862				err "post-fill: should have returned $elem but got $got"
1863				return 1
1864			fi
1865
1866			maybe_send_match "$elem" $(printf "10.%d.%d.1" $i $j) $(printf "%d" $dport) "recheck" || return 1
1867			maybe_send_nomatch "$elem" $(printf "10.%d.%d.1" $i $j) $(printf "%d1" $((dport+1))) "recheck out-of-range" || return 1
1868		done
1869	done
1870
1871	# random del and re-fetch
1872	for i in $(seq 1 10); do
1873		for j in $(seq 1 20) ; do
1874			local rnd=$((RANDOM%10))
1875			local dport=$j
1876			local got=""
1877
1878			elem=$(printf "10.%d.%d.0/24 . %d-%d0 . 6-17 " ${i} ${j} ${dport} "$((dport+1))")
1879			if [ $rnd -gt 0 ];then
1880				continue
1881			fi
1882
1883			nft "delete element inet filter test { $elem }"
1884			got=$(nft "get element inet filter test { $elem }" 2>/dev/null)
1885			if [ $? -eq 0 ];then
1886				err "post-delete: query for $elem returned $got instead of error."
1887				return 1
1888			fi
1889
1890			maybe_send_nomatch "$elem" $(printf "10.%d.%d.1" $i $j) $(printf "%d" $dport) "match after deletion" || return 1
1891		done
1892	done
1893
1894	nft flush ruleset
1895}
1896
1897test_bug_avx2_mismatch()
1898{
1899	setup veth send_"${proto}" set || return ${ksft_skip}
1900
1901	local a1="fe80:dead:01ff:0a02:0b03:6007:8009:a001"
1902	local a2="fe80:dead:01fe:0a02:0b03:6007:8009:a001"
1903
1904	nft "add element inet filter test { icmpv6 . $a1 }"
1905
1906	dst_addr6="$a2"
1907	send_icmp6
1908
1909	if [ "$(count_packets "{ icmpv6 . $a1 }")" -gt "0" ]; then
1910		err "False match for $a2"
1911		return 1
1912	fi
1913}
1914
1915test_bug_doublecreate()
1916{
1917	local elements="1.2.3.4 . 1.2.4.1, 1.2.4.1 . 1.2.3.4"
1918	local ret=1
1919	local i
1920
1921	setup veth send_"${proto}" set || return ${ksft_skip}
1922
1923	add "{ $elements }" || return 1
1924	# expected to work: 'add' on existing should be no-op.
1925	add "{ $elements }" || return 1
1926
1927	# 'create' should return an error.
1928	if nft create element inet filter test "{ $elements }" 2>/dev/null; then
1929		err "Could create an existing element"
1930		return 1
1931	fi
1932nft -f - <<EOF 2>/dev/null
1933flush set inet filter test
1934create element inet filter test { $elements }
1935create element inet filter test { $elements }
1936EOF
1937	ret=$?
1938	if [ $ret -eq 0 ]; then
1939		err "Could create element twice in one transaction"
1940		err "$(nft -a list ruleset)"
1941		return 1
1942	fi
1943
1944nft -f - <<EOF 2>/dev/null
1945flush set inet filter test
1946create element inet filter test { $elements }
1947EOF
1948	ret=$?
1949	if [ $ret -ne 0 ]; then
1950		err "Could not flush and re-create element in one transaction"
1951		return 1
1952	fi
1953
1954	return 0
1955}
1956
1957test_reported_issues() {
1958	eval test_bug_"${subtest}"
1959}
1960
1961# Run everything in a separate network namespace
1962[ "${1}" != "run" ] && { unshare -n "${0}" run; exit $?; }
1963tmp="$(mktemp)"
1964trap cleanup_exit EXIT
1965
1966# Entry point for test runs
1967passed=0
1968for name in ${TESTS}; do
1969	printf "TEST: %s\n" "$(echo "$name" | tr '_' ' ')"
1970	if [ "${name}" = "reported_issues" ]; then
1971		SUBTESTS="${BUGS}"
1972	else
1973		SUBTESTS="${TYPES}"
1974	fi
1975
1976	for subtest in ${SUBTESTS}; do
1977		eval desc=\$TYPE_"${subtest}"
1978		IFS='
1979'
1980		for __line in ${desc}; do
1981			# shellcheck disable=SC2086
1982			eval ${__line%%	*}=\"${__line##*	}\";
1983		done
1984		IFS='
1985'
1986
1987		if [ "${name}" = "concurrency" ] && \
1988		   [ "${race_repeat}" = "0" ]; then
1989			continue
1990		fi
1991		if [ "${name}" = "performance" ] && \
1992		   [ "${perf_duration}" = "0" ]; then
1993			continue
1994		fi
1995
1996		[ "$KSFT_MACHINE_SLOW" = "yes" ] && count=1
1997
1998		printf "  %-32s  " "${display}"
1999		tthen=$(date +%s)
2000		eval test_"${name}"
2001		ret=$?
2002
2003		tnow=$(date +%s)
2004		printf "%5ds%-30s" $((tnow-tthen))
2005
2006		if [ $ret -eq 0 ]; then
2007			printf "[ OK ]\n"
2008			info_flush
2009			passed=$((passed + 1))
2010		elif [ $ret -eq 1 ]; then
2011			printf "[FAIL]\n"
2012			err_flush
2013			exit 1
2014		elif [ $ret -eq ${ksft_skip} ]; then
2015			printf "[SKIP]\n"
2016			err_flush
2017		fi
2018	done
2019done
2020
2021[ ${passed} -eq 0 ] && exit ${ksft_skip} || exit 0
2022