# xdp-firewall drops packets before the stack sees them

> xdp-firewall is a stateless eBPF firewall that attaches to the XDP hook in the Linux kernel, with source IP blocking, rule based filtering, rate limiting, CIDR drops and real-time counters. Rules are managed from the user space through pinned BPF maps, so no restart is needed to change what it drops.

**gamemann/xdp-firewall** — A firewall that utilizes the Linux kernel's XDP hook. The XDP hook allows for very fast network processing on Linux systems. This is great for dropping malicious traffic from a (D)DoS attack. IPv6 is supported with this firewall! I hope this helps network engineers/programmers interested in utilizing XDP!

- Repository: https://github.com/gamemann/xdp-firewall
- Website: https://cdeacon.net
- Stars: 956 · Forks: 134
- Language: C
- License: MIT
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/gamemann-xdp-firewall

## A stateless firewall in the earliest hook

xdp-firewall attaches to the XDP hook in the Linux kernel using eBPF, and the README describes it plainly as a stateless firewall.

The positioning is important and easy to misread. This is not a replacement for a general purpose firewall with connection tracking, NAT or stateful inspection. It is a filter that sees packets at the earliest point in the network stack and makes a decision about each one before the rest of the stack has done any work.

The README explains the appeal in terms of performance: the XDP hook allows for very fast network processing, which is great for dropping malicious traffic from a (D)DoS attack, and it calls the tool powerful for network engineers, security professionals and anyone interested in XDP or high-performance firewalling. The design goal is stated as performance and flexibility.

The use case that follows from that is narrow but real. When a host is under flood and the kernel's own filtering is the bottleneck, a program that can say drop early, in a few instructions per packet, changes what the machine can survive. That is also why the tool exists as a learning artefact: the README hopes it helps people getting into XDP, and mentions that understanding packet flow at a low to medium level would help anyone pursuing a career in the field.

IPv6 is supported, which matters more than it sounds for a tool aimed at network engineers, since a filter that only understands IPv4 leaves a hole in exactly the deployments that have been migrating for a decade.

## Every feature is a switch you can turn off

The design decision that makes this tool flexible is also its main operational hazard, because features are compiled in or out, or set in the config file on disk, and the recommendation is to disable what you do not need.

The README states that all features can be enabled or disabled through the build-time configuration in config.h before compilation, or through runtime configuration on disk. If you are planning to only use certain features such as the source IP block and drop functionality, it is recommended you disable other features like dynamic filtering to achieve best performance.

That is a meaningfully different model from a Linux firewall where you write rules and reload. Here, adding a rule is a runtime operation, but adding a category of rule is a recompile. The reason is visible in the design. Each feature adds work to the per-packet path, and in the XDP hook every instruction is on the critical path for every packet the machine receives. A filter that checks for the presence of a dynamic rules map is doing work on every packet even when the map is empty.

The runtime configuration lives on disk, with the CLI table giving the path to the config file as /etc/xdpfw/xdpfw.conf by default, and the repository ships an example file, xdpfw.conf.example. So the operational split is: what the binary can do is decided when you compile it, and what those capabilities are configured to do is decided in that file at startup.

For a deployment, that means the build configuration becomes part of your change management, in a way it does not for a conventional firewall.

## Rules change through pinned BPF maps

The rule management is what makes the firewall usable in practice, and it works through the standard BPF map pinning mechanism.

Pinned BPF maps allow external programs to interact with firewall rules, and the CLI utilities xdpfw-add and xdpfw-del enable dynamic rule management without restarting the firewall. The README frames this as support for integration with user-space security systems.

The source IP blocking feature works the same way. Packets from known bad addresses are dropped instantly, the lookups use a BPF map for efficiency, and the block list is managed dynamically with those two command line tools.

That combination, a fast in-kernel data path and a user-space control plane, is the shape of most modern packet filtering systems, and it is the reason the tool can be used for a live attack rather than only for a static policy. A firewall you cannot change while the flood is running is a firewall that will be wrong the moment the traffic changes, and attack traffic always changes.

