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::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; 17 use vm_memory::{ByteValued, Bytes, GuestMemoryAtomic, 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 queue: &mut Queue<GuestMemoryAtomic<GuestMemoryMmap>>, 62 access_platform: Option<&Arc<dyn AccessPlatform>>, 63 ) -> Result<()> { 64 let mut used_desc_heads = Vec::new(); 65 loop { 66 for mut desc_chain in queue.iter().map_err(Error::QueueIterator)? { 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 used_desc_heads.push((desc_chain.head_index(), len)); 143 } 144 145 for (desc_index, len) in used_desc_heads.iter() { 146 queue 147 .add_used(*desc_index, *len) 148 .map_err(Error::QueueAddUsed)?; 149 } 150 151 if !queue 152 .enable_notification() 153 .map_err(Error::QueueEnableNotification)? 154 { 155 break; 156 } 157 } 158 159 Ok(()) 160 } 161 } 162 163 pub fn virtio_features_to_tap_offload(features: u64) -> c_uint { 164 let mut tap_offloads: c_uint = 0; 165 if features & (1 << VIRTIO_NET_F_GUEST_CSUM) != 0 { 166 tap_offloads |= net_gen::TUN_F_CSUM; 167 } 168 if features & (1 << VIRTIO_NET_F_GUEST_TSO4) != 0 { 169 tap_offloads |= net_gen::TUN_F_TSO4; 170 } 171 if features & (1 << VIRTIO_NET_F_GUEST_TSO6) != 0 { 172 tap_offloads |= net_gen::TUN_F_TSO6; 173 } 174 if features & (1 << VIRTIO_NET_F_GUEST_ECN) != 0 { 175 tap_offloads |= net_gen::TUN_F_TSO_ECN; 176 } 177 if features & (1 << VIRTIO_NET_F_GUEST_UFO) != 0 { 178 tap_offloads |= net_gen::TUN_F_UFO; 179 } 180 181 tap_offloads 182 } 183