tor  0.4.1.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 
206 int
207 dirserv_read_measured_bandwidths(const char *from_file,
208  smartlist_t *routerstatuses,
209  smartlist_t *bw_file_headers,
210  uint8_t *digest_out)
211 {
212  FILE *fp = tor_fopen_cloexec(from_file, "r");
213  int applied_lines = 0;
214  time_t file_time, now;
215  int ok;
216  /* This flag will be 1 only when the first successful bw measurement line
217  * has been encountered, so that measured_bw_line_parse don't give warnings
218  * if there are additional header lines, as introduced in Bandwidth List spec
219  * version 1.1.0 */
220  int line_is_after_headers = 0;
221  int rv = -1;
222  char *line = NULL;
223  size_t n = 0;
224  crypto_digest_t *digest = crypto_digest256_new(DIGEST_SHA256);
225 
226  /* Initialise line, so that we can't possibly run off the end. */
227 
228  if (fp == NULL) {
229  log_warn(LD_CONFIG, "Can't open bandwidth file at configured location: %s",
230  from_file);
231  goto err;
232  }
233 
234  /* If fgets fails, line is either unmodified, or indeterminate. */
235  if (tor_getline(&line,&n,fp) <= 0) {
236  log_warn(LD_DIRSERV, "Empty bandwidth file");
237  goto err;
238  }
239  /* If the line could be gotten, add it to the digest */
240  crypto_digest_add_bytes(digest, (const char *) line, strlen(line));
241 
242  if (!strlen(line) || line[strlen(line)-1] != '\n') {
243  log_warn(LD_DIRSERV, "Long or truncated time in bandwidth file: %s",
244  escaped(line));
245  /* Continue adding lines to the digest. */
246  goto continue_digest;
247  }
248 
249  line[strlen(line)-1] = '\0';
250  file_time = (time_t)tor_parse_ulong(line, 10, 0, ULONG_MAX, &ok, NULL);
251  if (!ok) {
252  log_warn(LD_DIRSERV, "Non-integer time in bandwidth file: %s",
253  escaped(line));
254  goto continue_digest;
255  }
256 
257  now = approx_time();
258  if ((now - file_time) > MAX_MEASUREMENT_AGE) {
259  log_warn(LD_DIRSERV, "Bandwidth measurement file stale. Age: %u",
260  (unsigned)(time(NULL) - file_time));
261  goto continue_digest;
262  }
263 
264  /* If timestamp was correct and bw_file_headers is not NULL,
265  * add timestamp to bw_file_headers */
266  if (bw_file_headers)
267  smartlist_add_asprintf(bw_file_headers, "timestamp=%lu",
268  (unsigned long)file_time);
269 
270  if (routerstatuses)
271  smartlist_sort(routerstatuses, compare_vote_routerstatus_entries);
272 
273  while (!feof(fp)) {
274  measured_bw_line_t parsed_line;
275  if (tor_getline(&line, &n, fp) >= 0) {
276  crypto_digest_add_bytes(digest, (const char *) line, strlen(line));
277  if (measured_bw_line_parse(&parsed_line, line,
278  line_is_after_headers) != -1) {
279  /* This condition will be true when the first complete valid bw line
280  * has been encountered, which means the end of the header lines. */
281  line_is_after_headers = 1;
282  /* Also cache the line for dirserv_get_bandwidth_for_router() */
283  dirserv_cache_measured_bw(&parsed_line, file_time);
284  if (measured_bw_line_apply(&parsed_line, routerstatuses) > 0)
285  applied_lines++;
286  /* if the terminator is found, it is the end of header lines, set the
287  * flag but do not store anything */
288  } else if (strcmp(line, BW_FILE_HEADERS_TERMINATOR) == 0) {
289  line_is_after_headers = 1;
290  /* if the line was not a correct relay line nor the terminator and
291  * the end of the header lines has not been detected yet
292  * and it is key_value and bw_file_headers did not reach the maximum
293  * number of headers,
294  * then assume this line is a header and add it to bw_file_headers */
295  } else if (bw_file_headers &&
296  (line_is_after_headers == 0) &&
298  !strchr(line, ' ') &&
299  (smartlist_len(bw_file_headers)
301  line[strlen(line)-1] = '\0';
302  smartlist_add_strdup(bw_file_headers, line);
303  };
304  }
305  }
306 
307  /* Now would be a nice time to clean the cache, too */
309 
310  log_info(LD_DIRSERV,
311  "Bandwidth measurement file successfully read. "
312  "Applied %d measurements.", applied_lines);
313  rv = 0;
314 
315  continue_digest:
316  /* Continue parsing lines to return the digest of the Bandwidth File. */
317  while (!feof(fp)) {
318  if (tor_getline(&line, &n, fp) >= 0) {
319  crypto_digest_add_bytes(digest, (const char *) line, strlen(line));
320  }
321  }
322 
323  err:
324  if (line) {
325  // we need to raw_free this buffer because we got it from tor_getdelim()
326  raw_free(line);
327  }
328  if (fp)
329  fclose(fp);
330  if (digest_out)
331  crypto_digest_get_digest(digest, (char *) digest_out, DIGEST256_LEN);
332  crypto_digest_free(digest);
333  return rv;
334 }
335 
349 STATIC int
350 measured_bw_line_parse(measured_bw_line_t *out, const char *orig_line,
351  int line_is_after_headers)
352 {
353  char *line = tor_strdup(orig_line);
354  char *cp = line;
355  int got_bw = 0;
356  int got_node_id = 0;
357  char *strtok_state; /* lame sauce d'jour */
358 
359  if (strlen(line) == 0) {
360  log_warn(LD_DIRSERV, "Empty line in bandwidth file");
361  tor_free(line);
362  return -1;
363  }
364 
365  /* Remove end of line character, so that is not part of the token */
366  if (line[strlen(line) - 1] == '\n') {
367  line[strlen(line) - 1] = '\0';
368  }
369 
370  cp = tor_strtok_r(cp, " \t", &strtok_state);
371 
372  if (!cp) {
373  log_warn(LD_DIRSERV, "Invalid line in bandwidth file: %s",
374  escaped(orig_line));
375  tor_free(line);
376  return -1;
377  }
378 
379  if (orig_line[strlen(orig_line)-1] != '\n') {
380  log_warn(LD_DIRSERV, "Incomplete line in bandwidth file: %s",
381  escaped(orig_line));
382  tor_free(line);
383  return -1;
384  }
385 
386  do {
387  if (strcmpstart(cp, "bw=") == 0) {
388  int parse_ok = 0;
389  char *endptr;
390  if (got_bw) {
391  log_warn(LD_DIRSERV, "Double bw= in bandwidth file line: %s",
392  escaped(orig_line));
393  tor_free(line);
394  return -1;
395  }
396  cp+=strlen("bw=");
397 
398  out->bw_kb = tor_parse_long(cp, 10, 0, LONG_MAX, &parse_ok, &endptr);
399  if (!parse_ok || (*endptr && !TOR_ISSPACE(*endptr))) {
400  log_warn(LD_DIRSERV, "Invalid bandwidth in bandwidth file line: %s",
401  escaped(orig_line));
402  tor_free(line);
403  return -1;
404  }
405  got_bw=1;
406  } else if (strcmpstart(cp, "node_id=$") == 0) {
407  if (got_node_id) {
408  log_warn(LD_DIRSERV, "Double node_id= in bandwidth file line: %s",
409  escaped(orig_line));
410  tor_free(line);
411  return -1;
412  }
413  cp+=strlen("node_id=$");
414 
415  if (strlen(cp) != HEX_DIGEST_LEN ||
416  base16_decode(out->node_id, DIGEST_LEN,
417  cp, HEX_DIGEST_LEN) != DIGEST_LEN) {
418  log_warn(LD_DIRSERV, "Invalid node_id in bandwidth file line: %s",
419  escaped(orig_line));
420  tor_free(line);
421  return -1;
422  }
423  strlcpy(out->node_hex, cp, sizeof(out->node_hex));
424  got_node_id=1;
425  }
426  } while ((cp = tor_strtok_r(NULL, " \t", &strtok_state)));
427 
428  if (got_bw && got_node_id) {
429  tor_free(line);
430  return 0;
431  } else if (line_is_after_headers == 0) {
432  /* There could be additional header lines, therefore do not give warnings
433  * but returns -1 since it's not a complete bw line. */
434  log_debug(LD_DIRSERV, "Missing bw or node_id in bandwidth file line: %s",
435  escaped(orig_line));
436  tor_free(line);
437  return -1;
438  } else {
439  log_warn(LD_DIRSERV, "Incomplete line in bandwidth file: %s",
440  escaped(orig_line));
441  tor_free(line);
442  return -1;
443  }
444 }
445 
451 STATIC int
452 measured_bw_line_apply(measured_bw_line_t *parsed_line,
453  smartlist_t *routerstatuses)
454 {
455  vote_routerstatus_t *rs = NULL;
456  if (!routerstatuses)
457  return 0;
458 
459  rs = smartlist_bsearch(routerstatuses, parsed_line->node_id,
461 
462  if (rs) {
463  rs->has_measured_bw = 1;
464  rs->measured_bw_kb = (uint32_t)parsed_line->bw_kb;
465  } else {
466  log_info(LD_DIRSERV, "Node ID %s not found in routerstatus list",
467  parsed_line->node_hex);
468  }
469 
470  return rs != NULL;
471 }
void crypto_digest_add_bytes(crypto_digest_t *digest, const char *data, size_t len)
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 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
Header file for config.c.
int strcmpstart(const char *s1, const char *s2)
Definition: util_string.c:209
#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:207
#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
void crypto_digest_get_digest(crypto_digest_t *digest, char *out, size_t out_len)
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:86
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:350
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:38
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:504
STATIC int measured_bw_line_apply(measured_bw_line_t *parsed_line, smartlist_t *routerstatuses)
Definition: bwauth.c:452
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:64
Header file for routerlist.c.
struct mbw_cache_entry_s mbw_cache_entry_t
crypto_digest_t * crypto_digest256_new(digest_algorithm_t algorithm)