//! VPN mode: bring up a WireGuard tunnel from a config file, entirely in user //! space, and confine every torrent socket to it. //! //! # Using it //! //! ```text //! torad --wireguard-config /path/to/wg0.conf ... //! ``` //! //! That is the whole interface. No `sudo`, no `wg-quick`, no pre-created //! interface, no kernel module, and no one-time host setup. `--bind-device` is //! ignored when this is given; the tunnel is the bind device. //! //! Requires `pasta` (from passt), `wireguard-go`, and `ip` on `PATH`. //! [`namespace::preflight`] checks for all three before anything is torn down, //! so a missing one fails immediately with a message naming it. //! //! # How it works //! //! ```text //! host │ torad's user+network namespace //! ────────────────────────────────┼──────────────────────────────────────── //! aggregator ──unix socket───────────► torad gRPC server //! postgres, jackett ◄────────────┐ (mount ns shares the path, so the //! │ host still reaches the socket) //! │ //! │ pasta ── tap iface, host addressing //! │ ├─ 127.0.0.1 → host loopback //! │ └─ 169.254.1.1 → host loopback //! │ //! │ wireguard-go ── wg0 (TUN) //! │ └─ default route ──► internet //! │ //! │ librqbit: SO_BINDTODEVICE(wg0) //! │ peers, trackers, DHT, LSD //! ``` //! //! 1. torad re-executes itself under `pasta`, which supplies an unprivileged //! user+network namespace with `CAP_NET_ADMIN` ([`namespace`]). //! 2. It unshares a mount namespace so `wireguard-go` has a writable socket //! directory ([`namespace::enter_mount_namespace`]). //! 3. It starts `wireguard-go` and configures the device over its UAPI socket //! ([`uapi`]) — no `wg` binary involved, so `wireguard-tools` is not a //! dependency. //! 4. It assigns the address, pins the peer endpoint outside the tunnel, moves //! the default route onto `wg0`, and installs the tunnel's DNS ([`tunnel`]). //! 5. librqbit binds every socket to `wg0` via `SO_BINDTODEVICE`. //! //! Host services stay reachable without any URL rewriting: pasta splices the //! namespace's loopback to the host's, so `http://localhost:9117/...` for //! Jackett and a loopback `--database-url` for Postgres work unchanged, and //! deliberately do not traverse the tunnel. //! //! # When the tunnel drops //! //! Two independent mechanisms, both fail-closed: //! //! - `SO_BINDTODEVICE` is kernel-enforced. If `wg0` goes away, librqbit's //! sockets error rather than falling back to the host route. //! - torad supervises `wireguard-go` and shuts down if it exits. A live torad //! with a dead tunnel is the failure mode that leaks. //! //! DNS is inside the tunnel too, so a dead tunnel means tracker hostnames stop //! resolving rather than being resolved by the host's resolver. //! //! # Limitations //! //! - **Linux only.** User namespaces, `SO_BINDTODEVICE`, and pasta are all //! Linux-specific. //! - **Leech-only.** librqbit runs with `listen: None`, so there is no //! listening socket and no uTP in either direction. Incoming peer //! connections would need NAT-PMP port forwarding through the provider, //! which is not implemented. //! - **The peer endpoint is pinned at startup.** It is resolved once and given //! a host route so the handshake does not enter its own tunnel; an endpoint //! that roams to a new address mid-session is not followed. Restart to pick //! up a new one. //! - **Userspace crypto.** Throughput is lower than the kernel module's. //! - **One peer.** [`config`] rejects configs with more than one `[Peer]` //! rather than silently using the last. pub mod config; pub mod namespace; pub mod tunnel; pub mod uapi; pub use config::{WireguardConfig, parse}; pub use tunnel::{INTERFACE, Tunnel};