Skip to main content

etherparse/transport/
icmpv6_type.rs

1use crate::{
2    err::{ValueTooBigError, ValueType},
3    icmpv6::RouterAdvertisementHeader,
4    *,
5};
6
7/// Different kinds of ICMPv6 messages.
8///
9/// The data stored in this enum corresponds to the statically sized data
10/// at the start of an ICMPv6 packet without the checksum. If you also need
11/// the checksum you can package and [`Icmpv6Type`] value in an [`Icmpv6Header`]
12/// struct.
13///
14/// # Decoding Example (complete packet):
15///
16/// ```
17/// # use etherparse::{PacketBuilder};
18/// # let mut builder = PacketBuilder::
19/// #   ethernet2([0;6], [0;6])
20/// #   .ipv6([0;16], [0;16], 20)
21/// #   .icmpv6_echo_request(1, 2);
22/// # let payload = [1,2,3,4];
23/// # let mut packet = Vec::<u8>::with_capacity(builder.size(payload.len()));
24/// # builder.write(&mut packet, &payload);
25/// use etherparse::PacketHeaders;
26///
27/// let headers = PacketHeaders::from_ethernet_slice(&packet).unwrap();
28///
29/// use etherparse::TransportHeader::*;
30/// match headers.transport {
31///     Some(Icmpv6(icmp)) => {
32///         use etherparse::Icmpv6Type::*;
33///         match icmp.icmp_type {
34///             // Unknown is used when further decoding is currently not supported for the icmp type & code.
35///             // You can still further decode the packet on your own by using the raw data in this enum
36///             // together with `headers.payload` (contains the packet data after the 8th byte)
37///             Unknown{ type_u8, code_u8, bytes5to8 } => println!("Unknown{{ type_u8: {}, code_u8: {}, bytes5to8: {:?} }}", type_u8, code_u8, bytes5to8),
38///             DestinationUnreachable(header) => println!("{:?}", header),
39///             PacketTooBig { mtu } => println!("TimeExceeded{{ mtu: {} }}", mtu),
40///             TimeExceeded(code) => println!("{:?}", code),
41///             ParameterProblem(header) => println!("{:?}", header),
42///             EchoRequest(header) => println!("{:?}", header),
43///             EchoReply(header) => println!("{:?}", header),
44///             RouterSolicitation => println!("RouterSolicitation"),
45///             RouterAdvertisement(header) => println!("{:?}", header),
46///             NeighborSolicitation => println!("NeighborSolicitation"),
47///             NeighborAdvertisement(header) => println!("{:?}", header),
48///             Redirect => println!("Redirect"),
49///         }
50///     },
51///     _ => {},
52/// }
53/// ```
54///
55/// # Encoding Example (only ICMPv6 part)
56///
57/// To get the on wire bytes of an Icmpv6Type it needs to get packaged
58/// into a [`Icmpv6Header`] so the checksum gets calculated.
59///
60/// ```
61/// # use etherparse::Ipv6Header;
62/// # let ip_header: Ipv6Header = Default::default();
63/// # let invoking_packet : [u8;0] = [];
64///
65/// use etherparse::{Icmpv6Type, icmpv6::DestUnreachableCode};
66/// let t = Icmpv6Type::DestinationUnreachable(
67///     DestUnreachableCode::Address
68/// );
69///
70/// // to calculate the checksum the ip header and the payload
71/// // (in case of dest unreachable the invoking packet) are needed
72/// let header = t.to_header(ip_header.source, ip_header.destination, &invoking_packet).unwrap();
73///
74/// // an ICMPv6 packet is composed of the header and payload
75/// let mut packet = Vec::with_capacity(header.header_len() + invoking_packet.len());
76/// packet.extend_from_slice(&header.to_bytes());
77/// packet.extend_from_slice(&invoking_packet);
78/// #
79/// # {
80/// #   let checksum_be = header.checksum.to_be_bytes();
81/// #   assert_eq!(
82/// #       &packet,
83/// #       &[
84/// #           header.icmp_type.type_u8(),
85/// #           header.icmp_type.code_u8(),
86/// #           checksum_be[0],
87/// #           checksum_be[1],
88/// #           0,0,0,0
89/// #       ]
90/// #   );
91/// # }
92/// ```
93#[derive(Clone, Copy, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
94pub enum Icmpv6Type {
95    /// In case of an unknown icmp type is received the header elements of
96    /// the first 8 bytes/octets are stored raw in this enum value.
97    ///
98    /// # What is part of the header for `Icmpv6Type::Unknown`?
99    ///
100    /// For unknown ICMPv6 type & code combination the first 8 bytes are stored
101    /// in the [`Icmpv6Header`] and the rest is stored in the payload
102    /// ([`Icmpv6Slice::payload`] or [`PacketHeaders::payload`]).
103    ///
104    ///
105    /// ```text
106    /// 0               1               2               3               4
107    /// +---------------------------------------------------------------+  -
108    /// |     type_u8   |    code_u8    |  checksum (in Icmpv6Header)   |  |
109    /// +---------------------------------------------------------------+  | part of header & type
110    /// |                          bytes5to8                            |  ↓
111    /// +---------------------------------------------------------------+  -
112    /// |                                                               |  |
113    /// ...                           ...                             ...  | part of payload
114    /// |                                                               |  ↓
115    /// +---------------------------------------------------------------+  -
116    /// ```
117    Unknown {
118        /// ICMPv6 type (present in the first byte of the ICMPv6 packet).
119        type_u8: u8,
120        /// ICMPv6 code (present in the 2nd byte of the ICMPv6 packet).
121        code_u8: u8,
122        /// Bytes located at th 5th, 6th, 7th and 8th position of the ICMP packet.
123        bytes5to8: [u8; 4],
124    },
125
126    /// Message sent to inform the client that the destination is unreachable for
127    /// some reason.
128    ///
129    /// # What is part of the header for `Icmpv6Type::DestinationUnreachable`?
130    ///
131    /// For the `Icmpv6Type::DestinationUnreachable` type the first 8 bytes/octets of the ICMPv6
132    /// packet are part of the header. The `unused` part is not stored and droped.
133    /// The offending packet is stored in the payload part of the packet
134    /// ([`Icmpv6Slice::payload`] & [`PacketHeaders::payload`]) and is not part of
135    /// the [`Icmpv6Header`].
136    ///
137    /// ```text
138    /// 0               1               2               3               4
139    /// +---------------------------------------------------------------+  -
140    /// |       1       | [value as u8] |  checksum (in Icmpv6Header)   |  |
141    /// +---------------------------------------------------------------+  | part of header & type
142    /// |                           <unused>                            |  ↓
143    /// +---------------------------------------------------------------+  -
144    /// |                                                               |  |
145    /// |     <As much of invoking packet as possible without           |  | part of payload
146    /// ...   the ICMPv6 packet exceeding the minimum IPv6 MTU>       ...  |
147    /// |                                                               |  ↓
148    /// +---------------------------------------------------------------+  -
149    /// ```
150    ///
151    /// # RFC 4443 Description
152    ///
153    /// A Destination Unreachable message SHOULD be generated by a router, or
154    /// by the IPv6 layer in the originating node, in response to a packet
155    /// that cannot be delivered to its destination address for reasons other
156    /// than congestion.  (An ICMPv6 message MUST NOT be generated if a
157    /// packet is dropped due to congestion.)
158    DestinationUnreachable(icmpv6::DestUnreachableCode),
159
160    /// Sent if a packet to too big to be forwarded.
161    ///
162    /// # What is part of the header for `Icmpv6Type::PacketTooBig`?
163    ///
164    /// For the `Icmpv6Type::PacketTooBig` type the first 8 bytes/octets of the ICMPv6
165    /// packet are part of the header. The offending packet is stored in the payload part of the packet
166    /// ([`Icmpv6Slice::payload`] & [`PacketHeaders::payload`]) and is not part of
167    /// the [`Icmpv6Header`].
168    ///
169    /// ```text
170    /// 0               1               2               3               4
171    /// +---------------------------------------------------------------+  -
172    /// |       2       |       0       |  checksum (in Icmpv6Header)   |  |
173    /// +---------------------------------------------------------------+  | part of header & type
174    /// |                              mtu                              |  ↓
175    /// +---------------------------------------------------------------+  -
176    /// |                                                               |  |
177    /// |     <As much of invoking packet as possible without           |  | part of payload
178    /// ...   the ICMPv6 packet exceeding the minimum IPv6 MTU>       ...  |
179    /// |                                                               |  ↓
180    /// +---------------------------------------------------------------+  -
181    /// ```
182    ///
183    /// # RFC 4443 Description
184    ///
185    /// A Packet Too Big MUST be sent by a router in response to a packet
186    /// that it cannot forward because the packet is larger than the MTU of
187    /// the outgoing link.  The information in this message is used as part
188    /// of the Path MTU Discovery process.
189    PacketTooBig {
190        /// The Maximum Transmission Unit of the next-hop link.
191        mtu: u32,
192    },
193
194    /// Generated when a datagram had to be discarded due to the hop limit field
195    /// reaching zero.
196    ///
197    /// # What is part of the header for `Icmpv6Type::TimeExceeded`?
198    ///
199    /// For the `Icmpv6Type::TimeExceeded` type the first 8 bytes/octets of the ICMPv6
200    /// packet are part of the header. The `unused` part is not stored and droped.
201    /// The offending packet is stored in the payload part of the packet
202    /// ([`Icmpv6Slice::payload`] & [`PacketHeaders::payload`]) and is not part of
203    /// the [`Icmpv6Header`].
204    ///
205    /// ```text
206    /// 0               1               2               3               4
207    /// +---------------------------------------------------------------+  -
208    /// |       3       | [value as u8] |  checksum (in Icmpv6Header)   |  |
209    /// +---------------------------------------------------------------+  | part of header & type
210    /// |                           <unused>                            |  ↓
211    /// +---------------------------------------------------------------+  -
212    /// |                                                               |  |
213    /// |     <As much of invoking packet as possible without           |  | part of payload
214    /// ...   the ICMPv6 packet exceeding the minimum IPv6 MTU>       ...  |
215    /// |                                                               |  ↓
216    /// +---------------------------------------------------------------+  -
217    /// ```
218    ///
219    /// # RFC 4443 Description
220    ///
221    /// If a router receives a packet with a Hop Limit of zero, or if a
222    /// router decrements a packet's Hop Limit to zero, it MUST discard the
223    /// packet and originate an ICMPv6 Time Exceeded message with Code 0 to
224    /// the source of the packet.  This indicates either a routing loop or
225    /// too small an initial Hop Limit value.
226    ///
227    /// An ICMPv6 Time Exceeded message with Code 1 is used to report
228    /// fragment reassembly timeout, as specified in [IPv6, Section 4.5].
229    TimeExceeded(icmpv6::TimeExceededCode),
230
231    /// Sent if there is a problem with a parameter in a received packet.
232    ///
233    /// # What is part of the header for `Icmpv6Type::ParameterProblem`?
234    ///
235    /// For the `Icmpv6Type::ParameterProblem` type the first 8 bytes/octets of the ICMPv6
236    /// packet are part of the header. The `unused` part is not stored and droped.
237    /// The offending packet is stored in the payload part of the packet
238    /// ([`Icmpv6Slice::payload`] & [`PacketHeaders::payload`]) and is not part of
239    /// the [`Icmpv6Header`].
240    ///
241    /// ```text
242    /// 0               1               2               3               4
243    /// +---------------------------------------------------------------+  -
244    /// |       4       | [value].code  |  checksum (in Icmpv6Header)   |  |
245    /// +---------------------------------------------------------------+  | part of header & type
246    /// |                        [value].pointer                        |  ↓
247    /// +---------------------------------------------------------------+  -
248    /// |                                                               |  |
249    /// |     <As much of invoking packet as possible without           |  | part of payload
250    /// ...   the ICMPv6 packet exceeding the minimum IPv6 MTU>       ...  |
251    /// |                                                               |  ↓
252    /// +---------------------------------------------------------------+  -
253    /// ```
254    ///
255    /// # RFC 4443 Description
256    ///
257    /// If an IPv6 node processing a packet finds a problem with a field in
258    /// the IPv6 header or extension headers such that it cannot complete
259    /// processing the packet, it MUST discard the packet and SHOULD
260    /// originate an ICMPv6 Parameter Problem message to the packet's source,
261    /// indicating the type and location of the problem.
262    ParameterProblem(icmpv6::ParameterProblemHeader),
263
264    /// Requesting an `EchoReply` from the receiver.
265    ///
266    /// # What is part of the header for `Icmpv6Type::EchoRequest`?
267    ///
268    /// For the [`Icmpv6Type::EchoRequest`] type the first 8 bytes/octets of the
269    /// ICMPv6 packet are part of the header. This includes the `id` and `seq`
270    /// fields. The data part of the ICMP echo request packet is part of the payload
271    /// ([`Icmpv6Slice::payload`] & [`PacketHeaders::payload`]) and not part of the
272    /// [`Icmpv6Header`].
273    ///
274    /// ```text
275    /// 0               1               2               3               4
276    /// +---------------------------------------------------------------+  -
277    /// |      128      |       0       |  checksum (in Icmpv6Header)   |  |
278    /// +---------------------------------------------------------------+  | part of header & type
279    /// |          [value].id           |         [value].seq           |  ↓
280    /// +---------------------------------------------------------------+  -
281    /// |                                                               |  |
282    /// ...                          <data>                           ...  | part of payload
283    /// |                                                               |  ↓
284    /// +---------------------------------------------------------------+  -
285    /// ```
286    ///
287    /// # RFC 4443 Description
288    ///
289    /// Every node MUST implement an ICMPv6 Echo responder function that
290    /// receives Echo Requests and originates corresponding Echo Replies.  A
291    /// node SHOULD also implement an application-layer interface for
292    /// originating Echo Requests and receiving Echo Replies, for diagnostic
293    /// purposes.
294    EchoRequest(IcmpEchoHeader),
295    /// Response to an `EchoRequest` message.
296    ///
297    /// # What is part of the header for `Icmpv6Type::EchoReply`?
298    ///
299    /// For the [`Icmpv6Type::EchoReply`] type the first 8 bytes/octets of the
300    /// ICMPv6 packet are part of the header. This includes the `id` and `seq`
301    /// fields. The data part of the ICMP echo request packet is part of the payload
302    /// ([`Icmpv6Slice::payload`] & [`PacketHeaders::payload`]) and not part of the
303    /// [`Icmpv6Header`].
304    ///
305    /// ```text
306    /// 0               1               2               3               4
307    /// +---------------------------------------------------------------+  -
308    /// |      129      |       0       |  checksum (in Icmpv6Header)   |  |
309    /// +---------------------------------------------------------------+  | part of header & type
310    /// |          [value].id           |         [value].seq           |  ↓
311    /// +---------------------------------------------------------------+  -
312    /// |                                                               |  |
313    /// ...                          <data>                           ...  | part of payload
314    /// |                                                               |  ↓
315    /// +---------------------------------------------------------------+  -
316    /// ```
317    ///
318    /// # RFC 4443 Description
319    ///
320    /// Every node MUST implement an ICMPv6 Echo responder function that
321    /// receives Echo Requests and originates corresponding Echo Replies. A
322    /// node SHOULD also implement an application-layer interface for
323    /// originating Echo Requests and receiving Echo Replies, for diagnostic
324    /// purposes.
325    ///
326    /// The source address of an Echo Reply sent in response to a unicast
327    /// Echo Request message MUST be the same as the destination address of
328    /// that Echo Request message.
329    ///
330    /// An Echo Reply SHOULD be sent in response to an Echo Request message
331    /// sent to an IPv6 multicast or anycast address.  In this case, the
332    /// source address of the reply MUST be a unicast address belonging to
333    /// the interface on which the Echo Request message was received.
334    ///
335    /// The data received in the ICMPv6 Echo Request message MUST be returned
336    /// entirely and unmodified in the ICMPv6 Echo Reply message.
337    EchoReply(IcmpEchoHeader),
338
339    /// Router Solicitation message header (part of "Neighbor Discovery Protocol"
340    /// [RFC 4861](https://datatracker.ietf.org/doc/html/rfc4861)).
341    ///
342    /// # What is part of the header for `Icmpv6Type::RouterSolicitation`?
343    ///
344    /// For the [`Icmpv6Type::RouterSolicitation`] type the first 8 bytes/octets
345    /// of the ICMPv6 packet are part of the header.
346    ///
347    /// The options part of the ICMP Router Solicitation packet is part of the payload
348    /// ([`Icmpv6Slice::payload`] & [`PacketHeaders::payload`]) and not part of the
349    /// [`Icmpv6Header`].
350    /// ```text
351    /// 0               1               2               3               4
352    /// +---------------------------------------------------------------+  -
353    /// |      133      |       0       |  checksum (in Icmpv6Header)   |  |
354    /// +---------------------------------------------------------------+  | part of header & type
355    /// |                           <unused>                            |  ↓
356    /// +---------------------------------------------------------------+  -
357    /// |   Options ...                                                 |  | part of payload
358    /// |                                                               |  ↓
359    /// +-----------------                                                 -
360    /// ```
361    ///
362    /// # RFC 4861 Description
363    ///
364    /// Hosts send Router Solicitations in order to prompt routers to
365    /// generate Router Advertisements quickly.
366    RouterSolicitation,
367
368    /// Router Advertisement message header (part of "Neighbor Discovery Protocol"
369    /// [RFC 4861](https://datatracker.ietf.org/doc/html/rfc4861)).
370    ///
371    /// # What is part of the header for `Icmpv6Type::RouterAdvertisement`?
372    ///
373    /// For the [`Icmpv6Type::RouterAdvertisement`] type the first 8 bytes/octets
374    /// of the ICMPv6 packet are part of the header.
375    ///
376    /// The options part of the ICMP Router Advertisement packet is part of the payload
377    /// ([`Icmpv6Slice::payload`] & [`PacketHeaders::payload`]) and not part of the
378    /// [`Icmpv6Header`].
379    /// ```text
380    /// 0               1               2               3               4
381    /// +---------------------------------------------------------------+  -
382    /// |      134      |       0       |  checksum (in Icmpv6Header)   |  |
383    /// +---------------------------------------------------------------+  | part of header & type
384    /// | Cur Hop Limit |M|O|  Reserved |     Router Lifetime           |  ↓
385    /// +---------------------------------------------------------------+  -
386    /// |                        Reachable Time                         |  |
387    /// +---------------------------------------------------------------+  |
388    /// |                         Retrans Timer                         |  | part of payload
389    /// +---------------------------------------------------------------+  |
390    /// |   Options ...                                                 |  ↓
391    /// +-----------------                                                 -
392    /// ```
393    ///
394    /// # RFC 4861 Description
395    ///
396    /// Routers send out Router Advertisement messages periodically, or in
397    /// response to Router Solicitations.
398    RouterAdvertisement(RouterAdvertisementHeader),
399
400    /// Requesting the link-layer address of a target node (part of "Neighbor Discovery Protocol"
401    /// [RFC 4861](https://datatracker.ietf.org/doc/html/rfc4861)).
402    ///
403    /// # What is part of the header for `Icmpv6Type::NeighborSolicitation`?
404    ///
405    /// For the [`Icmpv6Type::NeighborSolicitation`] type the first 8 bytes/octets
406    /// of the ICMPv6 packet are part of the header.
407    ///
408    /// The target address & options of the ICMP Neighbor Solicitation packet is part
409    /// of the payload ([`Icmpv6Slice::payload`] & [`PacketHeaders::payload`]) and not
410    /// part of the [`Icmpv6Header`].
411    /// ```text
412    /// 0               1               2               3               4
413    /// +---------------------------------------------------------------+  -
414    /// |      135      |       0       |  checksum (in Icmpv6Header)   |  |
415    /// +---------------------------------------------------------------+  | part of header & type
416    /// |                           <unused>                            |  ↓
417    /// +---------------------------------------------------------------+  -
418    /// |                                                               |  |
419    /// |                       Target Address                          |  |
420    /// |                                                               |  | part of payload
421    /// +---------------------------------------------------------------+  |
422    /// |   Options ...                                                    ↓
423    /// +-----------------                                                 -
424    /// ```
425    ///
426    /// # RFC 4861 Description
427    ///
428    /// Nodes send Neighbor Solicitations to request the link-layer address
429    /// of a target node while also providing their own link-layer address to
430    /// the target.  Neighbor Solicitations are multicast when the node needs
431    /// to resolve an address and unicast when the node seeks to verify the
432    /// reachability of a neighbor.
433    NeighborSolicitation,
434
435    /// Header of "Neighbor Advertisement" message (part of "Neighbor Discovery Protocol"
436    /// [RFC 4861](https://datatracker.ietf.org/doc/html/rfc4861)).
437    ///
438    /// # What is part of the header for `Icmpv6Type::NeighborAdvertisement`?
439    ///
440    /// For the [`Icmpv6Type::NeighborAdvertisement`] type the first 8 bytes/octets
441    /// of the ICMPv6 packet are part of the header. This includes 3 bits for
442    /// - router
443    /// - solicited
444    /// - override
445    ///
446    /// The target address & options of the ICMP Neighbor Advertisement packet is part
447    /// of the payload ([`Icmpv6Slice::payload`] & [`PacketHeaders::payload`]) and not
448    /// part of the [`Icmpv6Header`].
449    ///
450    /// ```text
451    /// 0               1               2               3               4
452    /// +---------------------------------------------------------------+  -
453    /// |      136      |       0       |  checksum (in Icmpv6Header)   |  |
454    /// +---------------------------------------------------------------+  | part of header & type
455    /// |R|S|O|                    <unused>                             |  ↓
456    /// +---------------------------------------------------------------+  -
457    /// |                                                               |  |
458    /// |                       Target Address                          |  |
459    /// |                                                               |  | part of payload
460    /// +---------------------------------------------------------------+  |
461    /// |   Options ...                                                    ↓
462    /// +-----------------                                                 -
463    /// ```
464    ///
465    /// # RFC 4861 Description
466    ///
467    /// A node sends Neighbor Advertisements in response to Neighbor
468    /// Solicitations and sends unsolicited Neighbor Advertisements in order
469    /// to (unreliably) propagate new information quickly.
470    ///
471    /// R          Router flag. When set, the R-bit indicates that
472    ///            the sender is a router. The R-bit is used by
473    ///            Neighbor Unreachability Detection to detect a
474    ///            router that changes to a host.
475    ///
476    /// S          Solicited flag. When set, the S-bit indicates that
477    ///            the advertisement was sent in response to a
478    ///            Neighbor Solicitation from the Destination address.
479    ///            The S-bit is used as a reachability confirmation
480    ///            for Neighbor Unreachability Detection.  It MUST NOT
481    ///            be set in multicast advertisements or in
482    ///            unsolicited unicast advertisements.
483    ///
484    /// O          Override flag. When set, the O-bit indicates that
485    ///            the advertisement should override an existing cache
486    ///            entry and update the cached link-layer address.
487    ///            When it is not set the advertisement will not
488    ///            update a cached link-layer address though it will
489    ///            update an existing Neighbor Cache entry for which
490    ///            no link-layer address is known.  It SHOULD NOT be
491    ///            set in solicited advertisements for anycast
492    ///            addresses and in solicited proxy advertisements.
493    ///            It SHOULD be set in other solicited advertisements
494    ///            and in unsolicited advertisements.
495    NeighborAdvertisement(icmpv6::NeighborAdvertisementHeader),
496
497    /// Header of "Redirect" message (part of "Neighbor Discovery Protocol"
498    /// [RFC 4861](https://datatracker.ietf.org/doc/html/rfc4861)).
499    ///
500    /// # What is part of the header for `Icmpv6Type::Redirect`?
501    ///
502    /// For the [`Icmpv6Type::Redirect`] type the first 8 bytes/octets
503    /// of the ICMPv6 packet are part of the header.
504    ///
505    /// The "target address", "destination address" & options part of the ICMP Redirect
506    /// packet is part of the payload ([`Icmpv6Slice::payload`] & [`PacketHeaders::payload`])
507    /// and not part of the [`Icmpv6Header`].
508    /// ```text
509    /// 0               1               2               3               4
510    /// +---------------------------------------------------------------+  -
511    /// |      137      |       0       |  checksum (in Icmpv6Header)   |  |
512    /// +---------------------------------------------------------------+  | part of header & type
513    /// |                           <unused>                            |  ↓
514    /// +---------------------------------------------------------------+  -
515    /// |                                                               |  |
516    /// |                       Target Address                          |  |
517    /// |                                                               |  |
518    /// +---------------------------------------------------------------+  |
519    /// |                                                               |  | part of payload
520    /// |                    Destination Address                        |  |
521    /// |                                                               |  |
522    /// +---------------------------------------------------------------+  |
523    /// |   Options ...                                                    ↓
524    /// +-----------------                                                 -                                             -
525    /// ```
526    ///
527    /// # RFC 4861 Description
528    ///
529    /// Routers send Redirect packets to inform a host of a better first-hop
530    /// node on the path to a destination.  Hosts can be redirected to a
531    /// better first-hop router but can also be informed by a redirect that
532    /// the destination is in fact a neighbor.  The latter is accomplished by
533    /// setting the ICMP Target Address equal to the ICMP Destination
534    /// Address.
535    Redirect,
536}
537
538impl Icmpv6Type {
539    /// Returns the type value (first byte of the ICMPv6 header) of this type.
540    #[inline]
541    pub fn type_u8(&self) -> u8 {
542        use crate::{icmpv6::*, Icmpv6Type::*};
543        match self {
544            Unknown {
545                type_u8,
546                code_u8: _,
547                bytes5to8: _,
548            } => *type_u8,
549            DestinationUnreachable(_) => TYPE_DST_UNREACH,
550            PacketTooBig { mtu: _ } => TYPE_PACKET_TOO_BIG,
551            TimeExceeded(_) => TYPE_TIME_EXCEEDED,
552            ParameterProblem(_) => TYPE_PARAMETER_PROBLEM,
553            EchoRequest(_) => TYPE_ECHO_REQUEST,
554            EchoReply(_) => TYPE_ECHO_REPLY,
555            RouterSolicitation => TYPE_ROUTER_SOLICITATION,
556            RouterAdvertisement(_) => TYPE_ROUTER_ADVERTISEMENT,
557            NeighborSolicitation => TYPE_NEIGHBOR_SOLICITATION,
558            NeighborAdvertisement(_) => TYPE_NEIGHBOR_ADVERTISEMENT,
559            Redirect => TYPE_REDIRECT_MESSAGE,
560        }
561    }
562
563    /// Returns the code value (second byte of the ICMPv6 header) of this type.
564    #[inline]
565    pub fn code_u8(&self) -> u8 {
566        use Icmpv6Type::*;
567        match self {
568            Unknown {
569                type_u8: _,
570                code_u8,
571                bytes5to8: _,
572            } => *code_u8,
573            DestinationUnreachable(code) => code.code_u8(),
574            PacketTooBig { mtu: _ } => 0,
575            TimeExceeded(code) => code.code_u8(),
576            ParameterProblem(header) => header.code.code_u8(),
577            EchoRequest(_) => 0,
578            EchoReply(_) => 0,
579            RouterSolicitation => 0,
580            RouterAdvertisement(_) => 0,
581            NeighborSolicitation => 0,
582            NeighborAdvertisement(_) => 0,
583            Redirect => 0,
584        }
585    }
586
587    /// Calculates the checksum of the ICMPv6 header.
588    ///
589    /// <p style="background:rgba(255,181,77,0.16);padding:0.75em;">
590    /// <strong>Warning:</strong> Don't use this method to verfy if a checksum of a
591    /// received packet is correct. This method assumes that all unused bytes are
592    /// filled with zeros. If this is not the case the computed checksum value will
593    /// will be incorrect for a received packet.
594    ///
595    /// If you want to verify that a received packet has a correct checksum use
596    /// [`Icmpv6Slice::is_checksum_valid`] instead.
597    /// </p>
598    pub fn calc_checksum(
599        &self,
600        source_ip: [u8; 16],
601        destination_ip: [u8; 16],
602        payload: &[u8],
603    ) -> Result<u16, ValueTooBigError<usize>> {
604        // check that the total length fits into the field
605        //
606        // Note according to RFC 2460 the "Upper-Layer Packet Length" used
607        // in the checksum calculation, for protocols that don't contain
608        // their own length information (like ICMPv6), is "the Payload Length
609        // from the IPv6 header, minus the length of any extension headers present
610        // between the IPv6 header and the upper-layer header."
611        let max_payload_len: usize = (u32::MAX as usize) - self.header_len();
612        if max_payload_len < payload.len() {
613            return Err(ValueTooBigError {
614                actual: payload.len(),
615                max_allowed: max_payload_len,
616                value_type: ValueType::Icmpv6PayloadLength,
617            });
618        }
619
620        let msg_len = payload.len() + self.header_len();
621
622        // calculate the checksum
623        // NOTE: rfc4443 section 2.3 - Icmp6 *does* use a pseudoheader,
624        // unlike Icmp4
625        let pseudo_sum = checksum::Sum16BitWords::new()
626            .add_16bytes(source_ip)
627            .add_16bytes(destination_ip)
628            .add_2bytes([0, ip_number::IPV6_ICMP.0])
629            .add_4bytes((msg_len as u32).to_be_bytes());
630
631        use crate::{icmpv6::*, Icmpv6Type::*};
632        Ok(match self {
633            Unknown {
634                type_u8,
635                code_u8,
636                bytes5to8,
637            } => pseudo_sum
638                .add_2bytes([*type_u8, *code_u8])
639                .add_4bytes(*bytes5to8),
640            DestinationUnreachable(header) => {
641                pseudo_sum.add_2bytes([TYPE_DST_UNREACH, header.code_u8()])
642            }
643            PacketTooBig { mtu } => pseudo_sum
644                .add_2bytes([TYPE_PACKET_TOO_BIG, 0])
645                .add_4bytes(mtu.to_be_bytes()),
646            TimeExceeded(code) => pseudo_sum.add_2bytes([TYPE_TIME_EXCEEDED, code.code_u8()]),
647            ParameterProblem(header) => pseudo_sum
648                .add_2bytes([TYPE_PARAMETER_PROBLEM, header.code.code_u8()])
649                .add_4bytes(header.pointer.to_be_bytes()),
650            EchoRequest(echo) => pseudo_sum
651                .add_2bytes([TYPE_ECHO_REQUEST, 0])
652                .add_4bytes(echo.to_bytes()),
653            EchoReply(echo) => pseudo_sum
654                .add_2bytes([TYPE_ECHO_REPLY, 0])
655                .add_4bytes(echo.to_bytes()),
656            RouterSolicitation => pseudo_sum
657                .add_2bytes([TYPE_ROUTER_SOLICITATION, 0])
658                .add_4bytes([0; 4]),
659            RouterAdvertisement(header) => pseudo_sum
660                .add_2bytes([TYPE_ROUTER_ADVERTISEMENT, 0])
661                .add_4bytes(header.to_bytes()),
662            NeighborSolicitation => pseudo_sum
663                .add_2bytes([TYPE_NEIGHBOR_SOLICITATION, 0])
664                .add_4bytes([0; 4]),
665            NeighborAdvertisement(header) => pseudo_sum
666                .add_2bytes([TYPE_NEIGHBOR_ADVERTISEMENT, 0])
667                .add_4bytes(header.to_bytes()),
668            Redirect => pseudo_sum
669                .add_2bytes([TYPE_REDIRECT_MESSAGE, 0])
670                .add_4bytes([0; 4]),
671        }
672        .add_slice(payload)
673        .ones_complement()
674        .to_be())
675    }
676
677    /// Creates a header with the correct checksum.
678    pub fn to_header(
679        self,
680        source_ip: [u8; 16],
681        destination_ip: [u8; 16],
682        payload: &[u8],
683    ) -> Result<Icmpv6Header, ValueTooBigError<usize>> {
684        Ok(Icmpv6Header {
685            checksum: self.calc_checksum(source_ip, destination_ip, payload)?,
686            icmp_type: self,
687        })
688    }
689
690    /// Decode a structured payload based on this ICMPv6 type.
691    #[inline]
692    pub fn payload_slice<'a>(
693        &self,
694        payload: &'a [u8],
695    ) -> Result<icmpv6::Icmpv6PayloadSlice<'a>, err::LenError> {
696        icmpv6::Icmpv6PayloadSlice::from_slice(self, payload)
697    }
698
699    /// Convert a structured payload slice into an owned payload if supported.
700    ///
701    /// The returned [`icmpv6::Icmpv6Payload`] variants contain only fixed-part
702    /// message fields (for example `target_address`) and intentionally exclude
703    /// NDP options. For payload types with variable trailing data, the second
704    /// tuple element contains the remaining bytes that must be parsed
705    /// separately (for example via [`icmpv6::Icmpv6PayloadSlice`] option
706    /// accessors/iterators).
707    #[inline]
708    pub fn payload_from_slice<'a>(
709        &self,
710        payload: &'a [u8],
711    ) -> Result<Option<(icmpv6::Icmpv6Payload, &'a [u8])>, err::LenError> {
712        self.payload_slice(payload).map(|value| value.to_payload())
713    }
714
715    /// Serialized length of the header in bytes/octets.
716    ///
717    /// Note that this size is not the size of the entire
718    /// ICMPv6 packet but only the header.
719    pub fn header_len(&self) -> usize {
720        use Icmpv6Type::*;
721        match self {
722            Unknown {
723                type_u8: _,
724                code_u8: _,
725                bytes5to8: _,
726            }
727            | DestinationUnreachable(_)
728            | PacketTooBig { mtu: _ }
729            | TimeExceeded(_)
730            | ParameterProblem(_)
731            | EchoRequest(_)
732            | EchoReply(_)
733            | RouterSolicitation
734            | RouterAdvertisement(_)
735            | NeighborSolicitation
736            | NeighborAdvertisement(_)
737            | Redirect => 8,
738        }
739    }
740
741    /// If the ICMP type has a fixed size returns the number of
742    /// bytes that should be present after the header of this type.
743    #[inline]
744    pub fn fixed_payload_size(&self) -> Option<usize> {
745        use Icmpv6Type::*;
746        match self {
747            Unknown {
748                type_u8: _,
749                code_u8: _,
750                bytes5to8: _,
751            }
752            | DestinationUnreachable(_)
753            | PacketTooBig { mtu: _ }
754            | TimeExceeded(_)
755            | ParameterProblem(_)
756            | EchoRequest(_)
757            | EchoReply(_)
758            | RouterSolicitation
759            | RouterAdvertisement(_)
760            | NeighborSolicitation
761            | NeighborAdvertisement(_)
762            | Redirect => None,
763        }
764    }
765}
766
767#[cfg(test)]
768mod test {
769    use crate::{
770        err::{ValueTooBigError, ValueType},
771        icmpv6::*,
772        test_gens::*,
773        Icmpv6Type::*,
774        *,
775    };
776    use alloc::format;
777    use proptest::prelude::*;
778
779    proptest! {
780        #[test]
781        fn type_u8(
782            code_u8 in any::<u8>(),
783            bytes5to8 in any::<[u8;4]>(),
784        ) {
785            {
786                let type_u8_type_pair = [
787                    (TYPE_DST_UNREACH, DestinationUnreachable(DestUnreachableCode::SourceAddressFailedPolicy)),
788                    (TYPE_PACKET_TOO_BIG, PacketTooBig{ mtu: u32::from_be_bytes(bytes5to8), }),
789                    (TYPE_TIME_EXCEEDED, TimeExceeded(TimeExceededCode::HopLimitExceeded)),
790                    (TYPE_PARAMETER_PROBLEM, ParameterProblem(ParameterProblemHeader{ code: ParameterProblemCode::UnrecognizedNextHeader, pointer: u32::from_be_bytes(bytes5to8)})),
791                    (TYPE_ECHO_REQUEST, EchoRequest(IcmpEchoHeader::from_bytes(bytes5to8))),
792                    (TYPE_ECHO_REPLY, EchoReply(IcmpEchoHeader::from_bytes(bytes5to8))),
793                    (TYPE_ROUTER_SOLICITATION, RouterSolicitation),
794                    (TYPE_ROUTER_ADVERTISEMENT, RouterAdvertisement(RouterAdvertisementHeader::from_bytes(bytes5to8))),
795                    (TYPE_NEIGHBOR_SOLICITATION, NeighborSolicitation),
796                    (TYPE_NEIGHBOR_ADVERTISEMENT, NeighborAdvertisement(NeighborAdvertisementHeader::from_bytes(bytes5to8))),
797                    (TYPE_REDIRECT_MESSAGE, Redirect),
798                ];
799                for test in type_u8_type_pair {
800                    assert_eq!(test.0, test.1.type_u8());
801                }
802            }
803
804            for t in 0..=u8::MAX {
805                assert_eq!(
806                    t,
807                    Unknown{
808                        type_u8: t,
809                        code_u8,
810                        bytes5to8,
811                    }.type_u8()
812                );
813            }
814        }
815    }
816
817    proptest! {
818        #[test]
819        fn code_u8(
820            code_u8 in any::<u8>(),
821            bytes5to8 in any::<[u8;4]>(),
822        ) {
823            // types with 0 as code
824            {
825                let code_type_pair = [
826                    (0, PacketTooBig{ mtu: u32::from_be_bytes(bytes5to8), }),
827                    (0, EchoRequest(IcmpEchoHeader::from_bytes(bytes5to8))),
828                    (0, EchoReply(IcmpEchoHeader::from_bytes(bytes5to8))),
829                    (0, RouterSolicitation),
830                    (0, RouterAdvertisement(RouterAdvertisementHeader::from_bytes(bytes5to8))),
831                    (0, NeighborSolicitation),
832                    (0, NeighborAdvertisement(NeighborAdvertisementHeader::from_bytes(bytes5to8))),
833                    (0, Redirect),
834                ];
835                for test in code_type_pair {
836                    assert_eq!(test.0, test.1.code_u8());
837                }
838            }
839
840            // destination unreachable
841            for (code, code_u8) in dest_unreachable_code_test_consts::VALID_VALUES {
842                assert_eq!(code_u8, DestinationUnreachable(code).code_u8());
843            }
844
845            // time exceeded
846            for (code, code_u8) in time_exceeded_code_test_consts::VALID_VALUES {
847                assert_eq!(code_u8, TimeExceeded(code).code_u8());
848            }
849
850            // parameter problem
851            for (code, code_u8) in parameter_problem_code_test_consts::VALID_VALUES {
852                assert_eq!(
853                    code_u8,
854                    ParameterProblem(
855                        ParameterProblemHeader{
856                            code,
857                            pointer: u32::from_be_bytes(bytes5to8),
858                        }
859                    ).code_u8()
860                );
861            }
862
863            // unknown
864            for t in 0..=u8::MAX {
865                assert_eq!(
866                    code_u8,
867                    Unknown{
868                        type_u8: t,
869                        code_u8,
870                        bytes5to8,
871                    }.code_u8()
872                );
873            }
874        }
875    }
876
877    proptest! {
878        #[test]
879        #[cfg(not(target_pointer_width = "32"))]
880        fn calc_checksum(
881            ip_header in ipv6_any(),
882            icmpv6_type in icmpv6_type_any(),
883            type_u8 in any::<u8>(),
884            code_u8 in any::<u8>(),
885            bytes5to8 in any::<[u8;4]>(),
886            // max length is u32::MAX - header_len (7)
887            bad_len in (core::u32::MAX - 7) as usize..=(core::isize::MAX as usize),
888            payload in proptest::collection::vec(any::<u8>(), 0..64)
889        ) {
890            use Icmpv6Type::*;
891
892            // size error case
893            {
894                // SAFETY: In case the error is not triggered
895                //         a segmentation fault will be triggered.
896                let too_big_slice = unsafe {
897                    //NOTE: The pointer must be initialized with a non null value
898                    //      otherwise a key constraint of slices is not fulfilled
899                    //      which can lead to crashes in release mode.
900                    use core::ptr::NonNull;
901                    core::slice::from_raw_parts(
902                        NonNull::<u8>::dangling().as_ptr(),
903                        bad_len
904                    )
905                };
906                assert_eq!(
907                    icmpv6_type.calc_checksum(ip_header.source, ip_header.destination, too_big_slice),
908                    Err(ValueTooBigError{
909                        actual: bad_len,
910                        max_allowed: (core::u32::MAX - 8) as usize,
911                        value_type: ValueType::Icmpv6PayloadLength
912                    })
913                );
914            }
915
916            // normal cases
917            {
918                let test_checksum_calc = |icmp_type: Icmpv6Type| {
919                    let expected_checksum = {
920                        crate::checksum::Sum16BitWords::new()
921                        .add_16bytes(ip_header.source)
922                        .add_16bytes(ip_header.destination)
923                        .add_2bytes([0, ip_number::IPV6_ICMP.0])
924                        .add_4bytes((
925                            payload.len() as u32 + icmpv6_type.header_len() as u32
926                        ).to_be_bytes())
927                        .add_slice(&Icmpv6Header {
928                            icmp_type: icmp_type.clone(),
929                            checksum: 0 // use zero so the checksum gets correct calculated
930                        }.to_bytes())
931                        .add_slice(&payload)
932                        .ones_complement()
933                        .to_be()
934                    };
935                    assert_eq!(
936                        expected_checksum,
937                        icmp_type.calc_checksum(
938                            ip_header.source,
939                            ip_header.destination,
940                            &payload
941                        ).unwrap()
942                    );
943                };
944
945                // unknown
946                test_checksum_calc(
947                    Unknown{
948                        type_u8, code_u8, bytes5to8
949                    }
950                );
951
952                // destination unreachable
953                for (code, _) in dest_unreachable_code_test_consts::VALID_VALUES {
954                    test_checksum_calc(DestinationUnreachable(code));
955                }
956
957                // packet too big
958                test_checksum_calc(PacketTooBig{
959                    mtu: u32::from_be_bytes(bytes5to8)
960                });
961
962                // time exceeded
963                for (code, _) in time_exceeded_code_test_consts::VALID_VALUES {
964                    test_checksum_calc(TimeExceeded(code));
965                }
966
967                // parameter problem
968                for (code, _) in parameter_problem_code_test_consts::VALID_VALUES {
969                    test_checksum_calc(ParameterProblem(
970                        ParameterProblemHeader{
971                            code,
972                            pointer: u32::from_be_bytes(bytes5to8)
973                        }
974                    ));
975                }
976
977                // echo request
978                test_checksum_calc(EchoRequest(
979                    IcmpEchoHeader::from_bytes(bytes5to8)
980                ));
981
982                // echo reply
983                test_checksum_calc(EchoReply(
984                    IcmpEchoHeader::from_bytes(bytes5to8)
985                ));
986
987                // router solicitation
988                test_checksum_calc(RouterSolicitation);
989
990                // router advertisement
991                test_checksum_calc(RouterAdvertisement(
992                    RouterAdvertisementHeader::from_bytes(bytes5to8)
993                ));
994
995                // neighbor solicitation
996                test_checksum_calc(NeighborSolicitation);
997
998                // neighbor advertisement
999                test_checksum_calc(NeighborAdvertisement(
1000                    NeighborAdvertisementHeader::from_bytes(bytes5to8)
1001                ));
1002
1003                // redirect
1004                test_checksum_calc(Redirect);
1005            }
1006        }
1007    }
1008
1009    proptest! {
1010        #[test]
1011        #[cfg(not(target_pointer_width = "32"))]
1012        fn to_header(
1013            ip_header in ipv6_any(),
1014            icmpv6_type in icmpv6_type_any(),
1015            // max length is u32::MAX - header_len (7)
1016            bad_len in (core::u32::MAX - 7) as usize..=(core::isize::MAX as usize),
1017            payload in proptest::collection::vec(any::<u8>(), 0..1024)
1018        ) {
1019            // size error case
1020            {
1021                // SAFETY: In case the error is not triggered
1022                //         a segmentation fault will be triggered.
1023                let too_big_slice = unsafe {
1024                    //NOTE: The pointer must be initialized with a non null value
1025                    //      otherwise a key constraint of slices is not fulfilled
1026                    //      which can lead to crashes in release mode.
1027                    use core::ptr::NonNull;
1028                    core::slice::from_raw_parts(
1029                        NonNull::<u8>::dangling().as_ptr(),
1030                        bad_len
1031                    )
1032                };
1033                assert_eq!(
1034                    icmpv6_type.to_header(ip_header.source, ip_header.destination, too_big_slice),
1035                    Err(ValueTooBigError{
1036                        actual: bad_len,
1037                        max_allowed: (core::u32::MAX - 8) as usize,
1038                        value_type: ValueType::Icmpv6PayloadLength,
1039                    })
1040                );
1041            }
1042            // normal case
1043            assert_eq!(
1044                icmpv6_type.to_header(ip_header.source, ip_header.destination, &payload).unwrap(),
1045                Icmpv6Header {
1046                    checksum: icmpv6_type.calc_checksum(ip_header.source, ip_header.destination, &payload).unwrap(),
1047                    icmp_type: icmpv6_type,
1048                }
1049            );
1050        }
1051    }
1052
1053    proptest! {
1054        #[test]
1055        fn header_len(
1056            code_u8 in any::<u8>(),
1057            bytes5to8 in any::<[u8;4]>(),
1058        ) {
1059            let len_8_hdrs = [
1060                DestinationUnreachable(DestUnreachableCode::Prohibited),
1061                PacketTooBig{ mtu: u32::from_be_bytes(bytes5to8), },
1062                TimeExceeded(TimeExceededCode::FragmentReassemblyTimeExceeded),
1063                ParameterProblem(ParameterProblemHeader{
1064                    code: ParameterProblemCode::UnrecognizedIpv6Option,
1065                    pointer: u32::from_be_bytes(bytes5to8),
1066                }),
1067                EchoRequest(IcmpEchoHeader::from_bytes(bytes5to8)),
1068                EchoReply(IcmpEchoHeader::from_bytes(bytes5to8)),
1069                RouterSolicitation,
1070                RouterAdvertisement(RouterAdvertisementHeader::from_bytes(bytes5to8)),
1071                NeighborSolicitation,
1072                NeighborAdvertisement(NeighborAdvertisementHeader::from_bytes(bytes5to8)),
1073                Redirect,
1074            ];
1075
1076            for hdr in len_8_hdrs {
1077                assert_eq!(8, hdr.header_len());
1078            }
1079
1080            for t in 0..=u8::MAX {
1081                assert_eq!(
1082                    8,
1083                    Unknown{
1084                        type_u8: t,
1085                        code_u8,
1086                        bytes5to8,
1087                    }.header_len()
1088                );
1089            }
1090        }
1091    }
1092
1093    proptest! {
1094        #[test]
1095        fn fixed_payload_size(
1096            code_u8 in any::<u8>(),
1097            bytes5to8 in any::<[u8;4]>(),
1098        ) {
1099            let variable_payload_headers = [
1100                DestinationUnreachable(DestUnreachableCode::Prohibited),
1101                PacketTooBig{ mtu: u32::from_be_bytes(bytes5to8), },
1102                TimeExceeded(TimeExceededCode::HopLimitExceeded),
1103                ParameterProblem(ParameterProblemHeader{
1104                    code: ParameterProblemCode::SrUpperLayerHeaderError,
1105                    pointer: u32::from_be_bytes(bytes5to8),
1106                }),
1107                EchoRequest(IcmpEchoHeader::from_bytes(bytes5to8)),
1108                EchoReply(IcmpEchoHeader::from_bytes(bytes5to8)),
1109                RouterSolicitation,
1110                RouterAdvertisement(RouterAdvertisementHeader::from_bytes(bytes5to8)),
1111                NeighborSolicitation,
1112                NeighborAdvertisement(NeighborAdvertisementHeader::from_bytes(bytes5to8)),
1113                Redirect,
1114            ];
1115
1116            for hdr in variable_payload_headers {
1117                assert_eq!(None, hdr.fixed_payload_size());
1118            }
1119
1120            for t in 0..=u8::MAX {
1121                assert_eq!(
1122                    None,
1123                    Unknown{
1124                        type_u8: t,
1125                        code_u8,
1126                        bytes5to8,
1127                    }.fixed_payload_size()
1128                );
1129            }
1130        }
1131    }
1132
1133    #[test]
1134    fn payload_slice() {
1135        use crate::icmpv6::*;
1136
1137        assert_eq!(
1138            Icmpv6Type::RouterSolicitation
1139                .payload_slice(&[1, 1, 1, 2, 3, 4, 5, 6])
1140                .unwrap(),
1141            Icmpv6PayloadSlice::RouterSolicitation(
1142                RouterSolicitationPayloadSlice::from_slice(&[1, 1, 1, 2, 3, 4, 5, 6]).unwrap()
1143            )
1144        );
1145
1146        assert_eq!(
1147            Icmpv6Type::RouterAdvertisement(RouterAdvertisementHeader {
1148                cur_hop_limit: 0,
1149                managed_address_config: false,
1150                other_config: false,
1151                router_lifetime: 0,
1152            })
1153            .payload_slice(&[0, 0, 0, 1, 0, 0, 0, 2])
1154            .unwrap(),
1155            Icmpv6PayloadSlice::RouterAdvertisement(
1156                RouterAdvertisementPayloadSlice::from_slice(&[0, 0, 0, 1, 0, 0, 0, 2]).unwrap()
1157            )
1158        );
1159
1160        assert_eq!(
1161            Icmpv6Type::NeighborSolicitation
1162                .payload_from_slice(&[1; 16])
1163                .unwrap(),
1164            Some((
1165                Icmpv6Payload::NeighborSolicitation(NeighborSolicitationPayload {
1166                    target_address: [1; 16].into()
1167                }),
1168                &[][..]
1169            ))
1170        );
1171
1172        assert_eq!(
1173            Icmpv6Type::NeighborSolicitation
1174                .payload_from_slice(&[1; 16 + 8])
1175                .unwrap(),
1176            Some((
1177                Icmpv6Payload::NeighborSolicitation(NeighborSolicitationPayload {
1178                    target_address: [1; 16].into()
1179                }),
1180                &[1; 8][..]
1181            ))
1182        );
1183
1184        assert_eq!(
1185            Icmpv6Type::EchoRequest(IcmpEchoHeader { id: 1, seq: 2 })
1186                .payload_from_slice(&[1, 2, 3, 4])
1187                .unwrap(),
1188            None
1189        );
1190    }
1191
1192    #[test]
1193    fn debug() {
1194        assert_eq!(
1195            format!(
1196                "{:?}",
1197                Icmpv6Type::Unknown {
1198                    type_u8: 0,
1199                    code_u8: 1,
1200                    bytes5to8: [2, 3, 4, 5]
1201                }
1202            ),
1203            "Unknown { type_u8: 0, code_u8: 1, bytes5to8: [2, 3, 4, 5] }"
1204        )
1205    }
1206
1207    proptest! {
1208        #[test]
1209        fn clone_eq(t in icmpv6_type_any()) {
1210            assert_eq!(t, t.clone());
1211        }
1212    }
1213}