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