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}