aboutsummaryrefslogtreecommitdiffstats
path: root/Sources/WireGuardApp/Tunnel/ActivateOnDemandOption.swift
diff options
context:
space:
mode:
authorAndrej Mihajlov <and@mullvad.net>2020-12-02 12:27:39 +0100
committerAndrej Mihajlov <and@mullvad.net>2020-12-03 13:32:24 +0100
commitec574085703ea1c8b2d4538596961beb910c4382 (patch)
tree73cf8bbdb74fe5575606664bccd0232ffa911803 /Sources/WireGuardApp/Tunnel/ActivateOnDemandOption.swift
parentWireGuardKit: Assert that resolutionResults must not contain failures (diff)
downloadwireguard-apple-ec574085703ea1c8b2d4538596961beb910c4382.tar.xz
wireguard-apple-ec574085703ea1c8b2d4538596961beb910c4382.zip
Move all source files to `Sources/` and rename WireGuardKit targets
Signed-off-by: Andrej Mihajlov <and@mullvad.net>
Diffstat (limited to 'Sources/WireGuardApp/Tunnel/ActivateOnDemandOption.swift')
-rw-r--r--Sources/WireGuardApp/Tunnel/ActivateOnDemandOption.swift133
1 files changed, 133 insertions, 0 deletions
diff --git a/Sources/WireGuardApp/Tunnel/ActivateOnDemandOption.swift b/Sources/WireGuardApp/Tunnel/ActivateOnDemandOption.swift
new file mode 100644
index 0000000..d44e1d6
--- /dev/null
+++ b/Sources/WireGuardApp/Tunnel/ActivateOnDemandOption.swift
@@ -0,0 +1,133 @@
+// SPDX-License-Identifier: MIT
+// Copyright © 2018-2019 WireGuard LLC. All Rights Reserved.
+
+import NetworkExtension
+
+enum ActivateOnDemandOption: Equatable {
+ case off
+ case wiFiInterfaceOnly(ActivateOnDemandSSIDOption)
+ case nonWiFiInterfaceOnly
+ case anyInterface(ActivateOnDemandSSIDOption)
+}
+
+#if os(iOS)
+private let nonWiFiInterfaceType: NEOnDemandRuleInterfaceType = .cellular
+#elseif os(macOS)
+private let nonWiFiInterfaceType: NEOnDemandRuleInterfaceType = .ethernet
+#else
+#error("Unimplemented")
+#endif
+
+enum ActivateOnDemandSSIDOption: Equatable {
+ case anySSID
+ case onlySpecificSSIDs([String])
+ case exceptSpecificSSIDs([String])
+}
+
+extension ActivateOnDemandOption {
+ func apply(on tunnelProviderManager: NETunnelProviderManager) {
+ let rules: [NEOnDemandRule]?
+ switch self {
+ case .off:
+ rules = nil
+ case .wiFiInterfaceOnly(let ssidOption):
+ rules = ssidOnDemandRules(option: ssidOption) + [NEOnDemandRuleDisconnect(interfaceType: nonWiFiInterfaceType)]
+ case .nonWiFiInterfaceOnly:
+ 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
+ tunnelProviderManager.isOnDemandEnabled = self != .off
+ }
+
+ init(from tunnelProviderManager: NETunnelProviderManager) {
+ if tunnelProviderManager.isOnDemandEnabled, let onDemandRules = tunnelProviderManager.onDemandRules {
+ self = ActivateOnDemandOption.create(from: onDemandRules)
+ } else {
+ self = .off
+ }
+ }
+
+ private static func create(from rules: [NEOnDemandRule]) -> ActivateOnDemandOption {
+ switch rules.count {
+ case 0:
+ return .off
+ case 1:
+ let rule = rules[0]
+ guard rule.action == .connect else { return .off }
+ return .anyInterface(.anySSID)
+ case 2:
+ guard let connectRule = rules.first(where: { $0.action == .connect }) else {
+ wg_log(.error, message: "Unexpected onDemandRules set on tunnel provider manager: \(rules.count) rules found but no connect rule.")
+ return .off
+ }
+ guard let disconnectRule = rules.first(where: { $0.action == .disconnect }) else {
+ wg_log(.error, message: "Unexpected onDemandRules set on tunnel provider manager: \(rules.count) rules found but no disconnect rule.")
+ return .off
+ }
+ if connectRule.interfaceTypeMatch == .wiFi && disconnectRule.interfaceTypeMatch == nonWiFiInterfaceType {
+ return .wiFiInterfaceOnly(.anySSID)
+ } else if connectRule.interfaceTypeMatch == nonWiFiInterfaceType && disconnectRule.interfaceTypeMatch == .wiFi {
+ return .nonWiFiInterfaceOnly
+ } else {
+ wg_log(.error, message: "Unexpected onDemandRules set on tunnel provider manager: \(rules.count) rules found but interface types are inconsistent.")
+ return .off
+ }
+ case 3:
+ guard let ssidRule = rules.first(where: { $0.interfaceTypeMatch == .wiFi && $0.ssidMatch != nil }) else { return .off }
+ guard let nonWiFiRule = rules.first(where: { $0.interfaceTypeMatch == nonWiFiInterfaceType }) else { return .off }
+ let ssids = ssidRule.ssidMatch!
+ switch (ssidRule.action, nonWiFiRule.action) {
+ case (.connect, .connect):
+ return .anyInterface(.onlySpecificSSIDs(ssids))
+ case (.connect, .disconnect):
+ return .wiFiInterfaceOnly(.onlySpecificSSIDs(ssids))
+ case (.disconnect, .connect):
+ return .anyInterface(.exceptSpecificSSIDs(ssids))
+ case (.disconnect, .disconnect):
+ return .wiFiInterfaceOnly(.exceptSpecificSSIDs(ssids))
+ default:
+ wg_log(.error, message: "Unexpected onDemandRules set on tunnel provider manager: \(rules.count) rules found")
+ return .off
+ }
+ default:
+ wg_log(.error, message: "Unexpected number of onDemandRules set on tunnel provider manager: \(rules.count) rules found")
+ return .off
+ }
+ }
+}
+
+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)]
+ }
+}