Skip to content

About

live XDP packet-capture analyzer — a mini-Wireshark

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

4 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

NetScope

A live XDP packet-capture analyzer — a mini-Wireshark — that is Rust end to end: an XDP program (Rust → eBPF bytecode) copies packet bytes out of the kernel, a Rust agent streams them over WebSocket, and Rust compiled to WebAssembly dissects every packet and evaluates a tcpdump-style display filter in the browser. SvelteKit renders the classic three-pane capture UI.

NetScope — packet list, protocol dissection tree, and hex view

┌────────────────────── Linux (Docker Desktop VM) ────────────────────┐
│  kernel                                   userspace                 │
│  ┌─────────────────────────┐  ring buffer ┌───────────────────────┐ │
│  │ netscope-ebpf (Rust →   │ ───────────► │ netscope-agent (Rust) │ │
│  │ BPF via aya)            │  hdr+snaplen │ aya loader + axum WS  │ │
│  │ · XDP on eth0 (SKB mode)│              │ · one frame / packet  │ │
│  │ · snaplen 256 bytes     │              └───────────┬───────────┘ │
│  │ · skips its own WS port │                          │ ws://:8080  │
│  └─────────────────────────┘                          │             │
└───────────────────────────────────────────────────────┼─────────────┘
                                                        │ hdr + bytes
┌────────────────────────── Browser ────────────────────▼─────────────┐
│  netscope-wasm (Rust → WASM)               SvelteKit                │
│  · dissector: Eth/IPv4/IPv6/ARP/TCP/  ──►  packet list (filtered)   │
│    UDP/ICMP → labeled tree + facts         dissection tree + hex    │
│  · tcpdump-style filter parser/eval        live protocol counters   │
└─────────────────────────────────────────────────────────────────────┘

The netscope-common::CaptureHeader layout is shared byte-for-byte across all three runtimes (bpfel-unknown-none → linux → wasm32).

Layout

  • agent/netscope-ebpf — XDP capture program (aya-ebpf, nightly, bpfel-unknown-none)
  • agent/netscope-common — shared #[repr(C)] capture header (no_std)
  • agent/netscope-agent — loader + WebSocket server (aya, axum, tokio)
  • wasm/ — browser-side dissector + filter engine (wasm-bindgen)
  • web/ — SvelteKit capture UI

Run

XDP needs a Linux kernel; on macOS the agent runs in Docker Desktop's VM (with scoped capabilities — BPF, NET_ADMIN, PERFMON, SYS_RESOURCE — not full privileged) and captures on the container's own eth0.

One command — the agent serves the dashboard itself:

cd web && pnpm install && pnpm build      # build the SPA once
cd .. && docker compose up -d --build     # agent serves it + captures
docker compose --profile tools up -d traffic   # optional traffic generator
# → http://localhost:8082

Dev mode (hot reload) is still available separately:

cd web && pnpm dev
# → http://localhost:5173   (talks to the agent WS on ws://localhost:8082/ws)

Conversations & pcap

  • conversations groups the buffer by 5-tuple (both directions collapse into one row); click a row to filter to it.
  • save .pcap downloads the buffer as a classic pcap you can open in Wireshark; load .pcap replays a saved or real capture offline through the same dissector — so you can develop and demo without a live kernel.

You can also drive traffic yourself into the agent's namespace:

docker exec netscope-agent sh -c 'wget -q -O- http://example.com >/dev/null'

Display filter

A small tcpdump/BPF-flavored filter, parsed and evaluated in Rust/WASM:

tcp                       udp                      icmp / arp / ip / ip6
port 443                  src port 53              dst port 80
host 1.1.1.1              src host 192.168.0.2     dst host 8.8.8.8
tcp and port 443          arp or icmp              not (tcp or udp)

Terms combine with and, or, not, parentheses, and adjacency (implicit and). An invalid filter is reported inline and treated as match-all. Pause to inspect without the list moving; the WASM buffer keeps the last 5000 packets regardless of filter.

Notes & caveats

  • XDP is ingress-only. You see packets arriving on the interface (responses to outbound requests, inbound connections), not egress. A bidirectional capture would use a tc/clsact classifier instead; XDP is what the "X" in the name refers to.
  • Snaplen is 256 bytes — enough for L2–L4 headers plus a little payload. The hex pane notes when the original frame was longer.
  • The capture program skips TCP to/from its own WebSocket port (8080) so the tool doesn't capture — and amplify — its own transport on the shared interface.
  • The eBPF copy uses bpf_xdp_load_bytes with an opaque (black_box) length guard; a hand-rolled indexed copy trips the verifier's packet-range tracking, and without the barrier LLVM folds the required non-zero length check into a pointer comparison the verifier can't use. See the comments in agent/netscope-ebpf/src/main.rs.

Development

cd wasm && cargo test    # dissector + filter + buffer, all native
wasm-pack build wasm --target web --release --out-dir ../web/src/lib/netscope-wasm --no-pack
cd web && pnpm run check

Wire protocol

Each binary WS frame is one packet: a 16-byte little-endian CaptureHeader (ts: u64 kernel-ns, orig_len: u32, cap_len: u32) immediately followed by cap_len raw packet bytes. On connect the agent sends one JSON text frame {type, iface, snaplen}.

Slides

A self-contained overview deck lives in docs/index.html — open it in any browser (arrow keys / space to navigate).

About

live XDP packet-capture analyzer — a mini-Wireshark

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages