NetworkExtension: rescope socket instead of tearing down socket
This commit is contained in:
parent
a380f17974
commit
0cc55db4cb
|
@ -10,7 +10,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||||
|
|
||||||
private var handle: Int32?
|
private var handle: Int32?
|
||||||
private var networkMonitor: NWPathMonitor?
|
private var networkMonitor: NWPathMonitor?
|
||||||
private var lastFirstInterface: NWInterface?
|
private var ifname: String?
|
||||||
private var packetTunnelSettingsGenerator: PacketTunnelSettingsGenerator?
|
private var packetTunnelSettingsGenerator: PacketTunnelSettingsGenerator?
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
|
@ -49,7 +49,6 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||||
startTunnelCompletionHandler(PacketTunnelProviderError.couldNotSetNetworkSettings)
|
startTunnelCompletionHandler(PacketTunnelProviderError.couldNotSetNetworkSettings)
|
||||||
} else {
|
} else {
|
||||||
self.networkMonitor = NWPathMonitor()
|
self.networkMonitor = NWPathMonitor()
|
||||||
self.lastFirstInterface = self.networkMonitor!.currentPath.availableInterfaces.first
|
|
||||||
self.networkMonitor!.pathUpdateHandler = self.pathUpdate
|
self.networkMonitor!.pathUpdateHandler = self.pathUpdate
|
||||||
self.networkMonitor!.start(queue: DispatchQueue(label: "NetworkMonitor"))
|
self.networkMonitor!.start(queue: DispatchQueue(label: "NetworkMonitor"))
|
||||||
|
|
||||||
|
@ -60,6 +59,13 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||||
startTunnelCompletionHandler(PacketTunnelProviderError.couldNotDetermineFileDescriptor)
|
startTunnelCompletionHandler(PacketTunnelProviderError.couldNotDetermineFileDescriptor)
|
||||||
return
|
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) }
|
let handle = self.packetTunnelSettingsGenerator!.uapiConfiguration().withGoString { return wgTurnOn($0, fileDescriptor) }
|
||||||
if handle < 0 {
|
if handle < 0 {
|
||||||
wg_log(.error, message: "Starting tunnel failed with wgTurnOn returning \(handle)")
|
wg_log(.error, message: "Starting tunnel failed with wgTurnOn returning \(handle)")
|
||||||
|
@ -107,19 +113,14 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||||
|
|
||||||
private func pathUpdate(path: Network.NWPath) {
|
private func pathUpdate(path: Network.NWPath) {
|
||||||
guard let handle = handle, let packetTunnelSettingsGenerator = packetTunnelSettingsGenerator else { return }
|
guard let handle = handle, let packetTunnelSettingsGenerator = packetTunnelSettingsGenerator else { return }
|
||||||
var listenPort: UInt16?
|
wg_log(.debug, message: "Network change detected with \(path.status) route and interface order \(path.availableInterfaces)")
|
||||||
//TODO(zx2c4): Remove the `true` here after extensive testing with network/cell simulations.
|
_ = packetTunnelSettingsGenerator.endpointUapiConfiguration().withGoString { return wgSetConfig(handle, $0) }
|
||||||
if true || path.availableInterfaces.isEmpty || lastFirstInterface != path.availableInterfaces.first {
|
var interfaces = path.availableInterfaces
|
||||||
listenPort = wgGetListenPort(handle)
|
if let ifname = ifname {
|
||||||
lastFirstInterface = path.availableInterfaces.first
|
interfaces = interfaces.filter { $0.name != ifname }
|
||||||
}
|
}
|
||||||
guard path.status == .satisfied else { return }
|
if let ifscope = interfaces.first?.index {
|
||||||
wg_log(.debug, message: "Network change detected, re-establishing sockets and IPs: \(path.availableInterfaces)")
|
wgBindInterfaceScope(handle, Int32(ifscope))
|
||||||
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) }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,13 +14,8 @@ class PacketTunnelSettingsGenerator {
|
||||||
self.resolvedEndpoints = resolvedEndpoints
|
self.resolvedEndpoints = resolvedEndpoints
|
||||||
}
|
}
|
||||||
|
|
||||||
func endpointUapiConfiguration(currentListenPort: UInt16?) -> String {
|
func endpointUapiConfiguration() -> String {
|
||||||
var wgSettings = ""
|
var wgSettings = ""
|
||||||
|
|
||||||
if let currentListenPort = currentListenPort {
|
|
||||||
wgSettings.append("listen_port=\(tunnelConfiguration.interface.listenPort ?? currentListenPort)\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
for (index, peer) in tunnelConfiguration.peers.enumerated() {
|
for (index, peer) in tunnelConfiguration.peers.enumerated() {
|
||||||
wgSettings.append("public_key=\(peer.publicKey.hexEncodedString())\n")
|
wgSettings.append("public_key=\(peer.publicKey.hexEncodedString())\n")
|
||||||
if let endpoint = resolvedEndpoints[index] {
|
if let endpoint = resolvedEndpoints[index] {
|
||||||
|
@ -28,7 +23,6 @@ class PacketTunnelSettingsGenerator {
|
||||||
wgSettings.append("endpoint=\(endpoint.stringRepresentation)\n")
|
wgSettings.append("endpoint=\(endpoint.stringRepresentation)\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return wgSettings
|
return wgSettings
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -137,13 +137,49 @@ func wgSetConfig(tunnelHandle int32, settings string) int64 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
//export wgGetListenPort
|
//export wgBindInterfaceScope
|
||||||
func wgGetListenPort(tunnelHandle int32) uint16 {
|
func wgBindInterfaceScope(tunnelHandle int32, ifscope int32) {
|
||||||
|
var operr error
|
||||||
device, ok := tunnelHandles[tunnelHandle]
|
device, ok := tunnelHandles[tunnelHandle]
|
||||||
if !ok {
|
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
|
//export wgVersion
|
||||||
|
|
|
@ -15,7 +15,7 @@ extern void wgSetLogger(logger_fn_t logger_fn);
|
||||||
extern int wgTurnOn(gostring_t settings, int32_t tun_fd);
|
extern int wgTurnOn(gostring_t settings, int32_t tun_fd);
|
||||||
extern void wgTurnOff(int handle);
|
extern void wgTurnOff(int handle);
|
||||||
extern int64_t wgSetConfig(int handle, gostring_t settings);
|
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();
|
extern char *wgVersion();
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue