Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
5c13155
dkms: build module with KCFLAGS=-O3
Sep 25, 2026
173bdd6
add direct banset backend and xtables match
Sep 25, 2026
84154b8
Add direct banset XDP acceleration
Sep 25, 2026
75b6379
Make banset build helper executable
Sep 25, 2026
5fba8f0
Embed banset source revision
Sep 25, 2026
7d3fe41
banset: add native NAPI and i40e batch hooks
Sep 25, 2026
845d99e
banset: cache batch bindings per receive device
Sep 25, 2026
fb77769
banset: build AVX2 asm under kernel no-SIMD flags
Sep 25, 2026
6d84e34
banset: protect AVX2 asm input registers
Sep 25, 2026
4a50e70
banset: support legacy ipset module autoload name
Sep 25, 2026
9294cd3
Optimize banset batch lookup and early hook
Sep 26, 2026
eca8f4a
Allow live banset prefetch tuning
Sep 26, 2026
ccd8967
Allocate hook counters outside module percpu reserve
Sep 26, 2026
13bedc1
Reduce banset batch refresh overhead
Sep 26, 2026
ce0cbad
Optimize native banset receive batches
Sep 26, 2026
9bc3ddd
Make native batch stages independently selectable
Sep 26, 2026
1412d16
Allow runtime native batch experiment switches
Sep 27, 2026
d217c4a
Allocate native batch scratch dynamically
Sep 27, 2026
19059ce
Allow transactional table layout selection
Sep 27, 2026
4be52a8
export banset provider for x4b hpfw
Sep 27, 2026
d19bdac
report exact native refresh diagnostics
Sep 27, 2026
5826bc1
count successful native expiry refreshes
Sep 27, 2026
1a1233f
perf: enable accepted banset batch defaults
Sep 27, 2026
ce20c3d
continue
Sep 27, 2026
ef6b675
fixes
Sep 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@
*.so
*_sh.o
.*.cmd
.*.o.d
modules.order
Module.symvers

