]> git.ipfire.org Git - thirdparty/wireguard-apple.git/commitdiff
NetworkExtension: rescope socket instead of tearing down socket
authorJason A. Donenfeld <Jason@zx2c4.com>
Tue, 25 Dec 2018 21:38:32 +0000 (22:38 +0100)
committerJason A. Donenfeld <Jason@zx2c4.com>
Wed, 26 Dec 2018 00:17:55 +0000 (01:17 +0100)
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
WireGuard/WireGuardNetworkExtension/PacketTunnelProvider.swift
WireGuard/WireGuardNetworkExtension/PacketTunnelSettingsGenerator.swift
wireguard-go-bridge/src/api-ios.go
wireguard-go-bridge/wireguard.h

index 6b300584447c4ba9f3061c097825a235dd081a18..67b1f4d03fc5d0c9d238d9986dd04844ba69593f 100644 (file)
@@ -10,7 +10,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
 
     private var handle: Int32?
     private var networkMonitor: NWPathMonitor?
-    private var lastFirstInterface: NWInterface?
+    private var ifname: String?
     private var packetTunnelSettingsGenerator: PacketTunnelSettingsGenerator?
 
     deinit {
@@ -49,7 +49,6 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
                 startTunnelCompletionHandler(PacketTunnelProviderError.couldNotSetNetworkSettings)
             } else {
                 self.networkMonitor = NWPathMonitor()
-                self.lastFirstInterface = self.networkMonitor!.currentPath.availableInterfaces.first
                 self.networkMonitor!.pathUpdateHandler = self.pathUpdate
                 self.networkMonitor!.start(queue: DispatchQueue(label: "NetworkMonitor"))
 
@@ -60,6 +59,13 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
                     startTunnelCompletionHandler(PacketTunnelProviderError.couldNotDetermineFileDescriptor)
                     return
                 }
