I have the code below to create an NEPacketTunnelProvider. I can inspect the packets and see the source and destination addresses and ports if the packets are tcp or udp etc. However, I lose all routing. My understanding is that if I write to the packetFlow the same data I read no routing would be lost. What am I doing wrong?
import NetworkExtension
import os.log
class AppProxyProvider: NEPacketTunnelProvider {
func getIPAddress() -> String {
var address: String?
var ifaddr: UnsafeMutablePointer? = nil
if getifaddrs(&ifaddr) == 0 {
var ptr = ifaddr
while ptr != nil {
defer { ptr = ptr?.pointee.ifa_next }
let interface = ptr?.pointee
let addrFamily = interface?.ifa_addr.pointee.sa_family
if addrFamily == UInt8(AF_INET) || addrFamily == UInt8(AF_INET6) {
// wifi = ["en0"]
// wired = ["en2", "en3", "en4"]
// cellular = ["pdp_ip0","pdp_ip1","pdp_ip2","pdp_ip3"]
let name: String = String(cString: (interface!.ifa_name))
if name == "en0" || name == "en2" || name == "en3" || name == "en4" || name == "pdp_ip0" || name == "pdp_ip1" || name == "pdp_ip2" || name == "pdp_ip3" {
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
getnameinfo(interface?.ifa_addr, socklen_t((interface?.ifa_addr.pointee.sa_len)!), &hostname, socklen_t(hostname.count), nil, socklen_t(0), NI_NUMERICHOST)
address = String(cString: hostname)
}
}
}
freeifaddrs(ifaddr)
}
return address ?? ""
}
override func startTunnel(options: [String : NSObject]? = nil, completionHandler: @escaping (Error?) -> Void) {
NSLog("Starting tunnel: '%@'", self.protocolConfiguration.serverAddress ?? "")
let settings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: getIPAddress())
settings.tunnelOverheadBytes = 81
settings.mtu = 1200
settings.ipv4Settings = NEIPv4Settings(addresses: [getIPAddress()], subnetMasks: ["255.255.255.255"]) settings.ipv4Settings?.includedRoutes = [NEIPv4Route.default()] // all routes
settings.ipv4Settings?.excludedRoutes = [
NEIPv4Route(destinationAddress: "0.0.0.0", subnetMask: "255.0.0.0"),
NEIPv4Route(destinationAddress: "10.0.0.0", subnetMask: "255.0.0.0"),
NEIPv4Route(destinationAddress: "127.0.0.0", subnetMask: "255.0.0.0"),
NEIPv4Route(destinationAddress: "192.168.0.0", subnetMask: "255.255.0.0"),
NEIPv4Route(destinationAddress: "255.255.255.255", subnetMask: "255.255.255.255"),
NEIPv4Route(destinationAddress: "1.1.1.1", subnetMask: "255.255.255.255"),
NEIPv4Route(destinationAddress: "1.0.0.1", subnetMask: "255.255.255.255"),
NEIPv4Route(destinationAddress: "8.8.8.8", subnetMask: "255.255.255.255"),
NEIPv4Route(destinationAddress: "8.8.4.4", subnetMask: "255.255.255.255")
] // avoid local routes and our dns servers
// https://en.wikipedia.org/wiki/Reserved_IP_addresses
//settings.dnsSettings = NEDNSSettings(servers: []) // just use the system dns servers
// https://securitytrails.com/blog/dns-servers-privacy-security
settings.dnsSettings = NEDNSSettings(servers: ["1.1.1.1", "1.0.0.1", "8.8.8.8", "8.8.4.4"])
settings.dnsSettings?.matchDomains = [] // use system DNS
//settings.dnsSettings?.matchDomains = [""] // override system dns
self.reasserting = true
self.setTunnelNetworkSettings(settings) { error in
if let e = error {
NSLog("Settings error %@", e.localizedDescription)
completionHandler(e)
} else {
NSLog("Settings set without error")
self.reasserting = false
self.readPackets()
completionHandler(nil)
}
}
}
override func stopTunnel(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
NSLog("Stopping tunnel")
completionHandler()
}
private func readPackets() {
self.packetFlow.readPackets() { datas, protocolNumbers in
self.sendPackets(datas, withProtocols: protocolNumbers)
self.readPackets()
}
}
private func sendPackets(_ data: [Data], withProtocols numbers: [NSNumber]) {
self.packetFlow.writePackets(data, withProtocols: numbers)
}
private func processPacket(_ data: Data, protocolNumber: NSNumber) -> (Data, NSNumber) {
let packet = IpPacket(withData: data, protocol: Protocol(rawValue: protocolNumber)!)
NSLog("%@ packet created with %@ protocol src %@:%d dst %@:%d", packet.ipVersion.debugDescription, packet.transportProtocol.debugDescription, (packet.sourceAddress?.presentation)!, (packet.sourcePort ?? -1), (packet.destinationAddress?.presentation)!, (packet.destinationPort ?? -1))
return (data, protocolNumber)
}
}