diff --git a/USAGE.md b/USAGE.md index db13c0b..e4dcf43 100644 --- a/USAGE.md +++ b/USAGE.md @@ -177,10 +177,17 @@ Apply a BPF (Berkeley Packet Filter) expression to filter packets at capture tim **Common filter expressions:** ```bash -# Filter by port +# Filter by port (matches source OR destination) rustnet --bpf-filter "port 443" rustnet --bpf-filter "port 80 or port 8080" +# Filter by destination port specifically +rustnet --bpf-filter "dst port 443" +rustnet --bpf-filter "tcp dst port 80" + +# Filter by source port specifically +rustnet --bpf-filter "src port 443" + # Filter by host rustnet --bpf-filter "host 192.168.1.1" rustnet --bpf-filter "net 10.0.0.0/8" @@ -196,7 +203,9 @@ rustnet --bpf-filter "tcp port 443 and host github.com" rustnet --bpf-filter "not port 22" ``` -**Note:** BPF filter syntax follows the pcap-filter(7) format. Invalid filters will cause RustNet to exit with an error. Use `man pcap-filter` for complete syntax documentation. +**Notes:** +- BPF filter syntax follows the pcap-filter(7) format. Invalid filters will cause RustNet to exit with an error. Use `man pcap-filter` for complete syntax documentation. +- **macOS limitation:** BPF filters are incompatible with PKTAP (linktype 149). When you specify a BPF filter on macOS, RustNet automatically falls back to regular interface capture. This means process identification uses `lsof` instead of PKTAP's direct process metadata, which may be slightly less accurate for short-lived connections. #### `-l, --log-level ` diff --git a/src/cli.rs b/src/cli.rs index d8afc20..fbbeab2 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -61,7 +61,7 @@ pub fn build_cli() -> Command { .short('f') .long("bpf-filter") .value_name("FILTER") - .help("BPF filter expression for packet capture (e.g., \"tcp port 443\")") + .help("BPF filter expression for packet capture (e.g., \"tcp port 443\", \"dst port 80\"). Note: On macOS, using a BPF filter disables PKTAP (process info falls back to lsof)") .required(false), ) .arg( diff --git a/src/network/capture.rs b/src/network/capture.rs index 1877e18..8c17c45 100644 --- a/src/network/capture.rs +++ b/src/network/capture.rs @@ -162,9 +162,11 @@ fn find_best_device() -> Result { /// Setup packet capture with the given configuration pub fn setup_packet_capture(config: CaptureConfig) -> Result<(Capture, String, i32)> { - // Try PKTAP first on macOS for process metadata, but only when no interface is explicitly specified + // Try PKTAP first on macOS for process metadata, but only when: + // - No interface is explicitly specified + // - No BPF filter is specified (BPF filters don't work with PKTAP's linktype 149) #[cfg(target_os = "macos")] - if config.interface.is_none() { + if config.interface.is_none() && config.filter.is_none() { log::info!("Attempting to use PKTAP for process metadata on macOS"); match Capture::from_device("pktap") { @@ -227,6 +229,10 @@ pub fn setup_packet_capture(config: CaptureConfig) -> Result<(Capture, S } // Fallback to regular capture (original code) + #[cfg(target_os = "macos")] + if config.filter.is_some() { + log::warn!("BPF filter specified - using regular capture instead of PKTAP (BPF filters don't work with PKTAP)"); + } log::info!("Setting up regular packet capture"); let device = find_capture_device(&config.interface)?;