cu_udp_inject/
lib.rs

1use cu29_traits::CuResult;
2use pcap::Capture;
3use std::net::UdpSocket;
4use std::path::Path;
5use std::thread::sleep;
6use std::time::{Duration, Instant};
7
8/// Small helper to stream a pcap file over UDP
9pub struct PcapStreamer {
10    capture: Capture<pcap::Offline>,
11    socket: UdpSocket,
12    target_addr: String,
13    last_packet_ts: Option<Duration>,
14    start_instant: Instant,
15}
16
17impl PcapStreamer {
18    pub fn new(file_path: impl AsRef<Path>, target_addr: impl Into<String>) -> Self {
19        let capture = Capture::from_file(file_path).expect("Failed to open pcap file");
20        let socket = UdpSocket::bind("0.0.0.0:0").expect("Failed to bind UDP socket");
21        Self {
22            capture,
23            socket,
24            target_addr: target_addr.into(),
25            last_packet_ts: None,
26            start_instant: Instant::now(),
27        }
28    }
29
30    /// Send the next packet in the pcap file over UDP
31    /// PS is the expected payload size to send in each packet
32    /// Returns false if the end of the pcap file is reached
33    pub fn send_next<const PS: usize>(&mut self) -> CuResult<bool> {
34        // Get the next packet and check for end of stream
35        let packet = match self.capture.next_packet() {
36            Ok(packet) => packet,
37            Err(_) => return Ok(false), // End of the stream
38        };
39
40        // Assume 42-byte header (Ethernet + IP + UDP) and an optional 4-byte FCS
41        let payload_offset = 42;
42        if packet.data.len() < payload_offset + PS {
43            return Err("Packet too short".into());
44        }
45
46        // Extract only the payload, excluding headers and trailing FCS if present
47        let payload = &packet.data[payload_offset..payload_offset + PS];
48
49        // Extract the timestamp from the packet
50        let ts = packet.header.ts;
51        let packet_ts = Duration::new(ts.tv_sec as u64, ts.tv_usec as u32 * 1000);
52
53        if let Some(last_ts) = self.last_packet_ts {
54            // Sleep to match the delay between packets
55            let elapsed = self.start_instant.elapsed();
56            if packet_ts > last_ts {
57                let wait_time = packet_ts - last_ts;
58                if elapsed < wait_time {
59                    sleep(wait_time - elapsed);
60                }
61            }
62        }
63        self.last_packet_ts = Some(packet_ts);
64        self.socket
65            .send_to(payload, &self.target_addr)
66            .expect("Failed to send packet");
67        Ok(true)
68    }
69}