packet_strata/packet/
ipv6.rs

1//! IPv6 (Internet Protocol version 6) packet parser
2//!
3//! This module implements parsing for IPv6 packets as defined in RFC 8200.
4//! IPv6 is the most recent version of the Internet Protocol, designed to
5//! replace IPv4 and provide a much larger address space.
6//!
7//! # IPv6 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| Traffic Class |           Flow Label                  |
14//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
15//! |         Payload Length        |  Next Header  |   Hop Limit   |
16//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
17//! |                                                               |
18//! +                                                               +
19//! |                                                               |
20//! +                         Source Address                        +
21//! |                                                               |
22//! +                                                               +
23//! |                                                               |
24//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
25//! |                                                               |
26//! +                                                               +
27//! |                                                               |
28//! +                      Destination Address                      +
29//! |                                                               |
30//! +                                                               +
31//! |                                                               |
32//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
33//! ```
34//!
35//! # Key characteristics
36//!
37//! - Version: 6 (always)
38//! - Header size: 40 bytes (fixed, unlike IPv4)
39//! - Address size: 128 bits (16 bytes)
40//! - Extension headers used for optional features
41//!
42//! # Examples
43//!
44//! ## Basic IPv6 parsing
45//!
46//! ```
47//! use packet_strata::packet::ipv6::Ipv6Header;
48//! use packet_strata::packet::protocol::IpProto;
49//! use packet_strata::packet::HeaderParser;
50//! use std::net::Ipv6Addr;
51//!
52//! // IPv6 packet with ICMPv6 payload
53//! let packet = vec![
54//!     0x60, 0x00, 0x00, 0x00,  // Version=6, TC=0, Flow Label=0
55//!     0x00, 0x08,              // Payload Length: 8 bytes
56//!     0x3A,                    // Next Header: ICMPv6 (58)
57//!     0x40,                    // Hop Limit: 64
58//!     // Source: ::1
59//!     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
60//!     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
61//!     // Destination: ::1
62//!     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
63//!     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
64//!     // ICMPv6 payload follows...
65//! ];
66//!
67//! let (header, payload) = Ipv6Header::from_bytes(&packet).unwrap();
68//! assert_eq!(header.version(), 6);
69//! assert_eq!(header.hop_limit(), 64);
70//! assert_eq!(header.next_header(), IpProto::IPV6_ICMP);
71//! assert_eq!(header.src_ip(), Ipv6Addr::LOCALHOST);
72//! assert_eq!(header.dst_ip(), Ipv6Addr::LOCALHOST);
73//! ```
74//!
75//! ## IPv6 with Traffic Class and Flow Label
76//!
77//! ```
78//! use packet_strata::packet::ipv6::Ipv6Header;
79//! use packet_strata::packet::HeaderParser;
80//!
81//! // IPv6 packet with Traffic Class and Flow Label set
82//! let packet = vec![
83//!     0x6F, 0x12, 0x34, 0x56,  // Version=6, TC=0xF1, Flow Label=0x23456
84//!     0x00, 0x00,              // Payload Length: 0
85//!     0x06,                    // Next Header: TCP (6)
86//!     0x80,                    // Hop Limit: 128
87//!     // Source: 2001:db8::1
88//!     0x20, 0x01, 0x0D, 0xB8, 0x00, 0x00, 0x00, 0x00,
89//!     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
90//!     // Destination: 2001:db8::2
91//!     0x20, 0x01, 0x0D, 0xB8, 0x00, 0x00, 0x00, 0x00,
92//!     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
93//! ];
94//!
95//! let (header, _) = Ipv6Header::from_bytes(&packet).unwrap();
96//! assert_eq!(header.traffic_class(), 0xF1);
97//! assert_eq!(header.flow_label(), 0x23456);
98//! assert_eq!(header.hop_limit(), 128);
99//! ```
100
101pub mod ext;
102
103use std::fmt::{self, Formatter};
104use std::net::Ipv6Addr;
105use std::ops::Deref;
106
107use zerocopy::byteorder::{BigEndian, U16};
108use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned};
109
110use crate::packet::ipv6::ext::Ipv6ExtensionHeadersIter;
111use crate::packet::protocol::IpProto;
112use crate::packet::{HeaderParser, PacketHeader};
113
114/// IPv6 Header structure as defined in RFC 8200
115///
116/// The IPv6 header has a fixed size of 40 bytes, unlike IPv4 which has a variable size.
117/// Extension headers are used for additional functionality instead of header options.
118#[repr(C, packed)]
119#[derive(FromBytes, IntoBytes, Immutable, Unaligned, KnownLayout, Debug, Clone, Copy)]
120pub struct Ipv6Header {
121    /// Version (4 bits), Traffic Class (8 bits), Flow Label (20 bits)
122    ver_tc_flow: [u8; 4],
123    /// Payload length (excludes the header itself)
124    payload_length: U16<BigEndian>,
125    /// Next header type (same values as IPv4 protocol field)
126    next_header: IpProto,
127    /// Hop limit (equivalent to IPv4 TTL)
128    hop_limit: u8,
129    /// Source IPv6 address (128 bits)
130    src_ip: [u8; 16],
131    /// Destination IPv6 address (128 bits)
132    dst_ip: [u8; 16],
133}
134
135// IPv6 Extension Header Types (next_header values)
136// Re-export from IpProto for convenience
137pub const IPV6_NEXT_HOPBYHOP: IpProto = IpProto::IPV6_HOPOPT;
138pub const IPV6_NEXT_TCP: IpProto = IpProto::TCP;
139pub const IPV6_NEXT_UDP: IpProto = IpProto::UDP;
140pub const IPV6_NEXT_IPV6: IpProto = IpProto::IPV6;
141pub const IPV6_NEXT_ROUTING: IpProto = IpProto::IPV6_ROUTE;
142pub const IPV6_NEXT_FRAGMENT: IpProto = IpProto::IPV6_FRAG;
143pub const IPV6_NEXT_ICMPV6: IpProto = IpProto::IPV6_ICMP;
144pub const IPV6_NEXT_NONE: IpProto = IpProto::IPV6_NONXT;
145pub const IPV6_NEXT_DSTOPTS: IpProto = IpProto::IPV6_OPTS;
146pub const IPV6_NEXT_MOBILITY: IpProto = IpProto::IPV6_MOBILITY;
147
148impl Ipv6Header {
149    /// Returns the IP version (should always be 6)
150    #[inline]
151    pub fn version(&self) -> u8 {
152        self.ver_tc_flow[0] >> 4
153    }
154
155    /// Returns the Traffic Class (8 bits)
156    /// Equivalent to DSCP + ECN in IPv4
157    #[inline]
158    pub fn traffic_class(&self) -> u8 {
159        ((self.ver_tc_flow[0] & 0x0F) << 4) | (self.ver_tc_flow[1] >> 4)
160    }
161
162    /// Returns the DSCP portion of Traffic Class (6 bits)
163    #[inline]
164    pub fn dscp(&self) -> u8 {
165        self.traffic_class() >> 2
166    }
167
168    /// Returns the ECN portion of Traffic Class (2 bits)
169    #[inline]
170    pub fn ecn(&self) -> u8 {
171        self.traffic_class() & 0x03
172    }
173
174    /// Returns the Flow Label (20 bits)
175    /// Used for QoS and flow identification
176    #[inline]
177    pub fn flow_label(&self) -> u32 {
178        let b1 = (self.ver_tc_flow[1] & 0x0F) as u32;
179        let b2 = self.ver_tc_flow[2] as u32;
180        let b3 = self.ver_tc_flow[3] as u32;
181        (b1 << 16) | (b2 << 8) | b3
182    }
183
184    /// Returns the payload length in bytes
185    /// Note: This does NOT include the IPv6 header itself (40 bytes)
186    #[inline]
187    pub fn payload_length(&self) -> usize {
188        self.payload_length.get() as usize
189    }
190
191    /// Returns the total packet length (header + payload)
192    #[inline]
193    pub fn total_length(&self) -> usize {
194        Self::FIXED_LEN + self.payload_length()
195    }
196
197    /// Returns the next header type
198    /// This indicates the protocol of the next header (TCP, UDP, extension header, etc.)
199    #[inline]
200    pub fn next_header(&self) -> IpProto {
201        self.next_header
202    }
203
204    /// Returns the hop limit
205    /// Equivalent to TTL in IPv4
206    #[inline]
207    pub fn hop_limit(&self) -> u8 {
208        self.hop_limit
209    }
210
211    /// Returns the source IPv6 address as `Ipv6Addr`
212    #[inline]
213    pub fn src_ip(&self) -> Ipv6Addr {
214        Ipv6Addr::from(self.src_ip)
215    }
216
217    /// Returns the destination IPv6 address as `Ipv6Addr`
218    #[inline]
219    pub fn dst_ip(&self) -> Ipv6Addr {
220        Ipv6Addr::from(self.dst_ip)
221    }
222
223    /// Returns the source IPv6 address as raw bytes
224    #[inline]
225    pub fn src_ip_raw(&self) -> [u8; 16] {
226        self.src_ip
227    }
228
229    /// Returns the destination IPv6 address as raw bytes
230    #[inline]
231    pub fn dst_ip_raw(&self) -> [u8; 16] {
232        self.dst_ip
233    }
234
235    /// Check if this packet uses extension headers
236    #[inline]
237    pub fn has_extension_headers(&self) -> bool {
238        matches!(
239            self.next_header,
240            IPV6_NEXT_HOPBYHOP | IPV6_NEXT_ROUTING | IPV6_NEXT_FRAGMENT | IPV6_NEXT_DSTOPTS
241        )
242    }
243
244    /// Check if the next header is a transport protocol (TCP/UDP)
245    #[inline]
246    pub fn is_transport_protocol(&self) -> bool {
247        matches!(self.next_header, IPV6_NEXT_TCP | IPV6_NEXT_UDP)
248    }
249
250    /// Parse extension headers starting from a given offset
251    /// Returns (total_header_length, upper_layer_protocol, is_fragmented)
252    /// Returns None if parsing fails or buffer is too small
253    fn parse_extension_headers(
254        buf: &[u8],
255        bytes_available: usize,
256        initial_next_header: IpProto,
257        allow_fragment: bool,
258    ) -> Option<(usize, IpProto, bool)> {
259        if bytes_available < Self::FIXED_LEN {
260            return None;
261        }
262
263        let mut len = Self::FIXED_LEN;
264        let mut next_hdr = initial_next_header;
265        let mut is_fragmented = false;
266
267        // Parse extension headers
268        while matches!(
269            next_hdr,
270            IPV6_NEXT_HOPBYHOP
271                | IPV6_NEXT_ROUTING
272                | IPV6_NEXT_DSTOPTS
273                | IPV6_NEXT_MOBILITY
274                | IPV6_NEXT_FRAGMENT
275        ) {
276            if next_hdr == IPV6_NEXT_FRAGMENT {
277                if !allow_fragment {
278                    return None;
279                }
280                is_fragmented = true;
281                // Fragment header is 8 bytes fixed
282                // Get next header field
283                if len >= bytes_available {
284                    return None;
285                }
286                next_hdr = IpProto::from(buf[len]);
287                len += 8;
288            } else {
289                // Other extension headers
290                // Need at least 2 more bytes to read next header and length
291                if len + 2 > bytes_available {
292                    return None;
293                }
294
295                // Extension header format:
296                // - Byte 0: Next Header type
297                // - Byte 1: Header Extension Length (in 8-byte units, not including first 8 bytes)
298                next_hdr = IpProto::from(buf[len]);
299                let ext_len = buf[len + 1];
300
301                // Calculate delta: (1 + ext_len) * 8
302                // This gives us the total size of this extension header
303                let delta = (1 + ext_len as usize) * 8;
304
305                // Zero length is invalid
306                if delta == 0 {
307                    return None;
308                }
309
310                len += delta;
311
312                // Check if we have enough bytes
313                if len > bytes_available {
314                    return None;
315                }
316            }
317        }
318
319        Some((len, next_hdr, is_fragmented))
320    }
321
322    /// Calculate the total header length including extension headers
323    ///
324    /// Returns the total length of IPv6 header + all extension headers,
325    /// or 0 if the packet is fragmented or if there's an error parsing.
326    ///
327    /// `buf` should start at the beginning of the IPv6 header and contain
328    /// at least `bytes_available` bytes.
329    pub fn total_header_len(&self, buf: &[u8], bytes_available: usize) -> usize {
330        // Don't allow fragment headers - return 0 if encountered
331        Self::parse_extension_headers(buf, bytes_available, self.next_header, false)
332            .map(|(len, _, _)| len)
333            .unwrap_or(0)
334    }
335
336    /// Get the upper layer protocol after skipping all extension headers
337    /// Returns the protocol number and whether fragmentation was encountered
338    pub fn upper_layer_protocol(
339        &self,
340        buf: &[u8],
341        bytes_available: usize,
342    ) -> Option<(IpProto, bool)> {
343        // Allow fragment headers - we want to know if packet is fragmented
344        Self::parse_extension_headers(buf, bytes_available, self.next_header, true)
345            .map(|(_, protocol, is_fragmented)| (protocol, is_fragmented))
346    }
347
348    /// Check if there are extension headers to parse
349    #[inline]
350    pub fn should_parse_extensions(&self) -> bool {
351        self.has_extension_headers()
352    }
353}
354
355/// IPv6 Header with extension headers
356#[derive(Debug, Clone)]
357pub struct Ipv6HeaderExt<'a> {
358    pub header: &'a Ipv6Header,
359    pub raw_extensions: &'a [u8],
360}
361
362impl<'a> Ipv6HeaderExt<'a> {
363    /// Get IPv6 extension headers iterator
364    pub fn extensions(&'a self) -> Ipv6ExtensionHeadersIter<'a> {
365        Ipv6ExtensionHeadersIter::new(self.header.next_header, self.raw_extensions)
366    }
367
368    /// Get the total header length including extensions
369    pub fn ext_headers_len(&self) -> usize {
370        Ipv6Header::FIXED_LEN + self.raw_extensions.len()
371    }
372}
373
374impl Deref for Ipv6HeaderExt<'_> {
375    type Target = Ipv6Header;
376
377    #[inline]
378    fn deref(&self) -> &Self::Target {
379        self.header
380    }
381}
382
383impl PacketHeader for Ipv6Header {
384    const NAME: &'static str = "IPv6Header";
385    type InnerType = IpProto;
386
387    #[inline]
388    fn inner_type(&self) -> Self::InnerType {
389        self.next_header
390    }
391
392    /// IPv6 header length including extension headers
393    /// This uses the buffer to parse extension headers and calculate the correct length
394    #[inline]
395    fn total_len(&self, buf: &[u8]) -> usize {
396        // Calculate total header length including extension headers
397        // Allow fragment headers for parsing, return total length
398        Self::parse_extension_headers(buf, buf.len(), self.next_header, true)
399            .map(|(len, _, _)| len)
400            .unwrap_or(Self::FIXED_LEN) // Return at least the fixed header length
401    }
402
403    /// Validate the IPv6 header
404    #[inline]
405    fn is_valid(&self) -> bool {
406        // Version must be 6
407        self.version() == 6
408    }
409}
410
411impl HeaderParser for Ipv6Header {
412    type Output<'a> = Ipv6HeaderExt<'a>;
413
414    #[inline]
415    fn into_view<'a>(header: &'a Self, raw_extensions: &'a [u8]) -> Self::Output<'a> {
416        Ipv6HeaderExt {
417            header,
418            raw_extensions,
419        }
420    }
421}
422
423impl fmt::Display for Ipv6Header {
424    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
425        write!(
426            f,
427            "IPv6 {} -> {} proto={} hop={} len={}",
428            self.src_ip(),
429            self.dst_ip(),
430            self.next_header(),
431            self.hop_limit(),
432            self.total_length()
433        )?;
434
435        if self.flow_label() != 0 {
436            write!(f, " flow=0x{:05x}", self.flow_label())?;
437        }
438
439        if self.has_extension_headers() {
440            write!(f, " +exts")?;
441        }
442
443        Ok(())
444    }
445}
446
447impl fmt::Display for Ipv6HeaderExt<'_> {
448    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
449        write!(f, "{}", self.header)?;
450
451        if !self.raw_extensions.is_empty() {
452            write!(f, " exts=[")?;
453            let mut first = true;
454            for ext in self.extensions().flatten() {
455                if !first {
456                    write!(f, ",")?;
457                }
458                first = false;
459                write!(f, "{}", ext)?;
460            }
461            write!(f, "]")?;
462        }
463
464        Ok(())
465    }
466}
467
468#[cfg(test)]
469mod tests {
470    use super::*;
471    use std::mem;
472
473    #[test]
474    fn test_ipv6_header_size() {
475        assert_eq!(mem::size_of::<Ipv6Header>(), 40);
476        assert_eq!(Ipv6Header::FIXED_LEN, 40);
477    }
478
479    #[test]
480    fn test_ipv6_version() {
481        let header = create_test_header();
482        assert_eq!(header.version(), 6);
483        assert!(header.is_valid());
484    }
485
486    #[test]
487    fn test_ipv6_traffic_class() {
488        let mut header = create_test_header();
489
490        // Set traffic class to 0xAB (10101011)
491        // Version is 6 (0110), so first byte should be 0x6A (01101010)
492        // Second nibble goes to upper 4 bits of second byte: 0xB0
493        header.ver_tc_flow[0] = 0x6A;
494        header.ver_tc_flow[1] = 0xB0;
495
496        assert_eq!(header.traffic_class(), 0xAB);
497        assert_eq!(header.dscp(), 0xAB >> 2);
498        assert_eq!(header.ecn(), 0xAB & 0x03);
499    }
500
501    #[test]
502    fn test_ipv6_flow_label() {
503        let mut header = create_test_header();
504
505        // Set flow label to 0x12345
506        // Lower 4 bits of byte[1], all of byte[2] and byte[3]
507        header.ver_tc_flow[1] = (header.ver_tc_flow[1] & 0xF0) | 0x01;
508        header.ver_tc_flow[2] = 0x23;
509        header.ver_tc_flow[3] = 0x45;
510
511        assert_eq!(header.flow_label(), 0x12345);
512    }
513
514    #[test]
515    fn test_ipv6_addresses() {
516        let header = create_test_header();
517
518        // Test source address: 2001:db8::1
519        let expected_src = Ipv6Addr::new(0x2001, 0x0db8, 0, 0, 0, 0, 0, 1);
520        assert_eq!(header.src_ip(), expected_src);
521
522        // Test destination address: 2001:db8::2
523        let expected_dst = Ipv6Addr::new(0x2001, 0x0db8, 0, 0, 0, 0, 0, 2);
524        assert_eq!(header.dst_ip(), expected_dst);
525    }
526
527    #[test]
528    fn test_ipv6_payload_length() {
529        let mut header = create_test_header();
530        header.payload_length = U16::new(1024);
531
532        assert_eq!(header.payload_length(), 1024);
533        assert_eq!(header.total_length(), 40 + 1024);
534    }
535
536    #[test]
537    fn test_ipv6_next_header() {
538        let mut header = create_test_header();
539
540        header.next_header = IPV6_NEXT_TCP;
541        assert_eq!(header.next_header(), IpProto::TCP);
542        assert!(header.is_transport_protocol());
543        assert!(!header.has_extension_headers());
544
545        header.next_header = IPV6_NEXT_FRAGMENT;
546        assert!(header.has_extension_headers());
547        assert!(!header.is_transport_protocol());
548    }
549
550    #[test]
551    fn test_ipv6_parsing() {
552        let packet = create_test_packet();
553
554        let result = Ipv6Header::from_bytes(&packet);
555        assert!(result.is_ok());
556
557        let (header_ext, payload) = result.unwrap();
558        assert_eq!(header_ext.version(), 6);
559        assert_eq!(header_ext.next_header(), IPV6_NEXT_TCP);
560        assert_eq!(header_ext.hop_limit(), 64);
561        assert_eq!(header_ext.raw_extensions.len(), 0);
562        assert_eq!(payload.len(), 0);
563    }
564
565    #[test]
566    fn test_ipv6_parsing_invalid_version() {
567        let mut packet = create_test_packet();
568        packet[0] = 0x40; // Version 4 instead of 6
569
570        let result = Ipv6Header::from_bytes(&packet);
571        assert!(result.is_err());
572    }
573
574    #[test]
575    fn test_ipv6_parsing_too_small() {
576        let packet = vec![0u8; 39]; // Only 39 bytes, need 40
577
578        let result = Ipv6Header::from_bytes(&packet);
579        assert!(result.is_err());
580    }
581
582    #[test]
583    fn test_ipv6_total_header_len_no_extensions() {
584        let packet = create_test_packet();
585        let (header, _) = Ipv6Header::from_bytes(&packet).unwrap();
586
587        // No extension headers, should return 40
588        assert_eq!(header.total_header_len(&packet, packet.len()), 40);
589    }
590
591    #[test]
592    fn test_ipv6_total_header_len_with_extensions() {
593        let mut packet = create_test_packet();
594
595        // Change next header to Hop-by-Hop Options
596        packet[6] = IPV6_NEXT_HOPBYHOP.into();
597
598        // Add Hop-by-Hop extension header (8 bytes minimum)
599        // Next Header: TCP (6)
600        packet.push(IPV6_NEXT_TCP.into());
601        // Hdr Ext Len: 0 (meaning 8 bytes total: (1+0)*8)
602        packet.push(0);
603        // Padding (6 bytes to make it 8 bytes total)
604        packet.extend_from_slice(&[0, 0, 0, 0, 0, 0]);
605
606        let (header, _) = Ipv6Header::from_bytes(&packet).unwrap();
607
608        // 40 (base) + 8 (hop-by-hop) = 48
609        assert_eq!(header.total_header_len(&packet, packet.len()), 48);
610    }
611
612    #[test]
613    fn test_ipv6_upper_layer_protocol() {
614        let packet = create_test_packet();
615        let (header, _) = Ipv6Header::from_bytes(&packet).unwrap();
616
617        // Direct TCP, no fragmentation
618        let result = header.upper_layer_protocol(&packet, packet.len());
619        assert_eq!(result, Some((IpProto::TCP, false)));
620    }
621
622    #[test]
623    fn test_ipv6_upper_layer_protocol_with_extensions() {
624        let mut packet = create_test_packet();
625
626        // Change next header to Hop-by-Hop Options
627        packet[6] = IPV6_NEXT_HOPBYHOP.into();
628
629        // Add Hop-by-Hop extension header pointing to TCP
630        packet.push(IPV6_NEXT_TCP.into()); // Next Header: TCP
631        packet.push(0); // Hdr Ext Len: 0
632        packet.extend_from_slice(&[0, 0, 0, 0, 0, 0]); // Padding
633
634        let (header, _) = Ipv6Header::from_bytes(&packet).unwrap();
635
636        let result = header.upper_layer_protocol(&packet, packet.len());
637        assert_eq!(result, Some((IpProto::TCP, false)));
638    }
639
640    #[test]
641    fn test_ipv6_from_bytes_with_routing_extension() {
642        let mut packet = create_test_packet();
643
644        // Change next header to Routing
645        packet[6] = IPV6_NEXT_ROUTING.into();
646
647        // Add Routing extension header
648        packet.push(IPV6_NEXT_TCP.into()); // Next Header: TCP
649        packet.push(2); // Hdr Ext Len: 2 (meaning (1+2)*8 = 24 bytes total)
650        packet.push(0); // Routing Type: 0
651        packet.push(1); // Segments Left: 1
652        packet.extend_from_slice(&[0, 0, 0, 0]); // Reserved (4 bytes)
653
654        // Add one IPv6 address (16 bytes) for the route
655        packet.extend_from_slice(&[
656            0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
657            0x00, 0x03,
658        ]);
659
660        // Add some payload data after the extension headers
661        let payload_data = b"Test payload after routing header";
662        packet.extend_from_slice(payload_data);
663
664        // Update payload length in the header (extension headers + payload)
665        let total_payload = 24 + payload_data.len();
666        packet[4] = ((total_payload >> 8) & 0xFF) as u8;
667        packet[5] = (total_payload & 0xFF) as u8;
668
669        // Parse with from_bytes
670        let result = Ipv6Header::from_bytes(&packet);
671        assert!(result.is_ok());
672
673        let (header, payload) = result.unwrap();
674
675        // Verify the payload starts after ALL headers (base + extension)
676        // Should skip 40 (base) + 24 (routing) = 64 bytes
677        assert_eq!(payload.len(), payload_data.len());
678        assert_eq!(payload, payload_data);
679
680        // Verify header info
681        assert_eq!(header.next_header(), IpProto::IPV6_ROUTE);
682        assert!(header.has_extension_headers());
683
684        // Verify total_length() is compatible with header size + payload
685        // total_length = 40 (base) + payload_length
686        // payload_length should include extension headers (24) + actual payload
687        assert_eq!(header.total_length(), 40 + 24 + payload_data.len());
688        assert_eq!(header.payload_length(), 24 + payload_data.len());
689
690        // Verify total_length is at least as large as the total header length
691        let total_header_len = header.total_header_len(&packet, packet.len());
692        assert!(header.total_length() >= total_header_len);
693    }
694
695    #[test]
696    fn test_ipv6_from_bytes_with_hopbyhop_extension() {
697        let mut packet = create_test_packet();
698
699        // Change next header to Hop-by-Hop Options
700        packet[6] = IPV6_NEXT_HOPBYHOP.into();
701
702        // Add Hop-by-Hop extension header (8 bytes)
703        packet.push(IPV6_NEXT_UDP.into()); // Next Header: UDP
704        packet.push(0); // Hdr Ext Len: 0 (meaning (1+0)*8 = 8 bytes total)
705        packet.push(1); // PadN option
706        packet.push(4); // Option length: 4
707        packet.extend_from_slice(&[0, 0, 0, 0]); // Padding data
708
709        // Add some payload data after the extension headers
710        let payload_data = b"UDP payload";
711        packet.extend_from_slice(payload_data);
712
713        // Update payload length in the header (extension headers + payload)
714        let total_payload = 8 + payload_data.len();
715        packet[4] = ((total_payload >> 8) & 0xFF) as u8;
716        packet[5] = (total_payload & 0xFF) as u8;
717
718        // Parse with from_bytes
719        let result = Ipv6Header::from_bytes(&packet);
720        assert!(result.is_ok());
721
722        let (header, payload) = result.unwrap();
723
724        // Verify the payload starts after ALL headers (base + extension)
725        // Should skip 40 (base) + 8 (hop-by-hop) = 48 bytes
726        assert_eq!(payload.len(), payload_data.len());
727        assert_eq!(payload, payload_data);
728
729        // Verify header info
730        assert_eq!(header.next_header(), IpProto::IPV6_HOPOPT);
731        assert!(header.has_extension_headers());
732
733        // Verify total_length() includes extension headers
734        assert_eq!(header.total_length(), 40 + 8 + payload_data.len());
735        assert_eq!(header.payload_length(), 8 + payload_data.len());
736
737        // Verify total_length is at least as large as the total header length
738        let total_header_len = header.total_header_len(&packet, packet.len());
739        assert!(header.total_length() >= total_header_len);
740    }
741
742    #[test]
743    fn test_ipv6_multiple_chained_extension_headers() {
744        let mut packet = create_test_packet();
745
746        // Change next header to Hop-by-Hop Options
747        packet[6] = IPV6_NEXT_HOPBYHOP.into();
748
749        // Add Hop-by-Hop extension header (8 bytes) pointing to Routing
750        packet.push(IPV6_NEXT_ROUTING.into()); // Next Header: Routing
751        packet.push(0); // Hdr Ext Len: 0 (8 bytes total)
752        packet.push(1); // PadN option
753        packet.push(4); // Option length: 4
754        packet.extend_from_slice(&[0, 0, 0, 0]); // Padding data
755
756        // Add Routing extension header (24 bytes) pointing to Destination Options
757        packet.push(IPV6_NEXT_DSTOPTS.into()); // Next Header: Destination Options
758        packet.push(2); // Hdr Ext Len: 2 (24 bytes total)
759        packet.push(0); // Routing Type: 0
760        packet.push(1); // Segments Left: 1
761        packet.extend_from_slice(&[0, 0, 0, 0]); // Reserved (4 bytes)
762                                                 // Add one IPv6 address (16 bytes)
763        packet.extend_from_slice(&[
764            0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
765            0x00, 0x03,
766        ]);
767
768        // Add Destination Options extension header (8 bytes) pointing to TCP
769        packet.push(IPV6_NEXT_TCP.into()); // Next Header: TCP
770        packet.push(0); // Hdr Ext Len: 0 (8 bytes total)
771        packet.push(1); // PadN option
772        packet.push(4); // Option length: 4
773        packet.extend_from_slice(&[0, 0, 0, 0]); // Padding data
774
775        // Add some payload data
776        let payload_data = b"TCP payload after multiple extension headers";
777        packet.extend_from_slice(payload_data);
778
779        // Update payload length: 8 (hop-by-hop) + 24 (routing) + 8 (dest opts) + payload
780        let total_payload = 8 + 24 + 8 + payload_data.len();
781        packet[4] = ((total_payload >> 8) & 0xFF) as u8;
782        packet[5] = (total_payload & 0xFF) as u8;
783
784        // Parse with from_bytes
785        let result = Ipv6Header::from_bytes(&packet);
786        assert!(result.is_ok());
787
788        let (header, payload) = result.unwrap();
789
790        // Verify the payload starts after ALL headers
791        // Should skip 40 (base) + 8 (hop-by-hop) + 24 (routing) + 8 (dest opts) = 80 bytes
792        assert_eq!(payload.len(), payload_data.len());
793        assert_eq!(payload, payload_data);
794
795        // Verify header info
796        assert_eq!(header.next_header(), IpProto::IPV6_HOPOPT);
797        assert!(header.has_extension_headers());
798
799        // Verify total header length calculation
800        assert_eq!(header.total_header_len(&packet, packet.len()), 80);
801
802        // Verify upper layer protocol is TCP
803        let result = header.upper_layer_protocol(&packet, packet.len());
804        assert_eq!(result, Some((IpProto::TCP, false)));
805
806        // Verify total_length() accounts for all extension headers
807        // total_length = 40 (base) + 8 (hop) + 24 (routing) + 8 (dest opts) + payload
808        assert_eq!(header.total_length(), 40 + 8 + 24 + 8 + payload_data.len());
809        assert_eq!(header.payload_length(), 8 + 24 + 8 + payload_data.len());
810
811        // Verify total_length is at least as large as the total header length
812        let total_header_len = header.total_header_len(&packet, packet.len());
813        assert!(header.total_length() >= total_header_len);
814        assert_eq!(header.total_length(), total_header_len + payload_data.len());
815    }
816
817    #[test]
818    fn test_ipv6_routing_extension_header() {
819        let mut packet = create_test_packet();
820
821        // Change next header to Routing
822        packet[6] = IPV6_NEXT_ROUTING.into();
823
824        // Add Routing extension header
825        // Routing Type 0 header (deprecated but simple for testing)
826        packet.push(IPV6_NEXT_TCP.into()); // Next Header: TCP
827        packet.push(2); // Hdr Ext Len: 2 (meaning (1+2)*8 = 24 bytes total)
828        packet.push(0); // Routing Type: 0
829        packet.push(1); // Segments Left: 1
830        packet.extend_from_slice(&[0, 0, 0, 0]); // Reserved (4 bytes)
831
832        // Add one IPv6 address (16 bytes) for the route
833        packet.extend_from_slice(&[
834            0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
835            0x00, 0x03,
836        ]);
837
838        // Update payload length to include the extension header (24 bytes)
839        packet[4] = 0;
840        packet[5] = 24;
841
842        let (header, _) = Ipv6Header::from_bytes(&packet).unwrap();
843
844        // Verify extension header is detected
845        assert!(header.has_extension_headers());
846        assert!(!header.is_transport_protocol());
847
848        // Total header length: 40 (base) + 24 (routing) = 64
849        assert_eq!(header.total_header_len(&packet, packet.len()), 64);
850
851        // Upper layer protocol should find TCP after routing header
852        let result = header.upper_layer_protocol(&packet, packet.len());
853        assert_eq!(result, Some((IpProto::TCP, false)));
854
855        // Verify total_length() is compatible with extension headers
856        let total_header_len = header.total_header_len(&packet, packet.len());
857        assert!(header.total_length() >= total_header_len);
858        assert_eq!(header.total_length(), 40 + 24); // base + routing extension
859        assert_eq!(header.payload_length(), 24);
860    }
861
862    #[test]
863    fn test_ipv6_hopbyhop_extension_header() {
864        let mut packet = create_test_packet();
865
866        // Change next header to Hop-by-Hop Options
867        packet[6] = IPV6_NEXT_HOPBYHOP.into();
868
869        // Add Hop-by-Hop extension header (8 bytes minimum)
870        packet.push(IPV6_NEXT_UDP.into()); // Next Header: UDP
871        packet.push(0); // Hdr Ext Len: 0 (meaning (1+0)*8 = 8 bytes total)
872
873        // Add padding options (6 bytes to make it 8 bytes total)
874        packet.push(1); // PadN option
875        packet.push(4); // Option length: 4
876        packet.extend_from_slice(&[0, 0, 0, 0]); // Padding data
877
878        // Update payload length to include the extension header (8 bytes)
879        packet[4] = 0;
880        packet[5] = 8;
881
882        let (header, _) = Ipv6Header::from_bytes(&packet).unwrap();
883
884        // Verify extension header is detected
885        assert!(header.has_extension_headers());
886        assert!(!header.is_transport_protocol());
887
888        // Total header length: 40 (base) + 8 (hop-by-hop) = 48
889        assert_eq!(header.total_header_len(&packet, packet.len()), 48);
890
891        // Upper layer protocol should find UDP after hop-by-hop header
892        let result = header.upper_layer_protocol(&packet, packet.len());
893        assert_eq!(result, Some((IpProto::UDP, false)));
894
895        // Verify total_length() is compatible with extension headers
896        let total_header_len = header.total_header_len(&packet, packet.len());
897        assert!(header.total_length() >= total_header_len);
898        assert_eq!(header.total_length(), 40 + 8); // base + hop-by-hop extension
899        assert_eq!(header.payload_length(), 8);
900    }
901
902    #[test]
903    fn test_ipv6_total_len_includes_extension_headers() {
904        // Test that total_len() correctly includes extension headers
905        let mut packet = create_test_packet();
906
907        // Test 1: No extension headers - should return 40
908        let (header, _) = Ipv6Header::from_bytes(&packet).unwrap();
909        assert_eq!(header.total_len(&packet), 40);
910
911        // Test 2: With Hop-by-Hop extension header
912        packet = create_test_packet();
913        packet[6] = IPV6_NEXT_HOPBYHOP.into();
914        packet.push(IPV6_NEXT_TCP.into());
915        packet.push(0);
916        packet.extend_from_slice(&[1, 4, 0, 0, 0, 0]);
917        packet[4] = 0;
918        packet[5] = 8;
919
920        let (header, _) = Ipv6Header::from_bytes(&packet).unwrap();
921        assert_eq!(header.total_len(&packet), 48); // 40 + 8
922
923        // Test 3: With Routing extension header
924        packet = create_test_packet();
925        packet[6] = IPV6_NEXT_ROUTING.into();
926        packet.push(IPV6_NEXT_TCP.into());
927        packet.push(2);
928        packet.push(0);
929        packet.push(1);
930        packet.extend_from_slice(&[0, 0, 0, 0]);
931        packet.extend_from_slice(&[
932            0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
933            0x00, 0x03,
934        ]);
935        packet[4] = 0;
936        packet[5] = 24;
937
938        let (header, _) = Ipv6Header::from_bytes(&packet).unwrap();
939        assert_eq!(header.total_len(&packet), 64); // 40 + 24
940
941        // Test 4: Multiple chained extension headers
942        packet = create_test_packet();
943        packet[6] = IPV6_NEXT_HOPBYHOP.into();
944        // Hop-by-Hop -> Routing
945        packet.push(IPV6_NEXT_ROUTING.into());
946        packet.push(0);
947        packet.extend_from_slice(&[1, 4, 0, 0, 0, 0]);
948        // Routing -> TCP
949        packet.push(IPV6_NEXT_TCP.into());
950        packet.push(2);
951        packet.push(0);
952        packet.push(1);
953        packet.extend_from_slice(&[0, 0, 0, 0]);
954        packet.extend_from_slice(&[
955            0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
956            0x00, 0x03,
957        ]);
958        packet[4] = 0;
959        packet[5] = 32;
960
961        let (header, _) = Ipv6Header::from_bytes(&packet).unwrap();
962        assert_eq!(header.total_len(&packet), 72); // 40 + 8 + 24
963    }
964
965    // Helper function to create a test header
966    fn create_test_header() -> Ipv6Header {
967        Ipv6Header {
968            ver_tc_flow: [0x60, 0x00, 0x00, 0x00], // Version 6
969            payload_length: U16::new(0),
970            next_header: IpProto::TCP,
971            hop_limit: 64,
972            src_ip: [0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
973            dst_ip: [0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2],
974        }
975    }
976
977    // Helper function to create a test packet
978    fn create_test_packet() -> Vec<u8> {
979        let mut packet = Vec::new();
980
981        // Version (6) + Traffic Class (0) + Flow Label (0)
982        packet.extend_from_slice(&[0x60, 0x00, 0x00, 0x00]);
983
984        // Payload length (0 bytes)
985        packet.extend_from_slice(&[0x00, 0x00]);
986
987        // Next header (TCP)
988        packet.push(IpProto::TCP.into());
989
990        // Hop limit
991        packet.push(64);
992
993        // Source address: 2001:db8::1
994        packet.extend_from_slice(&[
995            0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
996            0x00, 0x01,
997        ]);
998
999        // Destination address: 2001:db8::2
1000        packet.extend_from_slice(&[
1001            0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1002            0x00, 0x02,
1003        ]);
1004
1005        packet
1006    }
1007
1008    #[test]
1009    fn test_ipv6_header_ext_no_extensions() {
1010        let packet = create_test_packet();
1011        let (header_ext, _) = Ipv6Header::from_bytes(&packet).unwrap();
1012
1013        assert_eq!(header_ext.raw_extensions.len(), 0);
1014        assert!(!header_ext.should_parse_extensions());
1015        assert_eq!(header_ext.ext_headers_len(), 40); // Just the IPv6 header
1016
1017        // Test Deref
1018        assert_eq!(header_ext.version(), 6);
1019        assert_eq!(header_ext.next_header(), IPV6_NEXT_TCP);
1020        assert_eq!(header_ext.hop_limit(), 64);
1021    }
1022
1023    #[test]
1024    fn test_ipv6_header_ext_with_fragment() {
1025        let mut packet = create_test_packet();
1026
1027        // Update next header to Fragment
1028        packet[6] = IPV6_NEXT_FRAGMENT.into();
1029
1030        // Update payload length to 8 (fragment header size)
1031        packet[4] = 0;
1032        packet[5] = 8;
1033
1034        // Add Fragment header
1035        packet.extend_from_slice(&[
1036            IpProto::TCP.into(), // Next Header: TCP
1037            0,                   // Reserved
1038            0x00,
1039            0x01, // Fragment Offset=0, M=1
1040            0x00,
1041            0x00,
1042            0x00,
1043            0x01, // Identification=1
1044        ]);
1045
1046        let (header_ext, _) = Ipv6Header::from_bytes(&packet).unwrap();
1047
1048        assert_eq!(header_ext.raw_extensions.len(), 8);
1049        assert!(header_ext.should_parse_extensions());
1050        assert_eq!(header_ext.ext_headers_len(), 48); // 40 + 8
1051
1052        // Parse extensions
1053        let exts: Vec<_> = header_ext
1054            .extensions()
1055            .collect::<Result<Vec<_>, _>>()
1056            .unwrap();
1057        assert_eq!(exts.len(), 1);
1058
1059        use crate::packet::ipv6::ext::Ipv6ExtensionHeader;
1060        match &exts[0] {
1061            Ipv6ExtensionHeader::Fragment {
1062                next_header,
1063                fragment_offset,
1064                more_fragments,
1065                identification,
1066            } => {
1067                assert_eq!(*next_header, IpProto::TCP);
1068                assert_eq!(*fragment_offset, 0);
1069                assert!(*more_fragments);
1070                assert_eq!(*identification, 1);
1071            }
1072            _ => panic!("Expected Fragment header"),
1073        }
1074    }
1075
1076    #[test]
1077    fn test_ipv6_header_ext_with_hop_by_hop() {
1078        let mut packet = create_test_packet();
1079
1080        // Update next header to Hop-by-Hop
1081        packet[6] = IPV6_NEXT_HOPBYHOP.into();
1082
1083        // Update payload length to 8 (hop-by-hop header size)
1084        packet[4] = 0;
1085        packet[5] = 8;
1086
1087        // Add Hop-by-Hop Options header
1088        packet.extend_from_slice(&[
1089            IpProto::TCP.into(), // Next Header: TCP
1090            0,                   // Hdr Ext Len: 0 (8 bytes total)
1091            1,
1092            4,
1093            0,
1094            0,
1095            0,
1096            0, // PadN option
1097        ]);
1098
1099        let (header_ext, _) = Ipv6Header::from_bytes(&packet).unwrap();
1100
1101        assert_eq!(header_ext.raw_extensions.len(), 8);
1102        assert_eq!(header_ext.ext_headers_len(), 48);
1103
1104        // Parse extensions
1105        let exts: Vec<_> = header_ext
1106            .extensions()
1107            .collect::<Result<Vec<_>, _>>()
1108            .unwrap();
1109        assert_eq!(exts.len(), 1);
1110
1111        use crate::packet::ipv6::ext::Ipv6ExtensionHeader;
1112        match &exts[0] {
1113            Ipv6ExtensionHeader::HopByHop {
1114                next_header,
1115                options,
1116            } => {
1117                assert_eq!(*next_header, IpProto::TCP);
1118                assert_eq!(options.len(), 6);
1119            }
1120            _ => panic!("Expected HopByHop header"),
1121        }
1122    }
1123
1124    #[test]
1125    fn test_ipv6_header_ext_multiple_extensions() {
1126        let mut packet = create_test_packet();
1127
1128        // Update next header to Hop-by-Hop
1129        packet[6] = IPV6_NEXT_HOPBYHOP.into();
1130
1131        // Update payload length to 16 (8 + 8 bytes)
1132        packet[4] = 0;
1133        packet[5] = 16;
1134
1135        // Add Hop-by-Hop Options header (Next: Fragment)
1136        packet.extend_from_slice(&[
1137            IPV6_NEXT_FRAGMENT.into(), // Next Header: Fragment
1138            0,                         // Hdr Ext Len: 0
1139            1,
1140            4,
1141            0,
1142            0,
1143            0,
1144            0, // PadN option
1145        ]);
1146
1147        // Add Fragment header (Next: TCP)
1148        packet.extend_from_slice(&[
1149            IpProto::TCP.into(), // Next Header: TCP
1150            0,                   // Reserved
1151            0x00,
1152            0x00, // Fragment Offset=0, M=0
1153            0x00,
1154            0x00,
1155            0x00,
1156            0x42, // Identification=66
1157        ]);
1158
1159        let (header_ext, _) = Ipv6Header::from_bytes(&packet).unwrap();
1160
1161        assert_eq!(header_ext.raw_extensions.len(), 16);
1162        assert_eq!(header_ext.ext_headers_len(), 56); // 40 + 16
1163
1164        // Parse extensions
1165        let exts: Vec<_> = header_ext
1166            .extensions()
1167            .collect::<Result<Vec<_>, _>>()
1168            .unwrap();
1169        assert_eq!(exts.len(), 2);
1170
1171        use crate::packet::ipv6::ext::Ipv6ExtensionHeader;
1172        assert!(matches!(&exts[0], Ipv6ExtensionHeader::HopByHop { .. }));
1173        assert!(matches!(&exts[1], Ipv6ExtensionHeader::Fragment { .. }));
1174
1175        // Verify next_header chaining
1176        assert_eq!(exts[0].next_header(), IPV6_NEXT_FRAGMENT);
1177        assert_eq!(exts[1].next_header(), IpProto::TCP);
1178    }
1179
1180    #[test]
1181    fn test_ipv6_header_ext_deref() {
1182        let packet = create_test_packet();
1183        let (header_ext, _) = Ipv6Header::from_bytes(&packet).unwrap();
1184
1185        // Test that Deref allows access to all Ipv6Header methods
1186        assert_eq!(header_ext.version(), 6);
1187        assert_eq!(header_ext.traffic_class(), 0);
1188        assert_eq!(header_ext.flow_label(), 0);
1189        assert_eq!(header_ext.payload_length(), 0);
1190        assert_eq!(header_ext.next_header(), IPV6_NEXT_TCP);
1191        assert_eq!(header_ext.hop_limit(), 64);
1192
1193        let src = header_ext.src_ip();
1194        assert_eq!(src.to_string(), "2001:db8::1");
1195
1196        let dst = header_ext.dst_ip();
1197        assert_eq!(dst.to_string(), "2001:db8::2");
1198    }
1199
1200    #[test]
1201    fn test_ipv6_header_ext_with_routing() {
1202        let mut packet = create_test_packet();
1203
1204        // Update next header to Routing
1205        packet[6] = IPV6_NEXT_ROUTING.into();
1206
1207        // Update payload length to 8 (routing header size)
1208        packet[4] = 0;
1209        packet[5] = 8;
1210
1211        // Add Routing header (minimal, Hdr Ext Len = 0)
1212        packet.extend_from_slice(&[
1213            IpProto::TCP.into(), // Next Header: TCP
1214            0,                   // Hdr Ext Len: 0 (8 bytes total)
1215            0,                   // Routing Type: 0
1216            0,                   // Segments Left: 0
1217            0,
1218            0,
1219            0,
1220            0, // Reserved/data
1221        ]);
1222
1223        let (header_ext, _) = Ipv6Header::from_bytes(&packet).unwrap();
1224
1225        assert_eq!(header_ext.raw_extensions.len(), 8);
1226
1227        // Parse extensions
1228        let exts: Vec<_> = header_ext
1229            .extensions()
1230            .collect::<Result<Vec<_>, _>>()
1231            .unwrap();
1232        assert_eq!(exts.len(), 1);
1233
1234        use crate::packet::ipv6::ext::Ipv6ExtensionHeader;
1235        match &exts[0] {
1236            Ipv6ExtensionHeader::Routing {
1237                next_header,
1238                routing_type,
1239                segments_left,
1240                data,
1241            } => {
1242                assert_eq!(*next_header, IpProto::TCP);
1243                assert_eq!(*routing_type, 0);
1244                assert_eq!(*segments_left, 0);
1245                assert_eq!(data.len(), 4);
1246            }
1247            _ => panic!("Expected Routing header"),
1248        }
1249    }
1250}