virtio_drivers/device/socket/
protocol.rs

1//! This module defines the socket device protocol according to the virtio spec v1.1 5.10 Socket Device
2
3use super::error::{self, SocketError};
4use crate::config::ReadOnly;
5use bitflags::bitflags;
6use core::{
7    convert::{TryFrom, TryInto},
8    fmt,
9};
10use zerocopy::{
11    byteorder::{LittleEndian, U16, U32, U64},
12    FromBytes, Immutable, IntoBytes, KnownLayout,
13};
14
15/// Well-known CID for the host.
16pub const VMADDR_CID_HOST: u64 = 2;
17
18/// Currently only stream sockets are supported. type is 1 for stream socket types.
19#[derive(Copy, Clone, Debug)]
20#[repr(u16)]
21pub enum SocketType {
22    /// Stream sockets provide in-order, guaranteed, connection-oriented delivery without message boundaries.
23    Stream = 1,
24    /// seqpacket socket type introduced in virtio-v1.2.
25    SeqPacket = 2,
26}
27
28impl From<SocketType> for U16<LittleEndian> {
29    fn from(socket_type: SocketType) -> Self {
30        (socket_type as u16).into()
31    }
32}
33
34/// VirtioVsockConfig is the vsock device configuration space.
35#[derive(FromBytes, Immutable, IntoBytes)]
36#[repr(C)]
37pub struct VirtioVsockConfig {
38    /// The guest_cid field contains the guest’s context ID, which uniquely identifies
39    /// the device for its lifetime. The upper 32 bits of the CID are reserved and zeroed.
40    ///
41    /// According to virtio spec v1.1 2.4.1 Driver Requirements: Device Configuration Space,
42    /// drivers MUST NOT assume reads from fields greater than 32 bits wide are atomic.
43    /// So we need to split the u64 guest_cid into two parts.
44    pub guest_cid_low: ReadOnly<u32>,
45    pub guest_cid_high: ReadOnly<u32>,
46}
47
48/// The message header for data packets sent on the tx/rx queues
49#[repr(C, packed)]
50#[derive(Clone, Copy, Debug, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
51pub struct VirtioVsockHdr {
52    pub src_cid: U64<LittleEndian>,
53    pub dst_cid: U64<LittleEndian>,
54    pub src_port: U32<LittleEndian>,
55    pub dst_port: U32<LittleEndian>,
56    pub len: U32<LittleEndian>,
57    pub socket_type: U16<LittleEndian>,
58    pub op: U16<LittleEndian>,
59    pub flags: U32<LittleEndian>,
60    /// Total receive buffer space for this socket. This includes both free and in-use buffers.
61    pub buf_alloc: U32<LittleEndian>,
62    /// Free-running bytes received counter.
63    pub fwd_cnt: U32<LittleEndian>,
64}
65
66impl Default for VirtioVsockHdr {
67    fn default() -> Self {
68        Self {
69            src_cid: 0.into(),
70            dst_cid: 0.into(),
71            src_port: 0.into(),
72            dst_port: 0.into(),
73            len: 0.into(),
74            socket_type: SocketType::Stream.into(),
75            op: 0.into(),
76            flags: 0.into(),
77            buf_alloc: 0.into(),
78            fwd_cnt: 0.into(),
79        }
80    }
81}
82
83impl VirtioVsockHdr {
84    /// Returns the length of the data.
85    pub fn len(&self) -> u32 {
86        u32::from(self.len)
87    }
88
89    pub fn op(&self) -> error::Result<VirtioVsockOp> {
90        self.op.try_into()
91    }
92
93    pub fn source(&self) -> VsockAddr {
94        VsockAddr {
95            cid: self.src_cid.get(),
96            port: self.src_port.get(),
97        }
98    }
99
100    pub fn destination(&self) -> VsockAddr {
101        VsockAddr {
102            cid: self.dst_cid.get(),
103            port: self.dst_port.get(),
104        }
105    }
106
107    pub fn check_data_is_empty(&self) -> error::Result<()> {
108        if self.len() == 0 {
109            Ok(())
110        } else {
111            Err(SocketError::UnexpectedDataInPacket)
112        }
113    }
114}
115
116/// Socket address.
117#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
118pub struct VsockAddr {
119    /// Context Identifier.
120    pub cid: u64,
121    /// Port number.
122    pub port: u32,
123}
124
125/// An event sent to the event queue
126#[derive(Copy, Clone, Debug, Default, IntoBytes, FromBytes, Immutable, KnownLayout)]
127#[repr(C)]
128pub struct VirtioVsockEvent {
129    // ID from the virtio_vsock_event_id struct in the virtio spec
130    pub id: U32<LittleEndian>,
131}
132
133#[derive(Copy, Clone, Eq, PartialEq)]
134#[repr(u16)]
135pub enum VirtioVsockOp {
136    Invalid = 0,
137
138    /* Connect operations */
139    Request = 1,
140    Response = 2,
141    Rst = 3,
142    Shutdown = 4,
143
144    /* To send payload */
145    Rw = 5,
146
147    /* Tell the peer our credit info */
148    CreditUpdate = 6,
149    /* Request the peer to send the credit info to us */
150    CreditRequest = 7,
151}
152
153impl From<VirtioVsockOp> for U16<LittleEndian> {
154    fn from(op: VirtioVsockOp) -> Self {
155        (op as u16).into()
156    }
157}
158
159impl TryFrom<U16<LittleEndian>> for VirtioVsockOp {
160    type Error = SocketError;
161
162    fn try_from(v: U16<LittleEndian>) -> Result<Self, Self::Error> {
163        let op = match u16::from(v) {
164            0 => Self::Invalid,
165            1 => Self::Request,
166            2 => Self::Response,
167            3 => Self::Rst,
168            4 => Self::Shutdown,
169            5 => Self::Rw,
170            6 => Self::CreditUpdate,
171            7 => Self::CreditRequest,
172            _ => return Err(SocketError::UnknownOperation(v.into())),
173        };
174        Ok(op)
175    }
176}
177
178impl fmt::Debug for VirtioVsockOp {
179    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
180        match self {
181            Self::Invalid => write!(f, "VIRTIO_VSOCK_OP_INVALID"),
182            Self::Request => write!(f, "VIRTIO_VSOCK_OP_REQUEST"),
183            Self::Response => write!(f, "VIRTIO_VSOCK_OP_RESPONSE"),
184            Self::Rst => write!(f, "VIRTIO_VSOCK_OP_RST"),
185            Self::Shutdown => write!(f, "VIRTIO_VSOCK_OP_SHUTDOWN"),
186            Self::Rw => write!(f, "VIRTIO_VSOCK_OP_RW"),
187            Self::CreditUpdate => write!(f, "VIRTIO_VSOCK_OP_CREDIT_UPDATE"),
188            Self::CreditRequest => write!(f, "VIRTIO_VSOCK_OP_CREDIT_REQUEST"),
189        }
190    }
191}
192
193bitflags! {
194    #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
195    pub(crate) struct Feature: u64 {
196        /// stream socket type is supported.
197        const STREAM = 1 << 0;
198        /// seqpacket socket type is supported.
199        const SEQ_PACKET = 1 << 1;
200
201        // device independent
202        const NOTIFY_ON_EMPTY       = 1 << 24; // legacy
203        const ANY_LAYOUT            = 1 << 27; // legacy
204        const RING_INDIRECT_DESC    = 1 << 28;
205        const RING_EVENT_IDX        = 1 << 29;
206        const UNUSED                = 1 << 30; // legacy
207        const VERSION_1             = 1 << 32; // detect legacy
208
209        // since virtio v1.1
210        const ACCESS_PLATFORM       = 1 << 33;
211        const RING_PACKED           = 1 << 34;
212        const IN_ORDER              = 1 << 35;
213        const ORDER_PLATFORM        = 1 << 36;
214        const SR_IOV                = 1 << 37;
215        const NOTIFICATION_DATA     = 1 << 38;
216    }
217}
218
219bitflags! {
220    /// Flags sent with a shutdown request to hint that the peer won't send or receive more data.
221    #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
222    pub struct StreamShutdown: u32 {
223        /// The peer will not receive any more data.
224        const RECEIVE = 1 << 0;
225        /// The peer will not send any more data.
226        const SEND = 1 << 1;
227    }
228}
229
230impl From<StreamShutdown> for U32<LittleEndian> {
231    fn from(flags: StreamShutdown) -> Self {
232        flags.bits().into()
233    }
234}