<div align="center">
# 🚀 tun-rs
### High-Performance Cross-Platform TUN/TAP Library for Rust
[](https://crates.io/crates/tun-rs)
[](https://docs.rs/tun-rs/latest/tun_rs)
[](https://github.com/tun-rs/tun-rs/blob/master/LICENSE)
[](https://crates.io/crates/tun-rs)
[Features](#-key-features) • [Performance](#-performance-benchmarks) • [Installation](#-installation) • [Examples](#-examples) • [Platforms](#-supported-platforms)
</div>
---
## 📖 Overview
**tun-rs** is a powerful, production-ready Rust library for creating and managing TUN and TAP virtual network interfaces. Built with performance and cross-platform compatibility in mind, it provides both synchronous and asynchronous APIs to suit your application's needs.
### 🎯 Why Choose tun-rs?
- **🏆 Exceptional Performance**: Achieves up to **70.6 Gbps** throughput with concurrent operations and offload features
- **🌍 True Cross-Platform**: Consistent API across Windows, Linux, macOS, BSD, iOS, Android, and more
- **⚡ Async-Ready**: Native support for `tokio` and `async-io` runtimes
- **🔧 Feature-Rich**: Multiple IP addresses, DNS support, hardware offload, multi-queue, and more
- **🛡️ Production-Tested**: Used in real-world VPN and networking applications
- **📦 Zero Hassle**: Minimal dependencies and straightforward API design
## 🌟 Key Features
### Core Capabilities
- ✅ **Dual Mode Support**: Both TUN (Layer 3) and TAP (Layer 2) interfaces
- ✅ **Multiple IP Addresses**: Assign multiple IPv4 and IPv6 addresses to a single interface
- ✅ **Sync & Async APIs**: Choose between synchronous blocking or async non-blocking operations
- ✅ **Runtime Flexibility**: Optional `tokio` or `async-io` integration for async operations
### Platform-Specific Optimizations
- 🚀 **Hardware Offload**: TSO/GSO support on Linux for maximum throughput
- 🔀 **Multi-Queue**: Leverage multiple CPU cores with multi-queue support on Linux
- 🍎 **macOS TAP**: Native TAP implementation using `feth` pairs
- 🪟 **Windows DNS**: Full DNS configuration support on Windows
### Developer Experience
- 🎯 **Consistent Behavior**: Unified packet format across all platforms (no platform-specific headers)
- 🔄 **Automatic Routing**: Consistent route setup behavior when creating devices
- 🛑 **Graceful Shutdown**: Proper cleanup and shutdown support for sync operations
- 📝 **Rich Examples**: Comprehensive examples for common use cases
## 💻 Supported Platforms
| **Linux** | ✅ | ✅ | Full offload & multi-queue support |
| **Windows** | ✅ | ✅ | Requires wintun.dll / tap-windows |
| **macOS** | ✅ | ✅* | TAP via feth pairs |
| **FreeBSD** | ✅ | ✅ | Full support |
| **OpenBSD** | ✅ | ✅ | Full support |
| **NetBSD** | ✅ | ✅ | Full support |
| **Android** | ✅ | - | Via VpnService API |
| **iOS** | ✅ | - | Via NEPacketTunnelProvider |
| **tvOS** | ✅ | - | Via NEPacketTunnelProvider |
| **OpenHarmony** | ✅ | - | TUN support |
| **Other Unix*** | ✅ | - | Via raw file descriptor |
> **Note**: For unlisted Unix-like platforms, you can use the raw file descriptor API to integrate with platform-specific TUN implementations.
## 🚀 Performance Benchmarks
tun-rs delivers **exceptional performance** compared to other TUN implementations. Benchmarks conducted on Linux (Ubuntu 20.04, i7-13700K, DDR5 32GB) using [tun-benchmark2](https://github.com/tun-rs/tun-benchmark2) show impressive results:
### 🏆 Highlights
- **Peak Performance**: Up to **70.6 Gbps** with concurrent sync operations + offload
- **Best Async Performance**: **35.7 Gbps** async without channel buffering + offload
- **Optimized Async**: **31.4 Gbps** with BytesPool optimization
- **Rust vs Go**: Peak-to-peak, tun-rs achieves **2.3x higher throughput** (70.6 vs 30.1 Gbps)
### 📊 Performance Comparison
| **Sync + Offload + Concurrent** | 🥇 **70.6 Gbps** | 124% | 10.6 MB | 2748 |
| **Async + Offload** | 🥈 **35.7 Gbps** | 64.9% | 7.4 MB | 0 |
| **Async + Offload + BytesPool** | 🥉 **31.4 Gbps** | 93.0% | 16.0 MB | 0 |
| Sync + Offload + Channel | 29.5 Gbps | 90.4% | 14.9 MB | 0 |
| Go + Offload + BytesPool | 30.1 Gbps | 101.6% | 39.5 MB | 0 |
| Go + Offload | 28.8 Gbps | 64.1% | 4.2 MB | 0 |
| Async (no offload) | 8.84 Gbps | 87.6% | 3.7 MB | 326 |
### 💡 Key Takeaways
1. **Hardware Offload is Critical**: TSO/GSO increases throughput by 3-4x
2. **Concurrent I/O Scales**: Dual-threaded sync operations achieve the highest throughput
3. **Memory Efficiency**: tun-rs uses significantly less memory than Go alternatives
4. **Zero Retransmissions**: Offload configurations achieve reliable, lossless transmission
5. **Flexible Performance Profile**: Choose async for low CPU usage or sync+concurrent for maximum throughput
<details>
<summary>📈 View Full Benchmark Chart</summary>

</details>
> For detailed benchmark methodology and results, visit [tun-benchmark2](https://github.com/tun-rs/tun-benchmark2)
---
## 📦 Installation
Add tun-rs to your `Cargo.toml`:
```toml
[dependencies]
# Base synchronous API (no async runtime required)
tun-rs = "2"
# For Tokio async runtime
tun-rs = { version = "2", features = ["async"] }
# For async-std, smol, or other async-io based runtimes
tun-rs = { version = "2", features = ["async_io"] }
# For framed codec support (with tokio)
tun-rs = { version = "2", features = ["async", "async_framed"] }
```
---
## 🎓 Quick Start
### Basic Synchronous TUN Interface
Create and use a TUN interface in just a few lines:
```rust
use tun_rs::DeviceBuilder;
fn main() -> std::io::Result<()> {
// Create a TUN device with IPv4 and IPv6 addresses
let dev = DeviceBuilder::new()
.ipv4("10.0.0.1", 24, None)
.ipv6("CDCD:910A:2222:5498:8475:1111:3900:2021", 64)
.mtu(1400)
.build_sync()?;
println!("TUN device created: {:?}", dev.name());
let mut buf = [0; 1400];
loop {
let amount = dev.recv(&mut buf)?;
println!("Received {} bytes: {:?}", amount, &buf[0..amount]);
}
}
```
### Asynchronous TUN with Tokio
Use async/await for non-blocking I/O:
````rust
use tun_rs::DeviceBuilder;
#[tokio::main]
async fn main() -> std::io::Result<()> {
let dev = DeviceBuilder::new()
.ipv4("10.0.0.1", 24, None)
.build_async()?;
println!("Async TUN device ready!");
let mut buf = vec![0; 65536];
loop {
let len = dev.recv(&mut buf).await?;
println!("Received packet: {} bytes", len);
// Echo the packet back
dev.send(&buf[..len]).await?;
}
}
````
---
## 📚 Examples
### Multiple IP Addresses
Assign multiple IP addresses to a single interface:
````rust
use tun_rs::DeviceBuilder;
#[tokio::main]
async fn main() -> std::io::Result<()> {
let dev = DeviceBuilder::new()
.ipv4("10.0.0.1", 24, None)
.build_async()?;
// Add multiple IPv4 addresses
dev.add_address_v4("10.1.0.1", 24)?;
dev.add_address_v4("10.2.0.1", 24)?;
// Add multiple IPv6 addresses
dev.add_address_v6("CDCD:910A:2222:5498:8475:1111:3900:2021", 64)?;
dev.add_address_v6("BDCD:910A:2222:5498:8475:1111:3900:2021", 64)?;
// Remove addresses when needed
// dev.remove_address("10.2.0.1".parse().unwrap())?;
println!("All addresses: {:?}", dev.addresses()?);
let mut buf = vec![0; 65536];
loop {
let len = dev.recv(&mut buf).await?;
println!("Received on multi-IP device: {} bytes", len);
}
}
````
### Using Raw File Descriptor (Unix)
Create a device from an existing file descriptor (useful for iOS, Android, or custom TUN implementations):
```rust
use tun_rs::SyncDevice;
use std::os::unix::io::RawFd;
fn main() -> std::io::Result<()> {
// The fd must be a valid file descriptor obtained from:
// - iOS: NEPacketTunnelProvider's packetFlow.value(forKeyPath: "socket.fileDescriptor")
// - Android: VpnService.Builder().establish().getFd()
// - Linux: open("/dev/net/tun", O_RDWR)
// - Or any other platform-specific TUN device API
let fd: RawFd = get_tun_fd_from_platform(); // Your platform-specific function
// Take ownership of the file descriptor
let dev = unsafe { SyncDevice::from_fd(fd)? };
// Or borrow without taking ownership
// let dev = unsafe { tun_rs::BorrowedSyncDevice::borrow_raw(fd)? };
// Also works with async devices
// let async_dev = unsafe { tun_rs::AsyncDevice::from_fd(fd)? };
let mut buf = [0; 4096];
loop {
let amount = dev.recv(&mut buf)?;
println!("Received: {:?}", &buf[0..amount]);
}
}
// NOTE: This is example-only code. In real applications, obtain the fd from:
// - iOS: tunFd = packetFlow.value(forKeyPath: "socket.fileDescriptor") as! Int32
// - Android: fd = vpnInterface.getFd() (from VpnService.Builder().establish())
// - Linux: fd = open("/dev/net/tun", O_RDWR) or use DeviceBuilder instead
fn get_tun_fd_from_platform() -> RawFd {
// See the iOS and Android sections below for complete working examples
unimplemented!("Replace with your platform-specific fd acquisition code")
}
```
### Linux Hardware Offload (TSO/GSO)
Maximize performance with hardware offload features:
````rust
use tun_rs::DeviceBuilder;
#[cfg(target_os = "linux")]
use tun_rs::{GROTable, IDEAL_BATCH_SIZE, VIRTIO_NET_HDR_LEN};
#[cfg(target_os = "linux")]
fn main() -> std::io::Result<()> {
let builder = DeviceBuilder::new()
.offload(true) // Enable TSO/GSO for 3-4x throughput boost
// .multi_queue(true) // Enable multi-queue for concurrent I/O
.ipv4("10.0.0.1", 24, None)
.mtu(1400);
let dev = builder.build_sync()?;
// For multi-queue, clone the device for use in other threads
// let dev_clone = dev.try_clone()?;
let mut original_buffer = vec![0; VIRTIO_NET_HDR_LEN + 65535];
let mut bufs = vec![vec![0u8; 1500]; IDEAL_BATCH_SIZE];
let mut sizes = vec![0; IDEAL_BATCH_SIZE];
// Optional: Use GROTable for Generic Receive Offload optimization
// let mut gro_table = GROTable::default();
loop {
let num = dev.recv_multiple(&mut original_buffer, &mut bufs, &mut sizes, 0)?;
println!("Received {} packets in batch", num);
for i in 0..num {
println!("Packet {}: {} bytes", i, sizes[i]);
}
}
}
````
### TAP Interface (Layer 2)
```rust
use tun_rs::{DeviceBuilder, Layer};
#[tokio::main]
async fn main() -> std::io::Result<()> {
let dev = DeviceBuilder::new()
.layer(Layer::L2) // TAP mode for Ethernet frames
.build_async()?;
let mut buf = vec![0; 65536];
loop {
let len = dev.recv(&mut buf).await?;
// Process Ethernet frame
println!("Ethernet frame: {} bytes", len);
}
}
```
> **💡 More Examples**: Check out [examples/](https://github.com/tun-rs/tun-rs/tree/main/examples) for additional use cases including:
> - ICMP ping responder
> - iOS/Android integration
> - Framed codec usage
> - Interruptible operations
> - And more!
---
## 🔧 Platform-Specific Setup
### Linux
**Requirements:**
- TUN kernel module must be loaded: `sudo modprobe tun`
- Root privileges or `CAP_NET_ADMIN` capability required
**Performance Tips:**
- Enable hardware offload for 3-4x throughput improvement
- Use multi-queue for concurrent operations across multiple cores
- Consider using `recv_multiple()` for batch packet processing
```bash
# Load TUN module
sudo modprobe tun
# Run your application
sudo ./your_app
```
### macOS & BSD
**Automatic Routing:**
tun-rs automatically configures routes based on your IP settings:
```bash
# Example: This route is added automatically for 10.0.0.0/24
sudo route -n add -net 10.0.0.0/24 10.0.0.1
```
**TAP Mode on macOS:**
- Implemented using `feth` interface pairs
- Interfaces persist until explicitly destroyed
- Uses BPF for I/O operations
- Multiple file descriptors involved (be careful with `AsRawFd`)
```rust
use tun_rs::{DeviceBuilder, Layer};
fn main() -> std::io::Result<()> {
let dev = DeviceBuilder::new()
.layer(Layer::L2) // TAP mode
.ipv4("10.0.0.1", 24, None)
.build_sync()?;
// TAP interface is ready
println!("macOS TAP device: {:?}", dev.name());
Ok(())
}
```
### Windows
**TUN Mode:**
1. Download [wintun.dll](https://wintun.net/) matching your architecture (x64, x86, ARM, or ARM64)
2. Place `wintun.dll` in the same directory as your executable
3. Run your application as Administrator
**TAP Mode:**
1. Install [tap-windows](https://build.openvpn.net/downloads/releases/) matching your architecture
2. Run your application as Administrator
```rust
use tun_rs::DeviceBuilder;
fn main() -> std::io::Result<()> {
// Windows supports DNS configuration
let dev = DeviceBuilder::new()
.ipv4("10.0.0.1", 24, None)
.build_sync()?;
// Set DNS servers (Windows-specific)
#[cfg(windows)]
{
// Configure DNS if needed
println!("TUN version: {:?}", dev.version());
}
Ok(())
}
```
### iOS / tvOS
Integrate with `NEPacketTunnelProvider`:
```swift
// Swift
class PacketTunnelProvider: NEPacketTunnelProvider {
override func startTunnel(options: [String : NSObject]?, completionHandler: @escaping (Error?) -> Void) {
let tunnelNetworkSettings = createTunnelSettings() // Configure TUN address, DNS, mtu, routing...
setTunnelNetworkSettings(tunnelNetworkSettings) { [weak self] error in
// Get the file descriptor from packet flow
let tunFd = self?.packetFlow.value(forKeyPath: "socket.fileDescriptor") as! Int32
DispatchQueue.global(qos: .default).async {
start_tun(tunFd)
}
completionHandler(nil)
}
}
}
```
```rust
// Rust FFI
#[no_mangle]
pub extern "C" fn start_tun(fd: std::os::raw::c_int) {
// Create device from file descriptor
let tun = unsafe { tun_rs::SyncDevice::from_fd(fd).unwrap() };
let mut buf = [0u8; 1500];
while let Ok(len) = tun.recv(&mut buf) {
// Process packet
println!("iOS: received {} bytes", len);
}
}
```
### Android
Integrate with Android `VpnService`:
```java
// Java
private void startVpn() {
VpnService.Builder builder = new VpnService.Builder();
builder
.allowFamily(OsConstants.AF_INET)
.addAddress("10.0.0.2", 24);
ParcelFileDescriptor vpnInterface = builder
.setSession("tun-rs")
.establish();
int fd = vpnInterface.getFd();
// Pass fd to Rust via JNI
startTunNative(fd);
}
```
```rust
// Rust JNI binding
#[no_mangle]
pub extern "C" fn startTunNative(fd: std::os::raw::c_int) {
let tun = unsafe { tun_rs::SyncDevice::from_fd(fd).unwrap() };
let mut buf = [0u8; 1500];
while let Ok(len) = tun.recv(&mut buf) {
// Process packet
}
}
```
---
## 🤝 Comparison with Other Libraries
| **Peak Throughput** | 70.6 Gbps | 30.1 Gbps | Not benchmarked |
| **Memory Usage** | ✅ Low (3-16 MB) | ❌ High (39-43 MB) | Unknown |
| **Cross-Platform** | ✅ 11+ platforms | ⚠️ Limited | ⚠️ Linux/macOS only |
| **Async Support** | ✅ Tokio + async-io | ✅ Go runtime | ⚠️ Varies by lib |
| **Hardware Offload** | ✅ TSO/GSO/Multi-queue | ✅ TSO/GSO | ❌ No |
| **Multiple IPs** | ✅ Yes | ❌ No | ❌ No |
| **TAP on macOS** | ✅ Yes (feth) | ❌ No | ❌ No |
| **Mobile Support** | ✅ iOS/Android | ❌ No | ❌ No |
> **Note**: Benchmarks for go-tun were conducted in the same environment. Other Rust TUN libraries lack comprehensive cross-platform support and haven't been benchmarked in our test suite.
---
## 🛠️ API Overview
### Device Creation
```rust
use tun_rs::{DeviceBuilder, Layer};
// Build with configuration
let dev = DeviceBuilder::new()
.name("tun0") // Optional: specify interface name
.layer(Layer::L3) // L3 (TUN) or L2 (TAP)
.ipv4("10.0.0.1", 24, None) // IPv4 address, prefix, destination
.ipv6("fd00::1", 64) // IPv6 address, prefix
.mtu(1400) // Set MTU
.offload(true) // Enable offload (Linux only)
.multi_queue(true) // Enable multi-queue (Linux only)
.build_sync()?; // Or build_async()
```
### Core Operations
```rust
// Receive packet
let mut buf = vec![0; 65536];
let len = dev.recv(&mut buf)?;
// Send packet
dev.send(&packet)?;
// Device information
let name = dev.name()?;
let mtu = dev.mtu()?;
let addresses = dev.addresses()?;
let if_index = dev.if_index()?;
// Address management
dev.add_address_v4("10.1.0.1", 24)?;
dev.add_address_v6("fd00::2", 64)?;
dev.remove_address(addr)?;
```
### Async Operations
```rust
// Async recv/send
let len = dev.recv(&mut buf).await?;
dev.send(&packet).await?;
// Use with tokio::select!
tokio::select! {
result = dev.recv(&mut buf) => { /* handle */ }
_ = shutdown_signal => { /* cleanup */ }
}
```
---
## 📖 Documentation
- **API Documentation**: [docs.rs/tun-rs](https://docs.rs/tun-rs)
- **Examples**: [github.com/tun-rs/tun-rs/tree/main/examples](https://github.com/tun-rs/tun-rs/tree/main/examples)
- **Benchmark Details**: [github.com/tun-rs/tun-benchmark2](https://github.com/tun-rs/tun-benchmark2)
---
## 🐛 Troubleshooting
<details>
<summary><b>Permission denied when creating TUN interface</b></summary>
**Linux/BSD/macOS**: Run with `sudo` or grant `CAP_NET_ADMIN` capability:
```bash
sudo ./your_app
# Or grant capability
sudo setcap cap_net_admin+ep ./your_app
```
**Windows**: Run as Administrator
**Android/iOS**: Use platform VPN APIs to obtain file descriptor
</details>
<details>
<summary><b>Module not loaded on Linux</b></summary>
```bash
sudo modprobe tun
# Make it persistent
</details>
<details>
<summary><b>wintun.dll not found on Windows</b></summary>
1. Download from [wintun.net](https://wintun.net/)
2. Extract the DLL for your architecture (x64, x86, ARM, ARM64)
3. Place in the same directory as your executable
</details>
<details>
<summary><b>Low performance without offload</b></summary>
On Linux, enable hardware offload for 3-4x performance boost:
```rust
let dev = DeviceBuilder::new()
.offload(true)
.build_sync()?;
```
</details>
---
## 🙏 Contributing
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
### Development
```bash
# Clone repository
git clone https://github.com/tun-rs/tun-rs.git
cd tun-rs
# Run tests (requires root/admin)
cargo test
# Build examples
cargo build --examples --features async
# Run example
sudo ./target/debug/examples/read
```
---
## 📄 License
Licensed under the [Apache License 2.0](LICENSE)
---
## 🌟 Acknowledgments
- Thanks to all [contributors](https://github.com/tun-rs/tun-rs/graphs/contributors)
- Inspired by the networking community's need for high-performance TUN/TAP implementations
- Special thanks to the Rust async ecosystem (tokio, async-io) for making async networking seamless
---
<div align="center">
**[⬆ Back to Top](#-tun-rs)**
Made with ❤️ by the tun-rs team
</div>