Skip to content

Add user-configurable IP/subnet split tunneling on Windows#9968

Open
bearinmindcat wants to merge 11 commits intomullvad:mainfrom
bearinmindcat:ip-split-tunneling
Open

Add user-configurable IP/subnet split tunneling on Windows#9968
bearinmindcat wants to merge 11 commits intomullvad:mainfrom
bearinmindcat:ip-split-tunneling

Conversation

@bearinmindcat
Copy link

@bearinmindcat bearinmindcat commented Mar 10, 2026

What

Add the ability for users to specify IP addresses and CIDR subnets to
bypass the VPN firewall (created by mullvad) on Windows by letting traffic
from programs such as Tailscale pass-through (which currently doesn't work
for the app based Split Tunneling). This addition would allow services like
Tailscale to work alongside Mullvad since Tailscale communicates over a subnet.

(lots of people requesting this feature, can also make a PR for linux as well)

Why

Why? Simply because multiple users (like me) and other in a reddit post I made
about a personal change would be really glad to see this implemented
(https://www.reddit.com/r/mullvadvpn/comments/1rmeyxj/would_anyone_else_use_this_worth_openning_a_pr_to/)
As of right now the only ways to get this to work is to disable mullvad entirely, but
my addition to the app would allow people to use both tailscale (which is already encrypted) with mullvad.

I'd also like to highlight there were other instances of people requesting this feature, and
I believe only one person who tried to open a PR as well.

(shown below)

This was suggested by @faern in #6089:

"Maybe we should consider adding a feature where the user can enter a
list of networks that they want unblocked?"

Related: #5921, #2071, #4557, #6219, #9537

How

Feature is opt-in (no on/off switch like Split Tunneling) but you can add your own
IP/Subnets manually, using a very similar GUI as what's already there with Split Tunneling.

Added a new field called "ip_exclusions" in the "SplitTunnelSettings" that stores a list
of user-specified IP networks to exclude from the VPN tunnel, with there being persistence
between sessions via settings migration v15.

Added a WFP firewall rule called PermitSplitTunnelSubnets, built to follow the same pattern
already being used with PermitLan rule. It creates both outbound & inbound filters so traffic
can flow in both directions without being superseded or interrupted by mullvad.

When a user adds or removes a subnet, the change flows through the already existing AllowLan
pipeline: with the setting being saved, daemon sending a TunnelCommand, which updates the
SharedTunnelStateValues, thus triggering the firewall to reapply its rules with the updated
subnet list from the users ip_exclusion settings.

In the Split Tunneling (IP) section the gRPC interface pipeline has three commands.
AddSplitTunnelIpNetwork - add a subnet/IP
RemoveSplitTunnelIpNetwork - remove a subnet/IP
ClearSplitTunnelIpNetworks - remove all subnets/IPs (no UI button for this yet, but can implement if needed)

All changes affect the WFP firewall that the windows client already uses for AllowLan so we don't
have to do any kernel-level or deeper config to split tunnels.

How to test

  1. Add a split tunnel subnet via CLI:
    mullvad split-tunnel ip add 100.64.0.0/10
  2. List current exclusions: mullvad split-tunnel ip list
  3. Connect to Mullvad VPN
  4. Verify traffic to the excluded subnet bypasses the VPN
  5. Verify all other traffic still routes through VPN
  6. Remove the subnet: mullvad split-tunnel ip remove 100.64.0.0/10
  7. Verify the subnet is blocked again
  8. Test via desktop GUI: Settings > Split tunneling (IP)

I have done all of this myself as well to verify tailscale connections from
my self hosted server can get though the Mullvad firewall.


This change is Reviewable

Add a new `ip_exclusions` field to split tunnel settings, containing
a list of IP networks (CIDR notation) whose traffic should bypass the
VPN firewall. Include settings migration v15 -> v16 to add the field.
Add SetSplitTunnelIpNetworks and GetSplitTunnelIpNetworks RPCs to
the management interface proto definition, along with client methods
and settings type conversions for IP network exclusions.
Add daemon command handling and management interface service methods
for adding, removing, and listing IP network exclusions. Changes
propagate through TunnelCommand to the firewall policy.
Add PermitSplitTunnelSubnets WFP rule with both outbound and inbound
filters for bidirectional communication with user-specified IP
networks. Modeled after the existing PermitLan rule. Pass excluded
subnets through WinFwSettings to the C++ firewall context.
Add split tunnel subnet handling to all tunnel states. Subnets are
stored in SharedTunnelStateValues and applied via firewall policy
reapply, following the same pattern as AllowLan.
Add 'mullvad split-tunnel ip' subcommands: add, remove, list, clear,
set-state, and get-state. Enables managing IP network exclusions from
the command line.
Add Split Tunneling (IP) view with input validation, add/remove
functionality, and animated list UI. Includes settings navigation
list item, Redux state management, IPC schema updates, and daemon
RPC integration. All strings use pgettext for i18n support.
- Move React hooks before conditional return (rules-of-hooks)
- Extract IncludedIpRow and ExcludedIpRow components (jsx-no-bind)
- Run cargo fmt on Rust files
- Run eslint --fix on TypeScript files
@hulthe
Copy link
Contributor

hulthe commented Mar 13, 2026

Hello! First of all, awesome feature. I know that many of us would find it useful.

We will need some time to come to a decision though. We're currently discussing internally whether we want to accept this feature as-is, accept it in some reduced form (maybe cli only for now?), or whether we'd prefer to go a different route.

There's also some technical decisions we are unsure of. For example, should we add routes for split tunneled IPs? How would that work? In it's current form, this feature does not address #6219 afaict, but it potentially could.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants