Skip to main content

Overview

The offline monitor detects when the device has no network connectivity, allowing the tunnel state machine to avoid futile connection attempts and properly inform the user. Each platform uses different mechanisms to achieve reliable offline detection without generating network traffic. Reference: talpid-core/src/offline/mod.rs, docs/architecture.md:218-269

Design Principles

No Network Traffic

The offline monitor must not send any network traffic to determine offline state:
  • Cannot perform ping tests
  • Cannot attempt DNS lookups
  • Cannot connect to remote servers
This ensures:
  • No information leakage during offline detection
  • No interference with firewall policies
  • Reliable operation in secured states
Reference: docs/architecture.md:220-223

Platform-Specific Implementation

Each platform provides different APIs and mechanisms:
  • Windows: Route table monitoring + power state tracking
  • Linux: Route queries via Netlink with firewall mark
  • macOS: System Configuration framework with synthetic offline periods
  • Android: ConnectivityManager network callbacks
  • iOS: NWPathMonitor (via WireGuard Kit)
Reference: talpid-core/src/offline/mod.rs:9-23

Windows Implementation

Detection Strategy

Connectivity is inferred if:
  1. A default route exists, AND
  2. The machine is not suspended
Reference: docs/architecture.md:226-229

Route Monitoring

Reference: talpid-core/src/offline/windows.rs:79-99 NotifyRouteChange2 API: Listens for default route changes via Windows networking API:
Receives callbacks whenever a default route is:
  • Added
  • Removed
  • Modified
Reference: talpid-core/src/offline/windows.rs:69-77, NotifyRouteChange2 docs

Power State Tracking

The machine is considered offline after suspend until a grace period expires:
Why the grace period? Tunnel device drivers may not work correctly immediately after wakeup. The 5-second offline period ensures the system is fully ready before attempting to connect. Reference: talpid-core/src/offline/windows.rs:46-67, docs/architecture.md:230-236

State Tracking

If suspended, always reports offline regardless of routes. Reference: talpid-core/src/offline/windows.rs:35-39

Linux Implementation

Detection Strategy

Connectivity is inferred by checking if a route exists to a public IP address:
  • Query: “Is there a route to 193.138.218.78 (Mullvad API)?”
  • Route query uses exclusion firewall mark
  • Without the mark, query would always succeed in connected state (route via tunnel)
Reference: docs/architecture.md:239-245
Reference: talpid-core/src/offline/linux.rs:24-100

Firewall Mark Coupling

The offline monitor is coupled to routing and split tunneling on Linux:
  • Uses the same firewall mark as excluded processes
  • Route query bypasses tunnel routing table
  • Ensures accurate offline detection even when tunnel is connected
Reference: docs/architecture.md:242-245

Change Listener

Listens for routing table changes via Netlink and rechecks connectivity. Reference: talpid-core/src/offline/linux.rs:35-71

macOS Implementation

Detection Strategy

Detects offline state using SCDynamicStore to check for active network services:
Reference: talpid-core/src/offline/macos.rs:64-81, SCDynamicStore docs

Synthetic Offline Period

A major issue on macOS: the app can get stuck offline after network changes due to DNS blocking. The Problem: After waking from sleep or switching networks:
  1. macOS performs connectivity checks (including DNS queries)
  2. Mullvad’s firewall blocks DNS (security policy)
  3. macOS’s checks time out, delaying route publication
  4. Network reachability callback not invoked until timeouts complete
  5. App thinks it’s offline for extended period
Reference: docs/architecture.md:252-259, talpid-core/src/offline/macos.rs:1-10 The Solution: Synthesize a brief offline state between network transitions:
The 1-second synthetic offline period:
  • Prevents DNS blocking from delaying connectivity
  • Allows macOS connectivity checks to complete
  • Ensures default route is available before connecting
Reference: talpid-core/src/offline/macos.rs:24,92-131

Route Listener

Uses RouteManagerHandle::default_route_listener() to observe default route changes:
  • Receives DefaultRouteEvent::Updated with IPv4/IPv6 route information
  • No active polling required
  • Efficient, event-driven design
