vhost/
net.rs

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
5use std::fs::File;
6use std::fs::OpenOptions;
7use std::marker::PhantomData;
8use std::os::unix::fs::OpenOptionsExt;
9use std::path::Path;
10
11use base::ioctl_with_ref;
12use base::AsRawDescriptor;
13use base::RawDescriptor;
14use net_util::TapT;
15
16use super::ioctl_result;
17use super::Error;
18use super::Result;
19use 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.
25pub 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
32pub trait NetT<T: TapT>: Vhost + AsRawDescriptor + Send + Sized {
33    /// Create a new NetT instance
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.
42    fn set_backend(&self, queue_index: usize, descriptor: Option<&T>) -> Result<()>;
43}
44
45impl<T> NetT<T> for Net<T>
46where
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.
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
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
88impl<T> Vhost for Net<T> {}
89
90impl<T> AsRawDescriptor for Net<T> {
91    fn as_raw_descriptor(&self) -> RawDescriptor {
92        self.descriptor.as_raw_descriptor()
93    }
94}
95
96pub 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> {
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    {
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
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> {
139        fn as_raw_descriptor(&self) -> RawDescriptor {
140            self.descriptor.as_raw_descriptor()
141        }
142    }
143}