feat(rust-server, rust-client, ts-interfaces): add configurable packet forwarding with TUN and userspace NAT modes

This commit is contained in:
2026-03-29 23:33:44 +00:00
parent e9cf575271
commit 9d105e8034
12 changed files with 1130 additions and 24 deletions

View File

@@ -1,6 +1,7 @@
use anyhow::Result;
use bytes::BytesMut;
use serde::Deserialize;
use std::net::Ipv4Addr;
use std::sync::Arc;
use tokio::sync::{mpsc, watch, RwLock};
use tracing::{info, error, warn, debug};
@@ -12,6 +13,7 @@ use crate::telemetry::ConnectionQuality;
use crate::transport;
use crate::transport_trait::{self, TransportSink, TransportStream};
use crate::quic_transport;
use crate::tunnel::{self, TunConfig};
/// Client configuration (matches TS IVpnClientConfig).
#[derive(Debug, Clone, Deserialize)]
@@ -30,6 +32,9 @@ pub struct ClientConfig {
pub transport: Option<String>,
/// For QUIC: SHA-256 hash of server certificate (base64) for cert pinning.
pub server_cert_hash: Option<String>,
/// Forwarding mode: "tun" (TUN device, requires root) or "testing" (no TUN).
/// Default: "testing".
pub forwarding_mode: Option<String>,
}
/// Client statistics.
@@ -234,6 +239,31 @@ impl VpnClient {
info!("Connected to VPN, assigned IP: {}", assigned_ip);
// Optionally create TUN device for IP packet forwarding (requires root)
let tun_enabled = config.forwarding_mode.as_deref() == Some("tun");
let (tun_reader, tun_writer, tun_subnet) = if tun_enabled {
let client_tun_ip: Ipv4Addr = assigned_ip.parse()?;
let mtu = ip_info["mtu"].as_u64().unwrap_or(1420) as u16;
let tun_config = TunConfig {
name: "svpn-client0".to_string(),
address: client_tun_ip,
netmask: Ipv4Addr::new(255, 255, 255, 0),
mtu,
};
let tun_device = tunnel::create_tun(&tun_config)?;
// Add route for VPN subnet through the TUN device
let gateway_str = ip_info["gateway"].as_str().unwrap_or("10.8.0.1");
let gateway: Ipv4Addr = gateway_str.parse().unwrap_or(Ipv4Addr::new(10, 8, 0, 1));
let subnet = format!("{}/24", Ipv4Addr::from(u32::from(gateway) & 0xFFFFFF00));
tunnel::add_route(&subnet, &tun_config.name).await?;
let (reader, writer) = tokio::io::split(tun_device);
(Some(reader), Some(writer), Some(subnet))
} else {
(None, None, None)
};
// Create adaptive keepalive monitor (use custom interval if configured)
let ka_config = config.keepalive_interval_secs.map(|secs| {
let mut cfg = keepalive::AdaptiveKeepaliveConfig::default();
@@ -260,6 +290,9 @@ impl VpnClient {
handle.signal_rx,
handle.ack_tx,
link_health,
tun_reader,
tun_writer,
tun_subnet,
));
Ok(assigned_ip_clone)
@@ -356,8 +389,14 @@ async fn client_loop(
mut signal_rx: mpsc::Receiver<KeepaliveSignal>,
ack_tx: mpsc::Sender<()>,
link_health: Arc<RwLock<LinkHealth>>,
mut tun_reader: Option<tokio::io::ReadHalf<tun::AsyncDevice>>,
mut tun_writer: Option<tokio::io::WriteHalf<tun::AsyncDevice>>,
tun_subnet: Option<String>,
) {
use tokio::io::{AsyncReadExt, AsyncWriteExt};
let mut buf = vec![0u8; 65535];
let mut tun_buf = vec![0u8; 65536];
loop {
tokio::select! {
@@ -373,6 +412,14 @@ async fn client_loop(
let mut s = stats.write().await;
s.bytes_received += len as u64;
s.packets_received += 1;
drop(s);
// Write decrypted packet to TUN device (if enabled)
if let Some(ref mut writer) = tun_writer {
if let Err(e) = writer.write_all(&buf[..len]).await {
warn!("TUN write error: {}", e);
}
}
}
Err(e) => {
warn!("Decrypt error: {}", e);
@@ -407,6 +454,50 @@ async fn client_loop(
}
}
}
// Read outbound packets from TUN and send to server (only when TUN enabled)
result = async {
match tun_reader {
Some(ref mut reader) => reader.read(&mut tun_buf).await,
None => std::future::pending::<std::io::Result<usize>>().await,
}
} => {
match result {
Ok(0) => {
info!("TUN device closed");
break;
}
Ok(n) => {
match noise_transport.write_message(&tun_buf[..n], &mut buf) {
Ok(len) => {
let frame = Frame {
packet_type: PacketType::IpPacket,
payload: buf[..len].to_vec(),
};
let mut frame_bytes = BytesMut::new();
if <FrameCodec as tokio_util::codec::Encoder<Frame>>::encode(
&mut FrameCodec, frame, &mut frame_bytes
).is_ok() {
if sink.send_reliable(frame_bytes.to_vec()).await.is_err() {
warn!("Failed to send TUN packet to server");
break;
}
let mut s = stats.write().await;
s.bytes_sent += n as u64;
s.packets_sent += 1;
}
}
Err(e) => {
warn!("Noise encrypt error: {}", e);
break;
}
}
}
Err(e) => {
warn!("TUN read error: {}", e);
break;
}
}
}
signal = signal_rx.recv() => {
match signal {
Some(KeepaliveSignal::SendPing(timestamp_ms)) => {
@@ -456,6 +547,13 @@ async fn client_loop(
}
}
}
// Cleanup: remove TUN route if enabled
if let Some(ref subnet) = tun_subnet {
if let Err(e) = tunnel::remove_route(subnet, "svpn-client0").await {
warn!("Failed to remove client TUN route: {}", e);
}
}
}
/// Try to connect via QUIC. Returns transport halves on success.