crafter 0.3.2

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

use common::{flag_present, print_help_if_requested, ExampleResult, EXAMPLE_IFACE};
use crafter::prelude::*;
use crafter::protocols::rip::ripng::ripng_whole_table_request;
use std::net::Ipv6Addr;

/// Documentation-range source address for the RIPng speaker (2001:db8::/32).
const RIPNG_SOURCE: Ipv6Addr = Ipv6Addr::new(0x2001, 0x0db8, 0, 0, 0, 0, 0, 0x0010);
/// First advertised IPv6 prefix (2001:db8:1::/48).
const ROUTE_ONE_PREFIX: Ipv6Addr = Ipv6Addr::new(0x2001, 0x0db8, 1, 0, 0, 0, 0, 0);
/// Second advertised IPv6 prefix (2001:db8:2::/48).
const ROUTE_TWO_PREFIX: Ipv6Addr = Ipv6Addr::new(0x2001, 0x0db8, 2, 0, 0, 0, 0, 0);

fn main() -> ExampleResult<()> {
    if print_help_if_requested(
        "usage: cargo run --example ripng_request -- [--send]\n\nBuild a RIPng whole-table request and a RIPng response over documentation address space (2001:db8::/32) and inspect their dry-run send plans. Offline by default; --send is reserved for an explicit, gated live path and is not enabled in this example.",
    ) {
        return Ok(());
    }

    // The live path is opt-in only. Even when requested we refuse to place real
    // RIPng traffic on the wire from this example: real sends must originate
    // from a provider-backed endpoint or lab session, not the developer machine.
    if flag_present("--send") {
        eprintln!(
            "refusing live send: ripng_request is an offline stimulus driver; \
             run RIPng against a provider-backed routing daemon via the probe/lab runners instead"
        );
        return Err("live RIPng send is not enabled in this example".into());
    }

    println!("example: ripng_request");
    println!("mode: dry-run");
    println!("source: {RIPNG_SOURCE}");

    // 1. RIPng whole-table request (RFC 2080 ยง2.4.1): a Request message carrying
    //    the single ::/0 metric-16 sentinel RTE, addressed to UDP/521 and the
    //    all-RIPng-routers multicast group ff02::9.
    let request = ripng_whole_table_request(RIPNG_SOURCE);
    print_plan("request", "RIPng whole-table request", &request)?;

    // 2. RIPng response (RFC 2080) advertising a couple of documentation-range
    //    IPv6 prefixes (2001:db8::/32) to the ff02::9 multicast group over
    //    UDP/521.
    let response = Ipv6::new().src(RIPNG_SOURCE).dst(RIPNG_MULTICAST)
        / Udp::new().sport(RIPNG_UDP_PORT).dport(RIPNG_UDP_PORT)
        / Ripng::response().with_rtes(vec![
            RipngRte::route(ROUTE_ONE_PREFIX, 48, 1),
            RipngRte::route(ROUTE_TWO_PREFIX, 48, 2).route_tag(0),
        ]);
    print_plan("response", "RIPng multicast response", &response)?;

    Ok(())
}

fn print_plan(label: &str, description: &str, packet: &Packet) -> ExampleResult<()> {
    let plan = packet.send_dry_run(SendOptions::new().iface(EXAMPLE_IFACE).network_layer())?;

    println!();
    println!("{label}: {description}");
    println!("summary: {}", packet.summary());
    println!("interface: {}", plan.interface());
    println!("target: {:?}", plan.target());
    println!("compiled bytes: {}", plan.len());
    println!("show:\n{}", packet.show());
    println!("hexdump:\n{}", plan.compiled_packet().hexdump());
    Ok(())
}