2016-12-13 07:22:59 +08:00
|
|
|
use core::{cmp, fmt};
|
2016-12-13 06:11:52 +08:00
|
|
|
use byteorder::{ByteOrder, NetworkEndian};
|
|
|
|
|
2020-12-27 07:11:30 +08:00
|
|
|
use crate::{Error, Result};
|
|
|
|
use crate::phy::ChecksumCapabilities;
|
|
|
|
use crate::wire::ip::checksum;
|
|
|
|
use crate::wire::{Ipv4Packet, Ipv4Repr};
|
2016-12-13 06:11:52 +08:00
|
|
|
|
|
|
|
enum_with_unknown! {
|
|
|
|
/// Internet protocol control message type.
|
2016-12-20 21:54:11 +08:00
|
|
|
pub doc enum Message(u8) {
|
2016-12-13 06:11:52 +08:00
|
|
|
/// Echo reply
|
|
|
|
EchoReply = 0,
|
|
|
|
/// Destination unreachable
|
2016-12-21 03:18:35 +08:00
|
|
|
DstUnreachable = 3,
|
2016-12-13 06:11:52 +08:00
|
|
|
/// 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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-20 21:54:11 +08:00
|
|
|
impl fmt::Display for Message {
|
2016-12-13 06:38:13 +08:00
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
2020-12-26 16:28:05 +08:00
|
|
|
match *self {
|
|
|
|
Message::EchoReply => write!(f, "echo reply"),
|
|
|
|
Message::DstUnreachable => write!(f, "destination unreachable"),
|
|
|
|
Message::Redirect => write!(f, "message redirect"),
|
|
|
|
Message::EchoRequest => write!(f, "echo request"),
|
|
|
|
Message::RouterAdvert => write!(f, "router advertisement"),
|
|
|
|
Message::RouterSolicit => write!(f, "router solicitation"),
|
|
|
|
Message::TimeExceeded => write!(f, "time exceeded"),
|
|
|
|
Message::ParamProblem => write!(f, "parameter problem"),
|
|
|
|
Message::Timestamp => write!(f, "timestamp"),
|
|
|
|
Message::TimestampReply => write!(f, "timestamp reply"),
|
|
|
|
Message::Unknown(id) => write!(f, "{}", id)
|
2016-12-13 06:38:13 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-13 06:11:52 +08:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-21 03:18:35 +08:00
|
|
|
impl fmt::Display for DstUnreachable {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
2020-12-26 16:28:05 +08:00
|
|
|
match *self {
|
|
|
|
DstUnreachable::NetUnreachable =>
|
|
|
|
write!(f, "destination network unreachable"),
|
|
|
|
DstUnreachable::HostUnreachable =>
|
|
|
|
write!(f, "destination host unreachable"),
|
|
|
|
DstUnreachable::ProtoUnreachable =>
|
|
|
|
write!(f, "destination protocol unreachable"),
|
|
|
|
DstUnreachable::PortUnreachable =>
|
|
|
|
write!(f, "destination port unreachable"),
|
|
|
|
DstUnreachable::FragRequired =>
|
|
|
|
write!(f, "fragmentation required, and DF flag set"),
|
|
|
|
DstUnreachable::SrcRouteFailed =>
|
|
|
|
write!(f, "source route failed"),
|
|
|
|
DstUnreachable::DstNetUnknown =>
|
|
|
|
write!(f, "destination network unknown"),
|
|
|
|
DstUnreachable::DstHostUnknown =>
|
|
|
|
write!(f, "destination host unknown"),
|
|
|
|
DstUnreachable::SrcHostIsolated =>
|
|
|
|
write!(f, "source host isolated"),
|
|
|
|
DstUnreachable::NetProhibited =>
|
|
|
|
write!(f, "network administratively prohibited"),
|
|
|
|
DstUnreachable::HostProhibited =>
|
|
|
|
write!(f, "host administratively prohibited"),
|
|
|
|
DstUnreachable::NetUnreachToS =>
|
|
|
|
write!(f, "network unreachable for ToS"),
|
|
|
|
DstUnreachable::HostUnreachToS =>
|
|
|
|
write!(f, "host unreachable for ToS"),
|
|
|
|
DstUnreachable::CommProhibited =>
|
|
|
|
write!(f, "communication administratively prohibited"),
|
|
|
|
DstUnreachable::HostPrecedViol =>
|
|
|
|
write!(f, "host precedence violation"),
|
|
|
|
DstUnreachable::PrecedCutoff =>
|
|
|
|
write!(f, "precedence cutoff in effect"),
|
|
|
|
DstUnreachable::Unknown(id) =>
|
2016-12-21 03:18:35 +08:00
|
|
|
write!(f, "{}", id)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-13 06:11:52 +08:00
|
|
|
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.
|
2018-02-05 19:42:05 +08:00
|
|
|
#[derive(Debug, PartialEq, Clone)]
|
2016-12-13 06:11:52 +08:00
|
|
|
pub struct Packet<T: AsRef<[u8]>> {
|
|
|
|
buffer: T
|
|
|
|
}
|
|
|
|
|
|
|
|
mod field {
|
2020-12-27 07:11:30 +08:00
|
|
|
use crate::wire::field::*;
|
2016-12-13 06:11:52 +08:00
|
|
|
|
|
|
|
pub const TYPE: usize = 0;
|
|
|
|
pub const CODE: usize = 1;
|
|
|
|
pub const CHECKSUM: Field = 2..4;
|
|
|
|
|
2016-12-21 03:18:35 +08:00
|
|
|
pub const UNUSED: Field = 4..8;
|
|
|
|
|
2016-12-13 06:11:52 +08:00
|
|
|
pub const ECHO_IDENT: Field = 4..6;
|
|
|
|
pub const ECHO_SEQNO: Field = 6..8;
|
2018-01-27 02:59:11 +08:00
|
|
|
|
|
|
|
pub const HEADER_END: usize = 8;
|
2016-12-13 06:11:52 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<T: AsRef<[u8]>> Packet<T> {
|
Do not attempt to validate length of packets being emitted.
This is a form of an uninitialized read bug; although safe it caused
panics. In short, transmit buffers received from the network stack
should be considered uninitialized (in practice they will often
contain previously transmitted packets or parts thereof). Wrapping
them with the only method we had (e.g. Ipv4Packet) treated the buffer
as if it contained a valid incoming packet, which can easily fail
with Error::Truncated.
This commit splits every `fn new(buffer: T) -> Result<Self, Error>`
method on a `Packet` into three smaller ones:
* `fn check_len(&self) -> Result<(), Error>`, purely a validator;
* `fn new(T) -> Self`, purely a wrapper;
* `fn new_checked(T) -> Result<Self, Error>`, a validating wrapper.
This makes it easy to process ingress packets (using `new_checked`),
egress packets (using `new`), and, if needed, maintain the invariants
at any point during packet construction (using `check_len`).
Fixes #17.
2017-06-24 17:15:22 +08:00
|
|
|
/// Imbue a raw octet buffer with ICMPv4 packet structure.
|
2018-07-11 08:22:43 +08:00
|
|
|
pub fn new_unchecked(buffer: T) -> Packet<T> {
|
Do not attempt to validate length of packets being emitted.
This is a form of an uninitialized read bug; although safe it caused
panics. In short, transmit buffers received from the network stack
should be considered uninitialized (in practice they will often
contain previously transmitted packets or parts thereof). Wrapping
them with the only method we had (e.g. Ipv4Packet) treated the buffer
as if it contained a valid incoming packet, which can easily fail
with Error::Truncated.
This commit splits every `fn new(buffer: T) -> Result<Self, Error>`
method on a `Packet` into three smaller ones:
* `fn check_len(&self) -> Result<(), Error>`, purely a validator;
* `fn new(T) -> Self`, purely a wrapper;
* `fn new_checked(T) -> Result<Self, Error>`, a validating wrapper.
This makes it easy to process ingress packets (using `new_checked`),
egress packets (using `new`), and, if needed, maintain the invariants
at any point during packet construction (using `check_len`).
Fixes #17.
2017-06-24 17:15:22 +08:00
|
|
|
Packet { buffer }
|
|
|
|
}
|
|
|
|
|
2018-07-11 08:22:43 +08:00
|
|
|
/// Shorthand for a combination of [new_unchecked] and [check_len].
|
Do not attempt to validate length of packets being emitted.
This is a form of an uninitialized read bug; although safe it caused
panics. In short, transmit buffers received from the network stack
should be considered uninitialized (in practice they will often
contain previously transmitted packets or parts thereof). Wrapping
them with the only method we had (e.g. Ipv4Packet) treated the buffer
as if it contained a valid incoming packet, which can easily fail
with Error::Truncated.
This commit splits every `fn new(buffer: T) -> Result<Self, Error>`
method on a `Packet` into three smaller ones:
* `fn check_len(&self) -> Result<(), Error>`, purely a validator;
* `fn new(T) -> Self`, purely a wrapper;
* `fn new_checked(T) -> Result<Self, Error>`, a validating wrapper.
This makes it easy to process ingress packets (using `new_checked`),
egress packets (using `new`), and, if needed, maintain the invariants
at any point during packet construction (using `check_len`).
Fixes #17.
2017-06-24 17:15:22 +08:00
|
|
|
///
|
2018-07-11 08:22:43 +08:00
|
|
|
/// [new_unchecked]: #method.new_unchecked
|
Do not attempt to validate length of packets being emitted.
This is a form of an uninitialized read bug; although safe it caused
panics. In short, transmit buffers received from the network stack
should be considered uninitialized (in practice they will often
contain previously transmitted packets or parts thereof). Wrapping
them with the only method we had (e.g. Ipv4Packet) treated the buffer
as if it contained a valid incoming packet, which can easily fail
with Error::Truncated.
This commit splits every `fn new(buffer: T) -> Result<Self, Error>`
method on a `Packet` into three smaller ones:
* `fn check_len(&self) -> Result<(), Error>`, purely a validator;
* `fn new(T) -> Self`, purely a wrapper;
* `fn new_checked(T) -> Result<Self, Error>`, a validating wrapper.
This makes it easy to process ingress packets (using `new_checked`),
egress packets (using `new`), and, if needed, maintain the invariants
at any point during packet construction (using `check_len`).
Fixes #17.
2017-06-24 17:15:22 +08:00
|
|
|
/// [check_len]: #method.check_len
|
2017-07-27 21:51:02 +08:00
|
|
|
pub fn new_checked(buffer: T) -> Result<Packet<T>> {
|
2018-07-11 08:22:43 +08:00
|
|
|
let packet = Self::new_unchecked(buffer);
|
2017-06-25 00:34:32 +08:00
|
|
|
packet.check_len()?;
|
Do not attempt to validate length of packets being emitted.
This is a form of an uninitialized read bug; although safe it caused
panics. In short, transmit buffers received from the network stack
should be considered uninitialized (in practice they will often
contain previously transmitted packets or parts thereof). Wrapping
them with the only method we had (e.g. Ipv4Packet) treated the buffer
as if it contained a valid incoming packet, which can easily fail
with Error::Truncated.
This commit splits every `fn new(buffer: T) -> Result<Self, Error>`
method on a `Packet` into three smaller ones:
* `fn check_len(&self) -> Result<(), Error>`, purely a validator;
* `fn new(T) -> Self`, purely a wrapper;
* `fn new_checked(T) -> Result<Self, Error>`, a validating wrapper.
This makes it easy to process ingress packets (using `new_checked`),
egress packets (using `new`), and, if needed, maintain the invariants
at any point during packet construction (using `check_len`).
Fixes #17.
2017-06-24 17:15:22 +08:00
|
|
|
Ok(packet)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Ensure that no accessor method will panic if called.
|
|
|
|
/// Returns `Err(Error::Truncated)` if the buffer is too short.
|
|
|
|
///
|
|
|
|
/// The result of this check is invalidated by calling [set_header_len].
|
|
|
|
///
|
|
|
|
/// [set_header_len]: #method.set_header_len
|
2017-07-27 21:51:02 +08:00
|
|
|
pub fn check_len(&self) -> Result<()> {
|
Do not attempt to validate length of packets being emitted.
This is a form of an uninitialized read bug; although safe it caused
panics. In short, transmit buffers received from the network stack
should be considered uninitialized (in practice they will often
contain previously transmitted packets or parts thereof). Wrapping
them with the only method we had (e.g. Ipv4Packet) treated the buffer
as if it contained a valid incoming packet, which can easily fail
with Error::Truncated.
This commit splits every `fn new(buffer: T) -> Result<Self, Error>`
method on a `Packet` into three smaller ones:
* `fn check_len(&self) -> Result<(), Error>`, purely a validator;
* `fn new(T) -> Self`, purely a wrapper;
* `fn new_checked(T) -> Result<Self, Error>`, a validating wrapper.
This makes it easy to process ingress packets (using `new_checked`),
egress packets (using `new`), and, if needed, maintain the invariants
at any point during packet construction (using `check_len`).
Fixes #17.
2017-06-24 17:15:22 +08:00
|
|
|
let len = self.buffer.as_ref().len();
|
2018-01-27 02:59:11 +08:00
|
|
|
if len < field::HEADER_END {
|
2016-12-13 06:11:52 +08:00
|
|
|
Err(Error::Truncated)
|
|
|
|
} else {
|
2018-01-18 09:32:00 +08:00
|
|
|
Ok(())
|
2016-12-13 06:11:52 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Do not attempt to validate length of packets being emitted.
This is a form of an uninitialized read bug; although safe it caused
panics. In short, transmit buffers received from the network stack
should be considered uninitialized (in practice they will often
contain previously transmitted packets or parts thereof). Wrapping
them with the only method we had (e.g. Ipv4Packet) treated the buffer
as if it contained a valid incoming packet, which can easily fail
with Error::Truncated.
This commit splits every `fn new(buffer: T) -> Result<Self, Error>`
method on a `Packet` into three smaller ones:
* `fn check_len(&self) -> Result<(), Error>`, purely a validator;
* `fn new(T) -> Self`, purely a wrapper;
* `fn new_checked(T) -> Result<Self, Error>`, a validating wrapper.
This makes it easy to process ingress packets (using `new_checked`),
egress packets (using `new`), and, if needed, maintain the invariants
at any point during packet construction (using `check_len`).
Fixes #17.
2017-06-24 17:15:22 +08:00
|
|
|
/// Consume the packet, returning the underlying buffer.
|
2016-12-13 06:11:52 +08:00
|
|
|
pub fn into_inner(self) -> T {
|
|
|
|
self.buffer
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Return the message type field.
|
2016-12-31 00:55:31 +08:00
|
|
|
#[inline]
|
2016-12-20 21:54:11 +08:00
|
|
|
pub fn msg_type(&self) -> Message {
|
2016-12-13 06:11:52 +08:00
|
|
|
let data = self.buffer.as_ref();
|
2016-12-20 21:54:11 +08:00
|
|
|
Message::from(data[field::TYPE])
|
2016-12-13 06:11:52 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Return the message code field.
|
2016-12-31 00:55:31 +08:00
|
|
|
#[inline]
|
2016-12-13 06:11:52 +08:00
|
|
|
pub fn msg_code(&self) -> u8 {
|
|
|
|
let data = self.buffer.as_ref();
|
|
|
|
data[field::CODE]
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Return the checksum field.
|
2016-12-31 00:55:31 +08:00
|
|
|
#[inline]
|
2016-12-13 06:11:52 +08:00
|
|
|
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.
|
2016-12-31 00:55:31 +08:00
|
|
|
#[inline]
|
2016-12-13 06:11:52 +08:00
|
|
|
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.
|
2016-12-31 00:55:31 +08:00
|
|
|
#[inline]
|
2016-12-13 06:11:52 +08:00
|
|
|
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() {
|
2016-12-21 03:18:35 +08:00
|
|
|
Message::EchoRequest => field::ECHO_SEQNO.end,
|
|
|
|
Message::EchoReply => field::ECHO_SEQNO.end,
|
|
|
|
Message::DstUnreachable => field::UNUSED.end,
|
2018-01-18 09:32:00 +08:00
|
|
|
_ => field::UNUSED.end // make a conservative assumption
|
2016-12-13 06:11:52 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Validate the header checksum.
|
2017-06-24 23:26:15 +08:00
|
|
|
///
|
|
|
|
/// # Fuzzing
|
|
|
|
/// This function always returns `true` when fuzzing.
|
2016-12-13 06:11:52 +08:00
|
|
|
pub fn verify_checksum(&self) -> bool {
|
2017-06-24 23:26:15 +08:00
|
|
|
if cfg!(fuzzing) { return true }
|
|
|
|
|
2016-12-14 01:31:08 +08:00
|
|
|
let data = self.buffer.as_ref();
|
2016-12-14 09:59:47 +08:00
|
|
|
checksum::data(data) == !0
|
2016-12-13 06:11:52 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-13 23:18:56 +08:00
|
|
|
impl<'a, T: AsRef<[u8]> + ?Sized> Packet<&'a T> {
|
|
|
|
/// Return a pointer to the type-specific data.
|
2016-12-31 00:55:31 +08:00
|
|
|
#[inline]
|
2016-12-13 23:18:56 +08:00
|
|
|
pub fn data(&self) -> &'a [u8] {
|
|
|
|
let data = self.buffer.as_ref();
|
|
|
|
&data[self.header_len()..]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-13 06:11:52 +08:00
|
|
|
impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
|
|
|
|
/// Set the message type field.
|
2016-12-31 00:55:31 +08:00
|
|
|
#[inline]
|
2016-12-20 21:54:11 +08:00
|
|
|
pub fn set_msg_type(&mut self, value: Message) {
|
2017-09-25 08:55:54 +08:00
|
|
|
let data = self.buffer.as_mut();
|
2016-12-13 06:11:52 +08:00
|
|
|
data[field::TYPE] = value.into()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Set the message code field.
|
2016-12-31 00:55:31 +08:00
|
|
|
#[inline]
|
2016-12-13 06:11:52 +08:00
|
|
|
pub fn set_msg_code(&mut self, value: u8) {
|
2017-09-25 08:55:54 +08:00
|
|
|
let data = self.buffer.as_mut();
|
2016-12-13 06:11:52 +08:00
|
|
|
data[field::CODE] = value
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Set the checksum field.
|
2016-12-31 00:55:31 +08:00
|
|
|
#[inline]
|
2016-12-13 06:11:52 +08:00
|
|
|
pub fn set_checksum(&mut self, value: u16) {
|
2017-09-25 08:55:54 +08:00
|
|
|
let data = self.buffer.as_mut();
|
2016-12-13 06:11:52 +08:00
|
|
|
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.
|
2016-12-31 00:55:31 +08:00
|
|
|
#[inline]
|
2016-12-13 06:11:52 +08:00
|
|
|
pub fn set_echo_ident(&mut self, value: u16) {
|
2017-09-25 08:55:54 +08:00
|
|
|
let data = self.buffer.as_mut();
|
2016-12-13 06:11:52 +08:00
|
|
|
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.
|
2016-12-31 00:55:31 +08:00
|
|
|
#[inline]
|
2016-12-13 06:11:52 +08:00
|
|
|
pub fn set_echo_seq_no(&mut self, value: u16) {
|
2017-09-25 08:55:54 +08:00
|
|
|
let data = self.buffer.as_mut();
|
2016-12-13 06:11:52 +08:00
|
|
|
NetworkEndian::write_u16(&mut data[field::ECHO_SEQNO], value)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Compute and fill in the header checksum.
|
|
|
|
pub fn fill_checksum(&mut self) {
|
2016-12-14 01:31:08 +08:00
|
|
|
self.set_checksum(0);
|
2016-12-13 06:11:52 +08:00
|
|
|
let checksum = {
|
|
|
|
let data = self.buffer.as_ref();
|
2016-12-14 09:59:47 +08:00
|
|
|
!checksum::data(data)
|
2016-12-13 06:11:52 +08:00
|
|
|
};
|
|
|
|
self.set_checksum(checksum)
|
|
|
|
}
|
|
|
|
}
|
2016-12-13 06:38:13 +08:00
|
|
|
|
2016-12-14 06:37:05 +08:00
|
|
|
impl<'a, T: AsRef<[u8]> + AsMut<[u8]> + ?Sized> Packet<&'a mut T> {
|
|
|
|
/// Return a mutable pointer to the type-specific data.
|
2016-12-31 00:55:31 +08:00
|
|
|
#[inline]
|
2016-12-14 06:37:05 +08:00
|
|
|
pub fn data_mut(&mut self) -> &mut [u8] {
|
|
|
|
let range = self.header_len()..;
|
2017-09-25 08:55:54 +08:00
|
|
|
let data = self.buffer.as_mut();
|
2016-12-14 06:37:05 +08:00
|
|
|
&mut data[range]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-08 16:54:50 +08:00
|
|
|
impl<T: AsRef<[u8]>> AsRef<[u8]> for Packet<T> {
|
|
|
|
fn as_ref(&self) -> &[u8] {
|
|
|
|
self.buffer.as_ref()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-13 06:38:13 +08:00
|
|
|
/// A high-level representation of an Internet Control Message Protocol version 4 packet header.
|
|
|
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
|
|
|
pub enum Repr<'a> {
|
|
|
|
EchoRequest {
|
|
|
|
ident: u16,
|
|
|
|
seq_no: u16,
|
|
|
|
data: &'a [u8]
|
|
|
|
},
|
|
|
|
EchoReply {
|
|
|
|
ident: u16,
|
|
|
|
seq_no: u16,
|
|
|
|
data: &'a [u8]
|
|
|
|
},
|
2016-12-21 03:18:35 +08:00
|
|
|
DstUnreachable {
|
|
|
|
reason: DstUnreachable,
|
|
|
|
header: Ipv4Repr,
|
2017-06-21 12:08:33 +08:00
|
|
|
data: &'a [u8]
|
2016-12-21 03:18:35 +08:00
|
|
|
},
|
2016-12-13 06:38:13 +08:00
|
|
|
#[doc(hidden)]
|
|
|
|
__Nonexhaustive
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> Repr<'a> {
|
|
|
|
/// Parse an Internet Control Message Protocol version 4 packet and return
|
2016-12-14 09:59:47 +08:00
|
|
|
/// a high-level representation.
|
2017-10-03 05:51:43 +08:00
|
|
|
pub fn parse<T>(packet: &Packet<&'a T>, checksum_caps: &ChecksumCapabilities)
|
|
|
|
-> Result<Repr<'a>>
|
|
|
|
where T: AsRef<[u8]> + ?Sized {
|
|
|
|
// Valid checksum is expected.
|
|
|
|
if checksum_caps.icmpv4.rx() && !packet.verify_checksum() { return Err(Error::Checksum) }
|
2017-10-02 18:47:51 +08:00
|
|
|
|
2016-12-13 06:38:13 +08:00
|
|
|
match (packet.msg_type(), packet.msg_code()) {
|
2016-12-20 21:54:11 +08:00
|
|
|
(Message::EchoRequest, 0) => {
|
2016-12-13 06:38:13 +08:00
|
|
|
Ok(Repr::EchoRequest {
|
|
|
|
ident: packet.echo_ident(),
|
|
|
|
seq_no: packet.echo_seq_no(),
|
|
|
|
data: packet.data()
|
|
|
|
})
|
|
|
|
},
|
2016-12-21 03:18:35 +08:00
|
|
|
|
2016-12-20 21:54:11 +08:00
|
|
|
(Message::EchoReply, 0) => {
|
2016-12-13 06:38:13 +08:00
|
|
|
Ok(Repr::EchoReply {
|
|
|
|
ident: packet.echo_ident(),
|
|
|
|
seq_no: packet.echo_seq_no(),
|
|
|
|
data: packet.data()
|
|
|
|
})
|
|
|
|
},
|
2016-12-21 03:18:35 +08:00
|
|
|
|
|
|
|
(Message::DstUnreachable, code) => {
|
2017-06-25 00:34:32 +08:00
|
|
|
let ip_packet = Ipv4Packet::new_checked(packet.data())?;
|
2016-12-21 03:18:35 +08:00
|
|
|
|
|
|
|
let payload = &packet.data()[ip_packet.header_len() as usize..];
|
2017-06-21 12:08:33 +08:00
|
|
|
// RFC 792 requires exactly eight bytes to be returned.
|
|
|
|
// We allow more, since there isn't a reason not to, but require at least eight.
|
|
|
|
if payload.len() < 8 { return Err(Error::Truncated) }
|
2016-12-21 03:18:35 +08:00
|
|
|
|
|
|
|
Ok(Repr::DstUnreachable {
|
|
|
|
reason: DstUnreachable::from(code),
|
2017-06-21 12:08:33 +08:00
|
|
|
header: Ipv4Repr {
|
|
|
|
src_addr: ip_packet.src_addr(),
|
|
|
|
dst_addr: ip_packet.dst_addr(),
|
|
|
|
protocol: ip_packet.protocol(),
|
|
|
|
payload_len: payload.len(),
|
2017-12-10 11:09:50 +08:00
|
|
|
hop_limit: ip_packet.hop_limit()
|
2017-06-21 12:08:33 +08:00
|
|
|
},
|
|
|
|
data: payload
|
2016-12-21 03:18:35 +08:00
|
|
|
})
|
|
|
|
}
|
2016-12-13 06:38:13 +08:00
|
|
|
_ => Err(Error::Unrecognized)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-13 07:22:59 +08:00
|
|
|
/// Return the length of a packet that will be emitted from this high-level representation.
|
2016-12-20 07:50:04 +08:00
|
|
|
pub fn buffer_len(&self) -> usize {
|
2016-12-13 07:22:59 +08:00
|
|
|
match self {
|
|
|
|
&Repr::EchoRequest { data, .. } |
|
|
|
|
&Repr::EchoReply { data, .. } => {
|
|
|
|
field::ECHO_SEQNO.end + data.len()
|
|
|
|
},
|
2016-12-21 03:18:35 +08:00
|
|
|
&Repr::DstUnreachable { header, data, .. } => {
|
|
|
|
field::UNUSED.end + header.buffer_len() + data.len()
|
|
|
|
}
|
2016-12-13 07:22:59 +08:00
|
|
|
&Repr::__Nonexhaustive => unreachable!()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-14 09:59:47 +08:00
|
|
|
/// Emit a high-level representation into an Internet Control Message Protocol version 4
|
|
|
|
/// packet.
|
2017-10-03 05:51:43 +08:00
|
|
|
pub fn emit<T>(&self, packet: &mut Packet<&mut T>, checksum_caps: &ChecksumCapabilities)
|
|
|
|
where T: AsRef<[u8]> + AsMut<[u8]> + ?Sized {
|
2016-12-13 06:38:13 +08:00
|
|
|
packet.set_msg_code(0);
|
2020-12-26 16:28:05 +08:00
|
|
|
match *self {
|
|
|
|
Repr::EchoRequest { ident, seq_no, data } => {
|
2016-12-20 21:54:11 +08:00
|
|
|
packet.set_msg_type(Message::EchoRequest);
|
2016-12-21 03:18:35 +08:00
|
|
|
packet.set_msg_code(0);
|
2016-12-13 06:38:13 +08:00
|
|
|
packet.set_echo_ident(ident);
|
|
|
|
packet.set_echo_seq_no(seq_no);
|
2016-12-13 07:22:59 +08:00
|
|
|
let data_len = cmp::min(packet.data_mut().len(), data.len());
|
|
|
|
packet.data_mut()[..data_len].copy_from_slice(&data[..data_len])
|
2016-12-13 06:38:13 +08:00
|
|
|
},
|
2016-12-21 03:18:35 +08:00
|
|
|
|
2020-12-26 16:28:05 +08:00
|
|
|
Repr::EchoReply { ident, seq_no, data } => {
|
2016-12-20 21:54:11 +08:00
|
|
|
packet.set_msg_type(Message::EchoReply);
|
2016-12-21 03:18:35 +08:00
|
|
|
packet.set_msg_code(0);
|
2016-12-13 06:38:13 +08:00
|
|
|
packet.set_echo_ident(ident);
|
|
|
|
packet.set_echo_seq_no(seq_no);
|
2016-12-13 07:22:59 +08:00
|
|
|
let data_len = cmp::min(packet.data_mut().len(), data.len());
|
|
|
|
packet.data_mut()[..data_len].copy_from_slice(&data[..data_len])
|
2016-12-13 06:38:13 +08:00
|
|
|
},
|
2016-12-21 03:18:35 +08:00
|
|
|
|
2020-12-26 16:28:05 +08:00
|
|
|
Repr::DstUnreachable { reason, header, data } => {
|
2016-12-21 03:18:35 +08:00
|
|
|
packet.set_msg_type(Message::DstUnreachable);
|
|
|
|
packet.set_msg_code(reason.into());
|
|
|
|
|
2018-07-11 08:22:43 +08:00
|
|
|
let mut ip_packet = Ipv4Packet::new_unchecked(packet.data_mut());
|
2017-10-02 18:47:51 +08:00
|
|
|
header.emit(&mut ip_packet, checksum_caps);
|
2017-09-25 08:55:54 +08:00
|
|
|
let payload = &mut ip_packet.into_inner()[header.buffer_len()..];
|
2016-12-21 03:18:35 +08:00
|
|
|
payload.copy_from_slice(&data[..])
|
|
|
|
}
|
|
|
|
|
2020-12-26 16:28:05 +08:00
|
|
|
Repr::__Nonexhaustive => unreachable!()
|
2016-12-13 06:38:13 +08:00
|
|
|
}
|
2017-10-02 18:47:51 +08:00
|
|
|
|
|
|
|
if checksum_caps.icmpv4.tx() {
|
|
|
|
packet.fill_checksum()
|
|
|
|
} else {
|
2018-01-27 01:28:52 +08:00
|
|
|
// make sure we get a consistently zeroed checksum,
|
|
|
|
// since implementations might rely on it
|
2017-10-02 18:47:51 +08:00
|
|
|
packet.set_checksum(0);
|
|
|
|
}
|
2016-12-13 06:38:13 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-13 23:18:56 +08:00
|
|
|
impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> {
|
2016-12-13 06:38:13 +08:00
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
2017-10-02 18:47:51 +08:00
|
|
|
match Repr::parse(self, &ChecksumCapabilities::default()) {
|
2016-12-13 06:38:13 +08:00
|
|
|
Ok(repr) => write!(f, "{}", repr),
|
2016-12-21 03:18:35 +08:00
|
|
|
Err(err) => {
|
2017-06-25 00:34:32 +08:00
|
|
|
write!(f, "ICMPv4 ({})", err)?;
|
|
|
|
write!(f, " type={:?}", self.msg_type())?;
|
2016-12-21 03:18:35 +08:00
|
|
|
match self.msg_type() {
|
|
|
|
Message::DstUnreachable =>
|
2017-06-25 00:34:32 +08:00
|
|
|
write!(f, " code={:?}", DstUnreachable::from(self.msg_code())),
|
|
|
|
_ => write!(f, " code={}", self.msg_code())
|
2016-12-21 03:18:35 +08:00
|
|
|
}
|
2016-12-13 06:38:13 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> fmt::Display for Repr<'a> {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
2020-12-26 16:28:05 +08:00
|
|
|
match *self {
|
|
|
|
Repr::EchoRequest { ident, seq_no, data } =>
|
|
|
|
write!(f, "ICMPv4 echo request id={} seq={} len={}",
|
|
|
|
ident, seq_no, data.len()),
|
|
|
|
Repr::EchoReply { ident, seq_no, data } =>
|
|
|
|
write!(f, "ICMPv4 echo reply id={} seq={} len={}",
|
|
|
|
ident, seq_no, data.len()),
|
|
|
|
Repr::DstUnreachable { reason, .. } =>
|
|
|
|
write!(f, "ICMPv4 destination unreachable ({})",
|
|
|
|
reason),
|
|
|
|
Repr::__Nonexhaustive => unreachable!()
|
2016-12-13 06:38:13 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-27 07:11:30 +08:00
|
|
|
use crate::wire::pretty_print::{PrettyPrint, PrettyIndent};
|
2016-12-13 06:38:13 +08:00
|
|
|
|
|
|
|
impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
|
2019-06-22 16:19:39 +08:00
|
|
|
fn pretty_print(buffer: &dyn AsRef<[u8]>, f: &mut fmt::Formatter,
|
2016-12-13 06:38:13 +08:00
|
|
|
indent: &mut PrettyIndent) -> fmt::Result {
|
Do not attempt to validate length of packets being emitted.
This is a form of an uninitialized read bug; although safe it caused
panics. In short, transmit buffers received from the network stack
should be considered uninitialized (in practice they will often
contain previously transmitted packets or parts thereof). Wrapping
them with the only method we had (e.g. Ipv4Packet) treated the buffer
as if it contained a valid incoming packet, which can easily fail
with Error::Truncated.
This commit splits every `fn new(buffer: T) -> Result<Self, Error>`
method on a `Packet` into three smaller ones:
* `fn check_len(&self) -> Result<(), Error>`, purely a validator;
* `fn new(T) -> Self`, purely a wrapper;
* `fn new_checked(T) -> Result<Self, Error>`, a validating wrapper.
This makes it easy to process ingress packets (using `new_checked`),
egress packets (using `new`), and, if needed, maintain the invariants
at any point during packet construction (using `check_len`).
Fixes #17.
2017-06-24 17:15:22 +08:00
|
|
|
let packet = match Packet::new_checked(buffer) {
|
2017-12-17 05:42:19 +08:00
|
|
|
Err(err) => return write!(f, "{}({})", indent, err),
|
2016-12-21 03:18:35 +08:00
|
|
|
Ok(packet) => packet
|
|
|
|
};
|
2017-12-17 05:42:19 +08:00
|
|
|
write!(f, "{}{}", indent, packet)?;
|
2016-12-21 03:18:35 +08:00
|
|
|
|
|
|
|
match packet.msg_type() {
|
2017-12-17 05:42:19 +08:00
|
|
|
Message::DstUnreachable => {
|
|
|
|
indent.increase(f)?;
|
|
|
|
super::Ipv4Packet::<&[u8]>::pretty_print(&packet.data(), f, indent)
|
|
|
|
}
|
2016-12-21 03:18:35 +08:00
|
|
|
_ => Ok(())
|
2016-12-13 06:38:13 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
static ECHO_PACKET_BYTES: [u8; 12] =
|
2016-12-14 01:31:08 +08:00
|
|
|
[0x08, 0x00, 0x8e, 0xfe,
|
2016-12-13 06:38:13 +08:00
|
|
|
0x12, 0x34, 0xab, 0xcd,
|
|
|
|
0xaa, 0x00, 0x00, 0xff];
|
|
|
|
|
|
|
|
static ECHO_DATA_BYTES: [u8; 4] =
|
|
|
|
[0xaa, 0x00, 0x00, 0xff];
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_echo_deconstruct() {
|
2018-07-11 08:22:43 +08:00
|
|
|
let packet = Packet::new_unchecked(&ECHO_PACKET_BYTES[..]);
|
2016-12-20 21:54:11 +08:00
|
|
|
assert_eq!(packet.msg_type(), Message::EchoRequest);
|
2016-12-13 06:38:13 +08:00
|
|
|
assert_eq!(packet.msg_code(), 0);
|
2016-12-14 01:31:08 +08:00
|
|
|
assert_eq!(packet.checksum(), 0x8efe);
|
2016-12-13 06:38:13 +08:00
|
|
|
assert_eq!(packet.echo_ident(), 0x1234);
|
|
|
|
assert_eq!(packet.echo_seq_no(), 0xabcd);
|
|
|
|
assert_eq!(packet.data(), &ECHO_DATA_BYTES[..]);
|
2016-12-14 01:31:08 +08:00
|
|
|
assert_eq!(packet.verify_checksum(), true);
|
2016-12-13 06:38:13 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_echo_construct() {
|
2017-06-26 11:44:36 +08:00
|
|
|
let mut bytes = vec![0xa5; 12];
|
2018-07-11 08:22:43 +08:00
|
|
|
let mut packet = Packet::new_unchecked(&mut bytes);
|
2016-12-20 21:54:11 +08:00
|
|
|
packet.set_msg_type(Message::EchoRequest);
|
2016-12-13 06:38:13 +08:00
|
|
|
packet.set_msg_code(0);
|
|
|
|
packet.set_echo_ident(0x1234);
|
|
|
|
packet.set_echo_seq_no(0xabcd);
|
|
|
|
packet.data_mut().copy_from_slice(&ECHO_DATA_BYTES[..]);
|
2016-12-14 01:31:08 +08:00
|
|
|
packet.fill_checksum();
|
2016-12-13 06:38:13 +08:00
|
|
|
assert_eq!(&packet.into_inner()[..], &ECHO_PACKET_BYTES[..]);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn echo_packet_repr() -> Repr<'static> {
|
|
|
|
Repr::EchoRequest {
|
|
|
|
ident: 0x1234,
|
|
|
|
seq_no: 0xabcd,
|
|
|
|
data: &ECHO_DATA_BYTES
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_echo_parse() {
|
2018-07-11 08:22:43 +08:00
|
|
|
let packet = Packet::new_unchecked(&ECHO_PACKET_BYTES[..]);
|
2017-10-02 18:47:51 +08:00
|
|
|
let repr = Repr::parse(&packet, &ChecksumCapabilities::default()).unwrap();
|
2016-12-13 06:38:13 +08:00
|
|
|
assert_eq!(repr, echo_packet_repr());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_echo_emit() {
|
2016-12-20 20:52:33 +08:00
|
|
|
let repr = echo_packet_repr();
|
2017-06-26 11:44:36 +08:00
|
|
|
let mut bytes = vec![0xa5; repr.buffer_len()];
|
2018-07-11 08:22:43 +08:00
|
|
|
let mut packet = Packet::new_unchecked(&mut bytes);
|
2017-10-02 18:47:51 +08:00
|
|
|
repr.emit(&mut packet, &ChecksumCapabilities::default());
|
2016-12-13 06:38:13 +08:00
|
|
|
assert_eq!(&packet.into_inner()[..], &ECHO_PACKET_BYTES[..]);
|
|
|
|
}
|
2018-01-18 09:32:00 +08:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_check_len() {
|
|
|
|
let bytes = [0x0b, 0x00, 0x00, 0x00,
|
|
|
|
0x00, 0x00, 0x00, 0x00];
|
2018-01-27 02:59:11 +08:00
|
|
|
assert_eq!(Packet::new_checked(&[]), Err(Error::Truncated));
|
2018-01-18 09:32:00 +08:00
|
|
|
assert_eq!(Packet::new_checked(&bytes[..4]), Err(Error::Truncated));
|
|
|
|
assert!(Packet::new_checked(&bytes[..]).is_ok());
|
|
|
|
}
|
2016-12-13 06:38:13 +08:00
|
|
|
}
|