Skip to main content

arcbox_virtio_vsock/
protocol.rs

1//! Vsock wire protocol — `VsockOp` enum and 44-byte packet header.
2
3use crate::addr::VsockAddr;
4
5/// Vsock operation types.
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7#[repr(u16)]
8pub enum VsockOp {
9    /// Invalid operation.
10    Invalid = 0,
11    /// Request connection.
12    Request = 1,
13    /// Connection response.
14    Response = 2,
15    /// Reset connection.
16    Rst = 3,
17    /// Shutdown connection.
18    Shutdown = 4,
19    /// Data transfer.
20    Rw = 5,
21    /// Credit update.
22    CreditUpdate = 6,
23    /// Credit request.
24    CreditRequest = 7,
25}
26
27impl VsockOp {
28    /// Converts from u16.
29    #[must_use]
30    pub const fn from_u16(val: u16) -> Option<Self> {
31        match val {
32            0 => Some(Self::Invalid),
33            1 => Some(Self::Request),
34            2 => Some(Self::Response),
35            3 => Some(Self::Rst),
36            4 => Some(Self::Shutdown),
37            5 => Some(Self::Rw),
38            6 => Some(Self::CreditUpdate),
39            7 => Some(Self::CreditRequest),
40            _ => None,
41        }
42    }
43}
44
45/// Vsock packet header.
46#[derive(Debug, Clone, Copy)]
47#[repr(C, packed)]
48pub struct VsockHeader {
49    /// Source CID.
50    pub src_cid: u64,
51    /// Destination CID.
52    pub dst_cid: u64,
53    /// Source port.
54    pub src_port: u32,
55    /// Destination port.
56    pub dst_port: u32,
57    /// Payload length.
58    pub len: u32,
59    /// Socket type (stream = 1).
60    pub socket_type: u16,
61    /// Operation.
62    pub op: u16,
63    /// Flags.
64    pub flags: u32,
65    /// Buffer allocation.
66    pub buf_alloc: u32,
67    /// Forward count.
68    pub fwd_cnt: u32,
69}
70
71impl VsockHeader {
72    /// Header size in bytes.
73    ///
74    /// The VirtIO vsock spec defines the header as exactly 44 bytes (packed).
75    /// We cannot use `mem::size_of::<Self>()` because Rust adds trailing padding
76    /// to satisfy the struct's 8-byte alignment (from u64 fields), yielding 48.
77    /// The guest kernel sends and expects exactly 44 bytes per header.
78    pub const SIZE: usize = 44;
79
80    /// Creates a new header.
81    #[must_use]
82    pub const fn new(src: VsockAddr, dst: VsockAddr, op: VsockOp) -> Self {
83        Self {
84            src_cid: src.cid,
85            dst_cid: dst.cid,
86            src_port: src.port,
87            dst_port: dst.port,
88            len: 0,
89            socket_type: 1, // SOCK_STREAM
90            op: op as u16,
91            flags: 0,
92            buf_alloc: 64 * 1024,
93            fwd_cnt: 0,
94        }
95    }
96
97    /// Returns the operation type.
98    #[must_use]
99    pub const fn operation(&self) -> Option<VsockOp> {
100        VsockOp::from_u16(self.op)
101    }
102
103    /// Parses a vsock header from a byte slice.
104    ///
105    /// Returns `None` if the slice is too short.
106    #[must_use]
107    pub fn from_bytes(bytes: &[u8]) -> Option<Self> {
108        if bytes.len() < Self::SIZE {
109            return None;
110        }
111        Some(Self {
112            src_cid: u64::from_le_bytes([
113                bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
114            ]),
115            dst_cid: u64::from_le_bytes([
116                bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14],
117                bytes[15],
118            ]),
119            src_port: u32::from_le_bytes([bytes[16], bytes[17], bytes[18], bytes[19]]),
120            dst_port: u32::from_le_bytes([bytes[20], bytes[21], bytes[22], bytes[23]]),
121            len: u32::from_le_bytes([bytes[24], bytes[25], bytes[26], bytes[27]]),
122            socket_type: u16::from_le_bytes([bytes[28], bytes[29]]),
123            op: u16::from_le_bytes([bytes[30], bytes[31]]),
124            flags: u32::from_le_bytes([bytes[32], bytes[33], bytes[34], bytes[35]]),
125            buf_alloc: u32::from_le_bytes([bytes[36], bytes[37], bytes[38], bytes[39]]),
126            fwd_cnt: u32::from_le_bytes([bytes[40], bytes[41], bytes[42], bytes[43]]),
127        })
128    }
129
130    /// Serializes the header to a byte array.
131    #[must_use]
132    pub fn to_bytes(&self) -> [u8; Self::SIZE] {
133        let mut buf = [0u8; Self::SIZE];
134        // Copy packed fields to locals to avoid unaligned reference UB.
135        let src_cid = self.src_cid;
136        let dst_cid = self.dst_cid;
137        let src_port = self.src_port;
138        let dst_port = self.dst_port;
139        let len = self.len;
140        let socket_type = self.socket_type;
141        let op = self.op;
142        let flags = self.flags;
143        let buf_alloc = self.buf_alloc;
144        let fwd_cnt = self.fwd_cnt;
145
146        buf[0..8].copy_from_slice(&src_cid.to_le_bytes());
147        buf[8..16].copy_from_slice(&dst_cid.to_le_bytes());
148        buf[16..20].copy_from_slice(&src_port.to_le_bytes());
149        buf[20..24].copy_from_slice(&dst_port.to_le_bytes());
150        buf[24..28].copy_from_slice(&len.to_le_bytes());
151        buf[28..30].copy_from_slice(&socket_type.to_le_bytes());
152        buf[30..32].copy_from_slice(&op.to_le_bytes());
153        buf[32..36].copy_from_slice(&flags.to_le_bytes());
154        buf[36..40].copy_from_slice(&buf_alloc.to_le_bytes());
155        buf[40..44].copy_from_slice(&fwd_cnt.to_le_bytes());
156        buf
157    }
158}
159
160#[cfg(test)]
161mod tests {
162    use super::*;
163
164    #[test]
165    fn test_vsock_op_from_u16() {
166        assert_eq!(VsockOp::from_u16(0), Some(VsockOp::Invalid));
167        assert_eq!(VsockOp::from_u16(1), Some(VsockOp::Request));
168        assert_eq!(VsockOp::from_u16(5), Some(VsockOp::Rw));
169        assert_eq!(VsockOp::from_u16(100), None);
170    }
171
172    #[test]
173    fn test_vsock_header() {
174        let src = VsockAddr::new(3, 1000);
175        let dst = VsockAddr::new(2, 80);
176        let header = VsockHeader::new(src, dst, VsockOp::Request);
177
178        let src_cid = header.src_cid;
179        let dst_cid = header.dst_cid;
180        let src_port = header.src_port;
181        let dst_port = header.dst_port;
182
183        assert_eq!(src_cid, 3);
184        assert_eq!(dst_cid, 2);
185        assert_eq!(src_port, 1000);
186        assert_eq!(dst_port, 80);
187        assert_eq!(header.operation(), Some(VsockOp::Request));
188    }
189
190    #[test]
191    fn test_vsock_header_size() {
192        assert_eq!(VsockHeader::SIZE, 44);
193    }
194
195    #[test]
196    fn test_vsock_header_roundtrip() {
197        let src = VsockAddr::new(3, 1000);
198        let dst = VsockAddr::new(2, 80);
199        let original = VsockHeader::new(src, dst, VsockOp::Request);
200
201        let bytes = original.to_bytes();
202        assert_eq!(bytes.len(), VsockHeader::SIZE);
203
204        let parsed = VsockHeader::from_bytes(&bytes).unwrap();
205        let p_src_cid = parsed.src_cid;
206        let p_dst_cid = parsed.dst_cid;
207        let p_src_port = parsed.src_port;
208        let p_dst_port = parsed.dst_port;
209        let p_socket_type = parsed.socket_type;
210        assert_eq!(p_src_cid, 3);
211        assert_eq!(p_dst_cid, 2);
212        assert_eq!(p_src_port, 1000);
213        assert_eq!(p_dst_port, 80);
214        assert_eq!(parsed.operation(), Some(VsockOp::Request));
215        assert_eq!(p_socket_type, 1); // SOCK_STREAM
216    }
217
218    #[test]
219    fn test_vsock_header_from_bytes_too_short() {
220        let short = [0u8; 20];
221        assert!(VsockHeader::from_bytes(&short).is_none());
222    }
223
224    #[test]
225    fn test_vsock_header_to_bytes_all_fields() {
226        let mut header = VsockHeader::new(
227            VsockAddr::new(0xAABB, 0x1234),
228            VsockAddr::new(0xCCDD, 0x5678),
229            VsockOp::Rw,
230        );
231        header.len = 256;
232        header.flags = 0x42;
233        header.buf_alloc = 32768;
234        header.fwd_cnt = 100;
235
236        let bytes = header.to_bytes();
237        let parsed = VsockHeader::from_bytes(&bytes).unwrap();
238
239        let p_src_cid = parsed.src_cid;
240        let p_dst_cid = parsed.dst_cid;
241        let p_src_port = parsed.src_port;
242        let p_dst_port = parsed.dst_port;
243        let p_len = parsed.len;
244        let p_op = parsed.op;
245        let p_flags = parsed.flags;
246        let p_buf_alloc = parsed.buf_alloc;
247        let p_fwd_cnt = parsed.fwd_cnt;
248        assert_eq!(p_src_cid, 0xAABB);
249        assert_eq!(p_dst_cid, 0xCCDD);
250        assert_eq!(p_src_port, 0x1234);
251        assert_eq!(p_dst_port, 0x5678);
252        assert_eq!(p_len, 256);
253        assert_eq!(p_op, VsockOp::Rw as u16);
254        assert_eq!(p_flags, 0x42);
255        assert_eq!(p_buf_alloc, 32768);
256        assert_eq!(p_fwd_cnt, 100);
257    }
258
259    #[test]
260    fn test_vsock_header_size_is_44() {
261        assert_eq!(VsockHeader::SIZE, 44);
262        let hdr = VsockHeader::new(
263            VsockAddr::host(50000),
264            VsockAddr::new(3, 1024),
265            VsockOp::Request,
266        );
267        let bytes = hdr.to_bytes();
268        assert_eq!(bytes.len(), 44);
269
270        let parsed = VsockHeader::from_bytes(&bytes).unwrap();
271        assert_eq!({ parsed.src_cid }, 2);
272        assert_eq!({ parsed.dst_cid }, 3);
273        assert_eq!({ parsed.src_port }, 50000);
274        assert_eq!({ parsed.dst_port }, 1024);
275        assert_eq!({ parsed.op }, VsockOp::Request as u16);
276        assert_eq!({ parsed.socket_type }, 1);
277        assert_eq!({ parsed.buf_alloc }, 64 * 1024);
278    }
279}