2017-11-04 07:15:07 +08:00
|
|
|
use core::cell::RefCell;
|
|
|
|
|
2017-07-27 21:51:02 +08:00
|
|
|
use {Error, Result};
|
2017-11-04 07:15:07 +08:00
|
|
|
use phy::{self, DeviceCapabilities, Device};
|
2016-12-31 09:04:39 +08:00
|
|
|
|
|
|
|
// 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;
|
|
|
|
|
2017-05-29 18:53:30 +08:00
|
|
|
#[derive(Debug, Clone, Copy, Default)]
|
2016-12-31 09:04:39 +08:00
|
|
|
struct Config {
|
|
|
|
corrupt_pct: u8,
|
|
|
|
drop_pct: u8,
|
2017-05-29 18:53:30 +08:00
|
|
|
reorder_pct: u8,
|
|
|
|
max_size: usize,
|
|
|
|
max_tx_rate: u64,
|
|
|
|
max_rx_rate: u64,
|
2017-07-23 23:10:57 +08:00
|
|
|
interval: u64,
|
2017-05-29 18:53:30 +08:00
|
|
|
}
|
|
|
|
|
2017-11-04 07:15:07 +08:00
|
|
|
#[derive(Debug, Clone)]
|
2017-05-29 18:53:30 +08:00
|
|
|
struct State {
|
|
|
|
rng_seed: u32,
|
2017-07-23 23:10:57 +08:00
|
|
|
refilled_at: u64,
|
2017-05-29 18:53:30 +08:00
|
|
|
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) {
|
2017-09-25 08:55:54 +08:00
|
|
|
let buffer = buffer.as_mut();
|
2017-05-29 18:53:30 +08:00
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
2017-07-23 23:10:57 +08:00
|
|
|
fn refill(&mut self, config: &Config, timestamp: u64) {
|
2017-09-01 05:39:01 +08:00
|
|
|
if timestamp - self.refilled_at > config.interval {
|
2017-05-29 18:53:30 +08:00
|
|
|
self.tx_bucket = config.max_tx_rate;
|
|
|
|
self.rx_bucket = config.max_rx_rate;
|
2017-07-23 23:10:57 +08:00
|
|
|
self.refilled_at = timestamp;
|
2017-05-29 18:53:30 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-23 23:10:57 +08:00
|
|
|
fn maybe_transmit(&mut self, config: &Config, timestamp: u64) -> bool {
|
2017-05-29 18:53:30 +08:00
|
|
|
if config.max_tx_rate == 0 { return true }
|
|
|
|
|
2017-07-23 23:10:57 +08:00
|
|
|
self.refill(config, timestamp);
|
2017-05-29 18:53:30 +08:00
|
|
|
if self.tx_bucket > 0 {
|
|
|
|
self.tx_bucket -= 1;
|
|
|
|
true
|
|
|
|
} else {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-23 23:10:57 +08:00
|
|
|
fn maybe_receive(&mut self, config: &Config, timestamp: u64) -> bool {
|
2017-05-29 18:53:30 +08:00
|
|
|
if config.max_rx_rate == 0 { return true }
|
|
|
|
|
2017-07-23 23:10:57 +08:00
|
|
|
self.refill(config, timestamp);
|
2017-05-29 18:53:30 +08:00
|
|
|
if self.rx_bucket > 0 {
|
|
|
|
self.rx_bucket -= 1;
|
|
|
|
true
|
|
|
|
} else {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
2016-12-31 09:04:39 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// A fault injector device.
|
|
|
|
///
|
2017-05-29 18:53:30 +08:00
|
|
|
/// 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).
|
2016-12-31 09:04:39 +08:00
|
|
|
#[derive(Debug)]
|
2017-11-04 07:15:07 +08:00
|
|
|
pub struct FaultInjector<D: for<'a> Device<'a>> {
|
|
|
|
inner: D,
|
|
|
|
state: RefCell<State>,
|
|
|
|
config: Config,
|
2016-12-31 09:04:39 +08:00
|
|
|
}
|
|
|
|
|
2017-11-04 07:15:07 +08:00
|
|
|
impl<D: for<'a> Device<'a>> FaultInjector<D> {
|
2017-05-29 18:53:30 +08:00
|
|
|
/// Create a fault injector device, using the given random number generator seed.
|
2017-07-23 18:20:05 +08:00
|
|
|
pub fn new(inner: D, seed: u32) -> FaultInjector<D> {
|
2017-05-29 19:04:49 +08:00
|
|
|
let state = State {
|
|
|
|
rng_seed: seed,
|
2017-07-23 23:10:57 +08:00
|
|
|
refilled_at: 0,
|
2017-05-29 19:04:49 +08:00
|
|
|
tx_bucket: 0,
|
|
|
|
rx_bucket: 0,
|
|
|
|
};
|
2016-12-31 09:04:39 +08:00
|
|
|
FaultInjector {
|
2017-07-23 18:20:05 +08:00
|
|
|
inner: inner,
|
2017-11-04 07:15:07 +08:00
|
|
|
state: RefCell::new(state),
|
|
|
|
config: Config::default(),
|
2016-12-31 09:04:39 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-29 18:53:30 +08:00
|
|
|
/// Return the underlying device, consuming the fault injector.
|
2017-07-23 18:20:05 +08:00
|
|
|
pub fn into_inner(self) -> D {
|
|
|
|
self.inner
|
2016-12-31 09:04:39 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// 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
|
|
|
|
}
|
|
|
|
|
2017-05-29 18:53:30 +08:00
|
|
|
/// 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.
|
2017-07-23 23:10:57 +08:00
|
|
|
pub fn bucket_interval(&self) -> u64 {
|
2017-05-29 18:53:30 +08:00
|
|
|
self.config.interval
|
|
|
|
}
|
|
|
|
|
2016-12-31 09:04:39 +08:00
|
|
|
/// 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
|
|
|
|
}
|
2017-05-29 18:53:30 +08:00
|
|
|
|
|
|
|
/// 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.
|
2017-07-23 23:10:57 +08:00
|
|
|
pub fn set_bucket_interval(&mut self, interval: u64) {
|
2017-11-04 07:15:07 +08:00
|
|
|
self.state.borrow_mut().refilled_at = 0;
|
2017-05-29 18:53:30 +08:00
|
|
|
self.config.interval = interval
|
|
|
|
}
|
2016-12-31 09:04:39 +08:00
|
|
|
}
|
|
|
|
|
2017-11-04 07:15:07 +08:00
|
|
|
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>;
|
2016-12-31 09:04:39 +08:00
|
|
|
|
2017-09-16 17:04:07 +08:00
|
|
|
fn capabilities(&self) -> DeviceCapabilities {
|
|
|
|
let mut caps = self.inner.capabilities();
|
|
|
|
if caps.max_transmission_unit > MTU {
|
|
|
|
caps.max_transmission_unit = MTU;
|
2016-12-31 09:04:39 +08:00
|
|
|
}
|
2017-09-16 17:04:07 +08:00
|
|
|
caps
|
2016-12-31 09:04:39 +08:00
|
|
|
}
|
|
|
|
|
2017-11-04 07:15:07 +08:00
|
|
|
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: &state,
|
|
|
|
config: config,
|
|
|
|
token: rx_token,
|
|
|
|
corrupt: [0; MTU],
|
|
|
|
};
|
|
|
|
let tx = TxToken {
|
|
|
|
state: &state,
|
|
|
|
config: 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: &state,
|
|
|
|
config: config,
|
|
|
|
token: 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> {
|
2017-11-04 07:28:10 +08:00
|
|
|
fn consume<R, F>(self, timestamp: u64, f: F) -> Result<R>
|
|
|
|
where F: FnOnce(&[u8]) -> Result<R>
|
|
|
|
{
|
2017-11-04 07:15:07 +08:00
|
|
|
if self.state.borrow_mut().maybe(self.config.drop_pct) {
|
2017-05-29 18:53:30 +08:00
|
|
|
net_trace!("rx: randomly dropping a packet");
|
2016-12-31 09:04:39 +08:00
|
|
|
return Err(Error::Exhausted)
|
|
|
|
}
|
2017-11-04 07:15:07 +08:00
|
|
|
if !self.state.borrow_mut().maybe_receive(&self.config, timestamp) {
|
2017-05-29 18:53:30 +08:00
|
|
|
net_trace!("rx: dropping a packet because of rate limiting");
|
|
|
|
return Err(Error::Exhausted)
|
2016-12-31 09:04:39 +08:00
|
|
|
}
|
2017-11-04 07:15:07 +08:00
|
|
|
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)
|
|
|
|
}
|
2016-12-31 09:04:39 +08:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[doc(hidden)]
|
2017-11-04 07:15:07 +08:00
|
|
|
pub struct TxToken<'a, Tx: phy::TxToken> {
|
|
|
|
state: &'a RefCell<State>,
|
2016-12-31 09:04:39 +08:00
|
|
|
config: Config,
|
2017-11-04 07:15:07 +08:00
|
|
|
token: Tx,
|
2016-12-31 09:10:46 +08:00
|
|
|
junk: [u8; MTU],
|
2016-12-31 09:04:39 +08:00
|
|
|
}
|
|
|
|
|
2017-11-04 07:15:07 +08:00
|
|
|
impl<'a, Tx: phy::TxToken> phy::TxToken for TxToken<'a, Tx> {
|
2017-11-04 07:28:10 +08:00
|
|
|
fn consume<R, F>(mut self, timestamp: u64, len: usize, f: F) -> Result<R>
|
|
|
|
where F: FnOnce(&mut [u8]) -> Result<R>
|
2017-11-04 07:15:07 +08:00
|
|
|
{
|
|
|
|
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
|
|
|
|
};
|
2016-12-31 09:04:39 +08:00
|
|
|
|
2017-11-04 07:15:07 +08:00
|
|
|
if drop {
|
|
|
|
return f(&mut self.junk);
|
2016-12-31 09:04:39 +08:00
|
|
|
}
|
|
|
|
|
2017-11-04 07:15:07 +08:00
|
|
|
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)
|
|
|
|
})
|
2016-12-31 09:04:39 +08:00
|
|
|
}
|
|
|
|
}
|