use std::fs::File;
use std::io::Read;
use std::io::Result as IoResult;
use std::io::Write;
use std::mem;
use std::net;
use std::os::raw::*;
use std::os::unix::io::AsRawFd;
use std::os::unix::io::FromRawFd;
use std::os::unix::io::RawFd;
use base::add_fd_flags;
use base::error;
use base::ioctl_with_mut_ref;
use base::ioctl_with_ref;
use base::ioctl_with_val;
use base::volatile_impl;
use base::warn;
use base::AsRawDescriptor;
use base::Error as SysError;
use base::FileReadWriteVolatile;
use base::FromRawDescriptor;
use base::IoctlNr;
use base::RawDescriptor;
use base::ReadNotifier;
use cros_async::IntoAsync;
use crate::sys::linux::TapTLinux;
use crate::Error;
use crate::MacAddress;
use crate::Result;
use crate::TapT;
use crate::TapTCommon;
#[derive(Debug)]
pub struct Tap {
tap_file: File,
if_name: [c_char; 16usize],
if_flags: ::std::os::raw::c_short,
}
impl Tap {
pub unsafe fn from_raw_descriptor(descriptor: RawDescriptor) -> Result<Tap> {
let tap_file = File::from_raw_descriptor(descriptor);
add_fd_flags(tap_file.as_raw_descriptor(), libc::O_NONBLOCK).map_err(Error::IoctlError)?;
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<Tap> {
let rd = unsafe {
libc::open64(
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()));
}
let tuntap = unsafe { File::from_raw_descriptor(rd) };
let ret = unsafe { ioctl_with_mut_ref(&tuntap, net_sys::TUNSETIFF, ifreq) };
if ret < 0 {
return Err(Error::CreateTap(SysError::last()));
}
Ok(Tap {
tap_file: tuntap,
if_name: unsafe { ifreq.ifr_ifrn.ifrn_name },
if_flags: unsafe { ifreq.ifr_ifru.ifru_flags },
})
}
fn get_ifreq(&self) -> net_sys::ifreq {
let mut ifreq: net_sys::ifreq = Default::default();
unsafe {
let ifrn_name = ifreq.ifr_ifrn.ifrn_name.as_mut();
ifrn_name.clone_from_slice(&self.if_name);
}
ifreq.ifr_ifru.ifru_flags = self.if_flags;
ifreq
}
pub fn try_clone(&self) -> Result<Tap> {
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 {
fn new(vnet_hdr: bool, multi_vq: bool) -> Result<Self> {
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<Tap> {
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()
.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<Vec<Tap>> {
let mut taps: Vec<Tap> = Vec::new();
if vq_pairs <= 1 {
taps.push(self);
return Ok(taps);
}
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<net::Ipv4Addr> {
let sock = create_socket()?;
let mut ifreq = self.get_ifreq();
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()));
}
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;
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<net::Ipv4Addr> {
let sock = create_socket()?;
let mut ifreq = self.get_ifreq();
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()));
}
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;
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<u16> {
let sock = create_socket()?;
let mut ifreq = self.get_ifreq();
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()));
}
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);
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<MacAddress> {
let sock = create_socket()?;
let mut ifreq = self.get_ifreq();
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()));
}
let sa: libc::sockaddr = unsafe { ifreq.ifr_ifru.ifru_hwaddr };
if sa.sa_family != libc::ARPHRD_ETHER {
return Err(crate::Error::IoctlError(base::Error::new(libc::EINVAL)));
}
let mut mac = MacAddress::default();
#[allow(clippy::unnecessary_cast)] for (mac_addr, sa_data) in mac.addr.iter_mut().zip(sa.sa_data.iter()) {
*mac_addr = *sa_data as u8;
}
Ok(mac)
}
fn set_mac_address(&self, mac_addr: MacAddress) -> Result<()> {
let mut sa = libc::sockaddr {
sa_family: libc::ARPHRD_ETHER,
sa_data: Default::default(),
};
#[allow(clippy::unnecessary_cast)] for (sa_data, mac) in sa
.sa_data
.iter_mut()
.zip(mac_addr.octets().iter().chain(std::iter::repeat(&0)))
{
*sa_data = *mac as c_char;
}
let sock = create_socket()?;
let mut ifreq = self.get_ifreq();
ifreq.ifr_ifru.ifru_hwaddr = sa;
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<()> {
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;
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 try_clone(&self) -> Result<Self> {
self.try_clone()
}
unsafe fn from_raw_descriptor(descriptor: RawDescriptor) -> Result<Self> {
Tap::from_raw_descriptor(descriptor)
}
}
impl TapTLinux for Tap {
fn set_vnet_hdr_size(&self, size: usize) -> Result<()> {
let size = size as c_int;
let ret = unsafe { ioctl_with_ref(&self.tap_file, net_sys::TUNSETVNETHDRSZ, &size) };
if ret < 0 {
return Err(Error::IoctlError(SysError::last()));
}
Ok(())
}
fn if_flags(&self) -> u32 {
self.if_flags as u32
}
}
impl Read for Tap {
fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
self.tap_file.read(buf)
}
}
impl Write for Tap {
fn write(&mut self, buf: &[u8]) -> IoResult<usize> {
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<net::UdpSocket> {
let sock = unsafe { libc::socket(libc::AF_INET, libc::SOCK_DGRAM, 0) };
if sock >= 0 {
return Ok(unsafe { net::UdpSocket::from_raw_fd(sock) });
}
warn!("INET not supported on this machine. Trying to open an INET6 socket.");
let sock6 = unsafe { libc::socket(libc::AF_INET6, libc::SOCK_DGRAM, 0) };
if sock6 >= 0 {
return Ok(unsafe { net::UdpSocket::from_raw_fd(sock6) });
}
error!("Neither INET nor INET6 supported on this machine");
Err(Error::CreateSocket(SysError::last()))
}
fn sockaddr_from_sockaddr_in(addr_in: libc::sockaddr_in) -> libc::sockaddr {
assert_eq!(
mem::size_of::<libc::sockaddr_in>(),
mem::size_of::<libc::sockaddr>()
);
unsafe { mem::transmute::<libc::sockaddr_in, libc::sockaddr>(addr_in) }
}
fn sockaddr_in_from_sockaddr(addr: libc::sockaddr) -> Option<libc::sockaddr_in> {
if addr.sa_family as i32 != libc::AF_INET {
return None;
}
assert_eq!(
mem::size_of::<libc::sockaddr_in>(),
mem::size_of::<libc::sockaddr>()
);
Some(unsafe { mem::transmute::<libc::sockaddr, libc::sockaddr_in>(addr) })
}
fn create_sockaddr(ip_addr: net::Ipv4Addr) -> libc::sockaddr {
let addr_in = libc::sockaddr_in {
sin_family: libc::AF_INET as u16,
sin_port: 0,
sin_addr: libc::in_addr {
s_addr: u32::from_be_bytes(ip_addr.octets()).to_be(),
},
sin_zero: [0; 8usize],
};
sockaddr_from_sockaddr_in(addr_in)
}
fn read_ipv4_addr(addr: &libc::sockaddr) -> net::Ipv4Addr {
let in_addr = sockaddr_in_from_sockaddr(*addr).unwrap();
net::Ipv4Addr::from(in_addr.sin_addr.s_addr)
}
impl TapT for Tap {}
impl IntoAsync for Tap {}
volatile_impl!(Tap);
pub mod fakes {
use std::fs::remove_file;
use std::fs::OpenOptions;
use super::*;
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<Self> {
Self::new_with_name(b"", false, false)
}
fn new_with_name(_: &[u8], _: bool, _: bool) -> Result<FakeTap> {
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<Vec<FakeTap>> {
Ok(Vec::new())
}
fn ip_addr(&self) -> Result<net::Ipv4Addr> {
Ok(net::Ipv4Addr::new(1, 2, 3, 4))
}
fn set_ip_addr(&self, _: net::Ipv4Addr) -> Result<()> {
Ok(())
}
fn netmask(&self) -> Result<net::Ipv4Addr> {
Ok(net::Ipv4Addr::new(255, 255, 255, 252))
}
fn set_netmask(&self, _: net::Ipv4Addr) -> Result<()> {
Ok(())
}
fn mtu(&self) -> Result<u16> {
Ok(1500)
}
fn set_mtu(&self, _: u16) -> Result<()> {
Ok(())
}
fn mac_address(&self) -> Result<MacAddress> {
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 try_clone(&self) -> Result<Self> {
Ok(FakeTap {
tap_file: self.tap_file.try_clone().unwrap(),
})
}
unsafe fn from_raw_descriptor(_descriptor: RawDescriptor) -> Result<Self> {
unimplemented!()
}
}
impl TapTLinux for FakeTap {
fn set_vnet_hdr_size(&self, _: usize) -> Result<()> {
Ok(())
}
fn if_flags(&self) -> u32 {
net_sys::IFF_TAP
}
}
impl Drop for FakeTap {
fn drop(&mut self) {
let _ = remove_file(TMP_FILE);
}
}
impl Read for FakeTap {
fn read(&mut self, _: &mut [u8]) -> IoResult<usize> {
Ok(0)
}
}
impl Write for FakeTap {
fn write(&mut self, _: &[u8]) -> IoResult<usize> {
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)]
pub mod tests {
use super::*;
#[test]
fn sockaddr_byte_order() {
let sa = create_sockaddr(net::Ipv4Addr::new(1, 2, 3, 4));
assert_eq!(sa.sa_family, 2); assert_eq!(
sa.sa_data,
[
0, 0, 1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, ]
);
}
}