use super::super::*;

use arrayvec::ArrayVec;
use std::slice::from_raw_parts;

/// Module containing ICMPv6 related types and constants
pub mod icmpv6 {

    /// The maximum number of bytes/octets the ICMPv6 part of a packet can contain.
    ///
    /// The value is determined by the maximum value of the "Upper-Layer Packet Length"
    /// field. This field is not directly part of the packet but used during the checksum
    /// calculation in the pseudo header.
    ///
    /// The "Upper-Layer Packet Length" is represented as an `u32` and defined as
    /// "...the Payload Length from the IPv6 header, minus the length of any
    /// extension headers present between the IPv6 header and the upper-layer
    /// header" (according to RFC 2460 Section 8.1). In other words, the length of the
    /// ICMPv6 part of the packet.
    ///
    /// Therefor the maximum size of an ICMPv6 packet is `u32::MAX`.
    pub const MAX_ICMPV6_BYTE_LEN: usize = u32::MAX as usize;

    /// ICMPv6 type value indicating a "Destination Unreachable" message.
    pub const TYPE_DST_UNREACH: u8 = 1;

    /// ICMPv6 type value indicating a "Packet Too Big" message.
    pub const TYPE_PACKET_TOO_BIG: u8 = 2;

    /// ICMPv6 type value indicating a "Time Exceeded" message.
    pub const TYPE_TIME_EXCEEDED: u8 = 3;

    /// ICMPv6 type value indicating a "Parameter Problem" message.
    pub const TYPE_PARAMETER_PROBLEM: u8 = 4;

    /// ICMPv6 type value indicating an "Echo Request" message.
    pub const TYPE_ECHO_REQUEST: u8 = 128;

    /// ICMPv6 type value indicating an "Echo Reply" message.
    pub const TYPE_ECHO_REPLY: u8 = 129;

    /// ICMPv6 type value indicating a "Multicast Listener Query" message.
    pub const TYPE_MULTICAST_LISTENER_QUERY: u8 = 130;

    /// ICMPv6 type value indicating a "Multicast Listener Report" message.
    pub const TYPE_MULTICAST_LISTENER_REPORT: u8 = 131;

    /// ICMPv6 type value indicating a "Multicast Listener Done" message.
    pub const TYPE_MULTICAST_LISTENER_REDUCTION: u8 = 132;

    /// ICMPv6 type value indicating a "Router Solicitation" message.
    pub const TYPE_ROUTER_SOLICITATION: u8 = 133;

    /// ICMPv6 type value indicating a "Router Advertisement" message.
    pub const TYPE_ROUTER_ADVERTISEMENT: u8 = 134;

    /// ICMPv6 type value indicating a "Neighbor Solicitation" message.
    pub const TYPE_NEIGHBOR_SOLICITATION: u8 = 135;

    /// ICMPv6 type value indicating a "Neighbor Advertisement" message.
    pub const TYPE_NEIGHBOR_ADVERTISEMENT: u8 = 136;

    /// ICMPv6 type value indicating a "Redirect Message" message.
    pub const TYPE_REDIRECT_MESSAGE: u8 = 137;

    /// ICMPv6 type value indicating a "Router Renumbering" message.
    pub const TYPE_ROUTER_RENUMBERING: u8 = 138;

    /// ICMPv6 type value indicating a "Inverse Neighbor Discovery Solicitation" message.
    pub const TYPE_INVERSE_NEIGHBOR_DISCOVERY_SOLICITATION: u8 = 141;

    /// ICMPv6 type value indicating a "Inverse Neighbor Discovery Advertisement" message.
    pub const TYPE_INVERSE_NEIGHBOR_DISCOVERY_ADVERTISEMENT: u8 = 142;

    /// ICMPv6 type value indicating a "Extended Echo Request" message.
    pub const TYPE_EXT_ECHO_REQUEST: u8 = 160;

    /// ICMPv6 type value indicating a "Extended Echo Reply" message.
    pub const TYPE_EXT_ECHO_REPLY: u8 = 161;

    /// ICMPv6 destination unreachable code for "no route to destination".
    pub const CODE_DST_UNREACH_NO_ROUTE: u8 = 0;

    /// ICMPv6 destination unreachable code for "communication with
    /// destination administratively prohibited".
    pub const CODE_DST_UNREACH_PROHIBITED: u8 = 1;

    /// ICMPv6 destination unreachable code for "beyond scope of source address".
    pub const CODE_DST_UNREACH_BEYOND_SCOPE: u8 = 2;

    /// ICMPv6 destination unreachable code for "address unreachable".
    pub const CODE_DST_UNREACH_ADDR: u8 = 3;

    /// ICMPv6 destination unreachable code for "port unreachable".
    pub const CODE_DST_UNREACH_PORT: u8 = 4;

    /// ICMPv6 destination unreachable code for "source address failed ingress/egress policy".
    pub const CODE_DST_UNREACH_SOURCE_ADDRESS_FAILED_POLICY: u8 = 5;

    /// ICMPv6 destination unreachable code for "reject route to destination".
    pub const CODE_DST_UNREACH_REJECT_ROUTE_TO_DEST: u8 = 6;

    /// "Destination Unreachable" ICMPv6 code containing a reason why a
    /// destination could not be reached.
    ///
    /// # RFC 4443 Description:
    ///
    /// A Destination Unreachable message SHOULD be generated by a router, or
    /// by the IPv6 layer in the originating node, in response to a packet
    /// that cannot be delivered to its destination address for reasons other
    /// than congestion.  (An ICMPv6 message MUST NOT be generated if a
    /// packet is dropped due to congestion.)
    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
    pub enum DestUnreachableCode {
        /// No route to destination
        NoRoute = 0,
        /// Communication with destination administratively prohibited
        Prohibited = 1,
        /// Beyond scope of source address
        BeyondScope = 2,
        /// Address unreachable
        Address = 3,
        /// Port unreachable
        Port = 4,
        /// Source address failed ingress/egress policy
        SourceAddressFailedPolicy = 5,
        /// Reject route to destination
        RejectRoute = 6,
    }

    impl DestUnreachableCode {
        /// Converts the u8 code value from an ICMPv6 "destination unreachable"
        /// packet to an `icmpv6::DestUnreachableCode` enum.
        ///
        /// # Example Usage:
        ///
        /// ```
        /// use etherparse::{icmpv6, icmpv6::DestUnreachableCode};
        /// let icmp_packet: [u8;8] = [
        ///     icmpv6::TYPE_DST_UNREACH, icmpv6::CODE_DST_UNREACH_PORT, 0, 0,
        ///     0, 0, 0, 0,
        /// ];
        ///
        /// if icmpv6::TYPE_DST_UNREACH == icmp_packet[0] {
        ///     let dst = icmpv6::DestUnreachableCode::from_u8(
        ///         icmp_packet[1]
        ///     );
        ///     assert_eq!(dst, Some(icmpv6::DestUnreachableCode::Port));
        /// }
        /// ```
        pub fn from_u8(code_u8: u8) -> Option<DestUnreachableCode> {
            use DestUnreachableCode::*;
            match code_u8 {
                CODE_DST_UNREACH_NO_ROUTE => Some(NoRoute),
                CODE_DST_UNREACH_PROHIBITED => Some(Prohibited),
                CODE_DST_UNREACH_BEYOND_SCOPE => Some(BeyondScope),
                CODE_DST_UNREACH_ADDR => Some(Address),
                CODE_DST_UNREACH_PORT => Some(Port),
                CODE_DST_UNREACH_SOURCE_ADDRESS_FAILED_POLICY => Some(SourceAddressFailedPolicy),
                CODE_DST_UNREACH_REJECT_ROUTE_TO_DEST => Some(RejectRoute),
                _ => None,
            }
        }

        /// Returns the code value of the destination unreachable packet.
        ///
        /// This is the second byte of an ICMPv6 packet.
        #[inline]
        pub fn code_u8(&self) -> u8 {
            *self as u8
        }
    }

    /// ICMPv6 time exceeded code for "hop limit exceeded in transit"
    pub const CODE_TIME_EXCEEDED_HOP_LIMIT_EXCEEDED: u8 = 0;

    /// ICMPv6 time exceeded code for "fragment reassembly time exceeded"
    pub const CODE_TIME_EXCEEDED_FRAGMENT_REASSEMBLY_TIME_EXCEEDED: u8 = 1;

    /// Code values for ICMPv6 time exceeded message.
    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
    pub enum TimeExceededCode {
        /// "hop limit exceeded in transit"
        HopLimitExceeded = 0,
        /// "fragment reassembly time exceeded"
        FragmentReassemblyTimeExceeded = 1,
    }

    impl TimeExceededCode {

        /// Tries to convert a code [`u8`] value to a [`TimeExceededCode`] value.
        ///
        /// Returns [`None`] in case the code value is not known as a time exceeded code.
        #[inline]
        pub fn from_u8(code_u8: u8) -> Option<TimeExceededCode> {
            use TimeExceededCode::*;
            match code_u8 {
                CODE_TIME_EXCEEDED_HOP_LIMIT_EXCEEDED => Some(HopLimitExceeded),
                CODE_TIME_EXCEEDED_FRAGMENT_REASSEMBLY_TIME_EXCEEDED => {
                    Some(FragmentReassemblyTimeExceeded)
                }
                _ => None,
            }
        }

        /// Returns the [`u8`] value of the code.
        #[inline]
        pub fn code_u8(&self) -> u8 {
            *self as u8
        }
    }

    /// ICMPv6 parameter problem code for "erroneous header field encountered" (from [RFC 4443](https://tools.ietf.org/html/rfc4443)).
    pub const CODE_PARAM_PROBLEM_ERR_HEADER_FIELD: u8 = 0;

    /// ICMPv6 parameter problem code for "unrecognized Next Header type encountered" (from [RFC 4443](https://tools.ietf.org/html/rfc4443)).
    pub const CODE_PARAM_PROBLEM_UNRECOG_NEXT_HEADER: u8 = 1;

    /// ICMPv6 parameter problem code for "unrecognized IPv6 option encountered" (from [RFC 4443](https://tools.ietf.org/html/rfc4443)).
    pub const CODE_PARAM_PROBLEM_UNRECOG_IPV6_OPTION: u8 = 2;

    /// ICMPv6 parameter problem code for "IPv6 First Fragment has incomplete IPv6 Header Chain" (from [RFC 7112](https://tools.ietf.org/html/rfc7112)).
    pub const CODE_PARAM_PROBLEM_IPV6_FIRST_FRAG_INCOMP_HEADER_CHAIN: u8 = 3;

    /// ICMPv6 parameter problem code for "SR Upper-layer Header Error" (from [RFC 8754](https://tools.ietf.org/html/rfc8754)).
    pub const CODE_PARAM_PROBLEM_SR_UPPER_LAYER_HEADER_ERROR: u8 = 4;

    /// ICMPv6 parameter problem code for "Unrecognized Next Header type encountered by intermediate node" (from [RFC 8883](https://tools.ietf.org/html/rfc8883)).
    pub const CODE_PARAM_PROBLEM_UNRECOG_NEXT_HEADER_BY_INTERMEDIATE_NODE: u8 = 5;

    /// ICMPv6 parameter problem code for "Extension header too big" (from [RFC 8883](https://tools.ietf.org/html/rfc8883)).
    pub const CODE_PARAM_PROBLEM_EXT_HEADER_TOO_BIG: u8 = 6;

    /// ICMPv6 parameter problem code for "Extension header chain too long" (from [RFC 8883](https://tools.ietf.org/html/rfc8883)).
    pub const CODE_PARAM_PROBLEM_EXT_HEADER_CHAIN_TOO_LONG: u8 = 7;

    /// ICMPv6 parameter problem code for "Too many extension headers" (from [RFC 8883](https://tools.ietf.org/html/rfc8883)).
    pub const CODE_PARAM_PROBLEM_TOO_MANY_EXT_HEADERS: u8 = 8;

    /// ICMPv6 parameter problem code for "Too many options in extension header" (from [RFC 8883](https://tools.ietf.org/html/rfc8883)).
    pub const CODE_PARAM_PROBLEM_TOO_MANY_OPTIONS_EXT_HEADER: u8 = 9;

    /// ICMPv6 parameter problem code for "Option too big" (from [RFC 8883](https://tools.ietf.org/html/rfc8883)).
    pub const CODE_PARAM_PROBLEM_OPTION_TOO_BIG: u8 = 10;

