fault_injector.rs 8.9 KB

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