1use smallvec::SmallVec;
4
5use etherparse::Ipv4HeaderSlice;
6
7use super::ethernet::ethertype;
8use super::{FieldValue, ParseContext, ParseResult, Protocol, TunnelType};
9use crate::schema::{DataKind, FieldDescriptor};
10
11mod ip_in_ip_protocol {
13 pub const IPIP: u8 = 4;
15 pub const IPV6_IN_IP: u8 = 41;
17}
18
19#[derive(Debug, Clone, Copy)]
21pub struct Ipv4Protocol;
22
23impl Protocol for Ipv4Protocol {
24 fn name(&self) -> &'static str {
25 "ipv4"
26 }
27
28 fn display_name(&self) -> &'static str {
29 "IPv4"
30 }
31
32 fn can_parse(&self, context: &ParseContext) -> Option<u32> {
33 match context.hint("ethertype") {
34 Some(et) if et == ethertype::IPV4 as u64 => Some(100),
35 _ => None,
36 }
37 }
38
39 fn parse<'a>(&self, data: &'a [u8], _context: &ParseContext) -> ParseResult<'a> {
40 match Ipv4HeaderSlice::from_slice(data) {
41 Ok(ipv4) => {
42 let mut fields = SmallVec::new();
43
44 fields.push(("version", FieldValue::UInt8(4)));
45 fields.push(("ihl", FieldValue::UInt8(ipv4.ihl())));
46 fields.push(("dscp", FieldValue::UInt8(ipv4.dcp().value())));
47 fields.push(("ecn", FieldValue::UInt8(ipv4.ecn().value())));
48 fields.push(("total_length", FieldValue::UInt16(ipv4.total_len())));
49 fields.push(("identification", FieldValue::UInt16(ipv4.identification())));
50 fields.push(("dont_fragment", FieldValue::Bool(ipv4.dont_fragment())));
51 fields.push(("more_fragments", FieldValue::Bool(ipv4.more_fragments())));
52 fields.push((
53 "fragment_offset",
54 FieldValue::UInt16(ipv4.fragments_offset().value()),
55 ));
56 fields.push(("ttl", FieldValue::UInt8(ipv4.ttl())));
57 fields.push(("protocol", FieldValue::UInt8(ipv4.protocol().0)));
58 fields.push(("checksum", FieldValue::UInt16(ipv4.header_checksum())));
59 fields.push(("src_ip", FieldValue::ipv4(&ipv4.source())));
60 fields.push(("dst_ip", FieldValue::ipv4(&ipv4.destination())));
61
62 let mut child_hints = SmallVec::new();
63 let protocol = ipv4.protocol().0;
64 child_hints.push(("ip_protocol", protocol as u64));
65 child_hints.push(("ip_version", 4));
66
67 if protocol == ip_in_ip_protocol::IPIP {
69 child_hints.push(("tunnel_type", TunnelType::IpInIp as u64));
71 child_hints.push(("ethertype", ethertype::IPV4 as u64));
73 } else if protocol == ip_in_ip_protocol::IPV6_IN_IP {
74 child_hints.push(("tunnel_type", TunnelType::Ip6InIp as u64));
76 child_hints.push(("ethertype", ethertype::IPV6 as u64));
78 }
79
80 let header_len = ipv4.slice().len();
81 ParseResult::success(fields, &data[header_len..], child_hints)
82 }
83 Err(e) => ParseResult::error(format!("IPv4 parse error: {e}"), data),
84 }
85 }
86
87 fn schema_fields(&self) -> Vec<FieldDescriptor> {
88 vec![
89 FieldDescriptor::new("ipv4.version", DataKind::UInt8).set_nullable(true),
90 FieldDescriptor::new("ipv4.ihl", DataKind::UInt8).set_nullable(true),
91 FieldDescriptor::new("ipv4.dscp", DataKind::UInt8).set_nullable(true),
92 FieldDescriptor::new("ipv4.ecn", DataKind::UInt8).set_nullable(true),
93 FieldDescriptor::new("ipv4.total_length", DataKind::UInt16).set_nullable(true),
94 FieldDescriptor::new("ipv4.identification", DataKind::UInt16).set_nullable(true),
95 FieldDescriptor::new("ipv4.dont_fragment", DataKind::Bool).set_nullable(true),
96 FieldDescriptor::new("ipv4.more_fragments", DataKind::Bool).set_nullable(true),
97 FieldDescriptor::new("ipv4.fragment_offset", DataKind::UInt16).set_nullable(true),
98 FieldDescriptor::new("ipv4.ttl", DataKind::UInt8).set_nullable(true),
99 FieldDescriptor::new("ipv4.protocol", DataKind::UInt8).set_nullable(true),
100 FieldDescriptor::new("ipv4.checksum", DataKind::UInt16).set_nullable(true),
101 FieldDescriptor::new("ipv4.src_ip", DataKind::String).set_nullable(true),
102 FieldDescriptor::new("ipv4.dst_ip", DataKind::String).set_nullable(true),
103 ]
104 }
105
106 fn child_protocols(&self) -> &[&'static str] {
107 &["tcp", "udp", "icmp"]
108 }
109
110 fn dependencies(&self) -> &'static [&'static str] {
111 &["ethernet", "vlan", "mpls", "gre", "vxlan", "gtp"]
112 }
113}
114
115#[cfg(test)]
116mod tests {
117 use super::*;
118 use std::net::IpAddr;
119
120 #[test]
121 fn test_parse_ipv4() {
122 let header = [
124 0x45, 0x00, 0x00, 0x28, 0x00, 0x01, 0x00, 0x00, 0x40, 0x06, 0x00, 0x00, 0xc0, 0xa8, 0x01, 0x01, 0xc0, 0xa8, 0x01, 0x02, ];
135
136 let parser = Ipv4Protocol;
137 let mut context = ParseContext::new(1);
138 context.insert_hint("ethertype", ethertype::IPV4 as u64);
139
140 let result = parser.parse(&header, &context);
141
142 assert!(result.is_ok());
143 assert_eq!(result.get("ttl"), Some(&FieldValue::UInt8(64)));
144 assert_eq!(result.get("protocol"), Some(&FieldValue::UInt8(6)));
145 assert_eq!(result.hint("ip_protocol"), Some(6u64));
146 }
147
148 #[test]
149 fn test_parse_ipv4_udp() {
150 let header = [
151 0x45, 0x00, 0x00, 0x1c, 0x12, 0x34, 0x40, 0x00, 0x80, 0x11, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x01, 0x0a, 0x00, 0x00, 0x02, ];
162
163 let parser = Ipv4Protocol;
164 let mut context = ParseContext::new(1);
165 context.insert_hint("ethertype", ethertype::IPV4 as u64);
166
167 let result = parser.parse(&header, &context);
168
169 assert!(result.is_ok());
170 assert_eq!(result.get("ttl"), Some(&FieldValue::UInt8(128)));
171 assert_eq!(result.get("protocol"), Some(&FieldValue::UInt8(17)));
172 assert_eq!(result.get("dont_fragment"), Some(&FieldValue::Bool(true)));
173 assert_eq!(result.hint("ip_protocol"), Some(17u64));
174 }
175
176 #[test]
177 fn test_parse_ipv4_icmp() {
178 let header = [
179 0x45, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0xc0, 0xa8, 0x01, 0x01, ];
190
191 let parser = Ipv4Protocol;
192 let mut context = ParseContext::new(1);
193 context.insert_hint("ethertype", ethertype::IPV4 as u64);
194
195 let result = parser.parse(&header, &context);
196
197 assert!(result.is_ok());
198 assert_eq!(result.get("protocol"), Some(&FieldValue::UInt8(1)));
199 assert_eq!(result.hint("ip_protocol"), Some(1u64));
200 }
201
202 #[test]
203 fn test_parse_ipv4_with_payload() {
204 let packet = [
205 0x45, 0x00, 0x00, 0x28, 0x00, 0x01, 0x00, 0x00, 0x40, 0x06, 0x00, 0x00, 0xc0, 0xa8, 0x01, 0x01, 0xc0, 0xa8, 0x01, 0x02, 0x00, 0x50, 0x1f, 0x90, 0x00, 0x00, 0x00, 0x01,
217 ];
218
219 let parser = Ipv4Protocol;
220 let mut context = ParseContext::new(1);
221 context.insert_hint("ethertype", ethertype::IPV4 as u64);
222
223 let result = parser.parse(&packet, &context);
224
225 assert!(result.is_ok());
226 assert_eq!(result.remaining.len(), 8); }
228
229 #[test]
230 fn test_can_parse_ipv4() {
231 let parser = Ipv4Protocol;
232
233 let ctx1 = ParseContext::new(1);
235 assert!(parser.can_parse(&ctx1).is_none());
236
237 let mut ctx2 = ParseContext::new(1);
239 ctx2.insert_hint("ethertype", ethertype::IPV6 as u64);
240 assert!(parser.can_parse(&ctx2).is_none());
241
242 let mut ctx3 = ParseContext::new(1);
244 ctx3.insert_hint("ethertype", ethertype::IPV4 as u64);
245 assert!(parser.can_parse(&ctx3).is_some());
246 }
247
248 #[test]
249 fn test_parse_ipv4_too_short() {
250 let short_header = [0x45, 0x00, 0x00, 0x28]; let parser = Ipv4Protocol;
253 let mut context = ParseContext::new(1);
254 context.insert_hint("ethertype", ethertype::IPV4 as u64);
255
256 let result = parser.parse(&short_header, &context);
257
258 assert!(!result.is_ok());
259 assert!(result.error.is_some());
260 }
261
262 #[test]
263 fn test_ipv4_ip_addresses() {
264 let header = [
265 0x45, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x40, 0x06, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x01, 0x0a, 0x0b, 0x0c, 0x0d, ];
271
272 let parser = Ipv4Protocol;
273 let mut context = ParseContext::new(1);
274 context.insert_hint("ethertype", ethertype::IPV4 as u64);
275
276 let result = parser.parse(&header, &context);
277
278 assert!(result.is_ok());
279 assert_eq!(
280 result.get("src_ip"),
281 Some(&FieldValue::IpAddr(IpAddr::V4(
282 "127.0.0.1".parse().unwrap()
283 )))
284 );
285 assert_eq!(
286 result.get("dst_ip"),
287 Some(&FieldValue::IpAddr(IpAddr::V4(
288 "10.11.12.13".parse().unwrap()
289 )))
290 );
291 }
292
293 #[test]
294 fn test_ipv4_fragment_flags() {
295 let header = [
296 0x45, 0x00, 0x00, 0x14, 0x12, 0x34, 0x20, 0x00, 0x40, 0x06, 0x00, 0x00, 0xc0, 0xa8, 0x01, 0x01, 0xc0, 0xa8, 0x01, 0x02, ];
303
304 let parser = Ipv4Protocol;
305 let mut context = ParseContext::new(1);
306 context.insert_hint("ethertype", ethertype::IPV4 as u64);
307
308 let result = parser.parse(&header, &context);
309
310 assert!(result.is_ok());
311 assert_eq!(result.get("more_fragments"), Some(&FieldValue::Bool(true)));
312 assert_eq!(result.get("dont_fragment"), Some(&FieldValue::Bool(false)));
313 assert_eq!(
314 result.get("identification"),
315 Some(&FieldValue::UInt16(0x1234))
316 );
317 }
318
319 #[test]
320 fn test_ipv4_child_hints() {
321 let header = [
322 0x45, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x40, 0x06, 0x00,
323 0x00, 0xc0, 0xa8, 0x01, 0x01, 0xc0, 0xa8, 0x01, 0x02,
325 ];
326
327 let parser = Ipv4Protocol;
328 let mut context = ParseContext::new(1);
329 context.insert_hint("ethertype", ethertype::IPV4 as u64);
330
331 let result = parser.parse(&header, &context);
332
333 assert!(result.is_ok());
334 assert_eq!(result.hint("ip_protocol"), Some(6u64));
335 assert_eq!(result.hint("ip_version"), Some(4u64));
336 }
337}