    /// Code values for ICMPv6 parameter problem messages.
    ///
    /// Source: <https://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xhtml#icmpv6-parameters-codes-5>
    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
    pub enum ParameterProblemCode {
        /// Erroneous header field encountered (from [RFC 4443](https://tools.ietf.org/html/rfc4443))
        ErroneousHeaderField = 0,
        /// Unrecognized Next Header type encountered (from [RFC 4443](https://tools.ietf.org/html/rfc4443))
        UnrecognizedNextHeader = 1,
        /// Unrecognized IPv6 option encountered (from [RFC 4443](https://tools.ietf.org/html/rfc4443))
        UnrecognizedIpv6Option = 2,
        /// IPv6 First Fragment has incomplete IPv6 Header Chain (from [RFC 7112](https://tools.ietf.org/html/rfc7112))
        Ipv6FirstFragmentIncompleteHeaderChain = 3,
        /// SR Upper-layer Header Error (from [RFC 8754](https://tools.ietf.org/html/rfc8754)).
        SrUpperLayerHeaderError = 4,
        /// Unrecognized Next Header type encountered by intermediate node (from [RFC 8883](https://tools.ietf.org/html/rfc8883))
        UnrecognizedNextHeaderByIntermediateNode = 5,
        /// Extension header too big (from [RFC 8883](https://tools.ietf.org/html/rfc8883))
        ExtensionHeaderTooBig = 6,
        /// Extension header chain too long (from [RFC 8883](https://tools.ietf.org/html/rfc8883))
        ExtensionHeaderChainTooLong = 7,
        /// Too many extension headers (from [RFC 8883](https://tools.ietf.org/html/rfc8883))
        TooManyExtensionHeaders = 8,
        /// Too many options in extension header (from [RFC 8883](https://tools.ietf.org/html/rfc8883))
        TooManyOptionsInExtensionHeader = 9,
        /// Option too big (from [RFC 8883](https://tools.ietf.org/html/rfc8883))
        OptionTooBig = 10,
    }

    impl ParameterProblemCode {

        /// Tries to convert a code [`u8`] value to a [`ParameterProblemCode`] value.
        ///
        /// Returns [`None`] in case the code value is not known as a parameter problem code.
        pub fn from_u8(code_u8: u8) -> Option<ParameterProblemCode> {
            use ParameterProblemCode::*;
            match code_u8 {
                CODE_PARAM_PROBLEM_ERR_HEADER_FIELD => Some(ErroneousHeaderField),
                CODE_PARAM_PROBLEM_UNRECOG_NEXT_HEADER => Some(UnrecognizedNextHeader),
                CODE_PARAM_PROBLEM_UNRECOG_IPV6_OPTION => Some(UnrecognizedIpv6Option),
                CODE_PARAM_PROBLEM_IPV6_FIRST_FRAG_INCOMP_HEADER_CHAIN => {
                    Some(Ipv6FirstFragmentIncompleteHeaderChain)
                }
                CODE_PARAM_PROBLEM_SR_UPPER_LAYER_HEADER_ERROR => Some(SrUpperLayerHeaderError),
                CODE_PARAM_PROBLEM_UNRECOG_NEXT_HEADER_BY_INTERMEDIATE_NODE => {
                    Some(UnrecognizedNextHeaderByIntermediateNode)
                }
                CODE_PARAM_PROBLEM_EXT_HEADER_TOO_BIG => Some(ExtensionHeaderTooBig),
                CODE_PARAM_PROBLEM_EXT_HEADER_CHAIN_TOO_LONG => Some(ExtensionHeaderChainTooLong),
                CODE_PARAM_PROBLEM_TOO_MANY_EXT_HEADERS => Some(TooManyExtensionHeaders),
                CODE_PARAM_PROBLEM_TOO_MANY_OPTIONS_EXT_HEADER => Some(TooManyOptionsInExtensionHeader),
                CODE_PARAM_PROBLEM_OPTION_TOO_BIG => Some(OptionTooBig),
                _ => None,
            }
        }

        /// Returns the [`u8`] value of the code.
        #[inline]
        pub fn code_u8(&self) -> u8 {
            *self as u8
        }
    }

    /// ICMPv6 parameter problem header.
    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
    pub struct ParameterProblemHeader {
        /// The code can offer additional informations about what kind of parameter
        /// problem caused the error.
        pub code: ParameterProblemCode,
        /// Identifies the octet offset within the
        /// invoking packet where the error was detected.
        ///
        /// The pointer will point beyond the end of the ICMPv6
        /// packet if the field in error is beyond what can fit
        /// in the maximum size of an ICMPv6 error message.
        pub pointer: u32,
    }

} // mod icmpv6

use icmpv6::*;

