ferranet 0.2.0

A modern, async-first, zero-copy datalink-layer (L2) networking library
Documentation
//! Capture and print frames arriving on an interface.
//!
//! ```sh
//! cargo build --example sniff
//! sudo setcap cap_net_raw+ep target/debug/examples/sniff
//! ./target/debug/examples/sniff eth0
//! ```

use ferranet::Channel;

#[tokio::main]
async fn main() -> ferranet::Result<()> {
    let iface = std::env::args().nth(1).unwrap_or_else(|| {
        eprintln!("usage: sniff <interface>");
        std::process::exit(2);
    });

    let (_tx, mut rx) = Channel::builder(&iface).promiscuous(true).build_async()?;
    println!("sniffing {iface} (Ctrl-C to stop)…");

    loop {
        let block = rx.recv_block().await?;
        for frame in block.frames() {
            let data = frame.data();
            let dst = data.get(0..6).map(hex).unwrap_or_default();
            let src = data.get(6..12).map(hex).unwrap_or_default();
            println!(
                "{:5} bytes  {} -> {}  type={:?}  vlan={:?}",
                frame.data().len(),
                src,
                dst,
                frame.packet_type(),
                frame.vlan(),
            );
        }
    }
}

fn hex(bytes: &[u8]) -> String {
    bytes.iter().map(|b| format!("{b:02x}")).collect::<Vec<_>>().join(":")
}