mirror of
https://github.com/smoltcp-rs/smoltcp.git
synced 2025-09-30 22:31:25 +00:00
Rewrite the ARP cache to allow for flood protection and expiration.
This commit is contained in:
parent
5e2ae22302
commit
34c3a8c905
@ -13,10 +13,15 @@ license = "0BSD"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
byteorder = { version = "1.0", default-features = false }
|
byteorder = { version = "1.0", default-features = false }
|
||||||
managed = { version = "0.4.0", default-features = false }
|
|
||||||
log = { version = "0.3", default-features = false, optional = true }
|
log = { version = "0.3", default-features = false, optional = true }
|
||||||
libc = { version = "0.2.18", optional = true }
|
libc = { version = "0.2.18", optional = true }
|
||||||
|
|
||||||
|
[dependencies.managed]
|
||||||
|
git = "https://github.com/m-labs/rust-managed.git"
|
||||||
|
rev = "629a6786a1cf1692015f464ed16c04eafa5cb8d1"
|
||||||
|
default-features = false
|
||||||
|
features = ["map"]
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
log = "0.3"
|
log = "0.3"
|
||||||
env_logger = "0.4"
|
env_logger = "0.4"
|
||||||
|
@ -22,7 +22,8 @@ The only supported medium is Ethernet.
|
|||||||
* Regular Ethernet II frames are supported.
|
* Regular Ethernet II frames are supported.
|
||||||
* Unicast and broadcast packets are supported, multicast packets are **not** supported.
|
* Unicast and broadcast packets are supported, multicast packets are **not** supported.
|
||||||
* ARP packets (including gratuitous requests and replies) are supported.
|
* ARP packets (including gratuitous requests and replies) are supported.
|
||||||
* ARP rate limiting and cache expiration is **not** supported.
|
* ARP requests are sent at a rate not exceeding one per second.
|
||||||
|
* Cached ARP entries expire after one minute.
|
||||||
* 802.3 frames and 802.1Q are **not** supported.
|
* 802.3 frames and 802.1Q are **not** supported.
|
||||||
* Jumbo frames are **not** supported.
|
* Jumbo frames are **not** supported.
|
||||||
|
|
||||||
|
@ -7,11 +7,12 @@ extern crate smoltcp;
|
|||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
use std::str::{self, FromStr};
|
use std::str::{self, FromStr};
|
||||||
|
use std::collections::BTreeMap;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use std::os::unix::io::AsRawFd;
|
use std::os::unix::io::AsRawFd;
|
||||||
use smoltcp::phy::wait as phy_wait;
|
use smoltcp::phy::wait as phy_wait;
|
||||||
use smoltcp::wire::{EthernetAddress, Ipv4Address, IpAddress, IpCidr};
|
use smoltcp::wire::{EthernetAddress, Ipv4Address, IpAddress, IpCidr};
|
||||||
use smoltcp::iface::{ArpCache, SliceArpCache, EthernetInterface};
|
use smoltcp::iface::{NeighborCache, EthernetInterface};
|
||||||
use smoltcp::socket::{SocketSet, TcpSocket, TcpSocketBuffer};
|
use smoltcp::socket::{SocketSet, TcpSocket, TcpSocketBuffer};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -32,7 +33,7 @@ fn main() {
|
|||||||
|
|
||||||
let startup_time = Instant::now();
|
let startup_time = Instant::now();
|
||||||
|
|
||||||
let arp_cache = SliceArpCache::new(vec![Default::default(); 8]);
|
let neighbor_cache = NeighborCache::new(BTreeMap::new());
|
||||||
|
|
||||||
let tcp_rx_buffer = TcpSocketBuffer::new(vec![0; 64]);
|
let tcp_rx_buffer = TcpSocketBuffer::new(vec![0; 64]);
|
||||||
let tcp_tx_buffer = TcpSocketBuffer::new(vec![0; 128]);
|
let tcp_tx_buffer = TcpSocketBuffer::new(vec![0; 128]);
|
||||||
@ -42,8 +43,7 @@ fn main() {
|
|||||||
let ip_addrs = [IpCidr::new(IpAddress::v4(192, 168, 69, 2), 24)];
|
let ip_addrs = [IpCidr::new(IpAddress::v4(192, 168, 69, 2), 24)];
|
||||||
let default_v4_gw = Ipv4Address::new(192, 168, 69, 100);
|
let default_v4_gw = Ipv4Address::new(192, 168, 69, 100);
|
||||||
let mut iface = EthernetInterface::new(
|
let mut iface = EthernetInterface::new(
|
||||||
device, Box::new(arp_cache) as Box<ArpCache>,
|
device, neighbor_cache, ethernet_addr, ip_addrs, Some(default_v4_gw));
|
||||||
ethernet_addr, ip_addrs, Some(default_v4_gw));
|
|
||||||
|
|
||||||
let mut sockets = SocketSet::new(vec![]);
|
let mut sockets = SocketSet::new(vec![]);
|
||||||
let tcp_handle = sockets.add(tcp_socket);
|
let tcp_handle = sockets.add(tcp_socket);
|
||||||
|
@ -18,7 +18,7 @@ mod utils;
|
|||||||
use core::str;
|
use core::str;
|
||||||
use smoltcp::phy::Loopback;
|
use smoltcp::phy::Loopback;
|
||||||
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr};
|
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr};
|
||||||
use smoltcp::iface::{ArpCache, SliceArpCache, EthernetInterface};
|
use smoltcp::iface::{NeighborCache, EthernetInterface};
|
||||||
use smoltcp::socket::{SocketSet, TcpSocket, TcpSocketBuffer};
|
use smoltcp::socket::{SocketSet, TcpSocket, TcpSocketBuffer};
|
||||||
|
|
||||||
#[cfg(not(feature = "std"))]
|
#[cfg(not(feature = "std"))]
|
||||||
@ -85,13 +85,12 @@ fn main() {
|
|||||||
device
|
device
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut arp_cache_entries: [_; 8] = Default::default();
|
let mut neighbor_cache_entries = [None; 8];
|
||||||
let mut arp_cache = SliceArpCache::new(&mut arp_cache_entries[..]);
|
let mut neighbor_cache = NeighborCache::new(&mut neighbor_cache_entries[..]);
|
||||||
|
|
||||||
let mut ip_addrs = [IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8)];
|
let mut ip_addrs = [IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8)];
|
||||||
let mut iface = EthernetInterface::new(
|
let mut iface = EthernetInterface::new(
|
||||||
device, &mut arp_cache as &mut ArpCache,
|
device, neighbor_cache, EthernetAddress::default(), &mut ip_addrs[..], None);
|
||||||
EthernetAddress::default(), &mut ip_addrs[..], None);
|
|
||||||
|
|
||||||
let server_socket = {
|
let server_socket = {
|
||||||
// It is not strictly necessary to use a `static mut` and unsafe code here, but
|
// It is not strictly necessary to use a `static mut` and unsafe code here, but
|
||||||
|
@ -8,13 +8,14 @@ extern crate byteorder;
|
|||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
use std::collections::BTreeMap;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use std::os::unix::io::AsRawFd;
|
use std::os::unix::io::AsRawFd;
|
||||||
use smoltcp::phy::Device;
|
use smoltcp::phy::Device;
|
||||||
use smoltcp::phy::wait as phy_wait;
|
use smoltcp::phy::wait as phy_wait;
|
||||||
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr,
|
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr,
|
||||||
Ipv4Address, Icmpv4Repr, Icmpv4Packet};
|
Ipv4Address, Icmpv4Repr, Icmpv4Packet};
|
||||||
use smoltcp::iface::{ArpCache, SliceArpCache, EthernetInterface};
|
use smoltcp::iface::{NeighborCache, EthernetInterface};
|
||||||
use smoltcp::socket::{SocketSet, IcmpSocket, IcmpSocketBuffer, IcmpPacketBuffer, IcmpEndpoint};
|
use smoltcp::socket::{SocketSet, IcmpSocket, IcmpSocketBuffer, IcmpPacketBuffer, IcmpEndpoint};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use byteorder::{ByteOrder, NetworkEndian};
|
use byteorder::{ByteOrder, NetworkEndian};
|
||||||
@ -45,7 +46,7 @@ fn main() {
|
|||||||
|
|
||||||
let startup_time = Instant::now();
|
let startup_time = Instant::now();
|
||||||
|
|
||||||
let arp_cache = SliceArpCache::new(vec![Default::default(); 8]);
|
let neighbor_cache = NeighborCache::new(BTreeMap::new());
|
||||||
|
|
||||||
let remote_addr = address;
|
let remote_addr = address;
|
||||||
let local_addr = Ipv4Address::new(192, 168, 69, 1);
|
let local_addr = Ipv4Address::new(192, 168, 69, 1);
|
||||||
@ -58,8 +59,7 @@ fn main() {
|
|||||||
let ip_addr = IpCidr::new(IpAddress::from(local_addr), 24);
|
let ip_addr = IpCidr::new(IpAddress::from(local_addr), 24);
|
||||||
let default_v4_gw = Ipv4Address::new(192, 168, 69, 100);
|
let default_v4_gw = Ipv4Address::new(192, 168, 69, 100);
|
||||||
let mut iface = EthernetInterface::new(
|
let mut iface = EthernetInterface::new(
|
||||||
device, Box::new(arp_cache) as Box<ArpCache>,
|
device, neighbor_cache, ethernet_addr, [ip_addr], Some(default_v4_gw));
|
||||||
ethernet_addr, [ip_addr], Some(default_v4_gw));
|
|
||||||
|
|
||||||
let mut sockets = SocketSet::new(vec![]);
|
let mut sockets = SocketSet::new(vec![]);
|
||||||
let icmp_handle = sockets.add(icmp_socket);
|
let icmp_handle = sockets.add(icmp_socket);
|
||||||
|
@ -7,12 +7,13 @@ extern crate smoltcp;
|
|||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
use std::str;
|
use std::str;
|
||||||
|
use std::collections::BTreeMap;
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use std::os::unix::io::AsRawFd;
|
use std::os::unix::io::AsRawFd;
|
||||||
use smoltcp::phy::wait as phy_wait;
|
use smoltcp::phy::wait as phy_wait;
|
||||||
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr};
|
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr};
|
||||||
use smoltcp::iface::{ArpCache, SliceArpCache, EthernetInterface};
|
use smoltcp::iface::{NeighborCache, EthernetInterface};
|
||||||
use smoltcp::socket::SocketSet;
|
use smoltcp::socket::SocketSet;
|
||||||
use smoltcp::socket::{UdpSocket, UdpSocketBuffer, UdpPacketBuffer};
|
use smoltcp::socket::{UdpSocket, UdpSocketBuffer, UdpPacketBuffer};
|
||||||
use smoltcp::socket::{TcpSocket, TcpSocketBuffer};
|
use smoltcp::socket::{TcpSocket, TcpSocketBuffer};
|
||||||
@ -31,7 +32,7 @@ fn main() {
|
|||||||
|
|
||||||
let startup_time = Instant::now();
|
let startup_time = Instant::now();
|
||||||
|
|
||||||
let arp_cache = SliceArpCache::new(vec![Default::default(); 8]);
|
let neighbor_cache = NeighborCache::new(BTreeMap::new());
|
||||||
|
|
||||||
let udp_rx_buffer = UdpSocketBuffer::new(vec![UdpPacketBuffer::new(vec![0; 64])]);
|
let udp_rx_buffer = UdpSocketBuffer::new(vec![UdpPacketBuffer::new(vec![0; 64])]);
|
||||||
let udp_tx_buffer = UdpSocketBuffer::new(vec![UdpPacketBuffer::new(vec![0; 128])]);
|
let udp_tx_buffer = UdpSocketBuffer::new(vec![UdpPacketBuffer::new(vec![0; 128])]);
|
||||||
@ -56,8 +57,7 @@ fn main() {
|
|||||||
let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]);
|
let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]);
|
||||||
let ip_addrs = [IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24)];
|
let ip_addrs = [IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24)];
|
||||||
let mut iface = EthernetInterface::new(
|
let mut iface = EthernetInterface::new(
|
||||||
device, Box::new(arp_cache) as Box<ArpCache>,
|
device, neighbor_cache, ethernet_addr, ip_addrs, None);
|
||||||
ethernet_addr, ip_addrs, None);
|
|
||||||
|
|
||||||
let mut sockets = SocketSet::new(vec![]);
|
let mut sockets = SocketSet::new(vec![]);
|
||||||
let udp_handle = sockets.add(udp_socket);
|
let udp_handle = sockets.add(udp_socket);
|
||||||
|
@ -1,171 +0,0 @@
|
|||||||
use managed::ManagedSlice;
|
|
||||||
|
|
||||||
use wire::{EthernetAddress, IpAddress};
|
|
||||||
|
|
||||||
/// An Address Resolution Protocol cache.
|
|
||||||
///
|
|
||||||
/// This interface 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: &IpAddress, hardware_addr: &EthernetAddress);
|
|
||||||
|
|
||||||
/// Look up the hardware address corresponding for the given protocol address.
|
|
||||||
fn lookup(&mut self, protocol_addr: &IpAddress) -> 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
|
|
||||||
///
|
|
||||||
/// On systems with heap, this cache can be created with:
|
|
||||||
/// ```rust
|
|
||||||
/// use smoltcp::iface::SliceArpCache;
|
|
||||||
/// let mut arp_cache = SliceArpCache::new(vec![Default::default(); 8]);
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// On systems without heap, use:
|
|
||||||
/// ```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: ManagedSlice<'a, (IpAddress, 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<T>(storage: T) -> SliceCache<'a>
|
|
||||||
where T: Into<ManagedSlice<'a, (IpAddress, EthernetAddress, usize)>> {
|
|
||||||
let mut storage = storage.into();
|
|
||||||
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: &IpAddress) -> Option<usize> {
|
|
||||||
// The order of comparison is important: any valid IpAddress should
|
|
||||||
// sort before IpAddress::Invalid.
|
|
||||||
self.storage.binary_search_by_key(protocol_addr, |&(key, _, _)| key).ok()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sort entries in an order suitable for `find`.
|
|
||||||
fn sort(&mut self) {
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
fn sort(data: &mut [(IpAddress, EthernetAddress, usize)]) {
|
|
||||||
data.sort_by_key(|&(key, _, _)| key)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "std"))]
|
|
||||||
fn sort(data: &mut [(IpAddress, EthernetAddress, usize)]) {
|
|
||||||
// Use an insertion sort, which performs best on 10 elements and less.
|
|
||||||
for i in 1..data.len() {
|
|
||||||
let mut j = i;
|
|
||||||
while j > 0 && data[j-1].0 > data[j].0 {
|
|
||||||
data.swap(j, j - 1);
|
|
||||||
j = j - 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sort(&mut self.storage)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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: &IpAddress, hardware_addr: &EthernetAddress) {
|
|
||||||
debug_assert!(protocol_addr.is_unicast());
|
|
||||||
debug_assert!(hardware_addr.is_unicast());
|
|
||||||
|
|
||||||
if let None = self.find(protocol_addr) {
|
|
||||||
let lru_index = self.lru();
|
|
||||||
|
|
||||||
if net_log_enabled!(trace) {
|
|
||||||
let (old_protocol_addr, old_hardware_addr, _counter) = self.storage[lru_index];
|
|
||||||
if !old_protocol_addr.is_unspecified() {
|
|
||||||
net_trace!("evicting {} => {}", old_protocol_addr, old_hardware_addr);
|
|
||||||
}
|
|
||||||
net_trace!("filling {} => {}", protocol_addr, hardware_addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.counter += 1;
|
|
||||||
self.storage[lru_index] =
|
|
||||||
(*protocol_addr, *hardware_addr, self.counter);
|
|
||||||
self.sort()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lookup(&mut self, protocol_addr: &IpAddress) -> 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 wire::Ipv4Address;
|
|
||||||
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: IpAddress = IpAddress::Ipv4(Ipv4Address([1, 0, 0, 1]));
|
|
||||||
const PADDR_B: IpAddress = IpAddress::Ipv4(Ipv4Address([1, 0, 0, 2]));
|
|
||||||
const PADDR_C: IpAddress = IpAddress::Ipv4(Ipv4Address([1, 0, 0, 3]));
|
|
||||||
const PADDR_D: IpAddress = IpAddress::Ipv4(Ipv4Address([1, 0, 0, 4]));
|
|
||||||
|
|
||||||
#[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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
|||||||
// of RFC 1122 that discuss Ethernet, ARP and IP.
|
// of RFC 1122 that discuss Ethernet, ARP and IP.
|
||||||
|
|
||||||
use core::cmp;
|
use core::cmp;
|
||||||
use managed::{Managed, ManagedSlice};
|
use managed::ManagedSlice;
|
||||||
|
|
||||||
use {Error, Result};
|
use {Error, Result};
|
||||||
use phy::{Device, DeviceCapabilities, RxToken, TxToken};
|
use phy::{Device, DeviceCapabilities, RxToken, TxToken};
|
||||||
@ -26,7 +26,7 @@ use socket::IcmpSocket;
|
|||||||
use socket::UdpSocket;
|
use socket::UdpSocket;
|
||||||
#[cfg(feature = "socket-tcp")]
|
#[cfg(feature = "socket-tcp")]
|
||||||
use socket::TcpSocket;
|
use socket::TcpSocket;
|
||||||
use super::ArpCache;
|
use super::{NeighborCache, NeighborAnswer};
|
||||||
|
|
||||||
/// An Ethernet network interface.
|
/// An Ethernet network interface.
|
||||||
///
|
///
|
||||||
@ -46,7 +46,7 @@ pub struct Interface<'b, 'c, DeviceT: for<'d> Device<'d>> {
|
|||||||
/// methods on the `Interface` in this time (since its `device` field is borrowed
|
/// methods on the `Interface` in this time (since its `device` field is borrowed
|
||||||
/// exclusively). However, it is still possible to call methods on its `inner` field.
|
/// exclusively). However, it is still possible to call methods on its `inner` field.
|
||||||
struct InterfaceInner<'b, 'c> {
|
struct InterfaceInner<'b, 'c> {
|
||||||
arp_cache: Managed<'b, ArpCache>,
|
neighbor_cache: NeighborCache<'b>,
|
||||||
ethernet_addr: EthernetAddress,
|
ethernet_addr: EthernetAddress,
|
||||||
ip_addrs: ManagedSlice<'c, IpCidr>,
|
ip_addrs: ManagedSlice<'c, IpCidr>,
|
||||||
ipv4_gateway: Option<Ipv4Address>,
|
ipv4_gateway: Option<Ipv4Address>,
|
||||||
@ -73,23 +73,21 @@ impl<'b, 'c, DeviceT> Interface<'b, 'c, DeviceT>
|
|||||||
/// # Panics
|
/// # Panics
|
||||||
/// See the restrictions on [set_hardware_addr](#method.set_hardware_addr)
|
/// See the restrictions on [set_hardware_addr](#method.set_hardware_addr)
|
||||||
/// and [set_protocol_addrs](#method.set_protocol_addrs) functions.
|
/// and [set_protocol_addrs](#method.set_protocol_addrs) functions.
|
||||||
pub fn new<ArpCacheMT, ProtocolAddrsMT, Ipv4GatewayAddrT>
|
pub fn new<ProtocolAddrsMT, Ipv4GatewayAddrT>
|
||||||
(device: DeviceT, arp_cache: ArpCacheMT,
|
(device: DeviceT,
|
||||||
|
neighbor_cache: NeighborCache<'b>,
|
||||||
ethernet_addr: EthernetAddress,
|
ethernet_addr: EthernetAddress,
|
||||||
ip_addrs: ProtocolAddrsMT,
|
ip_addrs: ProtocolAddrsMT,
|
||||||
ipv4_gateway: Ipv4GatewayAddrT) ->
|
ipv4_gateway: Ipv4GatewayAddrT) ->
|
||||||
Interface<'b, 'c, DeviceT>
|
Interface<'b, 'c, DeviceT>
|
||||||
where ArpCacheMT: Into<Managed<'b, ArpCache>>,
|
where ProtocolAddrsMT: Into<ManagedSlice<'c, IpCidr>>,
|
||||||
ProtocolAddrsMT: Into<ManagedSlice<'c, IpCidr>>,
|
|
||||||
Ipv4GatewayAddrT: Into<Option<Ipv4Address>>, {
|
Ipv4GatewayAddrT: Into<Option<Ipv4Address>>, {
|
||||||
let ip_addrs = ip_addrs.into();
|
let ip_addrs = ip_addrs.into();
|
||||||
InterfaceInner::check_ethernet_addr(ðernet_addr);
|
InterfaceInner::check_ethernet_addr(ðernet_addr);
|
||||||
InterfaceInner::check_ip_addrs(&ip_addrs);
|
InterfaceInner::check_ip_addrs(&ip_addrs);
|
||||||
|
|
||||||
let inner = InterfaceInner {
|
let inner = InterfaceInner {
|
||||||
ethernet_addr,
|
ethernet_addr, ip_addrs, neighbor_cache,
|
||||||
ip_addrs,
|
|
||||||
arp_cache: arp_cache.into(),
|
|
||||||
ipv4_gateway: ipv4_gateway.into(),
|
ipv4_gateway: ipv4_gateway.into(),
|
||||||
device_capabilities: device.capabilities(),
|
device_capabilities: device.capabilities(),
|
||||||
};
|
};
|
||||||
@ -301,7 +299,7 @@ impl<'b, 'c> InterfaceInner<'b, 'c> {
|
|||||||
|
|
||||||
match eth_frame.ethertype() {
|
match eth_frame.ethertype() {
|
||||||
EthernetProtocol::Arp =>
|
EthernetProtocol::Arp =>
|
||||||
self.process_arp(ð_frame),
|
self.process_arp(timestamp, ð_frame),
|
||||||
EthernetProtocol::Ipv4 =>
|
EthernetProtocol::Ipv4 =>
|
||||||
self.process_ipv4(sockets, timestamp, ð_frame),
|
self.process_ipv4(sockets, timestamp, ð_frame),
|
||||||
// Drop all other traffic.
|
// Drop all other traffic.
|
||||||
@ -310,7 +308,7 @@ impl<'b, 'c> InterfaceInner<'b, 'c> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn process_arp<'frame, T: AsRef<[u8]>>
|
fn process_arp<'frame, T: AsRef<[u8]>>
|
||||||
(&mut self, eth_frame: &EthernetFrame<&'frame T>) ->
|
(&mut self, timestamp: u64, eth_frame: &EthernetFrame<&'frame T>) ->
|
||||||
Result<Packet<'frame>>
|
Result<Packet<'frame>>
|
||||||
{
|
{
|
||||||
let arp_packet = ArpPacket::new_checked(eth_frame.payload())?;
|
let arp_packet = ArpPacket::new_checked(eth_frame.payload())?;
|
||||||
@ -324,8 +322,9 @@ impl<'b, 'c> InterfaceInner<'b, 'c> {
|
|||||||
operation, source_hardware_addr, source_protocol_addr, target_protocol_addr, ..
|
operation, source_hardware_addr, source_protocol_addr, target_protocol_addr, ..
|
||||||
} => {
|
} => {
|
||||||
if source_protocol_addr.is_unicast() && source_hardware_addr.is_unicast() {
|
if source_protocol_addr.is_unicast() && source_hardware_addr.is_unicast() {
|
||||||
self.arp_cache.fill(&source_protocol_addr.into(),
|
self.neighbor_cache.fill(source_protocol_addr.into(),
|
||||||
&source_hardware_addr);
|
source_hardware_addr,
|
||||||
|
timestamp);
|
||||||
} else {
|
} else {
|
||||||
// Discard packets with non-unicast source addresses.
|
// Discard packets with non-unicast source addresses.
|
||||||
net_debug!("non-unicast source address");
|
net_debug!("non-unicast source address");
|
||||||
@ -350,7 +349,7 @@ impl<'b, 'c> InterfaceInner<'b, 'c> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn process_ipv4<'frame, T: AsRef<[u8]>>
|
fn process_ipv4<'frame, T: AsRef<[u8]>>
|
||||||
(&mut self, sockets: &mut SocketSet, _timestamp: u64,
|
(&mut self, sockets: &mut SocketSet, timestamp: u64,
|
||||||
eth_frame: &EthernetFrame<&'frame T>) ->
|
eth_frame: &EthernetFrame<&'frame T>) ->
|
||||||
Result<Packet<'frame>>
|
Result<Packet<'frame>>
|
||||||
{
|
{
|
||||||
@ -366,8 +365,9 @@ impl<'b, 'c> InterfaceInner<'b, 'c> {
|
|||||||
|
|
||||||
if eth_frame.src_addr().is_unicast() {
|
if eth_frame.src_addr().is_unicast() {
|
||||||
// Fill the ARP cache from IP header of unicast frames.
|
// Fill the ARP cache from IP header of unicast frames.
|
||||||
self.arp_cache.fill(&IpAddress::Ipv4(ipv4_repr.src_addr),
|
self.neighbor_cache.fill(IpAddress::Ipv4(ipv4_repr.src_addr),
|
||||||
ð_frame.src_addr());
|
eth_frame.src_addr(),
|
||||||
|
timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
let ip_repr = IpRepr::Ipv4(ipv4_repr);
|
let ip_repr = IpRepr::Ipv4(ipv4_repr);
|
||||||
@ -406,7 +406,7 @@ impl<'b, 'c> InterfaceInner<'b, 'c> {
|
|||||||
|
|
||||||
#[cfg(feature = "socket-tcp")]
|
#[cfg(feature = "socket-tcp")]
|
||||||
IpProtocol::Tcp =>
|
IpProtocol::Tcp =>
|
||||||
self.process_tcp(sockets, _timestamp, ip_repr, ip_payload),
|
self.process_tcp(sockets, timestamp, ip_repr, ip_payload),
|
||||||
|
|
||||||
#[cfg(feature = "socket-raw")]
|
#[cfg(feature = "socket-raw")]
|
||||||
_ if handled_by_raw_socket =>
|
_ if handled_by_raw_socket =>
|
||||||
@ -678,12 +678,12 @@ impl<'b, 'c> InterfaceInner<'b, 'c> {
|
|||||||
{
|
{
|
||||||
let dst_addr = self.route(dst_addr)?;
|
let dst_addr = self.route(dst_addr)?;
|
||||||
|
|
||||||
if let Some(hardware_addr) = self.arp_cache.lookup(&dst_addr) {
|
match self.neighbor_cache.lookup(&dst_addr, timestamp) {
|
||||||
return Ok((hardware_addr,tx_token))
|
NeighborAnswer::Found(hardware_addr) =>
|
||||||
}
|
return Ok((hardware_addr, tx_token)),
|
||||||
|
NeighborAnswer::Hushed =>
|
||||||
if dst_addr.is_broadcast() {
|
return Err(Error::Unaddressable),
|
||||||
return Ok((EthernetAddress::BROADCAST, tx_token))
|
NeighborAnswer::NotFound => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
match (src_addr, dst_addr) {
|
match (src_addr, dst_addr) {
|
||||||
@ -740,10 +740,10 @@ impl<'b, 'c> InterfaceInner<'b, 'c> {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use std::boxed::Box;
|
use std::collections::BTreeMap;
|
||||||
use {Result, Error};
|
use {Result, Error};
|
||||||
|
|
||||||
use iface::{ArpCache, SliceArpCache, EthernetInterface};
|
use iface::{NeighborCache, EthernetInterface};
|
||||||
use phy::{self, Loopback, ChecksumCapabilities};
|
use phy::{self, Loopback, ChecksumCapabilities};
|
||||||
use socket::SocketSet;
|
use socket::SocketSet;
|
||||||
use wire::{ArpOperation, ArpPacket, ArpRepr};
|
use wire::{ArpOperation, ArpPacket, ArpRepr};
|
||||||
@ -755,17 +755,17 @@ mod test {
|
|||||||
|
|
||||||
use super::Packet;
|
use super::Packet;
|
||||||
|
|
||||||
fn create_loopback<'a, 'b>() ->
|
fn create_loopback<'a, 'b>() -> (EthernetInterface<'static, 'b, Loopback>,
|
||||||
(EthernetInterface<'static, 'b, Loopback>, SocketSet<'static, 'a, 'b>) {
|
SocketSet<'static, 'a, 'b>) {
|
||||||
// Create a basic device
|
// Create a basic device
|
||||||
let device = Loopback::new();
|
let device = Loopback::new();
|
||||||
|
|
||||||
let arp_cache = SliceArpCache::new(vec![Default::default(); 8]);
|
let neighbor_cache = NeighborCache::new(BTreeMap::new());
|
||||||
|
|
||||||
let ip_addr = IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8);
|
let ip_addr = IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8);
|
||||||
(EthernetInterface::new(
|
(EthernetInterface::new(device, neighbor_cache,
|
||||||
device, Box::new(arp_cache) as Box<ArpCache>,
|
EthernetAddress::default(), [ip_addr], None),
|
||||||
EthernetAddress::default(), [ip_addr], None), SocketSet::new(vec![]))
|
SocketSet::new(vec![]))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
|
@ -3,9 +3,10 @@
|
|||||||
//! The `iface` module deals with the *network interfaces*. It filters incoming frames,
|
//! The `iface` module deals with the *network interfaces*. It filters incoming frames,
|
||||||
//! provides lookup and caching of hardware addresses, and handles management packets.
|
//! provides lookup and caching of hardware addresses, and handles management packets.
|
||||||
|
|
||||||
mod arp_cache;
|
mod neighbor;
|
||||||
mod ethernet;
|
mod ethernet;
|
||||||
|
|
||||||
pub use self::arp_cache::Cache as ArpCache;
|
pub use self::neighbor::Neighbor as Neighbor;
|
||||||
pub use self::arp_cache::SliceCache as SliceArpCache;
|
pub(crate) use self::neighbor::Answer as NeighborAnswer;
|
||||||
|
pub use self::neighbor::Cache as NeighborCache;
|
||||||
pub use self::ethernet::Interface as EthernetInterface;
|
pub use self::ethernet::Interface as EthernetInterface;
|
||||||
|
185
src/iface/neighbor.rs
Normal file
185
src/iface/neighbor.rs
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
// Heads up! Before working on this file you should read, at least,
|
||||||
|
// the parts of RFC 1122 that discuss ARP.
|
||||||
|
|
||||||
|
use managed::ManagedMap;
|
||||||
|
|
||||||
|
use wire::{EthernetAddress, IpAddress};
|
||||||
|
|
||||||
|
/// A cached neighbor.
|
||||||
|
///
|
||||||
|
/// A neighbor mapping translates from a protocol address to a hardware address,
|
||||||
|
/// and contains the timestamp past which the mapping should be discarded.
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct Neighbor {
|
||||||
|
hardware_addr: EthernetAddress,
|
||||||
|
expires_at: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An answer to a neighbor cache lookup.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub(crate) enum Answer {
|
||||||
|
/// The neighbor address is in the cache and not expired.
|
||||||
|
Found(EthernetAddress),
|
||||||
|
/// The neighbor address is not in the cache, or has expired.
|
||||||
|
NotFound,
|
||||||
|
/// The neighbor address is not in the cache, or has expired,
|
||||||
|
/// and a lookup has been made recently.
|
||||||
|
Hushed
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A neighbor cache backed by a map.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// On systems with heap, this cache can be created with:
|
||||||
|
/// ```rust
|
||||||
|
/// use std::collections::BTreeMap;
|
||||||
|
/// use smoltcp::iface::NeighborCache;
|
||||||
|
/// let mut neighbor_cache = NeighborCache::new(BTreeMap::new());
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// On systems without heap, use:
|
||||||
|
/// ```rust
|
||||||
|
/// use smoltcp::iface::NeighborCache;
|
||||||
|
/// let mut neighbor_cache_storage = [None; 8];
|
||||||
|
/// let mut neighbor_cache = NeighborCache::new(&mut neighbor_cache_storage[..]);
|
||||||
|
/// ```
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Cache<'a> {
|
||||||
|
storage: ManagedMap<'a, IpAddress, Neighbor>,
|
||||||
|
hushed_until: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Cache<'a> {
|
||||||
|
/// Flood protection delay, in milliseconds.
|
||||||
|
const FLOOD_TIMER: u64 = 1_000;
|
||||||
|
|
||||||
|
/// Neighbor entry lifetime, in milliseconds.
|
||||||
|
const ENTRY_LIFETIME: u64 = 60_000;
|
||||||
|
|
||||||
|
/// Create a cache. The backing storage is cleared upon creation.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
/// This function panics if `storage.len() == 0`.
|
||||||
|
pub fn new<T>(storage: T) -> Cache<'a>
|
||||||
|
where T: Into<ManagedMap<'a, IpAddress, Neighbor>> {
|
||||||
|
let mut storage = storage.into();
|
||||||
|
storage.clear();
|
||||||
|
|
||||||
|
Cache { storage, hushed_until: 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn fill(&mut self, protocol_addr: IpAddress, hardware_addr: EthernetAddress,
|
||||||
|
timestamp: u64) {
|
||||||
|
debug_assert!(protocol_addr.is_unicast());
|
||||||
|
debug_assert!(hardware_addr.is_unicast());
|
||||||
|
|
||||||
|
let neighbor = Neighbor {
|
||||||
|
expires_at: timestamp + Self::ENTRY_LIFETIME, hardware_addr
|
||||||
|
};
|
||||||
|
match self.storage.insert(protocol_addr, neighbor) {
|
||||||
|
Ok(Some(old_neighbor)) => {
|
||||||
|
if old_neighbor.hardware_addr != hardware_addr {
|
||||||
|
net_trace!("replaced {} => {} (was {})",
|
||||||
|
protocol_addr, hardware_addr, old_neighbor.hardware_addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(None) => {
|
||||||
|
net_trace!("filled {} => {}", protocol_addr, hardware_addr);
|
||||||
|
}
|
||||||
|
Err(_) => unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn lookup_pure(&self, protocol_addr: &IpAddress, timestamp: u64) ->
|
||||||
|
Option<EthernetAddress> {
|
||||||
|
if protocol_addr.is_broadcast() {
|
||||||
|
return Some(EthernetAddress::BROADCAST)
|
||||||
|
}
|
||||||
|
|
||||||
|
match self.storage.get(protocol_addr) {
|
||||||
|
Some(&Neighbor { expires_at, hardware_addr }) => {
|
||||||
|
if timestamp < expires_at {
|
||||||
|
return Some(hardware_addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => ()
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn lookup(&mut self, protocol_addr: &IpAddress, timestamp: u64) -> Answer {
|
||||||
|
match self.lookup_pure(protocol_addr, timestamp) {
|
||||||
|
Some(hardware_addr) =>
|
||||||
|
Answer::Found(hardware_addr),
|
||||||
|
None if timestamp < self.hushed_until =>
|
||||||
|
Answer::Hushed,
|
||||||
|
None => {
|
||||||
|
self.hushed_until = timestamp + Self::FLOOD_TIMER;
|
||||||
|
Answer::NotFound
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use wire::Ipv4Address;
|
||||||
|
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 PADDR_A: IpAddress = IpAddress::Ipv4(Ipv4Address([1, 0, 0, 1]));
|
||||||
|
const PADDR_B: IpAddress = IpAddress::Ipv4(Ipv4Address([1, 0, 0, 2]));
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_fill() {
|
||||||
|
let mut cache_storage = [Default::default(); 3];
|
||||||
|
let mut cache = Cache::new(&mut cache_storage[..]);
|
||||||
|
|
||||||
|
assert_eq!(cache.lookup_pure(&PADDR_A, 0), None);
|
||||||
|
assert_eq!(cache.lookup_pure(&PADDR_B, 0), None);
|
||||||
|
|
||||||
|
cache.fill(PADDR_A, HADDR_A, 0);
|
||||||
|
assert_eq!(cache.lookup_pure(&PADDR_A, 0), Some(HADDR_A));
|
||||||
|
assert_eq!(cache.lookup_pure(&PADDR_B, 0), None);
|
||||||
|
assert_eq!(cache.lookup_pure(&PADDR_A, 2 * Cache::ENTRY_LIFETIME), None);
|
||||||
|
|
||||||
|
cache.fill(PADDR_A, HADDR_A, 0);
|
||||||
|
assert_eq!(cache.lookup_pure(&PADDR_B, 0), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_expire() {
|
||||||
|
let mut cache_storage = [Default::default(); 3];
|
||||||
|
let mut cache = Cache::new(&mut cache_storage[..]);
|
||||||
|
|
||||||
|
cache.fill(PADDR_A, HADDR_A, 0);
|
||||||
|
assert_eq!(cache.lookup_pure(&PADDR_A, 0), Some(HADDR_A));
|
||||||
|
assert_eq!(cache.lookup_pure(&PADDR_A, 2 * Cache::ENTRY_LIFETIME), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_replace() {
|
||||||
|
let mut cache_storage = [Default::default(); 3];
|
||||||
|
let mut cache = Cache::new(&mut cache_storage[..]);
|
||||||
|
|
||||||
|
cache.fill(PADDR_A, HADDR_A, 0);
|
||||||
|
assert_eq!(cache.lookup_pure(&PADDR_A, 0), Some(HADDR_A));
|
||||||
|
cache.fill(PADDR_A, HADDR_B, 0);
|
||||||
|
assert_eq!(cache.lookup_pure(&PADDR_A, 0), Some(HADDR_B));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_hush() {
|
||||||
|
let mut cache_storage = [Default::default(); 3];
|
||||||
|
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, 2000), Answer::NotFound);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user