+                var ifnameSize = socklen_t(IFNAMSIZ)
+                let ifnamePtr = UnsafeMutablePointer<CChar>.allocate(capacity: Int(ifnameSize))
+                ifnamePtr.initialize(repeating: 0, count: Int(ifnameSize))
+                if getsockopt(fileDescriptor, 2 /* SYSPROTO_CONTROL */, 2 /* UTUN_OPT_IFNAME */, ifnamePtr, &ifnameSize) == 0 {
+                    self.ifname = String(cString: ifnamePtr)
+                }
+                wg_log(.info, message: "Tunnel interface is \(self.ifname ?? "unknown")")
                 let handle = self.packetTunnelSettingsGenerator!.uapiConfiguration().withGoString { return wgTurnOn($0, fileDescriptor) }
                 if handle < 0 {
                     wg_log(.error, message: "Starting tunnel failed with wgTurnOn returning \(handle)")
@@ -107,19 +113,14 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
 
     private func pathUpdate(path: Network.NWPath) {
         guard let handle = handle, let packetTunnelSettingsGenerator = packetTunnelSettingsGenerator else { return }
-        var listenPort: UInt16?
-        //TODO(zx2c4): Remove the `true` here after extensive testing with network/cell simulations.
-        if true || path.availableInterfaces.isEmpty || lastFirstInterface != path.availableInterfaces.first {
-            listenPort = wgGetListenPort(handle)
-            lastFirstInterface = path.availableInterfaces.first
+        wg_log(.debug, message: "Network change detected with \(path.status) route and interface order \(path.availableInterfaces)")
+        _ = packetTunnelSettingsGenerator.endpointUapiConfiguration().withGoString { return wgSetConfig(handle, $0) }
+        var interfaces = path.availableInterfaces
+        if let ifname = ifname {
+            interfaces = interfaces.filter { $0.name != ifname }
         }
-        guard path.status == .satisfied else { return }
-        wg_log(.debug, message: "Network change detected, re-establishing sockets and IPs: \(path.availableInterfaces)")
-        let endpointString = packetTunnelSettingsGenerator.endpointUapiConfiguration(currentListenPort: listenPort)
-        let err = endpointString.withGoString { return wgSetConfig(handle, $0) }
-        if err == -EADDRINUSE && listenPort != nil {
-            let endpointString = packetTunnelSettingsGenerator.endpointUapiConfiguration(currentListenPort: 0)
-            _ = endpointString.withGoString { return wgSetConfig(handle, $0) }
+        if let ifscope = interfaces.first?.index {
+            wgBindInterfaceScope(handle, Int32(ifscope))
         }
     }
 }
index 462d11061e05b5137c7f54222924d0c5cc7fa45c..5946843ddf7eb36c85e84ebee35f8c0b99188c52 100644 (file)
@@ -14,13 +14,8 @@ class PacketTunnelSettingsGenerator {
         self.resolvedEndpoints = resolvedEndpoints
     }
 
-    func endpointUapiConfiguration(currentListenPort: UInt16?) -> String {
+    func endpointUapiConfiguration() -> String {
         var wgSettings = ""
-
-        if let currentListenPort = currentListenPort {
-            wgSettings.append("listen_port=\(tunnelConfiguration.interface.listenPort ?? currentListenPort)\n")
-        }
-
         for (index, peer) in tunnelConfiguration.peers.enumerated() {
             wgSettings.append("public_key=\(peer.publicKey.hexEncodedString())\n")
             if let endpoint = resolvedEndpoints[index] {
@@ -28,7 +23,6 @@ class PacketTunnelSettingsGenerator {
                 wgSettings.append("endpoint=\(endpoint.stringRepresentation)\n")
             }
         }
-
         return wgSettings
     }
 
index 902cfac9586fd14652e3e4735a952aa2b4530f1e..5221bb2f5d8230237f15462ea347e794c2f1d4eb 100644 (file)
@@ -137,13 +137,49 @@ func wgSetConfig(tunnelHandle int32, settings string) int64 {
        return 0
 }
 
-//export wgGetListenPort
-func wgGetListenPort(tunnelHandle int32) uint16 {
+//export wgBindInterfaceScope
+func wgBindInterfaceScope(tunnelHandle int32, ifscope int32) {
+       var operr error
        device, ok := tunnelHandles[tunnelHandle]
        if !ok {
-               return 0
+               return
+       }
+       device.log.Info.Printf("Binding sockets to interface %d\n", ifscope)
+       bind := device.net.bind.(*NativeBind)
+       for bind.ipv4 != nil {
+               fd, err := bind.ipv4.SyscallConn()
+               if err != nil {
+                       device.log.Error.Printf("Unable to bind v4 socket to interface:", err)
+                       break
+               }
+               err = fd.Control(func(fd uintptr) {
+                       operr = unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_BOUND_IF, int(ifscope))
+               })
+               if err == nil {
+                       err = operr
+               }
+               if err != nil {
+                       device.log.Error.Printf("Unable to bind v4 socket to interface:", err)
+               }
+               break
+       }
+       for bind.ipv6 != nil {
+               fd, err := bind.ipv6.SyscallConn()
+               if err != nil {
+                       device.log.Error.Printf("Unable to bind v6 socket to interface:", err)
+                       break
+               }
+               err = fd.Control(func(fd uintptr) {
+                       operr = unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_BOUND_IF, int(ifscope))
+               })
+               if err == nil {
+                       err = operr
+               }
+               if err != nil {
+                       device.log.Error.Printf("Unable to bind v6 socket to interface:", err)
+               }
+               break
        }
-       return device.net.port
 }
 
 //export wgVersion
index d7183c9793187f5ac5b9aa452a3a18dabb971838..71b4c832a923bb6bef378e2353764dde9395fa8d 100644 (file)
@@ -15,7 +15,7 @@ extern void wgSetLogger(logger_fn_t logger_fn);
 extern int wgTurnOn(gostring_t settings, int32_t tun_fd);
 extern void wgTurnOff(int handle);
 extern int64_t wgSetConfig(int handle, gostring_t settings);
-extern uint16_t wgGetListenPort(int handle);
+extern void wgBindInterfaceScope(int handle, int32_t ifscope);
 extern char *wgVersion();
 
 #endif