crafter 0.3.0

Packet-level network interaction for Rust tools and agents.
Documentation
use std::net::Ipv4Addr;

use super::{IpDefrag, IpFragment};
use crate::wire::record::PacketRecord;
use crate::{Ipv4, Raw};

const MTU: usize = 40;
const IDENTIFICATION: u16 = 0x3939;
const PROTOCOL: u8 = 253;

fn source() -> Ipv4Addr {
    Ipv4Addr::new(192, 0, 2, 39)
}

fn destination() -> Ipv4Addr {
    Ipv4Addr::new(198, 51, 100, 39)
}

fn source_two() -> Ipv4Addr {
    Ipv4Addr::new(192, 0, 2, 40)
}

fn raw_record(payload: &[u8]) -> PacketRecord {
    PacketRecord::new(Raw::from_bytes(payload))
}

fn ipv4_fragment_record(
    source: Ipv4Addr,
    fragment_offset: u16,
    more_fragments: bool,
    payload: &[u8],
) -> PacketRecord {
    PacketRecord::new(
        Ipv4::with_addresses(source, destination())
            .protocol(PROTOCOL)
            .identification(IDENTIFICATION)
            .fragment_offset(fragment_offset)
            .more_fragments(more_fragments)
            / Raw::from_bytes(payload),
    )
}

fn oversized_ipv4_record(payload: &[u8]) -> PacketRecord {
    PacketRecord::new(
        Ipv4::with_addresses(source(), destination())
            .protocol(PROTOCOL)
            .identification(IDENTIFICATION)
            / Raw::from_bytes(payload),
    )
}

fn oversized_df_ipv4_record(payload: &[u8]) -> PacketRecord {
    PacketRecord::new(
        Ipv4::with_addresses(source(), destination())
            .protocol(PROTOCOL)
            .identification(IDENTIFICATION)
            .dont_fragment(true)
            / Raw::from_bytes(payload),
    )
}

#[test]
fn defrag_stats_count_pass_through_and_completed_datagrams() {
    let mut transform = IpDefrag::new().trace_passthrough(true);

    let pass_through = transform.defrag_record(raw_record(b"opaque")).unwrap();
    assert_eq!(pass_through.len(), 1);
    assert!(transform
        .defrag_record(ipv4_fragment_record(source(), 1, false, b"ijkl"))
        .unwrap()
        .is_empty());
    let reassembled = transform
        .defrag_record(ipv4_fragment_record(source(), 0, true, b"abcdefgh"))
        .unwrap();

    assert_eq!(reassembled.len(), 1);
    assert_eq!(transform.input_count(), 3);
    assert_eq!(transform.emitted_count(), 2);
    assert_eq!(transform.pass_through_count(), 1);
    assert_eq!(transform.fragments_observed(), 2);
    assert_eq!(transform.completed_datagrams(), 1);
    assert_eq!(transform.evicted_datagrams(), 0);
    assert_eq!(transform.conflicts(), 0);
    assert_eq!(transform.errors(), 0);

    let stats = transform.stats();
    assert_eq!(stats.input_count(), 3);
    assert_eq!(stats.emitted_count(), 2);
    assert_eq!(stats.pass_through_count(), 1);
    assert_eq!(stats.fragments_observed(), 2);
    assert_eq!(stats.completed_datagrams(), 1);
    assert_eq!(stats.evicted_datagrams(), 0);
    assert_eq!(stats.conflicts(), 0);
    assert_eq!(stats.errors(), 0);
}

#[test]
fn defrag_stats_count_evicted_datagrams_conflicts_and_errors() {
    let mut evicting = IpDefrag::new().max_datagrams(1);
    assert!(evicting
        .defrag_record(ipv4_fragment_record(source(), 0, true, b"abcdefgh"))
        .unwrap()
        .is_empty());
    assert!(evicting
        .defrag_record(ipv4_fragment_record(source_two(), 0, true, b"abcdefgh"))
        .unwrap()
        .is_empty());
    assert_eq!(evicting.fragments_observed(), 2);
    assert_eq!(evicting.evicted_datagrams(), 1);
    assert_eq!(evicting.stats().evicted_datagrams(), 1);

    let mut conflicting = IpDefrag::new();
    assert!(conflicting
        .defrag_record(ipv4_fragment_record(source(), 0, true, b"abcdefghijklmnop"))
        .unwrap()
        .is_empty());
    let error = conflicting
        .defrag_record(ipv4_fragment_record(source(), 1, false, b"QRSTUVWX"))
        .unwrap_err();
    assert!(!error.to_string().is_empty());
    assert_eq!(conflicting.fragments_observed(), 2);
    assert_eq!(conflicting.conflicts(), 1);
    assert_eq!(conflicting.errors(), 1);
    assert_eq!(conflicting.stats().conflicts(), 1);
    assert_eq!(conflicting.stats().errors(), 1);
}

#[test]
fn fragment_stats_count_emitted_fragments_and_pass_through() {
    let payload = (0u8..21).collect::<Vec<_>>();
    let mut transform = IpFragment::new(MTU);

    let fragments = transform
        .fragment_record(oversized_ipv4_record(&payload))
        .unwrap();
    assert_eq!(fragments.len(), 2);
    assert_eq!(transform.input_count(), 1);
    assert_eq!(transform.emitted_count(), 2);
    assert_eq!(transform.pass_through_count(), 0);
    assert_eq!(transform.fragments_observed(), 2);
    assert_eq!(transform.completed_datagrams(), 1);
    assert_eq!(transform.evicted_datagrams(), 0);
    assert_eq!(transform.conflicts(), 0);
    assert_eq!(transform.errors(), 0);

    let pass_through = transform.fragment_record(raw_record(b"opaque")).unwrap();
    assert_eq!(pass_through.len(), 1);
    let stats = transform.stats();
    assert_eq!(stats.input_count(), 2);
    assert_eq!(stats.emitted_count(), 3);
    assert_eq!(stats.pass_through_count(), 1);
    assert_eq!(stats.fragments_observed(), 2);
    assert_eq!(stats.completed_datagrams(), 1);
    assert_eq!(stats.evicted_datagrams(), 0);
    assert_eq!(stats.conflicts(), 0);
    assert_eq!(stats.errors(), 0);
}

#[test]
fn fragment_stats_count_errors() {
    let payload = (0u8..21).collect::<Vec<_>>();
    let mut transform = IpFragment::new(MTU);

    let error = transform
        .fragment_record(oversized_df_ipv4_record(&payload))
        .unwrap_err();

    assert!(!error.to_string().is_empty());
    assert_eq!(transform.input_count(), 1);
    assert_eq!(transform.emitted_count(), 0);
    assert_eq!(transform.pass_through_count(), 0);
    assert_eq!(transform.fragments_observed(), 0);
    assert_eq!(transform.completed_datagrams(), 0);
    assert_eq!(transform.errors(), 1);
    assert_eq!(transform.stats().errors(), 1);
}