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

@ -120,13 +120,6 @@ will by default listen on all of them for incoming connections.
It is possible to bind only to a single interface with this variable.
.Pp
This option may not work on all platforms.
.It Va BlockingTCP Li = yes | no Po no Pc Bq experimental
This options selects whether TCP connections, when established, should use blocking writes.
When turned off, tinc will never block when a TCP connection becomes congested, but will have to terminate that connection instead.
If turned on, tinc will not terminate connections but will block, thereby unable to process data to/from other connections.
Turn this option on if you also use
.Va TCPOnly
and tinc terminates connections frequently.
.It Va ConnectTo Li = Ar name
Specifies which other tinc daemon to connect to on startup.
Multiple
@ -206,11 +199,13 @@ while no routing table is managed.
.It Va Name Li = Ar name Bq required
This is the name which identifies this tinc daemon.
It must be unique for the virtual private network this daemon will connect to.
.It Va PingTimeout Li = Ar seconds Pq 60
.It Va PingInterval Li = Ar seconds Pq 60
The number of seconds of inactivity that
.Nm tinc
will wait before sending a probe to the other end.
If that other end doesn't answer within that same amount of time,
.It Va PingTimeout Li = Ar seconds Pq 5
The number of seconds to wait for a response to pings or to allow meta
connections to block. If the other end doesn't respond within this time,
the connection is terminated,
and the others will be notified of this.
.It Va PriorityInheritance Li = yes | no Po no Pc Bq experimental

View file

@ -841,15 +841,6 @@ variable.
This option may not work on all platforms.
@cindex BlockingTCP
@item BlockingTCP = <yes|no> (no) [experimental]
This options selects whether TCP connections, when established, should use blocking writes.
When turned off, tinc will never block when a TCP connection becomes congested,
but will have to terminate that connection instead.
If turned on, tinc will not terminate connections but will block,
thereby unable to process data to/from other connections.
Turn this option on if you also use TCPOnly and tinc terminates connections frequently.
@cindex ConnectTo
@item ConnectTo = <@var{name}>
Specifies which other tinc daemon to connect to on startup.
@ -933,12 +924,16 @@ This only has effect when Mode is set to "switch".
@item Name = <@var{name}> [required]
This is a symbolic name for this connection. It can be anything
@cindex PingTimeout
@item PingTimeout = <@var{seconds}> (60)
@cindex PingInterval
@item PingInterval = <@var{seconds}> (60)
The number of seconds of inactivity that tinc will wait before sending a
probe to the other end. If that other end doesn't answer within that
same amount of seconds, the connection is terminated, and the others
will be notified of this.
probe to the other end.
@cindex PingTimeout
@item PingTimeout = <@var{seconds}> (5)
The number of seconds to wait for a response to pings or to allow meta
connections to block. If the other end doesn't respond within this time,
the connection is terminated, and the others will be notified of this.
@cindex PriorityInheritance
@item PriorityInheritance = <yes|no> (no) [experimental]

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;