1#[cfg(feature = "pci-hotplug")]
6pub mod pci_hotplug;
7mod sys;
8#[cfg(any(target_os = "android", target_os = "linux"))]
9pub mod vhost;
10pub mod vhost_user;
11
12use std::collections::BTreeMap;
13use std::fmt;
14use std::io;
15use std::io::Write;
16use std::net::Ipv4Addr;
17use std::os::raw::c_uint;
18#[cfg(any(target_os = "android", target_os = "linux"))]
19use std::path::PathBuf;
20use std::str::FromStr;
21
22use anyhow::anyhow;
23use anyhow::Context;
24use base::error;
25#[cfg(windows)]
26use base::named_pipes::OverlappedWrapper;
27use base::warn;
28use base::Error as SysError;
29use base::Event;
30use base::EventToken;
31use base::RawDescriptor;
32use base::ReadNotifier;
33use base::WaitContext;
34use base::WorkerThread;
35use data_model::Le16;
36use data_model::Le64;
37use devices::virtio::copy_config;
38use devices::virtio::DeviceType;
39use devices::virtio::Interrupt;
40use devices::virtio::Queue;
41use devices::virtio::Reader;
42use devices::virtio::VirtioDevice;
43use devices::PciAddress;
44use devices::VirtioDeviceArgs;
45use devices::VirtioDeviceModule;
46use hypervisor::ProtectionType;
47use net_util::Error as TapError;
48use net_util::MacAddress;
49use net_util::TapT;
50#[cfg(feature = "pci-hotplug")]
51pub use pci_hotplug::NetPciHotplugResourceCarrier;
52use remain::sorted;
53use serde::Deserialize;
54use serde::Serialize;
55use snapshot::AnySnapshot;
56#[cfg(any(target_os = "android", target_os = "linux"))]
57pub use sys::linux::create_tap_for_net_device;
58pub use sys::process_mrg_rx;
59pub use sys::process_rx;
60pub use sys::process_tx;
61pub use sys::validate_and_configure_tap;
62pub use sys::virtio_features_to_tap_offload;
63pub use sys::PendingBuffer;
64use thiserror::Error as ThisError;
65pub use vhost_user::run_net_device;
66#[cfg(windows)]
67#[cfg(feature = "slirp")]
68pub use vhost_user::sys::windows::NetBackendConfig;
69pub use vhost_user::NetBackend;
70pub use vhost_user::Options as NetOptions;
71use virtio_sys::virtio_config::VIRTIO_F_RING_PACKED;
72use virtio_sys::virtio_net;
73use virtio_sys::virtio_net::VIRTIO_NET_CTRL_GUEST_OFFLOADS;
74use virtio_sys::virtio_net::VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET;
75use virtio_sys::virtio_net::VIRTIO_NET_CTRL_MQ;
76use virtio_sys::virtio_net::VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET;
77use virtio_sys::virtio_net::VIRTIO_NET_ERR;
78use virtio_sys::virtio_net::VIRTIO_NET_OK;
79use vm_memory::GuestMemory;
80use zerocopy::FromBytes;
81use zerocopy::Immutable;
82use zerocopy::IntoBytes;
83use zerocopy::KnownLayout;
84
85#[cfg(windows)]
89const MAX_BUFFER_SIZE: usize = 65562;
90const QUEUE_SIZE: u16 = 256;
91
92#[cfg(any(target_os = "android", target_os = "linux"))]
93pub static VHOST_NET_DEFAULT_PATH: &str = "/dev/vhost-net";
94
95#[sorted]
96#[derive(ThisError, Debug)]
97pub enum NetError {
98 #[error("failed to clone kill event: {0}")]
100 CloneKillEvent(SysError),
101 #[error("failed to create kill event: {0}")]
103 CreateKillEvent(SysError),
104 #[error("failed to create wait context: {0}")]
106 CreateWaitContext(SysError),
107 #[error("failed to add tap trigger to event context: {0}")]
109 EventAddTap(SysError),
110 #[error("failed to remove tap trigger from event context: {0}")]
112 EventRemoveTap(SysError),
113 #[error("invalid control command")]
115 InvalidCmd,
116 #[error("failed to read control message data: {0}")]
118 ReadCtrlData(io::Error),
119 #[error("failed to read control message header: {0}")]
121 ReadCtrlHeader(io::Error),
122 #[cfg(any(target_os = "android", target_os = "linux"))]
124 #[error("no rx descriptors available")]
125 RxDescriptorsExhausted,
126 #[cfg(windows)]
128 #[error("error creating Slirp: {0}")]
129 SlirpCreateError(net_util::Error),
130 #[error("failed to enable tap interface: {0}")]
132 TapEnable(TapError),
133 #[error("failed to get tap interface MTU: {0}")]
135 TapGetMtu(TapError),
136 #[error("failed to open tap device: {0}")]
138 TapOpen(TapError),
139 #[error("failed to set tap IP: {0}")]
141 TapSetIp(TapError),
142 #[error("failed to set tap mac address: {0}")]
144 TapSetMacAddress(TapError),
145 #[error("failed to set tap netmask: {0}")]
147 TapSetNetmask(TapError),
148 #[error("failed to set tap offload: {0}")]
150 TapSetOffload(TapError),
151 #[error("failed to set vnet header size: {0}")]
153 TapSetVnetHdrSize(TapError),
154 #[error("failed to validate tap interface: {0}")]
156 TapValidate(String),
157 #[error("failed to disable EPOLLIN on tap fd: {0}")]
159 WaitContextDisableTap(SysError),
160 #[error("failed to enable EPOLLIN on tap fd: {0}")]
162 WaitContextEnableTap(SysError),
163 #[error("error while waiting for events: {0}")]
165 WaitError(SysError),
166 #[error("failed to write control message ack: {0}")]
168 WriteAck(io::Error),
169 #[cfg(any(target_os = "android", target_os = "linux"))]
171 #[error("failed to write to guest buffer: {0}")]
172 WriteBuffer(io::Error),
173}
174
175#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)]
176#[serde(untagged, deny_unknown_fields)]
177pub enum NetParametersMode {
178 #[serde(rename_all = "kebab-case")]
179 TapName {
180 tap_name: String,
181 mac: Option<MacAddress>,
182 },
183 #[serde(rename_all = "kebab-case")]
184 TapFd {
185 tap_fd: i32,
186 mac: Option<MacAddress>,
187 },
188 #[serde(rename_all = "kebab-case")]
189 RawConfig {
190 host_ip: Ipv4Addr,
191 netmask: Ipv4Addr,
192 mac: MacAddress,
193 },
194}
195
196#[cfg(any(target_os = "android", target_os = "linux"))]
197fn vhost_net_device_path_default() -> PathBuf {
198 PathBuf::from(VHOST_NET_DEFAULT_PATH)
199}
200
201#[cfg(any(target_os = "android", target_os = "linux"))]
202#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
203#[serde(rename_all = "kebab-case", deny_unknown_fields)]
204pub struct VhostNetParameters {
205 #[serde(default = "vhost_net_device_path_default")]
206 pub device: PathBuf,
207}
208
209#[cfg(any(target_os = "android", target_os = "linux"))]
210impl Default for VhostNetParameters {
211 fn default() -> Self {
212 Self {
213 device: vhost_net_device_path_default(),
214 }
215 }
216}
217
218#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)]
219#[serde(rename_all = "kebab-case")]
220pub struct NetParameters {
221 #[serde(flatten)]
222 pub mode: NetParametersMode,
223 pub vq_pairs: Option<u16>,
224 #[cfg(any(target_os = "android", target_os = "linux"))]
227 pub vhost_net: Option<VhostNetParameters>,
228 #[serde(default)]
229 pub packed_queue: bool,
230 pub pci_address: Option<PciAddress>,
231 #[serde(default)]
232 pub mrg_rxbuf: bool,
233}
234
235impl FromStr for NetParameters {
236 type Err = String;
237 fn from_str(s: &str) -> Result<Self, Self::Err> {
238 serde_keyvalue::from_key_values(s).map_err(|e| e.to_string())
239 }
240}
241
242#[repr(C, packed)]
243#[derive(Debug, Clone, Copy, FromBytes, Immutable, IntoBytes, KnownLayout)]
244pub struct virtio_net_ctrl_hdr {
245 pub class: u8,
246 pub cmd: u8,
247}
248
249#[derive(Debug, Clone, Copy, Default, FromBytes, Immutable, IntoBytes, KnownLayout)]
250#[repr(C)]
251pub struct VirtioNetConfig {
252 mac: [u8; 6],
253 status: Le16,
254 max_vq_pairs: Le16,
255 mtu: Le16,
256}
257
258fn process_ctrl_request<T: TapT>(
259 reader: &mut Reader,
260 tap: &mut T,
261 acked_features: u64,
262 vq_pairs: u16,
263) -> Result<(), NetError> {
264 let ctrl_hdr: virtio_net_ctrl_hdr = reader.read_obj().map_err(NetError::ReadCtrlHeader)?;
265
266 match ctrl_hdr.class as c_uint {
267 VIRTIO_NET_CTRL_GUEST_OFFLOADS => {
268 if ctrl_hdr.cmd != VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET as u8 {
269 error!(
270 "invalid cmd for VIRTIO_NET_CTRL_GUEST_OFFLOADS: {}",
271 ctrl_hdr.cmd
272 );
273 return Err(NetError::InvalidCmd);
274 }
275 let offloads: Le64 = reader.read_obj().map_err(NetError::ReadCtrlData)?;
276 let tap_offloads = virtio_features_to_tap_offload(offloads.into());
277 tap.set_offload(tap_offloads)
278 .map_err(NetError::TapSetOffload)?;
279 }
280 VIRTIO_NET_CTRL_MQ => {
281 if ctrl_hdr.cmd == VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET as u8 {
282 let pairs: Le16 = reader.read_obj().map_err(NetError::ReadCtrlData)?;
283 if acked_features & 1 << virtio_net::VIRTIO_NET_F_MQ == 0
285 || pairs.to_native() != vq_pairs
286 {
287 error!(
288 "Invalid VQ_PAIRS_SET cmd, driver request pairs: {}, device vq pairs: {}",
289 pairs.to_native(),
290 vq_pairs
291 );
292 return Err(NetError::InvalidCmd);
293 }
294 }
295 }
296 _ => {
297 warn!(
298 "unimplemented class for VIRTIO_NET_CTRL_GUEST_OFFLOADS: {}",
299 ctrl_hdr.class
300 );
301 return Err(NetError::InvalidCmd);
302 }
303 }
304
305 Ok(())
306}
307
308pub fn process_ctrl<T: TapT>(
309 ctrl_queue: &mut Queue,
310 tap: &mut T,
311 acked_features: u64,
312 vq_pairs: u16,
313) -> Result<(), NetError> {
314 while let Some(mut desc_chain) = ctrl_queue.pop() {
315 if let Err(e) = process_ctrl_request(&mut desc_chain.reader, tap, acked_features, vq_pairs)
316 {
317 error!("process_ctrl_request failed: {}", e);
318 desc_chain
319 .writer
320 .write_all(&[VIRTIO_NET_ERR as u8])
321 .map_err(NetError::WriteAck)?;
322 } else {
323 desc_chain
324 .writer
325 .write_all(&[VIRTIO_NET_OK as u8])
326 .map_err(NetError::WriteAck)?;
327 }
328 ctrl_queue.add_used(desc_chain);
329 }
330
331 ctrl_queue.trigger_interrupt();
332 Ok(())
333}
334
335#[derive(EventToken, Debug, Clone)]
336pub enum Token {
337 RxTap,
339 RxQueue,
341 TxQueue,
343 CtrlQueue,
345 Kill,
347}
348
349pub(crate) struct Worker<T: TapT> {
350 pub(crate) rx_queue: Queue,
351 pub(crate) tx_queue: Queue,
352 pub(crate) ctrl_queue: Option<Queue>,
353 pub(crate) tap: T,
354 #[cfg(windows)]
355 pub(crate) overlapped_wrapper: OverlappedWrapper,
356 #[cfg(windows)]
357 pub(crate) rx_buf: [u8; MAX_BUFFER_SIZE],
358 #[cfg(windows)]
359 pub(crate) rx_count: usize,
360 #[cfg(windows)]
361 pub(crate) deferred_rx: bool,
362 acked_features: u64,
363 vq_pairs: u16,
364 #[allow(dead_code)]
365 kill_evt: Event,
366}
367
368impl<T> Worker<T>
369where
370 T: TapT + ReadNotifier,
371{
372 fn process_tx(&mut self) {
373 process_tx(&mut self.tx_queue, &mut self.tap)
374 }
375
376 fn process_ctrl(&mut self) -> Result<(), NetError> {
377 let ctrl_queue = match self.ctrl_queue.as_mut() {
378 Some(queue) => queue,
379 None => return Ok(()),
380 };
381
382 process_ctrl(
383 ctrl_queue,
384 &mut self.tap,
385 self.acked_features,
386 self.vq_pairs,
387 )
388 }
389
390 fn run(&mut self) -> Result<(), NetError> {
391 let wait_ctx: WaitContext<Token> = WaitContext::build_with(&[
392 #[cfg(windows)]
397 (
398 self.overlapped_wrapper.get_h_event_ref().unwrap(),
399 Token::RxTap,
400 ),
401 #[cfg(any(target_os = "android", target_os = "linux"))]
402 (self.tap.get_read_notifier(), Token::RxTap),
403 (self.rx_queue.event(), Token::RxQueue),
404 (self.tx_queue.event(), Token::TxQueue),
405 (&self.kill_evt, Token::Kill),
406 ])
407 .map_err(NetError::CreateWaitContext)?;
408
409 if let Some(ctrl_queue) = &self.ctrl_queue {
410 wait_ctx
411 .add(ctrl_queue.event(), Token::CtrlQueue)
412 .map_err(NetError::CreateWaitContext)?;
413 }
414
415 let mut tap_polling_enabled = true;
416 let mut pending_buffer_for_mrg_rx = PendingBuffer::new();
417 'wait: loop {
418 let events = wait_ctx.wait().map_err(NetError::WaitError)?;
419 for event in events.iter() {
420 if event.is_hungup && matches!(event.token, Token::RxTap) {
421 warn!("net: TAP fd hung up, exiting worker");
422 break 'wait;
423 }
424 if !event.is_readable {
425 continue;
426 }
427 match event.token {
428 Token::RxTap => {
429 let _trace = cros_tracing::trace_event!(VirtioNet, "handle RxTap event");
430 self.handle_rx_token(&wait_ctx, &mut pending_buffer_for_mrg_rx)?;
431 tap_polling_enabled = false;
432 }
433 Token::RxQueue => {
434 let _trace = cros_tracing::trace_event!(VirtioNet, "handle RxQueue event");
435 if let Err(e) = self.rx_queue.event().wait() {
436 error!("net: error reading rx queue Event: {}", e);
437 break 'wait;
438 }
439 self.handle_rx_queue(&wait_ctx, tap_polling_enabled)?;
440 tap_polling_enabled = true;
441 }
442 Token::TxQueue => {
443 let _trace = cros_tracing::trace_event!(VirtioNet, "handle TxQueue event");
444 if let Err(e) = self.tx_queue.event().wait() {
445 error!("net: error reading tx queue Event: {}", e);
446 break 'wait;
447 }
448 self.process_tx();
449 }
450 Token::CtrlQueue => {
451 let _trace =
452 cros_tracing::trace_event!(VirtioNet, "handle CtrlQueue event");
453 if let Some(ctrl_evt) = self.ctrl_queue.as_ref().map(|q| q.event()) {
454 if let Err(e) = ctrl_evt.wait() {
455 error!("net: error reading ctrl queue Event: {}", e);
456 break 'wait;
457 }
458 } else {
459 break 'wait;
460 }
461 if let Err(e) = self.process_ctrl() {
462 error!("net: failed to process control message: {}", e);
463 break 'wait;
464 }
465 }
466 Token::Kill => {
467 let _ = self.kill_evt.wait();
468 break 'wait;
469 }
470 }
471 }
472 }
473 Ok(())
474 }
475}
476
477pub fn build_config(vq_pairs: u16, mtu: u16, mac: Option<[u8; 6]>) -> VirtioNetConfig {
478 VirtioNetConfig {
479 max_vq_pairs: Le16::from(vq_pairs),
480 mtu: Le16::from(mtu),
481 mac: mac.unwrap_or_default(),
482 ..Default::default()
486 }
487}
488
489pub struct Net<T: TapT + ReadNotifier + 'static> {
490 guest_mac: Option<[u8; 6]>,
491 queue_sizes: Box<[u16]>,
492 worker_threads: Vec<WorkerThread<Worker<T>>>,
493 taps: Vec<T>,
494 avail_features: u64,
495 acked_features: u64,
496 mtu: u16,
497 pci_address: Option<PciAddress>,
498 #[cfg(windows)]
499 slirp_kill_evt: Option<Event>,
500}
501
502#[derive(Serialize, Deserialize)]
503struct NetSnapshot {
504 avail_features: u64,
505 acked_features: u64,
506}
507
508impl<T> Net<T>
509where
510 T: TapT + ReadNotifier,
511{
512 pub fn new(
515 base_features: u64,
516 tap: T,
517 vq_pairs: u16,
518 mac_addr: Option<MacAddress>,
519 use_packed_queue: bool,
520 pci_address: Option<PciAddress>,
521 mrg_rxbuf: bool,
522 ) -> Result<Net<T>, NetError> {
523 let taps = tap.into_mq_taps(vq_pairs).map_err(NetError::TapOpen)?;
524
525 let mut mtu = u16::MAX;
526 for tap in &taps {
530 validate_and_configure_tap(tap, vq_pairs)?;
531 mtu = std::cmp::min(mtu, tap.mtu().map_err(NetError::TapGetMtu)?);
532 }
533
534 let mut avail_features = base_features
541 | 1 << virtio_net::VIRTIO_NET_F_GUEST_CSUM
542 | 1 << virtio_net::VIRTIO_NET_F_CSUM
543 | 1 << virtio_net::VIRTIO_NET_F_CTRL_VQ
544 | 1 << virtio_net::VIRTIO_NET_F_CTRL_GUEST_OFFLOADS
545 | 1 << virtio_net::VIRTIO_NET_F_GUEST_TSO4
546 | 1 << virtio_net::VIRTIO_NET_F_GUEST_UFO
547 | 1 << virtio_net::VIRTIO_NET_F_HOST_TSO4
548 | 1 << virtio_net::VIRTIO_NET_F_HOST_UFO
549 | 1 << virtio_net::VIRTIO_NET_F_MTU;
550
551 if vq_pairs > 1 {
552 avail_features |= 1 << virtio_net::VIRTIO_NET_F_MQ;
553 }
554
555 if use_packed_queue {
556 avail_features |= 1 << VIRTIO_F_RING_PACKED;
557 }
558
559 if mac_addr.is_some() {
560 avail_features |= 1 << virtio_net::VIRTIO_NET_F_MAC;
561 }
562
563 if mrg_rxbuf {
564 avail_features |= 1 << virtio_net::VIRTIO_NET_F_MRG_RXBUF;
565 }
566
567 Self::new_internal(
568 taps,
569 avail_features,
570 mtu,
571 mac_addr,
572 pci_address,
573 #[cfg(windows)]
574 None,
575 )
576 }
577
578 pub(crate) fn new_internal(
579 taps: Vec<T>,
580 avail_features: u64,
581 mtu: u16,
582 mac_addr: Option<MacAddress>,
583 pci_address: Option<PciAddress>,
584 #[cfg(windows)] _slirp_kill_evt: Option<Event>,
585 ) -> Result<Self, NetError> {
586 let net = Self {
587 guest_mac: mac_addr.map(|mac| mac.octets()),
588 queue_sizes: vec![QUEUE_SIZE; taps.len() * 2 + 1].into_boxed_slice(),
589 worker_threads: Vec::new(),
590 taps,
591 avail_features,
592 acked_features: 0u64,
593 mtu,
594 pci_address,
595 #[cfg(windows)]
597 slirp_kill_evt: None,
598 };
599 cros_tracing::trace_simple_print!("New Net device created: {:?}", net);
600 Ok(net)
601 }
602
603 fn max_virtqueue_pairs(&self) -> usize {
606 self.taps.len()
607 }
608}
609
610impl<T> Drop for Net<T>
611where
612 T: TapT + ReadNotifier,
613{
614 fn drop(&mut self) {
615 #[cfg(windows)]
616 {
617 if let Some(slirp_kill_evt) = self.slirp_kill_evt.take() {
618 let _ = slirp_kill_evt.signal();
619 }
620 }
621 }
622}
623
624impl<T> VirtioDevice for Net<T>
625where
626 T: 'static + TapT + ReadNotifier,
627{
628 fn keep_rds(&self) -> Vec<RawDescriptor> {
629 let mut keep_rds = Vec::new();
630
631 for tap in &self.taps {
632 keep_rds.push(tap.as_raw_descriptor());
633 }
634
635 keep_rds
636 }
637
638 fn device_type(&self) -> DeviceType {
639 DeviceType::Net
640 }
641
642 fn queue_max_sizes(&self) -> &[u16] {
643 &self.queue_sizes
644 }
645
646 fn features(&self) -> u64 {
647 self.avail_features
648 }
649
650 fn ack_features(&mut self, value: u64) {
651 let mut v = value;
652
653 let unrequested_features = v & !self.avail_features;
655 if unrequested_features != 0 {
656 warn!("net: virtio net got unknown feature ack: {:x}", v);
657
658 v &= !unrequested_features;
660 }
661 self.acked_features |= v;
662
663 if let Some(tap) = self.taps.first() {
665 if let Err(e) = tap.set_offload(virtio_features_to_tap_offload(self.acked_features)) {
666 warn!(
667 "net: failed to set tap offload to match acked features: {}",
668 e
669 );
670 }
671 }
672 }
673
674 fn read_config(&self, offset: u64, data: &mut [u8]) {
675 let vq_pairs = self.queue_sizes.len() / 2;
676 let config_space = build_config(vq_pairs as u16, self.mtu, self.guest_mac);
677 copy_config(data, 0, config_space.as_bytes(), offset);
678 }
679
680 fn activate(
681 &mut self,
682 _mem: GuestMemory,
683 _interrupt: Interrupt,
684 mut queues: BTreeMap<usize, Queue>,
685 ) -> anyhow::Result<()> {
686 let ctrl_vq_enabled = self.acked_features & (1 << virtio_net::VIRTIO_NET_F_CTRL_VQ) != 0;
687 let mq_enabled = self.acked_features & (1 << virtio_net::VIRTIO_NET_F_MQ) != 0;
688
689 let vq_pairs = if mq_enabled {
690 self.max_virtqueue_pairs()
691 } else {
692 1
693 };
694
695 let mut num_queues_expected = vq_pairs * 2;
696 if ctrl_vq_enabled {
697 num_queues_expected += 1;
698 }
699
700 if queues.len() != num_queues_expected {
701 return Err(anyhow!(
702 "net: expected {} queues, got {} queues",
703 self.queue_sizes.len(),
704 queues.len(),
705 ));
706 }
707
708 if self.taps.len() < vq_pairs {
709 return Err(anyhow!(
710 "net: expected {} taps, got {}",
711 vq_pairs,
712 self.taps.len()
713 ));
714 }
715
716 for i in 0..vq_pairs {
717 let tap = self.taps.remove(0);
718 let acked_features = self.acked_features;
719 let first_queue = i == 0;
720 let rx_queue = queues.pop_first().unwrap().1;
722 let tx_queue = queues.pop_first().unwrap().1;
723 let ctrl_queue = if first_queue && ctrl_vq_enabled {
724 Some(queues.pop_last().unwrap().1)
725 } else {
726 None
727 };
728 let pairs = vq_pairs as u16;
729 #[cfg(windows)]
730 let overlapped_wrapper = OverlappedWrapper::new(true).unwrap();
731 self.worker_threads
732 .push(WorkerThread::start(format!("v_net:{i}"), move |kill_evt| {
733 let mut worker = Worker {
734 rx_queue,
735 tx_queue,
736 ctrl_queue,
737 tap,
738 #[cfg(windows)]
739 overlapped_wrapper,
740 acked_features,
741 vq_pairs: pairs,
742 #[cfg(windows)]
743 rx_buf: [0u8; MAX_BUFFER_SIZE],
744 #[cfg(windows)]
745 rx_count: 0,
746 #[cfg(windows)]
747 deferred_rx: false,
748 kill_evt,
749 };
750 let result = worker.run();
751 if let Err(e) = result {
752 error!("net worker thread exited with error: {}", e);
753 }
754 worker
755 }));
756 }
757 cros_tracing::trace_simple_print!("Net device activated: {:?}", self);
758 Ok(())
759 }
760
761 fn pci_address(&self) -> Option<PciAddress> {
762 self.pci_address
763 }
764
765 fn virtio_sleep(&mut self) -> anyhow::Result<Option<BTreeMap<usize, Queue>>> {
766 if self.worker_threads.is_empty() {
767 return Ok(None);
768 }
769 let mut queues = BTreeMap::new();
770 let mut queue_index = 0;
771 let mut ctrl_queue = None;
772 for worker_thread in self.worker_threads.drain(..) {
773 let mut worker = worker_thread.stop();
774 if worker.ctrl_queue.is_some() {
775 ctrl_queue = worker.ctrl_queue.take();
776 }
777 self.taps.push(worker.tap);
778 queues.insert(queue_index + 0, worker.rx_queue);
779 queues.insert(queue_index + 1, worker.tx_queue);
780 queue_index += 2;
781 }
782 if let Some(ctrl_queue) = ctrl_queue {
783 queues.insert(queue_index, ctrl_queue);
784 }
785 Ok(Some(queues))
786 }
787
788 fn virtio_wake(
789 &mut self,
790 device_state: Option<(GuestMemory, Interrupt, BTreeMap<usize, Queue>)>,
791 ) -> anyhow::Result<()> {
792 match device_state {
793 None => Ok(()),
794 Some((mem, interrupt, queues)) => {
795 self.activate(mem, interrupt, queues)?;
799 Ok(())
800 }
801 }
802 }
803
804 fn virtio_snapshot(&mut self) -> anyhow::Result<AnySnapshot> {
805 AnySnapshot::to_any(NetSnapshot {
806 acked_features: self.acked_features,
807 avail_features: self.avail_features,
808 })
809 .context("failed to snapshot virtio Net device")
810 }
811
812 fn virtio_restore(&mut self, data: AnySnapshot) -> anyhow::Result<()> {
813 let deser: NetSnapshot =
814 AnySnapshot::from_any(data).context("failed to deserialize Net device")?;
815 anyhow::ensure!(
816 self.avail_features == deser.avail_features,
817 "Available features for net device do not match. expected: {}, got: {}",
818 deser.avail_features,
819 self.avail_features
820 );
821 self.acked_features = deser.acked_features;
822 Ok(())
823 }
824
825 fn reset(&mut self) -> anyhow::Result<()> {
826 for worker_thread in self.worker_threads.drain(..) {
827 let worker = worker_thread.stop();
828 self.taps.push(worker.tap);
829 }
830
831 Ok(())
832 }
833}
834
835impl<T> std::fmt::Debug for Net<T>
836where
837 T: TapT + ReadNotifier,
838{
839 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
840 f.debug_struct("Net")
841 .field("guest_mac", &self.guest_mac)
842 .field("queue_sizes", &self.queue_sizes)
843 .field("worker_threads_size", &self.worker_threads.len())
844 .field("taps_size", &self.taps.len())
845 .field("avail_features", &self.avail_features)
846 .field("acked_features", &self.acked_features)
847 .field("mtu", &self.mtu)
848 .finish()
849 }
850}
851
852impl NetParameters {
853 pub fn create_net_device(
854 &self,
855 protection_type: ProtectionType,
856 ) -> anyhow::Result<Box<dyn VirtioDevice>> {
857 #[cfg(any(target_os = "android", target_os = "linux"))]
858 {
859 let vq_pairs = self.vq_pairs.unwrap_or(1);
860 let multi_vq = vq_pairs > 1 && self.vhost_net.is_none();
861
862 let features = devices::virtio::base_features(protection_type);
863 let (tap, mac) = create_tap_for_net_device(&self.mode, multi_vq)?;
864
865 let dev = if let Some(vhost_net) = &self.vhost_net {
866 Box::new(
867 crate::vhost::Net::<_, ::vhost::Net<_>>::new(
868 &vhost_net.device,
869 features,
870 tap,
871 mac,
872 self.packed_queue,
873 self.pci_address,
874 self.mrg_rxbuf,
875 )
876 .context("failed to set up virtio-vhost networking")?,
877 ) as Box<dyn VirtioDevice>
878 } else {
879 Box::new(
880 Net::new(
881 features,
882 tap,
883 vq_pairs,
884 mac,
885 self.packed_queue,
886 self.pci_address,
887 self.mrg_rxbuf,
888 )
889 .context("failed to set up virtio networking")?,
890 ) as Box<dyn VirtioDevice>
891 };
892 Ok(dev)
893 }
894 #[cfg(windows)]
895 {
896 let _ = protection_type;
897 anyhow::bail!("net device not supported on Windows");
898 }
899 }
900}
901
902impl VirtioDeviceModule for NetParameters {
903 fn sort_name(&self) -> &'static str {
904 "net"
905 }
906
907 fn create(&self, args: &mut VirtioDeviceArgs<'_>) -> anyhow::Result<Box<dyn VirtioDevice>> {
908 self.create_net_device(args.protection_type)
909 }
910
911 #[cfg(any(target_os = "android", target_os = "linux"))]
912 fn create_jail(
913 &self,
914 jail_config: &jail::JailConfig,
915 ) -> anyhow::Result<Option<minijail::Minijail>> {
916 let policy = if self.vhost_net.is_some() {
917 "vhost_net"
918 } else {
919 "net"
920 };
921 let jail = jail::simple_jail(
922 Some(jail_config),
923 &devices::virtio::VirtioDeviceType::Regular.seccomp_policy_file(policy),
924 )?;
925 Ok(jail)
926 }
927}
928
929#[cfg(test)]
930mod tests {
931 use serde_keyvalue::*;
932
933 use super::*;
934
935 fn from_net_arg(options: &str) -> Result<NetParameters, ParseError> {
936 from_key_values(options)
937 }
938
939 #[test]
940 fn params_from_key_values() {
941 let params = from_net_arg("");
942 assert!(params.is_err());
943
944 let params = from_net_arg("tap-name=tap").unwrap();
945 assert_eq!(
946 params,
947 NetParameters {
948 #[cfg(any(target_os = "android", target_os = "linux"))]
949 vhost_net: None,
950 vq_pairs: None,
951 mode: NetParametersMode::TapName {
952 tap_name: "tap".to_string(),
953 mac: None
954 },
955 packed_queue: false,
956 pci_address: None,
957 mrg_rxbuf: false,
958 }
959 );
960
961 let params = from_net_arg("tap-name=tap,mrg-rxbuf=true").unwrap();
962 assert_eq!(
963 params,
964 NetParameters {
965 #[cfg(any(target_os = "android", target_os = "linux"))]
966 vhost_net: None,
967 vq_pairs: None,
968 mode: NetParametersMode::TapName {
969 tap_name: "tap".to_string(),
970 mac: None
971 },
972 packed_queue: false,
973 pci_address: None,
974 mrg_rxbuf: true,
975 }
976 );
977
978 let params = from_net_arg("tap-name=tap,mac=\"3d:70:eb:61:1a:91\"").unwrap();
979 assert_eq!(
980 params,
981 NetParameters {
982 #[cfg(any(target_os = "android", target_os = "linux"))]
983 vhost_net: None,
984 vq_pairs: None,
985 mode: NetParametersMode::TapName {
986 tap_name: "tap".to_string(),
987 mac: Some(MacAddress::from_str("3d:70:eb:61:1a:91").unwrap())
988 },
989 packed_queue: false,
990 pci_address: None,
991 mrg_rxbuf: false,
992 }
993 );
994
995 let params = from_net_arg("tap-fd=12").unwrap();
996 assert_eq!(
997 params,
998 NetParameters {
999 #[cfg(any(target_os = "android", target_os = "linux"))]
1000 vhost_net: None,
1001 vq_pairs: None,
1002 mode: NetParametersMode::TapFd {
1003 tap_fd: 12,
1004 mac: None
1005 },
1006 packed_queue: false,
1007 pci_address: None,
1008 mrg_rxbuf: false,
1009 }
1010 );
1011
1012 let params = from_net_arg("tap-fd=12,mac=\"3d:70:eb:61:1a:91\"").unwrap();
1013 assert_eq!(
1014 params,
1015 NetParameters {
1016 #[cfg(any(target_os = "android", target_os = "linux"))]
1017 vhost_net: None,
1018 vq_pairs: None,
1019 mode: NetParametersMode::TapFd {
1020 tap_fd: 12,
1021 mac: Some(MacAddress::from_str("3d:70:eb:61:1a:91").unwrap())
1022 },
1023 packed_queue: false,
1024 pci_address: None,
1025 mrg_rxbuf: false,
1026 }
1027 );
1028
1029 let params = from_net_arg(
1030 "host-ip=\"192.168.10.1\",netmask=\"255.255.255.0\",mac=\"3d:70:eb:61:1a:91\"",
1031 )
1032 .unwrap();
1033 assert_eq!(
1034 params,
1035 NetParameters {
1036 #[cfg(any(target_os = "android", target_os = "linux"))]
1037 vhost_net: None,
1038 vq_pairs: None,
1039 mode: NetParametersMode::RawConfig {
1040 host_ip: Ipv4Addr::from_str("192.168.10.1").unwrap(),
1041 netmask: Ipv4Addr::from_str("255.255.255.0").unwrap(),
1042 mac: MacAddress::from_str("3d:70:eb:61:1a:91").unwrap(),
1043 },
1044 packed_queue: false,
1045 pci_address: None,
1046 mrg_rxbuf: false,
1047 }
1048 );
1049
1050 let params = from_net_arg("tap-fd=12,pci-address=00:01.1").unwrap();
1051 assert_eq!(
1052 params,
1053 NetParameters {
1054 #[cfg(any(target_os = "android", target_os = "linux"))]
1055 vhost_net: None,
1056 vq_pairs: None,
1057 mode: NetParametersMode::TapFd {
1058 tap_fd: 12,
1059 mac: None,
1060 },
1061 packed_queue: false,
1062 pci_address: Some(PciAddress {
1063 bus: 0,
1064 dev: 1,
1065 func: 1,
1066 }),
1067 mrg_rxbuf: false,
1068 }
1069 );
1070
1071 assert!(from_net_arg("tap-fd=12,pci-address=hello").is_err());
1073
1074 assert!(from_net_arg("host-ip=\"192.168.10.1\",mac=\"3d:70:eb:61:1a:91\"").is_err());
1076
1077 assert!(from_net_arg("tap-name=tap,foomatic=true").is_err());
1079 }
1080
1081 #[test]
1082 #[cfg(any(target_os = "android", target_os = "linux"))]
1083 fn params_from_key_values_vhost_net() {
1084 let params = from_net_arg(
1085 "vhost-net=[device=/dev/foo],\
1086 host-ip=\"192.168.10.1\",\
1087 netmask=\"255.255.255.0\",\
1088 mac=\"3d:70:eb:61:1a:91\"",
1089 )
1090 .unwrap();
1091 assert_eq!(
1092 params,
1093 NetParameters {
1094 vhost_net: Some(VhostNetParameters {
1095 device: PathBuf::from("/dev/foo")
1096 }),
1097 vq_pairs: None,
1098 mode: NetParametersMode::RawConfig {
1099 host_ip: Ipv4Addr::from_str("192.168.10.1").unwrap(),
1100 netmask: Ipv4Addr::from_str("255.255.255.0").unwrap(),
1101 mac: MacAddress::from_str("3d:70:eb:61:1a:91").unwrap(),
1102 },
1103 packed_queue: false,
1104 pci_address: None,
1105 mrg_rxbuf: false,
1106 }
1107 );
1108
1109 let params = from_net_arg("tap-fd=3,vhost-net").unwrap();
1110 assert_eq!(
1111 params,
1112 NetParameters {
1113 vhost_net: Some(Default::default()),
1114 vq_pairs: None,
1115 mode: NetParametersMode::TapFd {
1116 tap_fd: 3,
1117 mac: None
1118 },
1119 packed_queue: false,
1120 pci_address: None,
1121 mrg_rxbuf: false,
1122 }
1123 );
1124
1125 let params = from_net_arg("vhost-net,tap-name=crosvm_tap").unwrap();
1126 assert_eq!(
1127 params,
1128 NetParameters {
1129 vhost_net: Some(Default::default()),
1130 vq_pairs: None,
1131 mode: NetParametersMode::TapName {
1132 tap_name: "crosvm_tap".to_owned(),
1133 mac: None
1134 },
1135 packed_queue: false,
1136 pci_address: None,
1137 mrg_rxbuf: false,
1138 }
1139 );
1140
1141 let params =
1142 from_net_arg("vhost-net,mac=\"3d:70:eb:61:1a:91\",tap-name=crosvm_tap").unwrap();
1143 assert_eq!(
1144 params,
1145 NetParameters {
1146 vhost_net: Some(Default::default()),
1147 vq_pairs: None,
1148 mode: NetParametersMode::TapName {
1149 tap_name: "crosvm_tap".to_owned(),
1150 mac: Some(MacAddress::from_str("3d:70:eb:61:1a:91").unwrap())
1151 },
1152 packed_queue: false,
1153 pci_address: None,
1154 mrg_rxbuf: false,
1155 }
1156 );
1157
1158 let params = from_net_arg("tap-name=tap,packed-queue=true").unwrap();
1159 assert_eq!(
1160 params,
1161 NetParameters {
1162 #[cfg(any(target_os = "android", target_os = "linux"))]
1163 vhost_net: None,
1164 vq_pairs: None,
1165 mode: NetParametersMode::TapName {
1166 tap_name: "tap".to_string(),
1167 mac: None
1168 },
1169 packed_queue: true,
1170 pci_address: None,
1171 mrg_rxbuf: false,
1172 }
1173 );
1174
1175 let params = from_net_arg("tap-name=tap,packed-queue").unwrap();
1176 assert_eq!(
1177 params,
1178 NetParameters {
1179 #[cfg(any(target_os = "android", target_os = "linux"))]
1180 vhost_net: None,
1181 vq_pairs: None,
1182 mode: NetParametersMode::TapName {
1183 tap_name: "tap".to_string(),
1184 mac: None
1185 },
1186 packed_queue: true,
1187 pci_address: None,
1188 mrg_rxbuf: false,
1189 }
1190 );
1191
1192 let params = from_net_arg("vhost-net,tap-name=crosvm_tap,pci-address=00:01.1").unwrap();
1193 assert_eq!(
1194 params,
1195 NetParameters {
1196 vhost_net: Some(Default::default()),
1197 vq_pairs: None,
1198 mode: NetParametersMode::TapName {
1199 tap_name: "crosvm_tap".to_owned(),
1200 mac: None,
1201 },
1202 packed_queue: false,
1203 pci_address: Some(PciAddress {
1204 bus: 0,
1205 dev: 1,
1206 func: 1,
1207 }),
1208 mrg_rxbuf: false,
1209 }
1210 );
1211
1212 let params = from_net_arg("vhost-net,tap-name=crosvm_tap,mrg-rxbuf=true").unwrap();
1213 assert_eq!(
1214 params,
1215 NetParameters {
1216 vhost_net: Some(Default::default()),
1217 vq_pairs: None,
1218 mode: NetParametersMode::TapName {
1219 tap_name: "crosvm_tap".to_owned(),
1220 mac: None,
1221 },
1222 packed_queue: false,
1223 pci_address: None,
1224 mrg_rxbuf: true,
1225 }
1226 );
1227
1228 let params = from_net_arg("vhost-net,tap-name=crosvm_tap,mrg-rxbuf").unwrap();
1229 assert_eq!(
1230 params,
1231 NetParameters {
1232 vhost_net: Some(Default::default()),
1233 vq_pairs: None,
1234 mode: NetParametersMode::TapName {
1235 tap_name: "crosvm_tap".to_owned(),
1236 mac: None,
1237 },
1238 packed_queue: false,
1239 pci_address: None,
1240 mrg_rxbuf: true,
1241 }
1242 );
1243
1244 assert!(from_net_arg(
1246 "tap-name=tap,\
1247 vhost-net,\
1248 host-ip=\"192.168.10.1\",\
1249 netmask=\"255.255.255.0\",\
1250 mac=\"3d:70:eb:61:1a:91\"",
1251 )
1252 .is_err());
1253 }
1254}