diff --git a/ChangeLog b/ChangeLog index 28eaeec44f..f84c6ee1ba 100644 --- a/ChangeLog +++ b/ChangeLog @@ -16,6 +16,9 @@ Changes in version 0.1.2.5-xxxx - 200?-??-?? same directory as tor.exe (Bug #356) and default to using the torrc located in the %appdata%\Tor\ of the user who installed the service. Patch from Matt Edman. + - Include TLS overhead when counting bandwidth usage; previously, we + would count only the bytes sent over TLS, but not the bytes used to + send them. o Minor features: - Start using the state file to store bandwidth accounting data: diff --git a/doc/TODO b/doc/TODO index 8ea356ee31..879eedc98f 100644 --- a/doc/TODO +++ b/doc/TODO @@ -296,7 +296,7 @@ P - Figure out why openssl 0.9.8d "make test" fails at sha256t test. - a way to pick entry guards based wholly on extend_info equivalent; a way to export extend_info equivalent. - - Count TLS bandwidth more accurately + o Count TLS bandwidth more accurately - Better estimates in the directory of whether servers have good uptime (high expected time to failure) or good guard qualities (high diff --git a/src/common/tortls.c b/src/common/tortls.c index 51c4abe247..b183ca1df8 100644 --- a/src/common/tortls.c +++ b/src/common/tortls.c @@ -53,9 +53,11 @@ struct tor_tls_t { TOR_TLS_ST_SENTCLOSE, TOR_TLS_ST_CLOSED } state; /**< The current SSL state, depending on which operations have * completed successfully. */ - int isServer; + int isServer; /**< True iff this is a server-side connection */ size_t wantwrite_n; /**< 0 normally, >0 if we returned wantwrite last * time. */ + unsigned long last_write_count; + unsigned long last_read_count; }; static X509* tor_tls_create_certificate(crypto_pk_env_t *rsa, @@ -339,7 +341,7 @@ tor_tls_context_new(crypto_pk_env_t *identity, const char *nickname, goto error; } - result = tor_malloc(sizeof(tor_tls_context_t)); + result = tor_malloc_zero(sizeof(tor_tls_context_t)); #ifdef EVERYONE_HAS_AES /* Tell OpenSSL to only use TLS1 */ if (!(result->ctx = SSL_CTX_new(TLSv1_method()))) @@ -415,7 +417,7 @@ tor_tls_t * tor_tls_new(int sock, int isServer) { BIO *bio = NULL; - tor_tls_t *result = tor_malloc(sizeof(tor_tls_t)); + tor_tls_t *result = tor_malloc_zero(sizeof(tor_tls_t)); tor_assert(global_tls_context); /* make sure somebody made it first */ if (!(result->ssl = SSL_new(global_tls_context->ctx))) { @@ -860,19 +862,23 @@ tor_tls_get_forced_write_size(tor_tls_t *tls) return tls->wantwrite_n; } -/** Return the number of bytes read across the underlying socket. */ -unsigned long -tor_tls_get_n_bytes_read(tor_tls_t *tls) +/** Sets n_read and n_written to the number of bytes read and written, + * respectivey, on the raw socket used by tls since the last time this + * function was called on tls. */ +void +tor_tls_get_n_raw_bytes(tor_tls_t *tls, size_t *n_read, size_t *n_written) { - tor_assert(tls); - return BIO_number_read(SSL_get_rbio(tls->ssl)); -} -/** Return the number of bytes written across the underlying socket. */ -unsigned long -tor_tls_get_n_bytes_written(tor_tls_t *tls) -{ - tor_assert(tls); - return BIO_number_written(SSL_get_wbio(tls->ssl)); + unsigned long r, w; + r = BIO_number_read(SSL_get_rbio(tls->ssl)); + w = BIO_number_written(SSL_get_wbio(tls->ssl)); + /* If we wrapped around, this should still give us the right answer, unless + * we wrapped around by more than ULONG_MAX since the last time we called + * this function. + */ + *n_read = (size_t)(r - tls->last_read_count); + *n_written = (size_t)(w - tls->last_write_count); + tls->last_read_count = r; + tls->last_write_count = w; } /** Implement check_no_tls_errors: If there are any pending OpenSSL diff --git a/src/common/tortls.h b/src/common/tortls.h index dea072a338..8667ddf9b5 100644 --- a/src/common/tortls.h +++ b/src/common/tortls.h @@ -43,8 +43,8 @@ int tor_tls_shutdown(tor_tls_t *tls); int tor_tls_get_pending_bytes(tor_tls_t *tls); size_t tor_tls_get_forced_write_size(tor_tls_t *tls); -unsigned long tor_tls_get_n_bytes_read(tor_tls_t *tls); -unsigned long tor_tls_get_n_bytes_written(tor_tls_t *tls); +void tor_tls_get_n_raw_bytes(tor_tls_t *tls, + size_t *n_read, size_t *n_written); /* Log and abort if there are unhandled TLS errors in OpenSSL's error stack. */ diff --git a/src/or/connection.c b/src/or/connection.c index 858ec3de33..c2136ac858 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -1418,6 +1418,7 @@ connection_read_to_buf(connection_t *conn, int *max_to_read) { int result, at_most = *max_to_read; size_t bytes_in_buf, more_to_read; + size_t n_read = 0, n_written = 0; if (at_most == -1) { /* we need to initialize it */ /* how many bytes are we allowed to read? */ @@ -1475,7 +1476,7 @@ connection_read_to_buf(connection_t *conn, int *max_to_read) } pending = tor_tls_get_pending_bytes(or_conn->tls); if (pending) { - /* XXXX012 If we have any pending bytes, read them now. This *can* + /* If we have any pending bytes, we read them now. This *can* * take us over our read allotment, but really we shouldn't be * believing that SSL bytes are the same as TCP bytes anyway. */ int r2 = read_to_buf_tls(or_conn->tls, pending, conn->inbuf); @@ -1487,6 +1488,9 @@ connection_read_to_buf(connection_t *conn, int *max_to_read) } } + tor_tls_get_n_raw_bytes(or_conn->tls, &n_read, &n_written); + log_debug(LD_GENERAL, "After TLS read of %d: %ld read, %ld written", + result, (long)n_read, (long)n_written); } else { int reached_eof = 0; CONN_LOG_PROTECT(conn, @@ -1498,15 +1502,24 @@ connection_read_to_buf(connection_t *conn, int *max_to_read) if (result < 0) return -1; + n_read = (size_t) result; } - if (result > 0) { /* change *max_to_read */ - *max_to_read = at_most - result; + if (n_read > 0) { /* change *max_to_read */ + *max_to_read = at_most - n_read; } - if (result > 0 && !is_internal_IP(conn->addr, 0)) { /* remember it */ - rep_hist_note_bytes_read(result, time(NULL)); - connection_read_bucket_decrement(conn, result); + if (!is_internal_IP(conn->addr, 0)) { + /* For non-local IPs, remember if we flushed any bytes over the wire. */ + time_t now = time(NULL); + if (n_read > 0) { + rep_hist_note_bytes_read(n_read, now); + connection_read_bucket_decrement(conn, n_read); + } + if (n_written > 0) { + rep_hist_note_bytes_written(n_written, now); + global_write_bucket -= n_written; + } } if (more_to_read && result == at_most) { @@ -1520,6 +1533,8 @@ connection_read_to_buf(connection_t *conn, int *max_to_read) * have reached 0 on a different conn, and this guy needs to * know to stop reading. */ connection_consider_empty_read_buckets(conn); + if (n_written > 0) + connection_consider_empty_write_buckets(conn); return 0; } @@ -1571,6 +1586,7 @@ connection_handle_write(connection_t *conn) int result; int max_to_write; time_t now = time(NULL); + size_t n_read = 0, n_written = 0; tor_assert(!connection_is_listener(conn)); @@ -1664,6 +1680,10 @@ connection_handle_write(connection_t *conn) * is empty, so we can stop writing. */ } + + tor_tls_get_n_raw_bytes(or_conn->tls, &n_read, &n_written); + log_debug(LD_GENERAL, "After TLS write of %d: %ld read, %ld written", + result, (long)n_read, (long)n_written); } else { CONN_LOG_PROTECT(conn, result = flush_buf(conn->s, conn->outbuf, @@ -1677,13 +1697,25 @@ connection_handle_write(connection_t *conn) connection_mark_for_close(conn); return -1; } + n_written = (size_t) result; + } + + if (!is_internal_IP(conn->addr, 0)) { + /* For non-local IPs, remember if we flushed any bytes over the wire. */ + time_t now = time(NULL); + if (n_written > 0) { + rep_hist_note_bytes_written(n_written, now); + global_write_bucket -= n_written; + } + if (n_read > 0) { + rep_hist_note_bytes_read(n_read, now); + global_read_bucket -= n_read; + } } if (result > 0) { - if (!is_internal_IP(conn->addr, 0)) { /* remember it */ - rep_hist_note_bytes_written(result, time(NULL)); - global_write_bucket -= result; - } + /* If we wrote any bytes from our buffer, then call the appropriate + * functions. */ if (connection_flushed_some(conn) < 0) connection_mark_for_close(conn); } @@ -1700,6 +1732,9 @@ connection_handle_write(connection_t *conn) * have reached 0 on a different conn, and this guy needs to * know to stop writing. */ connection_consider_empty_write_buckets(conn); + if (n_read > 0) + connection_consider_empty_read_buckets(conn); + return 0; }