aboutsummaryrefslogtreecommitdiffstats
path: root/wg-dynamic-server.c
diff options
context:
space:
mode:
Diffstat (limited to 'wg-dynamic-server.c')
-rw-r--r--wg-dynamic-server.c798
1 files changed, 378 insertions, 420 deletions
diff --git a/wg-dynamic-server.c b/wg-dynamic-server.c
index fca1dfc..5149cb5 100644
--- a/wg-dynamic-server.c
+++ b/wg-dynamic-server.c
@@ -9,7 +9,6 @@
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
-#include <string.h>
#include <time.h>
#include <arpa/inet.h>
@@ -24,6 +23,7 @@
#include "common.h"
#include "dbg.h"
+#include "ipm.h"
#include "khash.h"
#include "lease.h"
#include "netlink.h"
@@ -33,7 +33,7 @@ static const char *wg_interface;
static struct in6_addr well_known;
static wg_device *device = NULL;
-static struct wg_dynamic_request requests[MAX_CONNECTIONS] = { 0 };
+static uint32_t leasetime = WG_DYNAMIC_DEFAULT_LEASETIME;
static int sockfd = -1;
static int epollfd = -1;
@@ -42,534 +42,492 @@ static struct mnl_socket *nlsock = NULL;
KHASH_MAP_INIT_INT64(allowedht, wg_key *)
khash_t(allowedht) * allowedips_ht;
-struct mnl_cb_data {
- uint32_t ifindex;
- bool valid_ip_found;
+struct wg_dynamic_connection {
+ struct wg_dynamic_request req;
+ int fd;
+ wg_key pubkey;
+ struct in6_addr lladdr;
+ unsigned char *outbuf;
+ size_t buflen;
};
-static void usage()
-{
- die("usage: %s <wg-interface>\n", progname);
-}
-
-static int data_cb(const struct nlmsghdr *nlh, void *data)
-{
- struct nlattr *tb[IFA_MAX + 1] = {};
- struct ifaddrmsg *ifa = mnl_nlmsg_get_payload(nlh);
- struct mnl_cb_data *cb_data = (struct mnl_cb_data *)data;
- unsigned char *addr;
-
- if (ifa->ifa_index != cb_data->ifindex)
- return MNL_CB_OK;
-
- if (ifa->ifa_scope != RT_SCOPE_LINK)
- return MNL_CB_OK;
-
- mnl_attr_parse(nlh, sizeof(*ifa), data_attr_cb, tb);
-
- if (!tb[IFA_ADDRESS])
- return MNL_CB_OK;
-
- addr = mnl_attr_get_payload(tb[IFA_ADDRESS]);
- char out[INET6_ADDRSTRLEN];
- inet_ntop(ifa->ifa_family, addr, out, sizeof(out));
- debug("index=%d, family=%d, addr=%s\n", ifa->ifa_index, ifa->ifa_family,
- out);
-
- if (ifa->ifa_prefixlen != 64 || memcmp(addr, well_known.s6_addr, 16))
- return MNL_CB_OK;
+static struct wg_dynamic_connection connections[MAX_CONNECTIONS] = { 0 };
- cb_data->valid_ip_found = true;
-
- return MNL_CB_OK;
-}
-
-static bool validate_link_local_ip(uint32_t ifindex)
+static void usage()
{
- struct mnl_cb_data cb_data = {
- .ifindex = ifindex,
- .valid_ip_found = false,
- };
-
- iface_get_all_addrs(AF_INET6, data_cb, &cb_data);
-
- return cb_data.valid_ip_found;
+ die("usage: %s <wg-interface> <leasetime>\n", progname);
}
static bool valid_peer_found(wg_device *device)
{
- wg_peer *peer;
- wg_key_b64_string key;
- wg_allowedip *allowedip;
- wg_for_each_peer (device, peer) {
- wg_key_to_base64(key, peer->public_key);
- debug("- peer %s\n", key);
- debug(" allowedips:\n");
-
- wg_for_each_allowedip (peer, allowedip) {
- char out[INET6_ADDRSTRLEN];
- inet_ntop(allowedip->family, &allowedip->ip6, out,
- sizeof(out));
- debug(" %s\n", out);
-
- if (is_link_local(allowedip->ip6.s6_addr) &&
- allowedip->cidr == 128)
- return true;
- }
- }
-
- return false;
+ wg_peer *peer;
+ wg_key_b64_string key;
+ wg_allowedip *allowedip;
+ wg_for_each_peer (device, peer) {
+ wg_key_to_base64(key, peer->public_key);
+ debug("- peer %s\n", key);
+ debug(" allowedips:\n");
+
+ wg_for_each_allowedip (peer, allowedip) {
+ char out[INET6_ADDRSTRLEN];
+ inet_ntop(allowedip->family, &allowedip->ip6, out,
+ sizeof(out));
+ debug(" %s\n", out);
+
+ if (is_link_local(allowedip->ip6.s6_addr) &&
+ allowedip->cidr == 128)
+ return true;
+ }
+ }
+
+ return false;
}
static void rebuild_allowedips_ht()
{
- wg_peer *peer;
- wg_allowedip *allowedip;
- khiter_t k;
- uint64_t lh;
- int ret;
-
- kh_clear(allowedht, allowedips_ht);
-
- wg_free_device(device);
- if (wg_get_device(&device, wg_interface))
- fatal("Unable to access interface %s", wg_interface);
-
- wg_for_each_peer (device, peer) {
- wg_for_each_allowedip (peer, allowedip) {
- if (allowedip->family == AF_INET6 &&
- is_link_local(allowedip->ip6.s6_addr) &&
- allowedip->cidr == 128) {
- memcpy(&lh, allowedip->ip6.s6_addr + 8, 8);
- k = kh_put(allowedht, allowedips_ht, lh, &ret);
- if (ret <= 0)
- die("Failed to rebuild allowedips hashtable\n");
-
- kh_value(allowedips_ht, k) = &peer->public_key;
- }
- }
- }
+ wg_peer *peer;
+ wg_allowedip *allowedip;
+ khiter_t k;
+ uint64_t lh;
+ int ret;
+
+ kh_clear(allowedht, allowedips_ht);
+
+ wg_free_device(device);
+ if (wg_get_device(&device, wg_interface))
+ fatal("Unable to access interface %s", wg_interface);
+
+ wg_for_each_peer (device, peer) {
+ wg_for_each_allowedip (peer, allowedip) {
+ if (allowedip->family == AF_INET6 &&
+ is_link_local(allowedip->ip6.s6_addr) &&
+ allowedip->cidr == 128) {
+ memcpy(&lh, allowedip->ip6.s6_addr + 8, 8);
+ k = kh_put(allowedht, allowedips_ht, lh, &ret);
+ if (ret <= 0)
+ die("Failed to rebuild allowedips hashtable\n");
+
+ kh_value(allowedips_ht, k) = &peer->public_key;
+ }
+ }
+ }
}
static wg_key *addr_to_pubkey(struct sockaddr_storage *addr)
{
- khiter_t k;
- uint64_t lh;
-
- if (addr->ss_family == AF_INET6) {
- lh = *(uint64_t *)&((struct sockaddr_in6 *)addr)
- ->sin6_addr.s6_addr[8];
- k = kh_get(allowedht, allowedips_ht, lh);
- if (k != kh_end(allowedips_ht))
- return kh_val(allowedips_ht, k);
- }
-
- return NULL;
+ khiter_t k;
+ uint64_t lh;
+
+ if (addr->ss_family == AF_INET6) {
+ lh = *(uint64_t *)&((struct sockaddr_in6 *)addr)
+ ->sin6_addr.s6_addr[8];
+ k = kh_get(allowedht, allowedips_ht, lh);
+ if (k != kh_end(allowedips_ht))
+ return kh_val(allowedips_ht, k);
+ }
+
+ return NULL;
}
-static int accept_connection(int sockfd, wg_key *dest)
+static int accept_connection(wg_key *dest_pubkey,
+ struct in6_addr *dest_lladdr)
{
- int fd;
- wg_key *pubkey;
- struct sockaddr_storage addr;
- socklen_t size = sizeof addr;
+ int fd;
+ wg_key *pubkey;
+ struct sockaddr_storage addr;
+ socklen_t size = sizeof addr;
#ifdef __linux__
- fd = accept4(sockfd, (struct sockaddr *)&addr, &size, SOCK_NONBLOCK);
- if (fd < 0)
- return -errno;
+ fd = accept4(sockfd, (struct sockaddr *)&addr, &size, SOCK_NONBLOCK);
+ if (fd < 0)
+ return -errno;
#else
- fd = accept(sockfd, (struct sockaddr *)&addr, &size);
- if (fd < 0)
- return -errno;
+ fd = accept(sockfd, (struct sockaddr *)&addr, &size);
+ if (fd < 0)
+ return -errno;
- int res = fcntl(fd, F_GETFL, 0);
- if (res < 0 || fcntl(fd, F_SETFL, res | O_NONBLOCK) < 0)
- fatal("Setting socket to nonblocking failed");
+ int res = fcntl(fd, F_GETFL, 0);
+ if (res < 0 || fcntl(fd, F_SETFL, res | O_NONBLOCK) < 0)
+ fatal("Setting socket to nonblocking failed");
#endif
- if (addr.ss_family != AF_INET6) {
- debug("Rejecting client for not using an IPv6 address\n");
- return -EINVAL;
- }
-
- if (((struct sockaddr_in6 *)&addr)->sin6_port !=
- htons(WG_DYNAMIC_PORT)) {
- debug("Rejecting client for using port %u != %u\n",
- htons(((struct sockaddr_in6 *)&addr)->sin6_port),
- WG_DYNAMIC_PORT);
- return -EINVAL;
- }
-
- pubkey = addr_to_pubkey(&addr);
- if (!pubkey) {
- /* our copy of allowedips is outdated, refresh */
- rebuild_allowedips_ht();
- pubkey = addr_to_pubkey(&addr);
- if (!pubkey) {
- /* either we lost the race or something is very wrong */
- close(fd);
- return -ENOENT;
- }
- }
- memcpy(dest, pubkey, sizeof *dest);
-
- wg_key_b64_string key;
- char out[INET6_ADDRSTRLEN];
- wg_key_to_base64(key, *pubkey);
- inet_ntop(addr.ss_family, &((struct sockaddr_in6 *)&addr)->sin6_addr,
- out, sizeof(out));
- debug("%s has pubkey: %s\n", out, key);
-
- return fd;
-}
-
-static bool send_error(struct wg_dynamic_request *req, int error)
-{
- char buf[MAX_RESPONSE_SIZE];
- size_t msglen = 0;
-
- print_to_buf(buf, sizeof buf, &msglen, "errno=%d\nerrmsg=%s\n\n", error,
- WG_DYNAMIC_ERR[error]);
-
- return send_message(req, buf, msglen);
+ if (addr.ss_family != AF_INET6) {
+ debug("Rejecting client for not using an IPv6 address\n");
+ return -EINVAL;
+ }
+
+ if (((struct sockaddr_in6 *)&addr)->sin6_port !=
+ htons(WG_DYNAMIC_PORT)) {
+ debug("Rejecting client for using port %u != %u\n",
+ htons(((struct sockaddr_in6 *)&addr)->sin6_port),
+ WG_DYNAMIC_PORT);
+ return -EINVAL;
+ }
+
+ pubkey = addr_to_pubkey(&addr);
+ if (!pubkey) {
+ /* our copy of allowedips is outdated, refresh */
+ rebuild_allowedips_ht();
+ pubkey = addr_to_pubkey(&addr);
+ if (!pubkey) {
+ /* either we lost the race or something is very wrong */
+ close(fd);
+ return -ENOENT;
+ }
+ }
+ memcpy(dest_pubkey, pubkey, sizeof *dest_pubkey);
+
+ memcpy(dest_lladdr, &((struct sockaddr_in6 *)&addr)->sin6_addr,
+ sizeof *dest_lladdr);
+
+ wg_key_b64_string key;
+ char out[INET6_ADDRSTRLEN];
+ wg_key_to_base64(key, *pubkey);
+ inet_ntop(addr.ss_family, &((struct sockaddr_in6 *)&addr)->sin6_addr,
+ out, sizeof(out));
+ debug("%s has pubkey: %s\n", out, key);
+
+ return fd;
}
-static size_t serialize_lease(char *buf, size_t len,
- const struct wg_dynamic_lease *lease)
+static bool send_message(struct wg_dynamic_connection *con,
+ const unsigned char *buf, size_t len)
{
- char addrbuf[INET6_ADDRSTRLEN];
- size_t off = 0;
-
- if (lease->ipv4.s_addr) {
- if (!inet_ntop(AF_INET, &lease->ipv4, addrbuf, sizeof addrbuf))
- fatal("inet_ntop()");
-
- print_to_buf(buf, len, &off, "ipv4=%s/%d\n", addrbuf, 32);
- }
-
- if (!IN6_IS_ADDR_UNSPECIFIED(&lease->ipv6)) {
- if (!inet_ntop(AF_INET6, &lease->ipv6, addrbuf, sizeof addrbuf))
- fatal("inet_ntop()");
-
- print_to_buf(buf, len, &off, "ipv6=%s/%d\n", addrbuf, 128);
- }
-
- print_to_buf(buf, len, &off, "leasestart=%u\nleasetime=%u\nerrno=0\n\n",
- lease->start_real, lease->leasetime);
-
- return off;
+ size_t offset = 0;
+
+ while (1) {
+ ssize_t written = write(con->fd, buf + offset, len - offset);
+ if (written < 0) {
+ if (errno == EWOULDBLOCK || errno == EAGAIN)
+ break;
+
+ if (errno == EINTR)
+ continue;
+
+ debug("Writing to socket %d failed: %s\n", con->fd,
+ strerror(errno));
+ return false;
+ }
+
+ offset += written;
+ if (offset == len)
+ return true;
+ }
+
+ debug("Socket %d blocking on write with %lu bytes left, postponing\n",
+ con->fd, len - offset);
+
+ if (!con->outbuf) {
+ con->buflen = len - offset;
+ con->outbuf = malloc(con->buflen);
+ if (!con->outbuf)
+ fatal("malloc()");
+ memcpy(con->outbuf, buf + offset, con->buflen);
+ } else {
+ con->buflen = len - offset;
+ memmove(con->outbuf, buf + offset, con->buflen);
+ }
+
+ return true;
}
-static void add_allowed_ips(wg_key pubkey, struct in_addr *ipv4,
- struct in6_addr *ipv6)
+void close_connection(struct wg_dynamic_connection *con)
{
- wg_allowedip allowed_v4, allowed_v6;
- wg_peer peer = { 0 };
- wg_device dev = { .first_peer = &peer };
-
- strcpy(dev.name, wg_interface);
- 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;
- }
+ free_wg_dynamic_request(&con->req);
- if (ipv6) {
- allowed_v6 = (wg_allowedip){
- .family = AF_INET6,
- .cidr = 128,
- .ip6 = *ipv6,
- };
- *cur = &allowed_v6;
- }
+ if (close(con->fd))
+ debug("Failed to close socket\n");
- if (wg_set_device(&dev))
- fatal("wg_set_device()");
+ con->fd = -1;
+ memset(con->pubkey, 0, sizeof con->pubkey);
+ free(con->outbuf);
+ con->outbuf = NULL;
+ con->buflen = 0;
}
-static int response_request_ip(struct wg_dynamic_attr *cur, wg_key pubkey,
- struct wg_dynamic_lease **lease)
+static bool send_response(struct wg_dynamic_connection *con)
{
- 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 E_INVALID_REQ;
-
- *lease = new_lease(pubkey, leasetime, ipv4, ipv6);
- if (!*lease)
- return E_IP_UNAVAIL;
-
- return E_NO_ERROR;
+ char buf[MAX_RESPONSE_SIZE];
+ size_t msglen;
+
+ switch (con->req.cmd) {
+ case WGKEY_REQUEST_IP:;
+ struct wg_dynamic_request_ip *rip = con->req.result;
+ struct in6_addr *lladdr = &con->lladdr;
+ struct in_addr *ip4 = rip->has_ipv4 ? &rip->ipv4 : NULL;
+ struct in6_addr *ip6 = rip->has_ipv6 ? &rip->ipv6 : NULL;
+ struct wg_dynamic_lease *lease;
+ struct wg_dynamic_request_ip ans = { 0 };
+
+ lease = set_lease(wg_interface, con->pubkey, leasetime, lladdr, ip4, ip6);
+ if (lease) {
+ memcpy(&ans.ipv4, &lease->ipv4, sizeof ans.ipv4);
+ memcpy(&ans.ipv6, &lease->ipv6, sizeof ans.ipv6);
+ ans.has_ipv4 = ans.has_ipv6 = true;
+ ans.start = lease->start_real;
+ ans.leasetime = lease->leasetime;
+ } else {
+ ans.wg_errno = E_IP_UNAVAIL;
+ }
+
+ msglen = serialize_request_ip(false, buf, sizeof buf, &ans);
+ break;
+ default:
+ debug("Unknown command: %d\n", con->req.cmd);
+ BUG();
+ }
+
+ return send_message(con, (unsigned char *)buf, msglen);
}
-static bool send_response(struct wg_dynamic_request *req)
+static void handle_client(struct wg_dynamic_connection *con)
{
- char buf[MAX_RESPONSE_SIZE];
- struct wg_dynamic_attr *cur = req->first;
- struct wg_dynamic_lease *lease;
- size_t msglen;
- int ret;
+ unsigned char buf[RECV_BUFSIZE + MAX_LINESIZE];
+ size_t rem = 0;
+ ssize_t ret;
- switch (req->cmd) {
- case WGKEY_REQUEST_IP:
- ret = response_request_ip(cur, req->pubkey, &lease);
- if (ret)
- break;
-
- add_allowed_ips(req->pubkey, &lease->ipv4, &lease->ipv6);
- msglen = serialize_lease(buf, sizeof buf, lease);
- break;
- default:
- debug("Unknown command: %d\n", req->cmd);
- BUG();
- }
+ while ((ret = handle_request(con->fd, &con->req, buf, &rem)) > 0) {
+ if (!send_response(con)) {
+ close_connection(con);
+ break;
+ }
- if (ret)
- return send_error(req, ret);
+ free_wg_dynamic_request(&con->req);
+ }
- return send_message(req, buf, msglen);
+ if (ret < 0)
+ close_connection(con);
}
static void setup_sockets()
{
- int val = 1, res;
- struct sockaddr_in6 addr = {
- .sin6_family = AF_INET6,
- .sin6_port = htons(WG_DYNAMIC_PORT),
- .sin6_addr = well_known,
- .sin6_scope_id = device->ifindex,
- };
-
- sockfd = socket(AF_INET6, SOCK_STREAM, 0);
- if (sockfd < 0)
- fatal("Creating a socket failed");
-
- res = fcntl(sockfd, F_GETFL, 0);
- if (res < 0 || fcntl(sockfd, F_SETFL, res | O_NONBLOCK) < 0)
- fatal("Setting socket to nonblocking failed");
-
- if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof val))
- fatal("Setting socket option failed");
-
- if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) == -1)
- fatal("Binding socket failed");
-
- if (listen(sockfd, SOMAXCONN) == -1)
- fatal("Listening to socket failed");
-
- /* netlink route socket */
- nlsock = mnl_socket_open(NETLINK_ROUTE);
- if (!nlsock)
- fatal("mnl_socket_open(NETLINK_ROUTE)");
-
- res = fcntl(mnl_socket_get_fd(nlsock), F_GETFL, 0);
- if (res < 0 ||
- fcntl(mnl_socket_get_fd(nlsock), F_SETFL, res | O_NONBLOCK) < 0)
- fatal("Setting netlink socket to nonblocking failed");
-
- if (mnl_socket_bind(nlsock, 0, MNL_SOCKET_AUTOPID) < 0)
- fatal("mnl_socket_bind()");
-
- val = RTNLGRP_IPV4_ROUTE;
- if (mnl_socket_setsockopt(nlsock, NETLINK_ADD_MEMBERSHIP, &val,
- sizeof val) < 0)
- fatal("mnl_socket_setsockopt()");
-
- val = RTNLGRP_IPV6_ROUTE;
- if (mnl_socket_setsockopt(nlsock, NETLINK_ADD_MEMBERSHIP, &val,
- sizeof val) < 0)
- fatal("mnl_socket_setsockopt()");
+ int val = 1, res;
+ struct sockaddr_in6 addr = {
+ .sin6_family = AF_INET6,
+ .sin6_port = htons(WG_DYNAMIC_PORT),
+ .sin6_addr = well_known,
+ .sin6_scope_id = device->ifindex,
+ };
+
+ sockfd = socket(AF_INET6, SOCK_STREAM, 0);
+ if (sockfd < 0)
+ fatal("Creating a socket failed");
+
+ res = fcntl(sockfd, F_GETFL, 0);
+ if (res < 0 || fcntl(sockfd, F_SETFL, res | O_NONBLOCK) < 0)
+ fatal("Setting socket to nonblocking failed");
+
+ if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof val))
+ fatal("Setting socket option failed");
+
+ if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) == -1)
+ fatal("Binding socket failed");
+
+ if (listen(sockfd, SOMAXCONN) == -1)
+ fatal("Listening to socket failed");
+
+ /* netlink route socket */
+ nlsock = mnl_socket_open(NETLINK_ROUTE);
+ if (!nlsock)
+ fatal("mnl_socket_open(NETLINK_ROUTE)");
+
+ res = fcntl(mnl_socket_get_fd(nlsock), F_GETFL, 0);
+ if (res < 0 ||
+ fcntl(mnl_socket_get_fd(nlsock), F_SETFL, res | O_NONBLOCK) < 0)
+ fatal("Setting netlink socket to nonblocking failed");
+
+ if (mnl_socket_bind(nlsock, 0, MNL_SOCKET_AUTOPID) < 0)
+ fatal("mnl_socket_bind()");
+
+ val = RTNLGRP_IPV4_ROUTE;
+ if (mnl_socket_setsockopt(nlsock, NETLINK_ADD_MEMBERSHIP, &val,
+ sizeof val) < 0)
+ fatal("mnl_socket_setsockopt()");
+
+ val = RTNLGRP_IPV6_ROUTE;
+ if (mnl_socket_setsockopt(nlsock, NETLINK_ADD_MEMBERSHIP, &val,
+ sizeof val) < 0)
+ fatal("mnl_socket_setsockopt()");
}
static void cleanup()
{
- leases_free();
- kh_destroy(allowedht, allowedips_ht);
- wg_free_device(device);
+ leases_free();
+ kh_destroy(allowedht, allowedips_ht);
+ wg_free_device(device);
- if (nlsock)
- mnl_socket_close(nlsock);
+ if (nlsock)
+ mnl_socket_close(nlsock);
- if (sockfd >= 0)
- close(sockfd);
+ if (sockfd >= 0)
+ close(sockfd);
- if (epollfd >= 0)
- close(epollfd);
+ if (epollfd >= 0)
+ close(epollfd);
- for (int i = 0; i < MAX_CONNECTIONS; ++i) {
- if (requests[i].fd < 0)
- continue;
+ for (int i = 0; i < MAX_CONNECTIONS; ++i) {
+ if (connections[i].fd < 0)
+ continue;
- close_connection(&requests[i]);
- }
+ close_connection(&connections[i]);
+ }
}
static void setup()
{
+ struct wg_combined_ip ip;
+ int ret;
+
if (inet_pton(AF_INET6, WG_DYNAMIC_ADDR, &well_known) != 1)
fatal("inet_pton()");
- allowedips_ht = kh_init(allowedht);
+ allowedips_ht = kh_init(allowedht);
+
+ for (int i = 0; i < MAX_CONNECTIONS; ++i)
+ connections[i].fd = -1;
- for (int i = 0; i < MAX_CONNECTIONS; ++i)
- requests[i].fd = -1;
+ if (atexit(cleanup))
+ die("Failed to set exit function\n");
- if (atexit(cleanup))
- die("Failed to set exit function\n");
+ rebuild_allowedips_ht();
- rebuild_allowedips_ht();
+ ipm_init();
+ ret = ipm_getlladdr(device->ifindex, &ip);
+ if (ret == -1)
+ fatal("ipm_getlladdr()");
+ if (ret == -2)
+ die("Interface must not have multiple link-local addresses assigned\n");
+ ipm_free();
- if (!validate_link_local_ip(device->ifindex))
- // TODO: assign IP instead?
+ if (ret == -1 || ip.family != AF_INET6 ||
+ memcmp(&ip.ip6, well_known.s6_addr, 16))
+ /* TODO: assign IP instead? */
die("%s needs to have %s assigned\n", wg_interface,
WG_DYNAMIC_ADDR);
+ if (ip.cidr != 64)
+ die("Link-local address must have a CIDR of 64\n");
+
if (!valid_peer_found(device))
die("%s has no peers with link-local allowedips\n",
wg_interface);
- setup_sockets();
- leases_init("leases_file", nlsock);
+ setup_sockets();
+ leases_init("leases_file", nlsock);
}
static int get_avail_request()
{
- for (int nfds = 0;; ++nfds) {
- if (nfds >= MAX_CONNECTIONS)
- return -1;
+ for (int nfds = 0;; ++nfds) {
+ if (nfds >= MAX_CONNECTIONS)
+ return -1;
- if (requests[nfds].fd < 0)
- return nfds;
- }
+ if (connections[nfds].fd < 0)
+ return nfds;
+ }
}
-static void accept_incoming(int sockfd, int epollfd,
- struct wg_dynamic_request *requests)
+static void accept_incoming()
{
int n, fd;
struct epoll_event ev;
while ((n = get_avail_request()) >= 0) {
- fd = accept_connection(sockfd, &requests[n].pubkey);
+ fd = accept_connection(&connections[n].pubkey, &connections[n].lladdr);
if (fd < 0) {
if (fd == -ENOENT) {
debug("Failed to match IP to pubkey\n");
continue;
- } else if (fd != -EAGAIN && fd != -EWOULDBLOCK) {
- debug("Failed to accept connection: %s\n",
- strerror(-fd));
- continue;
+ } else if (fd == -EAGAIN || fd == -EWOULDBLOCK) {
+ return;
}
- break;
+ debug("Failed to accept connection: %s\n",
+ strerror(-fd));
+ continue;
}
ev.events = EPOLLIN | EPOLLET;
- ev.data.ptr = &requests[n];
+ ev.data.ptr = &connections[n];
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) == -1)
fatal("epoll_ctl()");
- requests[n].fd = fd;
+ connections[n].fd = fd;
}
}
static void handle_event(void *ptr, uint32_t events)
{
- struct wg_dynamic_request *req;
-
- if (ptr == &sockfd) {
- accept_incoming(sockfd, epollfd, requests);
- return;
- }
-
- if (ptr == nlsock) {
- leases_update_pools(nlsock);
- return;
- }
-
- req = (struct wg_dynamic_request *)ptr;
- if (events & EPOLLIN) {
- if (handle_request(req, send_response, send_error))
- close_connection(req);
- }
-
- if (events & EPOLLOUT) {
- if (send_message(req, req->buf, req->buflen))
- close_connection(req);
- }
+ struct wg_dynamic_connection *con;
+
+ if (ptr == &sockfd) {
+ accept_incoming();
+ return;
+ }
+
+ if (ptr == nlsock) {
+ leases_update_pools(nlsock);
+ return;
+ }
+
+ con = (struct wg_dynamic_connection *)ptr;
+ if (events & EPOLLIN) {
+ handle_client(con);
+ }
+
+ if (events & EPOLLOUT) {
+ if (!send_message(con, con->outbuf, con->buflen))
+ close_connection(con);
+ }
}
static void poll_loop()
{
- struct epoll_event ev, events[MAX_CONNECTIONS];
- epollfd = epoll_create1(0);
- if (epollfd == -1)
- fatal("epoll_create1()");
-
- ev.events = EPOLLIN | EPOLLET;
- ev.data.ptr = &sockfd;
- if (epoll_ctl(epollfd, EPOLL_CTL_ADD, sockfd, &ev))
- fatal("epoll_ctl()");
-
- ev.events = EPOLLIN;
- ev.data.ptr = nlsock;
- if (epoll_ctl(epollfd, EPOLL_CTL_ADD, mnl_socket_get_fd(nlsock), &ev))
- fatal("epoll_ctl()");
-
- while (1) {
- time_t next = leases_refresh() * 1000;
- int nfds = epoll_wait(epollfd, events, MAX_CONNECTIONS, next);
- if (nfds == -1) {
- if (errno == EINTR)
- continue;
-
- fatal("epoll_wait()");
- }
-
- for (int i = 0; i < nfds; ++i)
- handle_event(events[i].data.ptr, events[i].events);
- }
+ struct epoll_event ev, events[MAX_CONNECTIONS];
+ epollfd = epoll_create1(0);
+ if (epollfd == -1)
+ fatal("epoll_create1()");
+
+ ev.events = EPOLLIN | EPOLLET;
+ ev.data.ptr = &sockfd;
+ if (epoll_ctl(epollfd, EPOLL_CTL_ADD, sockfd, &ev))
+ fatal("epoll_ctl()");
+
+ ev.events = EPOLLIN;
+ ev.data.ptr = nlsock;
+ if (epoll_ctl(epollfd, EPOLL_CTL_ADD, mnl_socket_get_fd(nlsock), &ev))
+ fatal("epoll_ctl()");
+
+ while (1) {
+ time_t next = leases_refresh(wg_interface) * 1000;
+ int nfds = epoll_wait(epollfd, events, MAX_CONNECTIONS, next);
+ if (nfds == -1) {
+ if (errno == EINTR)
+ continue;
+
+ fatal("epoll_wait()");
+ }
+
+ for (int i = 0; i < nfds; ++i)
+ handle_event(events[i].data.ptr, events[i].events);
+ }
}
int main(int argc, char *argv[])
{
- progname = argv[0];
- if (argc != 2)
- usage();
+ char *endptr = NULL;
+
+ progname = argv[0];
+ if (argc != 3)
+ usage();
+
+ wg_interface = argv[1];
+ leasetime = (uint32_t) strtoul(argv[2], &endptr, 10);
+ if (*endptr)
+ usage();
- wg_interface = argv[1];
- setup();
+ setup();
- poll_loop();
+ poll_loop();
- return 0;
+ return 0;
}