From 9dfdf2fa52422709ce3df7a50643e0abc6ade319 Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Thu, 2 May 2024 11:54:13 +0200 Subject: [PATCH 01/16] Print backtrace on caught fault signals (unix) Useful for debugging nil derefs in linked Go code and whatnot. Co-authored-by: Sebastian Holmin --- mullvad-daemon/src/exception_logging/unix.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mullvad-daemon/src/exception_logging/unix.rs b/mullvad-daemon/src/exception_logging/unix.rs index 36d1f679dd01..2bbbb62dad01 100644 --- a/mullvad-daemon/src/exception_logging/unix.rs +++ b/mullvad-daemon/src/exception_logging/unix.rs @@ -4,6 +4,7 @@ use libc::siginfo_t; use nix::sys::signal::{sigaction, SaFlags, SigAction, SigHandler, SigSet, Signal}; use std::{ + backtrace::Backtrace, ffi::{c_int, c_void}, sync::Once, }; @@ -84,5 +85,9 @@ extern "C" fn fault_handler( }; log::error!("Caught signal {}", signal); + log::error!("Backtrace:"); + for line in format!("{}", Backtrace::force_capture()).lines() { + log::error!("{line}"); + } std::process::exit(2); } From da95b2603470841b64518959ceac0d03aab0068a Mon Sep 17 00:00:00 2001 From: Sebastian Holmin Date: Thu, 2 May 2024 11:54:13 +0200 Subject: [PATCH 02/16] Add a safe FFI wrapper in `wireguard-go-rs` - Add local wireguard go import - Activate DAITA and add `wgActivateDaita` and `wgReceiveEvent` FFI - Implement `start_daita` on Wireguard-go tunnel type - Mention DAITA in `wireguard-go-rs` description - Do not compile `wireguard-go-rs` on Windows - Handle DAITA closed on `nil` event - Handle daita action timeouts in libwg - Remove noisy log lines - Remove `maybenot_on_action` callback - Remove unused link to `../build/lib` for `talpid-wireguard` - Bump the `wireguard-go` submodule to a signed release tag in Mullvad's `wireguard-go` fork. - Update path to `libwg/go.sum` in verification script Also: - Use u64 instead of *mut void as log context - Make Tunnel::set_config take a &mut self - Use dyn Error instead of i32s for wg errors Co-authored-by: Joakim Hulthe --- .../verify-locked-down-signatures.yml | 2 +- .gitignore | 1 - .gitmodules | 3 + Cargo.lock | 18 +- Cargo.toml | 1 + build.sh | 6 +- ci/check-rust.sh | 5 - talpid-wireguard/Cargo.toml | 4 +- talpid-wireguard/build.rs | 16 - talpid-wireguard/src/config.rs | 4 +- talpid-wireguard/src/connectivity_check.rs | 16 +- talpid-wireguard/src/lib.rs | 57 ++-- talpid-wireguard/src/logging.rs | 35 +-- .../{wireguard_go.rs => wireguard_go/mod.rs} | 256 ++++++---------- .../src/wireguard_kernel/netlink_tunnel.rs | 9 +- .../src/wireguard_kernel/nm_tunnel.rs | 9 +- talpid-wireguard/src/wireguard_nt/mod.rs | 12 +- wireguard-go-rs/Cargo.toml | 10 + wireguard-go-rs/README.md | 10 + .../build-wireguard-go.sh | 64 ++-- wireguard-go-rs/build.rs | 65 ++++ .../libwg/Android.mk | 0 .../libwg/README.md | 0 .../libwg/build-android.sh | 0 {wireguard => wireguard-go-rs}/libwg/go.mod | 4 +- {wireguard => wireguard-go-rs}/libwg/go.sum | 6 +- .../goruntime-boottime-over-monotonic.diff | 171 +++++++++++ {wireguard => wireguard-go-rs}/libwg/libwg.go | 37 ++- wireguard-go-rs/libwg/libwg.h | 8 + .../libwg/libwg_android.go | 26 +- wireguard-go-rs/libwg/libwg_daita.go | 41 +++ .../libwg/libwg_default.go | 13 +- .../libwg/logging/logging.go | 7 +- .../libwg/tunnelcontainer/tunnelcontainer.go | 0 wireguard-go-rs/libwg/wireguard-go | 1 + wireguard-go-rs/src/lib.rs | 288 ++++++++++++++++++ wireguard-go-rs/src/util.rs | 15 + 37 files changed, 904 insertions(+), 316 deletions(-) rename talpid-wireguard/src/{wireguard_go.rs => wireguard_go/mod.rs} (67%) create mode 100644 wireguard-go-rs/Cargo.toml create mode 100644 wireguard-go-rs/README.md rename {wireguard => wireguard-go-rs}/build-wireguard-go.sh (71%) create mode 100644 wireguard-go-rs/build.rs rename {wireguard => wireguard-go-rs}/libwg/Android.mk (100%) rename {wireguard => wireguard-go-rs}/libwg/README.md (100%) rename {wireguard => wireguard-go-rs}/libwg/build-android.sh (100%) rename {wireguard => wireguard-go-rs}/libwg/go.mod (69%) rename {wireguard => wireguard-go-rs}/libwg/go.sum (56%) create mode 100644 wireguard-go-rs/libwg/goruntime-boottime-over-monotonic.diff rename {wireguard => wireguard-go-rs}/libwg/libwg.go (66%) create mode 100644 wireguard-go-rs/libwg/libwg.h rename {wireguard => wireguard-go-rs}/libwg/libwg_android.go (83%) create mode 100644 wireguard-go-rs/libwg/libwg_daita.go rename {wireguard => wireguard-go-rs}/libwg/libwg_default.go (86%) rename {wireguard => wireguard-go-rs}/libwg/logging/logging.go (93%) rename {wireguard => wireguard-go-rs}/libwg/tunnelcontainer/tunnelcontainer.go (100%) create mode 160000 wireguard-go-rs/libwg/wireguard-go create mode 100644 wireguard-go-rs/src/lib.rs create mode 100644 wireguard-go-rs/src/util.rs diff --git a/.github/workflows/verify-locked-down-signatures.yml b/.github/workflows/verify-locked-down-signatures.yml index 0e36154a15b3..118e44914a71 100644 --- a/.github/workflows/verify-locked-down-signatures.yml +++ b/.github/workflows/verify-locked-down-signatures.yml @@ -11,7 +11,7 @@ on: - deny.toml - test/deny.toml - gui/package-lock.json - - wireguard/libwg/go.sum + - wireguard-go-rs/libwg/go.sum - ci/keys/** - ci/verify-locked-down-signatures.sh - ios/MullvadVPN.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved diff --git a/.gitignore b/.gitignore index 1efb0161fcf2..569d6ca706a0 100644 --- a/.gitignore +++ b/.gitignore @@ -26,7 +26,6 @@ /android/keystore.properties /android/local.properties /android/play-api-key.json -/wireguard/libwg/libwg.h /wireguard/libwg/libwg.exp /wireguard/libwg/exports.def **/.vs/ diff --git a/.gitmodules b/.gitmodules index e8c3ba4187fd..39aa8b722dc3 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,6 @@ [submodule "windows/windows-libraries"] path = windows/windows-libraries url = https://github.com/mullvad/windows-libraries +[submodule "wireguard-go-rs/libwg/wireguard-go"] + path = wireguard-go-rs/libwg/wireguard-go + url = https://github.com/mullvad/wireguard-go/ diff --git a/Cargo.lock b/Cargo.lock index c6a1e328fbf9..8709388967f5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2026,9 +2026,9 @@ checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" [[package]] name = "maybenot" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94ed977e86fc65a7ffae967a6a973e6f7a90b5d747ebd755703d5718804f7c16" +checksum = "a7fe205734d700937dabf0b8687e290f8574fac996f8a9d04bd7a62d7c2c1dad" dependencies = [ "byteorder", "hex", @@ -4144,6 +4144,7 @@ dependencies = [ "tunnel-obfuscation", "widestring", "windows-sys 0.52.0", + "wireguard-go-rs", "zeroize", ] @@ -5078,6 +5079,15 @@ dependencies = [ "toml", ] +[[package]] +name = "wireguard-go-rs" +version = "0.0.0" +dependencies = [ + "log", + "thiserror", + "zeroize", +] + [[package]] name = "x25519-dalek" version = "2.0.1" @@ -5112,9 +5122,9 @@ dependencies = [ [[package]] name = "zeroize" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" dependencies = [ "zeroize_derive", ] diff --git a/Cargo.toml b/Cargo.toml index aa3b9145c884..e126c6a13ecc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,6 +39,7 @@ members = [ "talpid-windows", "talpid-wireguard", "tunnel-obfuscation", + "wireguard-go-rs" ] # Keep all lints in sync with `test/Cargo.toml` diff --git a/build.sh b/build.sh index 7b34896ca673..a3cbb50e3154 100755 --- a/build.sh +++ b/build.sh @@ -213,11 +213,6 @@ function build { # Compile and link all binaries. ################################################################################ - if [[ "$(uname -s)" != "MINGW"* ]]; then - log_header "Building wireguard-go$for_target_string" - ./wireguard/build-wireguard-go.sh "$current_target" - fi - log_header "Building Rust code in $RUST_BUILD_MODE mode using $RUSTC_VERSION$for_target_string" local cargo_target_arg=() @@ -312,6 +307,7 @@ for t in "${TARGETS[@]:-""}"; do build "$t" done + ################################################################################ # Package app. ################################################################################ diff --git a/ci/check-rust.sh b/ci/check-rust.sh index d42784d36e02..cb48fbed3f1b 100755 --- a/ci/check-rust.sh +++ b/ci/check-rust.sh @@ -4,11 +4,6 @@ set -eux export RUSTFLAGS="--deny warnings" -# Build WireGuard Go -if [[ "$(uname -s)" != "MINGW"* ]]; then - ./wireguard/build-wireguard-go.sh -fi - # Build Rust crates source env.sh time cargo build --locked --verbose diff --git a/talpid-wireguard/Cargo.toml b/talpid-wireguard/Cargo.toml index c1eb33576638..09b7baa57f12 100644 --- a/talpid-wireguard/Cargo.toml +++ b/talpid-wireguard/Cargo.toml @@ -30,6 +30,9 @@ tunnel-obfuscation = { path = "../tunnel-obfuscation" } rand = "0.8.5" surge-ping = "0.8.0" +[target.'cfg(not(windows))'.dependencies] +wireguard-go-rs = { path = "../wireguard-go-rs"} + [target.'cfg(target_os="android")'.dependencies] duct = "0.13" @@ -42,7 +45,6 @@ tokio-stream = { version = "0.1", features = ["io-util"] } [target.'cfg(unix)'.dependencies] nix = "0.23" -[target.'cfg(target_os = "linux")'.dependencies] rtnetlink = "0.11" netlink-packet-core = "0.4.2" netlink-packet-route = "0.13" diff --git a/talpid-wireguard/build.rs b/talpid-wireguard/build.rs index fe5b45b8199d..3abec6abe2aa 100644 --- a/talpid-wireguard/build.rs +++ b/talpid-wireguard/build.rs @@ -4,22 +4,6 @@ fn main() { let target_os = env::var("CARGO_CFG_TARGET_OS").expect("CARGO_CFG_TARGET_OS not set"); declare_libs_dir("../dist-assets/binaries"); - declare_libs_dir("../build/lib"); - - let link_type = match target_os.as_str() { - "android" => "", - "linux" | "macos" => "=static", - // We would like to avoid panicking on windows even if we can not link correctly - // because we would like to be able to run check and clippy. - // This does not allow for correct linking or buijding. - #[cfg(not(windows))] - "windows" => "", - #[cfg(windows)] - "windows" => "dylib", - _ => panic!("Unsupported platform: {target_os}"), - }; - - println!("cargo:rustc-link-lib{link_type}=wg"); add_wireguard_go_cfg(&target_os); } diff --git a/talpid-wireguard/src/config.rs b/talpid-wireguard/src/config.rs index f10a0e485991..e9059c6cca94 100644 --- a/talpid-wireguard/src/config.rs +++ b/talpid-wireguard/src/config.rs @@ -97,9 +97,9 @@ impl Config { enable_ipv6: generic_options.enable_ipv6, obfuscator_config: obfuscator_config.to_owned(), quantum_resistant: wg_options.quantum_resistant, - #[cfg(target_os = "windows")] + #[cfg(any(target_os = "windows", target_os = "linux"))] daita: wg_options.daita, - #[cfg(not(target_os = "windows"))] + #[cfg(not(any(target_os = "windows", target_os = "linux")))] daita: false, }; diff --git a/talpid-wireguard/src/connectivity_check.rs b/talpid-wireguard/src/connectivity_check.rs index 70f88e687215..41ad0b5bf34e 100644 --- a/talpid-wireguard/src/connectivity_check.rs +++ b/talpid-wireguard/src/connectivity_check.rs @@ -5,9 +5,10 @@ use crate::{ use std::{ cmp, net::Ipv4Addr, - sync::{mpsc, Mutex, Weak}, + sync::{mpsc, Weak}, time::{Duration, Instant}, }; +use tokio::sync::Mutex; use super::{Tunnel, TunnelError}; @@ -211,11 +212,12 @@ impl ConnectivityMonitor { /// If None is returned, then the underlying tunnel has already been closed and all subsequent /// calls will also return None. + /// + /// NOTE: will panic if called from within a tokio runtime. fn get_stats(&self) -> Option> { self.tunnel_handle .upgrade()? - .lock() - .ok()? + .blocking_lock() .as_ref() .and_then(|tunnel| match tunnel.get_tunnel_stats() { Ok(stats) if stats.is_empty() => { @@ -550,7 +552,7 @@ mod test { rx_bytes: 0, }, ); - let peers = Mutex::new(map); + let peers = std::sync::Mutex::new(map); Self { on_get_stats: Box::new(move || { let mut peers = peers.lock().unwrap(); @@ -607,13 +609,13 @@ mod test { } fn set_config( - &self, + &mut self, _config: Config, ) -> Pin> + Send>> { Box::pin(async { Ok(()) }) } - #[cfg(target_os = "windows")] + #[cfg(any(target_os = "windows", target_os = "linux"))] fn start_daita(&mut self) -> std::result::Result<(), TunnelError> { Ok(()) } @@ -745,7 +747,7 @@ mod test { rx_bytes: 0, }, ); - let tunnel_stats = Mutex::new(map); + let tunnel_stats = std::sync::Mutex::new(map); let pinger = MockPinger::default(); let (_tunnel_anchor, tunnel) = MockTunnel::new(move || { diff --git a/talpid-wireguard/src/lib.rs b/talpid-wireguard/src/lib.rs index 7c01538b60ac..e8334f43d1df 100644 --- a/talpid-wireguard/src/lib.rs +++ b/talpid-wireguard/src/lib.rs @@ -115,7 +115,7 @@ impl Error { Error::CreateObfuscatorError(_) => true, Error::ObfuscatorError(_) => true, Error::PskNegotiationError(_) => true, - Error::TunnelError(TunnelError::RecoverableStartWireguardError) => true, + Error::TunnelError(TunnelError::RecoverableStartWireguardError(..)) => true, Error::SetupRoutingError(error) => error.is_recoverable(), @@ -144,7 +144,7 @@ impl Error { pub struct WireguardMonitor { runtime: tokio::runtime::Handle, /// Tunnel implementation - tunnel: Arc>>>, + tunnel: Arc>>>, /// Callback to signal tunnel events event_callback: EventCallback, close_msg_receiver: sync_mpsc::Receiver, @@ -306,7 +306,7 @@ impl WireguardMonitor { let (pinger_tx, pinger_rx) = sync_mpsc::channel(); let monitor = WireguardMonitor { runtime: args.runtime.clone(), - tunnel: Arc::new(Mutex::new(Some(tunnel))), + tunnel: Arc::new(AsyncMutex::new(Some(tunnel))), event_callback, close_msg_receiver: close_obfs_listener, pinger_stop_sender: pinger_tx, @@ -473,7 +473,7 @@ impl WireguardMonitor { #[allow(clippy::too_many_arguments)] async fn config_ephemeral_peers( - tunnel: &Arc>>>, + tunnel: &Arc>>>, config: &mut Config, retry_attempt: u32, on_event: F, @@ -576,10 +576,10 @@ impl WireguardMonitor { ) .await?; - #[cfg(target_os = "windows")] + #[cfg(any(target_os = "windows", target_os = "linux"))] if config.daita { // Start local DAITA machines - let mut tunnel = tunnel.lock().unwrap(); + let mut tunnel = tunnel.lock().await; if let Some(tunnel) = tunnel.as_mut() { tunnel .start_daita() @@ -601,7 +601,7 @@ impl WireguardMonitor { /// Reconfigures the tunnel to use the provided config while potentially modifying the config /// and restarting the obfuscation provider. Returns the new config used by the new tunnel. async fn reconfigure_tunnel( - tunnel: &Arc>>>, + tunnel: &Arc>>>, mut config: Config, obfuscator: Arc>>, close_obfs_sender: sync_mpsc::Sender, @@ -625,11 +625,12 @@ impl WireguardMonitor { } } + let mut tunnel = tunnel.lock().await; + let set_config_future = tunnel - .lock() - .unwrap() - .as_ref() + .as_mut() .map(|tunnel| tunnel.set_config(config.clone())); + if let Some(f) = set_config_future { f.await .map_err(Error::TunnelError) @@ -817,6 +818,7 @@ impl WireguardMonitor { log_path, tun_provider, routes, + resource_dir, ) .map_err(Error::TunnelError)?, )) @@ -843,8 +845,11 @@ impl WireguardMonitor { wait_result } + /// Tear down the tunnel. + /// + /// NOTE: will panic if called from within a tokio runtime. fn stop_tunnel(&mut self) { - match self.tunnel.lock().expect("Tunnel lock poisoned").take() { + match self.tunnel.blocking_lock().take() { Some(tunnel) => { if let Err(e) = tunnel.stop() { log::error!("{}", e.display_chain_with_msg("Failed to stop tunnel")); @@ -1025,11 +1030,12 @@ pub(crate) trait Tunnel: Send { fn get_interface_name(&self) -> String; fn stop(self: Box) -> std::result::Result<(), TunnelError>; fn get_tunnel_stats(&self) -> std::result::Result; - fn set_config( - &self, + fn set_config<'a>( + &'a mut self, _config: Config, - ) -> Pin> + Send>>; - #[cfg(target_os = "windows")] + ) -> Pin> + Send + 'a>>; + #[cfg(any(target_os = "windows", target_os = "linux"))] + /// A [`Tunnel`] capable of using DAITA. fn start_daita(&mut self) -> std::result::Result<(), TunnelError>; } @@ -1041,7 +1047,7 @@ pub enum TunnelError { /// This is an error returned by the implementation that indicates that trying to establish the /// tunnel again should work normally. The error encountered is known to be sporadic. #[error("Recoverable error while starting wireguard tunnel")] - RecoverableStartWireguardError, + RecoverableStartWireguardError(#[source] Box), /// An unrecoverable error occurred while starting the wireguard tunnel /// @@ -1049,14 +1055,11 @@ pub enum TunnelError { /// tunnel again will likely fail with the same error. An error was encountered during tunnel /// configuration which can't be dealt with gracefully. #[error("Failed to start wireguard tunnel")] - FatalStartWireguardError, + FatalStartWireguardError(#[source] Box), /// Failed to tear down wireguard tunnel. - #[error("Failed to stop wireguard tunnel. Status: {status}")] - StopWireguardError { - /// Returned error code - status: i32, - }, + #[error("Failed to tear down wireguard tunnel")] + StopWireguardError(#[source] Box), /// Error whilst trying to parse the WireGuard config to read the stats #[error("Reading tunnel stats failed")] @@ -1107,6 +1110,16 @@ pub enum TunnelError { /// Failure to set up logging #[error("Failed to set up logging")] LoggingError(#[source] logging::Error), + + /// Failed to receive DAITA event + #[cfg(any(target_os = "windows", target_os = "linux"))] + #[error("Failed to start DAITA")] + StartDaita(#[source] Box), + + /// This tunnel does not support DAITA. + #[cfg(any(target_os = "windows", target_os = "linux"))] + #[error("Failed to start DAITA - tunnel implemenation does not support DAITA")] + DaitaNotSupported, } #[cfg(target_os = "linux")] diff --git a/talpid-wireguard/src/logging.rs b/talpid-wireguard/src/logging.rs index 6d1d364342f7..a4d8c7f240b8 100644 --- a/talpid-wireguard/src/logging.rs +++ b/talpid-wireguard/src/logging.rs @@ -2,9 +2,13 @@ use once_cell::sync::Lazy; use parking_lot::Mutex; use std::{collections::HashMap, fmt, fs, io::Write, path::Path}; -static LOG_MUTEX: Lazy>> = Lazy::new(|| Mutex::new(HashMap::new())); +static LOG_MUTEX: Lazy> = Lazy::new(|| Mutex::new(LogState::default())); -static mut LOG_CONTEXT_NEXT_ORDINAL: u32 = 0; +#[derive(Default)] +struct LogState { + map: HashMap, + next_ordinal: u64, +} /// Errors encountered when initializing logging #[derive(thiserror::Error, Debug)] @@ -14,18 +18,15 @@ pub enum Error { PrepareLogFileError(#[from] std::io::Error), } -pub fn initialize_logging(log_path: Option<&Path>) -> Result { +pub fn initialize_logging(log_path: Option<&Path>) -> Result { let log_file = create_log_file(log_path)?; - let log_context_ordinal = unsafe { - let mut map = LOG_MUTEX.lock(); - let ordinal = LOG_CONTEXT_NEXT_ORDINAL; - LOG_CONTEXT_NEXT_ORDINAL += 1; - map.insert(ordinal, log_file); - ordinal - }; + let mut state = LOG_MUTEX.lock(); + let ordinal = state.next_ordinal; + state.next_ordinal += 1; + state.map.insert(ordinal, log_file); - Ok(log_context_ordinal) + Ok(ordinal) } #[cfg(target_os = "windows")] @@ -39,9 +40,9 @@ fn create_log_file(log_path: Option<&Path>) -> Result { .map_err(Error::PrepareLogFileError) } -pub fn clean_up_logging(ordinal: u32) { - let mut map = LOG_MUTEX.lock(); - map.remove(&ordinal); +pub fn clean_up_logging(ordinal: u64) { + let mut state = LOG_MUTEX.lock(); + state.map.remove(&ordinal); } pub enum LogLevel { @@ -71,9 +72,9 @@ impl AsRef for LogLevel { } } -pub fn log(context: u32, level: LogLevel, tag: &str, msg: &str) { - let mut map = LOG_MUTEX.lock(); - if let Some(logfile) = map.get_mut(&{ context }) { +pub fn log(context: u64, level: LogLevel, tag: &str, msg: &str) { + let mut state = LOG_MUTEX.lock(); + if let Some(logfile) = state.map.get_mut(&context) { log_inner(logfile, level, tag, msg); } } diff --git a/talpid-wireguard/src/wireguard_go.rs b/talpid-wireguard/src/wireguard_go/mod.rs similarity index 67% rename from talpid-wireguard/src/wireguard_go.rs rename to talpid-wireguard/src/wireguard_go/mod.rs index b08b241bb941..32181beaea43 100644 --- a/talpid-wireguard/src/wireguard_go.rs +++ b/talpid-wireguard/src/wireguard_go/mod.rs @@ -1,35 +1,40 @@ -use super::{ - stats::{Stats, StatsMap}, - Config, Tunnel, TunnelError, -}; -use crate::logging::{clean_up_logging, initialize_logging}; use ipnetwork::IpNetwork; +#[cfg(any(target_os = "windows", target_os = "linux"))] +use once_cell::sync::OnceCell; +#[cfg(any(target_os = "windows", target_os = "linux"))] +use std::{ffi::CString, fs, path::PathBuf}; use std::{ - ffi::{c_char, c_void, CStr}, future::Future, + net::IpAddr, + os::unix::io::{AsRawFd, RawFd}, path::Path, pin::Pin, + sync::{Arc, Mutex}, }; -use talpid_tunnel::tun_provider::TunProvider; -use talpid_types::BoxedError; -use zeroize::Zeroize; - #[cfg(target_os = "android")] -use talpid_tunnel::tun_provider; +use talpid_tunnel::tun_provider::Error as TunProviderError; +use talpid_tunnel::tun_provider::{Tun, TunConfig, TunProvider}; +use talpid_types::BoxedError; -use std::{ - net::IpAddr, - os::unix::io::{AsRawFd, RawFd}, +use super::{ + stats::{Stats, StatsMap}, + Config, Tunnel, TunnelError, }; -use talpid_tunnel::tun_provider::{Tun, TunConfig}; +use crate::logging::{clean_up_logging, initialize_logging}; -type Result = std::result::Result; +const MAX_PREPARE_TUN_ATTEMPTS: usize = 4; -use std::sync::{Arc, Mutex}; +/// Maximum number of events that can be stored in the underlying buffer +#[cfg(any(target_os = "windows", target_os = "linux"))] +const DAITA_EVENTS_CAPACITY: u32 = 1000; -const MAX_PREPARE_TUN_ATTEMPTS: usize = 4; +/// Maximum number of actions that can be stored in the underlying buffer +#[cfg(any(target_os = "windows", target_os = "linux"))] +const DAITA_ACTIONS_CAPACITY: u32 = 1000; + +type Result = std::result::Result; -struct LoggingContext(u32); +struct LoggingContext(u64); impl Drop for LoggingContext { fn drop(&mut self) { @@ -39,7 +44,7 @@ impl Drop for LoggingContext { pub struct WgGoTunnel { interface_name: String, - handle: Option, + tunnel_handle: wireguard_go_rs::Tunnel, // holding on to the tunnel device and the log file ensures that the associated file handles // live long enough and get closed when the tunnel is stopped _tunnel_device: Tun, @@ -47,6 +52,9 @@ pub struct WgGoTunnel { _logging_context: LoggingContext, #[cfg(target_os = "android")] tun_provider: Arc>, + #[cfg(any(target_os = "windows", target_os = "linux"))] + resource_dir: PathBuf, + config: Config, } impl WgGoTunnel { @@ -55,6 +63,7 @@ impl WgGoTunnel { log_path: Option<&Path>, tun_provider: Arc>, routes: impl Iterator, + resource_dir: &Path, ) -> Result { #[cfg(target_os = "android")] let tun_provider_clone = tun_provider.clone(); @@ -70,29 +79,30 @@ impl WgGoTunnel { #[cfg(not(target_os = "android"))] let mtu = config.mtu as isize; - let handle = unsafe { - wgTurnOn( - #[cfg(not(target_os = "android"))] - mtu, - wg_config_str.as_ptr() as _, - tunnel_fd, - Some(logging::wg_go_logging_callback), - logging_context.0 as *mut c_void, - ) - }; - check_wg_status(handle)?; + let handle = wireguard_go_rs::Tunnel::turn_on( + #[cfg(not(target_os = "android"))] + mtu, + &wg_config_str, + tunnel_fd, + Some(logging::wg_go_logging_callback), + logging_context.0, + ) + .map_err(|e| TunnelError::FatalStartWireguardError(Box::new(e)))?; #[cfg(target_os = "android")] - Self::bypass_tunnel_sockets(&mut tunnel_device, handle) + Self::bypass_tunnel_sockets(&handle, &mut tunnel_device) .map_err(TunnelError::BypassError)?; Ok(WgGoTunnel { interface_name, - handle: Some(handle), + tunnel_handle: handle, _tunnel_device: tunnel_device, _logging_context: logging_context, #[cfg(target_os = "android")] tun_provider: tun_provider_clone, + resource_dir: resource_dir.to_owned(), + #[cfg(any(target_os = "windows", target_os = "linux"))] + config: config.clone(), }) } @@ -130,11 +140,11 @@ impl WgGoTunnel { #[cfg(target_os = "android")] fn bypass_tunnel_sockets( + handle: &wireguard_go_rs::Tunnel, tunnel_device: &mut Tun, - handle: i32, - ) -> std::result::Result<(), tun_provider::Error> { - let socket_v4 = unsafe { wgGetSocketV4(handle) }; - let socket_v6 = unsafe { wgGetSocketV6(handle) }; + ) -> std::result::Result<(), TunProviderError> { + let socket_v4 = handle.get_socket_v4(); + let socket_v6 = handle.get_socket_v6(); tunnel_device.bypass(socket_v4)?; tunnel_device.bypass(socket_v6)?; @@ -142,16 +152,6 @@ impl WgGoTunnel { Ok(()) } - fn stop_tunnel(&mut self) -> Result<()> { - if let Some(handle) = self.handle.take() { - let status = unsafe { wgTurnOff(handle) }; - if status < 0 { - return Err(TunnelError::StopWireguardError { status }); - } - } - Ok(()) - } - fn get_tunnel( tun_provider: Arc>, config: &Config, @@ -187,71 +187,46 @@ impl WgGoTunnel { } } -impl Drop for WgGoTunnel { - fn drop(&mut self) { - if let Err(e) = self.stop_tunnel() { - log::error!("Failed to stop tunnel: {}", e); - } - } -} - impl Tunnel for WgGoTunnel { fn get_interface_name(&self) -> String { self.interface_name.clone() } fn get_tunnel_stats(&self) -> Result { - let config_str = unsafe { - let ptr = wgGetConfig(self.handle.unwrap()); - if ptr.is_null() { - log::error!("Failed to get config !"); - return Err(TunnelError::GetConfigError); - } - - CStr::from_ptr(ptr) - }; - - let result = - Stats::parse_config_str(config_str.to_str().expect("Go strings are always UTF-8")) - .map_err(|error| TunnelError::StatsError(BoxedError::new(error))); - unsafe { - // Zeroing out config string to not leave private key in memory. - let slice = std::slice::from_raw_parts_mut( - config_str.as_ptr() as *mut c_char, - config_str.to_bytes().len(), - ); - slice.zeroize(); - - wgFreePtr(config_str.as_ptr() as *mut c_void); - } - - result + self.tunnel_handle + .get_config(|cstr| { + Stats::parse_config_str(cstr.to_str().expect("Go strings are always UTF-8")) + }) + .ok_or(TunnelError::GetConfigError)? + .map_err(|error| TunnelError::StatsError(BoxedError::new(error))) } - fn stop(mut self: Box) -> Result<()> { - self.stop_tunnel() + fn stop(self: Box) -> Result<()> { + self.tunnel_handle + .turn_off() + .map_err(|e| TunnelError::StopWireguardError(Box::new(e))) } fn set_config( - &self, + &mut self, config: Config, - ) -> Pin> + Send>> { - let wg_config_str = config.to_userspace_format(); - let handle = self.handle.unwrap(); - #[cfg(target_os = "android")] - let tun_provider = self.tun_provider.clone(); + ) -> Pin> + Send + '_>> { Box::pin(async move { - let status = unsafe { wgSetConfig(handle, wg_config_str.as_ptr() as _) }; - if status != 0 { - return Err(TunnelError::SetConfigError); - } + let wg_config_str = config.to_userspace_format(); + + self.tunnel_handle + .set_config(&wg_config_str) + .map_err(|_| TunnelError::SetConfigError)?; + + #[cfg(target_os = "android")] + let tun_provider = self.tun_provider.clone(); // When reapplying the config, the endpoint socket may be discarded // and needs to be excluded again #[cfg(target_os = "android")] { - let socket_v4 = unsafe { wgGetSocketV4(handle) }; - let socket_v6 = unsafe { wgGetSocketV6(handle) }; + let socket_v4 = self.tunnel_handle.get_socket_v4(); + let socket_v6 = self.tunnel_handle.get_socket_v6(); let mut provider = tun_provider.lock().unwrap(); provider .bypass(socket_v4) @@ -264,68 +239,32 @@ impl Tunnel for WgGoTunnel { Ok(()) }) } -} - -fn check_wg_status(wg_code: i32) -> Result<()> { - match wg_code { - ERROR_GENERAL_FAILURE => Err(TunnelError::FatalStartWireguardError), - ERROR_INTERMITTENT_FAILURE => Err(TunnelError::RecoverableStartWireguardError), - 0.. => Ok(()), - _ => { - log::error!("Unknown status code returned from wireguard-go"); - Err(TunnelError::FatalStartWireguardError) - } - } -} - -pub type Fd = std::os::unix::io::RawFd; - -const ERROR_GENERAL_FAILURE: i32 = -1; -const ERROR_INTERMITTENT_FAILURE: i32 = -2; - -extern "C" { - /// Creates a new wireguard tunnel, uses the specific interface name, MTU and file descriptors - /// for the tunnel device and logging. - /// - /// Positive return values are tunnel handles for this specific wireguard tunnel instance. - /// Negative return values signify errors. All error codes are opaque. - #[cfg(not(target_os = "android"))] - fn wgTurnOn( - mtu: isize, - settings: *const i8, - fd: Fd, - logging_callback: Option, - logging_context: *mut c_void, - ) -> i32; - - // Android - #[cfg(target_os = "android")] - fn wgTurnOn( - settings: *const i8, - fd: Fd, - logging_callback: Option, - logging_context: *mut c_void, - ) -> i32; - - // Pass a handle that was created by wgTurnOn to stop a wireguard tunnel. - fn wgTurnOff(handle: i32) -> i32; - - // Returns the file descriptor of the tunnel IPv4 socket. - fn wgGetConfig(handle: i32) -> *mut c_char; - // Sets the config of the WireGuard interface. - fn wgSetConfig(handle: i32, settings: *const i8) -> i32; - - // Frees a pointer allocated by the go runtime - useful to free return value of wgGetConfig - fn wgFreePtr(ptr: *mut c_void); - - // Returns the file descriptor of the tunnel IPv4 socket. - #[cfg(target_os = "android")] - fn wgGetSocketV4(handle: i32) -> Fd; + fn start_daita(&mut self) -> Result<()> { + static MAYBENOT_MACHINES: OnceCell = OnceCell::new(); + let machines = MAYBENOT_MACHINES.get_or_try_init(|| { + let path = self.resource_dir.join("maybenot_machines"); + log::debug!("Reading maybenot machines from {}", path.display()); + + // TODO: errors + let machines = fs::read_to_string(path).unwrap(); + let machines = CString::new(machines).unwrap(); + Ok(machines) + })?; + + log::info!("Initializing DAITA for wireguard device"); + let peer_public_key = &self.config.entry_peer.public_key; + self.tunnel_handle + .activate_daita( + peer_public_key.as_bytes(), + machines, + DAITA_EVENTS_CAPACITY, + DAITA_ACTIONS_CAPACITY, + ) + .map_err(|e| TunnelError::StartDaita(Box::new(e)))?; - // Returns the file descriptor of the tunnel IPv6 socket. - #[cfg(target_os = "android")] - fn wgGetSocketV6(handle: i32) -> Fd; + Ok(()) + } } mod stats { @@ -438,13 +377,13 @@ mod stats { mod logging { use super::super::logging::{log, LogLevel}; - use std::ffi::{c_char, c_void}; + use std::ffi::c_char; // Callback that receives messages from WireGuard pub unsafe extern "system" fn wg_go_logging_callback( level: WgLogLevel, msg: *const c_char, - context: *mut c_void, + context: u64, ) { let managed_msg = if !msg.is_null() { std::ffi::CStr::from_ptr(msg).to_string_lossy().to_string() @@ -457,7 +396,7 @@ mod logging { _ => LogLevel::Error, }; - log(context as u32, level, "wireguard-go", &managed_msg); + log(context, level, "wireguard-go", &managed_msg); } // wireguard-go supports log levels 0 through 3 with 3 being the most verbose @@ -466,7 +405,4 @@ mod logging { const WG_GO_LOG_VERBOSE: WgLogLevel = 2; pub type WgLogLevel = u32; - - pub type LoggingCallback = - unsafe extern "system" fn(level: WgLogLevel, msg: *const c_char, context: *mut c_void); } diff --git a/talpid-wireguard/src/wireguard_kernel/netlink_tunnel.rs b/talpid-wireguard/src/wireguard_kernel/netlink_tunnel.rs index 579bcde65aa0..bdc187e1bdb3 100644 --- a/talpid-wireguard/src/wireguard_kernel/netlink_tunnel.rs +++ b/talpid-wireguard/src/wireguard_kernel/netlink_tunnel.rs @@ -93,7 +93,7 @@ impl Tunnel for NetlinkTunnel { tokio_handle.block_on(async move { if let Err(err) = netlink_connections.delete_device(interface_index).await { log::error!("Failed to remove WireGuard device: {}", err); - Err(TunnelError::FatalStartWireguardError) + Err(TunnelError::FatalStartWireguardError(Box::new(err))) } else { Ok(()) } @@ -113,7 +113,7 @@ impl Tunnel for NetlinkTunnel { } fn set_config( - &self, + &mut self, config: Config, ) -> Pin> + Send + 'static>> { let mut wg = self.netlink_connections.wg_handle.clone(); @@ -127,4 +127,9 @@ impl Tunnel for NetlinkTunnel { }) }) } + + // TODO: We shouldn't force `NetlinkTunnel` to implement `start_daita` + fn start_daita(&mut self) -> std::result::Result<(), TunnelError> { + unreachable!("Netlink tunnel does not support DAITA") + } } diff --git a/talpid-wireguard/src/wireguard_kernel/nm_tunnel.rs b/talpid-wireguard/src/wireguard_kernel/nm_tunnel.rs index 7c24c42a7063..9fc935159156 100644 --- a/talpid-wireguard/src/wireguard_kernel/nm_tunnel.rs +++ b/talpid-wireguard/src/wireguard_kernel/nm_tunnel.rs @@ -70,7 +70,7 @@ impl Tunnel for NetworkManagerTunnel { if let Some(tunnel) = self.tunnel.take() { if let Err(err) = self.network_manager.remove_tunnel(tunnel) { log::error!("Failed to remove WireGuard tunnel via NM: {}", err); - Err(TunnelError::StopWireguardError { status: 0 }) + Err(TunnelError::StopWireguardError(Box::new(err))) } else { Ok(()) } @@ -94,7 +94,7 @@ impl Tunnel for NetworkManagerTunnel { } fn set_config( - &self, + &mut self, config: Config, ) -> Pin> + Send>> { let interface_name = self.interface_name.clone(); @@ -110,6 +110,11 @@ impl Tunnel for NetworkManagerTunnel { }) }) } + + // TODO: We shouldn't force `NetworkManagerTunnel` tunnel to implement `start_daita` + fn start_daita(&mut self) -> std::result::Result<(), TunnelError> { + unreachable!("NetworkManager tunnel does not support DAITA") + } } fn convert_config_to_dbus(config: &Config) -> DeviceConfig { diff --git a/talpid-wireguard/src/wireguard_nt/mod.rs b/talpid-wireguard/src/wireguard_nt/mod.rs index 375f28844a48..de32c8d83e83 100644 --- a/talpid-wireguard/src/wireguard_nt/mod.rs +++ b/talpid-wireguard/src/wireguard_nt/mod.rs @@ -70,7 +70,6 @@ type WireGuardGetConfigurationFn = unsafe extern "stdcall" fn( type WireGuardSetStateFn = unsafe extern "stdcall" fn(adapter: RawHandle, state: WgAdapterState) -> BOOL; -#[cfg(windows)] #[repr(C)] #[allow(dead_code)] enum LogLevel { @@ -79,7 +78,6 @@ enum LogLevel { Err = 2, } -#[cfg(windows)] impl From for logging::LogLevel { fn from(level: LogLevel) -> Self { match level { @@ -430,7 +428,7 @@ impl WgNtTunnel { match error { Error::CreateTunnelDevice(error) => super::TunnelError::SetupTunnelDevice(error), - _ => super::TunnelError::FatalStartWireguardError, + _ => super::TunnelError::FatalStartWireguardError(Box::new(error)), } }) } @@ -542,11 +540,11 @@ impl Drop for WgNtTunnel { } } -static LOG_CONTEXT: Lazy>> = Lazy::new(|| Mutex::new(None)); +static LOG_CONTEXT: Lazy>> = Lazy::new(|| Mutex::new(None)); struct LoggerHandle { dll: &'static WgNtDll, - context: u32, + context: u64, } impl LoggerHandle { @@ -1080,7 +1078,7 @@ impl Tunnel for WgNtTunnel { } fn set_config( - &self, + &mut self, config: Config, ) -> Pin> + Send>> { let device = self.device.clone(); @@ -1145,7 +1143,6 @@ mod tests { allowed_ips: vec!["1.3.3.0/24".parse().unwrap()], endpoint: "1.2.3.4:1234".parse().unwrap(), psk: None, - #[cfg(target_os = "windows")] constant_packet_size: false, }, exit_peer: None, @@ -1181,7 +1178,6 @@ mod tests { rx_bytes: 0, last_handshake: 0, allowed_ips_count: 1, - #[cfg(target_os = "windows")] constant_packet_size: 0, }, p0_allowed_ip_0: WgAllowedIp { diff --git a/wireguard-go-rs/Cargo.toml b/wireguard-go-rs/Cargo.toml new file mode 100644 index 000000000000..3725787bf6dc --- /dev/null +++ b/wireguard-go-rs/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "wireguard-go-rs" +description = "Rust bindings to wireguard-go with DAITA support" +edition = "2021" +license.workspace = true + +[dependencies] +thiserror.workspace = true +log.workspace = true +zeroize = "1.8.1" diff --git a/wireguard-go-rs/README.md b/wireguard-go-rs/README.md new file mode 100644 index 000000000000..24a08fa54b24 --- /dev/null +++ b/wireguard-go-rs/README.md @@ -0,0 +1,10 @@ +# `wireguard-go-rs` +This crate wraps `libwg`, which in turn wraps [Mullvad VPN's fork of wireguard-go](https://github.com/mullvad/wireguard-go) which extends `wireguard-go` with [DAITA](https://mullvad.net/en/blog/introducing-defense-against-ai-guided-traffic-analysis-daita). + +## Known limitation +To extend `wireguard-go` with DAITA capabilities, it statically links against [maybenot](https://github.com/maybenot-io/maybenot/), which at the time of writing will cause issues if it in turn is statically linked from another Rust crate: https://github.com/rust-lang/rust/issues/104707. +As such, `libwg` is built as a shared object which you have to link to dynamically. +To get rid of this limitation, you could compile `wireguard-go` without DAITA support. See [build-wireguard-go.sh](./build-wireguard-go.sh) for details. + +## Upgrading `wireguard-go` +Upgrading `wireguard-go` involves updating the git submodule found in `libwg/wireguard-go`. This module uses [Mullvad VPN's fork of wireguard-go](https://github.com/mullvad/wireguard-go). diff --git a/wireguard/build-wireguard-go.sh b/wireguard-go-rs/build-wireguard-go.sh similarity index 71% rename from wireguard/build-wireguard-go.sh rename to wireguard-go-rs/build-wireguard-go.sh index 7e3b11910d82..e270fb82191a 100755 --- a/wireguard/build-wireguard-go.sh +++ b/wireguard-go-rs/build-wireguard-go.sh @@ -1,30 +1,27 @@ #!/usr/bin/env bash # This script is used to build wireguard-go libraries for all the platforms. +# +# If "DAITA" support should be enabled, pass the `--daita` flag when invoking this script. set -eu -function is_android_build { - for arg in "$@" - do - case "$arg" in - "--android") - return 0 - esac - done - return 1 -} +# If Wireguard-go should be built with DAITA-support. +DAITA="false" +# If the target OS is Adnroid. +ANDROID="false" -function is_docker_build { - for arg in "$@" - do - case "$arg" in - "--no-docker") - return 1 - esac - done - return 0 -} +while [[ "$#" -gt 0 ]]; do + case $1 in + --android) ANDROID="true";; + --daita) DAITA="true";; + *) + log_error "Unknown parameter: $1" + exit 1 + ;; + esac + shift +done function unix_target_triple { local platform @@ -48,6 +45,7 @@ function unix_target_triple { function build_unix { + # TODO: consider using `log_header` here echo "Building wireguard-go for $1" # Flags for cross compiling @@ -80,33 +78,35 @@ function build_unix { fi fi - pushd libwg - target_triple_dir="../../build/lib/$1" - mkdir -p "$target_triple_dir" - go build -v -o "$target_triple_dir"/libwg.a -buildmode c-archive + # Build wiregaurd-go as a library + pushd libwg + if [[ "$DAITA" == "true" ]]; then + pushd wireguard-go + make libmaybenot.a LIBDEST="$OUT_DIR" + popd + go build -v --tags daita -o "$OUT_DIR"/libwg.a -buildmode c-archive + else + go build -v -o "$OUT_DIR"/libwg.a -buildmode c-archive + fi popd } function build_android { - echo "Building for android" + echo "Building wireguard-go for android" - if is_docker_build "$@"; then - ../building/container-run.sh android wireguard/libwg/build-android.sh - else - ./libwg/build-android.sh - fi + ./libwg/build-android.sh } function build_wireguard_go { - if is_android_build "$@"; then + if [[ "$ANDROID" == "true" ]]; then build_android "$@" return fi local platform platform="$(uname -s)"; - case "$platform" in + case "$platform" in Linux*|Darwin*) build_unix "${1:-$(unix_target_triple)}";; *) echo "Unsupported platform" diff --git a/wireguard-go-rs/build.rs b/wireguard-go-rs/build.rs new file mode 100644 index 000000000000..82a8ff254b55 --- /dev/null +++ b/wireguard-go-rs/build.rs @@ -0,0 +1,65 @@ +use core::{panic, str}; +use std::{env, path::PathBuf}; + +fn main() { + let out_dir = env::var("OUT_DIR").expect("Missing OUT_DIR"); + eprintln!("OUT_DIR: {out_dir}"); + + let target_os = env::var("CARGO_CFG_TARGET_OS").expect("Missing 'CARGO_CFG_TARGET_OS"); + let mut cmd = std::process::Command::new("bash"); + cmd.arg("./build-wireguard-go.sh"); + + match target_os.as_str() { + "linux" => { + // Enable DAITA & Tell rustc to link libmaybenot + println!("cargo::rustc-link-lib=static=maybenot"); + // Tell the build script to build wireguard-go with DAITA support + cmd.arg("--daita"); + } + "android" => { + cmd.arg("--android"); + } + "macos" => {} + // building wireguard-go-rs for windows is not implemented + _ => return, + } + + let output = cmd.output().expect("build-wireguard-go.sh failed"); + if !output.status.success() { + let stdout = str::from_utf8(&output.stdout).unwrap(); + let stderr = str::from_utf8(&output.stderr).unwrap(); + eprintln!("build-wireguard-go.sh failed."); + eprintln!("stdout:\n{stdout}"); + eprintln!("stderr:\n{stderr}"); + panic!(); + } + + if target_os == "android" { + // NOTE: Go programs does not support being statically linked on android + // so we need to dynamically link to libwg + println!("cargo::rustc-link-lib=wg"); + declare_libs_dir("../build/lib"); + } else { + // other platforms can statically link to libwg just fine + // TODO: consider doing dynamic linking everywhere, to keep things simpler + println!("cargo::rustc-link-lib=static=wg"); + println!("cargo::rustc-link-search={out_dir}"); + } + + println!("cargo::rerun-if-changed=libwg"); +} + +/// Tell linker to check `base`/$TARGET for shared libraries. +fn declare_libs_dir(base: &str) { + let target_triplet = env::var("TARGET").expect("TARGET is not set"); + let lib_dir = manifest_dir().join(base).join(target_triplet); + println!("cargo::rerun-if-changed={}", lib_dir.display()); + println!("cargo::rustc-link-search={}", lib_dir.display()); +} + +/// Get the directory containing `Cargo.toml` +fn manifest_dir() -> PathBuf { + env::var("CARGO_MANIFEST_DIR") + .map(PathBuf::from) + .expect("CARGO_MANIFEST_DIR env var not set") +} diff --git a/wireguard/libwg/Android.mk b/wireguard-go-rs/libwg/Android.mk similarity index 100% rename from wireguard/libwg/Android.mk rename to wireguard-go-rs/libwg/Android.mk diff --git a/wireguard/libwg/README.md b/wireguard-go-rs/libwg/README.md similarity index 100% rename from wireguard/libwg/README.md rename to wireguard-go-rs/libwg/README.md diff --git a/wireguard/libwg/build-android.sh b/wireguard-go-rs/libwg/build-android.sh similarity index 100% rename from wireguard/libwg/build-android.sh rename to wireguard-go-rs/libwg/build-android.sh diff --git a/wireguard/libwg/go.mod b/wireguard-go-rs/libwg/go.mod similarity index 69% rename from wireguard/libwg/go.mod rename to wireguard-go-rs/libwg/go.mod index 2f65c7bf7b3a..76627dcb7f93 100644 --- a/wireguard/libwg/go.mod +++ b/wireguard-go-rs/libwg/go.mod @@ -10,5 +10,7 @@ require ( require ( golang.org/x/crypto v0.22.0 // indirect golang.org/x/net v0.24.0 // indirect - golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect + golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect ) + +replace golang.zx2c4.com/wireguard => ./wireguard-go diff --git a/wireguard/libwg/go.sum b/wireguard-go-rs/libwg/go.sum similarity index 56% rename from wireguard/libwg/go.sum rename to wireguard-go-rs/libwg/go.sum index c90f293b177f..b41c5842d1e4 100644 --- a/wireguard/libwg/go.sum +++ b/wireguard-go-rs/libwg/go.sum @@ -4,7 +4,5 @@ golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 h1:Ug9qvr1myri/zFN6xL17LSCBGFDnphBBhzmILHsM5TY= -golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI= -golang.zx2c4.com/wireguard v0.0.0-20230223181233-21636207a675 h1:/J/RVnr7ng4fWPRH3xa4WtBJ1Jp+Auu4YNLmGiPv5QU= -golang.zx2c4.com/wireguard v0.0.0-20230223181233-21636207a675/go.mod h1:whfbyDBt09xhCYQWtO2+3UVjlaq6/9hDZrjg2ZE6SyA= +golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg= +golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI= diff --git a/wireguard-go-rs/libwg/goruntime-boottime-over-monotonic.diff b/wireguard-go-rs/libwg/goruntime-boottime-over-monotonic.diff new file mode 100644 index 000000000000..5d78242b139e --- /dev/null +++ b/wireguard-go-rs/libwg/goruntime-boottime-over-monotonic.diff @@ -0,0 +1,171 @@ +From 61f3ae8298d1c503cbc31539e0f3a73446c7db9d Mon Sep 17 00:00:00 2001 +From: "Jason A. Donenfeld" +Date: Tue, 21 Mar 2023 15:33:56 +0100 +Subject: [PATCH] [release-branch.go1.20] runtime: use CLOCK_BOOTTIME in + nanotime on Linux + +This makes timers account for having expired while a computer was +asleep, which is quite common on mobile devices. Note that BOOTTIME is +identical to MONOTONIC, except that it takes into account time spent +in suspend. In Linux 4.17, the kernel will actually make MONOTONIC act +like BOOTTIME anyway, so this switch will additionally unify the +timer behavior across kernels. + +BOOTTIME was introduced into Linux 2.6.39-rc1 with 70a08cca1227d in +2011. + +Fixes #24595 + +Change-Id: I7b2a6ca0c5bc5fce57ec0eeafe7b68270b429321 +--- + src/runtime/sys_linux_386.s | 4 ++-- + src/runtime/sys_linux_amd64.s | 2 +- + src/runtime/sys_linux_arm.s | 4 ++-- + src/runtime/sys_linux_arm64.s | 4 ++-- + src/runtime/sys_linux_mips64x.s | 4 ++-- + src/runtime/sys_linux_mipsx.s | 2 +- + src/runtime/sys_linux_ppc64x.s | 2 +- + src/runtime/sys_linux_s390x.s | 2 +- + 8 files changed, 12 insertions(+), 12 deletions(-) + +diff --git a/src/runtime/sys_linux_386.s b/src/runtime/sys_linux_386.s +index 12a294153d..17e3524b40 100644 +--- a/src/runtime/sys_linux_386.s ++++ b/src/runtime/sys_linux_386.s +@@ -352,13 +352,13 @@ noswitch: + + LEAL 8(SP), BX // &ts (struct timespec) + MOVL BX, 4(SP) +- MOVL $1, 0(SP) // CLOCK_MONOTONIC ++ MOVL $7, 0(SP) // CLOCK_BOOTTIME + CALL AX + JMP finish + + fallback: + MOVL $SYS_clock_gettime, AX +- MOVL $1, BX // CLOCK_MONOTONIC ++ MOVL $7, BX // CLOCK_BOOTTIME + LEAL 8(SP), CX + INVOKE_SYSCALL + +diff --git a/src/runtime/sys_linux_amd64.s b/src/runtime/sys_linux_amd64.s +index c7a89ba536..01f0a6a26e 100644 +--- a/src/runtime/sys_linux_amd64.s ++++ b/src/runtime/sys_linux_amd64.s +@@ -255,7 +255,7 @@ noswitch: + SUBQ $16, SP // Space for results + ANDQ $~15, SP // Align for C code + +- MOVL $1, DI // CLOCK_MONOTONIC ++ MOVL $7, DI // CLOCK_BOOTTIME + LEAQ 0(SP), SI + MOVQ runtime·vdsoClockgettimeSym(SB), AX + CMPQ AX, $0 +diff --git a/src/runtime/sys_linux_arm.s b/src/runtime/sys_linux_arm.s +index 7b8c4f0e04..9798a1334e 100644 +--- a/src/runtime/sys_linux_arm.s ++++ b/src/runtime/sys_linux_arm.s +@@ -11,7 +11,7 @@ + #include "textflag.h" + + #define CLOCK_REALTIME 0 +-#define CLOCK_MONOTONIC 1 ++#define CLOCK_BOOTTIME 7 + + // for EABI, as we don't support OABI + #define SYS_BASE 0x0 +@@ -374,7 +374,7 @@ finish: + + // func nanotime1() int64 + TEXT runtime·nanotime1(SB),NOSPLIT,$12-8 +- MOVW $CLOCK_MONOTONIC, R0 ++ MOVW $CLOCK_BOOTTIME, R0 + MOVW $spec-12(SP), R1 // timespec + + MOVW runtime·vdsoClockgettimeSym(SB), R4 +diff --git a/src/runtime/sys_linux_arm64.s b/src/runtime/sys_linux_arm64.s +index 38ff6ac330..6b819c5441 100644 +--- a/src/runtime/sys_linux_arm64.s ++++ b/src/runtime/sys_linux_arm64.s +@@ -14,7 +14,7 @@ + #define AT_FDCWD -100 + + #define CLOCK_REALTIME 0 +-#define CLOCK_MONOTONIC 1 ++#define CLOCK_BOOTTIME 7 + + #define SYS_exit 93 + #define SYS_read 63 +@@ -338,7 +338,7 @@ noswitch: + BIC $15, R1 + MOVD R1, RSP + +- MOVW $CLOCK_MONOTONIC, R0 ++ MOVW $CLOCK_BOOTTIME, R0 + MOVD runtime·vdsoClockgettimeSym(SB), R2 + CBZ R2, fallback + +diff --git a/src/runtime/sys_linux_mips64x.s b/src/runtime/sys_linux_mips64x.s +index 47f2da524d..a8b387f193 100644 +--- a/src/runtime/sys_linux_mips64x.s ++++ b/src/runtime/sys_linux_mips64x.s +@@ -326,7 +326,7 @@ noswitch: + AND $~15, R1 // Align for C code + MOVV R1, R29 + +- MOVW $1, R4 // CLOCK_MONOTONIC ++ MOVW $7, R4 // CLOCK_BOOTTIME + MOVV $0(R29), R5 + + MOVV runtime·vdsoClockgettimeSym(SB), R25 +@@ -336,7 +336,7 @@ noswitch: + // see walltime for detail + BEQ R2, R0, finish + MOVV R0, runtime·vdsoClockgettimeSym(SB) +- MOVW $1, R4 // CLOCK_MONOTONIC ++ MOVW $7, R4 // CLOCK_BOOTTIME + MOVV $0(R29), R5 + JMP fallback + +diff --git a/src/runtime/sys_linux_mipsx.s b/src/runtime/sys_linux_mipsx.s +index 5e6b6c1504..7f5fd2a80e 100644 +--- a/src/runtime/sys_linux_mipsx.s ++++ b/src/runtime/sys_linux_mipsx.s +@@ -243,7 +243,7 @@ TEXT runtime·walltime(SB),NOSPLIT,$8-12 + RET + + TEXT runtime·nanotime1(SB),NOSPLIT,$8-8 +- MOVW $1, R4 // CLOCK_MONOTONIC ++ MOVW $7, R4 // CLOCK_BOOTTIME + MOVW $4(R29), R5 + MOVW $SYS_clock_gettime, R2 + SYSCALL +diff --git a/src/runtime/sys_linux_ppc64x.s b/src/runtime/sys_linux_ppc64x.s +index d0427a4807..05ee9fede9 100644 +--- a/src/runtime/sys_linux_ppc64x.s ++++ b/src/runtime/sys_linux_ppc64x.s +@@ -298,7 +298,7 @@ fallback: + JMP return + + TEXT runtime·nanotime1(SB),NOSPLIT,$16-8 +- MOVD $1, R3 // CLOCK_MONOTONIC ++ MOVD $7, R3 // CLOCK_BOOTTIME + + MOVD R1, R15 // R15 is unchanged by C code + MOVD g_m(g), R21 // R21 = m +diff --git a/src/runtime/sys_linux_s390x.s b/src/runtime/sys_linux_s390x.s +index 1448670b91..7d2ee3231c 100644 +--- a/src/runtime/sys_linux_s390x.s ++++ b/src/runtime/sys_linux_s390x.s +@@ -296,7 +296,7 @@ fallback: + RET + + TEXT runtime·nanotime1(SB),NOSPLIT,$32-8 +- MOVW $1, R2 // CLOCK_MONOTONIC ++ MOVW $7, R2 // CLOCK_BOOTTIME + + MOVD R15, R7 // Backup stack pointer + +-- +2.17.1 + diff --git a/wireguard/libwg/libwg.go b/wireguard-go-rs/libwg/libwg.go similarity index 66% rename from wireguard/libwg/libwg.go rename to wireguard-go-rs/libwg/libwg.go index e26ea7b7daac..aaa03ef838dd 100644 --- a/wireguard/libwg/libwg.go +++ b/wireguard-go-rs/libwg/libwg.go @@ -6,7 +6,9 @@ package main +// #include // #include +// #include import "C" import ( @@ -17,11 +19,31 @@ import ( "unsafe" "github.com/mullvad/mullvadvpn-app/wireguard/libwg/tunnelcontainer" + "golang.zx2c4.com/wireguard/device" ) +// FFI integer result codes +// NOTE: Must be kept in sync with the Error enum in wireguard-go-rs const ( - ERROR_GENERAL_FAILURE = -1 - ERROR_INTERMITTENT_FAILURE = -2 + OK = C.int32_t(-iota) + + // Something went wrong. + ERROR_GENERAL_FAILURE + + // Something went wrong, but trying again might help. + ERROR_INTERMITTENT_FAILURE + + // A bad argument was provided to libwg. + ERROR_INVALID_ARGUMENT + + // The provided tunnel handle did not refer to an existing tunnel. + ERROR_UNKNOWN_TUNNEL + + // The provided public key did not refer to an existing peer. + ERROR_UNKNOWN_PEER + + // Something went wrong when enabling DAITA. + ERROR_ENABLE_DAITA ) var tunnels tunnelcontainer.Container @@ -30,6 +52,11 @@ func init() { tunnels = tunnelcontainer.New() } +type EventContext struct { + tunnelHandle int32 + peer device.NoisePublicKey +} + //export wgTurnOff func wgTurnOff(tunnelHandle int32) { { @@ -61,14 +88,14 @@ func wgGetConfig(tunnelHandle int32) *C.char { } //export wgSetConfig -func wgSetConfig(tunnelHandle int32, cSettings *C.char) int32 { +func wgSetConfig(tunnelHandle int32, cSettings *C.char) C.int32_t { tunnel, err := tunnels.Get(tunnelHandle) if err != nil { - return ERROR_GENERAL_FAILURE + return ERROR_UNKNOWN_TUNNEL } if cSettings == nil { tunnel.Logger.Errorf("cSettings is null\n") - return ERROR_GENERAL_FAILURE + return ERROR_INVALID_ARGUMENT } settings := C.GoString(cSettings) diff --git a/wireguard-go-rs/libwg/libwg.h b/wireguard-go-rs/libwg/libwg.h new file mode 100644 index 000000000000..23c87092ccbf --- /dev/null +++ b/wireguard-go-rs/libwg/libwg.h @@ -0,0 +1,8 @@ +#include +#include + +/// Activate DAITA for the specified tunnel. +int32_t wgActivateDaita(int32_t tunnelHandle, uint8_t* noisePublic, char* machines, uint32_t eventsCapacity, uint32_t actionsCapacity); +char* wgGetConfig(int32_t tunnelHandle); +int32_t wgSetConfig(int32_t tunnelHandle, char* cSettings); +void wgFreePtr(void*); diff --git a/wireguard/libwg/libwg_android.go b/wireguard-go-rs/libwg/libwg_android.go similarity index 83% rename from wireguard/libwg/libwg_android.go rename to wireguard-go-rs/libwg/libwg_android.go index df54d4cc8b64..86410721f5a4 100644 --- a/wireguard/libwg/libwg_android.go +++ b/wireguard-go-rs/libwg/libwg_android.go @@ -6,8 +6,10 @@ package main +// #include +import "C" + import ( - "C" "bufio" "strings" "unsafe" @@ -25,15 +27,15 @@ import ( // Redefined here because otherwise the compiler doesn't realize it's a type alias for a type that's safe to export. // Taken from the contained logging package. type LogSink = unsafe.Pointer -type LogContext = unsafe.Pointer +type LogContext = C.uint64_t //export wgTurnOn -func wgTurnOn(cSettings *C.char, fd int, logSink LogSink, logContext LogContext) int32 { - logger := logging.NewLogger(logSink, logContext) +func wgTurnOn(cSettings *C.char, fd int, logSink LogSink, logContext LogContext) C.int32_t { + logger := logging.NewLogger(logSink, logging.LogContext(logContext)) if cSettings == nil { logger.Errorf("cSettings is null\n") - return ERROR_GENERAL_FAILURE + return ERROR_INVALID_ARGUMENT } settings := C.GoString(cSettings) @@ -71,33 +73,33 @@ func wgTurnOn(cSettings *C.char, fd int, logSink LogSink, logContext LogContext) return ERROR_GENERAL_FAILURE } - return handle + return C.int32_t(handle) } //export wgGetSocketV4 -func wgGetSocketV4(tunnelHandle int32) int32 { +func wgGetSocketV4(tunnelHandle int32) C.int32_t { tunnel, err := tunnels.Get(tunnelHandle) if err != nil { - return ERROR_GENERAL_FAILURE + return ERROR_UNKNOWN_TUNNEL } peek := tunnel.Device.Bind().(conn.PeekLookAtSocketFd) fd, err := peek.PeekLookAtSocketFd4() if err != nil { return ERROR_GENERAL_FAILURE } - return int32(fd) + return C.int32_t(fd) } //export wgGetSocketV6 -func wgGetSocketV6(tunnelHandle int32) int32 { +func wgGetSocketV6(tunnelHandle int32) C.int32_t { tunnel, err := tunnels.Get(tunnelHandle) if err != nil { - return ERROR_GENERAL_FAILURE + return ERROR_UNKNOWN_TUNNEL } peek := tunnel.Device.Bind().(conn.PeekLookAtSocketFd) fd, err := peek.PeekLookAtSocketFd6() if err != nil { return ERROR_GENERAL_FAILURE } - return int32(fd) + return C.int32_t(fd) } diff --git a/wireguard-go-rs/libwg/libwg_daita.go b/wireguard-go-rs/libwg/libwg_daita.go new file mode 100644 index 000000000000..e33de84e4911 --- /dev/null +++ b/wireguard-go-rs/libwg/libwg_daita.go @@ -0,0 +1,41 @@ +//go:build daita +// +build daita + +package main + +// #include +// #include +// #include +import "C" + +import ( + "unsafe" + + "golang.zx2c4.com/wireguard/device" +) + +const maxPaddingBytes = 0.0 +const maxBlockingBytes = 0.0 + +//export wgActivateDaita +func wgActivateDaita(tunnelHandle C.int32_t, peerPubkey *C.uint8_t, machines *C.char, eventsCapacity C.uint32_t, actionsCapacity C.uint32_t) C.int32_t { + + tunnel, err := tunnels.Get(int32(tunnelHandle)) + if err != nil { + return ERROR_UNKNOWN_TUNNEL + } + + var publicKey device.NoisePublicKey + copy(publicKey[:], C.GoBytes(unsafe.Pointer(peerPubkey), device.NoisePublicKeySize)) + peer := tunnel.Device.LookupPeer(publicKey) + + if peer == nil { + return ERROR_UNKNOWN_PEER + } + + if !peer.EnableDaita(C.GoString((*C.char)(machines)), uint(eventsCapacity), uint(actionsCapacity), maxPaddingBytes, maxBlockingBytes) { + return ERROR_ENABLE_DAITA + } + + return OK +} diff --git a/wireguard/libwg/libwg_default.go b/wireguard-go-rs/libwg/libwg_default.go similarity index 86% rename from wireguard/libwg/libwg_default.go rename to wireguard-go-rs/libwg/libwg_default.go index 7282c0ca8a42..6741de715fab 100644 --- a/wireguard/libwg/libwg_default.go +++ b/wireguard-go-rs/libwg/libwg_default.go @@ -1,3 +1,4 @@ +//go:build (darwin || linux) && !android // +build darwin linux // +build !android @@ -10,6 +11,7 @@ package main // #include +// #include import "C" import ( "bufio" @@ -28,16 +30,15 @@ import ( // Redefined here because otherwise the compiler doesn't realize it's a type alias for a type that's safe to export. // Taken from the contained logging package. type LogSink = unsafe.Pointer -type LogContext = unsafe.Pointer +type LogContext = C.uint64_t //export wgTurnOn -func wgTurnOn(mtu int, cSettings *C.char, fd int, logSink LogSink, logContext LogContext) int32 { - - logger := logging.NewLogger(logSink, logContext) +func wgTurnOn(mtu int, cSettings *C.char, fd int, logSink LogSink, logContext LogContext) C.int32_t { + logger := logging.NewLogger(logSink, logging.LogContext(logContext)) if cSettings == nil { logger.Errorf("cSettings is null\n") - return ERROR_GENERAL_FAILURE + return ERROR_INVALID_ARGUMENT } settings := C.GoString(cSettings) @@ -74,5 +75,5 @@ func wgTurnOn(mtu int, cSettings *C.char, fd int, logSink LogSink, logContext Lo return ERROR_GENERAL_FAILURE } - return handle + return C.int32_t(handle) } diff --git a/wireguard/libwg/logging/logging.go b/wireguard-go-rs/libwg/logging/logging.go similarity index 93% rename from wireguard/libwg/logging/logging.go rename to wireguard-go-rs/libwg/logging/logging.go index a917e964935b..a6782ec39ac4 100644 --- a/wireguard/libwg/logging/logging.go +++ b/wireguard-go-rs/libwg/logging/logging.go @@ -7,12 +7,13 @@ package logging // #include +// #include // #include // #ifndef WIN32 // #define __stdcall // #endif -// typedef void (__stdcall *LogSink)(unsigned int, const char *, void *); -// static void callLogSink(void *logSink, int level, const char *message, void *context) +// typedef void (__stdcall *LogSink)(unsigned int, const char *, uint64_t); +// static void callLogSink(void *logSink, int level, const char *message, uint64_t context) // { // ((LogSink)logSink)((unsigned int)level, message, context); // } @@ -27,7 +28,7 @@ import ( // Define type aliases. type LogSink = unsafe.Pointer -type LogContext = unsafe.Pointer +type LogContext = C.uint64_t type Logger struct { sink LogSink diff --git a/wireguard/libwg/tunnelcontainer/tunnelcontainer.go b/wireguard-go-rs/libwg/tunnelcontainer/tunnelcontainer.go similarity index 100% rename from wireguard/libwg/tunnelcontainer/tunnelcontainer.go rename to wireguard-go-rs/libwg/tunnelcontainer/tunnelcontainer.go diff --git a/wireguard-go-rs/libwg/wireguard-go b/wireguard-go-rs/libwg/wireguard-go new file mode 160000 index 000000000000..f4bc3aefeb6c --- /dev/null +++ b/wireguard-go-rs/libwg/wireguard-go @@ -0,0 +1 @@ +Subproject commit f4bc3aefeb6c6ae50567a4bfc177593337eb9cba diff --git a/wireguard-go-rs/src/lib.rs b/wireguard-go-rs/src/lib.rs new file mode 100644 index 000000000000..f87b2586d0f0 --- /dev/null +++ b/wireguard-go-rs/src/lib.rs @@ -0,0 +1,288 @@ +//! This crate provides Rust bindings to wireguard-go with DAITA support. +//! +//! The bindings on the Go side are provided by `libwg`, which is a Go package that wraps +//! `wireguard-go` and provides a C FFI that we can use from Rust. On the Rust side, the FFI is +//! in the private `ffi` module below. It needs to be kept in sync with any changes to libwg. +//! +//! The [`Tunnel`] type provides a safe Rust wrapper around the C FFI. + +#![cfg(unix)] + +use core::slice; +use std::{ + ffi::{c_char, CStr}, + mem::ManuallyDrop, +}; +use util::OnDrop; +use zeroize::Zeroize; + +mod util; + +pub type Fd = std::os::unix::io::RawFd; + +pub type WgLogLevel = u32; + +pub type LoggingContext = u64; +pub type LoggingCallback = + unsafe extern "system" fn(level: WgLogLevel, msg: *const c_char, context: LoggingContext); + +/// A wireguard-go tunnel +pub struct Tunnel { + /// wireguard-go handle to the tunnel. + handle: i32, +} + +// NOTE: Must be kept in sync with libwg.go +// NOTE: must be kept in sync with `result_from_code` +// INVARIANT: Will always be represented as a negative i32 +#[repr(i32)] +#[non_exhaustive] +#[derive(Clone, Copy, Debug, thiserror::Error)] +pub enum Error { + #[error("Something went wrong.")] + GeneralFailure = -1, + + #[error("Something went wrong, but trying again might help.")] + IntermittentFailure = -2, + + #[error("An argument you provided was invalid.")] + InvalidArgument = -3, + + #[error("The tunnel handle did not refer to an existing tunnel.")] + UnknownTunnel = -4, + + #[error("The provided public key did not refer to an existing peer.")] + UnknownPeer = -5, + + #[error("Something went wrong when enabling DAITA.")] + EnableDaita = -6, + + #[error("`libwg` provided an unknown error code. This is a bug.")] + Other = i32::MIN, +} + +impl Tunnel { + /// Creates a new wireguard tunnel, uses the specific interface name, and file descriptors + /// for the tunnel device and logging. For targets other than android, this also takes an MTU + /// value. + /// + /// The `logging_callback` let's you provide a Rust function that receives any logging output + /// from wireguard-go. `logging_context` is a value that will be passed to each invocation of + /// `logging_callback`. + pub fn turn_on( + #[cfg(not(target_os = "android"))] mtu: isize, + settings: &CStr, + device: Fd, + logging_callback: Option, + logging_context: LoggingContext, + ) -> Result { + // SAFETY: pointer is valid for the the lifetime of this function + let code = unsafe { + ffi::wgTurnOn( + #[cfg(not(target_os = "android"))] + mtu, + settings.as_ptr(), + device, + logging_callback, + logging_context, + ) + }; + + result_from_code(code)?; + Ok(Tunnel { handle: code }) + } + + /// Stop the wireguard tunnel. This also happens automatically if the [`Tunnel`] is dropped. + pub fn turn_off(self) -> Result<(), Error> { + // we manually turn off the tunnel here, so wrap it in ManuallyDrop to prevent the Drop + // impl from doing the same. + let code = unsafe { ffi::wgTurnOff(self.handle) }; + let _ = ManuallyDrop::new(self); + result_from_code(code) + } + + /// Get the config of the WireGuard interface and make it available in the provided function. + /// + /// This takes a function to make sure the cstr get's zeroed and freed afterwards. + /// Returns `None` if the call to wgGetConfig returned nil. + /// + /// **NOTE:** You should take extra care to avoid copying any secrets from the config without + /// zeroizing them afterwards. + // NOTE: this could return a guard type with a custom Drop impl instead, but me lazy. + pub fn get_config(&self, f: impl FnOnce(&CStr) -> T) -> Option { + let ptr = unsafe { ffi::wgGetConfig(self.handle) }; + + if ptr.is_null() { + return None; + } + + // SAFETY: we checked for null, and wgGetConfig promises that this is a valid cstr + let config = unsafe { CStr::from_ptr(ptr) }; + let config_len = config.to_bytes().len(); + + // execute cleanup code on Drop to make sure that it happens even if `f` panics + let on_drop = OnDrop::new(|| { + { + // SAFETY: + // we checked for null, and wgGetConfig promises that this is a valid cstr. + // config_len comes from the CStr above, so it should be good. + let config_bytes = unsafe { slice::from_raw_parts_mut(ptr, config_len) }; + config_bytes.zeroize(); + } + + // SAFETY: the pointer was created by wgGetConfig, and we are no longer using it. + unsafe { ffi::wgFreePtr(ptr.cast()) }; + }); + + let t = f(config); + let _ = config; + drop(on_drop); + + Some(t) + } + + /// Set the config of the WireGuard interface. + pub fn set_config(&self, config: &CStr) -> Result<(), Error> { + // SAFETY: pointer is valid for the lifetime of this function. + let code = unsafe { ffi::wgSetConfig(self.handle, config.as_ptr()) }; + result_from_code(code) + } + + /// Activate DAITA for the specified peer. + /// + /// `machines` is a string containing LF-separated maybenot machines. + #[cfg(any(target_os = "windows", target_os = "linux"))] + pub fn activate_daita( + &self, + peer_public_key: &[u8; 32], + machines: &CStr, + events_capacity: u32, + actions_capacity: u32, + ) -> Result<(), Error> { + // SAFETY: pointers are valid for the lifetime of this function. + let code = unsafe { + ffi::wgActivateDaita( + self.handle, + peer_public_key.as_ptr(), + machines.as_ptr(), + events_capacity, + actions_capacity, + ) + }; + + result_from_code(code) + } + + /// Get the file descriptor of the tunnel IPv4 socket. + #[cfg(target_os = "android")] + pub fn get_socket_v4(&self) -> Fd { + unsafe { ffi::wgGetSocketV4(self.handle) } + } + + /// Get the file descriptor of the tunnel IPv6 socket. + #[cfg(target_os = "android")] + pub fn get_socket_v6(&self) -> Fd { + unsafe { ffi::wgGetSocketV6(self.handle) } + } +} + +impl Drop for Tunnel { + fn drop(&mut self) { + let code = unsafe { ffi::wgTurnOff(self.handle) }; + if let Err(e) = result_from_code(code) { + log::error!("Failed to stop wireguard-go tunnel,oerror_code={code} ({e:?})") + } + } +} + +fn result_from_code(code: i32) -> Result<(), Error> { + // NOTE: must be kept in sync with enum definition + Err(match code { + 0.. => return Ok(()), + -1 => Error::GeneralFailure, + -2 => Error::IntermittentFailure, + -3 => Error::UnknownTunnel, + -4 => Error::UnknownPeer, + -5 => Error::EnableDaita, + _ => Error::Other, + }) +} + +impl Error { + pub const fn as_raw(self) -> i32 { + self as i32 + } +} + +mod ffi { + use super::{Fd, LoggingCallback, LoggingContext}; + use core::ffi::{c_char, c_void}; + + extern "C" { + /// Creates a new wireguard tunnel, uses the specific interface name, and file descriptors + /// for the tunnel device and logging. For targets other than android, this also takes an + /// MTU value. + /// + /// Positive return values are tunnel handles for this specific wireguard tunnel instance. + /// Negative return values signify errors. + pub fn wgTurnOn( + #[cfg(not(target_os = "android"))] mtu: isize, + settings: *const c_char, + fd: Fd, + logging_callback: Option, + logging_context: LoggingContext, + ) -> i32; + + /// Pass a handle that was created by wgTurnOn to stop a wireguard tunnel. + /// + /// Negative return values signify errors. + pub fn wgTurnOff(handle: i32) -> i32; + + /// Get the config of the WireGuard interface. Returns null in case of error. + /// + /// # Safety: + /// - The function returns an owned pointer to a null-terminated UTF-8 string. + /// - The pointer may only be freed using [wgFreePtr]. + pub fn wgGetConfig(handle: i32) -> *mut c_char; + + /// Set the config of the WireGuard interface. + /// + /// Negative return values signify errors. + /// + /// # Safety: + /// - `settings` must point to a null-terminated UTF-8 string. + /// - The pointer will not be read from after `wgActivateDaita` has returned. + pub fn wgSetConfig(handle: i32, settings: *const c_char) -> i32; + + /// Activate DAITA for the specified peer. + /// + /// `tunnel_handle` must come from [wgTurnOn]. `machines` is a string containing + /// LF-separated maybenot machines. + /// + /// Negative return values signify errors. + /// + /// # Safety: + /// - `peer_public_key` must point to a 32 byte array. + /// - `machines` must point to a null-terminated UTF-8 string. + /// - Neither pointer will be read from after `wgActivateDaita` has returned. + #[cfg(any(target_os = "windows", target_os = "linux"))] + pub fn wgActivateDaita( + tunnel_handle: i32, + peer_public_key: *const u8, + machines: *const c_char, + events_capacity: u32, + actions_capacity: u32, + ) -> i32; + + /// Free a pointer allocated by the go runtime - useful to free return value of wgGetConfig + pub fn wgFreePtr(ptr: *mut c_void); + + /// Get the file descriptor of the tunnel IPv4 socket. + #[cfg(target_os = "android")] + pub fn wgGetSocketV4(handle: i32) -> Fd; + + /// Get the file descriptor of the tunnel IPv6 socket. + #[cfg(target_os = "android")] + pub fn wgGetSocketV6(handle: i32) -> Fd; + } +} diff --git a/wireguard-go-rs/src/util.rs b/wireguard-go-rs/src/util.rs new file mode 100644 index 000000000000..4c2df2f9bbc5 --- /dev/null +++ b/wireguard-go-rs/src/util.rs @@ -0,0 +1,15 @@ +pub struct OnDrop(Option); + +impl OnDrop { + pub fn new(f: F) -> Self { + OnDrop(Some(f)) + } +} + +impl Drop for OnDrop { + fn drop(&mut self) { + if let Some(f) = self.0.take() { + f() + } + } +} From 540f68fe14164f604be897b0324717aac402b116 Mon Sep 17 00:00:00 2001 From: Sebastian Holmin Date: Mon, 6 May 2024 13:42:39 +0200 Subject: [PATCH 03/16] Add resource `maybenot_machines` to `distribution.js` for linux --- gui/tasks/distribution.js | 1 + 1 file changed, 1 insertion(+) diff --git a/gui/tasks/distribution.js b/gui/tasks/distribution.js index 0bf9cc369fa1..bd9f025bbd67 100644 --- a/gui/tasks/distribution.js +++ b/gui/tasks/distribution.js @@ -179,6 +179,7 @@ const config = { { from: distAssets(path.join('linux', 'apparmor_mullvad')), to: '.' }, { from: distAssets(path.join('binaries', '${env.TARGET_TRIPLE}', 'openvpn')), to: '.' }, { from: distAssets(path.join('binaries', '${env.TARGET_TRIPLE}', 'apisocks5')), to: '.' }, + { from: distAssets('maybenot_machines'), to: '.' }, ], }, From 7475c53d540768a49ccda0517a1437bf50a06e2d Mon Sep 17 00:00:00 2001 From: Sebastian Holmin Date: Wed, 17 Apr 2024 13:06:02 +0200 Subject: [PATCH 04/16] Enable DAITA for linux in CLI and types --- mullvad-cli/src/cmds/relay.rs | 2 +- mullvad-cli/src/cmds/tunnel.rs | 14 +++++++------- mullvad-cli/src/format.rs | 4 ++-- mullvad-daemon/src/lib.rs | 8 ++++---- mullvad-daemon/src/management_interface.rs | 4 ++-- mullvad-management-interface/src/client.rs | 4 ++-- .../src/types/conversions/custom_tunnel.rs | 2 +- .../src/types/conversions/net.rs | 6 +++--- .../src/types/conversions/settings.rs | 6 +++--- .../src/types/conversions/wireguard.rs | 4 ++-- .../src/relay_selector/detailer.rs | 6 +++--- mullvad-types/src/wireguard.rs | 8 ++++---- talpid-types/src/net/mod.rs | 6 +++--- talpid-types/src/net/wireguard.rs | 4 ++-- test/test-manager/src/tests/dns.rs | 2 ++ test/test-manager/src/tests/helpers.rs | 2 ++ test/test-manager/src/tests/tunnel_state.rs | 2 ++ 17 files changed, 45 insertions(+), 39 deletions(-) diff --git a/mullvad-cli/src/cmds/relay.rs b/mullvad-cli/src/cmds/relay.rs index f022402a8316..585b2736a493 100644 --- a/mullvad-cli/src/cmds/relay.rs +++ b/mullvad-cli/src/cmds/relay.rs @@ -542,7 +542,7 @@ impl Relay { allowed_ips: all_of_the_internet(), endpoint: SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), port), psk: None, - #[cfg(target_os = "windows")] + #[cfg(any(target_os = "windows", target_os = "linux"))] constant_packet_size: false, }, exit_peer: None, diff --git a/mullvad-cli/src/cmds/tunnel.rs b/mullvad-cli/src/cmds/tunnel.rs index 77338ee336b8..316bada856fe 100644 --- a/mullvad-cli/src/cmds/tunnel.rs +++ b/mullvad-cli/src/cmds/tunnel.rs @@ -1,7 +1,7 @@ use anyhow::Result; use clap::Subcommand; use mullvad_management_interface::MullvadProxyClient; -#[cfg(target_os = "windows")] +#[cfg(any(target_os = "windows", target_os = "linux"))] use mullvad_types::wireguard::DaitaSettings; use mullvad_types::{ constraints::Constraint, @@ -41,7 +41,7 @@ pub enum TunnelOptions { #[arg(long)] quantum_resistant: Option, /// Configure whether to enable DAITA - #[cfg(target_os = "windows")] + #[cfg(any(target_os = "windows", target_os = "linux"))] #[arg(long)] daita: Option, /// The key rotation interval. Number of hours, or 'any' @@ -101,7 +101,7 @@ impl Tunnel { tunnel_options.wireguard.quantum_resistant, ); - #[cfg(target_os = "windows")] + #[cfg(any(target_os = "windows", target_os = "linux"))] print_option!("DAITA", tunnel_options.wireguard.daita.enabled); let key = rpc.get_wireguard_key().await?; @@ -138,7 +138,7 @@ impl Tunnel { TunnelOptions::Wireguard { mtu, quantum_resistant, - #[cfg(target_os = "windows")] + #[cfg(any(target_os = "windows", target_os = "linux"))] daita, rotation_interval, rotate_key, @@ -146,7 +146,7 @@ impl Tunnel { Self::handle_wireguard( mtu, quantum_resistant, - #[cfg(target_os = "windows")] + #[cfg(any(target_os = "windows", target_os = "linux"))] daita, rotation_interval, rotate_key, @@ -178,7 +178,7 @@ impl Tunnel { async fn handle_wireguard( mtu: Option>, quantum_resistant: Option, - #[cfg(target_os = "windows")] daita: Option, + #[cfg(any(target_os = "windows", target_os = "linux"))] daita: Option, rotation_interval: Option>, rotate_key: Option, ) -> Result<()> { @@ -194,7 +194,7 @@ impl Tunnel { println!("Quantum resistant setting has been updated"); } - #[cfg(target_os = "windows")] + #[cfg(any(target_os = "windows", target_os = "linux"))] if let Some(daita) = daita { rpc.set_daita_settings(DaitaSettings { enabled: *daita }) .await?; diff --git a/mullvad-cli/src/format.rs b/mullvad-cli/src/format.rs index 6b092e8939c5..abd85b178a0c 100644 --- a/mullvad-cli/src/format.rs +++ b/mullvad-cli/src/format.rs @@ -174,7 +174,7 @@ fn format_relay_connection( "\nQuantum resistant tunnel: no" }; - #[cfg(target_os = "windows")] + #[cfg(any(target_os = "windows", target_os = "linux"))] let daita = if !verbose { "" } else if endpoint.daita { @@ -182,7 +182,7 @@ fn format_relay_connection( } else { "\nDAITA: no" }; - #[cfg(not(target_os = "windows"))] + #[cfg(not(any(target_os = "windows", target_os = "linux")))] let daita = ""; let mut bridge_type = String::new(); diff --git a/mullvad-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs index 269bd1dbb166..6e5ae40042cb 100644 --- a/mullvad-daemon/src/lib.rs +++ b/mullvad-daemon/src/lib.rs @@ -44,7 +44,7 @@ use mullvad_relay_selector::{ use mullvad_types::account::{PlayPurchase, PlayPurchasePaymentToken}; #[cfg(any(windows, target_os = "android", target_os = "macos"))] use mullvad_types::settings::SplitApp; -#[cfg(target_os = "windows")] +#[cfg(any(target_os = "windows", target_os = "linux"))] use mullvad_types::wireguard::DaitaSettings; use mullvad_types::{ access_method::{AccessMethod, AccessMethodSetting}, @@ -255,7 +255,7 @@ pub enum DaemonCommand { /// Set whether to enable PQ PSK exchange in the tunnel SetQuantumResistantTunnel(ResponseTx<(), settings::Error>, QuantumResistantState), /// Set DAITA settings for the tunnel - #[cfg(target_os = "windows")] + #[cfg(any(target_os = "windows", target_os = "linux"))] SetDaitaSettings(ResponseTx<(), settings::Error>, DaitaSettings), /// Set DNS options or servers to use SetDnsOptions(ResponseTx<(), settings::Error>, DnsOptions), @@ -1172,7 +1172,7 @@ where self.on_set_quantum_resistant_tunnel(tx, quantum_resistant_state) .await } - #[cfg(target_os = "windows")] + #[cfg(any(target_os = "windows", target_os = "linux"))] SetDaitaSettings(tx, daita_settings) => { self.on_set_daita_settings(tx, daita_settings).await } @@ -2240,7 +2240,7 @@ where } } - #[cfg(target_os = "windows")] + #[cfg(any(target_os = "windows", target_os = "linux"))] async fn on_set_daita_settings( &mut self, tx: ResponseTx<(), settings::Error>, diff --git a/mullvad-daemon/src/management_interface.rs b/mullvad-daemon/src/management_interface.rs index b5f29a386e96..a151feae8d4e 100644 --- a/mullvad-daemon/src/management_interface.rs +++ b/mullvad-daemon/src/management_interface.rs @@ -330,7 +330,7 @@ impl ManagementService for ManagementServiceImpl { Ok(Response::new(())) } - #[cfg(target_os = "windows")] + #[cfg(any(target_os = "windows", target_os = "linux"))] async fn set_daita_settings( &self, request: Request, @@ -344,7 +344,7 @@ impl ManagementService for ManagementServiceImpl { Ok(Response::new(())) } - #[cfg(not(target_os = "windows"))] + #[cfg(not(any(target_os = "windows", target_os = "linux")))] async fn set_daita_settings(&self, _: Request) -> ServiceResult<()> { Ok(Response::new(())) } diff --git a/mullvad-management-interface/src/client.rs b/mullvad-management-interface/src/client.rs index 3d217ace32a5..41609a1d4fe2 100644 --- a/mullvad-management-interface/src/client.rs +++ b/mullvad-management-interface/src/client.rs @@ -3,7 +3,7 @@ use crate::types; #[cfg(not(target_os = "android"))] use futures::{Stream, StreamExt}; -#[cfg(target_os = "windows")] +#[cfg(any(target_os = "windows", target_os = "linux"))] use mullvad_types::wireguard::DaitaSettings; use mullvad_types::{ access_method::AccessMethodSetting, @@ -372,7 +372,7 @@ impl MullvadProxyClient { Ok(()) } - #[cfg(target_os = "windows")] + #[cfg(any(target_os = "windows", target_os = "linux"))] pub async fn set_daita_settings(&mut self, settings: DaitaSettings) -> Result<()> { let settings = types::DaitaSettings::from(settings); self.0 diff --git a/mullvad-management-interface/src/types/conversions/custom_tunnel.rs b/mullvad-management-interface/src/types/conversions/custom_tunnel.rs index 2445ec3292d1..0e5f362eb6e5 100644 --- a/mullvad-management-interface/src/types/conversions/custom_tunnel.rs +++ b/mullvad-management-interface/src/types/conversions/custom_tunnel.rs @@ -91,7 +91,7 @@ impl TryFrom for mullvad_types::ConnectionConfig { allowed_ips, endpoint, psk: None, - #[cfg(target_os = "windows")] + #[cfg(any(target_os = "windows", target_os = "linux"))] constant_packet_size: false, }, exit_peer: None, diff --git a/mullvad-management-interface/src/types/conversions/net.rs b/mullvad-management-interface/src/types/conversions/net.rs index 3557a6a636f9..77c0d1a4497f 100644 --- a/mullvad-management-interface/src/types/conversions/net.rs +++ b/mullvad-management-interface/src/types/conversions/net.rs @@ -40,9 +40,9 @@ impl From for proto::TunnelEndpoint { tunnel_metadata: endpoint .tunnel_interface .map(|tunnel_interface| proto::TunnelMetadata { tunnel_interface }), - #[cfg(target_os = "windows")] + #[cfg(any(target_os = "windows", target_os = "linux"))] daita: endpoint.daita, - #[cfg(not(target_os = "windows"))] + #[cfg(not(any(target_os = "windows", target_os = "linux")))] daita: false, } } @@ -127,7 +127,7 @@ impl TryFrom for talpid_types::net::TunnelEndpoint { tunnel_interface: endpoint .tunnel_metadata .map(|tunnel_metadata| tunnel_metadata.tunnel_interface), - #[cfg(target_os = "windows")] + #[cfg(any(target_os = "windows", target_os = "linux"))] daita: endpoint.daita, }) } diff --git a/mullvad-management-interface/src/types/conversions/settings.rs b/mullvad-management-interface/src/types/conversions/settings.rs index e1d1905575e0..393fae6b9d73 100644 --- a/mullvad-management-interface/src/types/conversions/settings.rs +++ b/mullvad-management-interface/src/types/conversions/settings.rs @@ -100,9 +100,9 @@ impl From<&mullvad_types::settings::TunnelOptions> for proto::TunnelOptions { .expect("Failed to convert std::time::Duration to prost_types::Duration for tunnel_options.wireguard.rotation_interval") }), quantum_resistant: Some(proto::QuantumResistantState::from(options.wireguard.quantum_resistant)), - #[cfg(target_os = "windows")] + #[cfg(any(target_os = "windows", target_os = "linux"))] daita: Some(proto::DaitaSettings::from(options.wireguard.daita.clone())), - #[cfg(not(target_os = "windows"))] + #[cfg(not(any(target_os = "windows", target_os = "linux")))] daita: None, }), generic: Some(proto::tunnel_options::GenericOptions { @@ -283,7 +283,7 @@ impl TryFrom for mullvad_types::settings::TunnelOptions { .ok_or(FromProtobufTypeError::InvalidArgument( "missing quantum resistant state", ))??, - #[cfg(target_os = "windows")] + #[cfg(any(target_os = "windows", target_os = "linux"))] daita: wireguard_options .daita .map(mullvad_types::wireguard::DaitaSettings::from) diff --git a/mullvad-management-interface/src/types/conversions/wireguard.rs b/mullvad-management-interface/src/types/conversions/wireguard.rs index 1ef378ef5e37..875b96389797 100644 --- a/mullvad-management-interface/src/types/conversions/wireguard.rs +++ b/mullvad-management-interface/src/types/conversions/wireguard.rs @@ -73,7 +73,7 @@ impl TryFrom for mullvad_types::wireguard::Quantum } } -#[cfg(target_os = "windows")] +#[cfg(any(target_os = "windows", target_os = "linux"))] impl From for proto::DaitaSettings { fn from(settings: mullvad_types::wireguard::DaitaSettings) -> Self { proto::DaitaSettings { @@ -82,7 +82,7 @@ impl From for proto::DaitaSettings { } } -#[cfg(target_os = "windows")] +#[cfg(any(target_os = "windows", target_os = "linux"))] impl From for mullvad_types::wireguard::DaitaSettings { fn from(settings: proto::DaitaSettings) -> Self { mullvad_types::wireguard::DaitaSettings { diff --git a/mullvad-relay-selector/src/relay_selector/detailer.rs b/mullvad-relay-selector/src/relay_selector/detailer.rs index 3dc1cc903e7d..a9921c3556c0 100644 --- a/mullvad-relay-selector/src/relay_selector/detailer.rs +++ b/mullvad-relay-selector/src/relay_selector/detailer.rs @@ -85,7 +85,7 @@ fn wireguard_singlehop_endpoint( // This will be filled in later, not the relay selector's problem psk: None, // This will be filled in later - #[cfg(target_os = "windows")] + #[cfg(any(target_os = "windows", target_os = "linux"))] constant_packet_size: false, }; Ok(MullvadWireguardEndpoint { @@ -126,7 +126,7 @@ fn wireguard_multihop_endpoint( // This will be filled in later, not the relay selector's problem psk: None, // This will be filled in later - #[cfg(target_os = "windows")] + #[cfg(any(target_os = "windows", target_os = "linux"))] constant_packet_size: false, }; @@ -144,7 +144,7 @@ fn wireguard_multihop_endpoint( // This will be filled in later psk: None, // This will be filled in later - #[cfg(target_os = "windows")] + #[cfg(any(target_os = "windows", target_os = "linux"))] constant_packet_size: false, }; diff --git a/mullvad-types/src/wireguard.rs b/mullvad-types/src/wireguard.rs index 5bb3981a7c33..2ee30df90247 100644 --- a/mullvad-types/src/wireguard.rs +++ b/mullvad-types/src/wireguard.rs @@ -51,7 +51,7 @@ impl FromStr for QuantumResistantState { #[error("Not a valid state")] pub struct QuantumResistantStateParseError; -#[cfg(target_os = "windows")] +#[cfg(any(target_os = "windows", target_os = "linux"))] #[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)] pub struct DaitaSettings { pub enabled: bool, @@ -195,7 +195,7 @@ pub struct TunnelOptions { /// Obtain a PSK using the relay config client. pub quantum_resistant: QuantumResistantState, /// Configure DAITA - #[cfg(target_os = "windows")] + #[cfg(any(target_os = "windows", target_os = "linux"))] pub daita: DaitaSettings, /// Interval used for automatic key rotation pub rotation_interval: Option, @@ -207,7 +207,7 @@ impl Default for TunnelOptions { TunnelOptions { mtu: None, quantum_resistant: QuantumResistantState::Auto, - #[cfg(target_os = "windows")] + #[cfg(any(target_os = "windows", target_os = "linux"))] daita: DaitaSettings::default(), rotation_interval: None, } @@ -223,7 +223,7 @@ impl TunnelOptions { QuantumResistantState::On => true, QuantumResistantState::Off => false, }, - #[cfg(target_os = "windows")] + #[cfg(any(target_os = "windows", target_os = "linux"))] daita: self.daita.enabled, } } diff --git a/talpid-types/src/net/mod.rs b/talpid-types/src/net/mod.rs index 576899c91183..09e5a399282e 100644 --- a/talpid-types/src/net/mod.rs +++ b/talpid-types/src/net/mod.rs @@ -38,7 +38,7 @@ impl TunnelParameters { obfuscation: None, entry_endpoint: None, tunnel_interface: None, - #[cfg(target_os = "windows")] + #[cfg(any(target_os = "windows", target_os = "linux"))] daita: false, }, TunnelParameters::Wireguard(params) => TunnelEndpoint { @@ -55,7 +55,7 @@ impl TunnelParameters { .get_exit_endpoint() .map(|_| params.connection.get_endpoint()), tunnel_interface: None, - #[cfg(target_os = "windows")] + #[cfg(any(target_os = "windows", target_os = "linux"))] daita: params.options.daita, }, } @@ -179,7 +179,7 @@ pub struct TunnelEndpoint { pub obfuscation: Option, pub entry_endpoint: Option, pub tunnel_interface: Option, - #[cfg(target_os = "windows")] + #[cfg(any(target_os = "windows", target_os = "linux"))] pub daita: bool, } diff --git a/talpid-types/src/net/wireguard.rs b/talpid-types/src/net/wireguard.rs index 4f1298323fec..8fc189e64fdf 100644 --- a/talpid-types/src/net/wireguard.rs +++ b/talpid-types/src/net/wireguard.rs @@ -62,7 +62,7 @@ pub struct PeerConfig { #[serde(skip)] pub psk: Option, /// Enable constant packet sizes for `entry_peer`` - #[cfg(target_os = "windows")] + #[cfg(any(target_os = "windows", target_os = "linux"))] #[serde(skip)] pub constant_packet_size: bool, } @@ -82,7 +82,7 @@ pub struct TunnelOptions { /// Perform PQ-safe PSK exchange when connecting pub quantum_resistant: bool, /// Enable DAITA during tunnel config - #[cfg(target_os = "windows")] + #[cfg(any(target_os = "windows", target_os = "linux"))] pub daita: bool, } diff --git a/test/test-manager/src/tests/dns.rs b/test/test-manager/src/tests/dns.rs index a6f564139f95..35a2fcc91775 100644 --- a/test/test-manager/src/tests/dns.rs +++ b/test/test-manager/src/tests/dns.rs @@ -650,6 +650,8 @@ async fn connect_local_wg_relay(mullvad_client: &mut MullvadProxyClient) -> Resu allowed_ips: vec!["0.0.0.0/0".parse().unwrap()], endpoint: peer_addr, psk: None, + #[cfg(target_os = "linux")] + constant_packet_size: false, }, ipv4_gateway: CUSTOM_TUN_GATEWAY, exit_peer: None, diff --git a/test/test-manager/src/tests/helpers.rs b/test/test-manager/src/tests/helpers.rs index 96c6dc65bbe3..a6a2cae242d4 100644 --- a/test/test-manager/src/tests/helpers.rs +++ b/test/test-manager/src/tests/helpers.rs @@ -517,6 +517,8 @@ pub fn unreachable_wireguard_tunnel() -> talpid_types::net::wireguard::Connectio ], endpoint: "1.3.3.7:1234".parse().unwrap(), psk: None, + #[cfg(target_os = "linux")] + constant_packet_size: false, }, exit_peer: None, ipv4_gateway: Ipv4Addr::new(10, 64, 10, 1), diff --git a/test/test-manager/src/tests/tunnel_state.rs b/test/test-manager/src/tests/tunnel_state.rs index f75bc79498d3..b04a71ba5ab2 100644 --- a/test/test-manager/src/tests/tunnel_state.rs +++ b/test/test-manager/src/tests/tunnel_state.rs @@ -369,6 +369,8 @@ pub async fn test_connected_state( obfuscation: None, entry_endpoint: None, tunnel_interface: _, + #[cfg(target_os = "linux")] + daita: _, }, .. } => { From b41704c8a47d1b1568a0918f782f543277ae6acc Mon Sep 17 00:00:00 2001 From: Markus Pettersson Date: Mon, 10 Jun 2024 13:46:29 +0200 Subject: [PATCH 05/16] Use Wireguard-go when DAITA is enabled --- talpid-wireguard/Cargo.toml | 1 + talpid-wireguard/src/lib.rs | 65 +++++++++++++++++------- talpid-wireguard/src/wireguard_go/mod.rs | 5 +- 3 files changed, 53 insertions(+), 18 deletions(-) diff --git a/talpid-wireguard/Cargo.toml b/talpid-wireguard/Cargo.toml index 09b7baa57f12..e7db72cac6fe 100644 --- a/talpid-wireguard/Cargo.toml +++ b/talpid-wireguard/Cargo.toml @@ -45,6 +45,7 @@ tokio-stream = { version = "0.1", features = ["io-util"] } [target.'cfg(unix)'.dependencies] nix = "0.23" +[target.'cfg(target_os = "linux")'.dependencies] rtnetlink = "0.11" netlink-packet-core = "0.4.2" netlink-packet-route = "0.13" diff --git a/talpid-wireguard/src/lib.rs b/talpid-wireguard/src/lib.rs index e8334f43d1df..d18f5c9fb2c3 100644 --- a/talpid-wireguard/src/lib.rs +++ b/talpid-wireguard/src/lib.rs @@ -761,6 +761,14 @@ impl WireguardMonitor { #[cfg(target_os = "linux")] if !*FORCE_USERSPACE_WIREGUARD { + // If DAITA is enabled, wireguard-go has to be used. + if config.daita { + let tunnel = + Self::open_wireguard_go_tunnel(config, log_path, resource_dir, tun_provider) + .map(Box::new)?; + return Ok(tunnel); + } + if will_nm_manage_dns() { match wireguard_kernel::NetworkManagerTunnel::new(runtime, config) { Ok(tunnel) => { @@ -803,28 +811,51 @@ impl WireguardMonitor { #[cfg(wireguard_go)] { - let routes = - Self::get_tunnel_destinations(config).flat_map(Self::replace_default_prefixes); - - #[cfg(target_os = "android")] - let config = Self::patch_allowed_ips(config, gateway_only); - #[cfg(target_os = "linux")] log::debug!("Using userspace WireGuard implementation"); - Ok(Box::new( - WgGoTunnel::start_tunnel( - #[allow(clippy::needless_borrow)] - &config, - log_path, - tun_provider, - routes, - resource_dir, - ) - .map_err(Error::TunnelError)?, - )) + + let tunnel = Self::open_wireguard_go_tunnel( + config, + log_path, + #[cfg(any(target_os = "windows", target_os = "linux"))] + resource_dir, + tun_provider, + #[cfg(target_os = "android")] + gateway_only, + ) + .map(Box::new)?; + Ok(tunnel) } } + /// Configure and start a Wireguard-go tunnel. + #[cfg(wireguard_go)] + fn open_wireguard_go_tunnel( + config: &Config, + log_path: Option<&Path>, + #[cfg(any(target_os = "windows", target_os = "linux"))] resource_dir: &Path, + tun_provider: Arc>, + #[cfg(target_os = "android")] gateway_only: bool, + ) -> Result { + let routes = Self::get_tunnel_destinations(config).flat_map(Self::replace_default_prefixes); + + #[cfg(target_os = "android")] + let config = Self::patch_allowed_ips(config, gateway_only); + + let tunnel = WgGoTunnel::start_tunnel( + #[allow(clippy::needless_borrow)] + &config, + log_path, + tun_provider, + routes, + #[cfg(any(target_os = "windows", target_os = "linux"))] + resource_dir, + ) + .map_err(Error::TunnelError)?; + + Ok(tunnel) + } + /// Blocks the current thread until tunnel disconnects pub fn wait(mut self) -> Result<()> { let wait_result = match self.close_msg_receiver.recv() { diff --git a/talpid-wireguard/src/wireguard_go/mod.rs b/talpid-wireguard/src/wireguard_go/mod.rs index 32181beaea43..de5e3e0f8389 100644 --- a/talpid-wireguard/src/wireguard_go/mod.rs +++ b/talpid-wireguard/src/wireguard_go/mod.rs @@ -54,6 +54,7 @@ pub struct WgGoTunnel { tun_provider: Arc>, #[cfg(any(target_os = "windows", target_os = "linux"))] resource_dir: PathBuf, + #[cfg(any(target_os = "windows", target_os = "linux"))] config: Config, } @@ -63,7 +64,7 @@ impl WgGoTunnel { log_path: Option<&Path>, tun_provider: Arc>, routes: impl Iterator, - resource_dir: &Path, + #[cfg(any(target_os = "windows", target_os = "linux"))] resource_dir: &Path, ) -> Result { #[cfg(target_os = "android")] let tun_provider_clone = tun_provider.clone(); @@ -100,6 +101,7 @@ impl WgGoTunnel { _logging_context: logging_context, #[cfg(target_os = "android")] tun_provider: tun_provider_clone, + #[cfg(any(target_os = "windows", target_os = "linux"))] resource_dir: resource_dir.to_owned(), #[cfg(any(target_os = "windows", target_os = "linux"))] config: config.clone(), @@ -240,6 +242,7 @@ impl Tunnel for WgGoTunnel { }) } + #[cfg(any(target_os = "windows", target_os = "linux"))] fn start_daita(&mut self) -> Result<()> { static MAYBENOT_MACHINES: OnceCell = OnceCell::new(); let machines = MAYBENOT_MACHINES.get_or_try_init(|| { From f58c16ad92931d0b825d9898b702bd928767e30d Mon Sep 17 00:00:00 2001 From: Markus Pettersson Date: Thu, 2 May 2024 11:54:14 +0200 Subject: [PATCH 06/16] Enable constant packet size for wireguard-go Activate the setting when DAITA is enabled and write constant packet size to WG config string. Co-authored-by: Sebastian Holmin --- talpid-wireguard/src/config.rs | 4 ++++ talpid-wireguard/src/lib.rs | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/talpid-wireguard/src/config.rs b/talpid-wireguard/src/config.rs index e9059c6cca94..43f6f256c195 100644 --- a/talpid-wireguard/src/config.rs +++ b/talpid-wireguard/src/config.rs @@ -141,6 +141,10 @@ impl Config { for addr in &peer.allowed_ips { wg_conf.add("allowed_ip", addr.to_string().as_str()); } + #[cfg(daita)] + if peer.constant_packet_size { + wg_conf.add("constant_packet_size", "true"); + } } let bytes = wg_conf.into_config(); diff --git a/talpid-wireguard/src/lib.rs b/talpid-wireguard/src/lib.rs index d18f5c9fb2c3..596c96285114 100644 --- a/talpid-wireguard/src/lib.rs +++ b/talpid-wireguard/src/lib.rs @@ -558,7 +558,7 @@ impl WireguardMonitor { } config.exit_peer_mut().psk = exit_psk; - #[cfg(target_os = "windows")] + #[cfg(any(target_os = "windows", target_os = "linux"))] if config.daita { log::trace!("Enabling constant packet size for entry peer"); config.entry_peer.constant_packet_size = true; From 3b1246c9a94690687f0bb60ee865797643c53ea1 Mon Sep 17 00:00:00 2001 From: Markus Pettersson Date: Fri, 17 May 2024 16:36:56 +0200 Subject: [PATCH 07/16] Add `daita` as a Cargo cfg variable Gate DAITA compilation on `"cargo::rustc-cfg=daita"` emitted in build files per platform. --- mullvad-cli/build.rs | 7 ++++ mullvad-cli/src/cmds/relay.rs | 2 +- mullvad-cli/src/cmds/tunnel.rs | 14 +++---- mullvad-cli/src/format.rs | 4 +- mullvad-daemon/build.rs | 11 +++++- mullvad-daemon/src/lib.rs | 12 +++--- mullvad-daemon/src/management_interface.rs | 4 +- mullvad-management-interface/build.rs | 8 ++++ mullvad-management-interface/src/client.rs | 4 +- .../src/types/conversions/custom_tunnel.rs | 2 +- .../src/types/conversions/net.rs | 6 +-- .../src/types/conversions/settings.rs | 6 +-- .../src/types/conversions/wireguard.rs | 4 +- mullvad-relay-selector/build.rs | 9 +++++ .../src/relay_selector/detailer.rs | 6 +-- mullvad-types/build.rs | 9 +++++ mullvad-types/src/wireguard.rs | 8 ++-- talpid-core/build.rs | 8 ++-- talpid-types/build.rs | 9 +++++ talpid-types/src/net/mod.rs | 6 +-- talpid-types/src/net/wireguard.rs | 4 +- talpid-wireguard/build.rs | 14 +++++-- talpid-wireguard/src/config.rs | 4 +- talpid-wireguard/src/connectivity_check.rs | 3 +- talpid-wireguard/src/lib.rs | 19 +++++----- talpid-wireguard/src/wireguard_go/mod.rs | 20 +++++----- .../src/wireguard_kernel/netlink_tunnel.rs | 4 +- .../src/wireguard_kernel/nm_tunnel.rs | 4 +- talpid-wireguard/src/wireguard_nt/mod.rs | 37 +++++++++++++++++-- test/test-manager/build.rs | 2 +- wireguard-go-rs/build.rs | 4 +- wireguard-go-rs/src/lib.rs | 4 +- 32 files changed, 174 insertions(+), 84 deletions(-) create mode 100644 mullvad-relay-selector/build.rs create mode 100644 mullvad-types/build.rs create mode 100644 talpid-types/build.rs diff --git a/mullvad-cli/build.rs b/mullvad-cli/build.rs index de110d3a76b0..13b06384a5d7 100644 --- a/mullvad-cli/build.rs +++ b/mullvad-cli/build.rs @@ -15,4 +15,11 @@ fn main() { )); res.compile().expect("Unable to generate windows resources"); } + let target_os = std::env::var("CARGO_CFG_TARGET_OS").expect("CARGO_CFG_TARGET_OS not set"); + + // Enable Daita by default on Linux and Windows. + println!("cargo::rustc-check-cfg=cfg(daita)"); + if let "linux" | "windows" = target_os.as_str() { + println!(r#"cargo::rustc-cfg=daita"#); + } } diff --git a/mullvad-cli/src/cmds/relay.rs b/mullvad-cli/src/cmds/relay.rs index 585b2736a493..146d37c2b23f 100644 --- a/mullvad-cli/src/cmds/relay.rs +++ b/mullvad-cli/src/cmds/relay.rs @@ -542,7 +542,7 @@ impl Relay { allowed_ips: all_of_the_internet(), endpoint: SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), port), psk: None, - #[cfg(any(target_os = "windows", target_os = "linux"))] + #[cfg(daita)] constant_packet_size: false, }, exit_peer: None, diff --git a/mullvad-cli/src/cmds/tunnel.rs b/mullvad-cli/src/cmds/tunnel.rs index 316bada856fe..0937cc82299e 100644 --- a/mullvad-cli/src/cmds/tunnel.rs +++ b/mullvad-cli/src/cmds/tunnel.rs @@ -1,7 +1,7 @@ use anyhow::Result; use clap::Subcommand; use mullvad_management_interface::MullvadProxyClient; -#[cfg(any(target_os = "windows", target_os = "linux"))] +#[cfg(daita)] use mullvad_types::wireguard::DaitaSettings; use mullvad_types::{ constraints::Constraint, @@ -41,7 +41,7 @@ pub enum TunnelOptions { #[arg(long)] quantum_resistant: Option, /// Configure whether to enable DAITA - #[cfg(any(target_os = "windows", target_os = "linux"))] + #[cfg(daita)] #[arg(long)] daita: Option, /// The key rotation interval. Number of hours, or 'any' @@ -101,7 +101,7 @@ impl Tunnel { tunnel_options.wireguard.quantum_resistant, ); - #[cfg(any(target_os = "windows", target_os = "linux"))] + #[cfg(daita)] print_option!("DAITA", tunnel_options.wireguard.daita.enabled); let key = rpc.get_wireguard_key().await?; @@ -138,7 +138,7 @@ impl Tunnel { TunnelOptions::Wireguard { mtu, quantum_resistant, - #[cfg(any(target_os = "windows", target_os = "linux"))] + #[cfg(daita)] daita, rotation_interval, rotate_key, @@ -146,7 +146,7 @@ impl Tunnel { Self::handle_wireguard( mtu, quantum_resistant, - #[cfg(any(target_os = "windows", target_os = "linux"))] + #[cfg(daita)] daita, rotation_interval, rotate_key, @@ -178,7 +178,7 @@ impl Tunnel { async fn handle_wireguard( mtu: Option>, quantum_resistant: Option, - #[cfg(any(target_os = "windows", target_os = "linux"))] daita: Option, + #[cfg(daita)] daita: Option, rotation_interval: Option>, rotate_key: Option, ) -> Result<()> { @@ -194,7 +194,7 @@ impl Tunnel { println!("Quantum resistant setting has been updated"); } - #[cfg(any(target_os = "windows", target_os = "linux"))] + #[cfg(daita)] if let Some(daita) = daita { rpc.set_daita_settings(DaitaSettings { enabled: *daita }) .await?; diff --git a/mullvad-cli/src/format.rs b/mullvad-cli/src/format.rs index abd85b178a0c..86606839190c 100644 --- a/mullvad-cli/src/format.rs +++ b/mullvad-cli/src/format.rs @@ -174,7 +174,7 @@ fn format_relay_connection( "\nQuantum resistant tunnel: no" }; - #[cfg(any(target_os = "windows", target_os = "linux"))] + #[cfg(daita)] let daita = if !verbose { "" } else if endpoint.daita { @@ -182,7 +182,7 @@ fn format_relay_connection( } else { "\nDAITA: no" }; - #[cfg(not(any(target_os = "windows", target_os = "linux")))] + #[cfg(not(daita))] let daita = ""; let mut bridge_type = String::new(); diff --git a/mullvad-daemon/build.rs b/mullvad-daemon/build.rs index ef84845f7462..6f37ea2e193c 100644 --- a/mullvad-daemon/build.rs +++ b/mullvad-daemon/build.rs @@ -18,17 +18,24 @@ fn main() { windows_sys::Win32::System::SystemServices::LANG_ENGLISH as u16, windows_sys::Win32::System::SystemServices::SUBLANG_ENGLISH_US as u16, )); - println!("cargo:rerun-if-env-changed=MULLVAD_ADD_MANIFEST"); + println!("cargo::rerun-if-env-changed=MULLVAD_ADD_MANIFEST"); if env::var("MULLVAD_ADD_MANIFEST") .map(|s| s != "0") .unwrap_or(false) { res.set_manifest_file("mullvad-daemon.manifest"); } else { - println!("cargo:warning=Skipping mullvad-daemon manifest"); + println!("cargo::warning=Skipping mullvad-daemon manifest"); } res.compile().expect("Unable to generate windows resources"); } + let target_os = std::env::var("CARGO_CFG_TARGET_OS").expect("CARGO_CFG_TARGET_OS not set"); + + // Enable Daita by default on Linux and Windows. + println!("cargo::rustc-check-cfg=cfg(daita)"); + if let "linux" | "windows" = target_os.as_str() { + println!(r#"cargo::rustc-cfg=daita"#); + } } fn commit_date() -> String { diff --git a/mullvad-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs index 6e5ae40042cb..ea75118f7b46 100644 --- a/mullvad-daemon/src/lib.rs +++ b/mullvad-daemon/src/lib.rs @@ -44,7 +44,7 @@ use mullvad_relay_selector::{ use mullvad_types::account::{PlayPurchase, PlayPurchasePaymentToken}; #[cfg(any(windows, target_os = "android", target_os = "macos"))] use mullvad_types::settings::SplitApp; -#[cfg(any(target_os = "windows", target_os = "linux"))] +#[cfg(daita)] use mullvad_types::wireguard::DaitaSettings; use mullvad_types::{ access_method::{AccessMethod, AccessMethodSetting}, @@ -255,7 +255,7 @@ pub enum DaemonCommand { /// Set whether to enable PQ PSK exchange in the tunnel SetQuantumResistantTunnel(ResponseTx<(), settings::Error>, QuantumResistantState), /// Set DAITA settings for the tunnel - #[cfg(any(target_os = "windows", target_os = "linux"))] + #[cfg(daita)] SetDaitaSettings(ResponseTx<(), settings::Error>, DaitaSettings), /// Set DNS options or servers to use SetDnsOptions(ResponseTx<(), settings::Error>, DnsOptions), @@ -1172,7 +1172,7 @@ where self.on_set_quantum_resistant_tunnel(tx, quantum_resistant_state) .await } - #[cfg(any(target_os = "windows", target_os = "linux"))] + #[cfg(daita)] SetDaitaSettings(tx, daita_settings) => { self.on_set_daita_settings(tx, daita_settings).await } @@ -2240,7 +2240,7 @@ where } } - #[cfg(any(target_os = "windows", target_os = "linux"))] + #[cfg(daita)] async fn on_set_daita_settings( &mut self, tx: ResponseTx<(), settings::Error>, @@ -2804,9 +2804,9 @@ impl DaemonShutdownHandle { fn new_selector_config(settings: &Settings) -> SelectorConfig { let additional_constraints = AdditionalRelayConstraints { wireguard: AdditionalWireguardConstraints { - #[cfg(target_os = "windows")] + #[cfg(daita)] daita: settings.tunnel_options.wireguard.daita.enabled, - #[cfg(not(target_os = "windows"))] + #[cfg(not(daita))] daita: false, }, }; diff --git a/mullvad-daemon/src/management_interface.rs b/mullvad-daemon/src/management_interface.rs index a151feae8d4e..3ea6db073f1d 100644 --- a/mullvad-daemon/src/management_interface.rs +++ b/mullvad-daemon/src/management_interface.rs @@ -330,7 +330,7 @@ impl ManagementService for ManagementServiceImpl { Ok(Response::new(())) } - #[cfg(any(target_os = "windows", target_os = "linux"))] + #[cfg(daita)] async fn set_daita_settings( &self, request: Request, @@ -344,7 +344,7 @@ impl ManagementService for ManagementServiceImpl { Ok(Response::new(())) } - #[cfg(not(any(target_os = "windows", target_os = "linux")))] + #[cfg(not(daita))] async fn set_daita_settings(&self, _: Request) -> ServiceResult<()> { Ok(Response::new(())) } diff --git a/mullvad-management-interface/build.rs b/mullvad-management-interface/build.rs index 22dfcb9b4e0a..c6e53f8a401d 100644 --- a/mullvad-management-interface/build.rs +++ b/mullvad-management-interface/build.rs @@ -1,3 +1,11 @@ fn main() { tonic_build::compile_protos("proto/management_interface.proto").unwrap(); + + let target_os = std::env::var("CARGO_CFG_TARGET_OS").expect("CARGO_CFG_TARGET_OS not set"); + + // Enable Daita by default on Linux and Windows. + println!("cargo::rustc-check-cfg=cfg(daita)"); + if let "linux" | "windows" = target_os.as_str() { + println!(r#"cargo::rustc-cfg=daita"#); + } } diff --git a/mullvad-management-interface/src/client.rs b/mullvad-management-interface/src/client.rs index 41609a1d4fe2..bb74b5d7483d 100644 --- a/mullvad-management-interface/src/client.rs +++ b/mullvad-management-interface/src/client.rs @@ -3,7 +3,7 @@ use crate::types; #[cfg(not(target_os = "android"))] use futures::{Stream, StreamExt}; -#[cfg(any(target_os = "windows", target_os = "linux"))] +#[cfg(daita)] use mullvad_types::wireguard::DaitaSettings; use mullvad_types::{ access_method::AccessMethodSetting, @@ -372,7 +372,7 @@ impl MullvadProxyClient { Ok(()) } - #[cfg(any(target_os = "windows", target_os = "linux"))] + #[cfg(daita)] pub async fn set_daita_settings(&mut self, settings: DaitaSettings) -> Result<()> { let settings = types::DaitaSettings::from(settings); self.0 diff --git a/mullvad-management-interface/src/types/conversions/custom_tunnel.rs b/mullvad-management-interface/src/types/conversions/custom_tunnel.rs index 0e5f362eb6e5..03cfa658605b 100644 --- a/mullvad-management-interface/src/types/conversions/custom_tunnel.rs +++ b/mullvad-management-interface/src/types/conversions/custom_tunnel.rs @@ -91,7 +91,7 @@ impl TryFrom for mullvad_types::ConnectionConfig { allowed_ips, endpoint, psk: None, - #[cfg(any(target_os = "windows", target_os = "linux"))] + #[cfg(daita)] constant_packet_size: false, }, exit_peer: None, diff --git a/mullvad-management-interface/src/types/conversions/net.rs b/mullvad-management-interface/src/types/conversions/net.rs index 77c0d1a4497f..e9c8a9483838 100644 --- a/mullvad-management-interface/src/types/conversions/net.rs +++ b/mullvad-management-interface/src/types/conversions/net.rs @@ -40,9 +40,9 @@ impl From for proto::TunnelEndpoint { tunnel_metadata: endpoint .tunnel_interface .map(|tunnel_interface| proto::TunnelMetadata { tunnel_interface }), - #[cfg(any(target_os = "windows", target_os = "linux"))] + #[cfg(daita)] daita: endpoint.daita, - #[cfg(not(any(target_os = "windows", target_os = "linux")))] + #[cfg(not(daita))] daita: false, } } @@ -127,7 +127,7 @@ impl TryFrom for talpid_types::net::TunnelEndpoint { tunnel_interface: endpoint .tunnel_metadata .map(|tunnel_metadata| tunnel_metadata.tunnel_interface), - #[cfg(any(target_os = "windows", target_os = "linux"))] + #[cfg(daita)] daita: endpoint.daita, }) } diff --git a/mullvad-management-interface/src/types/conversions/settings.rs b/mullvad-management-interface/src/types/conversions/settings.rs index 393fae6b9d73..d3be7f75a2dc 100644 --- a/mullvad-management-interface/src/types/conversions/settings.rs +++ b/mullvad-management-interface/src/types/conversions/settings.rs @@ -100,9 +100,9 @@ impl From<&mullvad_types::settings::TunnelOptions> for proto::TunnelOptions { .expect("Failed to convert std::time::Duration to prost_types::Duration for tunnel_options.wireguard.rotation_interval") }), quantum_resistant: Some(proto::QuantumResistantState::from(options.wireguard.quantum_resistant)), - #[cfg(any(target_os = "windows", target_os = "linux"))] + #[cfg(daita)] daita: Some(proto::DaitaSettings::from(options.wireguard.daita.clone())), - #[cfg(not(any(target_os = "windows", target_os = "linux")))] + #[cfg(not(daita))] daita: None, }), generic: Some(proto::tunnel_options::GenericOptions { @@ -283,7 +283,7 @@ impl TryFrom for mullvad_types::settings::TunnelOptions { .ok_or(FromProtobufTypeError::InvalidArgument( "missing quantum resistant state", ))??, - #[cfg(any(target_os = "windows", target_os = "linux"))] + #[cfg(daita)] daita: wireguard_options .daita .map(mullvad_types::wireguard::DaitaSettings::from) diff --git a/mullvad-management-interface/src/types/conversions/wireguard.rs b/mullvad-management-interface/src/types/conversions/wireguard.rs index 875b96389797..b4e3bcaef722 100644 --- a/mullvad-management-interface/src/types/conversions/wireguard.rs +++ b/mullvad-management-interface/src/types/conversions/wireguard.rs @@ -73,7 +73,7 @@ impl TryFrom for mullvad_types::wireguard::Quantum } } -#[cfg(any(target_os = "windows", target_os = "linux"))] +#[cfg(daita)] impl From for proto::DaitaSettings { fn from(settings: mullvad_types::wireguard::DaitaSettings) -> Self { proto::DaitaSettings { @@ -82,7 +82,7 @@ impl From for proto::DaitaSettings { } } -#[cfg(any(target_os = "windows", target_os = "linux"))] +#[cfg(daita)] impl From for mullvad_types::wireguard::DaitaSettings { fn from(settings: proto::DaitaSettings) -> Self { mullvad_types::wireguard::DaitaSettings { diff --git a/mullvad-relay-selector/build.rs b/mullvad-relay-selector/build.rs new file mode 100644 index 000000000000..bab04aea3cfc --- /dev/null +++ b/mullvad-relay-selector/build.rs @@ -0,0 +1,9 @@ +fn main() { + let target_os = std::env::var("CARGO_CFG_TARGET_OS").expect("CARGO_CFG_TARGET_OS not set"); + + // Enable Daita by default on Linux and Windows. + println!("cargo::rustc-check-cfg=cfg(daita)"); + if let "linux" | "windows" = target_os.as_str() { + println!(r#"cargo::rustc-cfg=daita"#); + } +} diff --git a/mullvad-relay-selector/src/relay_selector/detailer.rs b/mullvad-relay-selector/src/relay_selector/detailer.rs index a9921c3556c0..8b4d446fa3e5 100644 --- a/mullvad-relay-selector/src/relay_selector/detailer.rs +++ b/mullvad-relay-selector/src/relay_selector/detailer.rs @@ -85,7 +85,7 @@ fn wireguard_singlehop_endpoint( // This will be filled in later, not the relay selector's problem psk: None, // This will be filled in later - #[cfg(any(target_os = "windows", target_os = "linux"))] + #[cfg(daita)] constant_packet_size: false, }; Ok(MullvadWireguardEndpoint { @@ -126,7 +126,7 @@ fn wireguard_multihop_endpoint( // This will be filled in later, not the relay selector's problem psk: None, // This will be filled in later - #[cfg(any(target_os = "windows", target_os = "linux"))] + #[cfg(daita)] constant_packet_size: false, }; @@ -144,7 +144,7 @@ fn wireguard_multihop_endpoint( // This will be filled in later psk: None, // This will be filled in later - #[cfg(any(target_os = "windows", target_os = "linux"))] + #[cfg(daita)] constant_packet_size: false, }; diff --git a/mullvad-types/build.rs b/mullvad-types/build.rs new file mode 100644 index 000000000000..bab04aea3cfc --- /dev/null +++ b/mullvad-types/build.rs @@ -0,0 +1,9 @@ +fn main() { + let target_os = std::env::var("CARGO_CFG_TARGET_OS").expect("CARGO_CFG_TARGET_OS not set"); + + // Enable Daita by default on Linux and Windows. + println!("cargo::rustc-check-cfg=cfg(daita)"); + if let "linux" | "windows" = target_os.as_str() { + println!(r#"cargo::rustc-cfg=daita"#); + } +} diff --git a/mullvad-types/src/wireguard.rs b/mullvad-types/src/wireguard.rs index 2ee30df90247..008b7a475c8d 100644 --- a/mullvad-types/src/wireguard.rs +++ b/mullvad-types/src/wireguard.rs @@ -51,7 +51,7 @@ impl FromStr for QuantumResistantState { #[error("Not a valid state")] pub struct QuantumResistantStateParseError; -#[cfg(any(target_os = "windows", target_os = "linux"))] +#[cfg(daita)] #[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)] pub struct DaitaSettings { pub enabled: bool, @@ -195,7 +195,7 @@ pub struct TunnelOptions { /// Obtain a PSK using the relay config client. pub quantum_resistant: QuantumResistantState, /// Configure DAITA - #[cfg(any(target_os = "windows", target_os = "linux"))] + #[cfg(daita)] pub daita: DaitaSettings, /// Interval used for automatic key rotation pub rotation_interval: Option, @@ -207,7 +207,7 @@ impl Default for TunnelOptions { TunnelOptions { mtu: None, quantum_resistant: QuantumResistantState::Auto, - #[cfg(any(target_os = "windows", target_os = "linux"))] + #[cfg(daita)] daita: DaitaSettings::default(), rotation_interval: None, } @@ -223,7 +223,7 @@ impl TunnelOptions { QuantumResistantState::On => true, QuantumResistantState::Off => false, }, - #[cfg(any(target_os = "windows", target_os = "linux"))] + #[cfg(daita)] daita: self.daita.enabled, } } diff --git a/talpid-core/build.rs b/talpid-core/build.rs index e39847945ce4..0f3d5bd10ca9 100644 --- a/talpid-core/build.rs +++ b/talpid-core/build.rs @@ -29,12 +29,12 @@ mod win { } pub fn declare_library(env_var: &str, default_dir: &str, lib_name: &str) { - println!("cargo:rerun-if-env-changed={}", env_var); + println!("cargo::rerun-if-env-changed={}", env_var); let lib_dir = env::var_os(env_var) .map(PathBuf::from) .unwrap_or_else(|| default_windows_build_artifact_dir(default_dir)); - println!("cargo:rustc-link-search={}", lib_dir.display()); - println!("cargo:rustc-link-lib=dylib={}", lib_name); + println!("cargo::rustc-link-search={}", lib_dir.display()); + println!("cargo::rustc-link-lib=dylib={}", lib_name); } pub fn manifest_dir() -> PathBuf { @@ -53,7 +53,7 @@ fn main() { const WINFW_DIR_VAR: &str = "WINFW_LIB_DIR"; declare_library(WINFW_DIR_VAR, WINFW_BUILD_DIR, "winfw"); let lib_dir = manifest_dir().join("../build/lib/x86_64-pc-windows-msvc"); - println!("cargo:rustc-link-search={}", &lib_dir.display()); + println!("cargo::rustc-link-search={}", &lib_dir.display()); } #[cfg(not(windows))] diff --git a/talpid-types/build.rs b/talpid-types/build.rs new file mode 100644 index 000000000000..bab04aea3cfc --- /dev/null +++ b/talpid-types/build.rs @@ -0,0 +1,9 @@ +fn main() { + let target_os = std::env::var("CARGO_CFG_TARGET_OS").expect("CARGO_CFG_TARGET_OS not set"); + + // Enable Daita by default on Linux and Windows. + println!("cargo::rustc-check-cfg=cfg(daita)"); + if let "linux" | "windows" = target_os.as_str() { + println!(r#"cargo::rustc-cfg=daita"#); + } +} diff --git a/talpid-types/src/net/mod.rs b/talpid-types/src/net/mod.rs index 09e5a399282e..d6113ab41f64 100644 --- a/talpid-types/src/net/mod.rs +++ b/talpid-types/src/net/mod.rs @@ -38,7 +38,7 @@ impl TunnelParameters { obfuscation: None, entry_endpoint: None, tunnel_interface: None, - #[cfg(any(target_os = "windows", target_os = "linux"))] + #[cfg(daita)] daita: false, }, TunnelParameters::Wireguard(params) => TunnelEndpoint { @@ -55,7 +55,7 @@ impl TunnelParameters { .get_exit_endpoint() .map(|_| params.connection.get_endpoint()), tunnel_interface: None, - #[cfg(any(target_os = "windows", target_os = "linux"))] + #[cfg(daita)] daita: params.options.daita, }, } @@ -179,7 +179,7 @@ pub struct TunnelEndpoint { pub obfuscation: Option, pub entry_endpoint: Option, pub tunnel_interface: Option, - #[cfg(any(target_os = "windows", target_os = "linux"))] + #[cfg(daita)] pub daita: bool, } diff --git a/talpid-types/src/net/wireguard.rs b/talpid-types/src/net/wireguard.rs index 8fc189e64fdf..df1e0999fc1b 100644 --- a/talpid-types/src/net/wireguard.rs +++ b/talpid-types/src/net/wireguard.rs @@ -62,7 +62,7 @@ pub struct PeerConfig { #[serde(skip)] pub psk: Option, /// Enable constant packet sizes for `entry_peer`` - #[cfg(any(target_os = "windows", target_os = "linux"))] + #[cfg(daita)] #[serde(skip)] pub constant_packet_size: bool, } @@ -82,7 +82,7 @@ pub struct TunnelOptions { /// Perform PQ-safe PSK exchange when connecting pub quantum_resistant: bool, /// Enable DAITA during tunnel config - #[cfg(any(target_os = "windows", target_os = "linux"))] + #[cfg(daita)] pub daita: bool, } diff --git a/talpid-wireguard/build.rs b/talpid-wireguard/build.rs index 3abec6abe2aa..a5984ef5fa9a 100644 --- a/talpid-wireguard/build.rs +++ b/talpid-wireguard/build.rs @@ -9,17 +9,23 @@ fn main() { } fn add_wireguard_go_cfg(target_os: &str) { - println!("cargo:rustc-check-cfg=cfg(wireguard_go)"); + println!("cargo::rustc-check-cfg=cfg(wireguard_go)"); if matches!(target_os, "linux" | "macos" | "android") { - println!("cargo:rustc-cfg=wireguard_go"); + println!("cargo::rustc-cfg=wireguard_go"); + } + + // Enable Daita by default on Linux and Windows. + println!("cargo::rustc-check-cfg=cfg(daita)"); + if matches!(target_os, "linux" | "windows") { + println!(r#"cargo::rustc-cfg=daita"#); } } fn declare_libs_dir(base: &str) { let target_triplet = env::var("TARGET").expect("TARGET is not set"); let lib_dir = manifest_dir().join(base).join(target_triplet); - println!("cargo:rerun-if-changed={}", lib_dir.display()); - println!("cargo:rustc-link-search={}", lib_dir.display()); + println!("cargo::rerun-if-changed={}", lib_dir.display()); + println!("cargo::rustc-link-search={}", lib_dir.display()); } fn manifest_dir() -> PathBuf { diff --git a/talpid-wireguard/src/config.rs b/talpid-wireguard/src/config.rs index 43f6f256c195..18c7ea5e14be 100644 --- a/talpid-wireguard/src/config.rs +++ b/talpid-wireguard/src/config.rs @@ -97,9 +97,9 @@ impl Config { enable_ipv6: generic_options.enable_ipv6, obfuscator_config: obfuscator_config.to_owned(), quantum_resistant: wg_options.quantum_resistant, - #[cfg(any(target_os = "windows", target_os = "linux"))] + #[cfg(daita)] daita: wg_options.daita, - #[cfg(not(any(target_os = "windows", target_os = "linux")))] + #[cfg(not(daita))] daita: false, }; diff --git a/talpid-wireguard/src/connectivity_check.rs b/talpid-wireguard/src/connectivity_check.rs index 41ad0b5bf34e..4514df0495ab 100644 --- a/talpid-wireguard/src/connectivity_check.rs +++ b/talpid-wireguard/src/connectivity_check.rs @@ -403,6 +403,7 @@ mod test { use crate::{ config::Config, stats::{self, Stats}, + Tunnel, }; use std::{ pin::Pin, @@ -615,7 +616,7 @@ mod test { Box::pin(async { Ok(()) }) } - #[cfg(any(target_os = "windows", target_os = "linux"))] + #[cfg(daita)] fn start_daita(&mut self) -> std::result::Result<(), TunnelError> { Ok(()) } diff --git a/talpid-wireguard/src/lib.rs b/talpid-wireguard/src/lib.rs index 596c96285114..a0a97e7a41bc 100644 --- a/talpid-wireguard/src/lib.rs +++ b/talpid-wireguard/src/lib.rs @@ -385,7 +385,7 @@ impl WireguardMonitor { let config = config.clone(); let iface_name = iface_name.clone(); tokio::task::spawn(async move { - #[cfg(target_os = "windows")] + #[cfg(daita)] if config.daita { // TODO: For now, we assume the MTU during the tunnel lifetime. // We could instead poke maybenot whenever we detect changes to it. @@ -558,7 +558,7 @@ impl WireguardMonitor { } config.exit_peer_mut().psk = exit_psk; - #[cfg(any(target_os = "windows", target_os = "linux"))] + #[cfg(daita)] if config.daita { log::trace!("Enabling constant packet size for entry peer"); config.entry_peer.constant_packet_size = true; @@ -576,7 +576,7 @@ impl WireguardMonitor { ) .await?; - #[cfg(any(target_os = "windows", target_os = "linux"))] + #[cfg(daita)] if config.daita { // Start local DAITA machines let mut tunnel = tunnel.lock().await; @@ -817,7 +817,7 @@ impl WireguardMonitor { let tunnel = Self::open_wireguard_go_tunnel( config, log_path, - #[cfg(any(target_os = "windows", target_os = "linux"))] + #[cfg(daita)] resource_dir, tun_provider, #[cfg(target_os = "android")] @@ -833,7 +833,7 @@ impl WireguardMonitor { fn open_wireguard_go_tunnel( config: &Config, log_path: Option<&Path>, - #[cfg(any(target_os = "windows", target_os = "linux"))] resource_dir: &Path, + #[cfg(daita)] resource_dir: &Path, tun_provider: Arc>, #[cfg(target_os = "android")] gateway_only: bool, ) -> Result { @@ -848,7 +848,7 @@ impl WireguardMonitor { log_path, tun_provider, routes, - #[cfg(any(target_os = "windows", target_os = "linux"))] + #[cfg(daita)] resource_dir, ) .map_err(Error::TunnelError)?; @@ -1065,7 +1065,7 @@ pub(crate) trait Tunnel: Send { &'a mut self, _config: Config, ) -> Pin> + Send + 'a>>; - #[cfg(any(target_os = "windows", target_os = "linux"))] + #[cfg(daita)] /// A [`Tunnel`] capable of using DAITA. fn start_daita(&mut self) -> std::result::Result<(), TunnelError>; } @@ -1143,17 +1143,18 @@ pub enum TunnelError { LoggingError(#[source] logging::Error), /// Failed to receive DAITA event - #[cfg(any(target_os = "windows", target_os = "linux"))] + #[cfg(daita)] #[error("Failed to start DAITA")] StartDaita(#[source] Box), /// This tunnel does not support DAITA. - #[cfg(any(target_os = "windows", target_os = "linux"))] + #[cfg(daita)] #[error("Failed to start DAITA - tunnel implemenation does not support DAITA")] DaitaNotSupported, } #[cfg(target_os = "linux")] +#[allow(dead_code)] fn will_nm_manage_dns() -> bool { use talpid_dbus::network_manager::NetworkManager; diff --git a/talpid-wireguard/src/wireguard_go/mod.rs b/talpid-wireguard/src/wireguard_go/mod.rs index de5e3e0f8389..5a30506f8425 100644 --- a/talpid-wireguard/src/wireguard_go/mod.rs +++ b/talpid-wireguard/src/wireguard_go/mod.rs @@ -1,7 +1,7 @@ use ipnetwork::IpNetwork; -#[cfg(any(target_os = "windows", target_os = "linux"))] +#[cfg(daita)] use once_cell::sync::OnceCell; -#[cfg(any(target_os = "windows", target_os = "linux"))] +#[cfg(daita)] use std::{ffi::CString, fs, path::PathBuf}; use std::{ future::Future, @@ -25,11 +25,11 @@ use crate::logging::{clean_up_logging, initialize_logging}; const MAX_PREPARE_TUN_ATTEMPTS: usize = 4; /// Maximum number of events that can be stored in the underlying buffer -#[cfg(any(target_os = "windows", target_os = "linux"))] +#[cfg(daita)] const DAITA_EVENTS_CAPACITY: u32 = 1000; /// Maximum number of actions that can be stored in the underlying buffer -#[cfg(any(target_os = "windows", target_os = "linux"))] +#[cfg(daita)] const DAITA_ACTIONS_CAPACITY: u32 = 1000; type Result = std::result::Result; @@ -52,9 +52,9 @@ pub struct WgGoTunnel { _logging_context: LoggingContext, #[cfg(target_os = "android")] tun_provider: Arc>, - #[cfg(any(target_os = "windows", target_os = "linux"))] + #[cfg(daita)] resource_dir: PathBuf, - #[cfg(any(target_os = "windows", target_os = "linux"))] + #[cfg(daita)] config: Config, } @@ -64,7 +64,7 @@ impl WgGoTunnel { log_path: Option<&Path>, tun_provider: Arc>, routes: impl Iterator, - #[cfg(any(target_os = "windows", target_os = "linux"))] resource_dir: &Path, + #[cfg(daita)] resource_dir: &Path, ) -> Result { #[cfg(target_os = "android")] let tun_provider_clone = tun_provider.clone(); @@ -101,9 +101,9 @@ impl WgGoTunnel { _logging_context: logging_context, #[cfg(target_os = "android")] tun_provider: tun_provider_clone, - #[cfg(any(target_os = "windows", target_os = "linux"))] + #[cfg(daita)] resource_dir: resource_dir.to_owned(), - #[cfg(any(target_os = "windows", target_os = "linux"))] + #[cfg(daita)] config: config.clone(), }) } @@ -242,7 +242,7 @@ impl Tunnel for WgGoTunnel { }) } - #[cfg(any(target_os = "windows", target_os = "linux"))] + #[cfg(daita)] fn start_daita(&mut self) -> Result<()> { static MAYBENOT_MACHINES: OnceCell = OnceCell::new(); let machines = MAYBENOT_MACHINES.get_or_try_init(|| { diff --git a/talpid-wireguard/src/wireguard_kernel/netlink_tunnel.rs b/talpid-wireguard/src/wireguard_kernel/netlink_tunnel.rs index bdc187e1bdb3..0c6bec882327 100644 --- a/talpid-wireguard/src/wireguard_kernel/netlink_tunnel.rs +++ b/talpid-wireguard/src/wireguard_kernel/netlink_tunnel.rs @@ -128,8 +128,8 @@ impl Tunnel for NetlinkTunnel { }) } - // TODO: We shouldn't force `NetlinkTunnel` to implement `start_daita` + /// Outright fail to start - this tunnel type does not support DAITA. fn start_daita(&mut self) -> std::result::Result<(), TunnelError> { - unreachable!("Netlink tunnel does not support DAITA") + Err(TunnelError::DaitaNotSupported) } } diff --git a/talpid-wireguard/src/wireguard_kernel/nm_tunnel.rs b/talpid-wireguard/src/wireguard_kernel/nm_tunnel.rs index 9fc935159156..12f37f3e4540 100644 --- a/talpid-wireguard/src/wireguard_kernel/nm_tunnel.rs +++ b/talpid-wireguard/src/wireguard_kernel/nm_tunnel.rs @@ -111,9 +111,9 @@ impl Tunnel for NetworkManagerTunnel { }) } - // TODO: We shouldn't force `NetworkManagerTunnel` tunnel to implement `start_daita` + /// Outright fail to start - this tunnel type does not support DAITA. fn start_daita(&mut self) -> std::result::Result<(), TunnelError> { - unreachable!("NetworkManager tunnel does not support DAITA") + Err(TunnelError::DaitaNotSupported) } } diff --git a/talpid-wireguard/src/wireguard_nt/mod.rs b/talpid-wireguard/src/wireguard_nt/mod.rs index de32c8d83e83..254c503dcff8 100644 --- a/talpid-wireguard/src/wireguard_nt/mod.rs +++ b/talpid-wireguard/src/wireguard_nt/mod.rs @@ -8,15 +8,17 @@ use bitflags::bitflags; use futures::SinkExt; use ipnetwork::IpNetwork; use once_cell::sync::{Lazy, OnceCell}; +#[cfg(daita)] +use std::{ffi::c_uchar, path::PathBuf}; use std::{ - ffi::{c_uchar, CStr}, + ffi::CStr, fmt, future::Future, io, mem::{self, MaybeUninit}, net::{IpAddr, Ipv4Addr, Ipv6Addr}, os::windows::io::RawHandle, - path::{Path, PathBuf}, + path::Path, pin::Pin, ptr, sync::{Arc, Mutex}, @@ -36,6 +38,7 @@ use windows_sys::{ }, }; +#[cfg(daita)] mod daita; static WG_NT_DLL: OnceCell = OnceCell::new(); @@ -159,20 +162,24 @@ pub enum Error { InvalidConfigData, /// DAITA machinist failed + #[cfg(daita)] #[error("Failed to enable DAITA on tunnel device")] EnableTunnelDaita(#[source] io::Error), /// DAITA machinist failed + #[cfg(daita)] #[error("Failed to initialize DAITA machinist")] InitializeMachinist(#[source] daita::Error), } pub struct WgNtTunnel { + #[cfg(daita)] resource_dir: PathBuf, config: Arc>, device: Option>, interface_name: String, setup_handle: tokio::task::JoinHandle<()>, + #[cfg(daita)] daita_handle: Option, _logger_handle: LoggerHandle, } @@ -314,6 +321,7 @@ bitflags! { const REPLACE_ALLOWED_IPS = 0b00100000; const REMOVE = 0b01000000; const UPDATE = 0b10000000; + #[cfg(daita)] const HAS_CONSTANT_PACKET_SIZE = 0b100000000; } } @@ -332,6 +340,7 @@ struct WgPeer { rx_bytes: u64, last_handshake: u64, allowed_ips_count: u32, + #[cfg(daita)] constant_packet_size: c_uchar, } @@ -468,11 +477,13 @@ impl WgNtTunnel { }); Ok(WgNtTunnel { + #[cfg(daita)] resource_dir: resource_dir.to_owned(), config: Arc::new(Mutex::new(config.clone())), device, interface_name, setup_handle, + #[cfg(daita)] daita_handle: None, _logger_handle: logger_handle, }) @@ -480,12 +491,14 @@ impl WgNtTunnel { fn stop_tunnel(&mut self) { self.setup_handle.abort(); + #[cfg(daita)] if let Some(daita_handle) = self.daita_handle.take() { let _ = daita_handle.close(); } let _ = self.device.take(); } + #[cfg(daita)] fn spawn_machinist(&mut self) -> Result<()> { if let Some(handle) = self.daita_handle.take() { log::info!("Stopping previous DAITA machines"); @@ -666,10 +679,13 @@ struct WgNtDll { func_set_adapter_state: WireGuardSetStateFn, func_set_logger: WireGuardSetLoggerFn, func_set_adapter_logging: WireGuardSetAdapterLoggingFn, - + #[cfg(daita)] func_daita_activate: daita::bindings::WireGuardDaitaActivateFn, + #[cfg(daita)] func_daita_event_data_available_event: daita::bindings::WireGuardDaitaEventDataAvailableEventFn, + #[cfg(daita)] func_daita_receive_events: daita::bindings::WireGuardDaitaReceiveEventsFn, + #[cfg(daita)] func_daita_send_action: daita::bindings::WireGuardDaitaSendActionFn, } @@ -743,24 +759,28 @@ impl WgNtDll { CStr::from_bytes_with_nul(b"WireGuardSetAdapterLogging\0").unwrap(), )?) as *const _ as *const _) }, + #[cfg(daita)] func_daita_activate: unsafe { *((&get_proc_fn( handle, CStr::from_bytes_with_nul(b"WireGuardDaitaActivate\0").unwrap(), )?) as *const _ as *const _) }, + #[cfg(daita)] func_daita_event_data_available_event: unsafe { *((&get_proc_fn( handle, CStr::from_bytes_with_nul(b"WireGuardDaitaEventDataAvailableEvent\0").unwrap(), )?) as *const _ as *const _) }, + #[cfg(daita)] func_daita_receive_events: unsafe { *((&get_proc_fn( handle, CStr::from_bytes_with_nul(b"WireGuardDaitaReceiveEvents\0").unwrap(), )?) as *const _ as *const _) }, + #[cfg(daita)] func_daita_send_action: unsafe { *((&get_proc_fn( handle, @@ -864,6 +884,7 @@ impl WgNtDll { Ok(()) } + #[cfg(daita)] pub unsafe fn daita_activate( &self, adapter: RawHandle, @@ -876,6 +897,7 @@ impl WgNtDll { Ok(()) } + #[cfg(daita)] pub unsafe fn daita_event_data_available_event( &self, adapter: RawHandle, @@ -887,6 +909,7 @@ impl WgNtDll { Ok(ready_event) } + #[cfg(daita)] pub unsafe fn daita_receive_events( &self, adapter: RawHandle, @@ -899,6 +922,7 @@ impl WgNtDll { Ok(num_events) } + #[cfg(daita)] pub unsafe fn daita_send_action( &self, adapter: RawHandle, @@ -935,12 +959,16 @@ fn serialize_config(config: &Config) -> Result>> { buffer.extend(as_uninit_byte_slice(&header)); for peer in config.peers() { + #[cfg(not(daita))] + let mut flags = WgPeerFlag::HAS_PUBLIC_KEY | WgPeerFlag::HAS_ENDPOINT; + #[cfg(daita)] let mut flags = WgPeerFlag::HAS_PUBLIC_KEY | WgPeerFlag::HAS_ENDPOINT | WgPeerFlag::HAS_CONSTANT_PACKET_SIZE; if peer.psk.is_some() { flags |= WgPeerFlag::HAS_PRESHARED_KEY; } + #[cfg(daita)] let constant_packet_size = if peer.constant_packet_size { 1 } else { 0 }; let wg_peer = WgPeer { flags, @@ -957,6 +985,7 @@ fn serialize_config(config: &Config) -> Result>> { rx_bytes: 0, last_handshake: 0, allowed_ips_count: u32::try_from(peer.allowed_ips.len()).unwrap(), + #[cfg(daita)] constant_packet_size, }; @@ -1101,6 +1130,7 @@ impl Tunnel for WgNtTunnel { }) } + #[cfg(daita)] fn start_daita(&mut self) -> std::result::Result<(), crate::TunnelError> { self.spawn_machinist().map_err(|error| { log::error!( @@ -1150,6 +1180,7 @@ mod tests { ipv6_gateway: None, mtu: 0, obfuscator_config: None, + #[cfg(daita)] daita: false, quantum_resistant: false, }); diff --git a/test/test-manager/build.rs b/test/test-manager/build.rs index 07be8b9677c0..37d4cac43b17 100644 --- a/test/test-manager/build.rs +++ b/test/test-manager/build.rs @@ -1,4 +1,4 @@ fn main() { // Rebuild if SSH provision script changes - println!("cargo:rerun-if-changed=../scripts/ssh-setup.sh"); + println!("cargo::rerun-if-changed=../scripts/ssh-setup.sh"); } diff --git a/wireguard-go-rs/build.rs b/wireguard-go-rs/build.rs index 82a8ff254b55..9986e9778c9a 100644 --- a/wireguard-go-rs/build.rs +++ b/wireguard-go-rs/build.rs @@ -4,6 +4,8 @@ use std::{env, path::PathBuf}; fn main() { let out_dir = env::var("OUT_DIR").expect("Missing OUT_DIR"); eprintln!("OUT_DIR: {out_dir}"); + // Add DAITA as a conditional configuration + println!("cargo::rustc-check-cfg=cfg(daita)"); let target_os = env::var("CARGO_CFG_TARGET_OS").expect("Missing 'CARGO_CFG_TARGET_OS"); let mut cmd = std::process::Command::new("bash"); @@ -12,7 +14,7 @@ fn main() { match target_os.as_str() { "linux" => { // Enable DAITA & Tell rustc to link libmaybenot - println!("cargo::rustc-link-lib=static=maybenot"); + println!(r#"cargo::rustc-cfg=daita"#); // Tell the build script to build wireguard-go with DAITA support cmd.arg("--daita"); } diff --git a/wireguard-go-rs/src/lib.rs b/wireguard-go-rs/src/lib.rs index f87b2586d0f0..02518997549c 100644 --- a/wireguard-go-rs/src/lib.rs +++ b/wireguard-go-rs/src/lib.rs @@ -151,7 +151,7 @@ impl Tunnel { /// Activate DAITA for the specified peer. /// /// `machines` is a string containing LF-separated maybenot machines. - #[cfg(any(target_os = "windows", target_os = "linux"))] + #[cfg(daita)] pub fn activate_daita( &self, peer_public_key: &[u8; 32], @@ -265,7 +265,7 @@ mod ffi { /// - `peer_public_key` must point to a 32 byte array. /// - `machines` must point to a null-terminated UTF-8 string. /// - Neither pointer will be read from after `wgActivateDaita` has returned. - #[cfg(any(target_os = "windows", target_os = "linux"))] + #[cfg(daita)] pub fn wgActivateDaita( tunnel_handle: i32, peer_public_key: *const u8, From 7c63a2eed57529f2ac19990f0b529087846a0839 Mon Sep 17 00:00:00 2001 From: Markus Pettersson Date: Fri, 24 May 2024 15:50:37 +0200 Subject: [PATCH 08/16] Link statically against libwg MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: David Lönnhager --- Cargo.lock | 75 +++++++++----------------- README.md | 6 +++ talpid-wireguard/Cargo.toml | 2 +- wireguard-go-rs/Cargo.toml | 9 +++- wireguard-go-rs/README.md | 9 ++-- wireguard-go-rs/build-wireguard-go.sh | 35 +++++++----- wireguard-go-rs/build.rs | 24 +++++---- wireguard-go-rs/libwg/build-android.sh | 2 - wireguard-go-rs/src/lib.rs | 4 ++ 9 files changed, 82 insertions(+), 84 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8709388967f5..898d99849156 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1932,7 +1932,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" dependencies = [ "cfg-if", - "windows-targets 0.52.5", + "windows-targets 0.48.5", ] [[package]] @@ -2026,20 +2026,29 @@ checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" [[package]] name = "maybenot" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7fe205734d700937dabf0b8687e290f8574fac996f8a9d04bd7a62d7c2c1dad" +checksum = "e308ea251c8fe965732a020db1aa182a1df0cfb551da0d422bf83016d0f10153" dependencies = [ "byteorder", "hex", "libflate", "rand 0.8.5", "rand_distr", - "ring 0.16.20", + "ring", "serde", "simple-error", ] +[[package]] +name = "maybenot-ffi" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12a95dd874046b87f98b3a54e6beed8a63db6354088efd0ae7dc23c0f23931ce" +dependencies = [ + "maybenot", +] + [[package]] name = "md-5" version = "0.10.6" @@ -3277,21 +3286,6 @@ dependencies = [ "quick-error", ] -[[package]] -name = "ring" -version = "0.16.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" -dependencies = [ - "cc", - "libc", - "once_cell", - "spin 0.5.2", - "untrusted 0.7.1", - "web-sys", - "winapi", -] - [[package]] name = "ring" version = "0.17.8" @@ -3302,8 +3296,8 @@ dependencies = [ "cfg-if", "getrandom 0.2.14", "libc", - "spin 0.9.8", - "untrusted 0.9.0", + "spin", + "untrusted", "windows-sys 0.52.0", ] @@ -3322,7 +3316,7 @@ dependencies = [ "p384", "pkcs8", "rand_core 0.6.4", - "ring 0.17.8", + "ring", "signature", ] @@ -3388,7 +3382,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fecbfb7b1444f477b345853b1fce097a2c6fb637b2bfb87e6bc5db0f043fae4" dependencies = [ "log", - "ring 0.17.8", + "ring", "rustls-webpki", "sct", ] @@ -3408,8 +3402,8 @@ version = "0.101.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" dependencies = [ - "ring 0.17.8", - "untrusted 0.9.0", + "ring", + "untrusted", ] [[package]] @@ -3457,8 +3451,8 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ - "ring 0.17.8", - "untrusted 0.9.0", + "ring", + "untrusted", ] [[package]] @@ -3592,7 +3586,7 @@ dependencies = [ "serde_urlencoded", "shadowsocks-crypto", "socket2", - "spin 0.9.8", + "spin", "thiserror", "tokio", "tokio-tfo", @@ -3661,7 +3655,7 @@ dependencies = [ "serde", "shadowsocks", "socket2", - "spin 0.9.8", + "spin", "thiserror", "tokio", "windows-sys 0.52.0", @@ -3748,12 +3742,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - [[package]] name = "spin" version = "0.9.8" @@ -4583,12 +4571,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "untrusted" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" - [[package]] name = "untrusted" version = "0.9.0" @@ -4723,16 +4705,6 @@ version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" -[[package]] -name = "web-sys" -version = "0.3.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - [[package]] name = "which" version = "4.4.2" @@ -5084,6 +5056,7 @@ name = "wireguard-go-rs" version = "0.0.0" dependencies = [ "log", + "maybenot-ffi", "thiserror", "zeroize", ] diff --git a/README.md b/README.md index 3450ad0ba8f9..8788e3c8592b 100644 --- a/README.md +++ b/README.md @@ -91,6 +91,12 @@ cd mullvadvpn-app git submodule update --init ``` +On Android, Linux and macOS you also want to checkout the wireguard-go submodule recursively: +```bash +git submodule update --init --recursive --depth=1 wireguard-go-rs +``` +Further details on why this is necessary can be found in the [wireguard-go-rs crate](./wireguard-go-rs/README.md). + We sign every commit on the `main` branch as well as our release tags. If you would like to verify your checkout, you can find our developer keys on [Mullvad's Open Source page]. diff --git a/talpid-wireguard/Cargo.toml b/talpid-wireguard/Cargo.toml index e7db72cac6fe..1b103f9d1d54 100644 --- a/talpid-wireguard/Cargo.toml +++ b/talpid-wireguard/Cargo.toml @@ -57,7 +57,7 @@ talpid-dbus = { path = "../talpid-dbus" } bitflags = "1.2" talpid-windows = { path = "../talpid-windows" } widestring = "1.0" -maybenot = "1.0" +maybenot = "1.1.2" # TODO: Figure out which features are needed and which are not [target.'cfg(windows)'.dependencies.windows-sys] diff --git a/wireguard-go-rs/Cargo.toml b/wireguard-go-rs/Cargo.toml index 3725787bf6dc..9b09df4f758a 100644 --- a/wireguard-go-rs/Cargo.toml +++ b/wireguard-go-rs/Cargo.toml @@ -4,7 +4,14 @@ description = "Rust bindings to wireguard-go with DAITA support" edition = "2021" license.workspace = true -[dependencies] +[target.'cfg(unix)'.dependencies] thiserror.workspace = true log.workspace = true zeroize = "1.8.1" + +[target.'cfg(target_os = "linux")'.dependencies] +# The app does not depend on maybenot-ffi itself, but adds it as a dependency to expose FFI symbols to wireguard-go. +# This is done, instead of using the makefile in wireguard-go to build maybenot-ffi into its archive, to prevent +# name clashes induced by link-time optimization. +# NOTE: the version of maybenot-ffi below must be the same as the version checked into the wireguard-go submodule +maybenot-ffi = "1.0.0" diff --git a/wireguard-go-rs/README.md b/wireguard-go-rs/README.md index 24a08fa54b24..948c1d0390be 100644 --- a/wireguard-go-rs/README.md +++ b/wireguard-go-rs/README.md @@ -1,10 +1,11 @@ # `wireguard-go-rs` -This crate wraps `libwg`, which in turn wraps [Mullvad VPN's fork of wireguard-go](https://github.com/mullvad/wireguard-go) which extends `wireguard-go` with [DAITA](https://mullvad.net/en/blog/introducing-defense-against-ai-guided-traffic-analysis-daita). +This crate is a Rust-friendly wrapper around `wireguard-go`. +It wraps `libwg`, which in turn wraps [Mullvad VPN's fork of wireguard-go](https://github.com/mullvad/wireguard-go) that extends `wireguard-go` with [DAITA](https://mullvad.net/en/blog/introducing-defense-against-ai-guided-traffic-analysis-daita). ## Known limitation -To extend `wireguard-go` with DAITA capabilities, it statically links against [maybenot](https://github.com/maybenot-io/maybenot/), which at the time of writing will cause issues if it in turn is statically linked from another Rust crate: https://github.com/rust-lang/rust/issues/104707. -As such, `libwg` is built as a shared object which you have to link to dynamically. -To get rid of this limitation, you could compile `wireguard-go` without DAITA support. See [build-wireguard-go.sh](./build-wireguard-go.sh) for details. +To extend `wireguard-go` with DAITA capabilities, `wireguard-go` links against [maybenot](https://github.com/maybenot-io/maybenot/). This is done statically, which at the time of writing will cause issues if `wireguard-go` in turn is statically linked from another Rust crate: https://github.com/rust-lang/rust/issues/104707. +As such `libwg` is built as a shared object which you can link to dynamically, which circumvents this issue. +To get rid of this limitation, you can compile `wireguard-go` without DAITA support. See [build-wireguard-go.sh](./build-wireguard-go.sh) for details on how to do that. ## Upgrading `wireguard-go` Upgrading `wireguard-go` involves updating the git submodule found in `libwg/wireguard-go`. This module uses [Mullvad VPN's fork of wireguard-go](https://github.com/mullvad/wireguard-go). diff --git a/wireguard-go-rs/build-wireguard-go.sh b/wireguard-go-rs/build-wireguard-go.sh index e270fb82191a..35bb9413f20e 100755 --- a/wireguard-go-rs/build-wireguard-go.sh +++ b/wireguard-go-rs/build-wireguard-go.sh @@ -1,22 +1,30 @@ #!/usr/bin/env bash - -# This script is used to build wireguard-go libraries for all the platforms. # -# If "DAITA" support should be enabled, pass the `--daita` flag when invoking this script. +# This script is used to build libwg (wireguard-go as an FFI-friendly library) for different platforms. +# +# Supported arguments: +# * --android +# If libwg should be built for an Android target. +# * --daita +# Build libwg with "DAITA" support. +# set -eu +# If the target OS is Android. +ANDROID="false" # If Wireguard-go should be built with DAITA-support. DAITA="false" -# If the target OS is Adnroid. -ANDROID="false" + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +BUILD_DIR="$SCRIPT_DIR/../build" while [[ "$#" -gt 0 ]]; do case $1 in --android) ANDROID="true";; --daita) DAITA="true";; *) - log_error "Unknown parameter: $1" + echo "Unknown parameter: $1" exit 1 ;; esac @@ -45,7 +53,6 @@ function unix_target_triple { function build_unix { - # TODO: consider using `log_header` here echo "Building wireguard-go for $1" # Flags for cross compiling @@ -79,16 +86,16 @@ function build_unix { fi - # Build wiregaurd-go as a library + # Build wireguard-go as a library + mkdir -p "$BUILD_DIR/lib/$TARGET" pushd libwg + if [[ "$DAITA" == "true" ]]; then - pushd wireguard-go - make libmaybenot.a LIBDEST="$OUT_DIR" - popd - go build -v --tags daita -o "$OUT_DIR"/libwg.a -buildmode c-archive + go build -v --tags daita -o "$BUILD_DIR/lib/$TARGET"/libwg.a -buildmode c-archive else - go build -v -o "$OUT_DIR"/libwg.a -buildmode c-archive + go build -v -o "$BUILD_DIR/lib/$TARGET"/libwg.a -buildmode c-archive fi + popd } @@ -107,7 +114,7 @@ function build_wireguard_go { local platform platform="$(uname -s)"; case "$platform" in - Linux*|Darwin*) build_unix "${1:-$(unix_target_triple)}";; + Linux*|Darwin*) build_unix "${TARGET:-$(unix_target_triple)}";; *) echo "Unsupported platform" return 1 diff --git a/wireguard-go-rs/build.rs b/wireguard-go-rs/build.rs index 9986e9778c9a..b82fa0eb5fee 100644 --- a/wireguard-go-rs/build.rs +++ b/wireguard-go-rs/build.rs @@ -3,7 +3,7 @@ use std::{env, path::PathBuf}; fn main() { let out_dir = env::var("OUT_DIR").expect("Missing OUT_DIR"); - eprintln!("OUT_DIR: {out_dir}"); + // Add DAITA as a conditional configuration println!("cargo::rustc-check-cfg=cfg(daita)"); @@ -13,7 +13,7 @@ fn main() { match target_os.as_str() { "linux" => { - // Enable DAITA & Tell rustc to link libmaybenot + // Enable DAITA println!(r#"cargo::rustc-cfg=daita"#); // Tell the build script to build wireguard-go with DAITA support cmd.arg("--daita"); @@ -36,19 +36,21 @@ fn main() { panic!(); } - if target_os == "android" { - // NOTE: Go programs does not support being statically linked on android - // so we need to dynamically link to libwg - println!("cargo::rustc-link-lib=wg"); - declare_libs_dir("../build/lib"); - } else { - // other platforms can statically link to libwg just fine - // TODO: consider doing dynamic linking everywhere, to keep things simpler + if target_os.as_str() != "android" { println!("cargo::rustc-link-lib=static=wg"); - println!("cargo::rustc-link-search={out_dir}"); + } else { + // NOTE: Link dynamically to libwg on Android, as go cannot produce archives + println!("cargo::rustc-link-lib=dylib=wg"); } + declare_libs_dir("../build/lib"); println!("cargo::rerun-if-changed=libwg"); + + // Add `OUT_DIR` to the library search path to facilitate linking of libwg for debug artifacts, + // such as test binaries. + if cfg!(debug_assertions) { + println!("cargo::rustc-link-search={out_dir}"); + } } /// Tell linker to check `base`/$TARGET for shared libraries. diff --git a/wireguard-go-rs/libwg/build-android.sh b/wireguard-go-rs/libwg/build-android.sh index 750438c503dd..0c2336185ada 100755 --- a/wireguard-go-rs/libwg/build-android.sh +++ b/wireguard-go-rs/libwg/build-android.sh @@ -57,8 +57,6 @@ for arch in ${ARCHITECTURES:-armv7 aarch64 x86_64 i686}; do # Set permissions so that the build server can clean the outputs afterwards chmod 777 "$STRIPPED_LIB_PATH" - - rm -rf build done # ensure `git clean -fd` does not require root permissions diff --git a/wireguard-go-rs/src/lib.rs b/wireguard-go-rs/src/lib.rs index 02518997549c..b5b4000a80e9 100644 --- a/wireguard-go-rs/src/lib.rs +++ b/wireguard-go-rs/src/lib.rs @@ -26,6 +26,10 @@ pub type LoggingContext = u64; pub type LoggingCallback = unsafe extern "system" fn(level: WgLogLevel, msg: *const c_char, context: LoggingContext); +// Make symbols from maybenot-ffi visible to wireguard-go +#[cfg(daita)] +use maybenot_ffi as _; + /// A wireguard-go tunnel pub struct Tunnel { /// wireguard-go handle to the tunnel. From 9217bc1ed8e7c41a241d98f655d05dbc48a26ae6 Mon Sep 17 00:00:00 2001 From: Sebastian Holmin Date: Thu, 20 Jun 2024 10:46:33 +0200 Subject: [PATCH 09/16] Add instructions for upgrading golang and `wireguard-go` - Remove unused android go patch duplicate - Add mullvad copyright to `libwg_daita.go` - Fix daita blog link language --- wireguard-go-rs/README.md | 32 +++- .../goruntime-boottime-over-monotonic.diff | 171 ------------------ wireguard-go-rs/libwg/libwg_daita.go | 5 + 3 files changed, 30 insertions(+), 178 deletions(-) delete mode 100644 wireguard-go-rs/libwg/goruntime-boottime-over-monotonic.diff diff --git a/wireguard-go-rs/README.md b/wireguard-go-rs/README.md index 948c1d0390be..a4745edc3ed2 100644 --- a/wireguard-go-rs/README.md +++ b/wireguard-go-rs/README.md @@ -1,11 +1,29 @@ # `wireguard-go-rs` -This crate is a Rust-friendly wrapper around `wireguard-go`. -It wraps `libwg`, which in turn wraps [Mullvad VPN's fork of wireguard-go](https://github.com/mullvad/wireguard-go) that extends `wireguard-go` with [DAITA](https://mullvad.net/en/blog/introducing-defense-against-ai-guided-traffic-analysis-daita). -## Known limitation -To extend `wireguard-go` with DAITA capabilities, `wireguard-go` links against [maybenot](https://github.com/maybenot-io/maybenot/). This is done statically, which at the time of writing will cause issues if `wireguard-go` in turn is statically linked from another Rust crate: https://github.com/rust-lang/rust/issues/104707. -As such `libwg` is built as a shared object which you can link to dynamically, which circumvents this issue. -To get rid of this limitation, you can compile `wireguard-go` without DAITA support. See [build-wireguard-go.sh](./build-wireguard-go.sh) for details on how to do that. +This crate is a Rust-friendly wrapper around `wireguard-go`. It wraps `libwg`, which in turn wraps +[Mullvad VPN's fork of wireguard-go](https://github.com/mullvad/wireguard-go) with support for +[DAITA](https://mullvad.net/blog/introducing-defense-against-ai-guided-traffic-analysis-daita). ## Upgrading `wireguard-go` -Upgrading `wireguard-go` involves updating the git submodule found in `libwg/wireguard-go`. This module uses [Mullvad VPN's fork of wireguard-go](https://github.com/mullvad/wireguard-go). + +Upgrading `wireguard-go` involves updating the git submodule found in `libwg/wireguard-go`, +which points to [Mullvad VPN's fork of wireguard-go](https://github.com/mullvad/wireguard-go). +To update the fork, find the desired release of `wireguard-go` at + and rebase the fork on the corresponding commit. +Change directory to `libwg` and run `go mod tidy` to update indirect dependencies. + +To upgrade the version of `Go` run `go mod edit -go=XX`. You will also need to update the +`ARG GOLANG_VERSION` version in `building/Dockerfile` and build and distribute new container images, +see the corresponding [instructions](../building/README.md). + +### Caution: Go runtime patch + +Before upgrading the version of `Go` or `wireguard-go`, be aware that we depend on a patch for the +internal clocks of the go runtime on android, +see . Upgrading the versions of +`wireguard-go` or `Go` beyond what the patch is built for should be done with caution. Note, however, +that the patch states that "In Linux 4.17, the kernel will actually make MONOTONIC act like BOOTTIME +anyway, so this switch will additionally unify the timer behavior across kernels." According to +, Android version 11 and +newer seem to use sufficiently new versions of the linux kernel to not need this patch. When we no +longer support older versions of android, we may be able to drop this compatibility requirement. diff --git a/wireguard-go-rs/libwg/goruntime-boottime-over-monotonic.diff b/wireguard-go-rs/libwg/goruntime-boottime-over-monotonic.diff deleted file mode 100644 index 5d78242b139e..000000000000 --- a/wireguard-go-rs/libwg/goruntime-boottime-over-monotonic.diff +++ /dev/null @@ -1,171 +0,0 @@ -From 61f3ae8298d1c503cbc31539e0f3a73446c7db9d Mon Sep 17 00:00:00 2001 -From: "Jason A. Donenfeld" -Date: Tue, 21 Mar 2023 15:33:56 +0100 -Subject: [PATCH] [release-branch.go1.20] runtime: use CLOCK_BOOTTIME in - nanotime on Linux - -This makes timers account for having expired while a computer was -asleep, which is quite common on mobile devices. Note that BOOTTIME is -identical to MONOTONIC, except that it takes into account time spent -in suspend. In Linux 4.17, the kernel will actually make MONOTONIC act -like BOOTTIME anyway, so this switch will additionally unify the -timer behavior across kernels. - -BOOTTIME was introduced into Linux 2.6.39-rc1 with 70a08cca1227d in -2011. - -Fixes #24595 - -Change-Id: I7b2a6ca0c5bc5fce57ec0eeafe7b68270b429321 ---- - src/runtime/sys_linux_386.s | 4 ++-- - src/runtime/sys_linux_amd64.s | 2 +- - src/runtime/sys_linux_arm.s | 4 ++-- - src/runtime/sys_linux_arm64.s | 4 ++-- - src/runtime/sys_linux_mips64x.s | 4 ++-- - src/runtime/sys_linux_mipsx.s | 2 +- - src/runtime/sys_linux_ppc64x.s | 2 +- - src/runtime/sys_linux_s390x.s | 2 +- - 8 files changed, 12 insertions(+), 12 deletions(-) - -diff --git a/src/runtime/sys_linux_386.s b/src/runtime/sys_linux_386.s -index 12a294153d..17e3524b40 100644 ---- a/src/runtime/sys_linux_386.s -+++ b/src/runtime/sys_linux_386.s -@@ -352,13 +352,13 @@ noswitch: - - LEAL 8(SP), BX // &ts (struct timespec) - MOVL BX, 4(SP) -- MOVL $1, 0(SP) // CLOCK_MONOTONIC -+ MOVL $7, 0(SP) // CLOCK_BOOTTIME - CALL AX - JMP finish - - fallback: - MOVL $SYS_clock_gettime, AX -- MOVL $1, BX // CLOCK_MONOTONIC -+ MOVL $7, BX // CLOCK_BOOTTIME - LEAL 8(SP), CX - INVOKE_SYSCALL - -diff --git a/src/runtime/sys_linux_amd64.s b/src/runtime/sys_linux_amd64.s -index c7a89ba536..01f0a6a26e 100644 ---- a/src/runtime/sys_linux_amd64.s -+++ b/src/runtime/sys_linux_amd64.s -@@ -255,7 +255,7 @@ noswitch: - SUBQ $16, SP // Space for results - ANDQ $~15, SP // Align for C code - -- MOVL $1, DI // CLOCK_MONOTONIC -+ MOVL $7, DI // CLOCK_BOOTTIME - LEAQ 0(SP), SI - MOVQ runtime·vdsoClockgettimeSym(SB), AX - CMPQ AX, $0 -diff --git a/src/runtime/sys_linux_arm.s b/src/runtime/sys_linux_arm.s -index 7b8c4f0e04..9798a1334e 100644 ---- a/src/runtime/sys_linux_arm.s -+++ b/src/runtime/sys_linux_arm.s -@@ -11,7 +11,7 @@ - #include "textflag.h" - - #define CLOCK_REALTIME 0 --#define CLOCK_MONOTONIC 1 -+#define CLOCK_BOOTTIME 7 - - // for EABI, as we don't support OABI - #define SYS_BASE 0x0 -@@ -374,7 +374,7 @@ finish: - - // func nanotime1() int64 - TEXT runtime·nanotime1(SB),NOSPLIT,$12-8 -- MOVW $CLOCK_MONOTONIC, R0 -+ MOVW $CLOCK_BOOTTIME, R0 - MOVW $spec-12(SP), R1 // timespec - - MOVW runtime·vdsoClockgettimeSym(SB), R4 -diff --git a/src/runtime/sys_linux_arm64.s b/src/runtime/sys_linux_arm64.s -index 38ff6ac330..6b819c5441 100644 ---- a/src/runtime/sys_linux_arm64.s -+++ b/src/runtime/sys_linux_arm64.s -@@ -14,7 +14,7 @@ - #define AT_FDCWD -100 - - #define CLOCK_REALTIME 0 --#define CLOCK_MONOTONIC 1 -+#define CLOCK_BOOTTIME 7 - - #define SYS_exit 93 - #define SYS_read 63 -@@ -338,7 +338,7 @@ noswitch: - BIC $15, R1 - MOVD R1, RSP - -- MOVW $CLOCK_MONOTONIC, R0 -+ MOVW $CLOCK_BOOTTIME, R0 - MOVD runtime·vdsoClockgettimeSym(SB), R2 - CBZ R2, fallback - -diff --git a/src/runtime/sys_linux_mips64x.s b/src/runtime/sys_linux_mips64x.s -index 47f2da524d..a8b387f193 100644 ---- a/src/runtime/sys_linux_mips64x.s -+++ b/src/runtime/sys_linux_mips64x.s -@@ -326,7 +326,7 @@ noswitch: - AND $~15, R1 // Align for C code - MOVV R1, R29 - -- MOVW $1, R4 // CLOCK_MONOTONIC -+ MOVW $7, R4 // CLOCK_BOOTTIME - MOVV $0(R29), R5 - - MOVV runtime·vdsoClockgettimeSym(SB), R25 -@@ -336,7 +336,7 @@ noswitch: - // see walltime for detail - BEQ R2, R0, finish - MOVV R0, runtime·vdsoClockgettimeSym(SB) -- MOVW $1, R4 // CLOCK_MONOTONIC -+ MOVW $7, R4 // CLOCK_BOOTTIME - MOVV $0(R29), R5 - JMP fallback - -diff --git a/src/runtime/sys_linux_mipsx.s b/src/runtime/sys_linux_mipsx.s -index 5e6b6c1504..7f5fd2a80e 100644 ---- a/src/runtime/sys_linux_mipsx.s -+++ b/src/runtime/sys_linux_mipsx.s -@@ -243,7 +243,7 @@ TEXT runtime·walltime(SB),NOSPLIT,$8-12 - RET - - TEXT runtime·nanotime1(SB),NOSPLIT,$8-8 -- MOVW $1, R4 // CLOCK_MONOTONIC -+ MOVW $7, R4 // CLOCK_BOOTTIME - MOVW $4(R29), R5 - MOVW $SYS_clock_gettime, R2 - SYSCALL -diff --git a/src/runtime/sys_linux_ppc64x.s b/src/runtime/sys_linux_ppc64x.s -index d0427a4807..05ee9fede9 100644 ---- a/src/runtime/sys_linux_ppc64x.s -+++ b/src/runtime/sys_linux_ppc64x.s -@@ -298,7 +298,7 @@ fallback: - JMP return - - TEXT runtime·nanotime1(SB),NOSPLIT,$16-8 -- MOVD $1, R3 // CLOCK_MONOTONIC -+ MOVD $7, R3 // CLOCK_BOOTTIME - - MOVD R1, R15 // R15 is unchanged by C code - MOVD g_m(g), R21 // R21 = m -diff --git a/src/runtime/sys_linux_s390x.s b/src/runtime/sys_linux_s390x.s -index 1448670b91..7d2ee3231c 100644 ---- a/src/runtime/sys_linux_s390x.s -+++ b/src/runtime/sys_linux_s390x.s -@@ -296,7 +296,7 @@ fallback: - RET - - TEXT runtime·nanotime1(SB),NOSPLIT,$32-8 -- MOVW $1, R2 // CLOCK_MONOTONIC -+ MOVW $7, R2 // CLOCK_BOOTTIME - - MOVD R15, R7 // Backup stack pointer - --- -2.17.1 - diff --git a/wireguard-go-rs/libwg/libwg_daita.go b/wireguard-go-rs/libwg/libwg_daita.go index e33de84e4911..d5f8f132eddc 100644 --- a/wireguard-go-rs/libwg/libwg_daita.go +++ b/wireguard-go-rs/libwg/libwg_daita.go @@ -1,6 +1,11 @@ //go:build daita // +build daita +/* SPDX-License-Identifier: Apache-2.0 + * + * Copyright (C) 2024 Mullvad VPN AB. All Rights Reserved. + */ + package main // #include From e5c0771680a8c66e236fbd30ae24f51334de8c80 Mon Sep 17 00:00:00 2001 From: Markus Pettersson Date: Fri, 17 May 2024 16:39:33 +0200 Subject: [PATCH 10/16] Update Github actions to use `wireguard-go` as a submodule --- .github/workflows/android-app.yml | 11 ++++++- .github/workflows/cargo-vendor.yml | 4 ++- .github/workflows/clippy.yml | 17 +++++++++-- .github/workflows/daemon.yml | 9 ++++-- .github/workflows/desktop-e2e.yml | 15 +++++----- .github/workflows/frontend.yml | 3 ++ .github/workflows/rust-supply-chain.yml | 3 ++ .../workflows/rust-unused-dependencies.yml | 29 ++++++++++++++----- .github/workflows/rustfmt.yml | 3 ++ .github/workflows/unicode-check.yml | 2 +- 10 files changed, 74 insertions(+), 22 deletions(-) diff --git a/.github/workflows/android-app.yml b/.github/workflows/android-app.yml index 562ae34e8e6f..6637183db238 100644 --- a/.github/workflows/android-app.yml +++ b/.github/workflows/android-app.yml @@ -118,6 +118,8 @@ jobs: - name: Checkout repository if: steps.cache-relay-list.outputs.cache-hit != 'true' uses: actions/checkout@v4 + with: + submodules: true - name: Generate if: steps.cache-relay-list.outputs.cache-hit != 'true' @@ -164,6 +166,8 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 + with: + submodules: true - name: Calculate native lib cache hash id: native-lib-cache-hash @@ -195,7 +199,6 @@ jobs: UNSTRIPPED_LIB_PATH="$CARGO_TARGET_DIR/${{ matrix.target }}/$BUILD_TYPE/libmullvad_jni.so" STRIPPED_LIB_PATH="./android/app/build/extraJni/${{ matrix.abi }}/libmullvad_jni.so" NDK_TOOLCHAIN_STRIP_TOOL="$NDK_TOOLCHAIN_DIR/llvm-strip" - ./wireguard/build-wireguard-go.sh --android --no-docker cargo build --target ${{ matrix.target }} --verbose --package mullvad-jni --features api-override $NDK_TOOLCHAIN_STRIP_TOOL --strip-debug --strip-unneeded -o "$STRIPPED_LIB_PATH" "$UNSTRIPPED_LIB_PATH" @@ -235,6 +238,8 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 + with: + submodules: true - name: Run gradle task uses: burrunan/gradle-cache-action@v1 @@ -262,6 +267,8 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 + with: + submodules: true - uses: actions/download-artifact@v4 with: @@ -366,6 +373,8 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 + with: + submodules: true - uses: actions/download-artifact@v4 with: diff --git a/.github/workflows/cargo-vendor.yml b/.github/workflows/cargo-vendor.yml index 276760781a06..64bd08328f13 100644 --- a/.github/workflows/cargo-vendor.yml +++ b/.github/workflows/cargo-vendor.yml @@ -16,7 +16,9 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 + with: + submodules: true - name: Install Rust uses: actions-rs/toolchain@v1.0.6 diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 237ba8d953a3..a922f647854f 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -43,8 +43,10 @@ jobs: with: repo-token: ${{ secrets.GITHUB_TOKEN }} - - name: Checkout binaries submodule - run: git submodule update --init --depth=1 dist-assets/binaries + - name: Checkout submodules + run: | + git submodule update --init --depth=1 dist-assets/binaries + git submodule update --init --recursive --depth=1 wireguard-go-rs - uses: actions-rs/toolchain@v1.0.6 with: @@ -58,6 +60,12 @@ jobs: sudo apt-get update sudo apt-get install libdbus-1-dev + - name: Install Go + if: matrix.os == 'linux-latest' || matrix.os == 'macos-latest' + uses: actions/setup-go@v5 + with: + go-version: 1.21.3 + - name: Clippy check shell: bash env: @@ -83,6 +91,11 @@ jobs: - name: Checkout repository uses: actions/checkout@v3 + - name: Checkout wireguard-go submodule + run: | + git config --global --add safe.directory '*' + git submodule update --init --depth=1 wireguard-go-rs + - name: Clippy check env: RUSTFLAGS: --deny warnings diff --git a/.github/workflows/daemon.yml b/.github/workflows/daemon.yml index 1d9c1d90a8e9..24060bfe2f68 100644 --- a/.github/workflows/daemon.yml +++ b/.github/workflows/daemon.yml @@ -68,11 +68,11 @@ jobs: - name: Checkout repository uses: actions/checkout@v3 - - name: Checkout binaries submodule + - name: Checkout submodules run: | git config --global --add safe.directory '*' git submodule update --init --depth=1 dist-assets/binaries - + git submodule update --init --recursive --depth=1 wireguard-go-rs # The container image already has rustup and Rust, but only the stable toolchain - name: Install Rust toolchain run: rustup default ${{ matrix.rust }} @@ -86,6 +86,11 @@ jobs: - name: Checkout repository uses: actions/checkout@v2 + - name: Checkout wireguard-go submodule + run: | + git config --global --add safe.directory '*' + git submodule update --init --recursive --depth=1 wireguard-go-rs + - name: Install Protoc uses: arduino/setup-protoc@v3 with: diff --git a/.github/workflows/desktop-e2e.yml b/.github/workflows/desktop-e2e.yml index 31b4436da04b..7f106dcaf566 100644 --- a/.github/workflows/desktop-e2e.yml +++ b/.github/workflows/desktop-e2e.yml @@ -103,10 +103,11 @@ jobs: run: echo "HOME=/root" >> $GITHUB_ENV - name: Checkout repository uses: actions/checkout@v3 - - name: Checkout binaries submodule + - name: Checkout submodules run: | git config --global --add safe.directory '*' git submodule update --init --depth=1 dist-assets/binaries + git submodule update --init --recursive --depth=1 wireguard-go-rs - name: Build app env: USE_MOLD: false @@ -159,9 +160,9 @@ jobs: runs-on: windows-latest steps: - name: Checkout repository - uses: actions/checkout@v2 - - name: Checkout submodules - run: git submodule update --init --depth=1 + uses: actions/checkout@v4 + with: + submodules: true - name: Install Protoc uses: arduino/setup-protoc@v3 with: @@ -230,9 +231,9 @@ jobs: runs-on: [self-hosted, desktop-test, macOS] # app-test-macos-arm steps: - name: Checkout repository - uses: actions/checkout@v2 - - name: Checkout submodules - run: git submodule update --init --depth=1 + uses: actions/checkout@v4 + with: + submodules: true - name: Install Go uses: actions/setup-go@v3 with: diff --git a/.github/workflows/frontend.yml b/.github/workflows/frontend.yml index cb14e4397d28..92edc31599a5 100644 --- a/.github/workflows/frontend.yml +++ b/.github/workflows/frontend.yml @@ -19,6 +19,9 @@ jobs: - name: Checkout repository uses: actions/checkout@v3 + - name: Checkout wireguard-go submodule + run: git submodule update --init --depth=1 wireguard-go-rs + - name: Install Rust uses: actions-rs/toolchain@v1.0.6 with: diff --git a/.github/workflows/rust-supply-chain.yml b/.github/workflows/rust-supply-chain.yml index b679766c73c2..13c34a9842a1 100644 --- a/.github/workflows/rust-supply-chain.yml +++ b/.github/workflows/rust-supply-chain.yml @@ -16,6 +16,9 @@ jobs: - name: Checkout repository uses: actions/checkout@v3 + - name: Checkout wireguard-go submodule + run: git submodule update --init --depth=1 wireguard-go-rs + - name: Run cargo deny uses: EmbarkStudios/cargo-deny-action@v1 with: diff --git a/.github/workflows/rust-unused-dependencies.yml b/.github/workflows/rust-unused-dependencies.yml index 8e1a464e1226..f845b1928e2a 100644 --- a/.github/workflows/rust-unused-dependencies.yml +++ b/.github/workflows/rust-unused-dependencies.yml @@ -10,7 +10,7 @@ on: workflow_dispatch: env: # Pinning nightly just to avoid random breakage. It's fine to bump this at any time - RUST_NIGHTLY_TOOLCHAIN: nightly-2024-02-06 + RUST_NIGHTLY_TOOLCHAIN: nightly-2024-06-06 jobs: prepare-containers: runs-on: ubuntu-latest @@ -41,17 +41,18 @@ jobs: - name: Checkout repository uses: actions/checkout@v3 - - name: Checkout binaries submodule + - name: Checkout submodules run: | git config --global --add safe.directory '*' git submodule update --init --depth=1 dist-assets/binaries + git submodule update --init --recursive --depth=1 wireguard-go-rs - name: Install nightly Rust toolchain run: rustup default $RUST_NIGHTLY_TOOLCHAIN - uses: taiki-e/install-action@v2 with: - tool: cargo-udeps + tool: cargo-udeps@0.1.48 - name: Check for unused dependencies shell: bash @@ -72,6 +73,11 @@ jobs: - name: Checkout repository uses: actions/checkout@v3 + - name: Checkout wireguard-go submodule + run: | + git config --global --add safe.directory '*' + git submodule update --init --depth=1 wireguard-go-rs + - name: Install nightly Rust toolchain run: | rustup default $RUST_NIGHTLY_TOOLCHAIN @@ -79,7 +85,7 @@ jobs: - uses: taiki-e/install-action@v2 with: - tool: cargo-udeps + tool: cargo-udeps@0.1.48 - name: Check for unused dependencies run: cargo udeps --target aarch64-linux-android --package mullvad-jni @@ -87,12 +93,13 @@ jobs: cargo-udeps: strategy: matrix: - os: [macos-latest, windows-latest] + os: [windows-latest, macos-latest] runs-on: ${{ matrix.os }} - steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 + with: + submodules: true - name: Install Protoc uses: arduino/setup-protoc@v3 @@ -108,7 +115,13 @@ jobs: - uses: taiki-e/install-action@v2 with: - tool: cargo-udeps + tool: cargo-udeps@0.1.48 + + - name: Install Go + if: matrix.os == 'macos-latest' + uses: actions/setup-go@v3 + with: + go-version: 1.21.3 - name: Check for unused dependencies shell: bash diff --git a/.github/workflows/rustfmt.yml b/.github/workflows/rustfmt.yml index 456653dd38c7..2968efdf4971 100644 --- a/.github/workflows/rustfmt.yml +++ b/.github/workflows/rustfmt.yml @@ -14,6 +14,9 @@ jobs: - name: Checkout repository uses: actions/checkout@v3 + - name: Checkout wireguard-go submodule + run: git submodule update --init --depth=1 wireguard-go-rs + - name: Install nightly Rust uses: actions-rs/toolchain@v1.0.6 with: diff --git a/.github/workflows/unicode-check.yml b/.github/workflows/unicode-check.yml index db768f8cb323..d6f13d05cf73 100644 --- a/.github/workflows/unicode-check.yml +++ b/.github/workflows/unicode-check.yml @@ -9,7 +9,7 @@ jobs: uses: actions/checkout@v3 - name: Checkout submodules - run: git submodule update --init + run: git submodule update --init --depth=1 dist-assets/binaries wireguard-go-rs - name: Scan for code points run: ./ci/check-trojan-source.sh . From 49b93df817244cb672f6bcf0c5f190dd037d8f55 Mon Sep 17 00:00:00 2001 From: Markus Pettersson Date: Tue, 4 Jun 2024 17:02:18 +0200 Subject: [PATCH 11/16] Update build server scripts Fix the build server scripts for Linux and Android by checking out the wireguard-go submodule. --- ci/buildserver-build-android.sh | 1 + ci/buildserver-build.sh | 1 + 2 files changed, 2 insertions(+) diff --git a/ci/buildserver-build-android.sh b/ci/buildserver-build-android.sh index 62bf8b02898f..bada3011ee30 100755 --- a/ci/buildserver-build-android.sh +++ b/ci/buildserver-build-android.sh @@ -65,6 +65,7 @@ function checkout_ref { git reset --hard git checkout "$ref" git submodule update + git submodule update --init --recursive --depth=1 wireguard-go-rs || true git clean -df } diff --git a/ci/buildserver-build.sh b/ci/buildserver-build.sh index 9e8b1eb04f20..09941da9bfb2 100755 --- a/ci/buildserver-build.sh +++ b/ci/buildserver-build.sh @@ -158,6 +158,7 @@ function checkout_ref { git reset --hard git checkout "$ref" git submodule update + git submodule update --init --recursive --depth=1 wireguard-go-rs || true git clean -df } From 99654a441a17b14773989772d419824142b78bdc Mon Sep 17 00:00:00 2001 From: Sebastian Holmin Date: Thu, 20 Jun 2024 14:57:48 +0200 Subject: [PATCH 12/16] Add simple e2e test for connecting with DAITA Co-authored-by: Markus Pettersson --- .../src/relay_selector/query.rs | 10 ++++-- test/test-manager/src/tests/helpers.rs | 4 +-- test/test-manager/src/tests/tunnel.rs | 35 +++++++++++++++++++ 3 files changed, 45 insertions(+), 4 deletions(-) diff --git a/mullvad-relay-selector/src/relay_selector/query.rs b/mullvad-relay-selector/src/relay_selector/query.rs index f37013a5b2a3..1fd8b1c5b90c 100644 --- a/mullvad-relay-selector/src/relay_selector/query.rs +++ b/mullvad-relay-selector/src/relay_selector/query.rs @@ -33,8 +33,8 @@ use mullvad_types::{ constraints::Constraint, relay_constraints::{ BridgeConstraints, LocationConstraint, OpenVpnConstraints, Ownership, Providers, - RelayConstraints, SelectedObfuscation, TransportPort, Udp2TcpObfuscationSettings, - WireguardConstraints, + RelayConstraints, RelaySettings, SelectedObfuscation, TransportPort, + Udp2TcpObfuscationSettings, WireguardConstraints, }, Intersection, }; @@ -129,6 +129,12 @@ impl From for RelayConstraints { } } +impl From for RelaySettings { + fn from(value: RelayQuery) -> Self { + RelayConstraints::from(value).into() + } +} + /// A query for a relay with Wireguard-specific properties, such as `multihop` and [wireguard /// obfuscation][`SelectedObfuscation`]. /// diff --git a/test/test-manager/src/tests/helpers.rs b/test/test-manager/src/tests/helpers.rs index a6a2cae242d4..1ec0b38cb766 100644 --- a/test/test-manager/src/tests/helpers.rs +++ b/test/test-manager/src/tests/helpers.rs @@ -464,10 +464,10 @@ impl Drop for AbortOnDrop { pub async fn set_relay_settings( mullvad_client: &mut MullvadProxyClient, - relay_settings: RelaySettings, + relay_settings: impl Into, ) -> Result<(), Error> { mullvad_client - .set_relay_settings(relay_settings) + .set_relay_settings(relay_settings.into()) .await .map_err(|error| Error::Daemon(format!("Failed to set relay settings: {}", error))) } diff --git a/test/test-manager/src/tests/tunnel.rs b/test/test-manager/src/tests/tunnel.rs index 8c6482d48365..65f7a744c3c0 100644 --- a/test/test-manager/src/tests/tunnel.rs +++ b/test/test-manager/src/tests/tunnel.rs @@ -10,6 +10,8 @@ use crate::{ tests::helpers::{login_with_retries, ConnChecker}, }; +#[cfg(not(target_os = "macos"))] +use anyhow::Context; use anyhow::{bail, ensure}; use mullvad_management_interface::MullvadProxyClient; use mullvad_relay_selector::query::builder::RelayQueryBuilder; @@ -401,6 +403,39 @@ pub async fn test_wireguard_autoconnect( Ok(()) } +/// Test connecting to a WireGuard relay using DAITA. +/// +/// # Limitations +/// +/// The test does not analyze any traffic, nor verify that DAITA is in use. +#[cfg(not(target_os = "macos"))] +#[test_function(target_os = "linux", target_os = "windows")] +pub async fn test_daita( + _: TestContext, + rpc: ServiceClient, + mut mullvad_client: MullvadProxyClient, +) -> anyhow::Result<()> { + log::info!("Connecting to relay with DAITA"); + + set_relay_settings( + &mut mullvad_client, + RelayQueryBuilder::new().wireguard().build(), + ) + .await?; + + mullvad_client + .set_daita_settings(wireguard::DaitaSettings { enabled: true }) + .await + .context("Failed to enable daita")?; + + connect_and_wait(&mut mullvad_client).await?; + + log::info!("Check that the connection works"); + let _ = helpers::geoip_lookup_with_retries(&rpc).await?; + + Ok(()) +} + /// Test whether the daemon automatically connects on reboot when using /// OpenVPN. /// From 357ada38c2e41f6ecd06877ed564453fa530d4ae Mon Sep 17 00:00:00 2001 From: Markus Pettersson Date: Fri, 19 Apr 2024 15:27:35 +0200 Subject: [PATCH 13/16] Add Daita toggle in GUI on Linux --- gui/src/renderer/components/WireguardSettings.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gui/src/renderer/components/WireguardSettings.tsx b/gui/src/renderer/components/WireguardSettings.tsx index f926fe9f4c92..e4cbb17942e6 100644 --- a/gui/src/renderer/components/WireguardSettings.tsx +++ b/gui/src/renderer/components/WireguardSettings.tsx @@ -99,7 +99,7 @@ export default function WireguardSettings() { - {window.env.platform === 'win32' && ( + {(window.env.platform === 'linux' || window.env.platform === 'win32') && ( From 853c26058b6e051bbc2d32cdea9e22750ec6f63f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20L=C3=B6nnhager?= Date: Mon, 24 Jun 2024 13:39:30 +0200 Subject: [PATCH 14/16] Add DAITA for Linux to features table --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8788e3c8592b..7dca116fc650 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ the current state of the latest code in git, not necessarily any existing releas | OpenVPN | ✓ | ✓ | ✓ | | | | WireGuard | ✓ | ✓ | ✓ | ✓ | ✓ | | Quantum-resistant tunnels | ✓ | ✓ | ✓ | ✓ | | -| [DAITA] | ✓ | | | | | +| [DAITA] | ✓ | ✓ | | | | | WireGuard multihop | ✓ | ✓ | ✓ | | | | WireGuard over TCP | ✓ | ✓ | ✓ | ✓ | | | OpenVPN over Shadowsocks | ✓ | ✓ | ✓ | | | From 3961cc5ee4a3c0c02a546474d02b2106b4354a1f Mon Sep 17 00:00:00 2001 From: Markus Pettersson Date: Wed, 26 Jun 2024 08:53:30 +0200 Subject: [PATCH 15/16] Update changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index de61fc74a30d..a5380ef05680 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,9 @@ Line wrap the file at 100 chars. Th #### macOS - Add support for split tunneling (beta). +#### Linux +- Add DAITA (Defence against AI-guided Traffic Analysis) setting. + ### Changed - Update Electron from 28.1.3 to 30.0.4. From cd55664dd71751ffcf146183824a7a6e6aaea657 Mon Sep 17 00:00:00 2001 From: Markus Pettersson Date: Fri, 17 May 2024 16:33:40 +0200 Subject: [PATCH 16/16] Remove `--no-docker` flag in `build-apk.sh` Align `build-apk.sh` with `build.sh` --- android/docker/Dockerfile | 2 +- android/docs/BuildInstructions.macos.md | 2 +- build-apk.sh | 6 ------ building/containerized-build.sh | 2 +- 4 files changed, 3 insertions(+), 9 deletions(-) diff --git a/android/docker/Dockerfile b/android/docker/Dockerfile index 1ed9a1ccdc6c..d6b2afbfeafb 100644 --- a/android/docker/Dockerfile +++ b/android/docker/Dockerfile @@ -8,7 +8,7 @@ # -v $GRADLE_CACHE_VOLUME_NAME:/root/.gradle:Z \ # -v $ANDROID_CREDENTIALS_DIR:/build/android/credentials:Z \ # -v /path/to/repository_root:/build:Z \ -# mullvadvpn-app-build-android ./build-apk.sh --dev-build --no-docker +# mullvadvpn-app-build-android ./build-apk.sh --dev-build # # See the base image Dockerfile in the repository root (../../Dockerfile) # for more information. diff --git a/android/docs/BuildInstructions.macos.md b/android/docs/BuildInstructions.macos.md index f0af51b5ef50..87d77fa78eb4 100644 --- a/android/docs/BuildInstructions.macos.md +++ b/android/docs/BuildInstructions.macos.md @@ -65,7 +65,7 @@ export CARGO_TARGET_X86_64_LINUX_ANDROID_LINKER="$NDK_TOOLCHAIN_DIR/x86_64-linux Run the build script in the root of the project to assemble all the native libraries and the app: ```bash -./build-apk.sh --dev-build --no-docker +./build-apk.sh --dev-build ``` Once the build is complete you should receive a message looking similar to this: diff --git a/build-apk.sh b/build-apk.sh index 952fb8b5a6bf..517dd6f5d9e6 100755 --- a/build-apk.sh +++ b/build-apk.sh @@ -16,7 +16,6 @@ GRADLE_BUILD_TYPE="release" GRADLE_TASKS=(createOssProdReleaseDistApk createPlayProdReleaseDistApk) BUNDLE_TASKS=(createPlayProdReleaseDistBundle) CARGO_ARGS=( "--release" ) -EXTRA_WGGO_ARGS="" BUILD_BUNDLE="no" CARGO_TARGET_DIR=${CARGO_TARGET_DIR:-"target"} SKIP_STRIPPING=${SKIP_STRIPPING:-"no"} @@ -32,11 +31,8 @@ while [ -n "${1:-""}" ]; do GRADLE_BUILD_TYPE="fdroid" GRADLE_TASKS=(createOssProdFdroidDistApk) BUNDLE_TASKS=(createOssProdFdroidDistBundle) - EXTRA_WGGO_ARGS="--no-docker" elif [[ "${1:-""}" == "--app-bundle" ]]; then BUILD_BUNDLE="yes" - elif [[ "${1:-""}" == "--no-docker" ]]; then - EXTRA_WGGO_ARGS="--no-docker" elif [[ "${1:-""}" == "--skip-stripping" ]]; then SKIP_STRIPPING="yes" fi @@ -80,8 +76,6 @@ $GRADLE_CMD --console plain clean mkdir -p "app/build/extraJni" popd -./wireguard/build-wireguard-go.sh --android $EXTRA_WGGO_ARGS - for ARCHITECTURE in ${ARCHITECTURES:-aarch64 armv7 x86_64 i686}; do case "$ARCHITECTURE" in "x86_64") diff --git a/building/containerized-build.sh b/building/containerized-build.sh index dd8bf17fa881..86b0148a7237 100755 --- a/building/containerized-build.sh +++ b/building/containerized-build.sh @@ -18,7 +18,7 @@ case $platform in shift 1 ;; android) - build_command=("./build-apk.sh" "--no-docker") + build_command=("./build-apk.sh") shift 1 ;; *)