diff --git a/contrib/embeddable-wg-library/wireguard.c b/contrib/embeddable-wg-library/wireguard.c index 500f33d..0ba767b 100644 --- a/contrib/embeddable-wg-library/wireguard.c +++ b/contrib/embeddable-wg-library/wireguard.c @@ -57,6 +57,15 @@ enum wgdevice_attribute { WGDEVICE_A_H2, WGDEVICE_A_H3, 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 }; diff --git a/src/config.c b/src/config.c index f663b7d..6801840 100644 --- a/src/config.c +++ b/src/config.c @@ -410,13 +410,29 @@ err: 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) { if (!strlen(value)) { fprintf(stderr, "Unable to parse empty string\n"); return false; } - + char *end; uint32_t ret; ret = strtoul(value, &end, 10); @@ -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); if (ret) 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 goto error; } else if (ctx->is_peer_section) { @@ -703,66 +755,129 @@ struct wgdevice *config_read_cmd(const char *argv[], int argc) } else if (!strcmp(argv[0], "jc") && argc >= 2 && !peer) { if (!parse_uint16(&device->junk_packet_count, "jc", argv[1])) goto error; - + device->flags |= WGDEVICE_HAS_JC; argv += 2; argc -= 2; } else if (!strcmp(argv[0], "jmin") && argc >= 2 && !peer) { if (!parse_uint16(&device->junk_packet_min_size, "jmin", argv[1])) goto error; - + device->flags |= WGDEVICE_HAS_JMIN; argv += 2; argc -= 2; } else if (!strcmp(argv[0], "jmax") && argc >= 2 && !peer) { if (!parse_uint16(&device->junk_packet_max_size, "jmax", argv[1])) goto error; - + device->flags |= WGDEVICE_HAS_JMAX; argv += 2; argc -= 2; } else if (!strcmp(argv[0], "s1") && argc >= 2 && !peer) { if (!parse_uint16(&device->init_packet_junk_size, "s1", argv[1])) goto error; - + device->flags |= WGDEVICE_HAS_S1; argv += 2; argc -= 2; } else if (!strcmp(argv[0], "s2") && argc >= 2 && !peer) { if (!parse_uint16(&device->response_packet_junk_size, "s2", argv[1])) goto error; - + device->flags |= WGDEVICE_HAS_S2; argv += 2; argc -= 2; } else if (!strcmp(argv[0], "h1") && argc >= 2 && !peer) { if (!parse_uint32(&device->init_packet_magic_header, "h1", argv[1])) goto error; - + device->flags |= WGDEVICE_HAS_H1; argv += 2; argc -= 2; } else if (!strcmp(argv[0], "h2") && argc >= 2 && !peer) { if (!parse_uint32(&device->response_packet_magic_header, "h2", argv[1])) goto error; - + device->flags |= WGDEVICE_HAS_H2; argv += 2; argc -= 2; } else if (!strcmp(argv[0], "h3") && argc >= 2 && !peer) { if (!parse_uint32(&device->underload_packet_magic_header, "h3", argv[1])) goto error; - + device->flags |= WGDEVICE_HAS_H3; argv += 2; argc -= 2; } else if (!strcmp(argv[0], "h4") && argc >= 2 && !peer) { if (!parse_uint32(&device->transport_packet_magic_header, "h4", argv[1])) goto error; - + device->flags |= WGDEVICE_HAS_H4; argv += 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) { struct wgpeer *new_peer = calloc(1, sizeof(*new_peer)); diff --git a/src/containers.h b/src/containers.h index 8f93bb5..d6589e0 100644 --- a/src/containers.h +++ b/src/containers.h @@ -6,13 +6,13 @@ #ifndef CONTAINERS_H #define CONTAINERS_H -#include -#include -#include -#include -#include #include #include +#include +#include +#include +#include +#include #if defined(__linux__) #include #elif defined(__OpenBSD__) @@ -23,108 +23,146 @@ #define WG_KEY_LEN 32 #endif +#ifndef MAX_AWG_JUNK_LEN +#define MAX_AWG_JUNK_LEN 5 * 1024 +#endif + /* Cross platform __kernel_timespec */ -struct timespec64 { - int64_t tv_sec; - int64_t tv_nsec; -}; - -struct wgallowedip { - uint16_t family; - union { - struct in_addr ip4; - struct in6_addr ip6; - }; - uint8_t cidr; - struct wgallowedip *next_allowedip; -}; - -enum { - WGPEER_REMOVE_ME = 1U << 0, - WGPEER_REPLACE_ALLOWEDIPS = 1U << 1, - WGPEER_HAS_PUBLIC_KEY = 1U << 2, - WGPEER_HAS_PRESHARED_KEY = 1U << 3, - WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL = 1U << 4, - WGPEER_HAS_ADVANCED_SECURITY = 1U << 5 -}; - -struct wgpeer { - uint32_t flags; - - uint8_t public_key[WG_KEY_LEN]; - uint8_t preshared_key[WG_KEY_LEN]; - - union { - struct sockaddr addr; - struct sockaddr_in addr4; - struct sockaddr_in6 addr6; - } endpoint; - - struct timespec64 last_handshake_time; - uint64_t rx_bytes, tx_bytes; - uint16_t persistent_keepalive_interval; - - bool advanced_security; - - struct wgallowedip *first_allowedip, *last_allowedip; - struct wgpeer *next_peer; -}; - -enum { - WGDEVICE_REPLACE_PEERS = 1U << 0, - WGDEVICE_HAS_PRIVATE_KEY = 1U << 1, - WGDEVICE_HAS_PUBLIC_KEY = 1U << 2, - WGDEVICE_HAS_LISTEN_PORT = 1U << 3, - WGDEVICE_HAS_FWMARK = 1U << 4, - WGDEVICE_HAS_JC = 1U << 5, - WGDEVICE_HAS_JMIN = 1U << 6, - WGDEVICE_HAS_JMAX = 1U << 7, - WGDEVICE_HAS_S1 = 1U << 8, - WGDEVICE_HAS_S2 = 1U << 9, - WGDEVICE_HAS_H1 = 1U << 10, - WGDEVICE_HAS_H2 = 1U << 11, - WGDEVICE_HAS_H3 = 1U << 12, - WGDEVICE_HAS_H4 = 1U << 13 -}; - -struct wgdevice { - char name[IFNAMSIZ]; - uint32_t ifindex; - - uint32_t flags; - - uint8_t public_key[WG_KEY_LEN]; - uint8_t private_key[WG_KEY_LEN]; - - uint32_t fwmark; - uint16_t listen_port; - - struct wgpeer *first_peer, *last_peer; - - uint16_t junk_packet_count; - uint16_t junk_packet_min_size; - uint16_t junk_packet_max_size; - uint16_t init_packet_junk_size; - uint16_t response_packet_junk_size; - uint32_t init_packet_magic_header; - uint32_t response_packet_magic_header; - uint32_t underload_packet_magic_header; - uint32_t transport_packet_magic_header; -}; - -#define for_each_wgpeer(__dev, __peer) 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) +struct timespec64 { - if (!dev) - 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 wgallowedip *allowedip = peer->first_allowedip, *na = allowedip ? allowedip->next_allowedip : NULL; allowedip; allowedip = na, na = allowedip ? allowedip->next_allowedip : NULL) - free(allowedip); - free(peer); - } - free(dev); + int64_t tv_sec; + int64_t tv_nsec; +}; + +struct wgallowedip +{ + uint16_t family; + union + { + struct in_addr ip4; + struct in6_addr ip6; + }; + uint8_t cidr; + struct wgallowedip* next_allowedip; +}; + +enum +{ + WGPEER_REMOVE_ME = 1U << 0, + WGPEER_REPLACE_ALLOWEDIPS = 1U << 1, + WGPEER_HAS_PUBLIC_KEY = 1U << 2, + WGPEER_HAS_PRESHARED_KEY = 1U << 3, + WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL = 1U << 4, + WGPEER_HAS_ADVANCED_SECURITY = 1U << 5 +}; + +struct wgpeer +{ + uint32_t flags; + + uint8_t public_key[WG_KEY_LEN]; + uint8_t preshared_key[WG_KEY_LEN]; + + union + { + struct sockaddr addr; + struct sockaddr_in addr4; + struct sockaddr_in6 addr6; + } endpoint; + + struct timespec64 last_handshake_time; + uint64_t rx_bytes, tx_bytes; + uint16_t persistent_keepalive_interval; + + bool advanced_security; + + struct wgallowedip *first_allowedip, *last_allowedip; + struct wgpeer* next_peer; +}; + +enum +{ + WGDEVICE_REPLACE_PEERS = 1U << 0, + WGDEVICE_HAS_PRIVATE_KEY = 1U << 1, + WGDEVICE_HAS_PUBLIC_KEY = 1U << 2, + WGDEVICE_HAS_LISTEN_PORT = 1U << 3, + WGDEVICE_HAS_FWMARK = 1U << 4, + WGDEVICE_HAS_JC = 1U << 5, + WGDEVICE_HAS_JMIN = 1U << 6, + WGDEVICE_HAS_JMAX = 1U << 7, + WGDEVICE_HAS_S1 = 1U << 8, + WGDEVICE_HAS_S2 = 1U << 9, + WGDEVICE_HAS_H1 = 1U << 10, + WGDEVICE_HAS_H2 = 1U << 11, + WGDEVICE_HAS_H3 = 1U << 12, + 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 +{ + char name[IFNAMSIZ]; + uint32_t ifindex; + + uint32_t flags; + + uint8_t public_key[WG_KEY_LEN]; + uint8_t private_key[WG_KEY_LEN]; + + uint32_t fwmark; + uint16_t listen_port; + + struct wgpeer *first_peer, *last_peer; + + uint16_t junk_packet_count; + uint16_t junk_packet_min_size; + uint16_t junk_packet_max_size; + uint16_t init_packet_junk_size; + uint16_t response_packet_junk_size; + uint32_t init_packet_magic_header; + uint32_t response_packet_magic_header; + uint32_t underload_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_wgallowedip(__peer, __allowedip) \ + for ((__allowedip) = (__peer)->first_allowedip; (__allowedip); \ + (__allowedip) = (__allowedip)->next_allowedip) + +static inline void free_wgdevice(struct wgdevice* dev) +{ + if (!dev) + 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 wgallowedip *allowedip = peer->first_allowedip, + *na = allowedip ? allowedip->next_allowedip : NULL; + allowedip; + allowedip = na, na = allowedip ? allowedip->next_allowedip : NULL) + free(allowedip); + free(peer); + } + free(dev); } #endif diff --git a/src/ipc-linux.h b/src/ipc-linux.h index c6d9847..a385f06 100644 --- a/src/ipc-linux.h +++ b/src/ipc-linux.h @@ -3,619 +3,787 @@ * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. */ -#include -#include -#include -#include -#include +#include "containers.h" +#include "encoding.h" +#include "netlink.h" #include -#include -#include -#include -#include #include #include #include #include #include #include -#include "containers.h" -#include "encoding.h" -#include "netlink.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include #define IPC_SUPPORTS_KERNEL_INTERFACE #define SOCKET_BUFFER_SIZE (mnl_ideal_socket_buffer_size()) -struct interface { - const char *name; - bool is_wireguard; +struct interface +{ + const char* name; + 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))) - interface->is_wireguard = true; - return MNL_CB_OK; + if (mnl_attr_get_type(attr) == IFLA_INFO_KIND && + !strcmp(WG_GENL_NAME, mnl_attr_get_str(attr))) + interface->is_wireguard = true; + 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) - return mnl_attr_parse_nested(attr, parse_linkinfo, data); - else if (mnl_attr_get_type(attr) == IFLA_IFNAME) - interface->name = mnl_attr_get_str(attr); - return MNL_CB_OK; + if (mnl_attr_get_type(attr) == IFLA_LINKINFO) + return mnl_attr_parse_nested(attr, parse_linkinfo, data); + else if (mnl_attr_get_type(attr) == IFLA_IFNAME) + interface->name = mnl_attr_get_str(attr); + 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 interface interface = { 0 }; - int ret; + struct string_list* list = data; + struct interface interface = {0}; + int ret; - ret = mnl_attr_parse(nlh, sizeof(struct ifinfomsg), parse_infomsg, &interface); - if (ret != MNL_CB_OK) - return ret; - if (interface.name && interface.is_wireguard) - ret = string_list_add(list, interface.name); - if (ret < 0) - return ret; - if (nlh->nlmsg_type != NLMSG_DONE) - return MNL_CB_OK + 1; - return MNL_CB_OK; + ret = mnl_attr_parse(nlh, sizeof(struct ifinfomsg), parse_infomsg, &interface); + if (ret != MNL_CB_OK) + return ret; + if (interface.name && interface.is_wireguard) + ret = string_list_add(list, interface.name); + if (ret < 0) + return ret; + if (nlh->nlmsg_type != NLMSG_DONE) + return MNL_CB_OK + 1; + 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; - char *rtnl_buffer = NULL; - size_t message_len; - unsigned int portid, seq; - ssize_t len; - int ret = 0; - struct nlmsghdr *nlh; - struct ifinfomsg *ifm; + struct mnl_socket* nl = NULL; + char* rtnl_buffer = NULL; + size_t message_len; + unsigned int portid, seq; + ssize_t len; + int ret = 0; + struct nlmsghdr* nlh; + struct ifinfomsg* ifm; - ret = -ENOMEM; - rtnl_buffer = calloc(SOCKET_BUFFER_SIZE, 1); - if (!rtnl_buffer) - goto cleanup; + ret = -ENOMEM; + rtnl_buffer = calloc(SOCKET_BUFFER_SIZE, 1); + if (!rtnl_buffer) + goto cleanup; - nl = mnl_socket_open(NETLINK_ROUTE); - if (!nl) { - ret = -errno; - goto cleanup; - } + nl = mnl_socket_open(NETLINK_ROUTE); + if (!nl) + { + ret = -errno; + goto cleanup; + } - if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { - ret = -errno; - goto cleanup; - } + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) + { + ret = -errno; + goto cleanup; + } - seq = time(NULL); - portid = mnl_socket_get_portid(nl); - nlh = mnl_nlmsg_put_header(rtnl_buffer); - nlh->nlmsg_type = RTM_GETLINK; - nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP; - nlh->nlmsg_seq = seq; - ifm = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifm)); - ifm->ifi_family = AF_UNSPEC; - message_len = nlh->nlmsg_len; + seq = time(NULL); + portid = mnl_socket_get_portid(nl); + nlh = mnl_nlmsg_put_header(rtnl_buffer); + nlh->nlmsg_type = RTM_GETLINK; + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP; + nlh->nlmsg_seq = seq; + ifm = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifm)); + ifm->ifi_family = AF_UNSPEC; + message_len = nlh->nlmsg_len; - if (mnl_socket_sendto(nl, rtnl_buffer, message_len) < 0) { - ret = -errno; - goto cleanup; - } + if (mnl_socket_sendto(nl, rtnl_buffer, message_len) < 0) + { + ret = -errno; + goto cleanup; + } another: - if ((len = mnl_socket_recvfrom(nl, rtnl_buffer, SOCKET_BUFFER_SIZE)) < 0) { - ret = -errno; - goto cleanup; - } - 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 + if ((len = mnl_socket_recvfrom(nl, rtnl_buffer, SOCKET_BUFFER_SIZE)) < 0) + { + ret = -errno; + goto cleanup; + } + 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 * during the dump. That's unfortunate, but is pretty common on busy * systems that are adding and removing tunnels all the time. Rather * than retrying, potentially indefinitely, we just work with the * partial results. */ - if (errno != EINTR) { - ret = -errno; - goto cleanup; - } - } - if (len == MNL_CB_OK + 1) - goto another; - ret = 0; + if (errno != EINTR) + { + ret = -errno; + goto cleanup; + } + } + if (len == MNL_CB_OK + 1) + goto another; + ret = 0; cleanup: - free(rtnl_buffer); - if (nl) - mnl_socket_close(nl); - return ret; + free(rtnl_buffer); + if (nl) + mnl_socket_close(nl); + return ret; } -static int kernel_set_device(struct wgdevice *dev) +static int kernel_set_device(struct wgdevice* dev) { - int ret = 0; - struct wgpeer *peer = NULL; - struct wgallowedip *allowedip = NULL; - struct nlattr *peers_nest, *peer_nest, *allowedips_nest, *allowedip_nest; - struct nlmsghdr *nlh; - struct mnlg_socket *nlg; + int ret = 0; + struct wgpeer* peer = NULL; + struct wgallowedip* allowedip = NULL; + struct nlattr * peers_nest, *peer_nest, *allowedips_nest, *allowedip_nest; + struct nlmsghdr* nlh; + struct mnlg_socket* nlg; - nlg = mnlg_socket_open(WG_GENL_NAME, WG_GENL_VERSION); - if (!nlg) - return -errno; + nlg = mnlg_socket_open(WG_GENL_NAME, WG_GENL_VERSION); + if (!nlg) + return -errno; again: - 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); + 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); - if (!peer) { - uint32_t flags = 0; + if (!peer) + { + uint32_t flags = 0; - if (dev->flags & WGDEVICE_HAS_PRIVATE_KEY) - mnl_attr_put(nlh, WGDEVICE_A_PRIVATE_KEY, sizeof(dev->private_key), dev->private_key); - if (dev->flags & WGDEVICE_HAS_LISTEN_PORT) - mnl_attr_put_u16(nlh, WGDEVICE_A_LISTEN_PORT, dev->listen_port); - if (dev->flags & WGDEVICE_HAS_JC) - mnl_attr_put_u16(nlh, WGDEVICE_A_JC, dev->junk_packet_count); - if (dev->flags & WGDEVICE_HAS_JMIN) - mnl_attr_put_u16(nlh, WGDEVICE_A_JMIN, dev->junk_packet_min_size); - if (dev->flags & WGDEVICE_HAS_JMAX) - mnl_attr_put_u16(nlh, WGDEVICE_A_JMAX, dev->junk_packet_max_size); - if (dev->flags & WGDEVICE_HAS_S1) - mnl_attr_put_u16(nlh, WGDEVICE_A_S1, dev->init_packet_junk_size); - if (dev->flags & WGDEVICE_HAS_S2) - mnl_attr_put_u16(nlh, WGDEVICE_A_S2, dev->response_packet_junk_size); - if (dev->flags & WGDEVICE_HAS_H1) - mnl_attr_put_u32(nlh, WGDEVICE_A_H1, dev->init_packet_magic_header); - if (dev->flags & WGDEVICE_HAS_H2) - mnl_attr_put_u32(nlh, WGDEVICE_A_H2, dev->response_packet_magic_header); - if (dev->flags & WGDEVICE_HAS_H3) - mnl_attr_put_u32(nlh, WGDEVICE_A_H3, dev->underload_packet_magic_header); - if (dev->flags & WGDEVICE_HAS_H4) - mnl_attr_put_u32(nlh, WGDEVICE_A_H4, dev->transport_packet_magic_header); - if (dev->flags & WGDEVICE_HAS_FWMARK) - mnl_attr_put_u32(nlh, WGDEVICE_A_FWMARK, dev->fwmark); - if (dev->flags & WGDEVICE_REPLACE_PEERS) - flags |= WGDEVICE_F_REPLACE_PEERS; - if (flags) - mnl_attr_put_u32(nlh, WGDEVICE_A_FLAGS, flags); - } - if (!dev->first_peer) - goto send; - peers_nest = peer_nest = allowedips_nest = allowedip_nest = NULL; - peers_nest = mnl_attr_nest_start(nlh, WGDEVICE_A_PEERS); - for (peer = peer ? peer : dev->first_peer; peer; peer = peer->next_peer) { - uint32_t flags = 0; + if (dev->flags & WGDEVICE_HAS_PRIVATE_KEY) + mnl_attr_put( + nlh, WGDEVICE_A_PRIVATE_KEY, sizeof(dev->private_key), dev->private_key); + if (dev->flags & WGDEVICE_HAS_LISTEN_PORT) + mnl_attr_put_u16(nlh, WGDEVICE_A_LISTEN_PORT, dev->listen_port); + if (dev->flags & WGDEVICE_HAS_JC) + mnl_attr_put_u16(nlh, WGDEVICE_A_JC, dev->junk_packet_count); + if (dev->flags & WGDEVICE_HAS_JMIN) + mnl_attr_put_u16(nlh, WGDEVICE_A_JMIN, dev->junk_packet_min_size); + if (dev->flags & WGDEVICE_HAS_JMAX) + mnl_attr_put_u16(nlh, WGDEVICE_A_JMAX, dev->junk_packet_max_size); + if (dev->flags & WGDEVICE_HAS_S1) + mnl_attr_put_u16(nlh, WGDEVICE_A_S1, dev->init_packet_junk_size); + if (dev->flags & WGDEVICE_HAS_S2) + mnl_attr_put_u16(nlh, WGDEVICE_A_S2, dev->response_packet_junk_size); + if (dev->flags & WGDEVICE_HAS_H1) + mnl_attr_put_u32(nlh, WGDEVICE_A_H1, dev->init_packet_magic_header); + if (dev->flags & WGDEVICE_HAS_H2) + mnl_attr_put_u32(nlh, WGDEVICE_A_H2, dev->response_packet_magic_header); + if (dev->flags & WGDEVICE_HAS_H3) + mnl_attr_put_u32(nlh, WGDEVICE_A_H3, dev->underload_packet_magic_header); + if (dev->flags & WGDEVICE_HAS_H4) + mnl_attr_put_u32(nlh, WGDEVICE_A_H4, dev->transport_packet_magic_header); + if (dev->flags & WGDEVICE_HAS_FWMARK) + mnl_attr_put_u32(nlh, WGDEVICE_A_FWMARK, dev->fwmark); + if (dev->flags & WGDEVICE_REPLACE_PEERS) + flags |= WGDEVICE_F_REPLACE_PEERS; + if (flags) + mnl_attr_put_u32(nlh, WGDEVICE_A_FLAGS, flags); + } + if (!dev->first_peer) + goto send; + peers_nest = peer_nest = allowedips_nest = allowedip_nest = NULL; + peers_nest = mnl_attr_nest_start(nlh, WGDEVICE_A_PEERS); + for (peer = peer ? peer : dev->first_peer; peer; peer = peer->next_peer) + { + uint32_t flags = 0; - peer_nest = mnl_attr_nest_start_check(nlh, SOCKET_BUFFER_SIZE, 0); - if (!peer_nest) - goto toobig_peers; - if (!mnl_attr_put_check(nlh, SOCKET_BUFFER_SIZE, WGPEER_A_PUBLIC_KEY, sizeof(peer->public_key), peer->public_key)) - goto toobig_peers; - if (peer->flags & WGPEER_REMOVE_ME) - flags |= WGPEER_F_REMOVE_ME; - if (!allowedip) { - if (peer->flags & WGPEER_REPLACE_ALLOWEDIPS) - flags |= WGPEER_F_REPLACE_ALLOWEDIPS; - 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)) - goto toobig_peers; - } - 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; - } else if (peer->endpoint.addr.sa_family == AF_INET6) { - 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; - } - } - if (peer->flags & WGPEER_HAS_ADVANCED_SECURITY) { - if (peer->advanced_security) - mnl_attr_put_check(nlh, SOCKET_BUFFER_SIZE, WGPEER_A_ADVANCED_SECURITY, 0, NULL); - flags |= WGPEER_F_HAS_ADVANCED_SECURITY; - } - if (flags) { - if (!mnl_attr_put_u32_check(nlh, SOCKET_BUFFER_SIZE, WGPEER_A_FLAGS, flags)) - goto toobig_peers; - } - if (peer->first_allowedip) { - if (!allowedip) - allowedip = peer->first_allowedip; - allowedips_nest = mnl_attr_nest_start_check(nlh, SOCKET_BUFFER_SIZE, WGPEER_A_ALLOWEDIPS); - if (!allowedips_nest) - goto toobig_allowedips; - for (; allowedip; allowedip = allowedip->next_allowedip) { - allowedip_nest = mnl_attr_nest_start_check(nlh, SOCKET_BUFFER_SIZE, 0); - if (!allowedip_nest) - goto toobig_allowedips; - if (!mnl_attr_put_u16_check(nlh, SOCKET_BUFFER_SIZE, WGALLOWEDIP_A_FAMILY, allowedip->family)) - goto toobig_allowedips; - 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; - } 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; - mnl_attr_nest_end(nlh, allowedip_nest); - allowedip_nest = NULL; - } - mnl_attr_nest_end(nlh, allowedips_nest); - allowedips_nest = NULL; - } + peer_nest = mnl_attr_nest_start_check(nlh, SOCKET_BUFFER_SIZE, 0); + if (!peer_nest) + goto toobig_peers; + if (!mnl_attr_put_check( + nlh, + SOCKET_BUFFER_SIZE, + WGPEER_A_PUBLIC_KEY, + sizeof(peer->public_key), + peer->public_key)) + goto toobig_peers; + if (peer->flags & WGPEER_REMOVE_ME) + flags |= WGPEER_F_REMOVE_ME; + if (!allowedip) + { + if (peer->flags & WGPEER_REPLACE_ALLOWEDIPS) + flags |= WGPEER_F_REPLACE_ALLOWEDIPS; + 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)) + goto toobig_peers; + } + 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; + } + else if (peer->endpoint.addr.sa_family == AF_INET6) + { + 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; + } + } + if (peer->flags & WGPEER_HAS_ADVANCED_SECURITY) + { + if (peer->advanced_security) + mnl_attr_put_check( + nlh, SOCKET_BUFFER_SIZE, WGPEER_A_ADVANCED_SECURITY, 0, NULL); + flags |= WGPEER_F_HAS_ADVANCED_SECURITY; + } + if (flags) + { + if (!mnl_attr_put_u32_check(nlh, SOCKET_BUFFER_SIZE, WGPEER_A_FLAGS, flags)) + goto toobig_peers; + } + if (peer->first_allowedip) + { + if (!allowedip) + allowedip = peer->first_allowedip; + allowedips_nest = + mnl_attr_nest_start_check(nlh, SOCKET_BUFFER_SIZE, WGPEER_A_ALLOWEDIPS); + if (!allowedips_nest) + goto toobig_allowedips; + for (; allowedip; allowedip = allowedip->next_allowedip) + { + allowedip_nest = mnl_attr_nest_start_check(nlh, SOCKET_BUFFER_SIZE, 0); + if (!allowedip_nest) + goto toobig_allowedips; + if (!mnl_attr_put_u16_check( + nlh, SOCKET_BUFFER_SIZE, WGALLOWEDIP_A_FAMILY, allowedip->family)) + goto toobig_allowedips; + 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; + } + 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; + mnl_attr_nest_end(nlh, allowedip_nest); + allowedip_nest = NULL; + } + mnl_attr_nest_end(nlh, allowedips_nest); + allowedips_nest = NULL; + } - mnl_attr_nest_end(nlh, peer_nest); - peer_nest = NULL; - } - mnl_attr_nest_end(nlh, peers_nest); - peers_nest = NULL; - goto send; + mnl_attr_nest_end(nlh, peer_nest); + peer_nest = NULL; + } + mnl_attr_nest_end(nlh, peers_nest); + peers_nest = NULL; + goto send; toobig_allowedips: - if (allowedip_nest) - mnl_attr_nest_cancel(nlh, allowedip_nest); - if (allowedips_nest) - mnl_attr_nest_end(nlh, allowedips_nest); - mnl_attr_nest_end(nlh, peer_nest); - mnl_attr_nest_end(nlh, peers_nest); - goto send; + if (allowedip_nest) + mnl_attr_nest_cancel(nlh, allowedip_nest); + if (allowedips_nest) + mnl_attr_nest_end(nlh, allowedips_nest); + mnl_attr_nest_end(nlh, peer_nest); + mnl_attr_nest_end(nlh, peers_nest); + goto send; toobig_peers: - if (peer_nest) - mnl_attr_nest_cancel(nlh, peer_nest); - mnl_attr_nest_end(nlh, peers_nest); - goto send; + if (peer_nest) + mnl_attr_nest_cancel(nlh, peer_nest); + mnl_attr_nest_end(nlh, peers_nest); + goto send; send: - if (mnlg_socket_send(nlg, nlh) < 0) { - ret = -errno; - goto out; - } - errno = 0; - if (mnlg_socket_recv_run(nlg, NULL, NULL) < 0) { - ret = errno ? -errno : -EINVAL; - goto out; - } - if (peer) - goto again; + if (mnlg_socket_send(nlg, nlh) < 0) + { + ret = -errno; + goto out; + } + errno = 0; + if (mnlg_socket_recv_run(nlg, NULL, NULL) < 0) + { + ret = errno ? -errno : -EINVAL; + goto out; + } + if (peer) + goto again; out: - mnlg_socket_close(nlg); - errno = -ret; - return ret; + mnlg_socket_close(nlg); + errno = -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)) { - case WGALLOWEDIP_A_UNSPEC: - break; - case WGALLOWEDIP_A_FAMILY: - if (!mnl_attr_validate(attr, MNL_TYPE_U16)) - allowedip->family = mnl_attr_get_u16(attr); - break; - case WGALLOWEDIP_A_IPADDR: - if (mnl_attr_get_payload_len(attr) == sizeof(allowedip->ip4)) - memcpy(&allowedip->ip4, mnl_attr_get_payload(attr), sizeof(allowedip->ip4)); - else if (mnl_attr_get_payload_len(attr) == sizeof(allowedip->ip6)) - memcpy(&allowedip->ip6, mnl_attr_get_payload(attr), sizeof(allowedip->ip6)); - break; - case WGALLOWEDIP_A_CIDR_MASK: - if (!mnl_attr_validate(attr, MNL_TYPE_U8)) - allowedip->cidr = mnl_attr_get_u8(attr); - break; - } + switch (mnl_attr_get_type(attr)) + { + case WGALLOWEDIP_A_UNSPEC: + break; + case WGALLOWEDIP_A_FAMILY: + if (!mnl_attr_validate(attr, MNL_TYPE_U16)) + allowedip->family = mnl_attr_get_u16(attr); + break; + case WGALLOWEDIP_A_IPADDR: + if (mnl_attr_get_payload_len(attr) == sizeof(allowedip->ip4)) + memcpy(&allowedip->ip4, mnl_attr_get_payload(attr), sizeof(allowedip->ip4)); + else if (mnl_attr_get_payload_len(attr) == sizeof(allowedip->ip6)) + memcpy(&allowedip->ip6, mnl_attr_get_payload(attr), sizeof(allowedip->ip6)); + break; + case WGALLOWEDIP_A_CIDR_MASK: + if (!mnl_attr_validate(attr, MNL_TYPE_U8)) + allowedip->cidr = mnl_attr_get_u8(attr); + break; + } - 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 wgallowedip *new_allowedip = calloc(1, sizeof(*new_allowedip)); - int ret; + struct wgpeer* peer = data; + struct wgallowedip* new_allowedip = calloc(1, sizeof(*new_allowedip)); + int ret; - if (!new_allowedip) { - perror("calloc"); - return MNL_CB_ERROR; - } - if (!peer->first_allowedip) - peer->first_allowedip = peer->last_allowedip = new_allowedip; - else { - peer->last_allowedip->next_allowedip = new_allowedip; - peer->last_allowedip = new_allowedip; - } - ret = mnl_attr_parse_nested(attr, parse_allowedip, new_allowedip); - if (!ret) - return ret; - 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_OK; + if (!new_allowedip) + { + perror("calloc"); + return MNL_CB_ERROR; + } + if (!peer->first_allowedip) + peer->first_allowedip = peer->last_allowedip = new_allowedip; + else + { + peer->last_allowedip->next_allowedip = new_allowedip; + peer->last_allowedip = new_allowedip; + } + ret = mnl_attr_parse_nested(attr, parse_allowedip, new_allowedip); + if (!ret) + return ret; + 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_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)) { - case WGPEER_A_UNSPEC: - break; - case WGPEER_A_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)); - peer->flags |= WGPEER_HAS_PUBLIC_KEY; - } - break; - case WGPEER_A_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)); - if (!key_is_zero(peer->preshared_key)) - peer->flags |= WGPEER_HAS_PRESHARED_KEY; - } - break; - case WGPEER_A_ENDPOINT: { - struct sockaddr *addr; + switch (mnl_attr_get_type(attr)) + { + case WGPEER_A_UNSPEC: + break; + case WGPEER_A_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)); + peer->flags |= WGPEER_HAS_PUBLIC_KEY; + } + break; + case WGPEER_A_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)); + if (!key_is_zero(peer->preshared_key)) + peer->flags |= WGPEER_HAS_PRESHARED_KEY; + } + break; + case WGPEER_A_ENDPOINT: { + struct sockaddr* addr; - if (mnl_attr_get_payload_len(attr) < sizeof(*addr)) - break; - addr = mnl_attr_get_payload(attr); - 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)); - 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)); - break; - } - case WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL: - if (!mnl_attr_validate(attr, MNL_TYPE_U16)) - peer->persistent_keepalive_interval = mnl_attr_get_u16(attr); - break; - case WGPEER_A_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)); - break; - case WGPEER_A_RX_BYTES: - if (!mnl_attr_validate(attr, MNL_TYPE_U64)) - peer->rx_bytes = mnl_attr_get_u64(attr); - break; - case WGPEER_A_TX_BYTES: - if (!mnl_attr_validate(attr, MNL_TYPE_U64)) - peer->tx_bytes = mnl_attr_get_u64(attr); - break; - case WGPEER_A_FLAGS: - if (!mnl_attr_validate(attr, MNL_TYPE_U32)) { - uint32_t flags = mnl_attr_get_u32(attr); + if (mnl_attr_get_payload_len(attr) < sizeof(*addr)) + break; + addr = mnl_attr_get_payload(attr); + 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)); + 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)); + break; + } + case WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL: + if (!mnl_attr_validate(attr, MNL_TYPE_U16)) + peer->persistent_keepalive_interval = mnl_attr_get_u16(attr); + break; + case WGPEER_A_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)); + break; + case WGPEER_A_RX_BYTES: + if (!mnl_attr_validate(attr, MNL_TYPE_U64)) + peer->rx_bytes = mnl_attr_get_u64(attr); + break; + case WGPEER_A_TX_BYTES: + if (!mnl_attr_validate(attr, MNL_TYPE_U64)) + peer->tx_bytes = mnl_attr_get_u64(attr); + break; + case WGPEER_A_FLAGS: + if (!mnl_attr_validate(attr, MNL_TYPE_U32)) + { + uint32_t flags = mnl_attr_get_u32(attr); - if (flags & WGPEER_F_HAS_ADVANCED_SECURITY && !(peer->flags & WGPEER_HAS_ADVANCED_SECURITY)) { - peer->flags |= WGPEER_HAS_ADVANCED_SECURITY; - peer->advanced_security = false; - } - } - break; - case WGPEER_A_ADVANCED_SECURITY: - if (!mnl_attr_validate(attr, MNL_TYPE_FLAG)) { - peer->advanced_security = true; + if (flags & WGPEER_F_HAS_ADVANCED_SECURITY && + !(peer->flags & WGPEER_HAS_ADVANCED_SECURITY)) + { + peer->flags |= WGPEER_HAS_ADVANCED_SECURITY; + peer->advanced_security = false; + } + } + break; + case WGPEER_A_ADVANCED_SECURITY: + if (!mnl_attr_validate(attr, MNL_TYPE_FLAG)) + { + peer->advanced_security = true; - if (!(peer->flags & WGPEER_HAS_ADVANCED_SECURITY)) { - peer->flags |= WGPEER_HAS_ADVANCED_SECURITY; - } - } - break; - case WGPEER_A_ALLOWEDIPS: - return mnl_attr_parse_nested(attr, parse_allowedips, peer); - } + if (!(peer->flags & WGPEER_HAS_ADVANCED_SECURITY)) + { + peer->flags |= WGPEER_HAS_ADVANCED_SECURITY; + } + } + break; + case WGPEER_A_ALLOWEDIPS: + return mnl_attr_parse_nested(attr, parse_allowedips, peer); + } - 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 wgpeer *new_peer = calloc(1, sizeof(*new_peer)); - int ret; + struct wgdevice* device = data; + struct wgpeer* new_peer = calloc(1, sizeof(*new_peer)); + int ret; - if (!new_peer) { - perror("calloc"); - return MNL_CB_ERROR; - } - if (!device->first_peer) - device->first_peer = device->last_peer = new_peer; - else { - device->last_peer->next_peer = new_peer; - device->last_peer = new_peer; - } - ret = mnl_attr_parse_nested(attr, parse_peer, new_peer); - if (!ret) - return ret; - if (!(new_peer->flags & WGPEER_HAS_PUBLIC_KEY)) - return MNL_CB_ERROR; - return MNL_CB_OK; + if (!new_peer) + { + perror("calloc"); + return MNL_CB_ERROR; + } + if (!device->first_peer) + device->first_peer = device->last_peer = new_peer; + else + { + device->last_peer->next_peer = new_peer; + device->last_peer = new_peer; + } + ret = mnl_attr_parse_nested(attr, parse_peer, new_peer); + if (!ret) + return ret; + if (!(new_peer->flags & WGPEER_HAS_PUBLIC_KEY)) + return MNL_CB_ERROR; + 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)) { - case WGDEVICE_A_UNSPEC: - break; - case WGDEVICE_A_IFINDEX: - if (!mnl_attr_validate(attr, MNL_TYPE_U32)) - device->ifindex = mnl_attr_get_u32(attr); - break; - case WGDEVICE_A_IFNAME: - if (!mnl_attr_validate(attr, MNL_TYPE_STRING)) { - strncpy(device->name, mnl_attr_get_str(attr), sizeof(device->name) - 1); - device->name[sizeof(device->name) - 1] = '\0'; - } - break; - case WGDEVICE_A_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)); - device->flags |= WGDEVICE_HAS_PRIVATE_KEY; - } - break; - case WGDEVICE_A_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)); - device->flags |= WGDEVICE_HAS_PUBLIC_KEY; - } - break; - case WGDEVICE_A_LISTEN_PORT: - if (!mnl_attr_validate(attr, MNL_TYPE_U16)) - device->listen_port = mnl_attr_get_u16(attr); - break; - case WGDEVICE_A_FWMARK: - if (!mnl_attr_validate(attr, MNL_TYPE_U32)) - device->fwmark = mnl_attr_get_u32(attr); - break; - case WGDEVICE_A_PEERS: - return mnl_attr_parse_nested(attr, parse_peers, device); - case WGDEVICE_A_JC: - if (!mnl_attr_validate(attr, MNL_TYPE_U16)) { - device->junk_packet_count = mnl_attr_get_u16(attr); - device->flags |= WGDEVICE_HAS_JC; - } - break; - case WGDEVICE_A_JMIN: - if (!mnl_attr_validate(attr, MNL_TYPE_U16)) { - device->junk_packet_min_size = mnl_attr_get_u16(attr); - device->flags |= WGDEVICE_HAS_JMIN; - } - break; - case WGDEVICE_A_JMAX: - if (!mnl_attr_validate(attr, MNL_TYPE_U16)) { - device->junk_packet_max_size = mnl_attr_get_u16(attr); - device->flags |= WGDEVICE_HAS_JMAX; - } - break; - case WGDEVICE_A_S1: - if (!mnl_attr_validate(attr, MNL_TYPE_U16)) { - device->init_packet_junk_size = mnl_attr_get_u16(attr); - device->flags |= WGDEVICE_HAS_S1; - } - break; - case WGDEVICE_A_S2: - if (!mnl_attr_validate(attr, MNL_TYPE_U16)) { - device->response_packet_junk_size = mnl_attr_get_u16(attr); - device->flags |= WGDEVICE_HAS_S2; - } - break; - case WGDEVICE_A_H1: - if (!mnl_attr_validate(attr, MNL_TYPE_U32)) { - device->init_packet_magic_header = mnl_attr_get_u32(attr); - device->flags |= WGDEVICE_HAS_H1; - } - break; - case WGDEVICE_A_H2: - if (!mnl_attr_validate(attr, MNL_TYPE_U32)) { - device->response_packet_magic_header = mnl_attr_get_u32(attr); - device->flags |= WGDEVICE_HAS_H2; - } - break; - case WGDEVICE_A_H3: - if (!mnl_attr_validate(attr, MNL_TYPE_U32)) { - device->underload_packet_magic_header = mnl_attr_get_u32(attr); - device->flags |= WGDEVICE_HAS_H3; - } - break; - case WGDEVICE_A_H4: - if (!mnl_attr_validate(attr, MNL_TYPE_U32)) { - device->transport_packet_magic_header = mnl_attr_get_u32(attr); - device->flags |= WGDEVICE_HAS_H4; - } - break; - } + switch (mnl_attr_get_type(attr)) + { + case WGDEVICE_A_UNSPEC: + break; + case WGDEVICE_A_IFINDEX: + if (!mnl_attr_validate(attr, MNL_TYPE_U32)) + device->ifindex = mnl_attr_get_u32(attr); + break; + case WGDEVICE_A_IFNAME: + if (!mnl_attr_validate(attr, MNL_TYPE_STRING)) + { + strncpy(device->name, mnl_attr_get_str(attr), sizeof(device->name) - 1); + device->name[sizeof(device->name) - 1] = '\0'; + } + break; + case WGDEVICE_A_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)); + device->flags |= WGDEVICE_HAS_PRIVATE_KEY; + } + break; + case WGDEVICE_A_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)); + device->flags |= WGDEVICE_HAS_PUBLIC_KEY; + } + break; + case WGDEVICE_A_LISTEN_PORT: + if (!mnl_attr_validate(attr, MNL_TYPE_U16)) + device->listen_port = mnl_attr_get_u16(attr); + break; + case WGDEVICE_A_FWMARK: + if (!mnl_attr_validate(attr, MNL_TYPE_U32)) + device->fwmark = mnl_attr_get_u32(attr); + break; + case WGDEVICE_A_PEERS: + return mnl_attr_parse_nested(attr, parse_peers, device); + case WGDEVICE_A_JC: + if (!mnl_attr_validate(attr, MNL_TYPE_U16)) + { + device->junk_packet_count = mnl_attr_get_u16(attr); + device->flags |= WGDEVICE_HAS_JC; + } + break; + case WGDEVICE_A_JMIN: + if (!mnl_attr_validate(attr, MNL_TYPE_U16)) + { + device->junk_packet_min_size = mnl_attr_get_u16(attr); + device->flags |= WGDEVICE_HAS_JMIN; + } + break; + case WGDEVICE_A_JMAX: + if (!mnl_attr_validate(attr, MNL_TYPE_U16)) + { + device->junk_packet_max_size = mnl_attr_get_u16(attr); + device->flags |= WGDEVICE_HAS_JMAX; + } + break; + case WGDEVICE_A_S1: + if (!mnl_attr_validate(attr, MNL_TYPE_U16)) + { + device->init_packet_junk_size = mnl_attr_get_u16(attr); + device->flags |= WGDEVICE_HAS_S1; + } + break; + case WGDEVICE_A_S2: + if (!mnl_attr_validate(attr, MNL_TYPE_U16)) + { + device->response_packet_junk_size = mnl_attr_get_u16(attr); + device->flags |= WGDEVICE_HAS_S2; + } + break; + case WGDEVICE_A_H1: + if (!mnl_attr_validate(attr, MNL_TYPE_U32)) + { + device->init_packet_magic_header = mnl_attr_get_u32(attr); + device->flags |= WGDEVICE_HAS_H1; + } + break; + case WGDEVICE_A_H2: + if (!mnl_attr_validate(attr, MNL_TYPE_U32)) + { + device->response_packet_magic_header = mnl_attr_get_u32(attr); + device->flags |= WGDEVICE_HAS_H2; + } + break; + case WGDEVICE_A_H3: + if (!mnl_attr_validate(attr, MNL_TYPE_U32)) + { + device->underload_packet_magic_header = mnl_attr_get_u32(attr); + device->flags |= WGDEVICE_HAS_H3; + } + break; + case WGDEVICE_A_H4: + if (!mnl_attr_validate(attr, MNL_TYPE_U32)) + { + device->transport_packet_magic_header = mnl_attr_get_u32(attr); + device->flags |= WGDEVICE_HAS_H4; + } - return MNL_CB_OK; + 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; + } + + 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) { - if (memcmp(peer->public_key, peer->next_peer->public_key, sizeof(peer->public_key))) { - peer = peer->next_peer; - continue; - } - if (!peer->first_allowedip) { - peer->first_allowedip = peer->next_peer->first_allowedip; - peer->last_allowedip = peer->next_peer->last_allowedip; - } else { - peer->last_allowedip->next_allowedip = peer->next_peer->first_allowedip; - peer->last_allowedip = peer->next_peer->last_allowedip; - } - old_next_peer = peer->next_peer; - peer->next_peer = old_next_peer->next_peer; - free(old_next_peer); - } + while (peer && peer->next_peer) + { + if (memcmp( + peer->public_key, peer->next_peer->public_key, sizeof(peer->public_key))) + { + peer = peer->next_peer; + continue; + } + if (!peer->first_allowedip) + { + peer->first_allowedip = peer->next_peer->first_allowedip; + peer->last_allowedip = peer->next_peer->last_allowedip; + } + else + { + peer->last_allowedip->next_allowedip = peer->next_peer->first_allowedip; + peer->last_allowedip = peer->next_peer->last_allowedip; + } + old_next_peer = peer->next_peer; + peer->next_peer = old_next_peer->next_peer; + free(old_next_peer); + } } -static int kernel_get_device(struct wgdevice **device, const char *iface) +static int kernel_get_device(struct wgdevice** device, const char* iface) { - int ret; - struct nlmsghdr *nlh; - struct mnlg_socket *nlg; + int ret; + struct nlmsghdr* nlh; + struct mnlg_socket* nlg; - /* libmnl doesn't check the buffer size, so enforce that before using. */ - if (strlen(iface) >= IFNAMSIZ) { - errno = ENAMETOOLONG; - return -ENAMETOOLONG; - } + /* libmnl doesn't check the buffer size, so enforce that before using. */ + if (strlen(iface) >= IFNAMSIZ) + { + errno = ENAMETOOLONG; + return -ENAMETOOLONG; + } try_again: - ret = 0; - *device = calloc(1, sizeof(**device)); - if (!*device) - return -errno; + ret = 0; + *device = calloc(1, sizeof(**device)); + if (!*device) + return -errno; - nlg = mnlg_socket_open(WG_GENL_NAME, WG_GENL_VERSION); - if (!nlg) { - free_wgdevice(*device); - *device = NULL; - return -errno; - } + nlg = mnlg_socket_open(WG_GENL_NAME, WG_GENL_VERSION); + if (!nlg) + { + free_wgdevice(*device); + *device = NULL; + return -errno; + } - 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); - if (mnlg_socket_send(nlg, nlh) < 0) { - ret = -errno; - goto out; - } - errno = 0; - if (mnlg_socket_recv_run(nlg, read_device_cb, *device) < 0) { - ret = errno ? -errno : -EINVAL; - goto out; - } - coalesce_peers(*device); + 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); + if (mnlg_socket_send(nlg, nlh) < 0) + { + ret = -errno; + goto out; + } + errno = 0; + if (mnlg_socket_recv_run(nlg, read_device_cb, *device) < 0) + { + ret = errno ? -errno : -EINVAL; + goto out; + } + coalesce_peers(*device); out: - if (nlg) - mnlg_socket_close(nlg); - if (ret) { - free_wgdevice(*device); - if (ret == -EINTR) - goto try_again; - *device = NULL; - } - errno = -ret; - return ret; + if (nlg) + mnlg_socket_close(nlg); + if (ret) + { + free_wgdevice(*device); + if (ret == -EINTR) + goto try_again; + *device = NULL; + } + errno = -ret; + return ret; } diff --git a/src/showconf.c b/src/showconf.c index 424b2be..473416c 100644 --- a/src/showconf.c +++ b/src/showconf.c @@ -65,6 +65,25 @@ int showconf_main(int argc, const char *argv[]) if (device->flags & WGDEVICE_HAS_H4) 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"); for_each_wgpeer(device, peer) { key_to_base64(base64, peer->public_key); diff --git a/src/uapi/linux/linux/wireguard.h b/src/uapi/linux/linux/wireguard.h index 8d38c90..c4849bb 100644 --- a/src/uapi/linux/linux/wireguard.h +++ b/src/uapi/linux/linux/wireguard.h @@ -191,6 +191,15 @@ enum wgdevice_attribute { WGDEVICE_A_H2, WGDEVICE_A_H3, 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_LAST };