# Generated files
src/Makefile
src/Makefile
src/compat_def.h
47 changes: 45 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,49 @@ You can disable DKMS during configuration if needed:
./configure --disable-dkms-install # Build with DKMS support but don't auto-install
```

## xt_setban
## xt_banset

a single rule banning module
`xt_banset` owns the exact-pair `hash:ip,ip,flag` ipset type and supplies a
packet lookup path that does not enter the ipset core. Sets must have a positive
timeout of at most 1,048,513 seconds and accept only scalar source/destination
addresses, an 8-bit flag, and IPv4 or IPv6 families.

```bash
ipset create ban hash:ip,ip,flag family inet timeout 600 maxelem 2097152
ipset add ban 192.0.2.1,198.51.100.10,7 timeout 600
iptables -t raw -A PREROUTING \
-m banset --ban-set ban --ban-mode refresh --ban-probability 0.01 \
-j DROP
```

The direct match modes are `match`, `refresh`, and `add`. Probability applies
to refresh/add mutations; a refresh rule still reports the membership result
on every packet. Ranges, networks, permanent entries, counters, comments,
skbinfo, and force-add are intentionally unsupported.

## XDP acceleration

The XDP-capable branch builds `src/x4b_banset_xdp.bpf.o`. Its production
program is in section `xdp/x4b_banset`; it looks up the live `ban`/`ban6`
tables through `bpf_x4b_banset_match`, refreshes hits with a 1% sample, records
NetFlow status 32 through `bpf_x4b_netflow_xdp_record`, and drops the packet.
Lookup errors, malformed packets, unsupported protocols, and misses pass to the
normal stack, where a direct netfilter rule should remain installed as fallback.

The object also contains `xdp/x4b_banset_lookup` (drop hits without NetFlow)
and `xdp/x4b_pass` sections for controlled benchmarks. Per-CPU pass, hit, drop,
lookup-error, and NetFlow-error counters are exposed in the
`x4b_banset_stats` map.

## Native receive hook

The versioned GPL provider exported by `xt_banset.ko` is consumed by the
separate `x4b_hpfw.ko` module. Keeping hook ownership out of this repository
allows HPFW to enter an RCU-safe bypass before the banset control-plane module
is reloaded. The optional XDP object remains available as a rollback path.

The build probes the selected kernel with kbuild rather than inferring support
from its release number. When the versioned HPFW headers and receive-hook ABI
are absent, `xt_setset.ko`, `xt_banset.ko`, and their userspace plugins still
build normally, but the HPFW provider and XDP kfunc are omitted from
`xt_banset.ko`.
8 changes: 6 additions & 2 deletions build.sh
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ fi

DEFAULT_KERNEL_RELEASE_PATH="/lib/modules/$KVER/build/include/config/kernel.release"
HEADER_PACKAGE="linux-headers-$KVER"
PACKAGES=(build-essential git iptables libxtables-dev pkg-config)
PACKAGES=(bpftool build-essential clang dwarves git ipset iptables libbpf-dev libipset-dev libxtables-dev pkg-config)

if [[ -z "$KDIR" && ! -f "$DEFAULT_KERNEL_RELEASE_PATH" ]]; then
PACKAGES+=("$HEADER_PACKAGE")
Expand Down Expand Up @@ -204,7 +204,11 @@ echo "Produced artifacts:"
for artifact in \
"$SRC_DIR/Makefile" \
"$SRC_DIR/xt_setset.ko" \
"$SRC_DIR/libxt_setset.so"
"$SRC_DIR/xt_banset.ko" \
"$SRC_DIR/libxt_setset.so" \
"$SRC_DIR/libxt_banset.so" \
"$SRC_DIR/libipset_hash_ipipflag.so" \
"$SRC_DIR/x4b_banset_xdp.bpf.o"
do
if [[ -e "$artifact" ]]; then
printf ' %s\n' "${artifact#$SCRIPT_DIR/}"
Expand Down
168 changes: 168 additions & 0 deletions scripts/qemu/setset-local-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
- name: verify xtables match is registered
shell: iptables -m setset -h | grep -q setset

- name: verify banset module and xtables match are registered
shell: |
modprobe xt_banset
iptables -m banset -h | grep -q -- '--ban-set'

- name: test setset rule parsing
shell: |
set -e
Expand Down Expand Up @@ -42,3 +47,166 @@
-j RETURN
args:
executable: /bin/bash

- name: test direct banset control and packet paths
shell: |
set -e

cleanup() {
iptables -w -D INPUT -i lo -p icmp \
-m banset --ban-set banset_ci4 --ban-mode match \
-j DROP 2>/dev/null || true
ip6tables -w -D INPUT -i lo -p ipv6-icmp \
-m banset --ban-set banset_ci6 --ban-mode refresh \
--ban-probability 1 -j DROP 2>/dev/null || true
ipset destroy banset_ci4 2>/dev/null || true
ipset destroy banset_ci6 2>/dev/null || true
ipset destroy banset_expiry 2>/dev/null || true
ipset destroy banset_swap 2>/dev/null || true
}
trap cleanup EXIT
cleanup

ipset create banset_ci4 hash:ip,ip,flag family inet \
timeout 600 maxelem 262144
ipset create banset_ci6 hash:ip,ip,flag family inet6 \
timeout 600 maxelem 262144
ipset create banset_expiry hash:ip,ip,flag family inet \
timeout 32 maxelem 262144
ipset create banset_swap hash:ip,ip,flag family inet \
timeout 600 maxelem 262144
ipset add banset_ci4 127.0.0.1,127.0.0.1,7 timeout 600
ipset add banset_ci6 ::1,::1,9 timeout 600
ipset test banset_ci4 127.0.0.1,127.0.0.1,0
ipset test banset_ci6 ::1,::1,0
ipset save banset_ci4 | grep -q '127.0.0.1,127.0.0.1,7'
ipset save banset_ci6 | grep -q '::1,::1,9'

# Add late in an epoch so a relative, pre-rounded expiry would fire
# after less than the requested two seconds.
while :; do
read -r uptime _ < /proc/uptime
whole=${uptime%.*}
fraction=${uptime#*.}
fraction=${fraction:0:2}
if [ $((whole % 32)) -eq 30 ] && \
[ $((10#$fraction)) -lt 20 ]; then
break
fi
sleep 0.1
done
sleep 1
ipset add banset_expiry 198.51.100.1,192.0.2.1,3 timeout 2
sleep 1
ipset test banset_expiry 198.51.100.1,192.0.2.1,0

iptables -w -I INPUT -i lo -p icmp \
-m banset --ban-set banset_ci4 --ban-mode match -j DROP
! ping -c 1 -W 1 127.0.0.1
iptables-save | grep -q -- '--ban-set banset_ci4 --ban-mode match'

ipset swap banset_ci4 banset_swap
ping -c 1 -W 1 127.0.0.1
ipset destroy banset_swap
ping -c 1 -W 1 127.0.0.1
ipset add banset_ci4 127.0.0.1,127.0.0.1,7 timeout 600
! ping -c 1 -W 1 127.0.0.1

ip6tables -w -I INPUT -i lo -p ipv6-icmp \
-m banset --ban-set banset_ci6 --ban-mode refresh \
--ban-probability 1 -j DROP
! ping -6 -c 1 -W 1 ::1
ip6tables-save | grep -q -- '--ban-set banset_ci6 --ban-mode refresh'

ipset del banset_ci4 127.0.0.1,127.0.0.1,0
! ipset test banset_ci4 127.0.0.1,127.0.0.1,0
ipset flush banset_ci6
! ipset test banset_ci6 ::1,::1,0

sleep 33
! ipset test banset_expiry 198.51.100.1,192.0.2.1,0

! ipset create banset_permanent hash:ip,ip,flag family inet
! ipset create banset_too_long hash:ip,ip,flag family inet \
timeout 1500000
! ipset add banset_ci4 10.0.0.1,192.0.2.1,1 timeout 1500000
! ipset add banset_ci4 10.0.0.0/24,192.0.2.1,1 timeout 600
! ipset add banset_ci4 10.0.0.1,192.0.2.1,256 timeout 600
args:
executable: /bin/bash

- name: test banset growth, save restore, flush, and concurrent updates
shell: |
set -euo pipefail

cleanup() {
ipset destroy banset_growth 2>/dev/null || true
ipset destroy banset_restore 2>/dev/null || true
}
trap cleanup EXIT
cleanup

ipset create banset_growth hash:ip,ip,flag family inet \
timeout 600 maxelem 524288
seq 0 219999 | awk '
{
a = int($1 / 65536); b = int($1 / 256) % 256; c = $1 % 256;
printf "add banset_growth 10.%d.%d.%d,192.0.2.1,32 timeout 600\n", a, b, c
}
' | ipset restore -exist
ipset test banset_growth 10.0.0.0,192.0.2.1,0
ipset test banset_growth 10.3.91.95,192.0.2.1,0

for attempt in $(seq 1 100); do
hashsize=$(ipset list banset_growth -terse |
awk '/^Header:/ { for (i=1; i<=NF; i++) if ($i == "hashsize") print $(i+1) }')
test "${hashsize:-0}" -gt 32768 && break
sleep 0.1
done
test "${hashsize:-0}" -gt 32768

ipset save banset_growth |
sed 's/banset_growth/banset_restore/g' | ipset restore
ipset test banset_restore 10.1.2.3,192.0.2.1,0

(
for i in $(seq 1 2000); do
ipset add banset_growth 11.0.$((i / 256)).$((i % 256)),192.0.2.2,7 \
timeout 600 -exist
ipset del banset_growth 11.0.$((i / 256)).$((i % 256)),192.0.2.2,0 \
2>/dev/null || true
done
) &
updater=$!
for i in $(seq 1 10000); do
ipset test banset_growth 10.0.0.1,192.0.2.1,0 >/dev/null
done
wait "$updater"

ipset flush banset_growth
! ipset test banset_growth 10.0.0.0,192.0.2.1,0
ipset add banset_growth 198.51.100.1,192.0.2.1,99 timeout 600
ipset test banset_growth 198.51.100.1,192.0.2.1,0
args:
executable: /bin/bash

- name: test packed IPv4 metadata flush
ansible.builtin.shell: |
set -eu
cleanup() {
ipset destroy banset_packed 2>/dev/null || true
printf 'N\n' > /sys/module/xt_banset/parameters/packed_meta
}
trap cleanup EXIT
printf 'Y\n' > /sys/module/xt_banset/parameters/packed_meta
ipset create banset_packed hash:ip,ip,flag family inet \
hashsize 262144 maxelem 65536 timeout 600
ipset add banset_packed 203.0.113.1,192.0.2.1,17 timeout 600
ipset test banset_packed 203.0.113.1,192.0.2.1,0
ipset flush banset_packed
! ipset test banset_packed 203.0.113.1,192.0.2.1,0
ipset add banset_packed 203.0.113.2,192.0.2.1,18 timeout 600
ipset test banset_packed 203.0.113.2,192.0.2.1,0
args:
executable: /bin/bash
changed_when: false
56 changes: 47 additions & 9 deletions src/Makefile.in
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,50 @@ KINSTDIR = $(shell dirname @KDIR@)
KOPTS = @KOPTS@
IPTABLES_CFLAGS = @IPTABLES_CFLAGS@
IPTABLES_MODULES = @IPTABLES_MODULES@
IPSET_MODULES ?= $(shell pkg-config --variable=libdir libipset)/ipset
DEPMOD = /sbin/depmod -a $(KVERSION)
CARGS = @CARGS@
CC = gcc
BPF_CLANG ?= clang
BPF_ARCH := $(shell uname -m | sed -e 's/x86_64/x86/' -e 's/aarch64/arm64/')
BPF_SYS_INCLUDES := -I/usr/include/$(shell $(CC) -dumpmachine)

# https://www.kernel.org/doc/Documentation/kbuild/modules.txt
# https://www.kernel.org/doc/Documentation/kbuild/makefiles.txt
X4B_SOURCE_COMMIT = @GIT_COMMIT@
X4B_GIT_COMMIT := $(shell git -c safe.directory=$(abspath $(src)/..) -C $(src)/.. rev-parse --verify HEAD 2>/dev/null || echo $(X4B_SOURCE_COMMIT))
obj-m = xt_setset.o
obj-m = xt_setset.o xt_banset.o
ccflags-y = @KOPTS@ -DX4B_GIT_COMMIT='"$(X4B_GIT_COMMIT)"'

all: xt_setset.ko libxt_setset.so
all: xt_setset.ko xt_banset.ko libxt_setset.so libxt_banset.so libipset_hash_ipipflag.so x4b_banset_xdp.bpf.o

xt_setset.ko: xt_setset.c Makefile
define build_kernel_modules
@btf_link="$(KDIR)/vmlinux"; created=0; \
if test "$(KVERSION)" = "`uname -r`" && test ! -e "$$btf_link" && \
test -r /sys/kernel/btf/vmlinux; then \
ln -s /sys/kernel/btf/vmlinux "$$btf_link" && created=1; \
fi; \
trap 'if test "$$created" = 1 && test "`readlink "$$btf_link"`" = /sys/kernel/btf/vmlinux; then unlink "$$btf_link"; fi' EXIT; \
$(MAKE) -C $(KDIR) M=$(CURDIR) modules CONFIG_DEBUG_INFO=y
endef

xt_setset.ko: xt_setset.c compat_def.h Makefile
@echo Compiling for kernel $(KVERSION)
$(build_kernel_modules)
@touch $@

xt_banset.ko: xt_banset.c xt_banset.h x4b_banset_native.h compat_def.h Makefile
@echo Compiling for kernel $(KVERSION)
make -C $(KDIR) M=$(CURDIR) modules CONFIG_DEBUG_INFO=y
$(build_kernel_modules)
@touch $@
sparse: | xt_setset.c Makefile

sparse: | xt_setset.c compat_def.h Makefile
@rm -f xt_setset.ko xt_setset.o
@echo Compiling for kernel $(KVERSION)
make -C $(KDIR) M=$(CURDIR) modules C=1
@touch xt_setset.ko
compat_def.h: gen_compat_def Makefile
bash ./gen_compat_def > $@
minstall: | xt_setset.ko
@echo " *"
make -C $(KDIR) M=$(CURDIR) modules_install INSTALL_MOD_PATH=$(DESTDIR)
Expand All @@ -38,17 +59,29 @@ mclean:
lclean:
-rm -f *.so *_sh.o
clean: mclean lclean
-rm -f *.so *.o modules.order
-rm -f *.so *.o modules.order compat_def.h

libxt_setset_sh.o: libxt_setset.c
gcc -O2 -Wall -Wunused $(IPTABLES_CFLAGS) -fPIC -o $@ -c libxt_setset.c

%.so: %_sh.o
gcc -shared -o $@ $< -lxtables
gcc -shared -nostartfiles -o $@ $< -lxtables

libxt_banset_sh.o: libxt_banset.c xt_banset.h
gcc -O2 -Wall -Wunused $(IPTABLES_CFLAGS) -fPIC -o $@ -c libxt_banset.c

libipset_hash_ipipflag.so: libipset_hash_ipipflag.c
$(CC) -O2 -Wall -Wextra -fPIC -shared -o $@ $< $$(pkg-config --cflags --libs libipset)

x4b_banset_xdp.bpf.o: x4b_banset_xdp.bpf.c
$(BPF_CLANG) -O2 -g -target bpf -D__TARGET_ARCH_$(BPF_ARCH) \
$(BPF_SYS_INCLUDES) -Wall -Werror -c $< -o $@

linstall: | libxt_setset.so
linstall: | libxt_setset.so libxt_banset.so libipset_hash_ipipflag.so
@echo " *"
install -D libxt_setset.so $(DESTDIR)$(IPTABLES_MODULES)/libxt_setset.so
install -D libxt_banset.so $(DESTDIR)$(IPTABLES_MODULES)/libxt_banset.so
install -D libipset_hash_ipipflag.so $(DESTDIR)$(IPSET_MODULES)/libipset_hash_ipipflag.so

dinstall:
@echo " *"
Expand All @@ -58,17 +91,22 @@ install: dinstall linstall

uninstall:
-rm -f $(DESTDIR)$(IPTABLES_MODULES)/libxt_setset.so
-rm -f $(DESTDIR)$(IPTABLES_MODULES)/libxt_banset.so
-rm -f $(DESTDIR)$(IPSET_MODULES)/libipset_hash_ipipflag.so
@if [ "@DKMSINSTALL@" = dinstall ]; then ./install-dkms.sh --uninstall; fi
-rm -f $(DESTDIR)$(KINSTDIR)/extra/xt_setset.ko
-rm -f $(DESTDIR)$(KINSTDIR)/extra/xt_banset.ko

Makefile: Makefile.in configure
./configure --make ${CARGS}

load: all
-insmod xt_banset.ko
-insmod xt_setset.ko

unload:
-rmmod xt_setset.ko
-rmmod xt_banset.ko

reload: unload load

Expand Down
5 changes: 4 additions & 1 deletion src/dkms.conf
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ popd
BUILT_MODULE_NAME[0]=xt_setset
DEST_MODULE_LOCATION[0]=/kernel/extra
STRIP[0]=no
MAKE[0]="make xt_setset.ko"
BUILT_MODULE_NAME[1]=xt_banset
DEST_MODULE_LOCATION[1]=/kernel/extra
STRIP[1]=no
MAKE[0]="make xt_setset.ko KCFLAGS=-O3"
PRE_BUILD="./configure --from-dkms-conf=$kernel_source_dir"
AUTOINSTALL=yes
Loading