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