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