A lightweight eBPF/XDP-based traffic manager and loader for Linux.
This repository contains an XDP/eBPF program and a Go userspace loader to build, load, and test the packet processing program.
Contents
ebpf/src/xdp_prog.c— BPF entry point; role-based BPF modules and headers live underebpf/src/andebpf/include/.cmd/kelvosd/main.go— Go userspace loader and Cobra CLI.internal/config— TOML loading and validation.internal/events— eBPF perf-event ABI decoding and JSON event shaping.internal/loader— XDP attachment and perf-buffer lifecycle.internal/output— buffered JSONL event output.config/kelvos_config.tomlandbuild/kelvos_config.toml— example configuration files.build/— CMake build artifacts and helper build scripts.
Requirements
- Linux kernel with eBPF and XDP support (recent kernel recommended).
clangandllc(orclangwith-target bpf) for compiling BPF programs.cmakeandmakefor compiling the eBPF object.- Go 1.23 or newer for the userspace CLI.
- Linux kernel headers and the Go dependencies declared in
go.mod.
Quick start
- Build the project (out-of-tree recommended):
mkdir -p build && cd build
cmake ..
make -j- Configure the loader
Create or edit config/kelvos_config.toml with a [traffic_monitor] table and
dynamic protocol/port policy. Example:
[traffic_monitor]
enabled = true
ethernet_interface = "enp1s0"
log_file = "/var/log/kelvos_traffic_monitor.jsonl"
[protocols]
tcp = 6
udp = 17
[[ports]]
service = "http"
numbers = [80, 8080]
protocols = ["tcp"]
description = "Web traffic"Only packets matching a configured protocol and source or destination port are
sent from XDP to userspace. Add more [[ports]] entries without recompiling
the BPF object.
Firewall rules can control ingress packet handling directly in XDP:
[firewall]
default_ingress = "drop"
default_egress = "allow"
[[rules]]
id = 100
action = "allow"
direction = "ingress"
protocols = ["tcp"]
destination_ports = [22]
[[rules]]
id = 101
action = "allow"
direction = "ingress"
state = ["established", "related"]Ingress rules with action = "drop" return XDP_DROP; allowed packets are
sent to userspace as traffic events. state accepts new, established,
related, and invalid; omitted protocol and destination-port filters match
any protocol and port. TCP becomes established after the SYN, SYN-ACK, ACK
handshake and is removed after RST or both FINs. related matches ICMP errors
whose quoted TCP/UDP tuple belongs to a tracked flow; malformed or unmatched
ICMP errors are invalid. Tracked flows expire after five idle minutes and
inactive entries are also removed by LRU eviction. Egress rules are validated
and retained in the configuration, but require a separate egress hook and are
not enforced by the current XDP program.
Rate-limit rules use a per-source-IP token bucket scoped to the rule ID. rate
is a positive packet count per second and burst is the bucket capacity:
[[rules]]
id = 500
action = "rate_limit"
direction = "ingress"
protocol = "tcp"
flags = ["syn"]
rate = "100/s"
burst = 200
[[rules]]
id = 501
action = "rate_limit"
direction = "ingress"
protocol = "udp"
rate = "500/s"
burst = 1000
[[rules]]
id = 502
action = "rate_limit"
direction = "ingress"
protocol = "icmp"
rate = "50/s"
burst = 100Matching packets are allowed while tokens remain and dropped when the source's bucket is empty. IPv4 and IPv6 sources use separate buckets. Rate rules currently apply only to ingress; global, interface-wide, and per-flow limiters are not implemented.
- Build and validate the Go CLI
From the repository root:
go mod tidy
go build -o kelvosd ./cmd/kelvosd
./kelvosd validate --config config/kelvos_config.toml- Load the BPF program
Run the loader (example):
sudo ./kelvosd run --config config/kelvos_config.toml --object build/xdp_prog.oThe Cobra CLI also supports direct overrides:
sudo ./kelvosd run --interface eth0 --object build/xdp_prog.o --log /tmp/traffic.jsonl
./kelvosd --help
./kelvosd run --helpFor a live terminal dashboard with total packets, traffic volume, packet/byte rates, and protocol breakdown:
sudo ./kelvosd top --interface eth0 --object build/xdp_prog.o --log /tmp/traffic.jsonlThe dashboard refreshes every second and keeps writing the same JSONL event stream. Press Ctrl+C to stop.
The run command opens the perf event map, attaches xdp_monitor to the selected interface, and appends JSONL events to the configured log file. Use Ctrl+C to detach cleanly.
Development notes
- BPF program sources are located under
ebpf/, split by packet parsing, policy, flow tracking, and XDP entry responsibilities. The CMake target compiles the entry point as one translation unit so BPF maps and helper references remain linked together. - Keep BPF C code minimal and avoid heavy C standard library usage.
- Use
clangwith-O2 -target bpfor the project CMake rules to compile the program into an object file. - When iterating on BPF code, use
bpftoolandipto inspect and manage loaded programs and maps.
Testing and debugging
- Use
dmesgto check kernel logs when loading fails. - Use
bpftool progandbpftool mapto list programs and maps. - Consider enabling verbose logging in the loader to troubleshoot attach failures.
License
Check the repository Packet.json and project headers for licensing information.
Contributing
Feel free to open issues or submit pull requests with bug fixes or enhancements. Provide a minimal reproduction and test steps.
Contact
For questions, add an issue in the repository.