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