usb_vsock/
lib.rs

1// Copyright 2025 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4#![warn(missing_docs, unsafe_op_in_unsafe_fn)]
5//! A transport-agnostic library for implementing a vsock bridge over a usb bulk device.
6
7mod connection;
8mod packet;
9
10pub use connection::*;
11pub use packet::*;
12
13/// Protocol version. This can be extracted from the payload of the sync packet
14/// and will determine what features a connection supports.
15#[derive(Copy, Clone, PartialEq, Eq)]
16pub enum ProtocolVersion {
17    /// Protocol version 0
18    V0,
19    /// Protocol version 1
20    V1,
21}
22
23impl ProtocolVersion {
24    /// The latest protocol version.
25    pub const LATEST: ProtocolVersion = ProtocolVersion::V1;
26
27    /// Magic sent in the sync packet of the USB protocol.
28    ///
29    /// The format is a byte string of the form `vsock:0`, where the 0 indicates
30    /// protocol version 0, and we expect the reply sync packet to have the
31    /// exact same contents. As we version the protocol this may increment.
32    ///
33    /// To document the semantics, let's say this header were "vsock:3". The device
34    /// could reply with a lower number, say "vsock:1". This is the device
35    /// requesting a downgrade, and if we accept we send the final sync with
36    /// "vsock:1". Otherwise we hang up.
37    pub fn magic(&self) -> &[u8] {
38        match self {
39            ProtocolVersion::V0 => b"vsock:0",
40            ProtocolVersion::V1 => b"vsock:1",
41        }
42    }
43
44    /// Derive the protocol version from the magic sent in the sync packet.
45    pub fn from_magic(magic: &[u8]) -> Option<ProtocolVersion> {
46        if magic == b"vsock:0" {
47            Some(ProtocolVersion::V0)
48        } else if magic == b"vsock:1" {
49            Some(ProtocolVersion::V1)
50        } else {
51            None
52        }
53    }
54
55    /// Given `self` is the protocol version the target prefers and
56    /// `host_version` is the protocol version sent in the magic as the host
57    /// connects, find the protocol version that should be sent in the reply
58    /// magic and used for the connection. If `None`, negotiation has broken
59    /// down.
60    pub fn negotiate(&self, host_version: &ProtocolVersion) -> Option<ProtocolVersion> {
61        match (self, host_version) {
62            (ProtocolVersion::V1, ProtocolVersion::V1) => Some(ProtocolVersion::V1),
63            (ProtocolVersion::V0, _) | (_, ProtocolVersion::V0) => Some(ProtocolVersion::V0),
64        }
65    }
66
67    /// Whether we support the pause protocol message.
68    pub(crate) fn has_pause_packets(&self) -> bool {
69        matches!(self, ProtocolVersion::V1)
70    }
71}
72
73impl std::fmt::Display for ProtocolVersion {
74    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
75        match self {
76            ProtocolVersion::V0 => write!(f, "0"),
77            ProtocolVersion::V1 => write!(f, "1"),
78        }
79    }
80}
81
82/// A placeholder CID indicating "any" CID is acceptable.
83pub const CID_ANY: u32 = u32::MAX;
84
85/// CID of the host.
86pub const CID_HOST: u32 = 2;
87
88/// The loopback CID.
89pub const CID_LOOPBACK: u32 = 1;
90
91/// An address for a vsock packet transmitted over USB. Since this library does not implement
92/// policy decisions, it includes all four components of a vsock address pair even though some
93/// may not be appropriate for some situations.
94#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
95pub struct Address {
96    /// For Connect, Reset, Accept, and Data packets this represents the device side's address.
97    /// Usually this will be a special value representing either that it is simply "the device",
98    /// or zero along with the rest of the cid and port fields to indicate that it's a control stream
99    /// packet. Must be zero for any other packet type.
100    pub device_cid: u32,
101    /// For Connect, Reset, Accept, and Data packets this represents the host side's address.
102    /// Usually this will be a special value representing either that it is simply "the host",
103    /// or zero along with the rest of the cid and port fields to indicate that it's a control stream
104    /// packet. Must be zero for any other packet type.
105    pub host_cid: u32,
106    /// For Connect, Reset, Accept, and Data packets this represents the device side's port.
107    /// This must be a valid positive value for any of those packet types, unless all of the cid and
108    /// port fields are also zero, in which case it is a control stream packet. Must be zero for any
109    /// other packet type.
110    pub device_port: u32,
111    /// For Connect, Reset, Accept, and Data packets this represents the host side's port.
112    /// This must be a valid positive value for any of those packet types, unless all of the cid and
113    /// port fields are also zero, in which case it is a control stream packet. Must be zero for any
114    /// other packet type.
115    pub host_port: u32,
116}
117
118impl Address {
119    /// Returns true if all the fields of this address are zero (which usually means it's a control
120    /// packet of some sort).
121    pub fn is_zeros(&self) -> bool {
122        *self == Self::default()
123    }
124}
125
126impl From<&Header> for Address {
127    fn from(header: &Header) -> Self {
128        Self {
129            device_cid: header.device_cid.get(),
130            host_cid: header.host_cid.get(),
131            device_port: header.device_port.get(),
132            host_port: header.host_port.get(),
133        }
134    }
135}