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