selftests: netfilter: Introduce tests for sets with range concatenation
authorStefano Brivio <sbrivio@redhat.com>
Tue, 21 Jan 2020 23:17:56 +0000 (00:17 +0100)
committerPablo Neira Ayuso <pablo@netfilter.org>
Mon, 27 Jan 2020 07:54:30 +0000 (08:54 +0100)
This test covers functionality and stability of the newly added
nftables set implementation supporting concatenation of ranged
fields.

For some selected set expression types, test:
- correctness, by checking that packets match or don't
- concurrency, by attempting races between insertion, deletion, lookup
- timeout feature, checking that packets don't match expired entries

and (roughly) estimate matching rates, comparing to baselines for
simple drop on netdev ingress hook and for hash and rbtrees sets.

In order to send packets, this needs one of sendip, netcat or bash.
To flood with traffic, iperf3, iperf and netperf are supported. For
performance measurements, this relies on the sample pktgen script
pktgen_bench_xmit_mode_netif_receive.sh.

If none of the tools suitable for a given test are available, specific
tests will be skipped.

Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
tools/testing/selftests/netfilter/Makefile
tools/testing/selftests/netfilter/nft_concat_range.sh [new file with mode: 0755]

index de1032b..08194aa 100644 (file)
@@ -2,6 +2,7 @@
 # Makefile for netfilter selftests
 
 TEST_PROGS := nft_trans_stress.sh nft_nat.sh bridge_brouter.sh \
-       conntrack_icmp_related.sh nft_flowtable.sh ipvs.sh
+       conntrack_icmp_related.sh nft_flowtable.sh ipvs.sh \
+       nft_concat_range.sh
 
 include ../lib.mk
