use std::mem;
use std::ptr;
use crate::sys::linux::ring::frames_over;
use crate::sys::linux::tpacket::tpacket_align;
#[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;
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 };
unsafe {
ptr::write_unaligned(buf.as_mut_ptr().add(off).cast::<libc::tpacket3_hdr>(), h);
}
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)
}
#[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::*;
#[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);
let cases: &[(&[u8], usize, u32)] = &[
(&[], 0, 0),
(&[], 0, 100),
(&[0u8; 8], 0, 50), (&buf, buf.len() + 1, 4), (&buf, usize::MAX, 4), (&buf, 0, u32::MAX), (&buf, 0, 4), ];
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); let (frames, bytes) = parse_v3_block(&buf, first, pkts);
assert!(frames <= 6);
assert!(bytes <= buf.len());
}
}