/// Different kinds of ICMPv6 messages.
///
/// The data stored in this enum corresponds to the statically sized data
/// at the start of an ICMPv6 packet without the checksum. If you also need
/// the checksum you can package and [`Icmpv6Type`] value in an [`Icmpv6Header`]
/// struct.
///
/// # Decoding Example (complete packet):
///
/// ```
/// # use etherparse::{PacketBuilder};
/// # let mut builder = PacketBuilder::
/// #   ethernet2([0;6], [0;6])
/// #   .ipv6([0;16], [0;16], 20)
/// #   .icmpv6_echo_request(1, 2);
/// # let payload = [1,2,3,4];
/// # let mut packet = Vec::<u8>::with_capacity(builder.size(payload.len()));
/// # builder.write(&mut packet, &payload);
/// use etherparse::PacketHeaders;
///
/// let headers = PacketHeaders::from_ethernet_slice(&packet).unwrap();
///
/// use etherparse::TransportHeader::*;
/// match headers.transport {
///     Some(Icmpv6(icmp)) => {
///         use etherparse::Icmpv6Type::*;
///         match icmp.icmp_type {
///             // Unknown is used when further decoding is currently not supported for the icmp type & code.
///             // You can still further decode the packet on your own by using the raw data in this enum
///             // together with `headers.payload` (contains the packet data after the 8th byte)
///             Unknown{ type_u8, code_u8, bytes5to8 } => println!("Unknown{{ type_u8: {}, code_u8: {}, bytes5to8: {:?} }}", type_u8, code_u8, bytes5to8),
///             DestinationUnreachable(header) => println!("{:?}", header),
///             PacketTooBig { mtu } => println!("TimeExceeded{{ mtu: {} }}", mtu),
///             TimeExceeded(code) => println!("{:?}", code),
///             ParameterProblem(header) => println!("{:?}", header),
///             EchoRequest(header) => println!("{:?}", header),
///             EchoReply(header) => println!("{:?}", header),
///         }
///     },
///     _ => {},
/// }
/// ```
///
/// # Encoding Example (only ICMPv6 part)
///
/// To get the on wire bytes of an Icmpv6Type it needs to get packaged
/// into a [`Icmpv6Header`] so the checksum gets calculated.
///
/// ```
/// # use etherparse::Ipv6Header;
/// # let ip_header: Ipv6Header = Default::default();
/// # let invoking_packet : [u8;0] = [];
///
/// use etherparse::{Icmpv6Type, icmpv6::DestUnreachableCode};
/// let t = Icmpv6Type::DestinationUnreachable(
///     DestUnreachableCode::Address
/// );
///
/// // to calculate the checksum the ip header and the payload
/// // (in case of dest unreachable the invoking packet) are needed
/// let header = t.to_header(ip_header.source, ip_header.destination, &invoking_packet).unwrap();
///
/// // an ICMPv6 packet is composed of the header and payload
/// let mut packet = Vec::with_capacity(header.header_len() + invoking_packet.len());
/// packet.extend_from_slice(&header.to_bytes());
/// packet.extend_from_slice(&invoking_packet);
/// #
/// # {
/// #   let checksum_be = header.checksum.to_be_bytes();
/// #   assert_eq!(
/// #       &packet,
/// #       &[
/// #           header.icmp_type.type_u8(),
/// #           header.icmp_type.code_u8(),
/// #           checksum_be[0],
/// #           checksum_be[1],
/// #           0,0,0,0
/// #       ]
/// #   );
/// # }
/// ```
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Icmpv6Type {
    /// In case of an unknown icmp type is received the header elements of
    /// the first 8 bytes/octets are stored raw in this enum value.
    ///
    /// # What is part of the header for `Icmpv6Type::Unknown`?
    ///
    /// For unknown ICMPv6 type & code combination the first 8 bytes are stored
    /// in the [`Icmpv6Header`] and the rest is stored in the payload
    /// ([`Icmpv6Slice::payload`] or [`PacketHeaders::payload`]).
    ///
    ///
    /// ```text
    /// 0               1               2               3               4
    /// +---------------------------------------------------------------+  -
    /// |     type_u8   |    code_u8    |  checksum (in Icmpv6Header)   |  |
    /// +---------------------------------------------------------------+  | part of header & type
    /// |                          bytes5to8                            |  ↓
    /// +---------------------------------------------------------------+  -
    /// |                                                               |  |
    /// ...                           ...                             ...  | part of payload
    /// |                                                               |  ↓
    /// +---------------------------------------------------------------+  -
    /// ```
    Unknown {
        /// ICMPv6 type (present in the first byte of the ICMPv6 packet).
        type_u8: u8,
        /// ICMPv6 code (present in the 2nd byte of the ICMPv6 packet).
        code_u8: u8,
        /// Bytes located at th 5th, 6th, 7th and 8th position of the ICMP packet.
        bytes5to8: [u8; 4],
    },

    /// Message sent to inform the client that the destination is unreachable for
    /// some reason.
    ///
    /// # What is part of the header for `Icmpv6Type::DestinationUnreachable`?
    ///
    /// For the `Icmpv6Type::DestinationUnreachable` type the first 8 bytes/octets of the ICMPv6
    /// packet are part of the header. The `unused` part is not stored and droped.
    /// The offending packet is stored in the payload part of the packet
    /// ([`Icmpv6Slice::payload`] & [`PacketHeaders::payload`]) and is not part of
    /// the [`Icmpv6Header`].
    ///
    /// ```text
    /// 0               1               2               3               4
    /// +---------------------------------------------------------------+  -
    /// |       1       | [value as u8] |  checksum (in Icmpv6Header)   |  |
    /// +---------------------------------------------------------------+  | part of header & type
    /// |                           <unused>                            |  ↓
    /// +---------------------------------------------------------------+  -
    /// |                                                               |  |
    /// |     <As much of invoking packet as possible without           |  | part of payload
    /// ...   the ICMPv6 packet exceeding the minimum IPv6 MTU>       ...  |
    /// |                                                               |  ↓
    /// +---------------------------------------------------------------+  -
    /// ```
    ///
    /// # RFC 4443 Description
    ///
    /// A Destination Unreachable message SHOULD be generated by a router, or
    /// by the IPv6 layer in the originating node, in response to a packet
    /// that cannot be delivered to its destination address for reasons other
    /// than congestion.  (An ICMPv6 message MUST NOT be generated if a
    /// packet is dropped due to congestion.)
    DestinationUnreachable(icmpv6::DestUnreachableCode),

    /// Sent if a packet to too big to be forwarded.
    ///
    /// # What is part of the header for `Icmpv6Type::PacketTooBig`?
    ///
    /// For the `Icmpv6Type::PacketTooBig` type the first 8 bytes/octets of the ICMPv6
    /// packet are part of the header. The offending packet is stored in the payload part of the packet
    /// ([`Icmpv6Slice::payload`] & [`PacketHeaders::payload`]) and is not part of
    /// the [`Icmpv6Header`].
    ///
    /// ```text
    /// 0               1               2               3               4
    /// +---------------------------------------------------------------+  -
    /// |       2       |       0       |  checksum (in Icmpv6Header)   |  |
    /// +---------------------------------------------------------------+  | part of header & type
    /// |                              mtu                              |  ↓
    /// +---------------------------------------------------------------+  -
    /// |                                                               |  |
    /// |     <As much of invoking packet as possible without           |  | part of payload
    /// ...   the ICMPv6 packet exceeding the minimum IPv6 MTU>       ...  |
    /// |                                                               |  ↓
    /// +---------------------------------------------------------------+  -
    /// ```
    ///
    /// # RFC 4443 Description
    ///
    /// A Packet Too Big MUST be sent by a router in response to a packet
    /// that it cannot forward because the packet is larger than the MTU of
    /// the outgoing link.  The information in this message is used as part
    /// of the Path MTU Discovery process.
    PacketTooBig {
        /// The Maximum Transmission Unit of the next-hop link.
        mtu: u32,
    },

    /// Generated when a datagram had to be discarded due to the hop limit field
    /// reaching zero.
    ///
    /// # What is part of the header for `Icmpv6Type::TimeExceeded`?
    ///
    /// For the `Icmpv6Type::TimeExceeded` type the first 8 bytes/octets of the ICMPv6
    /// packet are part of the header. The `unused` part is not stored and droped.
    /// The offending packet is stored in the payload part of the packet
    /// ([`Icmpv6Slice::payload`] & [`PacketHeaders::payload`]) and is not part of
    /// the [`Icmpv6Header`].
    ///
    /// ```text
    /// 0               1               2               3               4
    /// +---------------------------------------------------------------+  -
    /// |       3       | [value as u8] |  checksum (in Icmpv6Header)   |  |
    /// +---------------------------------------------------------------+  | part of header & type
    /// |                           <unused>                            |  ↓
    /// +---------------------------------------------------------------+  -
    /// |                                                               |  |
    /// |     <As much of invoking packet as possible without           |  | part of payload
    /// ...   the ICMPv6 packet exceeding the minimum IPv6 MTU>       ...  |
    /// |                                                               |  ↓
    /// +---------------------------------------------------------------+  -
    /// ```
    ///
    /// # RFC 4443 Description
    ///
    /// If a router receives a packet with a Hop Limit of zero, or if a
    /// router decrements a packet's Hop Limit to zero, it MUST discard the
    /// packet and originate an ICMPv6 Time Exceeded message with Code 0 to
    /// the source of the packet.  This indicates either a routing loop or
    /// too small an initial Hop Limit value.
    ///
    /// An ICMPv6 Time Exceeded message with Code 1 is used to report
    /// fragment reassembly timeout, as specified in [IPv6, Section 4.5].
    TimeExceeded(icmpv6::TimeExceededCode),

    /// Sent if there is a problem with a parameter in a received packet.
    ///
    /// # What is part of the header for `Icmpv6Type::ParameterProblem`?
    ///
    /// For the `Icmpv6Type::ParameterProblem` type the first 8 bytes/octets of the ICMPv6
    /// packet are part of the header. The `unused` part is not stored and droped.
    /// The offending packet is stored in the payload part of the packet
    /// ([`Icmpv6Slice::payload`] & [`PacketHeaders::payload`]) and is not part of
    /// the [`Icmpv6Header`].
    ///
    /// ```text
    /// 0               1               2               3               4
    /// +---------------------------------------------------------------+  -
    /// |       4       | [value].code  |  checksum (in Icmpv6Header)   |  |
    /// +---------------------------------------------------------------+  | part of header & type
    /// |                        [value].pointer                        |  ↓
    /// +---------------------------------------------------------------+  -
    /// |                                                               |  |
    /// |     <As much of invoking packet as possible without           |  | part of payload
    /// ...   the ICMPv6 packet exceeding the minimum IPv6 MTU>       ...  |
    /// |                                                               |  ↓
    /// +---------------------------------------------------------------+  -
    /// ```
    ///
    /// # RFC 4443 Description
    ///
    /// If an IPv6 node processing a packet finds a problem with a field in
    /// the IPv6 header or extension headers such that it cannot complete
    /// processing the packet, it MUST discard the packet and SHOULD
    /// originate an ICMPv6 Parameter Problem message to the packet's source,
    /// indicating the type and location of the problem.
    ParameterProblem(icmpv6::ParameterProblemHeader),

    /// Requesting an `EchoReply` from the receiver.
    ///
    /// # What is part of the header for `Icmpv6Type::EchoRequest`?
    ///
    /// For the [`Icmpv6Type::EchoRequest`] type the first 8 bytes/octets of the
    /// ICMPv6 packet are part of the header. This includes the `id` and `seq`
    /// fields. The data part of the ICMP echo request packet is part of the payload
    /// ([`Icmpv6Slice::payload`] & [`PacketHeaders::payload`]) and not part of the
    /// [`Icmpv6Header`].
    ///
    /// ```text
    /// 0               1               2               3               4
    /// +---------------------------------------------------------------+  -
    /// |      128      |       0       |  checksum (in Icmpv6Header)   |  |
    /// +---------------------------------------------------------------+  | part of header & type
    /// |          [value].id           |         [value].seq           |  ↓
    /// +---------------------------------------------------------------+  -
    /// |                                                               |  |
    /// ...                          <data>                           ...  | part of payload
    /// |                                                               |  ↓
    /// +---------------------------------------------------------------+  -
    /// ```
    ///
    /// # RFC 4443 Description
    ///
    /// Every node MUST implement an ICMPv6 Echo responder function that
    /// receives Echo Requests and originates corresponding Echo Replies.  A
    /// node SHOULD also implement an application-layer interface for
    /// originating Echo Requests and receiving Echo Replies, for diagnostic
    /// purposes.
    EchoRequest(IcmpEchoHeader),
    /// Response to an `EchoRequest` message.
    ///
    /// # What is part of the header for `Icmpv6Type::EchoReply`?
    ///
    /// For the [`Icmpv6Type::EchoReply`] type the first 8 bytes/octets of the
    /// ICMPv6 packet are part of the header. This includes the `id` and `seq`
    /// fields. The data part of the ICMP echo request packet is part of the payload
    /// ([`Icmpv6Slice::payload`] & [`PacketHeaders::payload`]) and not part of the
    /// [`Icmpv6Header`].
    ///
    /// ```text
    /// 0               1               2               3               4
    /// +---------------------------------------------------------------+  -
    /// |      129      |       0       |  checksum (in Icmpv6Header)   |  |
    /// +---------------------------------------------------------------+  | part of header & type
    /// |          [value].id           |         [value].seq           |  ↓
    /// +---------------------------------------------------------------+  -
    /// |                                                               |  |
    /// ...                          <data>                           ...  | part of payload
    /// |                                                               |  ↓
    /// +---------------------------------------------------------------+  -
    /// ```
    ///
    /// # RFC 4443 Description
    ///
    /// Every node MUST implement an ICMPv6 Echo responder function that
    /// receives Echo Requests and originates corresponding Echo Replies. A
    /// node SHOULD also implement an application-layer interface for
    /// originating Echo Requests and receiving Echo Replies, for diagnostic
    /// purposes.
    ///
    /// The source address of an Echo Reply sent in response to a unicast
    /// Echo Request message MUST be the same as the destination address of
    /// that Echo Request message.
    ///
    /// An Echo Reply SHOULD be sent in response to an Echo Request message
    /// sent to an IPv6 multicast or anycast address.  In this case, the
    /// source address of the reply MUST be a unicast address belonging to
    /// the interface on which the Echo Request message was received.
    ///
    /// The data received in the ICMPv6 Echo Request message MUST be returned
    /// entirely and unmodified in the ICMPv6 Echo Reply message.
    EchoReply(IcmpEchoHeader),
}

