2018-10-27 09:32:32 +00:00
|
|
|
// SPDX-License-Identifier: MIT
|
2019-01-02 00:56:33 +00:00
|
|
|
// Copyright © 2018-2019 WireGuard LLC. All Rights Reserved.
|
2018-10-27 09:32:32 +00:00
|
|
|
|
|
|
|
import Foundation
|
|
|
|
import Network
|
2018-11-08 10:14:13 +00:00
|
|
|
import NetworkExtension
|
2018-10-27 09:32:32 +00:00
|
|
|
|
2018-11-08 10:14:13 +00:00
|
|
|
class PacketTunnelSettingsGenerator {
|
|
|
|
let tunnelConfiguration: TunnelConfiguration
|
|
|
|
let resolvedEndpoints: [Endpoint?]
|
2018-10-27 09:32:32 +00:00
|
|
|
|
2018-11-08 10:14:13 +00:00
|
|
|
init(tunnelConfiguration: TunnelConfiguration, resolvedEndpoints: [Endpoint?]) {
|
|
|
|
self.tunnelConfiguration = tunnelConfiguration
|
|
|
|
self.resolvedEndpoints = resolvedEndpoints
|
|
|
|
}
|
2018-10-27 09:32:32 +00:00
|
|
|
|
2018-12-25 21:38:32 +00:00
|
|
|
func endpointUapiConfiguration() -> String {
|
2018-12-21 12:02:44 +00:00
|
|
|
var wgSettings = ""
|
2018-12-12 17:40:57 +00:00
|
|
|
for (index, peer) in tunnelConfiguration.peers.enumerated() {
|
2018-12-11 22:12:04 +00:00
|
|
|
wgSettings.append("public_key=\(peer.publicKey.hexEncodedString())\n")
|
Rework DNS and routes in network extension
The DNS resolver prior had useless comments, awful nesting, converted
bytes into strings and back into bytes, and generally made no sense.
That's been rewritten now.
But more fundumentally, this commit made the DNS resolver actually
accomplish its objective, by passing AI_ALL to it. It turns out, though,
that the Go library isn't actually using GAI in the way we need for
parsing IP addresses, so we actually need to do another round, this time
with hints flag as zero, so that we get the DNS64 address.
Additionally, since we're now binding sockets to interfaces, we can
entirely remove the excludedRoutes logic.
2018-12-28 18:34:31 +00:00
|
|
|
if let endpoint = resolvedEndpoints[index]?.withReresolvedIP() {
|
2018-12-11 22:12:04 +00:00
|
|
|
if case .name(_, _) = endpoint.host { assert(false, "Endpoint is not resolved") }
|
2018-12-21 04:52:45 +00:00
|
|
|
wgSettings.append("endpoint=\(endpoint.stringRepresentation)\n")
|
2018-12-11 22:12:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return wgSettings
|
|
|
|
}
|
2018-12-12 17:40:57 +00:00
|
|
|
|
2018-12-11 22:59:15 +00:00
|
|
|
func uapiConfiguration() -> String {
|
2018-10-27 09:32:32 +00:00
|
|
|
var wgSettings = ""
|
2018-11-08 10:14:13 +00:00
|
|
|
let privateKey = tunnelConfiguration.interface.privateKey.hexEncodedString()
|
2018-10-27 09:32:32 +00:00
|
|
|
wgSettings.append("private_key=\(privateKey)\n")
|
2018-11-08 10:14:13 +00:00
|
|
|
if let listenPort = tunnelConfiguration.interface.listenPort {
|
2018-10-27 09:32:32 +00:00
|
|
|
wgSettings.append("listen_port=\(listenPort)\n")
|
|
|
|
}
|
2018-12-20 17:22:37 +00:00
|
|
|
if !tunnelConfiguration.peers.isEmpty {
|
2018-10-27 09:32:32 +00:00
|
|
|
wgSettings.append("replace_peers=true\n")
|
|
|
|
}
|
2018-11-08 10:14:13 +00:00
|
|
|
assert(tunnelConfiguration.peers.count == resolvedEndpoints.count)
|
2018-12-12 17:40:57 +00:00
|
|
|
for (index, peer) in tunnelConfiguration.peers.enumerated() {
|
2018-10-27 09:32:32 +00:00
|
|
|
wgSettings.append("public_key=\(peer.publicKey.hexEncodedString())\n")
|
|
|
|
if let preSharedKey = peer.preSharedKey {
|
|
|
|
wgSettings.append("preshared_key=\(preSharedKey.hexEncodedString())\n")
|
|
|
|
}
|
Rework DNS and routes in network extension
The DNS resolver prior had useless comments, awful nesting, converted
bytes into strings and back into bytes, and generally made no sense.
That's been rewritten now.
But more fundumentally, this commit made the DNS resolver actually
accomplish its objective, by passing AI_ALL to it. It turns out, though,
that the Go library isn't actually using GAI in the way we need for
parsing IP addresses, so we actually need to do another round, this time
with hints flag as zero, so that we get the DNS64 address.
Additionally, since we're now binding sockets to interfaces, we can
entirely remove the excludedRoutes logic.
2018-12-28 18:34:31 +00:00
|
|
|
if let endpoint = resolvedEndpoints[index]?.withReresolvedIP() {
|
2018-10-27 09:32:32 +00:00
|
|
|
if case .name(_, _) = endpoint.host { assert(false, "Endpoint is not resolved") }
|
2018-12-21 04:52:45 +00:00
|
|
|
wgSettings.append("endpoint=\(endpoint.stringRepresentation)\n")
|
2018-10-27 09:32:32 +00:00
|
|
|
}
|
|
|
|
let persistentKeepAlive = peer.persistentKeepAlive ?? 0
|
|
|
|
wgSettings.append("persistent_keepalive_interval=\(persistentKeepAlive)\n")
|
2018-12-12 18:28:27 +00:00
|
|
|
if !peer.allowedIPs.isEmpty {
|
2018-10-27 09:32:32 +00:00
|
|
|
wgSettings.append("replace_allowed_ips=true\n")
|
2018-12-21 04:52:45 +00:00
|
|
|
peer.allowedIPs.forEach { wgSettings.append("allowed_ip=\($0.stringRepresentation)\n") }
|
2018-10-27 09:32:32 +00:00
|
|
|
}
|
|
|
|
}
|
2018-11-08 10:14:13 +00:00
|
|
|
return wgSettings
|
|
|
|
}
|
2018-10-27 09:32:32 +00:00
|
|
|
|
2018-11-08 10:14:13 +00:00
|
|
|
func generateNetworkSettings() -> NEPacketTunnelNetworkSettings {
|
2018-11-05 05:23:26 +00:00
|
|
|
/* iOS requires a tunnel endpoint, whereas in WireGuard it's valid for
|
|
|
|
* a tunnel to have no endpoint, or for there to be many endpoints, in
|
|
|
|
* which case, displaying a single one in settings doesn't really
|
|
|
|
* make sense. So, we fill it in with this placeholder, which is not
|
|
|
|
* a valid IP address that will actually route over the Internet.
|
|
|
|
*/
|
2019-01-08 00:51:12 +00:00
|
|
|
let networkSettings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: "127.0.0.1")
|
2018-12-13 04:26:04 +00:00
|
|
|
|
2018-12-21 04:52:45 +00:00
|
|
|
let dnsServerStrings = tunnelConfiguration.interface.dns.map { $0.stringRepresentation }
|
2018-11-23 07:34:55 +00:00
|
|
|
let dnsSettings = NEDNSSettings(servers: dnsServerStrings)
|
2018-12-13 06:50:10 +00:00
|
|
|
dnsSettings.matchDomains = [""] // All DNS queries must first go through the tunnel's DNS
|
2018-11-23 07:34:55 +00:00
|
|
|
networkSettings.dnsSettings = dnsSettings
|
2018-12-13 04:26:04 +00:00
|
|
|
|
2019-01-15 20:21:50 +00:00
|
|
|
let mtu = tunnelConfiguration.interface.mtu ?? 0
|
2019-01-03 18:24:30 +00:00
|
|
|
|
|
|
|
/* 0 means automatic MTU. In theory, we should just do
|
|
|
|
* `networkSettings.tunnelOverheadBytes = 80` but in
|
|
|
|
* practice there are too many broken networks out there.
|
|
|
|
* Instead set it to 1280. Boohoo. Maybe someday we'll
|
|
|
|
* add a nob, maybe, or iOS will do probing for us.
|
|
|
|
*/
|
2018-12-12 18:28:27 +00:00
|
|
|
if mtu == 0 {
|
2019-01-15 20:21:50 +00:00
|
|
|
#if os(iOS)
|
|
|
|
networkSettings.mtu = NSNumber(value: 1280)
|
|
|
|
#elseif os(OSX)
|
|
|
|
networkSettings.tunnelOverheadBytes = 80
|
|
|
|
#endif
|
|
|
|
} else {
|
|
|
|
networkSettings.mtu = NSNumber(value: mtu)
|
2018-11-08 10:14:13 +00:00
|
|
|
}
|
2018-12-13 04:26:04 +00:00
|
|
|
|
2018-12-13 03:09:52 +00:00
|
|
|
let (ipv4Routes, ipv6Routes) = routes()
|
|
|
|
let (ipv4IncludedRoutes, ipv6IncludedRoutes) = includedRoutes()
|
2018-12-13 04:26:04 +00:00
|
|
|
|
2018-12-13 03:09:52 +00:00
|
|
|
let ipv4Settings = NEIPv4Settings(addresses: ipv4Routes.map { $0.destinationAddress }, subnetMasks: ipv4Routes.map { $0.destinationSubnetMask })
|
|
|
|
ipv4Settings.includedRoutes = ipv4IncludedRoutes
|
|
|
|
networkSettings.ipv4Settings = ipv4Settings
|
2018-12-13 04:26:04 +00:00
|
|
|
|
2018-12-13 03:09:52 +00:00
|
|
|
let ipv6Settings = NEIPv6Settings(addresses: ipv6Routes.map { $0.destinationAddress }, networkPrefixLengths: ipv6Routes.map { $0.destinationNetworkPrefixLength })
|
|
|
|
ipv6Settings.includedRoutes = ipv6IncludedRoutes
|
|
|
|
networkSettings.ipv6Settings = ipv6Settings
|
2018-10-27 09:32:32 +00:00
|
|
|
|
2018-12-13 03:09:52 +00:00
|
|
|
return networkSettings
|
|
|
|
}
|
2018-10-27 09:32:32 +00:00
|
|
|
|
2018-12-13 03:09:52 +00:00
|
|
|
private func ipv4SubnetMaskString(of addressRange: IPAddressRange) -> String {
|
|
|
|
let length: UInt8 = addressRange.networkPrefixLength
|
|
|
|
assert(length <= 32)
|
|
|
|
var octets: [UInt8] = [0, 0, 0, 0]
|
|
|
|
let subnetMask: UInt32 = length > 0 ? ~UInt32(0) << (32 - length) : UInt32(0)
|
|
|
|
octets[0] = UInt8(truncatingIfNeeded: subnetMask >> 24)
|
|
|
|
octets[1] = UInt8(truncatingIfNeeded: subnetMask >> 16)
|
|
|
|
octets[2] = UInt8(truncatingIfNeeded: subnetMask >> 8)
|
|
|
|
octets[3] = UInt8(truncatingIfNeeded: subnetMask)
|
|
|
|
return octets.map { String($0) }.joined(separator: ".")
|
|
|
|
}
|
2018-12-13 04:26:04 +00:00
|
|
|
|
2018-12-13 03:09:52 +00:00
|
|
|
private func routes() -> ([NEIPv4Route], [NEIPv6Route]) {
|
|
|
|
var ipv4Routes = [NEIPv4Route]()
|
|
|
|
var ipv6Routes = [NEIPv6Route]()
|
2018-11-08 10:14:13 +00:00
|
|
|
for addressRange in tunnelConfiguration.interface.addresses {
|
2018-12-12 18:28:27 +00:00
|
|
|
if addressRange.address is IPv4Address {
|
2018-12-13 03:09:52 +00:00
|
|
|
ipv4Routes.append(NEIPv4Route(destinationAddress: "\(addressRange.address)", subnetMask: ipv4SubnetMaskString(of: addressRange)))
|
2018-12-12 18:28:27 +00:00
|
|
|
} else if addressRange.address is IPv6Address {
|
2018-12-13 03:09:52 +00:00
|
|
|
/* Big fat ugly hack for broken iOS networking stack: the smallest prefix that will have
|
|
|
|
* any effect on iOS is a /120, so we clamp everything above to /120. This is potentially
|
|
|
|
* very bad, if various network parameters were actually relying on that subnet being
|
|
|
|
* intentionally small. TODO: talk about this with upstream iOS devs.
|
|
|
|
*/
|
|
|
|
ipv6Routes.append(NEIPv6Route(destinationAddress: "\(addressRange.address)", networkPrefixLength: NSNumber(value: min(120, addressRange.networkPrefixLength))))
|
2018-10-27 09:32:32 +00:00
|
|
|
}
|
|
|
|
}
|
2018-12-13 03:09:52 +00:00
|
|
|
return (ipv4Routes, ipv6Routes)
|
|
|
|
}
|
2018-12-13 04:26:04 +00:00
|
|
|
|
2018-12-13 03:09:52 +00:00
|
|
|
private func includedRoutes() -> ([NEIPv4Route], [NEIPv6Route]) {
|
|
|
|
var ipv4IncludedRoutes = [NEIPv4Route]()
|
|
|
|
var ipv6IncludedRoutes = [NEIPv6Route]()
|
2018-11-08 10:14:13 +00:00
|
|
|
for peer in tunnelConfiguration.peers {
|
2018-10-27 09:32:32 +00:00
|
|
|
for addressRange in peer.allowedIPs {
|
2018-12-12 18:28:27 +00:00
|
|
|
if addressRange.address is IPv4Address {
|
2018-12-13 03:09:52 +00:00
|
|
|
ipv4IncludedRoutes.append(NEIPv4Route(destinationAddress: "\(addressRange.address)", subnetMask: ipv4SubnetMaskString(of: addressRange)))
|
2018-12-12 18:28:27 +00:00
|
|
|
} else if addressRange.address is IPv6Address {
|
2018-12-13 03:09:52 +00:00
|
|
|
ipv6IncludedRoutes.append(NEIPv6Route(destinationAddress: "\(addressRange.address)", networkPrefixLength: NSNumber(value: addressRange.networkPrefixLength)))
|
2018-10-27 09:32:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-12-13 03:09:52 +00:00
|
|
|
return (ipv4IncludedRoutes, ipv6IncludedRoutes)
|
|
|
|
}
|
2018-10-27 09:32:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private extension Data {
|
|
|
|
func hexEncodedString() -> String {
|
|
|
|
return self.map { String(format: "%02x", $0) }.joined()
|
|
|
|
}
|
|
|
|
}
|