RustNet Roadmap

August 14, 2026 · View on GitHub

This document outlines the planned features and improvements for RustNet.

Platform Support

  • macOS Support: Full support including:
    • BPF device access and permissions setup
    • PKTAP (Packet Tap) headers for process identification from packet metadata
    • Fallback to lsof system commands for process-socket associations
    • DMG installation packages for Apple Silicon and Intel
    • Homebrew installation support
  • Windows Support: Full functionality working with:
    • Npcap SDK and runtime integration
    • MSI installation packages for 64-bit and 32-bit
    • Process identification via Windows IP Helper API (GetExtendedTcpTable/GetExtendedUdpTable)
  • FreeBSD Support: Full support including:
    • Process identification via sockstat command parsing
    • BPF device access and permissions setup
    • Native libpcap packet capture
    • Cross-compilation support from Linux
  • FreeBSD Capsicum Full Sandbox (cap_enter()): Replace per-FD cap_rights_limit() with full capability mode to prevent file access and data exfiltration. Requires:
    • Switch from sockstat subprocess to libprocstat(3) library calls for process lookup (eliminates fork()/execve() dependency)
    • Integrate libcasper for privileged sysctl access from inside capability mode (kern.proc.filedesc is blocked in cap_enter())
    • Architecture: pre-fork a Casper service before cap_enter(), communicate over socket pair at runtime
    • Write FFI bindings for libprocstat and libcasper (no Rust crate exists)
    • Link against -lprocstat -lcasper -lcap_sysctl (system libraries on FreeBSD 10+)
  • Windows Sandbox Hardening: Strengthen the current privilege-drop + Job Object setup with process mitigation policies (SetProcessMitigationPolicy), low-integrity execution, and evaluation of CreateRestrictedToken / AppContainer.
  • macOS Seatbelt Hardening: The current Seatbelt profile is allow-default with targeted denies (user homes, system credential stores, outbound TCP/UDP, process-exec except lsof). Tighten further:
    • Deny-by-default writes: rustnet only writes its log/PCAP/JSONL output, so flip file-write* to deny-by-default with a small allowlist. This blocks root-level persistence (/Library/LaunchDaemons, /Library/LaunchAgents, /private/etc cron/launchd, etc.) that the current allow-default write policy leaves open. Needs on-host validation that the TUI's writes to the already-open tty and the lsof child still work.
    • More credential read denies: system TCC database (/Library/Application Support/com.apple.TCC), Kerberos keytabs, master.passwd/sudoers, saved network/Wi-Fi configuration (/Library/Preferences/SystemConfiguration).
    • Eventual deny-by-default reads: whitelist the dyld shared cache, system frameworks, /dev/bpf*, resolver/locale/timezone data, and the GeoIP paths. Strongest containment, but fragile across macOS releases — requires a multi-version on-host test pass before shipping.
  • Linux Sandbox Hardening (Landlock UDP and explicit root-retention modes):
    • Default privilege drop: A normal root launch resolves the invoking user and irreversibly drops UID/GID after privileged initialization, clearing the effective and permitted capability sets before worker threads start.
    • Harden explicit root-retention modes: When --no-uid-drop or --no-sandbox keeps the process as root, clear the capability bounding set and drop every capability not required for continued capture or eBPF map access.
    • Block UDP egress when Landlock supports it: Landlock ABI v4 only governs TCP bind/connect, so UDP egress remains possible. Revisit this when a newer Landlock ABI adds UDP support. macOS Seatbelt already blocks both TCP and UDP.
  • OpenBSD and NetBSD Support: Future platforms to support
  • Linux Process Identification: Experimental eBPF Support Implemented - Basic eBPF-based process identification now available with --features ebpf. Provides efficient kernel-level process-to-connection mapping with lower overhead than procfs. Currently has limitations (see eBPF Improvements section below).

eBPF Improvements (Linux)

The experimental eBPF support provides efficient process identification but has several areas for improvement:

Current Limitations

  • Name recovery is best effort: Names originate from the kernel comm field (15 bytes). RustNet re-resolves the group leader's current name via /proc/<tgid>/comm and recovers comm-truncated names from the executable's file name, but a process that exits before enrichment runs keeps the short eBPF-recorded name.

