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