1 use core::fmt;
2 
3 use crate::phy::{self, Device, DeviceCapabilities, Medium};
4 use crate::time::Instant;
5 use 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.
12 pub struct Tracer<D: Device> {
13     inner: D,
14     writer: fn(Instant, Packet),
15 }
16 
17 impl<D: Device> Tracer<D> {
18     /// Create a tracer device.
new(inner: D, writer: fn(timestamp: Instant, packet: Packet)) -> Tracer<D>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.
get_ref(&self) -> &D27     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.
get_mut(&mut self) -> &mut D34     pub fn get_mut(&mut self) -> &mut D {
35         &mut self.inner
36     }
37 
38     /// Return the underlying device, consuming the tracer.
into_inner(self) -> D39     pub fn into_inner(self) -> D {
40         self.inner
41     }
42 }
43 
44 impl<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 
capabilities(&self) -> DeviceCapabilities52     fn capabilities(&self) -> DeviceCapabilities {
53         self.inner.capabilities()
54     }
55 
receive(&mut self, timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)>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 
transmit(&mut self, timestamp: Instant) -> Option<Self::TxToken<'_>>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)]
87 pub struct RxToken<Rx: phy::RxToken> {
88     token: Rx,
89     writer: fn(Instant, Packet),
90     medium: Medium,
91     timestamp: Instant,
92 }
93 
94 impl<Rx: phy::RxToken> phy::RxToken for RxToken<Rx> {
consume<R, F>(self, f: F) -> R where F: FnOnce(&mut [u8]) -> R,95     fn consume<R, F>(self, f: F) -> R
96     where
97         F: FnOnce(&mut [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 
113 #[doc(hidden)]
114 pub struct TxToken<Tx: phy::TxToken> {
115     token: Tx,
116     writer: fn(Instant, Packet),
117     medium: Medium,
118     timestamp: Instant,
119 }
120 
121 impl<Tx: phy::TxToken> phy::TxToken for TxToken<Tx> {
consume<R, F>(self, len: usize, f: F) -> R where F: FnOnce(&mut [u8]) -> R,122     fn consume<R, F>(self, len: usize, f: F) -> R
123     where
124         F: FnOnce(&mut [u8]) -> R,
125     {
126         self.token.consume(len, |buffer| {
127             let result = f(buffer);
128             (self.writer)(
129                 self.timestamp,
130                 Packet {
131                     buffer,
132                     medium: self.medium,
133                     prefix: "-> ",
134                 },
135             );
136             result
137         })
138     }
139 }
140 
141 pub struct Packet<'a> {
142     buffer: &'a [u8],
143     medium: Medium,
144     prefix: &'static str,
145 }
146 
147 impl<'a> fmt::Display for Packet<'a> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result148     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
149         let mut indent = PrettyIndent::new(self.prefix);
150         match self.medium {
151             #[cfg(feature = "medium-ethernet")]
152             Medium::Ethernet => crate::wire::EthernetFrame::<&'static [u8]>::pretty_print(
153                 &self.buffer,
154                 f,
155                 &mut indent,
156             ),
157             #[cfg(feature = "medium-ip")]
158             Medium::Ip => match crate::wire::IpVersion::of_packet(self.buffer) {
159                 #[cfg(feature = "proto-ipv4")]
160                 Ok(crate::wire::IpVersion::Ipv4) => {
161                     crate::wire::Ipv4Packet::<&'static [u8]>::pretty_print(
162                         &self.buffer,
163                         f,
164                         &mut indent,
165                     )
166                 }
167                 #[cfg(feature = "proto-ipv6")]
168                 Ok(crate::wire::IpVersion::Ipv6) => {
169                     crate::wire::Ipv6Packet::<&'static [u8]>::pretty_print(
170                         &self.buffer,
171                         f,
172                         &mut indent,
173                     )
174                 }
175                 _ => f.write_str("unrecognized IP version"),
176             },
177             #[cfg(feature = "medium-ieee802154")]
178             Medium::Ieee802154 => Ok(()), // XXX
179         }
180     }
181 }
182