Planned Improvements

  • Hybrid eBPF + Procfs Approach: eBPF handles connection tracking; the process name, executable path, parent PID, and credentials are resolved in user space via procfs right after a successful attribution.
  • Full Executable Path Resolution: /proc/<tgid>/exe is resolved in user space immediately after attribution; kernel-side resolution turned out to be unnecessary.
  • Better Process-Thread Mapping: eBPF records the group leader's TGID, the acting TID, and the leader's comm separately; the display name is re-resolved from /proc/<tgid>/comm, and comm truncation is recovered from the executable's file name.
  • Enhanced BTF Support: Covered by the fentry/fexit backend selection below, which uses actual BTF, load, and attach results per kernel.
  • Profile-Guided Performance Optimizations: Measure attribution cache hit rates and exact/fallback eBPF map reads under representative traffic, then reduce repeated lookups where profiling shows them to be hot. The existing unified attribution cache is the baseline.
  • Prefer fentry/fexit with legacy kprobe fallback: RustNet now tries a BPF trampoline backend first, then legacy kprobes, then procfs. Selection uses actual BTF, load, and attach results. The modern backend avoids perf_event_open(2), while the legacy backend preserves older-kernel compatibility when usable target BTF is available. Kernels without target BTF fall safely to procfs because fixed socket-structure offsets could misattribute traffic. Optional ICMP attachment failures no longer disable TCP and UDP attribution. kprobe.multi remains a possible future compatibility option if kernel-matrix testing shows a concrete need.

Future Enhancements

  • Real-time Process Updates: Refresh process names, executable paths, and credentials after initial attribution when a process changes.
  • Kubernetes Container Attribution: Resolve pod UID, name, namespace, container ID/name, and cgroup metadata, with filters, exports, and scoped per-PID network namespace lookups for kubepods processes.
  • Generic Container Attribution: Identify Docker, Podman, and LXC workloads and expose runtime/container metadata outside Kubernetes.
  • Process Credentials: Include effective UID and GID in attribution, details, and exports.
  • Process Security Context: Include Linux capabilities and SELinux/AppArmor context.
  • Ephemeral Cross-Namespace Attribution for Kubernetes: The current per-PID TCP/UDP table scan runs at the enrichment interval and can miss short-lived flows. Retain eBPF socket events long enough for userspace to consume them, or drain close events through a ring buffer, then validate end-to-end coverage in a kind cluster.

Features

