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