Add other traits for packet construction

This commit is contained in:
Jeremy Wall 2021-02-01 18:18:08 -05:00
parent 3a73734015
commit 6eb42ddf36
2 changed files with 217 additions and 74 deletions

View File

@ -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::*;

View File

@ -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)?;