context
If Apple does not provide a way to do this then it seems like our answer will be no, we won’t require VPN clients to do this on macOS. I can’t tell you with certainty though until we actually figure out this in detail. I need to do more digging since I haven’t seen anyone post any answers this question since I asked it yesterday.
That is your decision to make. Below I will provide you with more information regarding this so that you may be better informed.
From looking at the source code, I can confirm:
- MullvadVPN does not use Network Extension, it uses Packet Filter
- IVPN does not use Network Extension, it uses Packet Filter
- ProtonVPN uses Network Extension, it does not use Packet Filter
- * ProtonVPN previously did use Packet Filter when they first launched kill switch for macOS but have since stopped
Regarding Packet Filter, I will highlight this quote from an Apple engineer in 2019:
There’s a whole bunch of things that you can do with low-level services on macOS that just aren’t supported as APIs by Apple. PF is one example of this. PF is not suitable for use in third-party products because there’s no way to arbitrate the PF rules between the various folks who might be using PF
Another quote from apple engineer in 2025:
For context, the user’s initial question to the Apple engineer was about how to ensure that no traffic leaks outside the VPN tunnel because they wanted to migrate from Packet Filter to Network Extension.
we're interpreting TN3165 as a signal that pf should be considered deprecatedWell, we can’t deprecate something that we’ve never officially supported as an API (-: But, yes, our position is that, if you’re building Mac networking products using legacy techniques then you should move to a Network Extension provider. That includes PF, but other things as well, including manually manipulating UTUN interfaces and modifying the routing table ‘behind the back’ of
configd.
may not be available in the next major macOS releaseWe’ve made no announcement about timelines. However, our experience is that products using these legacy techniques tend to experience compatibility problems as we evolve the system, so it’d be better to move off them sooner rather than later.
It appears that Apple considers Packet Filter to be a legacy technique that may experience compatibility problems in future. However, the notion that Packet Filter will be deprecated is incorrect since it was not supported as an API in the first place.
Now, regarding the Network Extension framework, and in particular the includeAllNetworks issue. That same Apple engineer, states the following:
My go-to reference for this sort of stuff is Routing your VPN network traffic [1]. This outlines two options for preventing ‘escapes’:
- For a full tunnel, set
includeAllNetworksand then set exceptions via the variousexcludeXYZoptions.
He then addresses the user’s other question:
be able to block any traffic while the tunnel is not yet connectedHonestly, I’m not sure what the best path forward is for that requirement. I’m gonna research that and get back to you.
…
My initial thought on this front was to create a packet tunnel provider that starts and stays started regardless of the state of the underlying tunnel. The system will then route traffic for the destination network to you, and you can just drop it if the tunnel is down.
The drawback to this approach is that there isn’t a good way to start the tunnel at boot. We actually discussed this on the forums recently — see this thread — and there are approaches that work but nothing that I’d consider to be ‘good’.
An alternative approach is to create an NE filter data provider and have it and your packet tunnel provider cooperate to implement this feature. That is, when a new flow arrives at the filter, check whether the tunnel is up and, if it isn’t, block the flow.
Note that, if you use sysex packaging, which you really should, both NE providers run in the same process and thus this sort of cooperation is easy. It can be something as simple as a global variable (with ån appropriate mutex, of course).
Now, I will let others interpret this / verify - but the initial user did mark this answer as solving their answer. So I assume this means there is some possibility of not exposing IP addresses between server switches.
So, then, why is ProtonVPN leaking IP addresses on macOS during server switches?
I did a cursory investigation into this and believe that the issue is caused by their poor implementation of the Network Extension in macOS.
Disclaimer: This is my hypothesis, and I am not 100% confident it is correct. I am very open to being wrong here. I am not a software engineer or network engineer, so i cannot validate this. A lot of these findings come from my discussion with an AI code reviewer
This image shows the sequence of events that take place when you switch servers on ProtonVPN on macOS.
In essence, what the macOS client is doing is disconnecting from the tunnel, waiting >0.7 seconds, and connecting to a new tunnel. The macOS client fully depends on includeAllNetworks as the kill switch with no other backups.
Now, I am not 100% certain why the IP leak is occurring, but I think there are 2 possibilities:
includeAllNetworksmight require there to be a tunnel in order to work. By entering a NEVPNConnection.disconnectedstate,includeAllNetworksmay not be able to block the connection.
Apple documentation:
> source: The
NEVPNConnectionclass provides methods for starting and stopping the VPN programmatically.> source: NEVPNStatus.disconnected - The VPN is disconnected.
- ProtonVPN’s macOS client calls
exit(0)on the WireGuard system extension, which hosts theNEPacketTunnelProvidersubclass. Thisexit(0)method of stopping the tunnel is used for both server disconnects and server switches and preventsincludeAllNetworksfrom functioning correctly and blocking the user’s connection.
The apparent reason for using this exit(0)method is due to an old Apple bug that would crash NEPacketTunnelProvider that has seemingly since been fixed. It seems that ProtonVPN would have unscrupulously implemented a proposed fix that had this side effect.
Interestingly, this current behavior for macOS is flagged as “legacy”. For iOS, there is a ConnectionFeatureflag that does not result in the >0.7 second delay between tunnel switches. Instead, the tunnel is switched instantly. It is unclear to me if this resolves the kill switch issue, but it is an improvement.
Irrespective of the actual reason for the IP leak during server switching, one of my findings of the code was that ProtonVPN fully relies on includeAllNetworks alone with zero fallback in case of failure.
Again, I’m not fully confident with this assessment. Nevertheless, It seems that a lot of developers have been having issues with includeAllNetworks as demonstrated by the various bugs described on Apple’s developer forums. Its difficult through pure research to get a full understanding of the Network Extension’s expected behavior. I can see why many VPN providers have decided to outright reject it.
Given all of this, I think its understandable that other VPN providers may prefer to rely on Packet Filter and completely avoid Network Extension. My impression has been the it is Network Extension that is “hacky” rather than the other way around.
