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 pub enum PcapLinkType(u32) {
13 Ethernet = 1,
15 Ip = 101,
17 Ieee802154WithoutFcs = 230,
19 }
20}
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24#[cfg_attr(feature = "defmt", derive(defmt::Format))]
25pub enum PcapMode {
26 Both,
28 RxOnly,
30 TxOnly,
32}
33
34pub trait PcapSink {
36 fn write(&mut self, data: &[u8]);
38
39 fn flush(&mut self) {}
41
42 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 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 fn global_header(&mut self, link_type: PcapLinkType) {
60 self.write_u32(0xa1b2c3d4); self.write_u16(2); self.write_u16(4); self.write_u32(0); self.write_u32(0); self.write_u32(65535); self.write_u32(link_type.into()); }
68
69 fn packet_header(&mut self, timestamp: Instant, length: usize) {
76 assert!(length <= 65535);
77
78 self.write_u32(timestamp.secs() as u32); self.write_u32(timestamp.micros() as u32); self.write_u32(length as u32); self.write_u32(length as u32); }
83
84 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#[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 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 pub fn get_ref(&self) -> &D {
153 &self.lower
154 }
155
156 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}