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