wg: 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);
      }
  }
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
			
			
This commit is contained in:
		
							parent
							
								
									fd14807259
								
							
						
					
					
						commit
						b16641e30c
					
				
					 8 changed files with 268 additions and 50 deletions
				
			
		
							
								
								
									
										11
									
								
								src/Makefile
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								src/Makefile
									
									
									
									
									
								
							| 
						 | 
					@ -3,12 +3,19 @@ DESTDIR ?=
 | 
				
			||||||
BINDIR ?= $(PREFIX)/bin
 | 
					BINDIR ?= $(PREFIX)/bin
 | 
				
			||||||
LIBDIR ?= $(PREFIX)/lib
 | 
					LIBDIR ?= $(PREFIX)/lib
 | 
				
			||||||
MANDIR ?= $(PREFIX)/share/man
 | 
					MANDIR ?= $(PREFIX)/share/man
 | 
				
			||||||
 | 
					RUNSTATEDIR ?= /var/run
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CFLAGS += $(shell pkg-config --cflags libmnl 2>/dev/null)
 | 
					 | 
				
			||||||
CFLAGS += -std=gnu11
 | 
					CFLAGS += -std=gnu11
 | 
				
			||||||
CFLAGS += -pedantic -Wall -Wextra
 | 
					CFLAGS += -pedantic -Wall -Wextra
 | 
				
			||||||
CFLAGS += -MMD
 | 
					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))
 | 
					wg: $(patsubst %.c,%.o,$(wildcard *.c))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,9 +5,11 @@
 | 
				
			||||||
#include <unistd.h>
 | 
					#include <unistd.h>
 | 
				
			||||||
#include <sys/types.h>
 | 
					#include <sys/types.h>
 | 
				
			||||||
#include <sys/stat.h>
 | 
					#include <sys/stat.h>
 | 
				
			||||||
#include <syscall.h>
 | 
					 | 
				
			||||||
#include <string.h>
 | 
					#include <string.h>
 | 
				
			||||||
#include <fcntl.h>
 | 
					#include <fcntl.h>
 | 
				
			||||||
 | 
					#ifdef __linux
 | 
				
			||||||
 | 
					#include <syscall.h>
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "curve25519.h"
 | 
					#include "curve25519.h"
 | 
				
			||||||
#include "base64.h"
 | 
					#include "base64.h"
 | 
				
			||||||
| 
						 | 
					@ -17,7 +19,7 @@ static inline ssize_t get_random_bytes(uint8_t *out, size_t len)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	ssize_t ret;
 | 
						ssize_t ret;
 | 
				
			||||||
	int fd;
 | 
						int fd;
 | 
				
			||||||
#ifdef __NR_getrandom
 | 
					#if defined(__NR_getrandom) && defined(__linux__)
 | 
				
			||||||
	ret = syscall(__NR_getrandom, out, len, 0);
 | 
						ret = syscall(__NR_getrandom, out, len, 0);
 | 
				
			||||||
	if (ret >= 0)
 | 
						if (ret >= 0)
 | 
				
			||||||
		return ret;
 | 
							return ret;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										275
									
								
								src/kernel.c
									
									
									
									
									
								
							
							
						
						
									
										275
									
								
								src/kernel.c
									
									
									
									
									
								
							| 
						 | 
					@ -1,25 +1,37 @@
 | 
				
			||||||
