From fe7dc5f8a69c8e548906fc8a910aa9ef01593222 Mon Sep 17 00:00:00 2001 From: jmwample <8297368+jmwample@users.noreply.github.com> Date: Wed, 16 Oct 2024 22:14:57 -0600 Subject: [PATCH 1/6] initial compiling amnezia lib exchanged for wireguard-go basic structs and feature for amnezia configuration - libs still build --- Makefile | 3 + nym-vpn-core/crates/nym-vpn-lib/build.rs | 35 +++ nym-vpn-core/crates/nym-vpnd/build.rs | 35 +++ nym-vpn-core/crates/nym-wg-go/Cargo.toml | 4 + nym-vpn-core/crates/nym-wg-go/src/netstack.rs | 43 ++++ .../crates/nym-wg-go/src/wireguard_go.rs | 43 ++++ wireguard/build-wireguard-go.sh | 112 ++++++---- wireguard/libamnezia/Android.mk | 39 ++++ .../Dockerfile_AndroidPatchedGoruntime | 50 +++++ wireguard/libamnezia/README.md | 26 +++ wireguard/libamnezia/build-android.sh | 74 +++++++ wireguard/libamnezia/container/container.go | 63 ++++++ wireguard/libamnezia/go.mod | 21 ++ wireguard/libamnezia/go.sum | 18 ++ ...untime-boottime-over-monotonic-darwin.diff | 61 ++++++ .../goruntime-boottime-over-monotonic.diff | 170 +++++++++++++++ wireguard/libamnezia/libwg.go | 74 +++++++ wireguard/libamnezia/libwg_android.go | 103 +++++++++ wireguard/libamnezia/libwg_default.go | 97 +++++++++ wireguard/libamnezia/libwg_ios.go | 97 +++++++++ wireguard/libamnezia/libwg_mobile.go | 40 ++++ wireguard/libamnezia/libwg_windows.go | 153 ++++++++++++++ wireguard/libamnezia/logging/logging.go | 60 ++++++ wireguard/libamnezia/netstack_android.go | 39 ++++ wireguard/libamnezia/netstack_mobile.go | 200 ++++++++++++++++++ .../libamnezia/udp_forwarder/udp_forwarder.go | 194 +++++++++++++++++ 26 files changed, 1814 insertions(+), 40 deletions(-) create mode 100644 wireguard/libamnezia/Android.mk create mode 100644 wireguard/libamnezia/Dockerfile_AndroidPatchedGoruntime create mode 100644 wireguard/libamnezia/README.md create mode 100755 wireguard/libamnezia/build-android.sh create mode 100644 wireguard/libamnezia/container/container.go create mode 100644 wireguard/libamnezia/go.mod create mode 100644 wireguard/libamnezia/go.sum create mode 100644 wireguard/libamnezia/goruntime-boottime-over-monotonic-darwin.diff create mode 100644 wireguard/libamnezia/goruntime-boottime-over-monotonic.diff create mode 100644 wireguard/libamnezia/libwg.go create mode 100644 wireguard/libamnezia/libwg_android.go create mode 100644 wireguard/libamnezia/libwg_default.go create mode 100644 wireguard/libamnezia/libwg_ios.go create mode 100644 wireguard/libamnezia/libwg_mobile.go create mode 100644 wireguard/libamnezia/libwg_windows.go create mode 100644 wireguard/libamnezia/logging/logging.go create mode 100644 wireguard/libamnezia/netstack_android.go create mode 100644 wireguard/libamnezia/netstack_mobile.go create mode 100644 wireguard/libamnezia/udp_forwarder/udp_forwarder.go diff --git a/Makefile b/Makefile index 88dddeb6d9..62fb7e3689 100644 --- a/Makefile +++ b/Makefile @@ -6,6 +6,9 @@ all: build-wireguard build-nym-vpn-core build-wireguard: ./wireguard/build-wireguard-go.sh +build-amnezia-wg: + ./wireguard/build-wireguard-go.sh --amnezia + build-wireguard-ios: ./wireguard/build-wireguard-go.sh --ios diff --git a/nym-vpn-core/crates/nym-vpn-lib/build.rs b/nym-vpn-core/crates/nym-vpn-lib/build.rs index 17b0b39446..425025e065 100644 --- a/nym-vpn-core/crates/nym-vpn-lib/build.rs +++ b/nym-vpn-core/crates/nym-vpn-lib/build.rs @@ -1,6 +1,7 @@ // Copyright 2024 - Nym Technologies SA // SPDX-License-Identifier: GPL-3.0-only +use std::{env, path::PathBuf}; use vergen::EmitBuilder; fn main() -> Result<(), Box> { @@ -11,5 +12,39 @@ fn main() -> Result<(), Box> { .all_cargo() .emit() .expect("failed to extract build metadata"); + + let manifest_path = env::var_os("CARGO_MANIFEST_DIR").expect("manifest dir is not set"); + let target = env::var("TARGET").expect("target is not set"); + let target_os = env::var("CARGO_CFG_TARGET_OS").expect("target os is not set"); + + let mut build_dir = PathBuf::from(manifest_path) + .join("../../../build/lib") + .canonicalize() + .expect("failed to canonicalize build dir path"); + + build_dir.push(target); + + // CI may only provide universal builds + if target_os == "macos" { + let target_dir_exists = build_dir + .try_exists() + .expect("failed to check existence of target dir"); + + if !target_dir_exists { + build_dir.pop(); + build_dir.push("universal-apple-darwin"); + } + } + + println!("cargo::rustc-link-search={}", build_dir.display()); + + let link_type = match target_os.as_str() { + "android" => "", + "linux" | "macos" | "ios" => "=static", + "windows" => "dylib", + _ => panic!("Unsupported platform: {}", target_os), + }; + println!("cargo:rustc-link-lib{}=wg", link_type); + Ok(()) } diff --git a/nym-vpn-core/crates/nym-vpnd/build.rs b/nym-vpn-core/crates/nym-vpnd/build.rs index 17b0b39446..425025e065 100644 --- a/nym-vpn-core/crates/nym-vpnd/build.rs +++ b/nym-vpn-core/crates/nym-vpnd/build.rs @@ -1,6 +1,7 @@ // Copyright 2024 - Nym Technologies SA // SPDX-License-Identifier: GPL-3.0-only +use std::{env, path::PathBuf}; use vergen::EmitBuilder; fn main() -> Result<(), Box> { @@ -11,5 +12,39 @@ fn main() -> Result<(), Box> { .all_cargo() .emit() .expect("failed to extract build metadata"); + + let manifest_path = env::var_os("CARGO_MANIFEST_DIR").expect("manifest dir is not set"); + let target = env::var("TARGET").expect("target is not set"); + let target_os = env::var("CARGO_CFG_TARGET_OS").expect("target os is not set"); + + let mut build_dir = PathBuf::from(manifest_path) + .join("../../../build/lib") + .canonicalize() + .expect("failed to canonicalize build dir path"); + + build_dir.push(target); + + // CI may only provide universal builds + if target_os == "macos" { + let target_dir_exists = build_dir + .try_exists() + .expect("failed to check existence of target dir"); + + if !target_dir_exists { + build_dir.pop(); + build_dir.push("universal-apple-darwin"); + } + } + + println!("cargo::rustc-link-search={}", build_dir.display()); + + let link_type = match target_os.as_str() { + "android" => "", + "linux" | "macos" | "ios" => "=static", + "windows" => "dylib", + _ => panic!("Unsupported platform: {}", target_os), + }; + println!("cargo:rustc-link-lib{}=wg", link_type); + Ok(()) } diff --git a/nym-vpn-core/crates/nym-wg-go/Cargo.toml b/nym-vpn-core/crates/nym-wg-go/Cargo.toml index 533ad587a5..aaed1be3e1 100644 --- a/nym-vpn-core/crates/nym-wg-go/Cargo.toml +++ b/nym-vpn-core/crates/nym-wg-go/Cargo.toml @@ -8,6 +8,10 @@ documentation.workspace = true edition.workspace = true license.workspace = true +[features] +default = [] +amnezia = [] + [dependencies] ipnetwork.workspace = true thiserror.workspace = true diff --git a/nym-vpn-core/crates/nym-wg-go/src/netstack.rs b/nym-vpn-core/crates/nym-wg-go/src/netstack.rs index 0da81c1825..003e2fd140 100644 --- a/nym-vpn-core/crates/nym-wg-go/src/netstack.rs +++ b/nym-vpn-core/crates/nym-wg-go/src/netstack.rs @@ -20,6 +20,8 @@ pub struct InterfaceConfig { pub local_addrs: Vec, pub dns_addrs: Vec, pub mtu: u16, + #[cfg(feature = "amnezia")] + pub azwg_config: Option, } impl fmt::Debug for InterfaceConfig { @@ -33,6 +35,47 @@ impl fmt::Debug for InterfaceConfig { } } +/// Hold Amnezia-wireguard configuration parameters. +/// +/// All parameters should be the same between Client and Server, except Jc - it can vary. +/// +/// - Jc — 1 ≤ Jc ≤ 128; recommended range is from 3 to 10 inclusive +/// - Jmin — Jmin < Jmax; recommended value is 50 +/// - Jmax — Jmin < Jmax ≤ 1280; recommended value is 1000 +/// - S1 — S1 < 1280; S1 + 56 ≠ S2; recommended range is from 15 to 150 inclusive +/// - S2 — S2 < 1280; recommended range is from 15 to 150 inclusive +/// - H1/H2/H3/H4 — must be unique among each other; +/// recommended range is from 5 to 2_147_483_647 (2^31 - 1 i.e. signed 32 bit int) inclusive +#[cfg(feature = "amnezia")] +#[derive(Debug)] +pub struct AmneziaConfig { + pub junk_packet_count: i32, // Jc + pub junk_packet_min_size: i32, // Jmin + pub junk_packet_max_size: i32, // Jmax + pub init_packet_junk_size: i32, // S0 + pub response_packet_junk_size: i32, // S1 + pub init_packet_magic_header: u32, // H1 + pub response_packet_magic_header: u32, // H2 + pub under_load_packet_magic_header: u32, // H3 + pub transport_packet_magic_header: u32, // H4 +} + +#[cfg(feature = "amnezia")] +impl Default for AmneziaConfig { + fn default() -> Self { + Self { + junk_packet_count: 4_i32, + junk_packet_min_size: 40_i32, + junk_packet_max_size: 70_i32, + init_packet_junk_size: 0_i32, + response_packet_junk_size: 0_i32, + init_packet_magic_header: 1_u32, + response_packet_magic_header: 2_u32, + under_load_packet_magic_header: 3_u32, + transport_packet_magic_header: 4_u32, + } + } +} /// Netstack configuration. #[derive(Debug)] pub struct Config { diff --git a/nym-vpn-core/crates/nym-wg-go/src/wireguard_go.rs b/nym-vpn-core/crates/nym-wg-go/src/wireguard_go.rs index f37a6154e8..2f347beb09 100644 --- a/nym-vpn-core/crates/nym-wg-go/src/wireguard_go.rs +++ b/nym-vpn-core/crates/nym-wg-go/src/wireguard_go.rs @@ -20,6 +20,8 @@ pub struct InterfaceConfig { pub mtu: u16, #[cfg(target_os = "linux")] pub fwmark: Option, + #[cfg(feature = "amnezia")] + pub azwg_config: Option, } impl fmt::Debug for InterfaceConfig { @@ -33,6 +35,47 @@ impl fmt::Debug for InterfaceConfig { d.finish() } } +/// Hold Amnezia-wireguard configuration parameters. +/// +/// All parameters should be the same between Client and Server, except Jc - it can vary. +/// +/// - Jc — 1 ≤ Jc ≤ 128; recommended range is from 3 to 10 inclusive +/// - Jmin — Jmin < Jmax; recommended value is 50 +/// - Jmax — Jmin < Jmax ≤ 1280; recommended value is 1000 +/// - S1 — S1 < 1280; S1 + 56 ≠ S2; recommended range is from 15 to 150 inclusive +/// - S2 — S2 < 1280; recommended range is from 15 to 150 inclusive +/// - H1/H2/H3/H4 — must be unique among each other; +/// recommended range is from 5 to 2_147_483_647 (2^31 - 1 i.e. signed 32 bit int) inclusive +#[cfg(feature = "amnezia")] +#[derive(Debug)] +pub struct AmneziaConfig { + pub junk_packet_count: i32, // Jc + pub junk_packet_min_size: i32, // Jmin + pub junk_packet_max_size: i32, // Jmax + pub init_packet_junk_size: i32, // S0 + pub response_packet_junk_size: i32, // S1 + pub init_packet_magic_header: u32, // H1 + pub response_packet_magic_header: u32, // H2 + pub under_load_packet_magic_header: u32, // H3 + pub transport_packet_magic_header: u32, // H4 +} + +#[cfg(feature = "amnezia")] +impl Default for AmneziaConfig { + fn default() -> Self { + Self { + junk_packet_count: 4_i32, + junk_packet_min_size: 40_i32, + junk_packet_max_size: 70_i32, + init_packet_junk_size: 0_i32, + response_packet_junk_size: 0_i32, + init_packet_magic_header: 1_u32, + response_packet_magic_header: 2_u32, + under_load_packet_magic_header: 3_u32, + transport_packet_magic_header: 4_u32, + } + } +} /// Classic WireGuard configuration. #[derive(Debug)] diff --git a/wireguard/build-wireguard-go.sh b/wireguard/build-wireguard-go.sh index f7cd2110bf..d16584f20e 100755 --- a/wireguard/build-wireguard-go.sh +++ b/wireguard/build-wireguard-go.sh @@ -2,52 +2,67 @@ # This script is used to build wireguard-go libraries for all the platforms. +TEMP=$(getopt -o aiz --long android,docker,ios,amnezia \ + -n 'build-wireguard-go.sh' -- "$@") + +if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi + +# Note the quotes around '$TEMP': they are essential! +eval set -- "$TEMP" + +ANDROID_BUILD=false +IOS_BUILD=false +DOCKER_BUILD=true +AMNEZIA_BUILD=false +while true; do + case "$1" in + "-a" | "--android" ) ANDROID_BUILD=true; shift ;; + "-i" | "--ios" ) IOS_BUILD=true; shift ;; + "-z" | "--amnezia" ) AMNEZIA_BUILD=true; shift ;; + "--no-docker" ) DOCKER_BUILD=false; shift ;; + -- ) shift; break ;; + * ) break ;; + esac +done + set -eu function is_android_build { - for arg in "$@" - do - case "$arg" in - "--android") - return 0 - esac - done + if [ "$ANDROID_BUILD" = true ]; then + return 0 + fi return 1 } function is_ios_build { - for arg in "$@" - do - case "$arg" in - "--ios") - return 0 - esac - done + if [ "$IOS_BUILD" = true ]; then + return 0 + fi return 1 } function is_docker_build { - for arg in "$@" - do - case "$arg" in - "--no-docker") - return 1 - esac - done - return 0 + if [ "$DOCKER_BUILD" = true ]; then + return 0 + fi + return 1 } -function is_win_arm64 { - for arg in "$@" - do - case "$arg" in - "--arm64") - return 0 - esac - done +function is_amnezia_build { + if [ "$AMNEZIA_BUILD" = true ]; then + return 0 + fi return 1 } +function win_deduce_lib_executable_path { + msbuild_path="$(which msbuild.exe)" + msbuild_dir=$(dirname "$msbuild_path") + find "$msbuild_dir/../../../../" -name "lib.exe" | \ + grep -i "hostx64/x64" | \ + head -n1 +} + function win_gather_export_symbols { grep -Eo "\/\/export \w+" libwg.go libwg_windows.go | cut -d' ' -f2 } @@ -75,8 +90,16 @@ function win_create_lib_file { } function build_windows { +<<<<<<< HEAD export CGO_ENABLED=1 export GOOS=windows +======= + echo "Building wireguard-go for Windows" + pushd $LIB_DIR + export CGO_ENABLED=1 + go build -trimpath -v -o libwg.dll -buildmode c-shared + win_create_lib_file +>>>>>>> c0b0f37b (initial compiling amnezia lib exchanged for wireguard-go) if is_win_arm64 $@; then local arch="aarch64" @@ -132,7 +155,7 @@ function build_unix { fi fi - pushd libwg + pushd $LIB_DIR create_folder_and_build $1 popd } @@ -141,14 +164,14 @@ function build_android { echo "Building for android" local docker_image_hash="992c4d5c7dcd00eacf6f3e3667ce86b8e185f011352bdd9f79e467fef3e27abd" - if is_docker_build $@; then + if is_docker_build; then docker run --rm \ -v "$(pwd)/../":/workspace \ - --entrypoint "/workspace/wireguard/libwg/build-android.sh" \ + --entrypoint "/workspace/wireguard/$LIB_DIR/build-android.sh" \ --env ANDROID_NDK_HOME="/opt/android/android-ndk-r20b" \ docker.io/pronebird1337/nymtech-android-app@sha256:$docker_image_hash else - ./libwg/build-android.sh + ./$LIB_DIR/build-android.sh fi } @@ -166,7 +189,7 @@ function build_macos_universal { export MACOSX_DEPLOYMENT_TARGET=10.13 echo "🍎 Building for aarch64" - pushd libwg + pushd $LIB_DIR export GOOS=darwin export GOARCH=arm64 create_folder_and_build "aarch64-apple-darwin" @@ -189,7 +212,7 @@ function build_ios { export CGO_ENABLED=1 export IPHONEOS_DEPLOYMENT_TARGET=16.0 - pushd libwg + pushd $LIB_DIR echo "🍎 Building for ios/aarch64" export GOARCH=arm64 @@ -240,17 +263,23 @@ function patch_darwin_goruntime { REAL_GOROOT=$(go env GOROOT 2>/dev/null) export GOROOT="$BUILDDIR/goroot" mkdir -p "$GOROOT" - rsync -a --delete --exclude=pkg/obj/go-build "$REAL_GOROOT/" "$GOROOT/" - cat libwg/goruntime-boottime-over-monotonic-darwin.diff | patch -p1 -f -N -r- -d "$GOROOT" + rsync -a --delete --exclude=pkg/obj/go-build "$REAL_GOROOT/" "$GOROOT/" + cat $LIB_DIR/goruntime-boottime-over-monotonic-darwin.diff | patch -p1 -f -N -r- -d "$GOROOT" } function build_wireguard_go { - if is_android_build $@; then + + if is_amnezia_build ; then + LIB_DIR=$AMNEZIA_DIR + echo "amnezia wireguard build enabled" + fi + + if is_android_build ; then build_android $@ return fi - if is_ios_build $@; then + if is_ios_build ; then build_ios $@ return fi @@ -263,6 +292,9 @@ function build_wireguard_go { esac } +AMNEZIA_DIR="libamnezia" +LIB_DIR="libwg" + # Ensure we are in the correct directory for the execution of this script script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" cd $script_dir diff --git a/wireguard/libamnezia/Android.mk b/wireguard/libamnezia/Android.mk new file mode 100644 index 0000000000..1f8603ed54 --- /dev/null +++ b/wireguard/libamnezia/Android.mk @@ -0,0 +1,39 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright © 2017-2019 WireGuard LLC. All Rights Reserved. + +DESTDIR ?= $(CURDIR)/../../build/lib/$(RUST_TARGET_TRIPLE) + +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 + +export CGO_CFLAGS := $(CFLAGS) +export CGO_LDFLAGS := $(LDFLAGS) +export CC := $(ANDROID_C_COMPILER) +export GOARCH := $(NDK_GO_ARCH_MAP_$(ANDROID_ARCH_NAME)) +export GOOS := android +export CGO_ENABLED := 1 + +default: $(DESTDIR)/libwg.so + +GOBUILDARCH := $(NDK_GO_ARCH_MAP_$(shell uname -m)) +GOBUILDOS := $(shell uname -s | tr '[:upper:]' '[:lower:]') +GOBUILDVERSION := 1.22.6 +# TODO: Add checksum? +GOBUILDTARBALL := https://go.dev/dl/go$(GOBUILDVERSION).$(GOBUILDOS)-$(GOBUILDARCH).tar.gz +GOBUILDVERSION_NEEDED := go version go$(GOBUILDVERSION) $(GOBUILDOS)/$(GOBUILDARCH) + +$(DESTDIR)/libwg.so: + mkdir -p $(DESTDIR) + go get -tags "linux android" + chmod -fR +w "$(GOPATH)/pkg/mod" + go build -tags "linux android" -ldflags="-X main.socketDirectory=/data/data/$(ANDROID_PACKAGE_NAME)/cache/wireguard" -v -o "$@" -buildmode c-shared + rm -f $(DESTDIR)/libwg.h + + +clean: + rm -f $(DESTDIR)/libwg.so diff --git a/wireguard/libamnezia/Dockerfile_AndroidPatchedGoruntime b/wireguard/libamnezia/Dockerfile_AndroidPatchedGoruntime new file mode 100644 index 0000000000..cbdec91784 --- /dev/null +++ b/wireguard/libamnezia/Dockerfile_AndroidPatchedGoruntime @@ -0,0 +1,50 @@ +# To build the image: +# docker build . -t docker.io/pronebird1337/nymtech-android-app -f Dockerfile_AndroidPatchedGoruntime +# To push the image to docker.io: +# docker push docker.io/pronebird1337/nymtech-android-app + +FROM debian@sha256:77f46c1cf862290e750e913defffb2828c889d291a93bdd10a7a0597720948fc + +RUN apt-get update -y && apt-get install -y \ + curl \ + file \ + gcc \ + git \ + make \ + python \ + unzip + +# Install Android NDK +RUN cd /tmp && \ + curl -sf -L -o ndk.zip https://dl.google.com/android/repository/android-ndk-r20b-linux-x86_64.zip && \ + echo "8381c440fe61fcbb01e209211ac01b519cd6adf51ab1c2281d5daad6ca4c8c8c ndk.zip" | sha256sum -c - && \ + mkdir /opt/android && \ + cd /opt/android && \ + unzip -q /tmp/ndk.zip && \ + rm /tmp/ndk.zip + + +ENV ANDROID_NDK_HOME="/opt/android/android-ndk-r20b" +ENV NDK_TOOLCHAIN_DIR="${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin" + +ENV GOLANG_VERSION=1.22.6 +ENV GOLANG_HASH=999805bed7d9039ec3da1a53bfbcafc13e367da52aa823cb60b68ba22d44c616 + +# Install Go-lang and patch it to use the appropriate monotonic clock +COPY goruntime-boottime-over-monotonic.diff /opt/goruntime-boottime-over-monotonic.diff +RUN cd /tmp && \ + curl -sf -L -o go.tgz https://go.dev/dl/go${GOLANG_VERSION}.linux-amd64.tar.gz && \ + echo "${GOLANG_HASH} go.tgz" | sha256sum -c - && \ + cd /opt && \ + tar -xzf /tmp/go.tgz && \ + rm /tmp/go.tgz && \ + patch -p1 -f -N -r- -d "/opt/go" < /opt/goruntime-boottime-over-monotonic.diff + +ENV PATH=${PATH}:/opt/go/bin +ENV GOROOT=/opt/go +ENV GOPATH=/opt/go-path + +RUN apt-get remove -y curl && \ + apt-get autoremove -y + +ENTRYPOINT [] diff --git a/wireguard/libamnezia/README.md b/wireguard/libamnezia/README.md new file mode 100644 index 0000000000..e5b96928f7 --- /dev/null +++ b/wireguard/libamnezia/README.md @@ -0,0 +1,26 @@ +# Introduction + +`libwg` is a tiny wrapper around `wireguard-go`, with the main purpose of providing a simple FFI-friendly interface. + +It currently offers support for the following platforms: + +- Linux +- macOS +- Android +- Windows + +# Organization + +`libwg.go` has shared code that is used on all platforms. + +`libwg_default.go` has default implementations for Linux-based systems. + +`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. + +Call `wgTurnOff` to destroy the tunnel. diff --git a/wireguard/libamnezia/build-android.sh b/wireguard/libamnezia/build-android.sh new file mode 100755 index 0000000000..0d269919f2 --- /dev/null +++ b/wireguard/libamnezia/build-android.sh @@ -0,0 +1,74 @@ +#!/usr/bin/env bash + +set -eu + +# Ensure we are in the correct directory for the execution of this script +script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +cd $script_dir + +# Keep a GOPATH in the build directory to maintain a cache of downloaded libraries +export GOPATH=$script_dir/../../build/android-go-path/ +mkdir -p $GOPATH + +for arch in ${ARCHITECTURES:-armv7 aarch64 x86_64 i686}; do + case "$arch" in + "aarch64") + export ANDROID_C_COMPILER="${NDK_TOOLCHAIN_DIR}/aarch64-linux-android21-clang" + export ANDROID_STRIP_TOOL="${NDK_TOOLCHAIN_DIR}/aarch64-linux-android-strip" + export RUST_TARGET_TRIPLE="aarch64-linux-android" + export ANDROID_ABI="arm64-v8a" + export ANDROID_ARCH_NAME="arm64" + ;; + "x86_64") + export ANDROID_C_COMPILER="${NDK_TOOLCHAIN_DIR}/x86_64-linux-android21-clang" + export ANDROID_STRIP_TOOL="${NDK_TOOLCHAIN_DIR}/x86_64-linux-android-strip" + export RUST_TARGET_TRIPLE="x86_64-linux-android" + export ANDROID_ABI="x86_64" + export ANDROID_ARCH_NAME="x86_64" + ;; + "armv7") + export ANDROID_C_COMPILER="${NDK_TOOLCHAIN_DIR}/armv7a-linux-androideabi21-clang" + export ANDROID_STRIP_TOOL="${NDK_TOOLCHAIN_DIR}/arm-linux-androideabi-strip" + export RUST_TARGET_TRIPLE="armv7-linux-androideabi" + export ANDROID_ABI="armeabi-v7a" + export ANDROID_ARCH_NAME="arm" + ;; + "i686") + export ANDROID_C_COMPILER="${NDK_TOOLCHAIN_DIR}/i686-linux-android21-clang" + export ANDROID_STRIP_TOOL="${NDK_TOOLCHAIN_DIR}/i686-linux-android-strip" + export RUST_TARGET_TRIPLE="i686-linux-android" + export ANDROID_ABI="x86" + export ANDROID_ARCH_NAME="x86" + ;; + esac + + # Build Wireguard-Go + echo $(pwd) + make -f Android.mk clean + +# this is determined by the NDK +# export CFLAGS="-D__ANDROID_API__=21" + + make -f Android.mk + + # Strip and copy the libray to `android/build/extraJni/$ANDROID_ABI` to be able to build the APK + UNSTRIPPED_LIB_PATH="../../build/lib/$RUST_TARGET_TRIPLE/libwg.so" + STRIPPED_LIB_PATH="../../android/app/build/extraJni/$ANDROID_ABI/libwg.so" + + # Create the directories with RWX permissions for all users so that the build server can clean + # the directories afterwards + mkdir -m 777 -p "$(dirname "$STRIPPED_LIB_PATH")" + + cp "$UNSTRIPPED_LIB_PATH" "$STRIPPED_LIB_PATH" + +# this is not available in the newer NDK +# $ANDROID_STRIP_TOOL --strip-unneeded --strip-debug -o "$STRIPPED_LIB_PATH" "$UNSTRIPPED_LIB_PATH" + + # Set permissions so that the build server can clean the outputs afterwards + chmod 777 "$STRIPPED_LIB_PATH" + + rm -rf build +done + +# ensure `git clean -fd` does not require root permissions +find $GOPATH -exec chmod +rw {} \; diff --git a/wireguard/libamnezia/container/container.go b/wireguard/libamnezia/container/container.go new file mode 100644 index 0000000000..226616dbc0 --- /dev/null +++ b/wireguard/libamnezia/container/container.go @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: Apache-2.0 + * + * Copyright (C) 2017-2019 Jason A. Donenfeld . All Rights Reserved. + * Copyright (C) 2020 Mullvad VPN AB. All Rights Reserved. + * Copyright 2024 - Nym Technologies SA + */ + +package container + +import ( + "errors" + "math" +) + +// Generic index-based memory storage +type Container[Context any] struct { + tunnels map[int32]Context +} + +func New[Context any]() Container[Context] { + return Container[Context]{ + tunnels: make(map[int32]Context), + } +} + +func (wself *Container[Context]) Insert(context Context) (int32, error) { + var i int32 + for i = 0; i < math.MaxInt32; i++ { + if _, exists := wself.tunnels[i]; !exists { + break + } + } + + if i == math.MaxInt32 { + return 0, errors.New("container is full") + } + + wself.tunnels[i] = context + return i, nil +} + +func (wself *Container[Context]) Get(handle int32) (*Context, error) { + context, ok := wself.tunnels[handle] + if !ok { + return nil, errors.New("invalid context handle") + } + return &context, nil +} + +func (wself *Container[Context]) Remove(handle int32) (*Context, error) { + context, ok := wself.tunnels[handle] + if !ok { + return nil, errors.New("invalid context handle") + } + delete(wself.tunnels, handle) + return &context, nil +} + +func (wself *Container[Context]) ForEach(callback func(Context)) { + for _, tunnel := range wself.tunnels { + callback(tunnel) + } +} diff --git a/wireguard/libamnezia/go.mod b/wireguard/libamnezia/go.mod new file mode 100644 index 0000000000..56da2bbe59 --- /dev/null +++ b/wireguard/libamnezia/go.mod @@ -0,0 +1,21 @@ +module github.com/nymtech/nym-vpn-client/wireguard/libwg + +go 1.22.3 + +toolchain go1.23.1 + +require ( + // golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 + github.com/amnezia-vpn/amneziawg-go v0.2.12 + golang.org/x/sys v0.18.0 + gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259 +) + +require ( + github.com/google/btree v1.0.1 // indirect + github.com/tevino/abool/v2 v2.1.0 // indirect + golang.org/x/crypto v0.21.0 // indirect + golang.org/x/net v0.21.0 // indirect + golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect + golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect +) diff --git a/wireguard/libamnezia/go.sum b/wireguard/libamnezia/go.sum new file mode 100644 index 0000000000..42f90fd40d --- /dev/null +++ b/wireguard/libamnezia/go.sum @@ -0,0 +1,18 @@ +github.com/amnezia-vpn/amneziawg-go v0.2.12 h1:CxIQETy5kZ0ip/dFBpmnDxAcS/KuIQaJkOxDv5OQhVI= +github.com/amnezia-vpn/amneziawg-go v0.2.12/go.mod h1:d7WpNfzCRLy7ufGElJBYpD58WRmNjyLyt3IDHPY8AmM= +github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= +github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= +github.com/tevino/abool/v2 v2.1.0 h1:7w+Vf9f/5gmKT4m4qkayb33/92M+Um45F2BkHOR+L/c= +github.com/tevino/abool/v2 v2.1.0/go.mod h1:+Lmlqk6bHDWHqN1cbxqhwEAwMPXgc8I1SDEamtseuXY= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44= +golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +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= +gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259 h1:TbRPT0HtzFP3Cno1zZo7yPzEEnfu8EjLfl6IU9VfqkQ= +gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259/go.mod h1:AVgIgHMwK63XvmAzWG9vLQ41YnVHN0du0tEC46fI7yY= diff --git a/wireguard/libamnezia/goruntime-boottime-over-monotonic-darwin.diff b/wireguard/libamnezia/goruntime-boottime-over-monotonic-darwin.diff new file mode 100644 index 0000000000..2f7f54edd0 --- /dev/null +++ b/wireguard/libamnezia/goruntime-boottime-over-monotonic-darwin.diff @@ -0,0 +1,61 @@ +From 516dc0c15ff1ab781e0677606b5be72919251b3e Mon Sep 17 00:00:00 2001 +From: "Jason A. Donenfeld" +Date: Wed, 9 Dec 2020 14:07:06 +0100 +Subject: [PATCH] runtime: use libc_mach_continuous_time in nanotime on Darwin + +This makes timers account for having expired while a computer was +asleep, which is quite common on mobile devices. Note that +continuous_time absolute_time, except that it takes into account +time spent in suspend. + +Fixes #24595 + +Change-Id: Ia3282e8bd86f95ad2b76427063e60a005563f4eb +--- + src/runtime/sys_darwin.go | 2 +- + src/runtime/sys_darwin_amd64.s | 2 +- + src/runtime/sys_darwin_arm64.s | 2 +- + 3 files changed, 3 insertions(+), 3 deletions(-) + +diff --git a/src/runtime/sys_darwin.go b/src/runtime/sys_darwin.go +index 4a3f2fc453..4a69403b32 100644 +--- a/src/runtime/sys_darwin.go ++++ b/src/runtime/sys_darwin.go +@@ -440,7 +440,7 @@ func setNonblock(fd int32) { + //go:cgo_import_dynamic libc_usleep usleep "/usr/lib/libSystem.B.dylib" + + //go:cgo_import_dynamic libc_mach_timebase_info mach_timebase_info "/usr/lib/libSystem.B.dylib" +-//go:cgo_import_dynamic libc_mach_absolute_time mach_absolute_time "/usr/lib/libSystem.B.dylib" ++//go:cgo_import_dynamic libc_mach_continuous_time mach_continuous_time "/usr/lib/libSystem.B.dylib" + //go:cgo_import_dynamic libc_clock_gettime clock_gettime "/usr/lib/libSystem.B.dylib" + //go:cgo_import_dynamic libc_sigaction sigaction "/usr/lib/libSystem.B.dylib" + //go:cgo_import_dynamic libc_pthread_sigmask pthread_sigmask "/usr/lib/libSystem.B.dylib" +diff --git a/src/runtime/sys_darwin_amd64.s b/src/runtime/sys_darwin_amd64.s +index 630fb5df64..4499c88802 100644 +--- a/src/runtime/sys_darwin_amd64.s ++++ b/src/runtime/sys_darwin_amd64.s +@@ -114,7 +114,7 @@ TEXT runtime·nanotime_trampoline(SB),NOSPLIT,$0 + PUSHQ BP + MOVQ SP, BP + MOVQ DI, BX +- CALL libc_mach_absolute_time(SB) ++ CALL libc_mach_continuous_time(SB) + MOVQ AX, 0(BX) + MOVL timebase<>+machTimebaseInfo_numer(SB), SI + MOVL timebase<>+machTimebaseInfo_denom(SB), DI // atomic read +diff --git a/src/runtime/sys_darwin_arm64.s b/src/runtime/sys_darwin_arm64.s +index 96d2ed1076..f046545395 100644 +--- a/src/runtime/sys_darwin_arm64.s ++++ b/src/runtime/sys_darwin_arm64.s +@@ -143,7 +143,7 @@ GLOBL timebase<>(SB),NOPTR,$(machTimebaseInfo__size) + + TEXT runtime·nanotime_trampoline(SB),NOSPLIT,$40 + MOVD R0, R19 +- BL libc_mach_absolute_time(SB) ++ BL libc_mach_continuous_time(SB) + MOVD R0, 0(R19) + MOVW timebase<>+machTimebaseInfo_numer(SB), R20 + MOVD $timebase<>+machTimebaseInfo_denom(SB), R21 +-- +2.30.1 + diff --git a/wireguard/libamnezia/goruntime-boottime-over-monotonic.diff b/wireguard/libamnezia/goruntime-boottime-over-monotonic.diff new file mode 100644 index 0000000000..73eb999a95 --- /dev/null +++ b/wireguard/libamnezia/goruntime-boottime-over-monotonic.diff @@ -0,0 +1,170 @@ +From 61f3ae8298d1c503cbc31539e0f3a73446c7db9d Mon Sep 17 00:00:00 2001 +From: "Jason A. Donenfeld" +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/wireguard/libamnezia/libwg.go b/wireguard/libamnezia/libwg.go new file mode 100644 index 0000000000..a519822002 --- /dev/null +++ b/wireguard/libamnezia/libwg.go @@ -0,0 +1,74 @@ +/* SPDX-License-Identifier: Apache-2.0 + * + * Copyright (C) 2017-2019 Jason A. Donenfeld . All Rights Reserved. + * Copyright (C) 2021 Mullvad VPN AB. All Rights Reserved. + * Copyright (C) 2024 Nym Technologies SA . All Rights Reserved. + */ + +package main + +// #include +import "C" + +import ( + "bufio" + "bytes" + "runtime" + "unsafe" + + "github.com/nymtech/nym-vpn-client/wireguard/libwg/container" + "github.com/amnezia-vpn/amneziawg-go/device" +) + +const ( + ERROR_GENERAL_FAILURE = -1 + ERROR_INTERMITTENT_FAILURE = -2 +) + +type TunnelContext struct { + Device *device.Device + Logger *device.Logger +} + +var tunnels container.Container[TunnelContext] + +func init() { + tunnels = container.New[TunnelContext]() +} + +//export wgTurnOff +func wgTurnOff(tunnelHandle int32) { + { + tunnel, err := tunnels.Remove(tunnelHandle) + if err != nil { + return + } + tunnel.Device.Close() + } + // Calling twice convinces the GC to release NOW. + runtime.GC() + runtime.GC() +} + +//export wgGetConfig +func wgGetConfig(tunnelHandle int32) *C.char { + tunnel, err := tunnels.Get(tunnelHandle) + if err != nil { + return nil + } + settings := new(bytes.Buffer) + writer := bufio.NewWriter(settings) + if err := tunnel.Device.IpcGetOperation(writer); err != nil { + tunnel.Logger.Errorf("Failed to get config for tunnel: %s\n", err) + return nil + } + writer.Flush() + return C.CString(settings.String()) +} + +//export wgFreePtr +func wgFreePtr(ptr unsafe.Pointer) { + C.free(ptr) +} + +func main() {} diff --git a/wireguard/libamnezia/libwg_android.go b/wireguard/libamnezia/libwg_android.go new file mode 100644 index 0000000000..9470ed378b --- /dev/null +++ b/wireguard/libamnezia/libwg_android.go @@ -0,0 +1,103 @@ +/* SPDX-License-Identifier: Apache-2.0 + * + * Copyright (C) 2017-2019 Jason A. Donenfeld . All Rights Reserved. + * Copyright (C) 2021 Mullvad VPN AB. All Rights Reserved. + * Copyright (C) 2024 Nym Technologies SA . All Rights Reserved. + */ + +package main + +import ( + "C" + "bufio" + "strings" + "unsafe" + + "golang.org/x/sys/unix" + + "github.com/amnezia-vpn/amneziawg-go/conn" + "github.com/amnezia-vpn/amneziawg-go/device" + "github.com/amnezia-vpn/amneziawg-go/tun" + + "github.com/nymtech/nym-vpn-client/wireguard/libwg/logging" +) + +// 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 = unsafe.Pointer + +//export wgTurnOn +func wgTurnOn(cSettings *C.char, fd int, logSink LogSink, logContext LogContext) int32 { + logger := logging.NewLogger(logSink, logContext) + + if cSettings == nil { + logger.Errorf("cSettings is null\n") + return ERROR_GENERAL_FAILURE + } + settings := C.GoString(cSettings) + + 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() + + context := TunnelContext{ + Device: device, + Logger: logger, + } + + handle, err := tunnels.Insert(context) + if err != nil { + logger.Errorf("%s\n", err) + device.Close() + return ERROR_GENERAL_FAILURE + } + + return handle +} + +//export wgGetSocketV4 +func wgGetSocketV4(tunnelHandle int32) int32 { + tunnel, err := tunnels.Get(tunnelHandle) + if err != nil { + return ERROR_GENERAL_FAILURE + } + peek := tunnel.Device.Bind().(conn.PeekLookAtSocketFd) + fd, err := peek.PeekLookAtSocketFd4() + if err != nil { + return ERROR_GENERAL_FAILURE + } + return int32(fd) +} + +//export wgGetSocketV6 +func wgGetSocketV6(tunnelHandle int32) int32 { + tunnel, err := tunnels.Get(tunnelHandle) + if err != nil { + return ERROR_GENERAL_FAILURE + } + peek := tunnel.Device.Bind().(conn.PeekLookAtSocketFd) + fd, err := peek.PeekLookAtSocketFd6() + if err != nil { + return ERROR_GENERAL_FAILURE + } + return int32(fd) +} diff --git a/wireguard/libamnezia/libwg_default.go b/wireguard/libamnezia/libwg_default.go new file mode 100644 index 0000000000..050cdb8799 --- /dev/null +++ b/wireguard/libamnezia/libwg_default.go @@ -0,0 +1,97 @@ +//go:build (darwin || linux) && !android && !ios + +/* SPDX-License-Identifier: Apache-2.0 + * + * Copyright (C) 2017-2019 Jason A. Donenfeld . All Rights Reserved. + * Copyright (C) 2021 Mullvad VPN AB. All Rights Reserved. + * Copyright (C) 2024 Nym Technologies SA . All Rights Reserved. + */ + +package main + +// #include +import "C" +import ( + "bufio" + "os" + "strings" + "unsafe" + + "github.com/amnezia-vpn/amneziawg-go/conn" + "github.com/amnezia-vpn/amneziawg-go/device" + "github.com/amnezia-vpn/amneziawg-go/tun" + + "github.com/nymtech/nym-vpn-client/wireguard/libwg/logging" +) + +// 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 = unsafe.Pointer + +//export wgTurnOn +func wgTurnOn(mtu int, cSettings *C.char, fd int, logSink LogSink, logContext LogContext) int32 { + logger := logging.NewLogger(logSink, logContext) + + if cSettings == nil { + logger.Errorf("cSettings is null\n") + return ERROR_GENERAL_FAILURE + } + settings := C.GoString(cSettings) + + file := os.NewFile(uintptr(fd), "") + tunDevice, err := tun.CreateTUNFromFile(file, mtu) + if err != nil { + logger.Errorf("%s\n", err) + if err.Error() == "bad file descriptor" { + return ERROR_INTERMITTENT_FAILURE + } + return ERROR_GENERAL_FAILURE + } + + device := device.NewDevice(tunDevice, conn.NewDefaultBind(), logger) + + setErr := device.IpcSetOperation(bufio.NewReader(strings.NewReader(settings))) + if setErr != nil { + logger.Errorf("%s\n", setErr) + device.Close() + return ERROR_INTERMITTENT_FAILURE + } + + device.Up() + + context := TunnelContext{ + Device: device, + Logger: logger, + } + + handle, err := tunnels.Insert(context) + if err != nil { + logger.Errorf("%s\n", err) + device.Close() + return ERROR_GENERAL_FAILURE + } + + return handle +} + +//export wgSetConfig +func wgSetConfig(tunnelHandle int32, cSettings *C.char) int32 { + tunnel, err := tunnels.Get(tunnelHandle) + if err != nil { + return ERROR_GENERAL_FAILURE + } + if cSettings == nil { + tunnel.Logger.Errorf("cSettings is null\n") + return ERROR_GENERAL_FAILURE + } + settings := C.GoString(cSettings) + + setError := tunnel.Device.IpcSetOperation(bufio.NewReader(strings.NewReader(settings))) + if setError != nil { + tunnel.Logger.Errorf("Failed to set device configuration\n") + tunnel.Logger.Errorf("%s\n", setError) + return ERROR_GENERAL_FAILURE + } + return 0 +} diff --git a/wireguard/libamnezia/libwg_ios.go b/wireguard/libamnezia/libwg_ios.go new file mode 100644 index 0000000000..d45ef6a4cd --- /dev/null +++ b/wireguard/libamnezia/libwg_ios.go @@ -0,0 +1,97 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2018-2019 Jason A. Donenfeld . All Rights Reserved. + * Copyright (C) 2024 Nym Technologies SA . All Rights Reserved. + */ + +package main + +import "C" +import ( + "os" + "time" + "unsafe" + + "github.com/nymtech/nym-vpn-client/wireguard/libwg/logging" + "golang.org/x/sys/unix" + "github.com/amnezia-vpn/amneziawg-go/conn" + "github.com/amnezia-vpn/amneziawg-go/device" + "github.com/amnezia-vpn/amneziawg-go/tun" +) + +// 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 = unsafe.Pointer + +//export wgTurnOn +func wgTurnOn(settings *C.char, tunFd int32, logSink LogSink, logContext LogContext) int32 { + logger := logging.NewLogger(logSink, logContext) + + dupTunFd, err := unix.Dup(int(tunFd)) + if err != nil { + logger.Errorf("Unable to dup tun fd: %v", err) + return ERROR_GENERAL_FAILURE + } + + err = unix.SetNonblock(dupTunFd, true) + if err != nil { + logger.Errorf("Unable to set tun fd as non blocking: %v", err) + unix.Close(dupTunFd) + return ERROR_GENERAL_FAILURE + } + tun, err := tun.CreateTUNFromFile(os.NewFile(uintptr(dupTunFd), "/dev/tun"), 0) + if err != nil { + logger.Errorf("Unable to create new tun device from fd: %v", err) + unix.Close(dupTunFd) + return ERROR_INTERMITTENT_FAILURE + } + logger.Verbosef("Attaching to interface") + dev := device.NewDevice(tun, conn.NewStdNetBind(), logger) + + err = dev.IpcSet(C.GoString(settings)) + if err != nil { + logger.Errorf("Unable to set IPC settings: %v", err) + unix.Close(dupTunFd) + return ERROR_GENERAL_FAILURE + } + + dev.DisableSomeRoamingForBrokenMobileSemantics() + dev.Up() + + logger.Verbosef("Device started") + + context := TunnelContext{ + Device: dev, + Logger: logger, + } + + handle, err := tunnels.Insert(context) + if err != nil { + logger.Errorf("%s", err) + dev.Close() + return ERROR_GENERAL_FAILURE + } + + return handle +} + +//export wgBumpSockets +func wgBumpSockets(tunnelHandle int32) { + tunnel, err := tunnels.Get(tunnelHandle) + if err != nil { + return + } + go func() { + for i := 0; i < 10; i++ { + err := tunnel.Device.BindUpdate() + if err == nil { + tunnel.Device.SendKeepalivesToPeersWithCurrentKeypair() + return + } + tunnel.Logger.Errorf("Unable to update bind, try %d: %v", i+1, err) + time.Sleep(time.Second / 2) + } + tunnel.Logger.Errorf("Gave up trying to update bind; tunnel is likely dysfunctional") + }() +} diff --git a/wireguard/libamnezia/libwg_mobile.go b/wireguard/libamnezia/libwg_mobile.go new file mode 100644 index 0000000000..d80d4b0126 --- /dev/null +++ b/wireguard/libamnezia/libwg_mobile.go @@ -0,0 +1,40 @@ +//go:build ios || android + +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2018-2019 Jason A. Donenfeld . All Rights Reserved. + * Copyright (C) 2024 Nym Technologies SA . All Rights Reserved. + */ + +package main + +import "C" + +import ( + "bufio" + "strings" +) + +//export wgSetConfig +func wgSetConfig(tunnelHandle int32, cSettings *C.char) int32 { + tunnel, err := tunnels.Get(tunnelHandle) + if err != nil { + return ERROR_GENERAL_FAILURE + } + if cSettings == nil { + tunnel.Logger.Errorf("cSettings is null\n") + return ERROR_GENERAL_FAILURE + } + settings := C.GoString(cSettings) + + err = tunnel.Device.IpcSetOperation(bufio.NewReader(strings.NewReader(settings))) + if err != nil { + tunnel.Logger.Errorf("Failed to set device configuration\n") + tunnel.Logger.Errorf("%s\n", err) + return ERROR_GENERAL_FAILURE + } + + tunnel.Device.DisableSomeRoamingForBrokenMobileSemantics() + + return 0 +} diff --git a/wireguard/libamnezia/libwg_windows.go b/wireguard/libamnezia/libwg_windows.go new file mode 100644 index 0000000000..0a13431dd6 --- /dev/null +++ b/wireguard/libamnezia/libwg_windows.go @@ -0,0 +1,153 @@ +/* SPDX-License-Identifier: Apache-2.0 + * + * Copyright (C) 2017-2019 Jason A. Donenfeld . All Rights Reserved. + * Copyright (C) 2021 Mullvad VPN AB. All Rights Reserved. + * Copyright (C) 2024 Nym Technologies SA . All Rights Reserved. + */ + +package main + +// #include +import "C" + +import ( + "bufio" + "strings" + "unsafe" + + "golang.org/x/sys/windows" + + "github.com/amnezia-vpn/amneziawg-go/conn" + "github.com/amnezia-vpn/amneziawg-go/device" + "github.com/amnezia-vpn/amneziawg-go/tun" + + "github.com/nymtech/nym-vpn-client/wireguard/libwg/logging" +) + +// 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 = unsafe.Pointer + +//export wgTurnOn +func wgTurnOn(cIfaceName *C.char, mtu int, cSettings *C.char, cIfaceNameOut **C.char, cLuidOut *uint64, logSink LogSink, logContext LogContext) int32 { + logger := logging.NewLogger(logSink, 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{0xafe43773, 0xe1f8, 0x4ebb, [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 := TunnelContext{ + 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 handle +} + +//export wgSetConfig +func wgSetConfig(tunnelHandle int32, cSettings *C.char) int32 { + tunnel, err := tunnels.Get(tunnelHandle) + if err != nil { + return ERROR_GENERAL_FAILURE + } + if cSettings == nil { + tunnel.Logger.Errorf("cSettings is null\n") + return ERROR_GENERAL_FAILURE + } + settings := C.GoString(cSettings) + + setError := tunnel.Device.IpcSetOperation(bufio.NewReader(strings.NewReader(settings))) + if setError != nil { + tunnel.Logger.Errorf("Failed to set device configuration\n") + tunnel.Logger.Errorf("%s\n", setError) + return ERROR_GENERAL_FAILURE + } + return 0 +} + +//export wgRebindTunnelSocket +func wgRebindTunnelSocket(family uint16, interfaceIndex uint32) { + tunnels.ForEach(func(tunnel TunnelContext) { + 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) + } + } + }) +} diff --git a/wireguard/libamnezia/logging/logging.go b/wireguard/libamnezia/logging/logging.go new file mode 100644 index 0000000000..6d81ccf7a0 --- /dev/null +++ b/wireguard/libamnezia/logging/logging.go @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: Apache-2.0 + * + * Copyright (C) 2017-2019 Jason A. Donenfeld . All Rights Reserved. + * Copyright (C) 2021 Mullvad VPN AB. All Rights Reserved. + */ + +package logging + +// #include +// #include +// #ifndef WIN32 +// #define __stdcall +// #endif +// typedef void (__stdcall *LogSink)(unsigned int, const char *, void *); +// static void callLogSink(void *logSink, int level, const char *message, void *context) +// { +// ((LogSink)logSink)((unsigned int)level, message, context); +// } +import "C" + +import ( + "log" + "unsafe" + + "github.com/amnezia-vpn/amneziawg-go/device" +) + +// Define type aliases. +type LogSink = unsafe.Pointer +type LogContext = unsafe.Pointer + +type Logger struct { + sink LogSink + context LogContext + level C.int +} + +func (l *Logger) Write(message []byte) (int, error) { + msg := C.CString(string(message)) + C.callLogSink(l.sink, l.level, msg, l.context) + C.free(unsafe.Pointer(msg)) + return len(message), nil +} + +func NewLogger(logSink LogSink, logContext LogContext) *device.Logger { + logger := new(device.Logger) + + logger.Verbosef = log.New( + &Logger{sink: logSink, context: logContext, level: device.LogLevelVerbose}, + "", + 0, + ).Printf + logger.Errorf = log.New( + &Logger{sink: logSink, context: logContext, level: device.LogLevelError}, + "", + 0, + ).Printf + + return logger +} diff --git a/wireguard/libamnezia/netstack_android.go b/wireguard/libamnezia/netstack_android.go new file mode 100644 index 0000000000..1c2b8d2690 --- /dev/null +++ b/wireguard/libamnezia/netstack_android.go @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2018-2019 Jason A. Donenfeld . All Rights Reserved. + * Copyright (C) 2024 Nym Technologies SA . All Rights Reserved. + */ + +package main + +import "C" + +import "github.com/amnezia-vpn/amneziawg-go/conn" + +//export wgNetGetSocketV4 +func wgNetGetSocketV4(tunnelHandle int32) int32 { + tunnel, err := netTunnelHandles.Get(tunnelHandle) + if err != nil { + return ERROR_GENERAL_FAILURE + } + peek := tunnel.Device.Bind().(conn.PeekLookAtSocketFd) + fd, err := peek.PeekLookAtSocketFd4() + if err != nil { + return ERROR_GENERAL_FAILURE + } + return int32(fd) +} + +//export wgNetGetSocketV6 +func wgNetGetSocketV6(tunnelHandle int32) int32 { + tunnel, err := netTunnelHandles.Get(tunnelHandle) + if err != nil { + return ERROR_GENERAL_FAILURE + } + peek := tunnel.Device.Bind().(conn.PeekLookAtSocketFd) + fd, err := peek.PeekLookAtSocketFd6() + if err != nil { + return ERROR_GENERAL_FAILURE + } + return int32(fd) +} diff --git a/wireguard/libamnezia/netstack_mobile.go b/wireguard/libamnezia/netstack_mobile.go new file mode 100644 index 0000000000..b1f197c8f6 --- /dev/null +++ b/wireguard/libamnezia/netstack_mobile.go @@ -0,0 +1,200 @@ +//go:build ios || android + +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2018-2019 Jason A. Donenfeld . All Rights Reserved. + * Copyright (C) 2024 Nym Technologies SA . All Rights Reserved. + */ + +package main + +import "C" + +import ( + "net/netip" + "strings" + + "github.com/nymtech/nym-vpn-client/wireguard/libwg/container" + "github.com/nymtech/nym-vpn-client/wireguard/libwg/logging" + "github.com/nymtech/nym-vpn-client/wireguard/libwg/udp_forwarder" + "github.com/amnezia-vpn/amneziawg-go/conn" + "github.com/amnezia-vpn/amneziawg-go/device" + "github.com/amnezia-vpn/amneziawg-go/tun/netstack" +) + +type netTunnelHandle struct { + *device.Device + *netstack.Net + *device.Logger +} + +var netTunnelHandles container.Container[netTunnelHandle] +var udpForwarders container.Container[*udp_forwarder.UDPForwarder] + +func init() { + netTunnelHandles = container.New[netTunnelHandle]() + udpForwarders = container.New[*udp_forwarder.UDPForwarder]() +} + +//export wgNetTurnOn +func wgNetTurnOn(localAddresses *C.char, dnsAddresses *C.char, mtu int, settings *C.char, logSink LogSink, logContext LogContext) int32 { + logger := logging.NewLogger(logSink, logContext) + + // Parse comma separated list of IP addresses + tunAddrs, err := parseIPAddrs(C.GoString(localAddresses)) + if err != nil { + logger.Errorf("Failed to parse local addresses: %v", err) + return ERROR_GENERAL_FAILURE + } + + // Parse comma separated list of DNS addresses + dnsAddrs, err := parseIPAddrs(C.GoString(dnsAddresses)) + if err != nil { + logger.Errorf("Failed to parse dns addresses: %v", err) + return ERROR_GENERAL_FAILURE + } + + tun, tnet, err := netstack.CreateNetTUN(tunAddrs, dnsAddrs, mtu) + if err != nil { + logger.Errorf("Failed to create net tun: %v", err) + return ERROR_GENERAL_FAILURE + } + + dev := device.NewDevice( + tun, + conn.NewDefaultBind(), + logger, + ) + if dev == nil { + logger.Errorf("Failed to create device") + return ERROR_GENERAL_FAILURE + } + + err = dev.IpcSet(C.GoString(settings)) + if err != nil { + logger.Errorf("Unable to set IPC settings: %v", err) + dev.Close() + return ERROR_GENERAL_FAILURE + } + + dev.DisableSomeRoamingForBrokenMobileSemantics() + err = dev.Up() + if err != nil { + logger.Errorf("Failed to set device state to Up: %v", err) + dev.Close() + return ERROR_GENERAL_FAILURE + } + + logger.Verbosef("Net device started") + + i, err := netTunnelHandles.Insert(netTunnelHandle{dev, tnet, logger}) + if err != nil { + logger.Errorf("Failed to store tunnel: %v", err) + dev.Close() + return ERROR_GENERAL_FAILURE + } + + return i +} + +//export wgNetTurnOff +func wgNetTurnOff(tunnelHandle int32) { + dev, err := netTunnelHandles.Remove(tunnelHandle) + if err != nil { + return + } + dev.Close() +} + +//export wgNetSetConfig +func wgNetSetConfig(tunnelHandle int32, settings *C.char) int64 { + dev, err := netTunnelHandles.Get(tunnelHandle) + if err != nil { + return 0 + } + err = dev.IpcSet(C.GoString(settings)) + if err != nil { + dev.Errorf("Unable to set IPC settings: %v", err) + if ipcErr, ok := err.(*device.IPCError); ok { + return ipcErr.ErrorCode() + } + return ERROR_GENERAL_FAILURE + } + + dev.DisableSomeRoamingForBrokenMobileSemantics() + + return 0 +} + +//export wgNetGetConfig +func wgNetGetConfig(tunnelHandle int32) *C.char { + device, err := netTunnelHandles.Get(tunnelHandle) + if err != nil { + return nil + } + settings, err := device.IpcGet() + if err != nil { + return nil + } + return C.CString(settings) +} + +//export wgNetOpenConnectionThroughTunnel +func wgNetOpenConnectionThroughTunnel(entryTunnelHandle int32, listenPort uint16, clientPort uint16, exitEndpointStr *C.char, logSink LogSink, logContext LogContext) int32 { + logger := logging.NewLogger(logSink, logContext) + + dev, err := netTunnelHandles.Get(entryTunnelHandle) + if err != nil { + dev.Errorf("Invalid tunnel handle: %d", entryTunnelHandle) + return ERROR_GENERAL_FAILURE + } + + exitEndpoint, err := netip.ParseAddrPort(C.GoString(exitEndpointStr)) + if err != nil { + dev.Errorf("Failed to parse endpoint: %v", err) + return ERROR_GENERAL_FAILURE + } + + forwarderConfig := udp_forwarder.UDPForwarderConfig{ + ListenPort: listenPort, + ClientPort: clientPort, + ExitEndpoint: exitEndpoint, + } + + udpForwarder, err := udp_forwarder.New(forwarderConfig, dev.Net, logger) + if err != nil { + dev.Errorf("Failed to create udp forwarder: %v", err) + return ERROR_GENERAL_FAILURE + } + + forwarderHandle, err := udpForwarders.Insert(udpForwarder) + if err != nil { + dev.Errorf("Failed to store udp forwarder: %v", err) + udpForwarder.Close() + return ERROR_GENERAL_FAILURE + } + + return forwarderHandle +} + +//export wgNetCloseConnectionThroughTunnel +func wgNetCloseConnectionThroughTunnel(udpForwarderHandle int32) { + udpForwarder, err := udpForwarders.Remove(udpForwarderHandle) + if err != nil { + return + } + (*udpForwarder).Close() +} + +// Parse a list of comma-separated IP addresses into array of netip.Addr structs. +func parseIPAddrs(input string) ([]netip.Addr, error) { + addrs := []netip.Addr{} + for _, s := range strings.Split(input, ",") { + addr, err := netip.ParseAddr(strings.TrimSpace(s)) + if err != nil { + return addrs, err + } + addrs = append(addrs, addr) + } + return addrs, nil +} diff --git a/wireguard/libamnezia/udp_forwarder/udp_forwarder.go b/wireguard/libamnezia/udp_forwarder/udp_forwarder.go new file mode 100644 index 0000000000..54217ece85 --- /dev/null +++ b/wireguard/libamnezia/udp_forwarder/udp_forwarder.go @@ -0,0 +1,194 @@ +/* SPDX-License-Identifier: GPL-3.0-only + * + * Copyright 2024 - Nym Technologies SA + */ + +package udp_forwarder + +import ( + "net" + "net/netip" + "sync" + "time" + + "github.com/amnezia-vpn/amneziawg-go/device" + "github.com/amnezia-vpn/amneziawg-go/tun/netstack" + "gvisor.dev/gvisor/pkg/tcpip/adapters/gonet" +) + +const UDP_WRITE_TIMEOUT = time.Duration(5) * time.Second +const MAX_UDP_DATAGRAM_LEN = 65535 + +type UDPForwarderConfig struct { + // Listen port for incoming WireGuard traffic. + // For IPv4 exit endpoint, the listening port is bound to 127.0.0.1, for IPv6 it's ::1. + ListenPort uint16 + + // Client port on loopback from which the incoming WireGuard connection will be received. + // Only packets from this port will be passed through to the exit endpoint. + ClientPort uint16 + + // Exit endpoint which will receive the raw WireGuard packets received on the listen port. + // The connection to exit endpoint is established over the entry tunnel, thus it creates + // a tunnel inside of tunnel. + ExitEndpoint netip.AddrPort +} + +// UDP forwarder that creates a bidirectional connection between a local and exit UDP endpoints +// over the netstack-based WireGuard tunnel. +type UDPForwarder struct { + // Logger. + logger *device.Logger + + // Netstack tunnel wrapping the inbound WireGuard traffic. + tnet *netstack.Net + + // UDP listener that receives inbound WireGuard traffic destined to exit endpoint. + listener *net.UDPConn + + // Outbound connection to the exit endpoint over the entry tunnel. + outbound *gonet.UDPConn + + // Wait group used to signal when all goroutines have finished execution. + waitGroup *sync.WaitGroup +} + +func New(config UDPForwarderConfig, tnet *netstack.Net, logger *device.Logger) (*UDPForwarder, error) { + var listenAddr *net.UDPAddr + var clientAddr *net.UDPAddr + + // Use the same ip protocol family as exit endpoint. + if config.ExitEndpoint.Addr().Is4() { + loopback := netip.AddrFrom4([4]byte{127, 0, 0, 1}) + listenAddr = net.UDPAddrFromAddrPort(netip.AddrPortFrom(loopback, config.ListenPort)) + clientAddr = net.UDPAddrFromAddrPort(netip.AddrPortFrom(loopback, config.ClientPort)) + } else { + listenAddr = net.UDPAddrFromAddrPort(netip.AddrPortFrom(netip.IPv6Loopback(), config.ListenPort)) + clientAddr = net.UDPAddrFromAddrPort(netip.AddrPortFrom(netip.IPv6Loopback(), config.ClientPort)) + } + + listener, err := net.ListenUDP("udp", listenAddr) + if err != nil { + return nil, err + } + + outbound, err := tnet.DialUDPAddrPort(netip.AddrPort{}, config.ExitEndpoint) + if err != nil { + return nil, err + } + + waitGroup := &sync.WaitGroup{} + wrapper := &UDPForwarder{ + logger, + tnet, + listener, + outbound, + waitGroup, + } + + waitGroup.Add(2) + go wrapper.RoutineHandleInbound(listener, outbound, clientAddr) + go wrapper.RoutineHandleOutbound(listener, outbound, clientAddr) + + return wrapper, nil +} + +func (w *UDPForwarder) Close() { + // Close all connections. This should release any blocking ReadFromUDP() calls. + w.listener.Close() + w.outbound.Close() + + // Wait for all routines to complete. + w.waitGroup.Wait() +} + +func (w *UDPForwarder) Wait() { + w.waitGroup.Wait() +} + +func (w *UDPForwarder) RoutineHandleInbound(inbound *net.UDPConn, outbound *gonet.UDPConn, clientAddr *net.UDPAddr) { + defer w.waitGroup.Done() + + inboundBuffer := make([]byte, MAX_UDP_DATAGRAM_LEN) + + w.logger.Verbosef("udpforwarder(inbound): listening on %s", inbound.LocalAddr().String()) + defer w.logger.Verbosef("udpforwarder(inbound): closed") + + for { + // Receive the WireGuard packet from local port + bytesRead, senderAddr, err := inbound.ReadFromUDP(inboundBuffer) + if err != nil { + w.logger.Errorf("udpforwarder(inbound): %s", err.Error()) + // todo: handle error + return + } + + // Drop packet from unknown sender. + if !senderAddr.IP.IsLoopback() || senderAddr.Port != clientAddr.Port { + w.logger.Verbosef("udpforwarder(inbound): drop packet from unknown sender: %s, expected: %s.", senderAddr.String(), clientAddr.String()) + continue + } + + // Set write timeout for outbound. + deadline := time.Now().Add(UDP_WRITE_TIMEOUT) + err = outbound.SetWriteDeadline(deadline) + if err != nil { + w.logger.Errorf("udpforwarder(inbound): %s", err.Error()) + // todo: handle error + continue + } + + // Forward the packet over the outbound connection via another WireGuard tunnel. + _, err = outbound.Write(inboundBuffer[:bytesRead]) + if err != nil { + w.logger.Errorf("udpforwarder(inbound): %s", err.Error()) + // todo: handle error + continue + } + } +} + +func (w *UDPForwarder) RoutineHandleOutbound(inbound *net.UDPConn, outbound *gonet.UDPConn, clientAddr *net.UDPAddr) { + defer w.waitGroup.Done() + + remoteAddr := outbound.RemoteAddr().(*net.UDPAddr) + w.logger.Verbosef("udpforwarder(outbound): dial %s", remoteAddr.String()) + defer w.logger.Verbosef("udpforwarder(outbound): closed") + + outboundBuffer := make([]byte, MAX_UDP_DATAGRAM_LEN) + + for { + // Receive WireGuard packet from remote server. + bytesRead, senderAddr, err := outbound.ReadFrom(outboundBuffer) + if err != nil { + w.logger.Errorf("udpforwarder(outbound): %s", err.Error()) + // todo: handle error + return + } + // Cast net.Addr to net.UDPAddr + senderUDPAddr := senderAddr.(*net.UDPAddr) + + // Drop packet from unknown sender. + if !senderUDPAddr.IP.Equal(remoteAddr.IP) || senderUDPAddr.Port != remoteAddr.Port { + w.logger.Verbosef("udpforwarder(outbound): drop packet from unknown sender: %s, expected: %s", senderUDPAddr.String(), remoteAddr.String()) + continue + } + + // Set write timeout for inbound. + deadline := time.Now().Add(UDP_WRITE_TIMEOUT) + err = inbound.SetWriteDeadline(deadline) + if err != nil { + w.logger.Errorf("udpforwarder(outbound): %s", err.Error()) + // todo: handle error + continue + } + + // Forward packet from remote to local client. + _, err = inbound.WriteToUDP(outboundBuffer[:bytesRead], clientAddr) + if err != nil { + w.logger.Errorf("udpforwarder(outbound): %s", err.Error()) + // todo: handle error + continue + } + } +} From 96f37a72fa2a7a2e4b1cceedf996c56afbb5424d Mon Sep 17 00:00:00 2001 From: Jack Wampler Date: Wed, 30 Oct 2024 11:14:56 -0600 Subject: [PATCH 2/6] AmneziaWG Gateway probe (#1416) --- nym-vpn-core/Cargo.lock | 1 + nym-vpn-core/Cargo.toml | 2 +- .../crates/nym-gateway-probe/README.md | 56 +++++ .../nym-gateway-probe/netstack_ping/gen.go | 4 + .../nym-gateway-probe/netstack_ping/go.mod | 7 +- .../nym-gateway-probe/netstack_ping/go.sum | 6 +- .../nym-gateway-probe/netstack_ping/impl.go | 38 +++- .../crates/nym-gateway-probe/src/lib.rs | 140 +++++++----- .../crates/nym-gateway-probe/src/netstack.rs | 8 +- .../crates/nym-gateway-probe/src/run.rs | 14 +- nym-vpn-core/crates/nym-vpnd/build.rs | 15 +- nym-vpn-core/crates/nym-wg-go/Cargo.toml | 1 + nym-vpn-core/crates/nym-wg-go/README.md | 39 ++++ nym-vpn-core/crates/nym-wg-go/src/amnezia.rs | 210 ++++++++++++++++++ nym-vpn-core/crates/nym-wg-go/src/lib.rs | 2 + nym-vpn-core/crates/nym-wg-go/src/netstack.rs | 48 +--- .../crates/nym-wg-go/src/wireguard_go.rs | 49 +--- wireguard/build-wireguard-go.sh | 64 ++++-- 18 files changed, 516 insertions(+), 188 deletions(-) create mode 100644 nym-vpn-core/crates/nym-gateway-probe/README.md create mode 100644 nym-vpn-core/crates/nym-wg-go/README.md create mode 100644 nym-vpn-core/crates/nym-wg-go/src/amnezia.rs diff --git a/nym-vpn-core/Cargo.lock b/nym-vpn-core/Cargo.lock index 766dd4cc35..19e50da430 100644 --- a/nym-vpn-core/Cargo.lock +++ b/nym-vpn-core/Cargo.lock @@ -4711,6 +4711,7 @@ dependencies = [ "base64 0.22.1", "hex", "ipnetwork", + "rand 0.8.5", "thiserror", "tracing", "x25519-dalek", diff --git a/nym-vpn-core/Cargo.toml b/nym-vpn-core/Cargo.toml index a5069f7f01..28517941ad 100644 --- a/nym-vpn-core/Cargo.toml +++ b/nym-vpn-core/Cargo.toml @@ -94,7 +94,7 @@ prost-types = "0.12.6" rand = "0.8.5" rand_chacha = "0.3.1" reqwest = { version = "0.11.27", default-features = false } -rust2go = "0.3.4" +rust2go = "0.3.16" serde = "1.0" serde_json = "1.0" sha2 = "0.10" diff --git a/nym-vpn-core/crates/nym-gateway-probe/README.md b/nym-vpn-core/crates/nym-gateway-probe/README.md new file mode 100644 index 0000000000..3389496d9b --- /dev/null +++ b/nym-vpn-core/crates/nym-gateway-probe/README.md @@ -0,0 +1,56 @@ +# Nym Gateway Probe + +Probe IPv4 and IPv6 interfaces of available gateways to check for the +set that passes a set of minumum service guarantees. + + +## Build + +These instructions assume a debian based system. Adjust accordingly for your +preffered platform. + +Install required dependencies +```sh +sudo apt install libdbus-1-dev libmnl-dev libnftnl-dev protobuf-compiler clang +``` + + +Build piece by piece +```sh +cd nym-vpn-core/ +# build the prober +cargo build -p nym-gateway-probe +``` + +You may need to adjust your `RUSTFLAGS` or `.cargo/config.toml` to ensure that +the golang wireguard library links properly. + +## Usage + +```sh +Usage: nym-gateway-probe [OPTIONS] + +Options: + -c, --config-env-file + Path pointing to an env file describing the network + -g, --gateway + The specific gateway specified by ID + -n, --no-log + Disable logging during probe + -a, --amnezia-args + Arguments to be appended to the wireguard config enabling amnezia-wg configuration + -h, --help + Print help + -V, --version + Print version +``` + +Examples + +```sh +# Run a basic probe against the node with id "qj3GgGYg..." +nym-gateway-probe -g "qj3GgGYgGZZ3HkFrtD1GU9UJ5oNXME9eD2xtmPLqYYw" + +# Run a probe against the node with id "qj3GgGYg..." using amnezia with junk packets enabled. +nym-gateway-probe -g "qj3GgGYgGZZ3HkFrtD1GU9UJ5oNXME9eD2xtmPLqYYw" -a "Jc=4\nJmin=40\mJmax=70\n" +``` diff --git a/nym-vpn-core/crates/nym-gateway-probe/netstack_ping/gen.go b/nym-vpn-core/crates/nym-gateway-probe/netstack_ping/gen.go index 91bda5cc0f..63567b5390 100644 --- a/nym-vpn-core/crates/nym-gateway-probe/netstack_ping/gen.go +++ b/nym-vpn-core/crates/nym-gateway-probe/netstack_ping/gen.go @@ -38,6 +38,7 @@ typedef struct NetstackRequestRef { uint8_t num_ping; uint64_t send_timeout_sec; uint64_t recv_timeout_sec; + struct StringRef awg_args; } NetstackRequestRef; // hack from: https://stackoverflow.com/a/69904977 @@ -225,6 +226,7 @@ type NetstackRequest struct { num_ping uint8 send_timeout_sec uint64 recv_timeout_sec uint64 + awg_args string } func newNetstackRequest(p C.NetstackRequestRef) NetstackRequest { @@ -239,6 +241,7 @@ func newNetstackRequest(p C.NetstackRequestRef) NetstackRequest { num_ping: newC_uint8_t(p.num_ping), send_timeout_sec: newC_uint64_t(p.send_timeout_sec), recv_timeout_sec: newC_uint64_t(p.recv_timeout_sec), + awg_args: newString(p.awg_args), } } func cntNetstackRequest(s *NetstackRequest, cnt *uint) [0]C.NetstackRequestRef { @@ -258,6 +261,7 @@ func refNetstackRequest(p *NetstackRequest, buffer *[]byte) C.NetstackRequestRef num_ping: refC_uint8_t(&p.num_ping, buffer), send_timeout_sec: refC_uint64_t(&p.send_timeout_sec, buffer), recv_timeout_sec: refC_uint64_t(&p.recv_timeout_sec, buffer), + awg_args: refString(&p.awg_args, buffer), } } diff --git a/nym-vpn-core/crates/nym-gateway-probe/netstack_ping/go.mod b/nym-vpn-core/crates/nym-gateway-probe/netstack_ping/go.mod index c4d4079c82..c72aecd60d 100644 --- a/nym-vpn-core/crates/nym-gateway-probe/netstack_ping/go.mod +++ b/nym-vpn-core/crates/nym-gateway-probe/netstack_ping/go.mod @@ -1,14 +1,17 @@ module github.com/nymtech/nym-vpn-client/nym-vpn-core/crates/nym-gateway-probe/netstack_ping -go 1.22 +go 1.22.3 + +toolchain go1.23.1 require ( + github.com/amnezia-vpn/amneziawg-go v0.2.12 golang.org/x/net v0.23.0 - golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 ) require ( github.com/google/btree v1.0.1 // indirect + github.com/tevino/abool/v2 v2.1.0 // indirect golang.org/x/crypto v0.21.0 // indirect golang.org/x/sys v0.18.0 // indirect golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect diff --git a/nym-vpn-core/crates/nym-gateway-probe/netstack_ping/go.sum b/nym-vpn-core/crates/nym-gateway-probe/netstack_ping/go.sum index a904fe6ec2..3bfc3cb95d 100644 --- a/nym-vpn-core/crates/nym-gateway-probe/netstack_ping/go.sum +++ b/nym-vpn-core/crates/nym-gateway-probe/netstack_ping/go.sum @@ -1,5 +1,9 @@ +github.com/amnezia-vpn/amneziawg-go v0.2.12 h1:CxIQETy5kZ0ip/dFBpmnDxAcS/KuIQaJkOxDv5OQhVI= +github.com/amnezia-vpn/amneziawg-go v0.2.12/go.mod h1:d7WpNfzCRLy7ufGElJBYpD58WRmNjyLyt3IDHPY8AmM= github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= +github.com/tevino/abool/v2 v2.1.0 h1:7w+Vf9f/5gmKT4m4qkayb33/92M+Um45F2BkHOR+L/c= +github.com/tevino/abool/v2 v2.1.0/go.mod h1:+Lmlqk6bHDWHqN1cbxqhwEAwMPXgc8I1SDEamtseuXY= golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= @@ -10,7 +14,5 @@ golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0k golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 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-20231211153847-12269c276173 h1:/jFs0duh4rdb8uIfPMv78iAJGcPKDeqAFnaLBropIC4= -golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA= gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259 h1:TbRPT0HtzFP3Cno1zZo7yPzEEnfu8EjLfl6IU9VfqkQ= gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259/go.mod h1:AVgIgHMwK63XvmAzWG9vLQ41YnVHN0du0tEC46fI7yY= diff --git a/nym-vpn-core/crates/nym-gateway-probe/netstack_ping/impl.go b/nym-vpn-core/crates/nym-gateway-probe/netstack_ping/impl.go index 61df21e72c..aaef8ebd5e 100644 --- a/nym-vpn-core/crates/nym-gateway-probe/netstack_ping/impl.go +++ b/nym-vpn-core/crates/nym-gateway-probe/netstack_ping/impl.go @@ -8,11 +8,11 @@ import ( "strings" "time" + "github.com/amnezia-vpn/amneziawg-go/conn" + "github.com/amnezia-vpn/amneziawg-go/device" + "github.com/amnezia-vpn/amneziawg-go/tun/netstack" "golang.org/x/net/icmp" "golang.org/x/net/ipv4" - "golang.zx2c4.com/wireguard/conn" - "golang.zx2c4.com/wireguard/device" - "golang.zx2c4.com/wireguard/tun/netstack" ) type Netstack struct{} @@ -36,6 +36,10 @@ func (Netstack) ping(req NetstackRequest) NetstackResponse { ipc.WriteString("private_key=") ipc.WriteString(req.private_key) + if req.awg_args != "" { + awg := strings.ReplaceAll(req.awg_args, "\\n", "\n") + ipc.WriteString(fmt.Sprintf("\n%s", awg)) + } ipc.WriteString("\npublic_key=") ipc.WriteString(req.public_key) ipc.WriteString("\nendpoint=") @@ -45,6 +49,13 @@ func (Netstack) ping(req NetstackRequest) NetstackResponse { response := NetstackResponse{false, 0, 0, 0, 0, false} dev.IpcSet(ipc.String()) + + config, err := dev.IpcGet() + if err != nil { + log.Panic(err) + } + log.Printf("%s", config) + err = dev.Up() if err != nil { log.Panic(err) @@ -69,15 +80,18 @@ func (Netstack) ping(req NetstackRequest) NetstackResponse { for _, ip := range req.ping_ips { for i := uint8(0); i < req.num_ping; i++ { - log.Printf("Pinging %s seq=%d", ip, i) - response.sent_ips += 1 - rt, err := sendPing(ip, i, req.send_timeout_sec, req.recv_timeout_sec, tnet) - if err != nil { - log.Printf("Failed to send ping: %v\n", err) - continue - } - response.received_ips += 1 - log.Printf("Ping latency: %v\n", rt) + func() { + defer time.Sleep(5 * time.Second) + log.Printf("Pinging %s seq=%d", ip, i) + response.sent_ips += 1 + rt, err := sendPing(ip, i, req.send_timeout_sec, req.recv_timeout_sec, tnet) + if err != nil { + log.Printf("Failed to send ping: %v\n", err) + return + } + response.received_ips += 1 + log.Printf("Ping latency: %v\n", rt) + }() } } diff --git a/nym-vpn-core/crates/nym-gateway-probe/src/lib.rs b/nym-vpn-core/crates/nym-gateway-probe/src/lib.rs index c466c2d81d..7910f925de 100644 --- a/nym-vpn-core/crates/nym-gateway-probe/src/lib.rs +++ b/nym-vpn-core/crates/nym-gateway-probe/src/lib.rs @@ -58,78 +58,105 @@ pub async fn fetch_gateways_with_ipr() -> anyhow::Result { Ok(lookup_gateways().await?.into_exit_gateways()) } -pub async fn probe(entry_point: EntryPoint) -> anyhow::Result { - // Setup the entry gateways - let gateways = lookup_gateways().await?; - let entry_gateway = entry_point.lookup_gateway(&gateways).await?; - let exit_router_address = entry_gateway.ipr_address; - let authenticator = entry_gateway.authenticator_address; - let gateway_host = entry_gateway.host.clone().unwrap(); - let entry_gateway_id = entry_gateway.identity(); - - info!("Probing gateway: {entry_gateway:?}"); - - // Connect to the mixnet - let mixnet_client = MixnetClientBuilder::new_ephemeral() - .request_gateway(entry_gateway_id.to_string()) - .network_details(NymNetworkDetails::new_from_env()) - .debug_config(mixnet_debug_config()) - .build()? - .connect_to_mixnet() - .await; - - let mixnet_client = match mixnet_client { - Ok(mixnet_client) => mixnet_client, - Err(err) => { - error!("Failed to connect to mixnet: {err}"); - return Ok(ProbeResult { - gateway: entry_gateway_id.to_string(), - outcome: ProbeOutcome { - as_entry: Entry::fail_to_connect(), - as_exit: None, - wg: None, - }, - }); +pub struct Probe { + entrypoint: EntryPoint, + amnezia_args: String, +} + +impl Probe { + pub fn new(entrypoint: EntryPoint) -> Self { + Self { + entrypoint, + amnezia_args: "".into(), } - }; + } - let nym_address = *mixnet_client.nym_address(); - let entry_gateway = nym_address.gateway().to_base58_string(); + pub fn with_amnezia(&mut self, args: &str) -> &Self { + self.amnezia_args = args.to_string(); + self + } - info!("Successfully connected to entry gateway: {entry_gateway}"); - info!("Our nym address: {nym_address}"); + pub async fn probe(self) -> anyhow::Result { + let entry_point = self.entrypoint; + + // Setup the entry gateways + let gateways = lookup_gateways().await?; + let entry_gateway = entry_point.lookup_gateway(&gateways).await?; + let exit_router_address = entry_gateway.ipr_address; + let authenticator = entry_gateway.authenticator_address; + let gateway_host = entry_gateway.host.clone().unwrap(); + let entry_gateway_id = entry_gateway.identity(); + + info!("Probing gateway: {entry_gateway:?}"); + + // Connect to the mixnet + let mixnet_client = MixnetClientBuilder::new_ephemeral() + .request_gateway(entry_gateway_id.to_string()) + .network_details(NymNetworkDetails::new_from_env()) + .debug_config(mixnet_debug_config()) + .build()? + .connect_to_mixnet() + .await; + + let mixnet_client = match mixnet_client { + Ok(mixnet_client) => mixnet_client, + Err(err) => { + error!("Failed to connect to mixnet: {err}"); + return Ok(ProbeResult { + gateway: entry_gateway_id.to_string(), + outcome: ProbeOutcome { + as_entry: Entry::fail_to_connect(), + as_exit: None, + wg: None, + }, + }); + } + }; - let shared_client = Arc::new(tokio::sync::Mutex::new(Some(mixnet_client))); + let nym_address = *mixnet_client.nym_address(); + let entry_gateway = nym_address.gateway().to_base58_string(); - // Now that we have a connected mixnet client, we can start pinging - let shared_mixnet_client = SharedMixnetClient::from_shared(&shared_client); - let outcome = do_ping(shared_mixnet_client.clone(), exit_router_address).await; + info!("Successfully connected to entry gateway: {entry_gateway}"); + info!("Our nym address: {nym_address}"); - let wg_outcome = if let Some(authenticator) = authenticator { - wg_probe(authenticator, shared_client, &gateway_host) + let shared_client = Arc::new(tokio::sync::Mutex::new(Some(mixnet_client))); + + // Now that we have a connected mixnet client, we can start pinging + let shared_mixnet_client = SharedMixnetClient::from_shared(&shared_client); + let outcome = do_ping(shared_mixnet_client.clone(), exit_router_address).await; + + let wg_outcome = if let Some(authenticator) = authenticator { + wg_probe( + authenticator, + shared_client, + &gateway_host, + self.amnezia_args, + ) .await .unwrap_or_default() - } else { - WgProbeResults::default() - }; + } else { + WgProbeResults::default() + }; - let mixnet_client = shared_mixnet_client.lock().await.take().unwrap(); - mixnet_client.disconnect().await; + let mixnet_client = shared_mixnet_client.lock().await.take().unwrap(); + mixnet_client.disconnect().await; - // Disconnect the mixnet client gracefully - outcome.map(|mut outcome| { - outcome.wg = Some(wg_outcome); - ProbeResult { - gateway: entry_gateway.clone(), - outcome, - } - }) + // Disconnect the mixnet client gracefully + outcome.map(|mut outcome| { + outcome.wg = Some(wg_outcome); + ProbeResult { + gateway: entry_gateway.clone(), + outcome, + } + }) + } } async fn wg_probe( authenticator: AuthAddress, shared_mixnet_client: Arc>>, gateway_host: &nym_topology::NetworkAddress, + awg_args: String, ) -> anyhow::Result { let auth_shared_client = nym_authenticator_client::SharedMixnetClient::from_shared(&shared_mixnet_client); @@ -227,6 +254,7 @@ async fn wg_probe( private_key: private_key_hex, public_key: public_key_hex, endpoint: wg_endpoint.clone(), + awg_args, ..Default::default() }; diff --git a/nym-vpn-core/crates/nym-gateway-probe/src/netstack.rs b/nym-vpn-core/crates/nym-gateway-probe/src/netstack.rs index 6a701ca658..078f08eda2 100644 --- a/nym-vpn-core/crates/nym-gateway-probe/src/netstack.rs +++ b/nym-vpn-core/crates/nym-gateway-probe/src/netstack.rs @@ -15,6 +15,7 @@ pub struct NetstackRequest { pub num_ping: u8, pub send_timeout_sec: u64, pub recv_timeout_sec: u64, + pub awg_args: String, } impl Default for NetstackRequest { @@ -27,9 +28,10 @@ impl Default for NetstackRequest { dns: "1.1.1.1".to_string(), ping_hosts: vec!["nymtech.net".to_string()], ping_ips: vec!["1.1.1.1".to_string()], - num_ping: 3, - send_timeout_sec: 1, - recv_timeout_sec: 2, + num_ping: 10, + send_timeout_sec: 3, + recv_timeout_sec: 3, + awg_args: Default::default(), } } } diff --git a/nym-vpn-core/crates/nym-gateway-probe/src/run.rs b/nym-vpn-core/crates/nym-gateway-probe/src/run.rs index cdbb8dd4c9..20967eaf45 100644 --- a/nym-vpn-core/crates/nym-gateway-probe/src/run.rs +++ b/nym-vpn-core/crates/nym-gateway-probe/src/run.rs @@ -17,11 +17,17 @@ struct CliArgs { #[arg(short, long)] config_env_file: Option, + /// The specific gateway specified by ID. #[arg(long, short)] gateway: Option, + /// Disable logging during probe #[arg(long, short)] no_log: bool, + + /// Arguments to be appended to the wireguard config enabling amnezia-wg configuration + #[arg(long, short)] + amnezia_args: Option, } fn setup_logging() { @@ -38,7 +44,7 @@ fn setup_logging() { .init(); } -pub(crate) async fn run() -> anyhow::Result { +async fn run() -> anyhow::Result { let args = CliArgs::parse(); if !args.no_log { setup_logging(); @@ -52,7 +58,11 @@ pub(crate) async fn run() -> anyhow::Result { fetch_random_gateway_with_ipr().await? }; - nym_gateway_probe::probe(gateway).await + let mut trial = nym_gateway_probe::Probe::new(gateway); + if let Some(awg_args) = args.amnezia_args { + trial.with_amnezia(&awg_args); + } + trial.probe().await } async fn fetch_random_gateway_with_ipr() -> anyhow::Result { diff --git a/nym-vpn-core/crates/nym-vpnd/build.rs b/nym-vpn-core/crates/nym-vpnd/build.rs index 425025e065..9519095869 100644 --- a/nym-vpn-core/crates/nym-vpnd/build.rs +++ b/nym-vpn-core/crates/nym-vpnd/build.rs @@ -5,14 +5,6 @@ use std::{env, path::PathBuf}; use vergen::EmitBuilder; fn main() -> Result<(), Box> { - EmitBuilder::builder() - .all_build() - .all_git() - .all_rustc() - .all_cargo() - .emit() - .expect("failed to extract build metadata"); - let manifest_path = env::var_os("CARGO_MANIFEST_DIR").expect("manifest dir is not set"); let target = env::var("TARGET").expect("target is not set"); let target_os = env::var("CARGO_CFG_TARGET_OS").expect("target os is not set"); @@ -46,5 +38,12 @@ fn main() -> Result<(), Box> { }; println!("cargo:rustc-link-lib{}=wg", link_type); + EmitBuilder::builder() + .all_build() + .all_git() + .all_rustc() + .all_cargo() + .emit() + .expect("failed to extract build metadata"); Ok(()) } diff --git a/nym-vpn-core/crates/nym-wg-go/Cargo.toml b/nym-vpn-core/crates/nym-wg-go/Cargo.toml index aaed1be3e1..e3223992d5 100644 --- a/nym-vpn-core/crates/nym-wg-go/Cargo.toml +++ b/nym-vpn-core/crates/nym-wg-go/Cargo.toml @@ -20,3 +20,4 @@ hex.workspace = true base64.workspace = true x25519-dalek = { workspace = true, features = ["static_secrets"] } zeroize.workspace = true +rand.workspace = true diff --git a/nym-vpn-core/crates/nym-wg-go/README.md b/nym-vpn-core/crates/nym-wg-go/README.md new file mode 100644 index 0000000000..a69cb3fe28 --- /dev/null +++ b/nym-vpn-core/crates/nym-wg-go/README.md @@ -0,0 +1,39 @@ +# Rust Wireguard-go Wrapper + +This library wraps `wireguard-go` making it avaiable for transparent use to other +rust crates. + +## Usage + +Using this crate requires building the `libwg.a` library file and `libwg.h` header +file. + +```toml +nym-wg-go.workspace = true +``` + +```sh +# In the root of the repo +make build-wireguard + +# build this library (or downstream projects) +cargo build -p nym-wg-go +``` + +### Amnezia + +To use the AmneziaWG version we need to adjust the `libwg,a` file that gets built +to use the altered golang wrapper. Also you will need to enable the `amnezia` feature +in this crate. + +```toml +nym-wg-go = { workspace=true, features=["amnezia"]} +``` + +```sh +# In the root of the repo +make build-wireguard-amnezia + +# build this library (or downstream projects) in the same way as before. +cargo build -p nym-wg-go +``` diff --git a/nym-vpn-core/crates/nym-wg-go/src/amnezia.rs b/nym-vpn-core/crates/nym-wg-go/src/amnezia.rs new file mode 100644 index 0000000000..25f4e3ae16 --- /dev/null +++ b/nym-vpn-core/crates/nym-wg-go/src/amnezia.rs @@ -0,0 +1,210 @@ +//! Interface to amneziawg (fork of wireguard-go) allowing optional use of Amnezia features. +//! + +use crate::UapiConfigBuilder; + +use rand::{Rng, RngCore}; + +const OFF: AmneziaConfig = AmneziaConfig { + junk_pkt_count: 0, + junk_pkt_min_size: 0, + junk_pkt_max_size: 0, + init_pkt_junk_size: 0, + response_pkt_junk_size: 0, + init_pkt_magic_header: 1, + response_pkt_magic_header: 2, + under_load_pkt_magic_header: 3, + transport_pkt_magic_header: 4, +}; + +const BASE: AmneziaConfig = AmneziaConfig { + junk_pkt_count: 4, + junk_pkt_min_size: 40, + junk_pkt_max_size: 70, + init_pkt_junk_size: 0, + response_pkt_junk_size: 0, + init_pkt_magic_header: 1, + response_pkt_magic_header: 2, + under_load_pkt_magic_header: 3, + transport_pkt_magic_header: 4, +}; + +/// Hold Amnezia-wireguard configuration parameters. +/// +/// All parameters should be the same between Client and Server, except Jc - it can vary. +/// +/// - Jc — 1 ≤ Jc ≤ 128; recommended range is from 3 to 10 inclusive +/// - Jmin — Jmin < Jmax; recommended value is 50 +/// - Jmax — Jmin < Jmax ≤ 1280; recommended value is 1000 +/// - S1 — S1 < 1280; S1 + 56 ≠ S2; recommended range is from 15 to 150 inclusive +/// - S2 — S2 < 1280; recommended range is from 15 to 150 inclusive +/// - H1/H2/H3/H4 — must be unique among each other; +/// recommended range is from 5 to 2_147_483_647 (2^31 - 1 i.e. signed 32 bit int) inclusive +/// +/// Note: changes to S1, S2, H1, H2, H3, and H4 are required to match between client +/// and server. The connection will not work otherwise. +#[derive(Debug, Clone, PartialEq)] +pub struct AmneziaConfig { + /// Jc - Count of junk packets to send BEFORE sending the handshake Init message. + pub junk_pkt_count: u8, // Jc + /// Jmin - Minimum size in bytes of the Junk packets enabled by `junk_pkt_count` + pub junk_pkt_min_size: u16, // Jmin + /// Jmax - Maximum size in bytes of the Junk packets enabled by `junk_pkt_count` + pub junk_pkt_max_size: u16, // Jmax + /// S1 - Numer of byte to PREPEND to the Handshake init message + pub init_pkt_junk_size: u16, // S1 + /// S2 - Number of bytes to PREPEND to the Handshake response message + pub response_pkt_junk_size: u16, // S2 + /// H1 - Re-map handshake Init packet header type indicator to this value + pub init_pkt_magic_header: i32, // H1 + /// H2 - Re-map handshake reponse packet header type indicator to this value + pub response_pkt_magic_header: i32, // H2 + /// H3 - Re-map under load packet header type indicator to this value + pub under_load_pkt_magic_header: i32, // H3 + /// H4 - Re-map transport packet header type indicator to this value + pub transport_pkt_magic_header: i32, // H4 +} + +impl Default for AmneziaConfig { + fn default() -> Self { + OFF.clone() + } +} + +impl AmneziaConfig { + /// Creates a randomized configuration with parameters within suggested ranges. + /// + /// Attempts to retry if there is a collision in [H1, H2, H3, H4]. This should + /// almost never happen given the range available (5 to i32::MAX) unless the provided + /// rng is bad. If the rng is bad, then amneziawg will break anyways so we panic. + pub fn rand(rng: &mut impl RngCore) -> Self { + for _ in 0..16 { + let c = Self { + junk_pkt_count: rng.gen_range(3..10), + junk_pkt_min_size: rng.gen_range(0..900), + junk_pkt_max_size: 1000, + init_pkt_junk_size: rng.gen_range(15..150), + response_pkt_junk_size: rng.gen_range(15..150), + init_pkt_magic_header: rng.gen_range(5..i32::MAX), + response_pkt_magic_header: rng.gen_range(5..i32::MAX), + under_load_pkt_magic_header: rng.gen_range(5..i32::MAX), + transport_pkt_magic_header: rng.gen_range(5..i32::MAX), + }; + if c.validate() { + return c; + } + } + panic!("this should not be possible"); + } + + /// Returns a configuration that disables Amnezia. + pub fn off() -> Self { + OFF.clone() + } + + /// Returns a configuration that enables only the basic junk packet feature + /// of amneziawg + pub fn basic() -> Self { + BASE.clone() + } + + /// Adds the contained AmneziaWG parameters to the UAPI Config + pub fn append_to(&self, config_builder: &mut UapiConfigBuilder) { + if self == &OFF { + return; + } + config_builder.add("Jc", self.junk_pkt_count.to_string().as_str()); + config_builder.add("Jmin", self.junk_pkt_min_size.to_string().as_str()); + config_builder.add("Jmax", self.junk_pkt_max_size.to_string().as_str()); + + if self == &BASE { + return; + } + + config_builder.add("S1", self.init_pkt_junk_size.to_string().as_str()); + config_builder.add("S2", self.response_pkt_junk_size.to_string().as_str()); + config_builder.add("H1", self.init_pkt_magic_header.to_string().as_str()); + config_builder.add("H2", self.response_pkt_magic_header.to_string().as_str()); + config_builder.add("H3", self.under_load_pkt_magic_header.to_string().as_str()); + config_builder.add("H4", self.transport_pkt_magic_header.to_string().as_str()); + } + + /// Check if the provided configuration is valid + /// + /// - Jc — 1 ≤ Jc ≤ 128; recommended range is from 3 to 10 inclusive + /// - Jmin — Jmin < Jmax; recommended value is 50 + /// - Jmax — Jmin < Jmax ≤ 1280; recommended value is 1000 + /// - S1 — S1 < 1280; S1 + 56 ≠ S2; recommended range is from 15 to 150 inclusive + /// - S2 — S2 < 1280; recommended range is from 15 to 150 inclusive + /// - H1/H2/H3/H4 — must be unique among each other; + /// recommended range is from 5 to 2_147_483_647 (2^31 - 1 i.e. signed 32 bit int) inclusive + pub fn validate(&self) -> bool { + if self.junk_pkt_count > 128 + || self.junk_pkt_max_size > 1280 + || self.junk_pkt_min_size > self.junk_pkt_max_size + || self.init_pkt_junk_size > 1280 + || self.response_pkt_junk_size > 1280 + || [ + self.response_pkt_magic_header, + self.under_load_pkt_magic_header, + self.transport_pkt_magic_header, + ] + .contains(&self.init_pkt_magic_header) + || [ + self.init_pkt_magic_header, + self.under_load_pkt_magic_header, + self.transport_pkt_magic_header, + ] + .contains(&self.response_pkt_magic_header) + || [ + self.init_pkt_magic_header, + self.response_pkt_magic_header, + self.transport_pkt_magic_header, + ] + .contains(&self.under_load_pkt_magic_header) + || [ + self.init_pkt_magic_header, + self.response_pkt_magic_header, + self.under_load_pkt_magic_header, + ] + .contains(&self.transport_pkt_magic_header) + { + return false; + } + true + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_encode_amnezia_config() { + let mut config_builder = UapiConfigBuilder::new(); + OFF.append_to(&mut config_builder); + assert_eq!(config_builder.into_bytes(), b"\n"); + + let mut config_builder = UapiConfigBuilder::new(); + BASE.append_to(&mut config_builder); + assert_eq!(config_builder.into_bytes(), b"Jc=4\nJmin=40\nJmax=70\n\n"); + + let c = AmneziaConfig { + junk_pkt_count: 1, + junk_pkt_min_size: 20, + junk_pkt_max_size: 30, + init_pkt_junk_size: 40, + response_pkt_junk_size: 50, + init_pkt_magic_header: 11, + response_pkt_magic_header: 12, + under_load_pkt_magic_header: 13, + transport_pkt_magic_header: 14, + }; + let mut config_builder = UapiConfigBuilder::new(); + c.append_to(&mut config_builder); + assert_eq!( + config_builder.into_bytes(), + b"Jc=1\nJmin=20\nJmax=30\nS1=40\nS2=50\nH1=11\nH2=12\nH3=13\nH4=14\n\n" + ); + } +} diff --git a/nym-vpn-core/crates/nym-wg-go/src/lib.rs b/nym-vpn-core/crates/nym-wg-go/src/lib.rs index 895aacb00a..ae9de4b1d0 100644 --- a/nym-vpn-core/crates/nym-wg-go/src/lib.rs +++ b/nym-vpn-core/crates/nym-wg-go/src/lib.rs @@ -1,6 +1,8 @@ // Copyright 2024 - Nym Technologies SA // SPDX-License-Identifier: GPL-3.0-only +#[cfg(feature = "amnezia")] +pub mod amnezia; #[cfg(any(target_os = "ios", target_os = "android"))] pub mod netstack; pub mod uapi; diff --git a/nym-vpn-core/crates/nym-wg-go/src/netstack.rs b/nym-vpn-core/crates/nym-wg-go/src/netstack.rs index 003e2fd140..d582b0fdc3 100644 --- a/nym-vpn-core/crates/nym-wg-go/src/netstack.rs +++ b/nym-vpn-core/crates/nym-wg-go/src/netstack.rs @@ -13,6 +13,8 @@ use super::{ uapi::UapiConfigBuilder, Error, LoggingCallback, PeerConfig, PeerEndpointUpdate, PrivateKey, Result, }; +#[cfg(feature = "amnezia")] +use crate::amnezia::AmneziaConfig; /// Netstack interface configuration. pub struct InterfaceConfig { @@ -35,47 +37,6 @@ impl fmt::Debug for InterfaceConfig { } } -/// Hold Amnezia-wireguard configuration parameters. -/// -/// All parameters should be the same between Client and Server, except Jc - it can vary. -/// -/// - Jc — 1 ≤ Jc ≤ 128; recommended range is from 3 to 10 inclusive -/// - Jmin — Jmin < Jmax; recommended value is 50 -/// - Jmax — Jmin < Jmax ≤ 1280; recommended value is 1000 -/// - S1 — S1 < 1280; S1 + 56 ≠ S2; recommended range is from 15 to 150 inclusive -/// - S2 — S2 < 1280; recommended range is from 15 to 150 inclusive -/// - H1/H2/H3/H4 — must be unique among each other; -/// recommended range is from 5 to 2_147_483_647 (2^31 - 1 i.e. signed 32 bit int) inclusive -#[cfg(feature = "amnezia")] -#[derive(Debug)] -pub struct AmneziaConfig { - pub junk_packet_count: i32, // Jc - pub junk_packet_min_size: i32, // Jmin - pub junk_packet_max_size: i32, // Jmax - pub init_packet_junk_size: i32, // S0 - pub response_packet_junk_size: i32, // S1 - pub init_packet_magic_header: u32, // H1 - pub response_packet_magic_header: u32, // H2 - pub under_load_packet_magic_header: u32, // H3 - pub transport_packet_magic_header: u32, // H4 -} - -#[cfg(feature = "amnezia")] -impl Default for AmneziaConfig { - fn default() -> Self { - Self { - junk_packet_count: 4_i32, - junk_packet_min_size: 40_i32, - junk_packet_max_size: 70_i32, - init_packet_junk_size: 0_i32, - response_packet_junk_size: 0_i32, - init_packet_magic_header: 1_u32, - response_packet_magic_header: 2_u32, - under_load_packet_magic_header: 3_u32, - transport_packet_magic_header: 4_u32, - } - } -} /// Netstack configuration. #[derive(Debug)] pub struct Config { @@ -91,6 +52,11 @@ impl Config { self.interface.private_key.to_bytes().as_ref(), ); + #[cfg(feature = "amnezia")] + if let Some(azwg_config) = &self.interface.azwg_config { + azwg_config.append_to(&mut config_builder); + } + if !self.peers.is_empty() { config_builder.add("replace_peers", "true"); for peer in self.peers.iter() { diff --git a/nym-vpn-core/crates/nym-wg-go/src/wireguard_go.rs b/nym-vpn-core/crates/nym-wg-go/src/wireguard_go.rs index 2f347beb09..855d0adc34 100644 --- a/nym-vpn-core/crates/nym-wg-go/src/wireguard_go.rs +++ b/nym-vpn-core/crates/nym-wg-go/src/wireguard_go.rs @@ -12,6 +12,8 @@ use super::{ uapi::UapiConfigBuilder, Error, LoggingCallback, PeerConfig, PeerEndpointUpdate, PrivateKey, Result, }; +#[cfg(feature = "amnezia")] +use crate::amnezia::AmneziaConfig; /// Classic WireGuard interface configuration. pub struct InterfaceConfig { @@ -21,6 +23,7 @@ pub struct InterfaceConfig { #[cfg(target_os = "linux")] pub fwmark: Option, #[cfg(feature = "amnezia")] + /// Amnezia wireguard configuration disabled by default pub azwg_config: Option, } @@ -35,47 +38,6 @@ impl fmt::Debug for InterfaceConfig { d.finish() } } -/// Hold Amnezia-wireguard configuration parameters. -/// -/// All parameters should be the same between Client and Server, except Jc - it can vary. -/// -/// - Jc — 1 ≤ Jc ≤ 128; recommended range is from 3 to 10 inclusive -/// - Jmin — Jmin < Jmax; recommended value is 50 -/// - Jmax — Jmin < Jmax ≤ 1280; recommended value is 1000 -/// - S1 — S1 < 1280; S1 + 56 ≠ S2; recommended range is from 15 to 150 inclusive -/// - S2 — S2 < 1280; recommended range is from 15 to 150 inclusive -/// - H1/H2/H3/H4 — must be unique among each other; -/// recommended range is from 5 to 2_147_483_647 (2^31 - 1 i.e. signed 32 bit int) inclusive -#[cfg(feature = "amnezia")] -#[derive(Debug)] -pub struct AmneziaConfig { - pub junk_packet_count: i32, // Jc - pub junk_packet_min_size: i32, // Jmin - pub junk_packet_max_size: i32, // Jmax - pub init_packet_junk_size: i32, // S0 - pub response_packet_junk_size: i32, // S1 - pub init_packet_magic_header: u32, // H1 - pub response_packet_magic_header: u32, // H2 - pub under_load_packet_magic_header: u32, // H3 - pub transport_packet_magic_header: u32, // H4 -} - -#[cfg(feature = "amnezia")] -impl Default for AmneziaConfig { - fn default() -> Self { - Self { - junk_packet_count: 4_i32, - junk_packet_min_size: 40_i32, - junk_packet_max_size: 70_i32, - init_packet_junk_size: 0_i32, - response_packet_junk_size: 0_i32, - init_packet_magic_header: 1_u32, - response_packet_magic_header: 2_u32, - under_load_packet_magic_header: 3_u32, - transport_packet_magic_header: 4_u32, - } - } -} /// Classic WireGuard configuration. #[derive(Debug)] @@ -101,6 +63,11 @@ impl Config { config_builder.add("fwmark", fwmark.to_string().as_str()); } + #[cfg(feature = "amnezia")] + if let Some(azwg_config) = &self.interface.azwg_config { + azwg_config.append_to(&mut config_builder); + } + if !self.peers.is_empty() { config_builder.add("replace_peers", "true"); for peer in self.peers.iter() { diff --git a/wireguard/build-wireguard-go.sh b/wireguard/build-wireguard-go.sh index d16584f20e..ff2560d234 100755 --- a/wireguard/build-wireguard-go.sh +++ b/wireguard/build-wireguard-go.sh @@ -2,30 +2,53 @@ # This script is used to build wireguard-go libraries for all the platforms. -TEMP=$(getopt -o aiz --long android,docker,ios,amnezia \ - -n 'build-wireguard-go.sh' -- "$@") - -if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi - -# Note the quotes around '$TEMP': they are essential! -eval set -- "$TEMP" +function stringContain { + case $2 in *$1* ) return 0;; *) return 1;; esac; +} ANDROID_BUILD=false IOS_BUILD=false DOCKER_BUILD=true AMNEZIA_BUILD=false -while true; do - case "$1" in - "-a" | "--android" ) ANDROID_BUILD=true; shift ;; - "-i" | "--ios" ) IOS_BUILD=true; shift ;; - "-z" | "--amnezia" ) AMNEZIA_BUILD=true; shift ;; - "--no-docker" ) DOCKER_BUILD=false; shift ;; - -- ) shift; break ;; - * ) break ;; - esac -done - -set -eu + +function parseArgs { + if stringContain "Darwin" "$(uname -s)"; then + # Mac builds require gnu-getopt because regular macos getopt doesn't allow long args. -_- + # This could be avoided using something like `getopts` instead, but then we don't + # have the ability to use long options which pre-date this script change. + # > brew install gnu-getopt + # Installed for CI in `.github/workflows/ci-nym-vpn-core.yml` + echo "using gnu-getopt" + export PATH="/opt/homebrew/opt/gnu-getopt/bin:$PATH" + fi + + which getopt + TEMP=$(getopt -o aiz --long android,docker,ios,amnezia \ + -n 'build-wireguard-go.sh' -- "$@") + + if [ $? != 0 ]; then + echo "encountered an error parsing args" + exit 2 + fi + + # Note the quotes around '$TEMP': they are essential! + eval set -- "$TEMP" + + while true; do + case "$1" in + "-a" | "--android" ) ANDROID_BUILD=true; shift ;; + "-i" | "--ios" ) IOS_BUILD=true; shift ;; + "-z" | "--amnezia" ) AMNEZIA_BUILD=true; shift ;; + "--no-docker" ) DOCKER_BUILD=false; shift ;; + -- ) shift; break ;; + * ) break ;; + esac + done + + echo "android:$ANDROID_BUILD ios:$IOS_BUILD docker:$DOCKER_BUILD amnezia:$AMNEZIA_BUILD" + + set -eu +} function is_android_build { if [ "$ANDROID_BUILD" = true ]; then @@ -110,7 +133,7 @@ function build_windows { export GOARCH=amd64 export CC="x86_64-w64-mingw32-cc" fi - + echo "Building wireguard-go for Windows ($arch)" pushd libwg @@ -268,6 +291,7 @@ function patch_darwin_goruntime { } function build_wireguard_go { + parseArgs $@ if is_amnezia_build ; then LIB_DIR=$AMNEZIA_DIR From c245bb7c30ece7eb90e698973083ddd3178eb37b Mon Sep 17 00:00:00 2001 From: jmwample Date: Wed, 30 Oct 2024 16:11:37 -0600 Subject: [PATCH 3/6] ensure CI env compatibility --- .github/workflows/ci-nym-vpn-core-ios.yml | 3 +++ .github/workflows/ci-nym-vpn-core-macos.yml | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci-nym-vpn-core-ios.yml b/.github/workflows/ci-nym-vpn-core-ios.yml index 1e93f1146e..82e71828ab 100644 --- a/.github/workflows/ci-nym-vpn-core-ios.yml +++ b/.github/workflows/ci-nym-vpn-core-ios.yml @@ -46,6 +46,9 @@ jobs: version: "21.12" # 3.21.12: the version on ubuntu 24.04. Don't change this! repo-token: ${{ secrets.GITHUB_TOKEN }} + - name: Install script dependencies + run: brew install gnu-getopt + - name: Build wireguard shell: bash run: | diff --git a/.github/workflows/ci-nym-vpn-core-macos.yml b/.github/workflows/ci-nym-vpn-core-macos.yml index ae017291a0..54bab62690 100644 --- a/.github/workflows/ci-nym-vpn-core-macos.yml +++ b/.github/workflows/ci-nym-vpn-core-macos.yml @@ -24,7 +24,7 @@ jobs: rm -rf ./* || true rm -rf ./.??* || true ls -la ./ - + - name: Checkout repo uses: actions/checkout@v4 @@ -45,6 +45,9 @@ jobs: version: "21.12" # 3.21.12: the version on ubuntu 24.04. Don't change this! repo-token: ${{ secrets.GITHUB_TOKEN }} + - name: Install script dependencies + run: brew install gnu-getopt + - name: Build wireguard shell: bash run: | From d5fff03a1f622aa76f666a9db3658a1e6c89011f Mon Sep 17 00:00:00 2001 From: jmwample Date: Wed, 30 Oct 2024 16:15:35 -0600 Subject: [PATCH 4/6] correct missed conflict --- wireguard/build-wireguard-go.sh | 8 -------- 1 file changed, 8 deletions(-) diff --git a/wireguard/build-wireguard-go.sh b/wireguard/build-wireguard-go.sh index ff2560d234..7c51205fb4 100755 --- a/wireguard/build-wireguard-go.sh +++ b/wireguard/build-wireguard-go.sh @@ -113,16 +113,8 @@ function win_create_lib_file { } function build_windows { -<<<<<<< HEAD export CGO_ENABLED=1 export GOOS=windows -======= - echo "Building wireguard-go for Windows" - pushd $LIB_DIR - export CGO_ENABLED=1 - go build -trimpath -v -o libwg.dll -buildmode c-shared - win_create_lib_file ->>>>>>> c0b0f37b (initial compiling amnezia lib exchanged for wireguard-go) if is_win_arm64 $@; then local arch="aarch64" From e1d03fe4958009dcc033e34c0c1b58374a40649f Mon Sep 17 00:00:00 2001 From: jmwample Date: Wed, 30 Oct 2024 16:27:08 -0600 Subject: [PATCH 5/6] one more error from conflicts --- nym-vpn-core/crates/nym-gateway-probe/src/run.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nym-vpn-core/crates/nym-gateway-probe/src/run.rs b/nym-vpn-core/crates/nym-gateway-probe/src/run.rs index 20967eaf45..123b0d8ae8 100644 --- a/nym-vpn-core/crates/nym-gateway-probe/src/run.rs +++ b/nym-vpn-core/crates/nym-gateway-probe/src/run.rs @@ -44,7 +44,7 @@ fn setup_logging() { .init(); } -async fn run() -> anyhow::Result { +pub(crate) async fn run() -> anyhow::Result { let args = CliArgs::parse(); if !args.no_log { setup_logging(); From 04c9f421a8166f90e91a097f7d37f4d2cd31bc16 Mon Sep 17 00:00:00 2001 From: Jack Wampler Date: Wed, 6 Nov 2024 08:05:42 -0700 Subject: [PATCH 6/6] Amnezia Poc using amneziawg-go Only (#1441) swap wholesale to libamnezia --- Makefile | 3 - nym-vpn-core/crates/nym-vpn-lib/Cargo.toml | 1 + .../crates/nym-vpn-lib/src/wg_config.rs | 10 + nym-vpn-core/crates/nym-wg-go/README.md | 33 ++- wireguard/build-wireguard-go.sh | 14 +- wireguard/libamnezia/Android.mk | 39 ---- .../Dockerfile_AndroidPatchedGoruntime | 50 ----- wireguard/libamnezia/README.md | 26 --- wireguard/libamnezia/build-android.sh | 74 ------- wireguard/libamnezia/container/container.go | 63 ------ wireguard/libamnezia/go.mod | 21 -- wireguard/libamnezia/go.sum | 18 -- ...untime-boottime-over-monotonic-darwin.diff | 61 ------ .../goruntime-boottime-over-monotonic.diff | 170 --------------- wireguard/libamnezia/libwg.go | 74 ------- wireguard/libamnezia/libwg_android.go | 103 --------- wireguard/libamnezia/libwg_default.go | 97 --------- wireguard/libamnezia/libwg_ios.go | 97 --------- wireguard/libamnezia/libwg_mobile.go | 40 ---- wireguard/libamnezia/libwg_windows.go | 153 -------------- wireguard/libamnezia/logging/logging.go | 60 ------ wireguard/libamnezia/netstack_android.go | 39 ---- wireguard/libamnezia/netstack_mobile.go | 200 ------------------ .../libamnezia/udp_forwarder/udp_forwarder.go | 194 ----------------- wireguard/libwg/go.mod | 14 +- wireguard/libwg/go.sum | 18 +- wireguard/libwg/libwg.go | 2 +- wireguard/libwg/libwg_android.go | 6 +- wireguard/libwg/libwg_default.go | 6 +- wireguard/libwg/libwg_ios.go | 6 +- wireguard/libwg/libwg_windows.go | 6 +- wireguard/libwg/logging/logging.go | 2 +- wireguard/libwg/netstack_android.go | 2 +- wireguard/libwg/netstack_mobile.go | 6 +- .../libwg/udp_forwarder/udp_forwarder.go | 4 +- 35 files changed, 77 insertions(+), 1635 deletions(-) delete mode 100644 wireguard/libamnezia/Android.mk delete mode 100644 wireguard/libamnezia/Dockerfile_AndroidPatchedGoruntime delete mode 100644 wireguard/libamnezia/README.md delete mode 100755 wireguard/libamnezia/build-android.sh delete mode 100644 wireguard/libamnezia/container/container.go delete mode 100644 wireguard/libamnezia/go.mod delete mode 100644 wireguard/libamnezia/go.sum delete mode 100644 wireguard/libamnezia/goruntime-boottime-over-monotonic-darwin.diff delete mode 100644 wireguard/libamnezia/goruntime-boottime-over-monotonic.diff delete mode 100644 wireguard/libamnezia/libwg.go delete mode 100644 wireguard/libamnezia/libwg_android.go delete mode 100644 wireguard/libamnezia/libwg_default.go delete mode 100644 wireguard/libamnezia/libwg_ios.go delete mode 100644 wireguard/libamnezia/libwg_mobile.go delete mode 100644 wireguard/libamnezia/libwg_windows.go delete mode 100644 wireguard/libamnezia/logging/logging.go delete mode 100644 wireguard/libamnezia/netstack_android.go delete mode 100644 wireguard/libamnezia/netstack_mobile.go delete mode 100644 wireguard/libamnezia/udp_forwarder/udp_forwarder.go diff --git a/Makefile b/Makefile index 62fb7e3689..88dddeb6d9 100644 --- a/Makefile +++ b/Makefile @@ -6,9 +6,6 @@ all: build-wireguard build-nym-vpn-core build-wireguard: ./wireguard/build-wireguard-go.sh -build-amnezia-wg: - ./wireguard/build-wireguard-go.sh --amnezia - build-wireguard-ios: ./wireguard/build-wireguard-go.sh --ios diff --git a/nym-vpn-core/crates/nym-vpn-lib/Cargo.toml b/nym-vpn-core/crates/nym-vpn-lib/Cargo.toml index 573c0938a9..fe0a8d491c 100644 --- a/nym-vpn-core/crates/nym-vpn-lib/Cargo.toml +++ b/nym-vpn-core/crates/nym-vpn-lib/Cargo.toml @@ -104,3 +104,4 @@ vergen = { workspace = true, default-features = false, features = [ [features] metrics-server = ["nym-client-core/metrics-server"] +amnezia = ["nym-wg-go/amnezia"] diff --git a/nym-vpn-core/crates/nym-vpn-lib/src/wg_config.rs b/nym-vpn-core/crates/nym-vpn-lib/src/wg_config.rs index 201d990b14..4dc14e75b5 100644 --- a/nym-vpn-core/crates/nym-vpn-lib/src/wg_config.rs +++ b/nym-vpn-core/crates/nym-vpn-lib/src/wg_config.rs @@ -40,6 +40,10 @@ pub struct WgInterface { /// Mark used for mark-based routing. #[cfg(target_os = "linux")] pub fwmark: Option, + + /// Amnezia Configuration + #[cfg(feature = "amnezia")] + pub azwg_config: Option, } impl fmt::Debug for WgInterface { @@ -89,6 +93,8 @@ impl WgNodeConfig { .collect(), dns_addrs: self.interface.dns, mtu: self.interface.mtu, + #[cfg(feature = "amnezia")] + azwg_config: self.interface.azwg_config, }, peers: vec![PeerConfig { // todo: limit to loopback? @@ -108,6 +114,8 @@ impl WgNodeConfig { mtu: self.interface.mtu, #[cfg(target_os = "linux")] fwmark: self.interface.fwmark, + #[cfg(feature = "amnezia")] + azwg_config: self.interface.azwg_config, }, peers: vec![PeerConfig { public_key: self.peer.public_key, @@ -138,6 +146,8 @@ impl WgNodeConfig { mtu, #[cfg(target_os = "linux")] fwmark: None, + #[cfg(feature = "amnezia")] + azwg_config: None, }, peer: WgPeer { public_key: PublicKey::from(*gateway_data.public_key.as_bytes()), diff --git a/nym-vpn-core/crates/nym-wg-go/README.md b/nym-vpn-core/crates/nym-wg-go/README.md index a69cb3fe28..a3c37a27c8 100644 --- a/nym-vpn-core/crates/nym-wg-go/README.md +++ b/nym-vpn-core/crates/nym-wg-go/README.md @@ -22,18 +22,35 @@ cargo build -p nym-wg-go ### Amnezia -To use the AmneziaWG version we need to adjust the `libwg,a` file that gets built -to use the altered golang wrapper. Also you will need to enable the `amnezia` feature +To use the AmneziaWG version we need to enable the `amnezia` feature in this crate. ```toml nym-wg-go = { workspace=true, features=["amnezia"]} ``` -```sh -# In the root of the repo -make build-wireguard-amnezia - -# build this library (or downstream projects) in the same way as before. -cargo build -p nym-wg-go +The `AmneziaConfig` ( `Config > InterfaceConfig > AmneziaConfig`) can then be used +to enable and configure amnezia. + +```rs +pub struct AmneziaConfig { + /// Jc - Count of junk packets to send BEFORE sending the handshake Init message. + pub junk_pkt_count: u8, // Jc + /// Jmin - Minimum size in bytes of the Junk packets enabled by `junk_pkt_count` + pub junk_pkt_min_size: u16, // Jmin + /// Jmax - Maximum size in bytes of the Junk packets enabled by `junk_pkt_count` + pub junk_pkt_max_size: u16, // Jmax + /// S1 - Numer of byte to PREPEND to the Handshake init message + pub init_pkt_junk_size: u16, // S1 + /// S2 - Number of bytes to PREPEND to the Handshake response message + pub response_pkt_junk_size: u16, // S2 + /// H1 - Re-map handshake Init packet header type indicator to this value + pub init_pkt_magic_header: i32, // H1 + /// H2 - Re-map handshake reponse packet header type indicator to this value + pub response_pkt_magic_header: i32, // H2 + /// H3 - Re-map under load packet header type indicator to this value + pub under_load_pkt_magic_header: i32, // H3 + /// H4 - Re-map transport packet header type indicator to this value + pub transport_pkt_magic_header: i32, // H4 +} ``` diff --git a/wireguard/build-wireguard-go.sh b/wireguard/build-wireguard-go.sh index 7c51205fb4..73a897def9 100755 --- a/wireguard/build-wireguard-go.sh +++ b/wireguard/build-wireguard-go.sh @@ -9,7 +9,6 @@ function stringContain { ANDROID_BUILD=false IOS_BUILD=false DOCKER_BUILD=true -AMNEZIA_BUILD=false function parseArgs { if stringContain "Darwin" "$(uname -s)"; then @@ -23,7 +22,7 @@ function parseArgs { fi which getopt - TEMP=$(getopt -o aiz --long android,docker,ios,amnezia \ + TEMP=$(getopt -o ai --long android,docker,ios \ -n 'build-wireguard-go.sh' -- "$@") if [ $? != 0 ]; then @@ -38,14 +37,13 @@ function parseArgs { case "$1" in "-a" | "--android" ) ANDROID_BUILD=true; shift ;; "-i" | "--ios" ) IOS_BUILD=true; shift ;; - "-z" | "--amnezia" ) AMNEZIA_BUILD=true; shift ;; "--no-docker" ) DOCKER_BUILD=false; shift ;; -- ) shift; break ;; * ) break ;; esac done - echo "android:$ANDROID_BUILD ios:$IOS_BUILD docker:$DOCKER_BUILD amnezia:$AMNEZIA_BUILD" + echo "android:$ANDROID_BUILD ios:$IOS_BUILD docker:$DOCKER_BUILD" set -eu } @@ -71,13 +69,6 @@ function is_docker_build { return 1 } -function is_amnezia_build { - if [ "$AMNEZIA_BUILD" = true ]; then - return 0 - fi - return 1 -} - function win_deduce_lib_executable_path { msbuild_path="$(which msbuild.exe)" msbuild_dir=$(dirname "$msbuild_path") @@ -308,7 +299,6 @@ function build_wireguard_go { esac } -AMNEZIA_DIR="libamnezia" LIB_DIR="libwg" # Ensure we are in the correct directory for the execution of this script diff --git a/wireguard/libamnezia/Android.mk b/wireguard/libamnezia/Android.mk deleted file mode 100644 index 1f8603ed54..0000000000 --- a/wireguard/libamnezia/Android.mk +++ /dev/null @@ -1,39 +0,0 @@ -# SPDX-License-Identifier: Apache-2.0 -# -# Copyright © 2017-2019 WireGuard LLC. All Rights Reserved. - -DESTDIR ?= $(CURDIR)/../../build/lib/$(RUST_TARGET_TRIPLE) - -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 - -export CGO_CFLAGS := $(CFLAGS) -export CGO_LDFLAGS := $(LDFLAGS) -export CC := $(ANDROID_C_COMPILER) -export GOARCH := $(NDK_GO_ARCH_MAP_$(ANDROID_ARCH_NAME)) -export GOOS := android -export CGO_ENABLED := 1 - -default: $(DESTDIR)/libwg.so - -GOBUILDARCH := $(NDK_GO_ARCH_MAP_$(shell uname -m)) -GOBUILDOS := $(shell uname -s | tr '[:upper:]' '[:lower:]') -GOBUILDVERSION := 1.22.6 -# TODO: Add checksum? -GOBUILDTARBALL := https://go.dev/dl/go$(GOBUILDVERSION).$(GOBUILDOS)-$(GOBUILDARCH).tar.gz -GOBUILDVERSION_NEEDED := go version go$(GOBUILDVERSION) $(GOBUILDOS)/$(GOBUILDARCH) - -$(DESTDIR)/libwg.so: - mkdir -p $(DESTDIR) - go get -tags "linux android" - chmod -fR +w "$(GOPATH)/pkg/mod" - go build -tags "linux android" -ldflags="-X main.socketDirectory=/data/data/$(ANDROID_PACKAGE_NAME)/cache/wireguard" -v -o "$@" -buildmode c-shared - rm -f $(DESTDIR)/libwg.h - - -clean: - rm -f $(DESTDIR)/libwg.so diff --git a/wireguard/libamnezia/Dockerfile_AndroidPatchedGoruntime b/wireguard/libamnezia/Dockerfile_AndroidPatchedGoruntime deleted file mode 100644 index cbdec91784..0000000000 --- a/wireguard/libamnezia/Dockerfile_AndroidPatchedGoruntime +++ /dev/null @@ -1,50 +0,0 @@ -# To build the image: -# docker build . -t docker.io/pronebird1337/nymtech-android-app -f Dockerfile_AndroidPatchedGoruntime -# To push the image to docker.io: -# docker push docker.io/pronebird1337/nymtech-android-app - -FROM debian@sha256:77f46c1cf862290e750e913defffb2828c889d291a93bdd10a7a0597720948fc - -RUN apt-get update -y && apt-get install -y \ - curl \ - file \ - gcc \ - git \ - make \ - python \ - unzip - -# Install Android NDK -RUN cd /tmp && \ - curl -sf -L -o ndk.zip https://dl.google.com/android/repository/android-ndk-r20b-linux-x86_64.zip && \ - echo "8381c440fe61fcbb01e209211ac01b519cd6adf51ab1c2281d5daad6ca4c8c8c ndk.zip" | sha256sum -c - && \ - mkdir /opt/android && \ - cd /opt/android && \ - unzip -q /tmp/ndk.zip && \ - rm /tmp/ndk.zip - - -ENV ANDROID_NDK_HOME="/opt/android/android-ndk-r20b" -ENV NDK_TOOLCHAIN_DIR="${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin" - -ENV GOLANG_VERSION=1.22.6 -ENV GOLANG_HASH=999805bed7d9039ec3da1a53bfbcafc13e367da52aa823cb60b68ba22d44c616 - -# Install Go-lang and patch it to use the appropriate monotonic clock -COPY goruntime-boottime-over-monotonic.diff /opt/goruntime-boottime-over-monotonic.diff -RUN cd /tmp && \ - curl -sf -L -o go.tgz https://go.dev/dl/go${GOLANG_VERSION}.linux-amd64.tar.gz && \ - echo "${GOLANG_HASH} go.tgz" | sha256sum -c - && \ - cd /opt && \ - tar -xzf /tmp/go.tgz && \ - rm /tmp/go.tgz && \ - patch -p1 -f -N -r- -d "/opt/go" < /opt/goruntime-boottime-over-monotonic.diff - -ENV PATH=${PATH}:/opt/go/bin -ENV GOROOT=/opt/go -ENV GOPATH=/opt/go-path - -RUN apt-get remove -y curl && \ - apt-get autoremove -y - -ENTRYPOINT [] diff --git a/wireguard/libamnezia/README.md b/wireguard/libamnezia/README.md deleted file mode 100644 index e5b96928f7..0000000000 --- a/wireguard/libamnezia/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# Introduction - -`libwg` is a tiny wrapper around `wireguard-go`, with the main purpose of providing a simple FFI-friendly interface. - -It currently offers support for the following platforms: - -- Linux -- macOS -- Android -- Windows - -# Organization - -`libwg.go` has shared code that is used on all platforms. - -`libwg_default.go` has default implementations for Linux-based systems. - -`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. - -Call `wgTurnOff` to destroy the tunnel. diff --git a/wireguard/libamnezia/build-android.sh b/wireguard/libamnezia/build-android.sh deleted file mode 100755 index 0d269919f2..0000000000 --- a/wireguard/libamnezia/build-android.sh +++ /dev/null @@ -1,74 +0,0 @@ -#!/usr/bin/env bash - -set -eu - -# Ensure we are in the correct directory for the execution of this script -script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -cd $script_dir - -# Keep a GOPATH in the build directory to maintain a cache of downloaded libraries -export GOPATH=$script_dir/../../build/android-go-path/ -mkdir -p $GOPATH - -for arch in ${ARCHITECTURES:-armv7 aarch64 x86_64 i686}; do - case "$arch" in - "aarch64") - export ANDROID_C_COMPILER="${NDK_TOOLCHAIN_DIR}/aarch64-linux-android21-clang" - export ANDROID_STRIP_TOOL="${NDK_TOOLCHAIN_DIR}/aarch64-linux-android-strip" - export RUST_TARGET_TRIPLE="aarch64-linux-android" - export ANDROID_ABI="arm64-v8a" - export ANDROID_ARCH_NAME="arm64" - ;; - "x86_64") - export ANDROID_C_COMPILER="${NDK_TOOLCHAIN_DIR}/x86_64-linux-android21-clang" - export ANDROID_STRIP_TOOL="${NDK_TOOLCHAIN_DIR}/x86_64-linux-android-strip" - export RUST_TARGET_TRIPLE="x86_64-linux-android" - export ANDROID_ABI="x86_64" - export ANDROID_ARCH_NAME="x86_64" - ;; - "armv7") - export ANDROID_C_COMPILER="${NDK_TOOLCHAIN_DIR}/armv7a-linux-androideabi21-clang" - export ANDROID_STRIP_TOOL="${NDK_TOOLCHAIN_DIR}/arm-linux-androideabi-strip" - export RUST_TARGET_TRIPLE="armv7-linux-androideabi" - export ANDROID_ABI="armeabi-v7a" - export ANDROID_ARCH_NAME="arm" - ;; - "i686") - export ANDROID_C_COMPILER="${NDK_TOOLCHAIN_DIR}/i686-linux-android21-clang" - export ANDROID_STRIP_TOOL="${NDK_TOOLCHAIN_DIR}/i686-linux-android-strip" - export RUST_TARGET_TRIPLE="i686-linux-android" - export ANDROID_ABI="x86" - export ANDROID_ARCH_NAME="x86" - ;; - esac - - # Build Wireguard-Go - echo $(pwd) - make -f Android.mk clean - -# this is determined by the NDK -# export CFLAGS="-D__ANDROID_API__=21" - - make -f Android.mk - - # Strip and copy the libray to `android/build/extraJni/$ANDROID_ABI` to be able to build the APK - UNSTRIPPED_LIB_PATH="../../build/lib/$RUST_TARGET_TRIPLE/libwg.so" - STRIPPED_LIB_PATH="../../android/app/build/extraJni/$ANDROID_ABI/libwg.so" - - # Create the directories with RWX permissions for all users so that the build server can clean - # the directories afterwards - mkdir -m 777 -p "$(dirname "$STRIPPED_LIB_PATH")" - - cp "$UNSTRIPPED_LIB_PATH" "$STRIPPED_LIB_PATH" - -# this is not available in the newer NDK -# $ANDROID_STRIP_TOOL --strip-unneeded --strip-debug -o "$STRIPPED_LIB_PATH" "$UNSTRIPPED_LIB_PATH" - - # Set permissions so that the build server can clean the outputs afterwards - chmod 777 "$STRIPPED_LIB_PATH" - - rm -rf build -done - -# ensure `git clean -fd` does not require root permissions -find $GOPATH -exec chmod +rw {} \; diff --git a/wireguard/libamnezia/container/container.go b/wireguard/libamnezia/container/container.go deleted file mode 100644 index 226616dbc0..0000000000 --- a/wireguard/libamnezia/container/container.go +++ /dev/null @@ -1,63 +0,0 @@ -/* SPDX-License-Identifier: Apache-2.0 - * - * Copyright (C) 2017-2019 Jason A. Donenfeld . All Rights Reserved. - * Copyright (C) 2020 Mullvad VPN AB. All Rights Reserved. - * Copyright 2024 - Nym Technologies SA - */ - -package container - -import ( - "errors" - "math" -) - -// Generic index-based memory storage -type Container[Context any] struct { - tunnels map[int32]Context -} - -func New[Context any]() Container[Context] { - return Container[Context]{ - tunnels: make(map[int32]Context), - } -} - -func (wself *Container[Context]) Insert(context Context) (int32, error) { - var i int32 - for i = 0; i < math.MaxInt32; i++ { - if _, exists := wself.tunnels[i]; !exists { - break - } - } - - if i == math.MaxInt32 { - return 0, errors.New("container is full") - } - - wself.tunnels[i] = context - return i, nil -} - -func (wself *Container[Context]) Get(handle int32) (*Context, error) { - context, ok := wself.tunnels[handle] - if !ok { - return nil, errors.New("invalid context handle") - } - return &context, nil -} - -func (wself *Container[Context]) Remove(handle int32) (*Context, error) { - context, ok := wself.tunnels[handle] - if !ok { - return nil, errors.New("invalid context handle") - } - delete(wself.tunnels, handle) - return &context, nil -} - -func (wself *Container[Context]) ForEach(callback func(Context)) { - for _, tunnel := range wself.tunnels { - callback(tunnel) - } -} diff --git a/wireguard/libamnezia/go.mod b/wireguard/libamnezia/go.mod deleted file mode 100644 index 56da2bbe59..0000000000 --- a/wireguard/libamnezia/go.mod +++ /dev/null @@ -1,21 +0,0 @@ -module github.com/nymtech/nym-vpn-client/wireguard/libwg - -go 1.22.3 - -toolchain go1.23.1 - -require ( - // golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 - github.com/amnezia-vpn/amneziawg-go v0.2.12 - golang.org/x/sys v0.18.0 - gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259 -) - -require ( - github.com/google/btree v1.0.1 // indirect - github.com/tevino/abool/v2 v2.1.0 // indirect - golang.org/x/crypto v0.21.0 // indirect - golang.org/x/net v0.21.0 // indirect - golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect - golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect -) diff --git a/wireguard/libamnezia/go.sum b/wireguard/libamnezia/go.sum deleted file mode 100644 index 42f90fd40d..0000000000 --- a/wireguard/libamnezia/go.sum +++ /dev/null @@ -1,18 +0,0 @@ -github.com/amnezia-vpn/amneziawg-go v0.2.12 h1:CxIQETy5kZ0ip/dFBpmnDxAcS/KuIQaJkOxDv5OQhVI= -github.com/amnezia-vpn/amneziawg-go v0.2.12/go.mod h1:d7WpNfzCRLy7ufGElJBYpD58WRmNjyLyt3IDHPY8AmM= -github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= -github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= -github.com/tevino/abool/v2 v2.1.0 h1:7w+Vf9f/5gmKT4m4qkayb33/92M+Um45F2BkHOR+L/c= -github.com/tevino/abool/v2 v2.1.0/go.mod h1:+Lmlqk6bHDWHqN1cbxqhwEAwMPXgc8I1SDEamtseuXY= -golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= -golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= -golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= -golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44= -golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -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= -gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259 h1:TbRPT0HtzFP3Cno1zZo7yPzEEnfu8EjLfl6IU9VfqkQ= -gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259/go.mod h1:AVgIgHMwK63XvmAzWG9vLQ41YnVHN0du0tEC46fI7yY= diff --git a/wireguard/libamnezia/goruntime-boottime-over-monotonic-darwin.diff b/wireguard/libamnezia/goruntime-boottime-over-monotonic-darwin.diff deleted file mode 100644 index 2f7f54edd0..0000000000 --- a/wireguard/libamnezia/goruntime-boottime-over-monotonic-darwin.diff +++ /dev/null @@ -1,61 +0,0 @@ -From 516dc0c15ff1ab781e0677606b5be72919251b3e Mon Sep 17 00:00:00 2001 -From: "Jason A. Donenfeld" -Date: Wed, 9 Dec 2020 14:07:06 +0100 -Subject: [PATCH] runtime: use libc_mach_continuous_time in nanotime on Darwin - -This makes timers account for having expired while a computer was -asleep, which is quite common on mobile devices. Note that -continuous_time absolute_time, except that it takes into account -time spent in suspend. - -Fixes #24595 - -Change-Id: Ia3282e8bd86f95ad2b76427063e60a005563f4eb ---- - src/runtime/sys_darwin.go | 2 +- - src/runtime/sys_darwin_amd64.s | 2 +- - src/runtime/sys_darwin_arm64.s | 2 +- - 3 files changed, 3 insertions(+), 3 deletions(-) - -diff --git a/src/runtime/sys_darwin.go b/src/runtime/sys_darwin.go -index 4a3f2fc453..4a69403b32 100644 ---- a/src/runtime/sys_darwin.go -+++ b/src/runtime/sys_darwin.go -@@ -440,7 +440,7 @@ func setNonblock(fd int32) { - //go:cgo_import_dynamic libc_usleep usleep "/usr/lib/libSystem.B.dylib" - - //go:cgo_import_dynamic libc_mach_timebase_info mach_timebase_info "/usr/lib/libSystem.B.dylib" --//go:cgo_import_dynamic libc_mach_absolute_time mach_absolute_time "/usr/lib/libSystem.B.dylib" -+//go:cgo_import_dynamic libc_mach_continuous_time mach_continuous_time "/usr/lib/libSystem.B.dylib" - //go:cgo_import_dynamic libc_clock_gettime clock_gettime "/usr/lib/libSystem.B.dylib" - //go:cgo_import_dynamic libc_sigaction sigaction "/usr/lib/libSystem.B.dylib" - //go:cgo_import_dynamic libc_pthread_sigmask pthread_sigmask "/usr/lib/libSystem.B.dylib" -diff --git a/src/runtime/sys_darwin_amd64.s b/src/runtime/sys_darwin_amd64.s -index 630fb5df64..4499c88802 100644 ---- a/src/runtime/sys_darwin_amd64.s -+++ b/src/runtime/sys_darwin_amd64.s -@@ -114,7 +114,7 @@ TEXT runtime·nanotime_trampoline(SB),NOSPLIT,$0 - PUSHQ BP - MOVQ SP, BP - MOVQ DI, BX -- CALL libc_mach_absolute_time(SB) -+ CALL libc_mach_continuous_time(SB) - MOVQ AX, 0(BX) - MOVL timebase<>+machTimebaseInfo_numer(SB), SI - MOVL timebase<>+machTimebaseInfo_denom(SB), DI // atomic read -diff --git a/src/runtime/sys_darwin_arm64.s b/src/runtime/sys_darwin_arm64.s -index 96d2ed1076..f046545395 100644 ---- a/src/runtime/sys_darwin_arm64.s -+++ b/src/runtime/sys_darwin_arm64.s -@@ -143,7 +143,7 @@ GLOBL timebase<>(SB),NOPTR,$(machTimebaseInfo__size) - - TEXT runtime·nanotime_trampoline(SB),NOSPLIT,$40 - MOVD R0, R19 -- BL libc_mach_absolute_time(SB) -+ BL libc_mach_continuous_time(SB) - MOVD R0, 0(R19) - MOVW timebase<>+machTimebaseInfo_numer(SB), R20 - MOVD $timebase<>+machTimebaseInfo_denom(SB), R21 --- -2.30.1 - diff --git a/wireguard/libamnezia/goruntime-boottime-over-monotonic.diff b/wireguard/libamnezia/goruntime-boottime-over-monotonic.diff deleted file mode 100644 index 73eb999a95..0000000000 --- a/wireguard/libamnezia/goruntime-boottime-over-monotonic.diff +++ /dev/null @@ -1,170 +0,0 @@ -From 61f3ae8298d1c503cbc31539e0f3a73446c7db9d Mon Sep 17 00:00:00 2001 -From: "Jason A. Donenfeld" -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/wireguard/libamnezia/libwg.go b/wireguard/libamnezia/libwg.go deleted file mode 100644 index a519822002..0000000000 --- a/wireguard/libamnezia/libwg.go +++ /dev/null @@ -1,74 +0,0 @@ -/* SPDX-License-Identifier: Apache-2.0 - * - * Copyright (C) 2017-2019 Jason A. Donenfeld . All Rights Reserved. - * Copyright (C) 2021 Mullvad VPN AB. All Rights Reserved. - * Copyright (C) 2024 Nym Technologies SA . All Rights Reserved. - */ - -package main - -// #include -import "C" - -import ( - "bufio" - "bytes" - "runtime" - "unsafe" - - "github.com/nymtech/nym-vpn-client/wireguard/libwg/container" - "github.com/amnezia-vpn/amneziawg-go/device" -) - -const ( - ERROR_GENERAL_FAILURE = -1 - ERROR_INTERMITTENT_FAILURE = -2 -) - -type TunnelContext struct { - Device *device.Device - Logger *device.Logger -} - -var tunnels container.Container[TunnelContext] - -func init() { - tunnels = container.New[TunnelContext]() -} - -//export wgTurnOff -func wgTurnOff(tunnelHandle int32) { - { - tunnel, err := tunnels.Remove(tunnelHandle) - if err != nil { - return - } - tunnel.Device.Close() - } - // Calling twice convinces the GC to release NOW. - runtime.GC() - runtime.GC() -} - -//export wgGetConfig -func wgGetConfig(tunnelHandle int32) *C.char { - tunnel, err := tunnels.Get(tunnelHandle) - if err != nil { - return nil - } - settings := new(bytes.Buffer) - writer := bufio.NewWriter(settings) - if err := tunnel.Device.IpcGetOperation(writer); err != nil { - tunnel.Logger.Errorf("Failed to get config for tunnel: %s\n", err) - return nil - } - writer.Flush() - return C.CString(settings.String()) -} - -//export wgFreePtr -func wgFreePtr(ptr unsafe.Pointer) { - C.free(ptr) -} - -func main() {} diff --git a/wireguard/libamnezia/libwg_android.go b/wireguard/libamnezia/libwg_android.go deleted file mode 100644 index 9470ed378b..0000000000 --- a/wireguard/libamnezia/libwg_android.go +++ /dev/null @@ -1,103 +0,0 @@ -/* SPDX-License-Identifier: Apache-2.0 - * - * Copyright (C) 2017-2019 Jason A. Donenfeld . All Rights Reserved. - * Copyright (C) 2021 Mullvad VPN AB. All Rights Reserved. - * Copyright (C) 2024 Nym Technologies SA . All Rights Reserved. - */ - -package main - -import ( - "C" - "bufio" - "strings" - "unsafe" - - "golang.org/x/sys/unix" - - "github.com/amnezia-vpn/amneziawg-go/conn" - "github.com/amnezia-vpn/amneziawg-go/device" - "github.com/amnezia-vpn/amneziawg-go/tun" - - "github.com/nymtech/nym-vpn-client/wireguard/libwg/logging" -) - -// 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 = unsafe.Pointer - -//export wgTurnOn -func wgTurnOn(cSettings *C.char, fd int, logSink LogSink, logContext LogContext) int32 { - logger := logging.NewLogger(logSink, logContext) - - if cSettings == nil { - logger.Errorf("cSettings is null\n") - return ERROR_GENERAL_FAILURE - } - settings := C.GoString(cSettings) - - 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() - - context := TunnelContext{ - Device: device, - Logger: logger, - } - - handle, err := tunnels.Insert(context) - if err != nil { - logger.Errorf("%s\n", err) - device.Close() - return ERROR_GENERAL_FAILURE - } - - return handle -} - -//export wgGetSocketV4 -func wgGetSocketV4(tunnelHandle int32) int32 { - tunnel, err := tunnels.Get(tunnelHandle) - if err != nil { - return ERROR_GENERAL_FAILURE - } - peek := tunnel.Device.Bind().(conn.PeekLookAtSocketFd) - fd, err := peek.PeekLookAtSocketFd4() - if err != nil { - return ERROR_GENERAL_FAILURE - } - return int32(fd) -} - -//export wgGetSocketV6 -func wgGetSocketV6(tunnelHandle int32) int32 { - tunnel, err := tunnels.Get(tunnelHandle) - if err != nil { - return ERROR_GENERAL_FAILURE - } - peek := tunnel.Device.Bind().(conn.PeekLookAtSocketFd) - fd, err := peek.PeekLookAtSocketFd6() - if err != nil { - return ERROR_GENERAL_FAILURE - } - return int32(fd) -} diff --git a/wireguard/libamnezia/libwg_default.go b/wireguard/libamnezia/libwg_default.go deleted file mode 100644 index 050cdb8799..0000000000 --- a/wireguard/libamnezia/libwg_default.go +++ /dev/null @@ -1,97 +0,0 @@ -//go:build (darwin || linux) && !android && !ios - -/* SPDX-License-Identifier: Apache-2.0 - * - * Copyright (C) 2017-2019 Jason A. Donenfeld . All Rights Reserved. - * Copyright (C) 2021 Mullvad VPN AB. All Rights Reserved. - * Copyright (C) 2024 Nym Technologies SA . All Rights Reserved. - */ - -package main - -// #include -import "C" -import ( - "bufio" - "os" - "strings" - "unsafe" - - "github.com/amnezia-vpn/amneziawg-go/conn" - "github.com/amnezia-vpn/amneziawg-go/device" - "github.com/amnezia-vpn/amneziawg-go/tun" - - "github.com/nymtech/nym-vpn-client/wireguard/libwg/logging" -) - -// 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 = unsafe.Pointer - -//export wgTurnOn -func wgTurnOn(mtu int, cSettings *C.char, fd int, logSink LogSink, logContext LogContext) int32 { - logger := logging.NewLogger(logSink, logContext) - - if cSettings == nil { - logger.Errorf("cSettings is null\n") - return ERROR_GENERAL_FAILURE - } - settings := C.GoString(cSettings) - - file := os.NewFile(uintptr(fd), "") - tunDevice, err := tun.CreateTUNFromFile(file, mtu) - if err != nil { - logger.Errorf("%s\n", err) - if err.Error() == "bad file descriptor" { - return ERROR_INTERMITTENT_FAILURE - } - return ERROR_GENERAL_FAILURE - } - - device := device.NewDevice(tunDevice, conn.NewDefaultBind(), logger) - - setErr := device.IpcSetOperation(bufio.NewReader(strings.NewReader(settings))) - if setErr != nil { - logger.Errorf("%s\n", setErr) - device.Close() - return ERROR_INTERMITTENT_FAILURE - } - - device.Up() - - context := TunnelContext{ - Device: device, - Logger: logger, - } - - handle, err := tunnels.Insert(context) - if err != nil { - logger.Errorf("%s\n", err) - device.Close() - return ERROR_GENERAL_FAILURE - } - - return handle -} - -//export wgSetConfig -func wgSetConfig(tunnelHandle int32, cSettings *C.char) int32 { - tunnel, err := tunnels.Get(tunnelHandle) - if err != nil { - return ERROR_GENERAL_FAILURE - } - if cSettings == nil { - tunnel.Logger.Errorf("cSettings is null\n") - return ERROR_GENERAL_FAILURE - } - settings := C.GoString(cSettings) - - setError := tunnel.Device.IpcSetOperation(bufio.NewReader(strings.NewReader(settings))) - if setError != nil { - tunnel.Logger.Errorf("Failed to set device configuration\n") - tunnel.Logger.Errorf("%s\n", setError) - return ERROR_GENERAL_FAILURE - } - return 0 -} diff --git a/wireguard/libamnezia/libwg_ios.go b/wireguard/libamnezia/libwg_ios.go deleted file mode 100644 index d45ef6a4cd..0000000000 --- a/wireguard/libamnezia/libwg_ios.go +++ /dev/null @@ -1,97 +0,0 @@ -/* SPDX-License-Identifier: MIT - * - * Copyright (C) 2018-2019 Jason A. Donenfeld . All Rights Reserved. - * Copyright (C) 2024 Nym Technologies SA . All Rights Reserved. - */ - -package main - -import "C" -import ( - "os" - "time" - "unsafe" - - "github.com/nymtech/nym-vpn-client/wireguard/libwg/logging" - "golang.org/x/sys/unix" - "github.com/amnezia-vpn/amneziawg-go/conn" - "github.com/amnezia-vpn/amneziawg-go/device" - "github.com/amnezia-vpn/amneziawg-go/tun" -) - -// 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 = unsafe.Pointer - -//export wgTurnOn -func wgTurnOn(settings *C.char, tunFd int32, logSink LogSink, logContext LogContext) int32 { - logger := logging.NewLogger(logSink, logContext) - - dupTunFd, err := unix.Dup(int(tunFd)) - if err != nil { - logger.Errorf("Unable to dup tun fd: %v", err) - return ERROR_GENERAL_FAILURE - } - - err = unix.SetNonblock(dupTunFd, true) - if err != nil { - logger.Errorf("Unable to set tun fd as non blocking: %v", err) - unix.Close(dupTunFd) - return ERROR_GENERAL_FAILURE - } - tun, err := tun.CreateTUNFromFile(os.NewFile(uintptr(dupTunFd), "/dev/tun"), 0) - if err != nil { - logger.Errorf("Unable to create new tun device from fd: %v", err) - unix.Close(dupTunFd) - return ERROR_INTERMITTENT_FAILURE - } - logger.Verbosef("Attaching to interface") - dev := device.NewDevice(tun, conn.NewStdNetBind(), logger) - - err = dev.IpcSet(C.GoString(settings)) - if err != nil { - logger.Errorf("Unable to set IPC settings: %v", err) - unix.Close(dupTunFd) - return ERROR_GENERAL_FAILURE - } - - dev.DisableSomeRoamingForBrokenMobileSemantics() - dev.Up() - - logger.Verbosef("Device started") - - context := TunnelContext{ - Device: dev, - Logger: logger, - } - - handle, err := tunnels.Insert(context) - if err != nil { - logger.Errorf("%s", err) - dev.Close() - return ERROR_GENERAL_FAILURE - } - - return handle -} - -//export wgBumpSockets -func wgBumpSockets(tunnelHandle int32) { - tunnel, err := tunnels.Get(tunnelHandle) - if err != nil { - return - } - go func() { - for i := 0; i < 10; i++ { - err := tunnel.Device.BindUpdate() - if err == nil { - tunnel.Device.SendKeepalivesToPeersWithCurrentKeypair() - return - } - tunnel.Logger.Errorf("Unable to update bind, try %d: %v", i+1, err) - time.Sleep(time.Second / 2) - } - tunnel.Logger.Errorf("Gave up trying to update bind; tunnel is likely dysfunctional") - }() -} diff --git a/wireguard/libamnezia/libwg_mobile.go b/wireguard/libamnezia/libwg_mobile.go deleted file mode 100644 index d80d4b0126..0000000000 --- a/wireguard/libamnezia/libwg_mobile.go +++ /dev/null @@ -1,40 +0,0 @@ -//go:build ios || android - -/* SPDX-License-Identifier: MIT - * - * Copyright (C) 2018-2019 Jason A. Donenfeld . All Rights Reserved. - * Copyright (C) 2024 Nym Technologies SA . All Rights Reserved. - */ - -package main - -import "C" - -import ( - "bufio" - "strings" -) - -//export wgSetConfig -func wgSetConfig(tunnelHandle int32, cSettings *C.char) int32 { - tunnel, err := tunnels.Get(tunnelHandle) - if err != nil { - return ERROR_GENERAL_FAILURE - } - if cSettings == nil { - tunnel.Logger.Errorf("cSettings is null\n") - return ERROR_GENERAL_FAILURE - } - settings := C.GoString(cSettings) - - err = tunnel.Device.IpcSetOperation(bufio.NewReader(strings.NewReader(settings))) - if err != nil { - tunnel.Logger.Errorf("Failed to set device configuration\n") - tunnel.Logger.Errorf("%s\n", err) - return ERROR_GENERAL_FAILURE - } - - tunnel.Device.DisableSomeRoamingForBrokenMobileSemantics() - - return 0 -} diff --git a/wireguard/libamnezia/libwg_windows.go b/wireguard/libamnezia/libwg_windows.go deleted file mode 100644 index 0a13431dd6..0000000000 --- a/wireguard/libamnezia/libwg_windows.go +++ /dev/null @@ -1,153 +0,0 @@ -/* SPDX-License-Identifier: Apache-2.0 - * - * Copyright (C) 2017-2019 Jason A. Donenfeld . All Rights Reserved. - * Copyright (C) 2021 Mullvad VPN AB. All Rights Reserved. - * Copyright (C) 2024 Nym Technologies SA . All Rights Reserved. - */ - -package main - -// #include -import "C" - -import ( - "bufio" - "strings" - "unsafe" - - "golang.org/x/sys/windows" - - "github.com/amnezia-vpn/amneziawg-go/conn" - "github.com/amnezia-vpn/amneziawg-go/device" - "github.com/amnezia-vpn/amneziawg-go/tun" - - "github.com/nymtech/nym-vpn-client/wireguard/libwg/logging" -) - -// 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 = unsafe.Pointer - -//export wgTurnOn -func wgTurnOn(cIfaceName *C.char, mtu int, cSettings *C.char, cIfaceNameOut **C.char, cLuidOut *uint64, logSink LogSink, logContext LogContext) int32 { - logger := logging.NewLogger(logSink, 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{0xafe43773, 0xe1f8, 0x4ebb, [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 := TunnelContext{ - 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 handle -} - -//export wgSetConfig -func wgSetConfig(tunnelHandle int32, cSettings *C.char) int32 { - tunnel, err := tunnels.Get(tunnelHandle) - if err != nil { - return ERROR_GENERAL_FAILURE - } - if cSettings == nil { - tunnel.Logger.Errorf("cSettings is null\n") - return ERROR_GENERAL_FAILURE - } - settings := C.GoString(cSettings) - - setError := tunnel.Device.IpcSetOperation(bufio.NewReader(strings.NewReader(settings))) - if setError != nil { - tunnel.Logger.Errorf("Failed to set device configuration\n") - tunnel.Logger.Errorf("%s\n", setError) - return ERROR_GENERAL_FAILURE - } - return 0 -} - -//export wgRebindTunnelSocket -func wgRebindTunnelSocket(family uint16, interfaceIndex uint32) { - tunnels.ForEach(func(tunnel TunnelContext) { - 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) - } - } - }) -} diff --git a/wireguard/libamnezia/logging/logging.go b/wireguard/libamnezia/logging/logging.go deleted file mode 100644 index 6d81ccf7a0..0000000000 --- a/wireguard/libamnezia/logging/logging.go +++ /dev/null @@ -1,60 +0,0 @@ -/* SPDX-License-Identifier: Apache-2.0 - * - * Copyright (C) 2017-2019 Jason A. Donenfeld . All Rights Reserved. - * Copyright (C) 2021 Mullvad VPN AB. All Rights Reserved. - */ - -package logging - -// #include -// #include -// #ifndef WIN32 -// #define __stdcall -// #endif -// typedef void (__stdcall *LogSink)(unsigned int, const char *, void *); -// static void callLogSink(void *logSink, int level, const char *message, void *context) -// { -// ((LogSink)logSink)((unsigned int)level, message, context); -// } -import "C" - -import ( - "log" - "unsafe" - - "github.com/amnezia-vpn/amneziawg-go/device" -) - -// Define type aliases. -type LogSink = unsafe.Pointer -type LogContext = unsafe.Pointer - -type Logger struct { - sink LogSink - context LogContext - level C.int -} - -func (l *Logger) Write(message []byte) (int, error) { - msg := C.CString(string(message)) - C.callLogSink(l.sink, l.level, msg, l.context) - C.free(unsafe.Pointer(msg)) - return len(message), nil -} - -func NewLogger(logSink LogSink, logContext LogContext) *device.Logger { - logger := new(device.Logger) - - logger.Verbosef = log.New( - &Logger{sink: logSink, context: logContext, level: device.LogLevelVerbose}, - "", - 0, - ).Printf - logger.Errorf = log.New( - &Logger{sink: logSink, context: logContext, level: device.LogLevelError}, - "", - 0, - ).Printf - - return logger -} diff --git a/wireguard/libamnezia/netstack_android.go b/wireguard/libamnezia/netstack_android.go deleted file mode 100644 index 1c2b8d2690..0000000000 --- a/wireguard/libamnezia/netstack_android.go +++ /dev/null @@ -1,39 +0,0 @@ -/* SPDX-License-Identifier: MIT - * - * Copyright (C) 2018-2019 Jason A. Donenfeld . All Rights Reserved. - * Copyright (C) 2024 Nym Technologies SA . All Rights Reserved. - */ - -package main - -import "C" - -import "github.com/amnezia-vpn/amneziawg-go/conn" - -//export wgNetGetSocketV4 -func wgNetGetSocketV4(tunnelHandle int32) int32 { - tunnel, err := netTunnelHandles.Get(tunnelHandle) - if err != nil { - return ERROR_GENERAL_FAILURE - } - peek := tunnel.Device.Bind().(conn.PeekLookAtSocketFd) - fd, err := peek.PeekLookAtSocketFd4() - if err != nil { - return ERROR_GENERAL_FAILURE - } - return int32(fd) -} - -//export wgNetGetSocketV6 -func wgNetGetSocketV6(tunnelHandle int32) int32 { - tunnel, err := netTunnelHandles.Get(tunnelHandle) - if err != nil { - return ERROR_GENERAL_FAILURE - } - peek := tunnel.Device.Bind().(conn.PeekLookAtSocketFd) - fd, err := peek.PeekLookAtSocketFd6() - if err != nil { - return ERROR_GENERAL_FAILURE - } - return int32(fd) -} diff --git a/wireguard/libamnezia/netstack_mobile.go b/wireguard/libamnezia/netstack_mobile.go deleted file mode 100644 index b1f197c8f6..0000000000 --- a/wireguard/libamnezia/netstack_mobile.go +++ /dev/null @@ -1,200 +0,0 @@ -//go:build ios || android - -/* SPDX-License-Identifier: MIT - * - * Copyright (C) 2018-2019 Jason A. Donenfeld . All Rights Reserved. - * Copyright (C) 2024 Nym Technologies SA . All Rights Reserved. - */ - -package main - -import "C" - -import ( - "net/netip" - "strings" - - "github.com/nymtech/nym-vpn-client/wireguard/libwg/container" - "github.com/nymtech/nym-vpn-client/wireguard/libwg/logging" - "github.com/nymtech/nym-vpn-client/wireguard/libwg/udp_forwarder" - "github.com/amnezia-vpn/amneziawg-go/conn" - "github.com/amnezia-vpn/amneziawg-go/device" - "github.com/amnezia-vpn/amneziawg-go/tun/netstack" -) - -type netTunnelHandle struct { - *device.Device - *netstack.Net - *device.Logger -} - -var netTunnelHandles container.Container[netTunnelHandle] -var udpForwarders container.Container[*udp_forwarder.UDPForwarder] - -func init() { - netTunnelHandles = container.New[netTunnelHandle]() - udpForwarders = container.New[*udp_forwarder.UDPForwarder]() -} - -//export wgNetTurnOn -func wgNetTurnOn(localAddresses *C.char, dnsAddresses *C.char, mtu int, settings *C.char, logSink LogSink, logContext LogContext) int32 { - logger := logging.NewLogger(logSink, logContext) - - // Parse comma separated list of IP addresses - tunAddrs, err := parseIPAddrs(C.GoString(localAddresses)) - if err != nil { - logger.Errorf("Failed to parse local addresses: %v", err) - return ERROR_GENERAL_FAILURE - } - - // Parse comma separated list of DNS addresses - dnsAddrs, err := parseIPAddrs(C.GoString(dnsAddresses)) - if err != nil { - logger.Errorf("Failed to parse dns addresses: %v", err) - return ERROR_GENERAL_FAILURE - } - - tun, tnet, err := netstack.CreateNetTUN(tunAddrs, dnsAddrs, mtu) - if err != nil { - logger.Errorf("Failed to create net tun: %v", err) - return ERROR_GENERAL_FAILURE - } - - dev := device.NewDevice( - tun, - conn.NewDefaultBind(), - logger, - ) - if dev == nil { - logger.Errorf("Failed to create device") - return ERROR_GENERAL_FAILURE - } - - err = dev.IpcSet(C.GoString(settings)) - if err != nil { - logger.Errorf("Unable to set IPC settings: %v", err) - dev.Close() - return ERROR_GENERAL_FAILURE - } - - dev.DisableSomeRoamingForBrokenMobileSemantics() - err = dev.Up() - if err != nil { - logger.Errorf("Failed to set device state to Up: %v", err) - dev.Close() - return ERROR_GENERAL_FAILURE - } - - logger.Verbosef("Net device started") - - i, err := netTunnelHandles.Insert(netTunnelHandle{dev, tnet, logger}) - if err != nil { - logger.Errorf("Failed to store tunnel: %v", err) - dev.Close() - return ERROR_GENERAL_FAILURE - } - - return i -} - -//export wgNetTurnOff -func wgNetTurnOff(tunnelHandle int32) { - dev, err := netTunnelHandles.Remove(tunnelHandle) - if err != nil { - return - } - dev.Close() -} - -//export wgNetSetConfig -func wgNetSetConfig(tunnelHandle int32, settings *C.char) int64 { - dev, err := netTunnelHandles.Get(tunnelHandle) - if err != nil { - return 0 - } - err = dev.IpcSet(C.GoString(settings)) - if err != nil { - dev.Errorf("Unable to set IPC settings: %v", err) - if ipcErr, ok := err.(*device.IPCError); ok { - return ipcErr.ErrorCode() - } - return ERROR_GENERAL_FAILURE - } - - dev.DisableSomeRoamingForBrokenMobileSemantics() - - return 0 -} - -//export wgNetGetConfig -func wgNetGetConfig(tunnelHandle int32) *C.char { - device, err := netTunnelHandles.Get(tunnelHandle) - if err != nil { - return nil - } - settings, err := device.IpcGet() - if err != nil { - return nil - } - return C.CString(settings) -} - -//export wgNetOpenConnectionThroughTunnel -func wgNetOpenConnectionThroughTunnel(entryTunnelHandle int32, listenPort uint16, clientPort uint16, exitEndpointStr *C.char, logSink LogSink, logContext LogContext) int32 { - logger := logging.NewLogger(logSink, logContext) - - dev, err := netTunnelHandles.Get(entryTunnelHandle) - if err != nil { - dev.Errorf("Invalid tunnel handle: %d", entryTunnelHandle) - return ERROR_GENERAL_FAILURE - } - - exitEndpoint, err := netip.ParseAddrPort(C.GoString(exitEndpointStr)) - if err != nil { - dev.Errorf("Failed to parse endpoint: %v", err) - return ERROR_GENERAL_FAILURE - } - - forwarderConfig := udp_forwarder.UDPForwarderConfig{ - ListenPort: listenPort, - ClientPort: clientPort, - ExitEndpoint: exitEndpoint, - } - - udpForwarder, err := udp_forwarder.New(forwarderConfig, dev.Net, logger) - if err != nil { - dev.Errorf("Failed to create udp forwarder: %v", err) - return ERROR_GENERAL_FAILURE - } - - forwarderHandle, err := udpForwarders.Insert(udpForwarder) - if err != nil { - dev.Errorf("Failed to store udp forwarder: %v", err) - udpForwarder.Close() - return ERROR_GENERAL_FAILURE - } - - return forwarderHandle -} - -//export wgNetCloseConnectionThroughTunnel -func wgNetCloseConnectionThroughTunnel(udpForwarderHandle int32) { - udpForwarder, err := udpForwarders.Remove(udpForwarderHandle) - if err != nil { - return - } - (*udpForwarder).Close() -} - -// Parse a list of comma-separated IP addresses into array of netip.Addr structs. -func parseIPAddrs(input string) ([]netip.Addr, error) { - addrs := []netip.Addr{} - for _, s := range strings.Split(input, ",") { - addr, err := netip.ParseAddr(strings.TrimSpace(s)) - if err != nil { - return addrs, err - } - addrs = append(addrs, addr) - } - return addrs, nil -} diff --git a/wireguard/libamnezia/udp_forwarder/udp_forwarder.go b/wireguard/libamnezia/udp_forwarder/udp_forwarder.go deleted file mode 100644 index 54217ece85..0000000000 --- a/wireguard/libamnezia/udp_forwarder/udp_forwarder.go +++ /dev/null @@ -1,194 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-only - * - * Copyright 2024 - Nym Technologies SA - */ - -package udp_forwarder - -import ( - "net" - "net/netip" - "sync" - "time" - - "github.com/amnezia-vpn/amneziawg-go/device" - "github.com/amnezia-vpn/amneziawg-go/tun/netstack" - "gvisor.dev/gvisor/pkg/tcpip/adapters/gonet" -) - -const UDP_WRITE_TIMEOUT = time.Duration(5) * time.Second -const MAX_UDP_DATAGRAM_LEN = 65535 - -type UDPForwarderConfig struct { - // Listen port for incoming WireGuard traffic. - // For IPv4 exit endpoint, the listening port is bound to 127.0.0.1, for IPv6 it's ::1. - ListenPort uint16 - - // Client port on loopback from which the incoming WireGuard connection will be received. - // Only packets from this port will be passed through to the exit endpoint. - ClientPort uint16 - - // Exit endpoint which will receive the raw WireGuard packets received on the listen port. - // The connection to exit endpoint is established over the entry tunnel, thus it creates - // a tunnel inside of tunnel. - ExitEndpoint netip.AddrPort -} - -// UDP forwarder that creates a bidirectional connection between a local and exit UDP endpoints -// over the netstack-based WireGuard tunnel. -type UDPForwarder struct { - // Logger. - logger *device.Logger - - // Netstack tunnel wrapping the inbound WireGuard traffic. - tnet *netstack.Net - - // UDP listener that receives inbound WireGuard traffic destined to exit endpoint. - listener *net.UDPConn - - // Outbound connection to the exit endpoint over the entry tunnel. - outbound *gonet.UDPConn - - // Wait group used to signal when all goroutines have finished execution. - waitGroup *sync.WaitGroup -} - -func New(config UDPForwarderConfig, tnet *netstack.Net, logger *device.Logger) (*UDPForwarder, error) { - var listenAddr *net.UDPAddr - var clientAddr *net.UDPAddr - - // Use the same ip protocol family as exit endpoint. - if config.ExitEndpoint.Addr().Is4() { - loopback := netip.AddrFrom4([4]byte{127, 0, 0, 1}) - listenAddr = net.UDPAddrFromAddrPort(netip.AddrPortFrom(loopback, config.ListenPort)) - clientAddr = net.UDPAddrFromAddrPort(netip.AddrPortFrom(loopback, config.ClientPort)) - } else { - listenAddr = net.UDPAddrFromAddrPort(netip.AddrPortFrom(netip.IPv6Loopback(), config.ListenPort)) - clientAddr = net.UDPAddrFromAddrPort(netip.AddrPortFrom(netip.IPv6Loopback(), config.ClientPort)) - } - - listener, err := net.ListenUDP("udp", listenAddr) - if err != nil { - return nil, err - } - - outbound, err := tnet.DialUDPAddrPort(netip.AddrPort{}, config.ExitEndpoint) - if err != nil { - return nil, err - } - - waitGroup := &sync.WaitGroup{} - wrapper := &UDPForwarder{ - logger, - tnet, - listener, - outbound, - waitGroup, - } - - waitGroup.Add(2) - go wrapper.RoutineHandleInbound(listener, outbound, clientAddr) - go wrapper.RoutineHandleOutbound(listener, outbound, clientAddr) - - return wrapper, nil -} - -func (w *UDPForwarder) Close() { - // Close all connections. This should release any blocking ReadFromUDP() calls. - w.listener.Close() - w.outbound.Close() - - // Wait for all routines to complete. - w.waitGroup.Wait() -} - -func (w *UDPForwarder) Wait() { - w.waitGroup.Wait() -} - -func (w *UDPForwarder) RoutineHandleInbound(inbound *net.UDPConn, outbound *gonet.UDPConn, clientAddr *net.UDPAddr) { - defer w.waitGroup.Done() - - inboundBuffer := make([]byte, MAX_UDP_DATAGRAM_LEN) - - w.logger.Verbosef("udpforwarder(inbound): listening on %s", inbound.LocalAddr().String()) - defer w.logger.Verbosef("udpforwarder(inbound): closed") - - for { - // Receive the WireGuard packet from local port - bytesRead, senderAddr, err := inbound.ReadFromUDP(inboundBuffer) - if err != nil { - w.logger.Errorf("udpforwarder(inbound): %s", err.Error()) - // todo: handle error - return - } - - // Drop packet from unknown sender. - if !senderAddr.IP.IsLoopback() || senderAddr.Port != clientAddr.Port { - w.logger.Verbosef("udpforwarder(inbound): drop packet from unknown sender: %s, expected: %s.", senderAddr.String(), clientAddr.String()) - continue - } - - // Set write timeout for outbound. - deadline := time.Now().Add(UDP_WRITE_TIMEOUT) - err = outbound.SetWriteDeadline(deadline) - if err != nil { - w.logger.Errorf("udpforwarder(inbound): %s", err.Error()) - // todo: handle error - continue - } - - // Forward the packet over the outbound connection via another WireGuard tunnel. - _, err = outbound.Write(inboundBuffer[:bytesRead]) - if err != nil { - w.logger.Errorf("udpforwarder(inbound): %s", err.Error()) - // todo: handle error - continue - } - } -} - -func (w *UDPForwarder) RoutineHandleOutbound(inbound *net.UDPConn, outbound *gonet.UDPConn, clientAddr *net.UDPAddr) { - defer w.waitGroup.Done() - - remoteAddr := outbound.RemoteAddr().(*net.UDPAddr) - w.logger.Verbosef("udpforwarder(outbound): dial %s", remoteAddr.String()) - defer w.logger.Verbosef("udpforwarder(outbound): closed") - - outboundBuffer := make([]byte, MAX_UDP_DATAGRAM_LEN) - - for { - // Receive WireGuard packet from remote server. - bytesRead, senderAddr, err := outbound.ReadFrom(outboundBuffer) - if err != nil { - w.logger.Errorf("udpforwarder(outbound): %s", err.Error()) - // todo: handle error - return - } - // Cast net.Addr to net.UDPAddr - senderUDPAddr := senderAddr.(*net.UDPAddr) - - // Drop packet from unknown sender. - if !senderUDPAddr.IP.Equal(remoteAddr.IP) || senderUDPAddr.Port != remoteAddr.Port { - w.logger.Verbosef("udpforwarder(outbound): drop packet from unknown sender: %s, expected: %s", senderUDPAddr.String(), remoteAddr.String()) - continue - } - - // Set write timeout for inbound. - deadline := time.Now().Add(UDP_WRITE_TIMEOUT) - err = inbound.SetWriteDeadline(deadline) - if err != nil { - w.logger.Errorf("udpforwarder(outbound): %s", err.Error()) - // todo: handle error - continue - } - - // Forward packet from remote to local client. - _, err = inbound.WriteToUDP(outboundBuffer[:bytesRead], clientAddr) - if err != nil { - w.logger.Errorf("udpforwarder(outbound): %s", err.Error()) - // todo: handle error - continue - } - } -} diff --git a/wireguard/libwg/go.mod b/wireguard/libwg/go.mod index 1b0e3d3ae0..49efeffa02 100644 --- a/wireguard/libwg/go.mod +++ b/wireguard/libwg/go.mod @@ -1,17 +1,21 @@ module github.com/nymtech/nym-vpn-client/wireguard/libwg -go 1.22.0 +go 1.22.3 + +toolchain go1.23.1 require ( - golang.org/x/sys v0.12.0 - golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 + // golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 + github.com/amnezia-vpn/amneziawg-go v0.2.12 + golang.org/x/sys v0.18.0 gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259 ) require ( github.com/google/btree v1.0.1 // indirect - golang.org/x/crypto v0.13.0 // indirect - golang.org/x/net v0.15.0 // indirect + github.com/tevino/abool/v2 v2.1.0 // indirect + golang.org/x/crypto v0.21.0 // indirect + golang.org/x/net v0.23.0 // indirect golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect ) diff --git a/wireguard/libwg/go.sum b/wireguard/libwg/go.sum index 91d16755cc..3bfc3cb95d 100644 --- a/wireguard/libwg/go.sum +++ b/wireguard/libwg/go.sum @@ -1,16 +1,18 @@ +github.com/amnezia-vpn/amneziawg-go v0.2.12 h1:CxIQETy5kZ0ip/dFBpmnDxAcS/KuIQaJkOxDv5OQhVI= +github.com/amnezia-vpn/amneziawg-go v0.2.12/go.mod h1:d7WpNfzCRLy7ufGElJBYpD58WRmNjyLyt3IDHPY8AmM= github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= -golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= -golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= -golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= -golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= -golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= -golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +github.com/tevino/abool/v2 v2.1.0 h1:7w+Vf9f/5gmKT4m4qkayb33/92M+Um45F2BkHOR+L/c= +github.com/tevino/abool/v2 v2.1.0/go.mod h1:+Lmlqk6bHDWHqN1cbxqhwEAwMPXgc8I1SDEamtseuXY= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= +golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44= golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 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-20231211153847-12269c276173 h1:/jFs0duh4rdb8uIfPMv78iAJGcPKDeqAFnaLBropIC4= -golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA= gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259 h1:TbRPT0HtzFP3Cno1zZo7yPzEEnfu8EjLfl6IU9VfqkQ= gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259/go.mod h1:AVgIgHMwK63XvmAzWG9vLQ41YnVHN0du0tEC46fI7yY= diff --git a/wireguard/libwg/libwg.go b/wireguard/libwg/libwg.go index c93ed7ae1b..a519822002 100644 --- a/wireguard/libwg/libwg.go +++ b/wireguard/libwg/libwg.go @@ -17,7 +17,7 @@ import ( "unsafe" "github.com/nymtech/nym-vpn-client/wireguard/libwg/container" - "golang.zx2c4.com/wireguard/device" + "github.com/amnezia-vpn/amneziawg-go/device" ) const ( diff --git a/wireguard/libwg/libwg_android.go b/wireguard/libwg/libwg_android.go index 7abc8706e9..9470ed378b 100644 --- a/wireguard/libwg/libwg_android.go +++ b/wireguard/libwg/libwg_android.go @@ -15,9 +15,9 @@ import ( "golang.org/x/sys/unix" - "golang.zx2c4.com/wireguard/conn" - "golang.zx2c4.com/wireguard/device" - "golang.zx2c4.com/wireguard/tun" + "github.com/amnezia-vpn/amneziawg-go/conn" + "github.com/amnezia-vpn/amneziawg-go/device" + "github.com/amnezia-vpn/amneziawg-go/tun" "github.com/nymtech/nym-vpn-client/wireguard/libwg/logging" ) diff --git a/wireguard/libwg/libwg_default.go b/wireguard/libwg/libwg_default.go index 70c1c4a701..050cdb8799 100644 --- a/wireguard/libwg/libwg_default.go +++ b/wireguard/libwg/libwg_default.go @@ -17,9 +17,9 @@ import ( "strings" "unsafe" - "golang.zx2c4.com/wireguard/conn" - "golang.zx2c4.com/wireguard/device" - "golang.zx2c4.com/wireguard/tun" + "github.com/amnezia-vpn/amneziawg-go/conn" + "github.com/amnezia-vpn/amneziawg-go/device" + "github.com/amnezia-vpn/amneziawg-go/tun" "github.com/nymtech/nym-vpn-client/wireguard/libwg/logging" ) diff --git a/wireguard/libwg/libwg_ios.go b/wireguard/libwg/libwg_ios.go index 949ab8ca8a..d45ef6a4cd 100644 --- a/wireguard/libwg/libwg_ios.go +++ b/wireguard/libwg/libwg_ios.go @@ -14,9 +14,9 @@ import ( "github.com/nymtech/nym-vpn-client/wireguard/libwg/logging" "golang.org/x/sys/unix" - "golang.zx2c4.com/wireguard/conn" - "golang.zx2c4.com/wireguard/device" - "golang.zx2c4.com/wireguard/tun" + "github.com/amnezia-vpn/amneziawg-go/conn" + "github.com/amnezia-vpn/amneziawg-go/device" + "github.com/amnezia-vpn/amneziawg-go/tun" ) // Redefined here because otherwise the compiler doesn't realize it's a type alias for a type that's safe to export. diff --git a/wireguard/libwg/libwg_windows.go b/wireguard/libwg/libwg_windows.go index d82b63d1ef..0a13431dd6 100644 --- a/wireguard/libwg/libwg_windows.go +++ b/wireguard/libwg/libwg_windows.go @@ -17,9 +17,9 @@ import ( "golang.org/x/sys/windows" - "golang.zx2c4.com/wireguard/conn" - "golang.zx2c4.com/wireguard/device" - "golang.zx2c4.com/wireguard/tun" + "github.com/amnezia-vpn/amneziawg-go/conn" + "github.com/amnezia-vpn/amneziawg-go/device" + "github.com/amnezia-vpn/amneziawg-go/tun" "github.com/nymtech/nym-vpn-client/wireguard/libwg/logging" ) diff --git a/wireguard/libwg/logging/logging.go b/wireguard/libwg/logging/logging.go index a917e96493..6d81ccf7a0 100644 --- a/wireguard/libwg/logging/logging.go +++ b/wireguard/libwg/logging/logging.go @@ -22,7 +22,7 @@ import ( "log" "unsafe" - "golang.zx2c4.com/wireguard/device" + "github.com/amnezia-vpn/amneziawg-go/device" ) // Define type aliases. diff --git a/wireguard/libwg/netstack_android.go b/wireguard/libwg/netstack_android.go index be6e1a6388..1c2b8d2690 100644 --- a/wireguard/libwg/netstack_android.go +++ b/wireguard/libwg/netstack_android.go @@ -8,7 +8,7 @@ package main import "C" -import "golang.zx2c4.com/wireguard/conn" +import "github.com/amnezia-vpn/amneziawg-go/conn" //export wgNetGetSocketV4 func wgNetGetSocketV4(tunnelHandle int32) int32 { diff --git a/wireguard/libwg/netstack_mobile.go b/wireguard/libwg/netstack_mobile.go index 1836dcb528..b1f197c8f6 100644 --- a/wireguard/libwg/netstack_mobile.go +++ b/wireguard/libwg/netstack_mobile.go @@ -17,9 +17,9 @@ import ( "github.com/nymtech/nym-vpn-client/wireguard/libwg/container" "github.com/nymtech/nym-vpn-client/wireguard/libwg/logging" "github.com/nymtech/nym-vpn-client/wireguard/libwg/udp_forwarder" - "golang.zx2c4.com/wireguard/conn" - "golang.zx2c4.com/wireguard/device" - "golang.zx2c4.com/wireguard/tun/netstack" + "github.com/amnezia-vpn/amneziawg-go/conn" + "github.com/amnezia-vpn/amneziawg-go/device" + "github.com/amnezia-vpn/amneziawg-go/tun/netstack" ) type netTunnelHandle struct { diff --git a/wireguard/libwg/udp_forwarder/udp_forwarder.go b/wireguard/libwg/udp_forwarder/udp_forwarder.go index 948b378234..54217ece85 100644 --- a/wireguard/libwg/udp_forwarder/udp_forwarder.go +++ b/wireguard/libwg/udp_forwarder/udp_forwarder.go @@ -11,8 +11,8 @@ import ( "sync" "time" - "golang.zx2c4.com/wireguard/device" - "golang.zx2c4.com/wireguard/tun/netstack" + "github.com/amnezia-vpn/amneziawg-go/device" + "github.com/amnezia-vpn/amneziawg-go/tun/netstack" "gvisor.dev/gvisor/pkg/tcpip/adapters/gonet" )