Tor 0.4.9.0-alpha-dev
connstats.c
Go to the documentation of this file.
1/* Copyright (c) 2001 Matej Pfajfar.
2 * Copyright (c) 2001-2004, Roger Dingledine.
3 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
4 * Copyright (c) 2007-2021, The Tor Project, Inc. */
5/* See LICENSE for licensing information */
6
7/**
8 * @file connstats.c
9 * @brief Count bidirectional vs one-way connections.
10 *
11 * Connection statistics, use to track one-way and bidirectional connections.
12 *
13 * Note that this code counts concurrent connections in each
14 * BIDI_INTERVAL-second interval, not total connections. It can tell you what
15 * fraction of connections are bidirectional at each time, not necessarily
16 * what number are bidirectional.
17 **/
18
19#include "orconfig.h"
20#include "core/or/or.h"
22#include "app/config/config.h"
23
24/** Start of the current connection stats interval or 0 if we're not
25 * collecting connection statistics. */
27
28/** Initialize connection stats. */
29void
30conn_stats_init(time_t now)
31{
33}
34
35/** Count connections on which we read and wrote less than this many bytes
36 * as "below threshold." */
37#define BIDI_THRESHOLD 20480
38
39/** Count connections that we read or wrote at least this factor as many
40 * bytes from/to than we wrote or read to/from as mostly reading or
41 * writing. */
42#define BIDI_FACTOR 10
43
44/** Interval length in seconds for considering read and written bytes for
45 * connection stats. */
46#define BIDI_INTERVAL 10
47
48/** Start of next BIDI_INTERVAL second interval. */
49static time_t bidi_next_interval = 0;
50
51/** A single grouped set of connection type counts. */
52typedef struct conn_counts_t {
53 /** Number of connections that we read and wrote less than BIDI_THRESHOLD
54 * bytes from/to in BIDI_INTERVAL seconds. */
56
57 /** Number of connections that we read at least BIDI_FACTOR times more
58 * bytes from than we wrote to in BIDI_INTERVAL seconds. */
59 uint32_t mostly_read;
60
61 /** Number of connections that we wrote at least BIDI_FACTOR times more
62 * bytes to than we read from in BIDI_INTERVAL seconds. */
64
65 /** Number of connections that we read and wrote at least BIDI_THRESHOLD
66 * bytes from/to, but not BIDI_FACTOR times more in either direction in
67 * BIDI_INTERVAL seconds. */
70
71/** A collection of connection counts, over all OR connections. */
73/** A collection of connection counts, over IPv6 OR connections only. */
75
76/** Entry in a map from connection ID to the number of read and written
77 * bytes on this connection in a BIDI_INTERVAL second interval. */
78typedef struct bidi_map_entry_t {
79 HT_ENTRY(bidi_map_entry_t) node;
80 uint64_t conn_id; /**< Connection ID */
81 size_t read; /**< Number of read bytes */
82 size_t written; /**< Number of written bytes */
83 bool is_ipv6; /**< True if this is an IPv6 connection */
85
86/** Map of OR connections together with the number of read and written
87 * bytes in the current BIDI_INTERVAL second interval. */
88static HT_HEAD(bidimap, bidi_map_entry_t) bidi_map =
89 HT_INITIALIZER();
90
91/** Hashtable helper: return true if @a a and @a b have the same key. */
92static int
93bidi_map_ent_eq(const bidi_map_entry_t *a, const bidi_map_entry_t *b)
94{
95 return a->conn_id == b->conn_id;
96}
97
98/** Hashtable helper: compute a digest for the key of @a entry. */
99static unsigned
101{
102 return (unsigned) entry->conn_id;
103}
104
106 bidi_map_ent_eq);
107HT_GENERATE2(bidimap, bidi_map_entry_t, node, bidi_map_ent_hash,
108 bidi_map_ent_eq, 0.6, tor_reallocarray_, tor_free_);
109
110/** Release all storage held in connstats.c */
111void
113{
114 bidi_map_entry_t **ptr, **next, *ent;
115 for (ptr = HT_START(bidimap, &bidi_map); ptr; ptr = next) {
116 ent = *ptr;
117 next = HT_NEXT_RMV(bidimap, &bidi_map, ptr);
118 tor_free(ent);
119 }
120 HT_CLEAR(bidimap, &bidi_map);
121}
122
123/** Reset counters for conn statistics. */
124void
126{
128 memset(&counts, 0, sizeof(counts));
129 memset(&counts_ipv6, 0, sizeof(counts_ipv6));
131}
132
133/** Stop collecting connection stats in a way that we can re-start doing
134 * so in conn_stats_init(). */
135void
137{
139}
140
141/**
142 * Record a single entry @a ent in the counts structure @a cnt.
143 */
144static void
146{
147 if (ent->read + ent->written < BIDI_THRESHOLD)
148 cnt->below_threshold++;
149 else if (ent->read >= ent->written * BIDI_FACTOR)
150 cnt->mostly_read++;
151 else if (ent->written >= ent->read * BIDI_FACTOR)
152 cnt->mostly_written++;
153 else
155}
156
157/**
158 * Count all the connection information we've received during the current
159 * period in 'bidimap', and store that information in the appropriate count
160 * structures.
161 **/
162static void
164{
165 bidi_map_entry_t **ptr, **next, *ent;
166 for (ptr = HT_START(bidimap, &bidi_map); ptr; ptr = next) {
167 ent = *ptr;
169 if (ent->is_ipv6)
171 next = HT_NEXT_RMV(bidimap, &bidi_map, ptr);
172 tor_free(ent);
173 }
174 log_info(LD_GENERAL, "%d below threshold, %d mostly read, "
175 "%d mostly written, %d both read and written.",
178}
179
180/** We read <b>num_read</b> bytes and wrote <b>num_written</b> from/to OR
181 * connection <b>conn_id</b> in second <b>when</b>. If this is the first
182 * observation in a new interval, sum up the last observations. Add bytes
183 * for this connection. */
184void
185conn_stats_note_or_conn_bytes(uint64_t conn_id, size_t num_read,
186 size_t num_written, time_t when,
187 bool is_ipv6)
188{
190 return;
191 /* Initialize */
192 if (bidi_next_interval == 0)
194 /* Sum up last period's statistics */
195 if (when >= bidi_next_interval) {
197 while (when >= bidi_next_interval)
199 }
200 /* Add this connection's bytes. */
201 if (num_read > 0 || num_written > 0) {
202 bidi_map_entry_t *entry, lookup;
203 lookup.conn_id = conn_id;
204 entry = HT_FIND(bidimap, &bidi_map, &lookup);
205 if (entry) {
206 entry->written += num_written;
207 entry->read += num_read;
208 entry->is_ipv6 |= is_ipv6;
209 } else {
210 entry = tor_malloc_zero(sizeof(bidi_map_entry_t));
211 entry->conn_id = conn_id;
212 entry->written = num_written;
213 entry->read = num_read;
214 entry->is_ipv6 = is_ipv6;
215 HT_INSERT(bidimap, &bidi_map, entry);
216 }
217 }
218}
219
220/** Return a newly allocated string containing the connection statistics
221 * until <b>now</b>, or NULL if we're not collecting conn stats. Caller must
222 * ensure start_of_conn_stats_interval is in the past. */
223char *
225{
226 char *result, written_at[ISO_TIME_LEN+1];
227
229 return NULL; /* Not initialized. */
230
232
233 format_iso_time(written_at, now);
234 tor_asprintf(&result,
235 "conn-bi-direct %s (%d s) "
236 "%"PRIu32",%"PRIu32",%"PRIu32",%"PRIu32"\n"
237 "ipv6-conn-bi-direct %s (%d s) "
238 "%"PRIu32",%"PRIu32",%"PRIu32",%"PRIu32"\n",
239 written_at,
240 (unsigned) (now - start_of_conn_stats_interval),
245 written_at,
246 (unsigned) (now - start_of_conn_stats_interval),
251
252 return result;
253}
254
255/** If 24 hours have passed since the beginning of the current conn stats
256 * period, write conn stats to $DATADIR/stats/conn-stats (possibly
257 * overwriting an existing file) and reset counters. Return when we would
258 * next want to write conn stats or 0 if we never want to write. */
259time_t
261{
262 char *str = NULL;
263
265 return 0; /* Not initialized. */
266 if (start_of_conn_stats_interval + WRITE_STATS_INTERVAL > now)
267 goto done; /* Not ready to write */
268
269 /* Generate history string. */
270 str = conn_stats_format(now);
271
272 /* Reset counters. */
273 conn_stats_reset(now);
274
275 /* Try to write to disk. */
276 if (!check_or_create_data_subdir("stats")) {
277 write_to_data_subdir("stats", "conn-stats", str, "connection statistics");
278 }
279
280 done:
281 tor_free(str);
282 return start_of_conn_stats_interval + WRITE_STATS_INTERVAL;
283}
int check_or_create_data_subdir(const char *subdir)
Definition: config.c:7181
int write_to_data_subdir(const char *subdir, const char *fname, const char *str, const char *descr)
Definition: config.c:7200
Header file for config.c.
#define BIDI_FACTOR
Definition: connstats.c:42
time_t conn_stats_save(time_t now)
Definition: connstats.c:260
static void collect_period_statistics(void)
Definition: connstats.c:163
void conn_stats_reset(time_t now)
Definition: connstats.c:125
static conn_counts_t counts
Definition: connstats.c:72
void conn_stats_terminate(void)
Definition: connstats.c:136
#define BIDI_INTERVAL
Definition: connstats.c:46
static void add_entry_to_count(conn_counts_t *cnt, const bidi_map_entry_t *ent)
Definition: connstats.c:145
static time_t start_of_conn_stats_interval
Definition: connstats.c:26
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)
Definition: connstats.c:185
static HT_HEAD(bidimap, bidi_map_entry_t)
Definition: connstats.c:88
static conn_counts_t counts_ipv6
Definition: connstats.c:74
void conn_stats_free_all(void)
Definition: connstats.c:112
static time_t bidi_next_interval
Definition: connstats.c:49
static unsigned bidi_map_ent_hash(const bidi_map_entry_t *entry)
Definition: connstats.c:100
char * conn_stats_format(time_t now)
Definition: connstats.c:224
void conn_stats_init(time_t now)
Definition: connstats.c:30
#define BIDI_THRESHOLD
Definition: connstats.c:37
Header for feature/stats/connstats.c.
HT_PROTOTYPE(hs_circuitmap_ht, circuit_t, hs_circuitmap_node, hs_circuit_hash_token, hs_circuits_have_same_token)
#define LD_GENERAL
Definition: log.h:62
void * tor_reallocarray_(void *ptr, size_t sz1, size_t sz2)
Definition: malloc.c:146
void tor_free_(void *mem)
Definition: malloc.c:227
#define tor_free(p)
Definition: malloc.h:56
Master header file for Tor-specific functionality.
int tor_asprintf(char **strp, const char *fmt,...)
Definition: printf.c:75
Definition: connstats.c:78
uint32_t below_threshold
Definition: connstats.c:55
uint32_t mostly_written
Definition: connstats.c:63
uint32_t mostly_read
Definition: connstats.c:59
uint32_t both_read_and_written
Definition: connstats.c:68
void format_iso_time(char *buf, time_t t)
Definition: time_fmt.c:326
#define tor_assert(expr)
Definition: util_bug.h:103