├── .github └── workflows │ ├── linux-build.yml │ └── windows-build.yml ├── COPYING ├── README.md ├── amneziawg-tools.spec ├── build.cmd ├── contrib ├── dns-hatchet │ ├── README │ ├── apply.sh │ └── hatchet.bash ├── embeddable-wg-library │ ├── Makefile │ ├── README │ ├── test.c │ ├── wireguard.c │ └── wireguard.h ├── external-tests │ ├── go │ │ └── main.go │ ├── haskell │ │ ├── Setup.hs │ │ ├── package.yaml │ │ ├── src │ │ │ ├── Data │ │ │ │ └── Time │ │ │ │ │ └── TAI64.hs │ │ │ └── Main.hs │ │ └── stack.yaml │ ├── python │ │ └── main.py │ └── rust │ │ ├── Cargo.toml │ │ └── src │ │ └── main.rs ├── extract-handshakes │ ├── Makefile │ ├── README │ ├── extract-handshakes.sh │ └── offset-finder.c ├── extract-keys │ ├── Makefile │ ├── README │ ├── config.c │ └── extract-keys.c ├── highlighter │ ├── Makefile │ ├── README │ ├── fuzz.c │ ├── gui │ │ ├── highlight.cpp │ │ └── highlight.pro │ ├── highlight.c │ ├── highlighter.c │ └── highlighter.h ├── json │ ├── README │ └── wg-json ├── keygen-html │ ├── README │ ├── keygen.html │ └── wireguard.js ├── launchd │ ├── README │ └── com.wireguard.wg0.plist ├── nat-hole-punching │ ├── README │ ├── nat-punch-client.c │ └── nat-punch-server.c ├── ncat-client-server │ ├── README │ ├── client-quick.sh │ ├── client.sh │ └── server.sh ├── peer-approver │ ├── README │ ├── accounts.csv │ ├── approve.sh │ └── notification-listener.c ├── reresolve-dns │ ├── README │ └── reresolve-dns.sh ├── sticky-sockets │ ├── README │ └── sticky-sockets.c └── synergy │ ├── README │ ├── synergy-client.sh │ └── synergy-server.sh ├── debian ├── NEWS ├── TODO ├── amneziawg-tools.README.Debian ├── amneziawg-tools.examples ├── amneziawg-tools.lintian-overrides ├── changelog ├── clean ├── compat ├── control ├── copyright ├── files ├── gbp.conf ├── patches │ ├── 0001-Avoid-using-git-during-build.patch │ ├── 0002-Avoid-requiring-glibc-2.25-for-wireguard-tools.patch │ └── series ├── rules ├── source │ └── format ├── tests │ ├── control │ ├── keygen │ ├── netns-mini │ └── wg-quick ├── upstream │ └── signing-key.asc └── watch └── src ├── Makefile ├── completion ├── wg-quick.bash-completion └── wg.bash-completion ├── config.c ├── config.h ├── containers.h ├── ctype.h ├── curve25519-fiat32.h ├── curve25519-hacl64.h ├── curve25519.c ├── curve25519.h ├── encoding.c ├── encoding.h ├── fuzz ├── Makefile ├── cmd.c ├── config.c ├── set.c ├── setconf.c ├── stringlist.c └── uapi.c ├── genkey.c ├── ipc-freebsd.h ├── ipc-linux.h ├── ipc-openbsd.h ├── ipc-uapi-unix.h ├── ipc-uapi-windows.h ├── ipc-uapi.h ├── ipc-windows.h ├── ipc.c ├── ipc.h ├── man ├── wg-quick.8 └── wg.8 ├── netlink.h ├── pubkey.c ├── set.c ├── setconf.c ├── show.c ├── showconf.c ├── subcommands.h ├── systemd ├── wg-quick.target └── wg-quick@.service ├── terminal.c ├── terminal.h ├── uapi ├── freebsd │ └── dev │ │ └── wg │ │ └── if_wg.h ├── linux │ └── linux │ │ └── wireguard.h ├── openbsd │ └── net │ │ └── if_wg.h └── windows │ └── wireguard.h ├── version.h ├── wg-quick ├── android.c ├── awg ├── darwin.bash ├── freebsd.bash ├── linux.bash └── openbsd.bash ├── wg.c └── wincompat ├── compat.h ├── include ├── arpa │ └── inet.h ├── hashtable.h ├── net │ └── if.h ├── netdb.h ├── netinet │ └── in.h └── sys │ └── socket.h ├── init.c ├── libc.c ├── loader.c ├── manifest.xml └── resources.rc /.github/workflows/linux-build.yml: -------------------------------------------------------------------------------- 1 | name: Linux 2 | 3 | on: [push] 4 | 5 | jobs: 6 | Build-for-Ubuntu: 7 | name: Build for Ubuntu 8 | runs-on: ubuntu-latest 9 | container: 10 | image: ubuntu:22.04 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v4 14 | 15 | - name: Build AmneziaWG tools 16 | run: | 17 | apt -y update && 18 | apt -y install build-essential && 19 | cd src && 20 | make && 21 | mkdir build && 22 | cp wg ./build/awg && 23 | cp wg-quick/linux.bash ./build/awg-quick 24 | 25 | - name: Upload artifact 26 | uses: actions/upload-artifact@v4 27 | with: 28 | name: ubuntu-22.04-amneziawg-tools 29 | path: ./src/build 30 | 31 | Build-for-Alpine: 32 | name: Build for Alpine 33 | runs-on: ubuntu-latest 34 | container: 35 | image: alpine:3.19 36 | steps: 37 | - name: Checkout 38 | uses: actions/checkout@v4 39 | 40 | - name: Build AmneziaWG tools 41 | run: | 42 | apk add linux-headers build-base && 43 | cd src && 44 | make && 45 | mkdir build && 46 | cp wg ./build/awg && 47 | cp wg-quick/linux.bash ./build/awg-quick 48 | 49 | - name: Upload artifact 50 | uses: actions/upload-artifact@v4 51 | with: 52 | name: alpine-3.19-amneziawg-tools 53 | path: ./src/build 54 | 55 | GitHub-Release: 56 | name: GitHub Release 57 | needs: [Build-for-Ubuntu, Build-for-Alpine] 58 | strategy: 59 | matrix: 60 | include: 61 | - os: "ubuntu" 62 | release: "22.04" 63 | - os: "alpine" 64 | release: "3.19" 65 | runs-on: ubuntu-latest 66 | if: startsWith(github.ref, 'refs/tags/') 67 | steps: 68 | - name: Download artifacts 69 | uses: actions/download-artifact@v4 70 | 71 | - name: Calculate checksums 72 | run: for file in $(find ./${{ matrix.os }}-${{ matrix.release }}-amneziawg-tools/ -type f); do openssl dgst -sha256 -r "$file" | awk '{print $1}' > "${file}.sha256"; done 73 | 74 | - name: Zip files 75 | run: zip -r ${{ matrix.os }}-${{ matrix.release }}-amneziawg-tools.zip ${{ matrix.os }}-${{ matrix.release }}-amneziawg-tools 76 | 77 | - name: Upload binaries to Release 78 | uses: svenstaro/upload-release-action@v2 79 | with: 80 | repo_token: ${{ secrets.GITHUB_TOKEN }} 81 | file: ./${{ matrix.os }}-${{ matrix.release }}-amneziawg-tools.zip 82 | tag: ${{ github.ref }} 83 | release_name: ${{ github.ref_name }} 84 | overwrite: true 85 | file_glob: true 86 | -------------------------------------------------------------------------------- /.github/workflows/windows-build.yml: -------------------------------------------------------------------------------- 1 | name: Windows 2 | 3 | on: [push] 4 | 5 | jobs: 6 | Build-for-Windows: 7 | name: Build for Windows 8 | runs-on: windows-latest 9 | steps: 10 | - name: Setup ccache 11 | uses: hendrikmuhs/ccache-action@v1.2 12 | 13 | - name: Checkout 14 | uses: actions/checkout@v4 15 | 16 | - name: Build AmneziaWG tools 17 | run: | 18 | cmd /c build.cmd 19 | mkdir build 20 | move x64 build\x64 21 | move x86 build\x86 22 | move arm64 build\arm64 23 | 24 | - name: Upload artifact 25 | uses: actions/upload-artifact@v4 26 | with: 27 | retention-days: 1 28 | name: windows-amneziawg-tools 29 | path: build 30 | 31 | GitHub-Release: 32 | name: GitHub Release 33 | needs: Build-for-Windows 34 | runs-on: ubuntu-latest 35 | if: startsWith(github.ref, 'refs/tags/') 36 | steps: 37 | - name: Checkout 38 | uses: actions/checkout@v4 39 | 40 | - name: Download artifacts 41 | uses: actions/download-artifact@v4 42 | 43 | - name: Calculate checksums 44 | run: for file in $(find ./ -name '*.exe' ); do openssl dgst -sha256 -r "$file" | awk '{print $1}' > "${file}.sha256"; done 45 | 46 | - name: Zip files 47 | run: for file in *; do zip -r ${file%.*}.zip $file; done 48 | 49 | - name: Upload binaries to Release 50 | uses: svenstaro/upload-release-action@v2 51 | with: 52 | repo_token: ${{ secrets.GITHUB_TOKEN }} 53 | file: windows-amneziawg-tools.zip 54 | tag: ${{ github.ref }} 55 | release_name: ${{ github.ref_name }} 56 | overwrite: true 57 | file_glob: true 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [wireguard-tools](https://git.zx2c4.com/wireguard-tools/about/) — tools for configuring [WireGuard](https://www.wireguard.com/) 2 | 3 | This supplies the main userspace tooling for using and configuring WireGuard 4 | tunnels, including the 5 | [`awg(8)`](https://git.zx2c4.com/wireguard-tools/about/src/man/wg.8) and 6 | [`awg-quick(8)`](https://git.zx2c4.com/wireguard-tools/about/src/man/wg-quick.8) 7 | utilities. This project supports Linux, OpenBSD, FreeBSD, macOS, Windows, and 8 | Android. 9 | 10 | **More information may be found at [WireGuard.com](https://www.wireguard.com/).** 11 | 12 | ## Building 13 | 14 | $ cd src 15 | $ make 16 | 17 | There are no dependencies other than a good C compiler and a sane libc. 18 | 19 | ## Installing 20 | 21 | # make install 22 | 23 | This command takes into account several environment variables: 24 | 25 | * `PREFIX` default: `/usr` 26 | * `DESTDIR` default: 27 | * `BINDIR` default: `$(PREFIX)/bin` 28 | * `LIBDIR` default: `$(PREFIX)/lib` 29 | * `MANDIR` default: `$(PREFIX)/share/man` 30 | * `BASHCOMPDIR` default: `$(PREFIX)/share/bash-completion/completions` 31 | * `RUNSTATEDIR` default: `/var/run` 32 | * `PKG_CONFIG` default: `pkg-config` 33 | 34 | * `WITH_BASHCOMPLETION` default: [auto-detect] 35 | * `WITH_WGQUICK` default: [auto-detect] 36 | * `WITH_SYSTEMDUNITS` default: [auto-detect] 37 | * `DEBUG` default: 38 | 39 | The first section is rather standard. The second section is not: 40 | 41 | * `WITH_BASHCOMPLETION` decides whether or not bash completion files for the 42 | tools are installed. This is just a nice thing for people who have bash. 43 | If you don't have bash, or don't want this, set the environment variable 44 | to `no`. If you'd like to force its use, even if bash-completion isn't 45 | detected in `DESTDIR`, then set it to `yes`. 46 | 47 | * `WITH_WGQUICK` decides whether or not the wg-quick(8) script is installed. 48 | This is a very quick and dirty bash script for reading a few extra 49 | variables from wg(8)-style configuration files, and automatically 50 | configures the interface. If you don't have bash, you probably don't want 51 | this at all. Likewise, if you already have a working network management 52 | tool or configuration, you probably want to integrate wg(8) or the direct 53 | WireGuard API into your network manager, rather than using wg-quick(8). 54 | But for folks who like simple quick and dirty scripts, this is nice. If you'd 55 | like to force its use, even if bash isn't detected in DESTDIR, then set it 56 | to `yes`. 57 | 58 | * `WITH_SYSTEMDUNITS` decides whether or not systemd units are installed for 59 | wg-quick(8). If you don't use systemd, you certainly don't want this, and 60 | should set it to `no`. If systemd isn't auto-detected, but you still would 61 | like to install it, set this to `yes`. 62 | 63 | * `DEBUG` decides whether to build with `-g`, when set to `yes`. 64 | 65 | If you're a simple `make && make install` kind of user, you can get away with 66 | not setting these variables and relying on the auto-detection. However, if 67 | you're writing a package for a distro, you'll want to explicitly set these, 68 | depending on what you want. 69 | 70 | ## `contrib/` 71 | 72 | The `contrib/` subdirectory contains various scripts and examples. Most of these 73 | are not immediately useful for production use, but should provide inspiration for 74 | creating fully-featured tools. See the `README` in each directory. 75 | 76 | ## License 77 | 78 | This project is released under the [GPLv2](COPYING). 79 | -------------------------------------------------------------------------------- /build.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | rem SPDX-License-Identifier: MIT 3 | rem Copyright (C) 2019-2021 WireGuard LLC. All Rights Reserved. 4 | 5 | setlocal enabledelayedexpansion 6 | set BUILDDIR=%~dp0 7 | set PATH=%BUILDDIR%.deps\llvm-mingw\bin;%BUILDDIR%src;%PATH% 8 | set PATHEXT=.exe 9 | cd /d %BUILDDIR% || exit /b 1 10 | 11 | if exist .deps/prepared goto :build 12 | :installdeps 13 | rmdir /s /q .deps 2> NUL 14 | mkdir .deps || goto :error 15 | cd .deps || goto :error 16 | call :download llvm-mingw-msvcrt.zip https://download.wireguard.com/windows-toolchain/distfiles/llvm-mingw-20201020-msvcrt-x86_64.zip 2e46593245090df96d15e360e092f0b62b97e93866e0162dca7f93b16722b844 || goto :error 17 | call :download wireguard-nt.zip https://download.wireguard.com/wireguard-nt/wireguard-nt-0.10.1.zip 772c0b1463d8d2212716f43f06f4594d880dea4f735165bd68e388fc41b81605 || goto :error 18 | copy /y NUL prepared > NUL || goto :error 19 | cd .. || goto :error 20 | 21 | :build 22 | call :build_plat x64 x86_64 amd64 || goto :error 23 | call :build_plat x86 i686 386 || goto :error 24 | call :build_plat arm64 aarch64 arm64 || goto :error 25 | 26 | :success 27 | echo [+] Success 28 | exit /b 0 29 | 30 | :download 31 | echo [+] Downloading %1 32 | curl --retry 3 -#fLo %1 %2 || exit /b 1 33 | echo [+] Verifying %1 34 | for /f %%a in ('CertUtil -hashfile %1 SHA256 ^| findstr /r "^[0-9a-f]*$"') do if not "%%a"=="%~3" exit /b 1 35 | echo [+] Extracting %1 36 | tar -xf %1 %~4 || exit /b 1 37 | echo [+] Cleaning up %1 38 | del %1 || exit /b 1 39 | goto :eof 40 | 41 | :build_plat 42 | mkdir %1 >NUL 2>&1 43 | echo [+] Assembling resources %1 44 | %~2-w64-mingw32-windres -I ".deps\wireguard-nt\bin\%~1" -DWIREGUARD_VERSION_ARRAY=0.5.3 -DWIREGUARD_VERSION_STR=0.5.3 -i src/wincompat/resources.rc -o "src/wincompat/resources_%~3.syso" -O coff -c 65001 || exit /b %errorlevel% 45 | echo [+] Building command line tools %1 46 | del src\*.exe src\*.o src\wincompat\*.o src\wincompat\*.lib 2> NUL 47 | set LDFLAGS=-s 48 | make --no-print-directory -C src PLATFORM=windows CC=%~2-w64-mingw32-gcc WINDRES=%~2-w64-mingw32-windres V=1 RUNSTATEDIR= SYSTEMDUNITDIR= -j%NUMBER_OF_PROCESSORS% || exit /b 1 49 | move /Y src\wg.exe "%~1\awg.exe" > NUL || exit /b 1 50 | goto :eof 51 | 52 | :error 53 | echo [-] Failed with error #%errorlevel%. 54 | cmd /c exit %errorlevel% 55 | 56 | 57 | -------------------------------------------------------------------------------- /contrib/dns-hatchet/README: -------------------------------------------------------------------------------- 1 | The DNS Hatchet 2 | =============== 3 | 4 | This is a workaround for distributions without resolvconf or any proper 5 | mechanism of setting the DNS. Running 'apply.sh` in this directory will 6 | insert 'hatchet.bash` into the right place in 'wg-quick.bash`. It is 7 | recommended that distributions without any resolvconf available run this 8 | before calling 'make install` in their packaging scripts. 9 | -------------------------------------------------------------------------------- /contrib/dns-hatchet/apply.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # SPDX-License-Identifier: GPL-2.0 3 | # 4 | # Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. 5 | 6 | ME="$(readlink -f "$(dirname "$(readlink -f "$0")")")" 7 | TOOLS="$ME/../../src" 8 | 9 | sed -i "/~~ function override insertion point ~~/r $ME/hatchet.bash" "$TOOLS/wg-quick/linux.bash" 10 | -------------------------------------------------------------------------------- /contrib/dns-hatchet/hatchet.bash: -------------------------------------------------------------------------------- 1 | set_dns() { 2 | [[ ${#DNS[@]} -gt 0 ]] || return 0 3 | 4 | if [[ $(resolvconf --version 2>/dev/null) == openresolv\ * ]]; then 5 | { printf 'nameserver %s\n' "${DNS[@]}" 6 | [[ ${#DNS_SEARCH[@]} -eq 0 ]] || printf 'search %s\n' "${DNS_SEARCH[*]}" 7 | } | cmd resolvconf -a "$INTERFACE" -m 0 -x 8 | else 9 | echo "[#] mount \`${DNS[*]}' /etc/resolv.conf" >&2 10 | [[ -e /etc/resolv.conf ]] || touch /etc/resolv.conf 11 | { cat <<-_EOF 12 | # This file was generated by wg-quick(8) for use with 13 | # the WireGuard interface $INTERFACE. It cannot be 14 | # removed or altered directly. You may remove this file 15 | # by running \`wg-quick down $INTERFACE', or if that 16 | # poses problems, run \`umount /etc/resolv.conf'. 17 | 18 | _EOF 19 | printf 'nameserver %s\n' "${DNS[@]}" 20 | [[ ${#DNS_SEARCH[@]} -eq 0 ]] || printf 'search %s\n' "${DNS_SEARCH[*]}" 21 | } | unshare -m --propagation shared bash -c "$(cat <<-_EOF 22 | set -e 23 | context="\$(stat -c %C /etc/resolv.conf 2>/dev/null)" || unset context 24 | mount --make-private /dev/shm 25 | mount -t tmpfs none /dev/shm 26 | cat > /dev/shm/resolv.conf 27 | [[ -z \$context || \$context == "?" ]] || chcon "\$context" /dev/shm/resolv.conf 2>/dev/null || true 28 | mount -o remount,ro /dev/shm 29 | mount -o bind,ro /dev/shm/resolv.conf /etc/resolv.conf 30 | _EOF 31 | )" 32 | fi 33 | HAVE_SET_DNS=1 34 | } 35 | 36 | unset_dns() { 37 | [[ ${#DNS[@]} -gt 0 ]] || return 0 38 | 39 | if [[ $(resolvconf --version 2>/dev/null) == openresolv\ * ]]; then 40 | cmd resolvconf -d "$INTERFACE" 41 | else 42 | cmd umount /etc/resolv.conf 43 | fi 44 | } 45 | -------------------------------------------------------------------------------- /contrib/embeddable-wg-library/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS += -Wall 2 | 3 | test: test.c wireguard.c wireguard.h 4 | 5 | clean: 6 | rm -f test 7 | .PHONY: clean 8 | -------------------------------------------------------------------------------- /contrib/embeddable-wg-library/README: -------------------------------------------------------------------------------- 1 | Embeddable WireGuard C Library 2 | ============================== 3 | 4 | This is a mini single-file library, meant to be embedded directly into the 5 | source code of your program. It is *not* meant to be built as a shared 6 | library. 7 | 8 | 9 | Usage 10 | ----- 11 | 12 | Copy wireguard.c and wireguard.h into your project. They should build with 13 | any C89 compiler. There are no dependencies except libc. 14 | 15 | Please see the set of simple functions in wireguard.h for information on 16 | how to use, as well as the example code in test.c. 17 | 18 | 19 | License 20 | ------- 21 | 22 | Because this uses code from libmnl, wireguard.c and wireguard.h are licensed 23 | under the LGPL-2.1+. 24 | -------------------------------------------------------------------------------- /contrib/embeddable-wg-library/test.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.1+ 2 | /* 3 | * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | #include "wireguard.h" 7 | #include 8 | #include 9 | #include 10 | 11 | void list_devices(void) 12 | { 13 | char *device_names, *device_name; 14 | size_t len; 15 | 16 | device_names = wg_list_device_names(); 17 | if (!device_names) { 18 | perror("Unable to get device names"); 19 | exit(1); 20 | } 21 | wg_for_each_device_name(device_names, device_name, len) { 22 | wg_device *device; 23 | wg_peer *peer; 24 | wg_key_b64_string key; 25 | 26 | if (wg_get_device(&device, device_name) < 0) { 27 | perror("Unable to get device"); 28 | continue; 29 | } 30 | if (device->flags & WGDEVICE_HAS_PUBLIC_KEY) { 31 | wg_key_to_base64(key, device->public_key); 32 | printf("%s has public key %s\n", device_name, key); 33 | } else 34 | printf("%s has no public key\n", device_name); 35 | wg_for_each_peer(device, peer) { 36 | wg_key_to_base64(key, peer->public_key); 37 | printf(" - peer %s\n", key); 38 | } 39 | wg_free_device(device); 40 | } 41 | free(device_names); 42 | } 43 | 44 | int main(int argc, char *argv[]) 45 | { 46 | wg_peer new_peer = { 47 | .flags = WGPEER_HAS_PUBLIC_KEY | WGPEER_REPLACE_ALLOWEDIPS 48 | }; 49 | wg_device new_device = { 50 | .name = "wgtest0", 51 | .listen_port = 1234, 52 | .flags = WGDEVICE_HAS_PRIVATE_KEY | WGDEVICE_HAS_LISTEN_PORT, 53 | .first_peer = &new_peer, 54 | .last_peer = &new_peer 55 | }; 56 | wg_key temp_private_key; 57 | 58 | wg_generate_private_key(temp_private_key); 59 | wg_generate_public_key(new_peer.public_key, temp_private_key); 60 | wg_generate_private_key(new_device.private_key); 61 | 62 | if (wg_add_device(new_device.name) < 0) { 63 | perror("Unable to add device"); 64 | exit(1); 65 | } 66 | 67 | if (wg_set_device(&new_device) < 0) { 68 | perror("Unable to set device"); 69 | exit(1); 70 | } 71 | 72 | list_devices(); 73 | 74 | if (wg_del_device(new_device.name) < 0) { 75 | perror("Unable to delete device"); 76 | exit(1); 77 | } 78 | 79 | return 0; 80 | } 81 | -------------------------------------------------------------------------------- /contrib/embeddable-wg-library/wireguard.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: LGPL-2.1+ */ 2 | /* 3 | * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | #ifndef WIREGUARD_H 7 | #define WIREGUARD_H 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | typedef uint8_t wg_key[32]; 17 | typedef char wg_key_b64_string[((sizeof(wg_key) + 2) / 3) * 4 + 1]; 18 | 19 | /* Cross platform __kernel_timespec */ 20 | struct timespec64 { 21 | int64_t tv_sec; 22 | int64_t tv_nsec; 23 | }; 24 | 25 | typedef struct wg_allowedip { 26 | uint16_t family; 27 | union { 28 | struct in_addr ip4; 29 | struct in6_addr ip6; 30 | }; 31 | uint8_t cidr; 32 | struct wg_allowedip *next_allowedip; 33 | } wg_allowedip; 34 | 35 | enum wg_peer_flags { 36 | WGPEER_REMOVE_ME = 1U << 0, 37 | WGPEER_REPLACE_ALLOWEDIPS = 1U << 1, 38 | WGPEER_HAS_PUBLIC_KEY = 1U << 2, 39 | WGPEER_HAS_PRESHARED_KEY = 1U << 3, 40 | WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL = 1U << 4 41 | }; 42 | 43 | typedef union wg_endpoint { 44 | struct sockaddr addr; 45 | struct sockaddr_in addr4; 46 | struct sockaddr_in6 addr6; 47 | } wg_endpoint; 48 | 49 | typedef struct wg_peer { 50 | enum wg_peer_flags flags; 51 | 52 | wg_key public_key; 53 | wg_key preshared_key; 54 | 55 | wg_endpoint endpoint; 56 | 57 | struct timespec64 last_handshake_time; 58 | uint64_t rx_bytes, tx_bytes; 59 | uint16_t persistent_keepalive_interval; 60 | 61 | struct wg_allowedip *first_allowedip, *last_allowedip; 62 | struct wg_peer *next_peer; 63 | } wg_peer; 64 | 65 | enum wg_device_flags { 66 | WGDEVICE_REPLACE_PEERS = 1U << 0, 67 | WGDEVICE_HAS_PRIVATE_KEY = 1U << 1, 68 | WGDEVICE_HAS_PUBLIC_KEY = 1U << 2, 69 | WGDEVICE_HAS_LISTEN_PORT = 1U << 3, 70 | WGDEVICE_HAS_FWMARK = 1U << 4 71 | }; 72 | 73 | typedef struct wg_device { 74 | char name[IFNAMSIZ]; 75 | uint32_t ifindex; 76 | 77 | enum wg_device_flags flags; 78 | 79 | wg_key public_key; 80 | wg_key private_key; 81 | 82 | uint32_t fwmark; 83 | uint16_t listen_port; 84 | 85 | struct wg_peer *first_peer, *last_peer; 86 | } wg_device; 87 | 88 | #define wg_for_each_device_name(__names, __name, __len) for ((__name) = (__names), (__len) = 0; ((__len) = strlen(__name)); (__name) += (__len) + 1) 89 | #define wg_for_each_peer(__dev, __peer) for ((__peer) = (__dev)->first_peer; (__peer); (__peer) = (__peer)->next_peer) 90 | #define wg_for_each_allowedip(__peer, __allowedip) for ((__allowedip) = (__peer)->first_allowedip; (__allowedip); (__allowedip) = (__allowedip)->next_allowedip) 91 | 92 | int wg_set_device(wg_device *dev); 93 | int wg_get_device(wg_device **dev, const char *device_name); 94 | int wg_add_device(const char *device_name); 95 | int wg_del_device(const char *device_name); 96 | void wg_free_device(wg_device *dev); 97 | char *wg_list_device_names(void); /* first\0second\0third\0forth\0last\0\0 */ 98 | void wg_key_to_base64(wg_key_b64_string base64, const wg_key key); 99 | int wg_key_from_base64(wg_key key, const wg_key_b64_string base64); 100 | bool wg_key_is_zero(const wg_key key); 101 | void wg_generate_public_key(wg_key public_key, const wg_key private_key); 102 | void wg_generate_private_key(wg_key private_key); 103 | void wg_generate_preshared_key(wg_key preshared_key); 104 | 105 | #endif 106 | -------------------------------------------------------------------------------- /contrib/external-tests/haskell/Setup.hs: -------------------------------------------------------------------------------- 1 | import Distribution.Simple 2 | main = defaultMain 3 | -------------------------------------------------------------------------------- /contrib/external-tests/haskell/package.yaml: -------------------------------------------------------------------------------- 1 | name: cacophony-wg 2 | version: 0.1.0 3 | license: PublicDomain 4 | maintainer: John Galt 5 | category: Cryptography 6 | ghc-options: -Wall 7 | 8 | executables: 9 | cacophony-wg: 10 | main: Main.hs 11 | source-dirs: src 12 | 13 | dependencies: 14 | - base 15 | - base16-bytestring 16 | - base64-bytestring 17 | - blake2 18 | - bytestring 19 | - cacophony >= 0.10 20 | - cereal 21 | - cryptonite 22 | - memory 23 | - network 24 | - time 25 | 26 | ghc-options: 27 | - -O2 28 | - -rtsopts 29 | - -threaded 30 | - -with-rtsopts=-N 31 | 32 | other-modules: 33 | - Data.Time.TAI64 34 | 35 | default-extensions: 36 | - OverloadedStrings 37 | -------------------------------------------------------------------------------- /contrib/external-tests/haskell/src/Data/Time/TAI64.hs: -------------------------------------------------------------------------------- 1 | module Data.Time.TAI64 ( 2 | TAI64(..) 3 | , TAI64N(..) 4 | , TAI64NA(..) 5 | , posixToTAI64 6 | , posixToTAI64N 7 | , posixToTAI64NA 8 | , getCurrentTAI64 9 | , getCurrentTAI64N 10 | , getCurrentTAI64NA 11 | , tAI64ToPosix 12 | , tAI64NToPosix 13 | , tAI64NAToPosix 14 | ) where 15 | 16 | import Data.Serialize 17 | import Control.Monad 18 | import Data.Word 19 | 20 | import Data.Time.Clock 21 | import Data.Time.Clock.POSIX 22 | 23 | import Numeric 24 | 25 | data TAI64 = TAI64 26 | {-# UNPACK #-} !Word64 27 | deriving (Eq, Ord) 28 | 29 | data TAI64N = TAI64N 30 | {-# UNPACK #-} !TAI64 31 | {-# UNPACK #-} !Word32 32 | deriving (Eq, Ord, Show) 33 | 34 | data TAI64NA = TAI64NA 35 | {-# UNPACK #-} !TAI64N 36 | {-# UNPACK #-} !Word32 37 | deriving (Eq, Ord, Show) 38 | 39 | instance Show TAI64 where 40 | show (TAI64 t) = "TAI64 0x" ++ showHex t "" 41 | 42 | instance Serialize TAI64 where 43 | put (TAI64 t) = putWord64be t 44 | get = liftM TAI64 get 45 | 46 | instance Serialize TAI64N where 47 | put (TAI64N t' nt) = put t' >> putWord32be nt 48 | get = liftM2 TAI64N get get 49 | 50 | instance Serialize TAI64NA where 51 | put (TAI64NA t' at) = put t' >> putWord32be at 52 | get = liftM2 TAI64NA get get 53 | 54 | 55 | posixToTAI64 :: POSIXTime -> TAI64 56 | posixToTAI64 = TAI64 . (2^62 +) . truncate . realToFrac 57 | 58 | posixToTAI64N :: POSIXTime -> TAI64N 59 | posixToTAI64N pt = TAI64N t' ns where 60 | t' = posixToTAI64 pt 61 | ns = (`mod` 10^9) $ truncate (pts * 10**9) 62 | pts = realToFrac pt 63 | 64 | posixToTAI64NA :: POSIXTime -> TAI64NA -- | PICOsecond precision 65 | posixToTAI64NA pt = TAI64NA t' as where 66 | t' = posixToTAI64N pt 67 | as = (`mod` 10^9) $ truncate (pts * 10**18) 68 | pts = realToFrac pt 69 | 70 | getCurrentTAI64 :: IO TAI64 71 | getCurrentTAI64N :: IO TAI64N 72 | getCurrentTAI64NA :: IO TAI64NA 73 | getCurrentTAI64 = liftM posixToTAI64 getPOSIXTime 74 | getCurrentTAI64N = liftM posixToTAI64N getPOSIXTime 75 | getCurrentTAI64NA = liftM posixToTAI64NA getPOSIXTime 76 | 77 | tAI64ToPosix :: TAI64 -> POSIXTime 78 | tAI64ToPosix (TAI64 s) = fromRational . fromIntegral $ s - 2^62 79 | 80 | tAI64NToPosix :: TAI64N -> POSIXTime 81 | tAI64NToPosix (TAI64N t' n) = tAI64ToPosix t' + nanopart where 82 | nanopart = fromRational $ (toRational $ 10**(-9)) * toRational n -- TODO: optimize? 83 | 84 | tAI64NAToPosix :: TAI64NA -> POSIXTime 85 | tAI64NAToPosix (TAI64NA t' a) = tAI64NToPosix t' + attopart where 86 | attopart = fromRational $ (toRational $ 10**(-18)) * toRational a 87 | -------------------------------------------------------------------------------- /contrib/external-tests/haskell/src/Main.hs: -------------------------------------------------------------------------------- 1 | module Main where 2 | 3 | import Control.Monad (void) 4 | import Crypto.Hash.BLAKE2.BLAKE2s (hash) 5 | import Data.ByteArray (ScrubbedBytes, convert) 6 | import Data.ByteString (ByteString, replicate, take, drop) 7 | import qualified Data.ByteString.Base16 as B16 8 | import qualified Data.ByteString.Base64 as B64 9 | import Data.Maybe (fromMaybe) 10 | import Data.Monoid ((<>)) 11 | import qualified Data.Serialize as S 12 | import Network.Socket 13 | import qualified Network.Socket.ByteString as NBS 14 | import Prelude hiding (replicate, take, drop) 15 | 16 | import Crypto.Noise 17 | import Crypto.Noise.Cipher 18 | import Crypto.Noise.Cipher.ChaChaPoly1305 19 | import Crypto.Noise.DH 20 | import Crypto.Noise.DH.Curve25519 21 | import Crypto.Noise.HandshakePatterns (noiseIKpsk2) 22 | import Crypto.Noise.Hash hiding (hash) 23 | import Crypto.Noise.Hash.BLAKE2s 24 | 25 | import Data.Time.TAI64 26 | 27 | sampleICMPRequest :: ByteString 28 | sampleICMPRequest = fst . B16.decode $ 29 | "450000250000000014018f5b0abd81020abd810108001bfa039901b6576972654775617264" 30 | 31 | validateICMPResponse :: ByteString 32 | -> Bool 33 | validateICMPResponse r = 34 | -- Strip off part of IPv4 header because this is only a demo. 35 | drop 12 sample == drop 12 r 36 | where 37 | sample = fst . B16.decode $ "45000025e3030000400180570abd81010abd8102000023fa039901b65769726547756172640000000000000000000000" 38 | 39 | unsafeMessage :: (Cipher c, DH d, Hash h) 40 | => Bool 41 | -> Maybe ScrubbedBytes 42 | -> ScrubbedBytes 43 | -> NoiseState c d h 44 | -> (ScrubbedBytes, NoiseState c d h) 45 | unsafeMessage write mpsk msg ns = case operation msg ns of 46 | NoiseResultMessage ct ns' -> (ct, ns') 47 | 48 | NoiseResultNeedPSK ns' -> case mpsk of 49 | Nothing -> error "psk required but not provided" 50 | Just k -> case operation k ns' of 51 | NoiseResultMessage ct ns'' -> (ct, ns'') 52 | _ -> error "something terrible happened" 53 | 54 | _ -> error "something terrible happened" 55 | where 56 | operation = if write then writeMessage else readMessage 57 | 58 | main :: IO () 59 | main = do 60 | let ip = "demo.wireguard.com" 61 | port = "12913" 62 | myKeyB64 = "WAmgVYXkbT2bCtdcDwolI88/iVi/aV3/PHcUBTQSYmo=" -- private key 63 | serverKeyB64 = "qRCwZSKInrMAq5sepfCdaCsRJaoLe5jhtzfiw7CjbwM=" -- public key 64 | pskB64 = "FpCyhws9cxwWoV4xELtfJvjJN+zQVRPISllRWgeopVE=" 65 | 66 | addrInfo <- head <$> getAddrInfo Nothing (Just ip) (Just port) 67 | sock <- socket (addrFamily addrInfo) Datagram defaultProtocol 68 | 69 | let addr = addrAddress addrInfo 70 | myStaticKey = fromMaybe (error "invalid private key") 71 | . dhBytesToPair 72 | . convert 73 | . either (error "error Base64 decoding my private key") id 74 | . B64.decode 75 | $ myKeyB64 :: KeyPair Curve25519 76 | 77 | serverKey = fromMaybe (error "invalid public key") 78 | . dhBytesToPub 79 | . convert 80 | . either (error "error Base64 decoding server public key") id 81 | . B64.decode 82 | $ serverKeyB64 :: PublicKey Curve25519 83 | 84 | psk = convert 85 | . either (error "error decoding PSK") id 86 | . B64.decode 87 | $ pskB64 :: ScrubbedBytes 88 | 89 | myEphemeralKey <- dhGenKey 90 | 91 | let dho = defaultHandshakeOpts InitiatorRole "WireGuard v1 zx2c4 Jason@zx2c4.com" 92 | opts = setLocalEphemeral (Just myEphemeralKey) 93 | . setLocalStatic (Just myStaticKey) 94 | . setRemoteStatic (Just serverKey) 95 | $ dho 96 | ns0 = noiseState opts noiseIKpsk2 :: NoiseState ChaChaPoly1305 Curve25519 BLAKE2s 97 | 98 | tai64n <- convert . S.encode <$> getCurrentTAI64N 99 | 100 | -- Handshake: Initiator to responder ----------------------------------------- 101 | 102 | let (msg0, ns1) = unsafeMessage True Nothing tai64n ns0 103 | macKey = hash 32 mempty $ "mac1----" `mappend` (convert . dhPubToBytes) serverKey 104 | initiation = "\x01\x00\x00\x00\x1c\x00\x00\x00" <> convert msg0 -- sender index = 28 to match other examples 105 | mac1 = hash 16 macKey initiation 106 | 107 | void $ NBS.sendTo sock (initiation <> mac1 <> replicate 16 0) addr 108 | 109 | -- Handshake: Responder to initiator ----------------------------------------- 110 | 111 | (response0, _) <- NBS.recvFrom sock 1024 112 | 113 | let theirIndex = take 4 . drop 4 $ response0 114 | (_, ns2) = unsafeMessage False (Just psk) (convert . take 48 . drop 12 $ response0) ns1 115 | 116 | -- ICMP: Initiator to responder ---------------------------------------------- 117 | 118 | let (msg1, ns3) = unsafeMessage True Nothing (convert sampleICMPRequest) ns2 119 | icmp = "\x04\x00\x00\x00" <> theirIndex <> replicate 8 0 <> convert msg1 120 | 121 | void $ NBS.sendTo sock icmp addr 122 | 123 | -- ICMP: Responder to initiator ---------------------------------------------- 124 | 125 | (response1, _) <- NBS.recvFrom sock 1024 126 | 127 | let (icmpPayload, ns4) = unsafeMessage False Nothing (convert . drop 16 $ response1) ns3 128 | 129 | -- KeepAlive: Initiator to responder ----------------------------------------- 130 | 131 | if validateICMPResponse . convert $ icmpPayload 132 | then do 133 | let (msg2, _) = unsafeMessage True Nothing mempty ns4 134 | keepAlive = "\x04\x00\x00\x00" <> theirIndex <> "\x01" <> replicate 7 0 <> convert msg2 135 | 136 | void $ NBS.sendTo sock keepAlive addr 137 | 138 | else error "unexpected ICMP response from server!" 139 | -------------------------------------------------------------------------------- /contrib/external-tests/haskell/stack.yaml: -------------------------------------------------------------------------------- 1 | resolver: lts-8.18 2 | packages: 3 | - '.' 4 | extra-deps: [] 5 | flags: {} 6 | extra-package-dbs: [] 7 | -------------------------------------------------------------------------------- /contrib/external-tests/python/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | # SPDX-License-Identifier: MIT 4 | # Author: Piotr Lizonczyk 5 | 6 | import base64 7 | import datetime 8 | from hashlib import blake2s 9 | import socket 10 | import struct 11 | 12 | from scapy.layers.inet import IP, ICMP 13 | 14 | from noise.connection import NoiseConnection, Keypair 15 | 16 | 17 | address = ('demo.wireguard.com', 12913) 18 | 19 | our_private = base64.b64decode('WAmgVYXkbT2bCtdcDwolI88/iVi/aV3/PHcUBTQSYmo=') 20 | their_public = base64.b64decode('qRCwZSKInrMAq5sepfCdaCsRJaoLe5jhtzfiw7CjbwM=') 21 | preshared = base64.b64decode('FpCyhws9cxwWoV4xELtfJvjJN+zQVRPISllRWgeopVE=') 22 | prologue = b'WireGuard v1 zx2c4 Jason@zx2c4.com' 23 | 24 | noise = NoiseConnection.from_name(b'Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s') 25 | noise.set_as_initiator() 26 | noise.set_keypair_from_private_bytes(Keypair.STATIC, our_private) 27 | noise.set_keypair_from_public_bytes(Keypair.REMOTE_STATIC, their_public) 28 | noise.set_psks(psk=preshared) 29 | noise.set_prologue(prologue) 30 | noise.start_handshake() 31 | 32 | sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 33 | 34 | 35 | # 1. Prepare and send handshake initiation packet 36 | now = datetime.datetime.now() 37 | tai = struct.pack('!qi', 4611686018427387914 + int(now.timestamp()), int(now.microsecond * 1e3)) 38 | initiation_packet = b'\x01' # Type: initiation 39 | initiation_packet += b'\x00' * 3 # Reserved 40 | initiation_packet += struct.pack('. All Rights Reserved. */ 2 | 3 | extern crate snow; 4 | extern crate base64; 5 | extern crate time; 6 | extern crate byteorder; 7 | extern crate crypto; 8 | extern crate pnet; 9 | 10 | use byteorder::{ByteOrder, BigEndian, LittleEndian}; 11 | use crypto::blake2s::Blake2s; 12 | use snow::NoiseBuilder; 13 | use pnet::packet::Packet; 14 | use pnet::packet::ip::IpNextHeaderProtocols; 15 | use pnet::packet::ipv4::{MutableIpv4Packet, self}; 16 | use pnet::packet::icmp::{MutableIcmpPacket, IcmpTypes, echo_reply, echo_request, self}; 17 | use std::net::*; 18 | use std::str::FromStr; 19 | 20 | static TEST_SERVER: &'static str = "demo.wireguard.com:12913"; 21 | 22 | fn memcpy(out: &mut [u8], data: &[u8]) { 23 | out[..data.len()].copy_from_slice(data); 24 | } 25 | 26 | fn main() { 27 | let socket = UdpSocket::bind("0.0.0.0:0").unwrap(); 28 | 29 | let their_public = base64::decode(&"qRCwZSKInrMAq5sepfCdaCsRJaoLe5jhtzfiw7CjbwM=").unwrap(); 30 | let my_private = base64::decode(&"WAmgVYXkbT2bCtdcDwolI88/iVi/aV3/PHcUBTQSYmo=").unwrap(); 31 | let my_preshared = base64::decode(&"FpCyhws9cxwWoV4xELtfJvjJN+zQVRPISllRWgeopVE=").unwrap(); 32 | 33 | let mut noise = NoiseBuilder::new("Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s".parse().unwrap()) 34 | .local_private_key(&my_private[..]) 35 | .remote_public_key(&their_public[..]) 36 | .prologue("WireGuard v1 zx2c4 Jason@zx2c4.com".as_bytes()) 37 | .psk(2, &my_preshared[..]) 38 | .build_initiator().unwrap(); 39 | 40 | let now = time::get_time(); 41 | let mut tai64n = [0; 12]; 42 | BigEndian::write_i64(&mut tai64n[0..], 4611686018427387914 + now.sec); 43 | BigEndian::write_i32(&mut tai64n[8..], now.nsec); 44 | let mut initiation_packet = [0; 148]; 45 | initiation_packet[0] = 1; /* Type: Initiation */ 46 | initiation_packet[1] = 0; /* Reserved */ 47 | initiation_packet[2] = 0; /* Reserved */ 48 | initiation_packet[3] = 0; /* Reserved */ 49 | LittleEndian::write_u32(&mut initiation_packet[4..], 28); /* Sender index: 28 (arbitrary) */ 50 | noise.write_message(&tai64n, &mut initiation_packet[8..]).unwrap(); 51 | let mut mac_key_input = [0; 40]; 52 | let mut mac_key = [0; 32]; 53 | memcpy(&mut mac_key_input, b"mac1----"); 54 | memcpy(&mut mac_key_input[8..], &their_public); 55 | Blake2s::blake2s(&mut mac_key, &mac_key_input, &[0; 0]); 56 | let mut mac = [0; 16]; 57 | Blake2s::blake2s(&mut mac, &initiation_packet[0..116], &mac_key); 58 | memcpy(&mut initiation_packet[116..], &mac); 59 | socket.send_to(&initiation_packet, TEST_SERVER).unwrap(); 60 | 61 | let mut response_packet = [0; 92]; 62 | socket.recv_from(&mut response_packet).unwrap(); 63 | assert!(response_packet[0] == 2 /* Type: Response */); 64 | assert!(response_packet[1] == 0 /* Reserved */); 65 | assert!(response_packet[2] == 0 /* Reserved */); 66 | assert!(response_packet[3] == 0 /* Reserved */); 67 | let their_index = LittleEndian::read_u32(&response_packet[4..]); 68 | let our_index = LittleEndian::read_u32(&response_packet[8..]); 69 | assert!(our_index == 28); 70 | let payload_len = noise.read_message(&response_packet[12..60], &mut []).unwrap(); 71 | assert!(payload_len == 0); 72 | noise = noise.into_transport_mode().unwrap(); 73 | 74 | let mut icmp_packet = [0; 48]; 75 | { 76 | let mut ipv4 = MutableIpv4Packet::new(&mut icmp_packet).unwrap(); 77 | ipv4.set_version(4); 78 | ipv4.set_header_length(5); 79 | ipv4.set_total_length(37); 80 | ipv4.set_ttl(20); 81 | ipv4.set_next_level_protocol(IpNextHeaderProtocols::Icmp); 82 | ipv4.set_source(Ipv4Addr::from_str("10.189.129.2").unwrap()); 83 | ipv4.set_destination(Ipv4Addr::from_str("10.189.129.1").unwrap()); 84 | let checksum = ipv4::checksum(&ipv4.to_immutable()); 85 | ipv4.set_checksum(checksum); 86 | } 87 | { 88 | let mut icmp = echo_request::MutableEchoRequestPacket::new(&mut icmp_packet[20..]).unwrap(); 89 | icmp.set_icmp_type(IcmpTypes::EchoRequest); 90 | icmp.set_icmp_code(echo_request::IcmpCodes::NoCode); 91 | icmp.set_identifier(921); 92 | icmp.set_sequence_number(438); 93 | icmp.set_payload(b"WireGuard"); 94 | } 95 | { 96 | let mut icmp = MutableIcmpPacket::new(&mut icmp_packet[20..]).unwrap(); 97 | let checksum = icmp::checksum(&icmp.to_immutable()); 98 | icmp.set_checksum(checksum); 99 | } 100 | 101 | let mut ping_packet = [0; 80]; 102 | ping_packet[0] = 4; /* Type: Data */ 103 | ping_packet[1] = 0; /* Reserved */ 104 | ping_packet[2] = 0; /* Reserved */ 105 | ping_packet[3] = 0; /* Reserved */ 106 | LittleEndian::write_u32(&mut ping_packet[4..], their_index); 107 | LittleEndian::write_u64(&mut ping_packet[8..], 0); 108 | noise.write_message(&icmp_packet, &mut ping_packet[16..]).unwrap(); 109 | socket.send_to(&ping_packet, TEST_SERVER).unwrap(); 110 | 111 | socket.recv_from(&mut ping_packet).unwrap(); 112 | assert!(ping_packet[0] == 4 /* Type: Data */); 113 | assert!(ping_packet[1] == 0 /* Reserved */); 114 | assert!(ping_packet[2] == 0 /* Reserved */); 115 | assert!(ping_packet[3] == 0 /* Reserved */); 116 | let our_index_received = LittleEndian::read_u32(&ping_packet[4..]); 117 | assert!(our_index_received == 28); 118 | let nonce = LittleEndian::read_u64(&ping_packet[8..]); 119 | assert!(nonce == 0); 120 | let payload_len = noise.read_message(&ping_packet[16..], &mut icmp_packet).unwrap(); 121 | assert!(payload_len == 48); 122 | let icmp_reply = echo_reply::EchoReplyPacket::new(&icmp_packet[20..37]).unwrap(); 123 | assert!(icmp_reply.get_icmp_type() == IcmpTypes::EchoReply && icmp_reply.get_icmp_code() == echo_reply::IcmpCodes::NoCode); 124 | assert!(icmp_reply.get_identifier() == 921 && icmp_reply.get_sequence_number() == 438); 125 | assert!(icmp_reply.payload() == b"WireGuard"); 126 | 127 | let mut keepalive_packet = [0; 32]; 128 | keepalive_packet[0] = 4; /* Type: Data */ 129 | keepalive_packet[1] = 0; /* Reserved */ 130 | keepalive_packet[2] = 0; /* Reserved */ 131 | keepalive_packet[3] = 0; /* Reserved */ 132 | LittleEndian::write_u32(&mut keepalive_packet[4..], their_index); 133 | LittleEndian::write_u64(&mut keepalive_packet[8..], 1); 134 | let empty_payload = [0; 0]; /* Empty payload means keepalive */ 135 | noise.write_message(&empty_payload, &mut keepalive_packet[16..]).unwrap(); 136 | socket.send_to(&keepalive_packet, TEST_SERVER).unwrap(); 137 | } 138 | -------------------------------------------------------------------------------- /contrib/extract-handshakes/Makefile: -------------------------------------------------------------------------------- 1 | ifeq ($(KERNELRELEASE),) 2 | KERNELDIR ?= /lib/modules/$(shell uname -r)/build 3 | PWD := $(shell pwd) 4 | CFLAGS ?= -O3 -march=native 5 | CFLAGS += -Wall -pedantic -std=gnu11 6 | 7 | offsets.include: offset-finder 8 | ./$^ > $@ 9 | 10 | offset-finder: offset-finder.c offset-finder.o 11 | $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $^ 12 | 13 | offset-finder.o: offset-finder.c 14 | $(MAKE) -C $(KERNELDIR) M=$(PWD) $@ 15 | objcopy -j '.rodata*' $@ $@ 16 | 17 | clean: 18 | rm -f offset-finder offsets.include 19 | $(MAKE) -C $(KERNELDIR) M=$(PWD) clean 20 | 21 | .PHONY: clean 22 | else 23 | obj-m := offset-finder.o 24 | endif 25 | -------------------------------------------------------------------------------- /contrib/extract-handshakes/README: -------------------------------------------------------------------------------- 1 | Handshake Extractor 2 | =================== 3 | 4 | This will extract private keys from outgoing handshake sessions, prior 5 | to them being sent, via kprobes. It exports the bare minimum to be 6 | able to then decrypt all packets in the handshake and in the subsequent 7 | transport data session. 8 | 9 | Build: 10 | 11 | $ make 12 | 13 | Run (as root): 14 | 15 | # ./extract-handshakes.sh 16 | New handshake session: 17 | LOCAL_STATIC_PRIVATE_KEY = QChaGDXeH3eQsbFAhueUNWFdq9KfpF3yl+eITjZbXEk= 18 | REMOTE_STATIC_PUBLIC_KEY = HzgTY6aWXtuSyW/PUquZtg8LB/DyMwEXGkPiEmdSsUU= 19 | LOCAL_EPHEMERAL_PRIVATE_KEY = UNGdRHuKDeqbFvmiV5FD4wP7a8PqI6v3Xnnz6Jc6NXQ= 20 | PRESHARED_KEY = AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= 21 | -------------------------------------------------------------------------------- /contrib/extract-handshakes/extract-handshakes.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # SPDX-License-Identifier: GPL-2.0 3 | # 4 | # Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. 5 | # Copyright (C) 2017-2018 Peter Wu . All Rights Reserved. 6 | 7 | set -e 8 | 9 | ME_DIR="${BASH_SOURCE[0]}" 10 | ME_DIR="${ME_DIR%/*}" 11 | source "$ME_DIR/offsets.include" || { echo "Did you forget to run make?" >&2; exit 1; } 12 | 13 | case "$(uname -m)" in 14 | x86_64) ARGUMENT_REGISTER="%si" ;; 15 | i386|i686) ARGUMENT_REGISTER="%dx" ;; 16 | aarch64) ARGUMENT_REGISTER="%x1" ;; 17 | arm) ARGUMENT_REGISTER="%r1" ;; 18 | *) echo "ERROR: Unknown architecture" >&2; exit 1 ;; 19 | esac 20 | 21 | ARGS=( ) 22 | REGEX=".*: idxadd: .*" 23 | for key in "${!OFFSETS[@]}"; do 24 | values="${OFFSETS[$key]}" 25 | values=( ${values//,/ } ) 26 | for i in {0..3}; do 27 | value="$ARGUMENT_REGISTER" 28 | for indirection in "${values[@]:1}"; do 29 | value="+$indirection($value)" 30 | done 31 | value="+$((i * 8 + values[0]))($value)" 32 | ARGS+=( "${key,,}$i=$value:x64" ) 33 | REGEX="$REGEX ${key,,}$i=0x([0-9a-f]+)" 34 | done 35 | done 36 | 37 | turn_off() { 38 | set +e 39 | [[ -f /sys/kernel/debug/tracing/events/wireguard/idxadd/enable ]] || exit 40 | echo 0 > /sys/kernel/debug/tracing/events/wireguard/idxadd/enable 41 | echo "-:wireguard/idxadd" >> /sys/kernel/debug/tracing/kprobe_events 42 | exit 43 | } 44 | 45 | trap turn_off INT TERM EXIT 46 | echo "p:wireguard/idxadd index_hashtable_insert ${ARGS[*]}" >> /sys/kernel/debug/tracing/kprobe_events 47 | echo 1 > /sys/kernel/debug/tracing/events/wireguard/idxadd/enable 48 | 49 | unpack_u64() { 50 | local i expanded="$1" 51 | if [[ $ENDIAN == big ]]; then 52 | printf -v expanded "%.*s$expanded" $((16 - ${#expanded})) 0000000000000000 53 | for i in {0..7}; do 54 | echo -n "\\x${expanded:(i * 2):2}" 55 | done 56 | elif [[ $ENDIAN == little ]]; then 57 | (( ${#expanded} % 2 == 1 )) && expanded="0$expanded" 58 | expanded="${expanded}0000000000000000" 59 | for i in {0..7}; do 60 | echo -n "\\x${expanded:((7 - i) * 2):2}" 61 | done 62 | else 63 | echo "ERROR: Unable to determine endian" >&2 64 | exit 1 65 | fi 66 | } 67 | 68 | while read -r line; do 69 | [[ $line =~ $REGEX ]] || continue 70 | echo "New handshake session:" 71 | j=1 72 | for key in "${!OFFSETS[@]}"; do 73 | bytes="" 74 | for i in {0..3}; do 75 | bytes="$bytes$(unpack_u64 "${BASH_REMATCH[j]}")" 76 | ((++j)) 77 | done 78 | echo " $key = $(printf "$bytes" | base64)" 79 | done 80 | done < /sys/kernel/debug/tracing/trace_pipe 81 | -------------------------------------------------------------------------------- /contrib/extract-handshakes/offset-finder.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | /* 3 | * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | struct def { 7 | const char *name; 8 | unsigned long offset; 9 | unsigned long indirection_offset; 10 | }; 11 | extern const struct def defs[]; 12 | 13 | #ifdef __KERNEL__ 14 | #include "../drivers/net/wireguard/noise.h" 15 | 16 | const struct def defs[] = { 17 | { "LOCAL_STATIC_PRIVATE_KEY", offsetof(struct noise_static_identity, static_private), offsetof(struct noise_handshake, static_identity) }, 18 | { "LOCAL_EPHEMERAL_PRIVATE_KEY", offsetof(struct noise_handshake, ephemeral_private), -1 }, 19 | { "REMOTE_STATIC_PUBLIC_KEY", offsetof(struct noise_handshake, remote_static), -1 }, 20 | { "PRESHARED_KEY", offsetof(struct noise_handshake, preshared_key), -1 }, 21 | { NULL, 0 } 22 | }; 23 | #else 24 | #include 25 | int main(int argc, char *argv[]) 26 | { 27 | puts("declare -A OFFSETS=("); 28 | for (const struct def *def = defs; def->name; ++def) { 29 | printf("\t[%s]=%ld", def->name, def->offset); 30 | if (def->indirection_offset != -1) 31 | printf(",%ld", def->indirection_offset); 32 | putchar('\n'); 33 | } 34 | puts(")"); 35 | #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ 36 | puts("ENDIAN=big"); 37 | #elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 38 | puts("ENDIAN=little"); 39 | #else 40 | #error "Unsupported endianness" 41 | #endif 42 | return 0; 43 | } 44 | #endif 45 | -------------------------------------------------------------------------------- /contrib/extract-keys/Makefile: -------------------------------------------------------------------------------- 1 | ifeq ($(KERNELRELEASE),) 2 | KERNELDIR ?= /lib/modules/$(shell uname -r)/build 3 | PWD := $(shell pwd) 4 | CFLAGS ?= -O3 -march=native 5 | CFLAGS += -Wall -pedantic -std=gnu11 6 | 7 | extract-keys: extract-keys.c config.h 8 | $(CC) $(CFLAGS) $(CPPFLAGS) -D_FILE_OFFSET_BITS=64 -o $@ -lresolv $< 9 | 10 | config.o: config.c 11 | $(MAKE) -C $(KERNELDIR) M=$(PWD) $@ 12 | objcopy -j '.rodata*' $@ $@ 13 | 14 | config: config.c config.o 15 | $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $^ 16 | 17 | config.h: config 18 | ./$< > $@ 19 | 20 | clean: 21 | rm -f extract-keys config config.h 22 | $(MAKE) -C $(KERNELDIR) M=$(PWD) clean 23 | 24 | .PHONY: clean 25 | else 26 | obj-m := config.o 27 | endif 28 | -------------------------------------------------------------------------------- /contrib/extract-keys/README: -------------------------------------------------------------------------------- 1 | Key Extractor 2 | ============= 3 | 4 | This will extract the symmetric ChaCha20Poly1305 session keys from the kernel 5 | for a WireGuard interface, for use in making packet dissectors. 6 | 7 | 8 | Build: 9 | $ make 10 | 11 | Run (as root): 12 | # ./extract-keys INTERFACE 13 | 14 | Output: 15 | REMOTE_KEY_ID SENDING_KEY 16 | LOCAL_KEY_ID RECEIVING_KEY 17 | 18 | Example: 19 | # ./extract-keys wg0 20 | 0x57b56068 tMTSEOJpEYFAQV2UviDiYooX0A1AD/ONqrzoQVHa1rQ= 21 | 0xa182fd19 xvQSkQ5HTX5RUeJ74eAAb/xfNhdrDThxG91GXZIPKmY= 22 | 0x01662508 LbMc84JULzXJiHotSkdSOPZ0bHh6IDwOrbxWLfwosTs= 23 | 0xbd819021 4VA8lZ3I1HjnJcWTmhEzBdC92W1Aag9Lnyy2GkroOYI= 24 | -------------------------------------------------------------------------------- /contrib/extract-keys/config.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | /* 3 | * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | struct def { 7 | const char *name; 8 | long long value; 9 | }; 10 | extern const struct def defs[]; 11 | 12 | #ifdef __KERNEL__ 13 | #include "../drivers/net/wireguard/device.h" 14 | #include "../drivers/net/wireguard/peer.h" 15 | #include "../drivers/net/wireguard/noise.h" 16 | const struct def defs[] = { 17 | { "SOCK_DEVICE_OFFSET", offsetof(struct sock, sk_user_data) }, 18 | { "DEVICE_NAME_OFFSET", -ALIGN(sizeof(struct net_device), NETDEV_ALIGN) + offsetof(struct net_device, name) }, 19 | { "IFNAMSIZ", IFNAMSIZ }, 20 | { "DEVICE_PEERS_OFFSET", offsetof(struct wg_device, peer_list) }, 21 | { "PEERS_PEER_OFFSET", -offsetof(struct wg_peer, peer_list) }, 22 | { "PEER_CURRENTKEY_OFFSET", offsetof(struct wg_peer, keypairs.current_keypair) }, 23 | { "PEER_PREVIOUSKEY_OFFSET", offsetof(struct wg_peer, keypairs.previous_keypair) }, 24 | { "PEER_NEXTKEY_OFFSET", offsetof(struct wg_peer, keypairs.next_keypair) }, 25 | { "KEY_LOCALID_OFFSET", offsetof(struct noise_keypair, entry.index) }, 26 | { "KEY_REMOTEID_OFFSET", offsetof(struct noise_keypair, remote_index) }, 27 | { "KEY_SENDING_OFFSET", offsetof(struct noise_keypair, sending.key) }, 28 | { "KEY_RECEIVING_OFFSET", offsetof(struct noise_keypair, receiving.key) }, 29 | { NULL, 0 } 30 | }; 31 | #else 32 | #include 33 | int main(int argc, char *argv[]) 34 | { 35 | for (const struct def *def = defs; def->name; ++def) 36 | printf("#define %s %lld\n", def->name, def->value); 37 | return 0; 38 | } 39 | #endif 40 | -------------------------------------------------------------------------------- /contrib/extract-keys/extract-keys.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | /* 3 | * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "config.h" 16 | 17 | static int fd; 18 | 19 | static void open_kmem(void) 20 | { 21 | fd = open("/dev/kmem", O_RDONLY); 22 | if (fd < 0) { 23 | perror("open(/dev/kmem)"); 24 | exit(errno); 25 | } 26 | } 27 | 28 | static void read_kmem(void *buffer, size_t len, unsigned long addr) 29 | { 30 | if (lseek(fd, addr, SEEK_SET) == (off_t)-1) { 31 | perror("lseek"); 32 | exit(errno); 33 | } 34 | if (read(fd, buffer, len) != len) { 35 | perror("read"); 36 | exit(errno); 37 | } 38 | } 39 | 40 | static inline unsigned int read_int(unsigned long addr) 41 | { 42 | unsigned int ret; 43 | read_kmem(&ret, sizeof(ret), addr); 44 | return ret; 45 | } 46 | 47 | static inline unsigned long read_long(unsigned long addr) 48 | { 49 | unsigned long ret; 50 | read_kmem(&ret, sizeof(ret), addr); 51 | return ret; 52 | } 53 | 54 | static unsigned long find_interface(const char *interface) 55 | { 56 | FILE *f = fopen("/proc/net/udp", "r"); 57 | char line[256], *ptr; 58 | unsigned long addr = 0; 59 | char name[IFNAMSIZ + 1] = { 0 }; 60 | 61 | if (!f) { 62 | perror("fopen(/proc/net/udp)"); 63 | exit(errno); 64 | } 65 | if (!fgets(line, 256, f)) 66 | goto out; 67 | while (fgets(line, 256, f)) { 68 | ptr = line + strlen(line) - 1; 69 | while (*--ptr == ' '); 70 | while (*--ptr != ' '); 71 | while (*(--ptr - 1) != ' '); 72 | addr = strtoul(ptr, NULL, 16); 73 | if (!addr) 74 | continue; 75 | addr = read_long(addr + SOCK_DEVICE_OFFSET); 76 | if (!addr) 77 | continue; 78 | read_kmem(name, IFNAMSIZ, addr + DEVICE_NAME_OFFSET); 79 | if (!strcmp(name, interface)) 80 | goto out; 81 | } 82 | addr = 0; 83 | out: 84 | fclose(f); 85 | return addr; 86 | } 87 | 88 | static bool print_key(unsigned long key) 89 | { 90 | unsigned char sending[32], receiving[32]; 91 | char sending_b64[45], receiving_b64[45]; 92 | unsigned int local_index, remote_index; 93 | 94 | if (!key) 95 | return false; 96 | 97 | local_index = le32toh(read_int(key + KEY_LOCALID_OFFSET)); 98 | remote_index = le32toh(read_int(key + KEY_REMOTEID_OFFSET)); 99 | read_kmem(sending, 32, key + KEY_SENDING_OFFSET); 100 | read_kmem(receiving, 32, key + KEY_RECEIVING_OFFSET); 101 | 102 | b64_ntop(sending, 32, sending_b64, 45); 103 | b64_ntop(receiving, 32, receiving_b64, 45); 104 | 105 | printf("0x%08x %s\n", local_index, receiving_b64); 106 | printf("0x%08x %s\n", remote_index, sending_b64); 107 | return true; 108 | } 109 | 110 | static bool walk_peers(unsigned long peer_head) 111 | { 112 | unsigned long peer, peer_entry; 113 | bool found = false; 114 | for (peer_entry = read_long(peer_head); peer_entry != peer_head; peer_entry = read_long(peer_entry)) { 115 | peer = peer_entry + PEERS_PEER_OFFSET; 116 | if (print_key(read_long(peer + PEER_CURRENTKEY_OFFSET))) 117 | found = true; 118 | if (print_key(read_long(peer + PEER_PREVIOUSKEY_OFFSET))) 119 | found = true; 120 | if (print_key(read_long(peer + PEER_NEXTKEY_OFFSET))) 121 | found = true; 122 | } 123 | return found; 124 | } 125 | 126 | int main(int argc, char *argv[]) 127 | { 128 | unsigned long wireguard_device; 129 | if (argc < 2) { 130 | fprintf(stderr, "Usage: %s WIREGUARD_INTERFACE\n", argv[0]); 131 | return EINVAL; 132 | } 133 | open_kmem(); 134 | wireguard_device = find_interface(argv[1]); 135 | if (!wireguard_device) { 136 | fprintf(stderr, "Could not find interface %s\n", argv[1]); 137 | return EBADSLT; 138 | } 139 | if (!walk_peers(wireguard_device + DEVICE_PEERS_OFFSET)) { 140 | fprintf(stderr, "No active sessions\n"); 141 | return ENOKEY; 142 | } 143 | return 0; 144 | } 145 | -------------------------------------------------------------------------------- /contrib/highlighter/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS ?= -O3 -march=native 2 | CFLAGS += -std=gnu99 3 | CFLAGS += -Wall 4 | CFLAGS += -MMD -MP 5 | 6 | highlight: highlight.o highlighter.o 7 | 8 | fuzz: CC := clang 9 | fuzz: CFLAGS += -fsanitize=fuzzer 10 | fuzz: fuzz.c highlighter.c 11 | 12 | gui/Makefile: gui/highlight.pro 13 | cd gui && qmake 14 | gui: gui/Makefile 15 | @$(MAKE) -C gui 16 | 17 | clean: 18 | rm -f highlight fuzz *.o *.d 19 | @if [ -f gui/Makefile ]; then $(MAKE) -C gui distclean; fi 20 | 21 | .PHONY: clean gui 22 | .DEFAULT_GOAL: highlight 23 | MAKEFLAGS += --no-print-directory 24 | 25 | -include *.d 26 | -------------------------------------------------------------------------------- /contrib/highlighter/README: -------------------------------------------------------------------------------- 1 | wg(8) and wg-quick(8) syntax highlighter library 2 | ================================================ 3 | 4 | highlighter.c contains a simple portable highlighter for the wg(8) and 5 | wg-quick(8) configuration files. Simply copy `highlight.c` and 6 | `highlight.h` into your project wholesale. 7 | 8 | As a demo, a simple console highlighter program is included, alongside a 9 | simple Qt5 GUI app to show its usage in realtime. 10 | 11 | There is also a basic fuzzer, because why not? 12 | 13 | Usage: 14 | 15 | $ make 16 | $ ./highlight < path/to/tunnel.conf 17 | 18 | $ make gui 19 | $ ./gui/highlight 20 | 21 | $ make fuzz 22 | $ ./fuzz -workers=$(nproc) -jobs=$(nproc) ./corpus 23 | -------------------------------------------------------------------------------- /contrib/highlighter/fuzz.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | /* 3 | * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | #include 7 | #include 8 | #include "highlighter.h" 9 | 10 | int LLVMFuzzerTestOneInput(const char *data, size_t size) 11 | { 12 | char *str = strndup(data, size); 13 | if (!str) 14 | return 0; 15 | struct highlight_span *spans = highlight_config(str); 16 | if (!spans) 17 | return 0; 18 | for (struct highlight_span *span = spans; span->type != HighlightEnd; ++span); 19 | free(spans); 20 | free(str); 21 | return 0; 22 | } 23 | -------------------------------------------------------------------------------- /contrib/highlighter/gui/highlight.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | extern "C" { 9 | #include "../highlighter.h" 10 | } 11 | 12 | static QColor colormap[] = { 13 | [HighlightSection] = QColor("#ababab"), 14 | [HighlightField] = QColor("#70c0b1"), 15 | [HighlightPrivateKey] = QColor("#7aa6da"), 16 | [HighlightPublicKey] = QColor("#7aa6da"), 17 | [HighlightPresharedKey] = QColor("#7aa6da"), 18 | [HighlightIP] = QColor("#b9ca4a"), 19 | [HighlightCidr] = QColor("#e78c45"), 20 | [HighlightHost] = QColor("#b9ca4a"), 21 | [HighlightPort] = QColor("#e78c45"), 22 | [HighlightMTU] = QColor("#c397d8"), 23 | [HighlightKeepalive] = QColor("#c397d8"), 24 | [HighlightComment] = QColor("#969896"), 25 | [HighlightDelimiter] = QColor("#7aa6da"), 26 | #ifndef MOBILE_WGQUICK_SUBSET 27 | [HighlightTable] = QColor("#c397d8"), 28 | [HighlightFwMark] = QColor("#c397d8"), 29 | [HighlightSaveConfig] = QColor("#c397d8"), 30 | [HighlightCmd] = QColor("#969896"), 31 | #endif 32 | [HighlightError] = QColor("#d54e53") 33 | }; 34 | 35 | int main(int argc, char *argv[]) 36 | { 37 | QApplication a(argc, argv); 38 | QWidget w; 39 | w.setWindowTitle(QObject::tr("WireGuard Configuration Highlighter")); 40 | QVBoxLayout v; 41 | w.setLayout(&v); 42 | QPlainTextEdit e; 43 | v.addWidget(&e); 44 | QPalette p(e.palette()); 45 | p.setColor(QPalette::Base, QColor("#010101")); 46 | p.setColor(QPalette::Text, QColor("#eaeaea")); 47 | e.setPalette(p); 48 | QFont f(QFontDatabase::systemFont(QFontDatabase::FixedFont)); 49 | f.setPointSize(16); 50 | e.setFont(f); 51 | e.setMinimumSize(400, 500); 52 | bool guard = false; 53 | QObject::connect(&e, &QPlainTextEdit::textChanged, [&]() { 54 | if (guard) 55 | return; 56 | struct highlight_span *spans = highlight_config(e.toPlainText().toLatin1().data()); 57 | if (!spans) 58 | return; 59 | QTextCursor cursor(e.document()); 60 | QTextCharFormat format; 61 | cursor.beginEditBlock(); 62 | cursor.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor); 63 | cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor); 64 | format.setForeground(p.color(QPalette::Text)); 65 | format.setUnderlineStyle(QTextCharFormat::NoUnderline); 66 | cursor.mergeCharFormat(format); 67 | for (struct highlight_span *span = spans; span->type != HighlightEnd; ++span) { 68 | cursor.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor); 69 | cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, span->start); 70 | cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, span->len); 71 | format.setForeground(colormap[span->type]); 72 | format.setUnderlineStyle(span->type == HighlightError ? QTextCharFormat::SpellCheckUnderline : QTextCharFormat::NoUnderline); 73 | cursor.mergeCharFormat(format); 74 | } 75 | free(spans); 76 | guard = true; 77 | cursor.endEditBlock(); 78 | guard = false; 79 | }); 80 | QPushButton b; 81 | v.addWidget(&b); 82 | b.setText(QObject::tr("&Randomize colors")); 83 | QObject::connect(&b, &QPushButton::clicked, [&]() { 84 | for (size_t i = 0; i < sizeof(colormap) / sizeof(colormap[0]); ++i) 85 | colormap[i] = QColor::fromHsl(qrand() % 360, qrand() % 192 + 64, qrand() % 128 + 128); 86 | e.setPlainText(e.toPlainText()); 87 | }); 88 | w.show(); 89 | return a.exec(); 90 | } 91 | -------------------------------------------------------------------------------- /contrib/highlighter/gui/highlight.pro: -------------------------------------------------------------------------------- 1 | QT += core gui widgets 2 | TEMPLATE = app 3 | TARGET = highlight 4 | SOURCES += highlight.cpp ../highlighter.c 5 | HEADERS += ../highlighter.h 6 | -------------------------------------------------------------------------------- /contrib/highlighter/highlight.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | /* 3 | * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include "highlighter.h" 10 | 11 | #define TERMINAL_FG_BLACK "\x1b[30m" 12 | #define TERMINAL_FG_RED "\x1b[31m" 13 | #define TERMINAL_FG_GREEN "\x1b[32m" 14 | #define TERMINAL_FG_YELLOW "\x1b[33m" 15 | #define TERMINAL_FG_BLUE "\x1b[34m" 16 | #define TERMINAL_FG_MAGENTA "\x1b[35m" 17 | #define TERMINAL_FG_CYAN "\x1b[36m" 18 | #define TERMINAL_FG_WHITE "\x1b[37m" 19 | #define TERMINAL_FG_DEFAULT "\x1b[39m" 20 | 21 | #define TERMINAL_BG_BLACK "\x1b[40m" 22 | #define TERMINAL_BG_RED "\x1b[41m" 23 | #define TERMINAL_BG_GREEN "\x1b[42m" 24 | #define TERMINAL_BG_YELLOW "\x1b[43m" 25 | #define TERMINAL_BG_BLUE "\x1b[44m" 26 | #define TERMINAL_BG_MAGENTA "\x1b[45m" 27 | #define TERMINAL_BG_CYAN "\x1b[46m" 28 | #define TERMINAL_BG_WHITE "\x1b[47m" 29 | #define TERMINAL_BG_DEFAULT "\x1b[49m" 30 | 31 | #define TERMINAL_BOLD "\x1b[1m" 32 | #define TERMINAL_NO_BOLD "\x1b[22m" 33 | #define TERMINAL_UNDERLINE "\x1b[4m" 34 | #define TERMINAL_NO_UNDERLINE "\x1b[24m" 35 | 36 | #define TERMINAL_RESET "\x1b[0m" 37 | 38 | static const char *colormap[] = { 39 | [HighlightSection] = TERMINAL_FG_BLACK TERMINAL_BOLD, 40 | [HighlightField] = TERMINAL_FG_BLUE TERMINAL_BOLD, 41 | [HighlightPrivateKey] = TERMINAL_FG_YELLOW TERMINAL_BOLD, 42 | [HighlightPublicKey] = TERMINAL_FG_YELLOW TERMINAL_BOLD, 43 | [HighlightPresharedKey] = TERMINAL_FG_YELLOW TERMINAL_BOLD, 44 | [HighlightIP] = TERMINAL_FG_GREEN, 45 | [HighlightCidr] = TERMINAL_FG_YELLOW, 46 | [HighlightHost] = TERMINAL_FG_GREEN TERMINAL_BOLD, 47 | [HighlightPort] = TERMINAL_FG_MAGENTA, 48 | [HighlightMTU] = TERMINAL_FG_BLUE, 49 | [HighlightKeepalive] = TERMINAL_FG_BLUE, 50 | [HighlightComment] = TERMINAL_FG_CYAN, 51 | [HighlightDelimiter] = TERMINAL_FG_CYAN, 52 | #ifndef MOBILE_WGQUICK_SUBSET 53 | [HighlightTable] = TERMINAL_FG_BLUE, 54 | [HighlightFwMark] = TERMINAL_FG_BLUE, 55 | [HighlightSaveConfig] = TERMINAL_FG_BLUE, 56 | [HighlightCmd] = TERMINAL_FG_WHITE, 57 | #endif 58 | [HighlightError] = TERMINAL_FG_RED TERMINAL_UNDERLINE 59 | }; 60 | 61 | int main(int argc, char *argv[]) 62 | { 63 | char input[1024 * 1024]; 64 | struct highlight_span *spans; 65 | size_t last = 0, total_len; 66 | 67 | total_len = fread(input, 1, sizeof(input) - 1, stdin); 68 | input[total_len] = '\0'; 69 | spans = highlight_config(input); 70 | 71 | fputs(TERMINAL_RESET, stdout); 72 | for (struct highlight_span *span = spans; span->type != HighlightEnd; ++span) { 73 | fwrite(input + last, 1, span->start - last, stdout); 74 | fputs(colormap[span->type], stdout); 75 | fwrite(input + span->start, 1, span->len, stdout); 76 | fputs(TERMINAL_RESET, stdout); 77 | last = span->start + span->len; 78 | } 79 | fwrite(input + last, 1, total_len - last, stdout); 80 | 81 | free(spans); 82 | return 0; 83 | } 84 | -------------------------------------------------------------------------------- /contrib/highlighter/highlighter.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 */ 2 | /* 3 | * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | #include 7 | 8 | enum highlight_type { 9 | HighlightSection, 10 | HighlightField, 11 | HighlightPrivateKey, 12 | HighlightPublicKey, 13 | HighlightPresharedKey, 14 | HighlightIP, 15 | HighlightCidr, 16 | HighlightHost, 17 | HighlightPort, 18 | HighlightMTU, 19 | HighlightKeepalive, 20 | HighlightComment, 21 | HighlightDelimiter, 22 | #ifndef MOBILE_WGQUICK_SUBSET 23 | HighlightTable, 24 | HighlightFwMark, 25 | HighlightSaveConfig, 26 | HighlightCmd, 27 | #endif 28 | HighlightError, 29 | HighlightEnd 30 | }; 31 | 32 | struct highlight_span { 33 | enum highlight_type type; 34 | size_t start, len; 35 | }; 36 | 37 | struct highlight_span *highlight_config(const char *config); 38 | -------------------------------------------------------------------------------- /contrib/json/README: -------------------------------------------------------------------------------- 1 | wg-json 2 | ======= 3 | 4 | This will dump all current WireGuard status as JSON output. 5 | 6 | Usage: 7 | 8 | # wg-json 9 | -------------------------------------------------------------------------------- /contrib/json/wg-json: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # SPDX-License-Identifier: GPL-2.0 3 | # 4 | # Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. 5 | # Copyright (C) 2024 Amnezia VPN. All Rights Reserved. 6 | 7 | exec < <(exec awg show all dump) 8 | 9 | printf '{' 10 | while read -r -d $'\t' device; do 11 | if [[ $device != "$last_device" ]]; then 12 | [[ -z $last_device ]] && printf '\n' || printf '%s,\n' "$end" 13 | last_device="$device" 14 | read -r private_key public_key listen_port jc jmin jmax s1 s2 h1 h2 h3 h4 fwmark 15 | printf '\t"%s": {' "$device" 16 | delim=$'\n' 17 | [[ $private_key == "(none)" ]] || { printf '%s\t\t"privateKey": "%s"' "$delim" "$private_key"; delim=$',\n'; } 18 | [[ $public_key == "(none)" ]] || { printf '%s\t\t"publicKey": "%s"' "$delim" "$public_key"; delim=$',\n'; } 19 | [[ $listen_port == "0" ]] || { printf '%s\t\t"listenPort": %u' "$delim" $(( $listen_port )); delim=$',\n'; } 20 | [[ $jc == "0" ]] || { printf '%s\t\t"jc": %u' "$delim" $(( $jc )); delim=$',\n'; } 21 | [[ $jmin == "0" ]] || { printf '%s\t\t"jmin": %u' "$delim" $(( $jmin )); delim=$',\n'; } 22 | [[ $jmax == "0" ]] || { printf '%s\t\t"jmax": %u' "$delim" $(( $jmax )); delim=$',\n'; } 23 | [[ $s1 == "0" ]] || { printf '%s\t\t"s1": %u' "$delim" $(( $s1 )); delim=$',\n'; } 24 | [[ $s2 == "0" ]] || { printf '%s\t\t"s2": %u' "$delim" $(( $s2 )); delim=$',\n'; } 25 | [[ $h1 == "1" ]] || { printf '%s\t\t"h1": %u' "$delim" $(( $h1 )); delim=$',\n'; } 26 | [[ $h2 == "2" ]] || { printf '%s\t\t"h2": %u' "$delim" $(( $h2 )); delim=$',\n'; } 27 | [[ $h3 == "3" ]] || { printf '%s\t\t"h3": %u' "$delim" $(( $h3 )); delim=$',\n'; } 28 | [[ $h4 == "4" ]] || { printf '%s\t\t"h4": %u' "$delim" $(( $h4 )); delim=$',\n'; } 29 | [[ $fwmark == "off" ]] || { printf '%s\t\t"fwmark": %u' "$delim" $(( $fwmark )); delim=$',\n'; } 30 | printf '%s\t\t"peers": {' "$delim"; end=$'\n\t\t}\n\t}' 31 | delim=$'\n' 32 | else 33 | read -r public_key preshared_key endpoint allowed_ips latest_handshake transfer_rx transfer_tx persistent_keepalive 34 | printf '%s\t\t\t"%s": {' "$delim" "$public_key" 35 | delim=$'\n' 36 | [[ $preshared_key == "(none)" ]] || { printf '%s\t\t\t\t"presharedKey": "%s"' "$delim" "$preshared_key"; delim=$',\n'; } 37 | [[ $endpoint == "(none)" ]] || { printf '%s\t\t\t\t"endpoint": "%s"' "$delim" "$endpoint"; delim=$',\n'; } 38 | [[ $latest_handshake == "0" ]] || { printf '%s\t\t\t\t"latestHandshake": %u' "$delim" $(( $latest_handshake )); delim=$',\n'; } 39 | [[ $transfer_rx == "0" ]] || { printf '%s\t\t\t\t"transferRx": %u' "$delim" $(( $transfer_rx )); delim=$',\n'; } 40 | [[ $transfer_tx == "0" ]] || { printf '%s\t\t\t\t"transferTx": %u' "$delim" $(( $transfer_tx )); delim=$',\n'; } 41 | [[ $persistent_keepalive == "off" ]] || { printf '%s\t\t\t\t"persistentKeepalive": %u' "$delim" $(( $persistent_keepalive )); delim=$',\n'; } 42 | printf '%s\t\t\t\t"allowedIps": [' "$delim" 43 | delim=$'\n' 44 | if [[ $allowed_ips != "(none)" ]]; then 45 | old_ifs="$IFS" 46 | IFS=, 47 | for ip in $allowed_ips; do 48 | printf '%s\t\t\t\t\t"%s"' "$delim" "$ip" 49 | delim=$',\n' 50 | done 51 | IFS="$old_ifs" 52 | delim=$'\n' 53 | fi 54 | printf '%s\t\t\t\t]' "$delim" 55 | printf '\n\t\t\t}' 56 | delim=$',\n' 57 | fi 58 | 59 | 60 | done 61 | printf '%s\n' "$end" 62 | printf '}\n' 63 | -------------------------------------------------------------------------------- /contrib/keygen-html/README: -------------------------------------------------------------------------------- 1 | WireGuard Key Generation in JavaScript 2 | ====================================== 3 | 4 | Various people believe in JavaScript crypto, unfortunately. This small 5 | example helps them fuel their poor taste. 6 | 7 | It's possible to generate WireGuard keys (and thus configurations) in the 8 | browser. The webpage here simulates talking to a server to exchange keys 9 | and then generates a configuration file for the user to download. 10 | 11 | Bugs 12 | ---- 13 | 14 | Who knows how emscripten actually compiles this and whether or not it 15 | introduces interesting side-channel attacks. 16 | 17 | Secrets aren't zerored after use. Maybe you can get around this with 18 | some tricks taking advantage of browser allocator behavior and different 19 | processes, but it seems pretty hard. 20 | -------------------------------------------------------------------------------- /contrib/keygen-html/keygen.html: -------------------------------------------------------------------------------- 1 | 2 | 176 | 177 |

Download a WireGuard configuration file

178 |

Download several WireGuard configuration files

179 | -------------------------------------------------------------------------------- /contrib/keygen-html/wireguard.js: -------------------------------------------------------------------------------- 1 | /*! SPDX-License-Identifier: GPL-2.0 2 | * 3 | * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | (function() { 7 | function gf(init) { 8 | var r = new Float64Array(16); 9 | if (init) { 10 | for (var i = 0; i < init.length; ++i) 11 | r[i] = init[i]; 12 | } 13 | return r; 14 | } 15 | 16 | function pack(o, n) { 17 | var b, m = gf(), t = gf(); 18 | for (var i = 0; i < 16; ++i) 19 | t[i] = n[i]; 20 | carry(t); 21 | carry(t); 22 | carry(t); 23 | for (var j = 0; j < 2; ++j) { 24 | m[0] = t[0] - 0xffed; 25 | for (var i = 1; i < 15; ++i) { 26 | m[i] = t[i] - 0xffff - ((m[i - 1] >> 16) & 1); 27 | m[i - 1] &= 0xffff; 28 | } 29 | m[15] = t[15] - 0x7fff - ((m[14] >> 16) & 1); 30 | b = (m[15] >> 16) & 1; 31 | m[14] &= 0xffff; 32 | cswap(t, m, 1 - b); 33 | } 34 | for (var i = 0; i < 16; ++i) { 35 | o[2 * i] = t[i] & 0xff; 36 | o[2 * i + 1] = t[i] >> 8; 37 | } 38 | } 39 | 40 | function carry(o) { 41 | var c; 42 | for (var i = 0; i < 16; ++i) { 43 | o[(i + 1) % 16] += (i < 15 ? 1 : 38) * Math.floor(o[i] / 65536); 44 | o[i] &= 0xffff; 45 | } 46 | } 47 | 48 | function cswap(p, q, b) { 49 | var t, c = ~(b - 1); 50 | for (var i = 0; i < 16; ++i) { 51 | t = c & (p[i] ^ q[i]); 52 | p[i] ^= t; 53 | q[i] ^= t; 54 | } 55 | } 56 | 57 | function add(o, a, b) { 58 | for (var i = 0; i < 16; ++i) 59 | o[i] = (a[i] + b[i]) | 0; 60 | } 61 | 62 | function subtract(o, a, b) { 63 | for (var i = 0; i < 16; ++i) 64 | o[i] = (a[i] - b[i]) | 0; 65 | } 66 | 67 | function multmod(o, a, b) { 68 | var t = new Float64Array(31); 69 | for (var i = 0; i < 16; ++i) { 70 | for (var j = 0; j < 16; ++j) 71 | t[i + j] += a[i] * b[j]; 72 | } 73 | for (var i = 0; i < 15; ++i) 74 | t[i] += 38 * t[i + 16]; 75 | for (var i = 0; i < 16; ++i) 76 | o[i] = t[i]; 77 | carry(o); 78 | carry(o); 79 | } 80 | 81 | function invert(o, i) { 82 | var c = gf(); 83 | for (var a = 0; a < 16; ++a) 84 | c[a] = i[a]; 85 | for (var a = 253; a >= 0; --a) { 86 | multmod(c, c, c); 87 | if (a !== 2 && a !== 4) 88 | multmod(c, c, i); 89 | } 90 | for (var a = 0; a < 16; ++a) 91 | o[a] = c[a]; 92 | } 93 | 94 | function clamp(z) { 95 | z[31] = (z[31] & 127) | 64; 96 | z[0] &= 248; 97 | } 98 | 99 | function generatePublicKey(privateKey) { 100 | var r, z = new Uint8Array(32); 101 | var a = gf([1]), 102 | b = gf([9]), 103 | c = gf(), 104 | d = gf([1]), 105 | e = gf(), 106 | f = gf(), 107 | _121665 = gf([0xdb41, 1]), 108 | _9 = gf([9]); 109 | for (var i = 0; i < 32; ++i) 110 | z[i] = privateKey[i]; 111 | clamp(z); 112 | for (var i = 254; i >= 0; --i) { 113 | r = (z[i >>> 3] >>> (i & 7)) & 1; 114 | cswap(a, b, r); 115 | cswap(c, d, r); 116 | add(e, a, c); 117 | subtract(a, a, c); 118 | add(c, b, d); 119 | subtract(b, b, d); 120 | multmod(d, e, e); 121 | multmod(f, a, a); 122 | multmod(a, c, a); 123 | multmod(c, b, e); 124 | add(e, a, c); 125 | subtract(a, a, c); 126 | multmod(b, a, a); 127 | subtract(c, d, f); 128 | multmod(a, c, _121665); 129 | add(a, a, d); 130 | multmod(c, c, a); 131 | multmod(a, d, f); 132 | multmod(d, b, _9); 133 | multmod(b, e, e); 134 | cswap(a, b, r); 135 | cswap(c, d, r); 136 | } 137 | invert(c, c); 138 | multmod(a, a, c); 139 | pack(z, a); 140 | return z; 141 | } 142 | 143 | function generatePresharedKey() { 144 | var privateKey = new Uint8Array(32); 145 | window.crypto.getRandomValues(privateKey); 146 | return privateKey; 147 | } 148 | 149 | function generatePrivateKey() { 150 | var privateKey = generatePresharedKey(); 151 | clamp(privateKey); 152 | return privateKey; 153 | } 154 | 155 | function encodeBase64(dest, src) { 156 | var input = Uint8Array.from([(src[0] >> 2) & 63, ((src[0] << 4) | (src[1] >> 4)) & 63, ((src[1] << 2) | (src[2] >> 6)) & 63, src[2] & 63]); 157 | for (var i = 0; i < 4; ++i) 158 | dest[i] = input[i] + 65 + 159 | (((25 - input[i]) >> 8) & 6) - 160 | (((51 - input[i]) >> 8) & 75) - 161 | (((61 - input[i]) >> 8) & 15) + 162 | (((62 - input[i]) >> 8) & 3); 163 | } 164 | 165 | function keyToBase64(key) { 166 | var i, base64 = new Uint8Array(44); 167 | for (i = 0; i < 32 / 3; ++i) 168 | encodeBase64(base64.subarray(i * 4), key.subarray(i * 3)); 169 | encodeBase64(base64.subarray(i * 4), Uint8Array.from([key[i * 3 + 0], key[i * 3 + 1], 0])); 170 | base64[43] = 61; 171 | return String.fromCharCode.apply(null, base64); 172 | } 173 | 174 | window.wireguard = { 175 | generateKeypair: function() { 176 | var privateKey = generatePrivateKey(); 177 | var publicKey = generatePublicKey(privateKey); 178 | return { 179 | publicKey: keyToBase64(publicKey), 180 | privateKey: keyToBase64(privateKey) 181 | }; 182 | } 183 | }; 184 | })(); 185 | -------------------------------------------------------------------------------- /contrib/launchd/README: -------------------------------------------------------------------------------- 1 | WireGuard for Launchd 2 | ===================== 3 | 4 | The example `com.wireguard.wg0.plist` file may be used for running wg-quick(8) 5 | as a launchd service. Note that the `PATH` variable is modified to point to 6 | the PATH used by Homebrew or Macports, so that it uses the non-system bash(1). 7 | 8 | Usage 9 | ----- 10 | 11 | $ sudo cp com.wireguard.wg0.plist /Library/LaunchDaemons 12 | $ sudo launchctl load /Library/LaunchDaemons/com.wireguard.wg0.plist 13 | -------------------------------------------------------------------------------- /contrib/launchd/com.wireguard.wg0.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Label 6 | com.wireguard.wg0 7 | ProgramArguments 8 | 9 | /usr/local/bin/wg-quick 10 | up 11 | /usr/local/etc/wireguard/wg0.conf 12 | 13 | OnDemand 14 | 15 | RunAtLoad 16 | 17 | TimeOut 18 | 90 19 | EnvironmentVariables 20 | 21 | PATH 22 | /usr/local/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /contrib/nat-hole-punching/README: -------------------------------------------------------------------------------- 1 | == NAT Hole Punching Example == 2 | 3 | This code should never be used, ever. But, it's a nice demonstration of how 4 | to punch holes and have two NAT'd peers talk to each other. 5 | 6 | Compile with: 7 | $ gcc nat-punch-client.c -o client -lresolv 8 | $ gcc nat-punch-server.c -o server 9 | 10 | 11 | Server is 1.2.3.4 and is on the public internet accepting UDP:49918. 12 | Client A is NAT'd and doesn't know its IP address. 13 | Client B is NAT'd and doesn't know its IP address. 14 | 15 | 16 | Server runs: 17 | $ ./server 18 | 19 | Client A runs: 20 | # ip link add wg0 type wireguard 21 | # ip addr add 10.200.200.1 peer 10.200.200.2 dev wg0 22 | # wg set wg0 private-key ... peer ... allowed-ips 10.200.200.2/32 23 | # ./client 1.2.3.4 wg0 24 | # ping 10.200.200.2 25 | 26 | Client B runs: 27 | # ip link add wg0 type wireguard 28 | # ip addr add 10.200.200.2 peer 10.200.200.1 dev wg0 29 | # wg set wg0 private-key ... peer ... allowed-ips 10.200.200.1/32 30 | # ./client 1.2.3.4 wg0 31 | # ping 10.200.200.1 32 | 33 | And voila! Client A and Client B can speak from behind NAT. 34 | 35 | 36 | 37 | ----- 38 | Keep in mind that this is proof-of-concept example code. It is not code that 39 | should be used in production, ever. It is woefully insecure, and is unsuitable 40 | for any real usage. With that said, this is useful as a learning example of 41 | how NAT hole punching might work within a more developed solution. 42 | -------------------------------------------------------------------------------- /contrib/nat-hole-punching/nat-punch-client.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | /* 3 | * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. 4 | * 5 | * Example only. Do not run in production. 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | enum { MAX_PEERS = 65536, PORT = 49918 }; 27 | 28 | static struct { 29 | uint8_t base64_key[45]; 30 | bool have_seen; 31 | } peers[MAX_PEERS]; 32 | static unsigned int total_peers; 33 | 34 | static const char *cmd(const char *line, ...) 35 | { 36 | static char buf[2048]; 37 | char full_cmd[2048] = { 0 }; 38 | size_t len; 39 | FILE *f; 40 | va_list args; 41 | va_start(args, line); 42 | vsnprintf(full_cmd, 2047, line, args); 43 | va_end(args); 44 | f = popen(full_cmd, "r"); 45 | if (!f) { 46 | perror("popen"); 47 | exit(errno); 48 | } 49 | if (!fgets(buf, 2048, f)) { 50 | pclose(f); 51 | return NULL; 52 | } 53 | pclose(f); 54 | len = strlen(buf); 55 | if (!len) 56 | return NULL; 57 | if (buf[len - 1] == '\n') 58 | buf[len - 1] = '\0'; 59 | return buf; 60 | } 61 | 62 | static void read_peers(const char *interface) 63 | { 64 | char full_cmd[2048] = { 0 }; 65 | size_t len; 66 | FILE *f; 67 | snprintf(full_cmd, 2047, "wg show %s peers", interface); 68 | f = popen(full_cmd, "r"); 69 | if (!f) { 70 | perror("popen"); 71 | exit(errno); 72 | } 73 | for (;;) { 74 | if (!fgets(peers[total_peers].base64_key, 45, f)) 75 | break; 76 | len = strlen(peers[total_peers].base64_key); 77 | if (len != 44 && len != 45) 78 | continue; 79 | if (peers[total_peers].base64_key[len - 1] == '\n') 80 | peers[total_peers].base64_key[len - 1] = '\0'; 81 | ++total_peers; 82 | } 83 | pclose(f); 84 | } 85 | 86 | static void unbase64(uint8_t dstkey[32], const char *srckey) 87 | { 88 | uint8_t buf[33]; 89 | if (b64_pton(srckey, buf, 33) != 32) { 90 | fprintf(stderr, "Could not parse base64 key: %s\n", srckey); 91 | exit(EINVAL); 92 | } 93 | memcpy(dstkey, buf, 32); 94 | } 95 | 96 | static void apply_bpf(int sock, uint16_t port, uint32_t ip) 97 | { 98 | struct sock_filter filter[] = { 99 | BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 12 /* src ip */), 100 | BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ip, 0, 5), 101 | BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20 /* src port */), 102 | BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, PORT, 0, 3), 103 | BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 22 /* dst port */), 104 | BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, port, 0, 1), 105 | BPF_STMT(BPF_RET + BPF_K, -1), 106 | BPF_STMT(BPF_RET + BPF_K, 0) 107 | }; 108 | struct sock_fprog filter_prog = { 109 | .len = sizeof(filter) / sizeof(filter[0]), 110 | .filter = filter 111 | }; 112 | if (setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter_prog, sizeof(filter_prog)) < 0) { 113 | perror("setsockopt(bpf)"); 114 | exit(errno); 115 | } 116 | } 117 | 118 | int main(int argc, char *argv[]) 119 | { 120 | struct sockaddr_in addr = { 121 | .sin_family = AF_INET 122 | }; 123 | struct { 124 | struct udphdr udp; 125 | uint8_t my_pubkey[32]; 126 | uint8_t their_pubkey[32]; 127 | } __attribute__((packed)) packet = { 128 | .udp = { 129 | .len = htons(sizeof(packet)), 130 | .dest = htons(PORT) 131 | } 132 | }; 133 | struct { 134 | struct iphdr iphdr; 135 | struct udphdr udp; 136 | uint32_t ip; 137 | uint16_t port; 138 | } __attribute__((packed)) reply; 139 | ssize_t len; 140 | int sock, i; 141 | bool repeat; 142 | struct hostent *ent; 143 | const char *server = argv[1], *interface = argv[2]; 144 | 145 | if (argc < 3) { 146 | fprintf(stderr, "Usage: %s SERVER WIREGUARD_INTERFACE\nExample:\n %s demo.wireguard.com wg0\n", argv[0], argv[0]); 147 | return EINVAL; 148 | } 149 | 150 | if (getuid() != 0) { 151 | fprintf(stderr, "Must be root!\n"); 152 | return EPERM; 153 | } 154 | 155 | ent = gethostbyname2(server, AF_INET); 156 | if (!ent) { 157 | herror("gethostbyname2"); 158 | return h_errno; 159 | } 160 | addr.sin_addr = *(struct in_addr *)ent->h_addr; 161 | read_peers(interface); 162 | cmd("ip link set %s up", interface); 163 | unbase64(packet.my_pubkey, cmd("wg show %s public-key", interface)); 164 | packet.udp.source = htons(atoi(cmd("wg show %s listen-port", interface))); 165 | 166 | /* We use raw sockets so that the WireGuard interface can actually own the real socket. */ 167 | sock = socket(AF_INET, SOCK_RAW, IPPROTO_UDP); 168 | if (sock < 0) { 169 | perror("socket"); 170 | return errno; 171 | } 172 | apply_bpf(sock, ntohs(packet.udp.source), ntohl(addr.sin_addr.s_addr)); 173 | 174 | check_again: 175 | repeat = false; 176 | for (i = 0; i < total_peers; ++i) { 177 | if (peers[i].have_seen) 178 | continue; 179 | printf("[+] Requesting IP and port of %s: ", peers[i].base64_key); 180 | unbase64(packet.their_pubkey, peers[i].base64_key); 181 | if (sendto(sock, &packet, sizeof(packet), 0, (struct sockaddr *)&addr, sizeof(addr)) < 0) { 182 | putchar('\n'); 183 | perror("sendto"); 184 | return errno; 185 | } 186 | len = recv(sock, &reply, sizeof(reply), 0); 187 | if (len < 0) { 188 | putchar('\n'); 189 | perror("recv"); 190 | return errno; 191 | } 192 | if (len != sizeof(reply)) { 193 | printf("server does not yet have it\n"); 194 | repeat = true; 195 | } else { 196 | printf("%s:%d\n", inet_ntoa(*(struct in_addr *)&reply.ip), ntohs(reply.port)); 197 | peers[i].have_seen = true; 198 | cmd("wg set %s peer %s persistent-keepalive 25 endpoint %s:%d", interface, peers[i].base64_key, inet_ntoa(*(struct in_addr *)&reply.ip), ntohs(reply.port)); 199 | } 200 | } 201 | if (repeat) { 202 | sleep(2); 203 | goto check_again; 204 | } 205 | 206 | close(sock); 207 | return 0; 208 | } 209 | -------------------------------------------------------------------------------- /contrib/nat-hole-punching/nat-punch-server.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | /* 3 | * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. 4 | * 5 | * Example only. Do not run in production. 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | struct entry { 20 | uint8_t pubkey[32]; 21 | uint32_t ip; 22 | uint16_t port; 23 | }; 24 | 25 | enum { MAX_ENTRIES = 65536, PORT = 49918 }; 26 | 27 | static struct entry entries[MAX_ENTRIES]; 28 | static unsigned int next_entry; 29 | 30 | /* XX: this should use a hash table */ 31 | static struct entry *find_entry(uint8_t key[32]) 32 | { 33 | int i; 34 | for (i = 0; i < MAX_ENTRIES; ++i) { 35 | if (!memcmp(entries[i].pubkey, key, 32)) 36 | return &entries[i]; 37 | } 38 | return NULL; 39 | } 40 | 41 | /* XX: this is obviously vulnerable to DoS */ 42 | static struct entry *find_or_insert_entry(uint8_t key[32]) 43 | { 44 | struct entry *entry = find_entry(key); 45 | if (!entry) { 46 | entry = &entries[next_entry++ % MAX_ENTRIES]; 47 | memcpy(entry->pubkey, key, 32); 48 | } 49 | return entry; 50 | } 51 | 52 | int main(int argc, char *argv[]) 53 | { 54 | struct sockaddr_in addr = { 55 | .sin_family = AF_INET, 56 | .sin_addr = { .s_addr = htonl(INADDR_ANY) }, 57 | .sin_port = htons(PORT) 58 | }; 59 | struct { 60 | uint8_t my_pubkey[32]; 61 | uint8_t their_pubkey[32]; 62 | } __attribute__((packed)) packet; 63 | struct { 64 | uint32_t ip; 65 | uint16_t port; 66 | } __attribute__((packed)) reply; 67 | struct entry *entry; 68 | socklen_t len; 69 | ssize_t retlen; 70 | int optval; 71 | int sock = socket(AF_INET, SOCK_DGRAM, 0); 72 | if (sock < 0) { 73 | perror("socket"); 74 | return errno; 75 | } 76 | 77 | optval = 1; 78 | if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0) { 79 | perror("setsockopt"); 80 | return errno; 81 | } 82 | 83 | if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { 84 | perror("bind"); 85 | return errno; 86 | } 87 | 88 | for (;;) { 89 | len = sizeof(addr); 90 | if (recvfrom(sock, &packet, sizeof(packet), 0, (struct sockaddr *)&addr, &len) != sizeof(packet)) { 91 | perror("recvfrom"); 92 | continue; 93 | } 94 | entry = find_or_insert_entry(packet.my_pubkey); 95 | entry->ip = addr.sin_addr.s_addr; 96 | entry->port = addr.sin_port; 97 | entry = find_entry(packet.their_pubkey); 98 | if (entry) { 99 | reply.ip = entry->ip; 100 | reply.port = entry->port; 101 | if (sendto(sock, &reply, sizeof(reply), 0, (struct sockaddr *)&addr, len) < 0) { 102 | perror("sendto"); 103 | continue; 104 | } 105 | } else { 106 | if (sendto(sock, NULL, 0, 0, (struct sockaddr *)&addr, len) < 0) { 107 | perror("sendto"); 108 | continue; 109 | } 110 | } 111 | } 112 | 113 | close(sock); 114 | return 0; 115 | } 116 | -------------------------------------------------------------------------------- /contrib/ncat-client-server/README: -------------------------------------------------------------------------------- 1 | === IMPORTANT NOTE === 2 | 3 | Do not use these scripts in production. They are simply a 4 | demonstration of how easy the `wg(8)` tool is at the command 5 | line, but by no means should you actually attempt to use 6 | these. They are horribly insecure and defeat the purpose 7 | of WireGuard. 8 | STAY AWAY! 9 | 10 | That all said, this is a pretty cool example of just how 11 | darn easy WireGuard can be. 12 | 13 | Disclaimer: 14 | The `demo.wireguard.com` server in client.sh is for testing 15 | purposes only. You may not use this server for abusive or 16 | illegal purposes. 17 | -------------------------------------------------------------------------------- /contrib/ncat-client-server/client-quick.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # SPDX-License-Identifier: GPL-2.0 3 | # 4 | # Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. 5 | 6 | set -e 7 | 8 | echo "[!] Warning: This server is for testing purposes only. You may not use this server for abusive or illegal purposes." 9 | 10 | echo "[+] Generating private key." 11 | privatekey="$(wg genkey)" 12 | 13 | echo "[+] Sending public key to server." 14 | exec 7<>/dev/tcp/demo.wireguard.com/42912 15 | wg pubkey <<<"$privatekey" >&7 16 | 17 | echo "[+] Parsing server response." 18 | IFS=: read -r status server_pubkey server_port internal_ip <&7 19 | [[ $status == OK ]] || exit 1 20 | 21 | echo "[+] Writing config file." 22 | [[ $UID -eq 0 ]] || sudo=sudo 23 | $sudo sh -c 'umask 077; mkdir -p /etc/wireguard; cat > /etc/wireguard/demo.conf' <<_EOF 24 | [Interface] 25 | PrivateKey = $privatekey 26 | Address = $internal_ip/24 27 | DNS = 8.8.8.8, 8.8.4.4, 1.1.1.1, 1.0.0.1 28 | 29 | [Peer] 30 | PublicKey = $server_pubkey 31 | Endpoint = demo.wireguard.com:$server_port 32 | AllowedIPs = 0.0.0.0/0 33 | _EOF 34 | 35 | echo "[+] Success. Run \`wg-quick up demo\` to turn on the tunnel to the demo server and \`wg-quick down demo\` to turn it off." 36 | -------------------------------------------------------------------------------- /contrib/ncat-client-server/client.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # SPDX-License-Identifier: GPL-2.0 3 | # 4 | # Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. 5 | 6 | set -e 7 | [[ $UID == 0 ]] || { echo "You must be root to run this."; exit 1; } 8 | exec 3<>/dev/tcp/demo.wireguard.com/42912 9 | privatekey="$(wg genkey)" 10 | wg pubkey <<<"$privatekey" >&3 11 | IFS=: read -r status server_pubkey server_port internal_ip <&3 12 | [[ $status == OK ]] 13 | ip link del dev wg0 2>/dev/null || true 14 | ip link add dev wg0 type wireguard 15 | wg set wg0 private-key <(echo "$privatekey") peer "$server_pubkey" allowed-ips 0.0.0.0/0 endpoint "demo.wireguard.com:$server_port" persistent-keepalive 25 16 | ip address add "$internal_ip"/24 dev wg0 17 | ip link set up dev wg0 18 | if [ "$1" == "default-route" ]; then 19 | host="$(wg show wg0 endpoints | sed -n 's/.*\t\(.*\):.*/\1/p')" 20 | ip route add $(ip route get $host | sed '/ via [0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}/{s/^\(.* via [0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\).*/\1/}' | head -n 1) 2>/dev/null || true 21 | ip route add 0/1 dev wg0 22 | ip route add 128/1 dev wg0 23 | fi 24 | -------------------------------------------------------------------------------- /contrib/ncat-client-server/server.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # SPDX-License-Identifier: GPL-2.0 3 | # 4 | # Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. 5 | 6 | if [[ -z $NCAT_REMOTE_ADDR ]]; then 7 | ip link del dev wg0 2>/dev/null 8 | set -e 9 | ip link add dev wg0 type wireguard 10 | ip address add 192.168.4.1/24 dev wg0 11 | wg set wg0 private-key <(wg genkey) listen-port 12912 12 | ip link set up dev wg0 13 | exec ncat -e "$(readlink -f "$0")" -k -l -p 42912 -v 14 | fi 15 | read -r public_key 16 | [[ $(wg show wg0 peers | wc -l) -ge 253 ]] && wg set wg0 peer $(wg show wg0 latest-handshakes | sort -k 2 -b -n | head -n 1 | cut -f 1) remove 17 | next_ip=$(all="$(wg show wg0 allowed-ips)"; for ((i=2; i<=254; i++)); do ip="192.168.4.$i"; [[ $all != *$ip/32* ]] && echo $ip && break; done) 18 | wg set wg0 peer "$public_key" allowed-ips $next_ip/32 2>/dev/null && echo "OK:$(wg show wg0 private-key | wg pubkey):$(wg show wg0 listen-port):$next_ip" || echo ERROR 19 | -------------------------------------------------------------------------------- /contrib/peer-approver/README: -------------------------------------------------------------------------------- 1 | === Dynamic peers authentication example === 2 | 3 | This example shows how to utilize netlink's multicast notifications 4 | in AmneziaWG kernel module to provide dynamic peer authentication. 5 | 6 | To compile it, you must install some pre-requisites: 7 | 8 | ```shell 9 | apt-get install build-essential pkg-config libnl-3-dev libnl-genl-3-dev 10 | ``` 11 | 12 | After that, build example with the following command: 13 | 14 | ```shell 15 | gcc notification-listener.c $(pkg-config --cflags --libs libnl-3.0 libnl-genl-3.0) -o notification-listener 16 | ``` 17 | 18 | Bring up AWG interface with `awg-quick` as usually, edit `accounts.csv` file accordingly to your needs and then run: 19 | 20 | ```shell 21 | sudo ./notification-listener ./approve.sh ./accounts.csv 22 | ``` 23 | 24 | ### **PLEASE NOTE: THIS EXAMPLE AS WELL AS OVERALL DYNAMIC AUTHENTICATION MECHANISM AND LEGACY CLIENTS' SUPPORT IN AMNEZIAWG IS SPONSORED BY [WINDSCRIBE LIMITED](https://windscribe.com)** -------------------------------------------------------------------------------- /contrib/peer-approver/accounts.csv: -------------------------------------------------------------------------------- 1 | Public Key,Allowed Ips,PSK 2 | /Ca5004uiLJVBqSPaBUKg5zBszO9qbzEUCWmVkelkjY=,"10.8.1.10/32",E37VXqGtGvwftop/uFsbZcIO76Ox1kMmB6Sz/JoIw2I= -------------------------------------------------------------------------------- /contrib/peer-approver/approve.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ACCOUNTS_FILE=$1 4 | INTERFACE_NAME=$2 5 | PUBLIC_KEY=$3 6 | ENDPOINT=$4 7 | ADVANCED_SECURITY=$5 8 | 9 | ACCOUNT_STR=`grep "${PUBLIC_KEY}" "${ACCOUNTS_FILE}"` 10 | 11 | if [ "${ACCOUNT_STR}" == "" ]; then 12 | echo "Public key not found in accounts file!" 13 | exit 255 14 | fi 15 | 16 | ACCOUNT=(${ACCOUNT_STR//,/ }) 17 | ALLOWED_IPS=$(echo ${ACCOUNT[1]}|tr -d '"') 18 | PSK=$(echo ${ACCOUNT[2]}|tr -d '"') 19 | PSK_FILE=$(tempfile) 20 | echo "${PSK}" > "${PSK_FILE}" 21 | 22 | awg set "${INTERFACE_NAME}" peer "${PUBLIC_KEY}" allowed-ips "${ALLOWED_IPS}" endpoint "${ENDPOINT}" allowed-ips "${ALLOWED_IPS}" preshared-key "${PSK_FILE}" advanced-security "${ADVANCED_SECURITY}" 23 | EXIT_CODE=$? 24 | 25 | rm -f "{$PSK_FILE}" 26 | exit ${EXIT_CODE} 27 | -------------------------------------------------------------------------------- /contrib/peer-approver/notification-listener.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "../../src/uapi/linux/linux/wireguard.h" 18 | 19 | #define prerr(...) fprintf(stderr, "Error: " __VA_ARGS__) 20 | 21 | #define WG_KEY_LEN 32 22 | #define WG_KEY_LEN_BASE64 ((((WG_KEY_LEN) + 2) / 3) * 4 + 1) 23 | 24 | static struct nl_sock *sk = NULL; 25 | static char **cb_argv; 26 | static int cb_argc; 27 | 28 | static int cleanup_and_exit(int ret) 29 | { 30 | if (sk != NULL) 31 | nl_socket_free(sk); 32 | exit(ret); 33 | } 34 | 35 | static void signal_handler(int sig) 36 | { 37 | cleanup_and_exit(EXIT_SUCCESS); 38 | } 39 | 40 | static inline void encode_base64(char dest[static 4], const uint8_t src[static 3]) 41 | { 42 | const uint8_t input[] = { (src[0] >> 2) & 63, ((src[0] << 4) | (src[1] >> 4)) & 63, ((src[1] << 2) | (src[2] >> 6)) & 63, src[2] & 63 }; 43 | 44 | for (unsigned int i = 0; i < 4; ++i) 45 | dest[i] = input[i] + 'A' 46 | + (((25 - input[i]) >> 8) & 6) 47 | - (((51 - input[i]) >> 8) & 75) 48 | - (((61 - input[i]) >> 8) & 15) 49 | + (((62 - input[i]) >> 8) & 3); 50 | 51 | } 52 | 53 | void key_to_base64(char base64[static WG_KEY_LEN_BASE64], const uint8_t key[static WG_KEY_LEN]) 54 | { 55 | unsigned int i; 56 | 57 | for (i = 0; i < WG_KEY_LEN / 3; ++i) 58 | encode_base64(&base64[i * 4], &key[i * 3]); 59 | encode_base64(&base64[i * 4], (const uint8_t[]){ key[i * 3 + 0], key[i * 3 + 1], 0 }); 60 | base64[WG_KEY_LEN_BASE64 - 2] = '='; 61 | base64[WG_KEY_LEN_BASE64 - 1] = '\0'; 62 | } 63 | 64 | static char *key(const uint8_t key[static WG_KEY_LEN]) 65 | { 66 | static char base64[WG_KEY_LEN_BASE64]; 67 | 68 | key_to_base64(base64, key); 69 | return base64; 70 | } 71 | 72 | static char *endpoint(const struct sockaddr *addr) 73 | { 74 | char host[4096 + 1]; 75 | char service[512 + 1]; 76 | static char buf[sizeof(host) + sizeof(service) + 4]; 77 | int ret; 78 | socklen_t addr_len = 0; 79 | 80 | memset(buf, 0, sizeof(buf)); 81 | if (addr->sa_family == AF_INET) 82 | addr_len = sizeof(struct sockaddr_in); 83 | else if (addr->sa_family == AF_INET6) 84 | addr_len = sizeof(struct sockaddr_in6); 85 | 86 | ret = getnameinfo(addr, addr_len, host, sizeof(host), service, sizeof(service), NI_DGRAM | NI_NUMERICSERV | NI_NUMERICHOST); 87 | if (ret) { 88 | strncpy(buf, gai_strerror(ret), sizeof(buf) - 1); 89 | buf[sizeof(buf) - 1] = '\0'; 90 | } else 91 | snprintf(buf, sizeof(buf), (addr->sa_family == AF_INET6 && strchr(host, ':')) ? "[%s]:%s" : "%s:%s", host, service); 92 | return buf; 93 | } 94 | 95 | static int get_ifname(struct nlattr *tb[], char **ifname) 96 | { 97 | if (tb[WGDEVICE_A_IFNAME] == NULL) 98 | return -1; 99 | *ifname = nla_data(tb[WGDEVICE_A_IFNAME]); 100 | return 0; 101 | } 102 | 103 | static int get_pubkey(struct nlattr *peer[], char **pubkey) 104 | { 105 | if (peer[WGPEER_A_PUBLIC_KEY] == NULL) 106 | return -1; 107 | *pubkey = key(nla_data(peer[WGPEER_A_PUBLIC_KEY])); 108 | return 0; 109 | } 110 | 111 | static int get_endpoint(struct nlattr *peer[], char **endpoint_ip) 112 | { 113 | if (peer[WGPEER_A_ENDPOINT] == NULL) 114 | return -1; 115 | *endpoint_ip = endpoint(nla_data(peer[WGPEER_A_ENDPOINT])); 116 | return 0; 117 | } 118 | 119 | static int run_callback(char *ifname, char *pubkey, char *endpoint_ip, bool advanced_security) 120 | { 121 | char** new_argv = malloc((cb_argc + 2) * sizeof *new_argv); 122 | 123 | new_argv[0] = cb_argv[1]; 124 | for (int i = 2; i < cb_argc - 3; i++) { 125 | new_argv[i - 1] = cb_argv[i]; 126 | } 127 | new_argv[cb_argc - 4] = ifname; 128 | new_argv[cb_argc - 3] = pubkey; 129 | new_argv[cb_argc - 2] = endpoint_ip; 130 | new_argv[cb_argc - 1] = (advanced_security ? "on\0" : "off\0"); 131 | new_argv[cb_argc] = NULL; 132 | 133 | int child_pid = fork(), ret; 134 | if (child_pid < 0) { 135 | prerr("failed to spawn child process: %d\n", child_pid); 136 | return child_pid; 137 | } else if (child_pid == 0) { 138 | execv(cb_argv[1], new_argv); 139 | exit(0); 140 | } else { 141 | waitpid(child_pid, &ret, 0); 142 | } 143 | 144 | free(new_argv); 145 | return ret; 146 | } 147 | 148 | static int netlink_callback(struct nl_msg *msg, void *arg) 149 | { 150 | struct nlmsghdr *ret_hdr = nlmsg_hdr(msg); 151 | struct genlmsghdr *gnlh = nlmsg_data(ret_hdr); 152 | struct nlattr *tb[WGDEVICE_A_MAX + 1]; 153 | struct nlattr *peer[WGPEER_A_MAX + 1]; 154 | 155 | nla_parse(tb, WGDEVICE_A_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); 156 | 157 | char *ifname, *pubkey, *endpoint_ip; 158 | bool advanced_security = false; 159 | int cb_ret; 160 | 161 | switch (gnlh->cmd) { 162 | case WG_CMD_UNKNOWN_PEER: 163 | if (get_ifname(tb, &ifname) < 0) { 164 | prerr("unknown interface name!\n"); 165 | return NL_SKIP; 166 | } 167 | if (nla_parse_nested(peer, WGPEER_A_MAX, tb[WGDEVICE_A_PEER], NULL)) { 168 | prerr("failed to parse nested peer!\n"); 169 | return NL_SKIP; 170 | } 171 | if (get_pubkey(peer, &pubkey)) { 172 | prerr("invalid public key!\n"); 173 | return NL_SKIP; 174 | } 175 | if (get_endpoint(peer, &endpoint_ip)) { 176 | prerr("invalid endpoint!\n"); 177 | return NL_SKIP; 178 | } 179 | if (nla_get_flag(peer[WGPEER_A_ADVANCED_SECURITY])) { 180 | advanced_security = true; 181 | } 182 | if (cb_ret = run_callback(ifname, pubkey, endpoint_ip, advanced_security)) { 183 | prerr("failed to execute callback script: %d!\n", cb_ret); 184 | return NL_SKIP; 185 | } 186 | printf("Callback executed successfully.\n"); 187 | break; 188 | default: 189 | return NL_SKIP; 190 | } 191 | 192 | return 0; 193 | } 194 | 195 | int main(int argc, char *argv[]) 196 | { 197 | int ret; 198 | int sk_fd; 199 | fd_set rfds; 200 | 201 | if (argc < 2) { 202 | prerr("usage: %s \n", argv[0]); 203 | cleanup_and_exit(EXIT_FAILURE); 204 | } 205 | 206 | cb_argc = argc + 3; 207 | cb_argv = argv; 208 | 209 | signal(SIGTERM, signal_handler); 210 | signal(SIGINT, signal_handler); 211 | 212 | sk = nl_socket_alloc(); 213 | if (sk == NULL) { 214 | prerr("unable to allocate Netlink socket!\n"); 215 | exit(EXIT_FAILURE); 216 | } 217 | 218 | ret = genl_connect(sk); 219 | if (ret < 0) { 220 | prerr("no connect %d!\n", ret); 221 | cleanup_and_exit(EXIT_FAILURE); 222 | } 223 | 224 | printf("Netlink socket connected.\n"); 225 | 226 | ret = genl_ctrl_resolve_grp(sk, WG_GENL_NAME, WG_MULTICAST_GROUP_AUTH); 227 | if (ret < 0) { 228 | prerr("auth group not found %d!\n", ret); 229 | cleanup_and_exit(EXIT_FAILURE); 230 | } 231 | 232 | ret = nl_socket_add_membership(sk, ret); 233 | if (ret < 0) { 234 | prerr("unable to join multicast group %d!\n", ret); 235 | cleanup_and_exit(EXIT_FAILURE); 236 | } 237 | 238 | nl_socket_disable_seq_check(sk); 239 | ret = nl_socket_modify_cb(sk, NL_CB_VALID, NL_CB_CUSTOM, netlink_callback, NULL); 240 | if (ret < 0) { 241 | prerr("unable to register callback %d!\n", ret); 242 | cleanup_and_exit(EXIT_FAILURE); 243 | } 244 | 245 | while (1) { 246 | FD_ZERO(&rfds); 247 | 248 | sk_fd = nl_socket_get_fd(sk); 249 | FD_SET(sk_fd, &rfds); 250 | 251 | ret = select(sk_fd + 1, &rfds, NULL, NULL, NULL); 252 | if (ret < 0) 253 | break; 254 | 255 | ret = nl_recvmsgs_default(sk); 256 | if (ret < 0) { 257 | prerr("error receiving message %d!\n", ret); 258 | cleanup_and_exit(EXIT_FAILURE); 259 | } 260 | } 261 | 262 | cleanup_and_exit(EXIT_FAILURE); 263 | } -------------------------------------------------------------------------------- /contrib/reresolve-dns/README: -------------------------------------------------------------------------------- 1 | reresolve-dns 2 | ============= 3 | 4 | Run this script from cron every thirty seconds or so, and it will ensure 5 | that if, when using a dynamic DNS service, the DNS entry for a hosts 6 | changes, the kernel will get the update to the DNS entry. 7 | 8 | This works by parsing configuration files, and simply running: 9 | $ wg set wg0 peer ... endpoint ... 10 | -------------------------------------------------------------------------------- /contrib/reresolve-dns/reresolve-dns.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # SPDX-License-Identifier: GPL-2.0 3 | # 4 | # Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. 5 | 6 | set -e 7 | shopt -s nocasematch 8 | shopt -s extglob 9 | export LC_ALL=C 10 | 11 | CONFIG_FILE="$1" 12 | [[ $CONFIG_FILE =~ ^[a-zA-Z0-9_=+.-]{1,15}$ ]] && CONFIG_FILE="/etc/wireguard/$CONFIG_FILE.conf" 13 | [[ $CONFIG_FILE =~ /?([a-zA-Z0-9_=+.-]{1,15})\.conf$ ]] 14 | INTERFACE="${BASH_REMATCH[1]}" 15 | 16 | process_peer() { 17 | [[ $PEER_SECTION -ne 1 || -z $PUBLIC_KEY || -z $ENDPOINT ]] && return 0 18 | [[ $(wg show "$INTERFACE" latest-handshakes) =~ ${PUBLIC_KEY//+/\\+}\ ([0-9]+) ]] || return 0 19 | (( ($EPOCHSECONDS - ${BASH_REMATCH[1]}) > 135 )) || return 0 20 | wg set "$INTERFACE" peer "$PUBLIC_KEY" endpoint "$ENDPOINT" 21 | reset_peer_section 22 | } 23 | 24 | reset_peer_section() { 25 | PEER_SECTION=0 26 | PUBLIC_KEY="" 27 | ENDPOINT="" 28 | } 29 | 30 | reset_peer_section 31 | while read -r line || [[ -n $line ]]; do 32 | stripped="${line%%\#*}" 33 | key="${stripped%%=*}"; key="${key##*([[:space:]])}"; key="${key%%*([[:space:]])}" 34 | value="${stripped#*=}"; value="${value##*([[:space:]])}"; value="${value%%*([[:space:]])}" 35 | [[ $key == "["* ]] && { process_peer; reset_peer_section; } 36 | [[ $key == "[Peer]" ]] && PEER_SECTION=1 37 | if [[ $PEER_SECTION -eq 1 ]]; then 38 | case "$key" in 39 | PublicKey) PUBLIC_KEY="$value"; continue ;; 40 | Endpoint) ENDPOINT="$value"; continue ;; 41 | esac 42 | fi 43 | done < "$CONFIG_FILE" 44 | process_peer 45 | -------------------------------------------------------------------------------- /contrib/sticky-sockets/README: -------------------------------------------------------------------------------- 1 | Sticky Sockets 2 | ============== 3 | 4 | This is a small userspace mini-library that implements as close to 5 | possible how the kernel does its sticky src address sending. 6 | -------------------------------------------------------------------------------- /contrib/synergy/README: -------------------------------------------------------------------------------- 1 | These scripts should be modified according to your precise setup. 2 | They provide a very simple way of tunneling synergy inside of a 3 | WireGuard tunnel, to protect your data in transit. 4 | -------------------------------------------------------------------------------- /contrib/synergy/synergy-client.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # SPDX-License-Identifier: GPL-2.0 3 | # 4 | # Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. 5 | 6 | set -ex 7 | if [[ $UID == 0 ]]; then 8 | ip link del dev synergy || true 9 | ip link add dev synergy type wireguard 10 | ip address add 10.193.125.39/32 peer 10.193.125.38/32 dev synergy 11 | wg set synergy \ 12 | listen-port 29184 \ 13 | private-key <(echo oNcsXA5Ma56q9xHmvvKuzLfwXYy7Uqy+bTmmXg/XtVs=) \ 14 | peer m321UMZXoJ6qw8Jli2spbAVBc2MdOzV/EHDKfZQy0g0= \ 15 | allowed-ips 10.193.125.38/32 \ 16 | endpoint 10.10.10.100:29184 17 | ip link set up dev synergy 18 | else 19 | sudo "$(readlink -f "$0")" 20 | killall synergyc || true 21 | synergyc 10.193.125.38:38382 22 | fi 23 | -------------------------------------------------------------------------------- /contrib/synergy/synergy-server.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # SPDX-License-Identifier: GPL-2.0 3 | # 4 | # Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. 5 | 6 | set -ex 7 | if [[ $UID == 0 ]]; then 8 | ip link del dev synergy || true 9 | ip link add dev synergy type wireguard 10 | ip address add 10.193.125.38/32 peer 10.193.125.39/32 dev synergy 11 | wg set synergy \ 12 | listen-port 29184 \ 13 | private-key <(echo 2InSrlZA5eQfI/MvnvPieqNTBo9cd+udc3SOO9yFpXo=) \ 14 | peer CBnoidQLjlbRsrqrI56WQbANWwkll41w/rVUIW9zISI= \ 15 | allowed-ips 10.193.125.39/32 16 | ip link set up dev synergy 17 | else 18 | sudo "$(readlink -f "$0")" 19 | killall synergys || true 20 | synergys -a 10.193.125.38:38382 21 | fi 22 | -------------------------------------------------------------------------------- /debian/NEWS: -------------------------------------------------------------------------------- 1 | amneziawg (1.0.20200206-2) unstable; urgency=medium 2 | 3 | As of wireguard-linux-compat and wireguard-dkms version 0.0.20200215-2, 4 | we no longer treat installation of the wireguard metapackage as a reason 5 | to try to reload the kernel module upon upgrade. 6 | 7 | See the discussion on 8 | https://salsa.debian.org/debian/wireguard-linux-compat/merge_requests/2 9 | for more details. 10 | 11 | -- Daniel Kahn Gillmor Mon, 24 Feb 2020 10:08:35 -0500 12 | -------------------------------------------------------------------------------- /debian/TODO: -------------------------------------------------------------------------------- 1 | Debian Packaging work for WireGuard: 2 | 3 | * wireguard metapackage should accept a 5.6 (or later) kernel instead 4 | of a module 5 | 6 | * consider make check for wg during build: this depends on 7 | scan-build, which is part of clang, but also wants to clean the 8 | build tree first. Currently, we don't do any build-time tests. 9 | 10 | * autopkgtest: 11 | 12 | - from src/, run "make check" (depending on clang-tools and sparse) 13 | and make sure it looks plausible. 14 | 15 | - Can we add other tests? 16 | -------------------------------------------------------------------------------- /debian/amneziawg-tools.README.Debian: -------------------------------------------------------------------------------- 1 | Using the WireGuard VPN and encrypted network tunnel 2 | ==================================================== 3 | 4 | To use WireGuard, you'll need an additional kernel module which will 5 | probably be built in for Linux 5.6 and later. 6 | 7 | On debian systems with a kernel older than 5.6, you should be able to 8 | build and install the kernel module with only: 9 | 10 | apt install linux-headers-$(uname -r) wireguard-dkms 11 | 12 | Please see https://www.wireguard.com/quickstart for more details on 13 | using WireGuard. 14 | 15 | -- Daniel Kahn Gillmor , Wed, 15 Jan 2020 10:41:35 -0500 16 | 17 | -------------------------------------------------------------------------------- /debian/amneziawg-tools.examples: -------------------------------------------------------------------------------- 1 | contrib/* 2 | -------------------------------------------------------------------------------- /debian/amneziawg-tools.lintian-overrides: -------------------------------------------------------------------------------- 1 | # upstream prefers the less-user-friendly locked-down /etc/wireguard by default 2 | # to avoid leaking local system secrets. I've adjusted the debian package to follow its lead. 3 | # see also https://bugs.debian.org/902831 4 | amneziawg-tools: non-standard-dir-perm etc/amneziawg/ 0700 != 0755 5 | # wg-quick@.service is a generator, not an initscript. On systems with sysvinit, 6 | # the admin probably wants to integrate with something like /etc/network/interfaces 7 | # rather than seeing a new script in /etc/init.d/. 8 | amneziawg-tools: package-supports-alternative-init-but-no-init.d-script lib/systemd/system/awg-quick@.service 9 | -------------------------------------------------------------------------------- /debian/clean: -------------------------------------------------------------------------------- 1 | src/*.o 2 | src/*.d 3 | src/wg 4 | -------------------------------------------------------------------------------- /debian/compat: -------------------------------------------------------------------------------- 1 | 9 -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: amneziawg 2 | Section: net 3 | Priority: optional 4 | Maintainer: Ubuntu Developers 5 | XSBC-Original-Maintainer: Daniel Kahn Gillmor 6 | Uploaders: 7 | Unit 193 , 8 | Build-Depends: 9 | debhelper, 10 | pkg-config, 11 | systemd, 12 | Standards-Version: 4.6.0 13 | Homepage: https://www.wireguard.com 14 | Vcs-Git: https://github.com/amnezia-vpn/amnezia-wg-tools.git 15 | Vcs-Browser: https://github.com/amnezia-vpn/amnezia-wg-tools 16 | Rules-Requires-Root: no 17 | 18 | Package: amneziawg 19 | Architecture: all 20 | Depends: 21 | amneziawg-dkms (>= 0.0.20200121-2) | amneziawg-modules (>= 0.0.20191219), 22 | amneziawg-tools (>= ${source:Version}), 23 | ${misc:Depends}, 24 | Description: fast, modern, secure kernel VPN tunnel (metapackage) 25 | WireGuard is a novel VPN that runs inside the Linux Kernel and uses 26 | state-of-the-art cryptography (the "Noise" protocol). It aims to be 27 | faster, simpler, leaner, and more useful than IPSec, while avoiding 28 | the massive headache. It intends to be considerably more performant 29 | than OpenVPN. WireGuard is designed as a general purpose VPN for 30 | running on embedded interfaces and super computers alike, fit for 31 | many different circumstances. It runs over UDP. 32 | . 33 | This metapackage explicitly depends on both the kernel module and the 34 | userspace tooling. 35 | 36 | Package: amneziawg-tools 37 | Architecture: linux-any 38 | Depends: 39 | ${misc:Depends}, 40 | ${shlibs:Depends}, 41 | Recommends: 42 | nftables | iptables, 43 | amneziawg-modules (>= 0.0.20171001) | amneziawg-dkms (>= 0.0.20191219), 44 | Suggests: openresolv | resolvconf, 45 | Description: fast, modern, secure kernel VPN tunnel (userland utilities) 46 | WireGuard is a novel VPN that runs inside the Linux Kernel and uses 47 | state-of-the-art cryptography (the "Noise" protocol). It aims to be 48 | faster, simpler, leaner, and more useful than IPSec, while avoiding 49 | the massive headache. It intends to be considerably more performant 50 | than OpenVPN. WireGuard is designed as a general purpose VPN for 51 | running on embedded interfaces and super computers alike, fit for 52 | many different circumstances. It runs over UDP. 53 | . 54 | This package contains command-line tools to interact with the 55 | WireGuard kernel module. Currently, it provides only a single tool: 56 | . 57 | awg: set and retrieve configuration of WireGuard interfaces 58 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: WireGuard command-line tools 3 | Source: https://www.wireguard.com/ 4 | Upstream-Contact: WireGuard mailing list 5 | 6 | Files: * 7 | Copyright: 2015-2020 Jason A. Donenfeld 8 | License: GPL-2 9 | 10 | Files: contrib/external-tests/python/main.py 11 | Copyright: 2018 Piotr Lizonczyk 12 | License: MIT 13 | 14 | Files: src/netlink.h contrib/embeddable-wg-library/* 15 | Copyright: 2015-2020 Jason A. Donenfeld 16 | 2008-2012 Pablo Neira Ayuso 17 | License: LGPL-2.1+ 18 | 19 | Files: src/curve25519-fiat32.h 20 | Copyright: 2015-2016 The fiat-crypto Authors. 21 | 2018-2020 Jason A. Donenfeld 22 | License: GPL-2 or MIT 23 | 24 | Files: src/curve25519-hacl64.h 25 | Copyright: 2016-2017 INRIA and Microsoft Corporation. 26 | 2018-2020 Jason A. Donenfeld 27 | License: GPL-2 or MIT 28 | 29 | Files: src/uapi/openbsd/net/if_wg.h 30 | Copyright: 2020 Matt Dunwoodie 31 | 2020 Jason A. Donenfeld 32 | License: ISC 33 | 34 | Files: debian/* 35 | Copyright: 2016-2020 Daniel Kahn Gillmor 36 | License: GPL-2 37 | 38 | License: GPL-2 39 | This package is free software; you can redistribute it and/or modify 40 | it under the terms of the GNU General Public License as published by 41 | the Free Software Foundation; version 2. 42 | . 43 | On Debian systems, the complete text of the GNU General Public License 44 | version 2 can be found in file "/usr/share/common-licenses/GPL-2". 45 | 46 | License: LGPL-2.1+ 47 | This package is free software; you can redistribute it and/or modify 48 | it under the terms of the GNU Lesser General Public License as 49 | published by the Free Software Foundation; version 2.1 or later. 50 | . 51 | On Debian systems, the complete text of the GNU Lesser General Public 52 | License version 2.1 can be found in file 53 | "/usr/share/common-licenses/LGPL-2.1". 54 | 55 | License: MIT 56 | Permission is hereby granted, free of charge, to any person obtaining a copy 57 | of this software and associated documentation files (the "Software"), to deal 58 | in the Software without restriction, including without limitation the rights 59 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 60 | copies of the Software, and to permit persons to whom the Software is 61 | furnished to do so, subject to the following conditions: 62 | . 63 | The above copyright notice and this permission notice shall be included in all 64 | copies or substantial portions of the Software. 65 | . 66 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 67 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 68 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 69 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 70 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 71 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 72 | SOFTWARE. 73 | 74 | License: ISC 75 | Permission to use, copy, modify, and/or distribute this software for 76 | any purpose with or without fee is hereby granted, provided that the 77 | above copyright notice and this permission notice appear in all copies. 78 | . 79 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 80 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 81 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 82 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 83 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 84 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 85 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 86 | -------------------------------------------------------------------------------- /debian/files: -------------------------------------------------------------------------------- 1 | amneziawg-tools-dbgsym_1.0.20210914-1ubuntu2_amd64.ddeb debug optional automatic=yes 2 | amneziawg-tools_1.0.20210914-1ubuntu2_amd64.deb net optional 3 | amneziawg_1.0.20210914-1ubuntu2_all.deb net optional 4 | amneziawg_1.0.20210914-1ubuntu2_amd64.buildinfo net optional 5 | -------------------------------------------------------------------------------- /debian/gbp.conf: -------------------------------------------------------------------------------- 1 | [DEFAULT] 2 | debian-branch = debian/master 3 | upstream-tag = v%(version)s 4 | 5 | [buildpackage] 6 | compression = xz 7 | -------------------------------------------------------------------------------- /debian/patches/0001-Avoid-using-git-during-build.patch: -------------------------------------------------------------------------------- 1 | From: Daniel Kahn Gillmor 2 | Date: Wed, 15 Jan 2020 11:23:27 -0500 3 | Subject: Avoid using git during build 4 | 5 | --- 6 | src/Makefile | 4 ---- 7 | 1 file changed, 4 deletions(-) 8 | 9 | --- a/src/Makefile 2021-08-13 17:36:03.385973646 -0400 10 | +++ b/src/Makefile 2021-08-13 17:36:03.369973776 -0400 11 | @@ -48,10 +48,6 @@ 12 | ifeq ($(DEBUG),yes) 13 | CFLAGS += -g 14 | endif 15 | -WIREGUARD_TOOLS_VERSION = $(patsubst v%,%,$(shell GIT_DIR="$(PWD)/../.git" git describe --dirty 2>/dev/null)) 16 | -ifneq ($(WIREGUARD_TOOLS_VERSION),) 17 | -CFLAGS += -D'WIREGUARD_TOOLS_VERSION="$(WIREGUARD_TOOLS_VERSION)"' 18 | -endif 19 | ifeq ($(PLATFORM),freebsd) 20 | LDLIBS += -lnv 21 | endif 22 | -------------------------------------------------------------------------------- /debian/patches/0002-Avoid-requiring-glibc-2.25-for-wireguard-tools.patch: -------------------------------------------------------------------------------- 1 | From: Daniel Kahn Gillmor 2 | Date: Mon, 18 Jun 2018 14:11:10 -0400 3 | Subject: Avoid requiring glibc 2.25 for wireguard-tools 4 | 5 | Upstream's instructions (https://www.wireguard.com/install/) suggest 6 | enabling the debian unstable repository to run wireguard. 7 | 8 | Without this patch, the current version of wireguard-tools will end up 9 | with a dependency on glibc 2.25 because of the invocation of 10 | getentropy. 11 | 12 | We avoid this situation (and fall through to the syscall interface 13 | for the Linux kernel) by omitting the test here. 14 | 15 | If we move wireguard into testing (and from there to 16 | stretch-backports) then i think we can convince upstream to change 17 | their installation instructions to refer to stretch-backports, and we 18 | can remove this patch. 19 | --- 20 | src/genkey.c | 2 +- 21 | 1 file changed, 1 insertion(+), 1 deletion(-) 22 | 23 | diff --git a/src/genkey.c b/src/genkey.c 24 | index d1bb643..6cdee42 100644 25 | --- a/src/genkey.c 26 | +++ b/src/genkey.c 27 | @@ -40,7 +40,7 @@ static inline bool __attribute__((__warn_unused_result__)) get_random_bytes(uint 28 | return false; 29 | } 30 | 31 | -#if defined(__OpenBSD__) || (defined(__APPLE__) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_12) || (defined(__GLIBC__) && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 25))) 32 | +#if defined(__OpenBSD__) || (defined(__APPLE__) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_12) 33 | if (!getentropy(out, len)) 34 | return true; 35 | #endif 36 | -------------------------------------------------------------------------------- /debian/patches/series: -------------------------------------------------------------------------------- 1 | 0001-Avoid-using-git-during-build.patch 2 | 0002-Avoid-requiring-glibc-2.25-for-wireguard-tools.patch 3 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | 3 | include /usr/share/dpkg/default.mk 4 | 5 | export DEB_BUILD_MAINT_OPTIONS = hardening=+all 6 | export DEB_VERSION_UPSTREAM 7 | 8 | AMNEZIAWG_ARGS = WITH_BASHCOMPLETION=yes WITH_WGQUICK=yes WITH_SYSTEMDUNITS=yes V=1 9 | 10 | %: 11 | dh $@ 12 | 13 | override_dh_auto_build-arch: 14 | dh_auto_build --sourcedirectory=src -- $(AMNEZIAWG_ARGS) 15 | 16 | override_dh_auto_install-arch: 17 | $(MAKE) -C src DESTDIR=../debian/amneziawg-tools $(AMNEZIAWG_ARGS) install 18 | 19 | override_dh_fixperms: 20 | dh_fixperms -Xetc/amnezia 21 | 22 | override_dh_installexamples: 23 | dh_installexamples -Xexternal-tests 24 | 25 | define test_wg 26 | set -x; set -e; \ 27 | echo "Testing command $1" && \ 28 | a="$$(src/wg $1)" && b="$$(src/wg $1)" && \ 29 | echo "a=$$a b=$$b" && \ 30 | test -n "$$a" && \ 31 | test -n "$$b" && \ 32 | test "$$a" != "$$b" 33 | endef 34 | 35 | override_dh_auto_test: 36 | test "$$(head -c 32 /dev/zero | base64 | src/wg pubkey)" = "L+V9o0fNYkMVKNqsX7spBzD/9oSvxM/C7ZCZX1jLO3Q=" 37 | $(call test_wg,genpsk) 38 | $(call test_wg,genkey) 39 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (quilt) 2 | -------------------------------------------------------------------------------- /debian/tests/control: -------------------------------------------------------------------------------- 1 | Tests: keygen 2 | Restrictions: superficial 3 | Depends: 4 | wireguard-tools, 5 | 6 | Tests: wg-quick 7 | Restrictions: needs-root, isolation-machine, allow-stderr 8 | Depends: 9 | iproute2, 10 | @, 11 | 12 | Tests: netns-mini 13 | Restrictions: needs-root, isolation-machine 14 | Depends: 15 | iproute2, 16 | iputils-ping, 17 | @, 18 | -------------------------------------------------------------------------------- /debian/tests/keygen: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | [[ $(head -c 32 /dev/zero | base64 | wg pubkey) == "L+V9o0fNYkMVKNqsX7spBzD/9oSvxM/C7ZCZX1jLO3Q=" ]] 5 | [[ $(wg genpsk) != "$(wg genpsk)" ]] 6 | -------------------------------------------------------------------------------- /debian/tests/netns-mini: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # SPDX-License-Identifier: GPL-2.0 3 | # Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. 4 | set -e 5 | 6 | exec 3>&1 7 | netns0="wg-test-$$-0" 8 | netns1="wg-test-$$-1" 9 | netns2="wg-test-$$-2" 10 | pretty() { echo -e "\x1b[32m\x1b[1m[+] ${1:+NS$1: }${2}\x1b[0m" >&3; } 11 | pp() { pretty "" "$*"; "$@"; } 12 | n1() { pretty 1 "$*"; ip netns exec $netns1 "$@"; } 13 | n2() { pretty 2 "$*"; ip netns exec $netns2 "$@"; } 14 | ip0() { pretty 0 "ip $*"; ip -n $netns0 "$@"; } 15 | ip1() { pretty 1 "ip $*"; ip -n $netns1 "$@"; } 16 | ip2() { pretty 2 "ip $*"; ip -n $netns2 "$@"; } 17 | 18 | cleanup() { 19 | set +e 20 | exec 2>/dev/null 21 | ip0 link del dev wg0 22 | ip1 link del dev wg0 23 | ip2 link del dev wg0 24 | local to_kill="$(ip netns pids $netns0) $(ip netns pids $netns1) $(ip netns pids $netns2)" 25 | [[ -n $to_kill ]] && kill $to_kill 26 | pp ip netns del $netns1 27 | pp ip netns del $netns2 28 | pp ip netns del $netns0 29 | exit 30 | } 31 | trap cleanup EXIT 32 | 33 | ip netns del $netns0 2>/dev/null || true 34 | ip netns del $netns1 2>/dev/null || true 35 | ip netns del $netns2 2>/dev/null || true 36 | pp ip netns add $netns0 37 | pp ip netns add $netns1 38 | pp ip netns add $netns2 39 | ip0 link set up dev lo 40 | ip0 link add dev wg0 type wireguard 41 | ip0 link set wg0 netns $netns1 42 | ip0 link add dev wg0 type wireguard 43 | ip0 link set wg0 netns $netns2 44 | ip1 addr add 192.168.241.1/24 dev wg0 45 | ip2 addr add 192.168.241.2/24 dev wg0 46 | key1="$(pp wg genkey)" 47 | key2="$(pp wg genkey)" 48 | pub1="$(pp wg pubkey <<<"$key1")" 49 | pub2="$(pp wg pubkey <<<"$key2")" 50 | n1 wg set wg0 private-key <(echo "$key1") listen-port 1 peer "$pub2" allowed-ips 192.168.241.2/32 51 | n2 wg set wg0 private-key <(echo "$key2") listen-port 2 peer "$pub1" allowed-ips 192.168.241.1/32 52 | ip1 link set up dev wg0 53 | ip2 link set up dev wg0 54 | n2 wg set wg0 peer "$pub1" endpoint 127.0.0.1:1 55 | n2 ping -c 10 -f -W 1 192.168.241.1 56 | -------------------------------------------------------------------------------- /debian/tests/wg-quick: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | set -o pipefail 5 | 6 | LEFT_NS="left_ns" 7 | LEFT_GW="10.0.5.1/24" 8 | LEFT_PORT=3001 9 | LEFT_INT="10.0.1.1/24" 10 | WG_LEFT_INTERFACE="wg_left" 11 | WG_LEFT_INTERFACE_CONF="/etc/wireguard/${WG_LEFT_INTERFACE}.conf" 12 | 13 | RIGHT_NS="right_ns" 14 | RIGHT_GW="10.0.5.2/24" 15 | RIGHT_PORT=3002 16 | RIGHT_INT="10.0.1.2/24" 17 | WG_RIGHT_INTERFACE="wg_right" 18 | WG_RIGHT_INTERFACE_CONF="/etc/wireguard/${WG_RIGHT_INTERFACE}.conf" 19 | 20 | cleanup() { 21 | if [ $? -ne 0 ]; then 22 | echo "Some test failed, here is some debugging" 23 | dmesg -T | grep wireguard 24 | fi 25 | rm -f "${WG_LEFT_INTERFACE_CONF}" "${WG_RIGHT_INTERFACE_CONF}" 26 | ip netns delete "${LEFT_NS}" &>/dev/null 27 | ip netns delete "${RIGHT_NS}" &>/dev/null 28 | } 29 | 30 | trap cleanup EXIT 31 | 32 | 33 | setup() { 34 | umask 0077 35 | echo "Generating keys" 36 | LEFT_PRIVKEY="$(wg genkey)" 37 | RIGHT_PRIVKEY="$(wg genkey)" 38 | LEFT_PUBKEY="$(wg pubkey <<<"${LEFT_PRIVKEY}")" 39 | RIGHT_PUBKEY="$(wg pubkey <<<"${RIGHT_PRIVKEY}")" 40 | 41 | echo "Generating wireguard config" 42 | cat > "${WG_LEFT_INTERFACE_CONF}" <<-EOF 43 | [Interface] 44 | ListenPort = ${LEFT_PORT} 45 | PrivateKey = ${LEFT_PRIVKEY} 46 | Address = ${LEFT_GW} 47 | 48 | [Peer] 49 | PublicKey = ${RIGHT_PUBKEY} 50 | AllowedIPs = ${RIGHT_GW%%/*}/32 51 | Endpoint = ${RIGHT_INT%%/*}:${RIGHT_PORT} 52 | EOF 53 | 54 | cat > "${WG_RIGHT_INTERFACE_CONF}" <<-EOF 55 | [Interface] 56 | ListenPort = ${RIGHT_PORT} 57 | PrivateKey = ${RIGHT_PRIVKEY} 58 | Address = ${RIGHT_GW} 59 | 60 | [Peer] 61 | PublicKey = ${LEFT_PUBKEY} 62 | AllowedIPs = ${LEFT_GW%%/*}/32 63 | Endpoint = ${LEFT_INT%%/*}:${LEFT_PORT} 64 | EOF 65 | 66 | echo "Cleaning up old namespaces" 67 | ip netns delete "${LEFT_NS}" &> /dev/null || true 68 | ip netns delete "${RIGHT_NS}" &> /dev/null || true 69 | 70 | echo "Creating new namespaces ${LEFT_NS} and ${RIGHT_NS} and adding loopback interface to them" 71 | ip netns add "${LEFT_NS}" 72 | ip netns exec "${LEFT_NS}" ip link set dev lo up 73 | 74 | ip netns add "${RIGHT_NS}" 75 | ip netns exec "${RIGHT_NS}" ip link set dev lo up 76 | 77 | echo "Creating veth interface connecting both namespaces" 78 | ip link add p1 netns "${LEFT_NS}" type veth peer p2 netns "${RIGHT_NS}" 79 | ip -n "${LEFT_NS}" addr add "${LEFT_INT}" dev p1 80 | ip -n "${LEFT_NS}" link set p1 up 81 | 82 | ip -n "${RIGHT_NS}" addr add "${RIGHT_INT}" dev p2 83 | ip -n "${RIGHT_NS}" link set p2 up 84 | 85 | echo "Bringing up LEFT wireguard interface in namespace ${LEFT_NS}" 86 | ip netns exec "${LEFT_NS}" wg-quick up "${WG_LEFT_INTERFACE}" 87 | 88 | echo "Bringing up RIGHT wireguard interface in namespace ${RIGHT_NS}" 89 | ip netns exec "${RIGHT_NS}" wg-quick up "${WG_RIGHT_INTERFACE}" 90 | } 91 | 92 | show_config() { 93 | echo "${LEFT_NS} namespace:" 94 | ip netns exec "${LEFT_NS}" wg showconf "${WG_LEFT_INTERFACE}" 95 | echo 96 | echo "${RIGHT_NS} namespace:" 97 | ip netns exec "${RIGHT_NS}" wg showconf "${WG_RIGHT_INTERFACE}" 98 | } 99 | 100 | test_stats() { 101 | local -i ret 102 | local output="" 103 | # to be run after the ping tests 104 | # by now, we MUST have "transfer" and "last handshake" 105 | for ns in "${LEFT_NS}" "${RIGHT_NS}"; do 106 | echo "Namespace ${ns}" 107 | output=$(ip netns exec "${ns}" wg show) 108 | echo "${output}" | grep -E "latest handshake:" || { 109 | ret=$? 110 | echo "Missing \"latest handshake\" from stats in namespace ${ns}" 111 | echo "Got this output:" 112 | echo "${output}" 113 | return $ret 114 | } 115 | echo "${output}" | grep -E "transfer:.*received.*sent" || { 116 | ret=$? 117 | echo "Missing \"transfer\" stats in namespace ${ns}" 118 | echo "Got this output:" 119 | echo "${output}" 120 | return $ret 121 | } 122 | done 123 | } 124 | 125 | test_gw_ping() { 126 | echo "Pinging right gateway, from ${LEFT_NS} namespace" 127 | ip netns exec "${LEFT_NS}" ping -W 2 -c 1 "${RIGHT_GW%%/*}" || return $? 128 | echo 129 | echo "Pinging left gateway, from ${RIGHT_NS} namespace" 130 | ip netns exec "${RIGHT_NS}" ping -W 2 -c 1 "${LEFT_GW%%/*}" || return $? 131 | } 132 | 133 | test_wireguard_ping() { 134 | echo "Pinging right wireguard IP from ${LEFT_NS} namespace" 135 | ip netns exec "${LEFT_NS}" ping -W 2 -c 1 "${RIGHT_INT%%/*}" || return $? 136 | echo 137 | echo "Pinging left wireguard IP from ${RIGHT_NS} namesapce" 138 | ip netns exec "${RIGHT_NS}" ping -W 2 -c 1 "${LEFT_INT%%/*}" || return $? 139 | } 140 | 141 | 142 | echo "Setting things up" 143 | setup || { 144 | echo "Failed vpn test setup" 145 | exit 1 146 | } 147 | 148 | echo 149 | echo "This is the config" 150 | show_config 151 | 152 | echo 153 | echo "Testing gateway ping" 154 | test_gw_ping || { 155 | echo "Failed gateway ping" 156 | exit 1 157 | } 158 | 159 | echo 160 | echo "Testing wireguard interface ping" 161 | test_wireguard_ping || { 162 | echo "Failed wireguard interface ping" 163 | exit 1 164 | } 165 | 166 | echo 167 | echo "Testing vpn stats" 168 | test_stats || { 169 | echo "Failed to verify vpn stats" 170 | exit 1 171 | } 172 | -------------------------------------------------------------------------------- /debian/upstream/signing-key.asc: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP PUBLIC KEY BLOCK----- 2 | 3 | mQINBE0yIvABEADc7Zn9kj02BBd1JQMtyidTHLbY2JnmMlGFx2pwoBCuw3ObDo3G 4 | s7GDxhiK7KoupaUbPklp8NSk914kusr0IOfCcis6lINOKEt3v31yJOpZzWxa0Wha 5 | DXUDWVJc4XhKSdg1LeNtFpLIl1CuOtmaMdOaH8lpKkr/5sL4FeG0g/a3R3ZOzOOj 6 | zoGAx79pyhp92L/qo5FfATTzmD2Pq9m6rxcftiO312gpT7ztKlWvsDmc4iJyyL54 7 | 1m57zSkG5aJqFexwW3C/iJOCSAiY/r6QTmqkbVA2BSSPANOXX9v0A1GX1rcayywR 8 | w7qZelYpaH6pBLUioI2mmnO432kxK2UKrxd3+1wz1G+fg3GWjaabruVnR+KBV4uJ 9 | Q140o6oj/r9k1Busl6elyDeFAdmO6D6i9Pj90oXJIEk0/wxf+DPmag8lVmxEH1CL 10 | ST0M1t68sR38VhNSeyTdcwnsW20D1Ag55aGk3ZN5eXDIw3ccGzFPBfV3w5wfGwWz 11 | idMmAQMxAmhnQCqwSjIXzv1Gv4NeVGze+/t28zUQInTZyZmxi5niMiz0NUeLqREX 12 | bepmUJulYPeXrty/6/7N6jkakXp7kNGIK2Zigadca/18x9H5DgEXHl0eW7ZOgSUH 13 | qQmmnvNNrArhYXlckYapIAZhwLJDGgv3ZhRyYRCEQ9BktVcE0+2R6zb3GQARAQAB 14 | tCRKYXNvbiBBLiBEb25lbmZlbGQgPEphc29uQHp4MmM0LmNvbT6JAlUEEwECAD8C 15 | GwMGCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAFiEEq5lC5tSkz8NBJiCnSfxwEqXe 16 | A64FAlp2RGIFCRLnu/IACgkQSfxwEqXeA67KJg//QxU7sM0SCN97+bu/Z7cRAffx 17 | MW+X84rRZRrZvAwkDutTSsvSaBn3dwF5VEnCB25q3px4ybjl0bJF7Klp0RVOBJZo 18 | FEo5BivXH9Ib6eCBn3EGk9g/K9ZZtJP44yF5oW/WZGjlTQdo/plYTd/8COkNA9LX 19 | z3f59ljvQaJNMOUBxnMJVqgvNufbdQehS5dbimhDn5CmBfC7mEpMbrlC7e0O43yl 20 | Tu+BuNssvrX/d3hU7zCYVDDZjVgqSY78YMpvre9xj1WdSdCx9FHo97UcGRHAlm2m 21 | IFIunvZB4s2nWafYAjZ2mg0/gEmn6pIUO+yTSziscoZOedwYDY6FRR26YDjDebF3 22 | oy5AqgfF8pYACtaKRShJjEfncJhZGKYshuZbuRtXs3yfIQi3QT2MCaCpcZ7C2llS 23 | /ire0qpq1Fp8a6tcyY/8ycr5fp7FO+SHPAQ9tnCZvej7N8guEF/LxkiekxQtkAYM 24 | Fk3Ve4ihFpfy+vT7eF4k95nd067dOwWC+wo/RZMpl5ZBxZJ1ZNzR/Uf9WQ+26KpN 25 | V3pu1cWuh4wjq9auz4MbU/sUMoC69HLDzxgPr8B0aKyU16nyy882R3Xp/SpKqhak 26 | 2l95vVi+vjz5YJ9xwoELQCGyb0HscmwNktOqNuev2tze7DpBq6SouK7mibVc9nhD 27 | s9cpv6qFWLLPG9nXC2G5Ag0ETTIi8AEQAL7FzsM9ztt7nCWg8CD8uT5JB0rwGcZT 28 | GZLKSHZAySNO1Gb0Zl0sKkp77UbBy1Unc7VAHmvfFVyUk8xuvkju/kKrLXu3xHNZ 29 | tdIu/o/db4KgDi+ty7YyOrQrmT2a8/Gv5bwAdbtnYvK3i5FMC52iYK+Jd5I4TNAR 30 | ajl+BaJXeuZ99Lf3GiBdkxVQ9B2Q9avUhwkqN0eYUHrZhGgsQ48LfFSnDS7RxBOY 31 | 3XX8/+28EqpQjDvHe34vVQXQ6xwusH1ZqD8Q9w1c+DkvS7aqa7pGMRVgcrejYp6e 32 | rcXH4G5S/tzIqcnA0wTX3570grpsSNM1lIQ43f5mcfYuSdIpE0YiHwKVlvBiHnq/ 33 | elyVZEB6Ul00SuW8FbsnrC/w6EFO1Mw1W8nFPHAM2hBuMXYgb4nwNxfFcT0X7Bt1 34 | 1dxIZo3isTRckzwkoXjwB09w1QQK5VvDh4vRS8mCEWRKq3jIZACxYq0wvCMwfbzj 35 | SpFwXlz3VEYAqOxZvbrOQ+0e8YBkrqxpygBI+Gw/yQMkh1KPUtswNyLzoQ4+Mul1 36 | fDvpAJ44wIFZDnIrr77xzfJ7h6Br0m6o5TinaF87Oxd1QsrY2ba0UfEza8atMmN/ 37 | kwcHdzW4lvJyzzGr+skvhsMdTj/qdyVmCyr/F6FoUyGrq9C+Ww3iXjSbV5g5i/Ol 38 | qurBVLPww8zdABEBAAGJAjwEGAECACYCGwwWIQSrmULm1KTPw0EmIKdJ/HASpd4D 39 | rgUCWnZEdAUJEue8BAAKCRBJ/HASpd4DrlguEACoHU3QB6p7bTY2HdEMRTpz0+pe 40 | E+OYJdB8/1JKqtW/odkTCQmodcJD1AO0jw5NGcFr0SyIzgXxrxFgCBTNGDNIoE+D 41 | gw9Nm6sXwDkeuoyEWHMGPdAYA0da8R9VHmJAG9ezMlHzggpgXMR5DTs16usIqrhG 42 | /gGEgvQWKl3snE+IDiGPLR8VwgF6g1dNEXvwCKhvnM700CBrQqbbmlL+JNVD0q8K 43 | qgtGaf1vqdoDWTalsrqfHLusRUoJOLkJcnaxJnx7mGoBQSjGLuDWlQhg+laQr/H+ 44 | pgTbOAvfTPLIMQNUJDPg4FenoG2uMlAadDEKJnsyo0jblPuaE7b9VBIFAHgrRO2/ 45 | Dgi3/3lUC/srZtPWp+yde10BgFadCgz/jbcfoD2Uq8uOJ8notSFHkqwu5UH/T0qZ 46 | Dd8H4HIgDCrIgOt2WaIriP/317xv5nqDCT/m7yKVxn/Uabu4GW5BFgLv1jUCrmNK 47 | eLGNYnN6xg1djR9hrD7Bvoi4fOU+EnrDmslXr1XZzjrn5ricsY5ezyD1lKPFXEnM 48 | X3gdVkKVzsBx0ZmhkFGiu1qZzGeWfLMDPNHmAZLN7Ovl/oEv7aVfWzzAaAy4kHPJ 49 | utFoKSYJ9MjfAiFlY9XiihNMJ2ZL8csKEGNE6AC0fiArfzh6WBMUU0xFRlVE/D8/ 50 | UqdkAOEzK8aNJzzwqg== 51 | =cPUl 52 | -----END PGP PUBLIC KEY BLOCK----- 53 | -------------------------------------------------------------------------------- /debian/watch: -------------------------------------------------------------------------------- 1 | version=4 2 | opts=mode=git,pgpmode=gittag \ 3 | https://github.com/amnezia-vpn/amnezia-wg-tools.git \ 4 | refs/tags/v?([\d\.]+) 5 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0 2 | # 3 | # Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. 4 | 5 | PKG_CONFIG ?= pkg-config 6 | PREFIX ?= /usr 7 | DESTDIR ?= 8 | SYSCONFDIR ?= /etc 9 | BINDIR ?= $(PREFIX)/bin 10 | LIBDIR ?= $(PREFIX)/lib 11 | MANDIR ?= $(PREFIX)/share/man 12 | BASHCOMPDIR ?= $(PREFIX)/share/bash-completion/completions 13 | SYSTEMDUNITDIR ?= $(shell $(PKG_CONFIG) --variable=systemdsystemunitdir systemd 2>/dev/null || echo "$(PREFIX)/lib/systemd/system") 14 | RUNSTATEDIR ?= /var/run 15 | WITH_BASHCOMPLETION ?= 16 | WITH_WGQUICK ?= 17 | WITH_SYSTEMDUNITS ?= 18 | 19 | ifeq ($(WITH_BASHCOMPLETION),) 20 | ifneq ($(strip $(wildcard $(BASHCOMPDIR))),) 21 | WITH_BASHCOMPLETION := yes 22 | endif 23 | endif 24 | ifeq ($(WITH_WGQUICK),) 25 | ifneq ($(strip $(wildcard $(BINDIR)/bash)),) 26 | WITH_WGQUICK := yes 27 | endif 28 | ifneq ($(strip $(wildcard $(DESTDIR)/bin/bash)),) 29 | WITH_WGQUICK := yes 30 | endif 31 | endif 32 | ifeq ($(WITH_SYSTEMDUNITS),) 33 | ifneq ($(strip $(wildcard $(SYSTEMDUNITDIR))),) 34 | WITH_SYSTEMDUNITS := yes 35 | endif 36 | endif 37 | 38 | PLATFORM ?= $(shell uname -s | tr '[:upper:]' '[:lower:]') 39 | 40 | CFLAGS ?= -O3 41 | 42 | ifneq ($(wildcard uapi/$(PLATFORM)/.),) 43 | CFLAGS += -I uapi/$(PLATFORM) 44 | endif 45 | CFLAGS += -std=gnu99 -D_GNU_SOURCE 46 | CFLAGS += -Wall -Wextra 47 | CFLAGS += -MMD -MP 48 | CFLAGS += -DRUNSTATEDIR="\"$(RUNSTATEDIR)\"" 49 | ifeq ($(DEBUG),yes) 50 | CFLAGS += -g 51 | endif 52 | WIREGUARD_TOOLS_VERSION = $(patsubst v%,%,$(shell GIT_DIR="$(PWD)/../.git" git describe --dirty 2>/dev/null)) 53 | ifneq ($(WIREGUARD_TOOLS_VERSION),) 54 | CFLAGS += -D'WIREGUARD_TOOLS_VERSION="$(WIREGUARD_TOOLS_VERSION)"' 55 | endif 56 | ifeq ($(PLATFORM),freebsd) 57 | LDLIBS += -lnv 58 | endif 59 | ifeq ($(PLATFORM),haiku) 60 | LDLIBS += -lnetwork -lbsd 61 | endif 62 | ifeq ($(PLATFORM),windows) 63 | CC := x86_64-w64-mingw32-clang 64 | WINDRES := $(shell $(CC) $(CFLAGS) -print-prog-name=windres 2>/dev/null) 65 | CFLAGS += -Iwincompat/include -include wincompat/compat.h -DWINVER=0x0601 -D_WIN32_WINNT=0x0601 -flto 66 | LDLIBS += -lws2_32 -lsetupapi -lole32 -ladvapi32 -lntdll -Lwincompat 67 | LDFLAGS += -flto -Wl,--dynamicbase -Wl,--nxcompat -Wl,--tsaware -mconsole 68 | LDFLAGS += -Wl,--major-os-version=6 -Wl,--minor-os-version=1 -Wl,--major-subsystem-version=6 -Wl,--minor-subsystem-version=1 69 | # The use of -Wl,/delayload: here implies we're using llvm-mingw 70 | LDFLAGS += -Wl,/delayload:ws2_32.dll -Wl,/delayload:setupapi.dll -Wl,/delayload:ole32.dll -Wl,/delayload:advapi32.dll 71 | VERSION := $(patsubst "%",%,$(filter "%",$(file < version.h))) 72 | wg: wincompat/libc.o wincompat/init.o wincompat/loader.o wincompat/resources.o 73 | wincompat/resources.o: wincompat/resources.rc wincompat/manifest.xml 74 | $(WINDRES) -DVERSION_STR=$(VERSION) -O coff -c 65001 -i $< -o $@ 75 | endif 76 | 77 | ifneq ($(V),1) 78 | BUILT_IN_LINK.o := $(LINK.o) 79 | LINK.o = @echo " LD $@"; 80 | LINK.o += $(BUILT_IN_LINK.o) 81 | BUILT_IN_COMPILE.c := $(COMPILE.c) 82 | COMPILE.c = @echo " CC $@"; 83 | COMPILE.c += $(BUILT_IN_COMPILE.c) 84 | BUILT_IN_RM := $(RM) 85 | RM := @a() { echo " CLEAN $$@"; $(BUILT_IN_RM) "$$@"; }; a 86 | WINDRES := @a() { echo " WINDRES $${@: -1}"; $(WINDRES) "$$@"; }; a 87 | endif 88 | 89 | wg: $(sort $(patsubst %.c,%.o,$(wildcard *.c))) 90 | 91 | clean: 92 | $(RM) wg *.o *.d $(wildcard wincompat/*.o wincompat/*.lib wincompat/*.dll) 93 | 94 | install: wg 95 | @install -v -d "$(DESTDIR)$(BINDIR)" && install -v -m 0755 wg "$(DESTDIR)$(BINDIR)/awg" 96 | @install -v -d "$(DESTDIR)$(MANDIR)/man8" && install -v -m 0644 man/wg.8 "$(DESTDIR)$(MANDIR)/man8/awg.8" 97 | @[ "$(WITH_BASHCOMPLETION)" = "yes" ] || exit 0; \ 98 | install -v -d "$(DESTDIR)$(BASHCOMPDIR)" && install -v -m 0644 completion/wg.bash-completion "$(DESTDIR)$(BASHCOMPDIR)/awg" 99 | @[ "$(WITH_WGQUICK)" = "yes" ] || exit 0; \ 100 | install -v -m 0755 wg-quick/$(PLATFORM).bash "$(DESTDIR)$(BINDIR)/awg-quick" && install -v -m 0700 -d "$(DESTDIR)$(SYSCONFDIR)/amnezia/amneziawg" 101 | @[ "$(WITH_WGQUICK)" = "yes" ] || exit 0; \ 102 | install -v -m 0644 man/wg-quick.8 "$(DESTDIR)$(MANDIR)/man8/awg-quick.8" 103 | @[ "$(WITH_WGQUICK)" = "yes" -a "$(WITH_BASHCOMPLETION)" = "yes" ] || exit 0; \ 104 | install -v -m 0644 completion/wg-quick.bash-completion "$(DESTDIR)$(BASHCOMPDIR)/awg-quick" 105 | @[ "$(WITH_WGQUICK)" = "yes" -a "$(WITH_SYSTEMDUNITS)" = "yes" ] || exit 0; \ 106 | install -v -d "$(DESTDIR)$(SYSTEMDUNITDIR)" && install -v -m 0644 systemd/wg-quick.target "$(DESTDIR)$(SYSTEMDUNITDIR)/awg-quick.target" && \ 107 | install -v -m 0644 systemd/wg-quick@.service "$(DESTDIR)$(SYSTEMDUNITDIR)/awg-quick@.service" 108 | 109 | check: clean 110 | scan-build --html-title=wireguard-tools -maxloop 100 --view --keep-going $(MAKE) wg 111 | 112 | all: wg 113 | .DEFAULT_GOAL: all 114 | .PHONY: clean install check 115 | 116 | -include *.d 117 | -------------------------------------------------------------------------------- /src/completion/wg-quick.bash-completion: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0 2 | # Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. 3 | 4 | _awg_quick_completion() { 5 | local p i a search_paths old_glob 6 | search_paths=( /etc/wireguard ) 7 | 8 | old_glob="$(shopt -p nullglob)" 9 | shopt -s nullglob 10 | 11 | [[ $OSTYPE == *freebsd* || $OSTYPE == *darwin* ]] && search_paths+=( /usr/local/etc/wireguard ) 12 | 13 | if [[ $COMP_CWORD -eq 1 ]]; then 14 | COMPREPLY+=( $(compgen -W "up down" -- "${COMP_WORDS[1]}") ) 15 | elif [[ $COMP_CWORD -eq 2 ]]; then 16 | if [[ ${COMP_WORDS[1]} == up ]]; then 17 | for p in "${search_paths[@]}"; do 18 | for i in "$p"/*.conf; do 19 | i="${i##*/}"; i="${i%.conf}" 20 | mapfile -t a < <(compgen -W "$i" -- "${COMP_WORDS[2]}") 21 | COMPREPLY+=( "${a[@]}" ) 22 | done 23 | done 24 | mapfile -t a < <(compgen -f -X '!*.conf' -- "${COMP_WORDS[2]}") 25 | COMPREPLY+=( "${a[@]}" ) 26 | mapfile -t a < <(compgen -d -- "${COMP_WORDS[2]}") 27 | COMPREPLY+=( "${a[@]}" ) 28 | elif [[ ${COMP_WORDS[1]} == down ]]; then 29 | if [[ $OSTYPE == *openbsd* || $OSTYPE == *darwin* ]]; then 30 | for i in /var/run/amneziawg/*.name; do 31 | i="${i##*/}"; i="${i%.name}" 32 | mapfile -t a < <(compgen -W "$i" -- "${COMP_WORDS[2]}") 33 | COMPREPLY+=( "${a[@]}" ) 34 | done 35 | else 36 | COMPREPLY+=( $(compgen -W "$(wg show interfaces)" -- "${COMP_WORDS[2]}") ) 37 | fi 38 | fi 39 | fi 40 | eval "$old_glob" 41 | } 42 | 43 | complete -o filenames -o nosort -F _awg_quick_completion awg-quick 44 | -------------------------------------------------------------------------------- /src/completion/wg.bash-completion: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0 2 | # Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. 3 | 4 | _awg_completion() { 5 | local a 6 | 7 | if [[ $COMP_CWORD -eq 1 ]]; then 8 | COMPREPLY+=( $(compgen -W "help show showconf set setconf addconf syncconf genkey genpsk pubkey" -- "${COMP_WORDS[1]}") ) 9 | return 10 | fi 11 | case "${COMP_WORDS[1]}" in 12 | genkey|genpsk|pubkey|help) return; ;; 13 | show|showconf|set|setconf|addconf|syncconf) ;; 14 | *) return; 15 | esac 16 | 17 | if [[ $COMP_CWORD -eq 2 ]]; then 18 | local extra 19 | [[ ${COMP_WORDS[1]} == show ]] && extra=" all interfaces" 20 | COMPREPLY+=( $(compgen -W "$(wg show interfaces 2>/dev/null)$extra" -- "${COMP_WORDS[2]}") ) 21 | return 22 | fi 23 | 24 | if [[ $COMP_CWORD -eq 3 && ${COMP_WORDS[1]} == show && ${COMP_WORDS[2]} != interfaces ]]; then 25 | COMPREPLY+=( $(compgen -W "public-key private-key listen-port peers preshared-keys endpoints allowed-ips fwmark latest-handshakes persistent-keepalive transfer dump" -- "${COMP_WORDS[3]}") ) 26 | return 27 | fi 28 | 29 | if [[ $COMP_CWORD -eq 3 && ( ${COMP_WORDS[1]} == setconf || ${COMP_WORDS[1]} == addconf || ${COMP_WORDS[1]} == syncconf) ]]; then 30 | compopt -o filenames 31 | mapfile -t a < <(compgen -f -- "${COMP_WORDS[3]}") 32 | COMPREPLY+=( "${a[@]}" ) 33 | return 34 | fi 35 | 36 | [[ ${COMP_WORDS[1]} == set ]] || return 37 | 38 | local has_listen_port=0 has_fwmark=0 has_private_key=0 has_preshared_key=0 has_peer=0 has_remove=0 has_endpoint=0 has_persistent_keepalive=0 has_allowed_ips=0 words=() i j 39 | for ((i=3;i/dev/null)" -- "${COMP_WORDS[COMP_CWORD]}") ) 62 | return 63 | fi 64 | 65 | for ((i=has_peer;i. All Rights Reserved. 4 | */ 5 | 6 | #ifndef CONFIG_H 7 | #define CONFIG_H 8 | 9 | #include 10 | 11 | struct wgdevice; 12 | struct wgpeer; 13 | struct wgallowedip; 14 | 15 | struct config_ctx { 16 | struct wgdevice *device; 17 | struct wgpeer *last_peer; 18 | struct wgallowedip *last_allowedip; 19 | bool is_peer_section, is_device_section; 20 | }; 21 | 22 | struct wgdevice *config_read_cmd(const char *argv[], int argc); 23 | bool config_read_init(struct config_ctx *ctx, bool append); 24 | bool config_read_line(struct config_ctx *ctx, const char *line); 25 | struct wgdevice *config_read_finish(struct config_ctx *ctx); 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /src/containers.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 OR MIT */ 2 | /* 3 | * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | #ifndef CONTAINERS_H 7 | #define CONTAINERS_H 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #if defined(__linux__) 17 | #include 18 | #elif defined(__OpenBSD__) 19 | #include 20 | #endif 21 | 22 | #ifndef WG_KEY_LEN 23 | #define WG_KEY_LEN 32 24 | #endif 25 | 26 | /* Cross platform __kernel_timespec */ 27 | struct timespec64 { 28 | int64_t tv_sec; 29 | int64_t tv_nsec; 30 | }; 31 | 32 | struct wgallowedip { 33 | uint16_t family; 34 | union { 35 | struct in_addr ip4; 36 | struct in6_addr ip6; 37 | }; 38 | uint8_t cidr; 39 | struct wgallowedip *next_allowedip; 40 | }; 41 | 42 | enum { 43 | WGPEER_REMOVE_ME = 1U << 0, 44 | WGPEER_REPLACE_ALLOWEDIPS = 1U << 1, 45 | WGPEER_HAS_PUBLIC_KEY = 1U << 2, 46 | WGPEER_HAS_PRESHARED_KEY = 1U << 3, 47 | WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL = 1U << 4, 48 | WGPEER_HAS_ADVANCED_SECURITY = 1U << 5 49 | }; 50 | 51 | struct wgpeer { 52 | uint32_t flags; 53 | 54 | uint8_t public_key[WG_KEY_LEN]; 55 | uint8_t preshared_key[WG_KEY_LEN]; 56 | 57 | union { 58 | struct sockaddr addr; 59 | struct sockaddr_in addr4; 60 | struct sockaddr_in6 addr6; 61 | } endpoint; 62 | 63 | struct timespec64 last_handshake_time; 64 | uint64_t rx_bytes, tx_bytes; 65 | uint16_t persistent_keepalive_interval; 66 | 67 | bool advanced_security; 68 | 69 | struct wgallowedip *first_allowedip, *last_allowedip; 70 | struct wgpeer *next_peer; 71 | }; 72 | 73 | enum { 74 | WGDEVICE_REPLACE_PEERS = 1U << 0, 75 | WGDEVICE_HAS_PRIVATE_KEY = 1U << 1, 76 | WGDEVICE_HAS_PUBLIC_KEY = 1U << 2, 77 | WGDEVICE_HAS_LISTEN_PORT = 1U << 3, 78 | WGDEVICE_HAS_FWMARK = 1U << 4, 79 | WGDEVICE_HAS_JC = 1U << 5, 80 | WGDEVICE_HAS_JMIN = 1U << 6, 81 | WGDEVICE_HAS_JMAX = 1U << 7, 82 | WGDEVICE_HAS_S1 = 1U << 8, 83 | WGDEVICE_HAS_S2 = 1U << 9, 84 | WGDEVICE_HAS_H1 = 1U << 10, 85 | WGDEVICE_HAS_H2 = 1U << 11, 86 | WGDEVICE_HAS_H3 = 1U << 12, 87 | WGDEVICE_HAS_H4 = 1U << 13 88 | }; 89 | 90 | struct wgdevice { 91 | char name[IFNAMSIZ]; 92 | uint32_t ifindex; 93 | 94 | uint32_t flags; 95 | 96 | uint8_t public_key[WG_KEY_LEN]; 97 | uint8_t private_key[WG_KEY_LEN]; 98 | 99 | uint32_t fwmark; 100 | uint16_t listen_port; 101 | 102 | struct wgpeer *first_peer, *last_peer; 103 | 104 | uint16_t junk_packet_count; 105 | uint16_t junk_packet_min_size; 106 | uint16_t junk_packet_max_size; 107 | uint16_t init_packet_junk_size; 108 | uint16_t response_packet_junk_size; 109 | uint32_t init_packet_magic_header; 110 | uint32_t response_packet_magic_header; 111 | uint32_t underload_packet_magic_header; 112 | uint32_t transport_packet_magic_header; 113 | }; 114 | 115 | #define for_each_wgpeer(__dev, __peer) for ((__peer) = (__dev)->first_peer; (__peer); (__peer) = (__peer)->next_peer) 116 | #define for_each_wgallowedip(__peer, __allowedip) for ((__allowedip) = (__peer)->first_allowedip; (__allowedip); (__allowedip) = (__allowedip)->next_allowedip) 117 | 118 | static inline void free_wgdevice(struct wgdevice *dev) 119 | { 120 | if (!dev) 121 | return; 122 | for (struct wgpeer *peer = dev->first_peer, *np = peer ? peer->next_peer : NULL; peer; peer = np, np = peer ? peer->next_peer : NULL) { 123 | for (struct wgallowedip *allowedip = peer->first_allowedip, *na = allowedip ? allowedip->next_allowedip : NULL; allowedip; allowedip = na, na = allowedip ? allowedip->next_allowedip : NULL) 124 | free(allowedip); 125 | free(peer); 126 | } 127 | free(dev); 128 | } 129 | 130 | #endif 131 | -------------------------------------------------------------------------------- /src/ctype.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 OR MIT */ 2 | /* 3 | * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. 4 | * 5 | * Specialized constant-time ctype.h reimplementations that aren't locale-specific. 6 | */ 7 | 8 | #ifndef CTYPE_H 9 | #define CTYPE_H 10 | 11 | #include 12 | 13 | static inline bool char_is_space(int c) 14 | { 15 | unsigned char d = c - 9; 16 | return (0x80001FU >> (d & 31)) & (1U >> (d >> 5)); 17 | } 18 | 19 | static inline bool char_is_digit(int c) 20 | { 21 | return (unsigned int)(('0' - 1 - c) & (c - ('9' + 1))) >> (sizeof(c) * 8 - 1); 22 | } 23 | 24 | static inline bool char_is_alpha(int c) 25 | { 26 | return (unsigned int)(('a' - 1 - (c | 32)) & ((c | 32) - ('z' + 1))) >> (sizeof(c) * 8 - 1); 27 | } 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /src/curve25519.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 OR MIT 2 | /* 3 | * Copyright (C) 2018-2020 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | #include "curve25519.h" 7 | 8 | #include 9 | #include 10 | 11 | #ifndef __BYTE_ORDER__ 12 | #include 13 | #if !defined(BYTE_ORDER) || !defined(BIG_ENDIAN) || !defined(LITTLE_ENDIAN) 14 | #error "Unable to determine endianness." 15 | #endif 16 | #define __BYTE_ORDER__ BYTE_ORDER 17 | #define __ORDER_BIG_ENDIAN__ BIG_ENDIAN 18 | #define __ORDER_LITTLE_ENDIAN__ LITTLE_ENDIAN 19 | #endif 20 | 21 | #ifdef __linux__ 22 | #include 23 | typedef __u64 u64; 24 | typedef __u32 u32; 25 | typedef __u8 u8; 26 | typedef __s64 s64; 27 | #else 28 | typedef uint64_t u64, __le64; 29 | typedef uint32_t u32, __le32; 30 | typedef uint8_t u8; 31 | typedef int64_t s64; 32 | #endif 33 | #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ 34 | #define le64_to_cpup(a) __builtin_bswap64(*(a)) 35 | #define le32_to_cpup(a) __builtin_bswap32(*(a)) 36 | #define cpu_to_le64(a) __builtin_bswap64(a) 37 | #else 38 | #define le64_to_cpup(a) (*(a)) 39 | #define le32_to_cpup(a) (*(a)) 40 | #define cpu_to_le64(a) (a) 41 | #endif 42 | #ifndef __unused 43 | #define __unused __attribute__((unused)) 44 | #endif 45 | #ifndef __always_inline 46 | #define __always_inline __inline __attribute__((__always_inline__)) 47 | #endif 48 | #ifndef noinline 49 | #define noinline __attribute__((noinline)) 50 | #endif 51 | #ifndef __aligned 52 | #define __aligned(x) __attribute__((aligned(x))) 53 | #endif 54 | #ifndef __force 55 | #define __force 56 | #endif 57 | 58 | static __always_inline __unused __le32 get_unaligned_le32(const u8 *a) 59 | { 60 | __le32 l; 61 | __builtin_memcpy(&l, a, sizeof(l)); 62 | return le32_to_cpup(&l); 63 | } 64 | static __always_inline __unused __le64 get_unaligned_le64(const u8 *a) 65 | { 66 | __le64 l; 67 | __builtin_memcpy(&l, a, sizeof(l)); 68 | return le64_to_cpup(&l); 69 | } 70 | static __always_inline __unused void put_unaligned_le64(u64 s, u8 *d) 71 | { 72 | __le64 l = cpu_to_le64(s); 73 | __builtin_memcpy(d, &l, sizeof(l)); 74 | } 75 | 76 | static noinline void memzero_explicit(void *s, size_t count) 77 | { 78 | memset(s, 0, count); 79 | asm volatile("": :"r"(s) : "memory"); 80 | } 81 | 82 | #ifdef __SIZEOF_INT128__ 83 | #include "curve25519-hacl64.h" 84 | #else 85 | #include "curve25519-fiat32.h" 86 | #endif 87 | 88 | void curve25519_generate_public(uint8_t pub[static CURVE25519_KEY_SIZE], const uint8_t secret[static CURVE25519_KEY_SIZE]) 89 | { 90 | static const uint8_t basepoint[CURVE25519_KEY_SIZE] __aligned(sizeof(uintptr_t)) = { 9 }; 91 | 92 | curve25519(pub, secret, basepoint); 93 | } 94 | 95 | void curve25519(uint8_t mypublic[static CURVE25519_KEY_SIZE], const uint8_t secret[static CURVE25519_KEY_SIZE], const uint8_t basepoint[static CURVE25519_KEY_SIZE]) 96 | { 97 | curve25519_generic(mypublic, secret, basepoint); 98 | } 99 | -------------------------------------------------------------------------------- /src/curve25519.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 OR MIT */ 2 | /* 3 | * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | #ifndef CURVE25519_H 7 | #define CURVE25519_H 8 | 9 | #include 10 | #include 11 | 12 | enum curve25519_lengths { 13 | CURVE25519_KEY_SIZE = 32 14 | }; 15 | 16 | void curve25519(uint8_t mypublic[static CURVE25519_KEY_SIZE], const uint8_t secret[static CURVE25519_KEY_SIZE], const uint8_t basepoint[static CURVE25519_KEY_SIZE]); 17 | void curve25519_generate_public(uint8_t pub[static CURVE25519_KEY_SIZE], const uint8_t secret[static CURVE25519_KEY_SIZE]); 18 | static inline void curve25519_clamp_secret(uint8_t secret[static CURVE25519_KEY_SIZE]) 19 | { 20 | secret[0] &= 248; 21 | secret[31] = (secret[31] & 127) | 64; 22 | } 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /src/encoding.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 OR MIT 2 | /* 3 | * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. 4 | * 5 | * This is a specialized constant-time base64/hex implementation that resists side-channel attacks. 6 | */ 7 | 8 | #include 9 | #include "encoding.h" 10 | 11 | static inline void encode_base64(char dest[static 4], const uint8_t src[static 3]) 12 | { 13 | const uint8_t input[] = { (src[0] >> 2) & 63, ((src[0] << 4) | (src[1] >> 4)) & 63, ((src[1] << 2) | (src[2] >> 6)) & 63, src[2] & 63 }; 14 | 15 | for (unsigned int i = 0; i < 4; ++i) 16 | dest[i] = input[i] + 'A' 17 | + (((25 - input[i]) >> 8) & 6) 18 | - (((51 - input[i]) >> 8) & 75) 19 | - (((61 - input[i]) >> 8) & 15) 20 | + (((62 - input[i]) >> 8) & 3); 21 | 22 | } 23 | 24 | void key_to_base64(char base64[static WG_KEY_LEN_BASE64], const uint8_t key[static WG_KEY_LEN]) 25 | { 26 | unsigned int i; 27 | 28 | for (i = 0; i < WG_KEY_LEN / 3; ++i) 29 | encode_base64(&base64[i * 4], &key[i * 3]); 30 | encode_base64(&base64[i * 4], (const uint8_t[]){ key[i * 3 + 0], key[i * 3 + 1], 0 }); 31 | base64[WG_KEY_LEN_BASE64 - 2] = '='; 32 | base64[WG_KEY_LEN_BASE64 - 1] = '\0'; 33 | } 34 | 35 | static inline int decode_base64(const char src[static 4]) 36 | { 37 | int val = 0; 38 | 39 | for (unsigned int i = 0; i < 4; ++i) 40 | val |= (-1 41 | + ((((('A' - 1) - src[i]) & (src[i] - ('Z' + 1))) >> 8) & (src[i] - 64)) 42 | + ((((('a' - 1) - src[i]) & (src[i] - ('z' + 1))) >> 8) & (src[i] - 70)) 43 | + ((((('0' - 1) - src[i]) & (src[i] - ('9' + 1))) >> 8) & (src[i] + 5)) 44 | + ((((('+' - 1) - src[i]) & (src[i] - ('+' + 1))) >> 8) & 63) 45 | + ((((('/' - 1) - src[i]) & (src[i] - ('/' + 1))) >> 8) & 64) 46 | ) << (18 - 6 * i); 47 | return val; 48 | } 49 | 50 | bool key_from_base64(uint8_t key[static WG_KEY_LEN], const char *base64) 51 | { 52 | unsigned int i; 53 | volatile uint8_t ret = 0; 54 | int val; 55 | 56 | if (strlen(base64) != WG_KEY_LEN_BASE64 - 1 || base64[WG_KEY_LEN_BASE64 - 2] != '=') 57 | return false; 58 | 59 | for (i = 0; i < WG_KEY_LEN / 3; ++i) { 60 | val = decode_base64(&base64[i * 4]); 61 | ret |= (uint32_t)val >> 31; 62 | key[i * 3 + 0] = (val >> 16) & 0xff; 63 | key[i * 3 + 1] = (val >> 8) & 0xff; 64 | key[i * 3 + 2] = val & 0xff; 65 | } 66 | val = decode_base64((const char[]){ base64[i * 4 + 0], base64[i * 4 + 1], base64[i * 4 + 2], 'A' }); 67 | ret |= ((uint32_t)val >> 31) | (val & 0xff); 68 | key[i * 3 + 0] = (val >> 16) & 0xff; 69 | key[i * 3 + 1] = (val >> 8) & 0xff; 70 | 71 | return 1 & ((ret - 1) >> 8); 72 | } 73 | 74 | void key_to_hex(char hex[static WG_KEY_LEN_HEX], const uint8_t key[static WG_KEY_LEN]) 75 | { 76 | unsigned int i; 77 | 78 | for (i = 0; i < WG_KEY_LEN; ++i) { 79 | hex[i * 2] = 87U + (key[i] >> 4) + ((((key[i] >> 4) - 10U) >> 8) & ~38U); 80 | hex[i * 2 + 1] = 87U + (key[i] & 0xf) + ((((key[i] & 0xf) - 10U) >> 8) & ~38U); 81 | } 82 | hex[i * 2] = '\0'; 83 | } 84 | 85 | bool key_from_hex(uint8_t key[static WG_KEY_LEN], const char *hex) 86 | { 87 | uint8_t c, c_acc, c_alpha0, c_alpha, c_num0, c_num, c_val; 88 | volatile uint8_t ret = 0; 89 | 90 | if (strlen(hex) != WG_KEY_LEN_HEX - 1) 91 | return false; 92 | 93 | for (unsigned int i = 0; i < WG_KEY_LEN_HEX - 1; i += 2) { 94 | c = (uint8_t)hex[i]; 95 | c_num = c ^ 48U; 96 | c_num0 = (c_num - 10U) >> 8; 97 | c_alpha = (c & ~32U) - 55U; 98 | c_alpha0 = ((c_alpha - 10U) ^ (c_alpha - 16U)) >> 8; 99 | ret |= ((c_num0 | c_alpha0) - 1) >> 8; 100 | c_val = (c_num0 & c_num) | (c_alpha0 & c_alpha); 101 | c_acc = c_val * 16U; 102 | 103 | c = (uint8_t)hex[i + 1]; 104 | c_num = c ^ 48U; 105 | c_num0 = (c_num - 10U) >> 8; 106 | c_alpha = (c & ~32U) - 55U; 107 | c_alpha0 = ((c_alpha - 10U) ^ (c_alpha - 16U)) >> 8; 108 | ret |= ((c_num0 | c_alpha0) - 1) >> 8; 109 | c_val = (c_num0 & c_num) | (c_alpha0 & c_alpha); 110 | key[i / 2] = c_acc | c_val; 111 | } 112 | 113 | return 1 & ((ret - 1) >> 8); 114 | } 115 | 116 | bool key_is_zero(const uint8_t key[static WG_KEY_LEN]) 117 | { 118 | volatile uint8_t acc = 0; 119 | 120 | for (unsigned int i = 0; i < WG_KEY_LEN; ++i) { 121 | acc |= key[i]; 122 | asm volatile("" : "=r"(acc) : "0"(acc)); 123 | } 124 | return 1 & ((acc - 1) >> 8); 125 | } 126 | -------------------------------------------------------------------------------- /src/encoding.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 OR MIT */ 2 | /* 3 | * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | #ifndef ENCODING_H 7 | #define ENCODING_H 8 | 9 | #include 10 | #include 11 | #include "containers.h" 12 | 13 | #define WG_KEY_LEN_BASE64 ((((WG_KEY_LEN) + 2) / 3) * 4 + 1) 14 | #define WG_KEY_LEN_HEX (WG_KEY_LEN * 2 + 1) 15 | 16 | void key_to_base64(char base64[static WG_KEY_LEN_BASE64], const uint8_t key[static WG_KEY_LEN]); 17 | bool key_from_base64(uint8_t key[static WG_KEY_LEN], const char *base64); 18 | 19 | void key_to_hex(char hex[static WG_KEY_LEN_HEX], const uint8_t key[static WG_KEY_LEN]); 20 | bool key_from_hex(uint8_t key[static WG_KEY_LEN], const char *hex); 21 | 22 | bool key_is_zero(const uint8_t key[static WG_KEY_LEN]); 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /src/fuzz/Makefile: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0 2 | # 3 | # Copyright (C) 2018-2020 Jason A. Donenfeld . All Rights Reserved. 4 | 5 | FUZZERS := config uapi stringlist cmd set setconf 6 | 7 | all: $(FUZZERS) 8 | 9 | CFLAGS ?= -O3 -march=native -g 10 | CFLAGS += -fsanitize=fuzzer -fsanitize=address -std=gnu11 -idirafter ../uapi -D_GNU_SOURCE 11 | CC := clang 12 | 13 | config: config.c ../config.c ../encoding.c 14 | $(CC) $(CFLAGS) -o $@ $< 15 | 16 | uapi: uapi.c ../ipc.c ../curve25519.c ../encoding.c 17 | $(CC) $(CFLAGS) -o $@ $< 18 | 19 | stringlist: stringlist.c ../ipc.c ../curve25519.c ../encoding.c 20 | $(CC) $(CFLAGS) -o $@ $< 21 | 22 | cmd: cmd.c $(wildcard ../*.c) 23 | $(CC) $(CFLAGS) -D'RUNSTATEDIR="/var/empty"' -D'main(a,b)=wg_main(a,b)' -o $@ $^ 24 | 25 | set: set.c ../set.c ../ipc.c ../encoding.c ../curve25519.c ../config.c 26 | $(CC) $(CFLAGS) -o $@ $< 27 | 28 | setconf: setconf.c ../setconf.c ../ipc.c ../encoding.c ../curve25519.c ../config.c 29 | $(CC) $(CFLAGS) -o $@ $< 30 | 31 | clean: 32 | $(RM) $(FUZZERS) 33 | 34 | .PHONY: all clean 35 | -------------------------------------------------------------------------------- /src/fuzz/cmd.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | /* 3 | * Copyright (C) 2018-2020 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | const char *__asan_default_options() 12 | { 13 | return "verbosity=1"; 14 | } 15 | 16 | int wg_main(int argc, char *argv[]); 17 | 18 | static FILE *devnull; 19 | 20 | int LLVMFuzzerTestOneInput(const char *data, size_t data_len) 21 | { 22 | char *argv[8192] = { 0 }, *args; 23 | size_t argc = 0; 24 | FILE *fake_stdin = NULL; 25 | 26 | if (!devnull) { 27 | assert((devnull = fopen("/dev/null", "r+"))); 28 | stdin = stdout = stderr = devnull; 29 | } 30 | 31 | assert((args = malloc(data_len))); 32 | memcpy(args, data, data_len); 33 | if (data_len) 34 | args[data_len - 1] = '\0'; 35 | 36 | for (const char *arg = args; argc < 8192 && arg - args < data_len; arg += strlen(arg) + 1) { 37 | if (arg[0]) 38 | assert((argv[argc++] = strdup(arg))); 39 | } 40 | if (!argc) 41 | assert((argv[argc++] = strdup("no argv[0]!"))); 42 | if (argc > 2 && (!strcmp(argv[1], "show") || !strcmp(argv[1], "showconf") || !strcmp(argv[1], "set") || !strcmp(argv[1], "setconf") || !strcmp(argv[1], "addconf") || !strcmp(argv[1], "syncconf"))) { 43 | free(argv[2]); 44 | assert((argv[2] = strdup("wg0"))); 45 | } 46 | if (argc >= 2 && !strcmp(argv[1], "pubkey")) { 47 | char *arg; 48 | size_t len; 49 | 50 | for (size_t i = 2; i < argc; ++i) 51 | free(argv[i]); 52 | argc = 2; 53 | arg = args; 54 | for (; !arg[0]; ++arg); 55 | arg += strlen(arg) + 1; 56 | for (; !arg[0]; ++arg); 57 | arg += strlen(arg) + 1; 58 | len = data_len - (arg - args); 59 | if (len <= 1) 60 | goto done; 61 | assert((fake_stdin = fmemopen(arg, len - 1, "r"))); 62 | stdin = fake_stdin; 63 | } 64 | wg_main(argc, argv); 65 | done: 66 | for (size_t i = 0; i < argc; ++i) 67 | free(argv[i]); 68 | free(args); 69 | if (fake_stdin) 70 | fclose(fake_stdin); 71 | return 0; 72 | } 73 | -------------------------------------------------------------------------------- /src/fuzz/config.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | /* 3 | * Copyright (C) 2018-2020 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | #include 7 | #undef stderr 8 | #define stderr stdin 9 | #include "../config.c" 10 | #include "../encoding.c" 11 | #undef stderr 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include "../config.h" 18 | 19 | const char *__asan_default_options() 20 | { 21 | return "verbosity=1"; 22 | } 23 | 24 | int LLVMFuzzerTestOneInput(const uint8_t *data, size_t len) 25 | { 26 | bool file; 27 | char *input; 28 | 29 | if (len < 2) 30 | return 0; 31 | file = !!(data[0] >> 7); 32 | input = malloc(len); 33 | if (!input) 34 | return 0; 35 | memcpy(input, data + 1, len - 1); 36 | input[len - 1] = '\0'; 37 | 38 | if (file) { 39 | struct config_ctx ctx; 40 | char *saveptr; 41 | 42 | config_read_init(&ctx, false); 43 | for (char *line = strtok_r(input, "\n", &saveptr); line; line = strtok_r(NULL, "\n", &saveptr)) { 44 | if (!config_read_line(&ctx, line)) 45 | config_read_init(&ctx, false); 46 | } 47 | free_wgdevice(config_read_finish(&ctx)); 48 | } else { 49 | size_t spaces = 0; 50 | char **argv, *saveptr; 51 | 52 | for (char *c = input; *c; ++c) { 53 | if (*c == ' ') 54 | ++spaces; 55 | } 56 | argv = calloc(spaces + 1, sizeof(char *)); 57 | if (!argv) 58 | goto out; 59 | spaces = 0; 60 | for (char *token = strtok_r(input, " ", &saveptr); token; token = strtok_r(NULL, " ", &saveptr)) 61 | argv[spaces++] = token; 62 | free_wgdevice(config_read_cmd(argv, spaces)); 63 | free(argv); 64 | } 65 | 66 | out: 67 | free(input); 68 | return 0; 69 | } 70 | -------------------------------------------------------------------------------- /src/fuzz/set.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | /* 3 | * Copyright (C) 2018-2020 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | #include 7 | #undef stderr 8 | #define stderr stdin 9 | #define RUNSTATEDIR "/var/empty" 10 | #include "../curve25519.c" 11 | #define parse_allowedips parse_allowedips_ipc 12 | #include "../ipc.c" 13 | #undef parse_allowedips 14 | #include "../encoding.c" 15 | static FILE *hacked_fopen(const char *pathname, const char *mode); 16 | #define fopen hacked_fopen 17 | #include "../config.c" 18 | #include "../set.c" 19 | #undef stderr 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | const char *__asan_default_options() 26 | { 27 | return "verbosity=1"; 28 | } 29 | 30 | const char *PROG_NAME = "wg"; 31 | 32 | static FILE *hacked_fopen(const char *pathname, const char *mode) 33 | { 34 | return fmemopen((char *)pathname, strlen(pathname), "r"); 35 | } 36 | 37 | int LLVMFuzzerTestOneInput(const char *data, size_t data_len) 38 | { 39 | char *argv[8192] = { "set", "wg0" }, *args; 40 | size_t argc = 2; 41 | 42 | if (!data_len) 43 | return 0; 44 | 45 | assert((args = malloc(data_len))); 46 | memcpy(args, data, data_len); 47 | args[data_len - 1] = '\0'; 48 | 49 | for (char *arg = strtok(args, " \t\n\r"); arg && argc < 8192; arg = strtok(NULL, " \t\n\r")) { 50 | if (arg[0]) 51 | argv[argc++] = arg; 52 | } 53 | set_main(argc, argv); 54 | free(args); 55 | return 0; 56 | } 57 | -------------------------------------------------------------------------------- /src/fuzz/setconf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | /* 3 | * Copyright (C) 2018-2020 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | #include 7 | #undef stderr 8 | #define stderr stdin 9 | #define RUNSTATEDIR "/var/empty" 10 | #include "../curve25519.c" 11 | #define parse_allowedips parse_allowedips_ipc 12 | #include "../ipc.c" 13 | #undef parse_allowedips 14 | #include "../encoding.c" 15 | #include "../config.c" 16 | static FILE *hacked_fopen(const char *pathname, const char *mode); 17 | #define fopen hacked_fopen 18 | #include "../setconf.c" 19 | #undef fopen 20 | #undef stderr 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | const char *__asan_default_options() 27 | { 28 | return "verbosity=1"; 29 | } 30 | 31 | const char *PROG_NAME = "wg"; 32 | 33 | struct hacked_pointers { 34 | const char *data; 35 | size_t data_len; 36 | }; 37 | 38 | static FILE *hacked_fopen(const char *pathname, const char *mode) 39 | { 40 | struct hacked_pointers *h = (struct hacked_pointers *)strtoul(pathname, NULL, 10); 41 | return fmemopen((char *)h->data, h->data_len, "r"); 42 | } 43 | 44 | int LLVMFuzzerTestOneInput(const char *data, size_t data_len) 45 | { 46 | char strptr[32]; 47 | char *argv[3] = { "setconf", "wg0", strptr }; 48 | struct hacked_pointers h = { data, data_len }; 49 | 50 | snprintf(strptr, sizeof(strptr), "%lu", (unsigned long)&h); 51 | setconf_main(3, argv); 52 | return 0; 53 | } 54 | -------------------------------------------------------------------------------- /src/fuzz/stringlist.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | /* 3 | * Copyright (C) 2018-2020 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | #define RUNSTATEDIR "/var/empty" 7 | #include "../curve25519.c" 8 | #undef __linux__ 9 | #include "../ipc.c" 10 | #include "../encoding.c" 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | const char *__asan_default_options() 19 | { 20 | return "verbosity=1"; 21 | } 22 | 23 | int LLVMFuzzerTestOneInput(const uint8_t *data, size_t data_len) 24 | { 25 | struct string_list list = { 0 }; 26 | char *interfaces; 27 | 28 | if (!data_len) 29 | return 0; 30 | 31 | interfaces = malloc(data_len); 32 | assert(interfaces); 33 | memcpy(interfaces, data, data_len); 34 | interfaces[data_len - 1] = '\0'; 35 | 36 | for (char *interface = interfaces; interface - interfaces < data_len; interface += strlen(interface) + 1) 37 | assert(string_list_add(&list, interface) == 0); 38 | 39 | for (char *interface = interfaces, *interface2 = list.buffer;;) { 40 | size_t len; 41 | 42 | if (interface - interfaces >= data_len) { 43 | assert(!interface2 || !strlen(interface2)); 44 | break; 45 | } 46 | len = strlen(interface); 47 | if (!len) { 48 | ++interface; 49 | continue; 50 | } 51 | assert(strlen(interface2) == len); 52 | assert(!memcmp(interface, interface2, len + 1)); 53 | interface += len + 1; 54 | interface2 += len + 1; 55 | } 56 | free(list.buffer); 57 | free(interfaces); 58 | return 0; 59 | } 60 | -------------------------------------------------------------------------------- /src/fuzz/uapi.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | /* 3 | * Copyright (C) 2018-2020 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | #include 7 | #include 8 | static FILE *hacked_userspace_interface_file(const char *iface); 9 | #define stat(a, b) ({ return hacked_userspace_interface_file(iface); 0; }) 10 | #define RUNSTATEDIR "/var/empty" 11 | #include "../curve25519.c" 12 | #undef __linux__ 13 | #include "../ipc.c" 14 | #include "../encoding.c" 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | const char *__asan_default_options() 22 | { 23 | return "verbosity=1"; 24 | } 25 | 26 | union hackiface { 27 | char ifname[IFNAMSIZ]; 28 | struct { 29 | const uint8_t *data; 30 | size_t len; 31 | }; 32 | }; 33 | 34 | static FILE *hacked_userspace_interface_file(const char *iface) 35 | { 36 | union hackiface *hack = (union hackiface *)iface; 37 | FILE *f = fmemopen(NULL, hack->len + 7, "r+"); 38 | fseek(f, 7, SEEK_SET); 39 | fwrite(hack->data, hack->len, 1, f); 40 | fseek(f, 0, SEEK_SET); 41 | memcpy(hack->ifname, "hack", 5); 42 | return f; 43 | } 44 | 45 | int LLVMFuzzerTestOneInput(const uint8_t *data, size_t len) 46 | { 47 | union hackiface hack = { 48 | .data = data, 49 | .len = len 50 | }; 51 | struct wgdevice *dev = NULL; 52 | 53 | userspace_get_device(&dev, (const char *)&hack); 54 | free_wgdevice(dev); 55 | return 0; 56 | } 57 | -------------------------------------------------------------------------------- /src/genkey.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 OR MIT 2 | /* 3 | * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #ifdef __linux__ 15 | #include 16 | #endif 17 | #ifdef __APPLE__ 18 | #include 19 | #ifndef MAC_OS_X_VERSION_10_12 20 | #define MAC_OS_X_VERSION_10_12 101200 21 | #endif 22 | #if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_12 23 | #include 24 | #endif 25 | #endif 26 | 27 | #include "curve25519.h" 28 | #include "encoding.h" 29 | #include "subcommands.h" 30 | 31 | #ifndef _WIN32 32 | static inline bool __attribute__((__warn_unused_result__)) get_random_bytes(uint8_t *out, size_t len) 33 | { 34 | ssize_t ret = 0; 35 | size_t i; 36 | int fd; 37 | 38 | if (len > 256) { 39 | errno = EOVERFLOW; 40 | return false; 41 | } 42 | 43 | #if defined(__OpenBSD__) || (defined(__APPLE__) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_12) || (defined(__GLIBC__) && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 25))) 44 | if (!getentropy(out, len)) 45 | return true; 46 | #endif 47 | 48 | #if defined(__NR_getrandom) && defined(__linux__) 49 | if (syscall(__NR_getrandom, out, len, 0) == (ssize_t)len) 50 | return true; 51 | #endif 52 | 53 | fd = open("/dev/urandom", O_RDONLY); 54 | if (fd < 0) 55 | return false; 56 | for (errno = 0, i = 0; i < len; i += ret, ret = 0) { 57 | ret = read(fd, out + i, len - i); 58 | if (ret <= 0) { 59 | ret = errno ? -errno : -EIO; 60 | break; 61 | } 62 | } 63 | close(fd); 64 | errno = -ret; 65 | return i == len; 66 | } 67 | #else 68 | #include 69 | static inline bool __attribute__((__warn_unused_result__)) get_random_bytes(uint8_t *out, size_t len) 70 | { 71 | return RtlGenRandom(out, len); 72 | } 73 | #endif 74 | 75 | int genkey_main(int argc, const char *argv[]) 76 | { 77 | uint8_t key[WG_KEY_LEN]; 78 | char base64[WG_KEY_LEN_BASE64]; 79 | struct stat stat; 80 | 81 | if (argc != 1) { 82 | fprintf(stderr, "Usage: %s %s\n", PROG_NAME, argv[0]); 83 | return 1; 84 | } 85 | 86 | if (!fstat(STDOUT_FILENO, &stat) && S_ISREG(stat.st_mode) && stat.st_mode & S_IRWXO) 87 | fputs("Warning: writing to world accessible file.\nConsider setting the umask to 077 and trying again.\n", stderr); 88 | 89 | if (!get_random_bytes(key, WG_KEY_LEN)) { 90 | perror("getrandom"); 91 | return 1; 92 | } 93 | if (!strcmp(argv[0], "genkey")) 94 | curve25519_clamp_secret(key); 95 | 96 | key_to_base64(base64, key); 97 | puts(base64); 98 | return 0; 99 | } 100 | -------------------------------------------------------------------------------- /src/ipc-uapi-unix.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | /* 3 | * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #define SOCK_PATH RUNSTATEDIR "/amneziawg/" 18 | #define SOCK_SUFFIX ".sock" 19 | 20 | static FILE *userspace_interface_file(const char *iface) 21 | { 22 | struct stat sbuf; 23 | struct sockaddr_un addr = { .sun_family = AF_UNIX }; 24 | int fd = -1, ret; 25 | FILE *f = NULL; 26 | 27 | errno = EINVAL; 28 | if (strchr(iface, '/')) 29 | goto out; 30 | ret = snprintf(addr.sun_path, sizeof(addr.sun_path), SOCK_PATH "%s" SOCK_SUFFIX, iface); 31 | if (ret < 0) 32 | goto out; 33 | ret = stat(addr.sun_path, &sbuf); 34 | if (ret < 0) 35 | goto out; 36 | errno = EBADF; 37 | if (!S_ISSOCK(sbuf.st_mode)) 38 | goto out; 39 | 40 | ret = fd = socket(AF_UNIX, SOCK_STREAM, 0); 41 | if (ret < 0) 42 | goto out; 43 | 44 | ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr)); 45 | if (ret < 0) { 46 | if (errno == ECONNREFUSED) /* If the process is gone, we try to clean up the socket. */ 47 | unlink(addr.sun_path); 48 | goto out; 49 | } 50 | f = fdopen(fd, "r+"); 51 | if (f) 52 | errno = 0; 53 | out: 54 | ret = -errno; 55 | if (ret) { 56 | if (fd >= 0) 57 | close(fd); 58 | errno = -ret; 59 | return NULL; 60 | } 61 | return f; 62 | } 63 | 64 | static bool userspace_has_wireguard_interface(const char *iface) 65 | { 66 | struct stat sbuf; 67 | struct sockaddr_un addr = { .sun_family = AF_UNIX }; 68 | int fd, ret; 69 | 70 | if (strchr(iface, '/')) 71 | return false; 72 | if (snprintf(addr.sun_path, sizeof(addr.sun_path), SOCK_PATH "%s" SOCK_SUFFIX, iface) < 0) 73 | return false; 74 | if (stat(addr.sun_path, &sbuf) < 0) 75 | return false; 76 | if (!S_ISSOCK(sbuf.st_mode)) 77 | return false; 78 | ret = fd = socket(AF_UNIX, SOCK_STREAM, 0); 79 | if (ret < 0) 80 | return false; 81 | ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr)); 82 | if (ret < 0 && errno == ECONNREFUSED) { /* If the process is gone, we try to clean up the socket. */ 83 | close(fd); 84 | unlink(addr.sun_path); 85 | return false; 86 | } 87 | close(fd); 88 | return true; 89 | } 90 | 91 | static int userspace_get_wireguard_interfaces(struct string_list *list) 92 | { 93 | DIR *dir; 94 | struct dirent *ent; 95 | size_t len; 96 | char *end; 97 | int ret = 0; 98 | 99 | dir = opendir(SOCK_PATH); 100 | if (!dir) 101 | return errno == ENOENT ? 0 : -errno; 102 | while ((ent = readdir(dir))) { 103 | len = strlen(ent->d_name); 104 | if (len <= strlen(SOCK_SUFFIX)) 105 | continue; 106 | end = &ent->d_name[len - strlen(SOCK_SUFFIX)]; 107 | if (strncmp(end, SOCK_SUFFIX, strlen(SOCK_SUFFIX))) 108 | continue; 109 | *end = '\0'; 110 | if (!userspace_has_wireguard_interface(ent->d_name)) 111 | continue; 112 | ret = string_list_add(list, ent->d_name); 113 | if (ret < 0) 114 | goto out; 115 | } 116 | out: 117 | closedir(dir); 118 | return ret; 119 | } 120 | -------------------------------------------------------------------------------- /src/ipc-uapi-windows.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | /* 3 | * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | static FILE *userspace_interface_file(const char *iface) 16 | { 17 | char fname[MAX_PATH]; 18 | HANDLE pipe_handle; 19 | SID expected_sid; 20 | DWORD bytes = sizeof(expected_sid); 21 | PSID pipe_sid; 22 | PSECURITY_DESCRIPTOR pipe_sd; 23 | bool equal; 24 | int fd; 25 | 26 | if (!CreateWellKnownSid(WinLocalSystemSid, NULL, &expected_sid, &bytes)) 27 | goto err; 28 | 29 | snprintf(fname, sizeof(fname), "\\\\.\\pipe\\ProtectedPrefix\\Administrators\\AmneziaWG\\%s", iface); 30 | pipe_handle = CreateFileA(fname, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); 31 | if (pipe_handle == INVALID_HANDLE_VALUE) 32 | goto err; 33 | if (GetSecurityInfo(pipe_handle, SE_FILE_OBJECT, OWNER_SECURITY_INFORMATION, &pipe_sid, NULL, NULL, NULL, &pipe_sd) != ERROR_SUCCESS) 34 | goto err_close; 35 | equal = EqualSid(&expected_sid, pipe_sid); 36 | LocalFree(pipe_sd); 37 | if (!equal) 38 | goto err_close; 39 | fd = _open_osfhandle((intptr_t)pipe_handle, _O_RDWR); 40 | if (fd == -1) { 41 | CloseHandle(pipe_handle); 42 | return NULL; 43 | } 44 | return _fdopen(fd, "r+"); 45 | err_close: 46 | CloseHandle(pipe_handle); 47 | err: 48 | errno = EACCES; 49 | return NULL; 50 | } 51 | 52 | static bool have_cached_interfaces; 53 | static struct hashtable cached_interfaces; 54 | 55 | static bool userspace_has_wireguard_interface(const char *iface) 56 | { 57 | char fname[MAX_PATH]; 58 | WIN32_FIND_DATA find_data; 59 | HANDLE find_handle; 60 | bool ret = false; 61 | 62 | if (have_cached_interfaces) 63 | return hashtable_find_entry(&cached_interfaces, iface) != NULL; 64 | 65 | snprintf(fname, sizeof(fname), "ProtectedPrefix\\Administrators\\AmneziaWG\\%s", iface); 66 | find_handle = FindFirstFile("\\\\.\\pipe\\*", &find_data); 67 | if (find_handle == INVALID_HANDLE_VALUE) 68 | return -EIO; 69 | do { 70 | if (!strcmp(fname, find_data.cFileName)) { 71 | ret = true; 72 | break; 73 | } 74 | } while (FindNextFile(find_handle, &find_data)); 75 | FindClose(find_handle); 76 | return ret; 77 | } 78 | 79 | static int userspace_get_wireguard_interfaces(struct string_list *list) 80 | { 81 | static const char prefix[] = "ProtectedPrefix\\Administrators\\AmneziaWG\\"; 82 | WIN32_FIND_DATA find_data; 83 | HANDLE find_handle; 84 | char *iface; 85 | int ret = 0; 86 | 87 | find_handle = FindFirstFile("\\\\.\\pipe\\*", &find_data); 88 | if (find_handle == INVALID_HANDLE_VALUE) 89 | return -EIO; 90 | do { 91 | if (strncmp(prefix, find_data.cFileName, strlen(prefix))) 92 | continue; 93 | iface = find_data.cFileName + strlen(prefix); 94 | ret = string_list_add(list, iface); 95 | if (ret < 0) 96 | goto out; 97 | if (!hashtable_find_or_insert_entry(&cached_interfaces, iface)) { 98 | ret = -errno; 99 | goto out; 100 | } 101 | } while (FindNextFile(find_handle, &find_data)); 102 | have_cached_interfaces = true; 103 | 104 | out: 105 | FindClose(find_handle); 106 | return ret; 107 | } 108 | -------------------------------------------------------------------------------- /src/ipc.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 OR MIT 2 | /* 3 | * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include "containers.h" 10 | #include "ipc.h" 11 | 12 | struct string_list { 13 | char *buffer; 14 | size_t len; 15 | size_t cap; 16 | }; 17 | 18 | static int string_list_add(struct string_list *list, const char *str) 19 | { 20 | size_t len = strlen(str) + 1; 21 | 22 | if (len == 1) 23 | return 0; 24 | 25 | if (len >= list->cap - list->len) { 26 | char *new_buffer; 27 | size_t new_cap = list->cap * 2; 28 | 29 | if (new_cap < list->len + len + 1) 30 | new_cap = list->len + len + 1; 31 | new_buffer = realloc(list->buffer, new_cap); 32 | if (!new_buffer) 33 | return -errno; 34 | list->buffer = new_buffer; 35 | list->cap = new_cap; 36 | } 37 | memcpy(list->buffer + list->len, str, len); 38 | list->len += len; 39 | list->buffer[list->len] = '\0'; 40 | return 0; 41 | } 42 | 43 | #include "ipc-uapi.h" 44 | #if defined(__linux__) 45 | #include "ipc-linux.h" 46 | #elif defined(__OpenBSD__) 47 | #include "ipc-openbsd.h" 48 | #elif defined(__FreeBSD__) 49 | #include "ipc-freebsd.h" 50 | #elif defined(_WIN32) 51 | #include "ipc-windows.h" 52 | #endif 53 | 54 | /* first\0second\0third\0forth\0last\0\0 */ 55 | char *ipc_list_devices(void) 56 | { 57 | struct string_list list = { 0 }; 58 | int ret; 59 | 60 | #ifdef IPC_SUPPORTS_KERNEL_INTERFACE 61 | ret = kernel_get_wireguard_interfaces(&list); 62 | if (ret < 0) 63 | goto cleanup; 64 | #endif 65 | ret = userspace_get_wireguard_interfaces(&list); 66 | if (ret < 0) 67 | goto cleanup; 68 | 69 | cleanup: 70 | errno = -ret; 71 | if (errno) { 72 | free(list.buffer); 73 | return NULL; 74 | } 75 | return list.buffer ?: strdup("\0"); 76 | } 77 | 78 | int ipc_get_device(struct wgdevice **dev, const char *iface) 79 | { 80 | #ifdef IPC_SUPPORTS_KERNEL_INTERFACE 81 | if (userspace_has_wireguard_interface(iface)) 82 | return userspace_get_device(dev, iface); 83 | return kernel_get_device(dev, iface); 84 | #else 85 | return userspace_get_device(dev, iface); 86 | #endif 87 | } 88 | 89 | int ipc_set_device(struct wgdevice *dev) 90 | { 91 | #ifdef IPC_SUPPORTS_KERNEL_INTERFACE 92 | if (userspace_has_wireguard_interface(dev->name)) 93 | return userspace_set_device(dev); 94 | return kernel_set_device(dev); 95 | #else 96 | return userspace_set_device(dev); 97 | #endif 98 | } 99 | -------------------------------------------------------------------------------- /src/ipc.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 OR MIT */ 2 | /* 3 | * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | #ifndef IPC_H 7 | #define IPC_H 8 | 9 | #include 10 | 11 | struct wgdevice; 12 | 13 | int ipc_set_device(struct wgdevice *dev); 14 | int ipc_get_device(struct wgdevice **dev, const char *interface); 15 | char *ipc_list_devices(void); 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /src/pubkey.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 OR MIT 2 | /* 3 | * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | #include 7 | #include 8 | 9 | #include "curve25519.h" 10 | #include "encoding.h" 11 | #include "subcommands.h" 12 | #include "ctype.h" 13 | 14 | int pubkey_main(int argc, const char *argv[]) 15 | { 16 | uint8_t key[WG_KEY_LEN] __attribute__((aligned(sizeof(uintptr_t)))); 17 | char base64[WG_KEY_LEN_BASE64]; 18 | int trailing_char; 19 | 20 | if (argc != 1) { 21 | fprintf(stderr, "Usage: %s %s\n", PROG_NAME, argv[0]); 22 | return 1; 23 | } 24 | 25 | if (fread(base64, 1, sizeof(base64) - 1, stdin) != sizeof(base64) - 1) { 26 | errno = EINVAL; 27 | fprintf(stderr, "%s: Key is not the correct length or format\n", PROG_NAME); 28 | return 1; 29 | } 30 | base64[WG_KEY_LEN_BASE64 - 1] = '\0'; 31 | 32 | for (;;) { 33 | trailing_char = getc(stdin); 34 | if (!trailing_char || char_is_space(trailing_char)) 35 | continue; 36 | if (trailing_char == EOF) 37 | break; 38 | fprintf(stderr, "%s: Trailing characters found after key\n", PROG_NAME); 39 | return 1; 40 | } 41 | 42 | if (!key_from_base64(key, base64)) { 43 | fprintf(stderr, "%s: Key is not the correct length or format\n", PROG_NAME); 44 | return 1; 45 | } 46 | curve25519_generate_public(key, key); 47 | key_to_base64(base64, key); 48 | puts(base64); 49 | return 0; 50 | } 51 | -------------------------------------------------------------------------------- /src/set.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 OR MIT 2 | /* 3 | * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include "containers.h" 11 | #include "config.h" 12 | #include "ipc.h" 13 | #include "subcommands.h" 14 | 15 | int set_main(int argc, const char *argv[]) 16 | { 17 | struct wgdevice *device = NULL; 18 | int ret = 1; 19 | 20 | if (argc < 3) { 21 | fprintf(stderr, "Usage: %s %s [listen-port ] [fwmark ] [private-key ] [peer [remove] [preshared-key ] [endpoint :] [persistent-keepalive ] [allowed-ips /[,/] [advanced-security ]...] ]...\n", PROG_NAME, argv[0]); 22 | return 1; 23 | } 24 | 25 | device = config_read_cmd(argv + 2, argc - 2); 26 | if (!device) 27 | goto cleanup; 28 | strncpy(device->name, argv[1], IFNAMSIZ - 1); 29 | device->name[IFNAMSIZ - 1] = '\0'; 30 | 31 | if (ipc_set_device(device) != 0) { 32 | perror("Unable to modify interface"); 33 | goto cleanup; 34 | } 35 | 36 | ret = 0; 37 | 38 | cleanup: 39 | free_wgdevice(device); 40 | return ret; 41 | } 42 | -------------------------------------------------------------------------------- /src/setconf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 OR MIT 2 | /* 3 | * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "containers.h" 12 | #include "config.h" 13 | #include "ipc.h" 14 | #include "subcommands.h" 15 | 16 | struct pubkey_origin { 17 | uint8_t *pubkey; 18 | bool from_file; 19 | }; 20 | 21 | static int pubkey_cmp(const void *first, const void *second) 22 | { 23 | const struct pubkey_origin *a = first, *b = second; 24 | int ret = memcmp(a->pubkey, b->pubkey, WG_KEY_LEN); 25 | if (ret) 26 | return ret; 27 | return a->from_file - b->from_file; 28 | } 29 | 30 | static bool sync_conf(struct wgdevice *file) 31 | { 32 | struct wgdevice *runtime; 33 | struct wgpeer *peer; 34 | struct pubkey_origin *pubkeys; 35 | size_t peer_count = 0, i = 0; 36 | 37 | if (!file->first_peer) 38 | return true; 39 | 40 | for_each_wgpeer(file, peer) 41 | ++peer_count; 42 | 43 | if (ipc_get_device(&runtime, file->name) != 0) { 44 | perror("Unable to retrieve current interface configuration"); 45 | return false; 46 | } 47 | 48 | if (!runtime->first_peer) { 49 | free_wgdevice(runtime); 50 | return true; 51 | } 52 | 53 | file->flags &= ~WGDEVICE_REPLACE_PEERS; 54 | 55 | for_each_wgpeer(runtime, peer) 56 | ++peer_count; 57 | 58 | pubkeys = calloc(peer_count, sizeof(*pubkeys)); 59 | if (!pubkeys) { 60 | free_wgdevice(runtime); 61 | perror("Public key allocation"); 62 | return false; 63 | } 64 | 65 | for_each_wgpeer(file, peer) { 66 | pubkeys[i].pubkey = peer->public_key; 67 | pubkeys[i].from_file = true; 68 | ++i; 69 | } 70 | for_each_wgpeer(runtime, peer) { 71 | pubkeys[i].pubkey = peer->public_key; 72 | pubkeys[i].from_file = false; 73 | ++i; 74 | } 75 | qsort(pubkeys, peer_count, sizeof(*pubkeys), pubkey_cmp); 76 | 77 | for (i = 0; i < peer_count; ++i) { 78 | if (pubkeys[i].from_file) 79 | continue; 80 | if (i == peer_count - 1 || !pubkeys[i + 1].from_file || memcmp(pubkeys[i].pubkey, pubkeys[i + 1].pubkey, WG_KEY_LEN)) { 81 | peer = calloc(1, sizeof(struct wgpeer)); 82 | if (!peer) { 83 | free_wgdevice(runtime); 84 | free(pubkeys); 85 | perror("Peer allocation"); 86 | return false; 87 | } 88 | peer->flags = WGPEER_REMOVE_ME; 89 | memcpy(peer->public_key, pubkeys[i].pubkey, WG_KEY_LEN); 90 | peer->next_peer = file->first_peer; 91 | file->first_peer = peer; 92 | if (!file->last_peer) 93 | file->last_peer = peer; 94 | } 95 | } 96 | free_wgdevice(runtime); 97 | free(pubkeys); 98 | return true; 99 | } 100 | 101 | int setconf_main(int argc, const char *argv[]) 102 | { 103 | struct wgdevice *device = NULL; 104 | struct config_ctx ctx; 105 | FILE *config_input = NULL; 106 | char *config_buffer = NULL; 107 | size_t config_buffer_len = 0; 108 | int ret = 1; 109 | 110 | if (argc != 3) { 111 | fprintf(stderr, "Usage: %s %s \n", PROG_NAME, argv[0]); 112 | return 1; 113 | } 114 | 115 | config_input = fopen(argv[2], "r"); 116 | if (!config_input) { 117 | perror("fopen"); 118 | return 1; 119 | } 120 | if (!config_read_init(&ctx, !strcmp(argv[0], "addconf"))) { 121 | fclose(config_input); 122 | return 1; 123 | } 124 | while (getline(&config_buffer, &config_buffer_len, config_input) >= 0) { 125 | if (!config_read_line(&ctx, config_buffer)) { 126 | fprintf(stderr, "Configuration parsing error\n"); 127 | goto cleanup; 128 | } 129 | } 130 | device = config_read_finish(&ctx); 131 | if (!device) { 132 | fprintf(stderr, "Invalid configuration\n"); 133 | goto cleanup; 134 | } 135 | strncpy(device->name, argv[1], IFNAMSIZ - 1); 136 | device->name[IFNAMSIZ - 1] = '\0'; 137 | 138 | if (!strcmp(argv[0], "syncconf")) { 139 | if (!sync_conf(device)) 140 | goto cleanup; 141 | } 142 | 143 | if (ipc_set_device(device) != 0) { 144 | perror("Unable to modify interface"); 145 | goto cleanup; 146 | } 147 | 148 | ret = 0; 149 | 150 | cleanup: 151 | if (config_input) 152 | fclose(config_input); 153 | free(config_buffer); 154 | free_wgdevice(device); 155 | return ret; 156 | } 157 | -------------------------------------------------------------------------------- /src/showconf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 OR MIT 2 | /* 3 | * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "containers.h" 17 | #include "encoding.h" 18 | #include "ipc.h" 19 | #include "subcommands.h" 20 | 21 | int showconf_main(int argc, const char *argv[]) 22 | { 23 | char base64[WG_KEY_LEN_BASE64]; 24 | char ip[INET6_ADDRSTRLEN]; 25 | struct wgdevice *device = NULL; 26 | struct wgpeer *peer; 27 | struct wgallowedip *allowedip; 28 | int ret = 1; 29 | 30 | if (argc != 2) { 31 | fprintf(stderr, "Usage: %s %s \n", PROG_NAME, argv[0]); 32 | return 1; 33 | } 34 | 35 | if (ipc_get_device(&device, argv[1])) { 36 | perror("Unable to access interface"); 37 | goto cleanup; 38 | } 39 | 40 | printf("[Interface]\n"); 41 | if (device->listen_port) 42 | printf("ListenPort = %u\n", device->listen_port); 43 | if (device->fwmark) 44 | printf("FwMark = 0x%x\n", device->fwmark); 45 | if (device->flags & WGDEVICE_HAS_PRIVATE_KEY) { 46 | key_to_base64(base64, device->private_key); 47 | printf("PrivateKey = %s\n", base64); 48 | } 49 | if (device->flags & WGDEVICE_HAS_JC) 50 | printf("Jc = %u\n", device->junk_packet_count); 51 | if (device->flags & WGDEVICE_HAS_JMIN) 52 | printf("Jmin = %u\n", device->junk_packet_min_size); 53 | if (device->flags & WGDEVICE_HAS_JMAX) 54 | printf("Jmax = %u\n", device->junk_packet_max_size); 55 | if (device->flags & WGDEVICE_HAS_S1) 56 | printf("S1 = %u\n", device->init_packet_junk_size); 57 | if (device->flags & WGDEVICE_HAS_S2) 58 | printf("S2 = %u\n", device->response_packet_junk_size); 59 | if (device->flags & WGDEVICE_HAS_H1) 60 | printf("H1 = %u\n", device->init_packet_magic_header); 61 | if (device->flags & WGDEVICE_HAS_H2) 62 | printf("H2 = %u\n", device->response_packet_magic_header); 63 | if (device->flags & WGDEVICE_HAS_H3) 64 | printf("H3 = %u\n", device->underload_packet_magic_header); 65 | if (device->flags & WGDEVICE_HAS_H4) 66 | printf("H4 = %u\n", device->transport_packet_magic_header); 67 | 68 | printf("\n"); 69 | for_each_wgpeer(device, peer) { 70 | key_to_base64(base64, peer->public_key); 71 | printf("[Peer]\nPublicKey = %s\n", base64); 72 | if (peer->flags & WGPEER_HAS_PRESHARED_KEY) { 73 | key_to_base64(base64, peer->preshared_key); 74 | printf("PresharedKey = %s\n", base64); 75 | } 76 | if (peer->flags & WGPEER_HAS_ADVANCED_SECURITY) { 77 | printf("AdvancedSecurity = %s\n", peer->advanced_security ? "on" : "off"); 78 | } 79 | if (peer->first_allowedip) 80 | printf("AllowedIPs = "); 81 | for_each_wgallowedip(peer, allowedip) { 82 | if (allowedip->family == AF_INET) { 83 | if (!inet_ntop(AF_INET, &allowedip->ip4, ip, INET6_ADDRSTRLEN)) 84 | continue; 85 | } else if (allowedip->family == AF_INET6) { 86 | if (!inet_ntop(AF_INET6, &allowedip->ip6, ip, INET6_ADDRSTRLEN)) 87 | continue; 88 | } else 89 | continue; 90 | printf("%s/%d", ip, allowedip->cidr); 91 | if (allowedip->next_allowedip) 92 | printf(", "); 93 | } 94 | if (peer->first_allowedip) 95 | printf("\n"); 96 | 97 | if (peer->endpoint.addr.sa_family == AF_INET || peer->endpoint.addr.sa_family == AF_INET6) { 98 | char host[4096 + 1]; 99 | char service[512 + 1]; 100 | socklen_t addr_len = 0; 101 | 102 | if (peer->endpoint.addr.sa_family == AF_INET) 103 | addr_len = sizeof(struct sockaddr_in); 104 | else if (peer->endpoint.addr.sa_family == AF_INET6) 105 | addr_len = sizeof(struct sockaddr_in6); 106 | if (!getnameinfo(&peer->endpoint.addr, addr_len, host, sizeof(host), service, sizeof(service), NI_DGRAM | NI_NUMERICSERV | NI_NUMERICHOST)) { 107 | if (peer->endpoint.addr.sa_family == AF_INET6 && strchr(host, ':')) 108 | printf("Endpoint = [%s]:%s\n", host, service); 109 | else 110 | printf("Endpoint = %s:%s\n", host, service); 111 | } 112 | } 113 | 114 | if (peer->persistent_keepalive_interval) 115 | printf("PersistentKeepalive = %u\n", peer->persistent_keepalive_interval); 116 | 117 | if (peer->next_peer) 118 | printf("\n"); 119 | } 120 | ret = 0; 121 | 122 | cleanup: 123 | free_wgdevice(device); 124 | return ret; 125 | } 126 | -------------------------------------------------------------------------------- /src/subcommands.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 OR MIT */ 2 | /* 3 | * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | #ifndef SUBCOMMANDS_H 7 | #define SUBCOMMANDS_H 8 | 9 | extern const char *PROG_NAME; 10 | int show_main(int argc, const char *argv[]); 11 | int showconf_main(int argc, const char *argv[]); 12 | int set_main(int argc, const char *argv[]); 13 | int setconf_main(int argc, const char *argv[]); 14 | int genkey_main(int argc, const char *argv[]); 15 | int pubkey_main(int argc, const char *argv[]); 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /src/systemd/wg-quick.target: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=WireGuard Tunnels via wg-quick(8) 3 | -------------------------------------------------------------------------------- /src/systemd/wg-quick@.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=WireGuard via wg-quick(8) for %I 3 | After=network-online.target nss-lookup.target 4 | Wants=network-online.target nss-lookup.target 5 | PartOf=awg-quick.target 6 | Documentation=man:awg-quick(8) 7 | Documentation=man:awg(8) 8 | Documentation=https://www.wireguard.com/ 9 | Documentation=https://www.wireguard.com/quickstart/ 10 | Documentation=https://git.zx2c4.com/wireguard-tools/about/src/man/wg-quick.8 11 | Documentation=https://git.zx2c4.com/wireguard-tools/about/src/man/wg.8 12 | 13 | [Service] 14 | Type=oneshot 15 | RemainAfterExit=yes 16 | ExecStart=/usr/bin/awg-quick up %i 17 | ExecStop=/usr/bin/awg-quick down %i 18 | ExecReload=/bin/bash -c 'exec /usr/bin/awg syncconf %i <(exec /usr/bin/awg-quick strip %i)' 19 | Environment=WG_ENDPOINT_RESOLUTION_RETRIES=infinity 20 | 21 | [Install] 22 | WantedBy=multi-user.target 23 | -------------------------------------------------------------------------------- /src/terminal.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 OR MIT 2 | /* 3 | * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "ctype.h" 14 | #include "terminal.h" 15 | 16 | static bool color_mode(void) 17 | { 18 | static int mode = -1; 19 | const char *var; 20 | 21 | if (mode != -1) 22 | return mode; 23 | var = getenv("WG_COLOR_MODE"); 24 | if (var && !strcmp(var, "always")) 25 | mode = true; 26 | else if (var && !strcmp(var, "never")) 27 | mode = false; 28 | else 29 | mode = isatty(fileno(stdout)); 30 | return mode; 31 | } 32 | 33 | static void filter_ansi(const char *fmt, va_list args) 34 | { 35 | char *str = NULL; 36 | size_t len, i, j; 37 | 38 | if (color_mode()) { 39 | vfprintf(stdout, fmt, args); 40 | return; 41 | } 42 | 43 | len = vasprintf(&str, fmt, args); 44 | 45 | if (len >= 2) { 46 | for (i = 0; i < len - 2; ++i) { 47 | if (str[i] == '\x1b' && str[i + 1] == '[') { 48 | str[i] = str[i + 1] = '\0'; 49 | for (j = i + 2; j < len; ++j) { 50 | if (char_is_alpha(str[j])) 51 | break; 52 | str[j] = '\0'; 53 | } 54 | str[j] = '\0'; 55 | } 56 | } 57 | } 58 | for (i = 0; i < len; i = j) { 59 | fputs(&str[i], stdout); 60 | for (j = i + strlen(&str[i]); j < len; ++j) { 61 | if (str[j] != '\0') 62 | break; 63 | } 64 | } 65 | 66 | free(str); 67 | } 68 | 69 | void terminal_printf(const char *fmt, ...) 70 | { 71 | va_list args; 72 | 73 | va_start(args, fmt); 74 | filter_ansi(fmt, args); 75 | va_end(args); 76 | } 77 | -------------------------------------------------------------------------------- /src/terminal.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 OR MIT */ 2 | /* 3 | * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | #ifndef TERMINAL_H 7 | #define TERMINAL_H 8 | 9 | #define TERMINAL_FG_BLACK "\x1b[30m" 10 | #define TERMINAL_FG_RED "\x1b[31m" 11 | #define TERMINAL_FG_GREEN "\x1b[32m" 12 | #define TERMINAL_FG_YELLOW "\x1b[33m" 13 | #define TERMINAL_FG_BLUE "\x1b[34m" 14 | #define TERMINAL_FG_MAGENTA "\x1b[35m" 15 | #define TERMINAL_FG_CYAN "\x1b[36m" 16 | #define TERMINAL_FG_WHITE "\x1b[37m" 17 | #define TERMINAL_FG_DEFAULT "\x1b[39m" 18 | 19 | #define TERMINAL_BG_BLACK "\x1b[40m" 20 | #define TERMINAL_BG_RED "\x1b[41m" 21 | #define TERMINAL_BG_GREEN "\x1b[42m" 22 | #define TERMINAL_BG_YELLOW "\x1b[43m" 23 | #define TERMINAL_BG_BLUE "\x1b[44m" 24 | #define TERMINAL_BG_MAGENTA "\x1b[45m" 25 | #define TERMINAL_BG_CYAN "\x1b[46m" 26 | #define TERMINAL_BG_WHITE "\x1b[47m" 27 | #define TERMINAL_BG_DEFAULT "\x1b[49m" 28 | 29 | #define TERMINAL_BOLD "\x1b[1m" 30 | #define TERMINAL_NO_BOLD "\x1b[22m" 31 | #define TERMINAL_UNDERLINE "\x1b[4m" 32 | #define TERMINAL_NO_UNDERLINE "\x1b[24m" 33 | 34 | #define TERMINAL_RESET "\x1b[0m" 35 | 36 | #define TERMINAL_SAVE_CURSOR "\x1b[s" 37 | #define TERMINAL_RESTORE_CURSOR "\x1b[u" 38 | #define TERMINAL_UP_CURSOR(l) "\x1b[" #l "A" 39 | #define TERMINAL_DOWN_CURSOR(l) "\x1b[" #l "B" 40 | #define TERMINAL_RIGHT_CURSOR(c) "\x1b[" #c "C" 41 | #define TERMINAL_LEFT_CURSOR(c) "\x1b[" #c "D" 42 | #define TERMINAL_CLEAR_DOWN "\x1b[0J" 43 | #define TERMINAL_CLEAR_UP "\x1b[1J" 44 | #define TERMINAL_CLEAR_RIGHT "\x1b[0K" 45 | #define TERMINAL_CLEAR_LEFT "\x1b[1K" 46 | #define TERMINAL_CLEAR_LINE "\x1b[2K" 47 | #define TERMINAL_CLEAR_ALL "\x1b[2J" 48 | 49 | void terminal_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /src/uapi/freebsd/dev/wg/if_wg.h: -------------------------------------------------------------------------------- 1 | #ifndef __IF_WG_H__ 2 | #define __IF_WG_H__ 3 | 4 | #include 5 | #include 6 | 7 | struct wg_data_io { 8 | char wgd_name[IFNAMSIZ]; 9 | void *wgd_data; 10 | size_t wgd_size; 11 | }; 12 | 13 | #define SIOCSWG _IOWR('i', 210, struct wg_data_io) 14 | #define SIOCGWG _IOWR('i', 211, struct wg_data_io) 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /src/uapi/openbsd/net/if_wg.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: ISC */ 2 | /* 3 | * Copyright (C) 2020 Jason A. Donenfeld . All Rights Reserved. 4 | * Copyright (c) 2020 Matt Dunwoodie 5 | */ 6 | 7 | #ifndef __IF_WG_H__ 8 | #define __IF_WG_H__ 9 | 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | 16 | 17 | /* 18 | * This is the public interface to the WireGuard network interface. 19 | * 20 | * It is designed to be used by tools such as ifconfig(8) and wg(8). 21 | */ 22 | 23 | #define WG_KEY_LEN 32 24 | 25 | #define SIOCSWG _IOWR('i', 210, struct wg_data_io) 26 | #define SIOCGWG _IOWR('i', 211, struct wg_data_io) 27 | 28 | #define a_ipv4 a_addr.addr_ipv4 29 | #define a_ipv6 a_addr.addr_ipv6 30 | 31 | struct wg_aip_io { 32 | sa_family_t a_af; 33 | int a_cidr; 34 | union wg_aip_addr { 35 | struct in_addr addr_ipv4; 36 | struct in6_addr addr_ipv6; 37 | } a_addr; 38 | }; 39 | 40 | #define WG_PEER_HAS_PUBLIC (1 << 0) 41 | #define WG_PEER_HAS_PSK (1 << 1) 42 | #define WG_PEER_HAS_PKA (1 << 2) 43 | #define WG_PEER_HAS_ENDPOINT (1 << 3) 44 | #define WG_PEER_REPLACE_AIPS (1 << 4) 45 | #define WG_PEER_REMOVE (1 << 5) 46 | #define WG_PEER_UPDATE (1 << 6) 47 | 48 | #define p_sa p_endpoint.sa_sa 49 | #define p_sin p_endpoint.sa_sin 50 | #define p_sin6 p_endpoint.sa_sin6 51 | 52 | struct wg_peer_io { 53 | int p_flags; 54 | int p_protocol_version; 55 | uint8_t p_public[WG_KEY_LEN]; 56 | uint8_t p_psk[WG_KEY_LEN]; 57 | uint16_t p_pka; 58 | union wg_peer_endpoint { 59 | struct sockaddr sa_sa; 60 | struct sockaddr_in sa_sin; 61 | struct sockaddr_in6 sa_sin6; 62 | } p_endpoint; 63 | uint64_t p_txbytes; 64 | uint64_t p_rxbytes; 65 | struct timespec p_last_handshake; /* nanotime */ 66 | size_t p_aips_count; 67 | struct wg_aip_io p_aips[]; 68 | }; 69 | 70 | #define WG_INTERFACE_HAS_PUBLIC (1 << 0) 71 | #define WG_INTERFACE_HAS_PRIVATE (1 << 1) 72 | #define WG_INTERFACE_HAS_PORT (1 << 2) 73 | #define WG_INTERFACE_HAS_RTABLE (1 << 3) 74 | #define WG_INTERFACE_REPLACE_PEERS (1 << 4) 75 | #define WG_INTERFACE_DEVICE_HAS_JC (1 << 5) 76 | #define WG_INTERFACE_DEVICE_HAS_JMIN (1 << 6) 77 | #define WG_INTERFACE_DEVICE_HAS_JMAX (1 << 7) 78 | #define WG_INTERFACE_DEVICE_HAS_S1 (1 << 8) 79 | #define WG_INTERFACE_DEVICE_HAS_S2 (1 << 9) 80 | #define WG_INTERFACE_DEVICE_HAS_H1 (1 << 10) 81 | #define WG_INTERFACE_DEVICE_HAS_H2 (1 << 11) 82 | #define WG_INTERFACE_DEVICE_HAS_H3 (1 << 12) 83 | #define WG_INTERFACE_DEVICE_HAS_H4 (1 << 13) 84 | 85 | struct wg_interface_io { 86 | uint16_t i_flags; 87 | in_port_t i_port; 88 | int i_rtable; 89 | uint8_t i_public[WG_KEY_LEN]; 90 | uint8_t i_private[WG_KEY_LEN]; 91 | size_t i_peers_count; 92 | struct wg_peer_io i_peers[]; 93 | 94 | uint16_t i_junk_packet_count; 95 | uint16_t i_junk_packet_min_size; 96 | uint16_t i_junk_packet_max_size; 97 | uint16_t i_init_packet_junk_size; 98 | uint16_t i_response_packet_junk_size; 99 | uint32_t i_init_packet_magic_header; 100 | uint32_t i_response_packet_magic_header; 101 | uint32_t i_underload_packet_magic_header; 102 | uint32_t i_transport_packet_magic_header; 103 | }; 104 | 105 | struct wg_data_io { 106 | char wgd_name[IFNAMSIZ]; 107 | size_t wgd_size; /* total size of the memory pointed to by wgd_interface */ 108 | struct wg_interface_io *wgd_interface; 109 | }; 110 | 111 | #endif /* __IF_WG_H__ */ 112 | -------------------------------------------------------------------------------- /src/uapi/windows/wireguard.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 2 | * 3 | * Copyright (C) 2021 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | #ifndef _WIREGUARD_NT_H 7 | #define _WIREGUARD_NT_H 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #define WG_KEY_LEN 32 16 | 17 | typedef struct _WG_IOCTL_ALLOWED_IP 18 | { 19 | union 20 | { 21 | IN_ADDR V4; 22 | IN6_ADDR V6; 23 | } Address; 24 | ADDRESS_FAMILY AddressFamily; 25 | UCHAR Cidr; 26 | } __attribute__((aligned(8))) WG_IOCTL_ALLOWED_IP; 27 | 28 | typedef enum 29 | { 30 | WG_IOCTL_PEER_HAS_PUBLIC_KEY = 1 << 0, 31 | WG_IOCTL_PEER_HAS_PRESHARED_KEY = 1 << 1, 32 | WG_IOCTL_PEER_HAS_PERSISTENT_KEEPALIVE = 1 << 2, 33 | WG_IOCTL_PEER_HAS_ENDPOINT = 1 << 3, 34 | WG_IOCTL_PEER_HAS_PROTOCOL_VERSION = 1 << 4, 35 | WG_IOCTL_PEER_REPLACE_ALLOWED_IPS = 1 << 5, 36 | WG_IOCTL_PEER_REMOVE = 1 << 6, 37 | WG_IOCTL_PEER_UPDATE = 1 << 7 38 | } WG_IOCTL_PEER_FLAG; 39 | 40 | typedef struct _WG_IOCTL_PEER 41 | { 42 | WG_IOCTL_PEER_FLAG Flags; 43 | ULONG ProtocolVersion; /* 0 = latest protocol, 1 = this protocol. */ 44 | UCHAR PublicKey[WG_KEY_LEN]; 45 | UCHAR PresharedKey[WG_KEY_LEN]; 46 | USHORT PersistentKeepalive; 47 | SOCKADDR_INET Endpoint; 48 | ULONG64 TxBytes; 49 | ULONG64 RxBytes; 50 | ULONG64 LastHandshake; 51 | ULONG AllowedIPsCount; 52 | } __attribute__((aligned(8))) WG_IOCTL_PEER; 53 | 54 | typedef enum 55 | { 56 | WG_IOCTL_INTERFACE_HAS_PUBLIC_KEY = 1 << 0, 57 | WG_IOCTL_INTERFACE_HAS_PRIVATE_KEY = 1 << 1, 58 | WG_IOCTL_INTERFACE_HAS_LISTEN_PORT = 1 << 2, 59 | WG_IOCTL_INTERFACE_REPLACE_PEERS = 1 << 3, 60 | WG_IOCTL_INTERFACE_PEERS = 1 << 4, 61 | WG_IOCTL_INTERFACE_JC = 1 << 5, 62 | WG_IOCTL_INTERFACE_JMIN = 1 << 6, 63 | WG_IOCTL_INTERFACE_JMAX = 1 << 7, 64 | WG_IOCTL_INTERFACE_S1 = 1 << 8, 65 | WG_IOCTL_INTERFACE_S2 = 1 << 9, 66 | WG_IOCTL_INTERFACE_H1 = 1 << 10, 67 | WG_IOCTL_INTERFACE_H2 = 1 << 11, 68 | WG_IOCTL_INTERFACE_H3 = 1 << 12, 69 | WG_IOCTL_INTERFACE_H4 = 1 << 13 70 | } WG_IOCTL_INTERFACE_FLAG; 71 | 72 | typedef struct _WG_IOCTL_INTERFACE 73 | { 74 | WG_IOCTL_INTERFACE_FLAG Flags; 75 | USHORT ListenPort; 76 | UCHAR PrivateKey[WG_KEY_LEN]; 77 | UCHAR PublicKey[WG_KEY_LEN]; 78 | ULONG PeersCount; 79 | USHORT JunkPacketCount; 80 | USHORT JunkPacketMinSize; 81 | USHORT JunkPacketMaxSize; 82 | USHORT InitPacketJunkSize; 83 | USHORT ResponsePacketJunkSize; 84 | ULONG InitPacketMagicHeader; 85 | ULONG ResponsePacketMagicHeader; 86 | ULONG UnderloadPacketMagicHeader; 87 | ULONG TransportPacketMagicHeader; 88 | } __attribute__((aligned(8))) WG_IOCTL_INTERFACE; 89 | 90 | #define WG_IOCTL_GET CTL_CODE(45208U, 321, METHOD_OUT_DIRECT, FILE_READ_DATA | FILE_WRITE_DATA) 91 | #define WG_IOCTL_SET CTL_CODE(45208U, 322, METHOD_IN_DIRECT, FILE_READ_DATA | FILE_WRITE_DATA) 92 | 93 | #define DEVPKEY_WG_NAME (DEVPROPKEY) { \ 94 | { 0x65726957, 0x7547, 0x7261, { 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x4b, 0x65, 0x79 } }, \ 95 | DEVPROPID_FIRST_USABLE + 1 \ 96 | } 97 | 98 | 99 | #endif 100 | -------------------------------------------------------------------------------- /src/version.h: -------------------------------------------------------------------------------- 1 | #ifndef WIREGUARD_TOOLS_VERSION 2 | #define WIREGUARD_TOOLS_VERSION "1.0.20210914" 3 | #endif 4 | -------------------------------------------------------------------------------- /src/wg-quick/awg: -------------------------------------------------------------------------------- 1 | ../wg -------------------------------------------------------------------------------- /src/wg.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 OR MIT 2 | /* 3 | * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include "subcommands.h" 11 | #include "version.h" 12 | 13 | const char *PROG_NAME; 14 | 15 | static const struct { 16 | const char *subcommand; 17 | int (*function)(int, const char**); 18 | const char *description; 19 | } subcommands[] = { 20 | { "show", show_main, "Shows the current configuration and device information" }, 21 | { "showconf", showconf_main, "Shows the current configuration of a given WireGuard interface, for use with `setconf'" }, 22 | { "set", set_main, "Change the current configuration, add peers, remove peers, or change peers" }, 23 | { "setconf", setconf_main, "Applies a configuration file to a WireGuard interface" }, 24 | { "addconf", setconf_main, "Appends a configuration file to a WireGuard interface" }, 25 | { "syncconf", setconf_main, "Synchronizes a configuration file to a WireGuard interface" }, 26 | { "genkey", genkey_main, "Generates a new private key and writes it to stdout" }, 27 | { "genpsk", genkey_main, "Generates a new preshared key and writes it to stdout" }, 28 | { "pubkey", pubkey_main, "Reads a private key from stdin and writes a public key to stdout" } 29 | }; 30 | 31 | static void show_usage(FILE *file) 32 | { 33 | fprintf(file, "Usage: %s []\n\n", PROG_NAME); 34 | fprintf(file, "Available subcommands:\n"); 35 | for (size_t i = 0; i < sizeof(subcommands) / sizeof(subcommands[0]); ++i) 36 | fprintf(file, " %s: %s\n", subcommands[i].subcommand, subcommands[i].description); 37 | fprintf(file, "You may pass `--help' to any of these subcommands to view usage.\n"); 38 | } 39 | 40 | int main(int argc, const char *argv[]) 41 | { 42 | PROG_NAME = argv[0]; 43 | 44 | if (argc == 2 && (!strcmp(argv[1], "-v") || !strcmp(argv[1], "--version") || !strcmp(argv[1], "version"))) { 45 | printf("wireguard-tools v%s - https://git.zx2c4.com/wireguard-tools/\n", WIREGUARD_TOOLS_VERSION); 46 | return 0; 47 | } 48 | if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help") || !strcmp(argv[1], "help"))) { 49 | show_usage(stdout); 50 | return 0; 51 | } 52 | 53 | if (argc == 1) { 54 | static const char *new_argv[] = { "show", NULL }; 55 | return show_main(1, new_argv); 56 | } 57 | 58 | for (size_t i = 0; i < sizeof(subcommands) / sizeof(subcommands[0]); ++i) { 59 | if (!strcmp(argv[1], subcommands[i].subcommand)) 60 | return subcommands[i].function(argc - 1, argv + 1); 61 | } 62 | 63 | fprintf(stderr, "Invalid subcommand: `%s'\n", argv[1]); 64 | show_usage(stderr); 65 | return 1; 66 | } 67 | -------------------------------------------------------------------------------- /src/wincompat/compat.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | /* 3 | * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | #define __USE_MINGW_ANSI_STDIO 1 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #undef interface 18 | #undef min 19 | #undef max 20 | 21 | #define IFNAMSIZ 64 22 | #define EAI_SYSTEM -99 23 | 24 | /* libc.c */ 25 | char *strsep(char **str, const char *sep); 26 | ssize_t getdelim(char **buf, size_t *bufsiz, int delimiter, FILE *fp); 27 | ssize_t getline(char **buf, size_t *bufsiz, FILE *fp); 28 | -------------------------------------------------------------------------------- /src/wincompat/include/arpa/inet.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amnezia-vpn/amneziawg-tools/c0b400c6dfc046f5cae8f3051b14cb61686fcf55/src/wincompat/include/arpa/inet.h -------------------------------------------------------------------------------- /src/wincompat/include/hashtable.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 2 | * 3 | * Copyright (C) 2018-2021 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | #ifndef _HASHTABLE_H 7 | #define _HASHTABLE_H 8 | 9 | #include 10 | 11 | enum { HASHTABLE_ENTRY_BUCKETS_POW2 = 1 << 10 }; 12 | 13 | struct hashtable_entry { 14 | char *key; 15 | void *value; 16 | struct hashtable_entry *next; 17 | }; 18 | 19 | struct hashtable { 20 | struct hashtable_entry *entry_buckets[HASHTABLE_ENTRY_BUCKETS_POW2]; 21 | }; 22 | 23 | static unsigned int hashtable_bucket(const char *str) 24 | { 25 | unsigned long hash = 5381; 26 | char c; 27 | while ((c = *str++)) 28 | hash = ((hash << 5) + hash) ^ c; 29 | return hash & (HASHTABLE_ENTRY_BUCKETS_POW2 - 1); 30 | } 31 | 32 | static struct hashtable_entry *hashtable_find_entry(struct hashtable *hashtable, const char *key) 33 | { 34 | struct hashtable_entry *entry; 35 | for (entry = hashtable->entry_buckets[hashtable_bucket(key)]; entry; entry = entry->next) { 36 | if (!strcmp(entry->key, key)) 37 | return entry; 38 | } 39 | return NULL; 40 | } 41 | 42 | static struct hashtable_entry *hashtable_find_or_insert_entry(struct hashtable *hashtable, const char *key) 43 | { 44 | struct hashtable_entry **entry; 45 | for (entry = &hashtable->entry_buckets[hashtable_bucket(key)]; *entry; entry = &(*entry)->next) { 46 | if (!strcmp((*entry)->key, key)) 47 | return *entry; 48 | } 49 | *entry = calloc(1, sizeof(**entry)); 50 | if (!*entry) 51 | return NULL; 52 | (*entry)->key = strdup(key); 53 | if (!(*entry)->key) { 54 | free(*entry); 55 | *entry = NULL; 56 | return NULL; 57 | } 58 | return *entry; 59 | } 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /src/wincompat/include/net/if.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amnezia-vpn/amneziawg-tools/c0b400c6dfc046f5cae8f3051b14cb61686fcf55/src/wincompat/include/net/if.h -------------------------------------------------------------------------------- /src/wincompat/include/netdb.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amnezia-vpn/amneziawg-tools/c0b400c6dfc046f5cae8f3051b14cb61686fcf55/src/wincompat/include/netdb.h -------------------------------------------------------------------------------- /src/wincompat/include/netinet/in.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amnezia-vpn/amneziawg-tools/c0b400c6dfc046f5cae8f3051b14cb61686fcf55/src/wincompat/include/netinet/in.h -------------------------------------------------------------------------------- /src/wincompat/include/sys/socket.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amnezia-vpn/amneziawg-tools/c0b400c6dfc046f5cae8f3051b14cb61686fcf55/src/wincompat/include/sys/socket.h -------------------------------------------------------------------------------- /src/wincompat/init.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | /* 3 | * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | #include 7 | #include 8 | 9 | #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING 10 | #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x4 11 | #endif 12 | 13 | extern void NTAPI RtlGetNtVersionNumbers(DWORD *major, DWORD *minor, DWORD *build); 14 | bool is_win7 = false; 15 | 16 | __attribute__((constructor)) static void init(void) 17 | { 18 | char *colormode; 19 | DWORD console_mode, major, minor; 20 | HANDLE stdout_handle; 21 | WSADATA wsaData; 22 | 23 | if (!SetDllDirectoryA("") || !SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_SYSTEM32)) 24 | abort(); 25 | 26 | RtlGetNtVersionNumbers(&major, &minor, NULL); 27 | is_win7 = (major == 6 && minor <= 1) || major < 6; 28 | 29 | WSAStartup(MAKEWORD(2, 2), &wsaData); 30 | 31 | stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE); // We don't close this. 32 | if (stdout_handle == INVALID_HANDLE_VALUE) 33 | goto no_color; 34 | if (!GetConsoleMode(stdout_handle, &console_mode)) 35 | goto no_color; 36 | if (!SetConsoleMode(stdout_handle, ENABLE_VIRTUAL_TERMINAL_PROCESSING | console_mode)) 37 | goto no_color; 38 | return; 39 | 40 | no_color: 41 | colormode = getenv("WG_COLOR_MODE"); 42 | if (!colormode) 43 | putenv("WG_COLOR_MODE=never"); 44 | } 45 | 46 | __attribute__((destructor)) static void deinit(void) 47 | { 48 | WSACleanup(); 49 | } 50 | -------------------------------------------------------------------------------- /src/wincompat/libc.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | /* 3 | * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | char *strsep(char **str, const char *sep) 12 | { 13 | char *s = *str, *end; 14 | if (!s) 15 | return NULL; 16 | end = s + strcspn(s, sep); 17 | if (*end) 18 | *end++ = 0; 19 | else 20 | end = 0; 21 | *str = end; 22 | return s; 23 | } 24 | 25 | ssize_t getdelim(char **buf, size_t *bufsiz, int delimiter, FILE *fp) 26 | { 27 | char *ptr, *eptr; 28 | 29 | if (!*buf || !*bufsiz) { 30 | *bufsiz = BUFSIZ; 31 | if (!(*buf = malloc(*bufsiz))) 32 | return -1; 33 | } 34 | 35 | for (ptr = *buf, eptr = *buf + *bufsiz;;) { 36 | int c = fgetc(fp); 37 | if (c == -1) { 38 | if (feof(fp)) { 39 | ssize_t diff = (ssize_t)(ptr - *buf); 40 | if (diff != 0) { 41 | *ptr = '\0'; 42 | return diff; 43 | } 44 | } 45 | return -1; 46 | } 47 | *ptr++ = c; 48 | if (c == delimiter) { 49 | *ptr = '\0'; 50 | return ptr - *buf; 51 | } 52 | if (ptr + 2 >= eptr) { 53 | char *nbuf; 54 | size_t nbufsiz = *bufsiz * 2; 55 | ssize_t d = ptr - *buf; 56 | if ((nbuf = realloc(*buf, nbufsiz)) == NULL) 57 | return -1; 58 | *buf = nbuf; 59 | *bufsiz = nbufsiz; 60 | eptr = nbuf + nbufsiz; 61 | ptr = nbuf + d; 62 | } 63 | } 64 | } 65 | 66 | ssize_t getline(char **buf, size_t *bufsiz, FILE *fp) 67 | { 68 | return getdelim(buf, bufsiz, '\n', fp); 69 | } 70 | -------------------------------------------------------------------------------- /src/wincompat/loader.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | /* 3 | * Copyright (C) 2015-2021 Jason A. Donenfeld . All Rights Reserved. 4 | */ 5 | 6 | #include 7 | #include 8 | 9 | static FARPROC WINAPI delayed_load_library_hook(unsigned dliNotify, PDelayLoadInfo pdli) 10 | { 11 | HMODULE library; 12 | if (dliNotify != dliNotePreLoadLibrary) 13 | return NULL; 14 | library = LoadLibraryExA(pdli->szDll, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32); 15 | if (!library) 16 | abort(); 17 | return (FARPROC)library; 18 | } 19 | 20 | PfnDliHook __pfnDliNotifyHook2 = delayed_load_library_hook; 21 | -------------------------------------------------------------------------------- /src/wincompat/manifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/wincompat/resources.rc: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 2 | * 3 | * Copyright (C) 2020 Jason A. Donenfeld. All Rights Reserved. 4 | */ 5 | 6 | #include 7 | 8 | #pragma code_page(65001) // UTF-8 9 | 10 | LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL 11 | CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST manifest.xml 12 | 13 | #define STRINGIZE(x) #x 14 | #define EXPAND(x) STRINGIZE(x) 15 | 16 | VS_VERSION_INFO VERSIONINFO 17 | FILEOS VOS_NT_WINDOWS32 18 | FILETYPE VFT_APP 19 | FILESUBTYPE VFT2_UNKNOWN 20 | BEGIN 21 | BLOCK "StringFileInfo" 22 | BEGIN 23 | BLOCK "040904b0" 24 | BEGIN 25 | VALUE "CompanyName", "WireGuard LLC" 26 | VALUE "FileDescription", "WireGuard wg(8) CLI: Fast, Modern, Secure VPN Tunnel" 27 | VALUE "FileVersion", EXPAND(VERSION_STR) 28 | VALUE "InternalName", "wg" 29 | VALUE "LegalCopyright", "Copyright © 2015-2020 Jason A. Donenfeld . All Rights Reserved." 30 | VALUE "OriginalFilename", "wg.exe" 31 | VALUE "ProductName", "WireGuard" 32 | VALUE "ProductVersion", EXPAND(VERSION_STR) 33 | VALUE "Comments", "https://www.wireguard.com/" 34 | END 35 | END 36 | BLOCK "VarFileInfo" 37 | BEGIN 38 | VALUE "Translation", 0x409, 0x4b0 39 | END 40 | END 41 | --------------------------------------------------------------------------------