diff options
| author | Andrej Mihajlov <and@mullvad.net> | 2023-08-16 13:08:43 +0200 |
|---|---|---|
| committer | Andrej Mihajlov <and@mullvad.net> | 2023-08-16 13:08:43 +0200 |
| commit | 436a87c0f0bd91db940643cf5f72f066243ae49e (patch) | |
| tree | 4bb7469fe157639cf1877672d824830f14175445 /ios | |
| parent | c06d09878954bc775727ed34d2ccaff30037c7cd (diff) | |
| parent | 75391574abec9dcf5460c96e32069751c9644ec3 (diff) | |
| download | mullvadvpn-436a87c0f0bd91db940643cf5f72f066243ae49e.tar.xz mullvadvpn-436a87c0f0bd91db940643cf5f72f066243ae49e.zip | |
Merge branch 'add-packet-tunnel-core'
Diffstat (limited to 'ios')
21 files changed, 939 insertions, 284 deletions
diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj index 6614c287fc..33ac96d9fd 100644 --- a/ios/MullvadVPN.xcodeproj/project.pbxproj +++ b/ios/MullvadVPN.xcodeproj/project.pbxproj @@ -88,7 +88,6 @@ 582BB1AF229566420055B6EF /* SettingsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 582BB1AE229566420055B6EF /* SettingsCell.swift */; }; 582BB1B1229569620055B6EF /* UINavigationBar+Appearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 582BB1B0229569620055B6EF /* UINavigationBar+Appearance.swift */; }; 5835B7CC233B76CB0096D79F /* TunnelManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5835B7CB233B76CB0096D79F /* TunnelManager.swift */; }; - 5838318B27C40A3900000571 /* Pinger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5838318A27C40A3900000571 /* Pinger.swift */; }; 583D86482A2678DC0060D63B /* DeviceStateAccessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 583D86472A2678DC0060D63B /* DeviceStateAccessor.swift */; }; 583DA21425FA4B5C00318683 /* LocationDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 583DA21325FA4B5C00318683 /* LocationDataSource.swift */; }; 583FE00C29C0C7FD006E85F9 /* ModalPresentationConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 583FE00B29C0C7FD006E85F9 /* ModalPresentationConfiguration.swift */; }; @@ -227,7 +226,6 @@ 589C6A7C2A45AE0100DAD3EF /* TunnelObfuscation.h in Headers */ = {isa = PBXBuildFile; fileRef = 589C6A7B2A45AE0100DAD3EF /* TunnelObfuscation.h */; settings = {ATTRIBUTES = (Public, ); }; }; 589C6A7D2A45B06800DAD3EF /* TunnelObfuscation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5840231F2A406BF5007B27AC /* TunnelObfuscation.framework */; }; 58A1AA8C23F5584C009F7EA6 /* ConnectionPanelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58A1AA8B23F5584B009F7EA6 /* ConnectionPanelView.swift */; }; - 58A3BDB028A1821A00C8C2C6 /* WgStats.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58A3BDAF28A1821A00C8C2C6 /* WgStats.swift */; }; 58A8EE5A2976BFBB009C0F8D /* SKError+Localized.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58A8EE592976BFBB009C0F8D /* SKError+Localized.swift */; }; 58A8EE5E2976DB00009C0F8D /* StorePaymentManagerError+Display.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58A8EE5D2976DB00009C0F8D /* StorePaymentManagerError+Display.swift */; }; 58A99ED3240014A0006599E9 /* TermsOfServiceViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58A99ED2240014A0006599E9 /* TermsOfServiceViewController.swift */; }; @@ -262,6 +260,24 @@ 58C76A092A33850E00100D75 /* ApplicationTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58C76A072A33850E00100D75 /* ApplicationTarget.swift */; }; 58C76A0B2A338E4300100D75 /* BackgroundTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58C76A0A2A338E4300100D75 /* BackgroundTask.swift */; }; 58C774BE29A7A249003A1A56 /* CustomNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58C774BD29A7A249003A1A56 /* CustomNavigationController.swift */; }; + 58C7A43E2A863F470060C66F /* PacketTunnelCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 58C7A4362A863F440060C66F /* PacketTunnelCore.framework */; }; + 58C7A4462A863F490060C66F /* PacketTunnelCore.h in Headers */ = {isa = PBXBuildFile; fileRef = 58C7A4382A863F450060C66F /* PacketTunnelCore.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 58C7A4492A863F490060C66F /* PacketTunnelCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 58C7A4362A863F440060C66F /* PacketTunnelCore.framework */; }; + 58C7A44A2A863F490060C66F /* PacketTunnelCore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 58C7A4362A863F440060C66F /* PacketTunnelCore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 58C7A4512A863FB50060C66F /* PingerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58799A352A84FC9F007BE51F /* PingerProtocol.swift */; }; + 58C7A4522A863FB50060C66F /* Pinger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5838318A27C40A3900000571 /* Pinger.swift */; }; + 58C7A4552A863FB90060C66F /* TunnelMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FC040927B3EE03001C21F0 /* TunnelMonitor.swift */; }; + 58C7A4562A863FB90060C66F /* DefaultPathObserverProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58225D252A84E8A10083D7F1 /* DefaultPathObserverProtocol.swift */; }; + 58C7A4572A863FB90060C66F /* TunnelDeviceInfoProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 582403162A821FD700163DE8 /* TunnelDeviceInfoProtocol.swift */; }; + 58C7A4582A863FB90060C66F /* TunnelMonitorProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58C7A42C2A85067A0060C66F /* TunnelMonitorProtocol.swift */; }; + 58C7A4592A863FB90060C66F /* WgStats.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58A3BDAF28A1821A00C8C2C6 /* WgStats.swift */; }; + 58C7A45A2A863FDD0060C66F /* WgAdapterDeviceInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 582403142A821FB000163DE8 /* WgAdapterDeviceInfo.swift */; }; + 58C7A45B2A8640030060C66F /* PacketTunnelPathObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58225D272A84F23B0083D7F1 /* PacketTunnelPathObserver.swift */; }; + 58C7A45C2A8640490060C66F /* MullvadLogging.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 58D223F3294C8FF00029F5F8 /* MullvadLogging.framework */; platformFilter = ios; }; + 58C7A45D2A8640490060C66F /* MullvadLogging.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 58D223F3294C8FF00029F5F8 /* MullvadLogging.framework */; platformFilter = ios; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 58C7A4692A8643A90060C66F /* IPv4Header.h in Headers */ = {isa = PBXBuildFile; fileRef = 58218E1428B65058000C624F /* IPv4Header.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 58C7A46A2A8643A90060C66F /* ICMPHeader.h in Headers */ = {isa = PBXBuildFile; fileRef = 58218E1628B65396000C624F /* ICMPHeader.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 58C7A4702A8649ED0060C66F /* PingerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58C7A46F2A8649ED0060C66F /* PingerTests.swift */; }; 58C8191829FAA2C400DEB1B4 /* NotificationConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58C8191729FAA2C400DEB1B4 /* NotificationConfiguration.swift */; }; 58CAF9F82983D36800BE19F7 /* Coordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58CAF9F72983D36800BE19F7 /* Coordinator.swift */; }; 58CAF9FA2983E0C600BE19F7 /* LoginCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58CAF9F92983E0C600BE19F7 /* LoginCoordinator.swift */; }; @@ -274,7 +290,6 @@ 58CCA01822426713004F3011 /* AccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58CCA01722426713004F3011 /* AccountViewController.swift */; }; 58CCA01E2242787B004F3011 /* AccountTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58CCA01D2242787B004F3011 /* AccountTextField.swift */; }; 58CE38C728992C8700A6D6E5 /* WireGuardAdapterError+Localization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58E07298288031D5008902F8 /* WireGuardAdapterError+Localization.swift */; }; - 58CE38C828992C9200A6D6E5 /* TunnelMonitorDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58E072A428814C28008902F8 /* TunnelMonitorDelegate.swift */; }; 58CE5E64224146200008646E /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58CE5E63224146200008646E /* AppDelegate.swift */; }; 58CE5E66224146200008646E /* LoginViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58CE5E65224146200008646E /* LoginViewController.swift */; }; 58CE5E6B224146210008646E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 58CE5E6A224146210008646E /* Assets.xcassets */; }; @@ -375,7 +390,6 @@ 58FBFBE9291622580020E046 /* ExponentialBackoffTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FBFBE8291622580020E046 /* ExponentialBackoffTests.swift */; }; 58FBFBEA291622580020E046 /* MullvadREST.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 06799ABC28F98E1D00ACD94E /* MullvadREST.framework */; }; 58FBFBF1291630700020E046 /* DurationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FBFBF0291630700020E046 /* DurationTests.swift */; }; - 58FC040A27B3EE03001C21F0 /* TunnelMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FC040927B3EE03001C21F0 /* TunnelMonitor.swift */; }; 58FD5BF024238EB300112C88 /* SKProduct+Formatting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FD5BEF24238EB300112C88 /* SKProduct+Formatting.swift */; }; 58FD5BF42428C67600112C88 /* InAppPurchaseButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FD5BF32428C67600112C88 /* InAppPurchaseButton.swift */; }; 58FDF2D92A0BA11A00C2B061 /* DeviceCheckOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FDF2D82A0BA11900C2B061 /* DeviceCheckOperation.swift */; }; @@ -526,6 +540,34 @@ remoteGlobalIDString = 06799ABB28F98E1D00ACD94E; remoteInfo = MullvadREST; }; + 58C7A43F2A863F470060C66F /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 58CE5E58224146200008646E /* Project object */; + proxyType = 1; + remoteGlobalIDString = 58C7A4352A863F440060C66F; + remoteInfo = PacketTunnelCore; + }; + 58C7A4472A863F490060C66F /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 58CE5E58224146200008646E /* Project object */; + proxyType = 1; + remoteGlobalIDString = 58C7A4352A863F440060C66F; + remoteInfo = PacketTunnelCore; + }; + 58C7A45E2A8640490060C66F /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 58CE5E58224146200008646E /* Project object */; + proxyType = 1; + remoteGlobalIDString = 58D223F2294C8FF00029F5F8; + remoteInfo = MullvadLogging; + }; + 58C7A4712A864B860060C66F /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 58CE5E58224146200008646E /* Project object */; + proxyType = 1; + remoteGlobalIDString = 58CE5E5F224146200008646E; + remoteInfo = MullvadVPN; + }; 58CE5E7F224146470008646E /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 58CE5E58224146200008646E /* Project object */; @@ -758,6 +800,7 @@ 06799AD228F98E1D00ACD94E /* MullvadREST.framework in Embed Frameworks */, 58D223CD294C8BCB0029F5F8 /* Operations.framework in Embed Frameworks */, A97F1F482A1F4E1A00ECEFDE /* MullvadTransport.framework in Embed Frameworks */, + 58C7A44A2A863F490060C66F /* PacketTunnelCore.framework in Embed Frameworks */, 58F0974F2A20C31100DA2DAD /* WireGuardKitTypes in Embed Frameworks */, 063F027A2902B63F001FA09F /* RelayCache.framework in Embed Frameworks */, ); @@ -811,6 +854,17 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 58C7A4602A8640490060C66F /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 58C7A45D2A8640490060C66F /* MullvadLogging.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; 58CE5E85224146470008646E /* Embed Foundation Extensions */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -918,9 +972,12 @@ 5820EDA8288FE064006BF4E4 /* DeviceManagementInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceManagementInteractor.swift; sourceTree = "<group>"; }; 5820EDAA288FF0D2006BF4E4 /* DeviceRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceRowView.swift; sourceTree = "<group>"; }; 58218E1428B65058000C624F /* IPv4Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IPv4Header.h; sourceTree = "<group>"; }; - 58218E1528B650C1000C624F /* ObjCBridgingHeader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ObjCBridgingHeader.h; sourceTree = "<group>"; }; 58218E1628B65396000C624F /* ICMPHeader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ICMPHeader.h; sourceTree = "<group>"; }; + 58225D252A84E8A10083D7F1 /* DefaultPathObserverProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultPathObserverProtocol.swift; sourceTree = "<group>"; }; + 58225D272A84F23B0083D7F1 /* PacketTunnelPathObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PacketTunnelPathObserver.swift; sourceTree = "<group>"; }; 5823FA5326CE49F600283BF8 /* TunnelObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelObserver.swift; sourceTree = "<group>"; }; + 582403142A821FB000163DE8 /* WgAdapterDeviceInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WgAdapterDeviceInfo.swift; sourceTree = "<group>"; }; + 582403162A821FD700163DE8 /* TunnelDeviceInfoProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelDeviceInfoProtocol.swift; sourceTree = "<group>"; }; 58293FAC2510CA58005D0BB5 /* ProblemReportViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProblemReportViewController.swift; sourceTree = "<group>"; }; 58293FB025124117005D0BB5 /* CustomTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomTextField.swift; sourceTree = "<group>"; }; 58293FB2251241B3005D0BB5 /* CustomTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomTextView.swift; sourceTree = "<group>"; }; @@ -1014,6 +1071,7 @@ 5878F4FF29CDA742003D4BE2 /* UIView+AutoLayoutBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+AutoLayoutBuilder.swift"; sourceTree = "<group>"; }; 5878F50129CDB989003D4BE2 /* ChangeLogCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChangeLogCoordinator.swift; sourceTree = "<group>"; }; 587988C628A2A01F00E3DF54 /* AccountDataThrottling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountDataThrottling.swift; sourceTree = "<group>"; }; + 58799A352A84FC9F007BE51F /* PingerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PingerProtocol.swift; sourceTree = "<group>"; }; 587A01FB23F1F0BE00B68763 /* SimulatorTunnelProviderHost.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimulatorTunnelProviderHost.swift; sourceTree = "<group>"; }; 587AD7C523421D7000E93A53 /* TunnelSettingsV1.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TunnelSettingsV1.swift; sourceTree = "<group>"; }; 587B7535266528A200DEF7E9 /* NotificationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationManager.swift; sourceTree = "<group>"; }; @@ -1106,6 +1164,11 @@ 58C76A072A33850E00100D75 /* ApplicationTarget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationTarget.swift; sourceTree = "<group>"; }; 58C76A0A2A338E4300100D75 /* BackgroundTask.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundTask.swift; sourceTree = "<group>"; }; 58C774BD29A7A249003A1A56 /* CustomNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomNavigationController.swift; sourceTree = "<group>"; }; + 58C7A42C2A85067A0060C66F /* TunnelMonitorProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelMonitorProtocol.swift; sourceTree = "<group>"; }; + 58C7A4362A863F440060C66F /* PacketTunnelCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = PacketTunnelCore.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 58C7A4382A863F450060C66F /* PacketTunnelCore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PacketTunnelCore.h; sourceTree = "<group>"; }; + 58C7A43D2A863F460060C66F /* PacketTunnelCoreTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PacketTunnelCoreTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 58C7A46F2A8649ED0060C66F /* PingerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PingerTests.swift; sourceTree = "<group>"; }; 58C8191729FAA2C400DEB1B4 /* NotificationConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationConfiguration.swift; sourceTree = "<group>"; }; 58CAF9F72983D36800BE19F7 /* Coordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Coordinator.swift; sourceTree = "<group>"; }; 58CAF9F92983E0C600BE19F7 /* LoginCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginCoordinator.swift; sourceTree = "<group>"; }; @@ -1143,7 +1206,6 @@ 58E0729C28814AAE008902F8 /* PacketTunnelConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PacketTunnelConfiguration.swift; sourceTree = "<group>"; }; 58E0729E28814ACC008902F8 /* WireGuardLogLevel+Logging.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WireGuardLogLevel+Logging.swift"; sourceTree = "<group>"; }; 58E072A028814B0E008902F8 /* MullvadEndpoint+WgEndpoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MullvadEndpoint+WgEndpoint.swift"; sourceTree = "<group>"; }; - 58E072A428814C28008902F8 /* TunnelMonitorDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelMonitorDelegate.swift; sourceTree = "<group>"; }; 58E0A98727C8F46300FE6BDD /* Tunnel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tunnel.swift; sourceTree = "<group>"; }; 58E0E2832A3718CE002E3420 /* URLSessionShadowsocksTransport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLSessionShadowsocksTransport.swift; sourceTree = "<group>"; }; 58E11187292FA11F009FCA84 /* SettingsMigrationUIHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsMigrationUIHandler.swift; sourceTree = "<group>"; }; @@ -1316,11 +1378,28 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 58C7A4332A863F440060C66F /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 58C7A45C2A8640490060C66F /* MullvadLogging.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 58C7A43A2A863F450060C66F /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 58C7A43E2A863F470060C66F /* PacketTunnelCore.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 58CE5E5D224146200008646E /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 58F0974E2A20C31100DA2DAD /* WireGuardKitTypes in Frameworks */, + 58C7A4492A863F490060C66F /* PacketTunnelCore.framework in Frameworks */, 5898D2A92901844E00EB5EBA /* libRelaySelector.a in Frameworks */, 58D223F9294C8FF00029F5F8 /* MullvadLogging.framework in Frameworks */, 58D223E6294C8F120029F5F8 /* MullvadTypes.framework in Frameworks */, @@ -2077,6 +2156,35 @@ path = Containers; sourceTree = "<group>"; }; + 58C7A42E2A85091B0060C66F /* Pinger */ = { + isa = PBXGroup; + children = ( + 58218E1628B65396000C624F /* ICMPHeader.h */, + 58218E1428B65058000C624F /* IPv4Header.h */, + 5838318A27C40A3900000571 /* Pinger.swift */, + 58799A352A84FC9F007BE51F /* PingerProtocol.swift */, + ); + path = Pinger; + sourceTree = "<group>"; + }; + 58C7A4372A863F450060C66F /* PacketTunnelCore */ = { + isa = PBXGroup; + children = ( + 58C7A4382A863F450060C66F /* PacketTunnelCore.h */, + 58C7A42E2A85091B0060C66F /* Pinger */, + 58E072A228814B96008902F8 /* TunnelMonitor */, + ); + path = PacketTunnelCore; + sourceTree = "<group>"; + }; + 58C7A4432A863F490060C66F /* PacketTunnelCoreTests */ = { + isa = PBXGroup; + children = ( + 58C7A46F2A8649ED0060C66F /* PingerTests.swift */, + ); + path = PacketTunnelCoreTests; + sourceTree = "<group>"; + }; 58CAF9F22983D32200BE19F7 /* Coordinators */ = { isa = PBXGroup; children = ( @@ -2110,6 +2218,8 @@ 584023202A406BF5007B27AC /* TunnelObfuscation */, 58695A9E2A4ADA9200328DB3 /* TunnelObfuscationTests */, 7A83C3FC2A55B39500DFB83A /* TestPlans */, + 58C7A4372A863F450060C66F /* PacketTunnelCore */, + 58C7A4432A863F490060C66F /* PacketTunnelCoreTests */, 58CE5E61224146200008646E /* Products */, 584F991F2902CBDD001F858D /* Frameworks */, ); @@ -2134,6 +2244,8 @@ A97F1F412A1F4E1A00ECEFDE /* MullvadTransport.framework */, 5840231F2A406BF5007B27AC /* TunnelObfuscation.framework */, 58695A9D2A4ADA9100328DB3 /* TunnelObfuscationTests.xctest */, + 58C7A4362A863F440060C66F /* PacketTunnelCore.framework */, + 58C7A43D2A863F460060C66F /* PacketTunnelCoreTests.xctest */, ); name = Products; sourceTree = "<group>"; @@ -2172,15 +2284,15 @@ isa = PBXGroup; children = ( 58CE5E7D224146470008646E /* Info.plist */, - 58218E1528B650C1000C624F /* ObjCBridgingHeader.h */, 58CE5E7E224146470008646E /* PacketTunnel.entitlements */, 58E0729C28814AAE008902F8 /* PacketTunnelConfiguration.swift */, 58CE5E7B224146470008646E /* PacketTunnelProvider.swift */, 58E072A028814B0E008902F8 /* MullvadEndpoint+WgEndpoint.swift */, - 58E072A228814B96008902F8 /* TunnelMonitor */, 58E07298288031D5008902F8 /* WireGuardAdapterError+Localization.swift */, 58E0729E28814ACC008902F8 /* WireGuardLogLevel+Logging.swift */, 58906DDF2445C7A5002F0673 /* NEProviderStopReason+Debug.swift */, + 582403142A821FB000163DE8 /* WgAdapterDeviceInfo.swift */, + 58225D272A84F23B0083D7F1 /* PacketTunnelPathObserver.swift */, 58915D662A25F9F20066445B /* DeviceCheck */, ); path = PacketTunnel; @@ -2256,12 +2368,11 @@ 58E072A228814B96008902F8 /* TunnelMonitor */ = { isa = PBXGroup; children = ( - 5838318A27C40A3900000571 /* Pinger.swift */, + 58225D252A84E8A10083D7F1 /* DefaultPathObserverProtocol.swift */, + 582403162A821FD700163DE8 /* TunnelDeviceInfoProtocol.swift */, 58FC040927B3EE03001C21F0 /* TunnelMonitor.swift */, - 58E072A428814C28008902F8 /* TunnelMonitorDelegate.swift */, + 58C7A42C2A85067A0060C66F /* TunnelMonitorProtocol.swift */, 58A3BDAF28A1821A00C8C2C6 /* WgStats.swift */, - 58218E1428B65058000C624F /* IPv4Header.h */, - 58218E1628B65396000C624F /* ICMPHeader.h */, ); path = TunnelMonitor; sourceTree = "<group>"; @@ -2408,6 +2519,16 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 58C7A4312A863F440060C66F /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 58C7A46A2A8643A90060C66F /* ICMPHeader.h in Headers */, + 58C7A4692A8643A90060C66F /* IPv4Header.h in Headers */, + 58C7A4462A863F490060C66F /* PacketTunnelCore.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 58D223A0294C8A480029F5F8 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; @@ -2625,6 +2746,45 @@ productReference = 58B0A2A0238EE67E00BC001D /* MullvadVPNTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; + 58C7A4352A863F440060C66F /* PacketTunnelCore */ = { + isa = PBXNativeTarget; + buildConfigurationList = 58C7A44B2A863F4A0060C66F /* Build configuration list for PBXNativeTarget "PacketTunnelCore" */; + buildPhases = ( + 58C7A4312A863F440060C66F /* Headers */, + 58C7A4322A863F440060C66F /* Sources */, + 58C7A4332A863F440060C66F /* Frameworks */, + 58C7A4342A863F440060C66F /* Resources */, + 58C7A4602A8640490060C66F /* Embed Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 58C7A45F2A8640490060C66F /* PBXTargetDependency */, + ); + name = PacketTunnelCore; + productName = PacketTunnelCore; + productReference = 58C7A4362A863F440060C66F /* PacketTunnelCore.framework */; + productType = "com.apple.product-type.framework"; + }; + 58C7A43C2A863F450060C66F /* PacketTunnelCoreTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 58C7A44E2A863F4A0060C66F /* Build configuration list for PBXNativeTarget "PacketTunnelCoreTests" */; + buildPhases = ( + 58C7A4392A863F450060C66F /* Sources */, + 58C7A43A2A863F450060C66F /* Frameworks */, + 58C7A43B2A863F450060C66F /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 58C7A4402A863F470060C66F /* PBXTargetDependency */, + 58C7A4722A864B860060C66F /* PBXTargetDependency */, + ); + name = PacketTunnelCoreTests; + productName = PacketTunnelCoreTests; + productReference = 58C7A43D2A863F460060C66F /* PacketTunnelCoreTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; 58CE5E5F224146200008646E /* MullvadVPN */ = { isa = PBXNativeTarget; buildConfigurationList = 58CE5E72224146210008646E /* Build configuration list for PBXNativeTarget "MullvadVPN" */; @@ -2648,6 +2808,7 @@ 58CE5E80224146470008646E /* PBXTargetDependency */, A97F1F462A1F4E1A00ECEFDE /* PBXTargetDependency */, A9EC20F22A5D79ED0040D56E /* PBXTargetDependency */, + 58C7A4482A863F490060C66F /* PBXTargetDependency */, ); name = MullvadVPN; packageProductDependencies = ( @@ -2845,6 +3006,12 @@ 58B0A29F238EE67E00BC001D = { CreatedOnToolsVersion = 11.2.1; }; + 58C7A4352A863F440060C66F = { + CreatedOnToolsVersion = 14.3.1; + }; + 58C7A43C2A863F450060C66F = { + CreatedOnToolsVersion = 14.3.1; + }; 58CE5E5F224146200008646E = { CreatedOnToolsVersion = 10.0; LastSwiftMigration = 1020; @@ -2924,6 +3091,8 @@ A97F1F402A1F4E1A00ECEFDE /* MullvadTransport */, 5840231E2A406BF5007B27AC /* TunnelObfuscation */, 58695A9C2A4ADA9100328DB3 /* TunnelObfuscationTests */, + 58C7A4352A863F440060C66F /* PacketTunnelCore */, + 58C7A43C2A863F450060C66F /* PacketTunnelCoreTests */, ); }; /* End PBXProject section */ @@ -2966,6 +3135,20 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 58C7A4342A863F440060C66F /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 58C7A43B2A863F450060C66F /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 58CE5E5E224146200008646E /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -3217,6 +3400,28 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 58C7A4322A863F440060C66F /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 58C7A4522A863FB50060C66F /* Pinger.swift in Sources */, + 58C7A4572A863FB90060C66F /* TunnelDeviceInfoProtocol.swift in Sources */, + 58C7A4562A863FB90060C66F /* DefaultPathObserverProtocol.swift in Sources */, + 58C7A4552A863FB90060C66F /* TunnelMonitor.swift in Sources */, + 58C7A4512A863FB50060C66F /* PingerProtocol.swift in Sources */, + 58C7A4592A863FB90060C66F /* WgStats.swift in Sources */, + 58C7A4582A863FB90060C66F /* TunnelMonitorProtocol.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 58C7A4392A863F450060C66F /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 58C7A4702A8649ED0060C66F /* PingerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 58CE5E5C224146200008646E /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -3456,12 +3661,10 @@ 587AD7C723421D8600E93A53 /* TunnelSettingsV1.swift in Sources */, 5893C6FA29C1B481009090D1 /* DNSSettings.swift in Sources */, 580810E52A30E13A00B74552 /* DeviceStateAccessorProtocol.swift in Sources */, - 58CE38C828992C9200A6D6E5 /* TunnelMonitorDelegate.swift in Sources */, + 58C7A45A2A863FDD0060C66F /* WgAdapterDeviceInfo.swift in Sources */, 580810E82A30E15500B74552 /* DeviceCheckRemoteServiceProtocol.swift in Sources */, 068CE5782927BE4800A068BB /* Migration.swift in Sources */, 58915D682A25FA080066445B /* DeviceCheckRemoteService.swift in Sources */, - 58FC040A27B3EE03001C21F0 /* TunnelMonitor.swift in Sources */, - 5838318B27C40A3900000571 /* Pinger.swift in Sources */, 06410E05292D0FC000AFC18C /* SettingsParser.swift in Sources */, A92ECC292A7802AB0052F1B1 /* StoredDeviceData.swift in Sources */, A9D96B1B2A8248F200A5C673 /* MigrationManager.swift in Sources */, @@ -3475,10 +3678,10 @@ 58906DE02445C7A5002F0673 /* NEProviderStopReason+Debug.swift in Sources */, 06410DFF292CF16C00AFC18C /* KeychainSettingsStore.swift in Sources */, 58E072A128814B0E008902F8 /* MullvadEndpoint+WgEndpoint.swift in Sources */, + 58C7A45B2A8640030060C66F /* PacketTunnelPathObserver.swift in Sources */, 06AC116228F94C450037AF9A /* ApplicationConfiguration.swift in Sources */, A92ECC252A7802520052F1B1 /* StoredAccountData.swift in Sources */, A92ECC2D2A7803A50052F1B1 /* DeviceState.swift in Sources */, - 58A3BDB028A1821A00C8C2C6 /* WgStats.swift in Sources */, 583FE02429C1ACB3006E85F9 /* RESTCreateApplePaymentResponse+Localization.swift in Sources */, 5877D70F282137E8002FCFC7 /* SettingsManager.swift in Sources */, 58CE38C728992C8700A6D6E5 /* WireGuardAdapterError+Localization.swift in Sources */, @@ -3655,6 +3858,27 @@ isa = PBXTargetDependency; productRef = 58915D6B2A2603700066445B /* WireGuardKitTypes */; }; + 58C7A4402A863F470060C66F /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 58C7A4352A863F440060C66F /* PacketTunnelCore */; + targetProxy = 58C7A43F2A863F470060C66F /* PBXContainerItemProxy */; + }; + 58C7A4482A863F490060C66F /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 58C7A4352A863F440060C66F /* PacketTunnelCore */; + targetProxy = 58C7A4472A863F490060C66F /* PBXContainerItemProxy */; + }; + 58C7A45F2A8640490060C66F /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + platformFilter = ios; + target = 58D223F2294C8FF00029F5F8 /* MullvadLogging */; + targetProxy = 58C7A45E2A8640490060C66F /* PBXContainerItemProxy */; + }; + 58C7A4722A864B860060C66F /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 58CE5E5F224146200008646E /* MullvadVPN */; + targetProxy = 58C7A4712A864B860060C66F /* PBXContainerItemProxy */; + }; 58CE5E80224146470008646E /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 58CE5E78224146470008646E /* PacketTunnel */; @@ -4181,6 +4405,124 @@ }; name = Release; }; + 58C7A44C2A863F4A0060C66F /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 5808273928487E3E006B77A4 /* Base.xcconfig */; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2023 Mullvad VPN AB. All rights reserved."; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).PacketTunnelCore"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 58C7A44D2A863F4A0060C66F /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 5808273928487E3E006B77A4 /* Base.xcconfig */; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2023 Mullvad VPN AB. All rights reserved."; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).PacketTunnelCore"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 58C7A44F2A863F4A0060C66F /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = CKG9MXH72F; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadVPN.PacketTunnelCoreTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 58C7A4502A863F4A0060C66F /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = CKG9MXH72F; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadVPN.PacketTunnelCoreTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; 58CE5E70224146210008646E /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -4355,7 +4697,6 @@ ); PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).PacketTunnel"; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OBJC_BRIDGING_HEADER = "$(SRCROOT)/PacketTunnel/ObjCBridgingHeader.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; }; @@ -4376,7 +4717,6 @@ ); PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).PacketTunnel"; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OBJC_BRIDGING_HEADER = "$(SRCROOT)/PacketTunnel/ObjCBridgingHeader.h"; SWIFT_VERSION = 5.0; }; name = Release; @@ -4827,6 +5167,24 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 58C7A44B2A863F4A0060C66F /* Build configuration list for PBXNativeTarget "PacketTunnelCore" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 58C7A44C2A863F4A0060C66F /* Debug */, + 58C7A44D2A863F4A0060C66F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 58C7A44E2A863F4A0060C66F /* Build configuration list for PBXNativeTarget "PacketTunnelCoreTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 58C7A44F2A863F4A0060C66F /* Debug */, + 58C7A4502A863F4A0060C66F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 58CE5E5B224146200008646E /* Build configuration list for PBXProject "MullvadVPN" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/MullvadVPN.xcscheme b/ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/MullvadVPN.xcscheme index ecfd93db2a..2757b5fb64 100644 --- a/ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/MullvadVPN.xcscheme +++ b/ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/MullvadVPN.xcscheme @@ -213,6 +213,47 @@ ReferencedContainer = "container:MullvadVPN.xcodeproj"> </BuildableReference> </TestableReference> + <TestableReference + skipped = "NO" + parallelizable = "YES"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "58C7A43C2A863F450060C66F" + BuildableName = "PacketTunnelCoreTests.xctest" + BlueprintName = "PacketTunnelCoreTests" + ReferencedContainer = "container:MullvadVPN.xcodeproj"> + </BuildableReference> + </TestableReference> + <TestableReference + skipped = "NO"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "58C7A43C2A863F450060C66F" + BuildableName = "PacketTunnelCoreTests.xctest" + BlueprintName = "PacketTunnelCoreTests" + ReferencedContainer = "container:MullvadVPN.xcodeproj"> + </BuildableReference> + </TestableReference> + <TestableReference + skipped = "NO"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "58C7A43C2A863F450060C66F" + BuildableName = "PacketTunnelCoreTests.xctest" + BlueprintName = "PacketTunnelCoreTests" + ReferencedContainer = "container:MullvadVPN.xcodeproj"> + </BuildableReference> + </TestableReference> + <TestableReference + skipped = "NO"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "58C7A43C2A863F450060C66F" + BuildableName = "PacketTunnelCoreTests.xctest" + BlueprintName = "PacketTunnelCoreTests" + ReferencedContainer = "container:MullvadVPN.xcodeproj"> + </BuildableReference> + </TestableReference> </Testables> </TestAction> <LaunchAction diff --git a/ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/PacketTunnelCore.xcscheme b/ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/PacketTunnelCore.xcscheme new file mode 100644 index 0000000000..b00fd5b4fd --- /dev/null +++ b/ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/PacketTunnelCore.xcscheme @@ -0,0 +1,66 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Scheme + LastUpgradeVersion = "1430" + version = "1.7"> + <BuildAction + parallelizeBuildables = "YES" + buildImplicitDependencies = "YES"> + <BuildActionEntries> + <BuildActionEntry + buildForTesting = "YES" + buildForRunning = "YES" + buildForProfiling = "YES" + buildForArchiving = "YES" + buildForAnalyzing = "YES"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "58C7A4352A863F440060C66F" + BuildableName = "PacketTunnelCore.framework" + BlueprintName = "PacketTunnelCore" + ReferencedContainer = "container:MullvadVPN.xcodeproj"> + </BuildableReference> + </BuildActionEntry> + </BuildActionEntries> + </BuildAction> + <TestAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + shouldUseLaunchSchemeArgsEnv = "YES" + shouldAutocreateTestPlan = "YES"> + </TestAction> + <LaunchAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + launchStyle = "0" + useCustomWorkingDirectory = "NO" + ignoresPersistentStateOnLaunch = "NO" + debugDocumentVersioning = "YES" + debugServiceExtension = "internal" + allowLocationSimulation = "YES"> + </LaunchAction> + <ProfileAction + buildConfiguration = "Release" + shouldUseLaunchSchemeArgsEnv = "YES" + savedToolIdentifier = "" + useCustomWorkingDirectory = "NO" + debugDocumentVersioning = "YES"> + <MacroExpansion> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "58C7A4352A863F440060C66F" + BuildableName = "PacketTunnelCore.framework" + BlueprintName = "PacketTunnelCore" + ReferencedContainer = "container:MullvadVPN.xcodeproj"> + </BuildableReference> + </MacroExpansion> + </ProfileAction> + <AnalyzeAction + buildConfiguration = "Debug"> + </AnalyzeAction> + <ArchiveAction + buildConfiguration = "Release" + revealArchiveInOrganizer = "YES"> + </ArchiveAction> +</Scheme> diff --git a/ios/PacketTunnel/ObjCBridgingHeader.h b/ios/PacketTunnel/ObjCBridgingHeader.h deleted file mode 100644 index 4e57f0de38..0000000000 --- a/ios/PacketTunnel/ObjCBridgingHeader.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// ObjCBridgingHeader.h -// MullvadVPN -// -// Created by pronebird on 24/08/2022. -// Copyright © 2022 Mullvad VPN AB. All rights reserved. -// - -#ifndef OBJCBRIDGINGHEADER_H -#define OBJCBRIDGINGHEADER_H - -#include "IPv4Header.h" -#include "ICMPHeader.h" - -#endif /* OBJCBRIDGINGHEADER_H */ diff --git a/ios/PacketTunnel/PacketTunnelPathObserver.swift b/ios/PacketTunnel/PacketTunnelPathObserver.swift new file mode 100644 index 0000000000..b16c62f705 --- /dev/null +++ b/ios/PacketTunnel/PacketTunnelPathObserver.swift @@ -0,0 +1,48 @@ +// +// PacketTunnelPathObserver.swift +// PacketTunnel +// +// Created by pronebird on 10/08/2023. +// Copyright © 2023 Mullvad VPN AB. All rights reserved. +// + +import Foundation +import NetworkExtension +import PacketTunnelCore + +final class PacketTunnelPathObserver: DefaultPathObserverProtocol { + private weak var packetTunnelProvider: NEPacketTunnelProvider? + private let stateLock = NSLock() + private var observationToken: NSKeyValueObservation? + + init(packetTunnelProvider: NEPacketTunnelProvider) { + self.packetTunnelProvider = packetTunnelProvider + } + + var defaultPath: NetworkPath? { + return packetTunnelProvider?.defaultPath + } + + func start(_ body: @escaping (NetworkPath) -> Void) { + stateLock.withLock { + observationToken?.invalidate() + + // Normally packet tunnel provider should exist throughout the network extension lifetime. + observationToken = packetTunnelProvider?.observe(\.defaultPath, options: [.new]) { _, change in + let nwPath = change.newValue.flatMap { $0 } + if let nwPath { + body(nwPath) + } + } + } + } + + func stop() { + stateLock.withLock { + observationToken?.invalidate() + observationToken = nil + } + } +} + +extension NetworkExtension.NWPath: NetworkPath {} diff --git a/ios/PacketTunnel/PacketTunnelProvider.swift b/ios/PacketTunnel/PacketTunnelProvider.swift index a964215771..5b7bdd3013 100644 --- a/ios/PacketTunnel/PacketTunnelProvider.swift +++ b/ios/PacketTunnel/PacketTunnelProvider.swift @@ -14,6 +14,7 @@ import MullvadTypes import Network import NetworkExtension import Operations +import PacketTunnelCore import RelayCache import RelaySelector import TunnelProviderMessaging @@ -25,7 +26,7 @@ private let tunnelStartupFailureRestartInterval: TimeInterval = 2 /// Delay before trying to reconnect tunnel after private key rotation. private let keyRotationTunnelReconnectionDelay = 60 * 2 -class PacketTunnelProvider: NEPacketTunnelProvider, TunnelMonitorDelegate { +class PacketTunnelProvider: NEPacketTunnelProvider { /// Tunnel provider logger. private let providerLogger: Logger @@ -178,11 +179,14 @@ class PacketTunnelProvider: NEPacketTunnelProvider, TunnelMonitorDelegate { ) tunnelMonitor = TunnelMonitor( - delegateQueue: dispatchQueue, - packetTunnelProvider: self, - adapter: adapter + eventQueue: dispatchQueue, + pinger: Pinger(replyQueue: dispatchQueue), + tunnelDeviceInfo: WgAdapterDeviceInfo(adapter: adapter), + defaultPathObserver: PacketTunnelPathObserver(packetTunnelProvider: self) ) - tunnelMonitor.delegate = self + tunnelMonitor.onEvent = { [weak self] event in + self?.handleTunnelMonitorEvent(event) + } } override func startTunnel(options: [String: NSObject]?, completionHandler: @escaping (Error?) -> Void) { @@ -381,9 +385,22 @@ class PacketTunnelProvider: NEPacketTunnelProvider, TunnelMonitorDelegate { tunnelMonitor.onWake() } - // MARK: - TunnelMonitorDelegate + // MARK: - Private: Tunnel monitoring + + private func handleTunnelMonitorEvent(_ event: TunnelMonitorEvent) { + switch event { + case .connectionEstablished: + tunnelConnectionEstablished() - func tunnelMonitorDidDetermineConnectionEstablished(_ tunnelMonitor: TunnelMonitor) { + case .connectionLost: + tunnelConnectionLost() + + case let .networkReachabilityChanged(isReachable): + tunnelReachabilityChanged(isReachable) + } + } + + private func tunnelConnectionEstablished() { dispatchPrecondition(condition: .onQueue(dispatchQueue)) providerLogger.debug("Connection established.") @@ -399,7 +416,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider, TunnelMonitorDelegate { setReconnecting(false) } - func tunnelMonitorDelegateShouldHandleConnectionRecovery(_ tunnelMonitor: TunnelMonitor) { + private func tunnelConnectionLost() { dispatchPrecondition(condition: .onQueue(dispatchQueue)) let (value, isOverflow) = numberOfFailedAttempts.addingReportingOverflow(1) @@ -414,10 +431,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider, TunnelMonitorDelegate { reconnectTunnel(to: .automatic, shouldStopTunnelMonitor: false) } - func tunnelMonitor( - _ tunnelMonitor: TunnelMonitor, - networkReachabilityStatusDidChange isNetworkReachable: Bool - ) { + private func tunnelReachabilityChanged(_ isNetworkReachable: Bool) { dispatchPrecondition(condition: .onQueue(dispatchQueue)) guard self.isNetworkReachable != isNetworkReachable else { return } diff --git a/ios/PacketTunnel/TunnelMonitor/TunnelMonitorDelegate.swift b/ios/PacketTunnel/TunnelMonitor/TunnelMonitorDelegate.swift deleted file mode 100644 index 9839faf3e5..0000000000 --- a/ios/PacketTunnel/TunnelMonitor/TunnelMonitorDelegate.swift +++ /dev/null @@ -1,23 +0,0 @@ -// -// TunnelMonitorDelegate.swift -// PacketTunnel -// -// Created by pronebird on 15/07/2022. -// Copyright © 2022 Mullvad VPN AB. All rights reserved. -// - -import Foundation - -protocol TunnelMonitorDelegate: AnyObject { - /// Invoked when tunnel monitor determined that connection is established. - func tunnelMonitorDidDetermineConnectionEstablished(_ tunnelMonitor: TunnelMonitor) - - /// Invoked when tunnel monitor determined that connection attempt has failed. - func tunnelMonitorDelegateShouldHandleConnectionRecovery(_ tunnelMonitor: TunnelMonitor) - - /// Invoked when network reachability status changes. - func tunnelMonitor( - _ tunnelMonitor: TunnelMonitor, - networkReachabilityStatusDidChange isNetworkReachable: Bool - ) -} diff --git a/ios/PacketTunnel/TunnelMonitor/WgStats.swift b/ios/PacketTunnel/TunnelMonitor/WgStats.swift deleted file mode 100644 index d2bec85d2d..0000000000 --- a/ios/PacketTunnel/TunnelMonitor/WgStats.swift +++ /dev/null @@ -1,51 +0,0 @@ -// -// WgStats.swift -// PacketTunnel -// -// Created by pronebird on 08/08/2022. -// Copyright © 2022 Mullvad VPN AB. All rights reserved. -// - -import Foundation - -struct WgStats { - let bytesReceived: UInt64 - let bytesSent: UInt64 - - init() { - bytesReceived = 0 - bytesSent = 0 - } - - init?(from string: String) { - var _bytesReceived: UInt64? - var _bytesSent: UInt64? - - string.enumerateLines { line, stop in - if _bytesReceived == nil, let value = parseValue("rx_bytes=", in: line) { - _bytesReceived = value - } else if _bytesSent == nil, let value = parseValue("tx_bytes=", in: line) { - _bytesSent = value - } - - if _bytesReceived != nil, _bytesSent != nil { - stop = true - } - } - - guard let _bytesReceived, let _bytesSent else { - return nil - } - - bytesReceived = _bytesReceived - bytesSent = _bytesSent - } -} - -@inline(__always) private func parseValue(_ prefixKey: String, in line: String) -> UInt64? { - guard line.hasPrefix(prefixKey) else { return nil } - - let value = line.dropFirst(prefixKey.count) - - return UInt64(value) -} diff --git a/ios/PacketTunnel/WgAdapterDeviceInfo.swift b/ios/PacketTunnel/WgAdapterDeviceInfo.swift new file mode 100644 index 0000000000..c32a37b111 --- /dev/null +++ b/ios/PacketTunnel/WgAdapterDeviceInfo.swift @@ -0,0 +1,84 @@ +// +// WgAdapterInfoProvider.swift +// PacketTunnel +// +// Created by pronebird on 08/08/2023. +// Copyright © 2023 Mullvad VPN AB. All rights reserved. +// + +import Foundation +import PacketTunnelCore +import WireGuardKit + +struct WgAdapterDeviceInfo: TunnelDeviceInfoProtocol { + let adapter: WireGuardAdapter + + var interfaceName: String? { + return adapter.interfaceName + } + + func getStats() throws -> WgStats { + var result: String? + + let dispatchGroup = DispatchGroup() + dispatchGroup.enter() + adapter.getRuntimeConfiguration { string in + result = string + dispatchGroup.leave() + } + + guard case .success = dispatchGroup.wait(wallTimeout: .now() + .seconds(1)) else { throw StatsError.timeout } + guard let result else { throw StatsError.nilValue } + guard let newStats = WgStats(from: result) else { throw StatsError.parse } + + return newStats + } + + enum StatsError: LocalizedError { + case timeout, nilValue, parse + + var errorDescription: String? { + switch self { + case .timeout: + return "adapter.getRuntimeConfiguration timeout." + case .nilValue: + return "Received nil string for stats." + case .parse: + return "Couldn't parse stats." + } + } + } +} + +private extension WgStats { + init?(from string: String) { + var bytesReceived: UInt64? + var bytesSent: UInt64? + + string.enumerateLines { line, stop in + if bytesReceived == nil, let value = parseValue("rx_bytes=", in: line) { + bytesReceived = value + } else if bytesSent == nil, let value = parseValue("tx_bytes=", in: line) { + bytesSent = value + } + + if bytesReceived != nil, bytesSent != nil { + stop = true + } + } + + guard let bytesReceived, let bytesSent else { + return nil + } + + self.init(bytesReceived: bytesReceived, bytesSent: bytesSent) + } +} + +@inline(__always) private func parseValue(_ prefixKey: String, in line: String) -> UInt64? { + guard line.hasPrefix(prefixKey) else { return nil } + + let value = line.dropFirst(prefixKey.count) + + return UInt64(value) +} diff --git a/ios/PacketTunnelCore/PacketTunnelCore.h b/ios/PacketTunnelCore/PacketTunnelCore.h new file mode 100644 index 0000000000..09a65f16c7 --- /dev/null +++ b/ios/PacketTunnelCore/PacketTunnelCore.h @@ -0,0 +1,19 @@ +// +// PacketTunnelCore.h +// PacketTunnelCore +// +// Created by pronebird on 11/08/2023. +// Copyright © 2023 Mullvad VPN AB. All rights reserved. +// + +#import <Foundation/Foundation.h> + +//! Project version number for PacketTunnelCore. +FOUNDATION_EXPORT double PacketTunnelCoreVersionNumber; + +//! Project version string for PacketTunnelCore. +FOUNDATION_EXPORT const unsigned char PacketTunnelCoreVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import <PacketTunnelCore/PublicHeader.h> +#import <PacketTunnelCore/ICMPHeader.h> +#import <PacketTunnelCore/IPv4Header.h> diff --git a/ios/PacketTunnel/TunnelMonitor/ICMPHeader.h b/ios/PacketTunnelCore/Pinger/ICMPHeader.h index 7e91f576d2..a5458b53b9 100644 --- a/ios/PacketTunnel/TunnelMonitor/ICMPHeader.h +++ b/ios/PacketTunnelCore/Pinger/ICMPHeader.h @@ -1,6 +1,6 @@ // // ICMPHeader.h -// MullvadVPN +// PacketTunnelCore // // Created by pronebird on 24/08/2022. // Copyright © 2022 Mullvad VPN AB. All rights reserved. @@ -9,6 +9,8 @@ #ifndef ICMPHEADER_H #define ICMPHEADER_H +#include <AssertMacros.h> + struct ICMPHeader { uint8_t type; uint8_t code; diff --git a/ios/PacketTunnel/TunnelMonitor/IPv4Header.h b/ios/PacketTunnelCore/Pinger/IPv4Header.h index bcb0b8ecc9..9024ce8208 100644 --- a/ios/PacketTunnel/TunnelMonitor/IPv4Header.h +++ b/ios/PacketTunnelCore/Pinger/IPv4Header.h @@ -1,6 +1,6 @@ // // IPv4Header.h -// MullvadVPN +// PacketTunnelCore // // Created by pronebird on 24/08/2022. // Copyright © 2022 Mullvad VPN AB. All rights reserved. diff --git a/ios/PacketTunnel/TunnelMonitor/Pinger.swift b/ios/PacketTunnelCore/Pinger/Pinger.swift index f5872bb590..c312f822a4 100644 --- a/ios/PacketTunnel/TunnelMonitor/Pinger.swift +++ b/ios/PacketTunnelCore/Pinger/Pinger.swift @@ -1,6 +1,6 @@ // // Pinger.swift -// PacketTunnel +// PacketTunnelCore // // Created by pronebird on 21/02/2022. // Copyright © 2022 Mullvad VPN AB. All rights reserved. @@ -11,25 +11,7 @@ import protocol Network.IPAddress import struct Network.IPv4Address import struct Network.IPv6Address -protocol PingerDelegate: AnyObject { - func pinger( - _ pinger: Pinger, - didReceiveResponseFromSender senderAddress: IPAddress, - icmpHeader: ICMPHeader - ) - - func pinger( - _ pinger: Pinger, - didFailWithError error: Error - ) -} - -final class Pinger { - struct SendResult { - var sequenceNumber: UInt16 - var bytesSent: UInt16 - } - +public final class Pinger: PingerProtocol { // Socket read buffer size. private static let bufferSize = 65535 @@ -40,37 +22,35 @@ final class Pinger { private var socket: CFSocket? private var readBuffer = [UInt8](repeating: 0, count: bufferSize) private let stateLock = NSRecursiveLock() + private let replyQueue: DispatchQueue - private weak var _delegate: PingerDelegate? - private let delegateQueue: DispatchQueue - - var delegate: PingerDelegate? { + public var onReply: ((PingerReply) -> Void)? { get { - stateLock.lock() - defer { stateLock.unlock() } - - return _delegate + stateLock.withLock { + return _onReply + } } set { - stateLock.lock() - defer { stateLock.unlock() } - - _delegate = newValue + stateLock.withLock { + _onReply = newValue + } } } + private var _onReply: ((PingerReply) -> Void)? + deinit { closeSocket() } - init(identifier: UInt16 = 757, delegateQueue: DispatchQueue) { + public init(identifier: UInt16 = 757, replyQueue: DispatchQueue) { self.identifier = identifier - self.delegateQueue = delegateQueue + self.replyQueue = replyQueue } /// Open socket and optionally bind it to the given interface. /// Automatically closes the previously opened socket when called multiple times in a row. - func openSocket(bindTo interfaceName: String?) throws { + public func openSocket(bindTo interfaceName: String?) throws { stateLock.lock() defer { stateLock.unlock() } @@ -117,7 +97,7 @@ final class Pinger { socket = newSocket } - func closeSocket() { + public func closeSocket() { stateLock.lock() defer { stateLock.unlock() } @@ -129,8 +109,8 @@ final class Pinger { } /// Send ping packet to the given address. - /// Returns `SendResult` on success, otherwise throws a `Pinger.Error`. - func send(to address: IPv4Address) throws -> SendResult { + /// Returns `PingerSendResult` on success, otherwise throws a `Pinger.Error`. + public func send(to address: IPv4Address) throws -> PingerSendResult { stateLock.lock() defer { stateLock.unlock() } @@ -170,7 +150,7 @@ final class Pinger { throw Error.sendPacket(errno) } - return SendResult(sequenceNumber: sequenceNumber, bytesSent: UInt16(bytesSent)) + return PingerSendResult(sequenceNumber: sequenceNumber, bytesSent: UInt16(bytesSent)) } private func nextSequenceNumber() -> UInt16 { @@ -201,18 +181,14 @@ final class Pinger { let icmpHeader = try parseICMPResponse(buffer: &readBuffer, length: bytesRead) guard let sender = Self.makeIPAddress(from: address) else { throw Error.parseIPAddress } - delegateQueue.async { - self.delegate?.pinger( - self, - didReceiveResponseFromSender: sender, - icmpHeader: icmpHeader - ) + replyQueue.async { + self.onReply?(.success(sender, icmpHeader.sequenceNumber)) } } catch Pinger.Error.clientIdentifierMismatch { // Ignore responses from other senders. } catch { - delegateQueue.async { - self.delegate?.pinger(self, didFailWithError: error) + replyQueue.async { + self.onReply?(.parseError(error)) } } } @@ -340,7 +316,7 @@ final class Pinger { } extension Pinger { - enum Error: LocalizedError { + public enum Error: LocalizedError { /// Failure to create a socket. case createSocket @@ -371,7 +347,7 @@ extension Pinger { /// Failure to parse IP address. case parseIPAddress - var errorDescription: String? { + public var errorDescription: String? { switch self { case .createSocket: return "Failure to create socket." @@ -397,7 +373,7 @@ extension Pinger { } } - enum MalformedResponseReason { + public enum MalformedResponseReason { case ipv4PacketTooSmall case icmpHeaderTooSmall case invalidIPVersion diff --git a/ios/PacketTunnelCore/Pinger/PingerProtocol.swift b/ios/PacketTunnelCore/Pinger/PingerProtocol.swift new file mode 100644 index 0000000000..6b7a4f9764 --- /dev/null +++ b/ios/PacketTunnelCore/Pinger/PingerProtocol.swift @@ -0,0 +1,28 @@ +// +// PingerProtocol.swift +// PacketTunnelCore +// +// Created by pronebird on 10/08/2023. +// Copyright © 2023 Mullvad VPN AB. All rights reserved. +// + +import Foundation +import Network + +public enum PingerReply { + case success(_ sender: IPAddress, _ sequenceNumber: UInt16) + case parseError(Error) +} + +public struct PingerSendResult { + public var sequenceNumber: UInt16 + public var bytesSent: UInt16 +} + +public protocol PingerProtocol { + var onReply: ((PingerReply) -> Void)? { get set } + + func openSocket(bindTo interfaceName: String?) throws + func closeSocket() + func send(to address: IPv4Address) throws -> PingerSendResult +} diff --git a/ios/PacketTunnelCore/TunnelMonitor/DefaultPathObserverProtocol.swift b/ios/PacketTunnelCore/TunnelMonitor/DefaultPathObserverProtocol.swift new file mode 100644 index 0000000000..97b31a1683 --- /dev/null +++ b/ios/PacketTunnelCore/TunnelMonitor/DefaultPathObserverProtocol.swift @@ -0,0 +1,25 @@ +// +// DefaultPathObserverProtocol.swift +// PacketTunnelCore +// +// Created by pronebird on 10/08/2023. +// Copyright © 2023 Mullvad VPN AB. All rights reserved. +// + +import Foundation +import NetworkExtension + +public protocol DefaultPathObserverProtocol { + /// Returns current default path or `nil` if unknown yet. + var defaultPath: NetworkPath? { get } + + /// Start observing changes to `defaultPath`. + func start(_ body: @escaping (NetworkPath) -> Void) + + /// Stop observing changes to `defaultPath`. + func stop() +} + +public protocol NetworkPath { + var status: NetworkExtension.NWPathStatus { get } +} diff --git a/ios/PacketTunnelCore/TunnelMonitor/TunnelDeviceInfoProtocol.swift b/ios/PacketTunnelCore/TunnelMonitor/TunnelDeviceInfoProtocol.swift new file mode 100644 index 0000000000..fcd1ff88c3 --- /dev/null +++ b/ios/PacketTunnelCore/TunnelMonitor/TunnelDeviceInfoProtocol.swift @@ -0,0 +1,17 @@ +// +// TunnelDeviceInfoProtocol.swift +// PacketTunnelCore +// +// Created by pronebird on 08/08/2023. +// Copyright © 2023 Mullvad VPN AB. All rights reserved. +// + +import Foundation + +public protocol TunnelDeviceInfoProtocol { + /// Returns tunnel interface name (i.e utun0) if available. + var interfaceName: String? { get } + + /// Returns tunnel statistics. + func getStats() throws -> WgStats +} diff --git a/ios/PacketTunnel/TunnelMonitor/TunnelMonitor.swift b/ios/PacketTunnelCore/TunnelMonitor/TunnelMonitor.swift index 8448a89991..9c88b9158c 100644 --- a/ios/PacketTunnel/TunnelMonitor/TunnelMonitor.swift +++ b/ios/PacketTunnelCore/TunnelMonitor/TunnelMonitor.swift @@ -1,6 +1,6 @@ // // TunnelMonitor.swift -// PacketTunnel +// PacketTunnelCore // // Created by pronebird on 09/02/2022. // Copyright © 2022 Mullvad VPN AB. All rights reserved. @@ -8,9 +8,8 @@ import Foundation import MullvadLogging -import MullvadTypes -import NetworkExtension -import WireGuardKit +import protocol Network.IPAddress +import struct Network.IPv4Address /// Interval for periodic heartbeat ping issued when traffic is flowing. /// Should help to detect connectivity issues on networks that drop traffic in one of directions, @@ -48,7 +47,7 @@ private let inboundTrafficTimeout: TimeInterval = 5 /// Ping is issued after that timeout is exceeded.s private let trafficTimeout: TimeInterval = 120 -final class TunnelMonitor: PingerDelegate { +public final class TunnelMonitor: TunnelMonitorProtocol { /// Connection state. private enum ConnectionState { /// Initialized and doing nothing. @@ -191,7 +190,7 @@ final class TunnelMonitor: PingerDelegate { netStats = newStats } - mutating func updatePingStats(sendResult: Pinger.SendResult, now: Date) { + mutating func updatePingStats(sendResult: PingerSendResult, now: Date) { pingStats.requests.updateValue(now, forKey: sendResult.sequenceNumber) pingStats.lastRequestDate = now } @@ -220,15 +219,15 @@ final class TunnelMonitor: PingerDelegate { var lastReplyDate: Date? } - private let adapter: WireGuardAdapter + private let tunnelDeviceInfo: TunnelDeviceInfoProtocol private let nslock = NSLock() - private let eventQueue = DispatchQueue(label: "TunnelMonitor-eventQueue") - private let delegateQueue: DispatchQueue + private let timerQueue = DispatchQueue(label: "TunnelMonitor-timerQueue") + private let eventQueue: DispatchQueue - private let pinger: Pinger - private weak var packetTunnelProvider: NEPacketTunnelProvider? - private var defaultPathObserver: NSKeyValueObservation? + private var pinger: PingerProtocol + private var defaultPathObserver: DefaultPathObserverProtocol + private var isObservingDefaultPath = false private var timer: DispatchSourceTimer? private var state = State() @@ -236,40 +235,49 @@ final class TunnelMonitor: PingerDelegate { private let logger = Logger(label: "TunnelMonitor") - private weak var _delegate: TunnelMonitorDelegate? - weak var delegate: TunnelMonitorDelegate? { + private var _onEvent: ((TunnelMonitorEvent) -> Void)? + public var onEvent: ((TunnelMonitorEvent) -> Void)? { set { - nslock.lock() - defer { nslock.unlock() } - - _delegate = newValue + nslock.withLock { + _onEvent = newValue + } } get { - nslock.lock() - defer { nslock.unlock() } - - return _delegate + nslock.withLock { + return _onEvent + } } } - init( - delegateQueue: DispatchQueue, - packetTunnelProvider: NEPacketTunnelProvider, - adapter: WireGuardAdapter + public init( + eventQueue: DispatchQueue, + pinger: PingerProtocol, + tunnelDeviceInfo: TunnelDeviceInfoProtocol, + defaultPathObserver: DefaultPathObserverProtocol ) { - self.delegateQueue = delegateQueue - self.packetTunnelProvider = packetTunnelProvider - self.adapter = adapter + self.eventQueue = eventQueue + self.tunnelDeviceInfo = tunnelDeviceInfo + self.defaultPathObserver = defaultPathObserver - pinger = Pinger(delegateQueue: eventQueue) - pinger.delegate = self + self.pinger = pinger + self.pinger.onReply = { [weak self] reply in + guard let self else { return } + + switch reply { + case let .success(sender, sequenceNumber): + didReceivePing(from: sender, sequenceNumber: sequenceNumber) + + case let .parseError(error): + logger.error(error: error, message: "Failed to parse ICMP response.") + } + } } deinit { stop() } - func start(probeAddress: IPv4Address) { + public func start(probeAddress: IPv4Address) { nslock.lock() defer { nslock.unlock() } @@ -286,14 +294,14 @@ final class TunnelMonitor: PingerDelegate { addDefaultPathObserver() } - func stop() { + public func stop() { nslock.lock() defer { nslock.unlock() } _stop() } - func onWake() { + public func onWake() { nslock.lock() defer { nslock.unlock() } @@ -312,7 +320,7 @@ final class TunnelMonitor: PingerDelegate { } } - func onSleep() { + public func onSleep() { nslock.lock() defer { nslock.unlock() } @@ -322,23 +330,6 @@ final class TunnelMonitor: PingerDelegate { removeDefaultPathObserver() } - // MARK: - PingerDelegate - - func pinger( - _ pinger: Pinger, - didReceiveResponseFromSender senderAddress: IPAddress, - icmpHeader: ICMPHeader - ) { - didReceivePing(from: senderAddress, icmpHeader: icmpHeader) - } - - func pinger(_ pinger: Pinger, didFailWithError error: Error) { - logger.error( - error: error, - message: "Failed to parse ICMP response." - ) - } - // MARK: - Private private func _stop(forRestart: Bool = false) { @@ -359,37 +350,33 @@ final class TunnelMonitor: PingerDelegate { } private func addDefaultPathObserver() { - guard let packetTunnelProvider else { return } - - defaultPathObserver?.invalidate() + defaultPathObserver.stop() logger.trace("Add default path observer.") - defaultPathObserver = packetTunnelProvider - .observe(\.defaultPath, options: [.new]) { [weak self] _, change in - guard let self else { return } + isObservingDefaultPath = true - nslock.lock() - defer { self.nslock.unlock() } + defaultPathObserver.start { [weak self] nwPath in + guard let self else { return } - let newValue = change.newValue.flatMap { $0 } - if let newPath = newValue { - handleNetworkPathUpdate(newPath) - } + nslock.withLock { + self.handleNetworkPathUpdate(nwPath) } + } - if let currentPath = packetTunnelProvider.defaultPath { + if let currentPath = defaultPathObserver.defaultPath { handleNetworkPathUpdate(currentPath) } } private func removeDefaultPathObserver() { - guard let defaultPathObserver else { return } + guard isObservingDefaultPath else { return } logger.trace("Remove default path observer.") - defaultPathObserver.invalidate() - self.defaultPathObserver = nil + defaultPathObserver.stop() + + isObservingDefaultPath = false } private func checkConnectivity() { @@ -468,7 +455,7 @@ final class TunnelMonitor: PingerDelegate { state.connectionState = .recovering probeAddress = nil - sendDelegateShouldHandleConnectionRecovery() + sendConnectionLostEvent() } private func sendPing(to receiver: IPv4Address, now: Date) { @@ -482,7 +469,7 @@ final class TunnelMonitor: PingerDelegate { } } - private func handleNetworkPathUpdate(_ networkPath: NetworkExtension.NWPath) { + private func handleNetworkPathUpdate(_ networkPath: NetworkPath) { let pathStatus = networkPath.status let isReachable = pathStatus == .satisfiable || pathStatus == .satisfied @@ -491,11 +478,11 @@ final class TunnelMonitor: PingerDelegate { if isReachable { logger.debug("Start monitoring connection.") startMonitoring() - sendDelegateNetworkStatusChange(true) + sendNetworkStatusChangeEvent(true) } else { logger.debug("Wait for network to become reachable before starting monitoring.") state.connectionState = .waitingConnectivity - sendDelegateNetworkStatusChange(false) + sendNetworkStatusChangeEvent(false) } case .waitingConnectivity: @@ -503,7 +490,7 @@ final class TunnelMonitor: PingerDelegate { logger.debug("Network is reachable. Resume monitoring.") startMonitoring() - sendDelegateNetworkStatusChange(true) + sendNetworkStatusChangeEvent(true) case .connecting, .connected: guard !isReachable else { return } @@ -511,14 +498,14 @@ final class TunnelMonitor: PingerDelegate { logger.debug("Network is unreachable. Pause monitoring.") state.connectionState = .waitingConnectivity stopMonitoring(resetRetryAttempt: true) - sendDelegateNetworkStatusChange(false) + sendNetworkStatusChangeEvent(false) case .stopped, .recovering: break } } - private func didReceivePing(from sender: IPAddress, icmpHeader: ICMPHeader) { + private func didReceivePing(from sender: IPAddress, sequenceNumber: UInt16) { nslock.lock() defer { nslock.unlock() } @@ -529,7 +516,6 @@ final class TunnelMonitor: PingerDelegate { } let now = Date() - let sequenceNumber = icmpHeader.sequenceNumber guard let pingTimestamp = state.setPingReplyReceived(sequenceNumber, now: now) else { logger.trace("Got unknown ping sequence: \(sequenceNumber).") return @@ -548,13 +534,13 @@ final class TunnelMonitor: PingerDelegate { if case .connecting = state.connectionState { state.connectionState = .connected state.retryAttempt = 0 - sendDelegateConnectionEstablished() + sendConnectionEstablishedEvent() } } private func startMonitoring() { do { - guard let interfaceName = adapter.interfaceName else { + guard let interfaceName = tunnelDeviceInfo.interfaceName else { logger.debug("Failed to obtain utun interface name.") return } @@ -585,7 +571,7 @@ final class TunnelMonitor: PingerDelegate { } private func startConnectivityCheckTimer() { - let timer = DispatchSource.makeTimerSource(queue: eventQueue) + let timer = DispatchSource.makeTimerSource(queue: timerQueue) timer.setEventHandler { [weak self] in self?.checkConnectivity() } @@ -609,24 +595,21 @@ final class TunnelMonitor: PingerDelegate { self.timer = nil } - private func sendDelegateConnectionEstablished() { - delegateQueue.async { - self.delegate?.tunnelMonitorDidDetermineConnectionEstablished(self) + private func sendConnectionEstablishedEvent() { + eventQueue.async { + self.onEvent?(.connectionEstablished) } } - private func sendDelegateShouldHandleConnectionRecovery() { - delegateQueue.async { - self.delegate?.tunnelMonitorDelegateShouldHandleConnectionRecovery(self) + private func sendConnectionLostEvent() { + eventQueue.async { + self.onEvent?(.connectionLost) } } - private func sendDelegateNetworkStatusChange(_ isNetworkReachable: Bool) { - delegateQueue.async { - self.delegate?.tunnelMonitor( - self, - networkReachabilityStatusDidChange: isNetworkReachable - ) + private func sendNetworkStatusChangeEvent(_ isNetworkReachable: Bool) { + eventQueue.async { + self.onEvent?(.networkReachabilityChanged(isNetworkReachable)) } } @@ -643,30 +626,12 @@ final class TunnelMonitor: PingerDelegate { } private func getStats() -> WgStats? { - var result: String? - - let dispatchGroup = DispatchGroup() - dispatchGroup.enter() - adapter.getRuntimeConfiguration { string in - result = string - dispatchGroup.leave() - } - - guard case .success = dispatchGroup.wait(wallTimeout: .now() + .seconds(1)) else { - logger.debug("adapter.getRuntimeConfiguration timeout.") - return nil - } - - guard let result else { - logger.debug("Received nil string for stats.") - return nil - } + do { + return try tunnelDeviceInfo.getStats() + } catch { + logger.error(error: error, message: "Failed to obtain adapter stats.") - guard let newStats = WgStats(from: result) else { - logger.debug("Couldn't parse stats.") return nil } - - return newStats } } diff --git a/ios/PacketTunnelCore/TunnelMonitor/TunnelMonitorProtocol.swift b/ios/PacketTunnelCore/TunnelMonitor/TunnelMonitorProtocol.swift new file mode 100644 index 0000000000..3bee394242 --- /dev/null +++ b/ios/PacketTunnelCore/TunnelMonitor/TunnelMonitorProtocol.swift @@ -0,0 +1,43 @@ +// +// TunnelMonitorProtocol.swift +// PacketTunnelCore +// +// Created by pronebird on 10/08/2023. +// Copyright © 2023 Mullvad VPN AB. All rights reserved. +// + +import Foundation +import Network + +/// Tunnel monitor event. +public enum TunnelMonitorEvent { + /// Dispatched after receiving the first ping response + case connectionEstablished + + /// Dispatched when connection stops receiving ping responses. + /// The handler is responsible to reconfigure the tunnel and call `TunnelMonitorProtocol.start(probeAddress:)` to resume connection monitoring. + case connectionLost + + /// Dispatched when network reachability changes. + case networkReachabilityChanged(_ isNetworkReachable: Bool) +} + +public protocol TunnelMonitorProtocol { + /// Event handler that starts receiving events after the call to `start(probeAddress:)`. + var onEvent: ((TunnelMonitorEvent) -> Void)? { get set } + + /// Start monitoring connection by pinging the given IP address. + /// Normally we should only give an address of a tunnel gateway here which is reachable over tunnel interface. + func start(probeAddress: IPv4Address) + + /// Stop monitoring connection. + func stop() + + /// Restarts internal timers and gracefully handles transition from sleep to awake device state. + /// Call this method when packet tunnel provider receives a wake event. + func onWake() + + /// 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() +} diff --git a/ios/PacketTunnelCore/TunnelMonitor/WgStats.swift b/ios/PacketTunnelCore/TunnelMonitor/WgStats.swift new file mode 100644 index 0000000000..ac343bba32 --- /dev/null +++ b/ios/PacketTunnelCore/TunnelMonitor/WgStats.swift @@ -0,0 +1,19 @@ +// +// WgStats.swift +// PacketTunnelCore +// +// Created by pronebird on 08/08/2022. +// Copyright © 2022 Mullvad VPN AB. All rights reserved. +// + +import Foundation + +public struct WgStats { + public let bytesReceived: UInt64 + public let bytesSent: UInt64 + + public init(bytesReceived: UInt64 = 0, bytesSent: UInt64 = 0) { + self.bytesReceived = bytesReceived + self.bytesSent = bytesSent + } +} diff --git a/ios/PacketTunnelCoreTests/PingerTests.swift b/ios/PacketTunnelCoreTests/PingerTests.swift new file mode 100644 index 0000000000..d77e9c4e5d --- /dev/null +++ b/ios/PacketTunnelCoreTests/PingerTests.swift @@ -0,0 +1,32 @@ +// +// PingerTests.swift +// PacketTunnelCoreTests +// +// Created by pronebird on 11/08/2023. +// Copyright © 2023 Mullvad VPN AB. All rights reserved. +// + +import Network +import PacketTunnelCore +import XCTest + +final class PingerTests: XCTestCase { + func testPingingLocalhost() throws { + let expectation = self.expectation(description: "Wait for ping reply.") + let pinger = Pinger(identifier: 1234, replyQueue: .main) + + var sendResult: PingerSendResult? + + pinger.onReply = { reply in + if case let .success(sender, sequenceNumber) = reply, sendResult?.sequenceNumber == sequenceNumber { + XCTAssertTrue(sender.isLoopback) + expectation.fulfill() + } + } + + try pinger.openSocket(bindTo: "lo0") + sendResult = try pinger.send(to: .loopback) + + waitForExpectations(timeout: 1) + } +} diff --git a/ios/TestPlans/MullvadVPNApp.xctestplan b/ios/TestPlans/MullvadVPNApp.xctestplan index 14e6322dd2..72cc666962 100644 --- a/ios/TestPlans/MullvadVPNApp.xctestplan +++ b/ios/TestPlans/MullvadVPNApp.xctestplan @@ -48,6 +48,13 @@ "identifier" : "589A455128E094B300565204", "name" : "OperationsTests" } + }, + { + "target" : { + "containerPath" : "container:MullvadVPN.xcodeproj", + "identifier" : "58C7A43C2A863F450060C66F", + "name" : "PacketTunnelCoreTests" + } } ], "version" : 1 |
