diff options
-rw-r--r-- | src/or/config.c | 14 | ||||
-rw-r--r-- | src/or/connection.c | 18 | ||||
-rw-r--r-- | src/or/main.c | 76 | ||||
-rw-r--r-- | src/or/or.h | 5 |
4 files changed, 94 insertions, 19 deletions
diff --git a/src/or/config.c b/src/or/config.c index 8bcb5bea1..af0fab8a6 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -99,6 +99,8 @@ RETURN VALUE: 0 on success, non-zero on error 0, "how many seconds between directory rebuilds", "<rebuildperiod>" }, { "DirFetchPeriod", 'F', POPT_ARG_INT, &options->DirFetchPeriod, 0, "how many seconds between directory fetches", "<fetchperiod>" }, + { "KeepalivePeriod", 'K', POPT_ARG_INT, &options->KeepalivePeriod, + 0, "how many seconds between keepalives", "<keepaliveperiod>" }, // { "ReconnectPeriod", 'e', POPT_ARG_INT, &options->ReconnectPeriod, // 0, "how many seconds between retrying all OR connections", "<reconnectperiod>" }, { "Role", 'R', POPT_ARG_INT, &options->Role, @@ -122,6 +124,7 @@ RETURN VALUE: 0 on success, non-zero on error options->LinkPadding = 0; options->DirRebuildPeriod = 600; options->DirFetchPeriod = 6000; + options->KeepalivePeriod = 300; // options->ReconnectPeriod = 6001; options->Role = ROLE_OR_LISTEN | ROLE_OR_CONNECT_ALL | ROLE_OP_LISTEN | ROLE_AP_LISTEN; @@ -170,9 +173,10 @@ RETURN VALUE: 0 on success, non-zero on error options->MaxConn, options->TrafficShaping, options->LinkPadding); - printf("DirRebuildPeriod=%d, DirFetchPeriod=%d\n", + printf("DirRebuildPeriod=%d, DirFetchPeriod=%d KeepalivePeriod=%d\n", options->DirRebuildPeriod, - options->DirFetchPeriod); + options->DirFetchPeriod, + options->KeepalivePeriod); } /* Validate options */ @@ -287,6 +291,12 @@ RETURN VALUE: 0 on success, non-zero on error code = -1; } + if ( options->KeepalivePeriod < 1) + { + log(LOG_ERR,"KeepalivePeriod option must be positive."); + code = -1; + } + return code; } diff --git a/src/or/connection.c b/src/or/connection.c index d2445effe..f8333c0ad 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -86,6 +86,10 @@ void tv_addms(struct timeval *a, long ms) { connection_t *connection_new(int type) { connection_t *conn; + struct timeval now; + + if(gettimeofday(&now,NULL) < 0) + return NULL; conn = (connection_t *)malloc(sizeof(connection_t)); if(!conn) @@ -99,6 +103,7 @@ connection_t *connection_new(int type) { conn->receiver_bucket = 10240; /* should be enough to do the handshake */ conn->bandwidth = conn->receiver_bucket / 10; /* give it a default */ + conn->timestamp_created = now.tv_sec; if (connection_speaks_cells(conn)) { conn->f_crypto = crypto_new_cipher_env(CRYPTO_CIPHER_DES); @@ -320,6 +325,7 @@ connection_t *connection_connect_to_router_as_op(routerinfo_t *router, uint16_t int connection_read_to_buf(connection_t *conn) { int read_result; + struct timeval now; if(connection_speaks_cells(conn)) { assert(conn->receiver_bucket >= 0); @@ -327,6 +333,11 @@ int connection_read_to_buf(connection_t *conn) { if(!connection_speaks_cells(conn)) { assert(conn->receiver_bucket < 0); } + + if(gettimeofday(&now,NULL) < 0) + return -1; + conn->timestamp_lastread = now.tv_sec; + read_result = read_to_buf(conn->s, conn->receiver_bucket, &conn->inbuf, &conn->inbuflen, &conn->inbuf_datalen, &conn->inbuf_reached_eof); // log(LOG_DEBUG,"connection_read_to_buf(): read_to_buf returned %d.",read_result); @@ -369,9 +380,16 @@ int connection_flush_buf(connection_t *conn) { } int connection_write_to_buf(char *string, int len, connection_t *conn) { + struct timeval now; + + if(gettimeofday(&now,NULL) < 0) + return -1; + if(!len) return 0; + conn->timestamp_lastwritten = now.tv_sec; + if( (!connection_speaks_cells(conn)) || (!connection_state_is_open(conn)) || (options.LinkPadding == 0) ) { diff --git a/src/or/main.c b/src/or/main.c index 0e4af5280..ff89dd757 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -310,20 +310,11 @@ int prepare_for_poll(int *timeout) { connection_t *conn = NULL; connection_t *tmpconn; struct timeval now, soonest; - static int current_second = 0; /* from previous calls to gettimeofday */ - static int time_to_rebuild_directory = 0; - static int time_to_fetch_directory = 0; + static long current_second = 0; /* from previous calls to gettimeofday */ + static long time_to_rebuild_directory = 0; + static long time_to_fetch_directory = 0; int ms_until_conn; - - *timeout = -1; /* set it to never timeout, possibly overridden below */ - - /* first check if we need to refill buckets */ - for(i=0;i<nfds;i++) { - if(connection_receiver_bucket_should_increase(connection_array[i])) { - need_to_refill_buckets = 1; - break; - } - } + cell_t cell; if(gettimeofday(&now,NULL) < 0) return -1; @@ -356,6 +347,50 @@ int prepare_for_poll(int *timeout) { *timeout = 1000*(time_to_fetch_directory - now.tv_sec) + (1000 - (now.tv_usec / 1000)); } + /* check connections to see whether we should send a keepalive, expire, or wait */ + for(i=0;i<nfds;i++) { + tmpconn = connection_array[i]; + if(!connection_speaks_cells(tmpconn)) + continue; /* this conn type doesn't send cells */ + if(!connection_state_is_open(tmpconn)) { + continue; /* only conns in state 'open' need a keepalive */ + /* XXX should time-out unfinished connections someday too */ + } + if(now.tv_sec >= tmpconn->timestamp_lastwritten + options.KeepalivePeriod) { + if(!(options.Role & ROLE_OR_CONNECT_ALL) && !circuit_get_by_conn(tmpconn)) { + /* we're an onion proxy, with no circuits. kill it. */ + log(LOG_DEBUG,"prepare_for_poll(): Expiring connection to %d (%s:%d).", + i,tmpconn->address, tmpconn->port); + tmpconn->marked_for_close = 1; + } else { + /* either a full router, or we've got a circuit. send a padding cell. */ +// log(LOG_DEBUG,"prepare_for_poll(): Sending keepalive to (%s:%d)", +// tmpconn->address, tmpconn->port); + memset(&cell,0,sizeof(cell_t)); + cell.command = CELL_PADDING; + connection_write_cell_to_buf(&cell, tmpconn); + } + } + if(!tmpconn->marked_for_close && + *timeout > 1000*(tmpconn->timestamp_lastwritten + options.KeepalivePeriod - now.tv_sec)) { + *timeout = 1000*(tmpconn->timestamp_lastwritten + options.KeepalivePeriod - now.tv_sec); + } + } + assert(*timeout >= 0); + /* blow away any connections that need to die. can't do this later + * because we might open up a circuit and not realize it. + */ + for(i=0;i<nfds;i++) + check_conn_marked(i); + + /* check if we need to refill buckets */ + for(i=0;i<nfds;i++) { + if(connection_receiver_bucket_should_increase(connection_array[i])) { + need_to_refill_buckets = 1; + break; + } + } + if(need_to_refill_buckets) { if(now.tv_sec > current_second) { /* the second has already rolled over! */ // log(LOG_DEBUG,"prepare_for_poll(): The second has rolled over, immediately refilling."); @@ -363,7 +398,7 @@ int prepare_for_poll(int *timeout) { connection_increment_receiver_bucket(connection_array[i]); current_second = now.tv_sec; /* remember which second it is, for next time */ } - /* this timeout is definitely sooner than either of the above two */ + /* this timeout is definitely sooner than any of the above ones */ *timeout = 1000 - (now.tv_usec / 1000); /* how many milliseconds til the next second? */ } @@ -394,7 +429,7 @@ int prepare_for_poll(int *timeout) { ms_until_conn = (soonest.tv_sec - now.tv_sec)*1000 + (soonest.tv_usec - now.tv_usec)/1000; // log(LOG_DEBUG,"prepare_for_poll(): conn %d times out in %d ms.",conn->s, ms_until_conn); - if(*timeout == -1 || ms_until_conn < *timeout) { /* use the new one */ + if(ms_until_conn < *timeout) { /* use the new one */ // log(LOG_DEBUG,"prepare_for_poll(): conn %d soonest, in %d ms.",conn->s,ms_until_conn); *timeout = ms_until_conn; } @@ -517,18 +552,25 @@ static void catch(int the_signal) { void dumpstats (void) { /* dump stats to stdout */ int i; connection_t *conn; + struct timeval now; extern char *conn_type_to_string[]; extern char *conn_state_to_string[][15]; printf("Dumping stats:\n"); + if(gettimeofday(&now,NULL) < 0) + return ; for(i=0;i<nfds;i++) { conn = connection_array[i]; - printf("Conn %d (socket %d) type %d (%s), state %d (%s)\n", + printf("Conn %d (socket %d) type %d (%s), state %d (%s), created %ld secs ago\n", i, conn->s, conn->type, conn_type_to_string[conn->type], - conn->state, conn_state_to_string[conn->type][conn->state]); + conn->state, conn_state_to_string[conn->type][conn->state], now.tv_sec - conn->timestamp_created); if(!connection_is_listener(conn)) { printf("Conn %d is to '%s:%d'.\n",i,conn->address, conn->port); + printf("Conn %d: %d bytes waiting on inbuf (last read %ld secs ago)\n",i,conn->inbuf_datalen, + now.tv_sec - conn->timestamp_lastread); + printf("Conn %d: %d bytes waiting on outbuf (last written %ld secs ago)\n",i,conn->outbuf_datalen, + now.tv_sec - conn->timestamp_lastwritten); } circuit_dump_by_conn(conn); /* dump info about all the circuits using this conn */ printf("\n"); diff --git a/src/or/or.h b/src/or/or.h index 72eb79176..7720ad3ed 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -188,11 +188,15 @@ typedef struct int inbuflen; int inbuf_datalen; int inbuf_reached_eof; + long timestamp_lastread; char *outbuf; int outbuflen; /* how many bytes are allocated for the outbuf? */ int outbuf_flushlen; /* how much data should we try to flush from the outbuf? */ int outbuf_datalen; /* how much data is there total on the outbuf? */ + long timestamp_lastwritten; + + long timestamp_created; // uint16_t aci; /* anonymous connection identifier */ @@ -357,6 +361,7 @@ typedef struct int LinkPadding; int DirRebuildPeriod; int DirFetchPeriod; + int KeepalivePeriod; int Role; int loglevel; } or_options_t; |