Monitoring & Protocol Support

  • Real-time Network Monitoring: Monitor active TCP, UDP, ICMP, and ARP connections
  • Connection States: Comprehensive state tracking for:
    • TCP states (ESTABLISHED, SYN_SENT, TIME_WAIT, CLOSED, etc.)
    • QUIC states (QUIC_INITIAL, QUIC_HANDSHAKE, QUIC_CONNECTED, QUIC_DRAINING)
    • DNS states (DNS_QUERY, DNS_RESPONSE)
    • SSH states (BANNER, KEYEXCHANGE, AUTHENTICATION, ESTABLISHED)
    • Activity states (UDP_ACTIVE, UDP_IDLE, UDP_STALE)
  • Deep Packet Inspection (DPI): Application protocol detection:
    • HTTP with host information and HTTPS/TLS with SNI
    • DNS queries and responses, including mDNS and LLMNR
    • SSH connections with version detection, software identification, and state tracking
    • QUIC protocol with CONNECTION_CLOSE frame detection and RFC 9000 compliance
    • FTP and MQTT
    • BitTorrent peer traffic, DHT, and uTP
    • DHCP and NTP
    • SSDP, NetBIOS, SNMP, and STUN
  • DPI Enhancements: Improve deep packet inspection capabilities:
    • Database and infrastructure protocols, in priority order:
      • MySQL: server version, capabilities, TLS use, command type, query verb, and error code
      • Redis: RESP version, command name, Pub/Sub state, and error type
      • PostgreSQL: TLS negotiation; database and user on plaintext connections; query verb, transaction state, and errors
      • Kafka: begin with header-level API operation/version and client ID, then consider selected client, topic/group, and error metadata without decoding record payloads
    • Add bounded, direction-aware, per-connection TCP stream reassembly before relying on protocol messages that cross TCP segments
    • Preserve the underlying application identity and TLS metadata for encrypted database connections instead of classifying every TLS handshake as HTTPS
    • Keep sensitive payloads out of DPI metadata: never surface full SQL, Redis arguments or AUTH data, database row values, or Kafka record payloads
    • Select further protocols only when usage and inspection value justify the maintenance cost
    • CDP/LLDP (network device discovery protocols)
    • LACP (Link Aggregation Control Protocol)
    • Reassemble QUIC CRYPTO frames across packets and promote partial SNI when complete metadata arrives
    • Recover complete HTTPS/TLS ClientHello metadata across TCP segments through bounded TCP reassembly
  • Per-connection RTT: Continuous smoothed round-trip estimate for every TCP connection (handshake RTT for QUIC), shown as a sortable table column, in the Details pane, and in exports
  • Connection Lifecycle Management: Smart protocol-aware timeouts with visual staleness indicators (yellow at 75%, red at 90%)
  • Process Identification: Associate network connections with running processes (with experimental eBPF support on Linux)
  • Service Name Resolution: Identify well-known services using port numbers
  • Cross-platform Support: Works on Linux, macOS, Windows, and FreeBSD
  • DNS Reverse Lookup: Add optional hostname resolution (toggle between IP and hostname display) - --resolve-dns flag with d key toggle
  • IPv6 Support: Parse and track IPv6 connections, including extension headers, process attribution, full-address display, and reverse DNS.
  • IPv6-only Interface Selection and Live Validation: Prefer a routed IPv6 interface when no usable IPv4 route exists, and validate live capture and process attribution on every supported platform.
  • VLAN Tag Detection: Surface 802.1Q VLAN IDs per connection. The Ethernet link-layer parser already parses VLAN tags to reach the inner packet (the VID is extracted and trace-logged); what remains is carrying the VID onto connections and showing it in the TUI
  • Passive Host Discovery: Learn a bounded cache of on-link IPv4 and IPv6 neighbors from observed ARP and NDP traffic, including MAC address, vendor, and last-seen metadata, without active scanning.
  • MAC Vendor Lookup (OUI): Resolve MAC addresses to hardware vendor names using a local OUI database (e.g. "Apple", "Intel", "Ubiquiti") - shown in the Details view, database bundled in rustnet-core
  • Advanced Filtering: Real-time vim/fzf-style filtering with:
    • Navigate while typing filters
    • Fuzzy search across all connection fields including DPI data
    • Keyword filters: port:, src:, dst:, sni:, process:, sport:, dport:, ssh:, state:
    • State filtering for all protocol states
    • Exact port matching by default (port:22 matches only port 22)
    • Regular expression support via /pattern/ syntax on any filter value

Sorting & Display

  • Sorting: Comprehensive table sorting with:
    • Sort by all columns: Protocol, Local/Remote Address, State, Service, Application, RTT, Bandwidth (Down/Up), Process
    • Intuitive left-to-right column cycling with s key
    • Direction toggle with S (Shift+s) for ascending/descending
    • Visual indicators: cyan/underlined active column, arrows showing direction
    • Smart defaults: bandwidth descending (show hogs), text ascending (alphabetical)
    • Bandwidth sorting: sorts by combined up+down bandwidth total
    • Seamless integration with filtering

Performance & Architecture

  • Multi-threaded Processing: Concurrent packet processing across multiple threads
  • Optional Logging: Detailed logging with configurable log levels (disabled by default)

Packaging & Distribution

  • Package Distribution: Pre-built packages available:
    • macOS DMG packages: Apple Silicon and Intel (via GitHub Actions release workflow)
    • Windows MSI packages: 64-bit and 32-bit (via cargo-wix)
    • Linux DEB packages: amd64, arm64, armhf (via cargo-deb)
    • Linux RPM packages: x86_64, aarch64 (via cargo-generate-rpm)
    • Cargo crates.io: Published as rustnet-monitor (version 0.10.0+)
    • Docker images: Available on GitHub Container Registry with eBPF support
    • Homebrew formula: Available in separate tap repository (domcyrus/rustnet)

