use std::collections::HashMap;
use std::net::{Ipv4Addr, SocketAddr};
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
use anyhow::{Context, Result};
use clap::Parser;
use log::{debug, error, info, warn};
use tokio::net::UdpSocket;
use shadowvpn::config::{ServerArgs, ServerConfig};
use shadowvpn::crypto::{decrypt_packet, encrypt_packet};
use shadowvpn::nat::{Ingress, Nat};
use shadowvpn::obfs::{self, Obfuscator};
use shadowvpn::protocol::{max_datagram_size, MAX_IP_PACKET};
use shadowvpn::tun_device::TunDevice;
enum Routing {
Learn(HashMap<Ipv4Addr, SocketAddr>),
Nat(Nat),
}
type Shared = Arc<Mutex<Routing>>;
#[tokio::main]
async fn main() -> Result<()> {
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init();
let cfg = ServerArgs::parse()
.resolve()
.context("failed to resolve server configuration")?;
if let Err(err) = run(cfg).await {
error!("server exited with error: {err:#}");
return Err(err);
}
Ok(())
}
async fn run(cfg: ServerConfig) -> Result<()> {
let socket = UdpSocket::bind(&cfg.listen)
.await
.with_context(|| format!("failed to bind UDP socket on {}", cfg.listen))?;
let socket = Arc::new(socket);
let tun = TunDevice::create(&cfg.tun)
.context("failed to create TUN device (TUN setup needs root / elevated privileges)")?;
let tun = Arc::new(tun);
let tun_name = tun.name().unwrap_or_else(|_| {
cfg.tun
.name
.clone()
.unwrap_or_else(|| "<unknown>".to_string())
});
print_banner(&cfg, &tun_name);
let routing: Shared = Arc::new(Mutex::new(if cfg.nat {
let nat = Nat::new(cfg.tun.ip, cfg.tun.netmask, cfg.lease_ttl);
info!(
" NAT : ENABLED ({} clients max, idle TTL {}s)",
nat.capacity(),
cfg.lease_ttl.as_secs()
);
Routing::Nat(nat)
} else {
Routing::Learn(HashMap::new())
}));
let obfuscator: Option<Arc<Obfuscator>> = cfg
.obfs
.as_deref()
.and_then(Obfuscator::from_name)
.map(Arc::new);
if let Some(name) = cfg.obfs.as_deref() {
info!(" obfuscation : {name} datagram shaping ENABLED");
}
let nat_enabled = cfg.nat;
let lease_ttl = cfg.lease_ttl;
let cfg = Arc::new(cfg);
let a = {
let socket = Arc::clone(&socket);
let tun = Arc::clone(&tun);
let routing = Arc::clone(&routing);
let cfg = Arc::clone(&cfg);
let obfs = obfuscator.clone();
tokio::spawn(async move { udp_to_tun(socket, tun, routing, cfg, obfs).await })
};
let b = {
let socket = Arc::clone(&socket);
let tun = Arc::clone(&tun);
let routing = Arc::clone(&routing);
let cfg = Arc::clone(&cfg);
let obfs = obfuscator.clone();
tokio::spawn(async move { tun_to_udp(socket, tun, routing, cfg, obfs).await })
};
let _sweeper = nat_enabled.then(|| {
let routing = Arc::clone(&routing);
let interval = (lease_ttl / 2).max(Duration::from_secs(5));
tokio::spawn(async move {
let mut tick = tokio::time::interval(interval);
tick.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Delay);
loop {
tick.tick().await;
if let Routing::Nat(nat) = &mut *routing.lock().unwrap() {
nat.reap(Instant::now());
}
}
})
});
tokio::select! {
res = a => res.context("UDP→TUN task panicked")?,
res = b => res.context("TUN→UDP task panicked")?,
}
}
async fn udp_to_tun(
socket: Arc<UdpSocket>,
tun: Arc<TunDevice>,
routing: Shared,
cfg: Arc<ServerConfig>,
obfuscator: Option<Arc<Obfuscator>>,
) -> Result<()> {
let cipher = cfg.cipher;
let mut buf = vec![0u8; max_datagram_size(cipher) + obfs::MAX_HEADER];
loop {
let (n, peer) = socket
.recv_from(&mut buf)
.await
.context("UDP recv_from failed")?;
let decoded;
let datagram: &[u8] = match obfuscator {
Some(ref o) => match o.unwrap(&buf[..n]) {
Some(inner) => {
decoded = inner;
&decoded
}
None => {
debug!("dropping {n}-byte non-obfs datagram from {peer}");
continue;
}
},
None => &buf[..n],
};
let mut plaintext = match decrypt_packet(cipher, &cfg.master_key, datagram) {
Ok(pt) => pt,
Err(err) => {
debug!("dropping {n}-byte datagram from {peer}: decrypt failed: {err}");
continue;
}
};
let now = Instant::now();
if plaintext.len() < 20 {
if let Routing::Nat(nat) = &mut *routing.lock().unwrap() {
nat.touch(peer, now);
}
debug!(
"dropping {}-byte sub-IP-header payload from {peer} (keepalive?)",
plaintext.len()
);
continue;
}
let forward = {
let mut guard = routing.lock().unwrap();
match &mut *guard {
Routing::Learn(clients) => {
if let Some(src) = ipv4_src(&plaintext) {
if clients.insert(src, peer) != Some(peer) {
info!("client {src} reachable via {peer}");
}
} else {
debug!("datagram from {peer} is not a parseable IPv4 packet; forwarding");
}
true
}
Routing::Nat(nat) => match nat.ingress(peer, &mut plaintext, now) {
Ingress::Rewritten(_) => true,
Ingress::Exhausted => {
warn!("NAT address pool exhausted; dropping packet from {peer}");
false
}
Ingress::Invalid => {
debug!("unparseable IPv4 packet from {peer}; dropping");
false
}
},
}
};
if forward {
if let Err(err) = tun.send(&plaintext).await {
return Err(err).context("failed to write packet to TUN");
}
}
}
}
async fn tun_to_udp(
socket: Arc<UdpSocket>,
tun: Arc<TunDevice>,
routing: Shared,
cfg: Arc<ServerConfig>,
obfuscator: Option<Arc<Obfuscator>>,
) -> Result<()> {
let cipher = cfg.cipher;
let mut buf = vec![0u8; MAX_IP_PACKET];
loop {
let n = tun
.recv(&mut buf)
.await
.context("failed to read from TUN")?;
let now = Instant::now();
let peer = {
let mut guard = routing.lock().unwrap();
match &mut *guard {
Routing::Learn(clients) => {
ipv4_dst(&buf[..n]).and_then(|dst| clients.get(&dst).copied())
}
Routing::Nat(nat) => nat.egress(&mut buf[..n], now),
}
};
let peer = match peer {
Some(peer) => peer,
None => {
debug!("dropping {n}-byte TUN packet: no known client for its destination");
continue;
}
};
let datagram = match encrypt_packet(cipher, &cfg.master_key, &buf[..n]) {
Ok(d) => d,
Err(err) => {
warn!("failed to encrypt packet for {peer}: {err}");
continue;
}
};
let datagram = match obfuscator {
Some(ref o) => o.wrap(&datagram),
None => datagram,
};
if let Err(err) = socket.send_to(&datagram, peer).await {
warn!("failed to send datagram to {peer}: {err}");
}
}
}
fn ipv4_src(packet: &[u8]) -> Option<Ipv4Addr> {
if packet.len() < 20 || (packet[0] >> 4) != 4 {
return None;
}
Some(Ipv4Addr::new(
packet[12], packet[13], packet[14], packet[15],
))
}
fn ipv4_dst(packet: &[u8]) -> Option<Ipv4Addr> {
if packet.len() < 20 || (packet[0] >> 4) != 4 {
return None;
}
Some(Ipv4Addr::new(
packet[16], packet[17], packet[18], packet[19],
))
}
fn print_banner(cfg: &ServerConfig, tun_name: &str) {
info!("ShadowVPN server starting");
info!(" listen (UDP) : {}", cfg.listen);
info!(" cipher : {}", cfg.cipher.name());
info!(
" TUN interface : {tun_name} ip={} netmask={} peer={} mtu={}",
cfg.tun.ip, cfg.tun.netmask, cfg.tun.peer_ip, cfg.tun.mtu
);
info!(" routing : learn inner src IP -> UDP addr; route by inner dst IP");
info!("To route client traffic beyond this host, enable forwarding + NAT:");
#[cfg(target_os = "linux")]
{
info!(" Linux: sysctl -w net.ipv4.ip_forward=1");
info!(
" Linux: iptables -t nat -A POSTROUTING -s {}/{} -o <wan-if> -j MASQUERADE",
cfg.tun.ip, cfg.tun.netmask
);
}
#[cfg(target_os = "macos")]
{
info!(" macOS: sysctl -w net.inet.ip.forwarding=1");
info!(
" macOS: configure pf NAT (nat on <wan-if> from {} -> (<wan-if>))",
cfg.tun.ip
);
}
}
#[cfg(test)]
mod tests {
use super::*;
fn ipv4_header(src: [u8; 4], dst: [u8; 4]) -> Vec<u8> {
let mut p = vec![0u8; 20];
p[0] = 0x45; p[12..16].copy_from_slice(&src);
p[16..20].copy_from_slice(&dst);
p
}
#[test]
fn parses_src_and_dst() {
let p = ipv4_header([10, 7, 0, 2], [10, 7, 0, 1]);
assert_eq!(ipv4_src(&p), Some(Ipv4Addr::new(10, 7, 0, 2)));
assert_eq!(ipv4_dst(&p), Some(Ipv4Addr::new(10, 7, 0, 1)));
}
#[test]
fn rejects_too_short() {
let p = vec![0x45u8; 10];
assert_eq!(ipv4_src(&p), None);
assert_eq!(ipv4_dst(&p), None);
}
#[test]
fn rejects_non_ipv4_version() {
let mut p = ipv4_header([1, 2, 3, 4], [5, 6, 7, 8]);
p[0] = 0x60;
assert_eq!(ipv4_src(&p), None);
assert_eq!(ipv4_dst(&p), None);
}
}