use crate::buffer::Buffer;
use crate::error::{Error, Result};
use std::fmt::{Debug, Formatter};
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub enum IcmpType {
EchoRequest,
EchoReply,
DestinationUnreachable,
TimeExceeded,
Other(u8),
}
impl IcmpType {
#[must_use]
pub const fn id(&self) -> u8 {
match self {
Self::EchoRequest => 8,
Self::EchoReply => 0,
Self::DestinationUnreachable => 3,
Self::TimeExceeded => 11,
Self::Other(id) => *id,
}
}
}
impl From<u8> for IcmpType {
fn from(val: u8) -> Self {
match val {
8 => Self::EchoRequest,
0 => Self::EchoReply,
3 => Self::DestinationUnreachable,
11 => Self::TimeExceeded,
id => Self::Other(id),
}
}
}
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub struct IcmpCode(pub u8);
impl From<u8> for IcmpCode {
fn from(val: u8) -> Self {
Self(val)
}
}
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub enum IcmpTimeExceededCode {
TtlExpired,
FragmentReassembly,
Unknown(u8),
}
impl From<IcmpCode> for IcmpTimeExceededCode {
fn from(val: IcmpCode) -> Self {
match val {
IcmpCode(0) => Self::TtlExpired,
IcmpCode(1) => Self::FragmentReassembly,
IcmpCode(id) => Self::Unknown(id),
}
}
}
const TYPE_OFFSET: usize = 0;
const CODE_OFFSET: usize = 1;
const CHECKSUM_OFFSET: usize = 2;
pub struct IcmpPacket<'a> {
buf: Buffer<'a>,
}
impl<'a> IcmpPacket<'a> {
pub fn new(packet: &'a mut [u8]) -> Result<Self> {
if packet.len() >= Self::minimum_packet_size() {
Ok(Self {
buf: Buffer::Mutable(packet),
})
} else {
Err(Error::InsufficientPacketBuffer(
String::from("IcmpPacket"),
Self::minimum_packet_size(),
packet.len(),
))
}
}
pub fn new_view(packet: &'a [u8]) -> Result<Self> {
if packet.len() >= Self::minimum_packet_size() {
Ok(Self {
buf: Buffer::Immutable(packet),
})
} else {
Err(Error::InsufficientPacketBuffer(
String::from("IcmpPacket"),
Self::minimum_packet_size(),
packet.len(),
))
}
}
#[must_use]
pub const fn minimum_packet_size() -> usize {
8
}
#[must_use]
pub fn get_icmp_type(&self) -> IcmpType {
IcmpType::from(self.buf.read(TYPE_OFFSET))
}
#[must_use]
pub fn get_icmp_code(&self) -> IcmpCode {
IcmpCode::from(self.buf.read(CODE_OFFSET))
}
#[must_use]
pub fn get_checksum(&self) -> u16 {
u16::from_be_bytes(self.buf.get_bytes(CHECKSUM_OFFSET))
}
pub fn set_icmp_type(&mut self, val: IcmpType) {
*self.buf.write(TYPE_OFFSET) = val.id();
}
pub fn set_icmp_code(&mut self, val: IcmpCode) {
*self.buf.write(CODE_OFFSET) = val.0;
}
pub fn set_checksum(&mut self, val: u16) {
self.buf.set_bytes(CHECKSUM_OFFSET, val.to_be_bytes());
}
#[must_use]
pub fn packet(&self) -> &[u8] {
self.buf.as_slice()
}
}
impl Debug for IcmpPacket<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("IcmpPacket")
.field("icmp_type", &self.get_icmp_type())
.field("icmp_code", &self.get_icmp_code())
.field("checksum", &self.get_checksum())
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_icmp_type() {
let mut buf = [0_u8; IcmpPacket::minimum_packet_size()];
let mut packet = IcmpPacket::new(&mut buf).unwrap();
packet.set_icmp_type(IcmpType::EchoRequest);
assert_eq!(IcmpType::EchoRequest, packet.get_icmp_type());
assert_eq!([0x08], packet.packet()[0..1]);
packet.set_icmp_type(IcmpType::EchoReply);
assert_eq!(IcmpType::EchoReply, packet.get_icmp_type());
assert_eq!([0x00], packet.packet()[0..1]);
packet.set_icmp_type(IcmpType::DestinationUnreachable);
assert_eq!(IcmpType::DestinationUnreachable, packet.get_icmp_type());
assert_eq!([0x03], packet.packet()[0..1]);
packet.set_icmp_type(IcmpType::TimeExceeded);
assert_eq!(IcmpType::TimeExceeded, packet.get_icmp_type());
assert_eq!([0x0B], packet.packet()[0..1]);
packet.set_icmp_type(IcmpType::Other(255));
assert_eq!(IcmpType::Other(255), packet.get_icmp_type());
assert_eq!([0xFF], packet.packet()[0..1]);
}
#[test]
fn test_icmp_code() {
let mut buf = [0_u8; IcmpPacket::minimum_packet_size()];
let mut packet = IcmpPacket::new(&mut buf).unwrap();
packet.set_icmp_code(IcmpCode(0));
assert_eq!(IcmpCode(0), packet.get_icmp_code());
assert_eq!([0x00], packet.packet()[1..2]);
packet.set_icmp_code(IcmpCode(5));
assert_eq!(IcmpCode(5), packet.get_icmp_code());
assert_eq!([0x05], packet.packet()[1..2]);
packet.set_icmp_code(IcmpCode(255));
assert_eq!(IcmpCode(255), packet.get_icmp_code());
assert_eq!([0xFF], packet.packet()[1..2]);
}
#[test]
fn test_checksum() {
let mut buf = [0_u8; IcmpPacket::minimum_packet_size()];
let mut packet = IcmpPacket::new(&mut buf).unwrap();
packet.set_checksum(0);
assert_eq!(0, packet.get_checksum());
assert_eq!([0x00, 0x00], packet.packet()[2..=3]);
packet.set_checksum(1999);
assert_eq!(1999, packet.get_checksum());
assert_eq!([0x07, 0xCF], packet.packet()[2..=3]);
packet.set_checksum(u16::MAX);
assert_eq!(u16::MAX, packet.get_checksum());
assert_eq!([0xFF, 0xFF], packet.packet()[2..=3]);
}
#[test]
fn test_new_insufficient_buffer() {
const SIZE: usize = IcmpPacket::minimum_packet_size();
let mut buf = [0_u8; SIZE - 1];
let err = IcmpPacket::new(&mut buf).unwrap_err();
assert_eq!(
Error::InsufficientPacketBuffer(String::from("IcmpPacket"), SIZE, SIZE - 1),
err
);
}
#[test]
fn test_new_view_insufficient_buffer() {
const SIZE: usize = IcmpPacket::minimum_packet_size();
let buf = [0_u8; SIZE - 1];
let err = IcmpPacket::new_view(&buf).unwrap_err();
assert_eq!(
Error::InsufficientPacketBuffer(String::from("IcmpPacket"), SIZE, SIZE - 1),
err
);
}
}
pub mod echo_request {
use crate::buffer::Buffer;
use crate::error::{Error, Result};
use crate::fmt_payload;
use crate::icmpv4::{IcmpCode, IcmpType};
use std::fmt::{Debug, Formatter};
const TYPE_OFFSET: usize = 0;
const CODE_OFFSET: usize = 1;
const CHECKSUM_OFFSET: usize = 2;
const IDENTIFIER_OFFSET: usize = 4;
const SEQUENCE_OFFSET: usize = 6;
pub struct EchoRequestPacket<'a> {
buf: Buffer<'a>,
}
impl<'a> EchoRequestPacket<'a> {
pub fn new(packet: &'a mut [u8]) -> Result<Self> {
if packet.len() >= Self::minimum_packet_size() {
Ok(Self {
buf: Buffer::Mutable(packet),
})
} else {
Err(Error::InsufficientPacketBuffer(
String::from("EchoRequestPacket"),
Self::minimum_packet_size(),
packet.len(),
))
}
}
pub fn new_view(packet: &'a [u8]) -> Result<Self> {
if packet.len() >= Self::minimum_packet_size() {
Ok(Self {
buf: Buffer::Immutable(packet),
})
} else {
Err(Error::InsufficientPacketBuffer(
String::from("EchoRequestPacket"),
Self::minimum_packet_size(),
packet.len(),
))
}
}
#[must_use]
pub const fn minimum_packet_size() -> usize {
8
}
#[must_use]
pub fn get_icmp_type(&self) -> IcmpType {
IcmpType::from(self.buf.read(TYPE_OFFSET))
}
#[must_use]
pub fn get_icmp_code(&self) -> IcmpCode {
IcmpCode::from(self.buf.read(CODE_OFFSET))
}
#[must_use]
pub fn get_checksum(&self) -> u16 {
u16::from_be_bytes(self.buf.get_bytes(CHECKSUM_OFFSET))
}
#[must_use]
pub fn get_identifier(&self) -> u16 {
u16::from_be_bytes(self.buf.get_bytes(IDENTIFIER_OFFSET))
}
#[must_use]
pub fn get_sequence(&self) -> u16 {
u16::from_be_bytes(self.buf.get_bytes(SEQUENCE_OFFSET))
}
pub fn set_icmp_type(&mut self, val: IcmpType) {
*self.buf.write(TYPE_OFFSET) = val.id();
}
pub fn set_icmp_code(&mut self, val: IcmpCode) {
*self.buf.write(CODE_OFFSET) = val.0;
}
pub fn set_checksum(&mut self, val: u16) {
self.buf.set_bytes(CHECKSUM_OFFSET, val.to_be_bytes());
}
pub fn set_identifier(&mut self, val: u16) {
self.buf.set_bytes(IDENTIFIER_OFFSET, val.to_be_bytes());
}
pub fn set_sequence(&mut self, val: u16) {
self.buf.set_bytes(SEQUENCE_OFFSET, val.to_be_bytes());
}
pub fn set_payload(&mut self, vals: &[u8]) {
let current_offset = Self::minimum_packet_size();
self.buf.as_slice_mut()[current_offset..current_offset + vals.len()]
.copy_from_slice(vals);
}
#[must_use]
pub fn packet(&self) -> &[u8] {
self.buf.as_slice()
}
#[must_use]
pub fn payload(&self) -> &[u8] {
&self.buf.as_slice()[Self::minimum_packet_size()..]
}
}
impl Debug for EchoRequestPacket<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("EchoRequestPacket")
.field("icmp_type", &self.get_icmp_type())
.field("icmp_code", &self.get_icmp_code())
.field("checksum", &self.get_checksum())
.field("identifier", &self.get_identifier())
.field("sequence", &self.get_sequence())
.field("payload", &fmt_payload(self.payload()))
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_icmp_type() {
let mut buf = [0_u8; EchoRequestPacket::minimum_packet_size()];
let mut packet = EchoRequestPacket::new(&mut buf).unwrap();
packet.set_icmp_type(IcmpType::EchoRequest);
assert_eq!(IcmpType::EchoRequest, packet.get_icmp_type());
assert_eq!([0x08], packet.packet()[0..1]);
packet.set_icmp_type(IcmpType::EchoReply);
assert_eq!(IcmpType::EchoReply, packet.get_icmp_type());
assert_eq!([0x00], packet.packet()[0..1]);
packet.set_icmp_type(IcmpType::DestinationUnreachable);
assert_eq!(IcmpType::DestinationUnreachable, packet.get_icmp_type());
assert_eq!([0x03], packet.packet()[0..1]);
packet.set_icmp_type(IcmpType::TimeExceeded);
assert_eq!(IcmpType::TimeExceeded, packet.get_icmp_type());
assert_eq!([0x0B], packet.packet()[0..1]);
packet.set_icmp_type(IcmpType::Other(255));
assert_eq!(IcmpType::Other(255), packet.get_icmp_type());
assert_eq!([0xFF], packet.packet()[0..1]);
}
#[test]
fn test_icmp_code() {
let mut buf = [0_u8; EchoRequestPacket::minimum_packet_size()];
let mut packet = EchoRequestPacket::new(&mut buf).unwrap();
packet.set_icmp_code(IcmpCode(0));
assert_eq!(IcmpCode(0), packet.get_icmp_code());
assert_eq!([0x00], packet.packet()[1..2]);
packet.set_icmp_code(IcmpCode(5));
assert_eq!(IcmpCode(5), packet.get_icmp_code());
assert_eq!([0x05], packet.packet()[1..2]);
packet.set_icmp_code(IcmpCode(255));
assert_eq!(IcmpCode(255), packet.get_icmp_code());
assert_eq!([0xFF], packet.packet()[1..2]);
}
#[test]
fn test_checksum() {
let mut buf = [0_u8; EchoRequestPacket::minimum_packet_size()];
let mut packet = EchoRequestPacket::new(&mut buf).unwrap();
packet.set_checksum(0);
assert_eq!(0, packet.get_checksum());
assert_eq!([0x00, 0x00], packet.packet()[2..=3]);
packet.set_checksum(1999);
assert_eq!(1999, packet.get_checksum());
assert_eq!([0x07, 0xCF], packet.packet()[2..=3]);
packet.set_checksum(u16::MAX);
assert_eq!(u16::MAX, packet.get_checksum());
assert_eq!([0xFF, 0xFF], packet.packet()[2..=3]);
}
#[test]
fn test_identifier() {
let mut buf = [0_u8; EchoRequestPacket::minimum_packet_size()];
let mut packet = EchoRequestPacket::new(&mut buf).unwrap();
packet.set_identifier(0);
assert_eq!(0, packet.get_identifier());
assert_eq!([0x00, 0x00], packet.packet()[4..=5]);
packet.set_identifier(1999);
assert_eq!(1999, packet.get_identifier());
assert_eq!([0x07, 0xCF], packet.packet()[4..=5]);
packet.set_identifier(u16::MAX);
assert_eq!(u16::MAX, packet.get_identifier());
assert_eq!([0xFF, 0xFF], packet.packet()[4..=5]);
}
#[test]
fn test_sequence() {
let mut buf = [0_u8; EchoRequestPacket::minimum_packet_size()];
let mut packet = EchoRequestPacket::new(&mut buf).unwrap();
packet.set_sequence(0);
assert_eq!(0, packet.get_sequence());
assert_eq!([0x00, 0x00], packet.packet()[6..=7]);
packet.set_sequence(1999);
assert_eq!(1999, packet.get_sequence());
assert_eq!([0x07, 0xCF], packet.packet()[6..=7]);
packet.set_sequence(u16::MAX);
assert_eq!(u16::MAX, packet.get_sequence());
assert_eq!([0xFF, 0xFF], packet.packet()[6..=7]);
}
#[test]
fn test_view() {
let buf = [0x08, 0x00, 0x16, 0x7c, 0x60, 0x9b, 0x82, 0x9a];
let packet = EchoRequestPacket::new_view(&buf).unwrap();
assert_eq!(IcmpType::EchoRequest, packet.get_icmp_type());
assert_eq!(IcmpCode(0), packet.get_icmp_code());
assert_eq!(5756, packet.get_checksum());
assert_eq!(24731, packet.get_identifier());
assert_eq!(33434, packet.get_sequence());
assert!(packet.payload().is_empty());
}
#[test]
fn test_new_insufficient_buffer() {
const SIZE: usize = EchoRequestPacket::minimum_packet_size();
let mut buf = [0_u8; SIZE - 1];
let err = EchoRequestPacket::new(&mut buf).unwrap_err();
assert_eq!(
Error::InsufficientPacketBuffer(String::from("EchoRequestPacket"), SIZE, SIZE - 1),
err
);
}
#[test]
fn test_new_view_insufficient_buffer() {
const SIZE: usize = EchoRequestPacket::minimum_packet_size();
let buf = [0_u8; SIZE - 1];
let err = EchoRequestPacket::new_view(&buf).unwrap_err();
assert_eq!(
Error::InsufficientPacketBuffer(String::from("EchoRequestPacket"), SIZE, SIZE - 1),
err
);
}
}
}
pub mod echo_reply {
use crate::buffer::Buffer;
use crate::error::{Error, Result};
use crate::fmt_payload;
use crate::icmpv4::{IcmpCode, IcmpType};
use std::fmt::{Debug, Formatter};
const TYPE_OFFSET: usize = 0;
const CODE_OFFSET: usize = 1;
const CHECKSUM_OFFSET: usize = 2;
const IDENTIFIER_OFFSET: usize = 4;
const SEQUENCE_OFFSET: usize = 6;
pub struct EchoReplyPacket<'a> {
buf: Buffer<'a>,
}
impl<'a> EchoReplyPacket<'a> {
pub fn new(packet: &'a mut [u8]) -> Result<Self> {
if packet.len() >= Self::minimum_packet_size() {
Ok(Self {
buf: Buffer::Mutable(packet),
})
} else {
Err(Error::InsufficientPacketBuffer(
String::from("EchoReplyPacket"),
Self::minimum_packet_size(),
packet.len(),
))
}
}
pub fn new_view(packet: &'a [u8]) -> Result<Self> {
if packet.len() >= Self::minimum_packet_size() {
Ok(Self {
buf: Buffer::Immutable(packet),
})
} else {
Err(Error::InsufficientPacketBuffer(
String::from("EchoReplyPacket"),
Self::minimum_packet_size(),
packet.len(),
))
}
}
#[must_use]
pub const fn minimum_packet_size() -> usize {
8
}
#[must_use]
pub fn get_icmp_type(&self) -> IcmpType {
IcmpType::from(self.buf.read(TYPE_OFFSET))
}
#[must_use]
pub fn get_icmp_code(&self) -> IcmpCode {
IcmpCode::from(self.buf.read(CODE_OFFSET))
}
#[must_use]
pub fn get_checksum(&self) -> u16 {
u16::from_be_bytes(self.buf.get_bytes(CHECKSUM_OFFSET))
}
#[must_use]
pub fn get_identifier(&self) -> u16 {
u16::from_be_bytes(self.buf.get_bytes(IDENTIFIER_OFFSET))
}
#[must_use]
pub fn get_sequence(&self) -> u16 {
u16::from_be_bytes(self.buf.get_bytes(SEQUENCE_OFFSET))
}
pub fn set_icmp_type(&mut self, val: IcmpType) {
*self.buf.write(TYPE_OFFSET) = val.id();
}
pub fn set_icmp_code(&mut self, val: IcmpCode) {
*self.buf.write(CODE_OFFSET) = val.0;
}
pub fn set_checksum(&mut self, val: u16) {
self.buf.set_bytes(CHECKSUM_OFFSET, val.to_be_bytes());
}
pub fn set_identifier(&mut self, val: u16) {
self.buf.set_bytes(IDENTIFIER_OFFSET, val.to_be_bytes());
}
pub fn set_sequence(&mut self, val: u16) {
self.buf.set_bytes(SEQUENCE_OFFSET, val.to_be_bytes());
}
pub fn set_payload(&mut self, vals: &[u8]) {
let current_offset = Self::minimum_packet_size();
self.buf.as_slice_mut()[current_offset..current_offset + vals.len()]
.copy_from_slice(vals);
}
#[must_use]
pub fn packet(&self) -> &[u8] {
self.buf.as_slice()
}
#[must_use]
pub fn payload(&self) -> &[u8] {
&self.buf.as_slice()[Self::minimum_packet_size()..]
}
}
impl Debug for EchoReplyPacket<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("EchoReplyPacket")
.field("icmp_type", &self.get_icmp_type())
.field("icmp_code", &self.get_icmp_code())
.field("checksum", &self.get_checksum())
.field("identifier", &self.get_identifier())
.field("sequence", &self.get_sequence())
.field("payload", &fmt_payload(self.payload()))
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_icmp_type() {
let mut buf = [0_u8; EchoReplyPacket::minimum_packet_size()];
let mut packet = EchoReplyPacket::new(&mut buf).unwrap();
packet.set_icmp_type(IcmpType::EchoRequest);
assert_eq!(IcmpType::EchoRequest, packet.get_icmp_type());
assert_eq!([0x08], packet.packet()[0..1]);
packet.set_icmp_type(IcmpType::EchoReply);
assert_eq!(IcmpType::EchoReply, packet.get_icmp_type());
assert_eq!([0x00], packet.packet()[0..1]);
packet.set_icmp_type(IcmpType::DestinationUnreachable);
assert_eq!(IcmpType::DestinationUnreachable, packet.get_icmp_type());
assert_eq!([0x03], packet.packet()[0..1]);
packet.set_icmp_type(IcmpType::TimeExceeded);
assert_eq!(IcmpType::TimeExceeded, packet.get_icmp_type());
assert_eq!([0x0B], packet.packet()[0..1]);
packet.set_icmp_type(IcmpType::Other(255));
assert_eq!(IcmpType::Other(255), packet.get_icmp_type());
assert_eq!([0xFF], packet.packet()[0..1]);
}
#[test]
fn test_icmp_code() {
let mut buf = [0_u8; EchoReplyPacket::minimum_packet_size()];
let mut packet = EchoReplyPacket::new(&mut buf).unwrap();
packet.set_icmp_code(IcmpCode(0));
assert_eq!(IcmpCode(0), packet.get_icmp_code());
assert_eq!([0x00], packet.packet()[1..2]);
packet.set_icmp_code(IcmpCode(5));
assert_eq!(IcmpCode(5), packet.get_icmp_code());
assert_eq!([0x05], packet.packet()[1..2]);
packet.set_icmp_code(IcmpCode(255));
assert_eq!(IcmpCode(255), packet.get_icmp_code());
assert_eq!([0xFF], packet.packet()[1..2]);
}
#[test]
fn test_checksum() {
let mut buf = [0_u8; EchoReplyPacket::minimum_packet_size()];
let mut packet = EchoReplyPacket::new(&mut buf).unwrap();
packet.set_checksum(0);
assert_eq!(0, packet.get_checksum());
assert_eq!([0x00, 0x00], packet.packet()[2..=3]);
packet.set_checksum(1999);
assert_eq!(1999, packet.get_checksum());
assert_eq!([0x07, 0xCF], packet.packet()[2..=3]);
packet.set_checksum(u16::MAX);
assert_eq!(u16::MAX, packet.get_checksum());
assert_eq!([0xFF, 0xFF], packet.packet()[2..=3]);
}
#[test]
fn test_identifier() {
let mut buf = [0_u8; EchoReplyPacket::minimum_packet_size()];
let mut packet = EchoReplyPacket::new(&mut buf).unwrap();
packet.set_identifier(0);
assert_eq!(0, packet.get_identifier());
assert_eq!([0x00, 0x00], packet.packet()[4..=5]);
packet.set_identifier(1999);
assert_eq!(1999, packet.get_identifier());
assert_eq!([0x07, 0xCF], packet.packet()[4..=5]);
packet.set_identifier(u16::MAX);
assert_eq!(u16::MAX, packet.get_identifier());
assert_eq!([0xFF, 0xFF], packet.packet()[4..=5]);
}
#[test]
fn test_sequence() {
let mut buf = [0_u8; EchoReplyPacket::minimum_packet_size()];
let mut packet = EchoReplyPacket::new(&mut buf).unwrap();
packet.set_sequence(0);
assert_eq!(0, packet.get_sequence());
assert_eq!([0x00, 0x00], packet.packet()[6..=7]);
packet.set_sequence(1999);
assert_eq!(1999, packet.get_sequence());
assert_eq!([0x07, 0xCF], packet.packet()[6..=7]);
packet.set_sequence(u16::MAX);
assert_eq!(u16::MAX, packet.get_sequence());
assert_eq!([0xFF, 0xFF], packet.packet()[6..=7]);
}
#[test]
fn test_view() {
let buf = [0x00, 0x00, 0x1e, 0x70, 0x60, 0x9b, 0x80, 0xf4];
let packet = EchoReplyPacket::new_view(&buf).unwrap();
assert_eq!(IcmpType::EchoReply, packet.get_icmp_type());
assert_eq!(IcmpCode(0), packet.get_icmp_code());
assert_eq!(7792, packet.get_checksum());
assert_eq!(24731, packet.get_identifier());
assert_eq!(33012, packet.get_sequence());
assert!(packet.payload().is_empty());
}
#[test]
fn test_new_insufficient_buffer() {
const SIZE: usize = EchoReplyPacket::minimum_packet_size();
let mut buf = [0_u8; SIZE - 1];
let err = EchoReplyPacket::new(&mut buf).unwrap_err();
assert_eq!(
Error::InsufficientPacketBuffer(String::from("EchoReplyPacket"), SIZE, SIZE - 1),
err
);
}
#[test]
fn test_new_view_insufficient_buffer() {
const SIZE: usize = EchoReplyPacket::minimum_packet_size();
let buf = [0_u8; SIZE - 1];
let err = EchoReplyPacket::new_view(&buf).unwrap_err();
assert_eq!(
Error::InsufficientPacketBuffer(String::from("EchoReplyPacket"), SIZE, SIZE - 1),
err
);
}
}
}
pub mod time_exceeded {
use crate::buffer::Buffer;
use crate::error::{Error, Result};
use crate::fmt_payload;
use crate::icmp_extension::extension_splitter::split;
use crate::icmpv4::{IcmpCode, IcmpType};
use std::fmt::{Debug, Formatter};
const TYPE_OFFSET: usize = 0;
const CODE_OFFSET: usize = 1;
const CHECKSUM_OFFSET: usize = 2;
const LENGTH_OFFSET: usize = 5;
pub struct TimeExceededPacket<'a> {
buf: Buffer<'a>,
}
impl<'a> TimeExceededPacket<'a> {
pub fn new(packet: &'a mut [u8]) -> Result<Self> {
if packet.len() >= Self::minimum_packet_size() {
Ok(Self {
buf: Buffer::Mutable(packet),
})
} else {
Err(Error::InsufficientPacketBuffer(
String::from("TimeExceededPacket"),
Self::minimum_packet_size(),
packet.len(),
))
}
}
pub fn new_view(packet: &'a [u8]) -> Result<Self> {
if packet.len() >= Self::minimum_packet_size() {
Ok(Self {
buf: Buffer::Immutable(packet),
})
} else {
Err(Error::InsufficientPacketBuffer(
String::from("TimeExceededPacket"),
Self::minimum_packet_size(),
packet.len(),
))
}
}
#[must_use]
pub const fn minimum_packet_size() -> usize {
8
}
#[must_use]
pub fn get_icmp_type(&self) -> IcmpType {
IcmpType::from(self.buf.read(TYPE_OFFSET))
}
#[must_use]
pub fn get_icmp_code(&self) -> IcmpCode {
IcmpCode::from(self.buf.read(CODE_OFFSET))
}
#[must_use]
pub fn get_checksum(&self) -> u16 {
u16::from_be_bytes(self.buf.get_bytes(CHECKSUM_OFFSET))
}
#[must_use]
pub fn get_length(&self) -> u8 {
self.buf.read(LENGTH_OFFSET)
}
pub fn set_icmp_type(&mut self, val: IcmpType) {
*self.buf.write(TYPE_OFFSET) = val.id();
}
pub fn set_icmp_code(&mut self, val: IcmpCode) {
*self.buf.write(CODE_OFFSET) = val.0;
}
pub fn set_checksum(&mut self, val: u16) {
self.buf.set_bytes(CHECKSUM_OFFSET, val.to_be_bytes());
}
pub fn set_length(&mut self, val: u8) {
*self.buf.write(LENGTH_OFFSET) = val;
}
pub fn set_payload(&mut self, vals: &[u8]) {
let current_offset = Self::minimum_packet_size();
self.buf.as_slice_mut()[current_offset..current_offset + vals.len()]
.copy_from_slice(vals);
}
#[must_use]
pub fn packet(&self) -> &[u8] {
self.buf.as_slice()
}
#[must_use]
pub fn payload(&self) -> &[u8] {
let (payload, _) = self.split_payload_extension();
payload
}
#[must_use]
pub fn payload_raw(&self) -> &[u8] {
&self.buf.as_slice()[Self::minimum_packet_size()..]
}
#[must_use]
pub fn extension(&self) -> Option<&[u8]> {
let (_, extension) = self.split_payload_extension();
extension
}
fn split_payload_extension(&self) -> (&[u8], Option<&[u8]>) {
let length = usize::from(self.get_length()) * 4;
let icmp_payload = &self.buf.as_slice()[Self::minimum_packet_size()..];
split(length, icmp_payload)
}
}
impl Debug for TimeExceededPacket<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("TimeExceededPacket")
.field("icmp_type", &self.get_icmp_type())
.field("icmp_code", &self.get_icmp_code())
.field("checksum", &self.get_checksum())
.field("length", &self.get_length())
.field("payload", &fmt_payload(self.payload()))
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_icmp_type() {
let mut buf = [0_u8; TimeExceededPacket::minimum_packet_size()];
let mut packet = TimeExceededPacket::new(&mut buf).unwrap();
packet.set_icmp_type(IcmpType::EchoRequest);
assert_eq!(IcmpType::EchoRequest, packet.get_icmp_type());
assert_eq!([0x08], packet.packet()[0..1]);
packet.set_icmp_type(IcmpType::EchoReply);
assert_eq!(IcmpType::EchoReply, packet.get_icmp_type());
assert_eq!([0x00], packet.packet()[0..1]);
packet.set_icmp_type(IcmpType::DestinationUnreachable);
assert_eq!(IcmpType::DestinationUnreachable, packet.get_icmp_type());
assert_eq!([0x03], packet.packet()[0..1]);
packet.set_icmp_type(IcmpType::TimeExceeded);
assert_eq!(IcmpType::TimeExceeded, packet.get_icmp_type());
assert_eq!([0x0B], packet.packet()[0..1]);
packet.set_icmp_type(IcmpType::Other(255));
assert_eq!(IcmpType::Other(255), packet.get_icmp_type());
assert_eq!([0xFF], packet.packet()[0..1]);
}
#[test]
fn test_icmp_code() {
let mut buf = [0_u8; TimeExceededPacket::minimum_packet_size()];
let mut packet = TimeExceededPacket::new(&mut buf).unwrap();
packet.set_icmp_code(IcmpCode(0));
assert_eq!(IcmpCode(0), packet.get_icmp_code());
assert_eq!([0x00], packet.packet()[1..2]);
packet.set_icmp_code(IcmpCode(5));
assert_eq!(IcmpCode(5), packet.get_icmp_code());
assert_eq!([0x05], packet.packet()[1..2]);
packet.set_icmp_code(IcmpCode(255));
assert_eq!(IcmpCode(255), packet.get_icmp_code());
assert_eq!([0xFF], packet.packet()[1..2]);
}
#[test]
fn test_checksum() {
let mut buf = [0_u8; TimeExceededPacket::minimum_packet_size()];
let mut packet = TimeExceededPacket::new(&mut buf).unwrap();
packet.set_checksum(0);
assert_eq!(0, packet.get_checksum());
assert_eq!([0x00, 0x00], packet.packet()[2..=3]);
packet.set_checksum(1999);
assert_eq!(1999, packet.get_checksum());
assert_eq!([0x07, 0xCF], packet.packet()[2..=3]);
packet.set_checksum(u16::MAX);
assert_eq!(u16::MAX, packet.get_checksum());
assert_eq!([0xFF, 0xFF], packet.packet()[2..=3]);
}
#[test]
fn test_length() {
let mut buf = [0_u8; TimeExceededPacket::minimum_packet_size()];
let mut packet = TimeExceededPacket::new(&mut buf).unwrap();
packet.set_length(0);
assert_eq!(0, packet.get_length());
assert_eq!([0x00], packet.packet()[5..6]);
packet.set_length(8);
assert_eq!(8, packet.get_length());
assert_eq!([0x08], packet.packet()[5..6]);
packet.set_length(u8::MAX);
assert_eq!(u8::MAX, packet.get_length());
assert_eq!([0xFF], packet.packet()[5..6]);
}
#[test]
fn test_view() {
let buf = [0x0b, 0x00, 0xf4, 0xee, 0x00, 0x11, 0x00, 0x00];
let packet = TimeExceededPacket::new_view(&buf).unwrap();
assert_eq!(IcmpType::TimeExceeded, packet.get_icmp_type());
assert_eq!(IcmpCode(0), packet.get_icmp_code());
assert_eq!(62702, packet.get_checksum());
assert_eq!(17, packet.get_length());
assert!(packet.payload().is_empty());
}
#[test]
fn test_view_large() {
let mut buf = [0x0_u8; 256];
buf[..8].copy_from_slice(&[0x0b, 0x00, 0xf4, 0xee, 0x00, 0x40, 0x00, 0x00]);
let packet = TimeExceededPacket::new_view(&buf).unwrap();
assert_eq!(IcmpType::TimeExceeded, packet.get_icmp_type());
assert_eq!(IcmpCode(0), packet.get_icmp_code());
assert_eq!(62702, packet.get_checksum());
assert_eq!(64, packet.get_length());
assert_eq!(&[0x0_u8; 248], packet.payload());
assert_eq!(None, packet.extension());
}
#[test]
fn test_new_insufficient_buffer() {
const SIZE: usize = TimeExceededPacket::minimum_packet_size();
let mut buf = [0_u8; SIZE - 1];
let err = TimeExceededPacket::new(&mut buf).unwrap_err();
assert_eq!(
Error::InsufficientPacketBuffer(String::from("TimeExceededPacket"), SIZE, SIZE - 1),
err
);
}
#[test]
fn test_new_view_insufficient_buffer() {
const SIZE: usize = TimeExceededPacket::minimum_packet_size();
let buf = [0_u8; SIZE - 1];
let err = TimeExceededPacket::new_view(&buf).unwrap_err();
assert_eq!(
Error::InsufficientPacketBuffer(String::from("TimeExceededPacket"), SIZE, SIZE - 1),
err
);
}
}
}
pub mod destination_unreachable {
use crate::buffer::Buffer;
use crate::error::{Error, Result};
use crate::fmt_payload;
use crate::icmp_extension::extension_splitter::split;
use crate::icmpv4::{IcmpCode, IcmpType};
use std::fmt::{Debug, Formatter};
const TYPE_OFFSET: usize = 0;
const CODE_OFFSET: usize = 1;
const CHECKSUM_OFFSET: usize = 2;
const LENGTH_OFFSET: usize = 5;
const NEXT_HOP_MTU_OFFSET: usize = 6;
pub struct DestinationUnreachablePacket<'a> {
buf: Buffer<'a>,
}
impl<'a> DestinationUnreachablePacket<'a> {
pub fn new(packet: &'a mut [u8]) -> Result<Self> {
if packet.len() >= Self::minimum_packet_size() {
Ok(Self {
buf: Buffer::Mutable(packet),
})
} else {
Err(Error::InsufficientPacketBuffer(
String::from("DestinationUnreachablePacket"),
Self::minimum_packet_size(),
packet.len(),
))
}
}
pub fn new_view(packet: &'a [u8]) -> Result<Self> {
if packet.len() >= Self::minimum_packet_size() {
Ok(Self {
buf: Buffer::Immutable(packet),
})
} else {
Err(Error::InsufficientPacketBuffer(
String::from("DestinationUnreachablePacket"),
Self::minimum_packet_size(),
packet.len(),
))
}
}
#[must_use]
pub const fn minimum_packet_size() -> usize {
8
}
#[must_use]
pub fn get_icmp_type(&self) -> IcmpType {
IcmpType::from(self.buf.read(TYPE_OFFSET))
}
#[must_use]
pub fn get_icmp_code(&self) -> IcmpCode {
IcmpCode::from(self.buf.read(CODE_OFFSET))
}
#[must_use]
pub fn get_checksum(&self) -> u16 {
u16::from_be_bytes(self.buf.get_bytes(CHECKSUM_OFFSET))
}
#[must_use]
pub fn get_length(&self) -> u8 {
self.buf.read(LENGTH_OFFSET)
}
#[must_use]
pub fn get_next_hop_mtu(&self) -> u16 {
u16::from_be_bytes(self.buf.get_bytes(NEXT_HOP_MTU_OFFSET))
}
pub fn set_icmp_type(&mut self, val: IcmpType) {
*self.buf.write(TYPE_OFFSET) = val.id();
}
pub fn set_icmp_code(&mut self, val: IcmpCode) {
*self.buf.write(CODE_OFFSET) = val.0;
}
pub fn set_checksum(&mut self, val: u16) {
self.buf.set_bytes(CHECKSUM_OFFSET, val.to_be_bytes());
}
pub fn set_length(&mut self, val: u8) {
*self.buf.write(LENGTH_OFFSET) = val;
}
pub fn set_next_hop_mtu(&mut self, val: u16) {
self.buf.set_bytes(NEXT_HOP_MTU_OFFSET, val.to_be_bytes());
}
pub fn set_payload(&mut self, vals: &[u8]) {
let current_offset = Self::minimum_packet_size();
self.buf.as_slice_mut()[current_offset..current_offset + vals.len()]
.copy_from_slice(vals);
}
#[must_use]
pub fn packet(&self) -> &[u8] {
self.buf.as_slice()
}
#[must_use]
pub fn payload(&self) -> &[u8] {
let (payload, _) = self.split_payload_extension();
payload
}
#[must_use]
pub fn payload_raw(&self) -> &[u8] {
&self.buf.as_slice()[Self::minimum_packet_size()..]
}
#[must_use]
pub fn extension(&self) -> Option<&[u8]> {
let (_, extension) = self.split_payload_extension();
extension
}
fn split_payload_extension(&self) -> (&[u8], Option<&[u8]>) {
let length = usize::from(self.get_length()) * 4;
let icmp_payload = &self.buf.as_slice()[Self::minimum_packet_size()..];
split(length, icmp_payload)
}
}
impl Debug for DestinationUnreachablePacket<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("DestinationUnreachablePacket")
.field("icmp_type", &self.get_icmp_type())
.field("icmp_code", &self.get_icmp_code())
.field("checksum", &self.get_checksum())
.field("length", &self.get_length())
.field("next_hop_mtu", &self.get_next_hop_mtu())
.field("payload", &fmt_payload(self.payload()))
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_icmp_type() {
let mut buf = [0_u8; DestinationUnreachablePacket::minimum_packet_size()];
let mut packet = DestinationUnreachablePacket::new(&mut buf).unwrap();
packet.set_icmp_type(IcmpType::EchoRequest);
assert_eq!(IcmpType::EchoRequest, packet.get_icmp_type());
assert_eq!([0x08], packet.packet()[0..1]);
packet.set_icmp_type(IcmpType::EchoReply);
assert_eq!(IcmpType::EchoReply, packet.get_icmp_type());
assert_eq!([0x00], packet.packet()[0..1]);
packet.set_icmp_type(IcmpType::DestinationUnreachable);
assert_eq!(IcmpType::DestinationUnreachable, packet.get_icmp_type());
assert_eq!([0x03], packet.packet()[0..1]);
packet.set_icmp_type(IcmpType::TimeExceeded);
assert_eq!(IcmpType::TimeExceeded, packet.get_icmp_type());
assert_eq!([0x0B], packet.packet()[0..1]);
packet.set_icmp_type(IcmpType::Other(255));
assert_eq!(IcmpType::Other(255), packet.get_icmp_type());
assert_eq!([0xFF], packet.packet()[0..1]);
}
#[test]
fn test_icmp_code() {
let mut buf = [0_u8; DestinationUnreachablePacket::minimum_packet_size()];
let mut packet = DestinationUnreachablePacket::new(&mut buf).unwrap();
packet.set_icmp_code(IcmpCode(0));
assert_eq!(IcmpCode(0), packet.get_icmp_code());
assert_eq!([0x00], packet.packet()[1..2]);
packet.set_icmp_code(IcmpCode(5));
assert_eq!(IcmpCode(5), packet.get_icmp_code());
assert_eq!([0x05], packet.packet()[1..2]);
packet.set_icmp_code(IcmpCode(255));
assert_eq!(IcmpCode(255), packet.get_icmp_code());
assert_eq!([0xFF], packet.packet()[1..2]);
}
#[test]
fn test_checksum() {
let mut buf = [0_u8; DestinationUnreachablePacket::minimum_packet_size()];
let mut packet = DestinationUnreachablePacket::new(&mut buf).unwrap();
packet.set_checksum(0);
assert_eq!(0, packet.get_checksum());
assert_eq!([0x00, 0x00], packet.packet()[2..=3]);
packet.set_checksum(1999);
assert_eq!(1999, packet.get_checksum());
assert_eq!([0x07, 0xCF], packet.packet()[2..=3]);
packet.set_checksum(u16::MAX);
assert_eq!(u16::MAX, packet.get_checksum());
assert_eq!([0xFF, 0xFF], packet.packet()[2..=3]);
}
#[test]
fn test_length() {
let mut buf = [0_u8; DestinationUnreachablePacket::minimum_packet_size()];
let mut packet = DestinationUnreachablePacket::new(&mut buf).unwrap();
packet.set_length(0);
assert_eq!(0, packet.get_length());
assert_eq!([0x00], packet.packet()[5..6]);
packet.set_length(8);
assert_eq!(8, packet.get_length());
assert_eq!([0x08], packet.packet()[5..6]);
packet.set_length(u8::MAX);
assert_eq!(u8::MAX, packet.get_length());
assert_eq!([0xFF], packet.packet()[5..6]);
}
#[test]
fn test_view() {
let buf = [0x03, 0x03, 0xdf, 0xdc, 0x00, 0x00, 0x00, 0x00];
let packet = DestinationUnreachablePacket::new_view(&buf).unwrap();
assert_eq!(IcmpType::DestinationUnreachable, packet.get_icmp_type());
assert_eq!(IcmpCode(3), packet.get_icmp_code());
assert_eq!(57308, packet.get_checksum());
assert_eq!(0, packet.get_length());
assert!(packet.payload().is_empty());
}
#[test]
fn test_view_large() {
let mut buf = [0x0_u8; 256];
buf[..8].copy_from_slice(&[0x03, 0x03, 0xdf, 0xdc, 0x00, 0x40, 0x00, 0x00]);
let packet = DestinationUnreachablePacket::new_view(&buf).unwrap();
assert_eq!(IcmpType::DestinationUnreachable, packet.get_icmp_type());
assert_eq!(IcmpCode(3), packet.get_icmp_code());
assert_eq!(57308, packet.get_checksum());
assert_eq!(64, packet.get_length());
assert_eq!(&[0x0_u8; 248], packet.payload());
assert_eq!(None, packet.extension());
}
#[test]
fn test_new_insufficient_buffer() {
const SIZE: usize = DestinationUnreachablePacket::minimum_packet_size();
let mut buf = [0_u8; SIZE - 1];
let err = DestinationUnreachablePacket::new(&mut buf).unwrap_err();
assert_eq!(
Error::InsufficientPacketBuffer(
String::from("DestinationUnreachablePacket"),
SIZE,
SIZE - 1
),
err
);
}
#[test]
fn test_new_view_insufficient_buffer() {
const SIZE: usize = DestinationUnreachablePacket::minimum_packet_size();
let buf = [0_u8; SIZE - 1];
let err = DestinationUnreachablePacket::new_view(&buf).unwrap_err();
assert_eq!(
Error::InsufficientPacketBuffer(
String::from("DestinationUnreachablePacket"),
SIZE,
SIZE - 1
),
err
);
}
}
}