1#![warn(missing_docs, unsafe_op_in_unsafe_fn)]
5mod connection;
8mod packet;
9pub mod transport;
11
12pub use connection::*;
13pub use packet::*;
14pub use transport::MpscTransport;
15
16#[derive(Copy, Clone, PartialEq, Eq, Debug)]
19pub enum ProtocolVersion {
20 V0,
22 V1,
24 V2(u32),
26}
27
28impl ProtocolVersion {
29 pub const LATEST: ProtocolVersion = ProtocolVersion::V2(0);
31
32 pub fn magic(&self) -> Vec<u8> {
46 match self {
47 ProtocolVersion::V0 => b"vsock:0".to_vec(),
48 ProtocolVersion::V1 => b"vsock:1".to_vec(),
49 ProtocolVersion::V2(nonce) => format!("vsock:2:{nonce:x}").into_bytes(),
50 }
51 }
52
53 pub fn from_magic(magic: &[u8]) -> Option<ProtocolVersion> {
55 if magic == b"vsock:0" {
56 Some(ProtocolVersion::V0)
57 } else if magic == b"vsock:1" {
58 Some(ProtocolVersion::V1)
59 } else if let Some(rest) = magic.strip_prefix(b"vsock:2:") {
60 let s = std::str::from_utf8(rest).ok()?;
61 let nonce = u32::from_str_radix(s, 16).ok()?;
62 Some(ProtocolVersion::V2(nonce))
63 } else {
64 None
65 }
66 }
67
68 pub fn negotiate(&self, host_version: &ProtocolVersion) -> Option<ProtocolVersion> {
74 match (self, host_version) {
75 (ProtocolVersion::V2(_), ProtocolVersion::V2(nonce)) => {
76 Some(ProtocolVersion::V2(*nonce))
77 }
78 (ProtocolVersion::V2(_), ProtocolVersion::V1)
79 | (ProtocolVersion::V1, ProtocolVersion::V2(_))
80 | (ProtocolVersion::V1, ProtocolVersion::V1) => Some(ProtocolVersion::V1),
81 (ProtocolVersion::V0, _) | (_, ProtocolVersion::V0) => Some(ProtocolVersion::V0),
82 }
83 }
84
85 pub(crate) fn has_pause_packets(&self) -> bool {
87 matches!(self, ProtocolVersion::V1 | ProtocolVersion::V2(_))
88 }
89}
90
91impl std::fmt::Display for ProtocolVersion {
92 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
93 match self {
94 ProtocolVersion::V0 => write!(f, "0"),
95 ProtocolVersion::V1 => write!(f, "1"),
96 ProtocolVersion::V2(nonce) => write!(f, "2:{nonce:x}"),
97 }
98 }
99}
100
101pub const CID_ANY: u32 = u32::MAX;
103
104pub const CID_HOST: u32 = 2;
106
107pub const CID_LOOPBACK: u32 = 1;
109
110#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
114pub struct Address {
115 pub device_cid: u32,
120 pub host_cid: u32,
125 pub device_port: u32,
130 pub host_port: u32,
135}
136
137impl Address {
138 pub fn is_zeros(&self) -> bool {
141 *self == Self::default()
142 }
143}
144
145impl From<&Header> for Address {
146 fn from(header: &Header) -> Self {
147 Self {
148 device_cid: header.device_cid.get(),
149 host_cid: header.host_cid.get(),
150 device_port: header.device_port.get(),
151 host_port: header.host_port.get(),
152 }
153 }
154}
155
156#[cfg(test)]
157mod tests {
158 use super::*;
159
160 #[test]
161 fn test_protocol_version_magic_roundtrip() {
162 let v0 = ProtocolVersion::V0;
163 assert_eq!(v0.magic(), b"vsock:0");
164 assert_eq!(ProtocolVersion::from_magic(b"vsock:0"), Some(v0));
165
166 let v1 = ProtocolVersion::V1;
167 assert_eq!(v1.magic(), b"vsock:1");
168 assert_eq!(ProtocolVersion::from_magic(b"vsock:1"), Some(v1));
169
170 let v2_zero = ProtocolVersion::V2(0);
171 assert_eq!(v2_zero.magic(), b"vsock:2:0");
172 assert_eq!(ProtocolVersion::from_magic(b"vsock:2:0"), Some(v2_zero));
173
174 let v2 = ProtocolVersion::V2(0xdeadbeef);
175 assert_eq!(v2.magic(), b"vsock:2:deadbeef");
176 assert_eq!(ProtocolVersion::from_magic(b"vsock:2:deadbeef"), Some(v2));
177
178 assert_eq!(
180 ProtocolVersion::from_magic(b"vsock:2:DEADBEEF"),
181 Some(ProtocolVersion::V2(0xdeadbeef))
182 );
183 assert_eq!(ProtocolVersion::from_magic(b"vsock:2:00000001"), Some(ProtocolVersion::V2(1)));
184
185 assert_eq!(ProtocolVersion::from_magic(b""), None);
187 assert_eq!(ProtocolVersion::from_magic(b"vsock"), None);
188 assert_eq!(ProtocolVersion::from_magic(b"vsock:3"), None);
189 assert_eq!(ProtocolVersion::from_magic(b"vsock:2:invalid"), None);
190 }
191
192 #[test]
193 fn test_protocol_version_negotiate() {
194 let target_v2 = ProtocolVersion::V2(0);
195 let host_v2 = ProtocolVersion::V2(0x12345678);
196 assert_eq!(target_v2.negotiate(&host_v2), Some(ProtocolVersion::V2(0x12345678)));
197 assert_eq!(target_v2.negotiate(&ProtocolVersion::V1), Some(ProtocolVersion::V1));
198 assert_eq!(target_v2.negotiate(&ProtocolVersion::V0), Some(ProtocolVersion::V0));
199
200 let target_v1 = ProtocolVersion::V1;
201 assert_eq!(target_v1.negotiate(&host_v2), Some(ProtocolVersion::V1));
202 assert_eq!(target_v1.negotiate(&ProtocolVersion::V1), Some(ProtocolVersion::V1));
203 assert_eq!(target_v1.negotiate(&ProtocolVersion::V0), Some(ProtocolVersion::V0));
204
205 let target_v0 = ProtocolVersion::V0;
206 assert_eq!(target_v0.negotiate(&host_v2), Some(ProtocolVersion::V0));
207 assert_eq!(target_v0.negotiate(&ProtocolVersion::V1), Some(ProtocolVersion::V0));
208 assert_eq!(target_v0.negotiate(&ProtocolVersion::V0), Some(ProtocolVersion::V0));
209 }
210
211 #[test]
212 fn test_protocol_version_features() {
213 assert!(!ProtocolVersion::V0.has_pause_packets());
214 assert!(ProtocolVersion::V1.has_pause_packets());
215 assert!(ProtocolVersion::V2(0).has_pause_packets());
216 assert!(ProtocolVersion::V2(0xabcdef).has_pause_packets());
217 }
218
219 #[test]
220 fn test_protocol_version_display() {
221 assert_eq!(format!("{}", ProtocolVersion::V0), "0");
222 assert_eq!(format!("{}", ProtocolVersion::V1), "1");
223 assert_eq!(format!("{}", ProtocolVersion::V2(0x1a2b)), "2:1a2b");
224 }
225}