1 // Copyright (c) 2021 Intel Corporation. All rights reserved. 2 // 3 // SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause 4 5 use std::sync::Arc; 6 7 use libc::c_uint; 8 use thiserror::Error; 9 use virtio_bindings::virtio_net::{ 10 VIRTIO_NET_CTRL_GUEST_OFFLOADS, VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET, VIRTIO_NET_CTRL_MQ, 11 VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN, 12 VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET, VIRTIO_NET_ERR, VIRTIO_NET_F_GUEST_CSUM, 13 VIRTIO_NET_F_GUEST_ECN, VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_TSO6, 14 VIRTIO_NET_F_GUEST_UFO, VIRTIO_NET_OK, 15 }; 16 use virtio_queue::{Queue, QueueT}; 17 use vm_memory::{ByteValued, Bytes, GuestMemoryError}; 18 use vm_virtio::{AccessPlatform, Translatable}; 19 20 use crate::{GuestMemoryMmap, Tap}; 21 22 #[derive(Error, Debug)] 23 pub enum Error { 24 /// Read queue failed. 25 #[error("Read queue failed")] 26 GuestMemory(#[source] GuestMemoryError), 27 /// No control header descriptor 28 #[error("No control header descriptor")] 29 NoControlHeaderDescriptor, 30 /// Missing the data descriptor in the chain. 31 #[error("Missing the data descriptor in the chain")] 32 NoDataDescriptor, 33 /// No status descriptor 34 #[error("No status descriptor")] 35 NoStatusDescriptor, 36 /// Failed adding used index 37 #[error("Failed adding used index")] 38 QueueAddUsed(#[source] virtio_queue::Error), 39 /// Failed creating an iterator over the queue 40 #[error("Failed creating an iterator over the queue")] 41 QueueIterator(#[source] virtio_queue::Error), 42 /// Failed enabling notification for the queue 43 #[error("Failed enabling notification for the queue")] 44 QueueEnableNotification(#[source] virtio_queue::Error), 45 } 46 47 type Result<T> = std::result::Result<T, Error>; 48 49 #[repr(C, packed)] 50 #[derive(Debug, Clone, Copy, Default)] 51 pub struct ControlHeader { 52 pub class: u8, 53 pub cmd: u8, 54 } 55 56 // SAFETY: ControlHeader only contains a series of integers 57 unsafe impl ByteValued for ControlHeader {} 58 59 pub struct CtrlQueue { 60 pub taps: Vec<Tap>, 61 } 62 63 impl CtrlQueue { 64 pub fn new(taps: Vec<Tap>) -> Self { 65 CtrlQueue { taps } 66 } 67 68 pub fn process( 69 &mut self, 70 mem: &GuestMemoryMmap, 71 queue: &mut Queue, 72 access_platform: Option<&Arc<dyn AccessPlatform>>, 73 ) -> Result<()> { 74 while let Some(mut desc_chain) = queue.pop_descriptor_chain(mem) { 75 let ctrl_desc = desc_chain.next().ok_or(Error::NoControlHeaderDescriptor)?; 76 77 let ctrl_hdr: ControlHeader = desc_chain 78 .memory() 79 .read_obj( 80 ctrl_desc 81 .addr() 82 .translate_gva(access_platform, ctrl_desc.len() as usize), 83 ) 84 .map_err(Error::GuestMemory)?; 85 let data_desc = desc_chain.next().ok_or(Error::NoDataDescriptor)?; 86 87 let data_desc_addr = data_desc 88 .addr() 89 .translate_gva(access_platform, data_desc.len() as usize); 90 91 let status_desc = desc_chain.next().ok_or(Error::NoStatusDescriptor)?; 92 93 let ok = match u32::from(ctrl_hdr.class) { 94 VIRTIO_NET_CTRL_MQ => { 95 let queue_pairs = desc_chain 96 .memory() 97 .read_obj::<u16>(data_desc_addr) 98 .map_err(Error::GuestMemory)?; 99 if u32::from(ctrl_hdr.cmd) != VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET { 100 warn!("Unsupported command: {}", ctrl_hdr.cmd); 101 false 102 } else if (queue_pairs < VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN as u16) 103 || (queue_pairs > VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX as u16) 104 { 105 warn!("Number of MQ pairs out of range: {}", queue_pairs); 106 false 107 } else { 108 info!("Number of MQ pairs requested: {}", queue_pairs); 109 true 110 } 111 } 112 VIRTIO_NET_CTRL_GUEST_OFFLOADS => { 113 let features = desc_chain 114 .memory() 115 .read_obj::<u64>(data_desc_addr) 116 .map_err(Error::GuestMemory)?; 117 if u32::from(ctrl_hdr.cmd) != VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET { 118 warn!("Unsupported command: {}", ctrl_hdr.cmd); 119 false 120 } else { 121 let mut ok = true; 122 for tap in self.taps.iter_mut() { 123 info!("Reprogramming tap offload with features: {}", features); 124 tap.set_offload(virtio_features_to_tap_offload(features)) 125 .map_err(|e| { 126 error!("Error programming tap offload: {:?}", e); 127 ok = false 128 }) 129 .ok(); 130 } 131 ok 132 } 133 } 134 _ => { 135 warn!("Unsupported command {:?}", ctrl_hdr); 136 false 137 } 138 }; 139 140 desc_chain 141 .memory() 142 .write_obj( 143 if ok { VIRTIO_NET_OK } else { VIRTIO_NET_ERR } as u8, 144 status_desc 145 .addr() 146 .translate_gva(access_platform, status_desc.len() as usize), 147 ) 148 .map_err(Error::GuestMemory)?; 149 let len = ctrl_desc.len() + data_desc.len() + status_desc.len(); 150 151 queue 152 .add_used(desc_chain.memory(), desc_chain.head_index(), len) 153 .map_err(Error::QueueAddUsed)?; 154 155 if !queue 156 .enable_notification(mem) 157 .map_err(Error::QueueEnableNotification)? 158 { 159 break; 160 } 161 } 162 163 Ok(()) 164 } 165 } 166 167 pub fn virtio_features_to_tap_offload(features: u64) -> c_uint { 168 let mut tap_offloads: c_uint = 0; 169 if features & (1 << VIRTIO_NET_F_GUEST_CSUM) != 0 { 170 tap_offloads |= net_gen::TUN_F_CSUM; 171 } 172 if features & (1 << VIRTIO_NET_F_GUEST_TSO4) != 0 { 173 tap_offloads |= net_gen::TUN_F_TSO4; 174 } 175 if features & (1 << VIRTIO_NET_F_GUEST_TSO6) != 0 { 176 tap_offloads |= net_gen::TUN_F_TSO6; 177 } 178 if features & (1 << VIRTIO_NET_F_GUEST_ECN) != 0 { 179 tap_offloads |= net_gen::TUN_F_TSO_ECN; 180 } 181 if features & (1 << VIRTIO_NET_F_GUEST_UFO) != 0 { 182 tap_offloads |= net_gen::TUN_F_UFO; 183 } 184 185 tap_offloads 186 } 187