Inject the current timestamp into Device::{transmit,receive}.

Various parts of smoltcp require an arrow of time; a monotonically
increasing timestamp. Most obviously this is TCP sockets, but
the tracer and the pcap writer devices also benefit from having
timestamps. There are a few ways this could be implemented:
  1. using a static Cell, global for the entire smoltcp crate;
  2. using a static method on Device;
  3. using an instance method on Device;
  4. passing the current timestamp into *Interface::poll.

The first two options are undesirable because they create a notion
of global clock, and interfere e.g. with mocking.
The third option is undesirable because not all devices are
inherently tied to a particular clock, e.g. a loopback device isn't.

Therefore, the timestamp is injected into both sockets and devices
through the *Interface::poll method.
v0.7.x
whitequark 2017-07-23 09:44:54 +00:00
parent fe6fb087e5
commit b97cacd521
10 changed files with 37 additions and 52 deletions

View File

@ -77,7 +77,7 @@ fn main() {
}
let mut device = Loopback::new();
let mut device = EthernetTracer::new(device, |printer| trace!("{}", printer));
let mut device = EthernetTracer::new(device, |printer, _timestamp| trace!("{}", printer));
let mut arp_cache_entries: [_; 8] = Default::default();
let mut arp_cache = SliceArpCache::new(&mut arp_cache_entries[..]);

View File

@ -8,7 +8,7 @@ fn main() {
let ifname = env::args().nth(1).unwrap();
let mut socket = RawSocket::new(ifname.as_ref()).unwrap();
loop {
let buffer = socket.receive().unwrap();
let buffer = socket.receive(/*timestamp=*/0).unwrap();
print!("{}", PrettyPrinter::<EthernetFrame<&[u8]>>::new("", &buffer))
}
}

View File

@ -78,7 +78,7 @@ pub fn setup_device(more_args: &[&str])
let seed = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().subsec_nanos();
let device = TapInterface::new(&matches.free[0]).unwrap();
let device = EthernetTracer::new(device, |printer| trace!("{}", printer));
let device = EthernetTracer::new(device, |printer, _timestamp| trace!("{}", printer));
let mut device = FaultInjector::new(device, seed);
device.set_drop_chance(drop_chance);
device.set_corrupt_chance(corrupt_chance);

View File

