Page MenuHomeFreeBSD

D36952.id111765.diff
No OneTemporary

D36952.id111765.diff

This file is larger than 256 KB, so syntax highlighting was skipped.
diff --git a/contrib/wireguard-tools/Makefile b/contrib/wireguard-tools/Makefile
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/Makefile
@@ -0,0 +1,114 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+
+PKG_CONFIG ?= pkg-config
+PREFIX ?= /usr
+DESTDIR ?=
+SYSCONFDIR ?= /etc
+BINDIR ?= $(PREFIX)/bin
+LIBDIR ?= $(PREFIX)/lib
+MANDIR ?= $(PREFIX)/share/man
+BASHCOMPDIR ?= $(PREFIX)/share/bash-completion/completions
+SYSTEMDUNITDIR ?= $(shell $(PKG_CONFIG) --variable=systemdsystemunitdir systemd 2>/dev/null || echo "$(PREFIX)/lib/systemd/system")
+RUNSTATEDIR ?= /var/run
+WITH_BASHCOMPLETION ?=
+WITH_WGQUICK ?=
+WITH_SYSTEMDUNITS ?=
+
+ifeq ($(WITH_BASHCOMPLETION),)
+ifneq ($(strip $(wildcard $(BASHCOMPDIR))),)
+WITH_BASHCOMPLETION := yes
+endif
+endif
+ifeq ($(WITH_WGQUICK),)
+ifneq ($(strip $(wildcard $(BINDIR)/bash)),)
+WITH_WGQUICK := yes
+endif
+ifneq ($(strip $(wildcard $(DESTDIR)/bin/bash)),)
+WITH_WGQUICK := yes
+endif
+endif
+ifeq ($(WITH_SYSTEMDUNITS),)
+ifneq ($(strip $(wildcard $(SYSTEMDUNITDIR))),)
+WITH_SYSTEMDUNITS := yes
+endif
+endif
+
+PLATFORM ?= $(shell uname -s | tr '[:upper:]' '[:lower:]')
+
+CFLAGS ?= -O3
+ifneq ($(wildcard uapi/$(PLATFORM)/.),)
+CFLAGS += -idirafter uapi/$(PLATFORM)
+endif
+CFLAGS += -std=gnu99 -D_GNU_SOURCE
+CFLAGS += -Wall -Wextra
+CFLAGS += -MMD -MP
+CFLAGS += -DRUNSTATEDIR="\"$(RUNSTATEDIR)\""
+ifeq ($(DEBUG),yes)
+CFLAGS += -g
+endif
+WIREGUARD_TOOLS_VERSION = $(patsubst v%,%,$(shell GIT_DIR="$(PWD)/../.git" git describe --dirty 2>/dev/null))
+ifneq ($(WIREGUARD_TOOLS_VERSION),)
+CFLAGS += -D'WIREGUARD_TOOLS_VERSION="$(WIREGUARD_TOOLS_VERSION)"'
+endif
+ifeq ($(PLATFORM),freebsd)
+LDLIBS += -lnv
+endif
+ifeq ($(PLATFORM),haiku)
+LDLIBS += -lnetwork -lbsd
+endif
+ifeq ($(PLATFORM),windows)
+CC := x86_64-w64-mingw32-clang
+WINDRES := $(shell $(CC) $(CFLAGS) -print-prog-name=windres 2>/dev/null)
+CFLAGS += -Iwincompat/include -include wincompat/compat.h -DWINVER=0x0601 -D_WIN32_WINNT=0x0601 -flto
+LDLIBS += -lws2_32 -lsetupapi -lole32 -ladvapi32 -lntdll -Lwincompat
+LDFLAGS += -flto -Wl,--dynamicbase -Wl,--nxcompat -Wl,--tsaware -mconsole
+LDFLAGS += -Wl,--major-os-version=6 -Wl,--minor-os-version=1 -Wl,--major-subsystem-version=6 -Wl,--minor-subsystem-version=1
+# The use of -Wl,/delayload: here implies we're using llvm-mingw
+LDFLAGS += -Wl,/delayload:ws2_32.dll -Wl,/delayload:setupapi.dll -Wl,/delayload:ole32.dll -Wl,/delayload:advapi32.dll
+VERSION := $(patsubst "%",%,$(filter "%",$(file < version.h)))
+wg: wincompat/libc.o wincompat/init.o wincompat/loader.o wincompat/resources.o
+wincompat/resources.o: wincompat/resources.rc wincompat/manifest.xml
+ $(WINDRES) -DVERSION_STR=$(VERSION) -O coff -c 65001 -i $< -o $@
+endif
+
+ifneq ($(V),1)
+BUILT_IN_LINK.o := $(LINK.o)
+LINK.o = @echo " LD $@";
+LINK.o += $(BUILT_IN_LINK.o)
+BUILT_IN_COMPILE.c := $(COMPILE.c)
+COMPILE.c = @echo " CC $@";
+COMPILE.c += $(BUILT_IN_COMPILE.c)
+BUILT_IN_RM := $(RM)
+RM := @a() { echo " CLEAN $$@"; $(BUILT_IN_RM) "$$@"; }; a
+WINDRES := @a() { echo " WINDRES $${@: -1}"; $(WINDRES) "$$@"; }; a
+endif
+
+wg: $(sort $(patsubst %.c,%.o,$(wildcard *.c)))
+
+clean:
+ $(RM) wg *.o *.d $(wildcard wincompat/*.o wincompat/*.lib wincompat/*.dll)
+
+install: wg
+ @install -v -d "$(DESTDIR)$(BINDIR)" && install -v -m 0755 wg "$(DESTDIR)$(BINDIR)/wg"
+ @install -v -d "$(DESTDIR)$(MANDIR)/man8" && install -v -m 0644 man/wg.8 "$(DESTDIR)$(MANDIR)/man8/wg.8"
+ @[ "$(WITH_BASHCOMPLETION)" = "yes" ] || exit 0; \
+ install -v -d "$(DESTDIR)$(BASHCOMPDIR)" && install -v -m 0644 completion/wg.bash-completion "$(DESTDIR)$(BASHCOMPDIR)/wg"
+ @[ "$(WITH_WGQUICK)" = "yes" ] || exit 0; \
+ install -v -m 0755 wg-quick/$(PLATFORM).bash "$(DESTDIR)$(BINDIR)/wg-quick" && install -v -m 0700 -d "$(DESTDIR)$(SYSCONFDIR)/wireguard"
+ @[ "$(WITH_WGQUICK)" = "yes" ] || exit 0; \
+ install -v -m 0644 man/wg-quick.8 "$(DESTDIR)$(MANDIR)/man8/wg-quick.8"
+ @[ "$(WITH_WGQUICK)" = "yes" -a "$(WITH_BASHCOMPLETION)" = "yes" ] || exit 0; \
+ install -v -m 0644 completion/wg-quick.bash-completion "$(DESTDIR)$(BASHCOMPDIR)/wg-quick"
+ @[ "$(WITH_WGQUICK)" = "yes" -a "$(WITH_SYSTEMDUNITS)" = "yes" ] || exit 0; \
+ install -v -d "$(DESTDIR)$(SYSTEMDUNITDIR)" && install -v -m 0644 systemd/* "$(DESTDIR)$(SYSTEMDUNITDIR)/"
+
+check: clean
+ scan-build --html-title=wireguard-tools -maxloop 100 --view --keep-going $(MAKE) wg
+
+all: wg
+.DEFAULT_GOAL: all
+.PHONY: clean install check
+
+-include *.d
diff --git a/contrib/wireguard-tools/completion/wg-quick.bash-completion b/contrib/wireguard-tools/completion/wg-quick.bash-completion
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/completion/wg-quick.bash-completion
@@ -0,0 +1,43 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+
+_wg_quick_completion() {
+ local p i a search_paths old_glob
+ search_paths=( /etc/wireguard )
+
+ old_glob="$(shopt -p nullglob)"
+ shopt -s nullglob
+
+ [[ $OSTYPE == *freebsd* || $OSTYPE == *darwin* ]] && search_paths+=( /usr/local/etc/wireguard )
+
+ if [[ $COMP_CWORD -eq 1 ]]; then
+ COMPREPLY+=( $(compgen -W "up down" -- "${COMP_WORDS[1]}") )
+ elif [[ $COMP_CWORD -eq 2 ]]; then
+ if [[ ${COMP_WORDS[1]} == up ]]; then
+ for p in "${search_paths[@]}"; do
+ for i in "$p"/*.conf; do
+ i="${i##*/}"; i="${i%.conf}"
+ mapfile -t a < <(compgen -W "$i" -- "${COMP_WORDS[2]}")
+ COMPREPLY+=( "${a[@]}" )
+ done
+ done
+ mapfile -t a < <(compgen -f -X '!*.conf' -- "${COMP_WORDS[2]}")
+ COMPREPLY+=( "${a[@]}" )
+ mapfile -t a < <(compgen -d -- "${COMP_WORDS[2]}")
+ COMPREPLY+=( "${a[@]}" )
+ elif [[ ${COMP_WORDS[1]} == down ]]; then
+ if [[ $OSTYPE == *openbsd* || $OSTYPE == *darwin* ]]; then
+ for i in /var/run/wireguard/*.name; do
+ i="${i##*/}"; i="${i%.name}"
+ mapfile -t a < <(compgen -W "$i" -- "${COMP_WORDS[2]}")
+ COMPREPLY+=( "${a[@]}" )
+ done
+ else
+ COMPREPLY+=( $(compgen -W "$(wg show interfaces)" -- "${COMP_WORDS[2]}") )
+ fi
+ fi
+ fi
+ eval "$old_glob"
+}
+
+complete -o filenames -o nosort -F _wg_quick_completion wg-quick
diff --git a/contrib/wireguard-tools/completion/wg.bash-completion b/contrib/wireguard-tools/completion/wg.bash-completion
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/completion/wg.bash-completion
@@ -0,0 +1,99 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+
+_wg_completion() {
+ local a
+
+ if [[ $COMP_CWORD -eq 1 ]]; then
+ COMPREPLY+=( $(compgen -W "help show showconf set setconf addconf syncconf genkey genpsk pubkey" -- "${COMP_WORDS[1]}") )
+ return
+ fi
+ case "${COMP_WORDS[1]}" in
+ genkey|genpsk|pubkey|help) return; ;;
+ show|showconf|set|setconf|addconf|syncconf) ;;
+ *) return;
+ esac
+
+ if [[ $COMP_CWORD -eq 2 ]]; then
+ local extra
+ [[ ${COMP_WORDS[1]} == show ]] && extra=" all interfaces"
+ COMPREPLY+=( $(compgen -W "$(wg show interfaces 2>/dev/null)$extra" -- "${COMP_WORDS[2]}") )
+ return
+ fi
+
+ if [[ $COMP_CWORD -eq 3 && ${COMP_WORDS[1]} == show && ${COMP_WORDS[2]} != interfaces ]]; then
+ 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]}") )
+ return
+ fi
+
+ if [[ $COMP_CWORD -eq 3 && ( ${COMP_WORDS[1]} == setconf || ${COMP_WORDS[1]} == addconf || ${COMP_WORDS[1]} == syncconf) ]]; then
+ compopt -o filenames
+ mapfile -t a < <(compgen -f -- "${COMP_WORDS[3]}")
+ COMPREPLY+=( "${a[@]}" )
+ return
+ fi
+
+ [[ ${COMP_WORDS[1]} == set ]] || return
+
+ 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
+ for ((i=3;i<COMP_CWORD;i+=2)); do
+ [[ ${COMP_WORDS[i]} == listen-port ]] && has_listen_port=1
+ [[ ${COMP_WORDS[i]} == fwmark ]] && has_fwmark=1
+ [[ ${COMP_WORDS[i]} == private-key ]] && has_private_key=1
+ [[ ${COMP_WORDS[i]} == peer ]] && { has_peer=$i; break; }
+ done
+ if [[ $has_peer -eq 0 ]]; then
+ if ((COMP_CWORD % 2 != 0)); then
+ [[ $has_listen_port -eq 1 ]] || words+=( listen-port )
+ [[ $has_fwmark -eq 1 ]] || words+=( fwmark )
+ [[ $has_private_key -eq 1 ]] || words+=( private-key )
+ words+=( peer )
+ COMPREPLY+=( $(compgen -W "${words[*]}" -- "${COMP_WORDS[COMP_CWORD]}") )
+ elif [[ ${COMP_WORDS[COMP_CWORD-1]} == *-key ]]; then
+ compopt -o filenames
+ mapfile -t a < <(compgen -f -- "${COMP_WORDS[COMP_CWORD]}")
+ COMPREPLY+=( "${a[@]}" )
+ fi
+ return
+ fi
+
+ if [[ ${COMP_WORDS[COMP_CWORD-1]} == peer ]]; then
+ COMPREPLY+=( $(compgen -W "$(wg show "${COMP_WORDS[2]}" peers 2>/dev/null)" -- "${COMP_WORDS[COMP_CWORD]}") )
+ return
+ fi
+
+ for ((i=has_peer;i<COMP_CWORD;i++)); do
+ j=i
+ if [[ ${COMP_WORDS[i]} == peer ]]; then
+ has_remove=0
+ has_endpoint=0
+ has_persistent_keepalive=0
+ has_allowed_ips=0
+ has_preshared_key=0
+ [[ ${COMP_WORDS[i+2]} == = ]] && ((i+=2)) || ((i++))
+ continue
+ fi
+ [[ ${COMP_WORDS[i]} == remove ]] && has_remove=1
+ [[ ${COMP_WORDS[i]} == endpoint ]] && has_endpoint=1
+ [[ ${COMP_WORDS[i]} == persistent-keepalive ]] && has_persistent_keepalive=1
+ [[ ${COMP_WORDS[i]} == allowed-ips ]] && has_allowed_ips=1
+ [[ ${COMP_WORDS[i]} == preshared-key ]] && has_preshared_key=1
+
+ [[ ${COMP_WORDS[i]} == remove ]] || ((i++))
+ done
+
+ ((COMP_CWORD == j)) || return
+
+ if [[ $has_remove -ne 1 ]]; then
+ [[ $has_preshared_key -eq 1 ]] || words+=( preshared-key )
+ [[ $has_endpoint -eq 1 ]] || words+=( endpoint )
+ [[ $has_allowed_ips -eq 1 ]] || words+=( allowed-ips )
+ [[ $has_persistent_keepalive -eq 1 ]] || words+=( persistent-keepalive )
+ words+=( remove )
+ fi
+ words+=( peer )
+
+ COMPREPLY+=( $(compgen -W "${words[*]}" -- "${COMP_WORDS[COMP_CWORD]}") )
+}
+
+complete -o nosort -F _wg_completion wg
diff --git a/contrib/wireguard-tools/config.h b/contrib/wireguard-tools/config.h
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/config.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0 OR MIT */
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#ifndef CONFIG_H
+#define CONFIG_H
+
+#include <stdbool.h>
+
+struct wgdevice;
+struct wgpeer;
+struct wgallowedip;
+
+struct config_ctx {
+ struct wgdevice *device;
+ struct wgpeer *last_peer;
+ struct wgallowedip *last_allowedip;
+ bool is_peer_section, is_device_section;
+};
+
+struct wgdevice *config_read_cmd(const char *argv[], int argc);
+bool config_read_init(struct config_ctx *ctx, bool append);
+bool config_read_line(struct config_ctx *ctx, const char *line);
+struct wgdevice *config_read_finish(struct config_ctx *ctx);
+
+#endif
diff --git a/contrib/wireguard-tools/config.c b/contrib/wireguard-tools/config.c
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/config.c
@@ -0,0 +1,650 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#include <arpa/inet.h>
+#include <limits.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <errno.h>
+
+#include "config.h"
+#include "containers.h"
+#include "ipc.h"
+#include "encoding.h"
+#include "ctype.h"
+
+#define COMMENT_CHAR '#'
+
+static const char *get_value(const char *line, const char *key)
+{
+ size_t linelen = strlen(line);
+ size_t keylen = strlen(key);
+
+ if (keylen >= linelen)
+ return NULL;
+
+ if (strncasecmp(line, key, keylen))
+ return NULL;
+
+ return line + keylen;
+}
+
+static inline bool parse_port(uint16_t *port, uint32_t *flags, const char *value)
+{
+ int ret;
+ struct addrinfo *resolved;
+ struct addrinfo hints = {
+ .ai_family = AF_UNSPEC,
+ .ai_socktype = SOCK_DGRAM,
+ .ai_protocol = IPPROTO_UDP,
+ .ai_flags = AI_PASSIVE
+ };
+
+ if (!strlen(value)) {
+ fprintf(stderr, "Unable to parse empty port\n");
+ return false;
+ }
+
+ ret = getaddrinfo(NULL, value, &hints, &resolved);
+ if (ret) {
+ fprintf(stderr, "%s: `%s'\n", ret == EAI_SYSTEM ? strerror(errno) : gai_strerror(ret), value);
+ return false;
+ }
+
+ ret = -1;
+ if (resolved->ai_family == AF_INET && resolved->ai_addrlen == sizeof(struct sockaddr_in)) {
+ *port = ntohs(((struct sockaddr_in *)resolved->ai_addr)->sin_port);
+ ret = 0;
+ } else if (resolved->ai_family == AF_INET6 && resolved->ai_addrlen == sizeof(struct sockaddr_in6)) {
+ *port = ntohs(((struct sockaddr_in6 *)resolved->ai_addr)->sin6_port);
+ ret = 0;
+ } else
+ fprintf(stderr, "Neither IPv4 nor IPv6 address found: `%s'\n", value);
+
+ freeaddrinfo(resolved);
+ if (!ret)
+ *flags |= WGDEVICE_HAS_LISTEN_PORT;
+ return ret == 0;
+}
+
+static inline bool parse_fwmark(uint32_t *fwmark, uint32_t *flags, const char *value)
+{
+ unsigned long ret;
+ char *end;
+ int base = 10;
+
+ if (!strcasecmp(value, "off")) {
+ *fwmark = 0;
+ *flags |= WGDEVICE_HAS_FWMARK;
+ return true;
+ }
+
+ if (!char_is_digit(value[0]))
+ goto err;
+
+ if (strlen(value) > 2 && value[0] == '0' && value[1] == 'x')
+ base = 16;
+
+ ret = strtoul(value, &end, base);
+ if (*end || ret > UINT32_MAX)
+ goto err;
+
+ *fwmark = ret;
+ *flags |= WGDEVICE_HAS_FWMARK;
+ return true;
+err:
+ fprintf(stderr, "Fwmark is neither 0/off nor 0-0xffffffff: `%s'\n", value);
+ return false;
+}
+
+static inline bool parse_key(uint8_t key[static WG_KEY_LEN], const char *value)
+{
+ if (!key_from_base64(key, value)) {
+ fprintf(stderr, "Key is not the correct length or format: `%s'\n", value);
+ memset(key, 0, WG_KEY_LEN);
+ return false;
+ }
+ return true;
+}
+
+static bool parse_keyfile(uint8_t key[static WG_KEY_LEN], const char *path)
+{
+ FILE *f;
+ int c;
+ char dst[WG_KEY_LEN_BASE64];
+ bool ret = false;
+
+ f = fopen(path, "r");
+ if (!f) {
+ perror("fopen");
+ return false;
+ }
+
+ if (fread(dst, WG_KEY_LEN_BASE64 - 1, 1, f) != 1) {
+ /* If we're at the end and we didn't read anything, we're /dev/null or an empty file. */
+ if (!ferror(f) && feof(f) && !ftell(f)) {
+ memset(key, 0, WG_KEY_LEN);
+ ret = true;
+ goto out;
+ }
+
+ fprintf(stderr, "Invalid length key in key file\n");
+ goto out;
+ }
+ dst[WG_KEY_LEN_BASE64 - 1] = '\0';
+
+ while ((c = getc(f)) != EOF) {
+ if (!char_is_space(c)) {
+ fprintf(stderr, "Found trailing character in key file: `%c'\n", c);
+ goto out;
+ }
+ }
+ if (ferror(f) && errno) {
+ perror("getc");
+ goto out;
+ }
+ ret = parse_key(key, dst);
+
+out:
+ fclose(f);
+ return ret;
+}
+
+static inline bool parse_ip(struct wgallowedip *allowedip, const char *value)
+{
+ allowedip->family = AF_UNSPEC;
+ if (strchr(value, ':')) {
+ if (inet_pton(AF_INET6, value, &allowedip->ip6) == 1)
+ allowedip->family = AF_INET6;
+ } else {
+ if (inet_pton(AF_INET, value, &allowedip->ip4) == 1)
+ allowedip->family = AF_INET;
+ }
+ if (allowedip->family == AF_UNSPEC) {
+ fprintf(stderr, "Unable to parse IP address: `%s'\n", value);
+ return false;
+ }
+ return true;
+}
+
+static inline int parse_dns_retries(void)
+{
+ unsigned long ret;
+ char *retries = getenv("WG_ENDPOINT_RESOLUTION_RETRIES"), *end;
+
+ if (!retries)
+ return 15;
+ if (!strcmp(retries, "infinity"))
+ return -1;
+
+ ret = strtoul(retries, &end, 10);
+ if (*end || ret > INT_MAX) {
+ fprintf(stderr, "Unable to parse WG_ENDPOINT_RESOLUTION_RETRIES: `%s'\n", retries);
+ exit(1);
+ }
+ return (int)ret;
+}
+
+static inline bool parse_endpoint(struct sockaddr *endpoint, const char *value)
+{
+ char *mutable = strdup(value);
+ char *begin, *end;
+ int ret, retries = parse_dns_retries();
+ struct addrinfo *resolved;
+ struct addrinfo hints = {
+ .ai_family = AF_UNSPEC,
+ .ai_socktype = SOCK_DGRAM,
+ .ai_protocol = IPPROTO_UDP
+ };
+ if (!mutable) {
+ perror("strdup");
+ return false;
+ }
+ if (!strlen(value)) {
+ free(mutable);
+ fprintf(stderr, "Unable to parse empty endpoint\n");
+ return false;
+ }
+ if (mutable[0] == '[') {
+ begin = &mutable[1];
+ end = strchr(mutable, ']');
+ if (!end) {
+ free(mutable);
+ fprintf(stderr, "Unable to find matching brace of endpoint: `%s'\n", value);
+ return false;
+ }
+ *end++ = '\0';
+ if (*end++ != ':' || !*end) {
+ free(mutable);
+ fprintf(stderr, "Unable to find port of endpoint: `%s'\n", value);
+ return false;
+ }
+ } else {
+ begin = mutable;
+ end = strrchr(mutable, ':');
+ if (!end || !*(end + 1)) {
+ free(mutable);
+ fprintf(stderr, "Unable to find port of endpoint: `%s'\n", value);
+ return false;
+ }
+ *end++ = '\0';
+ }
+
+ #define min(a, b) ((a) < (b) ? (a) : (b))
+ for (unsigned int timeout = 1000000;; timeout = min(20000000, timeout * 6 / 5)) {
+ ret = getaddrinfo(begin, end, &hints, &resolved);
+ if (!ret)
+ break;
+ /* The set of return codes that are "permanent failures". All other possibilities are potentially transient.
+ *
+ * This is according to https://sourceware.org/glibc/wiki/NameResolver which states:
+ * "From the perspective of the application that calls getaddrinfo() it perhaps
+ * doesn't matter that much since EAI_FAIL, EAI_NONAME and EAI_NODATA are all
+ * permanent failure codes and the causes are all permanent failures in the
+ * sense that there is no point in retrying later."
+ *
+ * So this is what we do, except FreeBSD removed EAI_NODATA some time ago, so that's conditional.
+ */
+ if (ret == EAI_NONAME || ret == EAI_FAIL ||
+ #ifdef EAI_NODATA
+ ret == EAI_NODATA ||
+ #endif
+ (retries >= 0 && !retries--)) {
+ free(mutable);
+ fprintf(stderr, "%s: `%s'\n", ret == EAI_SYSTEM ? strerror(errno) : gai_strerror(ret), value);
+ return false;
+ }
+ fprintf(stderr, "%s: `%s'. Trying again in %.2f seconds...\n", ret == EAI_SYSTEM ? strerror(errno) : gai_strerror(ret), value, timeout / 1000000.0);
+ usleep(timeout);
+ }
+
+ if ((resolved->ai_family == AF_INET && resolved->ai_addrlen == sizeof(struct sockaddr_in)) ||
+ (resolved->ai_family == AF_INET6 && resolved->ai_addrlen == sizeof(struct sockaddr_in6)))
+ memcpy(endpoint, resolved->ai_addr, resolved->ai_addrlen);
+ else {
+ freeaddrinfo(resolved);
+ free(mutable);
+ fprintf(stderr, "Neither IPv4 nor IPv6 address found: `%s'\n", value);
+ return false;
+ }
+ freeaddrinfo(resolved);
+ free(mutable);
+ return true;
+}
+
+static inline bool parse_persistent_keepalive(uint16_t *interval, uint32_t *flags, const char *value)
+{
+ unsigned long ret;
+ char *end;
+
+ if (!strcasecmp(value, "off")) {
+ *interval = 0;
+ *flags |= WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL;
+ return true;
+ }
+
+ if (!char_is_digit(value[0]))
+ goto err;
+
+ ret = strtoul(value, &end, 10);
+ if (*end || ret > 65535)
+ goto err;
+
+ *interval = (uint16_t)ret;
+ *flags |= WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL;
+ return true;
+err:
+ fprintf(stderr, "Persistent keepalive interval is neither 0/off nor 1-65535: `%s'\n", value);
+ return false;
+}
+
+static bool validate_netmask(struct wgallowedip *allowedip)
+{
+ uint32_t *ip;
+ int last;
+
+ switch (allowedip->family) {
+ case AF_INET:
+ last = 0;
+ ip = (uint32_t *)&allowedip->ip4;
+ break;
+ case AF_INET6:
+ last = 3;
+ ip = (uint32_t *)&allowedip->ip6;
+ break;
+ default:
+ return true; /* We don't know how to validate it, so say 'okay'. */
+ }
+
+ for (int i = last; i >= 0; --i) {
+ uint32_t mask = ~0;
+
+ if (allowedip->cidr >= 32 * (i + 1))
+ break;
+ if (allowedip->cidr > 32 * i)
+ mask >>= (allowedip->cidr - 32 * i);
+ if (ntohl(ip[i]) & mask)
+ return false;
+ }
+
+ return true;
+}
+
+static inline bool parse_allowedips(struct wgpeer *peer, struct wgallowedip **last_allowedip, const char *value)
+{
+ struct wgallowedip *allowedip = *last_allowedip, *new_allowedip;
+ char *mask, *mutable = strdup(value), *sep, *saved_entry;
+
+ if (!mutable) {
+ perror("strdup");
+ return false;
+ }
+ peer->flags |= WGPEER_REPLACE_ALLOWEDIPS;
+ if (!strlen(value)) {
+ free(mutable);
+ return true;
+ }
+ sep = mutable;
+ while ((mask = strsep(&sep, ","))) {
+ unsigned long cidr;
+ char *end, *ip;
+
+ saved_entry = strdup(mask);
+ ip = strsep(&mask, "/");
+
+ new_allowedip = calloc(1, sizeof(*new_allowedip));
+ if (!new_allowedip) {
+ perror("calloc");
+ free(saved_entry);
+ free(mutable);
+ return false;
+ }
+
+ if (!parse_ip(new_allowedip, ip)) {
+ free(new_allowedip);
+ free(saved_entry);
+ free(mutable);
+ return false;
+ }
+
+ if (mask) {
+ if (!char_is_digit(mask[0]))
+ goto err;
+ cidr = strtoul(mask, &end, 10);
+ if (*end || (cidr > 32 && new_allowedip->family == AF_INET) || (cidr > 128 && new_allowedip->family == AF_INET6))
+ goto err;
+ } else if (new_allowedip->family == AF_INET)
+ cidr = 32;
+ else if (new_allowedip->family == AF_INET6)
+ cidr = 128;
+ else
+ goto err;
+ new_allowedip->cidr = cidr;
+
+ if (!validate_netmask(new_allowedip))
+ fprintf(stderr, "Warning: AllowedIP has nonzero host part: %s/%s\n", ip, mask);
+
+ if (allowedip)
+ allowedip->next_allowedip = new_allowedip;
+ else
+ peer->first_allowedip = new_allowedip;
+ allowedip = new_allowedip;
+ free(saved_entry);
+ }
+ free(mutable);
+ *last_allowedip = allowedip;
+ return true;
+
+err:
+ free(new_allowedip);
+ free(mutable);
+ fprintf(stderr, "AllowedIP is not in the correct format: `%s'\n", saved_entry);
+ free(saved_entry);
+ return false;
+}
+
+static bool process_line(struct config_ctx *ctx, const char *line)
+{
+ const char *value;
+ bool ret = true;
+
+ if (!strcasecmp(line, "[Interface]")) {
+ ctx->is_peer_section = false;
+ ctx->is_device_section = true;
+ return true;
+ }
+ if (!strcasecmp(line, "[Peer]")) {
+ struct wgpeer *new_peer = calloc(1, sizeof(struct wgpeer));
+
+ if (!new_peer) {
+ perror("calloc");
+ return false;
+ }
+ ctx->last_allowedip = NULL;
+ if (ctx->last_peer)
+ ctx->last_peer->next_peer = new_peer;
+ else
+ ctx->device->first_peer = new_peer;
+ ctx->last_peer = new_peer;
+ ctx->is_peer_section = true;
+ ctx->is_device_section = false;
+ ctx->last_peer->flags |= WGPEER_REPLACE_ALLOWEDIPS;
+ return true;
+ }
+
+#define key_match(key) (value = get_value(line, key "="))
+
+ if (ctx->is_device_section) {
+ if (key_match("ListenPort"))
+ ret = parse_port(&ctx->device->listen_port, &ctx->device->flags, value);
+ else if (key_match("FwMark"))
+ ret = parse_fwmark(&ctx->device->fwmark, &ctx->device->flags, value);
+ else if (key_match("PrivateKey")) {
+ ret = parse_key(ctx->device->private_key, value);
+ if (ret)
+ ctx->device->flags |= WGDEVICE_HAS_PRIVATE_KEY;
+ } else
+ goto error;
+ } else if (ctx->is_peer_section) {
+ if (key_match("Endpoint"))
+ ret = parse_endpoint(&ctx->last_peer->endpoint.addr, value);
+ else if (key_match("PublicKey")) {
+ ret = parse_key(ctx->last_peer->public_key, value);
+ if (ret)
+ ctx->last_peer->flags |= WGPEER_HAS_PUBLIC_KEY;
+ } else if (key_match("AllowedIPs"))
+ ret = parse_allowedips(ctx->last_peer, &ctx->last_allowedip, value);
+ else if (key_match("PersistentKeepalive"))
+ ret = parse_persistent_keepalive(&ctx->last_peer->persistent_keepalive_interval, &ctx->last_peer->flags, value);
+ else if (key_match("PresharedKey")) {
+ ret = parse_key(ctx->last_peer->preshared_key, value);
+ if (ret)
+ ctx->last_peer->flags |= WGPEER_HAS_PRESHARED_KEY;
+ } else
+ goto error;
+ } else
+ goto error;
+ return ret;
+
+#undef key_match
+
+error:
+ fprintf(stderr, "Line unrecognized: `%s'\n", line);
+ return false;
+}
+
+bool config_read_line(struct config_ctx *ctx, const char *input)
+{
+ size_t len, cleaned_len = 0;
+ char *line, *comment;
+ bool ret = true;
+
+ /* This is what strchrnul is for, but that isn't portable. */
+ comment = strchr(input, COMMENT_CHAR);
+ if (comment)
+ len = comment - input;
+ else
+ len = strlen(input);
+
+ line = calloc(len + 1, sizeof(char));
+ if (!line) {
+ perror("calloc");
+ ret = false;
+ goto out;
+ }
+
+ for (size_t i = 0; i < len; ++i) {
+ if (!char_is_space(input[i]))
+ line[cleaned_len++] = input[i];
+ }
+ if (!cleaned_len)
+ goto out;
+ ret = process_line(ctx, line);
+out:
+ free(line);
+ if (!ret)
+ free_wgdevice(ctx->device);
+ return ret;
+}
+
+bool config_read_init(struct config_ctx *ctx, bool append)
+{
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->device = calloc(1, sizeof(*ctx->device));
+ if (!ctx->device) {
+ perror("calloc");
+ return false;
+ }
+ if (!append)
+ ctx->device->flags |= WGDEVICE_REPLACE_PEERS | WGDEVICE_HAS_PRIVATE_KEY | WGDEVICE_HAS_FWMARK | WGDEVICE_HAS_LISTEN_PORT;
+ return true;
+}
+
+struct wgdevice *config_read_finish(struct config_ctx *ctx)
+{
+ struct wgpeer *peer;
+
+ for_each_wgpeer(ctx->device, peer) {
+ if (!(peer->flags & WGPEER_HAS_PUBLIC_KEY)) {
+ fprintf(stderr, "A peer is missing a public key\n");
+ goto err;
+ }
+ }
+ return ctx->device;
+err:
+ free_wgdevice(ctx->device);
+ return NULL;
+}
+
+static char *strip_spaces(const char *in)
+{
+ char *out;
+ size_t t, l, i;
+
+ t = strlen(in);
+ out = calloc(t + 1, sizeof(char));
+ if (!out) {
+ perror("calloc");
+ return NULL;
+ }
+ for (i = 0, l = 0; i < t; ++i) {
+ if (!char_is_space(in[i]))
+ out[l++] = in[i];
+ }
+ return out;
+}
+
+struct wgdevice *config_read_cmd(const char *argv[], int argc)
+{
+ struct wgdevice *device = calloc(1, sizeof(*device));
+ struct wgpeer *peer = NULL;
+ struct wgallowedip *allowedip = NULL;
+
+ if (!device) {
+ perror("calloc");
+ return false;
+ }
+ while (argc > 0) {
+ if (!strcmp(argv[0], "listen-port") && argc >= 2 && !peer) {
+ if (!parse_port(&device->listen_port, &device->flags, argv[1]))
+ goto error;
+ argv += 2;
+ argc -= 2;
+ } else if (!strcmp(argv[0], "fwmark") && argc >= 2 && !peer) {
+ if (!parse_fwmark(&device->fwmark, &device->flags, argv[1]))
+ goto error;
+ argv += 2;
+ argc -= 2;
+ } else if (!strcmp(argv[0], "private-key") && argc >= 2 && !peer) {
+ if (!parse_keyfile(device->private_key, argv[1]))
+ goto error;
+ device->flags |= WGDEVICE_HAS_PRIVATE_KEY;
+ argv += 2;
+ argc -= 2;
+ } else if (!strcmp(argv[0], "peer") && argc >= 2) {
+ struct wgpeer *new_peer = calloc(1, sizeof(*new_peer));
+
+ allowedip = NULL;
+ if (!new_peer) {
+ perror("calloc");
+ goto error;
+ }
+ if (peer)
+ peer->next_peer = new_peer;
+ else
+ device->first_peer = new_peer;
+ peer = new_peer;
+ if (!parse_key(peer->public_key, argv[1]))
+ goto error;
+ peer->flags |= WGPEER_HAS_PUBLIC_KEY;
+ argv += 2;
+ argc -= 2;
+ } else if (!strcmp(argv[0], "remove") && argc >= 1 && peer) {
+ peer->flags |= WGPEER_REMOVE_ME;
+ argv += 1;
+ argc -= 1;
+ } else if (!strcmp(argv[0], "endpoint") && argc >= 2 && peer) {
+ if (!parse_endpoint(&peer->endpoint.addr, argv[1]))
+ goto error;
+ argv += 2;
+ argc -= 2;
+ } else if (!strcmp(argv[0], "allowed-ips") && argc >= 2 && peer) {
+ char *line = strip_spaces(argv[1]);
+
+ if (!line)
+ goto error;
+ if (!parse_allowedips(peer, &allowedip, line)) {
+ free(line);
+ goto error;
+ }
+ free(line);
+ argv += 2;
+ argc -= 2;
+ } else if (!strcmp(argv[0], "persistent-keepalive") && argc >= 2 && peer) {
+ if (!parse_persistent_keepalive(&peer->persistent_keepalive_interval, &peer->flags, argv[1]))
+ goto error;
+ argv += 2;
+ argc -= 2;
+ } else if (!strcmp(argv[0], "preshared-key") && argc >= 2 && peer) {
+ if (!parse_keyfile(peer->preshared_key, argv[1]))
+ goto error;
+ peer->flags |= WGPEER_HAS_PRESHARED_KEY;
+ argv += 2;
+ argc -= 2;
+ } else {
+ fprintf(stderr, "Invalid argument: %s\n", argv[0]);
+ goto error;
+ }
+ }
+ return device;
+error:
+ free_wgdevice(device);
+ return false;
+}
diff --git a/contrib/wireguard-tools/containers.h b/contrib/wireguard-tools/containers.h
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/containers.h
@@ -0,0 +1,107 @@
+/* SPDX-License-Identifier: GPL-2.0 OR MIT */
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#ifndef CONTAINERS_H
+#define CONTAINERS_H
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <time.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#if defined(__linux__)
+#include <linux/wireguard.h>
+#elif defined(__OpenBSD__)
+#include <net/if_wg.h>
+#endif
+
+#ifndef WG_KEY_LEN
+#define WG_KEY_LEN 32
+#endif
+
+/* Cross platform __kernel_timespec */
+struct timespec64 {
+ int64_t tv_sec;
+ int64_t tv_nsec;
+};
+
+struct wgallowedip {
+ uint16_t family;
+ union {
+ struct in_addr ip4;
+ struct in6_addr ip6;
+ };
+ uint8_t cidr;
+ struct wgallowedip *next_allowedip;
+};
+
+enum {
+ WGPEER_REMOVE_ME = 1U << 0,
+ WGPEER_REPLACE_ALLOWEDIPS = 1U << 1,
+ WGPEER_HAS_PUBLIC_KEY = 1U << 2,
+ WGPEER_HAS_PRESHARED_KEY = 1U << 3,
+ WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL = 1U << 4
+};
+
+struct wgpeer {
+ uint32_t flags;
+
+ uint8_t public_key[WG_KEY_LEN];
+ uint8_t preshared_key[WG_KEY_LEN];
+
+ union {
+ struct sockaddr addr;
+ struct sockaddr_in addr4;
+ struct sockaddr_in6 addr6;
+ } endpoint;
+
+ struct timespec64 last_handshake_time;
+ uint64_t rx_bytes, tx_bytes;
+ uint16_t persistent_keepalive_interval;
+
+ struct wgallowedip *first_allowedip, *last_allowedip;
+ struct wgpeer *next_peer;
+};
+
+enum {
+ WGDEVICE_REPLACE_PEERS = 1U << 0,
+ WGDEVICE_HAS_PRIVATE_KEY = 1U << 1,
+ WGDEVICE_HAS_PUBLIC_KEY = 1U << 2,
+ WGDEVICE_HAS_LISTEN_PORT = 1U << 3,
+ WGDEVICE_HAS_FWMARK = 1U << 4
+};
+
+struct wgdevice {
+ char name[IFNAMSIZ];
+ uint32_t ifindex;
+
+ uint32_t flags;
+
+ uint8_t public_key[WG_KEY_LEN];
+ uint8_t private_key[WG_KEY_LEN];
+
+ uint32_t fwmark;
+ uint16_t listen_port;
+
+ struct wgpeer *first_peer, *last_peer;
+};
+
+#define for_each_wgpeer(__dev, __peer) for ((__peer) = (__dev)->first_peer; (__peer); (__peer) = (__peer)->next_peer)
+#define for_each_wgallowedip(__peer, __allowedip) for ((__allowedip) = (__peer)->first_allowedip; (__allowedip); (__allowedip) = (__allowedip)->next_allowedip)
+
+static inline void free_wgdevice(struct wgdevice *dev)
+{
+ if (!dev)
+ return;
+ for (struct wgpeer *peer = dev->first_peer, *np = peer ? peer->next_peer : NULL; peer; peer = np, np = peer ? peer->next_peer : NULL) {
+ for (struct wgallowedip *allowedip = peer->first_allowedip, *na = allowedip ? allowedip->next_allowedip : NULL; allowedip; allowedip = na, na = allowedip ? allowedip->next_allowedip : NULL)
+ free(allowedip);
+ free(peer);
+ }
+ free(dev);
+}
+
+#endif
diff --git a/contrib/wireguard-tools/ctype.h b/contrib/wireguard-tools/ctype.h
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/ctype.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0 OR MIT */
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ *
+ * Specialized constant-time ctype.h reimplementations that aren't locale-specific.
+ */
+
+#ifndef CTYPE_H
+#define CTYPE_H
+
+#include <stdbool.h>
+
+static inline bool char_is_space(int c)
+{
+ unsigned char d = c - 9;
+ return (0x80001FU >> (d & 31)) & (1U >> (d >> 5));
+}
+
+static inline bool char_is_digit(int c)
+{
+ return (unsigned int)(('0' - 1 - c) & (c - ('9' + 1))) >> (sizeof(c) * 8 - 1);
+}
+
+static inline bool char_is_alpha(int c)
+{
+ return (unsigned int)(('a' - 1 - (c | 32)) & ((c | 32) - ('z' + 1))) >> (sizeof(c) * 8 - 1);
+}
+
+#endif
diff --git a/contrib/wireguard-tools/curve25519-fiat32.h b/contrib/wireguard-tools/curve25519-fiat32.h
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/curve25519-fiat32.h
@@ -0,0 +1,860 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * Copyright (C) 2015-2016 The fiat-crypto Authors.
+ * Copyright (C) 2018-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ *
+ * This is a machine-generated formally verified implementation of Curve25519
+ * ECDH from: <https://github.com/mit-plv/fiat-crypto>. Though originally
+ * machine generated, it has been tweaked to be suitable for use in the kernel.
+ * It is optimized for 32-bit machines and machines that cannot work efficiently
+ * with 128-bit integer types.
+ */
+
+/* fe means field element. Here the field is \Z/(2^255-19). An element t,
+ * entries t[0]...t[9], represents the integer t[0]+2^26 t[1]+2^51 t[2]+2^77
+ * t[3]+2^102 t[4]+...+2^230 t[9].
+ * fe limbs are bounded by 1.125*2^26,1.125*2^25,1.125*2^26,1.125*2^25,etc.
+ * Multiplication and carrying produce fe from fe_loose.
+ */
+typedef struct fe { u32 v[10]; } fe;
+
+/* fe_loose limbs are bounded by 3.375*2^26,3.375*2^25,3.375*2^26,3.375*2^25,etc
+ * Addition and subtraction produce fe_loose from (fe, fe).
+ */
+typedef struct fe_loose { u32 v[10]; } fe_loose;
+
+static __always_inline void fe_frombytes_impl(u32 h[10], const u8 *s)
+{
+ /* Ignores top bit of s. */
+ u32 a0 = get_unaligned_le32(s);
+ u32 a1 = get_unaligned_le32(s+4);
+ u32 a2 = get_unaligned_le32(s+8);
+ u32 a3 = get_unaligned_le32(s+12);
+ u32 a4 = get_unaligned_le32(s+16);
+ u32 a5 = get_unaligned_le32(s+20);
+ u32 a6 = get_unaligned_le32(s+24);
+ u32 a7 = get_unaligned_le32(s+28);
+ h[0] = a0&((1<<26)-1); /* 26 used, 32-26 left. 26 */
+ h[1] = (a0>>26) | ((a1&((1<<19)-1))<< 6); /* (32-26) + 19 = 6+19 = 25 */
+ h[2] = (a1>>19) | ((a2&((1<<13)-1))<<13); /* (32-19) + 13 = 13+13 = 26 */
+ h[3] = (a2>>13) | ((a3&((1<< 6)-1))<<19); /* (32-13) + 6 = 19+ 6 = 25 */
+ h[4] = (a3>> 6); /* (32- 6) = 26 */
+ h[5] = a4&((1<<25)-1); /* 25 */
+ h[6] = (a4>>25) | ((a5&((1<<19)-1))<< 7); /* (32-25) + 19 = 7+19 = 26 */
+ h[7] = (a5>>19) | ((a6&((1<<12)-1))<<13); /* (32-19) + 12 = 13+12 = 25 */
+ h[8] = (a6>>12) | ((a7&((1<< 6)-1))<<20); /* (32-12) + 6 = 20+ 6 = 26 */
+ h[9] = (a7>> 6)&((1<<25)-1); /* 25 */
+}
+
+static __always_inline void fe_frombytes(fe *h, const u8 *s)
+{
+ fe_frombytes_impl(h->v, s);
+}
+
+static __always_inline u8 /*bool*/
+addcarryx_u25(u8 /*bool*/ c, u32 a, u32 b, u32 *low)
+{
+ /* This function extracts 25 bits of result and 1 bit of carry
+ * (26 total), so a 32-bit intermediate is sufficient.
+ */
+ u32 x = a + b + c;
+ *low = x & ((1 << 25) - 1);
+ return (x >> 25) & 1;
+}
+
+static __always_inline u8 /*bool*/
+addcarryx_u26(u8 /*bool*/ c, u32 a, u32 b, u32 *low)
+{
+ /* This function extracts 26 bits of result and 1 bit of carry
+ * (27 total), so a 32-bit intermediate is sufficient.
+ */
+ u32 x = a + b + c;
+ *low = x & ((1 << 26) - 1);
+ return (x >> 26) & 1;
+}
+
+static __always_inline u8 /*bool*/
+subborrow_u25(u8 /*bool*/ c, u32 a, u32 b, u32 *low)
+{
+ /* This function extracts 25 bits of result and 1 bit of borrow
+ * (26 total), so a 32-bit intermediate is sufficient.
+ */
+ u32 x = a - b - c;
+ *low = x & ((1 << 25) - 1);
+ return x >> 31;
+}
+
+static __always_inline u8 /*bool*/
+subborrow_u26(u8 /*bool*/ c, u32 a, u32 b, u32 *low)
+{
+ /* This function extracts 26 bits of result and 1 bit of borrow
+ *(27 total), so a 32-bit intermediate is sufficient.
+ */
+ u32 x = a - b - c;
+ *low = x & ((1 << 26) - 1);
+ return x >> 31;
+}
+
+static __always_inline u32 cmovznz32(u32 t, u32 z, u32 nz)
+{
+ t = -!!t; /* all set if nonzero, 0 if 0 */
+ return (t&nz) | ((~t)&z);
+}
+
+static __always_inline void fe_freeze(u32 out[10], const u32 in1[10])
+{
+ { const u32 x17 = in1[9];
+ { const u32 x18 = in1[8];
+ { const u32 x16 = in1[7];
+ { const u32 x14 = in1[6];
+ { const u32 x12 = in1[5];
+ { const u32 x10 = in1[4];
+ { const u32 x8 = in1[3];
+ { const u32 x6 = in1[2];
+ { const u32 x4 = in1[1];
+ { const u32 x2 = in1[0];
+ { u32 x20; u8/*bool*/ x21 = subborrow_u26(0x0, x2, 0x3ffffed, &x20);
+ { u32 x23; u8/*bool*/ x24 = subborrow_u25(x21, x4, 0x1ffffff, &x23);
+ { u32 x26; u8/*bool*/ x27 = subborrow_u26(x24, x6, 0x3ffffff, &x26);
+ { u32 x29; u8/*bool*/ x30 = subborrow_u25(x27, x8, 0x1ffffff, &x29);
+ { u32 x32; u8/*bool*/ x33 = subborrow_u26(x30, x10, 0x3ffffff, &x32);
+ { u32 x35; u8/*bool*/ x36 = subborrow_u25(x33, x12, 0x1ffffff, &x35);
+ { u32 x38; u8/*bool*/ x39 = subborrow_u26(x36, x14, 0x3ffffff, &x38);
+ { u32 x41; u8/*bool*/ x42 = subborrow_u25(x39, x16, 0x1ffffff, &x41);
+ { u32 x44; u8/*bool*/ x45 = subborrow_u26(x42, x18, 0x3ffffff, &x44);
+ { u32 x47; u8/*bool*/ x48 = subborrow_u25(x45, x17, 0x1ffffff, &x47);
+ { u32 x49 = cmovznz32(x48, 0x0, 0xffffffff);
+ { u32 x50 = (x49 & 0x3ffffed);
+ { u32 x52; u8/*bool*/ x53 = addcarryx_u26(0x0, x20, x50, &x52);
+ { u32 x54 = (x49 & 0x1ffffff);
+ { u32 x56; u8/*bool*/ x57 = addcarryx_u25(x53, x23, x54, &x56);
+ { u32 x58 = (x49 & 0x3ffffff);
+ { u32 x60; u8/*bool*/ x61 = addcarryx_u26(x57, x26, x58, &x60);
+ { u32 x62 = (x49 & 0x1ffffff);
+ { u32 x64; u8/*bool*/ x65 = addcarryx_u25(x61, x29, x62, &x64);
+ { u32 x66 = (x49 & 0x3ffffff);
+ { u32 x68; u8/*bool*/ x69 = addcarryx_u26(x65, x32, x66, &x68);
+ { u32 x70 = (x49 & 0x1ffffff);
+ { u32 x72; u8/*bool*/ x73 = addcarryx_u25(x69, x35, x70, &x72);
+ { u32 x74 = (x49 & 0x3ffffff);
+ { u32 x76; u8/*bool*/ x77 = addcarryx_u26(x73, x38, x74, &x76);
+ { u32 x78 = (x49 & 0x1ffffff);
+ { u32 x80; u8/*bool*/ x81 = addcarryx_u25(x77, x41, x78, &x80);
+ { u32 x82 = (x49 & 0x3ffffff);
+ { u32 x84; u8/*bool*/ x85 = addcarryx_u26(x81, x44, x82, &x84);
+ { u32 x86 = (x49 & 0x1ffffff);
+ { u32 x88; addcarryx_u25(x85, x47, x86, &x88);
+ out[0] = x52;
+ out[1] = x56;
+ out[2] = x60;
+ out[3] = x64;
+ out[4] = x68;
+ out[5] = x72;
+ out[6] = x76;
+ out[7] = x80;
+ out[8] = x84;
+ out[9] = x88;
+ }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
+}
+
+static __always_inline void fe_tobytes(u8 s[32], const fe *f)
+{
+ u32 h[10];
+ fe_freeze(h, f->v);
+ s[0] = h[0] >> 0;
+ s[1] = h[0] >> 8;
+ s[2] = h[0] >> 16;
+ s[3] = (h[0] >> 24) | (h[1] << 2);
+ s[4] = h[1] >> 6;
+ s[5] = h[1] >> 14;
+ s[6] = (h[1] >> 22) | (h[2] << 3);
+ s[7] = h[2] >> 5;
+ s[8] = h[2] >> 13;
+ s[9] = (h[2] >> 21) | (h[3] << 5);
+ s[10] = h[3] >> 3;
+ s[11] = h[3] >> 11;
+ s[12] = (h[3] >> 19) | (h[4] << 6);
+ s[13] = h[4] >> 2;
+ s[14] = h[4] >> 10;
+ s[15] = h[4] >> 18;
+ s[16] = h[5] >> 0;
+ s[17] = h[5] >> 8;
+ s[18] = h[5] >> 16;
+ s[19] = (h[5] >> 24) | (h[6] << 1);
+ s[20] = h[6] >> 7;
+ s[21] = h[6] >> 15;
+ s[22] = (h[6] >> 23) | (h[7] << 3);
+ s[23] = h[7] >> 5;
+ s[24] = h[7] >> 13;
+ s[25] = (h[7] >> 21) | (h[8] << 4);
+ s[26] = h[8] >> 4;
+ s[27] = h[8] >> 12;
+ s[28] = (h[8] >> 20) | (h[9] << 6);
+ s[29] = h[9] >> 2;
+ s[30] = h[9] >> 10;
+ s[31] = h[9] >> 18;
+}
+
+/* h = f */
+static __always_inline void fe_copy(fe *h, const fe *f)
+{
+ memmove(h, f, sizeof(u32) * 10);
+}
+
+static __always_inline void fe_copy_lt(fe_loose *h, const fe *f)
+{
+ memmove(h, f, sizeof(u32) * 10);
+}
+
+/* h = 0 */
+static __always_inline void fe_0(fe *h)
+{
+ memset(h, 0, sizeof(u32) * 10);
+}
+
+/* h = 1 */
+static __always_inline void fe_1(fe *h)
+{
+ memset(h, 0, sizeof(u32) * 10);
+ h->v[0] = 1;
+}
+
+static void fe_add_impl(u32 out[10], const u32 in1[10], const u32 in2[10])
+{
+ { const u32 x20 = in1[9];
+ { const u32 x21 = in1[8];
+ { const u32 x19 = in1[7];
+ { const u32 x17 = in1[6];
+ { const u32 x15 = in1[5];
+ { const u32 x13 = in1[4];
+ { const u32 x11 = in1[3];
+ { const u32 x9 = in1[2];
+ { const u32 x7 = in1[1];
+ { const u32 x5 = in1[0];
+ { const u32 x38 = in2[9];
+ { const u32 x39 = in2[8];
+ { const u32 x37 = in2[7];
+ { const u32 x35 = in2[6];
+ { const u32 x33 = in2[5];
+ { const u32 x31 = in2[4];
+ { const u32 x29 = in2[3];
+ { const u32 x27 = in2[2];
+ { const u32 x25 = in2[1];
+ { const u32 x23 = in2[0];
+ out[0] = (x5 + x23);
+ out[1] = (x7 + x25);
+ out[2] = (x9 + x27);
+ out[3] = (x11 + x29);
+ out[4] = (x13 + x31);
+ out[5] = (x15 + x33);
+ out[6] = (x17 + x35);
+ out[7] = (x19 + x37);
+ out[8] = (x21 + x39);
+ out[9] = (x20 + x38);
+ }}}}}}}}}}}}}}}}}}}}
+}
+
+/* h = f + g
+ * Can overlap h with f or g.
+ */
+static __always_inline void fe_add(fe_loose *h, const fe *f, const fe *g)
+{
+ fe_add_impl(h->v, f->v, g->v);
+}
+
+static void fe_sub_impl(u32 out[10], const u32 in1[10], const u32 in2[10])
+{
+ { const u32 x20 = in1[9];
+ { const u32 x21 = in1[8];
+ { const u32 x19 = in1[7];
+ { const u32 x17 = in1[6];
+ { const u32 x15 = in1[5];
+ { const u32 x13 = in1[4];
+ { const u32 x11 = in1[3];
+ { const u32 x9 = in1[2];
+ { const u32 x7 = in1[1];
+ { const u32 x5 = in1[0];
+ { const u32 x38 = in2[9];
+ { const u32 x39 = in2[8];
+ { const u32 x37 = in2[7];
+ { const u32 x35 = in2[6];
+ { const u32 x33 = in2[5];
+ { const u32 x31 = in2[4];
+ { const u32 x29 = in2[3];
+ { const u32 x27 = in2[2];
+ { const u32 x25 = in2[1];
+ { const u32 x23 = in2[0];
+ out[0] = ((0x7ffffda + x5) - x23);
+ out[1] = ((0x3fffffe + x7) - x25);
+ out[2] = ((0x7fffffe + x9) - x27);
+ out[3] = ((0x3fffffe + x11) - x29);
+ out[4] = ((0x7fffffe + x13) - x31);
+ out[5] = ((0x3fffffe + x15) - x33);
+ out[6] = ((0x7fffffe + x17) - x35);
+ out[7] = ((0x3fffffe + x19) - x37);
+ out[8] = ((0x7fffffe + x21) - x39);
+ out[9] = ((0x3fffffe + x20) - x38);
+ }}}}}}}}}}}}}}}}}}}}
+}
+
+/* h = f - g
+ * Can overlap h with f or g.
+ */
+static __always_inline void fe_sub(fe_loose *h, const fe *f, const fe *g)
+{
+ fe_sub_impl(h->v, f->v, g->v);
+}
+
+static void fe_mul_impl(u32 out[10], const u32 in1[10], const u32 in2[10])
+{
+ { const u32 x20 = in1[9];
+ { const u32 x21 = in1[8];
+ { const u32 x19 = in1[7];
+ { const u32 x17 = in1[6];
+ { const u32 x15 = in1[5];
+ { const u32 x13 = in1[4];
+ { const u32 x11 = in1[3];
+ { const u32 x9 = in1[2];
+ { const u32 x7 = in1[1];
+ { const u32 x5 = in1[0];
+ { const u32 x38 = in2[9];
+ { const u32 x39 = in2[8];
+ { const u32 x37 = in2[7];
+ { const u32 x35 = in2[6];
+ { const u32 x33 = in2[5];
+ { const u32 x31 = in2[4];
+ { const u32 x29 = in2[3];
+ { const u32 x27 = in2[2];
+ { const u32 x25 = in2[1];
+ { const u32 x23 = in2[0];
+ { u64 x40 = ((u64)x23 * x5);
+ { u64 x41 = (((u64)x23 * x7) + ((u64)x25 * x5));
+ { u64 x42 = ((((u64)(0x2 * x25) * x7) + ((u64)x23 * x9)) + ((u64)x27 * x5));
+ { u64 x43 = (((((u64)x25 * x9) + ((u64)x27 * x7)) + ((u64)x23 * x11)) + ((u64)x29 * x5));
+ { u64 x44 = (((((u64)x27 * x9) + (0x2 * (((u64)x25 * x11) + ((u64)x29 * x7)))) + ((u64)x23 * x13)) + ((u64)x31 * x5));
+ { u64 x45 = (((((((u64)x27 * x11) + ((u64)x29 * x9)) + ((u64)x25 * x13)) + ((u64)x31 * x7)) + ((u64)x23 * x15)) + ((u64)x33 * x5));
+ { u64 x46 = (((((0x2 * ((((u64)x29 * x11) + ((u64)x25 * x15)) + ((u64)x33 * x7))) + ((u64)x27 * x13)) + ((u64)x31 * x9)) + ((u64)x23 * x17)) + ((u64)x35 * x5));
+ { u64 x47 = (((((((((u64)x29 * x13) + ((u64)x31 * x11)) + ((u64)x27 * x15)) + ((u64)x33 * x9)) + ((u64)x25 * x17)) + ((u64)x35 * x7)) + ((u64)x23 * x19)) + ((u64)x37 * x5));
+ { u64 x48 = (((((((u64)x31 * x13) + (0x2 * (((((u64)x29 * x15) + ((u64)x33 * x11)) + ((u64)x25 * x19)) + ((u64)x37 * x7)))) + ((u64)x27 * x17)) + ((u64)x35 * x9)) + ((u64)x23 * x21)) + ((u64)x39 * x5));
+ { u64 x49 = (((((((((((u64)x31 * x15) + ((u64)x33 * x13)) + ((u64)x29 * x17)) + ((u64)x35 * x11)) + ((u64)x27 * x19)) + ((u64)x37 * x9)) + ((u64)x25 * x21)) + ((u64)x39 * x7)) + ((u64)x23 * x20)) + ((u64)x38 * x5));
+ { u64 x50 = (((((0x2 * ((((((u64)x33 * x15) + ((u64)x29 * x19)) + ((u64)x37 * x11)) + ((u64)x25 * x20)) + ((u64)x38 * x7))) + ((u64)x31 * x17)) + ((u64)x35 * x13)) + ((u64)x27 * x21)) + ((u64)x39 * x9));
+ { u64 x51 = (((((((((u64)x33 * x17) + ((u64)x35 * x15)) + ((u64)x31 * x19)) + ((u64)x37 * x13)) + ((u64)x29 * x21)) + ((u64)x39 * x11)) + ((u64)x27 * x20)) + ((u64)x38 * x9));
+ { u64 x52 = (((((u64)x35 * x17) + (0x2 * (((((u64)x33 * x19) + ((u64)x37 * x15)) + ((u64)x29 * x20)) + ((u64)x38 * x11)))) + ((u64)x31 * x21)) + ((u64)x39 * x13));
+ { u64 x53 = (((((((u64)x35 * x19) + ((u64)x37 * x17)) + ((u64)x33 * x21)) + ((u64)x39 * x15)) + ((u64)x31 * x20)) + ((u64)x38 * x13));
+ { u64 x54 = (((0x2 * ((((u64)x37 * x19) + ((u64)x33 * x20)) + ((u64)x38 * x15))) + ((u64)x35 * x21)) + ((u64)x39 * x17));
+ { u64 x55 = (((((u64)x37 * x21) + ((u64)x39 * x19)) + ((u64)x35 * x20)) + ((u64)x38 * x17));
+ { u64 x56 = (((u64)x39 * x21) + (0x2 * (((u64)x37 * x20) + ((u64)x38 * x19))));
+ { u64 x57 = (((u64)x39 * x20) + ((u64)x38 * x21));
+ { u64 x58 = ((u64)(0x2 * x38) * x20);
+ { u64 x59 = (x48 + (x58 << 0x4));
+ { u64 x60 = (x59 + (x58 << 0x1));
+ { u64 x61 = (x60 + x58);
+ { u64 x62 = (x47 + (x57 << 0x4));
+ { u64 x63 = (x62 + (x57 << 0x1));
+ { u64 x64 = (x63 + x57);
+ { u64 x65 = (x46 + (x56 << 0x4));
+ { u64 x66 = (x65 + (x56 << 0x1));
+ { u64 x67 = (x66 + x56);
+ { u64 x68 = (x45 + (x55 << 0x4));
+ { u64 x69 = (x68 + (x55 << 0x1));
+ { u64 x70 = (x69 + x55);
+ { u64 x71 = (x44 + (x54 << 0x4));
+ { u64 x72 = (x71 + (x54 << 0x1));
+ { u64 x73 = (x72 + x54);
+ { u64 x74 = (x43 + (x53 << 0x4));
+ { u64 x75 = (x74 + (x53 << 0x1));
+ { u64 x76 = (x75 + x53);
+ { u64 x77 = (x42 + (x52 << 0x4));
+ { u64 x78 = (x77 + (x52 << 0x1));
+ { u64 x79 = (x78 + x52);
+ { u64 x80 = (x41 + (x51 << 0x4));
+ { u64 x81 = (x80 + (x51 << 0x1));
+ { u64 x82 = (x81 + x51);
+ { u64 x83 = (x40 + (x50 << 0x4));
+ { u64 x84 = (x83 + (x50 << 0x1));
+ { u64 x85 = (x84 + x50);
+ { u64 x86 = (x85 >> 0x1a);
+ { u32 x87 = ((u32)x85 & 0x3ffffff);
+ { u64 x88 = (x86 + x82);
+ { u64 x89 = (x88 >> 0x19);
+ { u32 x90 = ((u32)x88 & 0x1ffffff);
+ { u64 x91 = (x89 + x79);
+ { u64 x92 = (x91 >> 0x1a);
+ { u32 x93 = ((u32)x91 & 0x3ffffff);
+ { u64 x94 = (x92 + x76);
+ { u64 x95 = (x94 >> 0x19);
+ { u32 x96 = ((u32)x94 & 0x1ffffff);
+ { u64 x97 = (x95 + x73);
+ { u64 x98 = (x97 >> 0x1a);
+ { u32 x99 = ((u32)x97 & 0x3ffffff);
+ { u64 x100 = (x98 + x70);
+ { u64 x101 = (x100 >> 0x19);
+ { u32 x102 = ((u32)x100 & 0x1ffffff);
+ { u64 x103 = (x101 + x67);
+ { u64 x104 = (x103 >> 0x1a);
+ { u32 x105 = ((u32)x103 & 0x3ffffff);
+ { u64 x106 = (x104 + x64);
+ { u64 x107 = (x106 >> 0x19);
+ { u32 x108 = ((u32)x106 & 0x1ffffff);
+ { u64 x109 = (x107 + x61);
+ { u64 x110 = (x109 >> 0x1a);
+ { u32 x111 = ((u32)x109 & 0x3ffffff);
+ { u64 x112 = (x110 + x49);
+ { u64 x113 = (x112 >> 0x19);
+ { u32 x114 = ((u32)x112 & 0x1ffffff);
+ { u64 x115 = (x87 + (0x13 * x113));
+ { u32 x116 = (u32) (x115 >> 0x1a);
+ { u32 x117 = ((u32)x115 & 0x3ffffff);
+ { u32 x118 = (x116 + x90);
+ { u32 x119 = (x118 >> 0x19);
+ { u32 x120 = (x118 & 0x1ffffff);
+ out[0] = x117;
+ out[1] = x120;
+ out[2] = (x119 + x93);
+ out[3] = x96;
+ out[4] = x99;
+ out[5] = x102;
+ out[6] = x105;
+ out[7] = x108;
+ out[8] = x111;
+ out[9] = x114;
+ }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
+}
+
+static __always_inline void fe_mul_ttt(fe *h, const fe *f, const fe *g)
+{
+ fe_mul_impl(h->v, f->v, g->v);
+}
+
+static __always_inline void fe_mul_tlt(fe *h, const fe_loose *f, const fe *g)
+{
+ fe_mul_impl(h->v, f->v, g->v);
+}
+
+static __always_inline void
+fe_mul_tll(fe *h, const fe_loose *f, const fe_loose *g)
+{
+ fe_mul_impl(h->v, f->v, g->v);
+}
+
+static void fe_sqr_impl(u32 out[10], const u32 in1[10])
+{
+ { const u32 x17 = in1[9];
+ { const u32 x18 = in1[8];
+ { const u32 x16 = in1[7];
+ { const u32 x14 = in1[6];
+ { const u32 x12 = in1[5];
+ { const u32 x10 = in1[4];
+ { const u32 x8 = in1[3];
+ { const u32 x6 = in1[2];
+ { const u32 x4 = in1[1];
+ { const u32 x2 = in1[0];
+ { u64 x19 = ((u64)x2 * x2);
+ { u64 x20 = ((u64)(0x2 * x2) * x4);
+ { u64 x21 = (0x2 * (((u64)x4 * x4) + ((u64)x2 * x6)));
+ { u64 x22 = (0x2 * (((u64)x4 * x6) + ((u64)x2 * x8)));
+ { u64 x23 = ((((u64)x6 * x6) + ((u64)(0x4 * x4) * x8)) + ((u64)(0x2 * x2) * x10));
+ { u64 x24 = (0x2 * ((((u64)x6 * x8) + ((u64)x4 * x10)) + ((u64)x2 * x12)));
+ { u64 x25 = (0x2 * (((((u64)x8 * x8) + ((u64)x6 * x10)) + ((u64)x2 * x14)) + ((u64)(0x2 * x4) * x12)));
+ { u64 x26 = (0x2 * (((((u64)x8 * x10) + ((u64)x6 * x12)) + ((u64)x4 * x14)) + ((u64)x2 * x16)));
+ { u64 x27 = (((u64)x10 * x10) + (0x2 * ((((u64)x6 * x14) + ((u64)x2 * x18)) + (0x2 * (((u64)x4 * x16) + ((u64)x8 * x12))))));
+ { u64 x28 = (0x2 * ((((((u64)x10 * x12) + ((u64)x8 * x14)) + ((u64)x6 * x16)) + ((u64)x4 * x18)) + ((u64)x2 * x17)));
+ { u64 x29 = (0x2 * (((((u64)x12 * x12) + ((u64)x10 * x14)) + ((u64)x6 * x18)) + (0x2 * (((u64)x8 * x16) + ((u64)x4 * x17)))));
+ { u64 x30 = (0x2 * (((((u64)x12 * x14) + ((u64)x10 * x16)) + ((u64)x8 * x18)) + ((u64)x6 * x17)));
+ { u64 x31 = (((u64)x14 * x14) + (0x2 * (((u64)x10 * x18) + (0x2 * (((u64)x12 * x16) + ((u64)x8 * x17))))));
+ { u64 x32 = (0x2 * ((((u64)x14 * x16) + ((u64)x12 * x18)) + ((u64)x10 * x17)));
+ { u64 x33 = (0x2 * ((((u64)x16 * x16) + ((u64)x14 * x18)) + ((u64)(0x2 * x12) * x17)));
+ { u64 x34 = (0x2 * (((u64)x16 * x18) + ((u64)x14 * x17)));
+ { u64 x35 = (((u64)x18 * x18) + ((u64)(0x4 * x16) * x17));
+ { u64 x36 = ((u64)(0x2 * x18) * x17);
+ { u64 x37 = ((u64)(0x2 * x17) * x17);
+ { u64 x38 = (x27 + (x37 << 0x4));
+ { u64 x39 = (x38 + (x37 << 0x1));
+ { u64 x40 = (x39 + x37);
+ { u64 x41 = (x26 + (x36 << 0x4));
+ { u64 x42 = (x41 + (x36 << 0x1));
+ { u64 x43 = (x42 + x36);
+ { u64 x44 = (x25 + (x35 << 0x4));
+ { u64 x45 = (x44 + (x35 << 0x1));
+ { u64 x46 = (x45 + x35);
+ { u64 x47 = (x24 + (x34 << 0x4));
+ { u64 x48 = (x47 + (x34 << 0x1));
+ { u64 x49 = (x48 + x34);
+ { u64 x50 = (x23 + (x33 << 0x4));
+ { u64 x51 = (x50 + (x33 << 0x1));
+ { u64 x52 = (x51 + x33);
+ { u64 x53 = (x22 + (x32 << 0x4));
+ { u64 x54 = (x53 + (x32 << 0x1));
+ { u64 x55 = (x54 + x32);
+ { u64 x56 = (x21 + (x31 << 0x4));
+ { u64 x57 = (x56 + (x31 << 0x1));
+ { u64 x58 = (x57 + x31);
+ { u64 x59 = (x20 + (x30 << 0x4));
+ { u64 x60 = (x59 + (x30 << 0x1));
+ { u64 x61 = (x60 + x30);
+ { u64 x62 = (x19 + (x29 << 0x4));
+ { u64 x63 = (x62 + (x29 << 0x1));
+ { u64 x64 = (x63 + x29);
+ { u64 x65 = (x64 >> 0x1a);
+ { u32 x66 = ((u32)x64 & 0x3ffffff);
+ { u64 x67 = (x65 + x61);
+ { u64 x68 = (x67 >> 0x19);
+ { u32 x69 = ((u32)x67 & 0x1ffffff);
+ { u64 x70 = (x68 + x58);
+ { u64 x71 = (x70 >> 0x1a);
+ { u32 x72 = ((u32)x70 & 0x3ffffff);
+ { u64 x73 = (x71 + x55);
+ { u64 x74 = (x73 >> 0x19);
+ { u32 x75 = ((u32)x73 & 0x1ffffff);
+ { u64 x76 = (x74 + x52);
+ { u64 x77 = (x76 >> 0x1a);
+ { u32 x78 = ((u32)x76 & 0x3ffffff);
+ { u64 x79 = (x77 + x49);
+ { u64 x80 = (x79 >> 0x19);
+ { u32 x81 = ((u32)x79 & 0x1ffffff);
+ { u64 x82 = (x80 + x46);
+ { u64 x83 = (x82 >> 0x1a);
+ { u32 x84 = ((u32)x82 & 0x3ffffff);
+ { u64 x85 = (x83 + x43);
+ { u64 x86 = (x85 >> 0x19);
+ { u32 x87 = ((u32)x85 & 0x1ffffff);
+ { u64 x88 = (x86 + x40);
+ { u64 x89 = (x88 >> 0x1a);
+ { u32 x90 = ((u32)x88 & 0x3ffffff);
+ { u64 x91 = (x89 + x28);
+ { u64 x92 = (x91 >> 0x19);
+ { u32 x93 = ((u32)x91 & 0x1ffffff);
+ { u64 x94 = (x66 + (0x13 * x92));
+ { u32 x95 = (u32) (x94 >> 0x1a);
+ { u32 x96 = ((u32)x94 & 0x3ffffff);
+ { u32 x97 = (x95 + x69);
+ { u32 x98 = (x97 >> 0x19);
+ { u32 x99 = (x97 & 0x1ffffff);
+ out[0] = x96;
+ out[1] = x99;
+ out[2] = (x98 + x72);
+ out[3] = x75;
+ out[4] = x78;
+ out[5] = x81;
+ out[6] = x84;
+ out[7] = x87;
+ out[8] = x90;
+ out[9] = x93;
+ }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
+}
+
+static __always_inline void fe_sq_tl(fe *h, const fe_loose *f)
+{
+ fe_sqr_impl(h->v, f->v);
+}
+
+static __always_inline void fe_sq_tt(fe *h, const fe *f)
+{
+ fe_sqr_impl(h->v, f->v);
+}
+
+static __always_inline void fe_loose_invert(fe *out, const fe_loose *z)
+{
+ fe t0;
+ fe t1;
+ fe t2;
+ fe t3;
+ int i;
+
+ fe_sq_tl(&t0, z);
+ fe_sq_tt(&t1, &t0);
+ for (i = 1; i < 2; ++i)
+ fe_sq_tt(&t1, &t1);
+ fe_mul_tlt(&t1, z, &t1);
+ fe_mul_ttt(&t0, &t0, &t1);
+ fe_sq_tt(&t2, &t0);
+ fe_mul_ttt(&t1, &t1, &t2);
+ fe_sq_tt(&t2, &t1);
+ for (i = 1; i < 5; ++i)
+ fe_sq_tt(&t2, &t2);
+ fe_mul_ttt(&t1, &t2, &t1);
+ fe_sq_tt(&t2, &t1);
+ for (i = 1; i < 10; ++i)
+ fe_sq_tt(&t2, &t2);
+ fe_mul_ttt(&t2, &t2, &t1);
+ fe_sq_tt(&t3, &t2);
+ for (i = 1; i < 20; ++i)
+ fe_sq_tt(&t3, &t3);
+ fe_mul_ttt(&t2, &t3, &t2);
+ fe_sq_tt(&t2, &t2);
+ for (i = 1; i < 10; ++i)
+ fe_sq_tt(&t2, &t2);
+ fe_mul_ttt(&t1, &t2, &t1);
+ fe_sq_tt(&t2, &t1);
+ for (i = 1; i < 50; ++i)
+ fe_sq_tt(&t2, &t2);
+ fe_mul_ttt(&t2, &t2, &t1);
+ fe_sq_tt(&t3, &t2);
+ for (i = 1; i < 100; ++i)
+ fe_sq_tt(&t3, &t3);
+ fe_mul_ttt(&t2, &t3, &t2);
+ fe_sq_tt(&t2, &t2);
+ for (i = 1; i < 50; ++i)
+ fe_sq_tt(&t2, &t2);
+ fe_mul_ttt(&t1, &t2, &t1);
+ fe_sq_tt(&t1, &t1);
+ for (i = 1; i < 5; ++i)
+ fe_sq_tt(&t1, &t1);
+ fe_mul_ttt(out, &t1, &t0);
+}
+
+static __always_inline void fe_invert(fe *out, const fe *z)
+{
+ fe_loose l;
+ fe_copy_lt(&l, z);
+ fe_loose_invert(out, &l);
+}
+
+/* Replace (f,g) with (g,f) if b == 1;
+ * replace (f,g) with (f,g) if b == 0.
+ *
+ * Preconditions: b in {0,1}
+ */
+static __always_inline void fe_cswap(fe *f, fe *g, unsigned int b)
+{
+ unsigned i;
+ b = 0 - b;
+ for (i = 0; i < 10; i++) {
+ u32 x = f->v[i] ^ g->v[i];
+ x &= b;
+ f->v[i] ^= x;
+ g->v[i] ^= x;
+ }
+}
+
+/* NOTE: based on fiat-crypto fe_mul, edited for in2=121666, 0, 0.*/
+static __always_inline void fe_mul_121666_impl(u32 out[10], const u32 in1[10])
+{
+ { const u32 x20 = in1[9];
+ { const u32 x21 = in1[8];
+ { const u32 x19 = in1[7];
+ { const u32 x17 = in1[6];
+ { const u32 x15 = in1[5];
+ { const u32 x13 = in1[4];
+ { const u32 x11 = in1[3];
+ { const u32 x9 = in1[2];
+ { const u32 x7 = in1[1];
+ { const u32 x5 = in1[0];
+ { const u32 x38 = 0;
+ { const u32 x39 = 0;
+ { const u32 x37 = 0;
+ { const u32 x35 = 0;
+ { const u32 x33 = 0;
+ { const u32 x31 = 0;
+ { const u32 x29 = 0;
+ { const u32 x27 = 0;
+ { const u32 x25 = 0;
+ { const u32 x23 = 121666;
+ { u64 x40 = ((u64)x23 * x5);
+ { u64 x41 = (((u64)x23 * x7) + ((u64)x25 * x5));
+ { u64 x42 = ((((u64)(0x2 * x25) * x7) + ((u64)x23 * x9)) + ((u64)x27 * x5));
+ { u64 x43 = (((((u64)x25 * x9) + ((u64)x27 * x7)) + ((u64)x23 * x11)) + ((u64)x29 * x5));
+ { u64 x44 = (((((u64)x27 * x9) + (0x2 * (((u64)x25 * x11) + ((u64)x29 * x7)))) + ((u64)x23 * x13)) + ((u64)x31 * x5));
+ { u64 x45 = (((((((u64)x27 * x11) + ((u64)x29 * x9)) + ((u64)x25 * x13)) + ((u64)x31 * x7)) + ((u64)x23 * x15)) + ((u64)x33 * x5));
+ { u64 x46 = (((((0x2 * ((((u64)x29 * x11) + ((u64)x25 * x15)) + ((u64)x33 * x7))) + ((u64)x27 * x13)) + ((u64)x31 * x9)) + ((u64)x23 * x17)) + ((u64)x35 * x5));
+ { u64 x47 = (((((((((u64)x29 * x13) + ((u64)x31 * x11)) + ((u64)x27 * x15)) + ((u64)x33 * x9)) + ((u64)x25 * x17)) + ((u64)x35 * x7)) + ((u64)x23 * x19)) + ((u64)x37 * x5));
+ { u64 x48 = (((((((u64)x31 * x13) + (0x2 * (((((u64)x29 * x15) + ((u64)x33 * x11)) + ((u64)x25 * x19)) + ((u64)x37 * x7)))) + ((u64)x27 * x17)) + ((u64)x35 * x9)) + ((u64)x23 * x21)) + ((u64)x39 * x5));
+ { u64 x49 = (((((((((((u64)x31 * x15) + ((u64)x33 * x13)) + ((u64)x29 * x17)) + ((u64)x35 * x11)) + ((u64)x27 * x19)) + ((u64)x37 * x9)) + ((u64)x25 * x21)) + ((u64)x39 * x7)) + ((u64)x23 * x20)) + ((u64)x38 * x5));
+ { u64 x50 = (((((0x2 * ((((((u64)x33 * x15) + ((u64)x29 * x19)) + ((u64)x37 * x11)) + ((u64)x25 * x20)) + ((u64)x38 * x7))) + ((u64)x31 * x17)) + ((u64)x35 * x13)) + ((u64)x27 * x21)) + ((u64)x39 * x9));
+ { u64 x51 = (((((((((u64)x33 * x17) + ((u64)x35 * x15)) + ((u64)x31 * x19)) + ((u64)x37 * x13)) + ((u64)x29 * x21)) + ((u64)x39 * x11)) + ((u64)x27 * x20)) + ((u64)x38 * x9));
+ { u64 x52 = (((((u64)x35 * x17) + (0x2 * (((((u64)x33 * x19) + ((u64)x37 * x15)) + ((u64)x29 * x20)) + ((u64)x38 * x11)))) + ((u64)x31 * x21)) + ((u64)x39 * x13));
+ { u64 x53 = (((((((u64)x35 * x19) + ((u64)x37 * x17)) + ((u64)x33 * x21)) + ((u64)x39 * x15)) + ((u64)x31 * x20)) + ((u64)x38 * x13));
+ { u64 x54 = (((0x2 * ((((u64)x37 * x19) + ((u64)x33 * x20)) + ((u64)x38 * x15))) + ((u64)x35 * x21)) + ((u64)x39 * x17));
+ { u64 x55 = (((((u64)x37 * x21) + ((u64)x39 * x19)) + ((u64)x35 * x20)) + ((u64)x38 * x17));
+ { u64 x56 = (((u64)x39 * x21) + (0x2 * (((u64)x37 * x20) + ((u64)x38 * x19))));
+ { u64 x57 = (((u64)x39 * x20) + ((u64)x38 * x21));
+ { u64 x58 = ((u64)(0x2 * x38) * x20);
+ { u64 x59 = (x48 + (x58 << 0x4));
+ { u64 x60 = (x59 + (x58 << 0x1));
+ { u64 x61 = (x60 + x58);
+ { u64 x62 = (x47 + (x57 << 0x4));
+ { u64 x63 = (x62 + (x57 << 0x1));
+ { u64 x64 = (x63 + x57);
+ { u64 x65 = (x46 + (x56 << 0x4));
+ { u64 x66 = (x65 + (x56 << 0x1));
+ { u64 x67 = (x66 + x56);
+ { u64 x68 = (x45 + (x55 << 0x4));
+ { u64 x69 = (x68 + (x55 << 0x1));
+ { u64 x70 = (x69 + x55);
+ { u64 x71 = (x44 + (x54 << 0x4));
+ { u64 x72 = (x71 + (x54 << 0x1));
+ { u64 x73 = (x72 + x54);
+ { u64 x74 = (x43 + (x53 << 0x4));
+ { u64 x75 = (x74 + (x53 << 0x1));
+ { u64 x76 = (x75 + x53);
+ { u64 x77 = (x42 + (x52 << 0x4));
+ { u64 x78 = (x77 + (x52 << 0x1));
+ { u64 x79 = (x78 + x52);
+ { u64 x80 = (x41 + (x51 << 0x4));
+ { u64 x81 = (x80 + (x51 << 0x1));
+ { u64 x82 = (x81 + x51);
+ { u64 x83 = (x40 + (x50 << 0x4));
+ { u64 x84 = (x83 + (x50 << 0x1));
+ { u64 x85 = (x84 + x50);
+ { u64 x86 = (x85 >> 0x1a);
+ { u32 x87 = ((u32)x85 & 0x3ffffff);
+ { u64 x88 = (x86 + x82);
+ { u64 x89 = (x88 >> 0x19);
+ { u32 x90 = ((u32)x88 & 0x1ffffff);
+ { u64 x91 = (x89 + x79);
+ { u64 x92 = (x91 >> 0x1a);
+ { u32 x93 = ((u32)x91 & 0x3ffffff);
+ { u64 x94 = (x92 + x76);
+ { u64 x95 = (x94 >> 0x19);
+ { u32 x96 = ((u32)x94 & 0x1ffffff);
+ { u64 x97 = (x95 + x73);
+ { u64 x98 = (x97 >> 0x1a);
+ { u32 x99 = ((u32)x97 & 0x3ffffff);
+ { u64 x100 = (x98 + x70);
+ { u64 x101 = (x100 >> 0x19);
+ { u32 x102 = ((u32)x100 & 0x1ffffff);
+ { u64 x103 = (x101 + x67);
+ { u64 x104 = (x103 >> 0x1a);
+ { u32 x105 = ((u32)x103 & 0x3ffffff);
+ { u64 x106 = (x104 + x64);
+ { u64 x107 = (x106 >> 0x19);
+ { u32 x108 = ((u32)x106 & 0x1ffffff);
+ { u64 x109 = (x107 + x61);
+ { u64 x110 = (x109 >> 0x1a);
+ { u32 x111 = ((u32)x109 & 0x3ffffff);
+ { u64 x112 = (x110 + x49);
+ { u64 x113 = (x112 >> 0x19);
+ { u32 x114 = ((u32)x112 & 0x1ffffff);
+ { u64 x115 = (x87 + (0x13 * x113));
+ { u32 x116 = (u32) (x115 >> 0x1a);
+ { u32 x117 = ((u32)x115 & 0x3ffffff);
+ { u32 x118 = (x116 + x90);
+ { u32 x119 = (x118 >> 0x19);
+ { u32 x120 = (x118 & 0x1ffffff);
+ out[0] = x117;
+ out[1] = x120;
+ out[2] = (x119 + x93);
+ out[3] = x96;
+ out[4] = x99;
+ out[5] = x102;
+ out[6] = x105;
+ out[7] = x108;
+ out[8] = x111;
+ out[9] = x114;
+ }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
+}
+
+static __always_inline void fe_mul121666(fe *h, const fe_loose *f)
+{
+ fe_mul_121666_impl(h->v, f->v);
+}
+
+static void curve25519_generic(u8 out[CURVE25519_KEY_SIZE],
+ const u8 scalar[CURVE25519_KEY_SIZE],
+ const u8 point[CURVE25519_KEY_SIZE])
+{
+ fe x1, x2, z2, x3, z3;
+ fe_loose x2l, z2l, x3l;
+ unsigned swap = 0;
+ int pos;
+ u8 e[32];
+
+ memcpy(e, scalar, 32);
+ curve25519_clamp_secret(e);
+
+ /* The following implementation was transcribed to Coq and proven to
+ * correspond to unary scalar multiplication in affine coordinates given
+ * that x1 != 0 is the x coordinate of some point on the curve. It was
+ * also checked in Coq that doing a ladderstep with x1 = x3 = 0 gives
+ * z2' = z3' = 0, and z2 = z3 = 0 gives z2' = z3' = 0. The statement was
+ * quantified over the underlying field, so it applies to Curve25519
+ * itself and the quadratic twist of Curve25519. It was not proven in
+ * Coq that prime-field arithmetic correctly simulates extension-field
+ * arithmetic on prime-field values. The decoding of the byte array
+ * representation of e was not considered.
+ *
+ * Specification of Montgomery curves in affine coordinates:
+ * <https://github.com/mit-plv/fiat-crypto/blob/2456d821825521f7e03e65882cc3521795b0320f/src/Spec/MontgomeryCurve.v#L27>
+ *
+ * Proof that these form a group that is isomorphic to a Weierstrass
+ * curve:
+ * <https://github.com/mit-plv/fiat-crypto/blob/2456d821825521f7e03e65882cc3521795b0320f/src/Curves/Montgomery/AffineProofs.v#L35>
+ *
+ * Coq transcription and correctness proof of the loop
+ * (where scalarbits=255):
+ * <https://github.com/mit-plv/fiat-crypto/blob/2456d821825521f7e03e65882cc3521795b0320f/src/Curves/Montgomery/XZ.v#L118>
+ * <https://github.com/mit-plv/fiat-crypto/blob/2456d821825521f7e03e65882cc3521795b0320f/src/Curves/Montgomery/XZProofs.v#L278>
+ * preconditions: 0 <= e < 2^255 (not necessarily e < order),
+ * fe_invert(0) = 0
+ */
+ fe_frombytes(&x1, point);
+ fe_1(&x2);
+ fe_0(&z2);
+ fe_copy(&x3, &x1);
+ fe_1(&z3);
+
+ for (pos = 254; pos >= 0; --pos) {
+ fe tmp0, tmp1;
+ fe_loose tmp0l, tmp1l;
+ /* loop invariant as of right before the test, for the case
+ * where x1 != 0:
+ * pos >= -1; if z2 = 0 then x2 is nonzero; if z3 = 0 then x3
+ * is nonzero
+ * let r := e >> (pos+1) in the following equalities of
+ * projective points:
+ * to_xz (r*P) === if swap then (x3, z3) else (x2, z2)
+ * to_xz ((r+1)*P) === if swap then (x2, z2) else (x3, z3)
+ * x1 is the nonzero x coordinate of the nonzero
+ * point (r*P-(r+1)*P)
+ */
+ unsigned b = 1 & (e[pos / 8] >> (pos & 7));
+ swap ^= b;
+ fe_cswap(&x2, &x3, swap);
+ fe_cswap(&z2, &z3, swap);
+ swap = b;
+ /* Coq transcription of ladderstep formula (called from
+ * transcribed loop):
+ * <https://github.com/mit-plv/fiat-crypto/blob/2456d821825521f7e03e65882cc3521795b0320f/src/Curves/Montgomery/XZ.v#L89>
+ * <https://github.com/mit-plv/fiat-crypto/blob/2456d821825521f7e03e65882cc3521795b0320f/src/Curves/Montgomery/XZProofs.v#L131>
+ * x1 != 0 <https://github.com/mit-plv/fiat-crypto/blob/2456d821825521f7e03e65882cc3521795b0320f/src/Curves/Montgomery/XZProofs.v#L217>
+ * x1 = 0 <https://github.com/mit-plv/fiat-crypto/blob/2456d821825521f7e03e65882cc3521795b0320f/src/Curves/Montgomery/XZProofs.v#L147>
+ */
+ fe_sub(&tmp0l, &x3, &z3);
+ fe_sub(&tmp1l, &x2, &z2);
+ fe_add(&x2l, &x2, &z2);
+ fe_add(&z2l, &x3, &z3);
+ fe_mul_tll(&z3, &tmp0l, &x2l);
+ fe_mul_tll(&z2, &z2l, &tmp1l);
+ fe_sq_tl(&tmp0, &tmp1l);
+ fe_sq_tl(&tmp1, &x2l);
+ fe_add(&x3l, &z3, &z2);
+ fe_sub(&z2l, &z3, &z2);
+ fe_mul_ttt(&x2, &tmp1, &tmp0);
+ fe_sub(&tmp1l, &tmp1, &tmp0);
+ fe_sq_tl(&z2, &z2l);
+ fe_mul121666(&z3, &tmp1l);
+ fe_sq_tl(&x3, &x3l);
+ fe_add(&tmp0l, &tmp0, &z3);
+ fe_mul_ttt(&z3, &x1, &z2);
+ fe_mul_tll(&z2, &tmp1l, &tmp0l);
+ }
+ /* here pos=-1, so r=e, so to_xz (e*P) === if swap then (x3, z3)
+ * else (x2, z2)
+ */
+ fe_cswap(&x2, &x3, swap);
+ fe_cswap(&z2, &z3, swap);
+
+ fe_invert(&z2, &z2);
+ fe_mul_ttt(&x2, &x2, &z2);
+ fe_tobytes(out, &x2);
+
+ memzero_explicit(&x1, sizeof(x1));
+ memzero_explicit(&x2, sizeof(x2));
+ memzero_explicit(&z2, sizeof(z2));
+ memzero_explicit(&x3, sizeof(x3));
+ memzero_explicit(&z3, sizeof(z3));
+ memzero_explicit(&x2l, sizeof(x2l));
+ memzero_explicit(&z2l, sizeof(z2l));
+ memzero_explicit(&x3l, sizeof(x3l));
+ memzero_explicit(&e, sizeof(e));
+}
diff --git a/contrib/wireguard-tools/curve25519-hacl64.h b/contrib/wireguard-tools/curve25519-hacl64.h
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/curve25519-hacl64.h
@@ -0,0 +1,784 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * Copyright (C) 2016-2017 INRIA and Microsoft Corporation.
+ * Copyright (C) 2018-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ *
+ * This is a machine-generated formally verified implementation of Curve25519
+ * ECDH from: <https://github.com/mitls/hacl-star>. Though originally machine
+ * generated, it has been tweaked to be suitable for use in the kernel. It is
+ * optimized for 64-bit machines that can efficiently work with 128-bit
+ * integer types.
+ */
+
+typedef __uint128_t u128;
+
+static __always_inline u64 u64_eq_mask(u64 a, u64 b)
+{
+ u64 x = a ^ b;
+ u64 minus_x = ~x + (u64)1U;
+ u64 x_or_minus_x = x | minus_x;
+ u64 xnx = x_or_minus_x >> (u32)63U;
+ u64 c = xnx - (u64)1U;
+ return c;
+}
+
+static __always_inline u64 u64_gte_mask(u64 a, u64 b)
+{
+ u64 x = a;
+ u64 y = b;
+ u64 x_xor_y = x ^ y;
+ u64 x_sub_y = x - y;
+ u64 x_sub_y_xor_y = x_sub_y ^ y;
+ u64 q = x_xor_y | x_sub_y_xor_y;
+ u64 x_xor_q = x ^ q;
+ u64 x_xor_q_ = x_xor_q >> (u32)63U;
+ u64 c = x_xor_q_ - (u64)1U;
+ return c;
+}
+
+static __always_inline void modulo_carry_top(u64 *b)
+{
+ u64 b4 = b[4];
+ u64 b0 = b[0];
+ u64 b4_ = b4 & 0x7ffffffffffffLLU;
+ u64 b0_ = b0 + 19 * (b4 >> 51);
+ b[4] = b4_;
+ b[0] = b0_;
+}
+
+static __always_inline void fproduct_copy_from_wide_(u64 *output, u128 *input)
+{
+ {
+ u128 xi = input[0];
+ output[0] = ((u64)(xi));
+ }
+ {
+ u128 xi = input[1];
+ output[1] = ((u64)(xi));
+ }
+ {
+ u128 xi = input[2];
+ output[2] = ((u64)(xi));
+ }
+ {
+ u128 xi = input[3];
+ output[3] = ((u64)(xi));
+ }
+ {
+ u128 xi = input[4];
+ output[4] = ((u64)(xi));
+ }
+}
+
+static __always_inline void
+fproduct_sum_scalar_multiplication_(u128 *output, u64 *input, u64 s)
+{
+ output[0] += (u128)input[0] * s;
+ output[1] += (u128)input[1] * s;
+ output[2] += (u128)input[2] * s;
+ output[3] += (u128)input[3] * s;
+ output[4] += (u128)input[4] * s;
+}
+
+static __always_inline void fproduct_carry_wide_(u128 *tmp)
+{
+ {
+ u32 ctr = 0;
+ u128 tctr = tmp[ctr];
+ u128 tctrp1 = tmp[ctr + 1];
+ u64 r0 = ((u64)(tctr)) & 0x7ffffffffffffLLU;
+ u128 c = ((tctr) >> (51));
+ tmp[ctr] = ((u128)(r0));
+ tmp[ctr + 1] = ((tctrp1) + (c));
+ }
+ {
+ u32 ctr = 1;
+ u128 tctr = tmp[ctr];
+ u128 tctrp1 = tmp[ctr + 1];
+ u64 r0 = ((u64)(tctr)) & 0x7ffffffffffffLLU;
+ u128 c = ((tctr) >> (51));
+ tmp[ctr] = ((u128)(r0));
+ tmp[ctr + 1] = ((tctrp1) + (c));
+ }
+
+ {
+ u32 ctr = 2;
+ u128 tctr = tmp[ctr];
+ u128 tctrp1 = tmp[ctr + 1];
+ u64 r0 = ((u64)(tctr)) & 0x7ffffffffffffLLU;
+ u128 c = ((tctr) >> (51));
+ tmp[ctr] = ((u128)(r0));
+ tmp[ctr + 1] = ((tctrp1) + (c));
+ }
+ {
+ u32 ctr = 3;
+ u128 tctr = tmp[ctr];
+ u128 tctrp1 = tmp[ctr + 1];
+ u64 r0 = ((u64)(tctr)) & 0x7ffffffffffffLLU;
+ u128 c = ((tctr) >> (51));
+ tmp[ctr] = ((u128)(r0));
+ tmp[ctr + 1] = ((tctrp1) + (c));
+ }
+}
+
+static __always_inline void fmul_shift_reduce(u64 *output)
+{
+ u64 tmp = output[4];
+ u64 b0;
+ {
+ u32 ctr = 5 - 0 - 1;
+ u64 z = output[ctr - 1];
+ output[ctr] = z;
+ }
+ {
+ u32 ctr = 5 - 1 - 1;
+ u64 z = output[ctr - 1];
+ output[ctr] = z;
+ }
+ {
+ u32 ctr = 5 - 2 - 1;
+ u64 z = output[ctr - 1];
+ output[ctr] = z;
+ }
+ {
+ u32 ctr = 5 - 3 - 1;
+ u64 z = output[ctr - 1];
+ output[ctr] = z;
+ }
+ output[0] = tmp;
+ b0 = output[0];
+ output[0] = 19 * b0;
+}
+
+static __always_inline void fmul_mul_shift_reduce_(u128 *output, u64 *input,
+ u64 *input21)
+{
+ u32 i;
+ u64 input2i;
+ {
+ u64 input2i = input21[0];
+ fproduct_sum_scalar_multiplication_(output, input, input2i);
+ fmul_shift_reduce(input);
+ }
+ {
+ u64 input2i = input21[1];
+ fproduct_sum_scalar_multiplication_(output, input, input2i);
+ fmul_shift_reduce(input);
+ }
+ {
+ u64 input2i = input21[2];
+ fproduct_sum_scalar_multiplication_(output, input, input2i);
+ fmul_shift_reduce(input);
+ }
+ {
+ u64 input2i = input21[3];
+ fproduct_sum_scalar_multiplication_(output, input, input2i);
+ fmul_shift_reduce(input);
+ }
+ i = 4;
+ input2i = input21[i];
+ fproduct_sum_scalar_multiplication_(output, input, input2i);
+}
+
+static __always_inline void fmul_fmul(u64 *output, u64 *input, u64 *input21)
+{
+ u64 tmp[5] = { input[0], input[1], input[2], input[3], input[4] };
+ {
+ u128 b4;
+ u128 b0;
+ u128 b4_;
+ u128 b0_;
+ u64 i0;
+ u64 i1;
+ u64 i0_;
+ u64 i1_;
+ u128 t[5] = { 0 };
+ fmul_mul_shift_reduce_(t, tmp, input21);
+ fproduct_carry_wide_(t);
+ b4 = t[4];
+ b0 = t[0];
+ b4_ = ((b4) & (((u128)(0x7ffffffffffffLLU))));
+ b0_ = ((b0) + (((u128)(19) * (((u64)(((b4) >> (51))))))));
+ t[4] = b4_;
+ t[0] = b0_;
+ fproduct_copy_from_wide_(output, t);
+ i0 = output[0];
+ i1 = output[1];
+ i0_ = i0 & 0x7ffffffffffffLLU;
+ i1_ = i1 + (i0 >> 51);
+ output[0] = i0_;
+ output[1] = i1_;
+ }
+}
+
+static __always_inline void fsquare_fsquare__(u128 *tmp, u64 *output)
+{
+ u64 r0 = output[0];
+ u64 r1 = output[1];
+ u64 r2 = output[2];
+ u64 r3 = output[3];
+ u64 r4 = output[4];
+ u64 d0 = r0 * 2;
+ u64 d1 = r1 * 2;
+ u64 d2 = r2 * 2 * 19;
+ u64 d419 = r4 * 19;
+ u64 d4 = d419 * 2;
+ u128 s0 = ((((((u128)(r0) * (r0))) + (((u128)(d4) * (r1))))) +
+ (((u128)(d2) * (r3))));
+ u128 s1 = ((((((u128)(d0) * (r1))) + (((u128)(d4) * (r2))))) +
+ (((u128)(r3 * 19) * (r3))));
+ u128 s2 = ((((((u128)(d0) * (r2))) + (((u128)(r1) * (r1))))) +
+ (((u128)(d4) * (r3))));
+ u128 s3 = ((((((u128)(d0) * (r3))) + (((u128)(d1) * (r2))))) +
+ (((u128)(r4) * (d419))));
+ u128 s4 = ((((((u128)(d0) * (r4))) + (((u128)(d1) * (r3))))) +
+ (((u128)(r2) * (r2))));
+ tmp[0] = s0;
+ tmp[1] = s1;
+ tmp[2] = s2;
+ tmp[3] = s3;
+ tmp[4] = s4;
+}
+
+static __always_inline void fsquare_fsquare_(u128 *tmp, u64 *output)
+{
+ u128 b4;
+ u128 b0;
+ u128 b4_;
+ u128 b0_;
+ u64 i0;
+ u64 i1;
+ u64 i0_;
+ u64 i1_;
+ fsquare_fsquare__(tmp, output);
+ fproduct_carry_wide_(tmp);
+ b4 = tmp[4];
+ b0 = tmp[0];
+ b4_ = ((b4) & (((u128)(0x7ffffffffffffLLU))));
+ b0_ = ((b0) + (((u128)(19) * (((u64)(((b4) >> (51))))))));
+ tmp[4] = b4_;
+ tmp[0] = b0_;
+ fproduct_copy_from_wide_(output, tmp);
+ i0 = output[0];
+ i1 = output[1];
+ i0_ = i0 & 0x7ffffffffffffLLU;
+ i1_ = i1 + (i0 >> 51);
+ output[0] = i0_;
+ output[1] = i1_;
+}
+
+static __always_inline void fsquare_fsquare_times_(u64 *output, u128 *tmp,
+ u32 count1)
+{
+ u32 i;
+ fsquare_fsquare_(tmp, output);
+ for (i = 1; i < count1; ++i)
+ fsquare_fsquare_(tmp, output);
+}
+
+static __always_inline void fsquare_fsquare_times(u64 *output, u64 *input,
+ u32 count1)
+{
+ u128 t[5];
+ memcpy(output, input, 5 * sizeof(*input));
+ fsquare_fsquare_times_(output, t, count1);
+}
+
+static __always_inline void fsquare_fsquare_times_inplace(u64 *output,
+ u32 count1)
+{
+ u128 t[5];
+ fsquare_fsquare_times_(output, t, count1);
+}
+
+static __always_inline void crecip_crecip(u64 *out, u64 *z)
+{
+ u64 buf[20] = { 0 };
+ u64 *a0 = buf;
+ u64 *t00 = buf + 5;
+ u64 *b0 = buf + 10;
+ u64 *t01;
+ u64 *b1;
+ u64 *c0;
+ u64 *a;
+ u64 *t0;
+ u64 *b;
+ u64 *c;
+ fsquare_fsquare_times(a0, z, 1);
+ fsquare_fsquare_times(t00, a0, 2);
+ fmul_fmul(b0, t00, z);
+ fmul_fmul(a0, b0, a0);
+ fsquare_fsquare_times(t00, a0, 1);
+ fmul_fmul(b0, t00, b0);
+ fsquare_fsquare_times(t00, b0, 5);
+ t01 = buf + 5;
+ b1 = buf + 10;
+ c0 = buf + 15;
+ fmul_fmul(b1, t01, b1);
+ fsquare_fsquare_times(t01, b1, 10);
+ fmul_fmul(c0, t01, b1);
+ fsquare_fsquare_times(t01, c0, 20);
+ fmul_fmul(t01, t01, c0);
+ fsquare_fsquare_times_inplace(t01, 10);
+ fmul_fmul(b1, t01, b1);
+ fsquare_fsquare_times(t01, b1, 50);
+ a = buf;
+ t0 = buf + 5;
+ b = buf + 10;
+ c = buf + 15;
+ fmul_fmul(c, t0, b);
+ fsquare_fsquare_times(t0, c, 100);
+ fmul_fmul(t0, t0, c);
+ fsquare_fsquare_times_inplace(t0, 50);
+ fmul_fmul(t0, t0, b);
+ fsquare_fsquare_times_inplace(t0, 5);
+ fmul_fmul(out, t0, a);
+}
+
+static __always_inline void fsum(u64 *a, u64 *b)
+{
+ a[0] += b[0];
+ a[1] += b[1];
+ a[2] += b[2];
+ a[3] += b[3];
+ a[4] += b[4];
+}
+
+static __always_inline void fdifference(u64 *a, u64 *b)
+{
+ u64 tmp[5] = { 0 };
+ u64 b0;
+ u64 b1;
+ u64 b2;
+ u64 b3;
+ u64 b4;
+ memcpy(tmp, b, 5 * sizeof(*b));
+ b0 = tmp[0];
+ b1 = tmp[1];
+ b2 = tmp[2];
+ b3 = tmp[3];
+ b4 = tmp[4];
+ tmp[0] = b0 + 0x3fffffffffff68LLU;
+ tmp[1] = b1 + 0x3ffffffffffff8LLU;
+ tmp[2] = b2 + 0x3ffffffffffff8LLU;
+ tmp[3] = b3 + 0x3ffffffffffff8LLU;
+ tmp[4] = b4 + 0x3ffffffffffff8LLU;
+ {
+ u64 xi = a[0];
+ u64 yi = tmp[0];
+ a[0] = yi - xi;
+ }
+ {
+ u64 xi = a[1];
+ u64 yi = tmp[1];
+ a[1] = yi - xi;
+ }
+ {
+ u64 xi = a[2];
+ u64 yi = tmp[2];
+ a[2] = yi - xi;
+ }
+ {
+ u64 xi = a[3];
+ u64 yi = tmp[3];
+ a[3] = yi - xi;
+ }
+ {
+ u64 xi = a[4];
+ u64 yi = tmp[4];
+ a[4] = yi - xi;
+ }
+}
+
+static __always_inline void fscalar(u64 *output, u64 *b, u64 s)
+{
+ u128 tmp[5];
+ u128 b4;
+ u128 b0;
+ u128 b4_;
+ u128 b0_;
+ {
+ u64 xi = b[0];
+ tmp[0] = ((u128)(xi) * (s));
+ }
+ {
+ u64 xi = b[1];
+ tmp[1] = ((u128)(xi) * (s));
+ }
+ {
+ u64 xi = b[2];
+ tmp[2] = ((u128)(xi) * (s));
+ }
+ {
+ u64 xi = b[3];
+ tmp[3] = ((u128)(xi) * (s));
+ }
+ {
+ u64 xi = b[4];
+ tmp[4] = ((u128)(xi) * (s));
+ }
+ fproduct_carry_wide_(tmp);
+ b4 = tmp[4];
+ b0 = tmp[0];
+ b4_ = ((b4) & (((u128)(0x7ffffffffffffLLU))));
+ b0_ = ((b0) + (((u128)(19) * (((u64)(((b4) >> (51))))))));
+ tmp[4] = b4_;
+ tmp[0] = b0_;
+ fproduct_copy_from_wide_(output, tmp);
+}
+
+static __always_inline void fmul(u64 *output, u64 *a, u64 *b)
+{
+ fmul_fmul(output, a, b);
+}
+
+static __always_inline void crecip(u64 *output, u64 *input)
+{
+ crecip_crecip(output, input);
+}
+
+static __always_inline void point_swap_conditional_step(u64 *a, u64 *b,
+ u64 swap1, u32 ctr)
+{
+ u32 i = ctr - 1;
+ u64 ai = a[i];
+ u64 bi = b[i];
+ u64 x = swap1 & (ai ^ bi);
+ u64 ai1 = ai ^ x;
+ u64 bi1 = bi ^ x;
+ a[i] = ai1;
+ b[i] = bi1;
+}
+
+static __always_inline void point_swap_conditional5(u64 *a, u64 *b, u64 swap1)
+{
+ point_swap_conditional_step(a, b, swap1, 5);
+ point_swap_conditional_step(a, b, swap1, 4);
+ point_swap_conditional_step(a, b, swap1, 3);
+ point_swap_conditional_step(a, b, swap1, 2);
+ point_swap_conditional_step(a, b, swap1, 1);
+}
+
+static __always_inline void point_swap_conditional(u64 *a, u64 *b, u64 iswap)
+{
+ u64 swap1 = 0 - iswap;
+ point_swap_conditional5(a, b, swap1);
+ point_swap_conditional5(a + 5, b + 5, swap1);
+}
+
+static __always_inline void point_copy(u64 *output, u64 *input)
+{
+ memcpy(output, input, 5 * sizeof(*input));
+ memcpy(output + 5, input + 5, 5 * sizeof(*input));
+}
+
+static __always_inline void addanddouble_fmonty(u64 *pp, u64 *ppq, u64 *p,
+ u64 *pq, u64 *qmqp)
+{
+ u64 *qx = qmqp;
+ u64 *x2 = pp;
+ u64 *z2 = pp + 5;
+ u64 *x3 = ppq;
+ u64 *z3 = ppq + 5;
+ u64 *x = p;
+ u64 *z = p + 5;
+ u64 *xprime = pq;
+ u64 *zprime = pq + 5;
+ u64 buf[40] = { 0 };
+ u64 *origx = buf;
+ u64 *origxprime0 = buf + 5;
+ u64 *xxprime0;
+ u64 *zzprime0;
+ u64 *origxprime;
+ xxprime0 = buf + 25;
+ zzprime0 = buf + 30;
+ memcpy(origx, x, 5 * sizeof(*x));
+ fsum(x, z);
+ fdifference(z, origx);
+ memcpy(origxprime0, xprime, 5 * sizeof(*xprime));
+ fsum(xprime, zprime);
+ fdifference(zprime, origxprime0);
+ fmul(xxprime0, xprime, z);
+ fmul(zzprime0, x, zprime);
+ origxprime = buf + 5;
+ {
+ u64 *xx0;
+ u64 *zz0;
+ u64 *xxprime;
+ u64 *zzprime;
+ u64 *zzzprime;
+ xx0 = buf + 15;
+ zz0 = buf + 20;
+ xxprime = buf + 25;
+ zzprime = buf + 30;
+ zzzprime = buf + 35;
+ memcpy(origxprime, xxprime, 5 * sizeof(*xxprime));
+ fsum(xxprime, zzprime);
+ fdifference(zzprime, origxprime);
+ fsquare_fsquare_times(x3, xxprime, 1);
+ fsquare_fsquare_times(zzzprime, zzprime, 1);
+ fmul(z3, zzzprime, qx);
+ fsquare_fsquare_times(xx0, x, 1);
+ fsquare_fsquare_times(zz0, z, 1);
+ {
+ u64 *zzz;
+ u64 *xx;
+ u64 *zz;
+ u64 scalar;
+ zzz = buf + 10;
+ xx = buf + 15;
+ zz = buf + 20;
+ fmul(x2, xx, zz);
+ fdifference(zz, xx);
+ scalar = 121665;
+ fscalar(zzz, zz, scalar);
+ fsum(zzz, xx);
+ fmul(z2, zzz, zz);
+ }
+ }
+}
+
+static __always_inline void
+ladder_smallloop_cmult_small_loop_step(u64 *nq, u64 *nqpq, u64 *nq2, u64 *nqpq2,
+ u64 *q, u8 byt)
+{
+ u64 bit0 = (u64)(byt >> 7);
+ u64 bit;
+ point_swap_conditional(nq, nqpq, bit0);
+ addanddouble_fmonty(nq2, nqpq2, nq, nqpq, q);
+ bit = (u64)(byt >> 7);
+ point_swap_conditional(nq2, nqpq2, bit);
+}
+
+static __always_inline void
+ladder_smallloop_cmult_small_loop_double_step(u64 *nq, u64 *nqpq, u64 *nq2,
+ u64 *nqpq2, u64 *q, u8 byt)
+{
+ u8 byt1;
+ ladder_smallloop_cmult_small_loop_step(nq, nqpq, nq2, nqpq2, q, byt);
+ byt1 = byt << 1;
+ ladder_smallloop_cmult_small_loop_step(nq2, nqpq2, nq, nqpq, q, byt1);
+}
+
+static __always_inline void
+ladder_smallloop_cmult_small_loop(u64 *nq, u64 *nqpq, u64 *nq2, u64 *nqpq2,
+ u64 *q, u8 byt, u32 i)
+{
+ while (i--) {
+ ladder_smallloop_cmult_small_loop_double_step(nq, nqpq, nq2,
+ nqpq2, q, byt);
+ byt <<= 2;
+ }
+}
+
+static __always_inline void ladder_bigloop_cmult_big_loop(u8 *n1, u64 *nq,
+ u64 *nqpq, u64 *nq2,
+ u64 *nqpq2, u64 *q,
+ u32 i)
+{
+ while (i--) {
+ u8 byte = n1[i];
+ ladder_smallloop_cmult_small_loop(nq, nqpq, nq2, nqpq2, q,
+ byte, 4);
+ }
+}
+
+static void ladder_cmult(u64 *result, u8 *n1, u64 *q)
+{
+ u64 point_buf[40] = { 0 };
+ u64 *nq = point_buf;
+ u64 *nqpq = point_buf + 10;
+ u64 *nq2 = point_buf + 20;
+ u64 *nqpq2 = point_buf + 30;
+ point_copy(nqpq, q);
+ nq[0] = 1;
+ ladder_bigloop_cmult_big_loop(n1, nq, nqpq, nq2, nqpq2, q, 32);
+ point_copy(result, nq);
+}
+
+static __always_inline void format_fexpand(u64 *output, const u8 *input)
+{
+ const u8 *x00 = input + 6;
+ const u8 *x01 = input + 12;
+ const u8 *x02 = input + 19;
+ const u8 *x0 = input + 24;
+ u64 i0, i1, i2, i3, i4, output0, output1, output2, output3, output4;
+ i0 = get_unaligned_le64(input);
+ i1 = get_unaligned_le64(x00);
+ i2 = get_unaligned_le64(x01);
+ i3 = get_unaligned_le64(x02);
+ i4 = get_unaligned_le64(x0);
+ output0 = i0 & 0x7ffffffffffffLLU;
+ output1 = i1 >> 3 & 0x7ffffffffffffLLU;
+ output2 = i2 >> 6 & 0x7ffffffffffffLLU;
+ output3 = i3 >> 1 & 0x7ffffffffffffLLU;
+ output4 = i4 >> 12 & 0x7ffffffffffffLLU;
+ output[0] = output0;
+ output[1] = output1;
+ output[2] = output2;
+ output[3] = output3;
+ output[4] = output4;
+}
+
+static __always_inline void format_fcontract_first_carry_pass(u64 *input)
+{
+ u64 t0 = input[0];
+ u64 t1 = input[1];
+ u64 t2 = input[2];
+ u64 t3 = input[3];
+ u64 t4 = input[4];
+ u64 t1_ = t1 + (t0 >> 51);
+ u64 t0_ = t0 & 0x7ffffffffffffLLU;
+ u64 t2_ = t2 + (t1_ >> 51);
+ u64 t1__ = t1_ & 0x7ffffffffffffLLU;
+ u64 t3_ = t3 + (t2_ >> 51);
+ u64 t2__ = t2_ & 0x7ffffffffffffLLU;
+ u64 t4_ = t4 + (t3_ >> 51);
+ u64 t3__ = t3_ & 0x7ffffffffffffLLU;
+ input[0] = t0_;
+ input[1] = t1__;
+ input[2] = t2__;
+ input[3] = t3__;
+ input[4] = t4_;
+}
+
+static __always_inline void format_fcontract_first_carry_full(u64 *input)
+{
+ format_fcontract_first_carry_pass(input);
+ modulo_carry_top(input);
+}
+
+static __always_inline void format_fcontract_second_carry_pass(u64 *input)
+{
+ u64 t0 = input[0];
+ u64 t1 = input[1];
+ u64 t2 = input[2];
+ u64 t3 = input[3];
+ u64 t4 = input[4];
+ u64 t1_ = t1 + (t0 >> 51);
+ u64 t0_ = t0 & 0x7ffffffffffffLLU;
+ u64 t2_ = t2 + (t1_ >> 51);
+ u64 t1__ = t1_ & 0x7ffffffffffffLLU;
+ u64 t3_ = t3 + (t2_ >> 51);
+ u64 t2__ = t2_ & 0x7ffffffffffffLLU;
+ u64 t4_ = t4 + (t3_ >> 51);
+ u64 t3__ = t3_ & 0x7ffffffffffffLLU;
+ input[0] = t0_;
+ input[1] = t1__;
+ input[2] = t2__;
+ input[3] = t3__;
+ input[4] = t4_;
+}
+
+static __always_inline void format_fcontract_second_carry_full(u64 *input)
+{
+ u64 i0;
+ u64 i1;
+ u64 i0_;
+ u64 i1_;
+ format_fcontract_second_carry_pass(input);
+ modulo_carry_top(input);
+ i0 = input[0];
+ i1 = input[1];
+ i0_ = i0 & 0x7ffffffffffffLLU;
+ i1_ = i1 + (i0 >> 51);
+ input[0] = i0_;
+ input[1] = i1_;
+}
+
+static __always_inline void format_fcontract_trim(u64 *input)
+{
+ u64 a0 = input[0];
+ u64 a1 = input[1];
+ u64 a2 = input[2];
+ u64 a3 = input[3];
+ u64 a4 = input[4];
+ u64 mask0 = u64_gte_mask(a0, 0x7ffffffffffedLLU);
+ u64 mask1 = u64_eq_mask(a1, 0x7ffffffffffffLLU);
+ u64 mask2 = u64_eq_mask(a2, 0x7ffffffffffffLLU);
+ u64 mask3 = u64_eq_mask(a3, 0x7ffffffffffffLLU);
+ u64 mask4 = u64_eq_mask(a4, 0x7ffffffffffffLLU);
+ u64 mask = (((mask0 & mask1) & mask2) & mask3) & mask4;
+ u64 a0_ = a0 - (0x7ffffffffffedLLU & mask);
+ u64 a1_ = a1 - (0x7ffffffffffffLLU & mask);
+ u64 a2_ = a2 - (0x7ffffffffffffLLU & mask);
+ u64 a3_ = a3 - (0x7ffffffffffffLLU & mask);
+ u64 a4_ = a4 - (0x7ffffffffffffLLU & mask);
+ input[0] = a0_;
+ input[1] = a1_;
+ input[2] = a2_;
+ input[3] = a3_;
+ input[4] = a4_;
+}
+
+static __always_inline void format_fcontract_store(u8 *output, u64 *input)
+{
+ u64 t0 = input[0];
+ u64 t1 = input[1];
+ u64 t2 = input[2];
+ u64 t3 = input[3];
+ u64 t4 = input[4];
+ u64 o0 = t1 << 51 | t0;
+ u64 o1 = t2 << 38 | t1 >> 13;
+ u64 o2 = t3 << 25 | t2 >> 26;
+ u64 o3 = t4 << 12 | t3 >> 39;
+ u8 *b0 = output;
+ u8 *b1 = output + 8;
+ u8 *b2 = output + 16;
+ u8 *b3 = output + 24;
+ put_unaligned_le64(o0, b0);
+ put_unaligned_le64(o1, b1);
+ put_unaligned_le64(o2, b2);
+ put_unaligned_le64(o3, b3);
+}
+
+static __always_inline void format_fcontract(u8 *output, u64 *input)
+{
+ format_fcontract_first_carry_full(input);
+ format_fcontract_second_carry_full(input);
+ format_fcontract_trim(input);
+ format_fcontract_store(output, input);
+}
+
+static __always_inline void format_scalar_of_point(u8 *scalar, u64 *point)
+{
+ u64 *x = point;
+ u64 *z = point + 5;
+ u64 buf[10] __aligned(32) = { 0 };
+ u64 *zmone = buf;
+ u64 *sc = buf + 5;
+ crecip(zmone, z);
+ fmul(sc, x, zmone);
+ format_fcontract(scalar, sc);
+}
+
+static void curve25519_generic(u8 mypublic[CURVE25519_KEY_SIZE],
+ const u8 secret[CURVE25519_KEY_SIZE],
+ const u8 basepoint[CURVE25519_KEY_SIZE])
+{
+ u64 buf0[10] __aligned(32) = { 0 };
+ u64 *x0 = buf0;
+ u64 *z = buf0 + 5;
+ u64 *q;
+ format_fexpand(x0, basepoint);
+ z[0] = 1;
+ q = buf0;
+ {
+ u8 e[32] __aligned(32) = { 0 };
+ u8 *scalar;
+ memcpy(e, secret, 32);
+ curve25519_clamp_secret(e);
+ scalar = e;
+ {
+ u64 buf[15] = { 0 };
+ u64 *nq = buf;
+ u64 *x = nq;
+ x[0] = 1;
+ ladder_cmult(nq, scalar, q);
+ format_scalar_of_point(mypublic, nq);
+ memzero_explicit(buf, sizeof(buf));
+ }
+ memzero_explicit(e, sizeof(e));
+ }
+ memzero_explicit(buf0, sizeof(buf0));
+}
diff --git a/contrib/wireguard-tools/curve25519.h b/contrib/wireguard-tools/curve25519.h
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/curve25519.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0 OR MIT */
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#ifndef CURVE25519_H
+#define CURVE25519_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+enum curve25519_lengths {
+ CURVE25519_KEY_SIZE = 32
+};
+
+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]);
+void curve25519_generate_public(uint8_t pub[static CURVE25519_KEY_SIZE], const uint8_t secret[static CURVE25519_KEY_SIZE]);
+static inline void curve25519_clamp_secret(uint8_t secret[static CURVE25519_KEY_SIZE])
+{
+ secret[0] &= 248;
+ secret[31] = (secret[31] & 127) | 64;
+}
+
+#endif
diff --git a/contrib/wireguard-tools/curve25519.c b/contrib/wireguard-tools/curve25519.c
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/curve25519.c
@@ -0,0 +1,98 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * Copyright (C) 2018-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#include "curve25519.h"
+
+#include <stdint.h>
+#include <string.h>
+
+#ifndef __BYTE_ORDER__
+#include <sys/param.h>
+#if !defined(BYTE_ORDER) || !defined(BIG_ENDIAN) || !defined(LITTLE_ENDIAN)
+#error "Unable to determine endianness."
+#endif
+#define __BYTE_ORDER__ BYTE_ORDER
+#define __ORDER_BIG_ENDIAN__ BIG_ENDIAN
+#define __ORDER_LITTLE_ENDIAN__ LITTLE_ENDIAN
+#endif
+
+#ifdef __linux__
+#include <linux/types.h>
+typedef __u64 u64;
+typedef __u32 u32;
+typedef __u8 u8;
+typedef __s64 s64;
+#else
+typedef uint64_t u64, __le64;
+typedef uint32_t u32, __le32;
+typedef uint8_t u8;
+typedef int64_t s64;
+#endif
+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+#define le64_to_cpup(a) __builtin_bswap64(*(a))
+#define le32_to_cpup(a) __builtin_bswap32(*(a))
+#define cpu_to_le64(a) __builtin_bswap64(a)
+#else
+#define le64_to_cpup(a) (*(a))
+#define le32_to_cpup(a) (*(a))
+#define cpu_to_le64(a) (a)
+#endif
+#ifndef __unused
+#define __unused __attribute__((unused))
+#endif
+#ifndef __always_inline
+#define __always_inline __inline __attribute__((__always_inline__))
+#endif
+#ifndef noinline
+#define noinline __attribute__((noinline))
+#endif
+#ifndef __aligned
+#define __aligned(x) __attribute__((aligned(x)))
+#endif
+#ifndef __force
+#define __force
+#endif
+
+static __always_inline __unused __le32 get_unaligned_le32(const u8 *a)
+{
+ __le32 l;
+ __builtin_memcpy(&l, a, sizeof(l));
+ return le32_to_cpup(&l);
+}
+static __always_inline __unused __le64 get_unaligned_le64(const u8 *a)
+{
+ __le64 l;
+ __builtin_memcpy(&l, a, sizeof(l));
+ return le64_to_cpup(&l);
+}
+static __always_inline __unused void put_unaligned_le64(u64 s, u8 *d)
+{
+ __le64 l = cpu_to_le64(s);
+ __builtin_memcpy(d, &l, sizeof(l));
+}
+
+static noinline void memzero_explicit(void *s, size_t count)
+{
+ memset(s, 0, count);
+ asm volatile("": :"r"(s) : "memory");
+}
+
+#ifdef __SIZEOF_INT128__
+#include "curve25519-hacl64.h"
+#else
+#include "curve25519-fiat32.h"
+#endif
+
+void curve25519_generate_public(uint8_t pub[static CURVE25519_KEY_SIZE], const uint8_t secret[static CURVE25519_KEY_SIZE])
+{
+ static const uint8_t basepoint[CURVE25519_KEY_SIZE] __aligned(sizeof(uintptr_t)) = { 9 };
+
+ curve25519(pub, secret, basepoint);
+}
+
+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])
+{
+ curve25519_generic(mypublic, secret, basepoint);
+}
diff --git a/contrib/wireguard-tools/encoding.h b/contrib/wireguard-tools/encoding.h
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/encoding.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0 OR MIT */
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#ifndef ENCODING_H
+#define ENCODING_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include "containers.h"
+
+#define WG_KEY_LEN_BASE64 ((((WG_KEY_LEN) + 2) / 3) * 4 + 1)
+#define WG_KEY_LEN_HEX (WG_KEY_LEN * 2 + 1)
+
+void key_to_base64(char base64[static WG_KEY_LEN_BASE64], const uint8_t key[static WG_KEY_LEN]);
+bool key_from_base64(uint8_t key[static WG_KEY_LEN], const char *base64);
+
+void key_to_hex(char hex[static WG_KEY_LEN_HEX], const uint8_t key[static WG_KEY_LEN]);
+bool key_from_hex(uint8_t key[static WG_KEY_LEN], const char *hex);
+
+bool key_is_zero(const uint8_t key[static WG_KEY_LEN]);
+
+#endif
diff --git a/contrib/wireguard-tools/encoding.c b/contrib/wireguard-tools/encoding.c
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/encoding.c
@@ -0,0 +1,125 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ *
+ * This is a specialized constant-time base64/hex implementation that resists side-channel attacks.
+ */
+
+#include <string.h>
+#include "encoding.h"
+
+static inline void encode_base64(char dest[static 4], const uint8_t src[static 3])
+{
+ 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 };
+
+ for (unsigned int i = 0; i < 4; ++i)
+ dest[i] = input[i] + 'A'
+ + (((25 - input[i]) >> 8) & 6)
+ - (((51 - input[i]) >> 8) & 75)
+ - (((61 - input[i]) >> 8) & 15)
+ + (((62 - input[i]) >> 8) & 3);
+
+}
+
+void key_to_base64(char base64[static WG_KEY_LEN_BASE64], const uint8_t key[static WG_KEY_LEN])
+{
+ unsigned int i;
+
+ for (i = 0; i < WG_KEY_LEN / 3; ++i)
+ encode_base64(&base64[i * 4], &key[i * 3]);
+ encode_base64(&base64[i * 4], (const uint8_t[]){ key[i * 3 + 0], key[i * 3 + 1], 0 });
+ base64[WG_KEY_LEN_BASE64 - 2] = '=';
+ base64[WG_KEY_LEN_BASE64 - 1] = '\0';
+}
+
+static inline int decode_base64(const char src[static 4])
+{
+ int val = 0;
+
+ for (unsigned int i = 0; i < 4; ++i)
+ val |= (-1
+ + ((((('A' - 1) - src[i]) & (src[i] - ('Z' + 1))) >> 8) & (src[i] - 64))
+ + ((((('a' - 1) - src[i]) & (src[i] - ('z' + 1))) >> 8) & (src[i] - 70))
+ + ((((('0' - 1) - src[i]) & (src[i] - ('9' + 1))) >> 8) & (src[i] + 5))
+ + ((((('+' - 1) - src[i]) & (src[i] - ('+' + 1))) >> 8) & 63)
+ + ((((('/' - 1) - src[i]) & (src[i] - ('/' + 1))) >> 8) & 64)
+ ) << (18 - 6 * i);
+ return val;
+}
+
+bool key_from_base64(uint8_t key[static WG_KEY_LEN], const char *base64)
+{
+ unsigned int i;
+ volatile uint8_t ret = 0;
+ int val;
+
+ if (strlen(base64) != WG_KEY_LEN_BASE64 - 1 || base64[WG_KEY_LEN_BASE64 - 2] != '=')
+ return false;
+
+ for (i = 0; i < WG_KEY_LEN / 3; ++i) {
+ val = decode_base64(&base64[i * 4]);
+ ret |= (uint32_t)val >> 31;
+ key[i * 3 + 0] = (val >> 16) & 0xff;
+ key[i * 3 + 1] = (val >> 8) & 0xff;
+ key[i * 3 + 2] = val & 0xff;
+ }
+ val = decode_base64((const char[]){ base64[i * 4 + 0], base64[i * 4 + 1], base64[i * 4 + 2], 'A' });
+ ret |= ((uint32_t)val >> 31) | (val & 0xff);
+ key[i * 3 + 0] = (val >> 16) & 0xff;
+ key[i * 3 + 1] = (val >> 8) & 0xff;
+
+ return 1 & ((ret - 1) >> 8);
+}
+
+void key_to_hex(char hex[static WG_KEY_LEN_HEX], const uint8_t key[static WG_KEY_LEN])
+{
+ unsigned int i;
+
+ for (i = 0; i < WG_KEY_LEN; ++i) {
+ hex[i * 2] = 87U + (key[i] >> 4) + ((((key[i] >> 4) - 10U) >> 8) & ~38U);
+ hex[i * 2 + 1] = 87U + (key[i] & 0xf) + ((((key[i] & 0xf) - 10U) >> 8) & ~38U);
+ }
+ hex[i * 2] = '\0';
+}
+
+bool key_from_hex(uint8_t key[static WG_KEY_LEN], const char *hex)
+{
+ uint8_t c, c_acc, c_alpha0, c_alpha, c_num0, c_num, c_val;
+ volatile uint8_t ret = 0;
+
+ if (strlen(hex) != WG_KEY_LEN_HEX - 1)
+ return false;
+
+ for (unsigned int i = 0; i < WG_KEY_LEN_HEX - 1; i += 2) {
+ c = (uint8_t)hex[i];
+ c_num = c ^ 48U;
+ c_num0 = (c_num - 10U) >> 8;
+ c_alpha = (c & ~32U) - 55U;
+ c_alpha0 = ((c_alpha - 10U) ^ (c_alpha - 16U)) >> 8;
+ ret |= ((c_num0 | c_alpha0) - 1) >> 8;
+ c_val = (c_num0 & c_num) | (c_alpha0 & c_alpha);
+ c_acc = c_val * 16U;
+
+ c = (uint8_t)hex[i + 1];
+ c_num = c ^ 48U;
+ c_num0 = (c_num - 10U) >> 8;
+ c_alpha = (c & ~32U) - 55U;
+ c_alpha0 = ((c_alpha - 10U) ^ (c_alpha - 16U)) >> 8;
+ ret |= ((c_num0 | c_alpha0) - 1) >> 8;
+ c_val = (c_num0 & c_num) | (c_alpha0 & c_alpha);
+ key[i / 2] = c_acc | c_val;
+ }
+
+ return 1 & ((ret - 1) >> 8);
+}
+
+bool key_is_zero(const uint8_t key[static WG_KEY_LEN])
+{
+ volatile uint8_t acc = 0;
+
+ for (unsigned int i = 0; i < WG_KEY_LEN; ++i) {
+ acc |= key[i];
+ asm volatile("" : "=r"(acc) : "0"(acc));
+ }
+ return 1 & ((acc - 1) >> 8);
+}
diff --git a/contrib/wireguard-tools/fuzz/.gitignore b/contrib/wireguard-tools/fuzz/.gitignore
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/fuzz/.gitignore
@@ -0,0 +1,6 @@
+config
+uapi
+stringlist
+cmd
+set
+setconf
diff --git a/contrib/wireguard-tools/fuzz/Makefile b/contrib/wireguard-tools/fuzz/Makefile
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/fuzz/Makefile
@@ -0,0 +1,34 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (C) 2018-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+
+FUZZERS := config uapi stringlist cmd set setconf
+
+all: $(FUZZERS)
+
+CFLAGS ?= -O3 -march=native -g
+CFLAGS += -fsanitize=fuzzer -fsanitize=address -std=gnu11 -idirafter ../uapi -D_GNU_SOURCE
+CC := clang
+
+config: config.c ../config.c ../encoding.c
+ $(CC) $(CFLAGS) -o $@ $<
+
+uapi: uapi.c ../ipc.c ../curve25519.c ../encoding.c
+ $(CC) $(CFLAGS) -o $@ $<
+
+stringlist: stringlist.c ../ipc.c ../curve25519.c ../encoding.c
+ $(CC) $(CFLAGS) -o $@ $<
+
+cmd: cmd.c $(wildcard ../*.c)
+ $(CC) $(CFLAGS) -D'RUNSTATEDIR="/var/empty"' -D'main(a,b)=wg_main(a,b)' -o $@ $^
+
+set: set.c ../set.c ../ipc.c ../encoding.c ../curve25519.c ../config.c
+ $(CC) $(CFLAGS) -o $@ $<
+
+setconf: setconf.c ../setconf.c ../ipc.c ../encoding.c ../curve25519.c ../config.c
+ $(CC) $(CFLAGS) -o $@ $<
+
+clean:
+ $(RM) $(FUZZERS)
+
+.PHONY: all clean
diff --git a/contrib/wireguard-tools/fuzz/cmd.c b/contrib/wireguard-tools/fuzz/cmd.c
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/fuzz/cmd.c
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+
+const char *__asan_default_options()
+{
+ return "verbosity=1";
+}
+
+int wg_main(int argc, char *argv[]);
+
+static FILE *devnull;
+
+int LLVMFuzzerTestOneInput(const char *data, size_t data_len)
+{
+ char *argv[8192] = { 0 }, *args;
+ size_t argc = 0;
+ FILE *fake_stdin = NULL;
+
+ if (!devnull) {
+ assert((devnull = fopen("/dev/null", "r+")));
+ stdin = stdout = stderr = devnull;
+ }
+
+ assert((args = malloc(data_len)));
+ memcpy(args, data, data_len);
+ if (data_len)
+ args[data_len - 1] = '\0';
+
+ for (const char *arg = args; argc < 8192 && arg - args < data_len; arg += strlen(arg) + 1) {
+ if (arg[0])
+ assert((argv[argc++] = strdup(arg)));
+ }
+ if (!argc)
+ assert((argv[argc++] = strdup("no argv[0]!")));
+ 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"))) {
+ free(argv[2]);
+ assert((argv[2] = strdup("wg0")));
+ }
+ if (argc >= 2 && !strcmp(argv[1], "pubkey")) {
+ char *arg;
+ size_t len;
+
+ for (size_t i = 2; i < argc; ++i)
+ free(argv[i]);
+ argc = 2;
+ arg = args;
+ for (; !arg[0]; ++arg);
+ arg += strlen(arg) + 1;
+ for (; !arg[0]; ++arg);
+ arg += strlen(arg) + 1;
+ len = data_len - (arg - args);
+ if (len <= 1)
+ goto done;
+ assert((fake_stdin = fmemopen(arg, len - 1, "r")));
+ stdin = fake_stdin;
+ }
+ wg_main(argc, argv);
+done:
+ for (size_t i = 0; i < argc; ++i)
+ free(argv[i]);
+ free(args);
+ if (fake_stdin)
+ fclose(fake_stdin);
+ return 0;
+}
diff --git a/contrib/wireguard-tools/fuzz/config.c b/contrib/wireguard-tools/fuzz/config.c
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/fuzz/config.c
@@ -0,0 +1,69 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#include <stdio.h>
+#undef stderr
+#define stderr stdin
+#include "../config.c"
+#include "../encoding.c"
+#undef stderr
+
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "../config.h"
+
+const char *__asan_default_options()
+{
+ return "verbosity=1";
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t len)
+{
+ bool file;
+ char *input;
+
+ if (len < 2)
+ return 0;
+ file = !!(data[0] >> 7);
+ input = malloc(len);
+ if (!input)
+ return 0;
+ memcpy(input, data + 1, len - 1);
+ input[len - 1] = '\0';
+
+ if (file) {
+ struct config_ctx ctx;
+ char *saveptr;
+
+ config_read_init(&ctx, false);
+ for (char *line = strtok_r(input, "\n", &saveptr); line; line = strtok_r(NULL, "\n", &saveptr)) {
+ if (!config_read_line(&ctx, line))
+ config_read_init(&ctx, false);
+ }
+ free_wgdevice(config_read_finish(&ctx));
+ } else {
+ size_t spaces = 0;
+ char **argv, *saveptr;
+
+ for (char *c = input; *c; ++c) {
+ if (*c == ' ')
+ ++spaces;
+ }
+ argv = calloc(spaces + 1, sizeof(char *));
+ if (!argv)
+ goto out;
+ spaces = 0;
+ for (char *token = strtok_r(input, " ", &saveptr); token; token = strtok_r(NULL, " ", &saveptr))
+ argv[spaces++] = token;
+ free_wgdevice(config_read_cmd(argv, spaces));
+ free(argv);
+ }
+
+out:
+ free(input);
+ return 0;
+}
diff --git a/contrib/wireguard-tools/fuzz/set.c b/contrib/wireguard-tools/fuzz/set.c
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/fuzz/set.c
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#include <stdio.h>
+#undef stderr
+#define stderr stdin
+#define RUNSTATEDIR "/var/empty"
+#include "../curve25519.c"
+#define parse_allowedips parse_allowedips_ipc
+#include "../ipc.c"
+#undef parse_allowedips
+#include "../encoding.c"
+static FILE *hacked_fopen(const char *pathname, const char *mode);
+#define fopen hacked_fopen
+#include "../config.c"
+#include "../set.c"
+#undef stderr
+
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+
+const char *__asan_default_options()
+{
+ return "verbosity=1";
+}
+
+const char *PROG_NAME = "wg";
+
+static FILE *hacked_fopen(const char *pathname, const char *mode)
+{
+ return fmemopen((char *)pathname, strlen(pathname), "r");
+}
+
+int LLVMFuzzerTestOneInput(const char *data, size_t data_len)
+{
+ char *argv[8192] = { "set", "wg0" }, *args;
+ size_t argc = 2;
+
+ if (!data_len)
+ return 0;
+
+ assert((args = malloc(data_len)));
+ memcpy(args, data, data_len);
+ args[data_len - 1] = '\0';
+
+ for (char *arg = strtok(args, " \t\n\r"); arg && argc < 8192; arg = strtok(NULL, " \t\n\r")) {
+ if (arg[0])
+ argv[argc++] = arg;
+ }
+ set_main(argc, argv);
+ free(args);
+ return 0;
+}
diff --git a/contrib/wireguard-tools/fuzz/setconf.c b/contrib/wireguard-tools/fuzz/setconf.c
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/fuzz/setconf.c
@@ -0,0 +1,53 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#include <stdio.h>
+#undef stderr
+#define stderr stdin
+#define RUNSTATEDIR "/var/empty"
+#include "../curve25519.c"
+#define parse_allowedips parse_allowedips_ipc
+#include "../ipc.c"
+#undef parse_allowedips
+#include "../encoding.c"
+#include "../config.c"
+static FILE *hacked_fopen(const char *pathname, const char *mode);
+#define fopen hacked_fopen
+#include "../setconf.c"
+#undef fopen
+#undef stderr
+
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+
+const char *__asan_default_options()
+{
+ return "verbosity=1";
+}
+
+const char *PROG_NAME = "wg";
+
+struct hacked_pointers {
+ const char *data;
+ size_t data_len;
+};
+
+static FILE *hacked_fopen(const char *pathname, const char *mode)
+{
+ struct hacked_pointers *h = (struct hacked_pointers *)strtoul(pathname, NULL, 10);
+ return fmemopen((char *)h->data, h->data_len, "r");
+}
+
+int LLVMFuzzerTestOneInput(const char *data, size_t data_len)
+{
+ char strptr[32];
+ char *argv[3] = { "setconf", "wg0", strptr };
+ struct hacked_pointers h = { data, data_len };
+
+ snprintf(strptr, sizeof(strptr), "%lu", (unsigned long)&h);
+ setconf_main(3, argv);
+ return 0;
+}
diff --git a/contrib/wireguard-tools/fuzz/stringlist.c b/contrib/wireguard-tools/fuzz/stringlist.c
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/fuzz/stringlist.c
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#define RUNSTATEDIR "/var/empty"
+#include "../curve25519.c"
+#undef __linux__
+#include "../ipc.c"
+#include "../encoding.c"
+
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+
+const char *__asan_default_options()
+{
+ return "verbosity=1";
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t data_len)
+{
+ struct string_list list = { 0 };
+ char *interfaces;
+
+ if (!data_len)
+ return 0;
+
+ interfaces = malloc(data_len);
+ assert(interfaces);
+ memcpy(interfaces, data, data_len);
+ interfaces[data_len - 1] = '\0';
+
+ for (char *interface = interfaces; interface - interfaces < data_len; interface += strlen(interface) + 1)
+ assert(string_list_add(&list, interface) == 0);
+
+ for (char *interface = interfaces, *interface2 = list.buffer;;) {
+ size_t len;
+
+ if (interface - interfaces >= data_len) {
+ assert(!interface2 || !strlen(interface2));
+ break;
+ }
+ len = strlen(interface);
+ if (!len) {
+ ++interface;
+ continue;
+ }
+ assert(strlen(interface2) == len);
+ assert(!memcmp(interface, interface2, len + 1));
+ interface += len + 1;
+ interface2 += len + 1;
+ }
+ free(list.buffer);
+ free(interfaces);
+ return 0;
+}
diff --git a/contrib/wireguard-tools/fuzz/uapi.c b/contrib/wireguard-tools/fuzz/uapi.c
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/fuzz/uapi.c
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#include <stdio.h>
+#include <sys/stat.h>
+static FILE *hacked_userspace_interface_file(const char *iface);
+#define stat(a, b) ({ return hacked_userspace_interface_file(iface); 0; })
+#define RUNSTATEDIR "/var/empty"
+#include "../curve25519.c"
+#undef __linux__
+#include "../ipc.c"
+#include "../encoding.c"
+
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+const char *__asan_default_options()
+{
+ return "verbosity=1";
+}
+
+union hackiface {
+ char ifname[IFNAMSIZ];
+ struct {
+ const uint8_t *data;
+ size_t len;
+ };
+};
+
+static FILE *hacked_userspace_interface_file(const char *iface)
+{
+ union hackiface *hack = (union hackiface *)iface;
+ FILE *f = fmemopen(NULL, hack->len + 7, "r+");
+ fseek(f, 7, SEEK_SET);
+ fwrite(hack->data, hack->len, 1, f);
+ fseek(f, 0, SEEK_SET);
+ memcpy(hack->ifname, "hack", 5);
+ return f;
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t len)
+{
+ union hackiface hack = {
+ .data = data,
+ .len = len
+ };
+ struct wgdevice *dev = NULL;
+
+ userspace_get_device(&dev, (const char *)&hack);
+ free_wgdevice(dev);
+ return 0;
+}
diff --git a/contrib/wireguard-tools/genkey.c b/contrib/wireguard-tools/genkey.c
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/genkey.c
@@ -0,0 +1,99 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <fcntl.h>
+#ifdef __linux__
+#include <sys/syscall.h>
+#endif
+#ifdef __APPLE__
+#include <AvailabilityMacros.h>
+#ifndef MAC_OS_X_VERSION_10_12
+#define MAC_OS_X_VERSION_10_12 101200
+#endif
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_12
+#include <sys/random.h>
+#endif
+#endif
+
+#include "curve25519.h"
+#include "encoding.h"
+#include "subcommands.h"
+
+#ifndef _WIN32
+static inline bool __attribute__((__warn_unused_result__)) get_random_bytes(uint8_t *out, size_t len)
+{
+ ssize_t ret = 0;
+ size_t i;
+ int fd;
+
+ if (len > 256) {
+ errno = EOVERFLOW;
+ return false;
+ }
+
+#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)))
+ if (!getentropy(out, len))
+ return true;
+#endif
+
+#if defined(__NR_getrandom) && defined(__linux__)
+ if (syscall(__NR_getrandom, out, len, 0) == (ssize_t)len)
+ return true;
+#endif
+
+ fd = open("/dev/urandom", O_RDONLY);
+ if (fd < 0)
+ return false;
+ for (errno = 0, i = 0; i < len; i += ret, ret = 0) {
+ ret = read(fd, out + i, len - i);
+ if (ret <= 0) {
+ ret = errno ? -errno : -EIO;
+ break;
+ }
+ }
+ close(fd);
+ errno = -ret;
+ return i == len;
+}
+#else
+#include <ntsecapi.h>
+static inline bool __attribute__((__warn_unused_result__)) get_random_bytes(uint8_t *out, size_t len)
+{
+ return RtlGenRandom(out, len);
+}
+#endif
+
+int genkey_main(int argc, const char *argv[])
+{
+ uint8_t key[WG_KEY_LEN];
+ char base64[WG_KEY_LEN_BASE64];
+ struct stat stat;
+
+ if (argc != 1) {
+ fprintf(stderr, "Usage: %s %s\n", PROG_NAME, argv[0]);
+ return 1;
+ }
+
+ if (!fstat(STDOUT_FILENO, &stat) && S_ISREG(stat.st_mode) && stat.st_mode & S_IRWXO)
+ fputs("Warning: writing to world accessible file.\nConsider setting the umask to 077 and trying again.\n", stderr);
+
+ if (!get_random_bytes(key, WG_KEY_LEN)) {
+ perror("getrandom");
+ return 1;
+ }
+ if (!strcmp(argv[0], "genkey"))
+ curve25519_clamp_secret(key);
+
+ key_to_base64(base64, key);
+ puts(base64);
+ return 0;
+}
diff --git a/contrib/wireguard-tools/ipc-freebsd.h b/contrib/wireguard-tools/ipc-freebsd.h
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/ipc-freebsd.h
@@ -0,0 +1,350 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright (C) 2015-2021 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ *
+ */
+
+#include <sys/nv.h>
+#include <sys/sockio.h>
+#include <dev/if_wg/if_wg.h>
+
+#define IPC_SUPPORTS_KERNEL_INTERFACE
+
+static int get_dgram_socket(void)
+{
+ static int sock = -1;
+ if (sock < 0)
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+ return sock;
+}
+
+static int kernel_get_wireguard_interfaces(struct string_list *list)
+{
+ struct ifgroupreq ifgr = { .ifgr_name = "wg" };
+ struct ifg_req *ifg;
+ int s = get_dgram_socket(), ret = 0;
+
+ if (s < 0)
+ return -errno;
+
+ if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) < 0)
+ return errno == ENOENT ? 0 : -errno;
+
+ ifgr.ifgr_groups = calloc(1, ifgr.ifgr_len);
+ if (!ifgr.ifgr_groups)
+ return -errno;
+ if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) < 0) {
+ ret = -errno;
+ goto out;
+ }
+
+ for (ifg = ifgr.ifgr_groups; ifg && ifgr.ifgr_len > 0; ++ifg) {
+ if ((ret = string_list_add(list, ifg->ifgrq_member)) < 0)
+ goto out;
+ ifgr.ifgr_len -= sizeof(struct ifg_req);
+ }
+
+out:
+ free(ifgr.ifgr_groups);
+ return ret;
+}
+
+static int kernel_get_device(struct wgdevice **device, const char *ifname)
+{
+ struct wg_data_io wgd = { 0 };
+ nvlist_t *nvl_device = NULL;
+ const nvlist_t *const *nvl_peers;
+ struct wgdevice *dev = NULL;
+ size_t size, peer_count, i;
+ uint64_t number;
+ const void *binary;
+ int ret = 0, s;
+
+ *device = NULL;
+ s = get_dgram_socket();
+ if (s < 0)
+ goto err;
+
+ strlcpy(wgd.wgd_name, ifname, sizeof(wgd.wgd_name));
+ if (ioctl(s, SIOCGWG, &wgd) < 0)
+ goto err;
+
+ wgd.wgd_data = malloc(wgd.wgd_size);
+ if (!wgd.wgd_data)
+ goto err;
+ if (ioctl(s, SIOCGWG, &wgd) < 0)
+ goto err;
+
+ dev = calloc(1, sizeof(*dev));
+ if (!dev)
+ goto err;
+ strlcpy(dev->name, ifname, sizeof(dev->name));
+ nvl_device = nvlist_unpack(wgd.wgd_data, wgd.wgd_size, 0);
+ if (!nvl_device)
+ goto err;
+
+ if (nvlist_exists_number(nvl_device, "listen-port")) {
+ number = nvlist_get_number(nvl_device, "listen-port");
+ if (number <= UINT16_MAX) {
+ dev->listen_port = number;
+ dev->flags |= WGDEVICE_HAS_LISTEN_PORT;
+ }
+ }
+ if (nvlist_exists_number(nvl_device, "user-cookie")) {
+ number = nvlist_get_number(nvl_device, "user-cookie");
+ if (number <= UINT32_MAX) {
+ dev->fwmark = number;
+ dev->flags |= WGDEVICE_HAS_FWMARK;
+ }
+ }
+ if (nvlist_exists_binary(nvl_device, "public-key")) {
+ binary = nvlist_get_binary(nvl_device, "public-key", &size);
+ if (binary && size == sizeof(dev->public_key)) {
+ memcpy(dev->public_key, binary, sizeof(dev->public_key));
+ dev->flags |= WGDEVICE_HAS_PUBLIC_KEY;
+ }
+ }
+ if (nvlist_exists_binary(nvl_device, "private-key")) {
+ binary = nvlist_get_binary(nvl_device, "private-key", &size);
+ if (binary && size == sizeof(dev->private_key)) {
+ memcpy(dev->private_key, binary, sizeof(dev->private_key));
+ dev->flags |= WGDEVICE_HAS_PRIVATE_KEY;
+ }
+ }
+ if (!nvlist_exists_nvlist_array(nvl_device, "peers"))
+ goto skip_peers;
+ nvl_peers = nvlist_get_nvlist_array(nvl_device, "peers", &peer_count);
+ if (!nvl_peers)
+ goto skip_peers;
+ for (i = 0; i < peer_count; ++i) {
+ struct wgpeer *peer;
+ struct wgallowedip *aip;
+ const nvlist_t *const *nvl_aips;
+ size_t aip_count, j;
+
+ peer = calloc(1, sizeof(*peer));
+ if (!peer)
+ goto err_peer;
+ if (nvlist_exists_binary(nvl_peers[i], "public-key")) {
+ binary = nvlist_get_binary(nvl_peers[i], "public-key", &size);
+ if (binary && size == sizeof(peer->public_key)) {
+ memcpy(peer->public_key, binary, sizeof(peer->public_key));
+ peer->flags |= WGPEER_HAS_PUBLIC_KEY;
+ }
+ }
+ if (nvlist_exists_binary(nvl_peers[i], "preshared-key")) {
+ binary = nvlist_get_binary(nvl_peers[i], "preshared-key", &size);
+ if (binary && size == sizeof(peer->preshared_key)) {
+ memcpy(peer->preshared_key, binary, sizeof(peer->preshared_key));
+ if (!key_is_zero(peer->preshared_key))
+ peer->flags |= WGPEER_HAS_PRESHARED_KEY;
+ }
+ }
+ if (nvlist_exists_number(nvl_peers[i], "persistent-keepalive-interval")) {
+ number = nvlist_get_number(nvl_peers[i], "persistent-keepalive-interval");
+ if (number <= UINT16_MAX) {
+ peer->persistent_keepalive_interval = number;
+ peer->flags |= WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL;
+ }
+ }
+ if (nvlist_exists_binary(nvl_peers[i], "endpoint")) {
+ const struct sockaddr *endpoint = nvlist_get_binary(nvl_peers[i], "endpoint", &size);
+ if (endpoint && size <= sizeof(peer->endpoint) && size >= sizeof(peer->endpoint.addr) &&
+ (endpoint->sa_family == AF_INET || endpoint->sa_family == AF_INET6))
+ memcpy(&peer->endpoint.addr, endpoint, size);
+ }
+ if (nvlist_exists_number(nvl_peers[i], "rx-bytes"))
+ peer->rx_bytes = nvlist_get_number(nvl_peers[i], "rx-bytes");
+ if (nvlist_exists_number(nvl_peers[i], "tx-bytes"))
+ peer->tx_bytes = nvlist_get_number(nvl_peers[i], "tx-bytes");
+ if (nvlist_exists_binary(nvl_peers[i], "last-handshake-time")) {
+ binary = nvlist_get_binary(nvl_peers[i], "last-handshake-time", &size);
+ if (binary && size == sizeof(peer->last_handshake_time))
+ memcpy(&peer->last_handshake_time, binary, sizeof(peer->last_handshake_time));
+ }
+
+ if (!nvlist_exists_nvlist_array(nvl_peers[i], "allowed-ips"))
+ goto skip_allowed_ips;
+ nvl_aips = nvlist_get_nvlist_array(nvl_peers[i], "allowed-ips", &aip_count);
+ if (!aip_count || !nvl_aips)
+ goto skip_allowed_ips;
+ for (j = 0; j < aip_count; ++j) {
+ aip = calloc(1, sizeof(*aip));
+ if (!aip)
+ goto err_allowed_ips;
+ if (!nvlist_exists_number(nvl_aips[j], "cidr"))
+ continue;
+ number = nvlist_get_number(nvl_aips[j], "cidr");
+ if (nvlist_exists_binary(nvl_aips[j], "ipv4")) {
+ binary = nvlist_get_binary(nvl_aips[j], "ipv4", &size);
+ if (!binary || number > 32) {
+ ret = EINVAL;
+ goto err_allowed_ips;
+ }
+ aip->family = AF_INET;
+ aip->cidr = number;
+ memcpy(&aip->ip4, binary, sizeof(aip->ip4));
+ } else if (nvlist_exists_binary(nvl_aips[j], "ipv6")) {
+ binary = nvlist_get_binary(nvl_aips[j], "ipv6", &size);
+ if (!binary || number > 128) {
+ ret = EINVAL;
+ goto err_allowed_ips;
+ }
+ aip->family = AF_INET6;
+ aip->cidr = number;
+ memcpy(&aip->ip6, binary, sizeof(aip->ip6));
+ } else
+ continue;
+
+ if (!peer->first_allowedip)
+ peer->first_allowedip = aip;
+ else
+ peer->last_allowedip->next_allowedip = aip;
+ peer->last_allowedip = aip;
+ continue;
+
+ err_allowed_ips:
+ if (!ret)
+ ret = -errno;
+ free(aip);
+ goto err_peer;
+ }
+ skip_allowed_ips:
+ if (!dev->first_peer)
+ dev->first_peer = peer;
+ else
+ dev->last_peer->next_peer = peer;
+ dev->last_peer = peer;
+ continue;
+
+ err_peer:
+ if (!ret)
+ ret = -errno;
+ free(peer);
+ goto err;
+ }
+
+skip_peers:
+ free(wgd.wgd_data);
+ nvlist_destroy(nvl_device);
+ *device = dev;
+ return 0;
+
+err:
+ if (!ret)
+ ret = -errno;
+ free(wgd.wgd_data);
+ nvlist_destroy(nvl_device);
+ free(dev);
+ return ret;
+}
+
+
+static int kernel_set_device(struct wgdevice *dev)
+{
+ struct wg_data_io wgd = { 0 };
+ nvlist_t *nvl_device = NULL, **nvl_peers = NULL;
+ size_t peer_count = 0, i = 0;
+ struct wgpeer *peer;
+ int ret = 0, s;
+
+ strlcpy(wgd.wgd_name, dev->name, sizeof(wgd.wgd_name));
+
+ nvl_device = nvlist_create(0);
+ if (!nvl_device)
+ goto err;
+
+ for_each_wgpeer(dev, peer)
+ ++peer_count;
+ if (peer_count) {
+ nvl_peers = calloc(peer_count, sizeof(*nvl_peers));
+ if (!nvl_peers)
+ goto err;
+ }
+ if (dev->flags & WGDEVICE_HAS_PRIVATE_KEY)
+ nvlist_add_binary(nvl_device, "private-key", dev->private_key, sizeof(dev->private_key));
+ if (dev->flags & WGDEVICE_HAS_LISTEN_PORT)
+ nvlist_add_number(nvl_device, "listen-port", dev->listen_port);
+ if (dev->flags & WGDEVICE_HAS_FWMARK)
+ nvlist_add_number(nvl_device, "user-cookie", dev->fwmark);
+ if (dev->flags & WGDEVICE_REPLACE_PEERS)
+ nvlist_add_bool(nvl_device, "replace-peers", true);
+
+ for_each_wgpeer(dev, peer) {
+ size_t aip_count = 0, j = 0;
+ nvlist_t **nvl_aips = NULL;
+ struct wgallowedip *aip;
+
+ nvl_peers[i] = nvlist_create(0);
+ if (!nvl_peers[i])
+ goto err_peer;
+ for_each_wgallowedip(peer, aip)
+ ++aip_count;
+ if (aip_count) {
+ nvl_aips = calloc(aip_count, sizeof(*nvl_aips));
+ if (!nvl_aips)
+ goto err_peer;
+ }
+ nvlist_add_binary(nvl_peers[i], "public-key", peer->public_key, sizeof(peer->public_key));
+ if (peer->flags & WGPEER_HAS_PRESHARED_KEY)
+ nvlist_add_binary(nvl_peers[i], "preshared-key", peer->preshared_key, sizeof(peer->preshared_key));
+ if (peer->flags & WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL)
+ nvlist_add_number(nvl_peers[i], "persistent-keepalive-interval", peer->persistent_keepalive_interval);
+ if (peer->endpoint.addr.sa_family == AF_INET || peer->endpoint.addr.sa_family == AF_INET6)
+ nvlist_add_binary(nvl_peers[i], "endpoint", &peer->endpoint.addr, peer->endpoint.addr.sa_len);
+ if (peer->flags & WGPEER_REPLACE_ALLOWEDIPS)
+ nvlist_add_bool(nvl_peers[i], "replace-allowedips", true);
+ if (peer->flags & WGPEER_REMOVE_ME)
+ nvlist_add_bool(nvl_peers[i], "remove", true);
+ for_each_wgallowedip(peer, aip) {
+ nvl_aips[j] = nvlist_create(0);
+ if (!nvl_aips[j])
+ goto err_peer;
+ nvlist_add_number(nvl_aips[j], "cidr", aip->cidr);
+ if (aip->family == AF_INET)
+ nvlist_add_binary(nvl_aips[j], "ipv4", &aip->ip4, sizeof(aip->ip4));
+ else if (aip->family == AF_INET6)
+ nvlist_add_binary(nvl_aips[j], "ipv6", &aip->ip6, sizeof(aip->ip6));
+ ++j;
+ }
+ if (j) {
+ nvlist_add_nvlist_array(nvl_peers[i], "allowed-ips", (const nvlist_t *const *)nvl_aips, j);
+ for (j = 0; j < aip_count; ++j)
+ nvlist_destroy(nvl_aips[j]);
+ free(nvl_aips);
+ }
+ ++i;
+ continue;
+
+ err_peer:
+ ret = -errno;
+ for (j = 0; j < aip_count && nvl_aips; ++j)
+ nvlist_destroy(nvl_aips[j]);
+ free(nvl_aips);
+ nvlist_destroy(nvl_peers[i]);
+ goto err;
+ }
+ if (i) {
+ nvlist_add_nvlist_array(nvl_device, "peers", (const nvlist_t *const *)nvl_peers, i);
+ for (i = 0; i < peer_count; ++i)
+ nvlist_destroy(nvl_peers[i]);
+ free(nvl_peers);
+ }
+ wgd.wgd_data = nvlist_pack(nvl_device, &wgd.wgd_size);
+ nvlist_destroy(nvl_device);
+ if (!wgd.wgd_data)
+ goto err;
+ s = get_dgram_socket();
+ if (s < 0)
+ return -errno;
+ return ioctl(s, SIOCSWG, &wgd);
+
+err:
+ if (!ret)
+ ret = -errno;
+ for (i = 0; i < peer_count && nvl_peers; ++i)
+ nvlist_destroy(nvl_peers[i]);
+ free(nvl_peers);
+ nvlist_destroy(nvl_device);
+ return ret;
+}
diff --git a/contrib/wireguard-tools/ipc-linux.h b/contrib/wireguard-tools/ipc-linux.h
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/ipc-linux.h
@@ -0,0 +1,519 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <time.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <linux/genetlink.h>
+#include <linux/if_link.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <linux/wireguard.h>
+#include <netinet/in.h>
+#include "containers.h"
+#include "encoding.h"
+#include "netlink.h"
+
+#define IPC_SUPPORTS_KERNEL_INTERFACE
+
+#define SOCKET_BUFFER_SIZE (mnl_ideal_socket_buffer_size())
+
+struct interface {
+ const char *name;
+ bool is_wireguard;
+};
+
+static int parse_linkinfo(const struct nlattr *attr, void *data)
+{
+ struct interface *interface = data;
+
+ if (mnl_attr_get_type(attr) == IFLA_INFO_KIND && !strcmp(WG_GENL_NAME, mnl_attr_get_str(attr)))
+ interface->is_wireguard = true;
+ return MNL_CB_OK;
+}
+
+static int parse_infomsg(const struct nlattr *attr, void *data)
+{
+ struct interface *interface = data;
+
+ if (mnl_attr_get_type(attr) == IFLA_LINKINFO)
+ return mnl_attr_parse_nested(attr, parse_linkinfo, data);
+ else if (mnl_attr_get_type(attr) == IFLA_IFNAME)
+ interface->name = mnl_attr_get_str(attr);
+ return MNL_CB_OK;
+}
+
+static int read_devices_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct string_list *list = data;
+ struct interface interface = { 0 };
+ int ret;
+
+ ret = mnl_attr_parse(nlh, sizeof(struct ifinfomsg), parse_infomsg, &interface);
+ if (ret != MNL_CB_OK)
+ return ret;
+ if (interface.name && interface.is_wireguard)
+ ret = string_list_add(list, interface.name);
+ if (ret < 0)
+ return ret;
+ if (nlh->nlmsg_type != NLMSG_DONE)
+ return MNL_CB_OK + 1;
+ return MNL_CB_OK;
+}
+
+static int kernel_get_wireguard_interfaces(struct string_list *list)
+{
+ struct mnl_socket *nl = NULL;
+ char *rtnl_buffer = NULL;
+ size_t message_len;
+ unsigned int portid, seq;
+ ssize_t len;
+ int ret = 0;
+ struct nlmsghdr *nlh;
+ struct ifinfomsg *ifm;
+
+ ret = -ENOMEM;
+ rtnl_buffer = calloc(SOCKET_BUFFER_SIZE, 1);
+ if (!rtnl_buffer)
+ goto cleanup;
+
+ nl = mnl_socket_open(NETLINK_ROUTE);
+ if (!nl) {
+ ret = -errno;
+ goto cleanup;
+ }
+
+ if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
+ ret = -errno;
+ goto cleanup;
+ }
+
+ seq = time(NULL);
+ portid = mnl_socket_get_portid(nl);
+ nlh = mnl_nlmsg_put_header(rtnl_buffer);
+ nlh->nlmsg_type = RTM_GETLINK;
+ nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP;
+ nlh->nlmsg_seq = seq;
+ ifm = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifm));
+ ifm->ifi_family = AF_UNSPEC;
+ message_len = nlh->nlmsg_len;
+
+ if (mnl_socket_sendto(nl, rtnl_buffer, message_len) < 0) {
+ ret = -errno;
+ goto cleanup;
+ }
+
+another:
+ if ((len = mnl_socket_recvfrom(nl, rtnl_buffer, SOCKET_BUFFER_SIZE)) < 0) {
+ ret = -errno;
+ goto cleanup;
+ }
+ if ((len = mnl_cb_run(rtnl_buffer, len, seq, portid, read_devices_cb, list)) < 0) {
+ /* Netlink returns NLM_F_DUMP_INTR if the set of all tunnels changed
+ * during the dump. That's unfortunate, but is pretty common on busy
+ * systems that are adding and removing tunnels all the time. Rather
+ * than retrying, potentially indefinitely, we just work with the
+ * partial results. */
+ if (errno != EINTR) {
+ ret = -errno;
+ goto cleanup;
+ }
+ }
+ if (len == MNL_CB_OK + 1)
+ goto another;
+ ret = 0;
+
+cleanup:
+ free(rtnl_buffer);
+ if (nl)
+ mnl_socket_close(nl);
+ return ret;
+}
+
+static int kernel_set_device(struct wgdevice *dev)
+{
+ int ret = 0;
+ struct wgpeer *peer = NULL;
+ struct wgallowedip *allowedip = NULL;
+ struct nlattr *peers_nest, *peer_nest, *allowedips_nest, *allowedip_nest;
+ struct nlmsghdr *nlh;
+ struct mnlg_socket *nlg;
+
+ nlg = mnlg_socket_open(WG_GENL_NAME, WG_GENL_VERSION);
+ if (!nlg)
+ return -errno;
+
+again:
+ nlh = mnlg_msg_prepare(nlg, WG_CMD_SET_DEVICE, NLM_F_REQUEST | NLM_F_ACK);
+ mnl_attr_put_strz(nlh, WGDEVICE_A_IFNAME, dev->name);
+
+ if (!peer) {
+ uint32_t flags = 0;
+
+ if (dev->flags & WGDEVICE_HAS_PRIVATE_KEY)
+ mnl_attr_put(nlh, WGDEVICE_A_PRIVATE_KEY, sizeof(dev->private_key), dev->private_key);
+ if (dev->flags & WGDEVICE_HAS_LISTEN_PORT)
+ mnl_attr_put_u16(nlh, WGDEVICE_A_LISTEN_PORT, dev->listen_port);
+ if (dev->flags & WGDEVICE_HAS_FWMARK)
+ mnl_attr_put_u32(nlh, WGDEVICE_A_FWMARK, dev->fwmark);
+ if (dev->flags & WGDEVICE_REPLACE_PEERS)
+ flags |= WGDEVICE_F_REPLACE_PEERS;
+ if (flags)
+ mnl_attr_put_u32(nlh, WGDEVICE_A_FLAGS, flags);
+ }
+ if (!dev->first_peer)
+ goto send;
+ peers_nest = peer_nest = allowedips_nest = allowedip_nest = NULL;
+ peers_nest = mnl_attr_nest_start(nlh, WGDEVICE_A_PEERS);
+ for (peer = peer ? peer : dev->first_peer; peer; peer = peer->next_peer) {
+ uint32_t flags = 0;
+
+ peer_nest = mnl_attr_nest_start_check(nlh, SOCKET_BUFFER_SIZE, 0);
+ if (!peer_nest)
+ goto toobig_peers;
+ if (!mnl_attr_put_check(nlh, SOCKET_BUFFER_SIZE, WGPEER_A_PUBLIC_KEY, sizeof(peer->public_key), peer->public_key))
+ goto toobig_peers;
+ if (peer->flags & WGPEER_REMOVE_ME)
+ flags |= WGPEER_F_REMOVE_ME;
+ if (!allowedip) {
+ if (peer->flags & WGPEER_REPLACE_ALLOWEDIPS)
+ flags |= WGPEER_F_REPLACE_ALLOWEDIPS;
+ if (peer->flags & WGPEER_HAS_PRESHARED_KEY) {
+ if (!mnl_attr_put_check(nlh, SOCKET_BUFFER_SIZE, WGPEER_A_PRESHARED_KEY, sizeof(peer->preshared_key), peer->preshared_key))
+ goto toobig_peers;
+ }
+ if (peer->endpoint.addr.sa_family == AF_INET) {
+ if (!mnl_attr_put_check(nlh, SOCKET_BUFFER_SIZE, WGPEER_A_ENDPOINT, sizeof(peer->endpoint.addr4), &peer->endpoint.addr4))
+ goto toobig_peers;
+ } else if (peer->endpoint.addr.sa_family == AF_INET6) {
+ if (!mnl_attr_put_check(nlh, SOCKET_BUFFER_SIZE, WGPEER_A_ENDPOINT, sizeof(peer->endpoint.addr6), &peer->endpoint.addr6))
+ goto toobig_peers;
+ }
+ if (peer->flags & WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL) {
+ if (!mnl_attr_put_u16_check(nlh, SOCKET_BUFFER_SIZE, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, peer->persistent_keepalive_interval))
+ goto toobig_peers;
+ }
+ }
+ if (flags) {
+ if (!mnl_attr_put_u32_check(nlh, SOCKET_BUFFER_SIZE, WGPEER_A_FLAGS, flags))
+ goto toobig_peers;
+ }
+ if (peer->first_allowedip) {
+ if (!allowedip)
+ allowedip = peer->first_allowedip;
+ allowedips_nest = mnl_attr_nest_start_check(nlh, SOCKET_BUFFER_SIZE, WGPEER_A_ALLOWEDIPS);
+ if (!allowedips_nest)
+ goto toobig_allowedips;
+ for (; allowedip; allowedip = allowedip->next_allowedip) {
+ allowedip_nest = mnl_attr_nest_start_check(nlh, SOCKET_BUFFER_SIZE, 0);
+ if (!allowedip_nest)
+ goto toobig_allowedips;
+ if (!mnl_attr_put_u16_check(nlh, SOCKET_BUFFER_SIZE, WGALLOWEDIP_A_FAMILY, allowedip->family))
+ goto toobig_allowedips;
+ if (allowedip->family == AF_INET) {
+ if (!mnl_attr_put_check(nlh, SOCKET_BUFFER_SIZE, WGALLOWEDIP_A_IPADDR, sizeof(allowedip->ip4), &allowedip->ip4))
+ goto toobig_allowedips;
+ } else if (allowedip->family == AF_INET6) {
+ if (!mnl_attr_put_check(nlh, SOCKET_BUFFER_SIZE, WGALLOWEDIP_A_IPADDR, sizeof(allowedip->ip6), &allowedip->ip6))
+ goto toobig_allowedips;
+ }
+ if (!mnl_attr_put_u8_check(nlh, SOCKET_BUFFER_SIZE, WGALLOWEDIP_A_CIDR_MASK, allowedip->cidr))
+ goto toobig_allowedips;
+ mnl_attr_nest_end(nlh, allowedip_nest);
+ allowedip_nest = NULL;
+ }
+ mnl_attr_nest_end(nlh, allowedips_nest);
+ allowedips_nest = NULL;
+ }
+
+ mnl_attr_nest_end(nlh, peer_nest);
+ peer_nest = NULL;
+ }
+ mnl_attr_nest_end(nlh, peers_nest);
+ peers_nest = NULL;
+ goto send;
+toobig_allowedips:
+ if (allowedip_nest)
+ mnl_attr_nest_cancel(nlh, allowedip_nest);
+ if (allowedips_nest)
+ mnl_attr_nest_end(nlh, allowedips_nest);
+ mnl_attr_nest_end(nlh, peer_nest);
+ mnl_attr_nest_end(nlh, peers_nest);
+ goto send;
+toobig_peers:
+ if (peer_nest)
+ mnl_attr_nest_cancel(nlh, peer_nest);
+ mnl_attr_nest_end(nlh, peers_nest);
+ goto send;
+send:
+ if (mnlg_socket_send(nlg, nlh) < 0) {
+ ret = -errno;
+ goto out;
+ }
+ errno = 0;
+ if (mnlg_socket_recv_run(nlg, NULL, NULL) < 0) {
+ ret = errno ? -errno : -EINVAL;
+ goto out;
+ }
+ if (peer)
+ goto again;
+
+out:
+ mnlg_socket_close(nlg);
+ errno = -ret;
+ return ret;
+}
+
+static int parse_allowedip(const struct nlattr *attr, void *data)
+{
+ struct wgallowedip *allowedip = data;
+
+ switch (mnl_attr_get_type(attr)) {
+ case WGALLOWEDIP_A_UNSPEC:
+ break;
+ case WGALLOWEDIP_A_FAMILY:
+ if (!mnl_attr_validate(attr, MNL_TYPE_U16))
+ allowedip->family = mnl_attr_get_u16(attr);
+ break;
+ case WGALLOWEDIP_A_IPADDR:
+ if (mnl_attr_get_payload_len(attr) == sizeof(allowedip->ip4))
+ memcpy(&allowedip->ip4, mnl_attr_get_payload(attr), sizeof(allowedip->ip4));
+ else if (mnl_attr_get_payload_len(attr) == sizeof(allowedip->ip6))
+ memcpy(&allowedip->ip6, mnl_attr_get_payload(attr), sizeof(allowedip->ip6));
+ break;
+ case WGALLOWEDIP_A_CIDR_MASK:
+ if (!mnl_attr_validate(attr, MNL_TYPE_U8))
+ allowedip->cidr = mnl_attr_get_u8(attr);
+ break;
+ }
+
+ return MNL_CB_OK;
+}
+
+static int parse_allowedips(const struct nlattr *attr, void *data)
+{
+ struct wgpeer *peer = data;
+ struct wgallowedip *new_allowedip = calloc(1, sizeof(*new_allowedip));
+ int ret;
+
+ if (!new_allowedip) {
+ perror("calloc");
+ return MNL_CB_ERROR;
+ }
+ if (!peer->first_allowedip)
+ peer->first_allowedip = peer->last_allowedip = new_allowedip;
+ else {
+ peer->last_allowedip->next_allowedip = new_allowedip;
+ peer->last_allowedip = new_allowedip;
+ }
+ ret = mnl_attr_parse_nested(attr, parse_allowedip, new_allowedip);
+ if (!ret)
+ return ret;
+ if (!((new_allowedip->family == AF_INET && new_allowedip->cidr <= 32) || (new_allowedip->family == AF_INET6 && new_allowedip->cidr <= 128)))
+ return MNL_CB_ERROR;
+ return MNL_CB_OK;
+}
+
+static int parse_peer(const struct nlattr *attr, void *data)
+{
+ struct wgpeer *peer = data;
+
+ switch (mnl_attr_get_type(attr)) {
+ case WGPEER_A_UNSPEC:
+ break;
+ case WGPEER_A_PUBLIC_KEY:
+ if (mnl_attr_get_payload_len(attr) == sizeof(peer->public_key)) {
+ memcpy(peer->public_key, mnl_attr_get_payload(attr), sizeof(peer->public_key));
+ peer->flags |= WGPEER_HAS_PUBLIC_KEY;
+ }
+ break;
+ case WGPEER_A_PRESHARED_KEY:
+ if (mnl_attr_get_payload_len(attr) == sizeof(peer->preshared_key)) {
+ memcpy(peer->preshared_key, mnl_attr_get_payload(attr), sizeof(peer->preshared_key));
+ if (!key_is_zero(peer->preshared_key))
+ peer->flags |= WGPEER_HAS_PRESHARED_KEY;
+ }
+ break;
+ case WGPEER_A_ENDPOINT: {
+ struct sockaddr *addr;
+
+ if (mnl_attr_get_payload_len(attr) < sizeof(*addr))
+ break;
+ addr = mnl_attr_get_payload(attr);
+ if (addr->sa_family == AF_INET && mnl_attr_get_payload_len(attr) == sizeof(peer->endpoint.addr4))
+ memcpy(&peer->endpoint.addr4, addr, sizeof(peer->endpoint.addr4));
+ else if (addr->sa_family == AF_INET6 && mnl_attr_get_payload_len(attr) == sizeof(peer->endpoint.addr6))
+ memcpy(&peer->endpoint.addr6, addr, sizeof(peer->endpoint.addr6));
+ break;
+ }
+ case WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL:
+ if (!mnl_attr_validate(attr, MNL_TYPE_U16))
+ peer->persistent_keepalive_interval = mnl_attr_get_u16(attr);
+ break;
+ case WGPEER_A_LAST_HANDSHAKE_TIME:
+ if (mnl_attr_get_payload_len(attr) == sizeof(peer->last_handshake_time))
+ memcpy(&peer->last_handshake_time, mnl_attr_get_payload(attr), sizeof(peer->last_handshake_time));
+ break;
+ case WGPEER_A_RX_BYTES:
+ if (!mnl_attr_validate(attr, MNL_TYPE_U64))
+ peer->rx_bytes = mnl_attr_get_u64(attr);
+ break;
+ case WGPEER_A_TX_BYTES:
+ if (!mnl_attr_validate(attr, MNL_TYPE_U64))
+ peer->tx_bytes = mnl_attr_get_u64(attr);
+ break;
+ case WGPEER_A_ALLOWEDIPS:
+ return mnl_attr_parse_nested(attr, parse_allowedips, peer);
+ }
+
+ return MNL_CB_OK;
+}
+
+static int parse_peers(const struct nlattr *attr, void *data)
+{
+ struct wgdevice *device = data;
+ struct wgpeer *new_peer = calloc(1, sizeof(*new_peer));
+ int ret;
+
+ if (!new_peer) {
+ perror("calloc");
+ return MNL_CB_ERROR;
+ }
+ if (!device->first_peer)
+ device->first_peer = device->last_peer = new_peer;
+ else {
+ device->last_peer->next_peer = new_peer;
+ device->last_peer = new_peer;
+ }
+ ret = mnl_attr_parse_nested(attr, parse_peer, new_peer);
+ if (!ret)
+ return ret;
+ if (!(new_peer->flags & WGPEER_HAS_PUBLIC_KEY))
+ return MNL_CB_ERROR;
+ return MNL_CB_OK;
+}
+
+static int parse_device(const struct nlattr *attr, void *data)
+{
+ struct wgdevice *device = data;
+
+ switch (mnl_attr_get_type(attr)) {
+ case WGDEVICE_A_UNSPEC:
+ break;
+ case WGDEVICE_A_IFINDEX:
+ if (!mnl_attr_validate(attr, MNL_TYPE_U32))
+ device->ifindex = mnl_attr_get_u32(attr);
+ break;
+ case WGDEVICE_A_IFNAME:
+ if (!mnl_attr_validate(attr, MNL_TYPE_STRING)) {
+ strncpy(device->name, mnl_attr_get_str(attr), sizeof(device->name) - 1);
+ device->name[sizeof(device->name) - 1] = '\0';
+ }
+ break;
+ case WGDEVICE_A_PRIVATE_KEY:
+ if (mnl_attr_get_payload_len(attr) == sizeof(device->private_key)) {
+ memcpy(device->private_key, mnl_attr_get_payload(attr), sizeof(device->private_key));
+ device->flags |= WGDEVICE_HAS_PRIVATE_KEY;
+ }
+ break;
+ case WGDEVICE_A_PUBLIC_KEY:
+ if (mnl_attr_get_payload_len(attr) == sizeof(device->public_key)) {
+ memcpy(device->public_key, mnl_attr_get_payload(attr), sizeof(device->public_key));
+ device->flags |= WGDEVICE_HAS_PUBLIC_KEY;
+ }
+ break;
+ case WGDEVICE_A_LISTEN_PORT:
+ if (!mnl_attr_validate(attr, MNL_TYPE_U16))
+ device->listen_port = mnl_attr_get_u16(attr);
+ break;
+ case WGDEVICE_A_FWMARK:
+ if (!mnl_attr_validate(attr, MNL_TYPE_U32))
+ device->fwmark = mnl_attr_get_u32(attr);
+ break;
+ case WGDEVICE_A_PEERS:
+ return mnl_attr_parse_nested(attr, parse_peers, device);
+ }
+
+ return MNL_CB_OK;
+}
+
+static int read_device_cb(const struct nlmsghdr *nlh, void *data)
+{
+ return mnl_attr_parse(nlh, sizeof(struct genlmsghdr), parse_device, data);
+}
+
+static void coalesce_peers(struct wgdevice *device)
+{
+ struct wgpeer *old_next_peer, *peer = device->first_peer;
+
+ while (peer && peer->next_peer) {
+ if (memcmp(peer->public_key, peer->next_peer->public_key, sizeof(peer->public_key))) {
+ peer = peer->next_peer;
+ continue;
+ }
+ if (!peer->first_allowedip) {
+ peer->first_allowedip = peer->next_peer->first_allowedip;
+ peer->last_allowedip = peer->next_peer->last_allowedip;
+ } else {
+ peer->last_allowedip->next_allowedip = peer->next_peer->first_allowedip;
+ peer->last_allowedip = peer->next_peer->last_allowedip;
+ }
+ old_next_peer = peer->next_peer;
+ peer->next_peer = old_next_peer->next_peer;
+ free(old_next_peer);
+ }
+}
+
+static int kernel_get_device(struct wgdevice **device, const char *iface)
+{
+ int ret;
+ struct nlmsghdr *nlh;
+ struct mnlg_socket *nlg;
+
+try_again:
+ ret = 0;
+ *device = calloc(1, sizeof(**device));
+ if (!*device)
+ return -errno;
+
+ nlg = mnlg_socket_open(WG_GENL_NAME, WG_GENL_VERSION);
+ if (!nlg) {
+ free_wgdevice(*device);
+ *device = NULL;
+ return -errno;
+ }
+
+ nlh = mnlg_msg_prepare(nlg, WG_CMD_GET_DEVICE, NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP);
+ mnl_attr_put_strz(nlh, WGDEVICE_A_IFNAME, iface);
+ if (mnlg_socket_send(nlg, nlh) < 0) {
+ ret = -errno;
+ goto out;
+ }
+ errno = 0;
+ if (mnlg_socket_recv_run(nlg, read_device_cb, *device) < 0) {
+ ret = errno ? -errno : -EINVAL;
+ goto out;
+ }
+ coalesce_peers(*device);
+
+out:
+ if (nlg)
+ mnlg_socket_close(nlg);
+ if (ret) {
+ free_wgdevice(*device);
+ if (ret == -EINTR)
+ goto try_again;
+ *device = NULL;
+ }
+ errno = -ret;
+ return ret;
+}
diff --git a/contrib/wireguard-tools/ipc-openbsd.h b/contrib/wireguard-tools/ipc-openbsd.h
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/ipc-openbsd.h
@@ -0,0 +1,281 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#include <errno.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/types.h>
+#include <net/if.h>
+#include <net/if_wg.h>
+#include <netinet/in.h>
+#include "containers.h"
+
+#define IPC_SUPPORTS_KERNEL_INTERFACE
+
+static int get_dgram_socket(void)
+{
+ static int sock = -1;
+ if (sock < 0)
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+ return sock;
+}
+
+static int kernel_get_wireguard_interfaces(struct string_list *list)
+{
+ struct ifgroupreq ifgr = { .ifgr_name = "wg" };
+ struct ifg_req *ifg;
+ int s = get_dgram_socket(), ret = 0;
+
+ if (s < 0)
+ return -errno;
+
+ if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) < 0)
+ return errno == ENOENT ? 0 : -errno;
+
+ ifgr.ifgr_groups = calloc(1, ifgr.ifgr_len);
+ if (!ifgr.ifgr_groups)
+ return -errno;
+ if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) < 0) {
+ ret = -errno;
+ goto out;
+ }
+
+ for (ifg = ifgr.ifgr_groups; ifg && ifgr.ifgr_len > 0; ++ifg) {
+ if ((ret = string_list_add(list, ifg->ifgrq_member)) < 0)
+ goto out;
+ ifgr.ifgr_len -= sizeof(struct ifg_req);
+ }
+
+out:
+ free(ifgr.ifgr_groups);
+ return ret;
+}
+
+static int kernel_get_device(struct wgdevice **device, const char *iface)
+{
+ struct wg_data_io wgdata = { .wgd_size = 0 };
+ struct wg_interface_io *wg_iface;
+ struct wg_peer_io *wg_peer;
+ struct wg_aip_io *wg_aip;
+ struct wgdevice *dev;
+ struct wgpeer *peer;
+ struct wgallowedip *aip;
+ int s = get_dgram_socket(), ret;
+
+ if (s < 0)
+ return -errno;
+
+ *device = NULL;
+ strlcpy(wgdata.wgd_name, iface, sizeof(wgdata.wgd_name));
+ for (size_t last_size = wgdata.wgd_size;; last_size = wgdata.wgd_size) {
+ if (ioctl(s, SIOCGWG, (caddr_t)&wgdata) < 0)
+ goto out;
+ if (last_size >= wgdata.wgd_size)
+ break;
+ wgdata.wgd_interface = realloc(wgdata.wgd_interface, wgdata.wgd_size);
+ if (!wgdata.wgd_interface)
+ goto out;
+ }
+
+ wg_iface = wgdata.wgd_interface;
+ dev = calloc(1, sizeof(*dev));
+ if (!dev)
+ goto out;
+ strlcpy(dev->name, iface, sizeof(dev->name));
+
+ if (wg_iface->i_flags & WG_INTERFACE_HAS_RTABLE) {
+ dev->fwmark = wg_iface->i_rtable;
+ dev->flags |= WGDEVICE_HAS_FWMARK;
+ }
+
+ if (wg_iface->i_flags & WG_INTERFACE_HAS_PORT) {
+ dev->listen_port = wg_iface->i_port;
+ dev->flags |= WGDEVICE_HAS_LISTEN_PORT;
+ }
+
+ if (wg_iface->i_flags & WG_INTERFACE_HAS_PUBLIC) {
+ memcpy(dev->public_key, wg_iface->i_public, sizeof(dev->public_key));
+ dev->flags |= WGDEVICE_HAS_PUBLIC_KEY;
+ }
+
+ if (wg_iface->i_flags & WG_INTERFACE_HAS_PRIVATE) {
+ memcpy(dev->private_key, wg_iface->i_private, sizeof(dev->private_key));
+ dev->flags |= WGDEVICE_HAS_PRIVATE_KEY;
+ }
+
+ wg_peer = &wg_iface->i_peers[0];
+ for (size_t i = 0; i < wg_iface->i_peers_count; ++i) {
+ peer = calloc(1, sizeof(*peer));
+ if (!peer)
+ goto out;
+
+ if (dev->first_peer == NULL)
+ dev->first_peer = peer;
+ else
+ dev->last_peer->next_peer = peer;
+ dev->last_peer = peer;
+
+ if (wg_peer->p_flags & WG_PEER_HAS_PUBLIC) {
+ memcpy(peer->public_key, wg_peer->p_public, sizeof(peer->public_key));
+ peer->flags |= WGPEER_HAS_PUBLIC_KEY;
+ }
+
+ if (wg_peer->p_flags & WG_PEER_HAS_PSK) {
+ memcpy(peer->preshared_key, wg_peer->p_psk, sizeof(peer->preshared_key));
+ if (!key_is_zero(peer->preshared_key))
+ peer->flags |= WGPEER_HAS_PRESHARED_KEY;
+ }
+
+ if (wg_peer->p_flags & WG_PEER_HAS_PKA) {
+ peer->persistent_keepalive_interval = wg_peer->p_pka;
+ peer->flags |= WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL;
+ }
+
+ if (wg_peer->p_flags & WG_PEER_HAS_ENDPOINT && wg_peer->p_sa.sa_len <= sizeof(peer->endpoint.addr))
+ memcpy(&peer->endpoint.addr, &wg_peer->p_sa, wg_peer->p_sa.sa_len);
+
+ peer->rx_bytes = wg_peer->p_rxbytes;
+ peer->tx_bytes = wg_peer->p_txbytes;
+
+ peer->last_handshake_time.tv_sec = wg_peer->p_last_handshake.tv_sec;
+ peer->last_handshake_time.tv_nsec = wg_peer->p_last_handshake.tv_nsec;
+
+ wg_aip = &wg_peer->p_aips[0];
+ for (size_t j = 0; j < wg_peer->p_aips_count; ++j) {
+ aip = calloc(1, sizeof(*aip));
+ if (!aip)
+ goto out;
+
+ if (peer->first_allowedip == NULL)
+ peer->first_allowedip = aip;
+ else
+ peer->last_allowedip->next_allowedip = aip;
+ peer->last_allowedip = aip;
+
+ aip->family = wg_aip->a_af;
+ if (wg_aip->a_af == AF_INET) {
+ memcpy(&aip->ip4, &wg_aip->a_ipv4, sizeof(aip->ip4));
+ aip->cidr = wg_aip->a_cidr;
+ } else if (wg_aip->a_af == AF_INET6) {
+ memcpy(&aip->ip6, &wg_aip->a_ipv6, sizeof(aip->ip6));
+ aip->cidr = wg_aip->a_cidr;
+ }
+ ++wg_aip;
+ }
+ wg_peer = (struct wg_peer_io *)wg_aip;
+ }
+ *device = dev;
+ errno = 0;
+out:
+ ret = -errno;
+ free(wgdata.wgd_interface);
+ return ret;
+}
+
+static int kernel_set_device(struct wgdevice *dev)
+{
+ struct wg_data_io wgdata = { .wgd_size = sizeof(struct wg_interface_io) };
+ struct wg_interface_io *wg_iface;
+ struct wg_peer_io *wg_peer;
+ struct wg_aip_io *wg_aip;
+ struct wgpeer *peer;
+ struct wgallowedip *aip;
+ int s = get_dgram_socket(), ret;
+ size_t peer_count, aip_count;
+
+ if (s < 0)
+ return -errno;
+
+ for_each_wgpeer(dev, peer) {
+ wgdata.wgd_size += sizeof(struct wg_peer_io);
+ for_each_wgallowedip(peer, aip)
+ wgdata.wgd_size += sizeof(struct wg_aip_io);
+ }
+ wg_iface = wgdata.wgd_interface = calloc(1, wgdata.wgd_size);
+ if (!wgdata.wgd_interface)
+ return -errno;
+ strlcpy(wgdata.wgd_name, dev->name, sizeof(wgdata.wgd_name));
+
+ if (dev->flags & WGDEVICE_HAS_PRIVATE_KEY) {
+ memcpy(wg_iface->i_private, dev->private_key, sizeof(wg_iface->i_private));
+ wg_iface->i_flags |= WG_INTERFACE_HAS_PRIVATE;
+ }
+
+ if (dev->flags & WGDEVICE_HAS_LISTEN_PORT) {
+ wg_iface->i_port = dev->listen_port;
+ wg_iface->i_flags |= WG_INTERFACE_HAS_PORT;
+ }
+
+ if (dev->flags & WGDEVICE_HAS_FWMARK) {
+ wg_iface->i_rtable = dev->fwmark;
+ wg_iface->i_flags |= WG_INTERFACE_HAS_RTABLE;
+ }
+
+ if (dev->flags & WGDEVICE_REPLACE_PEERS)
+ wg_iface->i_flags |= WG_INTERFACE_REPLACE_PEERS;
+
+ peer_count = 0;
+ wg_peer = &wg_iface->i_peers[0];
+ for_each_wgpeer(dev, peer) {
+ wg_peer->p_flags = WG_PEER_HAS_PUBLIC;
+ memcpy(wg_peer->p_public, peer->public_key, sizeof(wg_peer->p_public));
+
+ if (peer->flags & WGPEER_HAS_PRESHARED_KEY) {
+ memcpy(wg_peer->p_psk, peer->preshared_key, sizeof(wg_peer->p_psk));
+ wg_peer->p_flags |= WG_PEER_HAS_PSK;
+ }
+
+ if (peer->flags & WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL) {
+ wg_peer->p_pka = peer->persistent_keepalive_interval;
+ wg_peer->p_flags |= WG_PEER_HAS_PKA;
+ }
+
+ if ((peer->endpoint.addr.sa_family == AF_INET || peer->endpoint.addr.sa_family == AF_INET6) &&
+ peer->endpoint.addr.sa_len <= sizeof(wg_peer->p_endpoint)) {
+ memcpy(&wg_peer->p_endpoint, &peer->endpoint.addr, peer->endpoint.addr.sa_len);
+ wg_peer->p_flags |= WG_PEER_HAS_ENDPOINT;
+ }
+
+ if (peer->flags & WGPEER_REPLACE_ALLOWEDIPS)
+ wg_peer->p_flags |= WG_PEER_REPLACE_AIPS;
+
+ if (peer->flags & WGPEER_REMOVE_ME)
+ wg_peer->p_flags |= WG_PEER_REMOVE;
+
+ aip_count = 0;
+ wg_aip = &wg_peer->p_aips[0];
+ for_each_wgallowedip(peer, aip) {
+ wg_aip->a_af = aip->family;
+ wg_aip->a_cidr = aip->cidr;
+
+ if (aip->family == AF_INET)
+ memcpy(&wg_aip->a_ipv4, &aip->ip4, sizeof(wg_aip->a_ipv4));
+ else if (aip->family == AF_INET6)
+ memcpy(&wg_aip->a_ipv6, &aip->ip6, sizeof(wg_aip->a_ipv6));
+ else
+ continue;
+ ++aip_count;
+ ++wg_aip;
+ }
+ wg_peer->p_aips_count = aip_count;
+ ++peer_count;
+ wg_peer = (struct wg_peer_io *)wg_aip;
+ }
+ wg_iface->i_peers_count = peer_count;
+
+ if (ioctl(s, SIOCSWG, (caddr_t)&wgdata) < 0)
+ goto out;
+ errno = 0;
+
+out:
+ ret = -errno;
+ free(wgdata.wgd_interface);
+ return ret;
+}
diff --git a/contrib/wireguard-tools/ipc-uapi-unix.h b/contrib/wireguard-tools/ipc-uapi-unix.h
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/ipc-uapi-unix.h
@@ -0,0 +1,119 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+
+#define SOCK_PATH RUNSTATEDIR "/wireguard/"
+#define SOCK_SUFFIX ".sock"
+
+static FILE *userspace_interface_file(const char *iface)
+{
+ struct stat sbuf;
+ struct sockaddr_un addr = { .sun_family = AF_UNIX };
+ int fd = -1, ret;
+ FILE *f = NULL;
+
+ errno = EINVAL;
+ if (strchr(iface, '/'))
+ goto out;
+ ret = snprintf(addr.sun_path, sizeof(addr.sun_path), SOCK_PATH "%s" SOCK_SUFFIX, iface);
+ if (ret < 0)
+ goto out;
+ ret = stat(addr.sun_path, &sbuf);
+ if (ret < 0)
+ goto out;
+ errno = EBADF;
+ if (!S_ISSOCK(sbuf.st_mode))
+ goto out;
+
+ ret = fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (ret < 0)
+ goto out;
+
+ ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
+ if (ret < 0) {
+ if (errno == ECONNREFUSED) /* If the process is gone, we try to clean up the socket. */
+ unlink(addr.sun_path);
+ goto out;
+ }
+ f = fdopen(fd, "r+");
+ if (f)
+ errno = 0;
+out:
+ ret = -errno;
+ if (ret) {
+ if (fd >= 0)
+ close(fd);
+ errno = -ret;
+ return NULL;
+ }
+ return f;
+}
+
+static bool userspace_has_wireguard_interface(const char *iface)
+{
+ struct stat sbuf;
+ struct sockaddr_un addr = { .sun_family = AF_UNIX };
+ int fd, ret;
+
+ if (strchr(iface, '/'))
+ return false;
+ if (snprintf(addr.sun_path, sizeof(addr.sun_path), SOCK_PATH "%s" SOCK_SUFFIX, iface) < 0)
+ return false;
+ if (stat(addr.sun_path, &sbuf) < 0)
+ return false;
+ if (!S_ISSOCK(sbuf.st_mode))
+ return false;
+ ret = fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (ret < 0)
+ return false;
+ ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
+ if (ret < 0 && errno == ECONNREFUSED) { /* If the process is gone, we try to clean up the socket. */
+ close(fd);
+ unlink(addr.sun_path);
+ return false;
+ }
+ close(fd);
+ return true;
+}
+
+static int userspace_get_wireguard_interfaces(struct string_list *list)
+{
+ DIR *dir;
+ struct dirent *ent;
+ size_t len;
+ char *end;
+ int ret = 0;
+
+ dir = opendir(SOCK_PATH);
+ if (!dir)
+ return errno == ENOENT ? 0 : -errno;
+ while ((ent = readdir(dir))) {
+ len = strlen(ent->d_name);
+ if (len <= strlen(SOCK_SUFFIX))
+ continue;
+ end = &ent->d_name[len - strlen(SOCK_SUFFIX)];
+ if (strncmp(end, SOCK_SUFFIX, strlen(SOCK_SUFFIX)))
+ continue;
+ *end = '\0';
+ if (!userspace_has_wireguard_interface(ent->d_name))
+ continue;
+ ret = string_list_add(list, ent->d_name);
+ if (ret < 0)
+ goto out;
+ }
+out:
+ closedir(dir);
+ return ret;
+}
diff --git a/contrib/wireguard-tools/ipc-uapi-windows.h b/contrib/wireguard-tools/ipc-uapi-windows.h
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/ipc-uapi-windows.h
@@ -0,0 +1,107 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#include <windows.h>
+#include <tlhelp32.h>
+#include <accctrl.h>
+#include <aclapi.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <fcntl.h>
+#include <hashtable.h>
+
+static FILE *userspace_interface_file(const char *iface)
+{
+ char fname[MAX_PATH];
+ HANDLE pipe_handle;
+ SID expected_sid;
+ DWORD bytes = sizeof(expected_sid);
+ PSID pipe_sid;
+ PSECURITY_DESCRIPTOR pipe_sd;
+ bool equal;
+ int fd;
+
+ if (!CreateWellKnownSid(WinLocalSystemSid, NULL, &expected_sid, &bytes))
+ goto err;
+
+ snprintf(fname, sizeof(fname), "\\\\.\\pipe\\ProtectedPrefix\\Administrators\\WireGuard\\%s", iface);
+ pipe_handle = CreateFileA(fname, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
+ if (pipe_handle == INVALID_HANDLE_VALUE)
+ goto err;
+ if (GetSecurityInfo(pipe_handle, SE_FILE_OBJECT, OWNER_SECURITY_INFORMATION, &pipe_sid, NULL, NULL, NULL, &pipe_sd) != ERROR_SUCCESS)
+ goto err_close;
+ equal = EqualSid(&expected_sid, pipe_sid);
+ LocalFree(pipe_sd);
+ if (!equal)
+ goto err_close;
+ fd = _open_osfhandle((intptr_t)pipe_handle, _O_RDWR);
+ if (fd == -1) {
+ CloseHandle(pipe_handle);
+ return NULL;
+ }
+ return _fdopen(fd, "r+");
+err_close:
+ CloseHandle(pipe_handle);
+err:
+ errno = EACCES;
+ return NULL;
+}
+
+static bool have_cached_interfaces;
+static struct hashtable cached_interfaces;
+
+static bool userspace_has_wireguard_interface(const char *iface)
+{
+ char fname[MAX_PATH];
+ WIN32_FIND_DATA find_data;
+ HANDLE find_handle;
+ bool ret = false;
+
+ if (have_cached_interfaces)
+ return hashtable_find_entry(&cached_interfaces, iface) != NULL;
+
+ snprintf(fname, sizeof(fname), "ProtectedPrefix\\Administrators\\WireGuard\\%s", iface);
+ find_handle = FindFirstFile("\\\\.\\pipe\\*", &find_data);
+ if (find_handle == INVALID_HANDLE_VALUE)
+ return -EIO;
+ do {
+ if (!strcmp(fname, find_data.cFileName)) {
+ ret = true;
+ break;
+ }
+ } while (FindNextFile(find_handle, &find_data));
+ FindClose(find_handle);
+ return ret;
+}
+
+static int userspace_get_wireguard_interfaces(struct string_list *list)
+{
+ static const char prefix[] = "ProtectedPrefix\\Administrators\\WireGuard\\";
+ WIN32_FIND_DATA find_data;
+ HANDLE find_handle;
+ char *iface;
+ int ret = 0;
+
+ find_handle = FindFirstFile("\\\\.\\pipe\\*", &find_data);
+ if (find_handle == INVALID_HANDLE_VALUE)
+ return -EIO;
+ do {
+ if (strncmp(prefix, find_data.cFileName, strlen(prefix)))
+ continue;
+ iface = find_data.cFileName + strlen(prefix);
+ ret = string_list_add(list, iface);
+ if (ret < 0)
+ goto out;
+ if (!hashtable_find_or_insert_entry(&cached_interfaces, iface)) {
+ ret = -errno;
+ goto out;
+ }
+ } while (FindNextFile(find_handle, &find_data));
+ have_cached_interfaces = true;
+
+out:
+ FindClose(find_handle);
+ return ret;
+}
diff --git a/contrib/wireguard-tools/ipc-uapi.h b/contrib/wireguard-tools/ipc-uapi.h
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/ipc-uapi.h
@@ -0,0 +1,297 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <limits.h>
+#include <net/if.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include "containers.h"
+#include "curve25519.h"
+#include "encoding.h"
+#include "ctype.h"
+
+#ifdef _WIN32
+#include "ipc-uapi-windows.h"
+#else
+#include "ipc-uapi-unix.h"
+#endif
+
+static int userspace_set_device(struct wgdevice *dev)
+{
+ char hex[WG_KEY_LEN_HEX], ip[INET6_ADDRSTRLEN], host[4096 + 1], service[512 + 1];
+ struct wgpeer *peer;
+ struct wgallowedip *allowedip;
+ FILE *f;
+ int ret, set_errno = -EPROTO;
+ socklen_t addr_len;
+ size_t line_buffer_len = 0, line_len;
+ char *key = NULL, *value;
+
+ f = userspace_interface_file(dev->name);
+ if (!f)
+ return -errno;
+ fprintf(f, "set=1\n");
+
+ if (dev->flags & WGDEVICE_HAS_PRIVATE_KEY) {
+ key_to_hex(hex, dev->private_key);
+ fprintf(f, "private_key=%s\n", hex);
+ }
+ if (dev->flags & WGDEVICE_HAS_LISTEN_PORT)
+ fprintf(f, "listen_port=%u\n", dev->listen_port);
+ if (dev->flags & WGDEVICE_HAS_FWMARK)
+ fprintf(f, "fwmark=%u\n", dev->fwmark);
+ if (dev->flags & WGDEVICE_REPLACE_PEERS)
+ fprintf(f, "replace_peers=true\n");
+
+ for_each_wgpeer(dev, peer) {
+ key_to_hex(hex, peer->public_key);
+ fprintf(f, "public_key=%s\n", hex);
+ if (peer->flags & WGPEER_REMOVE_ME) {
+ fprintf(f, "remove=true\n");
+ continue;
+ }
+ if (peer->flags & WGPEER_HAS_PRESHARED_KEY) {
+ key_to_hex(hex, peer->preshared_key);
+ fprintf(f, "preshared_key=%s\n", hex);
+ }
+ if (peer->endpoint.addr.sa_family == AF_INET || peer->endpoint.addr.sa_family == AF_INET6) {
+ addr_len = 0;
+ if (peer->endpoint.addr.sa_family == AF_INET)
+ addr_len = sizeof(struct sockaddr_in);
+ else if (peer->endpoint.addr.sa_family == AF_INET6)
+ addr_len = sizeof(struct sockaddr_in6);
+ if (!getnameinfo(&peer->endpoint.addr, addr_len, host, sizeof(host), service, sizeof(service), NI_DGRAM | NI_NUMERICSERV | NI_NUMERICHOST)) {
+ if (peer->endpoint.addr.sa_family == AF_INET6 && strchr(host, ':'))
+ fprintf(f, "endpoint=[%s]:%s\n", host, service);
+ else
+ fprintf(f, "endpoint=%s:%s\n", host, service);
+ }
+ }
+ if (peer->flags & WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL)
+ fprintf(f, "persistent_keepalive_interval=%u\n", peer->persistent_keepalive_interval);
+ if (peer->flags & WGPEER_REPLACE_ALLOWEDIPS)
+ fprintf(f, "replace_allowed_ips=true\n");
+ for_each_wgallowedip(peer, allowedip) {
+ if (allowedip->family == AF_INET) {
+ if (!inet_ntop(AF_INET, &allowedip->ip4, ip, INET6_ADDRSTRLEN))
+ continue;
+ } else if (allowedip->family == AF_INET6) {
+ if (!inet_ntop(AF_INET6, &allowedip->ip6, ip, INET6_ADDRSTRLEN))
+ continue;
+ } else
+ continue;
+ fprintf(f, "allowed_ip=%s/%d\n", ip, allowedip->cidr);
+ }
+ }
+ fprintf(f, "\n");
+ fflush(f);
+
+ while (getline(&key, &line_buffer_len, f) > 0) {
+ line_len = strlen(key);
+ ret = set_errno;
+ if (line_len == 1 && key[0] == '\n')
+ goto out;
+ value = strchr(key, '=');
+ if (!value || line_len == 0 || key[line_len - 1] != '\n')
+ break;
+ *value++ = key[--line_len] = '\0';
+
+ if (!strcmp(key, "errno")) {
+ long long num;
+ char *end;
+ if (value[0] != '-' && !char_is_digit(value[0]))
+ break;
+ num = strtoll(value, &end, 10);
+ if (*end || num > INT_MAX || num < INT_MIN)
+ break;
+ set_errno = num;
+ }
+ }
+ ret = errno ? -errno : -EPROTO;
+out:
+ free(key);
+ fclose(f);
+ errno = -ret;
+ return ret;
+}
+
+#define NUM(max) ({ \
+ unsigned long long num; \
+ char *end; \
+ if (!char_is_digit(value[0])) \
+ break; \
+ num = strtoull(value, &end, 10); \
+ if (*end || num > max) \
+ break; \
+ num; \
+})
+
+static int userspace_get_device(struct wgdevice **out, const char *iface)
+{
+ struct wgdevice *dev;
+ struct wgpeer *peer = NULL;
+ struct wgallowedip *allowedip = NULL;
+ size_t line_buffer_len = 0, line_len;
+ char *key = NULL, *value;
+ FILE *f;
+ int ret = -EPROTO;
+
+ *out = dev = calloc(1, sizeof(*dev));
+ if (!dev)
+ return -errno;
+
+ f = userspace_interface_file(iface);
+ if (!f) {
+ ret = -errno;
+ free(dev);
+ *out = NULL;
+ return ret;
+ }
+
+ fprintf(f, "get=1\n\n");
+ fflush(f);
+
+ strncpy(dev->name, iface, IFNAMSIZ - 1);
+ dev->name[IFNAMSIZ - 1] = '\0';
+
+ while (getline(&key, &line_buffer_len, f) > 0) {
+ line_len = strlen(key);
+ if (line_len == 1 && key[0] == '\n')
+ goto err;
+ value = strchr(key, '=');
+ if (!value || line_len == 0 || key[line_len - 1] != '\n')
+ break;
+ *value++ = key[--line_len] = '\0';
+
+ if (!peer && !strcmp(key, "private_key")) {
+ if (!key_from_hex(dev->private_key, value))
+ break;
+ curve25519_generate_public(dev->public_key, dev->private_key);
+ dev->flags |= WGDEVICE_HAS_PRIVATE_KEY | WGDEVICE_HAS_PUBLIC_KEY;
+ } else if (!peer && !strcmp(key, "listen_port")) {
+ dev->listen_port = NUM(0xffffU);
+ dev->flags |= WGDEVICE_HAS_LISTEN_PORT;
+ } else if (!peer && !strcmp(key, "fwmark")) {
+ dev->fwmark = NUM(0xffffffffU);
+ dev->flags |= WGDEVICE_HAS_FWMARK;
+ } else if (!strcmp(key, "public_key")) {
+ struct wgpeer *new_peer = calloc(1, sizeof(*new_peer));
+
+ if (!new_peer) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ allowedip = NULL;
+ if (peer)
+ peer->next_peer = new_peer;
+ else
+ dev->first_peer = new_peer;
+ peer = new_peer;
+ if (!key_from_hex(peer->public_key, value))
+ break;
+ peer->flags |= WGPEER_HAS_PUBLIC_KEY;
+ } else if (peer && !strcmp(key, "preshared_key")) {
+ if (!key_from_hex(peer->preshared_key, value))
+ break;
+ if (!key_is_zero(peer->preshared_key))
+ peer->flags |= WGPEER_HAS_PRESHARED_KEY;
+ } else if (peer && !strcmp(key, "endpoint")) {
+ char *begin, *end;
+ struct addrinfo *resolved;
+ struct addrinfo hints = {
+ .ai_family = AF_UNSPEC,
+ .ai_socktype = SOCK_DGRAM,
+ .ai_protocol = IPPROTO_UDP
+ };
+ if (!strlen(value))
+ break;
+ if (value[0] == '[') {
+ begin = &value[1];
+ end = strchr(value, ']');
+ if (!end)
+ break;
+ *end++ = '\0';
+ if (*end++ != ':' || !*end)
+ break;
+ } else {
+ begin = value;
+ end = strrchr(value, ':');
+ if (!end || !*(end + 1))
+ break;
+ *end++ = '\0';
+ }
+ if (getaddrinfo(begin, end, &hints, &resolved) != 0) {
+ ret = ENETUNREACH;
+ goto err;
+ }
+ if ((resolved->ai_family == AF_INET && resolved->ai_addrlen == sizeof(struct sockaddr_in)) ||
+ (resolved->ai_family == AF_INET6 && resolved->ai_addrlen == sizeof(struct sockaddr_in6)))
+ memcpy(&peer->endpoint.addr, resolved->ai_addr, resolved->ai_addrlen);
+ else {
+ freeaddrinfo(resolved);
+ break;
+ }
+ freeaddrinfo(resolved);
+ } else if (peer && !strcmp(key, "persistent_keepalive_interval")) {
+ peer->persistent_keepalive_interval = NUM(0xffffU);
+ peer->flags |= WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL;
+ } else if (peer && !strcmp(key, "allowed_ip")) {
+ struct wgallowedip *new_allowedip;
+ char *end, *mask = value, *ip = strsep(&mask, "/");
+
+ if (!mask || !char_is_digit(mask[0]))
+ break;
+ new_allowedip = calloc(1, sizeof(*new_allowedip));
+ if (!new_allowedip) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ if (allowedip)
+ allowedip->next_allowedip = new_allowedip;
+ else
+ peer->first_allowedip = new_allowedip;
+ allowedip = new_allowedip;
+ allowedip->family = AF_UNSPEC;
+ if (strchr(ip, ':')) {
+ if (inet_pton(AF_INET6, ip, &allowedip->ip6) == 1)
+ allowedip->family = AF_INET6;
+ } else {
+ if (inet_pton(AF_INET, ip, &allowedip->ip4) == 1)
+ allowedip->family = AF_INET;
+ }
+ allowedip->cidr = strtoul(mask, &end, 10);
+ if (*end || allowedip->family == AF_UNSPEC || (allowedip->family == AF_INET6 && allowedip->cidr > 128) || (allowedip->family == AF_INET && allowedip->cidr > 32))
+ break;
+ } else if (peer && !strcmp(key, "last_handshake_time_sec"))
+ peer->last_handshake_time.tv_sec = NUM(0x7fffffffffffffffULL);
+ else if (peer && !strcmp(key, "last_handshake_time_nsec"))
+ peer->last_handshake_time.tv_nsec = NUM(0x7fffffffffffffffULL);
+ else if (peer && !strcmp(key, "rx_bytes"))
+ peer->rx_bytes = NUM(0xffffffffffffffffULL);
+ else if (peer && !strcmp(key, "tx_bytes"))
+ peer->tx_bytes = NUM(0xffffffffffffffffULL);
+ else if (!strcmp(key, "errno"))
+ ret = -NUM(0x7fffffffU);
+ }
+ ret = -EPROTO;
+err:
+ free(key);
+ if (ret) {
+ free_wgdevice(dev);
+ *out = NULL;
+ }
+ fclose(f);
+ errno = -ret;
+ return ret;
+
+}
+#undef NUM
diff --git a/contrib/wireguard-tools/ipc-windows.h b/contrib/wireguard-tools/ipc-windows.h
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/ipc-windows.h
@@ -0,0 +1,450 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2015-2021 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#include "containers.h"
+#include <windows.h>
+#include <setupapi.h>
+#include <cfgmgr32.h>
+#include <iphlpapi.h>
+#include <initguid.h>
+#include <devguid.h>
+#include <ddk/ndisguid.h>
+#include <wireguard.h>
+#include <hashtable.h>
+
+#define IPC_SUPPORTS_KERNEL_INTERFACE
+
+static bool have_cached_kernel_interfaces;
+static struct hashtable cached_kernel_interfaces;
+static const DEVPROPKEY devpkey_name = DEVPKEY_WG_NAME;
+extern bool is_win7;
+
+static int kernel_get_wireguard_interfaces(struct string_list *list)
+{
+ HDEVINFO dev_info = SetupDiGetClassDevsExW(&GUID_DEVCLASS_NET, is_win7 ? L"ROOT\\WIREGUARD" : L"SWD\\WireGuard", NULL, DIGCF_PRESENT, NULL, NULL, NULL);
+ bool will_have_cached_kernel_interfaces = true;
+
+ if (dev_info == INVALID_HANDLE_VALUE) {
+ errno = EACCES;
+ return -errno;
+ }
+
+ for (DWORD i = 0;; ++i) {
+ DWORD buf_len;
+ WCHAR adapter_name[MAX_ADAPTER_NAME];
+ SP_DEVINFO_DATA dev_info_data = { .cbSize = sizeof(SP_DEVINFO_DATA) };
+ DEVPROPTYPE prop_type;
+ ULONG status, problem_code;
+ char *interface_name;
+ struct hashtable_entry *entry;
+
+ if (!SetupDiEnumDeviceInfo(dev_info, i, &dev_info_data)) {
+ if (GetLastError() == ERROR_NO_MORE_ITEMS)
+ break;
+ continue;
+ }
+
+ if (!SetupDiGetDevicePropertyW(dev_info, &dev_info_data, &devpkey_name,
+ &prop_type, (PBYTE)adapter_name,
+ sizeof(adapter_name), NULL, 0) ||
+ prop_type != DEVPROP_TYPE_STRING)
+ continue;
+ adapter_name[_countof(adapter_name) - 1] = L'0';
+ if (!adapter_name[0])
+ continue;
+ buf_len = WideCharToMultiByte(CP_UTF8, 0, adapter_name, -1, NULL, 0, NULL, NULL);
+ if (!buf_len)
+ continue;
+ interface_name = malloc(buf_len);
+ if (!interface_name)
+ continue;
+ buf_len = WideCharToMultiByte(CP_UTF8, 0, adapter_name, -1, interface_name, buf_len, NULL, NULL);
+ if (!buf_len) {
+ free(interface_name);
+ continue;
+ }
+
+ if (CM_Get_DevNode_Status(&status, &problem_code, dev_info_data.DevInst, 0) == CR_SUCCESS &&
+ (status & (DN_DRIVER_LOADED | DN_STARTED)) == (DN_DRIVER_LOADED | DN_STARTED))
+ string_list_add(list, interface_name);
+
+ entry = hashtable_find_or_insert_entry(&cached_kernel_interfaces, interface_name);
+ free(interface_name);
+ if (!entry)
+ continue;
+
+ if (SetupDiGetDeviceInstanceIdW(dev_info, &dev_info_data, NULL, 0, &buf_len) || GetLastError() != ERROR_INSUFFICIENT_BUFFER)
+ continue;
+ entry->value = calloc(sizeof(WCHAR), buf_len);
+ if (!entry->value)
+ continue;
+ if (!SetupDiGetDeviceInstanceIdW(dev_info, &dev_info_data, entry->value, buf_len, &buf_len)) {
+ free(entry->value);
+ entry->value = NULL;
+ continue;
+ }
+
+ will_have_cached_kernel_interfaces = true;
+ }
+ SetupDiDestroyDeviceInfoList(dev_info);
+ have_cached_kernel_interfaces = will_have_cached_kernel_interfaces;
+ return 0;
+}
+
+static HANDLE kernel_interface_handle(const char *iface)
+{
+ HDEVINFO dev_info;
+ WCHAR *interfaces = NULL;
+ HANDLE handle;
+
+ if (have_cached_kernel_interfaces) {
+ struct hashtable_entry *entry = hashtable_find_entry(&cached_kernel_interfaces, iface);
+ if (entry) {
+ DWORD buf_len;
+ if (CM_Get_Device_Interface_List_SizeW(
+ &buf_len, (GUID *)&GUID_DEVINTERFACE_NET, (DEVINSTID_W)entry->value,
+ CM_GET_DEVICE_INTERFACE_LIST_PRESENT) != CR_SUCCESS)
+ goto err_hash;
+ interfaces = calloc(buf_len, sizeof(*interfaces));
+ if (!interfaces)
+ goto err_hash;
+ if (CM_Get_Device_Interface_ListW(
+ (GUID *)&GUID_DEVINTERFACE_NET, (DEVINSTID_W)entry->value, interfaces, buf_len,
+ CM_GET_DEVICE_INTERFACE_LIST_PRESENT) != CR_SUCCESS || !interfaces[0]) {
+ free(interfaces);
+ interfaces = NULL;
+ goto err_hash;
+ }
+ handle = CreateFileW(interfaces, GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+ OPEN_EXISTING, 0, NULL);
+ free(interfaces);
+ if (handle == INVALID_HANDLE_VALUE)
+ goto err_hash;
+ return handle;
+err_hash:
+ errno = EACCES;
+ return NULL;
+ }
+ }
+
+ dev_info = SetupDiGetClassDevsExW(&GUID_DEVCLASS_NET, is_win7 ? L"ROOT\\WIREGUARD" : L"SWD\\WireGuard", NULL, DIGCF_PRESENT, NULL, NULL, NULL);
+ if (dev_info == INVALID_HANDLE_VALUE)
+ return NULL;
+
+ for (DWORD i = 0; !interfaces; ++i) {
+ bool found;
+ DWORD buf_len;
+ WCHAR *buf, adapter_name[MAX_ADAPTER_NAME];
+ SP_DEVINFO_DATA dev_info_data = { .cbSize = sizeof(SP_DEVINFO_DATA) };
+ DEVPROPTYPE prop_type;
+ char *interface_name;
+
+ if (!SetupDiEnumDeviceInfo(dev_info, i, &dev_info_data)) {
+ if (GetLastError() == ERROR_NO_MORE_ITEMS)
+ break;
+ continue;
+ }
+
+ if (!SetupDiGetDevicePropertyW(dev_info, &dev_info_data, &devpkey_name,
+ &prop_type, (PBYTE)adapter_name,
+ sizeof(adapter_name), NULL, 0) ||
+ prop_type != DEVPROP_TYPE_STRING)
+ continue;
+ adapter_name[_countof(adapter_name) - 1] = L'0';
+ if (!adapter_name[0])
+ continue;
+ buf_len = WideCharToMultiByte(CP_UTF8, 0, adapter_name, -1, NULL, 0, NULL, NULL);
+ if (!buf_len)
+ continue;
+ interface_name = malloc(buf_len);
+ if (!interface_name)
+ continue;
+ buf_len = WideCharToMultiByte(CP_UTF8, 0, adapter_name, -1, interface_name, buf_len, NULL, NULL);
+ if (!buf_len) {
+ free(interface_name);
+ continue;
+ }
+ found = !strcmp(interface_name, iface);
+ free(interface_name);
+ if (!found)
+ continue;
+
+ if (SetupDiGetDeviceInstanceIdW(dev_info, &dev_info_data, NULL, 0, &buf_len) || GetLastError() != ERROR_INSUFFICIENT_BUFFER)
+ continue;
+ buf = calloc(sizeof(*buf), buf_len);
+ if (!buf)
+ continue;
+ if (!SetupDiGetDeviceInstanceIdW(dev_info, &dev_info_data, buf, buf_len, &buf_len))
+ goto cleanup_instance_id;
+ if (CM_Get_Device_Interface_List_SizeW(
+ &buf_len, (GUID *)&GUID_DEVINTERFACE_NET, (DEVINSTID_W)buf,
+ CM_GET_DEVICE_INTERFACE_LIST_PRESENT) != CR_SUCCESS)
+ goto cleanup_instance_id;
+ interfaces = calloc(buf_len, sizeof(*interfaces));
+ if (!interfaces)
+ goto cleanup_instance_id;
+ if (CM_Get_Device_Interface_ListW(
+ (GUID *)&GUID_DEVINTERFACE_NET, (DEVINSTID_W)buf, interfaces, buf_len,
+ CM_GET_DEVICE_INTERFACE_LIST_PRESENT) != CR_SUCCESS || !interfaces[0]) {
+ free(interfaces);
+ interfaces = NULL;
+ goto cleanup_instance_id;
+ }
+cleanup_instance_id:
+ free(buf);
+ }
+ SetupDiDestroyDeviceInfoList(dev_info);
+ if (!interfaces) {
+ errno = ENOENT;
+ return NULL;
+ }
+ handle = CreateFileW(interfaces, GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+ OPEN_EXISTING, 0, NULL);
+ free(interfaces);
+ if (handle == INVALID_HANDLE_VALUE) {
+ errno = EACCES;
+ return NULL;
+ }
+ return handle;
+}
+
+static int kernel_get_device(struct wgdevice **device, const char *iface)
+{
+ WG_IOCTL_INTERFACE *wg_iface;
+ WG_IOCTL_PEER *wg_peer;
+ WG_IOCTL_ALLOWED_IP *wg_aip;
+ void *buf = NULL;
+ DWORD buf_len = 0;
+ HANDLE handle = kernel_interface_handle(iface);
+ struct wgdevice *dev;
+ struct wgpeer *peer;
+ struct wgallowedip *aip;
+ int ret;
+
+ *device = NULL;
+
+ if (!handle)
+ return -errno;
+
+ while (!DeviceIoControl(handle, WG_IOCTL_GET, NULL, 0, buf, buf_len, &buf_len, NULL)) {
+ free(buf);
+ if (GetLastError() != ERROR_MORE_DATA) {
+ errno = EACCES;
+ return -errno;
+ }
+ buf = malloc(buf_len);
+ if (!buf)
+ return -errno;
+ }
+
+ wg_iface = (WG_IOCTL_INTERFACE *)buf;
+ dev = calloc(1, sizeof(*dev));
+ if (!dev)
+ goto out;
+ strncpy(dev->name, iface, sizeof(dev->name));
+ dev->name[sizeof(dev->name) - 1] = '\0';
+
+ if (wg_iface->Flags & WG_IOCTL_INTERFACE_HAS_LISTEN_PORT) {
+ dev->listen_port = wg_iface->ListenPort;
+ dev->flags |= WGDEVICE_HAS_LISTEN_PORT;
+ }
+
+ if (wg_iface->Flags & WG_IOCTL_INTERFACE_HAS_PUBLIC_KEY) {
+ memcpy(dev->public_key, wg_iface->PublicKey, sizeof(dev->public_key));
+ dev->flags |= WGDEVICE_HAS_PUBLIC_KEY;
+ }
+
+ if (wg_iface->Flags & WG_IOCTL_INTERFACE_HAS_PRIVATE_KEY) {
+ memcpy(dev->private_key, wg_iface->PrivateKey, sizeof(dev->private_key));
+ dev->flags |= WGDEVICE_HAS_PRIVATE_KEY;
+ }
+
+ wg_peer = buf + sizeof(WG_IOCTL_INTERFACE);
+ for (ULONG i = 0; i < wg_iface->PeersCount; ++i) {
+ peer = calloc(1, sizeof(*peer));
+ if (!peer)
+ goto out;
+
+ if (dev->first_peer == NULL)
+ dev->first_peer = peer;
+ else
+ dev->last_peer->next_peer = peer;
+ dev->last_peer = peer;
+
+ if (wg_peer->Flags & WG_IOCTL_PEER_HAS_PUBLIC_KEY) {
+ memcpy(peer->public_key, wg_peer->PublicKey, sizeof(peer->public_key));
+ peer->flags |= WGPEER_HAS_PUBLIC_KEY;
+ }
+
+ if (wg_peer->Flags & WG_IOCTL_PEER_HAS_PRESHARED_KEY) {
+ memcpy(peer->preshared_key, wg_peer->PresharedKey, sizeof(peer->preshared_key));
+ if (!key_is_zero(peer->preshared_key))
+ peer->flags |= WGPEER_HAS_PRESHARED_KEY;
+ }
+
+ if (wg_peer->Flags & WG_IOCTL_PEER_HAS_PERSISTENT_KEEPALIVE) {
+ peer->persistent_keepalive_interval = wg_peer->PersistentKeepalive;
+ peer->flags |= WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL;
+ }
+
+ if (wg_peer->Flags & WG_IOCTL_PEER_HAS_ENDPOINT) {
+ if (wg_peer->Endpoint.si_family == AF_INET)
+ peer->endpoint.addr4 = wg_peer->Endpoint.Ipv4;
+ else if (wg_peer->Endpoint.si_family == AF_INET6)
+ peer->endpoint.addr6 = wg_peer->Endpoint.Ipv6;
+ }
+
+ peer->rx_bytes = wg_peer->RxBytes;
+ peer->tx_bytes = wg_peer->TxBytes;
+
+ if (wg_peer->LastHandshake) {
+ peer->last_handshake_time.tv_sec = wg_peer->LastHandshake / 10000000 - 11644473600LL;
+ peer->last_handshake_time.tv_nsec = wg_peer->LastHandshake % 10000000 * 100;
+ }
+
+ wg_aip = (void *)wg_peer + sizeof(WG_IOCTL_PEER);
+ for (ULONG j = 0; j < wg_peer->AllowedIPsCount; ++j) {
+ aip = calloc(1, sizeof(*aip));
+ if (!aip)
+ goto out;
+
+ if (peer->first_allowedip == NULL)
+ peer->first_allowedip = aip;
+ else
+ peer->last_allowedip->next_allowedip = aip;
+ peer->last_allowedip = aip;
+
+ aip->family = wg_aip->AddressFamily;
+ if (wg_aip->AddressFamily == AF_INET) {
+ memcpy(&aip->ip4, &wg_aip->Address.V4, sizeof(aip->ip4));
+ aip->cidr = wg_aip->Cidr;
+ } else if (wg_aip->AddressFamily == AF_INET6) {
+ memcpy(&aip->ip6, &wg_aip->Address.V6, sizeof(aip->ip6));
+ aip->cidr = wg_aip->Cidr;
+ }
+ ++wg_aip;
+ }
+ wg_peer = (WG_IOCTL_PEER *)wg_aip;
+ }
+ *device = dev;
+ errno = 0;
+out:
+ ret = -errno;
+ free(buf);
+ CloseHandle(handle);
+ return ret;
+}
+
+static int kernel_set_device(struct wgdevice *dev)
+{
+ WG_IOCTL_INTERFACE *wg_iface = NULL;
+ WG_IOCTL_PEER *wg_peer;
+ WG_IOCTL_ALLOWED_IP *wg_aip;
+ DWORD buf_len = sizeof(WG_IOCTL_INTERFACE);
+ HANDLE handle = kernel_interface_handle(dev->name);
+ struct wgpeer *peer;
+ struct wgallowedip *aip;
+ size_t peer_count, aip_count;
+ int ret = 0;
+
+ if (!handle)
+ return -errno;
+
+ for_each_wgpeer(dev, peer) {
+ if (DWORD_MAX - buf_len < sizeof(WG_IOCTL_PEER)) {
+ errno = EOVERFLOW;
+ goto out;
+ }
+ buf_len += sizeof(WG_IOCTL_PEER);
+ for_each_wgallowedip(peer, aip) {
+ if (DWORD_MAX - buf_len < sizeof(WG_IOCTL_ALLOWED_IP)) {
+ errno = EOVERFLOW;
+ goto out;
+ }
+ buf_len += sizeof(WG_IOCTL_ALLOWED_IP);
+ }
+ }
+ wg_iface = calloc(1, buf_len);
+ if (!wg_iface)
+ goto out;
+
+ if (dev->flags & WGDEVICE_HAS_PRIVATE_KEY) {
+ memcpy(wg_iface->PrivateKey, dev->private_key, sizeof(wg_iface->PrivateKey));
+ wg_iface->Flags |= WG_IOCTL_INTERFACE_HAS_PRIVATE_KEY;
+ }
+
+ if (dev->flags & WGDEVICE_HAS_LISTEN_PORT) {
+ wg_iface->ListenPort = dev->listen_port;
+ wg_iface->Flags |= WG_IOCTL_INTERFACE_HAS_LISTEN_PORT;
+ }
+
+ if (dev->flags & WGDEVICE_REPLACE_PEERS)
+ wg_iface->Flags |= WG_IOCTL_INTERFACE_REPLACE_PEERS;
+
+ peer_count = 0;
+ wg_peer = (void *)wg_iface + sizeof(WG_IOCTL_INTERFACE);
+ for_each_wgpeer(dev, peer) {
+ wg_peer->Flags = WG_IOCTL_PEER_HAS_PUBLIC_KEY;
+ memcpy(wg_peer->PublicKey, peer->public_key, sizeof(wg_peer->PublicKey));
+
+ if (peer->flags & WGPEER_HAS_PRESHARED_KEY) {
+ memcpy(wg_peer->PresharedKey, peer->preshared_key, sizeof(wg_peer->PresharedKey));
+ wg_peer->Flags |= WG_IOCTL_PEER_HAS_PRESHARED_KEY;
+ }
+
+ if (peer->flags & WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL) {
+ wg_peer->PersistentKeepalive = peer->persistent_keepalive_interval;
+ wg_peer->Flags |= WG_IOCTL_PEER_HAS_PERSISTENT_KEEPALIVE;
+ }
+
+ if (peer->endpoint.addr.sa_family == AF_INET) {
+ wg_peer->Endpoint.Ipv4 = peer->endpoint.addr4;
+ wg_peer->Flags |= WG_IOCTL_PEER_HAS_ENDPOINT;
+ } else if (peer->endpoint.addr.sa_family == AF_INET6) {
+ wg_peer->Endpoint.Ipv6 = peer->endpoint.addr6;
+ wg_peer->Flags |= WG_IOCTL_PEER_HAS_ENDPOINT;
+ }
+
+ if (peer->flags & WGPEER_REPLACE_ALLOWEDIPS)
+ wg_peer->Flags |= WG_IOCTL_PEER_REPLACE_ALLOWED_IPS;
+
+ if (peer->flags & WGPEER_REMOVE_ME)
+ wg_peer->Flags |= WG_IOCTL_PEER_REMOVE;
+
+ aip_count = 0;
+ wg_aip = (void *)wg_peer + sizeof(WG_IOCTL_PEER);
+ for_each_wgallowedip(peer, aip) {
+ wg_aip->AddressFamily = aip->family;
+ wg_aip->Cidr = aip->cidr;
+
+ if (aip->family == AF_INET)
+ wg_aip->Address.V4 = aip->ip4;
+ else if (aip->family == AF_INET6)
+ wg_aip->Address.V6 = aip->ip6;
+ else
+ continue;
+ ++aip_count;
+ ++wg_aip;
+ }
+ wg_peer->AllowedIPsCount = aip_count;
+ ++peer_count;
+ wg_peer = (WG_IOCTL_PEER *)wg_aip;
+ }
+ wg_iface->PeersCount = peer_count;
+
+ if (!DeviceIoControl(handle, WG_IOCTL_SET, NULL, 0, wg_iface, buf_len, &buf_len, NULL)) {
+ errno = EACCES;
+ goto out;
+ }
+ errno = 0;
+
+out:
+ ret = -errno;
+ free(wg_iface);
+ CloseHandle(handle);
+ return ret;
+}
diff --git a/contrib/wireguard-tools/ipc.h b/contrib/wireguard-tools/ipc.h
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/ipc.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0 OR MIT */
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#ifndef IPC_H
+#define IPC_H
+
+#include <stdbool.h>
+
+struct wgdevice;
+
+int ipc_set_device(struct wgdevice *dev);
+int ipc_get_device(struct wgdevice **dev, const char *interface);
+char *ipc_list_devices(void);
+
+#endif
diff --git a/contrib/wireguard-tools/ipc.c b/contrib/wireguard-tools/ipc.c
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/ipc.c
@@ -0,0 +1,98 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include "containers.h"
+#include "ipc.h"
+
+struct string_list {
+ char *buffer;
+ size_t len;
+ size_t cap;
+};
+
+static int string_list_add(struct string_list *list, const char *str)
+{
+ size_t len = strlen(str) + 1;
+
+ if (len == 1)
+ return 0;
+
+ if (len >= list->cap - list->len) {
+ char *new_buffer;
+ size_t new_cap = list->cap * 2;
+
+ if (new_cap < list->len + len + 1)
+ new_cap = list->len + len + 1;
+ new_buffer = realloc(list->buffer, new_cap);
+ if (!new_buffer)
+ return -errno;
+ list->buffer = new_buffer;
+ list->cap = new_cap;
+ }
+ memcpy(list->buffer + list->len, str, len);
+ list->len += len;
+ list->buffer[list->len] = '\0';
+ return 0;
+}
+
+#include "ipc-uapi.h"
+#if defined(__linux__)
+#include "ipc-linux.h"
+#elif defined(__OpenBSD__)
+#include "ipc-openbsd.h"
+#elif defined(__FreeBSD__)
+#include "ipc-freebsd.h"
+#elif defined(_WIN32)
+#include "ipc-windows.h"
+#endif
+
+/* first\0second\0third\0forth\0last\0\0 */
+char *ipc_list_devices(void)
+{
+ struct string_list list = { 0 };
+ int ret;
+
+#ifdef IPC_SUPPORTS_KERNEL_INTERFACE
+ ret = kernel_get_wireguard_interfaces(&list);
+ if (ret < 0)
+ goto cleanup;
+#endif
+ ret = userspace_get_wireguard_interfaces(&list);
+ if (ret < 0)
+ goto cleanup;
+
+cleanup:
+ errno = -ret;
+ if (errno) {
+ free(list.buffer);
+ return NULL;
+ }
+ return list.buffer ?: strdup("\0");
+}
+
+int ipc_get_device(struct wgdevice **dev, const char *iface)
+{
+#ifdef IPC_SUPPORTS_KERNEL_INTERFACE
+ if (userspace_has_wireguard_interface(iface))
+ return userspace_get_device(dev, iface);
+ return kernel_get_device(dev, iface);
+#else
+ return userspace_get_device(dev, iface);
+#endif
+}
+
+int ipc_set_device(struct wgdevice *dev)
+{
+#ifdef IPC_SUPPORTS_KERNEL_INTERFACE
+ if (userspace_has_wireguard_interface(dev->name))
+ return userspace_set_device(dev);
+ return kernel_set_device(dev);
+#else
+ return userspace_set_device(dev);
+#endif
+}
diff --git a/contrib/wireguard-tools/man/wg-quick.8 b/contrib/wireguard-tools/man/wg-quick.8
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/man/wg-quick.8
@@ -0,0 +1,277 @@
+.TH WG-QUICK 8 "2016 January 1" ZX2C4 "WireGuard"
+
+.SH NAME
+wg-quick - set up a WireGuard interface simply
+
+.SH SYNOPSIS
+.B wg-quick
+[
+.I up
+|
+.I down
+|
+.I save
+|
+.I strip
+] [
+.I CONFIG_FILE
+|
+.I INTERFACE
+]
+
+.SH DESCRIPTION
+
+This is an extremely simple script for easily bringing up a WireGuard interface,
+suitable for a few common use cases.
+
+Use \fIup\fP to add and set up an interface, and use \fIdown\fP to tear down and remove
+an interface. Running \fIup\fP adds a WireGuard interface, brings up the interface with the
+supplied IP addresses, sets up mtu and routes, and optionally runs pre/post up scripts. Running \fIdown\fP
+optionally saves the current configuration, removes the WireGuard interface, and optionally
+runs pre/post down scripts. Running \fIsave\fP saves the configuration of an existing
+interface without bringing the interface down. Use \fIstrip\fP to output a configuration file
+with all
+.BR wg-quick (8)-specific
+options removed, suitable for use with
+.BR wg (8).
+
+\fICONFIG_FILE\fP is a configuration file, whose filename is the interface name
+followed by `.conf'. Otherwise, \fIINTERFACE\fP is an interface name, with configuration
+found at `/etc/wireguard/\fIINTERFACE\fP.conf', searched first, followed by distro-specific
+search paths.
+
+Generally speaking, this utility is just a simple script that wraps invocations to
+.BR wg (8)
+and
+.BR ip (8)
+in order to set up a WireGuard interface. It is designed for users with simple
+needs, and users with more advanced needs are highly encouraged to use a more
+specific tool, a more complete network manager, or otherwise just use
+.BR wg (8)
+and
+.BR ip (8),
+as usual.
+
+.SH CONFIGURATION
+
+The configuration file adds a few extra configuration values to the format understood by
+.BR wg (8)
+in order to configure additional attributes of an interface. It handles the
+values that it understands, and then it passes the remaining ones directly to
+.BR wg (8)
+for further processing.
+
+It infers all routes from the list of peers' allowed IPs, and automatically adds
+them to the system routing table. If one of those routes is the default route
+(0.0.0.0/0 or ::/0), then it uses
+.BR ip-rule (8)
+to handle overriding of the default gateway.
+
+The configuration file will be passed directly to \fBwg\fP(8)'s `setconf'
+sub-command, with the exception of the following additions to the \fIInterface\fP section,
+which are handled by this tool:
+
+.IP \(bu
+Address \(em a comma-separated list of IP (v4 or v6) addresses (optionally with CIDR masks)
+to be assigned to the interface. May be specified multiple times.
+.IP \(bu
+DNS \(em a comma-separated list of IP (v4 or v6) addresses to be set as the interface's
+DNS servers, or non-IP hostnames to be set as the interface's DNS search domains. May be
+specified multiple times. Upon bringing the interface up, this runs
+`resolvconf -a tun.\fIINTERFACE\fP -m 0 -x` and upon bringing it down, this runs
+`resolvconf -d tun.\fIINTERFACE\fP`. If these particular invocations of
+.BR resolvconf (8)
+are undesirable, the PostUp and PostDown keys below may be used instead.
+.IP \(bu
+MTU \(em if not specified, the MTU is automatically determined from the endpoint addresses
+or the system default route, which is usually a sane choice. However, to manually specify
+an MTU to override this automatic discovery, this value may be specified explicitly.
+.IP \(bu
+Table \(em Controls the routing table to which routes are added. There are two
+special values: `off' disables the creation of routes altogether, and `auto'
+(the default) adds routes to the default table and enables special handling of
+default routes.
+.IP \(bu
+PreUp, PostUp, PreDown, PostDown \(em script snippets which will be executed by
+.BR bash (1)
+before/after setting up/tearing down the interface, most commonly used
+to configure custom DNS options or firewall rules. The special string `%i'
+is expanded to \fIINTERFACE\fP. Each one may be specified multiple times, in which case
+the commands are executed in order.
+.IP \(bu
+SaveConfig \(em if set to `true', the configuration is saved from the current state of the
+interface upon shutdown. Any changes made to the configuration file before the
+interface is removed will therefore be overwritten.
+
+.P
+Recommended \fIINTERFACE\fP names include `wg0' or `wgvpn0' or even `wgmgmtlan0'.
+However, the number at the end is in fact optional, and really
+any free-form string [a-zA-Z0-9_=+.-]{1,15} will work. So even interface names corresponding
+to geographic locations would suffice, such as `cincinnati', `nyc', or `paris', if that's
+somehow desirable.
+
+.SH EXAMPLES
+
+These examples draw on the same syntax found for
+.BR wg (8),
+and a more complete description may be found there. Bold lines below are for options that extend
+.BR wg (8).
+
+The following might be used for connecting as a client to a VPN gateway for tunneling all
+traffic:
+
+ [Interface]
+.br
+ \fBAddress = 10.200.100.8/24\fP
+.br
+ \fBDNS = 10.200.100.1\fP
+.br
+ PrivateKey = oK56DE9Ue9zK76rAc8pBl6opph+1v36lm7cXXsQKrQM=
+.br
+
+.br
+ [Peer]
+.br
+ PublicKey = GtL7fZc/bLnqZldpVofMCD6hDjrK28SsdLxevJ+qtKU=
+.br
+ PresharedKey = /UwcSPg38hW/D9Y3tcS1FOV0K1wuURMbS0sesJEP5ak=
+.br
+ AllowedIPs = 0.0.0.0/0
+.br
+ Endpoint = demo.wireguard.com:51820
+.br
+
+The `Address` field is added here in order to set up the address for the interface. The `DNS` field
+indicates that a DNS server for the interface should be configured via
+.BR resolvconf (8).
+The peer's allowed IPs entry implies that this interface should be configured as the default gateway,
+which this script does.
+
+Building on the last example, one might attempt the so-called ``kill-switch'', in order
+to prevent the flow of unencrypted packets through the non-WireGuard interfaces, by adding the following
+two lines `PostUp` and `PreDown` lines to the `[Interface]` section:
+
+ \fBPostUp = iptables -I OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT\fP
+.br
+ \fBPreDown = iptables -D OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT\fP
+.br
+
+The `PostUp' and `PreDown' fields have been added to specify an
+.BR iptables (8)
+command which, when used with interfaces that have a peer that specifies 0.0.0.0/0 as part of the
+`AllowedIPs', works together with wg-quick's fwmark usage in order to drop all packets that
+are either not coming out of the tunnel encrypted or not going through the tunnel itself. (Note
+that this continues to allow most DHCP traffic through, since most DHCP clients make use of PF_PACKET
+sockets, which bypass Netfilter.) When IPv6 is in use, additional similar lines could be added using
+.BR ip6tables (8).
+
+Or, perhaps it is desirable to store private keys in encrypted form, such as through use of
+.BR pass (1):
+
+ \fBPostUp = wg set %i private-key <(pass WireGuard/private-keys/%i)\fP
+.br
+
+For use on a server, the following is a more complicated example involving multiple peers:
+
+ [Interface]
+.br
+ \fBAddress = 10.192.122.1/24\fP
+.br
+ \fBAddress = 10.10.0.1/16\fP
+.br
+ \fBSaveConfig = true\fP
+.br
+ PrivateKey = yAnz5TF+lXXJte14tji3zlMNq+hd2rYUIgJBgB3fBmk=
+.br
+ ListenPort = 51820
+.br
+
+.br
+ [Peer]
+.br
+ PublicKey = xTIBA5rboUvnH4htodjb6e697QjLERt1NAB4mZqp8Dg=
+.br
+ AllowedIPs = 10.192.122.3/32, 10.192.124.1/24
+.br
+
+.br
+ [Peer]
+.br
+ PublicKey = TrMvSoP4jYQlY6RIzBgbssQqY3vxI2Pi+y71lOWWXX0=
+.br
+ AllowedIPs = 10.192.122.4/32, 192.168.0.0/16
+.br
+
+.br
+ [Peer]
+.br
+ PublicKey = gN65BkIKy1eCE9pP1wdc8ROUtkHLF2PfAqYdyYBz6EA=
+.br
+ AllowedIPs = 10.10.10.230/32
+
+Notice the two `Address' lines at the top, and that `SaveConfig' is set to `true', indicating
+that the configuration file should be saved on shutdown using the current status of the
+interface.
+
+A combination of the `Table', `PostUp', and `PreDown' fields may be used for policy routing
+as well. For example, the following may be used to send SSH traffic (TCP port 22) traffic
+through the tunnel:
+
+ [Interface]
+.br
+ Address = 10.192.122.1/24
+.br
+ PrivateKey = yAnz5TF+lXXJte14tji3zlMNq+hd2rYUIgJBgB3fBmk=
+.br
+ ListenPort = 51820
+.br
+ \fBTable = 1234\fP
+.br
+ \fBPostUp = ip rule add ipproto tcp dport 22 table 1234\fP
+.br
+ \fBPreDown = ip rule delete ipproto tcp dport 22 table 1234\fP
+.br
+
+.br
+ [Peer]
+.br
+ PublicKey = xTIBA5rboUvnH4htodjb6e697QjLERt1NAB4mZqp8Dg=
+.br
+ AllowedIPs = 0.0.0.0/0
+
+These configuration files may be placed in any directory, putting the desired interface name
+in the filename:
+
+\fB # wg-quick up /path/to/wgnet0.conf\fP
+
+For convenience, if only an interface name is supplied, it automatically chooses a path in
+`/etc/wireguard/':
+
+\fB # wg-quick up wgnet0\fP
+
+This will load the configuration file `/etc/wireguard/wgnet0.conf'.
+
+The \fIstrip\fP command is useful for reloading configuration files without disrupting active
+sessions:
+
+\fB # wg syncconf wgnet0 <(wg-quick strip wgnet0)\fP
+
+.SH SEE ALSO
+.BR wg (8),
+.BR ip (8),
+.BR ip-link (8),
+.BR ip-address (8),
+.BR ip-route (8),
+.BR ip-rule (8),
+.BR resolvconf (8).
+
+.SH AUTHOR
+.B wg-quick
+was written by
+.MT Jason@zx2c4.com
+Jason A. Donenfeld
+.ME .
+For updates and more information, a project page is available on the
+.UR https://\:www.wireguard.com/
+World Wide Web
+.UE .
diff --git a/contrib/wireguard-tools/man/wg.8 b/contrib/wireguard-tools/man/wg.8
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/man/wg.8
@@ -0,0 +1,258 @@
+.TH WG 8 "2015 August 13" ZX2C4 "WireGuard"
+
+.SH NAME
+wg - set and retrieve configuration of WireGuard interfaces
+
+.SH SYNOPSIS
+.B wg
+[
+.I COMMAND
+] [
+.I OPTIONS
+]... [
+.I ARGS
+]...
+
+.SH DESCRIPTION
+
+.B wg
+is the configuration utility for getting and setting the configuration of
+WireGuard tunnel interfaces. The interfaces themselves can be added and removed
+using
+.BR ip-link (8)
+and their IP addresses and routing tables can be set using
+.BR ip-address (8)
+and
+.BR ip-route (8).
+The
+.B wg
+utility provides a series of sub-commands for changing WireGuard-specific
+aspects of WireGuard interfaces.
+
+If no COMMAND is specified, COMMAND defaults to
+.BR show .
+Sub-commands that take an INTERFACE must be passed a WireGuard interface.
+
+.SH COMMANDS
+
+.TP
+\fBshow\fP { \fI<interface>\fP | \fIall\fP | \fIinterfaces\fP } [\fIpublic-key\fP | \fIprivate-key\fP | \fIlisten-port\fP | \fIfwmark\fP | \fIpeers\fP | \fIpreshared-keys\fP | \fIendpoints\fP | \fIallowed-ips\fP | \fIlatest-handshakes\fP | \fIpersistent-keepalive\fP | \fItransfer\fP | \fIdump\fP]
+Shows current WireGuard configuration and runtime information of specified \fI<interface>\fP.
+If no \fI<interface>\fP is specified, \fI<interface>\fP defaults to \fIall\fP.
+If \fIinterfaces\fP is specified, prints a list of all WireGuard interfaces,
+one per line, and quits. If no options are given after the interface
+specification, then prints a list of all attributes in a visually pleasing way
+meant for the terminal. Otherwise, prints specified information grouped by
+newlines and tabs, meant to be used in scripts. For this script-friendly display,
+if \fIall\fP is specified, then the first field for all categories of information
+is the interface name. If \fPdump\fP is specified, then several lines are printed;
+the first contains in order separated by tab: private-key, public-key, listen-port,
+fwmark. Subsequent lines are printed for each peer and contain in order separated
+by tab: public-key, preshared-key, endpoint, allowed-ips, latest-handshake,
+transfer-rx, transfer-tx, persistent-keepalive.
+.TP
+\fBshowconf\fP \fI<interface>\fP
+Shows the current configuration of \fI<interface>\fP in the format described
+by \fICONFIGURATION FILE FORMAT\fP below.
+.TP
+\fBset\fP \fI<interface>\fP [\fIlisten-port\fP \fI<port>\fP] [\fIfwmark\fP \fI<fwmark>\fP] [\fIprivate-key\fP \fI<file-path>\fP] [\fIpeer\fP \fI<base64-public-key>\fP [\fIremove\fP] [\fIpreshared-key\fP \fI<file-path>\fP] [\fIendpoint\fP \fI<ip>:<port>\fP] [\fIpersistent-keepalive\fP \fI<interval seconds>\fP] [\fIallowed-ips\fP \fI<ip1>/<cidr1>\fP[,\fI<ip2>/<cidr2>\fP]...] ]...
+Sets configuration values for the specified \fI<interface>\fP. Multiple
+\fIpeer\fPs may be specified, and if the \fIremove\fP argument is given
+for a peer, that peer is removed, not configured. If \fIlisten-port\fP
+is not specified, or set to 0, the port will be chosen randomly when the
+interface comes up. Both \fIprivate-key\fP and \fIpreshared-key\fP must
+be files, because command line arguments are not considered private on
+most systems but if you are using
+.BR bash (1),
+you may safely pass in a string by specifying as \fIprivate-key\fP or
+\fIpreshared-key\fP the expression: <(echo PRIVATEKEYSTRING). If
+\fI/dev/null\fP or another empty file is specified as the filename for
+either \fIprivate-key\fP or \fIpreshared-key\fP, the key is removed from
+the device. The use of \fIpreshared-key\fP is optional, and may be omitted;
+it adds an additional layer of symmetric-key cryptography to be mixed into
+the already existing public-key cryptography, for post-quantum resistance.
+If \fIallowed-ips\fP is specified, but the value is the empty string, all
+allowed ips are removed from the peer. The use of \fIpersistent-keepalive\fP
+is optional and is by default off; setting it to 0 or "off" disables it.
+Otherwise it represents, in seconds, between 1 and 65535 inclusive, how often
+to send an authenticated empty packet to the peer, for the purpose of keeping
+a stateful firewall or NAT mapping valid persistently. For example, if the
+interface very rarely sends traffic, but it might at anytime receive traffic
+from a peer, and it is behind NAT, the interface might benefit from having a
+persistent keepalive interval of 25 seconds; however, most users will not need
+this. The use of \fIfwmark\fP is optional and is by default off; setting it to
+0 or "off" disables it. Otherwise it is a 32-bit fwmark for outgoing packets
+and may be specified in hexadecimal by prepending "0x".
+.TP
+\fBsetconf\fP \fI<interface>\fP \fI<configuration-filename>\fP
+Sets the current configuration of \fI<interface>\fP to the contents of
+\fI<configuration-filename>\fP, which must be in the format described
+by \fICONFIGURATION FILE FORMAT\fP below.
+.TP
+\fBaddconf\fP \fI<interface>\fP \fI<configuration-filename>\fP
+Appends the contents of \fI<configuration-filename>\fP, which must
+be in the format described by \fICONFIGURATION FILE FORMAT\fP below,
+to the current configuration of \fI<interface>\fP.
+.TP
+\fBsyncconf\fP \fI<interface>\fP \fI<configuration-filename>\fP
+Like \fBsetconf\fP, but reads back the existing configuration first
+and only makes changes that are explicitly different between the configuration
+file and the interface. This is much less efficient than \fBsetconf\fP,
+but has the benefit of not disrupting current peer sessions. The contents of
+\fI<configuration-filename>\fP must be in the format described by
+\fICONFIGURATION FILE FORMAT\fP below.
+.TP
+\fBgenkey\fP
+Generates a random \fIprivate\fP key in base64 and prints it to
+standard output.
+.TP
+\fBgenpsk\fP
+Generates a random \fIpreshared\fP key in base64 and prints it to
+standard output.
+.TP
+\fBpubkey\fP
+Calculates a \fIpublic\fP key and prints it in base64 to standard
+output from a corresponding \fIprivate\fP key (generated with
+\fIgenkey\fP) given in base64 on standard input.
+
+A private key and a corresponding public key may be generated at once by calling:
+.br
+ $ umask 077
+.br
+ $ wg genkey | tee private.key | wg pubkey > public.key
+.TP
+\fBhelp\fP
+Shows usage message.
+
+.SH CONFIGURATION FILE FORMAT
+The configuration file format is based on \fIINI\fP. There are two top level sections
+-- \fIInterface\fP and \fIPeer\fP. Multiple \fIPeer\fP sections may be specified, but
+only one \fIInterface\fP section may be specified.
+
+.P
+The \fIInterface\fP section may contain the following fields:
+.IP \(bu
+PrivateKey \(em a base64 private key generated by \fIwg genkey\fP. Required.
+.IP \(bu
+ListenPort \(em a 16-bit port for listening. Optional; if not specified, chosen
+randomly.
+.IP \(bu
+FwMark \(em a 32-bit fwmark for outgoing packets. If set to 0 or "off", this
+option is disabled. May be specified in hexadecimal by prepending "0x". Optional.
+.P
+The \fIPeer\fP sections may contain the following fields:
+.IP \(bu
+PublicKey \(em a base64 public key calculated by \fIwg pubkey\fP from a
+private key, and usually transmitted out of band to the author of the
+configuration file. Required.
+.IP \(bu
+PresharedKey \(em a base64 preshared key generated by \fIwg genpsk\fP. Optional,
+and may be omitted. This option adds an additional layer of symmetric-key
+cryptography to be mixed into the already existing public-key cryptography,
+for post-quantum resistance.
+.IP \(bu
+AllowedIPs \(em a comma-separated list of IP (v4 or v6) addresses with
+CIDR masks from which incoming traffic for this peer is allowed and to
+which outgoing traffic for this peer is directed. The catch-all
+\fI0.0.0.0/0\fP may be specified for matching all IPv4 addresses, and
+\fI::/0\fP may be specified for matching all IPv6 addresses. May be specified
+multiple times.
+.IP \(bu
+Endpoint \(em an endpoint IP or hostname, followed by a colon, and then a
+port number. This endpoint will be updated automatically to the most recent
+source IP address and port of correctly authenticated packets from the peer.
+Optional.
+.IP \(bu
+PersistentKeepalive \(em a seconds interval, between 1 and 65535 inclusive, of
+how often to send an authenticated empty packet to the peer for the purpose of keeping a
+stateful firewall or NAT mapping valid persistently. For example, if the interface
+very rarely sends traffic, but it might at anytime receive traffic from a peer,
+and it is behind NAT, the interface might benefit from having a persistent keepalive
+interval of 25 seconds. If set to 0 or "off", this option is disabled. By default or
+when unspecified, this option is off. Most users will not need this. Optional.
+
+.SH CONFIGURATION FILE FORMAT EXAMPLE
+This example may be used as a model for writing configuration files, following an
+INI-like syntax. Characters after and including a '#' are considered comments and
+are thus ignored.
+
+ [Interface]
+.br
+ PrivateKey = yAnz5TF+lXXJte14tji3zlMNq+hd2rYUIgJBgB3fBmk=
+.br
+ ListenPort = 51820
+.br
+
+.br
+ [Peer]
+.br
+ PublicKey = xTIBA5rboUvnH4htodjb6e697QjLERt1NAB4mZqp8Dg=
+.br
+ Endpoint = 192.95.5.67:1234
+.br
+ AllowedIPs = 10.192.122.3/32, 10.192.124.1/24
+.br
+
+.br
+ [Peer]
+.br
+ PublicKey = TrMvSoP4jYQlY6RIzBgbssQqY3vxI2Pi+y71lOWWXX0=
+.br
+ Endpoint = [2607:5300:60:6b0::c05f:543]:2468
+.br
+ AllowedIPs = 10.192.122.4/32, 192.168.0.0/16
+.br
+
+.br
+ [Peer]
+.br
+ PublicKey = gN65BkIKy1eCE9pP1wdc8ROUtkHLF2PfAqYdyYBz6EA=
+.br
+ Endpoint = test.wireguard.com:18981
+.br
+ AllowedIPs = 10.10.10.230/32
+
+.SH DEBUGGING INFORMATION
+Sometimes it is useful to have information on the current runtime state of a tunnel. When using the Linux kernel module on a kernel that supports dynamic debugging, debugging information can be written into
+.BR dmesg (1)
+by running as root:
+
+\fB # modprobe wireguard && echo module wireguard +p > /sys/kernel/debug/dynamic_debug/control\fP
+
+On OpenBSD and FreeBSD, debugging information can be written into
+.BR dmesg (1)
+on a per-interface basis by using
+.BR ifconfig (1):
+
+\fB # ifconfig wg0 debug
+
+On userspace implementations, it is customary to set the \fILOG_LEVEL\fP environment variable to \fIverbose\fP.
+
+.SH ENVIRONMENT VARIABLES
+.TP
+.I WG_COLOR_MODE
+If set to \fIalways\fP, always print ANSI colorized output. If set to \fInever\fP, never print ANSI colorized output. If set to \fIauto\fP, something invalid, or unset, then print ANSI colorized output only when writing to a TTY.
+.TP
+.I WG_HIDE_KEYS
+If set to \fInever\fP, then the pretty-printing \fBshow\fP sub-command will show private and preshared keys in the output. If set to \fIalways\fP, something invalid, or unset, then private and preshared keys will be printed as "(hidden)".
+.TP
+.I WG_ENDPOINT_RESOLUTION_RETRIES
+If set to an integer or to \fIinfinity\fP, DNS resolution for each peer's endpoint will be retried that many times for non-permanent errors, with an increasing delay between retries. If unset, the default is 15 retries.
+
+.SH SEE ALSO
+.BR wg-quick (8),
+.BR ip (8),
+.BR ip-link (8),
+.BR ip-address (8),
+.BR ip-route (8).
+
+.SH AUTHOR
+.B wg
+was written by
+.MT Jason@zx2c4.com
+Jason A. Donenfeld
+.ME .
+For updates and more information, a project page is available on the
+.UR https://\:www.wireguard.com/
+World Wide Web
+.UE .
diff --git a/contrib/wireguard-tools/netlink.h b/contrib/wireguard-tools/netlink.h
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/netlink.h
@@ -0,0 +1,797 @@
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ * Copyright (C) 2008-2012 Pablo Neira Ayuso <pablo@netfilter.org>.
+ */
+
+/* This is a minimized version of libmnl meant to be #include'd */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <linux/netlink.h>
+#include <linux/genetlink.h>
+
+#define MNL_SOCKET_AUTOPID 0
+#define MNL_ALIGNTO 4
+#define MNL_ALIGN(len) (((len)+MNL_ALIGNTO-1) & ~(MNL_ALIGNTO-1))
+#define MNL_NLMSG_HDRLEN MNL_ALIGN(sizeof(struct nlmsghdr))
+#define MNL_ATTR_HDRLEN MNL_ALIGN(sizeof(struct nlattr))
+
+enum mnl_attr_data_type {
+ MNL_TYPE_UNSPEC,
+ MNL_TYPE_U8,
+ MNL_TYPE_U16,
+ MNL_TYPE_U32,
+ MNL_TYPE_U64,
+ MNL_TYPE_STRING,
+ MNL_TYPE_FLAG,
+ MNL_TYPE_MSECS,
+ MNL_TYPE_NESTED,
+ MNL_TYPE_NESTED_COMPAT,
+ MNL_TYPE_NUL_STRING,
+ MNL_TYPE_BINARY,
+ MNL_TYPE_MAX,
+};
+
+#define mnl_attr_for_each(attr, nlh, offset) \
+ for ((attr) = mnl_nlmsg_get_payload_offset((nlh), (offset)); \
+ mnl_attr_ok((attr), (char *)mnl_nlmsg_get_payload_tail(nlh) - (char *)(attr)); \
+ (attr) = mnl_attr_next(attr))
+
+#define mnl_attr_for_each_nested(attr, nest) \
+ for ((attr) = mnl_attr_get_payload(nest); \
+ mnl_attr_ok((attr), (char *)mnl_attr_get_payload(nest) + mnl_attr_get_payload_len(nest) - (char *)(attr)); \
+ (attr) = mnl_attr_next(attr))
+
+#define mnl_attr_for_each_payload(payload, payload_size) \
+ for ((attr) = (payload); \
+ mnl_attr_ok((attr), (char *)(payload) + payload_size - (char *)(attr)); \
+ (attr) = mnl_attr_next(attr))
+
+#define MNL_CB_ERROR -1
+#define MNL_CB_STOP 0
+#define MNL_CB_OK 1
+
+typedef int (*mnl_attr_cb_t)(const struct nlattr *attr, void *data);
+typedef int (*mnl_cb_t)(const struct nlmsghdr *nlh, void *data);
+
+#ifndef MNL_ARRAY_SIZE
+#define MNL_ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))
+#endif
+
+static size_t mnl_ideal_socket_buffer_size(void)
+{
+ static size_t size = 0;
+
+ if (size)
+ return size;
+ size = (size_t)sysconf(_SC_PAGESIZE);
+ if (size > 8192)
+ size = 8192;
+ return size;
+}
+
+static size_t mnl_nlmsg_size(size_t len)
+{
+ return len + MNL_NLMSG_HDRLEN;
+}
+
+static struct nlmsghdr *mnl_nlmsg_put_header(void *buf)
+{
+ int len = MNL_ALIGN(sizeof(struct nlmsghdr));
+ struct nlmsghdr *nlh = buf;
+
+ memset(buf, 0, len);
+ nlh->nlmsg_len = len;
+ return nlh;
+}
+
+static void *mnl_nlmsg_put_extra_header(struct nlmsghdr *nlh, size_t size)
+{
+ char *ptr = (char *)nlh + nlh->nlmsg_len;
+ size_t len = MNL_ALIGN(size);
+ nlh->nlmsg_len += len;
+ memset(ptr, 0, len);
+ return ptr;
+}
+
+static void *mnl_nlmsg_get_payload(const struct nlmsghdr *nlh)
+{
+ return (void *)nlh + MNL_NLMSG_HDRLEN;
+}
+
+static void *mnl_nlmsg_get_payload_offset(const struct nlmsghdr *nlh, size_t offset)
+{
+ return (void *)nlh + MNL_NLMSG_HDRLEN + MNL_ALIGN(offset);
+}
+
+static bool mnl_nlmsg_ok(const struct nlmsghdr *nlh, int len)
+{
+ return len >= (int)sizeof(struct nlmsghdr) &&
+ nlh->nlmsg_len >= sizeof(struct nlmsghdr) &&
+ (int)nlh->nlmsg_len <= len;
+}
+
+static struct nlmsghdr *mnl_nlmsg_next(const struct nlmsghdr *nlh, int *len)
+{
+ *len -= MNL_ALIGN(nlh->nlmsg_len);
+ return (struct nlmsghdr *)((void *)nlh + MNL_ALIGN(nlh->nlmsg_len));
+}
+
+static void *mnl_nlmsg_get_payload_tail(const struct nlmsghdr *nlh)
+{
+ return (void *)nlh + MNL_ALIGN(nlh->nlmsg_len);
+}
+
+static bool mnl_nlmsg_seq_ok(const struct nlmsghdr *nlh, unsigned int seq)
+{
+ return nlh->nlmsg_seq && seq ? nlh->nlmsg_seq == seq : true;
+}
+
+static bool mnl_nlmsg_portid_ok(const struct nlmsghdr *nlh, unsigned int portid)
+{
+ return nlh->nlmsg_pid && portid ? nlh->nlmsg_pid == portid : true;
+}
+
+static uint16_t mnl_attr_get_type(const struct nlattr *attr)
+{
+ return attr->nla_type & NLA_TYPE_MASK;
+}
+
+static uint16_t mnl_attr_get_payload_len(const struct nlattr *attr)
+{
+ return attr->nla_len - MNL_ATTR_HDRLEN;
+}
+
+static void *mnl_attr_get_payload(const struct nlattr *attr)
+{
+ return (void *)attr + MNL_ATTR_HDRLEN;
+}
+
+static bool mnl_attr_ok(const struct nlattr *attr, int len)
+{
+ return len >= (int)sizeof(struct nlattr) &&
+ attr->nla_len >= sizeof(struct nlattr) &&
+ (int)attr->nla_len <= len;
+}
+
+static struct nlattr *mnl_attr_next(const struct nlattr *attr)
+{
+ return (struct nlattr *)((void *)attr + MNL_ALIGN(attr->nla_len));
+}
+
+static int mnl_attr_type_valid(const struct nlattr *attr, uint16_t max)
+{
+ if (mnl_attr_get_type(attr) > max) {
+ errno = EOPNOTSUPP;
+ return -1;
+ }
+ return 1;
+}
+
+static int __mnl_attr_validate(const struct nlattr *attr,
+ enum mnl_attr_data_type type, size_t exp_len)
+{
+ uint16_t attr_len = mnl_attr_get_payload_len(attr);
+ const char *attr_data = mnl_attr_get_payload(attr);
+
+ if (attr_len < exp_len) {
+ errno = ERANGE;
+ return -1;
+ }
+ switch(type) {
+ case MNL_TYPE_FLAG:
+ if (attr_len > 0) {
+ errno = ERANGE;
+ return -1;
+ }
+ break;
+ case MNL_TYPE_NUL_STRING:
+ if (attr_len == 0) {
+ errno = ERANGE;
+ return -1;
+ }
+ if (attr_data[attr_len-1] != '\0') {
+ errno = EINVAL;
+ return -1;
+ }
+ break;
+ case MNL_TYPE_STRING:
+ if (attr_len == 0) {
+ errno = ERANGE;
+ return -1;
+ }
+ break;
+ case MNL_TYPE_NESTED:
+
+ if (attr_len == 0)
+ break;
+
+ if (attr_len < MNL_ATTR_HDRLEN) {
+ errno = ERANGE;
+ return -1;
+ }
+ break;
+ default:
+
+ break;
+ }
+ if (exp_len && attr_len > exp_len) {
+ errno = ERANGE;
+ return -1;
+ }
+ return 0;
+}
+
+static const size_t mnl_attr_data_type_len[MNL_TYPE_MAX] = {
+ [MNL_TYPE_U8] = sizeof(uint8_t),
+ [MNL_TYPE_U16] = sizeof(uint16_t),
+ [MNL_TYPE_U32] = sizeof(uint32_t),
+ [MNL_TYPE_U64] = sizeof(uint64_t),
+ [MNL_TYPE_MSECS] = sizeof(uint64_t),
+};
+
+static int mnl_attr_validate(const struct nlattr *attr, enum mnl_attr_data_type type)
+{
+ int exp_len;
+
+ if (type >= MNL_TYPE_MAX) {
+ errno = EINVAL;
+ return -1;
+ }
+ exp_len = mnl_attr_data_type_len[type];
+ return __mnl_attr_validate(attr, type, exp_len);
+}
+
+static int mnl_attr_parse(const struct nlmsghdr *nlh, unsigned int offset,
+ mnl_attr_cb_t cb, void *data)
+{
+ int ret = MNL_CB_OK;
+ const struct nlattr *attr;
+
+ mnl_attr_for_each(attr, nlh, offset)
+ if ((ret = cb(attr, data)) <= MNL_CB_STOP)
+ return ret;
+ return ret;
+}
+
+static int mnl_attr_parse_nested(const struct nlattr *nested, mnl_attr_cb_t cb,
+ void *data)
+{
+ int ret = MNL_CB_OK;
+ const struct nlattr *attr;
+
+ mnl_attr_for_each_nested(attr, nested)
+ if ((ret = cb(attr, data)) <= MNL_CB_STOP)
+ return ret;
+ return ret;
+}
+
+static uint8_t mnl_attr_get_u8(const struct nlattr *attr)
+{
+ return *((uint8_t *)mnl_attr_get_payload(attr));
+}
+
+static uint16_t mnl_attr_get_u16(const struct nlattr *attr)
+{
+ return *((uint16_t *)mnl_attr_get_payload(attr));
+}
+
+static uint32_t mnl_attr_get_u32(const struct nlattr *attr)
+{
+ return *((uint32_t *)mnl_attr_get_payload(attr));
+}
+
+static uint64_t mnl_attr_get_u64(const struct nlattr *attr)
+{
+ uint64_t tmp;
+ memcpy(&tmp, mnl_attr_get_payload(attr), sizeof(tmp));
+ return tmp;
+}
+
+static const char *mnl_attr_get_str(const struct nlattr *attr)
+{
+ return mnl_attr_get_payload(attr);
+}
+
+static void mnl_attr_put(struct nlmsghdr *nlh, uint16_t type, size_t len,
+ const void *data)
+{
+ struct nlattr *attr = mnl_nlmsg_get_payload_tail(nlh);
+ uint16_t payload_len = MNL_ALIGN(sizeof(struct nlattr)) + len;
+ int pad;
+
+ attr->nla_type = type;
+ attr->nla_len = payload_len;
+ memcpy(mnl_attr_get_payload(attr), data, len);
+ nlh->nlmsg_len += MNL_ALIGN(payload_len);
+ pad = MNL_ALIGN(len) - len;
+ if (pad > 0)
+ memset(mnl_attr_get_payload(attr) + len, 0, pad);
+}
+
+static void mnl_attr_put_u16(struct nlmsghdr *nlh, uint16_t type, uint16_t data)
+{
+ mnl_attr_put(nlh, type, sizeof(uint16_t), &data);
+}
+
+static void mnl_attr_put_u32(struct nlmsghdr *nlh, uint16_t type, uint32_t data)
+{
+ mnl_attr_put(nlh, type, sizeof(uint32_t), &data);
+}
+
+static void mnl_attr_put_strz(struct nlmsghdr *nlh, uint16_t type, const char *data)
+{
+ mnl_attr_put(nlh, type, strlen(data)+1, data);
+}
+
+static struct nlattr *mnl_attr_nest_start(struct nlmsghdr *nlh, uint16_t type)
+{
+ struct nlattr *start = mnl_nlmsg_get_payload_tail(nlh);
+
+ start->nla_type = NLA_F_NESTED | type;
+ nlh->nlmsg_len += MNL_ALIGN(sizeof(struct nlattr));
+ return start;
+}
+
+static bool mnl_attr_put_check(struct nlmsghdr *nlh, size_t buflen,
+ uint16_t type, size_t len, const void *data)
+{
+ if (nlh->nlmsg_len + MNL_ATTR_HDRLEN + MNL_ALIGN(len) > buflen)
+ return false;
+ mnl_attr_put(nlh, type, len, data);
+ return true;
+}
+
+static bool mnl_attr_put_u8_check(struct nlmsghdr *nlh, size_t buflen,
+ uint16_t type, uint8_t data)
+{
+ return mnl_attr_put_check(nlh, buflen, type, sizeof(uint8_t), &data);
+}
+
+static bool mnl_attr_put_u16_check(struct nlmsghdr *nlh, size_t buflen,
+ uint16_t type, uint16_t data)
+{
+ return mnl_attr_put_check(nlh, buflen, type, sizeof(uint16_t), &data);
+}
+
+static bool mnl_attr_put_u32_check(struct nlmsghdr *nlh, size_t buflen,
+ uint16_t type, uint32_t data)
+{
+ return mnl_attr_put_check(nlh, buflen, type, sizeof(uint32_t), &data);
+}
+
+static struct nlattr *mnl_attr_nest_start_check(struct nlmsghdr *nlh, size_t buflen,
+ uint16_t type)
+{
+ if (nlh->nlmsg_len + MNL_ATTR_HDRLEN > buflen)
+ return NULL;
+ return mnl_attr_nest_start(nlh, type);
+}
+
+static void mnl_attr_nest_end(struct nlmsghdr *nlh, struct nlattr *start)
+{
+ start->nla_len = mnl_nlmsg_get_payload_tail(nlh) - (void *)start;
+}
+
+static void mnl_attr_nest_cancel(struct nlmsghdr *nlh, struct nlattr *start)
+{
+ nlh->nlmsg_len -= mnl_nlmsg_get_payload_tail(nlh) - (void *)start;
+}
+
+static int mnl_cb_noop(__attribute__((unused)) const struct nlmsghdr *nlh, __attribute__((unused)) void *data)
+{
+ return MNL_CB_OK;
+}
+
+static int mnl_cb_error(const struct nlmsghdr *nlh, __attribute__((unused)) void *data)
+{
+ const struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh);
+
+ if (nlh->nlmsg_len < mnl_nlmsg_size(sizeof(struct nlmsgerr))) {
+ errno = EBADMSG;
+ return MNL_CB_ERROR;
+ }
+
+ if (err->error < 0)
+ errno = -err->error;
+ else
+ errno = err->error;
+
+ return err->error == 0 ? MNL_CB_STOP : MNL_CB_ERROR;
+}
+
+static int mnl_cb_stop(__attribute__((unused)) const struct nlmsghdr *nlh, __attribute__((unused)) void *data)
+{
+ return MNL_CB_STOP;
+}
+
+static const mnl_cb_t default_cb_array[NLMSG_MIN_TYPE] = {
+ [NLMSG_NOOP] = mnl_cb_noop,
+ [NLMSG_ERROR] = mnl_cb_error,
+ [NLMSG_DONE] = mnl_cb_stop,
+ [NLMSG_OVERRUN] = mnl_cb_noop,
+};
+
+static int __mnl_cb_run(const void *buf, size_t numbytes,
+ unsigned int seq, unsigned int portid,
+ mnl_cb_t cb_data, void *data,
+ const mnl_cb_t *cb_ctl_array,
+ unsigned int cb_ctl_array_len)
+{
+ int ret = MNL_CB_OK, len = numbytes;
+ const struct nlmsghdr *nlh = buf;
+
+ while (mnl_nlmsg_ok(nlh, len)) {
+
+ if (!mnl_nlmsg_portid_ok(nlh, portid)) {
+ errno = ESRCH;
+ return -1;
+ }
+
+ if (!mnl_nlmsg_seq_ok(nlh, seq)) {
+ errno = EPROTO;
+ return -1;
+ }
+
+ if (nlh->nlmsg_flags & NLM_F_DUMP_INTR) {
+ errno = EINTR;
+ return -1;
+ }
+
+ if (nlh->nlmsg_type >= NLMSG_MIN_TYPE) {
+ if (cb_data){
+ ret = cb_data(nlh, data);
+ if (ret <= MNL_CB_STOP)
+ goto out;
+ }
+ } else if (nlh->nlmsg_type < cb_ctl_array_len) {
+ if (cb_ctl_array && cb_ctl_array[nlh->nlmsg_type]) {
+ ret = cb_ctl_array[nlh->nlmsg_type](nlh, data);
+ if (ret <= MNL_CB_STOP)
+ goto out;
+ }
+ } else if (default_cb_array[nlh->nlmsg_type]) {
+ ret = default_cb_array[nlh->nlmsg_type](nlh, data);
+ if (ret <= MNL_CB_STOP)
+ goto out;
+ }
+ nlh = mnl_nlmsg_next(nlh, &len);
+ }
+out:
+ return ret;
+}
+
+static int mnl_cb_run2(const void *buf, size_t numbytes, unsigned int seq,
+ unsigned int portid, mnl_cb_t cb_data, void *data,
+ const mnl_cb_t *cb_ctl_array, unsigned int cb_ctl_array_len)
+{
+ return __mnl_cb_run(buf, numbytes, seq, portid, cb_data, data,
+ cb_ctl_array, cb_ctl_array_len);
+}
+
+static int mnl_cb_run(const void *buf, size_t numbytes, unsigned int seq,
+ unsigned int portid, mnl_cb_t cb_data, void *data)
+{
+ return __mnl_cb_run(buf, numbytes, seq, portid, cb_data, data, NULL, 0);
+}
+
+struct mnl_socket {
+ int fd;
+ struct sockaddr_nl addr;
+};
+
+static unsigned int mnl_socket_get_portid(const struct mnl_socket *nl)
+{
+ return nl->addr.nl_pid;
+}
+
+static struct mnl_socket *__mnl_socket_open(int bus, int flags)
+{
+ struct mnl_socket *nl;
+
+ nl = calloc(1, sizeof(struct mnl_socket));
+ if (nl == NULL)
+ return NULL;
+
+ nl->fd = socket(AF_NETLINK, SOCK_RAW | flags, bus);
+ if (nl->fd == -1) {
+ free(nl);
+ return NULL;
+ }
+
+ return nl;
+}
+
+static struct mnl_socket *mnl_socket_open(int bus)
+{
+ return __mnl_socket_open(bus, 0);
+}
+
+static int mnl_socket_bind(struct mnl_socket *nl, unsigned int groups, pid_t pid)
+{
+ int ret;
+ socklen_t addr_len;
+
+ nl->addr.nl_family = AF_NETLINK;
+ nl->addr.nl_groups = groups;
+ nl->addr.nl_pid = pid;
+
+ ret = bind(nl->fd, (struct sockaddr *) &nl->addr, sizeof (nl->addr));
+ if (ret < 0)
+ return ret;
+
+ addr_len = sizeof(nl->addr);
+ ret = getsockname(nl->fd, (struct sockaddr *) &nl->addr, &addr_len);
+ if (ret < 0)
+ return ret;
+
+ if (addr_len != sizeof(nl->addr)) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (nl->addr.nl_family != AF_NETLINK) {
+ errno = EINVAL;
+ return -1;
+ }
+ return 0;
+}
+
+static ssize_t mnl_socket_sendto(const struct mnl_socket *nl, const void *buf,
+ size_t len)
+{
+ static const struct sockaddr_nl snl = {
+ .nl_family = AF_NETLINK
+ };
+ return sendto(nl->fd, buf, len, 0,
+ (struct sockaddr *) &snl, sizeof(snl));
+}
+
+static ssize_t mnl_socket_recvfrom(const struct mnl_socket *nl, void *buf,
+ size_t bufsiz)
+{
+ ssize_t ret;
+ struct sockaddr_nl addr;
+ struct iovec iov = {
+ .iov_base = buf,
+ .iov_len = bufsiz,
+ };
+ struct msghdr msg = {
+ .msg_name = &addr,
+ .msg_namelen = sizeof(struct sockaddr_nl),
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ .msg_control = NULL,
+ .msg_controllen = 0,
+ .msg_flags = 0,
+ };
+ ret = recvmsg(nl->fd, &msg, 0);
+ if (ret == -1)
+ return ret;
+
+ if (msg.msg_flags & MSG_TRUNC) {
+ errno = ENOSPC;
+ return -1;
+ }
+ if (msg.msg_namelen != sizeof(struct sockaddr_nl)) {
+ errno = EINVAL;
+ return -1;
+ }
+ return ret;
+}
+
+static int mnl_socket_close(struct mnl_socket *nl)
+{
+ int ret = close(nl->fd);
+ free(nl);
+ return ret;
+}
+
+/* This is a wrapper for generic netlink, originally from Jiri Pirko <jiri@mellanox.com>: */
+
+struct mnlg_socket {
+ struct mnl_socket *nl;
+ char *buf;
+ uint16_t id;
+ uint8_t version;
+ unsigned int seq;
+ unsigned int portid;
+};
+
+static struct nlmsghdr *__mnlg_msg_prepare(struct mnlg_socket *nlg, uint8_t cmd,
+ uint16_t flags, uint16_t id,
+ uint8_t version)
+{
+ struct nlmsghdr *nlh;
+ struct genlmsghdr *genl;
+
+ nlh = mnl_nlmsg_put_header(nlg->buf);
+ nlh->nlmsg_type = id;
+ nlh->nlmsg_flags = flags;
+ nlg->seq = time(NULL);
+ nlh->nlmsg_seq = nlg->seq;
+
+ genl = mnl_nlmsg_put_extra_header(nlh, sizeof(struct genlmsghdr));
+ genl->cmd = cmd;
+ genl->version = version;
+
+ return nlh;
+}
+
+static struct nlmsghdr *mnlg_msg_prepare(struct mnlg_socket *nlg, uint8_t cmd,
+ uint16_t flags)
+{
+ return __mnlg_msg_prepare(nlg, cmd, flags, nlg->id, nlg->version);
+}
+
+static int mnlg_socket_send(struct mnlg_socket *nlg, const struct nlmsghdr *nlh)
+{
+ return mnl_socket_sendto(nlg->nl, nlh, nlh->nlmsg_len);
+}
+
+static int mnlg_cb_noop(const struct nlmsghdr *nlh, void *data)
+{
+ (void)nlh;
+ (void)data;
+ return MNL_CB_OK;
+}
+
+static int mnlg_cb_error(const struct nlmsghdr *nlh, void *data)
+{
+ const struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh);
+ (void)data;
+
+ if (nlh->nlmsg_len < mnl_nlmsg_size(sizeof(struct nlmsgerr))) {
+ errno = EBADMSG;
+ return MNL_CB_ERROR;
+ }
+ /* Netlink subsystems returns the errno value with different signess */
+ if (err->error < 0)
+ errno = -err->error;
+ else
+ errno = err->error;
+
+ return err->error == 0 ? MNL_CB_STOP : MNL_CB_ERROR;
+}
+
+static int mnlg_cb_stop(const struct nlmsghdr *nlh, void *data)
+{
+ (void)data;
+ if (nlh->nlmsg_flags & NLM_F_MULTI && nlh->nlmsg_len == mnl_nlmsg_size(sizeof(int))) {
+ int error = *(int *)mnl_nlmsg_get_payload(nlh);
+ /* Netlink subsystems returns the errno value with different signess */
+ if (error < 0)
+ errno = -error;
+ else
+ errno = error;
+
+ return error == 0 ? MNL_CB_STOP : MNL_CB_ERROR;
+ }
+ return MNL_CB_STOP;
+}
+
+static const mnl_cb_t mnlg_cb_array[] = {
+ [NLMSG_NOOP] = mnlg_cb_noop,
+ [NLMSG_ERROR] = mnlg_cb_error,
+ [NLMSG_DONE] = mnlg_cb_stop,
+ [NLMSG_OVERRUN] = mnlg_cb_noop,
+};
+
+static int mnlg_socket_recv_run(struct mnlg_socket *nlg, mnl_cb_t data_cb, void *data)
+{
+ int err;
+
+ do {
+ err = mnl_socket_recvfrom(nlg->nl, nlg->buf,
+ mnl_ideal_socket_buffer_size());
+ if (err <= 0)
+ break;
+ err = mnl_cb_run2(nlg->buf, err, nlg->seq, nlg->portid,
+ data_cb, data, mnlg_cb_array, MNL_ARRAY_SIZE(mnlg_cb_array));
+ } while (err > 0);
+
+ return err;
+}
+
+static int get_family_id_attr_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, CTRL_ATTR_MAX) < 0)
+ return MNL_CB_ERROR;
+
+ if (type == CTRL_ATTR_FAMILY_ID &&
+ mnl_attr_validate(attr, MNL_TYPE_U16) < 0)
+ return MNL_CB_ERROR;
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static int get_family_id_cb(const struct nlmsghdr *nlh, void *data)
+{
+ uint16_t *p_id = data;
+ struct nlattr *tb[CTRL_ATTR_MAX + 1] = { 0 };
+
+ mnl_attr_parse(nlh, sizeof(struct genlmsghdr), get_family_id_attr_cb, tb);
+ if (!tb[CTRL_ATTR_FAMILY_ID])
+ return MNL_CB_ERROR;
+ *p_id = mnl_attr_get_u16(tb[CTRL_ATTR_FAMILY_ID]);
+ return MNL_CB_OK;
+}
+
+static struct mnlg_socket *mnlg_socket_open(const char *family_name, uint8_t version)
+{
+ struct mnlg_socket *nlg;
+ struct nlmsghdr *nlh;
+ int err;
+
+ nlg = malloc(sizeof(*nlg));
+ if (!nlg)
+ return NULL;
+ nlg->id = 0;
+
+ err = -ENOMEM;
+ nlg->buf = malloc(mnl_ideal_socket_buffer_size());
+ if (!nlg->buf)
+ goto err_buf_alloc;
+
+ nlg->nl = mnl_socket_open(NETLINK_GENERIC);
+ if (!nlg->nl) {
+ err = -errno;
+ goto err_mnl_socket_open;
+ }
+
+ if (mnl_socket_bind(nlg->nl, 0, MNL_SOCKET_AUTOPID) < 0) {
+ err = -errno;
+ goto err_mnl_socket_bind;
+ }
+
+ nlg->portid = mnl_socket_get_portid(nlg->nl);
+
+ nlh = __mnlg_msg_prepare(nlg, CTRL_CMD_GETFAMILY,
+ NLM_F_REQUEST | NLM_F_ACK, GENL_ID_CTRL, 1);
+ mnl_attr_put_strz(nlh, CTRL_ATTR_FAMILY_NAME, family_name);
+
+ if (mnlg_socket_send(nlg, nlh) < 0) {
+ err = -errno;
+ goto err_mnlg_socket_send;
+ }
+
+ errno = 0;
+ if (mnlg_socket_recv_run(nlg, get_family_id_cb, &nlg->id) < 0) {
+ errno = errno == ENOENT ? EPROTONOSUPPORT : errno;
+ err = errno ? -errno : -ENOSYS;
+ goto err_mnlg_socket_recv_run;
+ }
+
+ nlg->version = version;
+ errno = 0;
+ return nlg;
+
+err_mnlg_socket_recv_run:
+err_mnlg_socket_send:
+err_mnl_socket_bind:
+ mnl_socket_close(nlg->nl);
+err_mnl_socket_open:
+ free(nlg->buf);
+err_buf_alloc:
+ free(nlg);
+ errno = -err;
+ return NULL;
+}
+
+static void mnlg_socket_close(struct mnlg_socket *nlg)
+{
+ mnl_socket_close(nlg->nl);
+ free(nlg->buf);
+ free(nlg);
+}
diff --git a/contrib/wireguard-tools/pubkey.c b/contrib/wireguard-tools/pubkey.c
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/pubkey.c
@@ -0,0 +1,50 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+
+#include "curve25519.h"
+#include "encoding.h"
+#include "subcommands.h"
+#include "ctype.h"
+
+int pubkey_main(int argc, const char *argv[])
+{
+ uint8_t key[WG_KEY_LEN] __attribute__((aligned(sizeof(uintptr_t))));
+ char base64[WG_KEY_LEN_BASE64];
+ int trailing_char;
+
+ if (argc != 1) {
+ fprintf(stderr, "Usage: %s %s\n", PROG_NAME, argv[0]);
+ return 1;
+ }
+
+ if (fread(base64, 1, sizeof(base64) - 1, stdin) != sizeof(base64) - 1) {
+ errno = EINVAL;
+ fprintf(stderr, "%s: Key is not the correct length or format\n", PROG_NAME);
+ return 1;
+ }
+ base64[WG_KEY_LEN_BASE64 - 1] = '\0';
+
+ for (;;) {
+ trailing_char = getc(stdin);
+ if (!trailing_char || char_is_space(trailing_char))
+ continue;
+ if (trailing_char == EOF)
+ break;
+ fprintf(stderr, "%s: Trailing characters found after key\n", PROG_NAME);
+ return 1;
+ }
+
+ if (!key_from_base64(key, base64)) {
+ fprintf(stderr, "%s: Key is not the correct length or format\n", PROG_NAME);
+ return 1;
+ }
+ curve25519_generate_public(key, key);
+ key_to_base64(base64, key);
+ puts(base64);
+ return 0;
+}
diff --git a/contrib/wireguard-tools/set.c b/contrib/wireguard-tools/set.c
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/set.c
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "containers.h"
+#include "config.h"
+#include "ipc.h"
+#include "subcommands.h"
+
+int set_main(int argc, const char *argv[])
+{
+ struct wgdevice *device = NULL;
+ int ret = 1;
+
+ if (argc < 3) {
+ fprintf(stderr, "Usage: %s %s <interface> [listen-port <port>] [fwmark <mark>] [private-key <file path>] [peer <base64 public key> [remove] [preshared-key <file path>] [endpoint <ip>:<port>] [persistent-keepalive <interval seconds>] [allowed-ips <ip1>/<cidr1>[,<ip2>/<cidr2>]...] ]...\n", PROG_NAME, argv[0]);
+ return 1;
+ }
+
+ device = config_read_cmd(argv + 2, argc - 2);
+ if (!device)
+ goto cleanup;
+ strncpy(device->name, argv[1], IFNAMSIZ - 1);
+ device->name[IFNAMSIZ - 1] = '\0';
+
+ if (ipc_set_device(device) != 0) {
+ perror("Unable to modify interface");
+ goto cleanup;
+ }
+
+ ret = 0;
+
+cleanup:
+ free_wgdevice(device);
+ return ret;
+}
diff --git a/contrib/wireguard-tools/setconf.c b/contrib/wireguard-tools/setconf.c
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/setconf.c
@@ -0,0 +1,156 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "containers.h"
+#include "config.h"
+#include "ipc.h"
+#include "subcommands.h"
+
+struct pubkey_origin {
+ uint8_t *pubkey;
+ bool from_file;
+};
+
+static int pubkey_cmp(const void *first, const void *second)
+{
+ const struct pubkey_origin *a = first, *b = second;
+ int ret = memcmp(a->pubkey, b->pubkey, WG_KEY_LEN);
+ if (ret)
+ return ret;
+ return a->from_file - b->from_file;
+}
+
+static bool sync_conf(struct wgdevice *file)
+{
+ struct wgdevice *runtime;
+ struct wgpeer *peer;
+ struct pubkey_origin *pubkeys;
+ size_t peer_count = 0, i = 0;
+
+ if (!file->first_peer)
+ return true;
+
+ for_each_wgpeer(file, peer)
+ ++peer_count;
+
+ if (ipc_get_device(&runtime, file->name) != 0) {
+ perror("Unable to retrieve current interface configuration");
+ return false;
+ }
+
+ if (!runtime->first_peer) {
+ free_wgdevice(runtime);
+ return true;
+ }
+
+ file->flags &= ~WGDEVICE_REPLACE_PEERS;
+
+ for_each_wgpeer(runtime, peer)
+ ++peer_count;
+
+ pubkeys = calloc(peer_count, sizeof(*pubkeys));
+ if (!pubkeys) {
+ free_wgdevice(runtime);
+ perror("Public key allocation");
+ return false;
+ }
+
+ for_each_wgpeer(file, peer) {
+ pubkeys[i].pubkey = peer->public_key;
+ pubkeys[i].from_file = true;
+ ++i;
+ }
+ for_each_wgpeer(runtime, peer) {
+ pubkeys[i].pubkey = peer->public_key;
+ pubkeys[i].from_file = false;
+ ++i;
+ }
+ qsort(pubkeys, peer_count, sizeof(*pubkeys), pubkey_cmp);
+
+ for (i = 0; i < peer_count; ++i) {
+ if (pubkeys[i].from_file)
+ continue;
+ if (i == peer_count - 1 || !pubkeys[i + 1].from_file || memcmp(pubkeys[i].pubkey, pubkeys[i + 1].pubkey, WG_KEY_LEN)) {
+ peer = calloc(1, sizeof(struct wgpeer));
+ if (!peer) {
+ free_wgdevice(runtime);
+ free(pubkeys);
+ perror("Peer allocation");
+ return false;
+ }
+ peer->flags = WGPEER_REMOVE_ME;
+ memcpy(peer->public_key, pubkeys[i].pubkey, WG_KEY_LEN);
+ peer->next_peer = file->first_peer;
+ file->first_peer = peer;
+ if (!file->last_peer)
+ file->last_peer = peer;
+ }
+ }
+ free_wgdevice(runtime);
+ free(pubkeys);
+ return true;
+}
+
+int setconf_main(int argc, const char *argv[])
+{
+ struct wgdevice *device = NULL;
+ struct config_ctx ctx;
+ FILE *config_input = NULL;
+ char *config_buffer = NULL;
+ size_t config_buffer_len = 0;
+ int ret = 1;
+
+ if (argc != 3) {
+ fprintf(stderr, "Usage: %s %s <interface> <configuration filename>\n", PROG_NAME, argv[0]);
+ return 1;
+ }
+
+ config_input = fopen(argv[2], "r");
+ if (!config_input) {
+ perror("fopen");
+ return 1;
+ }
+ if (!config_read_init(&ctx, !strcmp(argv[0], "addconf"))) {
+ fclose(config_input);
+ return 1;
+ }
+ while (getline(&config_buffer, &config_buffer_len, config_input) >= 0) {
+ if (!config_read_line(&ctx, config_buffer)) {
+ fprintf(stderr, "Configuration parsing error\n");
+ goto cleanup;
+ }
+ }
+ device = config_read_finish(&ctx);
+ if (!device) {
+ fprintf(stderr, "Invalid configuration\n");
+ goto cleanup;
+ }
+ strncpy(device->name, argv[1], IFNAMSIZ - 1);
+ device->name[IFNAMSIZ - 1] = '\0';
+
+ if (!strcmp(argv[0], "syncconf")) {
+ if (!sync_conf(device))
+ goto cleanup;
+ }
+
+ if (ipc_set_device(device) != 0) {
+ perror("Unable to modify interface");
+ goto cleanup;
+ }
+
+ ret = 0;
+
+cleanup:
+ if (config_input)
+ fclose(config_input);
+ free(config_buffer);
+ free_wgdevice(device);
+ return ret;
+}
diff --git a/contrib/wireguard-tools/show.c b/contrib/wireguard-tools/show.c
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/show.c
@@ -0,0 +1,454 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#include <arpa/inet.h>
+#include <inttypes.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <netdb.h>
+
+#include "containers.h"
+#include "ipc.h"
+#include "terminal.h"
+#include "encoding.h"
+#include "subcommands.h"
+
+static int peer_cmp(const void *first, const void *second)
+{
+ time_t diff;
+ const struct wgpeer *a = *(const void **)first, *b = *(const void **)second;
+
+ if (!a->last_handshake_time.tv_sec && !a->last_handshake_time.tv_nsec && (b->last_handshake_time.tv_sec || b->last_handshake_time.tv_nsec))
+ return 1;
+ if (!b->last_handshake_time.tv_sec && !b->last_handshake_time.tv_nsec && (a->last_handshake_time.tv_sec || a->last_handshake_time.tv_nsec))
+ return -1;
+ diff = a->last_handshake_time.tv_sec - b->last_handshake_time.tv_sec;
+ if (!diff)
+ diff = a->last_handshake_time.tv_nsec - b->last_handshake_time.tv_nsec;
+ if (diff < 0)
+ return 1;
+ if (diff > 0)
+ return -1;
+ return 0;
+}
+
+/* This, hilariously, is not the right way to sort a linked list... */
+static void sort_peers(struct wgdevice *device)
+{
+ size_t peer_count = 0, i = 0;
+ struct wgpeer *peer, **peers;
+
+ for_each_wgpeer(device, peer)
+ ++peer_count;
+ if (!peer_count)
+ return;
+ peers = calloc(peer_count, sizeof(*peers));
+ if (!peers)
+ return;
+ for_each_wgpeer(device, peer)
+ peers[i++] = peer;
+ qsort(peers, peer_count, sizeof(*peers), peer_cmp);
+ device->first_peer = peers[0];
+ for (i = 1; i < peer_count; ++i) {
+ peers[i - 1]->next_peer = peers[i];
+ }
+ peers[peer_count - 1]->next_peer = NULL;
+ free(peers);
+}
+
+static char *key(const uint8_t key[static WG_KEY_LEN])
+{
+ static char base64[WG_KEY_LEN_BASE64];
+
+ key_to_base64(base64, key);
+ return base64;
+}
+
+static const char *maybe_key(const uint8_t maybe_key[static WG_KEY_LEN], bool have_it)
+{
+ if (!have_it)
+ return "(none)";
+ return key(maybe_key);
+}
+
+static const char *masked_key(const uint8_t masked_key[static WG_KEY_LEN])
+{
+ const char *var = getenv("WG_HIDE_KEYS");
+
+ if (var && !strcmp(var, "never"))
+ return key(masked_key);
+ return "(hidden)";
+}
+
+static char *ip(const struct wgallowedip *ip)
+{
+ static char buf[INET6_ADDRSTRLEN + 1];
+
+ memset(buf, 0, INET6_ADDRSTRLEN + 1);
+ if (ip->family == AF_INET)
+ inet_ntop(AF_INET, &ip->ip4, buf, INET6_ADDRSTRLEN);
+ else if (ip->family == AF_INET6)
+ inet_ntop(AF_INET6, &ip->ip6, buf, INET6_ADDRSTRLEN);
+ return buf;
+}
+
+static char *endpoint(const struct sockaddr *addr)
+{
+ char host[4096 + 1];
+ char service[512 + 1];
+ static char buf[sizeof(host) + sizeof(service) + 4];
+ int ret;
+ socklen_t addr_len = 0;
+
+ memset(buf, 0, sizeof(buf));
+ if (addr->sa_family == AF_INET)
+ addr_len = sizeof(struct sockaddr_in);
+ else if (addr->sa_family == AF_INET6)
+ addr_len = sizeof(struct sockaddr_in6);
+
+ ret = getnameinfo(addr, addr_len, host, sizeof(host), service, sizeof(service), NI_DGRAM | NI_NUMERICSERV | NI_NUMERICHOST);
+ if (ret) {
+ strncpy(buf, gai_strerror(ret), sizeof(buf) - 1);
+ buf[sizeof(buf) - 1] = '\0';
+ } else
+ snprintf(buf, sizeof(buf), (addr->sa_family == AF_INET6 && strchr(host, ':')) ? "[%s]:%s" : "%s:%s", host, service);
+ return buf;
+}
+
+static size_t pretty_time(char *buf, const size_t len, unsigned long long left)
+{
+ size_t offset = 0;
+ unsigned long long years, days, hours, minutes, seconds;
+
+ years = left / (365 * 24 * 60 * 60);
+ left = left % (365 * 24 * 60 * 60);
+ days = left / (24 * 60 * 60);
+ left = left % (24 * 60 * 60);
+ hours = left / (60 * 60);
+ left = left % (60 * 60);
+ minutes = left / 60;
+ seconds = left % 60;
+
+ if (years)
+ offset += snprintf(buf + offset, len - offset, "%s%llu " TERMINAL_FG_CYAN "year%s" TERMINAL_RESET, offset ? ", " : "", years, years == 1 ? "" : "s");
+ if (days)
+ offset += snprintf(buf + offset, len - offset, "%s%llu " TERMINAL_FG_CYAN "day%s" TERMINAL_RESET, offset ? ", " : "", days, days == 1 ? "" : "s");
+ if (hours)
+ offset += snprintf(buf + offset, len - offset, "%s%llu " TERMINAL_FG_CYAN "hour%s" TERMINAL_RESET, offset ? ", " : "", hours, hours == 1 ? "" : "s");
+ if (minutes)
+ offset += snprintf(buf + offset, len - offset, "%s%llu " TERMINAL_FG_CYAN "minute%s" TERMINAL_RESET, offset ? ", " : "", minutes, minutes == 1 ? "" : "s");
+ if (seconds)
+ offset += snprintf(buf + offset, len - offset, "%s%llu " TERMINAL_FG_CYAN "second%s" TERMINAL_RESET, offset ? ", " : "", seconds, seconds == 1 ? "" : "s");
+
+ return offset;
+}
+
+static char *ago(const struct timespec64 *t)
+{
+ static char buf[1024];
+ size_t offset;
+ time_t now = time(NULL);
+
+ if (now == t->tv_sec)
+ strncpy(buf, "Now", sizeof(buf) - 1);
+ else if (now < t->tv_sec)
+ strncpy(buf, "(" TERMINAL_FG_RED "System clock wound backward; connection problems may ensue." TERMINAL_RESET ")", sizeof(buf) - 1);
+ else {
+ offset = pretty_time(buf, sizeof(buf), now - t->tv_sec);
+ strncpy(buf + offset, " ago", sizeof(buf) - offset - 1);
+ }
+ buf[sizeof(buf) - 1] = '\0';
+
+ return buf;
+}
+
+static char *every(uint16_t seconds)
+{
+ static char buf[1024] = "every ";
+
+ pretty_time(buf + strlen("every "), sizeof(buf) - strlen("every ") - 1, seconds);
+ return buf;
+}
+
+static char *bytes(uint64_t b)
+{
+ static char buf[1024];
+
+ if (b < 1024ULL)
+ snprintf(buf, sizeof(buf), "%u " TERMINAL_FG_CYAN "B" TERMINAL_RESET, (unsigned int)b);
+ else if (b < 1024ULL * 1024ULL)
+ snprintf(buf, sizeof(buf), "%.2f " TERMINAL_FG_CYAN "KiB" TERMINAL_RESET, (double)b / 1024);
+ else if (b < 1024ULL * 1024ULL * 1024ULL)
+ snprintf(buf, sizeof(buf), "%.2f " TERMINAL_FG_CYAN "MiB" TERMINAL_RESET, (double)b / (1024 * 1024));
+ else if (b < 1024ULL * 1024ULL * 1024ULL * 1024ULL)
+ snprintf(buf, sizeof(buf), "%.2f " TERMINAL_FG_CYAN "GiB" TERMINAL_RESET, (double)b / (1024 * 1024 * 1024));
+ else
+ snprintf(buf, sizeof(buf), "%.2f " TERMINAL_FG_CYAN "TiB" TERMINAL_RESET, (double)b / (1024 * 1024 * 1024) / 1024);
+
+ return buf;
+}
+
+static const char *COMMAND_NAME;
+static void show_usage(void)
+{
+ fprintf(stderr, "Usage: %s %s { <interface> | all | interfaces } [public-key | private-key | listen-port | fwmark | peers | preshared-keys | endpoints | allowed-ips | latest-handshakes | transfer | persistent-keepalive | dump]\n", PROG_NAME, COMMAND_NAME);
+}
+
+static void pretty_print(struct wgdevice *device)
+{
+ struct wgpeer *peer;
+ struct wgallowedip *allowedip;
+
+ terminal_printf(TERMINAL_RESET);
+ terminal_printf(TERMINAL_FG_GREEN TERMINAL_BOLD "interface" TERMINAL_RESET ": " TERMINAL_FG_GREEN "%s" TERMINAL_RESET "\n", device->name);
+ if (device->flags & WGDEVICE_HAS_PUBLIC_KEY)
+ terminal_printf(" " TERMINAL_BOLD "public key" TERMINAL_RESET ": %s\n", key(device->public_key));
+ if (device->flags & WGDEVICE_HAS_PRIVATE_KEY)
+ terminal_printf(" " TERMINAL_BOLD "private key" TERMINAL_RESET ": %s\n", masked_key(device->private_key));
+ if (device->listen_port)
+ terminal_printf(" " TERMINAL_BOLD "listening port" TERMINAL_RESET ": %u\n", device->listen_port);
+ if (device->fwmark)
+ terminal_printf(" " TERMINAL_BOLD "fwmark" TERMINAL_RESET ": 0x%x\n", device->fwmark);
+ if (device->first_peer) {
+ sort_peers(device);
+ terminal_printf("\n");
+ }
+ for_each_wgpeer(device, peer) {
+ terminal_printf(TERMINAL_FG_YELLOW TERMINAL_BOLD "peer" TERMINAL_RESET ": " TERMINAL_FG_YELLOW "%s" TERMINAL_RESET "\n", key(peer->public_key));
+ if (peer->flags & WGPEER_HAS_PRESHARED_KEY)
+ terminal_printf(" " TERMINAL_BOLD "preshared key" TERMINAL_RESET ": %s\n", masked_key(peer->preshared_key));
+ if (peer->endpoint.addr.sa_family == AF_INET || peer->endpoint.addr.sa_family == AF_INET6)
+ terminal_printf(" " TERMINAL_BOLD "endpoint" TERMINAL_RESET ": %s\n", endpoint(&peer->endpoint.addr));
+ terminal_printf(" " TERMINAL_BOLD "allowed ips" TERMINAL_RESET ": ");
+ if (peer->first_allowedip) {
+ for_each_wgallowedip(peer, allowedip)
+ terminal_printf("%s" TERMINAL_FG_CYAN "/" TERMINAL_RESET "%u%s", ip(allowedip), allowedip->cidr, allowedip->next_allowedip ? ", " : "\n");
+ } else
+ terminal_printf("(none)\n");
+ if (peer->last_handshake_time.tv_sec)
+ terminal_printf(" " TERMINAL_BOLD "latest handshake" TERMINAL_RESET ": %s\n", ago(&peer->last_handshake_time));
+ if (peer->rx_bytes || peer->tx_bytes) {
+ terminal_printf(" " TERMINAL_BOLD "transfer" TERMINAL_RESET ": ");
+ terminal_printf("%s received, ", bytes(peer->rx_bytes));
+ terminal_printf("%s sent\n", bytes(peer->tx_bytes));
+ }
+ if (peer->persistent_keepalive_interval)
+ terminal_printf(" " TERMINAL_BOLD "persistent keepalive" TERMINAL_RESET ": %s\n", every(peer->persistent_keepalive_interval));
+ if (peer->next_peer)
+ terminal_printf("\n");
+ }
+}
+
+static void dump_print(struct wgdevice *device, bool with_interface)
+{
+ struct wgpeer *peer;
+ struct wgallowedip *allowedip;
+
+ if (with_interface)
+ printf("%s\t", device->name);
+ printf("%s\t", maybe_key(device->private_key, device->flags & WGDEVICE_HAS_PRIVATE_KEY));
+ printf("%s\t", maybe_key(device->public_key, device->flags & WGDEVICE_HAS_PUBLIC_KEY));
+ printf("%u\t", device->listen_port);
+ if (device->fwmark)
+ printf("0x%x\n", device->fwmark);
+ else
+ printf("off\n");
+ for_each_wgpeer(device, peer) {
+ if (with_interface)
+ printf("%s\t", device->name);
+ printf("%s\t", key(peer->public_key));
+ printf("%s\t", maybe_key(peer->preshared_key, peer->flags & WGPEER_HAS_PRESHARED_KEY));
+ if (peer->endpoint.addr.sa_family == AF_INET || peer->endpoint.addr.sa_family == AF_INET6)
+ printf("%s\t", endpoint(&peer->endpoint.addr));
+ else
+ printf("(none)\t");
+ if (peer->first_allowedip) {
+ for_each_wgallowedip(peer, allowedip)
+ printf("%s/%u%c", ip(allowedip), allowedip->cidr, allowedip->next_allowedip ? ',' : '\t');
+ } else
+ printf("(none)\t");
+ printf("%llu\t", (unsigned long long)peer->last_handshake_time.tv_sec);
+ printf("%" PRIu64 "\t%" PRIu64 "\t", (uint64_t)peer->rx_bytes, (uint64_t)peer->tx_bytes);
+ if (peer->persistent_keepalive_interval)
+ printf("%u\n", peer->persistent_keepalive_interval);
+ else
+ printf("off\n");
+ }
+}
+
+static bool ugly_print(struct wgdevice *device, const char *param, bool with_interface)
+{
+ struct wgpeer *peer;
+ struct wgallowedip *allowedip;
+
+ if (!strcmp(param, "public-key")) {
+ if (with_interface)
+ printf("%s\t", device->name);
+ printf("%s\n", maybe_key(device->public_key, device->flags & WGDEVICE_HAS_PUBLIC_KEY));
+ } else if (!strcmp(param, "private-key")) {
+ if (with_interface)
+ printf("%s\t", device->name);
+ printf("%s\n", maybe_key(device->private_key, device->flags & WGDEVICE_HAS_PRIVATE_KEY));
+ } else if (!strcmp(param, "listen-port")) {
+ if (with_interface)
+ printf("%s\t", device->name);
+ printf("%u\n", device->listen_port);
+ } else if (!strcmp(param, "fwmark")) {
+ if (with_interface)
+ printf("%s\t", device->name);
+ if (device->fwmark)
+ printf("0x%x\n", device->fwmark);
+ else
+ printf("off\n");
+ } else if (!strcmp(param, "endpoints")) {
+ if (with_interface)
+ printf("%s\t", device->name);
+ for_each_wgpeer(device, peer) {
+ printf("%s\t", key(peer->public_key));
+ if (peer->endpoint.addr.sa_family == AF_INET || peer->endpoint.addr.sa_family == AF_INET6)
+ printf("%s\n", endpoint(&peer->endpoint.addr));
+ else
+ printf("(none)\n");
+ }
+ } else if (!strcmp(param, "allowed-ips")) {
+ for_each_wgpeer(device, peer) {
+ if (with_interface)
+ printf("%s\t", device->name);
+ printf("%s\t", key(peer->public_key));
+ if (peer->first_allowedip) {
+ for_each_wgallowedip(peer, allowedip)
+ printf("%s/%u%c", ip(allowedip), allowedip->cidr, allowedip->next_allowedip ? ' ' : '\n');
+ } else
+ printf("(none)\n");
+ }
+ } else if (!strcmp(param, "latest-handshakes")) {
+ for_each_wgpeer(device, peer) {
+ if (with_interface)
+ printf("%s\t", device->name);
+ printf("%s\t%llu\n", key(peer->public_key), (unsigned long long)peer->last_handshake_time.tv_sec);
+ }
+ } else if (!strcmp(param, "transfer")) {
+ for_each_wgpeer(device, peer) {
+ if (with_interface)
+ printf("%s\t", device->name);
+ printf("%s\t%" PRIu64 "\t%" PRIu64 "\n", key(peer->public_key), (uint64_t)peer->rx_bytes, (uint64_t)peer->tx_bytes);
+ }
+ } else if (!strcmp(param, "persistent-keepalive")) {
+ for_each_wgpeer(device, peer) {
+ if (with_interface)
+ printf("%s\t", device->name);
+ if (peer->persistent_keepalive_interval)
+ printf("%s\t%u\n", key(peer->public_key), peer->persistent_keepalive_interval);
+ else
+ printf("%s\toff\n", key(peer->public_key));
+ }
+ } else if (!strcmp(param, "preshared-keys")) {
+ for_each_wgpeer(device, peer) {
+ if (with_interface)
+ printf("%s\t", device->name);
+ printf("%s\t", key(peer->public_key));
+ printf("%s\n", maybe_key(peer->preshared_key, peer->flags & WGPEER_HAS_PRESHARED_KEY));
+ }
+ } else if (!strcmp(param, "peers")) {
+ for_each_wgpeer(device, peer) {
+ if (with_interface)
+ printf("%s\t", device->name);
+ printf("%s\n", key(peer->public_key));
+ }
+ } else if (!strcmp(param, "dump"))
+ dump_print(device, with_interface);
+ else {
+ fprintf(stderr, "Invalid parameter: `%s'\n", param);
+ show_usage();
+ return false;
+ }
+ return true;
+}
+
+int show_main(int argc, const char *argv[])
+{
+ int ret = 0;
+
+ COMMAND_NAME = argv[0];
+
+ if (argc > 3) {
+ show_usage();
+ return 1;
+ }
+
+ if (argc == 1 || !strcmp(argv[1], "all")) {
+ char *interfaces = ipc_list_devices(), *interface;
+
+ if (!interfaces) {
+ perror("Unable to list interfaces");
+ return 1;
+ }
+ ret = !!*interfaces;
+ interface = interfaces;
+ for (size_t len = 0; (len = strlen(interface)); interface += len + 1) {
+ struct wgdevice *device = NULL;
+
+ if (ipc_get_device(&device, interface) < 0) {
+ fprintf(stderr, "Unable to access interface %s: %s\n", interface, strerror(errno));
+ continue;
+ }
+ if (argc == 3) {
+ if (!ugly_print(device, argv[2], true)) {
+ ret = 1;
+ free_wgdevice(device);
+ break;
+ }
+ } else {
+ pretty_print(device);
+ if (strlen(interface + len + 1))
+ printf("\n");
+ }
+ free_wgdevice(device);
+ ret = 0;
+ }
+ free(interfaces);
+ } else if (!strcmp(argv[1], "interfaces")) {
+ char *interfaces, *interface;
+
+ if (argc > 2) {
+ show_usage();
+ return 1;
+ }
+ interfaces = ipc_list_devices();
+ if (!interfaces) {
+ perror("Unable to list interfaces");
+ return 1;
+ }
+ interface = interfaces;
+ for (size_t len = 0; (len = strlen(interface)); interface += len + 1)
+ printf("%s%c", interface, strlen(interface + len + 1) ? ' ' : '\n');
+ free(interfaces);
+ } else if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help") || !strcmp(argv[1], "help")))
+ show_usage();
+ else {
+ struct wgdevice *device = NULL;
+
+ if (ipc_get_device(&device, argv[1]) < 0) {
+ perror("Unable to access interface");
+ return 1;
+ }
+ if (argc == 3) {
+ if (!ugly_print(device, argv[2], false))
+ ret = 1;
+ } else
+ pretty_print(device);
+ free_wgdevice(device);
+ }
+ return ret;
+}
diff --git a/contrib/wireguard-tools/showconf.c b/contrib/wireguard-tools/showconf.c
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/showconf.c
@@ -0,0 +1,103 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <netdb.h>
+
+#include "containers.h"
+#include "encoding.h"
+#include "ipc.h"
+#include "subcommands.h"
+
+int showconf_main(int argc, const char *argv[])
+{
+ char base64[WG_KEY_LEN_BASE64];
+ char ip[INET6_ADDRSTRLEN];
+ struct wgdevice *device = NULL;
+ struct wgpeer *peer;
+ struct wgallowedip *allowedip;
+ int ret = 1;
+
+ if (argc != 2) {
+ fprintf(stderr, "Usage: %s %s <interface>\n", PROG_NAME, argv[0]);
+ return 1;
+ }
+
+ if (ipc_get_device(&device, argv[1])) {
+ perror("Unable to access interface");
+ goto cleanup;
+ }
+
+ printf("[Interface]\n");
+ if (device->listen_port)
+ printf("ListenPort = %u\n", device->listen_port);
+ if (device->fwmark)
+ printf("FwMark = 0x%x\n", device->fwmark);
+ if (device->flags & WGDEVICE_HAS_PRIVATE_KEY) {
+ key_to_base64(base64, device->private_key);
+ printf("PrivateKey = %s\n", base64);
+ }
+ printf("\n");
+ for_each_wgpeer(device, peer) {
+ key_to_base64(base64, peer->public_key);
+ printf("[Peer]\nPublicKey = %s\n", base64);
+ if (peer->flags & WGPEER_HAS_PRESHARED_KEY) {
+ key_to_base64(base64, peer->preshared_key);
+ printf("PresharedKey = %s\n", base64);
+ }
+ if (peer->first_allowedip)
+ printf("AllowedIPs = ");
+ for_each_wgallowedip(peer, allowedip) {
+ if (allowedip->family == AF_INET) {
+ if (!inet_ntop(AF_INET, &allowedip->ip4, ip, INET6_ADDRSTRLEN))
+ continue;
+ } else if (allowedip->family == AF_INET6) {
+ if (!inet_ntop(AF_INET6, &allowedip->ip6, ip, INET6_ADDRSTRLEN))
+ continue;
+ } else
+ continue;
+ printf("%s/%d", ip, allowedip->cidr);
+ if (allowedip->next_allowedip)
+ printf(", ");
+ }
+ if (peer->first_allowedip)
+ printf("\n");
+
+ if (peer->endpoint.addr.sa_family == AF_INET || peer->endpoint.addr.sa_family == AF_INET6) {
+ char host[4096 + 1];
+ char service[512 + 1];
+ socklen_t addr_len = 0;
+
+ if (peer->endpoint.addr.sa_family == AF_INET)
+ addr_len = sizeof(struct sockaddr_in);
+ else if (peer->endpoint.addr.sa_family == AF_INET6)
+ addr_len = sizeof(struct sockaddr_in6);
+ if (!getnameinfo(&peer->endpoint.addr, addr_len, host, sizeof(host), service, sizeof(service), NI_DGRAM | NI_NUMERICSERV | NI_NUMERICHOST)) {
+ if (peer->endpoint.addr.sa_family == AF_INET6 && strchr(host, ':'))
+ printf("Endpoint = [%s]:%s\n", host, service);
+ else
+ printf("Endpoint = %s:%s\n", host, service);
+ }
+ }
+
+ if (peer->persistent_keepalive_interval)
+ printf("PersistentKeepalive = %u\n", peer->persistent_keepalive_interval);
+
+ if (peer->next_peer)
+ printf("\n");
+ }
+ ret = 0;
+
+cleanup:
+ free_wgdevice(device);
+ return ret;
+}
diff --git a/contrib/wireguard-tools/subcommands.h b/contrib/wireguard-tools/subcommands.h
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/subcommands.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0 OR MIT */
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#ifndef SUBCOMMANDS_H
+#define SUBCOMMANDS_H
+
+extern const char *PROG_NAME;
+int show_main(int argc, const char *argv[]);
+int showconf_main(int argc, const char *argv[]);
+int set_main(int argc, const char *argv[]);
+int setconf_main(int argc, const char *argv[]);
+int genkey_main(int argc, const char *argv[]);
+int pubkey_main(int argc, const char *argv[]);
+
+#endif
diff --git a/contrib/wireguard-tools/systemd/wg-quick.target b/contrib/wireguard-tools/systemd/wg-quick.target
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/systemd/wg-quick.target
@@ -0,0 +1,2 @@
+[Unit]
+Description=WireGuard Tunnels via wg-quick(8)
diff --git a/contrib/wireguard-tools/systemd/wg-quick@.service b/contrib/wireguard-tools/systemd/wg-quick@.service
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/systemd/wg-quick@.service
@@ -0,0 +1,22 @@
+[Unit]
+Description=WireGuard via wg-quick(8) for %I
+After=network-online.target nss-lookup.target
+Wants=network-online.target nss-lookup.target
+PartOf=wg-quick.target
+Documentation=man:wg-quick(8)
+Documentation=man:wg(8)
+Documentation=https://www.wireguard.com/
+Documentation=https://www.wireguard.com/quickstart/
+Documentation=https://git.zx2c4.com/wireguard-tools/about/src/man/wg-quick.8
+Documentation=https://git.zx2c4.com/wireguard-tools/about/src/man/wg.8
+
+[Service]
+Type=oneshot
+RemainAfterExit=yes
+ExecStart=/usr/bin/wg-quick up %i
+ExecStop=/usr/bin/wg-quick down %i
+ExecReload=/bin/bash -c 'exec /usr/bin/wg syncconf %i <(exec /usr/bin/wg-quick strip %i)'
+Environment=WG_ENDPOINT_RESOLUTION_RETRIES=infinity
+
+[Install]
+WantedBy=multi-user.target
diff --git a/contrib/wireguard-tools/terminal.h b/contrib/wireguard-tools/terminal.h
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/terminal.h
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: GPL-2.0 OR MIT */
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#ifndef TERMINAL_H
+#define TERMINAL_H
+
+#define TERMINAL_FG_BLACK "\x1b[30m"
+#define TERMINAL_FG_RED "\x1b[31m"
+#define TERMINAL_FG_GREEN "\x1b[32m"
+#define TERMINAL_FG_YELLOW "\x1b[33m"
+#define TERMINAL_FG_BLUE "\x1b[34m"
+#define TERMINAL_FG_MAGENTA "\x1b[35m"
+#define TERMINAL_FG_CYAN "\x1b[36m"
+#define TERMINAL_FG_WHITE "\x1b[37m"
+#define TERMINAL_FG_DEFAULT "\x1b[39m"
+
+#define TERMINAL_BG_BLACK "\x1b[40m"
+#define TERMINAL_BG_RED "\x1b[41m"
+#define TERMINAL_BG_GREEN "\x1b[42m"
+#define TERMINAL_BG_YELLOW "\x1b[43m"
+#define TERMINAL_BG_BLUE "\x1b[44m"
+#define TERMINAL_BG_MAGENTA "\x1b[45m"
+#define TERMINAL_BG_CYAN "\x1b[46m"
+#define TERMINAL_BG_WHITE "\x1b[47m"
+#define TERMINAL_BG_DEFAULT "\x1b[49m"
+
+#define TERMINAL_BOLD "\x1b[1m"
+#define TERMINAL_NO_BOLD "\x1b[22m"
+#define TERMINAL_UNDERLINE "\x1b[4m"
+#define TERMINAL_NO_UNDERLINE "\x1b[24m"
+
+#define TERMINAL_RESET "\x1b[0m"
+
+#define TERMINAL_SAVE_CURSOR "\x1b[s"
+#define TERMINAL_RESTORE_CURSOR "\x1b[u"
+#define TERMINAL_UP_CURSOR(l) "\x1b[" #l "A"
+#define TERMINAL_DOWN_CURSOR(l) "\x1b[" #l "B"
+#define TERMINAL_RIGHT_CURSOR(c) "\x1b[" #c "C"
+#define TERMINAL_LEFT_CURSOR(c) "\x1b[" #c "D"
+#define TERMINAL_CLEAR_DOWN "\x1b[0J"
+#define TERMINAL_CLEAR_UP "\x1b[1J"
+#define TERMINAL_CLEAR_RIGHT "\x1b[0K"
+#define TERMINAL_CLEAR_LEFT "\x1b[1K"
+#define TERMINAL_CLEAR_LINE "\x1b[2K"
+#define TERMINAL_CLEAR_ALL "\x1b[2J"
+
+void terminal_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
+
+#endif
diff --git a/contrib/wireguard-tools/terminal.c b/contrib/wireguard-tools/terminal.c
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/terminal.c
@@ -0,0 +1,76 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include "ctype.h"
+#include "terminal.h"
+
+static bool color_mode(void)
+{
+ static int mode = -1;
+ const char *var;
+
+ if (mode != -1)
+ return mode;
+ var = getenv("WG_COLOR_MODE");
+ if (var && !strcmp(var, "always"))
+ mode = true;
+ else if (var && !strcmp(var, "never"))
+ mode = false;
+ else
+ mode = isatty(fileno(stdout));
+ return mode;
+}
+
+static void filter_ansi(const char *fmt, va_list args)
+{
+ char *str = NULL;
+ size_t len, i, j;
+
+ if (color_mode()) {
+ vfprintf(stdout, fmt, args);
+ return;
+ }
+
+ len = vasprintf(&str, fmt, args);
+
+ if (len >= 2) {
+ for (i = 0; i < len - 2; ++i) {
+ if (str[i] == '\x1b' && str[i + 1] == '[') {
+ str[i] = str[i + 1] = '\0';
+ for (j = i + 2; j < len; ++j) {
+ if (char_is_alpha(str[j]))
+ break;
+ str[j] = '\0';
+ }
+ str[j] = '\0';
+ }
+ }
+ }
+ for (i = 0; i < len; i = j) {
+ fputs(&str[i], stdout);
+ for (j = i + strlen(&str[i]); j < len; ++j) {
+ if (str[j] != '\0')
+ break;
+ }
+ }
+
+ free(str);
+}
+
+void terminal_printf(const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ filter_ansi(fmt, args);
+ va_end(args);
+}
diff --git a/contrib/wireguard-tools/uapi/freebsd/dev/if_wg/if_wg.h b/contrib/wireguard-tools/uapi/freebsd/dev/if_wg/if_wg.h
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/uapi/freebsd/dev/if_wg/if_wg.h
@@ -0,0 +1,16 @@
+#ifndef __IF_WG_H__
+#define __IF_WG_H__
+
+#include <net/if.h>
+#include <netinet/in.h>
+
+struct wg_data_io {
+ char wgd_name[IFNAMSIZ];
+ void *wgd_data;
+ size_t wgd_size;
+};
+
+#define SIOCSWG _IOWR('i', 210, struct wg_data_io)
+#define SIOCGWG _IOWR('i', 211, struct wg_data_io)
+
+#endif
diff --git a/contrib/wireguard-tools/uapi/linux/linux/wireguard.h b/contrib/wireguard-tools/uapi/linux/linux/wireguard.h
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/uapi/linux/linux/wireguard.h
@@ -0,0 +1,196 @@
+/* SPDX-License-Identifier: (GPL-2.0 WITH Linux-syscall-note) OR MIT */
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ *
+ * Documentation
+ * =============
+ *
+ * The below enums and macros are for interfacing with WireGuard, using generic
+ * netlink, with family WG_GENL_NAME and version WG_GENL_VERSION. It defines two
+ * methods: get and set. Note that while they share many common attributes,
+ * these two functions actually accept a slightly different set of inputs and
+ * outputs.
+ *
+ * WG_CMD_GET_DEVICE
+ * -----------------
+ *
+ * May only be called via NLM_F_REQUEST | NLM_F_DUMP. The command should contain
+ * one but not both of:
+ *
+ * WGDEVICE_A_IFINDEX: NLA_U32
+ * WGDEVICE_A_IFNAME: NLA_NUL_STRING, maxlen IFNAMSIZ - 1
+ *
+ * The kernel will then return several messages (NLM_F_MULTI) containing the
+ * following tree of nested items:
+ *
+ * WGDEVICE_A_IFINDEX: NLA_U32
+ * WGDEVICE_A_IFNAME: NLA_NUL_STRING, maxlen IFNAMSIZ - 1
+ * WGDEVICE_A_PRIVATE_KEY: NLA_EXACT_LEN, len WG_KEY_LEN
+ * WGDEVICE_A_PUBLIC_KEY: NLA_EXACT_LEN, len WG_KEY_LEN
+ * WGDEVICE_A_LISTEN_PORT: NLA_U16
+ * WGDEVICE_A_FWMARK: NLA_U32
+ * WGDEVICE_A_PEERS: NLA_NESTED
+ * 0: NLA_NESTED
+ * WGPEER_A_PUBLIC_KEY: NLA_EXACT_LEN, len WG_KEY_LEN
+ * WGPEER_A_PRESHARED_KEY: NLA_EXACT_LEN, len WG_KEY_LEN
+ * WGPEER_A_ENDPOINT: NLA_MIN_LEN(struct sockaddr), struct sockaddr_in or struct sockaddr_in6
+ * WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL: NLA_U16
+ * WGPEER_A_LAST_HANDSHAKE_TIME: NLA_EXACT_LEN, struct __kernel_timespec
+ * WGPEER_A_RX_BYTES: NLA_U64
+ * WGPEER_A_TX_BYTES: NLA_U64
+ * WGPEER_A_ALLOWEDIPS: NLA_NESTED
+ * 0: NLA_NESTED
+ * WGALLOWEDIP_A_FAMILY: NLA_U16
+ * WGALLOWEDIP_A_IPADDR: NLA_MIN_LEN(struct in_addr), struct in_addr or struct in6_addr
+ * WGALLOWEDIP_A_CIDR_MASK: NLA_U8
+ * 0: NLA_NESTED
+ * ...
+ * 0: NLA_NESTED
+ * ...
+ * ...
+ * WGPEER_A_PROTOCOL_VERSION: NLA_U32
+ * 0: NLA_NESTED
+ * ...
+ * ...
+ *
+ * It is possible that all of the allowed IPs of a single peer will not
+ * fit within a single netlink message. In that case, the same peer will
+ * be written in the following message, except it will only contain
+ * WGPEER_A_PUBLIC_KEY and WGPEER_A_ALLOWEDIPS. This may occur several
+ * times in a row for the same peer. It is then up to the receiver to
+ * coalesce adjacent peers. Likewise, it is possible that all peers will
+ * not fit within a single message. So, subsequent peers will be sent
+ * in following messages, except those will only contain WGDEVICE_A_IFNAME
+ * and WGDEVICE_A_PEERS. It is then up to the receiver to coalesce these
+ * messages to form the complete list of peers.
+ *
+ * Since this is an NLA_F_DUMP command, the final message will always be
+ * NLMSG_DONE, even if an error occurs. However, this NLMSG_DONE message
+ * contains an integer error code. It is either zero or a negative error
+ * code corresponding to the errno.
+ *
+ * WG_CMD_SET_DEVICE
+ * -----------------
+ *
+ * May only be called via NLM_F_REQUEST. The command should contain the
+ * following tree of nested items, containing one but not both of
+ * WGDEVICE_A_IFINDEX and WGDEVICE_A_IFNAME:
+ *
+ * WGDEVICE_A_IFINDEX: NLA_U32
+ * WGDEVICE_A_IFNAME: NLA_NUL_STRING, maxlen IFNAMSIZ - 1
+ * WGDEVICE_A_FLAGS: NLA_U32, 0 or WGDEVICE_F_REPLACE_PEERS if all current
+ * peers should be removed prior to adding the list below.
+ * WGDEVICE_A_PRIVATE_KEY: len WG_KEY_LEN, all zeros to remove
+ * WGDEVICE_A_LISTEN_PORT: NLA_U16, 0 to choose randomly
+ * WGDEVICE_A_FWMARK: NLA_U32, 0 to disable
+ * WGDEVICE_A_PEERS: NLA_NESTED
+ * 0: NLA_NESTED
+ * WGPEER_A_PUBLIC_KEY: len WG_KEY_LEN
+ * WGPEER_A_FLAGS: NLA_U32, 0 and/or WGPEER_F_REMOVE_ME if the
+ * specified peer should not exist at the end of the
+ * operation, rather than added/updated and/or
+ * WGPEER_F_REPLACE_ALLOWEDIPS if all current allowed
+ * IPs of this peer should be removed prior to adding
+ * the list below and/or WGPEER_F_UPDATE_ONLY if the
+ * peer should only be set if it already exists.
+ * WGPEER_A_PRESHARED_KEY: len WG_KEY_LEN, all zeros to remove
+ * WGPEER_A_ENDPOINT: struct sockaddr_in or struct sockaddr_in6
+ * WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL: NLA_U16, 0 to disable
+ * WGPEER_A_ALLOWEDIPS: NLA_NESTED
+ * 0: NLA_NESTED
+ * WGALLOWEDIP_A_FAMILY: NLA_U16
+ * WGALLOWEDIP_A_IPADDR: struct in_addr or struct in6_addr
+ * WGALLOWEDIP_A_CIDR_MASK: NLA_U8
+ * 0: NLA_NESTED
+ * ...
+ * 0: NLA_NESTED
+ * ...
+ * ...
+ * WGPEER_A_PROTOCOL_VERSION: NLA_U32, should not be set or used at
+ * all by most users of this API, as the
+ * most recent protocol will be used when
+ * this is unset. Otherwise, must be set
+ * to 1.
+ * 0: NLA_NESTED
+ * ...
+ * ...
+ *
+ * It is possible that the amount of configuration data exceeds that of
+ * the maximum message length accepted by the kernel. In that case, several
+ * messages should be sent one after another, with each successive one
+ * filling in information not contained in the prior. Note that if
+ * WGDEVICE_F_REPLACE_PEERS is specified in the first message, it probably
+ * should not be specified in fragments that come after, so that the list
+ * of peers is only cleared the first time but appended after. Likewise for
+ * peers, if WGPEER_F_REPLACE_ALLOWEDIPS is specified in the first message
+ * of a peer, it likely should not be specified in subsequent fragments.
+ *
+ * If an error occurs, NLMSG_ERROR will reply containing an errno.
+ */
+
+#ifndef _WG_UAPI_WIREGUARD_H
+#define _WG_UAPI_WIREGUARD_H
+
+#define WG_GENL_NAME "wireguard"
+#define WG_GENL_VERSION 1
+
+#define WG_KEY_LEN 32
+
+enum wg_cmd {
+ WG_CMD_GET_DEVICE,
+ WG_CMD_SET_DEVICE,
+ __WG_CMD_MAX
+};
+#define WG_CMD_MAX (__WG_CMD_MAX - 1)
+
+enum wgdevice_flag {
+ WGDEVICE_F_REPLACE_PEERS = 1U << 0,
+ __WGDEVICE_F_ALL = WGDEVICE_F_REPLACE_PEERS
+};
+enum wgdevice_attribute {
+ WGDEVICE_A_UNSPEC,
+ WGDEVICE_A_IFINDEX,
+ WGDEVICE_A_IFNAME,
+ WGDEVICE_A_PRIVATE_KEY,
+ WGDEVICE_A_PUBLIC_KEY,
+ WGDEVICE_A_FLAGS,
+ WGDEVICE_A_LISTEN_PORT,
+ WGDEVICE_A_FWMARK,
+ WGDEVICE_A_PEERS,
+ __WGDEVICE_A_LAST
+};
+#define WGDEVICE_A_MAX (__WGDEVICE_A_LAST - 1)
+
+enum wgpeer_flag {
+ WGPEER_F_REMOVE_ME = 1U << 0,
+ WGPEER_F_REPLACE_ALLOWEDIPS = 1U << 1,
+ WGPEER_F_UPDATE_ONLY = 1U << 2,
+ __WGPEER_F_ALL = WGPEER_F_REMOVE_ME | WGPEER_F_REPLACE_ALLOWEDIPS |
+ WGPEER_F_UPDATE_ONLY
+};
+enum wgpeer_attribute {
+ WGPEER_A_UNSPEC,
+ WGPEER_A_PUBLIC_KEY,
+ WGPEER_A_PRESHARED_KEY,
+ WGPEER_A_FLAGS,
+ WGPEER_A_ENDPOINT,
+ WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL,
+ WGPEER_A_LAST_HANDSHAKE_TIME,
+ WGPEER_A_RX_BYTES,
+ WGPEER_A_TX_BYTES,
+ WGPEER_A_ALLOWEDIPS,
+ WGPEER_A_PROTOCOL_VERSION,
+ __WGPEER_A_LAST
+};
+#define WGPEER_A_MAX (__WGPEER_A_LAST - 1)
+
+enum wgallowedip_attribute {
+ WGALLOWEDIP_A_UNSPEC,
+ WGALLOWEDIP_A_FAMILY,
+ WGALLOWEDIP_A_IPADDR,
+ WGALLOWEDIP_A_CIDR_MASK,
+ __WGALLOWEDIP_A_LAST
+};
+#define WGALLOWEDIP_A_MAX (__WGALLOWEDIP_A_LAST - 1)
+
+#endif /* _WG_UAPI_WIREGUARD_H */
diff --git a/contrib/wireguard-tools/uapi/openbsd/net/if_wg.h b/contrib/wireguard-tools/uapi/openbsd/net/if_wg.h
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/uapi/openbsd/net/if_wg.h
@@ -0,0 +1,92 @@
+/* SPDX-License-Identifier: ISC */
+/*
+ * Copyright (C) 2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ * Copyright (c) 2020 Matt Dunwoodie <ncon@noconroy.net>
+ */
+
+#ifndef __IF_WG_H__
+#define __IF_WG_H__
+
+#include <sys/limits.h>
+#include <sys/errno.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+
+
+/*
+ * This is the public interface to the WireGuard network interface.
+ *
+ * It is designed to be used by tools such as ifconfig(8) and wg(8).
+ */
+
+#define WG_KEY_LEN 32
+
+#define SIOCSWG _IOWR('i', 210, struct wg_data_io)
+#define SIOCGWG _IOWR('i', 211, struct wg_data_io)
+
+#define a_ipv4 a_addr.addr_ipv4
+#define a_ipv6 a_addr.addr_ipv6
+
+struct wg_aip_io {
+ sa_family_t a_af;
+ int a_cidr;
+ union wg_aip_addr {
+ struct in_addr addr_ipv4;
+ struct in6_addr addr_ipv6;
+ } a_addr;
+};
+
+#define WG_PEER_HAS_PUBLIC (1 << 0)
+#define WG_PEER_HAS_PSK (1 << 1)
+#define WG_PEER_HAS_PKA (1 << 2)
+#define WG_PEER_HAS_ENDPOINT (1 << 3)
+#define WG_PEER_REPLACE_AIPS (1 << 4)
+#define WG_PEER_REMOVE (1 << 5)
+#define WG_PEER_UPDATE (1 << 6)
+
+#define p_sa p_endpoint.sa_sa
+#define p_sin p_endpoint.sa_sin
+#define p_sin6 p_endpoint.sa_sin6
+
+struct wg_peer_io {
+ int p_flags;
+ int p_protocol_version;
+ uint8_t p_public[WG_KEY_LEN];
+ uint8_t p_psk[WG_KEY_LEN];
+ uint16_t p_pka;
+ union wg_peer_endpoint {
+ struct sockaddr sa_sa;
+ struct sockaddr_in sa_sin;
+ struct sockaddr_in6 sa_sin6;
+ } p_endpoint;
+ uint64_t p_txbytes;
+ uint64_t p_rxbytes;
+ struct timespec p_last_handshake; /* nanotime */
+ size_t p_aips_count;
+ struct wg_aip_io p_aips[];
+};
+
+#define WG_INTERFACE_HAS_PUBLIC (1 << 0)
+#define WG_INTERFACE_HAS_PRIVATE (1 << 1)
+#define WG_INTERFACE_HAS_PORT (1 << 2)
+#define WG_INTERFACE_HAS_RTABLE (1 << 3)
+#define WG_INTERFACE_REPLACE_PEERS (1 << 4)
+
+struct wg_interface_io {
+ uint8_t i_flags;
+ in_port_t i_port;
+ int i_rtable;
+ uint8_t i_public[WG_KEY_LEN];
+ uint8_t i_private[WG_KEY_LEN];
+ size_t i_peers_count;
+ struct wg_peer_io i_peers[];
+};
+
+struct wg_data_io {
+ char wgd_name[IFNAMSIZ];
+ size_t wgd_size; /* total size of the memory pointed to by wgd_interface */
+ struct wg_interface_io *wgd_interface;
+};
+
+#endif /* __IF_WG_H__ */
diff --git a/contrib/wireguard-tools/uapi/windows/wireguard.h b/contrib/wireguard-tools/uapi/windows/wireguard.h
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/uapi/windows/wireguard.h
@@ -0,0 +1,80 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2021 WireGuard LLC. All Rights Reserved.
+ */
+
+#ifndef _WIREGUARD_NT_H
+#define _WIREGUARD_NT_H
+
+#include <ntdef.h>
+#include <ws2def.h>
+#include <ws2ipdef.h>
+#include <inaddr.h>
+#include <in6addr.h>
+
+#define WG_KEY_LEN 32
+
+typedef struct _WG_IOCTL_ALLOWED_IP
+{
+ union
+ {
+ IN_ADDR V4;
+ IN6_ADDR V6;
+ } Address;
+ ADDRESS_FAMILY AddressFamily;
+ UCHAR Cidr;
+} __attribute__((aligned(8))) WG_IOCTL_ALLOWED_IP;
+
+typedef enum
+{
+ WG_IOCTL_PEER_HAS_PUBLIC_KEY = 1 << 0,
+ WG_IOCTL_PEER_HAS_PRESHARED_KEY = 1 << 1,
+ WG_IOCTL_PEER_HAS_PERSISTENT_KEEPALIVE = 1 << 2,
+ WG_IOCTL_PEER_HAS_ENDPOINT = 1 << 3,
+ WG_IOCTL_PEER_HAS_PROTOCOL_VERSION = 1 << 4,
+ WG_IOCTL_PEER_REPLACE_ALLOWED_IPS = 1 << 5,
+ WG_IOCTL_PEER_REMOVE = 1 << 6,
+ WG_IOCTL_PEER_UPDATE = 1 << 7
+} WG_IOCTL_PEER_FLAG;
+
+typedef struct _WG_IOCTL_PEER
+{
+ WG_IOCTL_PEER_FLAG Flags;
+ ULONG ProtocolVersion; /* 0 = latest protocol, 1 = this protocol. */
+ UCHAR PublicKey[WG_KEY_LEN];
+ UCHAR PresharedKey[WG_KEY_LEN];
+ USHORT PersistentKeepalive;
+ SOCKADDR_INET Endpoint;
+ ULONG64 TxBytes;
+ ULONG64 RxBytes;
+ ULONG64 LastHandshake;
+ ULONG AllowedIPsCount;
+} __attribute__((aligned(8))) WG_IOCTL_PEER;
+
+typedef enum
+{
+ WG_IOCTL_INTERFACE_HAS_PUBLIC_KEY = 1 << 0,
+ WG_IOCTL_INTERFACE_HAS_PRIVATE_KEY = 1 << 1,
+ WG_IOCTL_INTERFACE_HAS_LISTEN_PORT = 1 << 2,
+ WG_IOCTL_INTERFACE_REPLACE_PEERS = 1 << 3
+} WG_IOCTL_INTERFACE_FLAG;
+
+typedef struct _WG_IOCTL_INTERFACE
+{
+ WG_IOCTL_INTERFACE_FLAG Flags;
+ USHORT ListenPort;
+ UCHAR PrivateKey[WG_KEY_LEN];
+ UCHAR PublicKey[WG_KEY_LEN];
+ ULONG PeersCount;
+} __attribute__((aligned(8))) WG_IOCTL_INTERFACE;
+
+#define WG_IOCTL_GET CTL_CODE(45208U, 321, METHOD_OUT_DIRECT, FILE_READ_DATA | FILE_WRITE_DATA)
+#define WG_IOCTL_SET CTL_CODE(45208U, 322, METHOD_IN_DIRECT, FILE_READ_DATA | FILE_WRITE_DATA)
+
+#define DEVPKEY_WG_NAME (DEVPROPKEY) { \
+ { 0x65726957, 0x7547, 0x7261, { 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x4b, 0x65, 0x79 } }, \
+ DEVPROPID_FIRST_USABLE + 1 \
+ }
+
+
+#endif
diff --git a/contrib/wireguard-tools/version.h b/contrib/wireguard-tools/version.h
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/version.h
@@ -0,0 +1,3 @@
+#ifndef WIREGUARD_TOOLS_VERSION
+#define WIREGUARD_TOOLS_VERSION "1.0.20210914"
+#endif
diff --git a/contrib/wireguard-tools/wg-quick/android.c b/contrib/wireguard-tools/wg-quick/android.c
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/wg-quick/android.c
@@ -0,0 +1,1303 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2015-2021 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ *
+ * This is a shell script written in C. It very intentionally still functions like
+ * a shell script, calling out to external executables such as ip(8).
+ */
+
+#define _GNU_SOURCE
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+#include <strings.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <ctype.h>
+#include <time.h>
+#include <unistd.h>
+#include <errno.h>
+#include <regex.h>
+#include <dlfcn.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/param.h>
+#include <sys/system_properties.h>
+
+#ifndef WG_PACKAGE_NAME
+#define WG_PACKAGE_NAME "com.wireguard.android"
+#endif
+#ifndef WG_CONFIG_SEARCH_PATHS
+#define WG_CONFIG_SEARCH_PATHS "/data/misc/wireguard /data/data/" WG_PACKAGE_NAME "/files"
+#endif
+
+#define _printf_(x, y) __attribute__((format(printf, x, y)))
+#define _cleanup_(x) __attribute__((cleanup(x)))
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
+
+static bool is_exiting = false;
+static bool binder_available = false;
+static unsigned int sdk_version;
+
+static void *xmalloc(size_t size)
+{
+ void *ret = malloc(size);
+ if (ret)
+ return ret;
+ perror("Error: malloc");
+ exit(errno);
+}
+
+static void *xcalloc(size_t nmemb, size_t size)
+{
+ void *ret = calloc(nmemb, size);
+ if (ret)
+ return ret;
+ perror("Error: calloc");
+ exit(errno);
+}
+
+static void *xstrdup(const char *str)
+{
+ char *ret = strdup(str);
+ if (ret)
+ return ret;
+ perror("Error: strdup");
+ exit(errno);
+}
+
+static void xregcomp(regex_t *preg, const char *regex, int cflags)
+{
+ if (regcomp(preg, regex, cflags)) {
+ fprintf(stderr, "Error: Regex compilation error\n");
+ exit(EBADR);
+ }
+}
+
+static char *concat(char *first, ...)
+{
+ va_list args;
+ size_t len = 0;
+ char *ret;
+
+ va_start(args, first);
+ for (char *i = first; i; i = va_arg(args, char *))
+ len += strlen(i);
+ va_end(args);
+
+ ret = xmalloc(len + 1);
+ ret[0] = '\0';
+
+ va_start(args, first);
+ for (char *i = first; i; i = va_arg(args, char *))
+ strcat(ret, i);
+ va_end(args);
+
+ return ret;
+}
+
+static char *concat_and_free(char *orig, const char *delim, const char *new_line)
+{
+ char *ret;
+
+ if (!orig)
+ ret = xstrdup(new_line);
+ else
+ ret = concat(orig, delim, new_line, NULL);
+ free(orig);
+ return ret;
+}
+
+struct command_buffer {
+ char *line;
+ size_t len;
+ FILE *stream;
+};
+
+static void free_command_buffer(struct command_buffer *c)
+{
+ if (!c)
+ return;
+ if (c->stream)
+ pclose(c->stream);
+ free(c->line);
+}
+
+static void freep(void *p)
+{
+ free(*(void **)p);
+}
+static void fclosep(FILE **f)
+{
+ if (*f)
+ fclose(*f);
+}
+#define _cleanup_free_ _cleanup_(freep)
+#define _cleanup_fclose_ _cleanup_(fclosep)
+#define _cleanup_regfree_ _cleanup_(regfree)
+
+#define DEFINE_CMD(name) _cleanup_(free_command_buffer) struct command_buffer name = { 0 };
+
+static char *vcmd_ret(struct command_buffer *c, const char *cmd_fmt, va_list args)
+{
+ _cleanup_free_ char *cmd = NULL;
+
+ if (!c->stream && !cmd_fmt)
+ return NULL;
+ if (c->stream && cmd_fmt)
+ pclose(c->stream);
+
+ if (cmd_fmt) {
+ if (vasprintf(&cmd, cmd_fmt, args) < 0) {
+ perror("Error: vasprintf");
+ exit(errno);
+ }
+
+ c->stream = popen(cmd, "r");
+ if (!c->stream) {
+ perror("Error: popen");
+ exit(errno);
+ }
+ }
+ errno = 0;
+ if (getline(&c->line, &c->len, c->stream) < 0) {
+ if (errno) {
+ perror("Error: getline");
+ exit(errno);
+ }
+ return NULL;
+ }
+ return c->line;
+}
+
+_printf_(1, 2) static void cmd(const char *cmd_fmt, ...)
+{
+ _cleanup_free_ char *cmd = NULL;
+ va_list args;
+ int ret;
+
+ va_start(args, cmd_fmt);
+ if (vasprintf(&cmd, cmd_fmt, args) < 0) {
+ perror("Error: vasprintf");
+ exit(errno);
+ }
+ va_end(args);
+
+ printf("[#] %s\n", cmd);
+ ret = system(cmd);
+
+ if (ret < 0)
+ ret = ESRCH;
+ else if (ret > 0)
+ ret = WIFEXITED(ret) ? WEXITSTATUS(ret) : EIO;
+
+ if (ret && !is_exiting)
+ exit(ret);
+}
+
+_printf_(2, 3) static char *cmd_ret(struct command_buffer *c, const char *cmd_fmt, ...)
+{
+ va_list args;
+ char *ret;
+
+ va_start(args, cmd_fmt);
+ ret = vcmd_ret(c, cmd_fmt, args);
+ va_end(args);
+ return ret;
+}
+
+_printf_(1, 2) static void cndc(const char *cmd_fmt, ...)
+{
+ DEFINE_CMD(c);
+ int error_code;
+ char *ret;
+ va_list args;
+ _cleanup_free_ char *ndc_fmt = concat("ndc ", cmd_fmt, NULL);
+
+ va_start(args, cmd_fmt);
+ printf("[#] ");
+ vprintf(ndc_fmt, args);
+ printf("\n");
+ va_end(args);
+
+ va_start(args, cmd_fmt);
+ ret = vcmd_ret(&c, ndc_fmt, args);
+ va_end(args);
+
+ if (!ret) {
+ fprintf(stderr, "Error: could not call ndc\n");
+ exit(ENOSYS);
+ }
+
+ error_code = atoi(ret);
+ if (error_code >= 400 && error_code < 600) {
+ fprintf(stderr, "Error: %s\n", ret);
+ exit(ENONET);
+ }
+}
+
+/* Values are from AOSP repository platform/frameworks/native in libs/binder/ndk/include_ndk/android/binder_status.h. */
+enum {
+ STATUS_OK = 0,
+ STATUS_UNKNOWN_ERROR = -2147483647 - 1,
+ STATUS_NO_MEMORY = -ENOMEM,
+ STATUS_INVALID_OPERATION = -ENOSYS,
+ STATUS_BAD_VALUE = -EINVAL,
+ STATUS_BAD_TYPE = STATUS_UNKNOWN_ERROR + 1,
+ STATUS_NAME_NOT_FOUND = -ENOENT,
+ STATUS_PERMISSION_DENIED = -EPERM,
+ STATUS_NO_INIT = -ENODEV,
+ STATUS_ALREADY_EXISTS = -EEXIST,
+ STATUS_DEAD_OBJECT = -EPIPE,
+ STATUS_FAILED_TRANSACTION = STATUS_UNKNOWN_ERROR + 2,
+ STATUS_BAD_INDEX = -EOVERFLOW,
+ STATUS_NOT_ENOUGH_DATA = -ENODATA,
+ STATUS_WOULD_BLOCK = -EWOULDBLOCK,
+ STATUS_TIMED_OUT = -ETIMEDOUT,
+ STATUS_UNKNOWN_TRANSACTION = -EBADMSG,
+ STATUS_FDS_NOT_ALLOWED = STATUS_UNKNOWN_ERROR + 7,
+ STATUS_UNEXPECTED_NULL = STATUS_UNKNOWN_ERROR + 8
+};
+enum {
+ EX_NONE = 0,
+ EX_SECURITY = -1,
+ EX_BAD_PARCELABLE = -2,
+ EX_ILLEGAL_ARGUMENT = -3,
+ EX_NULL_POINTER = -4,
+ EX_ILLEGAL_STATE = -5,
+ EX_NETWORK_MAIN_THREAD = -6,
+ EX_UNSUPPORTED_OPERATION = -7,
+ EX_SERVICE_SPECIFIC = -8,
+ EX_PARCELABLE = -9,
+ EX_TRANSACTION_FAILED = -129
+};
+enum {
+ FLAG_ONEWAY = 0x01,
+};
+enum {
+ FIRST_CALL_TRANSACTION = 0x00000001,
+ LAST_CALL_TRANSACTION = 0x00ffffff
+};
+struct AIBinder;
+struct AParcel;
+struct AStatus;
+struct AIBinder_Class;
+typedef struct AIBinder AIBinder;
+typedef struct AParcel AParcel;
+typedef struct AStatus AStatus;
+typedef struct AIBinder_Class AIBinder_Class;
+typedef int32_t binder_status_t;
+typedef int32_t binder_exception_t;
+typedef uint32_t transaction_code_t;
+typedef uint32_t binder_flags_t;
+typedef void *(*AIBinder_Class_onCreate)(void *args);
+typedef void (*AIBinder_Class_onDestroy)(void *userData);
+typedef binder_status_t (*AIBinder_Class_onTransact)(AIBinder *binder, transaction_code_t code, const AParcel *in, AParcel *out);
+typedef const char *(*AParcel_stringArrayElementGetter)(const void *arrayData, size_t index, int32_t *outLength);
+static AIBinder_Class *(*AIBinder_Class_define)(const char *interfaceDescriptor, AIBinder_Class_onCreate onCreate, AIBinder_Class_onDestroy onDestroy, AIBinder_Class_onTransact onTransact) __attribute__((warn_unused_result));
+static bool (*AIBinder_associateClass)(AIBinder *binder, const AIBinder_Class *clazz);
+static void (*AIBinder_decStrong)(AIBinder *binder);
+static binder_status_t (*AIBinder_prepareTransaction)(AIBinder *binder, AParcel **in);
+static binder_status_t (*AIBinder_transact)(AIBinder *binder, transaction_code_t code, AParcel **in, AParcel **out, binder_flags_t flags);
+static binder_status_t (*AIBinder_ping)(AIBinder *binder);
+static binder_status_t (*AIBinder_dump)(AIBinder *binder, int fd, const char **args, uint32_t numArgs);
+static binder_status_t (*AParcel_readStatusHeader)(const AParcel *parcel, AStatus **status);
+static binder_status_t (*AParcel_readBool)(const AParcel *parcel, bool *value);
+static void (*AParcel_delete)(AParcel *parcel);
+static binder_status_t (*AParcel_setDataPosition)(const AParcel *parcel, int32_t position);
+static int32_t (*AParcel_getDataPosition)(const AParcel *parcel);
+static binder_status_t (*AParcel_writeInt32)(AParcel *parcel, int32_t value);
+static binder_status_t (*AParcel_writeStringArray)(AParcel *parcel, const void *arrayData, int32_t length, AParcel_stringArrayElementGetter getter);
+static binder_status_t (*AParcel_writeString)(AParcel *parcel, const char *string, int32_t length);
+static bool (*AStatus_isOk)(const AStatus *status);
+static void (*AStatus_delete)(AStatus *status);
+static binder_exception_t (*AStatus_getExceptionCode)(const AStatus *status);
+static int32_t (*AStatus_getServiceSpecificError)(const AStatus *status);
+static const char* (*AStatus_getMessage)(const AStatus *status);
+static binder_status_t (*AStatus_getStatus)(const AStatus *status);
+static AIBinder *(*AServiceManager_getService)(const char *instance) __attribute__((__warn_unused_result__));
+
+static __attribute__((__constructor__(65535))) void load_symbols(void)
+{
+ void *handle = dlopen("libbinder_ndk.so", RTLD_LAZY);
+ binder_available = !!handle;
+ if (!binder_available)
+ return;
+
+#define X(symb) do { \
+ if (!((symb) = (typeof(symb))dlsym(handle, #symb))) { \
+ fprintf(stderr, "Error: unable to import " #symb " from libbinder_ndk.so\n"); \
+ exit(ELIBACC); \
+ } \
+ } while (0)
+ X(AIBinder_Class_define);
+ X(AIBinder_associateClass);
+ X(AIBinder_decStrong);
+ X(AIBinder_prepareTransaction);
+ X(AIBinder_transact);
+ X(AIBinder_ping);
+ X(AIBinder_dump);
+ X(AParcel_readStatusHeader);
+ X(AParcel_readBool);
+ X(AParcel_delete);
+ X(AParcel_setDataPosition);
+ X(AParcel_getDataPosition);
+ X(AParcel_writeInt32);
+ X(AParcel_writeStringArray);
+ X(AParcel_writeString);
+ X(AStatus_isOk);
+ X(AStatus_delete);
+ X(AStatus_getExceptionCode);
+ X(AStatus_getServiceSpecificError);
+ X(AStatus_getMessage);
+ X(AStatus_getStatus);
+ X(AServiceManager_getService);
+#undef X
+}
+
+static void cleanup_binder(AIBinder **binder)
+{
+ if (*binder)
+ AIBinder_decStrong(*binder);
+}
+static void cleanup_status(AStatus **status)
+{
+ if (*status)
+ AStatus_delete(*status);
+}
+static void cleanup_parcel(AParcel **parcel)
+{
+ if (*parcel)
+ AParcel_delete(*parcel);
+}
+
+#define _cleanup_status_ __attribute__((__cleanup__(cleanup_status)))
+#define _cleanup_parcel_ __attribute__((__cleanup__(cleanup_parcel)))
+#define _cleanup_binder_ __attribute__((__cleanup__(cleanup_binder)))
+
+static int32_t string_size(const char *str)
+{
+ return str ? strlen(str) : -1;
+}
+
+static int32_t string_array_size(char *const *array)
+{
+ int32_t size = -1;
+ if (!array)
+ return size;
+ for (size = 0; array[size]; ++size);
+ return size;
+}
+
+static const char *string_array_getter(const void *array_data, size_t index, int32_t *out_length)
+{
+ const char **array = (const char **)array_data;
+ *out_length = array[index] ? strlen(array[index]) : -1;
+ return array[index];
+}
+
+static binder_status_t meaningful_binder_status(const AStatus *status_out)
+{
+ binder_status_t status = STATUS_OK;
+ binder_exception_t exc_code;
+ int32_t exc_code_service;
+ const char *message;
+
+ if (!AStatus_isOk(status_out)) {
+ exc_code = AStatus_getExceptionCode(status_out);
+ if (exc_code == EX_TRANSACTION_FAILED) {
+ status = AStatus_getStatus(status_out);
+ fprintf(stderr, "Error: transaction failed: %d\n", status);
+ }
+ else {
+ message = AStatus_getMessage(status_out);
+
+ if (exc_code == EX_SERVICE_SPECIFIC) {
+ exc_code_service = AStatus_getServiceSpecificError(status_out);
+ fprintf(stderr, "Error: service specific exception code: %d%s%s\n", exc_code_service, message ? ": " : "", message ?: "");
+ }
+ else
+ fprintf(stderr, "Error: exception code: %d%s%s\n", exc_code, message ? ": " : "", message ?: "");
+
+ status = STATUS_FAILED_TRANSACTION;
+ }
+ }
+
+ return status;
+}
+
+/* These values are default values observed in AOSP. */
+enum {
+ DNSRESOLVER_SAMPLE_VALIDITY = 1800 /* sec */,
+ DNSRESOLVER_SUCCESS_THRESHOLD = 25,
+ DNSRESOLVER_MIN_SAMPLES = 8,
+ DNSRESOLVER_MAX_SAMPLES = 8,
+ DNSRESOLVER_BASE_TIMEOUT = 5000 /* msec */,
+ DNSRESOLVER_RETRY_COUNT = 2
+};
+
+struct dnsresolver_params {
+ int32_t netid;
+ int32_t sample_validity_seconds;
+ int32_t success_threshold;
+ int32_t min_samples;
+ int32_t max_samples;
+ int32_t base_timeout_msec;
+ int32_t retry_count;
+ char **servers; /* NULL terminated array of zero-terminated UTF-8 strings */
+ char **domains; /* NULL terminated array of zero-terminated UTF-8 strings */
+ char *tls_name; /* zero-terminated UTF-8 string */
+ char **tls_servers; /* NULL terminated array of zero-terminated UTF-8 strings */
+ char **tls_fingerprints; /* NULL terminated array of zero-terminated UTF-8 strings */
+};
+
+static void *on_create()
+{
+ fprintf(stderr, "Error: on_create called on proxy object\n");
+ exit(ENOTSUP);
+ return NULL;
+}
+
+static void on_destroy()
+{
+ fprintf(stderr, "Error: on_destroy called on proxy object\n");
+ exit(ENOTSUP);
+}
+
+static binder_status_t on_transact()
+{
+ fprintf(stderr, "Error: on_transact called on a proxy object\n");
+ exit(ENOTSUP);
+ return 0;
+}
+
+static AIBinder *dnsresolver_get_handle(void)
+{
+ AIBinder *binder;
+ AIBinder_Class *clazz;
+
+ if (!binder_available)
+ return NULL;
+
+ binder = AServiceManager_getService("dnsresolver");
+ if (!binder)
+ return NULL;
+ clazz = AIBinder_Class_define("android.net.IDnsResolver", &on_create, &on_destroy, &on_transact);
+ if (!clazz)
+ goto error;
+
+ if (!AIBinder_associateClass(binder, clazz))
+ goto error;
+
+ return binder;
+error:
+ AIBinder_decStrong(binder);
+ return NULL;
+}
+
+static int32_t dnsresolver_create_network_cache(void *handle, int32_t netid)
+{
+ AIBinder *const binder = handle;
+ binder_status_t status;
+ _cleanup_parcel_ AParcel *parcel_in = NULL;
+ _cleanup_parcel_ AParcel *parcel_out = NULL;
+ _cleanup_status_ AStatus *status_out = NULL;
+
+ status = AIBinder_prepareTransaction(binder, &parcel_in);
+ if (status != STATUS_OK)
+ return status;
+
+ status = AParcel_writeInt32(parcel_in, netid);
+ if (status != STATUS_OK)
+ return status;
+
+ status = AIBinder_transact(binder, FIRST_CALL_TRANSACTION + 7 /* createNetworkCache */, &parcel_in, &parcel_out, 0);
+ if (status != STATUS_OK)
+ return status;
+
+ status = AParcel_readStatusHeader(parcel_out, &status_out);
+ if (status != STATUS_OK)
+ return status;
+
+ if (!AStatus_isOk(status_out))
+ return meaningful_binder_status(status_out);
+
+ return STATUS_OK;
+}
+
+static int32_t dnsresolver_set_resolver_configuration(void *handle, const struct dnsresolver_params *params)
+{
+ AIBinder *const binder = handle;
+ binder_status_t status;
+ _cleanup_parcel_ AParcel *parcel_in = NULL;
+ _cleanup_parcel_ AParcel *parcel_out = NULL;
+ _cleanup_status_ AStatus *status_out = NULL;
+ int32_t start_position, end_position;
+
+ status = AIBinder_prepareTransaction(binder, &parcel_in);
+ if (status != STATUS_OK)
+ return status;
+
+ status = AParcel_writeInt32(parcel_in, 1);
+ if (status != STATUS_OK)
+ return status;
+
+ start_position = AParcel_getDataPosition(parcel_in);
+ status = AParcel_writeInt32(parcel_in, 0);
+ if (status != STATUS_OK)
+ return status;
+
+ status = AParcel_writeInt32(parcel_in, params->netid);
+ if (status != STATUS_OK)
+ return status;
+ status = AParcel_writeInt32(parcel_in, params->sample_validity_seconds);
+ if (status != STATUS_OK)
+ return status;
+ status = AParcel_writeInt32(parcel_in, params->success_threshold);
+ if (status != STATUS_OK)
+ return status;
+ status = AParcel_writeInt32(parcel_in, params->min_samples);
+ if (status != STATUS_OK)
+ return status;
+ status = AParcel_writeInt32(parcel_in, params->max_samples);
+ if (status != STATUS_OK)
+ return status;
+ status = AParcel_writeInt32(parcel_in, params->base_timeout_msec);
+ if (status != STATUS_OK)
+ return status;
+ status = AParcel_writeInt32(parcel_in, params->retry_count);
+ if (status != STATUS_OK)
+ return status;
+ status = AParcel_writeStringArray(parcel_in, params->servers, string_array_size(params->servers), &string_array_getter);
+ if (status != STATUS_OK)
+ return status;
+ status = AParcel_writeStringArray(parcel_in, params->domains, string_array_size(params->domains), &string_array_getter);
+ if (status != STATUS_OK)
+ return status;
+ status = AParcel_writeString(parcel_in, params->tls_name, string_size(params->tls_name));
+ if (status != STATUS_OK)
+ return status;
+ status = AParcel_writeStringArray(parcel_in, params->tls_servers, string_array_size(params->tls_servers), &string_array_getter);
+ if (status != STATUS_OK)
+ return status;
+ status = AParcel_writeStringArray(parcel_in, params->tls_fingerprints, string_array_size(params->tls_fingerprints), &string_array_getter);
+ if (status != STATUS_OK)
+ return status;
+
+ end_position = AParcel_getDataPosition(parcel_in);
+ status = AParcel_setDataPosition(parcel_in, start_position);
+ if (status != STATUS_OK)
+ return status;
+ status = AParcel_writeInt32(parcel_in, end_position - start_position);
+ if (status != STATUS_OK)
+ return status;
+ status = AParcel_setDataPosition(parcel_in, end_position);
+ if (status != STATUS_OK)
+ return status;
+
+ status = AIBinder_transact(binder, FIRST_CALL_TRANSACTION + 2 /* setResolverConfiguration */, &parcel_in, &parcel_out, 0);
+ if (status != STATUS_OK)
+ return status;
+
+ status = AParcel_readStatusHeader(parcel_out, &status_out);
+ if (status != STATUS_OK)
+ return status;
+
+ return meaningful_binder_status(status_out);
+}
+
+static void auto_su(int argc, char *argv[])
+{
+ char *args[argc + 4];
+
+ if (!getuid())
+ return;
+
+ args[0] = "su";
+ args[1] = "-p";
+ args[2] = "-c";
+ memcpy(&args[3], argv, argc * sizeof(*args));
+ args[argc + 3] = NULL;
+
+ printf("[$] su -p -c ");
+ for (int i = 0; i < argc; ++i)
+ printf("%s%c", argv[i], i == argc - 1 ? '\n' : ' ');
+
+ execvp("su", args);
+ exit(errno);
+}
+
+static void add_if(const char *iface)
+{
+ cmd("ip link add %s type wireguard", iface);
+}
+
+static void del_if(const char *iface)
+{
+ DEFINE_CMD(c_rule);
+ DEFINE_CMD(c_iptables);
+ DEFINE_CMD(c_ip6tables);
+ _cleanup_regfree_ regex_t rule_reg = { 0 }, iptables_reg = { 0 };
+ regmatch_t matches[2];
+ char *netid = NULL;
+ _cleanup_free_ char *rule_regex = concat("0xc([0-9a-f]+)/0xcffff lookup ", iface, NULL);
+ _cleanup_free_ char *iptables_regex = concat("^-A (.* --comment \"wireguard rule ", iface, "\"[^\n]*)\n*$", NULL);
+
+ xregcomp(&rule_reg, rule_regex, REG_EXTENDED);
+ xregcomp(&iptables_reg, iptables_regex, REG_EXTENDED);
+
+ cmd("ip link del %s", iface);
+
+ for (char *rule = cmd_ret(&c_iptables, "iptables-save"); rule; rule = cmd_ret(&c_iptables, NULL)) {
+ if (!regexec(&iptables_reg, rule, ARRAY_SIZE(matches), matches, 0)) {
+ rule[matches[1].rm_eo] = '\0';
+ cmd("iptables -D %s", &rule[matches[1].rm_so]);
+ }
+ }
+ for (char *rule = cmd_ret(&c_ip6tables, "ip6tables-save"); rule; rule = cmd_ret(&c_ip6tables, NULL)) {
+ if (!regexec(&iptables_reg, rule, ARRAY_SIZE(matches), matches, 0)) {
+ rule[matches[1].rm_eo] = '\0';
+ cmd("ip6tables -D %s", &rule[matches[1].rm_so]);
+ }
+ }
+
+ for (char *rule = cmd_ret(&c_rule, "ip rule show"); rule; rule = cmd_ret(&c_rule, NULL)) {
+ if (!regexec(&rule_reg, rule, ARRAY_SIZE(matches), matches, 0)) {
+ rule[matches[1].rm_eo] = '\0';
+ netid = &rule[matches[1].rm_so];
+ break;
+ }
+ }
+ if (netid)
+ cndc("network destroy %lu", strtoul(netid, NULL, 16));
+}
+
+static bool should_block_ipv6(const char *iface)
+{
+ DEFINE_CMD(c);
+ bool has_ipv6 = false, has_all_none = true;
+
+ for (char *endpoint = cmd_ret(&c, "wg show %s endpoints", iface); endpoint; endpoint = cmd_ret(&c, NULL)) {
+ char *start = strchr(endpoint, '\t');
+
+ if (!start)
+ continue;
+ ++start;
+ if (start[0] != '(')
+ has_all_none = false;
+ if (start[0] == '[')
+ has_ipv6 = true;
+ }
+ return !has_ipv6 && !has_all_none;
+}
+
+static uint16_t determine_listen_port(const char *iface)
+{
+ DEFINE_CMD(c);
+ unsigned long listen_port = 0;
+ char *value;
+
+ cmd("ip link set up dev %s", iface);
+ value = cmd_ret(&c, "wg show %s listen-port", iface);
+ if (!value)
+ goto set_back_down;
+ listen_port = strtoul(value, NULL, 10);
+ if (listen_port > UINT16_MAX || !listen_port) {
+ listen_port = 0;
+ goto set_back_down;
+ }
+set_back_down:
+ cmd("ip link set down dev %s", iface);
+ return listen_port;
+}
+
+static void up_if(unsigned int *netid, const char *iface, uint16_t listen_port)
+{
+ srandom(time(NULL) ^ getpid()); /* Not real randomness. */
+
+ while (*netid < 4096)
+ *netid = random() & 0xfffe;
+
+ cmd("wg set %s fwmark 0x20000", iface);
+ cmd("iptables -I OUTPUT 1 -m mark --mark 0x20000 -j ACCEPT -m comment --comment \"wireguard rule %s\"", iface);
+ cmd("ip6tables -I OUTPUT 1 -m mark --mark 0x20000 -j ACCEPT -m comment --comment \"wireguard rule %s\"", iface);
+ if (listen_port) {
+ cmd("iptables -I INPUT 1 -p udp --dport %u -j ACCEPT -m comment --comment \"wireguard rule %s\"", listen_port, iface);
+ cmd("ip6tables -I INPUT 1 -p udp --dport %u -j %s -m comment --comment \"wireguard rule %s\"", listen_port, should_block_ipv6(iface) ? "DROP" : "ACCEPT", iface);
+ }
+ cmd("ip link set up dev %s", iface);
+ cndc(sdk_version < 31 ? "network create %u vpn 1 1" : "network create %u vpn 1", *netid);
+ cndc("network interface add %u %s", *netid, iface);
+}
+
+static int compare_uid(const void *a, const void *b)
+{
+ return *(const uid_t *)a - *(const uid_t *)b;
+}
+
+static uid_t *get_uid_list(const char *selected_applications)
+{
+ _cleanup_fclose_ FILE *package_list = NULL;
+ _cleanup_free_ char *line = NULL;
+ uid_t package_uid;
+ size_t line_len = 0, i;
+ const char *comma, *start;
+ uid_t *uid_list;
+
+ if (!selected_applications)
+ return xcalloc(1, sizeof(*uid_list));
+
+ for (i = 1, comma = selected_applications; comma; comma = strchr(comma + 1, ','), ++i);
+ uid_list = xcalloc(i, sizeof(*uid_list));
+ i = 0;
+
+ package_list = fopen("/data/system/packages.list", "r");
+ if (!package_list) {
+ perror("Error: Unable to open package list");
+ exit(errno);
+ }
+
+ while (getline(&line, &line_len, package_list) >= 0) {
+ char *package_name, *package_uid_str, *endptr;
+
+ package_name = line;
+ package_uid_str = strchr(package_name, ' ');
+ if (!package_uid_str)
+ continue;
+ *package_uid_str++ = '\0';
+ *strchrnul(package_uid_str, ' ') = '\0';
+ package_uid = strtoul(package_uid_str, &endptr, 10);
+ if (!package_uid || !*package_uid_str || *endptr)
+ continue;
+
+ for (start = selected_applications; comma = strchrnul(start, ','), *start; start = comma + 1) {
+ ptrdiff_t token_len = comma - start;
+
+ if (token_len && strlen(package_name) == token_len && !strncmp(start, package_name, token_len))
+ uid_list[i++] = package_uid;
+ }
+ }
+ qsort(uid_list, i, sizeof(*uid_list), compare_uid);
+ return uid_list;
+}
+
+static void set_users(unsigned int netid, const char *excluded_applications, const char *included_applications)
+{
+ _cleanup_free_ uid_t *uids = NULL;
+ uid_t *uid;
+ unsigned int args_per_command = 0;
+ _cleanup_free_ char *ranges = NULL;
+ char range[22];
+ uid_t start;
+
+ if (excluded_applications && included_applications) {
+ fprintf(stderr, "Error: only one of ExcludedApplications and IncludedApplications may be specified, but not both\n");
+ exit(EEXIST);
+ }
+
+ if (excluded_applications || !included_applications) {
+ uid = uids = get_uid_list(excluded_applications);
+ for (start = 0; *uid; start = *uid + 1, ++uid) {
+ if (start > *uid - 1)
+ continue;
+ else if (start == *uid - 1)
+ snprintf(range, sizeof(range), "%u", start);
+ else
+ snprintf(range, sizeof(range), "%u-%u", start, *uid - 1);
+ ranges = concat_and_free(ranges, " ", range);
+ if (++args_per_command % 18 == 0) {
+ cndc("network users add %u %s", netid, ranges);
+ free(ranges);
+ ranges = NULL;
+ }
+ }
+ if (start < 99999) {
+ snprintf(range, sizeof(range), "%u-99999", start);
+ ranges = concat_and_free(ranges, " ", range);
+ }
+ } else {
+ for (uid = uids = get_uid_list(included_applications); *uid; ++uid) {
+ snprintf(range, sizeof(range), "%u", *uid);
+ ranges = concat_and_free(ranges, " ", range);
+ if (++args_per_command % 18 == 0) {
+ cndc("network users add %u %s", netid, ranges);
+ free(ranges);
+ ranges = NULL;
+ }
+ }
+ }
+
+ if (ranges)
+ cndc("network users add %u %s", netid, ranges);
+}
+
+static void set_dnses(unsigned int netid, const char *dnses)
+{
+ size_t len = strlen(dnses);
+ if (len > (1<<16))
+ return;
+ _cleanup_free_ char *mutable = xstrdup(dnses);
+ _cleanup_free_ char *dns_shell_arglist = xmalloc(len * 4 + 1);
+ _cleanup_free_ char *dns_search_shell_arglist = xmalloc(len * 4 + 1);
+ _cleanup_free_ char *dns_function_arglist = xmalloc(len * 4 + 1);
+ _cleanup_free_ char *dns_search_function_arglist = xmalloc(len * 4 + 1);
+ _cleanup_free_ char *arg = xmalloc(len + 4);
+ _cleanup_free_ char **dns_list = NULL;
+ _cleanup_free_ char **dns_search_list = NULL;
+ _cleanup_binder_ AIBinder *handle = NULL;
+ _cleanup_regfree_ regex_t regex_ipnothost = { 0 };
+ size_t dns_list_size = 0, dns_search_list_size = 0;
+ bool is_ip;
+
+ if (!len)
+ return;
+
+ xregcomp(&regex_ipnothost, "(^[0-9.]+$)|(^.*:.*$)", REG_EXTENDED | REG_NOSUB);
+ for (char *dns = strtok(mutable, ", \t\n"); dns; dns = strtok(NULL, ", \t\n")) {
+ if (strchr(dns, '\'') || strchr(dns, '\\'))
+ continue;
+ ++*(!regexec(&regex_ipnothost, dns, 0, NULL, 0) ? &dns_list_size : &dns_search_list_size);
+ }
+ if (!dns_list_size)
+ return;
+ dns_list = xcalloc(dns_list_size + 1, sizeof(*dns_list));
+ dns_search_list = xcalloc(dns_search_list_size + 1, sizeof(*dns_search_list));
+ free(mutable);
+ mutable = xstrdup(dnses);
+
+ dns_shell_arglist[0] = '\0';
+ dns_search_shell_arglist[0] = '\0';
+ dns_function_arglist[0] = '\0';
+ dns_search_function_arglist[0] = '\0';
+ dns_list_size = 0;
+ dns_search_list_size = 0;
+ for (char *dns = strtok(mutable, ", \t\n"); dns; dns = strtok(NULL, ", \t\n")) {
+ if (strchr(dns, '\'') || strchr(dns, '\\'))
+ continue;
+ is_ip = !regexec(&regex_ipnothost, dns, 0, NULL, 0);
+ snprintf(arg, len + 3, "'%s' ", dns);
+ strncat(is_ip ? dns_shell_arglist : dns_search_shell_arglist, arg, len * 4 - 1);
+ snprintf(arg, len + 2, (is_ip ? dns_function_arglist[0] : dns_search_function_arglist[0]) == '\0' ? "%s" : ", %s", dns);
+ strncat(is_ip ? dns_function_arglist : dns_search_function_arglist, arg, len * 4 - 1);
+ *(is_ip ? &dns_list[dns_list_size++] : &dns_search_list[dns_search_list_size++]) = dns;
+ }
+
+ if ((handle = dnsresolver_get_handle())) {
+ binder_status_t status;
+
+ printf("[#] <binder>::dnsResolver->createNetworkCache(%u)\n", netid);
+ status = dnsresolver_create_network_cache(handle, netid);
+ if (status != 0) {
+ fprintf(stderr, "Error: unable to create network cache\n");
+ exit(ENONET);
+ }
+
+ struct dnsresolver_params params = {
+ .netid = netid,
+ .sample_validity_seconds = DNSRESOLVER_SAMPLE_VALIDITY,
+ .success_threshold = DNSRESOLVER_SUCCESS_THRESHOLD,
+ .min_samples = DNSRESOLVER_MIN_SAMPLES,
+ .max_samples = DNSRESOLVER_MAX_SAMPLES,
+ .base_timeout_msec = DNSRESOLVER_BASE_TIMEOUT,
+ .retry_count = DNSRESOLVER_RETRY_COUNT,
+ .servers = dns_list,
+ .domains = dns_search_list,
+ .tls_name = "",
+ .tls_servers = (char *[]){NULL},
+ .tls_fingerprints = (char *[]){NULL}
+ };
+
+ printf("[#] <binder>::dnsResolver->setResolverConfiguration(%u, [%s], [%s], %d, %d, %d, %d, %d, %d, [], [])\n",
+ netid, dns_function_arglist, dns_search_function_arglist, DNSRESOLVER_SAMPLE_VALIDITY,
+ DNSRESOLVER_SUCCESS_THRESHOLD, DNSRESOLVER_MIN_SAMPLES, DNSRESOLVER_MAX_SAMPLES,
+ DNSRESOLVER_BASE_TIMEOUT, DNSRESOLVER_RETRY_COUNT);
+ status = dnsresolver_set_resolver_configuration(handle, &params);
+
+ if (status != 0) {
+ fprintf(stderr, "Error: unable to set DNS servers through Binder: %d\n", status);
+ exit(ENONET);
+ }
+ } else
+ cndc("resolver setnetdns %u '%s' %s", netid, dns_search_shell_arglist, dns_shell_arglist);
+}
+
+static void add_addr(const char *iface, const char *addr)
+{
+ if (strchr(addr, ':')) {
+ cndc("interface ipv6 %s enable", iface);
+ cmd("ip -6 addr add '%s' dev %s", addr, iface);
+ } else {
+ _cleanup_free_ char *mut_addr = strdup(addr);
+ char *slash = strchr(mut_addr, '/');
+ unsigned char mask = 32;
+
+ if (slash) {
+ *slash = '\0';
+ mask = atoi(slash + 1);
+ }
+ cndc("interface setcfg %s '%s' %u", iface, mut_addr, mask);
+ }
+}
+
+static void set_addr(const char *iface, const char *addrs)
+{
+ _cleanup_free_ char *mutable = xstrdup(addrs);
+
+ for (char *addr = strtok(mutable, ", \t\n"); addr; addr = strtok(NULL, ", \t\n")) {
+ if (strchr(addr, '\'') || strchr(addr, '\\'))
+ continue;
+ add_addr(iface, addr);
+ }
+}
+
+static int get_route_mtu(const char *endpoint)
+{
+ DEFINE_CMD(c_route);
+ DEFINE_CMD(c_dev);
+ regmatch_t matches[2];
+ _cleanup_regfree_ regex_t regex_mtu = { 0 }, regex_dev = { 0 };
+ char *route, *mtu, *dev;
+
+ xregcomp(&regex_mtu, "mtu ([0-9]+)", REG_EXTENDED);
+ xregcomp(&regex_dev, "dev ([^ ]+)", REG_EXTENDED);
+
+ if (strcmp(endpoint, "default"))
+ route = cmd_ret(&c_route, "ip -o route get %s", endpoint);
+ else
+ route = cmd_ret(&c_route, "ip -o route show %s", endpoint);
+ if (!route)
+ return -1;
+
+ if (!regexec(&regex_mtu, route, ARRAY_SIZE(matches), matches, 0)) {
+ route[matches[1].rm_eo] = '\0';
+ mtu = &route[matches[1].rm_so];
+ } else if (!regexec(&regex_dev, route, ARRAY_SIZE(matches), matches, 0)) {
+ route[matches[1].rm_eo] = '\0';
+ dev = &route[matches[1].rm_so];
+ route = cmd_ret(&c_dev, "ip -o link show dev %s", dev);
+ if (!route)
+ return -1;
+ if (regexec(&regex_mtu, route, ARRAY_SIZE(matches), matches, 0))
+ return -1;
+ route[matches[1].rm_eo] = '\0';
+ mtu = &route[matches[1].rm_so];
+ } else
+ return -1;
+ return atoi(mtu);
+}
+
+static void set_mtu(const char *iface, unsigned int mtu)
+{
+ DEFINE_CMD(c_endpoints);
+ _cleanup_regfree_ regex_t regex_endpoint = { 0 };
+ regmatch_t matches[2];
+ int endpoint_mtu, next_mtu;
+
+ if (mtu) {
+ cndc("interface setmtu %s %u", iface, mtu);
+ return;
+ }
+
+ xregcomp(&regex_endpoint, "^\\[?([a-z0-9:.]+)\\]?:[0-9]+$", REG_EXTENDED);
+
+ endpoint_mtu = get_route_mtu("default");
+ if (endpoint_mtu == -1)
+ endpoint_mtu = 1500;
+
+ for (char *endpoint = cmd_ret(&c_endpoints, "wg show %s endpoints", iface); endpoint; endpoint = cmd_ret(&c_endpoints, NULL)) {
+ if (regexec(&regex_endpoint, endpoint, ARRAY_SIZE(matches), matches, 0))
+ continue;
+ endpoint[matches[1].rm_eo] = '\0';
+ endpoint = &endpoint[matches[1].rm_so];
+
+ next_mtu = get_route_mtu(endpoint);
+ if (next_mtu > 0 && next_mtu < endpoint_mtu)
+ endpoint_mtu = next_mtu;
+ }
+
+ cndc("interface setmtu %s %d", iface, endpoint_mtu - 80);
+}
+
+static void add_route(const char *iface, unsigned int netid, const char *route)
+{
+ cndc("network route add %u %s %s", netid, iface, route);
+}
+
+static void set_routes(const char *iface, unsigned int netid)
+{
+ DEFINE_CMD(c);
+
+ for (char *allowedips = cmd_ret(&c, "wg show %s allowed-ips", iface); allowedips; allowedips = cmd_ret(&c, NULL)) {
+ char *start = strchr(allowedips, '\t');
+
+ if (!start)
+ continue;
+ ++start;
+ for (char *allowedip = strtok(start, " \n"); allowedip; allowedip = strtok(NULL, " \n")) {
+ if (!strcmp(allowedip, "(none)"))
+ continue;
+ add_route(iface, netid, allowedip);
+ }
+ }
+}
+
+static void set_config(const char *iface, const char *config)
+{
+ FILE *config_writer;
+ _cleanup_free_ char *cmd = concat("wg setconf ", iface, " /proc/self/fd/0", NULL);
+ int ret;
+
+ printf("[#] %s\n", cmd);
+
+ config_writer = popen(cmd, "w");
+ if (!config_writer) {
+ perror("Error: popen");
+ exit(errno);
+ }
+ if (fputs(config, config_writer) < 0) {
+ perror("Error: fputs");
+ exit(errno);
+ }
+ ret = pclose(config_writer);
+ if (ret)
+ exit(WIFEXITED(ret) ? WEXITSTATUS(ret) : EIO);
+}
+
+static void broadcast_change(void)
+{
+ const char *pkg = getenv("CALLING_PACKAGE");
+
+ if (!pkg || strcmp(pkg, WG_PACKAGE_NAME))
+ cmd("am broadcast -a com.wireguard.android.action.REFRESH_TUNNEL_STATES " WG_PACKAGE_NAME);
+}
+
+static void print_search_paths(FILE *file, const char *prefix)
+{
+ _cleanup_free_ char *paths = strdup(WG_CONFIG_SEARCH_PATHS);
+
+ for (char *path = strtok(paths, " "); path; path = strtok(NULL, " "))
+ fprintf(file, "%s%s\n", prefix, path);
+}
+
+static void cmd_usage(const char *program)
+{
+ printf( "Usage: %s [ up | down ] [ CONFIG_FILE | INTERFACE ]\n"
+ "\n"
+ " CONFIG_FILE is a configuration file, whose filename is the interface name\n"
+ " followed by `.conf'. Otherwise, INTERFACE is an interface name, with\n"
+ " configuration found at:\n\n", program);
+ print_search_paths(stdout, " - ");
+ printf( "\n It is to be readable by wg(8)'s `setconf' sub-command, with the exception\n"
+ " of the following additions to the [Interface] section, which are handled by\n"
+ " this program:\n\n"
+ " - Address: may be specified one or more times and contains one or more\n"
+ " IP addresses (with an optional CIDR mask) to be set for the interface.\n"
+ " - MTU: an optional MTU for the interface; if unspecified, auto-calculated.\n"
+ " - DNS: an optional DNS server to use while the device is up.\n"
+ " - ExcludedApplications: optional blacklist of applications to exclude from the tunnel.\n\n"
+ " - IncludedApplications: optional whitelist of applications to include in the tunnel.\n\n"
+ " See wg-quick(8) for more info and examples.\n");
+}
+
+static char *cleanup_iface = NULL;
+
+static void cmd_up_cleanup(void)
+{
+ is_exiting = true;
+ if (cleanup_iface)
+ del_if(cleanup_iface);
+ free(cleanup_iface);
+}
+
+static void cmd_up(const char *iface, const char *config, unsigned int mtu, const char *addrs, const char *dnses, const char *excluded_applications, const char *included_applications)
+{
+ DEFINE_CMD(c);
+ unsigned int netid = 0;
+ uint16_t listen_port;
+
+ if (cmd_ret(&c, "ip link show dev %s 2>/dev/null", iface)) {
+ fprintf(stderr, "Error: %s already exists\n", iface);
+ exit(EEXIST);
+ }
+
+ cleanup_iface = xstrdup(iface);
+ atexit(cmd_up_cleanup);
+
+ add_if(iface);
+ set_config(iface, config);
+ listen_port = determine_listen_port(iface);
+ up_if(&netid, iface, listen_port);
+ set_addr(iface, addrs);
+ set_dnses(netid, dnses);
+ set_routes(iface, netid);
+ set_mtu(iface, mtu);
+ set_users(netid, excluded_applications, included_applications);
+ broadcast_change();
+
+ free(cleanup_iface);
+ cleanup_iface = NULL;
+ exit(EXIT_SUCCESS);
+}
+
+static void cmd_down(const char *iface)
+{
+ DEFINE_CMD(c);
+ bool found = false;
+
+ char *ifaces = cmd_ret(&c, "wg show interfaces");
+ if (ifaces) {
+ for (char *eiface = strtok(ifaces, " \n"); eiface; eiface = strtok(NULL, " \n")) {
+ if (!strcmp(iface, eiface)) {
+ found = true;
+ break;
+ }
+ }
+ }
+ if (!found) {
+ fprintf(stderr, "Error: %s is not a WireGuard interface\n", iface);
+ exit(EMEDIUMTYPE);
+ }
+
+ del_if(iface);
+ broadcast_change();
+ exit(EXIT_SUCCESS);
+}
+
+static void parse_options(char **iface, char **config, unsigned int *mtu, char **addrs, char **dnses, char **excluded_applications, char **included_applications, const char *arg)
+{
+ _cleanup_fclose_ FILE *file = NULL;
+ _cleanup_free_ char *line = NULL;
+ _cleanup_free_ char *filename = NULL;
+ _cleanup_free_ char *paths = strdup(WG_CONFIG_SEARCH_PATHS);
+ _cleanup_regfree_ regex_t regex_iface = { 0 }, regex_conf = { 0 };
+ regmatch_t matches[2];
+ struct stat sbuf;
+ size_t n = 0;
+ bool in_interface_section = false;
+
+ *iface = *config = *addrs = *dnses = NULL;
+ *mtu = 0;
+
+ xregcomp(&regex_iface, "^[a-zA-Z0-9_=+.-]{1,15}$", REG_EXTENDED | REG_NOSUB);
+ xregcomp(&regex_conf, "/?([a-zA-Z0-9_=+.-]{1,15})\\.conf$", REG_EXTENDED);
+
+ if (!regexec(&regex_iface, arg, 0, NULL, 0)) {
+ for (char *path = strtok(paths, " "); path; path = strtok(NULL, " ")) {
+ free(filename);
+ if (asprintf(&filename, "%s/%s.conf", path, arg) < 0) {
+ perror("Error: asprintf");
+ exit(errno);
+ }
+ file = fopen(filename, "r");
+ if (file)
+ break;
+ }
+ if (!file) {
+ fprintf(stderr, "Error: Unable to find configuration file for `%s' in:\n", arg);
+ print_search_paths(stderr, "- ");
+ exit(errno);
+ }
+ } else {
+ filename = xstrdup(arg);
+ file = fopen(filename, "r");
+ if (!file) {
+ fprintf(stderr, "Error: Unable to find configuration file at `%s'\n", filename);
+ exit(errno);
+ }
+ }
+
+ if (regexec(&regex_conf, filename, ARRAY_SIZE(matches), matches, 0)) {
+ fprintf(stderr, "Error: The config file must be a valid interface name, followed by .conf\n");
+ exit(EINVAL);
+ }
+
+ if (fstat(fileno(file), &sbuf) < 0) {
+ perror("Error: fstat");
+ exit(errno);
+ }
+ if (sbuf.st_mode & 0007)
+ fprintf(stderr, "Warning: `%s' is world accessible\n", filename);
+
+ filename[matches[1].rm_eo] = '\0';
+ *iface = xstrdup(&filename[matches[1].rm_so]);
+
+ while (getline(&line, &n, file) >= 0) {
+ size_t len = strlen(line), j = 0;
+ if (len > (1<<16))
+ return;
+ _cleanup_free_ char *clean = xmalloc(len + 1);
+
+ for (size_t i = 0; i < len; ++i) {
+ if (!isspace(line[i]))
+ clean[j++] = line[i];
+ }
+ clean[j] = '\0';
+
+ if (clean[0] == '[')
+ in_interface_section = false;
+ if (!strcasecmp(clean, "[Interface]"))
+ in_interface_section = true;
+ if (in_interface_section) {
+ if (!strncasecmp(clean, "Address=", 8) && j > 8) {
+ *addrs = concat_and_free(*addrs, ",", clean + 8);
+ continue;
+ } else if (!strncasecmp(clean, "DNS=", 4) && j > 4) {
+ *dnses = concat_and_free(*dnses, ",", clean + 4);
+ continue;
+ } else if (!strncasecmp(clean, "ExcludedApplications=", 21) && j > 4) {
+ *excluded_applications = concat_and_free(*excluded_applications, ",", clean + 21);
+ continue;
+ } else if (!strncasecmp(clean, "IncludedApplications=", 21) && j > 4) {
+ *included_applications = concat_and_free(*included_applications, ",", clean + 21);
+ continue;
+ } else if (!strncasecmp(clean, "MTU=", 4) && j > 4) {
+ *mtu = atoi(clean + 4);
+ continue;
+ }
+ }
+ *config = concat_and_free(*config, "", line);
+ }
+
+ if (!*iface)
+ *iface = xstrdup("");
+ if (!*config)
+ *config = xstrdup("");
+ if (!*addrs)
+ *addrs = xstrdup("");
+ if (!*dnses)
+ *dnses = xstrdup("");
+}
+
+int main(int argc, char *argv[])
+{
+ _cleanup_free_ char *iface = NULL;
+ _cleanup_free_ char *config = NULL;
+ _cleanup_free_ char *addrs = NULL;
+ _cleanup_free_ char *dnses = NULL;
+ _cleanup_free_ char *excluded_applications = NULL;
+ _cleanup_free_ char *included_applications = NULL;
+ unsigned int mtu;
+ char prop[PROP_VALUE_MAX + 1];
+
+ if (__system_property_get("ro.build.version.sdk", prop))
+ sdk_version = atoi(prop);
+
+ if (argc == 2 && (!strcmp(argv[1], "help") || !strcmp(argv[1], "--help") || !strcmp(argv[1], "-h")))
+ cmd_usage(argv[0]);
+ else if (argc == 3 && !strcmp(argv[1], "up")) {
+ auto_su(argc, argv);
+ parse_options(&iface, &config, &mtu, &addrs, &dnses, &excluded_applications, &included_applications, argv[2]);
+ cmd_up(iface, config, mtu, addrs, dnses, excluded_applications, included_applications);
+ } else if (argc == 3 && !strcmp(argv[1], "down")) {
+ auto_su(argc, argv);
+ parse_options(&iface, &config, &mtu, &addrs, &dnses, &excluded_applications, &included_applications, argv[2]);
+ cmd_down(iface);
+ } else {
+ cmd_usage(argv[0]);
+ return 1;
+ }
+ return 0;
+}
diff --git a/contrib/wireguard-tools/wg-quick/darwin.bash b/contrib/wireguard-tools/wg-quick/darwin.bash
new file mode 100755
--- /dev/null
+++ b/contrib/wireguard-tools/wg-quick/darwin.bash
@@ -0,0 +1,522 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+#
+
+set -e -o pipefail
+shopt -s extglob
+export LC_ALL=C
+
+SELF="${BASH_SOURCE[0]}"
+[[ $SELF == */* ]] || SELF="./$SELF"
+SELF="$(cd "${SELF%/*}" && pwd -P)/${SELF##*/}"
+export PATH="/usr/bin:/bin:/usr/sbin:/sbin:${SELF%/*}:$PATH"
+
+WG_CONFIG=""
+INTERFACE=""
+ADDRESSES=( )
+MTU=""
+DNS=( )
+DNS_SEARCH=( )
+TABLE=""
+PRE_UP=( )
+POST_UP=( )
+PRE_DOWN=( )
+POST_DOWN=( )
+SAVE_CONFIG=0
+CONFIG_FILE=""
+PROGRAM="${0##*/}"
+ARGS=( "$@" )
+
+cmd() {
+ echo "[#] $*" >&2
+ "$@"
+}
+
+die() {
+ echo "$PROGRAM: $*" >&2
+ exit 1
+}
+
+[[ ${BASH_VERSINFO[0]} -ge 4 ]] || die "Version mismatch: bash ${BASH_VERSINFO[0]} detected, when bash 4+ required"
+
+CONFIG_SEARCH_PATHS=( /etc/wireguard /usr/local/etc/wireguard )
+
+parse_options() {
+ local interface_section=0 line key value stripped path v
+ CONFIG_FILE="$1"
+ if [[ $CONFIG_FILE =~ ^[a-zA-Z0-9_=+.-]{1,15}$ ]]; then
+ for path in "${CONFIG_SEARCH_PATHS[@]}"; do
+ CONFIG_FILE="$path/$1.conf"
+ [[ -e $CONFIG_FILE ]] && break
+ done
+ fi
+ [[ -e $CONFIG_FILE ]] || die "\`$CONFIG_FILE' does not exist"
+ [[ $CONFIG_FILE =~ (^|/)([a-zA-Z0-9_=+.-]{1,15})\.conf$ ]] || die "The config file must be a valid interface name, followed by .conf"
+ CONFIG_FILE="$(cd "${CONFIG_FILE%/*}" && pwd -P)/${CONFIG_FILE##*/}"
+ ((($(stat -f '0%#p' "$CONFIG_FILE") & $(stat -f '0%#p' "${CONFIG_FILE%/*}") & 0007) == 0)) || echo "Warning: \`$CONFIG_FILE' is world accessible" >&2
+ INTERFACE="${BASH_REMATCH[2]}"
+ shopt -s nocasematch
+ while read -r line || [[ -n $line ]]; do
+ stripped="${line%%\#*}"
+ key="${stripped%%=*}"; key="${key##*([[:space:]])}"; key="${key%%*([[:space:]])}"
+ value="${stripped#*=}"; value="${value##*([[:space:]])}"; value="${value%%*([[:space:]])}"
+ [[ $key == "["* ]] && interface_section=0
+ [[ $key == "[Interface]" ]] && interface_section=1
+ if [[ $interface_section -eq 1 ]]; then
+ case "$key" in
+ Address) ADDRESSES+=( ${value//,/ } ); continue ;;
+ MTU) MTU="$value"; continue ;;
+ DNS) for v in ${value//,/ }; do
+ [[ $v =~ (^[0-9.]+$)|(^.*:.*$) ]] && DNS+=( $v ) || DNS_SEARCH+=( $v )
+ done; continue ;;
+ Table) TABLE="$value"; continue ;;
+ PreUp) PRE_UP+=( "$value" ); continue ;;
+ PreDown) PRE_DOWN+=( "$value" ); continue ;;
+ PostUp) POST_UP+=( "$value" ); continue ;;
+ PostDown) POST_DOWN+=( "$value" ); continue ;;
+ SaveConfig) read_bool SAVE_CONFIG "$value"; continue ;;
+ esac
+ fi
+ WG_CONFIG+="$line"$'\n'
+ done < "$CONFIG_FILE"
+ shopt -u nocasematch
+}
+
+detect_launchd() {
+ unset LAUNCHED_BY_LAUNCHD
+ local line
+ while read -r line; do
+ if [[ $line =~ ^\s*domain\ =\ ]]; then
+ LAUNCHED_BY_LAUNCHD=1
+ break
+ fi
+ done < <(launchctl procinfo $$ 2>/dev/null)
+}
+
+read_bool() {
+ case "$2" in
+ true) printf -v "$1" 1 ;;
+ false) printf -v "$1" 0 ;;
+ *) die "\`$2' is neither true nor false"
+ esac
+}
+
+auto_su() {
+ [[ $UID == 0 ]] || exec sudo -p "$PROGRAM must be run as root. Please enter the password for %u to continue: " -- "$BASH" -- "$SELF" "${ARGS[@]}"
+}
+
+get_real_interface() {
+ local interface diff
+ wg show interfaces >/dev/null
+ [[ -f "/var/run/wireguard/$INTERFACE.name" ]] || return 1
+ interface="$(< "/var/run/wireguard/$INTERFACE.name")"
+ [[ -n $interface && -S "/var/run/wireguard/$interface.sock" ]] || return 1
+ diff=$(( $(stat -f %m "/var/run/wireguard/$interface.sock" 2>/dev/null || echo 200) - $(stat -f %m "/var/run/wireguard/$INTERFACE.name" 2>/dev/null || echo 100) ))
+ [[ $diff -ge 2 || $diff -le -2 ]] && return 1
+ REAL_INTERFACE="$interface"
+ echo "[+] Interface for $INTERFACE is $REAL_INTERFACE" >&2
+ return 0
+}
+
+add_if() {
+ export WG_TUN_NAME_FILE="/var/run/wireguard/$INTERFACE.name"
+ mkdir -p "/var/run/wireguard/"
+ cmd "${WG_QUICK_USERSPACE_IMPLEMENTATION:-wireguard-go}" utun
+ get_real_interface
+}
+
+del_routes() {
+ [[ -n $REAL_INTERFACE ]] || return 0
+ local todelete=( ) destination gateway netif
+ while read -r destination _ _ _ _ netif _; do
+ [[ $netif == "$REAL_INTERFACE" ]] && todelete+=( "$destination" )
+ done < <(netstat -nr -f inet)
+ for destination in "${todelete[@]}"; do
+ cmd route -q -n delete -inet "$destination" >/dev/null || true
+ done
+ todelete=( )
+ while read -r destination gateway _ netif; do
+ [[ $netif == "$REAL_INTERFACE" || ( $netif == lo* && $gateway == "$REAL_INTERFACE" ) ]] && todelete+=( "$destination" )
+ done < <(netstat -nr -f inet6)
+ for destination in "${todelete[@]}"; do
+ cmd route -q -n delete -inet6 "$destination" >/dev/null || true
+ done
+ for destination in "${ENDPOINTS[@]}"; do
+ if [[ $destination == *:* ]]; then
+ cmd route -q -n delete -inet6 "$destination" >/dev/null || true
+ else
+ cmd route -q -n delete -inet "$destination" >/dev/null || true
+ fi
+ done
+}
+
+del_if() {
+ [[ -z $REAL_INTERFACE ]] || cmd rm -f "/var/run/wireguard/$REAL_INTERFACE.sock"
+ cmd rm -f "/var/run/wireguard/$INTERFACE.name"
+}
+
+up_if() {
+ cmd ifconfig "$REAL_INTERFACE" up
+}
+
+add_addr() {
+ if [[ $1 == *:* ]]; then
+ cmd ifconfig "$REAL_INTERFACE" inet6 "$1" alias
+ else
+ cmd ifconfig "$REAL_INTERFACE" inet "$1" "${1%%/*}" alias
+ fi
+}
+
+set_mtu() {
+ # TODO: use better set_mtu algorithm from freebsd.bash
+ local mtu=0 current_mtu=-1 destination netif defaultif
+ if [[ -n $MTU ]]; then
+ cmd ifconfig "$REAL_INTERFACE" mtu "$MTU"
+ return
+ fi
+ while read -r destination _ _ _ _ netif _; do
+ if [[ $destination == default ]]; then
+ defaultif="$netif"
+ break
+ fi
+ done < <(netstat -nr -f inet)
+ [[ -n $defaultif && $(ifconfig "$defaultif") =~ mtu\ ([0-9]+) ]] && mtu="${BASH_REMATCH[1]}"
+ [[ $mtu -gt 0 ]] || mtu=1500
+ mtu=$(( mtu - 80 ))
+ [[ $(ifconfig "$REAL_INTERFACE") =~ mtu\ ([0-9]+) ]] && current_mtu="${BASH_REMATCH[1]}"
+ [[ $mtu -eq $current_mtu ]] || cmd ifconfig "$REAL_INTERFACE" mtu "$mtu"
+}
+
+collect_gateways() {
+ local destination gateway
+
+ GATEWAY4=""
+ while read -r destination gateway _; do
+ [[ $destination == default && $gateway != "link#"* ]] || continue
+ GATEWAY4="$gateway"
+ break
+ done < <(netstat -nr -f inet)
+
+ GATEWAY6=""
+ while read -r destination gateway _; do
+ [[ $destination == default && $gateway != "link#"* ]] || continue
+ GATEWAY6="$gateway"
+ break
+ done < <(netstat -nr -f inet6)
+}
+
+collect_endpoints() {
+ ENDPOINTS=( )
+ while read -r _ endpoint; do
+ [[ $endpoint =~ ^\[?([a-z0-9:.]+)\]?:[0-9]+$ ]] || continue
+ ENDPOINTS+=( "${BASH_REMATCH[1]}" )
+ done < <(wg show "$REAL_INTERFACE" endpoints)
+}
+
+declare -A SERVICE_DNS
+declare -A SERVICE_DNS_SEARCH
+collect_new_service_dns() {
+ local service get_response
+ local -A found_services
+ { read -r _ && while read -r service; do
+ [[ $service == "*"* ]] && service="${service:1}"
+ found_services["$service"]=1
+ [[ -n ${SERVICE_DNS["$service"]} ]] && continue
+ get_response="$(cmd networksetup -getdnsservers "$service")"
+ [[ $get_response == *" "* ]] && get_response="Empty"
+ [[ -n $get_response ]] && SERVICE_DNS["$service"]="$get_response"
+ get_response="$(cmd networksetup -getsearchdomains "$service")"
+ [[ $get_response == *" "* ]] && get_response="Empty"
+ [[ -n $get_response ]] && SERVICE_DNS_SEARCH["$service"]="$get_response"
+ done; } < <(networksetup -listallnetworkservices)
+
+ for service in "${!SERVICE_DNS[@]}"; do
+ if ! [[ -n ${found_services["$service"]} ]]; then
+ unset SERVICE_DNS["$service"]
+ unset SERVICE_DNS_SEARCH["$service"]
+ fi
+ done
+}
+
+set_endpoint_direct_route() {
+ local old_endpoints endpoint old_gateway4 old_gateway6 remove_all_old=0 added=( )
+ old_endpoints=( "${ENDPOINTS[@]}" )
+ old_gateway4="$GATEWAY4"
+ old_gateway6="$GATEWAY6"
+ collect_gateways
+ collect_endpoints
+
+ [[ $old_gateway4 != "$GATEWAY4" || $old_gateway6 != "$GATEWAY6" ]] && remove_all_old=1
+
+ if [[ $remove_all_old -eq 1 ]]; then
+ for endpoint in "${ENDPOINTS[@]}"; do
+ [[ " ${old_endpoints[*]} " == *" $endpoint "* ]] || old_endpoints+=( "$endpoint" )
+ done
+ fi
+
+ for endpoint in "${old_endpoints[@]}"; do
+ [[ $remove_all_old -eq 0 && " ${ENDPOINTS[*]} " == *" $endpoint "* ]] && continue
+ if [[ $endpoint == *:* && $AUTO_ROUTE6 -eq 1 ]]; then
+ cmd route -q -n delete -inet6 "$endpoint" >/dev/null 2>&1 || true
+ elif [[ $AUTO_ROUTE4 -eq 1 ]]; then
+ cmd route -q -n delete -inet "$endpoint" >/dev/null 2>&1 || true
+ fi
+ done
+
+ for endpoint in "${ENDPOINTS[@]}"; do
+ if [[ $remove_all_old -eq 0 && " ${old_endpoints[*]} " == *" $endpoint "* ]]; then
+ added+=( "$endpoint" )
+ continue
+ fi
+ if [[ $endpoint == *:* && $AUTO_ROUTE6 -eq 1 ]]; then
+ if [[ -n $GATEWAY6 ]]; then
+ cmd route -q -n add -inet6 "$endpoint" -gateway "$GATEWAY6" >/dev/null || true
+ else
+ # Prevent routing loop
+ cmd route -q -n add -inet6 "$endpoint" ::1 -blackhole >/dev/null || true
+ fi
+ added+=( "$endpoint" )
+ elif [[ $AUTO_ROUTE4 -eq 1 ]]; then
+ if [[ -n $GATEWAY4 ]]; then
+ cmd route -q -n add -inet "$endpoint" -gateway "$GATEWAY4" >/dev/null || true
+ else
+ # Prevent routing loop
+ cmd route -q -n add -inet "$endpoint" 127.0.0.1 -blackhole >/dev/null || true
+ fi
+ added+=( "$endpoint" )
+ fi
+ done
+ ENDPOINTS=( "${added[@]}" )
+}
+
+set_dns() {
+ collect_new_service_dns
+ local service response
+ for service in "${!SERVICE_DNS[@]}"; do
+ while read -r response; do
+ [[ $response == *Error* ]] && echo "$response" >&2
+ done < <(
+ cmd networksetup -setdnsservers "$service" "${DNS[@]}"
+ if [[ ${#DNS_SEARCH[@]} -eq 0 ]]; then
+ cmd networksetup -setsearchdomains "$service" Empty
+ else
+ cmd networksetup -setsearchdomains "$service" "${DNS_SEARCH[@]}"
+ fi
+ )
+ done
+}
+
+del_dns() {
+ local service response
+ for service in "${!SERVICE_DNS[@]}"; do
+ while read -r response; do
+ [[ $response == *Error* ]] && echo "$response" >&2
+ done < <(
+ cmd networksetup -setdnsservers "$service" ${SERVICE_DNS["$service"]} || true
+ cmd networksetup -setsearchdomains "$service" ${SERVICE_DNS_SEARCH["$service"]} || true
+ )
+ done
+}
+
+monitor_daemon() {
+ echo "[+] Backgrounding route monitor" >&2
+ (trap 'del_routes; del_dns; exit 0' INT TERM EXIT
+ exec >/dev/null 2>&1
+ exec 19< <(exec route -n monitor)
+ local event bpid=$BASHPID mpid=$!
+ [[ ${#DNS[@]} -gt 0 ]] && trap set_dns ALRM
+ # TODO: this should also check to see if the endpoint actually changes
+ # in response to incoming packets, and then call set_endpoint_direct_route
+ # then too. That function should be able to gracefully cleanup if the
+ # endpoints change.
+ while read -u 19 -r event; do
+ [[ $event == RTM_* ]] || continue
+ ifconfig "$REAL_INTERFACE" >/dev/null 2>&1 || break
+ [[ $AUTO_ROUTE4 -eq 1 || $AUTO_ROUTE6 -eq 1 ]] && set_endpoint_direct_route
+ [[ -z $MTU ]] && set_mtu
+ if [[ ${#DNS[@]} -gt 0 ]]; then
+ set_dns
+ sleep 2 && kill -ALRM $bpid 2>/dev/null &
+ fi
+ done
+ kill $mpid) &
+ [[ -n $LAUNCHED_BY_LAUNCHD ]] || disown
+}
+
+add_route() {
+ [[ $TABLE != off ]] || return 0
+
+ local family=inet
+ [[ $1 == *:* ]] && family=inet6
+
+ if [[ $1 == */0 && ( -z $TABLE || $TABLE == auto ) ]]; then
+ if [[ $1 == *:* ]]; then
+ AUTO_ROUTE6=1
+ cmd route -q -n add -inet6 ::/1 -interface "$REAL_INTERFACE" >/dev/null
+ cmd route -q -n add -inet6 8000::/1 -interface "$REAL_INTERFACE" >/dev/null
+ else
+ AUTO_ROUTE4=1
+ cmd route -q -n add -inet 0.0.0.0/1 -interface "$REAL_INTERFACE" >/dev/null
+ cmd route -q -n add -inet 128.0.0.0/1 -interface "$REAL_INTERFACE" >/dev/null
+ fi
+ else
+ [[ $TABLE == main || $TABLE == auto || -z $TABLE ]] || die "Darwin only supports TABLE=auto|main|off"
+ [[ $(route -n get "-$family" "$1" 2>/dev/null) =~ interface:\ $REAL_INTERFACE$'\n' ]] || cmd route -q -n add -$family "$1" -interface "$REAL_INTERFACE" >/dev/null
+
+ fi
+}
+
+set_config() {
+ cmd wg setconf "$REAL_INTERFACE" <(echo "$WG_CONFIG")
+}
+
+save_config() {
+ local old_umask new_config current_config address cmd
+ new_config=$'[Interface]\n'
+ while read -r address; do
+ [[ $address =~ inet6?\ ([^ ]+) ]] && new_config+="Address = ${BASH_REMATCH[1]}"$'\n'
+ done < <(ifconfig "$REAL_INTERFACE")
+ # TODO: actually determine current DNS for interface
+ for address in "${DNS[@]}"; do
+ new_config+="DNS = $address"$'\n'
+ done
+ [[ -n $MTU ]] && new_config+="MTU = $MTU"$'\n'
+ [[ -n $TABLE ]] && new_config+="Table = $TABLE"$'\n'
+ [[ $SAVE_CONFIG -eq 0 ]] || new_config+=$'SaveConfig = true\n'
+ for cmd in "${PRE_UP[@]}"; do
+ new_config+="PreUp = $cmd"$'\n'
+ done
+ for cmd in "${POST_UP[@]}"; do
+ new_config+="PostUp = $cmd"$'\n'
+ done
+ for cmd in "${PRE_DOWN[@]}"; do
+ new_config+="PreDown = $cmd"$'\n'
+ done
+ for cmd in "${POST_DOWN[@]}"; do
+ new_config+="PostDown = $cmd"$'\n'
+ done
+ old_umask="$(umask)"
+ umask 077
+ current_config="$(cmd wg showconf "$REAL_INTERFACE")"
+ trap 'rm -f "$CONFIG_FILE.tmp"; exit' INT TERM EXIT
+ echo "${current_config/\[Interface\]$'\n'/$new_config}" > "$CONFIG_FILE.tmp" || die "Could not write configuration file"
+ sync "$CONFIG_FILE.tmp"
+ mv "$CONFIG_FILE.tmp" "$CONFIG_FILE" || die "Could not move configuration file"
+ trap - INT TERM EXIT
+ umask "$old_umask"
+}
+
+execute_hooks() {
+ local hook
+ for hook in "$@"; do
+ hook="${hook//%i/$REAL_INTERFACE}"
+ hook="${hook//%I/$INTERFACE}"
+ echo "[#] $hook" >&2
+ (eval "$hook")
+ done
+}
+
+cmd_usage() {
+ cat >&2 <<-_EOF
+ Usage: $PROGRAM [ up | down | save | strip ] [ CONFIG_FILE | INTERFACE ]
+
+ CONFIG_FILE is a configuration file, whose filename is the interface name
+ followed by \`.conf'. Otherwise, INTERFACE is an interface name, with
+ configuration found at:
+ ${CONFIG_SEARCH_PATHS[@]/%//INTERFACE.conf}.
+ It is to be readable by wg(8)'s \`setconf' sub-command, with the exception
+ of the following additions to the [Interface] section, which are handled
+ by $PROGRAM:
+
+ - Address: may be specified one or more times and contains one or more
+ IP addresses (with an optional CIDR mask) to be set for the interface.
+ - DNS: an optional DNS server to use while the device is up.
+ - MTU: an optional MTU for the interface; if unspecified, auto-calculated.
+ - Table: an optional routing table to which routes will be added; if
+ unspecified or \`auto', the default table is used. If \`off', no routes
+ are added. Besides \`auto' and \`off', only \`main' is supported on
+ this platform.
+ - PreUp, PostUp, PreDown, PostDown: script snippets which will be executed
+ by bash(1) at the corresponding phases of the link, most commonly used
+ to configure DNS. The string \`%i' is expanded to INTERFACE.
+ - SaveConfig: if set to \`true', the configuration is saved from the current
+ state of the interface upon shutdown.
+
+ See wg-quick(8) for more info and examples.
+ _EOF
+}
+
+cmd_up() {
+ local i
+ get_real_interface && die "\`$INTERFACE' already exists as \`$REAL_INTERFACE'"
+ trap 'del_if; del_routes; exit' INT TERM EXIT
+ execute_hooks "${PRE_UP[@]}"
+ add_if
+ set_config
+ for i in "${ADDRESSES[@]}"; do
+ add_addr "$i"
+ done
+ set_mtu
+ up_if
+ for i in $(while read -r _ i; do for i in $i; do [[ $i =~ ^[0-9a-z:.]+/[0-9]+$ ]] && echo "$i"; done; done < <(wg show "$REAL_INTERFACE" allowed-ips) | sort -nr -k 2 -t /); do
+ add_route "$i"
+ done
+ [[ $AUTO_ROUTE4 -eq 1 || $AUTO_ROUTE6 -eq 1 ]] && set_endpoint_direct_route
+ [[ ${#DNS[@]} -gt 0 ]] && set_dns
+ monitor_daemon
+ execute_hooks "${POST_UP[@]}"
+ trap - INT TERM EXIT
+}
+
+cmd_down() {
+ if ! get_real_interface || [[ " $(wg show interfaces) " != *" $REAL_INTERFACE "* ]]; then
+ die "\`$INTERFACE' is not a WireGuard interface"
+ fi
+ execute_hooks "${PRE_DOWN[@]}"
+ [[ $SAVE_CONFIG -eq 0 ]] || save_config
+ del_if
+ execute_hooks "${POST_DOWN[@]}"
+}
+
+cmd_save() {
+ if ! get_real_interface || [[ " $(wg show interfaces) " != *" $REAL_INTERFACE "* ]]; then
+ die "\`$INTERFACE' is not a WireGuard interface"
+ fi
+ save_config
+}
+
+cmd_strip() {
+ echo "$WG_CONFIG"
+}
+
+# ~~ function override insertion point ~~
+
+if [[ $# -eq 1 && ( $1 == --help || $1 == -h || $1 == help ) ]]; then
+ cmd_usage
+elif [[ $# -eq 2 && $1 == up ]]; then
+ auto_su
+ detect_launchd
+ parse_options "$2"
+ cmd_up
+elif [[ $# -eq 2 && $1 == down ]]; then
+ auto_su
+ parse_options "$2"
+ cmd_down
+elif [[ $# -eq 2 && $1 == save ]]; then
+ auto_su
+ parse_options "$2"
+ cmd_save
+elif [[ $# -eq 2 && $1 == strip ]]; then
+ auto_su
+ parse_options "$2"
+ cmd_strip
+else
+ cmd_usage
+ exit 1
+fi
+
+[[ -n $LAUNCHED_BY_LAUNCHD ]] && wait
+
+exit 0
diff --git a/contrib/wireguard-tools/wg-quick/freebsd.bash b/contrib/wireguard-tools/wg-quick/freebsd.bash
new file mode 100755
--- /dev/null
+++ b/contrib/wireguard-tools/wg-quick/freebsd.bash
@@ -0,0 +1,487 @@
+#!/usr/local/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+#
+
+set -e -o pipefail
+shopt -s extglob
+export LC_ALL=C
+
+exec 3>&2
+SELF="$(readlink -f "${BASH_SOURCE[0]}")"
+export PATH="${SELF%/*}:$PATH"
+
+WG_CONFIG=""
+INTERFACE=""
+ADDRESSES=( )
+MTU=""
+DNS=( )
+DNS_SEARCH=( )
+TABLE=""
+PRE_UP=( )
+POST_UP=( )
+PRE_DOWN=( )
+POST_DOWN=( )
+SAVE_CONFIG=0
+CONFIG_FILE=""
+PROGRAM="${0##*/}"
+ARGS=( "$@" )
+
+cmd() {
+ echo "[#] $*" >&3
+ "$@"
+}
+
+die() {
+ echo "$PROGRAM: $*" >&2
+ exit 1
+}
+
+CONFIG_SEARCH_PATHS=( /etc/wireguard /usr/local/etc/wireguard )
+
+unset ORIGINAL_TMPDIR
+make_temp() {
+ local old_umask
+
+ [[ -v ORIGINAL_TMPDIR ]] && export TMPDIR="$ORIGINAL_TMPDIR"
+ ORIGINAL_TMPDIR="$TMPDIR"
+ [[ -z $TMPDIR ]] && unset TMPDIR
+
+ old_umask="$(umask)"
+ umask 077
+ export TMPDIR="$(mktemp -d)"
+ umask "$old_umask"
+
+ [[ -d $TMPDIR ]] || die "Unable to create safe temporary directory"
+ CLEANUP_TMPDIR="$TMPDIR"
+}
+
+clean_temp() {
+ [[ -n $CLEANUP_TMPDIR ]] && rm -rf "$CLEANUP_TMPDIR"
+}
+
+parse_options() {
+ local interface_section=0 line key value stripped path v
+ CONFIG_FILE="$1"
+ if [[ $CONFIG_FILE =~ ^[a-zA-Z0-9_=+.-]{1,15}$ ]]; then
+ for path in "${CONFIG_SEARCH_PATHS[@]}"; do
+ CONFIG_FILE="$path/$1.conf"
+ [[ -e $CONFIG_FILE ]] && break
+ done
+ fi
+ [[ -e $CONFIG_FILE ]] || die "\`$CONFIG_FILE' does not exist"
+ [[ $CONFIG_FILE =~ (^|/)([a-zA-Z0-9_=+.-]{1,15})\.conf$ ]] || die "The config file must be a valid interface name, followed by .conf"
+ CONFIG_FILE="$(readlink -f "$CONFIG_FILE")"
+ ((($(stat -f '0%#p' "$CONFIG_FILE") & $(stat -f '0%#p' "${CONFIG_FILE%/*}") & 0007) == 0)) || echo "Warning: \`$CONFIG_FILE' is world accessible" >&2
+ INTERFACE="${BASH_REMATCH[2]}"
+ shopt -s nocasematch
+ while read -r line || [[ -n $line ]]; do
+ stripped="${line%%\#*}"
+ key="${stripped%%=*}"; key="${key##*([[:space:]])}"; key="${key%%*([[:space:]])}"
+ value="${stripped#*=}"; value="${value##*([[:space:]])}"; value="${value%%*([[:space:]])}"
+ [[ $key == "["* ]] && interface_section=0
+ [[ $key == "[Interface]" ]] && interface_section=1
+ if [[ $interface_section -eq 1 ]]; then
+ case "$key" in
+ Address) ADDRESSES+=( ${value//,/ } ); continue ;;
+ MTU) MTU="$value"; continue ;;
+ DNS) for v in ${value//,/ }; do
+ [[ $v =~ (^[0-9.]+$)|(^.*:.*$) ]] && DNS+=( $v ) || DNS_SEARCH+=( $v )
+ done; continue ;;
+ Table) TABLE="$value"; continue ;;
+ PreUp) PRE_UP+=( "$value" ); continue ;;
+ PreDown) PRE_DOWN+=( "$value" ); continue ;;
+ PostUp) POST_UP+=( "$value" ); continue ;;
+ PostDown) POST_DOWN+=( "$value" ); continue ;;
+ SaveConfig) read_bool SAVE_CONFIG "$value"; continue ;;
+ esac
+ fi
+ WG_CONFIG+="$line"$'\n'
+ done < "$CONFIG_FILE"
+ shopt -u nocasematch
+}
+
+read_bool() {
+ case "$2" in
+ true) printf -v "$1" 1 ;;
+ false) printf -v "$1" 0 ;;
+ *) die "\`$2' is neither true nor false"
+ esac
+}
+
+auto_su() {
+ [[ $UID == 0 ]] || exec sudo -p "$PROGRAM must be run as root. Please enter the password for %u to continue: " -- "$BASH" -- "$SELF" "${ARGS[@]}"
+}
+
+add_if() {
+ local ret rc
+ if ret="$(cmd ifconfig wg create name "$INTERFACE" 2>&1 >/dev/null)"; then
+ return 0
+ fi
+ rc=$?
+ if [[ $ret == *"ifconfig: ioctl SIOCSIFNAME (set name): File exists"* ]]; then
+ echo "$ret" >&3
+ return $rc
+ fi
+ echo "[!] Missing WireGuard kernel support ($ret). Falling back to slow userspace implementation." >&3
+ cmd "${WG_QUICK_USERSPACE_IMPLEMENTATION:-wireguard-go}" "$INTERFACE"
+}
+
+del_routes() {
+ local todelete=( ) destination gateway netif
+ while read -r destination _ _ _ _ netif _; do
+ [[ $netif == "$INTERFACE" ]] && todelete+=( "$destination" )
+ done < <(netstat -nr -f inet)
+ for destination in "${todelete[@]}"; do
+ cmd route -q -n delete -inet "$destination" || true
+ done
+ todelete=( )
+ while read -r destination gateway _ netif; do
+ [[ $netif == "$INTERFACE" || ( $netif == lo* && $gateway == "$INTERFACE" ) ]] && todelete+=( "$destination" )
+ done < <(netstat -nr -f inet6)
+ for destination in "${todelete[@]}"; do
+ cmd route -q -n delete -inet6 "$destination" || true
+ done
+ for destination in "${ENDPOINTS[@]}"; do
+ if [[ $destination == *:* ]]; then
+ cmd route -q -n delete -inet6 "$destination" || true
+ else
+ cmd route -q -n delete -inet "$destination" || true
+ fi
+ done
+}
+
+del_if() {
+ [[ $HAVE_SET_DNS -eq 0 ]] || unset_dns
+ if [[ -S /var/run/wireguard/$INTERFACE.sock ]]; then
+ cmd rm -f "/var/run/wireguard/$INTERFACE.sock"
+ else
+ cmd ifconfig "$INTERFACE" destroy
+ fi
+ while ifconfig "$INTERFACE" >/dev/null 2>&1; do
+ # HACK: it would be nice to `route monitor` here and wait for RTM_IFANNOUNCE
+ # but it turns out that the announcement is made before the interface
+ # disappears so we sometimes get a hang. So, we're instead left with polling
+ # in a sleep loop like this.
+ sleep 0.1
+ done
+}
+
+up_if() {
+ cmd ifconfig "$INTERFACE" up
+}
+
+add_addr() {
+ if [[ $1 == *:* ]]; then
+ cmd ifconfig "$INTERFACE" inet6 "$1" alias
+ else
+ cmd ifconfig "$INTERFACE" inet "$1" alias
+ fi
+}
+
+set_mtu() {
+ local mtu=0 endpoint output family
+ if [[ -n $MTU ]]; then
+ cmd ifconfig "$INTERFACE" mtu "$MTU"
+ return
+ fi
+ while read -r _ endpoint; do
+ [[ $endpoint =~ ^\[?([a-z0-9:.]+)\]?:[0-9]+$ ]] || continue
+ family=inet
+ [[ ${BASH_REMATCH[1]} == *:* ]] && family=inet6
+ output="$(route -n get "-$family" "${BASH_REMATCH[1]}" || true)"
+ [[ $output =~ interface:\ ([^ ]+)$'\n' && $(ifconfig "${BASH_REMATCH[1]}") =~ mtu\ ([0-9]+) && ${BASH_REMATCH[1]} -gt $mtu ]] && mtu="${BASH_REMATCH[1]}"
+ done < <(wg show "$INTERFACE" endpoints)
+ if [[ $mtu -eq 0 ]]; then
+ read -r output < <(route -n get default || true) || true
+ [[ $output =~ interface:\ ([^ ]+)$'\n' && $(ifconfig "${BASH_REMATCH[1]}") =~ mtu\ ([0-9]+) && ${BASH_REMATCH[1]} -gt $mtu ]] && mtu="${BASH_REMATCH[1]}"
+ fi
+ [[ $mtu -gt 0 ]] || mtu=1500
+ cmd ifconfig "$INTERFACE" mtu $(( mtu - 80 ))
+}
+
+
+collect_gateways() {
+ local destination gateway
+
+ GATEWAY4=""
+ while read -r destination gateway _; do
+ [[ $destination == default ]] || continue
+ GATEWAY4="$gateway"
+ break
+ done < <(netstat -nr -f inet)
+
+ GATEWAY6=""
+ while read -r destination gateway _; do
+ [[ $destination == default ]] || continue
+ GATEWAY6="$gateway"
+ break
+ done < <(netstat -nr -f inet6)
+}
+
+collect_endpoints() {
+ ENDPOINTS=( )
+ while read -r _ endpoint; do
+ [[ $endpoint =~ ^\[?([a-z0-9:.]+)\]?:[0-9]+$ ]] || continue
+ ENDPOINTS+=( "${BASH_REMATCH[1]}" )
+ done < <(wg show "$INTERFACE" endpoints)
+}
+
+set_endpoint_direct_route() {
+ local old_endpoints endpoint old_gateway4 old_gateway6 remove_all_old=0 added=( )
+ old_endpoints=( "${ENDPOINTS[@]}" )
+ old_gateway4="$GATEWAY4"
+ old_gateway6="$GATEWAY6"
+ collect_gateways
+ collect_endpoints
+
+ [[ $old_gateway4 != "$GATEWAY4" || $old_gateway6 != "$GATEWAY6" ]] && remove_all_old=1
+
+ if [[ $remove_all_old -eq 1 ]]; then
+ for endpoint in "${ENDPOINTS[@]}"; do
+ [[ " ${old_endpoints[*]} " == *" $endpoint "* ]] || old_endpoints+=( "$endpoint" )
+ done
+ fi
+
+ for endpoint in "${old_endpoints[@]}"; do
+ [[ $remove_all_old -eq 0 && " ${ENDPOINTS[*]} " == *" $endpoint "* ]] && continue
+ if [[ $endpoint == *:* && $AUTO_ROUTE6 -eq 1 ]]; then
+ cmd route -q -n delete -inet6 "$endpoint" 2>/dev/null || true
+ elif [[ $AUTO_ROUTE4 -eq 1 ]]; then
+ cmd route -q -n delete -inet "$endpoint" 2>/dev/null || true
+ fi
+ done
+
+ for endpoint in "${ENDPOINTS[@]}"; do
+ if [[ $remove_all_old -eq 0 && " ${old_endpoints[*]} " == *" $endpoint "* ]]; then
+ added+=( "$endpoint" )
+ continue
+ fi
+ if [[ $endpoint == *:* && $AUTO_ROUTE6 -eq 1 ]]; then
+ if [[ -n $GATEWAY6 ]]; then
+ cmd route -q -n add -inet6 "$endpoint" -gateway "$GATEWAY6" || true
+ else
+ # Prevent routing loop
+ cmd route -q -n add -inet6 "$endpoint" ::1 -blackhole || true
+ fi
+ added+=( "$endpoint" )
+ elif [[ $AUTO_ROUTE4 -eq 1 ]]; then
+ if [[ -n $GATEWAY4 ]]; then
+ cmd route -q -n add -inet "$endpoint" -gateway "$GATEWAY4" || true
+ else
+ # Prevent routing loop
+ cmd route -q -n add -inet "$endpoint" 127.0.0.1 -blackhole || true
+ fi
+ added+=( "$endpoint" )
+ fi
+ done
+ ENDPOINTS=( "${added[@]}" )
+}
+
+monitor_daemon() {
+ echo "[+] Backgrounding route monitor" >&2
+ (make_temp
+ trap 'del_routes; clean_temp; exit 0' INT TERM EXIT
+ exec >/dev/null 2>&1
+ exec 19< <(exec route -n monitor)
+ local event pid=$!
+ # TODO: this should also check to see if the endpoint actually changes
+ # in response to incoming packets, and then call set_endpoint_direct_route
+ # then too. That function should be able to gracefully cleanup if the
+ # endpoints change.
+ while read -u 19 -r event; do
+ [[ $event == RTM_* ]] || continue
+ ifconfig "$INTERFACE" >/dev/null 2>&1 || break
+ [[ $AUTO_ROUTE4 -eq 1 || $AUTO_ROUTE6 -eq 1 ]] && set_endpoint_direct_route
+ # TODO: set the mtu as well, but only if up
+ done
+ kill $pid) & disown
+}
+
+HAVE_SET_DNS=0
+set_dns() {
+ [[ ${#DNS[@]} -gt 0 ]] || return 0
+ { printf 'nameserver %s\n' "${DNS[@]}"
+ [[ ${#DNS_SEARCH[@]} -eq 0 ]] || printf 'search %s\n' "${DNS_SEARCH[*]}"
+ } | cmd resolvconf -a "$INTERFACE" -x
+ HAVE_SET_DNS=1
+}
+
+unset_dns() {
+ [[ ${#DNS[@]} -gt 0 ]] || return 0
+ cmd resolvconf -d "$INTERFACE"
+}
+
+add_route() {
+ [[ $TABLE != off ]] || return 0
+
+ local family=inet
+ [[ $1 == *:* ]] && family=inet6
+
+ if [[ -n $TABLE && $TABLE != auto ]]; then
+ cmd route -q -n add "-$family" -fib "$TABLE" "$1" -interface "$INTERFACE"
+ elif [[ $1 == */0 ]]; then
+ if [[ $1 == *:* ]]; then
+ AUTO_ROUTE6=1
+ cmd route -q -n add -inet6 ::/1 -interface "$INTERFACE"
+ cmd route -q -n add -inet6 8000::/1 -interface "$INTERFACE"
+ else
+ AUTO_ROUTE4=1
+ cmd route -q -n add -inet 0.0.0.0/1 -interface "$INTERFACE"
+ cmd route -q -n add -inet 128.0.0.0/1 -interface "$INTERFACE"
+ fi
+ else
+ [[ $(route -n get "-$family" "$1" 2>/dev/null) =~ interface:\ $INTERFACE$'\n' ]] || cmd route -q -n add "-$family" "$1" -interface "$INTERFACE"
+ fi
+}
+
+set_config() {
+ echo "$WG_CONFIG" | cmd wg setconf "$INTERFACE" /dev/stdin
+}
+
+save_config() {
+ local old_umask new_config current_config address cmd
+ new_config=$'[Interface]\n'
+ { read -r _; while read -r _ _ _ address _; do
+ new_config+="Address = $address"$'\n'
+ done } < <(netstat -I "$INTERFACE" -n -W -f inet)
+ { read -r _; while read -r _ _ _ address _; do
+ new_config+="Address = $address"$'\n'
+ done } < <(netstat -I "$INTERFACE" -n -W -f inet6)
+ while read -r address; do
+ [[ $address =~ ^nameserver\ ([a-zA-Z0-9_=+:%.-]+)$ ]] && new_config+="DNS = ${BASH_REMATCH[1]}"$'\n'
+ done < <(resolvconf -l "$INTERFACE" 2>/dev/null)
+ [[ -n $MTU ]] && new_config+="MTU = $MTU"$'\n'
+ [[ -n $TABLE ]] && new_config+="Table = $TABLE"$'\n'
+ [[ $SAVE_CONFIG -eq 0 ]] || new_config+=$'SaveConfig = true\n'
+ for cmd in "${PRE_UP[@]}"; do
+ new_config+="PreUp = $cmd"$'\n'
+ done
+ for cmd in "${POST_UP[@]}"; do
+ new_config+="PostUp = $cmd"$'\n'
+ done
+ for cmd in "${PRE_DOWN[@]}"; do
+ new_config+="PreDown = $cmd"$'\n'
+ done
+ for cmd in "${POST_DOWN[@]}"; do
+ new_config+="PostDown = $cmd"$'\n'
+ done
+ old_umask="$(umask)"
+ umask 077
+ current_config="$(cmd wg showconf "$INTERFACE")"
+ trap 'rm -f "$CONFIG_FILE.tmp"; clean_temp; exit' INT TERM EXIT
+ echo "${current_config/\[Interface\]$'\n'/$new_config}" > "$CONFIG_FILE.tmp" || die "Could not write configuration file"
+ sync "$CONFIG_FILE.tmp"
+ mv "$CONFIG_FILE.tmp" "$CONFIG_FILE" || die "Could not move configuration file"
+ trap 'clean_temp; exit' INT TERM EXIT
+ umask "$old_umask"
+}
+
+execute_hooks() {
+ local hook
+ for hook in "$@"; do
+ hook="${hook//%i/$INTERFACE}"
+ echo "[#] $hook" >&2
+ (eval "$hook")
+ done
+}
+
+cmd_usage() {
+ cat >&2 <<-_EOF
+ Usage: $PROGRAM [ up | down | save | strip ] [ CONFIG_FILE | INTERFACE ]
+
+ CONFIG_FILE is a configuration file, whose filename is the interface name
+ followed by \`.conf'. Otherwise, INTERFACE is an interface name, with
+ configuration found at:
+ ${CONFIG_SEARCH_PATHS[@]/%//INTERFACE.conf}.
+ It is to be readable by wg(8)'s \`setconf' sub-command, with the exception
+ of the following additions to the [Interface] section, which are handled
+ by $PROGRAM:
+
+ - Address: may be specified one or more times and contains one or more
+ IP addresses (with an optional CIDR mask) to be set for the interface.
+ - DNS: an optional DNS server to use while the device is up.
+ - MTU: an optional MTU for the interface; if unspecified, auto-calculated.
+ - Table: an optional routing table to which routes will be added; if
+ unspecified or \`auto', the default table is used. If \`off', no routes
+ are added.
+ - PreUp, PostUp, PreDown, PostDown: script snippets which will be executed
+ by bash(1) at the corresponding phases of the link, most commonly used
+ to configure DNS. The string \`%i' is expanded to INTERFACE.
+ - SaveConfig: if set to \`true', the configuration is saved from the current
+ state of the interface upon shutdown.
+
+ See wg-quick(8) for more info and examples.
+ _EOF
+}
+
+cmd_up() {
+ local i
+ [[ -z $(ifconfig "$INTERFACE" 2>/dev/null) ]] || die "\`$INTERFACE' already exists"
+ trap 'del_if; del_routes; clean_temp; exit' INT TERM EXIT
+ execute_hooks "${PRE_UP[@]}"
+ add_if
+ set_config
+ for i in "${ADDRESSES[@]}"; do
+ add_addr "$i"
+ done
+ set_mtu
+ up_if
+ set_dns
+ for i in $(while read -r _ i; do for i in $i; do [[ $i =~ ^[0-9a-z:.]+/[0-9]+$ ]] && echo "$i"; done; done < <(wg show "$INTERFACE" allowed-ips) | sort -nr -k 2 -t /); do
+ add_route "$i"
+ done
+ [[ $AUTO_ROUTE4 -eq 1 || $AUTO_ROUTE6 -eq 1 ]] && set_endpoint_direct_route
+ monitor_daemon
+ execute_hooks "${POST_UP[@]}"
+ trap 'clean_temp; exit' INT TERM EXIT
+}
+
+cmd_down() {
+ [[ " $(wg show interfaces) " == *" $INTERFACE "* ]] || die "\`$INTERFACE' is not a WireGuard interface"
+ execute_hooks "${PRE_DOWN[@]}"
+ [[ $SAVE_CONFIG -eq 0 ]] || save_config
+ del_if
+ unset_dns
+ execute_hooks "${POST_DOWN[@]}"
+}
+
+cmd_save() {
+ [[ " $(wg show interfaces) " == *" $INTERFACE "* ]] || die "\`$INTERFACE' is not a WireGuard interface"
+ save_config
+}
+
+cmd_strip() {
+ echo "$WG_CONFIG"
+}
+
+# ~~ function override insertion point ~~
+
+make_temp
+trap 'clean_temp; exit' INT TERM EXIT
+
+if [[ $# -eq 1 && ( $1 == --help || $1 == -h || $1 == help ) ]]; then
+ cmd_usage
+elif [[ $# -eq 2 && $1 == up ]]; then
+ auto_su
+ parse_options "$2"
+ cmd_up
+elif [[ $# -eq 2 && $1 == down ]]; then
+ auto_su
+ parse_options "$2"
+ cmd_down
+elif [[ $# -eq 2 && $1 == save ]]; then
+ auto_su
+ parse_options "$2"
+ cmd_save
+elif [[ $# -eq 2 && $1 == strip ]]; then
+ auto_su
+ parse_options "$2"
+ cmd_strip
+else
+ cmd_usage
+ exit 1
+fi
+
+exit 0
diff --git a/contrib/wireguard-tools/wg-quick/linux.bash b/contrib/wireguard-tools/wg-quick/linux.bash
new file mode 100755
--- /dev/null
+++ b/contrib/wireguard-tools/wg-quick/linux.bash
@@ -0,0 +1,389 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+#
+
+set -e -o pipefail
+shopt -s extglob
+export LC_ALL=C
+
+SELF="$(readlink -f "${BASH_SOURCE[0]}")"
+export PATH="${SELF%/*}:$PATH"
+
+WG_CONFIG=""
+INTERFACE=""
+ADDRESSES=( )
+MTU=""
+DNS=( )
+DNS_SEARCH=( )
+TABLE=""
+PRE_UP=( )
+POST_UP=( )
+PRE_DOWN=( )
+POST_DOWN=( )
+SAVE_CONFIG=0
+CONFIG_FILE=""
+PROGRAM="${0##*/}"
+ARGS=( "$@" )
+
+cmd() {
+ echo "[#] $*" >&2
+ "$@"
+}
+
+die() {
+ echo "$PROGRAM: $*" >&2
+ exit 1
+}
+
+parse_options() {
+ local interface_section=0 line key value stripped v
+ CONFIG_FILE="$1"
+ [[ $CONFIG_FILE =~ ^[a-zA-Z0-9_=+.-]{1,15}$ ]] && CONFIG_FILE="/etc/wireguard/$CONFIG_FILE.conf"
+ [[ -e $CONFIG_FILE ]] || die "\`$CONFIG_FILE' does not exist"
+ [[ $CONFIG_FILE =~ (^|/)([a-zA-Z0-9_=+.-]{1,15})\.conf$ ]] || die "The config file must be a valid interface name, followed by .conf"
+ CONFIG_FILE="$(readlink -f "$CONFIG_FILE")"
+ ((($(stat -c '0%#a' "$CONFIG_FILE") & $(stat -c '0%#a' "${CONFIG_FILE%/*}") & 0007) == 0)) || echo "Warning: \`$CONFIG_FILE' is world accessible" >&2
+ INTERFACE="${BASH_REMATCH[2]}"
+ shopt -s nocasematch
+ while read -r line || [[ -n $line ]]; do
+ stripped="${line%%\#*}"
+ key="${stripped%%=*}"; key="${key##*([[:space:]])}"; key="${key%%*([[:space:]])}"
+ value="${stripped#*=}"; value="${value##*([[:space:]])}"; value="${value%%*([[:space:]])}"
+ [[ $key == "["* ]] && interface_section=0
+ [[ $key == "[Interface]" ]] && interface_section=1
+ if [[ $interface_section -eq 1 ]]; then
+ case "$key" in
+ Address) ADDRESSES+=( ${value//,/ } ); continue ;;
+ MTU) MTU="$value"; continue ;;
+ DNS) for v in ${value//,/ }; do
+ [[ $v =~ (^[0-9.]+$)|(^.*:.*$) ]] && DNS+=( $v ) || DNS_SEARCH+=( $v )
+ done; continue ;;
+ Table) TABLE="$value"; continue ;;
+ PreUp) PRE_UP+=( "$value" ); continue ;;
+ PreDown) PRE_DOWN+=( "$value" ); continue ;;
+ PostUp) POST_UP+=( "$value" ); continue ;;
+ PostDown) POST_DOWN+=( "$value" ); continue ;;
+ SaveConfig) read_bool SAVE_CONFIG "$value"; continue ;;
+ esac
+ fi
+ WG_CONFIG+="$line"$'\n'
+ done < "$CONFIG_FILE"
+ shopt -u nocasematch
+}
+
+read_bool() {
+ case "$2" in
+ true) printf -v "$1" 1 ;;
+ false) printf -v "$1" 0 ;;
+ *) die "\`$2' is neither true nor false"
+ esac
+}
+
+auto_su() {
+ [[ $UID == 0 ]] || exec sudo -p "$PROGRAM must be run as root. Please enter the password for %u to continue: " -- "$BASH" -- "$SELF" "${ARGS[@]}"
+}
+
+add_if() {
+ local ret
+ if ! cmd ip link add "$INTERFACE" type wireguard; then
+ ret=$?
+ [[ -e /sys/module/wireguard ]] || ! command -v "${WG_QUICK_USERSPACE_IMPLEMENTATION:-wireguard-go}" >/dev/null && exit $ret
+ echo "[!] Missing WireGuard kernel module. Falling back to slow userspace implementation." >&2
+ cmd "${WG_QUICK_USERSPACE_IMPLEMENTATION:-wireguard-go}" "$INTERFACE"
+ fi
+}
+
+del_if() {
+ local table
+ [[ $HAVE_SET_DNS -eq 0 ]] || unset_dns
+ [[ $HAVE_SET_FIREWALL -eq 0 ]] || remove_firewall
+ if [[ -z $TABLE || $TABLE == auto ]] && get_fwmark table && [[ $(wg show "$INTERFACE" allowed-ips) =~ /0(\ |$'\n'|$) ]]; then
+ while [[ $(ip -4 rule show 2>/dev/null) == *"lookup $table"* ]]; do
+ cmd ip -4 rule delete table $table
+ done
+ while [[ $(ip -4 rule show 2>/dev/null) == *"from all lookup main suppress_prefixlength 0"* ]]; do
+ cmd ip -4 rule delete table main suppress_prefixlength 0
+ done
+ while [[ $(ip -6 rule show 2>/dev/null) == *"lookup $table"* ]]; do
+ cmd ip -6 rule delete table $table
+ done
+ while [[ $(ip -6 rule show 2>/dev/null) == *"from all lookup main suppress_prefixlength 0"* ]]; do
+ cmd ip -6 rule delete table main suppress_prefixlength 0
+ done
+ fi
+ cmd ip link delete dev "$INTERFACE"
+}
+
+add_addr() {
+ local proto=-4
+ [[ $1 == *:* ]] && proto=-6
+ cmd ip $proto address add "$1" dev "$INTERFACE"
+}
+
+set_mtu_up() {
+ local mtu=0 endpoint output
+ if [[ -n $MTU ]]; then
+ cmd ip link set mtu "$MTU" up dev "$INTERFACE"
+ return
+ fi
+ while read -r _ endpoint; do
+ [[ $endpoint =~ ^\[?([a-z0-9:.]+)\]?:[0-9]+$ ]] || continue
+ output="$(ip route get "${BASH_REMATCH[1]}" || true)"
+ [[ ( $output =~ mtu\ ([0-9]+) || ( $output =~ dev\ ([^ ]+) && $(ip link show dev "${BASH_REMATCH[1]}") =~ mtu\ ([0-9]+) ) ) && ${BASH_REMATCH[1]} -gt $mtu ]] && mtu="${BASH_REMATCH[1]}"
+ done < <(wg show "$INTERFACE" endpoints)
+ if [[ $mtu -eq 0 ]]; then
+ read -r output < <(ip route show default || true) || true
+ [[ ( $output =~ mtu\ ([0-9]+) || ( $output =~ dev\ ([^ ]+) && $(ip link show dev "${BASH_REMATCH[1]}") =~ mtu\ ([0-9]+) ) ) && ${BASH_REMATCH[1]} -gt $mtu ]] && mtu="${BASH_REMATCH[1]}"
+ fi
+ [[ $mtu -gt 0 ]] || mtu=1500
+ cmd ip link set mtu $(( mtu - 80 )) up dev "$INTERFACE"
+}
+
+resolvconf_iface_prefix() {
+ [[ -f /etc/resolvconf/interface-order ]] || return 0
+ local iface
+ while read -r iface; do
+ [[ $iface =~ ^([A-Za-z0-9-]+)\*$ ]] || continue
+ echo "${BASH_REMATCH[1]}." && return 0
+ done < /etc/resolvconf/interface-order
+}
+
+HAVE_SET_DNS=0
+set_dns() {
+ [[ ${#DNS[@]} -gt 0 ]] || return 0
+ { printf 'nameserver %s\n' "${DNS[@]}"
+ [[ ${#DNS_SEARCH[@]} -eq 0 ]] || printf 'search %s\n' "${DNS_SEARCH[*]}"
+ } | cmd resolvconf -a "$(resolvconf_iface_prefix)$INTERFACE" -m 0 -x
+ HAVE_SET_DNS=1
+}
+
+unset_dns() {
+ [[ ${#DNS[@]} -gt 0 ]] || return 0
+ cmd resolvconf -d "$(resolvconf_iface_prefix)$INTERFACE" -f
+}
+
+add_route() {
+ local proto=-4
+ [[ $1 == *:* ]] && proto=-6
+ [[ $TABLE != off ]] || return 0
+
+ if [[ -n $TABLE && $TABLE != auto ]]; then
+ cmd ip $proto route add "$1" dev "$INTERFACE" table "$TABLE"
+ elif [[ $1 == */0 ]]; then
+ add_default "$1"
+ else
+ [[ -n $(ip $proto route show dev "$INTERFACE" match "$1" 2>/dev/null) ]] || cmd ip $proto route add "$1" dev "$INTERFACE"
+ fi
+}
+
+get_fwmark() {
+ local fwmark
+ fwmark="$(wg show "$INTERFACE" fwmark)" || return 1
+ [[ -n $fwmark && $fwmark != off ]] || return 1
+ printf -v "$1" "%d" "$fwmark"
+ return 0
+}
+
+remove_firewall() {
+ if type -p nft >/dev/null; then
+ local table nftcmd
+ while read -r table; do
+ [[ $table == *" wg-quick-$INTERFACE" ]] && printf -v nftcmd '%sdelete %s\n' "$nftcmd" "$table"
+ done < <(nft list tables 2>/dev/null)
+ [[ -z $nftcmd ]] || cmd nft -f <(echo -n "$nftcmd")
+ fi
+ if type -p iptables >/dev/null; then
+ local line iptables found restore
+ for iptables in iptables ip6tables; do
+ restore="" found=0
+ while read -r line; do
+ [[ $line == "*"* || $line == COMMIT || $line == "-A "*"-m comment --comment \"wg-quick(8) rule for $INTERFACE\""* ]] || continue
+ [[ $line == "-A"* ]] && found=1
+ printf -v restore '%s%s\n' "$restore" "${line/#-A/-D}"
+ done < <($iptables-save 2>/dev/null)
+ [[ $found -ne 1 ]] || echo -n "$restore" | cmd $iptables-restore -n
+ done
+ fi
+}
+
+HAVE_SET_FIREWALL=0
+add_default() {
+ local table line
+ if ! get_fwmark table; then
+ table=51820
+ while [[ -n $(ip -4 route show table $table 2>/dev/null) || -n $(ip -6 route show table $table 2>/dev/null) ]]; do
+ ((table++))
+ done
+ cmd wg set "$INTERFACE" fwmark $table
+ fi
+ local proto=-4 iptables=iptables pf=ip
+ [[ $1 == *:* ]] && proto=-6 iptables=ip6tables pf=ip6
+ cmd ip $proto rule add not fwmark $table table $table
+ cmd ip $proto rule add table main suppress_prefixlength 0
+ cmd ip $proto route add "$1" dev "$INTERFACE" table $table
+
+ local marker="-m comment --comment \"wg-quick(8) rule for $INTERFACE\"" restore=$'*raw\n' nftable="wg-quick-$INTERFACE" nftcmd
+ printf -v nftcmd '%sadd table %s %s\n' "$nftcmd" "$pf" "$nftable"
+ printf -v nftcmd '%sadd chain %s %s preraw { type filter hook prerouting priority -300; }\n' "$nftcmd" "$pf" "$nftable"
+ printf -v nftcmd '%sadd chain %s %s premangle { type filter hook prerouting priority -150; }\n' "$nftcmd" "$pf" "$nftable"
+ printf -v nftcmd '%sadd chain %s %s postmangle { type filter hook postrouting priority -150; }\n' "$nftcmd" "$pf" "$nftable"
+ while read -r line; do
+ [[ $line =~ .*inet6?\ ([0-9a-f:.]+)/[0-9]+.* ]] || continue
+ printf -v restore '%s-I PREROUTING ! -i %s -d %s -m addrtype ! --src-type LOCAL -j DROP %s\n' "$restore" "$INTERFACE" "${BASH_REMATCH[1]}" "$marker"
+ printf -v nftcmd '%sadd rule %s %s preraw iifname != "%s" %s daddr %s fib saddr type != local drop\n' "$nftcmd" "$pf" "$nftable" "$INTERFACE" "$pf" "${BASH_REMATCH[1]}"
+ done < <(ip -o $proto addr show dev "$INTERFACE" 2>/dev/null)
+ printf -v restore '%sCOMMIT\n*mangle\n-I POSTROUTING -m mark --mark %d -p udp -j CONNMARK --save-mark %s\n-I PREROUTING -p udp -j CONNMARK --restore-mark %s\nCOMMIT\n' "$restore" $table "$marker" "$marker"
+ printf -v nftcmd '%sadd rule %s %s postmangle meta l4proto udp mark %d ct mark set mark \n' "$nftcmd" "$pf" "$nftable" $table
+ printf -v nftcmd '%sadd rule %s %s premangle meta l4proto udp meta mark set ct mark \n' "$nftcmd" "$pf" "$nftable"
+ [[ $proto == -4 ]] && cmd sysctl -q net.ipv4.conf.all.src_valid_mark=1
+ if type -p nft >/dev/null; then
+ cmd nft -f <(echo -n "$nftcmd")
+ else
+ echo -n "$restore" | cmd $iptables-restore -n
+ fi
+ HAVE_SET_FIREWALL=1
+ return 0
+}
+
+set_config() {
+ cmd wg setconf "$INTERFACE" <(echo "$WG_CONFIG")
+}
+
+save_config() {
+ local old_umask new_config current_config address cmd
+ [[ $(ip -all -brief address show dev "$INTERFACE") =~ ^$INTERFACE\ +\ [A-Z]+\ +(.+)$ ]] || true
+ new_config=$'[Interface]\n'
+ for address in ${BASH_REMATCH[1]}; do
+ new_config+="Address = $address"$'\n'
+ done
+ while read -r address; do
+ [[ $address =~ ^nameserver\ ([a-zA-Z0-9_=+:%.-]+)$ ]] && new_config+="DNS = ${BASH_REMATCH[1]}"$'\n'
+ done < <(resolvconf -l "$(resolvconf_iface_prefix)$INTERFACE" 2>/dev/null || cat "/etc/resolvconf/run/interface/$(resolvconf_iface_prefix)$INTERFACE" 2>/dev/null)
+ [[ -n $MTU && $(ip link show dev "$INTERFACE") =~ mtu\ ([0-9]+) ]] && new_config+="MTU = ${BASH_REMATCH[1]}"$'\n'
+ [[ -n $TABLE ]] && new_config+="Table = $TABLE"$'\n'
+ [[ $SAVE_CONFIG -eq 0 ]] || new_config+=$'SaveConfig = true\n'
+ for cmd in "${PRE_UP[@]}"; do
+ new_config+="PreUp = $cmd"$'\n'
+ done
+ for cmd in "${POST_UP[@]}"; do
+ new_config+="PostUp = $cmd"$'\n'
+ done
+ for cmd in "${PRE_DOWN[@]}"; do
+ new_config+="PreDown = $cmd"$'\n'
+ done
+ for cmd in "${POST_DOWN[@]}"; do
+ new_config+="PostDown = $cmd"$'\n'
+ done
+ old_umask="$(umask)"
+ umask 077
+ current_config="$(cmd wg showconf "$INTERFACE")"
+ trap 'rm -f "$CONFIG_FILE.tmp"; exit' INT TERM EXIT
+ echo "${current_config/\[Interface\]$'\n'/$new_config}" > "$CONFIG_FILE.tmp" || die "Could not write configuration file"
+ sync "$CONFIG_FILE.tmp"
+ mv "$CONFIG_FILE.tmp" "$CONFIG_FILE" || die "Could not move configuration file"
+ trap - INT TERM EXIT
+ umask "$old_umask"
+}
+
+execute_hooks() {
+ local hook
+ for hook in "$@"; do
+ hook="${hook//%i/$INTERFACE}"
+ echo "[#] $hook" >&2
+ (eval "$hook")
+ done
+}
+
+cmd_usage() {
+ cat >&2 <<-_EOF
+ Usage: $PROGRAM [ up | down | save | strip ] [ CONFIG_FILE | INTERFACE ]
+
+ CONFIG_FILE is a configuration file, whose filename is the interface name
+ followed by \`.conf'. Otherwise, INTERFACE is an interface name, with
+ configuration found at /etc/wireguard/INTERFACE.conf. It is to be readable
+ by wg(8)'s \`setconf' sub-command, with the exception of the following additions
+ to the [Interface] section, which are handled by $PROGRAM:
+
+ - Address: may be specified one or more times and contains one or more
+ IP addresses (with an optional CIDR mask) to be set for the interface.
+ - DNS: an optional DNS server to use while the device is up.
+ - MTU: an optional MTU for the interface; if unspecified, auto-calculated.
+ - Table: an optional routing table to which routes will be added; if
+ unspecified or \`auto', the default table is used. If \`off', no routes
+ are added.
+ - PreUp, PostUp, PreDown, PostDown: script snippets which will be executed
+ by bash(1) at the corresponding phases of the link, most commonly used
+ to configure DNS. The string \`%i' is expanded to INTERFACE.
+ - SaveConfig: if set to \`true', the configuration is saved from the current
+ state of the interface upon shutdown.
+
+ See wg-quick(8) for more info and examples.
+ _EOF
+}
+
+cmd_up() {
+ local i
+ [[ -z $(ip link show dev "$INTERFACE" 2>/dev/null) ]] || die "\`$INTERFACE' already exists"
+ trap 'del_if; exit' INT TERM EXIT
+ execute_hooks "${PRE_UP[@]}"
+ add_if
+ set_config
+ for i in "${ADDRESSES[@]}"; do
+ add_addr "$i"
+ done
+ set_mtu_up
+ set_dns
+ for i in $(while read -r _ i; do for i in $i; do [[ $i =~ ^[0-9a-z:.]+/[0-9]+$ ]] && echo "$i"; done; done < <(wg show "$INTERFACE" allowed-ips) | sort -nr -k 2 -t /); do
+ add_route "$i"
+ done
+ execute_hooks "${POST_UP[@]}"
+ trap - INT TERM EXIT
+}
+
+cmd_down() {
+ [[ " $(wg show interfaces) " == *" $INTERFACE "* ]] || die "\`$INTERFACE' is not a WireGuard interface"
+ execute_hooks "${PRE_DOWN[@]}"
+ [[ $SAVE_CONFIG -eq 0 ]] || save_config
+ del_if
+ unset_dns || true
+ remove_firewall || true
+ execute_hooks "${POST_DOWN[@]}"
+}
+
+cmd_save() {
+ [[ " $(wg show interfaces) " == *" $INTERFACE "* ]] || die "\`$INTERFACE' is not a WireGuard interface"
+ save_config
+}
+
+cmd_strip() {
+ echo "$WG_CONFIG"
+}
+
+# ~~ function override insertion point ~~
+
+if [[ $# -eq 1 && ( $1 == --help || $1 == -h || $1 == help ) ]]; then
+ cmd_usage
+elif [[ $# -eq 2 && $1 == up ]]; then
+ auto_su
+ parse_options "$2"
+ cmd_up
+elif [[ $# -eq 2 && $1 == down ]]; then
+ auto_su
+ parse_options "$2"
+ cmd_down
+elif [[ $# -eq 2 && $1 == save ]]; then
+ auto_su
+ parse_options "$2"
+ cmd_save
+elif [[ $# -eq 2 && $1 == strip ]]; then
+ auto_su
+ parse_options "$2"
+ cmd_strip
+else
+ cmd_usage
+ exit 1
+fi
+
+exit 0
diff --git a/contrib/wireguard-tools/wg-quick/openbsd.bash b/contrib/wireguard-tools/wg-quick/openbsd.bash
new file mode 100755
--- /dev/null
+++ b/contrib/wireguard-tools/wg-quick/openbsd.bash
@@ -0,0 +1,481 @@
+#!/usr/local/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+#
+
+set -e -o pipefail
+shopt -s extglob
+export LC_ALL=C
+
+exec 3>&2
+SELF="$(readlink -f "${BASH_SOURCE[0]}")"
+export PATH="${SELF%/*}:$PATH"
+
+WG_CONFIG=""
+INTERFACE=""
+ADDRESSES=( )
+MTU=""
+DNS=( )
+DNS_SEARCH=( )
+TABLE=""
+PRE_UP=( )
+POST_UP=( )
+PRE_DOWN=( )
+POST_DOWN=( )
+SAVE_CONFIG=0
+CONFIG_FILE=""
+PROGRAM="${0##*/}"
+ARGS=( "$@" )
+
+cmd() {
+ echo "[#] $*" >&3
+ "$@"
+}
+
+die() {
+ echo "$PROGRAM: $*" >&2
+ exit 1
+}
+
+parse_options() {
+ local interface_section=0 line key value stripped
+ CONFIG_FILE="$1"
+ [[ $CONFIG_FILE =~ ^[a-zA-Z0-9_=+.-]{1,15}$ ]] && CONFIG_FILE="/etc/wireguard/$CONFIG_FILE.conf"
+ [[ -e $CONFIG_FILE ]] || die "\`$CONFIG_FILE' does not exist"
+ [[ $CONFIG_FILE =~ (^|/)([a-zA-Z0-9_=+.-]{1,15})\.conf$ ]] || die "The config file must be a valid interface name, followed by .conf"
+ CONFIG_FILE="$(readlink -f "$CONFIG_FILE")"
+ ((($(stat -f '0%#p' "$CONFIG_FILE") & $(stat -f '0%#p' "${CONFIG_FILE%/*}") & 0007) == 0)) || echo "Warning: \`$CONFIG_FILE' is world accessible" >&2
+ INTERFACE="${BASH_REMATCH[2]}"
+ shopt -s nocasematch
+ while read -r line || [[ -n $line ]]; do
+ stripped="${line%%\#*}"
+ key="${stripped%%=*}"; key="${key##*([[:space:]])}"; key="${key%%*([[:space:]])}"
+ value="${stripped#*=}"; value="${value##*([[:space:]])}"; value="${value%%*([[:space:]])}"
+ [[ $key == "["* ]] && interface_section=0
+ [[ $key == "[Interface]" ]] && interface_section=1
+ if [[ $interface_section -eq 1 ]]; then
+ case "$key" in
+ Address) ADDRESSES+=( ${value//,/ } ); continue ;;
+ MTU) MTU="$value"; continue ;;
+ DNS) for v in ${value//,/ }; do
+ [[ $v =~ (^[0-9.]+$)|(^.*:.*$) ]] && DNS+=( $v ) || DNS_SEARCH+=( $v )
+ done; continue ;;
+ Table) TABLE="$value"; continue ;;
+ PreUp) PRE_UP+=( "$value" ); continue ;;
+ PreDown) PRE_DOWN+=( "$value" ); continue ;;
+ PostUp) POST_UP+=( "$value" ); continue ;;
+ PostDown) POST_DOWN+=( "$value" ); continue ;;
+ SaveConfig) read_bool SAVE_CONFIG "$value"; continue ;;
+ esac
+ fi
+ WG_CONFIG+="$line"$'\n'
+ done < "$CONFIG_FILE"
+ shopt -u nocasematch
+}
+
+read_bool() {
+ case "$2" in
+ true) printf -v "$1" 1 ;;
+ false) printf -v "$1" 0 ;;
+ *) die "\`$2' is neither true nor false"
+ esac
+}
+
+auto_su() {
+ [[ $UID == 0 ]] || exec doas -- "$BASH" -- "$SELF" "${ARGS[@]}"
+}
+
+
+get_real_interface() {
+ local interface line
+ while IFS= read -r line; do
+ if [[ $line =~ ^([a-z]+[0-9]+):\ .+ ]]; then
+ interface="${BASH_REMATCH[1]}"
+ continue
+ fi
+ if [[ $interface == wg* && $line =~ ^\ description:\ wg-quick:\ (.+) && ${BASH_REMATCH[1]} == "$INTERFACE" ]]; then
+ REAL_INTERFACE="$interface"
+ return 0
+ fi
+ done < <(ifconfig)
+ return 1
+}
+
+add_if() {
+ while true; do
+ local -A existing_ifs="( $(wg show interfaces | sed 's/\([^ ]*\)/[\1]=1/g') )"
+ local index ret
+ for ((index=0; index <= 2147483647; ++index)); do [[ -v existing_ifs[wg$index] ]] || break; done
+ if ret="$(cmd ifconfig wg$index create description "wg-quick: $INTERFACE" 2>&1)"; then
+ REAL_INTERFACE="wg$index"
+ return 0
+ fi
+ [[ $ret == *"ifconfig: SIOCIFCREATE: File exists"* ]] && continue
+ echo "$ret" >&3
+ return 1
+ done
+}
+
+del_routes() {
+ local todelete=( ) destination gateway netif
+ [[ -n $REAL_INTERFACE ]] || return 0
+ while read -r destination _ _ _ _ netif _; do
+ [[ $netif == "$REAL_INTERFACE" ]] && todelete+=( "$destination" )
+ done < <(netstat -nr -f inet)
+ for destination in "${todelete[@]}"; do
+ cmd route -q -n delete -inet "$destination" || true
+ done
+ todelete=( )
+ while read -r destination gateway _ netif; do
+ [[ $netif == "$REAL_INTERFACE" || ( $netif == lo* && $gateway == "$REAL_INTERFACE" ) ]] && todelete+=( "$destination" )
+ done < <(netstat -nr -f inet6)
+ for destination in "${todelete[@]}"; do
+ cmd route -q -n delete -inet6 "$destination" || true
+ done
+ for destination in "${ENDPOINTS[@]}"; do
+ if [[ $destination == *:* ]]; then
+ cmd route -q -n delete -inet6 "$destination" || true
+ else
+ cmd route -q -n delete -inet "$destination" || true
+ fi
+ done
+}
+
+del_if() {
+ unset_dns
+ [[ -n $REAL_INTERFACE ]] && cmd ifconfig $REAL_INTERFACE destroy
+}
+
+up_if() {
+ cmd ifconfig "$REAL_INTERFACE" up
+}
+
+add_addr() {
+ local family
+ if [[ $1 == *:* ]]; then
+ family=inet6
+ [[ -n $FIRSTADDR6 ]] || FIRSTADDR6="${1%/*}"
+ else
+ family=inet
+ [[ -n $FIRSTADDR4 ]] || FIRSTADDR4="${1%/*}"
+ fi
+ cmd ifconfig "$REAL_INTERFACE" $family "$1" alias
+}
+
+set_mtu() {
+ local mtu=0 endpoint output family
+ if [[ -n $MTU ]]; then
+ cmd ifconfig "$REAL_INTERFACE" mtu "$MTU"
+ return
+ fi
+ while read -r _ endpoint; do
+ [[ $endpoint =~ ^\[?([a-z0-9:.]+)\]?:[0-9]+$ ]] || continue
+ family=inet
+ [[ ${BASH_REMATCH[1]} == *:* ]] && family=inet6
+ output="$(route -n get "-$family" "${BASH_REMATCH[1]}" || true)"
+ [[ $output =~ interface:\ ([^ ]+)$'\n' && $(ifconfig "${BASH_REMATCH[1]}") =~ mtu\ ([0-9]+) && ${BASH_REMATCH[1]} -gt $mtu ]] && mtu="${BASH_REMATCH[1]}"
+ done < <(wg show "$REAL_INTERFACE" endpoints)
+ if [[ $mtu -eq 0 ]]; then
+ read -r output < <(route -n get default || true) || true
+ [[ $output =~ interface:\ ([^ ]+)$'\n' && $(ifconfig "${BASH_REMATCH[1]}") =~ mtu\ ([0-9]+) && ${BASH_REMATCH[1]} -gt $mtu ]] && mtu="${BASH_REMATCH[1]}"
+ fi
+ [[ $mtu -gt 0 ]] || mtu=1500
+ cmd ifconfig "$REAL_INTERFACE" mtu $(( mtu - 80 ))
+}
+
+
+collect_gateways() {
+ local destination gateway
+
+ GATEWAY4=""
+ while read -r destination gateway _; do
+ [[ $destination == default ]] || continue
+ GATEWAY4="$gateway"
+ break
+ done < <(netstat -nr -f inet)
+
+ GATEWAY6=""
+ while read -r destination gateway _; do
+ [[ $destination == default ]] || continue
+ GATEWAY6="$gateway"
+ break
+ done < <(netstat -nr -f inet6)
+}
+
+collect_endpoints() {
+ ENDPOINTS=( )
+ while read -r _ endpoint; do
+ [[ $endpoint =~ ^\[?([a-z0-9:.]+)\]?:[0-9]+$ ]] || continue
+ ENDPOINTS+=( "${BASH_REMATCH[1]}" )
+ done < <(wg show "$REAL_INTERFACE" endpoints)
+}
+
+set_endpoint_direct_route() {
+ local old_endpoints endpoint old_gateway4 old_gateway6 remove_all_old=0 added=( )
+ old_endpoints=( "${ENDPOINTS[@]}" )
+ old_gateway4="$GATEWAY4"
+ old_gateway6="$GATEWAY6"
+ collect_gateways
+ collect_endpoints
+
+ [[ $old_gateway4 != "$GATEWAY4" || $old_gateway6 != "$GATEWAY6" ]] && remove_all_old=1
+
+ if [[ $remove_all_old -eq 1 ]]; then
+ for endpoint in "${ENDPOINTS[@]}"; do
+ [[ " ${old_endpoints[*]} " == *" $endpoint "* ]] || old_endpoints+=( "$endpoint" )
+ done
+ fi
+
+ for endpoint in "${old_endpoints[@]}"; do
+ [[ $remove_all_old -eq 0 && " ${ENDPOINTS[*]} " == *" $endpoint "* ]] && continue
+ if [[ $endpoint == *:* && $AUTO_ROUTE6 -eq 1 ]]; then
+ cmd route -q -n delete -inet6 "$endpoint" 2>/dev/null || true
+ elif [[ $AUTO_ROUTE4 -eq 1 ]]; then
+ cmd route -q -n delete -inet "$endpoint" 2>/dev/null || true
+ fi
+ done
+
+ for endpoint in "${ENDPOINTS[@]}"; do
+ if [[ $remove_all_old -eq 0 && " ${old_endpoints[*]} " == *" $endpoint "* ]]; then
+ added+=( "$endpoint" )
+ continue
+ fi
+ if [[ $endpoint == *:* && $AUTO_ROUTE6 -eq 1 ]]; then
+ if [[ -n $GATEWAY6 ]]; then
+ cmd route -q -n add -inet6 "$endpoint" -gateway "$GATEWAY6" || true
+ else
+ # Prevent routing loop
+ cmd route -q -n add -inet6 "$endpoint" ::1 -blackhole || true
+ fi
+ added+=( "$endpoint" )
+ elif [[ $AUTO_ROUTE4 -eq 1 ]]; then
+ if [[ -n $GATEWAY4 ]]; then
+ cmd route -q -n add -inet "$endpoint" -gateway "$GATEWAY4" || true
+ else
+ # Prevent routing loop
+ cmd route -q -n add -inet "$endpoint" 127.0.0.1 -blackhole || true
+ fi
+ added+=( "$endpoint" )
+ fi
+ done
+ ENDPOINTS=( "${added[@]}" )
+}
+
+monitor_daemon() {
+ echo "[+] Backgrounding route monitor" >&2
+ (trap 'del_routes; exit 0' INT TERM EXIT
+ exec >/dev/null 2>&1
+ exec 19< <(exec route -n monitor)
+ local event pid=$!
+ # TODO: this should also check to see if the endpoint actually changes
+ # in response to incoming packets, and then call set_endpoint_direct_route
+ # then too. That function should be able to gracefully cleanup if the
+ # endpoints change.
+ while read -u 19 -r event; do
+ [[ $event == RTM_* ]] || continue
+ ifconfig "$REAL_INTERFACE" >/dev/null 2>&1 || break
+ [[ $AUTO_ROUTE4 -eq 1 || $AUTO_ROUTE6 -eq 1 ]] && set_endpoint_direct_route
+ # TODO: set the mtu as well, but only if up
+ done
+ kill $pid) & disown
+}
+
+set_dns() {
+ [[ ${#DNS[@]} -gt 0 ]] || return 0
+
+ # TODO: add exclusive support for nameservers
+ if pgrep -qx unwind; then
+ echo "[!] WARNING: unwind will leak DNS queries" >&2
+ elif pgrep -qx resolvd; then
+ echo "[!] WARNING: resolvd may leak DNS queries" >&2
+ else
+ echo "[+] resolvd is not running, DNS will not be configured" >&2
+ return 0
+ fi
+
+ cmd cp /etc/resolv.conf "/etc/resolv.conf.wg-quick-backup.$INTERFACE"
+ [[ ${#DNS_SEARCH[@]} -eq 0 ]] || cmd printf 'search %s\n' "${DNS_SEARCH[*]}" > /etc/resolv.conf
+ route nameserver ${REAL_INTERFACE} ${DNS[@]}
+}
+
+unset_dns() {
+ [[ -f "/etc/resolv.conf.wg-quick-backup.$INTERFACE" ]] || return 0
+ route nameserver ${REAL_INTERFACE}
+ cmd mv "/etc/resolv.conf.wg-quick-backup.$INTERFACE" /etc/resolv.conf
+}
+
+add_route() {
+ [[ $TABLE != off ]] || return 0
+ local family ifaceroute
+
+ if [[ $1 == *:* ]]; then
+ family=inet6
+ [[ -n $FIRSTADDR6 ]] || die "Local IPv6 address must be set to have routes"
+ ifaceroute="$FIRSTADDR6"
+ else
+ family=inet
+ [[ -n $FIRSTADDR4 ]] || die "Local IPv4 address must be set to have routes"
+ ifaceroute="$FIRSTADDR4"
+ fi
+
+ if [[ -n $TABLE && $TABLE != auto ]]; then
+ cmd route -q -n -T "$TABLE" add "-$family" "$1" -iface "$ifaceroute"
+ elif [[ $1 == */0 ]]; then
+ if [[ $1 == *:* ]]; then
+ AUTO_ROUTE6=1
+ cmd route -q -n add -inet6 ::/1 -iface "$ifaceroute"
+ cmd route -q -n add -inet6 8000::/1 -iface "$ifaceroute"
+ else
+ AUTO_ROUTE4=1
+ cmd route -q -n add -inet 0.0.0.0/1 -iface "$ifaceroute"
+ cmd route -q -n add -inet 128.0.0.0/1 -iface "$ifaceroute"
+ fi
+ else
+ [[ $(route -n get "-$family" "$1" 2>/dev/null) =~ interface:\ $REAL_INTERFACE$'\n' ]] || cmd route -q -n add "-$family" "$1" -iface "$ifaceroute"
+ fi
+}
+
+set_config() {
+ cmd wg setconf "$REAL_INTERFACE" <(echo "$WG_CONFIG")
+}
+
+save_config() {
+ local old_umask new_config current_config address network cmd
+ new_config=$'[Interface]\n'
+ { read -r _; while read -r _ _ network address _; do
+ [[ $network == *Link* ]] || new_config+="Address = $address"$'\n'
+ done } < <(netstat -I "$REAL_INTERFACE" -n -v)
+ # TODO: actually determine current DNS for interface
+ for address in "${DNS[@]}"; do
+ new_config+="DNS = $address"$'\n'
+ done
+ [[ -n $MTU ]] && new_config+="MTU = $MTU"$'\n'
+ [[ -n $TABLE ]] && new_config+="Table = $TABLE"$'\n'
+ [[ $SAVE_CONFIG -eq 0 ]] || new_config+=$'SaveConfig = true\n'
+ for cmd in "${PRE_UP[@]}"; do
+ new_config+="PreUp = $cmd"$'\n'
+ done
+ for cmd in "${POST_UP[@]}"; do
+ new_config+="PostUp = $cmd"$'\n'
+ done
+ for cmd in "${PRE_DOWN[@]}"; do
+ new_config+="PreDown = $cmd"$'\n'
+ done
+ for cmd in "${POST_DOWN[@]}"; do
+ new_config+="PostDown = $cmd"$'\n'
+ done
+ old_umask="$(umask)"
+ umask 077
+ current_config="$(cmd wg showconf "$REAL_INTERFACE")"
+ trap 'rm -f "$CONFIG_FILE.tmp"; exit' INT TERM EXIT
+ echo "${current_config/\[Interface\]$'\n'/$new_config}" > "$CONFIG_FILE.tmp" || die "Could not write configuration file"
+ sync "$CONFIG_FILE.tmp"
+ mv "$CONFIG_FILE.tmp" "$CONFIG_FILE" || die "Could not move configuration file"
+ trap - INT TERM EXIT
+ umask "$old_umask"
+}
+
+execute_hooks() {
+ local hook
+ for hook in "$@"; do
+ hook="${hook//%i/$REAL_INTERFACE}"
+ hook="${hook//%I/$INTERFACE}"
+ echo "[#] $hook" >&2
+ (eval "$hook")
+ done
+}
+
+cmd_usage() {
+ cat >&2 <<-_EOF
+ Usage: $PROGRAM [ up | down | save | strip ] [ CONFIG_FILE | INTERFACE ]
+
+ CONFIG_FILE is a configuration file, whose filename is the interface name
+ followed by \`.conf'. Otherwise, INTERFACE is an interface name, with
+ configuration found at /etc/wireguard/INTERFACE.conf. It is to be readable
+ by wg(8)'s \`setconf' sub-command, with the exception of the following additions
+ to the [Interface] section, which are handled by $PROGRAM:
+
+ - Address: may be specified one or more times and contains one or more
+ IP addresses (with an optional CIDR mask) to be set for the interface.
+ - DNS: an optional DNS server to use while the device is up.
+ - MTU: an optional MTU for the interface; if unspecified, auto-calculated.
+ - Table: an optional routing table to which routes will be added; if
+ unspecified or \`auto', the default table is used. If \`off', no routes
+ are added.
+ - PreUp, PostUp, PreDown, PostDown: script snippets which will be executed
+ by bash(1) at the corresponding phases of the link, most commonly used
+ to configure DNS. The string \`%i' is expanded to INTERFACE.
+ - SaveConfig: if set to \`true', the configuration is saved from the current
+ state of the interface upon shutdown.
+
+ See wg-quick(8) for more info and examples.
+ _EOF
+}
+
+cmd_up() {
+ local i
+ get_real_interface && die "\`$INTERFACE' already exists as \`$REAL_INTERFACE'"
+ trap 'del_if; del_routes; exit' INT TERM EXIT
+ execute_hooks "${PRE_UP[@]}"
+ add_if
+ set_config
+ for i in "${ADDRESSES[@]}"; do
+ add_addr "$i"
+ done
+ set_mtu
+ up_if
+ set_dns
+ for i in $(while read -r _ i; do for i in $i; do [[ $i =~ ^[0-9a-z:.]+/[0-9]+$ ]] && echo "$i"; done; done < <(wg show "$REAL_INTERFACE" allowed-ips) | sort -nr -k 2 -t /); do
+ add_route "$i"
+ done
+ [[ $AUTO_ROUTE4 -eq 1 || $AUTO_ROUTE6 -eq 1 ]] && set_endpoint_direct_route
+ monitor_daemon
+ execute_hooks "${POST_UP[@]}"
+ trap - INT TERM EXIT
+}
+
+cmd_down() {
+ get_real_interface || die "\`$INTERFACE' is not a WireGuard interface"
+ execute_hooks "${PRE_DOWN[@]}"
+ [[ $SAVE_CONFIG -eq 0 ]] || save_config
+ del_if
+ unset_dns
+ execute_hooks "${POST_DOWN[@]}"
+}
+
+cmd_save() {
+ get_real_interface || die "\`$INTERFACE' is not a WireGuard interface"
+ save_config
+}
+
+cmd_strip() {
+ echo "$WG_CONFIG"
+}
+
+# ~~ function override insertion point ~~
+
+if [[ $# -eq 1 && ( $1 == --help || $1 == -h || $1 == help ) ]]; then
+ cmd_usage
+elif [[ $# -eq 2 && $1 == up ]]; then
+ auto_su
+ parse_options "$2"
+ cmd_up
+elif [[ $# -eq 2 && $1 == down ]]; then
+ auto_su
+ parse_options "$2"
+ cmd_down
+elif [[ $# -eq 2 && $1 == save ]]; then
+ auto_su
+ parse_options "$2"
+ cmd_save
+elif [[ $# -eq 2 && $1 == strip ]]; then
+ auto_su
+ parse_options "$2"
+ cmd_strip
+else
+ cmd_usage
+ exit 1
+fi
+
+exit 0
diff --git a/contrib/wireguard-tools/wg-quick/wg b/contrib/wireguard-tools/wg-quick/wg
new file mode 120000
--- /dev/null
+++ b/contrib/wireguard-tools/wg-quick/wg
@@ -0,0 +1 @@
+../wg
\ No newline at end of file
diff --git a/contrib/wireguard-tools/wg.c b/contrib/wireguard-tools/wg.c
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/wg.c
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "subcommands.h"
+#include "version.h"
+
+const char *PROG_NAME;
+
+static const struct {
+ const char *subcommand;
+ int (*function)(int, const char**);
+ const char *description;
+} subcommands[] = {
+ { "show", show_main, "Shows the current configuration and device information" },
+ { "showconf", showconf_main, "Shows the current configuration of a given WireGuard interface, for use with `setconf'" },
+ { "set", set_main, "Change the current configuration, add peers, remove peers, or change peers" },
+ { "setconf", setconf_main, "Applies a configuration file to a WireGuard interface" },
+ { "addconf", setconf_main, "Appends a configuration file to a WireGuard interface" },
+ { "syncconf", setconf_main, "Synchronizes a configuration file to a WireGuard interface" },
+ { "genkey", genkey_main, "Generates a new private key and writes it to stdout" },
+ { "genpsk", genkey_main, "Generates a new preshared key and writes it to stdout" },
+ { "pubkey", pubkey_main, "Reads a private key from stdin and writes a public key to stdout" }
+};
+
+static void show_usage(FILE *file)
+{
+ fprintf(file, "Usage: %s <cmd> [<args>]\n\n", PROG_NAME);
+ fprintf(file, "Available subcommands:\n");
+ for (size_t i = 0; i < sizeof(subcommands) / sizeof(subcommands[0]); ++i)
+ fprintf(file, " %s: %s\n", subcommands[i].subcommand, subcommands[i].description);
+ fprintf(file, "You may pass `--help' to any of these subcommands to view usage.\n");
+}
+
+int main(int argc, const char *argv[])
+{
+ PROG_NAME = argv[0];
+
+ if (argc == 2 && (!strcmp(argv[1], "-v") || !strcmp(argv[1], "--version") || !strcmp(argv[1], "version"))) {
+ printf("wireguard-tools v%s - https://git.zx2c4.com/wireguard-tools/\n", WIREGUARD_TOOLS_VERSION);
+ return 0;
+ }
+ if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help") || !strcmp(argv[1], "help"))) {
+ show_usage(stdout);
+ return 0;
+ }
+
+ if (argc == 1) {
+ static const char *new_argv[] = { "show", NULL };
+ return show_main(1, new_argv);
+ }
+
+ for (size_t i = 0; i < sizeof(subcommands) / sizeof(subcommands[0]); ++i) {
+ if (!strcmp(argv[1], subcommands[i].subcommand))
+ return subcommands[i].function(argc - 1, argv + 1);
+ }
+
+ fprintf(stderr, "Invalid subcommand: `%s'\n", argv[1]);
+ show_usage(stderr);
+ return 1;
+}
diff --git a/contrib/wireguard-tools/wincompat/compat.h b/contrib/wireguard-tools/wincompat/compat.h
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/wincompat/compat.h
@@ -0,0 +1,27 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#define __USE_MINGW_ANSI_STDIO 1
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <winsock2.h>
+#include <ws2ipdef.h>
+#include <ws2tcpip.h>
+#include <in6addr.h>
+#include <windows.h>
+
+#undef interface
+#undef min
+#undef max
+
+#define IFNAMSIZ 64
+#define EAI_SYSTEM -99
+
+/* libc.c */
+char *strsep(char **str, const char *sep);
+ssize_t getdelim(char **buf, size_t *bufsiz, int delimiter, FILE *fp);
+ssize_t getline(char **buf, size_t *bufsiz, FILE *fp);
diff --git a/contrib/wireguard-tools/wincompat/include/arpa/inet.h b/contrib/wireguard-tools/wincompat/include/arpa/inet.h
new file mode 100644
diff --git a/contrib/wireguard-tools/wincompat/include/hashtable.h b/contrib/wireguard-tools/wincompat/include/hashtable.h
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/wincompat/include/hashtable.h
@@ -0,0 +1,61 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2018-2021 WireGuard LLC. All Rights Reserved.
+ */
+
+#ifndef _HASHTABLE_H
+#define _HASHTABLE_H
+
+#include <string.h>
+
+enum { HASHTABLE_ENTRY_BUCKETS_POW2 = 1 << 10 };
+
+struct hashtable_entry {
+ char *key;
+ void *value;
+ struct hashtable_entry *next;
+};
+
+struct hashtable {
+ struct hashtable_entry *entry_buckets[HASHTABLE_ENTRY_BUCKETS_POW2];
+};
+
+static unsigned int hashtable_bucket(const char *str)
+{
+ unsigned long hash = 5381;
+ char c;
+ while ((c = *str++))
+ hash = ((hash << 5) + hash) ^ c;
+ return hash & (HASHTABLE_ENTRY_BUCKETS_POW2 - 1);
+}
+
+static struct hashtable_entry *hashtable_find_entry(struct hashtable *hashtable, const char *key)
+{
+ struct hashtable_entry *entry;
+ for (entry = hashtable->entry_buckets[hashtable_bucket(key)]; entry; entry = entry->next) {
+ if (!strcmp(entry->key, key))
+ return entry;
+ }
+ return NULL;
+}
+
+static struct hashtable_entry *hashtable_find_or_insert_entry(struct hashtable *hashtable, const char *key)
+{
+ struct hashtable_entry **entry;
+ for (entry = &hashtable->entry_buckets[hashtable_bucket(key)]; *entry; entry = &(*entry)->next) {
+ if (!strcmp((*entry)->key, key))
+ return *entry;
+ }
+ *entry = calloc(1, sizeof(**entry));
+ if (!*entry)
+ return NULL;
+ (*entry)->key = strdup(key);
+ if (!(*entry)->key) {
+ free(*entry);
+ *entry = NULL;
+ return NULL;
+ }
+ return *entry;
+}
+
+#endif
diff --git a/contrib/wireguard-tools/wincompat/include/net/if.h b/contrib/wireguard-tools/wincompat/include/net/if.h
new file mode 100644
diff --git a/contrib/wireguard-tools/wincompat/include/netdb.h b/contrib/wireguard-tools/wincompat/include/netdb.h
new file mode 100644
diff --git a/contrib/wireguard-tools/wincompat/include/netinet/in.h b/contrib/wireguard-tools/wincompat/include/netinet/in.h
new file mode 100644
diff --git a/contrib/wireguard-tools/wincompat/include/sys/socket.h b/contrib/wireguard-tools/wincompat/include/sys/socket.h
new file mode 100644
diff --git a/contrib/wireguard-tools/wincompat/init.c b/contrib/wireguard-tools/wincompat/init.c
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/wincompat/init.c
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#include <winsock2.h>
+#include <windows.h>
+
+#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
+#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x4
+#endif
+
+extern void NTAPI RtlGetNtVersionNumbers(DWORD *major, DWORD *minor, DWORD *build);
+bool is_win7 = false;
+
+__attribute__((constructor)) static void init(void)
+{
+ char *colormode;
+ DWORD console_mode, major, minor;
+ HANDLE stdout_handle;
+ WSADATA wsaData;
+
+ if (!SetDllDirectoryA("") || !SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_SYSTEM32))
+ abort();
+
+ RtlGetNtVersionNumbers(&major, &minor, NULL);
+ is_win7 = (major == 6 && minor <= 1) || major < 6;
+
+ WSAStartup(MAKEWORD(2, 2), &wsaData);
+
+ stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE); // We don't close this.
+ if (stdout_handle == INVALID_HANDLE_VALUE)
+ goto no_color;
+ if (!GetConsoleMode(stdout_handle, &console_mode))
+ goto no_color;
+ if (!SetConsoleMode(stdout_handle, ENABLE_VIRTUAL_TERMINAL_PROCESSING | console_mode))
+ goto no_color;
+ return;
+
+no_color:
+ colormode = getenv("WG_COLOR_MODE");
+ if (!colormode)
+ putenv("WG_COLOR_MODE=never");
+}
+
+__attribute__((destructor)) static void deinit(void)
+{
+ WSACleanup();
+}
diff --git a/contrib/wireguard-tools/wincompat/libc.c b/contrib/wireguard-tools/wincompat/libc.c
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/wincompat/libc.c
@@ -0,0 +1,69 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <windows.h>
+
+char *strsep(char **str, const char *sep)
+{
+ char *s = *str, *end;
+ if (!s)
+ return NULL;
+ end = s + strcspn(s, sep);
+ if (*end)
+ *end++ = 0;
+ else
+ end = 0;
+ *str = end;
+ return s;
+}
+
+ssize_t getdelim(char **buf, size_t *bufsiz, int delimiter, FILE *fp)
+{
+ char *ptr, *eptr;
+
+ if (!*buf || !*bufsiz) {
+ *bufsiz = BUFSIZ;
+ if (!(*buf = malloc(*bufsiz)))
+ return -1;
+ }
+
+ for (ptr = *buf, eptr = *buf + *bufsiz;;) {
+ int c = fgetc(fp);
+ if (c == -1) {
+ if (feof(fp)) {
+ ssize_t diff = (ssize_t)(ptr - *buf);
+ if (diff != 0) {
+ *ptr = '\0';
+ return diff;
+ }
+ }
+ return -1;
+ }
+ *ptr++ = c;
+ if (c == delimiter) {
+ *ptr = '\0';
+ return ptr - *buf;
+ }
+ if (ptr + 2 >= eptr) {
+ char *nbuf;
+ size_t nbufsiz = *bufsiz * 2;
+ ssize_t d = ptr - *buf;
+ if ((nbuf = realloc(*buf, nbufsiz)) == NULL)
+ return -1;
+ *buf = nbuf;
+ *bufsiz = nbufsiz;
+ eptr = nbuf + nbufsiz;
+ ptr = nbuf + d;
+ }
+ }
+}
+
+ssize_t getline(char **buf, size_t *bufsiz, FILE *fp)
+{
+ return getdelim(buf, bufsiz, '\n', fp);
+}
diff --git a/contrib/wireguard-tools/wincompat/loader.c b/contrib/wireguard-tools/wincompat/loader.c
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/wincompat/loader.c
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2015-2021 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#include <windows.h>
+#include <delayimp.h>
+
+static FARPROC WINAPI delayed_load_library_hook(unsigned dliNotify, PDelayLoadInfo pdli)
+{
+ HMODULE library;
+ if (dliNotify != dliNotePreLoadLibrary)
+ return NULL;
+ library = LoadLibraryExA(pdli->szDll, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
+ if (!library)
+ abort();
+ return (FARPROC)library;
+}
+
+PfnDliHook __pfnDliNotifyHook2 = delayed_load_library_hook;
diff --git a/contrib/wireguard-tools/wincompat/manifest.xml b/contrib/wireguard-tools/wincompat/manifest.xml
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/wincompat/manifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+ <assemblyIdentity version="1.0.0.0" processorArchitecture="*" name="wg" type="win32" />
+ <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+ <application>
+ <!-- Windows 10 -->
+ <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
+ <!-- Windows 8.1 -->
+ <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
+ <!-- Windows 8 -->
+ <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
+ <!-- Windows 7 -->
+ <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
+ </application>
+ </compatibility>
+ <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
+ <security>
+ <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
+ <requestedExecutionLevel level="asInvoker" />
+ </requestedPrivileges>
+ </security>
+ </trustInfo>
+</assembly>
diff --git a/contrib/wireguard-tools/wincompat/resources.rc b/contrib/wireguard-tools/wincompat/resources.rc
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/wincompat/resources.rc
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2020 Jason A. Donenfeld. All Rights Reserved.
+ */
+
+#include <windows.h>
+
+#pragma code_page(65001) // UTF-8
+
+LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
+CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST manifest.xml
+
+#define STRINGIZE(x) #x
+#define EXPAND(x) STRINGIZE(x)
+
+VS_VERSION_INFO VERSIONINFO
+FILEOS VOS_NT_WINDOWS32
+FILETYPE VFT_APP
+FILESUBTYPE VFT2_UNKNOWN
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "CompanyName", "WireGuard LLC"
+ VALUE "FileDescription", "WireGuard wg(8) CLI: Fast, Modern, Secure VPN Tunnel"
+ VALUE "FileVersion", EXPAND(VERSION_STR)
+ VALUE "InternalName", "wg"
+ VALUE "LegalCopyright", "Copyright © 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved."
+ VALUE "OriginalFilename", "wg.exe"
+ VALUE "ProductName", "WireGuard"
+ VALUE "ProductVersion", EXPAND(VERSION_STR)
+ VALUE "Comments", "https://www.wireguard.com/"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 0x4b0
+ END
+END
diff --git a/usr.bin/Makefile b/usr.bin/Makefile
--- a/usr.bin/Makefile
+++ b/usr.bin/Makefile
@@ -174,6 +174,7 @@
w \
wall \
wc \
+ wg \
what \
whereis \
which \
diff --git a/usr.bin/wg/Makefile b/usr.bin/wg/Makefile
new file mode 100644
--- /dev/null
+++ b/usr.bin/wg/Makefile
@@ -0,0 +1,41 @@
+
+PROG= wg
+MAN= wg.8
+
+LIBADD= nv
+
+.PATH: ${SRCTOP}/contrib/wireguard-tools
+.PATH: ${SRCTOP}/contrib/wireguard-tools/man
+
+SRCS+= wg.c
+
+SRCS+= config.c
+SRCS+= encoding.c
+SRCS+= ipc.c
+SRCS+= terminal.c
+
+SRCS+= curve25519.c
+
+# Subcommands
+SRCS+= show.c
+SRCS+= showconf.c
+SRCS+= set.c
+SRCS+= setconf.c
+SRCS+= genkey.c
+SRCS+= pubkey.c
+
+# Base should pull RUNSTATEDIR from paths.h, _PATH_VARRUN
+CFLAGS+= -include paths.h
+CFLAGS+= -DRUNSTATEDIR="_PATH_VARRUN"
+
+# Not going to change this one.
+CWARNFLAGS.curve25519.c+= -Wno-shadow
+
+# sockaddr{,_in} alignment
+CWARNFLAGS.config.c+= -Wno-cast-align
+CWARNFLAGS.ipc.c+= -Wno-cast-align
+
+# qsort comparator
+CWARNFLAGS.show.c+= -Wno-cast-qual
+
+.include <bsd.prog.mk>

File Metadata

Mime Type
text/plain
Expires
Fri, Nov 8, 1:07 PM (21 h, 20 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
14535433
Default Alt Text
D36952.id111765.diff (362 KB)

Event Timeline