Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/devices/src/virtio/net.rs
5394 views
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
mod sys;
6
7
use std::collections::BTreeMap;
8
use std::fmt;
9
use std::io;
10
use std::io::Write;
11
use std::net::Ipv4Addr;
12
use std::os::raw::c_uint;
13
use std::path::PathBuf;
14
use std::str::FromStr;
15
16
use anyhow::anyhow;
17
use anyhow::Context;
18
use base::error;
19
#[cfg(windows)]
20
use base::named_pipes::OverlappedWrapper;
21
use base::warn;
22
use base::Error as SysError;
23
use base::Event;
24
use base::EventToken;
25
use base::RawDescriptor;
26
use base::ReadNotifier;
27
use base::WaitContext;
28
use base::WorkerThread;
29
use data_model::Le16;
30
use data_model::Le64;
31
use net_util::Error as TapError;
32
use net_util::MacAddress;
33
use net_util::TapT;
34
use remain::sorted;
35
use serde::Deserialize;
36
use serde::Serialize;
37
use snapshot::AnySnapshot;
38
use thiserror::Error as ThisError;
39
use virtio_sys::virtio_config::VIRTIO_F_RING_PACKED;
40
use virtio_sys::virtio_net;
41
use virtio_sys::virtio_net::VIRTIO_NET_CTRL_GUEST_OFFLOADS;
42
use virtio_sys::virtio_net::VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET;
43
use virtio_sys::virtio_net::VIRTIO_NET_CTRL_MQ;
44
use virtio_sys::virtio_net::VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET;
45
use virtio_sys::virtio_net::VIRTIO_NET_ERR;
46
use virtio_sys::virtio_net::VIRTIO_NET_OK;
47
use vm_memory::GuestMemory;
48
use zerocopy::FromBytes;
49
use zerocopy::Immutable;
50
use zerocopy::IntoBytes;
51
use zerocopy::KnownLayout;
52
53
use super::copy_config;
54
use super::DeviceType;
55
use super::Interrupt;
56
use super::Queue;
57
use super::Reader;
58
use super::VirtioDevice;
59
use crate::PciAddress;
60
61
/// The maximum buffer size when segmentation offload is enabled. This
62
/// includes the 12-byte virtio net header.
63
/// http://docs.oasis-open.org/virtio/virtio/v1.0/virtio-v1.0.html#x1-1740003
64
#[cfg(windows)]
65
pub(crate) const MAX_BUFFER_SIZE: usize = 65562;
66
const QUEUE_SIZE: u16 = 256;
67
68
#[cfg(any(target_os = "android", target_os = "linux"))]
69
pub static VHOST_NET_DEFAULT_PATH: &str = "/dev/vhost-net";
70
71
pub(crate) use sys::process_mrg_rx;
72
pub(crate) use sys::process_rx;
73
pub(crate) use sys::process_tx;
74
pub(crate) use sys::validate_and_configure_tap;
75
pub(crate) use sys::virtio_features_to_tap_offload;
76
pub(crate) use sys::PendingBuffer;
77
78
#[sorted]
79
#[derive(ThisError, Debug)]
80
pub enum NetError {
81
/// Cloning kill event failed.
82
#[error("failed to clone kill event: {0}")]
83
CloneKillEvent(SysError),
84
/// Creating kill event failed.
85
#[error("failed to create kill event: {0}")]
86
CreateKillEvent(SysError),
87
/// Creating WaitContext failed.
88
#[error("failed to create wait context: {0}")]
89
CreateWaitContext(SysError),
90
/// Adding the tap descriptor back to the event context failed.
91
#[error("failed to add tap trigger to event context: {0}")]
92
EventAddTap(SysError),
93
/// Removing the tap descriptor from the event context failed.
94
#[error("failed to remove tap trigger from event context: {0}")]
95
EventRemoveTap(SysError),
96
/// Invalid control command
97
#[error("invalid control command")]
98
InvalidCmd,
99
/// Error reading data from control queue.
100
#[error("failed to read control message data: {0}")]
101
ReadCtrlData(io::Error),
102
/// Error reading header from control queue.
103
#[error("failed to read control message header: {0}")]
104
ReadCtrlHeader(io::Error),
105
/// There are no more available descriptors to receive into.
106
#[cfg(any(target_os = "android", target_os = "linux"))]
107
#[error("no rx descriptors available")]
108
RxDescriptorsExhausted,
109
/// Failure creating the Slirp loop.
110
#[cfg(windows)]
111
#[error("error creating Slirp: {0}")]
112
SlirpCreateError(net_util::Error),
113
/// Enabling tap interface failed.
114
#[error("failed to enable tap interface: {0}")]
115
TapEnable(TapError),
116
/// Couldn't get the MTU from the tap device.
117
#[error("failed to get tap interface MTU: {0}")]
118
TapGetMtu(TapError),
119
/// Open tap device failed.
120
#[error("failed to open tap device: {0}")]
121
TapOpen(TapError),
122
/// Setting tap IP failed.
123
#[error("failed to set tap IP: {0}")]
124
TapSetIp(TapError),
125
/// Setting tap mac address failed.
126
#[error("failed to set tap mac address: {0}")]
127
TapSetMacAddress(TapError),
128
/// Setting tap netmask failed.
129
#[error("failed to set tap netmask: {0}")]
130
TapSetNetmask(TapError),
131
/// Setting tap offload failed.
132
#[error("failed to set tap offload: {0}")]
133
TapSetOffload(TapError),
134
/// Setting vnet header size failed.
135
#[error("failed to set vnet header size: {0}")]
136
TapSetVnetHdrSize(TapError),
137
/// Validating tap interface failed.
138
#[error("failed to validate tap interface: {0}")]
139
TapValidate(String),
140
/// Removing read event from the tap fd events failed.
141
#[error("failed to disable EPOLLIN on tap fd: {0}")]
142
WaitContextDisableTap(SysError),
143
/// Adding read event to the tap fd events failed.
144
#[error("failed to enable EPOLLIN on tap fd: {0}")]
145
WaitContextEnableTap(SysError),
146
/// Error while waiting for events.
147
#[error("error while waiting for events: {0}")]
148
WaitError(SysError),
149
/// Failed writing an ack in response to a control message.
150
#[error("failed to write control message ack: {0}")]
151
WriteAck(io::Error),
152
/// Writing to a buffer in the guest failed.
153
#[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)]
160
pub 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"))]
180
fn 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)]
187
pub 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"))]
193
impl 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")]
203
pub struct NetParameters {
204
#[serde(flatten)]
205
pub mode: NetParametersMode,
206
pub vq_pairs: Option<u16>,
207
// Style-guide asks to refrain against #[cfg] directives in structs, this is an exception due
208
// to the fact this struct is used for argument parsing.
209
#[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
218
impl 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)]
227
pub 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)]
234
pub struct VirtioNetConfig {
235
mac: [u8; 6],
236
status: Le16,
237
max_vq_pairs: Le16,
238
mtu: Le16,
239
}
240
241
fn 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
// Simple handle it now
267
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
291
pub 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)]
319
pub enum Token {
320
// A frame is available for reading from the tap device to receive in the guest.
321
RxTap,
322
// The guest has made a buffer available to receive a frame into.
323
RxQueue,
324
// The transmit queue has a frame that is ready to send from the guest.
325
TxQueue,
326
// The control queue has a message.
327
CtrlQueue,
328
// crosvm has requested the device to shut down.
329
Kill,
330
}
331
332
pub(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
351
impl<T> Worker<T>
352
where
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
// This doesn't use get_read_notifier() because of overlapped io; we
376
// have overlapped wrapper separate from the TAP so that we can pass
377
// the overlapped wrapper into the read function. This overlapped
378
// wrapper's event is where we get the read notification.
379
#[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
453
pub 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
// Other field has meaningful value when the corresponding feature
459
// is enabled, but all these features aren't supported now.
460
// So set them to default.
461
..Default::default()
462
}
463
}
464
465
pub 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)]
479
struct NetSnapshot {
480
avail_features: u64,
481
acked_features: u64,
482
}
483
484
impl<T> Net<T>
485
where
486
T: TapT + ReadNotifier,
487
{
488
/// Creates a new virtio network device from a tap device that has already been
489
/// configured.
490
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
// This would also validate a tap created by Self::new(), but that's a good thing as it
503
// would ensure that any changes in the creation procedure are matched in the validation.
504
// Plus we still need to set the offload and vnet_hdr_size values.
505
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
// Indicate that the TAP device supports a number of features, such as:
511
// Partial checksum offload
512
// TSO (TCP segmentation offload)
513
// UFO (UDP fragmentation offload)
514
// See the network device feature bits section for further details:
515
// http://docs.oasis-open.org/virtio/virtio/v1.1/csprd01/virtio-v1.1-csprd01.html#x1-1970003
516
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
/// Returns the maximum number of receive/transmit queue pairs for this device.
579
/// Only relevant when multi-queue support is negotiated.
580
fn max_virtqueue_pairs(&self) -> usize {
581
self.taps.len()
582
}
583
}
584
585
impl<T> Drop for Net<T>
586
where
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
599
impl<T> VirtioDevice for Net<T>
600
where
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
// Check if the guest is ACK'ing a feature that we didn't claim to have.
629
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
// Don't count these features as acked.
634
v &= !unrequested_features;
635
}
636
self.acked_features |= v;
637
638
// Set offload flags to match acked virtio features.
639
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
// Queues alternate between rx0, tx0, rx1, tx1, ..., rxN, txN, ctrl.
696
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
// TODO: activate is just what we want at the moment, but we should probably move
771
// it into a "start workers" function to make it obvious that it isn't strictly
772
// used for activate events.
773
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
810
impl<T> std::fmt::Debug for Net<T>
811
where
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)]
828
mod 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
// wrong pci format
970
assert!(from_net_arg("tap-fd=12,pci-address=hello").is_err());
971
972
// missing netmask
973
assert!(from_net_arg("host-ip=\"192.168.10.1\",mac=\"3d:70:eb:61:1a:91\"").is_err());
974
975
// invalid parameter
976
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
// mixed configs
1143
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
}
1153
1154