My question has to do with the rules that govern the network interface to which exclude routes are bound when calling NEPacketTunnelProvider::setTunnelNetworkSettings().
In most cases, it seems that the exclude routes get bound to Wifi if Wifi is available and to Cellular otherwise. This makes sense to me as it’s consistent with the selection of default route on the device. However, I’ve encountered a particular scenario which is inconsistent with the behavior described above.
My scenario is as follows:
- The VPN is configured to exclude the top half of the IP range (128.0.0.0/1).
- The VPN is started with both Wifi and Cellular networks available. setTunnelNetworkSettings is called resulting in the excluded route being bound to Wifi.
- While the VPN is connected, the Wifi interface is disabled. This results in the an update to the VPN configuration which results in the excluded route being bound to Cellular.
- The VPN is stopped.
- The Wifi interface is re-enabled.
- The VPN is restarted. At this point, the exclude routes get bound to Cellular and the status bar at the top of the iPhone displays LTE when it should display Wifi. At this same time, the Settings app shows that Wifi is connected and in fact the VPN's tunnel traffic is going over the Wifi interface.
Note that when specifying the exclude routes in the VPN configuration, they include a valid gateway address for the relevant physical interface. In other words, when our exclude routes gets (inappropriately) bound to Cellular, the exclude route passed to setTunnelNetworkSettings contains the gateway address of the wifi interface.
Also note that, when the VPN starts up and Wifi is available, the default route that has no RTF_IFSCOPE flag is always associated with the Wifi interface - even when the problem described above occurs.
Also note that, after the problem behavior occurs, if Wifi is toggled off/on while the VPN is connected, the problem will correct itself.
Is there a reasonable explanation for this behavior?