smoltcp/phy/
pcap_writer.rs

1use byteorder::{ByteOrder, NativeEndian};
2use core::cell::RefCell;
3use phy::Medium;
4#[cfg(feature = "std")]
5use std::io::Write;
6
7use crate::phy::{self, Device, DeviceCapabilities};
8use crate::time::Instant;
9
10enum_with_unknown! {
11    /// Captured packet header type.
12    pub enum PcapLinkType(u32) {
13        /// Ethernet frames
14        Ethernet =   1,
15        /// IPv4 or IPv6 packets (depending on the version field)
16        Ip       = 101,
17        /// IEEE 802.15.4 packets without FCS.
18        Ieee802154WithoutFcs = 230,
19    }
20}
21
22/// Packet capture mode.
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24#[cfg_attr(feature = "defmt", derive(defmt::Format))]
25pub enum PcapMode {
26    /// Capture both received and transmitted packets.
27    Both,
28    /// Capture only received packets.
29    RxOnly,
30    /// Capture only transmitted packets.
31    TxOnly,
32}
33
34/// A packet capture sink.
35pub trait PcapSink {
36    /// Write data into the sink.
37    fn write(&mut self, data: &[u8]);
38
39    /// Flush data written into the sync.
40    fn flush(&mut self) {}
41
42    /// Write an `u16` into the sink, in native byte order.
43    fn write_u16(&mut self, value: u16) {
44        let mut bytes = [0u8; 2];
45        NativeEndian::write_u16(&mut bytes, value);
46        self.write(&bytes[..])
47    }
48
49    /// Write an `u32` into the sink, in native byte order.
50    fn write_u32(&mut self, value: u32) {
51        let mut bytes = [0u8; 4];
52        NativeEndian::write_u32(&mut bytes, value);
53        self.write(&bytes[..])
54    }
55
56    /// Write the libpcap global header into the sink.
57    ///
58    /// This method may be overridden e.g. if special synchronization is necessary.
59    fn global_header(&mut self, link_type: PcapLinkType) {
60        self.write_u32(0xa1b2c3d4); // magic number
61        self.write_u16(2); // major version
62        self.write_u16(4); // minor version
63        self.write_u32(0); // timezone (= UTC)
64        self.write_u32(0); // accuracy (not used)
65        self.write_u32(65535); // maximum packet length
66        self.write_u32(link_type.into()); // link-layer header type
67    }
68
69    /// Write the libpcap packet header into the sink.
70    ///
71    /// See also the note for [global_header](#method.global_header).
72    ///
73    /// # Panics
74    /// This function panics if `length` is greater than 65535.
75    fn packet_header(&mut self, timestamp: Instant, length: usize) {
76        assert!(length <= 65535);
77
78        self.write_u32(timestamp.secs() as u32); // timestamp seconds
79        self.write_u32(timestamp.micros() as u32); // timestamp microseconds
80        self.write_u32(length as u32); // captured length
81        self.write_u32(length as u32); // original length
82    }
83
84    /// Write the libpcap packet header followed by packet data into the sink.
85    ///
86    /// See also the note for [global_header](#method.global_header).
87    fn packet(&mut self, timestamp: Instant, packet: &[u8]) {
88        self.packet_header(timestamp, packet.len());
89        self.write(packet);
90        self.flush();
91    }
92}
93
94#[cfg(feature = "std")]
95impl<T: Write> PcapSink for T {
96    fn write(&mut self, data: &[u8]) {
97        T::write_all(self, data).expect("cannot write")
98    }
99
100    fn flush(&mut self) {
101        T::flush(self).expect("cannot flush")
102    }
103}
104
105/// A packet capture writer device.
106///
107/// Every packet transmitted or received through this device is timestamped
108/// and written (in the [libpcap] format) using the provided [sink].
109/// Note that writes are fine-grained, and buffering is recommended.
110///
111/// The packet sink should be cheaply cloneable, as it is cloned on every
112/// transmitted packet. For example, `&'a mut Vec<u8>` is cheaply cloneable
113/// but `&std::io::File`
114///
115/// [libpcap]: https://wiki.wireshark.org/Development/LibpcapFileFormat
116/// [sink]: trait.PcapSink.html
117#[derive(Debug)]
118pub struct PcapWriter<D, S>
119where
120    D: Device,
121    S: PcapSink,
122{
123    lower: D,
124    sink: RefCell<S>,
125    mode: PcapMode,
126}
127
128impl<D: Device, S: PcapSink> PcapWriter<D, S> {
129    /// Creates a packet capture writer.
130    pub fn new(lower: D, mut sink: S, mode: PcapMode) -> PcapWriter<D, S> {
131        let medium = lower.capabilities().medium;
132        let link_type = match medium {
133            #[cfg(feature = "medium-ip")]
134            Medium::Ip => PcapLinkType::Ip,
135            #[cfg(feature = "medium-ethernet")]
136            Medium::Ethernet => PcapLinkType::Ethernet,
137            #[cfg(feature = "medium-ieee802154")]
138            Medium::Ieee802154 => PcapLinkType::Ieee802154WithoutFcs,
139        };
140        sink.global_header(link_type);
141        PcapWriter {
142            lower,
143            sink: RefCell::new(sink),
144            mode,
145        }
146    }
147
148    /// Get a reference to the underlying device.
149    ///
150    /// Even if the device offers reading through a standard reference, it is inadvisable to
151    /// directly read from the device as doing so will circumvent the packet capture.
152    pub fn get_ref(&self) -> &D {
153        &self.lower
154    }
155
156    /// Get a mutable reference to the underlying device.
157    ///
158    /// It is inadvisable to directly read from the device as doing so will circumvent the packet capture.
159    pub fn get_mut(&mut self) -> &mut D {
160        &mut self.lower
161    }
162}
163
164impl<D: Device, S> Device for PcapWriter<D, S>
165where
166    S: PcapSink,
167{
168    type RxToken<'a> = RxToken<'a, D::RxToken<'a>, S>
169    where
170        Self: 'a;
171    type TxToken<'a> = TxToken<'a, D::TxToken<'a>, S>
172    where
173        Self: 'a;
174
175    fn capabilities(&self) -> DeviceCapabilities {
176        self.lower.capabilities()
177    }
178
179    fn receive(&mut self, timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
180        let sink = &self.sink;
181        let mode = self.mode;
182        self.lower
183            .receive(timestamp)
184            .map(move |(rx_token, tx_token)| {
185                let rx = RxToken {
186                    token: rx_token,
187                    sink,
188                    mode,
189                    timestamp,
190                };
191                let tx = TxToken {
192                    token: tx_token,
193                    sink,
194                    mode,
195                    timestamp,
196                };
197                (rx, tx)
198            })
199    }
200
201    fn transmit(&mut self, timestamp: Instant) -> Option<Self::TxToken<'_>> {
202        let sink = &self.sink;
203        let mode = self.mode;
204        self.lower.transmit(timestamp).map(move |token| TxToken {
205            token,
206            sink,
207            mode,
208            timestamp,
209        })
210    }
211}
212
213#[doc(hidden)]
214pub struct RxToken<'a, Rx: phy::RxToken, S: PcapSink> {
215    token: Rx,
216    sink: &'a RefCell<S>,
217    mode: PcapMode,
218    timestamp: Instant,
219}
220
221impl<'a, Rx: phy::RxToken, S: PcapSink> phy::RxToken for RxToken<'a, Rx, S> {
222    fn consume<R, F: FnOnce(&[u8]) -> R>(self, f: F) -> R {
223        self.token.consume(|buffer| {
224            match self.mode {
225                PcapMode::Both | PcapMode::RxOnly => self
226                    .sink
227                    .borrow_mut()
228                    .packet(self.timestamp, buffer.as_ref()),
229                PcapMode::TxOnly => (),
230            }
231            f(buffer)
232        })
233    }
234
235    fn meta(&self) -> phy::PacketMeta {
236        self.token.meta()
237    }
238}
239
240#[doc(hidden)]
241pub struct TxToken<'a, Tx: phy::TxToken, S: PcapSink> {
242    token: Tx,
243    sink: &'a RefCell<S>,
244    mode: PcapMode,
245    timestamp: Instant,
246}
247
248impl<'a, Tx: phy::TxToken, S: PcapSink> phy::TxToken for TxToken<'a, Tx, S> {
249    fn consume<R, F>(self, len: usize, f: F) -> R
250    where
251        F: FnOnce(&mut [u8]) -> R,
252    {
253        self.token.consume(len, |buffer| {
254            let result = f(buffer);
255            match self.mode {
256                PcapMode::Both | PcapMode::TxOnly => {
257                    self.sink.borrow_mut().packet(self.timestamp, buffer)
258                }
259                PcapMode::RxOnly => (),
260            };
261            result
262        })
263    }
264
265    fn set_meta(&mut self, meta: phy::PacketMeta) {
266        self.token.set_meta(meta)
267    }
268}