1 use crate::phy::{self, Device, DeviceCapabilities};
2 use crate::time::{Duration, Instant};
3 
4 // We use our own RNG to stay compatible with #![no_std].
5 // The use of the RNG below has a slight bias, but it doesn't matter.
xorshift32(state: &mut u32) -> u326 fn xorshift32(state: &mut u32) -> u32 {
7     let mut x = *state;
8     x ^= x << 13;
9     x ^= x >> 17;
10     x ^= x << 5;
11     *state = x;
12     x
13 }
14 
15 // This could be fixed once associated consts are stable.
16 const MTU: usize = 1536;
17 
18 #[derive(Debug, Default, Clone, Copy)]
19 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
20 struct Config {
21     corrupt_pct: u8,
22     drop_pct: u8,
23     max_size: usize,
24     max_tx_rate: u64,
25     max_rx_rate: u64,
26     interval: Duration,
27 }
28 
29 #[derive(Debug, Clone)]
30 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
31 struct State {
32     rng_seed: u32,
33     refilled_at: Instant,
34     tx_bucket: u64,
35     rx_bucket: u64,
36 }
37 
38 impl State {
maybe(&mut self, pct: u8) -> bool39     fn maybe(&mut self, pct: u8) -> bool {
40         xorshift32(&mut self.rng_seed) % 100 < pct as u32
41     }
42 
corrupt<T: AsMut<[u8]>>(&mut self, mut buffer: T)43     fn corrupt<T: AsMut<[u8]>>(&mut self, mut buffer: T) {
44         let buffer = buffer.as_mut();
45         // We introduce a single bitflip, as the most likely, and the hardest to detect, error.
46         let index = (xorshift32(&mut self.rng_seed) as usize) % buffer.len();
47         let bit = 1 << (xorshift32(&mut self.rng_seed) % 8) as u8;
48         buffer[index] ^= bit;
49     }
50 
refill(&mut self, config: &Config, timestamp: Instant)51     fn refill(&mut self, config: &Config, timestamp: Instant) {
52         if timestamp - self.refilled_at > config.interval {
53             self.tx_bucket = config.max_tx_rate;
54             self.rx_bucket = config.max_rx_rate;
55             self.refilled_at = timestamp;
56         }
57     }
58 
maybe_transmit(&mut self, config: &Config, timestamp: Instant) -> bool59     fn maybe_transmit(&mut self, config: &Config, timestamp: Instant) -> bool {
60         if config.max_tx_rate == 0 {
61             return true;
62         }
63 
64         self.refill(config, timestamp);
65         if self.tx_bucket > 0 {
66             self.tx_bucket -= 1;
67             true
68         } else {
69             false
70         }
71     }
72 
maybe_receive(&mut self, config: &Config, timestamp: Instant) -> bool73     fn maybe_receive(&mut self, config: &Config, timestamp: Instant) -> bool {
74         if config.max_rx_rate == 0 {
75             return true;
76         }
77 
78         self.refill(config, timestamp);
79         if self.rx_bucket > 0 {
80             self.rx_bucket -= 1;
81             true
82         } else {
83             false
84         }
85     }
86 }
87 
88 /// A fault injector device.
89 ///
90 /// A fault injector is a device that alters packets traversing through it to simulate
91 /// adverse network conditions (such as random packet loss or corruption), or software
92 /// or hardware limitations (such as a limited number or size of usable network buffers).
93 #[derive(Debug)]
94 pub struct FaultInjector<D: Device> {
95     inner: D,
96     state: State,
97     config: Config,
98     rx_buf: [u8; MTU],
99 }
100 
101 impl<D: Device> FaultInjector<D> {
102     /// Create a fault injector device, using the given random number generator seed.
new(inner: D, seed: u32) -> FaultInjector<D>103     pub fn new(inner: D, seed: u32) -> FaultInjector<D> {
104         FaultInjector {
105             inner,
106             state: State {
107                 rng_seed: seed,
108                 refilled_at: Instant::from_millis(0),
109                 tx_bucket: 0,
110                 rx_bucket: 0,
111             },
112             config: Config::default(),
113             rx_buf: [0u8; MTU],
114         }
115     }
116 
117     /// Return the underlying device, consuming the fault injector.
into_inner(self) -> D118     pub fn into_inner(self) -> D {
119         self.inner
120     }
121 
122     /// Return the probability of corrupting a packet, in percents.
corrupt_chance(&self) -> u8123     pub fn corrupt_chance(&self) -> u8 {
124         self.config.corrupt_pct
125     }
126 
127     /// Return the probability of dropping a packet, in percents.
drop_chance(&self) -> u8128     pub fn drop_chance(&self) -> u8 {
129         self.config.drop_pct
130     }
131 
132     /// Return the maximum packet size, in octets.
max_packet_size(&self) -> usize133     pub fn max_packet_size(&self) -> usize {
134         self.config.max_size
135     }
136 
137     /// Return the maximum packet transmission rate, in packets per second.
max_tx_rate(&self) -> u64138     pub fn max_tx_rate(&self) -> u64 {
139         self.config.max_tx_rate
140     }
141 
142     /// Return the maximum packet reception rate, in packets per second.
max_rx_rate(&self) -> u64143     pub fn max_rx_rate(&self) -> u64 {
144         self.config.max_rx_rate
145     }
146 
147     /// Return the interval for packet rate limiting, in milliseconds.
bucket_interval(&self) -> Duration148     pub fn bucket_interval(&self) -> Duration {
149         self.config.interval
150     }
151 
152     /// Set the probability of corrupting a packet, in percents.
153     ///
154     /// # Panics
155     /// This function panics if the probability is not between 0% and 100%.
set_corrupt_chance(&mut self, pct: u8)156     pub fn set_corrupt_chance(&mut self, pct: u8) {
157         if pct > 100 {
158             panic!("percentage out of range")
159         }
160         self.config.corrupt_pct = pct
161     }
162 
163     /// Set the probability of dropping a packet, in percents.
164     ///
165     /// # Panics
166     /// This function panics if the probability is not between 0% and 100%.
set_drop_chance(&mut self, pct: u8)167     pub fn set_drop_chance(&mut self, pct: u8) {
168         if pct > 100 {
169             panic!("percentage out of range")
170         }
171         self.config.drop_pct = pct
172     }
173 
174     /// Set the maximum packet size, in octets.
set_max_packet_size(&mut self, size: usize)175     pub fn set_max_packet_size(&mut self, size: usize) {
176         self.config.max_size = size
177     }
178 
179     /// Set the maximum packet transmission rate, in packets per interval.
set_max_tx_rate(&mut self, rate: u64)180     pub fn set_max_tx_rate(&mut self, rate: u64) {
181         self.config.max_tx_rate = rate
182     }
183 
184     /// Set the maximum packet reception rate, in packets per interval.
set_max_rx_rate(&mut self, rate: u64)185     pub fn set_max_rx_rate(&mut self, rate: u64) {
186         self.config.max_rx_rate = rate
187     }
188 
189     /// Set the interval for packet rate limiting, in milliseconds.
set_bucket_interval(&mut self, interval: Duration)190     pub fn set_bucket_interval(&mut self, interval: Duration) {
191         self.state.refilled_at = Instant::from_millis(0);
192         self.config.interval = interval
193     }
194 }
195 
196 impl<D: Device> Device for FaultInjector<D> {
197     type RxToken<'a> = RxToken<'a>
198     where
199         Self: 'a;
200     type TxToken<'a> = TxToken<'a, D::TxToken<'a>>
201     where
202         Self: 'a;
203 
capabilities(&self) -> DeviceCapabilities204     fn capabilities(&self) -> DeviceCapabilities {
205         let mut caps = self.inner.capabilities();
206         if caps.max_transmission_unit > MTU {
207             caps.max_transmission_unit = MTU;
208         }
209         caps
210     }
211 
receive(&mut self, timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)>212     fn receive(&mut self, timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
213         let (rx_token, tx_token) = self.inner.receive(timestamp)?;
214 
215         let len = super::RxToken::consume(rx_token, |buffer| {
216             if (self.config.max_size > 0 && buffer.len() > self.config.max_size)
217                 || buffer.len() > self.rx_buf.len()
218             {
219                 net_trace!("rx: dropping a packet that is too large");
220                 return None;
221             }
222             self.rx_buf[..buffer.len()].copy_from_slice(buffer);
223             Some(buffer.len())
224         })?;
225 
226         let buf = &mut self.rx_buf[..len];
227 
228         if self.state.maybe(self.config.drop_pct) {
229             net_trace!("rx: randomly dropping a packet");
230             return None;
231         }
232 
233         if !self.state.maybe_receive(&self.config, timestamp) {
234             net_trace!("rx: dropping a packet because of rate limiting");
235             return None;
236         }
237 
238         if self.state.maybe(self.config.corrupt_pct) {
239             net_trace!("rx: randomly corrupting a packet");
240             self.state.corrupt(&mut buf[..]);
241         }
242 
243         let rx = RxToken { buf };
244         let tx = TxToken {
245             state: &mut self.state,
246             config: self.config,
247             token: tx_token,
248             junk: [0; MTU],
249             timestamp,
250         };
251         Some((rx, tx))
252     }
253 
transmit(&mut self, timestamp: Instant) -> Option<Self::TxToken<'_>>254     fn transmit(&mut self, timestamp: Instant) -> Option<Self::TxToken<'_>> {
255         self.inner.transmit(timestamp).map(|token| TxToken {
256             state: &mut self.state,
257             config: self.config,
258             token,
259             junk: [0; MTU],
260             timestamp,
261         })
262     }
263 }
264 
265 #[doc(hidden)]
266 pub struct RxToken<'a> {
267     buf: &'a mut [u8],
268 }
269 
270 impl<'a> phy::RxToken for RxToken<'a> {
consume<R, F>(self, f: F) -> R where F: FnOnce(&mut [u8]) -> R,271     fn consume<R, F>(self, f: F) -> R
272     where
273         F: FnOnce(&mut [u8]) -> R,
274     {
275         f(self.buf)
276     }
277 }
278 
279 #[doc(hidden)]
280 pub struct TxToken<'a, Tx: phy::TxToken> {
281     state: &'a mut State,
282     config: Config,
283     token: Tx,
284     junk: [u8; MTU],
285     timestamp: Instant,
286 }
287 
288 impl<'a, Tx: phy::TxToken> phy::TxToken for TxToken<'a, Tx> {
consume<R, F>(mut self, len: usize, f: F) -> R where F: FnOnce(&mut [u8]) -> R,289     fn consume<R, F>(mut self, len: usize, f: F) -> R
290     where
291         F: FnOnce(&mut [u8]) -> R,
292     {
293         let drop = if self.state.maybe(self.config.drop_pct) {
294             net_trace!("tx: randomly dropping a packet");
295             true
296         } else if self.config.max_size > 0 && len > self.config.max_size {
297             net_trace!("tx: dropping a packet that is too large");
298             true
299         } else if !self.state.maybe_transmit(&self.config, self.timestamp) {
300             net_trace!("tx: dropping a packet because of rate limiting");
301             true
302         } else {
303             false
304         };
305 
306         if drop {
307             return f(&mut self.junk[..len]);
308         }
309 
310         self.token.consume(len, |mut buf| {
311             if self.state.maybe(self.config.corrupt_pct) {
312                 net_trace!("tx: corrupting a packet");
313                 self.state.corrupt(&mut buf)
314             }
315             f(buf)
316         })
317     }
318 }
319