setTunnelNetworkSettings with enforceRoutes defuncts connected sockets, even with unchanged settings

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:

  1. Is there a supported way to change excludedRoutes on a running tunnel without the interface reset?
  2. If not, is there anything a provider can do to avoid this today, short of not updating routes while connected?

Thanks!

setTunnelNetworkSettings with enforceRoutes defuncts connected sockets, even with unchanged settings
 
 
Q