• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! Driver for VirtIO socket devices.
2 #![deny(unsafe_op_in_unsafe_fn)]
3 
4 use super::error::SocketError;
5 use super::protocol::{
6     Feature, StreamShutdown, VirtioVsockConfig, VirtioVsockHdr, VirtioVsockOp, VsockAddr,
7 };
8 use super::DEFAULT_RX_BUFFER_SIZE;
9 use crate::hal::Hal;
10 use crate::queue::VirtQueue;
11 use crate::transport::Transport;
12 use crate::volatile::volread;
13 use crate::{Error, Result};
14 use alloc::boxed::Box;
15 use core::mem::size_of;
16 use core::ptr::{null_mut, NonNull};
17 use log::debug;
18 use zerocopy::{AsBytes, FromBytes, FromZeroes};
19 
20 pub(crate) const RX_QUEUE_IDX: u16 = 0;
21 pub(crate) const TX_QUEUE_IDX: u16 = 1;
22 const EVENT_QUEUE_IDX: u16 = 2;
23 
24 pub(crate) const QUEUE_SIZE: usize = 8;
25 const SUPPORTED_FEATURES: Feature = Feature::RING_EVENT_IDX.union(Feature::RING_INDIRECT_DESC);
26 
27 #[derive(Clone, Debug, Default, PartialEq, Eq)]
28 pub struct ConnectionInfo {
29     pub dst: VsockAddr,
30     pub src_port: u32,
31     /// The last `buf_alloc` value the peer sent to us, indicating how much receive buffer space in
32     /// bytes it has allocated for packet bodies.
33     peer_buf_alloc: u32,
34     /// The last `fwd_cnt` value the peer sent to us, indicating how many bytes of packet bodies it
35     /// has finished processing.
36     peer_fwd_cnt: u32,
37     /// The number of bytes of packet bodies which we have sent to the peer.
38     tx_cnt: u32,
39     /// The number of bytes of buffer space we have allocated to receive packet bodies from the
40     /// peer.
41     pub buf_alloc: u32,
42     /// The number of bytes of packet bodies which we have received from the peer and handled.
43     fwd_cnt: u32,
44     /// Whether we have recently requested credit from the peer.
45     ///
46     /// This is set to true when we send a `VIRTIO_VSOCK_OP_CREDIT_REQUEST`, and false when we
47     /// receive a `VIRTIO_VSOCK_OP_CREDIT_UPDATE`.
48     has_pending_credit_request: bool,
49 }
50 
51 impl ConnectionInfo {
new(destination: VsockAddr, src_port: u32) -> Self52     pub fn new(destination: VsockAddr, src_port: u32) -> Self {
53         Self {
54             dst: destination,
55             src_port,
56             ..Default::default()
57         }
58     }
59 
60     /// Updates this connection info with the peer buffer allocation and forwarded count from the
61     /// given event.
update_for_event(&mut self, event: &VsockEvent)62     pub fn update_for_event(&mut self, event: &VsockEvent) {
63         self.peer_buf_alloc = event.buffer_status.buffer_allocation;
64         self.peer_fwd_cnt = event.buffer_status.forward_count;
65 
66         if let VsockEventType::CreditUpdate = event.event_type {
67             self.has_pending_credit_request = false;
68         }
69     }
70 
71     /// Increases the forwarded count recorded for this connection by the given number of bytes.
72     ///
73     /// This should be called once received data has been passed to the client, so there is buffer
74     /// space available for more.
done_forwarding(&mut self, length: usize)75     pub fn done_forwarding(&mut self, length: usize) {
76         self.fwd_cnt += length as u32;
77     }
78 
79     /// Returns the number of bytes of RX buffer space the peer has available to receive packet body
80     /// data from us.
peer_free(&self) -> u3281     fn peer_free(&self) -> u32 {
82         self.peer_buf_alloc - (self.tx_cnt - self.peer_fwd_cnt)
83     }
84 
new_header(&self, src_cid: u64) -> VirtioVsockHdr85     fn new_header(&self, src_cid: u64) -> VirtioVsockHdr {
86         VirtioVsockHdr {
87             src_cid: src_cid.into(),
88             dst_cid: self.dst.cid.into(),
89             src_port: self.src_port.into(),
90             dst_port: self.dst.port.into(),
91             buf_alloc: self.buf_alloc.into(),
92             fwd_cnt: self.fwd_cnt.into(),
93             ..Default::default()
94         }
95     }
96 }
97 
98 /// An event received from a VirtIO socket device.
99 #[derive(Clone, Debug, Eq, PartialEq)]
100 pub struct VsockEvent {
101     /// The source of the event, i.e. the peer who sent it.
102     pub source: VsockAddr,
103     /// The destination of the event, i.e. the CID and port on our side.
104     pub destination: VsockAddr,
105     /// The peer's buffer status for the connection.
106     pub buffer_status: VsockBufferStatus,
107     /// The type of event.
108     pub event_type: VsockEventType,
109 }
110 
111 impl VsockEvent {
112     /// Returns whether the event matches the given connection.
matches_connection(&self, connection_info: &ConnectionInfo, guest_cid: u64) -> bool113     pub fn matches_connection(&self, connection_info: &ConnectionInfo, guest_cid: u64) -> bool {
114         self.source == connection_info.dst
115             && self.destination.cid == guest_cid
116             && self.destination.port == connection_info.src_port
117     }
118 
from_header(header: &VirtioVsockHdr) -> Result<Self>119     fn from_header(header: &VirtioVsockHdr) -> Result<Self> {
120         let op = header.op()?;
121         let buffer_status = VsockBufferStatus {
122             buffer_allocation: header.buf_alloc.into(),
123             forward_count: header.fwd_cnt.into(),
124         };
125         let source = header.source();
126         let destination = header.destination();
127 
128         let event_type = match op {
129             VirtioVsockOp::Request => {
130                 header.check_data_is_empty()?;
131                 VsockEventType::ConnectionRequest
132             }
133             VirtioVsockOp::Response => {
134                 header.check_data_is_empty()?;
135                 VsockEventType::Connected
136             }
137             VirtioVsockOp::CreditUpdate => {
138                 header.check_data_is_empty()?;
139                 VsockEventType::CreditUpdate
140             }
141             VirtioVsockOp::Rst | VirtioVsockOp::Shutdown => {
142                 header.check_data_is_empty()?;
143                 debug!("Disconnected from the peer");
144                 let reason = if op == VirtioVsockOp::Rst {
145                     DisconnectReason::Reset
146                 } else {
147                     DisconnectReason::Shutdown
148                 };
149                 VsockEventType::Disconnected { reason }
150             }
151             VirtioVsockOp::Rw => VsockEventType::Received {
152                 length: header.len() as usize,
153             },
154             VirtioVsockOp::CreditRequest => {
155                 header.check_data_is_empty()?;
156                 VsockEventType::CreditRequest
157             }
158             VirtioVsockOp::Invalid => return Err(SocketError::InvalidOperation.into()),
159         };
160 
161         Ok(VsockEvent {
162             source,
163             destination,
164             buffer_status,
165             event_type,
166         })
167     }
168 }
169 
170 #[derive(Clone, Debug, Eq, PartialEq)]
171 pub struct VsockBufferStatus {
172     pub buffer_allocation: u32,
173     pub forward_count: u32,
174 }
175 
176 /// The reason why a vsock connection was closed.
177 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
178 pub enum DisconnectReason {
179     /// The peer has either closed the connection in response to our shutdown request, or forcibly
180     /// closed it of its own accord.
181     Reset,
182     /// The peer asked to shut down the connection.
183     Shutdown,
184 }
185 
186 /// Details of the type of an event received from a VirtIO socket.
187 #[derive(Clone, Debug, Eq, PartialEq)]
188 pub enum VsockEventType {
189     /// The peer requests to establish a connection with us.
190     ConnectionRequest,
191     /// The connection was successfully established.
192     Connected,
193     /// The connection was closed.
194     Disconnected {
195         /// The reason for the disconnection.
196         reason: DisconnectReason,
197     },
198     /// Data was received on the connection.
199     Received {
200         /// The length of the data in bytes.
201         length: usize,
202     },
203     /// The peer requests us to send a credit update.
204     CreditRequest,
205     /// The peer just sent us a credit update with nothing else.
206     CreditUpdate,
207 }
208 
209 /// Low-level driver for a VirtIO socket device.
210 ///
211 /// You probably want to use [`VsockConnectionManager`](super::VsockConnectionManager) rather than
212 /// using this directly.
213 ///
214 /// `RX_BUFFER_SIZE` is the size in bytes of each buffer used in the RX virtqueue. This must be
215 /// bigger than `size_of::<VirtioVsockHdr>()`.
216 pub struct VirtIOSocket<H: Hal, T: Transport, const RX_BUFFER_SIZE: usize = DEFAULT_RX_BUFFER_SIZE>
217 {
218     transport: T,
219     /// Virtqueue to receive packets.
220     rx: VirtQueue<H, { QUEUE_SIZE }>,
221     tx: VirtQueue<H, { QUEUE_SIZE }>,
222     /// Virtqueue to receive events from the device.
223     event: VirtQueue<H, { QUEUE_SIZE }>,
224     /// The guest_cid field contains the guest’s context ID, which uniquely identifies
225     /// the device for its lifetime. The upper 32 bits of the CID are reserved and zeroed.
226     guest_cid: u64,
227     rx_queue_buffers: [NonNull<[u8; RX_BUFFER_SIZE]>; QUEUE_SIZE],
228 }
229 
230 // SAFETY: The `rx_queue_buffers` can be accessed from any thread.
231 unsafe impl<H: Hal, T: Transport + Send> Send for VirtIOSocket<H, T> where
232     VirtQueue<H, QUEUE_SIZE>: Send
233 {
234 }
235 
236 // SAFETY: A `&VirtIOSocket` only allows reading the guest CID from a field.
237 unsafe impl<H: Hal, T: Transport + Sync> Sync for VirtIOSocket<H, T> where
238     VirtQueue<H, QUEUE_SIZE>: Sync
239 {
240 }
241 
242 impl<H: Hal, T: Transport, const RX_BUFFER_SIZE: usize> Drop
243     for VirtIOSocket<H, T, RX_BUFFER_SIZE>
244 {
drop(&mut self)245     fn drop(&mut self) {
246         // Clear any pointers pointing to DMA regions, so the device doesn't try to access them
247         // after they have been freed.
248         self.transport.queue_unset(RX_QUEUE_IDX);
249         self.transport.queue_unset(TX_QUEUE_IDX);
250         self.transport.queue_unset(EVENT_QUEUE_IDX);
251 
252         for buffer in self.rx_queue_buffers {
253             // Safe because we obtained the RX buffer pointer from Box::into_raw, and it won't be
254             // used anywhere else after the driver is destroyed.
255             unsafe { drop(Box::from_raw(buffer.as_ptr())) };
256         }
257     }
258 }
259 
260 impl<H: Hal, T: Transport, const RX_BUFFER_SIZE: usize> VirtIOSocket<H, T, RX_BUFFER_SIZE> {
261     /// Create a new VirtIO Vsock driver.
new(mut transport: T) -> Result<Self>262     pub fn new(mut transport: T) -> Result<Self> {
263         assert!(RX_BUFFER_SIZE > size_of::<VirtioVsockHdr>());
264 
265         let negotiated_features = transport.begin_init(SUPPORTED_FEATURES);
266 
267         let config = transport.config_space::<VirtioVsockConfig>()?;
268         debug!("config: {:?}", config);
269         // Safe because config is a valid pointer to the device configuration space.
270         let guest_cid = unsafe {
271             volread!(config, guest_cid_low) as u64 | (volread!(config, guest_cid_high) as u64) << 32
272         };
273         debug!("guest cid: {guest_cid:?}");
274 
275         let mut rx = VirtQueue::new(
276             &mut transport,
277             RX_QUEUE_IDX,
278             negotiated_features.contains(Feature::RING_INDIRECT_DESC),
279             negotiated_features.contains(Feature::RING_EVENT_IDX),
280         )?;
281         let tx = VirtQueue::new(
282             &mut transport,
283             TX_QUEUE_IDX,
284             negotiated_features.contains(Feature::RING_INDIRECT_DESC),
285             negotiated_features.contains(Feature::RING_EVENT_IDX),
286         )?;
287         let event = VirtQueue::new(
288             &mut transport,
289             EVENT_QUEUE_IDX,
290             negotiated_features.contains(Feature::RING_INDIRECT_DESC),
291             negotiated_features.contains(Feature::RING_EVENT_IDX),
292         )?;
293 
294         // Allocate and add buffers for the RX queue.
295         let mut rx_queue_buffers = [null_mut(); QUEUE_SIZE];
296         for (i, rx_queue_buffer) in rx_queue_buffers.iter_mut().enumerate() {
297             let mut buffer: Box<[u8; RX_BUFFER_SIZE]> = FromZeroes::new_box_zeroed();
298             // Safe because the buffer lives as long as the queue, as specified in the function
299             // safety requirement, and we don't access it until it is popped.
300             let token = unsafe { rx.add(&[], &mut [buffer.as_mut_slice()]) }?;
301             assert_eq!(i, token.into());
302             *rx_queue_buffer = Box::into_raw(buffer);
303         }
304         let rx_queue_buffers = rx_queue_buffers.map(|ptr| NonNull::new(ptr).unwrap());
305 
306         transport.finish_init();
307         if rx.should_notify() {
308             transport.notify(RX_QUEUE_IDX);
309         }
310 
311         Ok(Self {
312             transport,
313             rx,
314             tx,
315             event,
316             guest_cid,
317             rx_queue_buffers,
318         })
319     }
320 
321     /// Returns the CID which has been assigned to this guest.
guest_cid(&self) -> u64322     pub fn guest_cid(&self) -> u64 {
323         self.guest_cid
324     }
325 
326     /// Sends a request to connect to the given destination.
327     ///
328     /// This returns as soon as the request is sent; you should wait until `poll` returns a
329     /// `VsockEventType::Connected` event indicating that the peer has accepted the connection
330     /// before sending data.
connect(&mut self, connection_info: &ConnectionInfo) -> Result331     pub fn connect(&mut self, connection_info: &ConnectionInfo) -> Result {
332         let header = VirtioVsockHdr {
333             op: VirtioVsockOp::Request.into(),
334             ..connection_info.new_header(self.guest_cid)
335         };
336         // Sends a header only packet to the TX queue to connect the device to the listening socket
337         // at the given destination.
338         self.send_packet_to_tx_queue(&header, &[])
339     }
340 
341     /// Accepts the given connection from a peer.
accept(&mut self, connection_info: &ConnectionInfo) -> Result342     pub fn accept(&mut self, connection_info: &ConnectionInfo) -> Result {
343         let header = VirtioVsockHdr {
344             op: VirtioVsockOp::Response.into(),
345             ..connection_info.new_header(self.guest_cid)
346         };
347         self.send_packet_to_tx_queue(&header, &[])
348     }
349 
350     /// Requests the peer to send us a credit update for the given connection.
request_credit(&mut self, connection_info: &ConnectionInfo) -> Result351     fn request_credit(&mut self, connection_info: &ConnectionInfo) -> Result {
352         let header = VirtioVsockHdr {
353             op: VirtioVsockOp::CreditRequest.into(),
354             ..connection_info.new_header(self.guest_cid)
355         };
356         self.send_packet_to_tx_queue(&header, &[])
357     }
358 
359     /// Sends the buffer to the destination.
send(&mut self, buffer: &[u8], connection_info: &mut ConnectionInfo) -> Result360     pub fn send(&mut self, buffer: &[u8], connection_info: &mut ConnectionInfo) -> Result {
361         self.check_peer_buffer_is_sufficient(connection_info, buffer.len())?;
362 
363         let len = buffer.len() as u32;
364         let header = VirtioVsockHdr {
365             op: VirtioVsockOp::Rw.into(),
366             len: len.into(),
367             ..connection_info.new_header(self.guest_cid)
368         };
369         connection_info.tx_cnt += len;
370         self.send_packet_to_tx_queue(&header, buffer)
371     }
372 
check_peer_buffer_is_sufficient( &mut self, connection_info: &mut ConnectionInfo, buffer_len: usize, ) -> Result373     fn check_peer_buffer_is_sufficient(
374         &mut self,
375         connection_info: &mut ConnectionInfo,
376         buffer_len: usize,
377     ) -> Result {
378         if connection_info.peer_free() as usize >= buffer_len {
379             Ok(())
380         } else {
381             // Request an update of the cached peer credit, if we haven't already done so, and tell
382             // the caller to try again later.
383             if !connection_info.has_pending_credit_request {
384                 self.request_credit(connection_info)?;
385                 connection_info.has_pending_credit_request = true;
386             }
387             Err(SocketError::InsufficientBufferSpaceInPeer.into())
388         }
389     }
390 
391     /// Tells the peer how much buffer space we have to receive data.
credit_update(&mut self, connection_info: &ConnectionInfo) -> Result392     pub fn credit_update(&mut self, connection_info: &ConnectionInfo) -> Result {
393         let header = VirtioVsockHdr {
394             op: VirtioVsockOp::CreditUpdate.into(),
395             ..connection_info.new_header(self.guest_cid)
396         };
397         self.send_packet_to_tx_queue(&header, &[])
398     }
399 
400     /// Polls the RX virtqueue for the next event, and calls the given handler function to handle
401     /// it.
poll( &mut self, handler: impl FnOnce(VsockEvent, &[u8]) -> Result<Option<VsockEvent>>, ) -> Result<Option<VsockEvent>>402     pub fn poll(
403         &mut self,
404         handler: impl FnOnce(VsockEvent, &[u8]) -> Result<Option<VsockEvent>>,
405     ) -> Result<Option<VsockEvent>> {
406         let Some((header, body, token)) = self.pop_packet_from_rx_queue()? else {
407             return Ok(None);
408         };
409 
410         let result = VsockEvent::from_header(&header).and_then(|event| handler(event, body));
411 
412         unsafe {
413             // TODO: What about if both handler and this give errors?
414             self.add_buffer_to_rx_queue(token)?;
415         }
416 
417         result
418     }
419 
420     /// Requests to shut down the connection cleanly, sending hints about whether we will send or
421     /// receive more data.
422     ///
423     /// This returns as soon as the request is sent; you should wait until `poll` returns a
424     /// `VsockEventType::Disconnected` event if you want to know that the peer has acknowledged the
425     /// shutdown.
shutdown_with_hints( &mut self, connection_info: &ConnectionInfo, hints: StreamShutdown, ) -> Result426     pub fn shutdown_with_hints(
427         &mut self,
428         connection_info: &ConnectionInfo,
429         hints: StreamShutdown,
430     ) -> Result {
431         let header = VirtioVsockHdr {
432             op: VirtioVsockOp::Shutdown.into(),
433             flags: hints.into(),
434             ..connection_info.new_header(self.guest_cid)
435         };
436         self.send_packet_to_tx_queue(&header, &[])
437     }
438 
439     /// Requests to shut down the connection cleanly, telling the peer that we won't send or receive
440     /// any more data.
441     ///
442     /// This returns as soon as the request is sent; you should wait until `poll` returns a
443     /// `VsockEventType::Disconnected` event if you want to know that the peer has acknowledged the
444     /// shutdown.
shutdown(&mut self, connection_info: &ConnectionInfo) -> Result445     pub fn shutdown(&mut self, connection_info: &ConnectionInfo) -> Result {
446         self.shutdown_with_hints(
447             connection_info,
448             StreamShutdown::SEND | StreamShutdown::RECEIVE,
449         )
450     }
451 
452     /// Forcibly closes the connection without waiting for the peer.
force_close(&mut self, connection_info: &ConnectionInfo) -> Result453     pub fn force_close(&mut self, connection_info: &ConnectionInfo) -> Result {
454         let header = VirtioVsockHdr {
455             op: VirtioVsockOp::Rst.into(),
456             ..connection_info.new_header(self.guest_cid)
457         };
458         self.send_packet_to_tx_queue(&header, &[])?;
459         Ok(())
460     }
461 
send_packet_to_tx_queue(&mut self, header: &VirtioVsockHdr, buffer: &[u8]) -> Result462     fn send_packet_to_tx_queue(&mut self, header: &VirtioVsockHdr, buffer: &[u8]) -> Result {
463         let _len = if buffer.is_empty() {
464             self.tx
465                 .add_notify_wait_pop(&[header.as_bytes()], &mut [], &mut self.transport)?
466         } else {
467             self.tx.add_notify_wait_pop(
468                 &[header.as_bytes(), buffer],
469                 &mut [],
470                 &mut self.transport,
471             )?
472         };
473         Ok(())
474     }
475 
476     /// Adds the buffer at the given index in `rx_queue_buffers` back to the RX queue.
477     ///
478     /// # Safety
479     ///
480     /// The buffer must not currently be in the RX queue, and no other references to it must exist
481     /// between when this method is called and when it is popped from the queue.
add_buffer_to_rx_queue(&mut self, index: u16) -> Result482     unsafe fn add_buffer_to_rx_queue(&mut self, index: u16) -> Result {
483         // Safe because the buffer lives as long as the queue, and the caller guarantees that it's
484         // not currently in the queue or referred to anywhere else until it is popped.
485         unsafe {
486             let buffer = self
487                 .rx_queue_buffers
488                 .get_mut(usize::from(index))
489                 .ok_or(Error::WrongToken)?
490                 .as_mut();
491             let new_token = self.rx.add(&[], &mut [buffer])?;
492             // If the RX buffer somehow gets assigned a different token, then our safety assumptions
493             // are broken and we can't safely continue to do anything with the device.
494             assert_eq!(new_token, index);
495         }
496 
497         if self.rx.should_notify() {
498             self.transport.notify(RX_QUEUE_IDX);
499         }
500 
501         Ok(())
502     }
503 
504     /// Pops one packet from the RX queue, if there is one pending. Returns the header, and a
505     /// reference to the buffer containing the body.
506     ///
507     /// Returns `None` if there is no pending packet.
pop_packet_from_rx_queue(&mut self) -> Result<Option<(VirtioVsockHdr, &[u8], u16)>>508     fn pop_packet_from_rx_queue(&mut self) -> Result<Option<(VirtioVsockHdr, &[u8], u16)>> {
509         let Some(token) = self.rx.peek_used() else {
510             return Ok(None);
511         };
512 
513         // Safe because we maintain a consistent mapping of tokens to buffers, so we pass the same
514         // buffer to `pop_used` as we previously passed to `add` for the token. Once we add the
515         // buffer back to the RX queue then we don't access it again until next time it is popped.
516         let (header, body) = unsafe {
517             let buffer = self.rx_queue_buffers[usize::from(token)].as_mut();
518             let _len = self.rx.pop_used(token, &[], &mut [buffer])?;
519 
520             // Read the header and body from the buffer. Don't check the result yet, because we need
521             // to add the buffer back to the queue either way.
522             let header_result = read_header_and_body(buffer);
523             if header_result.is_err() {
524                 // If there was an error, add the buffer back immediately. Ignore any errors, as we
525                 // need to return the first error.
526                 let _ = self.add_buffer_to_rx_queue(token);
527             }
528 
529             header_result
530         }?;
531 
532         debug!("Received packet {:?}. Op {:?}", header, header.op());
533         Ok(Some((header, body, token)))
534     }
535 }
536 
read_header_and_body(buffer: &[u8]) -> Result<(VirtioVsockHdr, &[u8])>537 fn read_header_and_body(buffer: &[u8]) -> Result<(VirtioVsockHdr, &[u8])> {
538     // Shouldn't panic, because we know `RX_BUFFER_SIZE > size_of::<VirtioVsockHdr>()`.
539     let header = VirtioVsockHdr::read_from_prefix(buffer).unwrap();
540     let body_length = header.len() as usize;
541 
542     // This could fail if the device returns an unreasonably long body length.
543     let data_end = size_of::<VirtioVsockHdr>()
544         .checked_add(body_length)
545         .ok_or(SocketError::InvalidNumber)?;
546     // This could fail if the device returns a body length longer than the buffer we gave it.
547     let data = buffer
548         .get(size_of::<VirtioVsockHdr>()..data_end)
549         .ok_or(SocketError::BufferTooShort)?;
550     Ok((header, data))
551 }
552 
553 #[cfg(test)]
554 mod tests {
555     use super::*;
556     use crate::{
557         hal::fake::FakeHal,
558         transport::{
559             fake::{FakeTransport, QueueStatus, State},
560             DeviceType,
561         },
562         volatile::ReadOnly,
563     };
564     use alloc::{sync::Arc, vec};
565     use core::ptr::NonNull;
566     use std::sync::Mutex;
567 
568     #[test]
config()569     fn config() {
570         let mut config_space = VirtioVsockConfig {
571             guest_cid_low: ReadOnly::new(66),
572             guest_cid_high: ReadOnly::new(0),
573         };
574         let state = Arc::new(Mutex::new(State {
575             queues: vec![
576                 QueueStatus::default(),
577                 QueueStatus::default(),
578                 QueueStatus::default(),
579             ],
580             ..Default::default()
581         }));
582         let transport = FakeTransport {
583             device_type: DeviceType::Socket,
584             max_queue_size: 32,
585             device_features: 0,
586             config_space: NonNull::from(&mut config_space),
587             state: state.clone(),
588         };
589         let socket =
590             VirtIOSocket::<FakeHal, FakeTransport<VirtioVsockConfig>>::new(transport).unwrap();
591         assert_eq!(socket.guest_cid(), 0x00_0000_0042);
592     }
593 }
594