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