device_virtio_net/
vhost.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::collections::BTreeMap;
6use std::mem;
7use std::path::Path;
8
9use anyhow::anyhow;
10use anyhow::Context;
11use base::error;
12use base::warn;
13use base::AsRawDescriptor;
14use base::RawDescriptor;
15use base::Tube;
16use base::WorkerThread;
17use devices::virtio::copy_config;
18use devices::virtio::vhost::worker::Worker;
19use devices::virtio::vhost::Error;
20use devices::virtio::vhost::Result;
21use devices::virtio::vhost::VhostDevRequest;
22use devices::virtio::vhost::VhostDevResponse;
23use devices::virtio::DeviceType;
24use devices::virtio::Interrupt;
25use devices::virtio::Queue;
26use devices::virtio::VirtioDevice;
27use devices::MsixStatus;
28use devices::PciAddress;
29use net_util::MacAddress;
30use net_util::TapT;
31use vhost::NetT as VhostNetT;
32use virtio_sys::virtio_config::VIRTIO_F_RING_PACKED;
33use virtio_sys::virtio_net;
34use vm_memory::GuestMemory;
35use zerocopy::IntoBytes;
36
37use crate::build_config;
38
39const QUEUE_SIZE: u16 = 256;
40const NUM_QUEUES: usize = 2;
41const QUEUE_SIZES: &[u16] = &[QUEUE_SIZE; NUM_QUEUES];
42
43pub struct Net<T: TapT + 'static, U: VhostNetT<T> + 'static> {
44    worker_thread: Option<WorkerThread<(Worker<U>, T)>>,
45    tap: Option<T>,
46    guest_mac: Option<[u8; 6]>,
47    vhost_net_handle: Option<U>,
48    avail_features: u64,
49    acked_features: u64,
50    worker_client_tube: Tube,
51    worker_server_tube: Option<Tube>,
52    pci_address: Option<PciAddress>,
53}
54
55impl<T, U> Net<T, U>
56where
57    T: TapT,
58    U: VhostNetT<T>,
59{
60    /// Creates a new virtio network device from a tap device that has already been
61    /// configured.
62    pub fn new(
63        vhost_net_device_path: &Path,
64        base_features: u64,
65        tap: T,
66        mac_addr: Option<MacAddress>,
67        use_packed_queue: bool,
68        pci_address: Option<PciAddress>,
69        mrg_rxbuf: bool,
70    ) -> Result<Net<T, U>> {
71        // Set offload flags to match the virtio features below.
72        tap.set_offload(
73            net_sys::TUN_F_CSUM | net_sys::TUN_F_UFO | net_sys::TUN_F_TSO4 | net_sys::TUN_F_TSO6,
74        )
75        .map_err(Error::TapSetOffload)?;
76
77        // We declare VIRTIO_NET_F_MRG_RXBUF, so set the vnet hdr size to match.
78        let vnet_hdr_size = mem::size_of::<virtio_net::virtio_net_hdr_mrg_rxbuf>();
79        tap.set_vnet_hdr_size(vnet_hdr_size)
80            .map_err(Error::TapSetVnetHdrSize)?;
81
82        let vhost_net_handle = U::new(vhost_net_device_path).map_err(Error::VhostOpen)?;
83
84        let mut avail_features = base_features
85            | 1 << virtio_net::VIRTIO_NET_F_GUEST_CSUM
86            | 1 << virtio_net::VIRTIO_NET_F_CSUM
87            | 1 << virtio_net::VIRTIO_NET_F_GUEST_TSO4
88            | 1 << virtio_net::VIRTIO_NET_F_GUEST_UFO
89            | 1 << virtio_net::VIRTIO_NET_F_HOST_TSO4
90            | 1 << virtio_net::VIRTIO_NET_F_HOST_UFO
91            | 1 << virtio_net::VIRTIO_NET_F_MRG_RXBUF;
92
93        if use_packed_queue {
94            avail_features |= 1 << VIRTIO_F_RING_PACKED;
95        }
96
97        if mac_addr.is_some() {
98            avail_features |= 1 << virtio_net::VIRTIO_NET_F_MAC;
99        }
100
101        if mrg_rxbuf {
102            avail_features |= 1 << virtio_net::VIRTIO_NET_F_MRG_RXBUF;
103        }
104
105        let (worker_client_tube, worker_server_tube) = Tube::pair().map_err(Error::CreateTube)?;
106
107        Ok(Net {
108            worker_thread: None,
109            tap: Some(tap),
110            guest_mac: mac_addr.map(|mac| mac.octets()),
111            vhost_net_handle: Some(vhost_net_handle),
112            avail_features,
113            acked_features: 0u64,
114            worker_client_tube,
115            worker_server_tube: Some(worker_server_tube),
116            pci_address,
117        })
118    }
119}
120
121impl<T, U> VirtioDevice for Net<T, U>
122where
123    T: TapT + 'static,
124    U: VhostNetT<T> + 'static,
125{
126    fn keep_rds(&self) -> Vec<RawDescriptor> {
127        let mut keep_rds = Vec::new();
128
129        if let Some(tap) = &self.tap {
130            keep_rds.push(tap.as_raw_descriptor());
131        }
132
133        if let Some(vhost_net_handle) = &self.vhost_net_handle {
134            keep_rds.push(vhost_net_handle.as_raw_descriptor());
135        }
136
137        keep_rds.push(self.worker_client_tube.as_raw_descriptor());
138
139        if let Some(worker_server_tube) = &self.worker_server_tube {
140            keep_rds.push(worker_server_tube.as_raw_descriptor());
141        }
142
143        keep_rds
144    }
145
146    fn device_type(&self) -> DeviceType {
147        DeviceType::Net
148    }
149
150    fn queue_max_sizes(&self) -> &[u16] {
151        QUEUE_SIZES
152    }
153
154    fn features(&self) -> u64 {
155        self.avail_features
156    }
157
158    fn ack_features(&mut self, value: u64) {
159        let mut v = value;
160
161        // Check if the guest is ACK'ing a feature that we didn't claim to have.
162        let unrequested_features = v & !self.avail_features;
163        if unrequested_features != 0 {
164            warn!("net: virtio net got unknown feature ack: {:x}", v);
165
166            // Don't count these features as acked.
167            v &= !unrequested_features;
168        }
169        self.acked_features |= v;
170    }
171
172    fn read_config(&self, offset: u64, data: &mut [u8]) {
173        let vq_pairs = QUEUE_SIZES.len() / 2;
174        // VIRTIO_NET_F_MTU is not set.
175        let config_space = build_config(vq_pairs as u16, /* mtu= */ 0, self.guest_mac);
176        copy_config(data, 0, config_space.as_bytes(), offset);
177    }
178
179    fn activate(
180        &mut self,
181        mem: GuestMemory,
182        interrupt: Interrupt,
183        queues: BTreeMap<usize, Queue>,
184    ) -> anyhow::Result<()> {
185        if queues.len() != NUM_QUEUES {
186            return Err(anyhow!(
187                "net: expected {} queues, got {}",
188                NUM_QUEUES,
189                queues.len()
190            ));
191        }
192
193        let vhost_net_handle = self
194            .vhost_net_handle
195            .take()
196            .context("missing vhost_net_handle")?;
197        let tap = self.tap.take().context("missing tap")?;
198        let acked_features = self.acked_features;
199        let mut worker = Worker::new(
200            "vhost-net",
201            queues,
202            vhost_net_handle,
203            interrupt,
204            acked_features,
205            self.worker_server_tube
206                .take()
207                .expect("worker control tube missing"),
208            mem,
209            None,
210        )
211        .context("net worker init exited with error")?;
212        for idx in 0..NUM_QUEUES {
213            worker
214                .vhost_handle
215                .set_backend(idx, Some(&tap))
216                .map_err(Error::VhostNetSetBackend)?;
217        }
218        self.worker_thread = Some(WorkerThread::start("vhost_net", move |kill_evt| {
219            let result = worker.run(kill_evt);
220            if let Err(e) = result {
221                error!("net worker thread exited with error: {}", e);
222            }
223            for idx in 0..NUM_QUEUES {
224                if let Err(e) = worker.vhost_handle.set_backend(idx, None) {
225                    error!("net worker thread failed to clear backend: {:#}", e);
226                }
227            }
228            (worker, tap)
229        }));
230
231        Ok(())
232    }
233
234    fn pci_address(&self) -> Option<PciAddress> {
235        self.pci_address
236    }
237
238    fn on_device_sandboxed(&mut self) {
239        // ignore the error but to log the error. We don't need to do
240        // anything here because when activate, the other vhost set up
241        // will be failed to stop the activate thread.
242        if let Some(vhost_net_handle) = &self.vhost_net_handle {
243            match vhost_net_handle.set_owner() {
244                Ok(_) => {}
245                Err(e) => error!("{}: failed to set owner: {:?}", self.debug_label(), e),
246            }
247        }
248    }
249
250    fn control_notify(&self, behavior: MsixStatus) {
251        if self.worker_thread.is_none() {
252            return;
253        }
254        match behavior {
255            MsixStatus::EntryChanged(index) => {
256                if let Err(e) = self
257                    .worker_client_tube
258                    .send(&VhostDevRequest::MsixEntryChanged(index))
259                {
260                    error!(
261                        "{} failed to send VhostMsixEntryChanged request for entry {}: {:?}",
262                        self.debug_label(),
263                        index,
264                        e
265                    );
266                    return;
267                }
268                if let Err(e) = self.worker_client_tube.recv::<VhostDevResponse>() {
269                    error!(
270                        "{} failed to receive VhostMsixEntryChanged response for entry {}: {:?}",
271                        self.debug_label(),
272                        index,
273                        e
274                    );
275                }
276            }
277            MsixStatus::Changed => {
278                if let Err(e) = self.worker_client_tube.send(&VhostDevRequest::MsixChanged) {
279                    error!(
280                        "{} failed to send VhostMsixChanged request: {:?}",
281                        self.debug_label(),
282                        e
283                    );
284                    return;
285                }
286                if let Err(e) = self.worker_client_tube.recv::<VhostDevResponse>() {
287                    error!(
288                        "{} failed to receive VhostMsixChanged response {:?}",
289                        self.debug_label(),
290                        e
291                    );
292                }
293            }
294            _ => {}
295        }
296    }
297
298    fn reset(&mut self) -> anyhow::Result<()> {
299        if let Some(worker_thread) = self.worker_thread.take() {
300            let (worker, tap) = worker_thread.stop();
301            self.vhost_net_handle = Some(worker.vhost_handle);
302            self.tap = Some(tap);
303            self.worker_server_tube = Some(worker.server_tube);
304        }
305        Ok(())
306    }
307}
308
309#[cfg(test)]
310pub mod tests {
311    use std::net::Ipv4Addr;
312    use std::path::PathBuf;
313    use std::result;
314
315    use base::pagesize;
316    use base::Event;
317    use devices::virtio::base_features;
318    use devices::virtio::QueueConfig;
319    use hypervisor::ProtectionType;
320    use net_util::sys::linux::fakes::FakeTap;
321    use net_util::TapTCommon;
322    use vhost::net::fakes::FakeNet;
323    use vm_memory::GuestAddress;
324    use vm_memory::GuestMemory;
325    use vm_memory::GuestMemoryError;
326
327    use super::*;
328
329    fn create_guest_memory() -> result::Result<GuestMemory, GuestMemoryError> {
330        let start_addr1 = GuestAddress(0x0);
331        let start_addr2 = GuestAddress(pagesize() as u64);
332        GuestMemory::new(&[
333            (start_addr1, pagesize() as u64),
334            (start_addr2, 4 * pagesize() as u64),
335        ])
336    }
337
338    fn create_net_common() -> Net<FakeTap, FakeNet<FakeTap>> {
339        let tap = FakeTap::new(true, false).unwrap();
340        tap.set_ip_addr(Ipv4Addr::new(127, 0, 0, 1))
341            .map_err(Error::TapSetIp)
342            .unwrap();
343        tap.set_netmask(Ipv4Addr::new(255, 255, 255, 0))
344            .map_err(Error::TapSetNetmask)
345            .unwrap();
346        let mac = "de:21:e8:47:6b:6a".parse().unwrap();
347        tap.set_mac_address(mac).unwrap();
348        tap.enable().unwrap();
349
350        let features = base_features(ProtectionType::Unprotected);
351        Net::<FakeTap, FakeNet<FakeTap>>::new(
352            &PathBuf::from(""),
353            features,
354            tap,
355            Some(mac),
356            false,
357            None,
358            false,
359        )
360        .unwrap()
361    }
362
363    #[test]
364    fn create_net() {
365        create_net_common();
366    }
367
368    #[test]
369    fn keep_rds() {
370        let net = create_net_common();
371        let fds = net.keep_rds();
372        assert!(
373            !fds.is_empty(),
374            "We should have gotten at least one descriptor"
375        );
376    }
377
378    #[test]
379    fn features() {
380        let net = create_net_common();
381        // Feature bits 0-23 and 50-127 are specific for the device type, but
382        // at the moment crosvm only supports 64 bits of feature bits.
383        const DEVICE_FEATURE_BITS: u64 = 0xffffff;
384        let expected_features = 1 << 0 // VIRTIO_NET_F_CSUM
385            | 1 << 1 // VIRTIO_NET_F_GUEST_CSUM
386            | 1 << 5 // VIRTIO_NET_F_MAC
387            | 1 << 7 // VIRTIO_NET_F_GUEST_TSO4
388            | 1 << 10 // VIRTIO_NET_F_GUEST_UFO
389            | 1 << 11 // VIRTIO_NET_F_HOST_TSO4
390            | 1 << 14 // VIRTIO_NET_F_HOST_UFO
391            | 1 << 15; // VIRTIO_NET_F_MRG_RXBUF
392        assert_eq!(net.features() & DEVICE_FEATURE_BITS, expected_features);
393    }
394
395    #[test]
396    fn ack_features() {
397        let mut net = create_net_common();
398        // Just testing that we don't panic, for now
399        net.ack_features(1);
400        net.ack_features(1 << 32);
401    }
402
403    #[test]
404    fn activate() {
405        let mut net = create_net_common();
406        let guest_memory = create_guest_memory().unwrap();
407        let interrupt = Interrupt::new_for_test();
408
409        let mut q0 = QueueConfig::new(1, 0);
410        q0.set_ready(true);
411        let q0 = q0
412            .activate(&guest_memory, Event::new().unwrap(), interrupt.clone())
413            .expect("QueueConfig::activate");
414
415        let mut q1 = QueueConfig::new(1, 0);
416        q1.set_ready(true);
417        let q1 = q1
418            .activate(&guest_memory, Event::new().unwrap(), interrupt.clone())
419            .expect("QueueConfig::activate");
420
421        // Just testing that we don't panic, for now
422        let _ = net.activate(guest_memory, interrupt, BTreeMap::from([(0, q0), (1, q1)]));
423    }
424}