nex_packet/
icmpv6.rs

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