/* Copyright 2015-2016 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. */
 | 
					/* Copyright 2015-2016 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <errno.h>
 | 
					#ifdef __linux__
 | 
				
			||||||
#include <libmnl/libmnl.h>
 | 
					#include <libmnl/libmnl.h>
 | 
				
			||||||
#include <linux/if_link.h>
 | 
					#include <linux/if_link.h>
 | 
				
			||||||
#include <linux/netlink.h>
 | 
					#include <linux/netlink.h>
 | 
				
			||||||
#include <linux/rtnetlink.h>
 | 
					#include <linux/rtnetlink.h>
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
#include <netinet/in.h>
 | 
					#include <netinet/in.h>
 | 
				
			||||||
#include <net/if.h>
 | 
					#include <net/if.h>
 | 
				
			||||||
 | 
					#include <errno.h>
 | 
				
			||||||
#include <stdbool.h>
 | 
					#include <stdbool.h>
 | 
				
			||||||
#include <stddef.h>
 | 
					#include <stddef.h>
 | 
				
			||||||
#include <stdio.h>
 | 
					#include <stdio.h>
 | 
				
			||||||
#include <stdlib.h>
 | 
					#include <stdlib.h>
 | 
				
			||||||
#include <string.h>
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					#include <errno.h>
 | 
				
			||||||
 | 
					#include <unistd.h>
 | 
				
			||||||
 | 
					#include <time.h>
 | 
				
			||||||
 | 
					#include <dirent.h>
 | 
				
			||||||
#include <sys/socket.h>
 | 
					#include <sys/socket.h>
 | 
				
			||||||
#include <sys/types.h>
 | 
					#include <sys/types.h>
 | 
				
			||||||
#include <sys/ioctl.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 "kernel.h"
 | 
				
			||||||
#include "../uapi.h"
 | 
					#include "../uapi.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define SOCK_PATH RUNSTATEDIR "/wireguard/"
 | 
				
			||||||
 | 
					#define SOCK_SUFFIX ".sock"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct inflatable_buffer {
 | 
					struct inflatable_buffer {
 | 
				
			||||||
	char *buffer;
 | 
						char *buffer;
 | 
				
			||||||
	char *next;
 | 
						char *next;
 | 
				
			||||||
| 
						 | 
					@ -37,19 +49,24 @@ static int add_next_to_inflatable_buffer(struct inflatable_buffer *buffer)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!buffer->good || !buffer->next) {
 | 
						if (!buffer->good || !buffer->next) {
 | 
				
			||||||
		free(buffer->next);
 | 
							free(buffer->next);
 | 
				
			||||||
 | 
							buffer->good = false;
 | 
				
			||||||
		return 0;
 | 
							return 0;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	len = strlen(buffer->next) + 1;
 | 
						len = strlen(buffer->next) + 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (len == 1)
 | 
						if (len == 1) {
 | 
				
			||||||
 | 
							free(buffer->next);
 | 
				
			||||||
 | 
							buffer->good = false;
 | 
				
			||||||
		return 0;
 | 
							return 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (buffer->len - buffer->pos <= len) {
 | 
						if (buffer->len - buffer->pos <= len) {
 | 
				
			||||||
		expand_to = max(buffer->len * 2, buffer->len + len + 1);
 | 
							expand_to = max(buffer->len * 2, buffer->len + len + 1);
 | 
				
			||||||
		new_buffer = realloc(buffer->buffer, expand_to);
 | 
							new_buffer = realloc(buffer->buffer, expand_to);
 | 
				
			||||||
		if (!new_buffer) {
 | 
							if (!new_buffer) {
 | 
				
			||||||
			free(buffer->next);
 | 
								free(buffer->next);
 | 
				
			||||||
 | 
								buffer->good = false;
 | 
				
			||||||
			return -errno;
 | 
								return -errno;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		memset(&new_buffer[buffer->len], 0, expand_to - buffer->len);
 | 
							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);
 | 
						memcpy(&buffer->buffer[buffer->pos], buffer->next, len);
 | 
				
			||||||
	free(buffer->next);
 | 
						free(buffer->next);
 | 
				
			||||||
 | 
						buffer->good = false;
 | 
				
			||||||
	buffer->pos += len;
 | 
						buffer->pos += len;
 | 
				
			||||||
	return 0;
 | 
						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)
 | 
					static int parse_linkinfo(const struct nlattr *attr, void *data)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct inflatable_buffer *buffer = 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;
 | 
						return MNL_CB_OK;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* first\0second\0third\0forth\0last\0\0 */
 | 
					static int kernel_get_wireguard_interfaces(struct inflatable_buffer *buffer)
 | 
				
			||||||
