Skip to content

Latest commit

Β 

History

12 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

rust-wire

A Rust network sniffer with an htop-style TUI. Captures live traffic from a network interface and writes a Wireshark-compatible .pcap. image

Quick start

git clone https://github.com/jaymeklein/rust-wire && cd rust-wire

sudo apt install -y libpcap-dev                              # 1. build dependency
cargo install --path .                                       # 2. build + install to ~/.cargo/bin
sudo setcap cap_net_raw,cap_net_admin=eip ~/.cargo/bin/rust-wire   # 3. capture without root

rust-wire -L                    # list interfaces
rust-wire -i eth0               # capture to capture.pcap, press q to stop

That's it. Details on each step below.

1. Install libpcap

Needed at build time to link against libpcap.so.

Platform Command
Debian / Ubuntu sudo apt install -y libpcap-dev
Fedora sudo dnf install -y libpcap-devel
Atomic Fedora (Bazzite, Silverblue, Bluefin, Kinoite) see below
macOS brew install libpcap

Atomic Fedora

/usr is read-only on the atomic images, so dnf isn't the install path. Two options:

Layer it on the host β€” keeps steps 2 and 3 exactly as written, since ~/.cargo/bin lives under the writable /var/home:

sudo rpm-ostree install libpcap-devel
systemctl reboot   # layering only takes effect on the next deployment

Build in a Distrobox β€” the distro's own recommendation for development packages, as layered packages slow down (and can block) system upgrades:

distrobox create --name dev --image fedora:latest
distrobox enter dev
sudo dnf install -y libpcap-devel
cargo install --path .

The binary lands in your shared ~/.cargo/bin but links against the container's libpcap.so.1. Confirm the host has the runtime library before running it outside the container:

ldconfig -p | grep libpcap

If it's missing, run rust-wire from inside the distrobox β€” containers share the host network namespace, so the interfaces are all visible β€” or layer the package instead.

2. Build

cargo install --path . puts rust-wire on your PATH (via ~/.cargo/bin, already there if you used rustup) so you can run it from any directory. Verify with which rust-wire.

Prefer to work out of the repo instead? Use cargo build --release and invoke it by path as ./target/release/rust-wire.

Either way the binary is a copy β€” after changing the source, re-run the same command to update it, then redo step 3.

3. Capture permissions

Opening a live interface needs elevated privileges. setcap grants just that capability to the binary, so you never need sudo:

sudo setcap cap_net_raw,cap_net_admin=eip ~/.cargo/bin/rust-wire   # installed
sudo setcap cap_net_raw,cap_net_admin=eip target/release/rust-wire # or built in-repo

Rebuilding replaces the file and drops the capability β€” re-run setcap after each build.

Skipping this step is fine; just prefix runs with sudo. See Troubleshooting if that gives you command not found.

Usage

rust-wire [OPTIONS]

  -i, --interface <NAME>       Interface to capture on (default: first non-loopback up)
  -L, --list-interfaces        List interfaces and exit
  -f, --filter <BPF>           BPF filter, e.g. "tcp port 80"
  -c, --count <N>              Stop after N packets
      --duration <DUR>         Stop after duration (30s, 5m, 1h)
      --max-file-size <SIZE>   Stop when the pcap reaches this size (100k, 500M, 2G)
  -s, --snaplen <BYTES>        Bytes per packet to capture [default: 65535]
      --promisc                Enable promiscuous mode
      --no-tui                 Disable TUI; use tcpdump-style stdout
      --quiet                  No display; write pcap only
  -o, --output <FILE>          Output pcap [default: capture.pcap]
      --max-talkers <N>        Cap on unique src→dst pairs tracked for top talkers
                               (bounds RAM on long/high-cardinality runs) [default: 1024]

Examples

# List interfaces
rust-wire -L

# Interactive TUI capture, everything on eth0, to capture.pcap
rust-wire -i eth0

# 5 seconds of ICMP on loopback, then exit
rust-wire -i lo -f "icmp" --duration 5s -o icmp.pcap

# Silent capture of 100 HTTP packets
rust-wire -i eth0 -f "tcp port 80" -c 100 --quiet -o http.pcap

# Stop after 100 MB of pcap OR 10 minutes, whichever first β€” safe for small disks
rust-wire -i eth0 --max-file-size 100M --duration 10m -o rolling.pcap

# High-cardinality trace (public webserver) with tight memory budget
rust-wire -i eth0 --max-talkers 256 --max-file-size 500M -o web.pcap

# Non-TTY: falls back to tcpdump-style, one line per packet
rust-wire -i lo | head

These assume you ran setcap; otherwise prefix each with sudo.

Interactive UI

