mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-10 13:13:44 +01:00
Merge branch 'tor-gitlab/mr/39'
This commit is contained in:
commit
063cdb3142
4
changes/ticket33264
Normal file
4
changes/ticket33264
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
o Minor features (statistics, ipv6):
|
||||||
|
- Relays now publish IPv6-specific counts of single-direction
|
||||||
|
versus bidirectional relay connections.
|
||||||
|
Closes ticket 33264.
|
@ -105,6 +105,7 @@
|
|||||||
#include "feature/relay/routermode.h"
|
#include "feature/relay/routermode.h"
|
||||||
#include "feature/rend/rendclient.h"
|
#include "feature/rend/rendclient.h"
|
||||||
#include "feature/rend/rendcommon.h"
|
#include "feature/rend/rendcommon.h"
|
||||||
|
#include "feature/stats/connstats.h"
|
||||||
#include "feature/stats/rephist.h"
|
#include "feature/stats/rephist.h"
|
||||||
#include "feature/stats/bwhist.h"
|
#include "feature/stats/bwhist.h"
|
||||||
#include "lib/crypt_ops/crypto_util.h"
|
#include "lib/crypt_ops/crypto_util.h"
|
||||||
@ -3362,11 +3363,11 @@ record_num_bytes_transferred_impl(connection_t *conn,
|
|||||||
if (!connection_is_rate_limited(conn))
|
if (!connection_is_rate_limited(conn))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (conn->type == CONN_TYPE_OR)
|
|
||||||
rep_hist_note_or_conn_bytes(conn->global_identifier, num_read,
|
|
||||||
num_written, now);
|
|
||||||
|
|
||||||
const bool is_ipv6 = (conn->socket_family == AF_INET6);
|
const bool is_ipv6 = (conn->socket_family == AF_INET6);
|
||||||
|
if (conn->type == CONN_TYPE_OR)
|
||||||
|
conn_stats_note_or_conn_bytes(conn->global_identifier, num_read,
|
||||||
|
num_written, now, is_ipv6);
|
||||||
|
|
||||||
if (num_read > 0) {
|
if (num_read > 0) {
|
||||||
bwhist_note_bytes_read(num_read, now, is_ipv6);
|
bwhist_note_bytes_read(num_read, now, is_ipv6);
|
||||||
}
|
}
|
||||||
|
@ -95,6 +95,7 @@
|
|||||||
#include "feature/rend/rendservice.h"
|
#include "feature/rend/rendservice.h"
|
||||||
#include "feature/stats/geoip_stats.h"
|
#include "feature/stats/geoip_stats.h"
|
||||||
#include "feature/stats/predict_ports.h"
|
#include "feature/stats/predict_ports.h"
|
||||||
|
#include "feature/stats/connstats.h"
|
||||||
#include "feature/stats/rephist.h"
|
#include "feature/stats/rephist.h"
|
||||||
#include "lib/buf/buffers.h"
|
#include "lib/buf/buffers.h"
|
||||||
#include "lib/crypt_ops/crypto_rand.h"
|
#include "lib/crypt_ops/crypto_rand.h"
|
||||||
@ -1950,7 +1951,7 @@ write_stats_file_callback(time_t now, const or_options_t *options)
|
|||||||
next_time_to_write_stats_files = next_write;
|
next_time_to_write_stats_files = next_write;
|
||||||
}
|
}
|
||||||
if (options->ConnDirectionStatistics) {
|
if (options->ConnDirectionStatistics) {
|
||||||
time_t next_write = rep_hist_conn_stats_write(now);
|
time_t next_write = conn_stats_save(now);
|
||||||
if (next_write && next_write < next_time_to_write_stats_files)
|
if (next_write && next_write < next_time_to_write_stats_files)
|
||||||
next_time_to_write_stats_files = next_write;
|
next_time_to_write_stats_files = next_write;
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,7 @@
|
|||||||
#include "feature/nodelist/nickname.h"
|
#include "feature/nodelist/nickname.h"
|
||||||
#include "feature/stats/geoip_stats.h"
|
#include "feature/stats/geoip_stats.h"
|
||||||
#include "feature/stats/predict_ports.h"
|
#include "feature/stats/predict_ports.h"
|
||||||
|
#include "feature/stats/connstats.h"
|
||||||
#include "feature/stats/rephist.h"
|
#include "feature/stats/rephist.h"
|
||||||
|
|
||||||
#include "feature/dirauth/authmode.h"
|
#include "feature/dirauth/authmode.h"
|
||||||
@ -1307,7 +1308,7 @@ options_act_relay_stats(const or_options_t *old_options,
|
|||||||
}
|
}
|
||||||
if ((!old_options || !old_options->ConnDirectionStatistics) &&
|
if ((!old_options || !old_options->ConnDirectionStatistics) &&
|
||||||
options->ConnDirectionStatistics) {
|
options->ConnDirectionStatistics) {
|
||||||
rep_hist_conn_stats_init(now);
|
conn_stats_init(now);
|
||||||
}
|
}
|
||||||
if ((!old_options || !old_options->HiddenServiceStatistics) &&
|
if ((!old_options || !old_options->HiddenServiceStatistics) &&
|
||||||
options->HiddenServiceStatistics) {
|
options->HiddenServiceStatistics) {
|
||||||
@ -1337,7 +1338,7 @@ options_act_relay_stats(const or_options_t *old_options,
|
|||||||
rep_hist_exit_stats_term();
|
rep_hist_exit_stats_term();
|
||||||
if (old_options && old_options->ConnDirectionStatistics &&
|
if (old_options && old_options->ConnDirectionStatistics &&
|
||||||
!options->ConnDirectionStatistics)
|
!options->ConnDirectionStatistics)
|
||||||
rep_hist_conn_stats_term();
|
conn_stats_terminate();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
283
src/feature/stats/connstats.c
Normal file
283
src/feature/stats/connstats.c
Normal file
@ -0,0 +1,283 @@
|
|||||||
|
/* Copyright (c) 2001 Matej Pfajfar.
|
||||||
|
* Copyright (c) 2001-2004, Roger Dingledine.
|
||||||
|
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
|
||||||
|
* Copyright (c) 2007-2020, The Tor Project, Inc. */
|
||||||
|
/* See LICENSE for licensing information */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file connstats.c
|
||||||
|
* @brief Count bidirectional vs one-way connections.
|
||||||
|
*
|
||||||
|
* Connection statistics, use to track one-way and bidirectional connections.
|
||||||
|
*
|
||||||
|
* Note that this code counts concurrent connections in each
|
||||||
|
* BIDI_INTERVAL-second interval, not total connections. It can tell you what
|
||||||
|
* fraction of connections are bidirectional at each time, not necessarily
|
||||||
|
* what number are bidirectional.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#include "orconfig.h"
|
||||||
|
#include "core/or/or.h"
|
||||||
|
#include "feature/stats/connstats.h"
|
||||||
|
#include "app/config/config.h"
|
||||||
|
|
||||||
|
/** Start of the current connection stats interval or 0 if we're not
|
||||||
|
* collecting connection statistics. */
|
||||||
|
static time_t start_of_conn_stats_interval;
|
||||||
|
|
||||||
|
/** Initialize connection stats. */
|
||||||
|
void
|
||||||
|
conn_stats_init(time_t now)
|
||||||
|
{
|
||||||
|
start_of_conn_stats_interval = now;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Count connections on which we read and wrote less than this many bytes
|
||||||
|
* as "below threshold." */
|
||||||
|
#define BIDI_THRESHOLD 20480
|
||||||
|
|
||||||
|
/** Count connections that we read or wrote at least this factor as many
|
||||||
|
* bytes from/to than we wrote or read to/from as mostly reading or
|
||||||
|
* writing. */
|
||||||
|
#define BIDI_FACTOR 10
|
||||||
|
|
||||||
|
/** Interval length in seconds for considering read and written bytes for
|
||||||
|
* connection stats. */
|
||||||
|
#define BIDI_INTERVAL 10
|
||||||
|
|
||||||
|
/** Start of next BIDI_INTERVAL second interval. */
|
||||||
|
static time_t bidi_next_interval = 0;
|
||||||
|
|
||||||
|
/** A single grouped set of connection type counts. */
|
||||||
|
typedef struct conn_counts_t {
|
||||||
|
/** Number of connections that we read and wrote less than BIDI_THRESHOLD
|
||||||
|
* bytes from/to in BIDI_INTERVAL seconds. */
|
||||||
|
uint32_t below_threshold;
|
||||||
|
|
||||||
|
/** Number of connections that we read at least BIDI_FACTOR times more
|
||||||
|
* bytes from than we wrote to in BIDI_INTERVAL seconds. */
|
||||||
|
uint32_t mostly_read;
|
||||||
|
|
||||||
|
/** Number of connections that we wrote at least BIDI_FACTOR times more
|
||||||
|
* bytes to than we read from in BIDI_INTERVAL seconds. */
|
||||||
|
uint32_t mostly_written;
|
||||||
|
|
||||||
|
/** Number of connections that we read and wrote at least BIDI_THRESHOLD
|
||||||
|
* bytes from/to, but not BIDI_FACTOR times more in either direction in
|
||||||
|
* BIDI_INTERVAL seconds. */
|
||||||
|
uint32_t both_read_and_written;
|
||||||
|
} conn_counts_t ;
|
||||||
|
|
||||||
|
/** A collection of connection counts, over all OR connections. */
|
||||||
|
static conn_counts_t counts;
|
||||||
|
/** A collection of connection counts, over IPv6 OR connections only. */
|
||||||
|
static conn_counts_t counts_ipv6;
|
||||||
|
|
||||||
|
/** Entry in a map from connection ID to the number of read and written
|
||||||
|
* bytes on this connection in a BIDI_INTERVAL second interval. */
|
||||||
|
typedef struct bidi_map_entry_t {
|
||||||
|
HT_ENTRY(bidi_map_entry_t) node;
|
||||||
|
uint64_t conn_id; /**< Connection ID */
|
||||||
|
size_t read; /**< Number of read bytes */
|
||||||
|
size_t written; /**< Number of written bytes */
|
||||||
|
bool is_ipv6; /**< True if this is an IPv6 connection */
|
||||||
|
} bidi_map_entry_t;
|
||||||
|
|
||||||
|
/** Map of OR connections together with the number of read and written
|
||||||
|
* bytes in the current BIDI_INTERVAL second interval. */
|
||||||
|
static HT_HEAD(bidimap, bidi_map_entry_t) bidi_map =
|
||||||
|
HT_INITIALIZER();
|
||||||
|
|
||||||
|
/** Hashtable helper: return true if @a a and @a b have the same key. */
|
||||||
|
static int
|
||||||
|
bidi_map_ent_eq(const bidi_map_entry_t *a, const bidi_map_entry_t *b)
|
||||||
|
{
|
||||||
|
return a->conn_id == b->conn_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Hashtable helper: compute a digest for the key of @a entry. */
|
||||||
|
static unsigned
|
||||||
|
bidi_map_ent_hash(const bidi_map_entry_t *entry)
|
||||||
|
{
|
||||||
|
return (unsigned) entry->conn_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
HT_PROTOTYPE(bidimap, bidi_map_entry_t, node, bidi_map_ent_hash,
|
||||||
|
bidi_map_ent_eq);
|
||||||
|
HT_GENERATE2(bidimap, bidi_map_entry_t, node, bidi_map_ent_hash,
|
||||||
|
bidi_map_ent_eq, 0.6, tor_reallocarray_, tor_free_);
|
||||||
|
|
||||||
|
/** Release all storage held in connstats.c */
|
||||||
|
void
|
||||||
|
conn_stats_free_all(void)
|
||||||
|
{
|
||||||
|
bidi_map_entry_t **ptr, **next, *ent;
|
||||||
|
for (ptr = HT_START(bidimap, &bidi_map); ptr; ptr = next) {
|
||||||
|
ent = *ptr;
|
||||||
|
next = HT_NEXT_RMV(bidimap, &bidi_map, ptr);
|
||||||
|
tor_free(ent);
|
||||||
|
}
|
||||||
|
HT_CLEAR(bidimap, &bidi_map);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Reset counters for conn statistics. */
|
||||||
|
void
|
||||||
|
conn_stats_reset(time_t now)
|
||||||
|
{
|
||||||
|
start_of_conn_stats_interval = now;
|
||||||
|
memset(&counts, 0, sizeof(counts));
|
||||||
|
memset(&counts_ipv6, 0, sizeof(counts_ipv6));
|
||||||
|
conn_stats_free_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Stop collecting connection stats in a way that we can re-start doing
|
||||||
|
* so in conn_stats_init(). */
|
||||||
|
void
|
||||||
|
conn_stats_terminate(void)
|
||||||
|
{
|
||||||
|
conn_stats_reset(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Record a single entry @a ent in the counts structure @cnt.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
add_entry_to_count(conn_counts_t *cnt, const bidi_map_entry_t *ent)
|
||||||
|
{
|
||||||
|
if (ent->read + ent->written < BIDI_THRESHOLD)
|
||||||
|
cnt->below_threshold++;
|
||||||
|
else if (ent->read >= ent->written * BIDI_FACTOR)
|
||||||
|
cnt->mostly_read++;
|
||||||
|
else if (ent->written >= ent->read * BIDI_FACTOR)
|
||||||
|
cnt->mostly_written++;
|
||||||
|
else
|
||||||
|
cnt->both_read_and_written++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Count all the connection information we've received during the current
|
||||||
|
* period in 'bidimap', and store that information in the appropriate count
|
||||||
|
* structures.
|
||||||
|
**/
|
||||||
|
static void
|
||||||
|
collect_period_statistics(void)
|
||||||
|
{
|
||||||
|
bidi_map_entry_t **ptr, **next, *ent;
|
||||||
|
for (ptr = HT_START(bidimap, &bidi_map); ptr; ptr = next) {
|
||||||
|
ent = *ptr;
|
||||||
|
add_entry_to_count(&counts, ent);
|
||||||
|
if (ent->is_ipv6)
|
||||||
|
add_entry_to_count(&counts_ipv6, ent);
|
||||||
|
next = HT_NEXT_RMV(bidimap, &bidi_map, ptr);
|
||||||
|
tor_free(ent);
|
||||||
|
}
|
||||||
|
log_info(LD_GENERAL, "%d below threshold, %d mostly read, "
|
||||||
|
"%d mostly written, %d both read and written.",
|
||||||
|
counts.below_threshold, counts.mostly_read, counts.mostly_written,
|
||||||
|
counts.both_read_and_written);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** We read <b>num_read</b> bytes and wrote <b>num_written</b> from/to OR
|
||||||
|
* connection <b>conn_id</b> in second <b>when</b>. If this is the first
|
||||||
|
* observation in a new interval, sum up the last observations. Add bytes
|
||||||
|
* for this connection. */
|
||||||
|
void
|
||||||
|
conn_stats_note_or_conn_bytes(uint64_t conn_id, size_t num_read,
|
||||||
|
size_t num_written, time_t when,
|
||||||
|
bool is_ipv6)
|
||||||
|
{
|
||||||
|
if (!start_of_conn_stats_interval)
|
||||||
|
return;
|
||||||
|
/* Initialize */
|
||||||
|
if (bidi_next_interval == 0)
|
||||||
|
bidi_next_interval = when + BIDI_INTERVAL;
|
||||||
|
/* Sum up last period's statistics */
|
||||||
|
if (when >= bidi_next_interval) {
|
||||||
|
collect_period_statistics();
|
||||||
|
while (when >= bidi_next_interval)
|
||||||
|
bidi_next_interval += BIDI_INTERVAL;
|
||||||
|
}
|
||||||
|
/* Add this connection's bytes. */
|
||||||
|
if (num_read > 0 || num_written > 0) {
|
||||||
|
bidi_map_entry_t *entry, lookup;
|
||||||
|
lookup.conn_id = conn_id;
|
||||||
|
entry = HT_FIND(bidimap, &bidi_map, &lookup);
|
||||||
|
if (entry) {
|
||||||
|
entry->written += num_written;
|
||||||
|
entry->read += num_read;
|
||||||
|
entry->is_ipv6 |= is_ipv6;
|
||||||
|
} else {
|
||||||
|
entry = tor_malloc_zero(sizeof(bidi_map_entry_t));
|
||||||
|
entry->conn_id = conn_id;
|
||||||
|
entry->written = num_written;
|
||||||
|
entry->read = num_read;
|
||||||
|
entry->is_ipv6 = is_ipv6;
|
||||||
|
HT_INSERT(bidimap, &bidi_map, entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Return a newly allocated string containing the connection statistics
|
||||||
|
* until <b>now</b>, or NULL if we're not collecting conn stats. Caller must
|
||||||
|
* ensure start_of_conn_stats_interval is in the past. */
|
||||||
|
char *
|
||||||
|
conn_stats_format(time_t now)
|
||||||
|
{
|
||||||
|
char *result, written_at[ISO_TIME_LEN+1];
|
||||||
|
|
||||||
|
if (!start_of_conn_stats_interval)
|
||||||
|
return NULL; /* Not initialized. */
|
||||||
|
|
||||||
|
tor_assert(now >= start_of_conn_stats_interval);
|
||||||
|
|
||||||
|
format_iso_time(written_at, now);
|
||||||
|
tor_asprintf(&result,
|
||||||
|
"conn-bi-direct %s (%d s) "
|
||||||
|
"%"PRIu32",%"PRIu32",%"PRIu32",%"PRIu32"\n"
|
||||||
|
"ipv6-conn-bi-direct %s (%d s) "
|
||||||
|
"%"PRIu32",%"PRIu32",%"PRIu32",%"PRIu32"\n",
|
||||||
|
written_at,
|
||||||
|
(unsigned) (now - start_of_conn_stats_interval),
|
||||||
|
counts.below_threshold,
|
||||||
|
counts.mostly_read,
|
||||||
|
counts.mostly_written,
|
||||||
|
counts.both_read_and_written,
|
||||||
|
written_at,
|
||||||
|
(unsigned) (now - start_of_conn_stats_interval),
|
||||||
|
counts_ipv6.below_threshold,
|
||||||
|
counts_ipv6.mostly_read,
|
||||||
|
counts_ipv6.mostly_written,
|
||||||
|
counts_ipv6.both_read_and_written);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** If 24 hours have passed since the beginning of the current conn stats
|
||||||
|
* period, write conn stats to $DATADIR/stats/conn-stats (possibly
|
||||||
|
* overwriting an existing file) and reset counters. Return when we would
|
||||||
|
* next want to write conn stats or 0 if we never want to write. */
|
||||||
|
time_t
|
||||||
|
conn_stats_save(time_t now)
|
||||||
|
{
|
||||||
|
char *str = NULL;
|
||||||
|
|
||||||
|
if (!start_of_conn_stats_interval)
|
||||||
|
return 0; /* Not initialized. */
|
||||||
|
if (start_of_conn_stats_interval + WRITE_STATS_INTERVAL > now)
|
||||||
|
goto done; /* Not ready to write */
|
||||||
|
|
||||||
|
/* Generate history string. */
|
||||||
|
str = conn_stats_format(now);
|
||||||
|
|
||||||
|
/* Reset counters. */
|
||||||
|
conn_stats_reset(now);
|
||||||
|
|
||||||
|
/* Try to write to disk. */
|
||||||
|
if (!check_or_create_data_subdir("stats")) {
|
||||||
|
write_to_data_subdir("stats", "conn-stats", str, "connection statistics");
|
||||||
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
|
tor_free(str);
|
||||||
|
return start_of_conn_stats_interval + WRITE_STATS_INTERVAL;
|
||||||
|
}
|
25
src/feature/stats/connstats.h
Normal file
25
src/feature/stats/connstats.h
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/* Copyright (c) 2001 Matej Pfajfar.
|
||||||
|
* Copyright (c) 2001-2004, Roger Dingledine.
|
||||||
|
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
|
||||||
|
* Copyright (c) 2007-2020, The Tor Project, Inc. */
|
||||||
|
/* See LICENSE for licensing information */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file connstats.h
|
||||||
|
* @brief Header for feature/stats/connstats.c
|
||||||
|
**/
|
||||||
|
|
||||||
|
#ifndef TOR_FEATURE_STATS_CONNSTATS_H
|
||||||
|
#define TOR_FEATURE_STATS_CONNSTATS_H
|
||||||
|
|
||||||
|
void conn_stats_init(time_t now);
|
||||||
|
void conn_stats_note_or_conn_bytes(uint64_t conn_id, size_t num_read,
|
||||||
|
size_t num_written, time_t when,
|
||||||
|
bool is_ipv6);
|
||||||
|
void conn_stats_reset(time_t now);
|
||||||
|
char *conn_stats_format(time_t now);
|
||||||
|
time_t conn_stats_save(time_t now);
|
||||||
|
void conn_stats_terminate(void);
|
||||||
|
void conn_stats_free_all(void);
|
||||||
|
|
||||||
|
#endif /* !defined(TOR_FEATURE_STATS_CONNSTATS_H) */
|
@ -2,6 +2,7 @@
|
|||||||
# ADD_C_FILE: INSERT SOURCES HERE.
|
# ADD_C_FILE: INSERT SOURCES HERE.
|
||||||
LIBTOR_APP_A_SOURCES += \
|
LIBTOR_APP_A_SOURCES += \
|
||||||
src/feature/stats/bwhist.c \
|
src/feature/stats/bwhist.c \
|
||||||
|
src/feature/stats/connstats.c \
|
||||||
src/feature/stats/geoip_stats.c \
|
src/feature/stats/geoip_stats.c \
|
||||||
src/feature/stats/rephist.c \
|
src/feature/stats/rephist.c \
|
||||||
src/feature/stats/predict_ports.c
|
src/feature/stats/predict_ports.c
|
||||||
@ -9,6 +10,7 @@ LIBTOR_APP_A_SOURCES += \
|
|||||||
# ADD_C_FILE: INSERT HEADERS HERE.
|
# ADD_C_FILE: INSERT HEADERS HERE.
|
||||||
noinst_HEADERS += \
|
noinst_HEADERS += \
|
||||||
src/feature/stats/bwhist.h \
|
src/feature/stats/bwhist.h \
|
||||||
|
src/feature/stats/connstats.h \
|
||||||
src/feature/stats/geoip_stats.h \
|
src/feature/stats/geoip_stats.h \
|
||||||
src/feature/stats/rephist.h \
|
src/feature/stats/rephist.h \
|
||||||
src/feature/stats/predict_ports.h
|
src/feature/stats/predict_ports.h
|
||||||
|
@ -43,9 +43,6 @@
|
|||||||
* <li>Descriptor serving statistics, used by directory caches to track
|
* <li>Descriptor serving statistics, used by directory caches to track
|
||||||
* how many descriptors they've served.
|
* how many descriptors they've served.
|
||||||
*
|
*
|
||||||
* <li>Connection statistics, used by relays to track one-way and
|
|
||||||
* bidirectional connections.
|
|
||||||
*
|
|
||||||
* <li>Onion handshake statistics, used by relays to count how many
|
* <li>Onion handshake statistics, used by relays to count how many
|
||||||
* TAP and ntor handshakes they've handled.
|
* TAP and ntor handshakes they've handled.
|
||||||
*
|
*
|
||||||
@ -78,6 +75,7 @@
|
|||||||
#include "feature/nodelist/networkstatus.h"
|
#include "feature/nodelist/networkstatus.h"
|
||||||
#include "feature/nodelist/nodelist.h"
|
#include "feature/nodelist/nodelist.h"
|
||||||
#include "feature/stats/predict_ports.h"
|
#include "feature/stats/predict_ports.h"
|
||||||
|
#include "feature/stats/connstats.h"
|
||||||
#include "feature/stats/rephist.h"
|
#include "feature/stats/rephist.h"
|
||||||
#include "lib/container/order.h"
|
#include "lib/container/order.h"
|
||||||
#include "lib/crypt_ops/crypto_rand.h"
|
#include "lib/crypt_ops/crypto_rand.h"
|
||||||
@ -1648,223 +1646,6 @@ rep_hist_note_desc_served(const char * desc)
|
|||||||
|
|
||||||
/*** Connection statistics ***/
|
/*** Connection statistics ***/
|
||||||
|
|
||||||
/** Start of the current connection stats interval or 0 if we're not
|
|
||||||
* collecting connection statistics. */
|
|
||||||
static time_t start_of_conn_stats_interval;
|
|
||||||
|
|
||||||
/** Initialize connection stats. */
|
|
||||||
void
|
|
||||||
rep_hist_conn_stats_init(time_t now)
|
|
||||||
{
|
|
||||||
start_of_conn_stats_interval = now;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Count connections that we read and wrote less than these many bytes
|
|
||||||
* from/to as below threshold. */
|
|
||||||
#define BIDI_THRESHOLD 20480
|
|
||||||
|
|
||||||
/* Count connections that we read or wrote at least this factor as many
|
|
||||||
* bytes from/to than we wrote or read to/from as mostly reading or
|
|
||||||
* writing. */
|
|
||||||
#define BIDI_FACTOR 10
|
|
||||||
|
|
||||||
/* Interval length in seconds for considering read and written bytes for
|
|
||||||
* connection stats. */
|
|
||||||
#define BIDI_INTERVAL 10
|
|
||||||
|
|
||||||
/** Start of next BIDI_INTERVAL second interval. */
|
|
||||||
static time_t bidi_next_interval = 0;
|
|
||||||
|
|
||||||
/** Number of connections that we read and wrote less than BIDI_THRESHOLD
|
|
||||||
* bytes from/to in BIDI_INTERVAL seconds. */
|
|
||||||
static uint32_t below_threshold = 0;
|
|
||||||
|
|
||||||
/** Number of connections that we read at least BIDI_FACTOR times more
|
|
||||||
* bytes from than we wrote to in BIDI_INTERVAL seconds. */
|
|
||||||
static uint32_t mostly_read = 0;
|
|
||||||
|
|
||||||
/** Number of connections that we wrote at least BIDI_FACTOR times more
|
|
||||||
* bytes to than we read from in BIDI_INTERVAL seconds. */
|
|
||||||
static uint32_t mostly_written = 0;
|
|
||||||
|
|
||||||
/** Number of connections that we read and wrote at least BIDI_THRESHOLD
|
|
||||||
* bytes from/to, but not BIDI_FACTOR times more in either direction in
|
|
||||||
* BIDI_INTERVAL seconds. */
|
|
||||||
static uint32_t both_read_and_written = 0;
|
|
||||||
|
|
||||||
/** Entry in a map from connection ID to the number of read and written
|
|
||||||
* bytes on this connection in a BIDI_INTERVAL second interval. */
|
|
||||||
typedef struct bidi_map_entry_t {
|
|
||||||
HT_ENTRY(bidi_map_entry_t) node;
|
|
||||||
uint64_t conn_id; /**< Connection ID */
|
|
||||||
size_t read; /**< Number of read bytes */
|
|
||||||
size_t written; /**< Number of written bytes */
|
|
||||||
} bidi_map_entry_t;
|
|
||||||
|
|
||||||
/** Map of OR connections together with the number of read and written
|
|
||||||
* bytes in the current BIDI_INTERVAL second interval. */
|
|
||||||
static HT_HEAD(bidimap, bidi_map_entry_t) bidi_map =
|
|
||||||
HT_INITIALIZER();
|
|
||||||
|
|
||||||
static int
|
|
||||||
bidi_map_ent_eq(const bidi_map_entry_t *a, const bidi_map_entry_t *b)
|
|
||||||
{
|
|
||||||
return a->conn_id == b->conn_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* DOCDOC bidi_map_ent_hash */
|
|
||||||
static unsigned
|
|
||||||
bidi_map_ent_hash(const bidi_map_entry_t *entry)
|
|
||||||
{
|
|
||||||
return (unsigned) entry->conn_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
HT_PROTOTYPE(bidimap, bidi_map_entry_t, node, bidi_map_ent_hash,
|
|
||||||
bidi_map_ent_eq);
|
|
||||||
HT_GENERATE2(bidimap, bidi_map_entry_t, node, bidi_map_ent_hash,
|
|
||||||
bidi_map_ent_eq, 0.6, tor_reallocarray_, tor_free_);
|
|
||||||
|
|
||||||
/* DOCDOC bidi_map_free */
|
|
||||||
static void
|
|
||||||
bidi_map_free_all(void)
|
|
||||||
{
|
|
||||||
bidi_map_entry_t **ptr, **next, *ent;
|
|
||||||
for (ptr = HT_START(bidimap, &bidi_map); ptr; ptr = next) {
|
|
||||||
ent = *ptr;
|
|
||||||
next = HT_NEXT_RMV(bidimap, &bidi_map, ptr);
|
|
||||||
tor_free(ent);
|
|
||||||
}
|
|
||||||
HT_CLEAR(bidimap, &bidi_map);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Reset counters for conn statistics. */
|
|
||||||
void
|
|
||||||
rep_hist_reset_conn_stats(time_t now)
|
|
||||||
{
|
|
||||||
start_of_conn_stats_interval = now;
|
|
||||||
below_threshold = 0;
|
|
||||||
mostly_read = 0;
|
|
||||||
mostly_written = 0;
|
|
||||||
both_read_and_written = 0;
|
|
||||||
bidi_map_free_all();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Stop collecting connection stats in a way that we can re-start doing
|
|
||||||
* so in rep_hist_conn_stats_init(). */
|
|
||||||
void
|
|
||||||
rep_hist_conn_stats_term(void)
|
|
||||||
{
|
|
||||||
rep_hist_reset_conn_stats(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** We read <b>num_read</b> bytes and wrote <b>num_written</b> from/to OR
|
|
||||||
* connection <b>conn_id</b> in second <b>when</b>. If this is the first
|
|
||||||
* observation in a new interval, sum up the last observations. Add bytes
|
|
||||||
* for this connection. */
|
|
||||||
void
|
|
||||||
rep_hist_note_or_conn_bytes(uint64_t conn_id, size_t num_read,
|
|
||||||
size_t num_written, time_t when)
|
|
||||||
{
|
|
||||||
if (!start_of_conn_stats_interval)
|
|
||||||
return;
|
|
||||||
/* Initialize */
|
|
||||||
if (bidi_next_interval == 0)
|
|
||||||
bidi_next_interval = when + BIDI_INTERVAL;
|
|
||||||
/* Sum up last period's statistics */
|
|
||||||
if (when >= bidi_next_interval) {
|
|
||||||
bidi_map_entry_t **ptr, **next, *ent;
|
|
||||||
for (ptr = HT_START(bidimap, &bidi_map); ptr; ptr = next) {
|
|
||||||
ent = *ptr;
|
|
||||||
if (ent->read + ent->written < BIDI_THRESHOLD)
|
|
||||||
below_threshold++;
|
|
||||||
else if (ent->read >= ent->written * BIDI_FACTOR)
|
|
||||||
mostly_read++;
|
|
||||||
else if (ent->written >= ent->read * BIDI_FACTOR)
|
|
||||||
mostly_written++;
|
|
||||||
else
|
|
||||||
both_read_and_written++;
|
|
||||||
next = HT_NEXT_RMV(bidimap, &bidi_map, ptr);
|
|
||||||
tor_free(ent);
|
|
||||||
}
|
|
||||||
while (when >= bidi_next_interval)
|
|
||||||
bidi_next_interval += BIDI_INTERVAL;
|
|
||||||
log_info(LD_GENERAL, "%d below threshold, %d mostly read, "
|
|
||||||
"%d mostly written, %d both read and written.",
|
|
||||||
below_threshold, mostly_read, mostly_written,
|
|
||||||
both_read_and_written);
|
|
||||||
}
|
|
||||||
/* Add this connection's bytes. */
|
|
||||||
if (num_read > 0 || num_written > 0) {
|
|
||||||
bidi_map_entry_t *entry, lookup;
|
|
||||||
lookup.conn_id = conn_id;
|
|
||||||
entry = HT_FIND(bidimap, &bidi_map, &lookup);
|
|
||||||
if (entry) {
|
|
||||||
entry->written += num_written;
|
|
||||||
entry->read += num_read;
|
|
||||||
} else {
|
|
||||||
entry = tor_malloc_zero(sizeof(bidi_map_entry_t));
|
|
||||||
entry->conn_id = conn_id;
|
|
||||||
entry->written = num_written;
|
|
||||||
entry->read = num_read;
|
|
||||||
HT_INSERT(bidimap, &bidi_map, entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Return a newly allocated string containing the connection statistics
|
|
||||||
* until <b>now</b>, or NULL if we're not collecting conn stats. Caller must
|
|
||||||
* ensure start_of_conn_stats_interval is in the past. */
|
|
||||||
char *
|
|
||||||
rep_hist_format_conn_stats(time_t now)
|
|
||||||
{
|
|
||||||
char *result, written[ISO_TIME_LEN+1];
|
|
||||||
|
|
||||||
if (!start_of_conn_stats_interval)
|
|
||||||
return NULL; /* Not initialized. */
|
|
||||||
|
|
||||||
tor_assert(now >= start_of_conn_stats_interval);
|
|
||||||
|
|
||||||
format_iso_time(written, now);
|
|
||||||
tor_asprintf(&result, "conn-bi-direct %s (%d s) %d,%d,%d,%d\n",
|
|
||||||
written,
|
|
||||||
(unsigned) (now - start_of_conn_stats_interval),
|
|
||||||
below_threshold,
|
|
||||||
mostly_read,
|
|
||||||
mostly_written,
|
|
||||||
both_read_and_written);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** If 24 hours have passed since the beginning of the current conn stats
|
|
||||||
* period, write conn stats to $DATADIR/stats/conn-stats (possibly
|
|
||||||
* overwriting an existing file) and reset counters. Return when we would
|
|
||||||
* next want to write conn stats or 0 if we never want to write. */
|
|
||||||
time_t
|
|
||||||
rep_hist_conn_stats_write(time_t now)
|
|
||||||
{
|
|
||||||
char *str = NULL;
|
|
||||||
|
|
||||||
if (!start_of_conn_stats_interval)
|
|
||||||
return 0; /* Not initialized. */
|
|
||||||
if (start_of_conn_stats_interval + WRITE_STATS_INTERVAL > now)
|
|
||||||
goto done; /* Not ready to write */
|
|
||||||
|
|
||||||
/* Generate history string. */
|
|
||||||
str = rep_hist_format_conn_stats(now);
|
|
||||||
|
|
||||||
/* Reset counters. */
|
|
||||||
rep_hist_reset_conn_stats(now);
|
|
||||||
|
|
||||||
/* Try to write to disk. */
|
|
||||||
if (!check_or_create_data_subdir("stats")) {
|
|
||||||
write_to_data_subdir("stats", "conn-stats", str, "connection statistics");
|
|
||||||
}
|
|
||||||
|
|
||||||
done:
|
|
||||||
tor_free(str);
|
|
||||||
return start_of_conn_stats_interval + WRITE_STATS_INTERVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Internal statistics to track how many requests of each type of
|
/** Internal statistics to track how many requests of each type of
|
||||||
* handshake we've received, and how many we've assigned to cpuworkers.
|
* handshake we've received, and how many we've assigned to cpuworkers.
|
||||||
* Useful for seeing trends in cpu load.
|
* Useful for seeing trends in cpu load.
|
||||||
@ -2360,7 +2141,7 @@ rep_hist_free_all(void)
|
|||||||
tor_free(exit_bytes_written);
|
tor_free(exit_bytes_written);
|
||||||
tor_free(exit_streams);
|
tor_free(exit_streams);
|
||||||
predicted_ports_free_all();
|
predicted_ports_free_all();
|
||||||
bidi_map_free_all();
|
conn_stats_free_all();
|
||||||
|
|
||||||
if (circuits_for_buffer_stats) {
|
if (circuits_for_buffer_stats) {
|
||||||
SMARTLIST_FOREACH(circuits_for_buffer_stats, circ_buffer_stats_t *, s,
|
SMARTLIST_FOREACH(circuits_for_buffer_stats, circ_buffer_stats_t *, s,
|
||||||
|
@ -56,14 +56,6 @@ void rep_hist_note_desc_served(const char * desc);
|
|||||||
void rep_hist_desc_stats_term(void);
|
void rep_hist_desc_stats_term(void);
|
||||||
time_t rep_hist_desc_stats_write(time_t now);
|
time_t rep_hist_desc_stats_write(time_t now);
|
||||||
|
|
||||||
void rep_hist_conn_stats_init(time_t now);
|
|
||||||
void rep_hist_note_or_conn_bytes(uint64_t conn_id, size_t num_read,
|
|
||||||
size_t num_written, time_t when);
|
|
||||||
void rep_hist_reset_conn_stats(time_t now);
|
|
||||||
char *rep_hist_format_conn_stats(time_t now);
|
|
||||||
time_t rep_hist_conn_stats_write(time_t now);
|
|
||||||
void rep_hist_conn_stats_term(void);
|
|
||||||
|
|
||||||
void rep_hist_note_circuit_handshake_requested(uint16_t type);
|
void rep_hist_note_circuit_handshake_requested(uint16_t type);
|
||||||
void rep_hist_note_circuit_handshake_assigned(uint16_t type);
|
void rep_hist_note_circuit_handshake_assigned(uint16_t type);
|
||||||
void rep_hist_log_circuit_handshake_stats(time_t now);
|
void rep_hist_log_circuit_handshake_stats(time_t now);
|
||||||
|
@ -39,6 +39,7 @@
|
|||||||
#include "test/test.h"
|
#include "test/test.h"
|
||||||
#include "core/mainloop/mainloop.h"
|
#include "core/mainloop/mainloop.h"
|
||||||
#include "lib/memarea/memarea.h"
|
#include "lib/memarea/memarea.h"
|
||||||
|
#include "feature/stats/connstats.h"
|
||||||
#include "feature/stats/rephist.h"
|
#include "feature/stats/rephist.h"
|
||||||
#include "app/config/statefile.h"
|
#include "app/config/statefile.h"
|
||||||
|
|
||||||
@ -111,37 +112,41 @@ test_stats(void *arg)
|
|||||||
|
|
||||||
/* Continue with testing connection statistics; we shouldn't collect
|
/* Continue with testing connection statistics; we shouldn't collect
|
||||||
* conn stats without initializing them. */
|
* conn stats without initializing them. */
|
||||||
rep_hist_note_or_conn_bytes(1, 20, 400, now);
|
conn_stats_note_or_conn_bytes(1, 20, 400, now, false);
|
||||||
s = rep_hist_format_conn_stats(now + 86400);
|
s = conn_stats_format(now + 86400);
|
||||||
tt_ptr_op(s, OP_EQ, NULL);
|
tt_ptr_op(s, OP_EQ, NULL);
|
||||||
|
|
||||||
/* Initialize stats, note bytes, and generate history string. */
|
/* Initialize stats, note bytes, and generate history string. */
|
||||||
rep_hist_conn_stats_init(now);
|
conn_stats_init(now);
|
||||||
rep_hist_note_or_conn_bytes(1, 30000, 400000, now);
|
conn_stats_note_or_conn_bytes(1, 30000, 400000, now, false);
|
||||||
rep_hist_note_or_conn_bytes(1, 30000, 400000, now + 5);
|
conn_stats_note_or_conn_bytes(1, 30000, 400000, now + 5, false);
|
||||||
rep_hist_note_or_conn_bytes(2, 400000, 30000, now + 10);
|
conn_stats_note_or_conn_bytes(2, 400000, 30000, now + 10, true);
|
||||||
rep_hist_note_or_conn_bytes(2, 400000, 30000, now + 15);
|
conn_stats_note_or_conn_bytes(2, 400000, 30000, now + 15, true);
|
||||||
s = rep_hist_format_conn_stats(now + 86400);
|
s = conn_stats_format(now + 86400);
|
||||||
tt_str_op("conn-bi-direct 2010-08-12 13:27:30 (86400 s) 0,0,1,0\n",OP_EQ, s);
|
tt_str_op("conn-bi-direct 2010-08-12 13:27:30 (86400 s) 0,0,1,0\n"
|
||||||
|
"ipv6-conn-bi-direct 2010-08-12 13:27:30 (86400 s) 0,0,0,0\n",
|
||||||
|
OP_EQ, s);
|
||||||
tor_free(s);
|
tor_free(s);
|
||||||
|
|
||||||
/* Stop collecting stats, add some bytes, and ensure we don't generate
|
/* Stop collecting stats, add some bytes, and ensure we don't generate
|
||||||
* a history string. */
|
* a history string. */
|
||||||
rep_hist_conn_stats_term();
|
conn_stats_terminate();
|
||||||
rep_hist_note_or_conn_bytes(2, 400000, 30000, now + 15);
|
conn_stats_note_or_conn_bytes(2, 400000, 30000, now + 15, true);
|
||||||
s = rep_hist_format_conn_stats(now + 86400);
|
s = conn_stats_format(now + 86400);
|
||||||
tt_ptr_op(s, OP_EQ, NULL);
|
tt_ptr_op(s, OP_EQ, NULL);
|
||||||
|
|
||||||
/* Re-start stats, add some bytes, reset stats, and see what history we
|
/* Re-start stats, add some bytes, reset stats, and see what history we
|
||||||
* get when observing no bytes at all. */
|
* get when observing no bytes at all. */
|
||||||
rep_hist_conn_stats_init(now);
|
conn_stats_init(now);
|
||||||
rep_hist_note_or_conn_bytes(1, 30000, 400000, now);
|
conn_stats_note_or_conn_bytes(1, 30000, 400000, now, false);
|
||||||
rep_hist_note_or_conn_bytes(1, 30000, 400000, now + 5);
|
conn_stats_note_or_conn_bytes(1, 30000, 400000, now + 5, false);
|
||||||
rep_hist_note_or_conn_bytes(2, 400000, 30000, now + 10);
|
conn_stats_note_or_conn_bytes(2, 400000, 30000, now + 10, true);
|
||||||
rep_hist_note_or_conn_bytes(2, 400000, 30000, now + 15);
|
conn_stats_note_or_conn_bytes(2, 400000, 30000, now + 15, true);
|
||||||
rep_hist_reset_conn_stats(now);
|
conn_stats_reset(now);
|
||||||
s = rep_hist_format_conn_stats(now + 86400);
|
s = conn_stats_format(now + 86400);
|
||||||
tt_str_op("conn-bi-direct 2010-08-12 13:27:30 (86400 s) 0,0,0,0\n",OP_EQ, s);
|
tt_str_op("conn-bi-direct 2010-08-12 13:27:30 (86400 s) 0,0,0,0\n"
|
||||||
|
"ipv6-conn-bi-direct 2010-08-12 13:27:30 (86400 s) 0,0,0,0\n",
|
||||||
|
OP_EQ, s);
|
||||||
tor_free(s);
|
tor_free(s);
|
||||||
|
|
||||||
/* Continue with testing buffer statistics; we shouldn't collect buffer
|
/* Continue with testing buffer statistics; we shouldn't collect buffer
|
||||||
|
Loading…
Reference in New Issue
Block a user