summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAndrej Mihajlov <and@mullvad.net>2020-05-19 14:12:49 +0200
committerAndrej Mihajlov <and@mullvad.net>2020-05-19 14:12:49 +0200
commit0753dd15c6f9543d9f0d80342fd8ace45dcb0533 (patch)
treec52dc794dae69dc8df00717bb1ef2d6d23294b2c
parent17e2160cbc1438b080e0181aa022b9630e4b6ce9 (diff)
parent97b49787a496709156a1cb07ad7dd0f555aec517 (diff)
downloadmullvadvpn-0753dd15c6f9543d9f0d80342fd8ace45dcb0533.tar.xz
mullvadvpn-0753dd15c6f9543d9f0d80342fd8ace45dcb0533.zip
Merge branch 'fix-ipv6-support'
-rw-r--r--ios/CHANGELOG.md1
-rw-r--r--ios/PacketTunnel/AnyIPEndpoint+DNS64.swift6
-rw-r--r--ios/PacketTunnel/PacketTunnelProvider.swift6
-rw-r--r--ios/PacketTunnel/PacketTunnelSettingsGenerator.swift26
-rw-r--r--ios/PacketTunnel/WireguardCommand.swift21
-rw-r--r--ios/PacketTunnel/WireguardConfiguration.swift45
-rw-r--r--ios/PacketTunnel/WireguardDevice.swift142
7 files changed, 131 insertions, 116 deletions
diff --git a/ios/CHANGELOG.md b/ios/CHANGELOG.md
index 50627f748a..7e37c1150f 100644
--- a/ios/CHANGELOG.md
+++ b/ios/CHANGELOG.md
@@ -33,6 +33,7 @@ Line wrap the file at 100 chars. Th
the tunnel.
- Properly format date intervals close to 1 day or less than 1 minute. Enforce intervals between 1
and 90 days to always be displayed in days quantity.
+- Fix a number of errors in DNS64 resolution and IPv6 support.
## [2020.2] - 2020-04-16
### Fixed
diff --git a/ios/PacketTunnel/AnyIPEndpoint+DNS64.swift b/ios/PacketTunnel/AnyIPEndpoint+DNS64.swift
index 758b731bd8..238db4b253 100644
--- a/ios/PacketTunnel/AnyIPEndpoint+DNS64.swift
+++ b/ios/PacketTunnel/AnyIPEndpoint+DNS64.swift
@@ -51,12 +51,6 @@ extension AnyIPEndpoint {
freeaddrinfo(resultPointer)
- if "\(resolvedAddress.ip)" == "\(self.ip)" {
- os_log(.debug, "DNS64: mapped %{public}s to itself", "\(resolvedAddress.ip)")
- } else {
- os_log(.debug, "DNS64: mapped %{public}s to %{public}s", "\(self.ip)", "\(resolvedAddress.ip)")
- }
-
return .success(resolvedAddress)
}
}
diff --git a/ios/PacketTunnel/PacketTunnelProvider.swift b/ios/PacketTunnel/PacketTunnelProvider.swift
index c022f930cf..6ca459ef3b 100644
--- a/ios/PacketTunnel/PacketTunnelProvider.swift
+++ b/ios/PacketTunnel/PacketTunnelProvider.swift
@@ -89,7 +89,11 @@ extension PacketTunnelConfiguration {
return WireguardConfiguration(
privateKey: tunnelConfig.interface.privateKey,
- peers: wireguardPeers
+ peers: wireguardPeers,
+ allowedIPs: [
+ IPAddressRange(address: IPv4Address.any, networkPrefixLength: 0),
+ IPAddressRange(address: IPv6Address.any, networkPrefixLength: 0)
+ ]
)
}
}
diff --git a/ios/PacketTunnel/PacketTunnelSettingsGenerator.swift b/ios/PacketTunnel/PacketTunnelSettingsGenerator.swift
index 63c0bad96f..82fca24762 100644
--- a/ios/PacketTunnel/PacketTunnelSettingsGenerator.swift
+++ b/ios/PacketTunnel/PacketTunnelSettingsGenerator.swift
@@ -29,7 +29,7 @@ struct PacketTunnelSettingsGenerator {
private func dnsSettings() -> NEDNSSettings {
let serverAddresses = [mullvadEndpoint.ipv4Gateway, mullvadEndpoint.ipv6Gateway]
- .map { String(reflecting: $0) }
+ .map { "\($0)" }
let dnsSettings = NEDNSSettings(servers: serverAddresses)
@@ -51,14 +51,6 @@ struct PacketTunnelSettingsGenerator {
NEIPv4Route.default() // 0.0.0.0/0
]
- let relayAddressRange = IPAddressRange(address: mullvadEndpoint.ipv4Relay.ip, networkPrefixLength: 32)
-
- ipv4Settings.excludedRoutes = [
- NEIPv4Route(
- destinationAddress: "\(relayAddressRange.address)",
- subnetMask: ipv4SubnetMaskString(of: relayAddressRange))
- ]
-
return ipv4Settings
}
@@ -66,21 +58,21 @@ struct PacketTunnelSettingsGenerator {
let interfaceAddresses = tunnelConfiguration.interface.addresses
let ipv6AddressRanges = interfaceAddresses.filter { $0.address is IPv6Address }
+ let addresses = ipv6AddressRanges.map { "\($0.address)" }
+
+ // The smallest prefix that will have any effect on iOS is /120
+ let networkPrefixLengths = ipv6AddressRanges
+ .map { NSNumber(value: min(120, $0.networkPrefixLength)) }
+
let ipv6Settings = NEIPv6Settings(
- addresses: ipv6AddressRanges.map { "\($0.address)" },
- networkPrefixLengths: ipv6AddressRanges.map { NSNumber(value: $0.networkPrefixLength) }
+ addresses: addresses,
+ networkPrefixLengths: networkPrefixLengths
)
ipv6Settings.includedRoutes = [
NEIPv6Route.default() // ::0
]
- if let ipv6Relay = mullvadEndpoint.ipv6Relay {
- ipv6Settings.excludedRoutes = [
- NEIPv6Route(destinationAddress: "\(ipv6Relay.ip)", networkPrefixLength: 128)
- ]
- }
-
return ipv6Settings
}
diff --git a/ios/PacketTunnel/WireguardCommand.swift b/ios/PacketTunnel/WireguardCommand.swift
index 86d0bb898f..1ece7883e5 100644
--- a/ios/PacketTunnel/WireguardCommand.swift
+++ b/ios/PacketTunnel/WireguardCommand.swift
@@ -17,30 +17,18 @@ struct WireguardPeer: Hashable {
extension WireguardPeer {
func withReresolvedEndpoint() -> Result<WireguardPeer, Error> {
- self.endpoint.withReresolvedIP()
+ return self.endpoint.withReresolvedIP()
.map { WireguardPeer(endpoint: $0, publicKey: self.publicKey) }
}
}
-extension WireguardPeer {
-
- /// Returns a 0.0.0.0/0 for IPv4 and ::0/0 for IPv6
- var anyAllowedIP: IPAddressRange {
- switch endpoint {
- case .ipv4:
- return IPAddressRange(address: IPv4Address.any, networkPrefixLength: 0)
- case .ipv6:
- return IPAddressRange(address: IPv6Address.any, networkPrefixLength: 0)
- }
- }
-}
-
enum WireguardCommand {
case privateKey(WireguardPrivateKey)
case listenPort(UInt16)
case replacePeers
case peer(WireguardPeer)
+ case replaceAllowedIPs
case allowedIP(IPAddressRange)
}
@@ -66,6 +54,9 @@ extension WireguardCommand {
return ["public_key=\(keyString)", "endpoint=\(endpointString)"]
.joined(separator: "\n")
+ case .replaceAllowedIPs:
+ return "replace_allowed_ips=true"
+
case .allowedIP(let ipAddressRange):
return "allowed_ip=\(ipAddressRange)"
}
@@ -75,7 +66,7 @@ extension WireguardCommand {
extension Array where Element == WireguardCommand {
func toRawWireguardConfigString() -> String {
- map { $0.toRawWireguardCommand() }
+ return map { $0.toRawWireguardCommand() }
.joined(separator: "\n")
}
}
diff --git a/ios/PacketTunnel/WireguardConfiguration.swift b/ios/PacketTunnel/WireguardConfiguration.swift
index d4a31327bf..2652a3145e 100644
--- a/ios/PacketTunnel/WireguardConfiguration.swift
+++ b/ios/PacketTunnel/WireguardConfiguration.swift
@@ -6,14 +6,13 @@
// Copyright © 2019 Mullvad VPN AB. All rights reserved.
//
-import Combine
import Foundation
-import os
/// A struct describing a basic WireGuard configuration
struct WireguardConfiguration {
var privateKey: WireguardPrivateKey
var peers: [WireguardPeer]
+ var allowedIPs: [IPAddressRange]
}
extension WireguardConfiguration {
@@ -28,7 +27,10 @@ extension WireguardConfiguration {
peers.forEach { (peer) in
commands.append(.peer(peer))
- commands.append(.allowedIP(peer.anyAllowedIP))
+ }
+
+ allowedIPs.forEach { (ipAddressRange) in
+ commands.append(.allowedIP(ipAddressRange))
}
return commands
@@ -44,43 +46,34 @@ extension WireguardConfiguration {
let oldPeers = Set(self.peers)
let newPeers = Set(newConfig.peers)
+ let oldPublicKeys = Set(oldPeers.map { $0.publicKey })
+ let newPublicKeys = Set(newPeers.map { $0.publicKey })
+ let shouldReplacePeers = oldPublicKeys != newPublicKeys
if oldPeers != newPeers {
- let oldPublicKeys = Set(oldPeers.map { $0.publicKey })
- let newPublicKeys = Set(newPeers.map { $0.publicKey })
-
// Avoid using `replace_peers` when updating the existing peers.
- if oldPublicKeys != newPublicKeys {
+ if shouldReplacePeers {
commands.append(.replacePeers)
}
newPeers.forEach { (peer) in
commands.append(.peer(peer))
- commands.append(.allowedIP(peer.anyAllowedIP))
}
}
- return commands
- }
+ let oldAllowedIPs = Set(self.allowedIPs)
+ let newAllowedIPs = Set(newConfig.allowedIPs)
- func withReresolvedPeers(maxRetryOnFailure: Int = 0) -> AnyPublisher<WireguardConfiguration, Error> {
- self.peers
- .publisher
- .setFailureType(to: Error.self)
- .flatMap {
- $0.withReresolvedEndpoint()
- .publisher
- .retry(maxRetryOnFailure)
+ // It looks like the `allowed_ip` table is being flushed when `replace_peers=true` is passed
+ if oldAllowedIPs != newAllowedIPs || shouldReplacePeers {
+ commands.append(.replaceAllowedIPs)
+ newAllowedIPs.forEach { (allowedIP) in
+ commands.append(.allowedIP(allowedIP))
+ }
}
- .collect()
- .map({ (peers) in
- WireguardConfiguration(
- privateKey: self.privateKey,
- peers: peers
- )
- })
- .eraseToAnyPublisher()
+
+ return commands
}
}
diff --git a/ios/PacketTunnel/WireguardDevice.swift b/ios/PacketTunnel/WireguardDevice.swift
index efb62b526c..9dc8fe4dbb 100644
--- a/ios/PacketTunnel/WireguardDevice.swift
+++ b/ios/PacketTunnel/WireguardDevice.swift
@@ -33,8 +33,8 @@ class WireguardDevice {
/// A failure that indicates that Wireguard has already been started
case alreadyStarted
- /// A failure to resolve endpoints
- case resolveEndpoints(Swift.Error)
+ /// A failure to resolve an endpoint
+ case resolveEndpoint(AnyIPEndpoint, Swift.Error)
var localizedDescription: String {
switch self {
@@ -46,8 +46,8 @@ class WireguardDevice {
return "Wireguard has not been started yet"
case .alreadyStarted:
return "Wireguard has already been started"
- case .resolveEndpoints(let resolutionError):
- return "Failed to resolve endpoints: \(resolutionError.localizedDescription)"
+ case .resolveEndpoint(let endpoint, let error):
+ return "Failed to resolve the endpoint: \(endpoint). Error: \(error.localizedDescription)"
}
}
}
@@ -75,9 +75,6 @@ class WireguardDevice {
/// Network routes monitor
private var networkMonitor: NWPathMonitor?
- /// A subscriber used when resolving peer addresses
- private var peerResolutionSubscriber: AnyCancellable?
-
/// A tunnel device descriptor
private let tunFd: Int32
@@ -85,9 +82,12 @@ class WireguardDevice {
/// with the specific Wireguard tunnel.
private var wireguardHandle: Int32?
- /// An instance of `WireguardConfiguration`
+ /// Active configuration
private var configuration: WireguardConfiguration?
+ /// Active configuration with resolved endpoints
+ private var resolvedConfiguration: WireguardConfiguration?
+
/// Returns a Wireguard version
class var version: String {
String(cString: wgVersion())
@@ -135,31 +135,28 @@ class WireguardDevice {
// MARK: - Public methods
- func start(configuration: WireguardConfiguration) -> AnyPublisher<(), Error> {
- return Deferred {
- Future { (fulfill) in
+ func start(configuration: WireguardConfiguration) -> Future<(), Error> {
+ return Future { (fulfill) in
+ self.workQueue.async {
fulfill(self._start(configuration: configuration))
}
- }.subscribe(on: workQueue)
- .eraseToAnyPublisher()
+ }
}
- func stop() -> AnyPublisher<(), Error> {
- Deferred {
- Future { (fulfill) in
+ func stop() -> Future<(), Error> {
+ return Future { (fulfill) in
+ self.workQueue.async {
fulfill(self._stop())
}
- }.subscribe(on: workQueue)
- .eraseToAnyPublisher()
+ }
}
- func setConfig(configuration: WireguardConfiguration) -> AnyPublisher<(), Error> {
- Deferred {
- Future { (fulfill) in
+ func setConfig(configuration: WireguardConfiguration) -> Future<(), Error> {
+ return Future { (fulfill) in
+ self.workQueue.async {
fulfill(self._setConfig(configuration: configuration))
}
- }.subscribe(on: workQueue)
- .eraseToAnyPublisher()
+ }
}
func getInterfaceName() -> String? {
@@ -191,14 +188,18 @@ class WireguardDevice {
return .failure(.alreadyStarted)
}
- let handle = configuration.baseline().toRawWireguardConfigString()
+ let resolvedConfiguration = Self.resolveConfiguration(configuration)
+ let handle = resolvedConfiguration
+ .baseline()
+ .toRawWireguardConfigString()
.withCString { wgTurnOn($0, self.tunFd) }
if handle < 0 {
return .failure(.start(handle))
} else {
- wireguardHandle = handle
+ self.wireguardHandle = handle
self.configuration = configuration
+ self.resolvedConfiguration = resolvedConfiguration
startNetworkMonitor()
@@ -220,13 +221,17 @@ class WireguardDevice {
}
}
- private func _setConfig(configuration: WireguardConfiguration) -> Result<(), Error> {
- if let handle = wireguardHandle, let activeConfiguration = self.configuration {
- let wireguardCommands = activeConfiguration.transition(to: configuration)
+ private func _setConfig(configuration newConfiguration: WireguardConfiguration) -> Result<(), Error> {
+ if let handle = wireguardHandle,
+ let oldResolvedConfigration = self.resolvedConfiguration
+ {
+ let newResolvedConfiguration = Self.resolveConfiguration(newConfiguration)
+ let wireguardCommands = oldResolvedConfigration.transition(to: newResolvedConfiguration)
Self.setWireguardConfig(handle: handle, commands: wireguardCommands)
- self.configuration = configuration
+ self.configuration = newConfiguration
+ self.resolvedConfiguration = newResolvedConfiguration
return .success(())
} else {
@@ -242,6 +247,55 @@ class WireguardDevice {
.withCString { wgSetConfig(handle, $0) }
}
+ private class func resolveConfiguration(_ configuration: WireguardConfiguration)
+ -> WireguardConfiguration
+ {
+ return WireguardConfiguration(
+ privateKey: configuration.privateKey,
+ peers: resolvePeers(configuration.peers),
+ allowedIPs: configuration.allowedIPs
+ )
+ }
+
+ private class func resolvePeers(_ peers: [WireguardPeer]) -> [WireguardPeer] {
+ var newPeers = [WireguardPeer]()
+
+ for peer in peers {
+ switch self.resolvePeer(peer) {
+ case .success(let resolvedPeer):
+ newPeers.append(resolvedPeer)
+ case .failure(_):
+ // Fix me: Ignore resolution error and carry on with the last known peer
+ newPeers.append(peer)
+ }
+ }
+
+ return newPeers
+ }
+
+ private class func resolvePeer(_ peer: WireguardPeer) -> Result<WireguardPeer, Error> {
+ switch peer.withReresolvedEndpoint() {
+ case .success(let resolvedPeer):
+ if "\(peer.endpoint.ip)" == "\(resolvedPeer.endpoint.ip)" {
+ os_log(.debug, log: wireguardDeviceLog,
+ "DNS64: mapped %{public}s to itself", "\(resolvedPeer.endpoint.ip)")
+ } else {
+ os_log(.debug, log: wireguardDeviceLog,
+ "DNS64: mapped %{public}s to %{public}s",
+ "\(peer.endpoint.ip)", "\(resolvedPeer.endpoint.ip)")
+ }
+
+ return .success(resolvedPeer)
+
+ case .failure(let error):
+ os_log(.error, log: wireguardDeviceLog,
+ "Failed to re-resolve the peer: %{public}s. Error: %{public}s",
+ "\(peer.endpoint.ip)", error.localizedDescription)
+
+ return .failure(.resolveEndpoint(peer.endpoint, error))
+ }
+ }
+
// MARK: - Network monitoring
private func startNetworkMonitor() {
@@ -264,30 +318,16 @@ class WireguardDevice {
String(describing: path.status),
String(describing: path.availableInterfaces))
- // Re-resolve endpoints on network changes and update Wireguard configuration
- if let activeConfiguration = self.configuration {
- self.peerResolutionSubscriber = activeConfiguration
- .withReresolvedPeers(maxRetryOnFailure: 1)
- .mapError { WireguardDevice.Error.resolveEndpoints($0) }
- .sink(receiveCompletion: { (completion) in
- switch completion {
- case .finished:
- os_log(.debug, log: wireguardDeviceLog, "Re-resolved endpoints")
+ // Re-resolve endpoints on network changes
+ if let currentConfiguration = self.configuration,
+ let oldResolvedConfigration = self.resolvedConfiguration
+ {
+ let newResolvedConfiguration = Self.resolveConfiguration(currentConfiguration)
+ let commands = oldResolvedConfigration.transition(to: newResolvedConfiguration)
- case .failure(let error):
- os_log(.error, log: wireguardDeviceLog,
- "Failed to re-resolve endpoints: %{public}s",
- error.localizedDescription)
- }
- }, receiveValue: { (reresolvedConfiguration) in
- let commands = activeConfiguration
- .transition(to: reresolvedConfiguration)
+ Self.setWireguardConfig(handle: handle, commands: commands)
- Self.setWireguardConfig(
- handle: handle,
- commands: commands
- )
- })
+ self.resolvedConfiguration = newResolvedConfiguration
}
// Tell Wireguard to re-open sockets and bind them to the new network interface