Apply patch from Scott Lamb adding an output buffer for the TCP sockets.

This helps coalescing multiple send_meta() commands into one TCP packet.
Also limit the size of the output buffer before dropping PACKETs.
This commit is contained in:
Guus Sliepen 2006-01-19 17:13:18 +00:00
parent a5a4d2b865
commit 228e7a5c8f
13 changed files with 147 additions and 77 deletions

View file

@ -33,7 +33,8 @@
avl_tree_t *config_tree;
int pingtimeout = 0; /* seconds before timeout */
int pinginterval = 0; /* seconds between pings */
int pingtimeout = 0; /* seconds to wait for response */
char *confbase = NULL; /* directory in which all config files are */
char *netname = NULL; /* name of the vpn network */

View file

@ -36,6 +36,7 @@ typedef struct config_t {
extern avl_tree_t *config_tree;
extern int pinginterval;
extern int pingtimeout;
extern int maxtimeout;
extern bool bypass_security;

View file

@ -121,8 +121,9 @@ void dump_connections(void)
for(node = connection_tree->head; node; node = node->next) {
c = node->data;
logger(LOG_DEBUG, _(" %s at %s options %lx socket %d status %04x"),
c->name, c->hostname, c->options, c->socket, *(uint32_t *)&c->status);
logger(LOG_DEBUG, _(" %s at %s options %lx socket %d status %04x outbuf %d/%d/%d"),
c->name, c->hostname, c->options, c->socket, *(uint32_t *)&c->status,
c->outbufsize, c->outbufstart, c->outbuflen);
}
logger(LOG_DEBUG, _("End of connections."));

View file

@ -91,7 +91,13 @@ typedef struct connection_t {
int tcplen; /* length of incoming TCPpacket */
int allow_request; /* defined if there's only one request possible */
time_t last_ping_time; /* last time we saw some activity from the other end */
char *outbuf; /* metadata output buffer */
int outbufstart; /* index of first meaningful byte in output buffer */
int outbuflen; /* number of meaningful bytes in output buffer */
int outbufsize; /* number of bytes allocated to output buffer */
time_t last_ping_time; /* last time we saw some activity from the other end or pinged them */
time_t last_flushed_time; /* last time buffer was empty. Only meaningful if outbuflen > 0 */
avl_tree_t *config_tree; /* Pointer to configuration tree belonging to him */
} connection_t;

View file

@ -35,9 +35,7 @@
bool send_meta(connection_t *c, const char *buffer, int length)
{
const char *bufp;
int outlen;
char outbuf[MAXBUFSIZE];
int result;
cp();
@ -45,35 +43,73 @@ bool send_meta(connection_t *c, const char *buffer, int length)
ifdebug(META) logger(LOG_DEBUG, _("Sending %d bytes of metadata to %s (%s)"), length,
c->name, c->hostname);
if(!c->outbuflen)
c->last_flushed_time = now;
/* Find room in connection's buffer */
if(length + c->outbuflen > c->outbufsize) {
c->outbufsize = length + c->outbuflen;
c->outbuf = xrealloc(c->outbuf, c->outbufsize);
}
if(length + c->outbuflen + c->outbufstart > c->outbufsize) {
memmove(c->outbuf, c->outbuf + c->outbufstart, c->outbuflen);
c->outbufstart = 0;
}
/* Add our data to buffer */
if(c->status.encryptout) {
result = EVP_EncryptUpdate(c->outctx, outbuf, &outlen, buffer, length);
if(!result || outlen != length) {
result = EVP_EncryptUpdate(c->outctx, c->outbuf + c->outbufstart + c->outbuflen,
&outlen, buffer, length);
if(!result || outlen < length) {
logger(LOG_ERR, _("Error while encrypting metadata to %s (%s): %s"),
c->name, c->hostname, ERR_error_string(ERR_get_error(), NULL));
return false;
} else if(outlen > length) {
logger(LOG_EMERG, _("Encrypted data too long! Heap corrupted!"));
abort();
}
bufp = outbuf;
length = outlen;
} else
bufp = buffer;
c->outbuflen += outlen;
} else {
memcpy(c->outbuf + c->outbufstart + c->outbuflen, buffer, length);
c->outbuflen += length;
}
while(length) {
result = send(c->socket, bufp, length, 0);
return true;
}
bool flush_meta(connection_t *c)
{
int result;
ifdebug(META) logger(LOG_DEBUG, _("Flushing %d bytes to %s (%s)"),
c->outbuflen, c->name, c->hostname);
while(c->outbuflen) {
result = send(c->socket, c->outbuf + c->outbufstart, c->outbuflen, 0);
if(result <= 0) {
if(!errno || errno == EPIPE) {
ifdebug(CONNECTIONS) logger(LOG_NOTICE, _("Connection closed by %s (%s)"),
c->name, c->hostname);
} else if(errno == EINTR)
} else if(errno == EINTR) {
continue;
else
logger(LOG_ERR, _("Sending meta data to %s (%s) failed: %s"), c->name,
} else if(errno == EWOULDBLOCK) {
ifdebug(CONNECTIONS) logger(LOG_DEBUG, _("Flushing %d bytes to %s (%s) would block"),
c->outbuflen, c->name, c->hostname);
return true;
} else {
logger(LOG_ERR, _("Flushing meta data to %s (%s) failed: %s"), c->name,
c->hostname, strerror(errno));
}
return false;
}
bufp += result;
length -= result;
c->outbufstart += result;
c->outbuflen -= result;
}
c->outbufstart = 0; /* avoid unnecessary memmoves */
return true;
}

View file

@ -112,7 +112,7 @@ static void purge(void)
put all file descriptors in an fd_set array
While we're at it, purge stuff that needs to be removed.
*/
static int build_fdset(fd_set * fs)
static int build_fdset(fd_set *readset, fd_set *writeset)
{
avl_node_t *node, *next;
connection_t *c;
@ -120,7 +120,8 @@ static int build_fdset(fd_set * fs)
cp();
FD_ZERO(fs);
FD_ZERO(readset);
FD_ZERO(writeset);
for(node = connection_tree->head; node; node = next) {
next = node->next;
@ -131,22 +132,24 @@ static int build_fdset(fd_set * fs)
if(!connection_tree->head)
purge();
} else {
FD_SET(c->socket, fs);
FD_SET(c->socket, readset);
if(c->outbuflen > 0)
FD_SET(c->socket, writeset);
if(c->socket > max)
max = c->socket;
}
}
for(i = 0; i < listen_sockets; i++) {
FD_SET(listen_socket[i].tcp, fs);
FD_SET(listen_socket[i].tcp, readset);
if(listen_socket[i].tcp > max)
max = listen_socket[i].tcp;
FD_SET(listen_socket[i].udp, fs);
FD_SET(listen_socket[i].udp, readset);
if(listen_socket[i].udp > max)
max = listen_socket[i].udp;
}
FD_SET(device_fd, fs);
FD_SET(device_fd, readset);
if(device_fd > max)
max = device_fd;
@ -208,6 +211,12 @@ void terminate_connection(connection_t *c, bool report)
retry_outgoing(c->outgoing);
c->outgoing = NULL;
}
free(c->outbuf);
c->outbuf = NULL;
c->outbuflen = 0;
c->outbufsize = 0;
c->outbufstart = 0;
}
/*
@ -232,11 +241,11 @@ static void check_dead_connections(void)
if(c->last_ping_time + pingtimeout < now) {
if(c->status.active) {
if(c->status.pinged) {
ifdebug(CONNECTIONS) logger(LOG_INFO, _("%s (%s) didn't respond to PING"),
c->name, c->hostname);
ifdebug(CONNECTIONS) logger(LOG_INFO, _("%s (%s) didn't respond to PING in %d seconds"),
c->name, c->hostname, now - c->last_ping_time);
c->status.timeout = true;
terminate_connection(c, true);
} else {
} else if(c->last_ping_time + pinginterval < now) {
send_ping(c);
}
} else {
@ -257,6 +266,16 @@ static void check_dead_connections(void)
}
}
}
if(c->outbuflen > 0 && c->last_flushed_time + pingtimeout < now) {
if(c->status.active) {
ifdebug(CONNECTIONS) logger(LOG_INFO,
_("%s (%s) could not flush for %d seconds (%d bytes remaining)"),
c->name, c->hostname, now - c->last_flushed_time, c->outbuflen);
c->status.timeout = true;
terminate_connection(c, true);
}
}
}
}
@ -264,7 +283,7 @@ static void check_dead_connections(void)
check all connections to see if anything
happened on their sockets
*/
static void check_network_activity(fd_set * f)
static void check_network_activity(fd_set * readset, fd_set * writeset)
{
connection_t *c;
avl_node_t *node;
@ -274,18 +293,20 @@ static void check_network_activity(fd_set * f)
cp();
if(FD_ISSET(device_fd, f)) {
/* check input from kernel */
if(FD_ISSET(device_fd, readset)) {
if(read_packet(&packet))
route(myself, &packet);
}
/* check meta connections */
for(node = connection_tree->head; node; node = node->next) {
c = node->data;
if(c->status.remove)
continue;
if(FD_ISSET(c->socket, f)) {
if(FD_ISSET(c->socket, readset)) {
if(c->status.connecting) {
c->status.connecting = false;
getsockopt(c->socket, SOL_SOCKET, SO_ERROR, &result, &len);
@ -307,13 +328,20 @@ static void check_network_activity(fd_set * f)
continue;
}
}
if(FD_ISSET(c->socket, writeset)) {
if(!flush_meta(c)) {
terminate_connection(c, c->status.active);
continue;
}
}
}
for(i = 0; i < listen_sockets; i++) {
if(FD_ISSET(listen_socket[i].udp, f))
if(FD_ISSET(listen_socket[i].udp, readset))
handle_incoming_vpn_data(listen_socket[i].udp);
if(FD_ISSET(listen_socket[i].tcp, f))
if(FD_ISSET(listen_socket[i].tcp, readset))
handle_new_meta_connection(listen_socket[i].tcp);
}
}
@ -323,7 +351,7 @@ static void check_network_activity(fd_set * f)
*/
int main_loop(void)
{
fd_set fset;
fd_set readset, writeset;
struct timeval tv;
int r, maxfd;
time_t last_ping_check, last_config_check;
@ -344,9 +372,9 @@ int main_loop(void)
tv.tv_sec = 1;
tv.tv_usec = 0;
maxfd = build_fdset(&fset);
maxfd = build_fdset(&readset, &writeset);
r = select(maxfd + 1, &fset, NULL, NULL, &tv);
r = select(maxfd + 1, &readset, &writeset, NULL, &tv);
if(r < 0) {
if(errno != EINTR && errno != EAGAIN) {
@ -360,7 +388,7 @@ int main_loop(void)
continue;
}
check_network_activity(&fset);
check_network_activity(&readset, &writeset);
if(do_purge) {
purge();

View file

@ -114,10 +114,9 @@ typedef struct outgoing_t {
struct addrinfo *aip;
} outgoing_t;
extern int maxtimeout;
extern int maxoutbufsize;
extern int seconds_till_retry;
extern int addressfamily;
extern bool blockingtcp;
extern listen_socket_t listen_socket[MAXSOCKETS];
extern int listen_sockets;

View file

@ -286,8 +286,6 @@ bool setup_myself(void)
if(get_config_bool(lookup_config(myself->connection->config_tree, "TCPOnly"), &choice) && choice)
myself->options |= OPTION_TCPONLY;
get_config_bool(lookup_config(config_tree, "BlockingTCP"), &blockingtcp);
if(get_config_bool(lookup_config(myself->connection->config_tree, "PMTUDiscovery"), &choice) && choice)
myself->options |= OPTION_PMTU_DISCOVERY;
@ -536,12 +534,20 @@ bool setup_network_connections(void)
init_events();
init_requests();
if(get_config_int(lookup_config(config_tree, "PingTimeout"), &pingtimeout)) {
if(pingtimeout < 1) {
pingtimeout = 86400;
if(get_config_int(lookup_config(config_tree, "PingInterval"), &pinginterval)) {
if(pinginterval < 1) {
pinginterval = 86400;
}
} else
pingtimeout = 60;
pinginterval = 60;
if(!get_config_int(lookup_config(config_tree, "PingTimeout"), &pingtimeout))
pingtimeout = 5;
if(pingtimeout < 1 || pingtimeout > pinginterval)
pingtimeout = pinginterval;
if(!get_config_int(lookup_config(config_tree, "MaxOutputBufferSize"), &maxoutbufsize))
maxoutbufsize = 4 * MTU;
if(!setup_myself())
return false;

View file

@ -46,7 +46,6 @@
int addressfamily = AF_UNSPEC;
int maxtimeout = 900;
int seconds_till_retry = 5;
bool blockingtcp = false;
listen_socket_t listen_socket[MAXSOCKETS];
int listen_sockets;
@ -58,12 +57,10 @@ static void configure_tcp(connection_t *c)
int option;
#ifdef O_NONBLOCK
if(!blockingtcp) {
int flags = fcntl(c->socket, F_GETFL);
int flags = fcntl(c->socket, F_GETFL);
if(fcntl(c->socket, F_SETFL, flags | O_NONBLOCK) < 0) {
logger(LOG_ERR, _("fcntl for %s: %s"), c->hostname, strerror(errno));
}
if(fcntl(c->socket, F_SETFL, flags | O_NONBLOCK) < 0) {
logger(LOG_ERR, _("fcntl for %s: %s"), c->hostname, strerror(errno));
}
#endif

View file

@ -241,7 +241,7 @@ void age_past_requests(void)
next = node->next;
p = node->data;
if(p->firstseen + pingtimeout < now)
if(p->firstseen + pinginterval < now)
avl_delete_node(past_request_tree, node), deleted++;
else
left++;

View file

@ -31,6 +31,8 @@
#include "protocol.h"
#include "utils.h"
int maxoutbufsize = 0;
/* Status and error notification routines */
bool send_status(connection_t *c, int statusno, const char *statusstring)
@ -153,7 +155,10 @@ bool send_tcppacket(connection_t *c, vpn_packet_t *packet)
{
cp();
/* Evil hack. */
/* If there already is a lot of data in the outbuf buffer, discard this packet. */
if(c->outbuflen > maxoutbufsize)
return true;
if(!send_request(c, "%d %hd", PACKET, packet->len))
return false;