Future Enhancements

  • Internationalization (i18n): Support for multiple languages in the UI
  • Connection History: Store and display historical connection data (toggle with t key, up to 5,000 archived connections)
  • PCAP Export: Export packets to PCAP file with process attribution sidecar (--pcap-export)
    • Standard PCAP format compatible with Wireshark/tcpdump
    • Streaming JSONL sidecar with PID, process name, timestamps
    • Python enrichment script to create annotated PCAPNG
  • Native Annotated PCAPNG Export: Export a Wireshark-ready PCAPNG file with live best-effort RustNet packet comments (--pcapng-export)
    • Per-packet comments include process/PID, direction, DPI/SNI, and GeoIP/ASN when available
    • Uses true capture timestamps and bounded attribution retry
  • Enhanced PCAP Metadata: Richer process information in sidecar file
    • Process executable full path (not just name)
    • Command line arguments
    • Working directory
    • User/UID information (UID and GID)
    • Parent process information (PPID)
  • Configuration File: Support for persistent configuration:
    • Custom color themes and UI styling
    • Default filters and sort preferences
    • Default process grouping (start with group: true in config)
    • Color mode preference (disable colors via config, complementing --no-color flag)
    • Per-interface settings
    • Keybinding customization
  • Connection Alerts: Notifications for new connections or suspicious activity
  • GeoIP Integration: Geographical location of remote IPs
  • GeoIP City-Level Resolution: Extend GeoIP to include city-level location data using GeoLite2-City database
  • Protocol Statistics: Summary view of protocol distribution (Graph tab shows application protocol distribution and TCP state distribution)
  • Rate Limiting Detection: Build an evidence-based classifier from the existing rolling throughput, retransmission, loss, and RTT metrics, and show the signals supporting each finding.
  • Bufferbloat Detection: Correlate the existing RTT history with throughput/load against an idle baseline, and report confidence without treating passive observations alone as proof.
  • PCAP Import/Replay: Add an offline capture reader, optional JSON process-attribution sidecar matching, and TUI playback controls. The core tracker already accepts trace timestamps for correct offline lifecycle and rate calculations.
  • Route Table Display: Expand the existing cross-platform default-route discovery and (gw) connection marker into a full TUI route table with prefixes, next hops, interfaces, metrics, and flags.
  • Privacy/Redact Mode: Obfuscate sensitive information (IPs, MACs, hostnames) in the TUI for safe screenshots and sharing. Include option to export connection details from the details view to a text file with privacy redaction applied

UI Improvements

  • Terminal User Interface: TUI built with ratatui with adjustable column widths
  • Sortable Columns: Keyboard-based sorting by all table columns
  • Keyboard Controls: Comprehensive keyboard navigation (q, Ctrl+C, x, Tab, arrows, j/k, g/G, PageUp/Down, Enter, Esc, c, p, s, S, h, /, a, r, Space)
  • Connection Details View: Detailed information about selected connections (Enter key)
  • Help Screen: Toggle help screen with keyboard shortcuts (h key)
  • Clipboard Support: Copy remote address to clipboard (c key)
  • Service/Port Toggle: Toggle between service names and port numbers (p key)
  • Platform-Specific CLI Help: Show only relevant options per platform (hide Linux sandbox options on macOS, hide PKTAP notes on Linux)
  • Connection Grouping: Group connections by process with expandable tree view (press a to toggle, aggregated stats, Space/arrows to expand/collapse)
  • Reset View: Reset all view settings (grouping, sort, filter) with r key
  • Resizable Columns: Automatically allocate widths for the available terminal size and hide lower-priority columns when space is constrained.
  • ASCII Graphs: Terminal-based graphs for bandwidth/packet visualization (Graph tab with braille traffic chart and connections sparkline)
  • Mouse Support: Click to select connections, double-click to open Details, clickable tab bar
  • Split Pane View: Show the Overview connection table beside the system-information sidebar, and use a responsive two-column Details layout.

Architecture

Workspace Split

