aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Gschwantner <tharre3@gmail.com>2019-04-23 22:06:22 +0200
committerThomas Gschwantner <tharre3@gmail.com>2019-06-12 19:05:33 +0200
commit0f06351cb0085c79b617a4acc0bd6749b53c638e (patch)
tree13f6eb367f4ed326817741953166975b912b214e
parentradix-trie: implement ipp_addrnd_* and related (diff)
downloadwg-dynamic-0f06351cb0085c79b617a4acc0bd6749b53c638e.tar.xz
wg-dynamic-0f06351cb0085c79b617a4acc0bd6749b53c638e.zip
Implement basic lease management
-rw-r--r--Makefile2
-rw-r--r--common.c12
-rw-r--r--common.h15
-rw-r--r--khash.h21
-rw-r--r--lease.c210
-rw-r--r--lease.h68
-rw-r--r--random.c92
-rw-r--r--random.h13
-rw-r--r--wg-dynamic-client.c50
-rw-r--r--wg-dynamic-server.c215
10 files changed, 536 insertions, 162 deletions
diff --git a/Makefile b/Makefile
index 575fc38..11deced 100644
--- a/Makefile
+++ b/Makefile
@@ -46,7 +46,7 @@ endif
all: wg-dynamic-server wg-dynamic-client
wg-dynamic-client: wg-dynamic-client.o netlink.o common.o
-wg-dynamic-server: wg-dynamic-server.o netlink.o radix-trie.o common.o
+wg-dynamic-server: wg-dynamic-server.o netlink.o radix-trie.o common.o random.o lease.o
ifneq ($(V),1)
clean:
diff --git a/common.c b/common.c
index a3ce7ae..5be1226 100644
--- a/common.c
+++ b/common.c
@@ -25,12 +25,20 @@ static bool parse_ip_cidr(struct wg_combined_ip *ip, char *value)
{
uintmax_t res;
char *endptr;
- char *sep = strchr(value, '/');
+ char *sep;
+
+ if (value[0] == '\0') {
+ memset(ip, 0, ip->family == AF_INET ? 4 : 16);
+ ip->cidr = 0;
+ return true;
+ }
+
+ sep = strchr(value, '/');
if (!sep)
return false;
*sep = '\0';
- if (inet_pton(ip->family, value, &ip->ip) != 1)
+ if (inet_pton(ip->family, value, ip) != 1)
return false;
res = strtoumax(sep + 1, &endptr, 10);
diff --git a/common.h b/common.h
index 7c0c0e9..d0f8ffd 100644
--- a/common.h
+++ b/common.h
@@ -6,10 +6,11 @@
#ifndef __COMMON_H__
#define __COMMON_H__
+#include <libmnl/libmnl.h>
+#include <netinet/in.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
-#include <netinet/in.h>
#include "netlink.h"
@@ -65,22 +66,14 @@ struct wg_dynamic_request {
};
struct wg_combined_ip {
- uint16_t family;
union {
struct in_addr ip4;
struct in6_addr ip6;
- } ip;
+ };
+ uint16_t family;
uint8_t cidr;
};
-struct wg_dynamic_lease {
- struct wg_combined_ip ip4;
- struct wg_combined_ip ip6;
- uint32_t start;
- uint32_t leasetime;
- struct wg_dynamic_lease *next;
-};
-
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
void free_wg_dynamic_request(struct wg_dynamic_request *req);
diff --git a/khash.h b/khash.h
index f75f347..445ee3d 100644
--- a/khash.h
+++ b/khash.h
@@ -624,4 +624,25 @@ typedef const char *kh_cstr_t;
#define KHASH_MAP_INIT_STR(name, khval_t) \
KHASH_INIT(name, kh_cstr_t, khval_t, 1, kh_str_hash_func, kh_str_hash_equal)
+/* Custom functions for wg_key (32 bytes) */
+typedef const unsigned char *khwgkey_t;
+
+#define kh_wgkey_hash_equal(a, b) (memcmp(a, b, 32) == 0)
+
+static kh_inline khint_t __fnv_1a_32_hash(const unsigned char *wgkey)
+{
+ khint_t hash = 0x811c9dc5UL;
+ for (int i = 0; i < 32; ++i) {
+ hash ^= wgkey[i];
+ hash *= 16777619UL;
+ }
+ return hash;
+}
+
+#define KHASH_SET_INIT_WGKEY(name) \
+ KHASH_INIT(name, khwgkey_t, char, 0, __fnv_1a_32_hash, kh_wgkey_hash_equal)
+
+#define KHASH_MAP_INIT_WGKEY(name, khval_t) \
+ KHASH_INIT(name, khwgkey_t, khval_t, 1, __fnv_1a_32_hash, kh_wgkey_hash_equal)
+
#endif /* __AC_KHASH_H */
diff --git a/lease.c b/lease.c
new file mode 100644
index 0000000..936a4a8
--- /dev/null
+++ b/lease.c
@@ -0,0 +1,210 @@
+#define _POSIX_C_SOURCE 200809L
+
+#include <arpa/inet.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <time.h>
+
+#include "dbg.h"
+#include "khash.h"
+#include "lease.h"
+#include "netlink.h"
+#include "radix-trie.h"
+#include "random.h"
+
+static struct ip_pool pools;
+static time_t gexpires;
+
+KHASH_MAP_INIT_WGKEY(leaseht, struct wg_dynamic_lease *)
+khash_t(leaseht) * leases_ht;
+
+static uint64_t totall_ipv6;
+static uint32_t totalh_ipv6, total_ipv4;
+
+static time_t get_monotonic_time()
+{
+ struct timespec monotime;
+#ifdef __linux__
+ /* in linux 4.17, CLOCK_MONOTONIC was changed to be like CLOCK_BOOTTIME,
+ * see https://git.kernel.org/torvalds/c/d6ed449, but glibc's wrapper
+ * seems to still have the old behavior
+ */
+ if (clock_gettime(CLOCK_BOOTTIME, &monotime))
+ fatal("clock_gettime(CLOCK_BOOTTIME)");
+#else
+ /* CLOCK_MONOTONIC works on openbsd, but apparently not (yet) on
+ * freebsd: https://lists.freebsd.org/pipermail/freebsd-hackers/2018-June/052899.html
+ */
+ if (clock_gettime(CLOCK_MONOTONIC, &monotime))
+ fatal("clock_gettime(CLOCK_MONOTONIC)");
+
+#endif
+ /* TODO: what about darwin? */
+
+ return monotime.tv_sec;
+}
+
+void leases_init(char *fname)
+{
+ UNUSED(fname); /* TODO: open file and initialize from it */
+
+ leases_ht = kh_init(leaseht);
+
+ ipp_init(&pools);
+
+ /* TODO: initialize pools properly from routes */
+ struct in_addr pool1_v4, pool2_v4;
+ struct in6_addr pool1_v6, pool2_v6;
+ inet_pton(AF_INET, "192.168.4.0", &pool1_v4);
+ inet_pton(AF_INET, "192.168.73.0", &pool2_v4);
+ inet_pton(AF_INET6, "2001:db8:1234::", &pool1_v6);
+ inet_pton(AF_INET6, "2001:db8:7777::", &pool2_v6);
+
+ ipp_addpool_v4(&pools, &pool1_v4, 28);
+ ipp_addpool_v4(&pools, &pool2_v4, 27);
+ ipp_addpool_v6(&pools, &pool1_v6, 124);
+ ipp_addpool_v6(&pools, &pool2_v6, 124);
+
+ total_ipv4 = ipp_gettotal_v4(&pools);
+ totall_ipv6 = ipp_gettotal_v6(&pools, &totalh_ipv6);
+}
+
+void leases_free()
+{
+ kh_destroy(leaseht, leases_ht);
+ ipp_free(&pools);
+}
+
+struct wg_dynamic_lease *new_lease(wg_key pubkey, uint32_t leasetime,
+ struct in_addr *ipv4, struct in6_addr *ipv6,
+ time_t *expires)
+{
+ struct wg_dynamic_lease *lease, *parent;
+ uint64_t index_low;
+ uint32_t index, index_high;
+ struct timespec tp;
+ khiter_t k;
+ int ret;
+
+ lease = malloc(sizeof *lease);
+ if (!lease)
+ fatal("malloc()");
+
+ if (!ipv4 || ipv4->s_addr) {
+ if (total_ipv4 == 0)
+ return NULL;
+
+ --total_ipv4;
+ }
+ if (!ipv6 || !IN6_IS_ADDR_UNSPECIFIED(ipv6)) {
+ if (totalh_ipv6 == 0 && totall_ipv6 == 0) {
+ if (!ipv4 || ipv4->s_addr)
+ ++total_ipv4;
+
+ return NULL;
+ }
+
+ if (totall_ipv6 == 0)
+ --totalh_ipv6;
+
+ --totall_ipv6;
+ }
+
+ if (!ipv4 || ipv4->s_addr) {
+ if (!ipv4) {
+ index = random_bounded(total_ipv4);
+ debug("new_lease(v4): %u of %u\n", index, total_ipv4);
+ ipp_addnth_v4(&pools, &lease->ipv4, index);
+ } else {
+ if (ipp_add_v4(&pools, ipv4, 32))
+ return NULL;
+ memcpy(&lease->ipv4, ipv4, sizeof *ipv4);
+ }
+ }
+ if (!ipv6 || !IN6_IS_ADDR_UNSPECIFIED(ipv6)) {
+ if (!ipv6) {
+ if (totalh_ipv6 > 0) {
+ index_low = random_bounded(UINT64_MAX);
+ if (totall_ipv6 - index_low > totall_ipv6)
+ --totalh_ipv6;
+
+ index_high = random_bounded(totalh_ipv6);
+ } else {
+ index_low = random_bounded(totall_ipv6);
+ index_high = 0;
+ }
+ debug("new_lease(v6): %u:%ju of %u:%ju\n", index_high,
+ index_low, totalh_ipv6, totall_ipv6);
+
+ ipp_addnth_v6(&pools, &lease->ipv6, index_low,
+ index_high);
+ } else {
+ if (ipp_add_v6(&pools, ipv6, 128))
+ return NULL; /* TODO: free ipv4 addr */
+ memcpy(&lease->ipv6, ipv6, sizeof *ipv6);
+ }
+ }
+
+ if (clock_gettime(CLOCK_REALTIME, &tp))
+ fatal("clock_gettime(CLOCK_REALTIME)");
+
+ lease->start_real = tp.tv_sec;
+ lease->start_mono = get_monotonic_time();
+ lease->leasetime = leasetime;
+ lease->next = NULL;
+
+ k = kh_put(leaseht, leases_ht, pubkey, &ret);
+ if (ret < 0) {
+ die("kh_put()");
+ } else if (ret == 0) {
+ parent = kh_value(leases_ht, k);
+ while (parent->next)
+ parent = parent->next;
+
+ parent->next = lease;
+ } else {
+ kh_value(leases_ht, k) = lease;
+ }
+
+ if (lease->start_mono < gexpires)
+ gexpires = lease->start_mono;
+
+ *expires = gexpires;
+
+ /* TODO: add record to file */
+
+ return lease;
+}
+
+struct wg_dynamic_lease *get_leases(wg_key pubkey)
+{
+ khiter_t k = kh_get(leaseht, leases_ht, pubkey);
+
+ if (k == kh_end(leases_ht))
+ return NULL;
+ else
+ return kh_val(leases_ht, k);
+}
+
+bool extend_lease(struct wg_dynamic_lease *lease, uint32_t leasetime,
+ time_t *expires)
+{
+ UNUSED(lease);
+ UNUSED(leasetime);
+ UNUSED(expires);
+ return false;
+}
+
+time_t leases_refresh()
+{
+ /* TODO: remove expired leases */
+ return gexpires;
+}
+
+void leases_update_pools(int fd)
+{
+ UNUSED(fd);
+}
diff --git a/lease.h b/lease.h
new file mode 100644
index 0000000..4052bf9
--- /dev/null
+++ b/lease.h
@@ -0,0 +1,68 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ */
+
+#ifndef __LEASE_H__
+#define __LEASE_H__
+
+#include <stdint.h>
+#include <sys/socket.h>
+#include <time.h>
+
+#include "common.h"
+#include "netlink.h"
+
+struct wg_dynamic_lease {
+ time_t start_real;
+ time_t start_mono;
+ uint32_t leasetime; /* in seconds */
+ struct in_addr ipv4;
+ struct in6_addr ipv6;
+ struct wg_dynamic_lease *next;
+};
+
+/*
+ * Initializes internal state, reads leases from fname.
+ */
+void leases_init(char *fname);
+
+/*
+ * Frees everything, closes file.
+ */
+void leases_free();
+
+/*
+ * Creates a new lease and returns a pointer to it, or NULL if either we ran out
+ * of assignable IPs or the requested IP is already taken.
+ * expires contains the (monotonic) timestamp after which the next lease,
+ * possibly the newly created one, will expire.
+ */
+struct wg_dynamic_lease *new_lease(wg_key pubkey, uint32_t leasetime,
+ struct in_addr *ipv4, struct in6_addr *ipv6,
+ time_t *expires);
+
+/*
+ * Returns all leases belonging to pubkey, or NULL if there are none.
+ */
+struct wg_dynamic_lease *get_leases(wg_key pubkey);
+
+/*
+ * Extend the lease to be leasetime long again. Returns true on error, or false
+ * otherwise. expires behaves exactly as in new_lease().
+ */
+bool extend_lease(struct wg_dynamic_lease *lease, uint32_t leasetime,
+ time_t *expires);
+
+/*
+ * Refreshes all leases, meaning expired ones will be removed. Returns the
+ * expiration timestamp of the lease that will expire next.
+ */
+time_t leases_refresh();
+
+/*
+ * Updates all pools with information from the netlink file descriptor fd.
+ */
+void leases_update_pools(int fd);
+
+#endif
diff --git a/random.c b/random.c
new file mode 100644
index 0000000..eedb52f
--- /dev/null
+++ b/random.c
@@ -0,0 +1,92 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "dbg.h"
+
+#ifdef __linux__
+#include <sys/syscall.h>
+#endif
+#ifdef __APPLE__
+#include <AvailabilityMacros.h>
+#ifndef MAC_OS_X_VERSION_10_12
+#define MAC_OS_X_VERSION_10_12 101200
+#endif
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_12
+#include <sys/random.h>
+#endif
+#endif
+
+static inline bool __attribute__((__warn_unused_result__))
+get_random_bytes(uint8_t *out, size_t len)
+{
+ ssize_t ret = 0;
+ size_t i;
+ int fd;
+
+ if (len > 256) {
+ errno = EOVERFLOW;
+ return false;
+ }
+
+#if defined(__OpenBSD__) || \
+ (defined(__APPLE__) && \
+ MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_12) || \
+ (defined(__GLIBC__) && \
+ (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 25)))
+ if (!getentropy(out, len))
+ return true;
+#endif
+
+#if defined(__NR_getrandom) && defined(__linux__)
+ if (syscall(__NR_getrandom, out, len, 0) == (ssize_t)len)
+ return true;
+#endif
+
+ fd = open("/dev/urandom", O_RDONLY);
+ if (fd < 0)
+ return false;
+ for (errno = 0, i = 0; i < len; i += ret, ret = 0) {
+ ret = read(fd, out + i, len - i);
+ if (ret <= 0) {
+ ret = errno ? -errno : -EIO;
+ break;
+ }
+ }
+ close(fd);
+ errno = -ret;
+ return i == len;
+}
+
+uint64_t random_bounded(uint64_t bound)
+{
+ uint64_t ret;
+
+ if (bound == 0)
+ return 0;
+
+ if (bound == 1) {
+ if (!get_random_bytes((uint8_t *)&ret, sizeof(ret)))
+ fatal("get_random_bytes()");
+ return (ret > 0x7FFFFFFFFFFFFFFF) ? 1 : 0;
+ }
+
+ const uint64_t max_mod_bound = (1 + ~bound) % bound;
+
+ do {
+ if (!get_random_bytes((uint8_t *)&ret, sizeof(ret)))
+ fatal("get_random_bytes()");
+ } while (ret < max_mod_bound);
+
+ return ret % bound;
+}
diff --git a/random.h b/random.h
new file mode 100644
index 0000000..04271a4
--- /dev/null
+++ b/random.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ */
+
+#ifndef __RANDOM_H__
+#define __RANDOM_H__
+
+#include <stdint.h>
+
+uint64_t random_bounded(uint64_t bound);
+
+#endif
diff --git a/wg-dynamic-client.c b/wg-dynamic-client.c
index fe0b6b5..2224a22 100644
--- a/wg-dynamic-client.c
+++ b/wg-dynamic-client.c
@@ -20,6 +20,14 @@
#include "dbg.h"
#include "netlink.h"
+struct wg_dynamic_lease {
+ struct wg_combined_ip ip4;
+ struct wg_combined_ip ip6;
+ uint32_t start;
+ uint32_t leasetime;
+ struct wg_dynamic_lease *next;
+};
+
static const char *progname;
static const char *wg_interface;
static wg_device *device = NULL;
@@ -70,12 +78,12 @@ int data_cb(const struct nlmsghdr *nlh, void *data)
switch (ifa->ifa_family) {
case AF_INET:
cb_data->gaddr4->family = ifa->ifa_family;
- memcpy(&cb_data->gaddr4->ip, addr, 4);
+ memcpy(&cb_data->gaddr4->ip4, addr, 4);
cb_data->gaddr4->cidr = ifa->ifa_prefixlen;
break;
case AF_INET6:
cb_data->gaddr6->family = ifa->ifa_family;
- memcpy(&cb_data->gaddr6->ip, addr, 16);
+ memcpy(&cb_data->gaddr6->ip6, addr, 16);
cb_data->gaddr6->cidr = ifa->ifa_prefixlen;
break;
default:
@@ -114,8 +122,7 @@ static void iface_update(uint16_t cmd, uint16_t flags, uint32_t ifindex,
ifaddr->ifa_prefixlen = addr->cidr;
ifaddr->ifa_scope = RT_SCOPE_UNIVERSE; /* linux/rtnetlink.h */
ifaddr->ifa_index = ifindex;
- mnl_attr_put(nlh, IFA_LOCAL, addr->family == AF_INET ? 4 : 16,
- &addr->ip);
+ mnl_attr_put(nlh, IFA_LOCAL, addr->family == AF_INET ? 4 : 16, &addr);
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0)
fatal("mnl_socket_sendto");
@@ -138,8 +145,8 @@ static void iface_remove_addr(uint32_t ifindex,
{
char ipstr[INET6_ADDRSTRLEN];
debug("removing %s/%u from interface %u\n",
- inet_ntop(addr->family, &addr->ip, ipstr, sizeof ipstr),
- addr->cidr, ifindex);
+ inet_ntop(addr->family, &addr, ipstr, sizeof ipstr), addr->cidr,
+ ifindex);
iface_update(RTM_DELADDR, 0, ifindex, addr);
}
@@ -147,8 +154,8 @@ static void iface_add_addr(uint32_t ifindex, const struct wg_combined_ip *addr)
{
char ipstr[INET6_ADDRSTRLEN];
debug("adding %s/%u to interface %u\n",
- inet_ntop(addr->family, &addr->ip, ipstr, sizeof ipstr),
- addr->cidr, ifindex);
+ inet_ntop(addr->family, &addr, ipstr, sizeof ipstr), addr->cidr,
+ ifindex);
iface_update(RTM_NEWADDR, NLM_F_REPLACE | NLM_F_CREATE, ifindex, addr);
}
@@ -172,7 +179,7 @@ static bool get_and_validate_local_addrs(uint32_t ifindex,
static int try_connect(int *fd)
{
- struct timeval tval = {.tv_sec = 1, .tv_usec = 0 };
+ struct timeval tval = { .tv_sec = 1, .tv_usec = 0 };
struct sockaddr_in6 our_addr = {
.sin6_family = AF_INET6,
.sin6_addr = our_lladdr,
@@ -227,15 +234,15 @@ static void request_ip(int fd, const struct wg_dynamic_lease *lease)
msglen += print_to_buf((char *)buf, sizeof buf, msglen, "%s=%d\n",
WG_DYNAMIC_KEY[WGKEY_REQUEST_IP], 1);
- if (lease && lease->ip4.ip.ip4.s_addr) {
- if (!inet_ntop(AF_INET, &lease->ip4.ip.ip4, addrstr,
+ if (lease && lease->ip4.ip4.s_addr) {
+ if (!inet_ntop(AF_INET, &lease->ip4.ip4, addrstr,
sizeof addrstr))
fatal("inet_ntop()");
msglen += print_to_buf((char *)buf, sizeof buf, msglen,
"ipv4=%s/32\n", addrstr);
}
- if (lease && !IN6_IS_ADDR_UNSPECIFIED(&lease->ip6.ip.ip6)) {
- if (!inet_ntop(AF_INET6, &lease->ip6.ip.ip6, addrstr,
+ if (lease && !IN6_IS_ADDR_UNSPECIFIED(&lease->ip6.ip6)) {
+ if (!inet_ntop(AF_INET6, &lease->ip6.ip6, addrstr,
sizeof addrstr))
fatal("inet_ntop()");
msglen += print_to_buf((char *)buf, sizeof buf, msglen,
@@ -307,9 +314,8 @@ static int handle_received_lease(const struct wg_dynamic_request *req)
attr = attr->next;
}
- if (lease->leasetime == 0 ||
- (lease->ip4.ip.ip4.s_addr == 0 &&
- IN6_IS_ADDR_UNSPECIFIED(&lease->ip6.ip.ip6)))
+ if (lease->leasetime == 0 || (lease->ip4.ip4.s_addr == 0 &&
+ IN6_IS_ADDR_UNSPECIFIED(&lease->ip6.ip6)))
return -EINVAL;
if (abs(now - lease_start) < 15)
@@ -342,16 +348,16 @@ static bool handle_error(int fd, int ret)
static void maybe_update_iface()
{
- if (memcmp(&our_gaddr4.ip, &our_lease.ip4.ip, sizeof our_gaddr4.ip) ||
+ if (memcmp(&our_gaddr4, &our_lease.ip4, sizeof our_gaddr4) ||
our_gaddr4.cidr != our_lease.ip4.cidr) {
- if (our_gaddr4.ip.ip4.s_addr)
+ if (our_gaddr4.ip4.s_addr)
iface_remove_addr(device->ifindex, &our_gaddr4);
iface_add_addr(device->ifindex, &our_lease.ip4);
memcpy(&our_gaddr4, &our_lease.ip4, sizeof our_gaddr4);
}
- if (memcmp(&our_gaddr6.ip, &our_lease.ip6.ip, sizeof our_gaddr6.ip) ||
+ if (memcmp(&our_gaddr6, &our_lease.ip6, sizeof our_gaddr6) ||
our_gaddr6.cidr != our_lease.ip6.cidr) {
- if (!IN6_IS_ADDR_UNSPECIFIED(&our_gaddr6.ip.ip6))
+ if (!IN6_IS_ADDR_UNSPECIFIED(&our_gaddr6.ip6))
iface_remove_addr(device->ifindex, &our_gaddr6);
iface_add_addr(device->ifindex, &our_lease.ip6);
memcpy(&our_gaddr6, &our_lease.ip6, sizeof our_gaddr6);
@@ -420,8 +426,8 @@ int main(int argc __attribute__((unused)), char *argv[] __attribute__((unused)))
/* If we have an address configured, let's assume it's from a
* lease in order to get renewal done. */
- if (our_gaddr4.ip.ip4.s_addr ||
- !IN6_IS_ADDR_UNSPECIFIED(&our_gaddr6.ip.ip6)) {
+ if (our_gaddr4.ip4.s_addr ||
+ !IN6_IS_ADDR_UNSPECIFIED(&our_gaddr6.ip6)) {
our_lease.start = current_time();
our_lease.leasetime = 15;
memcpy(&our_lease.ip4, &our_gaddr4,
diff --git a/wg-dynamic-server.c b/wg-dynamic-server.c
index a408a75..168a902 100644
--- a/wg-dynamic-server.c
+++ b/wg-dynamic-server.c
@@ -25,6 +25,7 @@
#include "common.h"
#include "dbg.h"
#include "khash.h"
+#include "lease.h"
#include "netlink.h"
static const char *progname;
@@ -246,57 +247,6 @@ static void accept_incoming(int sockfd, struct wg_dynamic_request *reqs)
}
}
-static int allocate_from_pool(struct wg_dynamic_request *const req,
- struct wg_dynamic_lease *lease)
-{
- struct wg_dynamic_attr *attr;
- struct wg_combined_ip default_v4 = { .family = AF_INET, .cidr = 32 },
- default_v6 = { .family = AF_INET6, .cidr = 128 };
-
- /* NOTE: handing out whatever the client asks for */
- /* TODO: choose an ip address from pool of available
- * addresses, together with an appropriate lease time */
- /* NOTE: the pool is to be drawn from what routes are pointing
- * to the wg interface, and kept up to date as the routing
- * table changes */
-
- if (inet_pton(AF_INET, "192.168.47.11", &default_v4.ip.ip4) != 1)
- fatal("inet_pton()");
- memcpy(&lease->ip4, &default_v4, sizeof(struct wg_combined_ip));
-
- if (inet_pton(AF_INET6, "fd00::4711", &default_v6.ip.ip6) != 1)
- fatal("inet_pton()");
- memcpy(&lease->ip6, &default_v6, sizeof(struct wg_combined_ip));
-
- lease->start = current_time();
- lease->leasetime = WG_DYNAMIC_LEASETIME;
-
- attr = req->first;
- while (attr) {
- switch (attr->key) {
- case WGKEY_IPV4:
- memcpy(&lease->ip4, attr->value,
- sizeof(struct wg_combined_ip));
- break;
- case WGKEY_IPV6:
- memcpy(&lease->ip6, attr->value,
- sizeof(struct wg_combined_ip));
- break;
- case WGKEY_LEASETIME:
- memcpy(&lease->leasetime, attr->value,
- sizeof(uint32_t));
- break;
- default:
- debug("Ignoring invalid attribute for request_ip: %d\n",
- attr->key);
- }
-
- attr = attr->next;
- }
-
- return 0;
-}
-
static bool send_error(int fd, int ret)
{
UNUSED(fd);
@@ -304,39 +254,32 @@ static bool send_error(int fd, int ret)
return true;
}
-static bool serialise_lease(char *buf, size_t bufsize, size_t *offset,
+static void serialise_lease(char *buf, size_t bufsize, size_t *offset,
const struct wg_dynamic_lease *lease)
{
char addrbuf[INET6_ADDRSTRLEN];
- bool ret = false;
- if (lease->ip4.ip.ip4.s_addr) {
- if (!inet_ntop(AF_INET, &lease->ip4.ip.ip4, addrbuf,
- sizeof addrbuf))
+ if (lease->ipv4.s_addr) {
+ if (!inet_ntop(AF_INET, &lease->ipv4, addrbuf, sizeof addrbuf))
fatal("inet_ntop()");
*offset += print_to_buf(buf, bufsize, *offset, "ipv4=%s/%d\n",
- addrbuf, lease->ip4.cidr);
- ret = true;
+ addrbuf, 32);
}
- if (!IN6_IS_ADDR_UNSPECIFIED(&lease->ip6.ip.ip6)) {
- if (!inet_ntop(AF_INET6, &lease->ip6.ip.ip6, addrbuf,
- sizeof addrbuf))
+
+ if (!IN6_IS_ADDR_UNSPECIFIED(&lease->ipv6)) {
+ if (!inet_ntop(AF_INET6, &lease->ipv6, addrbuf, sizeof addrbuf))
fatal("inet_ntop()");
*offset += print_to_buf(buf, bufsize, *offset, "ipv6=%s/%d\n",
- addrbuf, lease->ip6.cidr);
- ret = true;
+ addrbuf, 128);
}
- if (ret) {
- *offset += print_to_buf(buf, bufsize, *offset,
- "leasestart=%u\n", lease->start);
- *offset += print_to_buf(buf, bufsize, *offset, "leasetime=%u\n",
- lease->leasetime);
- }
-
- return ret;
+ *offset += print_to_buf(buf, bufsize, *offset, "leasestart=%u\n",
+ lease->start_real);
+ *offset += print_to_buf(buf, bufsize, *offset, "leasetime=%u\n",
+ lease->leasetime);
}
+/* TODO: put this in a hashtable instead? */
static struct wg_peer *current_peer(struct wg_dynamic_request *req)
{
struct wg_peer *peer;
@@ -346,29 +289,12 @@ static struct wg_peer *current_peer(struct wg_dynamic_request *req)
return peer;
}
- return NULL;
+ die("Unable to find peer\n");
}
-static void insert_allowed_ip(struct wg_peer *peer,
- const struct wg_combined_ip *ip)
+/* TODO: this will overwrite changes done to the interface by others */
+static void insert_allowed_ip(struct wg_peer *peer, struct wg_allowedip *newip)
{
- struct wg_allowedip *newip;
-
- newip = calloc(1, sizeof(struct wg_allowedip));
- if (!newip)
- fatal("calloc()");
-
- newip->family = ip->family;
- switch (newip->family) {
- case AF_INET:
- memcpy(&newip->ip4, &ip->ip.ip4, sizeof(struct in_addr));
- break;
- case AF_INET6:
- memcpy(&newip->ip6, &ip->ip.ip6, sizeof(struct in6_addr));
- break;
- }
- newip->cidr = ip->cidr;
-
if (!peer->first_allowedip)
peer->first_allowedip = newip;
else
@@ -376,62 +302,96 @@ static void insert_allowed_ip(struct wg_peer *peer,
peer->last_allowedip = newip;
}
-static int add_allowed_ips(struct wg_peer *peer,
- const struct wg_dynamic_lease *lease)
+static int add_allowed_ips(struct wg_peer *peer, struct in_addr *ipv4,
+ struct in6_addr *ipv6)
{
- if (lease->ip4.ip.ip4.s_addr)
- insert_allowed_ip(peer, &lease->ip4);
- if (!IN6_IS_ADDR_UNSPECIFIED(&lease->ip6.ip.ip6))
- insert_allowed_ip(peer, &lease->ip6);
+ struct wg_allowedip *newip;
+
+ if (ipv4 && ipv4->s_addr) {
+ newip = calloc(1, sizeof *newip);
+ if (!newip)
+ fatal("calloc()");
+
+ newip->family = AF_INET;
+ newip->cidr = 32;
+ memcpy(&newip->ip4, &ipv4->s_addr, sizeof(struct in_addr));
+ insert_allowed_ip(peer, newip);
+ }
+ if (ipv6 && !IN6_IS_ADDR_UNSPECIFIED(ipv6)) {
+ newip = calloc(1, sizeof *newip);
+ if (!newip)
+ fatal("calloc()");
+
+ newip->family = AF_INET6;
+ newip->cidr = 128;
+ memcpy(&newip->ip6, &ipv6->s6_addr, sizeof(struct in6_addr));
+ insert_allowed_ip(peer, newip);
+ }
return wg_set_device(device);
}
+static int response_request_ip(struct wg_dynamic_attr *cur, wg_key pubkey,
+ struct wg_dynamic_lease **lease)
+{
+ time_t expires;
+ struct in_addr *ipv4 = NULL;
+ struct in6_addr *ipv6 = NULL;
+ uint32_t leasetime = WG_DYNAMIC_LEASETIME;
+
+ *lease = get_leases(pubkey);
+
+ while (cur) {
+ switch (cur->key) {
+ case WGKEY_IPV4:
+ ipv4 = &((struct wg_combined_ip *)cur->value)->ip4;
+ break;
+ case WGKEY_IPV6:
+ ipv6 = &((struct wg_combined_ip *)cur->value)->ip6;
+ break;
+ case WGKEY_LEASETIME:
+ leasetime = *(uint32_t *)cur->value;
+ break;
+ default:
+ debug("Ignoring invalid attribute for request_ip: %d\n",
+ cur->key);
+ }
+ cur = cur->next;
+ }
+
+ if (ipv4 && ipv6 && !ipv4->s_addr && IN6_IS_ADDR_UNSPECIFIED(ipv6))
+ return 2; /* TODO: invalid request */
+
+ *lease = new_lease(pubkey, leasetime, ipv4, ipv6, &expires);
+ if (!*lease)
+ return 1; /* TODO: either out of IPs or IP unavailable */
+
+ return 0;
+}
+
static bool send_response(int fd, struct wg_dynamic_request *req)
{
- int ret;
char *errmsg = "OK";
+ struct wg_dynamic_attr *cur = req->first;
+ struct wg_dynamic_lease *lease;
unsigned char buf[MAX_RESPONSE_SIZE + 1];
size_t msglen;
size_t written;
- struct wg_dynamic_lease lease = { 0 };
- struct wg_peer *peer;
+ int ret = 0;
- peer = current_peer(req);
- if (!peer)
- die("Unable to find peer\n");
-
- ret = 0;
- msglen = 0;
switch (req->cmd) {
case WGKEY_REQUEST_IP:
msglen = print_to_buf((char *)buf, sizeof buf, 0, "%s=%d\n",
WG_DYNAMIC_KEY[req->cmd], 1);
- ret = allocate_from_pool(req, &lease);
+ ret = response_request_ip(cur, req->pubkey, &lease);
if (ret) {
- debug("IP address allocation failing with %d\n", ret);
- ret = 0x02;
- errmsg = "Out of IP addresses";
- break;
- }
-
- ret = add_allowed_ips(peer, &lease);
- if (ret) {
- debug("Unable to add address(es) to peer: %s\n",
- strerror(-ret));
- ret = 0x1;
- errmsg = "Internal error";
- break;
- }
-
- if (!serialise_lease((char *)buf, sizeof buf, &msglen,
- &lease)) {
- die("Nothing to hand out, despite succeeding allocate_from_pool()\n");
+ errmsg = "Out of IP addresses"; /* TODO: distinguish */
break;
}
+ add_allowed_ips(current_peer(req), &lease->ipv4, &lease->ipv6);
+ serialise_lease((char *)buf, sizeof buf, &msglen, lease);
break;
-
default:
debug("Unknown command: %d\n", req->cmd);
return true;
@@ -486,6 +446,7 @@ static void setup_socket(int *fd)
static void cleanup()
{
+ leases_free();
kh_destroy(allowedht, allowedips_ht);
wg_free_device(device);
@@ -519,6 +480,8 @@ int main(int argc, char *argv[])
allowedips_ht = kh_init(allowedht);
+ leases_init("leases_file");
+
wg_interface = argv[1];
if (atexit(cleanup))
die("Failed to set exit function\n");