Skip to main content

packet_dissector_ipv4/
lib.rs

1//! IPv4 (Internet Protocol version 4) dissector.
2//!
3//! ## References
4//! - RFC 791: <https://www.rfc-editor.org/rfc/rfc791>
5//! - RFC 2474 (DSCP, updates RFC 791 ToS field): <https://www.rfc-editor.org/rfc/rfc2474>
6//! - RFC 3168 (ECN): <https://www.rfc-editor.org/rfc/rfc3168>
7//! - RFC 6864 (updates RFC 791 Identification field): <https://www.rfc-editor.org/rfc/rfc6864>
8
9#![deny(missing_docs)]
10
11use packet_dissector_core::dissector::{DispatchHint, DissectResult, Dissector};
12use packet_dissector_core::error::PacketError;
13use packet_dissector_core::field::{FieldDescriptor, FieldType, FieldValue};
14use packet_dissector_core::lookup::ip_protocol_name;
15use packet_dissector_core::packet::DissectBuffer;
16use packet_dissector_core::util::read_be_u16;
17
18/// Minimum IPv4 header size (no options).
19const MIN_HEADER_SIZE: usize = 20;
20
21/// Field descriptor indices for [`FIELD_DESCRIPTORS`].
22const FD_VERSION: usize = 0;
23const FD_IHL: usize = 1;
24const FD_DSCP: usize = 2;
25const FD_ECN: usize = 3;
26const FD_TOTAL_LENGTH: usize = 4;
27const FD_IDENTIFICATION: usize = 5;
28const FD_FLAGS: usize = 6;
29const FD_FRAGMENT_OFFSET: usize = 7;
30const FD_TTL: usize = 8;
31const FD_PROTOCOL: usize = 9;
32const FD_CHECKSUM: usize = 10;
33const FD_SRC: usize = 11;
34const FD_DST: usize = 12;
35const FD_OPTIONS: usize = 13;
36
37static FIELD_DESCRIPTORS: &[FieldDescriptor] = &[
38    FieldDescriptor::new("version", "Version", FieldType::U8),
39    FieldDescriptor::new("ihl", "Internet Header Length", FieldType::U8),
40    FieldDescriptor::new("dscp", "Differentiated Services Code Point", FieldType::U8),
41    FieldDescriptor::new("ecn", "Explicit Congestion Notification", FieldType::U8),
42    FieldDescriptor::new("total_length", "Total Length", FieldType::U16),
43    FieldDescriptor::new("identification", "Identification", FieldType::U16),
44    FieldDescriptor::new("flags", "Flags", FieldType::U8),
45    FieldDescriptor::new("fragment_offset", "Fragment Offset", FieldType::U16),
46    FieldDescriptor::new("ttl", "Time to Live", FieldType::U8),
47    FieldDescriptor {
48        name: "protocol",
49        display_name: "Protocol",
50        field_type: FieldType::U8,
51        optional: false,
52        children: None,
53        display_fn: Some(|v, _siblings| match v {
54            FieldValue::U8(p) => ip_protocol_name(*p),
55            _ => None,
56        }),
57        format_fn: None,
58    },
59    FieldDescriptor::new("checksum", "Header Checksum", FieldType::U16),
60    FieldDescriptor::new("src", "Source Address", FieldType::Ipv4Addr),
61    FieldDescriptor::new("dst", "Destination Address", FieldType::Ipv4Addr),
62    FieldDescriptor::new("options", "Options", FieldType::Bytes).optional(),
63];
64
65/// IPv4 dissector.
66pub struct Ipv4Dissector;
67
68impl Dissector for Ipv4Dissector {
69    fn name(&self) -> &'static str {
70        "Internet Protocol version 4"
71    }
72
73    fn short_name(&self) -> &'static str {
74        "IPv4"
75    }
76
77    fn field_descriptors(&self) -> &'static [FieldDescriptor] {
78        FIELD_DESCRIPTORS
79    }
80
81    fn dissect<'pkt>(
82        &self,
83        data: &'pkt [u8],
84        buf: &mut DissectBuffer<'pkt>,
85        offset: usize,
86    ) -> Result<DissectResult, PacketError> {
87        if data.len() < MIN_HEADER_SIZE {
88            return Err(PacketError::Truncated {
89                expected: MIN_HEADER_SIZE,
90                actual: data.len(),
91            });
92        }
93
94        // RFC 791, Section 3.1 — Internet Header Format
95        // https://www.rfc-editor.org/rfc/rfc791#section-3.1
96        let version = (data[0] >> 4) & 0x0F;
97        let ihl = (data[0] & 0x0F) as usize;
98
99        // RFC 791, Section 3.1 — "The Version field indicates the format of the
100        // internet header. This document describes version 4."
101        // https://www.rfc-editor.org/rfc/rfc791#section-3.1
102        if version != 4 {
103            return Err(PacketError::InvalidFieldValue {
104                field: "version",
105                value: version as u32,
106            });
107        }
108
109        // RFC 791, Section 3.1 — "The minimum value for a correct header is 5."
110        // https://www.rfc-editor.org/rfc/rfc791#section-3.1
111        if ihl < 5 {
112            return Err(PacketError::InvalidFieldValue {
113                field: "ihl",
114                value: ihl as u32,
115            });
116        }
117
118        let header_len = ihl * 4;
119        if data.len() < header_len {
120            return Err(PacketError::Truncated {
121                expected: header_len,
122                actual: data.len(),
123            });
124        }
125
126        // RFC 791, Section 3.1 — Total Length is the length of the datagram,
127        // measured in octets, including internet header and data; it must
128        // therefore be at least IHL * 4.
129        // https://www.rfc-editor.org/rfc/rfc791#section-3.1
130        let total_length = read_be_u16(data, 2)?;
131        if (total_length as usize) < header_len {
132            return Err(PacketError::InvalidFieldValue {
133                field: "total_length",
134                value: total_length as u32,
135            });
136        }
137
138        // RFC 791, Section 3.1 — the buffer must contain at least Total Length
139        // octets; otherwise the datagram is truncated.
140        // https://www.rfc-editor.org/rfc/rfc791#section-3.1
141        if data.len() < total_length as usize {
142            return Err(PacketError::Truncated {
143                expected: total_length as usize,
144                actual: data.len(),
145            });
146        }
147
148        // RFC 2474, Section 3 — DSCP occupies bits 0-5 of the DS Field.
149        // https://www.rfc-editor.org/rfc/rfc2474#section-3
150        // RFC 3168, Section 5 — ECN occupies bits 6-7 (formerly CU in RFC 2474).
151        // https://www.rfc-editor.org/rfc/rfc3168#section-5
152        let dscp = data[1] >> 2;
153        let ecn = data[1] & 0x03;
154
155        // RFC 791, Section 3.1 — Identification (16 bits).
156        // RFC 6864, Section 4 — atomic datagrams (DF=1, MF=0, frag_offset=0) MAY
157        // carry any value; parse verbatim without additional validation.
158        // https://www.rfc-editor.org/rfc/rfc6864#section-4
159        let identification = read_be_u16(data, 4)?;
160
161        // RFC 791, Section 3.1 — Flags (3 bits, bit 0 Reserved / bit 1 DF / bit 2 MF)
162        // and Fragment Offset (13 bits, in units of 8 octets).
163        // https://www.rfc-editor.org/rfc/rfc791#section-3.1
164        let flags_frag = read_be_u16(data, 6)?;
165        let flags = ((flags_frag >> 13) & 0x07) as u8;
166        let fragment_offset = flags_frag & 0x1FFF;
167
168        let ttl = data[8];
169        let protocol = data[9];
170        let checksum = read_be_u16(data, 10)?;
171        let src = [data[12], data[13], data[14], data[15]];
172        let dst = [data[16], data[17], data[18], data[19]];
173
174        buf.begin_layer(
175            self.short_name(),
176            None,
177            FIELD_DESCRIPTORS,
178            offset..offset + header_len,
179        );
180        buf.push_field(
181            &FIELD_DESCRIPTORS[FD_VERSION],
182            FieldValue::U8(version),
183            offset..offset + 1,
184        );
185        buf.push_field(
186            &FIELD_DESCRIPTORS[FD_IHL],
187            FieldValue::U8(ihl as u8),
188            offset..offset + 1,
189        );
190        buf.push_field(
191            &FIELD_DESCRIPTORS[FD_DSCP],
192            FieldValue::U8(dscp),
193            offset + 1..offset + 2,
194        );
195        buf.push_field(
196            &FIELD_DESCRIPTORS[FD_ECN],
197            FieldValue::U8(ecn),
198            offset + 1..offset + 2,
199        );
200        buf.push_field(
201            &FIELD_DESCRIPTORS[FD_TOTAL_LENGTH],
202            FieldValue::U16(total_length),
203            offset + 2..offset + 4,
204        );
205        buf.push_field(
206            &FIELD_DESCRIPTORS[FD_IDENTIFICATION],
207            FieldValue::U16(identification),
208            offset + 4..offset + 6,
209        );
210        // Flags occupies bits 0-2 of byte 6 only, so its highlight range is a
211        // single byte even though Fragment Offset (which shares the same 16-bit
212        // word) spans bytes 6-7. RFC 791, Section 3.1.
213        // https://www.rfc-editor.org/rfc/rfc791#section-3.1
214        buf.push_field(
215            &FIELD_DESCRIPTORS[FD_FLAGS],
216            FieldValue::U8(flags),
217            offset + 6..offset + 7,
218        );
219        buf.push_field(
220            &FIELD_DESCRIPTORS[FD_FRAGMENT_OFFSET],
221            FieldValue::U16(fragment_offset),
222            offset + 6..offset + 8,
223        );
224        buf.push_field(
225            &FIELD_DESCRIPTORS[FD_TTL],
226            FieldValue::U8(ttl),
227            offset + 8..offset + 9,
228        );
229        buf.push_field(
230            &FIELD_DESCRIPTORS[FD_PROTOCOL],
231            FieldValue::U8(protocol),
232            offset + 9..offset + 10,
233        );
234        buf.push_field(
235            &FIELD_DESCRIPTORS[FD_CHECKSUM],
236            FieldValue::U16(checksum),
237            offset + 10..offset + 12,
238        );
239        buf.push_field(
240            &FIELD_DESCRIPTORS[FD_SRC],
241            FieldValue::Ipv4Addr(src),
242            offset + 12..offset + 16,
243        );
244        buf.push_field(
245            &FIELD_DESCRIPTORS[FD_DST],
246            FieldValue::Ipv4Addr(dst),
247            offset + 16..offset + 20,
248        );
249
250        // RFC 791, Section 3.1 — Options (variable length, present when IHL > 5).
251        // https://www.rfc-editor.org/rfc/rfc791#section-3.1
252        if header_len > MIN_HEADER_SIZE {
253            buf.push_field(
254                &FIELD_DESCRIPTORS[FD_OPTIONS],
255                FieldValue::Bytes(&data[MIN_HEADER_SIZE..header_len]),
256                offset + MIN_HEADER_SIZE..offset + header_len,
257            );
258        }
259
260        buf.end_layer();
261
262        Ok(DissectResult::new(
263            header_len,
264            DispatchHint::ByIpProtocol(protocol),
265        ))
266    }
267}