pcap_frame_parser/
pcap.rs1use nom::{
10 bytes::complete::take,
11 number::complete::{be_u32, le_u16, le_u32},
12 IResult,
13};
14
15const MAGIC_LE: u32 = 0xa1b2c3d4; const MAGIC_BE: u32 = 0xd4c3b2a1; #[derive(Debug, Clone)]
21pub struct PcapHeader {
22 pub magic: u32,
23 pub version_major: u16,
24 pub version_minor: u16,
25 pub snaplen: u32,
26 pub network: u32,
29 pub big_endian: bool,
30}
31
32#[derive(Debug, Clone)]
34pub struct RawPacket<'a> {
35 pub ts_sec: u32,
36 pub ts_usec: u32,
37 pub data: &'a [u8],
38}
39
40pub fn parse_pcap_header(input: &[u8]) -> IResult<&[u8], PcapHeader> {
42 let (input, magic) = le_u32(input)?;
43
44 let big_endian = match magic {
45 MAGIC_LE => false,
46 MAGIC_BE => true,
47 _ => {
48 return Err(nom::Err::Error(nom::error::Error::new(
49 input,
50 nom::error::ErrorKind::Tag,
51 )))
52 }
53 };
54
55 if big_endian {
56 let (input, version_major) = nom::number::complete::be_u16(input)?;
57 let (input, version_minor) = nom::number::complete::be_u16(input)?;
58 let (input, _thiszone) = be_u32(input)?;
59 let (input, _sigfigs) = be_u32(input)?;
60 let (input, snaplen) = be_u32(input)?;
61 let (input, network) = be_u32(input)?;
62 Ok((
63 input,
64 PcapHeader { magic, version_major, version_minor, snaplen, network, big_endian },
65 ))
66 } else {
67 let (input, version_major) = le_u16(input)?;
68 let (input, version_minor) = le_u16(input)?;
69 let (input, _thiszone) = le_u32(input)?;
70 let (input, _sigfigs) = le_u32(input)?;
71 let (input, snaplen) = le_u32(input)?;
72 let (input, network) = le_u32(input)?;
73 Ok((
74 input,
75 PcapHeader { magic, version_major, version_minor, snaplen, network, big_endian },
76 ))
77 }
78}
79
80pub fn parse_packet(input: &[u8], big_endian: bool) -> IResult<&[u8], RawPacket<'_>> {
82 let (input, ts_sec) = if big_endian { be_u32(input)? } else { le_u32(input)? };
83 let (input, ts_usec) = if big_endian { be_u32(input)? } else { le_u32(input)? };
84 let (input, incl_len) = if big_endian { be_u32(input)? } else { le_u32(input)? };
85 let (input, _orig_len) = if big_endian { be_u32(input)? } else { le_u32(input)? };
86 let (input, data) = take(incl_len)(input)?;
87
88 Ok((input, RawPacket { ts_sec, ts_usec, data }))
89}
90
91pub fn iter_packets(mut input: &[u8]) -> Result<(PcapHeader, Vec<RawPacket<'_>>), String> {
100 let (rest, header) =
101 parse_pcap_header(input).map_err(|e| format!("failed to parse PCAP header: {e:?}"))?;
102
103 if header.network != 1 {
104 return Err(format!(
105 "unsupported link type: {} (only Ethernet/1 is supported)",
106 header.network
107 ));
108 }
109
110 input = rest;
111 let mut packets = Vec::new();
112
113 while !input.is_empty() {
114 match parse_packet(input, header.big_endian) {
115 Ok((rest, pkt)) => {
116 packets.push(pkt);
117 input = rest;
118 }
119 Err(_) => break,
120 }
121 }
122
123 Ok((header, packets))
124}
125
126#[cfg(test)]
127mod tests {
128 use super::*;
129
130 fn le_u32_bytes(v: u32) -> [u8; 4] {
131 v.to_le_bytes()
132 }
133 fn le_u16_bytes(v: u16) -> [u8; 2] {
134 v.to_le_bytes()
135 }
136
137 fn build_pcap_le(frames: &[&[u8]]) -> Vec<u8> {
139 let mut buf = Vec::new();
140 buf.extend_from_slice(&le_u32_bytes(MAGIC_LE));
141 buf.extend_from_slice(&le_u16_bytes(2)); buf.extend_from_slice(&le_u16_bytes(4)); buf.extend_from_slice(&le_u32_bytes(0)); buf.extend_from_slice(&le_u32_bytes(0)); buf.extend_from_slice(&le_u32_bytes(65535)); buf.extend_from_slice(&le_u32_bytes(1)); for (i, frame) in frames.iter().enumerate() {
149 buf.extend_from_slice(&le_u32_bytes(1_700_000_000 + i as u32)); buf.extend_from_slice(&le_u32_bytes(0)); buf.extend_from_slice(&le_u32_bytes(frame.len() as u32)); buf.extend_from_slice(&le_u32_bytes(frame.len() as u32)); buf.extend_from_slice(frame);
154 }
155 buf
156 }
157
158 #[test]
159 fn parses_header_little_endian() {
160 let file = build_pcap_le(&[]);
161 let (_, header) = parse_pcap_header(&file).unwrap();
162 assert!(!header.big_endian);
163 assert_eq!(header.network, 1);
164 assert_eq!(header.snaplen, 65535);
165 }
166
167 #[test]
168 fn rejects_bad_magic() {
169 let mut file = build_pcap_le(&[]);
170 file[0] = 0xff;
171 assert!(parse_pcap_header(&file).is_err());
172 }
173
174 #[test]
175 fn iterates_multiple_packets() {
176 let frame_a = [0xAAu8; 20];
177 let frame_b = [0xBBu8; 34];
178 let file = build_pcap_le(&[&frame_a, &frame_b]);
179
180 let (header, packets) = iter_packets(&file).unwrap();
181 assert_eq!(header.network, 1);
182 assert_eq!(packets.len(), 2);
183 assert_eq!(packets[0].data, &frame_a[..]);
184 assert_eq!(packets[1].data, &frame_b[..]);
185 }
186
187 #[test]
188 fn rejects_non_ethernet_linktype() {
189 let mut file = build_pcap_le(&[]);
190 file[20..24].copy_from_slice(&le_u32_bytes(101));
193 assert!(iter_packets(&file).is_err());
194 }
195
196 #[test]
197 fn truncated_trailing_record_is_dropped_not_fatal() {
198 let frame_a = [0xCCu8; 16];
199 let mut file = build_pcap_le(&[&frame_a]);
200 file.extend_from_slice(&[0u8; 5]);
202
203 let (_, packets) = iter_packets(&file).unwrap();
204 assert_eq!(packets.len(), 1);
205 }
206}