Tunnel State Machine
The tunnel state machine (TSM) is the core component of Talpid that coordinates the lifecycle of VPN tunnel connections. It enforces security policies, manages system configuration, and ensures that no traffic leaks outside the tunnel when a secure connection is requested.Security Philosophy
The tunnel state machine is designed with a fail-secure approach:- Security policies are applied before establishing a connection
- Errors result in blocking states that prevent traffic leaks
- System configuration is restored when disconnecting
- State transitions are atomic and carefully ordered
- The machine never allows unprotected traffic when user requested a secure connection
State Diagram
The tunnel state machine has five primary states:State Descriptions
Disconnected
Description: Initial and final resting state. The system is in its normal configuration with no VPN active. Characteristics:- No tunnel is active
- No security policies applied (unless lockdown mode enabled)
- System DNS, routing, and firewall are in default state
- No changes to the operating system
- Firewall blocks all traffic even in disconnected state
- Only exception: API endpoint for logging in and updating relay list
- → Connecting: When user requests connection or auto-connect triggers
Connecting
Description: Actively establishing a VPN tunnel. Security policies are applied to prevent leaks during connection setup. What happens on entry:- Firewall rules applied - Block all traffic except:
- VPN server endpoint
- DHCP (if needed)
- LAN traffic (if allow LAN enabled)
- DNS configuration - Set to Mullvad DNS or custom DNS
- Routing tables - Prepare routes for tunnel
- Tunnel creation - Start WireGuard or OpenVPN process
- Parameter generation - Generate tunnel parameters:
- Select relay server
- Generate ephemeral keys (if quantum-resistant)
- Configure obfuscation (if needed)
- Connection establishment - Connect to selected relay
- Connectivity verification - Verify tunnel is working
- Only the VPN endpoint can be reached
- All other traffic is blocked by firewall
- Multiple connection attempts may be made
- Offline detection can pause attempts
- → Connected: Tunnel is up and verified working
- → Error: Unrecoverable error during connection
- → Disconnecting: User requests disconnect
- → Connecting (same state): Reconnect with different parameters (e.g., new relay)
- Automatic retry with same relay on transient failures
- Switch to different relay after repeated failures
- Respect offline state (pause attempts when no network)
Connected
Description: Tunnel is established and verified working. All traffic is routed through the VPN. What happens on entry:- Tunnel monitor started - Continuously verifies connectivity
- Final routing configuration - Ensure all traffic goes through tunnel
- Split tunneling applied - Exclude specified apps (if configured)
- Location query - Determine exit IP and location
- State broadcast - Notify frontends of successful connection
- All traffic routed through VPN tunnel
- Firewall enforces tunnel-only traffic
- Continuous monitoring of tunnel health
- GeoIP queries to verify exit location
- Leak checker validates no leaks (on supported platforms)
- Tunnel monitor: Sends periodic pings through tunnel
- Connectivity check: Verifies internet connectivity
- Offline detection: Detects if network interface goes down
- → Disconnecting: User requests disconnect
- → Connecting: Intentional reconnect (relay change, settings update)
- → Disconnecting → Error: Unrecoverable error detected
Disconnecting
Description: Tearing down the tunnel and restoring system to original configuration. What happens in this state:- Stop tunnel monitor
- Close tunnel connection - Terminate WireGuard/OpenVPN
- Remove routes - Clean up routing table entries
- Restore DNS - Return to system default DNS
- Remove firewall rules - Clean up tunnel-related rules
- Split tunneling cleanup
- AfterDisconnect::Nothing → Disconnected: Normal disconnect, return to disconnected state
- AfterDisconnect::Block → Error: Error occurred, enter error state
- AfterDisconnect::Reconnect → Connecting: Reconnect with new parameters
- → Disconnected: Cleanup complete, after-action was Nothing
- → Error: Cleanup complete, after-action was Block
- → Connecting: Cleanup complete, after-action was Reconnect
Error
Description: An unrecoverable error occurred. All traffic is blocked to prevent leaks. Characteristics:- All traffic blocked (except API endpoint)
- User cannot access network
- Firewall in maximum security configuration
- Error cause is recorded and displayed to user
- Authentication failure (invalid account/device)
- No relays match current constraints
- Firewall configuration failure
- Tunnel creation failure (missing kernel modules, driver issues)
- Device revoked or removed
- Account expired
-
Blocking: Successfully configured firewall to block traffic
- System is secured, no leaks possible
- User cannot access network until issue resolved
-
Non-blocking: Failed to configure firewall
- Rare and serious: indicates system firewall unavailable
- Traffic may leak; user is warned
- → Connecting: User takes action to resolve error (new account, change settings)
- → Disconnecting → Disconnected: User requests disconnect
- Some errors auto-resolve (temporary API failures)
- Most require user intervention (account issues, setting changes)
- UI displays error with actionable guidance
State Machine Inputs
The tunnel state machine reacts to two types of inputs:Commands
Commands are sent from the daemon to control tunnel behavior:Connect
- Effect: Establish a secure VPN connection
- From Disconnected: → Connecting
- From Error: → Connecting (attempt to recover)
- From Connecting/Connected: No effect (already connecting/connected)
Disconnect
- Effect: Tear down VPN connection and return to disconnected
- From Connected/Connecting: → Disconnecting → Disconnected
- From Error: → Disconnecting → Disconnected
- From Disconnected: No effect
Reconnect
- Effect: Disconnect and immediately reconnect (used for setting changes)
- From Connected: → Disconnecting → Connecting
- From Connecting: → (new) Connecting with new parameters
- From Disconnected: → Connecting
AllowLan(bool)
- Effect: Enable/disable local network access while connected
- Updates firewall rules in real-time
- No state transition
BlockWhenDisconnected(bool)
- Effect: Enable/disable lockdown mode
- Configures whether firewall blocks traffic in disconnected state
- No state transition, but affects disconnected state behavior
External Events
Events from the system or tunnel monitor that the state machine reacts to:TunnelUp
- Source: Tunnel monitor
- Effect: Connectivity verified, tunnel is working
- Transition: Connecting → Connected
TunnelDown
- Source: Tunnel monitor
- Effect: Tunnel connection lost
- From Connected: → Disconnecting → Connecting (attempt reconnect)
- From Connecting: Retry or try different relay
TunnelMonitorStopped
- Source: Tunnel monitor process died
- Effect: Lost ability to monitor tunnel
- Transition: Connected → Disconnecting → Error (cannot verify tunnel)
IsOffline(bool)
- Source: Offline monitor
- Effect: Network connectivity changed
- true (offline): Pause connection attempts in Connecting state
- false (online): Resume connection attempts
- Windows: Default route existence + suspend/resume events
- Linux: Route to public IP via exclusion mark (Netlink)
- macOS: SCDynamicStore network service availability
- Android: ConnectivityManager network callbacks
- iOS: NWPathMonitor route monitoring
State Machine Outputs
Every state transition produces aTunnelStateTransition event broadcast to all subscribed frontends:
TunnelStateTransition::Disconnected
TunnelStateTransition::Connecting
TunnelStateTransition::Connected
TunnelStateTransition::Disconnecting
TunnelStateTransition::Error
Connection Flow Details
Standard Connection Sequence
Reconnection on Setting Change
Error Recovery
Quantum-Resistant Tunnels
When quantum-resistant encryption is enabled, the connection process involves additional steps:Single-Hop Quantum-Resistant
Multihop Quantum-Resistant
talpid-tunnel-config-client/proto/ephemeralpeer.proto for protocol details.
Platform-Specific Considerations
Windows
- Suspend/resume events affect offline detection
- WinFW firewall integration (see winfw documentation)
- Split tunneling via driver
- Network interface changes require special handling
Linux
- Netlink for routing and offline detection
- iptables/nftables for firewall
- Split tunneling via cgroups v2 or v1 (net_cls)
- Firewall mark for exclusion routing
macOS
- SCDynamicStore for offline detection
- macOS may delay route publication after wake see documentation
- Split tunneling via driver
- Filtering resolver for DNS
Android
- VpnService.Builder API
- Per-app VPN (split tunneling)
- ConnectivityManager for offline detection
- Always-on VPN support
iOS
- NEPacketTunnelProvider API
- WireGuard-kit handles tunnel
- NWPathMonitor for offline detection
- Network extension sandboxing
Debugging and Observability
State transitions are logged extensively:- Timestamp
- Previous state
- New state
- Reason for transition
- Relevant endpoint/error information
Related Documentation
- Daemon Architecture - How TSM integrates with daemon
- Architecture Overview - Overall system design
- Frontends - How frontends receive state updates
- Security Model - Security guarantees
- Split Tunneling - Split tunneling implementation