1 // Copyright 2019 Intel Corporation. All Rights Reserved. 2 // 3 // Portions Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 // 5 // Portions Copyright 2017 The Chromium OS Authors. All rights reserved. 6 // 7 // SPDX-License-Identifier: (Apache-2.0 AND BSD-3-Clause) 8 9 use std::net::Ipv4Addr; 10 use std::ops::Deref; 11 use std::os::unix::io::{AsRawFd, RawFd}; 12 use std::sync::{Arc, Mutex, RwLock}; 13 use std::{fmt, io, process}; 14 15 use libc::EFD_NONBLOCK; 16 use log::*; 17 use net_util::{ 18 open_tap, MacAddr, NetCounters, NetQueuePair, OpenTapError, RxVirtio, Tap, TxVirtio, 19 }; 20 use option_parser::{OptionParser, OptionParserError, Toggle}; 21 use vhost::vhost_user::message::*; 22 use vhost::vhost_user::Listener; 23 use vhost_user_backend::bitmap::BitmapMmapRegion; 24 use vhost_user_backend::{VhostUserBackendMut, VhostUserDaemon, VringRwLock, VringT}; 25 use virtio_bindings::virtio_config::{VIRTIO_F_NOTIFY_ON_EMPTY, VIRTIO_F_VERSION_1}; 26 use virtio_bindings::virtio_net::*; 27 use vm_memory::{GuestAddressSpace, GuestMemoryAtomic}; 28 use vmm_sys_util::epoll::EventSet; 29 use vmm_sys_util::eventfd::EventFd; 30 31 type GuestMemoryMmap = vm_memory::GuestMemoryMmap<BitmapMmapRegion>; 32 33 pub type Result<T> = std::result::Result<T, Error>; 34 type VhostUserBackendResult<T> = std::result::Result<T, std::io::Error>; 35 36 #[derive(Debug)] 37 pub enum Error { 38 /// Failed to create kill eventfd. 39 CreateKillEventFd(io::Error), 40 /// Failed to parse configuration string. 41 FailedConfigParse(OptionParserError), 42 /// Failed to signal used queue. 43 FailedSignalingUsedQueue(io::Error), 44 /// Failed to handle event other than input event. 45 HandleEventNotEpollIn, 46 /// Failed to handle unknown event. 47 HandleEventUnknownEvent, 48 /// Failed to open tap device. 49 OpenTap(OpenTapError), 50 /// No socket provided. 51 SocketParameterMissing, 52 /// Underlying QueuePair error. 53 NetQueuePair(net_util::NetQueuePairError), 54 /// Failed to register the TAP listener. 55 RegisterTapListener(io::Error), 56 } 57 58 pub const SYNTAX: &str = "vhost-user-net backend parameters \ 59 \"ip=<ip_addr>,mask=<net_mask>,socket=<socket_path>,client=on|off,\ 60 num_queues=<number_of_queues>,queue_size=<size_of_each_queue>,tap=<if_name>\""; 61 62 impl fmt::Display for Error { 63 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 64 write!(f, "vhost_user_net_error: {self:?}") 65 } 66 } 67 68 impl std::error::Error for Error {} 69 70 impl std::convert::From<Error> for std::io::Error { 71 fn from(e: Error) -> Self { 72 std::io::Error::new(io::ErrorKind::Other, e) 73 } 74 } 75 76 struct VhostUserNetThread { 77 net: NetQueuePair, 78 kill_evt: EventFd, 79 } 80 81 impl VhostUserNetThread { 82 /// Create a new virtio network device with the given TAP interface. 83 fn new(tap: Tap) -> Result<Self> { 84 Ok(VhostUserNetThread { 85 kill_evt: EventFd::new(EFD_NONBLOCK).map_err(Error::CreateKillEventFd)?, 86 net: NetQueuePair { 87 tap_for_write_epoll: tap.clone(), 88 tap, 89 rx: RxVirtio::new(), 90 tx: TxVirtio::new(), 91 rx_tap_listening: false, 92 tx_tap_listening: false, 93 epoll_fd: None, 94 counters: NetCounters::default(), 95 tap_rx_event_id: 3, 96 tap_tx_event_id: 4, 97 rx_desc_avail: false, 98 rx_rate_limiter: None, 99 tx_rate_limiter: None, 100 access_platform: None, 101 }, 102 }) 103 } 104 105 pub fn set_epoll_fd(&mut self, fd: RawFd) { 106 self.net.epoll_fd = Some(fd); 107 } 108 } 109 110 pub struct VhostUserNetBackend { 111 threads: Vec<Mutex<VhostUserNetThread>>, 112 num_queues: usize, 113 queue_size: u16, 114 queues_per_thread: Vec<u64>, 115 mem: GuestMemoryAtomic<GuestMemoryMmap>, 116 } 117 118 impl VhostUserNetBackend { 119 #[allow(clippy::too_many_arguments)] 120 fn new( 121 ip_addr: Ipv4Addr, 122 host_mac: MacAddr, 123 netmask: Ipv4Addr, 124 mtu: Option<u16>, 125 num_queues: usize, 126 queue_size: u16, 127 ifname: Option<&str>, 128 mem: GuestMemoryAtomic<GuestMemoryMmap>, 129 ) -> Result<Self> { 130 let mut taps = open_tap( 131 ifname, 132 Some(ip_addr), 133 Some(netmask), 134 &mut Some(host_mac), 135 mtu, 136 num_queues / 2, 137 None, 138 ) 139 .map_err(Error::OpenTap)?; 140 141 let mut queues_per_thread = Vec::new(); 142 let mut threads = Vec::new(); 143 for (i, tap) in taps.drain(..).enumerate() { 144 let thread = Mutex::new(VhostUserNetThread::new(tap)?); 145 threads.push(thread); 146 queues_per_thread.push(0b11 << (i * 2)); 147 } 148 149 Ok(VhostUserNetBackend { 150 threads, 151 num_queues, 152 queue_size, 153 queues_per_thread, 154 mem, 155 }) 156 } 157 } 158 159 impl VhostUserBackendMut for VhostUserNetBackend { 160 type Bitmap = BitmapMmapRegion; 161 type Vring = VringRwLock<GuestMemoryAtomic<GuestMemoryMmap>>; 162 163 fn num_queues(&self) -> usize { 164 self.num_queues 165 } 166 167 fn max_queue_size(&self) -> usize { 168 self.queue_size as usize 169 } 170 171 fn features(&self) -> u64 { 172 (1 << VIRTIO_NET_F_GUEST_CSUM) 173 | (1 << VIRTIO_NET_F_CSUM) 174 | (1 << VIRTIO_NET_F_GUEST_TSO4) 175 | (1 << VIRTIO_NET_F_GUEST_TSO6) 176 | (1 << VIRTIO_NET_F_GUEST_ECN) 177 | (1 << VIRTIO_NET_F_GUEST_UFO) 178 | (1 << VIRTIO_NET_F_HOST_TSO4) 179 | (1 << VIRTIO_NET_F_HOST_TSO6) 180 | (1 << VIRTIO_NET_F_HOST_ECN) 181 | 1 << VIRTIO_NET_F_HOST_UFO 182 | 1 << VIRTIO_NET_F_CTRL_VQ 183 | 1 << VIRTIO_NET_F_MQ 184 | 1 << VIRTIO_NET_F_MAC 185 | 1 << VIRTIO_NET_F_MTU 186 | 1 << VIRTIO_F_NOTIFY_ON_EMPTY 187 | 1 << VIRTIO_F_VERSION_1 188 | VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits() 189 } 190 191 fn protocol_features(&self) -> VhostUserProtocolFeatures { 192 VhostUserProtocolFeatures::MQ 193 | VhostUserProtocolFeatures::REPLY_ACK 194 | VhostUserProtocolFeatures::CONFIGURE_MEM_SLOTS 195 } 196 197 fn set_event_idx(&mut self, _enabled: bool) {} 198 199 fn handle_event( 200 &mut self, 201 device_event: u16, 202 _evset: EventSet, 203 vrings: &[VringRwLock<GuestMemoryAtomic<GuestMemoryMmap>>], 204 thread_id: usize, 205 ) -> VhostUserBackendResult<()> { 206 let mut thread = self.threads[thread_id].lock().unwrap(); 207 match device_event { 208 0 => { 209 if !thread.net.rx_tap_listening { 210 net_util::register_listener( 211 thread.net.epoll_fd.unwrap(), 212 thread.net.tap.as_raw_fd(), 213 epoll::Events::EPOLLIN, 214 u64::from(thread.net.tap_rx_event_id), 215 ) 216 .map_err(Error::RegisterTapListener)?; 217 thread.net.rx_tap_listening = true; 218 } 219 } 220 1 | 4 => { 221 let mut vring = vrings[1].get_mut(); 222 if thread 223 .net 224 .process_tx(self.mem.memory().deref(), vring.get_queue_mut()) 225 .map_err(Error::NetQueuePair)? 226 { 227 vring 228 .signal_used_queue() 229 .map_err(Error::FailedSignalingUsedQueue)? 230 } 231 } 232 3 => { 233 let mut vring = vrings[0].get_mut(); 234 if thread 235 .net 236 .process_rx(self.mem.memory().deref(), vring.get_queue_mut()) 237 .map_err(Error::NetQueuePair)? 238 { 239 vring 240 .signal_used_queue() 241 .map_err(Error::FailedSignalingUsedQueue)? 242 } 243 } 244 _ => return Err(Error::HandleEventUnknownEvent.into()), 245 } 246 247 Ok(()) 248 } 249 250 fn exit_event(&self, thread_index: usize) -> Option<EventFd> { 251 Some( 252 self.threads[thread_index] 253 .lock() 254 .unwrap() 255 .kill_evt 256 .try_clone() 257 .unwrap(), 258 ) 259 } 260 261 fn queues_per_thread(&self) -> Vec<u64> { 262 self.queues_per_thread.clone() 263 } 264 265 fn update_memory( 266 &mut self, 267 _mem: GuestMemoryAtomic<GuestMemoryMmap>, 268 ) -> VhostUserBackendResult<()> { 269 Ok(()) 270 } 271 } 272 273 pub struct VhostUserNetBackendConfig { 274 pub ip: Ipv4Addr, 275 pub host_mac: MacAddr, 276 pub mask: Ipv4Addr, 277 pub mtu: Option<u16>, 278 pub socket: String, 279 pub num_queues: usize, 280 pub queue_size: u16, 281 pub tap: Option<String>, 282 pub client: bool, 283 } 284 285 impl VhostUserNetBackendConfig { 286 pub fn parse(backend: &str) -> Result<Self> { 287 let mut parser = OptionParser::new(); 288 289 parser 290 .add("tap") 291 .add("ip") 292 .add("host_mac") 293 .add("mask") 294 .add("mtu") 295 .add("queue_size") 296 .add("num_queues") 297 .add("socket") 298 .add("client"); 299 300 parser.parse(backend).map_err(Error::FailedConfigParse)?; 301 302 let tap = parser.get("tap"); 303 let ip = parser 304 .convert("ip") 305 .map_err(Error::FailedConfigParse)? 306 .unwrap_or_else(|| Ipv4Addr::new(192, 168, 100, 1)); 307 let host_mac = parser 308 .convert("host_mac") 309 .map_err(Error::FailedConfigParse)? 310 .unwrap_or_else(MacAddr::local_random); 311 let mask = parser 312 .convert("mask") 313 .map_err(Error::FailedConfigParse)? 314 .unwrap_or_else(|| Ipv4Addr::new(255, 255, 255, 0)); 315 let mtu = parser.convert("mtu").map_err(Error::FailedConfigParse)?; 316 let queue_size = parser 317 .convert("queue_size") 318 .map_err(Error::FailedConfigParse)? 319 .unwrap_or(256); 320 let num_queues = parser 321 .convert("num_queues") 322 .map_err(Error::FailedConfigParse)? 323 .unwrap_or(2); 324 let socket = parser.get("socket").ok_or(Error::SocketParameterMissing)?; 325 let client = parser 326 .convert::<Toggle>("client") 327 .map_err(Error::FailedConfigParse)? 328 .unwrap_or(Toggle(false)) 329 .0; 330 331 Ok(VhostUserNetBackendConfig { 332 ip, 333 host_mac, 334 mask, 335 mtu, 336 socket, 337 num_queues, 338 queue_size, 339 tap, 340 client, 341 }) 342 } 343 } 344 345 pub fn start_net_backend(backend_command: &str) { 346 let backend_config = match VhostUserNetBackendConfig::parse(backend_command) { 347 Ok(config) => config, 348 Err(e) => { 349 eprintln!("Failed parsing parameters {e:?}"); 350 process::exit(1); 351 } 352 }; 353 354 let tap = backend_config.tap.as_deref(); 355 356 let mem = GuestMemoryAtomic::new(GuestMemoryMmap::new()); 357 358 let net_backend = Arc::new(RwLock::new( 359 VhostUserNetBackend::new( 360 backend_config.ip, 361 backend_config.host_mac, 362 backend_config.mask, 363 backend_config.mtu, 364 backend_config.num_queues, 365 backend_config.queue_size, 366 tap, 367 mem.clone(), 368 ) 369 .unwrap(), 370 )); 371 372 let mut net_daemon = VhostUserDaemon::new( 373 "vhost-user-net-backend".to_string(), 374 net_backend.clone(), 375 mem, 376 ) 377 .unwrap(); 378 379 let epoll_handlers = net_daemon.get_epoll_handlers(); 380 if epoll_handlers.len() != net_backend.read().unwrap().threads.len() { 381 error!("Number of vring workers must be identical to the number of backend threads"); 382 process::exit(1); 383 } 384 385 for (index, thread) in net_backend.read().unwrap().threads.iter().enumerate() { 386 thread 387 .lock() 388 .unwrap() 389 .set_epoll_fd(epoll_handlers[index].as_raw_fd()); 390 } 391 392 if let Err(e) = if backend_config.client { 393 net_daemon.start_client(&backend_config.socket) 394 } else { 395 net_daemon.start(Listener::new(&backend_config.socket, true).unwrap()) 396 } { 397 error!( 398 "failed to start daemon for vhost-user-net with error: {:?}", 399 e 400 ); 401 process::exit(1); 402 } 403 404 if let Err(e) = net_daemon.wait() { 405 error!("Error from the main thread: {:?}", e); 406 } 407 408 for thread in net_backend.read().unwrap().threads.iter() { 409 if let Err(e) = thread.lock().unwrap().kill_evt.write(1) { 410 error!("Error shutting down worker thread: {:?}", e) 411 } 412 } 413 } 414