on-demand: Infrastructure for supporting SSID-based rules

Signed-off-by: Roopesh Chander <roop@roopc.net>
This commit is contained in:
Roopesh Chander 2019-02-23 13:56:51 +05:30 committed by Jason A. Donenfeld
parent 3767a12983
commit 5d757982ba
4 changed files with 71 additions and 21 deletions

View File

@ -8,11 +8,11 @@ struct ActivateOnDemandSetting {
var activateOnDemandOption: ActivateOnDemandOption
}
enum ActivateOnDemandOption {
enum ActivateOnDemandOption: Equatable {
case none // Valid only when isActivateOnDemandEnabled is false
case wiFiInterfaceOnly
case wiFiInterfaceOnly(ActivateOnDemandSSIDOption)
case nonWiFiInterfaceOnly
case anyInterface
case anyInterface(ActivateOnDemandSSIDOption)
}
#if os(iOS)
@ -23,25 +23,29 @@ private let nonWiFiInterfaceType: NEOnDemandRuleInterfaceType = .ethernet
#error("Unimplemented")
#endif
enum ActivateOnDemandSSIDOption: Equatable {
case anySSID
case onlySpecificSSIDs([String])
case exceptSpecificSSIDs([String])
}
extension ActivateOnDemandSetting {
func apply(on tunnelProviderManager: NETunnelProviderManager) {
tunnelProviderManager.isOnDemandEnabled = isActivateOnDemandEnabled
let rules: [NEOnDemandRule]?
let connectRule = NEOnDemandRuleConnect()
let disconnectRule = NEOnDemandRuleDisconnect()
switch activateOnDemandOption {
case .none:
rules = nil
case .wiFiInterfaceOnly:
connectRule.interfaceTypeMatch = .wiFi
disconnectRule.interfaceTypeMatch = nonWiFiInterfaceType
rules = [connectRule, disconnectRule]
case .wiFiInterfaceOnly(let ssidOption):
rules = ssidOnDemandRules(option: ssidOption) + [NEOnDemandRuleDisconnect(interfaceType: nonWiFiInterfaceType)]
case .nonWiFiInterfaceOnly:
connectRule.interfaceTypeMatch = nonWiFiInterfaceType
disconnectRule.interfaceTypeMatch = .wiFi
rules = [connectRule, disconnectRule]
case .anyInterface:
rules = [connectRule]
rules = [NEOnDemandRuleConnect(interfaceType: nonWiFiInterfaceType), NEOnDemandRuleDisconnect(interfaceType: .wiFi)]
case .anyInterface(let ssidOption):
if case .anySSID = ssidOption {
rules = [NEOnDemandRuleConnect(interfaceType: .any)]
} else {
rules = ssidOnDemandRules(option: ssidOption) + [NEOnDemandRuleConnect(interfaceType: nonWiFiInterfaceType)]
}
}
tunnelProviderManager.onDemandRules = rules
}
@ -55,17 +59,33 @@ extension ActivateOnDemandSetting {
case 1:
let rule = rules[0]
precondition(rule.action == .connect)
activateOnDemandOption = .anyInterface
activateOnDemandOption = .anyInterface(.anySSID)
case 2:
let connectRule = rules.first(where: { $0.action == .connect })!
let disconnectRule = rules.first(where: { $0.action == .disconnect })!
if connectRule.interfaceTypeMatch == .wiFi && disconnectRule.interfaceTypeMatch == nonWiFiInterfaceType {
activateOnDemandOption = .wiFiInterfaceOnly
activateOnDemandOption = .wiFiInterfaceOnly(.anySSID)
} else if connectRule.interfaceTypeMatch == nonWiFiInterfaceType && disconnectRule.interfaceTypeMatch == .wiFi {
activateOnDemandOption = .nonWiFiInterfaceOnly
} else {
fatalError("Unexpected onDemandRules set on tunnel provider manager")
}
case 3:
let ssidRule = rules.first(where: { $0.interfaceTypeMatch == .wiFi && $0.ssidMatch != nil })!
let nonWiFiRule = rules.first(where: { $0.interfaceTypeMatch == nonWiFiInterfaceType })!
let ssids = ssidRule.ssidMatch!
switch (ssidRule.action, nonWiFiRule.action) {
case (.connect, .connect):
activateOnDemandOption = .anyInterface(.onlySpecificSSIDs(ssids))
case (.connect, .disconnect):
activateOnDemandOption = .wiFiInterfaceOnly(.onlySpecificSSIDs(ssids))
case (.disconnect, .connect):
activateOnDemandOption = .anyInterface(.exceptSpecificSSIDs(ssids))
case (.disconnect, .disconnect):
activateOnDemandOption = .wiFiInterfaceOnly(.exceptSpecificSSIDs(ssids))
default:
fatalError("Unexpected SSID onDemandRules set on tunnel provider manager")
}
default:
fatalError("Unexpected number of onDemandRules set on tunnel provider manager")
}
@ -82,3 +102,33 @@ extension ActivateOnDemandSetting {
extension ActivateOnDemandSetting {
static var defaultSetting = ActivateOnDemandSetting(isActivateOnDemandEnabled: false, activateOnDemandOption: .none)
}
private extension NEOnDemandRuleConnect {
convenience init(interfaceType: NEOnDemandRuleInterfaceType, ssids: [String]? = nil) {
self.init()
interfaceTypeMatch = interfaceType
ssidMatch = ssids
}
}
private extension NEOnDemandRuleDisconnect {
convenience init(interfaceType: NEOnDemandRuleInterfaceType, ssids: [String]? = nil) {
self.init()
interfaceTypeMatch = interfaceType
ssidMatch = ssids
}
}
private func ssidOnDemandRules(option: ActivateOnDemandSSIDOption) -> [NEOnDemandRule] {
switch option {
case .anySSID:
return [NEOnDemandRuleConnect(interfaceType: .wiFi)]
case .onlySpecificSSIDs(let ssids):
assert(!ssids.isEmpty)
return [NEOnDemandRuleConnect(interfaceType: .wiFi, ssids: ssids),
NEOnDemandRuleDisconnect(interfaceType: .wiFi)]
case .exceptSpecificSSIDs(let ssids):
return [NEOnDemandRuleDisconnect(interfaceType: .wiFi, ssids: ssids),
NEOnDemandRuleConnect(interfaceType: .wiFi)]
}
}

View File

@ -661,7 +661,7 @@ extension TunnelViewModel {
}
static func defaultActivateOnDemandOption() -> ActivateOnDemandOption {
return .anyInterface
return .anyInterface(.anySSID)
}
}

View File

@ -44,8 +44,8 @@ class TunnelEditTableViewController: UITableViewController {
]
let activateOnDemandOptions: [ActivateOnDemandOption] = [
.anyInterface,
.wiFiInterfaceOnly,
.anyInterface(.anySSID),
.wiFiInterfaceOnly(.anySSID),
.nonWiFiInterfaceOnly
]

View File

@ -82,8 +82,8 @@ class TunnelEditViewController: NSViewController {
let activateOnDemandOptions: [ActivateOnDemandOption] = [
.none,
.anyInterface,
.wiFiInterfaceOnly,
.anyInterface(.anySSID),
.wiFiInterfaceOnly(.anySSID),
.nonWiFiInterfaceOnly
]