diff options
| author | Jonatan Rhodin <jonatan.rhodin@mullvad.net> | 2024-10-09 13:43:50 +0200 |
|---|---|---|
| committer | Markus Pettersson <markus.pettersson@mullvad.net> | 2024-11-22 17:42:38 +0100 |
| commit | 848ddea38dcd93a1cf1575f87f8fada316dff590 (patch) | |
| tree | 855e9ef69fbd3f1e9e3943cb2c15502190f76cca | |
| parent | 734abdd7fcafa3d1b4553460de1b2199d4043b51 (diff) | |
| download | mullvadvpn-848ddea38dcd93a1cf1575f87f8fada316dff590.tar.xz mullvadvpn-848ddea38dcd93a1cf1575f87f8fada316dff590.zip | |
Add initial multihop config on Android
- Add some minor wireguard go changes
| -rw-r--r-- | android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/SelectLocationScreen.kt | 2 | ||||
| -rw-r--r-- | talpid-wireguard/src/wireguard_go/mod.rs | 50 | ||||
| -rw-r--r-- | wireguard-go-rs/libwg/libwg.go | 3 | ||||
| -rw-r--r-- | wireguard-go-rs/libwg/libwg_android.go | 74 | ||||
| -rw-r--r-- | wireguard-go-rs/libwg/tunnelcontainer/tunnelcontainer.go | 1 | ||||
| -rw-r--r-- | wireguard-go-rs/src/lib.rs | 30 |
6 files changed, 136 insertions, 24 deletions
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/SelectLocationScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/SelectLocationScreen.kt index c36f10212e..c63da68471 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/SelectLocationScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/SelectLocationScreen.kt @@ -66,6 +66,7 @@ import com.ramcosta.composedestinations.result.NavResult import com.ramcosta.composedestinations.result.ResultBackNavigator import com.ramcosta.composedestinations.result.ResultRecipient import com.ramcosta.composedestinations.spec.DestinationSpec +import java.io.FileDescriptor import kotlinx.coroutines.launch import net.mullvad.mullvadvpn.R import net.mullvad.mullvadvpn.compose.cell.FilterRow @@ -753,6 +754,7 @@ private fun EditCustomListBottomSheet( onDeleteCustomList: (item: RelayItem.CustomList) -> Unit, closeBottomSheet: (animate: Boolean) -> Unit, ) { + FileDescriptor.out. MullvadModalBottomSheet( backgroundColor = backgroundColor, onBackgroundColor = onBackgroundColor, diff --git a/talpid-wireguard/src/wireguard_go/mod.rs b/talpid-wireguard/src/wireguard_go/mod.rs index 25ebb45a38..5caab06d09 100644 --- a/talpid-wireguard/src/wireguard_go/mod.rs +++ b/talpid-wireguard/src/wireguard_go/mod.rs @@ -5,6 +5,7 @@ use once_cell::sync::OnceCell; use std::{ffi::CString, fs, path::PathBuf}; use std::{ future::Future, + net::IpAddr, os::unix::io::{AsRawFd, RawFd}, path::Path, pin::Pin, @@ -59,6 +60,34 @@ pub struct WgGoTunnel { config: Config, } +// TODO: move into impl of Config +fn exit_config(multihop_config: &Config) -> Option<Config> { + let mut exit_config = multihop_config.clone(); + exit_config.entry_peer = multihop_config.exit_peer.clone()?; + Some(exit_config) +} + +// TODO: move into impl of Config +fn entry_config(multihop_config: &Config) -> Config { + let mut entry_config = multihop_config.clone(); + entry_config.exit_peer = None; + entry_config +} + +// TODO: move into impl of Config +fn private_ip(config: &Config) -> CString { + if let Some(ip) = config + .tunnel + .addresses + .iter() + .find(|addr| matches!(addr, IpAddr::V4(_))) { + CString::new(ip.to_string()).unwrap() + } + else { + CString::default() + } +} + impl WgGoTunnel { pub fn start_tunnel( config: &Config, @@ -81,6 +110,27 @@ impl WgGoTunnel { #[cfg(not(target_os = "android"))] let mtu = config.mtu as isize; + + let entry_config = entry_config(config); + let exit_config = exit_config(config); + + #[cfg(target_os = "android")] + if let Some(exit_config) = exit_config { + let entry_config_str = entry_config.to_userspace_format(); + let exit_config_str = exit_config.to_userspace_format(); + let private_ip = private_ip(config); + + let handle = wireguard_go_rs::Tunnel::turn_on_multihop( + exit_config_str, + entry_config_str, + private_ip, + tunnel_fd, + Some(logging::wg_go_logging_callback), + logging_context.0, + ) + .map_err(|e| TunnelError::FatalStartWireguardError(Box::new(e)))?; + } + let handle = wireguard_go_rs::Tunnel::turn_on( #[cfg(not(target_os = "android"))] mtu, diff --git a/wireguard-go-rs/libwg/libwg.go b/wireguard-go-rs/libwg/libwg.go index 6cfbd0ba55..5dcc9141b2 100644 --- a/wireguard-go-rs/libwg/libwg.go +++ b/wireguard-go-rs/libwg/libwg.go @@ -65,6 +65,9 @@ func wgTurnOff(tunnelHandle int32) { return } tunnel.Device.Close() + if tunnel.EntryDevice != nil { + tunnel.EntryDevice.Close() + } } // Calling twice convinces the GC to release NOW. runtime.GC() diff --git a/wireguard-go-rs/libwg/libwg_android.go b/wireguard-go-rs/libwg/libwg_android.go index ce0b5e10a9..98599ba701 100644 --- a/wireguard-go-rs/libwg/libwg_android.go +++ b/wireguard-go-rs/libwg/libwg_android.go @@ -13,7 +13,6 @@ import ( "bufio" "errors" "net/netip" - "os" "strings" "unsafe" @@ -88,19 +87,27 @@ func wgTurnOn(cSettings *C.char, fd int, logSink LogSink, logContext LogContext) return C.int32_t(handle) } -func wgTurnOnMultihopAgain(mtu int, exitSettings *C.char, entrySettings *C.char, privateIp *C.char, fd int32, logSink LogSink, logContext LogContext) C.int32_t { +//export wgTurnOnMultihop +func wgTurnOnMultihop(cExitSettings *C.char, cEntrySettings *C.char, privateIp *C.char, fd int, logSink LogSink, logContext LogContext) C.int32_t { logger := logging.NewLogger(logSink, logging.LogContext(logContext)) - if exitSettings == nil { - logger.Errorf("exitSettings is null\n") + if cExitSettings == nil { + logger.Errorf("cExitSettings is null\n") return ERROR_INVALID_ARGUMENT } - exitSettings := goStringFixed(exitSettings) + exitSettings := goStringFixed(cExitSettings) - if entrySettings == nil { - logger.Errorf("exitSettings is null\n") + if cEntrySettings == nil { + logger.Errorf("cEntrySettings is null\n") + return ERROR_INVALID_ARGUMENT + } + entrySettings := goStringFixed(cEntrySettings) + + exitEndpoint := parseEndpointFromConfig(exitSettings) + + if exitEndpoint == nil { + logger.Errorf("exitEndpoint is null\n") return ERROR_INVALID_ARGUMENT } - entrySettings := goStringFixed(entrySettings) // Set up a two tunnel devices: One 'fake' device for the exit relay and one 'real' device for the entry relay @@ -114,39 +121,70 @@ func wgTurnOnMultihopAgain(mtu int, exitSettings *C.char, entrySettings *C.char, return ERROR_GENERAL_FAILURE } - device := device.NewDevice(tunDevice, conn.NewStdNetBind(), logger) + ip, err := netip.ParseAddr(goStringFixed(privateIp)) + if err != nil { + logger.Errorf("%s\n", err) + tunDevice.Close() + return ERROR_INVALID_ARGUMENT + } - setErr := device.IpcSetOperation(bufio.NewReader(strings.NewReader(settings))) + mtu, err := tunDevice.MTU() + if err != nil { + logger.Errorf("%s\n", err) + tunDevice.Close() + return ERROR_GENERAL_FAILURE + } + + singleTunMtu := mtu - 80 //Internet mtu - Wireguard header size - ipv4 UDP header + singletun := multihoptun.NewMultihopTun(ip, exitEndpoint.Addr(), exitEndpoint.Port(), singleTunMtu) + + entryDevice := device.NewDevice(tunDevice, singletun.Binder(), logger) + + setErr := entryDevice.IpcSetOperation(bufio.NewReader(strings.NewReader(entrySettings))) if setErr != nil { logger.Errorf("%s\n", setErr) - device.Close() + entryDevice.Close() return ERROR_INTERMITTENT_FAILURE } - device.DisableSomeRoamingForBrokenMobileSemantics() - device.Up() + entryDevice.DisableSomeRoamingForBrokenMobileSemantics() + + exitDevice := device.NewDevice(&singletun, conn.NewStdNetBind(), logger) + + setErr = exitDevice.IpcSetOperation(bufio.NewReader(strings.NewReader(exitSettings))) + if setErr != nil { + logger.Errorf("%s\n", setErr) + exitDevice.Close() + return ERROR_INTERMITTENT_FAILURE + } + + exitDevice.DisableSomeRoamingForBrokenMobileSemantics() + + exitDevice.Up() + entryDevice.Up() // Create the stuff that needs context := tunnelcontainer.Context{ - Device: device, + Device: exitDevice, + EntryDevice: entryDevice, Logger: logger, } handle, err := tunnels.Insert(context) if err != nil { logger.Errorf("%s\n", err) - device.Close() + entryDevice.Close() + exitDevice.Close() return ERROR_GENERAL_FAILURE } - // TODO return C.int32_t(handle) } //export wgTurnOnMultihop -func wgTurnOnMultihop(mtu int, exitSettings *C.char, entrySettings *C.char, privateIp *C.char, fd int32, logSink LogSink, logContext LogContext) C.int32_t { +/*func wgTurnOnMultihop(mtu int, exitSettings *C.char, entrySettings *C.char, privateIp *C.char, fd int32, logSink LogSink, logContext LogContext) C.int32_t { logger := logging.NewLogger(logSink, logging.LogContext(logContext)) if exitSettings == nil { @@ -219,7 +257,7 @@ func wgTurnOnMultihop(mtu int, exitSettings *C.char, entrySettings *C.char, priv } return C.int32_t(handle) -} +}*/ func addTunnelFromDevice(exitDev *device.Device, entryDev *device.Device, exitSettings string, entrySettings string, logger *device.Logger) (*tunnelHandle, error) { err := bringUpDevice(exitDev, exitSettings, logger) diff --git a/wireguard-go-rs/libwg/tunnelcontainer/tunnelcontainer.go b/wireguard-go-rs/libwg/tunnelcontainer/tunnelcontainer.go index 91291dcf4b..bd4003b409 100644 --- a/wireguard-go-rs/libwg/tunnelcontainer/tunnelcontainer.go +++ b/wireguard-go-rs/libwg/tunnelcontainer/tunnelcontainer.go @@ -18,6 +18,7 @@ type Context struct { Device *device.Device Uapi net.Listener Logger *device.Logger + EntryDevice *device.Device } type Container struct { diff --git a/wireguard-go-rs/src/lib.rs b/wireguard-go-rs/src/lib.rs index 2a994a1443..321189ff5c 100644 --- a/wireguard-go-rs/src/lib.rs +++ b/wireguard-go-rs/src/lib.rs @@ -112,19 +112,21 @@ impl Tunnel { /// 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`. + #[cfg(target_os = "android")] pub fn turn_on_multihop( - #[cfg(not(target_os = "android"))] mtu: isize, - settings: &CStr, + exit_settings: &CStr, + entry_settings: &CStr, + private_ip: &CStr, device: Fd, logging_callback: Option<LoggingCallback>, logging_context: LoggingContext, ) -> Result<Self, Error> { // 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(), + ffi::wgTurnOnMultihop( + exit_settings.as_ptr(), + entry_settings.as_ptr(), + private_ip.as_ptr(), device, logging_callback, logging_context, @@ -287,6 +289,22 @@ mod ffi { logging_context: LoggingContext, ) -> i32; + /// 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 wgTurnOnMultihop( + exit_settings: *const c_char, + entry_settings: *const c_char, + private_ip: *const c_char, + fd: Fd, + logging_callback: Option<LoggingCallback>, + logging_context: LoggingContext, + ) -> i32; + + /// Pass a handle that was created by wgTurnOn to stop a wireguard tunnel. /// /// Negative return values signify errors. |
