Ver Fonte

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.
whitequark há 7 anos atrás
pai
commit
42c93b99c0
4 ficheiros alterados com 153 adições e 51 exclusões
  1. 56 29
      src/iface/ethernet.rs
  2. 7 7
      src/iface/neighbor.rs
  3. 86 0
      src/socket/meta.rs
  4. 4 15
      src/socket/mod.rs

+ 56 - 29
src/iface/ethernet.rs

@@ -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 => (),
         }

+ 7 - 7
src/iface/neighbor.rs

@@ -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);
     }
 }

+ 86 - 0
src/socket/meta.rs

@@ -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
+        };
+    }
+}

+ 4 - 15
src/socket/mod.rs

@@ -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())
     }
 }