use crate::packets::icmp::v4::{Icmpv4, Icmpv4Message, Icmpv4Packet, Icmpv4Type, Icmpv4Types};
use crate::packets::ip::v4::IPV4_MIN_MTU;
use crate::packets::types::u32be;
use crate::packets::{Internal, Packet};
use crate::SizeOf;
use anyhow::Result;
use std::fmt;
use std::ptr::NonNull;
#[derive(Icmpv4Packet)]
pub struct TimeExceeded {
icmp: Icmpv4,
body: NonNull<TimeExceededBody>,
}
impl TimeExceeded {
#[inline]
fn data_offset(&self) -> usize {
self.payload_offset() + TimeExceededBody::size_of()
}
#[inline]
fn data_len(&self) -> usize {
self.payload_len() - TimeExceededBody::size_of()
}
#[inline]
pub fn data(&self) -> &[u8] {
if let Ok(data) = self
.icmp()
.mbuf()
.read_data_slice(self.data_offset(), self.data_len())
{
unsafe { &*data.as_ptr() }
} else {
&[]
}
}
}
impl fmt::Debug for TimeExceeded {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("TimeExceeded")
.field("type", &format!("{}", self.msg_type()))
.field("code", &self.code())
.field("checksum", &format!("0x{:04x}", self.checksum()))
.field("$offset", &self.offset())
.field("$len", &self.len())
.field("$header_len", &self.header_len())
.finish()
}
}
impl Icmpv4Message for TimeExceeded {
#[inline]
fn msg_type() -> Icmpv4Type {
Icmpv4Types::TimeExceeded
}
#[inline]
fn icmp(&self) -> &Icmpv4 {
&self.icmp
}
#[inline]
fn icmp_mut(&mut self) -> &mut Icmpv4 {
&mut self.icmp
}
#[inline]
fn into_icmp(self) -> Icmpv4 {
self.icmp
}
#[inline]
unsafe fn clone(&self, internal: Internal) -> Self {
TimeExceeded {
icmp: self.icmp.clone(internal),
body: self.body,
}
}
#[inline]
fn try_parse(icmp: Icmpv4, _internal: Internal) -> Result<Self> {
let mbuf = icmp.mbuf();
let offset = icmp.payload_offset();
let body = mbuf.read_data(offset)?;
Ok(TimeExceeded { icmp, body })
}
#[inline]
fn try_push(mut icmp: Icmpv4, _internal: Internal) -> Result<Self> {
let offset = icmp.payload_offset();
let mbuf = icmp.mbuf_mut();
mbuf.extend(offset, TimeExceededBody::size_of())?;
let body = mbuf.write_data(offset, &TimeExceededBody::default())?;
Ok(TimeExceeded { icmp, body })
}
#[inline]
fn reconcile(&mut self) {
let len = self.data_len();
let offset = self.data_offset();
if len > IPV4_MIN_MTU {
let _ = self
.mbuf_mut()
.shrink(offset + IPV4_MIN_MTU, len - IPV4_MIN_MTU);
}
self.icmp_mut().compute_checksum();
}
}
#[derive(Clone, Copy, Debug, Default, SizeOf)]
#[repr(C, packed)]
struct TimeExceededBody {
_unused: u32be,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::packets::ip::v4::Ipv4;
use crate::packets::Ethernet;
use crate::testils::byte_arrays::IPV4_TCP_PACKET;
use crate::Mbuf;
#[test]
fn size_of_time_exceeded_body() {
assert_eq!(4, TimeExceededBody::size_of());
}
#[capsule::test]
fn push_and_set_time_exceeded() {
let packet = Mbuf::from_bytes(&IPV4_TCP_PACKET).unwrap();
let ethernet = packet.parse::<Ethernet>().unwrap();
let ipv4 = ethernet.parse::<Ipv4>().unwrap();
let tcp_len = ipv4.payload_len();
let mut exceeded = ipv4.push::<TimeExceeded>().unwrap();
assert_eq!(4, exceeded.header_len());
assert_eq!(
TimeExceededBody::size_of() + tcp_len,
exceeded.payload_len()
);
assert_eq!(Icmpv4Types::TimeExceeded, exceeded.msg_type());
assert_eq!(0, exceeded.code());
assert_eq!(tcp_len, exceeded.data().len());
exceeded.set_code(1);
assert_eq!(1, exceeded.code());
exceeded.reconcile_all();
assert!(exceeded.checksum() != 0);
}
#[capsule::test]
fn shrinks_to_ipv4_min_mtu() {
let packet = Mbuf::from_bytes(&[42; 100]).unwrap();
let ethernet = packet.push::<Ethernet>().unwrap();
let ipv4 = ethernet.push::<Ipv4>().unwrap();
let mut exceeded = ipv4.push::<TimeExceeded>().unwrap();
assert!(exceeded.data_len() > IPV4_MIN_MTU);
exceeded.reconcile_all();
assert_eq!(IPV4_MIN_MTU, exceeded.data_len());
}
#[capsule::test]
fn message_body_no_shrink() {
let packet = Mbuf::from_bytes(&[42; 50]).unwrap();
let ethernet = packet.push::<Ethernet>().unwrap();
let ipv4 = ethernet.push::<Ipv4>().unwrap();
let mut exceeded = ipv4.push::<TimeExceeded>().unwrap();
assert!(exceeded.data_len() < IPV4_MIN_MTU);
exceeded.reconcile_all();
assert_eq!(50, exceeded.data_len());
}
}