// IFTOPRS — REAL-TIME PACKET INTERCEPT

iftoprs v2.22.8 · ratatui 0.30 + crossterm 0.29 + pcap 2.4 · 31 cyberpunk themes · per-flow process attribution · JSON streaming · mouse + sparklines · auto-restart capture

21,812 production Rust src · 6,112 integration-test src · 2,256 test functions

Report GitHub Issues
// Color scheme

>_IFTOPRS — JACK INTO THE PACKET STREAM

A neon-drenched terminal UI for real-time bandwidth monitoring. Live libpcap capture with BPF filters, per-flow sliding-window averages (2 s / 10 s / 40 s), reverse-DNS + port-name resolution, per-flow process attribution via lsof, hover + right-click tooltips, mouse + keyboard navigation, 31 cyberpunk color themes, --json NDJSON stream for headless pipelines, configurable bandwidth alerts, sparkline history per flow, and auto-restart on transient capture errors. macOS & Linux. Built in Rust with ratatui + crossterm + pcap.

Quickstart

Install from crates.io or build from source. Raw packet capture needs root or cap_net_raw:

# install from crates.io
cargo install iftoprs

# from source
git clone https://github.com/MenkeTechnologies/iftoprs
cd iftoprs && cargo build --release

# launch (needs root for raw capture)
sudo iftoprs                        # auto-detect default interface
sudo iftoprs -i en0                 # specific interface
sudo iftoprs -f "tcp port 443"      # BPF filter — HTTPS only
sudo iftoprs -F 10.0.0.0/8 -B       # filter private net, show bytes
sudo iftoprs -Z                     # show process names per flow

# headless / pipelines
sudo iftoprs --json                 # NDJSON stream to stdout
sudo iftoprs --json | jq '.flows[0]'

# shell completions
iftoprs --completions zsh > _iftoprs
iftoprs --completions bash > iftoprs.bash
iftoprs --completions fish > iftoprs.fish

Full install + dependency matrix lives in the README. On Linux apt install libpcap-dev before cargo build. On macOS libpcap ships with the system — no install needed.

Why iftoprs — against the classic tool

Featureiftoprsiftop (C, 2002)
Memory safetyRust — no UB, no leaksraw C
Async capture looptokio + mpscblocking pcap_loop
Per-flow process attributionPID + name via lsofnone
Mouse supportleft/right/middle, scroll, hover tooltipsnone
Tooltips on hover & right-click14 segment tooltips + flow drill-downnone
Per-flow sparkline (40 s)inline ▁▂▃▅▇█ + tooltipnone
Color themes31 — live chooser4 hardcoded colors
JSON / NDJSON output--json to stdoutnone
Bandwidth alertsconfigurable threshold + bell + flashnone
Auto-restart on transient errorsexponential backoffnone
Pinned / bookmarked flowsF — ★ floats to topnone
Live filter (hostname/IP)/ — substring matchnone
Clipboard exporty — selected flownone
NDJSON-pipeable exporte — full snapshot to filenone
Shell completionszsh / bash / fish / elvish / powershellnone
TOML config + auto-save~/.iftoprs.confreadline-style settings
Single static binarycargo install — one binaryautotools build

Capture engine

The capture pipeline is built around three concerns — read packets, parse to flow tuples, survive transient errors:

  • libpcappcap::Capture opens the device with the configured BPF filter, snaplen, and promiscuous flag. The capture thread lives in src/capture/sniffer.rs.
  • async hand-off — raw packets flow over a tokio::sync::mpsc channel to the parser task; the UI thread reads parsed flow updates from a second channel. Lossy capture (kernel drop) is logged but never panics.
  • parsersrc/capture/parser.rs walks Ethernet/IPv4/IPv6/TCP/UDP/ICMP headers, tracks 5-tuples, classifies protocol, and folds bytes into the per-flow counters.
  • auto-restart — transient pcap errors trigger an exponential-backoff reopen instead of crash. v2.22.2 release notes (76f42105bd) wired this in.
  • sliding windows — bandwidth is tracked over 2 s / 10 s / 40 s averages with a per-flow ring buffer in src/data/history.rs. Cumulative + peak counters live alongside.

Flow tracker

