• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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