Open javierdemartin opened 5 years ago
Facing exactly same issue while using VPN ON Demand. Did you find any solution ?
Also in my case- when I switched mobile network from Wifi to LTE or LTE to Wifi, device looses internet due to connection from client to server. And when switching network VPN gets stuck & in iphone settings I can see it stays in loop of connecting , disconnecting & so on., due to this device loose out internet connection.
Don't know what to do ? Can we generate log file similar to one in OpenVPN app ?
I am monitoring my interface changes using defaultPath
in the provider. There depending on the interface changes I can reconnect/cancel the tunnel. The problem is that in some cases it enters a loop draining the battery. And I don't know in which cases do I have to resume/cancel/reconnect my tunnel.
I don't think it's the lack of documentation on how to do this with this library. I don't see anything in the Apple's documentation that details how to handle these interface changes.
@javierdemartin Thanks for reply, If it is possible for you, can you share the code snippet for monitoring interface changes? Actually i don't know how to use defaultPath in the provider. And I m using .ovpn file to connect with vpn.
You need to add your this observer in the NEPacketTunnelProvider
(the network extension target)
// Determine the change in physical network interfaces observing the property using KVO
let options = NSKeyValueObservingOptions([.new, .old])
self.addObserver(self, forKeyPath: "defaultPath", options: options, context: nil)
And observe the changes in the interfaces,
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
guard let keyPath = keyPath else {
return
}
// defaultPath is the current default path used for connections created by the provider (tunnel-extension)
if keyPath == "defaultPath" {
// NWPath objects contain information about which physical network interface will be used by connections
// opened by the Network Extension provider.
let oldPath = change?[.oldKey] as! NWPath // Old interface
let newPath = change?[.newKey] as! NWPath // New interface
if !reasserting {
// For example, WiFi (unsatisfied) --> Cellular (satisfied) disconnects from WiFi and switches to Cellular, cancel the tunnel
if (!oldPath.isExpensive && oldPath.status == .unsatisfied && newPath.isExpensive && newPath.status == .satisfied) {
self.cancelTunnelWithError(nil)
}
}
}
Here is a reference from StackOverflow talking about it.
@javierdemartin Hi,
Can you help me ? I want to print logs same like OpenVPN client app. They shows all network tests and vpn details(handshakes, sleep/wake, uptime) in logs. Can we do the same with this adapter? If yes HOW and in which class (tunnelProvider or can in viewController).
I tried to use this method but it doesn't transfer live continuous value to print on run time in viewController class. I was using userDefaults share data between app and tunnelExtension like:- In PacketTunnel - let kAppGroupName = "group.com.abc.efgh" var sharedContainer : UserDefaults? self.sharedContainer = UserDefaults(suiteName: kAppGroupName) func openVPNAdapter(_ openVPNAdapter: OpenVPNAdapter, handleLogMessage logMessage: String) { // Handle log messages if let sharedContainer = self.sharedContainer { sharedContainer.set(logMessage as String, forKey: "networkLogsMessages") sharedContainer.synchronize() } }
In viewController where networkmonitor - let kAppGroupName = "group.com.abc.efgh" var sharedContainer : UserDefaults? self.sharedContainer = UserDefaults(suiteName: kAppGroupName) func networkStatusMonitor(){ NetStatus.shared.netStatusChangeHandler = { DispatchQueue.main.async { [unowned self] in if let sharedContainer = self.sharedContainer { let logMessages = sharedContainer.string(forKey: "networkLogsMessages") print(logMessages) } } } } But it only shows once and old saved logs because when any logMessage occured it saves in userDefaults(in extension) and when in viewController on button click I retrieve these message(In viewDidLoad) from userdefaults it show once only. Within this time period there are new logs occured and it only shows the previous fetched.
I am solving this at the moment by cancelling the tunnel if that log message is received from the provider. It's a workaround as I am checking if the String
contains the KEEPALIVE_TIMEOUT
message. The implementation is as follows,
func openVPNAdapter(_ openVPNAdapter: OpenVPNAdapter, handleLogMessage logMessage: String) {
if logMessage.contains("KEEPALIVE_TIMEOUT") {
cancelTunnelWithError(CancelTunnelErrors.keepAliveTimeout)
}
}
I've seen that there is an error of the type OpenVPNAdapterErrorKeepaliveTimeout
, how could I be able to throw an error that goes to openVPNAdapter(_, handleError)
so I can manage the error there or subscribe to a notification of that event happening. Pinging @ss-abramchuk
Hi @javierdemartin,
openvpn3 library responsible for throwing errors. I think it doesn't treat KEEPALIVE_TIMEOUT as error because you have ping-restart
in your config. Probably using of ping-exit
might help but I'm not sure, I couldn't find any reference of this option in openvpn3 source code.
As for switching interface, normally, these lines of code should force resetting of the tunnel and initialize reconnection using active interface:
vpnReachability.startTracking { [weak self] status in
guard status != .notReachable else { return }
self?.vpnAdapter.reconnect(interval: 5)
}
I'm going to update dependencies this weekend and will take a look at this issue as well. It would be very handy if you share your OpenVPN config with me so that I could find root of the problem.
I've solved my issue by cancelling the tunnel when a KEEPALIVE_TIMEOUT
message is received and also when the vpnReachability
status is .notReachable
. By doing so I guarantee my traffic is always going out of my tunnel interface.
My .ovpn
configuration is as follow
client
remote VPN_SERVER_ADDRESS 1194 udp
nobind
dev tun
persist-tun
persist-key
verify-x509-name VPN_SERVER_ADDRESS name
remote-cert-tls server
cipher AES-128-CBC
<ca>
-----BEGIN CERTIFICATE-----
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
-----END CERTIFICATE-----
</ca>
<cert>
-----BEGIN CERTIFICATE-----
-----END CERTIFICATE-----
</cert>
<key>
-----BEGIN PRIVATE KEY-----
-----END PRIVATE KEY-----
</key>
I am tunnelling my traffic through a server. In some cases it has to block determinate websites, e.g. gambling sites. The tunnel works without a problem if I start the tunnel with WiFi or Cellular. When I change the network to the opposite the VPN reconnects automatically as I am using on demand VPN but the VPN doesn't block the websites it did before. My main supposition is that after changing the interface, WiFi to Cellular or vice versa, the outgoing traffic from the device it is not routed through the tunnel interface (
utun
) but the VPN is still up.How could I check or "force" the outgoing traffic through my tunnel interface?
I can detect when the interface changes and I could force restart the tunnel but I shouldn't do it. That should be the tunnel's task and I prefer not to force the tunnel's state.
This also happens after a while, between 2 to 5 minutes, the VPN restarts and in the network extension's provider
openVPNAdapter(_ openVPNAdapter: OpenVPNAdapter, handleLogMessage logMessage: String)
method I get the following messagesI don't know if this has to be related with my Swift code or with the OpenVPN server setup.