The flow model lives in src/data/flow.rs and the tracker in src/data/tracker.rs. Every flow is keyed by (src, dst, src_port, dst_port, protocol); counters split into tx and rx directions based on the configured interface IP / network filter.

  • 5-tuple keys — protocols cover TCP / UDP / ICMP / Other (ARP, raw IP, etc.).
  • three windows — 2 s yellow, 10 s green, 40 s cyan; sort cycles through them with 1/2/3.
  • peak + cumulative — toggle the cumulative row with U.
  • process attribution — an opt-out background poller in src/util/procinfo.rs shells out to lsof and maps socket → PID + process name. Joined into the flow on render.
  • DNS resolution — async reverse-DNS cache in src/util/resolver.rs; failed lookups never block the UI.
  • port names/etc/services-style mapping from the same resolver.

CLI flags

Defined in src/config/cli.rs via clap derive macros. Generate shell completions with --completions SHELL.

// CAPTURE

FlagDescription
-i, --interface NAMENetwork interface to monitor (auto-detect default gateway interface if absent).
-f, --filter EXPRBPF filter expression, e.g. "tcp port 80", "host 10.0.0.1".
-F, --net-filter CIDRIPv4 network filter, e.g. 192.168.1.0/24. Auto-detected from interface if omitted.
-p, --promiscuousEnable promiscuous mode — capture all traffic on the segment.

// DISPLAY

FlagDescription
-n, --no-dnsDisable DNS hostname resolution — raw IPs only.
-N, --no-port-namesDisable port-to-service resolution — numeric ports only.
-b, --no-barsDisable bar graph display.
-B, --bytesDisplay bandwidth in bytes instead of bits.
-P, --hide-portsHide ports alongside hosts.
-Z, --no-processesHide the process column (shown by default).

// OUTPUT

FlagDescription
--jsonStream NDJSON snapshots to stdout instead of running the TUI. Pipe to jq, append to log, feed into a dashboard.

// SYSTEM

FlagDescription
-l, --list-interfacesList available capture interfaces and exit.
--list-colorsPreview all 31 color themes with swatches and exit.
--completions SHELLGenerate shell completions: zsh / bash / fish / elvish / powershell.
-h, --helpDisplay help and exit.
-V, --versionDisplay version and exit.

// EXAMPLES

sudo iftoprs -i en0 # monitor specific interface
sudo iftoprs -f "tcp port 443" # HTTPS only
sudo iftoprs -F 10.0.0.0/8 -B # private net, bytes mode
sudo iftoprs -n -N -b # raw IPs, no bars, minimal
sudo iftoprs -Z # show process column
sudo iftoprs -p # promiscuous mode
iftoprs --completions zsh > _iftoprs # dump zsh completions
sudo iftoprs --json # NDJSON stream
sudo iftoprs --json | jq '.flows[0]' # pipe through jq

Keybind matrix

Defined in src/ui/app.rs. Every toggle that changes display state writes through to ~/.iftoprs.conf immediately — preferences survive across runs without explicit save.

// DISPLAY MODS

KeyAction
TabSwitch view — Flows / Processes.
nToggle DNS resolution.
NToggle service-name resolution.
tCycle line display — two-line / one-line / sent-only / recv-only.
pToggle port display.
ZToggle process display.
bCycle bar style — gradient / solid / thin / ascii.
BToggle bytes / bits.
TToggle hover tooltips (right-click still works).
UToggle cumulative totals row.
PPause / resume display — shows overlay.
xToggle border chrome.
gToggle column header.
fCycle refresh rate — 1 s / 2 s / 5 s / 10 s.

// SORT

KeyAction
1Sort by 2 s average.
2Sort by 10 s average.
3Sort by 40 s average.
<Sort by source name.
>Sort by destination name.
oFreeze current sort order.
rReverse sort order.

// NAVIGATION

KeyAction
j Select next flow.
k Select previous flow.
Ctrl+DHalf-page down.
Ctrl+UHalf-page up.
G EndJump to last.
HomeJump to first.
EscDeselect / clear process filter / close overlay.
EnterDrill into selected process (Processes tab).

// FILTER

KeyAction
/Enter filter mode — live substring match on hostname / IP.
0Clear filter.
EnterConfirm filter.
EscCancel filter.
Ctrl+WDelete word in filter prompt.
Ctrl+KKill to end of line in filter prompt.

// CHOOSERS

KeyAction
cOpen color-theme chooser — live preview, j/k navigates, Enter confirms.
iOpen interface chooser — saves to config; restart to apply.

// ACTIONS

KeyAction
yCopy selected flow to clipboard.
FPin / unpin selected flow — ★ floats to top.
eExport all flows to ~/.iftoprs.export.txt.

// MOUSE

