wg: support text-based ipc
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
This commit is contained in:
parent
c3b2dbcdb0
commit
945fae0c7c
11
src/config.c
11
src/config.c
|
@ -15,7 +15,7 @@
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "ipc.h"
|
#include "ipc.h"
|
||||||
#include "base64.h"
|
#include "encoding.h"
|
||||||
|
|
||||||
#define COMMENT_CHAR '#'
|
#define COMMENT_CHAR '#'
|
||||||
|
|
||||||
|
@ -171,14 +171,12 @@ static inline bool parse_endpoint(struct sockaddr *endpoint, const char *value)
|
||||||
fprintf(stderr, "Unable to find matching brace of endpoint: `%s`\n", value);
|
fprintf(stderr, "Unable to find matching brace of endpoint: `%s`\n", value);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
*end = '\0';
|
*end++ = '\0';
|
||||||
++end;
|
if (*end++ != ':' || !*end) {
|
||||||
if (*end != ':' || !*(end + 1)) {
|
|
||||||
free(mutable);
|
free(mutable);
|
||||||
fprintf(stderr, "Unable to find port of endpoint: `%s`\n", value);
|
fprintf(stderr, "Unable to find port of endpoint: `%s`\n", value);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
++end;
|
|
||||||
} else {
|
} else {
|
||||||
begin = mutable;
|
begin = mutable;
|
||||||
end = strrchr(mutable, ':');
|
end = strrchr(mutable, ':');
|
||||||
|
@ -187,8 +185,7 @@ static inline bool parse_endpoint(struct sockaddr *endpoint, const char *value)
|
||||||
fprintf(stderr, "Unable to find port of endpoint: `%s`\n", value);
|
fprintf(stderr, "Unable to find port of endpoint: `%s`\n", value);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
*end = '\0';
|
*end++ = '\0';
|
||||||
++end;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (unsigned int timeout = 1000000; timeout < 90000000; timeout = timeout * 3 / 2) {
|
for (unsigned int timeout = 1000000; timeout < 90000000; timeout = timeout * 3 / 2) {
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
/* Copyright (C) 2015-2017 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
/* Copyright (C) 2015-2017 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||||
*
|
*
|
||||||
* This is a specialized constant-time base64 implementation that resists side-channel attacks.
|
* This is a specialized constant-time base64/hex implementation that resists side-channel attacks.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "base64.h"
|
#include "encoding.h"
|
||||||
|
|
||||||
static inline void encode(char dest[4], const uint8_t src[3])
|
static inline void encode_base64(char dest[4], const uint8_t src[3])
|
||||||
{
|
{
|
||||||
const uint8_t input[] = { (src[0] >> 2) & 63, ((src[0] << 4) | (src[1] >> 4)) & 63, ((src[1] << 2) | (src[2] >> 6)) & 63, src[2] & 63 };
|
const uint8_t input[] = { (src[0] >> 2) & 63, ((src[0] << 4) | (src[1] >> 4)) & 63, ((src[1] << 2) | (src[2] >> 6)) & 63, src[2] & 63 };
|
||||||
for (unsigned int i = 0; i < 4; ++i)
|
for (unsigned int i = 0; i < 4; ++i)
|
||||||
|
@ -22,13 +22,13 @@ void key_to_base64(char base64[static WG_KEY_LEN_BASE64], const uint8_t key[stat
|
||||||
{
|
{
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
for (i = 0; i < WG_KEY_LEN / 3; ++i)
|
for (i = 0; i < WG_KEY_LEN / 3; ++i)
|
||||||
encode(&base64[i * 4], &key[i * 3]);
|
encode_base64(&base64[i * 4], &key[i * 3]);
|
||||||
encode(&base64[i * 4], (const uint8_t[]){ key[i * 3 + 0], key[i * 3 + 1], 0 });
|
encode_base64(&base64[i * 4], (const uint8_t[]){ key[i * 3 + 0], key[i * 3 + 1], 0 });
|
||||||
base64[WG_KEY_LEN_BASE64 - 2] = '=';
|
base64[WG_KEY_LEN_BASE64 - 2] = '=';
|
||||||
base64[WG_KEY_LEN_BASE64 - 1] = '\0';
|
base64[WG_KEY_LEN_BASE64 - 1] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int decode(const char src[4])
|
static inline int decode_base64(const char src[4])
|
||||||
{
|
{
|
||||||
int val = 0;
|
int val = 0;
|
||||||
for (unsigned int i = 0; i < 4; ++i)
|
for (unsigned int i = 0; i < 4; ++i)
|
||||||
|
@ -50,17 +50,52 @@ bool key_from_base64(uint8_t key[static WG_KEY_LEN], const char *base64)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
for (i = 0; i < WG_KEY_LEN / 3; ++i) {
|
for (i = 0; i < WG_KEY_LEN / 3; ++i) {
|
||||||
val = decode(&base64[i * 4]);
|
val = decode_base64(&base64[i * 4]);
|
||||||
if (val < 0)
|
if (val < 0)
|
||||||
return false;
|
return false;
|
||||||
key[i * 3 + 0] = (val >> 16) & 0xff;
|
key[i * 3 + 0] = (val >> 16) & 0xff;
|
||||||
key[i * 3 + 1] = (val >> 8) & 0xff;
|
key[i * 3 + 1] = (val >> 8) & 0xff;
|
||||||
key[i * 3 + 2] = val & 0xff;
|
key[i * 3 + 2] = val & 0xff;
|
||||||
}
|
}
|
||||||
val = decode((const char[]){ base64[i * 4 + 0], base64[i * 4 + 1], base64[i * 4 + 2], 'A' });
|
val = decode_base64((const char[]){ base64[i * 4 + 0], base64[i * 4 + 1], base64[i * 4 + 2], 'A' });
|
||||||
if (val < 0 || val & 0xff)
|
if (val < 0 || val & 0xff)
|
||||||
return false;
|
return false;
|
||||||
key[i * 3 + 0] = (val >> 16) & 0xff;
|
key[i * 3 + 0] = (val >> 16) & 0xff;
|
||||||
key[i * 3 + 1] = (val >> 8) & 0xff;
|
key[i * 3 + 1] = (val >> 8) & 0xff;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void key_to_hex(char hex[static WG_KEY_LEN_HEX], const uint8_t key[static WG_KEY_LEN])
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
for (i = 0; i < WG_KEY_LEN; ++i) {
|
||||||
|
hex[i * 2] = 87U + (key[i] >> 4) + ((((key[i] >> 4) - 10U) >> 8) & ~38U);
|
||||||
|
hex[i * 2 + 1] = 87U + (key[i] & 0xf) + ((((key[i] & 0xf) - 10U) >> 8) & ~38U);
|
||||||
|
}
|
||||||
|
hex[i * 2] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
bool key_from_hex(uint8_t key[static WG_KEY_LEN], const char *hex)
|
||||||
|
{
|
||||||
|
uint8_t i, c, c_acc = 0, c_alpha0, c_alpha, c_num0, c_num, c_val, state = 0;
|
||||||
|
|
||||||
|
if (strlen(hex) != WG_KEY_LEN_HEX - 1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (i = 0; i < WG_KEY_LEN_HEX - 1; ++i) {
|
||||||
|
c = (uint8_t)hex[i];
|
||||||
|
c_num = c ^ 48U;
|
||||||
|
c_num0 = (c_num - 10U) >> 8;
|
||||||
|
c_alpha = (c & ~32U) - 55U;
|
||||||
|
c_alpha0 = ((c_alpha - 10U) ^ (c_alpha - 16U)) >> 8;
|
||||||
|
if (!(c_num0 | c_alpha0))
|
||||||
|
return false;
|
||||||
|
c_val = (c_num0 & c_num) | (c_alpha0 & c_alpha);
|
||||||
|
if (!state)
|
||||||
|
c_acc = c_val * 16U;
|
||||||
|
else
|
||||||
|
key[i / 2] = c_acc | c_val;
|
||||||
|
state = ~state;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
|
@ -1,15 +1,19 @@
|
||||||
/* Copyright (C) 2015-2017 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. */
|
/* Copyright (C) 2015-2017 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. */
|
||||||
|
|
||||||
#ifndef BASE64_H
|
#ifndef ENCODING_H
|
||||||
#define BASE64_H
|
#define ENCODING_H
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "../uapi.h"
|
#include "../uapi.h"
|
||||||
|
|
||||||
#define WG_KEY_LEN_BASE64 ((((WG_KEY_LEN) + 2) / 3) * 4 + 1)
|
#define WG_KEY_LEN_BASE64 ((((WG_KEY_LEN) + 2) / 3) * 4 + 1)
|
||||||
|
#define WG_KEY_LEN_HEX (WG_KEY_LEN * 2 + 1)
|
||||||
|
|
||||||
void key_to_base64(char base64[static WG_KEY_LEN_BASE64], const uint8_t key[static WG_KEY_LEN]);
|
void key_to_base64(char base64[static WG_KEY_LEN_BASE64], const uint8_t key[static WG_KEY_LEN]);
|
||||||
bool key_from_base64(uint8_t key[static WG_KEY_LEN], const char *base64);
|
bool key_from_base64(uint8_t key[static WG_KEY_LEN], const char *base64);
|
||||||
|
|
||||||
|
void key_to_hex(char hex[static WG_KEY_LEN_HEX], const uint8_t key[static WG_KEY_LEN]);
|
||||||
|
bool key_from_hex(uint8_t key[static WG_KEY_LEN], const char *hex);
|
||||||
|
|
||||||
#endif
|
#endif
|
|
@ -12,7 +12,7 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "curve25519.h"
|
#include "curve25519.h"
|
||||||
#include "base64.h"
|
#include "encoding.h"
|
||||||
#include "subcommands.h"
|
#include "subcommands.h"
|
||||||
|
|
||||||
static inline ssize_t get_random_bytes(uint8_t *out, size_t len)
|
static inline ssize_t get_random_bytes(uint8_t *out, size_t len)
|
||||||
|
|
364
src/ipc.c
364
src/ipc.c
|
@ -19,14 +19,19 @@
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <limits.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 <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/un.h>
|
#include <sys/un.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
#include "ipc.h"
|
#include "ipc.h"
|
||||||
|
#include "encoding.h"
|
||||||
|
#include "curve25519.h"
|
||||||
#include "../uapi.h"
|
#include "../uapi.h"
|
||||||
|
|
||||||
#define SOCK_PATH RUNSTATEDIR "/wireguard/"
|
#define SOCK_PATH RUNSTATEDIR "/wireguard/"
|
||||||
|
@ -42,16 +47,6 @@ struct inflatable_buffer {
|
||||||
|
|
||||||
#define max(a, b) ((a) > (b) ? (a) : (b))
|
#define max(a, b) ((a) > (b) ? (a) : (b))
|
||||||
|
|
||||||
static int check_version_magic(struct wgdevice *device, int ret)
|
|
||||||
{
|
|
||||||
if (ret == -EPROTO || (!ret && device->version_magic != WG_API_VERSION_MAGIC)) {
|
|
||||||
fprintf(stderr, "This program was built for a different version of WireGuard than\nwhat is currently running. Either this version of wg(8) is out\nof date, or the currently loaded WireGuard module is out of date.\nIf you have just updated your WireGuard installation, you may have\nforgotten to unload the previous running WireGuard module. Try\nrunning `rmmod wireguard` as root, and then try re-adding the interface\nand trying again.\n\n");
|
|
||||||
errno = EPROTO;
|
|
||||||
return -EPROTO;
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int add_next_to_inflatable_buffer(struct inflatable_buffer *buffer)
|
static int add_next_to_inflatable_buffer(struct inflatable_buffer *buffer)
|
||||||
{
|
{
|
||||||
size_t len, expand_to;
|
size_t len, expand_to;
|
||||||
|
@ -90,11 +85,12 @@ static int add_next_to_inflatable_buffer(struct inflatable_buffer *buffer)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int userspace_interface_fd(const char *interface)
|
static FILE *userspace_interface_file(const char *interface)
|
||||||
{
|
{
|
||||||
struct stat sbuf;
|
struct stat sbuf;
|
||||||
struct sockaddr_un addr = { .sun_family = AF_UNIX };
|
struct sockaddr_un addr = { .sun_family = AF_UNIX };
|
||||||
int fd = -1, ret;
|
int fd = -1, ret;
|
||||||
|
FILE *f;
|
||||||
|
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
if (strchr(interface, '/'))
|
if (strchr(interface, '/'))
|
||||||
|
@ -119,20 +115,25 @@ static int userspace_interface_fd(const char *interface)
|
||||||
unlink(addr.sun_path);
|
unlink(addr.sun_path);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
f = fdopen(fd, "r+");
|
||||||
|
if (!f)
|
||||||
|
ret = -errno;
|
||||||
out:
|
out:
|
||||||
if (ret && fd >= 0)
|
if (ret && fd >= 0)
|
||||||
close(fd);
|
close(fd);
|
||||||
if (!ret)
|
if (ret) {
|
||||||
ret = fd;
|
errno = -ret;
|
||||||
return ret;
|
return NULL;
|
||||||
|
}
|
||||||
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool userspace_has_wireguard_interface(const char *interface)
|
static bool userspace_has_wireguard_interface(const char *interface)
|
||||||
{
|
{
|
||||||
int fd = userspace_interface_fd(interface);
|
FILE *f = userspace_interface_file(interface);
|
||||||
if (fd < 0)
|
if (!f)
|
||||||
return false;
|
return false;
|
||||||
close(fd);
|
fclose(f);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,110 +171,261 @@ out:
|
||||||
|
|
||||||
static int userspace_set_device(struct wgdevice *dev)
|
static int userspace_set_device(struct wgdevice *dev)
|
||||||
{
|
{
|
||||||
|
static const uint8_t zero[WG_KEY_LEN] = { 0 };
|
||||||
|
char hex[WG_KEY_LEN_HEX], ip[INET6_ADDRSTRLEN], host[4096 + 1], service[512 + 1];
|
||||||
struct wgpeer *peer;
|
struct wgpeer *peer;
|
||||||
size_t len;
|
struct wgipmask *ipmask;
|
||||||
ssize_t ret;
|
FILE *f;
|
||||||
int ret_code;
|
int ret;
|
||||||
int fd = userspace_interface_fd(dev->interface);
|
size_t i, j;
|
||||||
if (fd < 0)
|
socklen_t addr_len;
|
||||||
return fd;
|
|
||||||
for_each_wgpeer(dev, peer, len);
|
f = userspace_interface_file(dev->interface);
|
||||||
len = (uint8_t *)peer - (uint8_t *)dev;
|
if (!f)
|
||||||
ret = -EBADMSG;
|
return -errno;
|
||||||
if (!len)
|
fprintf(f, "set=1\n");
|
||||||
goto out;
|
|
||||||
dev->version_magic = WG_API_VERSION_MAGIC;
|
if (dev->flags & WGDEVICE_REMOVE_PRIVATE_KEY)
|
||||||
ret = write(fd, dev, len);
|
fprintf(f, "private_key=\n");
|
||||||
if (ret < 0)
|
else if (memcmp(dev->private_key, zero, WG_KEY_LEN)) {
|
||||||
goto out;
|
key_to_hex(hex, dev->private_key);
|
||||||
ret = read(fd, &ret_code, sizeof(ret_code));
|
fprintf(f, "private_key=%s\n", hex);
|
||||||
if (ret < 0)
|
|
||||||
goto out;
|
|
||||||
if (ret != sizeof(ret_code)) {
|
|
||||||
ret = -EBADMSG;
|
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
ret = ret_code;
|
if (dev->port)
|
||||||
out:
|
fprintf(f, "listen_port=%u\n", dev->port);
|
||||||
close(fd);
|
if (dev->flags & WGDEVICE_REMOVE_FWMARK)
|
||||||
|
fprintf(f, "fwmark=\n");
|
||||||
|
else if (dev->fwmark)
|
||||||
|
fprintf(f, "fwmark=%u\n", dev->fwmark);
|
||||||
|
if (dev->flags & WGDEVICE_REPLACE_PEERS)
|
||||||
|
fprintf(f, "replace_peers=true\n");
|
||||||
|
|
||||||
|
for_each_wgpeer(dev, peer, i) {
|
||||||
|
key_to_hex(hex, peer->public_key);
|
||||||
|
fprintf(f, "public_key=%s\n", hex);
|
||||||
|
if (peer->flags & WGPEER_REMOVE_ME) {
|
||||||
|
fprintf(f, "remove=true\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (peer->flags & WGPEER_REMOVE_PRESHARED_KEY)
|
||||||
|
fprintf(f, "preshared_key=\n");
|
||||||
|
else if (memcmp(peer->preshared_key, zero, WG_KEY_LEN)) {
|
||||||
|
key_to_hex(hex, peer->preshared_key);
|
||||||
|
fprintf(f, "preshared_key=%s\n", hex);
|
||||||
|
}
|
||||||
|
if (peer->endpoint.addr.sa_family == AF_INET || peer->endpoint.addr.sa_family == AF_INET6) {
|
||||||
|
addr_len = 0;
|
||||||
|
if (peer->endpoint.addr.sa_family == AF_INET)
|
||||||
|
addr_len = sizeof(struct sockaddr_in);
|
||||||
|
else if (peer->endpoint.addr.sa_family == AF_INET6)
|
||||||
|
addr_len = sizeof(struct sockaddr_in6);
|
||||||
|
if (!getnameinfo(&peer->endpoint.addr, addr_len, host, sizeof(host), service, sizeof(service), NI_DGRAM | NI_NUMERICSERV | NI_NUMERICHOST)) {
|
||||||
|
if (peer->endpoint.addr.sa_family == AF_INET6 && strchr(host, ':'))
|
||||||
|
fprintf(f, "endpoint=[%s]:%s\n", host, service);
|
||||||
|
else
|
||||||
|
fprintf(f, "endpoint=%s:%s\n", host, service);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (peer->persistent_keepalive_interval != (uint16_t)-1)
|
||||||
|
fprintf(f, "persistent_keepalive_interval=%u\n", peer->persistent_keepalive_interval);
|
||||||
|
if (peer->flags & WGPEER_REPLACE_IPMASKS)
|
||||||
|
fprintf(f, "replace_allowed_ips=true\n");
|
||||||
|
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;
|
||||||
|
fprintf(f, "allowed_ip=%s/%d\n", ip, ipmask->cidr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fprintf(f, "\n");
|
||||||
|
fflush(f);
|
||||||
|
|
||||||
|
if (fscanf(f, "errno=%d\n\n", &ret) != 1)
|
||||||
|
ret = errno ? -errno : -EPROTO;
|
||||||
|
fclose(f);
|
||||||
errno = -ret;
|
errno = -ret;
|
||||||
return check_version_magic(dev, ret);
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define READ_BYTES(bytes) ({ \
|
#define ADD(bytes) ({ \
|
||||||
void *__p; \
|
if (buffer_len - buffer_end < bytes) { \
|
||||||
size_t __bytes = (bytes); \
|
ptrdiff_t peer_offset = (void *)peer - (void *)*out; \
|
||||||
if (bytes_left < __bytes) { \
|
buffer_len = buffer_len * 2 + bytes; \
|
||||||
offset = p - buffer; \
|
*out = realloc(*out, buffer_len); \
|
||||||
bytes_left += buffer_size; \
|
if (!*out) { \
|
||||||
buffer_size *= 2; \
|
ret = -errno; \
|
||||||
ret = -ENOMEM; \
|
goto err; \
|
||||||
p = realloc(buffer, buffer_size); \
|
} \
|
||||||
if (!p) \
|
memset((void *)*out + buffer_end, 0, buffer_len - buffer_end); \
|
||||||
goto out; \
|
if (peer) \
|
||||||
buffer = p; \
|
peer = (void *)*out + peer_offset; \
|
||||||
p += offset; \
|
dev = *out; \
|
||||||
} \
|
} \
|
||||||
bytes_left -= __bytes; \
|
buffer_end += bytes; \
|
||||||
ret = read(fd, p, __bytes); \
|
(void *)*out + buffer_end - bytes; \
|
||||||
if (ret < 0) \
|
|
||||||
goto out; \
|
|
||||||
if ((size_t)ret != __bytes) { \
|
|
||||||
ret = -EBADMSG; \
|
|
||||||
goto out; \
|
|
||||||
} \
|
|
||||||
__p = p; \
|
|
||||||
p += __bytes; \
|
|
||||||
__p; \
|
|
||||||
})
|
})
|
||||||
static int userspace_get_device(struct wgdevice **dev, const char *interface)
|
|
||||||
|
#define NUM(max) ({ \
|
||||||
|
unsigned long long num; \
|
||||||
|
char *end; \
|
||||||
|
if (!strlen(value)) \
|
||||||
|
break; \
|
||||||
|
num = strtoull(value, &end, 10); \
|
||||||
|
if (*end || num > max) \
|
||||||
|
break; \
|
||||||
|
num; \
|
||||||
|
})
|
||||||
|
|
||||||
|
static int userspace_get_device(struct wgdevice **out, const char *interface)
|
||||||
{
|
{
|
||||||
unsigned int len = 0, i;
|
struct wgdevice *dev;
|
||||||
size_t buffer_size, bytes_left;
|
struct wgpeer *peer = NULL;
|
||||||
ssize_t ret;
|
size_t buffer_len = 0, buffer_end = 0, line_buffer_len = 0, line_len;
|
||||||
ptrdiff_t offset;
|
char *key = NULL, *value;
|
||||||
uint8_t *buffer = NULL, *p, byte = 0;
|
FILE *f;
|
||||||
|
int ret = -EPROTO;
|
||||||
|
|
||||||
int fd = userspace_interface_fd(interface);
|
f = userspace_interface_file(interface);
|
||||||
if (fd < 0)
|
if (!f)
|
||||||
return fd;
|
return -errno;
|
||||||
|
|
||||||
ret = write(fd, &byte, sizeof(byte));
|
fprintf(f, "get=1\n\n");
|
||||||
if (ret < 0)
|
fflush(f);
|
||||||
goto out;
|
|
||||||
if (ret != sizeof(byte)) {
|
*out = NULL;
|
||||||
ret = -EBADMSG;
|
dev = ADD(sizeof(struct wgdevice));
|
||||||
goto out;
|
dev->version_magic = WG_API_VERSION_MAGIC;
|
||||||
|
strncpy(dev->interface, interface, IFNAMSIZ - 1);
|
||||||
|
dev->interface[IFNAMSIZ - 1] = '\0';
|
||||||
|
|
||||||
|
while (getline(&key, &line_buffer_len, f) > 0) {
|
||||||
|
line_len = strlen(key);
|
||||||
|
if (line_len == 1 && key[0] == '\n') {
|
||||||
|
free(key);
|
||||||
|
fclose(f);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
value = strchr(key, '=');
|
||||||
|
if (!value || line_len == 0 || key[line_len - 1] != '\n')
|
||||||
|
break;
|
||||||
|
*value++ = key[--line_len] = '\0';
|
||||||
|
|
||||||
|
if (!strcmp(key, "private_key")) {
|
||||||
|
if (!key_from_hex(dev->private_key, value))
|
||||||
|
break;
|
||||||
|
curve25519_generate_public(dev->public_key, dev->private_key);
|
||||||
|
} else if (!strcmp(key, "listen_port"))
|
||||||
|
dev->port = NUM(0xffffU);
|
||||||
|
else if (!strcmp(key, "fwmark"))
|
||||||
|
dev->fwmark = NUM(0xffffffffU);
|
||||||
|
else if (!strcmp(key, "public_key")) {
|
||||||
|
peer = ADD(sizeof(struct wgpeer));
|
||||||
|
if (!key_from_hex(peer->public_key, value))
|
||||||
|
break;
|
||||||
|
++dev->num_peers;
|
||||||
|
} else if (peer && !strcmp(key, "preshared_key")) {
|
||||||
|
if (!key_from_hex(peer->preshared_key, value))
|
||||||
|
break;
|
||||||
|
} else if (peer && !strcmp(key, "endpoint")) {
|
||||||
|
char *begin, *end;
|
||||||
|
struct addrinfo *resolved;
|
||||||
|
struct addrinfo hints = {
|
||||||
|
.ai_family = AF_UNSPEC,
|
||||||
|
.ai_socktype = SOCK_DGRAM,
|
||||||
|
.ai_protocol = IPPROTO_UDP
|
||||||
|
};
|
||||||
|
if (!strlen(value))
|
||||||
|
break;
|
||||||
|
if (value[0] == '[') {
|
||||||
|
begin = &value[1];
|
||||||
|
end = strchr(value, ']');
|
||||||
|
if (!end)
|
||||||
|
break;
|
||||||
|
*end++ = '\0';
|
||||||
|
if (*end++ != ':' || !*end)
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
begin = value;
|
||||||
|
end = strrchr(value, ':');
|
||||||
|
if (!end || !*(end + 1))
|
||||||
|
break;
|
||||||
|
*end++ = '\0';
|
||||||
|
}
|
||||||
|
if (getaddrinfo(begin, end, &hints, &resolved) != 0) {
|
||||||
|
errno = ENETUNREACH;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
if ((resolved->ai_family == AF_INET && resolved->ai_addrlen == sizeof(struct sockaddr_in)) ||
|
||||||
|
(resolved->ai_family == AF_INET6 && resolved->ai_addrlen == sizeof(struct sockaddr_in6)))
|
||||||
|
memcpy(&peer->endpoint.addr, resolved->ai_addr, resolved->ai_addrlen);
|
||||||
|
else {
|
||||||
|
freeaddrinfo(resolved);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
freeaddrinfo(resolved);
|
||||||
|
} else if (peer && !strcmp(key, "persistent_keepalive_interval"))
|
||||||
|
peer->persistent_keepalive_interval = NUM(65535U);
|
||||||
|
else if (peer && !strcmp(key, "allowed_ip")) {
|
||||||
|
struct wgipmask *ipmask = ADD(sizeof(struct wgipmask));
|
||||||
|
char *end, *cidr = strchr(value, '/');
|
||||||
|
if (!cidr || strlen(cidr) <= 1)
|
||||||
|
break;
|
||||||
|
*cidr++ = '\0';
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
ipmask->cidr = strtoul(cidr, &end, 10);
|
||||||
|
if (*end || ipmask->family == AF_UNSPEC || (ipmask->family == AF_INET6 && ipmask->cidr > 128) || (ipmask->family == AF_INET && ipmask->cidr > 32))
|
||||||
|
break;
|
||||||
|
++peer->num_ipmasks;
|
||||||
|
} else if (peer && !strcmp(key, "last_handshake_time_sec"))
|
||||||
|
peer->last_handshake_time.tv_sec = NUM(0xffffffffffffffffULL);
|
||||||
|
else if (peer && !strcmp(key, "last_handshake_time_nsec"))
|
||||||
|
peer->last_handshake_time.tv_usec = NUM(0xffffffffffffffffULL) / 1000;
|
||||||
|
else if (peer && !strcmp(key, "rx_bytes"))
|
||||||
|
peer->rx_bytes = NUM(0xffffffffffffffffULL);
|
||||||
|
else if (peer && !strcmp(key, "tx_bytes"))
|
||||||
|
peer->tx_bytes = NUM(0xffffffffffffffffULL);
|
||||||
|
else if (!strcmp(key, "errno"))
|
||||||
|
ret = -NUM(0x7fffffffU);
|
||||||
|
else
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
ret = -EPROTO;
|
||||||
ioctl(fd, FIONREAD, &len);
|
err:
|
||||||
bytes_left = buffer_size = max(len, sizeof(struct wgdevice) + sizeof(struct wgpeer) + sizeof(struct wgipmask));
|
free(key);
|
||||||
p = buffer = malloc(buffer_size);
|
free(*out);
|
||||||
ret = -ENOMEM;
|
*out = NULL;
|
||||||
if (!buffer)
|
fclose(f);
|
||||||
goto out;
|
|
||||||
|
|
||||||
len = ((struct wgdevice *)READ_BYTES(sizeof(struct wgdevice)))->num_peers;
|
|
||||||
ret = check_version_magic((struct wgdevice *)buffer, ret);
|
|
||||||
if (ret)
|
|
||||||
goto out;
|
|
||||||
for (i = 0; i < len; ++i)
|
|
||||||
READ_BYTES(sizeof(struct wgipmask) * ((struct wgpeer *)READ_BYTES(sizeof(struct wgpeer)))->num_ipmasks);
|
|
||||||
ret = 0;
|
|
||||||
out:
|
|
||||||
if (buffer && ret) {
|
|
||||||
free(buffer);
|
|
||||||
buffer = NULL;
|
|
||||||
}
|
|
||||||
*dev = (struct wgdevice *)buffer;
|
|
||||||
close(fd);
|
|
||||||
errno = -ret;
|
errno = -ret;
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
}
|
}
|
||||||
#undef READ_BYTES
|
#undef ADD
|
||||||
|
#undef NUM
|
||||||
|
#undef KEY
|
||||||
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
|
static int check_version_magic(struct wgdevice *device, int ret)
|
||||||
|
{
|
||||||
|
if (ret == -EPROTO || (!ret && device->version_magic != WG_API_VERSION_MAGIC)) {
|
||||||
|
fprintf(stderr, "This program was built for a different version of WireGuard than\nwhat is currently running. Either this version of wg(8) is out\nof date, or the currently loaded WireGuard module is out of date.\nIf you have just updated your WireGuard installation, you may have\nforgotten to unload the previous running WireGuard module. Try\nrunning `rmmod wireguard` as root, and then try re-adding the interface\nand trying again.\n\n");
|
||||||
|
errno = EPROTO;
|
||||||
|
return -EPROTO;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
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;
|
||||||
|
@ -295,9 +447,11 @@ static int parse_infomsg(const struct nlattr *attr, void *data)
|
||||||
static int read_devices_cb(const struct nlmsghdr *nlh, void *data)
|
static int read_devices_cb(const struct nlmsghdr *nlh, void *data)
|
||||||
{
|
{
|
||||||
struct inflatable_buffer *buffer = data;
|
struct inflatable_buffer *buffer = data;
|
||||||
|
int ret;
|
||||||
|
|
||||||
buffer->good = false;
|
buffer->good = false;
|
||||||
buffer->next = NULL;
|
buffer->next = NULL;
|
||||||
int ret = mnl_attr_parse(nlh, sizeof(struct ifinfomsg), parse_infomsg, data);
|
ret = mnl_attr_parse(nlh, sizeof(struct ifinfomsg), parse_infomsg, data);
|
||||||
if (ret != MNL_CB_OK)
|
if (ret != MNL_CB_OK)
|
||||||
return ret;
|
return ret;
|
||||||
ret = add_next_to_inflatable_buffer(buffer);
|
ret = add_next_to_inflatable_buffer(buffer);
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
|
||||||
#include "curve25519.h"
|
#include "curve25519.h"
|
||||||
#include "base64.h"
|
#include "encoding.h"
|
||||||
#include "subcommands.h"
|
#include "subcommands.h"
|
||||||
|
|
||||||
int pubkey_main(int argc, char *argv[])
|
int pubkey_main(int argc, char *argv[])
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
#include "ipc.h"
|
#include "ipc.h"
|
||||||
#include "subcommands.h"
|
#include "subcommands.h"
|
||||||
#include "terminal.h"
|
#include "terminal.h"
|
||||||
#include "base64.h"
|
#include "encoding.h"
|
||||||
#include "../uapi.h"
|
#include "../uapi.h"
|
||||||
|
|
||||||
static int peer_cmp(const void *first, const void *second)
|
static int peer_cmp(const void *first, const void *second)
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
#include <netdb.h>
|
#include <netdb.h>
|
||||||
|
|
||||||
#include "subcommands.h"
|
#include "subcommands.h"
|
||||||
#include "base64.h"
|
#include "encoding.h"
|
||||||
#include "ipc.h"
|
#include "ipc.h"
|
||||||
#include "../uapi.h"
|
#include "../uapi.h"
|
||||||
|
|
||||||
|
@ -79,16 +79,16 @@ int showconf_main(int argc, char *argv[])
|
||||||
if (peer->endpoint.addr.sa_family == AF_INET || peer->endpoint.addr.sa_family == AF_INET6) {
|
if (peer->endpoint.addr.sa_family == AF_INET || peer->endpoint.addr.sa_family == AF_INET6) {
|
||||||
char host[4096 + 1];
|
char host[4096 + 1];
|
||||||
char service[512 + 1];
|
char service[512 + 1];
|
||||||
static char buf[sizeof(host) + sizeof(service) + 4];
|
|
||||||
socklen_t addr_len = 0;
|
socklen_t addr_len = 0;
|
||||||
memset(buf, 0, sizeof(buf));
|
|
||||||
if (peer->endpoint.addr.sa_family == AF_INET)
|
if (peer->endpoint.addr.sa_family == AF_INET)
|
||||||
addr_len = sizeof(struct sockaddr_in);
|
addr_len = sizeof(struct sockaddr_in);
|
||||||
else if (peer->endpoint.addr.sa_family == AF_INET6)
|
else if (peer->endpoint.addr.sa_family == AF_INET6)
|
||||||
addr_len = sizeof(struct sockaddr_in6);
|
addr_len = sizeof(struct sockaddr_in6);
|
||||||
if (!getnameinfo(&peer->endpoint.addr, addr_len, host, sizeof(host), service, sizeof(service), NI_DGRAM | NI_NUMERICSERV | NI_NUMERICHOST)) {
|
if (!getnameinfo(&peer->endpoint.addr, addr_len, host, sizeof(host), service, sizeof(service), NI_DGRAM | NI_NUMERICSERV | NI_NUMERICHOST)) {
|
||||||
snprintf(buf, sizeof(buf) - 1, (peer->endpoint.addr.sa_family == AF_INET6 && strchr(host, ':')) ? "[%s]:%s" : "%s:%s", host, service);
|
if (peer->endpoint.addr.sa_family == AF_INET6 && strchr(host, ':'))
|
||||||
printf("Endpoint = %s\n", buf);
|
printf("Endpoint = [%s]:%s\n", host, service);
|
||||||
|
else
|
||||||
|
printf("Endpoint = %s:%s\n", host, service);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue