From c9c343cde21eab0b776c97e7017e7fd515b4ac4d Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Tue, 25 Dec 2018 22:38:32 +0100 Subject: NetworkExtension: rescope socket instead of tearing down socket Signed-off-by: Jason A. Donenfeld --- .../PacketTunnelProvider.swift | 29 +++++++------- .../PacketTunnelSettingsGenerator.swift | 8 +--- wireguard-go-bridge/src/api-ios.go | 44 ++++++++++++++++++++-- wireguard-go-bridge/wireguard.h | 2 +- 4 files changed, 57 insertions(+), 26 deletions(-) diff --git a/WireGuard/WireGuardNetworkExtension/PacketTunnelProvider.swift b/WireGuard/WireGuardNetworkExtension/PacketTunnelProvider.swift index 6b30058..67b1f4d 100644 --- a/WireGuard/WireGuardNetworkExtension/PacketTunnelProvider.swift +++ b/WireGuard/WireGuardNetworkExtension/PacketTunnelProvider.swift @@ -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.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)) } } } diff --git a/WireGuard/WireGuardNetworkExtension/PacketTunnelSettingsGenerator.swift b/WireGuard/WireGuardNetworkExtension/PacketTunnelSettingsGenerator.swift index 462d110..5946843 100644 --- a/WireGuard/WireGuardNetworkExtension/PacketTunnelSettingsGenerator.swift +++ b/WireGuard/WireGuardNetworkExtension/PacketTunnelSettingsGenerator.swift @@ -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 } diff --git a/wireguard-go-bridge/src/api-ios.go b/wireguard-go-bridge/src/api-ios.go index 902cfac..5221bb2 100644 --- a/wireguard-go-bridge/src/api-ios.go +++ b/wireguard-go-bridge/src/api-ios.go @@ -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 diff --git a/wireguard-go-bridge/wireguard.h b/wireguard-go-bridge/wireguard.h index d7183c9..71b4c83 100644 --- a/wireguard-go-bridge/wireguard.h +++ b/wireguard-go-bridge/wireguard.h @@ -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 -- cgit v1.2.3-59-g8ed1b