Skip to main content

Overview

The TunBackend creates a virtual network interface (TUN device) to intercept and process IP packets at the network layer. This enables system-wide transparent DPI bypass without application-specific proxy configuration.
The TUN backend is currently a minimal implementation and requires platform-specific privileges. Full packet processing features are under development.

TunBackend

The main TUN backend structure that implements the Backend trait.

Structure

pub struct TunBackend {
    running: Arc<AtomicBool>,
    shutdown_tx: Option<mpsc::Sender<()>>,
    config: Option<TunSettings>,
    task_handle: Mutex<Option<tokio::task::JoinHandle<()>>>,
}

Fields

running
Arc<AtomicBool>
Atomic flag indicating whether the backend is currently running
shutdown_tx
Option<mpsc::Sender<()>>
Channel sender for graceful shutdown signaling
config
Option<TunSettings>
Current TUN device configuration settings
task_handle
Mutex<Option<tokio::task::JoinHandle<()>>>
Handle to the running TUN packet processing task

Methods

new()

Creates a new TunBackend instance.
pub fn new() -> Self
Returns: A new TunBackend in a stopped state. Example:
use backend::TunBackend;

let backend = TunBackend::new();

start()

Starts the TUN backend and creates the virtual network interface.
async fn start(&mut self, config: BackendConfig) -> Result<BackendHandle>
Parameters:
config
BackendConfig
required
Backend configuration including:
  • engine_config: DPI bypass engine configuration
  • max_queue_size: Maximum packet queue size
  • backend_settings: Must be BackendSettings::Tun(TunSettings)
Returns: BackendHandle containing:
  • shutdown_tx: Channel for triggering shutdown
  • stats: Reference to statistics collector
  • pipeline: Reference to the DPI bypass pipeline
Errors:
  • BackendError::AlreadyRunning - Backend is already running
  • BackendError::NotSupported - Wrong backend settings type or platform not supported
  • BackendError::Engine - Engine initialization failed
Example:
use backend::{TunBackend, BackendConfig, BackendSettings, TunSettings, Backend};
use engine::Config;

let mut backend = TunBackend::new();
let config = BackendConfig {
    engine_config: Config::default(),
    max_queue_size: 1000,
    backend_settings: BackendSettings::Tun(TunSettings {
        device_name: None,  // Auto-generate name
        mtu: 1500,
        address: "10.0.85.1".to_string(),
        netmask: "255.255.255.0".to_string(),
    }),
};

let handle = backend.start(config).await?;

stop()

Stops the TUN backend and destroys the virtual network interface.
async fn stop(&mut self) -> Result<()>
Errors:
  • BackendError::NotRunning - Backend is not currently running
Example:
backend.stop().await?;

is_running()

Checks if the backend is currently running.
fn is_running(&self) -> bool
Returns: true if the backend is running, false otherwise.

is_supported()

Checks if the TUN backend is supported on this platform.
fn is_supported() -> bool
Returns: true on macOS, false on other platforms. Note: Currently only macOS is fully supported. Linux and Windows support is planned.

name()

Returns the backend name.
fn name(&self) -> &'static str
Returns: "tun"

TunSettings

Configuration settings for the TUN device.
pub struct TunSettings {
    pub device_name: Option<String>,
    pub mtu: u16,
    pub address: String,
    pub netmask: String,
}

Fields

device_name
Option<String>
Name of the TUN device to createDefault: None (auto-generated by OS)Example: "utun3" on macOS, "tun0" on Linux
mtu
u16
required
Maximum Transmission Unit size in bytesDefault: 1500Range: Typically 1280-1500 for IPv4
address
String
required
IP address to assign to the TUN interfaceDefault: "10.0.85.1"
netmask
String
required
Network mask for the TUN interfaceDefault: "255.255.255.0"

Flow Key Parsing

The TUN backend includes IPv4 packet parsing to extract flow information:
fn parse_ipv4_flow_key(data: &[u8]) -> Option<FlowKey>

Supported Protocols

TCP
Protocol::Tcp
Transmission Control Protocol (protocol 6)Extracts source/destination ports from TCP header
UDP
Protocol::Udp
User Datagram Protocol (protocol 17)Extracts source/destination ports from UDP header
ICMP
Protocol::Icmp
Internet Control Message Protocol (protocol 1)Uses port 0 for both source and destination

Example

