Implement ICMPv4 echo request/reply packet support.
parent
c5bdf30458
commit
9fa0bffb7a
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
use core::fmt;
|
||||
use byteorder::{ByteOrder, NetworkEndian};
|
||||
|
||||
enum_with_unknown! {
|
||||
/// Internet protocol type.
|
||||
|
@ -19,3 +20,13 @@ impl fmt::Display for ProtocolType {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rfc1071_checksum(checksum_at: usize, data: &[u8]) -> u16 {
|
||||
let mut accum: u32 = 0;
|
||||
for i in (0..data.len()).step_by(2) {
|
||||
if i == checksum_at { continue }
|
||||
let word = NetworkEndian::read_u16(&data[i..i + 2]) as u32;
|
||||
accum += word;
|
||||
}
|
||||
!(((accum >> 16) as u16) + (accum as u16))
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
use core::fmt;
|
||||
use byteorder::{ByteOrder, NetworkEndian};
|
||||
|
||||
use Error;
|
||||
use super::ip::rfc1071_checksum;
|
||||
|
||||
pub use super::InternetProtocolType as ProtocolType;
|
||||
|
||||
|
@ -89,16 +91,6 @@ mod field {
|
|||
pub const DST_ADDR: Field = 16..20;
|
||||
}
|
||||
|
||||
fn checksum(data: &[u8]) -> u16 {
|
||||
let mut accum: u32 = 0;
|
||||
for i in (0..data.len()).step_by(2) {
|
||||
if i == field::CHECKSUM.start { continue }
|
||||
let word = NetworkEndian::read_u16(&data[i..i + 2]) as u32;
|
||||
accum += word;
|
||||
}
|
||||
!(((accum >> 16) as u16) + (accum as u16))
|
||||
}
|
||||
|
||||
impl<T: AsRef<[u8]>> Packet<T> {
|
||||
/// Wrap a buffer with an IPv4 packet. Returns an error if the buffer
|
||||
/// is too small to contain one.
|
||||
|
@ -229,7 +221,7 @@ impl<T: AsRef<[u8]>> Packet<T> {
|
|||
pub fn verify_checksum(&self) -> bool {
|
||||
let checksum = {
|
||||
let data = self.buffer.as_ref();
|
||||
checksum(&data[..self.header_len() as usize])
|
||||
rfc1071_checksum(field::CHECKSUM.start, &data[..self.header_len() as usize])
|
||||
};
|
||||
self.checksum() == checksum
|
||||
}
|
||||
|
@ -350,7 +342,7 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
|
|||
pub fn fill_checksum(&mut self) {
|
||||
let checksum = {
|
||||
let data = self.buffer.as_ref();
|
||||
checksum(&data[..self.header_len() as usize])
|
||||
rfc1071_checksum(field::CHECKSUM.start, &data[..self.header_len() as usize])
|
||||
};
|
||||
self.set_checksum(checksum)
|
||||
}
|
||||
|
|
|
@ -21,12 +21,35 @@
|
|||
//! as much as is necessary to build the representation.
|
||||
|
||||
macro_rules! enum_with_unknown {
|
||||
(#[$( $attr:meta ),*]
|
||||
pub enum $name:ident($ty:ty) { $( $variant:ident = $value:expr ),+ }) => {
|
||||
(
|
||||
$( #[$enum_attr:meta] )*
|
||||
pub enum $name:ident($ty:ty) {
|
||||
$( $variant:ident = $value:expr ),+
|
||||
}
|
||||
) => {
|
||||
enum_with_unknown! {
|
||||
$( #[$enum_attr] )*
|
||||
pub doc enum $name($ty) {
|
||||
$( #[doc(shown)] $variant = $value ),+
|
||||
}
|
||||
}
|
||||
};
|
||||
(
|
||||
$( #[$enum_attr:meta] )*
|
||||
pub doc enum $name:ident($ty:ty) {
|
||||
$(
|
||||
$( #[$variant_attr:meta] )+
|
||||
$variant:ident = $value:expr
|
||||
),+
|
||||
}
|
||||
) => {
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
|
||||
#[$( $attr ),*]
|
||||
$( #[$enum_attr] )*
|
||||
pub enum $name {
|
||||
$( $variant ),*,
|
||||
$(
|
||||
$( #[$variant_attr] )*
|
||||
$variant
|
||||
),*,
|
||||
Unknown($ty)
|
||||
}
|
||||
|
||||
|
@ -61,6 +84,7 @@ mod ethernet;
|
|||
mod arp;
|
||||
mod ip;
|
||||
mod ipv4;
|
||||
mod icmpv4;
|
||||
|
||||
pub use self::pretty_print::PrettyPrinter;
|
||||
|
||||
|
@ -79,3 +103,10 @@ pub use self::ip::ProtocolType as InternetProtocolType;
|
|||
pub use self::ipv4::Address as Ipv4Address;
|
||||
pub use self::ipv4::Packet as Ipv4Packet;
|
||||
pub use self::ipv4::Repr as Ipv4Repr;
|
||||
|
||||
pub use self::icmpv4::Type as Icmpv4Type;
|
||||
pub use self::icmpv4::DstUnreachable as Icmpv4DstUnreachable;
|
||||
pub use self::icmpv4::Redirect as Icmpv4Redirect;
|
||||
pub use self::icmpv4::TimeExceeded as Icmpv4TimeExceeded;
|
||||
pub use self::icmpv4::ParamProblem as Icmpv4ParamProblem;
|
||||
pub use self::icmpv4::Packet as Icmpv4Packet;
|
||||
|
|
Loading…
Reference in New Issue