summaryrefslogtreecommitdiffstatshomepage
path: root/src/tools
diff options
context:
space:
mode:
authorJason A. Donenfeld <Jason@zx2c4.com>2016-07-19 15:26:56 +0200
committerJason A. Donenfeld <Jason@zx2c4.com>2016-07-20 22:04:56 +0200
commit32373e10960c9f40561b52c815b92ad535e23039 (patch)
tree72f8ac6952822f67e3dcdfb20c0efbd617c4900d /src/tools
parentbuild system: revamp building and configuration (diff)
downloadwireguard-monolithic-historical-32373e10960c9f40561b52c815b92ad535e23039.tar.xz
wireguard-monolithic-historical-32373e10960c9f40561b52c815b92ad535e23039.zip
tools: first additions of userspace integration
This is designed to work with a server that follows this: struct sockaddr_un addr = { .sun_family = AF_UNIX, .sun_path = "/var/run/wireguard/wguserspace0.sock" }; int fd, ret; ssize_t len; socklen_t socklen; struct wgdevice *device; fd = socket(AF_UNIX, SOCK_DGRAM, 0); if (fd < 0) exit(1); if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) exit(1); for (;;) { /* First we look at how big the next message is, so we know how much to * allocate. Note on BSD you can instead use ioctl(fd, FIONREAD, &len). */ len = recv(fd, NULL, 0, MSG_PEEK | MSG_TRUNC); if (len < 0) { handle_error(); continue; } /* Next we allocate a buffer for the received data. */ device = NULL; if (len) { device = malloc(len); if (!device) { handle_error(); continue; } } /* Finally we receive the data, storing too the return address. */ socklen = sizeof(addr); len = recvfrom(fd, device, len, 0, (struct sockaddr *)&addr, (socklen_t *)&socklen); if (len < 0) { handle_error(); free(device); continue; } if (!len) { /* If len is zero, it's a "get" request, so we send our device back. */ device = get_current_wireguard_device(&len); sendto(fd, device, len, 0, (struct sockaddr *)&addr, socklen); } else { /* Otherwise, we just received a wgdevice, so we should "set" and send back the return status. */ ret = set_current_wireguard_device(device); sendto(fd, &ret, sizeof(ret), 0, (struct sockaddr *)&addr, socklen); free(device); } }
Diffstat (limited to 'src/tools')
-rw-r--r--src/tools/Makefile11
-rw-r--r--src/tools/genkey.c6
-rw-r--r--src/tools/kernel.c275
-rw-r--r--src/tools/kernel.h8
-rw-r--r--src/tools/set.c2
-rw-r--r--src/tools/setconf.c2
-rw-r--r--src/tools/show.c10
-rw-r--r--src/tools/showconf.c4
8 files changed, 268 insertions, 50 deletions
diff --git a/src/tools/Makefile b/src/tools/Makefile
index 060ae18..0436f69 100644
--- a/src/tools/Makefile
+++ b/src/tools/Makefile
@@ -3,12 +3,19 @@ DESTDIR ?=
BINDIR ?= $(PREFIX)/bin
LIBDIR ?= $(PREFIX)/lib
MANDIR ?= $(PREFIX)/share/man
+RUNSTATEDIR ?= /var/run
-CFLAGS += $(shell pkg-config --cflags libmnl 2>/dev/null)
CFLAGS += -std=gnu11
CFLAGS += -pedantic -Wall -Wextra
CFLAGS += -MMD
-LDLIBS += -lresolv $(shell pkg-config --libs libmnl 2>/dev/null || echo -lmnl)
+CFLAGS += -DRUNSTATEDIR="\"$(RUNSTATEDIR)\""
+LDLIBS += -lresolv
+ifeq ($(shell uname -s),Linux)
+LIBMNL_CFLAGS := $(shell pkg-config --cflags libmnl 2>/dev/null)
+LIBMNL_LDLIBS := $(shell pkg-config --libs libmnl 2>/dev/null || echo -lmnl)
+CFLAGS += $(LIBMNL_CFLAGS)
+LDLIBS += $(LIBMNL_LDLIBS)
+endif
wg: $(patsubst %.c,%.o,$(wildcard *.c))
diff --git a/src/tools/genkey.c b/src/tools/genkey.c
index a312b46..af2765f 100644
--- a/src/tools/genkey.c
+++ b/src/tools/genkey.c
@@ -5,9 +5,11 @@
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
-#include <syscall.h>
#include <string.h>
#include <fcntl.h>
+#ifdef __linux
+#include <syscall.h>
+#endif
#include "curve25519.h"
#include "base64.h"
@@ -17,7 +19,7 @@ static inline ssize_t get_random_bytes(uint8_t *out, size_t len)
{
ssize_t ret;
int fd;
-#ifdef __NR_getrandom
+#if defined(__NR_getrandom) && defined(__linux__)
ret = syscall(__NR_getrandom, out, len, 0);
if (ret >= 0)
return ret;
diff --git a/src/tools/kernel.c b/src/tools/kernel.c
index 0448308..da80d95 100644
--- a/src/tools/kernel.c
+++ b/src/tools/kernel.c
@@ -1,25 +1,37 @@
/* Copyright 2015-2016 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. */
-#include <errno.h>
+#ifdef __linux__
#include <libmnl/libmnl.h>
#include <linux/if_link.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
+#endif
#include <netinet/in.h>
#include <net/if.h>
+#include <errno.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <time.h>
+#include <dirent.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/ioctl.h>
-#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+#include <sys/signal.h>
#include "kernel.h"
#include "../uapi.h"
+#define SOCK_PATH RUNSTATEDIR "/wireguard/"
+#define SOCK_SUFFIX ".sock"
+
struct inflatable_buffer {
char *buffer;
char *next;
@@ -37,19 +49,24 @@ static int add_next_to_inflatable_buffer(struct inflatable_buffer *buffer)
if (!buffer->good || !buffer->next) {
free(buffer->next);
+ buffer->good = false;
return 0;
}
len = strlen(buffer->next) + 1;
- if (len == 1)
+ if (len == 1) {
+ free(buffer->next);
+ buffer->good = false;
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);
+ buffer->good = false;
return -errno;
}
memset(&new_buffer[buffer->len], 0, expand_to - buffer->len);
@@ -58,10 +75,149 @@ static int add_next_to_inflatable_buffer(struct inflatable_buffer *buffer)
}
memcpy(&buffer->buffer[buffer->pos], buffer->next, len);
free(buffer->next);
+ buffer->good = false;
buffer->pos += len;
return 0;
}
+static int userspace_interface_fd(const char *interface)
+{
+ struct stat sbuf;
+ struct sockaddr_un addr = { .sun_family = AF_UNIX };
+ int fd = -1, ret;
+ ret = snprintf(addr.sun_path, sizeof(addr.sun_path) - 1, SOCK_PATH "%s" SOCK_SUFFIX, interface);
+ if (ret < 0)
+ goto out;
+ ret = stat(addr.sun_path, &sbuf);
+ if (ret < 0)
+ goto out;
+ ret = -EBADF;
+ if (!S_ISSOCK(sbuf.st_mode))
+ goto out;
+
+ ret = fd = socket(AF_UNIX, SOCK_DGRAM, 0);
+ if (ret < 0)
+ goto out;
+ ret = bind(fd, (struct sockaddr *)&addr, sizeof(sa_family_t));
+ if (ret < 0)
+ goto out;
+ ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
+ if (ret < 0) {
+ if (errno == ECONNREFUSED) /* If the process is gone, we try to clean up the socket. */
+ unlink(addr.sun_path);
+ goto out;
+ }
+out:
+ if (ret && fd >= 0)
+ close(fd);
+ if (!ret)
+ ret = fd;
+ return ret;
+}
+
+static bool userspace_has_wireguard_interface(const char *interface)
+{
+ int fd = userspace_interface_fd(interface);
+ if (fd < 0)
+ return false;
+ close(fd);
+ return true;
+}
+
+static int userspace_get_wireguard_interfaces(struct inflatable_buffer *buffer)
+{
+ DIR *dir;
+ struct dirent *ent;
+ size_t len;
+ char *end;
+ int ret = 0;
+
+ dir = opendir(SOCK_PATH);
+ if (!dir)
+ return errno == ENOENT ? 0 : errno;
+ while ((ent = readdir(dir))) {
+ len = strlen(ent->d_name);
+ if (len <= strlen(SOCK_SUFFIX))
+ continue;
+ end = &ent->d_name[len - strlen(SOCK_SUFFIX)];
+ if (strncmp(end, SOCK_SUFFIX, strlen(SOCK_SUFFIX)))
+ continue;
+ *end = '\0';
+ if (!userspace_has_wireguard_interface(ent->d_name))
+ continue;
+ buffer->next = strdup(ent->d_name);
+ buffer->good = true;
+ ret = add_next_to_inflatable_buffer(buffer);
+ if (ret < 0)
+ goto out;
+ }
+out:
+ closedir(dir);
+ return ret;
+}
+
+static int userspace_set_device(struct wgdevice *dev)
+{
+ struct wgpeer *peer;
+ size_t len;
+ ssize_t ret;
+ int ret_code;
+ int fd = userspace_interface_fd(dev->interface);
+ if (fd < 0)
+ return fd;
+ for_each_wgpeer(dev, peer, len);
+ len = (unsigned char *)peer - (unsigned char *)dev;
+ ret = -EBADMSG;
+ if (!len)
+ goto out;
+ ret = send(fd, dev, len, 0);
+ if (ret < 0)
+ goto out;
+ ret = recv(fd, &ret_code, sizeof(ret_code), 0);
+ if (ret < 0)
+ goto out;
+ ret = ret_code;
+out:
+ close(fd);
+ return (int)ret;
+}
+
+static int userspace_get_device(struct wgdevice **dev, const char *interface)
+{
+ ssize_t len;
+ int ret, fd = userspace_interface_fd(interface);
+ if (fd < 0)
+ return fd;
+ *dev = NULL;
+ ret = send(fd, NULL, 0, 0);
+ if (ret < 0)
+ goto out;
+
+ ret = len = recv(fd, NULL, 0, MSG_PEEK | MSG_TRUNC);
+ if (len < 0)
+ goto out;
+ ret = -EBADMSG;
+ if ((size_t)len < sizeof(struct wgdevice))
+ goto out;
+
+ ret = -ENOMEM;
+ *dev = calloc(len, 1);
+ if (!*dev)
+ goto out;
+
+ ret = recv(fd, *dev, len, 0);
+ if (ret < 0)
+ goto out;
+ ret = 0;
+out:
+ if (*dev && ret)
+ free(*dev);
+ close(fd);
+ errno = -ret;
+ return ret;
+}
+
+#ifdef __linux__
static int parse_linkinfo(const struct nlattr *attr, void *data)
{
struct inflatable_buffer *buffer = data;
@@ -96,8 +252,7 @@ static int read_devices_cb(const struct nlmsghdr *nlh, void *data)
return MNL_CB_OK;
}
-/* first\0second\0third\0forth\0last\0\0 */
-char *kernel_get_wireguard_interfaces(void)
+static int kernel_get_wireguard_interfaces(struct inflatable_buffer *buffer)
{
struct mnl_socket *nl = NULL;
char *rtnl_buffer = NULL;
@@ -105,22 +260,13 @@ char *kernel_get_wireguard_interfaces(void)
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;
- }
-
+ ret = -ENOMEM;
rtnl_buffer = calloc(4096, 1);
- if (!rtnl_buffer) {
- ret = -errno;
+ if (!rtnl_buffer)
goto cleanup;
- }
nl = mnl_socket_open(NETLINK_ROUTE);
if (!nl) {
@@ -153,39 +299,41 @@ another:
ret = -errno;
goto cleanup;
}
- if ((len = mnl_cb_run(rtnl_buffer, len, seq, portid, read_devices_cb, &buffer)) < 0) {
+ 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;
+ ret = 0;
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;
+ return ret;
}
-bool kernel_has_wireguard_interface(const char *interface)
+static bool kernel_has_wireguard_interface(const char *interface)
{
- char *interfaces, *this_interface;
- this_interface = interfaces = kernel_get_wireguard_interfaces();
- if (!interfaces)
+ char *this_interface;
+ struct inflatable_buffer buffer = { .len = 4096 };
+
+ buffer.buffer = calloc(buffer.len, 1);
+ if (!buffer.buffer)
return false;
+ if (kernel_get_wireguard_interfaces(&buffer) < 0) {
+ free(buffer.buffer);
+ return false;
+ }
+ this_interface = buffer.buffer;
for (size_t len = 0; (len = strlen(this_interface)); this_interface += len + 1) {
if (!strcmp(interface, this_interface)) {
- free(interfaces);
+ free(buffer.buffer);
return true;
}
}
- free(interfaces);
+ free(buffer.buffer);
return false;
}
@@ -200,7 +348,7 @@ static int do_ioctl(int req, struct ifreq *ifreq)
return ioctl(fd, req, ifreq);
}
-int kernel_set_device(struct wgdevice *dev)
+static int kernel_set_device(struct wgdevice *dev)
{
struct ifreq ifreq = { .ifr_data = (char *)dev };
memcpy(&ifreq.ifr_name, dev->interface, IFNAMSIZ);
@@ -208,7 +356,7 @@ int kernel_set_device(struct wgdevice *dev)
return do_ioctl(WG_SET_DEVICE, &ifreq);
}
-int kernel_get_device(struct wgdevice **dev, const char *interface)
+static int kernel_get_device(struct wgdevice **dev, const char *interface)
{
int ret;
struct ifreq ifreq = { 0 };
@@ -222,7 +370,6 @@ int kernel_get_device(struct wgdevice **dev, const char *interface)
goto out;
*dev = calloc(ret + sizeof(struct wgdevice), 1);
if (!*dev) {
- perror("calloc");
ret = -ENOMEM;
goto out;
}
@@ -240,3 +387,65 @@ out:
errno = -ret;
return ret;
}
+#endif
+
+/* first\0second\0third\0forth\0last\0\0 */
+char *get_wireguard_interfaces(void)
+{
+ struct inflatable_buffer buffer = { .len = 4096 };
+ int ret;
+
+ ret = -ENOMEM;
+ buffer.buffer = calloc(buffer.len, 1);
+ if (!buffer.buffer)
+ goto cleanup;
+
+#ifdef __linux__
+ ret = kernel_get_wireguard_interfaces(&buffer);
+ if (ret < 0)
+ goto cleanup;
+#endif
+ ret = userspace_get_wireguard_interfaces(&buffer);
+ if (ret < 0)
+ goto cleanup;
+
+cleanup:
+ errno = -ret;
+ if (errno) {
+ perror("Error when trying to get a list of WireGuard interfaces");
+ free(buffer.buffer);
+ return NULL;
+ }
+ return buffer.buffer;
+}
+
+int get_device(struct wgdevice **dev, const char *interface)
+{
+#ifdef __linux__
+ if (userspace_has_wireguard_interface(interface))
+ return userspace_get_device(dev, interface);
+ return kernel_get_device(dev, interface);
+#else
+ return userspace_get_device(dev, interface);
+#endif
+}
+
+int set_device(struct wgdevice *dev)
+{
+#ifdef __linux__
+ if (userspace_has_wireguard_interface(dev->interface))
+ return userspace_set_device(dev);
+ return kernel_set_device(dev);
+#else
+ return userspace_set_device(dev);
+#endif
+}
+
+bool has_wireguard_interface(const char *interface)
+{
+#ifdef __linux__
+ return userspace_has_wireguard_interface(interface) || kernel_has_wireguard_interface(interface);
+#else
+ return userspace_has_wireguard_interface(interface);
+#endif
+}
diff --git a/src/tools/kernel.h b/src/tools/kernel.h
index 0525ce1..8aa0f82 100644
--- a/src/tools/kernel.h
+++ b/src/tools/kernel.h
@@ -7,10 +7,10 @@
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);
+int set_device(struct wgdevice *dev);
+int get_device(struct wgdevice **dev, const char *interface);
+char *get_wireguard_interfaces(void);
+bool 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)); \
diff --git a/src/tools/set.c b/src/tools/set.c
index c7656e8..8278151 100644
--- a/src/tools/set.c
+++ b/src/tools/set.c
@@ -22,7 +22,7 @@ int set_main(int argc, char *argv[])
strncpy(device->interface, argv[1], IFNAMSIZ - 1);
device->interface[IFNAMSIZ - 1] = 0;
- if (kernel_set_device(device) != 0) {
+ if (set_device(device) != 0) {
perror("Unable to set device");
goto cleanup;
}
diff --git a/src/tools/setconf.c b/src/tools/setconf.c
index 81faa64..a9787eb 100644
--- a/src/tools/setconf.c
+++ b/src/tools/setconf.c
@@ -45,7 +45,7 @@ int setconf_main(int argc, char *argv[])
strncpy(device->interface, argv[1], IFNAMSIZ - 1);
device->interface[IFNAMSIZ - 1] = 0;
- if (kernel_set_device(device) != 0) {
+ if (set_device(device) != 0) {
perror("Unable to set device");
goto cleanup;
}
diff --git a/src/tools/show.c b/src/tools/show.c
index 7927534..d606b4e 100644
--- a/src/tools/show.c
+++ b/src/tools/show.c
@@ -326,7 +326,7 @@ int show_main(int argc, char *argv[])
}
if (argc == 1 || !strcmp(argv[1], "all")) {
- char *interfaces = kernel_get_wireguard_interfaces(), *interface;
+ char *interfaces = get_wireguard_interfaces(), *interface;
if (!interfaces) {
perror("Unable to get devices");
return 1;
@@ -334,7 +334,7 @@ int show_main(int argc, char *argv[])
interface = interfaces;
for (size_t len = 0; (len = strlen(interface)); interface += len + 1) {
struct wgdevice *device = NULL;
- if (kernel_get_device(&device, interface) < 0) {
+ if (get_device(&device, interface) < 0) {
perror("Unable to get device");
continue;
}
@@ -358,7 +358,7 @@ int show_main(int argc, char *argv[])
show_usage();
return 1;
}
- interfaces = kernel_get_wireguard_interfaces();
+ interfaces = get_wireguard_interfaces();
if (!interfaces) {
perror("Unable to get devices");
return 1;
@@ -371,12 +371,12 @@ int show_main(int argc, char *argv[])
show_usage();
else {
struct wgdevice *device = NULL;
- if (!kernel_has_wireguard_interface(argv[1])) {
+ if (!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) {
+ if (get_device(&device, argv[1]) < 0) {
perror("Unable to get device");
show_usage();
return 1;
diff --git a/src/tools/showconf.c b/src/tools/showconf.c
index 95d2e17..68084c0 100644
--- a/src/tools/showconf.c
+++ b/src/tools/showconf.c
@@ -31,13 +31,13 @@ int showconf_main(int argc, char *argv[])
return 1;
}
- if (!kernel_has_wireguard_interface(argv[1])) {
+ if (!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])) {
+ if (get_device(&device, argv[1])) {
perror("Unable to get device");
goto cleanup;
}