use std::os::fd::AsRawFd;
use crate::libc;
use crate::nix;
use crate::tokio;
use tokio::io::Interest;
use tokio::io::unix::AsyncFd;
pub const TUNSETIFF: libc::c_ulong = nix::request_code_write!(b'T', 202, std::mem::size_of::<libc::c_int>());
pub const TUNSETPERSIST: libc::c_ulong = nix::request_code_write!(b'T', 203, std::mem::size_of::<libc::c_int>());
pub const TUNDEV: *const libc::c_char = "/dev/net/tun\0".as_ptr() as *const libc::c_char;
fn ifname_to_cstring(ifname: &str) -> std::io::Result<std::ffi::CString> {
if ifname.len() >= libc::IFNAMSIZ || ifname.len() == 0 {
return Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, "interface name too long or short"));
}
if ifname.contains('/') || ifname.contains(' ') || ifname.contains('\x0c') || ifname.contains('\n') || ifname.contains('\r') || ifname.contains('\t') || ifname.contains('\x0b'){
return Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, "interface name contains an invalid character"));
}
std::ffi::CString::new(ifname).map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidInput, e))
}
pub fn tap_add_ioctl(ifname: &str) -> std::io::Result<()> {
let ifname = ifname_to_cstring(ifname)?;
let mut ifr: libc::ifreq = unsafe { std::mem::zeroed() };
unsafe {
ifr.ifr_ifru.ifru_flags |= (libc::IFF_TAP | libc::IFF_NO_PI) as i16;
libc::strncpy(ifr.ifr_name.as_mut_ptr(), ifname.as_ptr(), libc::IFNAMSIZ);
let fd = libc::open(TUNDEV, libc::O_RDWR);
if fd < 0 {
return Err(std::io::Error::last_os_error());
}
let ret = libc::ioctl(fd, TUNSETIFF, &ifr);
if ret < 0 {
libc::close(fd);
return Err(std::io::Error::last_os_error());
}
let ret = libc::ioctl(fd, TUNSETPERSIST, 1);
if ret < 0 {
libc::close(fd);
return Err(std::io::Error::last_os_error());
}
libc::close(fd);
}
Ok(())
}
pub fn tap_del_ioctl(ifname: &str) -> std::io::Result<()> {
let ifname = ifname_to_cstring(ifname)?;
let mut ifr: libc::ifreq = unsafe { std::mem::zeroed() };
unsafe {
ifr.ifr_ifru.ifru_flags |= (libc::IFF_TAP | libc::IFF_NO_PI) as i16;
libc::strncpy(ifr.ifr_name.as_mut_ptr(), ifname.as_ptr(), libc::IFNAMSIZ);
let fd = libc::open(TUNDEV, libc::O_RDWR);
if fd < 0 {
return Err(std::io::Error::last_os_error());
}
let ret = libc::ioctl(fd, TUNSETIFF, &ifr);
if ret < 0 {
libc::close(fd);
return Err(std::io::Error::last_os_error());
}
let ret = libc::ioctl(fd, TUNSETPERSIST, 0);
if ret < 0 {
libc::close(fd);
return Err(std::io::Error::last_os_error());
}
libc::close(fd);
}
Ok(())
}
#[derive(Debug)]
pub struct RawTap {
tap_fd: libc::c_int,
}
impl RawTap {
pub fn new(ifname: &str) -> std::io::Result<Self> {
let ifname = ifname_to_cstring(ifname)?;
let mut ifr: libc::ifreq = unsafe { std::mem::zeroed() };
unsafe {
ifr.ifr_ifru.ifru_flags |= (libc::IFF_TAP | libc::IFF_NO_PI) as i16;
libc::strncpy(ifr.ifr_name.as_mut_ptr(), ifname.as_ptr(), libc::IFNAMSIZ);
let fd = libc::open(TUNDEV, libc::O_RDWR | libc::O_NONBLOCK);
if fd < 0 {
return Err(std::io::Error::last_os_error());
}
let ret = libc::ioctl(fd, TUNSETIFF, &ifr);
if ret < 0 {
libc::close(fd);
return Err(std::io::Error::last_os_error());
}
let ret = libc::ioctl(fd, TUNSETPERSIST, 1);
if ret < 0 {
libc::close(fd);
return Err(std::io::Error::last_os_error());
}
Ok(Self { tap_fd: fd })
}
}
pub fn read(&self, buf: &mut [u8]) -> std::io::Result<usize> {
let ret = unsafe { libc::read(self.tap_fd, buf.as_mut_ptr() as *mut libc::c_void, buf.len()) };
if ret < 0 {
Err(std::io::Error::last_os_error())
} else {
Ok(ret as usize)
}
}
pub fn write(&self, buf: &[u8]) -> std::io::Result<usize> {
let ret = unsafe { libc::write(self.tap_fd, buf.as_ptr() as *const libc::c_void, buf.len()) };
if ret < 0 {
Err(std::io::Error::last_os_error())
} else {
Ok(ret as usize)
}
}
}
impl Drop for RawTap {
fn drop(&mut self) {
unsafe {
libc::close(self.tap_fd);
}
}
}
impl AsRawFd for RawTap {
fn as_raw_fd(&self) -> std::os::unix::io::RawFd {
self.tap_fd
}
}
#[derive(Debug)]
pub struct Tap {
inner: AsyncFd<RawTap>,
}
impl Tap {
pub fn new(ifname: &str) -> std::io::Result<Self> {
let tap = RawTap::new(ifname)?;
let inner = AsyncFd::with_interest(tap, Interest::READABLE | Interest::WRITABLE)?;
Ok(Self { inner })
}
pub async fn read(&self, buf: &mut [u8]) -> std::io::Result<usize> {
loop {
let mut guard = self.inner.readable().await?;
match guard.try_io(|inner| inner.get_ref().read(buf)) {
Ok(result) => return result,
Err(_would_block) => continue,
}
}
}
pub async fn write(&self, buf: &[u8]) -> std::io::Result<usize> {
loop {
let mut guard = self.inner.writable().await?;
match guard.try_io(|inner| inner.get_ref().write(buf)) {
Ok(result) => return result,
Err(_would_block) => continue,
}
}
}
}