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