Skip to content

Commit

Permalink
Merge pull request #34 from pkts-rs/ungate-async
Browse files Browse the repository at this point in the history
Remove non-windows requirement for `async-io`/`tokio`
  • Loading branch information
nathaniel-bennett authored Oct 28, 2024
2 parents 7def922 + f15b68f commit a501303
Show file tree
Hide file tree
Showing 10 changed files with 126 additions and 109 deletions.
15 changes: 3 additions & 12 deletions src/async_io/tap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#[cfg(target_os = "windows")]
use std::cmp;
use std::io;
#[cfg(target_os = "windows")]
use std::mem::ManuallyDrop;
Expand Down Expand Up @@ -85,6 +87,7 @@ impl AsSocket for TapWrapper {

/// A cross-platform asynchronous TAP interface, suitable for tunnelling link-layer packets.
pub struct AsyncTap {
#[cfg(not(target_os = "windows"))]
tap: Async<Tap>,
#[cfg(target_os = "windows")]
tap: Async<TapWrapper>,
Expand Down Expand Up @@ -248,15 +251,3 @@ impl AsyncTap {
self.tap.read_with(|inner| inner.0.recv(buf)).await
}
}

impl Drop for AsyncTap {
fn drop(&mut self) {
#[cfg(target_os = "windows")]
{
// This ensures that `UdpSocket` is dropped properly while not double-closing the RawFd.
// SAFETY: `self.io` won't be accessed after this thanks to ManuallyDrop
let io = unsafe { ManuallyDrop::take(&mut self.io) };
io.into_raw_fd();
}
}
}
21 changes: 5 additions & 16 deletions src/async_io/tun.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::io;
#[cfg(target_os = "windows")]
use std::mem::ManuallyDrop;
use std::cmp;
use std::io;
#[cfg(not(target_os = "windows"))]
use std::net::IpAddr;
#[cfg(target_os = "windows")]
Expand Down Expand Up @@ -78,12 +78,13 @@ impl TunWrapper {
#[cfg(target_os = "windows")]
impl AsSocket for TunWrapper {
fn as_socket(&self) -> BorrowedSocket<'_> {
unsafe { BorrowedSocket::borrow_raw(self.inner.read_handle() as RawSocket) }
unsafe { BorrowedSocket::borrow_raw(self.0.read_handle() as RawSocket) }
}
}

/// A cross-platform asynchronous TUN interface, suitable for tunnelling network-layer packets.
pub struct AsyncTun {
#[cfg(not(target_os = "windows"))]
tun: Async<Tun>,
#[cfg(target_os = "windows")]
tun: Async<TunWrapper>,
Expand All @@ -102,7 +103,7 @@ impl AsyncTun {
tun.set_nonblocking(true)?;

Ok(Self {
tun: Async::new(TunWrapper(tun)),
tun: Async::new(TunWrapper(tun))?,
})
}

Expand Down Expand Up @@ -247,15 +248,3 @@ impl AsyncTun {
self.tun.read_with(|inner| inner.0.recv(buf)).await
}
}

impl Drop for AsyncTun {
fn drop(&mut self) {
#[cfg(target_os = "windows")]
{
// This ensures that `UdpSocket` is dropped properly while not double-closing the RawFd.
// SAFETY: `self.io` won't be accessed after this thanks to ManuallyDrop
let io = unsafe { ManuallyDrop::take(&mut self.io) };
io.into_raw_fd();
}
}
}
19 changes: 12 additions & 7 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,15 +133,15 @@
// Show required OS/features on docs.rs.
#![cfg_attr(docsrs, feature(doc_auto_cfg))]

#[cfg(any(doc, all(feature = "async-io", not(target_os = "windows"))))]
#[cfg(any(doc, feature = "async-io"))]
pub mod async_io;
#[cfg(any(doc, target_os = "linux"))]
pub mod linux;
#[cfg(any(doc, target_os = "macos"))]
pub mod macos;
#[cfg(any(doc, all(feature = "mio", not(target_os = "windows"))))]
pub mod mio;
#[cfg(any(doc, all(feature = "tokio", not(target_os = "windows"))))]
#[cfg(any(doc, feature = "tokio"))]
pub mod tokio;
#[cfg(any(
doc,
Expand Down Expand Up @@ -220,6 +220,7 @@ use rtnetlink::{
))]
use sysctl::*;

