Trace neighbor discovery status on a per-socket basis.
This avoids delaying the first packets for new neighbors by (at least) the ARP cache silence time, or potentially even indefinitely.v0.7.x
parent
16b59eefad
commit
8dd9bdeaad
|
@ -67,6 +67,22 @@ enum Packet<'a> {
|
|||
Tcp((IpRepr, TcpRepr<'a>))
|
||||
}
|
||||
|
||||
impl<'a> Packet<'a> {
|
||||
fn neighbor_addr(&self) -> Option<IpAddress> {
|
||||
match self {
|
||||
&Packet::None |
|
||||
&Packet::Arp(_) =>
|
||||
None,
|
||||
&Packet::Icmpv4((ref ipv4_repr, _)) =>
|
||||
Some(ipv4_repr.dst_addr.into()),
|
||||
&Packet::Raw((ref ip_repr, _)) |
|
||||
&Packet::Udp((ref ip_repr, _)) |
|
||||
&Packet::Tcp((ref ip_repr, _)) =>
|
||||
Some(ip_repr.dst_addr())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b, 'c, DeviceT> Interface<'b, 'c, DeviceT>
|
||||
where DeviceT: for<'d> Device<'d> {
|
||||
/// Create a network interface using the provided network device.
|
||||
|
@ -167,7 +183,11 @@ impl<'b, 'c, DeviceT> Interface<'b, 'c, DeviceT>
|
|||
if self.socket_ingress(sockets, timestamp)? {
|
||||
Ok(Some(0))
|
||||
} else {
|
||||
Ok(sockets.iter().filter_map(|socket| socket.poll_at()).min())
|
||||
Ok(sockets.iter().filter_map(|socket| {
|
||||
let socket_poll_at = socket.poll_at();
|
||||
socket.meta().poll_at(socket_poll_at, |ip_addr|
|
||||
self.inner.has_neighbor(&ip_addr, timestamp))
|
||||
}).min())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -203,12 +223,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
|
||||
}
|
||||
if !socket.meta_mut().egress_permitted(|ip_addr|
|
||||
self.inner.has_neighbor(&ip_addr, timestamp)) {
|
||||
continue
|
||||
}
|
||||
|
||||
let mut neighbor_addr = None;
|
||||
let mut device_result = Ok(());
|
||||
let &mut Self { ref mut device, ref mut inner } = self;
|
||||
let socket_result =
|
||||
|
@ -216,9 +236,10 @@ impl<'b, 'c, DeviceT> Interface<'b, 'c, DeviceT>
|
|||
#[cfg(feature = "socket-raw")]
|
||||
Socket::Raw(ref mut socket) =>
|
||||
socket.dispatch(|response| {
|
||||
let response = Packet::Raw(response);
|
||||
neighbor_addr = response.neighbor_addr();
|
||||
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, response);
|
||||
device_result
|
||||
}, &caps.checksum),
|
||||
#[cfg(feature = "socket-icmp")]
|
||||
|
@ -226,9 +247,11 @@ impl<'b, 'c, DeviceT> Interface<'b, 'c, DeviceT>
|
|||
socket.dispatch(&caps, |response| {
|
||||
let tx_token = device.transmit().ok_or(Error::Exhausted)?;
|
||||
device_result = match response {
|
||||
(IpRepr::Ipv4(ipv4_repr), icmpv4_repr) =>
|
||||
inner.dispatch(tx_token, timestamp,
|
||||
Packet::Icmpv4((ipv4_repr, icmpv4_repr))),
|
||||
(IpRepr::Ipv4(ipv4_repr), icmpv4_repr) => {
|
||||
let response = Packet::Icmpv4((ipv4_repr, icmpv4_repr));
|
||||
neighbor_addr = response.neighbor_addr();
|
||||
inner.dispatch(tx_token, timestamp, response)
|
||||
}
|
||||
_ => Err(Error::Unaddressable),
|
||||
};
|
||||
device_result
|
||||
|
@ -236,17 +259,19 @@ impl<'b, 'c, DeviceT> Interface<'b, 'c, DeviceT>
|
|||
#[cfg(feature = "socket-udp")]
|
||||
Socket::Udp(ref mut socket) =>
|
||||
socket.dispatch(|response| {
|
||||
let response = Packet::Udp(response);
|
||||
neighbor_addr = response.neighbor_addr();
|
||||
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, response);
|
||||
device_result
|
||||
}),
|
||||
#[cfg(feature = "socket-tcp")]
|
||||
Socket::Tcp(ref mut socket) =>
|
||||
socket.dispatch(timestamp, &caps, |response| {
|
||||
let response = Packet::Tcp(response);
|
||||
neighbor_addr = response.neighbor_addr();
|
||||
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, response);
|
||||
device_result
|
||||
}),
|
||||
Socket::__Nonexhaustive(_) => unreachable!()
|
||||
|
@ -258,12 +283,9 @@ impl<'b, 'c, DeviceT> Interface<'b, 'c, DeviceT>
|
|||
// `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);
|
||||
// neighboor.
|
||||
socket.meta_mut().neighbor_missing(timestamp,
|
||||
neighbor_addr.expect("non-IP response packet"));
|
||||
break
|
||||
}
|
||||
(Err(err), _) | (_, Err(err)) => {
|
||||
|
@ -271,13 +293,7 @@ impl<'b, 'c, DeviceT> Interface<'b, 'c, DeviceT>
|
|||
socket.meta().handle, err);
|
||||
return Err(err)
|
||||
}
|
||||
(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(()), Ok(())) => ()
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
@ -679,7 +695,7 @@ impl<'b, 'c> InterfaceInner<'b, 'c> {
|
|||
fn route(&self, addr: &IpAddress) -> Result<IpAddress> {
|
||||
self.ip_addrs
|
||||
.iter()
|
||||
.find(|cidr| cidr.contains_addr(&addr))
|
||||
.find(|cidr| cidr.contains_addr(addr))
|
||||
.map(|_cidr| Ok(addr.clone())) // route directly
|
||||
.unwrap_or_else(|| {
|
||||
match (addr, self.ipv4_gateway) {
|
||||
|
@ -692,6 +708,17 @@ impl<'b, 'c> InterfaceInner<'b, 'c> {
|
|||
})
|
||||
}
|
||||
|
||||
fn has_neighbor<'a>(&self, addr: &'a IpAddress, timestamp: u64) -> bool {
|
||||
match self.route(addr) {
|
||||
Ok(routed_addr) => {
|
||||
self.neighbor_cache
|
||||
.lookup_pure(&routed_addr, timestamp)
|
||||
.is_some()
|
||||
}
|
||||
Err(_) => false
|
||||
}
|
||||
}
|
||||
|
||||
fn lookup_hardware_addr<Tx>(&mut self, tx_token: Tx, timestamp: u64,
|
||||
src_addr: &IpAddress, dst_addr: &IpAddress) ->
|
||||
Result<(EthernetAddress, Tx)>
|
||||
|
@ -702,7 +729,7 @@ impl<'b, 'c> InterfaceInner<'b, 'c> {
|
|||
match self.neighbor_cache.lookup(&dst_addr, timestamp) {
|
||||
NeighborAnswer::Found(hardware_addr) =>
|
||||
return Ok((hardware_addr, tx_token)),
|
||||
NeighborAnswer::Hushed =>
|
||||
NeighborAnswer::RateLimited =>
|
||||
return Err(Error::Unaddressable),
|
||||
NeighborAnswer::NotFound => (),
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ pub(crate) enum Answer {
|
|||
NotFound,
|
||||
/// The neighbor address is not in the cache, or has expired,
|
||||
/// and a lookup has been made recently.
|
||||
Hushed
|
||||
RateLimited
|
||||
}
|
||||
|
||||
/// A neighbor cache backed by a map.
|
||||
|
@ -47,7 +47,7 @@ pub(crate) enum Answer {
|
|||
#[derive(Debug)]
|
||||
pub struct Cache<'a> {
|
||||
storage: ManagedMap<'a, IpAddress, Neighbor>,
|
||||
hushed_until: u64,
|
||||
silent_until: u64,
|
||||
}
|
||||
|
||||
impl<'a> Cache<'a> {
|
||||
|
@ -66,7 +66,7 @@ impl<'a> Cache<'a> {
|
|||
let mut storage = storage.into();
|
||||
storage.clear();
|
||||
|
||||
Cache { storage, hushed_until: 0 }
|
||||
Cache { storage, silent_until: 0 }
|
||||
}
|
||||
|
||||
pub(crate) fn fill(&mut self, protocol_addr: IpAddress, hardware_addr: EthernetAddress,
|
||||
|
@ -145,10 +145,10 @@ impl<'a> Cache<'a> {
|
|||
match self.lookup_pure(protocol_addr, timestamp) {
|
||||
Some(hardware_addr) =>
|
||||
Answer::Found(hardware_addr),
|
||||
None if timestamp < self.hushed_until =>
|
||||
Answer::Hushed,
|
||||
None if timestamp < self.silent_until =>
|
||||
Answer::RateLimited,
|
||||
None => {
|
||||
self.hushed_until = timestamp + Self::SILENT_TIME;
|
||||
self.silent_until = timestamp + Self::SILENT_TIME;
|
||||
Answer::NotFound
|
||||
}
|
||||
}
|
||||
|
@ -230,7 +230,7 @@ mod test {
|
|||
let mut cache = Cache::new(&mut cache_storage[..]);
|
||||
|
||||
assert_eq!(cache.lookup(&PADDR_A, 0), Answer::NotFound);
|
||||
assert_eq!(cache.lookup(&PADDR_A, 100), Answer::Hushed);
|
||||
assert_eq!(cache.lookup(&PADDR_A, 100), Answer::RateLimited);
|
||||
assert_eq!(cache.lookup(&PADDR_A, 2000), Answer::NotFound);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
use wire::IpAddress;
|
||||
use super::SocketHandle;
|
||||
|
||||
/// Neighbor dependency.
|
||||
///
|
||||
/// This enum tracks whether the socket should be polled based on the neighbor it is
|
||||
/// going to send packets to.
|
||||
#[derive(Debug)]
|
||||
enum NeighborState {
|
||||
/// Socket can be polled immediately.
|
||||
Active,
|
||||
/// Socket should not be polled until either `silent_until` passes or `neighbor` appears
|
||||
/// in the neighbor cache.
|
||||
Waiting {
|
||||
neighbor: IpAddress,
|
||||
silent_until: u64,
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for NeighborState {
|
||||
fn default() -> Self {
|
||||
NeighborState::Active
|
||||
}
|
||||
}
|
||||
|
||||
/// Network socket metadata.
|
||||
///
|
||||
/// This includes things that only external (to the socket, that is) code
|
||||
/// is interested in, but which are more conveniently stored inside the socket itself.
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Meta {
|
||||
/// Handle of this socket within its enclosing `SocketSet`.
|
||||
/// Mainly useful for debug output.
|
||||
pub(crate) handle: SocketHandle,
|
||||
/// See [NeighborState](struct.NeighborState.html).
|
||||
neighbor_state: NeighborState,
|
||||
}
|
||||
|
||||
impl Meta {
|
||||
/// Minimum delay between neighbor discovery requests for this particular socket,
|
||||
/// in milliseconds.
|
||||
///
|
||||
/// See also `iface::NeighborCache::SILENT_TIME`.
|
||||
pub(crate) const DISCOVERY_SILENT_TIME: u64 = 3_000;
|
||||
|
||||
pub(crate) fn poll_at<F>(&self, socket_poll_at: Option<u64>, has_neighbor: F) -> Option<u64>
|
||||
where F: Fn(IpAddress) -> bool
|
||||
{
|
||||
match self.neighbor_state {
|
||||
NeighborState::Active =>
|
||||
socket_poll_at,
|
||||
NeighborState::Waiting { neighbor, .. }
|
||||
if has_neighbor(neighbor) =>
|
||||
socket_poll_at,
|
||||
NeighborState::Waiting { silent_until, .. } =>
|
||||
Some(silent_until)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn egress_permitted<F>(&mut self, has_neighbor: F) -> bool
|
||||
where F: Fn(IpAddress) -> bool
|
||||
{
|
||||
match self.neighbor_state {
|
||||
NeighborState::Active =>
|
||||
true,
|
||||
NeighborState::Waiting { neighbor, .. } => {
|
||||
if has_neighbor(neighbor) {
|
||||
net_trace!("{}: neighbor {} discovered, unsilencing",
|
||||
self.handle, neighbor);
|
||||
self.neighbor_state = NeighborState::Active;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn neighbor_missing(&mut self, timestamp: u64, neighbor: IpAddress) {
|
||||
net_trace!("{}: neighbor {} missing, silencing until t+{}ms",
|
||||
self.handle, neighbor, Self::DISCOVERY_SILENT_TIME);
|
||||
self.neighbor_state = NeighborState::Waiting {
|
||||
neighbor, silent_until: timestamp + Self::DISCOVERY_SILENT_TIME
|
||||
};
|
||||
}
|
||||
}
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
use core::marker::PhantomData;
|
||||
|
||||
mod meta;
|
||||
#[cfg(feature = "socket-raw")]
|
||||
mod raw;
|
||||
#[cfg(feature = "socket-icmp")]
|
||||
|
@ -23,6 +24,8 @@ mod tcp;
|
|||
mod set;
|
||||
mod ref_;
|
||||
|
||||
pub(crate) use self::meta::Meta as SocketMeta;
|
||||
|
||||
#[cfg(feature = "socket-raw")]
|
||||
pub use self::raw::{PacketBuffer as RawPacketBuffer,
|
||||
SocketBuffer as RawSocketBuffer,
|
||||
|
@ -74,19 +77,6 @@ pub enum Socket<'a, 'b: 'a> {
|
|||
__Nonexhaustive(PhantomData<(&'a (), &'b ())>)
|
||||
}
|
||||
|
||||
/// Network socket metadata.
|
||||
///
|
||||
/// This includes things that only external (to the socket, that is) code
|
||||
/// is interested in, but which are more conveniently stored inside the socket itself.
|
||||
#[derive(Debug, Default)]
|
||||
pub(crate) struct SocketMeta {
|
||||
/// 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 {
|
||||
($self_:expr, |$socket:ident [$( $mut_:tt )*]| $code:expr) => ({
|
||||
match $self_ {
|
||||
|
@ -119,8 +109,7 @@ impl<'a, 'b> Socket<'a, 'b> {
|
|||
}
|
||||
|
||||
pub(crate) fn poll_at(&self) -> Option<u64> {
|
||||
let poll_at = dispatch_socket!(self, |socket []| socket.poll_at());
|
||||
self.meta().hushed_until.or(poll_at)
|
||||
dispatch_socket!(self, |socket []| socket.poll_at())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue