neighbor.rs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  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 wire::{EthernetAddress, IpAddress};
  5. use time::{Duration, Instant};
  6. #[cfg(any(feature = "std", feature = "alloc"))]
  7. use core::mem;
  8. /// A cached neighbor.
  9. ///
  10. /// A neighbor mapping translates from a protocol address to a hardware address,
  11. /// and contains the timestamp past which the mapping should be discarded.
  12. #[derive(Debug, Clone, Copy)]
  13. pub struct Neighbor {
  14. hardware_addr: EthernetAddress,
  15. expires_at: Instant,
  16. }
  17. /// An answer to a neighbor cache lookup.
  18. #[derive(Debug, Clone, Copy, PartialEq, Eq)]
  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. /// A neighbor cache backed by a map.
  29. ///
  30. /// # Examples
  31. ///
  32. /// On systems with heap, this cache can be created with:
  33. ///
  34. /// ```rust
  35. /// use std::collections::BTreeMap;
  36. /// use smoltcp::iface::NeighborCache;
  37. /// let mut neighbor_cache = NeighborCache::new(BTreeMap::new());
  38. /// ```
  39. ///
  40. /// On systems without heap, use:
  41. ///
  42. /// ```rust
  43. /// use smoltcp::iface::NeighborCache;
  44. /// let mut neighbor_cache_storage = [None; 8];
  45. /// let mut neighbor_cache = NeighborCache::new(&mut neighbor_cache_storage[..]);
  46. /// ```
  47. #[derive(Debug)]
  48. pub struct Cache<'a> {
  49. storage: ManagedMap<'a, IpAddress, Neighbor>,
  50. silent_until: Instant,
  51. gc_threshold: usize
  52. }
  53. impl<'a> Cache<'a> {
  54. /// Minimum delay between discovery requests, in milliseconds.
  55. pub(crate) const SILENT_TIME: Duration = Duration { millis: 1_000 };
  56. /// Neighbor entry lifetime, in milliseconds.
  57. pub(crate) const ENTRY_LIFETIME: Duration = Duration { millis: 60_000 };
  58. /// Default number of entries in the cache before GC kicks in
  59. pub(crate) const GC_THRESHOLD: usize = 1024;
  60. /// Create a cache. The backing storage is cleared upon creation.
  61. ///
  62. /// # Panics
  63. /// This function panics if `storage.len() == 0`.
  64. pub fn new<T>(storage: T) -> Cache<'a>
  65. where T: Into<ManagedMap<'a, IpAddress, Neighbor>> {
  66. Cache::new_with_limit(storage, Cache::GC_THRESHOLD)
  67. }
  68. pub fn new_with_limit<T>(storage: T, gc_threshold: usize) -> Cache<'a>
  69. where T: Into<ManagedMap<'a, IpAddress, Neighbor>> {
  70. let mut storage = storage.into();
  71. storage.clear();
  72. Cache { storage, gc_threshold, silent_until: Instant::from_millis(0) }
  73. }
  74. pub fn fill(&mut self, protocol_addr: IpAddress, hardware_addr: EthernetAddress,
  75. timestamp: Instant) {
  76. debug_assert!(protocol_addr.is_unicast());
  77. debug_assert!(hardware_addr.is_unicast());
  78. #[cfg(any(feature = "std", feature = "alloc"))]
  79. let current_storage_size = self.storage.len();
  80. match self.storage {
  81. ManagedMap::Borrowed(_) => (),
  82. #[cfg(any(feature = "std", feature = "alloc"))]
  83. ManagedMap::Owned(ref mut map) => {
  84. if current_storage_size >= self.gc_threshold {
  85. let new_btree_map = map.into_iter()
  86. .map(|(key, value)| (*key, *value))
  87. .filter(|(_, v)| timestamp < v.expires_at)
  88. .collect();
  89. mem::replace(map, new_btree_map);
  90. }
  91. }
  92. };
  93. let neighbor = Neighbor {
  94. expires_at: timestamp + Self::ENTRY_LIFETIME, hardware_addr
  95. };
  96. match self.storage.insert(protocol_addr, neighbor) {
  97. Ok(Some(old_neighbor)) => {
  98. if old_neighbor.hardware_addr != hardware_addr {
  99. net_trace!("replaced {} => {} (was {})",
  100. protocol_addr, hardware_addr, old_neighbor.hardware_addr);
  101. }
  102. }
  103. Ok(None) => {
  104. net_trace!("filled {} => {} (was empty)", protocol_addr, hardware_addr);
  105. }
  106. Err((protocol_addr, neighbor)) => {
  107. // If we're going down this branch, it means that a fixed-size cache storage
  108. // is full, and we need to evict an entry.
  109. let old_protocol_addr = match self.storage {
  110. ManagedMap::Borrowed(ref mut pairs) => {
  111. pairs
  112. .iter()
  113. .min_by_key(|pair_opt| {
  114. let (_protocol_addr, neighbor) = pair_opt.unwrap();
  115. neighbor.expires_at
  116. })
  117. .expect("empty neighbor cache storage") // unwraps min_by_key
  118. .unwrap() // unwraps pair
  119. .0
  120. }
  121. // Owned maps can extend themselves.
  122. #[cfg(any(feature = "std", feature = "alloc"))]
  123. ManagedMap::Owned(_) => unreachable!()
  124. };
  125. let _old_neighbor =
  126. self.storage.remove(&old_protocol_addr).unwrap();
  127. match self.storage.insert(protocol_addr, neighbor) {
  128. Ok(None) => {
  129. net_trace!("filled {} => {} (evicted {} => {})",
  130. protocol_addr, hardware_addr,
  131. old_protocol_addr, _old_neighbor.hardware_addr);
  132. }
  133. // We've covered everything else above.
  134. _ => unreachable!()
  135. }
  136. }
  137. }
  138. }
  139. pub(crate) fn lookup_pure(&self, protocol_addr: &IpAddress, timestamp: Instant) ->
  140. Option<EthernetAddress> {
  141. if protocol_addr.is_broadcast() {
  142. return Some(EthernetAddress::BROADCAST)
  143. }
  144. match self.storage.get(protocol_addr) {
  145. Some(&Neighbor { expires_at, hardware_addr }) => {
  146. if timestamp < expires_at {
  147. return Some(hardware_addr)
  148. }
  149. }
  150. None => ()
  151. }
  152. None
  153. }
  154. pub(crate) fn lookup(&mut self, protocol_addr: &IpAddress, timestamp: Instant) -> Answer {
  155. match self.lookup_pure(protocol_addr, timestamp) {
  156. Some(hardware_addr) =>
  157. Answer::Found(hardware_addr),
  158. None if timestamp < self.silent_until =>
  159. Answer::RateLimited,
  160. None => {
  161. self.silent_until = timestamp + Self::SILENT_TIME;
  162. Answer::NotFound
  163. }
  164. }
  165. }
  166. }
  167. #[cfg(test)]
  168. mod test {
  169. use super::*;
  170. use std::collections::BTreeMap;
  171. use wire::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2, MOCK_IP_ADDR_3, MOCK_IP_ADDR_4};
  172. const HADDR_A: EthernetAddress = EthernetAddress([0, 0, 0, 0, 0, 1]);
  173. const HADDR_B: EthernetAddress = EthernetAddress([0, 0, 0, 0, 0, 2]);
  174. const HADDR_C: EthernetAddress = EthernetAddress([0, 0, 0, 0, 0, 3]);
  175. const HADDR_D: EthernetAddress = EthernetAddress([0, 0, 0, 0, 0, 4]);
  176. #[test]
  177. fn test_fill() {
  178. let mut cache_storage = [Default::default(); 3];
  179. let mut cache = Cache::new(&mut cache_storage[..]);
  180. assert_eq!(cache.lookup_pure(&MOCK_IP_ADDR_1, Instant::from_millis(0)), None);
  181. assert_eq!(cache.lookup_pure(&MOCK_IP_ADDR_2, Instant::from_millis(0)), None);
  182. cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(0));
  183. assert_eq!(cache.lookup_pure(&MOCK_IP_ADDR_1, Instant::from_millis(0)), Some(HADDR_A));
  184. assert_eq!(cache.lookup_pure(&MOCK_IP_ADDR_2, Instant::from_millis(0)), None);
  185. assert_eq!(cache.lookup_pure(&MOCK_IP_ADDR_1, Instant::from_millis(0) + Cache::ENTRY_LIFETIME * 2),
  186. None);
  187. cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(0));
  188. assert_eq!(cache.lookup_pure(&MOCK_IP_ADDR_2, Instant::from_millis(0)), None);
  189. }
  190. #[test]
  191. fn test_expire() {
  192. let mut cache_storage = [Default::default(); 3];
  193. let mut cache = Cache::new(&mut cache_storage[..]);
  194. cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(0));
  195. assert_eq!(cache.lookup_pure(&MOCK_IP_ADDR_1, Instant::from_millis(0)), Some(HADDR_A));
  196. assert_eq!(cache.lookup_pure(&MOCK_IP_ADDR_1, Instant::from_millis(0) + Cache::ENTRY_LIFETIME * 2),
  197. None);
  198. }
  199. #[test]
  200. fn test_replace() {
  201. let mut cache_storage = [Default::default(); 3];
  202. let mut cache = Cache::new(&mut cache_storage[..]);
  203. cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(0));
  204. assert_eq!(cache.lookup_pure(&MOCK_IP_ADDR_1, Instant::from_millis(0)), Some(HADDR_A));
  205. cache.fill(MOCK_IP_ADDR_1, HADDR_B, Instant::from_millis(0));
  206. assert_eq!(cache.lookup_pure(&MOCK_IP_ADDR_1, Instant::from_millis(0)), Some(HADDR_B));
  207. }
  208. #[test]
  209. fn test_cache_gc() {
  210. let mut cache = Cache::new_with_limit(BTreeMap::new(), 2);
  211. cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(100));
  212. cache.fill(MOCK_IP_ADDR_2, HADDR_B, Instant::from_millis(50));
  213. // Adding third item after the expiration of the previous
  214. // two should garbage collect
  215. cache.fill(MOCK_IP_ADDR_3, HADDR_C, Instant::from_millis(50) + Cache::ENTRY_LIFETIME * 2);
  216. assert_eq!(cache.storage.len(), 1);
  217. assert_eq!(cache.lookup_pure(&MOCK_IP_ADDR_3, Instant::from_millis(50) + Cache::ENTRY_LIFETIME * 2), Some(HADDR_C));
  218. }
  219. #[test]
  220. fn test_evict() {
  221. let mut cache_storage = [Default::default(); 3];
  222. let mut cache = Cache::new(&mut cache_storage[..]);
  223. cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(100));
  224. cache.fill(MOCK_IP_ADDR_2, HADDR_B, Instant::from_millis(50));
  225. cache.fill(MOCK_IP_ADDR_3, HADDR_C, Instant::from_millis(200));
  226. assert_eq!(cache.lookup_pure(&MOCK_IP_ADDR_2, Instant::from_millis(1000)), Some(HADDR_B));
  227. assert_eq!(cache.lookup_pure(&MOCK_IP_ADDR_4, Instant::from_millis(1000)), None);
  228. cache.fill(MOCK_IP_ADDR_4, HADDR_D, Instant::from_millis(300));
  229. assert_eq!(cache.lookup_pure(&MOCK_IP_ADDR_2, Instant::from_millis(1000)), None);
  230. assert_eq!(cache.lookup_pure(&MOCK_IP_ADDR_4, Instant::from_millis(1000)), Some(HADDR_D));
  231. }
  232. #[test]
  233. fn test_hush() {
  234. let mut cache_storage = [Default::default(); 3];
  235. let mut cache = Cache::new(&mut cache_storage[..]);
  236. assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)), Answer::NotFound);
  237. assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(100)), Answer::RateLimited);
  238. assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(2000)), Answer::NotFound);
  239. }
  240. }