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().filter(|e| e.is_readable) {
403 match event.token {
404 Token::RxTap => {
405 let _trace = cros_tracing::trace_event!(VirtioNet, "handle RxTap event");
406 self.handle_rx_token(&wait_ctx, &mut pending_buffer_for_mrg_rx)?;
407 tap_polling_enabled = false;
408 }
409 Token::RxQueue => {
410 let _trace = cros_tracing::trace_event!(VirtioNet, "handle RxQueue event");
411 if let Err(e) = self.rx_queue.event().wait() {
412 error!("net: error reading rx queue Event: {}", e);
413 break 'wait;
414 }
415 self.handle_rx_queue(&wait_ctx, tap_polling_enabled)?;
416 tap_polling_enabled = true;
417 }
418 Token::TxQueue => {
419 let _trace = cros_tracing::trace_event!(VirtioNet, "handle TxQueue event");
420 if let Err(e) = self.tx_queue.event().wait() {
421 error!("net: error reading tx queue Event: {}", e);
422 break 'wait;
423 }
424 self.process_tx();
425 }
426 Token::CtrlQueue => {
427 let _trace =
428 cros_tracing::trace_event!(VirtioNet, "handle CtrlQueue event");
429 if let Some(ctrl_evt) = self.ctrl_queue.as_ref().map(|q| q.event()) {
430 if let Err(e) = ctrl_evt.wait() {
431 error!("net: error reading ctrl queue Event: {}", e);
432 break 'wait;
433 }
434 } else {
435 break 'wait;
436 }
437 if let Err(e) = self.process_ctrl() {
438 error!("net: failed to process control message: {}", e);
439 break 'wait;
440 }
441 }
442 Token::Kill => {
443 let _ = self.kill_evt.wait();
444 break 'wait;
445 }
446 }
447 }
448 }
449 Ok(())
450 }
451}
452
453pub fn build_config(vq_pairs: u16, mtu: u16, mac: Option<[u8; 6]>) -> VirtioNetConfig {
454 VirtioNetConfig {
455 max_vq_pairs: Le16::from(vq_pairs),
456 mtu: Le16::from(mtu),
457 mac: mac.unwrap_or_default(),
458 ..Default::default()
462 }
463}
464
465pub struct Net<T: TapT + ReadNotifier + 'static> {
466 guest_mac: Option<[u8; 6]>,
467 queue_sizes: Box<[u16]>,
468 worker_threads: Vec<WorkerThread<Worker<T>>>,
469 taps: Vec<T>,
470 avail_features: u64,
471 acked_features: u64,
472 mtu: u16,
473 pci_address: Option<PciAddress>,
474 #[cfg(windows)]
475 slirp_kill_evt: Option<Event>,
476}
477
478#[derive(Serialize, Deserialize)]
479struct NetSnapshot {
480 avail_features: u64,
481 acked_features: u64,
482}
483
484impl<T> Net<T>
485where
486 T: TapT + ReadNotifier,
487{
488 pub fn new(
491 base_features: u64,
492 tap: T,
493 vq_pairs: u16,
494 mac_addr: Option<MacAddress>,
495 use_packed_queue: bool,
496 pci_address: Option<PciAddress>,
497 mrg_rxbuf: bool,
498 ) -> Result<Net<T>, NetError> {
499 let taps = tap.into_mq_taps(vq_pairs).map_err(NetError::TapOpen)?;
500
501 let mut mtu = u16::MAX;
502 for tap in &taps {
506 validate_and_configure_tap(tap, vq_pairs)?;
507 mtu = std::cmp::min(mtu, tap.mtu().map_err(NetError::TapGetMtu)?);
508 }
509
510 let mut avail_features = base_features
517 | 1 << virtio_net::VIRTIO_NET_F_GUEST_CSUM
518 | 1 << virtio_net::VIRTIO_NET_F_CSUM
519 | 1 << virtio_net::VIRTIO_NET_F_CTRL_VQ
520 | 1 << virtio_net::VIRTIO_NET_F_CTRL_GUEST_OFFLOADS
521 | 1 << virtio_net::VIRTIO_NET_F_GUEST_TSO4
522 | 1 << virtio_net::VIRTIO_NET_F_GUEST_UFO
523 | 1 << virtio_net::VIRTIO_NET_F_HOST_TSO4
524 | 1 << virtio_net::VIRTIO_NET_F_HOST_UFO
525 | 1 << virtio_net::VIRTIO_NET_F_MTU;
526
527 if vq_pairs > 1 {
528 avail_features |= 1 << virtio_net::VIRTIO_NET_F_MQ;
529 }
530
531 if use_packed_queue {
532 avail_features |= 1 << VIRTIO_F_RING_PACKED;
533 }
534
535 if mac_addr.is_some() {
536 avail_features |= 1 << virtio_net::VIRTIO_NET_F_MAC;
537 }
538
539 if mrg_rxbuf {
540 avail_features |= 1 << virtio_net::VIRTIO_NET_F_MRG_RXBUF;
541 }
542
543 Self::new_internal(
544 taps,
545 avail_features,
546 mtu,
547 mac_addr,
548 pci_address,
549 #[cfg(windows)]
550 None,
551 )
552 }
553
554 pub(crate) fn new_internal(
555 taps: Vec<T>,
556 avail_features: u64,
557 mtu: u16,
558 mac_addr: Option<MacAddress>,
559 pci_address: Option<PciAddress>,
560 #[cfg(windows)] slirp_kill_evt: Option<Event>,
561 ) -> Result<Self, NetError> {
562 let net = Self {
563 guest_mac: mac_addr.map(|mac| mac.octets()),
564 queue_sizes: vec![QUEUE_SIZE; taps.len() * 2 + 1].into_boxed_slice(),
565 worker_threads: Vec::new(),
566 taps,
567 avail_features,
568 acked_features: 0u64,
569 mtu,
570 pci_address,
571 #[cfg(windows)]
572 slirp_kill_evt: None,
573 };
574 cros_tracing::trace_simple_print!("New Net device created: {:?}", net);
575 Ok(net)
576 }
577
578 fn max_virtqueue_pairs(&self) -> usize {
581 self.taps.len()
582 }
583}
584
585impl<T> Drop for Net<T>
586where
587 T: TapT + ReadNotifier,
588{
589 fn drop(&mut self) {
590 #[cfg(windows)]
591 {
592 if let Some(slirp_kill_evt) = self.slirp_kill_evt.take() {
593 let _ = slirp_kill_evt.signal();
594 }
595 }
596 }
597}
598
599impl<T> VirtioDevice for Net<T>
600where
601 T: 'static + TapT + ReadNotifier,
602{
603 fn keep_rds(&self) -> Vec<RawDescriptor> {
604 let mut keep_rds = Vec::new();
605
606 for tap in &self.taps {
607 keep_rds.push(tap.as_raw_descriptor());
608 }
609
610 keep_rds
611 }
612
613 fn device_type(&self) -> DeviceType {
614 DeviceType::Net
615 }
616
617 fn queue_max_sizes(&self) -> &[u16] {
618 &self.queue_sizes
619 }
620
621 fn features(&self) -> u64 {
622 self.avail_features
623 }
624
625 fn ack_features(&mut self, value: u64) {
626 let mut v = value;
627
628 let unrequested_features = v & !self.avail_features;
630 if unrequested_features != 0 {
631 warn!("net: virtio net got unknown feature ack: {:x}", v);
632
633 v &= !unrequested_features;
635 }
636 self.acked_features |= v;
637
638 if let Some(tap) = self.taps.first() {
640 if let Err(e) = tap.set_offload(virtio_features_to_tap_offload(self.acked_features)) {
641 warn!(
642 "net: failed to set tap offload to match acked features: {}",
643 e
644 );
645 }
646 }
647 }
648
649 fn read_config(&self, offset: u64, data: &mut [u8]) {
650 let vq_pairs = self.queue_sizes.len() / 2;
651 let config_space = build_config(vq_pairs as u16, self.mtu, self.guest_mac);
652 copy_config(data, 0, config_space.as_bytes(), offset);
653 }
654
655 fn activate(
656 &mut self,
657 _mem: GuestMemory,
658 _interrupt: Interrupt,
659 mut queues: BTreeMap<usize, Queue>,
660 ) -> anyhow::Result<()> {
661 let ctrl_vq_enabled = self.acked_features & (1 << virtio_net::VIRTIO_NET_F_CTRL_VQ) != 0;
662 let mq_enabled = self.acked_features & (1 << virtio_net::VIRTIO_NET_F_MQ) != 0;
663
664 let vq_pairs = if mq_enabled {
665 self.max_virtqueue_pairs()
666 } else {
667 1
668 };
669
670 let mut num_queues_expected = vq_pairs * 2;
671 if ctrl_vq_enabled {
672 num_queues_expected += 1;
673 }
674
675 if queues.len() != num_queues_expected {
676 return Err(anyhow!(
677 "net: expected {} queues, got {} queues",
678 self.queue_sizes.len(),
679 queues.len(),
680 ));
681 }
682
683 if self.taps.len() < vq_pairs {
684 return Err(anyhow!(
685 "net: expected {} taps, got {}",
686 vq_pairs,
687 self.taps.len()
688 ));
689 }
690
691 for i in 0..vq_pairs {
692 let tap = self.taps.remove(0);
693 let acked_features = self.acked_features;
694 let first_queue = i == 0;
695 let rx_queue = queues.pop_first().unwrap().1;
697 let tx_queue = queues.pop_first().unwrap().1;
698 let ctrl_queue = if first_queue && ctrl_vq_enabled {
699 Some(queues.pop_last().unwrap().1)
700 } else {
701 None
702 };
703 let pairs = vq_pairs as u16;
704 #[cfg(windows)]
705 let overlapped_wrapper = OverlappedWrapper::new(true).unwrap();
706 self.worker_threads
707 .push(WorkerThread::start(format!("v_net:{i}"), move |kill_evt| {
708 let mut worker = Worker {
709 rx_queue,
710 tx_queue,
711 ctrl_queue,
712 tap,
713 #[cfg(windows)]
714 overlapped_wrapper,
715 acked_features,
716 vq_pairs: pairs,
717 #[cfg(windows)]
718 rx_buf: [0u8; MAX_BUFFER_SIZE],
719 #[cfg(windows)]
720 rx_count: 0,
721 #[cfg(windows)]
722 deferred_rx: false,
723 kill_evt,
724 };
725 let result = worker.run();
726 if let Err(e) = result {
727 error!("net worker thread exited with error: {}", e);
728 }
729 worker
730 }));
731 }
732 cros_tracing::trace_simple_print!("Net device activated: {:?}", self);
733 Ok(())
734 }
735
736 fn pci_address(&self) -> Option<PciAddress> {
737 self.pci_address
738 }
739
740 fn virtio_sleep(&mut self) -> anyhow::Result<Option<BTreeMap<usize, Queue>>> {
741 if self.worker_threads.is_empty() {
742 return Ok(None);
743 }
744 let mut queues = BTreeMap::new();
745 let mut queue_index = 0;
746 let mut ctrl_queue = None;
747 for worker_thread in self.worker_threads.drain(..) {
748 let mut worker = worker_thread.stop();
749 if worker.ctrl_queue.is_some() {
750 ctrl_queue = worker.ctrl_queue.take();
751 }
752 self.taps.push(worker.tap);
753 queues.insert(queue_index + 0, worker.rx_queue);
754 queues.insert(queue_index + 1, worker.tx_queue);
755 queue_index += 2;
756 }
757 if let Some(ctrl_queue) = ctrl_queue {
758 queues.insert(queue_index, ctrl_queue);
759 }
760 Ok(Some(queues))
761 }
762
763 fn virtio_wake(
764 &mut self,
765 device_state: Option<(GuestMemory, Interrupt, BTreeMap<usize, Queue>)>,
766 ) -> anyhow::Result<()> {
767 match device_state {
768 None => Ok(()),
769 Some((mem, interrupt, queues)) => {
770 self.activate(mem, interrupt, queues)?;
774 Ok(())
775 }
776 }
777 }
778
779 fn virtio_snapshot(&mut self) -> anyhow::Result<AnySnapshot> {
780 AnySnapshot::to_any(NetSnapshot {
781 acked_features: self.acked_features,
782 avail_features: self.avail_features,
783 })
784 .context("failed to snapshot virtio Net device")
785 }
786
787 fn virtio_restore(&mut self, data: AnySnapshot) -> anyhow::Result<()> {
788 let deser: NetSnapshot =
789 AnySnapshot::from_any(data).context("failed to deserialize Net device")?;
790 anyhow::ensure!(
791 self.avail_features == deser.avail_features,
792 "Available features for net device do not match. expected: {}, got: {}",
793 deser.avail_features,
794 self.avail_features
795 );
796 self.acked_features = deser.acked_features;
797 Ok(())
798 }
799
800 fn reset(&mut self) -> anyhow::Result<()> {
801 for worker_thread in self.worker_threads.drain(..) {
802 let worker = worker_thread.stop();
803 self.taps.push(worker.tap);
804 }
805
806 Ok(())
807 }
808}
809
810impl<T> std::fmt::Debug for Net<T>
811where
812 T: TapT + ReadNotifier,
813{
814 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
815 f.debug_struct("Net")
816 .field("guest_mac", &self.guest_mac)
817 .field("queue_sizes", &self.queue_sizes)
818 .field("worker_threads_size", &self.worker_threads.len())
819 .field("taps_size", &self.taps.len())
820 .field("avail_features", &self.avail_features)
821 .field("acked_features", &self.acked_features)
822 .field("mtu", &self.mtu)
823 .finish()
824 }
825}
826
827#[cfg(test)]
828mod tests {
829 use serde_keyvalue::*;
830
831 use super::*;
832
833 fn from_net_arg(options: &str) -> Result<NetParameters, ParseError> {
834 from_key_values(options)
835 }
836
837 #[test]
838 fn params_from_key_values() {
839 let params = from_net_arg("");
840 assert!(params.is_err());
841
842 let params = from_net_arg("tap-name=tap").unwrap();
843 assert_eq!(
844 params,
845 NetParameters {
846 #[cfg(any(target_os = "android", target_os = "linux"))]
847 vhost_net: None,
848 vq_pairs: None,
849 mode: NetParametersMode::TapName {
850 tap_name: "tap".to_string(),
851 mac: None
852 },
853 packed_queue: false,
854 pci_address: None,
855 mrg_rxbuf: false,
856 }
857 );
858
859 let params = from_net_arg("tap-name=tap,mrg-rxbuf=true").unwrap();
860 assert_eq!(
861 params,
862 NetParameters {
863 #[cfg(any(target_os = "android", target_os = "linux"))]
864 vhost_net: None,
865 vq_pairs: None,
866 mode: NetParametersMode::TapName {
867 tap_name: "tap".to_string(),
868 mac: None
869 },
870 packed_queue: false,
871 pci_address: None,
872 mrg_rxbuf: true,
873 }
874 );
875
876 let params = from_net_arg("tap-name=tap,mac=\"3d:70:eb:61:1a:91\"").unwrap();
877 assert_eq!(
878 params,
879 NetParameters {
880 #[cfg(any(target_os = "android", target_os = "linux"))]
881 vhost_net: None,
882 vq_pairs: None,
883 mode: NetParametersMode::TapName {
884 tap_name: "tap".to_string(),
885 mac: Some(MacAddress::from_str("3d:70:eb:61:1a:91").unwrap())
886 },
887 packed_queue: false,
888 pci_address: None,
889 mrg_rxbuf: false,
890 }
891 );
892
893 let params = from_net_arg("tap-fd=12").unwrap();
894 assert_eq!(
895 params,
896 NetParameters {
897 #[cfg(any(target_os = "android", target_os = "linux"))]
898 vhost_net: None,
899 vq_pairs: None,
900 mode: NetParametersMode::TapFd {
901 tap_fd: 12,
902 mac: None
903 },
904 packed_queue: false,
905 pci_address: None,
906 mrg_rxbuf: false,
907 }
908 );
909
910 let params = from_net_arg("tap-fd=12,mac=\"3d:70:eb:61:1a:91\"").unwrap();
911 assert_eq!(
912 params,
913 NetParameters {
914 #[cfg(any(target_os = "android", target_os = "linux"))]
915 vhost_net: None,
916 vq_pairs: None,
917 mode: NetParametersMode::TapFd {
918 tap_fd: 12,
919 mac: Some(MacAddress::from_str("3d:70:eb:61:1a:91").unwrap())
920 },
921 packed_queue: false,
922 pci_address: None,
923 mrg_rxbuf: false,
924 }
925 );
926
927 let params = from_net_arg(
928 "host-ip=\"192.168.10.1\",netmask=\"255.255.255.0\",mac=\"3d:70:eb:61:1a:91\"",
929 )
930 .unwrap();
931 assert_eq!(
932 params,
933 NetParameters {
934 #[cfg(any(target_os = "android", target_os = "linux"))]
935 vhost_net: None,
936 vq_pairs: None,
937 mode: NetParametersMode::RawConfig {
938 host_ip: Ipv4Addr::from_str("192.168.10.1").unwrap(),
939 netmask: Ipv4Addr::from_str("255.255.255.0").unwrap(),
940 mac: MacAddress::from_str("3d:70:eb:61:1a:91").unwrap(),
941 },
942 packed_queue: false,
943 pci_address: None,
944 mrg_rxbuf: false,
945 }
946 );
947
948 let params = from_net_arg("tap-fd=12,pci-address=00:01.1").unwrap();
949 assert_eq!(
950 params,
951 NetParameters {
952 #[cfg(any(target_os = "android", target_os = "linux"))]
953 vhost_net: None,
954 vq_pairs: None,
955 mode: NetParametersMode::TapFd {
956 tap_fd: 12,
957 mac: None,
958 },
959 packed_queue: false,
960 pci_address: Some(PciAddress {
961 bus: 0,
962 dev: 1,
963 func: 1,
964 }),
965 mrg_rxbuf: false,
966 }
967 );
968
969 assert!(from_net_arg("tap-fd=12,pci-address=hello").is_err());
971
972 assert!(from_net_arg("host-ip=\"192.168.10.1\",mac=\"3d:70:eb:61:1a:91\"").is_err());
974
975 assert!(from_net_arg("tap-name=tap,foomatic=true").is_err());
977 }
978
979 #[test]
980 #[cfg(any(target_os = "android", target_os = "linux"))]
981 fn params_from_key_values_vhost_net() {
982 let params = from_net_arg(
983 "vhost-net=[device=/dev/foo],\
984 host-ip=\"192.168.10.1\",\
985 netmask=\"255.255.255.0\",\
986 mac=\"3d:70:eb:61:1a:91\"",
987 )
988 .unwrap();
989 assert_eq!(
990 params,
991 NetParameters {
992 vhost_net: Some(VhostNetParameters {
993 device: PathBuf::from("/dev/foo")
994 }),
995 vq_pairs: None,
996 mode: NetParametersMode::RawConfig {
997 host_ip: Ipv4Addr::from_str("192.168.10.1").unwrap(),
998 netmask: Ipv4Addr::from_str("255.255.255.0").unwrap(),
999 mac: MacAddress::from_str("3d:70:eb:61:1a:91").unwrap(),
1000 },
1001 packed_queue: false,
1002 pci_address: None,
1003 mrg_rxbuf: false,
1004 }
1005 );
1006
1007 let params = from_net_arg("tap-fd=3,vhost-net").unwrap();
1008 assert_eq!(
1009 params,
1010 NetParameters {
1011 vhost_net: Some(Default::default()),
1012 vq_pairs: None,
1013 mode: NetParametersMode::TapFd {
1014 tap_fd: 3,
1015 mac: None
1016 },
1017 packed_queue: false,
1018 pci_address: None,
1019 mrg_rxbuf: false,
1020 }
1021 );
1022
1023 let params = from_net_arg("vhost-net,tap-name=crosvm_tap").unwrap();
1024 assert_eq!(
1025 params,
1026 NetParameters {
1027 vhost_net: Some(Default::default()),
1028 vq_pairs: None,
1029 mode: NetParametersMode::TapName {
1030 tap_name: "crosvm_tap".to_owned(),
1031 mac: None
1032 },
1033 packed_queue: false,
1034 pci_address: None,
1035 mrg_rxbuf: false,
1036 }
1037 );
1038
1039 let params =
1040 from_net_arg("vhost-net,mac=\"3d:70:eb:61:1a:91\",tap-name=crosvm_tap").unwrap();
1041 assert_eq!(
1042 params,
1043 NetParameters {
1044 vhost_net: Some(Default::default()),
1045 vq_pairs: None,
1046 mode: NetParametersMode::TapName {
1047 tap_name: "crosvm_tap".to_owned(),
1048 mac: Some(MacAddress::from_str("3d:70:eb:61:1a:91").unwrap())
1049 },
1050 packed_queue: false,
1051 pci_address: None,
1052 mrg_rxbuf: false,
1053 }
1054 );
1055
1056 let params = from_net_arg("tap-name=tap,packed-queue=true").unwrap();
1057 assert_eq!(
1058 params,
1059 NetParameters {
1060 #[cfg(any(target_os = "android", target_os = "linux"))]
1061 vhost_net: None,
1062 vq_pairs: None,
1063 mode: NetParametersMode::TapName {
1064 tap_name: "tap".to_string(),
1065 mac: None
1066 },
1067 packed_queue: true,
1068 pci_address: None,
1069 mrg_rxbuf: false,
1070 }
1071 );
1072
1073 let params = from_net_arg("tap-name=tap,packed-queue").unwrap();
1074 assert_eq!(
1075 params,
1076 NetParameters {
1077 #[cfg(any(target_os = "android", target_os = "linux"))]
1078 vhost_net: None,
1079 vq_pairs: None,
1080 mode: NetParametersMode::TapName {
1081 tap_name: "tap".to_string(),
1082 mac: None
1083 },
1084 packed_queue: true,
1085 pci_address: None,
1086 mrg_rxbuf: false,
1087 }
1088 );
1089
1090 let params = from_net_arg("vhost-net,tap-name=crosvm_tap,pci-address=00:01.1").unwrap();
1091 assert_eq!(
1092 params,
1093 NetParameters {
1094 vhost_net: Some(Default::default()),
1095 vq_pairs: None,
1096 mode: NetParametersMode::TapName {
1097 tap_name: "crosvm_tap".to_owned(),
1098 mac: None,
1099 },
1100 packed_queue: false,
1101 pci_address: Some(PciAddress {
1102 bus: 0,
1103 dev: 1,
1104 func: 1,
1105 }),
1106 mrg_rxbuf: false,
1107 }
1108 );
1109
1110 let params = from_net_arg("vhost-net,tap-name=crosvm_tap,mrg-rxbuf=true").unwrap();
1111 assert_eq!(
1112 params,
1113 NetParameters {
1114 vhost_net: Some(Default::default()),
1115 vq_pairs: None,
1116 mode: NetParametersMode::TapName {
1117 tap_name: "crosvm_tap".to_owned(),
1118 mac: None,
1119 },
1120 packed_queue: false,
1121 pci_address: None,
1122 mrg_rxbuf: true,
1123 }
1124 );
1125
1126 let params = from_net_arg("vhost-net,tap-name=crosvm_tap,mrg-rxbuf").unwrap();
1127 assert_eq!(
1128 params,
1129 NetParameters {
1130 vhost_net: Some(Default::default()),
1131 vq_pairs: None,
1132 mode: NetParametersMode::TapName {
1133 tap_name: "crosvm_tap".to_owned(),
1134 mac: None,
1135 },
1136 packed_queue: false,
1137 pci_address: None,
1138 mrg_rxbuf: true,
1139 }
1140 );
1141
1142 assert!(from_net_arg(
1144 "tap-name=tap,\
1145 vhost-net,\
1146 host-ip=\"192.168.10.1\",\
1147 netmask=\"255.255.255.0\",\
1148 mac=\"3d:70:eb:61:1a:91\"",
1149 )
1150 .is_err());
1151 }
1152}