devices/virtio/vsock/sys/
linux.rs

1// Copyright 2023 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
5use std::path::Path;
6use std::path::PathBuf;
7
8use serde::Deserialize;
9use serde::Serialize;
10use serde_keyvalue::FromKeyValues;
11
12use crate::virtio::VirtioDevice;
13use crate::VirtioDeviceArgs;
14use crate::VirtioDeviceModule;
15
16static VHOST_VSOCK_DEFAULT_PATH: &str = "/dev/vhost-vsock";
17
18fn default_vsock_path() -> PathBuf {
19    PathBuf::from(VHOST_VSOCK_DEFAULT_PATH)
20}
21
22#[derive(Debug, Deserialize, Serialize, PartialEq, Eq, FromKeyValues)]
23#[serde(deny_unknown_fields, rename_all = "kebab-case")]
24/// Configuration for a Vsock device.
25pub struct VsockConfig {
26    /// CID to be used for this vsock device.
27    pub cid: u64,
28    /// Path to the vhost-vsock device.
29    #[serde(default = "default_vsock_path", rename = "device")]
30    pub vhost_device: PathBuf,
31    #[serde(default)]
32    pub max_queue_sizes: Option<[u16; 3]>,
33}
34
35impl VsockConfig {
36    /// Create a new vsock configuration. If `vhost_device` is `None`, the default vhost-vsock
37    /// device path will be used.
38    pub fn new<P: AsRef<Path>>(cid: u64, vhost_device: Option<P>) -> Self {
39        Self {
40            cid,
41            #[cfg(any(target_os = "android", target_os = "linux"))]
42            vhost_device: vhost_device
43                .map(|p| PathBuf::from(p.as_ref()))
44                .unwrap_or_else(|| PathBuf::from(VHOST_VSOCK_DEFAULT_PATH)),
45            max_queue_sizes: None,
46        }
47    }
48}
49
50#[derive(Serialize, Deserialize)]
51pub struct VirtioVsockModule {
52    config: VsockConfig,
53}
54
55impl VirtioVsockModule {
56    pub fn new(config: VsockConfig) -> Self {
57        Self { config }
58    }
59}
60
61impl VirtioDeviceModule for VirtioVsockModule {
62    fn sort_name(&self) -> &'static str {
63        "vsock"
64    }
65
66    fn create(&self, args: &mut VirtioDeviceArgs<'_>) -> anyhow::Result<Box<dyn VirtioDevice>> {
67        let features = crate::virtio::base_features(args.protection_type);
68        let dev = crate::virtio::vhost::Vsock::new(features, &self.config)?;
69        Ok(Box::new(dev))
70    }
71
72    fn create_jail(
73        &self,
74        jail_config: &jail::JailConfig,
75    ) -> anyhow::Result<Option<minijail::Minijail>> {
76        let jail = jail::simple_jail(
77            Some(jail_config),
78            &crate::virtio::VirtioDeviceType::Regular.seccomp_policy_file("vhost_vsock"),
79        )?;
80        Ok(jail)
81    }
82}
83
84#[cfg(test)]
85mod tests {
86    use serde_keyvalue::from_key_values;
87    use serde_keyvalue::ErrorKind;
88    use serde_keyvalue::ParseError;
89
90    use super::*;
91
92    fn from_vsock_arg(options: &str) -> Result<VsockConfig, ParseError> {
93        from_key_values(options)
94    }
95
96    #[test]
97    fn params_from_key_values() {
98        // Default device
99        assert_eq!(
100            from_vsock_arg("cid=56").unwrap(),
101            VsockConfig {
102                vhost_device: VHOST_VSOCK_DEFAULT_PATH.into(),
103                cid: 56,
104                max_queue_sizes: None,
105            }
106        );
107
108        // No argument
109        assert_eq!(
110            from_vsock_arg("").unwrap_err(),
111            ParseError {
112                kind: ErrorKind::SerdeError("missing field `cid`".into()),
113                pos: 0
114            }
115        );
116
117        // CID passed without key
118        assert_eq!(
119            from_vsock_arg("78").unwrap(),
120            VsockConfig {
121                #[cfg(any(target_os = "android", target_os = "linux"))]
122                vhost_device: VHOST_VSOCK_DEFAULT_PATH.into(),
123                cid: 78,
124                max_queue_sizes: None,
125            }
126        );
127
128        // CID passed twice
129        assert_eq!(
130            from_vsock_arg("cid=42,cid=56").unwrap_err(),
131            ParseError {
132                kind: ErrorKind::SerdeError("duplicate field `cid`".into()),
133                pos: 0,
134            }
135        );
136
137        // Invalid argument
138        assert_eq!(
139            from_vsock_arg("invalid=foo").unwrap_err(),
140            ParseError {
141                kind: ErrorKind::SerdeError(
142                    "unknown field `invalid`, expected one of `cid`, `device`, `max-queue-sizes`"
143                        .into()
144                ),
145                pos: 0,
146            }
147        );
148
149        // Path device
150        assert_eq!(
151            from_vsock_arg("device=/some/path,cid=56").unwrap(),
152            VsockConfig {
153                vhost_device: "/some/path".into(),
154                cid: 56,
155                max_queue_sizes: None,
156            }
157        );
158
159        // CID passed without key
160        assert_eq!(
161            from_vsock_arg("56,device=/some/path").unwrap(),
162            VsockConfig {
163                vhost_device: "/some/path".into(),
164                cid: 56,
165                max_queue_sizes: None,
166            }
167        );
168
169        // Missing cid
170        assert_eq!(
171            from_vsock_arg("device=42").unwrap_err(),
172            ParseError {
173                kind: ErrorKind::SerdeError("missing field `cid`".into()),
174                pos: 0,
175            }
176        );
177
178        // Device passed twice
179        assert_eq!(
180            from_vsock_arg("cid=56,device=42,device=/some/path").unwrap_err(),
181            ParseError {
182                kind: ErrorKind::SerdeError("duplicate field `device`".into()),
183                pos: 0,
184            }
185        );
186
187        // Queue sizes
188        assert_eq!(
189            from_vsock_arg("cid=56,max-queue-sizes=[1,2,4]").unwrap(),
190            VsockConfig {
191                vhost_device: VHOST_VSOCK_DEFAULT_PATH.into(),
192                cid: 56,
193                max_queue_sizes: Some([1, 2, 4]),
194            }
195        );
196    }
197}