summary refs log tree commit diff
path: root/vhost/src/net.rs
blob: 9b59cf27046d026784f29155894e694c4c1c4de1 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
// 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 net_util::TapT;
use std::fs::{File, OpenOptions};
use std::marker::PhantomData;
use std::os::unix::fs::OpenOptionsExt;
use std::os::unix::io::{AsRawFd, RawFd};

use sys_util::{ioctl_with_ref, GuestMemory};

use super::{ioctl_result, Error, Result, Vhost};

static DEVICE: &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.
pub struct Net<T> {
    // 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,
    phantom: PhantomData<T>,
}

pub trait NetT<T: TapT>: Vhost + AsRawFd + Send + Sized {
    /// Create a new NetT instance
    fn new(mem: &GuestMemory) -> Result<Self>;

    /// 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.
    fn set_backend(&self, queue_index: usize, fd: Option<&T>) -> Result<()>;
}

impl<T> NetT<T> for Net<T>
where
    T: TapT,
{
    /// Opens /dev/vhost-net and holds a file descriptor open for it.
    ///
    /// # Arguments
    /// * `mem` - Guest memory mapping.
    fn new(mem: &GuestMemory) -> Result<Net<T>> {
        Ok(Net::<T> {
            fd: OpenOptions::new()
                .read(true)
                .write(true)
                .custom_flags(libc::O_CLOEXEC | libc::O_NONBLOCK)
                .open(DEVICE)
                .map_err(Error::VhostOpen)?,
            mem: mem.clone(),
            phantom: PhantomData,
        })
    }

    fn set_backend(&self, queue_index: usize, fd: Option<&T>) -> Result<()> {
        let vring_file = virtio_sys::vhost_vring_file {
            index: queue_index as u32,
            fd: fd.map_or(-1, |fd| fd.as_raw_fd()),
        };

        // This ioctl is called on a valid vhost_net fd and has its
        // return value checked.
        let ret =
            unsafe { ioctl_with_ref(&self.fd, virtio_sys::VHOST_NET_SET_BACKEND(), &vring_file) };
        if ret < 0 {
            return ioctl_result();
        }
        Ok(())
    }
}

impl<T> Vhost for Net<T> {
    fn mem(&self) -> &GuestMemory {
        &self.mem
    }
}

impl<T> AsRawFd for Net<T> {
    fn as_raw_fd(&self) -> RawFd {
        self.fd.as_raw_fd()
    }
}

pub mod fakes {
    use super::*;
    use std::fs::remove_file;
    use std::fs::OpenOptions;

    const TMP_FILE: &str = "/tmp/crosvm_vhost_test_file";

    pub struct FakeNet<T> {
        fd: File,
        mem: GuestMemory,
        phantom: PhantomData<T>,
    }

    impl<T> Drop for FakeNet<T> {
        fn drop(&mut self) {
            let _ = remove_file(TMP_FILE);
        }
    }

    impl<T> NetT<T> for FakeNet<T>
    where
        T: TapT,
    {
        fn new(mem: &GuestMemory) -> Result<FakeNet<T>> {
            Ok(FakeNet::<T> {
                fd: OpenOptions::new()
                    .read(true)
                    .append(true)
                    .create(true)
                    .open(TMP_FILE)
                    .unwrap(),
                mem: mem.clone(),
                phantom: PhantomData,
            })
        }

        fn set_backend(&self, _queue_index: usize, _fd: Option<&T>) -> Result<()> {
            Ok(())
        }
    }

    impl<T> Vhost for FakeNet<T> {
        fn mem(&self) -> &GuestMemory {
            &self.mem
        }
    }

    impl<T> AsRawFd for FakeNet<T> {
        fn as_raw_fd(&self) -> RawFd {
            self.fd.as_raw_fd()
        }
    }
}