xenet_packet/
icmpv6.rs

1//! An ICMPv6 packet abstraction.
2
3use crate::ip::IpNextLevelProtocol;
4use crate::PrimitiveValues;
5
6use alloc::vec::Vec;
7
8use crate::ethernet::ETHERNET_HEADER_LEN;
9use crate::ipv6::IPV6_HEADER_LEN;
10use std::net::Ipv6Addr;
11use xenet_macro::packet;
12use xenet_macro_helper::types::*;
13
14#[cfg(feature = "serde")]
15use serde::{Deserialize, Serialize};
16
17/// ICMPv6 Header Length.
18pub const ICMPV6_HEADER_LEN: usize = echo_request::MutableEchoRequestPacket::minimum_packet_size();
19/// ICMPv6 Minimum Packet Length.
20pub const ICMPV6_PACKET_LEN: usize = ETHERNET_HEADER_LEN + IPV6_HEADER_LEN + ICMPV6_HEADER_LEN;
21/// ICMPv6 IP Packet Length.
22pub const ICMPV6_IP_PACKET_LEN: usize = IPV6_HEADER_LEN + ICMPV6_HEADER_LEN;
23
24/// Represents the ICMPv6 header.
25#[derive(Clone, Debug, PartialEq, Eq)]
26#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
27pub struct Icmpv6Header {
28    pub icmpv6_type: Icmpv6Type,
29    pub icmpv6_code: Icmpv6Code,
30    pub checksum: u16be,
31}
32
33impl Icmpv6Header {
34    /// Construct an ICMPv6 header from a byte slice.
35    pub fn from_bytes(packet: &[u8]) -> Result<Icmpv6Header, String> {
36        if packet.len() < ICMPV6_HEADER_LEN {
37            return Err("Packet is too small for ICMPv6 header".to_string());
38        }
39        match Icmpv6Packet::new(packet) {
40            Some(icmpv6_packet) => Ok(Icmpv6Header {
41                icmpv6_type: icmpv6_packet.get_icmpv6_type(),
42                icmpv6_code: icmpv6_packet.get_icmpv6_code(),
43                checksum: icmpv6_packet.get_checksum(),
44            }),
45            None => Err("Failed to parse ICMPv6 packet".to_string()),
46        }
47    }
48    /// Construct an ICMPv6 header from a Icmpv6Packet.
49    pub(crate) fn from_packet(icmpv6_packet: &Icmpv6Packet) -> Icmpv6Header {
50        Icmpv6Header {
51            icmpv6_type: icmpv6_packet.get_icmpv6_type(),
52            icmpv6_code: icmpv6_packet.get_icmpv6_code(),
53            checksum: icmpv6_packet.get_checksum(),
54        }
55    }
56}
57
58/// Represents the ICMPv6 types.
59/// <https://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xhtml>
60#[repr(u8)]
61#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
62#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
63pub enum Icmpv6Type {
64    DestinationUnreachable,
65    PacketTooBig,
66    TimeExceeded,
67    ParameterProblem,
68    EchoRequest,
69    EchoReply,
70    MulticastListenerQuery,
71    MulticastListenerReport,
72    MulticastListenerDone,
73    RouterSolicitation,
74    RouterAdvertisement,
75    NeighborSolicitation,
76    NeighborAdvertisement,
77    RedirectMessage,
78    RouterRenumbering,
79    NodeInformationQuery,
80    NodeInformationResponse,
81    InverseNeighborDiscoverySolicitation,
82    InverseNeighborDiscoveryAdvertisement,
83    Version2MulticastListenerReport,
84    HomeAgentAddressDiscoveryRequest,
85    HomeAgentAddressDiscoveryReply,
86    MobilePrefixSolicitation,
87    MobilePrefixAdvertisement,
88    CertificationPathSolicitationMessage,
89    CertificationPathAdvertisementMessage,
90    ExperimentalMobilityProtocols,
91    MulticastRouterAdvertisement,
92    MulticastRouterSolicitation,
93    MulticastRouterTermination,
94    FMIPv6Messages,
95    RPLControlMessage,
96    ILNPv6LocatorUpdateMessage,
97    DuplicateAddressRequest,
98    DuplicateAddressConfirmation,
99    MPLControlMessage,
100    ExtendedEchoRequest,
101    ExtendedEchoReply,
102    Unknown(u8),
103}
104
105impl Icmpv6Type {
106    pub fn new(value: u8) -> Self {
107        match value {
108            1 => Icmpv6Type::DestinationUnreachable,
109            2 => Icmpv6Type::PacketTooBig,
110            3 => Icmpv6Type::TimeExceeded,
111            4 => Icmpv6Type::ParameterProblem,
112            128 => Icmpv6Type::EchoRequest,
113            129 => Icmpv6Type::EchoReply,
114            130 => Icmpv6Type::MulticastListenerQuery,
115            131 => Icmpv6Type::MulticastListenerReport,
116            132 => Icmpv6Type::MulticastListenerDone,
117            133 => Icmpv6Type::RouterSolicitation,
118            134 => Icmpv6Type::RouterAdvertisement,
119            135 => Icmpv6Type::NeighborSolicitation,
120            136 => Icmpv6Type::NeighborAdvertisement,
121            137 => Icmpv6Type::RedirectMessage,
122            138 => Icmpv6Type::RouterRenumbering,
123            139 => Icmpv6Type::NodeInformationQuery,
124            140 => Icmpv6Type::NodeInformationResponse,
125            141 => Icmpv6Type::InverseNeighborDiscoverySolicitation,
126            142 => Icmpv6Type::InverseNeighborDiscoveryAdvertisement,
127            143 => Icmpv6Type::Version2MulticastListenerReport,
128            144 => Icmpv6Type::HomeAgentAddressDiscoveryRequest,
129            145 => Icmpv6Type::HomeAgentAddressDiscoveryReply,
130            146 => Icmpv6Type::MobilePrefixSolicitation,
131            147 => Icmpv6Type::MobilePrefixAdvertisement,
132            148 => Icmpv6Type::CertificationPathSolicitationMessage,
133            149 => Icmpv6Type::CertificationPathAdvertisementMessage,
134            150 => Icmpv6Type::ExperimentalMobilityProtocols,
135            151 => Icmpv6Type::MulticastRouterAdvertisement,
136            152 => Icmpv6Type::MulticastRouterSolicitation,
137            153 => Icmpv6Type::MulticastRouterTermination,
138            154 => Icmpv6Type::FMIPv6Messages,
139            155 => Icmpv6Type::RPLControlMessage,
140            156 => Icmpv6Type::ILNPv6LocatorUpdateMessage,
141            157 => Icmpv6Type::DuplicateAddressRequest,
142            158 => Icmpv6Type::DuplicateAddressConfirmation,
143            159 => Icmpv6Type::MPLControlMessage,
144            160 => Icmpv6Type::ExtendedEchoRequest,
145            161 => Icmpv6Type::ExtendedEchoReply,
146            n => Icmpv6Type::Unknown(n),
147        }
148    }
149}
150
151impl PrimitiveValues for Icmpv6Type {
152    type T = (u8,);
153    fn to_primitive_values(&self) -> (u8,) {
154        match *self {
155            Icmpv6Type::DestinationUnreachable => (1,),
156            Icmpv6Type::PacketTooBig => (2,),
157            Icmpv6Type::TimeExceeded => (3,),
158            Icmpv6Type::ParameterProblem => (4,),
159            Icmpv6Type::EchoRequest => (128,),
160            Icmpv6Type::EchoReply => (129,),
161            Icmpv6Type::MulticastListenerQuery => (130,),
162            Icmpv6Type::MulticastListenerReport => (131,),
163            Icmpv6Type::MulticastListenerDone => (132,),
164            Icmpv6Type::RouterSolicitation => (133,),
165            Icmpv6Type::RouterAdvertisement => (134,),
166            Icmpv6Type::NeighborSolicitation => (135,),
167            Icmpv6Type::NeighborAdvertisement => (136,),
168            Icmpv6Type::RedirectMessage => (137,),
169            Icmpv6Type::RouterRenumbering => (138,),
170            Icmpv6Type::NodeInformationQuery => (139,),
171            Icmpv6Type::NodeInformationResponse => (140,),
172            Icmpv6Type::InverseNeighborDiscoverySolicitation => (141,),
173            Icmpv6Type::InverseNeighborDiscoveryAdvertisement => (142,),
174            Icmpv6Type::Version2MulticastListenerReport => (143,),
175            Icmpv6Type::HomeAgentAddressDiscoveryRequest => (144,),
176            Icmpv6Type::HomeAgentAddressDiscoveryReply => (145,),
177            Icmpv6Type::MobilePrefixSolicitation => (146,),
178            Icmpv6Type::MobilePrefixAdvertisement => (147,),
179            Icmpv6Type::CertificationPathSolicitationMessage => (148,),
180            Icmpv6Type::CertificationPathAdvertisementMessage => (149,),
181            Icmpv6Type::ExperimentalMobilityProtocols => (150,),
182            Icmpv6Type::MulticastRouterAdvertisement => (151,),
183            Icmpv6Type::MulticastRouterSolicitation => (152,),
184            Icmpv6Type::MulticastRouterTermination => (153,),
185            Icmpv6Type::FMIPv6Messages => (154,),
186            Icmpv6Type::RPLControlMessage => (155,),
187            Icmpv6Type::ILNPv6LocatorUpdateMessage => (156,),
188            Icmpv6Type::DuplicateAddressRequest => (157,),
189            Icmpv6Type::DuplicateAddressConfirmation => (158,),
190            Icmpv6Type::MPLControlMessage => (159,),
191            Icmpv6Type::ExtendedEchoRequest => (160,),
192            Icmpv6Type::ExtendedEchoReply => (161,),
193            Icmpv6Type::Unknown(n) => (n,),
194        }
195    }
196}
197
198/// Represents the "ICMPv6 code" header field.
199#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
200#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
201pub struct Icmpv6Code(pub u8);
202
203impl Icmpv6Code {
204    /// Create a new `Icmpv6Code` instance.
205    pub fn new(val: u8) -> Icmpv6Code {
206        Icmpv6Code(val)
207    }
208}
209
210impl PrimitiveValues for Icmpv6Code {
211    type T = (u8,);
212    fn to_primitive_values(&self) -> (u8,) {
213        (self.0,)
214    }
215}
216
217/// Represents a generic ICMPv6 packet [RFC 4443 § 2.1]
218///
219/// ```text
220/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
221/// |     Type      |     Code      |          Checksum             |
222/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
223/// |                                                               |
224/// +                         Message Body                          +
225/// |                                                               |
226/// ```
227///
228/// [RFC 4443 § 2.1]: https://tools.ietf.org/html/rfc4443#section-2.1
229#[packet]
230pub struct Icmpv6 {
231    #[construct_with(u8)]
232    pub icmpv6_type: Icmpv6Type,
233    #[construct_with(u8)]
234    pub icmpv6_code: Icmpv6Code,
235    pub checksum: u16be,
236    #[payload]
237    pub payload: Vec<u8>,
238}
239
240/// Calculates a checksum of an ICMPv6 packet.
241pub fn checksum(packet: &Icmpv6Packet, source: &Ipv6Addr, destination: &Ipv6Addr) -> u16be {
242    use crate::util;
243    use crate::Packet;
244
245    util::ipv6_checksum(
246        packet.packet(),
247        1,
248        &[],
249        source,
250        destination,
251        IpNextLevelProtocol::Icmpv6,
252    )
253}
254
255#[cfg(test)]
256mod checksum_tests {
257    use super::*;
258    use alloc::vec;
259
260    #[test]
261    fn checksum_echo_request() {
262        // The equivalent of your typical ping -6 ::1%lo
263        let lo = &Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1);
264        let mut data = vec![
265            0x80, // Icmpv6 Type
266            0x00, // Code
267            0xff, 0xff, // Checksum
268            0x00, 0x00, // Id
269            0x00, 0x01, // Sequence
270            // 56 bytes of "random" data
271            0x20, 0x20, 0x75, 0x73, 0x74, 0x20, 0x61, 0x20, 0x66, 0x6c, 0x65, 0x73, 0x68, 0x20,
272            0x77, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x20, 0x74, 0x69, 0x73, 0x20, 0x62, 0x75, 0x74,
273            0x20, 0x61, 0x20, 0x73, 0x63, 0x72, 0x61, 0x74, 0x63, 0x68, 0x20, 0x20, 0x6b, 0x6e,
274            0x69, 0x67, 0x68, 0x74, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x6e, 0x69, 0x20, 0x20, 0x20,
275        ];
276        let mut pkg = MutableIcmpv6Packet::new(&mut data[..]).unwrap();
277        assert_eq!(checksum(&pkg.to_immutable(), lo, lo), 0x1d2e);
278
279        // Check
280        pkg.set_icmpv6_type(Icmpv6Type::new(0x81));
281        assert_eq!(checksum(&pkg.to_immutable(), lo, lo), 0x1c2e);
282    }
283}
284
285pub mod ndp {
286    //! Abstractions for the Neighbor Discovery Protocol [RFC 4861]
287    //!
288    //! [RFC 4861]: https://tools.ietf.org/html/rfc4861
289
290    use crate::icmpv6::{Icmpv6Code, Icmpv6Type};
291    use crate::Packet;
292    use crate::PrimitiveValues;
293
294    use alloc::vec::Vec;
295
296    use std::net::Ipv6Addr;
297    use xenet_macro::packet;
298    use xenet_macro_helper::types::*;
299
300    /// NDP SOL Packet Length.
301    pub const NDP_SOL_PACKET_LEN: usize = NeighborSolicitPacket::minimum_packet_size();
302    /// NDP ADV Packet Length.
303    pub const NDP_ADV_PACKET_LEN: usize = NeighborAdvertPacket::minimum_packet_size();
304    /// NDP OPT Packet Length.
305    pub const NDP_OPT_PACKET_LEN: usize = NdpOptionPacket::minimum_packet_size();
306
307    #[allow(non_snake_case)]
308    #[allow(non_upper_case_globals)]
309    pub mod Icmpv6Codes {
310        use crate::icmpv6::Icmpv6Code;
311        /// 0 is the only available ICMPv6 Code for the NDP.
312        pub const NoCode: Icmpv6Code = Icmpv6Code(0);
313    }
314
315    /// Represents a Neighbor Discovery Option Type.
316    #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
317    pub struct NdpOptionType(pub u8);
318
319    impl NdpOptionType {
320        /// Create a new `NdpOptionType` instance.
321        pub fn new(value: u8) -> NdpOptionType {
322            NdpOptionType(value)
323        }
324    }
325
326    impl PrimitiveValues for NdpOptionType {
327        type T = (u8,);
328        fn to_primitive_values(&self) -> (u8,) {
329            (self.0,)
330        }
331    }
332
333    /// Neighbor Discovery Option Types [RFC 4861 § 4.6]
334    ///
335    /// [RFC 4861 § 4.6]: https://tools.ietf.org/html/rfc4861#section-4.6
336    #[allow(non_snake_case)]
337    #[allow(non_upper_case_globals)]
338    pub mod NdpOptionTypes {
339        use super::NdpOptionType;
340
341        /// Source Link-Layer Address Option [RFC 4861 § 4.6.1]
342        ///
343        /// ```text
344        /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
345        /// |     Type      |    Length     |    Link-Layer Address ...
346        /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
347        /// ```
348        ///
349        /// [RFC 4861 § 4.6.1]: https://tools.ietf.org/html/rfc4861#section-4.6.1
350        pub const SourceLLAddr: NdpOptionType = NdpOptionType(1);
351
352        /// Target Link-Layer Address Option [RFC 4861 § 4.6.1]
353        ///
354        /// ```text
355        /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
356        /// |     Type      |    Length     |    Link-Layer Address ...
357        /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
358        /// ```
359        ///
360        /// [RFC 4861 § 4.6.1]: https://tools.ietf.org/html/rfc4861#section-4.6.1
361        pub const TargetLLAddr: NdpOptionType = NdpOptionType(2);
362
363        /// Prefix Information Option [RFC 4861 § 4.6.2]
364        ///
365        /// ```text
366        /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
367        /// |     Type      |    Length     | Prefix Length |L|A| Reserved1 |
368        /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
369        /// |                         Valid Lifetime                        |
370        /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
371        /// |                       Preferred Lifetime                      |
372        /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
373        /// |                           Reserved2                           |
374        /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
375        /// |                                                               |
376        /// +                                                               +
377        /// |                                                               |
378        /// +                            Prefix                             +
379        /// |                                                               |
380        /// +                                                               +
381        /// |                                                               |
382        /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
383        /// ```
384        ///
385        /// [RFC 4861 § 4.6.2]: https://tools.ietf.org/html/rfc4861#section-4.6.2
386        pub const PrefixInformation: NdpOptionType = NdpOptionType(3);
387
388        /// Redirected Header Option [RFC 4861 § 4.6.3]
389        ///
390        /// ```text
391        /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
392        /// |     Type      |    Length     |            Reserved           |
393        /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
394        /// |                           Reserved                            |
395        /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
396        /// |                                                               |
397        /// ~                       IP header + data                        ~
398        /// |                                                               |
399        /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
400        /// ```
401        ///
402        /// [RFC 4861 § 4.6.3]: https://tools.ietf.org/html/rfc4861#section-4.6.3
403        pub const RedirectedHeader: NdpOptionType = NdpOptionType(4);
404
405        /// MTU Option [RFC 4861 § 4.6.4]
406        ///
407        /// ```text
408        /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
409        /// |     Type      |    Length     |           Reserved            |
410        /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
411        /// |                              MTU                              |
412        /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
413        /// ```
414        ///
415        /// [RFC 4861 § 4.6.4]: https://tools.ietf.org/html/rfc4861#section-4.6.4
416        pub const MTU: NdpOptionType = NdpOptionType(5);
417    }
418
419    /// Neighbor Discovery Option [RFC 4861 § 4.6]
420    ///
421    /// ```text
422    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
423    /// |     Type      |    Length     |              ...              |
424    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
425    /// ~                              ...                              ~
426    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
427    /// ```
428    ///
429    /// [RFC 4861 § 4.6]: https://tools.ietf.org/html/rfc4861#section-4.6
430    #[packet]
431    pub struct NdpOption {
432        #[construct_with(u8)]
433        pub option_type: NdpOptionType,
434        #[construct_with(u8)]
435        pub length: u8,
436        #[length_fn = "ndp_option_payload_length"]
437        #[payload]
438        pub data: Vec<u8>,
439    }
440
441    /// Calculate a length of a `NdpOption`'s payload.
442    fn ndp_option_payload_length(option: &NdpOptionPacket) -> usize {
443        let len = option.get_length();
444        if len > 0 {
445            ((len * 8) - 2) as usize
446        } else {
447            0
448        }
449    }
450
451    /// Router Solicitation Message [RFC 4861 § 4.1]
452    ///
453    /// ```text
454    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
455    /// |     Type      |     Code      |          Checksum             |
456    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
457    /// |                            Reserved                           |
458    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
459    /// |   Options ...
460    /// ```
461    ///
462    /// [RFC 4861 § 4.1]: https://tools.ietf.org/html/rfc4861#section-4.1
463    #[packet]
464    pub struct RouterSolicit {
465        #[construct_with(u8)]
466        pub icmpv6_type: Icmpv6Type,
467        #[construct_with(u8)]
468        pub icmpv6_code: Icmpv6Code,
469        pub checksum: u16be,
470        pub reserved: u32be,
471        #[length_fn = "rs_ndp_options_length"]
472        pub options: Vec<NdpOption>,
473        #[payload]
474        #[length = "0"]
475        pub payload: Vec<u8>,
476    }
477
478    /// Router Solicit packet calculation for the length of the options.
479    fn rs_ndp_options_length(pkt: &RouterSolicitPacket) -> usize {
480        if pkt.packet().len() > 8 {
481            pkt.packet().len() - 8
482        } else {
483            0
484        }
485    }
486
487    /// The enumeration of recognized Router Advert flags.
488    #[allow(non_snake_case)]
489    #[allow(non_upper_case_globals)]
490    pub mod RouterAdvertFlags {
491        /// "Managed Address Configuration" flag. This is set when
492        /// addresses are available via DHCPv6.
493        pub const ManagedAddressConf: u8 = 0b10000000;
494        /// "Other Configuration" flag. This is set when other
495        /// configuration information is available via DHCPv6.
496        pub const OtherConf: u8 = 0b01000000;
497    }
498
499    /// Router Advertisement Message Format [RFC 4861 § 4.2]
500    ///
501    /// ```text
502    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
503    /// |     Type      |     Code      |          Checksum             |
504    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
505    /// | Cur Hop Limit |M|O|  Reserved |       Router Lifetime         |
506    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
507    /// |                         Reachable Time                        |
508    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
509    /// |                          Retrans Timer                        |
510    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
511    /// |   Options ...
512    /// +-+-+-+-+-+-+-+-+-+-+-+-
513    /// ```
514    ///
515    /// [RFC 4861 § 4.2]: https://tools.ietf.org/html/rfc4861#section-4.2
516    #[packet]
517    pub struct RouterAdvert {
518        #[construct_with(u8)]
519        pub icmpv6_type: Icmpv6Type,
520        #[construct_with(u8)]
521        pub icmpv6_code: Icmpv6Code,
522        pub checksum: u16be,
523        pub hop_limit: u8,
524        pub flags: u8,
525        pub lifetime: u16be,
526        pub reachable_time: u32be,
527        pub retrans_time: u32be,
528        #[length_fn = "ra_ndp_options_length"]
529        pub options: Vec<NdpOption>,
530        #[payload]
531        #[length = "0"]
532        pub payload: Vec<u8>,
533    }
534
535    /// Router Advert packet calculation for the length of the options.
536    fn ra_ndp_options_length(pkt: &RouterAdvertPacket) -> usize {
537        if pkt.packet().len() > 16 {
538            pkt.packet().len() - 16
539        } else {
540            0
541        }
542    }
543
544    /// Neighbor Solicitation Message Format [RFC 4861 § 4.3]
545    ///
546    /// ```text
547    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
548    /// |     Type      |     Code      |          Checksum             |
549    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
550    /// |                           Reserved                            |
551    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
552    /// |                                                               |
553    /// +                                                               +
554    /// |                                                               |
555    /// +                       Target Address                          +
556    /// |                                                               |
557    /// +                                                               +
558    /// |                                                               |
559    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
560    /// |   Options ...
561    /// +-+-+-+-+-+-+-+-+-+-+-+-
562    /// ```
563    ///
564    /// [RFC 4861 § 4.3]: https://tools.ietf.org/html/rfc4861#section-4.3
565    #[packet]
566    pub struct NeighborSolicit {
567        #[construct_with(u8)]
568        pub icmpv6_type: Icmpv6Type,
569        #[construct_with(u8)]
570        pub icmpv6_code: Icmpv6Code,
571        pub checksum: u16be,
572        pub reserved: u32be,
573        #[construct_with(u16, u16, u16, u16, u16, u16, u16, u16)]
574        pub target_addr: Ipv6Addr,
575        #[length_fn = "ns_ndp_options_length"]
576        pub options: Vec<NdpOption>,
577        #[payload]
578        #[length = "0"]
579        pub payload: Vec<u8>,
580    }
581
582    /// Neighbor Solicit packet calculation for the length of the options.
583    fn ns_ndp_options_length(pkt: &NeighborSolicitPacket) -> usize {
584        if pkt.packet().len() > 24 {
585            pkt.packet().len() - 24
586        } else {
587            0
588        }
589    }
590
591    /// Enumeration of recognized Neighbor Advert flags.
592    #[allow(non_snake_case)]
593    #[allow(non_upper_case_globals)]
594    pub mod NeighborAdvertFlags {
595        /// Indicates that the sender is a router.
596        pub const Router: u8 = 0b10000000;
597        /// Indicates that the advertisement was sent due to the receipt of a
598        /// Neighbor Solicitation message.
599        pub const Solicited: u8 = 0b01000000;
600        /// Indicates that the advertisement should override an existing cache
601        /// entry.
602        pub const Override: u8 = 0b00100000;
603    }
604
605    /// Neighbor Advertisement Message Format [RFC 4861 § 4.4]
606    ///
607    /// ```text
608    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
609    /// |     Type      |     Code      |          Checksum             |
610    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
611    /// |R|S|O|                     Reserved                            |
612    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
613    /// |                                                               |
614    /// +                                                               +
615    /// |                                                               |
616    /// +                       Target Address                          +
617    /// |                                                               |
618    /// +                                                               +
619    /// |                                                               |
620    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
621    /// |   Options ...
622    /// +-+-+-+-+-+-+-+-+-+-+-+-
623    /// ```
624    ///
625    /// [RFC 4861 § 4.4]: https://tools.ietf.org/html/rfc4861#section-4.4
626    #[packet]
627    pub struct NeighborAdvert {
628        #[construct_with(u8)]
629        pub icmpv6_type: Icmpv6Type,
630        #[construct_with(u8)]
631        pub icmpv6_code: Icmpv6Code,
632        pub checksum: u16be,
633        pub flags: u8,
634        pub reserved: u24be,
635        #[construct_with(u16, u16, u16, u16, u16, u16, u16, u16)]
636        pub target_addr: Ipv6Addr,
637        #[length_fn = "na_ndp_options_length"]
638        pub options: Vec<NdpOption>,
639        #[payload]
640        #[length = "0"]
641        pub payload: Vec<u8>,
642    }
643
644    /// Neighbor Advert packet calculation for the length of the options.
645    fn na_ndp_options_length(pkt: &NeighborAdvertPacket) -> usize {
646        if pkt.packet().len() > 24 {
647            pkt.packet().len() - 24
648        } else {
649            0
650        }
651    }
652
653    /// Redirect Message Format [RFC 4861 § 4.5]
654    ///
655    /// ```text
656    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
657    /// |     Type      |     Code      |          Checksum             |
658    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
659    /// |                           Reserved                            |
660    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
661    /// |                                                               |
662    /// +                                                               +
663    /// |                                                               |
664    /// +                       Target Address                          +
665    /// |                                                               |
666    /// +                                                               +
667    /// |                                                               |
668    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
669    /// |                                                               |
670    /// +                                                               +
671    /// |                                                               |
672    /// +                     Destination Address                       +
673    /// |                                                               |
674    /// +                                                               +
675    /// |                                                               |
676    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
677    /// |   Options ...
678    /// +-+-+-+-+-+-+-+-+-+-+-+-
679    /// ```
680    ///
681    /// [RFC 4861 § 4.5]: https://tools.ietf.org/html/rfc4861#section-4.5
682    #[packet]
683    pub struct Redirect {
684        #[construct_with(u8)]
685        pub icmpv6_type: Icmpv6Type,
686        #[construct_with(u8)]
687        pub icmpv6_code: Icmpv6Code,
688        pub checksum: u16be,
689        pub reserved: u32be,
690        #[construct_with(u16, u16, u16, u16, u16, u16, u16, u16)]
691        pub target_addr: Ipv6Addr,
692        #[construct_with(u16, u16, u16, u16, u16, u16, u16, u16)]
693        pub dest_addr: Ipv6Addr,
694        #[length_fn = "redirect_options_length"]
695        pub options: Vec<NdpOption>,
696        #[payload]
697        #[length = "0"]
698        pub payload: Vec<u8>,
699    }
700
701    /// Redirect packet calculation for the length of the options.
702    fn redirect_options_length(pkt: &RedirectPacket) -> usize {
703        if pkt.packet().len() > 40 {
704            pkt.packet().len() - 40
705        } else {
706            0
707        }
708    }
709
710    #[cfg(test)]
711    mod ndp_tests {
712        use super::*;
713        use crate::icmpv6::{Icmpv6Code, Icmpv6Type};
714        use alloc::vec;
715
716        #[test]
717        fn basic_option_parsing() {
718            let mut data = vec![
719                0x02, 0x01, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01,
720                // Extra bytes to confuse the parsing
721                0x00, 0x00, 0x00,
722            ];
723            let pkg = MutableNdpOptionPacket::new(&mut data[..]).unwrap();
724            assert_eq!(pkg.get_option_type(), NdpOptionTypes::TargetLLAddr);
725            assert_eq!(pkg.get_length(), 0x01);
726            assert_eq!(pkg.payload().len(), 6);
727            assert_eq!(pkg.payload(), &[0x06, 0x05, 0x04, 0x03, 0x02, 0x01]);
728        }
729
730        #[test]
731        fn basic_rs_parse() {
732            let mut data = vec![
733                0x85, // Type
734                0x00, // Code
735                0x00, 0x00, // Checksum
736                0x00, 0x00, 0x00, 0x00, // Reserved
737                0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00,
738                0x00, 0x00,
739            ];
740
741            let pkg = MutableRouterSolicitPacket::new(&mut data[..]).unwrap();
742            assert_eq!(pkg.get_icmpv6_type(), Icmpv6Type::RouterSolicitation);
743            assert_eq!(pkg.get_icmpv6_code(), Icmpv6Code(0));
744            assert_eq!(pkg.get_checksum(), 0);
745            assert_eq!(pkg.get_reserved(), 0);
746            assert_eq!(pkg.get_options().len(), 2);
747
748            let option = &pkg.get_options()[0];
749            assert_eq!(option.option_type, NdpOptionTypes::TargetLLAddr);
750            assert_eq!(option.length, 0x01);
751            assert_eq!(option.data, &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
752            assert_eq!(option.data.len(), 6);
753
754            let option = &pkg.get_options()[1];
755            assert_eq!(option.option_type, NdpOptionTypes::SourceLLAddr);
756            assert_eq!(option.length, 1);
757            assert_eq!(option.data, &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
758        }
759
760        #[test]
761        fn basic_rs_create() {
762            let ref_packet = vec![
763                0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00,
764                0x00, 0x00,
765            ];
766            let mut packet = [0u8; 16];
767            let options = vec![NdpOption {
768                option_type: NdpOptionTypes::SourceLLAddr,
769                length: 1,
770                data: vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
771            }];
772            {
773                let mut rs_packet = MutableRouterSolicitPacket::new(&mut packet[..]).unwrap();
774                rs_packet.set_icmpv6_type(Icmpv6Type::RouterSolicitation);
775                rs_packet.set_icmpv6_code(Icmpv6Code(0));
776                rs_packet.set_options(&options[..]);
777            }
778            assert_eq!(&ref_packet[..], &packet[..]);
779        }
780
781        #[test]
782        fn basic_ra_parse() {
783            let mut data = vec![
784                0x86, // Type
785                0x00, // Code
786                0x00, 0x00, // Checksum
787                0xff, // Hop Limit
788                0x80, // Flags
789                0x09, 0x00, // Lifetime
790                0x12, 0x34, 0x56, 0x78, // Reachable
791                0x87, 0x65, 0x43, 0x21, // Retrans
792                0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Source Link-Layer
793                0x05, 0x01, 0x00, 0x00, 0x57, 0x68, 0x61, 0x74, // MTU
794            ];
795            let pkg = MutableRouterAdvertPacket::new(&mut data[..]).unwrap();
796            assert_eq!(pkg.get_icmpv6_type(), Icmpv6Type::RouterAdvertisement);
797            assert_eq!(pkg.get_icmpv6_code(), Icmpv6Code(0));
798            assert_eq!(pkg.get_checksum(), 0x00);
799            assert_eq!(pkg.get_hop_limit(), 0xff);
800            assert_eq!(pkg.get_flags(), RouterAdvertFlags::ManagedAddressConf);
801            assert_eq!(pkg.get_lifetime(), 0x900);
802            assert_eq!(pkg.get_reachable_time(), 0x12345678);
803            assert_eq!(pkg.get_retrans_time(), 0x87654321);
804            assert_eq!(pkg.get_options().len(), 2);
805
806            let option = &pkg.get_options()[0];
807            assert_eq!(option.option_type, NdpOptionTypes::SourceLLAddr);
808            assert_eq!(option.length, 1);
809            assert_eq!(option.data, &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
810
811            let option = &pkg.get_options()[1];
812            assert_eq!(option.option_type, NdpOptionTypes::MTU);
813            assert_eq!(option.length, 1);
814            assert_eq!(option.data, &[0x00, 0x00, 0x57, 0x68, 0x61, 0x74]);
815        }
816
817        #[test]
818        fn basic_ra_create() {
819            let ref_packet = vec![
820                0x86, 0x00, 0x00, 0x00, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
821                0x00, 0x00, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
822            ];
823            let mut packet = [0u8; 24];
824            let options = vec![NdpOption {
825                option_type: NdpOptionTypes::MTU,
826                length: 1,
827                data: vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
828            }];
829            {
830                let mut ra_packet = MutableRouterAdvertPacket::new(&mut packet[..]).unwrap();
831                ra_packet.set_icmpv6_type(Icmpv6Type::RouterAdvertisement);
832                ra_packet.set_icmpv6_code(Icmpv6Code(0));
833                ra_packet.set_hop_limit(0xff);
834                ra_packet.set_flags(RouterAdvertFlags::ManagedAddressConf);
835                ra_packet.set_options(&options[..]);
836            }
837            assert_eq!(&ref_packet[..], &packet[..]);
838        }
839
840        #[test]
841        fn basic_ns_parse() {
842            let mut data = vec![
843                0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00,
844                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
845            ];
846            let pkg = MutableNeighborSolicitPacket::new(&mut data[..]).unwrap();
847            assert_eq!(pkg.get_icmpv6_type(), Icmpv6Type::NeighborSolicitation);
848            assert_eq!(pkg.get_icmpv6_code(), Icmpv6Code(0));
849            assert_eq!(pkg.get_checksum(), 0x00);
850            assert_eq!(pkg.get_reserved(), 0x00);
851            assert_eq!(
852                pkg.get_target_addr(),
853                Ipv6Addr::new(0xff02, 0, 0, 0, 0, 0, 0, 1)
854            );
855        }
856
857        #[test]
858        fn basic_ns_create() {
859            let ref_packet = vec![
860                0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00,
861                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
862            ];
863            let mut packet = [0u8; 24];
864            {
865                let mut ns_packet = MutableNeighborSolicitPacket::new(&mut packet[..]).unwrap();
866                ns_packet.set_icmpv6_type(Icmpv6Type::NeighborSolicitation);
867                ns_packet.set_icmpv6_code(Icmpv6Code(0));
868                ns_packet.set_target_addr(Ipv6Addr::new(0xff02, 0, 0, 0, 0, 0, 0, 1));
869            }
870            assert_eq!(&ref_packet[..], &packet[..]);
871        }
872
873        #[test]
874        fn basic_na_parse() {
875            let mut data = vec![
876                0x88, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00,
877                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
878            ];
879            let pkg = MutableNeighborAdvertPacket::new(&mut data[..]).unwrap();
880            assert_eq!(pkg.get_icmpv6_type(), Icmpv6Type::NeighborAdvertisement);
881            assert_eq!(pkg.get_icmpv6_code(), Icmpv6Code(0));
882            assert_eq!(pkg.get_checksum(), 0x00);
883            assert_eq!(pkg.get_reserved(), 0x00);
884            assert_eq!(pkg.get_flags(), 0x80);
885            assert_eq!(
886                pkg.get_target_addr(),
887                Ipv6Addr::new(0xff02, 0, 0, 0, 0, 0, 0, 1)
888            );
889        }
890
891        #[test]
892        fn basic_na_create() {
893            let ref_packet = vec![
894                0x88, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00,
895                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
896            ];
897            let mut packet = [0u8; 24];
898            {
899                let mut na_packet = MutableNeighborAdvertPacket::new(&mut packet[..]).unwrap();
900                na_packet.set_icmpv6_type(Icmpv6Type::NeighborAdvertisement);
901                na_packet.set_icmpv6_code(Icmpv6Code(0));
902                na_packet.set_target_addr(Ipv6Addr::new(0xff02, 0, 0, 0, 0, 0, 0, 1));
903                na_packet.set_flags(NeighborAdvertFlags::Router);
904            }
905            assert_eq!(&ref_packet[..], &packet[..]);
906        }
907
908        #[test]
909        fn basic_redirect_parse() {
910            let mut data = vec![
911                0x89, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00,
912                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
913                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
914            ];
915            let pkg = MutableRedirectPacket::new(&mut data[..]).unwrap();
916            assert_eq!(pkg.get_icmpv6_type(), Icmpv6Type::RedirectMessage);
917            assert_eq!(pkg.get_icmpv6_code(), Icmpv6Code(0));
918            assert_eq!(pkg.get_checksum(), 0x00);
919            assert_eq!(pkg.get_reserved(), 0x00);
920            assert_eq!(
921                pkg.get_target_addr(),
922                Ipv6Addr::new(0xff02, 0, 0, 0, 0, 0, 0, 1)
923            );
924            assert_eq!(pkg.get_dest_addr(), Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0));
925        }
926
927        #[test]
928        fn basic_redirect_create() {
929            let ref_packet = vec![
930                0x89, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00,
931                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
932                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
933            ];
934            let mut packet = [0u8; 40];
935            {
936                let mut rdr_packet = MutableRedirectPacket::new(&mut packet[..]).unwrap();
937                rdr_packet.set_icmpv6_type(Icmpv6Type::RedirectMessage);
938                rdr_packet.set_icmpv6_code(Icmpv6Code(0));
939                rdr_packet.set_target_addr(Ipv6Addr::new(0xff02, 0, 0, 0, 0, 0, 0, 1));
940                rdr_packet.set_dest_addr(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0));
941            }
942            assert_eq!(&ref_packet[..], &packet[..]);
943        }
944    }
945}
946
947pub mod echo_reply {
948    //! abstraction for "echo reply" ICMPv6 packets.
949    //!
950    //! ```text
951    //! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
952    //! |     Type      |     Code      |          Checksum             |
953    //! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
954    //! |           Identifier          |        Sequence Number        |
955    //! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
956    //! |     Data ...
957    //! +-+-+-+-+-
958    //! ```
959
960    use crate::icmpv6::{Icmpv6Code, Icmpv6Type};
961    use crate::PrimitiveValues;
962
963    use alloc::vec::Vec;
964
965    use xenet_macro::packet;
966    use xenet_macro_helper::types::*;
967
968    /// Represents the identifier field.
969    #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
970    pub struct Identifier(pub u16);
971
972    impl Identifier {
973        /// Create a new `Identifier` instance.
974        pub fn new(val: u16) -> Identifier {
975            Identifier(val)
976        }
977    }
978
979    impl PrimitiveValues for Identifier {
980        type T = (u16,);
981        fn to_primitive_values(&self) -> (u16,) {
982            (self.0,)
983        }
984    }
985
986    /// Represents the sequence number field.
987    #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
988    pub struct SequenceNumber(pub u16);
989
990    impl SequenceNumber {
991        /// Create a new `SequenceNumber` instance.
992        pub fn new(val: u16) -> SequenceNumber {
993            SequenceNumber(val)
994        }
995    }
996
997    impl PrimitiveValues for SequenceNumber {
998        type T = (u16,);
999        fn to_primitive_values(&self) -> (u16,) {
1000            (self.0,)
1001        }
1002    }
1003
1004    /// Enumeration of available ICMPv6 codes for "echo reply" ICMPv6 packets. There is actually only
1005    /// one, since the only valid ICMPv6 code is 0.
1006    #[allow(non_snake_case)]
1007    #[allow(non_upper_case_globals)]
1008    pub mod Icmpv6Codes {
1009        use crate::icmpv6::Icmpv6Code;
1010        /// 0 is the only available ICMPv6 code for "echo reply" ICMPv6 packets.
1011        pub const NoCode: Icmpv6Code = Icmpv6Code(0);
1012    }
1013
1014    /// Represents an "echo reply" ICMPv6 packet.
1015    #[packet]
1016    pub struct EchoReply {
1017        #[construct_with(u8)]
1018        pub icmpv6_type: Icmpv6Type,
1019        #[construct_with(u8)]
1020        pub icmpv6_code: Icmpv6Code,
1021        pub checksum: u16be,
1022        pub identifier: u16be,
1023        pub sequence_number: u16be,
1024        #[payload]
1025        pub payload: Vec<u8>,
1026    }
1027}
1028
1029pub mod echo_request {
1030    //! abstraction for "echo request" ICMPv6 packets.
1031    //!
1032    //! ```text
1033    //! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1034    //! |     Type      |     Code      |          Checksum             |
1035    //! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1036    //! |           Identifier          |        Sequence Number        |
1037    //! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1038    //! |     Data ...
1039    //! +-+-+-+-+-
1040    //! ```
1041
1042    use crate::icmpv6::{Icmpv6Code, Icmpv6Type};
1043    use crate::PrimitiveValues;
1044
1045    use alloc::vec::Vec;
1046
1047    use xenet_macro::packet;
1048    use xenet_macro_helper::types::*;
1049
1050    /// Represents the identifier field.
1051    #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
1052    pub struct Identifier(pub u16);
1053
1054    impl Identifier {
1055        /// Create a new `Identifier` instance.
1056        pub fn new(val: u16) -> Identifier {
1057            Identifier(val)
1058        }
1059    }
1060
1061    impl PrimitiveValues for Identifier {
1062        type T = (u16,);
1063        fn to_primitive_values(&self) -> (u16,) {
1064            (self.0,)
1065        }
1066    }
1067
1068    /// Represents the sequence number field.
1069    #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
1070    pub struct SequenceNumber(pub u16);
1071
1072    impl SequenceNumber {
1073        /// Create a new `SequenceNumber` instance.
1074        pub fn new(val: u16) -> SequenceNumber {
1075            SequenceNumber(val)
1076        }
1077    }
1078
1079    impl PrimitiveValues for SequenceNumber {
1080        type T = (u16,);
1081        fn to_primitive_values(&self) -> (u16,) {
1082            (self.0,)
1083        }
1084    }
1085
1086    /// Enumeration of available ICMPv6 codes for "echo reply" ICMPv6 packets. There is actually only
1087    /// one, since the only valid ICMPv6 code is 0.
1088    #[allow(non_snake_case)]
1089    #[allow(non_upper_case_globals)]
1090    pub mod Icmpv6Codes {
1091        use crate::icmpv6::Icmpv6Code;
1092        /// 0 is the only available ICMPv6 code for "echo reply" ICMPv6 packets.
1093        pub const NoCode: Icmpv6Code = Icmpv6Code(0);
1094    }
1095
1096    /// Represents an "echo request" ICMPv6 packet.
1097    #[packet]
1098    pub struct EchoRequest {
1099        #[construct_with(u8)]
1100        pub icmpv6_type: Icmpv6Type,
1101        #[construct_with(u8)]
1102        pub icmpv6_code: Icmpv6Code,
1103        pub checksum: u16be,
1104        pub identifier: u16be,
1105        pub sequence_number: u16be,
1106        #[payload]
1107        pub payload: Vec<u8>,
1108    }
1109}