We're moving our VPN client's firewall off pf (TN3165) and, following the advice in https://developer.apple.com/forums/thread/781996, relying on enforceRoutes plus a content filter. That works well for keeping traffic in the tunnel, but it has a side effect we can't work around.
We add excluded routes at runtime (hostname-based split tunnelling), so the provider calls setTunnelNetworkSettings while connected. With enforceRoutes = true, any connected socket to a tunnelled destination (TCP, or UDP after connect()) that sends during the update gets EPIPE and stays broken until the app reopens it. It happens with identical settings too, and the EPIPE raises SIGPIPE, so apps that don't ignore it are terminated.
From a sysdiagnose, every update logs "Resetting the virtual interface", the utun's address is removed and re-added about 5 ms later, and a socket that sends in that gap is marked defunct by necp_socket_find_policy_match ("Marking socket in state 258 as defunct"). With enforceRoutes = false nothing dies, but packets for tunnelled destinations leave on the physical interface during the update. It reproduces on macOS 15.7, 26.5, 27.0 and 27.2 beta 3 (26B5101f).
I filed FB25176278 with a minimal sample (a packet tunnel provider that echoes UDP, so no server is needed) and a sysdiagnose from 27.2 beta 3. Two questions:
- Is there a supported way to change
excludedRouteson a running tunnel without the interface reset? - If not, is there anything a provider can do to avoid this today, short of not updating routes while connected?
Thanks!
Thanks for the confirmation.
I can understand how NE gets itself into this state, but the final behaviour is less than helpful so I think a bug report is called for. So, thanks for filing FB25176278.
Having said that, I’m not sure if there’s an easy way to fix this. When you set enforceRoutes the system actually catches the flow at the socket level, rather than down at the IP level. If the tunnel gets reconfigured then there’s gonna be a window where things are in an inconsistent state and hence ‘poking’ the flow at that time will result in the disconnect. Hmmmm, I guess we could do some sort of atomic NECP update… but… yeah, that’s just idle speculation on my part. The NE team needs to look at this properly, and that’s what your bug report is for.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"