--- /dev/null
+# Test skeleton for kernel fixes:
+# b2f742c846ca netfilter: nf_tables: restart set lookup on base_seq change
+# a60f7bf4a152 netfilter: nft_set_rbtree: continue traversal if element is inactive
+# .. and related patches.
+#
+# Generate traffic and then flush the set contents and replace
+# them with the same matching entries.
+#
+# Fail when a packet gets through.
+
+# global variables:
+# R, S, C (network namespaces).
+# ip_s (server address)
+
+# helpers:
+# set_flush_add_atomic_cleanup
+# set_flush_add_create_topo
+# set_flush_add_atomic_run_test
+
+[ -z "$TIMEOUT" ] && TIMEOUT=30
+
+set_flush_add_atomic_cleanup()
+{
+ local tmp="$1"
+ local i
+
+ rm -f "$tmp"
+
+ ip netns exec $R $NFT --debug netlink list ruleset
+
+ for i in $C $S $R;do
+ kill $(ip netns pid $i) 2>/dev/null
+ ip netns del $i
+ done
+}
+
+check_counter()
+{
+ local tmp="$1"
+ local then="$2"
+
+ if ip netns exec $R $NFT list chain ip filter block-spoofed | grep -q 'counter packets 0 bytes 0'; then
+ return 0
+ fi
+
+ local now=$(date +%s)
+ echo "$0 failed counter check after $((now-then))s"
+
+ rm -f "$tmp"
+ kill $(ip netns pid $C) 2>/dev/null
+ return 1
+}
+
+load_ruleset()
+{
+ local type="$1"
+ local flags="$2"
+ local elements="$3"
+ local expr="$4"
+
+ip netns exec $R $NFT -f - <<EOF
+table ip filter {
+ set match {
+ type $type
+ $flags
+ elements = { $elements }
+ }
+
+ set bogon {
+ type ipv4_addr . ipv4_addr . icmp_type
+ flags dynamic
+ size 8
+ timeout 5m
+ }
+
+ chain block-spoofed {
+ type filter hook prerouting priority filter; policy accept;
+ $expr @match accept
+ counter add @bogon { ip saddr . ip daddr . icmp type } comment "must not match"
+ }
+}
+EOF
+}
+
+reload_set()
+{
+ local tmp="$1"
+ local elements="$2"
+
+ while [ -f "$tmp" ]; do
+ip netns exec $R $NFT -f - <<EOF
+flush set ip filter match
+create element ip filter match { $elements }
+EOF
+ if [ $? -ne 0 ];then
+ echo "reload of set failed unexpectedly"
+ rm -f "$tmp"
+ exit 1
+ fi
+
+ done
+}
+
+monitor_counter()
+{
+ local tmp="$1"
+ local then="$2"
+
+ while [ -f "$tmp" ]; do
+ sleep 1
+ check_counter "$tmp" "$then" || return 1
+ done
+
+ return 0
+}
+
+wait_for_timeout()
+{
+ local tmp="$1"
+ local rc=0
+
+ local then=$(date +%s)
+ local end=$((then+TIMEOUT))
+
+ while [ -f "$tmp" ];do
+ local now=$(date +%s)
+ [ "$now" -ge "$end" ] && break
+ sleep 1
+ done
+
+ test -f "$tmp" || rc=1
+ rm -f "$tmp"
+
+ return $rc
+}
+
+set_flush_add_create_topo()
+{
+ local test="$1"
+ local ip_r1=192.168.2.1
+ local ip_r2=192.168.3.1
+ # global
+ ip_c=192.168.2.30
+ ip_s=192.168.3.10
+
+ rnd=$(mktemp -u XXXXXXXX)
+ C="$test-client-$rnd"
+ R="$test-router-$rnd"
+ S="$test-server-$rnd"
+
+ ip netns add $S
+ ip netns add $R
+ ip netns add $C
+
+ ip link add veth0 netns $S type veth peer name rs netns $R
+ ip link add veth0 netns $C type veth peer name rc netns $R
+
+ ip -net $S link set veth0 up
+ ip -net $C link set veth0 up
+ ip -net $R link set rs up
+ ip -net $R link set rc up
+ ip -net $S link set lo up
+ ip -net $C link set lo up
+ ip -net $R link set lo up
+
+ for n in $S $R $C;do
+ ip netns exec $n sysctl -q net.ipv4.conf.all.rp_filter=0
+ done
+
+ ip netns exec $R sysctl -q net.ipv4.ip_forward=1
+
+ ip -net $S addr add ${ip_s}/24 dev veth0
+ ip -net $C addr add ${ip_c}/24 dev veth0
+ ip -net $R addr add ${ip_r1}/24 dev rc
+ ip -net $R addr add ${ip_r2}/24 dev rs
+
+ ip -net $C route add default via ${ip_r1} dev veth0
+ ip -net $S route add default via ${ip_r2} dev veth0
+
+ ip netns exec $S ping -q -c 1 -i 0.1 ${ip_c} || exit 1
+ ip netns exec $C ping -q -c 1 -i 0.1 ${ip_s} || exit 2
+}
+
+start_ping_flood()
+{
+ for i in $(seq 1 4);do
+ timeout $TIMEOUT ip netns exec $C ping -W 0.00001 -l 200 -q -f ${ip_s} &
+ done
+
+ wait
+}
+
+set_flush_add_atomic_run_test()
+{
+ local tmp="$1"
+ local type="$2"
+ local flags="$3"
+ local elements="$4"
+ local expr="$5"
+
+ local then=$(date +%s)
+ local now=$(date +%s)
+
+ load_ruleset "$type" "$flags" "$elements" "$expr"
+
+ # sanity check, counter must be 0, no parallel set flush/elem add yet.
+ check_counter "$tmp" "$then" || exit 3
+
+ start_ping_flood &
+
+ reload_set "$tmp" "$elements" &
+
+ monitor_counter "$tmp" "$then" &
+
+ wait_for_timeout "$tmp" || return 1
+ wait
+
+ check_counter "$tmp" "$then" || return 1
+
+ local now=$(date +%s)
+ echo "$0 test took $((now-then))s"
+ return 0
+}