use core::fmt; use crate::{Result, wire::pretty_print::{PrettyIndent, PrettyPrint}}; use crate::phy::{self, DeviceCapabilities, Device, Medium}; use crate::time::Instant; /// A tracer device. /// /// A tracer is a device that pretty prints all packets traversing it /// using the provided writer function, and then passes them to another /// device. pub struct Tracer Device<'a>> { inner: D, writer: fn(Instant, Packet), } impl Device<'a>> Tracer { /// Create a tracer device. pub fn new(inner: D, writer: fn(timestamp: Instant, packet: Packet)) -> Tracer { Tracer { inner, writer } } /// Get a reference to the underlying device. /// /// Even if the device offers reading through a standard reference, it is inadvisable to /// directly read from the device as doing so will circumvent the tracing. pub fn get_ref(&self) -> &D { &self.inner } /// Get a mutable reference to the underlying device. /// /// It is inadvisable to directly read from the device as doing so will circumvent the tracing. pub fn get_mut(&mut self) -> &mut D { &mut self.inner } /// Return the underlying device, consuming the tracer. pub fn into_inner(self) -> D { self.inner } } impl<'a, D> Device<'a> for Tracer where D: for<'b> Device<'b>, { type RxToken = RxToken<>::RxToken>; type TxToken = TxToken<>::TxToken>; fn capabilities(&self) -> DeviceCapabilities { self.inner.capabilities() } fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> { let &mut Self { ref mut inner, writer, .. } = self; let medium = inner.capabilities().medium; inner.receive().map(|(rx_token, tx_token)| { let rx = RxToken { token: rx_token, writer, medium }; let tx = TxToken { token: tx_token, writer, medium }; (rx, tx) }) } fn transmit(&'a mut self) -> Option { let &mut Self { ref mut inner, writer } = self; let medium = inner.capabilities().medium; inner.transmit().map(|tx_token| { TxToken { token: tx_token, medium, writer } }) } } #[doc(hidden)] pub struct RxToken { token: Rx, writer: fn(Instant, Packet), medium: Medium, } impl phy::RxToken for RxToken { fn consume(self, timestamp: Instant, f: F) -> Result where F: FnOnce(&mut [u8]) -> R { let Self { token, writer, medium } = self; token.consume(timestamp, |buffer| { writer(timestamp, Packet{ buffer, medium, prefix: "<- ", }); f(buffer) }) } } #[doc(hidden)] pub struct TxToken { token: Tx, writer: fn(Instant, Packet), medium: Medium, } impl phy::TxToken for TxToken { fn consume(self, timestamp: Instant, len: usize, f: F) -> Result where F: FnOnce(&mut [u8]) -> R { let Self { token, writer, medium } = self; token.consume(timestamp, len, |buffer| { let result = f(buffer); writer(timestamp, Packet{ buffer, medium, prefix: "-> ", }); result }) } } pub struct Packet<'a> { buffer: &'a [u8], medium: Medium, prefix: &'static str, } impl<'a> fmt::Display for Packet<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let mut indent = PrettyIndent::new(self.prefix); match self.medium { #[cfg(feature = "medium-ethernet")] Medium::Ethernet => crate::wire::EthernetFrame::<&'static [u8]>::pretty_print(&self.buffer, f, &mut indent), #[cfg(feature = "medium-ip")] Medium::Ip => match crate::wire::IpVersion::of_packet(&self.buffer) { #[cfg(feature = "proto-ipv4")] Ok(crate::wire::IpVersion::Ipv4) => crate::wire::Ipv4Packet::<&'static [u8]>::pretty_print(&self.buffer, f, &mut indent), #[cfg(feature = "proto-ipv6")] Ok(crate::wire::IpVersion::Ipv6) => crate::wire::Ipv6Packet::<&'static [u8]>::pretty_print(&self.buffer, f, &mut indent), _ => f.write_str("unrecognized IP version") } } } }