# RustNet Roadmap This document outlines the planned features and improvements for RustNet. ## Platform Support - [x] **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 - [x] **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) - [x] **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 (capabilities + Landlock network)**: Landlock already enforces deny-by-default filesystem access on the post-sandbox worker threads, but two gaps remain when rustnet runs as root: - **Drop all non-essential capabilities** (or clear the bounding set via `PR_CAPBSET_DROP`) before spawning the worker threads. Today only `CAP_NET_RAW`/`CAP_BPF`/`CAP_PERFMON` are dropped, so a root-launched process retains `CAP_DAC_OVERRIDE`, `CAP_SYS_ADMIN`, `CAP_SYS_MODULE`, etc. — which Landlock does not cover (non-filesystem/non-TCP abuse such as loading kernel modules). Running non-root with a `cap_net_raw` file capability already avoids this; the hardening is for the common `sudo rustnet` case. - **UDP egress is not blocked**: Landlock ABI v4 only governs TCP `bind`/`connect`, so UDP exfiltration remains possible (accepted as low risk today, since filesystem reads are tightly contained). Revisit when a newer Landlock ABI adds UDP support. Note macOS Seatbelt already blocks both TCP and UDP. - [ ] **OpenBSD and NetBSD Support**: Future platforms to support - [x] **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//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 - [x] **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. - [x] **Full Executable Path Resolution**: `/proc//exe` is resolved in user space immediately after attribution; kernel-side resolution turned out to be unnecessary. - [x] **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//comm`, and comm truncation is recovered from the executable's file name. - [x] **Enhanced BTF Support**: Covered by the fentry/fexit backend selection below, which uses actual BTF, load, and attach results per kernel. - [ ] **Performance Optimizations**: Reduce eBPF map lookups and improve connection-to-process matching efficiency - [x] **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**: Track process name changes and executable updates - **Container Support**: Better process identification within containerized environments - **Security Context**: Include process security attributes (capabilities, SELinux context, etc.) - **Cross-Namespace Attribution for Kubernetes**: The current procfs fallback reads `/proc/net/tcp` from the reader's network namespace, so under `hostNetwork: true` (as used by kubectl-rustnet) it never sees sockets owned by pods in their own netns. The kubernetes feature ships a scoped per-PID `/proc//net/{tcp,tcp6,udp,udp6}` walker that covers TCP+UDP for kubepods PIDs, but it ticks at the enrichment interval and so misses sub-tick ephemeral flows. The complete fix lives in the eBPF layer: kprobes/fentry are netns-agnostic and fire at `connect()`/`accept()` time, but the current socket-tracker map is being pruned more aggressively than userspace can consume. Plan: extend map retention (or switch to a ring buffer of close events that userspace drains opportunistically), debug the "Map Lookup Miss" path under Kubernetes traffic patterns, and verify cross-namespace coverage end-to-end in a kind cluster. This work also benefits ICMP and raw-socket attribution, which procfs cannot reach. ## Features ### Monitoring & Protocol Support - [x] **Real-time Network Monitoring**: Monitor active TCP, UDP, ICMP, and ARP connections - [x] **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) - [x] **Deep Packet Inspection (DPI)**: Application protocol detection: - HTTP with host information - HTTPS/TLS with SNI (Server Name Indication) - DNS queries and responses - SSH connections with version detection, software identification, and state tracking - QUIC protocol with CONNECTION_CLOSE frame detection and RFC 9000 compliance - [ ] **DPI Enhancements**: Improve deep packet inspection capabilities: - Support more protocols (e.g. FTP, SMTP, IMAP, etc.) - CDP/LLDP (network device discovery protocols) - LACP (Link Aggregation Control Protocol) - More accurate SNI detection for QUIC/HTTPS - [x] **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 - [x] **Connection Lifecycle Management**: Smart protocol-aware timeouts with visual staleness indicators (yellow at 75%, red at 90%) - [x] **Process Identification**: Associate network connections with running processes (with experimental eBPF support on Linux) - [x] **Service Name Resolution**: Identify well-known services using port numbers - [x] **Cross-platform Support**: Works on Linux, macOS, Windows, and FreeBSD - [x] **DNS Reverse Lookup**: Add optional hostname resolution (toggle between IP and hostname display) - `--resolve-dns` flag with `d` key toggle - [ ] **IPv6 Support**: Full IPv6 connection tracking and display, including DNS resolution (needs testing) - [ ] **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**: Infer local network hosts from observed ARP requests/replies and other broadcast traffic without active scanning - [x] **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` ### Filtering & Search - [x] **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 - [x] **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 - [x] **Multi-threaded Processing**: Concurrent packet processing across multiple threads - [x] **Optional Logging**: Detailed logging with configurable log levels (disabled by default) ### Packaging & Distribution - [x] **Package Distribution**: Pre-built packages available: - [x] **macOS DMG packages**: Apple Silicon and Intel (via GitHub Actions release workflow) - [x] **Windows MSI packages**: 64-bit and 32-bit (via cargo-wix) - [x] **Linux DEB packages**: amd64, arm64, armhf (via cargo-deb) - [x] **Linux RPM packages**: x86_64, aarch64 (via cargo-generate-rpm) - [x] **Cargo crates.io**: Published as `rustnet-monitor` (version 0.10.0+) - [x] **Docker images**: Available on GitHub Container Registry with eBPF support - [x] **Homebrew formula**: Available in separate tap repository (domcyrus/rustnet) ### Future Enhancements - [ ] **Internationalization (i18n)**: Support for multiple languages in the UI - [x] **Connection History**: Store and display historical connection data (toggle with `t` key, up to 5,000 archived connections) - [x] **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 - [x] **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 - [x] Process executable full path (not just name) - [ ] Command line arguments - [ ] Working directory - [x] User/UID information (UID and GID) - [x] 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 - [x] **GeoIP Integration**: Geographical location of remote IPs - [x] **GeoIP City-Level Resolution**: Extend GeoIP to include city-level location data using GeoLite2-City database - [x] **Protocol Statistics**: Summary view of protocol distribution (Graph tab shows application protocol distribution and TCP state distribution) - [ ] **Rate Limiting Detection**: Identify connections with unusual traffic patterns - [ ] **Bufferbloat Detection**: Measure latency under load to identify bufferbloat issues on the network - [ ] **PCAP Import/Replay**: Load a PCAP file (with optional JSON process attribution sidecar) and replay it in the TUI for offline analysis. Enables remote monitoring workflows: capture on a remote host with `--pcap-export`, transfer files, and replay locally with full process-attributed view - [ ] **Route Table Display**: Show the system routing table in a user-friendly view within the TUI - [ ] **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 - [x] **Terminal User Interface**: TUI built with ratatui with adjustable column widths - [x] **Sortable Columns**: Keyboard-based sorting by all table columns - [x] **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) - [x] **Connection Details View**: Detailed information about selected connections (Enter key) - [x] **Help Screen**: Toggle help screen with keyboard shortcuts (h key) - [x] **Clipboard Support**: Copy remote address to clipboard (c key) - [x] **Service/Port Toggle**: Toggle between service names and port numbers (p key) - [x] **Platform-Specific CLI Help**: Show only relevant options per platform (hide Linux sandbox options on macOS, hide PKTAP notes on Linux) - [x] **Connection Grouping**: Group connections by process with expandable tree view (press `a` to toggle, aggregated stats, Space/arrows to expand/collapse) - [x] **Reset View**: Reset all view settings (grouping, sort, filter) with `r` key - [ ] **Resizable Columns**: Dynamic column width adjustment - [x] **ASCII Graphs**: Terminal-based graphs for bandwidth/packet visualization (Graph tab with braille traffic chart and connections sparkline) - [x] **Mouse Support**: Click to select connections, double-click to open Details, clickable tab bar - [ ] **Split Pane View**: Show multiple views simultaneously ## Architecture ### Workspace Split Restructure the single crate into a Cargo workspace (same GitHub repo) with clear separation of concerns: - [x] **rustnet-monitor** (binary, bin name `rustnet`): CLI, TUI, app event loop, sandboxing (Landlock/Seatbelt), and interface statistics -- the user-facing application; process attribution is delegated to `rustnet-host`. (Package stays `rustnet-monitor` because the `rustnet` crate name is taken on crates.io; the installed binary is `rustnet`.) - [x] **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.) - [x] **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`. - [x] **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-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`, and `rustnet-host`. 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`. `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 `rustnet-helper` macOS pktap suid helper (needs real hardware). ### 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 - [x] **Unit Tests**: Basic unit tests in 12+ source modules (DPI protocols, filtering, services, network capture, etc.) - [x] **Integration Tests**: Platform-specific integration tests for Linux and macOS (tests/integration_tests.rs) - [ ] **Comprehensive Test Coverage**: Expand test coverage across all modules - [x] **CI/CD Pipeline**: Automated builds and releases for all platforms (GitHub Actions) - [x] **Release workflow**: Multi-platform builds with cross-compilation - [x] **Docker workflow**: Automated Docker image builds - [x] **Rust workflow**: Basic CI checks - [x] **Documentation**: Comprehensive README with usage guides, architecture overview, and troubleshooting - [x] **Packaging/Distribution**: Create packages for easy installation on Linux, macOS, and Windows - DMG packages with code signing - MSI packages with code signing for Windows