From 385d7348476c8ff0c6acb8fd923638744f270e91 Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Wed, 16 Dec 2020 16:56:44 +0000 Subject: [PATCH] net_util: Add API for creating a Tap from an fd Ultimately this will allow the creation of a virtio-net device that is backed by a file descriptor. This function ensures that the TAP device is correctly setup with offloading and non-blocking. Signed-off-by: Rob Bradford --- net_util/src/tap.rs | 53 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/net_util/src/tap.rs b/net_util/src/tap.rs index 892196030..69828f4c8 100644 --- a/net_util/src/tap.rs +++ b/net_util/src/tap.rs @@ -5,7 +5,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the THIRD-PARTY file. -use super::{create_sockaddr, create_socket, Error as NetUtilError, MacAddr}; +use super::{create_sockaddr, create_socket, vnet_hdr_len, Error as NetUtilError, MacAddr}; use mac::MAC_ADDR_LEN; use net_gen; use std::fs::File; @@ -153,6 +153,50 @@ impl Tap { Self::open_named("vmtap%d", num_queue_pairs, None) } + pub fn from_tap_fd(fd: RawFd) -> Result { + // Ensure that the file is opened non-blocking, this is particularly + // needed when opened via the shell for macvtap. + let ret = unsafe { + let mut flags = libc::fcntl(fd, libc::F_GETFL); + flags |= libc::O_NONBLOCK; + libc::fcntl(fd, libc::F_SETFL, flags) + }; + if ret < 0 { + return Err(Error::ConfigureTap(IoError::last_os_error())); + } + + let tap_file = unsafe { File::from_raw_fd(fd) }; + let mut ifreq: net_gen::ifreq = Default::default(); + + // Get current config including name + let ret = unsafe { ioctl_with_mut_ref(&tap_file, net_gen::TUNGETIFF(), &mut ifreq) }; + if ret < 0 { + return Err(Error::IoctlError(IoError::last_os_error())); + } + let if_name = unsafe { *ifreq.ifr_ifrn.ifrn_name.as_ref() }.to_vec(); + + // Try and update flags. Depending on how the tap was created (macvtap + // or via open_named()) this might return -EEXIST so we just ignore that. + unsafe { + let ifru_flags = ifreq.ifr_ifru.ifru_flags.as_mut(); + *ifru_flags = + (net_gen::IFF_TAP | net_gen::IFF_NO_PI | net_gen::IFF_VNET_HDR) as c_short; + } + let ret = unsafe { ioctl_with_mut_ref(&tap_file, net_gen::TUNSETIFF(), &mut ifreq) }; + if ret < 0 && IoError::last_os_error().raw_os_error().unwrap() != libc::EEXIST { + return Err(Error::ConfigureTap(IoError::last_os_error())); + } + + let tap = Tap { if_name, tap_file }; + let offload_flags = + net_gen::TUN_F_CSUM | net_gen::TUN_F_UFO | net_gen::TUN_F_TSO4 | net_gen::TUN_F_TSO6; + let vnet_hdr_size = vnet_hdr_len() as i32; + tap.set_offload(offload_flags)?; + tap.set_vnet_hdr_size(vnet_hdr_size)?; + + Ok(tap) + } + /// Set the host-side IP address for the tap interface. pub fn set_ip_addr(&self, ip_addr: net::Ipv4Addr) -> Result<()> { let sock = create_socket().map_err(Error::NetUtil)?; @@ -539,6 +583,13 @@ mod tests { println!("created tap: {:?}", t); } + #[test] + fn test_tap_from_fd() { + let orig_tap = Tap::new(1).unwrap(); + let fd = orig_tap.as_raw_fd(); + let _new_tap = Tap::from_tap_fd(fd).unwrap(); + } + #[test] fn test_tap_configure() { // This should be the first thing to be called inside the function, so everything else