Drop the attack in the kernel.
Normally Kapkan asks a router to drop the attack. This drops it itself. On the machine Kapkan runs on, it loads a tiny program into the Linux kernel (XDP) that throws away attack packets the instant they arrive — no router, no waiting. Same detection, same safety checks, one less thing that has to say yes.
- Linux 5.15+
- Nothing to compile on the box
- Watch-only by default
- Rules expire in the kernel

Three live detections, each dropped in the kernel — not announced to a router.
Announce, or drop.
Kapkan has always turned a detection into a BGP message — a blackhole route, or a precise FlowSpec rule — and left the actual dropping to your routers. The data plane adds a second option: do it yourself.
Announce (RTBH / FlowSpec)
Kapkan tells a router what to drop. The router does the work, wherever it sits in your network.
- Reaches every IP range your routers carry, far upstream of any single machine.
- Needs a router that speaks BGP, and a session it already trusts.
- Matches on packet headers only — it can't give each source its own rate limit.
Drop (in-kernel XDP)
Kapkan drops the packet itself, in the kernel of the machine running it, before the rest of the system even sees it.
- No router, no BGP session, no waiting — the rule is just an entry in a kernel table.
- Runs at the earliest point software can touch a packet: the driver's receive path.
- Gives each attacking source its own budget — the one thing FlowSpec can't do.
From detection to dropped packet.
The data plane plugs in at the same point as every other mitigation, so a detection reaches it having already passed every safety check. Only the last step changes: instead of announcing a route, the rules become entries in kernel tables.
The detector fires
The same sampling-corrected limits, learned baselines and classifier as always. Detection doesn't change just because the mitigation does.
Rules are generated
The detection produces the very rules it would have announced as FlowSpec — aimed at the victim, at most a handful per attack — but through a second encoder instead of the BGP one.
Rules load into the kernel
The rules are written into the XDP program's kernel tables, double-buffered so a reload swaps a whole set at once — with no moment where traffic goes unmatched.
The kernel decides
For every packet the program checks a fixed list — allow-list, static rules, victim match, per-source budget — and returns pass or drop. The default is always pass.
Per-source rate limiting — the one thing FlowSpec can't do.
A FlowSpec rule can match a flow and drop it, or cap it to one shared speed. It has no way to say “hold every single source to N”. The data plane does: each attacking source gets its own token bucket in the kernel. Set the limit to N and each source is held to N — instead of a thousand sources and your real users all fighting over one shared ceiling.
This is the one thing BGP simply can't do — not a faster version of an old trick, but a new one.
Safe by design.
The data plane keeps every safety property Kapkan already had, because it plugs in at the same point — and adds one the kernel enforces on its own.
Watch-only is still the default
Until you switch it live, the program attaches, matches and counts exactly as it would in production — but every “drop” becomes a “pass”. You see what it would do before it does anything.
Rules expire inside the kernel
Every generated rule carries its own deadline, and the program treats an expired rule as gone. A Kapkan that is killed, hung or restarted can't leave a victim's traffic dropped: the kernel forgets on schedule, with no daemon needed.
The protected list is enforced in the kernel
Your protected list is checked in the program itself, by both source and destination — so a protected host inside a blocked range keeps receiving traffic, with no trip back to user space.
The default is pass
Anything the rules don't explicitly match is forwarded. There's no hidden “deny everything”: even the one packet shape the program can't fully inspect is passed and counted, not dropped.
Measured, not claimed.
Eighteen recorded attacks run end to end on every change — fake telemetry into the real detector, its rules loaded into real kernel tables, then the recorded packets replayed through the program, with legitimate traffic mixed in the whole time.
recorded attacks, every build
of attack traffic dropped on 17 of 18 (98.5% on the per-source rate-limit test)
legitimate packets dropped · 0 allow-listed packets dropped
kernels the full suite runs on in CI (5.15, 6.1, 6.6, 6.12)
These are block rates, not throughput. A block rate tells you what share of an attack the rules catch; it says nothing about how many packets a given machine can absorb, which depends on your NIC, driver, CPU and whether the program attached in native or generic mode. Size your deployment on your own hardware.

One live detection, expanded in the console: the escalation ladder holding at In-kernel drop (XDP), and the exact rule it loaded into the kernel — dst 203.0.113.45/32, proto udp → discard.
The honest limits.
Two things worth knowing before you deploy it — stated here rather than found later.
One IPv6 packet shape is passed without checking
An IPv6 packet with more than eight extension headers is forwarded without a rule being checked — walking a longer chain would cost the program more kernel budget than it has. This is on purpose: a parse limit that dropped packets would be a hidden “deny everything”. No real traffic chains eight headers, so it is counted and shown — the CLI and console flag any change on that counter — not quietly buried.
Native and generic attach differ in capacity
On a driver with native XDP support the program runs in the driver's receive path, before the kernel builds its packet buffer. Without it, the kernel falls back to generic mode — correct, but it does far less per CPU core. Kapkan reports which mode each interface got; plan capacity around native, and treat generic as a working fallback, not the target.
What it needs.
No agent, no sidecar, no compiler on the box. The program ships as ready-to-run bytecode inside the binary.
- Linux 5.15 or newer, with BTF (CONFIG_DEBUG_INFO_BTF=y — every mainstream distro kernel has it).
- CAP_BPF and CAP_NET_ADMIN, and a writable bpffs at /sys/fs/bpf.
- An interface to attach to. Native XDP if the driver supports it; generic otherwise.
- Nothing to build. The XDP program is compiled ahead of time and shipped inside the Kapkan binary.
Drop it yourself.
Add a dataplane block, leave watch-only on, and see what it would drop before it drops anything.