iOS Network Extension life cycle after stop

We're working on Packet Tunnel Provider on iOS and I was wondering about the process life cycle, in particular when a connection is stopped.

There's some house-keeping I'd like to do when the connection has stopped, which may take up to a few seconds. It seems on iOS, the Network Extension process is terminated shortly after the completion handler of - stopTunnelWithReason:completionHandler: has been called.

Since the house-keeping is not strictly a part of stopping the connection and I would like to allow the user to start a new connection as soon as possible: is there a way to tell the Network Extension to keep the process alive (for a while)? Or do I have to do it before calling the completion handler of - stopTunnelWithReason:completionHandler:?

Related question: when there's an error and we need to stop the connection "from within" the NE, we're supposed to call - cancelTunnelWithError:. What's the lifecycle here? Do we need to tear everything down, do the house-keeping, and only then call - cancelTunnelWithError:? Or can we call - cancelTunnelWithError: and then continue to clean everything up?

Accepted Reply

I would like to allow the user to start a new connection as soon as possible: is there a way to tell the Network Extension to keep the process alive (for a while)? Or do I have to do it before calling the completion handler of - stopTunnelWithReason:completionHandler:?

Since the user is starting the connection, what is wrong with having the user initiate the connection from either the container app or the VPN Settings in the Settings app?

As for keeping the process alive, no, stopTunnelWithReason is call as part of the process to destroy the packet tunnel provider so any attempts to keep the process alive could end up with strange behavior.

Regarding:

What's the lifecycle here? Do we need to tear everything down, do the house-keeping, and only then call - cancelTunnelWithError:? Or can we call - cancelTunnelWithError: and then continue to clean everything up?

Tear everything down, do house-keeping, and then call cancelTunnelWithError.

Matt Eaton
DTS Engineering, CoreOS
meaton3@apple.com
  • Hi Matt, thank you for your speedy reply!

    Since the user is starting the connection, what is wrong with having the user initiate the connection from either the container app or the VPN Settings in the Settings app?

    We support both, but that's not what this is about. :-) My concern is: if the user stops the connection and the house-keeping is taking very long (say, it takes a minute), it would be good if the user would still be able to start a new connection.

    As a test I just delayed calling the callback of stopTunnelWithReason: using dispatch_after by 30 seconds. During this time, the connection is in "stopping" state and the user has to wait this whole time until they can start the connection again.

    As for keeping the process alive, no, stopTunnelWithReason is call as part of the process to destroy the packet tunnel provider so any attempts to keep the process alive could end up with strange behavior.

    I was hoping that I could do the cleanup in something like the background tasks for UI processes: https://developer.apple.com/documentation/uikit/app_and_environment/scenes/preparing_your_ui_to_run_in_the_background/extending_your_app_s_background_execution_time

    If something like this would be supported in a network extension, I could bring the connection down, then call the stopTunnelWithReason: callback, and only then do the house-keeping. This ways the user (or system, with on-demand rules) would be able to restart the connection more quickly.

    Tear everything down, do house-keeping, and then call cancelTunnelWithError.

    Thank you for clearing this up.

Add a Comment

Replies

I would like to allow the user to start a new connection as soon as possible: is there a way to tell the Network Extension to keep the process alive (for a while)? Or do I have to do it before calling the completion handler of - stopTunnelWithReason:completionHandler:?

Since the user is starting the connection, what is wrong with having the user initiate the connection from either the container app or the VPN Settings in the Settings app?

As for keeping the process alive, no, stopTunnelWithReason is call as part of the process to destroy the packet tunnel provider so any attempts to keep the process alive could end up with strange behavior.

Regarding:

What's the lifecycle here? Do we need to tear everything down, do the house-keeping, and only then call - cancelTunnelWithError:? Or can we call - cancelTunnelWithError: and then continue to clean everything up?

Tear everything down, do house-keeping, and then call cancelTunnelWithError.

