enforceRoutes causes excludedRoutes to be ignored

In our PacketTunnelProvider we are seeing behavior for enforceRoutes which appears to contradict the documentation.

According to the developer documentation (my emphasis):

If this property is YES when the includeAllNetworks property is NO, the system scopes the included routes to the VPN and the excluded routes to the current primary network interface.

If we set these IPv4 settings:

IPv4Settings = {
    configMethod = manual
    addresses = (
        172.16.1.1,
    )
    subnetMasks = (
        255.255.255.255,
    )
    includedRoutes = (
        {
            destinationAddress = 0.0.0.0
            destinationSubnetMask = 0.0.0.0
        },
    )
    excludedRoutes = (
        {
            destinationAddress = 10.10.0.0
            destinationSubnetMask = 255.255.255.0
        },
    )
    overridePrimary = YES
}

Then if enforceRoutes is set to YES, then we do not see traffic for the excluded network, which is the expected behavior. If enforceRoutes is set to NO, then we do see traffic for the excluded network.

In both cases includeAllNetworks and excludeLocalNetworks are both NO.

The excluded network is not one of the local LANs.

Is this a known issue? Is there some documented interaction that I missed here?

Is there a workaround we can use to make this function as intended, with enforceRoutes set to YES?

Interesting. Regarding:

In both cases includeAllNetworks and excludeLocalNetworks are both NO.

If you set excludeLocalNetworks and enforceRoutes to YES does this properly exclude the traffic in your excludedRoutes? If not, then I would open a bug report here just to see if this is something that needs to be further investigated.

I'll check this, but the excluded routes are not local networks. They're routes defined by the VPN gateway admin, who doesn't necessarily know anything about the user-side physical networks.

If it does work I'd be mightily surprised (and think that the documentation was even more obscure than I do now).

Kevin

The definition of excludedRoutes:


excludedRoutes

The IPv4 network traffic that the system routes to the primary physical interface, not the TUN interface.


Makes it clear that they should not be sent to the TUN interface. This is not what's happening.

If we define neither includeAllNetworks nor enforceRoutes, then the excludedRoutes are properly excluded.

If we define either includeAllNetworks or enforceRoutes, then the excludedRoutes are ignored, and all traffic gets tunneled.

We tested every combination of the enforceRoutes, includeAllNetworks, excludeLocalNetworks, and overridePrimary options.

We can provide logs showing the network settings provided to the Network Extension framework at start time, along with the protocol settings, as well as the traffic from the excluded network going to the VPN extension.

If we define either includeAllNetworks or enforceRoutes, then the excludedRoutes are ignored, and all traffic gets tunneled.

This is the part that needs to further investigation. I would expect that if you set includeAllNetworks to NO, enforceRoutes to YES, and then you have set of excludedRoutes that they be excluded from the tunnel and would go through the primary interface. I would open a bug report on this and add this to your thread.

Ok, I'll open a report. When I say I checked every combination I do mean every combination, even ones that didn't make sense. I would have expected the behavior you described. This is the log from enforceRoutes YES, includeAllNetworks NO, excludedRoutes set (pruned a bit because it was pretty verbose):

# Protocol properties seen by the extension:
#
[Jun 1, 2023 at 3:57:58 PM PDT] <Debug>: Protocol Properties:
    . . .
    includeAllNetworks = NO
    excludeLocalNetworks = NO
    excludeCellularServices = YES
    excludeAPNs = YES
    enforceRoutes = YES
    . . .

# Network settings we're passing to setTunnelNetworkSettings:completionHandler:
#
[Jun 1, 2023 at 3:57:59 PM PDT] <Debug>: setting NEPacketTunnelNetworkSettings:
{
    tunnelRemoteAddress = 10.200.1.200
    DNSSettings = {
        protocol = cleartext
        server = (
            172.16.1.1,
        )
        matchDomainsNoSearch = NO
    }
    proxySettings = {
        autoProxyDiscovery = NO
        autoProxyConfigurationEnabled = NO
        HTTPEnabled = NO
        HTTPSEnabled = NO
        FTPEnabled = NO
        SOCKSEnabled = NO
        RTSPEnabled = NO
        gopherEnabled = NO
        excludeSimpleHostnames = NO
        usePassiveFTP = YES
    }
    IPv4Settings = {
        configMethod = manual
        addresses = (
            172.16.1.1,
        )
        subnetMasks = (
            255.255.255.255,
        )
        includedRoutes = (
            {
                destinationAddress = 0.0.0.0
                destinationSubnetMask = 0.0.0.0
            },
        )
        excludedRoutes = (
            {
                destinationAddress = 10.10.0.0
                destinationSubnetMask = 255.255.255.0
            },
        )
        overridePrimary = NO
    }
}


# And here we have the message printed when we receive a SYN packet for a destination.  This
# should never be seen for excluded routes
#
[Jun 1, 2023 at 3:58:11 PM PDT] <Debug>: Trying to establish TCP tunnel to server 10.10.0.2:443...

hi @kbrock, Did you find the solution or any workaround? I do see the problem still.

enforceRoutes causes excludedRoutes to be ignored
 
 
Q