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