Skip to main content
Mullvad VPN uses WireGuard as its primary VPN protocol, with support for both kernel and userspace implementations across multiple platforms. This document provides a technical overview of the WireGuard configuration, tunnel management, and platform-specific implementations.

Architecture Overview

The WireGuard implementation is centered around the talpid-wireguard crate, which provides:
  • Tunnel Management: The WireguardMonitor orchestrates tunnel lifecycle, connectivity monitoring, and configuration
  • Multiple Implementations: Support for kernel-space (Linux), WireGuard-NT (Windows), and userspace implementations (wireguard-go, gotatun)
  • Platform Abstraction: Unified interface through the Tunnel trait for different implementations
  • Automatic Fallback: Graceful degradation from kernel to userspace when necessary

Code References

  • Main implementation: talpid-wireguard/src/lib.rs
  • Configuration: talpid-wireguard/src/config.rs
  • Kernel implementation (Linux): talpid-wireguard/src/wireguard_kernel/
  • Windows implementation: talpid-wireguard/src/wireguard_nt/mod.rs
  • Userspace implementations: talpid-wireguard/src/wireguard_go/mod.rs, talpid-wireguard/src/gotatun/mod.rs

WireGuard Configuration

Config Structure

The Config struct in talpid-wireguard/src/config.rs encapsulates all tunnel configuration:
Key Configuration Points:
  • Multihop Support: Optional exit_peer enables multi-hop connections through two relay servers
  • Obfuscation: obfuscator_config can specify UDP2TCP, Shadowsocks, QUIC, or LWO obfuscation
  • Quantum Resistance: When quantum_resistant is true, ephemeral peers are negotiated with post-quantum KEMs
  • DAITA: Defense Against AI-guided Traffic Analysis adds traffic padding

Peer Configuration

Userspace Format

The configuration can be converted to WireGuard-go’s format via to_userspace_format() (config.rs:122):

Platform Implementations

Linux

Linux supports both kernel and userspace implementations: Kernel Implementation (wireguard_kernel/mod.rs):
  • Uses netlink for configuration via NetlinkTunnel
  • NetworkManager integration via NetworkManagerTunnel when systemd-resolved is unavailable
  • Automatic fallback to userspace on failure
Userspace Implementation:
  • wireguard-go (via TALPID_FORCE_USERSPACE_WIREGUARD env var)
  • gotatun (without wireguard-go feature)
  • Required for DAITA support
Force Userspace (lib.rs:145-151):

Windows

Windows uses WireGuard-NT (kernel driver) or wireguard-go: WireGuard-NT (wireguard_nt/mod.rs):
  • Native kernel driver for best performance
  • Requires driver installation and elevated privileges
  • Manages tunnel adapter via Windows APIs
IP Address Configuration (lib.rs:659-691):
  • WireGuard-NT requires explicit IP address assignment
  • Waits for adapter to be ready before assigning addresses
  • Uses talpid_windows::net::add_ip_address_for_interface()

macOS

Always uses userspace implementation (lib.rs:736-762):
  • wireguard-go (with feature flag)
  • gotatun (without feature flag)
  • No kernel module available

Android

Special Considerations (lib.rs:405-601):
  1. No Route Configuration: Routes are managed by Android VpnService
  2. Connectivity Check Before Ephemeral Peer: Required for gotatun implementation
  3. Socket Bypass: VPN sockets must be protected from routing through the tunnel
  4. Multihop Support: Special handling via wgTurnOnMultihop() FFI call

Tunnel Lifecycle

Startup Sequence

  1. MTU Calculation (lib.rs:165-167):
  2. Configuration Creation (lib.rs:169-170):
  3. Obfuscation Setup (lib.rs:180-187):
  4. Tunnel Creation (lib.rs:200-213):
    • Platform-specific tunnel implementation instantiated
    • Returns interface name for routing configuration
  5. Interface Configuration (lib.rs:258-262):
    • IP addresses assigned
    • Emits TunnelEvent::InterfaceUp with restricted traffic
  6. Routing Setup (lib.rs:265-280):
    • Policy routing rules (Linux)
    • Routes to gateway and allowed IPs
    • Endpoint protection routes
  7. Ephemeral Peer Negotiation (lib.rs:283-301):
    • If quantum-resistant or DAITA enabled
    • Establishes PSK via post-quantum KEMs
    • Updates configuration with ephemeral keys
  8. Connectivity Check (lib.rs:343-359):
    • ICMP ping to gateway
    • Timeout on failure
  9. Default Route Installation (lib.rs:363-370):
    • 0.0.0.0/0 and ::/0 routes added last
    • Emits TunnelEvent::Up
  10. Monitoring (lib.rs:375-383):
    • Continuous connectivity monitoring
    • Automatic MTU detection (optional)

MTU Handling

MTU Calculation

The tunnel MTU accounts for encapsulation overhead (lib.rs:1229-1249):
WireGuard Overhead (lib.rs:1276-1281):
  • IPv4: 20 (IP header) + 32 (WireGuard header) = 52 bytes
  • IPv6: 40 (IP header) + 32 (WireGuard header) = 72 bytes

MTU Clamping

Automatic MTU Detection

When MTU is not explicitly set, the system monitors for dropped packets and adjusts MTU (mtu_detection.rs):

Multihop Configuration

Single-Hop vs Multihop

Single-Hop:
Multihop:

Peer Structure

In multihop mode (config.rs:134-146):

Routing Configuration

Multihop requires special route MTU considerations (lib.rs:989-1013):

Connectivity Monitoring

Pinger Implementation

The connectivity module (connectivity/mod.rs) implements ICMP/ICMPv6 pings to verify tunnel connectivity:
Establishment Check (lib.rs:343-359):

Continuous Monitoring

After tunnel is up, monitoring continues (lib.rs:375-383):

Tunnel Statistics

Tunnel statistics are retrieved via the Tunnel trait:
Stats include per-peer traffic counters and DAITA overhead information (lib.rs:1026-1071).

Error Handling

Recoverable Errors

Certain errors allow automatic retry (lib.rs:103-119):

Close Messages

Tunnel shutdown is coordinated via channels (lib.rs:1073-1081):

wireguard-go Integration

FFI Bindings

The wireguard-go-rs crate (wireguard-go-rs/src/lib.rs) provides Rust bindings to wireguard-go:

Configuration Management

Configuration updates use the userspace format: