mirror of
https://github.com/zaphar/icmp-socket.git
synced 2025-07-21 19:29:47 -04:00
Add other traits for packet construction
This commit is contained in:
parent
3a73734015
commit
6eb42ddf36
274
src/packet.rs
274
src/packet.rs
@ -42,6 +42,7 @@ fn sum_big_endian_words(bs: &[u8]) -> u32 {
|
|||||||
return sum;
|
return sum;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Construct a packet for the EchoRequest messages.
|
||||||
pub trait WithEchoRequest {
|
pub trait WithEchoRequest {
|
||||||
type Packet;
|
type Packet;
|
||||||
|
|
||||||
@ -52,6 +53,44 @@ pub trait WithEchoRequest {
|
|||||||
) -> Result<Self::Packet, IcmpPacketBuildError>;
|
) -> Result<Self::Packet, IcmpPacketBuildError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Construct a packet for Echo Reply messages.
|
||||||
|
pub trait WithEchoReply {
|
||||||
|
type Packet;
|
||||||
|
|
||||||
|
fn with_echo_reply(
|
||||||
|
identifier: u16,
|
||||||
|
sequence: u16,
|
||||||
|
payload: Vec<u8>,
|
||||||
|
) -> Result<Self::Packet, IcmpPacketBuildError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct a packet for Destination Unreachable messages.
|
||||||
|
pub trait WithUnreachable {
|
||||||
|
type Packet;
|
||||||
|
|
||||||
|
fn with_unreachable(code: u8, packet: Vec<u8>) -> Result<Self::Packet, IcmpPacketBuildError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct a packet for Parameter Problem messages.
|
||||||
|
pub trait WithParameterProblem {
|
||||||
|
type Packet;
|
||||||
|
type Pointer;
|
||||||
|
|
||||||
|
fn with_parameter_problem(
|
||||||
|
code: u8,
|
||||||
|
pointer: Self::Pointer,
|
||||||
|
packet: Vec<u8>,
|
||||||
|
) -> Result<Self::Packet, IcmpPacketBuildError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct a packet for Time Exceeded messages.
|
||||||
|
pub trait WithTimeExceeded {
|
||||||
|
type Packet;
|
||||||
|
|
||||||
|
fn with_time_exceeded(code: u8, packet: Vec<u8>) -> Result<Self::Packet, IcmpPacketBuildError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The possible Icmpv6 Message types.
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum Icmpv6Message {
|
pub enum Icmpv6Message {
|
||||||
// NOTE(JWALL): All of the below integers should be parsed as big endian on the
|
// NOTE(JWALL): All of the below integers should be parsed as big endian on the
|
||||||
@ -94,6 +133,7 @@ use Icmpv6Message::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
impl Icmpv6Message {
|
impl Icmpv6Message {
|
||||||
|
/// Get this Icmpv6Message serialized to bytes.
|
||||||
pub fn get_bytes(&self) -> Vec<u8> {
|
pub fn get_bytes(&self) -> Vec<u8> {
|
||||||
let mut bytes = Vec::new();
|
let mut bytes = Vec::new();
|
||||||
match self {
|
match self {
|
||||||
@ -155,9 +195,12 @@ pub struct Icmpv6Packet {
|
|||||||
pub message: Icmpv6Message,
|
pub message: Icmpv6Message,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Error type returned by parsing the ICMP packets.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum PacketParseError {
|
pub enum PacketParseError {
|
||||||
|
/// Not enough bytes to properly parse the packet from.
|
||||||
PacketTooSmall(usize),
|
PacketTooSmall(usize),
|
||||||
|
/// An unrecognized ICMP type.
|
||||||
UnrecognizedICMPType(u8),
|
UnrecognizedICMPType(u8),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -258,24 +301,6 @@ impl Icmpv6Packet {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct a packet for Destination Unreachable messages.
|
|
||||||
pub fn with_unreachable(code: u8, packet: Vec<u8>) -> Result<Self, IcmpPacketBuildError> {
|
|
||||||
if code > 6 {
|
|
||||||
return Err(IcmpPacketBuildError::InvalidCode(code));
|
|
||||||
}
|
|
||||||
Ok(Self {
|
|
||||||
typ: 1,
|
|
||||||
code: code,
|
|
||||||
checksum: 0,
|
|
||||||
// TODO(jwall): Should we enforce that the packet isn't too big?
|
|
||||||
// It is not supposed to be larger than the minimum IPv6 MTU
|
|
||||||
message: Unreachable {
|
|
||||||
_unused: 0,
|
|
||||||
invoking_packet: packet,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Construct a packet for Packet Too Big messages.
|
/// Construct a packet for Packet Too Big messages.
|
||||||
pub fn with_packet_too_big(mtu: u32, packet: Vec<u8>) -> Result<Self, IcmpPacketBuildError> {
|
pub fn with_packet_too_big(mtu: u32, packet: Vec<u8>) -> Result<Self, IcmpPacketBuildError> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
@ -290,62 +315,6 @@ impl Icmpv6Packet {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct a packet for Time Exceeded messages.
|
|
||||||
pub fn with_time_exceeded(code: u8, packet: Vec<u8>) -> Result<Self, IcmpPacketBuildError> {
|
|
||||||
if code > 1 {
|
|
||||||
return Err(IcmpPacketBuildError::InvalidCode(code));
|
|
||||||
}
|
|
||||||
Ok(Self {
|
|
||||||
typ: 3,
|
|
||||||
code: code,
|
|
||||||
checksum: 0,
|
|
||||||
// TODO(jwall): Should we enforce that the packet isn't too big?
|
|
||||||
// It is not supposed to be larger than the minimum IPv6 MTU
|
|
||||||
message: TimeExceeded {
|
|
||||||
_unused: 0,
|
|
||||||
invoking_packet: packet,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Construct a packet for Parameter Problem messages.
|
|
||||||
pub fn with_parameter_problem(
|
|
||||||
code: u8,
|
|
||||||
pointer: u32,
|
|
||||||
packet: Vec<u8>,
|
|
||||||
) -> Result<Self, IcmpPacketBuildError> {
|
|
||||||
if code > 1 {
|
|
||||||
return Err(IcmpPacketBuildError::InvalidCode(code));
|
|
||||||
}
|
|
||||||
Ok(Self {
|
|
||||||
typ: 4,
|
|
||||||
code: code,
|
|
||||||
checksum: 0,
|
|
||||||
message: ParameterProblem {
|
|
||||||
pointer: pointer,
|
|
||||||
invoking_packet: packet,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Construct a packet for Echo Reply messages.
|
|
||||||
pub fn with_echo_reply(
|
|
||||||
identifier: u16,
|
|
||||||
sequence: u16,
|
|
||||||
payload: Vec<u8>,
|
|
||||||
) -> Result<Self, IcmpPacketBuildError> {
|
|
||||||
Ok(Self {
|
|
||||||
typ: 129,
|
|
||||||
code: 0,
|
|
||||||
checksum: 0,
|
|
||||||
message: EchoReply {
|
|
||||||
identifier: identifier,
|
|
||||||
sequence: sequence,
|
|
||||||
payload: payload,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WithEchoRequest for Icmpv6Packet {
|
impl WithEchoRequest for Icmpv6Packet {
|
||||||
@ -369,6 +338,93 @@ impl WithEchoRequest for Icmpv6Packet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl WithEchoReply for Icmpv6Packet {
|
||||||
|
type Packet = Icmpv6Packet;
|
||||||
|
|
||||||
|
fn with_echo_reply(
|
||||||
|
identifier: u16,
|
||||||
|
sequence: u16,
|
||||||
|
payload: Vec<u8>,
|
||||||
|
) -> Result<Self, IcmpPacketBuildError> {
|
||||||
|
Ok(Self {
|
||||||
|
typ: 129,
|
||||||
|
code: 0,
|
||||||
|
checksum: 0,
|
||||||
|
message: EchoReply {
|
||||||
|
identifier: identifier,
|
||||||
|
sequence: sequence,
|
||||||
|
payload: payload,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WithUnreachable for Icmpv6Packet {
|
||||||
|
type Packet = Icmpv6Packet;
|
||||||
|
|
||||||
|
fn with_unreachable(code: u8, packet: Vec<u8>) -> Result<Self, IcmpPacketBuildError> {
|
||||||
|
if code > 6 {
|
||||||
|
return Err(IcmpPacketBuildError::InvalidCode(code));
|
||||||
|
}
|
||||||
|
Ok(Self {
|
||||||
|
typ: 1,
|
||||||
|
code: code,
|
||||||
|
checksum: 0,
|
||||||
|
// TODO(jwall): Should we enforce that the packet isn't too big?
|
||||||
|
// It is not supposed to be larger than the minimum IPv6 MTU
|
||||||
|
message: Unreachable {
|
||||||
|
_unused: 0,
|
||||||
|
invoking_packet: packet,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WithParameterProblem for Icmpv6Packet {
|
||||||
|
type Packet = Icmpv6Packet;
|
||||||
|
type Pointer = u32;
|
||||||
|
|
||||||
|
fn with_parameter_problem(
|
||||||
|
code: u8,
|
||||||
|
pointer: Self::Pointer,
|
||||||
|
packet: Vec<u8>,
|
||||||
|
) -> Result<Self, IcmpPacketBuildError> {
|
||||||
|
if code > 1 {
|
||||||
|
return Err(IcmpPacketBuildError::InvalidCode(code));
|
||||||
|
}
|
||||||
|
Ok(Self {
|
||||||
|
typ: 4,
|
||||||
|
code: code,
|
||||||
|
checksum: 0,
|
||||||
|
message: ParameterProblem {
|
||||||
|
pointer: pointer,
|
||||||
|
invoking_packet: packet,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WithTimeExceeded for Icmpv6Packet {
|
||||||
|
type Packet = Icmpv6Packet;
|
||||||
|
|
||||||
|
fn with_time_exceeded(code: u8, packet: Vec<u8>) -> Result<Self, IcmpPacketBuildError> {
|
||||||
|
if code > 1 {
|
||||||
|
return Err(IcmpPacketBuildError::InvalidCode(code));
|
||||||
|
}
|
||||||
|
Ok(Self {
|
||||||
|
typ: 3,
|
||||||
|
code: code,
|
||||||
|
checksum: 0,
|
||||||
|
// TODO(jwall): Should we enforce that the packet isn't too big?
|
||||||
|
// It is not supposed to be larger than the minimum IPv6 MTU
|
||||||
|
message: TimeExceeded {
|
||||||
|
_unused: 0,
|
||||||
|
invoking_packet: packet,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TryFrom<&[u8]> for Icmpv6Packet {
|
impl TryFrom<&[u8]> for Icmpv6Packet {
|
||||||
type Error = PacketParseError;
|
type Error = PacketParseError;
|
||||||
fn try_from(b: &[u8]) -> Result<Self, Self::Error> {
|
fn try_from(b: &[u8]) -> Result<Self, Self::Error> {
|
||||||
@ -376,8 +432,10 @@ impl TryFrom<&[u8]> for Icmpv6Packet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Errors returned by constructors for a given packet.
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum IcmpPacketBuildError {
|
pub enum IcmpPacketBuildError {
|
||||||
|
/// The code passed in for the payload was invalid for the message type.
|
||||||
InvalidCode(u8),
|
InvalidCode(u8),
|
||||||
}
|
}
|
||||||
use IcmpPacketBuildError::InvalidCode;
|
use IcmpPacketBuildError::InvalidCode;
|
||||||
@ -419,6 +477,7 @@ impl From<PacketParseError> for std::io::Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The various messages for an Icmpv4 packet.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Icmpv4Message {
|
pub enum Icmpv4Message {
|
||||||
Unreachable {
|
Unreachable {
|
||||||
@ -488,6 +547,7 @@ pub enum Icmpv4Message {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Icmpv4Message {
|
impl Icmpv4Message {
|
||||||
|
/// Get this Icmpv4Message serialized as bytes.
|
||||||
pub fn get_bytes(&self) -> Vec<u8> {
|
pub fn get_bytes(&self) -> Vec<u8> {
|
||||||
let mut bytes = Vec::with_capacity(20);
|
let mut bytes = Vec::with_capacity(20);
|
||||||
match self {
|
match self {
|
||||||
@ -599,6 +659,7 @@ impl Icmpv4Message {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An Icmpv4 Packet.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Icmpv4Packet {
|
pub struct Icmpv4Packet {
|
||||||
pub typ: u8,
|
pub typ: u8,
|
||||||
@ -608,6 +669,7 @@ pub struct Icmpv4Packet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Icmpv4Packet {
|
impl Icmpv4Packet {
|
||||||
|
/// Parse an Icmpv4Packet from bytes.
|
||||||
pub fn parse<B: AsRef<[u8]>>(bytes: B) -> Result<Self, PacketParseError> {
|
pub fn parse<B: AsRef<[u8]>>(bytes: B) -> Result<Self, PacketParseError> {
|
||||||
let mut bytes = bytes.as_ref();
|
let mut bytes = bytes.as_ref();
|
||||||
let mut packet_len = bytes.len();
|
let mut packet_len = bytes.len();
|
||||||
@ -721,6 +783,7 @@ impl Icmpv4Packet {
|
|||||||
!sum as u16
|
!sum as u16
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Populate the checksum field of this Packet.
|
||||||
pub fn with_checksum(mut self) -> Self {
|
pub fn with_checksum(mut self) -> Self {
|
||||||
self.checksum = self.calculate_checksum();
|
self.checksum = self.calculate_checksum();
|
||||||
self
|
self
|
||||||
@ -755,6 +818,69 @@ impl WithEchoRequest for Icmpv4Packet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl WithUnreachable for Icmpv4Packet {
|
||||||
|
type Packet = Icmpv4Packet;
|
||||||
|
|
||||||
|
fn with_unreachable(code: u8, packet: Vec<u8>) -> Result<Self::Packet, IcmpPacketBuildError> {
|
||||||
|
if code > 5 {
|
||||||
|
return Err(IcmpPacketBuildError::InvalidCode(code));
|
||||||
|
}
|
||||||
|
Ok(Self {
|
||||||
|
typ: 3,
|
||||||
|
code: code,
|
||||||
|
checksum: 0,
|
||||||
|
message: Icmpv4Message::Unreachable {
|
||||||
|
padding: 0,
|
||||||
|
header: packet,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WithParameterProblem for Icmpv4Packet {
|
||||||
|
type Packet = Icmpv4Packet;
|
||||||
|
type Pointer = u8;
|
||||||
|
|
||||||
|
fn with_parameter_problem(
|
||||||
|
code: u8,
|
||||||
|
pointer: Self::Pointer,
|
||||||
|
packet: Vec<u8>,
|
||||||
|
) -> Result<Self::Packet, IcmpPacketBuildError> {
|
||||||
|
if code != 0 {
|
||||||
|
return Err(IcmpPacketBuildError::InvalidCode(code));
|
||||||
|
}
|
||||||
|
Ok(Self {
|
||||||
|
typ: 12,
|
||||||
|
code: code,
|
||||||
|
checksum: 0,
|
||||||
|
message: Icmpv4Message::ParameterProblem {
|
||||||
|
pointer: pointer,
|
||||||
|
padding: (0, 0),
|
||||||
|
header: packet,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WithTimeExceeded for Icmpv4Packet {
|
||||||
|
type Packet = Icmpv4Packet;
|
||||||
|
|
||||||
|
fn with_time_exceeded(code: u8, packet: Vec<u8>) -> Result<Self::Packet, IcmpPacketBuildError> {
|
||||||
|
if code > 1 {
|
||||||
|
return Err(IcmpPacketBuildError::InvalidCode(code));
|
||||||
|
}
|
||||||
|
Ok(Self {
|
||||||
|
typ: 11,
|
||||||
|
code: code,
|
||||||
|
checksum: 0,
|
||||||
|
message: Icmpv4Message::TimeExceeded {
|
||||||
|
padding: 0,
|
||||||
|
header: packet,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -26,25 +26,37 @@ fn ip_to_socket(ip: &IpAddr) -> SocketAddr {
|
|||||||
SocketAddr::new(*ip, 0)
|
SocketAddr::new(*ip, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Trait for an IcmpSocket implemented by Icmpv4Socket and Icmpv6Socket.
|
||||||
pub trait IcmpSocket {
|
pub trait IcmpSocket {
|
||||||
|
/// The type of address this socket operates on.
|
||||||
type AddrType;
|
type AddrType;
|
||||||
|
/// The type of packet this socket handles.
|
||||||
type PacketType;
|
type PacketType;
|
||||||
|
|
||||||
|
/// Sets the timeout on the socket for rcv_from. A value of 0 will cause
|
||||||
|
/// rcv_from to block.
|
||||||
fn set_timeout(&mut self, timeout: Duration) -> std::io::Result<()>;
|
fn set_timeout(&mut self, timeout: Duration) -> std::io::Result<()>;
|
||||||
|
|
||||||
|
/// Sets the ttl for packets sent on this socket. Controls the number of
|
||||||
|
/// hops the packet will be allowed to traverse.
|
||||||
fn set_max_hops(&mut self, hops: u32);
|
fn set_max_hops(&mut self, hops: u32);
|
||||||
|
|
||||||
|
/// Binds this socket to an address.
|
||||||
fn bind<A: Into<Self::AddrType>>(&mut self, addr: A) -> std::io::Result<()>;
|
fn bind<A: Into<Self::AddrType>>(&mut self, addr: A) -> std::io::Result<()>;
|
||||||
|
|
||||||
|
/// Sends the packet to the given destination.
|
||||||
fn send_to(&mut self, dest: Self::AddrType, packet: Self::PacketType) -> std::io::Result<()>;
|
fn send_to(&mut self, dest: Self::AddrType, packet: Self::PacketType) -> std::io::Result<()>;
|
||||||
|
|
||||||
|
/// Receive a packet on this socket.
|
||||||
fn rcv_from(&mut self) -> std::io::Result<(Self::PacketType, SockAddr)>;
|
fn rcv_from(&mut self) -> std::io::Result<(Self::PacketType, SockAddr)>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Options for this socket.
|
||||||
pub struct Opts {
|
pub struct Opts {
|
||||||
hops: u32,
|
hops: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An ICMPv4 socket.
|
||||||
pub struct IcmpSocket4 {
|
pub struct IcmpSocket4 {
|
||||||
bound_to: Option<Ipv4Addr>,
|
bound_to: Option<Ipv4Addr>,
|
||||||
buf: Vec<u8>,
|
buf: Vec<u8>,
|
||||||
@ -53,6 +65,8 @@ pub struct IcmpSocket4 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl IcmpSocket4 {
|
impl IcmpSocket4 {
|
||||||
|
/// Construct a new socket. The socket must be bound to an address using `bind_to`
|
||||||
|
/// before it can be used to send and receive packets.
|
||||||
pub fn new() -> std::io::Result<Self> {
|
pub fn new() -> std::io::Result<Self> {
|
||||||
let socket = Socket::new(Domain::ipv4(), Type::raw(), Some(Protocol::icmpv4()))?;
|
let socket = Socket::new(Domain::ipv4(), Type::raw(), Some(Protocol::icmpv4()))?;
|
||||||
socket.set_recv_buffer_size(512)?;
|
socket.set_recv_buffer_size(512)?;
|
||||||
@ -100,6 +114,7 @@ impl IcmpSocket for IcmpSocket4 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An Icmpv6 socket.
|
||||||
pub struct IcmpSocket6 {
|
pub struct IcmpSocket6 {
|
||||||
bound_to: Option<Ipv6Addr>,
|
bound_to: Option<Ipv6Addr>,
|
||||||
inner: Socket,
|
inner: Socket,
|
||||||
@ -108,6 +123,8 @@ pub struct IcmpSocket6 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl IcmpSocket6 {
|
impl IcmpSocket6 {
|
||||||
|
/// Construct a new socket. The socket must be bound to an address using `bind_to`
|
||||||
|
/// before it can be used to send and receive packets.
|
||||||
pub fn new() -> std::io::Result<Self> {
|
pub fn new() -> std::io::Result<Self> {
|
||||||
let socket = Socket::new(Domain::ipv6(), Type::raw(), Some(Protocol::icmpv6()))?;
|
let socket = Socket::new(Domain::ipv6(), Type::raw(), Some(Protocol::icmpv6()))?;
|
||||||
socket.set_recv_buffer_size(512)?;
|
socket.set_recv_buffer_size(512)?;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user