diff options
| author | Markus Pettersson <markus.pettersson@mullvad.net> | 2024-10-07 14:23:01 +0200 |
|---|---|---|
| committer | Markus Pettersson <markus.pettersson@mullvad.net> | 2024-11-22 17:42:38 +0100 |
| commit | 734abdd7fcafa3d1b4553460de1b2199d4043b51 (patch) | |
| tree | 2ed666b1fa479431d9a38a210700c5e03aabe425 | |
| parent | 6b8c33669ee0622f0c6e42a52d8d380cc428824e (diff) | |
| download | mullvadvpn-734abdd7fcafa3d1b4553460de1b2199d4043b51.tar.xz mullvadvpn-734abdd7fcafa3d1b4553460de1b2199d4043b51.zip | |
Add `wgTurnOnMultihop`
| -rw-r--r-- | wireguard-go-rs/libwg/libwg_android.go | 196 | ||||
| -rw-r--r-- | wireguard-go-rs/src/lib.rs | 30 |
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. |
