diff --git a/src/iface/interface/mod.rs b/src/iface/interface/mod.rs index 58da3d05e..d8228e492 100644 --- a/src/iface/interface/mod.rs +++ b/src/iface/interface/mod.rs @@ -522,6 +522,17 @@ impl Interface { .found() } + /// Invalidate one dynamically learned neighbor and its discovery + /// rate-limit state without disturbing unrelated cache entries. + /// + /// Control planes that temporarily override dynamic resolution can use + /// this when removing the override, ensuring the next packet performs + /// fresh neighbor discovery instead of reviving a stale mapping. + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] + pub fn invalidate_neighbor(&mut self, protocol_addr: IpAddress) { + self.inner.neighbor_cache.remove(&protocol_addr); + } + /// Enable or disable the AnyIP capability. /// /// AnyIP allowins packets to be received diff --git a/src/iface/neighbor.rs b/src/iface/neighbor.rs index a512b60e3..95f3cf133 100644 --- a/src/iface/neighbor.rs +++ b/src/iface/neighbor.rs @@ -212,6 +212,12 @@ impl Cache { self.silent_until.clear(); } + /// Remove one cached mapping and its discovery rate-limit state. + pub(crate) fn remove(&mut self, protocol_addr: &IpAddress) { + self.storage.remove(protocol_addr); + self.silent_until.remove(protocol_addr); + } + /// 获取ARP缓存条目的迭代器 pub fn iter(&self) -> impl Iterator { self.storage.iter() @@ -379,4 +385,26 @@ mod test { .lookup(&MOCK_IP_ADDR_1.into(), Instant::from_millis(0)) .found()); } + + #[test] + fn test_remove_one_mapping_and_rate_limit() { + let mut cache = Cache::new(); + let first = IpAddress::from(MOCK_IP_ADDR_1); + let second = IpAddress::from(MOCK_IP_ADDR_2); + + cache.fill(first, HADDR_A, Instant::from_millis(0)); + cache.fill(second, HADDR_B, Instant::from_millis(0)); + cache.limit_rate(first, Instant::from_millis(0)); + + cache.remove(&first); + + assert_eq!( + cache.lookup(&first, Instant::from_millis(100)), + Answer::NotFound + ); + assert_eq!( + cache.lookup(&second, Instant::from_millis(100)), + Answer::Found(HADDR_B) + ); + } }