Skip to content

Commit

Permalink
Apply multi-hop on tunnel partially
Browse files Browse the repository at this point in the history
  • Loading branch information
mojganii committed Jun 26, 2024
1 parent f5fdc3a commit 47c68e1
Show file tree
Hide file tree
Showing 16 changed files with 242 additions and 73 deletions.
36 changes: 22 additions & 14 deletions ios/MullvadVPN.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

9 changes: 5 additions & 4 deletions ios/MullvadVPN/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -78,18 +78,19 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
ipOverrideRepository: ipOverrideRepository
)

let constraintsUpdater = RelayConstraintsUpdater()
let multihopListener = MultihopStateListener()
let multihopUpdater = MultihopUpdater(listener: multihopListener)

relayCacheTracker = RelayCacheTracker(
relayCache: ipOverrideWrapper,
application: application,
apiProxy: apiProxy
)

addressCacheTracker = AddressCacheTracker(application: application, apiProxy: apiProxy, store: addressCache)
tunnelStore = TunnelStore(application: application)

let constraintsUpdater = RelayConstraintsUpdater()
let multihopListener = MultihopStateListener()
let multihopUpdater = MultihopUpdater(listener: multihopListener)
tunnelStore = TunnelStore(application: application)

let relaySelector = RelaySelectorWrapper(
relayCache: ipOverrideWrapper,
Expand Down
36 changes: 36 additions & 0 deletions ios/MullvadVPN/TunnelManager/Tunnel+Settings.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//
// Tunnel+Settings.swift
// MullvadVPN
//
// Created by Mojgan on 2024-06-19.
// Copyright © 2024 Mullvad VPN AB. All rights reserved.
//

import MullvadLogging
import MullvadSettings

protocol TunnelSettingsStrategyProtocol {
func shouldReconnectToNewRelay(oldSettings: LatestTunnelSettings, newSettings: LatestTunnelSettings) -> Bool
}

struct TunnelSettingsStrategy: TunnelSettingsStrategyProtocol {
let logger: Logger?

init(logger: Logger? = nil) {
self.logger = logger
}

func shouldReconnectToNewRelay(oldSettings: LatestTunnelSettings, newSettings: LatestTunnelSettings) -> Bool {
switch (oldSettings, newSettings) {
case let (old, new) where old.relayConstraints != new.relayConstraints:
logger?.debug("Relay address changed from \(old.relayConstraints) to \(new.relayConstraints)")
return true
case let (old, new) where old.tunnelMultihopState != new.tunnelMultihopState:
logger?
.debug("Tunnel multi-hop state changed from \(old.tunnelMultihopState) to \(new.tunnelMultihopState)")
return true
default:
return false
}
}
}
40 changes: 20 additions & 20 deletions ios/MullvadVPN/TunnelManager/TunnelManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -939,16 +939,16 @@ final class TunnelManager: StorePaymentObserver {
let operation = AsyncBlockOperation(dispatchQueue: internalQueue) {
let currentSettings = self._tunnelSettings
var updatedSettings = self._tunnelSettings
let settingsStrategy = TunnelSettingsStrategy(logger: self.logger)

modificationBlock(&updatedSettings)

// Select new relay only when relay constraints change.
let currentConstraints = currentSettings.relayConstraints
let updatedConstraints = updatedSettings.relayConstraints
let selectNewRelay = currentConstraints != updatedConstraints

self.setSettings(updatedSettings, persist: true)
self.reconnectTunnel(selectNewRelay: selectNewRelay, completionHandler: nil)
self.reconnectTunnel(
selectNewRelay: settingsStrategy
.shouldReconnectToNewRelay(oldSettings: currentSettings, newSettings: updatedSettings),
completionHandler: nil
)
}

operation.completionBlock = {
Expand Down Expand Up @@ -1146,27 +1146,27 @@ extension TunnelManager {

```
func delay(seconds: UInt) async throws {
try await Task.sleep(nanoseconds: UInt64(seconds) * 1_000_000_000)
try await Task.sleep(nanoseconds: UInt64(seconds) * 1_000_000_000)
}

Task {
print("Wait 5 seconds")
try await delay(seconds: 5)
print("Wait 5 seconds")
try await delay(seconds: 5)

print("Simulate active account")
self.tunnelManager.simulateAccountExpiration(option: .active)
try await delay(seconds: 5)
print("Simulate active account")
self.tunnelManager.simulateAccountExpiration(option: .active)
try await delay(seconds: 5)

print("Simulate close to expiry")
self.tunnelManager.simulateAccountExpiration(option: .closeToExpiry)
try await delay(seconds: 10)
print("Simulate close to expiry")
self.tunnelManager.simulateAccountExpiration(option: .closeToExpiry)
try await delay(seconds: 10)

print("Simulate expired account")
self.tunnelManager.simulateAccountExpiration(option: .expired)
try await delay(seconds: 5)
print("Simulate expired account")
self.tunnelManager.simulateAccountExpiration(option: .expired)
try await delay(seconds: 5)

print("Simulate active account")
self.tunnelManager.simulateAccountExpiration(option: .active)
print("Simulate active account")
self.tunnelManager.simulateAccountExpiration(option: .active)
}
```

Expand Down
12 changes: 6 additions & 6 deletions ios/MullvadVPN/TunnelManager/TunnelState+UI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -187,8 +187,8 @@ extension TunnelState {
value: "Quantum secure connection. Connected to %@, %@",
comment: ""
),
tunnelInfo.exit.location.city, // TODO: Multihop
tunnelInfo.exit.location.country // TODO: Multihop
tunnelInfo.exit.location.city,
tunnelInfo.exit.location.country
)
} else {
String(
Expand All @@ -198,8 +198,8 @@ extension TunnelState {
value: "Secure connection. Connected to %@, %@",
comment: ""
),
tunnelInfo.exit.location.city, // TODO: Multihop
tunnelInfo.exit.location.country // TODO: Multihop
tunnelInfo.exit.location.city,
tunnelInfo.exit.location.country
)
}

Expand All @@ -219,8 +219,8 @@ extension TunnelState {
value: "Reconnecting to %@, %@",
comment: ""
),
tunnelInfo.exit.location.city, // TODO: Multihop
tunnelInfo.exit.location.country // TODO: Multihop
tunnelInfo.exit.location.city,
tunnelInfo.exit.location.country
)

case .waitingForConnectivity(.noConnection), .error:
Expand Down
25 changes: 20 additions & 5 deletions ios/MullvadVPN/TunnelManager/TunnelState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -78,29 +78,44 @@ enum TunnelState: Equatable, CustomStringConvertible {
case error(BlockedStateReason)

var description: String {
switch self {
return switch self {
case .pendingReconnect:
"pending reconnect after disconnect"
case let .connecting(tunnelRelays, isPostQuantum):
if let tunnelRelays {
"connecting \(isPostQuantum ? "(PQ) " : "")to \(tunnelRelays.exit.hostname)" // TODO: Multihop
"""
connecting \(isPostQuantum ? "(PQ) " : "")\
to \(tunnelRelays.exit.hostname)\
\(tunnelRelays.entry.flatMap { " via \($0.hostname)" } ?? "")
"""
} else {
"connecting\(isPostQuantum ? " (PQ)" : ""), fetching relay"
}
case let .connected(tunnelRelays, isPostQuantum):
"connected \(isPostQuantum ? "(PQ) " : "")to \(tunnelRelays.exit.hostname)" // TODO: Multihop
"""
connected \(isPostQuantum ? "(PQ) " : "")\
to \(tunnelRelays.exit.hostname)\
\(tunnelRelays.entry.flatMap { " via \($0.hostname)" } ?? "")
"""
case let .disconnecting(actionAfterDisconnect):
"disconnecting and then \(actionAfterDisconnect)"
case .disconnected:
"disconnected"
case let .reconnecting(tunnelRelays, isPostQuantum):
"reconnecting \(isPostQuantum ? "(PQ) " : "")to \(tunnelRelays.exit.hostname)" // TODO: Multihop
"""
reconnecting \(isPostQuantum ? "(PQ) " : "")\
to \(tunnelRelays.exit.hostname)\
\(tunnelRelays.entry.flatMap { " via \($0.hostname)" } ?? "")
"""
case .waitingForConnectivity:
"waiting for connectivity"
case let .error(blockedStateReason):
"error state: \(blockedStateReason)"
case let .negotiatingPostQuantumKey(tunnelRelays, _):
"negotiating key with \(tunnelRelays.exit.hostname)" // TODO: Multihop
"""
negotiating key with exit relay: \(tunnelRelays.exit.hostname)\
\(tunnelRelays.entry.flatMap { " via \($0.hostname)" } ?? "")
"""
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ final class TunnelControlView: UIView {
connectButtonBlurView.isEnabled = model.enableButtons
cityLabel.attributedText = attributedStringForLocation(string: model.city)
countryLabel.attributedText = attributedStringForLocation(string: model.country)
connectionPanel.connectedRelayName = model.connectedRelayName
connectionPanel.connectedRelayName = model.connectedRelaysName
connectionPanel.dataSource = model.connectionPanel

updateSecureLabel(tunnelState: tunnelState)
Expand Down Expand Up @@ -227,14 +227,15 @@ final class TunnelControlView: UIView {
private func updateTunnelRelays(tunnelRelays: SelectedRelays?) {
if let tunnelRelays {
cityLabel.attributedText = attributedStringForLocation(
string: tunnelRelays.exit.location.city // TODO: Multihop
string: tunnelRelays.exit.location.city
)
countryLabel.attributedText = attributedStringForLocation(
string: tunnelRelays.exit.location.country // TODO: Multihop
string: tunnelRelays.exit.location.country
)

connectionPanel.isHidden = false
connectionPanel.connectedRelayName = tunnelRelays.exit.hostname // TODO: Multihop
connectionPanel.connectedRelayName = tunnelRelays.exit
.hostname + "\(tunnelRelays.entry.flatMap { " via \($0.hostname)" } ?? "")"
} else {
countryLabel.attributedText = attributedStringForLocation(string: " ")
cityLabel.attributedText = attributedStringForLocation(string: " ")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ struct TunnelControlViewModel {
let enableButtons: Bool
let city: String
let country: String
let connectedRelayName: String
let connectedRelaysName: String
let outgoingConnectionInfo: OutgoingConnectionInfo?

var connectionPanel: ConnectionPanelData? {
Expand All @@ -29,7 +29,7 @@ struct TunnelControlViewModel {
}

return ConnectionPanelData(
inAddress: "\(tunnelRelays.exit.endpoint.ipv4Relay.ip)\(portAndTransport)", // TODO: Multihop
inAddress: "\(tunnelRelays.entry?.endpoint.ipv4Relay.ip ?? tunnelRelays.exit.endpoint.ipv4Relay.ip)\(portAndTransport)",
outAddress: outgoingConnectionInfo?.outAddress
)
}
Expand All @@ -41,7 +41,7 @@ struct TunnelControlViewModel {
enableButtons: true,
city: "",
country: "",
connectedRelayName: "",
connectedRelaysName: "",
outgoingConnectionInfo: nil
)
}
Expand All @@ -53,7 +53,7 @@ struct TunnelControlViewModel {
enableButtons: enableButtons,
city: city,
country: country,
connectedRelayName: connectedRelayName,
connectedRelaysName: connectedRelaysName,
outgoingConnectionInfo: nil
)
}
Expand All @@ -65,7 +65,7 @@ struct TunnelControlViewModel {
enableButtons: enableButtons,
city: city,
country: country,
connectedRelayName: connectedRelayName,
connectedRelaysName: connectedRelaysName,
outgoingConnectionInfo: outgoingConnectionInfo
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,15 +150,15 @@ class TunnelViewController: UIViewController, RootContainment {
case let .connecting(tunnelRelays, _):
mapViewController.removeLocationMarker()
contentView.setAnimatingActivity(true)
mapViewController.setCenter(tunnelRelays?.exit.location.geoCoordinate, animated: animated) // TODO: Multihop
mapViewController.setCenter(tunnelRelays?.exit.location.geoCoordinate, animated: animated)

case let .reconnecting(tunnelRelays, _), let .negotiatingPostQuantumKey(tunnelRelays, _):
mapViewController.removeLocationMarker()
contentView.setAnimatingActivity(true)
mapViewController.setCenter(tunnelRelays.exit.location.geoCoordinate, animated: animated) // TODO: Multihop
mapViewController.setCenter(tunnelRelays.exit.location.geoCoordinate, animated: animated)

case let .connected(tunnelRelays, _):
let center = tunnelRelays.exit.location.geoCoordinate // TODO: Multihop
let center = tunnelRelays.exit.location.geoCoordinate
mapViewController.setCenter(center, animated: animated) {
self.contentView.setAnimatingActivity(false)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ class RelaySelectorWrapperTests: XCTestCase {
relayCache: relayCache,
multihopUpdater: multihopUpdater
)

multihopStateListener.onNewMultihop?(.off)

let selectedRelays = try wrapper.selectRelays(with: RelayConstraints(), connectionAttemptCount: 0)
Expand Down
Loading

0 comments on commit 47c68e1

Please sign in to comment.