tor  0.4.2.0-alpha-dev
bwauth.c
Go to the documentation of this file.
1 /* Copyright (c) 2001-2004, Roger Dingledine.
2  * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
3  * Copyright (c) 2007-2019, The Tor Project, Inc. */
4 /* See LICENSE for licensing information */
5 
11 #define BWAUTH_PRIVATE
12 #include "core/or/or.h"
13 #include "feature/dirauth/bwauth.h"
14 
15 #include "app/config/config.h"
19 
20 #include "feature/nodelist/routerinfo_st.h"
21 #include "feature/nodelist/vote_routerstatus_st.h"
22 
24 #include "lib/encoding/keyval.h"
25 
31 static int routers_with_measured_bw = 0;
32 
36 void
38 {
39  /* Initialize this first */
41 
42  /* Iterate over the routerlist and count measured bandwidths */
43  SMARTLIST_FOREACH_BEGIN(routers, const routerinfo_t *, ri) {
44  /* Check if we know a measured bandwidth for this one */
45  if (dirserv_has_measured_bw(ri->cache_info.identity_digest)) {
47  }
48  } SMARTLIST_FOREACH_END(ri);
49 }
50 
52 int
54 {
56 }
57 
59 typedef struct mbw_cache_entry_s {
60  long mbw_kb;
61  time_t as_of;
63 
66 static digestmap_t *mbw_cache = NULL;
67 
70 STATIC void
71 dirserv_cache_measured_bw(const measured_bw_line_t *parsed_line,
72  time_t as_of)
73 {
74  mbw_cache_entry_t *e = NULL;
75 
76  tor_assert(parsed_line);
77 
78  /* Allocate a cache if we need */
79  if (!mbw_cache) mbw_cache = digestmap_new();
80 
81  /* Check if we have an existing entry */
82  e = digestmap_get(mbw_cache, parsed_line->node_id);
83  /* If we do, we can re-use it */
84  if (e) {
85  /* Check that we really are newer, and update */
86  if (as_of > e->as_of) {
87  e->mbw_kb = parsed_line->bw_kb;
88  e->as_of = as_of;
89  }
90  } else {
91  /* We'll have to insert a new entry */
92  e = tor_malloc(sizeof(*e));
93  e->mbw_kb = parsed_line->bw_kb;
94  e->as_of = as_of;
95  digestmap_set(mbw_cache, parsed_line->node_id, e);
96  }
97 }
98 
100 void
102 {
103  if (mbw_cache) {
104  /* Free the map and all entries */
105  digestmap_free(mbw_cache, tor_free_);
106  mbw_cache = NULL;
107  }
108 }
109 
111 STATIC void
113 {
114 
115  if (mbw_cache) {
116  /* Iterate through the cache and check each entry */
118  if (now > e->as_of + MAX_MEASUREMENT_AGE) {
119  tor_free(e);
120  MAP_DEL_CURRENT(k);
121  }
123 
124  /* Check if we cleared the whole thing and free if so */
125  if (digestmap_size(mbw_cache) == 0) {
126  digestmap_free(mbw_cache, tor_free_);
127  mbw_cache = 0;
128  }
129  }
130 }
131 
135 int
136 dirserv_query_measured_bw_cache_kb(const char *node_id, long *bw_kb_out,
137  time_t *as_of_out)
138 {
139  mbw_cache_entry_t *v = NULL;
140  int rv = 0;
141 
142  if (mbw_cache && node_id) {
143  v = digestmap_get(mbw_cache, node_id);
144  if (v) {
145  /* Found something */
146  rv = 1;
147  if (bw_kb_out) *bw_kb_out = v->mbw_kb;
148  if (as_of_out) *as_of_out = v->as_of;
149  }
150  }
151 
152  return rv;
153 }
154 
156 int
157 dirserv_has_measured_bw(const char *node_id)
158 {
159  return dirserv_query_measured_bw_cache_kb(node_id, NULL, NULL);
160 }
161 
163 int
165 {
166  if (mbw_cache) return digestmap_size(mbw_cache);
167  else return 0;
168 }
169 
174 uint32_t
176 {
177  int threshold;
178  uint32_t bw_kb = 0;
179  long mbw_kb;
180 
181  tor_assert(ri);
182  /* Check if we have a measured bandwidth, and check the threshold if not */
184  &mbw_kb, NULL))) {
185  threshold = get_options()->MinMeasuredBWsForAuthToIgnoreAdvertised;
186  if (routers_with_measured_bw > threshold) {
187  /* Return zero for unmeasured bandwidth if we are above threshold */
188  bw_kb = 0;
189  } else {
190  /* Return an advertised bandwidth otherwise */
191  bw_kb = router_get_advertised_bandwidth_capped(ri) / 1000;
192  }
193  } else {
194  /* We have the measured bandwidth in mbw */
195  bw_kb = (uint32_t)mbw_kb;
196  }
197 
198  return bw_kb;
199 }
200 
229 int
230 dirserv_read_measured_bandwidths(const char *from_file,
231  smartlist_t *routerstatuses,
232  smartlist_t *bw_file_headers,
233  uint8_t *digest_out)
234 {
235  FILE *fp = tor_fopen_cloexec(from_file, "r");
236  int applied_lines = 0;
237  time_t file_time, now;
238  int ok;
239  /* This flag will be 1 only when the first successful bw measurement line
240  * has been encountered, so that measured_bw_line_parse don't give warnings
241  * if there are additional header lines, as introduced in Bandwidth List spec
242  * version 1.1.0 */
243  int line_is_after_headers = 0;
244  int rv = -1;
245  char *line = NULL;
246  size_t n = 0;
247  crypto_digest_t *digest = crypto_digest256_new(DIGEST_SHA256);
248 
249  if (fp == NULL) {
250  log_warn(LD_CONFIG, "Can't open bandwidth file at configured location: %s",
251  from_file);
252  goto err;
253  }
254 
255  if (tor_getline(&line,&n,fp) <= 0) {
256  log_warn(LD_DIRSERV, "Empty bandwidth file");
257  goto err;
258  }
259  /* If the line could be gotten, add it to the digest */
260  crypto_digest_add_bytes(digest, (const char *) line, strlen(line));
261 
262  if (!strlen(line) || line[strlen(line)-1] != '\n') {
263  log_warn(LD_DIRSERV, "Long or truncated time in bandwidth file: %s",
264  escaped(line));
265  /* Continue adding lines to the digest. */
266  goto continue_digest;
267  }
268 
269  line[strlen(line)-1] = '\0';
270  file_time = (time_t)tor_parse_ulong(line, 10, 0, ULONG_MAX, &ok, NULL);
271  if (!ok) {
272  log_warn(LD_DIRSERV, "Non-integer time in bandwidth file: %s",
273  escaped(line));
274  goto continue_digest;
275  }
276 
277  now = approx_time();
278  if ((now - file_time) > MAX_MEASUREMENT_AGE) {
279  log_warn(LD_DIRSERV, "Bandwidth measurement file stale. Age: %u",
280  (unsigned)(time(NULL) - file_time));
281  goto continue_digest;
282  }
283 
284  /* If timestamp was correct and bw_file_headers is not NULL,
285  * add timestamp to bw_file_headers */
286  if (bw_file_headers)
287  smartlist_add_asprintf(bw_file_headers, "timestamp=%lu",
288  (unsigned long)file_time);
289 
290  if (routerstatuses)
291  smartlist_sort(routerstatuses, compare_vote_routerstatus_entries);
292 
293  while (!feof(fp)) {
294  measured_bw_line_t parsed_line;
295  if (tor_getline(&line, &n, fp) >= 0) {
296  crypto_digest_add_bytes(digest, (const char *) line, strlen(line));
297  if (measured_bw_line_parse(&parsed_line, line,
298  line_is_after_headers) != -1) {
299  /* This condition will be true when the first complete valid bw line
300  * has been encountered, which means the end of the header lines. */
301  line_is_after_headers = 1;
302  /* Also cache the line for dirserv_get_bandwidth_for_router() */
303  dirserv_cache_measured_bw(&parsed_line, file_time);
304  if (measured_bw_line_apply(&parsed_line, routerstatuses) > 0)
305  applied_lines++;
306  /* if the terminator is found, it is the end of header lines, set the
307  * flag but do not store anything */
308  } else if (strcmp(line, BW_FILE_HEADERS_TERMINATOR) == 0) {
309  line_is_after_headers = 1;
310  /* if the line was not a correct relay line nor the terminator and
311  * the end of the header lines has not been detected yet
312  * and it is key_value and bw_file_headers did not reach the maximum
313  * number of headers,
314  * then assume this line is a header and add it to bw_file_headers */
315  } else if (bw_file_headers &&
316  (line_is_after_headers == 0) &&
318  !strchr(line, ' ') &&
319  (smartlist_len(bw_file_headers)
321  line[strlen(line)-1] = '\0';
322  smartlist_add_strdup(bw_file_headers, line);
323  };
324  }
325  }
326 
327  /* Now would be a nice time to clean the cache, too */
329 
330  log_info(LD_DIRSERV,
331  "Bandwidth measurement file successfully read. "
332  "Applied %d measurements.", applied_lines);
333  rv = 0;
334 
335  continue_digest:
336  /* Continue parsing lines to return the digest of the Bandwidth File. */
337  while (!feof(fp)) {
338  if (tor_getline(&line, &n, fp) >= 0) {
339  crypto_digest_add_bytes(digest, (const char *) line, strlen(line));
340  }
341  }
342 
343  err:
344  if (line) {
345  // we need to raw_free this buffer because we got it from tor_getdelim()
346  raw_free(line);
347  }
348  if (fp)
349  fclose(fp);
350  if (digest_out)
351  crypto_digest_get_digest(digest, (char *) digest_out, DIGEST256_LEN);
352  crypto_digest_free(digest);
353  return rv;
354 }
355 
372 STATIC int
373 measured_bw_line_parse(measured_bw_line_t *out, const char *orig_line,
374  int line_is_after_headers)
375 {
376  char *line = tor_strdup(orig_line);
377  char *cp = line;
378  int got_bw = 0;
379  int got_node_id = 0;
380  char *strtok_state; /* lame sauce d'jour */
381 
382  if (strlen(line) == 0) {
383  log_warn(LD_DIRSERV, "Empty line in bandwidth file");
384  tor_free(line);
385  return -1;
386  }
387 
388  /* Remove end of line character, so that is not part of the token */
389  if (line[strlen(line) - 1] == '\n') {
390  line[strlen(line) - 1] = '\0';
391  }
392 
393  cp = tor_strtok_r(cp, " \t", &strtok_state);
394 
395  if (!cp) {
396  log_warn(LD_DIRSERV, "Invalid line in bandwidth file: %s",
397  escaped(orig_line));
398  tor_free(line);
399  return -1;
400  }
401 
402  if (orig_line[strlen(orig_line)-1] != '\n') {
403  log_warn(LD_DIRSERV, "Incomplete line in bandwidth file: %s",
404  escaped(orig_line));
405  tor_free(line);
406  return -1;
407  }
408 
409  do {
410  // If the line contains vote=0, ignore it.
411  if (strcmpstart(cp, "vote=0") == 0) {
412  log_debug(LD_DIRSERV, "Ignoring bandwidth file line that contains "
413  "vote=0: %s",escaped(orig_line));
414  tor_free(line);
415  return -1;
416  } else if (strcmpstart(cp, "bw=") == 0) {
417  int parse_ok = 0;
418  char *endptr;
419  if (got_bw) {
420  log_warn(LD_DIRSERV, "Double bw= in bandwidth file line: %s",
421  escaped(orig_line));
422  tor_free(line);
423  return -1;
424  }
425  cp+=strlen("bw=");
426 
427  out->bw_kb = tor_parse_long(cp, 10, 0, LONG_MAX, &parse_ok, &endptr);
428  if (!parse_ok || (*endptr && !TOR_ISSPACE(*endptr))) {
429  log_warn(LD_DIRSERV, "Invalid bandwidth in bandwidth file line: %s",
430  escaped(orig_line));
431  tor_free(line);
432  return -1;
433  }
434  got_bw=1;
435  } else if (strcmpstart(cp, "node_id=$") == 0) {
436  if (got_node_id) {
437  log_warn(LD_DIRSERV, "Double node_id= in bandwidth file line: %s",
438  escaped(orig_line));
439  tor_free(line);
440  return -1;
441  }
442  cp+=strlen("node_id=$");
443 
444  if (strlen(cp) != HEX_DIGEST_LEN ||
445  base16_decode(out->node_id, DIGEST_LEN,
446  cp, HEX_DIGEST_LEN) != DIGEST_LEN) {
447  log_warn(LD_DIRSERV, "Invalid node_id in bandwidth file line: %s",
448  escaped(orig_line));
449  tor_free(line);
450  return -1;
451  }
452  strlcpy(out->node_hex, cp, sizeof(out->node_hex));
453  got_node_id=1;
454  }
455  } while ((cp = tor_strtok_r(NULL, " \t", &strtok_state)));
456 
457  if (got_bw && got_node_id) {
458  tor_free(line);
459  return 0;
460  } else if (line_is_after_headers == 0) {
461  /* There could be additional header lines, therefore do not give warnings
462  * but returns -1 since it's not a complete bw line. */
463  log_debug(LD_DIRSERV, "Missing bw or node_id in bandwidth file line: %s",
464  escaped(orig_line));
465  tor_free(line);
466  return -1;
467  } else {
468  log_warn(LD_DIRSERV, "Incomplete line in bandwidth file: %s",
469  escaped(orig_line));
470  tor_free(line);
471  return -1;
472  }
473 }
474 
480 STATIC int
481 measured_bw_line_apply(measured_bw_line_t *parsed_line,
482  smartlist_t *routerstatuses)
483 {
484  vote_routerstatus_t *rs = NULL;
485  if (!routerstatuses)
486  return 0;
487 
488  rs = smartlist_bsearch(routerstatuses, parsed_line->node_id,
490 
491  if (rs) {
492  rs->has_measured_bw = 1;
493  rs->measured_bw_kb = (uint32_t)parsed_line->bw_kb;
494  } else {
495  log_info(LD_DIRSERV, "Node ID %s not found in routerstatus list",
496  parsed_line->node_hex);
497  }
498 
499  return rs != NULL;
500 }
uint32_t router_get_advertised_bandwidth_capped(const routerinfo_t *router)
Definition: routerlist.c:571
int dirserv_get_measured_bw_cache_size(void)
Definition: bwauth.c:164
#define SMARTLIST_FOREACH_BEGIN(sl, type, var)
void crypto_digest_get_digest(crypto_digest_t *digest, char *out, size_t out_len)
void smartlist_add_strdup(struct smartlist_t *sl, const char *string)
void dirserv_count_measured_bws(const smartlist_t *routers)
Definition: bwauth.c:37
unsigned long tor_parse_ulong(const char *s, int base, unsigned long min, unsigned long max, int *ok, char **next)
Definition: parse_int.c:75
crypto_digest_t * crypto_digest256_new(digest_algorithm_t algorithm)
Header file for config.c.
void crypto_digest_add_bytes(crypto_digest_t *digest, const char *data, size_t len)
int strcmpstart(const char *s1, const char *s2)
Definition: util_string.c:206
#define MAP_DEL_CURRENT(keyvar)
Definition: map.h:139
#define BW_FILE_HEADERS_TERMINATOR
Definition: bwauth.h:20
#define tor_free(p)
Definition: malloc.h:52
int dirserv_has_measured_bw(const char *node_id)
Definition: bwauth.c:157
static int routers_with_measured_bw
Definition: bwauth.c:31
void dirserv_clear_measured_bw_cache(void)
Definition: bwauth.c:101
uint32_t dirserv_get_credible_bandwidth_kb(const routerinfo_t *ri)
Definition: bwauth.c:175
#define MAX_BW_FILE_HEADER_COUNT_IN_VOTE
Definition: bwauth.h:16
int dirserv_read_measured_bandwidths(const char *from_file, smartlist_t *routerstatuses, smartlist_t *bw_file_headers, uint8_t *digest_out)
Definition: bwauth.c:230
#define DIGEST256_LEN
Definition: digest_sizes.h:23
STATIC void dirserv_expire_measured_bw_cache(time_t now)
Definition: bwauth.c:112
tor_assert(buffer)
Header for crypto_format.c.
#define DIGEST_LEN
Definition: digest_sizes.h:20
void smartlist_add_asprintf(struct smartlist_t *sl, const char *pattern,...)
Definition: smartlist.c:36
Master header file for Tor-specific functionality.
STATIC void dirserv_cache_measured_bw(const measured_bw_line_t *parsed_line, time_t as_of)
Definition: bwauth.c:71
#define LD_DIRSERV
Definition: log.h:87
Header file for bwauth.c.
void tor_free_(void *mem)
Definition: malloc.c:227
int compare_digest_to_vote_routerstatus_entry(const void *_key, const void **_member)
void * smartlist_bsearch(const smartlist_t *sl, const void *key, int(*compare)(const void *key, const void **member))
Definition: smartlist.c:411
#define DIGESTMAP_FOREACH_END
Definition: map.h:167
#define HEX_DIGEST_LEN
Definition: crypto_digest.h:35
STATIC int measured_bw_line_parse(measured_bw_line_t *out, const char *orig_line, int line_is_after_headers)
Definition: bwauth.c:373
char identity_digest[DIGEST_LEN]
int dirserv_get_last_n_measured_bws(void)
Definition: bwauth.c:53
Header file for ns_parse.c.
int dirserv_query_measured_bw_cache_kb(const char *node_id, long *bw_kb_out, time_t *as_of_out)
Definition: bwauth.c:136
static digestmap_t * mbw_cache
Definition: bwauth.c:66
const char * escaped(const char *s)
Definition: escape.c:126
#define DIGESTMAP_FOREACH_MODIFY(map, keyvar, valtype, valvar)
Definition: map.h:164
Header for keyval.c.
time_t approx_time(void)
Definition: approx_time.c:32
#define LOG_DEBUG
Definition: log.h:39
long tor_parse_long(const char *s, int base, long min, long max, int *ok, char **next)
Definition: parse_int.c:56
int base16_decode(char *dest, size_t destlen, const char *src, size_t srclen)
Definition: binascii.c:506
STATIC int measured_bw_line_apply(measured_bw_line_t *parsed_line, smartlist_t *routerstatuses)
Definition: bwauth.c:481
Definition: bwauth.c:59
int string_is_key_value(int severity, const char *string)
Definition: keyval.c:25
FILE * tor_fopen_cloexec(const char *path, const char *mode)
Definition: files.c:86
void smartlist_sort(smartlist_t *sl, int(*compare)(const void **a, const void **b))
Definition: smartlist.c:334
Header file for networkstatus.c.
#define LD_CONFIG
Definition: log.h:65
Header file for routerlist.c.
struct mbw_cache_entry_s mbw_cache_entry_t