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(self.max_packet_size()); self.write_u32(link_type.into()); }
68
69 fn packet_header(&mut self, timestamp: Instant, length: usize) {
76 let original_length = length.try_into().unwrap();
77
78 self.write_u32(timestamp.secs() as u32); self.write_u32(timestamp.micros() as u32); self.write_u32(self.max_packet_size().min(original_length)); self.write_u32(original_length);
82 }
83
84 fn packet(&mut self, timestamp: Instant, packet: &[u8]) {
90 let packet_len = packet.len();
91 let max_packet_size = usize::try_from(self.max_packet_size()).unwrap();
92
93 self.packet_header(timestamp, packet_len);
94 self.write(&packet[..max_packet_size.min(packet_len)]);
95 self.flush();
96 }
97
98 fn max_packet_size(&self) -> u32 {
104 262144
108 }
109}
110
111#[cfg(feature = "std")]
112impl<T: Write> PcapSink for T {
113 fn write(&mut self, data: &[u8]) {
114 T::write_all(self, data).expect("cannot write")
115 }
116
117 fn flush(&mut self) {
118 T::flush(self).expect("cannot flush")
119 }
120}
121
122#[derive(Debug)]
131pub struct PcapWriter<D, S>
132where
133 D: Device,
134 S: PcapSink,
135{
136 lower: D,
137 sink: RefCell<S>,
138 mode: PcapMode,
139}
140
141impl<D: Device, S: PcapSink> PcapWriter<D, S> {
142 pub fn new(lower: D, mut sink: S, mode: PcapMode) -> PcapWriter<D, S> {
144 let medium = lower.capabilities().medium;
145 let link_type = match medium {
146 #[cfg(feature = "medium-ip")]
147 Medium::Ip => PcapLinkType::Ip,
148 #[cfg(feature = "medium-ethernet")]
149 Medium::Ethernet => PcapLinkType::Ethernet,
150 #[cfg(feature = "medium-ieee802154")]
151 Medium::Ieee802154 => PcapLinkType::Ieee802154WithoutFcs,
152 };
153 sink.global_header(link_type);
154 PcapWriter {
155 lower,
156 sink: RefCell::new(sink),
157 mode,
158 }
159 }
160
161 pub fn get_ref(&self) -> &D {
166 &self.lower
167 }
168
169 pub fn get_mut(&mut self) -> &mut D {
173 &mut self.lower
174 }
175}
176
177impl<D: Device, S> Device for PcapWriter<D, S>
178where
179 S: PcapSink,
180{
181 type RxToken<'a>
182 = RxToken<'a, D::RxToken<'a>, S>
183 where
184 Self: 'a;
185 type TxToken<'a>
186 = TxToken<'a, D::TxToken<'a>, S>
187 where
188 Self: 'a;
189
190 fn capabilities(&self) -> DeviceCapabilities {
191 self.lower.capabilities()
192 }
193
194 fn receive(&mut self, timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
195 let sink = &self.sink;
196 let mode = self.mode;
197 self.lower
198 .receive(timestamp)
199 .map(move |(rx_token, tx_token)| {
200 let rx = RxToken {
201 token: rx_token,
202 sink,
203 mode,
204 timestamp,
205 };
206 let tx = TxToken {
207 token: tx_token,
208 sink,
209 mode,
210 timestamp,
211 };
212 (rx, tx)
213 })
214 }
215
216 fn transmit(&mut self, timestamp: Instant) -> Option<Self::TxToken<'_>> {
217 let sink = &self.sink;
218 let mode = self.mode;
219 self.lower.transmit(timestamp).map(move |token| TxToken {
220 token,
221 sink,
222 mode,
223 timestamp,
224 })
225 }
226}
227
228#[doc(hidden)]
229pub struct RxToken<'a, Rx: phy::RxToken, S: PcapSink> {
230 token: Rx,
231 sink: &'a RefCell<S>,
232 mode: PcapMode,
233 timestamp: Instant,
234}
235
236impl<'a, Rx: phy::RxToken, S: PcapSink> phy::RxToken for RxToken<'a, Rx, S> {
237 fn consume<R, F: FnOnce(&[u8]) -> R>(self, f: F) -> R {
238 self.token.consume(|buffer| {
239 match self.mode {
240 PcapMode::Both | PcapMode::RxOnly => self
241 .sink
242 .borrow_mut()
243 .packet(self.timestamp, buffer.as_ref()),
244 PcapMode::TxOnly => (),
245 }
246 f(buffer)
247 })
248 }
249
250 fn meta(&self) -> phy::PacketMeta {
251 self.token.meta()
252 }
253}
254
255#[doc(hidden)]
256pub struct TxToken<'a, Tx: phy::TxToken, S: PcapSink> {
257 token: Tx,
258 sink: &'a RefCell<S>,
259 mode: PcapMode,
260 timestamp: Instant,
261}
262
263impl<'a, Tx: phy::TxToken, S: PcapSink> phy::TxToken for TxToken<'a, Tx, S> {
264 fn consume<R, F>(self, len: usize, f: F) -> R
265 where
266 F: FnOnce(&mut [u8]) -> R,
267 {
268 self.token.consume(len, |buffer| {
269 let result = f(buffer);
270 match self.mode {
271 PcapMode::Both | PcapMode::TxOnly => {
272 self.sink.borrow_mut().packet(self.timestamp, buffer)
273 }
274 PcapMode::RxOnly => (),
275 };
276 result
277 })
278 }
279
280 fn set_meta(&mut self, meta: phy::PacketMeta) {
281 self.token.set_meta(meta)
282 }
283}