1use std::sync::Arc;
22
23use parking_lot::Mutex;
24use squib_core::GuestMemory;
25
26use crate::{
27 device::{ActivateError, VirtioDevice},
28 device_id::VirtioDeviceType,
29 interrupt::IrqLine,
30 queue::Queue,
31};
32
33pub const F_MAC: u64 = 1 << 5;
35pub const F_STATUS: u64 = 1 << 16;
37pub const F_MTU: u64 = 1 << 3;
39
40pub const RX_QUEUE: usize = 0;
42pub const TX_QUEUE: usize = 1;
44
45const QUEUE_MAX_SIZE: u16 = 256;
46const VIRTIO_NET_HDR_LEN: u32 = 12;
47
48#[derive(Debug, Clone)]
58pub struct Frame {
59 pub bytes: bytes::Bytes,
61}
62
63impl Frame {
64 #[must_use]
68 pub fn from_slice(slice: &[u8]) -> Self {
69 Self {
70 bytes: bytes::Bytes::copy_from_slice(slice),
71 }
72 }
73
74 #[must_use]
78 pub fn from_buf(buf: bytes::BytesMut) -> Self {
79 Self {
80 bytes: buf.freeze(),
81 }
82 }
83
84 #[must_use]
88 pub const fn from_bytes(bytes: bytes::Bytes) -> Self {
89 Self { bytes }
90 }
91
92 #[must_use]
94 pub fn len(&self) -> usize {
95 self.bytes.len()
96 }
97
98 #[must_use]
100 pub fn is_empty(&self) -> bool {
101 self.bytes.is_empty()
102 }
103}
104
105#[derive(Debug)]
119pub struct FramePool {
120 free: Mutex<Vec<bytes::BytesMut>>,
121 mtu: usize,
122 pool_capacity: usize,
123}
124
125impl FramePool {
126 #[must_use]
129 pub fn new(mtu: usize, pool_capacity: usize) -> Self {
130 Self {
131 free: Mutex::new(Vec::with_capacity(pool_capacity)),
132 mtu,
133 pool_capacity,
134 }
135 }
136
137 #[must_use]
141 pub fn acquire(&self) -> bytes::BytesMut {
142 let mut g = self.free.lock();
143 match g.pop() {
144 Some(mut buf) => {
145 buf.clear();
146 buf
147 }
148 None => bytes::BytesMut::with_capacity(self.mtu),
149 }
150 }
151
152 pub fn release(&self, buf: bytes::BytesMut) {
155 let mut g = self.free.lock();
156 if g.len() < self.pool_capacity {
157 g.push(buf);
158 }
159 }
161
162 #[must_use]
164 pub const fn capacity(&self) -> usize {
165 self.pool_capacity
166 }
167
168 #[must_use]
170 pub const fn mtu(&self) -> usize {
171 self.mtu
172 }
173
174 #[must_use]
176 pub fn free_count(&self) -> usize {
177 self.free.lock().len()
178 }
179}
180
181pub trait NetBackend: Send + Sync + std::fmt::Debug {
184 fn send(&self, frame: &Frame);
186
187 fn recv(&self) -> Vec<Frame>;
190}
191
192#[derive(Debug, Default)]
196pub struct LoopbackBackend {
197 pending: Mutex<Vec<Frame>>,
198}
199
200impl NetBackend for LoopbackBackend {
201 fn send(&self, frame: &Frame) {
202 self.pending.lock().push(frame.clone());
203 }
204 fn recv(&self) -> Vec<Frame> {
205 std::mem::take(&mut *self.pending.lock())
206 }
207}
208
209pub trait FrameInterceptor: Send + Sync + std::fmt::Debug {
213 fn intercept_tx(&self, frame: &Frame) -> bool;
216
217 fn drain_rx(&self) -> Vec<Frame>;
220}
221
222#[derive(Debug, Default)]
225pub struct NoopInterceptor;
226
227impl FrameInterceptor for NoopInterceptor {
228 fn intercept_tx(&self, _frame: &Frame) -> bool {
229 false
230 }
231 fn drain_rx(&self) -> Vec<Frame> {
232 Vec::new()
233 }
234}
235
236impl FrameInterceptor for squib_mmds::MmdsInterceptor {
241 fn intercept_tx(&self, frame: &Frame) -> bool {
242 squib_mmds::MmdsInterceptor::intercept(self, &frame.bytes)
246 }
247 fn drain_rx(&self) -> Vec<Frame> {
248 squib_mmds::MmdsInterceptor::drain_rx(self)
249 .into_iter()
250 .map(Frame::from_bytes)
251 .collect()
252 }
253}
254
255#[derive(Debug, Clone)]
257pub struct NetConfig {
258 pub iface_id: String,
260 pub host_dev_name: String,
262 pub guest_mac: Option<[u8; 6]>,
264 pub mtu: Option<u16>,
266}
267
268#[derive(Debug)]
270pub struct NetDevice {
271 avail: u64,
272 acked: u64,
273 queues: Vec<Queue>,
274 config: NetConfig,
275 backend: Arc<dyn NetBackend>,
276 interceptor: Arc<dyn FrameInterceptor>,
277 state: Arc<Mutex<ActiveState>>,
278}
279
280#[derive(Debug, Default)]
281struct ActiveState {
282 mem: Option<Arc<dyn GuestMemory>>,
283 irq: Option<IrqLine>,
284 activated: bool,
285}
286
287impl NetDevice {
288 #[must_use]
290 pub fn new(
291 config: NetConfig,
292 backend: Arc<dyn NetBackend>,
293 interceptor: Arc<dyn FrameInterceptor>,
294 ) -> Self {
295 let mut avail = 0;
296 if config.guest_mac.is_some() {
297 avail |= F_MAC;
298 }
299 if config.mtu.is_some() {
300 avail |= F_MTU;
301 }
302 Self {
303 avail,
304 acked: 0,
305 queues: vec![Queue::new(QUEUE_MAX_SIZE), Queue::new(QUEUE_MAX_SIZE)],
306 config,
307 backend,
308 interceptor,
309 state: Arc::new(Mutex::new(ActiveState::default())),
310 }
311 }
312
313 fn drain_tx(&mut self) {
314 let (mem, irq) = {
315 let state = self.state.lock();
316 match (state.mem.clone(), state.irq.clone()) {
317 (Some(m), Some(i)) => (m, i),
318 _ => return,
319 }
320 };
321 let backend = Arc::clone(&self.backend);
322 let interceptor = Arc::clone(&self.interceptor);
323 let queue = &mut self.queues[TX_QUEUE];
324 let mut completed = false;
325 loop {
326 let chain = match queue.pop_avail(mem.as_ref()) {
327 Ok(Some(c)) => c,
328 Ok(None) => break,
329 Err(err) => {
330 tracing::warn!(error = %err, "net: tx walk failed");
331 break;
332 }
333 };
334 let head = chain.head_index();
335 let descs = match chain.collect(mem.as_ref()) {
336 Ok(d) => d,
337 Err(err) => {
338 tracing::warn!(error = %err, "net: tx chain collect failed");
339 break;
340 }
341 };
342 let mut frame_bytes = Vec::new();
345 for desc in &descs {
346 if desc.is_write_only() {
347 continue;
348 }
349 let mut buf = vec![0u8; desc.len as usize];
350 if let Err(err) = mem.read(desc.addr, &mut buf) {
351 tracing::warn!(error = %err, "net: tx frame read failed");
352 continue;
353 }
354 frame_bytes.extend_from_slice(&buf);
355 }
356 let payload = if frame_bytes.len() > VIRTIO_NET_HDR_LEN as usize {
358 Frame::from_slice(&frame_bytes[VIRTIO_NET_HDR_LEN as usize..])
359 } else {
360 continue;
361 };
362 if !interceptor.intercept_tx(&payload) {
363 backend.send(&payload);
364 }
365 if let Err(err) = queue.push_used(mem.as_ref(), head, 0) {
366 tracing::warn!(error = %err, "net: tx push_used failed");
367 break;
368 }
369 completed = true;
370 }
371 if completed {
372 let _ = irq.trigger_queue();
373 }
374 }
375
376 fn drain_rx(&mut self) {
377 let (mem, irq) = {
378 let state = self.state.lock();
379 match (state.mem.clone(), state.irq.clone()) {
380 (Some(m), Some(i)) => (m, i),
381 _ => return,
382 }
383 };
384 let backend = Arc::clone(&self.backend);
385 let interceptor = Arc::clone(&self.interceptor);
386 let mut frames = interceptor.drain_rx();
389 frames.extend(backend.recv());
390 if frames.is_empty() {
391 return;
392 }
393 let queue = &mut self.queues[RX_QUEUE];
394 let mut completed = false;
395 for frame in frames {
396 let chain = match queue.pop_avail(mem.as_ref()) {
397 Ok(Some(c)) => c,
398 Ok(None) => break,
399 Err(err) => {
400 tracing::warn!(error = %err, "net: rx walk failed");
401 break;
402 }
403 };
404 let head = chain.head_index();
405 let descs = match chain.collect(mem.as_ref()) {
406 Ok(d) => d,
407 Err(err) => {
408 tracing::warn!(error = %err, "net: rx chain collect failed");
409 break;
410 }
411 };
412 let mut wire = vec![0u8; VIRTIO_NET_HDR_LEN as usize];
414 wire.extend_from_slice(&frame.bytes);
415 let mut written: u32 = 0;
416 let mut wire_off: usize = 0;
417 for desc in descs {
418 if !desc.is_write_only() {
419 continue;
420 }
421 let len = (desc.len as usize).min(wire.len() - wire_off);
422 if len == 0 {
423 continue;
424 }
425 if mem
426 .write(desc.addr, &wire[wire_off..wire_off + len])
427 .is_err()
428 {
429 break;
430 }
431 wire_off += len;
432 written = written.saturating_add(len as u32);
433 if wire_off >= wire.len() {
434 break;
435 }
436 }
437 if let Err(err) = queue.push_used(mem.as_ref(), head, written) {
438 tracing::warn!(error = %err, "net: rx push_used failed");
439 break;
440 }
441 completed = true;
442 }
443 if completed && let Err(e) = irq.trigger_queue() {
444 tracing::warn!(error = ?e, "net: rx irq trigger failed");
445 }
446 }
447}
448
449impl VirtioDevice for NetDevice {
450 fn device_type(&self) -> VirtioDeviceType {
451 VirtioDeviceType::Net
452 }
453 fn avail_features(&self) -> u64 {
454 self.avail
455 }
456 fn acked_features(&self) -> u64 {
457 self.acked
458 }
459 fn set_acked_features(&mut self, value: u64) {
460 self.acked = value;
461 }
462 fn queue_max_sizes(&self) -> &[u16] {
463 const SIZES: &[u16] = &[QUEUE_MAX_SIZE, QUEUE_MAX_SIZE];
464 SIZES
465 }
466 fn queues(&self) -> &[Queue] {
467 &self.queues
468 }
469 fn queues_mut(&mut self) -> &mut [Queue] {
470 &mut self.queues
471 }
472 fn read_config(&self, offset: u64, data: &mut [u8]) {
473 let mut full = [0u8; 16];
479 if let Some(mac) = self.config.guest_mac {
480 full[0..6].copy_from_slice(&mac);
481 }
482 if let Some(mtu) = self.config.mtu {
483 full[12..14].copy_from_slice(&mtu.to_le_bytes());
484 }
485 let off = offset as usize;
486 for (i, b) in data.iter_mut().enumerate() {
487 *b = full.get(off + i).copied().unwrap_or(0);
488 }
489 }
490 fn write_config(&mut self, _offset: u64, _data: &[u8]) {}
491 fn activate(&mut self, mem: Arc<dyn GuestMemory>, irq: IrqLine) -> Result<(), ActivateError> {
492 let mut state = self.state.lock();
493 state.mem = Some(mem);
494 state.irq = Some(irq);
495 state.activated = true;
496 Ok(())
497 }
498 fn is_activated(&self) -> bool {
499 self.state.lock().activated
500 }
501 fn process_queue(&mut self, queue_index: u16) {
502 match queue_index as usize {
503 TX_QUEUE => {
504 self.drain_tx();
505 self.drain_rx();
510 }
511 RX_QUEUE => self.drain_rx(),
512 _ => {}
513 }
514 }
515}
516
517#[cfg(test)]
518mod tests {
519 use squib_arch::IntId;
520 use squib_core::{GuestAddress, SliceGuestMemory};
521 use squib_gic::Gic;
522
523 use super::*;
524 use crate::queue::VIRTQ_DESC_F_WRITE;
525
526 #[derive(Debug, Default)]
527 struct StubGic;
528 impl Gic for StubGic {
529 fn pulse_spi(&self, _: IntId) -> Result<(), squib_gic::GicError> {
530 Ok(())
531 }
532 fn set_spi_level(&self, _: IntId, _: bool) -> Result<(), squib_gic::GicError> {
533 Ok(())
534 }
535 fn save_state(&self) -> Result<Vec<u8>, squib_gic::GicError> {
536 Ok(Vec::new())
537 }
538 fn restore_state(&self, _data: &[u8]) -> Result<(), squib_gic::GicError> {
539 Ok(())
540 }
541 }
542
543 fn line() -> IrqLine {
544 let gic: Arc<dyn Gic + Send + Sync> = Arc::new(StubGic);
545 IrqLine::new(gic, IntId::from_spi_cell(17).unwrap())
546 }
547
548 fn config() -> NetConfig {
549 NetConfig {
550 iface_id: "eth0".into(),
551 host_dev_name: "tap0".into(),
552 guest_mac: Some([0x06, 0x00, 0xAC, 0x10, 0x00, 0x02]),
553 mtu: Some(1500),
554 }
555 }
556
557 #[derive(Debug, Default)]
560 struct ReplayInterceptor {
561 intercepted: Mutex<Vec<Frame>>,
562 rx_queue: Mutex<Vec<Frame>>,
563 }
564 impl FrameInterceptor for ReplayInterceptor {
565 fn intercept_tx(&self, frame: &Frame) -> bool {
566 if frame.bytes.len() >= 6
568 && &frame.bytes[..6] == [0x06, 0x01, 0x23, 0x45, 0x67, 0x01].as_slice()
569 {
570 self.intercepted.lock().push(frame.clone());
571 true
572 } else {
573 false
574 }
575 }
576 fn drain_rx(&self) -> Vec<Frame> {
577 std::mem::take(&mut *self.rx_queue.lock())
578 }
579 }
580
581 #[test]
582 fn test_should_acquire_and_release_buffers_via_frame_pool() {
583 let pool = FramePool::new(1500, 4);
584 assert_eq!(pool.free_count(), 0);
585 let buf1 = pool.acquire();
586 assert_eq!(buf1.len(), 0);
587 assert!(buf1.capacity() >= 1500);
588 let buf2 = pool.acquire();
589 assert_eq!(pool.free_count(), 0);
590 pool.release(buf1);
591 pool.release(buf2);
592 assert_eq!(pool.free_count(), 2);
593 let _ = pool.acquire();
595 assert_eq!(pool.free_count(), 1);
596 }
597
598 #[test]
599 fn test_should_drop_releases_beyond_pool_capacity() {
600 let pool = FramePool::new(1500, 2);
601 pool.release(bytes::BytesMut::with_capacity(1500));
602 pool.release(bytes::BytesMut::with_capacity(1500));
603 pool.release(bytes::BytesMut::with_capacity(1500));
605 assert_eq!(pool.free_count(), 2);
606 }
607
608 #[test]
609 fn test_should_clear_acquired_buffer_so_caller_writes_into_empty() {
610 let pool = FramePool::new(1500, 2);
611 let mut b = pool.acquire();
612 b.extend_from_slice(b"hello");
613 assert_eq!(b.len(), 5);
614 pool.release(b);
615 let b = pool.acquire();
616 assert_eq!(
617 b.len(),
618 0,
619 "pool must clear on acquire so the caller writes into an empty buffer"
620 );
621 }
622
623 #[test]
624 fn test_should_freeze_bytesmut_into_frame_via_from_buf() {
625 let mut buf = bytes::BytesMut::with_capacity(8);
626 buf.extend_from_slice(b"abcdef");
627 let frame = Frame::from_buf(buf);
628 assert_eq!(frame.bytes.as_ref(), b"abcdef");
629 assert_eq!(frame.len(), 6);
630 }
631
632 #[test]
633 fn test_should_offer_mac_feature_when_config_supplies_one() {
634 let dev = NetDevice::new(
635 config(),
636 Arc::new(LoopbackBackend::default()),
637 Arc::new(NoopInterceptor),
638 );
639 assert_ne!(dev.avail_features() & F_MAC, 0);
640 }
641
642 #[test]
643 fn test_should_publish_mac_in_config_space() {
644 let dev = NetDevice::new(
645 config(),
646 Arc::new(LoopbackBackend::default()),
647 Arc::new(NoopInterceptor),
648 );
649 let mut got = [0u8; 6];
650 dev.read_config(0, &mut got);
651 assert_eq!(got, [0x06, 0x00, 0xAC, 0x10, 0x00, 0x02]);
652 }
653
654 #[derive(Debug, Default)]
658 struct CapturedBackend {
659 sent: Mutex<Vec<Frame>>,
660 }
661 impl NetBackend for CapturedBackend {
662 fn send(&self, frame: &Frame) {
663 self.sent.lock().push(frame.clone());
664 }
665 fn recv(&self) -> Vec<Frame> {
666 Vec::new()
667 }
668 }
669
670 #[test]
671 fn test_should_send_tx_frames_to_backend_when_no_interception() {
672 let backend = Arc::new(CapturedBackend::default());
673 let mut dev = NetDevice::new(config(), backend.clone(), Arc::new(NoopInterceptor));
674 let mem = Arc::new(SliceGuestMemory::new(GuestAddress(0x4000_0000), 0x4000));
675 let q = &mut dev.queues_mut()[TX_QUEUE];
676 q.size = 8;
677 q.desc_table_addr = GuestAddress(0x4000_0000);
678 q.avail_ring_addr = GuestAddress(0x4000_0800);
679 q.used_ring_addr = GuestAddress(0x4000_1000);
680 q.ready = true;
681 let mut payload = vec![0u8; 12];
683 payload.extend_from_slice(b"helloeth");
684 mem.write(GuestAddress(0x4000_2000), &payload).unwrap();
685 let base = 0x4000_0000u64;
686 mem.write_u32_le(GuestAddress(base), 0x4000_2000).unwrap();
687 mem.write_u32_le(GuestAddress(base + 4), 0).unwrap();
688 mem.write_u32_le(GuestAddress(base + 8), payload.len() as u32)
689 .unwrap();
690 mem.write_u16_le(GuestAddress(base + 12), 0).unwrap();
691 mem.write_u16_le(GuestAddress(base + 14), 0).unwrap();
692 mem.write_u16_le(GuestAddress(0x4000_0804), 0).unwrap();
693 mem.write_u16_le(GuestAddress(0x4000_0802), 1).unwrap();
694 dev.activate(mem.clone(), line()).unwrap();
695 dev.process_queue(TX_QUEUE as u16);
696 let sent = backend.sent.lock().clone();
697 assert_eq!(sent.len(), 1);
698 assert_eq!(sent[0].bytes.as_ref(), b"helloeth");
699 }
700
701 #[test]
702 fn test_should_intercept_tx_frames_when_interceptor_claims_them() {
703 let backend = Arc::new(CapturedBackend::default());
704 let interceptor = Arc::new(ReplayInterceptor::default());
705 let mut dev = NetDevice::new(config(), backend.clone(), interceptor.clone());
706 let mem = Arc::new(SliceGuestMemory::new(GuestAddress(0x4000_0000), 0x4000));
707 let q = &mut dev.queues_mut()[TX_QUEUE];
708 q.size = 8;
709 q.desc_table_addr = GuestAddress(0x4000_0000);
710 q.avail_ring_addr = GuestAddress(0x4000_0800);
711 q.used_ring_addr = GuestAddress(0x4000_1000);
712 q.ready = true;
713 let mut payload = vec![0u8; 12];
715 payload.extend_from_slice(&[0x06, 0x01, 0x23, 0x45, 0x67, 0x01]);
716 payload.extend_from_slice(b"rest");
717 mem.write(GuestAddress(0x4000_2000), &payload).unwrap();
718 let base = 0x4000_0000u64;
719 mem.write_u32_le(GuestAddress(base), 0x4000_2000).unwrap();
720 mem.write_u32_le(GuestAddress(base + 4), 0).unwrap();
721 mem.write_u32_le(GuestAddress(base + 8), payload.len() as u32)
722 .unwrap();
723 mem.write_u16_le(GuestAddress(base + 12), 0).unwrap();
724 mem.write_u16_le(GuestAddress(base + 14), 0).unwrap();
725 mem.write_u16_le(GuestAddress(0x4000_0804), 0).unwrap();
726 mem.write_u16_le(GuestAddress(0x4000_0802), 1).unwrap();
727 dev.activate(mem.clone(), line()).unwrap();
728 dev.process_queue(TX_QUEUE as u16);
729 assert!(backend.sent.lock().is_empty());
731 assert_eq!(interceptor.intercepted.lock().len(), 1);
732 }
733
734 #[test]
735 fn test_should_inject_rx_frames_from_interceptor_with_virtio_header_prepended() {
736 let backend = Arc::new(LoopbackBackend::default());
737 let interceptor = Arc::new(ReplayInterceptor::default());
738 interceptor
739 .rx_queue
740 .lock()
741 .push(Frame::from_slice(b"hello-rx"));
742 let mut dev = NetDevice::new(config(), backend.clone(), interceptor.clone());
743 let mem = Arc::new(SliceGuestMemory::new(GuestAddress(0x4000_0000), 0x4000));
744 let q = &mut dev.queues_mut()[RX_QUEUE];
745 q.size = 8;
746 q.desc_table_addr = GuestAddress(0x4000_0000);
747 q.avail_ring_addr = GuestAddress(0x4000_0800);
748 q.used_ring_addr = GuestAddress(0x4000_1000);
749 q.ready = true;
750 let base = 0x4000_0000u64;
752 mem.write_u32_le(GuestAddress(base), 0x4000_2000).unwrap();
753 mem.write_u32_le(GuestAddress(base + 4), 0).unwrap();
754 mem.write_u32_le(GuestAddress(base + 8), 32).unwrap();
755 mem.write_u16_le(GuestAddress(base + 12), VIRTQ_DESC_F_WRITE)
756 .unwrap();
757 mem.write_u16_le(GuestAddress(base + 14), 0).unwrap();
758 mem.write_u16_le(GuestAddress(0x4000_0804), 0).unwrap();
759 mem.write_u16_le(GuestAddress(0x4000_0802), 1).unwrap();
760 dev.activate(mem.clone(), line()).unwrap();
761 dev.process_queue(RX_QUEUE as u16);
762 let mut got = [0u8; 20];
764 mem.read(GuestAddress(0x4000_2000), &mut got).unwrap();
765 assert_eq!(&got[0..12], &[0u8; 12]); assert_eq!(&got[12..20], b"hello-rx");
767 }
768}