Skip to main content

arcbox_virtio_net/
backend.rs

1//! `NetBackend` trait + the cross-platform `LoopbackBackend`.
2
3use std::collections::VecDeque;
4
5use crate::header::NetPacket;
6
7/// Which offload features the guest has negotiated — passed to the backend
8/// after feature acknowledgement so it can configure the host side to match.
9///
10/// For a TAP-based backend this is translated into the kernel's `TUN_F_*`
11/// bitmask; for userspace backends it's typically a no-op. We keep the
12/// shape abstract rather than leaking Linux constants into the trait.
13#[derive(Debug, Clone, Copy, Default)]
14pub struct NetOffloadFlags {
15    /// Guest can receive partial checksums — host may stamp
16    /// `CSUM_PARTIAL`-style frames.
17    pub csum: bool,
18    /// Guest accepts TCPv4 segmentation offload.
19    pub tso4: bool,
20    /// Guest accepts TCPv6 segmentation offload.
21    pub tso6: bool,
22    /// Guest accepts TSO with ECN.
23    pub tso_ecn: bool,
24    /// Guest accepts UDP fragmentation offload.
25    pub ufo: bool,
26}
27
28/// Network backend trait.
29pub trait NetBackend: Send + Sync {
30    /// Sends a packet.
31    fn send(&mut self, packet: &NetPacket) -> std::io::Result<usize>;
32
33    /// Sends a TSO/GSO packet.
34    ///
35    /// Called when the guest emits a packet with `gso_type != GSO_NONE`.
36    /// The packet contains a large payload that the guest expects the device
37    /// to segment (or relay as-is if the host stack handles it).
38    ///
39    /// The default implementation ignores the GSO header and forwards the
40    /// packet via `send()`. Backends that can exploit TSO (e.g. writing
41    /// directly to a host `TcpStream`) should override this.
42    fn send_tso(&mut self, packet: &NetPacket) -> std::io::Result<usize> {
43        self.send(packet)
44    }
45
46    /// Receives a packet.
47    fn recv(&mut self, buf: &mut [u8]) -> std::io::Result<usize>;
48
49    /// Returns whether packets are available to receive.
50    fn has_data(&self) -> bool;
51
52    /// Returns whether this backend supports TSO offload.
53    ///
54    /// When true, the device advertises `GUEST_TSO4/6` and `HOST_TSO4/6`
55    /// features to the guest, and routes TSO packets through `send_tso()`.
56    fn supports_tso(&self) -> bool {
57        false
58    }
59
60    /// Configure host-side offload to match the features the guest negotiated.
61    ///
62    /// Call from the device's `activate()` hook after `ack_features`. The
63    /// flags come from the guest's acknowledged `GUEST_*` feature bits — the
64    /// backend is free to translate (e.g. TAP translates to `TUN_F_*`) or
65    /// ignore. Default is no-op so non-kernel backends don't need to care.
66    fn configure_offload(&mut self, flags: NetOffloadFlags) -> std::io::Result<()> {
67        let _ = flags;
68        Ok(())
69    }
70
71    /// Configure the size of the `virtio_net_hdr` prepended to each frame on
72    /// the backend's wire representation.
73    ///
74    /// For TAP, this must match the guest's view of the header
75    /// (`sizeof(virtio_net_hdr_v1)` = 12 bytes when `MRG_RXBUF` or any of
76    /// the modern flags are negotiated; 10 bytes on legacy). Default no-op.
77    fn set_vnet_hdr_sz(&mut self, size: u32) -> std::io::Result<()> {
78        let _ = size;
79        Ok(())
80    }
81}
82
83/// Loopback network backend for testing.
84pub struct LoopbackBackend {
85    /// Packet queue.
86    packets: VecDeque<Vec<u8>>,
87}
88
89impl LoopbackBackend {
90    /// Creates a new loopback backend.
91    #[must_use]
92    pub const fn new() -> Self {
93        Self {
94            packets: VecDeque::new(),
95        }
96    }
97}
98
99impl Default for LoopbackBackend {
100    fn default() -> Self {
101        Self::new()
102    }
103}
104
105impl NetBackend for LoopbackBackend {
106    fn send(&mut self, packet: &NetPacket) -> std::io::Result<usize> {
107        self.packets.push_back(packet.data.clone());
108        Ok(packet.data.len())
109    }
110
111    fn recv(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
112        if let Some(packet) = self.packets.pop_front() {
113            let len = packet.len().min(buf.len());
114            buf[..len].copy_from_slice(&packet[..len]);
115            Ok(len)
116        } else {
117            Ok(0)
118        }
119    }
120
121    fn has_data(&self) -> bool {
122        !self.packets.is_empty()
123    }
124}
125
126#[cfg(test)]
127mod tests {
128    use super::*;
129
130    #[test]
131    fn test_loopback_backend_new() {
132        let backend = LoopbackBackend::new();
133        assert!(!backend.has_data());
134    }
135
136    #[test]
137    fn test_loopback_backend_default() {
138        let backend = LoopbackBackend::default();
139        assert!(!backend.has_data());
140    }
141
142    #[test]
143    fn test_loopback_backend_send_recv() {
144        let mut backend = LoopbackBackend::new();
145
146        let packet = NetPacket::new(vec![1, 2, 3, 4, 5]);
147        let sent = backend.send(&packet).unwrap();
148        assert_eq!(sent, 5);
149
150        assert!(backend.has_data());
151
152        let mut buf = [0u8; 10];
153        let n = backend.recv(&mut buf).unwrap();
154        assert_eq!(n, 5);
155        assert_eq!(&buf[..n], &[1, 2, 3, 4, 5]);
156
157        assert!(!backend.has_data());
158    }
159
160    #[test]
161    fn test_loopback_backend_recv_empty() {
162        let mut backend = LoopbackBackend::new();
163
164        let mut buf = [0u8; 10];
165        let n = backend.recv(&mut buf).unwrap();
166        assert_eq!(n, 0);
167    }
168
169    #[test]
170    fn test_loopback_backend_multiple_packets() {
171        let mut backend = LoopbackBackend::new();
172
173        for i in 0..5 {
174            let packet = NetPacket::new(vec![i; 10]);
175            backend.send(&packet).unwrap();
176        }
177
178        for i in 0..5 {
179            assert!(backend.has_data());
180            let mut buf = [0u8; 20];
181            let n = backend.recv(&mut buf).unwrap();
182            assert_eq!(n, 10);
183            assert!(buf[..n].iter().all(|&b| b == i));
184        }
185
186        assert!(!backend.has_data());
187    }
188
189    #[test]
190    fn test_loopback_backend_small_buffer() {
191        let mut backend = LoopbackBackend::new();
192
193        let packet = NetPacket::new(vec![0xAA; 100]);
194        backend.send(&packet).unwrap();
195
196        let mut buf = [0u8; 10];
197        let n = backend.recv(&mut buf).unwrap();
198        assert_eq!(n, 10);
199        assert!(buf.iter().all(|&b| b == 0xAA));
200    }
201
202    #[test]
203    fn test_loopback_large_packet() {
204        let mut backend = LoopbackBackend::new();
205
206        let data = vec![0xAB; 65536];
207        let packet = NetPacket::new(data.clone());
208        let sent = backend.send(&packet).unwrap();
209        assert_eq!(sent, 65536);
210
211        let mut buf = vec![0u8; 65536];
212        let n = backend.recv(&mut buf).unwrap();
213        assert_eq!(n, 65536);
214        assert_eq!(buf, data);
215    }
216
217    #[test]
218    fn test_loopback_supports_tso_default_false() {
219        let backend = LoopbackBackend::new();
220        assert!(!backend.supports_tso());
221    }
222}