Преглед на файлове

Add ICMPv6

Add support for ICMPv6 message types to the wire module.
Dan Robertson преди 7 години
родител
ревизия
783a76b2d8
променени са 4 файла, в които са добавени 696 реда и са изтрити 2 реда
  1. 6 0
      src/phy/mod.rs
  2. 680 0
      src/wire/icmpv6.rs
  3. 2 2
      src/wire/ip.rs
  4. 8 0
      src/wire/mod.rs

+ 6 - 0
src/phy/mod.rs

@@ -157,7 +157,10 @@ pub struct ChecksumCapabilities {
     pub ipv4: Checksum,
     pub udpv4: Checksum,
     pub tcpv4: Checksum,
+    #[cfg(feature = "proto-ipv4")]
     pub icmpv4: Checksum,
+    #[cfg(feature = "proto-ipv6")]
+    pub icmpv6: Checksum,
     dummy: (),
 }
 
@@ -169,7 +172,10 @@ impl ChecksumCapabilities {
             ipv4: Checksum::None,
             udpv4: Checksum::None,
             tcpv4: Checksum::None,
+            #[cfg(feature = "proto-ipv4")]
             icmpv4: Checksum::None,
+            #[cfg(feature = "proto-ipv6")]
+            icmpv6: Checksum::None,
             ..Self::default()
         }
     }

+ 680 - 0
src/wire/icmpv6.rs

