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> { 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> Packet { /// 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, 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 + AsMut<[u8]>> Packet { /// 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) } }