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