Restructure the single crate into a Cargo workspace (same GitHub repo) with clear separation of concerns:

  • rustnet-monitor (binary, bin name rustnet): CLI, TUI, app event loop, and the user-facing application; process attribution is delegated to rustnet-host, interface statistics to rustnet-core, and sandboxing plus root privilege dropping to rustnet-sandbox. (Package stays rustnet-monitor because the rustnet crate name is taken on crates.io; the installed binary is rustnet.)
  • rustnet-core (library): Packet parsing, protocol types, DPI, link-layer parsers, connection merging, and DNS/GeoIP/OUI lookups -- the reusable, platform-independent, capture-independent analysis core. Lives at crates/rustnet-core. (Named rustnet-core rather than rustnet-net to avoid the redundant "net-net"; verified available on crates.io.)
  • rustnet-capture (library): the libpcap/Npcap-based capture backend -- device selection, BPF filters, macOS PKTAP, TUN/TAP, and a raw-frame PacketReader. Lives at crates/rustnet-capture. This is the existing pcap code moved into its own crate (not a libpcap-free rewrite): the point of the split is composability — a headless front-end (e.g. a Prometheus exporter) can pair rustnet-capture + rustnet-core without the TUI, and a platform wanting a bespoke capture path (e.g. the macOS pktap helper) can swap it out. The macOS DegradationReason coupling was untangled by giving capture its own PktapUnavailable enum, which the binary maps to its UI DegradationReason.
  • rustnet-host (library): Per-connection process attribution behind one ProcessLookup trait -- eBPF/procfs on Linux, PKTAP/lsof on macOS, the IP Helper API on Windows, and sockstat on FreeBSD. Lives at crates/rustnet-host and owns the eBPF build tooling (the socket_tracker.bpf.c program and bundled vmlinux.h). The binary injects PKTAP availability via report_pktap_degradation, so the crate needs no dependency on rustnet-capture.
  • rustnet-sandbox (library): Post-initialization sandboxing and root privilege dropping behind one apply_sandbox entry point -- Landlock + capability drops on Linux, Seatbelt on macOS, restricted token + job object on Windows, and the shared uid drop on Linux/macOS/FreeBSD. Lives at crates/rustnet-sandbox and depends on no other workspace crate, so a headless front-end gets identical sandboxing without linking capture or attribution code.
  • rustnet-helper (binary): Minimal suid helper for macOS pktap privilege separation (~100 lines, zero C deps — just libc). Future work, not yet a crate. The root-gated pktap interface creation (SIOCIFCREATE) can only be written and validated on real macOS hardware, so this is deferred until it can be done for real rather than scaffolded. See "macOS Privilege Separation" below.

Benefits:

  • Clean dependency boundaries (helper has zero C dependencies)
  • rustnet-core becomes independently useful as a Rust network analysis library
  • Compile times improve (parallel crate compilation)
  • cargo install rustnet-monitor continues to work unchanged

Status: The workspace exists with rustnet-monitor (binary) depending on rustnet-core, rustnet-capture, rustnet-host, and rustnet-sandbox. The binary's src/network module re-exports rustnet_core::network::* and rustnet_capture (as capture) so existing crate::network::* paths, integration tests, and benches are unchanged. Net-only dependencies (dns-lookup, ring, aes, flate2, maxminddb, pnet_datalink) and the baked-in oui.gz / services assets live in rustnet-core; all pcap usage lives in rustnet-capture; and procfs / libbpf-rs plus the eBPF programs and vmlinux.h live in rustnet-host; and landlock / caps plus Seatbelt and the uid drop live in rustnet-sandbox. rustnet-core also exposes a ConnectionTracker so headless tools can fold captured packets into a live, lifecycle-managed connection table without the TUI. Remaining work: the headless workstream below and the rustnet-helper macOS pktap suid helper (needs real hardware).

Headless Front-End Workstream

The library crates now cover the whole privileged pipeline: capture (rustnet-capture), parsing + connection tracking + interface stats (rustnet-core), process attribution (rustnet-host), and sandboxing + uid drop (rustnet-sandbox). examples/headless.rs is a compiling, runnable proof of that pairing. What a full headless front-end (Prometheus exporter, JSON streamer) still cannot get from the crates, from an audit of the binary:

