nex_packet/
icmpv6.rs

1//! An ICMPv6 packet abstraction.
2
3use crate::checksum::{ChecksumMode, ChecksumState, TransportChecksumContext};
4use crate::ip::IpNextProtocol;
5use crate::ipv6::IPV6_HEADER_LEN;
6use crate::{
7    ethernet::ETHERNET_HEADER_LEN,
8    packet::{MutablePacket, Packet},
9};
10use std::net::Ipv6Addr;
11
12use bytes::Bytes;
13#[cfg(feature = "serde")]
14use serde::{Deserialize, Serialize};
15
16/// ICMPv6 Common Header Length.
17pub const ICMPV6_COMMON_HEADER_LEN: usize = 4;
18/// ICMPv6 Header Length. Including the common header (4 bytes) and the type specific header (4 bytes).
19pub const ICMPV6_HEADER_LEN: usize = 8;
20/// ICMPv6 Minimum Packet Length.
21pub const ICMPV6_PACKET_LEN: usize = ETHERNET_HEADER_LEN + IPV6_HEADER_LEN + ICMPV6_HEADER_LEN;
22/// ICMPv6 IP Packet Length.
23pub const ICMPV6_IP_PACKET_LEN: usize = IPV6_HEADER_LEN + ICMPV6_HEADER_LEN;
24
25/// Represents the ICMPv6 types.
26/// <https://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xhtml>
27#[repr(u8)]
28#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
29#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
30pub enum Icmpv6Type {
31    DestinationUnreachable,
32    PacketTooBig,
33    TimeExceeded,
34    ParameterProblem,
35    EchoRequest,
36    EchoReply,
37    MulticastListenerQuery,
38    MulticastListenerReport,
39    MulticastListenerDone,
40    RouterSolicitation,
41    RouterAdvertisement,
42    NeighborSolicitation,
43    NeighborAdvertisement,
44    RedirectMessage,
45    RouterRenumbering,
46    NodeInformationQuery,
47    NodeInformationResponse,
48    InverseNeighborDiscoverySolicitation,
49    InverseNeighborDiscoveryAdvertisement,
50    Version2MulticastListenerReport,
51    HomeAgentAddressDiscoveryRequest,
52    HomeAgentAddressDiscoveryReply,
53    MobilePrefixSolicitation,
54    MobilePrefixAdvertisement,
55    CertificationPathSolicitationMessage,
56    CertificationPathAdvertisementMessage,
57    ExperimentalMobilityProtocols,
58    MulticastRouterAdvertisement,
59    MulticastRouterSolicitation,
60    MulticastRouterTermination,
61    FMIPv6Messages,
62    RPLControlMessage,
63    ILNPv6LocatorUpdateMessage,
64    DuplicateAddressRequest,
65    DuplicateAddressConfirmation,
66    MPLControlMessage,
67    ExtendedEchoRequest,
68    ExtendedEchoReply,
69    Unknown(u8),
70}
71
72impl Icmpv6Type {
73    pub fn new(value: u8) -> Self {
74        match value {
75            1 => Icmpv6Type::DestinationUnreachable,
76            2 => Icmpv6Type::PacketTooBig,
77            3 => Icmpv6Type::TimeExceeded,
78            4 => Icmpv6Type::ParameterProblem,
79            128 => Icmpv6Type::EchoRequest,
80            129 => Icmpv6Type::EchoReply,
81            130 => Icmpv6Type::MulticastListenerQuery,
82            131 => Icmpv6Type::MulticastListenerReport,
83            132 => Icmpv6Type::MulticastListenerDone,
84            133 => Icmpv6Type::RouterSolicitation,
85            134 => Icmpv6Type::RouterAdvertisement,
86            135 => Icmpv6Type::NeighborSolicitation,
87            136 => Icmpv6Type::NeighborAdvertisement,
88            137 => Icmpv6Type::RedirectMessage,
89            138 => Icmpv6Type::RouterRenumbering,
90            139 => Icmpv6Type::NodeInformationQuery,
91            140 => Icmpv6Type::NodeInformationResponse,
92            141 => Icmpv6Type::InverseNeighborDiscoverySolicitation,
93            142 => Icmpv6Type::InverseNeighborDiscoveryAdvertisement,
94            143 => Icmpv6Type::Version2MulticastListenerReport,
95            144 => Icmpv6Type::HomeAgentAddressDiscoveryRequest,
96            145 => Icmpv6Type::HomeAgentAddressDiscoveryReply,
97            146 => Icmpv6Type::MobilePrefixSolicitation,
98            147 => Icmpv6Type::MobilePrefixAdvertisement,
99            148 => Icmpv6Type::CertificationPathSolicitationMessage,
100            149 => Icmpv6Type::CertificationPathAdvertisementMessage,
101            150 => Icmpv6Type::ExperimentalMobilityProtocols,
102            151 => Icmpv6Type::MulticastRouterAdvertisement,
103            152 => Icmpv6Type::MulticastRouterSolicitation,
104            153 => Icmpv6Type::MulticastRouterTermination,
105            154 => Icmpv6Type::FMIPv6Messages,
106            155 => Icmpv6Type::RPLControlMessage,
107            156 => Icmpv6Type::ILNPv6LocatorUpdateMessage,
108            157 => Icmpv6Type::DuplicateAddressRequest,
109            158 => Icmpv6Type::DuplicateAddressConfirmation,
110            159 => Icmpv6Type::MPLControlMessage,
111            160 => Icmpv6Type::ExtendedEchoRequest,
112            161 => Icmpv6Type::ExtendedEchoReply,
113            n => Icmpv6Type::Unknown(n),
114        }
115    }
116    pub fn name(&self) -> &'static str {
117        match self {
118            Icmpv6Type::DestinationUnreachable => "Destination Unreachable",
119            Icmpv6Type::PacketTooBig => "Packet Too Big",
120            Icmpv6Type::TimeExceeded => "Time Exceeded",
121            Icmpv6Type::ParameterProblem => "Parameter Problem",
122            Icmpv6Type::EchoRequest => "Echo Request",
123            Icmpv6Type::EchoReply => "Echo Reply",
124            Icmpv6Type::MulticastListenerQuery => "Multicast Listener Query",
125            Icmpv6Type::MulticastListenerReport => "Multicast Listener Report",
126            Icmpv6Type::MulticastListenerDone => "Multicast Listener Done",
127            Icmpv6Type::RouterSolicitation => "Router Solicitation",
128            Icmpv6Type::RouterAdvertisement => "Router Advertisement",
129            Icmpv6Type::NeighborSolicitation => "Neighbor Solicitation",
130            Icmpv6Type::NeighborAdvertisement => "Neighbor Advertisement",
131            Icmpv6Type::RedirectMessage => "Redirect Message",
132            Icmpv6Type::RouterRenumbering => "Router Renumbering",
133            Icmpv6Type::NodeInformationQuery => "Node Information Query",
134            Icmpv6Type::NodeInformationResponse => "Node Information Response",
135            Icmpv6Type::InverseNeighborDiscoverySolicitation => {
136                "Inverse Neighbor Discovery Solicitation"
137            }
138            Icmpv6Type::InverseNeighborDiscoveryAdvertisement => {
139                "Inverse Neighbor Discovery Advertisement"
140            }
141            Icmpv6Type::Version2MulticastListenerReport => "Version 2 Multicast Listener Report",
142            Icmpv6Type::HomeAgentAddressDiscoveryRequest => "Home Agent Address Discovery Request",
143            Icmpv6Type::HomeAgentAddressDiscoveryReply => "Home Agent Address Discovery Reply",
144            Icmpv6Type::MobilePrefixSolicitation => "Mobile Prefix Solicitation",
145            Icmpv6Type::MobilePrefixAdvertisement => "Mobile Prefix Advertisement",
146            Icmpv6Type::CertificationPathSolicitationMessage => {
147                "Certification Path Solicitation Message"
148            }
149            Icmpv6Type::CertificationPathAdvertisementMessage => {
150                "Certification Path Advertisement Message"
151            }
152            Icmpv6Type::ExperimentalMobilityProtocols => "Experimental Mobility Protocols",
153            Icmpv6Type::MulticastRouterAdvertisement => "Multicast Router Advertisement",
154            Icmpv6Type::MulticastRouterSolicitation => "Multicast Router Solicitation",
155            Icmpv6Type::MulticastRouterTermination => "Multicast Router Termination",
156            Icmpv6Type::FMIPv6Messages => "FMIPv6 Messages",
157            Icmpv6Type::RPLControlMessage => "RPL Control Message",
158            Icmpv6Type::ILNPv6LocatorUpdateMessage => "ILNPv6 Locator Update Message",
159            Icmpv6Type::DuplicateAddressRequest => "Duplicate Address Request",
160            Icmpv6Type::DuplicateAddressConfirmation => "Duplicate Address Confirmation",
161            Icmpv6Type::MPLControlMessage => "MPL Control Message",
162            Icmpv6Type::ExtendedEchoRequest => "Extended Echo Request",
163            Icmpv6Type::ExtendedEchoReply => "Extended Echo Reply",
164            Icmpv6Type::Unknown(_) => "Unknown",
165        }
166    }
167    pub fn value(&self) -> u8 {
168        match self {
169            Icmpv6Type::DestinationUnreachable => 1,
170            Icmpv6Type::PacketTooBig => 2,
171            Icmpv6Type::TimeExceeded => 3,
172            Icmpv6Type::ParameterProblem => 4,
173            Icmpv6Type::EchoRequest => 128,
174            Icmpv6Type::EchoReply => 129,
175            Icmpv6Type::MulticastListenerQuery => 130,
176            Icmpv6Type::MulticastListenerReport => 131,
177            Icmpv6Type::MulticastListenerDone => 132,
178            Icmpv6Type::RouterSolicitation => 133,
179            Icmpv6Type::RouterAdvertisement => 134,
180            Icmpv6Type::NeighborSolicitation => 135,
181            Icmpv6Type::NeighborAdvertisement => 136,
182            Icmpv6Type::RedirectMessage => 137,
183            Icmpv6Type::RouterRenumbering => 138,
184            Icmpv6Type::NodeInformationQuery => 139,
185            Icmpv6Type::NodeInformationResponse => 140,
186            Icmpv6Type::InverseNeighborDiscoverySolicitation => 141,
187            Icmpv6Type::InverseNeighborDiscoveryAdvertisement => 142,
188            Icmpv6Type::Version2MulticastListenerReport => 143,
189            Icmpv6Type::HomeAgentAddressDiscoveryRequest => 144,
190            Icmpv6Type::HomeAgentAddressDiscoveryReply => 145,
191            Icmpv6Type::MobilePrefixSolicitation => 146,
192            Icmpv6Type::MobilePrefixAdvertisement => 147,
193            Icmpv6Type::CertificationPathSolicitationMessage => 148,
194            Icmpv6Type::CertificationPathAdvertisementMessage => 149,
195            Icmpv6Type::ExperimentalMobilityProtocols => 150,
196            Icmpv6Type::MulticastRouterAdvertisement => 151,
197            Icmpv6Type::MulticastRouterSolicitation => 152,
198            Icmpv6Type::MulticastRouterTermination => 153,
199            Icmpv6Type::FMIPv6Messages => 154,
200            Icmpv6Type::RPLControlMessage => 155,
201            Icmpv6Type::ILNPv6LocatorUpdateMessage => 156,
202            Icmpv6Type::DuplicateAddressRequest => 157,
203            Icmpv6Type::DuplicateAddressConfirmation => 158,
204            Icmpv6Type::MPLControlMessage => 159,
205            Icmpv6Type::ExtendedEchoRequest => 160,
206            Icmpv6Type::ExtendedEchoReply => 161,
207            Icmpv6Type::Unknown(n) => *n,
208        }
209    }
210}
211
212/// Represents the "ICMPv6 code" header field.
213#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
214#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
215pub struct Icmpv6Code(pub u8);
216
217impl Icmpv6Code {
218    /// Create a new `Icmpv6Code` instance.
219    pub fn new(val: u8) -> Icmpv6Code {
220        Icmpv6Code(val)
221    }
222    /// Get the value of the `Icmpv6Code`.
223    pub fn value(&self) -> u8 {
224        self.0
225    }
226}
227
228/// Represents the ICMPv6 header.
229#[derive(Clone, Debug, PartialEq, Eq)]
230#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
231pub struct Icmpv6Header {
232    pub icmpv6_type: Icmpv6Type,
233    pub icmpv6_code: Icmpv6Code,
234    pub checksum: u16,
235}
236
237/// ICMP packet representation
238#[derive(Clone, Debug, PartialEq, Eq)]
239pub struct Icmpv6Packet {
240    pub header: Icmpv6Header,
241    pub payload: Bytes,
242}
243
244impl Packet for Icmpv6Packet {
245    type Header = Icmpv6Header;
246
247    fn from_buf(bytes: &[u8]) -> Option<Self> {
248        if bytes.len() < ICMPV6_HEADER_LEN {
249            return None;
250        }
251        let icmpv6_type = Icmpv6Type::new(bytes[0]);
252        let icmpv6_code = Icmpv6Code::new(bytes[1]);
253        let checksum = u16::from_be_bytes([bytes[2], bytes[3]]);
254        let header = Icmpv6Header {
255            icmpv6_type,
256            icmpv6_code,
257            checksum,
258        };
259        let payload = Bytes::copy_from_slice(&bytes[ICMPV6_COMMON_HEADER_LEN..]);
260        Some(Icmpv6Packet { header, payload })
261    }
262    fn from_bytes(bytes: Bytes) -> Option<Self> {
263        Self::from_buf(&bytes)
264    }
265    fn to_bytes(&self) -> Bytes {
266        let mut bytes = Vec::with_capacity(ICMPV6_COMMON_HEADER_LEN + self.payload.len());
267        bytes.push(self.header.icmpv6_type.value());
268        bytes.push(self.header.icmpv6_code.value());
269        bytes.extend_from_slice(&self.header.checksum.to_be_bytes());
270        bytes.extend_from_slice(&self.payload);
271        Bytes::from(bytes)
272    }
273    fn header(&self) -> Bytes {
274        self.to_bytes().slice(..self.header_len())
275    }
276
277    fn payload(&self) -> Bytes {
278        self.payload.clone()
279    }
280
281    fn header_len(&self) -> usize {
282        ICMPV6_COMMON_HEADER_LEN
283    }
284
285    fn payload_len(&self) -> usize {
286        self.payload.len()
287    }
288
289    fn total_len(&self) -> usize {
290        self.header_len() + self.payload_len()
291    }
292
293    fn into_parts(self) -> (Self::Header, Bytes) {
294        (self.header, self.payload)
295    }
296}
297
298/// Represents a mutable ICMPv6 packet.
299pub struct MutableIcmpv6Packet<'a> {
300    buffer: &'a mut [u8],
301    checksum: ChecksumState,
302    checksum_context: Option<TransportChecksumContext>,
303}
304
305impl<'a> MutablePacket<'a> for MutableIcmpv6Packet<'a> {
306    type Packet = Icmpv6Packet;
307
308    fn new(buffer: &'a mut [u8]) -> Option<Self> {
309        Icmpv6Packet::from_buf(buffer)?;
310        Some(Self {
311            buffer,
312            checksum: ChecksumState::new(),
313            checksum_context: None,
314        })
315    }
316
317    fn packet(&self) -> &[u8] {
318        &*self.buffer
319    }
320
321    fn packet_mut(&mut self) -> &mut [u8] {
322        &mut *self.buffer
323    }
324
325    fn header(&self) -> &[u8] {
326        &self.packet()[..ICMPV6_COMMON_HEADER_LEN]
327    }
328
329    fn header_mut(&mut self) -> &mut [u8] {
330        let (header, _) = (&mut *self.buffer).split_at_mut(ICMPV6_COMMON_HEADER_LEN);
331        header
332    }
333
334    fn payload(&self) -> &[u8] {
335        &self.packet()[ICMPV6_COMMON_HEADER_LEN..]
336    }
337
338    fn payload_mut(&mut self) -> &mut [u8] {
339        let (_, payload) = (&mut *self.buffer).split_at_mut(ICMPV6_COMMON_HEADER_LEN);
340        payload
341    }
342}
343
344impl<'a> MutableIcmpv6Packet<'a> {
345    /// Create a mutable ICMPv6 packet without performing validation.
346    pub fn new_unchecked(buffer: &'a mut [u8]) -> Self {
347        Self {
348            buffer,
349            checksum: ChecksumState::new(),
350            checksum_context: None,
351        }
352    }
353
354    fn raw(&self) -> &[u8] {
355        &*self.buffer
356    }
357
358    fn raw_mut(&mut self) -> &mut [u8] {
359        &mut *self.buffer
360    }
361
362    fn after_field_mutation(&mut self) {
363        self.checksum.mark_dirty();
364        if self.checksum.automatic() {
365            let _ = self.recompute_checksum();
366        }
367    }
368
369    fn write_checksum(&mut self, value: u16) {
370        self.raw_mut()[2..4].copy_from_slice(&value.to_be_bytes());
371    }
372
373    /// Returns the checksum recalculation mode.
374    pub fn checksum_mode(&self) -> ChecksumMode {
375        self.checksum.mode()
376    }
377
378    /// Sets how checksum updates should be handled.
379    pub fn set_checksum_mode(&mut self, mode: ChecksumMode) {
380        self.checksum.set_mode(mode);
381        if self.checksum.automatic() && self.checksum.is_dirty() {
382            let _ = self.recompute_checksum();
383        }
384    }
385
386    /// Enables automatic checksum recomputation.
387    pub fn enable_auto_checksum(&mut self) {
388        self.set_checksum_mode(ChecksumMode::Automatic);
389    }
390
391    /// Disables automatic checksum recomputation.
392    pub fn disable_auto_checksum(&mut self) {
393        self.set_checksum_mode(ChecksumMode::Manual);
394    }
395
396    /// Returns true if the checksum needs to be recomputed.
397    pub fn is_checksum_dirty(&self) -> bool {
398        self.checksum.is_dirty()
399    }
400
401    /// Marks the checksum as dirty and recomputes it when automatic mode is enabled.
402    pub fn mark_checksum_dirty(&mut self) {
403        self.checksum.mark_dirty();
404        if self.checksum.automatic() {
405            let _ = self.recompute_checksum();
406        }
407    }
408
409    /// Sets the pseudo-header context required for checksum calculation.
410    pub fn set_checksum_context(&mut self, context: TransportChecksumContext) {
411        self.checksum_context = match context {
412            TransportChecksumContext::Ipv6 { .. } => Some(context),
413            _ => None,
414        };
415
416        if self.checksum.automatic() && self.checksum.is_dirty() {
417            let _ = self.recompute_checksum();
418        }
419    }
420
421    /// Configures the pseudo-header context for IPv6 checksums.
422    pub fn set_ipv6_checksum_context(&mut self, source: Ipv6Addr, destination: Ipv6Addr) {
423        self.set_checksum_context(TransportChecksumContext::ipv6(source, destination));
424    }
425
426    /// Clears the configured pseudo-header context.
427    pub fn clear_checksum_context(&mut self) {
428        self.checksum_context = None;
429    }
430
431    /// Returns the configured pseudo-header context.
432    pub fn checksum_context(&self) -> Option<TransportChecksumContext> {
433        self.checksum_context
434    }
435
436    /// Recomputes the checksum using the configured pseudo-header context.
437    pub fn recompute_checksum(&mut self) -> Option<u16> {
438        let context = match self.checksum_context? {
439            TransportChecksumContext::Ipv6 {
440                source,
441                destination,
442            } => (source, destination),
443            _ => return None,
444        };
445
446        let checksum = crate::util::ipv6_checksum(
447            self.raw(),
448            1,
449            &[],
450            &context.0,
451            &context.1,
452            IpNextProtocol::Icmpv6,
453        ) as u16;
454
455        self.write_checksum(checksum);
456        self.checksum.clear_dirty();
457        Some(checksum)
458    }
459
460    /// Returns the ICMPv6 type field.
461    pub fn get_type(&self) -> Icmpv6Type {
462        Icmpv6Type::new(self.raw()[0])
463    }
464
465    /// Sets the ICMPv6 type field and marks the checksum as dirty.
466    pub fn set_type(&mut self, icmpv6_type: Icmpv6Type) {
467        self.raw_mut()[0] = icmpv6_type.value();
468        self.after_field_mutation();
469    }
470
471    /// Returns the ICMPv6 code field.
472    pub fn get_code(&self) -> Icmpv6Code {
473        Icmpv6Code::new(self.raw()[1])
474    }
475
476    /// Sets the ICMPv6 code field and marks the checksum as dirty.
477    pub fn set_code(&mut self, icmpv6_code: Icmpv6Code) {
478        self.raw_mut()[1] = icmpv6_code.value();
479        self.after_field_mutation();
480    }
481
482    /// Returns the serialized checksum value.
483    pub fn get_checksum(&self) -> u16 {
484        u16::from_be_bytes([self.raw()[2], self.raw()[3]])
485    }
486
487    /// Sets the serialized checksum value and clears the dirty flag.
488    pub fn set_checksum(&mut self, checksum: u16) {
489        self.write_checksum(checksum);
490        self.checksum.clear_dirty();
491    }
492}
493
494#[cfg(test)]
495mod tests {
496    use super::*;
497
498    #[test]
499    fn test_mutable_icmpv6_packet_manual_checksum() {
500        let mut raw = [
501            Icmpv6Type::EchoRequest.value(),
502            0,
503            0,
504            0,
505            0,
506            1,
507            0,
508            1,
509            b'p',
510            b'i',
511        ];
512
513        let mut packet = MutableIcmpv6Packet::new(&mut raw).expect("mutable icmpv6");
514        let addr = Ipv6Addr::LOCALHOST;
515        packet.set_ipv6_checksum_context(addr, addr);
516        packet.set_type(Icmpv6Type::EchoReply);
517
518        assert!(packet.is_checksum_dirty());
519
520        let updated = packet.recompute_checksum().expect("checksum");
521        assert_eq!(packet.get_checksum(), updated);
522
523        let frozen = packet.freeze().expect("freeze");
524        let expected = checksum(&frozen, &addr, &addr);
525        assert_eq!(packet.get_checksum(), expected);
526    }
527
528    #[test]
529    fn test_mutable_icmpv6_packet_auto_checksum() {
530        let mut raw = [
531            Icmpv6Type::EchoRequest.value(),
532            0,
533            0,
534            0,
535            0,
536            1,
537            0,
538            1,
539            b'p',
540            b'i',
541        ];
542
543        let mut packet = MutableIcmpv6Packet::new(&mut raw).expect("mutable icmpv6");
544        let addr = Ipv6Addr::LOCALHOST;
545        packet.set_ipv6_checksum_context(addr, addr);
546        let baseline = packet.recompute_checksum().expect("checksum");
547
548        packet.enable_auto_checksum();
549        packet.set_code(Icmpv6Code::new(1));
550
551        assert!(!packet.is_checksum_dirty());
552
553        let frozen = packet.freeze().expect("freeze");
554        let expected = checksum(&frozen, &addr, &addr);
555        assert_ne!(baseline, expected);
556        assert_eq!(packet.get_checksum(), expected);
557    }
558}
559
560/// Calculates a checksum of an ICMPv6 packet.
561pub fn checksum(packet: &Icmpv6Packet, source: &Ipv6Addr, destination: &Ipv6Addr) -> u16 {
562    use crate::util;
563    util::ipv6_checksum(
564        &packet.to_bytes(),
565        1, // skip the checksum field
566        &[],
567        source,
568        destination,
569        crate::ip::IpNextProtocol::Icmpv6,
570    )
571}
572
573#[cfg(test)]
574mod checksum_tests {
575    use super::*;
576
577    #[test]
578    fn checksum_echo_request() {
579        // The equivalent of your typical ping -6 ::1%lo
580        let lo = &Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1);
581        let data = Bytes::from_static(&[
582            0x80, // Icmpv6 Type (Echo Request)
583            0x00, // Code
584            0xff, 0xff, // Checksum
585            0x00, 0x00, // Id
586            0x00, 0x01, // Sequence
587            // 56 bytes of "random" data
588            0x20, 0x20, 0x75, 0x73, 0x74, 0x20, 0x61, 0x20, 0x66, 0x6c, 0x65, 0x73, 0x68, 0x20,
589            0x77, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x20, 0x74, 0x69, 0x73, 0x20, 0x62, 0x75, 0x74,
590            0x20, 0x61, 0x20, 0x73, 0x63, 0x72, 0x61, 0x74, 0x63, 0x68, 0x20, 0x20, 0x6b, 0x6e,
591            0x69, 0x67, 0x68, 0x74, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x6e, 0x69, 0x20, 0x20, 0x20,
592        ]);
593        let mut pkg = Icmpv6Packet::from_bytes(data.clone()).unwrap();
594        assert_eq!(pkg.header.icmpv6_type, Icmpv6Type::EchoRequest);
595        assert_eq!(pkg.header.icmpv6_code, Icmpv6Code::new(0));
596        assert_eq!(pkg.header.checksum, 0xffff);
597        assert_eq!(pkg.to_bytes(), data);
598        assert_eq!(checksum(&pkg, lo, lo), 0x1d2e);
599
600        // Change type to Echo Reply
601        pkg.header.icmpv6_type = Icmpv6Type::new(0x81);
602        assert_eq!(checksum(&pkg, lo, lo), 0x1c2e);
603    }
604}
605
606pub mod ndp {
607    //! Abstractions for the Neighbor Discovery Protocol [RFC 4861]
608    //!
609    //! [RFC 4861]: https://tools.ietf.org/html/rfc4861
610
611    use bytes::Bytes;
612    use nex_core::bitfield::{self, u24be, u32be};
613
614    use crate::icmpv6::{ICMPV6_HEADER_LEN, Icmpv6Code, Icmpv6Header, Icmpv6Packet, Icmpv6Type};
615    use crate::packet::Packet;
616    use std::net::Ipv6Addr;
617
618    /// NDP SOL Packet Length.
619    pub const NDP_SOL_PACKET_LEN: usize = 24;
620    /// NDP ADV Packet Length.
621    pub const NDP_ADV_PACKET_LEN: usize = 24;
622    /// NDP REDIRECT Packet Length.
623    pub const NDP_REDIRECT_PACKET_LEN: usize = 40;
624    /// NDP OPT Packet Length.
625    pub const NDP_OPT_PACKET_LEN: usize = 2;
626
627    #[allow(non_snake_case)]
628    #[allow(non_upper_case_globals)]
629    pub mod Icmpv6Codes {
630        use crate::icmpv6::Icmpv6Code;
631        /// 0 is the only available ICMPv6 Code for the NDP.
632        pub const NoCode: Icmpv6Code = Icmpv6Code(0);
633    }
634
635    /// Represents a Neighbor Discovery Option Type.
636    #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
637    pub struct NdpOptionType(pub u8);
638
639    impl NdpOptionType {
640        /// Create a new `NdpOptionType` instance.
641        pub fn new(value: u8) -> NdpOptionType {
642            NdpOptionType(value)
643        }
644        /// Get the value of the `NdpOptionType`.
645        pub fn value(&self) -> u8 {
646            self.0
647        }
648    }
649
650    /// Neighbor Discovery Option Types [RFC 4861 Section 4.6]
651    ///
652    /// [RFC 4861 Section 4.6]: https://tools.ietf.org/html/rfc4861#section-4.6
653    #[allow(non_snake_case)]
654    #[allow(non_upper_case_globals)]
655    pub mod NdpOptionTypes {
656        use super::NdpOptionType;
657
658        /// Source Link-Layer Address Option [RFC 4861 Section 4.6.1]
659        ///
660        /// ```text
661        /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
662        /// |     Type      |    Length     |    Link-Layer Address ...
663        /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
664        /// ```
665        ///
666        /// [RFC 4861 Section 4.6.1]: https://tools.ietf.org/html/rfc4861#section-4.6.1
667        pub const SourceLLAddr: NdpOptionType = NdpOptionType(1);
668
669        /// Target Link-Layer Address Option [RFC 4861 Section 4.6.1]
670        ///
671        /// ```text
672        /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
673        /// |     Type      |    Length     |    Link-Layer Address ...
674        /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
675        /// ```
676        ///
677        /// [RFC 4861 Section 4.6.1]: https://tools.ietf.org/html/rfc4861#section-4.6.1
678        pub const TargetLLAddr: NdpOptionType = NdpOptionType(2);
679
680        /// Prefix Information Option [RFC 4861 Section 4.6.2]
681        ///
682        /// ```text
683        /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
684        /// |     Type      |    Length     | Prefix Length |L|A| Reserved1 |
685        /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
686        /// |                         Valid Lifetime                        |
687        /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
688        /// |                       Preferred Lifetime                      |
689        /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
690        /// |                           Reserved2                           |
691        /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
692        /// |                                                               |
693        /// +                                                               +
694        /// |                                                               |
695        /// +                            Prefix                             +
696        /// |                                                               |
697        /// +                                                               +
698        /// |                                                               |
699        /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
700        /// ```
701        ///
702        /// [RFC 4861 Section 4.6.2]: https://tools.ietf.org/html/rfc4861#section-4.6.2
703        pub const PrefixInformation: NdpOptionType = NdpOptionType(3);
704
705        /// Redirected Header Option [RFC 4861 Section 4.6.3]
706        ///
707        /// ```text
708        /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
709        /// |     Type      |    Length     |            Reserved           |
710        /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
711        /// |                           Reserved                            |
712        /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
713        /// |                                                               |
714        /// ~                       IP header + data                        ~
715        /// |                                                               |
716        /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
717        /// ```
718        ///
719        /// [RFC 4861 Section 4.6.3]: https://tools.ietf.org/html/rfc4861#section-4.6.3
720        pub const RedirectedHeader: NdpOptionType = NdpOptionType(4);
721
722        /// MTU Option [RFC 4861 Section 4.6.4]
723        ///
724        /// ```text
725        /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
726        /// |     Type      |    Length     |           Reserved            |
727        /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
728        /// |                              MTU                              |
729        /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
730        /// ```
731        ///
732        /// [RFC 4861 Section 4.6.4]: https://tools.ietf.org/html/rfc4861#section-4.6.4
733        pub const MTU: NdpOptionType = NdpOptionType(5);
734    }
735
736    /// Neighbor Discovery Option [RFC 4861 Section 4.6]
737    ///
738    /// ```text
739    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
740    /// |     Type      |    Length     |              ...              |
741    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
742    /// ~                              ...                              ~
743    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
744    /// ```
745    ///
746    /// [RFC 4861 Section 4.6]: https://tools.ietf.org/html/rfc4861#section-4.6
747    #[derive(Clone, Debug, PartialEq, Eq)]
748    pub struct NdpOptionPacket {
749        pub option_type: NdpOptionType,
750        pub length: u8,
751        pub payload: Bytes,
752    }
753
754    impl Packet for NdpOptionPacket {
755        type Header = ();
756        fn from_buf(bytes: &[u8]) -> Option<Self> {
757            if bytes.len() < 2 {
758                return None;
759            }
760
761            let option_type = NdpOptionType::new(bytes[0]);
762            let length = bytes[1]; // unit: 8 bytes
763
764            let total_len = (length as usize) * 8;
765            if bytes.len() < total_len {
766                return None;
767            }
768
769            let data_len = total_len - 2;
770            let payload = Bytes::copy_from_slice(&bytes[2..2 + data_len]);
771
772            Some(Self {
773                option_type,
774                length,
775                payload,
776            })
777        }
778        fn from_bytes(bytes: Bytes) -> Option<Self> {
779            Self::from_buf(&bytes)
780        }
781
782        fn to_bytes(&self) -> Bytes {
783            let mut bytes = Vec::with_capacity(NDP_OPT_PACKET_LEN + self.payload.len());
784            bytes.push(self.option_type.value());
785            bytes.push(self.length);
786            bytes.extend_from_slice(&self.payload);
787            Bytes::from(bytes)
788        }
789
790        fn header(&self) -> Bytes {
791            self.to_bytes().slice(..NDP_OPT_PACKET_LEN)
792        }
793
794        fn payload(&self) -> Bytes {
795            self.payload.clone()
796        }
797
798        fn header_len(&self) -> usize {
799            NDP_OPT_PACKET_LEN
800        }
801
802        fn payload_len(&self) -> usize {
803            self.payload.len()
804        }
805
806        fn total_len(&self) -> usize {
807            self.header_len() + self.payload_len()
808        }
809
810        fn into_parts(self) -> (Self::Header, Bytes) {
811            ((), self.payload)
812        }
813    }
814
815    impl NdpOptionPacket {
816        /// Calculate the length of the option's payload.
817        pub fn option_payload_length(&self) -> usize {
818            //let len = option.get_length();
819            let len = self.payload.len();
820            if len > 0 { ((len * 8) - 2) as usize } else { 0 }
821        }
822    }
823
824    /// Calculate a length of a `NdpOption`'s payload.
825
826    /// Router Solicitation Message [RFC 4861 Section 4.1]
827    ///
828    /// ```text
829    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
830    /// |     Type      |     Code      |          Checksum             |
831    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
832    /// |                            Reserved                           |
833    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
834    /// |   Options ...
835    /// ```
836    ///
837    /// [RFC 4861 Section 4.1]: https://tools.ietf.org/html/rfc4861#section-4.1
838    #[derive(Clone, Debug, PartialEq, Eq)]
839    pub struct RouterSolicitPacket {
840        pub header: Icmpv6Header,
841        pub reserved: u32,
842        pub options: Vec<NdpOptionPacket>,
843        pub payload: Bytes,
844    }
845
846    impl TryFrom<Icmpv6Packet> for RouterSolicitPacket {
847        type Error = &'static str;
848
849        fn try_from(value: Icmpv6Packet) -> Result<Self, Self::Error> {
850            if value.header.icmpv6_type != Icmpv6Type::RouterSolicitation {
851                return Err("Not a Router Solicitation packet");
852            }
853            if value.payload.len() < 8 {
854                return Err("Payload too short for Router Solicitation");
855            }
856            let reserved = u32::from_be_bytes([
857                value.payload[0],
858                value.payload[1],
859                value.payload[2],
860                value.payload[3],
861            ]);
862            let options = value
863                .payload
864                .slice(4..)
865                .chunks(8)
866                .map(|chunk| {
867                    let option_type = NdpOptionType::new(chunk[0]);
868                    let length = chunk[1];
869                    let payload = Bytes::from(chunk[2..].to_vec());
870                    NdpOptionPacket {
871                        option_type,
872                        length,
873                        payload,
874                    }
875                })
876                .collect();
877            Ok(RouterSolicitPacket {
878                header: value.header,
879                reserved,
880                options,
881                payload: Bytes::new(),
882            })
883        }
884    }
885
886    impl Packet for RouterSolicitPacket {
887        type Header = ();
888        fn from_buf(bytes: &[u8]) -> Option<Self> {
889            if bytes.len() < NDP_SOL_PACKET_LEN {
890                return None;
891            }
892
893            let icmpv6_type = Icmpv6Type::new(bytes[0]);
894            let icmpv6_code = Icmpv6Code::new(bytes[1]);
895            let checksum = u16::from_be_bytes([bytes[2], bytes[3]]);
896            let header = Icmpv6Header {
897                icmpv6_type,
898                icmpv6_code,
899                checksum,
900            };
901            let reserved = u32::from_be_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]);
902
903            let mut options = Vec::new();
904            let mut i = 8;
905            while i + 2 <= bytes.len() {
906                let option_type = NdpOptionType::new(bytes[i]);
907                let length = bytes[i + 1];
908                let option_len = (length as usize) * 8;
909
910                if i + option_len > bytes.len() {
911                    break;
912                }
913
914                let payload = Bytes::copy_from_slice(&bytes[i + 2..i + option_len]);
915                options.push(NdpOptionPacket {
916                    option_type,
917                    length,
918                    payload,
919                });
920                i += option_len;
921            }
922
923            let payload = Bytes::copy_from_slice(&bytes[i..]);
924
925            Some(RouterSolicitPacket {
926                header,
927                reserved,
928                options,
929                payload,
930            })
931        }
932        fn from_bytes(bytes: Bytes) -> Option<Self> {
933            Self::from_buf(&bytes)
934        }
935
936        fn to_bytes(&self) -> Bytes {
937            let mut bytes = Vec::with_capacity(NDP_SOL_PACKET_LEN);
938            bytes.push(self.header.icmpv6_type.value());
939            bytes.push(self.header.icmpv6_code.value());
940            bytes.extend_from_slice(&self.header.checksum.to_be_bytes());
941            bytes.extend_from_slice(&self.reserved.to_be_bytes());
942            for option in &self.options {
943                bytes.push(option.option_type.value());
944                bytes.push(option.length);
945                bytes.extend_from_slice(&option.payload);
946            }
947            Bytes::from(bytes)
948        }
949
950        fn header(&self) -> Bytes {
951            self.to_bytes().slice(..ICMPV6_HEADER_LEN)
952        }
953
954        fn payload(&self) -> Bytes {
955            self.payload.clone()
956        }
957
958        fn header_len(&self) -> usize {
959            ICMPV6_HEADER_LEN + 4 // 4 for reserved
960        }
961
962        fn payload_len(&self) -> usize {
963            self.payload.len()
964        }
965
966        fn total_len(&self) -> usize {
967            self.header_len() + self.payload_len()
968        }
969        fn into_parts(self) -> (Self::Header, Bytes) {
970            ((), self.payload)
971        }
972    }
973
974    impl RouterSolicitPacket {
975        /// Router Solicit packet calculation for the length of the options.
976        pub fn options_length(&self) -> usize {
977            if self.to_bytes().len() > 8 {
978                self.to_bytes().len() - 8
979            } else {
980                0
981            }
982        }
983    }
984
985    /// The enumeration of recognized Router Advert flags.
986    #[allow(non_snake_case)]
987    #[allow(non_upper_case_globals)]
988    pub mod RouterAdvertFlags {
989        /// "Managed Address Configuration" flag. This is set when
990        /// addresses are available via DHCPv6.
991        pub const ManagedAddressConf: u8 = 0b10000000;
992        /// "Other Configuration" flag. This is set when other
993        /// configuration information is available via DHCPv6.
994        pub const OtherConf: u8 = 0b01000000;
995    }
996
997    /// Router Advertisement Message Format [RFC 4861 Section 4.2]
998    ///
999    /// ```text
1000    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1001    /// |     Type      |     Code      |          Checksum             |
1002    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1003    /// | Cur Hop Limit |M|O|  Reserved |       Router Lifetime         |
1004    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1005    /// |                         Reachable Time                        |
1006    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1007    /// |                          Retrans Timer                        |
1008    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1009    /// |   Options ...
1010    /// +-+-+-+-+-+-+-+-+-+-+-+-
1011    /// ```
1012    ///
1013    /// [RFC 4861 Section 4.2]: https://tools.ietf.org/html/rfc4861#section-4.2
1014    #[derive(Clone, Debug, PartialEq, Eq)]
1015    pub struct RouterAdvertPacket {
1016        pub header: Icmpv6Header,
1017        pub hop_limit: u8,
1018        pub flags: u8,
1019        pub lifetime: u16,
1020        pub reachable_time: u32,
1021        pub retrans_time: u32,
1022        pub options: Vec<NdpOptionPacket>,
1023        pub payload: Bytes,
1024    }
1025
1026    impl TryFrom<Icmpv6Packet> for RouterAdvertPacket {
1027        type Error = &'static str;
1028
1029        fn try_from(value: Icmpv6Packet) -> Result<Self, Self::Error> {
1030            if value.header.icmpv6_type != Icmpv6Type::RouterAdvertisement {
1031                return Err("Not a Router Advertisement packet");
1032            }
1033            if value.payload.len() < 16 {
1034                return Err("Payload too short for Router Advertisement");
1035            }
1036            let hop_limit = value.payload[0];
1037            let flags = value.payload[1];
1038            let lifetime = u16::from_be_bytes([value.payload[2], value.payload[3]]);
1039            let reachable_time = u32::from_be_bytes([
1040                value.payload[4],
1041                value.payload[5],
1042                value.payload[6],
1043                value.payload[7],
1044            ]);
1045            let retrans_time = u32::from_be_bytes([
1046                value.payload[8],
1047                value.payload[9],
1048                value.payload[10],
1049                value.payload[11],
1050            ]);
1051            let options = value
1052                .payload
1053                .slice(12..)
1054                .chunks(8)
1055                .map(|chunk| {
1056                    let option_type = NdpOptionType::new(chunk[0]);
1057                    let length = chunk[1];
1058                    let payload = Bytes::from(chunk[2..].to_vec());
1059                    NdpOptionPacket {
1060                        option_type,
1061                        length,
1062                        payload,
1063                    }
1064                })
1065                .collect();
1066            Ok(RouterAdvertPacket {
1067                header: value.header,
1068                hop_limit,
1069                flags,
1070                lifetime,
1071                reachable_time,
1072                retrans_time,
1073                options,
1074                payload: Bytes::new(),
1075            })
1076        }
1077    }
1078    impl Packet for RouterAdvertPacket {
1079        type Header = ();
1080        fn from_buf(bytes: &[u8]) -> Option<Self> {
1081            if bytes.len() < NDP_ADV_PACKET_LEN {
1082                return None;
1083            }
1084
1085            let icmpv6_type = Icmpv6Type::new(bytes[0]);
1086            let icmpv6_code = Icmpv6Code::new(bytes[1]);
1087            let checksum = u16::from_be_bytes([bytes[2], bytes[3]]);
1088            let header = Icmpv6Header {
1089                icmpv6_type,
1090                icmpv6_code,
1091                checksum,
1092            };
1093
1094            let hop_limit = bytes[4];
1095            let flags = bytes[5];
1096            let lifetime = u16::from_be_bytes([bytes[6], bytes[7]]);
1097            let reachable_time = u32::from_be_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]);
1098            let retrans_time = u32::from_be_bytes([bytes[12], bytes[13], bytes[14], bytes[15]]);
1099
1100            let mut options = Vec::new();
1101            let mut i = 16;
1102            while i + 2 <= bytes.len() {
1103                let option_type = NdpOptionType::new(bytes[i]);
1104                let length = bytes[i + 1];
1105                let option_len = (length as usize) * 8;
1106
1107                if i + option_len > bytes.len() {
1108                    break;
1109                }
1110
1111                let payload = Bytes::copy_from_slice(&bytes[i + 2..i + option_len]);
1112                options.push(NdpOptionPacket {
1113                    option_type,
1114                    length,
1115                    payload,
1116                });
1117                i += option_len;
1118            }
1119
1120            let payload = Bytes::copy_from_slice(&bytes[i..]);
1121
1122            Some(RouterAdvertPacket {
1123                header,
1124                hop_limit,
1125                flags,
1126                lifetime,
1127                reachable_time,
1128                retrans_time,
1129                options,
1130                payload,
1131            })
1132        }
1133
1134        fn from_bytes(bytes: Bytes) -> Option<Self> {
1135            Self::from_buf(&bytes)
1136        }
1137
1138        fn to_bytes(&self) -> Bytes {
1139            let mut bytes = Vec::with_capacity(NDP_ADV_PACKET_LEN);
1140            bytes.push(self.header.icmpv6_type.value());
1141            bytes.push(self.header.icmpv6_code.value());
1142            bytes.extend_from_slice(&self.header.checksum.to_be_bytes());
1143            bytes.push(self.hop_limit);
1144            bytes.push(self.flags);
1145            bytes.extend_from_slice(&self.lifetime.to_be_bytes());
1146            bytes.extend_from_slice(&self.reachable_time.to_be_bytes());
1147            bytes.extend_from_slice(&self.retrans_time.to_be_bytes());
1148            for option in &self.options {
1149                bytes.push(option.option_type.value());
1150                bytes.push(option.length);
1151                bytes.extend_from_slice(&option.payload);
1152            }
1153            Bytes::from(bytes)
1154        }
1155
1156        fn header(&self) -> Bytes {
1157            self.to_bytes().slice(..ICMPV6_HEADER_LEN + 16) // 16 for the fixed part of the Router Advert
1158        }
1159        fn payload(&self) -> Bytes {
1160            self.payload.clone()
1161        }
1162        fn header_len(&self) -> usize {
1163            ICMPV6_HEADER_LEN + 16 // 16 for the fixed part of the Router Advert
1164        }
1165
1166        fn payload_len(&self) -> usize {
1167            self.payload.len()
1168        }
1169
1170        fn total_len(&self) -> usize {
1171            self.header_len() + self.payload_len()
1172        }
1173        fn into_parts(self) -> (Self::Header, Bytes) {
1174            ((), self.payload)
1175        }
1176    }
1177
1178    impl RouterAdvertPacket {
1179        /// Router Advert packet calculation for the length of the options.
1180        pub fn options_length(&self) -> usize {
1181            if self.to_bytes().len() > 16 {
1182                self.to_bytes().len() - 16
1183            } else {
1184                0
1185            }
1186        }
1187    }
1188
1189    /// Neighbor Solicitation Message Format [RFC 4861 Section 4.3]
1190    ///
1191    /// ```text
1192    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1193    /// |     Type      |     Code      |          Checksum             |
1194    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1195    /// |                           Reserved                            |
1196    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1197    /// |                                                               |
1198    /// +                                                               +
1199    /// |                                                               |
1200    /// +                       Target Address                          +
1201    /// |                                                               |
1202    /// +                                                               +
1203    /// |                                                               |
1204    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1205    /// |   Options ...
1206    /// +-+-+-+-+-+-+-+-+-+-+-+-
1207    /// ```
1208    ///
1209    /// [RFC 4861 Section 4.3]: https://tools.ietf.org/html/rfc4861#section-4.3
1210    #[derive(Clone, Debug, PartialEq, Eq)]
1211    pub struct NeighborSolicitPacket {
1212        pub header: Icmpv6Header,
1213        pub reserved: u32,
1214        pub target_addr: Ipv6Addr,
1215        pub options: Vec<NdpOptionPacket>,
1216        pub payload: Bytes,
1217    }
1218
1219    impl TryFrom<Icmpv6Packet> for NeighborSolicitPacket {
1220        type Error = &'static str;
1221
1222        fn try_from(value: Icmpv6Packet) -> Result<Self, Self::Error> {
1223            if value.header.icmpv6_type != Icmpv6Type::NeighborSolicitation {
1224                return Err("Not a Neighbor Solicitation packet");
1225            }
1226            if value.payload.len() < 24 {
1227                return Err("Payload too short for Neighbor Solicitation");
1228            }
1229            let reserved = u32::from_be_bytes([
1230                value.payload[0],
1231                value.payload[1],
1232                value.payload[2],
1233                value.payload[3],
1234            ]);
1235            let target_addr = Ipv6Addr::new(
1236                u16::from_be_bytes([value.payload[4], value.payload[5]]),
1237                u16::from_be_bytes([value.payload[6], value.payload[7]]),
1238                u16::from_be_bytes([value.payload[8], value.payload[9]]),
1239                u16::from_be_bytes([value.payload[10], value.payload[11]]),
1240                u16::from_be_bytes([value.payload[12], value.payload[13]]),
1241                u16::from_be_bytes([value.payload[14], value.payload[15]]),
1242                u16::from_be_bytes([value.payload[16], value.payload[17]]),
1243                u16::from_be_bytes([value.payload[18], value.payload[19]]),
1244            );
1245            let options = value
1246                .payload
1247                .slice(20..)
1248                .chunks(8)
1249                .map(|chunk| {
1250                    let option_type = NdpOptionType::new(chunk[0]);
1251                    let length = chunk[1];
1252                    let payload: Bytes = Bytes::from(chunk[2..].to_vec());
1253                    NdpOptionPacket {
1254                        option_type,
1255                        length,
1256                        payload,
1257                    }
1258                })
1259                .collect();
1260            Ok(NeighborSolicitPacket {
1261                header: value.header,
1262                reserved,
1263                target_addr,
1264                options,
1265                payload: Bytes::new(),
1266            })
1267        }
1268    }
1269
1270    impl Packet for NeighborSolicitPacket {
1271        type Header = ();
1272        fn from_buf(bytes: &[u8]) -> Option<Self> {
1273            if bytes.len() < 24 {
1274                return None;
1275            }
1276
1277            let icmpv6_type = Icmpv6Type::new(bytes[0]);
1278            let icmpv6_code = Icmpv6Code::new(bytes[1]);
1279            let checksum = u16::from_be_bytes([bytes[2], bytes[3]]);
1280            let reserved = u32::from_be_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]);
1281            let target_addr = Ipv6Addr::new(
1282                u16::from_be_bytes([bytes[8], bytes[9]]),
1283                u16::from_be_bytes([bytes[10], bytes[11]]),
1284                u16::from_be_bytes([bytes[12], bytes[13]]),
1285                u16::from_be_bytes([bytes[14], bytes[15]]),
1286                u16::from_be_bytes([bytes[16], bytes[17]]),
1287                u16::from_be_bytes([bytes[18], bytes[19]]),
1288                u16::from_be_bytes([bytes[20], bytes[21]]),
1289                u16::from_be_bytes([bytes[22], bytes[23]]),
1290            );
1291
1292            let mut options = Vec::new();
1293            let mut i = 24;
1294            while i + 2 <= bytes.len() {
1295                let option_type = NdpOptionType::new(bytes[i]);
1296                let length = bytes[i + 1];
1297                let option_len = (length as usize) * 8;
1298
1299                if option_len < 2 || i + option_len > bytes.len() {
1300                    break;
1301                }
1302
1303                let payload = Bytes::copy_from_slice(&bytes[i + 2..i + option_len]);
1304                options.push(NdpOptionPacket {
1305                    option_type,
1306                    length,
1307                    payload,
1308                });
1309
1310                i += option_len;
1311            }
1312
1313            let payload = Bytes::copy_from_slice(&bytes[i..]);
1314
1315            Some(NeighborSolicitPacket {
1316                header: Icmpv6Header {
1317                    icmpv6_type,
1318                    icmpv6_code,
1319                    checksum,
1320                },
1321                reserved,
1322                target_addr,
1323                options,
1324                payload,
1325            })
1326        }
1327        fn from_bytes(bytes: Bytes) -> Option<Self> {
1328            Self::from_buf(&bytes)
1329        }
1330
1331        fn to_bytes(&self) -> Bytes {
1332            let mut bytes = Vec::with_capacity(NDP_SOL_PACKET_LEN);
1333            bytes.push(self.header.icmpv6_type.value());
1334            bytes.push(self.header.icmpv6_code.value());
1335            bytes.extend_from_slice(&self.header.checksum.to_be_bytes());
1336            bytes.extend_from_slice(&self.reserved.to_be_bytes());
1337            for (_, segment) in self.target_addr.segments().iter().enumerate() {
1338                bytes.extend_from_slice(&segment.to_be_bytes());
1339            }
1340            for option in &self.options {
1341                bytes.push(option.option_type.value());
1342                bytes.push(option.length);
1343                bytes.extend_from_slice(&option.payload);
1344            }
1345            Bytes::from(bytes)
1346        }
1347        fn header(&self) -> Bytes {
1348            self.to_bytes().slice(..ICMPV6_HEADER_LEN + 24) // 24 for the fixed part of the Neighbor Solicit
1349        }
1350        fn payload(&self) -> Bytes {
1351            self.payload.clone()
1352        }
1353        fn header_len(&self) -> usize {
1354            ICMPV6_HEADER_LEN + 24 // 24 for the fixed part of the Neighbor Solicit
1355        }
1356
1357        fn payload_len(&self) -> usize {
1358            self.payload.len()
1359        }
1360
1361        fn total_len(&self) -> usize {
1362            self.header_len() + self.payload_len()
1363        }
1364
1365        fn into_parts(self) -> (Self::Header, Bytes) {
1366            ((), self.payload)
1367        }
1368    }
1369
1370    impl NeighborSolicitPacket {
1371        /// Neighbor Solicit packet calculation for the length of the options.
1372        pub fn options_length(&self) -> usize {
1373            // Calculate the length of the options in the Neighbor Solicitation packet.
1374            if self.to_bytes().len() > 24 {
1375                self.to_bytes().len() - 24
1376            } else {
1377                0
1378            }
1379        }
1380    }
1381
1382    /// Enumeration of recognized Neighbor Advert flags.
1383    #[allow(non_snake_case)]
1384    #[allow(non_upper_case_globals)]
1385    pub mod NeighborAdvertFlags {
1386        /// Indicates that the sender is a router.
1387        pub const Router: u8 = 0b10000000;
1388        /// Indicates that the advertisement was sent due to the receipt of a
1389        /// Neighbor Solicitation message.
1390        pub const Solicited: u8 = 0b01000000;
1391        /// Indicates that the advertisement should override an existing cache
1392        /// entry.
1393        pub const Override: u8 = 0b00100000;
1394    }
1395
1396    /// Neighbor Advertisement Message Format [RFC 4861 Section 4.4]
1397    ///
1398    /// ```text
1399    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1400    /// |     Type      |     Code      |          Checksum             |
1401    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1402    /// |R|S|O|                     Reserved                            |
1403    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1404    /// |                                                               |
1405    /// +                                                               +
1406    /// |                                                               |
1407    /// +                       Target Address                          +
1408    /// |                                                               |
1409    /// +                                                               +
1410    /// |                                                               |
1411    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1412    /// |   Options ...
1413    /// +-+-+-+-+-+-+-+-+-+-+-+-
1414    /// ```
1415    ///
1416    /// [RFC 4861 Section 4.4]: https://tools.ietf.org/html/rfc4861#section-4.4
1417    #[derive(Clone, Debug, PartialEq, Eq)]
1418    pub struct NeighborAdvertPacket {
1419        pub header: Icmpv6Header,
1420        pub flags: u8,
1421        pub reserved: u24be,
1422        pub target_addr: Ipv6Addr,
1423        pub options: Vec<NdpOptionPacket>,
1424        pub payload: Bytes,
1425    }
1426
1427    impl TryFrom<Icmpv6Packet> for NeighborAdvertPacket {
1428        type Error = &'static str;
1429
1430        fn try_from(value: Icmpv6Packet) -> Result<Self, Self::Error> {
1431            if value.header.icmpv6_type != Icmpv6Type::NeighborAdvertisement {
1432                return Err("Not a Neighbor Advert packet");
1433            }
1434            // The fixed part of a Neighbor Advertisement message is 20 bytes:
1435            // 1 byte for flags, 3 bytes reserved, and 16 bytes for the target address.
1436            // See RFC 4861 Section 4.4.
1437            // Some packets may not include any options, so 20 bytes is the minimum length.
1438            if value.payload.len() < 20 {
1439                return Err("Payload too short for Neighbor Advert");
1440            }
1441            let flags = value.payload[0];
1442            let reserved = bitfield::utils::u24be_from_bytes([
1443                value.payload[1],
1444                value.payload[2],
1445                value.payload[3],
1446            ]);
1447            let target_addr = Ipv6Addr::new(
1448                u16::from_be_bytes([value.payload[4], value.payload[5]]),
1449                u16::from_be_bytes([value.payload[6], value.payload[7]]),
1450                u16::from_be_bytes([value.payload[8], value.payload[9]]),
1451                u16::from_be_bytes([value.payload[10], value.payload[11]]),
1452                u16::from_be_bytes([value.payload[12], value.payload[13]]),
1453                u16::from_be_bytes([value.payload[14], value.payload[15]]),
1454                u16::from_be_bytes([value.payload[16], value.payload[17]]),
1455                u16::from_be_bytes([value.payload[18], value.payload[19]]),
1456            );
1457            let options = value
1458                .payload
1459                .slice(20..)
1460                .chunks(8)
1461                .map(|chunk| {
1462                    let option_type = NdpOptionType::new(chunk[0]);
1463                    let length = chunk[1];
1464                    let payload = Bytes::from(chunk[2..].to_vec());
1465                    NdpOptionPacket {
1466                        option_type,
1467                        length,
1468                        payload,
1469                    }
1470                })
1471                .collect();
1472            Ok(NeighborAdvertPacket {
1473                header: value.header,
1474                flags,
1475                reserved,
1476                target_addr,
1477                options,
1478                payload: Bytes::new(),
1479            })
1480        }
1481    }
1482
1483    impl Packet for NeighborAdvertPacket {
1484        type Header = ();
1485        fn from_buf(bytes: &[u8]) -> Option<Self> {
1486            if bytes.len() < 24 {
1487                return None;
1488            }
1489
1490            let icmpv6_type = Icmpv6Type::new(bytes[0]);
1491            let icmpv6_code = Icmpv6Code::new(bytes[1]);
1492            let checksum = u16::from_be_bytes([bytes[2], bytes[3]]);
1493            let header = Icmpv6Header {
1494                icmpv6_type,
1495                icmpv6_code,
1496                checksum,
1497            };
1498
1499            let flags = bytes[4];
1500            let reserved = bitfield::utils::u24be_from_bytes([bytes[5], bytes[6], bytes[7]]);
1501
1502            let target_addr = Ipv6Addr::new(
1503                u16::from_be_bytes([bytes[8], bytes[9]]),
1504                u16::from_be_bytes([bytes[10], bytes[11]]),
1505                u16::from_be_bytes([bytes[12], bytes[13]]),
1506                u16::from_be_bytes([bytes[14], bytes[15]]),
1507                u16::from_be_bytes([bytes[16], bytes[17]]),
1508                u16::from_be_bytes([bytes[18], bytes[19]]),
1509                u16::from_be_bytes([bytes[20], bytes[21]]),
1510                u16::from_be_bytes([bytes[22], bytes[23]]),
1511            );
1512
1513            let mut options = Vec::new();
1514            let mut i = 24;
1515            while i + 2 <= bytes.len() {
1516                let option_type = NdpOptionType::new(bytes[i]);
1517                let length = bytes[i + 1];
1518                let option_len = (length as usize) * 8;
1519
1520                if option_len < 2 || i + option_len > bytes.len() {
1521                    break;
1522                }
1523
1524                let payload = Bytes::copy_from_slice(&bytes[i + 2..i + option_len]);
1525                options.push(NdpOptionPacket {
1526                    option_type,
1527                    length,
1528                    payload,
1529                });
1530
1531                i += option_len;
1532            }
1533
1534            let payload = Bytes::copy_from_slice(&bytes[i..]);
1535
1536            Some(NeighborAdvertPacket {
1537                header,
1538                flags,
1539                reserved,
1540                target_addr,
1541                options,
1542                payload,
1543            })
1544        }
1545        fn from_bytes(bytes: Bytes) -> Option<Self> {
1546            Self::from_buf(&bytes)
1547        }
1548
1549        fn to_bytes(&self) -> Bytes {
1550            let mut bytes = Vec::with_capacity(NDP_ADV_PACKET_LEN);
1551            bytes.push(self.header.icmpv6_type.value());
1552            bytes.push(self.header.icmpv6_code.value());
1553            bytes.extend_from_slice(&self.header.checksum.to_be_bytes());
1554
1555            // Combine flags and reserved (flags in the most significant 8 bits)
1556            let flags_reserved = (self.flags as u32) << 24 | (self.reserved & 0x00FF_FFFF);
1557            bytes.extend_from_slice(&flags_reserved.to_be_bytes());
1558
1559            for segment in self.target_addr.segments().iter() {
1560                bytes.extend_from_slice(&segment.to_be_bytes());
1561            }
1562
1563            for option in &self.options {
1564                bytes.push(option.option_type.value());
1565                bytes.push(option.length);
1566                bytes.extend_from_slice(&option.payload);
1567            }
1568
1569            Bytes::from(bytes)
1570        }
1571        fn header(&self) -> Bytes {
1572            self.to_bytes().slice(..ICMPV6_HEADER_LEN + 24) // 24 for the fixed part of the Neighbor Advert
1573        }
1574        fn payload(&self) -> Bytes {
1575            self.payload.clone()
1576        }
1577        fn header_len(&self) -> usize {
1578            ICMPV6_HEADER_LEN + 24 // 24 for the fixed part of the Neighbor Advert
1579        }
1580        fn payload_len(&self) -> usize {
1581            self.payload.len()
1582        }
1583
1584        fn total_len(&self) -> usize {
1585            self.header_len() + self.payload_len()
1586        }
1587
1588        fn into_parts(self) -> (Self::Header, Bytes) {
1589            ((), self.payload)
1590        }
1591    }
1592
1593    impl NeighborAdvertPacket {
1594        /// Neighbor Advert packet calculation for the length of the options.
1595        pub fn options_length(&self) -> usize {
1596            // Calculate the length of the options in the Neighbor Advert packet.
1597            if self.to_bytes().len() > 24 {
1598                self.to_bytes().len() - 24
1599            } else {
1600                0
1601            }
1602        }
1603    }
1604
1605    /// Redirect Message Format [RFC 4861 Section 4.5]
1606    ///
1607    /// ```text
1608    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1609    /// |     Type      |     Code      |          Checksum             |
1610    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1611    /// |                           Reserved                            |
1612    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1613    /// |                                                               |
1614    /// +                                                               +
1615    /// |                                                               |
1616    /// +                       Target Address                          +
1617    /// |                                                               |
1618    /// +                                                               +
1619    /// |                                                               |
1620    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1621    /// |                                                               |
1622    /// +                                                               +
1623    /// |                                                               |
1624    /// +                     Destination Address                       +
1625    /// |                                                               |
1626    /// +                                                               +
1627    /// |                                                               |
1628    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1629    /// |   Options ...
1630    /// +-+-+-+-+-+-+-+-+-+-+-+-
1631    /// ```
1632    ///
1633    /// [RFC 4861 Section 4.5]: https://tools.ietf.org/html/rfc4861#section-4.5
1634    #[derive(Clone, Debug, PartialEq, Eq)]
1635    pub struct RedirectPacket {
1636        pub header: Icmpv6Header,
1637        pub reserved: u32be,
1638        pub target_addr: Ipv6Addr,
1639        pub dest_addr: Ipv6Addr,
1640        pub options: Vec<NdpOptionPacket>,
1641        pub payload: Bytes,
1642    }
1643
1644    impl TryFrom<Icmpv6Packet> for RedirectPacket {
1645        type Error = &'static str;
1646
1647        fn try_from(value: Icmpv6Packet) -> Result<Self, Self::Error> {
1648            if value.header.icmpv6_type != Icmpv6Type::RedirectMessage {
1649                return Err("Not a Redirect packet");
1650            }
1651            if value.payload.len() < 40 {
1652                return Err("Payload too short for Redirect");
1653            }
1654            let reserved = u32be::from_be_bytes([
1655                value.payload[0],
1656                value.payload[1],
1657                value.payload[2],
1658                value.payload[3],
1659            ]);
1660            let target_addr = Ipv6Addr::new(
1661                u16::from_be_bytes([value.payload[4], value.payload[5]]),
1662                u16::from_be_bytes([value.payload[6], value.payload[7]]),
1663                u16::from_be_bytes([value.payload[8], value.payload[9]]),
1664                u16::from_be_bytes([value.payload[10], value.payload[11]]),
1665                u16::from_be_bytes([value.payload[12], value.payload[13]]),
1666                u16::from_be_bytes([value.payload[14], value.payload[15]]),
1667                u16::from_be_bytes([value.payload[16], value.payload[17]]),
1668                u16::from_be_bytes([value.payload[18], value.payload[19]]),
1669            );
1670            let dest_addr = Ipv6Addr::new(
1671                u16::from_be_bytes([value.payload[20], value.payload[21]]),
1672                u16::from_be_bytes([value.payload[22], value.payload[23]]),
1673                u16::from_be_bytes([value.payload[24], value.payload[25]]),
1674                u16::from_be_bytes([value.payload[26], value.payload[27]]),
1675                u16::from_be_bytes([value.payload[28], value.payload[29]]),
1676                u16::from_be_bytes([value.payload[30], value.payload[31]]),
1677                u16::from_be_bytes([value.payload[32], value.payload[33]]),
1678                u16::from_be_bytes([value.payload[34], value.payload[35]]),
1679            );
1680            let options = value
1681                .payload
1682                .slice(36..)
1683                .chunks(8)
1684                .map(|chunk| {
1685                    let option_type = NdpOptionType::new(chunk[0]);
1686                    let length = chunk[1];
1687                    let payload = Bytes::from(chunk[2..].to_vec());
1688                    NdpOptionPacket {
1689                        option_type,
1690                        length,
1691                        payload,
1692                    }
1693                })
1694                .collect();
1695            Ok(RedirectPacket {
1696                header: value.header,
1697                reserved,
1698                target_addr,
1699                dest_addr,
1700                options,
1701                payload: Bytes::new(),
1702            })
1703        }
1704    }
1705
1706    impl Packet for RedirectPacket {
1707        type Header = ();
1708        fn from_buf(bytes: &[u8]) -> Option<Self> {
1709            if bytes.len() < 40 {
1710                return None;
1711            }
1712
1713            let icmpv6_type = Icmpv6Type::new(bytes[0]);
1714            let icmpv6_code = Icmpv6Code::new(bytes[1]);
1715            let checksum = u16::from_be_bytes([bytes[2], bytes[3]]);
1716            let header = Icmpv6Header {
1717                icmpv6_type,
1718                icmpv6_code,
1719                checksum,
1720            };
1721
1722            let reserved = u32be::from_be_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]);
1723
1724            let target_addr = Ipv6Addr::new(
1725                u16::from_be_bytes([bytes[8], bytes[9]]),
1726                u16::from_be_bytes([bytes[10], bytes[11]]),
1727                u16::from_be_bytes([bytes[12], bytes[13]]),
1728                u16::from_be_bytes([bytes[14], bytes[15]]),
1729                u16::from_be_bytes([bytes[16], bytes[17]]),
1730                u16::from_be_bytes([bytes[18], bytes[19]]),
1731                u16::from_be_bytes([bytes[20], bytes[21]]),
1732                u16::from_be_bytes([bytes[22], bytes[23]]),
1733            );
1734
1735            let dest_addr = Ipv6Addr::new(
1736                u16::from_be_bytes([bytes[24], bytes[25]]),
1737                u16::from_be_bytes([bytes[26], bytes[27]]),
1738                u16::from_be_bytes([bytes[28], bytes[29]]),
1739                u16::from_be_bytes([bytes[30], bytes[31]]),
1740                u16::from_be_bytes([bytes[32], bytes[33]]),
1741                u16::from_be_bytes([bytes[34], bytes[35]]),
1742                u16::from_be_bytes([bytes[36], bytes[37]]),
1743                u16::from_be_bytes([bytes[38], bytes[39]]),
1744            );
1745
1746            let mut options = Vec::new();
1747            let mut i = 40;
1748            while i + 2 <= bytes.len() {
1749                let option_type = NdpOptionType::new(bytes[i]);
1750                let length = bytes[i + 1];
1751                let option_len = (length as usize) * 8;
1752
1753                if option_len < 2 || i + option_len > bytes.len() {
1754                    break;
1755                }
1756
1757                let payload = Bytes::copy_from_slice(&bytes[i + 2..i + option_len]);
1758                options.push(NdpOptionPacket {
1759                    option_type,
1760                    length,
1761                    payload,
1762                });
1763
1764                i += option_len;
1765            }
1766
1767            let payload = Bytes::copy_from_slice(&bytes[i..]);
1768
1769            Some(RedirectPacket {
1770                header,
1771                reserved,
1772                target_addr,
1773                dest_addr,
1774                options,
1775                payload,
1776            })
1777        }
1778        fn from_bytes(bytes: Bytes) -> Option<Self> {
1779            Self::from_buf(&bytes)
1780        }
1781        fn to_bytes(&self) -> Bytes {
1782            let mut bytes = Vec::with_capacity(NDP_REDIRECT_PACKET_LEN);
1783            bytes.push(self.header.icmpv6_type.value());
1784            bytes.push(self.header.icmpv6_code.value());
1785            bytes.extend_from_slice(&self.header.checksum.to_be_bytes());
1786            bytes.extend_from_slice(&self.reserved.to_be_bytes());
1787            for (_, segment) in self.target_addr.segments().iter().enumerate() {
1788                bytes.extend_from_slice(&segment.to_be_bytes());
1789            }
1790            for (_, segment) in self.dest_addr.segments().iter().enumerate() {
1791                bytes.extend_from_slice(&segment.to_be_bytes());
1792            }
1793            for option in &self.options {
1794                bytes.push(option.option_type.value());
1795                bytes.push(option.length);
1796                bytes.extend_from_slice(&option.payload);
1797            }
1798            Bytes::from(bytes)
1799        }
1800        fn header(&self) -> Bytes {
1801            self.to_bytes().slice(..ICMPV6_HEADER_LEN + 40) // 40 for the fixed part of the Redirect
1802        }
1803        fn payload(&self) -> Bytes {
1804            self.payload.clone()
1805        }
1806        fn header_len(&self) -> usize {
1807            ICMPV6_HEADER_LEN + 40 // 40 for the fixed part of the Redirect
1808        }
1809
1810        fn payload_len(&self) -> usize {
1811            self.payload.len()
1812        }
1813
1814        fn total_len(&self) -> usize {
1815            self.header_len() + self.payload_len()
1816        }
1817
1818        fn into_parts(self) -> (Self::Header, Bytes) {
1819            ((), self.payload)
1820        }
1821    }
1822
1823    impl RedirectPacket {
1824        /// Redirect packet calculation for the length of the options.
1825        pub fn options_length(&self) -> usize {
1826            // Calculate the length of the options in the Redirect packet.
1827            if self.to_bytes().len() > 40 {
1828                self.to_bytes().len() - 40
1829            } else {
1830                0
1831            }
1832        }
1833    }
1834
1835    #[cfg(test)]
1836    mod ndp_tests {
1837        use super::*;
1838        use crate::icmpv6::{Icmpv6Code, Icmpv6Type};
1839
1840        #[test]
1841        fn basic_option_parsing() {
1842            let data = Bytes::from_static(&[
1843                0x02, 0x01, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01,
1844                // Extra bytes to confuse the parsing
1845                0x00, 0x00, 0x00,
1846            ]);
1847            let pkg = NdpOptionPacket::from_bytes(data).unwrap();
1848            assert_eq!(pkg.option_type, NdpOptionTypes::TargetLLAddr);
1849            assert_eq!(pkg.length, 0x01);
1850            assert_eq!(pkg.payload.len(), 6);
1851            assert_eq!(pkg.payload.as_ref(), &[0x06, 0x05, 0x04, 0x03, 0x02, 0x01]);
1852        }
1853
1854        #[test]
1855        fn basic_rs_parse() {
1856            let data = Bytes::from_static(&[
1857                0x85, // Type
1858                0x00, // Code
1859                0x00, 0x00, // Checksum
1860                0x00, 0x00, 0x00, 0x00, // Reserved
1861                0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00,
1862                0x00, 0x00,
1863            ]);
1864
1865            let pkg = RouterSolicitPacket::from_bytes(data).unwrap();
1866            assert_eq!(pkg.header.icmpv6_type, Icmpv6Type::RouterSolicitation);
1867            assert_eq!(pkg.header.icmpv6_code, Icmpv6Code(0));
1868            assert_eq!(pkg.header.checksum, 0);
1869            assert_eq!(pkg.reserved, 0);
1870            assert_eq!(pkg.options.len(), 2);
1871
1872            let option = &pkg.options[0];
1873            assert_eq!(option.option_type, NdpOptionTypes::TargetLLAddr);
1874            assert_eq!(option.length, 0x01);
1875            assert_eq!(
1876                option.payload.as_ref(),
1877                &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
1878            );
1879            assert_eq!(option.payload.len(), 6);
1880
1881            let option = &pkg.options[1];
1882            assert_eq!(option.option_type, NdpOptionTypes::SourceLLAddr);
1883            assert_eq!(option.length, 1);
1884            assert_eq!(
1885                option.payload.as_ref(),
1886                &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
1887            );
1888        }
1889
1890        #[test]
1891        fn basic_rs_create() {
1892            use crate::icmpv6::ndp::{NdpOptionPacket, RouterSolicitPacket};
1893
1894            let options = vec![NdpOptionPacket {
1895                option_type: NdpOptionTypes::SourceLLAddr,
1896                length: 1,
1897                payload: Bytes::from_static(&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00]),
1898            }];
1899
1900            let packet = RouterSolicitPacket {
1901                header: Icmpv6Header {
1902                    icmpv6_type: Icmpv6Type::RouterSolicitation,
1903                    icmpv6_code: Icmpv6Code(0),
1904                    checksum: 0,
1905                },
1906                reserved: 0,
1907                options,
1908                payload: Bytes::new(),
1909            };
1910
1911            let bytes = packet.to_bytes();
1912
1913            let expected = Bytes::from_static(&[
1914                0x85, 0x00, 0x00, 0x00, // Type, Code, Checksum
1915                0x00, 0x00, 0x00, 0x00, // Reserved
1916                0x01, 0x01, // Option Type, Length
1917                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Option Data
1918            ]);
1919
1920            assert_eq!(bytes, expected);
1921        }
1922
1923        #[test]
1924        fn basic_ra_parse() {
1925            let data = Bytes::from_static(&[
1926                0x86, // Type
1927                0x00, // Code
1928                0x00, 0x00, // Checksum
1929                0xff, // Hop Limit
1930                0x80, // Flags
1931                0x09, 0x00, // Lifetime
1932                0x12, 0x34, 0x56, 0x78, // Reachable
1933                0x87, 0x65, 0x43, 0x21, // Retrans
1934                0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Source Link-Layer
1935                0x05, 0x01, 0x00, 0x00, 0x57, 0x68, 0x61, 0x74, // MTU
1936            ]);
1937            let pkg = RouterAdvertPacket::from_bytes(data).unwrap();
1938            assert_eq!(pkg.header.icmpv6_type, Icmpv6Type::RouterAdvertisement);
1939            assert_eq!(pkg.header.icmpv6_code, Icmpv6Code(0));
1940            assert_eq!(pkg.header.checksum, 0x00);
1941            assert_eq!(pkg.hop_limit, 0xff);
1942            assert_eq!(pkg.flags, RouterAdvertFlags::ManagedAddressConf);
1943            assert_eq!(pkg.lifetime, 0x900);
1944            assert_eq!(pkg.reachable_time, 0x12345678);
1945            assert_eq!(pkg.retrans_time, 0x87654321);
1946            assert_eq!(pkg.options.len(), 2);
1947
1948            let option = &pkg.options[0];
1949            assert_eq!(option.option_type, NdpOptionTypes::SourceLLAddr);
1950            assert_eq!(option.length, 1);
1951            assert_eq!(
1952                option.payload.as_ref(),
1953                &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
1954            );
1955
1956            let option = &pkg.options[1];
1957            assert_eq!(option.option_type, NdpOptionTypes::MTU);
1958            assert_eq!(option.length, 1);
1959            assert_eq!(
1960                option.payload.as_ref(),
1961                &[0x00, 0x00, 0x57, 0x68, 0x61, 0x74]
1962            );
1963        }
1964
1965        #[test]
1966        fn basic_ra_create() {
1967            use crate::icmpv6::ndp::{NdpOptionPacket, RouterAdvertFlags, RouterAdvertPacket};
1968
1969            let options = vec![NdpOptionPacket {
1970                option_type: NdpOptionTypes::MTU,
1971                length: 1,
1972                payload: Bytes::from_static(&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00]),
1973            }];
1974
1975            let packet = RouterAdvertPacket {
1976                header: Icmpv6Header {
1977                    icmpv6_type: Icmpv6Type::RouterAdvertisement,
1978                    icmpv6_code: Icmpv6Code(0),
1979                    checksum: 0,
1980                },
1981                hop_limit: 0xff,
1982                flags: RouterAdvertFlags::ManagedAddressConf,
1983                lifetime: 0,
1984                reachable_time: 0,
1985                retrans_time: 0,
1986                options,
1987                payload: Bytes::new(),
1988            };
1989
1990            let bytes = packet.to_bytes();
1991            let expected = Bytes::from_static(&[
1992                0x86, 0x00, 0x00, 0x00, // header
1993                0xff, 0x80, 0x00, 0x00, // hop limit, flags, lifetime
1994                0x00, 0x00, 0x00, 0x00, // reachable
1995                0x00, 0x00, 0x00, 0x00, // retrans
1996                0x05, 0x01, // option type + len
1997                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // option data
1998            ]);
1999
2000            assert_eq!(bytes, expected);
2001        }
2002
2003        #[test]
2004        fn basic_ns_parse() {
2005            let data = Bytes::from_static(&[
2006                0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00,
2007                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
2008            ]);
2009            let pkg = NeighborSolicitPacket::from_bytes(data).unwrap();
2010            assert_eq!(pkg.header.icmpv6_type, Icmpv6Type::NeighborSolicitation);
2011            assert_eq!(pkg.header.icmpv6_code, Icmpv6Code(0));
2012            assert_eq!(pkg.header.checksum, 0x00);
2013            assert_eq!(pkg.reserved, 0x00);
2014            assert_eq!(pkg.target_addr, Ipv6Addr::new(0xff02, 0, 0, 0, 0, 0, 0, 1));
2015        }
2016
2017        #[test]
2018        fn basic_ns_create() {
2019            use crate::icmpv6::ndp::NeighborSolicitPacket;
2020
2021            let packet = NeighborSolicitPacket {
2022                header: Icmpv6Header {
2023                    icmpv6_type: Icmpv6Type::NeighborSolicitation,
2024                    icmpv6_code: Icmpv6Code(0),
2025                    checksum: 0,
2026                },
2027                reserved: 0,
2028                target_addr: Ipv6Addr::new(0xff02, 0, 0, 0, 0, 0, 0, 1),
2029                options: vec![],
2030                payload: Bytes::new(),
2031            };
2032
2033            let bytes = packet.to_bytes();
2034
2035            let expected = Bytes::from_static(&[
2036                0x87, 0x00, 0x00, 0x00, // header
2037                0x00, 0x00, 0x00, 0x00, // reserved
2038                0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2039                0x00, 0x01, // target
2040            ]);
2041
2042            assert_eq!(bytes, expected);
2043        }
2044
2045        #[test]
2046        fn basic_na_parse() {
2047            let data = Bytes::from_static(&[
2048                0x88, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00,
2049                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
2050            ]);
2051            let pkg = NeighborAdvertPacket::from_bytes(data).unwrap();
2052            assert_eq!(pkg.header.icmpv6_type, Icmpv6Type::NeighborAdvertisement);
2053            assert_eq!(pkg.header.icmpv6_code, Icmpv6Code(0));
2054            assert_eq!(pkg.header.checksum, 0x00);
2055            assert_eq!(pkg.reserved, 0x00);
2056            assert_eq!(pkg.flags, 0x80);
2057            assert_eq!(pkg.target_addr, Ipv6Addr::new(0xff02, 0, 0, 0, 0, 0, 0, 1));
2058        }
2059
2060        #[test]
2061        fn basic_na_create() {
2062            use crate::icmpv6::ndp::{NeighborAdvertFlags, NeighborAdvertPacket};
2063
2064            let packet = NeighborAdvertPacket {
2065                header: Icmpv6Header {
2066                    icmpv6_type: Icmpv6Type::NeighborAdvertisement,
2067                    icmpv6_code: Icmpv6Code(0),
2068                    checksum: 0,
2069                },
2070                flags: NeighborAdvertFlags::Router,
2071                reserved: 0,
2072                target_addr: Ipv6Addr::new(0xff02, 0, 0, 0, 0, 0, 0, 1),
2073                options: vec![],
2074                payload: Bytes::new(),
2075            };
2076
2077            let bytes = packet.to_bytes();
2078
2079            let expected = Bytes::from_static(&[
2080                0x88, 0x00, 0x00, 0x00, // header
2081                0x80, 0x00, 0x00, 0x00, // flags + reserved
2082                0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2083                0x00, 0x01,
2084            ]);
2085
2086            assert_eq!(bytes, expected);
2087        }
2088
2089        #[test]
2090        fn basic_redirect_parse() {
2091            let data = Bytes::from_static(&[
2092                0x89, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00,
2093                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
2094                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2095            ]);
2096            let pkg = RedirectPacket::from_bytes(data).unwrap();
2097            assert_eq!(pkg.header.icmpv6_type, Icmpv6Type::RedirectMessage);
2098            assert_eq!(pkg.header.icmpv6_code, Icmpv6Code(0));
2099            assert_eq!(pkg.header.checksum, 0x00);
2100            assert_eq!(pkg.reserved, 0x00);
2101            assert_eq!(pkg.target_addr, Ipv6Addr::new(0xff02, 0, 0, 0, 0, 0, 0, 1));
2102            assert_eq!(pkg.dest_addr, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0));
2103        }
2104
2105        #[test]
2106        fn basic_redirect_create() {
2107            use crate::icmpv6::ndp::RedirectPacket;
2108
2109            let packet = RedirectPacket {
2110                header: Icmpv6Header {
2111                    icmpv6_type: Icmpv6Type::RedirectMessage,
2112                    icmpv6_code: Icmpv6Code(0),
2113                    checksum: 0,
2114                },
2115                reserved: 0,
2116                target_addr: Ipv6Addr::new(0xff02, 0, 0, 0, 0, 0, 0, 1),
2117                dest_addr: Ipv6Addr::UNSPECIFIED,
2118                options: vec![],
2119                payload: Bytes::new(),
2120            };
2121
2122            let bytes = packet.to_bytes();
2123
2124            let expected = Bytes::from_static(&[
2125                0x89, 0x00, 0x00, 0x00, // header
2126                0x00, 0x00, 0x00, 0x00, // reserved
2127                0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2128                0x00, 0x01, // target
2129                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2130                0x00, 0x00, // dest
2131            ]);
2132
2133            assert_eq!(bytes, expected);
2134        }
2135    }
2136}
2137
2138pub mod echo_request {
2139    //! abstraction for "echo request" ICMPv6 packets.
2140    //!
2141    //! ```text
2142    //! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2143    //! |     Type      |     Code      |          Checksum             |
2144    //! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2145    //! |           Identifier          |        Sequence Number        |
2146    //! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2147    //! |     Data ...
2148    //! +-+-+-+-+-
2149    //! ```
2150
2151    use bytes::Bytes;
2152
2153    use crate::{
2154        icmpv6::{Icmpv6Code, Icmpv6Header, Icmpv6Packet, Icmpv6Type},
2155        packet::Packet,
2156    };
2157
2158    /// Represents the identifier field.
2159    #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
2160    pub struct Identifier(pub u16);
2161
2162    impl Identifier {
2163        /// Create a new `Identifier` instance.
2164        pub fn new(val: u16) -> Identifier {
2165            Identifier(val)
2166        }
2167        /// Get the value of the identifier.
2168        pub fn value(&self) -> u16 {
2169            self.0
2170        }
2171    }
2172
2173    /// Represents the sequence number field.
2174    #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
2175    pub struct SequenceNumber(pub u16);
2176
2177    impl SequenceNumber {
2178        /// Create a new `SequenceNumber` instance.
2179        pub fn new(val: u16) -> SequenceNumber {
2180            SequenceNumber(val)
2181        }
2182        /// Get the value of the sequence number.
2183        pub fn value(&self) -> u16 {
2184            self.0
2185        }
2186    }
2187
2188    /// Enumeration of available ICMPv6 codes for "echo reply" ICMPv6 packets. There is actually only
2189    /// one, since the only valid ICMPv6 code is 0.
2190    #[allow(non_snake_case)]
2191    #[allow(non_upper_case_globals)]
2192    pub mod Icmpv6Codes {
2193        use crate::icmpv6::Icmpv6Code;
2194        /// 0 is the only available ICMPv6 code for "echo reply" ICMPv6 packets.
2195        pub const NoCode: Icmpv6Code = Icmpv6Code(0);
2196    }
2197
2198    /// Represents an "echo request" ICMPv6 packet.
2199    #[derive(Clone, Debug, PartialEq, Eq)]
2200    pub struct EchoRequestPacket {
2201        pub header: Icmpv6Header,
2202        pub identifier: u16,
2203        pub sequence_number: u16,
2204        pub payload: Bytes,
2205    }
2206
2207    impl TryFrom<Icmpv6Packet> for EchoRequestPacket {
2208        type Error = &'static str;
2209
2210        fn try_from(value: Icmpv6Packet) -> Result<Self, Self::Error> {
2211            if value.header.icmpv6_type != Icmpv6Type::EchoRequest {
2212                return Err("Not an Echo Request packet");
2213            }
2214            if value.payload.len() < 8 {
2215                return Err("Payload too short for Echo Request");
2216            }
2217            let identifier = u16::from_be_bytes([value.payload[0], value.payload[1]]);
2218            let sequence_number = u16::from_be_bytes([value.payload[2], value.payload[3]]);
2219            Ok(EchoRequestPacket {
2220                header: value.header,
2221                identifier,
2222                sequence_number,
2223                payload: value.payload.slice(4..),
2224            })
2225        }
2226    }
2227
2228    impl Packet for EchoRequestPacket {
2229        type Header = ();
2230        fn from_buf(bytes: &[u8]) -> Option<Self> {
2231            if bytes.len() < 8 {
2232                return None;
2233            }
2234            let icmpv6_type = Icmpv6Type::new(bytes[0]);
2235            let icmpv6_code = Icmpv6Code::new(bytes[1]);
2236            let checksum = u16::from_be_bytes([bytes[2], bytes[3]]);
2237            let identifier = u16::from_be_bytes([bytes[4], bytes[5]]);
2238            let sequence_number = u16::from_be_bytes([bytes[6], bytes[7]]);
2239            Some(EchoRequestPacket {
2240                header: Icmpv6Header {
2241                    icmpv6_type,
2242                    icmpv6_code,
2243                    checksum,
2244                },
2245                identifier,
2246                sequence_number,
2247                payload: Bytes::copy_from_slice(&bytes[8..]),
2248            })
2249        }
2250        fn from_bytes(bytes: Bytes) -> Option<Self> {
2251            Self::from_buf(&bytes)
2252        }
2253
2254        fn to_bytes(&self) -> Bytes {
2255            let mut bytes = Vec::with_capacity(8 + self.payload.len());
2256            bytes.push(self.header.icmpv6_type.value());
2257            bytes.push(self.header.icmpv6_code.value());
2258            bytes.extend_from_slice(&self.header.checksum.to_be_bytes());
2259            bytes.extend_from_slice(&self.identifier.to_be_bytes());
2260            bytes.extend_from_slice(&self.sequence_number.to_be_bytes());
2261            bytes.extend_from_slice(&self.payload);
2262            Bytes::from(bytes)
2263        }
2264
2265        fn header(&self) -> Bytes {
2266            self.to_bytes().slice(..8)
2267        }
2268
2269        fn payload(&self) -> Bytes {
2270            self.payload.clone()
2271        }
2272
2273        fn header_len(&self) -> usize {
2274            8 // Header length for echo request
2275        }
2276
2277        fn payload_len(&self) -> usize {
2278            self.payload.len()
2279        }
2280
2281        fn total_len(&self) -> usize {
2282            self.header_len() + self.payload_len()
2283        }
2284
2285        fn into_parts(self) -> (Self::Header, Bytes) {
2286            ((), self.payload)
2287        }
2288    }
2289}
2290
2291pub mod echo_reply {
2292    //! abstraction for "echo reply" ICMPv6 packets.
2293    //!
2294    //! ```text
2295    //! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2296    //! |     Type      |     Code      |          Checksum             |
2297    //! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2298    //! |           Identifier          |        Sequence Number        |
2299    //! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2300    //! |     Data ...
2301    //! +-+-+-+-+-
2302    //! ```
2303
2304    use bytes::Bytes;
2305
2306    use crate::{
2307        icmpv6::{Icmpv6Code, Icmpv6Header, Icmpv6Packet, Icmpv6Type},
2308        packet::Packet,
2309    };
2310    /// Represents the identifier field.
2311    #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
2312    pub struct Identifier(pub u16);
2313
2314    impl Identifier {
2315        /// Create a new `Identifier` instance.
2316        pub fn new(val: u16) -> Identifier {
2317            Identifier(val)
2318        }
2319        /// Get the value of the identifier.
2320        pub fn value(&self) -> u16 {
2321            self.0
2322        }
2323    }
2324
2325    /// Represents the sequence number field.
2326    #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
2327    pub struct SequenceNumber(pub u16);
2328
2329    impl SequenceNumber {
2330        /// Create a new `SequenceNumber` instance.
2331        pub fn new(val: u16) -> SequenceNumber {
2332            SequenceNumber(val)
2333        }
2334        /// Get the value of the sequence number.
2335        pub fn value(&self) -> u16 {
2336            self.0
2337        }
2338    }
2339
2340    /// Enumeration of available ICMPv6 codes for "echo reply" ICMPv6 packets. There is actually only
2341    /// one, since the only valid ICMPv6 code is 0.
2342    #[allow(non_snake_case)]
2343    #[allow(non_upper_case_globals)]
2344    pub mod Icmpv6Codes {
2345        use crate::icmpv6::Icmpv6Code;
2346        /// 0 is the only available ICMPv6 code for "echo reply" ICMPv6 packets.
2347        pub const NoCode: Icmpv6Code = Icmpv6Code(0);
2348    }
2349
2350    /// Represents an "echo reply" ICMPv6 packet.
2351    #[derive(Clone, Debug, PartialEq, Eq)]
2352    pub struct EchoReplyPacket {
2353        pub header: Icmpv6Header,
2354        pub identifier: u16,
2355        pub sequence_number: u16,
2356        pub payload: Bytes,
2357    }
2358    impl TryFrom<Icmpv6Packet> for EchoReplyPacket {
2359        type Error = &'static str;
2360
2361        fn try_from(value: Icmpv6Packet) -> Result<Self, Self::Error> {
2362            if value.header.icmpv6_type != Icmpv6Type::EchoReply {
2363                return Err("Not an Echo Reply packet");
2364            }
2365            if value.payload.len() < 8 {
2366                return Err("Payload too short for Echo Reply");
2367            }
2368            let identifier = u16::from_be_bytes([value.payload[0], value.payload[1]]);
2369            let sequence_number = u16::from_be_bytes([value.payload[2], value.payload[3]]);
2370            Ok(EchoReplyPacket {
2371                header: value.header,
2372                identifier,
2373                sequence_number,
2374                payload: value.payload.slice(4..),
2375            })
2376        }
2377    }
2378    impl Packet for EchoReplyPacket {
2379        type Header = ();
2380        fn from_buf(bytes: &[u8]) -> Option<Self> {
2381            if bytes.len() < 8 {
2382                return None;
2383            }
2384            let icmpv6_type = Icmpv6Type::new(bytes[0]);
2385            let icmpv6_code = Icmpv6Code::new(bytes[1]);
2386            let checksum = u16::from_be_bytes([bytes[2], bytes[3]]);
2387            let identifier = u16::from_be_bytes([bytes[4], bytes[5]]);
2388            let sequence_number = u16::from_be_bytes([bytes[6], bytes[7]]);
2389            Some(EchoReplyPacket {
2390                header: Icmpv6Header {
2391                    icmpv6_type,
2392                    icmpv6_code,
2393                    checksum,
2394                },
2395                identifier,
2396                sequence_number,
2397                payload: Bytes::copy_from_slice(&bytes[8..]),
2398            })
2399        }
2400        fn from_bytes(bytes: Bytes) -> Option<Self> {
2401            Self::from_buf(&bytes)
2402        }
2403
2404        fn to_bytes(&self) -> Bytes {
2405            let mut bytes = Vec::with_capacity(8 + self.payload.len());
2406            bytes.push(self.header.icmpv6_type.value());
2407            bytes.push(self.header.icmpv6_code.value());
2408            bytes.extend_from_slice(&self.header.checksum.to_be_bytes());
2409            bytes.extend_from_slice(&self.identifier.to_be_bytes());
2410            bytes.extend_from_slice(&self.sequence_number.to_be_bytes());
2411            bytes.extend_from_slice(&self.payload);
2412            Bytes::from(bytes)
2413        }
2414
2415        fn header(&self) -> Bytes {
2416            self.to_bytes().slice(..8)
2417        }
2418
2419        fn payload(&self) -> Bytes {
2420            self.payload.clone()
2421        }
2422
2423        fn header_len(&self) -> usize {
2424            8 // Header length for echo reply
2425        }
2426
2427        fn payload_len(&self) -> usize {
2428            self.payload.len()
2429        }
2430
2431        fn total_len(&self) -> usize {
2432            self.header_len() + self.payload_len()
2433        }
2434
2435        fn into_parts(self) -> (Self::Header, Bytes) {
2436            ((), self.payload)
2437        }
2438    }
2439}
2440
2441#[cfg(test)]
2442mod echo_tests {
2443    use super::*;
2444    use crate::icmpv6::{
2445        Icmpv6Code, Icmpv6Type, echo_reply::EchoReplyPacket, echo_request::EchoRequestPacket,
2446    };
2447
2448    #[test]
2449    fn test_echo_request_parse() {
2450        let raw = Bytes::from_static(&[
2451            0x80, 0x00, 0xbe, 0xef, // header: type, code, checksum
2452            0x12, 0x34, // identifier
2453            0x56, 0x78, // sequence number
2454            b'p', b'i', b'n', b'g', b'!',
2455        ]);
2456
2457        let parsed = EchoRequestPacket::from_bytes(raw.clone())
2458            .expect("Failed to parse Echo Request packet");
2459
2460        assert_eq!(parsed.header.icmpv6_type, Icmpv6Type::EchoRequest);
2461        assert_eq!(parsed.header.icmpv6_code, Icmpv6Code(0));
2462        assert_eq!(parsed.header.checksum, 0xbeef);
2463        assert_eq!(parsed.identifier, 0x1234);
2464        assert_eq!(parsed.sequence_number, 0x5678);
2465        assert_eq!(parsed.payload, Bytes::from_static(b"ping!"));
2466    }
2467
2468    #[test]
2469    fn test_echo_request_create() {
2470        let payload = Bytes::from_static(b"hello");
2471        let packet = EchoRequestPacket {
2472            header: Icmpv6Header {
2473                icmpv6_type: Icmpv6Type::EchoRequest,
2474                icmpv6_code: Icmpv6Code(0),
2475                checksum: 0,
2476            },
2477            identifier: 0x1234,
2478            sequence_number: 0x5678,
2479            payload: payload.clone(),
2480        };
2481        let bytes = packet.to_bytes();
2482        let parsed = EchoRequestPacket::from_bytes(bytes).unwrap();
2483
2484        assert_eq!(parsed.identifier, 0x1234);
2485        assert_eq!(parsed.sequence_number, 0x5678);
2486        assert_eq!(parsed.payload, payload);
2487    }
2488
2489    #[test]
2490    fn test_echo_reply_parse() {
2491        let raw = Bytes::from_static(&[
2492            0x81, 0x00, 0x12, 0x34, // header: type, code, checksum
2493            0xab, 0xcd, // identifier
2494            0x56, 0x78, // sequence number
2495            b'h', b'e', b'l', b'l', b'o',
2496        ]);
2497
2498        let parsed =
2499            EchoReplyPacket::from_bytes(raw.clone()).expect("Failed to parse Echo Reply packet");
2500
2501        assert_eq!(parsed.header.icmpv6_type, Icmpv6Type::EchoReply);
2502        assert_eq!(parsed.header.icmpv6_code, Icmpv6Code(0));
2503        assert_eq!(parsed.header.checksum, 0x1234);
2504        assert_eq!(parsed.identifier, 0xabcd);
2505        assert_eq!(parsed.sequence_number, 0x5678);
2506        assert_eq!(parsed.payload, Bytes::from_static(b"hello"));
2507    }
2508
2509    #[test]
2510    fn test_echo_reply_create() {
2511        let payload = Bytes::from_static(b"world");
2512        let packet = EchoReplyPacket {
2513            header: Icmpv6Header {
2514                icmpv6_type: Icmpv6Type::EchoReply,
2515                icmpv6_code: Icmpv6Code(0),
2516                checksum: 0,
2517            },
2518            identifier: 0xabcd,
2519            sequence_number: 0x1234,
2520            payload: payload.clone(),
2521        };
2522
2523        let bytes = packet.to_bytes();
2524        let parsed = EchoReplyPacket::from_bytes(bytes).expect("Failed to parse Echo Reply packet");
2525
2526        assert_eq!(parsed.header.icmpv6_type, Icmpv6Type::EchoReply);
2527        assert_eq!(parsed.identifier, 0xabcd);
2528        assert_eq!(parsed.sequence_number, 0x1234);
2529        assert_eq!(parsed.payload, payload);
2530    }
2531}