123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349 |
- use core::cell::RefCell;
- use crate::phy::{self, Device, DeviceCapabilities};
- use crate::time::{Duration, Instant};
- use crate::{Error, Result};
- // We use our own RNG to stay compatible with #![no_std].
- // The use of the RNG below has a slight bias, but it doesn't matter.
- fn xorshift32(state: &mut u32) -> u32 {
- let mut x = *state;
- x ^= x << 13;
- x ^= x >> 17;
- x ^= x << 5;
- *state = x;
- x
- }
- // This could be fixed once associated consts are stable.
- const MTU: usize = 1536;
- #[derive(Debug, Default, Clone, Copy)]
- #[cfg_attr(feature = "defmt", derive(defmt::Format))]
- struct Config {
- corrupt_pct: u8,
- drop_pct: u8,
- reorder_pct: u8,
- max_size: usize,
- max_tx_rate: u64,
- max_rx_rate: u64,
- interval: Duration,
- }
- #[derive(Debug, Clone)]
- #[cfg_attr(feature = "defmt", derive(defmt::Format))]
- struct State {
- rng_seed: u32,
- refilled_at: Instant,
- tx_bucket: u64,
- rx_bucket: u64,
- }
- impl State {
- fn maybe(&mut self, pct: u8) -> bool {
- xorshift32(&mut self.rng_seed) % 100 < pct as u32
- }
- fn corrupt<T: AsMut<[u8]>>(&mut self, mut buffer: T) {
- let buffer = buffer.as_mut();
- // We introduce a single bitflip, as the most likely, and the hardest to detect, error.
- let index = (xorshift32(&mut self.rng_seed) as usize) % buffer.len();
- let bit = 1 << (xorshift32(&mut self.rng_seed) % 8) as u8;
- buffer[index] ^= bit;
- }
- fn refill(&mut self, config: &Config, timestamp: Instant) {
- if timestamp - self.refilled_at > config.interval {
- self.tx_bucket = config.max_tx_rate;
- self.rx_bucket = config.max_rx_rate;
- self.refilled_at = timestamp;
- }
- }
- fn maybe_transmit(&mut self, config: &Config, timestamp: Instant) -> bool {
- if config.max_tx_rate == 0 {
- return true;
- }
- self.refill(config, timestamp);
- if self.tx_bucket > 0 {
- self.tx_bucket -= 1;
- true
- } else {
- false
- }
- }
- fn maybe_receive(&mut self, config: &Config, timestamp: Instant) -> bool {
- if config.max_rx_rate == 0 {
- return true;
- }
- self.refill(config, timestamp);
- if self.rx_bucket > 0 {
- self.rx_bucket -= 1;
- true
- } else {
- false
- }
- }
- }
- /// A fault injector device.
- ///
- /// A fault injector is a device that alters packets traversing through it to simulate
- /// adverse network conditions (such as random packet loss or corruption), or software
- /// or hardware limitations (such as a limited number or size of usable network buffers).
- #[derive(Debug)]
- pub struct FaultInjector<D: for<'a> Device<'a>> {
- inner: D,
- state: RefCell<State>,
- config: Config,
- }
- impl<D: for<'a> Device<'a>> FaultInjector<D> {
- /// Create a fault injector device, using the given random number generator seed.
- pub fn new(inner: D, seed: u32) -> FaultInjector<D> {
- let state = State {
- rng_seed: seed,
- refilled_at: Instant::from_millis(0),
- tx_bucket: 0,
- rx_bucket: 0,
- };
- FaultInjector {
- inner,
- state: RefCell::new(state),
- config: Config::default(),
- }
- }
- /// Return the underlying device, consuming the fault injector.
- pub fn into_inner(self) -> D {
- self.inner
- }
- /// Return the probability of corrupting a packet, in percents.
- pub fn corrupt_chance(&self) -> u8 {
- self.config.corrupt_pct
- }
- /// Return the probability of dropping a packet, in percents.
- pub fn drop_chance(&self) -> u8 {
- self.config.drop_pct
- }
- /// Return the maximum packet size, in octets.
- pub fn max_packet_size(&self) -> usize {
- self.config.max_size
- }
- /// Return the maximum packet transmission rate, in packets per second.
- pub fn max_tx_rate(&self) -> u64 {
- self.config.max_rx_rate
- }
- /// Return the maximum packet reception rate, in packets per second.
- pub fn max_rx_rate(&self) -> u64 {
- self.config.max_tx_rate
- }
- /// Return the interval for packet rate limiting, in milliseconds.
- pub fn bucket_interval(&self) -> Duration {
- self.config.interval
- }
- /// Set the probability of corrupting a packet, in percents.
- ///
- /// # Panics
- /// This function panics if the probability is not between 0% and 100%.
- pub fn set_corrupt_chance(&mut self, pct: u8) {
- if pct > 100 {
- panic!("percentage out of range")
- }
- self.config.corrupt_pct = pct
- }
- /// Set the probability of dropping a packet, in percents.
- ///
- /// # Panics
- /// This function panics if the probability is not between 0% and 100%.
- pub fn set_drop_chance(&mut self, pct: u8) {
- if pct > 100 {
- panic!("percentage out of range")
- }
- self.config.drop_pct = pct
- }
- /// Set the maximum packet size, in octets.
- pub fn set_max_packet_size(&mut self, size: usize) {
- self.config.max_size = size
- }
- /// Set the maximum packet transmission rate, in packets per interval.
- pub fn set_max_tx_rate(&mut self, rate: u64) {
- self.config.max_tx_rate = rate
- }
- /// Set the maximum packet reception rate, in packets per interval.
- pub fn set_max_rx_rate(&mut self, rate: u64) {
- self.config.max_rx_rate = rate
- }
- /// Set the interval for packet rate limiting, in milliseconds.
- pub fn set_bucket_interval(&mut self, interval: Duration) {
- self.state.borrow_mut().refilled_at = Instant::from_millis(0);
- self.config.interval = interval
- }
- }
- impl<'a, D> Device<'a> for FaultInjector<D>
- where
- D: for<'b> Device<'b>,
- {
- type RxToken = RxToken<'a, <D as Device<'a>>::RxToken>;
- type TxToken = TxToken<'a, <D as Device<'a>>::TxToken>;
- fn capabilities(&self) -> DeviceCapabilities {
- let mut caps = self.inner.capabilities();
- if caps.max_transmission_unit > MTU {
- caps.max_transmission_unit = MTU;
- }
- caps
- }
- fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> {
- let &mut Self {
- ref mut inner,
- ref state,
- config,
- } = self;
- inner.receive().map(|(rx_token, tx_token)| {
- let rx = RxToken {
- state,
- config,
- token: rx_token,
- corrupt: [0; MTU],
- };
- let tx = TxToken {
- state,
- config,
- token: tx_token,
- junk: [0; MTU],
- };
- (rx, tx)
- })
- }
- fn transmit(&'a mut self) -> Option<Self::TxToken> {
- let &mut Self {
- ref mut inner,
- ref state,
- config,
- } = self;
- inner.transmit().map(|token| TxToken {
- state,
- config,
- token,
- junk: [0; MTU],
- })
- }
- }
- #[doc(hidden)]
- pub struct RxToken<'a, Rx: phy::RxToken> {
- state: &'a RefCell<State>,
- config: Config,
- token: Rx,
- corrupt: [u8; MTU],
- }
- impl<'a, Rx: phy::RxToken> phy::RxToken for RxToken<'a, Rx> {
- fn consume<R, F>(self, timestamp: Instant, f: F) -> Result<R>
- where
- F: FnOnce(&mut [u8]) -> Result<R>,
- {
- if self.state.borrow_mut().maybe(self.config.drop_pct) {
- net_trace!("rx: randomly dropping a packet");
- return Err(Error::Exhausted);
- }
- if !self
- .state
- .borrow_mut()
- .maybe_receive(&self.config, timestamp)
- {
- net_trace!("rx: dropping a packet because of rate limiting");
- return Err(Error::Exhausted);
- }
- let Self {
- token,
- config,
- state,
- mut corrupt,
- } = self;
- token.consume(timestamp, |buffer| {
- if config.max_size > 0 && buffer.as_ref().len() > config.max_size {
- net_trace!("rx: dropping a packet that is too large");
- return Err(Error::Exhausted);
- }
- if state.borrow_mut().maybe(config.corrupt_pct) {
- net_trace!("rx: randomly corrupting a packet");
- let mut corrupt = &mut corrupt[..buffer.len()];
- corrupt.copy_from_slice(buffer);
- state.borrow_mut().corrupt(&mut corrupt);
- f(&mut corrupt)
- } else {
- f(buffer)
- }
- })
- }
- }
- #[doc(hidden)]
- pub struct TxToken<'a, Tx: phy::TxToken> {
- state: &'a RefCell<State>,
- config: Config,
- token: Tx,
- junk: [u8; MTU],
- }
- impl<'a, Tx: phy::TxToken> phy::TxToken for TxToken<'a, Tx> {
- fn consume<R, F>(mut self, timestamp: Instant, len: usize, f: F) -> Result<R>
- where
- F: FnOnce(&mut [u8]) -> Result<R>,
- {
- let drop = if self.state.borrow_mut().maybe(self.config.drop_pct) {
- net_trace!("tx: randomly dropping a packet");
- true
- } else if self.config.max_size > 0 && len > self.config.max_size {
- net_trace!("tx: dropping a packet that is too large");
- true
- } else if !self
- .state
- .borrow_mut()
- .maybe_transmit(&self.config, timestamp)
- {
- net_trace!("tx: dropping a packet because of rate limiting");
- true
- } else {
- false
- };
- if drop {
- return f(&mut self.junk[..len]);
- }
- let Self {
- token,
- state,
- config,
- ..
- } = self;
- token.consume(timestamp, len, |mut buf| {
- if state.borrow_mut().maybe(config.corrupt_pct) {
- net_trace!("tx: corrupting a packet");
- state.borrow_mut().corrupt(&mut buf)
- }
- f(buf)
- })
- }
- }
|