Skip to content

Repository files navigation

flowscope

eBPF-powered network flow capture & analysis, built on aya.

flowscope attaches an XDP program to a network interface, accounts traffic per protocol and per flow entirely inside the kernel, and streams packet, DNS and raw-frame events to user space over ring buffers for live analysis, Prometheus metrics and pcap export. It is a pure-aya program: no libbpf, no C toolchain at run time.

Features

  • XDP capture — an XDP_PASS program reads every ingress frame on the chosen interface without copying or dropping traffic.
  • Per-protocol counters — TCP / UDP / ICMP / other packet and byte totals kept in a per-CPU array (PROTO_STATS).
  • 5-tuple flow table — per-flow packet/byte counts and SYN/FIN/RST tallies in a kernel hash map, with user-space idle eviction.
  • Packet event stream — compact per-packet metadata delivered over a ring buffer for live inspection and metrics.
  • IPv4 and IPv6 — a family-tagged flow key and 16-byte address fields cover both stacks through one code path.
  • DNS query visibility — UDP/53 queries and responses are parsed in the kernel and the raw QNAME labels are forwarded for user-space decoding.
  • 802.1Q VLAN — a single VLAN tag is transparently skipped before L3 parsing.
  • Top talkers — source hosts ranked by bytes, aggregated in user space.
  • Prometheus metrics — a built-in HTTP endpoint exposes counters in the standard exposition format.
  • TOML config — every CLI option is overridable from a config file (precedence: CLI > file > built-in default).
  • pcap export — optionally write sampled frames to a classic pcap file readable by Wireshark / tcpdump.
  • JSON or table output — periodic reports as a human table or machine JSON.
  • Graceful shutdownCtrl-C prints a final summary and exits cleanly.

Architecture

 ┌──────────────────────────── kernel (XDP) ───────────────────────────┐
ingress frame ──────▶│ parse eth [+ 802.1Q] → IPv4/IPv6 → TCP/UDP/ICMP │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ PROTO_STATS FLOWS EVENTS / DNS_EVENTS / CAPTURE │
│ (per-cpu) (hash 5-tuple) (ring buffers) ▲ │
│ CONFIG (Array) │
└──────────┬───────────┬───────────────┬───────────────────┬──────────┘
│ │ │ │ set at start
read each tick read each tick drained per wakeup │
│ │ │ │
┌──────────▼───────────▼───────────────▼───────────────────▼──────────┐
│ user space (tokio) │
│ flow table + idle eviction · analysis (top talkers) · dns decode │
│ metrics server (Prometheus) · pcap writer · output (table / JSON) │
└─────────────────────────────────────────────────────────────────────┘
  • Kernel side counts and indexes; it never allocates and never drops.
  • User side snapshots maps on a timer, evicts idle flows, drains the ring buffers, decodes DNS names, serves metrics and writes pcap.

See docs/architecture.md for the full design.

Requirements

  • Linux ≥ 5.8 — the BPF ring buffer (BPF_MAP_TYPE_RINGBUF) landed in 5.8.
  • PrivilegesCAP_BPF + CAP_NET_ADMIN (or simply root) to load the program and attach XDP.
  • No libbpf — loading is pure aya; there is no C toolchain dependency at run time.

Build

The user-space crate builds on stable Rust. The flowscope-ebpf crate is compiled to BPF bytecode and needs the nightly toolchain with the rust-src component plus bpf-linker:

rustup toolchain install nightly --component rust-src
cargo install bpf-linker

A normal release build then compiles the eBPF object automatically — the flowscope crate's build.rs invokes aya-build, which builds flowscope-ebpf for bpfel-unknown-none and embeds the result:

cargo build --release
# binary at target/release/flowscope

Usage

# basic: attach to eth0, print a table every 5s
sudo ./flowscope --iface eth0
# machine-readable periodic reports
sudo ./flowscope --iface eth0 --format json
# expose Prometheus metrics on :9184
sudo ./flowscope --iface eth0 --metrics-addr 0.0.0.0:9184
# load options from a config file (CLI still wins)
sudo ./flowscope --config /etc/flowscope.toml
# capture every 64th frame to a pcap while running
sudo ./flowscope --iface eth0 --pcap capture.pcap --sample 64
# tune reporting and eviction
sudo ./flowscope --iface wlan0 --interval 10 --top 20 --idle-timeout 120

Config file

All CLI options may be set in a TOML file; anything passed on the command line overrides the file, and the file overrides the built-in defaults:

# /etc/flowscope.tomliface = "eth0"interval = 5# seconds between reportstop = 10# number of top talkers to showidle_timeout = 60# seconds before an idle flow is evictedformat = "table"metrics_addr = "0.0.0.0:9184"pcap = "capture.pcap"sample = 64# write every Nth frame to the pcapdns = true# parse and report DNS queries

Metrics

When --metrics-addr is set, any GET returns a Prometheus exposition body:

SeriesTypeDescription
flowscope_packets_total{proto="…"}counterpackets seen per protocol
flowscope_bytes_total{proto="…"}counterbytes seen per protocol
flowscope_flowsgaugelive flows in the table
flowscope_packet_events_totalcounterpacket events streamed
flowscope_dns_queries_totalcounterDNS queries observed

Project layout

.
├── Cargo.toml # workspace manifest
├── flowscope/ # user-space loader + analysis (binary and library)
│ ├── build.rs # compiles + embeds the eBPF object via aya-build
│ └── src/
│ ├── main.rs # thin binary entry point
│ ├── lib.rs # re-exports the modules below for tests
│ ├── cli.rs # clap argument parsing
│ ├── config.rs # TOML config + settings resolution
│ ├── flow.rs # FlowTable: snapshot + idle eviction
│ ├── analysis.rs # top talkers / top flows
│ ├── dns.rs # DNS QNAME decoding
│ ├── metrics.rs # Prometheus HTTP endpoint
│ ├── pcap.rs # classic pcap writer
│ └── output.rs # table / JSON rendering
├── flowscope-common/ # shared no_std types (maps keys, ring events)
├── flowscope-ebpf/ # the XDP program (compiled to BPF bytecode)
└── docs/architecture.md # design notes

Limitations & notes

  • IPv4 options are not parsed; a 20-byte IPv4 header is assumed.
  • DNS QNAMEs are forwarded raw and truncated to DNS_NAME_CAP (192) bytes.
  • pcap capture is bounded to CAP_SNAPLEN (256) bytes per frame; longer frames are recorded with their true orig_len but a truncated payload.
  • Only a single 802.1Q tag is skipped; stacked (QinQ) tags fall through to the other bucket.

License

Dual-licensed under either of Apache-2.0 or MIT at your option.

About

An ebpf filter for data analysis

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages