pub struct SyncDevice(/* private fields */);Expand description
A transparent wrapper around DeviceImpl, providing synchronous I/O operations.
§Examples
Basic read/write operation:
use std::net::Ipv4Addr;
use tun_rs::DeviceBuilder;
fn main() -> std::io::Result<()> {
// Create a TUN device using the builder
let mut tun = DeviceBuilder::new()
.name("my-tun")
.ipv4(Ipv4Addr::new(10, 0, 0, 1), 24, None)
.build_sync()?;
// Send a packet
// Example IP packet (Replace with real IP message)
let packet = b"[IP Packet: 10.0.0.1 -> 10.0.0.2] Hello, TUN!";
tun.send(packet)?;
println!("Sent {} bytes IP packet", packet.len());
// Receive a packet
let mut buf = [0u8; 1500];
let n = tun.recv(&mut buf)?;
println!("Received {} bytes: {:?}", n, &buf[..n]);
Ok(())
}Implementations§
Source§impl SyncDevice
impl SyncDevice
Sourcepub unsafe fn from_fd(fd: RawFd) -> Result<Self>
Available on Unix only.
pub unsafe fn from_fd(fd: RawFd) -> Result<Self>
Creates a SyncDevice from a raw file descriptor.
§Safety
- The file descriptor (
fd) must be an owned file descriptor. - It must be valid and open.
- The file descriptor must refer to a TUN/TAP device.
- After calling this function, the
SyncDevicetakes ownership of the fd and will close it when dropped.
This function is only available on Unix platforms.
§Example
On iOS using PacketTunnelProvider:
use std::os::fd::RawFd;
use tun_rs::SyncDevice;
// On iOS, obtain fd from PacketTunnelProvider.packetFlow
// let fd: RawFd = packet_flow.value(forKeyPath: "socket.fileDescriptor") as! Int32
let fd: RawFd = 10; // Example value - obtain from platform VPN APIs
// SAFETY: fd must be a valid, open file descriptor to a TUN device
let dev = unsafe { SyncDevice::from_fd(fd)? };
// Device now owns the file descriptor
let mut buf = [0u8; 1500];
let n = dev.recv(&mut buf)?;
println!("Received {} bytes", n);On Android using VpnService:
use tun_rs::SyncDevice;
// On Android, obtain fd from VpnService.Builder.establish()
// ParcelFileDescriptor vpnInterface = builder.establish();
// int fd = vpnInterface.getFd();
let fd = 10; // Example value - obtain from VpnService
// SAFETY: fd must be valid and open
let dev = unsafe { SyncDevice::from_fd(fd)? };
let mut buf = [0u8; 1500];
loop {
let n = dev.recv(&mut buf)?;
// Process packet...
}Sourcepub fn recv(&self, buf: &mut [u8]) -> Result<usize>
pub fn recv(&self, buf: &mut [u8]) -> Result<usize>
Receives data from the device into the provided buffer.
Returns the number of bytes read, or an I/O error.
§Example
use std::net::Ipv4Addr;
use tun_rs::DeviceBuilder;
let mut tun = DeviceBuilder::new()
.name("my-tun")
.ipv4(Ipv4Addr::new(10, 0, 0, 1), 24, None)
.build_sync()
.unwrap();
let mut buf = [0u8; 1500];
tun.recv(&mut buf).unwrap();§Note
Blocking the current thread if no packet is available
Sourcepub fn send(&self, buf: &[u8]) -> Result<usize>
pub fn send(&self, buf: &[u8]) -> Result<usize>
Sends data from the provided buffer to the device.
Returns the number of bytes written, or an I/O error.
§Example
use std::net::Ipv4Addr;
use tun_rs::DeviceBuilder;
let mut tun = DeviceBuilder::new()
.name("my-tun")
.ipv4(Ipv4Addr::new(10, 0, 0, 1), 24, None)
.build_sync()
.unwrap();
tun.send(b"hello").unwrap();pub fn shutdown(&self) -> Result<()>
experimental only.Sourcepub fn recv_intr(&self, buf: &mut [u8], event: &InterruptEvent) -> Result<usize>
Available on crate feature interruptible only.
pub fn recv_intr(&self, buf: &mut [u8], event: &InterruptEvent) -> Result<usize>
interruptible only.Reads data into the provided buffer, with support for interruption.
This function attempts to read from the underlying file descriptor into buf,
and can be interrupted using the given InterruptEvent. If the event is triggered
while the read operation is blocked, the function will return early with
an error of kind std::io::ErrorKind::Interrupted.
§Arguments
buf- The buffer to store the read data.event- AnInterruptEventused to interrupt the blocking read.
§Returns
On success, returns the number of bytes read. On failure, returns an std::io::Error.
§Platform-specific Behavior
On Unix platforms, it is recommended to use this together with set_nonblocking(true).
Without setting non-blocking mode, concurrent reads may not respond properly to interrupt signals.
§Feature
This method is only available when the interruptible feature is enabled.
Sourcepub fn recv_intr_timeout(
&self,
buf: &mut [u8],
event: &InterruptEvent,
timeout: Option<Duration>,
) -> Result<usize>
Available on crate feature interruptible only.
pub fn recv_intr_timeout( &self, buf: &mut [u8], event: &InterruptEvent, timeout: Option<Duration>, ) -> Result<usize>
interruptible only.Like recv_intr, but with an optional timeout.
This function reads data from the device into the provided buffer, but can be interrupted by the given event or by the timeout expiring.
§Arguments
buf- The buffer to store the read dataevent- The interrupt event that can cancel the operationtimeout- Optional duration to wait before returning with a timeout error
§Returns
Ok(n)- Successfully readnbytesErr(e)withErrorKind::Interrupted- Operation was interrupted by the eventErr(e)withErrorKind::TimedOut- Timeout expired before data was available
§Example
use std::time::Duration;
use tun_rs::{DeviceBuilder, InterruptEvent};
let dev = DeviceBuilder::new()
.ipv4("10.0.0.1", 24, None)
.build_sync()?;
let event = InterruptEvent::new()?;
let mut buf = vec![0u8; 1500];
// Read with a 5-second timeout
match dev.recv_intr_timeout(&mut buf, &event, Some(Duration::from_secs(5))) {
Ok(n) => println!("Received {} bytes", n),
Err(e) if e.kind() == std::io::ErrorKind::TimedOut => {
println!("Timed out waiting for data");
}
Err(e) if e.kind() == std::io::ErrorKind::Interrupted => {
println!("Interrupted by event");
}
Err(e) => return Err(e),
}§Feature
This method is only available when the interruptible feature is enabled.
Sourcepub fn recv_vectored_intr(
&self,
bufs: &mut [IoSliceMut<'_>],
event: &InterruptEvent,
) -> Result<usize>
Available on Unix and crate feature interruptible only.
pub fn recv_vectored_intr( &self, bufs: &mut [IoSliceMut<'_>], event: &InterruptEvent, ) -> Result<usize>
interruptible only.Sourcepub fn recv_vectored_intr_timeout(
&self,
bufs: &mut [IoSliceMut<'_>],
event: &InterruptEvent,
timeout: Option<Duration>,
) -> Result<usize>
Available on Unix and crate feature interruptible only.
pub fn recv_vectored_intr_timeout( &self, bufs: &mut [IoSliceMut<'_>], event: &InterruptEvent, timeout: Option<Duration>, ) -> Result<usize>
interruptible only.Like recv_vectored_intr, but with an optional timeout.
This function reads data from the device into multiple buffers using vectored I/O, but can be interrupted by the given event or by the timeout expiring.
§Arguments
bufs- Multiple buffers to store the read dataevent- The interrupt event that can cancel the operationtimeout- Optional duration to wait before returning with a timeout error
§Returns
Ok(n)- Successfully readnbytes total across all buffersErr(e)withErrorKind::Interrupted- Operation was interrupted by the eventErr(e)withErrorKind::TimedOut- Timeout expired before data was available
§Example
use std::io::IoSliceMut;
use std::time::Duration;
use tun_rs::{DeviceBuilder, InterruptEvent};
let dev = DeviceBuilder::new()
.ipv4("10.0.0.1", 24, None)
.build_sync()?;
let event = InterruptEvent::new()?;
let mut header = [0u8; 20];
let mut payload = [0u8; 1480];
let mut bufs = [IoSliceMut::new(&mut header), IoSliceMut::new(&mut payload)];
// Read with timeout into multiple buffers
match dev.recv_vectored_intr_timeout(&mut bufs, &event, Some(Duration::from_secs(5))) {
Ok(n) => println!("Received {} bytes", n),
Err(e) if e.kind() == std::io::ErrorKind::TimedOut => {
println!("Timed out");
}
Err(e) => return Err(e),
}§Feature
This method is only available when the interruptible feature is enabled.
pub fn wait_readable_intr(&self, event: &InterruptEvent) -> Result<()>
interruptible only.Sourcepub fn wait_readable_intr_timeout(
&self,
event: &InterruptEvent,
timeout: Option<Duration>,
) -> Result<()>
Available on crate feature interruptible only.
pub fn wait_readable_intr_timeout( &self, event: &InterruptEvent, timeout: Option<Duration>, ) -> Result<()>
interruptible only.Like wait_readable_intr, but with an optional timeout.
This function waits until the device becomes readable, but can be interrupted by the given event or by the timeout expiring.
§Arguments
event- The interrupt event that can cancel the waittimeout- Optional duration to wait before returning with a timeout error
§Returns
Ok(())- Device is now readableErr(e)withErrorKind::Interrupted- Wait was interrupted by the eventErr(e)withErrorKind::TimedOut- Timeout expired
§Example
use std::time::Duration;
use tun_rs::{DeviceBuilder, InterruptEvent};
let dev = DeviceBuilder::new()
.ipv4("10.0.0.1", 24, None)
.build_sync()?;
let event = InterruptEvent::new()?;
// Wait for readability with timeout
match dev.wait_readable_intr_timeout(&event, Some(Duration::from_secs(10))) {
Ok(()) => {
println!("Device is readable");
// Now try to read...
}
Err(e) if e.kind() == std::io::ErrorKind::TimedOut => {
println!("Timed out waiting for data");
}
Err(e) => return Err(e),
}§Feature
This method is only available when the interruptible feature is enabled.
pub fn send_intr(&self, buf: &[u8], event: &InterruptEvent) -> Result<usize>
interruptible only.Sourcepub fn send_vectored_intr(
&self,
bufs: &[IoSlice<'_>],
event: &InterruptEvent,
) -> Result<usize>
Available on Unix and crate feature interruptible only.
pub fn send_vectored_intr( &self, bufs: &[IoSlice<'_>], event: &InterruptEvent, ) -> Result<usize>
interruptible only.Sends data to the device from multiple buffers using vectored I/O, with interruption support.
Like send_intr, but uses writev to send from multiple
non-contiguous buffers in a single operation.
§Arguments
bufs- Multiple buffers containing the data to sendevent- The interrupt event that can cancel the operation
§Returns
Ok(n)- Successfully sentnbytes total from all buffersErr(e)withErrorKind::Interrupted- Operation was interrupted by the event
§Example
use std::io::IoSlice;
use tun_rs::{DeviceBuilder, InterruptEvent};
let dev = DeviceBuilder::new()
.ipv4("10.0.0.1", 24, None)
.build_sync()?;
let event = InterruptEvent::new()?;
let header = [0x45, 0x00, 0x00, 0x14]; // IPv4 header
let payload = b"Hello, TUN!";
let bufs = [IoSlice::new(&header), IoSlice::new(payload)];
match dev.send_vectored_intr(&bufs, &event) {
Ok(n) => println!("Sent {} bytes", n),
Err(e) if e.kind() == std::io::ErrorKind::Interrupted => {
println!("Send was interrupted");
}
Err(e) => return Err(e),
}§Feature
This method is only available when the interruptible feature is enabled.
Sourcepub fn wait_writable_intr(&self, event: &InterruptEvent) -> Result<()>
Available on Unix and crate feature interruptible only.
pub fn wait_writable_intr(&self, event: &InterruptEvent) -> Result<()>
interruptible only.Waits for the device to become writable, with interruption support.
This function waits until the device is ready to accept data for sending, but can be interrupted by the given event.
§Arguments
event- The interrupt event that can cancel the wait
§Returns
Ok(())- Device is now writableErr(e)withErrorKind::Interrupted- Wait was interrupted by the event
§Example
use tun_rs::{DeviceBuilder, InterruptEvent};
let dev = DeviceBuilder::new()
.ipv4("10.0.0.1", 24, None)
.build_sync()?;
let event = InterruptEvent::new()?;
// Wait for device to be writable
match dev.wait_writable_intr(&event) {
Ok(()) => {
println!("Device is writable");
// Now send data...
}
Err(e) if e.kind() == std::io::ErrorKind::Interrupted => {
println!("Wait was interrupted");
}
Err(e) => return Err(e),
}§Feature
This method is only available when the interruptible feature is enabled.
Sourcepub fn recv_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> Result<usize>
Available on Unix only.
pub fn recv_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> Result<usize>
Receives data from the device into multiple buffers using vectored I/O.
Note: This method operates on a single packet only. It will only read data from one packet, even if multiple buffers are provided.
Returns the total number of bytes read from the packet, or an error.
§Example
use std::io::IoSliceMut;
use tun_rs::DeviceBuilder;
let dev = DeviceBuilder::new()
.ipv4("10.0.0.1", 24, None)
.build_sync()?;
// Prepare multiple buffers for receiving data
let mut header = [0u8; 20];
let mut payload = [0u8; 1480];
let mut bufs = [IoSliceMut::new(&mut header), IoSliceMut::new(&mut payload)];
// Read one packet into the buffers
let n = dev.recv_vectored(&mut bufs)?;
println!("Received {} bytes total", n);Sourcepub fn send_vectored(&self, bufs: &[IoSlice<'_>]) -> Result<usize>
Available on Unix only.
pub fn send_vectored(&self, bufs: &[IoSlice<'_>]) -> Result<usize>
Sends data to the device from multiple buffers using vectored I/O.
Note: This method operates on a single packet only. It will only send the data contained in the provided buffers as one packet.
Returns the total number of bytes written for the packet, or an error.
§Example
use std::io::IoSlice;
use tun_rs::DeviceBuilder;
let dev = DeviceBuilder::new()
.ipv4("10.0.0.1", 24, None)
.build_sync()?;
// Send a packet with header and payload in separate buffers
let header = [0x45, 0x00, 0x00, 0x14]; // IPv4 header
let payload = b"Hello, TUN!";
let bufs = [IoSlice::new(&header), IoSlice::new(payload)];
let n = dev.send_vectored(&bufs)?;
println!("Sent {} bytes", n);Sourcepub fn is_nonblocking(&self) -> Result<bool>
Available on Unix only.
pub fn is_nonblocking(&self) -> Result<bool>
Checks whether the device is currently operating in nonblocking mode.
Returns true if nonblocking mode is enabled, false otherwise, or an error.
§Example
use tun_rs::DeviceBuilder;
let dev = DeviceBuilder::new()
.ipv4("10.0.0.1", 24, None)
.build_sync()?;
// Check current nonblocking mode
if dev.is_nonblocking()? {
println!("Device is in nonblocking mode");
} else {
println!("Device is in blocking mode");
}Sourcepub fn set_nonblocking(&self, nonblocking: bool) -> Result<()>
Available on Unix only.
pub fn set_nonblocking(&self, nonblocking: bool) -> Result<()>
Sets the nonblocking mode for the device.
nonblocking: Passtrueto enable nonblocking mode,falseto disable.
Returns an empty result on success or an I/O error.
§Example
use tun_rs::DeviceBuilder;
let dev = DeviceBuilder::new()
.ipv4("10.0.0.1", 24, None)
.build_sync()?;
// Enable nonblocking mode for non-blocking I/O
dev.set_nonblocking(true)?;
// Now recv() will return WouldBlock if no data is available
let mut buf = [0u8; 1500];
match dev.recv(&mut buf) {
Ok(n) => println!("Received {} bytes", n),
Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => {
println!("No data available");
}
Err(e) => return Err(e),
}Sourcepub fn try_clone(&self) -> Result<SyncDevice>
Available on Linux and non-target_env=ohos only.
pub fn try_clone(&self) -> Result<SyncDevice>
target_env=ohos only.Creates a new queue for multi-queue TUN/TAP devices on Linux.
§Prerequisites
- The
IFF_MULTI_QUEUEflag must be enabled (via.multi_queue(true)in DeviceBuilder). - The system must support network interface multi-queue functionality.
§Description
When multi-queue is enabled, create a new queue by duplicating an existing one. This allows parallel packet processing across multiple threads/CPU cores.
§Example
use std::thread;
use tun_rs::DeviceBuilder;
let dev = DeviceBuilder::new()
.ipv4("10.0.0.1", 24, None)
.with(|builder| {
builder.multi_queue(true) // Enable multi-queue support
})
.build_sync()?;
// Clone the device to create a new queue
let dev_clone = dev.try_clone()?;
// Use the cloned device in another thread for parallel processing
thread::spawn(move || {
let mut buf = [0u8; 1500];
loop {
if let Ok(n) = dev_clone.recv(&mut buf) {
println!("Thread 2 received {} bytes", n);
}
}
});
// Process packets in the main thread
let mut buf = [0u8; 1500];
loop {
let n = dev.recv(&mut buf)?;
println!("Thread 1 received {} bytes", n);
}Source§impl SyncDevice
impl SyncDevice
pub fn send_multiple_intr<B: ExpandBuffer>( &self, gro_table: &mut GROTable, bufs: &mut [B], offset: usize, event: &InterruptEvent, ) -> Result<usize>
target_env=ohos and crate feature interruptible only.pub fn recv_multiple_intr<B: AsRef<[u8]> + AsMut<[u8]>>( &self, original_buffer: &mut [u8], bufs: &mut [B], sizes: &mut [usize], offset: usize, event: &InterruptEvent, ) -> Result<usize>
target_env=ohos and crate feature interruptible only.Methods from Deref<Target = DeviceImpl>§
Sourcepub fn if_index(&self) -> Result<u32>
Available on Unix and (Linux and non-target_env=ohos, or macOS, or FreeBSD, or OpenBSD, or NetBSD) only.
pub fn if_index(&self) -> Result<u32>
target_env=ohos, or macOS, or FreeBSD, or OpenBSD, or NetBSD) only.Retrieves the interface index for the network interface.
This function converts the interface name (obtained via self.name()) into a
C-compatible string (CString) and then calls the libc function if_nametoindex
to retrieve the corresponding interface index.
Sourcepub fn addresses(&self) -> Result<Vec<IpAddr>>
Available on Unix and (Linux and non-target_env=ohos, or macOS, or FreeBSD, or OpenBSD, or NetBSD) only.
pub fn addresses(&self) -> Result<Vec<IpAddr>>
target_env=ohos, or macOS, or FreeBSD, or OpenBSD, or NetBSD) only.Retrieves all IP addresses associated with the network interface.
This function calls getifaddrs with the interface name,
then iterates over the returned list of interface addresses, extracting and collecting
the IP addresses into a vector.
Sourcepub fn udp_gso(&self) -> bool
Available on Linux and non-target_env=ohos only.
pub fn udp_gso(&self) -> bool
target_env=ohos only.Returns whether UDP Generic Segmentation Offload (GSO) is enabled.
This is determined by the udp_gso flag in the device.
Sourcepub fn tcp_gso(&self) -> bool
Available on Linux and non-target_env=ohos only.
pub fn tcp_gso(&self) -> bool
target_env=ohos only.Returns whether TCP Generic Segmentation Offload (GSO) is enabled.
In this implementation, this is represented by the vnet_hdr flag.
Sourcepub fn set_tx_queue_len(&self, tx_queue_len: u32) -> Result<()>
Available on Linux and non-target_env=ohos only.
pub fn set_tx_queue_len(&self, tx_queue_len: u32) -> Result<()>
target_env=ohos only.Sets the transmit queue length for the network interface.
This method constructs an interface request (ifreq) structure,
assigns the desired transmit queue length to the ifru_metric field,
and calls the change_tx_queue_len function using the control file descriptor.
If the underlying operation fails, an I/O error is returned.
Sourcepub fn tx_queue_len(&self) -> Result<u32>
Available on Linux and non-target_env=ohos only.
pub fn tx_queue_len(&self) -> Result<u32>
target_env=ohos only.Retrieves the current transmit queue length for the network interface.
This function constructs an interface request structure and calls tx_queue_len
to populate it with the current transmit queue length. The value is then returned.
Sourcepub fn persist(&self) -> Result<()>
Available on Linux and non-target_env=ohos only.
pub fn persist(&self) -> Result<()>
target_env=ohos only.Make the device persistent.
By default, TUN/TAP devices are destroyed when the process exits. Calling this method makes the device persist after the program terminates, allowing it to be reused by other processes.
§Example
use tun_rs::DeviceBuilder;
let dev = DeviceBuilder::new()
.name("persistent-tun")
.ipv4("10.0.0.1", 24, None)
.build_sync()?;
// Make the device persistent so it survives after program exit
dev.persist()?;
println!("Device will persist after program exits");Sourcepub fn user(&self, value: i32) -> Result<()>
Available on Linux and non-target_env=ohos only.
pub fn user(&self, value: i32) -> Result<()>
target_env=ohos only.Set the owner (UID) of the device.
This allows non-root users to access the TUN/TAP device.
§Example
use tun_rs::DeviceBuilder;
let dev = DeviceBuilder::new()
.ipv4("10.0.0.1", 24, None)
.build_sync()?;
// Set ownership to UID 1000 (typical first user on Linux)
dev.user(1000)?;
println!("Device ownership set to UID 1000");Sourcepub fn group(&self, value: i32) -> Result<()>
Available on Linux and non-target_env=ohos only.
pub fn group(&self, value: i32) -> Result<()>
target_env=ohos only.Set the group (GID) of the device.
This allows members of a specific group to access the TUN/TAP device.
§Example
use tun_rs::DeviceBuilder;
let dev = DeviceBuilder::new()
.ipv4("10.0.0.1", 24, None)
.build_sync()?;
// Set group ownership to GID 1000
dev.group(1000)?;
println!("Device group ownership set to GID 1000");Sourcepub fn send_multiple<B: ExpandBuffer>(
&self,
gro_table: &mut GROTable,
bufs: &mut [B],
offset: usize,
) -> Result<usize>
Available on Linux and non-target_env=ohos only.
pub fn send_multiple<B: ExpandBuffer>( &self, gro_table: &mut GROTable, bufs: &mut [B], offset: usize, ) -> Result<usize>
target_env=ohos only.Sends multiple packets in a batch with GRO (Generic Receive Offload) coalescing.
This method allows efficient transmission of multiple packets by batching them together and applying GRO optimizations. When offload is enabled, packets may be coalesced to reduce system call overhead and improve throughput.
§Arguments
gro_table- A mutable reference to aGROTablethat manages packet coalescing state. This table can be reused across multiple calls to amortize allocation overhead.bufs- A mutable slice of buffers containing the packets to send. Each buffer must implement theExpandBuffertrait.offset- The byte offset within each buffer where the packet data begins. Must be >=VIRTIO_NET_HDR_LENto accommodate the virtio network header when offload is enabled.
§Returns
Returns the total number of bytes successfully sent, or an I/O error.
§Example
use tun_rs::{DeviceBuilder, GROTable, VIRTIO_NET_HDR_LEN};
let dev = DeviceBuilder::new()
.ipv4("10.0.0.1", 24, None)
.with(|builder| {
builder.offload(true) // Enable offload for GRO
})
.build_sync()?;
let mut gro_table = GROTable::default();
let offset = VIRTIO_NET_HDR_LEN;
// Prepare packets to send
let mut packet1 = vec![0u8; offset + 100]; // VIRTIO_NET_HDR + packet data
let mut packet2 = vec![0u8; offset + 200];
// Fill in packet data at offset...
let mut bufs = vec![packet1, packet2];
// Send all packets in one batch
let bytes_sent = dev.send_multiple(&mut gro_table, &mut bufs, offset)?;
println!("Sent {} bytes across {} packets", bytes_sent, bufs.len());§Platform
This method is only available on Linux.
§Performance Notes
- Use
IDEAL_BATCH_SIZEfor optimal batch size (typically 128 packets) - Reuse the same
GROTableinstance across calls to avoid allocations - Enable offload via
.offload(true)inDeviceBuilderfor best performance
Sourcepub fn recv_multiple<B: AsRef<[u8]> + AsMut<[u8]>>(
&self,
original_buffer: &mut [u8],
bufs: &mut [B],
sizes: &mut [usize],
offset: usize,
) -> Result<usize>
Available on Linux and non-target_env=ohos only.
pub fn recv_multiple<B: AsRef<[u8]> + AsMut<[u8]>>( &self, original_buffer: &mut [u8], bufs: &mut [B], sizes: &mut [usize], offset: usize, ) -> Result<usize>
target_env=ohos only.Receives multiple packets in a batch with GSO (Generic Segmentation Offload) splitting.
When offload is enabled, this method can receive large GSO packets from the TUN device and automatically split them into MTU-sized segments, significantly improving receive performance for high-bandwidth traffic.
§Arguments
original_buffer- A mutable buffer to store the raw received data, including the virtio network header and the potentially large GSO packet. Recommended size isVIRTIO_NET_HDR_LEN + 65535bytes.bufs- A mutable slice of buffers to store the segmented packets. Each buffer will receive one MTU-sized packet after GSO splitting.sizes- A mutable slice to store the actual size of each packet inbufs. Must have the same length asbufs.offset- The byte offset within each output buffer where packet data should be written. This allows for pre-allocated header space.
§Returns
Returns the number of packets successfully received and split, or an I/O error.
§Example
use tun_rs::{DeviceBuilder, VIRTIO_NET_HDR_LEN, IDEAL_BATCH_SIZE};
let dev = DeviceBuilder::new()
.ipv4("10.0.0.1", 24, None)
.with(|builder| {
builder.offload(true) // Enable offload for GSO
})
.build_sync()?;
// Buffer for the raw received packet (with virtio header)
let mut original_buffer = vec![0u8; VIRTIO_NET_HDR_LEN + 65535];
// Output buffers for segmented packets
let mut bufs = vec![vec![0u8; 1500]; IDEAL_BATCH_SIZE];
let mut sizes = vec![0usize; IDEAL_BATCH_SIZE];
let offset = 0;
loop {
// Receive and segment packets
let num_packets = dev.recv_multiple(
&mut original_buffer,
&mut bufs,
&mut sizes,
offset
)?;
// Process each segmented packet
for i in 0..num_packets {
let packet = &bufs[i][offset..offset + sizes[i]];
println!("Received packet {}: {} bytes", i, sizes[i]);
// Process packet...
}
}§Platform
This method is only available on Linux.
§Performance Notes
- Use
IDEAL_BATCH_SIZE(128) for the number of output buffers - A single
recv_multiplecall may return multiple MTU-sized packets from one large GSO packet - The performance benefit is most noticeable with TCP traffic using large send/receive windows
pub fn remove_address_v6_impl(&self, addr: Ipv6Addr, prefix: u8) -> Result<()>
target_env=ohos only.Sourcepub fn name(&self) -> Result<String>
Available on Linux and non-target_env=ohos only.
pub fn name(&self) -> Result<String>
target_env=ohos only.Retrieves the name of the network interface.
pub fn remove_address_v6(&self, addr: Ipv6Addr, prefix: u8) -> Result<()>
target_env=ohos only.Sourcepub fn set_name(&self, value: &str) -> Result<()>
Available on Linux and non-target_env=ohos only.
pub fn set_name(&self, value: &str) -> Result<()>
target_env=ohos only.Sets a new name for the network interface.
This function converts the provided name into a C-compatible string,
checks that its length does not exceed the maximum allowed (IFNAMSIZ),
and then copies it into an interface request structure. It then uses a system call
(via siocsifname) to apply the new name.
§Example
use tun_rs::DeviceBuilder;
let dev = DeviceBuilder::new()
.name("tun0")
.ipv4("10.0.0.1", 24, None)
.build_sync()?;
// Rename the device
dev.set_name("vpn-tun")?;
println!("Device renamed to vpn-tun");Sourcepub fn is_running(&self) -> Result<bool>
Available on Linux and non-target_env=ohos only.
pub fn is_running(&self) -> Result<bool>
target_env=ohos only.Checks whether the network interface is currently running.
The interface is considered running if both the IFF_UP and IFF_RUNNING flags are set.
Sourcepub fn enabled(&self, value: bool) -> Result<()>
Available on Linux and non-target_env=ohos only.
pub fn enabled(&self, value: bool) -> Result<()>
target_env=ohos only.Enables or disables the network interface.
If value is true, the interface is enabled by setting the IFF_UP and IFF_RUNNING flags.
If false, the IFF_UP flag is cleared. The change is applied using a system call.
Sourcepub fn broadcast(&self) -> Result<IpAddr>
Available on Linux and non-target_env=ohos only.
pub fn broadcast(&self) -> Result<IpAddr>
target_env=ohos only.Retrieves the broadcast address of the network interface.
This function populates an interface request with the broadcast address via a system call, converts it into a sockaddr structure, and then extracts the IP address.
§Example
use tun_rs::DeviceBuilder;
let dev = DeviceBuilder::new()
.ipv4("10.0.0.1", 24, None)
.build_sync()?;
// Get the broadcast address
let broadcast = dev.broadcast()?;
println!("Broadcast address: {}", broadcast);Sourcepub fn set_broadcast(&self, value: IpAddr) -> Result<()>
Available on Linux and non-target_env=ohos only.
pub fn set_broadcast(&self, value: IpAddr) -> Result<()>
target_env=ohos only.Sets the broadcast address of the network interface.
This function converts the given IP address into a sockaddr structure (with a specified overwrite size) and then applies it to the interface via a system call.
Sourcepub fn set_network_address<IPv4: ToIpv4Address, Netmask: ToIpv4Netmask>(
&self,
address: IPv4,
netmask: Netmask,
destination: Option<IPv4>,
) -> Result<()>
Available on Linux and non-target_env=ohos only.
pub fn set_network_address<IPv4: ToIpv4Address, Netmask: ToIpv4Netmask>( &self, address: IPv4, netmask: Netmask, destination: Option<IPv4>, ) -> Result<()>
target_env=ohos only.Sets the IPv4 network address, netmask, and an optional destination address. Remove all previous set IPv4 addresses and set the specified address.
§Example
use tun_rs::DeviceBuilder;
let dev = DeviceBuilder::new()
.ipv4("10.0.0.1", 24, None)
.build_sync()?;
// Change the primary IPv4 address
dev.set_network_address("10.1.0.1", 24, None)?;
println!("Updated device address to 10.1.0.1/24");Sourcepub fn add_address_v4<IPv4: ToIpv4Address, Netmask: ToIpv4Netmask>(
&self,
address: IPv4,
netmask: Netmask,
) -> Result<()>
Available on Linux and non-target_env=ohos only.
pub fn add_address_v4<IPv4: ToIpv4Address, Netmask: ToIpv4Netmask>( &self, address: IPv4, netmask: Netmask, ) -> Result<()>
target_env=ohos only.Add IPv4 network address and netmask to the interface.
This allows multiple IPv4 addresses on a single TUN/TAP device.
§Example
use tun_rs::DeviceBuilder;
let dev = DeviceBuilder::new()
.ipv4("10.0.0.1", 24, None)
.build_sync()?;
// Add additional IPv4 addresses
dev.add_address_v4("10.0.1.1", 24)?;
dev.add_address_v4("10.0.2.1", 24)?;
println!("Added multiple IPv4 addresses");Sourcepub fn remove_address(&self, addr: IpAddr) -> Result<()>
Available on Linux and non-target_env=ohos only.
pub fn remove_address(&self, addr: IpAddr) -> Result<()>
target_env=ohos only.Removes an IP address from the interface.
For IPv4 addresses, it iterates over the current addresses and if a match is found,
resets the address to 0.0.0.0 (unspecified).
For IPv6 addresses, it retrieves the interface addresses by name and removes the matching address,
taking into account its prefix length.
§Example
use std::net::IpAddr;
use tun_rs::DeviceBuilder;
let dev = DeviceBuilder::new()
.ipv4("10.0.0.1", 24, None)
.build_sync()?;
// Add an additional address
dev.add_address_v4("10.0.1.1", 24)?;
// Later, remove it
dev.remove_address("10.0.1.1".parse::<IpAddr>().unwrap())?;
println!("Removed address 10.0.1.1");Sourcepub fn add_address_v6<IPv6: ToIpv6Address, Netmask: ToIpv6Netmask>(
&self,
addr: IPv6,
netmask: Netmask,
) -> Result<()>
Available on Linux and non-target_env=ohos only.
pub fn add_address_v6<IPv6: ToIpv6Address, Netmask: ToIpv6Netmask>( &self, addr: IPv6, netmask: Netmask, ) -> Result<()>
target_env=ohos only.Adds an IPv6 address to the interface.
This function creates an in6_ifreq structure, fills in the interface index,
prefix length, and IPv6 address (converted into a sockaddr structure),
and then applies it using a system call.
§Example
use tun_rs::DeviceBuilder;
let dev = DeviceBuilder::new()
.ipv4("10.0.0.1", 24, None)
.build_sync()?;
// Add IPv6 addresses
dev.add_address_v6("fd00::1", 64)?;
dev.add_address_v6("fd00::2", 64)?;
println!("Added IPv6 addresses");Sourcepub fn mtu(&self) -> Result<u16>
Available on Linux and non-target_env=ohos only.
pub fn mtu(&self) -> Result<u16>
target_env=ohos only.Retrieves the current MTU (Maximum Transmission Unit) for the interface.
This function constructs an interface request and uses a system call (via siocgifmtu)
to obtain the MTU. The result is then converted to a u16.
Sourcepub fn set_mtu(&self, value: u16) -> Result<()>
Available on Linux and non-target_env=ohos only.
pub fn set_mtu(&self, value: u16) -> Result<()>
target_env=ohos only.Sets the MTU (Maximum Transmission Unit) for the interface.
This function creates an interface request, sets the ifru_mtu field to the new value,
and then applies it via a system call.
§Example
use tun_rs::DeviceBuilder;
let dev = DeviceBuilder::new()
.ipv4("10.0.0.1", 24, None)
.mtu(1400)
.build_sync()?;
// Change MTU to accommodate larger packets
dev.set_mtu(9000)?; // Jumbo frames
println!("MTU set to 9000 bytes");Sourcepub fn set_mac_address(&self, eth_addr: [u8; 6]) -> Result<()>
Available on Linux and non-target_env=ohos only.
pub fn set_mac_address(&self, eth_addr: [u8; 6]) -> Result<()>
target_env=ohos only.Sets the MAC (hardware) address for the interface.
This function constructs an interface request and copies the provided MAC address into the hardware address field. It then applies the change via a system call. This operation is typically supported only for TAP devices.
Sourcepub fn mac_address(&self) -> Result<[u8; 6]>
Available on Linux and non-target_env=ohos only.
pub fn mac_address(&self) -> Result<[u8; 6]>
target_env=ohos only.Retrieves the MAC (hardware) address of the interface.
This function queries the MAC address by the interface name using a helper function. An error is returned if the MAC address cannot be found.
Trait Implementations§
Source§impl AsFd for SyncDevice
Available on Unix only.
impl AsFd for SyncDevice
Source§fn as_fd(&self) -> BorrowedFd<'_>
fn as_fd(&self) -> BorrowedFd<'_>
Source§impl AsRawFd for SyncDevice
Available on Unix only.
impl AsRawFd for SyncDevice
Source§impl Deref for SyncDevice
impl Deref for SyncDevice
Source§impl FromRawFd for SyncDevice
Available on Unix only.
impl FromRawFd for SyncDevice
Source§unsafe fn from_raw_fd(fd: RawFd) -> Self
unsafe fn from_raw_fd(fd: RawFd) -> Self
Self from the given raw file
descriptor. Read more