|
@@ -0,0 +1,274 @@
|
|
|
+use core::fmt;
|
|
|
+use byteorder::{ByteOrder, NetworkEndian};
|
|
|
+
|
|
|
+use Error;
|
|
|
+use super::ip::rfc1071_checksum;
|
|
|
+
|
|
|
+enum_with_unknown! {
|
|
|
+ /// Internet protocol control message type.
|
|
|
+ pub doc enum Type(u8) {
|
|
|
+ /// Echo reply
|
|
|
+ EchoReply = 0,
|
|
|
+ /// Destination unreachable
|
|
|
+ DstUnreachable = 1,
|
|
|
+ /// Message redirect
|
|
|
+ Redirect = 5,
|
|
|
+ /// Echo request
|
|
|
+ EchoRequest = 8,
|
|
|
+ /// Router advertisement
|
|
|
+ RouterAdvert = 9,
|
|
|
+ /// Router solicitation
|
|
|
+ RouterSolicit = 10,
|
|
|
+ /// Time exceeded
|
|
|
+ TimeExceeded = 11,
|
|
|
+ /// Parameter problem
|
|
|
+ ParamProblem = 12,
|
|
|
+ /// Timestamp
|
|
|
+ Timestamp = 13,
|
|
|
+ /// Timestamp reply
|
|
|
+ TimestampReply = 14
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+enum_with_unknown! {
|
|
|
+ /// Internet protocol control message subtype for type "Destination Unreachable".
|
|
|
+ pub doc enum DstUnreachable(u8) {
|
|
|
+ /// Destination network unreachable
|
|
|
+ NetUnreachable = 0,
|
|
|
+ /// Destination host unreachable
|
|
|
+ HostUnreachable = 1,
|
|
|
+ /// Destination protocol unreachable
|
|
|
+ ProtoUnreachable = 2,
|
|
|
+ /// Destination port unreachable
|
|
|
+ PortUnreachable = 3,
|
|
|
+ /// Fragmentation required, and DF flag set
|
|
|
+ FragRequired = 4,
|
|
|
+ /// Source route failed
|
|
|
+ SrcRouteFailed = 5,
|
|
|
+ /// Destination network unknown
|
|
|
+ DstNetUnknown = 6,
|
|
|
+ /// Destination host unknown
|
|
|
+ DstHostUnknown = 7,
|
|
|
+ /// Source host isolated
|
|
|
+ SrcHostIsolated = 8,
|
|
|
+ /// Network administratively prohibited
|
|
|
+ NetProhibited = 9,
|
|
|
+ /// Host administratively prohibited
|
|
|
+ HostProhibited = 10,
|
|
|
+ /// Network unreachable for ToS
|
|
|
+ NetUnreachToS = 11,
|
|
|
+ /// Host unreachable for ToS
|
|
|
+ HostUnreachToS = 12,
|
|
|
+ /// Communication administratively prohibited
|
|
|
+ CommProhibited = 13,
|
|
|
+ /// Host precedence violation
|
|
|
+ HostPrecedViol = 14,
|
|
|
+ /// Precedence cutoff in effect
|
|
|
+ PrecedCutoff = 15
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+enum_with_unknown! {
|
|
|
+ /// Internet protocol control message subtype for type "Redirect Message".
|
|
|
+ pub doc enum Redirect(u8) {
|
|
|
+ /// Redirect Datagram for the Network
|
|
|
+ Net = 0,
|
|
|
+ /// Redirect Datagram for the Host
|
|
|
+ Host = 1,
|
|
|
+ /// Redirect Datagram for the ToS & network
|
|
|
+ NetToS = 2,
|
|
|
+ /// Redirect Datagram for the ToS & host
|
|
|
+ HostToS = 3
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+enum_with_unknown! {
|
|
|
+ /// Internet protocol control message subtype for type "Time Exceeded".
|
|
|
+ pub doc enum TimeExceeded(u8) {
|
|
|
+ /// TTL expired in transit
|
|
|
+ TtlExpired = 0,
|
|
|
+ /// Fragment reassembly time exceeded
|
|
|
+ FragExpired = 1
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+enum_with_unknown! {
|
|
|
+ /// Internet protocol control message subtype for type "Parameter Problem".
|
|
|
+ pub doc enum ParamProblem(u8) {
|
|
|
+ /// Pointer indicates the error
|
|
|
+ AtPointer = 0,
|
|
|
+ /// Missing a required option
|
|
|
+ MissingOption = 1,
|
|
|
+ /// Bad length
|
|
|
+ BadLength = 2
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/// A read/write wrapper around an Internet Control Message Protocol version 4 packet buffer.
|
|
|
+#[derive(Debug)]
|
|
|
+pub struct Packet<T: AsRef<[u8]>> {
|
|
|
+ buffer: T
|
|
|
+}
|
|
|
+
|
|
|
+mod field {
|
|
|
+ #![allow(non_snake_case)]
|
|
|
+
|
|
|
+ use wire::field::*;
|
|
|
+
|
|
|
+ pub const TYPE: usize = 0;
|
|
|
+ pub const CODE: usize = 1;
|
|
|
+ pub const CHECKSUM: Field = 2..4;
|
|
|
+
|
|
|
+ pub const ECHO_IDENT: Field = 4..6;
|
|
|
+ pub const ECHO_SEQNO: Field = 6..8;
|
|
|
+}
|
|
|
+
|
|
|
+impl<T: AsRef<[u8]>> Packet<T> {
|
|
|
+ /// Wrap a buffer with an ICMPv4 packet. Returns an error if the buffer
|
|
|
+ /// is too small to contain one.
|
|
|
+ pub fn new(buffer: T) -> Result<Packet<T>, Error> {
|
|
|
+ let len = buffer.as_ref().len();
|
|
|
+ if len < field::CHECKSUM.end {
|
|
|
+ Err(Error::Truncated)
|
|
|
+ } else {
|
|
|
+ let packet = Packet { buffer: buffer };
|
|
|
+ if len < packet.header_len() {
|
|
|
+ Err(Error::Truncated)
|
|
|
+ } else {
|
|
|
+ Ok(packet)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Consumes the packet, returning the underlying buffer.
|
|
|
+ pub fn into_inner(self) -> T {
|
|
|
+ self.buffer
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Return the message type field.
|
|
|
+ #[inline(always)]
|
|
|
+ pub fn msg_type(&self) -> Type {
|
|
|
+ let data = self.buffer.as_ref();
|
|
|
+ Type::from(data[field::TYPE])
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Return the message code field.
|
|
|
+ #[inline(always)]
|
|
|
+ pub fn msg_code(&self) -> u8 {
|
|
|
+ let data = self.buffer.as_ref();
|
|
|
+ data[field::CODE]
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Return the checksum field.
|
|
|
+ #[inline(always)]
|
|
|
+ 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).
|
|
|
+ ///
|
|
|
+ /// # Panics
|
|
|
+ /// This function may panic if this packet is not an echo request or reply packet.
|
|
|
+ #[inline(always)]
|
|
|
+ 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).
|
|
|
+ ///
|
|
|
+ /// # Panics
|
|
|
+ /// This function may panic if this packet is not an echo request or reply packet.
|
|
|
+ #[inline(always)]
|
|
|
+ pub fn echo_seq_no(&self) -> u16 {
|
|
|
+ let data = self.buffer.as_ref();
|
|
|
+ NetworkEndian::read_u16(&data[field::ECHO_SEQNO])
|
|
|
+ }
|
|
|
+
|
|
|
+ /// 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() {
|
|
|
+ Type::EchoRequest => field::ECHO_SEQNO.end,
|
|
|
+ Type::EchoReply => field::ECHO_SEQNO.end,
|
|
|
+ _ => field::CHECKSUM.end // make a conservative assumption
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Return a pointer to the type-specific data.
|
|
|
+ #[inline(always)]
|
|
|
+ pub fn data(&self) -> &[u8] {
|
|
|
+ let data = self.buffer.as_ref();
|
|
|
+ &data[self.header_len()..]
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Validate the header checksum.
|
|
|
+ pub fn verify_checksum(&self) -> bool {
|
|
|
+ let checksum = {
|
|
|
+ let data = self.buffer.as_ref();
|
|
|
+ rfc1071_checksum(field::CHECKSUM.start, &data[..self.header_len()])
|
|
|
+ };
|
|
|
+ self.checksum() == checksum
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
|
|
|
+ /// Set the message type field.
|
|
|
+ #[inline(always)]
|
|
|
+ pub fn set_msg_type(&mut self, value: Type) {
|
|
|
+ let mut data = self.buffer.as_mut();
|
|
|
+ data[field::TYPE] = value.into()
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Set the message code field.
|
|
|
+ #[inline(always)]
|
|
|
+ pub fn set_msg_code(&mut self, value: u8) {
|
|
|
+ let mut data = self.buffer.as_mut();
|
|
|
+ data[field::CODE] = value
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Set the checksum field.
|
|
|
+ #[inline(always)]
|
|
|
+ pub fn set_checksum(&mut self, value: u16) {
|
|
|
+ let mut 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(always)]
|
|
|
+ pub fn set_echo_ident(&mut self, value: u16) {
|
|
|
+ let mut 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(always)]
|
|
|
+ pub fn set_echo_seq_no(&mut self, value: u16) {
|
|
|
+ let mut data = self.buffer.as_mut();
|
|
|
+ NetworkEndian::write_u16(&mut data[field::ECHO_SEQNO], value)
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Return a mutable pointer to the type-specific data.
|
|
|
+ #[inline(always)]
|
|
|
+ pub fn data_mut(&mut self) -> &mut [u8] {
|
|
|
+ let range = self.header_len()..;
|
|
|
+ let mut data = self.buffer.as_mut();
|
|
|
+ &mut data[range]
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Compute and fill in the header checksum.
|
|
|
+ pub fn fill_checksum(&mut self) {
|
|
|
+ let checksum = {
|
|
|
+ let data = self.buffer.as_ref();
|
|
|
+ rfc1071_checksum(field::CHECKSUM.start, &data[..self.header_len()])
|
|
|
+ };
|
|
|
+ self.set_checksum(checksum)
|
|
|
+ }
|
|
|
+}
|