2017-08-23 22:02:37 +00:00
|
|
|
// Copyright 2017 The Chromium OS Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
|
|
// found in the LICENSE file.
|
|
|
|
|
|
|
|
use libc;
|
2017-09-28 04:04:03 +00:00
|
|
|
use net_util::TapT;
|
2017-08-28 16:51:18 +00:00
|
|
|
use std::fs::{File, OpenOptions};
|
2017-09-28 04:04:03 +00:00
|
|
|
use std::marker::PhantomData;
|
2017-08-28 16:51:18 +00:00
|
|
|
use std::os::unix::fs::OpenOptionsExt;
|
|
|
|
use std::os::unix::io::{AsRawFd, RawFd};
|
2017-08-23 22:02:37 +00:00
|
|
|
use virtio_sys;
|
|
|
|
|
2017-09-08 02:51:07 +00:00
|
|
|
use sys_util::{ioctl_with_ref, GuestMemory};
|
2017-08-23 22:02:37 +00:00
|
|
|
|
|
|
|
use super::{ioctl_result, Error, Result, Vhost};
|
|
|
|
|
|
|
|
static DEVICE: &'static str = "/dev/vhost-net";
|
|
|
|
|
|
|
|
/// 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.
|
2017-09-28 04:04:03 +00:00
|
|
|
pub struct Net<T> {
|
2017-08-23 22:02:37 +00:00
|
|
|
// fd must be dropped first, which will stop and tear down the
|
|
|
|
// vhost-net worker before GuestMemory can potentially be unmapped.
|
|
|
|
fd: File,
|
|
|
|
mem: GuestMemory,
|
2017-09-28 04:04:03 +00:00
|
|
|
phantom: PhantomData<T>,
|
2017-08-23 22:02:37 +00:00
|
|
|
}
|
|
|
|
|
2017-09-28 04:04:03 +00:00
|
|
|
pub trait NetT<T: TapT>: Vhost + AsRawFd + Send + Sized {
|
|
|
|
/// Create a new NetT instance
|
|
|
|
fn new(mem: &GuestMemory) -> Result<Self>;
|
|
|
|
|
2017-09-08 02:51:07 +00:00
|
|
|
/// 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.
|
|
|
|
/// * `fd` - Tap interface that will be used as the backend.
|
2017-09-28 04:04:03 +00:00
|
|
|
fn set_backend(&self, queue_index: usize, fd: &T) -> Result<()>;
|
2017-09-08 02:51:07 +00:00
|
|
|
}
|
|
|
|
|
2017-09-28 04:04:03 +00:00
|
|
|
impl<T> NetT<T> for Net<T>
|
|
|
|
where
|
|
|
|
T: TapT,
|
|
|
|
{
|
2017-08-23 22:02:37 +00:00
|
|
|
/// Opens /dev/vhost-net and holds a file descriptor open for it.
|
|
|
|
///
|
|
|
|
/// # Arguments
|
|
|
|
/// * `mem` - Guest memory mapping.
|
2017-09-28 04:04:03 +00:00
|
|
|
fn new(mem: &GuestMemory) -> Result<Net<T>> {
|
|
|
|
Ok(Net::<T> {
|
2017-08-28 16:51:18 +00:00
|
|
|
fd: OpenOptions::new()
|
|
|
|
.read(true)
|
|
|
|
.write(true)
|
|
|
|
.custom_flags(libc::O_CLOEXEC | libc::O_NONBLOCK)
|
|
|
|
.open(DEVICE)
|
|
|
|
.map_err(Error::VhostOpen)?,
|
2017-08-23 22:02:37 +00:00
|
|
|
mem: mem.clone(),
|
2017-09-28 04:04:03 +00:00
|
|
|
phantom: PhantomData,
|
2017-08-23 22:02:37 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2017-09-28 04:04:03 +00:00
|
|
|
fn set_backend(&self, queue_index: usize, fd: &T) -> Result<()> {
|
2017-08-23 22:02:37 +00:00
|
|
|
let vring_file = virtio_sys::vhost_vring_file {
|
|
|
|
index: queue_index as u32,
|
|
|
|
fd: fd.as_raw_fd(),
|
|
|
|
};
|
|
|
|
|
|
|
|
// This ioctl is called on a valid vhost_net fd and has its
|
|
|
|
// return value checked.
|
2017-09-08 02:51:07 +00:00
|
|
|
let ret =
|
|
|
|
unsafe { ioctl_with_ref(&self.fd, virtio_sys::VHOST_NET_SET_BACKEND(), &vring_file) };
|
2017-08-23 22:02:37 +00:00
|
|
|
if ret < 0 {
|
|
|
|
return ioctl_result();
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-28 04:04:03 +00:00
|
|
|
impl<T> Vhost for Net<T> {
|
2017-08-23 22:02:37 +00:00
|
|
|
fn mem(&self) -> &GuestMemory {
|
|
|
|
&self.mem
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-28 04:04:03 +00:00
|
|
|
impl<T> AsRawFd for Net<T> {
|
2017-08-23 22:02:37 +00:00
|
|
|
fn as_raw_fd(&self) -> RawFd {
|
|
|
|
self.fd.as_raw_fd()
|
|
|
|
}
|
|
|
|
}
|
2017-09-08 02:51:07 +00:00
|
|
|
|
2017-09-28 04:04:03 +00:00
|
|
|
pub mod fakes {
|
2017-09-08 02:51:07 +00:00
|
|
|
use super::*;
|
|
|
|
use std::fs::remove_file;
|
2018-10-03 17:22:32 +00:00
|
|
|
use std::fs::OpenOptions;
|
2017-09-08 02:51:07 +00:00
|
|
|
|
|
|
|
const TMP_FILE: &str = "/tmp/crosvm_vhost_test_file";
|
|
|
|
|
2017-09-28 04:04:03 +00:00
|
|
|
pub struct FakeNet<T> {
|
2017-09-08 02:51:07 +00:00
|
|
|
fd: File,
|
|
|
|
mem: GuestMemory,
|
2017-09-28 04:04:03 +00:00
|
|
|
phantom: PhantomData<T>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T> Drop for FakeNet<T> {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
let _ = remove_file(TMP_FILE);
|
|
|
|
}
|
2017-09-08 02:51:07 +00:00
|
|
|
}
|
|
|
|
|
2017-09-28 04:04:03 +00:00
|
|
|
impl<T> NetT<T> for FakeNet<T>
|
|
|
|
where
|
|
|
|
T: TapT,
|
|
|
|
{
|
|
|
|
fn new(mem: &GuestMemory) -> Result<FakeNet<T>> {
|
|
|
|
Ok(FakeNet::<T> {
|
2017-09-08 02:51:07 +00:00
|
|
|
fd: OpenOptions::new()
|
|
|
|
.read(true)
|
|
|
|
.append(true)
|
|
|
|
.create(true)
|
|
|
|
.open(TMP_FILE)
|
|
|
|
.unwrap(),
|
2017-09-28 04:04:03 +00:00
|
|
|
mem: mem.clone(),
|
|
|
|
phantom: PhantomData,
|
2017-09-08 02:51:07 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2017-09-28 04:04:03 +00:00
|
|
|
fn set_backend(&self, _queue_index: usize, _fd: &T) -> Result<()> {
|
2017-09-08 02:51:07 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-28 04:04:03 +00:00
|
|
|
impl<T> Vhost for FakeNet<T> {
|
2017-09-08 02:51:07 +00:00
|
|
|
fn mem(&self) -> &GuestMemory {
|
|
|
|
&self.mem
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-28 04:04:03 +00:00
|
|
|
impl<T> AsRawFd for FakeNet<T> {
|
2017-09-08 02:51:07 +00:00
|
|
|
fn as_raw_fd(&self) -> RawFd {
|
|
|
|
self.fd.as_raw_fd()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|