Limit the rate at which sockets will request neighbor discovery.

The rate of emission of neighbor discovery packets is already
limited at the level of the entire neighbor cache, but poll()
would uselessly spin until the answer arrives (if ever).
v0.7.x
whitequark 2017-11-22 07:20:31 +00:00
parent 6c83936872
commit 98a3ec8c3a
3 changed files with 47 additions and 14 deletions

View File

@ -210,6 +210,12 @@ impl<'b, 'c, DeviceT> Interface<'b, 'c, DeviceT>
caps.max_transmission_unit -= EthernetFrame::<&[u8]>::header_len();
for mut socket in sockets.iter_mut() {
if let Some(hushed_until) = socket.meta().hushed_until {
if hushed_until > timestamp {
continue
}
}
let mut device_result = Ok(());
let &mut Self { ref mut device, ref mut inner } = self;
let socket_result =
@ -218,7 +224,8 @@ impl<'b, 'c, DeviceT> Interface<'b, 'c, DeviceT>
Socket::Raw(ref mut socket) =>
socket.dispatch(|response| {
let tx_token = device.transmit().ok_or(Error::Exhausted)?;
device_result = inner.dispatch(tx_token, timestamp, Packet::Raw(response));
device_result = inner.dispatch(tx_token, timestamp,
Packet::Raw(response));
device_result
}, &caps.checksum),
#[cfg(feature = "socket-icmp")]
@ -237,27 +244,47 @@ impl<'b, 'c, DeviceT> Interface<'b, 'c, DeviceT>
Socket::Udp(ref mut socket) =>
socket.dispatch(|response| {
let tx_token = device.transmit().ok_or(Error::Exhausted)?;
device_result = inner.dispatch(tx_token, timestamp, Packet::Udp(response));
device_result = inner.dispatch(tx_token, timestamp,
Packet::Udp(response));
device_result
}),
#[cfg(feature = "socket-tcp")]
Socket::Tcp(ref mut socket) =>
socket.dispatch(timestamp, &caps, |response| {
let tx_token = device.transmit().ok_or(Error::Exhausted)?;
device_result = inner.dispatch(tx_token, timestamp, Packet::Tcp(response));
device_result = inner.dispatch(tx_token, timestamp,
Packet::Tcp(response));
device_result
}),
Socket::__Nonexhaustive(_) => unreachable!()
};
match (device_result, socket_result) {
(Err(Error::Unaddressable), _) => break, // no one to transmit to
(Err(Error::Exhausted), _) => break, // nowhere to transmit
(Ok(()), Err(Error::Exhausted)) => (), // nothing to transmit
(Err(Error::Unaddressable), _) => {
// `NeighborCache` already takes care of rate limiting the neighbor discovery
// requests from the socket. However, without an additional rate limiting
// mechanism, we would spin on every socket that has yet to discover its
// peer/neighboor./
if let None = socket.meta_mut().hushed_until {
net_trace!("{}: hushed", socket.meta().handle);
}
socket.meta_mut().hushed_until =
Some(timestamp + NeighborCache::SILENT_TIME);
break
}
(Err(err), _) | (_, Err(err)) => {
net_debug!("cannot dispatch egress packet: {}", err);
net_debug!("{}: cannot dispatch egress packet: {}",
socket.meta().handle, err);
return Err(err)
}
(Ok(()), Ok(())) => ()
(Ok(()), Ok(())) => {
// We definitely have a neighbor cache entry now, so this socket does not
// need to be hushed anymore.
if let Some(_) = socket.meta_mut().hushed_until.take() {
net_trace!("{}: unhushed", socket.meta().handle);
}
}
}
}
Ok(())
@ -364,7 +391,7 @@ impl<'b, 'c> InterfaceInner<'b, 'c> {
}
if eth_frame.src_addr().is_unicast() {
// Fill the ARP cache from IP header of unicast frames.
// Fill the neighbor cache from IP header of unicast frames.
self.neighbor_cache.fill(IpAddress::Ipv4(ipv4_repr.src_addr),
eth_frame.src_addr(),
timestamp);
@ -688,7 +715,7 @@ impl<'b, 'c> InterfaceInner<'b, 'c> {
match (src_addr, dst_addr) {
(&IpAddress::Ipv4(src_addr), IpAddress::Ipv4(dst_addr)) => {
net_debug!("address {} not in ARP cache, sending request",
net_debug!("address {} not in neighbor cache, sending ARP request",
dst_addr);
let arp_repr = ArpRepr::EthernetIpv4 {

View File

@ -51,11 +51,11 @@ pub struct Cache<'a> {
}
impl<'a> Cache<'a> {
/// Flood protection delay, in milliseconds.
const FLOOD_TIMER: u64 = 1_000;
/// Minimum delay between discovery requests, in milliseconds.
pub(crate) const SILENT_TIME: u64 = 1_000;
/// Neighbor entry lifetime, in milliseconds.
const ENTRY_LIFETIME: u64 = 60_000;
pub(crate) const ENTRY_LIFETIME: u64 = 60_000;
/// Create a cache. The backing storage is cleared upon creation.
///
@ -148,7 +148,7 @@ impl<'a> Cache<'a> {
None if timestamp < self.hushed_until =>
Answer::Hushed,
None => {
self.hushed_until = timestamp + Self::FLOOD_TIMER;
self.hushed_until = timestamp + Self::SILENT_TIME;
Answer::NotFound
}
}

View File

@ -80,7 +80,11 @@ pub enum Socket<'a, 'b: 'a> {
/// is interested in, but which are more conveniently stored inside the socket itself.
#[derive(Debug, Default)]
pub(crate) struct SocketMeta {
handle: SocketHandle,
/// Handle of this socket within its enclosing `SocketSet`.
/// Mainly useful for debug output.
pub(crate) handle: SocketHandle,
/// A lower limit on the timestamp returned from the socket's `poll_at()` method.
pub(crate) hushed_until: Option<u64>,
}
macro_rules! dispatch_socket {
@ -101,6 +105,7 @@ macro_rules! dispatch_socket {
impl<'a, 'b> Socket<'a, 'b> {
/// Return the socket handle.
#[inline]
pub fn handle(&self) -> SocketHandle {
self.meta().handle
}
@ -114,7 +119,8 @@ impl<'a, 'b> Socket<'a, 'b> {
}
pub(crate) fn poll_at(&self) -> Option<u64> {
dispatch_socket!(self, |socket []| socket.poll_at())
let poll_at = dispatch_socket!(self, |socket []| socket.poll_at());
self.meta().hushed_until.or(poll_at)
}
}