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