xref: /aosp_15_r20/external/crosvm/e2e_tests/tests/vsock.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1 // Copyright 2022 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 //! Testing vsock.
6 
7 #![cfg(any(target_os = "android", target_os = "linux"))]
8 
9 use std::io::Write;
10 use std::path::Path;
11 use std::process::Command;
12 use std::process::Stdio;
13 use std::time::Duration;
14 
15 use fixture::utils::retry;
16 use fixture::utils::ChildExt;
17 use fixture::utils::CommandExt;
18 use fixture::vhost_user::CmdType;
19 use fixture::vhost_user::Config as VuConfig;
20 use fixture::vhost_user::VhostUserBackend;
21 use fixture::vm::Config;
22 use fixture::vm::TestVm;
23 use rand::Rng;
24 use tempfile::tempdir;
25 use tempfile::NamedTempFile;
26 
27 const ANY_CID: &str = "4294967295"; // -1U
28 const HOST_CID: u64 = 2;
29 
30 const SERVER_TIMEOUT: Duration = Duration::from_secs(3);
31 const NCAT_RETRIES: usize = 10;
32 
33 const MESSAGE_TO_HOST: &str = "Connection from the host is successfully established";
34 const MESSAGE_TO_GUEST: &str = "Connection from the guest is successfully established";
35 
36 // generate a random CID to avoid conflicts with other VMs run on different processes
generate_guest_cid() -> u3237 fn generate_guest_cid() -> u32 {
38     // avoid special CIDs and negative values
39     rand::thread_rng().gen_range(3..0x8000_0000)
40 }
41 
generate_vhost_port() -> u3242 fn generate_vhost_port() -> u32 {
43     rand::thread_rng().gen_range(10000..99999)
44 }
45 
46 #[test]
47 #[ignore = "Test failing in latest version of debian. b/346365355"]
host_to_guest()48 fn host_to_guest() {
49     let guest_port = generate_vhost_port();
50     let guest_cid = generate_guest_cid();
51     let config = Config::new().extra_args(vec!["--cid".to_string(), guest_cid.to_string()]);
52     let mut vm = TestVm::new(config).unwrap();
53     host_to_guest_connection(&mut vm, guest_cid, guest_port);
54 }
55 
56 #[test]
57 #[ignore = "Test failing in latest version of debian. b/346365355"]
host_to_guest_disable_sandbox()58 fn host_to_guest_disable_sandbox() {
59     let guest_port = generate_vhost_port();
60     let guest_cid = generate_guest_cid();
61     let config = Config::new()
62         .extra_args(vec!["--cid".to_string(), guest_cid.to_string()])
63         .disable_sandbox();
64     let mut vm = TestVm::new(config).unwrap();
65     host_to_guest_connection(&mut vm, guest_cid, guest_port);
66 }
67 
68 #[test]
69 #[ignore = "Test failing in latest version of debian. b/346365355"]
host_to_guest_snapshot_restore()70 fn host_to_guest_snapshot_restore() {
71     let guest_port = generate_vhost_port();
72     let guest_cid = generate_guest_cid();
73     let config = Config::new()
74         .extra_args(vec![
75             "--cid".to_string(),
76             guest_cid.to_string(),
77             "--no-usb".to_string(),
78         ])
79         .with_stdout_hardware("legacy-virtio-console");
80     let mut vm = TestVm::new(config).unwrap();
81     host_to_guest_connection(&mut vm, guest_cid, guest_port);
82     let dir = tempdir().unwrap();
83     let snap = dir.path().join("snapshot.bkp");
84     vm.snapshot(&snap).unwrap();
85     let config = Config::new()
86         .extra_args(vec![
87             "--cid".to_string(),
88             guest_cid.to_string(),
89             "--restore".to_string(),
90             snap.to_str().unwrap().to_string(),
91             "--no-usb".to_string(),
92         ])
93         .with_stdout_hardware("legacy-virtio-console");
94     drop(vm);
95     vm = TestVm::new_restore(config).unwrap();
96     host_to_guest_connection(&mut vm, guest_cid, guest_port);
97 }
98 
99 #[test]
100 #[ignore = "Test failing in latest version of debian. b/346365355"]
host_to_guest_disable_sandbox_snapshot_restore()101 fn host_to_guest_disable_sandbox_snapshot_restore() {
102     let guest_port = generate_vhost_port();
103     let guest_cid = generate_guest_cid();
104     let config = Config::new()
105         .extra_args(vec![
106             "--cid".to_string(),
107             guest_cid.to_string(),
108             "--no-usb".to_string(),
109         ])
110         .with_stdout_hardware("legacy-virtio-console");
111     let mut vm = TestVm::new(config.disable_sandbox()).unwrap();
112     host_to_guest_connection(&mut vm, guest_cid, guest_port);
113     let dir = tempdir().unwrap();
114     let snap = dir.path().join("snapshot.bkp");
115     vm.snapshot(&snap).unwrap();
116     let config = Config::new()
117         .extra_args(vec![
118             "--cid".to_string(),
119             guest_cid.to_string(),
120             "--restore".to_string(),
121             snap.to_str().unwrap().to_string(),
122             "--no-usb".to_string(),
123         ])
124         .with_stdout_hardware("legacy-virtio-console");
125     drop(vm);
126     vm = TestVm::new_restore(config.disable_sandbox()).unwrap();
127     host_to_guest_connection(&mut vm, guest_cid, guest_port);
128 }
129 
host_to_guest_connection(vm: &mut TestVm, guest_cid: u32, guest_port: u32)130 fn host_to_guest_connection(vm: &mut TestVm, guest_cid: u32, guest_port: u32) {
131     let guest_cmd = vm
132         .exec_in_guest_async(&format!(
133             "echo {MESSAGE_TO_HOST} | ncat -l --vsock --send-only {ANY_CID} {guest_port}"
134         ))
135         .unwrap();
136 
137     let output = retry(
138         || {
139             Command::new("ncat")
140                 .args([
141                     "--recv-only",
142                     "--vsock",
143                     &guest_cid.to_string(),
144                     &guest_port.to_string(),
145                 ])
146                 .stderr(Stdio::inherit())
147                 .log()
148                 .output_checked()
149         },
150         NCAT_RETRIES,
151     )
152     .unwrap();
153 
154     let host_stdout = std::str::from_utf8(&output.stdout).unwrap();
155     assert_eq!(host_stdout.trim(), MESSAGE_TO_HOST);
156 
157     guest_cmd.wait_ok(vm).unwrap();
158 }
159 
160 #[test]
guest_to_host()161 fn guest_to_host() {
162     let host_port = generate_vhost_port();
163     let guest_cid = generate_guest_cid();
164     let config = Config::new().extra_args(vec!["--cid".to_string(), guest_cid.to_string()]);
165     let mut vm = TestVm::new(config).unwrap();
166     guest_to_host_connection(&mut vm, host_port);
167 }
168 
169 #[test]
guest_to_host_disable_sandbox()170 fn guest_to_host_disable_sandbox() {
171     let host_port = generate_vhost_port();
172     let guest_cid = generate_guest_cid();
173     let config = Config::new()
174         .extra_args(vec!["--cid".to_string(), guest_cid.to_string()])
175         .disable_sandbox();
176     let mut vm = TestVm::new(config).unwrap();
177     guest_to_host_connection(&mut vm, host_port);
178 }
179 
180 #[test]
guest_to_host_snapshot_restore()181 fn guest_to_host_snapshot_restore() {
182     let host_port = generate_vhost_port();
183     let guest_cid = generate_guest_cid();
184     let config = Config::new()
185         .extra_args(vec![
186             "--cid".to_string(),
187             guest_cid.to_string(),
188             "--no-usb".to_string(),
189         ])
190         .with_stdout_hardware("legacy-virtio-console");
191     let mut vm = TestVm::new(config).unwrap();
192     guest_to_host_connection(&mut vm, host_port);
193     let dir = tempdir().unwrap();
194     let snap = dir.path().join("snapshot.bkp");
195     vm.snapshot(&snap).unwrap();
196     let config = Config::new()
197         .extra_args(vec![
198             "--cid".to_string(),
199             guest_cid.to_string(),
200             "--no-usb".to_string(),
201             "--restore".to_string(),
202             snap.to_str().unwrap().to_string(),
203         ])
204         .with_stdout_hardware("legacy-virtio-console");
205     drop(vm);
206     vm = TestVm::new_restore(config).unwrap();
207     guest_to_host_connection(&mut vm, host_port);
208 }
209 
210 #[test]
guest_to_host_disable_sandbox_snapshot_restore()211 fn guest_to_host_disable_sandbox_snapshot_restore() {
212     let host_port = generate_vhost_port();
213     let guest_cid = generate_guest_cid();
214     let config = Config::new()
215         .extra_args(vec![
216             "--cid".to_string(),
217             guest_cid.to_string(),
218             "--no-usb".to_string(),
219         ])
220         .with_stdout_hardware("legacy-virtio-console")
221         .disable_sandbox();
222     let mut vm = TestVm::new(config).unwrap();
223     guest_to_host_connection(&mut vm, host_port);
224     let dir = tempdir().unwrap();
225     let snap = dir.path().join("snapshot.bkp");
226     vm.snapshot(&snap).unwrap();
227     let config = Config::new()
228         .extra_args(vec![
229             "--cid".to_string(),
230             guest_cid.to_string(),
231             "--no-usb".to_string(),
232             "--restore".to_string(),
233             snap.to_str().unwrap().to_string(),
234         ])
235         .with_stdout_hardware("legacy-virtio-console");
236     drop(vm);
237     vm = TestVm::new_restore(config.disable_sandbox()).unwrap();
238     guest_to_host_connection(&mut vm, host_port);
239 }
240 
guest_to_host_connection(vm: &mut TestVm, host_port: u32)241 fn guest_to_host_connection(vm: &mut TestVm, host_port: u32) {
242     let mut host_ncat = Command::new("ncat")
243         .arg("-l")
244         .arg("--send-only")
245         .args(["--vsock", ANY_CID, &host_port.to_string()])
246         .stdin(Stdio::piped())
247         .log()
248         .spawn()
249         .expect("failed to execute process");
250 
251     host_ncat
252         .stdin
253         .take()
254         .unwrap()
255         .write_all(MESSAGE_TO_GUEST.as_bytes())
256         .unwrap();
257 
258     let cmd = format!("ncat --recv-only --vsock {HOST_CID} {host_port}; echo ''");
259     let guest_stdout = retry(|| vm.exec_in_guest(&cmd), NCAT_RETRIES).unwrap();
260     assert_eq!(guest_stdout.stdout.trim(), MESSAGE_TO_GUEST);
261 
262     host_ncat.wait_with_timeout(SERVER_TIMEOUT).unwrap();
263 }
264 
create_vu_config(cmd_type: CmdType, socket: &Path, cid: u32) -> VuConfig265 fn create_vu_config(cmd_type: CmdType, socket: &Path, cid: u32) -> VuConfig {
266     let socket_path = socket.to_str().unwrap();
267     println!("cid={cid}, socket={socket_path}");
268     match cmd_type {
269         CmdType::Device => VuConfig::new(cmd_type, "vsock").extra_args(vec![
270             "vsock".to_string(),
271             "--socket-path".to_string(),
272             socket_path.to_string(),
273             "--cid".to_string(),
274             cid.to_string(),
275         ]),
276         CmdType::Devices => VuConfig::new(cmd_type, "vsock").extra_args(vec![
277             "--vsock".to_string(),
278             format!("vhost={},cid={}", socket_path, cid),
279         ]),
280     }
281 }
282 
283 #[test]
284 #[ignore = "b/333090069 test is flaky"]
vhost_user_host_to_guest()285 fn vhost_user_host_to_guest() {
286     let guest_port = generate_vhost_port();
287     let guest_cid = generate_guest_cid();
288     let socket = NamedTempFile::new().unwrap();
289 
290     let vu_config = create_vu_config(CmdType::Device, socket.path(), guest_cid);
291     let _vu_device = VhostUserBackend::new(vu_config).unwrap();
292 
293     let config = Config::new().extra_args(vec![
294         "--vhost-user".to_string(),
295         format!("vsock,socket={}", socket.path().to_str().unwrap()),
296     ]);
297 
298     let mut vm = TestVm::new(config).unwrap();
299     host_to_guest_connection(&mut vm, guest_cid, guest_port);
300 }
301 
302 #[test]
303 #[ignore = "b/333090069 test is flaky"]
vhost_user_host_to_guest_with_devices()304 fn vhost_user_host_to_guest_with_devices() {
305     let guest_port = generate_vhost_port();
306     let guest_cid = generate_guest_cid();
307     let socket = NamedTempFile::new().unwrap();
308 
309     let vu_config = create_vu_config(CmdType::Devices, socket.path(), guest_cid);
310     let _vu_device = VhostUserBackend::new(vu_config).unwrap();
311 
312     let config = Config::new().extra_args(vec![
313         "--vhost-user".to_string(),
314         format!("vsock,socket={}", socket.path().to_str().unwrap()),
315     ]);
316 
317     let mut vm = TestVm::new(config).unwrap();
318     host_to_guest_connection(&mut vm, guest_cid, guest_port);
319 }
320 
321 #[test]
vhost_user_guest_to_host()322 fn vhost_user_guest_to_host() {
323     let host_port = generate_vhost_port();
324     let guest_cid = generate_guest_cid();
325     let socket = NamedTempFile::new().unwrap();
326 
327     let vu_config = create_vu_config(CmdType::Device, socket.path(), guest_cid);
328     let _vu_device = VhostUserBackend::new(vu_config).unwrap();
329 
330     let config = Config::new().extra_args(vec![
331         "--vhost-user".to_string(),
332         format!("vsock,socket={}", socket.path().to_str().unwrap()),
333     ]);
334 
335     let mut vm = TestVm::new(config).unwrap();
336     guest_to_host_connection(&mut vm, host_port);
337 }
338 
339 #[test]
vhost_user_guest_to_host_with_devices()340 fn vhost_user_guest_to_host_with_devices() {
341     let host_port = generate_vhost_port();
342     let guest_cid = generate_guest_cid();
343     let socket = NamedTempFile::new().unwrap();
344 
345     let vu_config = create_vu_config(CmdType::Devices, socket.path(), guest_cid);
346     let _vu_device = VhostUserBackend::new(vu_config).unwrap();
347 
348     let config = Config::new().extra_args(vec![
349         "--vhost-user".to_string(),
350         format!("vsock,socket={}", socket.path().to_str().unwrap()),
351     ]);
352 
353     let mut vm = TestVm::new(config).unwrap();
354     guest_to_host_connection(&mut vm, host_port);
355 }
356