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