summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDavid Lönnhager <david.l@mullvad.net>2025-01-10 11:08:40 +0100
committerDavid Lönnhager <david.l@mullvad.net>2025-01-24 17:34:45 +0100
commit696c60680a7e4a2c4c6f4778d2d63c135e4469d8 (patch)
tree97a7de7bec4fa7fe6a60b80d5a2f5c0e20449291
parentff88a777e7c73dd1b6ea995df517d37bb26cc7e0 (diff)
downloadmullvadvpn-696c60680a7e4a2c4c6f4778d2d63c135e4469d8.tar.xz
mullvadvpn-696c60680a7e4a2c4c6f4778d2d63c135e4469d8.zip
Add Windows support to libwg
-rw-r--r--wireguard-go-rs/libwg/README.md3
-rw-r--r--wireguard-go-rs/libwg/libwg_windows.go138
2 files changed, 141 insertions, 0 deletions
diff --git a/wireguard-go-rs/libwg/README.md b/wireguard-go-rs/libwg/README.md
index 39ad48e3e0..e5b96928f7 100644
--- a/wireguard-go-rs/libwg/README.md
+++ b/wireguard-go-rs/libwg/README.md
@@ -7,6 +7,7 @@ It currently offers support for the following platforms:
- Linux
- macOS
- Android
+- Windows
# Organization
@@ -16,6 +17,8 @@ It currently offers support for the following platforms:
`libwg_android.go` has code specifically for Android.
+`libwg_windows.go` has code specifically for Windows.
+
# Usage
Call `wgTurnOn` to create and activate a tunnel. The prototype is different on different platforms, see the code for details.
diff --git a/wireguard-go-rs/libwg/libwg_windows.go b/wireguard-go-rs/libwg/libwg_windows.go
new file mode 100644
index 0000000000..f4ad7faa6d
--- /dev/null
+++ b/wireguard-go-rs/libwg/libwg_windows.go
@@ -0,0 +1,138 @@
+/* SPDX-License-Identifier: Apache-2.0
+ *
+ * Copyright (C) 2017-2019 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ * Copyright (C) 2024 Mullvad VPN AB. All Rights Reserved.
+ */
+
+package main
+
+// #include <stdlib.h>
+// #include <stdint.h>
+import "C"
+
+import (
+ "bufio"
+ "strings"
+ "unsafe"
+
+ "golang.org/x/sys/windows"
+
+ "golang.zx2c4.com/wireguard/conn"
+ "golang.zx2c4.com/wireguard/device"
+ "golang.zx2c4.com/wireguard/tun"
+
+ "github.com/mullvad/mullvadvpn-app/wireguard/libwg/logging"
+ "github.com/mullvad/mullvadvpn-app/wireguard/libwg/tunnelcontainer"
+)
+
+// 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 = C.uint64_t
+
+//export wgTurnOn
+func wgTurnOn(cIfaceName *C.char, cIfaceNameOut **C.char, cLuidOut *C.uint64_t, mtu C.uint16_t, cSettings *C.char, logSink LogSink, logContext LogContext) C.int32_t {
+ logger := logging.NewLogger(logSink, logging.LogContext(logContext))
+ if cIfaceNameOut != nil {
+ *cIfaceNameOut = nil
+ }
+
+ if cIfaceName == nil {
+ logger.Errorf("cIfaceName is null\n")
+ return ERROR_GENERAL_FAILURE
+ }
+
+ if cSettings == nil {
+ logger.Errorf("cSettings is null\n")
+ return ERROR_GENERAL_FAILURE
+ }
+
+ settings := C.GoString(cSettings)
+ ifaceName := C.GoString(cIfaceName)
+
+ // {AFE43773-E1F8-4EBB-8536-576AB86AFE9A}
+ networkId := windows.GUID{
+ Data1: 0xafe43773,
+ Data2: 0xe1f8,
+ Data3: 0x4ebb,
+ Data4: [8]byte{0x85, 0x36, 0x57, 0x6a, 0xb8, 0x6a, 0xfe, 0x9a},
+ }
+
+ tun.WintunTunnelType = "Mullvad"
+
+ wintun, err := tun.CreateTUNWithRequestedGUID(ifaceName, &networkId, mtu)
+ if err != nil {
+ logger.Errorf("Failed to create tunnel\n")
+ logger.Errorf("%s\n", err)
+ return ERROR_INTERMITTENT_FAILURE
+ }
+
+ nativeTun := wintun.(*tun.NativeTun)
+
+ actualInterfaceName, err := nativeTun.Name()
+ if err != nil {
+ nativeTun.Close()
+ logger.Errorf("Failed to determine name of wintun adapter\n")
+ return ERROR_GENERAL_FAILURE
+ }
+ if actualInterfaceName != ifaceName {
+ // WireGuard picked a different name for the adapter than the one we expected.
+ // This indicates there is already an adapter with the name we intended to use.
+ logger.Verbosef("Failed to create adapter with specific name\n")
+ }
+
+ device := device.NewDevice(wintun, conn.NewDefaultBind(), logger)
+
+ setError := device.IpcSetOperation(bufio.NewReader(strings.NewReader(settings)))
+ if setError != nil {
+ logger.Errorf("Failed to set device configuration\n")
+ logger.Errorf("%s\n", setError)
+ device.Close()
+ return ERROR_GENERAL_FAILURE
+ }
+
+ device.Up()
+
+ 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
+ }
+
+ if cIfaceNameOut != nil {
+ *cIfaceNameOut = C.CString(actualInterfaceName)
+ }
+ if cLuidOut != nil {
+ *cLuidOut = nativeTun.LUID()
+ }
+
+ return C.int32_t(handle)
+}
+
+//export wgRebindTunnelSocket
+func wgRebindTunnelSocket(family uint16, interfaceIndex uint32) {
+ tunnels.ForEach(func(tunnel tunnelcontainer.Context) {
+ blackhole := (interfaceIndex == 0)
+ bind := tunnel.Device.Bind().(conn.BindSocketToInterface)
+
+ if family == windows.AF_INET {
+ tunnel.Logger.Verbosef("Binding v4 socket to interface %d (blackhole=%v)\n", interfaceIndex, blackhole)
+ err := bind.BindSocketToInterface4(interfaceIndex, blackhole)
+ if err != nil {
+ tunnel.Logger.Verbosef("%s\n", err)
+ }
+ } else if family == windows.AF_INET6 {
+ tunnel.Logger.Verbosef("Binding v6 socket to interface %d (blackhole=%v)\n", interfaceIndex, blackhole)
+ err := bind.BindSocketToInterface6(interfaceIndex, blackhole)
+ if err != nil {
+ tunnel.Logger.Verbosef("%s\n", err)
+ }
+ }
+ })
+}