I believe the lifecycle of your NEPacketTunnelProvider instance is completely in the control of the system. Same for the process in which your extension is running. Once you call cancelTunnelWithError: inside your extension and your stopTunnelWithReason:completionHandler: method is called inside your extension, the NEPacketTunnelProvider instance may be deinit'd at any time and the extension process may be terminated/killed at any time. In your containing app, assuming it's running (which you cannot guarantee AFAIK), you could set a timer when the tunnel stops and retry again when the timer expires.
The docs are not exactly verbose, but I think the proper way to handle temporary and short networking issues that cause your tunnel to go down is to use the reasserting property inside your extension. The reasserting property is inherited from NETunnelProvider. So if your tunnel is disconnected because of a networking problem (e.g. the tunnel server severs the connection for any reason), you can set reasserting to true and try to reconnect. You should set a timer in your extension so that you do not try to reassert forever.
If the networking problem that caused your tunnel to disconnect is because the tunnel is unreachable (e.g. look at the currentPath property of NWUDPSession, the connectedPath of NWTCPConnection, or the defaultPath of NEProvider), I think the correct way to proceed is to cancel/stop the tunnel and not to try to reassert any longer.