libpacket/
icmpv6.rs

1// Copyright (c) 2014, 2015 Robert Clipsham <robert@octarineparrot.com>
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9//! An ICMPv6 packet abstraction.
10
11use crate::ip::IpNextHeaderProtocols;
12use crate::{types::*, util, Packet, PrimitiveValues};
13use std::net::Ipv6Addr;
14
15/// Represents the "ICMPv6 type" header field.
16#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
17pub struct Icmpv6Type(pub u8);
18
19impl Icmpv6Type {
20    /// Create a new `Icmpv6Type` instance.
21    pub fn new(val: u8) -> Icmpv6Type {
22        Icmpv6Type(val)
23    }
24}
25
26impl PrimitiveValues for Icmpv6Type {
27    type T = (u8,);
28    fn to_primitive_values(&self) -> (u8,) {
29        (self.0,)
30    }
31}
32
33/// Represents the "ICMPv6 code" header field.
34#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
35pub struct Icmpv6Code(pub u8);
36
37impl Icmpv6Code {
38    /// Create a new `Icmpv6Code` instance.
39    pub fn new(val: u8) -> Icmpv6Code {
40        Icmpv6Code(val)
41    }
42}
43
44impl PrimitiveValues for Icmpv6Code {
45    type T = (u8,);
46    fn to_primitive_values(&self) -> (u8,) {
47        (self.0,)
48    }
49}
50
51/// Represents a generic ICMPv6 packet [RFC 4443 § 2.1]
52///
53/// ```text
54/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
55/// |     Type      |     Code      |          Checksum             |
56/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
57/// |                                                               |
58/// +                         Message Body                          +
59/// |                                                               |
60/// ```
61///
62/// [RFC 4443 § 2.1]: https://tools.ietf.org/html/rfc4443#section-2.1
63#[derive(Debug, Packet)]
64pub struct Icmpv6 {
65    #[construct_with(u8)]
66    pub icmpv6_type: Icmpv6Type,
67    #[construct_with(u8)]
68    pub icmpv6_code: Icmpv6Code,
69    pub checksum: u16be,
70    #[payload]
71    pub payload: Vec<u8>,
72}
73
74/// Calculates a checksum of an ICMPv6 packet.
75pub fn checksum(packet: &Icmpv6Packet, source: &Ipv6Addr, destination: &Ipv6Addr) -> u16be {
76    util::ipv6_checksum(
77        packet.packet(),
78        1,
79        &[],
80        source,
81        destination,
82        IpNextHeaderProtocols::Icmpv6,
83    )
84}
85
86#[cfg(test)]
87mod checksum_tests {
88    use super::*;
89
90    #[test]
91    fn checksum_echo_request() {
92        // The equivalent of your typical ping -6 ::1%lo
93        let lo = &Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1);
94        let mut data = vec![
95            0x80, // Icmpv6 Type
96            0x00, // Code
97            0xff, 0xff, // Checksum
98            0x00, 0x00, // Id
99            0x00, 0x01, // Sequence
100            // 56 bytes of "random" data
101            0x20, 0x20, 0x75, 0x73, 0x74, 0x20, 0x61, 0x20, 0x66, 0x6c, 0x65, 0x73, 0x68, 0x20,
102            0x77, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x20, 0x74, 0x69, 0x73, 0x20, 0x62, 0x75, 0x74,
103            0x20, 0x61, 0x20, 0x73, 0x63, 0x72, 0x61, 0x74, 0x63, 0x68, 0x20, 0x20, 0x6b, 0x6e,
104            0x69, 0x67, 0x68, 0x74, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x6e, 0x69, 0x20, 0x20, 0x20,
105        ];
106        let mut pkg = MutableIcmpv6Packet::new(&mut data[..]).unwrap();
107        assert_eq!(checksum(&pkg.to_immutable(), lo, lo), 0x1d2e);
108
109        // Check
110        pkg.set_icmpv6_type(Icmpv6Type(0x81));
111        assert_eq!(checksum(&pkg.to_immutable(), lo, lo), 0x1c2e);
112    }
113}
114
115/// The enumeration of the recognized ICMPv6 types.
116#[allow(non_snake_case)]
117#[allow(non_upper_case_globals)]
118pub mod Icmpv6Types {
119    use crate::icmpv6::Icmpv6Type;
120    /// ICMPv6 type for "destination unreachable".
121    pub const DestinationUnreachable: Icmpv6Type = Icmpv6Type(1);
122    /// ICMPv6 type for "packet too big".
123    pub const PacketTooBig: Icmpv6Type = Icmpv6Type(2);
124    /// ICMPv6 type for "time exceeded".
125    pub const TimeExceeded: Icmpv6Type = Icmpv6Type(3);
126    /// ICMPv6 type for "parameter problem".
127    pub const ParameterProblem: Icmpv6Type = Icmpv6Type(4);
128    /// ICMPv6 type for "echo request".
129    pub const EchoRequest: Icmpv6Type = Icmpv6Type(128);
130    /// ICMPv6 type for "echo reply".
131    pub const EchoReply: Icmpv6Type = Icmpv6Type(129);
132    // Neighbor Discovery Protocol [RFC4861]
133    /// ICMPv6 type for "router solicitation".
134    pub const RouterSolicit: Icmpv6Type = Icmpv6Type(133);
135    /// ICMPv6 type for "router advertisement".
136    pub const RouterAdvert: Icmpv6Type = Icmpv6Type(134);
137    /// ICMPv6 type for "neighbor solicitation".
138    pub const NeighborSolicit: Icmpv6Type = Icmpv6Type(135);
139    /// ICMPv6 type for "neighbor advertisement".
140    pub const NeighborAdvert: Icmpv6Type = Icmpv6Type(136);
141    /// ICMPv6 type for "redirect".
142    pub const Redirect: Icmpv6Type = Icmpv6Type(137);
143}
144
145pub mod ndp {
146    //! Abstractions for the Neighbor Discovery Protocol [RFC 4861]
147    //!
148    //! [RFC 4861]: https://tools.ietf.org/html/rfc4861
149
150    use crate::icmpv6::{Icmpv6Code, Icmpv6Type};
151    use crate::{types::*, Packet, PrimitiveValues};
152    use std::net::Ipv6Addr;
153
154    #[allow(non_snake_case)]
155    #[allow(non_upper_case_globals)]
156    pub mod Icmpv6Codes {
157        use crate::icmpv6::Icmpv6Code;
158        /// 0 is the only available ICMPv6 Code for the NDP.
159        pub const NoCode: Icmpv6Code = Icmpv6Code(0);
160    }
161
162    /// Represents a Neighbor Discovery Option Type.
163    #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
164    pub struct NdpOptionType(pub u8);
165
166    impl NdpOptionType {
167        /// Create a new `NdpOptionType` instance.
168        pub fn new(value: u8) -> NdpOptionType {
169            NdpOptionType(value)
170        }
171    }
172
173    impl PrimitiveValues for NdpOptionType {
174        type T = (u8,);
175        fn to_primitive_values(&self) -> (u8,) {
176            (self.0,)
177        }
178    }
179
180    /// Neighbor Discovery Option Types [RFC 4861 § 4.6]
181    ///
182    /// [RFC 4861 § 4.6]: https://tools.ietf.org/html/rfc4861#section-4.6
183    #[allow(non_snake_case)]
184    #[allow(non_upper_case_globals)]
185    pub mod NdpOptionTypes {
186        use super::NdpOptionType;
187
188        /// Source Link-Layer Address Option [RFC 4861 § 4.6.1]
189        ///
190        /// ```text
191        /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
192        /// |     Type      |    Length     |    Link-Layer Address ...
193        /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
194        /// ```
195        ///
196        /// [RFC 4861 § 4.6.1]: https://tools.ietf.org/html/rfc4861#section-4.6.1
197        pub const SourceLLAddr: NdpOptionType = NdpOptionType(1);
198
199        /// Target Link-Layer Address Option [RFC 4861 § 4.6.1]
200        ///
201        /// ```text
202        /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
203        /// |     Type      |    Length     |    Link-Layer Address ...
204        /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
205        /// ```
206        ///
207        /// [RFC 4861 § 4.6.1]: https://tools.ietf.org/html/rfc4861#section-4.6.1
208        pub const TargetLLAddr: NdpOptionType = NdpOptionType(2);
209
210        /// Prefix Information Option [RFC 4861 § 4.6.2]
211        ///
212        /// ```text
213        /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
214        /// |     Type      |    Length     | Prefix Length |L|A| Reserved1 |
215        /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
216        /// |                         Valid Lifetime                        |
217        /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
218        /// |                       Preferred Lifetime                      |
219        /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
220        /// |                           Reserved2                           |
221        /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
222        /// |                                                               |
223        /// +                                                               +
224        /// |                                                               |
225        /// +                            Prefix                             +
226        /// |                                                               |
227        /// +                                                               +
228        /// |                                                               |
229        /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
230        /// ```
231        ///
232        /// [RFC 4861 § 4.6.2]: https://tools.ietf.org/html/rfc4861#section-4.6.2
233        pub const PrefixInformation: NdpOptionType = NdpOptionType(3);
234
235        /// Redirected Header Option [RFC 4861 § 4.6.3]
236        ///
237        /// ```text
238        /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
239        /// |     Type      |    Length     |            Reserved           |
240        /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
241        /// |                           Reserved                            |
242        /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
243        /// |                                                               |
244        /// ~                       IP header + data                        ~
245        /// |                                                               |
246        /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
247        /// ```
248        ///
249        /// [RFC 4861 § 4.6.3]: https://tools.ietf.org/html/rfc4861#section-4.6.3
250        pub const RedirectedHeader: NdpOptionType = NdpOptionType(4);
251
252        /// MTU Option [RFC 4861 § 4.6.4]
253        ///
254        /// ```text
255        /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
256        /// |     Type      |    Length     |           Reserved            |
257        /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
258        /// |                              MTU                              |
259        /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
260        /// ```
261        ///
262        /// [RFC 4861 § 4.6.4]: https://tools.ietf.org/html/rfc4861#section-4.6.4
263        pub const MTU: NdpOptionType = NdpOptionType(5);
264    }
265
266    /// Neighbor Discovery Option [RFC 4861 § 4.6]
267    ///
268    /// ```text
269    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
270    /// |     Type      |    Length     |              ...              |
271    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
272    /// ~                              ...                              ~
273    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
274    /// ```
275    ///
276    /// [RFC 4861 § 4.6]: https://tools.ietf.org/html/rfc4861#section-4.6
277    #[derive(Debug, Packet)]
278    pub struct NdpOption {
279        #[construct_with(u8)]
280        pub option_type: NdpOptionType,
281        #[construct_with(u8)]
282        pub length: u8,
283        #[length = "(length * 8).saturating_sub(2)"]
284        #[payload]
285        pub data: Vec<u8>,
286    }
287
288    /// Router Solicitation Message [RFC 4861 § 4.1]
289    ///
290    /// ```text
291    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
292    /// |     Type      |     Code      |          Checksum             |
293    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
294    /// |                            Reserved                           |
295    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
296    /// |   Options ...
297    /// ```
298    ///
299    /// [RFC 4861 § 4.1]: https://tools.ietf.org/html/rfc4861#section-4.1
300    #[derive(Debug, Packet)]
301    pub struct RouterSolicit {
302        #[construct_with(u8)]
303        pub icmpv6_type: Icmpv6Type,
304        #[construct_with(u8)]
305        pub icmpv6_code: Icmpv6Code,
306        pub checksum: u16be,
307        pub reserved: u32be,
308        #[payload]
309        #[length = "0"]
310        pub payload: Vec<u8>,
311        pub options: Vec<NdpOption>,
312    }
313
314    /// The enumeration of recognized Router Advert flags.
315    #[allow(non_snake_case)]
316    #[allow(non_upper_case_globals)]
317    pub mod RouterAdvertFlags {
318        /// "Managed Address Configuration" flag. This is set when
319        /// addresses are available via DHCPv6.
320        pub const ManagedAddressConf: u8 = 0b10000000;
321        /// "Other Configuration" flag. This is set when other
322        /// configuration information is available via DHCPv6.
323        pub const OtherConf: u8 = 0b01000000;
324    }
325
326    /// Router Advertisement Message Format [RFC 4861 § 4.2]
327    ///
328    /// ```text
329    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
330    /// |     Type      |     Code      |          Checksum             |
331    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
332    /// | Cur Hop Limit |M|O|  Reserved |       Router Lifetime         |
333    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
334    /// |                         Reachable Time                        |
335    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
336    /// |                          Retrans Timer                        |
337    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
338    /// |   Options ...
339    /// +-+-+-+-+-+-+-+-+-+-+-+-
340    /// ```
341    ///
342    /// [RFC 4861 § 4.2]: https://tools.ietf.org/html/rfc4861#section-4.2
343    #[derive(Debug, Packet)]
344    pub struct RouterAdvert {
345        #[construct_with(u8)]
346        pub icmpv6_type: Icmpv6Type,
347        #[construct_with(u8)]
348        pub icmpv6_code: Icmpv6Code,
349        pub checksum: u16be,
350        pub hop_limit: u8,
351        pub flags: u8,
352        pub lifetime: u16be,
353        pub reachable_time: u32be,
354        pub retrans_time: u32be,
355        #[payload]
356        #[length = "0"]
357        pub payload: Vec<u8>,
358        pub options: Vec<NdpOption>,
359    }
360
361    /// Neighbor Solicitation Message Format [RFC 4861 § 4.3]
362    ///
363    /// ```text
364    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
365    /// |     Type      |     Code      |          Checksum             |
366    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
367    /// |                           Reserved                            |
368    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
369    /// |                                                               |
370    /// +                                                               +
371    /// |                                                               |
372    /// +                       Target Address                          +
373    /// |                                                               |
374    /// +                                                               +
375    /// |                                                               |
376    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
377    /// |   Options ...
378    /// +-+-+-+-+-+-+-+-+-+-+-+-
379    /// ```
380    ///
381    /// [RFC 4861 § 4.3]: https://tools.ietf.org/html/rfc4861#section-4.3
382    #[derive(Debug, Packet)]
383    pub struct NeighborSolicit {
384        #[construct_with(u8)]
385        pub icmpv6_type: Icmpv6Type,
386        #[construct_with(u8)]
387        pub icmpv6_code: Icmpv6Code,
388        pub checksum: u16be,
389        pub reserved: u32be,
390        #[construct_with(u16, u16, u16, u16, u16, u16, u16, u16)]
391        pub target_addr: Ipv6Addr,
392        #[payload]
393        #[length = "0"]
394        pub payload: Vec<u8>,
395        pub options: Vec<NdpOption>,
396    }
397
398    /// Enumeration of recognized Neighbor Advert flags.
399    #[allow(non_snake_case)]
400    #[allow(non_upper_case_globals)]
401    pub mod NeighborAdvertFlags {
402        /// Indicates that the sender is a router.
403        pub const Router: u8 = 0b10000000;
404        /// Indicates that the advertisement was sent due to the receipt of a
405        /// Neighbor Solicitation message.
406        pub const Solicited: u8 = 0b01000000;
407        /// Indicates that the advertisement should override an existing cache
408        /// entry.
409        pub const Override: u8 = 0b00100000;
410    }
411
412    /// Neighbor Advertisement Message Format [RFC 4861 § 4.4]
413    ///
414    /// ```text
415    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
416    /// |     Type      |     Code      |          Checksum             |
417    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
418    /// |R|S|O|                     Reserved                            |
419    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
420    /// |                                                               |
421    /// +                                                               +
422    /// |                                                               |
423    /// +                       Target Address                          +
424    /// |                                                               |
425    /// +                                                               +
426    /// |                                                               |
427    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
428    /// |   Options ...
429    /// +-+-+-+-+-+-+-+-+-+-+-+-
430    /// ```
431    ///
432    /// [RFC 4861 § 4.4]: https://tools.ietf.org/html/rfc4861#section-4.4
433    #[derive(Debug, Packet)]
434    pub struct NeighborAdvert {
435        #[construct_with(u8)]
436        pub icmpv6_type: Icmpv6Type,
437        #[construct_with(u8)]
438        pub icmpv6_code: Icmpv6Code,
439        pub checksum: u16be,
440        pub flags: u8,
441        pub reserved: u24be,
442        #[construct_with(u16, u16, u16, u16, u16, u16, u16, u16)]
443        pub target_addr: Ipv6Addr,
444        #[payload]
445        #[length = "0"]
446        pub payload: Vec<u8>,
447        pub options: Vec<NdpOption>,
448    }
449
450    /// Redirect Message Format [RFC 4861 § 4.5]
451    ///
452    /// ```text
453    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
454    /// |     Type      |     Code      |          Checksum             |
455    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
456    /// |                           Reserved                            |
457    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
458    /// |                                                               |
459    /// +                                                               +
460    /// |                                                               |
461    /// +                       Target Address                          +
462    /// |                                                               |
463    /// +                                                               +
464    /// |                                                               |
465    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
466    /// |                                                               |
467    /// +                                                               +
468    /// |                                                               |
469    /// +                     Destination Address                       +
470    /// |                                                               |
471    /// +                                                               +
472    /// |                                                               |
473    /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
474    /// |   Options ...
475    /// +-+-+-+-+-+-+-+-+-+-+-+-
476    /// ```
477    ///
478    /// [RFC 4861 § 4.5]: https://tools.ietf.org/html/rfc4861#section-4.5
479    #[derive(Debug, Packet)]
480    pub struct Redirect {
481        #[construct_with(u8)]
482        pub icmpv6_type: Icmpv6Type,
483        #[construct_with(u8)]
484        pub icmpv6_code: Icmpv6Code,
485        pub checksum: u16be,
486        pub reserved: u32be,
487        #[construct_with(u16, u16, u16, u16, u16, u16, u16, u16)]
488        pub target_addr: Ipv6Addr,
489        #[construct_with(u16, u16, u16, u16, u16, u16, u16, u16)]
490        pub dest_addr: Ipv6Addr,
491        #[payload]
492        #[length = "0"]
493        pub payload: Vec<u8>,
494        pub options: Vec<NdpOption>,
495    }
496
497    #[cfg(test)]
498    mod ndp_tests {
499        use super::*;
500        use crate::icmpv6::{Icmpv6Code, Icmpv6Types};
501
502        #[test]
503        fn basic_option_parsing() {
504            let mut data = vec![
505                0x02, 0x01, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01,
506                // Extra bytes to confuse the parsing
507                0x00, 0x00, 0x00,
508            ];
509            let pkg = MutableNdpOptionPacket::new(&mut data[..]).unwrap();
510            assert_eq!(pkg.get_option_type(), NdpOptionTypes::TargetLLAddr);
511            assert_eq!(pkg.get_length(), 0x01);
512            assert_eq!(pkg.payload().len(), 6);
513            assert_eq!(pkg.payload(), &[0x06, 0x05, 0x04, 0x03, 0x02, 0x01]);
514        }
515
516        #[test]
517        fn basic_rs_parse() {
518            let mut data = vec![
519                0x85, // Type
520                0x00, // Code
521                0x00, 0x00, // Checksum
522                0x00, 0x00, 0x00, 0x00, // Reserved
523                0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00,
524                0x00, 0x00,
525            ];
526
527            let pkg = MutableRouterSolicitPacket::new(&mut data[..]).unwrap();
528            assert_eq!(pkg.get_icmpv6_type(), Icmpv6Types::RouterSolicit);
529            assert_eq!(pkg.get_icmpv6_code(), Icmpv6Code(0));
530            assert_eq!(pkg.get_checksum(), 0);
531            assert_eq!(pkg.get_reserved(), 0);
532            assert_eq!(pkg.get_options().len(), 2);
533
534            let option = &pkg.get_options()[0];
535            assert_eq!(option.option_type, NdpOptionTypes::TargetLLAddr);
536            assert_eq!(option.length, 0x01);
537            assert_eq!(option.data, &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
538            assert_eq!(option.data.len(), 6);
539
540            let option = &pkg.get_options()[1];
541            assert_eq!(option.option_type, NdpOptionTypes::SourceLLAddr);
542            assert_eq!(option.length, 1);
543            assert_eq!(option.data, &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
544        }
545
546        #[test]
547        fn basic_rs_create() {
548            let ref_packet = vec![
549                0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00,
550                0x00, 0x00,
551            ];
552            let mut packet = [0u8; 16];
553            let options = vec![NdpOption {
554                option_type: NdpOptionTypes::SourceLLAddr,
555                length: 1,
556                data: vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
557            }];
558            {
559                let mut rs_packet = MutableRouterSolicitPacket::new(&mut packet[..]).unwrap();
560                rs_packet.set_icmpv6_type(Icmpv6Types::RouterSolicit);
561                rs_packet.set_icmpv6_code(Icmpv6Code(0));
562                rs_packet.set_options(&options[..]);
563            }
564            assert_eq!(&ref_packet[..], &packet[..]);
565        }
566
567        #[test]
568        fn basic_ra_parse() {
569            let mut data = vec![
570                0x86, // Type
571                0x00, // Code
572                0x00, 0x00, // Checksum
573                0xff, // Hop Limit
574                0x80, // Flags
575                0x09, 0x00, // Lifetime
576                0x12, 0x34, 0x56, 0x78, // Reachable
577                0x87, 0x65, 0x43, 0x21, // Retrans
578                0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Source Link-Layer
579                0x05, 0x01, 0x00, 0x00, 0x57, 0x68, 0x61, 0x74, // MTU
580            ];
581            let pkg = MutableRouterAdvertPacket::new(&mut data[..]).unwrap();
582            assert_eq!(pkg.get_icmpv6_type(), Icmpv6Types::RouterAdvert);
583            assert_eq!(pkg.get_icmpv6_code(), Icmpv6Code(0));
584            assert_eq!(pkg.get_checksum(), 0x00);
585            assert_eq!(pkg.get_hop_limit(), 0xff);
586            assert_eq!(pkg.get_flags(), RouterAdvertFlags::ManagedAddressConf);
587            assert_eq!(pkg.get_lifetime(), 0x900);
588            assert_eq!(pkg.get_reachable_time(), 0x12345678);
589            assert_eq!(pkg.get_retrans_time(), 0x87654321);
590            assert_eq!(pkg.get_options().len(), 2);
591
592            let option = &pkg.get_options()[0];
593            assert_eq!(option.option_type, NdpOptionTypes::SourceLLAddr);
594            assert_eq!(option.length, 1);
595            assert_eq!(option.data, &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
596
597            let option = &pkg.get_options()[1];
598            assert_eq!(option.option_type, NdpOptionTypes::MTU);
599            assert_eq!(option.length, 1);
600            assert_eq!(option.data, &[0x00, 0x00, 0x57, 0x68, 0x61, 0x74]);
601        }
602
603        #[test]
604        fn basic_ra_create() {
605            let ref_packet = vec![
606                0x86, 0x00, 0x00, 0x00, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
607                0x00, 0x00, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
608            ];
609            let mut packet = [0u8; 24];
610            let options = vec![NdpOption {
611                option_type: NdpOptionTypes::MTU,
612                length: 1,
613                data: vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
614            }];
615            {
616                let mut ra_packet = MutableRouterAdvertPacket::new(&mut packet[..]).unwrap();
617                ra_packet.set_icmpv6_type(Icmpv6Types::RouterAdvert);
618                ra_packet.set_icmpv6_code(Icmpv6Code(0));
619                ra_packet.set_hop_limit(0xff);
620                ra_packet.set_flags(RouterAdvertFlags::ManagedAddressConf);
621                ra_packet.set_options(&options[..]);
622            }
623            assert_eq!(&ref_packet[..], &packet[..]);
624        }
625
626        #[test]
627        fn basic_ns_parse() {
628            let mut data = vec![
629                0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00,
630                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
631            ];
632            let pkg = MutableNeighborSolicitPacket::new(&mut data[..]).unwrap();
633            assert_eq!(pkg.get_icmpv6_type(), Icmpv6Types::NeighborSolicit);
634            assert_eq!(pkg.get_icmpv6_code(), Icmpv6Code(0));
635            assert_eq!(pkg.get_checksum(), 0x00);
636            assert_eq!(pkg.get_reserved(), 0x00);
637            assert_eq!(
638                pkg.get_target_addr(),
639                Ipv6Addr::new(0xff02, 0, 0, 0, 0, 0, 0, 1)
640            );
641        }
642
643        #[test]
644        fn basic_ns_create() {
645            let ref_packet = vec![
646                0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00,
647                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
648            ];
649            let mut packet = [0u8; 24];
650            {
651                let mut ns_packet = MutableNeighborSolicitPacket::new(&mut packet[..]).unwrap();
652                ns_packet.set_icmpv6_type(Icmpv6Types::NeighborSolicit);
653                ns_packet.set_icmpv6_code(Icmpv6Code(0));
654                ns_packet.set_target_addr(Ipv6Addr::new(0xff02, 0, 0, 0, 0, 0, 0, 1));
655            }
656            assert_eq!(&ref_packet[..], &packet[..]);
657        }
658
659        #[test]
660        fn basic_na_parse() {
661            let mut data = vec![
662                0x88, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00,
663                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
664            ];
665            let pkg = MutableNeighborAdvertPacket::new(&mut data[..]).unwrap();
666            assert_eq!(pkg.get_icmpv6_type(), Icmpv6Types::NeighborAdvert);
667            assert_eq!(pkg.get_icmpv6_code(), Icmpv6Code(0));
668            assert_eq!(pkg.get_checksum(), 0x00);
669            assert_eq!(pkg.get_reserved(), 0x00);
670            assert_eq!(pkg.get_flags(), 0x80);
671            assert_eq!(
672                pkg.get_target_addr(),
673                Ipv6Addr::new(0xff02, 0, 0, 0, 0, 0, 0, 1)
674            );
675        }
676
677        #[test]
678        fn basic_na_create() {
679            let ref_packet = vec![
680                0x88, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00,
681                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
682            ];
683            let mut packet = [0u8; 24];
684            {
685                let mut na_packet = MutableNeighborAdvertPacket::new(&mut packet[..]).unwrap();
686                na_packet.set_icmpv6_type(Icmpv6Types::NeighborAdvert);
687                na_packet.set_icmpv6_code(Icmpv6Code(0));
688                na_packet.set_target_addr(Ipv6Addr::new(0xff02, 0, 0, 0, 0, 0, 0, 1));
689                na_packet.set_flags(NeighborAdvertFlags::Router);
690            }
691            assert_eq!(&ref_packet[..], &packet[..]);
692        }
693
694        #[test]
695        fn basic_redirect_parse() {
696            let mut data = vec![
697                0x89, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00,
698                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
699                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
700            ];
701            let pkg = MutableRedirectPacket::new(&mut data[..]).unwrap();
702            assert_eq!(pkg.get_icmpv6_type(), Icmpv6Types::Redirect);
703            assert_eq!(pkg.get_icmpv6_code(), Icmpv6Code(0));
704            assert_eq!(pkg.get_checksum(), 0x00);
705            assert_eq!(pkg.get_reserved(), 0x00);
706            assert_eq!(
707                pkg.get_target_addr(),
708                Ipv6Addr::new(0xff02, 0, 0, 0, 0, 0, 0, 1)
709            );
710            assert_eq!(pkg.get_dest_addr(), Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0));
711        }
712
713        #[test]
714        fn basic_redirect_create() {
715            let ref_packet = vec![
716                0x89, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00,
717                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
718                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
719            ];
720            let mut packet = [0u8; 40];
721            {
722                let mut rdr_packet = MutableRedirectPacket::new(&mut packet[..]).unwrap();
723                rdr_packet.set_icmpv6_type(Icmpv6Types::Redirect);
724                rdr_packet.set_icmpv6_code(Icmpv6Code(0));
725                rdr_packet.set_target_addr(Ipv6Addr::new(0xff02, 0, 0, 0, 0, 0, 0, 1));
726                rdr_packet.set_dest_addr(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0));
727            }
728            assert_eq!(&ref_packet[..], &packet[..]);
729        }
730    }
731}