This paper briefly introduces the simple App applications such as G advanced search and Y video in providing security science (double S) in iOS Network Extension, and the third-party library NEKit is used to provide routing rule support.

Demo code has been adapted to Swift5, click the GitHub link to view.

Demo requires a developer account to run. Change the Bundle ID and register with your own developer account.

We need to install before creating an NEProviderTargetTemplates. PKG, after xcode10.12 apple to delete the file in xcode, why? Probably for the same reason as those VXNS that were taken down in China. Fortunately, we can also extract this file from an older version of Xcode, click here to download the link, extract code: 18EK, keep a low profile, and restart Xcode after installation.

Create a project

Next, we will create the Project. First, we will create a Project just like we would create a normal App

Once created, we create a Target in the current Project

Select Network Extension, Next

The language is swift, because NEKit is written by SWIFT and does not support OC very well, so swift is used here.

Once created, a new folder will appear under the directory

About the code of the agent in PacketTunnelProvider. Swift in writing.

After the project is created, enable the Network Extensions and Personal VXN functions in Target’s Capabilities. Ensure that both the main Target and PacketTunnel of the project are enabled.

Ok, this project is created.

NEKit integration

Because NEKit, the third-party library used in the project, only supports Carthage for integration management, the integration tool used in demo is also Carthage. If you have not used it, you can Google it by yourself. It is not difficult to install and use, and you will know it at a sight.

However, note that the third party library needs to use NEKit’s compilation method when compiling, and the direct Carthage Update project will not run.

carthage update –no-use-binaries –platform mac,ios

Framework files will be generated in Build/iOS under the Carthage directory after the third-party library is compiled. We need to add these framework files to the project. The following two locations need to be added.

So far the relevant environment configuration has been done, let’s start to look at the code how to establish VXN link

Create VXN Manager

First you need to create a NETunnelProviderManager

fileprivate func createProviderManager(a) -> NETunnelProviderManager {
    let manager = NETunnelProviderManager(a)let conf = NETunnelProviderProtocol()
    conf.serverAddress = "BearFree"
    manager.protocolConfiguration = conf
    manager.localizedDescription = "BearFree"
    return manager
}
Copy the code

Save the manager to the system

manager.saveToPreferences{
    error in
        iferror ! =nil{print(error);return; }//Todo
}
Copy the code

After the save method is executed, the VXN menu is displayed to add the CREATED VPN

In this case, if the save method is invoked for several times, VPN 1, VPN 2 and other description files will appear. Therefore, Apple also requires that the current Managers be read before creation

NETunnelProviderManager.loadAllFromPreferencesWithCompletionHandler{ 
    (managers, error) in
    guard let managers = managers else{return}
    let manager: NETunnelProviderManager
    if managers.count > 0 {
        manager = managers[0]}else{
        manager = self.createProviderManager()
    }
    // Todo
    // manager.saveToPreferences.......
}
Copy the code

Configure the Network Extension

Open the template file in Extension and modify the syntax for the Swift version and parent class. The following two functions are required to control the VPN status

// Called when the VPN is started
func startTunnel(options: [String : NSObject]? , completionHandler: @escaping(Error?) -> Void)

// Called when the VPN is stopped
func stopTunnel(with reason: NEProviderStopReason, completionHandler: @escaping (a) -> Void)
Copy the code

At this point, we just need to test whether the VPN connection can be established normally. So we just need to call setTunnelNetworkSettings in startTunnelWithOptions. When completionHandler() executes, the VPN connection is displayed on the phone.

override func startTunnel(options: [String : NSObject]? , completionHandler: @escaping(Error?) -> Void) {
    let ipv4Settings = NEIPv4Settings(addresses: ["192.169.89.1"], subnetMasks: ["255.255.255.0"])
    let networkSettings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: "8.8.8.8")
    networkSettings.mtu = 1500
    networkSettings.iPv4Settings = ipv4Settings
    setTunnelNetworkSettings(networkSettings) {
        error in
        guard error == nil else {
            completionHandler(error)
            return
        }
            completionHandler(nil)}}Copy the code

Open VXN

Call the startVPNTunnel method on the newly created Manager

try manager.connection.startVPNTunnel(options: [:])
Copy the code

If startVPNTunnel is executed immediately after VXN saveToPreferences is created, Domin=”null” may be displayed, indicating that the system is not ready. So the startup code to loadAndCreatePrividerManager methods return in the block

