1 // Copyright (C) 2021 Alibaba Cloud Computing. All rights reserved.
2 // SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause
3 
4 //! Kernel-based vhost-net backend
5 
6 use std::fs::{File, OpenOptions};
7 use std::os::unix::fs::OpenOptionsExt;
8 use std::os::unix::io::{AsRawFd, RawFd};
9 
10 use vm_memory::GuestAddressSpace;
11 use vmm_sys_util::ioctl::ioctl_with_ref;
12 
13 use super::vhost_binding::*;
14 use super::{ioctl_result, Error, Result, VhostKernBackend};
15 
16 use crate::net::*;
17 
18 const VHOST_NET_PATH: &str = "/dev/vhost-net";
19 
20 /// Handle for running VHOST_NET ioctls
21 pub struct Net<AS: GuestAddressSpace> {
22     fd: File,
23     mem: AS,
24 }
25 
26 impl<AS: GuestAddressSpace> Net<AS> {
27     /// Open a handle to a new VHOST-NET instance.
new(mem: AS) -> Result<Self>28     pub fn new(mem: AS) -> Result<Self> {
29         Ok(Net {
30             fd: OpenOptions::new()
31                 .read(true)
32                 .write(true)
33                 .custom_flags(libc::O_CLOEXEC | libc::O_NONBLOCK)
34                 .open(VHOST_NET_PATH)
35                 .map_err(Error::VhostOpen)?,
36             mem,
37         })
38     }
39 }
40 
41 impl<AS: GuestAddressSpace> VhostNet for Net<AS> {
set_backend(&self, queue_index: usize, fd: Option<&File>) -> Result<()>42     fn set_backend(&self, queue_index: usize, fd: Option<&File>) -> Result<()> {
43         let vring_file = vhost_vring_file {
44             index: queue_index as u32,
45             fd: fd.map_or(-1, |v| v.as_raw_fd()),
46         };
47 
48         // SAFETY: Safe because the vhost-net fd is valid and we check the return value
49         let ret = unsafe { ioctl_with_ref(self, VHOST_NET_SET_BACKEND(), &vring_file) };
50         ioctl_result(ret, ())
51     }
52 }
53 
54 impl<AS: GuestAddressSpace> VhostKernBackend for Net<AS> {
55     type AS = AS;
56 
mem(&self) -> &Self::AS57     fn mem(&self) -> &Self::AS {
58         &self.mem
59     }
60 }
61 
62 impl<AS: GuestAddressSpace> AsRawFd for Net<AS> {
as_raw_fd(&self) -> RawFd63     fn as_raw_fd(&self) -> RawFd {
64         self.fd.as_raw_fd()
65     }
66 }
67 
68 #[cfg(test)]
69 mod tests {
70     use vm_memory::{GuestAddress, GuestMemory, GuestMemoryMmap};
71     use vmm_sys_util::eventfd::EventFd;
72 
73     use super::*;
74     use crate::{
75         VhostBackend, VhostUserDirtyLogRegion, VhostUserMemoryRegionInfo, VringConfigData,
76     };
77     use serial_test::serial;
78 
79     #[test]
80     #[serial]
test_net_new_device()81     fn test_net_new_device() {
82         let m = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10_0000)]).unwrap();
83         let net = Net::new(&m).unwrap();
84 
85         assert!(net.as_raw_fd() >= 0);
86         assert!(net.mem().find_region(GuestAddress(0x100)).is_some());
87         assert!(net.mem().find_region(GuestAddress(0x10_0000)).is_none());
88     }
89 
90     #[test]
91     #[serial]
test_net_is_valid()92     fn test_net_is_valid() {
93         let m = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10_0000)]).unwrap();
94         let net = Net::new(&m).unwrap();
95 
96         let mut config = VringConfigData {
97             queue_max_size: 32,
98             queue_size: 32,
99             flags: 0,
100             desc_table_addr: 0x1000,
101             used_ring_addr: 0x2000,
102             avail_ring_addr: 0x3000,
103             log_addr: None,
104         };
105         assert!(net.is_valid(&config));
106 
107         config.queue_size = 0;
108         assert!(!net.is_valid(&config));
109         config.queue_size = 31;
110         assert!(!net.is_valid(&config));
111         config.queue_size = 33;
112         assert!(!net.is_valid(&config));
113     }
114 
115     #[test]
116     #[serial]
test_net_ioctls()117     fn test_net_ioctls() {
118         let m = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10_0000)]).unwrap();
119         let net = Net::new(&m).unwrap();
120         let backend = OpenOptions::new()
121             .read(true)
122             .write(true)
123             .open("/dev/null")
124             .unwrap();
125 
126         let features = net.get_features().unwrap();
127         net.set_features(features).unwrap();
128 
129         net.set_owner().unwrap();
130 
131         net.set_mem_table(&[]).unwrap_err();
132 
133         let region = VhostUserMemoryRegionInfo::new(
134             0x0,
135             0x10_0000,
136             m.get_host_address(GuestAddress(0x0)).unwrap() as u64,
137             0,
138             -1,
139         );
140         net.set_mem_table(&[region]).unwrap();
141 
142         net.set_log_base(
143             0x4000,
144             Some(VhostUserDirtyLogRegion {
145                 mmap_size: 0x1000,
146                 mmap_offset: 0x10,
147                 mmap_handle: 1,
148             }),
149         )
150         .unwrap_err();
151         net.set_log_base(0x4000, None).unwrap();
152 
153         let eventfd = EventFd::new(0).unwrap();
154         net.set_log_fd(eventfd.as_raw_fd()).unwrap();
155 
156         net.set_vring_num(0, 32).unwrap();
157 
158         let config = VringConfigData {
159             queue_max_size: 32,
160             queue_size: 32,
161             flags: 0,
162             desc_table_addr: 0x1000,
163             used_ring_addr: 0x2000,
164             avail_ring_addr: 0x3000,
165             log_addr: None,
166         };
167         net.set_vring_addr(0, &config).unwrap();
168         net.set_vring_base(0, 1).unwrap();
169         net.set_vring_call(0, &eventfd).unwrap();
170         net.set_vring_kick(0, &eventfd).unwrap();
171         net.set_vring_err(0, &eventfd).unwrap();
172         assert_eq!(net.get_vring_base(0).unwrap(), 1);
173 
174         net.set_backend(0, Some(&backend)).unwrap_err();
175         net.set_backend(0, None).unwrap();
176     }
177 }
178