use pcap_frame_parser::{ethernet, pcapng};
fn build_udp_over_vlan_frame(payload: &[u8]) -> Vec<u8> {
let mut frame = Vec::new();
frame.extend_from_slice(&[0u8; 12]); frame.extend_from_slice(ðernet::ETHERTYPE_8021Q.to_be_bytes());
frame.extend_from_slice(&0x0010u16.to_be_bytes()); frame.extend_from_slice(ðernet::ETHERTYPE_IP.to_be_bytes());
let udp_len = 8 + payload.len();
let total_len = 20 + udp_len;
frame.push(0x45);
frame.push(0);
frame.extend_from_slice(&(total_len as u16).to_be_bytes());
frame.extend_from_slice(&0u16.to_be_bytes());
frame.extend_from_slice(&0u16.to_be_bytes());
frame.push(64);
frame.push(ethernet::PROTO_UDP);
frame.extend_from_slice(&0u16.to_be_bytes());
frame.extend_from_slice(&[10, 0, 0, 1]);
frame.extend_from_slice(&[10, 0, 0, 2]);
frame.extend_from_slice(&5353u16.to_be_bytes()); frame.extend_from_slice(&53u16.to_be_bytes()); frame.extend_from_slice(&(udp_len as u16).to_be_bytes());
frame.extend_from_slice(&0u16.to_be_bytes()); frame.extend_from_slice(payload);
frame
}
fn build_pcapng_with_frame(frame: &[u8]) -> Vec<u8> {
let mut buf = Vec::new();
const BLOCK_SHB: u32 = 0x0A0D_0D0A;
const BOM: u32 = 0x1A2B_3C4D;
const BLOCK_IDB: u32 = 1;
const BLOCK_EPB: u32 = 6;
let shb_len: u32 = 28;
buf.extend_from_slice(&BLOCK_SHB.to_le_bytes());
buf.extend_from_slice(&shb_len.to_le_bytes());
buf.extend_from_slice(&BOM.to_le_bytes());
buf.extend_from_slice(&1u16.to_le_bytes());
buf.extend_from_slice(&0u16.to_le_bytes());
buf.extend_from_slice(&(-1i64).to_le_bytes());
buf.extend_from_slice(&shb_len.to_le_bytes());
let idb_len: u32 = 20;
buf.extend_from_slice(&BLOCK_IDB.to_le_bytes());
buf.extend_from_slice(&idb_len.to_le_bytes());
buf.extend_from_slice(&1u16.to_le_bytes()); buf.extend_from_slice(&0u16.to_le_bytes());
buf.extend_from_slice(&65535u32.to_le_bytes());
buf.extend_from_slice(&idb_len.to_le_bytes());
let padded_len = (frame.len() + 3) & !3;
let epb_len = 32 + padded_len as u32;
buf.extend_from_slice(&BLOCK_EPB.to_le_bytes());
buf.extend_from_slice(&epb_len.to_le_bytes());
buf.extend_from_slice(&0u32.to_le_bytes());
buf.extend_from_slice(&0u32.to_le_bytes());
buf.extend_from_slice(&2_000_000u32.to_le_bytes()); buf.extend_from_slice(&(frame.len() as u32).to_le_bytes());
buf.extend_from_slice(&(frame.len() as u32).to_le_bytes());
buf.extend_from_slice(frame);
buf.extend(std::iter::repeat_n(0u8, padded_len - frame.len()));
buf.extend_from_slice(&epb_len.to_le_bytes());
buf
}
#[test]
fn full_pipeline_pcapng_to_udp_payload() {
let dns_query_stub = b"fake-dns-query-bytes";
let frame = build_udp_over_vlan_frame(dns_query_stub);
let capture = build_pcapng_with_frame(&frame);
let packets = pcapng::parse_pcapng(&capture).expect("valid capture");
assert_eq!(packets.len(), 1);
let (ts_sec, _ts_usec, raw_frame) = &packets[0];
assert_eq!(*ts_sec, 2);
let (ip_header, ip_payload) = ethernet::extract_ip(raw_frame).expect("VLAN-tagged IPv4 frame");
assert_eq!(ip_header.protocol, ethernet::PROTO_UDP);
assert_eq!(ip_header.src_str(), "10.0.0.1");
assert_eq!(ip_header.dst_str(), "10.0.0.2");
let (src_port, dst_port, udp_payload) =
ethernet::extract_udp(ip_payload).expect("well-formed UDP datagram");
assert_eq!(src_port, 5353);
assert_eq!(dst_port, 53);
assert_eq!(udp_payload, dns_query_stub);
}