From 3e807a19b7d6e07e7d3ec42b817204ec8e1cca95 Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Tue, 7 Jul 2020 15:02:18 +0100 Subject: [PATCH] net_util, virtio-devices, vhost_user_net: Relocate code for opening TAP By moving the code for opening the TAP device into a shared location we are starting to remove the requirement for the vhost-user-net backend to depend on the virtio-devices crate which in of itself depends on many other crates that are not necessary for the backend to function. Signed-off-by: Rob Bradford --- Cargo.lock | 1 + net_util/Cargo.toml | 4 +- net_util/src/lib.rs | 6 +- net_util/src/open_tap.rs | 124 +++++++++++++++++++++++++++++++++ vhost_user_net/src/lib.rs | 6 +- virtio-devices/src/net.rs | 9 +-- virtio-devices/src/net_util.rs | 109 +---------------------------- 7 files changed, 140 insertions(+), 119 deletions(-) create mode 100644 net_util/src/open_tap.rs diff --git a/Cargo.lock b/Cargo.lock index cfa6a36a0..9532b18a7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -638,6 +638,7 @@ dependencies = [ "rand 0.7.3", "serde", "serde_json", + "virtio-bindings", "vmm-sys-util", ] diff --git a/net_util/Cargo.toml b/net_util/Cargo.toml index 46ee64f44..2e71fc52f 100644 --- a/net_util/Cargo.toml +++ b/net_util/Cargo.toml @@ -5,12 +5,12 @@ authors = ["The Chromium OS Authors"] [dependencies] libc = "0.2.72" +net_gen = { path = "../net_gen" } rand = "0.7.3" serde = "1.0.114" +virtio-bindings = "0.1.0" vmm-sys-util = ">=0.3.1" -net_gen = { path = "../net_gen" } - [dev-dependencies] lazy_static = "1.3.0" pnet = "0.26.0" diff --git a/net_util/src/lib.rs b/net_util/src/lib.rs index 4c11592ac..943c3d5e7 100644 --- a/net_util/src/lib.rs +++ b/net_util/src/lib.rs @@ -11,13 +11,14 @@ #[macro_use] extern crate lazy_static; extern crate libc; +extern crate net_gen; extern crate rand; extern crate serde; - -extern crate net_gen; +extern crate virtio_bindings; extern crate vmm_sys_util; mod mac; +mod open_tap; mod tap; use std::io::Error as IoError; @@ -26,6 +27,7 @@ use std::net; use std::os::unix::io::FromRawFd; pub use mac::{MacAddr, MAC_ADDR_LEN}; +pub use open_tap::{open_tap, Error as OpenTapError}; pub use tap::{Error as TapError, Tap}; #[derive(Debug)] diff --git a/net_util/src/open_tap.rs b/net_util/src/open_tap.rs new file mode 100644 index 000000000..2360c8cdc --- /dev/null +++ b/net_util/src/open_tap.rs @@ -0,0 +1,124 @@ +// Copyright (c) 2020 Intel Corporation. All rights reserved. +// +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause + +use super::TapError; +use super::{MacAddr, Tap}; +use std::net::Ipv4Addr; +use std::path::Path; +use std::{fs, io}; +use virtio_bindings::bindings::virtio_net::virtio_net_hdr_v1; + +#[derive(Debug)] +pub enum Error { + /// Failed to convert an hexadecimal string into an integer. + ConvertHexStringToInt(std::num::ParseIntError), + /// Error related to the multiqueue support. + MultiQueueSupport(String), + /// Failed to read the TAP flags from sysfs. + ReadSysfsTunFlags(io::Error), + /// Open tap device failed. + TapOpen(TapError), + /// Setting tap IP failed. + TapSetIp(TapError), + /// Setting tap netmask failed. + TapSetNetmask(TapError), + /// Setting MAC address failed + TapSetMac(TapError), + /// Getting MAC address failed + TapGetMac(TapError), + /// Setting tap interface offload flags failed. + TapSetOffload(TapError), + /// Setting vnet header size failed. + TapSetVnetHdrSize(TapError), + /// Enabling tap interface failed. + TapEnable(TapError), +} + +type Result = std::result::Result; + +fn vnet_hdr_len() -> usize { + std::mem::size_of::() +} + +fn check_mq_support(if_name: &Option<&str>, queue_pairs: usize) -> Result<()> { + if let Some(tap_name) = if_name { + let mq = queue_pairs > 1; + let path = format!("/sys/class/net/{}/tun_flags", tap_name); + // interface does not exist, check is not required + if !Path::new(&path).exists() { + return Ok(()); + } + let tun_flags_str = fs::read_to_string(path).map_err(Error::ReadSysfsTunFlags)?; + let tun_flags = u32::from_str_radix(tun_flags_str.trim().trim_start_matches("0x"), 16) + .map_err(Error::ConvertHexStringToInt)?; + if (tun_flags & net_gen::IFF_MULTI_QUEUE != 0) && !mq { + return Err(Error::MultiQueueSupport(String::from( + "TAP interface supports MQ while device does not", + ))); + } else if (tun_flags & net_gen::IFF_MULTI_QUEUE == 0) && mq { + return Err(Error::MultiQueueSupport(String::from( + "Device supports MQ while TAP interface does not", + ))); + } + } + Ok(()) +} + +/// Create a new virtio network device with the given IP address and +/// netmask. +pub fn open_tap( + if_name: Option<&str>, + ip_addr: Option, + netmask: Option, + host_mac: &mut Option, + num_rx_q: usize, +) -> Result> { + let mut taps: Vec = Vec::new(); + let mut ifname: String = String::new(); + let vnet_hdr_size = vnet_hdr_len() as i32; + let flag = net_gen::TUN_F_CSUM | net_gen::TUN_F_UFO | net_gen::TUN_F_TSO4 | net_gen::TUN_F_TSO6; + + // In case the tap interface already exists, check if the number of + // queues is appropriate. The tap might not support multiqueue while + // the number of queues indicates the user expects multiple queues, or + // on the contrary, the tap might support multiqueue while the number + // of queues indicates the user doesn't expect multiple queues. + check_mq_support(&if_name, num_rx_q)?; + + for i in 0..num_rx_q { + let tap: Tap; + if i == 0 { + tap = match if_name { + Some(name) => Tap::open_named(name, num_rx_q).map_err(Error::TapOpen)?, + None => Tap::new(num_rx_q).map_err(Error::TapOpen)?, + }; + if let Some(ip) = ip_addr { + tap.set_ip_addr(ip).map_err(Error::TapSetIp)?; + } + if let Some(mask) = netmask { + tap.set_netmask(mask).map_err(Error::TapSetNetmask)?; + } + if let Some(mac) = host_mac { + tap.set_mac_addr(*mac).map_err(Error::TapSetMac)? + } else { + *host_mac = Some(tap.get_mac_addr().map_err(Error::TapGetMac)?) + } + tap.enable().map_err(Error::TapEnable)?; + tap.set_offload(flag).map_err(Error::TapSetOffload)?; + + tap.set_vnet_hdr_size(vnet_hdr_size) + .map_err(Error::TapSetVnetHdrSize)?; + + ifname = String::from_utf8(tap.get_if_name()).unwrap(); + } else { + tap = Tap::open_named(ifname.as_str(), num_rx_q).map_err(Error::TapOpen)?; + tap.set_offload(flag).map_err(Error::TapSetOffload)?; + + tap.set_vnet_hdr_size(vnet_hdr_size) + .map_err(Error::TapSetVnetHdrSize)?; + } + taps.push(tap); + } + Ok(taps) +} diff --git a/vhost_user_net/src/lib.rs b/vhost_user_net/src/lib.rs index f58eabd85..01662305f 100644 --- a/vhost_user_net/src/lib.rs +++ b/vhost_user_net/src/lib.rs @@ -14,7 +14,7 @@ extern crate virtio_devices; use libc::{self, EFD_NONBLOCK}; use log::*; -use net_util::{MacAddr, Tap}; +use net_util::{open_tap, MacAddr, OpenTapError, Tap}; use option_parser::{OptionParser, OptionParserError}; use std::fmt; use std::io::{self}; @@ -28,7 +28,7 @@ use vhost_rs::vhost_user::{Error as VhostUserError, Listener}; use vhost_user_backend::{VhostUserBackend, VhostUserDaemon, Vring, VringWorker}; use virtio_bindings::bindings::virtio_net::*; use virtio_bindings::bindings::virtio_ring::VIRTIO_RING_F_EVENT_IDX; -use virtio_devices::net_util::{open_tap, RxVirtio, TxVirtio}; +use virtio_devices::net_util::{RxVirtio, TxVirtio}; use virtio_devices::{NetCounters, NetQueuePair}; use vm_memory::{GuestMemoryAtomic, GuestMemoryMmap}; use vmm_sys_util::eventfd::EventFd; @@ -66,7 +66,7 @@ pub enum Error { /// No memory configured. NoMemoryConfigured, /// Open tap device failed. - OpenTap(virtio_devices::net_util::Error), + OpenTap(OpenTapError), /// No socket provided SocketParameterMissing, /// Underlying QueuePair error diff --git a/virtio-devices/src/net.rs b/virtio-devices/src/net.rs index 529f8cb21..1cd6dcffa 100644 --- a/virtio-devices/src/net.rs +++ b/virtio-devices/src/net.rs @@ -6,9 +6,9 @@ // found in the THIRD-PARTY file. use super::net_util::{ - build_net_config_space, build_net_config_space_with_mq, open_tap, register_listener, - unregister_listener, CtrlVirtio, NetCtrlEpollHandler, RxVirtio, TxVirtio, VirtioNetConfig, - KILL_EVENT, NET_EVENTS_COUNT, PAUSE_EVENT, RX_QUEUE_EVENT, RX_TAP_EVENT, TX_QUEUE_EVENT, + build_net_config_space, build_net_config_space_with_mq, register_listener, unregister_listener, + CtrlVirtio, NetCtrlEpollHandler, RxVirtio, TxVirtio, VirtioNetConfig, KILL_EVENT, + NET_EVENTS_COUNT, PAUSE_EVENT, RX_QUEUE_EVENT, RX_TAP_EVENT, TX_QUEUE_EVENT, }; use super::Error as DeviceError; use super::{ @@ -18,6 +18,7 @@ use crate::VirtioInterrupt; use anyhow::anyhow; use libc::EAGAIN; use libc::EFD_NONBLOCK; +use net_util::{open_tap, OpenTapError}; use net_util::{MacAddr, Tap}; use std::cmp; use std::collections::HashMap; @@ -44,7 +45,7 @@ use vmm_sys_util::eventfd::EventFd; #[derive(Debug)] pub enum Error { /// Failed to open taps. - OpenTap(super::net_util::Error), + OpenTap(OpenTapError), } pub type Result = result::Result; diff --git a/virtio-devices/src/net_util.rs b/virtio-devices/src/net_util.rs index 3f25ad73e..167692726 100644 --- a/virtio-devices/src/net_util.rs +++ b/virtio-devices/src/net_util.rs @@ -4,17 +4,14 @@ use super::Error as DeviceError; use super::{DescriptorChain, DeviceEventT, Queue}; -use net_util::{MacAddr, Tap, TapError}; +use net_util::{MacAddr, Tap}; use serde::ser::{Serialize, SerializeStruct, Serializer}; use std::cmp; -use std::fs; use std::fs::File; use std::io::{self, Write}; use std::mem; -use std::net::Ipv4Addr; use std::num::Wrapping; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; -use std::path::Path; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use std::thread; @@ -94,8 +91,6 @@ unsafe impl ByteValued for VirtioNetConfig {} #[derive(Debug)] pub enum Error { - /// Failed to convert an hexadecimal string into an integer. - ConvertHexStringToInt(std::num::ParseIntError), /// Read process MQ. FailedProcessMQ, /// Read queue failed. @@ -108,30 +103,10 @@ pub enum Error { InvalidDesc, /// Invalid queue pairs number InvalidQueuePairsNum, - /// Error related to the multiqueue support. - MultiQueueSupport(String), /// No memory passed in. NoMemory, /// No ueue pairs nummber. NoQueuePairsNum, - /// Failed to read the TAP flags from sysfs. - ReadSysfsTunFlags(io::Error), - /// Open tap device failed. - TapOpen(TapError), - /// Setting tap IP failed. - TapSetIp(TapError), - /// Setting tap netmask failed. - TapSetNetmask(TapError), - /// Setting MAC address failed - TapSetMac(TapError), - /// Getting MAC address failed - TapGetMac(TapError), - /// Setting tap interface offload flags failed. - TapSetOffload(TapError), - /// Setting vnet header size failed. - TapSetVnetHdrSize(TapError), - /// Enabling tap interface failed. - TapEnable(TapError), } pub struct CtrlVirtio { @@ -538,85 +513,3 @@ pub fn build_net_config_space_with_mq( fn vnet_hdr_len() -> usize { mem::size_of::() } - -fn check_mq_support(if_name: &Option<&str>, queue_pairs: usize) -> Result<()> { - if let Some(tap_name) = if_name { - let mq = queue_pairs > 1; - let path = format!("/sys/class/net/{}/tun_flags", tap_name); - // interface does not exist, check is not required - if !Path::new(&path).exists() { - return Ok(()); - } - let tun_flags_str = fs::read_to_string(path).map_err(Error::ReadSysfsTunFlags)?; - let tun_flags = u32::from_str_radix(tun_flags_str.trim().trim_start_matches("0x"), 16) - .map_err(Error::ConvertHexStringToInt)?; - if (tun_flags & net_gen::IFF_MULTI_QUEUE != 0) && !mq { - return Err(Error::MultiQueueSupport(String::from( - "TAP interface supports MQ while device does not", - ))); - } else if (tun_flags & net_gen::IFF_MULTI_QUEUE == 0) && mq { - return Err(Error::MultiQueueSupport(String::from( - "Device supports MQ while TAP interface does not", - ))); - } - } - Ok(()) -} - -/// Create a new virtio network device with the given IP address and -/// netmask. -pub fn open_tap( - if_name: Option<&str>, - ip_addr: Option, - netmask: Option, - host_mac: &mut Option, - num_rx_q: usize, -) -> Result> { - let mut taps: Vec = Vec::new(); - let mut ifname: String = String::new(); - let vnet_hdr_size = vnet_hdr_len() as i32; - let flag = net_gen::TUN_F_CSUM | net_gen::TUN_F_UFO | net_gen::TUN_F_TSO4 | net_gen::TUN_F_TSO6; - - // In case the tap interface already exists, check if the number of - // queues is appropriate. The tap might not support multiqueue while - // the number of queues indicates the user expects multiple queues, or - // on the contrary, the tap might support multiqueue while the number - // of queues indicates the user doesn't expect multiple queues. - check_mq_support(&if_name, num_rx_q)?; - - for i in 0..num_rx_q { - let tap: Tap; - if i == 0 { - tap = match if_name { - Some(name) => Tap::open_named(name, num_rx_q).map_err(Error::TapOpen)?, - None => Tap::new(num_rx_q).map_err(Error::TapOpen)?, - }; - if let Some(ip) = ip_addr { - tap.set_ip_addr(ip).map_err(Error::TapSetIp)?; - } - if let Some(mask) = netmask { - tap.set_netmask(mask).map_err(Error::TapSetNetmask)?; - } - if let Some(mac) = host_mac { - tap.set_mac_addr(*mac).map_err(Error::TapSetMac)? - } else { - *host_mac = Some(tap.get_mac_addr().map_err(Error::TapGetMac)?) - } - tap.enable().map_err(Error::TapEnable)?; - tap.set_offload(flag).map_err(Error::TapSetOffload)?; - - tap.set_vnet_hdr_size(vnet_hdr_size) - .map_err(Error::TapSetVnetHdrSize)?; - - ifname = String::from_utf8(tap.get_if_name()).unwrap(); - } else { - tap = Tap::open_named(ifname.as_str(), num_rx_q).map_err(Error::TapOpen)?; - tap.set_offload(flag).map_err(Error::TapSetOffload)?; - - tap.set_vnet_hdr_size(vnet_hdr_size) - .map_err(Error::TapSetVnetHdrSize)?; - } - taps.push(tap); - } - Ok(taps) -}