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)]
114pub struct PcapWriter<D, S>
115where
116 D: Device,
117 S: PcapSink,
118{
119 lower: D,
120 sink: RefCell<S>,
121 mode: PcapMode,
122}
123
124impl<D: Device, S: PcapSink> PcapWriter<D, S> {
125 pub fn new(lower: D, mut sink: S, mode: PcapMode) -> PcapWriter<D, S> {
127 let medium = lower.capabilities().medium;
128 let link_type = match medium {
129 #[cfg(feature = "medium-ip")]
130 Medium::Ip => PcapLinkType::Ip,
131 #[cfg(feature = "medium-ethernet")]
132 Medium::Ethernet => PcapLinkType::Ethernet,
133 #[cfg(feature = "medium-ieee802154")]
134 Medium::Ieee802154 => PcapLinkType::Ieee802154WithoutFcs,
135 };
136 sink.global_header(link_type);
137 PcapWriter {
138 lower,
139 sink: RefCell::new(sink),
140 mode,
141 }
142 }
143
144 pub fn get_ref(&self) -> &D {
149 &self.lower
150 }
151
152 pub fn get_mut(&mut self) -> &mut D {
156 &mut self.lower
157 }
158}
159
160impl<D: Device, S> Device for PcapWriter<D, S>
161where
162 S: PcapSink,
163{
164 type RxToken<'a>
165 = RxToken<'a, D::RxToken<'a>, S>
166 where
167 Self: 'a;
168 type TxToken<'a>
169 = TxToken<'a, D::TxToken<'a>, S>
170 where
171 Self: 'a;
172
173 fn capabilities(&self) -> DeviceCapabilities {
174 self.lower.capabilities()
175 }
176
177 fn receive(&mut self, timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
178 let sink = &self.sink;
179 let mode = self.mode;
180 self.lower
181 .receive(timestamp)
182 .map(move |(rx_token, tx_token)| {
183 let rx = RxToken {
184 token: rx_token,
185 sink,
186 mode,
187 timestamp,
188 };
189 let tx = TxToken {
190 token: tx_token,
191 sink,
192 mode,
193 timestamp,
194 };
195 (rx, tx)
196 })
197 }
198
199 fn transmit(&mut self, timestamp: Instant) -> Option<Self::TxToken<'_>> {
200 let sink = &self.sink;
201 let mode = self.mode;
202 self.lower.transmit(timestamp).map(move |token| TxToken {
203 token,
204 sink,
205 mode,
206 timestamp,
207 })
208 }
209}
210
211#[doc(hidden)]
212pub struct RxToken<'a, Rx: phy::RxToken, S: PcapSink> {
213 token: Rx,
214 sink: &'a RefCell<S>,
215 mode: PcapMode,
216 timestamp: Instant,
217}
218
219impl<'a, Rx: phy::RxToken, S: PcapSink> phy::RxToken for RxToken<'a, Rx, S> {
220 fn consume<R, F: FnOnce(&[u8]) -> R>(self, f: F) -> R {
221 self.token.consume(|buffer| {
222 match self.mode {
223 PcapMode::Both | PcapMode::RxOnly => self
224 .sink
225 .borrow_mut()
226 .packet(self.timestamp, buffer.as_ref()),
227 PcapMode::TxOnly => (),
228 }
229 f(buffer)
230 })
231 }
232
233 fn meta(&self) -> phy::PacketMeta {
234 self.token.meta()
235 }
236}
237
238#[doc(hidden)]
239pub struct TxToken<'a, Tx: phy::TxToken, S: PcapSink> {
240 token: Tx,
241 sink: &'a RefCell<S>,
242 mode: PcapMode,
243 timestamp: Instant,
244}
245
246impl<'a, Tx: phy::TxToken, S: PcapSink> phy::TxToken for TxToken<'a, Tx, S> {
247 fn consume<R, F>(self, len: usize, f: F) -> R
248 where
249 F: FnOnce(&mut [u8]) -> R,
250 {
251 self.token.consume(len, |buffer| {
252 let result = f(buffer);
253 match self.mode {
254 PcapMode::Both | PcapMode::TxOnly => {
255 self.sink.borrow_mut().packet(self.timestamp, buffer)
256 }
257 PcapMode::RxOnly => (),
258 };
259 result
260 })
261 }
262
263 fn set_meta(&mut self, meta: phy::PacketMeta) {
264 self.token.set_meta(meta)
265 }
266}