diff options
author | Linus Nordberg <linus@nordberg.se> | 2019-09-29 22:16:52 +0200 |
---|---|---|
committer | Linus Nordberg <linus@nordberg.se> | 2019-09-29 22:36:19 +0200 |
commit | 415a43be2ae31372a33e910913d6ae0ba401e167 (patch) | |
tree | bdb5e6fec4c4bd3fa1f61f491daad6e7f816bf58 /lease.c | |
parent | Remove allowed-ips from peers as leases expire (diff) | |
download | wg-dynamic-415a43be2ae31372a33e910913d6ae0ba401e167.tar.xz wg-dynamic-415a43be2ae31372a33e910913d6ae0ba401e167.zip |
Server side lease handling
Extend when requested, release when being replaced.
Separate address families so that one can be granted even when the
pool for the other is empty.
Diffstat (limited to 'lease.c')
-rw-r--r-- | lease.c | 211 |
1 files changed, 179 insertions, 32 deletions
@@ -91,9 +91,11 @@ void leases_free() } struct wg_dynamic_lease *new_lease(wg_key pubkey, uint32_t leasetime, - struct in_addr *ipv4, struct in6_addr *ipv6) + const struct in_addr *ipv4, + const struct in6_addr *ipv6, + struct wg_dynamic_lease *current) { - struct wg_dynamic_lease *lease, *parent; + struct wg_dynamic_lease *lease; uint64_t index_l; uint32_t index, index_h; struct timespec tp; @@ -101,34 +103,90 @@ struct wg_dynamic_lease *new_lease(wg_key pubkey, uint32_t leasetime, int ret; bool wants_ipv4 = !ipv4 || ipv4->s_addr; bool wants_ipv6 = !ipv6 || !IN6_IS_ADDR_UNSPECIFIED(ipv6); + bool ipv4_extended = false; + bool ipv6_extended = false; - lease = malloc(sizeof *lease); - if (!lease) - fatal("malloc()"); + char ipv4_asc[INET_ADDRSTRLEN], ipv6_asc[INET6_ADDRSTRLEN]; + wg_key_b64_string pubkey_asc; + wg_key_to_base64(pubkey_asc, pubkey); - if (wants_ipv4 && !pool.total_ipv4) - return NULL; /* no ipv4 addresses available */ + lease = calloc(1, sizeof *lease); + if (!lease) + fatal("calloc()"); + + /* Extend addresses explicitly asked for and which we already have. */ + if (lease_is_valid(current)) { + if (current->ipv4.s_addr) { + if (ipv4 && ipv4->s_addr == current->ipv4.s_addr) { + inet_ntop(AF_INET, ¤t->ipv4, ipv4_asc, + INET_ADDRSTRLEN); + debug("extending %s\n", ipv4_asc); + + memcpy(&lease->ipv4, ¤t->ipv4, + sizeof lease->ipv4); + memset(¤t->ipv4, 0, sizeof current->ipv4); + ipv4_extended = true; + } + } + if (!IN6_IS_ADDR_UNSPECIFIED(¤t->ipv6)) { + if (ipv6 && IN6_ARE_ADDR_EQUAL(ipv6, ¤t->ipv6)) { + inet_ntop(AF_INET6, ¤t->ipv6, ipv6_asc, + INET6_ADDRSTRLEN); + debug("extending %s\n", ipv6_asc); + + memcpy(&lease->ipv6, ¤t->ipv6, + sizeof lease->ipv6); + memset(¤t->ipv6, 0, sizeof current->ipv6); + ipv6_extended = true; + } + } + } - if (wants_ipv6 && !pool.totalh_ipv6 && !pool.totall_ipv6) - return NULL; /* no ipv6 addresses available */ + if (ipv4) + inet_ntop(AF_INET, ipv4, ipv4_asc, INET_ADDRSTRLEN); /* DEBUG */ - if (wants_ipv4) { - if (!ipv4) { + /* Allocate IPv4 if wanted and not already extended. */ + if (wants_ipv4 && !ipv4_extended) { + if (!pool.total_ipv4) { + debug("IPv4 pool empty\n"); + } else if (!ipv4) { index = random_bounded(pool.total_ipv4 - 1); + debug("new_lease(v4): %u of %u\n", index, pool.total_ipv4); ipp_addnth_v4(&pool, &lease->ipv4, index); } else { - if (ipp_add_v4(&pool, ipv4, 32)) - return NULL; + debug("wants %s: ", ipv4_asc); - memcpy(&lease->ipv4, ipv4, sizeof *ipv4); + if (ipp_add_v4(&pool, ipv4, 32)) { + debug("busy, possibly by us: %s\n", + lease_to_str(current)); + } else { + debug("allocated\n"); + memcpy(&lease->ipv4, ipv4, sizeof lease->ipv4); + } } } - if (wants_ipv6) { - if (!ipv6) { + /* Release IPv4 if not wanted and not extended. */ + if (!wants_ipv4 && !ipv4_extended && ipv4 && ipv4->s_addr) { + debug("releasing %s\n", ipv4_asc); + + if (ipp_del_v4(&pool, &lease->ipv4, 32)) + die("ipp_del_v4()\n"); + memset(&lease->ipv4, 0, sizeof lease->ipv4); + } + + if (ipv6) + inet_ntop(AF_INET6, ipv6, ipv6_asc, + INET6_ADDRSTRLEN); /* DEBUG */ + + /* Allocate IPv6 if wanted and not already extended. */ + if (wants_ipv6 && !ipv6_extended) { + if (!pool.totalh_ipv6 && !pool.totall_ipv6) { + debug("IPv6 pool empty\n"); + } else if (!ipv6) { if (pool.totalh_ipv6 > 0) { index_l = random_bounded(UINT64_MAX); index_h = random_bounded(pool.totalh_ipv6 - 1); @@ -139,19 +197,38 @@ struct wg_dynamic_lease *new_lease(wg_key pubkey, uint32_t leasetime, debug("new_lease(v6): %u:%ju of %u:%ju\n", index_h, index_l, pool.totalh_ipv6, pool.totall_ipv6); + ipp_addnth_v6(&pool, &lease->ipv6, index_l, index_h); } else { - if (ipp_add_v6(&pool, ipv6, 128)) { - if (!ipv4 || ipv4->s_addr) - ipp_del_v4(&pool, ipv4, 32); + debug("wants %s: ", ipv6_asc); - return NULL; + if (ipp_add_v6(&pool, ipv6, 128)) { + debug("busy, possibly by us: %s\n", + lease_to_str(current)); + } else { + debug("allocated\n"); + memcpy(&lease->ipv6, ipv6, sizeof lease->ipv6); } - - memcpy(&lease->ipv6, ipv6, sizeof *ipv6); } } + /* Release IPv6 if not wanted and not extended. */ + if (!wants_ipv6 && !ipv6_extended && ipv6 && + !IN6_IS_ADDR_UNSPECIFIED(ipv6)) { + debug("releasing %s\n", ipv6_asc); + + if (ipp_del_v6(&pool, &lease->ipv6, 128)) + die("ipp_del_v6()\n"); + memset(&lease->ipv6, 0, sizeof lease->ipv6); + } + + /* Return NULL if we didn't get at least one address. */ + if (!lease->ipv4.s_addr && IN6_IS_ADDR_UNSPECIFIED(&lease->ipv6)) { + free(lease); + return NULL; + } + + /* Set leasetime. */ if (clock_gettime(CLOCK_REALTIME, &tp)) fatal("clock_gettime(CLOCK_REALTIME)"); @@ -160,6 +237,7 @@ struct wg_dynamic_lease *new_lease(wg_key pubkey, uint32_t leasetime, lease->leasetime = leasetime; lease->next = NULL; + /* Update hash table. */ wg_key *pubcopy = malloc(sizeof(wg_key)); if (!pubcopy) fatal("malloc()"); @@ -169,14 +247,13 @@ struct wg_dynamic_lease *new_lease(wg_key pubkey, uint32_t leasetime, if (ret < 0) { fatal("kh_put()"); } else if (ret == 0) { - parent = kh_value(leases_ht, k); + struct wg_dynamic_lease *parent = kh_value(leases_ht, k); while (parent->next) parent = parent->next; - parent->next = lease; - } else { + } else kh_value(leases_ht, k) = lease; - } + debug("new lease: %s\n", lease_to_str(lease)); if (lease->start_mono + lease->leasetime < gexpires) gexpires = lease->start_mono + lease->leasetime; @@ -196,10 +273,50 @@ struct wg_dynamic_lease *get_leases(wg_key pubkey) return kh_val(leases_ht, k); } -bool extend_lease(struct wg_dynamic_lease *lease, uint32_t leasetime) +bool release_lease(struct wg_dynamic_lease *lease, wg_key pubkey) { - UNUSED(lease); - UNUSED(leasetime); + struct wg_dynamic_lease *first, *iter; + khiter_t k; + + if (!lease) + return true; + + k = kh_get(leaseht, leases_ht, pubkey); + if (k == kh_end(leases_ht)) + return true; + first = kh_val(leases_ht, k); + + for (iter = first; iter; iter = iter->next) + if (iter == lease) + break; + if (iter != lease) + return true; + + debug("Releasing lease: %s\n", lease_to_str(lease)); + if (lease->ipv4.s_addr && ipp_del_v4(&pool, &lease->ipv4, 32)) { + debug("Unable to delete IPv4 address from pool: %s\n", + lease_to_str(lease)); + die("ipp_del_v4()\n"); + } + if (!IN6_IS_ADDR_UNSPECIFIED(&lease->ipv6) && + ipp_del_v6(&pool, &lease->ipv6, 128)) { + debug("Unable to delete IPv6 address from pool: %s\n", + lease_to_str(lease)); + die("ipp_del_v6()\n"); + } + + if (lease == first) { + if (lease->next) { + kh_val(leases_ht, k) = lease->next; + } else { + kh_del(leaseht, leases_ht, k); + } + } else { + BUG_ON(first->next == NULL); + first->next = NULL; + } + free(lease); + return false; } @@ -230,11 +347,14 @@ int leases_refresh(void (*update_cb)(wg_key *, int)) if (!IN6_IS_ADDR_UNSPECIFIED(ipv6)) ipp_del_v6(&pool, ipv6, 128); - memcpy(updates[i], kh_key(leases_ht, k), sizeof(wg_key)); + memcpy(updates[i], kh_key(leases_ht, k), + sizeof(wg_key)); { wg_key_b64_string pubkey_asc; - wg_key_to_base64(pubkey_asc, updates[i]); - debug("Peer losing its lease: %s\n", pubkey_asc); + wg_key_to_base64(pubkey_asc, + updates[i]); + debug("Peer losing its lease: %s\n", + pubkey_asc); } i++; if (i == WG_DYNAMIC_LEASE_CHUNKSIZE) { @@ -365,3 +485,30 @@ void leases_update_pools(struct mnl_socket *nlsock) if (ret == -1 && errno != EAGAIN && errno != EWOULDBLOCK) fatal("mnl_socket_recvfrom()"); } + +bool lease_is_valid(const struct wg_dynamic_lease *lease) +{ + if (!lease) + return false; + + if (get_monotonic_time() >= lease->start_mono + lease->leasetime) + return false; + + return true; +} + +#ifdef DEBUG +char *lease_to_str(const struct wg_dynamic_lease *l) +{ + static char buf[4096]; + char v4[INET_ADDRSTRLEN], v6[INET6_ADDRSTRLEN]; + + if (!l) + return "(null)"; + + inet_ntop(AF_INET, &l->ipv4, v4, sizeof v4); + inet_ntop(AF_INET6, &l->ipv6, v6, sizeof v6); + snprintf(buf, sizeof buf, "(%p) %s [%s]", l, v4, v6); + return buf; +} +#endif |