1use crate::phy::{self, Device, DeviceCapabilities};
2use crate::time::{Duration, Instant};
3
4use super::PacketMeta;
5
6fn xorshift32(state: &mut u32) -> u32 {
9 let mut x = *state;
10 x ^= x << 13;
11 x ^= x >> 17;
12 x ^= x << 5;
13 *state = x;
14 x
15}
16
17const MTU: usize = 1536;
19
20#[derive(Debug, Default, Clone, Copy)]
21#[cfg_attr(feature = "defmt", derive(defmt::Format))]
22struct Config {
23 corrupt_pct: u8,
24 drop_pct: u8,
25 max_size: usize,
26 max_tx_rate: u64,
27 max_rx_rate: u64,
28 interval: Duration,
29}
30
31#[derive(Debug, Clone)]
32#[cfg_attr(feature = "defmt", derive(defmt::Format))]
33struct State {
34 rng_seed: u32,
35 refilled_at: Instant,
36 tx_bucket: u64,
37 rx_bucket: u64,
38}
39
40impl State {
41 fn maybe(&mut self, pct: u8) -> bool {
42 xorshift32(&mut self.rng_seed) % 100 < pct as u32
43 }
44
45 fn corrupt<T: AsMut<[u8]>>(&mut self, mut buffer: T) {
46 let buffer = buffer.as_mut();
47 let index = (xorshift32(&mut self.rng_seed) as usize) % buffer.len();
49 let bit = 1 << (xorshift32(&mut self.rng_seed) % 8) as u8;
50 buffer[index] ^= bit;
51 }
52
53 fn refill(&mut self, config: &Config, timestamp: Instant) {
54 if timestamp - self.refilled_at > config.interval {
55 self.tx_bucket = config.max_tx_rate;
56 self.rx_bucket = config.max_rx_rate;
57 self.refilled_at = timestamp;
58 }
59 }
60
61 fn maybe_transmit(&mut self, config: &Config, timestamp: Instant) -> bool {
62 if config.max_tx_rate == 0 {
63 return true;
64 }
65
66 self.refill(config, timestamp);
67 if self.tx_bucket > 0 {
68 self.tx_bucket -= 1;
69 true
70 } else {
71 false
72 }
73 }
74
75 fn maybe_receive(&mut self, config: &Config, timestamp: Instant) -> bool {
76 if config.max_rx_rate == 0 {
77 return true;
78 }
79
80 self.refill(config, timestamp);
81 if self.rx_bucket > 0 {
82 self.rx_bucket -= 1;
83 true
84 } else {
85 false
86 }
87 }
88}
89
90#[derive(Debug)]
96pub struct FaultInjector<D: Device> {
97 inner: D,
98 state: State,
99 config: Config,
100 rx_buf: [u8; MTU],
101}
102
103impl<D: Device> FaultInjector<D> {
104 pub fn new(inner: D, seed: u32) -> FaultInjector<D> {
106 FaultInjector {
107 inner,
108 state: State {
109 rng_seed: seed,
110 refilled_at: Instant::from_millis(0),
111 tx_bucket: 0,
112 rx_bucket: 0,
113 },
114 config: Config::default(),
115 rx_buf: [0u8; MTU],
116 }
117 }
118
119 pub fn into_inner(self) -> D {
121 self.inner
122 }
123
124 pub fn corrupt_chance(&self) -> u8 {
126 self.config.corrupt_pct
127 }
128
129 pub fn drop_chance(&self) -> u8 {
131 self.config.drop_pct
132 }
133
134 pub fn max_packet_size(&self) -> usize {
136 self.config.max_size
137 }
138
139 pub fn max_tx_rate(&self) -> u64 {
141 self.config.max_tx_rate
142 }
143
144 pub fn max_rx_rate(&self) -> u64 {
146 self.config.max_rx_rate
147 }
148
149 pub fn bucket_interval(&self) -> Duration {
151 self.config.interval
152 }
153
154 pub fn set_corrupt_chance(&mut self, pct: u8) {
159 if pct > 100 {
160 panic!("percentage out of range")
161 }
162 self.config.corrupt_pct = pct
163 }
164
165 pub fn set_drop_chance(&mut self, pct: u8) {
170 if pct > 100 {
171 panic!("percentage out of range")
172 }
173 self.config.drop_pct = pct
174 }
175
176 pub fn set_max_packet_size(&mut self, size: usize) {
178 self.config.max_size = size
179 }
180
181 pub fn set_max_tx_rate(&mut self, rate: u64) {
183 self.config.max_tx_rate = rate
184 }
185
186 pub fn set_max_rx_rate(&mut self, rate: u64) {
188 self.config.max_rx_rate = rate
189 }
190
191 pub fn set_bucket_interval(&mut self, interval: Duration) {
193 self.state.refilled_at = Instant::from_millis(0);
194 self.config.interval = interval
195 }
196}
197
198impl<D: Device> Device for FaultInjector<D> {
199 type RxToken<'a>
200 = RxToken<'a>
201 where
202 Self: 'a;
203 type TxToken<'a>
204 = TxToken<'a, D::TxToken<'a>>
205 where
206 Self: 'a;
207
208 fn capabilities(&self) -> DeviceCapabilities {
209 let mut caps = self.inner.capabilities();
210 if caps.max_transmission_unit > MTU {
211 caps.max_transmission_unit = MTU;
212 }
213 caps
214 }
215
216 fn receive(&mut self, timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
217 let (rx_token, tx_token) = self.inner.receive(timestamp)?;
218 let rx_meta = <D::RxToken<'_> as phy::RxToken>::meta(&rx_token);
219
220 let len = super::RxToken::consume(rx_token, |buffer| {
221 if (self.config.max_size > 0 && buffer.len() > self.config.max_size)
222 || buffer.len() > self.rx_buf.len()
223 {
224 net_trace!("rx: dropping a packet that is too large");
225 return None;
226 }
227 self.rx_buf[..buffer.len()].copy_from_slice(buffer);
228 Some(buffer.len())
229 })?;
230
231 let buf = &mut self.rx_buf[..len];
232
233 if self.state.maybe(self.config.drop_pct) {
234 net_trace!("rx: randomly dropping a packet");
235 return None;
236 }
237
238 if !self.state.maybe_receive(&self.config, timestamp) {
239 net_trace!("rx: dropping a packet because of rate limiting");
240 return None;
241 }
242
243 if self.state.maybe(self.config.corrupt_pct) {
244 net_trace!("rx: randomly corrupting a packet");
245 self.state.corrupt(&mut buf[..]);
246 }
247
248 let rx = RxToken { buf, meta: rx_meta };
249 let tx = TxToken {
250 state: &mut self.state,
251 config: self.config,
252 token: tx_token,
253 junk: [0; MTU],
254 timestamp,
255 };
256 Some((rx, tx))
257 }
258
259 fn transmit(&mut self, timestamp: Instant) -> Option<Self::TxToken<'_>> {
260 self.inner.transmit(timestamp).map(|token| TxToken {
261 state: &mut self.state,
262 config: self.config,
263 token,
264 junk: [0; MTU],
265 timestamp,
266 })
267 }
268}
269
270#[doc(hidden)]
271pub struct RxToken<'a> {
272 buf: &'a mut [u8],
273 meta: PacketMeta,
274}
275
276impl<'a> phy::RxToken for RxToken<'a> {
277 fn consume<R, F>(self, f: F) -> R
278 where
279 F: FnOnce(&[u8]) -> R,
280 {
281 f(self.buf)
282 }
283
284 fn meta(&self) -> phy::PacketMeta {
285 self.meta
286 }
287}
288
289#[doc(hidden)]
290pub struct TxToken<'a, Tx: phy::TxToken> {
291 state: &'a mut State,
292 config: Config,
293 token: Tx,
294 junk: [u8; MTU],
295 timestamp: Instant,
296}
297
298impl<'a, Tx: phy::TxToken> phy::TxToken for TxToken<'a, Tx> {
299 fn consume<R, F>(mut self, len: usize, f: F) -> R
300 where
301 F: FnOnce(&mut [u8]) -> R,
302 {
303 let drop = if self.state.maybe(self.config.drop_pct) {
304 net_trace!("tx: randomly dropping a packet");
305 true
306 } else if self.config.max_size > 0 && len > self.config.max_size {
307 net_trace!("tx: dropping a packet that is too large");
308 true
309 } else if !self.state.maybe_transmit(&self.config, self.timestamp) {
310 net_trace!("tx: dropping a packet because of rate limiting");
311 true
312 } else {
313 false
314 };
315
316 if drop {
317 return f(&mut self.junk[..len]);
318 }
319
320 self.token.consume(len, |buf| {
321 if self.state.maybe(self.config.corrupt_pct) {
322 net_trace!("tx: corrupting a packet");
323 self.state.corrupt(&mut *buf);
324 }
325 f(buf)
326 })
327 }
328
329 fn set_meta(&mut self, meta: PacketMeta) {
330 self.token.set_meta(meta);
331 }
332}