From e3ee9edc258eaaed4bcc2cd948fdca2249c369b5 Mon Sep 17 00:00:00 2001 From: Linus Nordberg Date: Thu, 25 Apr 2019 11:57:33 +0200 Subject: Use blocking I/O in client Avoiding the unnecessary complexity of non-blocking I/O. --- wg-dynamic-client.c | 158 +++++++++++++--------------------------------------- 1 file changed, 39 insertions(+), 119 deletions(-) diff --git a/wg-dynamic-client.c b/wg-dynamic-client.c index 1bef297..4e3ea24 100644 --- a/wg-dynamic-client.c +++ b/wg-dynamic-client.c @@ -14,17 +14,16 @@ #include #include #include +#include #include "common.h" #include "dbg.h" #include "netlink.h" -#define LEASE_CHECK_INTERVAL 1000 /* 1s is convenient for testing */ - static const char *progname; static const char *wg_interface; static wg_device *device = NULL; -static struct pollfd pollfds[1]; +static int our_fd = -1; static struct in6_addr our_lladdr = { 0 }; static struct wg_combined_ip our_gaddr4 = { 0 }; static struct wg_combined_ip our_gaddr6 = { 0 }; @@ -171,29 +170,9 @@ static bool get_and_validate_local_addrs(uint32_t ifindex, return !IN6_IS_ADDR_UNSPECIFIED(cb_data.lladdr); } -#if 0 -static void dump_leases() +static int try_connect(int *fd) { - char ip4str[INET6_ADDRSTRLEN], ip6str[INET6_ADDRSTRLEN]; - struct wg_dynamic_lease *l = &our_lease; - - if (l->start == 0) { - debug("lease NONE\n"); - return; - } - - debug("lease %u %u %s/%u %s/%u\n", l->start + l->leasetime, - l->start + l->leasetime - current_time(), - inet_ntop(AF_INET, &l->ip4.ip.ip4, ip4str, INET6_ADDRSTRLEN), - l->ip4.cidr, - inet_ntop(AF_INET6, &l->ip6.ip.ip6, ip6str, INET6_ADDRSTRLEN), - l->ip6.cidr); -} -#endif - -static int do_connect(int *fd) -{ - int res; + struct timeval tval = {.tv_sec = 1, .tv_usec = 0 }; struct sockaddr_in6 our_addr = { .sin6_family = AF_INET6, .sin6_addr = our_lladdr, @@ -210,49 +189,39 @@ static int do_connect(int *fd) if (*fd < 0) fatal("Creating a socket failed"); + if (setsockopt(*fd, SOL_SOCKET, SO_RCVTIMEO, &tval, sizeof tval) == -1) + fatal("Setting socket option failed"); + if (bind(*fd, (struct sockaddr *)&our_addr, sizeof(our_addr))) fatal("Binding socket failed"); if (inet_pton(AF_INET6, WG_DYNAMIC_ADDR, &their_addr.sin6_addr) != 1) fatal("inet_pton()"); + if (connect(*fd, (struct sockaddr *)&their_addr, sizeof(struct sockaddr_in6))) { char out[INET6_ADDRSTRLEN]; + if (!inet_ntop(their_addr.sin6_family, &their_addr.sin6_addr, out, sizeof out)) fatal("inet_ntop()"); debug("Connecting to [%s]:%u failed: %s\n", out, ntohs(their_addr.sin6_port), strerror(errno)); + if (close(*fd)) debug("Closing socket failed: %s\n", strerror(errno)); *fd = -1; return -1; } - res = fcntl(*fd, F_GETFL, 0); - if (res < 0 || fcntl(*fd, F_SETFL, res | O_NONBLOCK) < 0) - fatal("Setting socket to nonblocking failed"); - return 0; } -static size_t connect_and_send(unsigned char *buf, size_t *len) -{ - size_t ret; - if (pollfds[0].fd < 0) - if (do_connect(&pollfds[0].fd)) - return 0; - ret = send_message(pollfds[0].fd, buf, len); - return ret; -} - -static bool request_ip(struct wg_dynamic_request *req, - const struct wg_dynamic_lease *lease) +static void request_ip(int fd, const struct wg_dynamic_lease *lease) { unsigned char buf[MAX_RESPONSE_SIZE + 1]; char addrstr[INET6_ADDRSTRLEN]; size_t msglen; - size_t written; msglen = 0; msglen += print_to_buf((char *)buf, sizeof buf, msglen, "%s=%d\n", @@ -277,50 +246,20 @@ static bool request_ip(struct wg_dynamic_request *req, msglen += print_to_buf((char *)buf, sizeof buf, msglen, "\n"); - written = connect_and_send(buf, &msglen); - if (msglen == 0) - return true; - - debug("Socket %d blocking with %lu bytes to write, postponing\n", - pollfds[0].fd, msglen); - send_later(req, buf + written, msglen); - return false; + send_message(fd, buf, &msglen); } -static int maybe_refresh_lease(uint32_t now, struct wg_dynamic_lease *lease, - struct wg_dynamic_request *req) +static uint32_t time_until_refresh(uint32_t now, struct wg_dynamic_lease *lease) { - if (now > lease->start + (lease->leasetime * 2) / 3) { - debug("Refreshing lease expiring on %u\n", - lease->start + lease->leasetime); - request_ip(req, lease); - return 0; - } + uint32_t refresh_at; - return 1; -} - -static bool lease_is_valid(uint32_t now, struct wg_dynamic_lease *lease) -{ - return now < lease->start + lease->leasetime; -} - -static void maybe_remove_lease(uint32_t now, struct wg_dynamic_lease *lease) -{ - if (!lease_is_valid(now, lease)) - memset(lease, 0, sizeof *lease); -} - -static void check_leases(struct wg_dynamic_request *req) -{ - uint32_t now = current_time(); + if (lease->leasetime == 0) + return 0; + refresh_at = lease->start + (lease->leasetime * 8) / 10; - if (!lease_is_valid(now, &our_lease)) - request_ip(req, NULL); - else { - maybe_remove_lease(now, &our_lease); - maybe_refresh_lease(now, &our_lease, req); - } + if (refresh_at < now) + return 0; + return refresh_at - now; } static int handle_received_lease(const struct wg_dynamic_request *req) @@ -387,9 +326,8 @@ static int handle_received_lease(const struct wg_dynamic_request *req) static void cleanup() { wg_free_device(device); - if (pollfds[0].fd >= 0) - if (close(pollfds[0].fd)) - debug("Failed to close fd"); + if (our_fd != -1 && close(our_fd)) + debug("Failed to close fd %d\n", our_fd); } static bool handle_error(int fd, int ret) @@ -446,10 +384,17 @@ static bool handle_response(int fd, struct wg_dynamic_request *req) return true; } +static bool read_response(int fd, struct wg_dynamic_request *req, + bool (*success)(int, struct wg_dynamic_request *), + bool (*error)(int, int)) +{ + return handle_request(fd, req, success, error); +} + int main(int argc __attribute__((unused)), char *argv[] __attribute__((unused))) { + int *fd = &our_fd; struct wg_dynamic_request req = { 0 }; - uint32_t now = current_time(); progname = argv[0]; if (argc != 2) @@ -462,8 +407,6 @@ int main(int argc __attribute__((unused)), char *argv[] __attribute__((unused))) if (atexit(cleanup)) die("Failed to set exit function\n"); - if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) - fatal("Unable to ignore SIGPIPE"); if (!get_and_validate_local_addrs(device->ifindex, &our_lladdr, &our_gaddr4, &our_gaddr6)) @@ -475,9 +418,11 @@ int main(int argc __attribute__((unused)), char *argv[] __attribute__((unused))) debug("%s: %s\n", wg_interface, inet_ntop(AF_INET6, &our_lladdr, lladr_str, sizeof lladr_str)); + /* 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)) { - our_lease.start = now; + our_lease.start = current_time(); our_lease.leasetime = 15; memcpy(&our_lease.ip4, &our_gaddr4, sizeof(struct wg_combined_ip)); @@ -485,42 +430,17 @@ int main(int argc __attribute__((unused)), char *argv[] __attribute__((unused))) sizeof(struct wg_combined_ip)); } - /* TODO: use a blocking socket instead of the unnecessary - * complexity of nonblocking */ - - pollfds[0] = (struct pollfd){ .fd = -1, .events = POLLIN }; while (1) { - size_t off; - int nevents = poll(pollfds, 1, LEASE_CHECK_INTERVAL); - - if (nevents == -1) - fatal("poll()"); + sleep(MAX(1, time_until_refresh(current_time(), &our_lease))); - if (nevents == 0) { - /* FIXME: if there's any risk for this path to - * be starving, maybe do this regardless of - * socket readiness? */ - check_leases(&req); + if (*fd == -1 && try_connect(fd)) continue; - } - if (pollfds[0].revents & POLLOUT) { - debug("sending, trying again with %lu bytes\n", - req.buflen); - off = send_message(pollfds[0].fd, req.buf, &req.buflen); - if (req.buflen) - memmove(req.buf, req.buf + off, req.buflen); - else - close_connection(&pollfds[0].fd, &req); - } + request_ip(*fd, &our_lease); - if (pollfds[0].revents & POLLIN) { - if (handle_request(pollfds[0].fd, &req, handle_response, - handle_error)) - close_connection(&pollfds[0].fd, &req); - else if (req.buf) - pollfds[0].events |= POLLOUT; - } + while (!read_response(*fd, &req, handle_response, handle_error)) + sleep(1); + close_connection(fd, &req); } return 0; -- cgit v1.2.3-59-g8ed1b