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