@@ -14,188 +14,6 @@ import NetworkExtension
14
14
15
15
/// Tunnel monitor.
16
16
public final class TunnelMonitor : TunnelMonitorProtocol {
17
- /// Connection state.
18
- private enum ConnectionState {
19
- /// Initialized and doing nothing.
20
- case stopped
21
-
22
- /// Preparing to start.
23
- /// Intermediate state before receiving the first path update.
24
- case pendingStart
25
-
26
- /// Establishing connection.
27
- case connecting
28
-
29
- /// Connection is established.
30
- case connected
31
-
32
- /// Delegate is recovering connection.
33
- /// Delegate has to call `start(probeAddress:)` to complete recovery and resume monitoring.
34
- case recovering
35
-
36
- /// Waiting for network connectivity.
37
- case waitingConnectivity
38
- }
39
-
40
- /// Tunnel monitor state.
41
- private struct State {
42
- /// Current connection state.
43
- var connectionState : ConnectionState = . stopped
44
-
45
- /// Network counters.
46
- var netStats = WgStats ( )
47
-
48
- /// Ping stats.
49
- var pingStats = PingStats ( )
50
-
51
- /// Reference date used to determine if timeout has occurred.
52
- var timeoutReference = Date ( )
53
-
54
- /// Last seen change in rx counter.
55
- var lastSeenRx : Date ?
56
-
57
- /// Last seen change in tx counter.
58
- var lastSeenTx : Date ?
59
-
60
- /// Whether periodic heartbeat is suspended.
61
- var isHeartbeatSuspended = false
62
-
63
- /// Retry attempt.
64
- var retryAttempt : UInt32 = 0
65
-
66
- // Timings and timeouts.
67
- let timings : TunnelMonitorTimings
68
-
69
- func evaluateConnection( now: Date , pingTimeout: Duration ) -> ConnectionEvaluation {
70
- switch connectionState {
71
- case . connecting:
72
- return handleConnectingState ( now: now, pingTimeout: pingTimeout)
73
- case . connected:
74
- return handleConnectedState ( now: now, pingTimeout: pingTimeout)
75
- default :
76
- return . ok
77
- }
78
- }
79
-
80
- func getPingTimeout( ) -> Duration {
81
- switch connectionState {
82
- case . connecting:
83
- let multiplier = timings. establishTimeoutMultiplier. saturatingPow ( retryAttempt)
84
- let nextTimeout = timings. initialEstablishTimeout * Double( multiplier)
85
-
86
- if nextTimeout. isFinite, nextTimeout < timings. maxEstablishTimeout {
87
- return nextTimeout
88
- } else {
89
- return timings. maxEstablishTimeout
90
- }
91
-
92
- case . pendingStart, . connected, . waitingConnectivity, . stopped, . recovering:
93
- return timings. pingTimeout
94
- }
95
- }
96
-
97
- mutating func updateNetStats( newStats: WgStats , now: Date ) {
98
- if newStats. bytesReceived > netStats. bytesReceived {
99
- lastSeenRx = now
100
- }
101
-
102
- if newStats. bytesSent > netStats. bytesSent {
103
- lastSeenTx = now
104
- }
105
-
106
- netStats = newStats
107
- }
108
-
109
- mutating func updatePingStats( sendResult: PingerSendResult , now: Date ) {
110
- pingStats. requests. updateValue ( now, forKey: sendResult. sequenceNumber)
111
- pingStats. lastRequestDate = now
112
- }
113
-
114
- mutating func setPingReplyReceived( _ sequenceNumber: UInt16 , now: Date ) -> Date ? {
115
- guard let pingTimestamp = pingStats. requests. removeValue ( forKey: sequenceNumber) else {
116
- return nil
117
- }
118
-
119
- pingStats. lastReplyDate = now
120
- timeoutReference = now
121
-
122
- return pingTimestamp
123
- }
124
-
125
- private func handleConnectingState( now: Date , pingTimeout: Duration ) -> ConnectionEvaluation {
126
- if now. timeIntervalSince ( timeoutReference) >= pingTimeout {
127
- return . pingTimeout
128
- }
129
-
130
- guard let lastRequestDate = pingStats. lastRequestDate else {
131
- return . sendInitialPing
132
- }
133
-
134
- if now. timeIntervalSince ( lastRequestDate) >= timings. pingDelay {
135
- return . sendNextPing
136
- }
137
-
138
- return . ok
139
- }
140
-
141
- private func handleConnectedState( now: Date , pingTimeout: Duration ) -> ConnectionEvaluation {
142
- if now. timeIntervalSince ( timeoutReference) >= pingTimeout, !isHeartbeatSuspended {
143
- return . pingTimeout
144
- }
145
-
146
- guard let lastRequestDate = pingStats. lastRequestDate else {
147
- return . sendInitialPing
148
- }
149
-
150
- let timeSinceLastPing = now. timeIntervalSince ( lastRequestDate)
151
- if let lastReplyDate = pingStats. lastReplyDate,
152
- lastRequestDate. timeIntervalSince ( lastReplyDate) >= timings. heartbeatReplyTimeout,
153
- timeSinceLastPing >= timings. pingDelay, !isHeartbeatSuspended {
154
- return . retryHeartbeatPing
155
- }
156
-
157
- guard let lastSeenRx, let lastSeenTx else { return . ok }
158
-
159
- let rxTimeElapsed = now. timeIntervalSince ( lastSeenRx)
160
- let txTimeElapsed = now. timeIntervalSince ( lastSeenTx)
161
-
162
- if timeSinceLastPing >= timings. heartbeatPingInterval {
163
- // Send heartbeat if traffic is flowing.
164
- if rxTimeElapsed <= timings. trafficFlowTimeout || txTimeElapsed <= timings. trafficFlowTimeout {
165
- return . sendHeartbeatPing
166
- }
167
-
168
- if !isHeartbeatSuspended {
169
- return . suspendHeartbeat
170
- }
171
- }
172
-
173
- if timeSinceLastPing >= timings. pingDelay {
174
- if txTimeElapsed >= timings. trafficTimeout || rxTimeElapsed >= timings. trafficTimeout {
175
- return . trafficTimeout
176
- }
177
-
178
- if lastSeenTx > lastSeenRx, rxTimeElapsed >= timings. inboundTrafficTimeout {
179
- return . inboundTrafficTimeout
180
- }
181
- }
182
-
183
- return . ok
184
- }
185
- }
186
-
187
- /// Ping statistics.
188
- private struct PingStats {
189
- /// Dictionary holding sequence and corresponding date when echo request took place.
190
- var requests = [ UInt16: Date] ( )
191
-
192
- /// Timestamp when last echo request was sent.
193
- var lastRequestDate : Date ?
194
-
195
- /// Timestamp when last echo reply was received.
196
- var lastReplyDate : Date ?
197
- }
198
-
199
17
private let tunnelDeviceInfo : TunnelDeviceInfoProtocol
200
18
201
19
private let nslock = NSLock ( )
@@ -207,7 +25,7 @@ public final class TunnelMonitor: TunnelMonitorProtocol {
207
25
private var isObservingDefaultPath = false
208
26
private var timer : DispatchSourceTimer ?
209
27
210
- private var state : State
28
+ private var state : TunnelMonitorState
211
29
private var probeAddress : IPv4Address ?
212
30
213
31
private let logger = Logger ( label: " TunnelMonitor " )
@@ -236,7 +54,7 @@ public final class TunnelMonitor: TunnelMonitorProtocol {
236
54
self . tunnelDeviceInfo = tunnelDeviceInfo
237
55
238
56
self . timings = timings
239
- state = State ( timings: timings)
57
+ state = TunnelMonitorState ( timings: timings)
240
58
241
59
self . pinger = pinger
242
60
self . pinger. onReply = { [ weak self] reply in
@@ -547,18 +365,6 @@ public final class TunnelMonitor: TunnelMonitorProtocol {
547
365
}
548
366
}
549
367
550
- private enum ConnectionEvaluation {
551
- case ok
552
- case sendInitialPing
553
- case sendNextPing
554
- case sendHeartbeatPing
555
- case retryHeartbeatPing
556
- case suspendHeartbeat
557
- case inboundTrafficTimeout
558
- case trafficTimeout
559
- case pingTimeout
560
- }
561
-
562
368
private func getStats( ) -> WgStats ? {
563
369
do {
564
370
return try tunnelDeviceInfo. getStats ( )
@@ -568,6 +374,4 @@ public final class TunnelMonitor: TunnelMonitorProtocol {
568
374
return nil
569
375
}
570
376
}
571
-
572
- // swiftlint:disable:next file_length
573
377
}
0 commit comments