summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorBug Magnet <marco.nikic@mullvad.net>2024-02-06 10:12:31 +0100
committerBug Magnet <marco.nikic@mullvad.net>2024-02-06 16:49:45 +0100
commit8e8e3117faca6fcd0abeb9975d7e81ffcbd7a8f9 (patch)
tree0f52695b2d8dc9fb566cb12e52d10919e4f13295
parentf565a3d10559febe24f436f55c71aa1363e2f42a (diff)
downloadmullvadvpn-8e8e3117faca6fcd0abeb9975d7e81ffcbd7a8f9.tar.xz
mullvadvpn-8e8e3117faca6fcd0abeb9975d7e81ffcbd7a8f9.zip
Get rid of swiftlint warnings and move files around
-rw-r--r--ios/MullvadVPN.xcodeproj/project.pbxproj8
-rw-r--r--ios/MullvadVPN/View controllers/RedeemVoucher/RedeemVoucherViewController.swift3
-rw-r--r--ios/PacketTunnelCore/TunnelMonitor/PingStats.swift21
-rw-r--r--ios/PacketTunnelCore/TunnelMonitor/TunnelMonitor.swift200
-rw-r--r--ios/PacketTunnelCore/TunnelMonitor/TunnelMonitorState.swift192
5 files changed, 224 insertions, 200 deletions
diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj
index 7d4cd6e81c..075309bfee 100644
--- a/ios/MullvadVPN.xcodeproj/project.pbxproj
+++ b/ios/MullvadVPN.xcodeproj/project.pbxproj
@@ -616,6 +616,8 @@
A935594C2B4C2DA900D5D524 /* APIAvailabilityTestRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = A935594B2B4C2DA900D5D524 /* APIAvailabilityTestRequest.swift */; };
A94D691A2ABAD66700413DD4 /* WireGuardKitTypes in Frameworks */ = {isa = PBXBuildFile; productRef = 58FE25E22AA72AE9003D1918 /* WireGuardKitTypes */; };
A94D691B2ABAD66700413DD4 /* WireGuardKitTypes in Frameworks */ = {isa = PBXBuildFile; productRef = 58FE25E72AA7399D003D1918 /* WireGuardKitTypes */; };
+ A95EEE362B722CD600A8A39B /* TunnelMonitorState.swift in Sources */ = {isa = PBXBuildFile; fileRef = A95EEE352B722CD600A8A39B /* TunnelMonitorState.swift */; };
+ A95EEE382B722DFC00A8A39B /* PingStats.swift in Sources */ = {isa = PBXBuildFile; fileRef = A95EEE372B722DFC00A8A39B /* PingStats.swift */; };
A970C89D2B29E38C000A7684 /* Socks5UsernamePasswordCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = A970C89C2B29E38C000A7684 /* Socks5UsernamePasswordCommand.swift */; };
A97D25AE2B0BB18100946B2D /* ProtocolObfuscator.swift in Sources */ = {isa = PBXBuildFile; fileRef = A97D25AD2B0BB18100946B2D /* ProtocolObfuscator.swift */; };
A97D25B02B0BB5C400946B2D /* ProtocolObfuscationStub.swift in Sources */ = {isa = PBXBuildFile; fileRef = A97D25AF2B0BB5C400946B2D /* ProtocolObfuscationStub.swift */; };
@@ -1779,6 +1781,8 @@
A935594B2B4C2DA900D5D524 /* APIAvailabilityTestRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APIAvailabilityTestRequest.swift; sourceTree = "<group>"; };
A935594D2B4E919F00D5D524 /* Api.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Api.xcconfig; sourceTree = "<group>"; };
A9467E7E2A29DEFE000DC21F /* RelayCacheTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayCacheTests.swift; sourceTree = "<group>"; };
+ A95EEE352B722CD600A8A39B /* TunnelMonitorState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelMonitorState.swift; sourceTree = "<group>"; };
+ A95EEE372B722DFC00A8A39B /* PingStats.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PingStats.swift; sourceTree = "<group>"; };
A970C89C2B29E38C000A7684 /* Socks5UsernamePasswordCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Socks5UsernamePasswordCommand.swift; sourceTree = "<group>"; };
A97D25AD2B0BB18100946B2D /* ProtocolObfuscator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProtocolObfuscator.swift; sourceTree = "<group>"; };
A97D25AF2B0BB5C400946B2D /* ProtocolObfuscationStub.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProtocolObfuscationStub.swift; sourceTree = "<group>"; };
@@ -3116,9 +3120,11 @@
isa = PBXGroup;
children = (
58225D252A84E8A10083D7F1 /* DefaultPathObserverProtocol.swift */,
+ A95EEE372B722DFC00A8A39B /* PingStats.swift */,
582403162A821FD700163DE8 /* TunnelDeviceInfoProtocol.swift */,
58FC040927B3EE03001C21F0 /* TunnelMonitor.swift */,
58C7A42C2A85067A0060C66F /* TunnelMonitorProtocol.swift */,
+ A95EEE352B722CD600A8A39B /* TunnelMonitorState.swift */,
7A6B4F582AB8412E00123853 /* TunnelMonitorTimings.swift */,
58A3BDAF28A1821A00C8C2C6 /* WgStats.swift */,
);
@@ -4658,6 +4664,7 @@
58C7A4592A863FB90060C66F /* WgStats.swift in Sources */,
7AD0AA1F2AD6C8B900119E10 /* URLRequestProxyProtocol.swift in Sources */,
7A6B4F592AB8412E00123853 /* TunnelMonitorTimings.swift in Sources */,
+ A95EEE362B722CD600A8A39B /* TunnelMonitorState.swift in Sources */,
58FE25DB2AA72A8F003D1918 /* StartOptions.swift in Sources */,
A97D25AE2B0BB18100946B2D /* ProtocolObfuscator.swift in Sources */,
583832212AC3174700EA2071 /* PacketTunnelActor+NetworkReachability.swift in Sources */,
@@ -4670,6 +4677,7 @@
5838322B2AC3EF9600EA2071 /* CommandChannel.swift in Sources */,
586C145A2AC4735F00245C01 /* PacketTunnelActor+Public.swift in Sources */,
58342C042AAB61FB003BA12D /* State+Extensions.swift in Sources */,
+ A95EEE382B722DFC00A8A39B /* PingStats.swift in Sources */,
583832272AC3193600EA2071 /* PacketTunnelActor+SleepCycle.swift in Sources */,
58FE25DC2AA72A8F003D1918 /* AnyTask.swift in Sources */,
58FE25D92AA72A8F003D1918 /* AutoCancellingTask.swift in Sources */,
diff --git a/ios/MullvadVPN/View controllers/RedeemVoucher/RedeemVoucherViewController.swift b/ios/MullvadVPN/View controllers/RedeemVoucher/RedeemVoucherViewController.swift
index d88cc1e4ca..e1fdc52f16 100644
--- a/ios/MullvadVPN/View controllers/RedeemVoucher/RedeemVoucherViewController.swift
+++ b/ios/MullvadVPN/View controllers/RedeemVoucher/RedeemVoucherViewController.swift
@@ -138,8 +138,7 @@ class RedeemVoucherViewController: UIViewController, UINavigationControllerDeleg
contentView.state = .logout
- Task {
- [weak self] in
+ Task { [weak self] in
guard let self else { return }
await interactor.logout()
contentView.state = .initial
diff --git a/ios/PacketTunnelCore/TunnelMonitor/PingStats.swift b/ios/PacketTunnelCore/TunnelMonitor/PingStats.swift
new file mode 100644
index 0000000000..92054665f9
--- /dev/null
+++ b/ios/PacketTunnelCore/TunnelMonitor/PingStats.swift
@@ -0,0 +1,21 @@
+//
+// PingStats.swift
+// PacketTunnelCore
+//
+// Created by Marco Nikic on 2024-02-06.
+// Copyright © 2024 Mullvad VPN AB. All rights reserved.
+//
+
+import Foundation
+
+/// Ping statistics.
+struct PingStats {
+ /// Dictionary holding sequence and corresponding date when echo request took place.
+ var requests = [UInt16: Date]()
+
+ /// Timestamp when last echo request was sent.
+ var lastRequestDate: Date?
+
+ /// Timestamp when last echo reply was received.
+ var lastReplyDate: Date?
+}
diff --git a/ios/PacketTunnelCore/TunnelMonitor/TunnelMonitor.swift b/ios/PacketTunnelCore/TunnelMonitor/TunnelMonitor.swift
index 55facd9048..40adf180ce 100644
--- a/ios/PacketTunnelCore/TunnelMonitor/TunnelMonitor.swift
+++ b/ios/PacketTunnelCore/TunnelMonitor/TunnelMonitor.swift
@@ -14,188 +14,6 @@ import NetworkExtension
/// Tunnel monitor.
public final class TunnelMonitor: TunnelMonitorProtocol {
- /// Connection state.
- private enum ConnectionState {
- /// Initialized and doing nothing.
- case stopped
-
- /// Preparing to start.
- /// Intermediate state before receiving the first path update.
- case pendingStart
-
- /// Establishing connection.
- case connecting
-
- /// Connection is established.
- case connected
-
- /// Delegate is recovering connection.
- /// Delegate has to call `start(probeAddress:)` to complete recovery and resume monitoring.
- case recovering
-
- /// Waiting for network connectivity.
- case waitingConnectivity
- }
-
- /// Tunnel monitor state.
- private struct State {
- /// Current connection state.
- var connectionState: ConnectionState = .stopped
-
- /// Network counters.
- var netStats = WgStats()
-
- /// Ping stats.
- var pingStats = PingStats()
-
- /// Reference date used to determine if timeout has occurred.
- var timeoutReference = Date()
-
- /// Last seen change in rx counter.
- var lastSeenRx: Date?
-
- /// Last seen change in tx counter.
- var lastSeenTx: Date?
-
- /// Whether periodic heartbeat is suspended.
- var isHeartbeatSuspended = false
-
- /// Retry attempt.
- var retryAttempt: UInt32 = 0
-
- // Timings and timeouts.
- let timings: TunnelMonitorTimings
-
- func evaluateConnection(now: Date, pingTimeout: Duration) -> ConnectionEvaluation {
- switch connectionState {
- case .connecting:
- return handleConnectingState(now: now, pingTimeout: pingTimeout)
- case .connected:
- return handleConnectedState(now: now, pingTimeout: pingTimeout)
- default:
- return .ok
- }
- }
-
- func getPingTimeout() -> Duration {
- switch connectionState {
- case .connecting:
- let multiplier = timings.establishTimeoutMultiplier.saturatingPow(retryAttempt)
- let nextTimeout = timings.initialEstablishTimeout * Double(multiplier)
-
- if nextTimeout.isFinite, nextTimeout < timings.maxEstablishTimeout {
- return nextTimeout
- } else {
- return timings.maxEstablishTimeout
- }
-
- case .pendingStart, .connected, .waitingConnectivity, .stopped, .recovering:
- return timings.pingTimeout
- }
- }
-
- mutating func updateNetStats(newStats: WgStats, now: Date) {
- if newStats.bytesReceived > netStats.bytesReceived {
- lastSeenRx = now
- }
-
- if newStats.bytesSent > netStats.bytesSent {
- lastSeenTx = now
- }
-
- netStats = newStats
- }
-
- mutating func updatePingStats(sendResult: PingerSendResult, now: Date) {
- pingStats.requests.updateValue(now, forKey: sendResult.sequenceNumber)
- pingStats.lastRequestDate = now
- }
-
- mutating func setPingReplyReceived(_ sequenceNumber: UInt16, now: Date) -> Date? {
- guard let pingTimestamp = pingStats.requests.removeValue(forKey: sequenceNumber) else {
- return nil
- }
-
- pingStats.lastReplyDate = now
- timeoutReference = now
-
- return pingTimestamp
- }
-
- private func handleConnectingState(now: Date, pingTimeout: Duration) -> ConnectionEvaluation {
- if now.timeIntervalSince(timeoutReference) >= pingTimeout {
- return .pingTimeout
- }
-
- guard let lastRequestDate = pingStats.lastRequestDate else {
- return .sendInitialPing
- }
-
- if now.timeIntervalSince(lastRequestDate) >= timings.pingDelay {
- return .sendNextPing
- }
-
- return .ok
- }
-
- private func handleConnectedState(now: Date, pingTimeout: Duration) -> ConnectionEvaluation {
- if now.timeIntervalSince(timeoutReference) >= pingTimeout, !isHeartbeatSuspended {
- return .pingTimeout
- }
-
- guard let lastRequestDate = pingStats.lastRequestDate else {
- return .sendInitialPing
- }
-
- let timeSinceLastPing = now.timeIntervalSince(lastRequestDate)
- if let lastReplyDate = pingStats.lastReplyDate,
- lastRequestDate.timeIntervalSince(lastReplyDate) >= timings.heartbeatReplyTimeout,
- timeSinceLastPing >= timings.pingDelay, !isHeartbeatSuspended {
- return .retryHeartbeatPing
- }
-
- guard let lastSeenRx, let lastSeenTx else { return .ok }
-
- let rxTimeElapsed = now.timeIntervalSince(lastSeenRx)
- let txTimeElapsed = now.timeIntervalSince(lastSeenTx)
-
- if timeSinceLastPing >= timings.heartbeatPingInterval {
- // Send heartbeat if traffic is flowing.
- if rxTimeElapsed <= timings.trafficFlowTimeout || txTimeElapsed <= timings.trafficFlowTimeout {
- return .sendHeartbeatPing
- }
-
- if !isHeartbeatSuspended {
- return .suspendHeartbeat
- }
- }
-
- if timeSinceLastPing >= timings.pingDelay {
- if txTimeElapsed >= timings.trafficTimeout || rxTimeElapsed >= timings.trafficTimeout {
- return .trafficTimeout
- }
-
- if lastSeenTx > lastSeenRx, rxTimeElapsed >= timings.inboundTrafficTimeout {
- return .inboundTrafficTimeout
- }
- }
-
- return .ok
- }
- }
-
- /// Ping statistics.
- private struct PingStats {
- /// Dictionary holding sequence and corresponding date when echo request took place.
- var requests = [UInt16: Date]()
-
- /// Timestamp when last echo request was sent.
- var lastRequestDate: Date?
-
- /// Timestamp when last echo reply was received.
- var lastReplyDate: Date?
- }
-
private let tunnelDeviceInfo: TunnelDeviceInfoProtocol
private let nslock = NSLock()
@@ -207,7 +25,7 @@ public final class TunnelMonitor: TunnelMonitorProtocol {
private var isObservingDefaultPath = false
private var timer: DispatchSourceTimer?
- private var state: State
+ private var state: TunnelMonitorState
private var probeAddress: IPv4Address?
private let logger = Logger(label: "TunnelMonitor")
@@ -236,7 +54,7 @@ public final class TunnelMonitor: TunnelMonitorProtocol {
self.tunnelDeviceInfo = tunnelDeviceInfo
self.timings = timings
- state = State(timings: timings)
+ state = TunnelMonitorState(timings: timings)
self.pinger = pinger
self.pinger.onReply = { [weak self] reply in
@@ -547,18 +365,6 @@ public final class TunnelMonitor: TunnelMonitorProtocol {
}
}
- private enum ConnectionEvaluation {
- case ok
- case sendInitialPing
- case sendNextPing
- case sendHeartbeatPing
- case retryHeartbeatPing
- case suspendHeartbeat
- case inboundTrafficTimeout
- case trafficTimeout
- case pingTimeout
- }
-
private func getStats() -> WgStats? {
do {
return try tunnelDeviceInfo.getStats()
@@ -568,6 +374,4 @@ public final class TunnelMonitor: TunnelMonitorProtocol {
return nil
}
}
-
- // swiftlint:disable:next file_length
}
diff --git a/ios/PacketTunnelCore/TunnelMonitor/TunnelMonitorState.swift b/ios/PacketTunnelCore/TunnelMonitor/TunnelMonitorState.swift
new file mode 100644
index 0000000000..72b2bae083
--- /dev/null
+++ b/ios/PacketTunnelCore/TunnelMonitor/TunnelMonitorState.swift
@@ -0,0 +1,192 @@
+//
+// TunnelMonitorState.swift
+// PacketTunnelCore
+//
+// Created by Marco Nikic on 2024-02-06.
+// Copyright © 2024 Mullvad VPN AB. All rights reserved.
+//
+
+import Foundation
+import MullvadTypes
+
+/// Connection state.
+enum TunnelMonitorConnectionState {
+ /// Initialized and doing nothing.
+ case stopped
+
+ /// Preparing to start.
+ /// Intermediate state before receiving the first path update.
+ case pendingStart
+
+ /// Establishing connection.
+ case connecting
+
+ /// Connection is established.
+ case connected
+
+ /// Delegate is recovering connection.
+ /// Delegate has to call `start(probeAddress:)` to complete recovery and resume monitoring.
+ case recovering
+
+ /// Waiting for network connectivity.
+ case waitingConnectivity
+}
+
+enum ConnectionEvaluation {
+ case ok
+ case sendInitialPing
+ case sendNextPing
+ case sendHeartbeatPing
+ case retryHeartbeatPing
+ case suspendHeartbeat
+ case inboundTrafficTimeout
+ case trafficTimeout
+ case pingTimeout
+}
+
+/// Tunnel monitor state.
+struct TunnelMonitorState {
+ /// Current connection state.
+ var connectionState: TunnelMonitorConnectionState = .stopped
+
+ /// Network counters.
+ var netStats = WgStats()
+
+ /// Ping stats.
+ var pingStats = PingStats()
+
+ /// Reference date used to determine if timeout has occurred.
+ var timeoutReference = Date()
+
+ /// Last seen change in rx counter.
+ var lastSeenRx: Date?
+
+ /// Last seen change in tx counter.
+ var lastSeenTx: Date?
+
+ /// Whether periodic heartbeat is suspended.
+ var isHeartbeatSuspended = false
+
+ /// Retry attempt.
+ var retryAttempt: UInt32 = 0
+
+ // Timings and timeouts.
+ let timings: TunnelMonitorTimings
+
+ func evaluateConnection(now: Date, pingTimeout: Duration) -> ConnectionEvaluation {
+ switch connectionState {
+ case .connecting:
+ return handleConnectingState(now: now, pingTimeout: pingTimeout)
+ case .connected:
+ return handleConnectedState(now: now, pingTimeout: pingTimeout)
+ default:
+ return .ok
+ }
+ }
+
+ func getPingTimeout() -> Duration {
+ switch connectionState {
+ case .connecting:
+ let multiplier = timings.establishTimeoutMultiplier.saturatingPow(retryAttempt)
+ let nextTimeout = timings.initialEstablishTimeout * Double(multiplier)
+
+ if nextTimeout.isFinite, nextTimeout < timings.maxEstablishTimeout {
+ return nextTimeout
+ } else {
+ return timings.maxEstablishTimeout
+ }
+
+ case .pendingStart, .connected, .waitingConnectivity, .stopped, .recovering:
+ return timings.pingTimeout
+ }
+ }
+
+ mutating func updateNetStats(newStats: WgStats, now: Date) {
+ if newStats.bytesReceived > netStats.bytesReceived {
+ lastSeenRx = now
+ }
+
+ if newStats.bytesSent > netStats.bytesSent {
+ lastSeenTx = now
+ }
+
+ netStats = newStats
+ }
+
+ mutating func updatePingStats(sendResult: PingerSendResult, now: Date) {
+ pingStats.requests.updateValue(now, forKey: sendResult.sequenceNumber)
+ pingStats.lastRequestDate = now
+ }
+
+ mutating func setPingReplyReceived(_ sequenceNumber: UInt16, now: Date) -> Date? {
+ guard let pingTimestamp = pingStats.requests.removeValue(forKey: sequenceNumber) else {
+ return nil
+ }
+
+ pingStats.lastReplyDate = now
+ timeoutReference = now
+
+ return pingTimestamp
+ }
+
+ private func handleConnectingState(now: Date, pingTimeout: Duration) -> ConnectionEvaluation {
+ if now.timeIntervalSince(timeoutReference) >= pingTimeout {
+ return .pingTimeout
+ }
+
+ guard let lastRequestDate = pingStats.lastRequestDate else {
+ return .sendInitialPing
+ }
+
+ if now.timeIntervalSince(lastRequestDate) >= timings.pingDelay {
+ return .sendNextPing
+ }
+
+ return .ok
+ }
+
+ private func handleConnectedState(now: Date, pingTimeout: Duration) -> ConnectionEvaluation {
+ if now.timeIntervalSince(timeoutReference) >= pingTimeout, !isHeartbeatSuspended {
+ return .pingTimeout
+ }
+
+ guard let lastRequestDate = pingStats.lastRequestDate else {
+ return .sendInitialPing
+ }
+
+ let timeSinceLastPing = now.timeIntervalSince(lastRequestDate)
+ if let lastReplyDate = pingStats.lastReplyDate,
+ lastRequestDate.timeIntervalSince(lastReplyDate) >= timings.heartbeatReplyTimeout,
+ timeSinceLastPing >= timings.pingDelay, !isHeartbeatSuspended {
+ return .retryHeartbeatPing
+ }
+
+ guard let lastSeenRx, let lastSeenTx else { return .ok }
+
+ let rxTimeElapsed = now.timeIntervalSince(lastSeenRx)
+ let txTimeElapsed = now.timeIntervalSince(lastSeenTx)
+
+ if timeSinceLastPing >= timings.heartbeatPingInterval {
+ // Send heartbeat if traffic is flowing.
+ if rxTimeElapsed <= timings.trafficFlowTimeout || txTimeElapsed <= timings.trafficFlowTimeout {
+ return .sendHeartbeatPing
+ }
+
+ if !isHeartbeatSuspended {
+ return .suspendHeartbeat
+ }
+ }
+
+ if timeSinceLastPing >= timings.pingDelay {
+ if txTimeElapsed >= timings.trafficTimeout || rxTimeElapsed >= timings.trafficTimeout {
+ return .trafficTimeout
+ }
+
+ if lastSeenTx > lastSeenRx, rxTimeElapsed >= timings.inboundTrafficTimeout {
+ return .inboundTrafficTimeout
+ }
+ }
+
+ return .ok
+ }
+}