ferranet 0.2.0

A modern, async-first, zero-copy datalink-layer (L2) networking library
Documentation
//! Hidden helpers used by the crate's benchmarks. Not part of the public API and exempt from
//! semver guarantees.

use std::mem;
use std::ptr;

use crate::sys::linux::ring::frames_over;
use crate::sys::linux::tpacket::tpacket_align;

/// Builds a synthetic `TPACKET_V3` block holding `num_frames` frames, each carrying `payload_len`
/// bytes laid out exactly as the kernel would, so the parse loop can be benchmarked without
/// privileges. Returns `(buffer, first_offset, num_frames)`.
#[must_use]
pub fn synth_v3_block(num_frames: u32, payload_len: usize) -> (Vec<u8>, usize, u32) {
    let hdr = mem::size_of::<libc::tpacket3_hdr>();
    let tp_mac = tpacket_align(hdr);
    let stride = tpacket_align(tp_mac + payload_len);
    let mut buf = vec![0u8; stride * num_frames as usize];

    for i in 0..num_frames as usize {
        let off = i * stride;
        // SAFETY: tpacket3_hdr is plain-old-data; an all-zero value is a valid initial state.
        let mut h: libc::tpacket3_hdr = unsafe { mem::zeroed() };
        h.tp_mac = tp_mac as u16;
        h.tp_snaplen = payload_len as u32;
        h.tp_len = payload_len as u32;
        h.tp_status = libc::TP_STATUS_USER;
        h.tp_sec = 1;
        h.tp_nsec = 2;
        h.tp_next_offset = if i + 1 == num_frames as usize { 0 } else { stride as u32 };
        // SAFETY: `off` is within `buf` (buf is `stride * num_frames` long) and a full header fits
        // because `stride >= tpacket_align(tp_mac + payload_len) >= tp_mac >= aligned hdr size`.
        unsafe {
            ptr::write_unaligned(buf.as_mut_ptr().add(off).cast::<libc::tpacket3_hdr>(), h);
        }
        // Give each frame a plausible unicast destination MAC so classification runs.
        let d = off + tp_mac;
        if payload_len >= 6 {
            buf[d..d + 6].copy_from_slice(&[0x02, 0, 0, 0, 0, 1]);
        }
    }

    (buf, 0, num_frames)
}

/// Drives the zero-copy parse loop over a synthetic block, returning `(frames, total_bytes)`.
///
/// Each frame's lazily-decoded metadata (timestamp, VLAN, packet type) is decoded too, so every
/// harness driving this — the fuzzer, Miri, the proptests, the parse benchmark — exercises the
/// full untrusted-header surface rather than only the offset arithmetic and slicing.
#[must_use]
pub fn parse_v3_block(buf: &[u8], first_off: usize, num_pkts: u32) -> (usize, usize) {
    let mut frames = 0;
    let mut bytes = 0;
    for f in frames_over(buf, first_off, num_pkts) {
        frames += 1;
        bytes += f.data().len();
        std::hint::black_box(f.meta());
    }
    (frames, bytes)
}

#[cfg(test)]
mod tests {
    use super::*;

    // These drive the block parser's `unsafe` (header `read_unaligned`, offset arithmetic, frame
    // slicing) and the synthesizer's `write_unaligned` over plain heap buffers — no syscalls — so
    // they run under Miri (`cargo +nightly miri test --lib`) to check for UB, out-of-bounds reads,
    // and provenance violations, not just panics.

    #[test]
    fn synth_block_round_trips() {
        let (buf, first, pkts) = synth_v3_block(8, 100);
        let (frames, bytes) = parse_v3_block(&buf, first, pkts);
        assert_eq!(frames, 8);
        assert_eq!(bytes, 8 * 100);
    }

    #[test]
    fn parse_handles_adversarial_inputs() {
        let (buf, _, _) = synth_v3_block(4, 64);
        // Each case must return without UB/OOB; Miri proves the pointer reads stay in bounds.
        let cases: &[(&[u8], usize, u32)] = &[
            (&[], 0, 0),
            (&[], 0, 100),
            (&[0u8; 8], 0, 50),       // buffer smaller than one header
            (&buf, buf.len() + 1, 4), // start offset past the end
            (&buf, usize::MAX, 4),    // offset that would overflow on add
            (&buf, 0, u32::MAX),      // absurd frame count
            (&buf, 0, 4),             // the well-formed case
        ];
        for &(data, off, n) in cases {
            let (frames, bytes) = parse_v3_block(data, off, n);
            assert!(frames <= n as usize);
            assert!(bytes <= data.len());
        }
    }

    #[test]
    fn parse_truncated_block_is_safe() {
        let (mut buf, first, pkts) = synth_v3_block(6, 200);
        buf.truncate(buf.len() / 2); // cut through the middle of a frame
        let (frames, bytes) = parse_v3_block(&buf, first, pkts);
        assert!(frames <= 6);
        assert!(bytes <= buf.len());
    }
}