nex_packet/
ipv6.rs

1use crate::ip::IpNextProtocol;
2use crate::packet::{MutablePacket, Packet};
3use bytes::{BufMut, Bytes, BytesMut};
4use std::net::Ipv6Addr;
5
6#[cfg(feature = "serde")]
7use serde::{Deserialize, Serialize};
8
9pub const IPV6_HEADER_LEN: usize = 40;
10
11#[derive(Debug, Clone, PartialEq, Eq)]
12#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
13pub struct Ipv6Header {
14    pub version: u8,       // 4 bits
15    pub traffic_class: u8, // 8 bits
16    pub flow_label: u32,   // 20 bits
17    pub payload_length: u16,
18    pub next_header: IpNextProtocol,
19    pub hop_limit: u8,
20    pub source: Ipv6Addr,
21    pub destination: Ipv6Addr,
22}
23
24#[derive(Debug, Clone, PartialEq, Eq)]
25pub struct Ipv6Packet {
26    pub header: Ipv6Header,
27    pub extensions: Vec<Ipv6ExtensionHeader>,
28    pub payload: Bytes,
29}
30
31impl Packet for Ipv6Packet {
32    type Header = Ipv6Header;
33
34    fn from_buf(bytes: &[u8]) -> Option<Self> {
35        if bytes.len() < IPV6_HEADER_LEN {
36            return None;
37        }
38
39        // --- Parse the header section ---
40        let version_traffic_flow = &bytes[..4];
41        let version = version_traffic_flow[0] >> 4;
42        let traffic_class =
43            ((version_traffic_flow[0] & 0x0F) << 4) | (version_traffic_flow[1] >> 4);
44        let flow_label = u32::from(version_traffic_flow[1] & 0x0F) << 16
45            | u32::from(version_traffic_flow[2]) << 8
46            | u32::from(version_traffic_flow[3]);
47
48        let payload_length = u16::from_be_bytes([bytes[4], bytes[5]]);
49        let mut next_header = IpNextProtocol::new(bytes[6]);
50        let hop_limit = bytes[7];
51
52        let source = Ipv6Addr::from(<[u8; 16]>::try_from(&bytes[8..24]).ok()?);
53        let destination = Ipv6Addr::from(<[u8; 16]>::try_from(&bytes[24..40]).ok()?);
54
55        let header = Ipv6Header {
56            version,
57            traffic_class,
58            flow_label,
59            payload_length,
60            next_header,
61            hop_limit,
62            source,
63            destination,
64        };
65
66        // --- Walk through the extension headers ---
67        let mut offset = IPV6_HEADER_LEN;
68        let mut extensions = Vec::new();
69
70        loop {
71            match next_header {
72                IpNextProtocol::Hopopt
73                | IpNextProtocol::Ipv6Route
74                | IpNextProtocol::Ipv6Frag
75                | IpNextProtocol::Ipv6Opts => {
76                    if offset + 2 > bytes.len() {
77                        return None;
78                    }
79
80                    let nh = IpNextProtocol::new(bytes[offset]);
81                    let ext_len = bytes[offset + 1] as usize;
82
83                    match next_header {
84                        IpNextProtocol::Hopopt | IpNextProtocol::Ipv6Opts => {
85                            let total_len = 8 + ext_len * 8;
86                            if offset + total_len > bytes.len() {
87                                return None;
88                            }
89
90                            let data =
91                                Bytes::copy_from_slice(&bytes[offset + 2..offset + total_len]);
92                            let ext = match next_header {
93                                IpNextProtocol::Hopopt => {
94                                    Ipv6ExtensionHeader::HopByHop { next: nh, data }
95                                }
96                                IpNextProtocol::Ipv6Opts => {
97                                    Ipv6ExtensionHeader::Destination { next: nh, data }
98                                }
99                                _ => Ipv6ExtensionHeader::Raw {
100                                    next: nh,
101                                    raw: Bytes::copy_from_slice(&bytes[offset..offset + total_len]),
102                                },
103                            };
104
105                            extensions.push(ext);
106                            next_header = nh;
107                            offset += total_len;
108                        }
109
110                        IpNextProtocol::Ipv6Route => {
111                            if offset + 4 > bytes.len() {
112                                return None;
113                            }
114
115                            let routing_type = bytes[offset + 2];
116                            let segments_left = bytes[offset + 3];
117                            let total_len = 8 + ext_len * 8;
118                            if offset + total_len > bytes.len() {
119                                return None;
120                            }
121
122                            let data =
123                                Bytes::copy_from_slice(&bytes[offset + 4..offset + total_len]);
124                            extensions.push(Ipv6ExtensionHeader::Routing {
125                                next: nh,
126                                routing_type,
127                                segments_left,
128                                data,
129                            });
130
131                            next_header = nh;
132                            offset += total_len;
133                        }
134
135                        IpNextProtocol::Ipv6Frag => {
136                            if offset + 8 > bytes.len() {
137                                return None;
138                            }
139
140                            //let reserved = bytes[offset + 1];
141                            let frag_off_flags =
142                                u16::from_be_bytes([bytes[offset + 2], bytes[offset + 3]]);
143                            let offset_val = frag_off_flags >> 3;
144                            let more = (frag_off_flags & 0x1) != 0;
145                            let id = u32::from_be_bytes([
146                                bytes[offset + 4],
147                                bytes[offset + 5],
148                                bytes[offset + 6],
149                                bytes[offset + 7],
150                            ]);
151
152                            extensions.push(Ipv6ExtensionHeader::Fragment {
153                                next: nh,
154                                offset: offset_val,
155                                more,
156                                id,
157                            });
158
159                            next_header = nh;
160                            offset += 8;
161                        }
162
163                        _ => break,
164                    }
165                }
166
167                _ => break,
168            }
169        }
170
171        let payload = Bytes::copy_from_slice(&bytes[offset..]);
172        Some(Ipv6Packet {
173            header,
174            extensions,
175            payload,
176        })
177    }
178    fn from_bytes(bytes: Bytes) -> Option<Self> {
179        Self::from_buf(&bytes)
180    }
181
182    fn to_bytes(&self) -> Bytes {
183        let mut buf = BytesMut::with_capacity(self.total_len());
184
185        // --- 1. Basic header (first 40 bytes) ---
186        let vtf_1 = (self.header.version << 4) | (self.header.traffic_class >> 4);
187        let vtf_2 =
188            ((self.header.traffic_class & 0x0F) << 4) | ((self.header.flow_label >> 16) as u8);
189        let vtf_3 = (self.header.flow_label >> 8) as u8;
190        let vtf_4 = self.header.flow_label as u8;
191
192        buf.put_u8(vtf_1);
193        buf.put_u8(vtf_2);
194        buf.put_u8(vtf_3);
195        buf.put_u8(vtf_4);
196        buf.put_u16(self.header.payload_length);
197        // First next_header (first extension header if present)
198        let first_next_header = self
199            .extensions
200            .first()
201            .map(|ext| ext.next_protocol())
202            .unwrap_or(self.header.next_header);
203        buf.put_u8(first_next_header.value());
204        buf.put_u8(self.header.hop_limit);
205        buf.extend_from_slice(&self.header.source.octets());
206        buf.extend_from_slice(&self.header.destination.octets());
207
208        // --- 2. Encode the extension header chain ---
209        for ext in &self.extensions {
210            match ext {
211                Ipv6ExtensionHeader::HopByHop { next, data }
212                | Ipv6ExtensionHeader::Destination { next, data } => {
213                    let hdr_ext_len = ((data.len() + 6) / 8) as u8 - 1;
214                    buf.put_u8(next.value());
215                    buf.put_u8(hdr_ext_len);
216                    buf.extend_from_slice(data);
217                    // Padding (8 byte alignment)
218                    while (2 + data.len()) % 8 != 0 {
219                        buf.put_u8(0);
220                    }
221                }
222
223                Ipv6ExtensionHeader::Routing {
224                    next,
225                    routing_type,
226                    segments_left,
227                    data,
228                } => {
229                    let hdr_ext_len = ((data.len() + 4 + 6) / 8) as u8 - 1;
230                    buf.put_u8(next.value());
231                    buf.put_u8(hdr_ext_len);
232                    buf.put_u8(*routing_type);
233                    buf.put_u8(*segments_left);
234                    buf.extend_from_slice(data);
235                    while (4 + data.len()) % 8 != 0 {
236                        buf.put_u8(0);
237                    }
238                }
239
240                Ipv6ExtensionHeader::Fragment {
241                    next,
242                    offset,
243                    more,
244                    id,
245                } => {
246                    buf.put_u8(next.value());
247                    buf.put_u8(0); // reserved
248                    let offset_flags = (offset << 3) | if *more { 1 } else { 0 };
249                    buf.put_u16(offset_flags);
250                    buf.put_u32(*id);
251                }
252
253                Ipv6ExtensionHeader::Raw { next: _, raw } => {
254                    // Note: assume the raw header already includes the next field
255                    buf.extend_from_slice(&raw[..]);
256                }
257            }
258        }
259
260        // --- 3. Payload ---
261        buf.extend_from_slice(&self.payload);
262
263        buf.freeze()
264    }
265
266    fn header(&self) -> Bytes {
267        self.to_bytes().slice(..IPV6_HEADER_LEN)
268    }
269
270    fn payload(&self) -> Bytes {
271        self.payload.clone()
272    }
273
274    fn header_len(&self) -> usize {
275        IPV6_HEADER_LEN
276    }
277
278    fn payload_len(&self) -> usize {
279        self.payload.len()
280    }
281
282    fn total_len(&self) -> usize {
283        self.header_len() + self.payload_len()
284    }
285
286    fn into_parts(self) -> (Self::Header, Bytes) {
287        (self.header, self.payload)
288    }
289}
290
291impl Ipv6Packet {
292    pub fn total_len(&self) -> usize {
293        IPV6_HEADER_LEN
294            + self.extensions.iter().map(|ext| ext.len()).sum::<usize>()
295            + self.payload.len()
296    }
297    pub fn get_extension(&self, kind: ExtensionHeaderType) -> Option<&Ipv6ExtensionHeader> {
298        self.extensions.iter().find(|ext| ext.kind() == kind)
299    }
300}
301
302/// Represents a mutable IPv6 packet.
303pub struct MutableIpv6Packet<'a> {
304    buffer: &'a mut [u8],
305}
306
307impl<'a> MutablePacket<'a> for MutableIpv6Packet<'a> {
308    type Packet = Ipv6Packet;
309
310    fn new(buffer: &'a mut [u8]) -> Option<Self> {
311        if buffer.len() < IPV6_HEADER_LEN {
312            None
313        } else {
314            Some(Self { buffer })
315        }
316    }
317
318    fn packet(&self) -> &[u8] {
319        &*self.buffer
320    }
321
322    fn packet_mut(&mut self) -> &mut [u8] {
323        &mut *self.buffer
324    }
325
326    fn header(&self) -> &[u8] {
327        &self.packet()[..IPV6_HEADER_LEN]
328    }
329
330    fn header_mut(&mut self) -> &mut [u8] {
331        let (header, _) = (&mut *self.buffer).split_at_mut(IPV6_HEADER_LEN);
332        header
333    }
334
335    fn payload(&self) -> &[u8] {
336        &self.packet()[IPV6_HEADER_LEN..]
337    }
338
339    fn payload_mut(&mut self) -> &mut [u8] {
340        let (_, payload) = (&mut *self.buffer).split_at_mut(IPV6_HEADER_LEN);
341        payload
342    }
343}
344
345impl<'a> MutableIpv6Packet<'a> {
346    /// Create a new packet without checking length.
347    pub fn new_unchecked(buffer: &'a mut [u8]) -> Self {
348        Self { buffer }
349    }
350
351    fn raw(&self) -> &[u8] {
352        &*self.buffer
353    }
354
355    fn raw_mut(&mut self) -> &mut [u8] {
356        &mut *self.buffer
357    }
358
359    pub fn payload_len(&self) -> usize {
360        self.raw().len().saturating_sub(IPV6_HEADER_LEN)
361    }
362
363    pub fn get_version(&self) -> u8 {
364        self.raw()[0] >> 4
365    }
366
367    pub fn set_version(&mut self, version: u8) {
368        let buf = self.raw_mut();
369        buf[0] = (buf[0] & 0x0F) | ((version & 0x0F) << 4);
370    }
371
372    pub fn get_traffic_class(&self) -> u8 {
373        ((self.raw()[0] & 0x0F) << 4) | (self.raw()[1] >> 4)
374    }
375
376    pub fn set_traffic_class(&mut self, class: u8) {
377        let buf = self.raw_mut();
378        buf[0] = (buf[0] & 0xF0) | ((class >> 4) & 0x0F);
379        buf[1] = (buf[1] & 0x0F) | ((class & 0x0F) << 4);
380    }
381
382    pub fn get_flow_label(&self) -> u32 {
383        let buf = self.raw();
384        let high = (buf[1] as u32 & 0x0F) << 16;
385        let mid = (buf[2] as u32) << 8;
386        let low = buf[3] as u32;
387        high | mid | low
388    }
389
390    pub fn set_flow_label(&mut self, label: u32) {
391        let buf = self.raw_mut();
392        buf[1] = (buf[1] & 0xF0) | (((label >> 16) as u8) & 0x0F);
393        buf[2] = (label >> 8) as u8;
394        buf[3] = label as u8;
395    }
396
397    pub fn get_payload_length(&self) -> u16 {
398        u16::from_be_bytes([self.raw()[4], self.raw()[5]])
399    }
400
401    pub fn set_payload_length(&mut self, length: u16) {
402        self.raw_mut()[4..6].copy_from_slice(&length.to_be_bytes());
403    }
404
405    pub fn get_next_header(&self) -> IpNextProtocol {
406        IpNextProtocol::new(self.raw()[6])
407    }
408
409    pub fn set_next_header(&mut self, proto: IpNextProtocol) {
410        self.raw_mut()[6] = proto.value();
411    }
412
413    pub fn get_hop_limit(&self) -> u8 {
414        self.raw()[7]
415    }
416
417    pub fn set_hop_limit(&mut self, value: u8) {
418        self.raw_mut()[7] = value;
419    }
420
421    pub fn get_source(&self) -> Ipv6Addr {
422        Ipv6Addr::from(<[u8; 16]>::try_from(&self.raw()[8..24]).unwrap())
423    }
424
425    pub fn set_source(&mut self, addr: Ipv6Addr) {
426        self.raw_mut()[8..24].copy_from_slice(&addr.octets());
427    }
428
429    pub fn get_destination(&self) -> Ipv6Addr {
430        Ipv6Addr::from(<[u8; 16]>::try_from(&self.raw()[24..40]).unwrap())
431    }
432
433    pub fn set_destination(&mut self, addr: Ipv6Addr) {
434        self.raw_mut()[24..40].copy_from_slice(&addr.octets());
435    }
436}
437
438#[derive(Debug, Clone, PartialEq, Eq)]
439pub enum ExtensionHeaderType {
440    HopByHop,
441    Destination,
442    Routing,
443    Fragment,
444    Unknown(u8),
445}
446
447#[derive(Debug, Clone, PartialEq, Eq)]
448pub enum Ipv6ExtensionHeader {
449    HopByHop {
450        next: IpNextProtocol,
451        data: Bytes,
452    },
453    Destination {
454        next: IpNextProtocol,
455        data: Bytes,
456    },
457    Routing {
458        next: IpNextProtocol,
459        routing_type: u8,
460        segments_left: u8,
461        data: Bytes,
462    },
463    Fragment {
464        next: IpNextProtocol,
465        offset: u16,
466        more: bool,
467        id: u32,
468    },
469    Raw {
470        next: IpNextProtocol,
471        raw: Bytes,
472    },
473}
474
475impl Ipv6ExtensionHeader {
476    pub fn next_protocol(&self) -> IpNextProtocol {
477        match self {
478            Ipv6ExtensionHeader::HopByHop { next, .. } => *next,
479            Ipv6ExtensionHeader::Destination { next, .. } => *next,
480            Ipv6ExtensionHeader::Routing { next, .. } => *next,
481            Ipv6ExtensionHeader::Fragment { next, .. } => *next,
482            Ipv6ExtensionHeader::Raw { next, .. } => *next,
483        }
484    }
485    pub fn len(&self) -> usize {
486        match self {
487            Ipv6ExtensionHeader::HopByHop { data, .. }
488            | Ipv6ExtensionHeader::Destination { data, .. } => {
489                let base = 2 + data.len();
490                (base + 7) / 8 * 8 // padding to multiple of 8
491            }
492            Ipv6ExtensionHeader::Routing { data, .. } => {
493                let base = 4 + data.len();
494                (base + 7) / 8 * 8
495            }
496            Ipv6ExtensionHeader::Fragment { .. } => 8,
497            Ipv6ExtensionHeader::Raw { raw, .. } => raw.len(),
498        }
499    }
500    pub fn kind(&self) -> ExtensionHeaderType {
501        match self {
502            Ipv6ExtensionHeader::HopByHop { .. } => ExtensionHeaderType::HopByHop,
503            Ipv6ExtensionHeader::Destination { .. } => ExtensionHeaderType::Destination,
504            Ipv6ExtensionHeader::Routing { .. } => ExtensionHeaderType::Routing,
505            Ipv6ExtensionHeader::Fragment { .. } => ExtensionHeaderType::Fragment,
506            Ipv6ExtensionHeader::Raw { raw, .. } => {
507                // Even for Raw we can read the first byte to guess the kind
508                let kind = raw.get(0).copied().unwrap_or(0xff);
509                match kind {
510                    0 => ExtensionHeaderType::HopByHop,
511                    43 => ExtensionHeaderType::Routing,
512                    44 => ExtensionHeaderType::Fragment,
513                    60 => ExtensionHeaderType::Destination,
514                    other => ExtensionHeaderType::Unknown(other),
515                }
516            }
517        }
518    }
519}
520
521#[cfg(test)]
522mod tests {
523    use super::*;
524    use crate::ip::IpNextProtocol;
525    use std::net::Ipv6Addr;
526
527    #[test]
528    fn test_ipv6_basic_header_fields() {
529        let header = Ipv6Header {
530            version: 6,
531            traffic_class: 0xaa,
532            flow_label: 0x12345,
533            payload_length: 0,
534            next_header: IpNextProtocol::Udp,
535            hop_limit: 64,
536            source: Ipv6Addr::LOCALHOST,
537            destination: Ipv6Addr::UNSPECIFIED,
538        };
539
540        let packet = Ipv6Packet {
541            header: header.clone(),
542            extensions: vec![],
543            payload: Bytes::new(),
544        };
545
546        assert_eq!(packet.header.version, 6);
547        assert_eq!(packet.header.traffic_class, 0xaa);
548        assert_eq!(packet.header.flow_label, 0x12345);
549        assert_eq!(packet.header.payload_length, 0);
550        assert_eq!(packet.header.next_header, IpNextProtocol::Udp);
551        assert_eq!(packet.header.hop_limit, 64);
552        assert_eq!(packet.header.source, Ipv6Addr::LOCALHOST);
553        assert_eq!(packet.header.destination, Ipv6Addr::UNSPECIFIED);
554
555        let raw = packet.to_bytes();
556        assert_eq!(raw.len(), IPV6_HEADER_LEN);
557        let reparsed = Ipv6Packet::from_bytes(raw.clone()).unwrap();
558        assert_eq!(reparsed.header, packet.header);
559    }
560
561    #[test]
562    fn test_ipv6_from_bytes_parsing() {
563        use bytes::Bytes;
564
565        let raw_bytes = Bytes::from_static(&[
566            // Version(6), Traffic Class(0xa), Flow Label(0x12345)
567            0x60, 0xA1, 0x23, 0x45, // Payload Length: 8 bytes
568            0x00, 0x08, // Next Header: TCP (6)
569            0x06, // Hop Limit
570            0x40, // Source IP
571            0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x1a, 0x2b, 0xff, 0xfe, 0x1a,
572            0x2b, 0x3c, // Destination IP
573            0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
574            0x00, 0x02, // Payload (dummy 8 bytes)
575            b'H', b'e', b'l', b'l', b'o', b'!', b'!', b'\n',
576        ]);
577
578        let parsed = Ipv6Packet::from_bytes(raw_bytes.clone()).expect("should parse successfully");
579
580        assert_eq!(parsed.header.version, 6);
581        assert_eq!(parsed.header.traffic_class, 0xa);
582        assert_eq!(parsed.header.flow_label, 0x12345);
583        assert_eq!(parsed.header.payload_length, 8);
584        assert_eq!(parsed.header.next_header, IpNextProtocol::Tcp);
585        assert_eq!(parsed.header.hop_limit, 0x40);
586        assert_eq!(
587            parsed.header.source,
588            "fe80::21a:2bff:fe1a:2b3c".parse::<Ipv6Addr>().unwrap()
589        );
590        assert_eq!(
591            parsed.header.destination,
592            "ff02::2".parse::<Ipv6Addr>().unwrap()
593        );
594        assert_eq!(&parsed.payload[..], b"Hello!!\n");
595        assert_eq!(parsed.extensions.len(), 0);
596        assert_eq!(parsed.to_bytes(), raw_bytes);
597    }
598
599    #[test]
600    fn test_ipv6_payload_roundtrip() {
601        use bytes::Bytes;
602
603        let payload = Bytes::from_static(b"HELLO_WORLDS");
604        let packet = Ipv6Packet {
605            header: super::Ipv6Header {
606                version: 6,
607                traffic_class: 0,
608                flow_label: 0,
609                payload_length: payload.len() as u16,
610                next_header: IpNextProtocol::Tcp,
611                hop_limit: 32,
612                source: Ipv6Addr::LOCALHOST,
613                destination: Ipv6Addr::LOCALHOST,
614            },
615            extensions: vec![],
616            payload: payload.clone(),
617        };
618
619        let raw = packet.to_bytes();
620        let parsed = Ipv6Packet::from_bytes(raw.clone()).unwrap();
621
622        assert_eq!(parsed.header, packet.header);
623        assert_eq!(parsed.payload, payload);
624        assert_eq!(raw.len(), packet.total_len());
625    }
626
627    #[test]
628    fn test_ipv6_truncated_packet_rejected() {
629        use bytes::Bytes;
630
631        let short = Bytes::from_static(&[0u8; 20]); // insufficient
632        assert!(Ipv6Packet::from_bytes(short).is_none());
633    }
634
635    #[test]
636    fn test_ipv6_total_len_computation() {
637        use bytes::Bytes;
638
639        let ext = Ipv6ExtensionHeader::Fragment {
640            next: IpNextProtocol::Tcp,
641            offset: 1,
642            more: true,
643            id: 42,
644        };
645
646        let packet = Ipv6Packet {
647            header: Ipv6Header {
648                version: 6,
649                traffic_class: 0,
650                flow_label: 0,
651                payload_length: 8,
652                next_header: IpNextProtocol::Tcp,
653                hop_limit: 1,
654                source: Ipv6Addr::LOCALHOST,
655                destination: Ipv6Addr::LOCALHOST,
656            },
657            extensions: vec![ext],
658            payload: Bytes::from_static(b"ABCDEFGH"),
659        };
660
661        let expected_len = IPV6_HEADER_LEN + 8 + 8; // header + fragment ext + payload
662        assert_eq!(packet.total_len(), expected_len);
663        assert_eq!(packet.to_bytes().len(), expected_len);
664    }
665
666    #[test]
667    fn test_extension_kind_known_variants() {
668        let hop = Ipv6ExtensionHeader::HopByHop {
669            next: IpNextProtocol::Tcp,
670            data: Bytes::from_static(&[1, 2, 3, 4]),
671        };
672        assert_eq!(hop.kind(), ExtensionHeaderType::HopByHop);
673
674        let dst = Ipv6ExtensionHeader::Destination {
675            next: IpNextProtocol::Udp,
676            data: Bytes::from_static(&[9, 8, 7]),
677        };
678        assert_eq!(dst.kind(), ExtensionHeaderType::Destination);
679
680        let route = Ipv6ExtensionHeader::Routing {
681            next: IpNextProtocol::Tcp,
682            routing_type: 0,
683            segments_left: 0,
684            data: Bytes::from_static(&[1, 2, 3]),
685        };
686        assert_eq!(route.kind(), ExtensionHeaderType::Routing);
687
688        let frag = Ipv6ExtensionHeader::Fragment {
689            next: IpNextProtocol::Udp,
690            offset: 0,
691            more: false,
692            id: 12345,
693        };
694        assert_eq!(frag.kind(), ExtensionHeaderType::Fragment);
695    }
696
697    #[test]
698    fn test_extension_kind_raw_known() {
699        let raw_routing = Ipv6ExtensionHeader::Raw {
700            next: IpNextProtocol::new(43),
701            raw: Bytes::from_static(&[43, 1, 2, 3]),
702        };
703        assert_eq!(raw_routing.kind(), ExtensionHeaderType::Routing);
704
705        let raw_frag = Ipv6ExtensionHeader::Raw {
706            next: IpNextProtocol::new(44),
707            raw: Bytes::from_static(&[44, 0, 0, 0]),
708        };
709        assert_eq!(raw_frag.kind(), ExtensionHeaderType::Fragment);
710    }
711
712    #[test]
713    fn test_extension_kind_raw_unknown() {
714        let raw_unknown = Ipv6ExtensionHeader::Raw {
715            next: IpNextProtocol::new(250),
716            raw: Bytes::from_static(&[250, 0, 1, 2]),
717        };
718        assert_eq!(raw_unknown.kind(), ExtensionHeaderType::Unknown(250));
719    }
720
721    #[test]
722    fn test_mutable_ipv6_packet_mutations() {
723        let mut raw = [
724            0x60, 0x00, 0x00, 0x00, // version, traffic class, flow label
725            0x00, 0x04, // payload length
726            0x11, // next header (UDP)
727            0x40, // hop limit
728            // source
729            0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, // destination
730            0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, // payload
731            0xde, 0xad, 0xbe, 0xef,
732        ];
733
734        let mut packet = MutableIpv6Packet::new(&mut raw).expect("mutable ipv6");
735        assert_eq!(packet.get_version(), 6);
736        packet.set_hop_limit(0x7f);
737        packet.set_next_header(IpNextProtocol::Tcp);
738        packet.set_flow_label(0x12345);
739        packet.set_source(Ipv6Addr::LOCALHOST);
740        packet.payload_mut()[0] = 0xaa;
741
742        let frozen = packet.freeze().expect("freeze");
743        assert_eq!(frozen.header.hop_limit, 0x7f);
744        assert_eq!(frozen.header.next_header, IpNextProtocol::Tcp);
745        assert_eq!(frozen.header.flow_label, 0x12345);
746        assert_eq!(frozen.header.source, Ipv6Addr::LOCALHOST);
747        assert_eq!(frozen.payload[0], 0xaa);
748    }
749}