NetworkExtension stop method not called when stopping programmatically

Our SystemExtensions implements a NetworkExtensions by inheriting from NEPacketTunnelProvider. We override the two methods startWithOptions:completionHandler: and stopTunnelWithReason:completionHandler:.

When starting the connection from the network preferences using the Connect button and then stopping it again using the Disconnect button, the start/stop methods are called as expected.

But when we do the same from our main application using startVPNTunnel() and stopVPNTunnel(), the start method is also called as expected but the stop method is never called, although the connection is actually stopped (it shows disconnected in the network preferences after that call).

What could be the reason for the stop method to never be called? We need to perform clean up in that method and that isn't performed, the connection cannot be started again afterwards, so this is quite a showstopper at the moment.
Intersting. When I test these methods I usually put a subsystem log in each one of the method bodies just to make sure I can see what is happening from the Terminal. I usually test detached from the debugger and this can help out a lot. You could do something like this if you are not already:

Code Block swift
static let log = OSLog(subsystem: "com.example.packetTunnel.PacketTunnelTest", category: "provider")
...
override func stopTunnel(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
os_log(.debug, log: self.log, "provider will stop, reason: %zd", reason.rawValue)
self.queue.async {
completionHandler()
os_log(.debug, log: self.log, "provider did stop")
}
}


Code Block bash
$ log stream --level debug --predicate 'subsystem == "com.example.packetTunnel.PacketTunnelTest"'


Also, in your Console.app, do you see any additional information or logs if you search just on PacketTunnelTest (or the namne of your Packet Tunnel Extension)?

Matt Eaton
DTS Engineering, CoreOS
meaton3@apple.com
Thankyou for the question!
Please file a problem report with steps to reproduce the issue. Please include the working case as well as the failed case, and attach sysdiagnose output to the radar. We will analyze the issue as soon as we get the info from you.



Okay, we figured it out today. The problem was that while the main application was calling stopVPNTunnel(), another event from a helper process that took place at the same time (a bit of a race condition) caused the system extension itself to call [self cancelTunnelWithError:nil] right before it would process the stop call. Despite not reporting an error, this also stops the system extension and that way stopTunnelWithReason:completionHandler: was never called, as cancel will not trigger it and apparently once canceled, an external stop will just do nothing as the extension is already stopping.

The comments for the cancel method say "Subclasses should not override this method", but would that really be an issue? It just seems pratical to override it, so we can run the same clean up code we would otherwise run if the stop method is called and we will make sure, that in any case the super implementation is called as well. Also we will only call this method to report critical errors in the future, as for stopping gracefully, we think it is better practice to always wait for an external request either by our app or by the system preferences, even if the system extension knows that such a stop is going to occur very soon. It simply leads to clearer responsibilities that way.

so we can run the same clean up code we would otherwise run if the stop method is called

Why not just put that code in a separate method and then call it from your -stopTunnelWithReason:completionHandler: method and whatever method is calling -cancelTunnelWithError:?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@apple.com"
NetworkExtension stop method not called when stopping programmatically
 
 
Q