Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

net/interfaces: handle iOS network transitions when exit node in use (ENG-2507) #10680

Merged
merged 1 commit into from
Jan 4, 2024

Conversation

agottardo
Copy link
Contributor

@agottardo agottardo commented Dec 22, 2023

Fixes ENG-2507
Updates #8022
Updates #6075

On iOS, we currently rely on delegated interface information to figure out the default route interface. When an exit node is being use, our utun* interface is the default route, so we ask the system for the delegated interface for the utun* interface we use. The NetworkExtension framework in iOS seems to set the delegate interface only once, upon the creation of the VPN tunnel. If a network transition (e.g. from Wi-Fi to Cellular) happens while the tunnel is connected, it will be ignored and we will still try to set Wi-Fi as the default route because the delegated interface is not getting updated as connectivity transitions.

Here, we special-case iPhones and iPads with a simpler logic that doesn't look at the routing table: we try finding a hardcoded Wi-Fi interface called en0, if it doesn't have an address, we fall back to cellular, which is pdp_ip0. This logic can safely run every time a link change is detected.

I tested this on iPhones and iPads running iOS 17.1 and it appears to work. Switching between different cellular plans on a dual SIM configuration also works (the interface name remains pdp_ip0).

To-Dos

  • handle USB Ethernet
  • verify behaviour on other Apple platforms
  • verify behaviour on devices with no cellular interface
  • verify behaviour on device with cellular data available, but disabled
  • verify behaviour with Wireless CarPlay

@agottardo agottardo self-assigned this Dec 22, 2023
@agottardo agottardo linked an issue Dec 22, 2023 that may be closed by this pull request
@agottardo agottardo changed the title net/interfaces: handle iOS network transitions net/interfaces: handle iOS network transitions (ENG-2507) Dec 22, 2023
@agottardo agottardo changed the title net/interfaces: handle iOS network transitions (ENG-2507) net/interfaces: handle iOS network transitions when exit node in use (ENG-2507) Dec 22, 2023
@bobbyl140
Copy link

This may be a very specific use case, but what would happen in the case of a USB to Ethernet adapter? I don't frequently use one, but I have done it before. If there was a way to incorporate support for those without much additional effort, that would be awesome. If not, I understand, this seems to be an Apple problem in the first place.

@agottardo
Copy link
Contributor Author

This may be a very specific use case, but what would happen in the case of a USB to Ethernet adapter? I don't frequently use one, but I have done it before. If there was a way to incorporate support for those without much additional effort, that would be awesome. If not, I understand, this seems to be an Apple problem in the first place.

Thanks for the suggestion. We lack logic for that. We should look at all en* interfaces (USB to Ethernet adapters appear to use en4 on my iPhone 15).

@agottardo
Copy link
Contributor Author

I feel like this is re-inventing the wheel. Might be better to observe NWPathHandler updates from the network extension and forward those updates to Go.

@bobbyl140
Copy link

Thanks for the suggestion. We lack logic for that. We should look at all en* interfaces (USB to Ethernet adapters appear to use en4 on my iPhone 15).

Bluetooth PAN is probably somewhere in there too, which isn't a problem on iPhones (as originators of Personal Hotspot), but on iPads where people might use Bluetooth to connect to their iPhone it could be prevalent.

@agottardo
Copy link
Contributor Author

agottardo commented Dec 24, 2023

Bluetooth PAN is probably somewhere in there too, which isn't a problem on iPhones (as originators of Personal Hotspot), but on iPads where people might use Bluetooth to connect to their iPhone it could be prevalent.

Precisely. I spent the morning looking into how we can ask iOS for the right interface to use instead of making a decision on our end, because I'm concerned about edge cases like that. I'll likely scrap this entire PR and see if we can observe NWPathMonitor updates from our Swift code instead, which is the Apple-sanctioned way to do this.

We couldn't use this before because we had to support older versions of macOS and iOS where that wasn't available. But it is no longer an issue now.

@bobbyl140
Copy link

That would make sense... Thank you again for looking into this!

@skaeight
Copy link

I’m seeing this issue. Any eta when this change will go live?

net/interfaces/defaultroute_ios.go Outdated Show resolved Hide resolved
net/interfaces/defaultroute_ios.go Outdated Show resolved Hide resolved
@agottardo agottardo force-pushed the 8022-iphone-internet-break-after-leave-wi-fi-range branch from 2d1f1cd to 7d9565a Compare January 3, 2024 18:17
@agottardo
Copy link
Contributor Author

I refactored the logic here. We now have a UpdateLastKnownDefaultRouteInterface(string) function that the iOS app can call when it notices a network transition via NWPathMonitor. Once Swift has given us an interface name, we always use that, and that should handle all edge cases including USB Ethernet adapters and other similar setups.

The logic here that hard-codes en0 and pdp_ip0 will only be used as a fall back when Swift hasn't delivered an update yet, or has delivered an interface name that is invalid because the interface is down.

@agottardo agottardo force-pushed the 8022-iphone-internet-break-after-leave-wi-fi-range branch from 7d9565a to 8c064a4 Compare January 3, 2024 19:14
@agottardo agottardo marked this pull request as ready for review January 3, 2024 19:16
Copy link
Contributor

@knyar knyar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, just a couple style nits.

net/interfaces/defaultroute_ios.go Outdated Show resolved Hide resolved
net/interfaces/defaultroute_ios.go Outdated Show resolved Hide resolved
@agottardo agottardo force-pushed the 8022-iphone-internet-break-after-leave-wi-fi-range branch from 8c064a4 to 92c2683 Compare January 4, 2024 17:24
Updates #8022
Updates #6075

On iOS, we currently rely on delegated interface information to figure out the default route interface.  The NetworkExtension framework in iOS seems to set the delegate interface only once, upon the *creation* of the VPN tunnel. If a network transition (e.g. from Wi-Fi to Cellular) happens while the tunnel is connected, it will be ignored and we will still try to set Wi-Fi as the default route because the delegated interface is not getting updated as connectivity transitions.

Here we work around this on the Swift side with a NWPathMonitor instance that observes the interface name of the first currently satisfied network path. Our Swift code will call into `UpdateLastKnownDefaultRouteInterface`, so we can rely on that when it is set.

If for any reason the Swift machinery didn't work and we don't get any updates, here we also have some fallback logic: we try finding a hardcoded Wi-Fi interface called en0. If en0 is down, we fall back to cellular (pdp_ip0) as a last resort. This doesn't handle all edge cases like USB-Ethernet adapters or multiple Ethernet interfaces, but it is good enough to ensure connectivity isn't broken.

I tested this on iPhones and iPads running iOS 17.1 and it appears to work. Switching between different cellular plans on a dual SIM configuration also works (the interface name remains pdp_ip0).

Signed-off-by: Andrea Gottardo <andrea@tailscale.com>
@agottardo agottardo force-pushed the 8022-iphone-internet-break-after-leave-wi-fi-range branch from 92c2683 to aeef46e Compare January 4, 2024 17:32
@agottardo agottardo merged commit d9aeb30 into main Jan 4, 2024
46 checks passed
@agottardo agottardo deleted the 8022-iphone-internet-break-after-leave-wi-fi-range branch January 4, 2024 17:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

iPhone - internet break after leave wi-fi range
6 participants