Matt Eaton
DTS Engineering, CoreOS
meaton3@apple.com
  • Hi Matt, thank you for your speedy reply!

    Since the user is starting the connection, what is wrong with having the user initiate the connection from either the container app or the VPN Settings in the Settings app?

    We support both, but that's not what this is about. :-) My concern is: if the user stops the connection and the house-keeping is taking very long (say, it takes a minute), it would be good if the user would still be able to start a new connection.

    As a test I just delayed calling the callback of stopTunnelWithReason: using dispatch_after by 30 seconds. During this time, the connection is in "stopping" state and the user has to wait this whole time until they can start the connection again.

    As for keeping the process alive, no, stopTunnelWithReason is call as part of the process to destroy the packet tunnel provider so any attempts to keep the process alive could end up with strange behavior.

    I was hoping that I could do the cleanup in something like the background tasks for UI processes: https://developer.apple.com/documentation/uikit/app_and_environment/scenes/preparing_your_ui_to_run_in_the_background/extending_your_app_s_background_execution_time

    If something like this would be supported in a network extension, I could bring the connection down, then call the stopTunnelWithReason: callback, and only then do the house-keeping. This ways the user (or system, with on-demand rules) would be able to restart the connection more quickly.

    Tear everything down, do house-keeping, and then call cancelTunnelWithError.

    Thank you for clearing this up.

Add a Comment

Hi Matt, thank you for your speedy reply!

Since the user is starting the connection, what is wrong with having the user initiate the connection from either the container app or the VPN Settings in the Settings app?

We support both, but that's not what this is about. :-) My concern is: if the user stops the connection and the house-keeping is taking very long (say, it takes a minute), it would be good if the user would still be able to start a new connection.

As a test I just delayed calling the callback of stopTunnelWithReason: using dispatch_after by 30 seconds. During this time, the connection is in "stopping" state and the user has to wait this whole time until they can start the connection again.

As for keeping the process alive, no, stopTunnelWithReason is call as part of the process to destroy the packet tunnel provider so any attempts to keep the process alive could end up with strange behavior.

I was hoping that I could do the cleanup in something like the background tasks for UI processes: https://developer.apple.com/documentation/uikit/app_and_environment/scenes/preparing_your_ui_to_run_in_the_background/extending_your_app_s_background_execution_time

If something like this would be supported in a network extension, I could bring the connection down, then call the stopTunnelWithReason: callback, and only then do the house-keeping. This ways the user (or system, with on-demand rules) would be able to restart the connection more quickly.

Tear everything down, do house-keeping, and then call cancelTunnelWithError.

Thank you for clearing this up.

Are you able to delay calling the completion handler in stopTunnelWithReason until all of you cleanup is done? For example, call out to a function that sequentially performs your cleanup and when that is all done, notify a completion handle to send back to stopTunnelWithReason.

Matt Eaton
DTS Engineering, CoreOS
meaton3@apple.com

Yes, I'm able to do that, it works. I'm really grateful you're taking the time to answer my questions and I don't mean to be rude, but I think you're missing my point.

Right now, the cleanup must be done before calling the callback, and that's blocking the VPN connection (from a user's point of view) on stop. The user or OS (on-demand rule) is not able to start the connection until the cleanup is done, but actually we would be able to restart it already.

Since this cleanup can take some time, it would be good if we could signal to the OS "the connection is done and could get restarted" (what calling the callback is doing), and then do some additional work without the OS killing the process.

So what I would like to do (which is not possible right now, it seems):

  1. -stopTunnelWithReason: is called.
  2. We stop the network part of the connection ("logout").
  3. Call the stopTunnelWithReason: callback or otherwise signal to iOS: the connection has stopped and is now ready to be restarted.
  4. Then do some additional cleanup.

I understand that right now, I need to do step 4 before 3. But it would be a better experience for the user if the delay introduced by then cleanup were not considered part of the stop procedure.

I guess I'm going to write a Feedback describing the whole scenario in the next days, maybe it's something you guys would consider supporting.

I think you're missing my point. I understand that right now, I need to do step 4 before 3.

You are absolutely correct. I was under the impression that you were trying to step 4 before step 3. Right, so after step 3 there is no guarantee about how long the process will stay around.

Matt Eaton
DTS Engineering, CoreOS
meaton3@apple.com