diff options
-rw-r--r-- | ip_util.h | 125 | ||||
-rw-r--r-- | netlink.c | 5 | ||||
-rw-r--r-- | netlink.h | 7 | ||||
-rw-r--r-- | netlink_race_test.c | 67 |
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 @@ -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; @@ -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; +} |