neighbor.rs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. // Heads up! Before working on this file you should read, at least,
  2. // the parts of RFC 1122 that discuss ARP.
  3. use managed::ManagedMap;
  4. use crate::time::{Duration, Instant};
  5. use crate::wire::{EthernetAddress, IpAddress};
  6. /// A cached neighbor.
  7. ///
  8. /// A neighbor mapping translates from a protocol address to a hardware address,
  9. /// and contains the timestamp past which the mapping should be discarded.
  10. #[derive(Debug, Clone, Copy)]
  11. #[cfg_attr(feature = "defmt", derive(defmt::Format))]
  12. pub struct Neighbor {
  13. hardware_addr: EthernetAddress,
  14. expires_at: Instant,
  15. }
  16. /// An answer to a neighbor cache lookup.
  17. #[derive(Debug, Clone, Copy, PartialEq, Eq)]
  18. #[cfg_attr(feature = "defmt", derive(defmt::Format))]
  19. pub(crate) enum Answer {
  20. /// The neighbor address is in the cache and not expired.
  21. Found(EthernetAddress),
  22. /// The neighbor address is not in the cache, or has expired.
  23. NotFound,
  24. /// The neighbor address is not in the cache, or has expired,
  25. /// and a lookup has been made recently.
  26. RateLimited,
  27. }
  28. impl Answer {
  29. /// Returns whether a valid address was found.
  30. pub(crate) fn found(&self) -> bool {
  31. match self {
  32. Answer::Found(_) => true,
  33. _ => false,
  34. }
  35. }
  36. }
  37. /// A neighbor cache backed by a map.
  38. ///
  39. /// # Examples
  40. ///
  41. /// On systems with heap, this cache can be created with:
  42. ///
  43. /// ```rust
  44. /// use std::collections::BTreeMap;
  45. /// use smoltcp::iface::NeighborCache;
  46. /// let mut neighbor_cache = NeighborCache::new(BTreeMap::new());
  47. /// ```
  48. ///
  49. /// On systems without heap, use:
  50. ///
  51. /// ```rust
  52. /// use smoltcp::iface::NeighborCache;
  53. /// let mut neighbor_cache_storage = [None; 8];
  54. /// let mut neighbor_cache = NeighborCache::new(&mut neighbor_cache_storage[..]);
  55. /// ```
  56. #[derive(Debug)]
  57. pub struct Cache<'a> {
  58. storage: ManagedMap<'a, IpAddress, Neighbor>,
  59. silent_until: Instant,
  60. gc_threshold: usize,
  61. }
  62. impl<'a> Cache<'a> {
  63. /// Minimum delay between discovery requests, in milliseconds.
  64. pub(crate) const SILENT_TIME: Duration = Duration { millis: 1_000 };
  65. /// Neighbor entry lifetime, in milliseconds.
  66. pub(crate) const ENTRY_LIFETIME: Duration = Duration { millis: 60_000 };
  67. /// Default number of entries in the cache before GC kicks in
  68. pub(crate) const GC_THRESHOLD: usize = 1024;
  69. /// Create a cache. The backing storage is cleared upon creation.
  70. ///
  71. /// # Panics
  72. /// This function panics if `storage.len() == 0`.
  73. pub fn new<T>(storage: T) -> Cache<'a>
  74. where
  75. T: Into<ManagedMap<'a, IpAddress, Neighbor>>,
  76. {
  77. Cache::new_with_limit(storage, Cache::GC_THRESHOLD)
  78. }
  79. pub fn new_with_limit<T>(storage: T, gc_threshold: usize) -> Cache<'a>
  80. where
  81. T: Into<ManagedMap<'a, IpAddress, Neighbor>>,
  82. {
  83. let mut storage = storage.into();
  84. storage.clear();
  85. Cache {
  86. storage,
  87. gc_threshold,
  88. silent_until: Instant::from_millis(0),
  89. }
  90. }
  91. pub fn fill(
  92. &mut self,
  93. protocol_addr: IpAddress,
  94. hardware_addr: EthernetAddress,
  95. timestamp: Instant,
  96. ) {
  97. debug_assert!(protocol_addr.is_unicast());
  98. debug_assert!(hardware_addr.is_unicast());
  99. #[cfg(any(feature = "std", feature = "alloc"))]
  100. let current_storage_size = self.storage.len();
  101. match self.storage {
  102. ManagedMap::Borrowed(_) => (),
  103. #[cfg(any(feature = "std", feature = "alloc"))]
  104. ManagedMap::Owned(ref mut map) => {
  105. if current_storage_size >= self.gc_threshold {
  106. let new_btree_map = map
  107. .iter_mut()
  108. .map(|(key, value)| (*key, *value))
  109. .filter(|(_, v)| timestamp < v.expires_at)
  110. .collect();
  111. *map = new_btree_map;
  112. }
  113. }
  114. };
  115. let neighbor = Neighbor {
  116. expires_at: timestamp + Self::ENTRY_LIFETIME,
  117. hardware_addr,
  118. };
  119. match self.storage.insert(protocol_addr, neighbor) {
  120. Ok(Some(old_neighbor)) => {
  121. if old_neighbor.hardware_addr != hardware_addr {
  122. net_trace!(
  123. "replaced {} => {} (was {})",
  124. protocol_addr,
  125. hardware_addr,
  126. old_neighbor.hardware_addr
  127. );
  128. }
  129. }
  130. Ok(None) => {
  131. net_trace!("filled {} => {} (was empty)", protocol_addr, hardware_addr);
  132. }
  133. Err((protocol_addr, neighbor)) => {
  134. // If we're going down this branch, it means that a fixed-size cache storage
  135. // is full, and we need to evict an entry.
  136. let old_protocol_addr = match self.storage {
  137. ManagedMap::Borrowed(ref mut pairs) => {
  138. pairs
  139. .iter()
  140. .min_by_key(|pair_opt| {
  141. let (_protocol_addr, neighbor) = pair_opt.unwrap();
  142. neighbor.expires_at
  143. })
  144. .expect("empty neighbor cache storage") // unwraps min_by_key
  145. .unwrap() // unwraps pair
  146. .0
  147. }
  148. // Owned maps can extend themselves.
  149. #[cfg(any(feature = "std", feature = "alloc"))]
  150. ManagedMap::Owned(_) => unreachable!(),
  151. };
  152. let _old_neighbor = self.storage.remove(&old_protocol_addr).unwrap();
  153. match self.storage.insert(protocol_addr, neighbor) {
  154. Ok(None) => {
  155. net_trace!(
  156. "filled {} => {} (evicted {} => {})",
  157. protocol_addr,
  158. hardware_addr,
  159. old_protocol_addr,
  160. _old_neighbor.hardware_addr
  161. );
  162. }
  163. // We've covered everything else above.
  164. _ => unreachable!(),
  165. }
  166. }
  167. }
  168. }
  169. pub(crate) fn lookup(&self, protocol_addr: &IpAddress, timestamp: Instant) -> Answer {
  170. if protocol_addr.is_broadcast() {
  171. return Answer::Found(EthernetAddress::BROADCAST);
  172. }
  173. if let Some(&Neighbor {
  174. expires_at,
  175. hardware_addr,
  176. }) = self.storage.get(protocol_addr)
  177. {
  178. if timestamp < expires_at {
  179. return Answer::Found(hardware_addr);
  180. }
  181. }
  182. if timestamp < self.silent_until {
  183. Answer::RateLimited
  184. } else {
  185. Answer::NotFound
  186. }
  187. }
  188. pub(crate) fn limit_rate(&mut self, timestamp: Instant) {
  189. self.silent_until = timestamp + Self::SILENT_TIME;
  190. }
  191. }
  192. #[cfg(test)]
  193. mod test {
  194. use super::*;
  195. use crate::wire::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2, MOCK_IP_ADDR_3, MOCK_IP_ADDR_4};
  196. use std::collections::BTreeMap;
  197. const HADDR_A: EthernetAddress = EthernetAddress([0, 0, 0, 0, 0, 1]);
  198. const HADDR_B: EthernetAddress = EthernetAddress([0, 0, 0, 0, 0, 2]);
  199. const HADDR_C: EthernetAddress = EthernetAddress([0, 0, 0, 0, 0, 3]);
  200. const HADDR_D: EthernetAddress = EthernetAddress([0, 0, 0, 0, 0, 4]);
  201. #[test]
  202. fn test_fill() {
  203. let mut cache_storage = [Default::default(); 3];
  204. let mut cache = Cache::new(&mut cache_storage[..]);
  205. assert_eq!(
  206. cache
  207. .lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0))
  208. .found(),
  209. false
  210. );
  211. assert_eq!(
  212. cache
  213. .lookup(&MOCK_IP_ADDR_2, Instant::from_millis(0))
  214. .found(),
  215. false
  216. );
  217. cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(0));
  218. assert_eq!(
  219. cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)),
  220. Answer::Found(HADDR_A)
  221. );
  222. assert_eq!(
  223. cache
  224. .lookup(&MOCK_IP_ADDR_2, Instant::from_millis(0))
  225. .found(),
  226. false
  227. );
  228. assert_eq!(
  229. cache
  230. .lookup(
  231. &MOCK_IP_ADDR_1,
  232. Instant::from_millis(0) + Cache::ENTRY_LIFETIME * 2
  233. )
  234. .found(),
  235. false
  236. );
  237. cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(0));
  238. assert_eq!(
  239. cache
  240. .lookup(&MOCK_IP_ADDR_2, Instant::from_millis(0))
  241. .found(),
  242. false
  243. );
  244. }
  245. #[test]
  246. fn test_expire() {
  247. let mut cache_storage = [Default::default(); 3];
  248. let mut cache = Cache::new(&mut cache_storage[..]);
  249. cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(0));
  250. assert_eq!(
  251. cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)),
  252. Answer::Found(HADDR_A)
  253. );
  254. assert_eq!(
  255. cache
  256. .lookup(
  257. &MOCK_IP_ADDR_1,
  258. Instant::from_millis(0) + Cache::ENTRY_LIFETIME * 2
  259. )
  260. .found(),
  261. false
  262. );
  263. }
  264. #[test]
  265. fn test_replace() {
  266. let mut cache_storage = [Default::default(); 3];
  267. let mut cache = Cache::new(&mut cache_storage[..]);
  268. cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(0));
  269. assert_eq!(
  270. cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)),
  271. Answer::Found(HADDR_A)
  272. );
  273. cache.fill(MOCK_IP_ADDR_1, HADDR_B, Instant::from_millis(0));
  274. assert_eq!(
  275. cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)),
  276. Answer::Found(HADDR_B)
  277. );
  278. }
  279. #[test]
  280. fn test_cache_gc() {
  281. let mut cache = Cache::new_with_limit(BTreeMap::new(), 2);
  282. cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(100));
  283. cache.fill(MOCK_IP_ADDR_2, HADDR_B, Instant::from_millis(50));
  284. // Adding third item after the expiration of the previous
  285. // two should garbage collect
  286. cache.fill(
  287. MOCK_IP_ADDR_3,
  288. HADDR_C,
  289. Instant::from_millis(50) + Cache::ENTRY_LIFETIME * 2,
  290. );
  291. assert_eq!(cache.storage.len(), 1);
  292. assert_eq!(
  293. cache.lookup(
  294. &MOCK_IP_ADDR_3,
  295. Instant::from_millis(50) + Cache::ENTRY_LIFETIME * 2
  296. ),
  297. Answer::Found(HADDR_C)
  298. );
  299. }
  300. #[test]
  301. fn test_evict() {
  302. let mut cache_storage = [Default::default(); 3];
  303. let mut cache = Cache::new(&mut cache_storage[..]);
  304. cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(100));
  305. cache.fill(MOCK_IP_ADDR_2, HADDR_B, Instant::from_millis(50));
  306. cache.fill(MOCK_IP_ADDR_3, HADDR_C, Instant::from_millis(200));
  307. assert_eq!(
  308. cache.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(1000)),
  309. Answer::Found(HADDR_B)
  310. );
  311. assert_eq!(
  312. cache
  313. .lookup(&MOCK_IP_ADDR_4, Instant::from_millis(1000))
  314. .found(),
  315. false
  316. );
  317. cache.fill(MOCK_IP_ADDR_4, HADDR_D, Instant::from_millis(300));
  318. assert_eq!(
  319. cache
  320. .lookup(&MOCK_IP_ADDR_2, Instant::from_millis(1000))
  321. .found(),
  322. false
  323. );
  324. assert_eq!(
  325. cache.lookup(&MOCK_IP_ADDR_4, Instant::from_millis(1000)),
  326. Answer::Found(HADDR_D)
  327. );
  328. }
  329. #[test]
  330. fn test_hush() {
  331. let mut cache_storage = [Default::default(); 3];
  332. let mut cache = Cache::new(&mut cache_storage[..]);
  333. assert_eq!(
  334. cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)),
  335. Answer::NotFound
  336. );
  337. cache.limit_rate(Instant::from_millis(0));
  338. assert_eq!(
  339. cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(100)),
  340. Answer::RateLimited
  341. );
  342. assert_eq!(
  343. cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(2000)),
  344. Answer::NotFound
  345. );
  346. }
  347. }