@@ -0,0 +1,680 @@
+use core::{cmp, fmt};
+use byteorder::{ByteOrder, NetworkEndian};
+
+use {Error, Result};
+use phy::ChecksumCapabilities;
+use super::ip::checksum;
+use super::{Ipv6Packet, Ipv6Repr};
+
+enum_with_unknown! {
+    /// Internet protocol control message type.
+    pub doc enum Message(u8) {
+        /// Destination Unreachable.
+        DstUnreachable = 0x01,
+        /// Packet Too Big.
+        PktTooBig      = 0x02,
+        /// Time Exceeded.
+        TimeExceeded   = 0x03,
+        /// Parameter Problem.
+        ParamProblem   = 0x04,
+        /// Echo Request
+        EchoRequest    = 0x80,
+        /// Echo Reply
+        EchoReply      = 0x81
+    }
+}
+
+impl Message {
+    /// Per [RFC 4443 § 2.1] ICMPv6 message types with the highest order
+    /// bit set are informational messages while message types without
+    /// the highest order bit set are error messages.
+    ///
+    /// [RFC 4443 § 2.1]: https://tools.ietf.org/html/rfc4443#section-2.1
+    pub fn is_error(&self) -> bool {
+        (u8::from(*self) & 0x80) != 0x80
+    }
+}
+
+impl fmt::Display for Message {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            &Message::DstUnreachable => write!(f, "destination unreachable"),
+            &Message::PktTooBig      => write!(f, "packet too big"),
+            &Message::TimeExceeded   => write!(f, "time exceeded"),
+            &Message::ParamProblem   => write!(f, "parameter problem"),
+            &Message::EchoReply      => write!(f, "echo reply"),
+            &Message::EchoRequest    => write!(f, "echo request"),
+            &Message::Unknown(id) => write!(f, "{}", id)
+        }
+    }
+}
+
+enum_with_unknown! {
+    /// Internet protocol control message subtype for type "Destination Unreachable".
+    pub doc enum DstUnreachable(u8) {
+        /// No Route to destination.
+        NoRoute         = 0,
+        /// Communication with destination administratively prohibited.
+        AdminProhibit   = 1,
+        /// Beyond scope of source address.
+        BeyondScope     = 2,
+        /// Address unreachable.
+        AddrUnreachable = 3,
+        /// Port unreachable.
+        PortUnreachable = 4,
+        /// Source address failed ingress/egress policy.
+        FailedPolicy    = 5,
+        /// Reject route to destination.
+        RejectRoute     = 6
+    }
+}
+
+
+impl fmt::Display for DstUnreachable {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            &DstUnreachable::NoRoute =>
+                write!(f, "no route to destination"),
+            &DstUnreachable::AdminProhibit =>
+                write!(f, "communication with destination administratively prohibited"),
+            &DstUnreachable::BeyondScope =>
+                write!(f, "beyond scope of source address"),
+            &DstUnreachable::AddrUnreachable =>
+                write!(f, "address unreachable"),
+            &DstUnreachable::PortUnreachable =>
+                write!(f, "port unreachable"),
+            &DstUnreachable::FailedPolicy =>
+                write!(f, "source address failed ingress/egress policy"),
+            &DstUnreachable::RejectRoute =>
+                write!(f, "reject route to destination"),
+            &DstUnreachable::Unknown(id) =>
+                write!(f, "{}", id)
+        }
+    }
+}
+
+/// A read/write wrapper around an Internet Control Message Protocol version 6 packet buffer.
+#[derive(Debug, PartialEq)]
+pub struct Packet<T: AsRef<[u8]>> {
+    buffer: T
+}
+
+// Ranges and constants describing key boundaries in the ICMPv6 header.
+// See https://tools.ietf.org/html/rfc4443 for details.
+mod field {
+    use wire::field::*;
+
+    pub const TYPE:       usize = 0;
+    pub const CODE:       usize = 1;
+    pub const CHECKSUM:   Field = 2..4;
+
+    pub const UNUSED:     Field = 4..8;
+    pub const MTU:        Field = 4..8;
+    pub const POINTER:    Field = 4..8;
+    pub const ECHO_IDENT: Field = 4..6;
+    pub const ECHO_SEQNO: Field = 6..8;
+
+    pub const HEADER_END: usize = 8;
+}
+
+impl<T: AsRef<[u8]>> Packet<T> {
+    /// Imbue a raw octet buffer with ICMPv6 packet structure.
+    pub fn new(buffer: T) -> Packet<T> {
+        Packet { buffer }
+    }
+
+    /// Shorthand for a combination of [new] and [check_len].
+    ///
+    /// [new]: #method.new
+    /// [check_len]: #method.check_len
+    pub fn new_checked(buffer: T) -> Result<Packet<T>> {
+        let packet = Self::new(buffer);
+        packet.check_len()?;
+        Ok(packet)
+    }
+
+    /// Ensure that no accessor method will panic if called.
+    /// Returns `Err(Error::Truncated)` if the buffer is too short.
+    pub fn check_len(&self) -> Result<()> {
+        let len = self.buffer.as_ref().len();
+        if len < field::HEADER_END {
+            Err(Error::Truncated)
+        } else {
+            Ok(())
+        }
+    }
+
+    /// Consume the packet, returning the underlying buffer.
+    pub fn into_inner(self) -> T {
+        self.buffer
+    }
+
+    /// Return the message type field.
+    #[inline]
+    pub fn msg_type(&self) -> Message {
+        let data = self.buffer.as_ref();
+        Message::from(data[field::TYPE])
+    }
+
+    /// Return the message code field.
+    #[inline]
+    pub fn msg_code(&self) -> u8 {
+        let data = self.buffer.as_ref();
+        data[field::CODE]
+    }
+
+    /// Return the checksum field.
+    #[inline]
+    pub fn checksum(&self) -> u16 {
+        let data = self.buffer.as_ref();
+        NetworkEndian::read_u16(&data[field::CHECKSUM])
+    }
+
+    /// Return the identifier field (for echo request and reply packets).
+    #[inline]
+    pub fn echo_ident(&self) -> u16 {
+        let data = self.buffer.as_ref();
+        NetworkEndian::read_u16(&data[field::ECHO_IDENT])
+    }
+
+    /// Return the sequence number field (for echo request and reply packets).
+    #[inline]
+    pub fn echo_seq_no(&self) -> u16 {
+        let data = self.buffer.as_ref();
+        NetworkEndian::read_u16(&data[field::ECHO_SEQNO])
+    }
+
+    /// Return the MTU field (for packet too big messages).
+    #[inline]
+    pub fn pkt_too_big_mtu(&self) -> u32 {
+        let data = self.buffer.as_ref();
+        NetworkEndian::read_u32(&data[field::MTU])
+    }
+
+    /// Return the pointer field (for parameter problem messages).
+    #[inline]
+    pub fn param_problem_ptr(&self) -> u32 {
+        let data = self.buffer.as_ref();
+        NetworkEndian::read_u32(&data[field::POINTER])
+    }
+
+    /// Return the header length. The result depends on the value of
+    /// the message type field.
+    pub fn header_len(&self) -> usize {
+        match self.msg_type() {
+            Message::DstUnreachable => field::UNUSED.end,
+            Message::PktTooBig      => field::MTU.end,
+            Message::TimeExceeded   => field::UNUSED.end,
+            Message::ParamProblem   => field::POINTER.end,
+            Message::EchoRequest    => field::ECHO_SEQNO.end,
+            Message::EchoReply      => field::ECHO_SEQNO.end,
+            // For packets that are not included in RFC 4443, do not
+            // include the last 32 bits of the ICMPv6 header in
+            // `header_bytes`. This must be done so that these bytes
+            // can be accessed in the `payload`.
+            _ => field::CHECKSUM.end
+        }
+    }
+
+    /// Validate the header checksum.
+    ///
+    /// # Fuzzing
+    /// This function always returns `true` when fuzzing.
+    pub fn verify_checksum(&self) -> bool {
+        if cfg!(fuzzing) { return true }
+
+        let data = self.buffer.as_ref();
+        checksum::data(data) == !0
+    }
+}
+
+impl<'a, T: AsRef<[u8]> + ?Sized> Packet<&'a T> {
+    /// Return a pointer to the type-specific data.
+    #[inline]
+    pub fn payload(&self) -> &'a [u8] {
+        let data = self.buffer.as_ref();
+        &data[self.header_len()..]
+    }
+}
+
+impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
+    /// Set the message type field.
+    #[inline]
+    pub fn set_msg_type(&mut self, value: Message) {
+        let data = self.buffer.as_mut();
+        data[field::TYPE] = value.into()
+    }
+
+    /// Set the message code field.
+    #[inline]
+    pub fn set_msg_code(&mut self, value: u8) {
+        let data = self.buffer.as_mut();
+        data[field::CODE] = value
+    }
+
+    /// Set the checksum field.
+    #[inline]
+    pub fn set_checksum(&mut self, value: u16) {
+        let data = self.buffer.as_mut();
+        NetworkEndian::write_u16(&mut data[field::CHECKSUM], value)
+    }
+
+    /// Set the identifier field (for echo request and reply packets).
+    ///
+    /// # Panics
+    /// This function may panic if this packet is not an echo request or reply packet.
+    #[inline]
+    pub fn set_echo_ident(&mut self, value: u16) {
+        let data = self.buffer.as_mut();
+        NetworkEndian::write_u16(&mut data[field::ECHO_IDENT], value)
+    }
+
+    /// Set the sequence number field (for echo request and reply packets).
+    ///
+    /// # Panics
+    /// This function may panic if this packet is not an echo request or reply packet.
+    #[inline]
+    pub fn set_echo_seq_no(&mut self, value: u16) {
+        let data = self.buffer.as_mut();
+        NetworkEndian::write_u16(&mut data[field::ECHO_SEQNO], value)
+    }
+
+    /// Set the MTU field (for packet too big messages).
+    ///
+    /// # Panics
+    /// This function may panic if this packet is not an packet too big packet.
+    #[inline]
+    pub fn set_pkt_too_big_mtu(&mut self, value: u32) {
+        let data = self.buffer.as_mut();
+        NetworkEndian::write_u32(&mut data[field::MTU], value)
+    }
+
+    /// Set the pointer field (for parameter problem messages).
+    ///
+    /// # Panics
+    /// This function may panic if this packet
+    #[inline]
+    pub fn set_param_problem_ptr(&mut self, value: u32) {
+        let data = self.buffer.as_mut();
+        NetworkEndian::write_u32(&mut data[field::POINTER], value)
+    }
+
+    /// Compute and fill in the header checksum.
+    pub fn fill_checksum(&mut self) {
+        self.set_checksum(0);
+        let checksum = {
+            let data = self.buffer.as_ref();
+            !checksum::data(data)
+        };
+        self.set_checksum(checksum)
+    }
+}
+
+impl<'a, T: AsRef<[u8]> + AsMut<[u8]> + ?Sized> Packet<&'a mut T> {
+    /// Return a mutable pointer to the type-specific data.
+    #[inline]
+    pub fn payload_mut(&mut self) -> &mut [u8] {
+        let range = self.header_len()..;
+        let data = self.buffer.as_mut();
+        &mut data[range]
+    }
+}
+
+/// A high-level representation of an Internet Control Message Protocol version 6 packet header.
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub enum Repr<'a> {
+    DstUnreachable {
+        reason: DstUnreachable,
+        header: Ipv6Repr,
+        data:   &'a [u8]
+    },
+    PktTooBig {
+        mtu: u32,
+        header: Ipv6Repr,
+        data:   &'a [u8]
+    },
+    TimeExceeded {
+        header: Ipv6Repr,
+        data:   &'a [u8]
+    },
+    ParamProblem {
+        pointer: u32,
+        header:  Ipv6Repr,
+        data:    &'a [u8]
+    },
+    EchoRequest {
+        ident:  u16,
+        seq_no: u16,
+        data:   &'a [u8]
+    },
+    EchoReply {
+        ident:  u16,
+        seq_no: u16,
+        data:   &'a [u8]
+    },
+    #[doc(hidden)]
+    __Nonexhaustive
+}
+
+impl<'a> Repr<'a> {
+    /// Parse an Internet Control Message Protocol version 6 packet and return
+    /// a high-level representation.
+    pub fn parse<T>(packet: &Packet<&'a T>, checksum_caps: &ChecksumCapabilities)
+                   -> Result<Repr<'a>>
+                where T: AsRef<[u8]> + ?Sized {
+        fn create_packet_from_payload<'a, T>(packet: &Packet<&'a T>)
+                                            -> Result<(&'a [u8], Ipv6Repr)>
+                where T: AsRef<[u8]> + ?Sized {
+            let ip_packet = Ipv6Packet::new_checked(packet.payload())?;
+
+            let payload = &packet.payload()[ip_packet.header_len() as usize..];
+            if payload.len() < 8 { return Err(Error::Truncated) }
+            let repr = Ipv6Repr {
+                src_addr: ip_packet.src_addr(),
+                dst_addr: ip_packet.dst_addr(),
+                next_header: ip_packet.next_header(),
+                payload_len: payload.len(),
+                hop_limit: ip_packet.hop_limit()
+            };
+            Ok((payload, repr))
+        }
+        // Valid checksum is expected.
+        if checksum_caps.icmpv6.rx() && !packet.verify_checksum() { return Err(Error::Checksum) }
+
+        match (packet.msg_type(), packet.msg_code()) {
+            (Message::DstUnreachable, code) => {
+                let (payload, repr) = create_packet_from_payload(packet)?;
+                Ok(Repr::DstUnreachable {
+                    reason: DstUnreachable::from(code),
+                    header: repr,
+                    data: payload
+                })
+            },
+            (Message::PktTooBig, 0) => {
+                let (payload, repr) = create_packet_from_payload(packet)?;
+                Ok(Repr::PktTooBig {
+                    mtu: packet.pkt_too_big_mtu(),
+                    header: repr,
+                    data: payload
+                })
+            },
+            (Message::TimeExceeded, 0) => {
+                let (payload, repr) = create_packet_from_payload(packet)?;
+                Ok(Repr::TimeExceeded {
+                    header: repr,
+                    data: payload
+                })
+            },
+            (Message::ParamProblem, 0) => {
+                let (payload, repr) = create_packet_from_payload(packet)?;
+                Ok(Repr::ParamProblem {
+                    pointer: packet.param_problem_ptr(),
+                    header: repr,
+                    data: payload
+                })
+            },
+            (Message::EchoRequest, 0) => {
+                Ok(Repr::EchoRequest {
+                    ident:  packet.echo_ident(),
+                    seq_no: packet.echo_seq_no(),
+                    data:   packet.payload()
+                })
+            },
+            (Message::EchoReply, 0) => {
+                Ok(Repr::EchoReply {
+                    ident:  packet.echo_ident(),
+                    seq_no: packet.echo_seq_no(),
+                    data:   packet.payload()
+                })
+            },
+            _ => Err(Error::Unrecognized)
+        }
+    }
+
+    /// Return the length of a packet that will be emitted from this high-level representation.
+    pub fn buffer_len(&self) -> usize {
+        match self {
+            &Repr::DstUnreachable { header, data, .. } | &Repr::PktTooBig { header, data, .. } |
+            &Repr::TimeExceeded { header, data, .. } | &Repr::ParamProblem { header, data, .. } => {
+                field::UNUSED.end + header.buffer_len() + data.len()
+            }
+            &Repr::EchoRequest { data, .. } |
+            &Repr::EchoReply { data, .. } => {
+                field::ECHO_SEQNO.end + data.len()
+            },
+            &Repr::__Nonexhaustive => unreachable!()
+        }
+    }
+
+    /// Emit a high-level representation into an Internet Control Message Protocol version 6
+    /// packet.
+    pub fn emit<T>(&self, packet: &mut Packet<&mut T>, checksum_caps: &ChecksumCapabilities)
+            where T: AsRef<[u8]> + AsMut<[u8]> + ?Sized {
+        fn emit_contained_packet(buffer: &mut [u8], header: Ipv6Repr, data: &[u8]) {
+            let mut ip_packet = Ipv6Packet::new(buffer);
+            header.emit(&mut ip_packet);
+            let payload = &mut ip_packet.into_inner()[header.buffer_len()..];
+            payload.copy_from_slice(&data[..]);
+        }
+
+        match self {
+            &Repr::DstUnreachable { reason, header, data } => {
+                packet.set_msg_type(Message::DstUnreachable);
+                packet.set_msg_code(reason.into());
+
+                emit_contained_packet(packet.payload_mut(), header, &data);
+            },
+
+            &Repr::PktTooBig { mtu, header, data } => {
+                packet.set_msg_type(Message::PktTooBig);
+                packet.set_msg_code(0);
+                packet.set_pkt_too_big_mtu(mtu);
+
+                emit_contained_packet(packet.payload_mut(), header, &data);
+            },
+
+            &Repr::TimeExceeded { header, data } => {
+                packet.set_msg_type(Message::TimeExceeded);
+                packet.set_msg_code(0);
+
+                emit_contained_packet(packet.payload_mut(), header, &data);
+            },
+
+            &Repr::ParamProblem { pointer, header, data } => {
+                packet.set_msg_type(Message::ParamProblem);
+                packet.set_msg_code(0);
+                packet.set_param_problem_ptr(pointer);
+
+                emit_contained_packet(packet.payload_mut(), header, &data);
+            },
+
+            &Repr::EchoRequest { ident, seq_no, data } => {
+                packet.set_msg_type(Message::EchoRequest);
+                packet.set_msg_code(0);
+                packet.set_echo_ident(ident);
+                packet.set_echo_seq_no(seq_no);
+                let data_len = cmp::min(packet.payload_mut().len(), data.len());
+                packet.payload_mut()[..data_len].copy_from_slice(&data[..data_len])
+            },
+
+            &Repr::EchoReply { ident, seq_no, data } => {
+                packet.set_msg_type(Message::EchoReply);
+                packet.set_msg_code(0);
+                packet.set_echo_ident(ident);
+                packet.set_echo_seq_no(seq_no);
+                let data_len = cmp::min(packet.payload_mut().len(), data.len());
+                packet.payload_mut()[..data_len].copy_from_slice(&data[..data_len])
+            },
+
+            &Repr::__Nonexhaustive => unreachable!(),
+        }
+
+        if checksum_caps.icmpv6.tx() {
+            packet.fill_checksum()
+        } else {
+            // make sure we get a consistently zeroed checksum, since implementations might rely on it
+            packet.set_checksum(0);
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use wire::{Ipv6Address, Ipv6Repr, IpProtocol};
+    use super::*;
+
+    static ECHO_PACKET_BYTES: [u8; 12] =
+        [0x80, 0x00, 0x16, 0xfe,
+         0x12, 0x34, 0xab, 0xcd,
+         0xaa, 0x00, 0x00, 0xff];
+
+    static ECHO_PACKET_PAYLOAD: [u8; 4] =
+        [0xaa, 0x00, 0x00, 0xff];
+
+    static PKT_TOO_BIG_BYTES: [u8; 60] =
+        [0x02, 0x00, 0x0d, 0x44,
+         0x00, 0x00, 0x05, 0xdc,
+         0x60, 0x00, 0x00, 0x00,
+         0x00, 0x0c, 0x11, 0x40,
+         0xfe, 0x80, 0x00, 0x00,
+         0x00, 0x00, 0x00, 0x00,
+         0x00, 0x00, 0x00, 0x00,
+         0x00, 0x00, 0x00, 0x01,
+         0xfe, 0x80, 0x00, 0x00,
+         0x00, 0x00, 0x00, 0x00,
+         0x00, 0x00, 0x00, 0x00,
+         0x00, 0x00, 0x00, 0x02,
+         0xbf, 0x00, 0x00, 0x35,
+         0x00, 0x0c, 0x12, 0x4d,
+         0xaa, 0x00, 0x00, 0xff];
+
+    static PKT_TOO_BIG_IP_PAYLOAD: [u8; 52] =
+        [0x60, 0x00, 0x00, 0x00,
+         0x00, 0x0c, 0x11, 0x40,
+         0xfe, 0x80, 0x00, 0x00,
+         0x00, 0x00, 0x00, 0x00,
+         0x00, 0x00, 0x00, 0x00,
+         0x00, 0x00, 0x00, 0x01,
+         0xfe, 0x80, 0x00, 0x00,
+         0x00, 0x00, 0x00, 0x00,
+         0x00, 0x00, 0x00, 0x00,
+         0x00, 0x00, 0x00, 0x02,
+         0xbf, 0x00, 0x00, 0x35,
+         0x00, 0x0c, 0x12, 0x4d,
+         0xaa, 0x00, 0x00, 0xff];
+
+    static PKT_TOO_BIG_UDP_PAYLOAD: [u8; 12] =
+        [0xbf, 0x00, 0x00, 0x35,
+         0x00, 0x0c, 0x12, 0x4d,
+         0xaa, 0x00, 0x00, 0xff];
+
+    fn echo_packet_repr() -> Repr<'static> {
+        Repr::EchoRequest {
+            ident: 0x1234,
+            seq_no: 0xabcd,
+            data: &ECHO_PACKET_PAYLOAD
+        }
+    }
+
+    fn too_big_packet_repr() -> Repr<'static> {
+        Repr::PktTooBig {
+            mtu: 1500,
+            header: Ipv6Repr {
+                src_addr: Ipv6Address([0xfe, 0x80, 0x00, 0x00,
+                                       0x00, 0x00, 0x00, 0x00,
+                                       0x00, 0x00, 0x00, 0x00,
+                                       0x00, 0x00, 0x00, 0x01]),
+                dst_addr: Ipv6Address([0xfe, 0x80, 0x00, 0x00,
+                                       0x00, 0x00, 0x00, 0x00,
+                                       0x00, 0x00, 0x00, 0x00,
+                                       0x00, 0x00, 0x00, 0x02]),
+                next_header: IpProtocol::Udp,
+                payload_len: 12,
+                hop_limit: 0x40
+            },
+            data: &PKT_TOO_BIG_UDP_PAYLOAD,
+        }
+    }
+
+    #[test]
+    fn test_echo_deconstruct() {
+        let packet = Packet::new(&ECHO_PACKET_BYTES[..]);
+        assert_eq!(packet.msg_type(), Message::EchoRequest);
+        assert_eq!(packet.msg_code(), 0);
+        assert_eq!(packet.checksum(), 0x16fe);
+        assert_eq!(packet.echo_ident(), 0x1234);
+        assert_eq!(packet.echo_seq_no(), 0xabcd);
+        assert_eq!(packet.payload(), &ECHO_PACKET_PAYLOAD[..]);
+        assert_eq!(packet.verify_checksum(), true);
+        assert!(!packet.msg_type().is_error());
+    }
+
+    #[test]
+    fn test_echo_construct() {
+        let mut bytes = vec![0xa5; 12];
+        let mut packet = Packet::new(&mut bytes);
+        packet.set_msg_type(Message::EchoRequest);
+        packet.set_msg_code(0);
+        packet.set_echo_ident(0x1234);
+        packet.set_echo_seq_no(0xabcd);
+        packet.payload_mut().copy_from_slice(&ECHO_PACKET_PAYLOAD[..]);
+        packet.fill_checksum();
+        assert_eq!(&packet.into_inner()[..], &ECHO_PACKET_BYTES[..]);
+    }
+
+    #[test]
+    fn test_echo_repr_parse() {
+        let packet = Packet::new(&ECHO_PACKET_BYTES[..]);
+        let repr = Repr::parse(&packet, &ChecksumCapabilities::default()).unwrap();
+        assert_eq!(repr, echo_packet_repr());
+    }
+
+    #[test]
+    fn test_echo_emit() {
+        let repr = echo_packet_repr();
+        let mut bytes = vec![0xa5; repr.buffer_len()];
+        let mut packet = Packet::new(&mut bytes);
+        repr.emit(&mut packet, &ChecksumCapabilities::default());
+        assert_eq!(&packet.into_inner()[..], &ECHO_PACKET_BYTES[..]);
+    }
+
+    #[test]
+    fn test_too_big_deconstruct() {
+        let packet = Packet::new(&PKT_TOO_BIG_BYTES[..]);
+        assert_eq!(packet.msg_type(), Message::PktTooBig);
+        assert_eq!(packet.msg_code(), 0);
+        assert_eq!(packet.checksum(), 0x0d44);
+        assert_eq!(packet.pkt_too_big_mtu(), 1500);
+        assert_eq!(packet.payload(), &PKT_TOO_BIG_IP_PAYLOAD[..]);
+        assert_eq!(packet.verify_checksum(), true);
+        assert!(packet.msg_type().is_error());
+    }
+
+    #[test]
+    fn test_too_big_construct() {
+        let mut bytes = vec![0xa5; 60];
+        let mut packet = Packet::new(&mut bytes);
+        packet.set_msg_type(Message::PktTooBig);
+        packet.set_msg_code(0);
+        packet.set_pkt_too_big_mtu(1500);
+        packet.payload_mut().copy_from_slice(&PKT_TOO_BIG_IP_PAYLOAD[..]);
+        packet.fill_checksum();
+        assert_eq!(&packet.into_inner()[..], &PKT_TOO_BIG_BYTES[..]);
+    }
+
+    #[test]
+    fn test_too_big_repr_parse() {
+        let packet = Packet::new(&PKT_TOO_BIG_BYTES[..]);
+        let repr = Repr::parse(&packet, &ChecksumCapabilities::default()).unwrap();
+        assert_eq!(repr, too_big_packet_repr());
+    }
+
+    #[test]
+    fn test_too_big_emit() {
+        let repr = too_big_packet_repr();
+        let mut bytes = vec![0xa5; repr.buffer_len()];
+        let mut packet = Packet::new(&mut bytes);
+        repr.emit(&mut packet, &ChecksumCapabilities::default());
+        assert_eq!(&packet.into_inner()[..], &PKT_TOO_BIG_BYTES[..]);
+    }
+}

+ 2 - 2
src/wire/ip.rs

@@ -678,12 +678,12 @@ pub mod checksum {
             accum += NetworkEndian::read_u16(data) as u32;
             data = &data[2..];
         }
-        
+
         // Add the last remaining odd byte, if any.
         if let Some(&value) = data.first() {
             accum += (value as u32) << 8;
         }
-        
+
         propagate_carries(accum)
     }
 

+ 8 - 0
src/wire/mod.rs

@@ -87,6 +87,8 @@ mod ipv4;
 mod ipv6;
 #[cfg(feature = "proto-ipv4")]
 mod icmpv4;
+#[cfg(feature = "proto-ipv6")]
+mod icmpv6;
 mod udp;
 mod tcp;
 
@@ -132,6 +134,12 @@ pub use self::icmpv4::{Message as Icmpv4Message,
                        Packet as Icmpv4Packet,
                        Repr as Icmpv4Repr};
 
+#[cfg(feature = "proto-ipv6")]
+pub use self::icmpv6::{Message as Icmpv6Message,
+                       DstUnreachable as Icmpv6DstUnreachable,
+                       Packet as Icmpv6Packet,
+                       Repr as Icmpv6Repr};
+
 pub use self::udp::{Packet as UdpPacket,
                     Repr as UdpRepr};