Explorar o código

Implement an ARP LRU cache.

whitequark %!s(int64=8) %!d(string=hai) anos
pai
achega
dea581fa10
Modificáronse 7 ficheiros con 217 adicións e 6 borrados
  1. 128 0
      src/iface/arp_cache.rs
  2. 39 0
      src/iface/ethernet.rs
  3. 31 0
      src/iface/mod.rs
  4. 2 1
      src/lib.rs
  5. 3 3
      src/phy/mod.rs
  6. 13 1
      src/wire/ethernet.rs
  7. 1 1
      src/wire/ipv4.rs

+ 128 - 0
src/iface/arp_cache.rs

@@ -0,0 +1,128 @@
+use wire::EthernetAddress;
+use super::ProtocolAddress;
+
+/// An Address Resolution Protocol cache.
+///
+/// This cache maps protocol addresses to hardware addresses.
+pub trait Cache {
+    /// Update the cache to map given protocol address to given hardware address.
+    fn fill(&mut self, protocol_addr: ProtocolAddress, hardware_addr: EthernetAddress);
+
+    /// Look up the hardware address corresponding for the given protocol address.
+    fn lookup(&mut self, protocol_addr: ProtocolAddress) -> Option<EthernetAddress>;
+}
+
+/// An Address Resolution Protocol cache backed by a slice.
+///
+/// This cache uses a fixed-size storage, binary search, and a least recently used
+/// eviction strategy.
+///
+/// # Examples
+/// This cache can be created as:
+///
+/// ```rust
+/// use smoltcp::iface::SliceArpCache;
+/// let mut arp_cache_storage = [Default::default(); 8];
+/// let mut arp_cache = SliceArpCache::new(&mut arp_cache_storage);
+/// ```
+pub struct SliceCache<'a> {
+    storage: &'a mut [(ProtocolAddress, EthernetAddress, usize)],
+    counter: usize
+}
+
+impl<'a> SliceCache<'a> {
+    /// Create a cache. The backing storage is cleared upon creation.
+    ///
+    /// # Panics
+    /// This function panics if `storage.len() == 0`.
+    pub fn new(storage: &'a mut [(ProtocolAddress, EthernetAddress, usize)]) -> SliceCache<'a> {
+        if storage.len() == 0 {
+            panic!("ARP slice cache created with empty storage")
+        }
+
+        for elem in storage.iter_mut() {
+            *elem = Default::default()
+        }
+        SliceCache {
+            storage: storage,
+            counter: 0
+        }
+    }
+
+    /// Find an entry for the given protocol address, if any.
+    fn find(&self, protocol_addr: ProtocolAddress) -> Option<usize> {
+        // The order of comparison is important: any valid ProtocolAddress should
+        // sort before ProtocolAddress::Invalid.
+        self.storage.binary_search_by_key(&protocol_addr, |&(key, _, _)| key).ok()
+    }
+
+    /// Sort entries in an order suitable for `find`.
+    fn sort(&mut self) {
+        self.storage.sort_by_key(|&(key, _, _)| key)
+    }
+
+    /// Find the least recently used entry.
+    fn lru(&self) -> usize {
+        self.storage.iter().enumerate().min_by_key(|&(_, &(_, _, counter))| counter).unwrap().0
+    }
+}
+
+impl<'a> Cache for SliceCache<'a> {
+    fn fill(&mut self, protocol_addr: ProtocolAddress, hardware_addr: EthernetAddress) {
+        if let None = self.find(protocol_addr) {
+            self.storage[self.lru()] = (protocol_addr, hardware_addr, self.counter);
+            self.sort()
+        }
+    }
+
+    fn lookup(&mut self, protocol_addr: ProtocolAddress) -> Option<EthernetAddress> {
+        if let Some(index) = self.find(protocol_addr) {
+            let (_protocol_addr, hardware_addr, ref mut counter) = self.storage[index];
+            self.counter += 1;
+            *counter = self.counter;
+            Some(hardware_addr)
+        } else {
+            None
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+
+    const HADDR_A: EthernetAddress = EthernetAddress([0, 0, 0, 0, 0, 1]);
+    const HADDR_B: EthernetAddress = EthernetAddress([0, 0, 0, 0, 0, 2]);
+    const HADDR_C: EthernetAddress = EthernetAddress([0, 0, 0, 0, 0, 3]);
+    const HADDR_D: EthernetAddress = EthernetAddress([0, 0, 0, 0, 0, 4]);
+
+    const PADDR_A: ProtocolAddress = ProtocolAddress::ipv4([0, 0, 0, 0]);
+    const PADDR_B: ProtocolAddress = ProtocolAddress::ipv4([0, 0, 0, 1]);
+    const PADDR_C: ProtocolAddress = ProtocolAddress::ipv4([0, 0, 0, 2]);
+    const PADDR_D: ProtocolAddress = ProtocolAddress::ipv4([0, 0, 0, 3]);
+
+    #[test]
+    fn test_slice_cache() {
+        let mut cache_storage = [Default::default(); 3];
+        let mut cache = SliceCache::new(&mut cache_storage);
+
+        cache.fill(PADDR_A, HADDR_A);
+        assert_eq!(cache.lookup(PADDR_A), Some(HADDR_A));
+        assert_eq!(cache.lookup(PADDR_B), None);
+
+        cache.fill(PADDR_B, HADDR_B);
+        cache.fill(PADDR_C, HADDR_C);
+        assert_eq!(cache.lookup(PADDR_A), Some(HADDR_A));
+        assert_eq!(cache.lookup(PADDR_B), Some(HADDR_B));
+        assert_eq!(cache.lookup(PADDR_C), Some(HADDR_C));
+
+        cache.lookup(PADDR_B);
+        cache.lookup(PADDR_A);
+        cache.lookup(PADDR_C);
+        cache.fill(PADDR_D, HADDR_D);
+        assert_eq!(cache.lookup(PADDR_A), Some(HADDR_A));
+        assert_eq!(cache.lookup(PADDR_B), None);
+        assert_eq!(cache.lookup(PADDR_C), Some(HADDR_C));
+        assert_eq!(cache.lookup(PADDR_D), Some(HADDR_D));
+    }
+}

+ 39 - 0
src/iface/ethernet.rs

@@ -0,0 +1,39 @@
+use phy::Device;
+use wire::EthernetAddress;
+use super::{ProtocolAddress, ArpCache};
+
+/// An Ethernet network interface.
+#[derive(Debug)]
+pub struct Interface<DeviceT: Device, ArpCacheT: ArpCache> {
+    device:        DeviceT,
+    arp_cache:     ArpCacheT,
+    hardware_addr: EthernetAddress,
+}
+
+impl<DeviceT: Device, ArpCacheT: ArpCache> Interface<DeviceT, ArpCacheT> {
+    /// Create a network interface using the provided network device.
+    ///
+    /// The newly created interface uses hardware address `00-00-00-00-00-00` and
+    /// has no assigned protocol addresses.
+    pub fn new(device: DeviceT, arp_cache: ArpCacheT) -> Interface<DeviceT, ArpCacheT> {
+        Interface {
+            device:        device,
+            arp_cache:     arp_cache,
+            hardware_addr: EthernetAddress([0x00; 6])
+        }
+    }
+
+    /// Get the hardware address of the interface.
+    pub fn hardware_addr(&self) -> EthernetAddress {
+        self.hardware_addr
+    }
+
+    /// Set the hardware address of the interface.
+    ///
+    /// # Panics
+    /// This function panics if `addr` is not unicast.
+    pub fn set_hardware_addr(&mut self, addr: EthernetAddress) {
+        if addr.is_multicast() { panic!("hardware address should be unicast") }
+        self.hardware_addr = addr
+    }
+}

+ 31 - 0
src/iface/mod.rs

@@ -0,0 +1,31 @@
+//! Network interface logic.
+//!
+//! The `iface` module deals with the *network interfaces*. It filters incoming frames,
+//! provides lookup and caching of hardware addresses, and handles management packets.
+use wire;
+
+mod arp_cache;
+mod ethernet;
+
+pub use self::arp_cache::Cache as ArpCache;
+pub use self::arp_cache::SliceCache as SliceArpCache;
+pub use self::ethernet::Interface as EthernetInterface;
+
+/// An internetworking protocol address.
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
+pub enum ProtocolAddress {
+    Invalid,
+    Ipv4(wire::Ipv4Address)
+}
+
+impl ProtocolAddress {
+    pub const fn ipv4(bytes: [u8; 4]) -> ProtocolAddress {
+        ProtocolAddress::Ipv4(wire::Ipv4Address(bytes))
+    }
+}
+
+impl Default for ProtocolAddress {
+    fn default() -> ProtocolAddress {
+        ProtocolAddress::Invalid
+    }
+}

+ 2 - 1
src/lib.rs

@@ -1,4 +1,4 @@
-#![feature(range_contains, associated_consts)]
+#![feature(range_contains, associated_consts, const_fn)]
 #![no_std]
 
 extern crate byteorder;
@@ -11,3 +11,4 @@ extern crate libc;
 
 pub mod phy;
 pub mod wire;
+pub mod iface;

+ 3 - 3
src/phy/mod.rs

@@ -1,9 +1,9 @@
 //! Access to networking hardware.
 //!
-//! The `phy` module provides an interface for sending and receiving frames
-//! through a physical (or perhaps virtualized) network device, [Device](trait.Device.html),
+//! The `phy` module deals with the *network devices*. It provides an interface
+//! for transmitting and receiving frames, [Device](trait.Device.html),
 //! as well as an implementations of that trait that uses the host OS,
-//! [OsDevice](struct.OsDevice.html).
+//! [RawSocket](struct.RawSocket.html) and [TapInterface](struct.TapInterface.html).
 
 #[cfg(feature = "std")]
 mod sys;

+ 13 - 1
src/wire/ethernet.rs

@@ -22,10 +22,12 @@ impl fmt::Display for EtherType {
 }
 
 /// A six-octet Ethernet II address.
-#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)]
 pub struct Address(pub [u8; 6]);
 
 impl Address {
+    pub const BROADCAST: Address = Address([0xff; 6]);
+
     /// Construct an Ethernet address from a sequence of octets, in big-endian.
     ///
     /// # Panics
@@ -40,6 +42,16 @@ impl Address {
     pub fn as_bytes(&self) -> &[u8] {
         &self.0
     }
+
+    /// Query whether the "multicast" bit in the OUI is set.
+    pub fn is_multicast(&self) -> bool {
+        self.0[0] & 0x01 != 0
+    }
+
+    /// Query whether the "locally administered" bit in the OUI is set.
+    pub fn is_local(&self) -> bool {
+        self.0[0] & 0x02 != 0
+    }
 }
 
 impl fmt::Display for Address {

+ 1 - 1
src/wire/ipv4.rs

@@ -1,7 +1,7 @@
 use core::fmt;
 
 /// A four-octet IPv4 address.
-#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)]
 pub struct Address(pub [u8; 4]);
 
 impl Address {