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!("{}.{}.{}.{}", self.src[0], self.src[1], self.src[2], self.src[3])
35 }
36 pub fn dst_str(&self) -> String {
37 format!("{}.{}.{}.{}", self.dst[0], self.dst[1], self.dst[2], self.dst[3])
38 }
39}
40
41pub fn strip_ethernet(input: &[u8]) -> Option<&[u8]> {
49 if input.len() < 14 {
50 return None;
51 }
52
53 let ethertype = u16::from_be_bytes([input[12], input[13]]);
54 match ethertype {
55 e if e == ETHERTYPE_IP => Some(&input[14..]),
56
57 e if e == ETHERTYPE_8021Q => {
58 if input.len() < 18 {
60 return None;
61 }
62 let inner_et = u16::from_be_bytes([input[16], input[17]]);
63 if inner_et == ETHERTYPE_IP {
64 Some(&input[18..])
65 } else {
66 None
67 }
68 }
69
70 e if e == ETHERTYPE_8021AD => {
71 if input.len() < 22 {
74 return None;
75 }
76 let mid_et = u16::from_be_bytes([input[16], input[17]]);
77 if mid_et != ETHERTYPE_8021Q {
78 return None;
79 }
80 let inner_et = u16::from_be_bytes([input[20], input[21]]);
81 if inner_et == ETHERTYPE_IP {
82 Some(&input[22..])
83 } else {
84 None
85 }
86 }
87
88 _ => None,
89 }
90}
91
92fn parse_ip_header(input: &[u8]) -> IResult<&[u8], (IpHeader, &[u8])> {
93 let (input, ver_ihl) = be_u8(input)?;
94 let ihl = ((ver_ihl & 0x0f) * 4) as usize;
95
96 if ihl < 20 {
97 return Err(nom::Err::Error(nom::error::Error::new(input, nom::error::ErrorKind::Verify)));
98 }
99
100 let (input, _dscp_ecn) = be_u8(input)?;
101 let (input, total_len) = be_u16(input)?;
102 let (input, _ident) = be_u16(input)?;
103 let (input, _flags_frag) = be_u16(input)?;
104 let (input, ttl) = be_u8(input)?;
105 let (input, protocol) = be_u8(input)?;
106 let (input, _checksum) = be_u16(input)?;
107 let (input, src_raw) = take(4usize)(input)?;
108 let (input, dst_raw) = take(4usize)(input)?;
109
110 let src = [src_raw[0], src_raw[1], src_raw[2], src_raw[3]];
111 let dst = [dst_raw[0], dst_raw[1], dst_raw[2], dst_raw[3]];
112
113 let options_len = ihl - 20;
115 let (input, _) = take(options_len)(input)?;
116
117 let payload_len = (total_len as usize).saturating_sub(ihl);
118 let (input, payload) = take(payload_len)(input)?;
119
120 Ok((input, (IpHeader { src, dst, protocol, ttl }, payload)))
121}
122
123pub fn extract_ip(frame: &[u8]) -> Option<(IpHeader, &[u8])> {
126 let ip_data = strip_ethernet(frame)?;
127 parse_ip_header(ip_data).ok().map(|(_, result)| result)
128}
129
130pub fn extract_udp(payload: &[u8]) -> Option<(u16, u16, &[u8])> {
133 if payload.len() < 8 {
134 return None;
135 }
136 let src_port = u16::from_be_bytes([payload[0], payload[1]]);
137 let dst_port = u16::from_be_bytes([payload[2], payload[3]]);
138 let length = u16::from_be_bytes([payload[4], payload[5]]) as usize;
139 if length < 8 || length > payload.len() {
140 return None;
141 }
142 Some((src_port, dst_port, &payload[8..length]))
143}
144
145pub struct TcpSegment<'a> {
147 pub src_port: u16,
148 pub dst_port: u16,
149 pub payload: &'a [u8],
150}
151
152pub fn extract_tcp(payload: &[u8]) -> Option<TcpSegment<'_>> {
156 if payload.len() < 20 {
157 return None;
158 }
159 let src_port = u16::from_be_bytes([payload[0], payload[1]]);
160 let dst_port = u16::from_be_bytes([payload[2], payload[3]]);
161 let data_off = ((payload[12] >> 4) as usize) * 4;
163 if data_off < 20 || data_off > payload.len() {
164 return None;
165 }
166 let tcp_payload = &payload[data_off..];
167 Some(TcpSegment { src_port, dst_port, payload: tcp_payload })
168}
169
170#[cfg(test)]
171mod tests {
172 use super::*;
173
174 fn build_ip_frame(ethertype_prefix: &[u8], protocol: u8, payload: &[u8]) -> Vec<u8> {
175 let mut frame = Vec::new();
176 frame.extend_from_slice(&[0u8; 12]); frame.extend_from_slice(ethertype_prefix);
178
179 let total_len = 20 + payload.len();
181 frame.push(0x45); frame.push(0); frame.extend_from_slice(&(total_len as u16).to_be_bytes());
184 frame.extend_from_slice(&0u16.to_be_bytes()); frame.extend_from_slice(&0u16.to_be_bytes()); frame.push(64); frame.push(protocol);
188 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);
192 frame
193 }
194
195 fn build_udp_payload(src_port: u16, dst_port: u16, data: &[u8]) -> Vec<u8> {
196 let mut buf = Vec::new();
197 buf.extend_from_slice(&src_port.to_be_bytes());
198 buf.extend_from_slice(&dst_port.to_be_bytes());
199 buf.extend_from_slice(&((8 + data.len()) as u16).to_be_bytes());
200 buf.extend_from_slice(&0u16.to_be_bytes()); buf.extend_from_slice(data);
202 buf
203 }
204
205 #[test]
206 fn strips_untagged_ethernet() {
207 let frame = build_ip_frame(ÐERTYPE_IP.to_be_bytes(), PROTO_UDP, &[1, 2, 3]);
208 let ip = strip_ethernet(&frame).unwrap();
209 assert_eq!(ip[9], PROTO_UDP); }
211
212 #[test]
213 fn strips_single_8021q_tag() {
214 let mut prefix = ETHERTYPE_8021Q.to_be_bytes().to_vec();
215 prefix.extend_from_slice(&0x0064u16.to_be_bytes()); prefix.extend_from_slice(ÐERTYPE_IP.to_be_bytes());
217 let frame = build_ip_frame(&prefix, PROTO_UDP, &[1, 2, 3]);
218 assert!(strip_ethernet(&frame).is_some());
219 }
220
221 #[test]
222 fn strips_double_tagged_qinq_frame() {
223 let mut prefix = ETHERTYPE_8021AD.to_be_bytes().to_vec();
224 prefix.extend_from_slice(&0x002Au16.to_be_bytes()); prefix.extend_from_slice(ÐERTYPE_8021Q.to_be_bytes());
226 prefix.extend_from_slice(&0x0064u16.to_be_bytes()); prefix.extend_from_slice(ÐERTYPE_IP.to_be_bytes());
228 let frame = build_ip_frame(&prefix, PROTO_UDP, &[1, 2, 3]);
229 assert!(strip_ethernet(&frame).is_some());
230 }
231
232 #[test]
233 fn rejects_non_ip_ethertype() {
234 let frame = build_ip_frame(&0x86DDu16.to_be_bytes(), PROTO_UDP, &[1, 2, 3]); assert!(strip_ethernet(&frame).is_none());
236 }
237
238 #[test]
239 fn extracts_ip_and_udp_end_to_end() {
240 let udp_data = b"hello";
241 let udp = build_udp_payload(1234, 53, udp_data);
242 let frame = build_ip_frame(ÐERTYPE_IP.to_be_bytes(), PROTO_UDP, &udp);
243
244 let (ip_header, ip_payload) = extract_ip(&frame).unwrap();
245 assert_eq!(ip_header.protocol, PROTO_UDP);
246 assert_eq!(ip_header.src_str(), "192.0.2.1");
247 assert_eq!(ip_header.dst_str(), "192.0.2.2");
248
249 let (src_port, dst_port, payload) = extract_udp(ip_payload).unwrap();
250 assert_eq!(src_port, 1234);
251 assert_eq!(dst_port, 53);
252 assert_eq!(payload, udp_data);
253 }
254
255 #[test]
256 fn extracts_tcp_skipping_options() {
257 let mut tcp = Vec::new();
258 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");
269
270 let seg = extract_tcp(&tcp).unwrap();
271 assert_eq!(seg.src_port, 4321);
272 assert_eq!(seg.dst_port, 80);
273 assert_eq!(seg.payload, b"payload");
274 }
275
276 #[test]
277 fn rejects_truncated_udp() {
278 assert!(extract_udp(&[0u8; 4]).is_none());
279 }
280
281 #[test]
282 fn rejects_truncated_tcp() {
283 assert!(extract_tcp(&[0u8; 10]).is_none());
284 }
285}