use super::{Error, IfReq, Opener, Result};
use nix::{fcntl::OFlag, libc};
use std::io::{self, Read, Write};
use std::mem::MaybeUninit;
use std::os::unix::prelude::{AsRawFd, RawFd};
const PATH: &[u8] = b"/dev/net/tun\0";
nix::ioctl_write_int!(create_queue, b'T', 202);
#[cfg(target_pointer_width = "64")]
type PointerWidth = u64;
#[cfg(target_pointer_width = "32")]
type PointerWidth = u32;
#[cfg(target_pointer_width = "16")]
type PointerWidth = u16;
#[derive(Clone)]
pub struct Queue(RawFd);
impl Queue {
pub(crate) fn open(req: &IfReq) -> Result<Self> {
let fd = unsafe { libc::open(PATH.as_ptr() as *const libc::c_char, libc::O_RDWR) };
if fd < 0 {
return Err(Error::FS {
path: unsafe { String::from_utf8_unchecked(PATH.to_vec()) },
source: io::Error::last_os_error(),
});
}
let ret = unsafe { create_queue(fd, req as *const IfReq as PointerWidth)? };
if ret >= 1 {
return Err(Error::from(ret as i32));
}
Ok(Self(fd))
}
pub fn close(&mut self) -> Result<()> {
let ret = unsafe { libc::close(self.0) };
if ret < 0 {
Err(Error::errno())
} else {
self.0 = -1;
Ok(())
}
}
pub fn set_non_blocking(&self, on: bool) -> Result<()> {
let flags =
nix::fcntl::fcntl(self.0, nix::fcntl::FcntlArg::F_GETFL).map_err(Error::from)?;
let mut flags = OFlag::from_bits(flags).unwrap_or(OFlag::O_RDWR);
if on && !flags.contains(OFlag::O_NONBLOCK) {
flags.insert(OFlag::O_NONBLOCK);
} else if !on && flags.contains(OFlag::O_NONBLOCK) {
flags.remove(OFlag::O_NONBLOCK);
} else {
return Ok(());
}
nix::fcntl::fcntl(self.0, nix::fcntl::FcntlArg::F_SETFL(flags))
.map(|_| ())
.map_err(Error::from)
}
pub fn send(&self, datagram: &[u8]) -> io::Result<usize> {
let count = datagram.len();
let written = unsafe {
let ptr = datagram.as_ptr();
libc::write(self.0, ptr as *const libc::c_void, count)
};
if written < 0 {
Err(io::Error::last_os_error())
} else {
Ok(written as usize)
}
}
#[inline]
pub fn recv(&self, datagram: &mut [u8]) -> io::Result<usize> {
unsafe { self.recv_int(datagram.as_mut_ptr(), datagram.len()) }
}
#[inline]
pub fn recv_uninit(&self, datagram: &mut [MaybeUninit<u8>]) -> io::Result<usize> {
unsafe { self.recv_int(datagram.as_mut_ptr(), datagram.len()) }
}
unsafe fn recv_int<T>(&self, ptr: *mut T, count: usize) -> io::Result<usize> {
let read = libc::read(self.0, ptr as *mut libc::c_void, count);
if read < 0 {
Err(io::Error::last_os_error())
} else {
Ok(read as usize)
}
}
}
impl AsRawFd for Queue {
fn as_raw_fd(&self) -> RawFd {
self.0
}
}
impl Read for Queue {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
self.recv(buf)
}
}
impl Write for Queue {
#[inline]
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
self.send(buf)
}
#[inline]
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
impl Opener for Queue {
#[inline]
fn open(req: &IfReq) -> Result<Self> {
Self::open(req)
}
}