From 40da147efd4859c3da0b5b51a9f47f0c15186d09 Mon Sep 17 00:00:00 2001 From: yrutschle Date: Fri, 6 Aug 2021 22:29:00 +0200 Subject: [PATCH] keep track of next UDP timeout to only go through all connections when that happens --- sslh-select.c | 35 +++++++++++++++++++++++++++++------ udp-listener.c | 12 +++--------- udp-listener.h | 7 ++++--- 3 files changed, 36 insertions(+), 18 deletions(-) diff --git a/sslh-select.c b/sslh-select.c index 00dfa32..b5c7301 100644 --- a/sslh-select.c +++ b/sslh-select.c @@ -30,6 +30,8 @@ #define __LINUX__ +#include + #include "common.h" #include "probe.h" #include "udp-listener.h" @@ -51,6 +53,8 @@ struct select_info { fd_set fds_r, fds_w; /* reference fd sets (used to init working copies) */ cnx_collection* collection; /* Collection of connections linked to this loop */ + + time_t next_timeout; /* time at which next UDP connection times out */ }; @@ -460,22 +464,41 @@ void cnx_accept_process(struct select_info* fd_info, struct listen_endpoint* lis } -void udp_timeouts(struct select_info* fd_info) +/* Check all connections to see if a UDP connections has timed out, then free + * it. At the same time, keep track of the closest, next timeout. Only do the + * search through connections if that timeout actually happened. If the + * connection that would have timed out has had activity, it doesn't matter: we + * go through connections to find the next timeout, which was needed anyway. */ +static void udp_timeouts(struct select_info* fd_info) { time_t now = time(NULL); + if (now < fd_info->next_timeout) return; + for (int i = 0; i < fd_info->max_fd; i++) { + time_t next_timeout = INT_MAX; + /* if it's either in read or write set, there is a connection * behind that file descriptor */ if (FD_ISSET(i, &fd_info->fds_r) || FD_ISSET(i, &fd_info->fds_w)) { struct connection* cnx = collection_get_cnx_from_fd(fd_info->collection, i); - if (cnx && udp_timedout(now, cnx)) { - close(cnx->target_sock); - FD_CLR(i, &fd_info->fds_r); - FD_CLR(i, &fd_info->fds_w); - collection_remove_cnx(fd_info->collection, cnx); + if (cnx) { + time_t timeout = udp_timeout(cnx); + if (cnx && (timeout <= now)) { + if (cfg.verbose > 3) + fprintf(stderr, "timed out UDP %d\n", cnx->target_sock); + close(cnx->target_sock); + FD_CLR(i, &fd_info->fds_r); + FD_CLR(i, &fd_info->fds_w); + collection_remove_cnx(fd_info->collection, cnx); + } else { + if (timeout < next_timeout) next_timeout = timeout; + } } } + + if (next_timeout != INT_MAX) + fd_info->next_timeout = next_timeout; } } diff --git a/udp-listener.c b/udp-listener.c index 6209980..ce1021f 100644 --- a/udp-listener.c +++ b/udp-listener.c @@ -128,17 +128,11 @@ void udp_s2c_forward(struct connection* cnx) } -/* Checks if a connection timed out, in which case close the socket and return - * 1; otherwise return 0. */ -int udp_timedout(time_t now, struct connection* cnx) +/* returns date at which this socket times out. */ +int udp_timeout(struct connection* cnx) { if (cnx->type != SOCK_DGRAM) return 0; /* Not a UDP connection */ - if ((now - cnx->last_active > cnx->proto->udp_timeout)) { - if (cfg.verbose > 3) - fprintf(stderr, "timed out UDP %d\n", cnx->target_sock); - return 1; - } - return 0; + return cnx->proto->udp_timeout + cnx->last_active; } diff --git a/udp-listener.h b/udp-listener.h index 6c043ff..7b44f10 100644 --- a/udp-listener.h +++ b/udp-listener.h @@ -20,8 +20,9 @@ int udp_c2s_forward(int sockfd, cnx_collection* collection, int max_fd); void udp_s2c_forward(struct connection* cnx); -/* Checks if a connection timed out, in which case close the socket and return - * 1; otherwise return 0. */ -int udp_timedout(time_t now, struct connection* cnx); +/* returns how many seconds before socket times out. Negative if timed out + * already. + */ +int udp_timeout(struct connection* cnx); #endif /* UDPLISTENER_H */