• 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::retry_with_delay;
17 use fixture::utils::ChildExt;
18 use fixture::utils::CommandExt;
19 use fixture::vhost_user::CmdType;
20 use fixture::vhost_user::Config as VuConfig;
21 use fixture::vhost_user::VhostUserBackend;
22 use fixture::vm::Config;
23 use fixture::vm::TestVm;
24 use rand::Rng;
25 use tempfile::tempdir;
26 use tempfile::NamedTempFile;
27 
28 const ANY_CID: &str = "4294967295"; // -1U
29 const HOST_CID: u64 = 2;
30 
31 const SERVER_TIMEOUT: Duration = Duration::from_secs(3);
32 const NCAT_RETRIES: usize = 15;
33 const NCAT_RETRY_DELAY: Duration = Duration::from_millis(300);
34 
35 const MESSAGE_TO_HOST: &str = "Connection from the host is successfully established";
36 const MESSAGE_TO_GUEST: &str = "Connection from the guest is successfully established";
37 
38 // generate a random CID to avoid conflicts with other VMs run on different processes
generate_guest_cid() -> u3239 fn generate_guest_cid() -> u32 {
40     // avoid special CIDs and negative values
41     rand::thread_rng().gen_range(3..0x8000_0000)
42 }
43 
generate_vhost_port() -> u3244 fn generate_vhost_port() -> u32 {
45     rand::thread_rng().gen_range(10000..99999)
46 }
47 
48 #[test]
host_to_guest()49 fn host_to_guest() {
50     let guest_port = generate_vhost_port();
51     let guest_cid = generate_guest_cid();
52     let config = Config::new().extra_args(vec!["--cid".to_string(), guest_cid.to_string()]);
53     let mut vm = TestVm::new(config).unwrap();
54     host_to_guest_connection(&mut vm, guest_cid, guest_port);
55 }
56 
57 #[test]
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]
host_to_guest_snapshot_restore()69 fn host_to_guest_snapshot_restore() {
70     let guest_port = generate_vhost_port();
71     let guest_cid = generate_guest_cid();
72     let config = Config::new().extra_args(vec![
73         "--cid".to_string(),
74         guest_cid.to_string(),
75         "--no-usb".to_string(),
76     ]);
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().extra_args(vec![
83         "--cid".to_string(),
84         guest_cid.to_string(),
85         "--restore".to_string(),
86         snap.to_str().unwrap().to_string(),
87         "--no-usb".to_string(),
88     ]);
89     drop(vm);
90     vm = TestVm::new_restore(config).unwrap();
91     host_to_guest_connection(&mut vm, guest_cid, guest_port);
92 }
93 
94 #[test]
host_to_guest_disable_sandbox_snapshot_restore()95 fn host_to_guest_disable_sandbox_snapshot_restore() {
96     let guest_port = generate_vhost_port();
97     let guest_cid = generate_guest_cid();
98     let config = Config::new().extra_args(vec![
99         "--cid".to_string(),
100         guest_cid.to_string(),
101         "--no-usb".to_string(),
102     ]);
103     let mut vm = TestVm::new(config.disable_sandbox()).unwrap();
104     host_to_guest_connection(&mut vm, guest_cid, guest_port);
105     let dir = tempdir().unwrap();
106     let snap = dir.path().join("snapshot.bkp");
107     vm.snapshot(&snap).unwrap();
108     let config = Config::new().extra_args(vec![
109         "--cid".to_string(),
110         guest_cid.to_string(),
111         "--restore".to_string(),
112         snap.to_str().unwrap().to_string(),
113         "--no-usb".to_string(),
114     ]);
115     drop(vm);
116     vm = TestVm::new_restore(config.disable_sandbox()).unwrap();
117     host_to_guest_connection(&mut vm, guest_cid, guest_port);
118 }
119 
host_to_guest_connection(vm: &mut TestVm, guest_cid: u32, guest_port: u32)120 fn host_to_guest_connection(vm: &mut TestVm, guest_cid: u32, guest_port: u32) {
121     let guest_cmd = vm
122         .exec_in_guest_async(&format!(
123             "echo {MESSAGE_TO_HOST} | ncat -v -l --vsock --send-only {ANY_CID} {guest_port}"
124         ))
125         .unwrap();
126 
127     let output = retry_with_delay(
128         || {
129             // This will instantly fail if the guest isn't listening on the port yet, so we need to
130             // retry with a delay.
131             Command::new("ncat")
132                 .args([
133                     "-v",
134                     "--recv-only",
135                     "--vsock",
136                     &guest_cid.to_string(),
137                     &guest_port.to_string(),
138                 ])
139                 .stderr(Stdio::inherit())
140                 .log()
141                 .output_checked()
142         },
143         NCAT_RETRIES,
144         NCAT_RETRY_DELAY,
145     )
146     .unwrap();
147 
148     let host_stdout = std::str::from_utf8(&output.stdout).unwrap();
149     assert_eq!(host_stdout.trim(), MESSAGE_TO_HOST);
150 
151     guest_cmd.wait_ok(vm).unwrap();
152 }
153 
154 #[test]
guest_to_host()155 fn guest_to_host() {
156     let host_port = generate_vhost_port();
157     let guest_cid = generate_guest_cid();
158     let config = Config::new().extra_args(vec!["--cid".to_string(), guest_cid.to_string()]);
159     let mut vm = TestVm::new(config).unwrap();
160     guest_to_host_connection(&mut vm, host_port);
161 }
162 
163 #[test]
guest_to_host_disable_sandbox()164 fn guest_to_host_disable_sandbox() {
165     let host_port = generate_vhost_port();
166     let guest_cid = generate_guest_cid();
167     let config = Config::new()
168         .extra_args(vec!["--cid".to_string(), guest_cid.to_string()])
169         .disable_sandbox();
170     let mut vm = TestVm::new(config).unwrap();
171     guest_to_host_connection(&mut vm, host_port);
172 }
173 
174 #[test]
guest_to_host_snapshot_restore()175 fn guest_to_host_snapshot_restore() {
176     let host_port = generate_vhost_port();
177     let guest_cid = generate_guest_cid();
178     let config = Config::new().extra_args(vec![
179         "--cid".to_string(),
180         guest_cid.to_string(),
181         "--no-usb".to_string(),
182     ]);
183     let mut vm = TestVm::new(config).unwrap();
184     guest_to_host_connection(&mut vm, host_port);
185     let dir = tempdir().unwrap();
186     let snap = dir.path().join("snapshot.bkp");
187     vm.snapshot(&snap).unwrap();
188     let config = Config::new().extra_args(vec![
189         "--cid".to_string(),
190         guest_cid.to_string(),
191         "--no-usb".to_string(),
192         "--restore".to_string(),
193         snap.to_str().unwrap().to_string(),
194     ]);
195     drop(vm);
196     vm = TestVm::new_restore(config).unwrap();
197     guest_to_host_connection(&mut vm, host_port);
198 }
199 
200 #[test]
guest_to_host_disable_sandbox_snapshot_restore()201 fn guest_to_host_disable_sandbox_snapshot_restore() {
202     let host_port = generate_vhost_port();
203     let guest_cid = generate_guest_cid();
204     let config = Config::new()
205         .extra_args(vec![
206             "--cid".to_string(),
207             guest_cid.to_string(),
208             "--no-usb".to_string(),
209         ])
210         .disable_sandbox();
211     let mut vm = TestVm::new(config).unwrap();
212     guest_to_host_connection(&mut vm, host_port);
213     let dir = tempdir().unwrap();
214     let snap = dir.path().join("snapshot.bkp");
215     vm.snapshot(&snap).unwrap();
216     let config = Config::new().extra_args(vec![
217         "--cid".to_string(),
218         guest_cid.to_string(),
219         "--no-usb".to_string(),
220         "--restore".to_string(),
221         snap.to_str().unwrap().to_string(),
222     ]);
223     drop(vm);
224     vm = TestVm::new_restore(config.disable_sandbox()).unwrap();
225     guest_to_host_connection(&mut vm, host_port);
226 }
227 
guest_to_host_connection(vm: &mut TestVm, host_port: u32)228 fn guest_to_host_connection(vm: &mut TestVm, host_port: u32) {
229     let mut host_ncat = Command::new("ncat")
230         .arg("-l")
231         .arg("--send-only")
232         .args(["--vsock", ANY_CID, &host_port.to_string()])
233         .stdin(Stdio::piped())
234         .log()
235         .spawn()
236         .expect("failed to execute process");
237 
238     host_ncat
239         .stdin
240         .take()
241         .unwrap()
242         .write_all(MESSAGE_TO_GUEST.as_bytes())
243         .unwrap();
244 
245     let cmd = format!("ncat --recv-only --vsock {HOST_CID} {host_port}; echo ''");
246     let guest_stdout = retry(|| vm.exec_in_guest(&cmd), NCAT_RETRIES).unwrap();
247     assert_eq!(guest_stdout.stdout.trim(), MESSAGE_TO_GUEST);
248 
249     host_ncat.wait_with_timeout(SERVER_TIMEOUT).unwrap();
250 }
251 
create_vu_config(cmd_type: CmdType, socket: &Path, cid: u32) -> VuConfig252 fn create_vu_config(cmd_type: CmdType, socket: &Path, cid: u32) -> VuConfig {
253     let socket_path = socket.to_str().unwrap();
254     println!("cid={cid}, socket={socket_path}");
255     match cmd_type {
256         CmdType::Device => VuConfig::new(cmd_type, "vsock").extra_args(vec![
257             "vsock".to_string(),
258             "--socket-path".to_string(),
259             socket_path.to_string(),
260             "--cid".to_string(),
261             cid.to_string(),
262         ]),
263         CmdType::Devices => VuConfig::new(cmd_type, "vsock").extra_args(vec![
264             "--vsock".to_string(),
265             format!("vhost={},cid={}", socket_path, cid),
266         ]),
267     }
268 }
269 
270 #[test]
vhost_user_host_to_guest()271 fn vhost_user_host_to_guest() {
272     let guest_port = generate_vhost_port();
273     let guest_cid = generate_guest_cid();
274     let socket = NamedTempFile::new().unwrap();
275 
276     let vu_config = create_vu_config(CmdType::Device, socket.path(), guest_cid);
277     let _vu_device = VhostUserBackend::new(vu_config).unwrap();
278 
279     let config = Config::new().extra_args(vec![
280         "--vhost-user".to_string(),
281         format!("vsock,socket={}", socket.path().to_str().unwrap()),
282     ]);
283 
284     let mut vm = TestVm::new(config).unwrap();
285     host_to_guest_connection(&mut vm, guest_cid, guest_port);
286 }
287 
288 #[test]
vhost_user_host_to_guest_with_devices()289 fn vhost_user_host_to_guest_with_devices() {
290     let guest_port = generate_vhost_port();
291     let guest_cid = generate_guest_cid();
292     let socket = NamedTempFile::new().unwrap();
293 
294     let vu_config = create_vu_config(CmdType::Devices, socket.path(), guest_cid);
295     let _vu_device = VhostUserBackend::new(vu_config).unwrap();
296 
297     let config = Config::new().extra_args(vec![
298         "--vhost-user".to_string(),
299         format!("vsock,socket={}", socket.path().to_str().unwrap()),
300     ]);
301 
302     let mut vm = TestVm::new(config).unwrap();
303     host_to_guest_connection(&mut vm, guest_cid, guest_port);
304 }
305 
306 #[test]
vhost_user_guest_to_host()307 fn vhost_user_guest_to_host() {
308     let host_port = generate_vhost_port();
309     let guest_cid = generate_guest_cid();
310     let socket = NamedTempFile::new().unwrap();
311 
312     let vu_config = create_vu_config(CmdType::Device, socket.path(), guest_cid);
313     let _vu_device = VhostUserBackend::new(vu_config).unwrap();
314 
315     let config = Config::new().extra_args(vec![
316         "--vhost-user".to_string(),
317         format!("vsock,socket={}", socket.path().to_str().unwrap()),
318     ]);
319 
320     let mut vm = TestVm::new(config).unwrap();
321     guest_to_host_connection(&mut vm, host_port);
322 }
323 
324 #[test]
vhost_user_guest_to_host_with_devices()325 fn vhost_user_guest_to_host_with_devices() {
326     let host_port = generate_vhost_port();
327     let guest_cid = generate_guest_cid();
328     let socket = NamedTempFile::new().unwrap();
329 
330     let vu_config = create_vu_config(CmdType::Devices, socket.path(), guest_cid);
331     let _vu_device = VhostUserBackend::new(vu_config).unwrap();
332 
333     let config = Config::new().extra_args(vec![
334         "--vhost-user".to_string(),
335         format!("vsock,socket={}", socket.path().to_str().unwrap()),
336     ]);
337 
338     let mut vm = TestVm::new(config).unwrap();
339     guest_to_host_connection(&mut vm, host_port);
340 }
341