smoltcp/phy/
tracer.rs

1use core::fmt;
2
3use crate::phy::{self, Device, DeviceCapabilities, Medium};
4use crate::time::Instant;
5use crate::wire::pretty_print::{PrettyIndent, PrettyPrint};
6
7/// A tracer device.
8///
9/// A tracer is a device that pretty prints all packets traversing it
10/// using the provided writer function, and then passes them to another
11/// device.
12pub struct Tracer<D: Device> {
13    inner: D,
14    writer: fn(Instant, Packet),
15}
16
17impl<D: Device> Tracer<D> {
18    /// Create a tracer device.
19    pub fn new(inner: D, writer: fn(timestamp: Instant, packet: Packet)) -> Tracer<D> {
20        Tracer { inner, writer }
21    }
22
23    /// Get a reference to the underlying device.
24    ///
25    /// Even if the device offers reading through a standard reference, it is inadvisable to
26    /// directly read from the device as doing so will circumvent the tracing.
27    pub fn get_ref(&self) -> &D {
28        &self.inner
29    }
30
31    /// Get a mutable reference to the underlying device.
32    ///
33    /// It is inadvisable to directly read from the device as doing so will circumvent the tracing.
34    pub fn get_mut(&mut self) -> &mut D {
35        &mut self.inner
36    }
37
38    /// Return the underlying device, consuming the tracer.
39    pub fn into_inner(self) -> D {
40        self.inner
41    }
42}
43
44impl<D: Device> Device for Tracer<D> {
45    type RxToken<'a> = RxToken<D::RxToken<'a>>
46    where
47        Self: 'a;
48    type TxToken<'a> = TxToken<D::TxToken<'a>>
49    where
50        Self: 'a;
51
52    fn capabilities(&self) -> DeviceCapabilities {
53        self.inner.capabilities()
54    }
55
56    fn receive(&mut self, timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
57        let medium = self.inner.capabilities().medium;
58        self.inner.receive(timestamp).map(|(rx_token, tx_token)| {
59            let rx = RxToken {
60                token: rx_token,
61                writer: self.writer,
62                medium,
63                timestamp,
64            };
65            let tx = TxToken {
66                token: tx_token,
67                writer: self.writer,
68                medium,
69                timestamp,
70            };
71            (rx, tx)
72        })
73    }
74
75    fn transmit(&mut self, timestamp: Instant) -> Option<Self::TxToken<'_>> {
76        let medium = self.inner.capabilities().medium;
77        self.inner.transmit(timestamp).map(|tx_token| TxToken {
78            token: tx_token,
79            medium,
80            writer: self.writer,
81            timestamp,
82        })
83    }
84}
85
86#[doc(hidden)]
87pub struct RxToken<Rx: phy::RxToken> {
88    token: Rx,
89    writer: fn(Instant, Packet),
90    medium: Medium,
91    timestamp: Instant,
92}
93
94impl<Rx: phy::RxToken> phy::RxToken for RxToken<Rx> {
95    fn consume<R, F>(self, f: F) -> R
96    where
97        F: FnOnce(&[u8]) -> R,
98    {
99        self.token.consume(|buffer| {
100            (self.writer)(
101                self.timestamp,
102                Packet {
103                    buffer,
104                    medium: self.medium,
105                    prefix: "<- ",
106                },
107            );
108            f(buffer)
109        })
110    }
111
112    fn meta(&self) -> phy::PacketMeta {
113        self.token.meta()
114    }
115}
116
117#[doc(hidden)]
118pub struct TxToken<Tx: phy::TxToken> {
119    token: Tx,
120    writer: fn(Instant, Packet),
121    medium: Medium,
122    timestamp: Instant,
123}
124
125impl<Tx: phy::TxToken> phy::TxToken for TxToken<Tx> {
126    fn consume<R, F>(self, len: usize, f: F) -> R
127    where
128        F: FnOnce(&mut [u8]) -> R,
129    {
130        self.token.consume(len, |buffer| {
131            let result = f(buffer);
132            (self.writer)(
133                self.timestamp,
134                Packet {
135                    buffer,
136                    medium: self.medium,
137                    prefix: "-> ",
138                },
139            );
140            result
141        })
142    }
143
144    fn set_meta(&mut self, meta: phy::PacketMeta) {
145        self.token.set_meta(meta)
146    }
147}
148
149pub struct Packet<'a> {
150    buffer: &'a [u8],
151    medium: Medium,
152    prefix: &'static str,
153}
154
155impl<'a> fmt::Display for Packet<'a> {
156    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
157        let mut indent = PrettyIndent::new(self.prefix);
158        match self.medium {
159            #[cfg(feature = "medium-ethernet")]
160            Medium::Ethernet => crate::wire::EthernetFrame::<&'static [u8]>::pretty_print(
161                &self.buffer,
162                f,
163                &mut indent,
164            ),
165            #[cfg(feature = "medium-ip")]
166            Medium::Ip => match crate::wire::IpVersion::of_packet(self.buffer) {
167                #[cfg(feature = "proto-ipv4")]
168                Ok(crate::wire::IpVersion::Ipv4) => {
169                    crate::wire::Ipv4Packet::<&'static [u8]>::pretty_print(
170                        &self.buffer,
171                        f,
172                        &mut indent,
173                    )
174                }
175                #[cfg(feature = "proto-ipv6")]
176                Ok(crate::wire::IpVersion::Ipv6) => {
177                    crate::wire::Ipv6Packet::<&'static [u8]>::pretty_print(
178                        &self.buffer,
179                        f,
180                        &mut indent,
181                    )
182                }
183                _ => f.write_str("unrecognized IP version"),
184            },
185            #[cfg(feature = "medium-ieee802154")]
186            Medium::Ieee802154 => Ok(()), // XXX
187        }
188    }
189}