let packet = /* raw IP packet bytes */;
if let Some(flow_key) = TunBackend::parse_ipv4_flow_key(&packet) {
    println!("Flow: {}:{} -> {}:{}", 
        flow_key.src_ip, flow_key.src_port,
        flow_key.dst_ip, flow_key.dst_port);
}

Current Implementation

The TUN backend currently implements:

Flow Cleanup Task

A background task that periodically cleans up expired flows:
let handle = tokio::spawn(async move {
    let mut cleanup_interval = tokio::time::interval(
        std::time::Duration::from_secs(30)
    );
    
    loop {
        tokio::select! {
            _ = shutdown_rx.recv() => break,
            _ = cleanup_interval.tick() => {
                let evicted = pipeline_clone.cleanup();
                if evicted > 0 {
                    debug!(evicted, "Cleaned up expired flows");
                }
            }
        }
    }
});

Features Under Development

The following features are planned but not yet implemented:
  • TUN device creation and configuration
  • Packet reading from TUN device
  • Packet writing to TUN device
  • Routing table manipulation
  • IPv6 support
  • Full packet processing pipeline integration

Platform Support

macOS

Supported platform
Requires:
  • Administrator/root privileges
  • Uses utun devices

Linux

Planned - not yet implemented
Will require:
  • CAP_NET_ADMIN capability or root
  • /dev/net/tun device access

Windows

Planned - not yet implemented
Will require:
  • TAP-Windows adapter or WinTUN
  • Administrator privileges

Example: Basic Usage

use backend::{TunBackend, BackendConfig, BackendSettings, TunSettings, Backend};
use engine::Config;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Check platform support
    if !TunBackend::is_supported() {
        eprintln!("TUN backend not supported on this platform");
        return Ok(());
    }
    
    // Create TUN backend
    let mut backend = TunBackend::new();
    
    // Configure the backend
    let config = BackendConfig {
        engine_config: Config::default(),
        max_queue_size: 1000,
        backend_settings: BackendSettings::Tun(TunSettings {
            device_name: None,  // Let OS choose
            mtu: 1500,
            address: "10.0.85.1".to_string(),
            netmask: "255.255.255.0".to_string(),
        }),
    };
    
    // Start the backend
    let handle = backend.start(config).await?;
    println!("TUN backend started");
    
    // Run for some time
    tokio::time::sleep(std::time::Duration::from_secs(60)).await;
    
    // Shutdown
    backend.stop().await?;
    println!("TUN backend stopped");
    
    Ok(())
}

Testing

The TUN backend includes a mock device for testing:
#[cfg(test)]
pub struct MockTunDevice {
    read_queue: mpsc::Receiver<bytes::BytesMut>,
    write_queue: mpsc::Sender<bytes::BytesMut>,
}

impl MockTunDevice {
    pub fn new() -> (Self, 
                     mpsc::Sender<bytes::BytesMut>, 
                     mpsc::Receiver<bytes::BytesMut>)
    
    pub async fn read(&mut self) -> Option<bytes::BytesMut>
    
    pub async fn write(&self, data: bytes::BytesMut) -> Result<()>
}
Example test:
#[tokio::test]
async fn test_packet_processing() {
    let (mut device, tx, mut rx) = MockTunDevice::new();
    
    // Send a test packet
    let packet = create_ipv4_tcp_packet();
    tx.send(packet.clone()).await.unwrap();
    
    // Read it back
    let received = device.read().await.unwrap();
    assert_eq!(received, packet);
}

Security Considerations

TUN backends require elevated privileges and can intercept all network traffic
  • Privilege separation: Drop privileges after TUN device creation
  • Input validation: Validate all packets before processing
  • Resource limits: Enforce packet queue limits to prevent DoS
  • Audit logging: Log all TUN device operations

Performance Considerations

  • Packet buffering: Uses configurable queue size (max_queue_size)
  • Flow cleanup: Periodic cleanup every 30 seconds
  • Async I/O: All operations use tokio async runtime
  • Zero-copy: Minimal packet copying where possible

Future Development

Planned enhancements:
  1. Full packet processing: Integrate with engine pipeline
  2. IPv6 support: Parse and process IPv6 packets
  3. Platform support: Linux and Windows implementations
  4. Routing integration: Automatic route table management
  5. Split tunneling: Selective traffic routing
  6. DNS interception: Transparent DNS-over-HTTPS proxy

Build docs developers (and LLMs) love