mirror of
https://github.com/yrutschle/sslh.git
synced 2025-06-02 16:41:12 +03:00
Integrate hash for UDP
This commit is contained in:
parent
97810cf0b2
commit
9ce9b5cd82
4
Makefile
4
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=
|
||||
|
||||
|
BIN
hashtest/htest
BIN
hashtest/htest
Binary file not shown.
@ -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 */
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
34
t_load
34
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 {
|
||||
|
119
udp-listener.c
119
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;
|
||||
}
|
||||
|
||||
|
@ -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 */
|
||||
|
Loading…
x
Reference in New Issue
Block a user