Reference: talpid-core/src/offline/macos.rs:71, RouteManagerHandle::default_route_listener docs

Android Implementation

Detection Strategy

Relies on Android’s ConnectivityManager API:
Reference: talpid-core/src/offline/android.rs:1-32, ConnectivityManager docs

Connectivity Listener

The ConnectivityListener (Kotlin/Java) registers callbacks with Android:
Key constraints:
  • NET_CAPABILITY_INTERNET: Network provides internet connectivity
  • NET_CAPABILITY_NOT_VPN: Excludes VPN networks (only physical networks)
Connectivity is inferred if any non-VPN network with internet exists. Reference: docs/architecture.md:261-264

iOS Implementation

Detection Strategy

The iOS app uses WireGuard Kit’s offline detection, which internally uses NWPathMonitor:
Reference: docs/architecture.md:266-269, NWPathMonitor docs

Path Status

NWPathMonitor provides:
  • satisfied: Default route exists, connectivity likely
  • unsatisfied: No default route
  • requiresConnection: Connection needs to be established
The app assumes connectivity if status is satisfied.

Connectivity Type

The offline monitor reports connectivity as:
Reference: talpid-types/src/net.rs

IPv4 vs IPv6

Most platforms track IPv4 and IPv6 connectivity separately:
  • Allows IPv6-only or IPv4-only selection
  • Prevents connection attempts when protocol unavailable
  • Informs relay selection algorithm

Tunnel State Machine Integration

The offline monitor sends connectivity updates to the tunnel state machine:
Reference: talpid-core/src/offline/mod.rs:43-72

State Machine Response

When the tunnel state machine receives offline notification:
  • Connecting state: Stop attempting connection, enter offline wait
  • Connected state: Depends on offline timeout settings
  • Error state: May stay in error (already blocking)
  • Disconnecting/Disconnected: No action needed
When online notification received:
  • Resume connection attempts
  • Retry with current relay selection
Reference: docs/architecture.md:167-170

Fail-Open Strategy

All platform implementations “fail open” when uncertain:
Why fail open?
  • Prevents blocking the user when offline detection fails
  • Connectivity attempts may still succeed
  • User experience is better than being stuck
  • Security is not compromised (firewall still active)
Reference: talpid-core/src/offline/linux.rs:86-92, windows.rs:86-87,94-97

Disabling Offline Monitor

For debugging, the offline monitor can be disabled:
When disabled:
  • Always reports Connectivity::PresumeOnline
  • Tunnel state machine never enters offline wait
  • Useful for debugging connectivity issues
Reference: talpid-core/src/offline/mod.rs:26-30,49-51

API Communication Coordination

The offline monitor also affects API communication:
  • API requests blocked when offline
  • Prevents wasted connection attempts
  • Resumes when connectivity restored
See API Communication for details. Reference: docs/architecture.md:61-62

Performance Considerations

Event-Driven Design

All implementations use event-driven approaches:
  • No polling loops
  • Low CPU usage
  • Immediate response to connectivity changes

Minimal Overhead

  • No network traffic generated
  • Uses platform-native APIs
  • Efficient state tracking

Testing and Debugging

Simulating Offline State

Network disconnect:
Remove default route:

Monitoring Events

Enable debug logging to see offline detection events:
Look for log messages like:

Common Issues

macOS Stuck Offline

If the app remains offline on macOS after network changes:
  1. Check if DNS is being blocked by firewall
  2. Verify synthetic offline period is working
  3. Consider allowing macOS network check
Reference: docs/allow-macos-network-check.md

Linux False Positives

If offline detection incorrectly reports online:
  1. Verify firewall mark is set correctly
  2. Check if split tunneling configuration is correct
  3. Ensure routing tables are properly configured

Windows Sleep/Wake Issues

If connections fail after waking from sleep:
  1. Check if grace period is sufficient (default 5 seconds)
  2. Verify power management events are being received
  3. Consider increasing grace period for slow hardware