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