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

View File

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

View File

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