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