Tor  0.4.6.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-2020, 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. */
29 void
30 conn_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. */
49 static time_t bidi_next_interval = 0;
50 
51 /** A single grouped set of connection type counts. */
52 typedef 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. */
55  uint32_t below_threshold;
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. */
63  uint32_t mostly_written;
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. */
69 } conn_counts_t ;
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. */
78 typedef 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. */
88 static 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. */
92 static int
93 bidi_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. */
99 static unsigned
101 {
102  return (unsigned) entry->conn_id;
103 }
104 
106  bidi_map_ent_eq);
107 HT_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 */
111 void
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. */
124 void
125 conn_stats_reset(time_t now)
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(). */
135 void
137 {
138  conn_stats_reset(0);
139 }
140 
141 /**
142  * Record a single entry @a ent in the counts structure @a cnt.
143  */
144 static 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
154  cnt->both_read_and_written++;
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  **/
162 static void
164 {
165  bidi_map_entry_t **ptr, **next, *ent;
166  for (ptr = HT_START(bidimap, &bidi_map); ptr; ptr = next) {
167  ent = *ptr;
168  add_entry_to_count(&counts, ent);
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. */
184 void
185 conn_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. */
223 char *
224 conn_stats_format(time_t now)
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. */
259 time_t
260 conn_stats_save(time_t now)
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 }
tor_free
#define tor_free(p)
Definition: malloc.h:52
counts
static conn_counts_t counts
Definition: connstats.c:72
tor_free_
void tor_free_(void *mem)
Definition: malloc.c:227
conn_counts_t::both_read_and_written
uint32_t both_read_and_written
Definition: connstats.c:68
tor_assert
#define tor_assert(expr)
Definition: util_bug.h:102
format_iso_time
void format_iso_time(char *buf, time_t t)
Definition: time_fmt.c:295
LD_GENERAL
#define LD_GENERAL
Definition: log.h:62
add_entry_to_count
static void add_entry_to_count(conn_counts_t *cnt, const bidi_map_entry_t *ent)
Definition: connstats.c:145
conn_counts_t
Definition: connstats.c:52
tor_reallocarray_
void * tor_reallocarray_(void *ptr, size_t sz1, size_t sz2)
Definition: malloc.c:146
BIDI_FACTOR
#define BIDI_FACTOR
Definition: connstats.c:42
check_or_create_data_subdir
int check_or_create_data_subdir(const char *subdir)
Definition: config.c:7056
counts_ipv6
static conn_counts_t counts_ipv6
Definition: connstats.c:74
conn_stats_terminate
void conn_stats_terminate(void)
Definition: connstats.c:136
conn_counts_t::mostly_read
uint32_t mostly_read
Definition: connstats.c:59
start_of_conn_stats_interval
static time_t start_of_conn_stats_interval
Definition: connstats.c:26
conn_stats_note_or_conn_bytes
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
collect_period_statistics
static void collect_period_statistics(void)
Definition: connstats.c:163
conn_counts_t::mostly_written
uint32_t mostly_written
Definition: connstats.c:63
conn_stats_init
void conn_stats_init(time_t now)
Definition: connstats.c:30
conn_stats_save
time_t conn_stats_save(time_t now)
Definition: connstats.c:260
BIDI_INTERVAL
#define BIDI_INTERVAL
Definition: connstats.c:46
conn_stats_free_all
void conn_stats_free_all(void)
Definition: connstats.c:112
bidi_map_ent_hash
static unsigned bidi_map_ent_hash(const bidi_map_entry_t *entry)
Definition: connstats.c:100
bidi_next_interval
static time_t bidi_next_interval
Definition: connstats.c:49
write_to_data_subdir
int write_to_data_subdir(const char *subdir, const char *fname, const char *str, const char *descr)
Definition: config.c:7075
conn_stats_reset
void conn_stats_reset(time_t now)
Definition: connstats.c:125
connstats.h
Header for feature/stats/connstats.c.
bidi_map_entry_t
Definition: connstats.c:78
tor_asprintf
int tor_asprintf(char **strp, const char *fmt,...)
Definition: printf.c:75
conn_stats_format
char * conn_stats_format(time_t now)
Definition: connstats.c:224
HT_PROTOTYPE
HT_PROTOTYPE(hs_circuitmap_ht, circuit_t, hs_circuitmap_node, hs_circuit_hash_token, hs_circuits_have_same_token)
config.h
Header file for config.c.
HT_HEAD
static HT_HEAD(bidimap, bidi_map_entry_t)
Definition: connstats.c:88
conn_counts_t::below_threshold
uint32_t below_threshold
Definition: connstats.c:55
BIDI_THRESHOLD
#define BIDI_THRESHOLD
Definition: connstats.c:37
or.h
Master header file for Tor-specific functionality.