Code that could move into a crate:

  • ConnectionFilter (the vim/fzf-style filter language, src/filter) into rustnet-core, so headless tools can reuse the same query syntax.
  • Kubernetes pod/container resolution (src/network/kubernetes, ~1000 lines) into rustnet-host next to the rest of attribution.
  • Process-grouping aggregation (currently in src/ui/state.rs) into rustnet-core, since per-process rollups are exporter material.
  • Optional serde derives for Connection and the DPI types behind a rustnet-core feature; today JSON output is hand-built in src/app/logging.rs and unavailable to library consumers.
  • The capture-privileges preflight check (src/network/privileges.rs) into rustnet-capture (its Windows probe already uses the pcap crate).

Composition APIs that exist only as binary wiring:

  • An engine/runtime handle for the thread topology (capture thread, DPI workers, batching, backpressure, catch_unwind) that src/app hand-builds.
  • The two-phase privileged start contract (open capture + load eBPF, wait for readiness, sandbox, then spawn workers) as an API instead of main.rs choreography; the ordering is documented in rustnet-sandbox but each front-end still re-implements the sequence.
  • A published-snapshot policy layer (service-name enrichment, localhost and PTR-lookup filtering, historic merge, sorting) over ConnectionTracker::snapshot.
  • Aggregate counters: an AppStats-shaped struct plus the IngestOutcome-to-counter folding, which is exactly the metric set an exporter would publish.
  • The enrichment driver loops (process attribution cadence and write-once policy, GeoIP refresh) as reusable helpers.
  • Health/degradation status types (capture failure retention, PKTAP degradation mapping) shared between front-ends.

macOS Privilege Separation (pktap without root)

Currently pktap requires root because the macOS kernel enforces a root check (SIOCIFCREATE ioctl) when creating the pktap pseudo-interface. This is independent of BPF device permissions (ChmodBPF). The goal is to run the main RustNet process as a regular user while only the minimal helper runs privileged.

Approach: Small suid helper binary that:

  1. Opens /dev/bpf* and creates the pktap interface (requires root)
  2. Configures BPF device (bind interface, set buffer size, immediate mode)
  3. Locks the device with BIOCLOCK (prevents further configuration changes)
  4. Passes the BPF file descriptor to the unprivileged RustNet process via Unix socket (SCM_RIGHTS)
  5. Drops privileges and exits

The main RustNet process reads packets directly from the received BPF fd using read() -- no libpcap needed on this path. The existing pktap header parser (link_layer/pktap.rs) already handles the packet format. BPF filter compilation is not needed since BPF filters are already incompatible with pktap.

On Linux/Windows/FreeBSD, nothing changes -- libpcap is used as today, with the existing capability-based privilege model on Linux.

Security properties:

  • Helper is tiny (~100 lines of Rust, no C code) -- minimal attack surface as root
  • BIOCLOCK prevents the unprivileged process from reconfiguring the capture device
  • Seatbelt sandbox can still be applied to the main process after fd handoff
  • Similar pattern to Wireshark's dumpcap but with a smaller privileged surface (no libpcap in the helper)

Development

  • Unit Tests: Broad unit coverage across packet parsing, DPI, tracking, filtering, capture, process attribution, platform code, and the TUI.
  • Integration Tests: Platform-specific integration tests for Linux and macOS (tests/integration_tests.rs)
  • Coverage Measurement and Gap Closure: Add a CI coverage report and define measurable expectations, then target platform-only and live-network paths that the existing broad test suite does not exercise reliably.
  • CI/CD Pipeline: Automated builds and releases for all platforms (GitHub Actions)
    • Release workflow: Multi-platform builds with cross-compilation
    • Docker workflow: Automated Docker image builds
    • Rust workflow: Basic CI checks
  • Documentation: Comprehensive README with usage guides, architecture overview, and troubleshooting
  • Packaging/Distribution: Create packages for easy installation on Linux, macOS, and Windows
    • DMG packages with code signing
    • MSI packages with code signing for Windows