1 // Copyright 2017 The Chromium OS Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 use net_util::TapT; 6 use std::marker::PhantomData; 7 use std::os::unix::fs::OpenOptionsExt; 8 use std::{ 9 fs::{File, OpenOptions}, 10 path::PathBuf, 11 }; 12 13 use base::{ioctl_with_ref, AsRawDescriptor, RawDescriptor}; 14 use vm_memory::GuestMemory; 15 16 use super::{ioctl_result, Error, Result, Vhost}; 17 18 /// Handle to run VHOST_NET ioctls. 19 /// 20 /// This provides a simple wrapper around a VHOST_NET file descriptor and 21 /// methods that safely run ioctls on that file descriptor. 22 pub struct Net<T> { 23 // descriptor must be dropped first, which will stop and tear down the 24 // vhost-net worker before GuestMemory can potentially be unmapped. 25 descriptor: File, 26 mem: GuestMemory, 27 phantom: PhantomData<T>, 28 } 29 30 pub trait NetT<T: TapT>: Vhost + AsRawDescriptor + Send + Sized { 31 /// Create a new NetT instance new(vhost_net_device_path: &PathBuf, mem: &GuestMemory) -> Result<Self>32 fn new(vhost_net_device_path: &PathBuf, mem: &GuestMemory) -> Result<Self>; 33 34 /// Set the tap file descriptor that will serve as the VHOST_NET backend. 35 /// This will start the vhost worker for the given queue. 36 /// 37 /// # Arguments 38 /// * `queue_index` - Index of the queue to modify. 39 /// * `descriptor` - Tap interface that will be used as the backend. set_backend(&self, queue_index: usize, descriptor: Option<&T>) -> Result<()>40 fn set_backend(&self, queue_index: usize, descriptor: Option<&T>) -> Result<()>; 41 } 42 43 impl<T> NetT<T> for Net<T> 44 where 45 T: TapT, 46 { 47 /// Opens /dev/vhost-net and holds a file descriptor open for it. 48 /// 49 /// # Arguments 50 /// * `mem` - Guest memory mapping. new(vhost_net_device_path: &PathBuf, mem: &GuestMemory) -> Result<Net<T>>51 fn new(vhost_net_device_path: &PathBuf, mem: &GuestMemory) -> Result<Net<T>> { 52 Ok(Net::<T> { 53 descriptor: OpenOptions::new() 54 .read(true) 55 .write(true) 56 .custom_flags(libc::O_CLOEXEC | libc::O_NONBLOCK) 57 .open(vhost_net_device_path) 58 .map_err(Error::VhostOpen)?, 59 mem: mem.clone(), 60 phantom: PhantomData, 61 }) 62 } 63 set_backend(&self, queue_index: usize, event: Option<&T>) -> Result<()>64 fn set_backend(&self, queue_index: usize, event: Option<&T>) -> Result<()> { 65 let vring_file = virtio_sys::vhost_vring_file { 66 index: queue_index as u32, 67 event: event.map_or(-1, |event| event.as_raw_descriptor()), 68 }; 69 70 // This ioctl is called on a valid vhost_net descriptor and has its 71 // return value checked. 72 let ret = unsafe { 73 ioctl_with_ref( 74 &self.descriptor, 75 virtio_sys::VHOST_NET_SET_BACKEND(), 76 &vring_file, 77 ) 78 }; 79 if ret < 0 { 80 return ioctl_result(); 81 } 82 Ok(()) 83 } 84 } 85 86 impl<T> Vhost for Net<T> { mem(&self) -> &GuestMemory87 fn mem(&self) -> &GuestMemory { 88 &self.mem 89 } 90 } 91 92 impl<T> AsRawDescriptor for Net<T> { as_raw_descriptor(&self) -> RawDescriptor93 fn as_raw_descriptor(&self) -> RawDescriptor { 94 self.descriptor.as_raw_descriptor() 95 } 96 } 97 98 pub mod fakes { 99 use super::*; 100 use std::fs::remove_file; 101 use std::fs::OpenOptions; 102 103 const TMP_FILE: &str = "/tmp/crosvm_vhost_test_file"; 104 105 pub struct FakeNet<T> { 106 descriptor: File, 107 mem: GuestMemory, 108 phantom: PhantomData<T>, 109 } 110 111 impl<T> Drop for FakeNet<T> { drop(&mut self)112 fn drop(&mut self) { 113 let _ = remove_file(TMP_FILE); 114 } 115 } 116 117 impl<T> NetT<T> for FakeNet<T> 118 where 119 T: TapT, 120 { new(_vhost_net_device_path: &PathBuf, mem: &GuestMemory) -> Result<FakeNet<T>>121 fn new(_vhost_net_device_path: &PathBuf, mem: &GuestMemory) -> Result<FakeNet<T>> { 122 Ok(FakeNet::<T> { 123 descriptor: OpenOptions::new() 124 .read(true) 125 .append(true) 126 .create(true) 127 .open(TMP_FILE) 128 .unwrap(), 129 mem: mem.clone(), 130 phantom: PhantomData, 131 }) 132 } 133 set_backend(&self, _queue_index: usize, _fd: Option<&T>) -> Result<()>134 fn set_backend(&self, _queue_index: usize, _fd: Option<&T>) -> Result<()> { 135 Ok(()) 136 } 137 } 138 139 impl<T> Vhost for FakeNet<T> { mem(&self) -> &GuestMemory140 fn mem(&self) -> &GuestMemory { 141 &self.mem 142 } 143 } 144 145 impl<T> AsRawDescriptor for FakeNet<T> { as_raw_descriptor(&self) -> RawDescriptor146 fn as_raw_descriptor(&self) -> RawDescriptor { 147 self.descriptor.as_raw_descriptor() 148 } 149 } 150 } 151