use core::fmt; use crate::phy::{self, Device, DeviceCapabilities, Medium}; use crate::time::Instant; use crate::{ wire::pretty_print::{PrettyIndent, PrettyPrint}, Result, }; /// 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]) -> Result, { 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]) -> Result, { 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"), }, #[cfg(feature = "medium-ieee802154")] Medium::Ieee802154 => Ok(()), // XXX } } }