vmm/vmm_config/
vsock.rs

1// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4use std::convert::TryFrom;
5use std::sync::{Arc, Mutex};
6
7use serde::{Deserialize, Serialize};
8
9use crate::devices::virtio::vsock::{Vsock, VsockError, VsockUnixBackend, VsockUnixBackendError};
10
11type MutexVsockUnix = Arc<Mutex<Vsock<VsockUnixBackend>>>;
12
13/// Errors associated with `NetworkInterfaceConfig`.
14#[derive(Debug, derive_more::From, thiserror::Error, displaydoc::Display)]
15pub enum VsockConfigError {
16    /// Cannot create backend for vsock device: {0}
17    CreateVsockBackend(VsockUnixBackendError),
18    /// Cannot create vsock device: {0}
19    CreateVsockDevice(VsockError),
20}
21
22/// This struct represents the strongly typed equivalent of the json body
23/// from vsock related requests.
24#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
25#[serde(deny_unknown_fields)]
26pub struct VsockDeviceConfig {
27    #[serde(default)]
28    #[serde(skip_serializing_if = "Option::is_none")]
29    /// ID of the vsock device.
30    pub vsock_id: Option<String>,
31    /// A 32-bit Context Identifier (CID) used to identify the guest.
32    pub guest_cid: u32,
33    /// Path to local unix socket.
34    pub uds_path: String,
35}
36
37#[derive(Debug)]
38struct VsockAndUnixPath {
39    vsock: MutexVsockUnix,
40    uds_path: String,
41}
42
43impl From<&VsockAndUnixPath> for VsockDeviceConfig {
44    fn from(vsock: &VsockAndUnixPath) -> Self {
45        let vsock_lock = vsock.vsock.lock().unwrap();
46        VsockDeviceConfig {
47            vsock_id: None,
48            guest_cid: u32::try_from(vsock_lock.cid()).unwrap(),
49            uds_path: vsock.uds_path.clone(),
50        }
51    }
52}
53
54/// A builder of Vsock with Unix backend from 'VsockDeviceConfig'.
55#[derive(Debug, Default)]
56pub struct VsockBuilder {
57    inner: Option<VsockAndUnixPath>,
58}
59
60impl VsockBuilder {
61    /// Creates an empty Vsock with Unix backend Store.
62    pub fn new() -> Self {
63        Self { inner: None }
64    }
65
66    /// Inserts an existing vsock device.
67    pub fn set_device(&mut self, device: Arc<Mutex<Vsock<VsockUnixBackend>>>) {
68        self.inner = Some(VsockAndUnixPath {
69            uds_path: device
70                .lock()
71                .expect("Poisoned lock")
72                .backend()
73                .host_sock_path()
74                .to_owned(),
75            vsock: device.clone(),
76        });
77    }
78
79    /// Inserts a Unix backend Vsock in the store.
80    /// If an entry already exists, it will overwrite it.
81    pub fn insert(&mut self, cfg: VsockDeviceConfig) -> Result<(), VsockConfigError> {
82        // Make sure to drop the old one and remove the socket before creating a new one.
83        if let Some(existing) = self.inner.take() {
84            std::fs::remove_file(existing.uds_path).map_err(VsockUnixBackendError::UnixBind)?;
85        }
86        self.inner = Some(VsockAndUnixPath {
87            uds_path: cfg.uds_path.clone(),
88            vsock: Arc::new(Mutex::new(Self::create_unixsock_vsock(cfg)?)),
89        });
90        Ok(())
91    }
92
93    /// Provides a reference to the Vsock if present.
94    pub fn get(&self) -> Option<&MutexVsockUnix> {
95        self.inner.as_ref().map(|pair| &pair.vsock)
96    }
97
98    /// Creates a Vsock device from a VsockDeviceConfig.
99    pub fn create_unixsock_vsock(
100        cfg: VsockDeviceConfig,
101    ) -> Result<Vsock<VsockUnixBackend>, VsockConfigError> {
102        let backend = VsockUnixBackend::new(u64::from(cfg.guest_cid), cfg.uds_path)?;
103
104        Vsock::new(u64::from(cfg.guest_cid), backend).map_err(VsockConfigError::CreateVsockDevice)
105    }
106
107    /// Returns the structure used to configure the vsock device.
108    pub fn config(&self) -> Option<VsockDeviceConfig> {
109        self.inner.as_ref().map(VsockDeviceConfig::from)
110    }
111}
112
113#[cfg(test)]
114pub(crate) mod tests {
115    use vmm_sys_util::tempfile::TempFile;
116
117    use super::*;
118    use crate::devices::virtio::vsock::VSOCK_DEV_ID;
119
120    pub(crate) fn default_config(tmp_sock_file: &TempFile) -> VsockDeviceConfig {
121        VsockDeviceConfig {
122            vsock_id: None,
123            guest_cid: 3,
124            uds_path: tmp_sock_file.as_path().to_str().unwrap().to_string(),
125        }
126    }
127
128    #[test]
129    fn test_vsock_create() {
130        let mut tmp_sock_file = TempFile::new().unwrap();
131        tmp_sock_file.remove().unwrap();
132        let vsock_config = default_config(&tmp_sock_file);
133        VsockBuilder::create_unixsock_vsock(vsock_config).unwrap();
134    }
135
136    #[test]
137    fn test_vsock_insert() {
138        let mut store = VsockBuilder::new();
139        let mut tmp_sock_file = TempFile::new().unwrap();
140        tmp_sock_file.remove().unwrap();
141        let mut vsock_config = default_config(&tmp_sock_file);
142
143        store.insert(vsock_config.clone()).unwrap();
144        let vsock = store.get().unwrap();
145        assert_eq!(vsock.lock().unwrap().id(), VSOCK_DEV_ID);
146
147        let new_cid = vsock_config.guest_cid + 1;
148        vsock_config.guest_cid = new_cid;
149        store.insert(vsock_config).unwrap();
150        let vsock = store.get().unwrap();
151        assert_eq!(vsock.lock().unwrap().cid(), u64::from(new_cid));
152    }
153
154    #[test]
155    fn test_vsock_config() {
156        let mut vsock_builder = VsockBuilder::new();
157        let mut tmp_sock_file = TempFile::new().unwrap();
158        tmp_sock_file.remove().unwrap();
159        let vsock_config = default_config(&tmp_sock_file);
160        vsock_builder.insert(vsock_config.clone()).unwrap();
161
162        let config = vsock_builder.config();
163        assert!(config.is_some());
164        assert_eq!(config.unwrap(), vsock_config);
165    }
166
167    #[test]
168    fn test_set_device() {
169        let mut vsock_builder = VsockBuilder::new();
170        let mut tmp_sock_file = TempFile::new().unwrap();
171        tmp_sock_file.remove().unwrap();
172        let vsock = Vsock::new(
173            0,
174            VsockUnixBackend::new(1, tmp_sock_file.as_path().to_str().unwrap().to_string())
175                .unwrap(),
176        )
177        .unwrap();
178
179        vsock_builder.set_device(Arc::new(Mutex::new(vsock)));
180        assert!(vsock_builder.inner.is_some());
181        assert_eq!(
182            vsock_builder.inner.unwrap().uds_path,
183            tmp_sock_file.as_path().to_str().unwrap().to_string()
184        )
185    }
186}