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