The repository layout backs this up. The Makefile defines separate source directories for the loader and for the two rule tools, src/rule_add and src/rule_del, alongside src/xdp and src/common, with the loader's own utility modules for config, CLI parsing, XDP attachment, logging, statistics and helpers. The rule tools are separate binaries by design, which is what lets an external script or an alerting system push a change into a running firewall.

One detail to watch. The README notes that the features are enabled through config.h or runtime configuration on disk, so the rule tools can only add rules of a kind the binary was built with. A tool that was compiled without CIDR dropping cannot add a CIDR rule later, regardless of how the command is invoked.

## Rate limiting, temporary bans and layer 4 coverage

The filtering features cover more than a static block list, and the README is specific about what is supported.

Dynamic filtering is rule based, letting you define custom rules to allow or drop packets based on protocols, ports, IP addresses and more. It supports temporary bans, by adding IPs to the block list for a configurable duration. It handles the TCP, UDP and ICMP layer 4 protocols, and IPv6.

Rate limiting is included in both directions of the source: the README lists both source flow based and IP based rate limiting, and describes the feature as ideal for mitigating non-spoofed (D)DoS attacks. That qualifier is the honest part of the claim. Rate limiting on a per source flow basis cannot help against spoofed traffic, because the flow identity is the thing the attacker is forging.

The counters come with it. Real-time packet counters track allowed, dropped and passed packets, with support for per-second statistics for traffic analysis. The logging system writes to the terminal and or to a file, with configurable verbosity levels.

Taken together, these features describe a tool aimed at a specific incident: a flood from real, identifiable sources. The XDP hook drops the packets the kernel would otherwise have to allocate, track and process, and the counters give you evidence of what was dropped rather than a guess.

## CIDR drops are compiled in, and off by default

One feature is called out separately because of how it is enabled.

IP range dropping blocks entire IP subnets efficiently at the XDP level, using CIDR-based filtering such as 192.168.1.0/24. The README states that it is disabled by default but can be enabled in config.h.

A default of off for subnet blocking tells you something about the priorities. The source IP block list is a hash lookup on a single address, which is cheap, while a CIDR lookup means matching a packet address against a set of prefixes, which is a different and more expensive operation on the per-packet path. So the choice to leave it out of the default build is a choice about what the common case should cost.

This is the clearest example of the compile-time feature model in practice. Someone defending a network with a known hostile subnet turns the feature on at build time, accepts the per-packet cost, and gains the ability to drop whole ranges without enumerating every address. Someone defending a host under spray traffic leaves it off and pays only for the exact-address lookups.

The same reasoning applies to the dynamic filtering feature, which the README also suggests disabling if you are only using source IP blocking. Together these two recommendations describe two distinct deployment profiles, a static hash-based blocker and a full rule engine, and the project is telling you not to pay for both at once.

## Two build paths, one set of dependencies

Building xdp-firewall means building XDP-Tools as well, because the XDP and BPF libraries are consumed as a sub-module rather than as system packages.

The dependency list for a Debian-based system is short. The main packages are libconfig-dev, llvm, clang, libelf-dev and build-essential, with libpcap-dev, m4 and gcc-multilib needed for LibXDP and LibBPF, and the kernel tools package for your running kernel, since BPFTool is required:

```bash
sudo apt install -y libconfig-dev llvm clang libelf-dev build-essential
sudo apt install -y libpcap-dev m4 gcc-multilib
sudo apt install -y linux-tools-$(uname -r)
```

Cloning needs the recursive flag so the XDP Tools sub-module comes down with it, or you run the sub-module update command inside the clone afterwards:

```bash
git clone --recursive https://github.com/gamemann/XDP-Firewall.git
cd XDP-Firewall
```

From there there are two documented routes. The Bash script is described as the easiest way to build and install the firewall, and it relies on sudo being installed, with ./install.sh --libxdp recommended if you do not have LibXDP on the system yet. The script also takes --no-install, --clean, --no-static, --objdump and --help.

The manual route is the Makefile, and the order matters because LibXDP has to exist before the firewall links against it:

