'I am working on a VPN app which used Open VPN and I am not sure how to call the tunnel

I don't understand how does packet tunnel work and also don't understand where use packet tunnel in view controller. My .ovpn file is working fine but not connecting only show the details of my server address.

Can any one explain whole process how does implement OpenVPN and vpn on demand.

import NetworkExtension
import UIKit

class ViewController: UIViewController {

var providerManager: NETunnelProviderManager!

override func viewDidLoad() {
    super.viewDidLoad()
    self.loadProviderManager {
        self.configureVPN(serverAddress: "s_id", username: "uid", password: "password")
    }
 }

func loadProviderManager(completion:@escaping () -> Void) {
   NETunnelProviderManager.loadAllFromPreferences { (managers, error) in
       if error == nil {
           self.providerManager = managers?.first ?? NETunnelProviderManager()
           completion()
       }
   }
}

func configureVPN(serverAddress: String, username: String, password: String) {

    guard
        let configurationFileURL = Bundle.main.url(forResource: "ios", withExtension: ".ovpn"),
            let configData = try? Data(contentsOf: configurationFileURL)


        else {

            fatalError()
        }

    
  self.providerManager?.loadFromPreferences { error in
     if error == nil {
        let tunnelProtocol = NETunnelProviderProtocol()
        tunnelProtocol.username = username
        tunnelProtocol.serverAddress = serverAddress
        tunnelProtocol.providerBundleIdentifier = "com.pushNotifications.tunnel" // bundle id of the network extension target
        tunnelProtocol.providerConfiguration = ["ovpn": configData, "username": username, "password": password]
        tunnelProtocol.disconnectOnSleep = false
        self.providerManager.protocolConfiguration = tunnelProtocol
        self.providerManager.localizedDescription = "OpenVPN" // the title of the VPN profile which will appear on Settings
        self.providerManager.isEnabled = true
        self.providerManager.saveToPreferences(completionHandler: { (error) in
              if error == nil  {
                 self.providerManager.loadFromPreferences(completionHandler: { (error) in
                     do {
                       try self.providerManager.connection.startVPNTunnel()
                         print("Starting")// starts the VPN tunnel.
                     } catch let error {
                         print(error.localizedDescription)
                     }
                 })
              }
        })
      }
   }
}

This is my packet tunnel code.

import NetworkExtension
import OpenVPNAdapter
import UIKit

class PacketTunnelProvider: NEPacketTunnelProvider {

    var startHandler: ((Error?) -> Void)?
    var stopHandler: (() -> Void)?
    var vpnReachability = OpenVPNReachability()

    var configuration: OpenVPNConfiguration!
    var properties: OpenVPNConfigurationEvaluation!
    var UDPSession: NWUDPSession!
    var TCPConnection: NWTCPConnection!

    lazy var vpnAdapter: OpenVPNAdapter = {
        let adapter = OpenVPNAdapter()
        adapter.delegate = self
        return adapter
    }()
    
    override func startTunnel(options: [String : NSObject]?, completionHandler: @escaping (Error?) -> Void) {
        
        guard
            let protocolConfiguration = protocolConfiguration as? NETunnelProviderProtocol,
            let providerConfiguration = protocolConfiguration.providerConfiguration
            else {
                fatalError()
        }
        guard let ovpnFileContent: Data = providerConfiguration["ovpn"] as? Data else { return }
            let configuration = OpenVPNConfiguration()
            configuration.fileContent = ovpnFileContent
            do {
                properties = try vpnAdapter.apply(configuration: configuration)
            } catch {
                completionHandler(error)
                return
            }
        configuration.tunPersist = true

        if !properties.autologin {
            if let username: String = providerConfiguration["username"] as? String, let password: String = providerConfiguration["password"] as? String {
                let credentials = OpenVPNCredentials()
                credentials.username = username
                credentials.password = password
                do {
                    try vpnAdapter.provide(credentials: credentials)
                    print("vpnAdapter")
                } catch {
                    completionHandler(error)
                    return
                }
            }
        }

        vpnReachability.startTracking { [weak self] status in
            guard status != .notReachable else { return }
            self?.vpnAdapter.reconnect(afterTimeInterval: 5)
        }

        startHandler = completionHandler
        vpnAdapter.connect(using: packetFlow)

    }

    override func stopTunnel(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
        stopHandler = completionHandler
        if vpnReachability.isTracking {
            vpnReachability.stopTracking()
        }
        vpnAdapter.disconnect()
    }

    override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)?) {
        if let handler = completionHandler {
            handler(messageData)
        }
    }

    override func sleep(completionHandler: @escaping () -> Void) {
        completionHandler()
    }

    override func wake() {
    }

}

extension PacketTunnelProvider: OpenVPNAdapterDelegate {
    func openVPNAdapter(_ openVPNAdapter: OpenVPNAdapter, configureTunnelWithNetworkSettings networkSettings: NEPacketTunnelNetworkSettings?, completionHandler: @escaping (Error?) -> Void) {
        networkSettings?.dnsSettings?.matchDomains = [""]

                // Set the network settings for the current tunneling session.
                setTunnelNetworkSettings(networkSettings, completionHandler: completionHandler)
    }
    

    func openVPNAdapter(_ openVPNAdapter: OpenVPNAdapter, configureTunnelWithNetworkSettings networkSettings: NEPacketTunnelNetworkSettings?, completionHandler: @escaping (OpenVPNAdapterPacketFlow?) -> Void) {
        setTunnelNetworkSettings(networkSettings) { (error) in
            completionHandler(error == nil ? self.packetFlow : nil)
        }
    }

    func openVPNAdapter(_ openVPNAdapter: OpenVPNAdapter, handleEvent event: OpenVPNAdapterEvent, message: String?) {
        switch event {
        case .connected:
            if reasserting {
                reasserting = false
            }
            guard let startHandler = startHandler else { return }
            startHandler(nil)
            self.startHandler = nil
        case .disconnected:
            guard let stopHandler = stopHandler else { return }
            if vpnReachability.isTracking {
                vpnReachability.stopTracking()
            }
            stopHandler()
            self.stopHandler = nil
        case .reconnecting:
            reasserting = true
        default:
            break
        }
    }

    func openVPNAdapter(_ openVPNAdapter: OpenVPNAdapter, handleError error: Error) {
        guard let fatal = (error as NSError).userInfo[OpenVPNAdapterErrorFatalKey] as? Bool, fatal == true else {
            return
        }
        NSLog("Error: \(error.localizedDescription)")
        NSLog("Connection Info: \(vpnAdapter.connectionInformation.debugDescription)")
        if vpnReachability.isTracking {
            vpnReachability.stopTracking()
        }

        if let startHandler = startHandler {
            startHandler(error)
            self.startHandler = nil
        } else {
            cancelTunnelWithError(error)
        }
    }

    func openVPNAdapter(_ openVPNAdapter: OpenVPNAdapter, handleLogMessage logMessage: String) {
        NSLog("Log: \(logMessage)")
    }

}


extension PacketTunnelProvider: OpenVPNAdapterPacketFlow {
    func readPackets(completionHandler: @escaping ([Data], [NSNumber]) -> Void) {
        packetFlow.readPackets(completionHandler: completionHandler)
    }

    func writePackets(_ packets: [Data], withProtocols protocols: [NSNumber]) -> Bool {
        return packetFlow.writePackets(packets, withProtocols: protocols)
    }

}
extension NEPacketTunnelFlow: OpenVPNAdapterPacketFlow {}


Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source