feat: first batch of awg-1.5

This commit is contained in:
Mark Puha 2025-06-12 20:15:01 +02:00
parent c0b400c6df
commit 71219488c1
6 changed files with 990 additions and 632 deletions

View file

@ -57,6 +57,15 @@ enum wgdevice_attribute {
WGDEVICE_A_H2, WGDEVICE_A_H2,
WGDEVICE_A_H3, WGDEVICE_A_H3,
WGDEVICE_A_H4, WGDEVICE_A_H4,
WGDEVICE_A_I1,
WGDEVICE_A_I2,
WGDEVICE_A_I3,
WGDEVICE_A_I4,
WGDEVICE_A_I5,
WGDEVICE_A_J1,
WGDEVICE_A_J2,
WGDEVICE_A_J3,
WGDEVICE_A_ITIME,
__WGDEVICE_A_LAST __WGDEVICE_A_LAST
}; };

View file

@ -410,6 +410,22 @@ err:
return false; return false;
} }
static inline bool parse_string(char **device_value, const char *name, const char *value) {
size_t len = strlen(value);
if (!len) {
fprintf(stderr, "Unable to parse empty string for: %s\n", name);
return false;
}
if( len >= MAX_AWG_JUNK_LEN) {
fprintf(stderr, "Unable to process hex string longer than: %d\n", MAX_AWG_JUNK_LEN);
return false;
}
*device_value = strdup(value);
return true;
}
static inline bool parse_uint16(uint16_t *device_value, const char *name, const char *value) { static inline bool parse_uint16(uint16_t *device_value, const char *name, const char *value) {
if (!strlen(value)) { if (!strlen(value)) {
@ -558,6 +574,42 @@ static bool process_line(struct config_ctx *ctx, const char *line)
ret = parse_uint32(&ctx->device->transport_packet_magic_header, "H4", value); ret = parse_uint32(&ctx->device->transport_packet_magic_header, "H4", value);
if (ret) if (ret)
ctx->device->flags |= WGDEVICE_HAS_H4; ctx->device->flags |= WGDEVICE_HAS_H4;
} else if (key_match("I1")) {
ret = parse_string(&ctx->device->i1, "I1", value);
if (ret)
ctx->device->flags |= WGDEVICE_HAS_I1;
} else if (key_match("I2")) {
ret = parse_string(&ctx->device->i2, "I2", value);
if (ret)
ctx->device->flags |= WGDEVICE_HAS_I2;
} else if (key_match("I3")) {
ret = parse_string(&ctx->device->i3, "I3", value);
if (ret)
ctx->device->flags |= WGDEVICE_HAS_I3;
} else if (key_match("I4")) {
ret = parse_string(&ctx->device->i4, "I4", value);
if (ret)
ctx->device->flags |= WGDEVICE_HAS_I4;
} else if (key_match("I5")) {
ret = parse_string(&ctx->device->i5, "I5", value);
if (ret)
ctx->device->flags |= WGDEVICE_HAS_I5;
} else if (key_match("J1")) {
ret = parse_string(&ctx->device->j1, "J1", value);
if (ret)
ctx->device->flags |= WGDEVICE_HAS_J1;
} else if (key_match("J2")) {
ret = parse_string(&ctx->device->j2, "J2", value);
if (ret)
ctx->device->flags |= WGDEVICE_HAS_J2;
} else if (key_match("J3")) {
ret = parse_string(&ctx->device->j3, "J3", value);
if (ret)
ctx->device->flags |= WGDEVICE_HAS_J3;
} else if (key_match("ITIME")) {
ret = parse_string(&ctx->device->itime, "Itime", value);
if (ret)
ctx->device->flags |= WGDEVICE_HAS_ITIME;
} else } else
goto error; goto error;
} else if (ctx->is_peer_section) { } else if (ctx->is_peer_section) {
@ -763,6 +815,69 @@ struct wgdevice *config_read_cmd(const char *argv[], int argc)
device->flags |= WGDEVICE_HAS_H4; device->flags |= WGDEVICE_HAS_H4;
argv += 2; argv += 2;
argc -= 2; argc -= 2;
} else if (!strcmp(argv[0], "i1") && argc >= 2 && !peer) {
if (!parse_string(&device->i1, "i1", argv[1]))
goto error;
device->flags |= WGDEVICE_HAS_I1;
argv += 2;
argc -= 2;
} else if (!strcmp(argv[0], "i2") && argc >= 2 && !peer) {
if (!parse_string(&device->i2, "i2", argv[1]))
goto error;
device->flags |= WGDEVICE_HAS_I2;
argv += 2;
argc -=2;
} else if (!strcmp(argv[0], "i3") && argc >= 2 && !peer) {
if (!parse_string(&device->i3, "i3", argv[1]))
goto error;
device->flags |= WGDEVICE_HAS_I3;
argv += 2;
argc -=2;
} else if (!strcmp(argv[0], "i4") && argc >= 2 && !peer) {
if (!parse_string(&device->i4, "i4", argv[1]))
goto error;
device->flags |= WGDEVICE_HAS_I4;
argv += 2;
argc -=2;
} else if (!strcmp(argv[0], "i5") && argc >= 2 && !peer) {
if (!parse_string(&device->i5, "i5", argv[1]))
goto error;
device->flags |= WGDEVICE_HAS_I5;
argv += 2;
argc -=2;
} else if (!strcmp(argv[0], "j1") && argc >= 2 && !peer) {
if (!parse_string(&device->j1, "j1", argv[1]))
goto error;
device->flags |= WGDEVICE_HAS_J1;
argv += 2;
argc -=2;
} else if (!strcmp(argv[0], "j2") && argc >= 2 && !peer) {
if (!parse_string(&device->j2, "j2", argv[1]))
goto error;
device->flags |= WGDEVICE_HAS_J2;
argv += 2;
argc -=2;
} else if (!strcmp(argv[0], "j3") && argc >= 2 && !peer) {
if (!parse_string(&device->j3, "j3", argv[1]))
goto error;
device->flags |= WGDEVICE_HAS_J3;
argv += 2;
argc -=2;
} else if (!strcmp(argv[0], "itime") && argc >= 2 && !peer) {
if (!parse_string(&device->itime, "itime", argv[1]))
goto error;
device->flags |= WGDEVICE_HAS_ITIME;
argv += 2;
argc -=2;
} else if (!strcmp(argv[0], "peer") && argc >= 2) { } else if (!strcmp(argv[0], "peer") && argc >= 2) {
struct wgpeer *new_peer = calloc(1, sizeof(*new_peer)); struct wgpeer *new_peer = calloc(1, sizeof(*new_peer));

View file

@ -6,13 +6,13 @@
#ifndef CONTAINERS_H #ifndef CONTAINERS_H
#define CONTAINERS_H #define CONTAINERS_H
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <time.h>
#include <sys/socket.h>
#include <net/if.h> #include <net/if.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <time.h>
#if defined(__linux__) #if defined(__linux__)
#include <linux/wireguard.h> #include <linux/wireguard.h>
#elif defined(__OpenBSD__) #elif defined(__OpenBSD__)
@ -23,23 +23,31 @@
#define WG_KEY_LEN 32 #define WG_KEY_LEN 32
#endif #endif
#ifndef MAX_AWG_JUNK_LEN
#define MAX_AWG_JUNK_LEN 5 * 1024
#endif
/* Cross platform __kernel_timespec */ /* Cross platform __kernel_timespec */
struct timespec64 { struct timespec64
{
int64_t tv_sec; int64_t tv_sec;
int64_t tv_nsec; int64_t tv_nsec;
}; };
struct wgallowedip { struct wgallowedip
{
uint16_t family; uint16_t family;
union { union
{
struct in_addr ip4; struct in_addr ip4;
struct in6_addr ip6; struct in6_addr ip6;
}; };
uint8_t cidr; uint8_t cidr;
struct wgallowedip *next_allowedip; struct wgallowedip* next_allowedip;
}; };
enum { enum
{
WGPEER_REMOVE_ME = 1U << 0, WGPEER_REMOVE_ME = 1U << 0,
WGPEER_REPLACE_ALLOWEDIPS = 1U << 1, WGPEER_REPLACE_ALLOWEDIPS = 1U << 1,
WGPEER_HAS_PUBLIC_KEY = 1U << 2, WGPEER_HAS_PUBLIC_KEY = 1U << 2,
@ -48,13 +56,15 @@ enum {
WGPEER_HAS_ADVANCED_SECURITY = 1U << 5 WGPEER_HAS_ADVANCED_SECURITY = 1U << 5
}; };
struct wgpeer { struct wgpeer
{
uint32_t flags; uint32_t flags;
uint8_t public_key[WG_KEY_LEN]; uint8_t public_key[WG_KEY_LEN];
uint8_t preshared_key[WG_KEY_LEN]; uint8_t preshared_key[WG_KEY_LEN];
union { union
{
struct sockaddr addr; struct sockaddr addr;
struct sockaddr_in addr4; struct sockaddr_in addr4;
struct sockaddr_in6 addr6; struct sockaddr_in6 addr6;
@ -67,10 +77,11 @@ struct wgpeer {
bool advanced_security; bool advanced_security;
struct wgallowedip *first_allowedip, *last_allowedip; struct wgallowedip *first_allowedip, *last_allowedip;
struct wgpeer *next_peer; struct wgpeer* next_peer;
}; };
enum { enum
{
WGDEVICE_REPLACE_PEERS = 1U << 0, WGDEVICE_REPLACE_PEERS = 1U << 0,
WGDEVICE_HAS_PRIVATE_KEY = 1U << 1, WGDEVICE_HAS_PRIVATE_KEY = 1U << 1,
WGDEVICE_HAS_PUBLIC_KEY = 1U << 2, WGDEVICE_HAS_PUBLIC_KEY = 1U << 2,
@ -84,10 +95,20 @@ enum {
WGDEVICE_HAS_H1 = 1U << 10, WGDEVICE_HAS_H1 = 1U << 10,
WGDEVICE_HAS_H2 = 1U << 11, WGDEVICE_HAS_H2 = 1U << 11,
WGDEVICE_HAS_H3 = 1U << 12, WGDEVICE_HAS_H3 = 1U << 12,
WGDEVICE_HAS_H4 = 1U << 13 WGDEVICE_HAS_H4 = 1U << 13,
WGDEVICE_HAS_I1 = 1U << 14,
WGDEVICE_HAS_I2 = 1U << 15,
WGDEVICE_HAS_I3 = 1U << 16,
WGDEVICE_HAS_I4 = 1U << 17,
WGDEVICE_HAS_I5 = 1U << 18,
WGDEVICE_HAS_J1 = 1U << 19,
WGDEVICE_HAS_J2 = 1U << 20,
WGDEVICE_HAS_J3 = 1U << 21,
WGDEVICE_HAS_ITIME = 1U << 22
}; };
struct wgdevice { struct wgdevice
{
char name[IFNAMSIZ]; char name[IFNAMSIZ];
uint32_t ifindex; uint32_t ifindex;
@ -110,17 +131,34 @@ struct wgdevice {
uint32_t response_packet_magic_header; uint32_t response_packet_magic_header;
uint32_t underload_packet_magic_header; uint32_t underload_packet_magic_header;
uint32_t transport_packet_magic_header; uint32_t transport_packet_magic_header;
char* i1;
char* i2;
char* i3;
char* i4;
char* i5;
char* j1;
char* j2;
char* j3;
char* itime;
}; };
#define for_each_wgpeer(__dev, __peer) for ((__peer) = (__dev)->first_peer; (__peer); (__peer) = (__peer)->next_peer) #define for_each_wgpeer(__dev, __peer) \
#define for_each_wgallowedip(__peer, __allowedip) for ((__allowedip) = (__peer)->first_allowedip; (__allowedip); (__allowedip) = (__allowedip)->next_allowedip) for ((__peer) = (__dev)->first_peer; (__peer); (__peer) = (__peer)->next_peer)
#define for_each_wgallowedip(__peer, __allowedip) \
for ((__allowedip) = (__peer)->first_allowedip; (__allowedip); \
(__allowedip) = (__allowedip)->next_allowedip)
static inline void free_wgdevice(struct wgdevice *dev) static inline void free_wgdevice(struct wgdevice* dev)
{ {
if (!dev) if (!dev)
return; return;
for (struct wgpeer *peer = dev->first_peer, *np = peer ? peer->next_peer : NULL; peer; peer = np, np = peer ? peer->next_peer : NULL) { for (struct wgpeer *peer = dev->first_peer, *np = peer ? peer->next_peer : NULL; peer;
for (struct wgallowedip *allowedip = peer->first_allowedip, *na = allowedip ? allowedip->next_allowedip : NULL; allowedip; allowedip = na, na = allowedip ? allowedip->next_allowedip : NULL) peer = np, np = peer ? peer->next_peer : NULL)
{
for (struct wgallowedip *allowedip = peer->first_allowedip,
*na = allowedip ? allowedip->next_allowedip : NULL;
allowedip;
allowedip = na, na = allowedip ? allowedip->next_allowedip : NULL)
free(allowedip); free(allowedip);
free(peer); free(peer);
} }

View file

@ -3,47 +3,49 @@
* Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
*/ */
#include <stdbool.h> #include "containers.h"
#include <stddef.h> #include "encoding.h"
#include <stdint.h> #include "netlink.h"
#include <stdio.h>
#include <stdlib.h>
#include <errno.h> #include <errno.h>
#include <string.h>
#include <time.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <linux/genetlink.h> #include <linux/genetlink.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>
#include <linux/wireguard.h> #include <linux/wireguard.h>
#include <netinet/in.h> #include <netinet/in.h>
#include "containers.h" #include <stdbool.h>
#include "encoding.h" #include <stddef.h>
#include "netlink.h" #include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <time.h>
#define IPC_SUPPORTS_KERNEL_INTERFACE #define IPC_SUPPORTS_KERNEL_INTERFACE
#define SOCKET_BUFFER_SIZE (mnl_ideal_socket_buffer_size()) #define SOCKET_BUFFER_SIZE (mnl_ideal_socket_buffer_size())
struct interface { struct interface
const char *name; {
const char* name;
bool is_wireguard; bool is_wireguard;
}; };
static int parse_linkinfo(const struct nlattr *attr, void *data) static int parse_linkinfo(const struct nlattr* attr, void* data)
{ {
struct interface *interface = data; struct interface* interface = data;
if (mnl_attr_get_type(attr) == IFLA_INFO_KIND && !strcmp(WG_GENL_NAME, mnl_attr_get_str(attr))) if (mnl_attr_get_type(attr) == IFLA_INFO_KIND &&
!strcmp(WG_GENL_NAME, mnl_attr_get_str(attr)))
interface->is_wireguard = true; interface->is_wireguard = true;
return MNL_CB_OK; return MNL_CB_OK;
} }
static int parse_infomsg(const struct nlattr *attr, void *data) static int parse_infomsg(const struct nlattr* attr, void* data)
{ {
struct interface *interface = data; struct interface* interface = data;
if (mnl_attr_get_type(attr) == IFLA_LINKINFO) if (mnl_attr_get_type(attr) == IFLA_LINKINFO)
return mnl_attr_parse_nested(attr, parse_linkinfo, data); return mnl_attr_parse_nested(attr, parse_linkinfo, data);
@ -52,10 +54,10 @@ static int parse_infomsg(const struct nlattr *attr, void *data)
return MNL_CB_OK; return MNL_CB_OK;
} }
static int read_devices_cb(const struct nlmsghdr *nlh, void *data) static int read_devices_cb(const struct nlmsghdr* nlh, void* data)
{ {
struct string_list *list = data; struct string_list* list = data;
struct interface interface = { 0 }; struct interface interface = {0};
int ret; int ret;
ret = mnl_attr_parse(nlh, sizeof(struct ifinfomsg), parse_infomsg, &interface); ret = mnl_attr_parse(nlh, sizeof(struct ifinfomsg), parse_infomsg, &interface);
@ -70,16 +72,16 @@ static int read_devices_cb(const struct nlmsghdr *nlh, void *data)
return MNL_CB_OK; return MNL_CB_OK;
} }
static int kernel_get_wireguard_interfaces(struct string_list *list) static int kernel_get_wireguard_interfaces(struct string_list* list)
{ {
struct mnl_socket *nl = NULL; struct mnl_socket* nl = NULL;
char *rtnl_buffer = NULL; char* rtnl_buffer = NULL;
size_t message_len; size_t message_len;
unsigned int portid, seq; unsigned int portid, seq;
ssize_t len; ssize_t len;
int ret = 0; int ret = 0;
struct nlmsghdr *nlh; struct nlmsghdr* nlh;
struct ifinfomsg *ifm; struct ifinfomsg* ifm;
ret = -ENOMEM; ret = -ENOMEM;
rtnl_buffer = calloc(SOCKET_BUFFER_SIZE, 1); rtnl_buffer = calloc(SOCKET_BUFFER_SIZE, 1);
@ -87,12 +89,14 @@ static int kernel_get_wireguard_interfaces(struct string_list *list)
goto cleanup; goto cleanup;
nl = mnl_socket_open(NETLINK_ROUTE); nl = mnl_socket_open(NETLINK_ROUTE);
if (!nl) { if (!nl)
{
ret = -errno; ret = -errno;
goto cleanup; goto cleanup;
} }
if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0)
{
ret = -errno; ret = -errno;
goto cleanup; goto cleanup;
} }
@ -107,23 +111,27 @@ static int kernel_get_wireguard_interfaces(struct string_list *list)
ifm->ifi_family = AF_UNSPEC; ifm->ifi_family = AF_UNSPEC;
message_len = nlh->nlmsg_len; message_len = nlh->nlmsg_len;
if (mnl_socket_sendto(nl, rtnl_buffer, message_len) < 0) { if (mnl_socket_sendto(nl, rtnl_buffer, message_len) < 0)
{
ret = -errno; ret = -errno;
goto cleanup; goto cleanup;
} }
another: another:
if ((len = mnl_socket_recvfrom(nl, rtnl_buffer, SOCKET_BUFFER_SIZE)) < 0) { if ((len = mnl_socket_recvfrom(nl, rtnl_buffer, SOCKET_BUFFER_SIZE)) < 0)
{
ret = -errno; ret = -errno;
goto cleanup; goto cleanup;
} }
if ((len = mnl_cb_run(rtnl_buffer, len, seq, portid, read_devices_cb, list)) < 0) { if ((len = mnl_cb_run(rtnl_buffer, len, seq, portid, read_devices_cb, list)) < 0)
{
/* Netlink returns NLM_F_DUMP_INTR if the set of all tunnels changed /* Netlink returns NLM_F_DUMP_INTR if the set of all tunnels changed
* during the dump. That's unfortunate, but is pretty common on busy * during the dump. That's unfortunate, but is pretty common on busy
* systems that are adding and removing tunnels all the time. Rather * systems that are adding and removing tunnels all the time. Rather
* than retrying, potentially indefinitely, we just work with the * than retrying, potentially indefinitely, we just work with the
* partial results. */ * partial results. */
if (errno != EINTR) { if (errno != EINTR)
{
ret = -errno; ret = -errno;
goto cleanup; goto cleanup;
} }
@ -139,14 +147,14 @@ cleanup:
return ret; return ret;
} }
static int kernel_set_device(struct wgdevice *dev) static int kernel_set_device(struct wgdevice* dev)
{ {
int ret = 0; int ret = 0;
struct wgpeer *peer = NULL; struct wgpeer* peer = NULL;
struct wgallowedip *allowedip = NULL; struct wgallowedip* allowedip = NULL;
struct nlattr *peers_nest, *peer_nest, *allowedips_nest, *allowedip_nest; struct nlattr * peers_nest, *peer_nest, *allowedips_nest, *allowedip_nest;
struct nlmsghdr *nlh; struct nlmsghdr* nlh;
struct mnlg_socket *nlg; struct mnlg_socket* nlg;
nlg = mnlg_socket_open(WG_GENL_NAME, WG_GENL_VERSION); nlg = mnlg_socket_open(WG_GENL_NAME, WG_GENL_VERSION);
if (!nlg) if (!nlg)
@ -156,11 +164,13 @@ again:
nlh = mnlg_msg_prepare(nlg, WG_CMD_SET_DEVICE, NLM_F_REQUEST | NLM_F_ACK); nlh = mnlg_msg_prepare(nlg, WG_CMD_SET_DEVICE, NLM_F_REQUEST | NLM_F_ACK);
mnl_attr_put_strz(nlh, WGDEVICE_A_IFNAME, dev->name); mnl_attr_put_strz(nlh, WGDEVICE_A_IFNAME, dev->name);
if (!peer) { if (!peer)
{
uint32_t flags = 0; uint32_t flags = 0;
if (dev->flags & WGDEVICE_HAS_PRIVATE_KEY) if (dev->flags & WGDEVICE_HAS_PRIVATE_KEY)
mnl_attr_put(nlh, WGDEVICE_A_PRIVATE_KEY, sizeof(dev->private_key), dev->private_key); mnl_attr_put(
nlh, WGDEVICE_A_PRIVATE_KEY, sizeof(dev->private_key), dev->private_key);
if (dev->flags & WGDEVICE_HAS_LISTEN_PORT) if (dev->flags & WGDEVICE_HAS_LISTEN_PORT)
mnl_attr_put_u16(nlh, WGDEVICE_A_LISTEN_PORT, dev->listen_port); mnl_attr_put_u16(nlh, WGDEVICE_A_LISTEN_PORT, dev->listen_port);
if (dev->flags & WGDEVICE_HAS_JC) if (dev->flags & WGDEVICE_HAS_JC)
@ -192,64 +202,119 @@ again:
goto send; goto send;
peers_nest = peer_nest = allowedips_nest = allowedip_nest = NULL; peers_nest = peer_nest = allowedips_nest = allowedip_nest = NULL;
peers_nest = mnl_attr_nest_start(nlh, WGDEVICE_A_PEERS); peers_nest = mnl_attr_nest_start(nlh, WGDEVICE_A_PEERS);
for (peer = peer ? peer : dev->first_peer; peer; peer = peer->next_peer) { for (peer = peer ? peer : dev->first_peer; peer; peer = peer->next_peer)
{
uint32_t flags = 0; uint32_t flags = 0;
peer_nest = mnl_attr_nest_start_check(nlh, SOCKET_BUFFER_SIZE, 0); peer_nest = mnl_attr_nest_start_check(nlh, SOCKET_BUFFER_SIZE, 0);
if (!peer_nest) if (!peer_nest)
goto toobig_peers; goto toobig_peers;
if (!mnl_attr_put_check(nlh, SOCKET_BUFFER_SIZE, WGPEER_A_PUBLIC_KEY, sizeof(peer->public_key), peer->public_key)) if (!mnl_attr_put_check(
nlh,
SOCKET_BUFFER_SIZE,
WGPEER_A_PUBLIC_KEY,
sizeof(peer->public_key),
peer->public_key))
goto toobig_peers; goto toobig_peers;
if (peer->flags & WGPEER_REMOVE_ME) if (peer->flags & WGPEER_REMOVE_ME)
flags |= WGPEER_F_REMOVE_ME; flags |= WGPEER_F_REMOVE_ME;
if (!allowedip) { if (!allowedip)
{
if (peer->flags & WGPEER_REPLACE_ALLOWEDIPS) if (peer->flags & WGPEER_REPLACE_ALLOWEDIPS)
flags |= WGPEER_F_REPLACE_ALLOWEDIPS; flags |= WGPEER_F_REPLACE_ALLOWEDIPS;
if (peer->flags & WGPEER_HAS_PRESHARED_KEY) { if (peer->flags & WGPEER_HAS_PRESHARED_KEY)
if (!mnl_attr_put_check(nlh, SOCKET_BUFFER_SIZE, WGPEER_A_PRESHARED_KEY, sizeof(peer->preshared_key), peer->preshared_key)) {
if (!mnl_attr_put_check(
nlh,
SOCKET_BUFFER_SIZE,
WGPEER_A_PRESHARED_KEY,
sizeof(peer->preshared_key),
peer->preshared_key))
goto toobig_peers; goto toobig_peers;
} }
if (peer->endpoint.addr.sa_family == AF_INET) { if (peer->endpoint.addr.sa_family == AF_INET)
if (!mnl_attr_put_check(nlh, SOCKET_BUFFER_SIZE, WGPEER_A_ENDPOINT, sizeof(peer->endpoint.addr4), &peer->endpoint.addr4)) {
goto toobig_peers; if (!mnl_attr_put_check(
} else if (peer->endpoint.addr.sa_family == AF_INET6) { nlh,
if (!mnl_attr_put_check(nlh, SOCKET_BUFFER_SIZE, WGPEER_A_ENDPOINT, sizeof(peer->endpoint.addr6), &peer->endpoint.addr6)) SOCKET_BUFFER_SIZE,
WGPEER_A_ENDPOINT,
sizeof(peer->endpoint.addr4),
&peer->endpoint.addr4))
goto toobig_peers; goto toobig_peers;
} }
if (peer->flags & WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL) { else if (peer->endpoint.addr.sa_family == AF_INET6)
if (!mnl_attr_put_u16_check(nlh, SOCKET_BUFFER_SIZE, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, peer->persistent_keepalive_interval)) {
if (!mnl_attr_put_check(
nlh,
SOCKET_BUFFER_SIZE,
WGPEER_A_ENDPOINT,
sizeof(peer->endpoint.addr6),
&peer->endpoint.addr6))
goto toobig_peers;
}
if (peer->flags & WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL)
{
if (!mnl_attr_put_u16_check(
nlh,
SOCKET_BUFFER_SIZE,
WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL,
peer->persistent_keepalive_interval))
goto toobig_peers; goto toobig_peers;
} }
} }
if (peer->flags & WGPEER_HAS_ADVANCED_SECURITY) { if (peer->flags & WGPEER_HAS_ADVANCED_SECURITY)
{
if (peer->advanced_security) if (peer->advanced_security)
mnl_attr_put_check(nlh, SOCKET_BUFFER_SIZE, WGPEER_A_ADVANCED_SECURITY, 0, NULL); mnl_attr_put_check(
nlh, SOCKET_BUFFER_SIZE, WGPEER_A_ADVANCED_SECURITY, 0, NULL);
flags |= WGPEER_F_HAS_ADVANCED_SECURITY; flags |= WGPEER_F_HAS_ADVANCED_SECURITY;
} }
if (flags) { if (flags)
{
if (!mnl_attr_put_u32_check(nlh, SOCKET_BUFFER_SIZE, WGPEER_A_FLAGS, flags)) if (!mnl_attr_put_u32_check(nlh, SOCKET_BUFFER_SIZE, WGPEER_A_FLAGS, flags))
goto toobig_peers; goto toobig_peers;
} }
if (peer->first_allowedip) { if (peer->first_allowedip)
{
if (!allowedip) if (!allowedip)
allowedip = peer->first_allowedip; allowedip = peer->first_allowedip;
allowedips_nest = mnl_attr_nest_start_check(nlh, SOCKET_BUFFER_SIZE, WGPEER_A_ALLOWEDIPS); allowedips_nest =
mnl_attr_nest_start_check(nlh, SOCKET_BUFFER_SIZE, WGPEER_A_ALLOWEDIPS);
if (!allowedips_nest) if (!allowedips_nest)
goto toobig_allowedips; goto toobig_allowedips;
for (; allowedip; allowedip = allowedip->next_allowedip) { for (; allowedip; allowedip = allowedip->next_allowedip)
{
allowedip_nest = mnl_attr_nest_start_check(nlh, SOCKET_BUFFER_SIZE, 0); allowedip_nest = mnl_attr_nest_start_check(nlh, SOCKET_BUFFER_SIZE, 0);
if (!allowedip_nest) if (!allowedip_nest)
goto toobig_allowedips; goto toobig_allowedips;
if (!mnl_attr_put_u16_check(nlh, SOCKET_BUFFER_SIZE, WGALLOWEDIP_A_FAMILY, allowedip->family)) if (!mnl_attr_put_u16_check(
nlh, SOCKET_BUFFER_SIZE, WGALLOWEDIP_A_FAMILY, allowedip->family))
goto toobig_allowedips; goto toobig_allowedips;
if (allowedip->family == AF_INET) { if (allowedip->family == AF_INET)
if (!mnl_attr_put_check(nlh, SOCKET_BUFFER_SIZE, WGALLOWEDIP_A_IPADDR, sizeof(allowedip->ip4), &allowedip->ip4)) {
goto toobig_allowedips; if (!mnl_attr_put_check(
} else if (allowedip->family == AF_INET6) { nlh,
if (!mnl_attr_put_check(nlh, SOCKET_BUFFER_SIZE, WGALLOWEDIP_A_IPADDR, sizeof(allowedip->ip6), &allowedip->ip6)) SOCKET_BUFFER_SIZE,
WGALLOWEDIP_A_IPADDR,
sizeof(allowedip->ip4),
&allowedip->ip4))
goto toobig_allowedips; goto toobig_allowedips;
} }
if (!mnl_attr_put_u8_check(nlh, SOCKET_BUFFER_SIZE, WGALLOWEDIP_A_CIDR_MASK, allowedip->cidr)) else if (allowedip->family == AF_INET6)
{
if (!mnl_attr_put_check(
nlh,
SOCKET_BUFFER_SIZE,
WGALLOWEDIP_A_IPADDR,
sizeof(allowedip->ip6),
&allowedip->ip6))
goto toobig_allowedips;
}
if (!mnl_attr_put_u8_check(
nlh,
SOCKET_BUFFER_SIZE,
WGALLOWEDIP_A_CIDR_MASK,
allowedip->cidr))
goto toobig_allowedips; goto toobig_allowedips;
mnl_attr_nest_end(nlh, allowedip_nest); mnl_attr_nest_end(nlh, allowedip_nest);
allowedip_nest = NULL; allowedip_nest = NULL;
@ -278,12 +343,14 @@ toobig_peers:
mnl_attr_nest_end(nlh, peers_nest); mnl_attr_nest_end(nlh, peers_nest);
goto send; goto send;
send: send:
if (mnlg_socket_send(nlg, nlh) < 0) { if (mnlg_socket_send(nlg, nlh) < 0)
{
ret = -errno; ret = -errno;
goto out; goto out;
} }
errno = 0; errno = 0;
if (mnlg_socket_recv_run(nlg, NULL, NULL) < 0) { if (mnlg_socket_recv_run(nlg, NULL, NULL) < 0)
{
ret = errno ? -errno : -EINVAL; ret = errno ? -errno : -EINVAL;
goto out; goto out;
} }
@ -296,11 +363,12 @@ out:
return ret; return ret;
} }
static int parse_allowedip(const struct nlattr *attr, void *data) static int parse_allowedip(const struct nlattr* attr, void* data)
{ {
struct wgallowedip *allowedip = data; struct wgallowedip* allowedip = data;
switch (mnl_attr_get_type(attr)) { switch (mnl_attr_get_type(attr))
{
case WGALLOWEDIP_A_UNSPEC: case WGALLOWEDIP_A_UNSPEC:
break; break;
case WGALLOWEDIP_A_FAMILY: case WGALLOWEDIP_A_FAMILY:
@ -322,59 +390,72 @@ static int parse_allowedip(const struct nlattr *attr, void *data)
return MNL_CB_OK; return MNL_CB_OK;
} }
static int parse_allowedips(const struct nlattr *attr, void *data) static int parse_allowedips(const struct nlattr* attr, void* data)
{ {
struct wgpeer *peer = data; struct wgpeer* peer = data;
struct wgallowedip *new_allowedip = calloc(1, sizeof(*new_allowedip)); struct wgallowedip* new_allowedip = calloc(1, sizeof(*new_allowedip));
int ret; int ret;
if (!new_allowedip) { if (!new_allowedip)
{
perror("calloc"); perror("calloc");
return MNL_CB_ERROR; return MNL_CB_ERROR;
} }
if (!peer->first_allowedip) if (!peer->first_allowedip)
peer->first_allowedip = peer->last_allowedip = new_allowedip; peer->first_allowedip = peer->last_allowedip = new_allowedip;
else { else
{
peer->last_allowedip->next_allowedip = new_allowedip; peer->last_allowedip->next_allowedip = new_allowedip;
peer->last_allowedip = new_allowedip; peer->last_allowedip = new_allowedip;
} }
ret = mnl_attr_parse_nested(attr, parse_allowedip, new_allowedip); ret = mnl_attr_parse_nested(attr, parse_allowedip, new_allowedip);
if (!ret) if (!ret)
return ret; return ret;
if (!((new_allowedip->family == AF_INET && new_allowedip->cidr <= 32) || (new_allowedip->family == AF_INET6 && new_allowedip->cidr <= 128))) if (!((new_allowedip->family == AF_INET && new_allowedip->cidr <= 32) ||
(new_allowedip->family == AF_INET6 && new_allowedip->cidr <= 128)))
return MNL_CB_ERROR; return MNL_CB_ERROR;
return MNL_CB_OK; return MNL_CB_OK;
} }
static int parse_peer(const struct nlattr *attr, void *data) static int parse_peer(const struct nlattr* attr, void* data)
{ {
struct wgpeer *peer = data; struct wgpeer* peer = data;
switch (mnl_attr_get_type(attr)) { switch (mnl_attr_get_type(attr))
{
case WGPEER_A_UNSPEC: case WGPEER_A_UNSPEC:
break; break;
case WGPEER_A_PUBLIC_KEY: case WGPEER_A_PUBLIC_KEY:
if (mnl_attr_get_payload_len(attr) == sizeof(peer->public_key)) { if (mnl_attr_get_payload_len(attr) == sizeof(peer->public_key))
memcpy(peer->public_key, mnl_attr_get_payload(attr), sizeof(peer->public_key)); {
memcpy(
peer->public_key, mnl_attr_get_payload(attr), sizeof(peer->public_key));
peer->flags |= WGPEER_HAS_PUBLIC_KEY; peer->flags |= WGPEER_HAS_PUBLIC_KEY;
} }
break; break;
case WGPEER_A_PRESHARED_KEY: case WGPEER_A_PRESHARED_KEY:
if (mnl_attr_get_payload_len(attr) == sizeof(peer->preshared_key)) { if (mnl_attr_get_payload_len(attr) == sizeof(peer->preshared_key))
memcpy(peer->preshared_key, mnl_attr_get_payload(attr), sizeof(peer->preshared_key)); {
memcpy(
peer->preshared_key,
mnl_attr_get_payload(attr),
sizeof(peer->preshared_key));
if (!key_is_zero(peer->preshared_key)) if (!key_is_zero(peer->preshared_key))
peer->flags |= WGPEER_HAS_PRESHARED_KEY; peer->flags |= WGPEER_HAS_PRESHARED_KEY;
} }
break; break;
case WGPEER_A_ENDPOINT: { case WGPEER_A_ENDPOINT: {
struct sockaddr *addr; struct sockaddr* addr;
if (mnl_attr_get_payload_len(attr) < sizeof(*addr)) if (mnl_attr_get_payload_len(attr) < sizeof(*addr))
break; break;
addr = mnl_attr_get_payload(attr); addr = mnl_attr_get_payload(attr);
if (addr->sa_family == AF_INET && mnl_attr_get_payload_len(attr) == sizeof(peer->endpoint.addr4)) if (addr->sa_family == AF_INET &&
mnl_attr_get_payload_len(attr) == sizeof(peer->endpoint.addr4))
memcpy(&peer->endpoint.addr4, addr, sizeof(peer->endpoint.addr4)); memcpy(&peer->endpoint.addr4, addr, sizeof(peer->endpoint.addr4));
else if (addr->sa_family == AF_INET6 && mnl_attr_get_payload_len(attr) == sizeof(peer->endpoint.addr6)) else if (
addr->sa_family == AF_INET6 &&
mnl_attr_get_payload_len(attr) == sizeof(peer->endpoint.addr6))
memcpy(&peer->endpoint.addr6, addr, sizeof(peer->endpoint.addr6)); memcpy(&peer->endpoint.addr6, addr, sizeof(peer->endpoint.addr6));
break; break;
} }
@ -384,7 +465,10 @@ static int parse_peer(const struct nlattr *attr, void *data)
break; break;
case WGPEER_A_LAST_HANDSHAKE_TIME: case WGPEER_A_LAST_HANDSHAKE_TIME:
if (mnl_attr_get_payload_len(attr) == sizeof(peer->last_handshake_time)) if (mnl_attr_get_payload_len(attr) == sizeof(peer->last_handshake_time))
memcpy(&peer->last_handshake_time, mnl_attr_get_payload(attr), sizeof(peer->last_handshake_time)); memcpy(
&peer->last_handshake_time,
mnl_attr_get_payload(attr),
sizeof(peer->last_handshake_time));
break; break;
case WGPEER_A_RX_BYTES: case WGPEER_A_RX_BYTES:
if (!mnl_attr_validate(attr, MNL_TYPE_U64)) if (!mnl_attr_validate(attr, MNL_TYPE_U64))
@ -395,20 +479,25 @@ static int parse_peer(const struct nlattr *attr, void *data)
peer->tx_bytes = mnl_attr_get_u64(attr); peer->tx_bytes = mnl_attr_get_u64(attr);
break; break;
case WGPEER_A_FLAGS: case WGPEER_A_FLAGS:
if (!mnl_attr_validate(attr, MNL_TYPE_U32)) { if (!mnl_attr_validate(attr, MNL_TYPE_U32))
{
uint32_t flags = mnl_attr_get_u32(attr); uint32_t flags = mnl_attr_get_u32(attr);
if (flags & WGPEER_F_HAS_ADVANCED_SECURITY && !(peer->flags & WGPEER_HAS_ADVANCED_SECURITY)) { if (flags & WGPEER_F_HAS_ADVANCED_SECURITY &&
!(peer->flags & WGPEER_HAS_ADVANCED_SECURITY))
{
peer->flags |= WGPEER_HAS_ADVANCED_SECURITY; peer->flags |= WGPEER_HAS_ADVANCED_SECURITY;
peer->advanced_security = false; peer->advanced_security = false;
} }
} }
break; break;
case WGPEER_A_ADVANCED_SECURITY: case WGPEER_A_ADVANCED_SECURITY:
if (!mnl_attr_validate(attr, MNL_TYPE_FLAG)) { if (!mnl_attr_validate(attr, MNL_TYPE_FLAG))
{
peer->advanced_security = true; peer->advanced_security = true;
if (!(peer->flags & WGPEER_HAS_ADVANCED_SECURITY)) { if (!(peer->flags & WGPEER_HAS_ADVANCED_SECURITY))
{
peer->flags |= WGPEER_HAS_ADVANCED_SECURITY; peer->flags |= WGPEER_HAS_ADVANCED_SECURITY;
} }
} }
@ -420,19 +509,21 @@ static int parse_peer(const struct nlattr *attr, void *data)
return MNL_CB_OK; return MNL_CB_OK;
} }
static int parse_peers(const struct nlattr *attr, void *data) static int parse_peers(const struct nlattr* attr, void* data)
{ {
struct wgdevice *device = data; struct wgdevice* device = data;
struct wgpeer *new_peer = calloc(1, sizeof(*new_peer)); struct wgpeer* new_peer = calloc(1, sizeof(*new_peer));
int ret; int ret;
if (!new_peer) { if (!new_peer)
{
perror("calloc"); perror("calloc");
return MNL_CB_ERROR; return MNL_CB_ERROR;
} }
if (!device->first_peer) if (!device->first_peer)
device->first_peer = device->last_peer = new_peer; device->first_peer = device->last_peer = new_peer;
else { else
{
device->last_peer->next_peer = new_peer; device->last_peer->next_peer = new_peer;
device->last_peer = new_peer; device->last_peer = new_peer;
} }
@ -444,11 +535,12 @@ static int parse_peers(const struct nlattr *attr, void *data)
return MNL_CB_OK; return MNL_CB_OK;
} }
static int parse_device(const struct nlattr *attr, void *data) static int parse_device(const struct nlattr* attr, void* data)
{ {
struct wgdevice *device = data; struct wgdevice* device = data;
switch (mnl_attr_get_type(attr)) { switch (mnl_attr_get_type(attr))
{
case WGDEVICE_A_UNSPEC: case WGDEVICE_A_UNSPEC:
break; break;
case WGDEVICE_A_IFINDEX: case WGDEVICE_A_IFINDEX:
@ -456,20 +548,29 @@ static int parse_device(const struct nlattr *attr, void *data)
device->ifindex = mnl_attr_get_u32(attr); device->ifindex = mnl_attr_get_u32(attr);
break; break;
case WGDEVICE_A_IFNAME: case WGDEVICE_A_IFNAME:
if (!mnl_attr_validate(attr, MNL_TYPE_STRING)) { if (!mnl_attr_validate(attr, MNL_TYPE_STRING))
{
strncpy(device->name, mnl_attr_get_str(attr), sizeof(device->name) - 1); strncpy(device->name, mnl_attr_get_str(attr), sizeof(device->name) - 1);
device->name[sizeof(device->name) - 1] = '\0'; device->name[sizeof(device->name) - 1] = '\0';
} }
break; break;
case WGDEVICE_A_PRIVATE_KEY: case WGDEVICE_A_PRIVATE_KEY:
if (mnl_attr_get_payload_len(attr) == sizeof(device->private_key)) { if (mnl_attr_get_payload_len(attr) == sizeof(device->private_key))
memcpy(device->private_key, mnl_attr_get_payload(attr), sizeof(device->private_key)); {
memcpy(
device->private_key,
mnl_attr_get_payload(attr),
sizeof(device->private_key));
device->flags |= WGDEVICE_HAS_PRIVATE_KEY; device->flags |= WGDEVICE_HAS_PRIVATE_KEY;
} }
break; break;
case WGDEVICE_A_PUBLIC_KEY: case WGDEVICE_A_PUBLIC_KEY:
if (mnl_attr_get_payload_len(attr) == sizeof(device->public_key)) { if (mnl_attr_get_payload_len(attr) == sizeof(device->public_key))
memcpy(device->public_key, mnl_attr_get_payload(attr), sizeof(device->public_key)); {
memcpy(
device->public_key,
mnl_attr_get_payload(attr),
sizeof(device->public_key));
device->flags |= WGDEVICE_HAS_PUBLIC_KEY; device->flags |= WGDEVICE_HAS_PUBLIC_KEY;
} }
break; break;
@ -484,82 +585,143 @@ static int parse_device(const struct nlattr *attr, void *data)
case WGDEVICE_A_PEERS: case WGDEVICE_A_PEERS:
return mnl_attr_parse_nested(attr, parse_peers, device); return mnl_attr_parse_nested(attr, parse_peers, device);
case WGDEVICE_A_JC: case WGDEVICE_A_JC:
if (!mnl_attr_validate(attr, MNL_TYPE_U16)) { if (!mnl_attr_validate(attr, MNL_TYPE_U16))
{
device->junk_packet_count = mnl_attr_get_u16(attr); device->junk_packet_count = mnl_attr_get_u16(attr);
device->flags |= WGDEVICE_HAS_JC; device->flags |= WGDEVICE_HAS_JC;
} }
break; break;
case WGDEVICE_A_JMIN: case WGDEVICE_A_JMIN:
if (!mnl_attr_validate(attr, MNL_TYPE_U16)) { if (!mnl_attr_validate(attr, MNL_TYPE_U16))
{
device->junk_packet_min_size = mnl_attr_get_u16(attr); device->junk_packet_min_size = mnl_attr_get_u16(attr);
device->flags |= WGDEVICE_HAS_JMIN; device->flags |= WGDEVICE_HAS_JMIN;
} }
break; break;
case WGDEVICE_A_JMAX: case WGDEVICE_A_JMAX:
if (!mnl_attr_validate(attr, MNL_TYPE_U16)) { if (!mnl_attr_validate(attr, MNL_TYPE_U16))
{
device->junk_packet_max_size = mnl_attr_get_u16(attr); device->junk_packet_max_size = mnl_attr_get_u16(attr);
device->flags |= WGDEVICE_HAS_JMAX; device->flags |= WGDEVICE_HAS_JMAX;
} }
break; break;
case WGDEVICE_A_S1: case WGDEVICE_A_S1:
if (!mnl_attr_validate(attr, MNL_TYPE_U16)) { if (!mnl_attr_validate(attr, MNL_TYPE_U16))
{
device->init_packet_junk_size = mnl_attr_get_u16(attr); device->init_packet_junk_size = mnl_attr_get_u16(attr);
device->flags |= WGDEVICE_HAS_S1; device->flags |= WGDEVICE_HAS_S1;
} }
break; break;
case WGDEVICE_A_S2: case WGDEVICE_A_S2:
if (!mnl_attr_validate(attr, MNL_TYPE_U16)) { if (!mnl_attr_validate(attr, MNL_TYPE_U16))
{
device->response_packet_junk_size = mnl_attr_get_u16(attr); device->response_packet_junk_size = mnl_attr_get_u16(attr);
device->flags |= WGDEVICE_HAS_S2; device->flags |= WGDEVICE_HAS_S2;
} }
break; break;
case WGDEVICE_A_H1: case WGDEVICE_A_H1:
if (!mnl_attr_validate(attr, MNL_TYPE_U32)) { if (!mnl_attr_validate(attr, MNL_TYPE_U32))
{
device->init_packet_magic_header = mnl_attr_get_u32(attr); device->init_packet_magic_header = mnl_attr_get_u32(attr);
device->flags |= WGDEVICE_HAS_H1; device->flags |= WGDEVICE_HAS_H1;
} }
break; break;
case WGDEVICE_A_H2: case WGDEVICE_A_H2:
if (!mnl_attr_validate(attr, MNL_TYPE_U32)) { if (!mnl_attr_validate(attr, MNL_TYPE_U32))
{
device->response_packet_magic_header = mnl_attr_get_u32(attr); device->response_packet_magic_header = mnl_attr_get_u32(attr);
device->flags |= WGDEVICE_HAS_H2; device->flags |= WGDEVICE_HAS_H2;
} }
break; break;
case WGDEVICE_A_H3: case WGDEVICE_A_H3:
if (!mnl_attr_validate(attr, MNL_TYPE_U32)) { if (!mnl_attr_validate(attr, MNL_TYPE_U32))
{
device->underload_packet_magic_header = mnl_attr_get_u32(attr); device->underload_packet_magic_header = mnl_attr_get_u32(attr);
device->flags |= WGDEVICE_HAS_H3; device->flags |= WGDEVICE_HAS_H3;
} }
break; break;
case WGDEVICE_A_H4: case WGDEVICE_A_H4:
if (!mnl_attr_validate(attr, MNL_TYPE_U32)) { if (!mnl_attr_validate(attr, MNL_TYPE_U32))
{
device->transport_packet_magic_header = mnl_attr_get_u32(attr); device->transport_packet_magic_header = mnl_attr_get_u32(attr);
device->flags |= WGDEVICE_HAS_H4; device->flags |= WGDEVICE_HAS_H4;
} }
case WGDEVICE_A_I1:
if (!mnl_attr_validate(attr, MNL_TYPE_STRING) {
device->i1 = strdup(mnl_attr_get_str(attr));
device->flags |= WGDEVICE_HAS_I1;
}
case WGDEVICE_A_I2:
if (!mnl_attr_validate(attr, MNL_TYPE_STRING) {
device->i2 = strdup(mnl_attr_get_str(attr));
device->flags |= WGDEVICE_HAS_I2;
}
case WGDEVICE_A_I3:
if (!mnl_attr_validate(attr, MNL_TYPE_STRING) {
device->i3 = strdup(mnl_attr_get_str(attr));
device->flags |= WGDEVICE_HAS_I3;
}
case WGDEVICE_A_I4:
if (!mnl_attr_validate(attr, MNL_TYPE_STRING) {
device->i4 = strdup(mnl_attr_get_str(attr));
device->flags |= WGDEVICE_HAS_I4;
}
case WGDEVICE_A_I5:
if (!mnl_attr_validate(attr, MNL_TYPE_STRING) {
device->i5 = strdup(mnl_attr_get_str(attr));
device->flags |= WGDEVICE_HAS_I5;
}
case WGDEVICE_A_J1:
if (!mnl_attr_validate(attr, MNL_TYPE_STRING) {
device->j1 = strdup(mnl_attr_get_str(attr));
device->flags |= WGDEVICE_HAS_J1;
}
case WGDEVICE_A_J2:
if (!mnl_attr_validate(attr, MNL_TYPE_STRING) {
device->j2 = strdup(mnl_attr_get_str(attr));
device->flags |= WGDEVICE_HAS_J2;
}
case WGDEVICE_A_J3:
if (!mnl_attr_validate(attr, MNL_TYPE_STRING) {
device->j3 = strdup(mnl_attr_get_str(attr));
device->flags |= WGDEVICE_HAS_J3;
}
case WGDEVICE_A_ITIME:
if (!mnl_attr_validate(attr, MNL_TYPE_STRING) {
device->itime = strdup(mnl_attr_get_str(attr));
device->flags |= WGDEVICE_HAS_ITIME;
}
break; break;
} }
return MNL_CB_OK; return MNL_CB_OK;
} }
static int read_device_cb(const struct nlmsghdr *nlh, void *data) static int read_device_cb(const struct nlmsghdr* nlh, void* data)
{ {
return mnl_attr_parse(nlh, sizeof(struct genlmsghdr), parse_device, data); return mnl_attr_parse(nlh, sizeof(struct genlmsghdr), parse_device, data);
} }
static void coalesce_peers(struct wgdevice *device) static void coalesce_peers(struct wgdevice* device)
{ {
struct wgpeer *old_next_peer, *peer = device->first_peer; struct wgpeer *old_next_peer, *peer = device->first_peer;
while (peer && peer->next_peer) { while (peer && peer->next_peer)
if (memcmp(peer->public_key, peer->next_peer->public_key, sizeof(peer->public_key))) { {
if (memcmp(
peer->public_key, peer->next_peer->public_key, sizeof(peer->public_key)))
{
peer = peer->next_peer; peer = peer->next_peer;
continue; continue;
} }
if (!peer->first_allowedip) { if (!peer->first_allowedip)
{
peer->first_allowedip = peer->next_peer->first_allowedip; peer->first_allowedip = peer->next_peer->first_allowedip;
peer->last_allowedip = peer->next_peer->last_allowedip; peer->last_allowedip = peer->next_peer->last_allowedip;
} else { }
else
{
peer->last_allowedip->next_allowedip = peer->next_peer->first_allowedip; peer->last_allowedip->next_allowedip = peer->next_peer->first_allowedip;
peer->last_allowedip = peer->next_peer->last_allowedip; peer->last_allowedip = peer->next_peer->last_allowedip;
} }
@ -569,14 +731,15 @@ static void coalesce_peers(struct wgdevice *device)
} }
} }
static int kernel_get_device(struct wgdevice **device, const char *iface) static int kernel_get_device(struct wgdevice** device, const char* iface)
{ {
int ret; int ret;
struct nlmsghdr *nlh; struct nlmsghdr* nlh;
struct mnlg_socket *nlg; struct mnlg_socket* nlg;
/* libmnl doesn't check the buffer size, so enforce that before using. */ /* libmnl doesn't check the buffer size, so enforce that before using. */
if (strlen(iface) >= IFNAMSIZ) { if (strlen(iface) >= IFNAMSIZ)
{
errno = ENAMETOOLONG; errno = ENAMETOOLONG;
return -ENAMETOOLONG; return -ENAMETOOLONG;
} }
@ -588,20 +751,24 @@ try_again:
return -errno; return -errno;
nlg = mnlg_socket_open(WG_GENL_NAME, WG_GENL_VERSION); nlg = mnlg_socket_open(WG_GENL_NAME, WG_GENL_VERSION);
if (!nlg) { if (!nlg)
{
free_wgdevice(*device); free_wgdevice(*device);
*device = NULL; *device = NULL;
return -errno; return -errno;
} }
nlh = mnlg_msg_prepare(nlg, WG_CMD_GET_DEVICE, NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP); nlh =
mnlg_msg_prepare(nlg, WG_CMD_GET_DEVICE, NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP);
mnl_attr_put_strz(nlh, WGDEVICE_A_IFNAME, iface); mnl_attr_put_strz(nlh, WGDEVICE_A_IFNAME, iface);
if (mnlg_socket_send(nlg, nlh) < 0) { if (mnlg_socket_send(nlg, nlh) < 0)
{
ret = -errno; ret = -errno;
goto out; goto out;
} }
errno = 0; errno = 0;
if (mnlg_socket_recv_run(nlg, read_device_cb, *device) < 0) { if (mnlg_socket_recv_run(nlg, read_device_cb, *device) < 0)
{
ret = errno ? -errno : -EINVAL; ret = errno ? -errno : -EINVAL;
goto out; goto out;
} }
@ -610,7 +777,8 @@ try_again:
out: out:
if (nlg) if (nlg)
mnlg_socket_close(nlg); mnlg_socket_close(nlg);
if (ret) { if (ret)
{
free_wgdevice(*device); free_wgdevice(*device);
if (ret == -EINTR) if (ret == -EINTR)
goto try_again; goto try_again;

View file

@ -65,6 +65,25 @@ int showconf_main(int argc, const char *argv[])
if (device->flags & WGDEVICE_HAS_H4) if (device->flags & WGDEVICE_HAS_H4)
printf("H4 = %u\n", device->transport_packet_magic_header); printf("H4 = %u\n", device->transport_packet_magic_header);
if (device->flags & WGDEVICE_HAS_I1)
printf("I1 = %s\n", device->i1);
if (device->flags & WGDEVICE_HAS_I2)
printf("I2 = %s\n", device->i2);
if (device->flags & WGDEVICE_HAS_I3)
printf("I3 = %s\n", device->i3);
if (device->flags & WGDEVICE_HAS_I4)
printf("I4 = %s\n", device->i4);
if (device->flags & WGDEVICE_HAS_I5)
printf("I5 = %s\n", device->i5);
if (device->flags & WGDEVICE_HAS_J1)
printf("J1 = %s\n", device->j1);
if (device->flags & WGDEVICE_HAS_J2)
printf("J2 = %s\n", device->j2);
if (device->flags & WGDEVICE_HAS_J3)
printf("J3 = %s\n", device->j3);
if (device->flags & WGDEVICE_HAS_ITIME)
printf("Itime = %s\n", device->itime);
printf("\n"); printf("\n");
for_each_wgpeer(device, peer) { for_each_wgpeer(device, peer) {
key_to_base64(base64, peer->public_key); key_to_base64(base64, peer->public_key);

View file

@ -191,6 +191,15 @@ enum wgdevice_attribute {
WGDEVICE_A_H2, WGDEVICE_A_H2,
WGDEVICE_A_H3, WGDEVICE_A_H3,
WGDEVICE_A_H4, WGDEVICE_A_H4,
WGDEVICE_A_I1,
WGDEVICE_A_I2,
WGDEVICE_A_I3,
WGDEVICE_A_I4,
WGDEVICE_A_I5,
WGDEVICE_A_J1,
WGDEVICE_A_J2,
WGDEVICE_A_J3,
WGDEVICE_A_ITIME,
WGDEVICE_A_PEER, WGDEVICE_A_PEER,
__WGDEVICE_A_LAST __WGDEVICE_A_LAST
}; };