impl Icmpv6Type {

    /// Returns the type value (first byte of the ICMPv6 header) of this type.
    #[inline]
    pub fn type_u8(&self) -> u8 {
        use Icmpv6Type::*;
        match self {
            Unknown {
                type_u8,
                code_u8: _,
                bytes5to8: _,
            } => *type_u8,
            DestinationUnreachable(_) => TYPE_DST_UNREACH,
            PacketTooBig { mtu: _ } => TYPE_PACKET_TOO_BIG,
            TimeExceeded(_) => TYPE_TIME_EXCEEDED,
            ParameterProblem(_) => TYPE_PARAMETER_PROBLEM,
            EchoRequest(_) => TYPE_ECHO_REQUEST,
            EchoReply(_) => TYPE_ECHO_REPLY,
        }
    }

    /// Returns the code value (second byte of the ICMPv6 header) of this type.
    #[inline]
    pub fn code_u8(&self) -> u8 {
        use Icmpv6Type::*;
        match self {
            Unknown {
                type_u8: _,
                code_u8,
                bytes5to8: _,
            } => *code_u8,
            DestinationUnreachable(code) => code.code_u8(),
            PacketTooBig { mtu: _ } => 0,
            TimeExceeded(code) => code.code_u8(),
            ParameterProblem(header) => header.code.code_u8(),
            EchoRequest(_) => 0,
            EchoReply(_) => 0,
        }
    }

