Unable to configure routing in NEPacketTunnelNetworkSettings

I've implemented a NEPacketTunnelProvider implementation, but I can't see any packets in packet flow. Here is the settings

override func startTunnel(options: [String : NSObject]?, completionHandler: @escaping (Error?) -> Void) {

...

    let networkSettings = self.initTunnelSettings()
    setTunnelNetworkSettings(networkSettings) { (error) in
    if let error = error {
        completionHandler(error)
        return
    }
    NSLog("Proxy address: \(networkSettings.proxySettings?.httpsServer?.address)")
    
    self.readPackets()

    completionHandler(nil)
}

private func initTunnelSettings() -> NEPacketTunnelNetworkSettings {
    let settings: NEPacketTunnelNetworkSettings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: "85.182.68.10")
            
    /* ipv4 settings */
    settings.ipv4Settings = NEIPv4Settings(addresses: ["1.2.3.4"], subnetMasks: ["255.255.255.255"])
        
    settings.ipv4Settings?.includedRoutes = [NEIPv4Route.default()]
    
    /* MTU */
    settings.mtu = 1500

    return settings
}

There is even more strange thing. If I add more specific route into includedRoutes array, I can see packets from that route traffic

settings.ipv4Settings?.includedRoutes = [NEIPv4Route.default(), NEIPv4Route(destinationAddress: "192.168.0.103", subnetMask: "255.255.255.252")]

With this changes I will see packets with the destination IP "192.168.0.103", but if change mask to 255.255.255.0 no packet in the packet flow again settings.ipv4Settings?.includedRoutes = [NEIPv4Route.default(), NEIPv4Route(destinationAddress: "192.168.0.103", subnetMask: "255.255.255.0")]

Anyway my goal is to route all traffic through virtual interface and get all packets in the packetFlow.

Can you help me? What should I change?

Replies

I’m not sure if this explains the problem you’re seeing, but the startTunnel(…) snippet you posted has unbalanced curly brackets. That makes it hard to confirm whether the code’s structure is correct.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

@eskimo thanks for you answer. You are right. There is missed curly bracket at the end. Unfortunately it does not explains the problem. Snipped with fixed curly brackets:

override func startTunnel(options: [String : NSObject]?, completionHandler: @escaping (Error?) -> Void) {
            
     ...

    let networkSettings = self.initTunnelSettings(proxyHost: self.localProxyHost, proxyPort: PacketTunnelProvider.localProxyPort)
    
    setTunnelNetworkSettings(networkSettings) { (error) in
        if let error = error {
            completionHandler(error)
            return
        }
        
        self.readPackets()

        NSLog("Proxy address: \(networkSettings.proxySettings?.httpsServer?.address)")

        completionHandler(nil)
    }
}

Thanks for tidying that up. I agree, that looks like.

How are you testing your routing?

For reference, the way I test the initial bring up of an NE packet tunnel provider is to create a BSD Sockets UDP client that sends datagrams to a specific IP address. That eliminates a lot of potential confusion, like DNS and Happy Eyeballs. If you do that, do you see the packet for those datagrams in your provider?

ps If you’ve not familiar with BSD Sockets and you prefer to use Swift, see Calling BSD Sockets from Swift for an example of how to use it for tests like this.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

The code I use to test routing is below.

private func sendUDPRequestWithSocket() {
    let clientSocket = socket(AF_INET, SOCK_DGRAM, 0)
    guard clientSocket != -1 else {
        perror("Error creating socket")
        return
    }
    var addr = sockaddr_in()
            addr.sin_family = sa_family_t(AF_INET)
            addr.sin_port = in_port_t(UInt16(59178).bigEndian)
            addr.sin_addr.s_addr = inet_addr("192.168.0.103")
    
    withUnsafePointer(to: addr) { ptrAddr in
        ptrAddr.withMemoryRebound(to: sockaddr.self, capacity: 1) { ptrSockaddr in
            guard connect(clientSocket, ptrSockaddr, socklen_t(MemoryLayout<sockaddr_in>.size)) != -1 else {
                perror("Error connecting to server")
                close(clientSocket)
                return
            }
        }
    }
    
    let message = ““Text message””
    guard let messageData = message.data(using: .utf8) else {
       print("Error encoding message")
       return
    }

    _ = messageData.withUnsafeBytes { bufferPointer in
       send(clientSocket, bufferPointer.baseAddress, messageData.count, 0)
    }
}

I have an active UDP server on "192.168.0.103"

Behavior depending on includedRoutes option value: includedRoutes = [NEIPv4Route.default())] - no packets in the packet flow includedRoutes = [NEIPv4Route.default(), NEIPv4Route(destinationAddress: "192.168.0.103", subnetMask: "255.255.255.0")] - no packets in the packet flow includedRoutes = [NEIPv4Route.default(), NEIPv4Route(destinationAddress: "192.168.0.103", subnetMask: "255.255.255.252")] - packets present in the packet flow

@eskimo do you have any ideas or suggestions what should I change to get packets from all routes in packet flow?