xref: /linux/tools/testing/selftests/net/netfilter/nft_concat_range.sh (revision f2a3b12b305c7bb72467b2a56d19a4587b6007f9)
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 insert_overlap"
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
423TYPE_insert_overlap="
424display		reject overlapping range on add
425type_spec	ipv4_addr . ipv4_addr
426chain_spec	ip saddr . ip daddr
427dst		addr4
428proto		icmp
429
430race_repeat	0
431
432perf_duration	0
433"
434
435# Set template for all tests, types and rules are filled in depending on test
436set_template='
437flush ruleset
438
439table inet filter {
440	counter test {
441		packets 0 bytes 0
442	}
443
444	set test {
445		type ${type_spec}
446		counter
447		flags interval,timeout
448	}
449
450	chain input {
451		type filter hook prerouting priority 0; policy accept;
452		${chain_spec} @test counter name \"test\"
453	}
454}
455
456table netdev perf {
457	counter test {
458		packets 0 bytes 0
459	}
460
461	counter match {
462		packets 0 bytes 0
463	}
464
465	set test {
466		type ${type_spec}
467		flags interval
468	}
469
470	set norange {
471		type ${type_spec}
472	}
473
474	set noconcat {
475		type ${type_spec%% *}
476		flags interval
477	}
478
479	chain test {
480		type filter hook ingress device veth_a priority 0;
481	}
482}
483'
484
485err_buf=
486info_buf=
487
488# Append string to error buffer
489err() {
490	err_buf="${err_buf}${1}
491"
492}
493
494# Append string to information buffer
495info() {
496	info_buf="${info_buf}${1}
497"
498}
499
500# Flush error buffer to stdout
501err_flush() {
502	printf "%s" "${err_buf}"
503	err_buf=
504}
505
506# Flush information buffer to stdout
507info_flush() {
508	printf "%s" "${info_buf}"
509	info_buf=
510}
511
512# Setup veth pair: this namespace receives traffic, B generates it
513setup_veth() {
514	ip netns add B
515	ip link add veth_a type veth peer name veth_b || return 1
516
517	ip link set veth_a up
518	ip link set veth_b netns B
519
520	ip -n B link set veth_b up
521
522	ip addr add dev veth_a 10.0.0.1
523	ip route add default dev veth_a
524
525	ip -6 addr add fe80::1/64 dev veth_a nodad
526	ip -6 addr add 2001:db8::1/64 dev veth_a nodad
527	ip -6 route add default dev veth_a
528
529	ip -n B route add default dev veth_b
530
531	ip -6 -n B addr add fe80::2/64 dev veth_b nodad
532	ip -6 -n B addr add 2001:db8::2/64 dev veth_b nodad
533	ip -6 -n B route add default dev veth_b
534
535	B() {
536		ip netns exec B "$@" >/dev/null 2>&1
537	}
538}
539
540# Fill in set template and initialise set
541setup_set() {
542	eval "echo \"${set_template}\"" | nft -f -
543}
544
545# Check that at least one of the needed tools is available
546check_tools() {
547	[ -z "${tools}" ] && return 0
548
549	__tools=
550	for tool in ${tools}; do
551		__tools="${__tools} ${tool}"
552
553		command -v "${tool}" >/dev/null && return 0
554	done
555	err "need one of:${__tools}, skipping" && return 1
556}
557
558# Set up function to send ICMP packets
559setup_send_icmp() {
560	send_icmp() {
561		B ping -c1 -W1 "${dst_addr4}" >/dev/null 2>&1
562	}
563}
564
565# Set up function to send ICMPv6 packets
566setup_send_icmp6() {
567	if command -v ping6 >/dev/null; then
568		send_icmp6() {
569			ip -6 addr add "${dst_addr6}" dev veth_a nodad \
570				2>/dev/null
571			B ping6 -q -c1 -W1 "${dst_addr6}"
572		}
573	else
574		send_icmp6() {
575			ip -6 addr add "${dst_addr6}" dev veth_a nodad \
576				2>/dev/null
577			B ping -q -6 -c1 -W1 "${dst_addr6}"
578		}
579	fi
580}
581
582# Set up function to send single UDP packets on IPv4
583setup_send_udp() {
584	if command -v sendip >/dev/null; then
585		send_udp() {
586			[ -n "${src_port}" ] && src_port="-us ${src_port}"
587			[ -n "${dst_port}" ] && dst_port="-ud ${dst_port}"
588			[ -n "${src_addr4}" ] && src_addr4="-is ${src_addr4}"
589
590			# shellcheck disable=SC2086 # sendip needs split options
591			B sendip -p ipv4 -p udp ${src_addr4} ${src_port} \
592						${dst_port} "${dst_addr4}"
593
594			src_port=
595			dst_port=
596			src_addr4=
597		}
598	elif command -v socat -v >/dev/null; then
599		send_udp() {
600			if [ -n "${src_addr4}" ]; then
601				B ip addr add "${src_addr4}" dev veth_b
602				__socatbind=",bind=${src_addr4}"
603				if [ -n "${src_port}" ];then
604					__socatbind="${__socatbind}:${src_port}"
605				fi
606			fi
607
608			ip addr add "${dst_addr4}" dev veth_a 2>/dev/null
609			[ -z "${dst_port}" ] && dst_port=12345
610
611			echo "test4" | B socat -t 0.01 STDIN UDP4-DATAGRAM:"$dst_addr4":"$dst_port""${__socatbind}"
612
613			src_addr4=
614			src_port=
615		}
616	elif [ -z "$(bash -c 'type -p')" ]; then
617		send_udp() {
618			ip addr add "${dst_addr4}" dev veth_a 2>/dev/null
619			if [ -n "${src_addr4}" ]; then
620				B ip addr add "${src_addr4}/16" dev veth_b
621				B ip route add default dev veth_b
622			fi
623
624			B bash -c "echo > /dev/udp/${dst_addr4}/${dst_port}"
625
626			if [ -n "${src_addr4}" ]; then
627				B ip addr del "${src_addr4}/16" dev veth_b
628			fi
629			src_addr4=
630		}
631	else
632		return 1
633	fi
634}
635
636# Set up function to send single UDP packets on IPv6
637setup_send_udp6() {
638	if command -v sendip >/dev/null; then
639		send_udp6() {
640			[ -n "${src_port}" ] && src_port="-us ${src_port}"
641			[ -n "${dst_port}" ] && dst_port="-ud ${dst_port}"
642			if [ -n "${src_addr6}" ]; then
643				src_addr6="-6s ${src_addr6}"
644			else
645				src_addr6="-6s 2001:db8::2"
646			fi
647			ip -6 addr add "${dst_addr6}" dev veth_a nodad \
648				2>/dev/null
649
650			# shellcheck disable=SC2086 # this needs split options
651			B sendip -p ipv6 -p udp ${src_addr6} ${src_port} \
652						${dst_port} "${dst_addr6}"
653
654			src_port=
655			dst_port=
656			src_addr6=
657		}
658	elif command -v socat -v >/dev/null; then
659		send_udp6() {
660			ip -6 addr add "${dst_addr6}" dev veth_a nodad \
661				2>/dev/null
662
663			__socatbind6=
664
665			if [ -n "${src_addr6}" ]; then
666				B ip addr add "${src_addr6}" dev veth_b nodad
667
668				__socatbind6=",bind=[${src_addr6}]"
669
670				if [ -n "${src_port}" ] ;then
671					__socatbind6="${__socatbind6}:${src_port}"
672				fi
673			fi
674
675			echo "test6" | B socat -t 0.01 STDIN UDP6-DATAGRAM:["$dst_addr6"]:"$dst_port""${__socatbind6}"
676		}
677	elif [ -z "$(bash -c 'type -p')" ]; then
678		send_udp6() {
679			ip -6 addr add "${dst_addr6}" dev veth_a nodad \
680				2>/dev/null
681			B ip addr add "${src_addr6}" dev veth_b nodad
682			B bash -c "echo > /dev/udp/${dst_addr6}/${dst_port}"
683			ip -6 addr del "${dst_addr6}" dev veth_a 2>/dev/null
684		}
685	else
686		return 1
687	fi
688}
689
690listener_ready()
691{
692	port="$1"
693	ss -lnt -o "sport = :$port" | grep -q "$port"
694}
695
696# Set up function to send TCP traffic on IPv4
697setup_flood_tcp() {
698	if command -v iperf3 >/dev/null; then
699		flood_tcp() {
700			local n_port="${dst_port}"
701			[ -n "${dst_port}" ] && dst_port="-p ${dst_port}"
702			if [ -n "${src_addr4}" ]; then
703				B ip addr add "${src_addr4}/16" dev veth_b
704				src_addr4="-B ${src_addr4}"
705			else
706				B ip addr add dev veth_b 10.0.0.2
707				src_addr4="-B 10.0.0.2"
708			fi
709			if [ -n "${src_port}" ]; then
710				src_port="--cport ${src_port}"
711			fi
712			B ip route add default dev veth_b 2>/dev/null
713			ip addr add "${dst_addr4}" dev veth_a 2>/dev/null
714
715			# shellcheck disable=SC2086 # this needs split options
716			iperf3 -s -DB "${dst_addr4}" ${dst_port} >/dev/null 2>&1
717			busywait "$BUSYWAIT_TIMEOUT" listener_ready "$n_port"
718
719			# shellcheck disable=SC2086 # this needs split options
720			B iperf3 -c "${dst_addr4}" ${dst_port} ${src_port} \
721				${src_addr4} -l16 -t 1000
722
723			src_addr4=
724			src_port=
725			dst_port=
726		}
727	elif command -v iperf >/dev/null; then
728		flood_tcp() {
729			local n_port="${dst_port}"
730			[ -n "${dst_port}" ] && dst_port="-p ${dst_port}"
731			if [ -n "${src_addr4}" ]; then
732				B ip addr add "${src_addr4}/16" dev veth_b
733				src_addr4="-B ${src_addr4}"
734			else
735				B ip addr add dev veth_b 10.0.0.2 2>/dev/null
736				src_addr4="-B 10.0.0.2"
737			fi
738			if [ -n "${src_port}" ]; then
739				src_addr4="${src_addr4}:${src_port}"
740			fi
741			B ip route add default dev veth_b
742			ip addr add "${dst_addr4}" dev veth_a 2>/dev/null
743
744			# shellcheck disable=SC2086 # this needs split options
745			iperf -s -DB "${dst_addr4}" ${dst_port} >/dev/null 2>&1
746			busywait "$BUSYWAIT_TIMEOUT" listener_ready "$n_port"
747
748			# shellcheck disable=SC2086 # this needs split options
749			B iperf -c "${dst_addr4}" ${dst_port} ${src_addr4} \
750				-l20 -t 1000
751
752			src_addr4=
753			src_port=
754			dst_port=
755		}
756	elif command -v netperf >/dev/null; then
757		flood_tcp() {
758			local n_port="${dst_port}"
759			[ -n "${dst_port}" ] && dst_port="-p ${dst_port}"
760			if [ -n "${src_addr4}" ]; then
761				B ip addr add "${src_addr4}/16" dev veth_b
762			else
763				B ip addr add dev veth_b 10.0.0.2
764				src_addr4="10.0.0.2"
765			fi
766			if [ -n "${src_port}" ]; then
767				dst_port="${dst_port},${src_port}"
768			fi
769			B ip route add default dev veth_b
770			ip addr add "${dst_addr4}" dev veth_a 2>/dev/null
771
772			# shellcheck disable=SC2086 # this needs split options
773			netserver -4 ${dst_port} -L "${dst_addr4}" \
774				>/dev/null 2>&1
775			busywait "$BUSYWAIT_TIMEOUT" listener_ready "${n_port}"
776
777			# shellcheck disable=SC2086 # this needs split options
778			B netperf -4 -H "${dst_addr4}" ${dst_port} \
779				-L "${src_addr4}" -l 1000 -t TCP_STREAM
780
781			src_addr4=
782			src_port=
783			dst_port=
784		}
785	else
786		return 1
787	fi
788}
789
790# Set up function to send TCP traffic on IPv6
791setup_flood_tcp6() {
792	if command -v iperf3 >/dev/null; then
793		flood_tcp6() {
794			local n_port="${dst_port}"
795			[ -n "${dst_port}" ] && dst_port="-p ${dst_port}"
796			if [ -n "${src_addr6}" ]; then
797				B ip addr add "${src_addr6}" dev veth_b nodad
798				src_addr6="-B ${src_addr6}"
799			else
800				src_addr6="-B 2001:db8::2"
801			fi
802			if [ -n "${src_port}" ]; then
803				src_port="--cport ${src_port}"
804			fi
805			B ip route add default dev veth_b
806			ip -6 addr add "${dst_addr6}" dev veth_a nodad \
807				2>/dev/null
808
809			# shellcheck disable=SC2086 # this needs split options
810			iperf3 -s -DB "${dst_addr6}" ${dst_port} >/dev/null 2>&1
811			busywait "$BUSYWAIT_TIMEOUT" listener_ready "${n_port}"
812
813			# shellcheck disable=SC2086 # this needs split options
814			B iperf3 -c "${dst_addr6}" ${dst_port} \
815				${src_port} ${src_addr6} -l16 -t 1000
816
817			src_addr6=
818			src_port=
819			dst_port=
820		}
821	elif command -v iperf >/dev/null; then
822		flood_tcp6() {
823			local n_port="${dst_port}"
824			[ -n "${dst_port}" ] && dst_port="-p ${dst_port}"
825			if [ -n "${src_addr6}" ]; then
826				B ip addr add "${src_addr6}" dev veth_b nodad
827				src_addr6="-B ${src_addr6}"
828			else
829				src_addr6="-B 2001:db8::2"
830			fi
831			if [ -n "${src_port}" ]; then
832				src_addr6="${src_addr6}:${src_port}"
833			fi
834			B ip route add default dev veth_b
835			ip -6 addr add "${dst_addr6}" dev veth_a nodad \
836				2>/dev/null
837
838			# shellcheck disable=SC2086 # this needs split options
839			iperf -s -VDB "${dst_addr6}" ${dst_port} >/dev/null 2>&1
840			busywait "$BUSYWAIT_TIMEOUT" listener_ready "$n_port"
841
842			# shellcheck disable=SC2086 # this needs split options
843			B iperf -c "${dst_addr6}" -V ${dst_port} \
844				${src_addr6} -l1 -t 1000
845
846			src_addr6=
847			src_port=
848			dst_port=
849		}
850	elif command -v netperf >/dev/null; then
851		flood_tcp6() {
852			local n_port="${dst_port}"
853			[ -n "${dst_port}" ] && dst_port="-p ${dst_port}"
854			if [ -n "${src_addr6}" ]; then
855				B ip addr add "${src_addr6}" dev veth_b nodad
856			else
857				src_addr6="2001:db8::2"
858			fi
859			if [ -n "${src_port}" ]; then
860				dst_port="${dst_port},${src_port}"
861			fi
862			B ip route add default dev veth_b
863			ip -6 addr add "${dst_addr6}" dev veth_a nodad \
864				2>/dev/null
865
866			# shellcheck disable=SC2086 # this needs split options
867			netserver -6 ${dst_port} -L "${dst_addr6}" \
868				>/dev/null 2>&1
869			busywait "$BUSYWAIT_TIMEOUT" listener_ready "$n_port"
870
871			# shellcheck disable=SC2086 # this needs split options
872			B netperf -6 -H "${dst_addr6}" ${dst_port} \
873				-L "${src_addr6}" -l 1000 -t TCP_STREAM
874
875			src_addr6=
876			src_port=
877			dst_port=
878		}
879	else
880		return 1
881	fi
882}
883
884# Set up function to send UDP traffic on IPv4
885setup_flood_udp() {
886	if command -v iperf3 >/dev/null; then
887		flood_udp() {
888			local n_port="${dst_port}"
889			[ -n "${dst_port}" ] && dst_port="-p ${dst_port}"
890			if [ -n "${src_addr4}" ]; then
891				B ip addr add "${src_addr4}/16" dev veth_b
892				src_addr4="-B ${src_addr4}"
893			else
894				B ip addr add dev veth_b 10.0.0.2 2>/dev/null
895				src_addr4="-B 10.0.0.2"
896			fi
897			if [ -n "${src_port}" ]; then
898				src_port="--cport ${src_port}"
899			fi
900			B ip route add default dev veth_b
901			ip addr add "${dst_addr4}" dev veth_a 2>/dev/null
902
903			# shellcheck disable=SC2086 # this needs split options
904			iperf3 -s -DB "${dst_addr4}" ${dst_port}
905			busywait "$BUSYWAIT_TIMEOUT" listener_ready "$n_port"
906
907			# shellcheck disable=SC2086 # this needs split options
908			B iperf3 -u -c "${dst_addr4}" -Z -b 100M -l16 -t1000 \
909				${dst_port} ${src_port} ${src_addr4}
910
911			src_addr4=
912			src_port=
913			dst_port=
914		}
915	elif command -v iperf >/dev/null; then
916		flood_udp() {
917			local n_port="${dst_port}"
918			[ -n "${dst_port}" ] && dst_port="-p ${dst_port}"
919			if [ -n "${src_addr4}" ]; then
920				B ip addr add "${src_addr4}/16" dev veth_b
921				src_addr4="-B ${src_addr4}"
922			else
923				B ip addr add dev veth_b 10.0.0.2
924				src_addr4="-B 10.0.0.2"
925			fi
926			if [ -n "${src_port}" ]; then
927				src_addr4="${src_addr4}:${src_port}"
928			fi
929			B ip route add default dev veth_b
930			ip addr add "${dst_addr4}" dev veth_a 2>/dev/null
931
932			# shellcheck disable=SC2086 # this needs split options
933			iperf -u -sDB "${dst_addr4}" ${dst_port} >/dev/null 2>&1
934			busywait "$BUSYWAIT_TIMEOUT" listener_ready "$n_port"
935
936			# shellcheck disable=SC2086 # this needs split options
937			B iperf -u -c "${dst_addr4}" -b 100M -l1 -t1000 \
938				${dst_port} ${src_addr4}
939
940			src_addr4=
941			src_port=
942			dst_port=
943		}
944	elif command -v netperf >/dev/null; then
945		flood_udp() {
946			local n_port="${dst_port}"
947			[ -n "${dst_port}" ] && dst_port="-p ${dst_port}"
948			if [ -n "${src_addr4}" ]; then
949				B ip addr add "${src_addr4}/16" dev veth_b
950			else
951				B ip addr add dev veth_b 10.0.0.2
952				src_addr4="10.0.0.2"
953			fi
954			if [ -n "${src_port}" ]; then
955				dst_port="${dst_port},${src_port}"
956			fi
957			B ip route add default dev veth_b
958			ip addr add "${dst_addr4}" dev veth_a 2>/dev/null
959
960			# shellcheck disable=SC2086 # this needs split options
961			netserver -4 ${dst_port} -L "${dst_addr4}" \
962				>/dev/null 2>&1
963			busywait "$BUSYWAIT_TIMEOUT" listener_ready "$n_port"
964
965			# shellcheck disable=SC2086 # this needs split options
966			B netperf -4 -H "${dst_addr4}" ${dst_port} \
967				-L "${src_addr4}" -l 1000 -t UDP_STREAM
968
969			src_addr4=
970			src_port=
971			dst_port=
972		}
973	else
974		return 1
975	fi
976}
977
978# Find pktgen script and set up function to start pktgen injection
979setup_perf() {
980	for pktgen_script_path in ${PKTGEN_SCRIPT_PATHS} __notfound; do
981		command -v "${pktgen_script_path}" >/dev/null && break
982	done
983	[ "${pktgen_script_path}" = "__notfound" ] && return 1
984
985	perf_ipv4() {
986		${pktgen_script_path} -s80 \
987			-i veth_a -d "${dst_addr4}" -p "${dst_port}" \
988			-m "${dst_mac}" \
989			-t $(($(nproc) / 5 + 1)) -b10000 -n0 2>/dev/null &
990		perf_pid=$!
991	}
992	perf_ipv6() {
993		IP6=6 ${pktgen_script_path} -s100 \
994			-i veth_a -d "${dst_addr6}" -p "${dst_port}" \
995			-m "${dst_mac}" \
996			-t $(($(nproc) / 5 + 1)) -b10000 -n0 2>/dev/null &
997		perf_pid=$!
998	}
999}
1000
1001# Clean up before each test
1002cleanup() {
1003	nft reset counter inet filter test	>/dev/null 2>&1
1004	nft flush ruleset			>/dev/null 2>&1
1005	ip link del dummy0			2>/dev/null
1006	ip route del default			2>/dev/null
1007	ip -6 route del default			2>/dev/null
1008	ip netns pids B				2>/dev/null | xargs kill 2>/dev/null
1009	ip netns del B				2>/dev/null
1010	ip link del veth_a			2>/dev/null
1011	timeout=
1012	killall iperf3				2>/dev/null
1013	killall iperf				2>/dev/null
1014	killall netperf				2>/dev/null
1015	killall netserver			2>/dev/null
1016}
1017
1018cleanup_exit() {
1019	cleanup
1020	rm -f "$tmp"
1021}
1022
1023# Entry point for setup functions
1024setup() {
1025	if [ "$(id -u)" -ne 0 ]; then
1026		echo "  need to run as root"
1027		exit ${ksft_skip}
1028	fi
1029
1030	cleanup
1031	check_tools || return 1
1032	for arg do
1033		if ! eval setup_"${arg}"; then
1034			err "  ${arg} not supported"
1035			return 1
1036		fi
1037	done
1038}
1039
1040# Format integer into IPv4 address, summing 10.0.0.5 (arbitrary) to it
1041format_addr4() {
1042	a=$((${1} + 16777216 * 10 + 5))
1043	printf "%i.%i.%i.%i"						\
1044	       "$((a / 16777216))" "$((a % 16777216 / 65536))"	\
1045	       "$((a % 65536 / 256))" "$((a % 256))"
1046}
1047
1048# Format integer into IPv6 address, summing 2001:db8:: to it
1049format_addr6() {
1050	printf "2001:db8::%04x:%04x" "$((${1} / 65536))" "$((${1} % 65536))"
1051}
1052
1053# Format integer into EUI-48 address, summing 00:01:00:00:00:00 to it
1054format_mac() {
1055	printf "00:01:%02x:%02x:%02x:%02x" \
1056	       "$((${1} / 16777216))" "$((${1} % 16777216 / 65536))"	\
1057	       "$((${1} % 65536 / 256))" "$((${1} % 256))"
1058}
1059
1060# Format integer into port, avoid 0 port
1061format_port() {
1062	printf "%i" "$((${1} % 65534 + 1))"
1063}
1064
1065# Drop suffixed '6' from L4 protocol, if any
1066format_proto() {
1067	printf "%s" "${proto}" | tr -d 6
1068}
1069
1070# Format destination and source fields into nft concatenated type
1071format() {
1072	__start=
1073	__end=
1074	__expr="{ "
1075
1076	for f in ${dst}; do
1077		[ "${__expr}" != "{ " ] && __expr="${__expr} . "
1078
1079		__start="$(eval format_"${f}" "${start}")"
1080		__end="$(eval format_"${f}" "${end}")"
1081
1082		if [ "${f}" = "proto" ]; then
1083			__expr="${__expr}${__start}"
1084		else
1085			__expr="${__expr}${__start}-${__end}"
1086		fi
1087	done
1088	for f in ${src}; do
1089		[ "${__expr}" != "{ " ] && __expr="${__expr} . "
1090
1091		__start="$(eval format_"${f}" "${srcstart}")"
1092		__end="$(eval format_"${f}" "${srcend}")"
1093
1094		if [ "${f}" = "proto" ]; then
1095			__expr="${__expr}${__start}"
1096		else
1097			__expr="${__expr}${__start}-${__end}"
1098		fi
1099	done
1100
1101	if [ -n "${timeout}" ]; then
1102		echo "${__expr} timeout ${timeout}s }"
1103	else
1104		echo "${__expr} }"
1105	fi
1106}
1107
1108# Format destination and source fields into nft type, start element only
1109format_norange() {
1110	__expr="{ "
1111
1112	for f in ${dst}; do
1113		[ "${__expr}" != "{ " ] && __expr="${__expr} . "
1114
1115		__expr="${__expr}$(eval format_"${f}" "${start}")"
1116	done
1117	for f in ${src}; do
1118		__expr="${__expr} . $(eval format_"${f}" "${start}")"
1119	done
1120
1121	echo "${__expr} }"
1122}
1123
1124# Format first destination field into nft type
1125format_noconcat() {
1126	for f in ${dst}; do
1127		__start="$(eval format_"${f}" "${start}")"
1128		__end="$(eval format_"${f}" "${end}")"
1129
1130		if [ "${f}" = "proto" ]; then
1131			echo "{ ${__start} }"
1132		else
1133			echo "{ ${__start}-${__end} }"
1134		fi
1135		return
1136	done
1137}
1138
1139# Add single entry to 'test' set in 'inet filter' table
1140add() {
1141	if ! nft add element inet filter test "${1}"; then
1142		err "Failed to add ${1} given ruleset:"
1143		err "$(nft -a list ruleset)"
1144		return 1
1145	fi
1146}
1147
1148# Format and output entries for sets in 'netdev perf' table
1149add_perf() {
1150	if [ "${1}" = "test" ]; then
1151		echo "add element netdev perf test $(format)"
1152	elif [ "${1}" = "norange" ]; then
1153		echo "add element netdev perf norange $(format_norange)"
1154	elif [ "${1}" = "noconcat" ]; then
1155		echo "add element netdev perf noconcat $(format_noconcat)"
1156	fi
1157}
1158
1159# Add single entry to 'norange' set in 'netdev perf' table
1160add_perf_norange() {
1161	if ! nft add element netdev perf norange "${1}"; then
1162		err "Failed to add ${1} given ruleset:"
1163		err "$(nft -a list ruleset)"
1164		return 1
1165	fi
1166}
1167
1168# Add single entry to 'noconcat' set in 'netdev perf' table
1169add_perf_noconcat() {
1170	if ! nft add element netdev perf noconcat "${1}"; then
1171		err "Failed to add ${1} given ruleset:"
1172		err "$(nft -a list ruleset)"
1173		return 1
1174	fi
1175}
1176
1177# Delete single entry from set
1178del() {
1179	if ! nft delete element inet filter test "${1}"; then
1180		err "Failed to delete ${1} given ruleset:"
1181		err "$(nft -a list ruleset)"
1182		return 1
1183	fi
1184}
1185
1186# Return packet count for elem $1 from 'test' counter in 'inet filter' table
1187count_packets() {
1188	found=0
1189	for token in $(nft reset element inet filter test "${1}" ); do
1190		[ ${found} -eq 1 ] && echo "${token}" && return
1191		[ "${token}" = "packets" ] && found=1
1192	done
1193}
1194
1195# Return packet count from 'test' counter in 'inet filter' table
1196count_packets_nomatch() {
1197	found=0
1198	for token in $(nft list counter inet filter test); do
1199		[ ${found} -eq 1 ] && echo "${token}" && return
1200		[ "${token}" = "packets" ] && found=1
1201	done
1202}
1203
1204# Return packet count from 'test' counter in 'netdev perf' table
1205count_perf_packets() {
1206	found=0
1207	for token in $(nft list counter netdev perf test); do
1208		[ ${found} -eq 1 ] && echo "${token}" && return
1209		[ "${token}" = "packets" ] && found=1
1210	done
1211}
1212
1213# Set MAC addresses, send traffic according to specifier
1214flood() {
1215	ip link set veth_a address "$(format_mac "${1}")"
1216	ip -n B link set veth_b address "$(format_mac "${2}")"
1217
1218	for f in ${dst}; do
1219		eval dst_"$f"=\$\(format_\$f "${1}"\)
1220	done
1221	for f in ${src}; do
1222		eval src_"$f"=\$\(format_\$f "${2}"\)
1223	done
1224	eval flood_\$proto
1225}
1226
1227# Set MAC addresses, start pktgen injection
1228perf() {
1229	dst_mac="$(format_mac "${1}")"
1230	ip link set veth_a address "${dst_mac}"
1231
1232	for f in ${dst}; do
1233		eval dst_"$f"=\$\(format_\$f "${1}"\)
1234	done
1235	for f in ${src}; do
1236		eval src_"$f"=\$\(format_\$f "${2}"\)
1237	done
1238	eval perf_\$perf_proto
1239}
1240
1241# Set MAC addresses, send single packet, check that it matches, reset counter
1242send_match() {
1243	local elem="$1"
1244
1245	shift
1246
1247	ip link set veth_a address "$(format_mac "${1}")"
1248	ip -n B link set veth_b address "$(format_mac "${2}")"
1249
1250	for f in ${dst}; do
1251		eval dst_"$f"=\$\(format_\$f "${1}"\)
1252	done
1253	for f in ${src}; do
1254		eval src_"$f"=\$\(format_\$f "${2}"\)
1255	done
1256	eval send_\$proto
1257	if [ "$(count_packets "$elem")" != "1" ]; then
1258		err "${proto} packet to:"
1259		err "  $(for f in ${dst}; do
1260			 eval format_\$f "${1}"; printf ' '; done)"
1261		err "from:"
1262		err "  $(for f in ${src}; do
1263			 eval format_\$f "${2}"; printf ' '; done)"
1264		err "should have matched ruleset:"
1265		err "$(nft -a list ruleset)"
1266		return 1
1267	fi
1268	nft reset counter inet filter test >/dev/null
1269}
1270
1271# Set MAC addresses, send single packet, check that it doesn't match
1272send_nomatch() {
1273	ip link set veth_a address "$(format_mac "${1}")"
1274	ip -n B link set veth_b address "$(format_mac "${2}")"
1275
1276	for f in ${dst}; do
1277		eval dst_"$f"=\$\(format_\$f "${1}"\)
1278	done
1279	for f in ${src}; do
1280		eval src_"$f"=\$\(format_\$f "${2}"\)
1281	done
1282	eval send_\$proto
1283	if [ "$(count_packets_nomatch)" != "0" ]; then
1284		err "${proto} packet to:"
1285		err "  $(for f in ${dst}; do
1286			 eval format_\$f "${1}"; printf ' '; done)"
1287		err "from:"
1288		err "  $(for f in ${src}; do
1289			 eval format_\$f "${2}"; printf ' '; done)"
1290		err "should not have matched ruleset:"
1291		err "$(nft -a list ruleset)"
1292		return 1
1293	fi
1294}
1295
1296maybe_send_nomatch() {
1297	local elem="$1"
1298	local what="$4"
1299
1300	[ $((RANDOM%20)) -gt 0 ] && return
1301
1302	dst_addr4="$2"
1303	dst_port="$3"
1304	send_udp
1305
1306	if [ "$(count_packets_nomatch)" != "0" ]; then
1307		err "Packet to $dst_addr4:$dst_port did match $what"
1308		err "$(nft -a list ruleset)"
1309		return 1
1310	fi
1311}
1312
1313maybe_send_match() {
1314	local elem="$1"
1315	local what="$4"
1316
1317	[ $((RANDOM%20)) -gt 0 ] && return
1318
1319	dst_addr4="$2"
1320	dst_port="$3"
1321	send_udp
1322
1323	if [ "$(count_packets "{ $elem }")" != "1" ]; then
1324		err "Packet to $dst_addr4:$dst_port did not match $what"
1325		err "$(nft -a list ruleset)"
1326		return 1
1327	fi
1328	nft reset counter inet filter test >/dev/null
1329	nft reset element inet filter test "{ $elem }" >/dev/null
1330}
1331
1332# Correctness test template:
1333# - add ranged element, check that packets match it
1334# - check that packets outside range don't match it
1335# - remove some elements, check that packets don't match anymore
1336test_correctness_main() {
1337	range_size=1
1338
1339	send_nomatch $((end + 1)) $((end + 1 + src_delta)) || return 1
1340
1341	for i in $(seq "${start}" $((start + count))); do
1342		local elem=""
1343
1344		end=$((start + range_size))
1345
1346		# Avoid negative or zero-sized port ranges
1347		if [ $((end / 65534)) -gt $((start / 65534)) ]; then
1348			start=${end}
1349			end=$((end + 1))
1350		fi
1351		srcstart=$((start + src_delta))
1352		srcend=$((end + src_delta))
1353
1354		elem="$(format)"
1355		add "$elem" || return 1
1356		for j in $(seq "$start" $((range_size / 2 + 1)) ${end}); do
1357			send_match "$elem" "${j}" $((j + src_delta)) || return 1
1358		done
1359		send_nomatch $((end + 1)) $((end + 1 + src_delta)) || return 1
1360
1361		# Delete elements now and then
1362		if [ $((i % 3)) -eq 0 ]; then
1363			del "$elem" || return 1
1364			for j in $(seq "$start" \
1365				   $((range_size / 2 + 1)) ${end}); do
1366				send_nomatch "${j}" $((j + src_delta)) \
1367					|| return 1
1368			done
1369		fi
1370
1371		range_size=$((range_size + 1))
1372		start=$((end + range_size))
1373	done
1374}
1375
1376test_correctness() {
1377	setup veth send_"${proto}" set || return ${ksft_skip}
1378
1379	test_correctness_main
1380}
1381
1382# Repeat the correctness tests, but add extra non-matching entries.
1383# This exercises the more compact '4 bit group' representation that
1384# gets picked when the default 8-bit representation exceed
1385# NFT_PIPAPO_LT_SIZE_HIGH bytes of memory.
1386# See usage of NFT_PIPAPO_LT_SIZE_HIGH in pipapo_lt_bits_adjust().
1387#
1388# The format() helper is way too slow when generating lots of
1389# entries so its not used here.
1390test_correctness_large() {
1391	setup veth send_"${proto}" set || return ${ksft_skip}
1392	# number of dummy (filler) entries to add.
1393	local dcount=16385
1394
1395	(
1396	echo -n "add element inet filter test { "
1397
1398	case "$type_spec" in
1399	"ether_addr . ipv4_addr")
1400		for i in $(seq 1 $dcount); do
1401			[ $i -gt 1 ] && echo ", "
1402			format_mac $((1000000 + i))
1403			printf ". 172.%i.%i.%i " $((RANDOM%256)) $((RANDOM%256)) $((i%256))
1404		done
1405		;;
1406	"inet_proto . ipv6_addr")
1407		for i in $(seq 1 $dcount); do
1408			[ $i -gt 1 ] && echo ", "
1409			printf "%i . " $((RANDOM%256))
1410			format_addr6 $((1000000 + i))
1411		done
1412		;;
1413	"inet_service . inet_proto")
1414		# smaller key sizes, need more entries to hit the
1415		# 4-bit threshold.
1416		dcount=65536
1417		for i in $(seq 1 $dcount); do
1418			local proto=$((RANDOM%256))
1419
1420			# Test uses UDP to match, as it also fails when matching
1421			# an entry that doesn't exist, so skip 'udp' entries
1422			# to not trigger a wrong failure.
1423			[ $proto -eq 17 ] && proto=18
1424			[ $i -gt 1 ] && echo ", "
1425			printf "%i . %i " $(((i%65534) + 1)) $((proto))
1426		done
1427		;;
1428	"inet_service . ipv4_addr")
1429		dcount=32768
1430		for i in $(seq 1 $dcount); do
1431			[ $i -gt 1 ] && echo ", "
1432			printf "%i . 172.%i.%i.%i " $(((RANDOM%65534) + 1)) $((RANDOM%256)) $((RANDOM%256)) $((i%256))
1433		done
1434		;;
1435	"ipv4_addr . ether_addr")
1436		for i in $(seq 1 $dcount); do
1437			[ $i -gt 1 ] && echo ", "
1438			printf "172.%i.%i.%i . " $((RANDOM%256)) $((RANDOM%256)) $((i%256))
1439			format_mac $((1000000 + i))
1440		done
1441		;;
1442	"ipv4_addr . inet_service")
1443		dcount=32768
1444		for i in $(seq 1 $dcount); do
1445			[ $i -gt 1 ] && echo ", "
1446			printf "172.%i.%i.%i . %i" $((RANDOM%256)) $((RANDOM%256)) $((i%256)) $(((RANDOM%65534) + 1))
1447		done
1448		;;
1449	"ipv4_addr . inet_service . ether_addr . inet_proto . ipv4_addr")
1450		dcount=65536
1451		for i in $(seq 1 $dcount); do
1452			[ $i -gt 1 ] && echo ", "
1453			printf "172.%i.%i.%i . %i . " $((RANDOM%256)) $((RANDOM%256)) $((i%256)) $(((RANDOM%65534) + 1))
1454			format_mac $((1000000 + i))
1455			printf ". %i . 192.168.%i.%i" $((RANDOM%256)) $((RANDOM%256)) $((i%256))
1456		done
1457		;;
1458	"ipv4_addr . inet_service . inet_proto")
1459		for i in $(seq 1 $dcount); do
1460			[ $i -gt 1 ] && echo ", "
1461			printf "172.%i.%i.%i . %i . %i " $((RANDOM%256)) $((RANDOM%256)) $((i%256)) $(((RANDOM%65534) + 1)) $((RANDOM%256))
1462		done
1463		;;
1464	"ipv4_addr . inet_service . inet_proto . ipv4_addr")
1465		for i in $(seq 1 $dcount); do
1466			[ $i -gt 1 ] && echo ", "
1467			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))
1468		done
1469		;;
1470	"ipv4_addr . inet_service . ipv4_addr")
1471		dcount=32768
1472		for i in $(seq 1 $dcount); do
1473			[ $i -gt 1 ] && echo ", "
1474			printf "172.%i.%i.%i . %i . 192.168.%i.%i " $((RANDOM%256)) $((RANDOM%256)) $((i%256)) $(((RANDOM%65534) + 1)) $((RANDOM%256)) $((RANDOM%256))
1475		done
1476		;;
1477	"ipv6_addr . ether_addr")
1478		for i in $(seq 1 $dcount); do
1479			[ $i -gt 1 ] && echo ", "
1480			format_addr6 $((i + 1000000))
1481			echo -n " . "
1482			format_mac $((1000000 + i))
1483		done
1484		;;
1485	"ipv6_addr . inet_service")
1486		dcount=32768
1487		for i in $(seq 1 $dcount); do
1488			[ $i -gt 1 ] && echo ", "
1489			format_addr6 $((i + 1000000))
1490			echo -n " .  $(((RANDOM%65534) + 1))"
1491		done
1492		;;
1493	"ipv6_addr . inet_service . ether_addr")
1494		dcount=32768
1495		for i in $(seq 1 $dcount); do
1496			[ $i -gt 1 ] && echo ", "
1497			format_addr6 $((i + 1000000))
1498			echo -n " .  $(((RANDOM%65534) + 1)) . "
1499			format_mac $((i + 1000000))
1500		done
1501		;;
1502	"ipv6_addr . inet_service . ether_addr . inet_proto")
1503		dcount=65536
1504		for i in $(seq 1 $dcount); do
1505			[ $i -gt 1 ] && echo ", "
1506			format_addr6 $((i + 1000000))
1507			echo -n " .  $(((RANDOM%65534) + 1)) . "
1508			format_mac $((i + 1000000))
1509			echo -n " .  $((RANDOM%256))"
1510		done
1511		;;
1512	"ipv6_addr . inet_service . ipv6_addr . inet_service")
1513		dcount=32768
1514		for i in $(seq 1 $dcount); do
1515			[ $i -gt 1 ] && echo ", "
1516			format_addr6 $((i + 1000000))
1517			echo -n " .  $(((RANDOM%65534) + 1)) . "
1518			format_addr6 $((i + 2123456))
1519			echo -n " .  $((RANDOM%256))"
1520		done
1521		;;
1522	*)
1523		"Unhandled $type_spec"
1524		return 1
1525	esac
1526	echo -n "}"
1527
1528	) | nft -f - || return 1
1529
1530	test_correctness_main
1531}
1532
1533# Concurrency test template:
1534# - add all the elements
1535# - start a thread for each physical thread that:
1536#   - adds all the elements
1537#   - flushes the set
1538#   - adds all the elements
1539#   - flushes the entire ruleset
1540#   - adds the set back
1541#   - adds all the elements
1542#   - delete all the elements
1543test_concurrency() {
1544	proto=${flood_proto}
1545	tools=${flood_tools}
1546	chain_spec=${flood_spec}
1547	setup veth flood_"${proto}" set || return ${ksft_skip}
1548
1549	range_size=1
1550	cstart=${start}
1551	flood_pids=
1552	for i in $(seq "$start" $((start + count))); do
1553		end=$((start + range_size))
1554		srcstart=$((start + src_delta))
1555		srcend=$((end + src_delta))
1556
1557		add "$(format)" || return 1
1558
1559		flood "${i}" $((i + src_delta)) & flood_pids="${flood_pids} $!"
1560
1561		range_size=$((range_size + 1))
1562		start=$((end + range_size))
1563	done
1564
1565	sleep $((RANDOM%10))
1566
1567	pids=
1568	for c in $(seq 1 "$(nproc)"); do (
1569		for r in $(seq 1 "${race_repeat}"); do
1570			range_size=1
1571
1572			# $start needs to be local to this subshell
1573			# shellcheck disable=SC2030
1574			start=${cstart}
1575			for i in $(seq "$start" $((start + count))); do
1576				end=$((start + range_size))
1577				srcstart=$((start + src_delta))
1578				srcend=$((end + src_delta))
1579
1580				add "$(format)" 2>/dev/null
1581
1582				range_size=$((range_size + 1))
1583				start=$((end + range_size))
1584			done
1585
1586			nft flush inet filter test 2>/dev/null
1587
1588			range_size=1
1589			start=${cstart}
1590			for i in $(seq "$start" $((start + count))); do
1591				end=$((start + range_size))
1592				srcstart=$((start + src_delta))
1593				srcend=$((end + src_delta))
1594
1595				add "$(format)" 2>/dev/null
1596
1597				range_size=$((range_size + 1))
1598				start=$((end + range_size))
1599			done
1600
1601			nft flush ruleset
1602			setup set 2>/dev/null
1603
1604			range_size=1
1605			start=${cstart}
1606			for i in $(seq "$start" $((start + count))); do
1607				end=$((start + range_size))
1608				srcstart=$((start + src_delta))
1609				srcend=$((end + src_delta))
1610
1611				add "$(format)" 2>/dev/null
1612
1613				range_size=$((range_size + 1))
1614				start=$((end + range_size))
1615			done
1616
1617			range_size=1
1618			start=${cstart}
1619			for i in $(seq "$start" $((start + count))); do
1620				end=$((start + range_size))
1621				srcstart=$((start + src_delta))
1622				srcend=$((end + src_delta))
1623
1624				del "$(format)" 2>/dev/null
1625
1626				range_size=$((range_size + 1))
1627				start=$((end + range_size))
1628			done
1629		done
1630	) & pids="${pids} $!"
1631	done
1632
1633	# shellcheck disable=SC2046,SC2086 # word splitting wanted here
1634	wait $(for pid in ${pids}; do echo ${pid}; done)
1635	# shellcheck disable=SC2046,SC2086
1636	kill $(for pid in ${flood_pids}; do echo ${pid}; done) 2>/dev/null
1637	# shellcheck disable=SC2046,SC2086
1638	wait $(for pid in ${flood_pids}; do echo ${pid}; done) 2>/dev/null
1639
1640	return 0
1641}
1642
1643# Timeout test template:
1644# - add all the elements with 3s timeout while checking that packets match
1645# - wait 3s after the last insertion, check that packets don't match any entry
1646test_timeout() {
1647	setup veth send_"${proto}" set || return ${ksft_skip}
1648
1649	timeout=3
1650
1651	[ "$KSFT_MACHINE_SLOW" = "yes" ] && timeout=8
1652
1653	range_size=1
1654	for i in $(seq "$start" $((start + count))); do
1655		local elem=""
1656
1657		end=$((start + range_size))
1658		srcstart=$((start + src_delta))
1659		srcend=$((end + src_delta))
1660
1661		elem="$(format)"
1662		add "$elem" || return 1
1663
1664		for j in $(seq "$start" $((range_size / 2 + 1)) ${end}); do
1665			send_match "$elem" "${j}" $((j + src_delta)) || return 1
1666		done
1667
1668		range_size=$((range_size + 1))
1669		start=$((end + range_size))
1670	done
1671	sleep $timeout
1672	for i in $(seq "$start" $((start + count))); do
1673		end=$((start + range_size))
1674		srcstart=$((start + src_delta))
1675		srcend=$((end + src_delta))
1676
1677		for j in $(seq "$start" $((range_size / 2 + 1)) ${end}); do
1678			send_nomatch "${j}" $((j + src_delta)) || return 1
1679		done
1680
1681		range_size=$((range_size + 1))
1682		start=$((end + range_size))
1683	done
1684}
1685
1686# Performance test template:
1687# - add concatenated ranged entries
1688# - add non-ranged concatenated entries (for hash set matching rate baseline)
1689# - add ranged entries with first field only (for rbhash baseline)
1690# - start pktgen injection directly on device rx path of this namespace
1691# - measure drop only rate, hash and rbtree baselines, then matching rate
1692test_performance() {
1693	chain_spec=${perf_spec}
1694	dst="${perf_dst}"
1695	src="${perf_src}"
1696	setup veth perf set || return ${ksft_skip}
1697
1698	first=${start}
1699	range_size=1
1700	for set in test norange noconcat; do
1701		start=${first}
1702		for i in $(seq "$start" $((start + perf_entries))); do
1703			end=$((start + range_size))
1704			srcstart=$((start + src_delta))
1705			srcend=$((end + src_delta))
1706
1707			if [ $((end / 65534)) -gt $((start / 65534)) ]; then
1708				start=${end}
1709				end=$((end + 1))
1710			elif [ "$start" -eq "$end" ]; then
1711				end=$((start + 1))
1712			fi
1713
1714			add_perf ${set}
1715
1716			start=$((end + range_size))
1717		done > "${tmp}"
1718		nft -f "${tmp}"
1719	done
1720
1721	perf $((end - 1)) "$srcstart"
1722
1723	sleep 2
1724
1725	nft add rule netdev perf test counter name \"test\" drop
1726	nft reset counter netdev perf test >/dev/null 2>&1
1727	sleep "${perf_duration}"
1728	pps="$(printf %10s $(($(count_perf_packets) / perf_duration)))"
1729	info "    baseline (drop from netdev hook):            ${pps}pps"
1730	handle="$(nft -a list chain netdev perf test | grep counter)"
1731	handle="${handle##* }"
1732	nft delete rule netdev perf test handle "${handle}"
1733
1734	nft add rule "netdev perf test ${chain_spec} @norange \
1735		counter name \"test\" drop"
1736	nft reset counter netdev perf test >/dev/null 2>&1
1737	sleep "${perf_duration}"
1738	pps="$(printf %10s $(($(count_perf_packets) / perf_duration)))"
1739	info "    baseline hash (non-ranged entries):          ${pps}pps"
1740	handle="$(nft -a list chain netdev perf test | grep counter)"
1741	handle="${handle##* }"
1742	nft delete rule netdev perf test handle "${handle}"
1743
1744	nft add rule "netdev perf test ${chain_spec%%. *} @noconcat \
1745		counter name \"test\" drop"
1746	nft reset counter netdev perf test >/dev/null 2>&1
1747	sleep "${perf_duration}"
1748	pps="$(printf %10s $(($(count_perf_packets) / perf_duration)))"
1749	info "    baseline rbtree (match on first field only): ${pps}pps"
1750	handle="$(nft -a list chain netdev perf test | grep counter)"
1751	handle="${handle##* }"
1752	nft delete rule netdev perf test handle "${handle}"
1753
1754	nft add rule "netdev perf test ${chain_spec} @test \
1755		counter name \"test\" drop"
1756	nft reset counter netdev perf test >/dev/null 2>&1
1757	sleep "${perf_duration}"
1758	pps="$(printf %10s $(($(count_perf_packets) / perf_duration)))"
1759	p5="$(printf %5s "${perf_entries}")"
1760	info "    set with ${p5} full, ranged entries:         ${pps}pps"
1761	kill "${perf_pid}"
1762}
1763
1764test_bug_flush_remove_add() {
1765	rounds=100
1766	[ "$KSFT_MACHINE_SLOW" = "yes" ] && rounds=10
1767
1768	set_cmd='{ set s { type ipv4_addr . inet_service; flags interval; }; }'
1769	elem1='{ 10.0.0.1 . 22-25, 10.0.0.1 . 10-20 }'
1770	elem2='{ 10.0.0.1 . 10-20, 10.0.0.1 . 22-25 }'
1771	for i in $(seq 1 $rounds); do
1772		nft add table t "$set_cmd"	|| return ${ksft_skip}
1773		nft add element t s "$elem1"	2>/dev/null || return 1
1774		nft flush set t s		2>/dev/null || return 1
1775		nft add element t s "$elem2"	2>/dev/null || return 1
1776	done
1777	nft flush ruleset
1778}
1779
1780# - add ranged element, check that packets match it
1781# - reload the set, check packets still match
1782test_bug_reload() {
1783	setup veth send_"${proto}" set || return ${ksft_skip}
1784	rstart=${start}
1785
1786	range_size=1
1787	for i in $(seq "${start}" $((start + count))); do
1788		end=$((start + range_size))
1789
1790		# Avoid negative or zero-sized port ranges
1791		if [ $((end / 65534)) -gt $((start / 65534)) ]; then
1792			start=${end}
1793			end=$((end + 1))
1794		fi
1795		srcstart=$((start + src_delta))
1796		srcend=$((end + src_delta))
1797
1798		add "$(format)" || return 1
1799		range_size=$((range_size + 1))
1800		start=$((end + range_size))
1801	done
1802
1803	# check kernel does allocate pcpu sctrach map
1804	# for reload with no elemet add/delete
1805	( echo flush set inet filter test ;
1806	  nft list set inet filter test ) | nft -f -
1807
1808	start=${rstart}
1809	range_size=1
1810
1811	for i in $(seq "${start}" $((start + count))); do
1812		end=$((start + range_size))
1813
1814		# Avoid negative or zero-sized port ranges
1815		if [ $((end / 65534)) -gt $((start / 65534)) ]; then
1816			start=${end}
1817			end=$((end + 1))
1818		fi
1819		srcstart=$((start + src_delta))
1820		srcend=$((end + src_delta))
1821
1822		for j in $(seq "$start" $((range_size / 2 + 1)) ${end}); do
1823			send_match "$(format)" "${j}" $((j + src_delta)) || return 1
1824		done
1825
1826		range_size=$((range_size + 1))
1827		start=$((end + range_size))
1828	done
1829
1830	nft flush ruleset
1831}
1832
1833# - add ranged element, check that packets match it
1834# - delete element again, check it is gone
1835test_bug_net_port_proto_match() {
1836	setup veth send_"${proto}" set || return ${ksft_skip}
1837	rstart=${start}
1838
1839	range_size=1
1840	for i in $(seq 1 10); do
1841		for j in $(seq 1 20) ; do
1842			local dport=$j
1843
1844			elem=$(printf "10.%d.%d.0/24 . %d-%d0 . 6-17 " ${i} ${j} ${dport} "$((dport+1))")
1845
1846			# too slow, do not test all addresses
1847			maybe_send_nomatch "$elem" $(printf "10.%d.%d.1" $i $j) $(printf "%d1" $((dport+1))) "before add" || return 1
1848
1849			nft "add element inet filter test { $elem }" || return 1
1850
1851			maybe_send_match "$elem" $(printf "10.%d.%d.1" $i $j) $(printf "%d" $dport) "after add" || return 1
1852
1853			nft "get element inet filter test { $elem }" | grep -q "$elem"
1854			if [ $? -ne 0 ];then
1855				local got=$(nft "get element inet filter test { $elem }")
1856				err "post-add: should have returned $elem but got $got"
1857				return 1
1858			fi
1859
1860			maybe_send_nomatch "$elem" $(printf "10.%d.%d.1" $i $j) $(printf "%d1" $((dport+1))) "out-of-range" || return 1
1861		done
1862	done
1863
1864	# recheck after set was filled
1865	for i in $(seq 1 10); do
1866		for j in $(seq 1 20) ; do
1867			local dport=$j
1868
1869			elem=$(printf "10.%d.%d.0/24 . %d-%d0 . 6-17 " ${i} ${j} ${dport} "$((dport+1))")
1870
1871			nft "get element inet filter test { $elem }" | grep -q "$elem"
1872			if [ $? -ne 0 ];then
1873				local got=$(nft "get element inet filter test { $elem }")
1874				err "post-fill: should have returned $elem but got $got"
1875				return 1
1876			fi
1877
1878			maybe_send_match "$elem" $(printf "10.%d.%d.1" $i $j) $(printf "%d" $dport) "recheck" || return 1
1879			maybe_send_nomatch "$elem" $(printf "10.%d.%d.1" $i $j) $(printf "%d1" $((dport+1))) "recheck out-of-range" || return 1
1880		done
1881	done
1882
1883	# random del and re-fetch
1884	for i in $(seq 1 10); do
1885		for j in $(seq 1 20) ; do
1886			local rnd=$((RANDOM%10))
1887			local dport=$j
1888			local got=""
1889
1890			elem=$(printf "10.%d.%d.0/24 . %d-%d0 . 6-17 " ${i} ${j} ${dport} "$((dport+1))")
1891			if [ $rnd -gt 0 ];then
1892				continue
1893			fi
1894
1895			nft "delete element inet filter test { $elem }"
1896			got=$(nft "get element inet filter test { $elem }" 2>/dev/null)
1897			if [ $? -eq 0 ];then
1898				err "post-delete: query for $elem returned $got instead of error."
1899				return 1
1900			fi
1901
1902			maybe_send_nomatch "$elem" $(printf "10.%d.%d.1" $i $j) $(printf "%d" $dport) "match after deletion" || return 1
1903		done
1904	done
1905
1906	nft flush ruleset
1907}
1908
1909test_bug_avx2_mismatch()
1910{
1911	setup veth send_"${proto}" set || return ${ksft_skip}
1912
1913	local a1="fe80:dead:01ff:0a02:0b03:6007:8009:a001"
1914	local a2="fe80:dead:01fe:0a02:0b03:6007:8009:a001"
1915
1916	nft "add element inet filter test { icmpv6 . $a1 }"
1917
1918	dst_addr6="$a2"
1919	send_icmp6
1920
1921	if [ "$(count_packets "{ icmpv6 . $a1 }")" -gt "0" ]; then
1922		err "False match for $a2"
1923		return 1
1924	fi
1925}
1926
1927test_bug_doublecreate()
1928{
1929	local elements="1.2.3.4 . 1.2.4.1, 1.2.4.1 . 1.2.3.4"
1930	local ret=1
1931	local i
1932
1933	setup veth send_"${proto}" set || return ${ksft_skip}
1934
1935	add "{ $elements }" || return 1
1936	# expected to work: 'add' on existing should be no-op.
1937	add "{ $elements }" || return 1
1938
1939	# 'create' should return an error.
1940	if nft create element inet filter test "{ $elements }" 2>/dev/null; then
1941		err "Could create an existing element"
1942		return 1
1943	fi
1944nft -f - <<EOF 2>/dev/null
1945flush set inet filter test
1946create element inet filter test { $elements }
1947create element inet filter test { $elements }
1948EOF
1949	ret=$?
1950	if [ $ret -eq 0 ]; then
1951		err "Could create element twice in one transaction"
1952		err "$(nft -a list ruleset)"
1953		return 1
1954	fi
1955
1956nft -f - <<EOF 2>/dev/null
1957flush set inet filter test
1958create element inet filter test { $elements }
1959EOF
1960	ret=$?
1961	if [ $ret -ne 0 ]; then
1962		err "Could not flush and re-create element in one transaction"
1963		return 1
1964	fi
1965
1966	return 0
1967}
1968
1969add_fail()
1970{
1971	if nft add element inet filter test "$1" 2>/dev/null ; then
1972		err "Returned success for add ${1} given set:"
1973		err "$(nft -a list set inet filter test )"
1974		return 1
1975	fi
1976
1977	return 0
1978}
1979
1980test_bug_insert_overlap()
1981{
1982	local elements="1.2.3.4 . 1.2.4.1"
1983
1984	setup veth send_"${proto}" set || return ${ksft_skip}
1985
1986	add "{ $elements }" || return 1
1987
1988	elements="1.2.3.0-1.2.3.4 . 1.2.4.1"
1989	add_fail "{ $elements }" || return 1
1990
1991	elements="1.2.3.0-1.2.3.4 . 1.2.4.2"
1992	add "{ $elements }" || return 1
1993
1994	elements="1.2.3.4 . 1.2.4.1-1.2.4.2"
1995	add_fail "{ $elements }" || return 1
1996
1997	return 0
1998}
1999
2000test_reported_issues() {
2001	eval test_bug_"${subtest}"
2002}
2003
2004# Run everything in a separate network namespace
2005[ "${1}" != "run" ] && { unshare -n "${0}" run; exit $?; }
2006tmp="$(mktemp)"
2007trap cleanup_exit EXIT
2008
2009# Entry point for test runs
2010passed=0
2011for name in ${TESTS}; do
2012	printf "TEST: %s\n" "$(echo "$name" | tr '_' ' ')"
2013	if [ "${name}" = "reported_issues" ]; then
2014		SUBTESTS="${BUGS}"
2015	else
2016		SUBTESTS="${TYPES}"
2017	fi
2018
2019	for subtest in ${SUBTESTS}; do
2020		eval desc=\$TYPE_"${subtest}"
2021		IFS='
2022'
2023		for __line in ${desc}; do
2024			# shellcheck disable=SC2086
2025			eval ${__line%%	*}=\"${__line##*	}\";
2026		done
2027		IFS='
2028'
2029
2030		if [ "${name}" = "concurrency" ] && \
2031		   [ "${race_repeat}" = "0" ]; then
2032			continue
2033		fi
2034		if [ "${name}" = "performance" ] && \
2035		   [ "${perf_duration}" = "0" ]; then
2036			continue
2037		fi
2038
2039		[ "$KSFT_MACHINE_SLOW" = "yes" ] && count=1
2040
2041		printf "  %-32s  " "${display}"
2042		tthen=$(date +%s)
2043		eval test_"${name}"
2044		ret=$?
2045
2046		tnow=$(date +%s)
2047		printf "%5ds%-30s" $((tnow-tthen))
2048
2049		if [ $ret -eq 0 ]; then
2050			printf "[ OK ]\n"
2051			info_flush
2052			passed=$((passed + 1))
2053		elif [ $ret -eq 1 ]; then
2054			printf "[FAIL]\n"
2055			err_flush
2056			exit 1
2057		elif [ $ret -eq ${ksft_skip} ]; then
2058			printf "[SKIP]\n"
2059			err_flush
2060		fi
2061	done
2062done
2063
2064[ ${passed} -eq 0 ] && exit ${ksft_skip} || exit 0
2065