Skip to main content

ax_net/device/
mod.rs

1//! Logical network device abstraction.
2//!
3//! Device implementations hide physical transport details from the single
4//! protocol core. The router polls devices through this trait, while concrete
5//! adapters such as Ethernet and loopback decide how packets enter or leave the
6//! underlying hardware or virtual link.
7//!
8//! # Contract
9//!
10//! `recv()` moves complete IP packets into the caller-provided packet buffer;
11//! `send()` accepts complete IP packets plus the already selected next hop.
12//! Devices should not perform socket lookup, TCP/UDP processing, or route
13//! selection. Those belong above this trait in `service` and `router`.
14//!
15//! # Readiness
16//!
17//! Physical devices enter the router only through the IRQ-backed queue runtime;
18//! periodic polling and out-of-band wake fallbacks are not supported. The
19//! in-memory loopback device has no hardware readiness source. The router asks
20//! devices for protocol-side readiness and performs `PollSet` register/wake
21//! operations after releasing the concrete device lock.
22
23use alloc::{string::String, vec::Vec};
24use core::ops::Range;
25
26use smoltcp::{
27    storage::PacketBuffer,
28    time::Instant,
29    wire::{IpAddress, Ipv4Cidr},
30};
31
32use crate::config::InterfaceId;
33
34mod driver;
35mod ethernet;
36mod loopback;
37#[cfg(feature = "vsock")]
38mod vsock;
39
40pub use driver::*;
41pub use ethernet::*;
42pub use loopback::*;
43#[cfg(feature = "vsock")]
44pub use vsock::*;
45
46/// Owned IP packet whose backing RX DMA token is retained through consumption.
47pub(crate) struct DeviceRxPacket {
48    frame_len: usize,
49    frame: ProtocolRxFrame,
50    packet: Range<usize>,
51}
52
53impl DeviceRxPacket {
54    pub(crate) fn with_packet_range(
55        frame_len: usize,
56        frame: ProtocolRxFrame,
57        packet: Range<usize>,
58    ) -> Self {
59        assert!(packet.end <= frame.packet_len());
60        Self {
61            frame_len,
62            frame,
63            packet,
64        }
65    }
66
67    /// Borrows the IP packet without releasing the RX DMA token.
68    pub fn read_with<R>(&self, consume: impl FnOnce(&[u8]) -> R) -> R {
69        self.frame
70            .read_with(|frame| consume(&frame[self.packet.clone()]))
71    }
72
73    /// Consumes the IP packet and recycles its DMA token afterwards.
74    pub fn consume<R>(self, consume: impl FnOnce(&[u8]) -> R) -> R {
75        self.read_with(consume)
76    }
77
78    /// Returns the received L2 frame length excluding FCS.
79    pub const fn frame_len(&self) -> usize {
80        self.frame_len
81    }
82}
83
84/// Result of polling a device's optional owned receive path.
85pub(crate) enum DeviceRxPoll {
86    /// This device only implements the compatibility receive path.
87    Unsupported,
88    /// The owned receive path is supported but no IP packet is ready.
89    Idle,
90    /// One IP packet and its backing DMA token were received.
91    Packet(DeviceRxPacket),
92}
93
94#[derive(Clone, Debug, Eq, PartialEq)]
95pub struct ArpEntry {
96    /// IPv4 address in network byte order.
97    pub ip_addr: [u8; 4],
98    /// ARP hardware type.
99    pub hw_type: u16,
100    /// ARP entry flags exposed to userspace.
101    pub flags: u16,
102    /// Link-layer address.
103    pub hw_addr: [u8; 6],
104    /// Interface name that owns this neighbor entry.
105    pub device: String,
106}
107
108/// Packet I/O endpoint behind the multi-device router.
109pub(crate) trait Device: Send {
110    /// Human-readable device name used in logs and userspace queries.
111    fn name(&self) -> &str;
112
113    /// Moves packets from the device into the shared IP RX buffer.
114    ///
115    /// Returns the L2 frame byte count (excluding FCS) of the delivered IP
116    /// packet, or 0 when no IP packet was enqueued. ARP and other non-IP
117    /// frames are processed internally and do not produce a return value.
118    ///
119    /// The returned byte count aligns with Linux `/proc/net/dev` semantics
120    /// (Ethernet frame without trailing FCS).
121    ///
122    /// # Contract
123    ///
124    /// Each call that returns a non-zero value MUST have enqueued exactly one
125    /// IP packet into `buffer`. The return value is the L2 frame length of
126    /// that specific packet. The protocol executor relies on this 1:1
127    /// correspondence to pair frame lengths with dequeued packets in FIFO
128    /// order.
129    fn recv(
130        &mut self,
131        interface_id: InterfaceId,
132        buffer: &mut PacketBuffer<InterfaceId>,
133        timestamp: Instant,
134        snoop: &mut dyn FnMut(&[u8]),
135    ) -> usize;
136
137    /// Polls an optional owned receive path that retains DMA through `RxToken`.
138    fn poll_owned_rx(&mut self, _timestamp: Instant) -> DeviceRxPoll {
139        DeviceRxPoll::Unsupported
140    }
141
142    /// Receives directly from queue-owned backing into the final protocol
143    /// destination when supported.
144    ///
145    /// `None` selects the compatibility [`recv`](Self::recv) path. `Some(0)`
146    /// means the direct path is supported but no IP packet was delivered.
147    fn recv_direct(
148        &mut self,
149        _timestamp: Instant,
150        _deliver: &mut dyn FnMut(&[u8]) -> bool,
151        _snoop: &mut dyn FnMut(&[u8]),
152    ) -> Option<usize> {
153        None
154    }
155    /// Sends a packet to the next hop.
156    ///
157    /// Returns the L2 frame byte count (excluding FCS) actually transmitted,
158    /// or 0 if the packet was queued for later transmission (e.g. pending ARP
159    /// resolution) or could not be sent. The returned byte count aligns with
160    /// Linux `/proc/net/dev` semantics.
161    fn send(&mut self, next_hop: IpAddress, packet: &[u8], timestamp: Instant) -> usize;
162
163    /// Attempts a transmission while preserving transient queue backpressure.
164    ///
165    /// [`NetDeviceError::Again`] means the caller still owns the packet and
166    /// must leave it queued until a later protocol poll.
167    fn try_send(
168        &mut self,
169        next_hop: IpAddress,
170        packet: &[u8],
171        timestamp: Instant,
172    ) -> NetDeviceResult<usize> {
173        Ok(self.send(next_hop, packet, timestamp))
174    }
175
176    /// Returns the per-packet L2 frame byte counts for packets transmitted
177    /// on a side path during `recv()` (e.g. ARP resolution and replies)
178    /// since the last call. The internal accumulator is cleared on each call.
179    ///
180    /// Each element is the L2 frame byte count of one packet. An empty Vec
181    /// means no deferred transmissions occurred.
182    fn drain_deferred_tx(&mut self) -> Vec<usize> {
183        Vec::new()
184    }
185
186    /// Returns the per-packet L2 frame byte counts for non-IP frames
187    /// received during `recv()` (e.g. ARP requests and replies) since the
188    /// last call. The internal accumulator is cleared on each call.
189    ///
190    /// These frames were successfully received and processed at L2, but
191    /// were not enqueued into the IP buffer. Each element is the L2 frame
192    /// byte count of one received frame. An empty Vec means no non-IP
193    /// frames were received.
194    fn drain_deferred_rx(&mut self) -> Vec<usize> {
195        Vec::new()
196    }
197
198    /// Returns the count of TX errors accumulated during device operations
199    /// (e.g. buffer allocation failures, transmit hardware errors) since
200    /// the last call. The internal accumulator is cleared on each call.
201    fn drain_deferred_tx_errors(&mut self) -> u64 {
202        0
203    }
204
205    /// Returns the count of TX drops accumulated during device operations
206    /// (e.g. pending buffer full) since the last call.
207    /// The internal accumulator is cleared on each call.
208    ///
209    /// Distinct from `drain_deferred_tx_errors`: tx_errors counts hardware/
210    /// driver-level transmission failures and protocol errors; tx_drops counts
211    /// packets that were intentionally discarded due to resource constraints
212    /// (buffer exhaustion, queue overflow).
213    fn drain_deferred_tx_drops(&mut self) -> u64 {
214        0
215    }
216
217    /// Returns the count of RX errors accumulated during device operations
218    /// (e.g. driver receive errors, malformed frames) since the last call.
219    /// The internal accumulator is cleared on each call.
220    fn drain_deferred_rx_errors(&mut self) -> u64 {
221        0
222    }
223
224    /// Returns the count of RX drops accumulated during device operations
225    /// (e.g. frames with unsupported EtherType that were successfully
226    /// received at L2 but cannot be processed by the stack) since the last
227    /// call. The internal accumulator is cleared on each call.
228    fn drain_deferred_rx_drops(&mut self) -> u64 {
229        0
230    }
231
232    /// Updates the IPv4 address used by device-local protocol helpers.
233    fn set_ipv4_addr(&mut self, _addr: Option<Ipv4Cidr>) {}
234
235    /// Returns device-local ARP/neighbor entries for userspace queries.
236    fn arp_entries(&self, _timestamp: Instant) -> Vec<ArpEntry> {
237        Vec::new()
238    }
239}