    /// Calculates the checksum of the ICMPv6 header.
    ///
    /// <p style="background:rgba(255,181,77,0.16);padding:0.75em;">
    /// <strong>Warning:</strong> Don't use this method to verfy if a checksum of a
    /// received packet is correct. This method assumes that all unused bytes are
    /// filled with zeros. If this is not the case the computed checksum value will
    /// will be incorrect for a received packet.
    ///
    /// If you want to verify that a received packet has a correct checksum use
    /// [`Icmpv6Slice::is_checksum_valid`] instead.
    /// </p>
    pub fn calc_checksum(
        &self,
        source_ip: [u8; 16],
        destination_ip: [u8; 16],
        payload: &[u8],
    ) -> Result<u16, ValueError> {
        // check that the total length fits into the field
        //
        // Note according to RFC 2460 the "Upper-Layer Packet Length" used
        // in the checksum calculation, for protocols that don't contain
        // their own length information (like ICMPv6), is "the Payload Length
        // from the IPv6 header, minus the length of any extension headers present
        // between the IPv6 header and the upper-layer header."
        let max_payload_len: usize = (std::u32::MAX as usize) - self.header_len();
        if max_payload_len < payload.len() {
            return Err(ValueError::Ipv6PayloadLengthTooLarge(payload.len()));
        }

        let msg_len = payload.len() + self.header_len();

        // calculate the checksum
        // NOTE: rfc4443 section 2.3 - Icmp6 *does* use a pseudoheader,
        // unlike Icmp4
        let pseudo_sum = checksum::Sum16BitWords::new()
            .add_16bytes(source_ip)
            .add_16bytes(destination_ip)
            .add_2bytes([0, ip_number::IPV6_ICMP])
            .add_4bytes((msg_len as u32).to_be_bytes());

        use Icmpv6Type::*;
        Ok(
            match self {
                Unknown {
                    type_u8,
                    code_u8,
                    bytes5to8,
                } => {
                    pseudo_sum.add_2bytes([*type_u8, *code_u8])
                    .add_4bytes(*bytes5to8)
                },
                DestinationUnreachable(header) => {
                    pseudo_sum.add_2bytes([TYPE_DST_UNREACH, header.code_u8()])
                },
                PacketTooBig { mtu } => {
                    pseudo_sum.add_2bytes([TYPE_PACKET_TOO_BIG, 0])
                    .add_4bytes(mtu.to_be_bytes())
                },
                TimeExceeded(code) => {
                    pseudo_sum.add_2bytes([TYPE_TIME_EXCEEDED, code.code_u8()])
                },
                ParameterProblem(header) => {
                    pseudo_sum.add_2bytes([TYPE_PARAMETER_PROBLEM, header.code.code_u8()])
                    .add_4bytes(header.pointer.to_be_bytes())
                }
                EchoRequest(echo) => {
                    pseudo_sum.add_2bytes([TYPE_ECHO_REQUEST, 0])
                    .add_4bytes(echo.to_bytes())
                }
                EchoReply(echo) => {
                    pseudo_sum.add_2bytes([TYPE_ECHO_REPLY, 0])
                    .add_4bytes(echo.to_bytes())
                }
            }
            .add_slice(payload)
            .ones_complement()
            .to_be()
        )
    }

    /// Creates a header with the correct checksum.
    pub fn to_header(
        self,
        source_ip: [u8; 16],
        destination_ip: [u8; 16],
        payload: &[u8],
    ) -> Result<Icmpv6Header, ValueError> {
        Ok(Icmpv6Header {
            checksum: self.calc_checksum(source_ip, destination_ip, payload)?,
            icmp_type: self,
        })
    }

    /// Serialized length of the header in bytes/octets.
    ///
    /// Note that this size is not the size of the entire
    /// ICMPv6 packet but only the header.
    pub fn header_len(&self) -> usize {
        use Icmpv6Type::*;
        match self {
            Unknown {
                type_u8: _,
                code_u8: _,
                bytes5to8: _,
            }
            | DestinationUnreachable(_)
            | PacketTooBig{ mtu: _ }
            | TimeExceeded(_)
            | ParameterProblem(_)
            | EchoRequest(_)
            | EchoReply(_) => 8,
        }
    }

    /// If the ICMP type has a fixed size returns the number of
    /// bytes that should be present after the header of this type.
    #[inline]
    pub fn fixed_payload_size(&self) -> Option<usize> {
        use Icmpv6Type::*;
        match self {
            Unknown {
                type_u8: _,
                code_u8: _,
                bytes5to8: _,
            }
            | DestinationUnreachable(_)
            | PacketTooBig{ mtu: _ }
            | TimeExceeded(_)
            | ParameterProblem(_)
            | EchoRequest(_)
            | EchoReply(_) => None,
        }
    }
}

/// The statically sized data at the start of an ICMPv6 packet (at least the first 8 bytes of an ICMPv6 packet).
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Icmpv6Header {
    /// Type & type specific values & code.
    pub icmp_type: Icmpv6Type,
    /// Checksum in the ICMPv6 header.
    pub checksum: u16,
}

