aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/tunnel/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tunnel/tools')
-rw-r--r--tunnel/tools/CMakeLists.txt44
m---------tunnel/tools/elf-cleaner0
-rw-r--r--tunnel/tools/libwg-go/.gitignore1
-rw-r--r--tunnel/tools/libwg-go/Makefile52
-rw-r--r--tunnel/tools/libwg-go/api-android.go227
-rw-r--r--tunnel/tools/libwg-go/go.mod14
-rw-r--r--tunnel/tools/libwg-go/go.sum13
-rw-r--r--tunnel/tools/libwg-go/goruntime-boottime-over-monotonic.diff171
-rw-r--r--tunnel/tools/libwg-go/jni.c71
-rw-r--r--tunnel/tools/ndk-compat/compat.c25
-rw-r--r--tunnel/tools/ndk-compat/compat.h10
m---------tunnel/tools/wireguard-tools0
12 files changed, 628 insertions, 0 deletions
diff --git a/tunnel/tools/CMakeLists.txt b/tunnel/tools/CMakeLists.txt
new file mode 100644
index 00000000..490a1755
--- /dev/null
+++ b/tunnel/tools/CMakeLists.txt
@@ -0,0 +1,44 @@
+# SPDX-License-Identifier: Apache-2.0
+#
+# Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
+
+cmake_minimum_required(VERSION 3.4.1)
+project("WireGuard")
+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
+add_link_options(LINKER:--build-id=none)
+add_compile_options(-Wall -Werror)
+
+add_executable(libwg-quick.so wireguard-tools/src/wg-quick/android.c ndk-compat/compat.c)
+target_compile_options(libwg-quick.so PUBLIC -std=gnu11 -include ${CMAKE_CURRENT_SOURCE_DIR}/ndk-compat/compat.h -DWG_PACKAGE_NAME=\"${ANDROID_PACKAGE_NAME}\")
+target_link_libraries(libwg-quick.so -ldl)
+
+file(GLOB WG_SOURCES wireguard-tools/src/*.c ndk-compat/compat.c)
+add_executable(libwg.so ${WG_SOURCES})
+target_include_directories(libwg.so PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/wireguard-tools/src/uapi/linux/" "${CMAKE_CURRENT_SOURCE_DIR}/wireguard-tools/src/")
+target_compile_options(libwg.so PUBLIC -std=gnu11 -include ${CMAKE_CURRENT_SOURCE_DIR}/ndk-compat/compat.h -DRUNSTATEDIR=\"/data/data/${ANDROID_PACKAGE_NAME}/cache\")
+
+add_custom_target(libwg-go.so WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/libwg-go" COMMENT "Building wireguard-go" VERBATIM COMMAND "${ANDROID_HOST_PREBUILTS}/bin/make"
+ ANDROID_ARCH_NAME=${ANDROID_ARCH_NAME}
+ ANDROID_PACKAGE_NAME=${ANDROID_PACKAGE_NAME}
+ GRADLE_USER_HOME=${GRADLE_USER_HOME}
+ CC=${CMAKE_C_COMPILER}
+ CFLAGS=${CMAKE_C_FLAGS}
+ LDFLAGS=${CMAKE_SHARED_LINKER_FLAGS}
+ SYSROOT=${CMAKE_SYSROOT}
+ TARGET=${CMAKE_C_COMPILER_TARGET}
+ DESTDIR=${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
+ BUILDDIR=${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/../generated-src
+)
+
+# Strip unwanted ELF sections to prevent DT_FLAGS_1 warnings on old Android versions
+file(GLOB ELF_CLEANER_SOURCES elf-cleaner/*.c elf-cleaner/*.cpp)
+add_custom_target(elf-cleaner COMMENT "Building elf-cleaner" VERBATIM COMMAND cc
+ -O2 -DPACKAGE_NAME="elf-cleaner" -DPACKAGE_VERSION="" -DCOPYRIGHT=""
+ -o "${CMAKE_CURRENT_BINARY_DIR}/elf-cleaner" ${ELF_CLEANER_SOURCES}
+)
+add_custom_command(TARGET libwg.so POST_BUILD VERBATIM COMMAND "${CMAKE_CURRENT_BINARY_DIR}/elf-cleaner"
+ --api-level "${ANDROID_NATIVE_API_LEVEL}" "$<TARGET_FILE:libwg.so>")
+add_dependencies(libwg.so elf-cleaner)
+add_custom_command(TARGET libwg-quick.so POST_BUILD VERBATIM COMMAND "${CMAKE_CURRENT_BINARY_DIR}/elf-cleaner"
+ --api-level "${ANDROID_NATIVE_API_LEVEL}" "$<TARGET_FILE:libwg-quick.so>")
+add_dependencies(libwg-quick.so elf-cleaner)
diff --git a/tunnel/tools/elf-cleaner b/tunnel/tools/elf-cleaner
new file mode 160000
+Subproject 7efc05090675ec6161b7def862728086a26c3b1
diff --git a/tunnel/tools/libwg-go/.gitignore b/tunnel/tools/libwg-go/.gitignore
new file mode 100644
index 00000000..d1638636
--- /dev/null
+++ b/tunnel/tools/libwg-go/.gitignore
@@ -0,0 +1 @@
+build/ \ No newline at end of file
diff --git a/tunnel/tools/libwg-go/Makefile b/tunnel/tools/libwg-go/Makefile
new file mode 100644
index 00000000..427bfdc3
--- /dev/null
+++ b/tunnel/tools/libwg-go/Makefile
@@ -0,0 +1,52 @@
+# SPDX-License-Identifier: Apache-2.0
+#
+# Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
+
+BUILDDIR ?= $(CURDIR)/build
+DESTDIR ?= $(CURDIR)/out
+
+NDK_GO_ARCH_MAP_x86 := 386
+NDK_GO_ARCH_MAP_x86_64 := amd64
+NDK_GO_ARCH_MAP_arm := arm
+NDK_GO_ARCH_MAP_arm64 := arm64
+NDK_GO_ARCH_MAP_mips := mipsx
+NDK_GO_ARCH_MAP_mips64 := mips64x
+
+comma := ,
+CLANG_FLAGS := --target=$(TARGET) --sysroot=$(SYSROOT)
+export CGO_CFLAGS := $(CLANG_FLAGS) $(subst -mthumb,-marm,$(CFLAGS))
+export CGO_LDFLAGS := $(CLANG_FLAGS) $(patsubst -Wl$(comma)--build-id=%,-Wl$(comma)--build-id=none,$(LDFLAGS)) -Wl,-soname=libwg-go.so
+export GOARCH := $(NDK_GO_ARCH_MAP_$(ANDROID_ARCH_NAME))
+export GOOS := android
+export CGO_ENABLED := 1
+
+GO_VERSION := 1.21.3
+GO_PLATFORM := $(shell uname -s | tr '[:upper:]' '[:lower:]')-$(NDK_GO_ARCH_MAP_$(shell uname -m))
+GO_TARBALL := go$(GO_VERSION).$(GO_PLATFORM).tar.gz
+GO_HASH_darwin-amd64 := 27014fc69e301d7588a169ca239b3cc609f0aa1abf38528bf0d20d3b259211eb
+GO_HASH_darwin-arm64 := 65302a7a9f7a4834932b3a7a14cb8be51beddda757b567a2f9e0cbd0d7b5a6ab
+GO_HASH_linux-amd64 := 1241381b2843fae5a9707eec1f8fb2ef94d827990582c7c7c32f5bdfbfd420c8
+
+default: $(DESTDIR)/libwg-go.so
+
+$(GRADLE_USER_HOME)/caches/golang/$(GO_TARBALL):
+ mkdir -p "$(dir $@)"
+ flock "$@.lock" -c ' \
+ [ -f "$@" ] && exit 0; \
+ curl -o "$@.tmp" "https://dl.google.com/go/$(GO_TARBALL)" && \
+ echo "$(GO_HASH_$(GO_PLATFORM)) $@.tmp" | sha256sum -c && \
+ mv "$@.tmp" "$@"'
+
+$(BUILDDIR)/go-$(GO_VERSION)/.prepared: $(GRADLE_USER_HOME)/caches/golang/$(GO_TARBALL)
+ mkdir -p "$(dir $@)"
+ flock "$@.lock" -c ' \
+ [ -f "$@" ] && exit 0; \
+ tar -C "$(dir $@)" --strip-components=1 -xzf "$^" && \
+ patch -p1 -f -N -r- -d "$(dir $@)" < goruntime-boottime-over-monotonic.diff && \
+ touch "$@"'
+
+$(DESTDIR)/libwg-go.so: export PATH := $(BUILDDIR)/go-$(GO_VERSION)/bin/:$(PATH)
+$(DESTDIR)/libwg-go.so: $(BUILDDIR)/go-$(GO_VERSION)/.prepared go.mod
+ go build -tags linux -ldflags="-X golang.zx2c4.com/wireguard/ipc.socketDirectory=/data/data/$(ANDROID_PACKAGE_NAME)/cache/wireguard -buildid=" -v -trimpath -buildvcs=false -o "$@" -buildmode c-shared
+
+.DELETE_ON_ERROR:
diff --git a/tunnel/tools/libwg-go/api-android.go b/tunnel/tools/libwg-go/api-android.go
new file mode 100644
index 00000000..d47c5d76
--- /dev/null
+++ b/tunnel/tools/libwg-go/api-android.go
@@ -0,0 +1,227 @@
+/* SPDX-License-Identifier: Apache-2.0
+ *
+ * Copyright © 2017-2022 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+package main
+
+// #cgo LDFLAGS: -llog
+// #include <android/log.h>
+import "C"
+
+import (
+ "fmt"
+ "math"
+ "net"
+ "os"
+ "os/signal"
+ "runtime"
+ "runtime/debug"
+ "strings"
+ "unsafe"
+
+ "golang.org/x/sys/unix"
+ "golang.zx2c4.com/wireguard/conn"
+ "golang.zx2c4.com/wireguard/device"
+ "golang.zx2c4.com/wireguard/ipc"
+ "golang.zx2c4.com/wireguard/tun"
+)
+
+type AndroidLogger struct {
+ level C.int
+ tag *C.char
+}
+
+func cstring(s string) *C.char {
+ b, err := unix.BytePtrFromString(s)
+ if err != nil {
+ b := [1]C.char{}
+ return &b[0]
+ }
+ return (*C.char)(unsafe.Pointer(b))
+}
+
+func (l AndroidLogger) Printf(format string, args ...interface{}) {
+ C.__android_log_write(l.level, l.tag, cstring(fmt.Sprintf(format, args...)))
+}
+
+type TunnelHandle struct {
+ device *device.Device
+ uapi net.Listener
+}
+
+var tunnelHandles map[int32]TunnelHandle
+
+func init() {
+ tunnelHandles = make(map[int32]TunnelHandle)
+ signals := make(chan os.Signal)
+ signal.Notify(signals, unix.SIGUSR2)
+ go func() {
+ buf := make([]byte, os.Getpagesize())
+ for {
+ select {
+ case <-signals:
+ n := runtime.Stack(buf, true)
+ if n == len(buf) {
+ n--
+ }
+ buf[n] = 0
+ C.__android_log_write(C.ANDROID_LOG_ERROR, cstring("WireGuard/GoBackend/Stacktrace"), (*C.char)(unsafe.Pointer(&buf[0])))
+ }
+ }
+ }()
+}
+
+//export wgTurnOn
+func wgTurnOn(interfaceName string, tunFd int32, settings string) int32 {
+ tag := cstring("WireGuard/GoBackend/" + interfaceName)
+ logger := &device.Logger{
+ Verbosef: AndroidLogger{level: C.ANDROID_LOG_DEBUG, tag: tag}.Printf,
+ Errorf: AndroidLogger{level: C.ANDROID_LOG_ERROR, tag: tag}.Printf,
+ }
+
+ tun, name, err := tun.CreateUnmonitoredTUNFromFD(int(tunFd))
+ if err != nil {
+ unix.Close(int(tunFd))
+ logger.Errorf("CreateUnmonitoredTUNFromFD: %v", err)
+ return -1
+ }
+
+ logger.Verbosef("Attaching to interface %v", name)
+ device := device.NewDevice(tun, conn.NewStdNetBind(), logger)
+
+ err = device.IpcSet(settings)
+ if err != nil {
+ unix.Close(int(tunFd))
+ logger.Errorf("IpcSet: %v", err)
+ return -1
+ }
+ device.DisableSomeRoamingForBrokenMobileSemantics()
+
+ var uapi net.Listener
+
+ uapiFile, err := ipc.UAPIOpen(name)
+ if err != nil {
+ logger.Errorf("UAPIOpen: %v", err)
+ } else {
+ uapi, err = ipc.UAPIListen(name, uapiFile)
+ if err != nil {
+ uapiFile.Close()
+ logger.Errorf("UAPIListen: %v", err)
+ } else {
+ go func() {
+ for {
+ conn, err := uapi.Accept()
+ if err != nil {
+ return
+ }
+ go device.IpcHandle(conn)
+ }
+ }()
+ }
+ }
+
+ err = device.Up()
+ if err != nil {
+ logger.Errorf("Unable to bring up device: %v", err)
+ uapiFile.Close()
+ device.Close()
+ return -1
+ }
+ logger.Verbosef("Device started")
+
+ var i int32
+ for i = 0; i < math.MaxInt32; i++ {
+ if _, exists := tunnelHandles[i]; !exists {
+ break
+ }
+ }
+ if i == math.MaxInt32 {
+ logger.Errorf("Unable to find empty handle")
+ uapiFile.Close()
+ device.Close()
+ return -1
+ }
+ tunnelHandles[i] = TunnelHandle{device: device, uapi: uapi}
+ return i
+}
+
+//export wgTurnOff
+func wgTurnOff(tunnelHandle int32) {
+ handle, ok := tunnelHandles[tunnelHandle]
+ if !ok {
+ return
+ }
+ delete(tunnelHandles, tunnelHandle)
+ if handle.uapi != nil {
+ handle.uapi.Close()
+ }
+ handle.device.Close()
+}
+
+//export wgGetSocketV4
+func wgGetSocketV4(tunnelHandle int32) int32 {
+ handle, ok := tunnelHandles[tunnelHandle]
+ if !ok {
+ return -1
+ }
+ bind, _ := handle.device.Bind().(conn.PeekLookAtSocketFd)
+ if bind == nil {
+ return -1
+ }
+ fd, err := bind.PeekLookAtSocketFd4()
+ if err != nil {
+ return -1
+ }
+ return int32(fd)
+}
+
+//export wgGetSocketV6
+func wgGetSocketV6(tunnelHandle int32) int32 {
+ handle, ok := tunnelHandles[tunnelHandle]
+ if !ok {
+ return -1
+ }
+ bind, _ := handle.device.Bind().(conn.PeekLookAtSocketFd)
+ if bind == nil {
+ return -1
+ }
+ fd, err := bind.PeekLookAtSocketFd6()
+ if err != nil {
+ return -1
+ }
+ return int32(fd)
+}
+
+//export wgGetConfig
+func wgGetConfig(tunnelHandle int32) *C.char {
+ handle, ok := tunnelHandles[tunnelHandle]
+ if !ok {
+ return nil
+ }
+ settings, err := handle.device.IpcGet()
+ if err != nil {
+ return nil
+ }
+ return C.CString(settings)
+}
+
+//export wgVersion
+func wgVersion() *C.char {
+ info, ok := debug.ReadBuildInfo()
+ if !ok {
+ return C.CString("unknown")
+ }
+ for _, dep := range info.Deps {
+ if dep.Path == "golang.zx2c4.com/wireguard" {
+ parts := strings.Split(dep.Version, "-")
+ if len(parts) == 3 && len(parts[2]) == 12 {
+ return C.CString(parts[2][:7])
+ }
+ return C.CString(dep.Version)
+ }
+ }
+ return C.CString("unknown")
+}
+
+func main() {}
diff --git a/tunnel/tools/libwg-go/go.mod b/tunnel/tools/libwg-go/go.mod
new file mode 100644
index 00000000..9f9381ff
--- /dev/null
+++ b/tunnel/tools/libwg-go/go.mod
@@ -0,0 +1,14 @@
+module golang.zx2c4.com/wireguard/android
+
+go 1.20
+
+require (
+ golang.org/x/sys v0.13.0
+ golang.zx2c4.com/wireguard v0.0.0-20231022001213-2e0774f246fb
+)
+
+require (
+ golang.org/x/crypto v0.14.0 // indirect
+ golang.org/x/net v0.17.0 // indirect
+ golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
+)
diff --git a/tunnel/tools/libwg-go/go.sum b/tunnel/tools/libwg-go/go.sum
new file mode 100644
index 00000000..e5f8fc83
--- /dev/null
+++ b/tunnel/tools/libwg-go/go.sum
@@ -0,0 +1,13 @@
+github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
+golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
+golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
+golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
+golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
+golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
+golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44=
+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=
+golang.zx2c4.com/wireguard v0.0.0-20231022001213-2e0774f246fb h1:c5tyN8sSp8jSDxdCCDXVOpJwYXXhmTkNMt+g0zTSOic=
+golang.zx2c4.com/wireguard v0.0.0-20231022001213-2e0774f246fb/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA=
+gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259 h1:TbRPT0HtzFP3Cno1zZo7yPzEEnfu8EjLfl6IU9VfqkQ=
diff --git a/tunnel/tools/libwg-go/goruntime-boottime-over-monotonic.diff b/tunnel/tools/libwg-go/goruntime-boottime-over-monotonic.diff
new file mode 100644
index 00000000..5d78242b
--- /dev/null
+++ b/tunnel/tools/libwg-go/goruntime-boottime-over-monotonic.diff
@@ -0,0 +1,171 @@
+From 61f3ae8298d1c503cbc31539e0f3a73446c7db9d Mon Sep 17 00:00:00 2001
+From: "Jason A. Donenfeld" <Jason@zx2c4.com>
+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/tunnel/tools/libwg-go/jni.c b/tunnel/tools/libwg-go/jni.c
new file mode 100644
index 00000000..7ad94d35
--- /dev/null
+++ b/tunnel/tools/libwg-go/jni.c
@@ -0,0 +1,71 @@
+/* SPDX-License-Identifier: Apache-2.0
+ *
+ * Copyright © 2017-2021 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#include <jni.h>
+#include <stdlib.h>
+#include <string.h>
+
+struct go_string { const char *str; long n; };
+extern int wgTurnOn(struct go_string ifname, int tun_fd, struct go_string settings);
+extern void wgTurnOff(int handle);
+extern int wgGetSocketV4(int handle);
+extern int wgGetSocketV6(int handle);
+extern char *wgGetConfig(int handle);
+extern char *wgVersion();
+
+JNIEXPORT jint JNICALL Java_com_wireguard_android_backend_GoBackend_wgTurnOn(JNIEnv *env, jclass c, jstring ifname, jint tun_fd, jstring settings)
+{
+ const char *ifname_str = (*env)->GetStringUTFChars(env, ifname, 0);
+ size_t ifname_len = (*env)->GetStringUTFLength(env, ifname);
+ const char *settings_str = (*env)->GetStringUTFChars(env, settings, 0);
+ size_t settings_len = (*env)->GetStringUTFLength(env, settings);
+ int ret = wgTurnOn((struct go_string){
+ .str = ifname_str,
+ .n = ifname_len
+ }, tun_fd, (struct go_string){
+ .str = settings_str,
+ .n = settings_len
+ });
+ (*env)->ReleaseStringUTFChars(env, ifname, ifname_str);
+ (*env)->ReleaseStringUTFChars(env, settings, settings_str);
+ return ret;
+}
+
+JNIEXPORT void JNICALL Java_com_wireguard_android_backend_GoBackend_wgTurnOff(JNIEnv *env, jclass c, jint handle)
+{
+ wgTurnOff(handle);
+}
+
+JNIEXPORT jint JNICALL Java_com_wireguard_android_backend_GoBackend_wgGetSocketV4(JNIEnv *env, jclass c, jint handle)
+{
+ return wgGetSocketV4(handle);
+}
+
+JNIEXPORT jint JNICALL Java_com_wireguard_android_backend_GoBackend_wgGetSocketV6(JNIEnv *env, jclass c, jint handle)
+{
+ return wgGetSocketV6(handle);
+}
+
+JNIEXPORT jstring JNICALL Java_com_wireguard_android_backend_GoBackend_wgGetConfig(JNIEnv *env, jclass c, jint handle)
+{
+ jstring ret;
+ char *config = wgGetConfig(handle);
+ if (!config)
+ return NULL;
+ ret = (*env)->NewStringUTF(env, config);
+ free(config);
+ return ret;
+}
+
+JNIEXPORT jstring JNICALL Java_com_wireguard_android_backend_GoBackend_wgVersion(JNIEnv *env, jclass c)
+{
+ jstring ret;
+ char *version = wgVersion();
+ if (!version)
+ return NULL;
+ ret = (*env)->NewStringUTF(env, version);
+ free(version);
+ return ret;
+}
diff --git a/tunnel/tools/ndk-compat/compat.c b/tunnel/tools/ndk-compat/compat.c
new file mode 100644
index 00000000..c5ce85ec
--- /dev/null
+++ b/tunnel/tools/ndk-compat/compat.c
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: BSD
+ *
+ * Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
+ *
+ */
+
+#define FILE_IS_EMPTY
+
+#if defined(__ANDROID_MIN_SDK_VERSION__) && __ANDROID_MIN_SDK_VERSION__ < 24
+#undef FILE_IS_EMPTY
+#include <string.h>
+
+char *strchrnul(const char *s, int c)
+{
+ char *x = strchr(s, c);
+ if (!x)
+ return (char *)s + strlen(s);
+ return x;
+}
+#endif
+
+#ifdef FILE_IS_EMPTY
+#undef FILE_IS_EMPTY
+static char ____x __attribute__((unused));
+#endif
diff --git a/tunnel/tools/ndk-compat/compat.h b/tunnel/tools/ndk-compat/compat.h
new file mode 100644
index 00000000..7dfd8e14
--- /dev/null
+++ b/tunnel/tools/ndk-compat/compat.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: BSD
+ *
+ * Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
+ *
+ */
+
+#if defined(__ANDROID_MIN_SDK_VERSION__) && __ANDROID_MIN_SDK_VERSION__ < 24
+char *strchrnul(const char *s, int c);
+#endif
+
diff --git a/tunnel/tools/wireguard-tools b/tunnel/tools/wireguard-tools
new file mode 160000
+Subproject b4f6b4f229d291daf7c35c6f1e7f4841cc6d69b