net_replay/
ip.rs

1//! Parse and export IP packets
2//!
3//! This module has functionality and limitations very specific to its use case. Do not use this as a general
4//! purpose packet parsing module.
5//!
6//! This module provides functionality for parsing TCP/IP and UDP/IP packets from a raw socket and reading and writing
7//! those packets from a pcap file. The functionality in this module is mainly meant as utility for the rest of the
8//! library. As a user of the library, you should really only need to use [`read_pcap_file`] and [`write_pcap_file`] as
9//! well as the public types.
10
11use std::convert::From;
12use std::fmt;
13use std::io::{Read, Write};
14use std::mem::transmute;
15use std::net::Ipv4Addr;
16use std::time::{Duration, SystemTime};
17
18use bitflags::bitflags;
19use thiserror::Error;
20
21/// Magic value for a pcap file
22const PCAP_MAGIC: u32 = 0xA1B2_3C4D;
23
24/// PCAP magic of the opposite endianness
25const PCAP_BAD_ENDIANNESS: u32 = 0x4D3C_B2A1;
26
27/// Errors that can occur when parsing an ip header
28#[derive(Debug, Error)]
29pub enum IpParseErr {
30    /// IP packet was not version 4. Contains the parsed version.
31    #[error("Found version {0}, expected 4")]
32    Version(u8),
33    /// IP packet encapsulated an unsupported protocol
34    #[error("Found protocol {0}")]
35    Proto(u8),
36    /// Not enough bytes to be an IP header. Minimum of 20. Contains size of passed buffer.
37    #[error("IPv4 header is 20 bytes, only passed a buffer of size {0}")]
38    Size(usize),
39    /// Error occured when parsing the TCP portion of the payload.
40    #[error("Failed to parse the TCP payload")]
41    Tcp(#[from] TcpParseErr),
42    /// Failed to parse the underlying udp packet
43    #[error("Error occurred while parsing UDP payload")]
44    Udp(#[from] UdpParseErr),
45    /// IP header contained options. This struct doesn't support those.
46    #[error("This library doesn't support ip options")]
47    Options,
48    /// Size field in ip header is larger than passed in slice or smaller than the IP header
49    #[error("Size field in packet is larger than data passed in ({0})")]
50    InvalidSize(usize),
51    /// IHL field was not in a valid range
52    #[error("IHL field was outside valid range of [6, 15] ({0})")]
53    InvalidIHL(usize),
54    /// Error reading from IO
55    #[error("Encountered IO error")]
56    IOError(#[from] std::io::Error),
57    /// Endianness of pcap file does not match native endianness
58    #[error("PCAP file endianness does not match")]
59    EndianError,
60    /// Found a bad magic value in the pcap file
61    #[error("Pcap file had bad magic: {0:0X}")]
62    BadMagic(u32),
63    /// PCAP file is invalid in some way
64    #[error("Pcap file is invalid")]
65    PcapInvalid,
66    /// PCAP file does not have enough data or a size field was corrupted
67    #[error("Pcap file too small")]
68    PcapFileSize,
69}
70
71/// Data that the IP packet encapsulates
72///
73/// An IP packet can encapsulate many different types of data. Each possible encapsulated type that this library
74/// supports is represented by this enum.
75///
76/// This enum is marked as non-exhaustive as more underlying types may be added in future versions of the library.
77#[non_exhaustive]
78#[derive(Debug)]
79pub enum DataType {
80    TCP(TcpPacket),
81    UDP(UdpDatagram),
82}
83
84impl DataType {
85    /// Write the underlying protocol data to the pcap file
86    ///
87    /// # Errors
88    /// Will return any io errors that occur
89    pub fn pcap_write<W: Write>(&self, f: &mut W) -> std::io::Result<()> {
90        match self {
91            Self::TCP(p) => p.pcap_write(f),
92            Self::UDP(p) => p.pcap_write(f),
93        }
94    }
95
96    pub fn get_proto_num(&self) -> u8 {
97        match self {
98            Self::TCP(_) => 6,
99            Self::UDP(_) => 17,
100        }
101    }
102
103    pub fn get_payload(&self) -> &Vec<u8> {
104        match self {
105            Self::TCP(p) => &p.data,
106            Self::UDP(p) => &p.payload,
107        }
108    }
109}
110
111impl From<TcpPacket> for DataType {
112    fn from(packet: TcpPacket) -> Self {
113        Self::TCP(packet)
114    }
115}
116
117impl From<UdpDatagram> for DataType {
118    fn from(packet: UdpDatagram) -> Self {
119        Self::UDP(packet)
120    }
121}
122
123/// IP header and its associated TCP data
124#[derive(Debug)]
125pub struct IpPacket {
126    /// Differentiated Services Code Point
127    ///
128    /// This field is mainly used for real time protocols to determine what kind of data is in
129    /// the packet. Should be left alone.
130    pub dscp: u8,
131    /// Explicit Congestion Notification
132    ///
133    /// Used to indicate network congestion without having to drop packets.
134    pub ecn: u8,
135    /// Total Length
136    ///
137    /// Length of the IP packet and associated data. Minimum size of 20 bytes for the header.
138    /// This field is encoded as a u16 so it has a maximum value of 65535.
139    pub len: usize,
140    /// Identification
141    ///
142    /// Primarily used for identifying a group of fragments.
143    pub id: u16,
144    /// Flags
145    ///
146    /// A bit field used for controllig and identifying a fragmented IP packet.
147    pub flags: u8,
148    /// Fragment Offset
149    ///
150    /// Specifies the offset of a fragment with respect to the beginning of the fragment. Units
151    /// are 64 bits.
152    pub frag_off: u16,
153    /// Time to Live
154    ///
155    /// Originally specified the time to live for a packet in seconds. It is now generally used
156    /// as a hop count.
157    pub ttl: u8,
158    // Only support TCP so don't worry about that field
159    // pub protocol: u8,
160    /// Header Checksum
161    ///
162    /// Checksum over the header of the IP packet.
163    pub checksum: u16,
164    /// Source Address
165    ///
166    /// The IP address from which this packet was sent.
167    pub source: Ipv4Addr,
168    /// Destination Address
169    ///
170    /// The IP address where this packet is being sent to.
171    pub dest: Ipv4Addr,
172    /// IP options
173    ///
174    /// Any options the IP header may have. This field will only contain a vector if the IHL field is in the range
175    /// [6, 15].
176    pub options: Option<Vec<u8>>,
177    /// Encapsulated data
178    ///
179    /// The payload can be any of the supported types in the [`DataType`] enum
180    pub payload: DataType,
181    /// Time packet received at
182    ///
183    /// This field is populated at the time the packet was parsed at and may not perfectly reflect the time at which
184    /// the packet was actually received on the interface.
185    pub recv_time: SystemTime,
186}
187
188impl IpPacket {
189    /// Parse an ip header and TCP packet out of a stream of bytes
190    ///
191    /// This method is meant to be called with data read from the raw socket. It will read out any IPv4 TCP packet
192    /// and parse the containing data.
193    ///
194    /// # Errors
195    /// Returns an error if an unsupported packet type is passed in. Those cases are an IPv6 packet, non-TCP packet,
196    /// or a slice smaller than 20 bytes.
197    ///
198    /// # Panics
199    /// This method may panic. If the system time is before the [`SystemTime::UNIX_EPOCH`] time, then a panic will
200    /// occur.
201    pub fn parse_from_bytes(
202        data: &dyn AsRef<[u8]>,
203        time: Option<SystemTime>,
204    ) -> Result<Self, IpParseErr> {
205        // This should never fail. The only times that this would fail is if the current system time is from before the
206        // unix epoch. I feel safe in assuming that the system time will never drift that far.
207        let recv_time = match time {
208            None => SystemTime::now(),
209            Some(t) => t,
210        };
211        let data = data.as_ref();
212        if data.len() < 20 {
213            return Err(IpParseErr::Size(data.len()));
214        }
215        let version = data[0] >> 4;
216        if version != 4 {
217            return Err(IpParseErr::Version(version));
218        }
219        let ihl = data[0] & 0xf;
220        if !(5..=15).contains(&ihl) {
221            return Err(IpParseErr::InvalidIHL(ihl as usize));
222        }
223        if ihl as usize * 4 > data.len() {
224            return Err(IpParseErr::InvalidIHL(ihl as usize));
225        }
226        let dscp = (data[1] & 0xfc) >> 2;
227        let ecn = data[1] & 0x3;
228        let len = u16::from_be_bytes(data[2..4].try_into().unwrap()) as usize;
229        if len > data.len() {
230            return Err(IpParseErr::InvalidSize(len));
231        }
232        let id = u16::from_be_bytes(data[4..6].try_into().unwrap());
233        let flags = data[6] >> 5;
234        let frag_off: u16 = (u16::from(data[6] & 0x1f) << 8) + u16::from(data[7]);
235        let ttl = data[8];
236        let proto = data[9];
237        let checksum = u16::from_be_bytes(data[10..12].try_into().unwrap());
238        let source = Ipv4Addr::new(data[12], data[13], data[14], data[15]);
239        let dest = Ipv4Addr::new(data[16], data[17], data[18], data[19]);
240        let end: usize;
241        let options = if ihl == 5 {
242            end = 20;
243            None
244        } else {
245            end = ihl as usize * 4;
246            Some(data[20..end].to_vec())
247        };
248        if len < end {
249            return Err(IpParseErr::Size(len));
250        }
251        let payload: DataType = match proto {
252            6 => TcpPacket::parse_from_bytes(&&data[end..len])?.into(),
253            17 => UdpDatagram::parse_from_bytes(&&data[end..len])?.into(),
254            p => return Err(IpParseErr::Proto(p)),
255        };
256        Ok(IpPacket {
257            dscp,
258            ecn,
259            len,
260            id,
261            flags,
262            frag_off,
263            ttl,
264            checksum,
265            source,
266            dest,
267            options,
268            payload,
269            recv_time,
270        })
271    }
272
273    /// Write an IP packet to a pcap file
274    ///
275    /// # Errors
276    /// Returns an error if any of the writes fail.
277    pub fn pcap_write<W: Write>(&self, file: &mut W) -> std::io::Result<()> {
278        let diff = self
279            .recv_time
280            .duration_since(SystemTime::UNIX_EPOCH)
281            .unwrap();
282        let secs = diff.as_secs() as u32;
283        let nanos = diff.as_nanos() as u32;
284        file.write_all(&secs.to_ne_bytes())?;
285        file.write_all(&nanos.to_ne_bytes())?;
286        let size = self.len as u32;
287        file.write_all(&size.to_ne_bytes())?;
288        file.write_all(&size.to_ne_bytes())?;
289        let ihl = self.options.clone().map_or(0, |v| v.len() >> 2) as u8;
290        let version_ihl = 0x40 | (ihl + 5);
291        file.write_all(&version_ihl.to_be_bytes())?;
292        let dscp_ecn = (self.dscp << 2) | self.ecn;
293        file.write_all(&dscp_ecn.to_be_bytes())?;
294        let total_len = self.len as u16;
295        file.write_all(&total_len.to_be_bytes())?;
296        file.write_all(&self.id.to_be_bytes())?;
297        let frag_off = ((self.flags as u16) << 13) | self.frag_off;
298        file.write_all(&frag_off.to_be_bytes())?;
299        file.write_all(&self.ttl.to_be_bytes())?;
300        file.write_all(&self.payload.get_proto_num().to_be_bytes())?;
301        file.write_all(&self.checksum.to_be_bytes())?;
302        file.write_all(&self.source.octets())?;
303        file.write_all(&self.dest.octets())?;
304        if let Some(ref o) = self.options {
305            file.write_all(o)?;
306        }
307        self.payload.pcap_write(file)?;
308        Ok(())
309    }
310}
311
312impl fmt::Display for IpPacket {
313    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
314        f.debug_struct("IPv4 Header")
315            .field("Total length", &self.len)
316            .field("Source addr", &self.source)
317            .field("Dest addr", &self.dest)
318            .field("Payload", &self.payload)
319            .finish_non_exhaustive()
320    }
321}
322
323bitflags! {
324    /// TCP flag bits
325    ///
326    /// Represent the control bits in a TCP packet that help manage connection state
327    #[repr(transparent)]
328    pub struct TcpFlags: u16 {
329        /// ECN-nonce - concealment protection
330        const NS = 0b1_0000_0000;
331        /// Congestion window reduced
332        const CWR = 0b1000_0000;
333        /// ECN-Echo
334        ///
335        /// If the SYN flag is set, it means the TCP peer is ECN capable.
336        ///
337        /// If the SYN flag is clear, indicates that there is network congestion.
338        const ECE = 0b100_0000;
339        /// Indicates that the urget pointer field is set
340        const URG = 0b10_0000;
341        /// Indicates that the acknowledgement field of the header is significant
342        const ACK = 0b10000;
343        /// Indicates that there is data that needs to be forwarded to the application
344        const PSH = 0b1000;
345        /// Reset the connection
346        const RST = 0b100;
347        /// Synchronize sequence numbers
348        const SYN = 0b10;
349        /// Indicates that the packet is the last one from the sender
350        const FIN = 0b1;
351    }
352}
353
354/// TCP header and its associated data
355#[derive(Clone)]
356pub struct TcpPacket {
357    /// Source port
358    pub source: u16,
359    /// Destination port
360    pub dest: u16,
361    /// Sequence number
362    pub seq: u32,
363    /// Acknowledge number
364    pub ack: u32,
365    /// TCP flags such as reset, ack, push, etc.
366    pub flags: TcpFlags,
367    /// Window size
368    pub window: u16,
369    /// Checksum value
370    pub checksum: u16,
371    /// Urgent pointer
372    pub urg: u16,
373    /// Option data if there was any
374    pub option_data: Option<Vec<u8>>,
375    /// TCP data
376    pub data: Vec<u8>,
377}
378
379impl fmt::Debug for TcpPacket {
380    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
381        f.debug_struct("TCP Packet")
382            .field("Source Addr", &self.source)
383            .field("Dest Addr", &self.source)
384            .field("Data Len", &self.data)
385            .finish_non_exhaustive()
386    }
387}
388
389/// Errors that can occur when parsing a TCP packet
390#[derive(Error, Debug)]
391pub enum TcpParseErr {
392    /// Not enough bytes to be a TCP packet. Minimum of 20 bytes.
393    #[error("Data is too small to be a TCP packet {0}")]
394    Size(usize),
395    /// The value of the data offset field is too large for passed in data
396    #[error("Header has invalid size field {0}")]
397    InvalidSize(usize),
398}
399
400impl TcpPacket {
401    /// Parse a TCP packet and data out of a stream of bytes
402    ///
403    /// This method is meant to be called from the [`IpPacket::parse_from_bytes`] method.
404    ///
405    /// # Errors
406    /// Returns an error if there are not enough bytes for a valid TCP packet.
407    pub fn parse_from_bytes(data: &dyn AsRef<[u8]>) -> Result<Self, TcpParseErr> {
408        let data = data.as_ref();
409        if data.len() < 20 {
410            return Err(TcpParseErr::Size(data.len()));
411        }
412        let source = u16::from_be_bytes(data[0..2].try_into().unwrap());
413        let dest = u16::from_be_bytes(data[2..4].try_into().unwrap());
414        let seq = u32::from_be_bytes(data[4..8].try_into().unwrap());
415        let ack = u32::from_be_bytes(data[8..12].try_into().unwrap());
416        let data_off = data[12] >> 4;
417        if data_off as usize * 4 > data.len() {
418            return Err(TcpParseErr::InvalidSize(data_off as usize));
419        }
420        let mut flag_bits: u16 = u16::from(data[12] & 1) << 8;
421        flag_bits += u16::from(data[13]);
422        // SAFETY: These should have the same exact structure in memory.
423        // The high order bits from the packet that don't match up to flags are masked out
424        // so this should be completely fine.
425        let flags: TcpFlags = unsafe { transmute(flag_bits) };
426        let window = u16::from_be_bytes(data[14..16].try_into().unwrap());
427        let checksum = u16::from_be_bytes(data[16..18].try_into().unwrap());
428        let urg = u16::from_be_bytes(data[18..20].try_into().unwrap());
429        let option_data: Option<Vec<u8>> = if data_off > 5 {
430            Some(data[20..(data_off as usize) * 4].to_vec())
431        } else {
432            None
433        };
434        let data = data[(data_off as usize) * 4..].to_vec();
435        Ok(TcpPacket {
436            source,
437            dest,
438            seq,
439            ack,
440            flags,
441            window,
442            checksum,
443            urg,
444            option_data,
445            data,
446        })
447    }
448
449    /// Write packet to a pcap file
450    ///
451    /// Writes a tcp packet to a pcap file. This is for use by the [`write_pcap_file`] function.
452    ///
453    /// # Errors
454    /// Will return an error if any of the writes fail.
455    pub fn pcap_write<W: Write>(&self, file: &mut W) -> std::io::Result<()> {
456        file.write_all(&self.source.to_be_bytes())?;
457        file.write_all(&self.dest.to_be_bytes())?;
458        file.write_all(&self.seq.to_be_bytes())?;
459        file.write_all(&self.ack.to_be_bytes())?;
460        let mut data_off = self.option_data.clone().map_or(0_usize, |v| v.len() >> 2);
461        data_off += 5;
462        data_off <<= 4;
463        data_off |= (self.flags.bits >> 8) as usize;
464        let data_off_ns = data_off as u8;
465        file.write_all(&data_off_ns.to_be_bytes())?;
466        let options = (self.flags.bits & 0xFF) as u8;
467        file.write_all(&options.to_be_bytes())?;
468        file.write_all(&self.window.to_be_bytes())?;
469        file.write_all(&self.checksum.to_be_bytes())?;
470        file.write_all(&self.urg.to_be_bytes())?;
471        if let Some(ref v) = self.option_data {
472            file.write_all(v)?;
473        }
474        file.write_all(&self.data)?;
475        Ok(())
476    }
477}
478
479/// Errors that can happen while parsing a UDP packet
480#[derive(Debug, Error)]
481pub enum UdpParseErr {
482    /// Size in header is too large for passed in slice
483    #[error("Size in header did not match size of data")]
484    SizeMismatch,
485    /// The slice is less than 8 bytes, the minimum UDP packet size
486    #[error("Passed in data is not large enough for UDP header")]
487    MissingHeader,
488}
489
490/// UDP header and associated data
491pub struct UdpDatagram {
492    /// Source port
493    pub source: u16,
494    /// Destination port
495    pub dest: u16,
496    /// Packet checksum
497    pub checksum: u16,
498    /// UDP data
499    pub payload: Vec<u8>,
500}
501
502impl fmt::Debug for UdpDatagram {
503    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
504        f.debug_struct("UDP Packet")
505            .field("Source Port", &self.source)
506            .field("Dest Port", &self.dest)
507            .field("Data len", &self.payload.len())
508            .finish_non_exhaustive()
509    }
510}
511
512impl UdpDatagram {
513    /// Parse a udp packet from a slice of bytes
514    ///
515    /// This method will not check the checksum.
516    ///
517    /// # Errors
518    /// This method will return an error if the slice is less than 8 bytes or if the size of the length field is larger
519    /// than the length of the slice.
520    pub fn parse_from_bytes(data: &dyn AsRef<[u8]>) -> Result<Self, UdpParseErr> {
521        let data = data.as_ref();
522        if data.len() < 8 {
523            return Err(UdpParseErr::MissingHeader);
524        }
525        let source = u16::from_be_bytes(data[0..2].try_into().unwrap());
526        let dest = u16::from_be_bytes(data[2..4].try_into().unwrap());
527        let length = u16::from_be_bytes(data[4..6].try_into().unwrap()) as usize;
528        let checksum = u16::from_be_bytes(data[6..8].try_into().unwrap());
529        if !(8..data.len()).contains(&length) {
530            return Err(UdpParseErr::SizeMismatch);
531        }
532        let payload = data[8..length].to_vec();
533        Ok(Self {
534            source,
535            dest,
536            checksum,
537            payload,
538        })
539    }
540
541    /// Write packet to a pcap file
542    ///
543    /// # Errors
544    /// Will return any io errors that occur.
545    pub fn pcap_write<W: Write>(&self, f: &mut W) -> std::io::Result<()> {
546        f.write_all(&self.source.to_be_bytes())?;
547        f.write_all(&self.dest.to_be_bytes())?;
548        f.write_all(&self.payload.len().to_be_bytes())?;
549        f.write_all(&self.checksum.to_be_bytes())?;
550        f.write_all(&self.payload)?;
551        Ok(())
552    }
553}
554
555/// Write a packet capture out to a pcap file
556///
557/// It is recommended to pass a buf writer to this function as many small writes will be made while writing the file.
558///
559/// # Errors
560/// If any IO errors occur while writing the file, they will be returned.
561pub fn write_pcap_file<W: Write>(
562    packets: &Vec<IpPacket>,
563    pcap_file: &mut W,
564) -> std::io::Result<()> {
565    pcap_file.write_all(&PCAP_MAGIC.to_ne_bytes())?;
566    pcap_file.write_all(&2_u16.to_ne_bytes())?;
567    pcap_file.write_all(&4_u16.to_ne_bytes())?;
568    pcap_file.write_all(&0_u32.to_ne_bytes())?;
569    pcap_file.write_all(&0_u32.to_ne_bytes())?;
570    pcap_file.write_all(&0xFFFF_u32.to_ne_bytes())?;
571    pcap_file.write_all(&101_u32.to_ne_bytes())?;
572
573    for packet in packets {
574        packet.pcap_write(pcap_file)?;
575    }
576
577    pcap_file.flush()?;
578    Ok(())
579}
580
581/// Parse packets from a pcap file
582///
583/// Parses all of the IP/TCP packets from a pcap file. Any packet that isn't able to be parsed properly will just be
584/// skipped. This function assumes that the pcap file uses the same endianness as the system.
585///
586/// # Errors
587/// If the pcap file is not in the expected format, then an error will be returned.
588///
589/// # Panics
590/// This function calls unwrap on some slices to turn them into an array of a specific size. This should never fail
591/// because the slicing is for that specific size.
592pub fn read_pcap_file<R: Read>(pcap_file: &mut R) -> Result<Vec<IpPacket>, IpParseErr> {
593    let mut pcap_data = Vec::new();
594    let data_size = pcap_file.read_to_end(&mut pcap_data)?;
595    if data_size < 24 {
596        return Err(IpParseErr::PcapFileSize);
597    }
598    let magic = u32::from_ne_bytes(pcap_data[0..4].try_into().unwrap());
599    if magic == PCAP_BAD_ENDIANNESS {
600        return Err(IpParseErr::EndianError);
601    }
602    if magic != PCAP_MAGIC {
603        return Err(IpParseErr::BadMagic(magic));
604    }
605    // Check the major version field
606    if u16::from_ne_bytes(pcap_data[4..6].try_into().unwrap()) != 2 {
607        return Err(IpParseErr::PcapInvalid);
608    }
609    // Check the minor version field
610    if u16::from_ne_bytes(pcap_data[6..8].try_into().unwrap()) != 4 {
611        return Err(IpParseErr::PcapInvalid);
612    }
613    // Skip some fields that we don't care about
614    // Check to see if the pcap file is for raw IP packets
615    if u32::from_ne_bytes(pcap_data[20..24].try_into().unwrap()) != 101 {
616        return Err(IpParseErr::PcapInvalid);
617    }
618    let mut packets = Vec::new();
619    let mut index: usize = 24;
620    while index + 16 < data_size {
621        let seconds = u32::from_ne_bytes(pcap_data[index..index + 4].try_into().unwrap()) as u64;
622        let nanos = u32::from_ne_bytes(pcap_data[index + 4..index + 8].try_into().unwrap());
623        let time = Some(
624            SystemTime::UNIX_EPOCH
625                .checked_add(Duration::new(seconds, nanos))
626                .unwrap(),
627        );
628        let packet_size =
629            u32::from_ne_bytes(pcap_data[index + 8..index + 12].try_into().unwrap()) as usize;
630        if index + 16 + packet_size > data_size {
631            break;
632        }
633        let packet = IpPacket::parse_from_bytes(&&pcap_data[index + 16..][..packet_size], time);
634        if let Ok(p) = packet {
635            packets.push(p);
636        }
637        index += 16;
638        index += packet_size;
639    }
640    Ok(packets)
641}
642
643#[cfg(test)]
644mod ip_testing {
645    use super::*;
646    use std::io::Cursor;
647
648    const EMPTY_PACKET_BYTES: &[u8] = &[
649        0x45, 0x00, 0x00, 0x28, 0xde, 0xad, 0x00, 0x00, 0x00, 0x06, 0xbe, 0xef, 0x12, 0x34, 0x56,
650        0x78, 0xab, 0xcd, 0xef, 0x12, 0x43, 0x21, 0xfe, 0xdc, 0xde, 0xad, 0xbe, 0xef, 0x69, 0x69,
651        0x69, 0x69, 0x50, 0x00, 0x10, 0x00, 0x99, 0x99, 0x00, 0x00,
652    ];
653
654    #[test]
655    fn empty_packet() {
656        let packet = IpPacket::parse_from_bytes(&EMPTY_PACKET_BYTES, None).unwrap();
657        assert_eq!(packet.id, 0xdead);
658        assert_eq!(packet.checksum, 0xbeef);
659        assert_eq!(packet.len, 40);
660    }
661
662    #[test]
663    fn test_pcap_write() {
664        let packet = IpPacket::parse_from_bytes(&EMPTY_PACKET_BYTES, None).unwrap();
665        let mut file = Cursor::new(Vec::new());
666        packet.pcap_write(&mut file).unwrap();
667        let buffer = file.into_inner();
668        assert_eq!(buffer[8], 40);
669        assert_eq!(buffer[9], 0);
670        assert_eq!(buffer[10], 0);
671        assert_eq!(buffer[11], 0);
672        assert_eq!(buffer[12], 40);
673        assert_eq!(buffer[13], 0);
674        assert_eq!(buffer[14], 0);
675        assert_eq!(buffer[15], 0);
676        assert_eq!(&buffer[16..], EMPTY_PACKET_BYTES);
677    }
678
679    #[test]
680    fn test_read_pcap() {
681        use std::fs::File;
682        let mut file = File::open("./test/test.pcap").unwrap();
683        let packets = read_pcap_file(&mut file).unwrap();
684        assert!(packets.len() > 0);
685    }
686
687    #[test]
688    fn test_fuzz_crash_pcap_read() {
689        use std::fs::File;
690        let mut file =
691            File::open("test/test.pcap")
692                .unwrap();
693        let _data = read_pcap_file(&mut file);
694    }
695}