• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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]
host_to_guest()47 fn host_to_guest() {
48     let guest_port = generate_vhost_port();
49     let guest_cid = generate_guest_cid();
50     let config = Config::new().extra_args(vec!["--cid".to_string(), guest_cid.to_string()]);
51     let mut vm = TestVm::new(config).unwrap();
52     host_to_guest_connection(&mut vm, guest_cid, guest_port);
53 }
54 
55 #[test]
host_to_guest_disable_sandbox()56 fn host_to_guest_disable_sandbox() {
57     let guest_port = generate_vhost_port();
58     let guest_cid = generate_guest_cid();
59     let config = Config::new()
60         .extra_args(vec!["--cid".to_string(), guest_cid.to_string()])
61         .disable_sandbox();
62     let mut vm = TestVm::new(config).unwrap();
63     host_to_guest_connection(&mut vm, guest_cid, guest_port);
64 }
65 
66 #[test]
host_to_guest_snapshot_restore()67 fn host_to_guest_snapshot_restore() {
68     let guest_port = generate_vhost_port();
69     let guest_cid = generate_guest_cid();
70     let config = Config::new()
71         .extra_args(vec![
72             "--cid".to_string(),
73             guest_cid.to_string(),
74             "--no-usb".to_string(),
75         ])
76         .with_stdout_hardware("legacy-virtio-console");
77     let mut vm = TestVm::new(config).unwrap();
78     host_to_guest_connection(&mut vm, guest_cid, guest_port);
79     let dir = tempdir().unwrap();
80     let snap = dir.path().join("snapshot.bkp");
81     vm.snapshot(&snap).unwrap();
82     let config = Config::new()
83         .extra_args(vec![
84             "--cid".to_string(),
85             guest_cid.to_string(),
86             "--restore".to_string(),
87             snap.to_str().unwrap().to_string(),
88             "--no-usb".to_string(),
89         ])
90         .with_stdout_hardware("legacy-virtio-console");
91     drop(vm);
92     vm = TestVm::new_restore(config).unwrap();
93     host_to_guest_connection(&mut vm, guest_cid, guest_port);
94 }
95 
96 #[test]
host_to_guest_disable_sandbox_snapshot_restore()97 fn host_to_guest_disable_sandbox_snapshot_restore() {
98     let guest_port = generate_vhost_port();
99     let guest_cid = generate_guest_cid();
100     let config = Config::new()
101         .extra_args(vec![
102             "--cid".to_string(),
103             guest_cid.to_string(),
104             "--no-usb".to_string(),
105         ])
106         .with_stdout_hardware("legacy-virtio-console");
107     let mut vm = TestVm::new(config.disable_sandbox()).unwrap();
108     host_to_guest_connection(&mut vm, guest_cid, guest_port);
109     let dir = tempdir().unwrap();
110     let snap = dir.path().join("snapshot.bkp");
111     vm.snapshot(&snap).unwrap();
112     let config = Config::new()
113         .extra_args(vec![
114             "--cid".to_string(),
115             guest_cid.to_string(),
116             "--restore".to_string(),
117             snap.to_str().unwrap().to_string(),
118             "--no-usb".to_string(),
119         ])
120         .with_stdout_hardware("legacy-virtio-console");
121     drop(vm);
122     vm = TestVm::new_restore(config.disable_sandbox()).unwrap();
123     host_to_guest_connection(&mut vm, guest_cid, guest_port);
124 }
125 
host_to_guest_connection(vm: &mut TestVm, guest_cid: u32, guest_port: u32)126 fn host_to_guest_connection(vm: &mut TestVm, guest_cid: u32, guest_port: u32) {
127     let guest_cmd = vm
128         .exec_in_guest_async(&format!(
129             "echo {MESSAGE_TO_HOST} | ncat -l --vsock --send-only {ANY_CID} {guest_port}"
130         ))
131         .unwrap();
132 
133     let output = retry(
134         || {
135             Command::new("ncat")
136                 .args([
137                     "--recv-only",
138                     "--vsock",
139                     &guest_cid.to_string(),
140                     &guest_port.to_string(),
141                 ])
142                 .stderr(Stdio::inherit())
143                 .log()
144                 .output_checked()
145         },
146         NCAT_RETRIES,
147     )
148     .unwrap();
149 
150     let host_stdout = std::str::from_utf8(&output.stdout).unwrap();
151     assert_eq!(host_stdout.trim(), MESSAGE_TO_HOST);
152 
153     guest_cmd.wait_ok(vm).unwrap();
154 }
155 
156 #[test]
guest_to_host()157 fn guest_to_host() {
158     let host_port = generate_vhost_port();
159     let guest_cid = generate_guest_cid();
160     let config = Config::new().extra_args(vec!["--cid".to_string(), guest_cid.to_string()]);
161     let mut vm = TestVm::new(config).unwrap();
162     guest_to_host_connection(&mut vm, host_port);
163 }
164 
165 #[test]
guest_to_host_disable_sandbox()166 fn guest_to_host_disable_sandbox() {
167     let host_port = generate_vhost_port();
168     let guest_cid = generate_guest_cid();
169     let config = Config::new()
170         .extra_args(vec!["--cid".to_string(), guest_cid.to_string()])
171         .disable_sandbox();
172     let mut vm = TestVm::new(config).unwrap();
173     guest_to_host_connection(&mut vm, host_port);
174 }
175 
176 #[test]
guest_to_host_snapshot_restore()177 fn guest_to_host_snapshot_restore() {
178     let host_port = generate_vhost_port();
179     let guest_cid = generate_guest_cid();
180     let config = Config::new()
181         .extra_args(vec![
182             "--cid".to_string(),
183             guest_cid.to_string(),
184             "--no-usb".to_string(),
185         ])
186         .with_stdout_hardware("legacy-virtio-console");
187     let mut vm = TestVm::new(config).unwrap();
188     guest_to_host_connection(&mut vm, host_port);
189     let dir = tempdir().unwrap();
190     let snap = dir.path().join("snapshot.bkp");
191     vm.snapshot(&snap).unwrap();
192     let config = Config::new()
193         .extra_args(vec![
194             "--cid".to_string(),
195             guest_cid.to_string(),
196             "--no-usb".to_string(),
197             "--restore".to_string(),
198             snap.to_str().unwrap().to_string(),
199         ])
200         .with_stdout_hardware("legacy-virtio-console");
201     drop(vm);
202     vm = TestVm::new_restore(config).unwrap();
203     guest_to_host_connection(&mut vm, host_port);
204 }
205 
206 #[test]
guest_to_host_disable_sandbox_snapshot_restore()207 fn guest_to_host_disable_sandbox_snapshot_restore() {
208     let host_port = generate_vhost_port();
209     let guest_cid = generate_guest_cid();
210     let config = Config::new()
211         .extra_args(vec![
212             "--cid".to_string(),
213             guest_cid.to_string(),
214             "--no-usb".to_string(),
215         ])
216         .with_stdout_hardware("legacy-virtio-console")
217         .disable_sandbox();
218     let mut vm = TestVm::new(config).unwrap();
219     guest_to_host_connection(&mut vm, host_port);
220     let dir = tempdir().unwrap();
221     let snap = dir.path().join("snapshot.bkp");
222     vm.snapshot(&snap).unwrap();
223     let config = Config::new()
224         .extra_args(vec![
225             "--cid".to_string(),
226             guest_cid.to_string(),
227             "--no-usb".to_string(),
228             "--restore".to_string(),
229             snap.to_str().unwrap().to_string(),
230         ])
231         .with_stdout_hardware("legacy-virtio-console");
232     drop(vm);
233     vm = TestVm::new_restore(config.disable_sandbox()).unwrap();
234     guest_to_host_connection(&mut vm, host_port);
235 }
236 
guest_to_host_connection(vm: &mut TestVm, host_port: u32)237 fn guest_to_host_connection(vm: &mut TestVm, host_port: u32) {
238     let mut host_ncat = Command::new("ncat")
239         .arg("-l")
240         .arg("--send-only")
241         .args(["--vsock", ANY_CID, &host_port.to_string()])
242         .stdin(Stdio::piped())
243         .log()
244         .spawn()
245         .expect("failed to execute process");
246 
247     host_ncat
248         .stdin
249         .take()
250         .unwrap()
251         .write_all(MESSAGE_TO_GUEST.as_bytes())
252         .unwrap();
253 
254     let cmd = format!("ncat --recv-only --vsock {HOST_CID} {host_port}; echo ''");
255     let guest_stdout = retry(|| vm.exec_in_guest(&cmd), NCAT_RETRIES).unwrap();
256     assert_eq!(guest_stdout.stdout.trim(), MESSAGE_TO_GUEST);
257 
258     host_ncat.wait_with_timeout(SERVER_TIMEOUT).unwrap();
259 }
260 
create_vu_config(cmd_type: CmdType, socket: &Path, cid: u32) -> VuConfig261 fn create_vu_config(cmd_type: CmdType, socket: &Path, cid: u32) -> VuConfig {
262     let socket_path = socket.to_str().unwrap();
263     println!("cid={cid}, socket={socket_path}");
264     match cmd_type {
265         CmdType::Device => VuConfig::new(cmd_type, "vsock").extra_args(vec![
266             "vsock".to_string(),
267             "--socket".to_string(),
268             socket_path.to_string(),
269             "--cid".to_string(),
270             cid.to_string(),
271         ]),
272         CmdType::Devices => VuConfig::new(cmd_type, "vsock").extra_args(vec![
273             "--vsock".to_string(),
274             format!("vhost={},cid={}", socket_path, cid),
275         ]),
276     }
277 }
278 
279 #[test]
280 #[ignore = "b/333090069 test is flaky"]
vhost_user_host_to_guest()281 fn vhost_user_host_to_guest() {
282     let guest_port = generate_vhost_port();
283     let guest_cid = generate_guest_cid();
284     let socket = NamedTempFile::new().unwrap();
285 
286     let vu_config = create_vu_config(CmdType::Device, socket.path(), guest_cid);
287     let _vu_device = VhostUserBackend::new(vu_config).unwrap();
288 
289     let config = Config::new().extra_args(vec![
290         "--vhost-user".to_string(),
291         format!("vsock,socket={}", socket.path().to_str().unwrap()),
292     ]);
293 
294     let mut vm = TestVm::new(config).unwrap();
295     host_to_guest_connection(&mut vm, guest_cid, guest_port);
296 }
297 
298 #[test]
299 #[ignore = "b/333090069 test is flaky"]
vhost_user_host_to_guest_with_devices()300 fn vhost_user_host_to_guest_with_devices() {
301     let guest_port = generate_vhost_port();
302     let guest_cid = generate_guest_cid();
303     let socket = NamedTempFile::new().unwrap();
304 
305     let vu_config = create_vu_config(CmdType::Devices, socket.path(), guest_cid);
306     let _vu_device = VhostUserBackend::new(vu_config).unwrap();
307 
308     let config = Config::new().extra_args(vec![
309         "--vhost-user".to_string(),
310         format!("vsock,socket={}", socket.path().to_str().unwrap()),
311     ]);
312 
313     let mut vm = TestVm::new(config).unwrap();
314     host_to_guest_connection(&mut vm, guest_cid, guest_port);
315 }
316 
317 #[test]
vhost_user_guest_to_host()318 fn vhost_user_guest_to_host() {
319     let host_port = generate_vhost_port();
320     let guest_cid = generate_guest_cid();
321     let socket = NamedTempFile::new().unwrap();
322 
323     let vu_config = create_vu_config(CmdType::Device, socket.path(), guest_cid);
324     let _vu_device = VhostUserBackend::new(vu_config).unwrap();
325 
326     let config = Config::new().extra_args(vec![
327         "--vhost-user".to_string(),
328         format!("vsock,socket={}", socket.path().to_str().unwrap()),
329     ]);
330 
331     let mut vm = TestVm::new(config).unwrap();
332     guest_to_host_connection(&mut vm, host_port);
333 }
334 
335 #[test]
vhost_user_guest_to_host_with_devices()336 fn vhost_user_guest_to_host_with_devices() {
337     let host_port = generate_vhost_port();
338     let guest_cid = generate_guest_cid();
339     let socket = NamedTempFile::new().unwrap();
340 
341     let vu_config = create_vu_config(CmdType::Devices, socket.path(), guest_cid);
342     let _vu_device = VhostUserBackend::new(vu_config).unwrap();
343 
344     let config = Config::new().extra_args(vec![
345         "--vhost-user".to_string(),
346         format!("vsock,socket={}", socket.path().to_str().unwrap()),
347     ]);
348 
349     let mut vm = TestVm::new(config).unwrap();
350     guest_to_host_connection(&mut vm, host_port);
351 }
352