packet_strata/packet/
ipv4.rs

1//! IPv4 (Internet Protocol version 4) packet parser
2//!
3//! This module implements parsing for IPv4 packets as defined in RFC 791.
4//! IPv4 is the fourth version of the Internet Protocol and the first version
5//! to be widely deployed.
6//!
7//! # IPv4 Header Format
8//!
9//! ```text
10//!  0                   1                   2                   3
11//!  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
12//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
13//! |Version|  IHL  |    DSCP   |ECN|          Total Length         |
14//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
15//! |         Identification        |Flags|      Fragment Offset    |
16//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
17//! |  Time to Live |    Protocol   |         Header Checksum       |
18//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
19//! |                       Source Address                          |
20//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
21//! |                    Destination Address                        |
22//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
23//! |                    Options (if IHL > 5)                       |
24//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
25//! ```
26//!
27//! # Key characteristics
28//!
29//! - Version: 4 (always)
30//! - IHL: Internet Header Length in 32-bit words (minimum 5, maximum 15)
31//! - Minimum header size: 20 bytes (IHL = 5)
32//! - Maximum header size: 60 bytes (IHL = 15)
33//!
34//! # Examples
35//!
36//! ## Basic IPv4 parsing
37//!
38//! ```
39//! use packet_strata::packet::ipv4::Ipv4Header;
40//! use packet_strata::packet::protocol::IpProto;
41//! use packet_strata::packet::HeaderParser;
42//! use std::net::Ipv4Addr;
43//!
44//! // IPv4 packet with TCP payload
45//! let packet = vec![
46//!     0x45,              // Version=4, IHL=5 (20 bytes)
47//!     0x00,              // DSCP=0, ECN=0
48//!     0x00, 0x28,        // Total length: 40 bytes
49//!     0x1c, 0x46,        // Identification
50//!     0x40, 0x00,        // Flags=DF, Fragment offset=0
51//!     0x40,              // TTL: 64
52//!     0x06,              // Protocol: TCP (6)
53//!     0x00, 0x00,        // Checksum (not validated here)
54//!     0xC0, 0xA8, 0x01, 0x01,  // Source: 192.168.1.1
55//!     0xC0, 0xA8, 0x01, 0x02,  // Destination: 192.168.1.2
56//!     // TCP payload follows...
57//! ];
58//!
59//! let (header, payload) = Ipv4Header::from_bytes(&packet).unwrap();
60//! assert_eq!(header.version(), 4);
61//! assert_eq!(header.ihl(), 5);
62//! assert_eq!(header.ttl(), 64);
63//! assert_eq!(header.protocol(), IpProto::TCP);
64//! assert_eq!(header.src_ip(), Ipv4Addr::new(192, 168, 1, 1));
65//! assert_eq!(header.dst_ip(), Ipv4Addr::new(192, 168, 1, 2));
66//! ```
67//!
68//! ## IPv4 with options
69//!
70//! ```
71//! use packet_strata::packet::ipv4::Ipv4Header;
72//! use packet_strata::packet::HeaderParser;
73//!
74//! // IPv4 packet with options (IHL=6, 24 bytes header)
75//! let packet = vec![
76//!     0x46,              // Version=4, IHL=6 (24 bytes)
77//!     0x00,              // DSCP=0, ECN=0
78//!     0x00, 0x20,        // Total length: 32 bytes
79//!     0x00, 0x01,        // Identification
80//!     0x00, 0x00,        // Flags=0, Fragment offset=0
81//!     0x40,              // TTL: 64
82//!     0x01,              // Protocol: ICMP (1)
83//!     0x00, 0x00,        // Checksum
84//!     0x0A, 0x00, 0x00, 0x01,  // Source: 10.0.0.1
85//!     0x0A, 0x00, 0x00, 0x02,  // Destination: 10.0.0.2
86//!     0x01, 0x01, 0x01, 0x00,  // Options (NOP, NOP, NOP, EOL)
87//!     // Payload follows...
88//! ];
89//!
90//! let (header, payload) = Ipv4Header::from_bytes(&packet).unwrap();
91//! assert_eq!(header.ihl(), 6);
92//! assert_eq!(header.ihl() as usize * 4, 24);  // Header length in bytes
93//! assert!(header.has_options());
94//! ```
95
96pub mod opt;
97
98use std::fmt::{self, Formatter};
99use std::net::Ipv4Addr;
100use std::ops::Deref;
101
102use zerocopy::byteorder::{BigEndian, U16, U32};
103use zerocopy::{FromBytes, IntoBytes, Unaligned};
104
105use crate::packet::ipv4::opt::Ipv4OptionsIter;
106use crate::packet::protocol::IpProto;
107use crate::packet::{HeaderParser, PacketHeader};
108
109/// IPv4 Header structure as defined in RFC 791
110///
111/// The fixed portion of the IPv4 header is 20 bytes. Additional options
112/// may be present if IHL > 5.
113#[repr(C, packed)]
114#[derive(
115    FromBytes, IntoBytes, Unaligned, Debug, Clone, Copy, zerocopy::KnownLayout, zerocopy::Immutable,
116)]
117pub struct Ipv4Header {
118    ver_ihl: u8,
119    dscp_ecn: u8,
120    total_length: U16<BigEndian>,
121    identification: U16<BigEndian>,
122    flags_frag_offset: U16<BigEndian>,
123    ttl: u8,
124    protocol: IpProto,
125    checksum: U16<BigEndian>,
126    src_ip: U32<BigEndian>,
127    dst_ip: U32<BigEndian>,
128}
129
130impl Ipv4Header {
131    #[inline]
132    pub fn dscp(&self) -> u8 {
133        self.dscp_ecn >> 2
134    }
135
136    #[inline]
137    pub fn ecn(&self) -> u8 {
138        self.dscp_ecn & 0x03
139    }
140
141    #[inline]
142    pub fn version(&self) -> u8 {
143        self.ver_ihl >> 4
144    }
145
146    #[inline]
147    pub fn ihl(&self) -> u8 {
148        self.ver_ihl & 0x0F
149    }
150
151    const OFFSET_MASK: u16 = 0x1FFF;
152    const MF_FLAG_MASK: u16 = 0x2000;
153    const DF_FLAG_MASK: u16 = 0x4000;
154    const RS_FLAG_MASK: u16 = 0x8000;
155
156    #[inline]
157    pub fn flags(&self) -> u8 {
158        (self.flags_frag_offset.get() >> 13) as u8
159    }
160
161    #[inline]
162    pub fn fragment_offset(&self) -> u16 {
163        self.flags_frag_offset.get() & Self::OFFSET_MASK
164    }
165
166    #[inline]
167    pub fn has_dont_fragment(&self) -> bool {
168        // Check bit 14 directly (0x4000)
169        (self.flags_frag_offset.get() & Self::DF_FLAG_MASK) != 0
170    }
171
172    #[inline]
173    pub fn has_more_fragment(&self) -> bool {
174        // Check bit 14 directly (0x4000)
175        (self.flags_frag_offset.get() & Self::MF_FLAG_MASK) != 0
176    }
177
178    #[inline]
179    pub fn has_reserved_flag(&self) -> bool {
180        (self.flags_frag_offset.get() & Self::RS_FLAG_MASK) != 0
181    }
182
183    #[inline]
184    pub fn is_fragmenting(&self) -> bool {
185        // A packet is a fragment if MF is set (0x2000) OR offset is non-zero (0x1FFF).
186        (self.flags_frag_offset.get() & Self::MF_FLAG_MASK | Self::OFFSET_MASK) != 0
187    }
188
189    #[inline]
190    pub fn is_first_fragment(&self) -> bool {
191        // First fragment: MF set AND offset is 0
192        let raw = self.flags_frag_offset.get();
193        (raw & Self::MF_FLAG_MASK) != 0 && (raw & Self::OFFSET_MASK) == 0
194    }
195
196    #[inline]
197    pub fn is_last_fragment(&self) -> bool {
198        // Last fragment: MF NOT set AND offset > 0
199        let raw = self.flags_frag_offset.get();
200        (raw & Self::MF_FLAG_MASK) == 0 && (raw & Self::OFFSET_MASK) != 0
201    }
202
203    #[inline]
204    pub fn total_length(&self) -> usize {
205        self.total_length.get() as usize
206    }
207
208    #[inline]
209    pub fn ttl(&self) -> u8 {
210        self.ttl
211    }
212
213    #[inline]
214    pub fn protocol(&self) -> IpProto {
215        self.protocol
216    }
217
218    #[inline]
219    pub fn src_ip(&self) -> Ipv4Addr {
220        Ipv4Addr::from(self.src_ip.get())
221    }
222
223    #[inline]
224    pub fn dst_ip(&self) -> Ipv4Addr {
225        Ipv4Addr::from(self.dst_ip.get())
226    }
227
228    #[inline]
229    pub fn src_ip_raw(&self) -> [u8; 4] {
230        self.src_ip.get().to_be_bytes()
231    }
232
233    #[inline]
234    pub fn dst_ip_raw(&self) -> [u8; 4] {
235        self.dst_ip.get().to_be_bytes()
236    }
237
238    /// Returns the options slice if present
239    /// Options length = (ihl * 4) - 20
240    #[inline]
241    pub fn has_options(&self) -> bool {
242        self.ihl() > 5
243    }
244
245    #[inline]
246    pub fn id(&self) -> u16 {
247        self.identification.get()
248    }
249}
250
251/// IPv4 Header with options
252#[derive(Debug, Clone)]
253pub struct Ipv4HeaderOpt<'a> {
254    pub header: &'a Ipv4Header,
255    pub raw_options: &'a [u8],
256}
257
258impl<'a> Ipv4HeaderOpt<'a> {
259    /// Get IPv4 options iterator
260    pub fn options(&'a self) -> Ipv4OptionsIter<'a> {
261        Ipv4OptionsIter::new(self.raw_options)
262    }
263}
264
265impl Deref for Ipv4HeaderOpt<'_> {
266    type Target = Ipv4Header;
267
268    #[inline]
269    fn deref(&self) -> &Self::Target {
270        self.header
271    }
272}
273
274impl PacketHeader for Ipv4Header {
275    const NAME: &'static str = "IPv4Header";
276    type InnerType = IpProto;
277
278    #[inline]
279    fn inner_type(&self) -> Self::InnerType {
280        self.protocol
281    }
282
283    #[inline]
284    fn total_len(&self, _buf: &[u8]) -> usize {
285        (self.ihl() as usize) * 4
286    }
287
288    #[inline]
289    fn is_valid(&self) -> bool {
290        self.version() == 4 && self.ihl() >= 5
291    }
292}
293
294impl HeaderParser for Ipv4Header {
295    type Output<'a> = Ipv4HeaderOpt<'a>;
296
297    #[inline]
298    fn into_view<'a>(header: &'a Self, raw_options: &'a [u8]) -> Self::Output<'a> {
299        Ipv4HeaderOpt {
300            header,
301            raw_options,
302        }
303    }
304}
305
306impl fmt::Display for Ipv4Header {
307    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
308        write!(
309            f,
310            "IPv4 {} -> {} proto={} ttl={} len={}",
311            self.src_ip(),
312            self.dst_ip(),
313            self.protocol(),
314            self.ttl(),
315            self.total_length()
316        )?;
317
318        if self.is_fragmenting() {
319            write!(f, " frag offset={}", self.fragment_offset())?;
320        }
321
322        if self.has_options() {
323            write!(f, " +opts")?;
324        }
325
326        Ok(())
327    }
328}
329
330impl fmt::Display for Ipv4HeaderOpt<'_> {
331    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
332        write!(f, "{}", self.header)?;
333
334        if !self.raw_options.is_empty() {
335            write!(f, " opts=[")?;
336            let mut first = true;
337            for opt in self.options().flatten() {
338                if !first {
339                    write!(f, ",")?;
340                }
341                first = false;
342                write!(f, "{}", opt)?;
343            }
344            write!(f, "]")?;
345        }
346
347        Ok(())
348    }
349}
350
351mod tests {
352    use super::*;
353    #[test]
354    fn test_ipv4_header_size() {
355        assert_eq!(std::mem::size_of::<Ipv4Header>(), 20);
356        assert_eq!(Ipv4Header::FIXED_LEN, 20);
357    }
358
359    #[test]
360    fn test_ipv4_version_and_ihl() {
361        let header = create_test_header();
362        assert_eq!(header.version(), 4);
363        assert_eq!(header.ihl(), 5); // 5 * 4 = 20 bytes (no options)
364        assert!(header.is_valid());
365    }
366
367    #[test]
368    fn test_ipv4_dscp_ecn() {
369        let mut header = create_test_header();
370
371        // DSCP = 0x2E (46), ECN = 0x01
372        header.dscp_ecn = 0xB9; // 10111001 in binary
373        assert_eq!(header.dscp(), 46);
374        assert_eq!(header.ecn(), 1);
375    }
376
377    #[test]
378    fn test_ipv4_total_length() {
379        let mut header = create_test_header();
380        header.total_length = U16::new(1500);
381        assert_eq!(header.total_length(), 1500);
382    }
383
384    #[test]
385    fn test_ipv4_fragmentation() {
386        let mut header = create_test_header();
387
388        // No fragmentation
389        header.flags_frag_offset = U16::new(0x4000); // Don't Fragment flag
390        assert_eq!(header.flags(), 0x02);
391        assert_eq!(header.fragment_offset(), 0);
392        assert!(!header.is_fragmented());
393
394        // With fragmentation: offset = 185 (0x00B9), More Fragments flag
395        header.flags_frag_offset = U16::new(0x20B9);
396        assert_eq!(header.flags(), 0x01); // More Fragments
397        assert_eq!(header.fragment_offset(), 185);
398        assert!(header.is_fragmented());
399    }
400
401    #[test]
402    fn test_ipv4_addresses() {
403        let header = create_test_header();
404
405        // Test source address: 192.168.1.100
406        let expected_src = Ipv4Addr::new(192, 168, 1, 100);
407        assert_eq!(header.src_ip(), expected_src);
408        assert_eq!(header.src_ip_raw(), [192, 168, 1, 100]);
409
410        // Test destination address: 10.0.0.1
411        let expected_dst = Ipv4Addr::new(10, 0, 0, 1);
412        assert_eq!(header.dst_ip(), expected_dst);
413        assert_eq!(header.dst_ip_raw(), [10, 0, 0, 1]);
414    }
415
416    #[test]
417    fn test_ipv4_protocol_ttl() {
418        let header = create_test_header();
419        assert_eq!(header.protocol(), IpProto::TCP);
420        assert_eq!(header.ttl(), 64);
421    }
422
423    #[test]
424    fn test_ipv4_parsing_basic() {
425        let packet = create_test_packet();
426
427        let result = Ipv4Header::from_bytes(&packet);
428        assert!(result.is_ok());
429
430        let (header_ext, payload) = result.unwrap();
431        assert_eq!(header_ext.version(), 4);
432        assert_eq!(header_ext.ihl(), 5);
433        assert_eq!(header_ext.protocol(), IpProto::TCP);
434        assert!(!header_ext.has_options());
435        assert_eq!(header_ext.raw_options.len(), 0);
436        assert_eq!(payload.len(), 0); // No payload in test packet
437    }
438
439    #[test]
440    fn test_ipv4_parsing_invalid_version() {
441        let mut packet = create_test_packet();
442        packet[0] = 0x60; // Version 6 instead of 4
443
444        let result = Ipv4Header::from_bytes(&packet);
445        assert!(result.is_err());
446    }
447
448    #[test]
449    fn test_ipv4_parsing_invalid_ihl() {
450        let mut packet = create_test_packet();
451        packet[0] = 0x44; // IHL = 4, which is invalid (minimum is 5)
452
453        let result = Ipv4Header::from_bytes(&packet);
454        assert!(result.is_err());
455    }
456
457    #[test]
458    fn test_ipv4_parsing_too_small() {
459        let packet = vec![0u8; 19]; // Only 19 bytes, need 20
460
461        let result = Ipv4Header::from_bytes(&packet);
462        assert!(result.is_err());
463    }
464
465    #[test]
466    fn test_ipv4_total_len_no_options() {
467        let packet = create_test_packet();
468        let (header_ext, _) = Ipv4Header::from_bytes(&packet).unwrap();
469
470        // No options, should return 20 bytes
471        assert_eq!(header_ext.total_len(&packet), 20);
472        assert_eq!(header_ext.raw_options.len(), 0);
473    }
474
475    #[test]
476    fn test_ipv4_with_options() {
477        let mut packet = create_test_packet();
478
479        // Change IHL to 6 (6 * 4 = 24 bytes header with 4 bytes of options)
480        packet[0] = 0x46; // Version 4, IHL 6
481
482        // Add 4 bytes of options (NOP padding)
483        packet.extend_from_slice(&[0x01, 0x01, 0x01, 0x01]); // 4x NOP
484
485        let (header_ext, _) = Ipv4Header::from_bytes(&packet).unwrap();
486
487        // Verify header length includes options
488        assert_eq!(header_ext.ihl(), 6);
489        assert_eq!(header_ext.total_len(&packet), 24); // 6 * 4 = 24 bytes
490        assert!(header_ext.is_valid());
491        assert!(header_ext.has_options());
492        assert_eq!(header_ext.raw_options.len(), 4);
493
494        // Verify we can iterate options
495        let opts: Vec<_> = header_ext.options().collect::<Result<Vec<_>, _>>().unwrap();
496        assert_eq!(opts.len(), 4); // 4 NOPs
497    }
498
499    #[test]
500    fn test_ipv4_with_timestamp_option() {
501        let mut packet = create_test_packet();
502
503        // Change IHL to 9 (9 * 4 = 36 bytes header with 16 bytes of options)
504        packet[0] = 0x49; // Version 4, IHL 9
505
506        // Add Timestamp option (Type 68, Length 16)
507        packet.push(0x44); // Type: Timestamp
508        packet.push(0x10); // Length: 16 bytes
509        packet.push(0x05); // Pointer: 5
510        packet.push(0x00); // Overflow + Flags
511
512        // Add 3 timestamps (3 * 4 = 12 bytes)
513        packet.extend_from_slice(&[0x00, 0x00, 0x00, 0x01]); // Timestamp 1
514        packet.extend_from_slice(&[0x00, 0x00, 0x00, 0x02]); // Timestamp 2
515        packet.extend_from_slice(&[0x00, 0x00, 0x00, 0x03]); // Timestamp 3
516
517        let (header_ext, _) = Ipv4Header::from_bytes(&packet).unwrap();
518
519        // Verify header length includes options
520        assert_eq!(header_ext.ihl(), 9);
521        assert_eq!(header_ext.total_len(&packet), 36); // 9 * 4 = 36 bytes
522        assert!(header_ext.is_valid());
523        assert_eq!(header_ext.raw_options.len(), 16);
524
525        // Verify we can parse the timestamp option
526        let opts: Vec<_> = header_ext.options().collect::<Result<Vec<_>, _>>().unwrap();
527        assert_eq!(opts.len(), 1);
528    }
529
530    #[test]
531    fn test_ipv4_with_record_route_option() {
532        let mut packet = create_test_packet();
533
534        // Change IHL to 10 (10 * 4 = 40 bytes header with 20 bytes of options)
535        packet[0] = 0x4A; // Version 4, IHL 10
536
537        // Add Record Route option
538        // Type (1) + Length (1) + Pointer (1) + 4 addresses (16) = 19 bytes total
539        packet.push(0x07); // Type: Record Route
540        packet.push(0x13); // Length: 19 bytes (includes Type and Length fields)
541        packet.push(0x04); // Pointer: 4 (points to first address slot)
542
543        // Add 4 IP addresses (4 * 4 = 16 bytes)
544        packet.extend_from_slice(&[192, 168, 1, 1]);
545        packet.extend_from_slice(&[192, 168, 1, 2]);
546        packet.extend_from_slice(&[192, 168, 1, 3]);
547        packet.extend_from_slice(&[192, 168, 1, 4]);
548
549        // Add NOP for padding (19 + 1 = 20 bytes total options)
550        packet.push(0x01); // NOP
551
552        let (header_ext, _) = Ipv4Header::from_bytes(&packet).unwrap();
553
554        // Verify header length includes options
555        assert_eq!(header_ext.ihl(), 10);
556        assert_eq!(header_ext.total_len(&packet), 40); // 10 * 4 = 40 bytes
557        assert!(header_ext.is_valid());
558        assert_eq!(header_ext.raw_options.len(), 20);
559
560        // Verify we can parse the record route option
561        let opts: Vec<_> = header_ext.options().collect::<Result<Vec<_>, _>>().unwrap();
562        assert_eq!(opts.len(), 2); // RecordRoute + NOP
563    }
564
565    #[test]
566    fn test_ipv4_from_bytes_with_options_and_payload() {
567        let mut packet = create_test_packet();
568
569        // Change IHL to 7 (7 * 4 = 28 bytes header with 8 bytes of options)
570        packet[0] = 0x47; // Version 4, IHL 7
571
572        // Add 8 bytes of options (NOP + Security option)
573        packet.push(0x01); // NOP
574        packet.push(0x82); // Type: Security (copied)
575        packet.push(0x06); // Length: 6 bytes
576        packet.extend_from_slice(&[0x00, 0x00, 0x00, 0x00]); // Security data
577        packet.push(0x01); // NOP for padding
578
579        // Add some payload data
580        let payload_data = b"Test payload after IP options";
581        packet.extend_from_slice(payload_data);
582
583        // Update total length in header (28 bytes header + payload)
584        let total_len = 28 + payload_data.len();
585        packet[2] = ((total_len >> 8) & 0xFF) as u8;
586        packet[3] = (total_len & 0xFF) as u8;
587
588        // Parse with from_bytes
589        let result = Ipv4Header::from_bytes(&packet);
590        assert!(result.is_ok());
591
592        let (header_ext, payload) = result.unwrap();
593
594        // Verify the payload starts after ALL of the header (base + options)
595        // Should skip 28 bytes (7 * 4)
596        assert_eq!(payload.len(), payload_data.len());
597        assert_eq!(payload, payload_data);
598
599        // Verify header info
600        assert_eq!(header_ext.ihl(), 7);
601        assert_eq!(header_ext.total_len(&packet), 28);
602        assert_eq!(header_ext.total_length(), total_len);
603        assert_eq!(header_ext.raw_options.len(), 8);
604
605        // Verify we can parse the options
606        let opts: Vec<_> = header_ext.options().collect::<Result<Vec<_>, _>>().unwrap();
607        assert_eq!(opts.len(), 3); // NOP + Security + NOP
608    }
609
610    #[test]
611    fn test_ipv4_total_len_includes_options() {
612        // Test that total_len() correctly includes IP options
613        let mut packet = create_test_packet();
614
615        // Test 1: No options - should return 20
616        let (header_ext, _) = Ipv4Header::from_bytes(&packet).unwrap();
617        assert_eq!(header_ext.total_len(&packet), 20);
618        assert_eq!(header_ext.ihl(), 5);
619        assert_eq!(header_ext.raw_options.len(), 0);
620
621        // Test 2: With 4 bytes of options (IHL = 6)
622        packet = create_test_packet();
623        packet[0] = 0x46;
624        packet.extend_from_slice(&[0x01, 0x01, 0x01, 0x01]);
625        let (header_ext, _) = Ipv4Header::from_bytes(&packet).unwrap();
626        assert_eq!(header_ext.total_len(&packet), 24); // 6 * 4
627        assert_eq!(header_ext.ihl(), 6);
628        assert_eq!(header_ext.raw_options.len(), 4);
629
630        // Test 3: With 8 bytes of options (IHL = 7)
631        packet = create_test_packet();
632        packet[0] = 0x47;
633        packet.extend_from_slice(&[0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01]);
634        let (header_ext, _) = Ipv4Header::from_bytes(&packet).unwrap();
635        assert_eq!(header_ext.total_len(&packet), 28); // 7 * 4
636        assert_eq!(header_ext.ihl(), 7);
637        assert_eq!(header_ext.raw_options.len(), 8);
638
639        // Test 4: Maximum header size with options (IHL = 15)
640        packet = create_test_packet();
641        packet[0] = 0x4F; // IHL = 15
642        let options = vec![0x01u8; 40]; // 40 bytes of NOP options
643        packet.extend_from_slice(&options);
644        let (header_ext, _) = Ipv4Header::from_bytes(&packet).unwrap();
645        assert_eq!(header_ext.total_len(&packet), 60); // 15 * 4
646        assert_eq!(header_ext.ihl(), 15);
647        assert_eq!(header_ext.raw_options.len(), 40);
648    }
649
650    #[test]
651    fn test_ipv4_header_ext_with_options() {
652        let mut packet = create_test_packet();
653        packet[0] = 0x47; // IHL = 7
654
655        // Add Router Alert option
656        packet.extend_from_slice(&[148, 0x04, 0x00, 0x00]); // Router Alert
657
658        // Add NOP padding
659        packet.extend_from_slice(&[0x01, 0x01, 0x01, 0x01]);
660
661        let (header_ext, _) = Ipv4Header::from_bytes(&packet).unwrap();
662
663        assert_eq!(header_ext.raw_options.len(), 8);
664        assert!(header_ext.has_options());
665
666        // Parse options
667        let opts: Vec<_> = header_ext.options().collect::<Result<Vec<_>, _>>().unwrap();
668        assert_eq!(opts.len(), 5); // RouterAlert + 4 NOPs
669
670        // Verify Deref works
671        assert_eq!(header_ext.version(), 4);
672        assert_eq!(header_ext.ihl(), 7);
673        assert_eq!(header_ext.protocol(), IpProto::TCP);
674    }
675
676    #[test]
677    fn test_ipv4_header_ext_deref() {
678        let packet = create_test_packet();
679        let (header_ext, _) = Ipv4Header::from_bytes(&packet).unwrap();
680
681        // Test that Deref allows access to all Ipv4Header methods
682        assert_eq!(header_ext.version(), 4);
683        assert_eq!(header_ext.ihl(), 5);
684        assert_eq!(header_ext.dscp(), 0);
685        assert_eq!(header_ext.ecn(), 0);
686        assert_eq!(header_ext.ttl(), 64);
687        assert_eq!(header_ext.protocol(), IpProto::TCP);
688        assert_eq!(
689            header_ext.src_ip(),
690            "192.168.1.100".parse::<Ipv4Addr>().unwrap()
691        );
692        assert_eq!(header_ext.dst_ip(), "10.0.0.1".parse::<Ipv4Addr>().unwrap());
693    }
694
695    #[test]
696    fn test_ipv4_options_integration_record_route() {
697        let mut packet = create_test_packet();
698        packet[0] = 0x48; // IHL = 8 (32 bytes header)
699
700        // Add Record Route option
701        packet.push(0x07); // Type: Record Route
702        packet.push(0x0B); // Length: 11 bytes
703        packet.push(0x04); // Pointer: 4
704        packet.extend_from_slice(&[192, 168, 1, 1]); // IP 1
705        packet.extend_from_slice(&[192, 168, 1, 2]); // IP 2
706
707        // Add NOP for padding to 12 bytes
708        packet.push(0x01);
709
710        let (header_ext, _) = Ipv4Header::from_bytes(&packet).unwrap();
711
712        assert_eq!(header_ext.raw_options.len(), 12);
713
714        // Parse and verify the option
715        let opts: Vec<_> = header_ext.options().collect::<Result<Vec<_>, _>>().unwrap();
716        assert_eq!(opts.len(), 2); // RecordRoute + NOP
717
718        use crate::packet::ipv4::opt::Ipv4OptionElement;
719        match &opts[0] {
720            Ipv4OptionElement::RecordRoute {
721                pointer,
722                route_data,
723            } => {
724                assert_eq!(*pointer, 4);
725                assert_eq!(route_data.len(), 8);
726                assert!(!opts[0].is_copied());
727            }
728            _ => panic!("Expected RecordRoute option"),
729        }
730    }
731
732    #[test]
733    fn test_ipv4_options_integration_timestamp() {
734        let mut packet = create_test_packet();
735        packet[0] = 0x47; // IHL = 7 (28 bytes header)
736
737        // Add Timestamp option (Type 68)
738        packet.push(0x44); // Type: Timestamp
739        packet.push(0x08); // Length: 8 bytes
740        packet.push(0x05); // Pointer: 5
741        packet.push(0x00); // Overflow + Flags (0 = timestamps only)
742        packet.extend_from_slice(&[0x00, 0x00, 0x00, 0x01]); // Timestamp
743
744        let (header_ext, _) = Ipv4Header::from_bytes(&packet).unwrap();
745
746        assert_eq!(header_ext.raw_options.len(), 8);
747
748        // Parse and verify the option
749        let opts: Vec<_> = header_ext.options().collect::<Result<Vec<_>, _>>().unwrap();
750        assert_eq!(opts.len(), 1);
751
752        use crate::packet::ipv4::opt::{Ipv4OptionElement, TimestampFlag};
753        match &opts[0] {
754            Ipv4OptionElement::Timestamp {
755                pointer,
756                overflow,
757                flags,
758                data,
759            } => {
760                assert_eq!(*pointer, 5);
761                assert_eq!(*overflow, 0);
762                assert_eq!(*flags, TimestampFlag::TimestampsOnly);
763                assert_eq!(data.len(), 4);
764                assert_eq!(opts[0].option_class(), 2);
765            }
766            _ => panic!("Expected Timestamp option"),
767        }
768    }
769
770    #[test]
771    fn test_ipv4_options_integration_security() {
772        let mut packet = create_test_packet();
773        packet[0] = 0x47; // IHL = 7 (28 bytes header, need 8 bytes for option + padding)
774
775        // Add Security option (Type 130, copied bit set)
776        packet.push(0x82); // Type: Security (130, with copied bit)
777        packet.push(0x06); // Length: 6 bytes (Type + Length + 2 + 2)
778        packet.extend_from_slice(&[0x00, 0xAB, 0x00, 0xCD]); // Classification (2 bytes) + Protection Authority (2 bytes)
779
780        // Add padding to reach 8 bytes total options
781        packet.extend_from_slice(&[0x01, 0x01]); // 2 NOPs for padding
782
783        let (header_ext, _) = Ipv4Header::from_bytes(&packet).unwrap();
784
785        assert_eq!(header_ext.raw_options.len(), 8);
786
787        // Parse and verify the option
788        let opts: Vec<_> = header_ext.options().collect::<Result<Vec<_>, _>>().unwrap();
789        assert_eq!(opts.len(), 3); // Security + 2 NOPs
790
791        use crate::packet::ipv4::opt::Ipv4OptionElement;
792        match &opts[0] {
793            Ipv4OptionElement::Security {
794                classification,
795                protection_authority,
796            } => {
797                assert_eq!(*classification, 0x00AB);
798                assert_eq!(*protection_authority, 0x00CD);
799                assert!(opts[0].is_copied()); // Security should be copied
800            }
801            _ => panic!("Expected Security option"),
802        }
803    }
804
805    #[test]
806    fn test_ipv4_options_integration_multiple() {
807        let mut packet = create_test_packet();
808        packet[0] = 0x48; // IHL = 8 (32 bytes header)
809
810        // NOP
811        packet.push(0x01);
812
813        // Stream ID
814        packet.extend_from_slice(&[136, 0x04, 0x12, 0x34]);
815
816        // Router Alert
817        packet.extend_from_slice(&[148, 0x04, 0x00, 0x00]);
818
819        // NOP padding (3 bytes to reach 12 bytes total)
820        packet.extend_from_slice(&[0x01, 0x01, 0x01]);
821
822        let (header_ext, _) = Ipv4Header::from_bytes(&packet).unwrap();
823
824        assert_eq!(header_ext.raw_options.len(), 12);
825        assert!(header_ext.has_options());
826
827        // Parse all options
828        let opts: Vec<_> = header_ext.options().collect::<Result<Vec<_>, _>>().unwrap();
829        assert_eq!(opts.len(), 6); // NOP + StreamID + RouterAlert + 3 NOPs
830
831        use crate::packet::ipv4::opt::Ipv4OptionElement;
832        assert!(matches!(opts[0], Ipv4OptionElement::Nop));
833        assert!(matches!(opts[1], Ipv4OptionElement::StreamId(0x1234)));
834        assert!(matches!(opts[2], Ipv4OptionElement::RouterAlert(0)));
835        assert!(matches!(opts[3], Ipv4OptionElement::Nop));
836    }
837
838    // Helper function to create a test header
839    fn create_test_header() -> Ipv4Header {
840        Ipv4Header {
841            ver_ihl: 0x45, // Version 4, IHL 5
842            dscp_ecn: 0x00,
843            total_length: U16::new(20),
844            identification: U16::new(0x1234),
845            flags_frag_offset: U16::new(0x4000), // Don't Fragment
846            ttl: 64,
847            protocol: IpProto::TCP,
848            checksum: U16::new(0),
849            src_ip: U32::new(0xC0A80164), // 192.168.1.100
850            dst_ip: U32::new(0x0A000001), // 10.0.0.1
851        }
852    }
853
854    // Helper function to create a test packet
855    fn create_test_packet() -> Vec<u8> {
856        let mut packet = Vec::new();
857
858        // Version (4) + IHL (5)
859        packet.push(0x45);
860
861        // DSCP + ECN
862        packet.push(0x00);
863
864        // Total length (20 bytes for header only)
865        packet.extend_from_slice(&[0x00, 0x14]);
866
867        // Identification
868        packet.extend_from_slice(&[0x12, 0x34]);
869
870        // Flags + Fragment offset (Don't Fragment)
871        packet.extend_from_slice(&[0x40, 0x00]);
872
873        // TTL
874        packet.push(64);
875
876        // Protocol (TCP)
877        packet.push(6);
878
879        // Checksum
880        packet.extend_from_slice(&[0x00, 0x00]);
881
882        // Source IP: 192.168.1.100
883        packet.extend_from_slice(&[192, 168, 1, 100]);
884
885        // Destination IP: 10.0.0.1
886        packet.extend_from_slice(&[10, 0, 0, 1]);
887
888        packet
889    }
890}