The TUI is selected automatically when stdout is a terminal (unless --no-tui or --quiet).

Key Action
q / Esc quit and flush the pcap
r start / stop recording to the pcap (see below)
f / Ctrl-F edit the capture filter (see below)
↑ ↓ / k j move the cursor through the packet list
PgUp / PgDn move a page at a time
g / G jump to the newest / oldest buffered packet
d / Enter show or hide the packet detail pane
p freeze the packet list (capture and .pcap writing continue)

The screen shows a Wireshark-style packet table β€” number, time, source, destination, protocol, size, and a decoded Info summary β€” alongside live totals and pps/bps, a protocol mix bar chart, top talkers, and a throughput sparkline. Selecting a row opens its Ethernet / IP / transport fields in the detail pane at the bottom. Scrolling up pins the cursor to that packet as new ones arrive; g resumes following the live tail.

The last 2000 packets stay scrollable. Panes drop out on narrow terminals: the sidebar below 104 columns, the detail pane below 24 rows.

Recording

r toggles whether packets reach the .pcap. Recording starts on, and a badge in the header always says which way it is: ● REC or β—‹ NOT RECORDING.

Stopping recording does not stop the capture β€” packets keep arriving, the table keeps filling and the stats keep counting; they simply aren't written. That's the difference between r and p: p freezes the display, r gates the file.

  • Stopping flushes the pcap, so what's on disk is complete and openable in Wireshark while you're still watching the live traffic.
  • Starting again appends to the same file, so one run can hold several recorded stretches with the quiet parts left out.
  • --max-file-size only counts recorded bytes, so a run that's mostly not recording won't trip it.
  • If you never record β€” say you turned it off and then changed the filter β€” the output file is not created or truncated at all, and the closing summary says so.

The final summary reports recorded alongside packets whenever the two differ.

Changing the filter mid-run

f (or Ctrl-F) opens a one-line BPF editor over the packet list, pre-filled with the filter currently in force. Ctrl-F again applies it β€” so the whole edit is one key, type, same key.

Key Action
Ctrl-F / Enter apply the filter and restart the capture
Esc close the editor, leaving the running capture alone
Tab insert the highlighted completion
↑ ↓ / Ctrl-P Ctrl-N move through the completion list
← β†’, Home / End, Ctrl-A / Ctrl-E move the caret
Ctrl-W / Ctrl-U delete the previous word / the whole line

Completion matches the word under the caret against the BPF vocabulary β€” tcp, udp, host, src port, ip proto, portrange, less, greater, and the rest β€” each with a one-line reminder of its syntax. Parentheses start a fresh word, so completion keeps working inside (...) groups.

The filter is compiled before it is accepted: a typo is reported inline (βœ— syntax error) and the editor stays open on your text. An empty expression means no filter β€” capture everything.

Applying restarts the capture from scratch: stats, the packet list, the throughput history, and the --count / --duration / --max-file-size budgets are all reset, and the output pcap is overwritten as soon as the new run records a packet. Use -o with a fresh path per run if you need to keep an earlier capture β€” or press r before applying, which leaves the file alone until you record again.

Recording state is the one thing that carries across a restart: if you stopped recording before applying, it stays stopped.

Stop conditions

Capture ends on whichever fires first: --count, --duration, --max-file-size, Ctrl-C, or q / Esc in the TUI.

Memory bounds

  • --max-talkers <N> caps the top-talkers table. When full, the lowest-bytes entry is evicted to make room for a new pair. Set to 0 to disable tracking entirely.
  • The kernel packet-ring buffer is 10 MB by default (hard-coded in src/capture.rs; edit buffer_size for tighter memory budgets on small devices).

Open the pcap in Wireshark

wireshark capture.pcap
tshark    -r capture.pcap    # or from the CLI
capinfos     capture.pcap

Troubleshooting

rust-wire: command not found β€” you built with cargo build --release instead of cargo install --path .. Either install it, or run it as ./target/release/rust-wire.

sudo rust-wire says command not found, but rust-wire works β€” most distros configure sudo with secure_path, which strips ~/.cargo/bin. Run setcap (step 3) so sudo isn't needed, or use the full path: sudo ~/.cargo/bin/rust-wire ....

Permission denied opening the interface β€” setcap wasn't applied, or a rebuild wiped it. Check with getcap ~/.cargo/bin/rust-wire and re-apply.

A shell alias won't help here β€” sudo doesn't expand aliases, and neither do scripts, xargs, or watch. Use cargo install --path ..

Out of scope for v1

  • pcapng output format
  • TLS keylog / decryption
  • Custom dissectors beyond L3/L4
  • Windows / Npcap install docs (should still build)

About

Cool network sniffer built with rust πŸ¦€

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages