From 9ce9b5cd8267341050c8e64ccd815d821f8b9f5c Mon Sep 17 00:00:00 2001 From: yrutschle Date: Sat, 2 Apr 2022 18:48:24 +0200 Subject: [PATCH] Integrate hash for UDP --- Makefile | 4 +- hashtest/htest | Bin 25216 -> 25312 bytes processes.h | 2 + sslh-ev.c | 3 ++ sslh-select.c | 1 + t_load | 34 +++++++------- udp-listener.c | 119 ++++++++++++++++++++++++++++++++++++++++--------- udp-listener.h | 3 ++ 8 files changed, 125 insertions(+), 41 deletions(-) diff --git a/Makefile b/Makefile index e470246..aa625c6 100644 --- a/Makefile +++ b/Makefile @@ -29,8 +29,8 @@ CFLAGS ?=-Wall -DLIBPCRE -g $(CFLAGS_COV) LIBS=-lm -lpcre2-8 OBJS=sslh-conf.o common.o log.o sslh-main.o probe.o tls.o argtable3.o collection.o gap.o FORK_OBJS=$(OBJS) sslh-fork.o -SELECT_OBJS=$(OBJS) processes.o udp-listener.o sslh-select.o -EV_OBJS=$(OBJS) processes.o udp-listener.o sslh-ev.o +SELECT_OBJS=$(OBJS) processes.o udp-listener.o sslh-select.o hash.o +EV_OBJS=$(OBJS) processes.o udp-listener.o sslh-ev.o hash.o CONDITIONAL_TARGETS= diff --git a/hashtest/htest b/hashtest/htest index 55dc1c826c084febd1827f72a982837d2aa404a2..b525ec4f1be9fb5f019a89ea2f72304b88a59b13 100755 GIT binary patch delta 3543 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 aT|i4W%+arJPzhXtof{s+MRsIE;ePBlMn diff --git a/processes.h b/processes.h index a15d400..541d133 100644 --- a/processes.h +++ b/processes.h @@ -16,6 +16,8 @@ struct loop_info { * select() */ gap_array* probing_list; /* Pointers to cnx that are in probing mode */ + hash* hash_sources; /* UDP remote sources previously encountered */ + watchers* watchers; cnx_collection* collection; /* Collection of connections linked to this loop */ diff --git a/sslh-ev.c b/sslh-ev.c index 8410730..ff6f365 100644 --- a/sslh-ev.c +++ b/sslh-ev.c @@ -25,6 +25,7 @@ #include "processes.h" #include "gap.h" #include "log.h" +#include "udp-listener.h" const char* server_type = "sslh-ev"; @@ -147,6 +148,8 @@ void main_loop(struct listen_endpoint listen_sockets[], int num_addr_listen) ev_info.collection = collection_init(0); ev_info.probing_list = gap_init(0); + udp_init(&ev_info); + watchers_init(&ev_info.watchers, listen_sockets, num_addr_listen); ev_set_userdata(EV_A_ &ev_info); diff --git a/sslh-select.c b/sslh-select.c index c65847f..f15365b 100644 --- a/sslh-select.c +++ b/sslh-select.c @@ -135,6 +135,7 @@ void main_loop(struct listen_endpoint listen_sockets[], int num_addr_listen) fd_info.num_probing = 0; fd_info.probing_list = gap_init(0); + udp_init(&fd_info); watchers_init(&fd_info.watchers, listen_sockets, num_addr_listen); diff --git a/t_load b/t_load index 8131605..2fc6a5f 100755 --- a/t_load +++ b/t_load @@ -20,7 +20,7 @@ use Conf::Libconfig; # How many total clients to we start? Each client will pick # a new protocol among what's in test.cfg. -my $NUM_CNX = 50; +my $NUM_CNX = 16; # Delay between starting new processes when starting up. If # you start 200 processes in under a second, things go wrong @@ -35,7 +35,7 @@ my $block_rpt = 5; # Probability to stop a client after a message (e.g. with # .01 a client will send an average of 100 messages before # disconnecting). -my $stop_client_probability = .001; +my $stop_client_probability = .0001; ##END CONFIG @@ -65,21 +65,21 @@ my %connect_params = ( test_data => "foo bar", resp_len => 12, }, - ssh => { - sleep => 20, # So it times out 50% of connections - test_data => "SSH-2.0 hello", - resp_len => 18, # length "ssh: SSH-2.0 hello" => 18 - }, - tinc => { - sleep => 0, - test_data => "0 ", - resp_len => 8, # length "tinc: 0 " => 10 - }, - openvpn => { - sleep => 0, - test_data => "\x00\x00", - resp_len => 11, # length "openvpn: \x0\x0" => 11 - }, + # ssh => { + # sleep => 20, # So it times out 50% of connections + # test_data => "SSH-2.0 hello", + # resp_len => 18, # length "ssh: SSH-2.0 hello" => 18 + # }, + # tinc => { + # sleep => 0, + # test_data => "0 ", + # resp_len => 8, # length "tinc: 0 " => 10 + # }, + # openvpn => { + # sleep => 0, + # test_data => "\x00\x00", + # resp_len => 11, # length "openvpn: \x0\x0" => 11 + # }, ); sub connect_service { diff --git a/udp-listener.c b/udp-listener.c index ea5d4f4..2b065f7 100644 --- a/udp-listener.c +++ b/udp-listener.c @@ -27,6 +27,9 @@ #include "sslh-conf.h" #include "udp-listener.h" +typedef struct connection* hash_item; +#include "hash.h" + /* returns date at which this socket times out. */ static int udp_timeout(struct connection* cnx) @@ -36,6 +39,89 @@ static int udp_timeout(struct connection* cnx) return cnx->proto->udp_timeout + cnx->last_active; } +/* Incoming connections are of course all received on a single socket. Create a + * hash that associates (incoming sockaddr) => struct connection*, so finding + * the connection related to an incoming packet is fast. + */ + + + +static int cnx_cmp(struct connection* cnx1, struct connection* cnx2) +{ + struct sockaddr* addr1 = &cnx1->client_addr; + socklen_t addrlen1 = cnx1->addrlen; + + struct sockaddr* addr2 = &cnx2->client_addr; + socklen_t addrlen2 = cnx2->addrlen; + + if (addrlen1 != addrlen2) return -1; + + return memcmp(addr1, addr2, addrlen1); +} + +/* From an IP address, create something that's useable as a hash key. + * Currently: + * lowest bytes of remote port */ +static int hash_make_key(hash_item new) +{ + struct sockaddr* addr = &new->client_addr; + //socklen_t addrlen = new->addrlen; + struct sockaddr_in* addr4; + struct sockaddr_in6* addr6; + int out; + + switch (addr->sa_family) { + case AF_INET: + addr4 = (struct sockaddr_in*)addr; + out = addr4->sin_port; + break; + + case AF_INET6: + addr6 = (struct sockaddr_in6*)addr; + out = addr6->sin6_port; + break; + + default: /* Just use the first bytes, skipping the address family */ + out = ((char*)addr)[2]; + break; + } + return out; +} + +/* Init the UDP subsystem. + * - Initialise the hash + * - that's all, folks + * */ +void udp_init(struct loop_info* fd_info) +{ + fd_info->hash_sources = hash_init(&hash_make_key, &cnx_cmp); +} + + +/* Find if the specified source has been seen before. + * If yes, returns file descriptor of connection + * If not, returns -1 + * */ +static int known_source(hash* h, struct sockaddr* addr, socklen_t addrlen) +{ + struct connection search; + search.client_addr = *addr; + search.addrlen = addrlen; + + struct connection* cnx = hash_find(h, &search); + if (!cnx) return -1; + return cnx->q[0].fd; +} + + + +static int new_source(hash* h, struct connection* new) +{ + return hash_insert(h, new); +} + + + /* 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 @@ -71,6 +157,7 @@ void udp_timeouts(struct loop_info* fd_info) watchers_del_read(fd_info->watchers, i); watchers_del_write(fd_info->watchers, i); collection_remove_cnx(fd_info->collection, cnx); + hash_remove(fd_info->hash_sources, cnx); } else { if (timeout < next_timeout) next_timeout = timeout; } @@ -81,27 +168,6 @@ void udp_timeouts(struct loop_info* fd_info) fd_info->next_timeout = next_timeout; } -/* Find if the specified source has been seen before. -1 if not found - * - * TODO This is linear search and needs to be changed to something better for - * production if we have more than a dozen sources - * Also, this assumes src_addr from recvfrom() are repeatable for a specific - * source... - * */ -static int known_source(cnx_collection* collection, int max_fd, struct sockaddr* addr, socklen_t addrlen) -{ - int i; - - for (i = 0; i < max_fd; i++) { - struct connection* cnx = collection_get_cnx_from_fd(collection, i); - if (cnx && (cnx->type == SOCK_DGRAM) && cnx->target_sock) { - if (!memcmp(&cnx->client_addr, addr, addrlen)) { - return i; - } - } - } - return -1; -} /* Process UDP coming from outside (client towards server) * If it's a new source, probe; otherwise, forward to previous target @@ -124,6 +190,7 @@ int udp_c2s_forward(int sockfd, struct loop_info* fd_info) This will do. Dynamic allocation is possible with the MSG_PEEK flag in recvfrom(2), but that'd imply malloc/free overhead for each packet, when really 64K is not that much */ + udp_timeouts(fd_info); addrlen = sizeof(src_addr); @@ -132,7 +199,7 @@ int udp_c2s_forward(int sockfd, struct loop_info* fd_info) perror("recvfrom"); return -1; } - target = known_source(collection, max_fd, &src_addr, addrlen); + target = known_source(fd_info->hash_sources, &src_addr, addrlen); addrinfo.ai_addr = &src_addr; addrinfo.ai_addrlen = addrlen; print_message(msg_probe_info, "received %ld UDP from %d:%s\n", @@ -160,6 +227,13 @@ int udp_c2s_forward(int sockfd, struct loop_info* fd_info) cnx->client_addr = src_addr; cnx->addrlen = addrlen; cnx->local_endpoint = sockfd; + + res = new_source(fd_info->hash_sources, cnx); + if (res == -1) { + print_message(msg_connections_error, "Out of hash space for new incoming UDP connection"); + collection_remove_cnx(collection, cnx); + return -1; + } } cnx = collection_get_cnx_from_fd(collection, target); @@ -169,6 +243,7 @@ int udp_c2s_forward(int sockfd, struct loop_info* fd_info) cnx->last_active = time(NULL); print_message(msg_fd, "sending %d to %s\n", res, sprintaddr(data, sizeof(data), cnx->proto->saddr)); + return out; } diff --git a/udp-listener.h b/udp-listener.h index 23a62dc..67c2a00 100644 --- a/udp-listener.h +++ b/udp-listener.h @@ -21,4 +21,7 @@ int udp_c2s_forward(int sockfd, struct loop_info* fd_info); /* Process UDP coming from inside (server towards client) */ void udp_s2c_forward(struct connection* cnx); + +void udp_init(struct loop_info* fd_info); + #endif /* UDPLISTENER_H */