InputAction
Left clickSelect flow row.
Right click (flow)Tooltip — TX / RX rates, totals, process, sparkline.
Right click (header)Instant segment tooltip — persistent until dismissed.
Middle clickPin / unpin flow.
Mouse moveDismiss flow tooltip.
Scroll up / downNavigate flows; cycles themes in chooser.
Hover header barSegment tooltip after 1 s delay; auto-hides after 3 s.

// GENERAL

KeyAction
h ?Toggle help HUD.
qDisconnect — saves preferences.
Ctrl+CForce disconnect.

Color themes — 31 built-in

Defined in src/config/theme.rs. Press c in the TUI for the live chooser with side-by-side swatch preview. Selection is persisted to ~/.iftoprs.conf. Outside the TUI: iftoprs --list-colors prints every theme with swatches.

NeonSprawl — default
AcidRain
IceBreaker
SynthWave
RustBelt
GhostWire
RedSector
SakuraDen
DataStream
SolarFlare
NeonNoir
ChromeHeart
BladeRunner
VoidWalker
ToxicWaste
CyberFrost
PlasmaCore
SteelNerve
DarkSignal
GlitchPop
HoloShift
NightCity
DeepNet
LaserGrid
QuantumFlux
BioHazard
Darkwave
Overlock
Megacorp
Zaibatsu
Iftopcolor — classic

Configuration — ~/.iftoprs.conf

TOML file at ~/.iftoprs.conf. Auto-created on first run; every TUI toggle writes through immediately. The repo ships iftoprs.default.conf as a documented reference.

# Color theme (see --list-colors for all 31 options)
theme = "NeonSprawl"

# Network interface to capture on (overridden by -i flag)
# interface = "en0"

# DNS and port resolution
dns_resolution = true
port_resolution = true

# Display toggles
show_ports = true
show_bars = true
show_border = true
show_header = true
show_processes = true
show_cumulative = false
use_bytes = false

# Bar style: Gradient, Solid, Thin, Ascii
bar_style = "Gradient"

# Refresh rate in seconds (1, 2, 5, or 10)
refresh_rate = 1

# Alert threshold in bytes/sec (0.0 = disabled)
# 125000.0    = 1 Mbit/s
# 1250000.0   = 10 Mbit/s
# 125000000.0 = 1 Gbit/s
alert_threshold = 0.0

# Pinned flows — persisted automatically when you press F
pinned = []

Serialization lives in src/config/prefs.rs. The TOML schema is stable; missing keys fall back to defaults so old configs keep working across upgrades.

JSON streaming — --json

Runs the capture engine headless and writes one JSON snapshot per refresh interval to stdout. Pipe to jq, log to a file, ship to a dashboard. Each line is a complete {"flows": [...]} object — no TUI is started, no terminal control bytes are emitted.

sudo iftoprs --json | jq -c '.flows[] | select(.rate_2s > 1000000)'
sudo iftoprs --json > /var/log/iftoprs.ndjson &
sudo iftoprs --json -f "tcp port 443" | head -n 1 | jq .

Each flow record carries the 5-tuple (source, destination, ports, protocol), rate_2s / rate_10s / rate_40s in bytes/sec, cumulative TX / RX totals, resolved hostnames, port names, and (if available) the attributed PID + process name.

Process attribution — Tab view

Shown by default; toggle off with -Z at launch or in the TUI. Background poller in src/util/procinfo.rs calls lsof -i -n -P -F pcn, parses the result, and builds a (local_addr, local_port) → (pid, name) map cached in Arc<Mutex<...>>. The flow renderer joins against the cache on every paint.

  • Flows tab (default) — per-flow process column.
  • Processes tab — press Tab — aggregates bandwidth per process across all of that process's flows.
  • Drill-down — press Enter on a process row to filter the Flows tab to that process. Esc clears the filter.
  • Cost — the poller runs at the refresh-rate interval, not per-packet; even on busy hosts the overhead stays measurable but small.

Tooltips — 14 hover & right-click segments

Every segment of the header bar carries a rich contextual tooltip. Hover triggers after 1 s with a 3 s auto-hide; right-click triggers instantly and persists until dismissed. Disable hover (right-click still works) with T.

App info

Version, build date, repository link, license.

Interface

Selected interface, MAC, IP, MTU, status.

Flow count

Active flows, pinned count, filter state.

Clock

Local time, capture start, elapsed seconds.

Sort mode

Active sort key, direction, freeze status.

Refresh rate

Current interval, cycle hint, frame count.

Theme

Active theme name, swatch row, cycle hint.

Filter

Active substring filter, hit count, clear hint.

Pause state

Paused / running, last frame age, resume hint.

BPF filter

Compiled BPF expression, packet match count.

