aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Nordberg <linus@nordberg.se>2019-04-25 11:57:33 +0200
committerLinus Nordberg <linus@nordberg.se>2019-04-25 13:28:07 +0200
commit0130e48820adc5fdc8e681bfe75aa32cf453f669 (patch)
treeb4616be28d2efbb8019430da8a55c0634bcdbb10
parentNo need to clear bits in pollfd revents (diff)
downloadwg-dynamic-0130e48820adc5fdc8e681bfe75aa32cf453f669.tar.xz
wg-dynamic-0130e48820adc5fdc8e681bfe75aa32cf453f669.zip
Use blocking I/O in client
Avoiding the unnecessary complexity of non-blocking I/O.
-rw-r--r--wg-dynamic-client.c140
1 files changed, 50 insertions, 90 deletions
diff --git a/wg-dynamic-client.c b/wg-dynamic-client.c
index dec21d0..4e78c31 100644
--- a/wg-dynamic-client.c
+++ b/wg-dynamic-client.c
@@ -14,17 +14,16 @@
#include <arpa/inet.h>
#include <libmnl/libmnl.h>
#include <linux/rtnetlink.h>
+#include <sys/param.h>
#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 };
@@ -191,9 +190,9 @@ static void dump_leases()
}
#endif
-static int do_connect(int *fd)
+static int try_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,43 +209,35 @@ 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,
+static bool request_ip(int fd, struct wg_dynamic_request *req,
const struct wg_dynamic_lease *lease)
{
unsigned char buf[MAX_RESPONSE_SIZE + 1];
@@ -277,50 +268,28 @@ 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);
+ written = send_message(fd, buf, &msglen);
if (msglen == 0)
return true;
- debug("Socket %d blocking with %lu bytes to write, postponing\n",
- pollfds[0].fd, msglen);
+ debug("Socket %d blocking with %lu bytes to write, postponing\n", fd,
+ msglen);
send_later(req, buf + written, msglen);
- return false;
-}
-static int maybe_refresh_lease(uint32_t now, struct wg_dynamic_lease *lease,
- struct wg_dynamic_request *req)
-{
- 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;
- }
-
- return 1;
-}
-
-static bool lease_is_valid(uint32_t now, struct wg_dynamic_lease *lease)
-{
- return now < lease->start + lease->leasetime;
+ return false;
}
-static void maybe_remove_lease(uint32_t now, struct wg_dynamic_lease *lease)
+static uint32_t time_until_refresh(uint32_t now, struct wg_dynamic_lease *lease)
{
- if (!lease_is_valid(now, lease))
- memset(lease, 0, sizeof *lease);
-}
+ uint32_t refresh_at;
-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 +356,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 +414,18 @@ 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();
+ uint32_t now = current_time(), naptime;
progname = argv[0];
if (argc != 2)
@@ -462,8 +438,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))
@@ -485,42 +459,28 @@ 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()");
+ now = current_time();
+ naptime = time_until_refresh(now, &our_lease);
+ sleep(MAX(1, naptime));
- 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);
+ /* Short read might only happen if we set SO_SNDTIMEO
+ * which we currently don't. FIXME: Remove? */
+ while (req.buflen) {
+ size_t off = send_message(*fd, req.buf, &req.buflen);
+ memmove(req.buf, req.buf + off, req.buflen);
+ sleep(1);
}
- 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;
- }
+ if (!request_ip(*fd, &req, &our_lease))
+ continue;
+
+ while (!read_response(*fd, &req, handle_response, handle_error))
+ sleep(1);
+ close_connection(fd, &req);
}
return 0;