Overview
The firewall integration is platform-specific:- Windows: Windows Filtering Platform (WFP)
- macOS: Packet Filter (PF)
- Linux: nftables
- Android/iOS: VPN Service API / Network Extension (no direct firewall access)
Windows - WFP Integration
Implementation
The Windows firewall is implemented inwindows/winfw/ and uses the Windows Filtering Platform (WFP) API.
Source: talpid-core/src/firewall/windows/mod.rs
Architecture
winfw organizes filters into sublayers with specific weights:
Baseline Sublayer
- Highest weight to process all traffic first
- Contains permit-filters (highest weight within sublayer)
- Contains blocking filters (lowest weight within sublayer)
- Permit-filters “lift” matched traffic to next sublayer
- Blocking verdict is final - matched traffic is dropped
Specialized Sublayers
- DNS sublayer: Filters DNS traffic (port 53)
- Endpoint sublayers: Handle relay and API endpoints
- All weighted slightly lower than baseline
- High-weighted permit-filters for whitelisted traffic
- Low-weighted blocking filters as catch-all
Persistent Sublayer
When the daemon exits while a blocking policy should remain:- Created with highest possible weight
- Contains blocking filters only
- Persists across BFE restarts and reboots
- Ensures traffic blocked until daemon reinitializes
- Active before BFE service starts during boot
windows/winfw/README.md
Key Functions
Hyper-V Leak Protection
Windows implements additional rules to prevent Hyper-V (WSL) traffic leaks:- Uses WMI to enumerate Hyper-V virtual switches
- Adds blocking rules for virtual adapters
- Can be disabled via
TALPID_FIREWALL_BLOCK_HYPERV=0
talpid-core/src/firewall/windows/hyperv.rs
Process Restrictions
WFP rules restrict which processes can communicate with the VPN endpoint:- Connecting state: Only
mullvad-daemon.exeor root processes - Connected state: Only tunnel interface or root processes
- Uses binary path matching on Windows
- Prevents unprivileged programs from fingerprinting endpoint
docs/security.md:174-182
macOS - PF Integration
Implementation
The macOS firewall uses the Packet Filter (PF) system. Source:talpid-core/src/firewall/macos.rs
Architecture
Anchor System
- Creates anchor named
mullvad - Anchor contains filter rules, NAT rules, and scrub rules
- Atomic rule updates via
pfctl::AnchorChange - Preserves/restores PF enabled state
Rule Types
Filter Rules:- Pass rules (allow traffic)
- Drop rules (block with
returnordropaction) - All rules can have
quickflag for immediate verdict
- Force traffic through VPN interface
- Fixes Apple services bypassing tunnel
- Masquerade non-loopback, non-LAN traffic to tunnel IP
- Disabled on unaffected macOS versions
talpid-core/src/firewall/macos.rs:26-45
Scrub Rules:
- Reassemble packet fragments
- Ensures complete transport headers for filtering
State Management
PF retains connection states even after rules removed:- Clears connection states on policy changes
- Preserves VPN endpoint connections
- Preserves in-tunnel allowed traffic during multihop
- Preserves LAN traffic when allowed
- Flushes all states when toggling split tunneling
talpid-core/src/firewall/macos.rs:102-213
Process Restrictions
PF rules restrict endpoint access to root user:- Uses
usercondition matching UID 0 - Only root processes can reach relay endpoints in blocking states
- Prevents unprivileged leak attempts
talpid-core/src/firewall/macos.rs:582-598
Debugging
SetTALPID_FIREWALL_DEBUG to enable rule logging:
pass- Log allowing rules onlydrop- Log blocking rules onlyall- Log all rules- Rules logged to
pflog0interface - View with:
tcpdump -netttti pflog0
talpid-core/src/firewall/macos.rs:60-69
Linux - nftables Integration
Implementation
The Linux firewall uses nftables with thenftnl library.
Source: talpid-core/src/firewall/linux.rs
Architecture
Table and Chains
Creates an inet table namedmullvad:
| Chain | Hook | Priority | Policy | Purpose |
|---|---|---|---|---|
prerouting | PreRouting | NF_IP_PRI_CONNTRACK + 1 | Accept | Mark incoming relay traffic |
input | In | 0 | Drop | Filter incoming packets |
output | Out | 0 | Drop | Filter outgoing packets |
forward | Forward | 0 | Drop | Filter forwarded packets |
mangle | Out | NF_IP_PRI_MANGLE | Accept | Mark split tunnel traffic |
nat | PostRouting | NF_IP_PRI_NAT_SRC | Accept | Masquerade split traffic |
talpid-core/src/firewall/linux.rs:286-315
Atomic Updates
All rule changes are atomic:- Create table (if not exists)
- Delete table (clear existing rules)
- Re-create table with new chains
- Add all rules in single batch
- Verify table exists in kernel
talpid-core/src/firewall/linux.rs:145-151
Firewall Mark
Uses firewall markfwmark for traffic classification:
- Marked packets can bypass tunnel (split tunneling)
- Applied to relay endpoint traffic
- Applied to split-tunneled process traffic
- Prevents reverse path filter issues
talpid-core/src/firewall/linux.rs:104-106
Process Restrictions
nftables rules restrict endpoint access:- Uses
meta skuidto match root user (UID 0) - Alternatively uses
meta markmatchingfwmark - Only root or marked traffic reaches endpoints in blocking states
talpid-core/src/firewall/linux.rs:786-811
Kernel Parameters
When establishing tunnel, sets kernel parameters:src_valid_mark
- Enables fwmark consideration for reverse path filtering
- Prevents strict
rp_filterfrom blocking relay traffic - Disable via
TALPID_FIREWALL_DONT_SET_SRC_VALID_MARK=1
docs/README.md:168-174
arp_ignore
- Reply to ARP only for IPs on receiving interface
- Prevents ARP probing of in-tunnel IP (CVE-2019-14899 mitigation)
- Disable via
TALPID_FIREWALL_DONT_SET_ARP_IGNORE=1
talpid-core/src/firewall/linux.rs:186-196
Debugging
SetTALPID_FIREWALL_DEBUG=1 to add packet counters:
talpid-core/src/firewall/linux.rs:73-77
Common Firewall Rules
All platforms implement these rules:Always Allowed
- Loopback traffic - Always permitted
- DHCP client - UDP 68→67 (v4), UDP 546→547 (v6)
- DHCP server (when Allow LAN) - UDP 67→68 (v4)
- NDP (Neighbor Discovery Protocol):
- Router solicitation (type 133)
- Router advertisement (type 134) from
fe80::/10 - Redirect (type 137) from
fe80::/10 - Neighbor solicitation (type 135)
- Neighbor advertisement (type 136)
docs/security.md:87-106
LAN Access (when enabled)
Allows traffic to/from: Private networks:10.0.0.0/8172.16.0.0/12192.168.0.0/16169.254.0.0/16(link-local IPv4)fe80::/10(link-local IPv6)fc00::/7(ULA)
224.0.0.0/24(local IPv4)239.0.0.0/8(admin IPv4)255.255.255.255/32(broadcast)ff01::/16throughff05::/16(IPv6 multicast)
docs/security.md:107-127
Policy-Specific Rules
Connecting State
- Allow tunnel endpoint (IP:port:protocol)
- Allow API endpoint (root only)
- Block all DNS except to relay (if multihop)
- Allow in-tunnel traffic (if created)
- Allow LAN (if enabled)
docs/security.md:166-189
Connected State
- Allow tunnel endpoint
- Allow DNS to relay or custom servers only
- Block all other DNS
- Allow all tunnel interface traffic
- Allow LAN (if enabled)
docs/security.md:194-208
Blocked State
- Allow API endpoint (root only, if specified)
- Block all DNS
- Allow LAN (if enabled)
docs/security.md:221-234
API Access
All platforms allow API traffic in all states:- Connected: API traffic goes through tunnel
- Other states: API bypasses firewall
- Windows: Only
mullvad-daemon.exeand problem report tool - macOS/Linux: Only root processes
docs/security.md:133-144
Mobile Platforms
Android
Uses VPN Service API instead of firewall:- Routes
0.0.0.0/0and::/0through app - Applies in connecting, connected, and error states
- System “Block connections without VPN” provides OS-level enforcement
- Exempt traffic: connectivity checks, NTP, hotspot
docs/security.md:32-62
iOS
Uses Network Extension packet tunnel:- Packet tunnel process handles all traffic
- Routing rules force all traffic through tunnel
- Delegates to
wireguard-gofor tun interface - Local network always accessible
docs/security.md:71-76