diff --git a/tools/testing/selftests/netfilter/nft_concat_range.sh b/tools/testing/selftests/netfilter/nft_concat_range.sh
new file mode 100755 (executable)
index 0000000..aca21dd
--- /dev/null
@@ -0,0 +1,1481 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0
+#
+# nft_concat_range.sh - Tests for sets with concatenation of ranged fields
+#
+# Copyright (c) 2019 Red Hat GmbH
+#
+# Author: Stefano Brivio <sbrivio@redhat.com>
+#
+# shellcheck disable=SC2154,SC2034,SC2016,SC2030,SC2031
+# ^ Configuration and templates sourced with eval, counters reused in subshells
+
+KSELFTEST_SKIP=4
+
+# Available test groups:
+# - correctness: check that packets match given entries, and only those
+# - concurrency: attempt races between insertion, deletion and lookup
+# - timeout: check that packets match entries until they expire
+# - performance: estimate matching rate, compare with rbtree and hash baselines
+TESTS="correctness concurrency timeout"
+[ "${quicktest}" != "1" ] && TESTS="${TESTS} performance"
+
+# Set types, defined by TYPE_ variables below
+TYPES="net_port port_net net6_port port_proto net6_port_mac net6_port_mac_proto
+       net_port_net net_mac net_mac_icmp net6_mac_icmp net6_port_net6_port
+       net_port_mac_proto_net"
+
+# List of possible paths to pktgen script from kernel tree for performance tests
+PKTGEN_SCRIPT_PATHS="
+       ../../../samples/pktgen/pktgen_bench_xmit_mode_netif_receive.sh
+       pktgen/pktgen_bench_xmit_mode_netif_receive.sh"
+
+# Definition of set types:
+# display      display text for test report
+# type_spec    nftables set type specifier
+# chain_spec   nftables type specifier for rules mapping to set
+# dst          call sequence of format_*() functions for destination fields
+# src          call sequence of format_*() functions for source fields
+# start                initial integer used to generate addresses and ports
+# count                count of entries to generate and match
+# src_delta    number summed to destination generator for source fields
+# tools                list of tools for correctness and timeout tests, any can be used
+# proto                L4 protocol of test packets
+#
+# race_repeat  race attempts per thread, 0 disables concurrency test for type
+# flood_tools  list of tools for concurrency tests, any can be used
+# flood_proto  L4 protocol of test packets for concurrency tests
+# flood_spec   nftables type specifier for concurrency tests
+#
+# perf_duration        duration of single pktgen injection test
+# perf_spec    nftables type specifier for performance tests
+# perf_dst     format_*() functions for destination fields in performance test
+# perf_src     format_*() functions for source fields in performance test
+# perf_entries number of set entries for performance test
+# perf_proto   L3 protocol of test packets
+TYPE_net_port="
+display                net,port
+type_spec      ipv4_addr . inet_service
+chain_spec     ip daddr . udp dport
+dst            addr4 port
+src             
+start          1
+count          5
+src_delta      2000
+tools          sendip nc bash
+proto          udp
+
+race_repeat    3
+flood_tools    iperf3 iperf netperf
+flood_proto    udp
+flood_spec     ip daddr . udp dport
+
+perf_duration  5
+perf_spec      ip daddr . udp dport
+perf_dst       addr4 port
+perf_src        
+perf_entries   1000
+perf_proto     ipv4
+"
+
+TYPE_port_net="
+display                port,net
+type_spec      inet_service . ipv4_addr
+chain_spec     udp dport . ip daddr
+dst            port addr4
+src             
+start          1
+count          5
+src_delta      2000
+tools          sendip nc bash
+proto          udp
+
+race_repeat    3
+flood_tools    iperf3 iperf netperf
+flood_proto    udp
+flood_spec     udp dport . ip daddr
+
+perf_duration  5
+perf_spec      udp dport . ip daddr
+perf_dst       port addr4
+perf_src        
+perf_entries   100
+perf_proto     ipv4
+"
+
+TYPE_net6_port="
+display                net6,port
+type_spec      ipv6_addr . inet_service
+chain_spec     ip6 daddr . udp dport
+dst            addr6 port
+src             
+start          10
+count          5
+src_delta      2000
+tools          sendip nc bash
+proto          udp6
+
+race_repeat    3
+flood_tools    iperf3 iperf netperf
+flood_proto    tcp6
+flood_spec     ip6 daddr . udp dport
+
+perf_duration  5
+perf_spec      ip6 daddr . udp dport
+perf_dst       addr6 port
+perf_src        
+perf_entries   1000
+perf_proto     ipv6
+"
+
+TYPE_port_proto="
+display                port,proto
+type_spec      inet_service . inet_proto
+chain_spec     udp dport . meta l4proto
+dst            port proto
+src             
+start          1
+count          5
+src_delta      2000
+tools          sendip nc bash
+proto          udp
+
+race_repeat    0
+
+perf_duration  5
+perf_spec      udp dport . meta l4proto
+perf_dst       port proto
+perf_src        
+perf_entries   30000
+perf_proto     ipv4
+"
+
+TYPE_net6_port_mac="
+display                net6,port,mac
+type_spec      ipv6_addr . inet_service . ether_addr
+chain_spec     ip6 daddr . udp dport . ether saddr
+dst            addr6 port
+src            mac
+start          10
+count          5
+src_delta      2000
+tools          sendip nc bash
+proto          udp6
+
+race_repeat    0
+
+perf_duration  5
+perf_spec      ip6 daddr . udp dport . ether daddr
+perf_dst       addr6 port mac
+perf_src        
+perf_entries   10
+perf_proto     ipv6
+"
+
+TYPE_net6_port_mac_proto="
+display                net6,port,mac,proto
+type_spec      ipv6_addr . inet_service . ether_addr . inet_proto
+chain_spec     ip6 daddr . udp dport . ether saddr . meta l4proto
+dst            addr6 port
+src            mac proto
+start          10
+count          5
+src_delta      2000
+tools          sendip nc bash
+proto          udp6
+
+race_repeat    0
+
+perf_duration  5
+perf_spec      ip6 daddr . udp dport . ether daddr . meta l4proto
+perf_dst       addr6 port mac proto
+perf_src        
+perf_entries   1000
+perf_proto     ipv6
+"
+
+TYPE_net_port_net="
+display                net,port,net
+type_spec      ipv4_addr . inet_service . ipv4_addr
+chain_spec     ip daddr . udp dport . ip saddr
+dst            addr4 port
+src            addr4
+start          1
+count          5
+src_delta      2000
+tools          sendip nc bash
+proto          udp
+
+race_repeat    3
+flood_tools    iperf3 iperf netperf
+flood_proto    tcp
+flood_spec     ip daddr . udp dport . ip saddr
+
+perf_duration  0
+"
+
+TYPE_net6_port_net6_port="
+display                net6,port,net6,port
+type_spec      ipv6_addr . inet_service . ipv6_addr . inet_service
+chain_spec     ip6 daddr . udp dport . ip6 saddr . udp sport
+dst            addr6 port
+src            addr6 port
+start          10
+count          5
+src_delta      2000
+tools          sendip nc
+proto          udp6
+
+race_repeat    3
+flood_tools    iperf3 iperf netperf
+flood_proto    tcp6
+flood_spec     ip6 daddr . tcp dport . ip6 saddr . tcp sport
+
+perf_duration  0
+"
+
+TYPE_net_port_mac_proto_net="
+display                net,port,mac,proto,net
+type_spec      ipv4_addr . inet_service . ether_addr . inet_proto . ipv4_addr
+chain_spec     ip daddr . udp dport . ether saddr . meta l4proto . ip saddr
+dst            addr4 port
+src            mac proto addr4
+start          1
+count          5
+src_delta      2000
+tools          sendip nc bash
+proto          udp
+
+race_repeat    0
+
+perf_duration  0
+"
+
+TYPE_net_mac="
+display                net,mac
+type_spec      ipv4_addr . ether_addr
+chain_spec     ip daddr . ether saddr
+dst            addr4
+src            mac
+start          1
+count          5
+src_delta      2000
+tools          sendip nc bash
+proto          udp
+
+race_repeat    0
+
+perf_duration  5
+perf_spec      ip daddr . ether daddr
+perf_dst       addr4 mac
+perf_src        
+perf_entries   1000
+perf_proto     ipv4
+"
+
+TYPE_net_mac_icmp="
+display                net,mac - ICMP
+type_spec      ipv4_addr . ether_addr
+chain_spec     ip daddr . ether saddr
+dst            addr4
+src            mac
+start          1
+count          5
+src_delta      2000
+tools          ping
+proto          icmp
+
+race_repeat    0
+
+perf_duration  0
+"
+
+TYPE_net6_mac_icmp="
+display                net6,mac - ICMPv6
+type_spec      ipv6_addr . ether_addr
+chain_spec     ip6 daddr . ether saddr
+dst            addr6
+src            mac
+start          10
+count          50
+src_delta      2000
+tools          ping
+proto          icmp6
+
+race_repeat    0
+
+perf_duration  0
+"
+
+TYPE_net_port_proto_net="
+display                net,port,proto,net
+type_spec      ipv4_addr . inet_service . inet_proto . ipv4_addr
+chain_spec     ip daddr . udp dport . meta l4proto . ip saddr
+dst            addr4 port proto
+src            addr4
+start          1
+count          5
+src_delta      2000
+tools          sendip nc
+proto          udp
+
+race_repeat    3
+flood_tools    iperf3 iperf netperf
+flood_proto    tcp
+flood_spec     ip daddr . tcp dport . meta l4proto . ip saddr
+
+perf_duration  0
+"
+
+# Set template for all tests, types and rules are filled in depending on test
+set_template='
+flush ruleset
+
+table inet filter {
+       counter test {
+               packets 0 bytes 0
+       }
+
+       set test {
+               type ${type_spec}
+               flags interval,timeout
+       }
+
+       chain input {
+               type filter hook prerouting priority 0; policy accept;
+               ${chain_spec} @test counter name \"test\"
+       }
+}
+
+table netdev perf {
+       counter test {
+               packets 0 bytes 0
+       }
+
+       counter match {
+               packets 0 bytes 0
+       }
+
+       set test {
+               type ${type_spec}
+               flags interval
+       }
+
+       set norange {
+               type ${type_spec}
+       }
+
+       set noconcat {
+               type ${type_spec%% *}
+               flags interval
+       }
+
+       chain test {
+               type filter hook ingress device veth_a priority 0;
+       }
+}
+'
+
+err_buf=
+info_buf=
+
+# Append string to error buffer
+err() {
+       err_buf="${err_buf}${1}
+"
+}
+
+# Append string to information buffer
+info() {
+       info_buf="${info_buf}${1}
+"
+}
+
+# Flush error buffer to stdout
+err_flush() {
+       printf "%s" "${err_buf}"
+       err_buf=
+}
+
+# Flush information buffer to stdout
+info_flush() {
+       printf "%s" "${info_buf}"
+       info_buf=
+}
+
+# Setup veth pair: this namespace receives traffic, B generates it
+setup_veth() {
+       ip netns add B
+       ip link add veth_a type veth peer name veth_b || return 1
+
+       ip link set veth_a up
+       ip link set veth_b netns B
+
+       ip -n B link set veth_b up
+
+       ip addr add dev veth_a 10.0.0.1
+       ip route add default dev veth_a
+
+       ip -6 addr add fe80::1/64 dev veth_a nodad
+       ip -6 addr add 2001:db8::1/64 dev veth_a nodad
+       ip -6 route add default dev veth_a
+
+       ip -n B route add default dev veth_b
+
+       ip -6 -n B addr add fe80::2/64 dev veth_b nodad
+       ip -6 -n B addr add 2001:db8::2/64 dev veth_b nodad
+       ip -6 -n B route add default dev veth_b
+
+       B() {
+               ip netns exec B "$@" >/dev/null 2>&1
+       }
+
+       sleep 2
+}
+
+# Fill in set template and initialise set
+setup_set() {
+       eval "echo \"${set_template}\"" | nft -f -
+}
+
+# Check that at least one of the needed tools is available
+check_tools() {
+       __tools=
+       for tool in ${tools}; do
+               if [ "${tool}" = "nc" ] && [ "${proto}" = "udp6" ] && \
+                  ! nc -u -w0 1.1.1.1 1 2>/dev/null; then
+                       # Some GNU netcat builds might not support IPv6
+                       __tools="${__tools} netcat-openbsd"
+                       continue
+               fi
+               __tools="${__tools} ${tool}"
+
+               command -v "${tool}" >/dev/null && return 0
+       done
+       err "need one of:${__tools}, skipping" && return 1
+}
+
+# Set up function to send ICMP packets
+setup_send_icmp() {
+       send_icmp() {
+               B ping -c1 -W1 "${dst_addr4}" >/dev/null 2>&1
+       }
+}
+
+# Set up function to send ICMPv6 packets
+setup_send_icmp6() {
+       if command -v ping6 >/dev/null; then
+               send_icmp6() {
+                       ip -6 addr add "${dst_addr6}" dev veth_a nodad \
+                               2>/dev/null
+                       B ping6 -q -c1 -W1 "${dst_addr6}"
+               }
+       else
+               send_icmp6() {
+                       ip -6 addr add "${dst_addr6}" dev veth_a nodad \
+                               2>/dev/null
+                       B ping -q -6 -c1 -W1 "${dst_addr6}"
+               }
+       fi
+}
+
+# Set up function to send single UDP packets on IPv4
+setup_send_udp() {
+       if command -v sendip >/dev/null; then
+               send_udp() {
+                       [ -n "${src_port}" ] && src_port="-us ${src_port}"
+                       [ -n "${dst_port}" ] && dst_port="-ud ${dst_port}"
+                       [ -n "${src_addr4}" ] && src_addr4="-is ${src_addr4}"
+
+                       # shellcheck disable=SC2086 # sendip needs split options
+                       B sendip -p ipv4 -p udp ${src_addr4} ${src_port} \
+                                               ${dst_port} "${dst_addr4}"
+
+                       src_port=
+                       dst_port=
+                       src_addr4=
+               }
+       elif command -v nc >/dev/null; then
+               if nc -u -w0 1.1.1.1 1 2>/dev/null; then
+                       # OpenBSD netcat
+                       nc_opt="-w0"
+               else
+                       # GNU netcat
+                       nc_opt="-q0"
+               fi
+
+               send_udp() {
+                       if [ -n "${src_addr4}" ]; then
+                               B ip addr add "${src_addr4}" dev veth_b
+                               __src_addr4="-s ${src_addr4}"
+                       fi
+                       ip addr add "${dst_addr4}" dev veth_a 2>/dev/null
+                       [ -n "${src_port}" ] && src_port="-p ${src_port}"
+
+                       echo "" | B nc -u "${nc_opt}" "${__src_addr4}" \
+                                 "${src_port}" "${dst_addr4}" "${dst_port}"
+
+                       src_addr4=
+                       src_port=
+               }
+       elif [ -z "$(bash -c 'type -p')" ]; then
+               send_udp() {
+                       ip addr add "${dst_addr4}" dev veth_a 2>/dev/null
+                       if [ -n "${src_addr4}" ]; then
+                               B ip addr add "${src_addr4}/16" dev veth_b
+                               B ip route add default dev veth_b
+                       fi
+
+                       B bash -c "echo > /dev/udp/${dst_addr4}/${dst_port}"
+
+                       if [ -n "${src_addr4}" ]; then
+                               B ip addr del "${src_addr4}/16" dev veth_b
+                       fi
+                       src_addr4=
+               }
+       else
+               return 1
+       fi
+}
+
+# Set up function to send single UDP packets on IPv6
+setup_send_udp6() {
+       if command -v sendip >/dev/null; then
+               send_udp6() {
+                       [ -n "${src_port}" ] && src_port="-us ${src_port}"
+                       [ -n "${dst_port}" ] && dst_port="-ud ${dst_port}"
+                       if [ -n "${src_addr6}" ]; then
+                               src_addr6="-6s ${src_addr6}"
+                       else
+                               src_addr6="-6s 2001:db8::2"
+                       fi
+                       ip -6 addr add "${dst_addr6}" dev veth_a nodad \
+                               2>/dev/null
+
+                       # shellcheck disable=SC2086 # this needs split options
+                       B sendip -p ipv6 -p udp ${src_addr6} ${src_port} \
+                                               ${dst_port} "${dst_addr6}"
+
+                       src_port=
+                       dst_port=
+                       src_addr6=
+               }
+       elif command -v nc >/dev/null && nc -u -w0 1.1.1.1 1 2>/dev/null; then
+               # GNU netcat might not work with IPv6, try next tool
+               send_udp6() {
+                       ip -6 addr add "${dst_addr6}" dev veth_a nodad \
+                               2>/dev/null
+                       if [ -n "${src_addr6}" ]; then
+                               B ip addr add "${src_addr6}" dev veth_b nodad
+                       else
+                               src_addr6="2001:db8::2"
+                       fi
+                       [ -n "${src_port}" ] && src_port="-p ${src_port}"
+
+                       # shellcheck disable=SC2086 # this needs split options
+                       echo "" | B nc -u w0 "-s${src_addr6}" ${src_port} \
+                                              ${dst_addr6} ${dst_port}
+
+                       src_addr6=
+                       src_port=
+               }
+       elif [ -z "$(bash -c 'type -p')" ]; then
+               send_udp6() {
+                       ip -6 addr add "${dst_addr6}" dev veth_a nodad \
+                               2>/dev/null
+                       B ip addr add "${src_addr6}" dev veth_b nodad
+                       B bash -c "echo > /dev/udp/${dst_addr6}/${dst_port}"
+                       ip -6 addr del "${dst_addr6}" dev veth_a 2>/dev/null
+               }
+       else
+               return 1
+       fi
+}
+
+# Set up function to send TCP traffic on IPv4
+setup_flood_tcp() {
+       if command -v iperf3 >/dev/null; then
+               flood_tcp() {
+                       [ -n "${dst_port}" ] && dst_port="-p ${dst_port}"
+                       if [ -n "${src_addr4}" ]; then
+                               B ip addr add "${src_addr4}/16" dev veth_b
+                               src_addr4="-B ${src_addr4}"
+                       else
+                               B ip addr add dev veth_b 10.0.0.2
+                               src_addr4="-B 10.0.0.2"
+                       fi
+                       if [ -n "${src_port}" ]; then
+                               src_port="--cport ${src_port}"
+                       fi
+                       B ip route add default dev veth_b 2>/dev/null
+                       ip addr add "${dst_addr4}" dev veth_a 2>/dev/null
+
+                       # shellcheck disable=SC2086 # this needs split options
+                       iperf3 -s -DB "${dst_addr4}" ${dst_port} >/dev/null 2>&1
+                       sleep 2
+
+                       # shellcheck disable=SC2086 # this needs split options
+                       B iperf3 -c "${dst_addr4}" ${dst_port} ${src_port} \
+                               ${src_addr4} -l16 -t 1000
+
+                       src_addr4=
+                       src_port=
+                       dst_port=
+               }
+       elif command -v iperf >/dev/null; then
+               flood_tcp() {
+                       [ -n "${dst_port}" ] && dst_port="-p ${dst_port}"
+                       if [ -n "${src_addr4}" ]; then
+                               B ip addr add "${src_addr4}/16" dev veth_b
+                               src_addr4="-B ${src_addr4}"
+                       else
+                               B ip addr add dev veth_b 10.0.0.2 2>/dev/null
+                               src_addr4="-B 10.0.0.2"
+                       fi
+                       if [ -n "${src_port}" ]; then
+                               src_addr4="${src_addr4}:${src_port}"
+                       fi
+                       B ip route add default dev veth_b
+                       ip addr add "${dst_addr4}" dev veth_a 2>/dev/null
+
+                       # shellcheck disable=SC2086 # this needs split options
+                       iperf -s -DB "${dst_addr4}" ${dst_port} >/dev/null 2>&1
+                       sleep 2
+
+                       # shellcheck disable=SC2086 # this needs split options
+                       B iperf -c "${dst_addr4}" ${dst_port} ${src_addr4} \
+                               -l20 -t 1000
+
+                       src_addr4=
+                       src_port=
+                       dst_port=
+               }
+       elif command -v netperf >/dev/null; then
+               flood_tcp() {
+                       [ -n "${dst_port}" ] && dst_port="-p ${dst_port}"
+                       if [ -n "${src_addr4}" ]; then
+                               B ip addr add "${src_addr4}/16" dev veth_b
+                       else
+                               B ip addr add dev veth_b 10.0.0.2
+                               src_addr4="10.0.0.2"
+                       fi
+                       if [ -n "${src_port}" ]; then
+                               dst_port="${dst_port},${src_port}"
+                       fi
+                       B ip route add default dev veth_b
+                       ip addr add "${dst_addr4}" dev veth_a 2>/dev/null
+
+                       # shellcheck disable=SC2086 # this needs split options
+                       netserver -4 ${dst_port} -L "${dst_addr4}" \
+                               >/dev/null 2>&1
+                       sleep 2
+
+                       # shellcheck disable=SC2086 # this needs split options
+                       B netperf -4 -H "${dst_addr4}" ${dst_port} \
+                               -L "${src_addr4}" -l 1000 -t TCP_STREAM
+
+                       src_addr4=
+                       src_port=
+                       dst_port=
+               }
+       else
+               return 1
+       fi
+}
+
+# Set up function to send TCP traffic on IPv6
+setup_flood_tcp6() {
+       if command -v iperf3 >/dev/null; then
+               flood_tcp6() {
+                       [ -n "${dst_port}" ] && dst_port="-p ${dst_port}"
+                       if [ -n "${src_addr6}" ]; then
+                               B ip addr add "${src_addr6}" dev veth_b nodad
+                               src_addr6="-B ${src_addr6}"
+                       else
+                               src_addr6="-B 2001:db8::2"
+                       fi
+                       if [ -n "${src_port}" ]; then
+                               src_port="--cport ${src_port}"
+                       fi
+                       B ip route add default dev veth_b
+                       ip -6 addr add "${dst_addr6}" dev veth_a nodad \
+                               2>/dev/null
+
+                       # shellcheck disable=SC2086 # this needs split options
+                       iperf3 -s -DB "${dst_addr6}" ${dst_port} >/dev/null 2>&1
+                       sleep 2
+
+                       # shellcheck disable=SC2086 # this needs split options
+                       B iperf3 -c "${dst_addr6}" ${dst_port} \
+                               ${src_port} ${src_addr6} -l16 -t 1000
+
+                       src_addr6=
+                       src_port=
+                       dst_port=
+               }
+       elif command -v iperf >/dev/null; then
+               flood_tcp6() {
+                       [ -n "${dst_port}" ] && dst_port="-p ${dst_port}"
+                       if [ -n "${src_addr6}" ]; then
+                               B ip addr add "${src_addr6}" dev veth_b nodad
+                               src_addr6="-B ${src_addr6}"
+                       else
+                               src_addr6="-B 2001:db8::2"
+                       fi
+                       if [ -n "${src_port}" ]; then
+                               src_addr6="${src_addr6}:${src_port}"
+                       fi
+                       B ip route add default dev veth_b
+                       ip -6 addr add "${dst_addr6}" dev veth_a nodad \
+                               2>/dev/null
+
+                       # shellcheck disable=SC2086 # this needs split options
+                       iperf -s -VDB "${dst_addr6}" ${dst_port} >/dev/null 2>&1
+                       sleep 2
+
+                       # shellcheck disable=SC2086 # this needs split options
+                       B iperf -c "${dst_addr6}" -V ${dst_port} \
+                               ${src_addr6} -l1 -t 1000
+
+                       src_addr6=
+                       src_port=
+                       dst_port=
+               }
+       elif command -v netperf >/dev/null; then
+               flood_tcp6() {
+                       [ -n "${dst_port}" ] && dst_port="-p ${dst_port}"
+                       if [ -n "${src_addr6}" ]; then
+                               B ip addr add "${src_addr6}" dev veth_b nodad
+                       else
+                               src_addr6="2001:db8::2"
+                       fi
+                       if [ -n "${src_port}" ]; then
+                               dst_port="${dst_port},${src_port}"
+                       fi
+                       B ip route add default dev veth_b
+                       ip -6 addr add "${dst_addr6}" dev veth_a nodad \
+                               2>/dev/null
+
+                       # shellcheck disable=SC2086 # this needs split options
+                       netserver -6 ${dst_port} -L "${dst_addr6}" \
+                               >/dev/null 2>&1
+                       sleep 2
+
+                       # shellcheck disable=SC2086 # this needs split options
+                       B netperf -6 -H "${dst_addr6}" ${dst_port} \
+                               -L "${src_addr6}" -l 1000 -t TCP_STREAM
+
+                       src_addr6=
+                       src_port=
+                       dst_port=
+               }
+       else
+               return 1
+       fi
+}
+
+# Set up function to send UDP traffic on IPv4
+setup_flood_udp() {
+       if command -v iperf3 >/dev/null; then
+               flood_udp() {
+                       [ -n "${dst_port}" ] && dst_port="-p ${dst_port}"
+                       if [ -n "${src_addr4}" ]; then
+                               B ip addr add "${src_addr4}/16" dev veth_b
+                               src_addr4="-B ${src_addr4}"
+                       else
+                               B ip addr add dev veth_b 10.0.0.2 2>/dev/null
+                               src_addr4="-B 10.0.0.2"
+                       fi
+                       if [ -n "${src_port}" ]; then
+                               src_port="--cport ${src_port}"
+                       fi
+                       B ip route add default dev veth_b
+                       ip addr add "${dst_addr4}" dev veth_a 2>/dev/null
+
+                       # shellcheck disable=SC2086 # this needs split options
+                       iperf3 -s -DB "${dst_addr4}" ${dst_port}
+                       sleep 2
+
+                       # shellcheck disable=SC2086 # this needs split options
+                       B iperf3 -u -c "${dst_addr4}" -Z -b 100M -l16 -t1000 \
+                               ${dst_port} ${src_port} ${src_addr4}
+
+                       src_addr4=
+                       src_port=
+                       dst_port=
+               }
+       elif command -v iperf >/dev/null; then
+               flood_udp() {
+                       [ -n "${dst_port}" ] && dst_port="-p ${dst_port}"
+                       if [ -n "${src_addr4}" ]; then
+                               B ip addr add "${src_addr4}/16" dev veth_b
+                               src_addr4="-B ${src_addr4}"
+                       else
+                               B ip addr add dev veth_b 10.0.0.2
+                               src_addr4="-B 10.0.0.2"
+                       fi
+                       if [ -n "${src_port}" ]; then
+                               src_addr4="${src_addr4}:${src_port}"
+                       fi
+                       B ip route add default dev veth_b
+                       ip addr add "${dst_addr4}" dev veth_a 2>/dev/null
+
+                       # shellcheck disable=SC2086 # this needs split options
+                       iperf -u -sDB "${dst_addr4}" ${dst_port} >/dev/null 2>&1
+                       sleep 2
+
+                       # shellcheck disable=SC2086 # this needs split options
+                       B iperf -u -c "${dst_addr4}" -b 100M -l1 -t1000 \
+                               ${dst_port} ${src_addr4}
+
+                       src_addr4=
+                       src_port=
+                       dst_port=
+               }
+       elif command -v netperf >/dev/null; then
+               flood_udp() {
+                       [ -n "${dst_port}" ] && dst_port="-p ${dst_port}"
+                       if [ -n "${src_addr4}" ]; then
+                               B ip addr add "${src_addr4}/16" dev veth_b
+                       else
+                               B ip addr add dev veth_b 10.0.0.2
+                               src_addr4="10.0.0.2"
+                       fi
+                       if [ -n "${src_port}" ]; then
+                               dst_port="${dst_port},${src_port}"
+                       fi
+                       B ip route add default dev veth_b
+                       ip addr add "${dst_addr4}" dev veth_a 2>/dev/null
+
+                       # shellcheck disable=SC2086 # this needs split options
+                       netserver -4 ${dst_port} -L "${dst_addr4}" \
+                               >/dev/null 2>&1
+                       sleep 2
+
+                       # shellcheck disable=SC2086 # this needs split options
+                       B netperf -4 -H "${dst_addr4}" ${dst_port} \
+                               -L "${src_addr4}" -l 1000 -t UDP_STREAM
+
+                       src_addr4=
+                       src_port=
+                       dst_port=
+               }
+       else
+               return 1
+       fi
+}
+
+# Find pktgen script and set up function to start pktgen injection
+setup_perf() {
+       for pktgen_script_path in ${PKTGEN_SCRIPT_PATHS} __notfound; do
+               command -v "${pktgen_script_path}" >/dev/null && break
+       done
+       [ "${pktgen_script_path}" = "__notfound" ] && return 1
+
+       perf_ipv4() {
+               ${pktgen_script_path} -s80 \
+                       -i veth_a -d "${dst_addr4}" -p "${dst_port}" \
+                       -m "${dst_mac}" \
+                       -t $(($(nproc) / 5 + 1)) -b10000 -n0 2>/dev/null &
+               perf_pid=$!
+       }
+       perf_ipv6() {
+               IP6=6 ${pktgen_script_path} -s100 \
+                       -i veth_a -d "${dst_addr6}" -p "${dst_port}" \
+                       -m "${dst_mac}" \
+                       -t $(($(nproc) / 5 + 1)) -b10000 -n0 2>/dev/null &
+               perf_pid=$!
+       }
+}
+
+# Clean up before each test
+cleanup() {
+       nft reset counter inet filter test      >/dev/null 2>&1
+       nft flush ruleset                       >/dev/null 2>&1
+       ip link del dummy0                      2>/dev/null
+       ip route del default                    2>/dev/null
+       ip -6 route del default                 2>/dev/null
+       ip netns del B                          2>/dev/null
+       ip link del veth_a                      2>/dev/null
+       timeout=
+       killall iperf3                          2>/dev/null
+       killall iperf                           2>/dev/null
+       killall netperf                         2>/dev/null
+       killall netserver                       2>/dev/null
+       rm -f ${tmp}
+       sleep 2
+}
+
+# Entry point for setup functions
+setup() {
+       if [ "$(id -u)" -ne 0 ]; then
+               echo "  need to run as root"
+               exit ${KSELFTEST_SKIP}
+       fi
+
+       cleanup
+       check_tools || return 1
+       for arg do
+               if ! eval setup_"${arg}"; then
+                       err "  ${arg} not supported"
+                       return 1
+               fi
+       done
+}
+
+# Format integer into IPv4 address, summing 10.0.0.5 (arbitrary) to it
+format_addr4() {
+       a=$((${1} + 16777216 * 10 + 5))
+       printf "%i.%i.%i.%i"                                            \
+              "$((a / 16777216))" "$((a % 16777216 / 65536))"  \
+              "$((a % 65536 / 256))" "$((a % 256))"
+}
+
+# Format integer into IPv6 address, summing 2001:db8:: to it
+format_addr6() {
+       printf "2001:db8::%04x:%04x" "$((${1} / 65536))" "$((${1} % 65536))"
+}
+
+# Format integer into EUI-48 address, summing 00:01:00:00:00:00 to it
+format_mac() {
+       printf "00:01:%02x:%02x:%02x:%02x" \
+              "$((${1} / 16777216))" "$((${1} % 16777216 / 65536))"    \
+              "$((${1} % 65536 / 256))" "$((${1} % 256))"
+}
+
+# Format integer into port, avoid 0 port
+format_port() {
+       printf "%i" "$((${1} % 65534 + 1))"
+}
+
+# Drop suffixed '6' from L4 protocol, if any
+format_proto() {
+       printf "%s" "${proto}" | tr -d 6
+}
+
+# Format destination and source fields into nft concatenated type
+format() {
+       __start=
+       __end=
+       __expr="{ "
+
+       for f in ${dst}; do
+               [ "${__expr}" != "{ " ] && __expr="${__expr} . "
+
+               __start="$(eval format_"${f}" "${start}")"
+               __end="$(eval format_"${f}" "${end}")"
+
+               if [ "${f}" = "proto" ]; then
+                       __expr="${__expr}${__start}"
+               else
+                       __expr="${__expr}${__start}-${__end}"
+               fi
+       done
+       for f in ${src}; do
+               __expr="${__expr} . "
+               __start="$(eval format_"${f}" "${srcstart}")"
+               __end="$(eval format_"${f}" "${srcend}")"
+
+               if [ "${f}" = "proto" ]; then
+                       __expr="${__expr}${__start}"
+               else
+                       __expr="${__expr}${__start}-${__end}"
+               fi
+       done
+
+       if [ -n "${timeout}" ]; then
+               echo "${__expr} timeout ${timeout}s }"
+       else
+               echo "${__expr} }"
+       fi
+}
+
+# Format destination and source fields into nft type, start element only
+format_norange() {
+       __expr="{ "
+
+       for f in ${dst}; do
+               [ "${__expr}" != "{ " ] && __expr="${__expr} . "
+
+               __expr="${__expr}$(eval format_"${f}" "${start}")"
+       done
+       for f in ${src}; do
+               __expr="${__expr} . $(eval format_"${f}" "${start}")"
+       done
+
+       echo "${__expr} }"
+}
+
+# Format first destination field into nft type
+format_noconcat() {
+       for f in ${dst}; do
+               __start="$(eval format_"${f}" "${start}")"
+               __end="$(eval format_"${f}" "${end}")"
+
+               if [ "${f}" = "proto" ]; then
+                       echo "{ ${__start} }"
+               else
+                       echo "{ ${__start}-${__end} }"
+               fi
+               return
+       done
+}
+
+# Add single entry to 'test' set in 'inet filter' table
+add() {
+       if ! nft add element inet filter test "${1}"; then
+               err "Failed to add ${1} given ruleset:"
+               err "$(nft list ruleset -a)"
+               return 1
+       fi
+}
+
+# Format and output entries for sets in 'netdev perf' table
+add_perf() {
+       if [ "${1}" = "test" ]; then
+               echo "add element netdev perf test $(format)"
+       elif [ "${1}" = "norange" ]; then
+               echo "add element netdev perf norange $(format_norange)"
+       elif [ "${1}" = "noconcat" ]; then
+               echo "add element netdev perf noconcat $(format_noconcat)"
+       fi
+}
+
+# Add single entry to 'norange' set in 'netdev perf' table
+add_perf_norange() {
+       if ! nft add element netdev perf norange "${1}"; then
+               err "Failed to add ${1} given ruleset:"
+               err "$(nft list ruleset -a)"
+               return 1
+       fi
+}
+
+# Add single entry to 'noconcat' set in 'netdev perf' table
+add_perf_noconcat() {
+       if ! nft add element netdev perf noconcat "${1}"; then
+               err "Failed to add ${1} given ruleset:"
+               err "$(nft list ruleset -a)"
+               return 1
+       fi
+}
+
+# Delete single entry from set
+del() {
+       if ! nft delete element inet filter test "${1}"; then
+               err "Failed to delete ${1} given ruleset:"
+               err "$(nft list ruleset -a)"
+               return 1
+       fi
+}
+
+# Return packet count from 'test' counter in 'inet filter' table
+count_packets() {
+       found=0
+       for token in $(nft list counter inet filter test); do
+               [ ${found} -eq 1 ] && echo "${token}" && return
+               [ "${token}" = "packets" ] && found=1
+       done
+}
+
+# Return packet count from 'test' counter in 'netdev perf' table
+count_perf_packets() {
+       found=0
+       for token in $(nft list counter netdev perf test); do
+               [ ${found} -eq 1 ] && echo "${token}" && return
+               [ "${token}" = "packets" ] && found=1
+       done
+}
+
+# Set MAC addresses, send traffic according to specifier
+flood() {
+       ip link set veth_a address "$(format_mac "${1}")"
+       ip -n B link set veth_b address "$(format_mac "${2}")"
+
+       for f in ${dst}; do
+               eval dst_"$f"=\$\(format_\$f "${1}"\)
+       done
+       for f in ${src}; do
+               eval src_"$f"=\$\(format_\$f "${2}"\)
+       done
+       eval flood_\$proto
+}
+
+# Set MAC addresses, start pktgen injection
+perf() {
+       dst_mac="$(format_mac "${1}")"
+       ip link set veth_a address "${dst_mac}"
+
+       for f in ${dst}; do
+               eval dst_"$f"=\$\(format_\$f "${1}"\)
+       done
+       for f in ${src}; do
+               eval src_"$f"=\$\(format_\$f "${2}"\)
+       done
+       eval perf_\$perf_proto
+}
+
+# Set MAC addresses, send single packet, check that it matches, reset counter
+send_match() {
+       ip link set veth_a address "$(format_mac "${1}")"
+       ip -n B link set veth_b address "$(format_mac "${2}")"
+
+       for f in ${dst}; do
+               eval dst_"$f"=\$\(format_\$f "${1}"\)
+       done
+       for f in ${src}; do
+               eval src_"$f"=\$\(format_\$f "${2}"\)
+       done
+       eval send_\$proto
+       if [ "$(count_packets)" != "1" ]; then
+               err "${proto} packet to:"
+               err "  $(for f in ${dst}; do
+                        eval format_\$f "${1}"; printf ' '; done)"
+               err "from:"
+               err "  $(for f in ${src}; do
+                        eval format_\$f "${2}"; printf ' '; done)"
+               err "should have matched ruleset:"
+               err "$(nft list ruleset -a)"
+               return 1
+       fi
+       nft reset counter inet filter test >/dev/null
+}
+
+# Set MAC addresses, send single packet, check that it doesn't match
+send_nomatch() {
+       ip link set veth_a address "$(format_mac "${1}")"
+       ip -n B link set veth_b address "$(format_mac "${2}")"
+
+       for f in ${dst}; do
+               eval dst_"$f"=\$\(format_\$f "${1}"\)
+       done
+       for f in ${src}; do
+               eval src_"$f"=\$\(format_\$f "${2}"\)
+       done
+       eval send_\$proto
+       if [ "$(count_packets)" != "0" ]; then
+               err "${proto} packet to:"
+               err "  $(for f in ${dst}; do
+                        eval format_\$f "${1}"; printf ' '; done)"
+               err "from:"
+               err "  $(for f in ${src}; do
+                        eval format_\$f "${2}"; printf ' '; done)"
+               err "should not have matched ruleset:"
+               err "$(nft list ruleset -a)"
+               return 1
+       fi
+}
+
+# Correctness test template:
+# - add ranged element, check that packets match it
+# - check that packets outside range don't match it
+# - remove some elements, check that packets don't match anymore
+test_correctness() {
+       setup veth send_"${proto}" set || return ${KSELFTEST_SKIP}
+
+       range_size=1
+       for i in $(seq "${start}" $((start + count))); do
+               end=$((start + range_size))
+
+               # Avoid negative or zero-sized port ranges
+               if [ $((end / 65534)) -gt $((start / 65534)) ]; then
+                       start=${end}
+                       end=$((end + 1))
+               fi
+               srcstart=$((start + src_delta))
+               srcend=$((end + src_delta))
+
+               add "$(format)" || return 1
+               for j in $(seq ${start} $((range_size / 2 + 1)) ${end}); do
+                       send_match "${j}" $((j + src_delta)) || return 1
+               done
+               send_nomatch $((end + 1)) $((end + 1 + src_delta)) || return 1
+
+               # Delete elements now and then
+               if [ $((i % 3)) -eq 0 ]; then
+                       del "$(format)" || return 1
+                       for j in $(seq ${start} \
+                                  $((range_size / 2 + 1)) ${end}); do
+                               send_nomatch "${j}" $((j + src_delta)) \
+                                       || return 1
+                       done
+               fi
+
+               range_size=$((range_size + 1))
+               start=$((end + range_size))
+       done
+}
+
+# Concurrency test template:
+# - add all the elements
+# - start a thread for each physical thread that:
+#   - adds all the elements
+#   - flushes the set
+#   - adds all the elements
+#   - flushes the entire ruleset
+#   - adds the set back
+#   - adds all the elements
+#   - delete all the elements
+test_concurrency() {
+       proto=${flood_proto}
+       tools=${flood_tools}
+       chain_spec=${flood_spec}
+       setup veth flood_"${proto}" set || return ${KSELFTEST_SKIP}
+
+       range_size=1
+       cstart=${start}
+       flood_pids=
+       for i in $(seq ${start} $((start + count))); do
+               end=$((start + range_size))
+               srcstart=$((start + src_delta))
+               srcend=$((end + src_delta))
+
+               add "$(format)" || return 1
+
+               flood "${i}" $((i + src_delta)) & flood_pids="${flood_pids} $!"
+
+               range_size=$((range_size + 1))
+               start=$((end + range_size))
+       done
+
+       sleep 10
+
+       pids=
+       for c in $(seq 1 "$(nproc)"); do (
+               for r in $(seq 1 "${race_repeat}"); do
+                       range_size=1
+
+                       # $start needs to be local to this subshell
+                       # shellcheck disable=SC2030
+                       start=${cstart}
+                       for i in $(seq ${start} $((start + count))); do
+                               end=$((start + range_size))
+                               srcstart=$((start + src_delta))
+                               srcend=$((end + src_delta))
+
+                               add "$(format)" 2>/dev/null
+
+                               range_size=$((range_size + 1))
+                               start=$((end + range_size))
+                       done
+
+                       nft flush inet filter test 2>/dev/null
+
+                       range_size=1
+                       start=${cstart}
+                       for i in $(seq ${start} $((start + count))); do
+                               end=$((start + range_size))
+                               srcstart=$((start + src_delta))
+                               srcend=$((end + src_delta))
+
+                               add "$(format)" 2>/dev/null
+
+                               range_size=$((range_size + 1))
+                               start=$((end + range_size))
+                       done
+
+                       nft flush ruleset
+                       setup set 2>/dev/null
+
+                       range_size=1
+                       start=${cstart}
+                       for i in $(seq ${start} $((start + count))); do
+                               end=$((start + range_size))
+                               srcstart=$((start + src_delta))
+                               srcend=$((end + src_delta))
+
+                               add "$(format)" 2>/dev/null
+
+                               range_size=$((range_size + 1))
+                               start=$((end + range_size))
+                       done
+
+                       range_size=1
+                       start=${cstart}
+                       for i in $(seq ${start} $((start + count))); do
+                               end=$((start + range_size))
+                               srcstart=$((start + src_delta))
+                               srcend=$((end + src_delta))
+
+                               del "$(format)" 2>/dev/null
+
+                               range_size=$((range_size + 1))
+                               start=$((end + range_size))
+                       done
+               done
+       ) & pids="${pids} $!"
+       done
+
+       # shellcheck disable=SC2046,SC2086 # word splitting wanted here
+       wait $(for pid in ${pids}; do echo ${pid}; done)
+       # shellcheck disable=SC2046,SC2086
+       kill $(for pid in ${flood_pids}; do echo ${pid}; done) 2>/dev/null
+       # shellcheck disable=SC2046,SC2086
+       wait $(for pid in ${flood_pids}; do echo ${pid}; done) 2>/dev/null
+
+       return 0
+}
+
+# Timeout test template:
+# - add all the elements with 3s timeout while checking that packets match
+# - wait 3s after the last insertion, check that packets don't match any entry
+test_timeout() {
+       setup veth send_"${proto}" set || return ${KSELFTEST_SKIP}
+
+       timeout=3
+       range_size=1
+       for i in $(seq "${start}" $((start + count))); do
+               end=$((start + range_size))
+               srcstart=$((start + src_delta))
+               srcend=$((end + src_delta))
+
+               add "$(format)" || return 1
+
+               for j in $(seq ${start} $((range_size / 2 + 1)) ${end}); do
+                       send_match "${j}" $((j + src_delta)) || return 1
+               done
+
+               range_size=$((range_size + 1))
+               start=$((end + range_size))
+       done
+       sleep 3
+       for i in $(seq ${start} $((start + count))); do
+               end=$((start + range_size))
+               srcstart=$((start + src_delta))
+               srcend=$((end + src_delta))
+
+               for j in $(seq ${start} $((range_size / 2 + 1)) ${end}); do
+                       send_nomatch "${j}" $((j + src_delta)) || return 1
+               done
+
+               range_size=$((range_size + 1))
+               start=$((end + range_size))
+       done
+}
+
+# Performance test template:
+# - add concatenated ranged entries
+# - add non-ranged concatenated entries (for hash set matching rate baseline)
+# - add ranged entries with first field only (for rbhash baseline)
+# - start pktgen injection directly on device rx path of this namespace
+# - measure drop only rate, hash and rbtree baselines, then matching rate
+test_performance() {
+       chain_spec=${perf_spec}
+       dst="${perf_dst}"
+       src="${perf_src}"
+       setup veth perf set || return ${KSELFTEST_SKIP}
+
+       first=${start}
+       range_size=1
+       for set in test norange noconcat; do
+               start=${first}
+               for i in $(seq ${start} $((start + perf_entries))); do
+                       end=$((start + range_size))
+                       srcstart=$((start + src_delta))
+                       srcend=$((end + src_delta))
+
+                       if [ $((end / 65534)) -gt $((start / 65534)) ]; then
+                               start=${end}
+                               end=$((end + 1))
+                       elif [ ${start} -eq ${end} ]; then
+                               end=$((start + 1))
+                       fi
+
+                       add_perf ${set}
+
+                       start=$((end + range_size))
+               done > "${tmp}"
+               nft -f "${tmp}"
+       done
+
+       perf $((end - 1)) ${srcstart}
+
+       sleep 2
+
+       nft add rule netdev perf test counter name \"test\" drop
+       nft reset counter netdev perf test >/dev/null 2>&1
+       sleep "${perf_duration}"
+       pps="$(printf %10s $(($(count_perf_packets) / perf_duration)))"
+       info "    baseline (drop from netdev hook):            ${pps}pps"
+       handle="$(nft -a list chain netdev perf test | grep counter)"
+       handle="${handle##* }"
+       nft delete rule netdev perf test handle "${handle}"
+
+       nft add rule "netdev perf test ${chain_spec} @norange \
+               counter name \"test\" drop"
+       nft reset counter netdev perf test >/dev/null 2>&1
+       sleep "${perf_duration}"
+       pps="$(printf %10s $(($(count_perf_packets) / perf_duration)))"
+       info "    baseline hash (non-ranged entries):          ${pps}pps"
+       handle="$(nft -a list chain netdev perf test | grep counter)"
+       handle="${handle##* }"
+       nft delete rule netdev perf test handle "${handle}"
+
+       nft add rule "netdev perf test ${chain_spec%%. *} @noconcat \
+               counter name \"test\" drop"
+       nft reset counter netdev perf test >/dev/null 2>&1
+       sleep "${perf_duration}"
+       pps="$(printf %10s $(($(count_perf_packets) / perf_duration)))"
+       info "    baseline rbtree (match on first field only): ${pps}pps"
+       handle="$(nft -a list chain netdev perf test | grep counter)"
+       handle="${handle##* }"
+       nft delete rule netdev perf test handle "${handle}"
+
+       nft add rule "netdev perf test ${chain_spec} @test \
+               counter name \"test\" drop"
+       nft reset counter netdev perf test >/dev/null 2>&1
+       sleep "${perf_duration}"
+       pps="$(printf %10s $(($(count_perf_packets) / perf_duration)))"
+       p5="$(printf %5s "${perf_entries}")"
+       info "    set with ${p5} full, ranged entries:         ${pps}pps"
+       kill "${perf_pid}"
+}
+
+# Run everything in a separate network namespace
+[ "${1}" != "run" ] && { unshare -n "${0}" run; exit $?; }
+tmp="$(mktemp)"
+trap cleanup EXIT
+
+# Entry point for test runs
+passed=0
+for name in ${TESTS}; do
+       printf "TEST: %s\n" "${name}"
+       for type in ${TYPES}; do
+               eval desc=\$TYPE_"${type}"
+               IFS='
+'
+               for __line in ${desc}; do
+                       # shellcheck disable=SC2086
+                       eval ${__line%% *}=\"${__line##*        }\";
+               done
+               IFS='   
+'
+
+               if [ "${name}" = "concurrency" ] && \
+                  [ "${race_repeat}" = "0" ]; then
+                       continue
+               fi
+               if [ "${name}" = "performance" ] && \
+                  [ "${perf_duration}" = "0" ]; then
+                       continue
+               fi
+
+               printf "  %-60s  " "${display}"
+               eval test_"${name}"
+               ret=$?
+
+               if [ $ret -eq 0 ]; then
+                       printf "[ OK ]\n"
+                       info_flush
+                       passed=$((passed + 1))
+               elif [ $ret -eq 1 ]; then
+                       printf "[FAIL]\n"
+                       err_flush
+                       exit 1
+               elif [ $ret -eq ${KSELFTEST_SKIP} ]; then
+                       printf "[SKIP]\n"
+                       err_flush
+               fi
+       done
+done
+
+[ ${passed} -eq 0 ] && exit ${KSELFTEST_SKIP}