Net filter

CIDR mask in effect, auto / manual source.

Bandwidth scale

Bar log10 range, threshold, alert state.

View mode

Flows / Processes tab, drill-down chain.

Help

Top keybinds, full help shortcut h / ?.

Flow rows themselves carry a right-click tooltip: TX / RX rates across the three windows, cumulative totals, attributed process, and a 40 s sparkline of recent bandwidth (▁▂▃▅▇█).

Sparkline history

Per-flow rolling ring buffer of 40 samples in src/data/history.rs. Rendered two ways:

  • Inline below the selected row when sparkline display is enabled (40 s window, ▁▂▃▅▇█ block characters).
  • Inside the right-click tooltip for any flow — not only the selected one.

Samples are folded at the refresh-rate cadence, so the visible window is 40 × refresh_rate seconds. At default 1 s refresh that's a 40 s window; at 10 s refresh it stretches to 400 s.

Bandwidth alerts

Set alert_threshold in ~/.iftoprs.conf (bytes / sec, 0.0 disables). When any flow's instantaneous rate crosses the threshold:

  • Border flashes red for a beat.
  • Terminal bell (\x07) fires.
  • Status bar shows ⚠ ALERT: hostname rate/s.
# in ~/.iftoprs.conf
alert_threshold = 1250000.0     # 10 Mbit/s
alert_threshold = 125000000.0   # 1 Gbit/s

Tests & CI

2,256 test functions across the production crate and integration suite. CI runs on every push and PR to main via GitHub Actions.

JobCommandNotes
Formatcargo --locked fmt --all --checkNo pcap link — no libpcap-dev needed.
Clippycargo clippy --all-targets --locked -- -D warningsLinux installs libpcap-dev via apt.
Testcargo build --locked && cargo test --lockedUbuntu + macOS matrix, fail-fast: false.

Integration tests in tests/integration.rs launch the built binary via the CARGO_BIN_EXE_iftoprs environment variable — not cargo run — so CLI output is read directly from the process and stays reliable in CI. The toolchain is pinned through rust-toolchain.toml with stable + rustfmt + clippy so local and CI runs share the same compiler.

Local checks — identical to CI:

cargo --locked fmt --all --check
cargo clippy --all-targets --locked -- -D warnings
cargo test --locked

Crate layout

Single binary, eight modules, ~21k lines of production Rust:

src/main.rs

Entry point: CLI parse, capture spawn, TUI bootstrap, signal handling. 1,072 lines.

src/capture/

sniffer.rs — pcap loop + restart. parser.rs — Ethernet/IP/TCP/UDP/ICMP decode. ~3.1k lines.

src/data/

flow.rs 5-tuple model + counters. tracker.rs — flow store. history.rs sparkline ring. ~3.5k lines.

src/ui/

app.rs — TUI state + input. render.rs — ratatui paint. ~6.3k lines.

src/config/

cli.rs — clap derive. theme.rs — 31 themes. prefs.rs — TOML auto-save. ~4.5k lines.

src/util/

format.rs — rate/byte formatters. resolver.rs — async DNS + port names. procinfo.rs — lsof poll. ~2.8k lines.

tests/integration.rs

479 binary-driven integration tests. 5,258 lines.

completions/

Shipped zsh completion file (_iftoprs). Other shells generated on demand.

Full per-file breakdown is in the engineering report.

Dependencies — 14 direct

CrateVersionRole
ratatui0.30TUI rendering framework.
crossterm0.29Terminal events + manipulation, mouse capture.
pcap2.4Packet capture via libpcap (system).
tokio1.51Async runtime, mpsc channels.
clap4.6CLI argument parsing (derive).
clap_complete4Shell completion generation.
dns-lookup3.0Reverse DNS resolution.
regex1.12Pattern matching for filters.
chrono0.4Time operations, clock segment.
anyhow1.0Error handling.
serde1.0Config + JSON serialization.
serde_json1.0NDJSON streaming output.
toml1.1Config file format.
dirs6.0Home-directory detection for config / export paths.

Platform support

  • macOSlibpcap ships with the system; sudo iftoprs works out of the box.
  • Linux — install libpcap-dev (Debian / Ubuntu) or libpcap-devel (Fedora / RHEL). Run as root or grant cap_net_raw,cap_net_admin:
    sudo setcap cap_net_raw,cap_net_admin=eip $(which iftoprs)
  • Rust toolchain — pinned to stable, Rust 1.85+ (edition 2024).
  • Architectures — x86_64 + aarch64 verified on both macOS and Linux.

Repository & links