mirror of
https://github.com/outbackdingo/firezone.git
synced 2026-01-27 18:18:55 +00:00
chore(connlib): Add external ID to FFI, return fd in on_set_interface_config (#1945)
(Supersedes #1944) * Fixes https://github.com/firezone/product/issues/649 * Passes `dns_fallback_strategy` over FFI (these are hardcoded for now) * Incorporates @conectado 's #1944 and cleans up a few places `fd` was still passed Draft for now until I can test it more tomorrow --------- Co-authored-by: conectado <gabrielalejandro7@gmail.com>
This commit is contained in:
20
.github/workflows/rust.yml
vendored
20
.github/workflows/rust.yml
vendored
@@ -30,6 +30,20 @@ jobs:
|
||||
- macos-12
|
||||
- windows-2019
|
||||
- windows-2022
|
||||
# TODO: https://github.com/rust-lang/cargo/issues/5220
|
||||
include:
|
||||
- runs-on: ubuntu-20.04
|
||||
packages: -p headless -p gateway
|
||||
- runs-on: ubuntu-22.04
|
||||
packages: -p headless -p gateway
|
||||
- runs-on: macos-11
|
||||
packages: -p connlib-apple
|
||||
- runs-on: macos-12
|
||||
packages: -p connlib-apple
|
||||
- runs-on: windows-2019
|
||||
packages: -p firezone-client-connlib
|
||||
- runs-on: windows-2022
|
||||
packages: -p firezone-client-connlib
|
||||
runs-on: ${{ matrix.runs-on }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@@ -64,11 +78,11 @@ jobs:
|
||||
mv target/tools/windows/nasm/nasm.exe target/tools/nasm.exe
|
||||
|
||||
- run: cargo fmt -- --check
|
||||
- run: cargo doc --all-features --no-deps --document-private-items
|
||||
- run: cargo doc --all-features --no-deps --document-private-items ${{ matrix.packages }}
|
||||
env:
|
||||
RUSTDOCFLAGS: "-D warnings"
|
||||
- run: cargo clippy --all-targets --all-features -- -D warnings
|
||||
- run: cargo test --all-features
|
||||
- run: cargo clippy --all-targets --all-features ${{ matrix.packages }} -- -D warnings
|
||||
- run: cargo test --all-features ${{ matrix.packages }}
|
||||
|
||||
rust_smoke-test-relay:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -2,6 +2,7 @@ package dev.firezone.android.features.session.backend
|
||||
|
||||
import android.net.VpnService
|
||||
import android.util.Log
|
||||
import android.provider.Settings
|
||||
import dev.firezone.android.BuildConfig
|
||||
import dev.firezone.android.core.domain.preference.GetConfigUseCase
|
||||
import dev.firezone.android.core.domain.preference.SaveIsConnectedUseCase
|
||||
@@ -26,20 +27,15 @@ internal class SessionManager @Inject constructor(
|
||||
Log.d("Connlib", "token: ${config.token}")
|
||||
|
||||
if (config.accountId != null && config.token != null) {
|
||||
Log.d("Connlib", "Attempting to establish VPN connection...")
|
||||
buildVpnService().establish()?.let {
|
||||
Log.d("Connlib", "VPN connection established! Attempting to start connlib session...")
|
||||
sessionPtr = TunnelSession.connect(
|
||||
it.detachFd(),
|
||||
BuildConfig.CONTROL_PLANE_URL,
|
||||
config.token,
|
||||
TunnelCallbacks()
|
||||
)
|
||||
Log.d("Connlib", "connlib session started! sessionPtr: $sessionPtr")
|
||||
setConnectionStatus(true)
|
||||
} ?: let {
|
||||
Log.d("Connlib", "Failed to build VpnService")
|
||||
}
|
||||
Log.d("Connlib", "Attempting to establish TunnelSession...")
|
||||
sessionPtr = TunnelSession.connect(
|
||||
BuildConfig.CONTROL_PLANE_URL,
|
||||
config.token,
|
||||
Settings.Secure.ANDROID_ID,
|
||||
TunnelCallbacks()
|
||||
)
|
||||
Log.d("Connlib", "connlib session started! sessionPtr: $sessionPtr")
|
||||
setConnectionStatus(true)
|
||||
}
|
||||
} catch (exception: Exception) {
|
||||
Log.e("Connection error:", exception.message.toString())
|
||||
@@ -59,22 +55,7 @@ internal class SessionManager @Inject constructor(
|
||||
saveIsConnectedUseCase.sync(value)
|
||||
}
|
||||
|
||||
private fun buildVpnService(): VpnService.Builder =
|
||||
TunnelService().Builder().apply {
|
||||
// Add a dummy address for now. Needed for the "establish" call to succeed.
|
||||
// TODO: Remove these in favor of connecting the TunnelSession *without* the fd, and then
|
||||
// returning the fd in the onSetInterfaceConfig callback. This is being worked on by @conectado
|
||||
addAddress("100.100.111.1", 32)
|
||||
addAddress("fd00:2021:1111::100:100:111:1", 128)
|
||||
|
||||
// TODO: These are the staging Resources. Remove these in favor of the onUpdateResources callback.
|
||||
addRoute("172.31.93.123", 32)
|
||||
addRoute("172.31.83.10", 32)
|
||||
addRoute("172.31.82.179", 32)
|
||||
|
||||
setSession("Firezone VPN")
|
||||
setMtu(1280)
|
||||
}
|
||||
|
||||
internal companion object {
|
||||
var sessionPtr: Long? = null
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package dev.firezone.android.tunnel
|
||||
|
||||
import android.net.VpnService
|
||||
import android.util.Log
|
||||
|
||||
class TunnelCallbacks {
|
||||
@@ -13,8 +14,9 @@ class TunnelCallbacks {
|
||||
tunnelAddressIPv6: String,
|
||||
dnsAddress: String,
|
||||
dnsFallbackStrategy: String,
|
||||
) {
|
||||
): Int {
|
||||
Log.d(TunnelCallbacks.TAG, "onSetInterfaceConfig: [IPv4:$tunnelAddressIPv4] [IPv6:$tunnelAddressIPv6] [dns:$dnsAddress] [dnsFallbackStrategy:$dnsFallbackStrategy]")
|
||||
return buildVpnService(tunnelAddressIPv4, tunnelAddressIPv6).establish()?.detachFd() ?: -1
|
||||
}
|
||||
|
||||
fun onTunnelReady(): Boolean {
|
||||
@@ -42,6 +44,21 @@ class TunnelCallbacks {
|
||||
|
||||
return true
|
||||
}
|
||||
private fun buildVpnService(ipv4Address: String, ipv6Address: String): VpnService.Builder =
|
||||
TunnelService().Builder().apply {
|
||||
addAddress(ipv4Address, 32)
|
||||
addAddress(ipv6Address, 128)
|
||||
|
||||
// TODO: These are the staging Resources. Remove these in favor of the onUpdateResources callback.
|
||||
addRoute("172.31.93.123", 32)
|
||||
addRoute("172.31.83.10", 32)
|
||||
addRoute("172.31.82.179", 32)
|
||||
|
||||
setSession("Firezone VPN")
|
||||
|
||||
// TODO: Can we do better?
|
||||
setMtu(1280)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "TunnelCallbacks"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package dev.firezone.android.tunnel
|
||||
|
||||
object TunnelSession {
|
||||
external fun connect(fd: Int, controlPlaneUrl: String, token: String, callback: Any): Long
|
||||
external fun connect(controlPlaneUrl: String, token: String, externalId: String, callback: Any): Long
|
||||
external fun disconnect(session: Long): Boolean
|
||||
}
|
||||
|
||||
9
kotlin/android/local.properties.template
Normal file
9
kotlin/android/local.properties.template
Normal file
@@ -0,0 +1,9 @@
|
||||
# Copy this file to local.properties and define the variables below.
|
||||
#
|
||||
# Location of the SDK. This is only used by Gradle. For customization when using a Version Control System, please read the
|
||||
# header note.
|
||||
# e.g. /Users/jamil/Library/Android/sdk
|
||||
sdk.dir=
|
||||
|
||||
# Auth token from portal to use for debugUser in order to bypass the auth flow and send to connlib.
|
||||
token=
|
||||
165
rust/Cargo.lock
generated
165
rust/Cargo.lock
generated
@@ -124,6 +124,15 @@ dependencies = [
|
||||
"opaque-debug",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.6.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81ce3d38065e618af2d7b77e10c5ad9a069859b4be3c2250f674af3840d9c8a5"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.0.2"
|
||||
@@ -792,16 +801,32 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25b9e03f145fd4f2bf705e07b900cd41fc636598fe5dc452fd0db1441c3f496d"
|
||||
dependencies = [
|
||||
"core-foundation-sys 0.6.2",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"core-foundation-sys 0.8.4",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b"
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.4"
|
||||
@@ -1007,6 +1032,19 @@ dependencies = [
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dmidecode"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "35bbcc83e06814bcafa454ec0586c41d3000db69c0451208119270c05b840247"
|
||||
dependencies = [
|
||||
"aho-corasick 0.6.10",
|
||||
"bitflags 1.3.2",
|
||||
"failure",
|
||||
"failure_derive",
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "domain"
|
||||
version = "0.8.0"
|
||||
@@ -1098,6 +1136,28 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "failure"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"failure_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "failure_derive"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "1.9.0"
|
||||
@@ -1314,6 +1374,15 @@ dependencies = [
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getopts"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
|
||||
dependencies = [
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.10"
|
||||
@@ -1365,6 +1434,7 @@ dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
"ctrlc",
|
||||
"dmidecode",
|
||||
"firezone-client-connlib",
|
||||
"ip_network",
|
||||
"tracing",
|
||||
@@ -1493,7 +1563,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"core-foundation-sys",
|
||||
"core-foundation-sys 0.8.4",
|
||||
"iana-time-zone-haiku",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
@@ -1540,7 +1610,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "interceptor"
|
||||
version = "0.9.0"
|
||||
source = "git+https://github.com/firezone/webrtc?rev=9ddd589#9ddd5897e6f27b65261f2b9cf38e0d8649af2360"
|
||||
source = "git+https://github.com/firezone/webrtc?rev=672e728#672e728b2386e17d80c210a8be6ec364916ffb17"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"bytes",
|
||||
@@ -1555,6 +1625,16 @@ dependencies = [
|
||||
"webrtc-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "io-kit-sys"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f21dcc74995dd4cd090b147e79789f8d65959cbfb5f0b118002db869ea3bd0a0"
|
||||
dependencies = [
|
||||
"core-foundation-sys 0.6.2",
|
||||
"mach 0.2.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "io-lifetimes"
|
||||
version = "1.0.11"
|
||||
@@ -1744,6 +1824,7 @@ dependencies = [
|
||||
"rtnetlink",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"smbios-lib",
|
||||
"swift-bridge",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
@@ -1782,6 +1863,24 @@ version = "0.4.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
||||
|
||||
[[package]]
|
||||
name = "mach"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86dd2487cdfea56def77b88438a2c915fb45113c5319bfe7e14306ca4cd0b0e1"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mach"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "matchers"
|
||||
version = "0.1.0"
|
||||
@@ -2454,7 +2553,7 @@ version = "1.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81bc1d4caf89fac26a70747fe603c130093b53c773888797a6329091246d651a"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"aho-corasick 1.0.2",
|
||||
"memchr",
|
||||
"regex-automata 0.3.6",
|
||||
"regex-syntax 0.7.4",
|
||||
@@ -2475,7 +2574,7 @@ version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fed1ceff11a1dddaee50c9dc8e4938bd106e9d89ae372f192311e7da498e3b69"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"aho-corasick 1.0.2",
|
||||
"memchr",
|
||||
"regex-syntax 0.7.4",
|
||||
]
|
||||
@@ -2557,7 +2656,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rtcp"
|
||||
version = "0.9.0"
|
||||
source = "git+https://github.com/firezone/webrtc?rev=9ddd589#9ddd5897e6f27b65261f2b9cf38e0d8649af2360"
|
||||
source = "git+https://github.com/firezone/webrtc?rev=672e728#672e728b2386e17d80c210a8be6ec364916ffb17"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"thiserror",
|
||||
@@ -2585,7 +2684,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rtp"
|
||||
version = "0.8.0"
|
||||
source = "git+https://github.com/firezone/webrtc?rev=9ddd589#9ddd5897e6f27b65261f2b9cf38e0d8649af2360"
|
||||
source = "git+https://github.com/firezone/webrtc?rev=672e728#672e728b2386e17d80c210a8be6ec364916ffb17"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"rand",
|
||||
@@ -2778,7 +2877,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "sdp"
|
||||
version = "0.5.3"
|
||||
source = "git+https://github.com/firezone/webrtc?rev=9ddd589#9ddd5897e6f27b65261f2b9cf38e0d8649af2360"
|
||||
source = "git+https://github.com/firezone/webrtc?rev=672e728#672e728b2386e17d80c210a8be6ec364916ffb17"
|
||||
dependencies = [
|
||||
"rand",
|
||||
"substring",
|
||||
@@ -2807,8 +2906,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"core-foundation",
|
||||
"core-foundation-sys",
|
||||
"core-foundation 0.9.3",
|
||||
"core-foundation-sys 0.8.4",
|
||||
"libc",
|
||||
"security-framework-sys",
|
||||
]
|
||||
@@ -2819,7 +2918,7 @@ version = "2.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"core-foundation-sys 0.8.4",
|
||||
"libc",
|
||||
]
|
||||
|
||||
@@ -2937,6 +3036,22 @@ version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
|
||||
|
||||
[[package]]
|
||||
name = "smbios-lib"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "923b9d6161c2f7e29070f0b8642a9cf9f53bffd9bcf6c12cf0e295387f2fbaaa"
|
||||
dependencies = [
|
||||
"core-foundation 0.6.4",
|
||||
"core-foundation-sys 0.6.2",
|
||||
"getopts",
|
||||
"io-kit-sys",
|
||||
"libc",
|
||||
"mach 0.3.2",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smol_str"
|
||||
version = "0.2.0"
|
||||
@@ -3020,7 +3135,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "stun"
|
||||
version = "0.4.4"
|
||||
source = "git+https://github.com/firezone/webrtc?rev=9ddd589#9ddd5897e6f27b65261f2b9cf38e0d8649af2360"
|
||||
source = "git+https://github.com/firezone/webrtc?rev=672e728#672e728b2386e17d80c210a8be6ec364916ffb17"
|
||||
dependencies = [
|
||||
"base64 0.21.2",
|
||||
"crc",
|
||||
@@ -3530,7 +3645,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "turn"
|
||||
version = "0.6.1"
|
||||
source = "git+https://github.com/firezone/webrtc?rev=9ddd589#9ddd5897e6f27b65261f2b9cf38e0d8649af2360"
|
||||
source = "git+https://github.com/firezone/webrtc?rev=672e728#672e728b2386e17d80c210a8be6ec364916ffb17"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"base64 0.21.2",
|
||||
@@ -3578,6 +3693,12 @@ dependencies = [
|
||||
"tinyvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.4"
|
||||
@@ -3797,7 +3918,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "webrtc"
|
||||
version = "0.8.0"
|
||||
source = "git+https://github.com/firezone/webrtc?rev=9ddd589#9ddd5897e6f27b65261f2b9cf38e0d8649af2360"
|
||||
source = "git+https://github.com/firezone/webrtc?rev=672e728#672e728b2386e17d80c210a8be6ec364916ffb17"
|
||||
dependencies = [
|
||||
"arc-swap",
|
||||
"async-trait",
|
||||
@@ -3839,7 +3960,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "webrtc-data"
|
||||
version = "0.7.0"
|
||||
source = "git+https://github.com/firezone/webrtc?rev=9ddd589#9ddd5897e6f27b65261f2b9cf38e0d8649af2360"
|
||||
source = "git+https://github.com/firezone/webrtc?rev=672e728#672e728b2386e17d80c210a8be6ec364916ffb17"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"log",
|
||||
@@ -3852,7 +3973,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "webrtc-dtls"
|
||||
version = "0.7.2"
|
||||
source = "git+https://github.com/firezone/webrtc?rev=9ddd589#9ddd5897e6f27b65261f2b9cf38e0d8649af2360"
|
||||
source = "git+https://github.com/firezone/webrtc?rev=672e728#672e728b2386e17d80c210a8be6ec364916ffb17"
|
||||
dependencies = [
|
||||
"aes 0.6.0",
|
||||
"aes-gcm",
|
||||
@@ -3888,7 +4009,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "webrtc-ice"
|
||||
version = "0.9.1"
|
||||
source = "git+https://github.com/firezone/webrtc?rev=9ddd589#9ddd5897e6f27b65261f2b9cf38e0d8649af2360"
|
||||
source = "git+https://github.com/firezone/webrtc?rev=672e728#672e728b2386e17d80c210a8be6ec364916ffb17"
|
||||
dependencies = [
|
||||
"arc-swap",
|
||||
"async-trait",
|
||||
@@ -3911,7 +4032,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "webrtc-mdns"
|
||||
version = "0.5.2"
|
||||
source = "git+https://github.com/firezone/webrtc?rev=9ddd589#9ddd5897e6f27b65261f2b9cf38e0d8649af2360"
|
||||
source = "git+https://github.com/firezone/webrtc?rev=672e728#672e728b2386e17d80c210a8be6ec364916ffb17"
|
||||
dependencies = [
|
||||
"log",
|
||||
"socket2 0.4.9",
|
||||
@@ -3923,7 +4044,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "webrtc-media"
|
||||
version = "0.6.1"
|
||||
source = "git+https://github.com/firezone/webrtc?rev=9ddd589#9ddd5897e6f27b65261f2b9cf38e0d8649af2360"
|
||||
source = "git+https://github.com/firezone/webrtc?rev=672e728#672e728b2386e17d80c210a8be6ec364916ffb17"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"bytes",
|
||||
@@ -3935,7 +4056,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "webrtc-sctp"
|
||||
version = "0.8.0"
|
||||
source = "git+https://github.com/firezone/webrtc?rev=9ddd589#9ddd5897e6f27b65261f2b9cf38e0d8649af2360"
|
||||
source = "git+https://github.com/firezone/webrtc?rev=672e728#672e728b2386e17d80c210a8be6ec364916ffb17"
|
||||
dependencies = [
|
||||
"arc-swap",
|
||||
"async-trait",
|
||||
@@ -3951,7 +4072,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "webrtc-srtp"
|
||||
version = "0.10.0"
|
||||
source = "git+https://github.com/firezone/webrtc?rev=9ddd589#9ddd5897e6f27b65261f2b9cf38e0d8649af2360"
|
||||
source = "git+https://github.com/firezone/webrtc?rev=672e728#672e728b2386e17d80c210a8be6ec364916ffb17"
|
||||
dependencies = [
|
||||
"aead 0.4.3",
|
||||
"aes 0.7.5",
|
||||
@@ -3973,7 +4094,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "webrtc-util"
|
||||
version = "0.7.0"
|
||||
source = "git+https://github.com/firezone/webrtc?rev=9ddd589#9ddd5897e6f27b65261f2b9cf38e0d8649af2360"
|
||||
source = "git+https://github.com/firezone/webrtc?rev=672e728#672e728b2386e17d80c210a8be6ec364916ffb17"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"bitflags 1.3.2",
|
||||
|
||||
@@ -22,4 +22,4 @@ backoff = { version = "0.4", features = ["tokio"] }
|
||||
# (the `patch` section can't be used for build deps...)
|
||||
[patch.crates-io]
|
||||
ring = { git = "https://github.com/firezone/ring", branch = "v0.16.20-cc-fix" }
|
||||
webrtc = { git = "https://github.com/firezone/webrtc", rev = "9ddd589" }
|
||||
webrtc = { git = "https://github.com/firezone/webrtc", rev = "672e728" }
|
||||
|
||||
@@ -8,14 +8,14 @@ use ip_network::IpNetwork;
|
||||
use jni::{
|
||||
objects::{GlobalRef, JClass, JObject, JString, JValue},
|
||||
strings::JNIString,
|
||||
sys::jint,
|
||||
JNIEnv, JavaVM,
|
||||
};
|
||||
use std::net::{Ipv4Addr, Ipv6Addr};
|
||||
use std::{
|
||||
net::{Ipv4Addr, Ipv6Addr},
|
||||
os::fd::RawFd,
|
||||
};
|
||||
use thiserror::Error;
|
||||
|
||||
const DNS_FALLBACK_STRATEGY: &str = "upstream_resolver";
|
||||
|
||||
/// This should be called once after the library is loaded by the system.
|
||||
#[allow(non_snake_case)]
|
||||
#[no_mangle]
|
||||
@@ -97,7 +97,8 @@ impl Callbacks for CallbackHandler {
|
||||
tunnel_address_v4: Ipv4Addr,
|
||||
tunnel_address_v6: Ipv6Addr,
|
||||
dns_address: Ipv4Addr,
|
||||
) -> Result<(), Self::Error> {
|
||||
dns_fallback_strategy: String,
|
||||
) -> Result<RawFd, Self::Error> {
|
||||
self.env(|mut env| {
|
||||
let tunnel_address_v4 =
|
||||
env.new_string(tunnel_address_v4.to_string())
|
||||
@@ -118,17 +119,18 @@ impl Callbacks for CallbackHandler {
|
||||
}
|
||||
})?;
|
||||
let dns_fallback_strategy =
|
||||
env.new_string(DNS_FALLBACK_STRATEGY).map_err(|source| {
|
||||
env.new_string(dns_fallback_strategy).map_err(|source| {
|
||||
CallbackError::NewStringFailed {
|
||||
name: "dns_fallback_strategy",
|
||||
source,
|
||||
}
|
||||
})?;
|
||||
call_method(
|
||||
&mut env,
|
||||
|
||||
let name = "onSetInterfaceConfig";
|
||||
env.call_method(
|
||||
&self.callback_handler,
|
||||
"onSetInterfaceConfig",
|
||||
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V",
|
||||
name,
|
||||
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
|
||||
&[
|
||||
JValue::from(&tunnel_address_v4),
|
||||
JValue::from(&tunnel_address_v6),
|
||||
@@ -136,6 +138,8 @@ impl Callbacks for CallbackHandler {
|
||||
JValue::from(&dns_fallback_strategy),
|
||||
],
|
||||
)
|
||||
.and_then(|val| val.i())
|
||||
.map_err(|source| CallbackError::CallMethodFailed { name, source })
|
||||
})
|
||||
}
|
||||
|
||||
@@ -283,9 +287,9 @@ enum ConnectError {
|
||||
|
||||
fn connect(
|
||||
env: &mut JNIEnv,
|
||||
fd: jint,
|
||||
portal_url: JString,
|
||||
portal_token: JString,
|
||||
external_id: JString,
|
||||
callback_handler: GlobalRef,
|
||||
) -> Result<Session<CallbackHandler>, ConnectError> {
|
||||
let portal_url = String::from(env.get_string(&portal_url).map_err(|source| {
|
||||
@@ -305,10 +309,17 @@ fn connect(
|
||||
vm: env.get_java_vm().map_err(ConnectError::GetJavaVmFailed)?,
|
||||
callback_handler,
|
||||
};
|
||||
let external_id = env
|
||||
.get_string(&external_id)
|
||||
.map_err(|source| ConnectError::StringInvalid {
|
||||
name: "external_id",
|
||||
source,
|
||||
})?
|
||||
.into();
|
||||
Session::connect(
|
||||
Some(fd),
|
||||
portal_url.as_str(),
|
||||
portal_token,
|
||||
external_id,
|
||||
callback_handler,
|
||||
)
|
||||
.map_err(Into::into)
|
||||
@@ -322,15 +333,15 @@ fn connect(
|
||||
pub unsafe extern "system" fn Java_dev_firezone_android_tunnel_TunnelSession_connect(
|
||||
mut env: JNIEnv,
|
||||
_class: JClass,
|
||||
fd: jint,
|
||||
portal_url: JString,
|
||||
portal_token: JString,
|
||||
external_id: JString,
|
||||
callback_handler: JObject,
|
||||
) -> *const Session<CallbackHandler> {
|
||||
let Ok(callback_handler) = env.new_global_ref(callback_handler) else { return std::ptr::null() };
|
||||
|
||||
if let Some(result) = catch_and_throw(&mut env, |env| {
|
||||
connect(env, fd, portal_url, portal_token, callback_handler)
|
||||
connect(env, portal_url, portal_token, external_id, callback_handler)
|
||||
}) {
|
||||
match result {
|
||||
Ok(session) => return Box::into_raw(Box::new(session)),
|
||||
|
||||
@@ -29,13 +29,13 @@ public class CallbackHandler {
|
||||
public weak var delegate: CallbackHandlerDelegate?
|
||||
private let logger = Logger.make(for: CallbackHandler.self)
|
||||
|
||||
func onSetInterfaceConfig(tunnelAddressIPv4: RustString, tunnelAddressIPv6: RustString, dnsAddress: RustString) {
|
||||
func onSetInterfaceConfig(tunnelAddressIPv4: RustString, tunnelAddressIPv6: RustString, dnsAddress: RustString, dnsFallbackStrategy: RustString) {
|
||||
logger.debug("CallbackHandler.onSetInterfaceConfig: IPv4: \(tunnelAddressIPv4.toString(), privacy: .public), IPv6: \(tunnelAddressIPv6.toString(), privacy: .public), DNS: \(dnsAddress.toString(), privacy: .public)")
|
||||
delegate?.onSetInterfaceConfig(
|
||||
tunnelAddressIPv4: tunnelAddressIPv4.toString(),
|
||||
tunnelAddressIPv6: tunnelAddressIPv6.toString(),
|
||||
dnsAddress: dnsAddress.toString(),
|
||||
dnsFallbackStrategy: "system_resolver" // Will come from a onSetInterfaceConfig arg eventually
|
||||
dnsFallbackStrategy: dnsFallbackStrategy.toString()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ use firezone_client_connlib::{Callbacks, Error, ResourceDescription, Session};
|
||||
use ip_network::IpNetwork;
|
||||
use std::{
|
||||
net::{Ipv4Addr, Ipv6Addr},
|
||||
os::fd::RawFd,
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
@@ -18,6 +19,7 @@ mod ffi {
|
||||
fn connect(
|
||||
portal_url: String,
|
||||
token: String,
|
||||
external_id: String,
|
||||
callback_handler: CallbackHandler,
|
||||
) -> Result<WrappedSession, String>;
|
||||
|
||||
@@ -39,6 +41,7 @@ mod ffi {
|
||||
tunnelAddressIPv4: String,
|
||||
tunnelAddressIPv6: String,
|
||||
dnsAddress: String,
|
||||
dnsFallbackStrategy: String,
|
||||
);
|
||||
|
||||
#[swift_bridge(swift_name = "onTunnelReady")]
|
||||
@@ -86,13 +89,15 @@ impl Callbacks for CallbackHandler {
|
||||
tunnel_address_v4: Ipv4Addr,
|
||||
tunnel_address_v6: Ipv6Addr,
|
||||
dns_address: Ipv4Addr,
|
||||
) -> Result<(), Self::Error> {
|
||||
dns_fallback_strategy: String,
|
||||
) -> Result<RawFd, Self::Error> {
|
||||
self.0.on_set_interface_config(
|
||||
tunnel_address_v4.to_string(),
|
||||
tunnel_address_v6.to_string(),
|
||||
dns_address.to_string(),
|
||||
dns_fallback_strategy.to_string(),
|
||||
);
|
||||
Ok(())
|
||||
Ok(-1)
|
||||
}
|
||||
|
||||
fn on_tunnel_ready(&self) -> Result<(), Self::Error> {
|
||||
@@ -149,13 +154,14 @@ impl WrappedSession {
|
||||
fn connect(
|
||||
portal_url: String,
|
||||
token: String,
|
||||
external_id: String,
|
||||
callback_handler: ffi::CallbackHandler,
|
||||
) -> Result<Self, String> {
|
||||
init_logging();
|
||||
Session::connect(
|
||||
None,
|
||||
portal_url.as_str(),
|
||||
token,
|
||||
external_id,
|
||||
CallbackHandler(callback_handler.into()),
|
||||
)
|
||||
.map(|session| Self { session })
|
||||
|
||||
@@ -6,6 +6,7 @@ edition = "2021"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
dmidecode = "0.7"
|
||||
firezone-client-connlib = { path = "../../libs/client" }
|
||||
ip_network = "0.4"
|
||||
url = { version = "2.3.1", default-features = false }
|
||||
|
||||
@@ -3,10 +3,13 @@ use clap::Parser;
|
||||
use ip_network::IpNetwork;
|
||||
use std::{
|
||||
net::{Ipv4Addr, Ipv6Addr},
|
||||
os::fd::RawFd,
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
use firezone_client_connlib::{get_user_agent, Callbacks, Error, ResourceDescription, Session};
|
||||
use firezone_client_connlib::{
|
||||
get_external_id, get_user_agent, Callbacks, Error, ResourceDescription, Session,
|
||||
};
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -20,8 +23,9 @@ impl Callbacks for CallbackHandler {
|
||||
_tunnel_address_v4: Ipv4Addr,
|
||||
_tunnel_address_v6: Ipv6Addr,
|
||||
_dns_address: Ipv4Addr,
|
||||
) -> Result<(), Self::Error> {
|
||||
Ok(())
|
||||
_dns_fallback_strategy: String,
|
||||
) -> Result<RawFd, Self::Error> {
|
||||
Ok(-1)
|
||||
}
|
||||
|
||||
fn on_tunnel_ready(&self) -> Result<(), Self::Error> {
|
||||
@@ -41,7 +45,7 @@ impl Callbacks for CallbackHandler {
|
||||
&self,
|
||||
resource_list: Vec<ResourceDescription>,
|
||||
) -> Result<(), Self::Error> {
|
||||
tracing::trace!("Resources updated, current list: {resource_list:?}");
|
||||
tracing::trace!(message = "Resources updated", ?resource_list);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -78,7 +82,8 @@ fn main() -> Result<()> {
|
||||
// TODO: allow passing as arg vars
|
||||
let url = parse_env_var::<Url>(URL_ENV_VAR)?;
|
||||
let secret = parse_env_var::<String>(SECRET_ENV_VAR)?;
|
||||
let mut session = Session::connect(None, url, secret, CallbackHandler).unwrap();
|
||||
let external_id = get_external_id();
|
||||
let mut session = Session::connect(url, secret, external_id, CallbackHandler).unwrap();
|
||||
tracing::info!("Started new session");
|
||||
|
||||
block_on_ctrl_c();
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
use anyhow::{Context, Result};
|
||||
use ip_network::IpNetwork;
|
||||
use std::os::fd::RawFd;
|
||||
use std::{
|
||||
net::{Ipv4Addr, Ipv6Addr},
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
use firezone_gateway_connlib::{Callbacks, Error, ResourceDescription, Session};
|
||||
use firezone_gateway_connlib::{get_external_id, Callbacks, Error, ResourceDescription, Session};
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -19,8 +20,9 @@ impl Callbacks for CallbackHandler {
|
||||
_tunnel_address_v4: Ipv4Addr,
|
||||
_tunnel_address_v6: Ipv6Addr,
|
||||
_dns_address: Ipv4Addr,
|
||||
) -> Result<(), Self::Error> {
|
||||
Ok(())
|
||||
_dns_fallback_strategy: String,
|
||||
) -> Result<RawFd, Self::Error> {
|
||||
Ok(-1)
|
||||
}
|
||||
|
||||
fn on_tunnel_ready(&self) -> Result<(), Self::Error> {
|
||||
@@ -64,7 +66,8 @@ fn main() -> Result<()> {
|
||||
// TODO: allow passing as arg vars
|
||||
let url = parse_env_var::<Url>(URL_ENV_VAR)?;
|
||||
let secret = parse_env_var::<String>(SECRET_ENV_VAR)?;
|
||||
let mut session = Session::connect(None, url, secret, CallbackHandler).unwrap();
|
||||
let external_id = get_external_id();
|
||||
let mut session = Session::connect(url, secret, external_id, CallbackHandler).unwrap();
|
||||
|
||||
let (tx, rx) = std::sync::mpsc::channel();
|
||||
ctrlc::set_handler(move || tx.send(()).expect("Could not send stop signal on channel."))
|
||||
|
||||
@@ -234,15 +234,13 @@ impl<CB: Callbacks + 'static> ControlPlane<CB> {
|
||||
impl<CB: Callbacks + 'static> ControlSession<Messages, CB> for ControlPlane<CB> {
|
||||
#[tracing::instrument(level = "trace", skip(private_key, callbacks))]
|
||||
async fn start(
|
||||
fd: Option<i32>,
|
||||
private_key: StaticSecret,
|
||||
receiver: Receiver<MessageResult<Messages>>,
|
||||
control_signal: PhoenixSenderWithTopic,
|
||||
callbacks: CB,
|
||||
) -> Result<()> {
|
||||
let control_signaler = ControlSignaler { control_signal };
|
||||
let tunnel =
|
||||
Arc::new(Tunnel::new(fd, private_key, control_signaler.clone(), callbacks).await?);
|
||||
let tunnel = Arc::new(Tunnel::new(private_key, control_signaler.clone(), callbacks).await?);
|
||||
|
||||
let control_plane = ControlPlane {
|
||||
tunnel,
|
||||
|
||||
@@ -18,6 +18,8 @@ pub type Session<CB> = libs_common::Session<
|
||||
CB,
|
||||
>;
|
||||
|
||||
pub use libs_common::{get_user_agent, messages::ResourceDescription, Callbacks, Error};
|
||||
pub use libs_common::{
|
||||
get_external_id, get_user_agent, messages::ResourceDescription, Callbacks, Error,
|
||||
};
|
||||
use messages::Messages;
|
||||
use messages::ReplyMessages;
|
||||
|
||||
@@ -34,6 +34,10 @@ parking_lot = "0.12"
|
||||
# Needed for Android logging until tracing is working
|
||||
log = "0.4"
|
||||
|
||||
# smbios fails to build on iOS and Android
|
||||
[target.'cfg(not(any(target_os = "ios", target_os = "android")))'.dependencies]
|
||||
smbios-lib = "0.9"
|
||||
|
||||
[target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies]
|
||||
swift-bridge = { workspace = true }
|
||||
|
||||
|
||||
3
rust/connlib/libs/common/src/device_ref_unix.rs
Normal file
3
rust/connlib/libs/common/src/device_ref_unix.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
pub struct DeviceRef {
|
||||
device_ref: std::os::fd::RawFd,
|
||||
}
|
||||
3
rust/connlib/libs/common/src/device_ref_win.rs
Normal file
3
rust/connlib/libs/common/src/device_ref_win.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
pub struct DeviceRef {
|
||||
device_ref: std::os::windows::io::RawHandle,
|
||||
}
|
||||
@@ -26,3 +26,24 @@ pub fn get_user_agent() -> String {
|
||||
let lib_name = LIB_NAME;
|
||||
format!("{os_type}/{os_version} {lib_name}/{lib_version}")
|
||||
}
|
||||
|
||||
/// Returns the SMBios Serial of the device, or a random UUIDv4 if it can't be found.
|
||||
pub fn get_external_id() -> String {
|
||||
// smbios fails to build on mobile, but it works for other platforms.
|
||||
#[cfg(not(any(target_os = "ios", target_os = "android")))]
|
||||
match smbioslib::table_load_from_device() {
|
||||
Ok(data) => {
|
||||
match data.find_map(|sys_info: smbioslib::SMBiosSystemInformation| sys_info.uuid()) {
|
||||
Some(uuid) => uuid.to_string(),
|
||||
None => uuid::Uuid::new_v4().to_string(),
|
||||
}
|
||||
}
|
||||
Err(_err) => uuid::Uuid::new_v4().to_string(),
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "ios", target_os = "android"))]
|
||||
{
|
||||
tracing::debug!("smbios is not supported on iOS and Android, using random UUIDv4");
|
||||
uuid::Uuid::new_v4().to_string()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,19 +10,18 @@ use std::{
|
||||
marker::PhantomData,
|
||||
net::{Ipv4Addr, Ipv6Addr},
|
||||
result::Result as StdResult,
|
||||
time::Duration,
|
||||
};
|
||||
use tokio::{runtime::Runtime, sync::mpsc::Receiver};
|
||||
use url::Url;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
control::{MessageResult, PhoenixChannel, PhoenixSenderWithTopic},
|
||||
messages::{Key, ResourceDescription, ResourceDescriptionCidr},
|
||||
messages::{Key, ResourceDescription},
|
||||
Error, Result,
|
||||
};
|
||||
|
||||
pub const DNS_SENTINEL: Ipv4Addr = Ipv4Addr::new(100, 100, 111, 1);
|
||||
type RawFd = i32;
|
||||
|
||||
struct StopRuntime;
|
||||
|
||||
@@ -32,7 +31,6 @@ struct StopRuntime;
|
||||
pub trait ControlSession<T, CB: Callbacks> {
|
||||
/// Start control-plane with the given private-key in the background.
|
||||
async fn start(
|
||||
fd: Option<i32>,
|
||||
private_key: StaticSecret,
|
||||
receiver: Receiver<MessageResult<T>>,
|
||||
control_signal: PhoenixSenderWithTopic,
|
||||
@@ -69,7 +67,8 @@ pub trait Callbacks: Clone + Send + Sync {
|
||||
tunnel_address_v4: Ipv4Addr,
|
||||
tunnel_address_v6: Ipv6Addr,
|
||||
dns_address: Ipv4Addr,
|
||||
) -> StdResult<(), Self::Error>;
|
||||
dns_fallback_strategy: String,
|
||||
) -> StdResult<RawFd, Self::Error>;
|
||||
/// Called when the tunnel is connected.
|
||||
fn on_tunnel_ready(&self) -> StdResult<(), Self::Error>;
|
||||
/// Called when when a route is added.
|
||||
@@ -101,10 +100,16 @@ impl<CB: Callbacks> Callbacks for CallbackErrorFacade<CB> {
|
||||
tunnel_address_v4: Ipv4Addr,
|
||||
tunnel_address_v6: Ipv6Addr,
|
||||
dns_address: Ipv4Addr,
|
||||
) -> Result<()> {
|
||||
dns_fallback_strategy: String,
|
||||
) -> Result<RawFd> {
|
||||
let result = self
|
||||
.0
|
||||
.on_set_interface_config(tunnel_address_v4, tunnel_address_v6, dns_address)
|
||||
.on_set_interface_config(
|
||||
tunnel_address_v4,
|
||||
tunnel_address_v6,
|
||||
dns_address,
|
||||
dns_fallback_strategy,
|
||||
)
|
||||
.map_err(|err| Error::OnSetInterfaceConfigFailed(err.to_string()));
|
||||
if let Err(err) = result.as_ref() {
|
||||
tracing::error!("{err}");
|
||||
@@ -201,19 +206,14 @@ where
|
||||
/// 2. Connect to the control plane to the portal
|
||||
/// 3. Start the tunnel in the background and forward control plane messages to it.
|
||||
///
|
||||
/// If a fd is passed in, it's used for the tunnel interface. This is useful on Android where
|
||||
/// we can't create interfaces but we can easily get its file descriptor from the OS.
|
||||
/// If no fd is passed in, a new interface will be created (Linux) or we'll walk the fd table
|
||||
/// to find the interface (iOS/macOS).
|
||||
///
|
||||
/// The generic parameter `CB` should implement all the handlers and that's how errors will be surfaced.
|
||||
///
|
||||
/// On a fatal error you should call `[Session::disconnect]` and start a new one.
|
||||
// TODO: token should be something like SecretString but we need to think about FFI compatibility
|
||||
pub fn connect(
|
||||
fd: Option<i32>,
|
||||
portal_url: impl TryInto<Url>,
|
||||
token: String,
|
||||
external_id: String,
|
||||
callbacks: CB,
|
||||
) -> Result<Self> {
|
||||
// TODO: We could use tokio::runtime::current() to get the current runtime
|
||||
@@ -250,18 +250,14 @@ where
|
||||
}));
|
||||
}
|
||||
|
||||
if cfg!(feature = "mock") {
|
||||
Self::connect_mock(tx.clone(), this.callbacks.clone());
|
||||
} else {
|
||||
Self::connect_inner(
|
||||
&runtime,
|
||||
tx,
|
||||
fd,
|
||||
portal_url.try_into().map_err(|_| Error::UriError)?,
|
||||
token,
|
||||
this.callbacks.clone(),
|
||||
);
|
||||
}
|
||||
Self::connect_inner(
|
||||
&runtime,
|
||||
tx,
|
||||
portal_url.try_into().map_err(|_| Error::UriError)?,
|
||||
token,
|
||||
external_id,
|
||||
this.callbacks.clone(),
|
||||
);
|
||||
std::thread::spawn(move || {
|
||||
rx.blocking_recv();
|
||||
runtime.shutdown_background();
|
||||
@@ -273,18 +269,17 @@ where
|
||||
fn connect_inner(
|
||||
runtime: &Runtime,
|
||||
runtime_stopper: tokio::sync::mpsc::Sender<StopRuntime>,
|
||||
fd: Option<i32>,
|
||||
portal_url: Url,
|
||||
token: String,
|
||||
external_id: String,
|
||||
callbacks: CallbackErrorFacade<CB>,
|
||||
) {
|
||||
runtime.spawn(async move {
|
||||
let private_key = StaticSecret::random_from_rng(OsRng);
|
||||
let self_id = uuid::Uuid::new_v4();
|
||||
let name_suffix: String = thread_rng().sample_iter(&Alphanumeric).take(8).map(char::from).collect();
|
||||
|
||||
let connect_url = fatal_error!(
|
||||
get_websocket_path(portal_url, token, T::socket_path(), &Key(PublicKey::from(&private_key).to_bytes()), &self_id.to_string(), &name_suffix),
|
||||
get_websocket_path(portal_url, token, T::socket_path(), &Key(PublicKey::from(&private_key).to_bytes()), &external_id, &name_suffix),
|
||||
runtime_stopper,
|
||||
&callbacks
|
||||
);
|
||||
@@ -309,7 +304,7 @@ where
|
||||
let topic = T::socket_path().to_string();
|
||||
let internal_sender = connection.sender_with_topic(topic.clone());
|
||||
fatal_error!(
|
||||
T::start(fd, private_key, control_plane_receiver, internal_sender, callbacks.0.clone()).await,
|
||||
T::start(private_key, control_plane_receiver, internal_sender, callbacks.0.clone()).await,
|
||||
runtime_stopper,
|
||||
&callbacks
|
||||
);
|
||||
@@ -343,55 +338,6 @@ where
|
||||
});
|
||||
}
|
||||
|
||||
fn connect_mock(
|
||||
runtime_stopper: tokio::sync::mpsc::Sender<StopRuntime>,
|
||||
callbacks: CallbackErrorFacade<CB>,
|
||||
) {
|
||||
std::thread::sleep(Duration::from_secs(1));
|
||||
fatal_error!(
|
||||
callbacks.on_set_interface_config(
|
||||
"100.100.111.2".parse().unwrap(),
|
||||
"fd00:0222:2021:1111::2".parse().unwrap(),
|
||||
DNS_SENTINEL,
|
||||
),
|
||||
runtime_stopper,
|
||||
&callbacks
|
||||
);
|
||||
fatal_error!(callbacks.on_tunnel_ready(), runtime_stopper, &callbacks);
|
||||
let handle = {
|
||||
let callbacks = callbacks.clone();
|
||||
std::thread::spawn(move || -> Result<()> {
|
||||
std::thread::sleep(Duration::from_secs(3));
|
||||
let resources = vec![
|
||||
ResourceDescriptionCidr {
|
||||
id: Uuid::new_v4(),
|
||||
address: "8.8.4.4".parse::<Ipv4Addr>().unwrap().into(),
|
||||
name: "Google Public DNS IPv4".to_string(),
|
||||
},
|
||||
ResourceDescriptionCidr {
|
||||
id: Uuid::new_v4(),
|
||||
address: "2001:4860:4860::8844".parse::<Ipv6Addr>().unwrap().into(),
|
||||
name: "Google Public DNS IPv6".to_string(),
|
||||
},
|
||||
];
|
||||
for resource in &resources {
|
||||
callbacks.on_add_route(resource.address)?;
|
||||
}
|
||||
callbacks.on_update_resources(
|
||||
resources
|
||||
.into_iter()
|
||||
.map(ResourceDescription::Cidr)
|
||||
.collect(),
|
||||
)
|
||||
})
|
||||
};
|
||||
fatal_error!(
|
||||
handle.join().expect("mock thread panicked"),
|
||||
runtime_stopper,
|
||||
&callbacks
|
||||
);
|
||||
}
|
||||
|
||||
fn disconnect_inner(
|
||||
runtime_stopper: tokio::sync::mpsc::Sender<StopRuntime>,
|
||||
callbacks: &CallbackErrorFacade<CB>,
|
||||
|
||||
@@ -144,15 +144,13 @@ impl<CB: Callbacks + 'static> ControlPlane<CB> {
|
||||
impl<CB: Callbacks + 'static> ControlSession<IngressMessages, CB> for ControlPlane<CB> {
|
||||
#[tracing::instrument(level = "trace", skip(private_key, callbacks))]
|
||||
async fn start(
|
||||
fd: Option<i32>,
|
||||
private_key: StaticSecret,
|
||||
receiver: Receiver<MessageResult<IngressMessages>>,
|
||||
control_signal: PhoenixSenderWithTopic,
|
||||
callbacks: CB,
|
||||
) -> Result<()> {
|
||||
let control_signaler = ControlSignaler { control_signal };
|
||||
let tunnel =
|
||||
Arc::new(Tunnel::new(fd, private_key, control_signaler.clone(), callbacks).await?);
|
||||
let tunnel = Arc::new(Tunnel::new(private_key, control_signaler.clone(), callbacks).await?);
|
||||
|
||||
let control_plane = ControlPlane {
|
||||
tunnel,
|
||||
|
||||
@@ -19,4 +19,4 @@ pub type Session<CB> = libs_common::Session<
|
||||
CB,
|
||||
>;
|
||||
|
||||
pub use libs_common::{messages::ResourceDescription, Callbacks, Error};
|
||||
pub use libs_common::{get_external_id, messages::ResourceDescription, Callbacks, Error};
|
||||
|
||||
@@ -84,7 +84,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
self.start_peer_handler(peer);
|
||||
self.start_peer_handler(peer)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -401,7 +401,11 @@ where
|
||||
tracing::trace!("new data channel opened!");
|
||||
Box::pin(async move {
|
||||
{
|
||||
let mut iface_config = tunnel.iface_config.lock().await;
|
||||
let Some(ref mut iface_config) = *tunnel.iface_config.lock().await else {
|
||||
tracing::error!(message = "Error opening channel", error = "Tried to open a channel before interface was ready");
|
||||
let _ = tunnel.callbacks().on_error(&Error::NoIface);
|
||||
return;
|
||||
};
|
||||
for &ip in &peer.ips {
|
||||
if let Err(e) = iface_config.add_route(ip, tunnel.callbacks()).await
|
||||
{
|
||||
@@ -429,7 +433,7 @@ where
|
||||
let conn = tunnel.peer_connections.lock().remove(&client_id);
|
||||
if let Some(conn) = conn {
|
||||
if let Err(e) = conn.close().await {
|
||||
tracing::error!("Problem while trying to close channel: {e:?}");
|
||||
tracing::error!(message = "Error trying to close channel", error = ?e);
|
||||
let _ = tunnel.callbacks().on_error(&e.into());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use libs_common::{Error, Result};
|
||||
use libs_common::{messages::Interface, CallbackErrorFacade, Callbacks, Error, Result};
|
||||
use tokio::io::unix::AsyncFd;
|
||||
|
||||
use crate::tun::{IfaceConfig, IfaceDevice};
|
||||
@@ -60,11 +60,15 @@ impl DeviceChannel {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn create_iface(fd: Option<i32>) -> Result<(IfaceConfig, DeviceChannel)> {
|
||||
let dev = Arc::new(IfaceDevice::new(fd).await?.set_non_blocking()?);
|
||||
pub(crate) async fn create_iface(
|
||||
config: &Interface,
|
||||
callbacks: &CallbackErrorFacade<impl Callbacks>,
|
||||
) -> Result<(IfaceConfig, DeviceChannel)> {
|
||||
let dev = Arc::new(IfaceDevice::new(config, callbacks).await?);
|
||||
let async_dev = Arc::clone(&dev);
|
||||
let device_channel = DeviceChannel(AsyncFd::new(async_dev)?);
|
||||
let iface_config = IfaceConfig(dev);
|
||||
let mut iface_config = IfaceConfig(dev);
|
||||
iface_config.up().await?;
|
||||
|
||||
Ok((iface_config, device_channel))
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::tun::IfaceConfig;
|
||||
use libs_common::Result;
|
||||
use libs_common::{messages::Interface, CallbackErrorFacade, Callbacks, Result};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct DeviceChannel;
|
||||
@@ -23,7 +23,8 @@ impl DeviceChannel {
|
||||
}
|
||||
|
||||
pub(crate) async fn create_iface(
|
||||
_device_handle: Option<i32>,
|
||||
_: &Interface,
|
||||
_: &CallbackErrorFacade<impl Callbacks>,
|
||||
) -> Result<(IfaceConfig, DeviceChannel)> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ use boringtun::{
|
||||
};
|
||||
use ip_network::IpNetwork;
|
||||
use ip_network_table::IpNetworkTable;
|
||||
use libs_common::{Callbacks, DNS_SENTINEL};
|
||||
use libs_common::{Callbacks, Error, DNS_SENTINEL};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use bytes::Bytes;
|
||||
@@ -78,16 +78,11 @@ mod tun;
|
||||
#[path = "tun_android.rs"]
|
||||
mod tun;
|
||||
|
||||
#[cfg(any(
|
||||
target_os = "macos",
|
||||
target_os = "ios",
|
||||
target_os = "linux",
|
||||
target_os = "android"
|
||||
))]
|
||||
#[cfg(target_family = "unix")]
|
||||
#[path = "device_channel_unix.rs"]
|
||||
mod device_channel;
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
#[cfg(target_family = "windows")]
|
||||
#[path = "device_channel_win.rs"]
|
||||
mod device_channel;
|
||||
|
||||
@@ -141,8 +136,8 @@ pub struct Tunnel<C: ControlSignal, CB: Callbacks> {
|
||||
next_index: Mutex<IndexLfsr>,
|
||||
// We use a tokio's mutex here since it makes things easier and we only need it
|
||||
// during init, so the performance hit is neglibile
|
||||
iface_config: tokio::sync::Mutex<IfaceConfig>,
|
||||
device_channel: Arc<DeviceChannel>,
|
||||
iface_config: tokio::sync::Mutex<Option<IfaceConfig>>,
|
||||
device_channel: RwLock<Option<Arc<DeviceChannel>>>,
|
||||
rate_limiter: Arc<RateLimiter>,
|
||||
private_key: StaticSecret,
|
||||
public_key: PublicKey,
|
||||
@@ -170,7 +165,6 @@ where
|
||||
/// - `control_signaler`: this is used to send SDP from the tunnel to the control plane.
|
||||
#[tracing::instrument(level = "trace", skip(private_key, control_signaler, callbacks))]
|
||||
pub async fn new(
|
||||
fd: Option<i32>,
|
||||
private_key: StaticSecret,
|
||||
control_signaler: C,
|
||||
callbacks: CB,
|
||||
@@ -179,15 +173,14 @@ where
|
||||
let rate_limiter = Arc::new(RateLimiter::new(&public_key, HANDSHAKE_RATE_LIMIT));
|
||||
let peers_by_ip = RwLock::new(IpNetworkTable::new());
|
||||
let next_index = Default::default();
|
||||
let (iface_config, device_channel) = create_iface(fd).await?;
|
||||
let iface_config = tokio::sync::Mutex::new(iface_config);
|
||||
let device_channel = Arc::new(device_channel);
|
||||
let peer_connections = Default::default();
|
||||
let resources = Default::default();
|
||||
let awaiting_connection = Default::default();
|
||||
let gateway_public_keys = Default::default();
|
||||
let resources_gateways = Default::default();
|
||||
let gateway_awaiting_connection = Default::default();
|
||||
let iface_config = Default::default();
|
||||
let device_channel = Default::default();
|
||||
|
||||
// ICE
|
||||
let mut media_engine = MediaEngine::default();
|
||||
@@ -215,9 +208,9 @@ where
|
||||
peers_by_ip,
|
||||
next_index,
|
||||
webrtc_api,
|
||||
resources,
|
||||
iface_config,
|
||||
device_channel,
|
||||
resources,
|
||||
awaiting_connection,
|
||||
gateway_awaiting_connection,
|
||||
control_signaler,
|
||||
@@ -233,7 +226,10 @@ where
|
||||
#[tracing::instrument(level = "trace", skip(self))]
|
||||
pub async fn add_resource(&self, resource_description: ResourceDescription) -> Result<()> {
|
||||
{
|
||||
let mut iface_config = self.iface_config.lock().await;
|
||||
let Some(ref mut iface_config) = *self.iface_config.lock().await else {
|
||||
tracing::error!("Received resource add before initialization.");
|
||||
return Err(Error::ControlProtocolError)
|
||||
};
|
||||
for ip in resource_description.ips() {
|
||||
iface_config.add_route(ip, self.callbacks()).await?;
|
||||
}
|
||||
@@ -250,23 +246,16 @@ where
|
||||
/// Sets the interface configuration and starts background tasks.
|
||||
#[tracing::instrument(level = "trace", skip(self))]
|
||||
pub async fn set_interface(self: &Arc<Self>, config: &InterfaceConfig) -> Result<()> {
|
||||
{
|
||||
let mut iface_config = self.iface_config.lock().await;
|
||||
iface_config
|
||||
.set_iface_config(config, self.callbacks())
|
||||
.await
|
||||
.expect("Couldn't initiate interface");
|
||||
iface_config
|
||||
.up()
|
||||
.await
|
||||
.expect("Couldn't initiate interface");
|
||||
iface_config
|
||||
.add_route(DNS_SENTINEL.into(), self.callbacks())
|
||||
.await?;
|
||||
}
|
||||
let (mut iface_config, device_channel) = create_iface(config, self.callbacks()).await?;
|
||||
iface_config
|
||||
.add_route(DNS_SENTINEL.into(), self.callbacks())
|
||||
.await?;
|
||||
|
||||
let device_channel = Arc::new(device_channel);
|
||||
*self.device_channel.write() = Some(device_channel.clone());
|
||||
*self.iface_config.lock().await = Some(iface_config);
|
||||
self.start_timers();
|
||||
self.start_iface_handler();
|
||||
self.start_iface_handler(device_channel);
|
||||
|
||||
self.callbacks.on_tunnel_ready()?;
|
||||
|
||||
@@ -386,7 +375,8 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn start_peer_handler(self: &Arc<Self>, peer: Arc<Peer>) {
|
||||
fn start_peer_handler(self: &Arc<Self>, peer: Arc<Peer>) -> Result<()> {
|
||||
let Some(device_channel) = self.device_channel.read().clone() else { return Err(Error::NoIface); };
|
||||
let tunnel = Arc::clone(self);
|
||||
tokio::spawn(async move {
|
||||
let mut src_buf = [0u8; MAX_UDP_SIZE];
|
||||
@@ -400,7 +390,8 @@ where
|
||||
break;
|
||||
}
|
||||
|
||||
tracing::trace!("read {size} bytes from peer");
|
||||
tracing::trace!(action = "read", bytes = size, from = "peer");
|
||||
|
||||
// The rate limiter initially checks mac1 and mac2, and optionally asks to send a cookie
|
||||
let parsed_packet = match tunnel.rate_limiter.verify_packet(
|
||||
// TODO: Some(addr.ip()) webrtc doesn't expose easily the underlying data channel remote ip
|
||||
@@ -417,7 +408,7 @@ where
|
||||
continue;
|
||||
}
|
||||
Err(TunnResult::Err(e)) => {
|
||||
tracing::error!("Wireguard error: {e:?}");
|
||||
tracing::error!(message = "Wireguard error", error = ?e);
|
||||
let _ = tunnel.callbacks().on_error(&e.into());
|
||||
continue;
|
||||
}
|
||||
@@ -443,9 +434,9 @@ where
|
||||
let mut flush = false;
|
||||
match decapsulate_result {
|
||||
TunnResult::Done => {}
|
||||
TunnResult::Err(err) => {
|
||||
tracing::error!("Error decapsulating packet: {err:?}");
|
||||
let _ = tunnel.callbacks().on_error(&err.into());
|
||||
TunnResult::Err(e) => {
|
||||
tracing::error!(message = "Error decapsulating packet", error = ?e);
|
||||
let _ = tunnel.callbacks().on_error(&e.into());
|
||||
continue;
|
||||
}
|
||||
TunnResult::WriteToNetwork(packet) => {
|
||||
@@ -453,10 +444,14 @@ where
|
||||
peer.send_infallible(packet, &tunnel.callbacks).await;
|
||||
}
|
||||
TunnResult::WriteToTunnelV4(packet, addr) => {
|
||||
tunnel.send_to_resource(&peer, addr.into(), packet).await;
|
||||
tunnel
|
||||
.send_to_resource(&device_channel, &peer, addr.into(), packet)
|
||||
.await;
|
||||
}
|
||||
TunnResult::WriteToTunnelV6(packet, addr) => {
|
||||
tunnel.send_to_resource(&peer, addr.into(), packet).await;
|
||||
tunnel
|
||||
.send_to_resource(&device_channel, &peer, addr.into(), packet)
|
||||
.await;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -471,16 +466,18 @@ where
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn write4_device_infallible(&self, packet: &[u8]) {
|
||||
if let Err(e) = self.device_channel.write4(packet).await {
|
||||
async fn write4_device_infallible(&self, device_channel: &DeviceChannel, packet: &[u8]) {
|
||||
if let Err(e) = device_channel.write4(packet).await {
|
||||
let _ = self.callbacks.on_error(&e.into());
|
||||
}
|
||||
}
|
||||
|
||||
async fn write6_device_infallible(&self, packet: &[u8]) {
|
||||
if let Err(e) = self.device_channel.write6(packet).await {
|
||||
async fn write6_device_infallible(&self, device_channel: &DeviceChannel, packet: &[u8]) {
|
||||
if let Err(e) = device_channel.write6(packet).await {
|
||||
let _ = self.callbacks.on_error(&e.into());
|
||||
}
|
||||
}
|
||||
@@ -494,7 +491,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn start_iface_handler(self: &Arc<Self>) {
|
||||
fn start_iface_handler(self: &Arc<Self>, device_channel: Arc<DeviceChannel>) {
|
||||
let dev = self.clone();
|
||||
tokio::spawn(async move {
|
||||
loop {
|
||||
@@ -505,32 +502,36 @@ where
|
||||
// there's no docs on tun device on when a whole packet is read, is it \n or another thing?
|
||||
// found some comments saying that a single read syscall represents a single packet but no docs on that
|
||||
// See https://stackoverflow.com/questions/18461365/how-to-read-packet-by-packet-from-linux-tun-tap
|
||||
match dev.device_channel.mtu().await {
|
||||
match device_channel.mtu().await {
|
||||
// XXX: Do we need to fetch the mtu every time? In most clients it'll
|
||||
// be hardcoded to 1280, and if not, it'll only change before packets start
|
||||
// to flow.
|
||||
Ok(mtu) => match dev.device_channel.read(&mut src[..mtu]).await {
|
||||
Ok(mtu) => match device_channel.read(&mut src[..mtu]).await {
|
||||
Ok(res) => res,
|
||||
Err(err) => {
|
||||
tracing::error!("Couldn't read packet from interface: {err}");
|
||||
tracing::error!(message = "Couldn't read packet from interface", error = ?err);
|
||||
let _ = dev.callbacks.on_error(&err.into());
|
||||
continue;
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
tracing::error!("Couldn't obtain iface mtu: {err}");
|
||||
tracing::error!(message = "Couldn't obtain iface mtu", error = ?err);
|
||||
let _ = dev.callbacks.on_error(&err);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
tracing::trace!("Reading from iface {res} bytes");
|
||||
tracing::trace!(action = "reading", bytes = res, from = "iface");
|
||||
|
||||
if let Some(r) = dev.check_for_dns(&src[..res]) {
|
||||
match r {
|
||||
dns::SendPacket::Ipv4(r) => dev.write4_device_infallible(&r[..]).await,
|
||||
dns::SendPacket::Ipv6(r) => dev.write6_device_infallible(&r[..]).await,
|
||||
dns::SendPacket::Ipv4(r) => {
|
||||
dev.write4_device_infallible(&device_channel, &r[..]).await
|
||||
}
|
||||
dns::SendPacket::Ipv6(r) => {
|
||||
dev.write6_device_infallible(&device_channel, &r[..]).await
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@@ -582,7 +583,10 @@ where
|
||||
let mut awaiting_connection = dev.awaiting_connection.lock();
|
||||
let id = resource.id();
|
||||
if !awaiting_connection.contains(&id) {
|
||||
tracing::trace!("Found new intent to send packets to resource with resource-ip: {dst_addr}, initializing connection...");
|
||||
tracing::trace!(
|
||||
message = "Found new intent to send packets to resource",
|
||||
resource_ip = %dst_addr
|
||||
);
|
||||
|
||||
awaiting_connection.insert(id);
|
||||
let dev = Arc::clone(&dev);
|
||||
@@ -597,7 +601,7 @@ where
|
||||
dev.resources_gateways.lock().values().collect::<Vec<_>>(),
|
||||
);
|
||||
tracing::trace!(
|
||||
"Currently connected gateways: {connected_gateway_ids:?}"
|
||||
message = "Currently connected gateways", gateways = ?connected_gateway_ids
|
||||
);
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = dev
|
||||
@@ -607,7 +611,7 @@ where
|
||||
{
|
||||
// Not a deadlock because this is a different task
|
||||
dev.awaiting_connection.lock().remove(&id);
|
||||
tracing::error!("couldn't start protocol for new connection to resource: {e}");
|
||||
tracing::error!(message = "couldn't start protocol for new connection to resource", error = ?e);
|
||||
let _ = dev.callbacks.on_error(&e);
|
||||
}
|
||||
});
|
||||
@@ -626,11 +630,11 @@ where
|
||||
}
|
||||
|
||||
TunnResult::Err(e) => {
|
||||
tracing::error!(message = "Encapsulate error for resource corresponding to {dst_addr}", error = ?e);
|
||||
tracing::error!(message = "Encapsulate error for resource", resource_address = %dst_addr, error = ?e);
|
||||
let _ = dev.callbacks.on_error(&e.into());
|
||||
}
|
||||
TunnResult::WriteToNetwork(packet) => {
|
||||
tracing::trace!("writing iface packet to peer: {dst_addr}");
|
||||
tracing::trace!(action = "writing", from = "iface", to = %dst_addr);
|
||||
if let Err(e) = channel.write(&Bytes::copy_from_slice(packet)).await {
|
||||
tracing::error!("Couldn't write packet to channel: {e}");
|
||||
if matches!(
|
||||
|
||||
@@ -3,7 +3,9 @@ use std::{
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use crate::{ip_packet::MutableIpPacket, peer::Peer, ControlSignal, Tunnel};
|
||||
use crate::{
|
||||
device_channel::DeviceChannel, ip_packet::MutableIpPacket, peer::Peer, ControlSignal, Tunnel,
|
||||
};
|
||||
use boringtun::noise::Tunn;
|
||||
use libs_common::{messages::ResourceDescription, Callbacks, Error};
|
||||
|
||||
@@ -16,7 +18,12 @@ where
|
||||
((addr.is_ipv4() && ip.is_ipv4()) || (addr.is_ipv6() && ip.is_ipv6())).then_some(ip)
|
||||
}
|
||||
|
||||
async fn update_and_send_packet(&self, packet: &mut [u8], dst_addr: IpAddr) {
|
||||
async fn update_and_send_packet(
|
||||
&self,
|
||||
device_channel: &DeviceChannel,
|
||||
packet: &mut [u8],
|
||||
dst_addr: IpAddr,
|
||||
) {
|
||||
let Some(mut pkt) = MutableIpPacket::new(packet) else { return };
|
||||
pkt.set_dst(dst_addr);
|
||||
pkt.update_checksum();
|
||||
@@ -24,16 +31,22 @@ where
|
||||
match dst_addr {
|
||||
IpAddr::V4(addr) => {
|
||||
tracing::trace!("Sending packet to {addr}");
|
||||
self.write4_device_infallible(packet).await;
|
||||
self.write4_device_infallible(device_channel, packet).await;
|
||||
}
|
||||
IpAddr::V6(addr) => {
|
||||
tracing::trace!("Sending packet to {addr}");
|
||||
self.write6_device_infallible(packet).await;
|
||||
self.write6_device_infallible(device_channel, packet).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn send_to_resource(&self, peer: &Arc<Peer>, addr: IpAddr, packet: &mut [u8]) {
|
||||
pub(crate) async fn send_to_resource(
|
||||
&self,
|
||||
device_channel: &DeviceChannel,
|
||||
peer: &Arc<Peer>,
|
||||
addr: IpAddr,
|
||||
packet: &mut [u8],
|
||||
) {
|
||||
if peer.is_allowed(addr) {
|
||||
let Some(resources) = &peer.resources else {
|
||||
// If there's no associated resource it means that we are in a client, then the packet comes from a gateway
|
||||
@@ -41,8 +54,8 @@ where
|
||||
// In gateways this should never happen.
|
||||
tracing::trace!("Writing to interface with addr: {addr}");
|
||||
match addr {
|
||||
IpAddr::V4(_) => self.write4_device_infallible(packet).await,
|
||||
IpAddr::V6(_) => self.write6_device_infallible(packet).await,
|
||||
IpAddr::V4(_) => self.write4_device_infallible(device_channel, packet).await,
|
||||
IpAddr::V6(_) => self.write6_device_infallible(device_channel, packet).await,
|
||||
}
|
||||
return;
|
||||
};
|
||||
@@ -98,7 +111,8 @@ where
|
||||
}
|
||||
};
|
||||
|
||||
self.update_and_send_packet(packet, dst_addr).await;
|
||||
self.update_and_send_packet(device_channel, packet, dst_addr)
|
||||
.await;
|
||||
} else {
|
||||
tracing::warn!("Received packet from peer with an unallowed ip: {addr}");
|
||||
}
|
||||
|
||||
@@ -13,6 +13,9 @@ use std::{
|
||||
};
|
||||
|
||||
mod wrapped_socket;
|
||||
// Android doesn't support Split DNS. So we intercept all requests and forward
|
||||
// the non-Firezone name resolution requests to the upstream DNS resolver.
|
||||
const DNS_FALLBACK_STRATEGY: &str = "upstream_resolver";
|
||||
|
||||
#[repr(C)]
|
||||
union IfrIfru {
|
||||
@@ -69,18 +72,17 @@ impl IfaceDevice {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn new(fd: Option<i32>) -> Result<Self> {
|
||||
log::debug!("tunnel allocation unimplemented on Android; using provided fd");
|
||||
Ok(Self {
|
||||
fd: fd.expect("file descriptor must be provided!") as RawFd,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_non_blocking(self) -> Result<Self> {
|
||||
// Android already opens the tun device in non-blocking mode and we can't change it from
|
||||
// here.
|
||||
log::debug!("`set_non_blocking` unimplemented on Android");
|
||||
Ok(self)
|
||||
pub async fn new(
|
||||
config: &InterfaceConfig,
|
||||
callbacks: &CallbackErrorFacade<impl Callbacks>,
|
||||
) -> Result<Self> {
|
||||
let fd = callbacks.on_set_interface_config(
|
||||
config.ipv4,
|
||||
config.ipv6,
|
||||
DNS_SENTINEL,
|
||||
DNS_FALLBACK_STRATEGY.to_string(),
|
||||
)?;
|
||||
Ok(Self { fd })
|
||||
}
|
||||
|
||||
pub fn name(&self) -> Result<String> {
|
||||
@@ -146,14 +148,6 @@ fn get_last_error() -> Error {
|
||||
}
|
||||
|
||||
impl IfaceConfig {
|
||||
pub async fn set_iface_config(
|
||||
&mut self,
|
||||
config: &InterfaceConfig,
|
||||
callbacks: &CallbackErrorFacade<impl Callbacks>,
|
||||
) -> Result<()> {
|
||||
callbacks.on_set_interface_config(config.ipv4, config.ipv6, DNS_SENTINEL)
|
||||
}
|
||||
|
||||
pub async fn add_route(
|
||||
&mut self,
|
||||
route: IpNetwork,
|
||||
|
||||
@@ -97,7 +97,10 @@ impl IfaceDevice {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn new(_fd: Option<i32>) -> Result<Self> {
|
||||
pub async fn new(
|
||||
config: &InterfaceConfig,
|
||||
callbacks: &CallbackErrorFacade<impl Callbacks>,
|
||||
) -> Result<Self> {
|
||||
let mut info = ctl_info {
|
||||
ctl_id: 0,
|
||||
ctl_name: [0; 96],
|
||||
@@ -152,19 +155,27 @@ impl IfaceDevice {
|
||||
}
|
||||
|
||||
if addr.sc_id == info.ctl_id {
|
||||
return Ok(Self { fd });
|
||||
let _ = callbacks.on_set_interface_config(
|
||||
config.ipv4,
|
||||
config.ipv6,
|
||||
DNS_SENTINEL,
|
||||
"system_resolver".to_string(),
|
||||
);
|
||||
let this = Self { fd };
|
||||
let _ = this.set_non_blocking();
|
||||
return Ok(this);
|
||||
}
|
||||
}
|
||||
|
||||
Err(get_last_error())
|
||||
}
|
||||
|
||||
pub fn set_non_blocking(self) -> Result<Self> {
|
||||
fn set_non_blocking(&self) -> Result<()> {
|
||||
match unsafe { fcntl(self.fd, F_GETFL) } {
|
||||
-1 => Err(get_last_error()),
|
||||
flags => match unsafe { fcntl(self.fd, F_SETFL, flags | O_NONBLOCK) } {
|
||||
-1 => Err(get_last_error()),
|
||||
_ => Ok(self),
|
||||
_ => Ok(()),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -258,15 +269,6 @@ impl IfaceDevice {
|
||||
|
||||
// So, these functions take a mutable &self, this is not necessary in theory but it's correct!
|
||||
impl IfaceConfig {
|
||||
#[tracing::instrument(level = "trace", skip(self, callbacks))]
|
||||
pub async fn set_iface_config(
|
||||
&mut self,
|
||||
config: &InterfaceConfig,
|
||||
callbacks: &CallbackErrorFacade<impl Callbacks>,
|
||||
) -> Result<()> {
|
||||
callbacks.on_set_interface_config(config.ipv4, config.ipv6, DNS_SENTINEL)
|
||||
}
|
||||
|
||||
pub async fn add_route(
|
||||
&mut self,
|
||||
route: IpNetwork,
|
||||
|
||||
@@ -78,7 +78,10 @@ impl IfaceDevice {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn new(_fd: Option<i32>) -> Result<IfaceDevice> {
|
||||
pub async fn new(
|
||||
config: &InterfaceConfig,
|
||||
_: &CallbackErrorFacade<impl Callbacks>,
|
||||
) -> Result<IfaceDevice> {
|
||||
debug_assert!(IFACE_NAME.as_bytes().len() < IFNAMSIZ);
|
||||
|
||||
let fd = match unsafe { open(TUN_FILE.as_ptr() as _, O_RDWR) } {
|
||||
@@ -112,20 +115,52 @@ impl IfaceDevice {
|
||||
.header
|
||||
.index;
|
||||
|
||||
Ok(Self {
|
||||
let mut this = Self {
|
||||
fd,
|
||||
handle,
|
||||
connection: join_handle,
|
||||
interface_index,
|
||||
})
|
||||
};
|
||||
|
||||
this.set_iface_config(config).await?;
|
||||
this.set_non_blocking()?;
|
||||
|
||||
Ok(this)
|
||||
}
|
||||
|
||||
pub fn set_non_blocking(self) -> Result<Self> {
|
||||
async fn set_iface_config(&mut self, config: &InterfaceConfig) -> Result<()> {
|
||||
let ips = self
|
||||
.handle
|
||||
.address()
|
||||
.get()
|
||||
.set_link_index_filter(self.interface_index)
|
||||
.execute();
|
||||
|
||||
ips.try_for_each(|ip| self.handle.address().del(ip).execute())
|
||||
.await?;
|
||||
|
||||
self.handle
|
||||
.address()
|
||||
.add(self.interface_index, config.ipv4.into(), 32)
|
||||
.execute()
|
||||
.await?;
|
||||
|
||||
// TODO: Disable this when ipv6 is disabled
|
||||
self.handle
|
||||
.address()
|
||||
.add(self.interface_index, config.ipv6.into(), 128)
|
||||
.execute()
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_non_blocking(&self) -> Result<()> {
|
||||
match unsafe { fcntl(self.fd, F_GETFL) } {
|
||||
-1 => Err(get_last_error()),
|
||||
flags => match unsafe { fcntl(self.fd, F_SETFL, flags | O_NONBLOCK) } {
|
||||
-1 => Err(get_last_error()),
|
||||
_ => Ok(self),
|
||||
_ => Ok(()),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
794C38152970A2660029F38F /* FirezoneKit in Frameworks */ = {isa = PBXBuildFile; productRef = 794C38142970A2660029F38F /* FirezoneKit */; };
|
||||
794C38172970A26A0029F38F /* FirezoneKit in Frameworks */ = {isa = PBXBuildFile; productRef = 794C38162970A26A0029F38F /* FirezoneKit */; };
|
||||
79756C6629704A7A0018E2D5 /* FirezoneKit in Frameworks */ = {isa = PBXBuildFile; productRef = 79756C6529704A7A0018E2D5 /* FirezoneKit */; };
|
||||
8D2F64EF2A973F7000B6176A /* PrimaryMacAddress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D2F64EC2A97336C00B6176A /* PrimaryMacAddress.swift */; };
|
||||
8DCC021D28D512AC007E12D2 /* FirezoneApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DCC021C28D512AC007E12D2 /* FirezoneApp.swift */; };
|
||||
8DCC022628D512AE007E12D2 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8DCC022528D512AE007E12D2 /* Assets.xcassets */; };
|
||||
8DCC022A28D512AE007E12D2 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8DCC022928D512AE007E12D2 /* Preview Assets.xcassets */; };
|
||||
@@ -108,6 +109,7 @@
|
||||
6FE4550E2A5D112C006549B1 /* connlib-apple.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "connlib-apple.swift"; path = "Connlib/Generated/connlib-apple/connlib-apple.swift"; sourceTree = "<group>"; };
|
||||
6FE455112A5D13A2006549B1 /* FirezoneNetworkExtension-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "FirezoneNetworkExtension-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
6FE93AFA2A738D7E002D278A /* NetworkSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkSettings.swift; sourceTree = "<group>"; };
|
||||
8D2F64EC2A97336C00B6176A /* PrimaryMacAddress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrimaryMacAddress.swift; sourceTree = "<group>"; };
|
||||
8DCC021928D512AC007E12D2 /* Firezone.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Firezone.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
8DCC021C28D512AC007E12D2 /* FirezoneApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FirezoneApp.swift; sourceTree = "<group>"; };
|
||||
8DCC022528D512AE007E12D2 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
@@ -167,6 +169,7 @@
|
||||
6FE455082A5D110D006549B1 /* CallbackHandler.swift */,
|
||||
6FE4550B2A5D111D006549B1 /* SwiftBridgeCore.swift */,
|
||||
6FE4550E2A5D112C006549B1 /* connlib-apple.swift */,
|
||||
8D2F64EC2A97336C00B6176A /* PrimaryMacAddress.swift */,
|
||||
6FE455112A5D13A2006549B1 /* FirezoneNetworkExtension-Bridging-Header.h */,
|
||||
);
|
||||
path = FirezoneNetworkExtension;
|
||||
@@ -468,6 +471,7 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
8D2F64EF2A973F7000B6176A /* PrimaryMacAddress.swift in Sources */,
|
||||
6FE4550A2A5D110D006549B1 /* CallbackHandler.swift in Sources */,
|
||||
6FE455102A5D112C006549B1 /* connlib-apple.swift in Sources */,
|
||||
05CF1D16290B1FE700CF4755 /* PacketTunnelProvider.swift in Sources */,
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
//
|
||||
// Adapter.swift
|
||||
// (c) 2023 Firezone, Inc.
|
||||
// LICENSE: Apache-2.0
|
||||
//
|
||||
import FirezoneKit
|
||||
import Foundation
|
||||
import NetworkExtension
|
||||
import FirezoneKit
|
||||
import OSLog
|
||||
|
||||
#if os(iOS)
|
||||
import UIKit.UIDevice
|
||||
#endif
|
||||
|
||||
public enum AdapterError: Error {
|
||||
/// Failure to perform an operation in such state.
|
||||
case invalidState
|
||||
@@ -34,17 +37,18 @@ private enum AdapterState: CustomStringConvertible {
|
||||
case tunnelReady(session: WrappedSession)
|
||||
case stoppingTunnel(session: WrappedSession, onStopped: Adapter.StopTunnelCompletionHandler?)
|
||||
case stoppedTunnel
|
||||
case stoppingTunnelTemporarily(session: WrappedSession, onStopped: Adapter.StopTunnelCompletionHandler?)
|
||||
case stoppingTunnelTemporarily(
|
||||
session: WrappedSession, onStopped: Adapter.StopTunnelCompletionHandler?)
|
||||
case stoppedTunnelTemporarily
|
||||
|
||||
var description: String {
|
||||
switch self {
|
||||
case .startingTunnel: return "startingTunnel"
|
||||
case .tunnelReady: return "tunnelReady"
|
||||
case .stoppingTunnel: return "stoppingTunnel"
|
||||
case .stoppedTunnel: return "stoppedTunnel"
|
||||
case .stoppingTunnelTemporarily: return "stoppingTunnelTemporarily"
|
||||
case .stoppedTunnelTemporarily: return "stoppedTunnelTemporarily"
|
||||
case .startingTunnel: return "startingTunnel"
|
||||
case .tunnelReady: return "tunnelReady"
|
||||
case .stoppingTunnel: return "stoppingTunnel"
|
||||
case .stoppedTunnel: return "stoppedTunnel"
|
||||
case .stoppingTunnelTemporarily: return "stoppingTunnelTemporarily"
|
||||
case .stoppedTunnelTemporarily: return "stoppedTunnelTemporarily"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -85,7 +89,9 @@ public class Adapter {
|
||||
private var controlPlaneURLString: String
|
||||
private var token: String
|
||||
|
||||
public init(controlPlaneURLString: String, token: String, packetTunnelProvider: NEPacketTunnelProvider) {
|
||||
public init(
|
||||
controlPlaneURLString: String, token: String, packetTunnelProvider: NEPacketTunnelProvider
|
||||
) {
|
||||
self.controlPlaneURLString = controlPlaneURLString
|
||||
self.token = token
|
||||
self.packetTunnelProvider = packetTunnelProvider
|
||||
@@ -123,7 +129,8 @@ public class Adapter {
|
||||
self.logger.debug("Adapter.start: Starting connlib")
|
||||
do {
|
||||
self.state = .startingTunnel(
|
||||
session: try WrappedSession.connect(self.controlPlaneURLString, self.token, self.callbackHandler),
|
||||
session: try WrappedSession.connect(
|
||||
self.controlPlaneURLString, self.token, self.getExternalId(), self.callbackHandler),
|
||||
onStarted: completionHandler
|
||||
)
|
||||
} catch let error {
|
||||
@@ -143,27 +150,31 @@ public class Adapter {
|
||||
self.logger.debug("Adapter.stop")
|
||||
|
||||
switch self.state {
|
||||
case .stoppedTunnel, .stoppingTunnel:
|
||||
break
|
||||
case .tunnelReady(let session):
|
||||
self.logger.debug("Adapter.stop: Shutting down connlib")
|
||||
self.state = .stoppingTunnel(session: session, onStopped: completionHandler)
|
||||
session.disconnect()
|
||||
case .startingTunnel(let session, let onStarted):
|
||||
self.logger.debug("Adapter.stop: Shutting down connlib before tunnel ready")
|
||||
self.state = .stoppingTunnel(session: session, onStopped: {
|
||||
case .stoppedTunnel, .stoppingTunnel:
|
||||
break
|
||||
case .tunnelReady(let session):
|
||||
self.logger.debug("Adapter.stop: Shutting down connlib")
|
||||
self.state = .stoppingTunnel(session: session, onStopped: completionHandler)
|
||||
session.disconnect()
|
||||
case .startingTunnel(let session, let onStarted):
|
||||
self.logger.debug("Adapter.stop: Shutting down connlib before tunnel ready")
|
||||
self.state = .stoppingTunnel(
|
||||
session: session,
|
||||
onStopped: {
|
||||
onStarted?(AdapterError.stoppedByRequestWhileStarting)
|
||||
completionHandler()
|
||||
})
|
||||
session.disconnect()
|
||||
case .stoppingTunnelTemporarily(let session, let onStopped):
|
||||
self.state = .stoppingTunnel(session: session, onStopped: {
|
||||
session.disconnect()
|
||||
case .stoppingTunnelTemporarily(let session, let onStopped):
|
||||
self.state = .stoppingTunnel(
|
||||
session: session,
|
||||
onStopped: {
|
||||
onStopped?()
|
||||
completionHandler()
|
||||
})
|
||||
case .stoppedTunnelTemporarily:
|
||||
self.state = .stoppedTunnel
|
||||
completionHandler()
|
||||
case .stoppedTunnelTemporarily:
|
||||
self.state = .stoppedTunnel
|
||||
completionHandler()
|
||||
}
|
||||
|
||||
self.networkMonitor?.cancel()
|
||||
@@ -174,16 +185,38 @@ public class Adapter {
|
||||
/// Get the current set of resources in the completionHandler.
|
||||
/// If unchanged since referenceVersionString, call completionHandler(nil).
|
||||
public func getDisplayableResourcesIfVersionDifferentFrom(
|
||||
referenceVersionString: String, completionHandler: @escaping (DisplayableResources?) -> Void) {
|
||||
workQueue.async { [weak self] in
|
||||
guard let self = self else { return }
|
||||
referenceVersionString: String, completionHandler: @escaping (DisplayableResources?) -> Void
|
||||
) {
|
||||
workQueue.async { [weak self] in
|
||||
guard let self = self else { return }
|
||||
|
||||
if referenceVersionString == self.displayableResources.versionString {
|
||||
completionHandler(nil)
|
||||
} else {
|
||||
completionHandler(self.displayableResources)
|
||||
}
|
||||
if referenceVersionString == self.displayableResources.versionString {
|
||||
completionHandler(nil)
|
||||
} else {
|
||||
completionHandler(self.displayableResources)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Device unique identifiers
|
||||
extension Adapter {
|
||||
func getExternalId() -> String {
|
||||
#if os(iOS)
|
||||
guard let uuid = UIDevice.current.identifierForVendor?.uuidString else {
|
||||
// Send a blank string, letting either connlib or the portal handle this
|
||||
return ""
|
||||
}
|
||||
return uuid
|
||||
#elseif os(macOS)
|
||||
guard let macBytes = PrimaryMacAddress.copy_mac_address() else {
|
||||
// Send a blank string, letting either connlib or the portal handle this
|
||||
return ""
|
||||
}
|
||||
return (macBytes as Data).base64EncodedString()
|
||||
#else
|
||||
#error("Unsupported platform")
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,58 +236,62 @@ extension Adapter {
|
||||
// Will be invoked in the workQueue by the path monitor
|
||||
switch self.state {
|
||||
|
||||
case .startingTunnel(let session, let onStarted):
|
||||
if path.status != .satisfied {
|
||||
self.logger.debug("Adapter.didReceivePathUpdate: Offline. Shutting down connlib.")
|
||||
onStarted?(nil)
|
||||
self.packetTunnelProvider?.reasserting = true
|
||||
self.state = .stoppingTunnelTemporarily(session: session, onStopped: nil)
|
||||
session.disconnect()
|
||||
}
|
||||
case .startingTunnel(let session, let onStarted):
|
||||
if path.status != .satisfied {
|
||||
self.logger.debug("Adapter.didReceivePathUpdate: Offline. Shutting down connlib.")
|
||||
onStarted?(nil)
|
||||
self.packetTunnelProvider?.reasserting = true
|
||||
self.state = .stoppingTunnelTemporarily(session: session, onStopped: nil)
|
||||
session.disconnect()
|
||||
}
|
||||
|
||||
case .tunnelReady(let session):
|
||||
if path.status == .satisfied {
|
||||
self.logger.debug("Suppressing calls to disableSomeRoamingForBrokenMobileSemantics() and bumpSockets()")
|
||||
// #if os(iOS)
|
||||
// wrappedSession.disableSomeRoamingForBrokenMobileSemantics()
|
||||
// #endif
|
||||
// wrappedSession.bumpSockets()
|
||||
} else {
|
||||
self.logger.debug("Adapter.didReceivePathUpdate: Offline. Shutting down connlib.")
|
||||
self.packetTunnelProvider?.reasserting = true
|
||||
self.state = .stoppingTunnelTemporarily(session: session, onStopped: nil)
|
||||
session.disconnect()
|
||||
}
|
||||
case .tunnelReady(let session):
|
||||
if path.status == .satisfied {
|
||||
self.logger.debug(
|
||||
"Suppressing calls to disableSomeRoamingForBrokenMobileSemantics() and bumpSockets()")
|
||||
// #if os(iOS)
|
||||
// wrappedSession.disableSomeRoamingForBrokenMobileSemantics()
|
||||
// #endif
|
||||
// wrappedSession.bumpSockets()
|
||||
} else {
|
||||
self.logger.debug("Adapter.didReceivePathUpdate: Offline. Shutting down connlib.")
|
||||
self.packetTunnelProvider?.reasserting = true
|
||||
self.state = .stoppingTunnelTemporarily(session: session, onStopped: nil)
|
||||
session.disconnect()
|
||||
}
|
||||
|
||||
case .stoppingTunnelTemporarily:
|
||||
break
|
||||
case .stoppingTunnelTemporarily:
|
||||
break
|
||||
|
||||
case .stoppedTunnelTemporarily:
|
||||
guard path.status == .satisfied else { return }
|
||||
case .stoppedTunnelTemporarily:
|
||||
guard path.status == .satisfied else { return }
|
||||
|
||||
self.logger.debug("Adapter.didReceivePathUpdate: Back online. Starting connlib.")
|
||||
self.logger.debug("Adapter.didReceivePathUpdate: Back online. Starting connlib.")
|
||||
|
||||
do {
|
||||
self.state = .startingTunnel(
|
||||
session: try WrappedSession.connect(controlPlaneURLString, token, self.callbackHandler),
|
||||
onStarted: { error in
|
||||
if let error = error {
|
||||
self.logger.error("Adapter.didReceivePathUpdate: Error starting connlib: \(error, privacy: .public)")
|
||||
self.packetTunnelProvider?.cancelTunnelWithError(error)
|
||||
} else {
|
||||
self.packetTunnelProvider?.reasserting = false
|
||||
}
|
||||
do {
|
||||
self.state = .startingTunnel(
|
||||
session: try WrappedSession.connect(
|
||||
controlPlaneURLString, token, self.getExternalId(), self.callbackHandler),
|
||||
onStarted: { error in
|
||||
if let error = error {
|
||||
self.logger.error(
|
||||
"Adapter.didReceivePathUpdate: Error starting connlib: \(error, privacy: .public)")
|
||||
self.packetTunnelProvider?.cancelTunnelWithError(error)
|
||||
} else {
|
||||
self.packetTunnelProvider?.reasserting = false
|
||||
}
|
||||
)
|
||||
} catch let error as AdapterError {
|
||||
self.logger.error("Adapter.didReceivePathUpdate: Error: \(error, privacy: .public)")
|
||||
} catch {
|
||||
self.logger.error("Adapter.didReceivePathUpdate: Unknown error: \(error, privacy: .public) (fatal)")
|
||||
}
|
||||
}
|
||||
)
|
||||
} catch let error as AdapterError {
|
||||
self.logger.error("Adapter.didReceivePathUpdate: Error: \(error, privacy: .public)")
|
||||
} catch {
|
||||
self.logger.error(
|
||||
"Adapter.didReceivePathUpdate: Unknown error: \(error, privacy: .public) (fatal)")
|
||||
}
|
||||
|
||||
case .stoppingTunnel, .stoppedTunnel:
|
||||
// no-op
|
||||
break
|
||||
case .stoppingTunnel, .stoppedTunnel:
|
||||
// no-op
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -262,28 +299,34 @@ extension Adapter {
|
||||
// MARK: Implementing CallbackHandlerDelegate
|
||||
|
||||
extension Adapter: CallbackHandlerDelegate {
|
||||
public func onSetInterfaceConfig(tunnelAddressIPv4: String, tunnelAddressIPv6: String, dnsAddress: String, dnsFallbackStrategy: String) {
|
||||
public func onSetInterfaceConfig(
|
||||
tunnelAddressIPv4: String, tunnelAddressIPv6: String, dnsAddress: String,
|
||||
dnsFallbackStrategy: String
|
||||
) {
|
||||
workQueue.async { [weak self] in
|
||||
guard let self = self else { return }
|
||||
|
||||
self.logger.debug("Adapter.onSetInterfaceConfig")
|
||||
|
||||
switch self.state {
|
||||
case .startingTunnel:
|
||||
self.networkSettings = NetworkSettings(
|
||||
tunnelAddressIPv4: tunnelAddressIPv4, tunnelAddressIPv6: tunnelAddressIPv6,
|
||||
dnsAddress: dnsAddress, dnsFallbackStrategy: NetworkSettings.DNSFallbackStrategy(dnsFallbackStrategy))
|
||||
case .tunnelReady:
|
||||
if let networkSettings = self.networkSettings {
|
||||
networkSettings.setDNSFallbackStrategy(NetworkSettings.DNSFallbackStrategy(dnsFallbackStrategy))
|
||||
if let packetTunnelProvider = self.packetTunnelProvider {
|
||||
networkSettings.apply(on: packetTunnelProvider, logger: self.logger, completionHandler: nil)
|
||||
}
|
||||
case .startingTunnel:
|
||||
self.networkSettings = NetworkSettings(
|
||||
tunnelAddressIPv4: tunnelAddressIPv4, tunnelAddressIPv6: tunnelAddressIPv6,
|
||||
dnsAddress: dnsAddress,
|
||||
dnsFallbackStrategy: NetworkSettings.DNSFallbackStrategy(dnsFallbackStrategy))
|
||||
case .tunnelReady:
|
||||
if let networkSettings = self.networkSettings {
|
||||
networkSettings.setDNSFallbackStrategy(
|
||||
NetworkSettings.DNSFallbackStrategy(dnsFallbackStrategy))
|
||||
if let packetTunnelProvider = self.packetTunnelProvider {
|
||||
networkSettings.apply(
|
||||
on: packetTunnelProvider, logger: self.logger, completionHandler: nil)
|
||||
}
|
||||
}
|
||||
|
||||
case .stoppingTunnel, .stoppedTunnel, .stoppingTunnelTemporarily, .stoppedTunnelTemporarily:
|
||||
// This is not expected to happen
|
||||
break
|
||||
case .stoppingTunnel, .stoppedTunnel, .stoppingTunnelTemporarily, .stoppedTunnelTemporarily:
|
||||
// This is not expected to happen
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -294,7 +337,8 @@ extension Adapter: CallbackHandlerDelegate {
|
||||
|
||||
self.logger.debug("Adapter.onTunnelReady")
|
||||
guard case .startingTunnel(let session, let onStarted) = self.state else {
|
||||
self.logger.error("Adapter.onTunnelReady: Unexpected state: \(self.state, privacy: .public)")
|
||||
self.logger.error(
|
||||
"Adapter.onTunnelReady: Unexpected state: \(self.state, privacy: .public)")
|
||||
return
|
||||
}
|
||||
guard let networkSettings = self.networkSettings else {
|
||||
@@ -368,7 +412,8 @@ extension Adapter: CallbackHandlerDelegate {
|
||||
guard let jsonData = jsonString.data(using: .utf8) else {
|
||||
return
|
||||
}
|
||||
guard let networkResources = try? JSONDecoder().decode([NetworkResource].self, from: jsonData) else {
|
||||
guard let networkResources = try? JSONDecoder().decode([NetworkResource].self, from: jsonData)
|
||||
else {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -392,41 +437,43 @@ extension Adapter: CallbackHandlerDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
public func onDisconnect(error: Optional<String>) {
|
||||
public func onDisconnect(error: String?) {
|
||||
workQueue.async { [weak self] in
|
||||
guard let self = self else { return }
|
||||
|
||||
self.logger.debug("Adapter.onDisconnect")
|
||||
if let errorMessage = error {
|
||||
self.logger.error("Connlib disconnected with unrecoverable error: \(errorMessage, privacy: .public)")
|
||||
self.logger.error(
|
||||
"Connlib disconnected with unrecoverable error: \(errorMessage, privacy: .public)")
|
||||
switch self.state {
|
||||
case .stoppingTunnel(session: _, let onStopped):
|
||||
onStopped?()
|
||||
self.state = .stoppedTunnel
|
||||
case .stoppingTunnelTemporarily(session: _, let onStopped):
|
||||
onStopped?()
|
||||
self.state = .stoppedTunnel
|
||||
case .stoppedTunnel:
|
||||
// This should not happen
|
||||
break
|
||||
case .stoppedTunnelTemporarily:
|
||||
self.state = .stoppedTunnel
|
||||
default:
|
||||
self.packetTunnelProvider?.cancelTunnelWithError(AdapterError.connlibFatalError(errorMessage))
|
||||
self.state = .stoppedTunnel
|
||||
case .stoppingTunnel(session: _, let onStopped):
|
||||
onStopped?()
|
||||
self.state = .stoppedTunnel
|
||||
case .stoppingTunnelTemporarily(session: _, let onStopped):
|
||||
onStopped?()
|
||||
self.state = .stoppedTunnel
|
||||
case .stoppedTunnel:
|
||||
// This should not happen
|
||||
break
|
||||
case .stoppedTunnelTemporarily:
|
||||
self.state = .stoppedTunnel
|
||||
default:
|
||||
self.packetTunnelProvider?.cancelTunnelWithError(
|
||||
AdapterError.connlibFatalError(errorMessage))
|
||||
self.state = .stoppedTunnel
|
||||
}
|
||||
} else {
|
||||
self.logger.debug("Connlib disconnected")
|
||||
switch self.state {
|
||||
case .stoppingTunnel(session: _, let onStopped):
|
||||
onStopped?()
|
||||
self.state = .stoppedTunnel
|
||||
case .stoppingTunnelTemporarily(session: _, let onStopped):
|
||||
onStopped?()
|
||||
self.state = .stoppedTunnelTemporarily
|
||||
default:
|
||||
// This should not happen
|
||||
self.state = .stoppedTunnel
|
||||
case .stoppingTunnel(session: _, let onStopped):
|
||||
onStopped?()
|
||||
self.state = .stoppedTunnel
|
||||
case .stoppingTunnelTemporarily(session: _, let onStopped):
|
||||
onStopped?()
|
||||
self.state = .stoppedTunnelTemporarily
|
||||
default:
|
||||
// This should not happen
|
||||
self.state = .stoppedTunnel
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
77
swift/apple/FirezoneNetworkExtension/PrimaryMacAddress.swift
Normal file
77
swift/apple/FirezoneNetworkExtension/PrimaryMacAddress.swift
Normal file
@@ -0,0 +1,77 @@
|
||||
//
|
||||
// PrimaryMacAddress.swift
|
||||
// (c) 2023 Firezone, Inc.
|
||||
// LICENSE: Apache-2.0
|
||||
//
|
||||
// Contains convenience methods for getting a device ID for macOS.
|
||||
|
||||
// Believe it or not, this is Apple's recommended way of doing things for macOS
|
||||
// See https://developer.apple.com/documentation/appstorereceipts/validating_receipts_on_the_device#//apple_ref/doc/uid/TP40010573-CH1-SW14
|
||||
|
||||
import IOKit
|
||||
import Foundation
|
||||
import OSLog
|
||||
|
||||
public class PrimaryMacAddress {
|
||||
// Returns an object with a +1 retain count; the caller needs to release.
|
||||
private static func io_service(named name: String, wantBuiltIn: Bool) -> io_service_t? {
|
||||
let default_port = kIOMainPortDefault
|
||||
var iterator = io_iterator_t()
|
||||
defer {
|
||||
if iterator != IO_OBJECT_NULL {
|
||||
IOObjectRelease(iterator)
|
||||
}
|
||||
}
|
||||
|
||||
guard let matchingDict = IOBSDNameMatching(default_port, 0, name),
|
||||
IOServiceGetMatchingServices(default_port,
|
||||
matchingDict as CFDictionary,
|
||||
&iterator) == KERN_SUCCESS,
|
||||
iterator != IO_OBJECT_NULL
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
|
||||
var candidate = IOIteratorNext(iterator)
|
||||
while candidate != IO_OBJECT_NULL {
|
||||
if let cftype = IORegistryEntryCreateCFProperty(candidate,
|
||||
"IOBuiltin" as CFString,
|
||||
kCFAllocatorDefault,
|
||||
0) {
|
||||
let isBuiltIn = cftype.takeRetainedValue() as! CFBoolean
|
||||
if wantBuiltIn == CFBooleanGetValue(isBuiltIn) {
|
||||
return candidate
|
||||
}
|
||||
}
|
||||
|
||||
IOObjectRelease(candidate)
|
||||
candidate = IOIteratorNext(iterator)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
public static func copy_mac_address() -> CFData? {
|
||||
// Prefer built-in network interfaces.
|
||||
// For example, an external Ethernet adaptor can displace
|
||||
// the built-in Wi-Fi as en0.
|
||||
guard let service = io_service(named: "en0", wantBuiltIn: true)
|
||||
?? io_service(named: "en1", wantBuiltIn: true)
|
||||
?? io_service(named: "en0", wantBuiltIn: false)
|
||||
else { return nil }
|
||||
defer { IOObjectRelease(service) }
|
||||
|
||||
|
||||
if let cftype = IORegistryEntrySearchCFProperty(
|
||||
service,
|
||||
kIOServicePlane,
|
||||
"IOMACAddress" as CFString,
|
||||
kCFAllocatorDefault,
|
||||
IOOptionBits(kIORegistryIterateRecursively | kIORegistryIterateParents)) {
|
||||
return (cftype as! CFData)
|
||||
}
|
||||
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user