devices/virtio/vsock/sys/
linux.rs1use 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")]
24pub struct VsockConfig {
26 pub cid: u64,
28 #[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 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 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 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 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 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 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 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 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 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 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 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}