use alloc::{boxed::Box, collections::VecDeque, string::String, vec, vec::Vec};
use hashbrown::HashMap;
use smoltcp::{
storage::{PacketBuffer, PacketMetadata},
time::{Duration, Instant},
wire::{
ArpOperation, ArpPacket, ArpRepr, EthernetAddress, EthernetFrame, EthernetProtocol,
EthernetRepr, IpAddress, IpVersion, Ipv4Cidr,
},
};
use crate::{
config::InterfaceId,
consts::{ETHERNET_MAX_PENDING_PACKETS, STANDARD_MTU},
device::{
ArpEntry, Device, DeviceRxPacket, DeviceRxPoll, ETH_ZLEN, EthernetFramePort,
NetDeviceError, NetDeviceResult, ProtocolEthernetFrame, TxNotify, TxSubmitOptions,
},
};
const EMPTY_MAC: EthernetAddress = EthernetAddress([0; 6]);
struct Neighbor {
hardware_address: EthernetAddress,
expires_at: Instant,
}
struct PendingNeighbor {
requested_at: Instant,
}
pub struct EthernetDevice {
name: String,
inner: Box<dyn EthernetFramePort>,
neighbors: HashMap<IpAddress, Neighbor>,
pending_neighbors: HashMap<IpAddress, PendingNeighbor>,
ip: Option<Ipv4Cidr>,
pending_packets: PacketBuffer<'static, IpAddress>,
pending_arp_replies: VecDeque<(EthernetAddress, ArpRepr)>,
deferred_tx_frame_lens: Vec<usize>,
deferred_rx_frame_lens: Vec<usize>,
deferred_tx_errors: u64,
deferred_tx_drops: u64,
deferred_rx_errors: u64,
deferred_rx_drops: u64,
}
impl EthernetDevice {
const NEIGHBOR_TTL: Duration = Duration::from_secs(300);
const ARP_REQUEST_RETRY: Duration = Duration::from_secs(1);
pub fn new(name: String, inner: Box<dyn EthernetFramePort>, ip: Option<Ipv4Cidr>) -> Self {
let pending_packets = PacketBuffer::new(
vec![PacketMetadata::EMPTY; ETHERNET_MAX_PENDING_PACKETS],
vec![
0u8;
(STANDARD_MTU + EthernetFrame::<&[u8]>::header_len())
* ETHERNET_MAX_PENDING_PACKETS
],
);
Self {
name,
inner,
neighbors: HashMap::new(),
pending_neighbors: HashMap::new(),
ip,
pending_packets,
pending_arp_replies: VecDeque::new(),
deferred_tx_frame_lens: Vec::new(),
deferred_rx_frame_lens: Vec::new(),
deferred_tx_errors: 0,
deferred_tx_drops: 0,
deferred_rx_errors: 0,
deferred_rx_drops: 0,
}
}
#[inline]
fn hardware_address(&self) -> EthernetAddress {
EthernetAddress(self.inner.mac_address())
}
fn transmit_ip_to(
&mut self,
destination: EthernetAddress,
packet: &[u8],
) -> NetDeviceResult<usize> {
if !self.flush_arp_replies() {
return Err(NetDeviceError::Again);
}
let protocol = match IpVersion::of_packet(packet) {
Ok(IpVersion::Ipv4) => EthernetProtocol::Ipv4,
Ok(IpVersion::Ipv6) => EthernetProtocol::Ipv6,
Err(_) => return Err(NetDeviceError::InvalidParam),
};
Self::send_to_with_options(
&mut *self.inner,
destination,
packet.len(),
|buffer| buffer.copy_from_slice(packet),
protocol,
TxNotify::Deferred,
)
}
fn send_to<F>(
inner: &mut dyn EthernetFramePort,
dst: EthernetAddress,
size: usize,
f: F,
proto: EthernetProtocol,
) -> NetDeviceResult<usize>
where
F: FnOnce(&mut [u8]),
{
Self::send_to_with_options(inner, dst, size, f, proto, TxNotify::Immediate)
}
fn send_to_with_options<F>(
inner: &mut dyn EthernetFramePort,
dst: EthernetAddress,
size: usize,
f: F,
proto: EthernetProtocol,
notify: TxNotify,
) -> NetDeviceResult<usize>
where
F: FnOnce(&mut [u8]),
{
let repr = EthernetRepr {
src_addr: EthernetAddress(inner.mac_address()),
dst_addr: dst,
ethertype: proto,
};
let total_frame_len = repr.buffer_len() + size;
let wire_len = total_frame_len.max(ETH_ZLEN);
let mut fill_once = Some(f);
let mut fill = |packet: &mut [u8]| {
let mut frame = EthernetFrame::new_unchecked(packet);
repr.emit(&mut frame);
fill_once
.take()
.expect("frame port must fill each packet exactly once")(
frame.payload_mut()
);
trace!(
"SEND {} bytes: {:02X?}",
frame.as_ref().len(),
frame.as_ref()
);
};
inner.transmit_frame_with_options(
total_frame_len,
TxSubmitOptions {
checksum: None,
notify,
},
&mut fill,
)?;
Ok(wire_len)
}
fn flush_arp_replies(&mut self) -> bool {
while let Some((destination, reply)) = self.pending_arp_replies.front().copied() {
match Self::send_to(
&mut *self.inner,
destination,
reply.buffer_len(),
|buffer| reply.emit(&mut ArpPacket::new_unchecked(buffer)),
EthernetProtocol::Arp,
) {
Ok(frame_len) => self.deferred_tx_frame_lens.push(frame_len),
Err(NetDeviceError::Again) => return false,
Err(error) => {
warn!("{}: failed to send ARP reply: {error:?}", self.name);
self.deferred_tx_errors += 1;
}
}
self.pending_arp_replies.pop_front();
}
true
}
fn handle_frame(
&mut self,
frame: &[u8],
interface_id: InterfaceId,
buffer: &mut PacketBuffer<InterfaceId>,
timestamp: Instant,
snoop: &mut dyn FnMut(&[u8]),
) -> usize {
let frame_len = frame.len();
let frame = EthernetFrame::new_unchecked(frame);
let Ok(repr) = EthernetRepr::parse(&frame) else {
warn!("Dropping malformed Ethernet frame");
self.deferred_rx_errors += 1;
return 0;
};
if !repr.dst_addr.is_broadcast()
&& repr.dst_addr != EMPTY_MAC
&& repr.dst_addr != self.hardware_address()
{
return 0;
}
match repr.ethertype {
EthernetProtocol::Ipv4 | EthernetProtocol::Ipv6 => {
snoop(frame.payload());
buffer
.enqueue(frame.payload().len(), interface_id)
.expect(
"recv precondition: buffer checked !rx_buffer.is_full() before calling \
recv()",
)
.copy_from_slice(frame.payload());
frame_len
}
EthernetProtocol::Arp => {
self.process_arp(frame.payload(), timestamp);
self.deferred_rx_frame_lens.push(frame_len);
0
}
_ => {
self.deferred_rx_frame_lens.push(frame_len);
self.deferred_rx_drops += 1;
0
}
}
}
fn handle_non_ip_frame(&mut self, frame: &[u8], timestamp: Instant) {
let frame_len = frame.len();
let frame = EthernetFrame::new_unchecked(frame);
let Ok(repr) = EthernetRepr::parse(&frame) else {
self.deferred_rx_errors += 1;
return;
};
match repr.ethertype {
EthernetProtocol::Arp => {
self.process_arp(frame.payload(), timestamp);
self.deferred_rx_frame_lens.push(frame_len);
}
EthernetProtocol::Ipv4 | EthernetProtocol::Ipv6 => {}
_ => {
self.deferred_rx_frame_lens.push(frame_len);
self.deferred_rx_drops += 1;
}
}
}
fn request_arp(&mut self, target_ip: IpAddress, timestamp: Instant) -> NetDeviceResult {
let IpAddress::Ipv4(target_ipv4) = target_ip else {
warn!("IPv6 address ARP is not supported: {}", target_ip);
return Err(NetDeviceError::InvalidParam);
};
let Some(ip) = self.ip else {
warn!("cannot request ARP for {target_ipv4}: ethernet IPv4 is not configured");
return Err(NetDeviceError::InvalidParam);
};
info!("{}: requesting ARP for {}", self.name, target_ipv4);
let arp_repr = ArpRepr::EthernetIpv4 {
operation: ArpOperation::Request,
source_hardware_addr: self.hardware_address(),
source_protocol_addr: ip.address(),
target_hardware_addr: EMPTY_MAC,
target_protocol_addr: target_ipv4,
};
let arp_frame_len = Self::send_to(
&mut *self.inner,
EthernetAddress::BROADCAST,
arp_repr.buffer_len(),
|buf| arp_repr.emit(&mut ArpPacket::new_unchecked(buf)),
EthernetProtocol::Arp,
)?;
self.deferred_tx_frame_lens.push(arp_frame_len);
self.pending_neighbors.insert(
target_ip,
PendingNeighbor {
requested_at: timestamp,
},
);
Ok(())
}
fn process_arp(&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");
self.deferred_rx_errors += 1;
return;
};
if let ArpRepr::EthernetIpv4 {
operation,
source_hardware_addr,
source_protocol_addr,
target_hardware_addr,
target_protocol_addr,
} = repr
{
let is_unicast_mac =
target_hardware_addr != EMPTY_MAC && !target_hardware_addr.is_broadcast();
if is_unicast_mac && self.hardware_address() != target_hardware_addr {
return;
}
if let ArpOperation::Unknown(_) = operation {
return;
}
if !source_hardware_addr.is_unicast()
|| source_protocol_addr.is_broadcast()
|| source_protocol_addr.is_multicast()
|| source_protocol_addr.is_unspecified()
{
return;
}
let Some(ip) = self.ip else {
return;
};
if ip.address() != target_protocol_addr {
return;
}
info!(
"{}: ARP {} -> {}",
self.name, source_protocol_addr, source_hardware_addr
);
self.pending_neighbors
.remove(&IpAddress::Ipv4(source_protocol_addr));
self.neighbors.insert(
IpAddress::Ipv4(source_protocol_addr),
Neighbor {
hardware_address: source_hardware_addr,
expires_at: now + Self::NEIGHBOR_TTL,
},
);
if let ArpOperation::Request = operation {
let response = ArpRepr::EthernetIpv4 {
operation: ArpOperation::Reply,
source_hardware_addr: self.hardware_address(),
source_protocol_addr: ip.address(),
target_hardware_addr: source_hardware_addr,
target_protocol_addr: source_protocol_addr,
};
let reply = (source_hardware_addr, response);
if !self.pending_arp_replies.contains(&reply) {
if self.pending_arp_replies.len() < ETHERNET_MAX_PENDING_PACKETS {
self.pending_arp_replies.push_back(reply);
} else {
self.deferred_tx_drops += 1;
}
}
self.flush_arp_replies();
}
let mut kept: Vec<(IpAddress, Vec<u8>)> =
Vec::with_capacity(ETHERNET_MAX_PENDING_PACKETS);
for _ in 0..ETHERNET_MAX_PENDING_PACKETS {
let Ok((&next_hop, buf)) = self.pending_packets.peek() else {
break;
};
enum Action {
Send(EthernetAddress, Vec<u8>),
Refresh(Vec<u8>),
Keep(Vec<u8>),
}
let action = match self.neighbors.get(&next_hop) {
Some(neighbor) if neighbor.expires_at > now => {
Action::Send(neighbor.hardware_address, buf.to_vec())
}
Some(_) => Action::Refresh(buf.to_vec()),
None => Action::Keep(buf.to_vec()),
};
self.pending_packets
.dequeue()
.expect("peek succeeded moments ago; dequeue must succeed");
match action {
Action::Send(mac, payload) => {
info!(
"{}: sending pending IPv4 packet to {} via {}",
self.name, next_hop, mac
);
match self.transmit_ip_to(mac, &payload) {
Ok(frame_len) => self.deferred_tx_frame_lens.push(frame_len),
Err(NetDeviceError::Again) => kept.push((next_hop, payload)),
Err(err) => {
warn!(
"{}: failed to send pending packet to {}: {err:?}",
self.name, next_hop
);
self.deferred_tx_errors += 1;
}
}
}
Action::Refresh(payload) => {
self.neighbors.remove(&next_hop);
if let Err(err) = self.request_arp(next_hop, now)
&& !matches!(err, NetDeviceError::Again)
{
warn!(
"{}: failed to refresh ARP entry for {}: {err:?}",
self.name, next_hop
);
self.deferred_tx_errors += 1;
}
kept.push((next_hop, payload));
}
Action::Keep(payload) => {
kept.push((next_hop, payload));
}
}
}
for (next_hop, payload) in kept {
let Ok(dst) = self.pending_packets.enqueue(payload.len(), next_hop) else {
warn!(
"{}: pending buffer overflow while restoring queue entry to {}",
self.name, next_hop
);
break;
};
dst.copy_from_slice(&payload);
}
}
}
}
impl Device for EthernetDevice {
fn name(&self) -> &str {
&self.name
}
fn recv(
&mut self,
interface_id: InterfaceId,
buffer: &mut PacketBuffer<InterfaceId>,
timestamp: Instant,
snoop: &mut dyn FnMut(&[u8]),
) -> usize {
self.flush_arp_replies();
loop {
let rx_buf = match self.inner.receive() {
Ok(buf) => buf,
Err(err) => {
if !matches!(err, crate::device::NetDeviceError::Again) {
warn!("receive failed: {:?}", err);
self.deferred_rx_errors += 1;
}
return 0;
}
};
trace!(
"RECV {} bytes: {:02X?}",
rx_buf.packet_len(),
rx_buf.packet()
);
let frame_len =
self.handle_frame(rx_buf.packet(), interface_id, buffer, timestamp, snoop);
if frame_len > 0 {
return frame_len;
}
}
}
fn poll_owned_rx(&mut self, timestamp: Instant) -> DeviceRxPoll {
self.flush_arp_replies();
loop {
let frame = match self.inner.receive_owned() {
Ok(Some(frame)) => frame,
Ok(None) => return DeviceRxPoll::Unsupported,
Err(NetDeviceError::Again) => return DeviceRxPoll::Idle,
Err(err) => {
warn!("receive failed: {err:?}");
self.deferred_rx_errors += 1;
return DeviceRxPoll::Idle;
}
};
let hardware_address = self.hardware_address();
let mut malformed = false;
let mut side_frame = false;
let packet_range = frame.read_with(|packet| {
trace!("RECV {} bytes: {:02X?}", packet.len(), packet);
let Ok(ethernet) = EthernetFrame::new_checked(packet) else {
malformed = true;
return None;
};
let Ok(repr) = EthernetRepr::parse(ðernet) else {
malformed = true;
return None;
};
if !repr.dst_addr.is_broadcast()
&& repr.dst_addr != EMPTY_MAC
&& repr.dst_addr != hardware_address
{
return None;
}
match repr.ethertype {
EthernetProtocol::Ipv4 | EthernetProtocol::Ipv6 => {
let payload_len = ethernet.payload().len();
let payload_start = packet.len() - payload_len;
Some(payload_start..payload_start + payload_len)
}
_ => {
side_frame = true;
None
}
}
});
if malformed {
self.deferred_rx_errors += 1;
}
if side_frame {
frame.read_with(|packet| self.handle_non_ip_frame(packet, timestamp));
}
if let Some(packet_range) = packet_range {
let frame_len = frame.packet_len();
return DeviceRxPoll::Packet(DeviceRxPacket::with_packet_range(
frame_len,
frame,
packet_range,
));
}
}
}
fn recv_direct(
&mut self,
timestamp: Instant,
deliver: &mut dyn FnMut(&[u8]) -> bool,
snoop: &mut dyn FnMut(&[u8]),
) -> Option<usize> {
self.flush_arp_replies();
loop {
let hardware_address = self.hardware_address();
let mut side_frame = None;
let mut malformed = false;
let mut dropped = false;
let result = self.inner.receive_with(&mut |packet| {
trace!("RECV {} bytes: {:02X?}", packet.len(), packet);
let Ok(frame) = EthernetFrame::new_checked(packet) else {
malformed = true;
return 0;
};
let Ok(repr) = EthernetRepr::parse(&frame) else {
malformed = true;
return 0;
};
if !repr.dst_addr.is_broadcast()
&& repr.dst_addr != EMPTY_MAC
&& repr.dst_addr != hardware_address
{
return 0;
}
match repr.ethertype {
EthernetProtocol::Ipv4 | EthernetProtocol::Ipv6 => {
snoop(frame.payload());
if deliver(frame.payload()) {
packet.len()
} else {
dropped = true;
0
}
}
_ => {
match ProtocolEthernetFrame::copy_from_slice(packet) {
Ok(frame) => side_frame = Some(frame),
Err(_) => malformed = true,
}
0
}
}
});
let frame_len = match result {
Ok(frame_len) => frame_len,
Err(NetDeviceError::Again) => return Some(0),
Err(err) => {
warn!("receive failed: {err:?}");
self.deferred_rx_errors += 1;
return Some(0);
}
};
if malformed {
self.deferred_rx_errors += 1;
}
if dropped {
self.deferred_rx_drops += 1;
}
if let Some(frame) = side_frame {
self.handle_non_ip_frame(frame.packet(), timestamp);
}
if frame_len > 0 {
return Some(frame_len);
}
}
}
fn send(&mut self, next_hop: IpAddress, packet: &[u8], timestamp: Instant) -> usize {
match self.try_send(next_hop, packet, timestamp) {
Ok(frame_len) => frame_len,
Err(NetDeviceError::Again) => {
self.deferred_tx_drops += 1;
0
}
Err(err) => {
warn!("{}: transmit failed: {err:?}", self.name);
self.deferred_tx_errors += 1;
0
}
}
}
fn try_send(
&mut self,
next_hop: IpAddress,
packet: &[u8],
timestamp: Instant,
) -> NetDeviceResult<usize> {
let is_subnet_broadcast =
self.ip.and_then(|ip| ip.broadcast()).map(IpAddress::Ipv4) == Some(next_hop);
if next_hop.is_broadcast() || is_subnet_broadcast {
return self.transmit_ip_to(EthernetAddress::BROADCAST, packet);
}
if next_hop.is_multicast() {
let hardware_address = match next_hop {
IpAddress::Ipv4(address) => {
let octets = address.octets();
EthernetAddress([0x01, 0x00, 0x5e, octets[1] & 0x7f, octets[2], octets[3]])
}
IpAddress::Ipv6(address) => {
let octets = address.octets();
EthernetAddress([0x33, 0x33, octets[12], octets[13], octets[14], octets[15]])
}
};
return self.transmit_ip_to(hardware_address, packet);
}
let need_request = match self.neighbors.get(&next_hop) {
Some(neighbor) if neighbor.expires_at > timestamp => {
let hardware_address = neighbor.hardware_address;
return self.transmit_ip_to(hardware_address, packet);
}
Some(_) => {
self.neighbors.remove(&next_hop);
true
}
None => self
.pending_neighbors
.get(&next_hop)
.is_none_or(|pending| timestamp >= pending.requested_at + Self::ARP_REQUEST_RETRY),
};
if need_request {
self.request_arp(next_hop, timestamp)?;
}
if self.pending_packets.is_full() {
warn!(
"{}: Pending packets buffer is full, dropping packet",
self.name
);
self.deferred_tx_drops += 1;
return Ok(0);
}
let Ok(dst_buffer) = self.pending_packets.enqueue(packet.len(), next_hop) else {
warn!("Failed to enqueue packet in pending packets buffer");
self.deferred_tx_drops += 1;
return Ok(0);
};
dst_buffer.copy_from_slice(packet);
Ok(0)
}
fn drain_deferred_tx(&mut self) -> Vec<usize> {
core::mem::take(&mut self.deferred_tx_frame_lens)
}
fn drain_deferred_rx(&mut self) -> Vec<usize> {
core::mem::take(&mut self.deferred_rx_frame_lens)
}
fn drain_deferred_tx_errors(&mut self) -> u64 {
core::mem::take(&mut self.deferred_tx_errors)
}
fn drain_deferred_tx_drops(&mut self) -> u64 {
core::mem::take(&mut self.deferred_tx_drops)
}
fn drain_deferred_rx_errors(&mut self) -> u64 {
core::mem::take(&mut self.deferred_rx_errors)
}
fn drain_deferred_rx_drops(&mut self) -> u64 {
core::mem::take(&mut self.deferred_rx_drops) + self.inner.drain_rx_drops()
}
fn set_ipv4_addr(&mut self, addr: Option<Ipv4Cidr>) {
self.ip = addr;
self.neighbors.clear();
self.pending_neighbors.clear();
self.deferred_tx_drops += self.pending_arp_replies.len() as u64;
self.pending_arp_replies.clear();
}
fn arp_entries(&self, timestamp: Instant) -> Vec<ArpEntry> {
self.neighbors
.iter()
.filter_map(|(ip_addr, neighbor)| {
if neighbor.expires_at <= timestamp {
return None;
}
let IpAddress::Ipv4(ip_addr) = ip_addr else {
return None;
};
Some(ArpEntry {
ip_addr: ip_addr.octets(),
hw_type: 1,
flags: 2,
hw_addr: neighbor.hardware_address.0,
device: self.name.clone(),
})
})
.collect()
}
}
#[cfg(test)]
mod ethernet_counter_tests {
use alloc::{collections::VecDeque, sync::Arc};
use ax_sync::SpinLock;
use smoltcp::wire::{Ipv4Address, Ipv4Cidr};
use super::*;
use crate::device::{NetDeviceError, NetDeviceResult, TxChecksumCapabilities};
struct MockEthernetDriver {
mac: [u8; 6],
checksum_capabilities: TxChecksumCapabilities,
rx_frames: VecDeque<Vec<u8>>,
tx_frames: Vec<Vec<u8>>,
tx_alloc_fail: bool,
}
impl MockEthernetDriver {
fn new(mac: [u8; 6]) -> Self {
Self {
mac,
checksum_capabilities: TxChecksumCapabilities::NONE,
rx_frames: VecDeque::new(),
tx_frames: Vec::new(),
tx_alloc_fail: false,
}
}
fn enqueue_rx_frame(&mut self, frame: Vec<u8>) {
self.rx_frames.push_back(frame);
}
}
impl EthernetFramePort for MockEthernetDriver {
fn device_name(&self) -> &str {
"mock"
}
fn mac_address(&self) -> [u8; 6] {
self.mac
}
fn checksum_capabilities(&self) -> TxChecksumCapabilities {
self.checksum_capabilities
}
fn transmit(&mut self, frame: &ProtocolEthernetFrame) -> NetDeviceResult {
if self.tx_alloc_fail {
return Err(NetDeviceError::Again);
}
self.tx_frames.push(frame.packet().to_vec());
Ok(())
}
fn receive(&mut self) -> NetDeviceResult<ProtocolEthernetFrame> {
self.rx_frames
.pop_front()
.map(|packet| ProtocolEthernetFrame::copy_from_slice(&packet).unwrap())
.ok_or(NetDeviceError::Again)
}
}
#[derive(Default)]
struct TxProbe {
requests: SpinLock<Vec<(Vec<u8>, TxSubmitOptions)>>,
failure: SpinLock<Option<NetDeviceError>>,
rx_frames: SpinLock<VecDeque<Vec<u8>>>,
blocked: SpinLock<bool>,
}
struct RecordingFramePort {
probe: Arc<TxProbe>,
checksum_capabilities: TxChecksumCapabilities,
}
impl EthernetFramePort for RecordingFramePort {
fn device_name(&self) -> &str {
"recording"
}
fn mac_address(&self) -> [u8; 6] {
DEV_MAC
}
fn checksum_capabilities(&self) -> TxChecksumCapabilities {
self.checksum_capabilities
}
fn transmit(&mut self, frame: &ProtocolEthernetFrame) -> NetDeviceResult {
if *self.probe.blocked.lock_irqsave() {
return Err(NetDeviceError::Again);
}
if let Some(error) = self.probe.failure.lock_irqsave().take() {
return Err(error);
}
self.probe
.requests
.lock_irqsave()
.push((frame.packet().to_vec(), TxSubmitOptions::default()));
Ok(())
}
fn transmit_frame_with_options(
&mut self,
frame_len: usize,
options: TxSubmitOptions,
fill: &mut dyn FnMut(&mut [u8]),
) -> NetDeviceResult {
if *self.probe.blocked.lock_irqsave() {
return Err(NetDeviceError::Again);
}
if let Some(error) = self.probe.failure.lock_irqsave().take() {
return Err(error);
}
let mut frame = vec![0u8; frame_len];
fill(&mut frame);
self.probe.requests.lock_irqsave().push((frame, options));
Ok(())
}
fn receive(&mut self) -> NetDeviceResult<ProtocolEthernetFrame> {
self.probe
.rx_frames
.lock_irqsave()
.pop_front()
.map(|packet| ProtocolEthernetFrame::copy_from_slice(&packet).unwrap())
.ok_or(NetDeviceError::Again)
}
}
const DEV_MAC: [u8; 6] = [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF];
const REMOTE_MAC: [u8; 6] = [0x02, 0x00, 0x00, 0x00, 0x00, 0x01];
const DEV_IP: Ipv4Address = Ipv4Address::new(10, 0, 0, 2);
const REMOTE_IP: Ipv4Address = Ipv4Address::new(10, 0, 0, 1);
fn device_ip_cidr() -> Ipv4Cidr {
Ipv4Cidr::new(DEV_IP, 24)
}
fn make_test_device(mock: MockEthernetDriver) -> EthernetDevice {
EthernetDevice::new("mock0".into(), Box::new(mock), Some(device_ip_cidr()))
}
fn make_recording_device(
checksum_capabilities: TxChecksumCapabilities,
) -> (EthernetDevice, Arc<TxProbe>) {
let probe = Arc::new(TxProbe::default());
let port = RecordingFramePort {
probe: Arc::clone(&probe),
checksum_capabilities,
};
(
EthernetDevice::new("recording0".into(), Box::new(port), Some(device_ip_cidr())),
probe,
)
}
fn raw_tcp_packet() -> Vec<u8> {
let mut packet = vec![0u8; 60];
let packet_len = packet.len() as u16;
packet[0] = 0x45;
packet[2..4].copy_from_slice(&packet_len.to_be_bytes());
packet[8] = 64;
packet[9] = 6;
packet[12..16].copy_from_slice(&DEV_IP.octets());
packet[16..20].copy_from_slice(&REMOTE_IP.octets());
packet[20..22].copy_from_slice(&41000u16.to_be_bytes());
packet[22..24].copy_from_slice(&5201u16.to_be_bytes());
packet[32] = 5 << 4;
packet
}
fn enqueue_pending_packet(device: &mut EthernetDevice, packet: &[u8]) {
device
.pending_packets
.enqueue(packet.len(), IpAddress::Ipv4(REMOTE_IP))
.expect("the empty pending queue has capacity")
.copy_from_slice(packet);
}
fn process_remote_arp_reply(device: &mut EthernetDevice, timestamp: Instant) {
let reply = build_arp_frame(
ArpOperation::Reply,
REMOTE_MAC,
DEV_MAC,
REMOTE_IP,
DEV_IP,
DEV_MAC,
);
device.process_arp(&reply[EthernetFrame::<&[u8]>::header_len()..], timestamp);
}
fn build_arp_frame(
operation: ArpOperation,
src_mac: [u8; 6],
dst_mac: [u8; 6],
src_ip: Ipv4Address,
dst_ip: Ipv4Address,
target_mac: [u8; 6],
) -> Vec<u8> {
let arp_repr = ArpRepr::EthernetIpv4 {
operation,
source_hardware_addr: EthernetAddress(src_mac),
source_protocol_addr: src_ip,
target_hardware_addr: EthernetAddress(target_mac),
target_protocol_addr: dst_ip,
};
let eth_repr = EthernetRepr {
src_addr: EthernetAddress(src_mac),
dst_addr: EthernetAddress(dst_mac),
ethertype: EthernetProtocol::Arp,
};
let total_len = eth_repr.buffer_len() + arp_repr.buffer_len();
let mut buf = alloc::vec![0u8; total_len];
let mut frame = EthernetFrame::new_unchecked(&mut buf);
eth_repr.emit(&mut frame);
arp_repr.emit(&mut ArpPacket::new_unchecked(frame.payload_mut()));
buf
}
fn test_packet_buffer() -> PacketBuffer<'static, InterfaceId> {
PacketBuffer::new(vec![PacketMetadata::EMPTY; 4], vec![0u8; STANDARD_MTU * 4])
}
#[test]
fn arp_request_rx_is_counted_in_drain_deferred_rx() {
let mut mock = MockEthernetDriver::new(DEV_MAC);
let arp_frame = build_arp_frame(
ArpOperation::Request,
REMOTE_MAC,
DEV_MAC,
REMOTE_IP,
DEV_IP,
EMPTY_MAC.0,
);
let frame_len = arp_frame.len();
mock.enqueue_rx_frame(arp_frame);
let mut device = make_test_device(mock);
let mut buffer = test_packet_buffer();
let ts = Instant::from_millis(0);
let result = device.recv(InterfaceId::new(1), &mut buffer, ts, &mut |_| {});
assert_eq!(result, 0);
let rx_lens = device.drain_deferred_rx();
assert_eq!(rx_lens, &[frame_len]);
assert!(device.drain_deferred_rx().is_empty());
}
#[test]
fn arp_reply_rx_is_counted_in_drain_deferred_rx() {
let ts = Instant::from_millis(0);
let mut mock = MockEthernetDriver::new(DEV_MAC);
let arp_reply = build_arp_frame(
ArpOperation::Reply,
REMOTE_MAC,
DEV_MAC,
REMOTE_IP,
DEV_IP,
DEV_MAC,
);
let frame_len = arp_reply.len();
mock.enqueue_rx_frame(arp_reply);
let mut device = make_test_device(mock);
device.pending_neighbors.insert(
IpAddress::Ipv4(REMOTE_IP),
PendingNeighbor { requested_at: ts },
);
let mut buffer = test_packet_buffer();
let result = device.recv(InterfaceId::new(1), &mut buffer, ts, &mut |_| {});
assert_eq!(result, 0);
let rx_lens = device.drain_deferred_rx();
assert_eq!(rx_lens, &[frame_len]);
}
#[test]
fn arp_request_tx_is_counted_in_drain_deferred_tx() {
let mock = MockEthernetDriver::new(DEV_MAC);
let mut device = make_test_device(mock);
let ts = Instant::from_millis(0);
let result = device.send(IpAddress::Ipv4(REMOTE_IP), &[0u8; 64], ts);
assert_eq!(result, 0);
let tx_lens = device.drain_deferred_tx();
assert_eq!(tx_lens.len(), 1);
assert_eq!(tx_lens[0], 60);
}
#[test]
fn arp_reply_tx_is_counted_in_drain_deferred_tx() {
let mut mock = MockEthernetDriver::new(DEV_MAC);
let arp_request = build_arp_frame(
ArpOperation::Request,
REMOTE_MAC,
DEV_MAC,
REMOTE_IP,
DEV_IP,
EMPTY_MAC.0,
);
mock.enqueue_rx_frame(arp_request);
let mut device = make_test_device(mock);
let mut buffer = test_packet_buffer();
let ts = Instant::from_millis(0);
let result = device.recv(InterfaceId::new(1), &mut buffer, ts, &mut |_| {});
assert_eq!(result, 0);
let rx_lens = device.drain_deferred_rx();
assert_eq!(rx_lens.len(), 1);
let tx_lens = device.drain_deferred_tx();
assert_eq!(tx_lens.len(), 1); assert_eq!(tx_lens[0], 60);
}
#[test]
fn arp_reply_survives_tx_backpressure_and_precedes_bulk_ip() {
let (mut device, probe) = make_recording_device(TxChecksumCapabilities::NONE);
probe.rx_frames.lock_irqsave().push_back(build_arp_frame(
ArpOperation::Request,
REMOTE_MAC,
DEV_MAC,
REMOTE_IP,
DEV_IP,
EMPTY_MAC.0,
));
*probe.failure.lock_irqsave() = Some(NetDeviceError::Again);
device.recv(
InterfaceId::new(1),
&mut test_packet_buffer(),
Instant::ZERO,
&mut |_| {},
);
let packet = raw_tcp_packet();
device
.try_send(IpAddress::Ipv4(REMOTE_IP), &packet, Instant::ZERO)
.unwrap();
let requests = probe.requests.lock_irqsave();
assert_eq!(requests.len(), 2, "queue pressure lost the ARP reply");
let reply = EthernetFrame::new_checked(&requests[0].0).unwrap();
assert_eq!(reply.ethertype(), EthernetProtocol::Arp);
assert_eq!(reply.dst_addr(), EthernetAddress(REMOTE_MAC));
assert_eq!(
ArpRepr::parse(&ArpPacket::new_checked(reply.payload()).unwrap()).unwrap(),
ArpRepr::EthernetIpv4 {
operation: ArpOperation::Reply,
source_hardware_addr: EthernetAddress(DEV_MAC),
source_protocol_addr: DEV_IP,
target_hardware_addr: EthernetAddress(REMOTE_MAC),
target_protocol_addr: REMOTE_IP,
}
);
assert_eq!(requests[0].1.notify, TxNotify::Immediate);
assert_eq!(&requests[1].0[14..], &packet);
drop(requests);
assert_eq!(device.drain_deferred_tx(), vec![ETH_ZLEN]);
assert_eq!(device.drain_deferred_tx_drops(), 0);
}
#[test]
fn arp_reply_retries_on_tx_completion_poll_without_new_rx() {
for receive_path in 0..3 {
let (mut device, probe) = make_recording_device(TxChecksumCapabilities::NONE);
*probe.blocked.lock_irqsave() = true;
for _ in 0..3 {
probe.rx_frames.lock_irqsave().push_back(build_arp_frame(
ArpOperation::Request,
REMOTE_MAC,
DEV_MAC,
REMOTE_IP,
DEV_IP,
EMPTY_MAC.0,
));
}
let mut buffer = test_packet_buffer();
device.recv(InterfaceId::new(1), &mut buffer, Instant::ZERO, &mut |_| {});
*probe.blocked.lock_irqsave() = false;
match receive_path {
0 => {
device.recv(InterfaceId::new(1), &mut buffer, Instant::ZERO, &mut |_| {});
}
1 => {
let _ = device.poll_owned_rx(Instant::ZERO);
}
_ => {
let _ = device.recv_direct(Instant::ZERO, &mut |_| false, &mut |_| {});
}
}
let requests = probe.requests.lock_irqsave();
assert_eq!(
requests.len(),
1,
"poll path {receive_path} lost or duplicated the reply"
);
let frame = EthernetFrame::new_checked(&requests[0].0).unwrap();
assert_eq!(frame.ethertype(), EthernetProtocol::Arp);
assert_eq!(frame.dst_addr(), EthernetAddress(REMOTE_MAC));
drop(requests);
assert_eq!(device.drain_deferred_tx(), vec![ETH_ZLEN]);
assert_eq!(device.drain_deferred_tx_drops(), 0);
}
}
#[test]
fn pending_arp_replies_are_bounded_and_cancelled_on_address_change() {
let (mut device, probe) = make_recording_device(TxChecksumCapabilities::NONE);
*probe.blocked.lock_irqsave() = true;
for index in 0..=ETHERNET_MAX_PENDING_PACKETS {
probe.rx_frames.lock_irqsave().push_back(build_arp_frame(
ArpOperation::Request,
REMOTE_MAC,
DEV_MAC,
Ipv4Address::from((0x0a00_0101 + index as u32).to_be_bytes()),
DEV_IP,
EMPTY_MAC.0,
));
}
device.recv(
InterfaceId::new(1),
&mut test_packet_buffer(),
Instant::ZERO,
&mut |_| {},
);
assert_eq!(device.drain_deferred_tx_drops(), 1);
device.set_ipv4_addr(None);
*probe.blocked.lock_irqsave() = false;
let _ = device.poll_owned_rx(Instant::ZERO);
assert!(
probe.requests.lock_irqsave().is_empty(),
"sent replies for a removed address"
);
assert_eq!(
device.drain_deferred_tx_drops(),
ETHERNET_MAX_PENDING_PACKETS as u64
);
}
#[test]
fn pending_raw_tcp_packet_preserves_checksum_after_arp_resolution() {
let (mut device, probe) = make_recording_device(TxChecksumCapabilities::TCP_UDP);
let packet = raw_tcp_packet();
enqueue_pending_packet(&mut device, &packet);
process_remote_arp_reply(&mut device, Instant::from_millis(0));
let requests = probe.requests.lock_irqsave();
assert_eq!(requests.len(), 1);
assert_eq!(
&requests[0].0[EthernetFrame::<&[u8]>::header_len()..],
&packet
);
assert_eq!(requests[0].1.notify, TxNotify::Deferred);
assert_eq!(requests[0].1.checksum, None);
}
#[test]
fn ethernet_preserves_raw_udp_checksum_without_requesting_offload() {
let (mut device, probe) = make_recording_device(TxChecksumCapabilities::TCP_UDP);
for len in [32usize, 60] {
for checksum in [0u16, 0x1234] {
let mut packet = vec![0u8; len];
packet[0] = 0x45;
packet[2..4].copy_from_slice(&(len as u16).to_be_bytes());
packet[8] = 64;
packet[9] = 17;
packet[12..16].copy_from_slice(&DEV_IP.octets());
packet[16..20].copy_from_slice(&REMOTE_IP.octets());
packet[24..26].copy_from_slice(&((len - 20) as u16).to_be_bytes());
packet[26..28].copy_from_slice(&checksum.to_be_bytes());
device
.transmit_ip_to(EthernetAddress(REMOTE_MAC), &packet)
.unwrap();
let requests = probe.requests.lock_irqsave();
let (frame, options) = requests.last().unwrap();
assert_eq!(
&frame[14..14 + len],
&packet,
"Ethernet rewrote raw UDP transport bytes"
);
assert_eq!(
options.checksum, None,
"zero checksum is not an offload request"
);
}
}
}
#[test]
fn pending_packet_survives_arp_resolution_tx_backpressure() {
let (mut device, probe) = make_recording_device(TxChecksumCapabilities::TCP_UDP);
let packet = raw_tcp_packet();
enqueue_pending_packet(&mut device, &packet);
*probe.failure.lock_irqsave() = Some(NetDeviceError::Again);
process_remote_arp_reply(&mut device, Instant::from_millis(0));
let (&next_hop, queued) = device
.pending_packets
.peek()
.expect("transient queue pressure must retain the pending packet");
assert_eq!(next_hop, IpAddress::Ipv4(REMOTE_IP));
assert_eq!(queued, packet);
assert_eq!(device.drain_deferred_tx_errors(), 0);
assert_eq!(device.drain_deferred_tx_drops(), 0);
}
#[test]
fn multicast_egress_uses_the_ip_version_specific_destination_mac() {
use smoltcp::wire::Ipv6Address;
let destinations = [
(
IpAddress::Ipv4(Ipv4Address::new(224, 0, 0, 1)),
[1, 0, 0x5e, 0, 0, 1],
),
(
IpAddress::Ipv4(Ipv4Address::new(239, 255, 18, 52)),
[1, 0, 0x5e, 0x7f, 18, 52],
),
(
IpAddress::Ipv6(Ipv6Address::new(0xff02, 0, 0, 0, 0, 1, 0xff12, 0x3456)),
[0x33, 0x33, 0xff, 0x12, 0x34, 0x56],
),
(IpAddress::Ipv4(Ipv4Address::BROADCAST), [0xff; 6]),
(IpAddress::Ipv4(Ipv4Address::new(10, 0, 0, 255)), [0xff; 6]),
];
for (destination, expected_mac) in destinations {
let (mut device, probe) = make_recording_device(TxChecksumCapabilities::TCP_UDP);
let packet = match destination {
IpAddress::Ipv4(address) => {
let mut packet = raw_tcp_packet();
packet[16..20].copy_from_slice(&address.octets());
packet
}
IpAddress::Ipv6(address) => {
let mut packet = vec![0u8; 40];
packet[0] = 0x60;
packet[24..40].copy_from_slice(&address.octets());
packet
}
};
*probe.failure.lock_irqsave() = Some(NetDeviceError::Again);
assert_eq!(
device.try_send(destination, &packet, Instant::ZERO),
Err(NetDeviceError::Again)
);
assert!(probe.requests.lock_irqsave().is_empty());
assert!(device.pending_packets.is_empty());
assert!(device.pending_neighbors.is_empty());
assert!(
device
.try_send(destination, &packet, Instant::ZERO)
.unwrap()
> 0
);
let requests = probe.requests.lock_irqsave();
assert_eq!(requests.len(), 1);
let frame = EthernetFrame::new_checked(&requests[0].0).unwrap();
assert_eq!(
frame.dst_addr(),
EthernetAddress(expected_mac),
"destination {destination}"
);
assert_eq!(&frame.payload()[..packet.len()], packet);
assert_eq!(requests[0].1.checksum, None);
assert_eq!(device.drain_deferred_tx_errors(), 0);
assert_eq!(device.drain_deferred_tx_drops(), 0);
}
}
#[test]
fn arp_request_backpressure_is_returned_to_the_router() {
let (mut device, probe) = make_recording_device(TxChecksumCapabilities::TCP_UDP);
let packet = raw_tcp_packet();
*probe.failure.lock_irqsave() = Some(NetDeviceError::Again);
let result = device.try_send(IpAddress::Ipv4(REMOTE_IP), &packet, Instant::from_millis(0));
assert_eq!(result, Err(NetDeviceError::Again));
assert!(device.pending_packets.is_empty());
assert_eq!(device.drain_deferred_tx_errors(), 0);
assert_eq!(device.drain_deferred_tx_drops(), 0);
}
#[test]
fn consecutive_arp_frames_accumulate_in_drain_deferred_rx() {
let mut mock = MockEthernetDriver::new(DEV_MAC);
let frame1 = build_arp_frame(
ArpOperation::Request,
REMOTE_MAC,
DEV_MAC,
REMOTE_IP,
DEV_IP,
EMPTY_MAC.0,
);
let frame2 = build_arp_frame(
ArpOperation::Request,
REMOTE_MAC,
DEV_MAC,
Ipv4Address::new(10, 0, 0, 3),
DEV_IP,
EMPTY_MAC.0,
);
let len1 = frame1.len();
let len2 = frame2.len();
mock.enqueue_rx_frame(frame1);
mock.enqueue_rx_frame(frame2);
let mut device = make_test_device(mock);
let mut buffer = test_packet_buffer();
let ts = Instant::from_millis(0);
let result = device.recv(InterfaceId::new(1), &mut buffer, ts, &mut |_| {});
assert_eq!(result, 0);
let rx_lens = device.drain_deferred_rx();
assert_eq!(rx_lens, &[len1, len2]);
assert!(device.drain_deferred_rx().is_empty());
}
#[test]
fn set_ipv4_addr_preserves_undrained_frame_lens() {
let mock = MockEthernetDriver::new(DEV_MAC);
let mut device = make_test_device(mock);
let ts = Instant::from_millis(0);
let result = device.send(IpAddress::Ipv4(REMOTE_IP), &[0u8; 64], ts);
assert_eq!(result, 0);
let tx_lens_before = device.drain_deferred_tx();
assert_eq!(tx_lens_before.len(), 1);
assert_eq!(tx_lens_before[0], 60);
let result = device.send(
IpAddress::Ipv4(Ipv4Address::new(10, 0, 0, 99)),
&[0u8; 64],
ts,
);
assert_eq!(result, 0);
assert_eq!(device.deferred_tx_frame_lens.len(), 1);
device.set_ipv4_addr(Some(Ipv4Cidr::new(Ipv4Address::new(10, 0, 0, 99), 24)));
let tx_lens_after = device.drain_deferred_tx();
assert_eq!(tx_lens_after.len(), 1);
assert_eq!(tx_lens_after[0], 60);
}
#[test]
fn unknown_ethertype_frame_is_counted_in_drain_deferred_rx() {
let mut mock = MockEthernetDriver::new(DEV_MAC);
let eth_repr = EthernetRepr {
src_addr: EthernetAddress(REMOTE_MAC),
dst_addr: EthernetAddress(DEV_MAC),
ethertype: EthernetProtocol::Unknown(0x8100),
};
let payload = [0xAAu8; 46]; let mut frame_buf = alloc::vec![0u8; eth_repr.buffer_len() + payload.len()];
let mut frame = EthernetFrame::new_unchecked(&mut frame_buf);
eth_repr.emit(&mut frame);
frame.payload_mut().copy_from_slice(&payload);
let frame_len = frame_buf.len();
mock.enqueue_rx_frame(frame_buf);
let mut device = make_test_device(mock);
let mut buffer = test_packet_buffer();
let ts = Instant::from_millis(0);
let result = device.recv(InterfaceId::new(1), &mut buffer, ts, &mut |_| {});
assert_eq!(result, 0);
let rx_lens = device.drain_deferred_rx();
assert_eq!(rx_lens, &[frame_len]);
let rx_drops = device.drain_deferred_rx_drops();
assert_eq!(rx_drops, 1);
}
#[test]
fn send_to_wire_len_respects_eth_zlen_padding() {
let dst = EthernetAddress(REMOTE_MAC);
let mut mock = MockEthernetDriver::new(DEV_MAC);
let wire_len =
EthernetDevice::send_to(&mut mock, dst, 0, |_buf| {}, EthernetProtocol::Ipv4);
assert_eq!(wire_len, Ok(60));
let mut mock = MockEthernetDriver::new(DEV_MAC);
let wire_len = EthernetDevice::send_to(
&mut mock,
dst,
46,
|buf| buf.copy_from_slice(&[0xAAu8; 46]),
EthernetProtocol::Ipv4,
);
assert_eq!(wire_len, Ok(60));
let mut mock = MockEthernetDriver::new(DEV_MAC);
let wire_len = EthernetDevice::send_to(
&mut mock,
dst,
100,
|buf| buf.copy_from_slice(&[0xAAu8; 100]),
EthernetProtocol::Ipv4,
);
assert_eq!(wire_len, Ok(114));
}
#[test]
fn combined_arp_ip_recv_drain_cycle() {
let mut mock = MockEthernetDriver::new(DEV_MAC);
let arp_req = build_arp_frame(
ArpOperation::Request,
REMOTE_MAC,
DEV_MAC,
REMOTE_IP,
DEV_IP,
DEV_MAC,
);
mock.enqueue_rx_frame(arp_req);
let eth = EthernetRepr {
src_addr: EthernetAddress(REMOTE_MAC),
dst_addr: EthernetAddress(DEV_MAC),
ethertype: EthernetProtocol::Ipv4,
};
let ip_payload = [0x11u8; 64];
let mut ip_frame = alloc::vec![0u8; eth.buffer_len() + ip_payload.len()];
let mut frame = EthernetFrame::new_unchecked(&mut ip_frame);
eth.emit(&mut frame);
frame.payload_mut().copy_from_slice(&ip_payload);
let expected_ip_frame_len = ip_frame.len();
mock.enqueue_rx_frame(ip_frame);
let mut device = make_test_device(mock);
let mut buffer = test_packet_buffer();
let iface = InterfaceId::new(1);
let frame_len = device.recv(iface, &mut buffer, Instant::from_millis(0), &mut |_| {});
assert_eq!(frame_len, expected_ip_frame_len);
let rx_lens = device.drain_deferred_rx();
assert_eq!(rx_lens.len(), 1);
assert_eq!(rx_lens[0], 42);
let tx_lens = device.drain_deferred_tx();
assert_eq!(tx_lens.len(), 1);
assert_eq!(tx_lens[0], 60);
assert!(device.drain_deferred_rx().is_empty());
assert!(device.drain_deferred_tx().is_empty());
}
#[test]
fn malformed_ethernet_frame_counts_rx_errors() {
let mut mock = MockEthernetDriver::new(DEV_MAC);
mock.enqueue_rx_frame(alloc::vec![0xFF]); let mut device = make_test_device(mock);
let mut buffer = test_packet_buffer();
let ts = Instant::from_millis(0);
let result = device.recv(InterfaceId::new(1), &mut buffer, ts, &mut |_| {});
assert_eq!(result, 0);
assert_eq!(device.drain_deferred_rx_errors(), 1);
assert_eq!(device.drain_deferred_rx_errors(), 0);
}
#[test]
fn malformed_arp_payload_counts_rx_errors() {
let mut mock = MockEthernetDriver::new(DEV_MAC);
let eth = EthernetRepr {
src_addr: EthernetAddress(REMOTE_MAC),
dst_addr: EthernetAddress(DEV_MAC),
ethertype: EthernetProtocol::Arp,
};
let mut frame = alloc::vec![0u8; eth.buffer_len() + 16];
let mut eth_frame = EthernetFrame::new_unchecked(&mut frame);
eth.emit(&mut eth_frame);
eth_frame.payload_mut()[..16].fill(0xFF);
mock.enqueue_rx_frame(frame);
let mut device = make_test_device(mock);
let mut buffer = test_packet_buffer();
let ts = Instant::from_millis(0);
let result = device.recv(InterfaceId::new(1), &mut buffer, ts, &mut |_| {});
assert_eq!(result, 0);
assert_eq!(device.drain_deferred_rx_errors(), 1);
assert!(!device.drain_deferred_rx().is_empty());
}
#[test]
fn pending_buffer_full_counts_tx_drops() {
let mock = MockEthernetDriver::new(DEV_MAC);
let mut device = make_test_device(mock);
let ts = Instant::from_millis(0);
let base = Ipv4Address::new(10, 0, 0, 100);
for i in 0..crate::consts::ETHERNET_MAX_PENDING_PACKETS {
let ip = IpAddress::Ipv4(Ipv4Address::from(u32::from(base) + i as u32));
let result = device.send(ip, &[0u8; 64], ts);
assert_eq!(result, 0, "packet {i} should be queued, not dropped");
let _ = device.drain_deferred_tx();
}
let extra_ip = IpAddress::Ipv4(Ipv4Address::new(10, 0, 1, 1));
let result = device.send(extra_ip, &[0u8; 64], ts);
assert_eq!(result, 0);
assert_eq!(device.drain_deferred_tx_drops(), 1);
assert_eq!(device.drain_deferred_tx_drops(), 0);
}
#[test]
fn compatibility_send_counts_transient_backpressure_as_a_drop() {
let mut mock = MockEthernetDriver::new(DEV_MAC);
mock.tx_alloc_fail = true;
let mut device = make_test_device(mock);
let ts = Instant::from_millis(0);
let broadcast = IpAddress::Ipv4(Ipv4Address::BROADCAST);
let packet = raw_tcp_packet();
let result = device.send(broadcast, &packet, ts);
assert_eq!(result, 0);
assert_eq!(device.drain_deferred_tx_errors(), 0);
assert_eq!(device.drain_deferred_tx_drops(), 1);
assert_eq!(device.drain_deferred_tx_drops(), 0);
let tx_lens = device.drain_deferred_tx();
assert!(tx_lens.is_empty());
}
}