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