Packet Tunnel Provider - sleep

I've implemented a VPN app with Packet Tunnel Provider for MacOS and iOS.

I have two questions regarding the Extension's sleep/wake functions:


1. If the VPN configuration is set with disconnectOnSleep = false, and at the extension I'm sending keep-alives every X seconds,

What would happen when the device enters sleep mode? Will it keep sending keep-alive (because the VPN is configured with disconnectOnSleep=false) ?


2. If the VPN configuration is set with disconnectOnSleep = true, and also isOnDemandEnabled = true.

When the device enters sleep mode, do I need to disconnect the VPN myself? Or the OS would take care of it?

And if I should disconnect it myself, the on-demand won't try to turn it on again (because the on-demand) ?


Accepted Reply

I recently investigated the interaction between VPN and sleep as part of a DTS incident. I’ve included the resulting info below. I think that’ll answer a bunch of your questions. If there’s any remaining, please post back with the specifics.

Your tunnel provider can work in one of two modes:

  • If

    disconnectOnSleep
    is set (
    <NetworkExtension/NEVPNProtocol.h>
    ), the system will automatically stop the tunnel as the device goes to sleep. In that case the tunnel won’t automatically start again on wake (unless triggered by an on demand rule).
  • If

    disconnectOnSleep
    is false, the system does not automatically stop the tunnel on sleep. In that case:
    • Your provider should override the

      -sleepWithCompletionHandler:
      and
      -wake
      methods (
      <NetworkExtension/NEProvider.h>
      ).
    • In the ‘sleep’ override it should quiesce the tunnel as appropriate for your protocol. In the ‘wake’ override it should reactivate the tunnel.

    • If the reactivation fails (for example, because the network environment changed in a way that prevents reactivation) your provider should stop the tunnel by calling one of the ‘cancel’ methods (for example,

      -cancelTunnelWithError
      for a packet tunnel provider).
    • Between the tunnel quiesce and reactivate your provider should set

      reasserting
      to true (
      <NetworkExtension/NETunnelProvider.h>
      ) so that the system knows it’s not connected.
    • While reactivating you can choose to clear out your tunnel settings by calling

      -setTunnelNetworkSettings:completionHandler:
      with nil for the
      tunnelNetworkSettings
      parameter (
      <NetworkExtension/NETunnelProvider.h>
      ). Doing this will stop traffic flowing into the tunnel, which might be a nice thing to do for the user if the reactivation takes a long time.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"
  • Hi Quinn - I am trying to implement 'disconnectOnSleep' = false so that I can control the quiesce / resume of the tunnel. But sleep and wake are not being called. I have removed the VPN profile and restarted, which should have taken the new setting: // In the container code

    proto.serverAddress = "104.196.39.114" proto.disconnectOnSleep = false

    // In the tunnel code

    public override func wake() { log.info("Waking Up....") sleeping = false } public override func sleep(completionHandler: @escaping () -> Void) { log.info("Preparing to sleep...") sleeping = true completionHandler() }
  • Ah, just found your post about disconnecting/restarting and not having xcode connected, now I am getting them...

  • Hi Quinn:

    Let's say that when wake() is called, I am temporarily unable to resume the tunnel, for whatever reason. If I call cancelTunnel(), can I then later try to call startTunnel from within my provider? Or is cancelTunnel() the end of the line and the user has to restart from the container app?What is the proper way (if there is one) to quiesce the tunnel in such a way that the tunnel is no longer capturing traffic in either direction, in other words the device can route traffic normally outside the tunnel until I re-setup my links, set the downstream read handlers, and start the upstream traffic pump?
Add a Comment

Replies

I recently investigated the interaction between VPN and sleep as part of a DTS incident. I’ve included the resulting info below. I think that’ll answer a bunch of your questions. If there’s any remaining, please post back with the specifics.

Your tunnel provider can work in one of two modes:

  • If

    disconnectOnSleep
    is set (
    <NetworkExtension/NEVPNProtocol.h>
    ), the system will automatically stop the tunnel as the device goes to sleep. In that case the tunnel won’t automatically start again on wake (unless triggered by an on demand rule).
  • If

    disconnectOnSleep
    is false, the system does not automatically stop the tunnel on sleep. In that case:
    • Your provider should override the

      -sleepWithCompletionHandler:
      and
      -wake
      methods (
      <NetworkExtension/NEProvider.h>
      ).
    • In the ‘sleep’ override it should quiesce the tunnel as appropriate for your protocol. In the ‘wake’ override it should reactivate the tunnel.

    • If the reactivation fails (for example, because the network environment changed in a way that prevents reactivation) your provider should stop the tunnel by calling one of the ‘cancel’ methods (for example,

      -cancelTunnelWithError
      for a packet tunnel provider).
    • Between the tunnel quiesce and reactivate your provider should set

      reasserting
      to true (
      <NetworkExtension/NETunnelProvider.h>
      ) so that the system knows it’s not connected.
    • While reactivating you can choose to clear out your tunnel settings by calling

      -setTunnelNetworkSettings:completionHandler:
      with nil for the
      tunnelNetworkSettings
      parameter (
      <NetworkExtension/NETunnelProvider.h>
      ). Doing this will stop traffic flowing into the tunnel, which might be a nice thing to do for the user if the reactivation takes a long time.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"
  • Hi Quinn - I am trying to implement 'disconnectOnSleep' = false so that I can control the quiesce / resume of the tunnel. But sleep and wake are not being called. I have removed the VPN profile and restarted, which should have taken the new setting: // In the container code

    proto.serverAddress = "104.196.39.114" proto.disconnectOnSleep = false

    // In the tunnel code

    public override func wake() { log.info("Waking Up....") sleeping = false } public override func sleep(completionHandler: @escaping () -> Void) { log.info("Preparing to sleep...") sleeping = true completionHandler() }
  • Ah, just found your post about disconnecting/restarting and not having xcode connected, now I am getting them...

  • Hi Quinn:

    Let's say that when wake() is called, I am temporarily unable to resume the tunnel, for whatever reason. If I call cancelTunnel(), can I then later try to call startTunnel from within my provider? Or is cancelTunnel() the end of the line and the user has to restart from the container app?What is the proper way (if there is one) to quiesce the tunnel in such a way that the tunnel is no longer capturing traffic in either direction, in other words the device can route traffic normally outside the tunnel until I re-setup my links, set the downstream read handlers, and start the upstream traffic pump?
Add a Comment

Thanks, no more questions 🙂

4 years later..

disconnectOnSleep is set:

disconnectOnSleep = true

And I have the following on-demand rule (to always connect when there's traffic):

tunnelProviderManager.isOnDemandEnabled = true
let onDemandRuleConnect = NEOnDemandRuleConnect()
tunnelProviderManager.onDemandRules = [onDemandRuleConnect]

This causes a problem on sleep -

  • When the Mac is about to enter sleep
  • stopTunnelWithReason is called (by OS) and VPN stops
  • some other app has outgoing traffic
  • the on-demand rule is matched, and the OS calls to startTunnel() //BUG here
  • Mac enter sleeps, but the VPN is still 'enabled'

There's no reason for the OS to restart the VPN if it's going to enter sleep mode. It should ignore on-demand rules in such cases.

Any way to workaround this problem (and keep the 'disconnectOnSleep = true')?

  • I have the same issue… Any update on this?

Add a Comment