Implement an ARP LRU cache.
This commit is contained in:
parent
2e80d69384
commit
d862512582
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,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 {
|
||||
|
|
Loading…
Reference in New Issue