```bash
make libxdp
sudo libxdp_install
make
sudo make install
```

Both of the sudo steps in that sequence are called out in the README as needing root, and one warning is worth repeating: the --no-static option makes the build faster but you may need to alter LD_LIBRARY_PATH before running the tool, and it requires LibXDP to be installed on the system already.

## Loader flags, counters and the state of the project

The runtime interface of the loader is small and worth reading before you deploy.

The -c, --config flag takes the path to the config file, defaulting to /etc/xdpfw/xdpfw.conf. The -o, --offload flag attempts to load the XDP program in hardware or offload mode, which moves the filtering into the NIC where the hardware supports it. The -s, --skb flag forces the XDP program to be loaded using SKB mode instead of DRV mode, which is the generic path used when driver mode is not available.

Those two flags are the practical decision between a fast path that needs hardware support and a portable one that does not. DRV mode is the native mode and SKB mode is the fallback, and the flag exists so you can choose the fallback deliberately rather than by accident.

The project is MIT licensed, with the licence file in the repository as LICENSE.md. The homepage given for the project is cdeacon.net.

On maintenance, the repository is not archived and the last push was on 2026-05-10, and there are no GitHub releases. So there is no tagged version to pin and no changelog to consult; you would be running the master branch as it stands. The build and run workflows are visible in the badges, which means continuous integration is configured even though the release story is thin.

For a tool meant to sit on a machine under load, that combination is worth weighing. The code is small enough to read and the dependencies are standard Linux ones, but the absence of releases means an upgrade is a re-clone rather than a version bump, and the responsibility for tracking upstream changes sits with whoever deploys it.

## Conclusion

xdp-firewall suits network engineers who need to shed traffic at the earliest possible point, on a host or appliance where a connection tracking firewall would be the bottleneck, and it suits people learning XDP because the source is small and the feature switches are explicit. It is the wrong tool for anything that needs connection state, since the firewall is stateless by design, and the wrong tool for a machine where you cannot install kernel headers and BPF tooling. Before you deploy it, decide which features you actually need and compile the rest out, because the README is explicit that disabling unused features is how you get the best performance, and read the CLI table for the loader flags, since the default config path and the DRV versus SKB mode are choices you make at startup.

## FAQ

### What is the XDP hook and why does xdp-firewall use it?

XDP is a Linux kernel hook that allows for very fast network processing, running at the earliest point in the network stack for minimal latency. xdp-firewall is a stateless firewall that attaches there using eBPF, which lets it drop malicious traffic before the rest of the stack has to process it.

### How do I add or remove a blocked IP in xdp-firewall?

The CLI utilities xdpfw-add and xdpfw-del manage rules dynamically without restarting the firewall, and the rule lists live in pinned BPF maps that external programs can interact with.

### How do I build and install xdp-firewall?

Clone the repository with the recursive flag so the XDP Tools sub-module is included, then either run ./install.sh --libxdp or use make: make libxdp, sudo libxdp_install, make and sudo make install. The build needs clang, llvm, libconfig-dev, libelf-dev, build-essential and the kernel tools package for your running kernel.

### Which features do I have to compile in?

Features are enabled or disabled through the build-time configuration in config.h, and the README recommends disabling features you will not use, such as dynamic filtering, to get the best performance. CIDR based IP range dropping is disabled by default and enabled in config.h.

### Does xdp-firewall support IPv6 and stateful filtering?

IPv6 is supported, along with the TCP, UDP and ICMP layer 4 protocols and both source flow based and IP based rate limiting. The firewall is stateless by design, and the README describes it as ideal for mitigating non-spoofed (D)DoS attacks.

## Sources

- [gamemann/xdp-firewall on GitHub](https://github.com/gamemann/xdp-firewall)
- [Issues](https://github.com/gamemann/xdp-firewall/issues)
- [License: MIT](https://github.com/gamemann/xdp-firewall/blob/master/LICENSE)
- [Project website](https://cdeacon.net)
- [README](https://github.com/gamemann/xdp-firewall/blob/master/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/gamemann-xdp-firewall
