blob: 48e60ec2bc4651cdd045a73d554ddf37a12034dc [file] [log] [blame]
use core::fmt;
use crate::phy::{self, Device, DeviceCapabilities, Medium};
use crate::time::Instant;
use crate::wire::pretty_print::{PrettyIndent, PrettyPrint};
/// 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<D: Device> {
inner: D,
writer: fn(Instant, Packet),
}
impl<D: Device> Tracer<D> {
/// Create a tracer device.
pub fn new(inner: D, writer: fn(timestamp: Instant, packet: Packet)) -> Tracer<D> {
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<D: Device> Device for Tracer<D> {
type RxToken<'a> = RxToken<D::RxToken<'a>>
where
Self: 'a;
type TxToken<'a> = TxToken<D::TxToken<'a>>
where
Self: 'a;
fn capabilities(&self) -> DeviceCapabilities {
self.inner.capabilities()
}
fn receive(&mut self, timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
let medium = self.inner.capabilities().medium;
self.inner.receive(timestamp).map(|(rx_token, tx_token)| {
let rx = RxToken {
token: rx_token,
writer: self.writer,
medium,
timestamp,
};
let tx = TxToken {
token: tx_token,
writer: self.writer,
medium,
timestamp,
};
(rx, tx)
})
}
fn transmit(&mut self, timestamp: Instant) -> Option<Self::TxToken<'_>> {
let medium = self.inner.capabilities().medium;
self.inner.transmit(timestamp).map(|tx_token| TxToken {
token: tx_token,
medium,
writer: self.writer,
timestamp,
})
}
}
#[doc(hidden)]
pub struct RxToken<Rx: phy::RxToken> {
token: Rx,
writer: fn(Instant, Packet),
medium: Medium,
timestamp: Instant,
}
impl<Rx: phy::RxToken> phy::RxToken for RxToken<Rx> {
fn consume<R, F>(self, f: F) -> R
where
F: FnOnce(&mut [u8]) -> R,
{
self.token.consume(|buffer| {
(self.writer)(
self.timestamp,
Packet {
buffer,
medium: self.medium,
prefix: "<- ",
},
);
f(buffer)
})
}
fn meta(&self) -> phy::PacketMeta {
self.token.meta()
}
}
#[doc(hidden)]
pub struct TxToken<Tx: phy::TxToken> {
token: Tx,
writer: fn(Instant, Packet),
medium: Medium,
timestamp: Instant,
}
impl<Tx: phy::TxToken> phy::TxToken for TxToken<Tx> {
fn consume<R, F>(self, len: usize, f: F) -> R
where
F: FnOnce(&mut [u8]) -> R,
{
self.token.consume(len, |buffer| {
let result = f(buffer);
(self.writer)(
self.timestamp,
Packet {
buffer,
medium: self.medium,
prefix: "-> ",
},
);
result
})
}
fn set_meta(&mut self, meta: phy::PacketMeta) {
self.token.set_meta(meta)
}
}
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
}
}
}