diff --git a/devices/src/virtio/net.rs b/devices/src/virtio/net.rs index 1ab972457b..41bdf06a0e 100644 --- a/devices/src/virtio/net.rs +++ b/devices/src/virtio/net.rs @@ -582,7 +582,7 @@ where // Ensure that the tap interface has the correct flags and sets the offload and VNET header size // to the appropriate values. pub fn validate_and_configure_tap(tap: &T, vq_pairs: u16) -> Result<(), NetError> { - let flags = tap.if_flags(); + let flags = tap.if_flags() as i32; let mut required_flags = vec![ (libc::IFF_TAP, "IFF_TAP"), (libc::IFF_NO_PI, "IFF_NO_PI"), diff --git a/devices/src/virtio/vhost/net.rs b/devices/src/virtio/vhost/net.rs index 9fbdde554a..a56a75b593 100644 --- a/devices/src/virtio/vhost/net.rs +++ b/devices/src/virtio/vhost/net.rs @@ -357,7 +357,7 @@ pub mod tests { use crate::virtio::VIRTIO_MSI_NO_VECTOR; use crate::IrqLevelEvent; use hypervisor::ProtectionType; - use net_util::fakes::FakeTap; + use net_util::sys::unix::fakes::FakeTap; use std::path::PathBuf; use std::result; use std::sync::atomic::AtomicUsize; diff --git a/devices/src/virtio/vhost/user/device/unix/net.rs b/devices/src/virtio/vhost/user/device/unix/net.rs index 8df374d451..c12630c75b 100644 --- a/devices/src/virtio/vhost/user/device/unix/net.rs +++ b/devices/src/virtio/vhost/user/device/unix/net.rs @@ -15,7 +15,7 @@ use cros_async::{EventAsync, Executor, IntoAsync, IoSourceExt}; use futures::future::{AbortHandle, Abortable}; use hypervisor::ProtectionType; use net_util::TapT; -use net_util::{MacAddress, Tap}; +use net_util::{sys::unix::Tap, MacAddress}; use sync::Mutex; use virtio_sys::virtio_net; use vm_memory::GuestMemory; diff --git a/net_sys/src/lib.rs b/net_sys/src/lib.rs index 13515a1413..7fa27348c4 100644 --- a/net_sys/src/lib.rs +++ b/net_sys/src/lib.rs @@ -16,6 +16,12 @@ pub use crate::iff::{ifreq, net_device_flags}; pub const TUNTAP: ::std::os::raw::c_uint = 84; +// Windows doesn't have these constants in libc. As such, we preserve them here +// for use on that platform, and for the sake of simplicity, CrosVM code +// uses these constants on all platforms. +pub type sa_family_t = ::std::os::raw::c_ushort; +pub const ARPHRD_ETHER: sa_family_t = 1; + ioctl_iow_nr!(TUNSETNOCSUM, TUNTAP, 200, ::std::os::raw::c_int); ioctl_iow_nr!(TUNSETDEBUG, TUNTAP, 201, ::std::os::raw::c_int); ioctl_iow_nr!(TUNSETIFF, TUNTAP, 202, ::std::os::raw::c_int); diff --git a/net_util/Cargo.toml b/net_util/Cargo.toml index 972500edf4..07a37dfd3d 100644 --- a/net_util/Cargo.toml +++ b/net_util/Cargo.toml @@ -5,10 +5,13 @@ authors = ["The Chromium OS Authors"] edition = "2021" [dependencies] +cfg-if = "1.0.0" libc = "*" data_model = { path = "../common/data_model" } net_sys = { path = "../net_sys" } base = { path = "../base" } cros_async = { path = "../cros_async" } remain = "*" +serde = { version = "1", features = [ "derive" ] } thiserror = "*" + diff --git a/net_util/src/lib.rs b/net_util/src/lib.rs index 42e4658985..0de8259ad6 100644 --- a/net_util/src/lib.rs +++ b/net_util/src/lib.rs @@ -2,26 +2,19 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +pub mod sys; use std::fmt::{self, Display}; -use std::fs::File; -use std::io::{Read, Result as IoResult, Write}; -use std::mem; +use std::io::{Read, Write}; use std::net; use std::num::ParseIntError; use std::os::raw::*; -use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use std::str::FromStr; - -use libc::EPERM; +pub use sys::TapT; use base::Error as SysError; -use base::FileReadWriteVolatile; -use base::{ - ioctl_with_mut_ref, ioctl_with_ref, ioctl_with_val, volatile_impl, AsRawDescriptor, - FromRawDescriptor, IoctlNr, RawDescriptor, -}; -use cros_async::IntoAsync; +use base::{AsRawDescriptor, RawDescriptor}; use remain::sorted; +use serde::{Deserialize, Serialize}; use thiserror::Error as ThisError; #[sorted] @@ -43,6 +36,7 @@ pub enum Error { #[error("failed to open /dev/net/tun: {0}")] OpenTun(SysError), } + pub type Result = std::result::Result; impl Error { @@ -57,41 +51,6 @@ impl Error { } } -/// Create a sockaddr_in from an IPv4 address, and expose it as -/// an opaque sockaddr suitable for usage by socket ioctls. -fn create_sockaddr(ip_addr: net::Ipv4Addr) -> libc::sockaddr { - // IPv4 addresses big-endian (network order), but Ipv4Addr will give us - // a view of those bytes directly so we can avoid any endian trickiness. - let addr_in = libc::sockaddr_in { - sin_family: libc::AF_INET as u16, - sin_port: 0, - sin_addr: unsafe { mem::transmute(ip_addr.octets()) }, - sin_zero: [0; 8usize], - }; - - unsafe { mem::transmute(addr_in) } -} - -/// Extract the IPv4 address from a sockaddr. Assumes the sockaddr is a sockaddr_in. -fn read_ipv4_addr(addr: &libc::sockaddr) -> net::Ipv4Addr { - debug_assert_eq!(addr.sa_family as libc::c_int, libc::AF_INET); - // This is safe because sockaddr and sockaddr_in are the same size, and we've checked that - // this address is AF_INET. - let in_addr: libc::sockaddr_in = unsafe { mem::transmute(*addr) }; - net::Ipv4Addr::from(in_addr.sin_addr.s_addr) -} - -fn create_socket() -> Result { - // This is safe since we check the return value. - let sock = unsafe { libc::socket(libc::AF_INET, libc::SOCK_DGRAM, 0) }; - if sock < 0 { - return Err(Error::CreateSocket(SysError::last())); - } - - // This is safe; nothing else will use or hold onto the raw sock fd. - Ok(unsafe { net::UdpSocket::from_raw_fd(sock) }) -} - #[sorted] #[derive(ThisError, Debug)] pub enum MacAddressError { @@ -105,9 +64,9 @@ pub enum MacAddressError { /// An Ethernet mac address. This struct is compatible with the C `struct sockaddr`. #[repr(C)] -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Serialize, Deserialize)] pub struct MacAddress { - family: libc::sa_family_t, + family: net_sys::sa_family_t, addr: [u8; 6usize], __pad: [u8; 8usize], } @@ -128,7 +87,7 @@ impl FromStr for MacAddress { } let mut result = MacAddress { - family: libc::ARPHRD_ETHER, + family: net_sys::ARPHRD_ETHER, addr: [0; 6usize], __pad: [0; 8usize], }; @@ -151,58 +110,7 @@ impl Display for MacAddress { } } -/// Handle for a network tap interface. -/// -/// For now, this simply wraps the file descriptor for the tap device so methods -/// can run ioctls on the interface. The tap interface fd will be closed when -/// Tap goes out of scope, and the kernel will clean up the interface -/// automatically. -#[derive(Debug)] -pub struct Tap { - tap_file: File, - if_name: [c_char; 16usize], - if_flags: ::std::os::raw::c_short, -} - -impl Tap { - pub fn create_tap_with_ifreq(ifreq: &mut net_sys::ifreq) -> Result { - // Open calls are safe because we give a constant nul-terminated - // string and verify the result. - let fd = unsafe { - libc::open( - b"/dev/net/tun\0".as_ptr() as *const c_char, - libc::O_RDWR | libc::O_NONBLOCK | libc::O_CLOEXEC, - ) - }; - if fd < 0 { - return Err(Error::OpenTun(SysError::last())); - } - - // We just checked that the fd is valid. - let tuntap = unsafe { File::from_raw_descriptor(fd) }; - // ioctl is safe since we call it with a valid tap fd and check the return - // value. - let ret = unsafe { ioctl_with_mut_ref(&tuntap, net_sys::TUNSETIFF(), ifreq) }; - - if ret < 0 { - let error = SysError::last(); - - // In a non-root, test environment, we won't have permission to call this; allow - if !(cfg!(test) && error.errno() == EPERM) { - return Err(Error::CreateTap(error)); - } - } - - // Safe since only the name is accessed, and it's copied out. - Ok(Tap { - tap_file: tuntap, - if_name: unsafe { ifreq.ifr_ifrn.ifrn_name }, - if_flags: unsafe { ifreq.ifr_ifru.ifru_flags }, - }) - } -} - -pub trait TapT: FileReadWriteVolatile + Read + Write + AsRawDescriptor + Send + Sized { +pub trait TapTCommon: Read + Write + AsRawDescriptor + Send + Sized { /// Create a new tap interface named `name`, or open it if it already exists with the same /// parameters. /// @@ -211,16 +119,10 @@ pub trait TapT: FileReadWriteVolatile + Read + Write + AsRawDescriptor + Send + /// Set 'multi_vq' to true, if tap have multi virt queue pairs fn new_with_name(name: &[u8], vnet_hdr: bool, multi_vq: bool) -> Result; - /// Create a new tap interface. - /// - /// Set the `vnet_hdr` flag to true to allow offloading on this tap, + /// Create a new tap interface. Set the `vnet_hdr` flag to true to allow offloading on this tap, /// which will add an extra 12 byte virtio net header to incoming frames. Offloading cannot - /// be used if `vnet_hdr` is false. - /// Set 'multi_vq' to true, if tap have multi virt queue pairs - fn new(vnet_hdr: bool, multi_vq: bool) -> Result { - const TUNTAP_DEV_FORMAT: &[u8] = b"vmtap%d"; - Self::new_with_name(TUNTAP_DEV_FORMAT, vnet_hdr, multi_vq) - } + /// be used if `vnet_hdr` is false. Set 'multi_vq' to true if tap has multi virt queue pairs. + fn new(vnet_hdr: bool, multi_vq: bool) -> Result; /// Change the origin tap into multiqueue taps, this means create other taps based on the /// origin tap. @@ -267,516 +169,6 @@ pub trait TapT: FileReadWriteVolatile + Read + Write + AsRawDescriptor + Send + /// Try to clone fn try_clone(&self) -> Result; - /// Convert raw descriptor to TapT. + /// Convert raw descriptor to unsafe fn from_raw_descriptor(descriptor: RawDescriptor) -> Result; } - -impl TapT for Tap { - fn new_with_name(name: &[u8], vnet_hdr: bool, multi_vq: bool) -> Result { - // This is pretty messy because of the unions used by ifreq. Since we - // don't call as_mut on the same union field more than once, this block - // is safe. - let mut ifreq: net_sys::ifreq = Default::default(); - unsafe { - let ifrn_name = ifreq.ifr_ifrn.ifrn_name.as_mut(); - for (dst, src) in ifrn_name - .iter_mut() - // Add a zero terminator to the source string. - .zip(name.iter().chain(std::iter::once(&0))) - { - *dst = *src as c_char; - } - ifreq.ifr_ifru.ifru_flags = - (libc::IFF_TAP | libc::IFF_NO_PI | if vnet_hdr { libc::IFF_VNET_HDR } else { 0 }) - as c_short; - if multi_vq { - ifreq.ifr_ifru.ifru_flags |= libc::IFF_MULTI_QUEUE as c_short; - } - } - - Tap::create_tap_with_ifreq(&mut ifreq) - } - - fn into_mq_taps(self, vq_pairs: u16) -> Result> { - let mut taps: Vec = Vec::new(); - - if vq_pairs <= 1 { - taps.push(self); - return Ok(taps); - } - - // Add other socket into the origin tap interface - for _ in 0..vq_pairs - 1 { - let mut ifreq = self.get_ifreq(); - let tap = Tap::create_tap_with_ifreq(&mut ifreq)?; - - tap.enable()?; - - taps.push(tap); - } - - taps.insert(0, self); - Ok(taps) - } - - fn ip_addr(&self) -> Result { - let sock = create_socket()?; - let mut ifreq = self.get_ifreq(); - - // ioctl is safe. Called with a valid sock fd, and we check the return. - let ret = unsafe { - ioctl_with_mut_ref(&sock, net_sys::sockios::SIOCGIFADDR as IoctlNr, &mut ifreq) - }; - if ret < 0 { - return Err(Error::IoctlError(SysError::last())); - } - - // We only access one field of the ifru union, hence this is safe. - let addr = unsafe { ifreq.ifr_ifru.ifru_addr }; - - Ok(read_ipv4_addr(&addr)) - } - - fn set_ip_addr(&self, ip_addr: net::Ipv4Addr) -> Result<()> { - let sock = create_socket()?; - let addr = create_sockaddr(ip_addr); - - let mut ifreq = self.get_ifreq(); - ifreq.ifr_ifru.ifru_addr = addr; - - // ioctl is safe. Called with a valid sock fd, and we check the return. - let ret = - unsafe { ioctl_with_ref(&sock, net_sys::sockios::SIOCSIFADDR as IoctlNr, &ifreq) }; - if ret < 0 { - return Err(Error::IoctlError(SysError::last())); - } - - Ok(()) - } - - fn netmask(&self) -> Result { - let sock = create_socket()?; - let mut ifreq = self.get_ifreq(); - - // ioctl is safe. Called with a valid sock fd, and we check the return. - let ret = unsafe { - ioctl_with_mut_ref( - &sock, - net_sys::sockios::SIOCGIFNETMASK as IoctlNr, - &mut ifreq, - ) - }; - if ret < 0 { - return Err(Error::IoctlError(SysError::last())); - } - - // We only access one field of the ifru union, hence this is safe. - let addr = unsafe { ifreq.ifr_ifru.ifru_netmask }; - - Ok(read_ipv4_addr(&addr)) - } - - fn set_netmask(&self, netmask: net::Ipv4Addr) -> Result<()> { - let sock = create_socket()?; - let addr = create_sockaddr(netmask); - - let mut ifreq = self.get_ifreq(); - ifreq.ifr_ifru.ifru_netmask = addr; - - // ioctl is safe. Called with a valid sock fd, and we check the return. - let ret = - unsafe { ioctl_with_ref(&sock, net_sys::sockios::SIOCSIFNETMASK as IoctlNr, &ifreq) }; - if ret < 0 { - return Err(Error::IoctlError(SysError::last())); - } - - Ok(()) - } - - fn mtu(&self) -> Result { - let sock = create_socket()?; - let mut ifreq = self.get_ifreq(); - - // ioctl is safe. Called with a valid sock fd, and we check the return. - let ret = unsafe { - ioctl_with_mut_ref(&sock, net_sys::sockios::SIOCGIFMTU as IoctlNr, &mut ifreq) - }; - if ret < 0 { - return Err(Error::IoctlError(SysError::last())); - } - - // We only access one field of the ifru union, hence this is safe. - let mtu = unsafe { ifreq.ifr_ifru.ifru_mtu } as u16; - Ok(mtu) - } - - fn set_mtu(&self, mtu: u16) -> Result<()> { - let sock = create_socket()?; - - let mut ifreq = self.get_ifreq(); - ifreq.ifr_ifru.ifru_mtu = i32::from(mtu); - - // ioctl is safe. Called with a valid sock fd, and we check the return. - let ret = unsafe { ioctl_with_ref(&sock, net_sys::sockios::SIOCSIFMTU as IoctlNr, &ifreq) }; - if ret < 0 { - return Err(Error::IoctlError(SysError::last())); - } - - Ok(()) - } - - fn mac_address(&self) -> Result { - let sock = create_socket()?; - let mut ifreq = self.get_ifreq(); - - // ioctl is safe. Called with a valid sock fd, and we check the return. - let ret = unsafe { - ioctl_with_mut_ref( - &sock, - net_sys::sockios::SIOCGIFHWADDR as IoctlNr, - &mut ifreq, - ) - }; - if ret < 0 { - return Err(Error::IoctlError(SysError::last())); - } - - // We only access one field of the ifru union, hence this is safe. - // This is safe since the MacAddress struct is already sized to match the C sockaddr - // struct. The address family has also been checked. - Ok(unsafe { mem::transmute(ifreq.ifr_ifru.ifru_hwaddr) }) - } - - fn set_mac_address(&self, mac_addr: MacAddress) -> Result<()> { - let sock = create_socket()?; - - let mut ifreq = self.get_ifreq(); - - // We only access one field of the ifru union, hence this is safe. - unsafe { - // This is safe since the MacAddress struct is already sized to match the C sockaddr - // struct. - ifreq.ifr_ifru.ifru_hwaddr = std::mem::transmute(mac_addr); - } - - // ioctl is safe. Called with a valid sock fd, and we check the return. - let ret = - unsafe { ioctl_with_ref(&sock, net_sys::sockios::SIOCSIFHWADDR as IoctlNr, &ifreq) }; - if ret < 0 { - return Err(Error::IoctlError(SysError::last())); - } - - Ok(()) - } - - fn set_offload(&self, flags: c_uint) -> Result<()> { - // ioctl is safe. Called with a valid tap fd, and we check the return. - let ret = - unsafe { ioctl_with_val(&self.tap_file, net_sys::TUNSETOFFLOAD(), flags as c_ulong) }; - if ret < 0 { - return Err(Error::IoctlError(SysError::last())); - } - - Ok(()) - } - - fn enable(&self) -> Result<()> { - let sock = create_socket()?; - - let mut ifreq = self.get_ifreq(); - ifreq.ifr_ifru.ifru_flags = - (net_sys::net_device_flags::IFF_UP | net_sys::net_device_flags::IFF_RUNNING).0 as i16; - - // ioctl is safe. Called with a valid sock fd, and we check the return. - let ret = - unsafe { ioctl_with_ref(&sock, net_sys::sockios::SIOCSIFFLAGS as IoctlNr, &ifreq) }; - if ret < 0 { - return Err(Error::IoctlError(SysError::last())); - } - - Ok(()) - } - - fn set_vnet_hdr_size(&self, size: c_int) -> Result<()> { - // ioctl is safe. Called with a valid tap fd, and we check the return. - let ret = unsafe { ioctl_with_ref(&self.tap_file, net_sys::TUNSETVNETHDRSZ(), &size) }; - if ret < 0 { - return Err(Error::IoctlError(SysError::last())); - } - - Ok(()) - } - - fn get_ifreq(&self) -> net_sys::ifreq { - let mut ifreq: net_sys::ifreq = Default::default(); - - // This sets the name of the interface, which is the only entry - // in a single-field union. - unsafe { - let ifrn_name = ifreq.ifr_ifrn.ifrn_name.as_mut(); - ifrn_name.clone_from_slice(&self.if_name); - } - - // This sets the flags with which the interface was created, which is the only entry we set - // on the second union. - ifreq.ifr_ifru.ifru_flags = self.if_flags; - - ifreq - } - - fn if_flags(&self) -> i32 { - self.if_flags.into() - } - - fn try_clone(&self) -> Result { - self.tap_file - .try_clone() - .map(|tap_file| Tap { - tap_file, - if_name: self.if_name, - if_flags: self.if_flags, - }) - .map_err(SysError::from) - .map_err(Error::CloneTap) - } - - /// # Safety: fd is a valid FD and ownership of it is transferred when - /// calling this function. - unsafe fn from_raw_descriptor(fd: RawDescriptor) -> Result { - let tap_file = File::from_raw_descriptor(fd); - - // Get the interface name since we will need it for some ioctls. - let mut ifreq: net_sys::ifreq = Default::default(); - let ret = ioctl_with_mut_ref(&tap_file, net_sys::TUNGETIFF(), &mut ifreq); - - if ret < 0 { - return Err(Error::IoctlError(SysError::last())); - } - - Ok(Tap { - tap_file, - if_name: ifreq.ifr_ifrn.ifrn_name, - if_flags: ifreq.ifr_ifru.ifru_flags, - }) - } -} - -impl Read for Tap { - fn read(&mut self, buf: &mut [u8]) -> IoResult { - self.tap_file.read(buf) - } -} - -impl Write for Tap { - fn write(&mut self, buf: &[u8]) -> IoResult { - self.tap_file.write(buf) - } - - fn flush(&mut self) -> IoResult<()> { - Ok(()) - } -} - -impl AsRawFd for Tap { - fn as_raw_fd(&self) -> RawFd { - self.tap_file.as_raw_descriptor() - } -} - -impl AsRawDescriptor for Tap { - fn as_raw_descriptor(&self) -> RawDescriptor { - self.tap_file.as_raw_descriptor() - } -} - -impl IntoAsync for Tap {} - -volatile_impl!(Tap); - -pub mod fakes { - use super::*; - use std::fs::remove_file; - use std::fs::OpenOptions; - - const TMP_FILE: &str = "/tmp/crosvm_tap_test_file"; - - pub struct FakeTap { - tap_file: File, - } - - impl TapT for FakeTap { - fn new_with_name(_: &[u8], _: bool, _: bool) -> Result { - Ok(FakeTap { - tap_file: OpenOptions::new() - .read(true) - .append(true) - .create(true) - .open(TMP_FILE) - .unwrap(), - }) - } - - fn into_mq_taps(self, _vq_pairs: u16) -> Result> { - Ok(Vec::new()) - } - - fn ip_addr(&self) -> Result { - Ok(net::Ipv4Addr::new(1, 2, 3, 4)) - } - - fn set_ip_addr(&self, _: net::Ipv4Addr) -> Result<()> { - Ok(()) - } - - fn netmask(&self) -> Result { - Ok(net::Ipv4Addr::new(255, 255, 255, 252)) - } - - fn set_netmask(&self, _: net::Ipv4Addr) -> Result<()> { - Ok(()) - } - - fn mtu(&self) -> Result { - Ok(1500) - } - - fn set_mtu(&self, _: u16) -> Result<()> { - Ok(()) - } - - fn mac_address(&self) -> Result { - Ok("01:02:03:04:05:06".parse().unwrap()) - } - - fn set_mac_address(&self, _: MacAddress) -> Result<()> { - Ok(()) - } - - fn set_offload(&self, _: c_uint) -> Result<()> { - Ok(()) - } - - fn enable(&self) -> Result<()> { - Ok(()) - } - - fn set_vnet_hdr_size(&self, _: c_int) -> Result<()> { - Ok(()) - } - - fn get_ifreq(&self) -> net_sys::ifreq { - let ifreq: net_sys::ifreq = Default::default(); - ifreq - } - - fn if_flags(&self) -> i32 { - libc::IFF_TAP - } - - fn try_clone(&self) -> Result { - unimplemented!() - } - - /// # Safety: panics on call / does nothing. - unsafe fn from_raw_descriptor(_descriptor: RawDescriptor) -> Result { - unimplemented!() - } - } - - impl Drop for FakeTap { - fn drop(&mut self) { - let _ = remove_file(TMP_FILE); - } - } - - impl Read for FakeTap { - fn read(&mut self, _: &mut [u8]) -> IoResult { - Ok(0) - } - } - - impl Write for FakeTap { - fn write(&mut self, _: &[u8]) -> IoResult { - Ok(0) - } - - fn flush(&mut self) -> IoResult<()> { - Ok(()) - } - } - - impl AsRawFd for FakeTap { - fn as_raw_fd(&self) -> RawFd { - self.tap_file.as_raw_descriptor() - } - } - - impl AsRawDescriptor for FakeTap { - fn as_raw_descriptor(&self) -> RawDescriptor { - self.tap_file.as_raw_descriptor() - } - } - volatile_impl!(FakeTap); -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn parse_mac_address() { - assert!("01:02:03:04:05:06".parse::().is_ok()); - assert!("01:06".parse::().is_err()); - assert!("01:02:03:04:05:06:07:08:09".parse::().is_err()); - assert!("not a mac address".parse::().is_err()); - } - - #[test] - fn tap_create() { - Tap::new(true, false).unwrap(); - } - - #[test] - fn tap_configure() { - let tap = Tap::new(true, false).unwrap(); - let ip_addr: net::Ipv4Addr = "100.115.92.5".parse().unwrap(); - let netmask: net::Ipv4Addr = "255.255.255.252".parse().unwrap(); - let mac_addr: MacAddress = "a2:06:b9:3d:68:4d".parse().unwrap(); - - let ret = tap.set_ip_addr(ip_addr); - assert_ok_or_perm_denied(ret); - let ret = tap.set_netmask(netmask); - assert_ok_or_perm_denied(ret); - let ret = tap.set_mac_address(mac_addr); - assert_ok_or_perm_denied(ret); - } - - /// This test will only work if the test is run with root permissions and, unlike other tests - /// in this file, do not return PermissionDenied. They fail because the TAP FD is not - /// initialized (as opposed to permission denial). Run this with "cargo test -- --ignored". - #[test] - #[ignore] - fn root_only_tests() { - // This line will fail to provide an initialized FD if the test is not run as root. - let tap = Tap::new(true, false).unwrap(); - tap.set_vnet_hdr_size(16).unwrap(); - tap.set_offload(0).unwrap(); - } - - #[test] - fn tap_enable() { - let tap = Tap::new(true, false).unwrap(); - - let ret = tap.enable(); - assert_ok_or_perm_denied(ret); - } - - fn assert_ok_or_perm_denied(res: Result) { - match res { - // We won't have permission in test environments; allow that - Ok(_t) => {} - Err(Error::IoctlError(e)) if e.errno() == EPERM => {} - Err(e) => panic!("Unexpected Error:\n{}", e), - } - } -} diff --git a/net_util/src/sys.rs b/net_util/src/sys.rs new file mode 100644 index 0000000000..c1a26e2a45 --- /dev/null +++ b/net_util/src/sys.rs @@ -0,0 +1,14 @@ +// Copyright 2022 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cfg_if::cfg_if! { + if #[cfg(unix)] { + pub mod unix; + use unix as platform; + } else { + compile_error!("Unsupported platform"); + } +} + +pub use platform::TapT; diff --git a/net_util/src/sys/unix.rs b/net_util/src/sys/unix.rs new file mode 100644 index 0000000000..4b5a907bb0 --- /dev/null +++ b/net_util/src/sys/unix.rs @@ -0,0 +1,16 @@ +// Copyright 2022 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +pub mod tap; +pub use tap::Tap; + +use crate::TapTCommon; +use base::FileReadWriteVolatile; + +// TODO(b/159159958) implement FileReadWriteVolatile for slirp +pub trait TapT: FileReadWriteVolatile + TapTCommon {} + +pub mod fakes { + pub use super::tap::fakes::FakeTap; +} diff --git a/net_util/src/sys/unix/tap.rs b/net_util/src/sys/unix/tap.rs new file mode 100644 index 0000000000..96f921a96b --- /dev/null +++ b/net_util/src/sys/unix/tap.rs @@ -0,0 +1,643 @@ +// Copyright 2020 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use std::fs::File; +use std::io::{Read, Result as IoResult, Write}; +use std::mem; +use std::net; +use std::os::raw::*; +use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; + +use libc::EPERM; + +use crate::{Error, MacAddress, Result, TapT, TapTCommon}; +use base::Error as SysError; +use base::{ + ioctl_with_mut_ref, ioctl_with_ref, ioctl_with_val, volatile_impl, AsRawDescriptor, + FileReadWriteVolatile, FromRawDescriptor, IoctlNr, RawDescriptor, ReadNotifier, +}; +use cros_async::IntoAsync; + +/// Handle for a network tap interface. +/// +/// For now, this simply wraps the file descriptor for the tap device so methods +/// can run ioctls on the interface. The tap interface descriptor will be closed when +/// Tap goes out of scope, and the kernel will clean up the interface +/// automatically. +#[derive(Debug)] +pub struct Tap { + tap_file: File, + if_name: [c_char; 16usize], + if_flags: ::std::os::raw::c_short, +} + +impl Tap { + /// # Safety + /// 1. descriptor's ownership must be released by the caller. It is now owned by + /// the returned value (`Tap`), or is closed (if an error is returned). + pub unsafe fn from_raw_descriptor(descriptor: RawDescriptor) -> Result { + let tap_file = File::from_raw_descriptor(descriptor); + + // Get the interface name since we will need it for some ioctls. + let mut ifreq: net_sys::ifreq = Default::default(); + let ret = ioctl_with_mut_ref(&tap_file, net_sys::TUNGETIFF(), &mut ifreq); + + if ret < 0 { + return Err(Error::IoctlError(SysError::last())); + } + + Ok(Tap { + tap_file, + if_name: ifreq.ifr_ifrn.ifrn_name, + if_flags: ifreq.ifr_ifru.ifru_flags, + }) + } + + pub fn create_tap_with_ifreq(ifreq: &mut net_sys::ifreq) -> Result { + // Open calls are safe because we give a constant nul-terminated + // string and verify the result. + let rd = unsafe { + libc::open( + b"/dev/net/tun\0".as_ptr() as *const c_char, + libc::O_RDWR | libc::O_NONBLOCK | libc::O_CLOEXEC, + ) + }; + if rd < 0 { + return Err(Error::OpenTun(SysError::last())); + } + + // We just checked that the fd is valid. + let tuntap = unsafe { File::from_raw_descriptor(rd) }; + // ioctl is safe since we call it with a valid tap fd and check the return + // value. + let ret = unsafe { ioctl_with_mut_ref(&tuntap, net_sys::TUNSETIFF(), ifreq) }; + + if ret < 0 { + let error = SysError::last(); + + // In a non-root, test environment, we won't have permission to call this; allow + if !(cfg!(test) && error.errno() == EPERM) { + return Err(Error::CreateTap(error)); + } + } + + // Safe since only the name is accessed, and it's copied out. + Ok(Tap { + tap_file: tuntap, + if_name: unsafe { ifreq.ifr_ifrn.ifrn_name }, + if_flags: unsafe { ifreq.ifr_ifru.ifru_flags }, + }) + } + pub fn try_clone(&self) -> Result { + self.tap_file + .try_clone() + .map(|tap_file| Tap { + tap_file, + if_name: self.if_name, + if_flags: self.if_flags, + }) + .map_err(SysError::from) + .map_err(Error::CloneTap) + } +} + +impl TapTCommon for Tap { + /// Create a new tap interface. + /// + /// Set the `vnet_hdr` flag to true to allow offloading on this tap, + /// which will add an extra 12 byte virtio net header to incoming frames. Offloading cannot + /// be used if `vnet_hdr` is false. + /// Set 'multi_vq' to true, if tap have multi virt queue pairs + fn new(vnet_hdr: bool, multi_vq: bool) -> Result { + const TUNTAP_DEV_FORMAT: &[u8] = b"vmtap%d"; + Self::new_with_name(TUNTAP_DEV_FORMAT, vnet_hdr, multi_vq) + } + + fn new_with_name(name: &[u8], vnet_hdr: bool, multi_vq: bool) -> Result { + // This is pretty messy because of the unions used by ifreq. Since we + // don't call as_mut on the same union field more than once, this block + // is safe. + let mut ifreq: net_sys::ifreq = Default::default(); + unsafe { + let ifrn_name = ifreq.ifr_ifrn.ifrn_name.as_mut(); + for (dst, src) in ifrn_name + .iter_mut() + // Add a zero terminator to the source string. + .zip(name.iter().chain(std::iter::once(&0))) + { + *dst = *src as c_char; + } + ifreq.ifr_ifru.ifru_flags = + (libc::IFF_TAP | libc::IFF_NO_PI | if vnet_hdr { libc::IFF_VNET_HDR } else { 0 }) + as c_short; + if multi_vq { + ifreq.ifr_ifru.ifru_flags |= libc::IFF_MULTI_QUEUE as c_short; + } + } + + Tap::create_tap_with_ifreq(&mut ifreq) + } + + fn into_mq_taps(self, vq_pairs: u16) -> Result> { + let mut taps: Vec = Vec::new(); + + if vq_pairs <= 1 { + taps.push(self); + return Ok(taps); + } + + // Add other socket into the origin tap interface + for _ in 0..vq_pairs - 1 { + let mut ifreq = self.get_ifreq(); + let tap = Tap::create_tap_with_ifreq(&mut ifreq)?; + + tap.enable()?; + + taps.push(tap); + } + + taps.insert(0, self); + Ok(taps) + } + + fn ip_addr(&self) -> Result { + let sock = create_socket()?; + let mut ifreq = self.get_ifreq(); + + // ioctl is safe. Called with a valid sock descriptor, and we check the return. + let ret = unsafe { + ioctl_with_mut_ref(&sock, net_sys::sockios::SIOCGIFADDR as IoctlNr, &mut ifreq) + }; + + if ret < 0 { + return Err(Error::IoctlError(SysError::last())); + } + + // We only access one field of the ifru union, hence this is safe. + let addr = unsafe { ifreq.ifr_ifru.ifru_addr }; + + Ok(read_ipv4_addr(&addr)) + } + + fn set_ip_addr(&self, ip_addr: net::Ipv4Addr) -> Result<()> { + let sock = create_socket()?; + let addr = create_sockaddr(ip_addr); + + let mut ifreq = self.get_ifreq(); + ifreq.ifr_ifru.ifru_addr = addr; + + // ioctl is safe. Called with a valid sock descriptor, and we check the return. + let ret = + unsafe { ioctl_with_ref(&sock, net_sys::sockios::SIOCSIFADDR as IoctlNr, &ifreq) }; + if ret < 0 { + return Err(Error::IoctlError(SysError::last())); + } + + Ok(()) + } + + fn netmask(&self) -> Result { + let sock = create_socket()?; + let mut ifreq = self.get_ifreq(); + + // ioctl is safe. Called with a valid sock descriptor, and we check the return. + let ret = unsafe { + ioctl_with_mut_ref( + &sock, + net_sys::sockios::SIOCGIFNETMASK as IoctlNr, + &mut ifreq, + ) + }; + if ret < 0 { + return Err(Error::IoctlError(SysError::last())); + } + + // We only access one field of the ifru union, hence this is safe. + let addr = unsafe { ifreq.ifr_ifru.ifru_netmask }; + + Ok(read_ipv4_addr(&addr)) + } + + fn set_netmask(&self, netmask: net::Ipv4Addr) -> Result<()> { + let sock = create_socket()?; + let addr = create_sockaddr(netmask); + + let mut ifreq = self.get_ifreq(); + ifreq.ifr_ifru.ifru_netmask = addr; + + // ioctl is safe. Called with a valid sock descriptor, and we check the return. + let ret = + unsafe { ioctl_with_ref(&sock, net_sys::sockios::SIOCSIFNETMASK as IoctlNr, &ifreq) }; + if ret < 0 { + return Err(Error::IoctlError(SysError::last())); + } + + Ok(()) + } + + fn mtu(&self) -> Result { + let sock = create_socket()?; + let mut ifreq = self.get_ifreq(); + + // ioctl is safe. Called with a valid sock fd, and we check the return. + let ret = unsafe { + ioctl_with_mut_ref(&sock, net_sys::sockios::SIOCGIFMTU as IoctlNr, &mut ifreq) + }; + if ret < 0 { + return Err(Error::IoctlError(SysError::last())); + } + + // We only access one field of the ifru union, hence this is safe. + let mtu = unsafe { ifreq.ifr_ifru.ifru_mtu } as u16; + Ok(mtu) + } + + fn set_mtu(&self, mtu: u16) -> Result<()> { + let sock = create_socket()?; + + let mut ifreq = self.get_ifreq(); + ifreq.ifr_ifru.ifru_mtu = i32::from(mtu); + + // ioctl is safe. Called with a valid sock fd, and we check the return. + let ret = unsafe { ioctl_with_ref(&sock, net_sys::sockios::SIOCSIFMTU as IoctlNr, &ifreq) }; + if ret < 0 { + return Err(Error::IoctlError(SysError::last())); + } + + Ok(()) + } + + fn mac_address(&self) -> Result { + let sock = create_socket()?; + let mut ifreq = self.get_ifreq(); + + // ioctl is safe. Called with a valid sock descriptor, and we check the return. + let ret = unsafe { + ioctl_with_mut_ref( + &sock, + net_sys::sockios::SIOCGIFHWADDR as IoctlNr, + &mut ifreq, + ) + }; + if ret < 0 { + return Err(Error::IoctlError(SysError::last())); + } + + // We only access one field of the ifru union, hence this is safe. + // This is safe since the MacAddress struct is already sized to match the C sockaddr + // struct. The address family has also been checked. + Ok(unsafe { mem::transmute(ifreq.ifr_ifru.ifru_hwaddr) }) + } + + fn set_mac_address(&self, mac_addr: MacAddress) -> Result<()> { + let sock = create_socket()?; + + let mut ifreq = self.get_ifreq(); + + // We only access one field of the ifru union, hence this is safe. + unsafe { + // This is safe since the MacAddress struct is already sized to match the C sockaddr + // struct. + ifreq.ifr_ifru.ifru_hwaddr = std::mem::transmute(mac_addr); + } + + // ioctl is safe. Called with a valid sock descriptor, and we check the return. + let ret = + unsafe { ioctl_with_ref(&sock, net_sys::sockios::SIOCSIFHWADDR as IoctlNr, &ifreq) }; + if ret < 0 { + return Err(Error::IoctlError(SysError::last())); + } + + Ok(()) + } + + fn set_offload(&self, flags: c_uint) -> Result<()> { + // ioctl is safe. Called with a valid tap descriptor, and we check the return. + let ret = + unsafe { ioctl_with_val(&self.tap_file, net_sys::TUNSETOFFLOAD(), flags as c_ulong) }; + if ret < 0 { + return Err(Error::IoctlError(SysError::last())); + } + + Ok(()) + } + + fn enable(&self) -> Result<()> { + let sock = create_socket()?; + + let mut ifreq = self.get_ifreq(); + ifreq.ifr_ifru.ifru_flags = + (net_sys::net_device_flags::IFF_UP | net_sys::net_device_flags::IFF_RUNNING).0 as i16; + + // ioctl is safe. Called with a valid sock descriptor, and we check the return. + let ret = + unsafe { ioctl_with_ref(&sock, net_sys::sockios::SIOCSIFFLAGS as IoctlNr, &ifreq) }; + if ret < 0 { + return Err(Error::IoctlError(SysError::last())); + } + + Ok(()) + } + + fn set_vnet_hdr_size(&self, size: c_int) -> Result<()> { + // ioctl is safe. Called with a valid tap descriptor, and we check the return. + let ret = unsafe { ioctl_with_ref(&self.tap_file, net_sys::TUNSETVNETHDRSZ(), &size) }; + if ret < 0 { + return Err(Error::IoctlError(SysError::last())); + } + + Ok(()) + } + + fn get_ifreq(&self) -> net_sys::ifreq { + let mut ifreq: net_sys::ifreq = Default::default(); + + // This sets the name of the interface, which is the only entry + // in a single-field union. + unsafe { + let ifrn_name = ifreq.ifr_ifrn.ifrn_name.as_mut(); + ifrn_name.clone_from_slice(&self.if_name); + } + + // This sets the flags with which the interface was created, which is the only entry we set + // on the second union. + ifreq.ifr_ifru.ifru_flags = self.if_flags; + + ifreq + } + + fn if_flags(&self) -> i32 { + self.if_flags.into() + } + + fn try_clone(&self) -> Result { + self.try_clone() + } + + // Safe if caller provides a valid descriptor. + unsafe fn from_raw_descriptor(descriptor: RawDescriptor) -> Result { + Tap::from_raw_descriptor(descriptor) + } +} + +impl Read for Tap { + fn read(&mut self, buf: &mut [u8]) -> IoResult { + self.tap_file.read(buf) + } +} + +impl Write for Tap { + fn write(&mut self, buf: &[u8]) -> IoResult { + self.tap_file.write(buf) + } + + fn flush(&mut self) -> IoResult<()> { + Ok(()) + } +} + +impl AsRawFd for Tap { + fn as_raw_fd(&self) -> RawFd { + self.tap_file.as_raw_descriptor() + } +} + +impl AsRawDescriptor for Tap { + fn as_raw_descriptor(&self) -> RawDescriptor { + self.tap_file.as_raw_descriptor() + } +} + +impl ReadNotifier for Tap { + fn get_read_notifier(&self) -> &dyn AsRawDescriptor { + self + } +} + +fn create_socket() -> Result { + // This is safe since we check the return value. + let sock = unsafe { libc::socket(libc::AF_INET, libc::SOCK_DGRAM, 0) }; + if sock < 0 { + return Err(Error::CreateSocket(SysError::last())); + } + + // This is safe; nothing else will use or hold onto the raw sock descriptor. + Ok(unsafe { net::UdpSocket::from_raw_fd(sock) }) +} + +/// Create a sockaddr_in from an IPv4 address, and expose it as +/// an opaque sockaddr suitable for usage by socket ioctls. +fn create_sockaddr(ip_addr: net::Ipv4Addr) -> libc::sockaddr { + // IPv4 addresses big-endian (network order), but Ipv4Addr will give us + // a view of those bytes directly so we can avoid any endian trickiness. + let addr_in = libc::sockaddr_in { + sin_family: libc::AF_INET as u16, + sin_port: 0, + sin_addr: unsafe { mem::transmute(ip_addr.octets()) }, + sin_zero: [0; 8usize], + }; + + unsafe { mem::transmute(addr_in) } +} + +/// Extract the IPv4 address from a sockaddr. Assumes the sockaddr is a sockaddr_in. +fn read_ipv4_addr(addr: &libc::sockaddr) -> net::Ipv4Addr { + debug_assert_eq!(addr.sa_family as i32, libc::AF_INET); + // This is safe because sockaddr and sockaddr_in are the same size, and we've checked that + // this address is AF_INET. + let in_addr: libc::sockaddr_in = unsafe { mem::transmute(*addr) }; + net::Ipv4Addr::from(in_addr.sin_addr.s_addr) +} + +impl TapT for Tap {} +impl IntoAsync for Tap {} +volatile_impl!(Tap); + +pub mod fakes { + use super::*; + use std::fs::remove_file; + use std::fs::OpenOptions; + + const TMP_FILE: &str = "/tmp/crosvm_tap_test_file"; + + pub struct FakeTap { + tap_file: File, + } + + impl TapTCommon for FakeTap { + fn new(_vnet_hdr: bool, _multi_vq: bool) -> Result { + // Params don't matter + Self::new_with_name(b"", false, false) + } + + fn new_with_name(_: &[u8], _: bool, _: bool) -> Result { + Ok(FakeTap { + tap_file: OpenOptions::new() + .read(true) + .append(true) + .create(true) + .open(TMP_FILE) + .unwrap(), + }) + } + + fn into_mq_taps(self, _vq_pairs: u16) -> Result> { + Ok(Vec::new()) + } + + fn ip_addr(&self) -> Result { + Ok(net::Ipv4Addr::new(1, 2, 3, 4)) + } + + fn set_ip_addr(&self, _: net::Ipv4Addr) -> Result<()> { + Ok(()) + } + + fn netmask(&self) -> Result { + Ok(net::Ipv4Addr::new(255, 255, 255, 252)) + } + + fn set_netmask(&self, _: net::Ipv4Addr) -> Result<()> { + Ok(()) + } + + fn mtu(&self) -> Result { + Ok(1500) + } + + fn set_mtu(&self, _: u16) -> Result<()> { + Ok(()) + } + + fn mac_address(&self) -> Result { + Ok("01:02:03:04:05:06".parse().unwrap()) + } + + fn set_mac_address(&self, _: MacAddress) -> Result<()> { + Ok(()) + } + + fn set_offload(&self, _: c_uint) -> Result<()> { + Ok(()) + } + + fn enable(&self) -> Result<()> { + Ok(()) + } + + fn set_vnet_hdr_size(&self, _: c_int) -> Result<()> { + Ok(()) + } + + fn get_ifreq(&self) -> net_sys::ifreq { + let ifreq: net_sys::ifreq = Default::default(); + ifreq + } + + fn if_flags(&self) -> i32 { + libc::IFF_TAP + } + + // Return self so it can compile + fn try_clone(&self) -> Result { + Ok(FakeTap { + tap_file: self.tap_file.try_clone().unwrap(), + }) + } + + unsafe fn from_raw_descriptor(_descriptor: RawDescriptor) -> Result { + unimplemented!() + } + } + + impl Drop for FakeTap { + fn drop(&mut self) { + let _ = remove_file(TMP_FILE); + } + } + + impl Read for FakeTap { + fn read(&mut self, _: &mut [u8]) -> IoResult { + Ok(0) + } + } + + impl Write for FakeTap { + fn write(&mut self, _: &[u8]) -> IoResult { + Ok(0) + } + + fn flush(&mut self) -> IoResult<()> { + Ok(()) + } + } + + impl AsRawFd for FakeTap { + fn as_raw_fd(&self) -> RawFd { + self.tap_file.as_raw_descriptor() + } + } + + impl AsRawDescriptor for FakeTap { + fn as_raw_descriptor(&self) -> RawDescriptor { + self.tap_file.as_raw_descriptor() + } + } + impl TapT for FakeTap {} + volatile_impl!(FakeTap); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn tap_create() { + Tap::new(true, false).unwrap(); + } + + #[test] + fn tap_configure() { + let tap = Tap::new(true, false).unwrap(); + let ip_addr: net::Ipv4Addr = "100.115.92.5".parse().unwrap(); + let netmask: net::Ipv4Addr = "255.255.255.252".parse().unwrap(); + let mac_addr: MacAddress = "a2:06:b9:3d:68:4d".parse().unwrap(); + + let ret = tap.set_ip_addr(ip_addr); + assert_ok_or_perm_denied(ret); + let ret = tap.set_netmask(netmask); + assert_ok_or_perm_denied(ret); + let ret = tap.set_mac_address(mac_addr); + assert_ok_or_perm_denied(ret); + } + + /// This test will only work if the test is run with root permissions and, unlike other tests + /// in this file, do not return PermissionDenied. They fail because the TAP descriptor is not + /// initialized (as opposed to permission denial). Run this with "cargo test -- --ignored". + #[test] + #[ignore] + fn root_only_tests() { + // This line will fail to provide an initialized descriptor if the test is not run as root. + let tap = Tap::new(true, false).unwrap(); + tap.set_vnet_hdr_size(16).unwrap(); + tap.set_offload(0).unwrap(); + } + + #[test] + fn tap_enable() { + let tap = Tap::new(true, false).unwrap(); + + let ret = tap.enable(); + assert_ok_or_perm_denied(ret); + } + + fn assert_ok_or_perm_denied(res: Result) { + match res { + // We won't have permission in test environments; allow that + Ok(_t) => {} + Err(Error::IoctlError(e)) if e.errno() == EPERM => {} + Err(e) => panic!("Unexpected Error:\n{}", e), + } + } +} diff --git a/src/linux/device_helpers.rs b/src/linux/device_helpers.rs index cb402d31c0..bea9d0acc2 100644 --- a/src/linux/device_helpers.rs +++ b/src/linux/device_helpers.rs @@ -47,7 +47,7 @@ use devices::{ }; use hypervisor::Vm; use minijail::{self, Minijail}; -use net_util::{MacAddress, Tap, TapT}; +use net_util::{sys::unix::Tap, MacAddress}; use resources::{Alloc, MmioType, SystemAllocator}; use sync::Mutex; use vm_memory::GuestAddress; diff --git a/src/plugin/mod.rs b/src/plugin/mod.rs index ddf3a874de..e01ae56703 100644 --- a/src/plugin/mod.rs +++ b/src/plugin/mod.rs @@ -34,7 +34,7 @@ use base::{ }; use kvm::{Cap, Datamatch, IoeventAddress, Kvm, Vcpu, VcpuExit, Vm}; use minijail::{self, Minijail}; -use net_util::{Tap, TapT}; +use net_util::{sys::unix::Tap, TapTCommon}; use vm_memory::{GuestMemory, MemoryPolicy}; use self::process::*; diff --git a/src/plugin/process.rs b/src/plugin/process.rs index 88d078ff65..4976a0b53d 100644 --- a/src/plugin/process.rs +++ b/src/plugin/process.rs @@ -14,7 +14,7 @@ use std::result; use std::sync::{Arc, RwLock}; use std::thread::JoinHandle; -use net_util::Error as NetError; +use net_util::{Error as NetError, TapTCommon}; use libc::{ pid_t, waitpid, EINVAL, ENODATA, ENOTTY, STDERR_FILENO, WEXITSTATUS, WIFEXITED, WNOHANG, @@ -515,7 +515,7 @@ impl Process { } fn handle_get_net_config( - tap: &net_util::Tap, + tap: &net_util::sys::unix::Tap, config: &mut MainResponse_GetNetConfig, ) -> SysResult<()> { // Log any NetError so that the cause can be found later, but extract and return the diff --git a/tests/plugins.rs b/tests/plugins.rs index cb7afad517..eacd85f5b9 100644 --- a/tests/plugins.rs +++ b/tests/plugins.rs @@ -15,12 +15,13 @@ use std::thread::sleep; use std::time::Duration; use base::{ioctl, AsRawDescriptor}; +use net_util::TapTCommon; use tempfile::tempfile; lazy_static::lazy_static! { static ref TAP_AVAILABLE: bool = { use net_util::TapT; - net_util::Tap::new(true, false).is_ok() + net_util::sys::unix::Tap::new(true, false).is_ok() }; } diff --git a/vhost/src/lib.rs b/vhost/src/lib.rs index 4b947e3b10..c054f74565 100644 --- a/vhost/src/lib.rs +++ b/vhost/src/lib.rs @@ -390,7 +390,7 @@ mod tests { use super::*; use crate::net::fakes::FakeNet; - use net_util::fakes::FakeTap; + use net_util::sys::unix::fakes::FakeTap; use std::{path::PathBuf, result}; use vm_memory::{GuestAddress, GuestMemory, GuestMemoryError};