impl Icmpv6Header {
    /// Minimum number of bytes an ICMP header needs to have.
    ///
    /// Note that minimum size can be larger depending on
    /// the type and code.
    pub const MIN_SERIALIZED_SIZE: usize = 8;

    /// Maximum number of bytes/octets an Icmpv6Header takes up
    /// in serialized form.
    ///
    /// Currently this number is determined by the biggest
    /// planned ICMPv6 header type, which is currently the
    /// "Neighbor Discovery Protocol" "Redirect" message.
    pub const MAX_SERIALIZED_SIZE: usize = 8 + 16 + 16;

    /// Setups a new header with the checksum beeing set to 0.
    #[inline]
    pub fn new(icmp_type: Icmpv6Type) -> Icmpv6Header {
        Icmpv6Header {
            icmp_type,
            checksum: 0, // will be filled in later
        }
    }

    /// Creates a [`Icmpv6Header`] with a checksum calculated based
    /// on the given payload & ip addresses from the IPv6 header.
    pub fn with_checksum(
        icmp_type: Icmpv6Type,
        source_ip: [u8; 16],
        destination_ip: [u8; 16],
        payload: &[u8],
    ) -> Result<Icmpv6Header, ValueError> {
        let checksum = icmp_type.calc_checksum(source_ip, destination_ip, payload)?;
        Ok(Icmpv6Header {
            icmp_type,
            checksum,
        })
    }

    /// Reads an icmp6 header from a slice directly and returns a tuple
    /// containing the resulting header & unused part of the slice.
    #[inline]
    pub fn from_slice(slice: &[u8]) -> Result<(Icmpv6Header, &[u8]), ReadError> {
        let header = Icmpv6Slice::from_slice(slice)?.header();
        let len = header.header_len();
        Ok((header, &slice[len..]))
    }

    /// Read a ICMPv6 header from the given reader
    pub fn read<T: io::Read + Sized>(reader: &mut T) -> Result<Icmpv6Header, ReadError> {
        // read the initial 8 bytes
        let mut start = [0u8;8];
        reader.read_exact(&mut start)?;
        Ok(Icmpv6Slice{
            slice: &start
        }.header())
    }

    /// Write the ICMPv6 header to the given writer.
    pub fn write<T: io::Write + Sized>(&self, writer: &mut T) -> Result<(), WriteError> {
        writer.write_all(&self.to_bytes()).map_err(WriteError::from)
    }

    /// Serialized length of the header in bytes/octets.
    ///
    /// Note that this size is not the size of the entire
    /// ICMPv6 packet but only the header.
    #[inline]
    pub fn header_len(&self) -> usize {
        self.icmp_type.header_len()
    }

    /// If the ICMP type has a fixed size returns the number of
    /// bytes that should be present after the header of this type.
    #[inline]
    pub fn fixed_payload_size(&self) -> Option<usize> {
        self.icmp_type.fixed_payload_size()
    }

    /// Updates the checksum of the header.
    pub fn update_checksum(
        &mut self,
        source_ip: [u8; 16],
        destination_ip: [u8; 16],
        payload: &[u8],
    ) -> Result<(), ValueError> {
        self.checksum = self
            .icmp_type
            .calc_checksum(source_ip, destination_ip, payload)?;
        Ok(())
    }

    /// Returns the header on the wire bytes.
    #[inline]
    pub fn to_bytes(&self) -> ArrayVec<u8, { Icmpv6Header::MAX_SERIALIZED_SIZE }> {

        let checksum_be = self.checksum.to_be_bytes();

        let return_trivial = |type_u8: u8, code_u8: u8| -> ArrayVec<u8, { Icmpv6Header::MAX_SERIALIZED_SIZE }> {
            #[rustfmt::skip]
            let mut re = ArrayVec::from([
                type_u8, code_u8, checksum_be[0], checksum_be[1],
                0, 0, 0, 0,

                0, 0, 0, 0,
                0, 0, 0, 0,
                0, 0, 0, 0,
                0, 0, 0, 0,

                0, 0, 0, 0,
                0, 0, 0, 0,
                0, 0, 0, 0,
                0, 0, 0, 0,
            ]);
            // SAFETY: Safe as u8 has no destruction behavior and as 8 is smaller then 20.
            unsafe {
                re.set_len(8);
            }
            re
        };

        let return_4u8 = |type_u8: u8, code_u8: u8, bytes5to8: [u8;4]| -> ArrayVec<u8, { Icmpv6Header::MAX_SERIALIZED_SIZE }> {
            #[rustfmt::skip]
            let mut re = ArrayVec::from([
                type_u8, code_u8, checksum_be[0], checksum_be[1],
                bytes5to8[0], bytes5to8[1], bytes5to8[2], bytes5to8[3],

                0, 0, 0, 0,
                0, 0, 0, 0,
                0, 0, 0, 0,
                0, 0, 0, 0,

                0, 0, 0, 0,
                0, 0, 0, 0,
                0, 0, 0, 0,
                0, 0, 0, 0,
            ]);
            // SAFETY: Safe as u8 has no destruction behavior and as 8 is smaller then 20.
            unsafe {
                re.set_len(8);
            }
            re
        };

        use Icmpv6Type::*;
        match self.icmp_type {
            Unknown {
                type_u8,
                code_u8,
                bytes5to8,
            } => {
                return_4u8(type_u8, code_u8, bytes5to8)
            },
            DestinationUnreachable(header) => {
                return_trivial(TYPE_DST_UNREACH, header.code_u8())
            },
            PacketTooBig { mtu } => {
                return_4u8(TYPE_PACKET_TOO_BIG, 0, mtu.to_be_bytes())
            },
            TimeExceeded(code) => {
                return_trivial(TYPE_TIME_EXCEEDED, code.code_u8())
            },
            ParameterProblem(header) => {
                return_4u8(TYPE_PARAMETER_PROBLEM, header.code.code_u8(), header.pointer.to_be_bytes())
            }
            EchoRequest(echo) => {
                return_4u8(TYPE_ECHO_REQUEST, 0, echo.to_bytes())
            },
            EchoReply(echo) => {
                return_4u8(TYPE_ECHO_REPLY, 0, echo.to_bytes())
            },
        }
    }
}

/// A slice containing an ICMPv6 network package.
///
/// Struct allows the selective read of fields in the ICMPv6
/// packet.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Icmpv6Slice<'a> {
    slice: &'a [u8],
}

