1use nom::{
9 bytes::complete::take,
10 number::complete::{be_u16, be_u8},
11 IResult,
12};
13
14pub const PROTO_UDP: u8 = 17;
15pub const PROTO_TCP: u8 = 6;
16
17pub const ETHERTYPE_IP: u16 = 0x0800;
18pub const ETHERTYPE_8021Q: u16 = 0x8100;
20pub const ETHERTYPE_8021AD: u16 = 0x88a8;
23
24#[derive(Debug, Clone)]
25pub struct IpHeader {
26 pub src: [u8; 4],
27 pub dst: [u8; 4],
28 pub protocol: u8,
29 pub ttl: u8,
30}
31
32impl IpHeader {
33 pub fn src_str(&self) -> String {
34 format!(
35 "{}.{}.{}.{}",
36 self.src[0], self.src[1], self.src[2], self.src[3]
37 )
38 }
39 pub fn dst_str(&self) -> String {
40 format!(
41 "{}.{}.{}.{}",
42 self.dst[0], self.dst[1], self.dst[2], self.dst[3]
43 )
44 }
45}
46
47pub fn strip_ethernet(input: &[u8]) -> Option<&[u8]> {
55 if input.len() < 14 {
56 return None;
57 }
58
59 let ethertype = u16::from_be_bytes([input[12], input[13]]);
60 match ethertype {
61 e if e == ETHERTYPE_IP => Some(&input[14..]),
62
63 e if e == ETHERTYPE_8021Q => {
64 if input.len() < 18 {
66 return None;
67 }
68 let inner_et = u16::from_be_bytes([input[16], input[17]]);
69 if inner_et == ETHERTYPE_IP {
70 Some(&input[18..])
71 } else {
72 None
73 }
74 }
75
76 e if e == ETHERTYPE_8021AD => {
77 if input.len() < 22 {
80 return None;
81 }
82 let mid_et = u16::from_be_bytes([input[16], input[17]]);
83 if mid_et != ETHERTYPE_8021Q {
84 return None;
85 }
86 let inner_et = u16::from_be_bytes([input[20], input[21]]);
87 if inner_et == ETHERTYPE_IP {
88 Some(&input[22..])
89 } else {
90 None
91 }
92 }
93
94 _ => None,
95 }
96}
97
98fn parse_ip_header(input: &[u8]) -> IResult<&[u8], (IpHeader, &[u8])> {
99 let (input, ver_ihl) = be_u8(input)?;
100 let ihl = ((ver_ihl & 0x0f) * 4) as usize;
101
102 if ihl < 20 {
103 return Err(nom::Err::Error(nom::error::Error::new(
104 input,
105 nom::error::ErrorKind::Verify,
106 )));
107 }
108
109 let (input, _dscp_ecn) = be_u8(input)?;
110 let (input, total_len) = be_u16(input)?;
111 let (input, _ident) = be_u16(input)?;
112 let (input, _flags_frag) = be_u16(input)?;
113 let (input, ttl) = be_u8(input)?;
114 let (input, protocol) = be_u8(input)?;
115 let (input, _checksum) = be_u16(input)?;
116 let (input, src_raw) = take(4usize)(input)?;
117 let (input, dst_raw) = take(4usize)(input)?;
118
119 let src = [src_raw[0], src_raw[1], src_raw[2], src_raw[3]];
120 let dst = [dst_raw[0], dst_raw[1], dst_raw[2], dst_raw[3]];
121
122 let options_len = ihl - 20;
124 let (input, _) = take(options_len)(input)?;
125
126 let payload_len = (total_len as usize).saturating_sub(ihl);
127 let (input, payload) = take(payload_len)(input)?;
128
129 Ok((
130 input,
131 (
132 IpHeader {
133 src,
134 dst,
135 protocol,
136 ttl,
137 },
138 payload,
139 ),
140 ))
141}
142
143pub fn extract_ip(frame: &[u8]) -> Option<(IpHeader, &[u8])> {
146 let ip_data = strip_ethernet(frame)?;
147 parse_ip_header(ip_data).ok().map(|(_, result)| result)
148}
149
150pub fn extract_udp(payload: &[u8]) -> Option<(u16, u16, &[u8])> {
153 if payload.len() < 8 {
154 return None;
155 }
156 let src_port = u16::from_be_bytes([payload[0], payload[1]]);
157 let dst_port = u16::from_be_bytes([payload[2], payload[3]]);
158 let length = u16::from_be_bytes([payload[4], payload[5]]) as usize;
159 if length < 8 || length > payload.len() {
160 return None;
161 }
162 Some((src_port, dst_port, &payload[8..length]))
163}
164
165pub struct TcpSegment<'a> {
167 pub src_port: u16,
168 pub dst_port: u16,
169 pub payload: &'a [u8],
170}
171
172pub fn extract_tcp(payload: &[u8]) -> Option<TcpSegment<'_>> {
176 if payload.len() < 20 {
177 return None;
178 }
179 let src_port = u16::from_be_bytes([payload[0], payload[1]]);
180 let dst_port = u16::from_be_bytes([payload[2], payload[3]]);
181 let data_off = ((payload[12] >> 4) as usize) * 4;
183 if data_off < 20 || data_off > payload.len() {
184 return None;
185 }
186 let tcp_payload = &payload[data_off..];
187 Some(TcpSegment {
188 src_port,
189 dst_port,
190 payload: tcp_payload,
191 })
192}
193
194#[cfg(test)]
195mod tests {
196 use super::*;
197
198 fn build_ip_frame(ethertype_prefix: &[u8], protocol: u8, payload: &[u8]) -> Vec<u8> {
199 let mut frame = Vec::new();
200 frame.extend_from_slice(&[0u8; 12]); frame.extend_from_slice(ethertype_prefix);
202
203 let total_len = 20 + payload.len();
205 frame.push(0x45); frame.push(0); frame.extend_from_slice(&(total_len as u16).to_be_bytes());
208 frame.extend_from_slice(&0u16.to_be_bytes()); frame.extend_from_slice(&0u16.to_be_bytes()); frame.push(64); frame.push(protocol);
212 frame.extend_from_slice(&0u16.to_be_bytes()); frame.extend_from_slice(&[192, 0, 2, 1]); frame.extend_from_slice(&[192, 0, 2, 2]); frame.extend_from_slice(payload);
216 frame
217 }
218
219 fn build_udp_payload(src_port: u16, dst_port: u16, data: &[u8]) -> Vec<u8> {
220 let mut buf = Vec::new();
221 buf.extend_from_slice(&src_port.to_be_bytes());
222 buf.extend_from_slice(&dst_port.to_be_bytes());
223 buf.extend_from_slice(&((8 + data.len()) as u16).to_be_bytes());
224 buf.extend_from_slice(&0u16.to_be_bytes()); buf.extend_from_slice(data);
226 buf
227 }
228
229 #[test]
230 fn strips_untagged_ethernet() {
231 let frame = build_ip_frame(ÐERTYPE_IP.to_be_bytes(), PROTO_UDP, &[1, 2, 3]);
232 let ip = strip_ethernet(&frame).unwrap();
233 assert_eq!(ip[9], PROTO_UDP); }
235
236 #[test]
237 fn strips_single_8021q_tag() {
238 let mut prefix = ETHERTYPE_8021Q.to_be_bytes().to_vec();
239 prefix.extend_from_slice(&0x0064u16.to_be_bytes()); prefix.extend_from_slice(ÐERTYPE_IP.to_be_bytes());
241 let frame = build_ip_frame(&prefix, PROTO_UDP, &[1, 2, 3]);
242 assert!(strip_ethernet(&frame).is_some());
243 }
244
245 #[test]
246 fn strips_double_tagged_qinq_frame() {
247 let mut prefix = ETHERTYPE_8021AD.to_be_bytes().to_vec();
248 prefix.extend_from_slice(&0x002Au16.to_be_bytes()); prefix.extend_from_slice(ÐERTYPE_8021Q.to_be_bytes());
250 prefix.extend_from_slice(&0x0064u16.to_be_bytes()); prefix.extend_from_slice(ÐERTYPE_IP.to_be_bytes());
252 let frame = build_ip_frame(&prefix, PROTO_UDP, &[1, 2, 3]);
253 assert!(strip_ethernet(&frame).is_some());
254 }
255
256 #[test]
257 fn rejects_non_ip_ethertype() {
258 let frame = build_ip_frame(&0x86DDu16.to_be_bytes(), PROTO_UDP, &[1, 2, 3]); assert!(strip_ethernet(&frame).is_none());
260 }
261
262 #[test]
263 fn extracts_ip_and_udp_end_to_end() {
264 let udp_data = b"hello";
265 let udp = build_udp_payload(1234, 53, udp_data);
266 let frame = build_ip_frame(ÐERTYPE_IP.to_be_bytes(), PROTO_UDP, &udp);
267
268 let (ip_header, ip_payload) = extract_ip(&frame).unwrap();
269 assert_eq!(ip_header.protocol, PROTO_UDP);
270 assert_eq!(ip_header.src_str(), "192.0.2.1");
271 assert_eq!(ip_header.dst_str(), "192.0.2.2");
272
273 let (src_port, dst_port, payload) = extract_udp(ip_payload).unwrap();
274 assert_eq!(src_port, 1234);
275 assert_eq!(dst_port, 53);
276 assert_eq!(payload, udp_data);
277 }
278
279 #[test]
280 fn extracts_tcp_skipping_options() {
281 let mut tcp = Vec::new();
282 tcp.extend_from_slice(&4321u16.to_be_bytes()); tcp.extend_from_slice(&80u16.to_be_bytes()); tcp.extend_from_slice(&0u32.to_be_bytes()); tcp.extend_from_slice(&0u32.to_be_bytes()); tcp.push(0x70); tcp.push(0x02); tcp.extend_from_slice(&0u16.to_be_bytes()); tcp.extend_from_slice(&0u16.to_be_bytes()); tcp.extend_from_slice(&0u16.to_be_bytes()); tcp.extend_from_slice(&[0u8; 8]); tcp.extend_from_slice(b"payload");
293
294 let seg = extract_tcp(&tcp).unwrap();
295 assert_eq!(seg.src_port, 4321);
296 assert_eq!(seg.dst_port, 80);
297 assert_eq!(seg.payload, b"payload");
298 }
299
300 #[test]
301 fn rejects_truncated_udp() {
302 assert!(extract_udp(&[0u8; 4]).is_none());
303 }
304
305 #[test]
306 fn rejects_truncated_tcp() {
307 assert!(extract_tcp(&[0u8; 10]).is_none());
308 }
309}