diff options
Diffstat (limited to 'wg-dynamic-server.c')
-rw-r--r-- | wg-dynamic-server.c | 798 |
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; } |