A tool that utilizes an awesome Rust library called Aya to deploy a simple XDP stats program. This tool increments counters for packets and bytes using a per CPU array map and displays the counters inside of the user-space program. There is a matched stat which is only incremented when packets arrive on a configured protocol (TARGET_PROTO, default: UDP) and port (TARGET_PORT, default: 8080).
The goal of this project is to replicate functionality from my XDP Stats project I made years ago in C using Rust and Aya along with adding additional features. The XDP/eBPF kernel program is written in Rust as well!
You can display the counters in rate per second or as a total count as seen in the demo above. With that said, there's also an included watch mode that displays stats in a TUI (by passing the -w flag). This is useful for monitoring stats in real time!
- Support for calculating packet counters in both the XDP eBPF program and inside of AF_XDP sockets.
- Supports the option to have matched packets dropped or TX'd (with FIB lookup support!).
- This is great if you want to test both drop and TX actions!
- If FIB lookup isn't enabled, the ethernet, IP, and transport headers are swapped and the packet is sent back out the same interface it arrived on.
- Support for attaching to multiple interfaces.
- Display packet counters including total packets/bytes for different types of stats including matched, error, bad, dropped, and passed packets.
- Support for displaying stats in rate per second instead of total count.
- Support for a watch mode that displays stats in a TUI and updates in real time (as seen in the demo above).
This project is in early development. While the core features are functional, users may encounter bugs depending on specific configurations. If you find a bug or have a suggestion, please open an issue.
I developed this project using Debian 13 and the following commands should prepare your environment to run this project. I'd recommend giving this documentation a read as well!
# Install required packages through apt.
sudo apt install -y git curl cmake pkg-config libssl-dev llvm-19-dev libclang-19-dev libpolly-19-dev libelf-dev libpcap-dev
# Install Rust using rustup.
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# We need the Rust stable and nightly toolchains.
rustup install stable
rustup toolchain install nightly --component rust-src
# Install linker.
cargo install bpf-linker
# This is optional, but will be needed if you want to generate your own Aya project:
# cargo install cargo-generate
# cargo generate https://github.com/aya-rs/aya-templateNOTE - The default README that comes with Aya projects may be found here. This README contains more information for other operating systems!
Building and running this project is simple as long as you've ran the steps in the preparing section above.
You can use git to clone the project.
# Clone repository using Git
git clone https://github.com/gamemann/xdpstats-rs.git
# Change to project directory.
cd xdpstats-rsYou can then build and run the project using the following commands.
# Only build project.
cargo build
# Build project for release.
cargo build --release
# Build and run project in dev mode.
cargo run # Will fail if eth0 doesn't exist.
# Run project in release mode.,
cargo run --release
# Run project in release mode on interface 'enp1s0'.
cargo run --release -- -i enp1s0
# If you want to install to $PATH (/usr/bin), please use the included script!
sudo ./install_to_path.sh
# Now use it like a normal command!
sudo xdpstats -i enp1s0 -w -pThere are three features you may build the tool with: afxdp, tx, tx_fib.
| Feature | Description |
|---|---|
afxdp |
Forwards matched packets to AF_XDP sockets to calculate. |
tx |
Sends the packet back out through the same interface and swaps the packet headers including ethernet addresses, IP addresses, and layer 4 ports. |
tx_fib |
Instead of swapping ports when TX'ing the packet, performs a FIB (Forwarding Information Base) lookup to determine the next hop. |
To build the tool, you may pass F or --features when running or building.
For example:
# Build with AF_XDP support.
cargo build --features afxdp
# Build with TX support.
cargo build --features tx
# Build with TX_FIB support.
cargo build --features tx,tx_fib
# Build with AF_XDP and TX support.
cargo build --features afxdp,tx
# Build for release (AFXDP + TX).
cargo build --release --features afxdp,txThe following command line options are supported.
| Args | Default | Description |
|---|---|---|
-i --iface |
eth0 |
The interface(s) to attach the XDP program to. You may separate interfaces with commas (e.g. eth0,eth1). |
-l --list |
- | If set, lists all setting values and exits. |
-L --log |
info |
The log level to use. Supported values are trace, debug, info, warn, and error. |
-B --backlog |
200 |
The amount of log lines to keep in the buffer if using the watch mode. |
-w --watch |
- | If set, enables watch mode which displays log lines and stats in a TUI. |
-d --duration |
0 |
How long to run the program for in seconds (0 = unlimited util CTRL + C). |
-s --skb |
- | If set, attempts to load the XDP program in SKB mode which is slower, but more compatible. |
-o --offload |
- | If set, attempts to offload the XDP program to the NIC hardware (only certain NICs support this). |
-z --replace |
- | If set, passes the REPLACE flag when attaching the XDP program which replaces the XDP program if it is already loaded. For some reason this results in a crash. Otherwise it would be set by default. |
-p --per-sec |
false |
If set, displays stats in rate per second instead of total count. |
-N --sec-name |
xdp_stats |
The section name to use for the XDP program in the eBPF ELF file. This is only needed if you change the section name in the eBPF program. |
Here are settings for AF_XDP sockets.
| Args | Default | Description |
|---|---|---|
-n --num-socks |
0 |
The amount of AF_XDP sockets to create if using AF_XDP mode (0 = Auto). |
-b --batch-size |
64 |
The batch size to use when polling AF_XDP sockets. |
-r --rx-sz |
2048 |
The RX ring size to use for AF_XDP sockets. |
-t --tx-sz |
2048 |
The TX ring size to use for AF_XDP sockets. |
-c --cq-sz |
2048 |
The completion queue size to use for AF_XDP sockets. |
-F --fq-sz |
2048 |
The fill queue size to use for AF_XDP sockets. |
-f --frame-sz |
2048 |
The maximum amount of bytes a frame can hold for AF_XDP sockets. |
-m --frame-cnt |
4096 |
The amount of frames to use for AF_XDP sockets. |
-u --wakeup |
false |
If set, enables wakeup mode for AF_XDP sockets which uses interrupts instead of busy polling. |
-x --zero-copy |
false |
If set, enables zero copy mode for AF_XDP sockets which allows packets to be sent and received without copying data between user-space and kernel-space. |
-S --shared-umem |
false |
If set, allows multiple AF_XDP sockets to share the same UMEM which can reduce memory usage. |
-q --queue-id |
None |
The queue ID to use for AF_XDP sockets (0 = Auto; set to core ID from 0). |
-p --poll-timeout |
100 |
The amount of time in milliseconds to wait when polling AF_XDP sockets. |
There are constants you may change in the xdpstats-common/src/config.rs file. You will need to rebuild the tool after changing these values!
/* CONFIG OPTIONS */
/* -------------------------------- */
// The target protocol to match.
// You may use IpProto::Tcp, IpProto::Icmp, etc.
pub const TARGET_PROTOCOL: u8 = IpProto::Udp as u8;
// The target port to match packets on.
// Set this to 0 for no port matching.
pub const TARGET_PORT: u16 = 8080;
// The path to the ELF file to load with eBPF.
// Relative to $OUT_DIR env var, but you shouldn't need to change this.
pub const PATH_ELF_FILE: &str = "xdpstats";
// Max CPUs supported by the program.
pub const MAX_CPUS: usize = 256;
/* -------------------------------- */
/* CONFIG OPTIONS END */There are four additional XDP sections that may be loaded with the -N --sec argument.
xdp_stats_pass_simple- A simple XDP program that literally only returnsXDP_PASS. This is useful for testing and debugging. This does not increment the per-CPU stats map. Therefore you'll be relying on packet counters from the NIC (usually accessible throughethtool -S <iface>).xdp_stats_drop_simple- A simple XDP program that literally only returnsXDP_DROP. This is useful for testing and debugging. This does not increment the per-CPU stats map. Therefore you'll be relying on packet counters from the NIC (usually accessible throughethtool -S <iface>).xdp_stats_pass_simple_stats- Does whatxdp_stats_pass_simpledoes, but also increments thepassedstat for every packet.xdp_stats_drop_simple_stats- Does whatxdp_stats_drop_simpledoes, but also increments thedroppedstat for every packet.
