NETransparentProxyProvider frequent tunnel churn during Dark Wake cycles on macOS.

Description

  • Our NETransparentProxyProvider system extension maintains a persistent TLS/DTLS control channel to a security gateway. To maintain this stateful connection the extension sends application-level "Keep Alive" packets every few seconds (example : 20 seconds).

  • The Issue: When the macOS device enters a sleep state, the Network Extension process is suspended, causing our application-level heartbeat to cease. Consequently, our backend gateway—detecting no activity—terminates the session via Dead Peer Detection (DPD).

  • The problem is exacerbated by macOS Dark Wake cycles. We observe the extension's wake() callback being triggered periodically (approx. every 15 minutes) while the device remains in a sleep state (lid closed). During these brief windows:

  • The extension attempts to use the existing socket, finds it terminated by the backend, and initiates a full re-handshake.

  • Shortly after the connection is re-established, the OS triggers the sleep() callback and suspends the process again.

  • This creates a "connection churn" cycle that generates excessive telemetry noise and misleading "Session Disconnected" alerts for our enterprise customers.

Steps to Reproduce Activate Proxy:

  • Start the NETransparentProxyProvider and establish a TLS session to a gateway.

  • Apply Settings: Configure NETransparentProxyNetworkSettings to intercept outbound TCP/UDP traffic.

  • Initialize Heartbeat: Start a 20-second timer (DispatchSourceTimer) to log and send keep-alive packets.

  • Induce Sleep: Put the Mac to sleep (Apple Menu > Sleep).

  • Observe Logs: Monitor the system via sysdiagnose or the macOS Console.

  • Observation: Logs stop entirely during sleep, indicating process suspension.

  • Observation: wake() and sleep() callbacks are triggered repeatedly during Dark Wake intervals, causing a cycle of re-connections.

Expected Behavior We seek to minimize connection turnover during maintenance wakes and maintain session stability while the device is technically in a sleep state.

Questions for Apple

  • Is it possible to suppress the sleep and wake callback methods of NETransparentProxyProvider when the device is performing a maintenance/Dark Wake, only triggering them for a full user-initiated wake?

  • Is it possible to prevent the NETransparentProxyProvider process from being suspended during sleep, or at least grant it a high-priority background execution slot to maintain the heartbeat?

  • If suspension is mandatory, is there a recommended way to utilize TCP_KEEPALIVE socket options that the kernel can handle on behalf of the suspended extension?

  • How can the extension programmatically identify if a wake() call is a "Dark Wake" versus a "Full User Wake" to avoid unnecessary re-connection logic?

the Network Extension process is suspended

This comment suggests that you have misunderstood how sleep works on our platforms. When the system sleeps, the main CPU [1] stops running, meaning that all code on the device stops executing [2].

Now, on iOS and its child platforms we will suspend an app’s process when the app moves to the background. That mechanism is completely different from system sleep, and it doesn’t exist on macOS.

Is it possible to suppress the sleep and wake callback methods of NETransparentProxyProvider when the device is performing a maintenance/Dark Wake … ?

Not directly.

Is it possible to prevent the NETransparentProxyProvider process from being suspended during sleep … ?

That question doesn’t make sense given the design that I’ve outlined above.

If suspension is mandatory, is there a recommended way to utilize TCP_KEEPALIVE socket options that the kernel can handle on behalf of the suspended extension?

Likewise.

Again, during sleep the whole CPU is stopped, including the kernel.

How can the extension programmatically identify if a wake() call is a "Dark Wake" … ?

I don’t think so, but I’m going to touch base with a colleague to confirm that. One of us will update this thread once we’re done.

ps You already filed this as a bug report (FB21729064). I’ve recommended that we send that back to you without making any changes. Once we’ve driven this forums thread to a conclusion, if there’s still something you’d like to see change in the system, you can file a new, more specific enhancement request.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] On iPhone we call this the application processor, or AP.

[2] Except code being run by various coprocessors.

NETransparentProxyProvider frequent tunnel churn during Dark Wake cycles on macOS.
 
 
Q