char *kernel_get_wireguard_interfaces(void)
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct mnl_socket *nl = NULL;
 | 
						struct mnl_socket *nl = NULL;
 | 
				
			||||||
	char *rtnl_buffer = NULL;
 | 
						char *rtnl_buffer = NULL;
 | 
				
			||||||
| 
						 | 
					@ -105,22 +260,13 @@ char *kernel_get_wireguard_interfaces(void)
 | 
				
			||||||
	unsigned int portid, seq;
 | 
						unsigned int portid, seq;
 | 
				
			||||||
	ssize_t len;
 | 
						ssize_t len;
 | 
				
			||||||
	int ret = 0;
 | 
						int ret = 0;
 | 
				
			||||||
	struct inflatable_buffer buffer = { 0 };
 | 
					 | 
				
			||||||
	struct nlmsghdr *nlh;
 | 
						struct nlmsghdr *nlh;
 | 
				
			||||||
	struct ifinfomsg *ifm;
 | 
						struct ifinfomsg *ifm;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	buffer.len = 4096;
 | 
						ret = -ENOMEM;
 | 
				
			||||||
	buffer.buffer = calloc(buffer.len, 1);
 | 
					 | 
				
			||||||
	if (!buffer.buffer) {
 | 
					 | 
				
			||||||
		ret = -errno;
 | 
					 | 
				
			||||||
		goto cleanup;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	rtnl_buffer = calloc(4096, 1);
 | 
						rtnl_buffer = calloc(4096, 1);
 | 
				
			||||||
	if (!rtnl_buffer) {
 | 
						if (!rtnl_buffer)
 | 
				
			||||||
		ret = -errno;
 | 
					 | 
				
			||||||
		goto cleanup;
 | 
							goto cleanup;
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	nl = mnl_socket_open(NETLINK_ROUTE);
 | 
						nl = mnl_socket_open(NETLINK_ROUTE);
 | 
				
			||||||
	if (!nl) {
 | 
						if (!nl) {
 | 
				
			||||||
| 
						 | 
					@ -153,39 +299,41 @@ another:
 | 
				
			||||||
		ret = -errno;
 | 
							ret = -errno;
 | 
				
			||||||
		goto cleanup;
 | 
							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;
 | 
							ret = -errno;
 | 
				
			||||||
		goto cleanup;
 | 
							goto cleanup;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if (len == MNL_CB_OK + 1)
 | 
						if (len == MNL_CB_OK + 1)
 | 
				
			||||||
		goto another;
 | 
							goto another;
 | 
				
			||||||
 | 
						ret = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
cleanup:
 | 
					cleanup:
 | 
				
			||||||
	free(rtnl_buffer);
 | 
						free(rtnl_buffer);
 | 
				
			||||||
	if (nl)
 | 
						if (nl)
 | 
				
			||||||
		mnl_socket_close(nl);
 | 
							mnl_socket_close(nl);
 | 
				
			||||||
	errno = -ret;
 | 
						return 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)
 | 
					static bool kernel_has_wireguard_interface(const char *interface)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	char *interfaces, *this_interface;
 | 
						char *this_interface;
 | 
				
			||||||
	this_interface = interfaces = kernel_get_wireguard_interfaces();
 | 
						struct inflatable_buffer buffer = { .len = 4096 };
 | 
				
			||||||
	if (!interfaces)
 | 
					
 | 
				
			||||||
 | 
						buffer.buffer = calloc(buffer.len, 1);
 | 
				
			||||||
 | 
						if (!buffer.buffer)
 | 
				
			||||||
		return false;
 | 
							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) {
 | 
						for (size_t len = 0; (len = strlen(this_interface)); this_interface += len + 1) {
 | 
				
			||||||
		if (!strcmp(interface, this_interface)) {
 | 
							if (!strcmp(interface, this_interface)) {
 | 
				
			||||||
			free(interfaces);
 | 
								free(buffer.buffer);
 | 
				
			||||||
			return true;
 | 
								return true;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	free(interfaces);
 | 
						free(buffer.buffer);
 | 
				
			||||||
	return false;
 | 
						return false;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -200,7 +348,7 @@ static int do_ioctl(int req, struct ifreq *ifreq)
 | 
				
			||||||
	return ioctl(fd, req, 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 };
 | 
						struct ifreq ifreq = { .ifr_data = (char *)dev };
 | 
				
			||||||
	memcpy(&ifreq.ifr_name, dev->interface, IFNAMSIZ);
 | 
						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);
 | 
						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;
 | 
						int ret;
 | 
				
			||||||
	struct ifreq ifreq = { 0 };
 | 
						struct ifreq ifreq = { 0 };
 | 
				
			||||||
| 
						 | 
					@ -222,7 +370,6 @@ int kernel_get_device(struct wgdevice **dev, const char *interface)
 | 
				
			||||||
			goto out;
 | 
								goto out;
 | 
				
			||||||
		*dev = calloc(ret + sizeof(struct wgdevice), 1);
 | 
							*dev = calloc(ret + sizeof(struct wgdevice), 1);
 | 
				
			||||||
		if (!*dev) {
 | 
							if (!*dev) {
 | 
				
			||||||
			perror("calloc");
 | 
					 | 
				
			||||||
			ret = -ENOMEM;
 | 
								ret = -ENOMEM;
 | 
				
			||||||
			goto out;
 | 
								goto out;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -240,3 +387,65 @@ out:
 | 
				
			||||||
	errno = -ret;
 | 
						errno = -ret;
 | 
				
			||||||
	return 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
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,10 +7,10 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct wgdevice;
 | 
					struct wgdevice;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int kernel_set_device(struct wgdevice *dev);
 | 
					int set_device(struct wgdevice *dev);
 | 
				
			||||||
int kernel_get_device(struct wgdevice **dev, const char *interface);
 | 
					int get_device(struct wgdevice **dev, const char *interface);
 | 
				
			||||||
char *kernel_get_wireguard_interfaces(void);
 | 
					char *get_wireguard_interfaces(void);
 | 
				
			||||||
bool kernel_has_wireguard_interface(const char *interface);
 | 
					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)); \
 | 
					#define for_each_wgpeer(__dev, __peer, __i) for ((__i) = 0, (__peer) = (typeof(__peer))((uint8_t *)(__dev) + sizeof(struct wgdevice)); \
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -22,7 +22,7 @@ int set_main(int argc, char *argv[])
 | 
				
			||||||
	strncpy(device->interface, argv[1], IFNAMSIZ -  1);
 | 
						strncpy(device->interface, argv[1], IFNAMSIZ -  1);
 | 
				
			||||||
	device->interface[IFNAMSIZ - 1] = 0;
 | 
						device->interface[IFNAMSIZ - 1] = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (kernel_set_device(device) != 0) {
 | 
						if (set_device(device) != 0) {
 | 
				
			||||||
		perror("Unable to set device");
 | 
							perror("Unable to set device");
 | 
				
			||||||
		goto cleanup;
 | 
							goto cleanup;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -45,7 +45,7 @@ int setconf_main(int argc, char *argv[])
 | 
				
			||||||
	strncpy(device->interface, argv[1], IFNAMSIZ - 1);
 | 
						strncpy(device->interface, argv[1], IFNAMSIZ - 1);
 | 
				
			||||||
	device->interface[IFNAMSIZ - 1] = 0;
 | 
						device->interface[IFNAMSIZ - 1] = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (kernel_set_device(device) != 0) {
 | 
						if (set_device(device) != 0) {
 | 
				
			||||||
		perror("Unable to set device");
 | 
							perror("Unable to set device");
 | 
				
			||||||
		goto cleanup;
 | 
							goto cleanup;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										10
									
								
								src/show.c
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								src/show.c
									
									
									
									
									
								
							| 
						 | 
					@ -326,7 +326,7 @@ int show_main(int argc, char *argv[])
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (argc == 1 || !strcmp(argv[1], "all")) {
 | 
						if (argc == 1 || !strcmp(argv[1], "all")) {
 | 
				
			||||||
		char *interfaces = kernel_get_wireguard_interfaces(), *interface;
 | 
							char *interfaces = get_wireguard_interfaces(), *interface;
 | 
				
			||||||
		if (!interfaces) {
 | 
							if (!interfaces) {
 | 
				
			||||||
			perror("Unable to get devices");
 | 
								perror("Unable to get devices");
 | 
				
			||||||
			return 1;
 | 
								return 1;
 | 
				
			||||||
| 
						 | 
					@ -334,7 +334,7 @@ int show_main(int argc, char *argv[])
 | 
				
			||||||
		interface = interfaces;
 | 
							interface = interfaces;
 | 
				
			||||||
		for (size_t len = 0; (len = strlen(interface)); interface += len + 1) {
 | 
							for (size_t len = 0; (len = strlen(interface)); interface += len + 1) {
 | 
				
			||||||
			struct wgdevice *device = NULL;
 | 
								struct wgdevice *device = NULL;
 | 
				
			||||||
			if (kernel_get_device(&device, interface) < 0) {
 | 
								if (get_device(&device, interface) < 0) {
 | 
				
			||||||
				perror("Unable to get device");
 | 
									perror("Unable to get device");
 | 
				
			||||||
				continue;
 | 
									continue;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
| 
						 | 
					@ -358,7 +358,7 @@ int show_main(int argc, char *argv[])
 | 
				
			||||||
			show_usage();
 | 
								show_usage();
 | 
				
			||||||
			return 1;
 | 
								return 1;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		interfaces = kernel_get_wireguard_interfaces();
 | 
							interfaces = get_wireguard_interfaces();
 | 
				
			||||||
		if (!interfaces) {
 | 
							if (!interfaces) {
 | 
				
			||||||
			perror("Unable to get devices");
 | 
								perror("Unable to get devices");
 | 
				
			||||||
			return 1;
 | 
								return 1;
 | 
				
			||||||
| 
						 | 
					@ -371,12 +371,12 @@ int show_main(int argc, char *argv[])
 | 
				
			||||||
		show_usage();
 | 
							show_usage();
 | 
				
			||||||
	else {
 | 
						else {
 | 
				
			||||||
		struct wgdevice *device = NULL;
 | 
							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]);
 | 
								fprintf(stderr, "`%s` is not a valid WireGuard interface\n", argv[1]);
 | 
				
			||||||
			show_usage();
 | 
								show_usage();
 | 
				
			||||||
			return 1;
 | 
								return 1;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if (kernel_get_device(&device, argv[1]) < 0) {
 | 
							if (get_device(&device, argv[1]) < 0) {
 | 
				
			||||||
			perror("Unable to get device");
 | 
								perror("Unable to get device");
 | 
				
			||||||
			show_usage();
 | 
								show_usage();
 | 
				
			||||||
			return 1;
 | 
								return 1;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -31,13 +31,13 @@ int showconf_main(int argc, char *argv[])
 | 
				
			||||||
		return 1;
 | 
							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, "`%s` is not a valid WireGuard interface\n", argv[1]);
 | 
				
			||||||
		fprintf(stderr, "Usage: %s %s <interface>\n", PROG_NAME, argv[0]);
 | 
							fprintf(stderr, "Usage: %s %s <interface>\n", PROG_NAME, argv[0]);
 | 
				
			||||||
		return 1;
 | 
							return 1;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (kernel_get_device(&device, argv[1])) {
 | 
						if (get_device(&device, argv[1])) {
 | 
				
			||||||
		perror("Unable to get device");
 | 
							perror("Unable to get device");
 | 
				
			||||||
		goto cleanup;
 | 
							goto cleanup;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue