Overview
Routing management handles:- Default route replacement - Routes all traffic through tunnel
- Specific routes - Allows API access and relay connectivity
- Split tunneling - Excludes specific applications from tunnel
- Route monitoring - Detects and handles external changes
talpid-routing/src/lib.rs
Core Concepts
Route Structure
A route consists of:talpid-routing/src/lib.rs:75-83
Node Types
Gateway Node:talpid-routing/src/lib.rs:226-249
Route Destinations
Real Node:talpid-routing/src/lib.rs:199-210
Windows Routing
Implementation
Uses IP Helper API and routing change notifications: Source:talpid-routing/src/windows/mod.rs
Route Management
Add route:Default Route Handling
Windows monitors default route changes: Detection:- Listens for
NotifyRouteChange2callbacks - Detects new default gateway
- Updates symbolic default routes
talpid-routing/src/windows/default_route_monitor.rs
Best route selection:
- Interface with lowest metric
- Gateway IP address
- Used to route non-tunnel traffic
talpid-routing/src/windows/get_best_default_route.rs
Split Tunneling (Windows)
Windows implements split tunneling via driver: Architecture:- WFP callout driver intercepts packets
- Checks process path against exclusion list
- Redirects excluded traffic to alternative interface
- Preserves in-tunnel for other traffic
talpid-core/src/split_tunnel/windows/mod.rs
Driver communication:
- Opens device handle to driver
- Sends IOCTLs to configure exclusions
- Monitors driver status
- Watches for excluded executable changes
- Updates driver when paths modified
- Handles volume mount/unmount events
talpid-core/src/split_tunnel/windows/path_monitor.rs
Linux Routing
Implementation
Uses Netlink (rtnetlink) for routing operations: Source:talpid-routing/src/unix/linux.rs
Routing Tables
Linux uses multiple routing tables: Main table (254):
- Default application routes
- Tunnel default route
1984):
- Split tunnel routes
- Excluded process routes
- Selected via fwmark
talpid-core/src/firewall/linux.rs:98-100
Route Operations
Add route:RTA_DST- Destination prefixRTA_GATEWAY- Gateway IPRTA_OIF- Output interface indexRTA_PRIORITY- MetricRTA_TABLE- Routing table IDRTA_METRICS- MTU and other metrics
talpid-routing/src/unix/linux.rs
Default Route Replacement
Tunnel setup replaces default route:Policy Routing
Linux uses policy routing for split tunneling: IP rules:Split Tunneling (Linux)
cgroup2 Method (Modern)
Primary method using cgroups v2: Source:talpid-core/src/split_tunnel/linux/mod.rs:99-112
Setup:
-
Create cgroup2 at
/sys/fs/cgroup/mullvad-exclusions -
Add PIDs to cgroup:
-
nftables matches cgroup:
talpid-core/src/firewall/linux.rs:355-375
How it works:
- Processes added to cgroup2
- All packets from cgroup get fwmark
- Policy routing sends marked packets to custom table
- Custom table routes to non-tunnel interface
/sys/fs/cgroup
Customize: TALPID_CGROUP2_FS=/custom/path
Source: docs/README.md:207-210
net_cls Method (Legacy)
Fallback using cgroups v1 net_cls controller: Source:talpid-core/src/split_tunnel/linux/mod.rs:114-124
Setup:
-
Mount net_cls (if needed):
Customize:
TALPID_NET_CLS_MOUNT_DIR=/custom/path -
Create cgroup:
-
Add PIDs:
-
nftables matches classid:
talpid-core/src/firewall/linux.rs:376-383
How it works:
- Processes added to net_cls cgroup
- Packets inherit classid (0xf41)
- nftables matches classid
- Applies fwmark for routing
docs/README.md:212-214
Firewall Integration
Split tunnel rules in nftables: Mangle chain (mark packets):talpid-core/src/firewall/linux.rs:391-495
Connection Tracking
Connection tracking ensures consistency:- New connections marked based on cgroup
- Subsequent packets match conntrack mark
- Survives process exit or cgroup removal
talpid-core/src/firewall/linux.rs:426-441
macOS Routing
Implementation
Uses routing socket and BSD routing APIs: Source:talpid-routing/src/unix/macos/mod.rs
Routing Socket
MacOS communicates viaPF_ROUTE socket:
Operations:
RTM_ADD- Add routeRTM_DELETE- Delete routeRTM_GET- Query routeRTM_CHANGE- Modify route
talpid-routing/src/unix/macos/routing_socket.rs
Default Route Monitoring
Monitors default route changes: Detection:DefaultRouteAdded- New default gatewayDefaultRouteRemoved- Default gateway removedDefaultRouteChanged- Gateway IP changed
talpid-routing/src/unix/macos/watch.rs
Route parsing:
- Reads routing messages from socket
- Parses route attributes (dst, gateway, interface)
- Identifies default routes (
0.0.0.0/0,::/0) - Resolves MAC address via ARP
talpid-routing/src/unix/macos/default_routes.rs
Split Tunneling (macOS)
MacOS implements split tunneling via PF routing: Mechanism:- Process monitoring tracks excluded apps
- PF rules use BPF device filters
- Route-to directs traffic to physical interface
- Preserves tunnel traffic for other apps
talpid-core/src/split_tunnel/macos/mod.rs
PF route-to rule:
- Matches tunnel interface
- Routes to physical interface instead
- Bypasses tunnel for excluded processes
talpid-core/src/firewall/macos.rs:755-775
Process identification:
- Uses Endpoint Security framework
- Monitors process launch/exit
- Tracks process tree (parent-child)
- Updates exclusions dynamically
talpid-core/src/split_tunnel/macos/process.rs
BPF integration:
- Attaches BPF program to interface
- Filters packets by process
- Works with PF rules for routing
talpid-core/src/split_tunnel/macos/bpf.rs
Interface and Gateway Resolution
Gateway includes MAC address:talpid-routing/src/lib.rs:63-70
Android Routing
Implementation
Uses VPN Service API - no direct routing control: Source:talpid-routing/src/unix/android.rs
VPN Service Routing
Configuration:- System handles all routing automatically
- Cannot add specific routes
- Split tunneling via allowed/disallowed apps
Split Tunneling (Android)
Per-app exclusion:- Excluded apps bypass VPN entirely
- Use system default routes
- Get system DNS
docs/split-tunneling.md:63-73
Route Manager Interface
Initialization
Adding Routes
Simple route:talpid-routing/src/lib.rs:170-196
Removing Routes
Default Route Handling
Routes usingDefaultNode update automatically:
- Route manager detects change
- Updates all
DefaultNoderoutes - Uses new default gateway
talpid-routing/src/lib.rs:199-210
Common Routing Patterns
Tunnel All Traffic
Relay Endpoint Access
API Access
LAN Access
MTU Considerations
Routes can specify MTU:- WireGuard adds ~60 bytes overhead
- Prevents fragmentation
- Typical: 1420 for tunnel, 1380 for multihop
talpid-routing/src/lib.rs:192-196
Monitoring and Resilience
Route Change Detection
All platforms monitor routing changes: Windows:NotifyRouteChange2callbacks- Detects route additions/deletions
- Updates symbolic routes
talpid-routing/src/windows/default_route_monitor.rs
Linux:
- Netlink route notifications
- Monitors
RTM_NEWROUTE/RTM_DELROUTE - Validates expected routes
talpid-routing/src/unix/linux.rs
macOS:
- Routing socket messages
- Parses
RTM_*events - Updates gateway information
talpid-routing/src/unix/macos/watch.rs
Route Restoration
On disconnect:- Remove tunnel default routes
- Restore original default gateway
- Delete custom routes
- Reset routing tables (Linux)
Split Tunneling Summary
Windows
Method: WFP callout driver Granularity: Per executable path How: Driver intercepts and redirects packetsLinux
Method: cgroup2 + nftables (or net_cls fallback) Granularity: Per process (PID) How: Mark packets, policy routing to alternate tablemacOS
Method: PF route-to + process monitoring Granularity: Per process tree How: BPF + PF rules route to physical interfaceAndroid
Method: VPN Service disallowed apps Granularity: Per app package How: System bypasses VPN for excluded apps Source:docs/split-tunneling.md
Environment Variables
docs/README.md:207-214
Troubleshooting
Routes Not Applied
Check route manager status:Split Tunneling Not Working
Linux - Verify cgroup:Default Route Issues
Symptoms: No connectivity after tunnel up Diagnosis:- Verify tunnel interface exists
- Check default routes point to tunnel
- Verify relay route via physical interface
- Check firewall allows relay endpoint