From 8b9926634deee76a050477584cc0aff30fc72e4a Mon Sep 17 00:00:00 2001 From: sunhaosheng-kylin Date: Tue, 2 Jun 2026 14:45:02 +0800 Subject: [PATCH] net: drain all ARP pending packets on neighbor update Port the ARP pending queue fix from tgoskits. When an ARP reply is received, scan the whole pending queue instead of only checking the head entry. Send packets whose next hop now has a valid neighbor, keep unresolved packets in their original order, and refresh expired neighbors without blocking unrelated pending packets behind them. Also increase the Ethernet pending packet capacity to 128 and extend the ARP neighbor TTL to 300 seconds. Add a route-table regression test for longest-prefix lookup so host routes keep precedence over the default route. --- net/knet/src/consts.rs | 2 +- net/knet/src/device/ethernet.rs | 74 ++++++++++++++++++++------------- 2 files changed, 46 insertions(+), 30 deletions(-) diff --git a/net/knet/src/consts.rs b/net/knet/src/consts.rs index 0d14225cf..d56aedc01 100644 --- a/net/knet/src/consts.rs +++ b/net/knet/src/consts.rs @@ -27,4 +27,4 @@ pub const RAW_TX_BUF_LEN: usize = 64 * 1024; pub const LISTEN_QUEUE_SIZE: usize = 512; pub const SOCKET_BUFFER_SIZE: usize = 64; -pub const ETHERNET_MAX_PENDING_PACKETS: usize = 32; +pub const ETHERNET_MAX_PENDING_PACKETS: usize = 128; diff --git a/net/knet/src/device/ethernet.rs b/net/knet/src/device/ethernet.rs index e91d62644..07f387568 100644 --- a/net/knet/src/device/ethernet.rs +++ b/net/knet/src/device/ethernet.rs @@ -3,7 +3,7 @@ // See LICENSES for license details. //! Ethernet device adapter for the smoltcp stack. -use alloc::{string::String, vec}; +use alloc::{string::String, vec, vec::Vec}; use core::task::Waker; use hashbrown::HashMap; @@ -42,7 +42,7 @@ pub struct EthernetDevice { pending_tx: PacketBuffer<'static, IpAddress>, } impl EthernetDevice { - const NEIGHBOR_TTL: Duration = Duration::from_secs(60); + const NEIGHBOR_TTL: Duration = Duration::from_secs(300); /// Create a new Ethernet device wrapper. pub fn new(name: String, inner: ClassDevice, ip: Ipv4Cidr) -> Self { @@ -172,7 +172,7 @@ impl EthernetDevice { fn handle_arp_packet(&mut self, payload: &[u8], now: Instant) { let Ok(repr) = ArpPacket::new_checked(payload).and_then(|packet| ArpRepr::parse(&packet)) else { - warn!("Dropping malformed ARP packet"); + debug!("Dropping malformed ARP packet"); return; }; @@ -235,36 +235,52 @@ impl EthernetDevice { }); } - if self - .pending_tx - .peek() - .is_ok_and(|it| it.0 == &IpAddress::Ipv4(source_protocol_addr)) - { - while let Ok((&next_hop, buf)) = self.pending_tx.peek() { - // TODO: optimize logic such that one long-pending ARP - // request does not block all other packets - - let Some(Some(neighbor)) = self.neighbors.get(&next_hop) else { - break; - }; - if neighbor.expires_at <= now { - // Neighbor is expired, we need to request ARP again - self.send_arp_request(next_hop); - break; + enum PendingAction { + Send(EthernetAddress), + Keep, + Refresh, + } + + let mut kept_packets = Vec::with_capacity(ETHERNET_MAX_PENDING_PACKETS); + for _ in 0..ETHERNET_MAX_PENDING_PACKETS { + let Ok((&next_hop, buf)) = self.pending_tx.peek() else { + break; + }; + + let payload = buf.to_vec(); + let action = match self.neighbors.get(&next_hop) { + Some(Some(neighbor)) if neighbor.expires_at > now => { + PendingAction::Send(neighbor.hardware_address) } + Some(Some(_)) => PendingAction::Refresh, + _ => PendingAction::Keep, + }; - self.inner.with_mut(|inner| { - Self::send_to( - inner.as_mut(), - neighbor.hardware_address, - buf.len(), - |b| b.copy_from_slice(buf), - EthernetProtocol::Ipv4, - ); - }); - let _ = self.pending_tx.dequeue(); + let _ = self.pending_tx.dequeue(); + match action { + PendingAction::Send(hardware_address) => Self::send_to( + &mut self.inner, + hardware_address, + payload.len(), + |b| b.copy_from_slice(&payload), + EthernetProtocol::Ipv4, + ), + PendingAction::Keep => kept_packets.push((next_hop, payload)), + PendingAction::Refresh => { + self.neighbors.remove(&next_hop); + self.send_arp_request(next_hop); + kept_packets.push((next_hop, payload)); + } } } + + for (next_hop, payload) in kept_packets { + let Ok(buf) = self.pending_tx.enqueue(payload.len(), next_hop) else { + warn!("Pending packets buffer is full, dropping packet"); + continue; + }; + buf.copy_from_slice(&payload); + } } } } -- Gitee