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]
|
#![no_std]
|
||||||
|
|
||||||
extern crate byteorder;
|
extern crate byteorder;
|
||||||
|
@ -11,3 +11,4 @@ extern crate libc;
|
||||||
|
|
||||||
pub mod phy;
|
pub mod phy;
|
||||||
pub mod wire;
|
pub mod wire;
|
||||||
|
pub mod iface;
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
//! Access to networking hardware.
|
//! Access to networking hardware.
|
||||||
//!
|
//!
|
||||||
//! The `phy` module provides an interface for sending and receiving frames
|
//! The `phy` module deals with the *network devices*. It provides an interface
|
||||||
//! through a physical (or perhaps virtualized) network device, [Device](trait.Device.html),
|
//! for transmitting and receiving frames, [Device](trait.Device.html),
|
||||||
//! as well as an implementations of that trait that uses the host OS,
|
//! 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")]
|
#[cfg(feature = "std")]
|
||||||
mod sys;
|
mod sys;
|
||||||
|
|
|
@ -22,10 +22,12 @@ impl fmt::Display for EtherType {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A six-octet Ethernet II address.
|
/// 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]);
|
pub struct Address(pub [u8; 6]);
|
||||||
|
|
||||||
impl Address {
|
impl Address {
|
||||||
|
pub const BROADCAST: Address = Address([0xff; 6]);
|
||||||
|
|
||||||
/// Construct an Ethernet address from a sequence of octets, in big-endian.
|
/// Construct an Ethernet address from a sequence of octets, in big-endian.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
|
@ -40,6 +42,16 @@ impl Address {
|
||||||
pub fn as_bytes(&self) -> &[u8] {
|
pub fn as_bytes(&self) -> &[u8] {
|
||||||
&self.0
|
&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 {
|
impl fmt::Display for Address {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
|
|
||||||
/// A four-octet IPv4 address.
|
/// 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]);
|
pub struct Address(pub [u8; 4]);
|
||||||
|
|
||||||
impl Address {
|
impl Address {
|
||||||
|
|
Loading…
Reference in New Issue