func connect(a){
    self.loadAndCreatePrividerManager { (manager) in
        guard let manager = manager else{return}
        do{
            try manager.connection.startVPNTunnel(options: [:])
        }catch let err{
            print(err)
        }
    }
}
Copy the code

For the implementation logic, see the VPNManager. swift file in Demo.

After the success of the connection manager. The connection. The status will change, so we need to monitor the status value, thus learned that the current state of the VPN connection, used to update the UI display.

func addVPNStatusObserver(a) {
    guard! observerAddedelse{
        return
    }
    loadProviderManager { [unowned self] (manager) -> Void in
        if let manager = manager {
            self.observerAdded = true
            NotificationCenter.default.addObserver(forName: NSNotification.Name.NEVPNStatusDidChange, object: manager.connection, queue: OperationQueue.main, using: { [unowned self] (notification) -> Void in
                self.updateVPNStatus(manager)
                })
            }
        }
}
Copy the code

VXN configuration (SUCH as IP address and port)

The Manager transmits configuration information to Network Extension through the protocolConfiguration property. The following configuration method is implemented in vpnManager and invoked before starting the VPN

fileprivate func setRulerConfig(_ manager:NETunnelProviderManager){
    var conf = [String:AnyObject]()
    conf["ss_address"] = ip_address as AnyObject?
    conf["ss_port"] = port as AnyObject?
    conf["ss_method"] = algorithm as AnyObject? // A fatal Error is raised
    conf["ss_password"] = password as AnyObject?
    conf["ymal_conf"] = getRuleConf() as AnyObject?
    let orignConf = manager.protocolConfiguration as! NETunnelProviderProtocol
    orignConf.providerConfiguration = conf
    manager.protocolConfiguration = orignConf
    print(ip_address,port,algorithm,password)
}
Copy the code

In the Extension

public var protocolConfiguration: NEVPNProtocol { get }    
Copy the code

Save the configuration information written above, we can read directly.

About network status changes

4G and wifi switch, here is really a bit of a pit, in the inside around for a long time, at the beginning of the debugging found that VXN is through, but over a period of time can not be used, deleted reinstallation is not good, inadvertently changed an EXTENSION IP and good, changed the network environment and can not be used, puzzled, debugging for a long time did not find the problem, Here is how to monitor network environment changes:

Public var defaultPath: NWPath? {get} indicates the network request path of the current system. We can listen for defaultPath changes through KVO. If defaultPath changes from the previous state and defaultPath.status ==.Satisfied, we can assume that the system network has been switched. In this case, you need to restart the VPN and proxy server.

Reconnecting the VPN method is as simple as calling the StartVPN function again.

override func observeValue(forKeyPath keyPath: String? , of object: Any? , change: [NSKeyValueChangeKey : Any]? , context: UnsafeMutableRawPointer?) {
    if keyPath == "defaultPath" {
        if self.defaultPath?.status == .satisfied && self.defaultPath ! = lastPath{if(lastPath == nil){
                lastPath = self.defaultPath
            }else{
                NSLog("received network change notifcation")
                DispatchQueue.main.asyncAfter(deadline: .now() + 1) {[weak self] in
                // Delay for 1s to ensure the system is ready
                    guard let strongSelf = self else { return }
                    strongSelf.startTunnel(options: nil) {_ in}}}}else{
            lastPath = defaultPath
        }
    }
}
Copy the code

How do YOU debug in this Network Extension? We tried To add breakpoints To the current file in normal debug mode, but found no response, so Tunnel debugging has a separate debugging mode, first we need To run the main program on the device, and then through Xcode-> debug ->Attach To Process (there is a list loading Process, Wait a moment.) Select your Tunnel name and perform debugging. If you do not manually disable the debugging mode of a Tunnel during debugging, the VXN link will break in seconds. Here again, I was how to solve the above problems when switching network, find our App in the system Settings, adjust the network access for WLAN and cellular mobile, at this point before switching network, there is no problem, in order to when just start the App can get Internet access, I added a useless network request, You can get access on your own terms.

demoUI

For the UI layer, I directly use Eureka, the third-party framework, which is the Swift version of XLForm and is also integrated through Carthage. If you want to know more, you can also see the demo.

The End

Ok, THE problems I encountered should be clear, you can communicate with me if there is any problem, hope to correct, 3Q!

Reference Documents:

NEKit

A preliminary look at iOS Network Extension

IOS development practice -NetworkExtension