aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/src/tools
diff options
context:
space:
mode:
authorJason A. Donenfeld <Jason@zx2c4.com>2015-06-05 15:58:00 +0200
committerJason A. Donenfeld <Jason@zx2c4.com>2016-06-25 16:48:39 +0200
commit99d303ac2739e65a02fbbc325b74ad6fcac63cc2 (patch)
tree6f4095f42d3d298cdd5ab8bc6f8ed89d9673b38b /src/tools
downloadwireguard-monolithic-historical-99d303ac2739e65a02fbbc325b74ad6fcac63cc2.tar.xz
wireguard-monolithic-historical-99d303ac2739e65a02fbbc325b74ad6fcac63cc2.zip
Initial commit
Diffstat (limited to 'src/tools')
-rw-r--r--src/tools/.gitignore3
-rw-r--r--src/tools/Makefile26
-rw-r--r--src/tools/base64.c220
-rw-r--r--src/tools/base64.h20
-rw-r--r--src/tools/config.c518
-rw-r--r--src/tools/config.h34
-rw-r--r--src/tools/curve25519.c1258
-rw-r--r--src/tools/curve25519.h22
-rw-r--r--src/tools/genkey.c59
-rw-r--r--src/tools/kernel.c242
-rw-r--r--src/tools/kernel.h24
-rw-r--r--src/tools/pubkey.c33
-rw-r--r--src/tools/set.c35
-rw-r--r--src/tools/setconf.c61
-rw-r--r--src/tools/show.c366
-rw-r--r--src/tools/showconf.c102
-rw-r--r--src/tools/subcommands.h14
-rw-r--r--src/tools/terminal.c79
-rw-r--r--src/tools/terminal.h49
-rw-r--r--src/tools/wg.8194
-rw-r--r--src/tools/wg.c66
21 files changed, 3425 insertions, 0 deletions
diff --git a/src/tools/.gitignore b/src/tools/.gitignore
new file mode 100644
index 0000000..359f8b5
--- /dev/null
+++ b/src/tools/.gitignore
@@ -0,0 +1,3 @@
+*.d
+*.o
+wg
diff --git a/src/tools/Makefile b/src/tools/Makefile
new file mode 100644
index 0000000..4eddd25
--- /dev/null
+++ b/src/tools/Makefile
@@ -0,0 +1,26 @@
+PREFIX ?= /usr
+DESTDIR ?=
+BINDIR ?= $(PREFIX)/bin
+LIBDIR ?= $(PREFIX)/lib
+MANDIR ?= $(PREFIX)/share/man
+
+CFLAGS += -std=gnu11
+CFLAGS += -pedantic -Wall -Wextra
+CFLAGS += -MMD
+LDLIBS += -lresolv -lmnl
+
+wg: $(patsubst %.c,%.o,$(wildcard *.c))
+
+clean:
+ rm -f wg *.o *.d
+
+install: wg
+ install -v -d "$(DESTDIR)$(BINDIR)" && install -s -m 0755 -v wg "$(DESTDIR)$(BINDIR)/wg"
+ install -v -d "$(DESTDIR)$(MANDIR)/man8" && install -m 0644 -v wg.8 "$(DESTDIR)$(MANDIR)/man8/wg.8"
+
+check: clean
+ CFLAGS=-g scan-build --view --keep-going $(MAKE) wg
+
+.PHONY: clean install check
+
+-include *.d
diff --git a/src/tools/base64.c b/src/tools/base64.c
new file mode 100644
index 0000000..cf37464
--- /dev/null
+++ b/src/tools/base64.c
@@ -0,0 +1,220 @@
+/*
+ * Copyright (c) 1996, 1998 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
+ * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
+ * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ *
+ * Portions Copyright (c) 1995 by International Business Machines, Inc.
+ *
+ * International Business Machines, Inc. (hereinafter called IBM) grants
+ * permission under its copyrights to use, copy, modify, and distribute this
+ * Software with or without fee, provided that the above copyright notice and
+ * all paragraphs of this notice appear in all copies, and that the name of IBM
+ * not be used in connection with the marketing of any product incorporating
+ * the Software or modifications thereof, without specific, written prior
+ * permission.
+ *
+ * To the extent it has a right to do so, IBM grants an immunity from suit
+ * under its patents, if any, for the use, sale or manufacture of products to
+ * the extent that such products are used for performing Domain Name System
+ * dynamic updates in TCP/IP networks by means of the Software. No immunity is
+ * granted for any product per se or for any other function of any product.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL,
+ * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN
+ * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include "base64.h"
+#include <sys/types.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#if defined(NEED_B64_NTOP) || defined(NEED_B64_PTON)
+static const char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+static const char pad64 = '=';
+#endif
+
+#ifdef NEED_B64_NTOP
+int b64_ntop(unsigned char const *src, size_t srclength, char *target, size_t targsize)
+{
+ size_t datalength = 0;
+ uint8_t input[3];
+ uint8_t output[4];
+ size_t i;
+
+ while (2 < srclength) {
+ input[0] = *src++;
+ input[1] = *src++;
+ input[2] = *src++;
+ srclength -= 3;
+
+ output[0] = input[0] >> 2;
+ output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4);
+ output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6);
+ output[3] = input[2] & 0x3f;
+ assert(output[0] < 64);
+ assert(output[1] < 64);
+ assert(output[2] < 64);
+ assert(output[3] < 64);
+
+ if (datalength + 4 > targsize)
+ return -1;
+ target[datalength++] = base64[output[0]];
+ target[datalength++] = base64[output[1]];
+ target[datalength++] = base64[output[2]];
+ target[datalength++] = base64[output[3]];
+ }
+ if (0 != srclength) {
+ input[0] = input[1] = input[2] = '\0';
+ for (i = 0; i < srclength; i++)
+ input[i] = *src++;
+ output[0] = input[0] >> 2;
+ output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4);
+ output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6);
+ assert(output[0] < 64);
+ assert(output[1] < 64);
+ assert(output[2] < 64);
+
+ if (datalength + 4 > targsize)
+ return -1;
+ target[datalength++] = base64[output[0]];
+ target[datalength++] = base64[output[1]];
+ if (srclength == 1)
+ target[datalength++] = pad64;
+ else
+ target[datalength++] = base64[output[2]];
+ target[datalength++] = pad64;
+ }
+ if (datalength >= targsize)
+ return (-1);
+ target[datalength] = '\0';
+ return datalength;
+}
+#endif
+
+#ifdef NEED_B64_PTON
+int b64_pton(char const *src, uint8_t *target, size_t targsize)
+{
+ static int b64rmap_initialized = 0;
+ static uint8_t b64rmap[256];
+ static const uint8_t b64rmap_special = 0xf0;
+ static const uint8_t b64rmap_end = 0xfd;
+ static const uint8_t b64rmap_space = 0xfe;
+ static const uint8_t b64rmap_invalid = 0xff;
+ int tarindex, state, ch;
+ uint8_t ofs;
+
+ if (!b64rmap_initialized) {
+ int i;
+ char ch;
+ b64rmap[0] = b64rmap_end;
+ for (i = 1; i < 256; ++i) {
+ ch = (char)i;
+ if (isspace(ch))
+ b64rmap[i] = b64rmap_space;
+ else if (ch == pad64)
+ b64rmap[i] = b64rmap_end;
+ else
+ b64rmap[i] = b64rmap_invalid;
+ }
+ for (i = 0; base64[i] != '\0'; ++i)
+ b64rmap[(uint8_t)base64[i]] = i;
+ b64rmap_initialized = 1;
+ }
+
+ state = 0;
+ tarindex = 0;
+
+ for (;;) {
+ ch = *src++;
+ ofs = b64rmap[ch];
+
+ if (ofs >= b64rmap_special) {
+ if (ofs == b64rmap_space)
+ continue;
+ if (ofs == b64rmap_end)
+ break;
+ return -1;
+ }
+
+ switch (state) {
+ case 0:
+ if ((size_t)tarindex >= targsize)
+ return -1;
+ target[tarindex] = ofs << 2;
+ state = 1;
+ break;
+ case 1:
+ if ((size_t)tarindex + 1 >= targsize)
+ return -1;
+ target[tarindex] |= ofs >> 4;
+ target[tarindex+1] = (ofs & 0x0f) << 4 ;
+ tarindex++;
+ state = 2;
+ break;
+ case 2:
+ if ((size_t)tarindex + 1 >= targsize)
+ return -1;
+ target[tarindex] |= ofs >> 2;
+ target[tarindex+1] = (ofs & 0x03) << 6;
+ tarindex++;
+ state = 3;
+ break;
+ case 3:
+ if ((size_t)tarindex >= targsize)
+ return -1;
+ target[tarindex] |= ofs;
+ tarindex++;
+ state = 0;
+ break;
+ default:
+ abort();
+ }
+ }
+
+ if (ch == pad64) {
+ ch = *src++;
+ switch (state) {
+ case 0:
+ case 1:
+ return -1;
+
+ case 2:
+ for (; ch; ch = *src++) {
+ if (b64rmap[ch] != b64rmap_space)
+ break;
+ }
+ if (ch != pad64)
+ return -1;
+ ch = *src++;
+ case 3:
+ for (; ch; ch = *src++) {
+ if (b64rmap[ch] != b64rmap_space)
+ return -1;
+ }
+ if (target[tarindex] != 0)
+ return -1;
+ }
+ } else {
+ if (state != 0)
+ return -1;
+ }
+
+ return tarindex;
+}
+#endif
diff --git a/src/tools/base64.h b/src/tools/base64.h
new file mode 100644
index 0000000..5cc94e1
--- /dev/null
+++ b/src/tools/base64.h
@@ -0,0 +1,20 @@
+/* Copyright 2015-2016 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. */
+
+#ifndef BASE64_H
+#define BASE64_H
+
+#include <resolv.h>
+
+#define b64_len(len) ((((len) + 2) / 3) * 4 + 1)
+
+#ifndef b64_ntop
+int b64_ntop(unsigned char const *, size_t, char *, size_t);
+#define NEED_B64_NTOP
+#endif
+
+#ifndef b64_pton
+int b64_pton(char const *, unsigned char *, size_t);
+#define NEED_B64_PTON
+#endif
+
+#endif
diff --git a/src/tools/config.c b/src/tools/config.c
new file mode 100644
index 0000000..0cec30e
--- /dev/null
+++ b/src/tools/config.c
@@ -0,0 +1,518 @@
+/* Copyright 2015-2016 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. */
+
+
+#include <arpa/inet.h>
+#include <ctype.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <errno.h>
+
+#include "config.h"
+#include "kernel.h"
+#include "base64.h"
+
+#define COMMENT_CHAR '#'
+
+#define max(a, b) (a > b ? a : b)
+
+static inline struct wgpeer *peer_from_offset(struct wgdevice *dev, size_t offset)
+{
+ return (struct wgpeer *)((uint8_t *)dev + sizeof(struct wgdevice) + offset);
+}
+
+static int use_space(struct inflatable_device *buf, size_t space)
+{
+ size_t expand_to;
+ uint8_t *new_dev;
+
+ if (buf->len - buf->pos < space) {
+ expand_to = max(buf->len * 2, buf->len + space);
+ new_dev = realloc(buf->dev, expand_to + sizeof(struct wgdevice));
+ if (!new_dev)
+ return -errno;
+ memset(&new_dev[buf->len + sizeof(struct wgdevice)], 0, expand_to - buf->len);
+ buf->dev = (struct wgdevice *)new_dev;
+ buf->len = expand_to;
+ }
+ buf->pos += space;
+ return 0;
+}
+
+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 uint16_t parse_port(const char *value)
+{
+ int ret;
+ uint16_t port = 0;
+ struct addrinfo *resolved;
+ struct addrinfo hints = {
+ .ai_family = AF_UNSPEC,
+ .ai_socktype = SOCK_DGRAM,
+ .ai_protocol = IPPROTO_UDP,
+ .ai_flags = AI_ADDRCONFIG | AI_PASSIVE
+ };
+
+ if (!strlen(value)) {
+ fprintf(stderr, "Unable to parse empty port\n");
+ return 0;
+ }
+
+ ret = getaddrinfo(NULL, value, &hints, &resolved);
+ if (ret != 0) {
+ fprintf(stderr, "%s: `%s`\n", gai_strerror(ret), value);
+ return 0;
+ }
+
+ if (resolved->ai_family == AF_INET && resolved->ai_addrlen == sizeof(struct sockaddr_in))
+ port = ntohs(((struct sockaddr_in *)resolved->ai_addr)->sin_port);
+ else if (resolved->ai_family == AF_INET6 && resolved->ai_addrlen == sizeof(struct sockaddr_in6))
+ port = ntohs(((struct sockaddr_in6 *)resolved->ai_addr)->sin6_port);
+ else
+ fprintf(stderr, "Neither IPv4 nor IPv6 address found: `%s`\n", value);
+
+ freeaddrinfo(resolved);
+ return port;
+}
+
+static inline bool parse_key(uint8_t key[WG_KEY_LEN], const char *value)
+{
+ uint8_t tmp[WG_KEY_LEN + 1];
+ if (strlen(value) != b64_len(WG_KEY_LEN) - 1) {
+ fprintf(stderr, "Key is not the correct length: `%s`\n", value);
+ return false;
+ }
+ if (b64_pton(value, tmp, WG_KEY_LEN + 1) < 0) {
+ fprintf(stderr, "Could not parse base64 key: `%s`\n", value);
+ return false;
+ }
+ memcpy(key, tmp, WG_KEY_LEN);
+ return true;
+}
+
+static inline bool parse_ip(struct wgipmask *ipmask, const char *value)
+{
+ ipmask->family = AF_UNSPEC;
+ if (strchr(value, ':')) {
+ if (inet_pton(AF_INET6, value, &ipmask->ip6) == 1)
+ ipmask->family = AF_INET6;
+ } else {
+ if (inet_pton(AF_INET, value, &ipmask->ip4) == 1)
+ ipmask->family = AF_INET;
+ }
+ if (ipmask->family == AF_UNSPEC) {
+ fprintf(stderr, "Unable to parse IP address: `%s`\n", value);
+ return false;
+ }
+ return true;
+}
+
+static inline bool parse_endpoint(struct sockaddr_storage *endpoint, const char *value)
+{
+ char *mutable = strdup(value);
+ char *begin, *end;
+ int ret;
+ struct addrinfo *resolved;
+ struct addrinfo hints = {
+ .ai_family = AF_UNSPEC,
+ .ai_socktype = SOCK_DGRAM,
+ .ai_protocol = IPPROTO_UDP,
+ .ai_flags = AI_ADDRCONFIG
+ };
+ 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';
+ ++end;
+ if (*end != ':' || !*(end + 1)) {
+ free(mutable);
+ fprintf(stderr, "Unable to find port of endpoint: `%s`\n", value);
+ return false;
+ }
+ ++end;
+ } 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';
+ ++end;
+ }
+ ret = getaddrinfo(begin, end, &hints, &resolved);
+ if (ret != 0) {
+ free(mutable);
+ fprintf(stderr, "%s: `%s`\n", gai_strerror(ret), value);
+ return false;
+ }
+ 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_ipmasks(struct inflatable_device *buf, size_t peer_offset, const char *value)
+{
+ struct wgpeer *peer;
+ struct wgipmask *ipmask;
+ char *mask, *mutable = strdup(value), *sep;
+ if (!mutable) {
+ perror("strdup");
+ return false;
+ };
+ peer = peer_from_offset(buf->dev, peer_offset);
+ peer->num_ipmasks = 0;
+ peer->replace_ipmasks = true;
+ if (!strlen(value)) {
+ free(mutable);
+ return true;
+ }
+ sep = mutable;
+ while ((mask = strsep(&sep, ","))) {
+ unsigned long cidr;
+ char *end, *ip = strsep(&mask, "/");
+ if (use_space(buf, sizeof(struct wgipmask)) < 0) {
+ perror("use_space");
+ free(mutable);
+ return false;
+ }
+ peer = peer_from_offset(buf->dev, peer_offset);
+ ipmask = (struct wgipmask *)((uint8_t *)peer + sizeof(struct wgpeer) + (sizeof(struct wgipmask) * peer->num_ipmasks));
+
+ if (!parse_ip(ipmask, ip)) {
+ free(mutable);
+ return false;
+ }
+ if (ipmask->family == AF_INET) {
+ if (mask) {
+ cidr = strtoul(mask, &end, 10);
+ if (*end)
+ mask = NULL;
+ if (cidr > 32)
+ mask = NULL;
+ }
+ if (!mask)
+ cidr = 32;
+ } else if (ipmask->family == AF_INET6) {
+ if (mask) {
+ cidr = strtoul(mask, &end, 10);
+ if (*end)
+ mask = NULL;
+ if (cidr > 128)
+ mask = NULL;
+ }
+ if (!mask)
+ cidr = 128;
+ } else
+ continue;
+ ipmask->cidr = cidr;
+ ++peer->num_ipmasks;
+ }
+ free(mutable);
+ return true;
+}
+
+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]")) {
+ ctx->peer_offset = ctx->buf.pos;
+ if (use_space(&ctx->buf, sizeof(struct wgpeer)) < 0) {
+ perror("use_space");
+ return false;
+ }
+ ++ctx->buf.dev->num_peers;
+ ctx->is_peer_section = true;
+ ctx->is_device_section = false;
+ peer_from_offset(ctx->buf.dev, ctx->peer_offset)->replace_ipmasks = true;
+ return true;
+ }
+
+#define key_match(key) (value = get_value(line, key "="))
+
+ if (ctx->is_device_section) {
+ if (key_match("ListenPort"))
+ ret = !!(ctx->buf.dev->port = parse_port(value));
+ else if (key_match("PrivateKey")) {
+ ret = parse_key(ctx->buf.dev->private_key, value);
+ if (!ret)
+ memset(ctx->buf.dev->private_key, 0, WG_KEY_LEN);
+ } else if (key_match("PresharedKey")) {
+ ret = parse_key(ctx->buf.dev->preshared_key, value);
+ if (!ret)
+ memset(ctx->buf.dev->preshared_key, 0, WG_KEY_LEN);
+ } else
+ goto error;
+ } else if (ctx->is_peer_section) {
+ if (key_match("Endpoint"))
+ ret = parse_endpoint(&peer_from_offset(ctx->buf.dev, ctx->peer_offset)->endpoint, value);
+ else if (key_match("PublicKey"))
+ ret = parse_key(peer_from_offset(ctx->buf.dev, ctx->peer_offset)->public_key, value);
+ else if (key_match("AllowedIPs"))
+ ret = parse_ipmasks(&ctx->buf, ctx->peer_offset, value);
+ 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 = strlen(input), cleaned_len = 0;
+ char *line = calloc(len + 1, sizeof(char));
+ bool ret = true;
+ if (!line) {
+ perror("calloc");
+ return false;
+ }
+ if (!len)
+ goto out;
+ for (size_t i = 0; i < len; ++i) {
+ if (!isspace(input[i]))
+ line[cleaned_len++] = input[i];
+ }
+ if (!cleaned_len)
+ goto out;
+ if (line[0] == COMMENT_CHAR)
+ goto out;
+ ret = process_line(ctx, line);
+out:
+ free(line);
+ return ret;
+}
+
+bool config_read_init(struct config_ctx *ctx, struct wgdevice **device, bool append)
+{
+ memset(ctx, 0, sizeof(struct config_ctx));
+ ctx->device = device;
+ ctx->buf.dev = calloc(1, sizeof(struct wgdevice));
+ if (!ctx->buf.dev) {
+ perror("calloc");
+ return false;
+ }
+ ctx->buf.dev->replace_peer_list = !append;
+ return true;
+}
+
+static inline bool key_is_valid(uint8_t key[WG_KEY_LEN])
+{
+ static const uint8_t zero[WG_KEY_LEN] = { 0 };
+ return !!memcmp(key, zero, WG_KEY_LEN);
+}
+
+bool config_read_finish(struct config_ctx *ctx)
+{
+ size_t i;
+ struct wgpeer *peer;
+ if (ctx->buf.dev->replace_peer_list && !ctx->buf.dev->num_peers) {
+ fprintf(stderr, "No peers configured\n");
+ goto err;
+ }
+ if (ctx->buf.dev->replace_peer_list && !key_is_valid(ctx->buf.dev->private_key)) {
+ fprintf(stderr, "No private key configured\n");
+ goto err;
+ }
+ for_each_wgpeer(ctx->buf.dev, peer, i) {
+ if (!key_is_valid(peer->public_key)) {
+ fprintf(stderr, "A peer is missing a public key\n");
+ goto err;
+ }
+ }
+ *ctx->device = ctx->buf.dev;
+ return true;
+err:
+ free(ctx->buf.dev);
+ return false;
+}
+
+static int read_line(char **dst, const char *path)
+{
+ FILE *f;
+ size_t n = 0;
+ struct stat stat;
+
+ *dst = NULL;
+
+ f = fopen(path, "r");
+ if (!f) {
+ perror("fopen");
+ return -1;
+ }
+ if (fstat(fileno(f), &stat) < 0) {
+ perror("fstat");
+ fclose(f);
+ return -1;
+ }
+ if (S_ISCHR(stat.st_mode) && stat.st_rdev == makedev(1, 3)) {
+ fclose(f);
+ return 1;
+ }
+ if (getline(dst, &n, f) < 0) {
+ perror("getline");
+ fclose(f);
+ return -1;
+ }
+ fclose(f);
+ n = strlen(*dst);
+ while (--n) {
+ if (isspace((*dst)[n]))
+ (*dst)[n] = '\0';
+ }
+ return 0;
+}
+
+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 (!isspace(in[i]))
+ out[l++] = in[i];
+ }
+ return out;
+}
+
+bool config_read_cmd(struct wgdevice **device, char *argv[], int argc)
+{
+ struct inflatable_device buf = { 0 };
+ size_t peer_offset = 0;
+ buf.dev = calloc(sizeof(struct wgdevice), 1);
+ if (!buf.dev) {
+ perror("calloc");
+ return false;
+ }
+ while (argc > 0) {
+ if (!strcmp(argv[0], "listen-port") && argc >= 2 && !buf.dev->num_peers) {
+ buf.dev->port = parse_port(argv[1]);
+ if (!buf.dev->port)
+ goto error;
+ argv += 2;
+ argc -= 2;
+ } else if (!strcmp(argv[0], "private-key") && argc >= 2 && !buf.dev->num_peers) {
+ char *line;
+ int ret = read_line(&line, argv[1]);
+ if (ret == 0) {
+ if (!parse_key(buf.dev->private_key, line)) {
+ free(line);
+ goto error;
+ }
+ free(line);
+ } else if (ret == 1)
+ buf.dev->remove_private_key = true;
+ else
+ goto error;
+ argv += 2;
+ argc -= 2;
+ } else if (!strcmp(argv[0], "preshared-key") && argc >= 2 && !buf.dev->num_peers) {
+ char *line;
+ int ret = read_line(&line, argv[1]);
+ if (ret == 0) {
+ if (!parse_key(buf.dev->preshared_key, line)) {
+ free(line);
+ goto error;
+ }
+ free(line);
+ } else if (ret == 1)
+ buf.dev->remove_preshared_key = true;
+ else
+ goto error;
+ argv += 2;
+ argc -= 2;
+ } else if (!strcmp(argv[0], "peer") && argc >= 2) {
+ peer_offset = buf.pos;
+ if (use_space(&buf, sizeof(struct wgpeer)) < 0) {
+ perror("use_space");
+ goto error;
+ }
+ ++buf.dev->num_peers;
+ if (!parse_key(peer_from_offset(buf.dev, peer_offset)->public_key, argv[1]))
+ goto error;
+ argv += 2;
+ argc -= 2;
+ } else if (!strcmp(argv[0], "remove") && argc >= 1 && buf.dev->num_peers) {
+ peer_from_offset(buf.dev, peer_offset)->remove_me = true;
+ argv += 1;
+ argc -= 1;
+ } else if (!strcmp(argv[0], "endpoint") && argc >= 2 && buf.dev->num_peers) {
+ if (!parse_endpoint(&peer_from_offset(buf.dev, peer_offset)->endpoint, argv[1]))
+ goto error;
+ argv += 2;
+ argc -= 2;
+ } else if (!strcmp(argv[0], "allowed-ips") && argc >= 2 && buf.dev->num_peers) {
+ char *line = strip_spaces(argv[1]);
+ if (!line)
+ goto error;
+ if (!parse_ipmasks(&buf, peer_offset, line)) {
+ free(line);
+ goto error;
+ }
+ free(line);
+ argv += 2;
+ argc -= 2;
+ } else {
+ fprintf(stderr, "Invalid argument: %s\n", argv[0]);
+ goto error;
+ }
+ }
+ *device = buf.dev;
+ return true;
+error:
+ free(buf.dev);
+ return false;
+}
diff --git a/src/tools/config.h b/src/tools/config.h
new file mode 100644
index 0000000..268e3f6
--- /dev/null
+++ b/src/tools/config.h
@@ -0,0 +1,34 @@
+/* Copyright 2015-2016 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. */
+
+#ifndef CONFIG_H
+#define CONFIG_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include "../uapi.h"
+
+struct inflatable_device {
+ struct wgdevice *dev;
+ size_t len;
+ size_t pos;
+};
+
+struct config_ctx {
+ struct inflatable_device buf;
+ size_t peer_offset;
+ struct wgdevice **device;
+ bool is_peer_section;
+ bool is_device_section;
+};
+
+bool config_read_cmd(struct wgdevice **dev, char *argv[], int argc);
+bool config_read_init(struct config_ctx *ctx, struct wgdevice **device, bool append);
+bool config_read_line(struct config_ctx *ctx, const char *line);
+bool config_read_finish(struct config_ctx *ctx);
+
+#endif
diff --git a/src/tools/curve25519.c b/src/tools/curve25519.c
new file mode 100644
index 0000000..6c26535
--- /dev/null
+++ b/src/tools/curve25519.c
@@ -0,0 +1,1258 @@
+/* Original author: Adam Langley <agl@imperialviolet.org>
+ *
+ * Copyright 2008 Google Inc. All Rights Reserved.
+ * Copyright 2015-2016 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms of this file, with or
+ * without modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "curve25519.h"
+
+#include <stdint.h>
+#include <string.h>
+
+#ifndef __always_inline
+#define __always_inline __inline __attribute__((__always_inline__))
+#endif
+
+#ifdef __SIZEOF_INT128__
+typedef uint64_t limb;
+typedef limb felem[5];
+typedef __uint128_t uint128_t;
+
+/* Sum two numbers: output += in */
+static __always_inline void fsum(limb *output, const limb *in)
+{
+ output[0] += in[0];
+ output[1] += in[1];
+ output[2] += in[2];
+ output[3] += in[3];
+ output[4] += in[4];
+}
+
+/* Find the difference of two numbers: output = in - output
+ * (note the order of the arguments!)
+ *
+ * Assumes that out[i] < 2**52
+ * On return, out[i] < 2**55
+ */
+static __always_inline void fdifference_backwards(felem out, const felem in)
+{
+ /* 152 is 19 << 3 */
+ static const limb two54m152 = (((limb)1) << 54) - 152;
+ static const limb two54m8 = (((limb)1) << 54) - 8;
+
+ out[0] = in[0] + two54m152 - out[0];
+ out[1] = in[1] + two54m8 - out[1];
+ out[2] = in[2] + two54m8 - out[2];
+ out[3] = in[3] + two54m8 - out[3];
+ out[4] = in[4] + two54m8 - out[4];
+}
+
+/* Multiply a number by a scalar: output = in * scalar */
+static __always_inline void fscalar_product(felem output, const felem in, const limb scalar)
+{
+ uint128_t a;
+
+ a = ((uint128_t) in[0]) * scalar;
+ output[0] = ((limb)a) & 0x7ffffffffffffUL;
+
+ a = ((uint128_t) in[1]) * scalar + ((limb) (a >> 51));
+ output[1] = ((limb)a) & 0x7ffffffffffffUL;
+
+ a = ((uint128_t) in[2]) * scalar + ((limb) (a >> 51));
+ output[2] = ((limb)a) & 0x7ffffffffffffUL;
+
+ a = ((uint128_t) in[3]) * scalar + ((limb) (a >> 51));
+ output[3] = ((limb)a) & 0x7ffffffffffffUL;
+
+ a = ((uint128_t) in[4]) * scalar + ((limb) (a >> 51));
+ output[4] = ((limb)a) & 0x7ffffffffffffUL;
+
+ output[0] += (a >> 51) * 19;
+}
+
+/* Multiply two numbers: output = in2 * in
+ *
+ * output must be distinct to both inputs. The inputs are reduced coefficient
+ * form, the output is not.
+ *
+ * Assumes that in[i] < 2**55 and likewise for in2.
+ * On return, output[i] < 2**52
+ */
+static __always_inline void fmul(felem output, const felem in2, const felem in)
+{
+ uint128_t t[5];
+ limb r0,r1,r2,r3,r4,s0,s1,s2,s3,s4,c;
+
+ r0 = in[0];
+ r1 = in[1];
+ r2 = in[2];
+ r3 = in[3];
+ r4 = in[4];
+
+ s0 = in2[0];
+ s1 = in2[1];
+ s2 = in2[2];
+ s3 = in2[3];
+ s4 = in2[4];
+
+ t[0] = ((uint128_t) r0) * s0;
+ t[1] = ((uint128_t) r0) * s1 + ((uint128_t) r1) * s0;
+ t[2] = ((uint128_t) r0) * s2 + ((uint128_t) r2) * s0 + ((uint128_t) r1) * s1;
+ t[3] = ((uint128_t) r0) * s3 + ((uint128_t) r3) * s0 + ((uint128_t) r1) * s2 + ((uint128_t) r2) * s1;
+ t[4] = ((uint128_t) r0) * s4 + ((uint128_t) r4) * s0 + ((uint128_t) r3) * s1 + ((uint128_t) r1) * s3 + ((uint128_t) r2) * s2;
+
+ r4 *= 19;
+ r1 *= 19;
+ r2 *= 19;
+ r3 *= 19;
+
+ t[0] += ((uint128_t) r4) * s1 + ((uint128_t) r1) * s4 + ((uint128_t) r2) * s3 + ((uint128_t) r3) * s2;
+ t[1] += ((uint128_t) r4) * s2 + ((uint128_t) r2) * s4 + ((uint128_t) r3) * s3;
+ t[2] += ((uint128_t) r4) * s3 + ((uint128_t) r3) * s4;
+ t[3] += ((uint128_t) r4) * s4;
+
+ r0 = (limb)t[0] & 0x7ffffffffffffUL; c = (limb)(t[0] >> 51);
+ t[1] += c; r1 = (limb)t[1] & 0x7ffffffffffffUL; c = (limb)(t[1] >> 51);
+ t[2] += c; r2 = (limb)t[2] & 0x7ffffffffffffUL; c = (limb)(t[2] >> 51);
+ t[3] += c; r3 = (limb)t[3] & 0x7ffffffffffffUL; c = (limb)(t[3] >> 51);
+ t[4] += c; r4 = (limb)t[4] & 0x7ffffffffffffUL; c = (limb)(t[4] >> 51);
+ r0 += c * 19; c = r0 >> 51; r0 = r0 & 0x7ffffffffffffUL;
+ r1 += c; c = r1 >> 51; r1 = r1 & 0x7ffffffffffffUL;
+ r2 += c;
+
+ output[0] = r0;
+ output[1] = r1;
+ output[2] = r2;
+ output[3] = r3;
+ output[4] = r4;
+}
+
+static __always_inline void fsquare_times(felem output, const felem in, limb count)
+{
+ uint128_t t[5];
+ limb r0,r1,r2,r3,r4,c;
+ limb d0,d1,d2,d4,d419;
+
+ r0 = in[0];
+ r1 = in[1];
+ r2 = in[2];
+ r3 = in[3];
+ r4 = in[4];
+
+ do {
+ d0 = r0 * 2;
+ d1 = r1 * 2;
+ d2 = r2 * 2 * 19;
+ d419 = r4 * 19;
+ d4 = d419 * 2;
+
+ t[0] = ((uint128_t) r0) * r0 + ((uint128_t) d4) * r1 + (((uint128_t) d2) * (r3 ));
+ t[1] = ((uint128_t) d0) * r1 + ((uint128_t) d4) * r2 + (((uint128_t) r3) * (r3 * 19));
+ t[2] = ((uint128_t) d0) * r2 + ((uint128_t) r1) * r1 + (((uint128_t) d4) * (r3 ));
+ t[3] = ((uint128_t) d0) * r3 + ((uint128_t) d1) * r2 + (((uint128_t) r4) * (d419 ));
+ t[4] = ((uint128_t) d0) * r4 + ((uint128_t) d1) * r3 + (((uint128_t) r2) * (r2 ));
+
+ r0 = (limb)t[0] & 0x7ffffffffffffUL; c = (limb)(t[0] >> 51);
+ t[1] += c; r1 = (limb)t[1] & 0x7ffffffffffffUL; c = (limb)(t[1] >> 51);
+ t[2] += c; r2 = (limb)t[2] & 0x7ffffffffffffUL; c = (limb)(t[2] >> 51);
+ t[3] += c; r3 = (limb)t[3] & 0x7ffffffffffffUL; c = (limb)(t[3] >> 51);
+ t[4] += c; r4 = (limb)t[4] & 0x7ffffffffffffUL; c = (limb)(t[4] >> 51);
+ r0 += c * 19; c = r0 >> 51; r0 = r0 & 0x7ffffffffffffUL;
+ r1 += c; c = r1 >> 51; r1 = r1 & 0x7ffffffffffffUL;
+ r2 += c;
+ } while(--count);
+
+ output[0] = r0;
+ output[1] = r1;
+ output[2] = r2;
+ output[3] = r3;
+ output[4] = r4;
+}
+
+/* Load a little-endian 64-bit number */
+static limb load_limb(const uint8_t *in)
+{
+ return
+ ((limb)in[0]) |
+ (((limb)in[1]) << 8) |
+ (((limb)in[2]) << 16) |
+ (((limb)in[3]) << 24) |
+ (((limb)in[4]) << 32) |
+ (((limb)in[5]) << 40) |
+ (((limb)in[6]) << 48) |
+ (((limb)in[7]) << 56);
+}
+
+static void store_limb(uint8_t *out, limb in)
+{
+ out[0] = in & 0xff;
+ out[1] = (in >> 8) & 0xff;
+ out[2] = (in >> 16) & 0xff;
+ out[3] = (in >> 24) & 0xff;
+ out[4] = (in >> 32) & 0xff;
+ out[5] = (in >> 40) & 0xff;
+ out[6] = (in >> 48) & 0xff;
+ out[7] = (in >> 56) & 0xff;
+}
+
+/* Take a little-endian, 32-byte number and expand it into polynomial form */
+static void fexpand(limb *output, const uint8_t *in)
+{
+ output[0] = load_limb(in) & 0x7ffffffffffffUL;
+ output[1] = (load_limb(in+6) >> 3) & 0x7ffffffffffffUL;
+ output[2] = (load_limb(in+12) >> 6) & 0x7ffffffffffffUL;
+ output[3] = (load_limb(in+19) >> 1) & 0x7ffffffffffffUL;
+ output[4] = (load_limb(in+24) >> 12) & 0x7ffffffffffffUL;
+}
+
+/* Take a fully reduced polynomial form number and contract it into a
+ * little-endian, 32-byte array
+ */
+static void fcontract(uint8_t *output, const felem input)
+{
+ uint128_t t[5];
+
+ t[0] = input[0];
+ t[1] = input[1];
+ t[2] = input[2];
+ t[3] = input[3];
+ t[4] = input[4];
+
+ t[1] += t[0] >> 51; t[0] &= 0x7ffffffffffffUL;
+ t[2] += t[1] >> 51; t[1] &= 0x7ffffffffffffUL;
+ t[3] += t[2] >> 51; t[2] &= 0x7ffffffffffffUL;
+ t[4] += t[3] >> 51; t[3] &= 0x7ffffffffffffUL;
+ t[0] += 19 * (t[4] >> 51); t[4] &= 0x7ffffffffffffUL;
+
+ t[1] += t[0] >> 51; t[0] &= 0x7ffffffffffffUL;
+ t[2] += t[1] >> 51; t[1] &= 0x7ffffffffffffUL;
+ t[3] += t[2] >> 51; t[2] &= 0x7ffffffffffffUL;
+ t[4] += t[3] >> 51; t[3] &= 0x7ffffffffffffUL;
+ t[0] += 19 * (t[4] >> 51); t[4] &= 0x7ffffffffffffUL;
+
+ /* now t is between 0 and 2^255-1, properly carried. */
+ /* case 1: between 0 and 2^255-20. case 2: between 2^255-19 and 2^255-1. */
+
+ t[0] += 19;
+
+ t[1] += t[0] >> 51; t[0] &= 0x7ffffffffffffUL;
+ t[2] += t[1] >> 51; t[1] &= 0x7ffffffffffffUL;
+ t[3] += t[2] >> 51; t[2] &= 0x7ffffffffffffUL;
+ t[4] += t[3] >> 51; t[3] &= 0x7ffffffffffffUL;
+ t[0] += 19 * (t[4] >> 51); t[4] &= 0x7ffffffffffffUL;
+
+ /* now between 19 and 2^255-1 in both cases, and offset by 19. */
+
+ t[0] += 0x8000000000000UL - 19;
+ t[1] += 0x8000000000000UL - 1;
+ t[2] += 0x8000000000000UL - 1;
+ t[3] += 0x8000000000000UL - 1;
+ t[4] += 0x8000000000000UL - 1;
+
+ /* now between 2^255 and 2^256-20, and offset by 2^255. */
+
+ t[1] += t[0] >> 51; t[0] &= 0x7ffffffffffffUL;
+ t[2] += t[1] >> 51; t[1] &= 0x7ffffffffffffUL;
+ t[3] += t[2] >> 51; t[2] &= 0x7ffffffffffffUL;
+ t[4] += t[3] >> 51; t[3] &= 0x7ffffffffffffUL;
+ t[4] &= 0x7ffffffffffffUL;
+
+ store_limb(output, t[0] | (t[1] << 51));
+ store_limb(output+8, (t[1] >> 13) | (t[2] << 38));
+ store_limb(output+16, (t[2] >> 26) | (t[3] << 25));
+ store_limb(output+24, (t[3] >> 39) | (t[4] << 12));
+}
+
+/* Input: Q, Q', Q-Q'
+ * Output: 2Q, Q+Q'
+ *
+ * x2 z3: long form
+ * x3 z3: long form
+ * x z: short form, destroyed
+ * xprime zprime: short form, destroyed
+ * qmqp: short form, preserved
+ */
+static void fmonty(limb *x2, limb *z2, /* output 2Q */
+ limb *x3, limb *z3, /* output Q + Q' */
+ limb *x, limb *z, /* input Q */
+ limb *xprime, limb *zprime, /* input Q' */
+ const limb *qmqp /* input Q - Q' */)
+{
+ limb origx[5], origxprime[5], zzz[5], xx[5], zz[5], xxprime[5], zzprime[5], zzzprime[5];
+
+ memcpy(origx, x, 5 * sizeof(limb));
+ fsum(x, z);
+ fdifference_backwards(z, origx); // does x - z
+
+ memcpy(origxprime, xprime, sizeof(limb) * 5);
+ fsum(xprime, zprime);
+ fdifference_backwards(zprime, origxprime);
+ fmul(xxprime, xprime, z);
+ fmul(zzprime, x, zprime);
+ memcpy(origxprime, xxprime, sizeof(limb) * 5);
+ fsum(xxprime, zzprime);
+ fdifference_backwards(zzprime, origxprime);
+ fsquare_times(x3, xxprime, 1);
+ fsquare_times(zzzprime, zzprime, 1);
+ fmul(z3, zzzprime, qmqp);
+
+ fsquare_times(xx, x, 1);
+ fsquare_times(zz, z, 1);
+ fmul(x2, xx, zz);
+ fdifference_backwards(zz, xx); // does zz = xx - zz
+ fscalar_product(zzz, zz, 121665);
+ fsum(zzz, xx);
+ fmul(z2, zz, zzz);
+}
+
+/* Maybe swap the contents of two limb arrays (@a and @b), each @len elements
+ * long. Perform the swap iff @swap is non-zero.
+ *
+ * This function performs the swap without leaking any side-channel
+ * information.
+ */
+static void swap_conditional(limb a[5], limb b[5], limb iswap)
+{
+ unsigned i;
+ const limb swap = -iswap;
+
+ for (i = 0; i < 5; ++i) {
+ const limb x = swap & (a[i] ^ b[i]);
+ a[i] ^= x;
+ b[i] ^= x;
+ }
+}
+
+/* Calculates nQ where Q is the x-coordinate of a point on the curve
+ *
+ * resultx/resultz: the x coordinate of the resulting curve point (short form)
+ * n: a little endian, 32-byte number
+ * q: a point of the curve (short form)
+ */
+static void cmult(limb *resultx, limb *resultz, const uint8_t *n, const limb *q)
+{
+ limb a[5] = {0}, b[5] = {1}, c[5] = {1}, d[5] = {0};
+ limb *nqpqx = a, *nqpqz = b, *nqx = c, *nqz = d, *t;
+ limb e[5] = {0}, f[5] = {1}, g[5] = {0}, h[5] = {1};
+ limb *nqpqx2 = e, *nqpqz2 = f, *nqx2 = g, *nqz2 = h;
+
+ unsigned i, j;
+
+ memcpy(nqpqx, q, sizeof(limb) * 5);
+
+ for (i = 0; i < 32; ++i) {
+ uint8_t byte = n[31 - i];
+ for (j = 0; j < 8; ++j) {
+ const limb bit = byte >> 7;
+
+ swap_conditional(nqx, nqpqx, bit);
+ swap_conditional(nqz, nqpqz, bit);
+ fmonty(nqx2, nqz2,
+ nqpqx2, nqpqz2,
+ nqx, nqz,
+ nqpqx, nqpqz,
+ q);
+ swap_conditional(nqx2, nqpqx2, bit);
+ swap_conditional(nqz2, nqpqz2, bit);
+
+ t = nqx;
+ nqx = nqx2;
+ nqx2 = t;
+ t = nqz;
+ nqz = nqz2;
+ nqz2 = t;
+ t = nqpqx;
+ nqpqx = nqpqx2;
+ nqpqx2 = t;
+ t = nqpqz;
+ nqpqz = nqpqz2;
+ nqpqz2 = t;
+
+ byte <<= 1;
+ }
+ }
+
+ memcpy(resultx, nqx, sizeof(limb) * 5);
+ memcpy(resultz, nqz, sizeof(limb) * 5);
+}
+
+static void crecip(felem out, const felem z)
+{
+ felem a,t0,b,c;
+
+ /* 2 */ fsquare_times(a, z, 1); // a = 2
+ /* 8 */ fsquare_times(t0, a, 2);
+ /* 9 */ fmul(b, t0, z); // b = 9
+ /* 11 */ fmul(a, b, a); // a = 11
+ /* 22 */ fsquare_times(t0, a, 1);
+ /* 2^5 - 2^0 = 31 */ fmul(b, t0, b);
+ /* 2^10 - 2^5 */ fsquare_times(t0, b, 5);
+ /* 2^10 - 2^0 */ fmul(b, t0, b);
+ /* 2^20 - 2^10 */ fsquare_times(t0, b, 10);
+ /* 2^20 - 2^0 */ fmul(c, t0, b);
+ /* 2^40 - 2^20 */ fsquare_times(t0, c, 20);
+ /* 2^40 - 2^0 */ fmul(t0, t0, c);
+ /* 2^50 - 2^10 */ fsquare_times(t0, t0, 10);
+ /* 2^50 - 2^0 */ fmul(b, t0, b);
+ /* 2^100 - 2^50 */ fsquare_times(t0, b, 50);
+ /* 2^100 - 2^0 */ fmul(c, t0, b);
+ /* 2^200 - 2^100 */ fsquare_times(t0, c, 100);
+ /* 2^200 - 2^0 */ fmul(t0, t0, c);
+ /* 2^250 - 2^50 */ fsquare_times(t0, t0, 50);
+ /* 2^250 - 2^0 */ fmul(t0, t0, b);
+ /* 2^255 - 2^5 */ fsquare_times(t0, t0, 5);
+ /* 2^255 - 21 */ fmul(out, t0, a);
+}
+
+void curve25519(uint8_t mypublic[CURVE25519_POINT_SIZE], const uint8_t secret[CURVE25519_POINT_SIZE], const uint8_t basepoint[CURVE25519_POINT_SIZE])
+{
+ limb bp[5], x[5], z[5], zmone[5];
+ uint8_t e[32];
+
+ memcpy(e, secret, 32);
+ curve25519_normalize_secret(e);
+
+ fexpand(bp, basepoint);
+ cmult(x, z, e, bp);
+ crecip(zmone, z);
+ fmul(z, x, zmone);
+ fcontract(mypublic, z);
+}
+
+#else
+typedef int64_t limb;
+
+/* Field element representation:
+ *
+ * Field elements are written as an array of signed, 64-bit limbs, least
+ * significant first. The value of the field element is:
+ * x[0] + 2^26·x[1] + x^51·x[2] + 2^102·x[3] + ...
+ *
+ * i.e. the limbs are 26, 25, 26, 25, ... bits wide. */
+
+/* Sum two numbers: output += in */
+static void fsum(limb *output, const limb *in)
+{
+ unsigned i;
+ for (i = 0; i < 10; i += 2) {
+ output[0 + i] = output[0 + i] + in[0 + i];
+ output[1 + i] = output[1 + i] + in[1 + i];
+ }
+}
+
+/* Find the difference of two numbers: output = in - output
+ * (note the order of the arguments!). */
+static void fdifference(limb *output, const limb *in)
+{
+ unsigned i;
+ for (i = 0; i < 10; ++i) {
+ output[i] = in[i] - output[i];
+ }
+}
+
+/* Multiply a number by a scalar: output = in * scalar */
+static void fscalar_product(limb *output, const limb *in, const limb scalar)
+{
+ unsigned i;
+ for (i = 0; i < 10; ++i) {
+ output[i] = in[i] * scalar;
+ }
+}
+
+/* Multiply two numbers: output = in2 * in
+ *
+ * output must be distinct to both inputs. The inputs are reduced coefficient
+ * form, the output is not.
+ *
+ * output[x] <= 14 * the largest product of the input limbs. */
+static void fproduct(limb *output, const limb *in2, const limb *in)
+{
+ output[0] = ((limb) ((int32_t) in2[0])) * ((int32_t) in[0]);
+ output[1] = ((limb) ((int32_t) in2[0])) * ((int32_t) in[1]) +
+ ((limb) ((int32_t) in2[1])) * ((int32_t) in[0]);
+ output[2] = 2 * ((limb) ((int32_t) in2[1])) * ((int32_t) in[1]) +
+ ((limb) ((int32_t) in2[0])) * ((int32_t) in[2]) +
+ ((limb) ((int32_t) in2[2])) * ((int32_t) in[0]);
+ output[3] = ((limb) ((int32_t) in2[1])) * ((int32_t) in[2]) +
+ ((limb) ((int32_t) in2[2])) * ((int32_t) in[1]) +
+ ((limb) ((int32_t) in2[0])) * ((int32_t) in[3]) +
+ ((limb) ((int32_t) in2[3])) * ((int32_t) in[0]);
+ output[4] = ((limb) ((int32_t) in2[2])) * ((int32_t) in[2]) +
+ 2 * (((limb) ((int32_t) in2[1])) * ((int32_t) in[3]) +
+ ((limb) ((int32_t) in2[3])) * ((int32_t) in[1])) +
+ ((limb) ((int32_t) in2[0])) * ((int32_t) in[4]) +
+ ((limb) ((int32_t) in2[4])) * ((int32_t) in[0]);
+ output[5] = ((limb) ((int32_t) in2[2])) * ((int32_t) in[3]) +
+ ((limb) ((int32_t) in2[3])) * ((int32_t) in[2]) +
+ ((limb) ((int32_t) in2[1])) * ((int32_t) in[4]) +
+ ((limb) ((int32_t) in2[4])) * ((int32_t) in[1]) +
+ ((limb) ((int32_t) in2[0])) * ((int32_t) in[5]) +
+ ((limb) ((int32_t) in2[5])) * ((int32_t) in[0]);
+ output[6] = 2 * (((limb) ((int32_t) in2[3])) * ((int32_t) in[3]) +
+ ((limb) ((int32_t) in2[1])) * ((int32_t) in[5]) +
+ ((limb) ((int32_t) in2[5])) * ((int32_t) in[1])) +
+ ((limb) ((int32_t) in2[2])) * ((int32_t) in[4]) +
+ ((limb) ((int32_t) in2[4])) * ((int32_t) in[2]) +
+ ((limb) ((int32_t) in2[0])) * ((int32_t) in[6]) +
+ ((limb) ((int32_t) in2[6])) * ((int32_t) in[0]);
+ output[7] = ((limb) ((int32_t) in2[3])) * ((int32_t) in[4]) +
+ ((limb) ((int32_t) in2[4])) * ((int32_t) in[3]) +
+ ((limb) ((int32_t) in2[2])) * ((int32_t) in[5]) +
+ ((limb) ((int32_t) in2[5])) * ((int32_t) in[2]) +
+ ((limb) ((int32_t) in2[1])) * ((int32_t) in[6]) +
+ ((limb) ((int32_t) in2[6])) * ((int32_t) in[1]) +
+ ((limb) ((int32_t) in2[0])) * ((int32_t) in[7]) +
+ ((limb) ((int32_t) in2[7])) * ((int32_t) in[0]);
+ output[8] = ((limb) ((int32_t) in2[4])) * ((int32_t) in[4]) +
+ 2 * (((limb) ((int32_t) in2[3])) * ((int32_t) in[5]) +
+ ((limb) ((int32_t) in2[5])) * ((int32_t) in[3]) +
+ ((limb) ((int32_t) in2[1])) * ((int32_t) in[7]) +
+ ((limb) ((int32_t) in2[7])) * ((int32_t) in[1])) +
+ ((limb) ((int32_t) in2[2])) * ((int32_t) in[6]) +
+ ((limb) ((int32_t) in2[6])) * ((int32_t) in[2]) +
+ ((limb) ((int32_t) in2[0])) * ((int32_t) in[8]) +
+ ((limb) ((int32_t) in2[8])) * ((int32_t) in[0]);
+ output[9] = ((limb) ((int32_t) in2[4])) * ((int32_t) in[5]) +
+ ((limb) ((int32_t) in2[5])) * ((int32_t) in[4]) +
+ ((limb) ((int32_t) in2[3])) * ((int32_t) in[6]) +
+ ((limb) ((int32_t) in2[6])) * ((int32_t) in[3]) +
+ ((limb) ((int32_t) in2[2])) * ((int32_t) in[7]) +
+ ((limb) ((int32_t) in2[7])) * ((int32_t) in[2]) +
+ ((limb) ((int32_t) in2[1])) * ((int32_t) in[8]) +
+ ((limb) ((int32_t) in2[8])) * ((int32_t) in[1]) +
+ ((limb) ((int32_t) in2[0])) * ((int32_t) in[9]) +
+ ((limb) ((int32_t) in2[9])) * ((int32_t) in[0]);
+ output[10] = 2 * (((limb) ((int32_t) in2[5])) * ((int32_t) in[5]) +
+ ((limb) ((int32_t) in2[3])) * ((int32_t) in[7]) +
+ ((limb) ((int32_t) in2[7])) * ((int32_t) in[3]) +
+ ((limb) ((int32_t) in2[1])) * ((int32_t) in[9]) +
+ ((limb) ((int32_t) in2[9])) * ((int32_t) in[1])) +
+ ((limb) ((int32_t) in2[4])) * ((int32_t) in[6]) +
+ ((limb) ((int32_t) in2[6])) * ((int32_t) in[4]) +
+ ((limb) ((int32_t) in2[2])) * ((int32_t) in[8]) +
+ ((limb) ((int32_t) in2[8])) * ((int32_t) in[2]);
+ output[11] = ((limb) ((int32_t) in2[5])) * ((int32_t) in[6]) +
+ ((limb) ((int32_t) in2[6])) * ((int32_t) in[5]) +
+ ((limb) ((int32_t) in2[4])) * ((int32_t) in[7]) +
+ ((limb) ((int32_t) in2[7])) * ((int32_t) in[4]) +
+ ((limb) ((int32_t) in2[3])) * ((int32_t) in[8]) +
+ ((limb) ((int32_t) in2[8])) * ((int32_t) in[3]) +
+ ((limb) ((int32_t) in2[2])) * ((int32_t) in[9]) +
+ ((limb) ((int32_t) in2[9])) * ((int32_t) in[2]);
+ output[12] = ((limb) ((int32_t) in2[6])) * ((int32_t) in[6]) +
+ 2 * (((limb) ((int32_t) in2[5])) * ((int32_t) in[7]) +
+ ((limb) ((int32_t) in2[7])) * ((int32_t) in[5]) +
+ ((limb) ((int32_t) in2[3])) * ((int32_t) in[9]) +
+ ((limb) ((int32_t) in2[9])) * ((int32_t) in[3])) +
+ ((limb) ((int32_t) in2[4])) * ((int32_t) in[8]) +
+ ((limb) ((int32_t) in2[8])) * ((int32_t) in[4]);
+ output[13] = ((limb) ((int32_t) in2[6])) * ((int32_t) in[7]) +
+ ((limb) ((int32_t) in2[7])) * ((int32_t) in[6]) +
+ ((limb) ((int32_t) in2[5])) * ((int32_t) in[8]) +
+ ((limb) ((int32_t) in2[8])) * ((int32_t) in[5]) +
+ ((limb) ((int32_t) in2[4])) * ((int32_t) in[9]) +
+ ((limb) ((int32_t) in2[9])) * ((int32_t) in[4]);
+ output[14] = 2 * (((limb) ((int32_t) in2[7])) * ((int32_t) in[7]) +
+ ((limb) ((int32_t) in2[5])) * ((int32_t) in[9]) +
+ ((limb) ((int32_t) in2[9])) * ((int32_t) in[5])) +
+ ((limb) ((int32_t) in2[6])) * ((int32_t) in[8]) +
+ ((limb) ((int32_t) in2[8])) * ((int32_t) in[6]);
+ output[15] = ((limb) ((int32_t) in2[7])) * ((int32_t) in[8]) +
+ ((limb) ((int32_t) in2[8])) * ((int32_t) in[7]) +
+ ((limb) ((int32_t) in2[6])) * ((int32_t) in[9]) +
+ ((limb) ((int32_t) in2[9])) * ((int32_t) in[6]);
+ output[16] = ((limb) ((int32_t) in2[8])) * ((int32_t) in[8]) +
+ 2 * (((limb) ((int32_t) in2[7])) * ((int32_t) in[9]) +
+ ((limb) ((int32_t) in2[9])) * ((int32_t) in[7]));
+ output[17] = ((limb) ((int32_t) in2[8])) * ((int32_t) in[9]) +
+ ((limb) ((int32_t) in2[9])) * ((int32_t) in[8]);
+ output[18] = 2 * ((limb) ((int32_t) in2[9])) * ((int32_t) in[9]);
+}
+
+/* Reduce a long form to a short form by taking the input mod 2^255 - 19.
+ *
+ * On entry: |output[i]| < 14*2^54
+ * On exit: |output[0..8]| < 280*2^54 */
+static void freduce_degree(limb *output)
+{
+ /* Each of these shifts and adds ends up multiplying the value by 19.
+ *
+ * For output[0..8], the absolute entry value is < 14*2^54 and we add, at
+ * most, 19*14*2^54 thus, on exit, |output[0..8]| < 280*2^54. */
+ output[8] += output[18] << 4;
+ output[8] += output[18] << 1;
+ output[8] += output[18];
+ output[7] += output[17] << 4;
+ output[7] += output[17] << 1;
+ output[7] += output[17];
+ output[6] += output[16] << 4;
+ output[6] += output[16] << 1;
+ output[6] += output[16];
+ output[5] += output[15] << 4;
+ output[5] += output[15] << 1;
+ output[5] += output[15];
+ output[4] += output[14] << 4;
+ output[4] += output[14] << 1;
+ output[4] += output[14];
+ output[3] += output[13] << 4;
+ output[3] += output[13] << 1;
+ output[3] += output[13];
+ output[2] += output[12] << 4;
+ output[2] += output[12] << 1;
+ output[2] += output[12];
+ output[1] += output[11] << 4;
+ output[1] += output[11] << 1;
+ output[1] += output[11];
+ output[0] += output[10] << 4;
+ output[0] += output[10] << 1;
+ output[0] += output[10];
+}
+
+#if (-1 & 3) != 3
+#error "This code only works on a two's complement system"
+#endif
+
+/* return v / 2^26, using only shifts and adds.
+ *
+ * On entry: v can take any value. */
+static inline limb div_by_2_26(const limb v)
+{
+ /* High word of v; no shift needed. */
+ const uint32_t highword = (uint32_t) (((uint64_t) v) >> 32);
+ /* Set to all 1s if v was negative; else set to 0s. */
+ const int32_t sign = ((int32_t) highword) >> 31;
+ /* Set to 0x3ffffff if v was negative; else set to 0. */
+ const int32_t roundoff = ((uint32_t) sign) >> 6;
+ /* Should return v / (1<<26) */
+ return (v + roundoff) >> 26;
+}
+
+/* return v / (2^25), using only shifts and adds.
+ *
+ * On entry: v can take any value. */
+static inline limb div_by_2_25(const limb v)
+{
+ /* High word of v; no shift needed*/
+ const uint32_t highword = (uint32_t) (((uint64_t) v) >> 32);
+ /* Set to all 1s if v was negative; else set to 0s. */
+ const int32_t sign = ((int32_t) highword) >> 31;
+ /* Set to 0x1ffffff if v was negative; else set to 0. */
+ const int32_t roundoff = ((uint32_t) sign) >> 7;
+ /* Should return v / (1<<25) */
+ return (v + roundoff) >> 25;
+}
+
+/* Reduce all coefficients of the short form input so that |x| < 2^26.
+ *
+ * On entry: |output[i]| < 280*2^54 */
+static void freduce_coefficients(limb *output)
+{
+ unsigned i;
+
+ output[10] = 0;
+
+ for (i = 0; i < 10; i += 2) {
+ limb over = div_by_2_26(output[i]);
+ /* The entry condition (that |output[i]| < 280*2^54) means that over is, at
+ * most, 280*2^28 in the first iteration of this loop. This is added to the
+ * next limb and we can approximate the resulting bound of that limb by
+ * 281*2^54. */
+ output[i] -= over << 26;
+ output[i+1] += over;
+
+ /* For the first iteration, |output[i+1]| < 281*2^54, thus |over| <
+ * 281*2^29. When this is added to the next limb, the resulting bound can
+ * be approximated as 281*2^54.
+ *
+ * For subsequent iterations of the loop, 281*2^54 remains a conservative
+ * bound and no overflow occurs. */
+ over = div_by_2_25(output[i+1]);
+ output[i+1] -= over << 25;
+ output[i+2] += over;
+ }
+ /* Now |output[10]| < 281*2^29 and all other coefficients are reduced. */
+ output[0] += output[10] << 4;
+ output[0] += output[10] << 1;
+ output[0] += output[10];
+
+ output[10] = 0;
+
+ /* Now output[1..9] are reduced, and |output[0]| < 2^26 + 19*281*2^29
+ * So |over| will be no more than 2^16. */
+ {
+ limb over = div_by_2_26(output[0]);
+ output[0] -= over << 26;
+ output[1] += over;
+ }
+
+ /* Now output[0,2..9] are reduced, and |output[1]| < 2^25 + 2^16 < 2^26. The
+ * bound on |output[1]| is sufficient to meet our needs. */
+}
+
+/* A helpful wrapper around fproduct: output = in * in2.
+ *
+ * On entry: |in[i]| < 2^27 and |in2[i]| < 2^27.
+ *
+ * output must be distinct to both inputs. The output is reduced degree
+ * (indeed, one need only provide storage for 10 limbs) and |output[i]| < 2^26. */
+static void fmul(limb *output, const limb *in, const limb *in2)
+{
+ limb t[19];
+ fproduct(t, in, in2);
+ /* |t[i]| < 14*2^54 */
+ freduce_degree(t);
+ freduce_coefficients(t);
+ /* |t[i]| < 2^26 */
+ memcpy(output, t, sizeof(limb) * 10);
+}
+
+/* Square a number: output = in**2
+ *
+ * output must be distinct from the input. The inputs are reduced coefficient
+ * form, the output is not.
+ *
+ * output[x] <= 14 * the largest product of the input limbs. */
+static void fsquare_inner(limb *output, const limb *in)
+{
+ output[0] = ((limb) ((int32_t) in[0])) * ((int32_t) in[0]);
+ output[1] = 2 * ((limb) ((int32_t) in[0])) * ((int32_t) in[1]);
+ output[2] = 2 * (((limb) ((int32_t) in[1])) * ((int32_t) in[1]) +
+ ((limb) ((int32_t) in[0])) * ((int32_t) in[2]));
+ output[3] = 2 * (((limb) ((int32_t) in[1])) * ((int32_t) in[2]) +
+ ((limb) ((int32_t) in[0])) * ((int32_t) in[3]));
+ output[4] = ((limb) ((int32_t) in[2])) * ((int32_t) in[2]) +
+ 4 * ((limb) ((int32_t) in[1])) * ((int32_t) in[3]) +
+ 2 * ((limb) ((int32_t) in[0])) * ((int32_t) in[4]);
+ output[5] = 2 * (((limb) ((int32_t) in[2])) * ((int32_t) in[3]) +
+ ((limb) ((int32_t) in[1])) * ((int32_t) in[4]) +
+ ((limb) ((int32_t) in[0])) * ((int32_t) in[5]));
+ output[6] = 2 * (((limb) ((int32_t) in[3])) * ((int32_t) in[3]) +
+ ((limb) ((int32_t) in[2])) * ((int32_t) in[4]) +
+ ((limb) ((int32_t) in[0])) * ((int32_t) in[6]) +
+ 2 * ((limb) ((int32_t) in[1])) * ((int32_t) in[5]));
+ output[7] = 2 * (((limb) ((int32_t) in[3])) * ((int32_t) in[4]) +
+ ((limb) ((int32_t) in[2])) * ((int32_t) in[5]) +
+ ((limb) ((int32_t) in[1])) * ((int32_t) in[6]) +
+ ((limb) ((int32_t) in[0])) * ((int32_t) in[7]));
+ output[8] = ((limb) ((int32_t) in[4])) * ((int32_t) in[4]) +
+ 2 * (((limb) ((int32_t) in[2])) * ((int32_t) in[6]) +
+ ((limb) ((int32_t) in[0])) * ((int32_t) in[8]) +
+ 2 * (((limb) ((int32_t) in[1])) * ((int32_t) in[7]) +
+ ((limb) ((int32_t) in[3])) * ((int32_t) in[5])));
+ output[9] = 2 * (((limb) ((int32_t) in[4])) * ((int32_t) in[5]) +
+ ((limb) ((int32_t) in[3])) * ((int32_t) in[6]) +
+ ((limb) ((int32_t) in[2])) * ((int32_t) in[7]) +
+ ((limb) ((int32_t) in[1])) * ((int32_t) in[8]) +
+ ((limb) ((int32_t) in[0])) * ((int32_t) in[9]));
+ output[10] = 2 * (((limb) ((int32_t) in[5])) * ((int32_t) in[5]) +
+ ((limb) ((int32_t) in[4])) * ((int32_t) in[6]) +
+ ((limb) ((int32_t) in[2])) * ((int32_t) in[8]) +
+ 2 * (((limb) ((int32_t) in[3])) * ((int32_t) in[7]) +
+ ((limb) ((int32_t) in[1])) * ((int32_t) in[9])));
+ output[11] = 2 * (((limb) ((int32_t) in[5])) * ((int32_t) in[6]) +
+ ((limb) ((int32_t) in[4])) * ((int32_t) in[7]) +
+ ((limb) ((int32_t) in[3])) * ((int32_t) in[8]) +
+ ((limb) ((int32_t) in[2])) * ((int32_t) in[9]));
+ output[12] = ((limb) ((int32_t) in[6])) * ((int32_t) in[6]) +
+ 2 * (((limb) ((int32_t) in[4])) * ((int32_t) in[8]) +
+ 2 * (((limb) ((int32_t) in[5])) * ((int32_t) in[7]) +
+ ((limb) ((int32_t) in[3])) * ((int32_t) in[9])));
+ output[13] = 2 * (((limb) ((int32_t) in[6])) * ((int32_t) in[7]) +
+ ((limb) ((int32_t) in[5])) * ((int32_t) in[8]) +
+ ((limb) ((int32_t) in[4])) * ((int32_t) in[9]));
+ output[14] = 2 * (((limb) ((int32_t) in[7])) * ((int32_t) in[7]) +
+ ((limb) ((int32_t) in[6])) * ((int32_t) in[8]) +
+ 2 * ((limb) ((int32_t) in[5])) * ((int32_t) in[9]));
+ output[15] = 2 * (((limb) ((int32_t) in[7])) * ((int32_t) in[8]) +
+ ((limb) ((int32_t) in[6])) * ((int32_t) in[9]));
+ output[16] = ((limb) ((int32_t) in[8])) * ((int32_t) in[8]) +
+ 4 * ((limb) ((int32_t) in[7])) * ((int32_t) in[9]);
+ output[17] = 2 * ((limb) ((int32_t) in[8])) * ((int32_t) in[9]);
+ output[18] = 2 * ((limb) ((int32_t) in[9])) * ((int32_t) in[9]);
+}
+
+/* fsquare sets output = in^2.
+ *
+ * On entry: The |in| argument is in reduced coefficients form and |in[i]| <
+ * 2^27.
+ *
+ * On exit: The |output| argument is in reduced coefficients form (indeed, one
+ * need only provide storage for 10 limbs) and |out[i]| < 2^26. */
+static void fsquare(limb *output, const limb *in)
+{
+ limb t[19];
+ fsquare_inner(t, in);
+ /* |t[i]| < 14*2^54 because the largest product of two limbs will be <
+ * 2^(27+27) and fsquare_inner adds together, at most, 14 of those
+ * products. */
+ freduce_degree(t);
+ freduce_coefficients(t);
+ /* |t[i]| < 2^26 */
+ memcpy(output, t, sizeof(limb) * 10);
+}
+
+/* Take a little-endian, 32-byte number and expand it into polynomial form */
+static void fexpand(limb *output, const uint8_t *input)
+{
+#define F(n,start,shift,mask) \
+ output[n] = ((((limb) input[start + 0]) | \
+ ((limb) input[start + 1]) << 8 | \
+ ((limb) input[start + 2]) << 16 | \
+ ((limb) input[start + 3]) << 24) >> shift) & mask;
+ F(0, 0, 0, 0x3ffffff);
+ F(1, 3, 2, 0x1ffffff);
+ F(2, 6, 3, 0x3ffffff);
+ F(3, 9, 5, 0x1ffffff);
+ F(4, 12, 6, 0x3ffffff);
+ F(5, 16, 0, 0x1ffffff);
+ F(6, 19, 1, 0x3ffffff);
+ F(7, 22, 3, 0x1ffffff);
+ F(8, 25, 4, 0x3ffffff);
+ F(9, 28, 6, 0x1ffffff);
+#undef F
+}
+
+#if (-32 >> 1) != -16
+#error "This code only works when >> does sign-extension on negative numbers"
+#endif
+
+/* int32_t_eq returns 0xffffffff iff a == b and zero otherwise. */
+static int32_t int32_t_eq(int32_t a, int32_t b)
+{
+ a = ~(a ^ b);
+ a &= a << 16;
+ a &= a << 8;
+ a &= a << 4;
+ a &= a << 2;
+ a &= a << 1;
+ return a >> 31;
+}
+
+/* int32_t_gte returns 0xffffffff if a >= b and zero otherwise, where a and b are
+ * both non-negative. */
+static int32_t int32_t_gte(int32_t a, int32_t b)
+{
+ a -= b;
+ /* a >= 0 iff a >= b. */
+ return ~(a >> 31);
+}
+
+/* Take a fully reduced polynomial form number and contract it into a
+ * little-endian, 32-byte array.
+ *
+ * On entry: |input_limbs[i]| < 2^26 */
+static void fcontract(uint8_t *output, limb *input_limbs)
+{
+ int i;
+ int j;
+ int32_t input[10];
+ int32_t mask;
+
+ /* |input_limbs[i]| < 2^26, so it's valid to convert to an int32_t. */
+ for (i = 0; i < 10; i++) {
+ input[i] = input_limbs[i];
+ }
+
+ for (j = 0; j < 2; ++j) {
+ for (i = 0; i < 9; ++i) {
+ if ((i & 1) == 1) {
+ /* This calculation is a time-invariant way to make input[i]
+ * non-negative by borrowing from the next-larger limb. */
+ const int32_t mask = input[i] >> 31;
+ const int32_t carry = -((input[i] & mask) >> 25);
+ input[i] = input[i] + (carry << 25);
+ input[i+1] = input[i+1] - carry;
+ } else {
+ const int32_t mask = input[i] >> 31;
+ const int32_t carry = -((input[i] & mask) >> 26);
+ input[i] = input[i] + (carry << 26);
+ input[i+1] = input[i+1] - carry;
+ }
+ }
+
+ /* There's no greater limb for input[9] to borrow from, but we can multiply
+ * by 19 and borrow from input[0], which is valid mod 2^255-19. */
+ {
+ const int32_t mask = input[9] >> 31;
+ const int32_t carry = -((input[9] & mask) >> 25);
+ input[9] = input[9] + (carry << 25);
+ input[0] = input[0] - (carry * 19);
+ }
+
+ /* After the first iteration, input[1..9] are non-negative and fit within
+ * 25 or 26 bits, depending on position. However, input[0] may be
+ * negative. */
+ }
+
+ /* The first borrow-propagation pass above ended with every limb
+ except (possibly) input[0] non-negative.
+ If input[0] was negative after the first pass, then it was because of a
+ carry from input[9]. On entry, input[9] < 2^26 so the carry was, at most,
+ one, since (2**26-1) >> 25 = 1. Thus input[0] >= -19.
+ In the second pass, each limb is decreased by at most one. Thus the second
+ borrow-propagation pass could only have wrapped around to decrease
+ input[0] again if the first pass left input[0] negative *and* input[1]
+ through input[9] were all zero. In that case, input[1] is now 2^25 - 1,
+ and this last borrow-propagation step will leave input[1] non-negative. */
+ {
+ const int32_t mask = input[0] >> 31;
+ const int32_t carry = -((input[0] & mask) >> 26);
+ input[0] = input[0] + (carry << 26);
+ input[1] = input[1] - carry;
+ }
+
+ /* All input[i] are now non-negative. However, there might be values between
+ * 2^25 and 2^26 in a limb which is, nominally, 25 bits wide. */
+ for (j = 0; j < 2; j++) {
+ for (i = 0; i < 9; i++) {
+ if ((i & 1) == 1) {
+ const int32_t carry = input[i] >> 25;
+ input[i] &= 0x1ffffff;
+ input[i+1] += carry;
+ } else {
+ const int32_t carry = input[i] >> 26;
+ input[i] &= 0x3ffffff;
+ input[i+1] += carry;
+ }
+ }
+
+ {
+ const int32_t carry = input[9] >> 25;
+ input[9] &= 0x1ffffff;
+ input[0] += 19*carry;
+ }
+ }
+
+ /* If the first carry-chain pass, just above, ended up with a carry from
+ * input[9], and that caused input[0] to be out-of-bounds, then input[0] was
+ * < 2^26 + 2*19, because the carry was, at most, two.
+ *
+ * If the second pass carried from input[9] again then input[0] is < 2*19 and
+ * the input[9] -> input[0] carry didn't push input[0] out of bounds. */
+
+ /* It still remains the case that input might be between 2^255-19 and 2^255.
+ * In this case, input[1..9] must take their maximum value and input[0] must
+ * be >= (2^255-19) & 0x3ffffff, which is 0x3ffffed. */
+ mask = int32_t_gte(input[0], 0x3ffffed);
+ for (i = 1; i < 10; i++) {
+ if ((i & 1) == 1) {
+ mask &= int32_t_eq(input[i], 0x1ffffff);
+ } else {
+ mask &= int32_t_eq(input[i], 0x3ffffff);
+ }
+ }
+
+ /* mask is either 0xffffffff (if input >= 2^255-19) and zero otherwise. Thus
+ * this conditionally subtracts 2^255-19. */
+ input[0] -= mask & 0x3ffffed;
+
+ for (i = 1; i < 10; i++) {
+ if ((i & 1) == 1) {
+ input[i] -= mask & 0x1ffffff;
+ } else {
+ input[i] -= mask & 0x3ffffff;
+ }
+ }
+
+ input[1] <<= 2;
+ input[2] <<= 3;
+ input[3] <<= 5;
+ input[4] <<= 6;
+ input[6] <<= 1;
+ input[7] <<= 3;
+ input[8] <<= 4;
+ input[9] <<= 6;
+#define F(i, s) \
+ output[s+0] |= input[i] & 0xff; \
+ output[s+1] = (input[i] >> 8) & 0xff; \
+ output[s+2] = (input[i] >> 16) & 0xff; \
+ output[s+3] = (input[i] >> 24) & 0xff;
+ output[0] = 0;
+ output[16] = 0;
+ F(0,0);
+ F(1,3);
+ F(2,6);
+ F(3,9);
+ F(4,12);
+ F(5,16);
+ F(6,19);
+ F(7,22);
+ F(8,25);
+ F(9,28);
+#undef F
+}
+
+/* Input: Q, Q', Q-Q'
+ * Output: 2Q, Q+Q'
+ *
+ * x2 z3: long form
+ * x3 z3: long form
+ * x z: short form, destroyed
+ * xprime zprime: short form, destroyed
+ * qmqp: short form, preserved
+ *
+ * On entry and exit, the absolute value of the limbs of all inputs and outputs
+ * are < 2^26. */
+static void fmonty(limb *x2, limb *z2, /* output 2Q */
+ limb *x3, limb *z3, /* output Q + Q' */
+ limb *x, limb *z, /* input Q */
+ limb *xprime, limb *zprime, /* input Q' */
+ const limb *qmqp /* input Q - Q' */)
+{
+ limb origx[10], origxprime[10], zzz[19], xx[19], zz[19], xxprime[19],
+ zzprime[19], zzzprime[19], xxxprime[19];
+
+ memcpy(origx, x, 10 * sizeof(limb));
+ fsum(x, z);
+ /* |x[i]| < 2^27 */
+ fdifference(z, origx); /* does x - z */
+ /* |z[i]| < 2^27 */
+
+ memcpy(origxprime, xprime, sizeof(limb) * 10);
+ fsum(xprime, zprime);
+ /* |xprime[i]| < 2^27 */
+ fdifference(zprime, origxprime);
+ /* |zprime[i]| < 2^27 */
+ fproduct(xxprime, xprime, z);
+ /* |xxprime[i]| < 14*2^54: the largest product of two limbs will be <
+ * 2^(27+27) and fproduct adds together, at most, 14 of those products.
+ * (Approximating that to 2^58 doesn't work out.) */
+ fproduct(zzprime, x, zprime);
+ /* |zzprime[i]| < 14*2^54 */
+ freduce_degree(xxprime);
+ freduce_coefficients(xxprime);
+ /* |xxprime[i]| < 2^26 */
+ freduce_degree(zzprime);
+ freduce_coefficients(zzprime);
+ /* |zzprime[i]| < 2^26 */
+ memcpy(origxprime, xxprime, sizeof(limb) * 10);
+ fsum(xxprime, zzprime);
+ /* |xxprime[i]| < 2^27 */
+ fdifference(zzprime, origxprime);
+ /* |zzprime[i]| < 2^27 */
+ fsquare(xxxprime, xxprime);
+ /* |xxxprime[i]| < 2^26 */
+ fsquare(zzzprime, zzprime);
+ /* |zzzprime[i]| < 2^26 */
+ fproduct(zzprime, zzzprime, qmqp);
+ /* |zzprime[i]| < 14*2^52 */
+ freduce_degree(zzprime);
+ freduce_coefficients(zzprime);
+ /* |zzprime[i]| < 2^26 */
+ memcpy(x3, xxxprime, sizeof(limb) * 10);
+ memcpy(z3, zzprime, sizeof(limb) * 10);
+
+ fsquare(xx, x);
+ /* |xx[i]| < 2^26 */
+ fsquare(zz, z);
+ /* |zz[i]| < 2^26 */
+ fproduct(x2, xx, zz);
+ /* |x2[i]| < 14*2^52 */
+ freduce_degree(x2);
+ freduce_coefficients(x2);
+ /* |x2[i]| < 2^26 */
+ fdifference(zz, xx); // does zz = xx - zz
+ /* |zz[i]| < 2^27 */
+ memset(zzz + 10, 0, sizeof(limb) * 9);
+ fscalar_product(zzz, zz, 121665);
+ /* |zzz[i]| < 2^(27+17) */
+ /* No need to call freduce_degree here:
+ fscalar_product doesn't increase the degree of its input. */
+ freduce_coefficients(zzz);
+ /* |zzz[i]| < 2^26 */
+ fsum(zzz, xx);
+ /* |zzz[i]| < 2^27 */
+ fproduct(z2, zz, zzz);
+ /* |z2[i]| < 14*2^(26+27) */
+ freduce_degree(z2);
+ freduce_coefficients(z2);
+ /* |z2|i| < 2^26 */
+}
+
+/* Conditionally swap two reduced-form limb arrays if 'iswap' is 1, but leave
+ * them unchanged if 'iswap' is 0. Runs in data-invariant time to avoid
+ * side-channel attacks.
+ *
+ * NOTE that this function requires that 'iswap' be 1 or 0; other values give
+ * wrong results. Also, the two limb arrays must be in reduced-coefficient,
+ * reduced-degree form: the values in a[10..19] or b[10..19] aren't swapped,
+ * and all all values in a[0..9],b[0..9] must have magnitude less than
+ * INT32_MAX. */
+static void swap_conditional(limb a[19], limb b[19], limb iswap)
+{
+ unsigned i;
+ const int32_t swap = (int32_t) -iswap;
+
+ for (i = 0; i < 10; ++i) {
+ const int32_t x = swap & ( ((int32_t)a[i]) ^ ((int32_t)b[i]) );
+ a[i] = ((int32_t)a[i]) ^ x;
+ b[i] = ((int32_t)b[i]) ^ x;
+ }
+}
+
+/* Calculates nQ where Q is the x-coordinate of a point on the curve
+ *
+ * resultx/resultz: the x coordinate of the resulting curve point (short form)
+ * n: a little endian, 32-byte number
+ * q: a point of the curve (short form) */
+static void cmult(limb *resultx, limb *resultz, const uint8_t *n, const limb *q)
+{
+ limb a[19] = {0}, b[19] = {1}, c[19] = {1}, d[19] = {0};
+ limb *nqpqx = a, *nqpqz = b, *nqx = c, *nqz = d, *t;
+ limb e[19] = {0}, f[19] = {1}, g[19] = {0}, h[19] = {1};
+ limb *nqpqx2 = e, *nqpqz2 = f, *nqx2 = g, *nqz2 = h;
+
+ unsigned i, j;
+
+ memcpy(nqpqx, q, sizeof(limb) * 10);
+
+ for (i = 0; i < 32; ++i) {
+ uint8_t byte = n[31 - i];
+ for (j = 0; j < 8; ++j) {
+ const limb bit = byte >> 7;
+
+ swap_conditional(nqx, nqpqx, bit);
+ swap_conditional(nqz, nqpqz, bit);
+ fmonty(nqx2, nqz2,
+ nqpqx2, nqpqz2,
+ nqx, nqz,
+ nqpqx, nqpqz,
+ q);
+ swap_conditional(nqx2, nqpqx2, bit);
+ swap_conditional(nqz2, nqpqz2, bit);
+
+ t = nqx;
+ nqx = nqx2;
+ nqx2 = t;
+ t = nqz;
+ nqz = nqz2;
+ nqz2 = t;
+ t = nqpqx;
+ nqpqx = nqpqx2;
+ nqpqx2 = t;
+ t = nqpqz;
+ nqpqz = nqpqz2;
+ nqpqz2 = t;
+
+ byte <<= 1;
+ }
+ }
+
+ memcpy(resultx, nqx, sizeof(limb) * 10);
+ memcpy(resultz, nqz, sizeof(limb) * 10);
+}
+
+static void crecip(limb *out, const limb *z)
+{
+ limb z2[10];
+ limb z9[10];
+ limb z11[10];
+ limb z2_5_0[10];
+ limb z2_10_0[10];
+ limb z2_20_0[10];
+ limb z2_50_0[10];
+ limb z2_100_0[10];
+ limb t0[10];
+ limb t1[10];
+ int i;
+
+ /* 2 */ fsquare(z2,z);
+ /* 4 */ fsquare(t1,z2);
+ /* 8 */ fsquare(t0,t1);
+ /* 9 */ fmul(z9,t0,z);
+ /* 11 */ fmul(z11,z9,z2);
+ /* 22 */ fsquare(t0,z11);
+ /* 2^5 - 2^0 = 31 */ fmul(z2_5_0,t0,z9);
+
+ /* 2^6 - 2^1 */ fsquare(t0,z2_5_0);
+ /* 2^7 - 2^2 */ fsquare(t1,t0);
+ /* 2^8 - 2^3 */ fsquare(t0,t1);
+ /* 2^9 - 2^4 */ fsquare(t1,t0);
+ /* 2^10 - 2^5 */ fsquare(t0,t1);
+ /* 2^10 - 2^0 */ fmul(z2_10_0,t0,z2_5_0);
+
+ /* 2^11 - 2^1 */ fsquare(t0,z2_10_0);
+ /* 2^12 - 2^2 */ fsquare(t1,t0);
+ /* 2^20 - 2^10 */ for (i = 2; i < 10; i += 2) { fsquare(t0,t1); fsquare(t1,t0); }
+ /* 2^20 - 2^0 */ fmul(z2_20_0,t1,z2_10_0);
+
+ /* 2^21 - 2^1 */ fsquare(t0,z2_20_0);
+ /* 2^22 - 2^2 */ fsquare(t1,t0);
+ /* 2^40 - 2^20 */ for (i = 2; i < 20; i += 2) { fsquare(t0,t1); fsquare(t1,t0); }
+ /* 2^40 - 2^0 */ fmul(t0,t1,z2_20_0);
+
+ /* 2^41 - 2^1 */ fsquare(t1,t0);
+ /* 2^42 - 2^2 */ fsquare(t0,t1);
+ /* 2^50 - 2^10 */ for (i = 2; i < 10; i += 2) { fsquare(t1,t0); fsquare(t0,t1); }
+ /* 2^50 - 2^0 */ fmul(z2_50_0,t0,z2_10_0);
+
+ /* 2^51 - 2^1 */ fsquare(t0,z2_50_0);
+ /* 2^52 - 2^2 */ fsquare(t1,t0);
+ /* 2^100 - 2^50 */ for (i = 2; i < 50; i += 2) { fsquare(t0,t1); fsquare(t1,t0); }
+ /* 2^100 - 2^0 */ fmul(z2_100_0,t1,z2_50_0);
+
+ /* 2^101 - 2^1 */ fsquare(t1,z2_100_0);
+ /* 2^102 - 2^2 */ fsquare(t0,t1);
+ /* 2^200 - 2^100 */ for (i = 2; i < 100; i += 2) { fsquare(t1,t0); fsquare(t0,t1); }
+ /* 2^200 - 2^0 */ fmul(t1,t0,z2_100_0);
+
+ /* 2^201 - 2^1 */ fsquare(t0,t1);
+ /* 2^202 - 2^2 */ fsquare(t1,t0);
+ /* 2^250 - 2^50 */ for (i = 2; i < 50; i += 2) { fsquare(t0,t1); fsquare(t1,t0); }
+ /* 2^250 - 2^0 */ fmul(t0,t1,z2_50_0);
+
+ /* 2^251 - 2^1 */ fsquare(t1,t0);
+ /* 2^252 - 2^2 */ fsquare(t0,t1);
+ /* 2^253 - 2^3 */ fsquare(t1,t0);
+ /* 2^254 - 2^4 */ fsquare(t0,t1);
+ /* 2^255 - 2^5 */ fsquare(t1,t0);
+ /* 2^255 - 21 */ fmul(out,t1,z11);
+}
+
+void curve25519(uint8_t mypublic[CURVE25519_POINT_SIZE], const uint8_t secret[CURVE25519_POINT_SIZE], const uint8_t basepoint[CURVE25519_POINT_SIZE])
+{
+ limb bp[10], x[10], z[11], zmone[10];
+ uint8_t e[32];
+
+ memcpy(e, secret, 32);
+ curve25519_normalize_secret(e);
+
+ fexpand(bp, basepoint);
+ cmult(x, z, e, bp);
+ crecip(zmone, z);
+ fmul(z, x, zmone);
+ fcontract(mypublic, z);
+}
+#endif
+
+void curve25519_generate_public(uint8_t *pub, const uint8_t *secret)
+{
+ static const uint8_t basepoint[CURVE25519_POINT_SIZE] = { 9 };
+ curve25519(pub, secret, basepoint);
+}
diff --git a/src/tools/curve25519.h b/src/tools/curve25519.h
new file mode 100644
index 0000000..3c1404a
--- /dev/null
+++ b/src/tools/curve25519.h
@@ -0,0 +1,22 @@
+/* Copyright 2015-2016 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_POINT_SIZE = 32,
+};
+
+void curve25519(uint8_t *mypublic, const uint8_t *secret, const uint8_t *basepoint);
+void curve25519_generate_public(uint8_t *pub, const uint8_t *secret);
+static inline void curve25519_normalize_secret(uint8_t secret[CURVE25519_POINT_SIZE])
+{
+ secret[0] &= 248;
+ secret[31] &= 127;
+ secret[31] |= 64;
+}
+
+#endif
diff --git a/src/tools/genkey.c b/src/tools/genkey.c
new file mode 100644
index 0000000..1602ae1
--- /dev/null
+++ b/src/tools/genkey.c
@@ -0,0 +1,59 @@
+/* Copyright 2015-2016 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. */
+
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <syscall.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "curve25519.h"
+#include "base64.h"
+
+#ifdef __NR_getrandom
+static inline ssize_t get_random_bytes(uint8_t *out, size_t len)
+{
+ return syscall(__NR_getrandom, out, len, 0);
+}
+#else
+#include <fcntl.h>
+static inline ssize_t get_random_bytes(uint8_t *out, size_t len)
+{
+ ssize_t ret;
+ int fd = open("/dev/urandom", O_RDONLY);
+ if (fd < 0)
+ return fd;
+ ret = read(fd, out, len);
+ close(fd);
+ return ret;
+}
+#endif
+
+int genkey_main(int argc, char *argv[])
+{
+ unsigned char private_key[CURVE25519_POINT_SIZE];
+ char private_key_base64[b64_len(CURVE25519_POINT_SIZE)];
+ struct stat stat;
+
+ 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(private_key, CURVE25519_POINT_SIZE) != CURVE25519_POINT_SIZE) {
+ perror("getrandom");
+ return 1;
+ }
+ if (argc && !strcmp(argv[0], "genkey"))
+ curve25519_normalize_secret(private_key);
+
+ if (b64_ntop(private_key, sizeof(private_key), private_key_base64, sizeof(private_key_base64)) < 0) {
+ errno = EINVAL;
+ perror("b64");
+ return 1;
+ }
+
+ puts(private_key_base64);
+ return 0;
+
+}
diff --git a/src/tools/kernel.c b/src/tools/kernel.c
new file mode 100644
index 0000000..0448308
--- /dev/null
+++ b/src/tools/kernel.c
@@ -0,0 +1,242 @@
+/* Copyright 2015-2016 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. */
+
+#include <errno.h>
+#include <libmnl/libmnl.h>
+#include <linux/if_link.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <netinet/in.h>
+#include <net/if.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <time.h>
+
+#include "kernel.h"
+#include "../uapi.h"
+
+struct inflatable_buffer {
+ char *buffer;
+ char *next;
+ bool good;
+ size_t len;
+ size_t pos;
+};
+
+#define max(a, b) (a > b ? a : b)
+
+static int add_next_to_inflatable_buffer(struct inflatable_buffer *buffer)
+{
+ size_t len, expand_to;
+ char *new_buffer;
+
+ if (!buffer->good || !buffer->next) {
+ free(buffer->next);
+ return 0;
+ }
+
+ len = strlen(buffer->next) + 1;
+
+ if (len == 1)
+ return 0;
+
+ if (buffer->len - buffer->pos <= len) {
+ expand_to = max(buffer->len * 2, buffer->len + len + 1);
+ new_buffer = realloc(buffer->buffer, expand_to);
+ if (!new_buffer) {
+ free(buffer->next);
+ return -errno;
+ }
+ memset(&new_buffer[buffer->len], 0, expand_to - buffer->len);
+ buffer->buffer = new_buffer;
+ buffer->len = expand_to;
+ }
+ memcpy(&buffer->buffer[buffer->pos], buffer->next, len);
+ free(buffer->next);
+ buffer->pos += len;
+ return 0;
+}
+
+static int parse_linkinfo(const struct nlattr *attr, void *data)
+{
+ struct inflatable_buffer *buffer = data;
+ if (mnl_attr_get_type(attr) == IFLA_INFO_KIND && !strcmp("wireguard", mnl_attr_get_str(attr)))
+ buffer->good = true;
+ return MNL_CB_OK;
+}
+
+static int parse_infomsg(const struct nlattr *attr, void *data)
+{
+ struct inflatable_buffer *buffer = 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)
+ buffer->next = strdup(mnl_attr_get_str(attr));
+ return MNL_CB_OK;
+}
+
+static int read_devices_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct inflatable_buffer *buffer = data;
+ buffer->good = false;
+ buffer->next = NULL;
+ int ret = mnl_attr_parse(nlh, sizeof(struct ifinfomsg), parse_infomsg, data);
+ if (ret != MNL_CB_OK)
+ return ret;
+ ret = add_next_to_inflatable_buffer(buffer);
+ if (ret < 0)
+ return ret;
+ if (nlh->nlmsg_type != NLMSG_DONE)
+ return MNL_CB_OK + 1;
+ return MNL_CB_OK;
+}
+
+/* first\0second\0third\0forth\0last\0\0 */
+char *kernel_get_wireguard_interfaces(void)
+{
+ struct mnl_socket *nl = NULL;
+ char *rtnl_buffer = NULL;
+ size_t message_len;
+ unsigned int portid, seq;
+ ssize_t len;
+ int ret = 0;
+ struct inflatable_buffer buffer = { 0 };
+ struct nlmsghdr *nlh;
+ struct ifinfomsg *ifm;
+
+ buffer.len = 4096;
+ buffer.buffer = calloc(buffer.len, 1);
+ if (!buffer.buffer) {
+ ret = -errno;
+ goto cleanup;
+ }
+
+ rtnl_buffer = calloc(4096, 1);
+ if (!rtnl_buffer) {
+ ret = -errno;
+ 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, 4096)) < 0) {
+ ret = -errno;
+ goto cleanup;
+ }
+ if ((len = mnl_cb_run(rtnl_buffer, len, seq, portid, read_devices_cb, &buffer)) < 0) {
+ ret = -errno;
+ goto cleanup;
+ }
+ if (len == MNL_CB_OK + 1)
+ goto another;
+
+cleanup:
+ free(rtnl_buffer);
+ if (nl)
+ mnl_socket_close(nl);
+ errno = -ret;
+ if (errno) {
+ perror("Error when trying to get a list of Wireguard interfaces");
+ free(buffer.buffer);
+ return NULL;
+ }
+ return buffer.buffer;
+}
+
+bool kernel_has_wireguard_interface(const char *interface)
+{
+ char *interfaces, *this_interface;
+ this_interface = interfaces = kernel_get_wireguard_interfaces();
+ if (!interfaces)
+ return false;
+ for (size_t len = 0; (len = strlen(this_interface)); this_interface += len + 1) {
+ if (!strcmp(interface, this_interface)) {
+ free(interfaces);
+ return true;
+ }
+ }
+ free(interfaces);
+ return false;
+}
+
+static int do_ioctl(int req, struct ifreq *ifreq)
+{
+ static int fd = -1;
+ if (fd < 0) {
+ fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fd < 0)
+ return fd;
+ }
+ return ioctl(fd, req, ifreq);
+}
+
+int kernel_set_device(struct wgdevice *dev)
+{
+ struct ifreq ifreq = { .ifr_data = (char *)dev };
+ memcpy(&ifreq.ifr_name, dev->interface, IFNAMSIZ);
+ ifreq.ifr_name[IFNAMSIZ - 1] = 0;
+ return do_ioctl(WG_SET_DEVICE, &ifreq);
+}
+
+int kernel_get_device(struct wgdevice **dev, const char *interface)
+{
+ int ret;
+ struct ifreq ifreq = { 0 };
+ memcpy(&ifreq.ifr_name, interface, IFNAMSIZ);
+ ifreq.ifr_name[IFNAMSIZ - 1] = 0;
+ *dev = NULL;
+ do {
+ free(*dev);
+ ret = do_ioctl(WG_GET_DEVICE, &ifreq);
+ if (ret < 0)
+ goto out;
+ *dev = calloc(ret + sizeof(struct wgdevice), 1);
+ if (!*dev) {
+ perror("calloc");
+ ret = -ENOMEM;
+ goto out;
+ }
+ (*dev)->peers_size = ret;
+ ifreq.ifr_data = (char *)*dev;
+ memcpy(&ifreq.ifr_name, interface, IFNAMSIZ);
+ ifreq.ifr_name[IFNAMSIZ - 1] = 0;
+ ret = do_ioctl(WG_GET_DEVICE, &ifreq);
+ } while (ret == -EMSGSIZE);
+ if (ret < 0) {
+ free(*dev);
+ *dev = NULL;
+ }
+out:
+ errno = -ret;
+ return ret;
+}
diff --git a/src/tools/kernel.h b/src/tools/kernel.h
new file mode 100644
index 0000000..0525ce1
--- /dev/null
+++ b/src/tools/kernel.h
@@ -0,0 +1,24 @@
+/* Copyright 2015-2016 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. */
+
+#ifndef KERNEL_H
+#define KERNEL_H
+
+#include <stdbool.h>
+
+struct wgdevice;
+
+int kernel_set_device(struct wgdevice *dev);
+int kernel_get_device(struct wgdevice **dev, const char *interface);
+char *kernel_get_wireguard_interfaces(void);
+bool kernel_has_wireguard_interface(const char *interface);
+
+
+#define for_each_wgpeer(__dev, __peer, __i) for ((__i) = 0, (__peer) = (typeof(__peer))((uint8_t *)(__dev) + sizeof(struct wgdevice)); \
+ (__i) < (__dev)->num_peers; \
+ ++(__i), (__peer) = (typeof(__peer))((uint8_t *)(__peer) + sizeof(struct wgpeer) + (sizeof(struct wgipmask) * (__peer)->num_ipmasks)))
+
+#define for_each_wgipmask(__peer, __ipmask, __i) for ((__i) = 0, (__ipmask) = (typeof(__ipmask))((uint8_t *)(__peer) + sizeof(struct wgpeer)); \
+ (__i) < (__peer)->num_ipmasks; \
+ ++(__i), (__ipmask) = (typeof(__ipmask))((uint8_t *)(__ipmask) + sizeof(struct wgipmask)))
+
+#endif
diff --git a/src/tools/pubkey.c b/src/tools/pubkey.c
new file mode 100644
index 0000000..d9a97d9
--- /dev/null
+++ b/src/tools/pubkey.c
@@ -0,0 +1,33 @@
+/* Copyright 2015-2016 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. */
+
+#include <errno.h>
+#include <resolv.h>
+#include <stdio.h>
+
+#include "curve25519.h"
+#include "base64.h"
+
+int pubkey_main(__attribute__((unused)) int argc, __attribute__((unused)) char *argv[])
+{
+ unsigned char private_key[CURVE25519_POINT_SIZE + 1] = { 0 }, public_key[CURVE25519_POINT_SIZE] = { 0 };
+ char private_key_base64[b64_len(CURVE25519_POINT_SIZE)] = { 0 }, public_key_base64[b64_len(CURVE25519_POINT_SIZE)] = { 0 };
+
+ if (fread(private_key_base64, 1, sizeof(private_key_base64) - 1, stdin) != sizeof(private_key_base64) - 1) {
+ errno = EINVAL;
+ perror("fread(private key)");
+ return 1;
+ }
+ if (b64_pton(private_key_base64, private_key, sizeof(private_key)) < 0) {
+ errno = EINVAL;
+ perror("b64");
+ return 1;
+ }
+ curve25519_generate_public(public_key, private_key);
+ if (b64_ntop(public_key, sizeof(public_key), public_key_base64, sizeof(public_key_base64)) < 0) {
+ errno = EINVAL;
+ perror("b64");
+ return 1;
+ }
+ puts(public_key_base64);
+ return 0;
+}
diff --git a/src/tools/set.c b/src/tools/set.c
new file mode 100644
index 0000000..f85162d
--- /dev/null
+++ b/src/tools/set.c
@@ -0,0 +1,35 @@
+/* Copyright 2015-2016 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "subcommands.h"
+#include "config.h"
+#include "kernel.h"
+
+int set_main(int argc, char *argv[])
+{
+ struct wgdevice *device = NULL;
+ int ret = 1;
+
+ if (argc < 3) {
+ fprintf(stderr, "Usage: %s %s <interface> [listen-port <port>] [private-key <file path>] [peer <base64 public key> [remove] [endpoint <ip>:<port>] [allowed-ips <ip1>/<cidr1>[,<ip2>/<cidr2>]...] ]...\n", PROG_NAME, argv[0]);
+ return 1;
+ }
+
+ if (!config_read_cmd(&device, argv + 2, argc - 2))
+ goto cleanup;
+ strncpy(device->interface, argv[1], IFNAMSIZ - 1);
+ device->interface[IFNAMSIZ - 1] = 0;
+
+ if (kernel_set_device(device) != 0) {
+ perror("Unable to set device");
+ goto cleanup;
+ }
+
+ ret = 0;
+
+cleanup:
+ free(device);
+ return ret;
+}
diff --git a/src/tools/setconf.c b/src/tools/setconf.c
new file mode 100644
index 0000000..81faa64
--- /dev/null
+++ b/src/tools/setconf.c
@@ -0,0 +1,61 @@
+/* Copyright 2015-2016 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. */
+
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "config.h"
+#include "kernel.h"
+#include "subcommands.h"
+
+int setconf_main(int argc, 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, &device, !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;
+ }
+ }
+ if (!config_read_finish(&ctx) || !device) {
+ fprintf(stderr, "Invalid configuration\n");
+ goto cleanup;
+ }
+ strncpy(device->interface, argv[1], IFNAMSIZ - 1);
+ device->interface[IFNAMSIZ - 1] = 0;
+
+ if (kernel_set_device(device) != 0) {
+ perror("Unable to set device");
+ goto cleanup;
+ }
+
+ ret = 0;
+
+cleanup:
+ if (config_input)
+ fclose(config_input);
+ free(config_buffer);
+ free(device);
+ return ret;
+}
diff --git a/src/tools/show.c b/src/tools/show.c
new file mode 100644
index 0000000..1662751
--- /dev/null
+++ b/src/tools/show.c
@@ -0,0 +1,366 @@
+/* Copyright 2015-2016 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. */
+
+#include <arpa/inet.h>
+#include <inttypes.h>
+#include <netinet/in.h>
+#include <net/if.h>
+#include <resolv.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <netdb.h>
+
+#include "kernel.h"
+#include "subcommands.h"
+#include "terminal.h"
+#include "base64.h"
+#include "../uapi.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_usec && (b->last_handshake_time.tv_sec || b->last_handshake_time.tv_usec))
+ return 1;
+ if (!b->last_handshake_time.tv_sec && !b->last_handshake_time.tv_usec && (a->last_handshake_time.tv_sec || a->last_handshake_time.tv_usec))
+ return -1;
+ diff = a->last_handshake_time.tv_sec - b->last_handshake_time.tv_sec;
+ if (!diff)
+ diff = a->last_handshake_time.tv_usec - b->last_handshake_time.tv_usec;
+ if (diff < 0)
+ return 1;
+ if (diff > 0)
+ return -1;
+ return 0;
+}
+
+static void sort_peers(struct wgdevice *device)
+{
+ uint8_t *new_device, *pos;
+ struct wgpeer **peers;
+ struct wgpeer *peer;
+ size_t i, len;
+
+ peers = calloc(device->num_peers, sizeof(struct wgpeer *));
+ if (!peers)
+ return;
+
+ len = sizeof(struct wgdevice);
+ for_each_wgpeer(device, peer, i)
+ len += sizeof(struct wgpeer) + (peer->num_ipmasks * sizeof(struct wgipmask));
+ pos = new_device = malloc(len);
+ if (!new_device) {
+ free(peers);
+ return;
+ }
+
+ memcpy(pos, device, sizeof(struct wgdevice));
+ pos += sizeof(struct wgdevice);
+
+ for_each_wgpeer(device, peer, i)
+ peers[i] = peer;
+
+ qsort(peers, device->num_peers, sizeof(struct wgpeer *), peer_cmp);
+ for (i = 0; i < device->num_peers; ++i) {
+ len = sizeof(struct wgpeer) + (peers[i]->num_ipmasks * sizeof(struct wgipmask));
+ memcpy(pos, peers[i], len);
+ pos += len;
+ }
+ free(peers);
+
+ memcpy(device, new_device, pos - new_device);
+ free(new_device);
+}
+
+static const uint8_t zero[WG_KEY_LEN] = { 0 };
+
+static char *key(const unsigned char key[WG_KEY_LEN])
+{
+ static char b64[b64_len(WG_KEY_LEN)];
+ if (!memcmp(key, zero, WG_KEY_LEN))
+ return "(none)";
+ memset(b64, 0, b64_len(WG_KEY_LEN));
+ b64_ntop(key, WG_KEY_LEN, b64, b64_len(WG_KEY_LEN));
+ return b64;
+}
+
+static char *ip(const struct wgipmask *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_storage *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->ss_family == AF_INET)
+ addr_len = sizeof(struct sockaddr_in);
+ else if (addr->ss_family == AF_INET6)
+ addr_len = sizeof(struct sockaddr_in6);
+
+ ret = getnameinfo((struct sockaddr *)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);
+ else
+ snprintf(buf, sizeof(buf) - 1, (addr->ss_family == AF_INET6 && strchr(host, ':')) ? "[%s]:%s" : "%s:%s", host, service);
+ return buf;
+}
+
+static char *ago(const struct timeval *t)
+{
+ static char buf[1024];
+ unsigned long long left, years, days, hours, minutes, seconds;
+ size_t offset = 0;
+
+ left = time(NULL) - t->tv_sec;
+ 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, sizeof(buf) - offset, "%s%llu " TERMINAL_FG_CYAN "year%s" TERMINAL_RESET, offset ? ", " : "", years, years == 1 ? "" : "s");
+ if (days)
+ offset += snprintf(buf + offset, sizeof(buf) - offset, "%s%llu " TERMINAL_FG_CYAN "day%s" TERMINAL_RESET, offset ? ", " : "", days, days == 1 ? "" : "s");
+ if (hours)
+ offset += snprintf(buf + offset, sizeof(buf) - offset, "%s%llu " TERMINAL_FG_CYAN "hour%s" TERMINAL_RESET, offset ? ", " : "", hours, hours == 1 ? "" : "s");
+ if (minutes)
+ offset += snprintf(buf + offset, sizeof(buf) - offset, "%s%llu " TERMINAL_FG_CYAN "minute%s" TERMINAL_RESET, offset ? ", " : "", minutes, minutes == 1 ? "" : "s");
+ if (seconds)
+ offset += snprintf(buf + offset, sizeof(buf) - offset, "%s%llu " TERMINAL_FG_CYAN "second%s" TERMINAL_RESET, offset ? ", " : "", seconds, seconds == 1 ? "" : "s");
+ if (offset)
+ snprintf(buf + offset, sizeof(buf) - offset, " ago");
+ else
+ snprintf(buf, sizeof(buf), "Now");
+
+ 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)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 = NULL;
+static void show_usage(void)
+{
+ fprintf(stderr, "Usage: %s %s { <interface> | all | interfaces } [public-key | private-key | preshared-key | listen-port | peers | endpoints | allowed-ips | latest-handshake | bandwidth]\n", PROG_NAME, COMMAND_NAME);
+}
+
+static void pretty_print(struct wgdevice *device)
+{
+ size_t i, j;
+ struct wgpeer *peer;
+ struct wgipmask *ipmask;
+
+ terminal_printf(TERMINAL_RESET);
+ terminal_printf(TERMINAL_FG_GREEN TERMINAL_BOLD "interface" TERMINAL_RESET ": " TERMINAL_FG_GREEN "%s" TERMINAL_RESET "\n", device->interface);
+ if (memcmp(device->public_key, zero, WG_KEY_LEN))
+ terminal_printf(" " TERMINAL_BOLD "public key" TERMINAL_RESET ": %s\n", key(device->public_key));
+ if (memcmp(device->private_key, zero, WG_KEY_LEN))
+ terminal_printf(" " TERMINAL_BOLD "private key" TERMINAL_RESET ": %s\n", key(device->private_key));
+ if (memcmp(device->preshared_key, zero, WG_KEY_LEN))
+ terminal_printf(" " TERMINAL_BOLD "pre-shared key" TERMINAL_RESET ": %s\n", key(device->preshared_key));
+ if (device->port)
+ terminal_printf(" " TERMINAL_BOLD "listening port" TERMINAL_RESET ": %u\n", device->port);
+ if (device->num_peers) {
+ sort_peers(device);
+ terminal_printf("\n");
+ }
+ for_each_wgpeer(device, peer, i) {
+ terminal_printf(TERMINAL_FG_YELLOW TERMINAL_BOLD "peer" TERMINAL_RESET ": " TERMINAL_FG_YELLOW "%s" TERMINAL_RESET "\n", key(peer->public_key));
+ if (peer->endpoint.ss_family == AF_INET || peer->endpoint.ss_family == AF_INET6)
+ terminal_printf(" " TERMINAL_BOLD "endpoint" TERMINAL_RESET ": %s\n", endpoint(&peer->endpoint));
+ terminal_printf(" " TERMINAL_BOLD "allowed ips" TERMINAL_RESET ": ");
+ if (peer->num_ipmasks) {
+ for_each_wgipmask(peer, ipmask, j)
+ terminal_printf("%s" TERMINAL_FG_CYAN "/" TERMINAL_RESET "%u%s", ip(ipmask), ipmask->cidr, j == (size_t)peer->num_ipmasks - 1 ? "\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 "bandwidth" TERMINAL_RESET ": ");
+ terminal_printf("%s received, ", bytes(peer->rx_bytes));
+ terminal_printf("%s sent\n", bytes(peer->tx_bytes));
+ }
+ if (i + 1 < device->num_peers)
+ terminal_printf("\n");
+ }
+}
+
+static bool ugly_print(struct wgdevice *device, const char *param, bool with_interface)
+{
+ size_t i, j;
+ struct wgpeer *peer;
+ struct wgipmask *ipmask;
+ if (!strcmp(param, "public-key")) {
+ if (with_interface)
+ printf("%s\t", device->interface);
+ printf("%s\n", key(device->public_key));
+ } else if (!strcmp(param, "private-key")) {
+ if (with_interface)
+ printf("%s\t", device->interface);
+ printf("%s\n", key(device->private_key));
+ } else if (!strcmp(param, "preshared-key")) {
+ if (with_interface)
+ printf("%s\t", device->interface);
+ printf("%s\n", key(device->preshared_key));
+ } else if (!strcmp(param, "listen-port")) {
+ if (with_interface)
+ printf("%s\t", device->interface);
+ printf("%u\n", device->port);
+ } else if (!strcmp(param, "endpoints")) {
+ if (with_interface)
+ printf("%s\t", device->interface);
+ for_each_wgpeer(device, peer, i) {
+ printf("%s\t", key(peer->public_key));
+ if (peer->endpoint.ss_family == AF_INET || peer->endpoint.ss_family == AF_INET6)
+ printf("%s\n", endpoint(&peer->endpoint));
+ else
+ printf("(none)\n");
+ }
+ } else if (!strcmp(param, "allowed-ips")) {
+ for_each_wgpeer(device, peer, i) {
+ if (with_interface)
+ printf("%s\t", device->interface);
+ printf("%s\t", key(peer->public_key));
+ if (peer->num_ipmasks) {
+ for_each_wgipmask(peer, ipmask, j)
+ printf("%s/%u%s", ip(ipmask), ipmask->cidr, j == (size_t)peer->num_ipmasks - 1 ? "\n" : ", ");
+ } else
+ printf("(none)\n");
+ }
+ } else if (!strcmp(param, "latest-handshakes")) {
+ for_each_wgpeer(device, peer, i) {
+ if (with_interface)
+ printf("%s\t", device->interface);
+ printf("%s\t%llu\n", key(peer->public_key), (unsigned long long)peer->last_handshake_time.tv_sec);
+ }
+ } else if (!strcmp(param, "bandwidth")) {
+ for_each_wgpeer(device, peer, i) {
+ if (with_interface)
+ printf("%s\t", device->interface);
+ 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, "peers")) {
+ for_each_wgpeer(device, peer, i) {
+ if (with_interface)
+ printf("%s\t", device->interface);
+ printf("%s\n", key(peer->public_key));
+ }
+ } else {
+ fprintf(stderr, "Invalid parameter: `%s`\n", param);
+ show_usage();
+ return false;
+ }
+ return true;
+}
+
+int show_main(int argc, 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 = kernel_get_wireguard_interfaces(), *interface;
+ if (!interfaces) {
+ perror("Unable to get devices");
+ return 1;
+ }
+ interface = interfaces;
+ for (size_t len = 0; (len = strlen(interface)); interface += len + 1) {
+ struct wgdevice *device = NULL;
+ if (kernel_get_device(&device, interface) < 0) {
+ perror("Unable to get device");
+ continue;
+ }
+ if (argc == 3) {
+ if (!ugly_print(device, argv[2], true)) {
+ ret = 1;
+ free(device);
+ break;
+ }
+ } else {
+ pretty_print(device);
+ if (strlen(interface + len + 1))
+ printf("\n");
+ }
+ free(device);
+ }
+ free(interfaces);
+ } else if (!strcmp(argv[1], "interfaces")) {
+ char *interfaces, *interface;
+ if (argc > 2) {
+ show_usage();
+ return 1;
+ }
+ interfaces = kernel_get_wireguard_interfaces();
+ if (!interfaces) {
+ perror("Unable to get devices");
+ 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 (!kernel_has_wireguard_interface(argv[1])) {
+ fprintf(stderr, "`%s` is not a valid WireGuard interface\n", argv[1]);
+ show_usage();
+ return 1;
+ }
+ if (kernel_get_device(&device, argv[1]) < 0) {
+ perror("Unable to get device");
+ show_usage();
+ return 1;
+ }
+ if (argc == 3) {
+ if (!ugly_print(device, argv[2], false))
+ ret = 1;
+ } else
+ pretty_print(device);
+ free(device);
+ }
+ return ret;
+}
diff --git a/src/tools/showconf.c b/src/tools/showconf.c
new file mode 100644
index 0000000..faf2482
--- /dev/null
+++ b/src/tools/showconf.c
@@ -0,0 +1,102 @@
+/* Copyright 2015-2016 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. */
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <net/if.h>
+#include <resolv.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <netdb.h>
+
+#include "subcommands.h"
+#include "base64.h"
+#include "kernel.h"
+#include "../uapi.h"
+
+int showconf_main(int argc, char *argv[])
+{
+ static const uint8_t zero[WG_KEY_LEN] = { 0 };
+ char b64[b64_len(WG_KEY_LEN)] = { 0 };
+ char ip[INET6_ADDRSTRLEN];
+ struct wgdevice *device = NULL;
+ struct wgpeer *peer;
+ struct wgipmask *ipmask;
+ size_t i, j;
+ int ret = 1;
+
+ if (argc != 2) {
+ fprintf(stderr, "Usage: %s %s <interface>\n", PROG_NAME, argv[0]);
+ return 1;
+ }
+
+ if (!kernel_has_wireguard_interface(argv[1])) {
+ fprintf(stderr, "`%s` is not a valid WireGuard interface\n", argv[1]);
+ fprintf(stderr, "Usage: %s %s <interface>\n", PROG_NAME, argv[0]);
+ return 1;
+ }
+
+ if (kernel_get_device(&device, argv[1])) {
+ perror("Unable to get device");
+ goto cleanup;
+ }
+
+ printf("[Interface]\n");
+ if (device->port)
+ printf("ListenPort = %d\n", device->port);
+ if (memcmp(device->private_key, zero, WG_KEY_LEN)) {
+ b64_ntop(device->private_key, WG_KEY_LEN, b64, b64_len(WG_KEY_LEN));
+ printf("PrivateKey = %s\n", b64);
+ }
+ if (memcmp(device->preshared_key, zero, WG_KEY_LEN)) {
+ b64_ntop(device->preshared_key, WG_KEY_LEN, b64, b64_len(WG_KEY_LEN));
+ printf("PresharedKey = %s\n", b64);
+ }
+ printf("\n");
+ for_each_wgpeer(device, peer, i) {
+ b64_ntop(peer->public_key, WG_KEY_LEN, b64, b64_len(WG_KEY_LEN));
+ printf("[Peer]\nPublicKey = %s\n", b64);
+ if (peer->num_ipmasks)
+ printf("AllowedIPs = ");
+ for_each_wgipmask(peer, ipmask, j) {
+ if (ipmask->family == AF_INET) {
+ if (!inet_ntop(AF_INET, &ipmask->ip4, ip, INET6_ADDRSTRLEN))
+ continue;
+ } else if (ipmask->family == AF_INET6) {
+ if (!inet_ntop(AF_INET6, &ipmask->ip6, ip, INET6_ADDRSTRLEN))
+ continue;
+ } else
+ continue;
+ printf("%s/%d", ip, ipmask->cidr);
+ if (j + 1 < (size_t)peer->num_ipmasks)
+ printf(", ");
+ }
+ if (peer->num_ipmasks)
+ printf("\n");
+
+ if (peer->endpoint.ss_family == AF_INET || peer->endpoint.ss_family == AF_INET6) {
+ char host[4096 + 1];
+ char service[512 + 1];
+ static char buf[sizeof(host) + sizeof(service) + 4];
+ socklen_t addr_len = 0;
+ memset(buf, 0, sizeof(buf));
+ if (peer->endpoint.ss_family == AF_INET)
+ addr_len = sizeof(struct sockaddr_in);
+ else if (peer->endpoint.ss_family == AF_INET6)
+ addr_len = sizeof(struct sockaddr_in6);
+ if (!getnameinfo((struct sockaddr *)&peer->endpoint, addr_len, host, sizeof(host), service, sizeof(service), NI_DGRAM | NI_NUMERICSERV | NI_NUMERICHOST)) {
+ snprintf(buf, sizeof(buf) - 1, (peer->endpoint.ss_family == AF_INET6 && strchr(host, ':')) ? "[%s]:%s" : "%s:%s", host, service);
+ printf("Endpoint = %s\n", buf);
+ }
+ }
+
+ if (i + 1 < device->num_peers)
+ printf("\n");
+ }
+ ret = 0;
+
+cleanup:
+ free(device);
+ return ret;
+}
diff --git a/src/tools/subcommands.h b/src/tools/subcommands.h
new file mode 100644
index 0000000..8351f8f
--- /dev/null
+++ b/src/tools/subcommands.h
@@ -0,0 +1,14 @@
+/* Copyright 2015-2016 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, char *argv[]);
+int showconf_main(int argc, char *argv[]);
+int set_main(int argc, char *argv[]);
+int setconf_main(int argc, char *argv[]);
+int genkey_main(int argc, char *argv[]);
+int pubkey_main(int argc, char *argv[]);
+
+#endif
diff --git a/src/tools/terminal.c b/src/tools/terminal.c
new file mode 100644
index 0000000..74d04c2
--- /dev/null
+++ b/src/tools/terminal.c
@@ -0,0 +1,79 @@
+/* Copyright 2015-2016 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. */
+
+#define _GNU_SOURCE
+#include <ctype.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include <unistd.h>
+
+static bool color_mode(FILE *file)
+{
+ static int mode = -1;
+ 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
+ return isatty(fileno(file));
+ return mode;
+}
+
+static void filter_ansi(FILE *file, const char *fmt, va_list args)
+{
+ char *str = NULL;
+ size_t len, i, j;
+
+ if (color_mode(file)) {
+ vfprintf(file, 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 (isalpha(str[j]))
+ break;
+ str[j] = '\0';
+ }
+ str[j] = '\0';
+ }
+ }
+ }
+ for (i = 0; i < len; i = j) {
+ fputs(&str[i], file);
+ 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(stdout, fmt, args);
+ va_end(args);
+}
+
+void terminal_fprintf(FILE *file, const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ filter_ansi(file, fmt, args);
+ va_end(args);
+}
diff --git a/src/tools/terminal.h b/src/tools/terminal.h
new file mode 100644
index 0000000..825c057
--- /dev/null
+++ b/src/tools/terminal.h
@@ -0,0 +1,49 @@
+/* Copyright 2015-2016 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)));
+void terminal_fprintf(FILE *file, const char *fmt, ...) __attribute__((format(printf, 2, 3)));
+
+#endif
diff --git a/src/tools/wg.8 b/src/tools/wg.8
new file mode 100644
index 0000000..4ee5027
--- /dev/null
+++ b/src/tools/wg.8
@@ -0,0 +1,194 @@
+.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 | \fIpreshared-key\fP | \fIlisten-port\fP | \fIpeers\fP | \fIendpoints\fP | \fIallowed-ips\fP | \fIlatest-handshake\fP | \fIbandwidth\fP]
+Shows current WireGuard configuration 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 quit. 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.
+.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] [\fIprivate-key\fP \fI<file-path>\fP] [\fIpreshared-key\fP \fI<file-path>\fP] [\fIpeer\fP \fI<base64-public-key>\fP [\fIremove\fP] [\fIendpoint\fP \fI<ip>:<port>\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, the port will be automatically generated when the
+interface comes up. Both \fIprivate-key\fP and \fIpreshared-key\fP must
+be a files, for security reasons, but if you're 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 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.
+.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
+\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
+Show 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 contains two fields:
+.IP \(bu
+PrivateKey \(em a base64 private key generated by \fIwg genkey\fP. 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
+ListenPort \(em a 16-bit port for listening. Optional; if not specified,
+automatically generated based on interface name.
+.P
+The \fIPeer\fP sections contain three fields each:
+.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
+AllowedIPs \(em a comma-separated list of IP (v4 or v6) addresses with
+CIDR masks. 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. Required.
+.IP \(bu
+Endpoint \(em an endpoint IP or hostname, followed by a comma, and then a
+port number. Optional.
+
+.SH CONFIGURATION FILE FORMAT EXAMPLE
+This example may be used as a model for writing configuration files.
+Note that not all keys are required.
+
+ [Interface]
+.br
+ PrivateKey = yAnz5TF+lXXJte14tji3zlMNq+hd2rYUIgJBgB3fBmk=
+.br
+ ListenPort = 41414
+.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.io:18981
+.br
+ AllowedIPs = 10.10.10.230/32
+
+.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.
+
+.SH SEE ALSO
+.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 http://\:www.wireguard.io/
+World Wide Web
+.UE .
diff --git a/src/tools/wg.c b/src/tools/wg.c
new file mode 100644
index 0000000..d4d2965
--- /dev/null
+++ b/src/tools/wg.c
@@ -0,0 +1,66 @@
+/* Copyright 2015-2016 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. */
+
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "subcommands.h"
+
+const char *PROG_NAME;
+
+static const struct {
+ const char *subcommand;
+ int (*function)(int, 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" },
+ { "genkey", genkey_main, "Generates a new private key and writes it to stdout" },
+ { "genpsk", genkey_main, "Generates a new pre-shared 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(void)
+{
+ fprintf(stderr, "Usage: %s <cmd> [<args>]\n\n", PROG_NAME);
+ fprintf(stderr, "Available subcommands:\n");
+ for (size_t i = 0; i < sizeof(subcommands) / sizeof(subcommands[0]); ++i)
+ fprintf(stderr, " %s: %s\n", subcommands[i].subcommand, subcommands[i].description);
+}
+
+int main(int argc, char *argv[])
+{
+ char *tmp = NULL;
+ PROG_NAME = argv[0];
+
+ if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help") || !strcmp(argv[1], "help"))) {
+ show_usage();
+ return 1;
+ }
+
+ if (argc == 1) {
+ char *new_argv[] = { "show", NULL };
+ return show_main(1, new_argv);
+ }
+
+findsubcommand:
+ 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);
+ }
+
+ /* Crude way of supporting "wg wg0 show..." */
+ if (!tmp && argc >= 3) {
+ tmp = argv[1];
+ argv[1] = argv[2];
+ argv[2] = tmp;
+ goto findsubcommand;
+ }
+
+ fprintf(stderr, "Invalid subcommand: `%s`\n", argv[1]);
+ show_usage();
+ return 1;
+}