summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorMarkus Pettersson <markus.pettersson@mullvad.net>2024-10-07 14:23:01 +0200
committerMarkus Pettersson <markus.pettersson@mullvad.net>2024-11-22 17:42:38 +0100
commit734abdd7fcafa3d1b4553460de1b2199d4043b51 (patch)
tree2ed666b1fa479431d9a38a210700c5e03aabe425
parent6b8c33669ee0622f0c6e42a52d8d380cc428824e (diff)
downloadmullvadvpn-734abdd7fcafa3d1b4553460de1b2199d4043b51.tar.xz
mullvadvpn-734abdd7fcafa3d1b4553460de1b2199d4043b51.zip
Add `wgTurnOnMultihop`
-rw-r--r--wireguard-go-rs/libwg/libwg_android.go196
-rw-r--r--wireguard-go-rs/src/lib.rs30
2 files changed, 226 insertions, 0 deletions
diff --git a/wireguard-go-rs/libwg/libwg_android.go b/wireguard-go-rs/libwg/libwg_android.go
index d623b7711d..ce0b5e10a9 100644
--- a/wireguard-go-rs/libwg/libwg_android.go
+++ b/wireguard-go-rs/libwg/libwg_android.go
@@ -11,6 +11,9 @@ import "C"
import (
"bufio"
+ "errors"
+ "net/netip"
+ "os"
"strings"
"unsafe"
@@ -19,6 +22,7 @@ import (
"golang.zx2c4.com/wireguard/conn"
"golang.zx2c4.com/wireguard/device"
"golang.zx2c4.com/wireguard/tun"
+ "golang.zx2c4.com/wireguard/tun/multihoptun"
"github.com/mullvad/mullvadvpn-app/wireguard/libwg/logging"
"github.com/mullvad/mullvadvpn-app/wireguard/libwg/tunnelcontainer"
@@ -29,6 +33,13 @@ import (
type LogSink = unsafe.Pointer
type LogContext = C.uint64_t
+// TODO: Document
+type tunnelHandle struct {
+ exit *device.Device
+ entry *device.Device
+ logger *device.Logger
+}
+
//export wgTurnOn
func wgTurnOn(cSettings *C.char, fd int, logSink LogSink, logContext LogContext) C.int32_t {
logger := logging.NewLogger(logSink, logging.LogContext(logContext))
@@ -77,6 +88,191 @@ 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 {
+ logger := logging.NewLogger(logSink, logging.LogContext(logContext))
+ if exitSettings == nil {
+ logger.Errorf("exitSettings is null\n")
+ return ERROR_INVALID_ARGUMENT
+ }
+ exitSettings := goStringFixed(exitSettings)
+
+ if entrySettings == nil {
+ logger.Errorf("exitSettings 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
+
+ tunDevice, _, err := tun.CreateUnmonitoredTUNFromFD(fd)
+ if err != nil {
+ logger.Errorf("%s\n", err)
+ unix.Close(fd)
+ if err.Error() == "bad file descriptor" {
+ return ERROR_INTERMITTENT_FAILURE
+ }
+ return ERROR_GENERAL_FAILURE
+ }
+
+ device := device.NewDevice(tunDevice, conn.NewStdNetBind(), logger)
+
+ setErr := device.IpcSetOperation(bufio.NewReader(strings.NewReader(settings)))
+ if setErr != nil {
+ logger.Errorf("%s\n", setErr)
+ device.Close()
+ return ERROR_INTERMITTENT_FAILURE
+ }
+
+ device.DisableSomeRoamingForBrokenMobileSemantics()
+ device.Up()
+
+ // Create the stuff that needs
+
+ context := tunnelcontainer.Context{
+ Device: device,
+ Logger: logger,
+ }
+
+ handle, err := tunnels.Insert(context)
+ if err != nil {
+ logger.Errorf("%s\n", err)
+ device.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 {
+ logger := logging.NewLogger(logSink, logging.LogContext(logContext))
+
+ if exitSettings == nil {
+ logger.Errorf("exitSettings is null\n")
+ return ERROR_INVALID_ARGUMENT
+ }
+
+ if entrySettings == nil {
+ logger.Errorf("exitSettings is null\n")
+ return ERROR_INVALID_ARGUMENT
+ }
+
+ // OLD
+ // settings := goStringFixed(cSettings)
+ // NEW
+ exitConfigString := goStringFixed(exitSettings)
+ entryConfigString := goStringFixed(entrySettings)
+ exitEndpoint := parseEndpointFromConfig(exitConfigString)
+ if exitEndpoint == nil {
+ return -10 // TODO: Define this error, previously '' errNoEndpointInConfig
+ }
+
+ ip, err := netip.ParseAddr(goStringFixed(privateIp))
+ if err != nil {
+ logger.Errorf("Failed to parse private IP: %v", err)
+ return -10 // TODO: Define this error, previously '' errBadIPString
+ }
+
+ // OLD
+ // device := device.NewDevice(tunDevice, conn.NewStdNetBind(), logger)
+ // NEW
+ // ip: First hop
+ // exitEndpoint: Last hop
+ // TODO: Is this mtu the correct one?
+ singleTunMtu := mtu - 80
+ singletun := multihoptun.NewMultihopTun(ip, exitEndpoint.Addr(), exitEndpoint.Port(), singleTunMtu)
+ entryDev := device.NewDevice(&singletun, conn.NewStdNetBind(), logger)
+
+ tunDevice, _, err := tun.CreateUnmonitoredTUNFromFD(fd)
+ if err != nil {
+ logger.Errorf("%s\n", err)
+ unix.Close(fd)
+ if err.Error() == "bad file descriptor" {
+ return ERROR_INTERMITTENT_FAILURE
+ }
+ return ERROR_GENERAL_FAILURE
+ }
+ exitDev := device.NewDevice(tunDevice, singletun.Binder(), logger)
+
+ setErr := device.IpcSetOperation(bufio.NewReader(strings.NewReader(settings)))
+ if setErr != nil {
+ logger.Errorf("%s\n", setErr)
+ device.Close()
+ return ERROR_INTERMITTENT_FAILURE
+ }
+
+ device.DisableSomeRoamingForBrokenMobileSemantics()
+ device.Up()
+
+ // context := tunnelcontainer.Context{
+ // Device: device,
+ // Logger: logger,
+ // }
+
+ handle, err := addTunnelFromDevice(exitDev, entryDev, exitSettings, entrySettings, logger)
+ if err != nil {
+ logger.Errorf("%s\n", err)
+ device.Close()
+ return ERROR_GENERAL_FAILURE
+ }
+
+ 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)
+ if err != nil {
+ return nil, errors.New("Could not bring up exit device") // errBadWgConfig
+ }
+
+ if entryDev != nil {
+ err = bringUpDevice(entryDev, entrySettings, logger)
+ if err != nil {
+ exitDev.Close()
+ return nil, errors.New("Could not bring up entry device")
+ }
+ }
+
+ return &tunnelHandle{exitDev, entryDev, logger}, nil
+}
+
+func bringUpDevice(dev *device.Device, settings string, logger *device.Logger) error {
+ err := dev.IpcSet(settings)
+ if err != nil {
+ logger.Errorf("Unable to set IPC settings: %v", err)
+ dev.Close()
+ return err
+ }
+
+ dev.Up()
+ logger.Verbosef("Device started")
+ return nil
+}
+
+// Parse a wireguard config and return the first endpoint address it finds and
+// parses successfully.gi b
+func parseEndpointFromConfig(config string) *netip.AddrPort {
+ scanner := bufio.NewScanner(strings.NewReader(config))
+ for scanner.Scan() {
+ line := scanner.Text()
+ key, value, ok := strings.Cut(line, "=")
+ if !ok {
+ continue
+ }
+
+ if key == "endpoint" {
+ endpoint, err := netip.ParseAddrPort(value)
+ if err == nil {
+ return &endpoint
+ }
+ }
+
+ }
+ return nil
+}
+
//export wgGetSocketV4
func wgGetSocketV4(tunnelHandle int32) C.int32_t {
tunnel, err := tunnels.Get(tunnelHandle)
diff --git a/wireguard-go-rs/src/lib.rs b/wireguard-go-rs/src/lib.rs
index 0ba47a947d..2a994a1443 100644
--- a/wireguard-go-rs/src/lib.rs
+++ b/wireguard-go-rs/src/lib.rs
@@ -6,6 +6,8 @@
//!
//! The [`Tunnel`] type provides a safe Rust wrapper around the C FFI.
+// TODO: Add a new function wgTurnOnMultihop for android.
+
#![cfg(unix)]
use core::{
@@ -105,6 +107,34 @@ impl Tunnel {
result_from_code(code)
}
+ /// TODO: Document
+ ///
+ /// 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_multihop(
+ #[cfg(not(target_os = "android"))] mtu: isize,
+ settings: &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(),
+ device,
+ logging_callback,
+ logging_context,
+ )
+ };
+
+ result_from_code(code)?;
+ Ok(Tunnel { handle: 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.