crafter 0.3.1

Packet-level network interaction for Rust tools and agents.
Documentation
mod common;

use std::time::Duration;

use common::{
    arg_or, flag_present, local_ipv6, parse_ipv6_arg, parse_u16_arg, print_help_if_requested,
    remote_ipv6, ExampleResult, EXAMPLE_IFACE,
};
use crafter::prelude::*;

fn main() -> ExampleResult<()> {
    if print_help_if_requested(
        "usage: cargo run --example icmpv6_echo -- [--send-recv] [--iface IFACE] [--src IPv6] [--dst IPv6] [--id N] [--seq N]\n\nBuild an IPv6 ICMPv6 echo request offline and optionally inspect a dry-run send/receive report.",
    ) {
        return Ok(());
    }

    let send_recv = flag_present("--send-recv");
    let iface = arg_or("--iface", EXAMPLE_IFACE);
    let src = parse_ipv6_arg("--src", local_ipv6())?;
    let dst = parse_ipv6_arg("--dst", remote_ipv6())?;
    let id = parse_u16_arg("--id", 0x4242)?;
    let seq = parse_u16_arg("--seq", 1)?;
    let packet = Ipv6::new().src(src).dst(dst).hlim(64)
        / Icmpv6::echo_request().id(id).seq(seq)
        / Raw::from("icmpv6-echo");
    let compiled = packet.compile()?;
    let decoded = Packet::decode_from_l3(NetworkLayer::Ipv6, compiled.as_bytes())?;
    let icmpv6 = decoded
        .layer::<Icmpv6>()
        .expect("decoded ICMPv6 echo request should contain Icmpv6");

    println!("example: icmpv6_echo");
    println!(
        "mode: {}",
        if send_recv {
            "dry-run send/receive"
        } else {
            "offline"
        }
    );
    println!("source: {src}");
    println!("destination: {dst}");
    println!("decoded summary: {}", decoded.summary());
    println!(
        "icmpv6 checksum: {}",
        icmpv6
            .checksum_value()
            .map(|checksum| format!("0x{checksum:04x}"))
            .unwrap_or_else(|| "not decoded".to_string())
    );
    println!("checksum source: IPv6 pseudo-header computed during compile");
    println!("hexdump:\n{}", compiled.hexdump());

    if send_recv {
        let report = packet.send_recv_report(
            SendRecv::new()
                .iface(iface.clone())
                .network_layer()
                .dry_run()
                .timeout(Duration::from_millis(250))
                .retries(1),
        )?;

        println!("dry-run interface: {iface}");
        println!("attempts: {}", report.attempts());
        println!(
            "effective filter: {}",
            report.effective_filter().unwrap_or("")
        );
        println!("timed out: {}", report.timed_out());
        match report.reply() {
            Some(reply) => println!("reply: {}", reply.summary()),
            None => println!("reply: none"),
        }
    }

    Ok(())
}