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 libc::EFD_NONBLOCK; 10 use log::*; 11 use net_util::{ 12 open_tap, MacAddr, NetCounters, NetQueuePair, OpenTapError, RxVirtio, Tap, TxVirtio, 13 }; 14 use option_parser::Toggle; 15 use option_parser::{OptionParser, OptionParserError}; 16 use std::fmt; 17 use std::io; 18 use std::net::Ipv4Addr; 19 use std::ops::Deref; 20 use std::os::unix::io::{AsRawFd, RawFd}; 21 use std::process; 22 use std::sync::{Arc, Mutex, RwLock}; 23 use vhost::vhost_user::message::*; 24 use vhost::vhost_user::Listener; 25 use vhost_user_backend::bitmap::BitmapMmapRegion; 26 use vhost_user_backend::{VhostUserBackendMut, VhostUserDaemon, VringRwLock, VringT}; 27 use virtio_bindings::virtio_config::{VIRTIO_F_NOTIFY_ON_EMPTY, VIRTIO_F_VERSION_1}; 28 use virtio_bindings::virtio_net::*; 29 use vm_memory::GuestAddressSpace; 30 use vm_memory::GuestMemoryAtomic; 31 use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; 32 33 type GuestMemoryMmap = vm_memory::GuestMemoryMmap<BitmapMmapRegion>; 34 35 pub type Result<T> = std::result::Result<T, Error>; 36 type VhostUserBackendResult<T> = std::result::Result<T, std::io::Error>; 37 38 #[derive(Debug)] 39 pub enum Error { 40 /// Failed to create kill eventfd. 41 CreateKillEventFd(io::Error), 42 /// Failed to parse configuration string. 43 FailedConfigParse(OptionParserError), 44 /// Failed to signal used queue. 45 FailedSignalingUsedQueue(io::Error), 46 /// Failed to handle event other than input event. 47 HandleEventNotEpollIn, 48 /// Failed to handle unknown event. 49 HandleEventUnknownEvent, 50 /// Failed to open tap device. 51 OpenTap(OpenTapError), 52 /// No socket provided. 53 SocketParameterMissing, 54 /// Underlying QueuePair error. 55 NetQueuePair(net_util::NetQueuePairError), 56 /// Failed to register the TAP listener. 57 RegisterTapListener(io::Error), 58 } 59 60 pub const SYNTAX: &str = "vhost-user-net backend parameters \ 61 \"ip=<ip_addr>,mask=<net_mask>,socket=<socket_path>,client=on|off,\ 62 num_queues=<number_of_queues>,queue_size=<size_of_each_queue>,tap=<if_name>\""; 63 64 impl fmt::Display for Error { 65 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 66 write!(f, "vhost_user_net_error: {self:?}") 67 } 68 } 69 70 impl std::error::Error for Error {} 71 72 impl std::convert::From<Error> for std::io::Error { 73 fn from(e: Error) -> Self { 74 std::io::Error::new(io::ErrorKind::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: Ipv4Addr, 124 host_mac: MacAddr, 125 netmask: Ipv4Addr, 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: Ipv4Addr, 277 pub host_mac: MacAddr, 278 pub mask: Ipv4Addr, 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(|| 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(|| 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