Files
AuraVPN/crates/aura-tunnel/src/lib.rs
T
xah30 ba8d6b796f feat(transport,cli,tunnel): v3.4 port auto-detect + bug fixes from live test
Live macOS test against the production server uncovered six bugs (one of which
turned out to be a port collision with sing-box, not a real bug); this commit
addresses all of them and adds v3.4 port discovery so the same collision is
handled transparently next time.

## v3.4 server port-discovery

- Defaults moved off 443/444 to 8443/8443/8444 (TransportSection::default,
  ServerInitOpts, ProvisionClientOpts, CLI flags). 443 is heavily contested in
  practice (sing-box, Hysteria2, reverse proxies) and the previous default
  silently lost the bind when a co-tenant was already there.
- MultiServer::bind_with_outer_or_scan: scans forward up to
  DEFAULT_PORT_SCAN_MAX (20) candidates per transport when the requested port
  is occupied; QUIC keeps walking if it lands on the custom-UDP port.
- MultiServer::bound_addrs(): the actual addresses each transport bound to.
- Server logs the bound addresses and writes a runtime snapshot
  (server.toml.runtime.json) when they differ from the requested ones, so
  `aura sign-bridges` can re-sign the bridges manifest later.
- BridgeManifest gains an optional `endpoints: Vec<BridgeEndpoint>` field
  with per-transport ports. Backward-compatible: old v3.3 clients ignore the
  field and continue to use the v1 `bridges` line.
- `aura sign-bridges --endpoints HOST:tcp=N:quic=N:udp=N` to mint v3.4
  manifests; bridges line is auto-synthesised for v3.3 clients.

## Bug fixes from the live test

- macOS TUN naming (#41): the tun crate rejects names that don't match
  ^utun[0-9]+$. On macOS we now substitute `""` (kernel auto-assigns utunN),
  capture the assigned name via inner.tun_name(), and propagate it through to
  os_routes::OsRouteGuard::install — so `route add -interface utunN` uses
  the real interface, not "aura0".
- Packet counters (#42): Stats { tx_packets, rx_packets } are now actually
  bumped by the data path. `aura status` shows live numbers instead of
  permanent zeros.
- render_client_toml schema (#44): provisioner emits proper
  `[[tunnel.split.vpn]] cidr = "..."` / `[[tunnel.split.direct]]` blocks from
  new --vpn-cidrs / --direct-cidrs flags. The v3.3 `vpn_cidrs = [...]` flat
  array was silently ignored by serde, leaving users with `rules: 0` even
  when their CIDRs looked right.
- #43 / #46 (TCP/443 dial early-eof / no payload back): diagnosed as the
  sing-box port collision, not an Aura bug. The v3.4 port-scan path makes it
  go away — the server picks a free port and clients learn it from the
  manifest.

## Test coverage

Three new unit tests for the port-scanner (UDP busy, TCP busy, zero budget);
two new tests for v3.4 BridgeManifest round-trip with endpoints; one
integration test for the new `[[tunnel.split.vpn]]` rendering; tests for the
runtime-state file write/read round-trip; agent-added router-counter tests
in aura-tunnel/tests/routes.rs.

cargo test --workspace, cargo clippy --workspace -- -D warnings, and
cargo fmt --check all pass.

#45 (silent client exit when underlying QUIC transport breaks) is still
outstanding — needs deeper investigation; deferred to a follow-up.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 17:14:45 +03:00

87 lines
3.4 KiB
Rust

//! aura-tunnel — the Aura VPN data-plane tunnel (project §8).
//!
//! This crate turns a host's IP traffic into something the encrypted transport can carry, and back
//! again. It has four pieces:
//!
//! * [`AuraTun`] — a cross-platform layer-3 TUN device (Linux/macOS via the `tun` crate; Windows via
//! `wintun`, `cfg`-gated). See [`tun`](mod@crate::tun).
//! * [`RouteTable`] / [`RouteAction`] — a longest-prefix-match split-tunnel routing table deciding
//! VPN-vs-direct per destination IP. See [`routes`](mod@crate::routes).
//! * [`AuraDns`] — a hickory-backed resolver that registers resolved domain addresses as host
//! routes in a shared [`RouteTable`]. See [`dns`](mod@crate::dns).
//! * [`AuraRouter`] — the run-loop bridging the TUN device and an
//! [`aura_proto::PacketConnection`]. See [`router`](mod@crate::router).
//!
//! ## Wiring it together (for the CLI)
//!
//! The router is generic over the [`PacketIo`] device seam and shares the routing table and the
//! packet connection by `Arc`:
//!
//! ```no_run
//! # async fn demo(conn: std::sync::Arc<dyn aura_proto::PacketConnection>) -> anyhow::Result<()> {
//! use std::sync::Arc;
//! use tokio::sync::RwLock;
//! use aura_tunnel::{AuraDns, AuraRouter, AuraTun, RouteAction, RouteTable};
//!
//! // 1. Build a shared routing table (default: everything through the VPN).
//! let routes = Arc::new(RwLock::new(RouteTable::new(RouteAction::Vpn)));
//! routes.write().await.add_cidr("192.168.0.0/16".parse()?, RouteAction::Direct);
//!
//! // 2. Optionally resolve domains into host routes.
//! let mut dns = AuraDns::new(Arc::clone(&routes)).await?;
//! dns.resolve_and_register("example.com", RouteAction::Direct).await?;
//!
//! // 3. Create the TUN device (needs privileges).
//! let tun = AuraTun::create("aura0", "10.7.0.2".parse()?, 24, 1420).await?;
//!
//! // 4. Build the router from the TUN, the table, and the connection, then run it.
//! let router = AuraRouter::new(tun, routes, conn);
//! router.run().await?;
//! # Ok(())
//! # }
//! ```
#![cfg_attr(not(windows), forbid(unsafe_code))]
#![warn(missing_docs)]
pub mod dns;
pub mod router;
pub mod routes;
pub mod tun;
pub use dns::AuraDns;
pub use router::{dst_ip, AuraRouter, PacketCounters};
pub use routes::{RouteAction, RouteTable};
pub use tun::{AuraTun, PacketIo};
use thiserror::Error;
/// Errors produced by the tunnel data plane.
///
/// The router and DNS surfaces mostly return [`anyhow::Result`] (they compose I/O, the `tun`/`wintun`
/// backends, hickory, and the [`aura_proto::PacketConnection`] contract, all of which already carry
/// rich context). This enum names the tunnel-specific failure modes for callers that want to match
/// on them, and converts cleanly from the underlying I/O and resolver errors.
#[derive(Debug, Error)]
pub enum TunnelError {
/// Creating or configuring the TUN/wintun device failed.
#[error("TUN device error: {0}")]
Device(String),
/// An I/O error while reading from or writing to the TUN device.
#[error("TUN I/O error: {0}")]
Io(#[from] std::io::Error),
/// The requested TUN address/prefix was not a valid network.
#[error("invalid TUN address or prefix: {0}")]
InvalidAddress(#[from] ipnetwork::IpNetworkError),
/// DNS resolution failed.
#[error("DNS resolution error: {0}")]
Dns(#[from] hickory_resolver::error::ResolveError),
/// The underlying encrypted packet connection failed.
#[error("packet connection error: {0}")]
Connection(String),
}