Split IPv4/6 settings and routes (#298)
* Postpone setting route gateway Resolve in NetworkSettingsBuilder. * Store routes separately from IP*Settings Parse as optionals to avoid empty arrays. * Deprecate routes stored in IP*Settings * Apply routes from new fields * Update CHANGELOG
This commit is contained in:
parent
703d1416ad
commit
cae371bb40
|
@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- OpenVPN: Deal with remote options properly. [#297](https://github.com/passepartoutvpn/tunnelkit/pull/297)
|
- OpenVPN: Deal with remote options properly. [#297](https://github.com/passepartoutvpn/tunnelkit/pull/297)
|
||||||
|
- OpenVPN: Routes from configuration file are ignored. [#278](https://github.com/passepartoutvpn/tunnelkit/issues/278)
|
||||||
- IPv6 endpoints are parsed improperly. [#293](https://github.com/passepartoutvpn/tunnelkit/issues/293)
|
- IPv6 endpoints are parsed improperly. [#293](https://github.com/passepartoutvpn/tunnelkit/issues/293)
|
||||||
- Fix abandoned MockVPN. [#285](https://github.com/passepartoutvpn/tunnelkit/pull/285)
|
- Fix abandoned MockVPN. [#285](https://github.com/passepartoutvpn/tunnelkit/pull/285)
|
||||||
|
|
||||||
|
|
|
@ -37,10 +37,10 @@ public struct IPv4Settings: Codable, Equatable, CustomStringConvertible {
|
||||||
/// The address mask.
|
/// The address mask.
|
||||||
public let mask: String
|
public let mask: String
|
||||||
|
|
||||||
/// The address of the gateway (uses default gateway if not set).
|
/// The address of the gateway (falls back to global gateway).
|
||||||
public let gateway: String
|
public let gateway: String?
|
||||||
|
|
||||||
public init(_ destination: String, _ mask: String?, _ gateway: String) {
|
public init(_ destination: String, _ mask: String?, _ gateway: String?) {
|
||||||
self.destination = destination
|
self.destination = destination
|
||||||
self.mask = mask ?? "255.255.255.255"
|
self.mask = mask ?? "255.255.255.255"
|
||||||
self.gateway = gateway
|
self.gateway = gateway
|
||||||
|
@ -49,7 +49,7 @@ public struct IPv4Settings: Codable, Equatable, CustomStringConvertible {
|
||||||
// MARK: CustomStringConvertible
|
// MARK: CustomStringConvertible
|
||||||
|
|
||||||
public var description: String {
|
public var description: String {
|
||||||
"{\(destination.maskedDescription)/\(mask) \(gateway.maskedDescription)}"
|
"{\(destination.maskedDescription)/\(mask) \(gateway?.maskedDescription ?? "*")}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,8 +63,17 @@ public struct IPv4Settings: Codable, Equatable, CustomStringConvertible {
|
||||||
public let defaultGateway: String
|
public let defaultGateway: String
|
||||||
|
|
||||||
/// The additional routes.
|
/// The additional routes.
|
||||||
|
@available(*, deprecated, message: "Store routes separately")
|
||||||
public let routes: [Route]
|
public let routes: [Route]
|
||||||
|
|
||||||
|
public init(address: String, addressMask: String, defaultGateway: String) {
|
||||||
|
self.address = address
|
||||||
|
self.addressMask = addressMask
|
||||||
|
self.defaultGateway = defaultGateway
|
||||||
|
self.routes = []
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(*, deprecated, message: "Store routes separately")
|
||||||
public init(address: String, addressMask: String, defaultGateway: String, routes: [Route]) {
|
public init(address: String, addressMask: String, defaultGateway: String, routes: [Route]) {
|
||||||
self.address = address
|
self.address = address
|
||||||
self.addressMask = addressMask
|
self.addressMask = addressMask
|
||||||
|
@ -75,6 +84,6 @@ public struct IPv4Settings: Codable, Equatable, CustomStringConvertible {
|
||||||
// MARK: CustomStringConvertible
|
// MARK: CustomStringConvertible
|
||||||
|
|
||||||
public var description: String {
|
public var description: String {
|
||||||
"addr \(address.maskedDescription) netmask \(addressMask) gw \(defaultGateway.maskedDescription) routes \(routes.map(\.maskedDescription))"
|
"addr \(address.maskedDescription) netmask \(addressMask) gw \(defaultGateway.maskedDescription)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,10 +37,10 @@ public struct IPv6Settings: Codable, Equatable, CustomStringConvertible {
|
||||||
/// The address prefix length.
|
/// The address prefix length.
|
||||||
public let prefixLength: UInt8
|
public let prefixLength: UInt8
|
||||||
|
|
||||||
/// The address of the gateway (uses default gateway if not set).
|
/// The address of the gateway (falls back to global gateway).
|
||||||
public let gateway: String
|
public let gateway: String?
|
||||||
|
|
||||||
public init(_ destination: String, _ prefixLength: UInt8?, _ gateway: String) {
|
public init(_ destination: String, _ prefixLength: UInt8?, _ gateway: String?) {
|
||||||
self.destination = destination
|
self.destination = destination
|
||||||
self.prefixLength = prefixLength ?? 3
|
self.prefixLength = prefixLength ?? 3
|
||||||
self.gateway = gateway
|
self.gateway = gateway
|
||||||
|
@ -49,7 +49,7 @@ public struct IPv6Settings: Codable, Equatable, CustomStringConvertible {
|
||||||
// MARK: CustomStringConvertible
|
// MARK: CustomStringConvertible
|
||||||
|
|
||||||
public var description: String {
|
public var description: String {
|
||||||
"{\(destination.maskedDescription)/\(prefixLength) \(gateway.maskedDescription)}"
|
"{\(destination.maskedDescription)/\(prefixLength) \(gateway?.maskedDescription ?? "*")}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,8 +63,17 @@ public struct IPv6Settings: Codable, Equatable, CustomStringConvertible {
|
||||||
public let defaultGateway: String
|
public let defaultGateway: String
|
||||||
|
|
||||||
/// The additional routes.
|
/// The additional routes.
|
||||||
|
@available(*, deprecated, message: "Store routes separately")
|
||||||
public let routes: [Route]
|
public let routes: [Route]
|
||||||
|
|
||||||
|
public init(address: String, addressPrefixLength: UInt8, defaultGateway: String) {
|
||||||
|
self.address = address
|
||||||
|
self.addressPrefixLength = addressPrefixLength
|
||||||
|
self.defaultGateway = defaultGateway
|
||||||
|
self.routes = []
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(*, deprecated, message: "Store routes separately")
|
||||||
public init(address: String, addressPrefixLength: UInt8, defaultGateway: String, routes: [Route]) {
|
public init(address: String, addressPrefixLength: UInt8, defaultGateway: String, routes: [Route]) {
|
||||||
self.address = address
|
self.address = address
|
||||||
self.addressPrefixLength = addressPrefixLength
|
self.addressPrefixLength = addressPrefixLength
|
||||||
|
@ -75,6 +84,6 @@ public struct IPv6Settings: Codable, Equatable, CustomStringConvertible {
|
||||||
// MARK: CustomStringConvertible
|
// MARK: CustomStringConvertible
|
||||||
|
|
||||||
public var description: String {
|
public var description: String {
|
||||||
"addr \(address.maskedDescription)/\(addressPrefixLength) gw \(defaultGateway.maskedDescription) routes \(routes.map(\.maskedDescription))"
|
"addr \(address.maskedDescription)/\(addressPrefixLength) gw \(defaultGateway.maskedDescription)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,19 +104,17 @@ extension NetworkSettingsBuilder {
|
||||||
routingPolicies?.contains(.IPv6) ?? false
|
routingPolicies?.contains(.IPv6) ?? false
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: local routes are empty, localOptions.ipv4 is always nil (#278)
|
|
||||||
private var allRoutes4: [IPv4Settings.Route] {
|
private var allRoutes4: [IPv4Settings.Route] {
|
||||||
var routes = localOptions.ipv4?.routes ?? []
|
var routes = localOptions.routes4 ?? []
|
||||||
if pullRoutes, let remoteRoutes = remoteOptions.ipv4?.routes {
|
if pullRoutes, let remoteRoutes = remoteOptions.routes4 {
|
||||||
routes.append(contentsOf: remoteRoutes)
|
routes.append(contentsOf: remoteRoutes)
|
||||||
}
|
}
|
||||||
return routes
|
return routes
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: local routes are empty, localOptions.ipv6 is always nil (#278)
|
|
||||||
private var allRoutes6: [IPv6Settings.Route] {
|
private var allRoutes6: [IPv6Settings.Route] {
|
||||||
var routes = localOptions.ipv6?.routes ?? []
|
var routes = localOptions.routes6 ?? []
|
||||||
if pullRoutes, let remoteRoutes = remoteOptions.ipv6?.routes {
|
if pullRoutes, let remoteRoutes = remoteOptions.routes6 {
|
||||||
routes.append(contentsOf: remoteRoutes)
|
routes.append(contentsOf: remoteRoutes)
|
||||||
}
|
}
|
||||||
return routes
|
return routes
|
||||||
|
@ -169,9 +167,10 @@ extension NetworkSettingsBuilder {
|
||||||
|
|
||||||
for r in allRoutes4 {
|
for r in allRoutes4 {
|
||||||
let ipv4Route = NEIPv4Route(destinationAddress: r.destination, subnetMask: r.mask)
|
let ipv4Route = NEIPv4Route(destinationAddress: r.destination, subnetMask: r.mask)
|
||||||
ipv4Route.gatewayAddress = r.gateway
|
let gw = r.gateway ?? ipv4.defaultGateway
|
||||||
|
ipv4Route.gatewayAddress = gw
|
||||||
neRoutes.append(ipv4Route)
|
neRoutes.append(ipv4Route)
|
||||||
log.info("Routing.IPv4: Adding route \(r.destination)/\(r.mask) -> \(r.gateway)")
|
log.info("Routing.IPv4: Adding route \(r.destination)/\(r.mask) -> \(gw)")
|
||||||
}
|
}
|
||||||
|
|
||||||
ipv4Settings.includedRoutes = neRoutes
|
ipv4Settings.includedRoutes = neRoutes
|
||||||
|
@ -196,9 +195,10 @@ extension NetworkSettingsBuilder {
|
||||||
|
|
||||||
for r in allRoutes6 {
|
for r in allRoutes6 {
|
||||||
let ipv6Route = NEIPv6Route(destinationAddress: r.destination, networkPrefixLength: r.prefixLength as NSNumber)
|
let ipv6Route = NEIPv6Route(destinationAddress: r.destination, networkPrefixLength: r.prefixLength as NSNumber)
|
||||||
ipv6Route.gatewayAddress = r.gateway
|
let gw = r.gateway ?? ipv6.defaultGateway
|
||||||
|
ipv6Route.gatewayAddress = gw
|
||||||
neRoutes.append(ipv6Route)
|
neRoutes.append(ipv6Route)
|
||||||
log.info("Routing.IPv6: Adding route \(r.destination)/\(r.prefixLength) -> \(r.gateway)")
|
log.info("Routing.IPv6: Adding route \(r.destination)/\(r.prefixLength) -> \(gw)")
|
||||||
}
|
}
|
||||||
|
|
||||||
ipv6Settings.includedRoutes = neRoutes
|
ipv6Settings.includedRoutes = neRoutes
|
||||||
|
|
|
@ -257,6 +257,12 @@ extension OpenVPN {
|
||||||
/// The settings for IPv6. `OpenVPNSession` only evaluates this server-side.
|
/// The settings for IPv6. `OpenVPNSession` only evaluates this server-side.
|
||||||
public var ipv6: IPv6Settings?
|
public var ipv6: IPv6Settings?
|
||||||
|
|
||||||
|
/// The IPv4 routes if `ipv4` is nil.
|
||||||
|
public var routes4: [IPv4Settings.Route]?
|
||||||
|
|
||||||
|
/// The IPv6 routes if `ipv6` is nil.
|
||||||
|
public var routes6: [IPv6Settings.Route]?
|
||||||
|
|
||||||
/// Set false to ignore DNS settings, even when pushed.
|
/// Set false to ignore DNS settings, even when pushed.
|
||||||
public var isDNSEnabled: Bool?
|
public var isDNSEnabled: Bool?
|
||||||
|
|
||||||
|
@ -356,6 +362,8 @@ extension OpenVPN {
|
||||||
peerId: peerId,
|
peerId: peerId,
|
||||||
ipv4: ipv4,
|
ipv4: ipv4,
|
||||||
ipv6: ipv6,
|
ipv6: ipv6,
|
||||||
|
routes4: routes4,
|
||||||
|
routes6: routes6,
|
||||||
isDNSEnabled: isDNSEnabled,
|
isDNSEnabled: isDNSEnabled,
|
||||||
dnsProtocol: dnsProtocol,
|
dnsProtocol: dnsProtocol,
|
||||||
dnsServers: dnsServers,
|
dnsServers: dnsServers,
|
||||||
|
@ -468,6 +476,12 @@ extension OpenVPN {
|
||||||
/// - Seealso: `ConfigurationBuilder.ipv6`
|
/// - Seealso: `ConfigurationBuilder.ipv6`
|
||||||
public let ipv6: IPv6Settings?
|
public let ipv6: IPv6Settings?
|
||||||
|
|
||||||
|
/// - Seealso: `ConfigurationBuilder.routes4`
|
||||||
|
public let routes4: [IPv4Settings.Route]?
|
||||||
|
|
||||||
|
/// - Seealso: `ConfigurationBuilder.routes6`
|
||||||
|
public let routes6: [IPv6Settings.Route]?
|
||||||
|
|
||||||
/// - Seealso: `ConfigurationBuilder.isDNSEnabled`
|
/// - Seealso: `ConfigurationBuilder.isDNSEnabled`
|
||||||
public let isDNSEnabled: Bool?
|
public let isDNSEnabled: Bool?
|
||||||
|
|
||||||
|
@ -597,6 +611,8 @@ extension OpenVPN.Configuration {
|
||||||
builder.peerId = peerId
|
builder.peerId = peerId
|
||||||
builder.ipv4 = ipv4
|
builder.ipv4 = ipv4
|
||||||
builder.ipv6 = ipv6
|
builder.ipv6 = ipv6
|
||||||
|
builder.routes4 = routes4
|
||||||
|
builder.routes6 = routes6
|
||||||
builder.isDNSEnabled = isDNSEnabled
|
builder.isDNSEnabled = isDNSEnabled
|
||||||
builder.dnsProtocol = dnsProtocol
|
builder.dnsProtocol = dnsProtocol
|
||||||
builder.dnsServers = dnsServers
|
builder.dnsServers = dnsServers
|
||||||
|
@ -631,6 +647,12 @@ extension OpenVPN.Configuration {
|
||||||
log.info("\tIPv4: \(ipv4?.description ?? "not configured")")
|
log.info("\tIPv4: \(ipv4?.description ?? "not configured")")
|
||||||
log.info("\tIPv6: \(ipv6?.description ?? "not configured")")
|
log.info("\tIPv6: \(ipv6?.description ?? "not configured")")
|
||||||
}
|
}
|
||||||
|
if let routes = routes4 {
|
||||||
|
log.info("\tRoutes (IPv4): \(routes)")
|
||||||
|
}
|
||||||
|
if let routes = routes6 {
|
||||||
|
log.info("\tRoutes (IPv6): \(routes)")
|
||||||
|
}
|
||||||
|
|
||||||
if let cipher = cipher {
|
if let cipher = cipher {
|
||||||
log.info("\tCipher: \(cipher)")
|
log.info("\tCipher: \(cipher)")
|
||||||
|
|
|
@ -284,8 +284,8 @@ extension OpenVPN {
|
||||||
var optIfconfig4Arguments: [String]?
|
var optIfconfig4Arguments: [String]?
|
||||||
var optIfconfig6Arguments: [String]?
|
var optIfconfig6Arguments: [String]?
|
||||||
var optGateway4Arguments: [String]?
|
var optGateway4Arguments: [String]?
|
||||||
var optRoutes4: [(String, String, String?)] = [] // address, netmask, gateway
|
var optRoutes4: [(String, String, String?)]? // address, netmask, gateway
|
||||||
var optRoutes6: [(String, UInt8, String?)] = [] // destination, prefix, gateway
|
var optRoutes6: [(String, UInt8, String?)]? // destination, prefix, gateway
|
||||||
var optDNSServers: [String]?
|
var optDNSServers: [String]?
|
||||||
var optSearchDomains: [String]?
|
var optSearchDomains: [String]?
|
||||||
var optHTTPProxy: Proxy?
|
var optHTTPProxy: Proxy?
|
||||||
|
@ -619,7 +619,10 @@ extension OpenVPN {
|
||||||
if gateway == "vpn_gateway" {
|
if gateway == "vpn_gateway" {
|
||||||
gateway = nil
|
gateway = nil
|
||||||
}
|
}
|
||||||
optRoutes4.append((address, mask, gateway))
|
if optRoutes4 == nil {
|
||||||
|
optRoutes4 = []
|
||||||
|
}
|
||||||
|
optRoutes4?.append((address, mask, gateway))
|
||||||
}
|
}
|
||||||
Regex.route6.enumerateSpacedArguments(in: line) {
|
Regex.route6.enumerateSpacedArguments(in: line) {
|
||||||
let routeEntryArguments = $0
|
let routeEntryArguments = $0
|
||||||
|
@ -637,7 +640,10 @@ extension OpenVPN {
|
||||||
if gateway == "vpn_gateway" {
|
if gateway == "vpn_gateway" {
|
||||||
gateway = nil
|
gateway = nil
|
||||||
}
|
}
|
||||||
optRoutes6.append((destination, prefix, gateway))
|
if optRoutes6 == nil {
|
||||||
|
optRoutes6 = []
|
||||||
|
}
|
||||||
|
optRoutes6?.append((destination, prefix, gateway))
|
||||||
}
|
}
|
||||||
Regex.gateway.enumerateSpacedArguments(in: line) {
|
Regex.gateway.enumerateSpacedArguments(in: line) {
|
||||||
optGateway4Arguments = $0
|
optGateway4Arguments = $0
|
||||||
|
@ -726,6 +732,12 @@ extension OpenVPN {
|
||||||
// MARK: Post-processing
|
// MARK: Post-processing
|
||||||
|
|
||||||
// ensure that non-nil network settings also imply non-empty
|
// ensure that non-nil network settings also imply non-empty
|
||||||
|
if let array = optRoutes4 {
|
||||||
|
assert(!array.isEmpty)
|
||||||
|
}
|
||||||
|
if let array = optRoutes6 {
|
||||||
|
assert(!array.isEmpty)
|
||||||
|
}
|
||||||
if let array = optDNSServers {
|
if let array = optDNSServers {
|
||||||
assert(!array.isEmpty)
|
assert(!array.isEmpty)
|
||||||
}
|
}
|
||||||
|
@ -857,17 +869,16 @@ extension OpenVPN {
|
||||||
addressMask4 = "255.255.255.255"
|
addressMask4 = "255.255.255.255"
|
||||||
defaultGateway4 = ifconfig4Arguments[1]
|
defaultGateway4 = ifconfig4Arguments[1]
|
||||||
}
|
}
|
||||||
let routes4 = optRoutes4.map {
|
|
||||||
IPv4Settings.Route($0.0, $0.1, $0.2 ?? defaultGateway4)
|
|
||||||
}
|
|
||||||
|
|
||||||
sessionBuilder.ipv4 = IPv4Settings(
|
sessionBuilder.ipv4 = IPv4Settings(
|
||||||
address: address4,
|
address: address4,
|
||||||
addressMask: addressMask4,
|
addressMask: addressMask4,
|
||||||
defaultGateway: defaultGateway4,
|
defaultGateway: defaultGateway4
|
||||||
routes: routes4
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
sessionBuilder.routes4 = optRoutes4?.map {
|
||||||
|
IPv4Settings.Route($0.0, $0.1, $0.2)
|
||||||
|
}
|
||||||
|
|
||||||
if let ifconfig6Arguments = optIfconfig6Arguments {
|
if let ifconfig6Arguments = optIfconfig6Arguments {
|
||||||
guard ifconfig6Arguments.count == 2 else {
|
guard ifconfig6Arguments.count == 2 else {
|
||||||
|
@ -883,17 +894,16 @@ extension OpenVPN {
|
||||||
|
|
||||||
let address6 = address6Components[0]
|
let address6 = address6Components[0]
|
||||||
let defaultGateway6 = ifconfig6Arguments[1]
|
let defaultGateway6 = ifconfig6Arguments[1]
|
||||||
let routes6 = optRoutes6.map {
|
|
||||||
IPv6Settings.Route($0.0, $0.1, $0.2 ?? defaultGateway6)
|
|
||||||
}
|
|
||||||
|
|
||||||
sessionBuilder.ipv6 = IPv6Settings(
|
sessionBuilder.ipv6 = IPv6Settings(
|
||||||
address: address6,
|
address: address6,
|
||||||
addressPrefixLength: addressPrefix6,
|
addressPrefixLength: addressPrefix6,
|
||||||
defaultGateway: defaultGateway6,
|
defaultGateway: defaultGateway6
|
||||||
routes: routes6
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
sessionBuilder.routes6 = optRoutes6?.map {
|
||||||
|
IPv6Settings.Route($0.0, $0.1, $0.2)
|
||||||
|
}
|
||||||
|
|
||||||
sessionBuilder.dnsServers = optDNSServers
|
sessionBuilder.dnsServers = optDNSServers
|
||||||
sessionBuilder.searchDomains = optSearchDomains
|
sessionBuilder.searchDomains = optSearchDomains
|
||||||
|
|
|
@ -76,7 +76,7 @@ class PushTests: XCTestCase {
|
||||||
let reply = try! OpenVPN.PushReply(message: msg)!
|
let reply = try! OpenVPN.PushReply(message: msg)!
|
||||||
reply.debug()
|
reply.debug()
|
||||||
|
|
||||||
let route = reply.options.ipv4!.routes.first!
|
let route = reply.options.routes4!.first!
|
||||||
|
|
||||||
XCTAssertEqual(route.destination, "192.168.0.0")
|
XCTAssertEqual(route.destination, "192.168.0.0")
|
||||||
XCTAssertEqual(route.mask, "255.255.255.0")
|
XCTAssertEqual(route.mask, "255.255.255.0")
|
||||||
|
|
Loading…
Reference in New Issue