Skip to content

Commit

Permalink
implement vectored write and send
Browse files Browse the repository at this point in the history
  • Loading branch information
yaa110 committed Oct 29, 2023
1 parent 2fb2bf2 commit f72cdf9
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 5 deletions.
21 changes: 20 additions & 1 deletion src/linux/io.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::convert::From;
use std::io::{self, Read, Write};
use std::io::{self, IoSlice, Read, Write};
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};

pub struct TunIo(RawFd);
Expand Down Expand Up @@ -33,6 +33,10 @@ impl Write for TunIo {
self.send(buf)
}

fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
self.sendv(bufs)
}

fn flush(&mut self) -> io::Result<()> {
let ret = unsafe { libc::fsync(self.0) };
if ret < 0 {
Expand All @@ -58,6 +62,21 @@ impl TunIo {
}
Ok(n as _)
}

pub fn sendv(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
let iov = bufs
.iter()
.map(|buf| libc::iovec {
iov_base: buf.as_ptr() as *mut _,
iov_len: buf.len() as _,
})
.collect::<Vec<_>>();
let n = unsafe { libc::writev(self.0, iov.as_ptr() as *const _, iov.len() as _) };
if n < 0 {
return Err(io::Error::last_os_error());
}
Ok(n as _)
}
}

impl Drop for TunIo {
Expand Down
52 changes: 48 additions & 4 deletions src/tun.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::linux::params::Params;
use crate::Result;
use crate::TunBuilder;
use std::io;
use std::io::IoSlice;
use std::io::{Read, Write};
use std::net::Ipv4Addr;
use std::os::raw::c_char;
Expand Down Expand Up @@ -75,6 +76,26 @@ impl AsyncWrite for Tun {
}
}

fn poll_write_vectored(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
bufs: &[IoSlice<'_>],
) -> Poll<std::result::Result<usize, io::Error>> {
let self_mut = self.get_mut();
loop {
let mut guard = ready!(self_mut.io.poll_write_ready_mut(cx))?;

match guard.try_io(|inner| inner.get_mut().write_vectored(bufs)) {
Ok(result) => return Poll::Ready(result),
Err(_would_block) => continue,
}
}
}

fn is_write_vectored(&self) -> bool {
true
}

fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> task::Poll<io::Result<()>> {
let self_mut = self.get_mut();
loop {
Expand Down Expand Up @@ -142,7 +163,7 @@ impl Tun {
Ok(iface)
}

/// Receives a packet from the Tun/Tap interface
/// Receives a packet from the Tun/Tap interface.
///
/// This method takes &self, so it is possible to call this method concurrently with other methods on this struct.
pub async fn recv(&self, buf: &mut [u8]) -> io::Result<usize> {
Expand All @@ -156,7 +177,7 @@ impl Tun {
}
}

/// Sends a packet to the Tun/Tap interface
/// Sends a buffer to the Tun/Tap interface.
///
/// This method takes &self, so it is possible to call this method concurrently with other methods on this struct.
pub async fn send(&self, buf: &[u8]) -> io::Result<usize> {
Expand All @@ -170,7 +191,21 @@ impl Tun {
}
}

/// Try to receive a packet from the Tun/Tap interface
/// Sends several different buffers to the Tun/Tap interface.
///
/// This method takes &self, so it is possible to call this method concurrently with other methods on this struct.
pub async fn send_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
loop {
let mut guard = self.io.writable().await?;

match guard.try_io(|inner| inner.get_ref().sendv(bufs)) {
Ok(res) => return res,
Err(_) => continue,
}
}
}

/// Tries to receive a buffer from the Tun/Tap interface.
///
/// When there is no pending data, `Err(io::ErrorKind::WouldBlock)` is returned.
///
Expand All @@ -179,7 +214,7 @@ impl Tun {
self.io.get_ref().recv(buf)
}

/// Try to send a packet to the Tun/Tap interface
/// Tries to send a packet to the Tun/Tap interface.
///
/// When the socket buffer is full, `Err(io::ErrorKind::WouldBlock)` is returned.
///
Expand All @@ -188,6 +223,15 @@ impl Tun {
self.io.get_ref().send(buf)
}

/// Tries to send several different buffers to the Tun/Tap interface.
///
/// When the socket buffer is full, `Err(io::ErrorKind::WouldBlock)` is returned.
///
/// This method takes &self, so it is possible to call this method concurrently with other methods on this struct.
pub fn try_send_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
self.io.get_ref().sendv(bufs)
}

/// Returns the name of Tun/Tap device.
pub fn name(&self) -> &str {
self.iface.name()
Expand Down

0 comments on commit f72cdf9

Please sign in to comment.