@ -116,7 +116,7 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
}
// Now, receive any incoming packets.
let rx_buffer = self.device.receive()?;
let rx_buffer = self.device.receive(timestamp)?;
let eth_frame = EthernetFrame::new_checked(&rx_buffer)?;
// Ignore any packets not directed to our hardware address.
@ -134,7 +134,7 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
_ => return Err(Error::Unrecognized),
};
self.send_response(response)
self.send_response(timestamp, response)
}
// Snoop all ARP traffic, and respond to ARP packets directed at us.
@ -360,7 +360,7 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
Ok(Response::Icmpv4(ipv4_reply_repr, icmp_reply_repr))
}
fn send_response(&mut self, response: Response) -> Result<(), Error> {
fn send_response(&mut self, timestamp: u64, response: Response) -> Result<(), Error> {
macro_rules! ip_response {
($tx_buffer:ident, $frame:ident, $ip_repr:ident) => ({
let dst_hardware_addr =
@ -371,7 +371,7 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
let frame_len = EthernetFrame::<&[u8]>::buffer_len($ip_repr.buffer_len() +
$ip_repr.payload_len);
$tx_buffer = self.device.transmit(frame_len)?;
$tx_buffer = self.device.transmit(timestamp, frame_len)?;
$frame = EthernetFrame::new_checked(&mut $tx_buffer)
.expect("transmit frame too small");
$frame.set_src_addr(self.hardware_addr);
@ -387,7 +387,7 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
match response {
Response::Arp(repr) => {
let tx_len = EthernetFrame::<&[u8]>::buffer_len(repr.buffer_len());
let mut tx_buffer = self.device.transmit(tx_len)?;
let mut tx_buffer = self.device.transmit(timestamp, tx_len)?;
let mut frame = EthernetFrame::new_checked(&mut tx_buffer)
.expect("transmit frame too small");
frame.set_src_addr(self.hardware_addr);
@ -449,7 +449,7 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
Some(dst_hardware_addr) => {
let tx_len = EthernetFrame::<&[u8]>::buffer_len(repr.buffer_len() +
payload.buffer_len());
let mut tx_buffer = device.transmit(tx_len)?;
let mut tx_buffer = device.transmit(timestamp, tx_len)?;
let mut frame = EthernetFrame::new_checked(&mut tx_buffer)
.expect("transmit frame too small");
frame.set_src_addr(src_hardware_addr);
@ -480,7 +480,7 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
};
let tx_len = EthernetFrame::<&[u8]>::buffer_len(payload.buffer_len());
let mut tx_buffer = device.transmit(tx_len)?;
let mut tx_buffer = device.transmit(timestamp, tx_len)?;
let mut frame = EthernetFrame::new_checked(&mut tx_buffer)
.expect("transmit frame too small");
frame.set_src_addr(src_hardware_addr);

View File

@ -229,8 +229,8 @@ impl<T: Device> Device for FaultInjector<T>
limits
}
fn receive(&mut self) -> Result<Self::RxBuffer, Error> {
let mut buffer = self.lower.receive()?;
fn receive(&mut self, timestamp: u64) -> Result<Self::RxBuffer, Error> {
let mut buffer = self.lower.receive(timestamp)?;
if self.state.maybe(self.config.drop_pct) {
net_trace!("rx: randomly dropping a packet");
return Err(Error::Exhausted)
@ -250,7 +250,7 @@ impl<T: Device> Device for FaultInjector<T>
Ok(buffer)
}
fn transmit(&mut self, length: usize) -> Result<Self::TxBuffer, Error> {
fn transmit(&mut self, timestamp: u64, length: usize) -> Result<Self::TxBuffer, Error> {
let buffer;
if self.state.maybe(self.config.drop_pct) {
net_trace!("tx: randomly dropping a packet");
@ -262,7 +262,7 @@ impl<T: Device> Device for FaultInjector<T>
net_trace!("tx: dropping a packet because of rate limiting");
buffer = None;
} else {
buffer = Some(self.lower.transmit(length)?);
buffer = Some(self.lower.transmit(timestamp, length)?);
}
Ok(TxBuffer {
buffer: buffer,

View File

@ -39,14 +39,14 @@ impl Device for Loopback {
}
}
fn receive(&mut self) -> Result<Self::RxBuffer, Error> {
fn receive(&mut self, _timestamp: u64) -> Result<Self::RxBuffer, Error> {
match self.0.borrow_mut().pop_front() {
Some(packet) => Ok(packet),
None => Err(Error::Exhausted)
}
}
fn transmit(&mut self, length: usize) -> Result<Self::TxBuffer, Error> {
fn transmit(&mut self, _timestamp: u64, length: usize) -> Result<Self::TxBuffer, Error> {
let mut buffer = Vec::new();
buffer.resize(length, 0);
Ok(TxBuffer {

View File

@ -61,7 +61,7 @@ impl Device for EthernetDevice {
limits
}
fn receive(&mut self) -> Result<Self::RxBuffer, Error> {
fn receive(&mut self, _timestamp: u64) -> Result<Self::RxBuffer, Error> {
if rx_full() {
let index = self.rx_next;
self.rx_next = (self.rx_next + 1) % RX_BUFFERS.len();
@ -75,7 +75,7 @@ impl Device for EthernetDevice {
}
}
fn transmit(&mut self, length: usize) -> Result<Self::TxBuffer, Error> {
fn transmit(&mut self, _timestamp: u64, length: usize) -> Result<Self::TxBuffer, Error> {
if tx_empty() {
let index = self.tx_next;
self.tx_next = (self.tx_next + 1) % TX_BUFFERS.len();
@ -175,12 +175,12 @@ pub trait Device {
/// It is expected that a `receive` implementation, once a packet is written to memory
/// through DMA, would gain ownership of the underlying buffer, provide it for parsing,
/// and return it to the network device once it is dropped.
fn receive(&mut self) -> Result<Self::RxBuffer, Error>;
fn receive(&mut self, timestamp: u64) -> Result<Self::RxBuffer, Error>;
/// Transmit a frame.
///
/// It is expected that a `transmit` implementation would gain ownership of a buffer with
/// the requested length, provide it for emission, and schedule it to be read from
/// memory by the network device once it is dropped.
fn transmit(&mut self, length: usize) -> Result<Self::TxBuffer, Error>;
fn transmit(&mut self, timestamp: u64, length: usize) -> Result<Self::TxBuffer, Error>;
}

View File

@ -40,7 +40,7 @@ impl Device for RawSocket {
}
}
fn receive(&mut self) -> Result<Self::RxBuffer, Error> {
fn receive(&mut self, _timestamp: u64) -> Result<Self::RxBuffer, Error> {
let mut lower = self.lower.borrow_mut();
let mut buffer = vec![0; self.mtu];
let size = lower.recv(&mut buffer[..]).unwrap();
@ -48,7 +48,7 @@ impl Device for RawSocket {
Ok(buffer)
}
fn transmit(&mut self, length: usize) -> Result<Self::TxBuffer, Error> {
fn transmit(&mut self, _timestamp: u64, length: usize) -> Result<Self::TxBuffer, Error> {
Ok(TxBuffer {
lower: self.lower.clone(),
buffer: vec![0; length]

View File

@ -41,7 +41,7 @@ impl Device for TapInterface {
}
}
fn receive(&mut self) -> Result<Self::RxBuffer, Error> {
fn receive(&mut self, _timestamp: u64) -> Result<Self::RxBuffer, Error> {
let mut lower = self.lower.borrow_mut();
let mut buffer = vec![0; self.mtu];
match lower.recv(&mut buffer[..]) {
@ -56,7 +56,7 @@ impl Device for TapInterface {
}
}
fn transmit(&mut self, length: usize) -> Result<Self::TxBuffer, Error> {
fn transmit(&mut self, _timestamp: u64, length: usize) -> Result<Self::TxBuffer, Error> {
Ok(TxBuffer {
lower: self.lower.clone(),
buffer: vec![0; length]

View File

@ -8,26 +8,13 @@ use super::{DeviceLimits, Device};
/// using the provided writer function, and then passes them to another
/// device.
pub struct Tracer<T: Device, U: PrettyPrint> {
lower: T,
writer: fn(PrettyPrinter<U>)
lower: T,
writer: fn(u64, PrettyPrinter<U>)
}
impl<T: Device, U: PrettyPrint> Tracer<T, U> {
/// Create a tracer device.
pub fn new(lower: T, writer: fn(PrettyPrinter<U>)) -> Tracer<T, U> {
Tracer {
lower: lower,
writer: writer
}
}
/// Create a tracer device, printing to standard output.
#[cfg(feature = "std")]
pub fn new_stdout(lower: T) -> Tracer<T, U> {
fn writer<U: PrettyPrint>(printer: PrettyPrinter<U>) {
print!("{}", printer)
}
pub fn new(lower: T, writer: fn(timestamp: u64, printer: PrettyPrinter<U>)) -> Tracer<T, U> {
Tracer {
lower: lower,
writer: writer
@ -46,25 +33,23 @@ impl<T: Device, U: PrettyPrint> Device for Tracer<T, U> {
fn limits(&self) -> DeviceLimits { self.lower.limits() }
fn receive(&mut self) -> Result<Self::RxBuffer, Error> {
let buffer = self.lower.receive()?;
(self.writer)(PrettyPrinter::<U>::new("<- ", &buffer));
fn receive(&mut self, timestamp: u64) -> Result<Self::RxBuffer, Error> {
let buffer = self.lower.receive(timestamp)?;
(self.writer)(timestamp, PrettyPrinter::<U>::new("<- ", &buffer));
Ok(buffer)
}
fn transmit(&mut self, length: usize) -> Result<Self::TxBuffer, Error> {
let buffer = self.lower.transmit(length)?;
Ok(TxBuffer {
buffer: buffer,
writer: self.writer
})
fn transmit(&mut self, timestamp: u64, length: usize) -> Result<Self::TxBuffer, Error> {
let buffer = self.lower.transmit(timestamp, length)?;
Ok(TxBuffer { buffer, timestamp, writer: self.writer })
}
}
#[doc(hidden)]
pub struct TxBuffer<T: AsRef<[u8]>, U: PrettyPrint> {
buffer: T,
writer: fn(PrettyPrinter<U>)
buffer: T,
timestamp: u64,
writer: fn(u64, PrettyPrinter<U>)
}
impl<T: AsRef<[u8]>, U: PrettyPrint> AsRef<[u8]>
@ -79,6 +64,6 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>, U: PrettyPrint> AsMut<[u8]>
impl<T: AsRef<[u8]>, U: PrettyPrint> Drop for TxBuffer<T, U> {
fn drop(&mut self) {
(self.writer)(PrettyPrinter::<U>::new("-> ", &self.buffer));
(self.writer)(self.timestamp, PrettyPrinter::<U>::new("-> ", &self.buffer));
}
}