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