Skip to content

Commit

Permalink
arp: flush neighbor cache after IP update
Browse files Browse the repository at this point in the history
Related to smoltcp-rs#543
  • Loading branch information
deantvv committed Oct 31, 2021
1 parent 2dfc159 commit a666a7a
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 0 deletions.
77 changes: 77 additions & 0 deletions src/iface/interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -613,6 +613,7 @@ where
/// This function panics if any of the addresses are not unicast.
pub fn update_ip_addrs<F: FnOnce(&mut ManagedSlice<'a, IpCidr>)>(&mut self, f: F) {
f(&mut self.inner.ip_addrs);
InterfaceInner::flush_cache(&mut self.inner);
InterfaceInner::check_ip_addrs(&self.inner.ip_addrs)
}

Expand Down Expand Up @@ -2225,6 +2226,12 @@ impl<'a> InterfaceInner<'a> {
Err(Error::Unaddressable)
}

fn flush_cache(&mut self) {
if let Some(cache) = self.neighbor_cache.as_mut() {
cache.flush()
}
}

fn dispatch_ip<Tx: TxToken>(
&mut self,
cx: &Context,
Expand Down Expand Up @@ -3313,6 +3320,76 @@ mod test {
);
}

#[test]
#[cfg(all(feature = "medium-ethernet", feature = "proto-ipv4"))]
fn test_arp_flush_after_update_ip() {
let (mut iface, mut socket_set) = create_loopback_ethernet();

let mut eth_bytes = vec![0u8; 42];

let local_ip_addr = Ipv4Address([0x7f, 0x00, 0x00, 0x01]);
let remote_ip_addr = Ipv4Address([0x7f, 0x00, 0x00, 0x02]);
let local_hw_addr = EthernetAddress([0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
let remote_hw_addr = EthernetAddress([0x52, 0x54, 0x00, 0x00, 0x00, 0x00]);

let repr = ArpRepr::EthernetIpv4 {
operation: ArpOperation::Request,
source_hardware_addr: remote_hw_addr,
source_protocol_addr: remote_ip_addr,
target_hardware_addr: EthernetAddress::default(),
target_protocol_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]),
};

let mut frame = EthernetFrame::new_unchecked(&mut eth_bytes);
frame.set_dst_addr(EthernetAddress::BROADCAST);
frame.set_src_addr(remote_hw_addr);
frame.set_ethertype(EthernetProtocol::Arp);
{
let mut packet = ArpPacket::new_unchecked(frame.payload_mut());
repr.emit(&mut packet);
}

let cx = iface.context(Instant::from_secs(0));

// Ensure an ARP Request for us triggers an ARP Reply
assert_eq!(
iface
.inner
.process_ethernet(&cx, &mut socket_set, frame.into_inner()),
Ok(Some(EthernetPacket::Arp(ArpRepr::EthernetIpv4 {
operation: ArpOperation::Reply,
source_hardware_addr: local_hw_addr,
source_protocol_addr: local_ip_addr,
target_hardware_addr: remote_hw_addr,
target_protocol_addr: remote_ip_addr
})))
);

// Ensure the address of the requestor was entered in the cache
assert_eq!(
iface.inner.lookup_hardware_addr(
&cx,
MockTxToken,
&IpAddress::Ipv4(local_ip_addr),
&IpAddress::Ipv4(remote_ip_addr)
),
Ok((HardwareAddress::Ethernet(remote_hw_addr), MockTxToken))
);

// Update IP addrs to trigger ARP cache flush
let local_ip_addr_new = Ipv4Address([0x7f, 0x00, 0x00, 0x01]);
iface.update_ip_addrs(|addrs| {
addrs.iter_mut().next().map(|addr| {
*addr = IpCidr::Ipv4(Ipv4Cidr::new(local_ip_addr_new, 24));
});
});

// ARP cache flush after address change
assert!(!iface
.inner
.has_neighbor(&cx, &IpAddress::Ipv4(remote_ip_addr)));
}

#[test]
#[cfg(all(feature = "socket-icmp", feature = "proto-ipv4"))]
fn test_icmpv4_socket() {
Expand Down
27 changes: 27 additions & 0 deletions src/iface/neighbor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,10 @@ impl<'a> Cache<'a> {
pub(crate) fn limit_rate(&mut self, timestamp: Instant) {
self.silent_until = timestamp + Self::SILENT_TIME;
}

pub(crate) fn flush(&mut self) {
self.storage.clear()
}
}

#[cfg(test)]
Expand Down Expand Up @@ -370,4 +374,27 @@ mod test {
Answer::NotFound
);
}

#[test]
fn test_flush() {
let mut cache_storage = [Default::default(); 3];
let mut cache = Cache::new(&mut cache_storage[..]);

cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(0));
assert_eq!(
cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)),
Answer::Found(HADDR_A)
);
assert!(!cache
.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(0))
.found());

cache.flush();
assert!(!cache
.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0))
.found());
assert!(!cache
.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0))
.found());
}
}

0 comments on commit a666a7a

Please sign in to comment.