#[cfg(target_os = "linux")]
const NETLINK_MAX_RECV: usize = 65536;

pub type Netmask = u8;
Expand Down Expand Up @@ -336,32 +337,36 @@ pub enum AddAddress {
}

impl AddAddress {
/// The IP address associated with the interface.
#[inline]
fn addr(&self) -> IpAddr {
pub fn addr(&self) -> IpAddr {
match self {
Self::V4(a) => a.addr.into(),
Self::V6(a) => a.addr.into(),
}
}

/// The broadcast address associated with the interface.
#[inline]
fn brd(&self) -> Option<IpAddr> {
pub fn brd(&self) -> Option<IpAddr> {
match self {
Self::V4(a) => Some(a.brd?.into()),
Self::V6(a) => Some(a.brd?.into()),
}
}

/// The point-to-point destination address associated with the interface.
#[inline]
fn dst(&self) -> Option<IpAddr> {
pub fn dst(&self) -> Option<IpAddr> {
match self {
Self::V4(a) => Some(a.dst?.into()),
Self::V6(a) => Some(a.dst?.into()),
}
}

/// The netmask associated with the interface.
#[inline]
fn netmask(&self) -> Option<Netmask> {
pub fn netmask(&self) -> Option<Netmask> {
match self {
Self::V4(a) => a.netmask,
Self::V6(a) => a.netmask,
Expand Down Expand Up @@ -1669,7 +1674,7 @@ impl Interface {
}
}

#[cfg(not(target_os = "windows"))]
#[cfg(target_os = "linux")]
#[inline]
fn close_fd(fd: RawFd) {
unsafe {
Expand Down
8 changes: 4 additions & 4 deletions src/libc_extra.rs
Original file line number Diff line number Diff line change
Expand Up @@ -856,14 +856,14 @@ pub const IF_FAKE_MEDIA_LIST_MAX: usize = 27;

#[cfg(target_os = "macos")]
pub const SIOCIFCREATE: libc::c_ulong = _IOWR::<ifreq>(b'i', 120);
#[cfg(target_os = "macos")]
pub const SIOCIFCREATE2: libc::c_ulong = _IOWR::<ifreq>(b'i', 122);
//#[cfg(target_os = "macos")]
//pub const SIOCIFCREATE2: libc::c_ulong = _IOWR::<ifreq>(b'i', 122);
#[cfg(any(target_os = "openbsd", target_os = "netbsd"))]
pub const SIOCIFCREATE: libc::c_ulong = _IOW::<ifreq>(b'i', 122);
#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
pub const SIOCIFCREATE2: libc::c_ulong = _IOWR::<ifreq>(b'i', 124);
#[cfg(target_os = "macos")]
pub const SIOCGDRVSPEC: libc::c_ulong = _IOWR::<ifdrv>(b'i', 123);
//#[cfg(target_os = "macos")]
//pub const SIOCGDRVSPEC: libc::c_ulong = _IOWR::<ifdrv>(b'i', 123);
#[cfg(target_os = "macos")]
pub const SIOCSDRVSPEC: libc::c_ulong = _IOW::<ifdrv>(b'i', 123);
#[cfg(any(
Expand Down
9 changes: 0 additions & 9 deletions src/macos/utun.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,6 @@ pub struct iovec_const {
pub iov_len: libc::size_t,
}

// MacOS `route` utility uses this buffer size
#[cfg(not(doc))]
#[repr(C)]
#[allow(non_camel_case_types)]
struct rtmsg {
m_rtm: libc::rt_msghdr,
m_space: [u8; 512],
}

pub struct Utun {
fd: RawFd,
}
Expand Down
1 change: 1 addition & 0 deletions src/mio/tap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ use mio::{Interest, Registry, Token};
/// A cross-platform asynchronous TAP interface, suitable for tunnelling link-layer packets.
pub struct AsyncTap {
tap: Tap,
/// SAFETY: file descriptor/handle is closed when `tap` goes out of scope, so this doesn't need to.
io: ManuallyDrop<UdpSocket>,
}

Expand Down
72 changes: 44 additions & 28 deletions src/tokio/tap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#[cfg(target_os = "windows")]
use std::cmp;
use std::io;
#[cfg(target_os = "windows")]
use std::mem::ManuallyDrop;
Expand All @@ -29,12 +31,30 @@ use tokio::io::Interest;
#[cfg(target_os = "windows")]
use tokio::net::UdpSocket;

/// A convenience type used to make internal operations consistent between Windows and Unix.
#[cfg(target_os = "windows")]
struct TapWrapper(Tap);

#[cfg(target_os = "windows")]
impl TapWrapper {
/// Returns a reference to the underlying `Tap` function.
pub fn get_ref(&self) -> &Tap {
&self.0
}

/// Returns a reference to the underlying `Tap` function.
pub fn get_mut(&mut self) -> &mut Tap {
&mut self.0
}
}

/// A cross-platform asynchronous TAP interface, suitable for tunnelling link-layer packets.
pub struct AsyncTap {
#[cfg(not(target_os = "windows"))]
tap: AsyncFd<Tap>,
#[cfg(target_os = "windows")]
tap: Tap,
tap: TapWrapper,
/// SAFETY: file descriptor/handle is closed when `tap` goes out of scope, so this doesn't need to.
#[cfg(target_os = "windows")]
io: ManuallyDrop<UdpSocket>,
}
Expand All @@ -46,28 +66,32 @@ impl AsyncTap {
Self::new_impl()
}

#[cfg(target_os = "windows")]
#[cfg(not(target_os = "windows"))]
fn new_impl() -> io::Result<Self> {
let mut tap = Tap::new()?;
tap.set_nonblocking(true)?;

// SAFETY: `AsyncTap` ensures that the RawFd is extracted from `io` in its drop()
// implementation so that the descriptor isn't closed twice.
let io = unsafe { UdpSocket::from_raw_socket(tap.read_handle() as RawSocket) };

Ok(Self {
tap,
io: ManuallyDrop::new(io),
tap: AsyncFd::new(tap)?,
})
}

#[cfg(not(target_os = "windows"))]
#[cfg(target_os = "windows")]
fn new_impl() -> io::Result<Self> {
let mut tap = Tap::new()?;
tap.set_nonblocking(true)?;

// SAFETY: `AsyncTap` ensures that the RawFd is extracted from `io` in its drop()
// implementation so that the descriptor isn't closed twice.
let io = unsafe {
UdpSocket::from_std(std::net::UdpSocket::from_raw_socket(
tap.read_handle() as RawSocket
))?
};

Ok(Self {
tap: AsyncFd::new(tap)?,
tap: TapWrapper(tap),
io: ManuallyDrop::new(io),
})
}

Expand Down Expand Up @@ -95,10 +119,14 @@ impl AsyncTap {
// SAFETY: `AsyncTap` ensures that the RawFd is extracted from `io` in its drop()
// implementation so that the descriptor isn't closed twice.
#[cfg(target_os = "windows")]
let io = unsafe { UdpSocket::from_raw_socket(tun.read_handle() as RawSocket) };
let io = unsafe {
UdpSocket::from_std(std::net::UdpSocket::from_raw_socket(
tap.read_handle() as RawSocket
))?
};

Ok(Self {
tap,
tap: TapWrapper(tap),
io: ManuallyDrop::new(io),
})
}
Expand Down Expand Up @@ -189,7 +217,7 @@ impl AsyncTap {
let mut timeout = 1; // Start with 1 millisecond timeout

loop {
match self.tun.send(buf) {
match self.tap.get_ref().send(buf) {
Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
tokio::time::sleep(Duration::from_millis(timeout)).await;
timeout = cmp::min(timeout * 2, SEND_MAX_BLOCKING_INTERVAL);
Expand Down Expand Up @@ -222,22 +250,10 @@ impl AsyncTap {
loop {
let mut guard = self.io.readable().await?;

match guard.try_io(Interest::READABLE, |inner| self.tap.recv(buf)) {
Ok(result) => return result,
Err(_would_block) => continue,
match guard.try_io(Interest::READABLE, || self.tap.get_ref().recv(buf)) {
Err(e) if e.kind() == io::ErrorKind::WouldBlock => continue,
res => return res,
}
}
}
}

impl Drop for AsyncTap {
fn drop(&mut self) {
#[cfg(target_os = "windows")]
{
// This ensures that `UdpSocket` is dropped properly while not double-closing the RawFd.
// SAFETY: `self.io` won't be accessed after this thanks to ManuallyDrop
let io = unsafe { ManuallyDrop::take(&mut self.io) };
io.into_raw_fd();
}
}
}
Loading

0 comments on commit a501303

Please sign in to comment.