Skip to content

Commit

Permalink
Merge branch 'refactor-packet-tunnel-to-use-only-a-single-network-pat…
Browse files Browse the repository at this point in the history
…h-ios-401'
  • Loading branch information
buggmagnet committed Nov 29, 2023
2 parents c3ea6f4 + 18e21fa commit 5c8d672
Show file tree
Hide file tree
Showing 6 changed files with 48 additions and 76 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
eventQueue: internalQueue,
pinger: Pinger(replyQueue: internalQueue),
tunnelDeviceInfo: adapter,
defaultPathObserver: PacketTunnelPathObserver(packetTunnelProvider: self, eventQueue: internalQueue),
timings: TunnelMonitorTimings()
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ extension PacketTunnelActor {
- Parameter notifyObserverWithCurrentPath: immediately notifies path observer with the current path when set to `true`.
*/
func startDefaultPathObserver(notifyObserverWithCurrentPath: Bool = false) {
logger.trace("Start default path observer.")

defaultPathObserver.start { [weak self] networkPath in
self?.commandChannel.send(.networkReachability(networkPath))
}
Expand All @@ -26,6 +28,8 @@ extension PacketTunnelActor {

/// Stop observing changes to default path.
func stopDefaultPathObserver() {
logger.trace("Stop default path observer.")

defaultPathObserver.stop()
}

Expand All @@ -35,6 +39,8 @@ extension PacketTunnelActor {
- Parameter networkPath: new default path
*/
func handleDefaultPathChange(_ networkPath: NetworkPath) {
tunnelMonitor.handleNetworkPathUpdate(networkPath)

let newReachability = networkPath.networkReachability

func mutateConnectionState(_ connState: inout ConnectionState) -> Bool {
Expand Down
111 changes: 37 additions & 74 deletions ios/PacketTunnelCore/TunnelMonitor/TunnelMonitor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import Foundation
import MullvadLogging
import MullvadTypes
import Network
import NetworkExtension

/// Tunnel monitor.
public final class TunnelMonitor: TunnelMonitorProtocol {
Expand Down Expand Up @@ -203,7 +204,6 @@ public final class TunnelMonitor: TunnelMonitorProtocol {
private let timings: TunnelMonitorTimings

private var pinger: PingerProtocol
private var defaultPathObserver: DefaultPathObserverProtocol
private var isObservingDefaultPath = false
private var timer: DispatchSourceTimer?

Expand All @@ -230,12 +230,10 @@ public final class TunnelMonitor: TunnelMonitorProtocol {
eventQueue: DispatchQueue,
pinger: PingerProtocol,
tunnelDeviceInfo: TunnelDeviceInfoProtocol,
defaultPathObserver: DefaultPathObserverProtocol,
timings: TunnelMonitorTimings
) {
self.eventQueue = eventQueue
self.tunnelDeviceInfo = tunnelDeviceInfo
self.defaultPathObserver = defaultPathObserver

self.timings = timings
state = State(timings: timings)
Expand Down Expand Up @@ -272,7 +270,7 @@ public final class TunnelMonitor: TunnelMonitorProtocol {
self.probeAddress = probeAddress
state.connectionState = .pendingStart

addDefaultPathObserver()
startMonitoring()
}

public func stop() {
Expand All @@ -291,12 +289,8 @@ public final class TunnelMonitor: TunnelMonitorProtocol {
switch state.connectionState {
case .connecting, .connected:
startConnectivityCheckTimer()
addDefaultPathObserver()

case .waitingConnectivity, .pendingStart:
addDefaultPathObserver()

case .stopped, .recovering:
case .waitingConnectivity, .pendingStart, .stopped, .recovering:
break
}
}
Expand All @@ -308,7 +302,40 @@ public final class TunnelMonitor: TunnelMonitorProtocol {
logger.trace("Prepare to sleep.")

stopConnectivityCheckTimer()
removeDefaultPathObserver()
}

public func handleNetworkPathUpdate(_ networkPath: NetworkPath) {
nslock.withLock {
let pathStatus = networkPath.status
let isReachable = pathStatus == .satisfiable || pathStatus == .satisfied

switch state.connectionState {
case .pendingStart:
if isReachable {
logger.debug("Start monitoring connection.")
startMonitoring()
} else {
logger.debug("Wait for network to become reachable before starting monitoring.")
state.connectionState = .waitingConnectivity
}

case .waitingConnectivity:
guard isReachable else { return }

logger.debug("Network is reachable. Resume monitoring.")
startMonitoring()

case .connecting, .connected:
guard !isReachable else { return }

logger.debug("Network is unreachable. Pause monitoring.")
state.connectionState = .waitingConnectivity
stopMonitoring(resetRetryAttempt: true)

case .stopped, .recovering:
break
}
}
}

// MARK: - Private
Expand All @@ -324,42 +351,11 @@ public final class TunnelMonitor: TunnelMonitorProtocol {

probeAddress = nil

removeDefaultPathObserver()
stopMonitoring(resetRetryAttempt: !forRestart)

state.connectionState = .stopped
}

private func addDefaultPathObserver() {
defaultPathObserver.stop()

logger.trace("Add default path observer.")

isObservingDefaultPath = true

defaultPathObserver.start { [weak self] nwPath in
guard let self else { return }

nslock.withLock {
self.handleNetworkPathUpdate(nwPath)
}
}

if let currentPath = defaultPathObserver.defaultPath {
handleNetworkPathUpdate(currentPath)
}
}

private func removeDefaultPathObserver() {
guard isObservingDefaultPath else { return }

logger.trace("Remove default path observer.")

defaultPathObserver.stop()

isObservingDefaultPath = false
}

private func checkConnectivity() {
nslock.lock()
defer { nslock.unlock() }
Expand Down Expand Up @@ -429,7 +425,6 @@ public final class TunnelMonitor: TunnelMonitorProtocol {
#endif

private func startConnectionRecovery() {
removeDefaultPathObserver()
stopMonitoring(resetRetryAttempt: false)

state.retryAttempt = state.retryAttempt.saturatingAddition(1)
Expand All @@ -450,38 +445,6 @@ public final class TunnelMonitor: TunnelMonitorProtocol {
}
}

private func handleNetworkPathUpdate(_ networkPath: NetworkPath) {
let pathStatus = networkPath.status
let isReachable = pathStatus == .satisfiable || pathStatus == .satisfied

switch state.connectionState {
case .pendingStart:
if isReachable {
logger.debug("Start monitoring connection.")
startMonitoring()
} else {
logger.debug("Wait for network to become reachable before starting monitoring.")
state.connectionState = .waitingConnectivity
}

case .waitingConnectivity:
guard isReachable else { return }

logger.debug("Network is reachable. Resume monitoring.")
startMonitoring()

case .connecting, .connected:
guard !isReachable else { return }

logger.debug("Network is unreachable. Pause monitoring.")
state.connectionState = .waitingConnectivity
stopMonitoring(resetRetryAttempt: true)

case .stopped, .recovering:
break
}
}

private func didReceivePing(from sender: IPAddress, sequenceNumber: UInt16) {
nslock.lock()
defer { nslock.unlock() }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,7 @@ public protocol TunnelMonitorProtocol: AnyObject {
/// Cancels internal timers and time dependent data in preparation for device sleep.
/// Call this method when packet tunnel provider receives a sleep event.
func onSleep()

/// Handle changes in network path, eg. update connection state and monitoring.
func handleNetworkPathUpdate(_ networkPath: NetworkPath)
}
2 changes: 2 additions & 0 deletions ios/PacketTunnelCoreTests/Mocks/TunnelMonitorStub.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ class TunnelMonitorStub: TunnelMonitorProtocol {

func onSleep() {}

func handleNetworkPathUpdate(_ networkPath: NetworkPath) {}

func dispatch(_ event: TunnelMonitorEvent, after delay: DispatchTimeInterval = .never) {
if case .never = delay {
onEvent?(event)
Expand Down
1 change: 0 additions & 1 deletion ios/PacketTunnelCoreTests/TunnelMonitorTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,6 @@ extension TunnelMonitorTests {
eventQueue: .main,
pinger: pinger,
tunnelDeviceInfo: TunnelDeviceInfoStub(networkStatsProviding: networkCounters),
defaultPathObserver: DefaultPathObserverFake(),
timings: timings
)
}
Expand Down

0 comments on commit 5c8d672

Please sign in to comment.