aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ip_util.h125
-rw-r--r--netlink.c5
-rw-r--r--netlink.h7
-rw-r--r--netlink_race_test.c67
4 files changed, 200 insertions, 4 deletions
diff --git a/ip_util.h b/ip_util.h
new file mode 100644
index 0000000..5550610
--- /dev/null
+++ b/ip_util.h
@@ -0,0 +1,125 @@
+#ifndef __IP_UTIL_H__
+#define __IP_UTIL_H__
+
+#include <arpa/inet.h>
+#include <printf.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#pragma GCC diagnostic ignored "-Wformat"
+#pragma GCC diagnostic ignored "-Wformat-extra-args"
+
+static int __printf_arginfo(const struct printf_info *info, size_t n,
+ int *argtypes, int *sz)
+{
+ if (n > 0)
+ argtypes[0] = PA_POINTER;
+
+ return 1;
+}
+
+static int __printf_output_b(FILE *stream, const struct printf_info *info,
+ const void *const *args)
+{
+ uint64_t value = 0;
+ char buf[8 * sizeof(uintmax_t) + 1] = { 0 };
+
+ if (info->width == 0 || info->width % 8 != 0)
+ return -1;
+
+ memcpy(&value, *(unsigned char **)(args[0]), info->width / 8);
+
+ for (int i = 0; i < info->width; ++i)
+ buf[info->width - 1 - i] = '0' + ((value >> i) & 0x1);
+
+ return fprintf(stream, "%s", buf);
+}
+
+static int __printf_output_y(FILE *stream, const struct printf_info *info,
+ const void *const *args)
+{
+ char buf[INET_ADDRSTRLEN];
+ struct in_addr ip;
+
+ if (!*(unsigned char **)(args[0]))
+ return fprintf(stream, "(nil)", buf);
+
+ memcpy(&ip, *(unsigned char **)(args[0]), sizeof ip);
+
+ inet_ntop(AF_INET, &ip.s_addr, buf, sizeof buf);
+
+ return fprintf(stream, "%s", buf);
+}
+
+static int __printf_output_Y(FILE *stream, const struct printf_info *info,
+ const void *const *args)
+{
+ char buf[INET6_ADDRSTRLEN];
+ struct in6_addr ip;
+
+ if (!*(unsigned char **)(args[0]))
+ return fprintf(stream, "(nil)", buf);
+
+ memcpy(&ip, *(unsigned char **)(args[0]), sizeof ip);
+
+ inet_ntop(AF_INET6, &ip.s6_addr, buf, sizeof buf);
+
+ return fprintf(stream, "%s", buf);
+}
+
+static void __attribute__((constructor)) __custom_printf_init()
+{
+ register_printf_specifier('b', __printf_output_b, __printf_arginfo);
+ register_printf_specifier('y', __printf_output_y, __printf_arginfo);
+ register_printf_specifier('Y', __printf_output_Y, __printf_arginfo);
+}
+
+struct ip_from {
+ unsigned char ip[16];
+ struct ip_from *next;
+};
+
+static struct ip_from *start = NULL;
+static struct ip_from *end = NULL;
+
+static unsigned char *ip_from(uint8_t bits, char *ip_str)
+{
+ struct ip_from *res = malloc(sizeof *res);
+
+ inet_pton(bits == 32 ? AF_INET : AF_INET6, ip_str, &res->ip);
+
+ if (!end)
+ start = end = res;
+ else
+ end->next = res;
+
+ end = res;
+ res->next = NULL;
+
+ return res->ip;
+}
+
+static struct in_addr *ip4_from(char *ip_str)
+{
+ return (struct in_addr *)ip_from(32, ip_str);
+}
+
+static struct in6_addr *ip6_from(char *ip_str)
+{
+ return (struct in6_addr *)ip_from(128, ip_str);
+}
+
+static void __attribute__((destructor)) __free_ip_froms()
+{
+ struct ip_from *cur = start, *next;
+ while (cur) {
+ next = cur->next;
+ free(cur);
+ cur = next;
+ }
+}
+
+#endif
diff --git a/netlink.c b/netlink.c
index aea8eac..4513bed 100644
--- a/netlink.c
+++ b/netlink.c
@@ -49,7 +49,8 @@ enum wgdevice_attribute {
enum wgpeer_flag {
WGPEER_F_REMOVE_ME = 1U << 0,
- WGPEER_F_REPLACE_ALLOWEDIPS = 1U << 1
+ WGPEER_F_REPLACE_ALLOWEDIPS = 1U << 1,
+ WGPEER_F_NO_CREATE = 1U << 2,
};
enum wgpeer_attribute {
WGPEER_A_UNSPEC,
@@ -531,6 +532,8 @@ again:
goto toobig_peers;
if (peer->flags & WGPEER_REMOVE_ME)
flags |= WGPEER_F_REMOVE_ME;
+ if (peer->flags & WGPEER_NO_CREATE)
+ flags |= WGPEER_F_NO_CREATE;
if (!allowedip) {
if (peer->flags & WGPEER_REPLACE_ALLOWEDIPS)
flags |= WGPEER_F_REPLACE_ALLOWEDIPS;
diff --git a/netlink.h b/netlink.h
index b4a6a63..887263a 100644
--- a/netlink.h
+++ b/netlink.h
@@ -36,9 +36,10 @@ typedef struct wg_allowedip {
enum wg_peer_flags {
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_NO_CREATE = 1U << 2,
+ WGPEER_HAS_PUBLIC_KEY = 1U << 3,
+ WGPEER_HAS_PRESHARED_KEY = 1U << 4,
+ WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL = 1U << 5,
};
typedef struct wg_peer {
diff --git a/netlink_race_test.c b/netlink_race_test.c
new file mode 100644
index 0000000..8ffe445
--- /dev/null
+++ b/netlink_race_test.c
@@ -0,0 +1,67 @@
+#include <stdio.h>
+#include <string.h>
+
+#include "netlink.h"
+#include "ip_util.h"
+
+static void add_allowed_ips(wg_key pubkey, struct in_addr *ipv4,
+ struct in6_addr *ipv6)
+{
+ wg_allowedip allowed_v4, allowed_v6;
+ wg_peer peer = { .flags = WGPEER_NO_CREATE };
+ wg_device dev = { .first_peer = &peer };
+
+ strcpy(dev.name, "wg0");
+ memcpy(peer.public_key, pubkey, sizeof peer.public_key);
+ wg_allowedip **cur = &peer.first_allowedip;
+
+ if (ipv4) {
+ allowed_v4 = (wg_allowedip){
+ .family = AF_INET,
+ .cidr = 32,
+ .ip4 = *ipv4,
+ };
+ *cur = &allowed_v4;
+ cur = &allowed_v4.next_allowedip;
+ }
+
+ if (ipv6) {
+ allowed_v6 = (wg_allowedip){
+ .family = AF_INET6,
+ .cidr = 128,
+ .ip6 = *ipv6,
+ };
+ *cur = &allowed_v6;
+ }
+
+ if (wg_set_device(&dev))
+ perror("wg_set_device()");
+}
+
+int main(void)
+{
+ struct wg_device *device;
+ if (wg_get_device(&device, "wg0")) {
+ perror("Unable to access interface wg0");
+ return 1;
+ }
+
+ if (!device->first_peer) {
+ wg_free_device(device);
+ return 1;
+ }
+
+ wg_key_b64_string str;
+ wg_key_to_base64(str, device->first_peer->public_key);
+ printf("Public key: %s\n", str);
+
+ char cmd[4096];
+ sprintf(cmd, "wg set wg0 peer %s remove", str);
+ system(cmd);
+
+ add_allowed_ips(device->first_peer->public_key, ip4_from("192.168.1.1"),
+ NULL);
+
+ wg_free_device(device);
+ return 0;
+}