xdp — XDP/eBPF drops via xdp-filter

Drops packets in the NIC driver, before the kernel network stack ever sees them, using xdp-filter from xdp-tools. The performance option for very large ban books or packet floods on Linux.

[kur.edge]
backend = "xdp"

[kur.edge.options]
interfaces = [ "eth0" ]

How it works

init loads the xdp-filter program onto each listed interface with a default allow policy for both families:

xdp-filter load -f ipv4,ipv6 -p allow -m native eth0

Bans then add the IP to the program's BPF map blocklist (xdp-filter ip <ip> -m src), unbans remove it (... -r). Because every packet from the IP dies pre-stack, there is no kill option and no need for one — established flows stop receiving anything the moment the ban lands.

Requirements

Settings

Options

| option | default | what | |------------------|--------------|--------------------------------------------------------------| | interfaces | (required) | array of interfaces to load the program onto, e.g. ["eth0"] | | mode | src | match direction for banned IPs — src or dst | | xdp_mode | native | attach mode: native, skb, hw, or unspecified | | xdp_filter_cmd | xdp-filter | the xdp-filter binary |

mode = "dst" turns the kur into an outbound blocker (drop traffic to the banned IPs) — occasionally useful, but src is the ban semantics everything else in Ereshkigal assumes.

What each operation runs

| operation | commands | |------------|------------------------------------------------------------------| | init | xdp-filter load -f ipv4,ipv6 -p allow -m <xdp_mode> <iface> per interface | | ban | xdp-filter ip <ip> -m <mode> | | unban | xdp-filter ip <ip> -m <mode> -r | | list | no command — the kur's own ban book | | check | xdp-filter status exits 0 and lists every configured interface as loaded | | flush | xdp-filter ip <ip> -m <mode> -r per banned IP | | re_init | teardown (best effort), init, re-add every banned IP | | teardown | xdp-filter unload <iface> per interface (ban book kept) |

self_heal

check runs the global xdp-filter status and requires each of this kur's configured interfaces to show as loaded in its output — the status exits 0 even with an interface unloaded, so the exit code alone would miss it. An externally unloaded interface therefore fails check and self_heal re_inits on the next ban/unban. What check cannot see is the map's contents: IPs removed from the map by hand stay gone until re_init.

Gotchas