impl<'a> Icmpv6Slice<'a> {
    /// Creates a slice containing an ICMPv6 packet.
    ///
    /// # Errors
    ///
    /// The function will return an `Err` `ReadError::UnexpectedEndOfSlice`
    /// if the given slice is too small (smaller then `Icmpv6Header::MIN_SERIALIZED_SIZE`) or
    /// too large (bigger then `icmpv6::MAX_ICMPV6_BYTE_LEN`).
    #[inline]
    pub fn from_slice(slice: &'a [u8]) -> Result<Icmpv6Slice<'a>, ReadError> {
        //check length
        use crate::ReadError::*;
        if slice.len() < Icmpv6Header::MIN_SERIALIZED_SIZE {
            return Err(UnexpectedEndOfSlice(Icmpv6Header::MIN_SERIALIZED_SIZE));
        }
        if slice.len() > icmpv6::MAX_ICMPV6_BYTE_LEN {
            return Err(Icmpv6PacketTooBig(slice.len()));
        }

        //done
        Ok(Icmpv6Slice { slice })
    }

    /// Decode the header fields and copy the results to a [`Icmpv6Header`] struct.
    #[inline]
    pub fn header(&self) -> Icmpv6Header {
        Icmpv6Header {
            icmp_type: self.icmp_type(),
            checksum: self.checksum(),
        }
    }

    /// Number of bytes/octets that will be converted into a
    /// [`Icmpv6Header`] when [`Icmpv6Slice::header`] gets called.
    #[inline]
    pub fn header_len(&self) -> usize {
        8
    }

    /// Decode the header values (excluding the checksum) into an [`Icmpv6Type`] enum.
    pub fn icmp_type(&self) -> Icmpv6Type {
        use Icmpv6Type::*;

        match self.type_u8() {
            TYPE_DST_UNREACH => {
                if let Some(code) = DestUnreachableCode::from_u8(self.code_u8()) {
                    return DestinationUnreachable(code);
                }
            }
            TYPE_PACKET_TOO_BIG => {
                if 0 == self.code_u8() {
                    return PacketTooBig {
                        mtu: u32::from_be_bytes(self.bytes5to8()),
                    };
                }
            }
            TYPE_TIME_EXCEEDED => {
                if let Some(code) = TimeExceededCode::from_u8(self.code_u8()) {
                    return TimeExceeded(code);
                }
            }
            TYPE_PARAMETER_PROBLEM => {
                if let Some(code) = ParameterProblemCode::from_u8(self.code_u8()) {
                    return ParameterProblem(
                        ParameterProblemHeader{
                            code,
                            pointer: u32::from_be_bytes(self.bytes5to8())
                        }
                    );
                }
            }
            TYPE_ECHO_REQUEST => {
                if 0 == self.code_u8() {
                    return EchoRequest(IcmpEchoHeader::from_bytes(self.bytes5to8()));
                }
            }
            TYPE_ECHO_REPLY => {
                if 0 == self.code_u8() {
                    return EchoReply(IcmpEchoHeader::from_bytes(self.bytes5to8()));
                }
            }
            _ => {}
        }
        Unknown {
            type_u8: self.type_u8(),
            code_u8: self.code_u8(),
            bytes5to8: self.bytes5to8(),
        }
    }

    /// Returns "type" value in the ICMPv6 header.
    #[inline]
    pub fn type_u8(&self) -> u8 {
        // SAFETY:
        // Safe as the contructor checks that the slice has
        // at least the length of Icmpv6Header::MIN_SERIALIZED_SIZE (8).
        unsafe { *self.slice.get_unchecked(0) }
    }

    /// Returns "code" value in the ICMPv6 header.
    #[inline]
    pub fn code_u8(&self) -> u8 {
        // SAFETY:
        // Safe as the contructor checks that the slice has
        // at least the length of Icmpv6Header::MIN_SERIALIZED_SIZE (8).
        unsafe { *self.slice.get_unchecked(1) }
    }

    /// Returns "checksum" value in the ICMPv6 header.
    #[inline]
    pub fn checksum(&self) -> u16 {
        // SAFETY:
        // Safe as the contructor checks that the slice has
        // at least the length of Icmpv6Header::MIN_SERIALIZED_SIZE  (8).
        unsafe { get_unchecked_be_u16(self.slice.as_ptr().add(2)) }
    }

    /// Returns if the checksum in the slice is correct.
    pub fn is_checksum_valid(&self, source_ip: [u8; 16], destination_ip: [u8; 16]) -> bool {
        // NOTE: rfc4443 section 2.3 - Icmp6 *does* use a pseudoheader,
        // unlike Icmp4
        checksum::Sum16BitWords::new()
            .add_16bytes(source_ip)
            .add_16bytes(destination_ip)
            .add_4bytes((self.slice().len() as u32).to_be_bytes())
            .add_2bytes([0, ip_number::IPV6_ICMP])
            // NOTE: From RFC 1071
            // To check a checksum, the 1's complement sum is computed over the
            // same set of octets, including the checksum field.  If the result
            // is all 1 bits (-0 in 1's complement arithmetic), the check
            // succeeds.
            .add_slice(self.slice)
            .ones_complement()
            == 0
    }

    /// Returns the bytes from position 4 till and including the 8th position
    /// in the ICMPv6 header.
    ///
    /// These bytes located at th 5th, 6th, 7th and 8th position of the ICMP
    /// packet can depending on the ICMPv6 type and code contain additional data.
    #[inline]
    pub fn bytes5to8(&self) -> [u8; 4] {
        // SAFETY:
        // Safe as the contructor checks that the slice has
        // at least the length of Icmpv6Header::MIN_SERIALIZED_SIZE  (8).
        unsafe {
            [
                *self.slice.get_unchecked(4),
                *self.slice.get_unchecked(5),
                *self.slice.get_unchecked(6),
                *self.slice.get_unchecked(7),
            ]
        }
    }

    /// Returns the slice containing the ICMPv6 packet.
    #[inline]
    pub fn slice(&self) -> &'a [u8] {
        self.slice
    }

    /// Returns a slice to the bytes not covered by `.header()`.
    #[inline]
    pub fn payload(&self) -> &'a [u8] {
        // SAFETY:
        // Safe as the contructor checks that the slice has
        // at least the length of Icmpv6Header::MIN_SERIALIZED_SIZE(8).
        unsafe { from_raw_parts(self.slice.as_ptr().add(8), self.slice.len() - 8) }
    }
}
