1use std::collections::HashMap;
4use std::sync::{Arc, Mutex, RwLock};
5
6use arcbox_virtio_core::error::{Result, VirtioError};
7use arcbox_virtio_core::queue::VirtQueue;
8use arcbox_virtio_core::{DeviceCtx, QueueConfig, VirtioDevice, VirtioDeviceId, virtio_bindings};
9
10use crate::addr::{HOST_CID, RESERVED_CID, VsockAddr, VsockHostConnections};
11use crate::backend::{LoopbackBackend, VsockBackend};
12use crate::connection::{ConnectionState, VsockConnection};
13use crate::manager::VsockConnectionManager;
14use crate::protocol::{VsockHeader, VsockOp};
15
16fn write_all_with_backoff(fd: i32, buf: &[u8]) -> usize {
33 const MAX_POLL_RETRIES: u32 = 16;
34 const POLL_TIMEOUT_MS: libc::c_int = 2; let mut offset = 0usize;
37 let mut eagain_retries = 0u32;
38
39 while offset < buf.len() {
40 let ret = unsafe {
43 libc::write(
44 fd,
45 buf[offset..].as_ptr().cast::<libc::c_void>(),
46 buf.len() - offset,
47 )
48 };
49
50 use std::cmp::Ordering;
51 match ret.cmp(&0) {
52 Ordering::Greater => {
53 offset += ret as usize;
54 eagain_retries = 0;
55 }
56 Ordering::Equal => {
57 break;
59 }
60 Ordering::Less => {
61 let err = std::io::Error::last_os_error();
62 match err.raw_os_error() {
63 Some(e) if e == libc::EAGAIN || e == libc::EWOULDBLOCK => {
64 if eagain_retries >= MAX_POLL_RETRIES {
65 tracing::warn!(
66 "Vsock: giving up after {MAX_POLL_RETRIES} EAGAIN retries at offset {offset}/{} on fd {fd}",
67 buf.len(),
68 );
69 break;
70 }
71 eagain_retries += 1;
72 let mut pfd = libc::pollfd {
74 fd,
75 events: libc::POLLOUT,
76 revents: 0,
77 };
78 let _ = unsafe { libc::poll(&mut pfd, 1, POLL_TIMEOUT_MS) };
80 }
81 Some(libc::EINTR) => {}
82 _ => {
83 tracing::warn!("Vsock: write to fd {fd} failed at offset {offset}: {err}");
84 break;
85 }
86 }
87 }
88 }
89 }
90
91 offset
92}
93
94#[derive(Debug, Clone)]
96pub struct VsockConfig {
97 pub guest_cid: u64,
99}
100
101impl Default for VsockConfig {
102 fn default() -> Self {
103 Self {
104 guest_cid: 3, }
106 }
107}
108
109pub struct VirtioVsock {
114 config: VsockConfig,
115 features: u64,
116 acked_features: u64,
117 backend: Option<Arc<Mutex<dyn VsockBackend>>>,
119 connections: RwLock<HashMap<(u32, u32), VsockConnection>>,
121 rx_queue: Option<VirtQueue>,
123 tx_queue: Option<VirtQueue>,
125 event_queue: Option<VirtQueue>,
127 host_connections: HashMap<u32, std::os::unix::io::RawFd>,
131 last_avail_idx_tx: usize,
133 last_avail_idx_rx: usize,
135 ctx: Option<DeviceCtx>,
139 conns: Option<Arc<Mutex<dyn VsockHostConnections>>>,
143 conn_mgr: Option<Arc<Mutex<VsockConnectionManager>>>,
150}
151
152impl VirtioVsock {
153 pub const FEATURE_STREAM: u64 = 1 << 0;
155 pub const FEATURE_SEQPACKET: u64 = 1 << 1;
157 pub const FEATURE_VERSION_1: u64 = 1 << virtio_bindings::virtio_config::VIRTIO_F_VERSION_1;
159
160 pub const HOST_CID: u64 = HOST_CID;
162 pub const RESERVED_CID: u64 = RESERVED_CID;
164
165 #[must_use]
167 pub fn new(config: VsockConfig) -> Self {
168 Self {
169 config,
170 features: Self::FEATURE_STREAM
171 | Self::FEATURE_VERSION_1
172 | arcbox_virtio_core::queue::VIRTIO_F_EVENT_IDX,
173 acked_features: 0,
174 backend: None,
175 connections: RwLock::new(HashMap::new()),
176 rx_queue: None,
177 tx_queue: None,
178 event_queue: None,
179 host_connections: HashMap::new(),
180 last_avail_idx_tx: 0,
181 last_avail_idx_rx: 0,
182 ctx: None,
183 conns: None,
184 conn_mgr: None,
185 }
186 }
187
188 #[must_use]
190 pub fn with_backend<B: VsockBackend + 'static>(config: VsockConfig, backend: B) -> Self {
191 Self {
192 config,
193 features: Self::FEATURE_STREAM
194 | Self::FEATURE_VERSION_1
195 | arcbox_virtio_core::queue::VIRTIO_F_EVENT_IDX,
196 acked_features: 0,
197 backend: Some(Arc::new(Mutex::new(backend))),
198 connections: RwLock::new(HashMap::new()),
199 rx_queue: None,
200 tx_queue: None,
201 event_queue: None,
202 host_connections: HashMap::new(),
203 last_avail_idx_tx: 0,
204 last_avail_idx_rx: 0,
205 ctx: None,
206 conns: None,
207 conn_mgr: None,
208 }
209 }
210
211 pub fn set_backend<B: VsockBackend + 'static>(&mut self, backend: B) {
213 self.backend = Some(Arc::new(Mutex::new(backend)));
214 }
215
216 pub fn bind_ctx(&mut self, ctx: DeviceCtx) {
219 self.ctx = Some(ctx);
220 }
221
222 pub fn bind_connections(&mut self, conns: Arc<Mutex<dyn VsockHostConnections>>) {
227 self.conns = Some(conns);
228 }
229
230 pub fn bind_connection_manager(&mut self, mgr: Arc<Mutex<VsockConnectionManager>>) {
235 self.conns = Some(mgr.clone());
236 self.conn_mgr = Some(mgr);
237 }
238
239 pub fn connections(&self) -> Option<Arc<Mutex<dyn VsockHostConnections>>> {
241 self.conns.clone()
242 }
243
244 #[must_use]
246 pub const fn guest_cid(&self) -> u64 {
247 self.config.guest_cid
248 }
249
250 pub fn handle_connect(&self, src_port: u32, dst_port: u32) -> Result<()> {
252 let local = VsockAddr::new(self.config.guest_cid, src_port);
253 let remote = VsockAddr::new(Self::HOST_CID, dst_port);
254
255 let mut conn = VsockConnection::new(local, remote);
256 conn.state = ConnectionState::Connecting;
257
258 if let Some(ref backend) = self.backend {
259 backend.lock().unwrap().on_connect(local)?;
260 conn.state = ConnectionState::Connected;
261 }
262
263 self.connections
264 .write()
265 .unwrap()
266 .insert((src_port, dst_port), conn);
267 tracing::debug!(
268 "Vsock connect: {}:{} -> {}:{}",
269 self.config.guest_cid,
270 src_port,
271 Self::HOST_CID,
272 dst_port
273 );
274
275 Ok(())
276 }
277
278 pub fn handle_send(&self, src_port: u32, dst_port: u32, data: &[u8]) -> Result<usize> {
280 let local = VsockAddr::new(self.config.guest_cid, src_port);
281
282 if let Some(ref backend) = self.backend {
283 backend.lock().unwrap().on_send(local, data)
284 } else {
285 let mut conns = self.connections.write().unwrap();
286 if let Some(conn) = conns.get_mut(&(src_port, dst_port)) {
287 conn.enqueue_tx(data);
288 Ok(data.len())
289 } else {
290 Err(VirtioError::InvalidOperation("Connection not found".into()))
291 }
292 }
293 }
294
295 pub fn handle_recv(&self, src_port: u32, dst_port: u32, buf: &mut [u8]) -> Result<usize> {
297 let local = VsockAddr::new(self.config.guest_cid, src_port);
298
299 if let Some(ref backend) = self.backend {
300 backend.lock().unwrap().on_recv(local, buf)
301 } else {
302 let mut conns = self.connections.write().unwrap();
303 if let Some(conn) = conns.get_mut(&(src_port, dst_port)) {
304 let data = conn.dequeue_rx(buf.len());
305 buf[..data.len()].copy_from_slice(&data);
306 Ok(data.len())
307 } else {
308 Err(VirtioError::InvalidOperation("Connection not found".into()))
309 }
310 }
311 }
312
313 pub fn handle_close(&self, src_port: u32, dst_port: u32) -> Result<()> {
315 let local = VsockAddr::new(self.config.guest_cid, src_port);
316
317 if let Some(ref backend) = self.backend {
318 backend.lock().unwrap().on_close(local)?;
319 }
320
321 self.connections
322 .write()
323 .unwrap()
324 .remove(&(src_port, dst_port));
325 tracing::debug!("Vsock close: {}:{}", self.config.guest_cid, src_port);
326
327 Ok(())
328 }
329
330 #[must_use]
332 pub fn connection_count(&self) -> usize {
333 self.connections.read().unwrap().len()
334 }
335
336 pub fn tx_queue_mut(&mut self) -> Option<&mut VirtQueue> {
338 self.tx_queue.as_mut()
339 }
340
341 pub fn rx_queue_mut(&mut self) -> Option<&mut VirtQueue> {
343 self.rx_queue.as_mut()
344 }
345
346 fn handle_tx_packet_with_fds(
348 &self,
349 hdr: &VsockHeader,
350 payload: &[u8],
351 connections: Option<&mut dyn VsockHostConnections>,
352 ) {
353 let src_cid = { hdr.src_cid };
355 let dst_cid = { hdr.dst_cid };
356 let src_port = { hdr.src_port };
357 let dst_port = { hdr.dst_port };
358 let buf_alloc = { hdr.buf_alloc };
359 let fwd_cnt = { hdr.fwd_cnt };
360 let flags = { hdr.flags };
361
362 match hdr.operation() {
363 Some(VsockOp::Request) => {
364 tracing::debug!(
365 "Vsock TX: OP_REQUEST src={}:{} dst={}:{}",
366 src_cid,
367 src_port,
368 dst_cid,
369 dst_port,
370 );
371 }
372 Some(VsockOp::Response) => {
373 tracing::info!(
376 "Vsock TX: OP_RESPONSE — connection established (guest_port={}, host_port={})",
377 src_port,
378 dst_port,
379 );
380 if let Some(conns) = connections {
381 conns.update_peer_credit(src_port, dst_port, buf_alloc, fwd_cnt);
382 conns.mark_connected(src_port, dst_port);
383 }
384 }
385 Some(VsockOp::Rw) => {
386 if let Some(conns) = connections {
388 conns.update_peer_credit(src_port, dst_port, buf_alloc, fwd_cnt);
389 if let Some(fd) = conns.fd_for(src_port, dst_port) {
390 if !payload.is_empty() {
391 let total = payload.len();
392 let forwarded = write_all_with_backoff(fd, payload);
393 if forwarded > 0 {
394 tracing::debug!(
395 "Vsock TX: OP_RW guest_port={} host_port={} -> fd {fd}, {}/{} bytes",
396 src_port,
397 dst_port,
398 forwarded,
399 total,
400 );
401 #[allow(clippy::cast_possible_truncation)]
408 {
409 conns.advance_fwd_cnt(src_port, dst_port, forwarded as u32);
410 }
411 }
412 if forwarded < total {
413 tracing::warn!(
414 "Vsock TX: truncated write guest_port={} host_port={}: only {}/{} bytes forwarded (ABX-365)",
415 src_port,
416 dst_port,
417 forwarded,
418 total,
419 );
420 }
421 }
422 } else {
423 tracing::warn!(
424 "Vsock TX: OP_RW no host fd for guest_port={} host_port={}",
425 src_port,
426 dst_port,
427 );
428 }
429 }
430 }
431 Some(VsockOp::Shutdown) => {
432 tracing::debug!(
433 "Vsock TX: OP_SHUTDOWN guest_port={} host_port={} flags=0x{:x}",
434 src_port,
435 dst_port,
436 flags,
437 );
438 if let Some(conns) = connections {
439 conns.handle_shutdown(src_port, dst_port, flags);
443 }
444 }
445 Some(VsockOp::Rst) => {
446 tracing::debug!(
447 "Vsock TX: OP_RST guest_port={} host_port={}",
448 src_port,
449 dst_port,
450 );
451 if let Some(conns) = connections {
452 conns.remove_connection(src_port, dst_port);
453 }
454 }
455 Some(VsockOp::CreditUpdate) => {
456 tracing::trace!(
457 "Vsock TX: OP_CREDIT_UPDATE guest_port={} host_port={} buf_alloc={} fwd_cnt={}",
458 src_port,
459 dst_port,
460 buf_alloc,
461 fwd_cnt,
462 );
463 if let Some(conns) = connections {
464 conns.update_peer_credit(src_port, dst_port, buf_alloc, fwd_cnt);
465 }
466 }
467 Some(VsockOp::CreditRequest) => {
468 tracing::trace!(
469 "Vsock TX: OP_CREDIT_REQUEST guest_port={} host_port={}",
470 src_port,
471 dst_port,
472 );
473 if let Some(conns) = connections {
474 conns.update_peer_credit(src_port, dst_port, buf_alloc, fwd_cnt);
475 conns.enqueue_credit_update(src_port, dst_port);
476 }
477 }
478 _ => {}
479 }
480 }
481
482 pub fn add_host_connection(&mut self, guest_port: u32, fd: std::os::unix::io::RawFd) {
486 tracing::info!("Vsock: host connection for guest port {guest_port} -> fd {fd}");
487 self.host_connections.insert(guest_port, fd);
488 }
489
490 pub fn process_tx_queue(&mut self, memory: &mut [u8]) -> Result<Vec<(u16, u32)>> {
501 let mut raw_packets: Vec<(u16, Vec<u8>)> = Vec::new();
503
504 {
505 let queue = self
506 .tx_queue
507 .as_mut()
508 .ok_or_else(|| VirtioError::NotReady("TX queue not ready".into()))?;
509
510 while let Some((head_idx, chain)) = queue.pop_avail() {
511 let mut data = Vec::new();
512
513 for desc in chain {
514 if !desc.is_write_only() {
515 let start = desc.addr as usize;
517 let end = start + desc.len as usize;
518 if end <= memory.len() {
519 data.extend_from_slice(&memory[start..end]);
520 }
521 }
522 }
523
524 raw_packets.push((head_idx, data));
525 }
526 }
527
528 let mut completions = Vec::new();
530 let mut rx_inject: Vec<(VsockHeader, Vec<u8>)> = Vec::new();
532
533 for (head_idx, data) in &raw_packets {
534 if data.len() < VsockHeader::SIZE {
535 tracing::warn!(
536 "Vsock TX: descriptor {} too short ({} bytes), skipping",
537 head_idx,
538 data.len()
539 );
540 completions.push((*head_idx, 0u32));
541 continue;
542 }
543
544 let header = match VsockHeader::from_bytes(&data[..VsockHeader::SIZE]) {
545 Some(h) => h,
546 None => {
547 tracing::warn!(
548 "Vsock TX: failed to parse header for descriptor {}",
549 head_idx
550 );
551 completions.push((*head_idx, 0u32));
552 continue;
553 }
554 };
555
556 let payload_len = { header.len } as usize;
557 let payload = if payload_len > 0 && data.len() > VsockHeader::SIZE {
558 let avail = data.len() - VsockHeader::SIZE;
559 &data[VsockHeader::SIZE..VsockHeader::SIZE + payload_len.min(avail)]
560 } else {
561 &[] as &[u8]
562 };
563
564 let src_port = { header.src_port };
565 let dst_port = { header.dst_port };
566
567 match header.operation() {
568 Some(VsockOp::Request) => {
569 tracing::debug!(
570 "Vsock TX: OP_REQUEST from port {} to port {}",
571 src_port,
572 dst_port
573 );
574 match self.handle_connect(src_port, dst_port) {
575 Ok(()) => {
576 let resp = VsockHeader::new(
578 VsockAddr::new(Self::HOST_CID, dst_port),
579 VsockAddr::new(self.config.guest_cid, src_port),
580 VsockOp::Response,
581 );
582 rx_inject.push((resp, Vec::new()));
583 }
584 Err(e) => {
585 tracing::warn!("Vsock TX: connect failed: {}", e);
586 let rst = VsockHeader::new(
588 VsockAddr::new(Self::HOST_CID, dst_port),
589 VsockAddr::new(self.config.guest_cid, src_port),
590 VsockOp::Rst,
591 );
592 rx_inject.push((rst, Vec::new()));
593 }
594 }
595 }
596 Some(VsockOp::Response) => {
597 tracing::debug!(
599 "Vsock TX: OP_RESPONSE from port {} to port {}",
600 src_port,
601 dst_port
602 );
603 let mut conns = self.connections.write().unwrap();
604 if let Some(conn) = conns.get_mut(&(src_port, dst_port)) {
605 conn.state = ConnectionState::Connected;
606 }
607 }
608 Some(VsockOp::Rw) => {
609 tracing::trace!(
610 "Vsock TX: OP_RW {} bytes from port {} to port {}",
611 payload.len(),
612 src_port,
613 dst_port
614 );
615 if let Err(e) = self.handle_send(src_port, dst_port, payload) {
616 tracing::warn!("Vsock TX: send failed: {}", e);
617 }
618 }
619 Some(VsockOp::Shutdown) => {
620 tracing::debug!(
621 "Vsock TX: OP_SHUTDOWN from port {} to port {}",
622 src_port,
623 dst_port
624 );
625 if let Err(e) = self.handle_close(src_port, dst_port) {
626 tracing::warn!("Vsock TX: close failed: {}", e);
627 }
628 let rst = VsockHeader::new(
630 VsockAddr::new(Self::HOST_CID, dst_port),
631 VsockAddr::new(self.config.guest_cid, src_port),
632 VsockOp::Rst,
633 );
634 rx_inject.push((rst, Vec::new()));
635 }
636 Some(VsockOp::Rst) => {
637 tracing::debug!(
638 "Vsock TX: OP_RST from port {} to port {}",
639 src_port,
640 dst_port
641 );
642 let _ = self.handle_close(src_port, dst_port);
643 }
644 Some(VsockOp::CreditUpdate) => {
645 let buf_alloc = { header.buf_alloc };
646 let fwd_cnt = { header.fwd_cnt };
647 tracing::trace!(
648 "Vsock TX: OP_CREDIT_UPDATE port {} buf_alloc={} fwd_cnt={}",
649 src_port,
650 buf_alloc,
651 fwd_cnt
652 );
653 let mut conns = self.connections.write().unwrap();
654 if let Some(conn) = conns.get_mut(&(src_port, dst_port)) {
655 conn.update_peer_credit(buf_alloc, fwd_cnt);
656 }
657 }
658 Some(VsockOp::CreditRequest) => {
659 tracing::trace!(
660 "Vsock TX: OP_CREDIT_REQUEST from port {} to port {}",
661 src_port,
662 dst_port
663 );
664 let conns = self.connections.read().unwrap();
666 if let Some(conn) = conns.get(&(src_port, dst_port)) {
667 let mut update = VsockHeader::new(
668 VsockAddr::new(Self::HOST_CID, dst_port),
669 VsockAddr::new(self.config.guest_cid, src_port),
670 VsockOp::CreditUpdate,
671 );
672 update.buf_alloc = conn.buf_alloc;
673 update.fwd_cnt = conn.fwd_cnt;
674 rx_inject.push((update, Vec::new()));
675 }
676 }
677 Some(VsockOp::Invalid) | None => {
678 let raw_op = { header.op };
679 tracing::warn!(
680 "Vsock TX: unknown/invalid op {} from port {}",
681 raw_op,
682 src_port
683 );
684 }
685 }
686
687 completions.push((*head_idx, data.len() as u32));
688 }
689
690 for (hdr, payload) in rx_inject {
692 if let Err(e) = self.inject_rx_packet(&hdr, &payload, memory) {
693 tracing::warn!("Vsock: failed to inject RX packet: {}", e);
694 }
695 }
696
697 Ok(completions)
698 }
699
700 pub fn process_queue(&mut self, queue_idx: u16, memory: &mut [u8]) -> Result<Vec<(u16, u32)>> {
711 match queue_idx {
712 1 => self.process_tx_queue(memory),
713 _ => Ok(Vec::new()),
714 }
715 }
716
717 pub fn inject_rx_packet(
729 &mut self,
730 header: &VsockHeader,
731 data: &[u8],
732 memory: &mut [u8],
733 ) -> Result<()> {
734 let queue = self
735 .rx_queue
736 .as_mut()
737 .ok_or_else(|| VirtioError::NotReady("RX queue not ready".into()))?;
738
739 let (head_idx, chain) = queue
740 .pop_avail()
741 .ok_or_else(|| VirtioError::InvalidQueue("No available RX descriptors".into()))?;
742
743 let header_bytes = header.to_bytes();
744 let total_len = header_bytes.len() + data.len();
745 let mut frame = Vec::with_capacity(total_len);
746 frame.extend_from_slice(&header_bytes);
747 frame.extend_from_slice(data);
748
749 let mut written = 0usize;
750 for desc in chain {
751 if !desc.is_write_only() {
752 continue;
753 }
754 let start = desc.addr as usize;
755 let remaining = frame.len().saturating_sub(written);
756 let to_write = remaining.min(desc.len as usize);
757 if to_write == 0 {
758 continue;
759 }
760 let end = start + to_write;
761 if end > memory.len() {
762 return Err(VirtioError::MemoryError(
763 "RX descriptor points outside guest memory".into(),
764 ));
765 }
766 memory[start..end].copy_from_slice(&frame[written..written + to_write]);
767 written += to_write;
768 }
769
770 queue.push_used(head_idx, written as u32);
771 Ok(())
772 }
773
774 #[allow(clippy::too_many_lines)]
798 pub fn poll_rx_injection(
799 &mut self,
800 rx_qcfg: &QueueConfig,
801 tx_qcfg: Option<&QueueConfig>,
802 ) -> bool {
803 use std::os::fd::AsRawFd;
804
805 use crate::manager::{RxOps, TX_BUFFER_SIZE};
806
807 let Some(ctx) = self.ctx.clone() else {
808 return false;
809 };
810 let Some(conns) = self.conn_mgr.clone() else {
811 return false;
812 };
813 let mem_arc = ctx.mem.clone();
814 let gpa_base_usize = mem_arc.gpa_base();
815 let mem_len = mem_arc.len();
816
817 let mut injected = false;
818
819 {
823 let connected_fds = conns
824 .lock()
825 .map(|mgr| mgr.connected_fds())
826 .unwrap_or_default();
827
828 static LAST_COUNT: std::sync::atomic::AtomicUsize =
830 std::sync::atomic::AtomicUsize::new(0);
831 let count = connected_fds.len();
832 if count != LAST_COUNT.swap(count, std::sync::atomic::Ordering::Relaxed) {
833 tracing::info!("vsock Phase 1: {} connected fds", count);
834 }
835
836 for (conn_id, fd) in &connected_fds {
837 let mut peek_buf = [0u8; 1];
838 let n = unsafe {
842 libc::recv(
843 *fd,
844 peek_buf.as_mut_ptr().cast::<libc::c_void>(),
845 1,
846 libc::MSG_PEEK | libc::MSG_DONTWAIT,
847 )
848 };
849 if n > 0 {
850 tracing::trace!(
851 "vsock Phase 1: data on fd {} for {:?} — enqueue RW",
852 fd,
853 conn_id,
854 );
855 if let Ok(mut mgr) = conns.lock() {
856 mgr.enqueue_rw(*conn_id);
857 }
858 } else if n == 0 {
859 tracing::debug!(
860 "vsock Phase 1: EOF on fd {} for {:?} — enqueue RST",
861 fd,
862 conn_id,
863 );
864 if let Ok(mut mgr) = conns.lock() {
865 mgr.enqueue_reset(*conn_id);
866 }
867 }
868 }
870 }
871
872 if !rx_qcfg.ready || rx_qcfg.size == 0 {
876 return injected;
877 }
878 let Some(rx_desc) = (rx_qcfg.desc_addr as usize).checked_sub(gpa_base_usize) else {
879 return injected;
880 };
881 let Some(rx_avail) = (rx_qcfg.avail_addr as usize).checked_sub(gpa_base_usize) else {
882 return injected;
883 };
884 let Some(rx_used) = (rx_qcfg.used_addr as usize).checked_sub(gpa_base_usize) else {
885 return injected;
886 };
887 let q_size = rx_qcfg.size as usize;
888
889 let Some(guest_mem) = (unsafe { mem_arc.slice_mut(gpa_base_usize, mem_len) }) else {
894 return injected;
895 };
896
897 if rx_avail + 4 > guest_mem.len() {
898 return injected;
899 }
900
901 let mut rxq_starved = false;
907 loop {
908 let avail_idx =
909 u16::from_le_bytes([guest_mem[rx_avail + 2], guest_mem[rx_avail + 3]]) as usize;
910 let used_idx_off = rx_used + 2;
911 let used_idx =
912 u16::from_le_bytes([guest_mem[used_idx_off], guest_mem[used_idx_off + 1]]) as usize;
913
914 if avail_idx == used_idx {
915 if let Ok(mgr) = conns.lock() {
916 if !mgr.backend_rxq.is_empty() {
917 rxq_starved = true;
918 }
919 }
920 break;
921 }
922
923 let conn_id = {
924 let Ok(mut mgr) = conns.lock() else {
925 break;
926 };
927 mgr.backend_rxq.pop_front()
928 };
929 let Some(conn_id) = conn_id else {
930 break; };
932
933 let packet = {
935 let Ok(mut mgr) = conns.lock() else {
936 break;
937 };
938 let Some(conn) = mgr.get_mut(&conn_id) else {
939 continue; };
941
942 if conn.rx_queue.peek() == RxOps::RESET {
943 conn.rx_queue.dequeue();
944 let hdr = VsockHeader::new(
945 VsockAddr::host(conn_id.host_port),
946 VsockAddr::new(conn.guest_cid, conn_id.guest_port),
947 VsockOp::Rst,
948 );
949 let pkt = hdr.to_bytes().to_vec();
950 mgr.remove(&conn_id);
951 pkt
952 } else {
953 let op = conn.rx_queue.dequeue();
954 if op == 0 {
955 continue; }
957
958 match op {
959 RxOps::REQUEST => {
960 let hdr = VsockHeader::new(
961 VsockAddr::host(conn_id.host_port),
962 VsockAddr::new(conn.guest_cid, conn_id.guest_port),
963 VsockOp::Request,
964 );
965 tracing::debug!(
966 "Vsock RX: OP_REQUEST guest_port={} host_port={}",
967 conn_id.guest_port,
968 conn_id.host_port,
969 );
970 hdr.to_bytes().to_vec()
971 }
972 RxOps::RESPONSE => {
973 conn.connect = true;
974 let hdr = VsockHeader::new(
975 VsockAddr::host(conn_id.host_port),
976 VsockAddr::new(conn.guest_cid, conn_id.guest_port),
977 VsockOp::Response,
978 );
979 tracing::debug!(
980 "Vsock RX: OP_RESPONSE guest_port={} host_port={}",
981 conn_id.guest_port,
982 conn_id.host_port,
983 );
984 hdr.to_bytes().to_vec()
985 }
986 RxOps::RW => {
987 if conn.peer_no_recv() {
988 tracing::trace!(
992 "Vsock RX: skipping RW for half-closed conn guest_port={} host_port={}",
993 conn_id.guest_port,
994 conn_id.host_port,
995 );
996 continue;
997 }
998 if !conn.connect {
999 let hdr = VsockHeader::new(
1000 VsockAddr::host(conn_id.host_port),
1001 VsockAddr::new(conn.guest_cid, conn_id.guest_port),
1002 VsockOp::Rst,
1003 );
1004 mgr.remove(&conn_id);
1005 hdr.to_bytes().to_vec()
1006 } else {
1007 let credit = conn.peer_avail_credit();
1008 if credit == 0 {
1009 let mut hdr = VsockHeader::new(
1010 VsockAddr::host(conn_id.host_port),
1011 VsockAddr::new(conn.guest_cid, conn_id.guest_port),
1012 VsockOp::CreditRequest,
1013 );
1014 hdr.buf_alloc = TX_BUFFER_SIZE;
1015 hdr.fwd_cnt = conn.fwd_cnt.0;
1016 conn.rx_queue.enqueue(RxOps::RW);
1021 conn.note_credit_request_sent();
1022 hdr.to_bytes().to_vec()
1023 } else {
1024 let fd = conn.internal_fd.as_raw_fd();
1025 let max_read = credit.min(4096);
1026 let mut buf = vec![0u8; max_read];
1027 let n = unsafe {
1031 libc::read(
1032 fd,
1033 buf.as_mut_ptr().cast::<libc::c_void>(),
1034 max_read,
1035 )
1036 };
1037 if n <= 0 {
1038 if n == 0 {
1039 let mut hdr = VsockHeader::new(
1040 VsockAddr::host(conn_id.host_port),
1041 VsockAddr::new(conn.guest_cid, conn_id.guest_port),
1042 VsockOp::Shutdown,
1043 );
1044 hdr.flags = 3; hdr.buf_alloc = TX_BUFFER_SIZE;
1046 hdr.fwd_cnt = conn.fwd_cnt.0;
1047 hdr.to_bytes().to_vec()
1048 } else {
1049 continue; }
1051 } else {
1052 let data = &buf[..n as usize];
1053 let mut hdr = VsockHeader::new(
1054 VsockAddr::host(conn_id.host_port),
1055 VsockAddr::new(conn.guest_cid, conn_id.guest_port),
1056 VsockOp::Rw,
1057 );
1058 hdr.len = data.len() as u32;
1059 hdr.buf_alloc = TX_BUFFER_SIZE;
1060 hdr.fwd_cnt = conn.fwd_cnt.0;
1061
1062 conn.record_rx(data.len() as u32);
1063 conn.maybe_request_credit();
1068
1069 let hdr_bytes = hdr.to_bytes();
1070 let mut pkt =
1071 Vec::with_capacity(VsockHeader::SIZE + data.len());
1072 pkt.extend_from_slice(&hdr_bytes[..VsockHeader::SIZE]);
1073 pkt.extend_from_slice(data);
1074
1075 tracing::debug!(
1076 "Vsock RX: OP_RW {} bytes guest_port={} host_port={} fwd_cnt={}",
1077 data.len(),
1078 conn_id.guest_port,
1079 conn_id.host_port,
1080 conn.fwd_cnt.0,
1081 );
1082 pkt
1083 }
1084 }
1085 }
1086 }
1087 RxOps::CREDIT_UPDATE => {
1088 let mut hdr = VsockHeader::new(
1089 VsockAddr::host(conn_id.host_port),
1090 VsockAddr::new(conn.guest_cid, conn_id.guest_port),
1091 VsockOp::CreditUpdate,
1092 );
1093 hdr.buf_alloc = TX_BUFFER_SIZE;
1094 hdr.fwd_cnt = conn.fwd_cnt.0;
1095 conn.mark_credit_sent();
1096 hdr.to_bytes().to_vec()
1097 }
1098 RxOps::CREDIT_REQUEST => {
1099 let mut hdr = VsockHeader::new(
1104 VsockAddr::host(conn_id.host_port),
1105 VsockAddr::new(conn.guest_cid, conn_id.guest_port),
1106 VsockOp::CreditRequest,
1107 );
1108 hdr.buf_alloc = TX_BUFFER_SIZE;
1109 hdr.fwd_cnt = conn.fwd_cnt.0;
1110 tracing::debug!(
1111 "Vsock RX: OP_CREDIT_REQUEST guest_port={} host_port={}",
1112 conn_id.guest_port,
1113 conn_id.host_port,
1114 );
1115 hdr.to_bytes().to_vec()
1116 }
1117 _ => continue,
1118 }
1119 }
1120 };
1121
1122 let written = Self::write_to_rx_descriptor(
1124 guest_mem,
1125 rx_desc,
1126 rx_avail,
1127 rx_used,
1128 q_size,
1129 gpa_base_usize,
1130 &packet,
1131 );
1132
1133 if written > 0 {
1134 injected = true;
1135
1136 if let Ok(mut mgr) = conns.lock() {
1139 if let Some(conn) = mgr.get_mut(&conn_id) {
1140 if let Some(tx) = conn.injected_notify.take() {
1141 let _ = tx.send(());
1142 }
1143 }
1144 }
1145 }
1146
1147 if let Ok(mut mgr) = conns.lock() {
1149 if let Some(conn) = mgr.get(&conn_id) {
1150 if conn.rx_queue.pending() {
1151 mgr.backend_rxq.push_back(conn_id);
1152 }
1153 }
1154 }
1155 }
1156
1157 if rxq_starved {
1158 injected = true;
1159 }
1160
1161 let _ = guest_mem;
1165
1166 if let Some(tx_qcfg) = tx_qcfg {
1170 let Some(tx_mem) = (unsafe { mem_arc.slice_mut(gpa_base_usize, mem_len) }) else {
1173 return injected;
1174 };
1175 match <Self as VirtioDevice>::process_queue(self, 1, tx_mem, tx_qcfg) {
1179 Ok(completions) if !completions.is_empty() => {
1180 tracing::trace!("Vsock TX poll: {} completions", completions.len());
1181 injected = true;
1182
1183 if let Ok(mut mgr) = conns.lock() {
1186 let ids: Vec<_> = mgr.connections_with_pending_rx();
1187 for id in ids {
1188 mgr.backend_rxq.push_back(id);
1189 }
1190 }
1191 }
1192 Err(e) => {
1193 tracing::warn!("Vsock TX poll error: {e}");
1194 }
1195 _ => {}
1196 }
1197 }
1198
1199 injected
1200 }
1201
1202 #[allow(clippy::too_many_arguments)]
1209 fn write_to_rx_descriptor(
1210 guest_mem: &mut [u8],
1211 desc_addr: usize,
1212 avail_addr: usize,
1213 used_addr: usize,
1214 q_size: usize,
1215 gpa_base: usize,
1216 packet: &[u8],
1217 ) -> usize {
1218 let avail_idx =
1219 u16::from_le_bytes([guest_mem[avail_addr + 2], guest_mem[avail_addr + 3]]) as usize;
1220 let used_idx_off = used_addr + 2;
1221 let used_idx =
1222 u16::from_le_bytes([guest_mem[used_idx_off], guest_mem[used_idx_off + 1]]) as usize;
1223
1224 if avail_idx == used_idx {
1225 return 0; }
1227
1228 let ring_off = avail_addr + 4 + 2 * (used_idx % q_size);
1229 if ring_off + 2 > guest_mem.len() {
1230 return 0;
1231 }
1232 let head_idx = u16::from_le_bytes([guest_mem[ring_off], guest_mem[ring_off + 1]]) as usize;
1233
1234 let mut written = 0;
1237 let mut idx = head_idx;
1238 for _ in 0..q_size {
1239 let d_off = desc_addr + idx * 16;
1240 if d_off + 16 > guest_mem.len() {
1241 break;
1242 }
1243 let addr_gpa =
1244 u64::from_le_bytes(guest_mem[d_off..d_off + 8].try_into().unwrap()) as usize;
1245 let len =
1246 u32::from_le_bytes(guest_mem[d_off + 8..d_off + 12].try_into().unwrap()) as usize;
1247 let flags = u16::from_le_bytes(guest_mem[d_off + 12..d_off + 14].try_into().unwrap());
1248 let next = u16::from_le_bytes(guest_mem[d_off + 14..d_off + 16].try_into().unwrap());
1249 let Some(addr) = addr_gpa.checked_sub(gpa_base) else {
1250 continue;
1251 };
1252
1253 if flags & 2 != 0 && addr + len <= guest_mem.len() {
1254 let remaining = packet.len().saturating_sub(written);
1255 let to_write = remaining.min(len);
1256 if to_write > 0 {
1257 guest_mem[addr..addr + to_write]
1258 .copy_from_slice(&packet[written..written + to_write]);
1259 written += to_write;
1260 }
1261 }
1262
1263 if flags & 1 == 0 || written >= packet.len() {
1264 break;
1265 }
1266 idx = next as usize;
1267 }
1268
1269 if written == 0 {
1270 return 0;
1271 }
1272
1273 let used_entry = used_addr + 4 + (used_idx % q_size) * 8;
1275 if used_entry + 8 <= guest_mem.len() {
1276 guest_mem[used_entry..used_entry + 4].copy_from_slice(&(head_idx as u32).to_le_bytes());
1277 guest_mem[used_entry + 4..used_entry + 8]
1278 .copy_from_slice(&(written as u32).to_le_bytes());
1279 std::sync::atomic::fence(std::sync::atomic::Ordering::Release);
1280 let new_used = (used_idx + 1) as u16;
1281 guest_mem[used_idx_off..used_idx_off + 2].copy_from_slice(&new_used.to_le_bytes());
1282 }
1283
1284 written
1285 }
1286}
1287
1288impl VirtioDevice for VirtioVsock {
1289 fn device_id(&self) -> VirtioDeviceId {
1290 VirtioDeviceId::Vsock
1291 }
1292
1293 fn features(&self) -> u64 {
1294 self.features
1295 }
1296
1297 fn ack_features(&mut self, features: u64) {
1298 self.acked_features = self.features & features;
1299 }
1300
1301 fn read_config(&self, offset: u64, data: &mut [u8]) {
1302 let config_data = self.config.guest_cid.to_le_bytes();
1305
1306 let offset = offset as usize;
1307 let len = data.len().min(config_data.len().saturating_sub(offset));
1308 if len > 0 {
1309 data[..len].copy_from_slice(&config_data[offset..offset + len]);
1310 }
1311 }
1312
1313 fn write_config(&mut self, _offset: u64, _data: &[u8]) {
1314 }
1316
1317 fn activate(&mut self) -> Result<()> {
1318 self.rx_queue = Some(VirtQueue::new(256)?);
1320 self.tx_queue = Some(VirtQueue::new(256)?);
1321 self.event_queue = Some(VirtQueue::new(64)?);
1322
1323 if self.backend.is_none() {
1325 tracing::info!("Vsock: using loopback backend (no backend configured)");
1326 self.backend = Some(Arc::new(Mutex::new(LoopbackBackend::new())));
1327 }
1328 tracing::info!(
1329 "Vsock device activated, guest CID: {}",
1330 self.config.guest_cid
1331 );
1332 Ok(())
1333 }
1334
1335 fn reset(&mut self) {
1336 self.acked_features = 0;
1337 self.connections.write().unwrap().clear();
1338 self.backend = None;
1339 self.rx_queue = None;
1340 self.tx_queue = None;
1341 self.event_queue = None;
1342 self.last_avail_idx_tx = 0;
1343 self.last_avail_idx_rx = 0;
1344 }
1345
1346 fn process_queue(
1347 &mut self,
1348 queue_idx: u16,
1349 memory: &mut [u8],
1350 queue_config: &QueueConfig,
1351 ) -> Result<Vec<(u16, u32)>> {
1352 if queue_idx != 1 || !queue_config.ready || queue_config.size == 0 {
1356 return Ok(Vec::new());
1357 }
1358
1359 let gpa_base = queue_config.gpa_base as usize;
1362 let desc_addr = (queue_config.desc_addr as usize)
1363 .checked_sub(gpa_base)
1364 .ok_or_else(|| {
1365 tracing::warn!(
1366 "invalid desc GPA {:#x} below ram base {:#x}",
1367 queue_config.desc_addr,
1368 gpa_base
1369 );
1370 VirtioError::InvalidQueue("desc GPA below ram base".into())
1371 })?;
1372 let avail_addr = (queue_config.avail_addr as usize)
1373 .checked_sub(gpa_base)
1374 .ok_or_else(|| {
1375 tracing::warn!(
1376 "invalid avail GPA {:#x} below ram base {:#x}",
1377 queue_config.avail_addr,
1378 gpa_base
1379 );
1380 VirtioError::InvalidQueue("avail GPA below ram base".into())
1381 })?;
1382 let used_addr = (queue_config.used_addr as usize)
1383 .checked_sub(gpa_base)
1384 .ok_or_else(|| {
1385 tracing::warn!(
1386 "invalid used GPA {:#x} below ram base {:#x}",
1387 queue_config.used_addr,
1388 gpa_base
1389 );
1390 VirtioError::InvalidQueue("used GPA below ram base".into())
1391 })?;
1392 let q_size = queue_config.size as usize;
1393
1394 if avail_addr + 4 > memory.len() {
1395 return Ok(Vec::new());
1396 }
1397 let avail_idx =
1398 u16::from_le_bytes([memory[avail_addr + 2], memory[avail_addr + 3]]) as usize;
1399
1400 let mut current_avail = self.last_avail_idx_tx;
1401 let mut completions = Vec::new();
1402
1403 while current_avail != avail_idx {
1404 let ring_off = avail_addr + 4 + 2 * (current_avail % q_size);
1405 if ring_off + 2 > memory.len() {
1406 break;
1407 }
1408 let head_idx = u16::from_le_bytes([memory[ring_off], memory[ring_off + 1]]) as usize;
1409
1410 let mut packet_data = Vec::new();
1412 let mut idx = head_idx;
1413 for _ in 0..q_size {
1414 let d_off = desc_addr + idx * 16;
1415 if d_off + 16 > memory.len() {
1416 break;
1417 }
1418 let addr = match (u64::from_le_bytes(memory[d_off..d_off + 8].try_into().unwrap())
1419 as usize)
1420 .checked_sub(gpa_base)
1421 {
1422 Some(a) => a,
1423 None => continue,
1424 };
1425 let len =
1426 u32::from_le_bytes(memory[d_off + 8..d_off + 12].try_into().unwrap()) as usize;
1427 let flags = u16::from_le_bytes(memory[d_off + 12..d_off + 14].try_into().unwrap());
1428 let next = u16::from_le_bytes(memory[d_off + 14..d_off + 16].try_into().unwrap());
1429
1430 if flags & arcbox_virtio_core::queue::flags::WRITE == 0
1432 && addr + len <= memory.len()
1433 {
1434 packet_data.extend_from_slice(&memory[addr..addr + len]);
1435 }
1436
1437 if flags & arcbox_virtio_core::queue::flags::NEXT == 0 {
1438 break;
1439 }
1440 idx = next as usize;
1441 }
1442
1443 if packet_data.len() >= VsockHeader::SIZE {
1445 if let Some(hdr) = VsockHeader::from_bytes(&packet_data[..VsockHeader::SIZE]) {
1446 let op_val = { hdr.op };
1447 let src_cid = { hdr.src_cid };
1448 let dst_cid = { hdr.dst_cid };
1449 let src_port = { hdr.src_port };
1450 let dst_port = { hdr.dst_port };
1451 tracing::info!(
1452 "Vsock TX: op={} src={}:{} dst={}:{} len={} (packet_data={} bytes)",
1453 op_val,
1454 src_cid,
1455 src_port,
1456 dst_cid,
1457 dst_port,
1458 { hdr.len },
1459 packet_data.len(),
1460 );
1461
1462 let payload = &packet_data[VsockHeader::SIZE..];
1463 if let Some(conns_arc) = self.conns.clone() {
1464 if let Ok(mut conns) = conns_arc.lock() {
1465 self.handle_tx_packet_with_fds(&hdr, payload, Some(&mut *conns));
1466 }
1467 } else {
1468 self.handle_tx_packet_with_fds(&hdr, payload, None);
1469 }
1470 }
1471 } else {
1472 tracing::warn!(
1473 "Vsock TX: packet too short ({} bytes < {} header), skipping",
1474 packet_data.len(),
1475 VsockHeader::SIZE,
1476 );
1477 }
1478
1479 let used_idx_off = used_addr + 2;
1481 let used_idx = u16::from_le_bytes([memory[used_idx_off], memory[used_idx_off + 1]]);
1482 let used_entry = used_addr + 4 + ((used_idx as usize) % q_size) * 8;
1483 if used_entry + 8 <= memory.len() {
1484 memory[used_entry..used_entry + 4]
1485 .copy_from_slice(&(head_idx as u32).to_le_bytes());
1486 memory[used_entry + 4..used_entry + 8]
1487 .copy_from_slice(&(packet_data.len() as u32).to_le_bytes());
1488 std::sync::atomic::fence(std::sync::atomic::Ordering::Release);
1489 let new_used = used_idx.wrapping_add(1);
1490 memory[used_idx_off..used_idx_off + 2].copy_from_slice(&new_used.to_le_bytes());
1491 }
1492
1493 let avail_event_off = used_addr + 4 + 8 * q_size;
1495 if avail_event_off + 2 <= memory.len() {
1496 let ae = ((current_avail + 1) as u16).to_le_bytes();
1497 memory[avail_event_off] = ae[0];
1498 memory[avail_event_off + 1] = ae[1];
1499 }
1500
1501 completions.push((head_idx as u16, packet_data.len() as u32));
1502 current_avail += 1;
1503 }
1504
1505 self.last_avail_idx_tx = current_avail;
1506 Ok(completions)
1507 }
1508}
1509
1510#[cfg(test)]
1511mod tests {
1512 use super::*;
1513
1514 #[test]
1515 fn test_vsock_config_default() {
1516 let config = VsockConfig::default();
1517 assert_eq!(config.guest_cid, 3);
1518 }
1519
1520 #[test]
1521 fn test_vsock_config_custom() {
1522 let config = VsockConfig { guest_cid: 100 };
1523 assert_eq!(config.guest_cid, 100);
1524 }
1525
1526 #[test]
1527 fn test_vsock_config_clone() {
1528 let config = VsockConfig { guest_cid: 42 };
1529 let cloned = config.clone();
1530 assert_eq!(cloned.guest_cid, 42);
1531 }
1532
1533 #[test]
1534 fn test_vsock_new() {
1535 let vsock = VirtioVsock::new(VsockConfig::default());
1536 assert_eq!(vsock.guest_cid(), 3);
1537 }
1538
1539 #[test]
1540 fn test_vsock_device_id() {
1541 let vsock = VirtioVsock::new(VsockConfig::default());
1542 assert_eq!(vsock.device_id(), VirtioDeviceId::Vsock);
1543 }
1544
1545 #[test]
1546 fn test_vsock_features() {
1547 let vsock = VirtioVsock::new(VsockConfig::default());
1548 let features = vsock.features();
1549 assert!(features & VirtioVsock::FEATURE_STREAM != 0);
1550 }
1551
1552 #[test]
1553 fn test_vsock_ack_features() {
1554 let mut vsock = VirtioVsock::new(VsockConfig::default());
1555
1556 vsock.ack_features(VirtioVsock::FEATURE_STREAM);
1557 assert_eq!(vsock.acked_features, VirtioVsock::FEATURE_STREAM);
1558 }
1559
1560 #[test]
1561 fn test_vsock_ack_unsupported_feature() {
1562 let mut vsock = VirtioVsock::new(VsockConfig::default());
1563
1564 vsock.ack_features(VirtioVsock::FEATURE_SEQPACKET);
1566 assert_eq!(vsock.acked_features, 0);
1567 }
1568
1569 #[test]
1570 fn test_vsock_read_config() {
1571 let config = VsockConfig {
1572 guest_cid: 0x12345678,
1573 };
1574 let vsock = VirtioVsock::new(config);
1575
1576 let mut data = [0u8; 8];
1577 vsock.read_config(0, &mut data);
1578
1579 let cid = u64::from_le_bytes(data);
1580 assert_eq!(cid, 0x12345678);
1581 }
1582
1583 #[test]
1584 fn test_vsock_read_config_partial() {
1585 let config = VsockConfig {
1586 guest_cid: 0xDEADBEEF,
1587 };
1588 let vsock = VirtioVsock::new(config);
1589
1590 let mut data = [0u8; 4];
1591 vsock.read_config(0, &mut data);
1592
1593 let low_bytes = u32::from_le_bytes(data);
1594 assert_eq!(low_bytes, 0xDEADBEEF);
1595 }
1596
1597 #[test]
1598 fn test_vsock_read_config_offset() {
1599 let config = VsockConfig {
1600 guest_cid: 0xAABBCCDD_11223344,
1601 };
1602 let vsock = VirtioVsock::new(config);
1603
1604 let mut data = [0u8; 4];
1605 vsock.read_config(4, &mut data);
1606
1607 let high_bytes = u32::from_le_bytes(data);
1608 assert_eq!(high_bytes, 0xAABBCCDD);
1609 }
1610
1611 #[test]
1612 fn test_vsock_read_config_beyond() {
1613 let vsock = VirtioVsock::new(VsockConfig::default());
1614
1615 let mut data = [0xFFu8; 4];
1616 vsock.read_config(100, &mut data);
1617 }
1618
1619 #[test]
1620 fn test_vsock_write_config_noop() {
1621 let mut vsock = VirtioVsock::new(VsockConfig { guest_cid: 42 });
1622
1623 vsock.write_config(0, &[0xFF; 8]);
1624
1625 assert_eq!(vsock.guest_cid(), 42);
1626 }
1627
1628 #[test]
1629 fn test_vsock_activate() {
1630 let mut vsock = VirtioVsock::new(VsockConfig::default());
1631 assert!(vsock.activate().is_ok());
1632 }
1633
1634 #[test]
1635 fn test_vsock_reset() {
1636 let mut vsock = VirtioVsock::new(VsockConfig::default());
1637 vsock.ack_features(VirtioVsock::FEATURE_STREAM);
1638 assert_ne!(vsock.acked_features, 0);
1639
1640 vsock.reset();
1641 assert_eq!(vsock.acked_features, 0);
1642 }
1643
1644 #[test]
1645 fn test_vsock_constants() {
1646 assert_eq!(VirtioVsock::HOST_CID, 2);
1647 assert_eq!(VirtioVsock::RESERVED_CID, 1);
1648 assert_eq!(VirtioVsock::FEATURE_STREAM, 1 << 0);
1649 assert_eq!(VirtioVsock::FEATURE_SEQPACKET, 1 << 1);
1650 }
1651
1652 #[test]
1653 fn test_vsock_with_loopback_backend() {
1654 let vsock = VirtioVsock::with_backend(VsockConfig::default(), LoopbackBackend::new());
1655 assert_eq!(vsock.guest_cid(), 3);
1656 assert_eq!(vsock.connection_count(), 0);
1657 }
1658
1659 #[test]
1660 fn test_vsock_connect_send_recv() {
1661 let vsock = VirtioVsock::with_backend(VsockConfig::default(), LoopbackBackend::new());
1662
1663 vsock.handle_connect(1000, 80).unwrap();
1664 assert_eq!(vsock.connection_count(), 1);
1665
1666 let data = b"GET / HTTP/1.1";
1667 let sent = vsock.handle_send(1000, 80, data).unwrap();
1668 assert_eq!(sent, data.len());
1669
1670 let mut buf = [0u8; 64];
1671 let received = vsock.handle_recv(1000, 80, &mut buf).unwrap();
1672 assert_eq!(received, data.len());
1673 assert_eq!(&buf[..received], data);
1674
1675 vsock.handle_close(1000, 80).unwrap();
1676 assert_eq!(vsock.connection_count(), 0);
1677 }
1678
1679 #[test]
1680 fn test_vsock_activate_creates_queues() {
1681 let mut vsock = VirtioVsock::new(VsockConfig::default());
1682 assert!(vsock.rx_queue.is_none());
1683 assert!(vsock.tx_queue.is_none());
1684 assert!(vsock.event_queue.is_none());
1685
1686 vsock.activate().unwrap();
1687
1688 assert!(vsock.rx_queue.is_some());
1689 assert!(vsock.tx_queue.is_some());
1690 assert!(vsock.event_queue.is_some());
1691 }
1692
1693 #[test]
1694 fn test_vsock_reset_clears_queues() {
1695 let mut vsock = VirtioVsock::new(VsockConfig::default());
1696 vsock.activate().unwrap();
1697 assert!(vsock.rx_queue.is_some());
1698
1699 vsock.reset();
1700 assert!(vsock.rx_queue.is_none());
1701 assert!(vsock.tx_queue.is_none());
1702 assert!(vsock.event_queue.is_none());
1703 }
1704
1705 fn setup_tx_packet(
1709 vsock: &mut VirtioVsock,
1710 guest_addr: usize,
1711 header: &VsockHeader,
1712 payload: &[u8],
1713 memory: &mut Vec<u8>,
1714 ) {
1715 let header_bytes = header.to_bytes();
1716 let total = header_bytes.len() + payload.len();
1717
1718 if memory.len() < guest_addr + total {
1719 memory.resize(guest_addr + total, 0);
1720 }
1721
1722 memory[guest_addr..guest_addr + header_bytes.len()].copy_from_slice(&header_bytes);
1723 if !payload.is_empty() {
1724 memory[guest_addr + header_bytes.len()..guest_addr + total].copy_from_slice(payload);
1725 }
1726
1727 let queue = vsock.tx_queue.as_mut().unwrap();
1728 let desc = arcbox_virtio_core::queue::Descriptor {
1729 addr: guest_addr as u64,
1730 len: total as u32,
1731 flags: 0, next: 0,
1733 };
1734 queue.set_descriptor(0, desc).unwrap();
1735 queue.add_avail(0).unwrap();
1736 }
1737
1738 #[test]
1739 fn test_process_tx_queue_not_ready() {
1740 let mut vsock = VirtioVsock::new(VsockConfig::default());
1741 let mut memory = vec![0u8; 1024];
1742 let result = vsock.process_tx_queue(&mut memory);
1743 assert!(result.is_err());
1744 }
1745
1746 #[test]
1747 fn test_process_tx_queue_empty() {
1748 let mut vsock = VirtioVsock::new(VsockConfig::default());
1749 vsock.activate().unwrap();
1750
1751 let mut memory = vec![0u8; 4096];
1752 let completions = vsock.process_tx_queue(&mut memory).unwrap();
1753 assert!(completions.is_empty());
1754 }
1755
1756 #[test]
1757 fn test_process_tx_queue_connect_request() {
1758 let mut vsock = VirtioVsock::with_backend(VsockConfig::default(), LoopbackBackend::new());
1759 vsock.activate().unwrap();
1760
1761 let mut memory = vec![0u8; 4096];
1762
1763 let header = VsockHeader::new(
1765 VsockAddr::new(3, 1000),
1766 VsockAddr::new(VirtioVsock::HOST_CID, 80),
1767 VsockOp::Request,
1768 );
1769 setup_tx_packet(&mut vsock, 0x100, &header, &[], &mut memory);
1770
1771 {
1773 let rx_queue = vsock.rx_queue.as_mut().unwrap();
1774 let rx_desc = arcbox_virtio_core::queue::Descriptor {
1775 addr: 0x800,
1776 len: 256,
1777 flags: arcbox_virtio_core::queue::flags::WRITE,
1778 next: 0,
1779 };
1780 rx_queue.set_descriptor(0, rx_desc).unwrap();
1781 rx_queue.add_avail(0).unwrap();
1782 }
1783
1784 let completions = vsock.process_tx_queue(&mut memory).unwrap();
1785 assert_eq!(completions.len(), 1);
1786 assert_eq!(completions[0].0, 0); assert_eq!(vsock.connection_count(), 1);
1789
1790 let resp_header = VsockHeader::from_bytes(&memory[0x800..0x800 + VsockHeader::SIZE]);
1791 assert!(resp_header.is_some());
1792 let resp = resp_header.unwrap();
1793 assert_eq!(resp.operation(), Some(VsockOp::Response));
1794 let resp_src_cid = resp.src_cid;
1795 let resp_dst_cid = resp.dst_cid;
1796 assert_eq!(resp_src_cid, VirtioVsock::HOST_CID);
1797 assert_eq!(resp_dst_cid, 3);
1798 }
1799
1800 #[test]
1801 fn test_process_tx_queue_data_rw() {
1802 let mut vsock = VirtioVsock::with_backend(VsockConfig::default(), LoopbackBackend::new());
1803 vsock.activate().unwrap();
1804
1805 vsock.handle_connect(1000, 80).unwrap();
1806
1807 let mut memory = vec![0u8; 4096];
1808
1809 let payload = b"hello world";
1810 let mut header = VsockHeader::new(
1811 VsockAddr::new(3, 1000),
1812 VsockAddr::new(VirtioVsock::HOST_CID, 80),
1813 VsockOp::Rw,
1814 );
1815 header.len = payload.len() as u32;
1816 setup_tx_packet(&mut vsock, 0x100, &header, payload, &mut memory);
1817
1818 let completions = vsock.process_tx_queue(&mut memory).unwrap();
1819 assert_eq!(completions.len(), 1);
1820
1821 let backend = vsock.backend.as_ref().unwrap();
1822 let mut backend = backend.lock().unwrap();
1823 let addr = VsockAddr::new(3, 1000);
1824 assert!(backend.has_pending_data(addr));
1825
1826 let mut buf = [0u8; 64];
1827 let n = backend.on_recv(addr, &mut buf).unwrap();
1828 assert_eq!(&buf[..n], payload);
1829 }
1830
1831 #[test]
1832 fn test_process_tx_queue_shutdown() {
1833 let mut vsock = VirtioVsock::with_backend(VsockConfig::default(), LoopbackBackend::new());
1834 vsock.activate().unwrap();
1835
1836 vsock.handle_connect(2000, 443).unwrap();
1837 assert_eq!(vsock.connection_count(), 1);
1838
1839 let mut memory = vec![0u8; 4096];
1840
1841 let header = VsockHeader::new(
1842 VsockAddr::new(3, 2000),
1843 VsockAddr::new(VirtioVsock::HOST_CID, 443),
1844 VsockOp::Shutdown,
1845 );
1846 setup_tx_packet(&mut vsock, 0x100, &header, &[], &mut memory);
1847
1848 {
1850 let rx_queue = vsock.rx_queue.as_mut().unwrap();
1851 let rx_desc = arcbox_virtio_core::queue::Descriptor {
1852 addr: 0x800,
1853 len: 256,
1854 flags: arcbox_virtio_core::queue::flags::WRITE,
1855 next: 0,
1856 };
1857 rx_queue.set_descriptor(0, rx_desc).unwrap();
1858 rx_queue.add_avail(0).unwrap();
1859 }
1860
1861 let completions = vsock.process_tx_queue(&mut memory).unwrap();
1862 assert_eq!(completions.len(), 1);
1863
1864 assert_eq!(vsock.connection_count(), 0);
1865
1866 let rst_header = VsockHeader::from_bytes(&memory[0x800..0x800 + VsockHeader::SIZE]);
1867 assert!(rst_header.is_some());
1868 assert_eq!(rst_header.unwrap().operation(), Some(VsockOp::Rst));
1869 }
1870
1871 #[test]
1872 fn test_process_tx_queue_credit_update() {
1873 let mut vsock = VirtioVsock::with_backend(VsockConfig::default(), LoopbackBackend::new());
1874 vsock.activate().unwrap();
1875
1876 vsock.handle_connect(3000, 22).unwrap();
1877
1878 let mut memory = vec![0u8; 4096];
1879
1880 let mut header = VsockHeader::new(
1881 VsockAddr::new(3, 3000),
1882 VsockAddr::new(VirtioVsock::HOST_CID, 22),
1883 VsockOp::CreditUpdate,
1884 );
1885 header.buf_alloc = 131_072;
1886 header.fwd_cnt = 500;
1887 setup_tx_packet(&mut vsock, 0x100, &header, &[], &mut memory);
1888
1889 let completions = vsock.process_tx_queue(&mut memory).unwrap();
1890 assert_eq!(completions.len(), 1);
1891
1892 let conns = vsock.connections.read().unwrap();
1893 let conn = conns.get(&(3000, 22)).unwrap();
1894 assert_eq!(conn.peer_buf_alloc, 131_072);
1895 assert_eq!(conn.peer_fwd_cnt, 500);
1896 }
1897
1898 #[test]
1899 fn test_process_queue_dispatches_tx() {
1900 let mut vsock = VirtioVsock::with_backend(VsockConfig::default(), LoopbackBackend::new());
1901 vsock.activate().unwrap();
1902
1903 let mut memory = vec![0u8; 4096];
1904
1905 let completions = vsock.process_queue(1, &mut memory).unwrap();
1906 assert!(completions.is_empty());
1907 }
1908
1909 #[test]
1910 fn test_process_queue_unknown_index() {
1911 let mut vsock = VirtioVsock::new(VsockConfig::default());
1912 vsock.activate().unwrap();
1913
1914 let mut memory = vec![0u8; 1024];
1915 let completions = vsock.process_queue(0, &mut memory).unwrap();
1916 assert!(completions.is_empty());
1917 let completions = vsock.process_queue(2, &mut memory).unwrap();
1918 assert!(completions.is_empty());
1919 let completions = vsock.process_queue(99, &mut memory).unwrap();
1920 assert!(completions.is_empty());
1921 }
1922
1923 #[test]
1924 fn test_inject_rx_packet_not_ready() {
1925 let mut vsock = VirtioVsock::new(VsockConfig::default());
1926 let header = VsockHeader::new(
1927 VsockAddr::host(80),
1928 VsockAddr::new(3, 1000),
1929 VsockOp::Response,
1930 );
1931 let mut memory = vec![0u8; 1024];
1932 let result = vsock.inject_rx_packet(&header, &[], &mut memory);
1933 assert!(result.is_err());
1934 }
1935
1936 #[test]
1937 fn test_inject_rx_packet_no_descriptors() {
1938 let mut vsock = VirtioVsock::new(VsockConfig::default());
1939 vsock.activate().unwrap();
1940
1941 let header = VsockHeader::new(
1942 VsockAddr::host(80),
1943 VsockAddr::new(3, 1000),
1944 VsockOp::Response,
1945 );
1946 let mut memory = vec![0u8; 1024];
1947 let result = vsock.inject_rx_packet(&header, &[], &mut memory);
1948 assert!(result.is_err());
1949 }
1950
1951 #[test]
1952 fn test_inject_rx_packet_with_data() {
1953 let mut vsock = VirtioVsock::new(VsockConfig::default());
1954 vsock.activate().unwrap();
1955
1956 let mut memory = vec![0u8; 4096];
1957
1958 {
1959 let rx_queue = vsock.rx_queue.as_mut().unwrap();
1960 let desc = arcbox_virtio_core::queue::Descriptor {
1961 addr: 0x200,
1962 len: 512,
1963 flags: arcbox_virtio_core::queue::flags::WRITE,
1964 next: 0,
1965 };
1966 rx_queue.set_descriptor(0, desc).unwrap();
1967 rx_queue.add_avail(0).unwrap();
1968 }
1969
1970 let payload = b"response data";
1971 let mut header =
1972 VsockHeader::new(VsockAddr::host(80), VsockAddr::new(3, 1000), VsockOp::Rw);
1973 header.len = payload.len() as u32;
1974
1975 vsock
1976 .inject_rx_packet(&header, payload, &mut memory)
1977 .unwrap();
1978
1979 let written_hdr =
1980 VsockHeader::from_bytes(&memory[0x200..0x200 + VsockHeader::SIZE]).unwrap();
1981 assert_eq!(written_hdr.operation(), Some(VsockOp::Rw));
1982 let wh_src_cid = written_hdr.src_cid;
1983 assert_eq!(wh_src_cid, VirtioVsock::HOST_CID);
1984
1985 let payload_start = 0x200 + VsockHeader::SIZE;
1986 assert_eq!(
1987 &memory[payload_start..payload_start + payload.len()],
1988 payload
1989 );
1990 }
1991
1992 fn setup_virtqueue_layout(
1995 memory: &mut Vec<u8>,
1996 base: usize,
1997 q_size: usize,
1998 ) -> (usize, usize, usize) {
1999 let desc_addr = base;
2000 let avail_addr = desc_addr + q_size * 16;
2001 let avail_addr = (avail_addr + 15) & !15;
2002 let avail_size = 4 + 2 * q_size + 2;
2003 let used_addr = avail_addr + avail_size;
2004 let used_addr = (used_addr + 15) & !15;
2005 let used_size = 4 + 8 * q_size + 2;
2006 let total = used_addr + used_size;
2007 if memory.len() < total {
2008 memory.resize(total, 0);
2009 }
2010 (desc_addr, avail_addr, used_addr)
2011 }
2012
2013 fn write_descriptor(
2014 memory: &mut [u8],
2015 desc_addr: usize,
2016 idx: usize,
2017 addr: u64,
2018 len: u32,
2019 flags: u16,
2020 next: u16,
2021 ) {
2022 let off = desc_addr + idx * 16;
2023 memory[off..off + 8].copy_from_slice(&addr.to_le_bytes());
2024 memory[off + 8..off + 12].copy_from_slice(&len.to_le_bytes());
2025 memory[off + 12..off + 14].copy_from_slice(&flags.to_le_bytes());
2026 memory[off + 14..off + 16].copy_from_slice(&next.to_le_bytes());
2027 }
2028
2029 fn avail_ring_push(memory: &mut [u8], avail_addr: usize, q_size: usize, head_idx: u16) {
2030 let avail_idx =
2031 u16::from_le_bytes([memory[avail_addr + 2], memory[avail_addr + 3]]) as usize;
2032 let ring_off = avail_addr + 4 + 2 * (avail_idx % q_size);
2033 memory[ring_off..ring_off + 2].copy_from_slice(&head_idx.to_le_bytes());
2034 let new_idx = (avail_idx + 1) as u16;
2035 memory[avail_addr + 2..avail_addr + 4].copy_from_slice(&new_idx.to_le_bytes());
2036 }
2037
2038 #[test]
2041 fn test_process_queue_guest_memory_op_response() {
2042 let mut vsock = VirtioVsock::new(VsockConfig::default());
2043 vsock.activate().unwrap();
2044
2045 let q_size = 16usize;
2046 let mut memory = vec![0u8; 0x10000];
2047
2048 let (desc_addr, avail_addr, used_addr) =
2049 setup_virtqueue_layout(&mut memory, 0x4000, q_size);
2050
2051 let pkt_addr = 0x8000usize;
2052 let hdr = VsockHeader::new(
2053 VsockAddr::new(3, 1024),
2054 VsockAddr::host(50000),
2055 VsockOp::Response,
2056 );
2057 let hdr_bytes = hdr.to_bytes();
2058 assert_eq!(
2059 hdr_bytes.len(),
2060 44,
2061 "VsockHeader must serialize to 44 bytes"
2062 );
2063 memory[pkt_addr..pkt_addr + 44].copy_from_slice(&hdr_bytes[..44]);
2064
2065 write_descriptor(&mut memory, desc_addr, 0, pkt_addr as u64, 44, 0, 0);
2066 avail_ring_push(&mut memory, avail_addr, q_size, 0);
2067
2068 struct MockConns {
2069 connected: Vec<(u32, u32)>,
2070 credit_updates: Vec<(u32, u32, u32, u32)>,
2071 }
2072 impl VsockHostConnections for MockConns {
2073 fn fd_for(&self, _gp: u32, _hp: u32) -> Option<std::os::unix::io::RawFd> {
2074 None
2075 }
2076 fn mark_connected(&mut self, gp: u32, hp: u32) {
2077 self.connected.push((gp, hp));
2078 }
2079 fn remove_connection(&mut self, _gp: u32, _hp: u32) {}
2080 fn update_peer_credit(&mut self, gp: u32, hp: u32, ba: u32, fc: u32) {
2081 self.credit_updates.push((gp, hp, ba, fc));
2082 }
2083 }
2084
2085 let mock = Arc::new(Mutex::new(MockConns {
2086 connected: Vec::new(),
2087 credit_updates: Vec::new(),
2088 }));
2089
2090 let qcfg = QueueConfig {
2091 desc_addr: desc_addr as u64,
2092 avail_addr: avail_addr as u64,
2093 used_addr: used_addr as u64,
2094 size: q_size as u16,
2095 ready: true,
2096 gpa_base: 0,
2097 };
2098 vsock.bind_connections(mock.clone());
2099
2100 let completions =
2101 <VirtioVsock as VirtioDevice>::process_queue(&mut vsock, 1, &mut memory, &qcfg)
2102 .unwrap();
2103
2104 assert_eq!(
2105 completions.len(),
2106 1,
2107 "Expected 1 completion for OP_RESPONSE"
2108 );
2109 assert_eq!(completions[0].0, 0, "head_idx should be 0");
2110 assert_eq!(completions[0].1, 44, "written bytes should be 44");
2111
2112 let mock_guard = mock.lock().unwrap();
2113 assert_eq!(
2114 mock_guard.connected.len(),
2115 1,
2116 "mark_connected should be called once for OP_RESPONSE"
2117 );
2118 assert_eq!(mock_guard.connected[0], (1024, 50000));
2119
2120 assert_eq!(mock_guard.credit_updates.len(), 1);
2121 assert_eq!(
2122 mock_guard.credit_updates[0],
2123 (1024, 50000, 64 * 1024, 0),
2124 "peer credit should be synced from OP_RESPONSE header"
2125 );
2126 }
2127
2128 #[test]
2131 fn test_process_queue_guest_memory_op_rst() {
2132 let mut vsock = VirtioVsock::new(VsockConfig::default());
2133 vsock.activate().unwrap();
2134
2135 let q_size = 16usize;
2136 let mut memory = vec![0u8; 0x10000];
2137
2138 let (desc_addr, avail_addr, used_addr) =
2139 setup_virtqueue_layout(&mut memory, 0x4000, q_size);
2140
2141 let pkt_addr = 0x8000usize;
2142 let hdr = VsockHeader::new(
2143 VsockAddr::new(3, 1024),
2144 VsockAddr::host(50000),
2145 VsockOp::Rst,
2146 );
2147 memory[pkt_addr..pkt_addr + 44].copy_from_slice(&hdr.to_bytes()[..44]);
2148
2149 write_descriptor(&mut memory, desc_addr, 0, pkt_addr as u64, 44, 0, 0);
2150 avail_ring_push(&mut memory, avail_addr, q_size, 0);
2151
2152 struct MockConns {
2153 removed: Vec<(u32, u32)>,
2154 }
2155 impl VsockHostConnections for MockConns {
2156 fn fd_for(&self, _: u32, _: u32) -> Option<std::os::unix::io::RawFd> {
2157 None
2158 }
2159 fn mark_connected(&mut self, _: u32, _: u32) {}
2160 fn remove_connection(&mut self, gp: u32, hp: u32) {
2161 self.removed.push((gp, hp));
2162 }
2163 }
2164 let mock = Arc::new(Mutex::new(MockConns {
2165 removed: Vec::new(),
2166 }));
2167
2168 let qcfg = QueueConfig {
2169 desc_addr: desc_addr as u64,
2170 avail_addr: avail_addr as u64,
2171 used_addr: used_addr as u64,
2172 size: q_size as u16,
2173 ready: true,
2174 gpa_base: 0,
2175 };
2176 vsock.bind_connections(mock.clone());
2177
2178 let completions =
2179 <VirtioVsock as VirtioDevice>::process_queue(&mut vsock, 1, &mut memory, &qcfg)
2180 .unwrap();
2181 assert_eq!(completions.len(), 1);
2182
2183 let mock_guard = mock.lock().unwrap();
2184 assert_eq!(mock_guard.removed.len(), 1);
2185 assert_eq!(mock_guard.removed[0], (1024, 50000));
2186 }
2187}