tor  0.4.0.1-alpha
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 
23 #include "lib/encoding/keyval.h"
24 
30 static int routers_with_measured_bw = 0;
31 
35 void
37 {
38  /* Initialize this first */
40 
41  /* Iterate over the routerlist and count measured bandwidths */
42  SMARTLIST_FOREACH_BEGIN(routers, const routerinfo_t *, ri) {
43  /* Check if we know a measured bandwidth for this one */
44  if (dirserv_has_measured_bw(ri->cache_info.identity_digest)) {
46  }
47  } SMARTLIST_FOREACH_END(ri);
48 }
49 
51 int
53 {
55 }
56 
58 typedef struct mbw_cache_entry_s {
59  long mbw_kb;
60  time_t as_of;
62 
65 static digestmap_t *mbw_cache = NULL;
66 
69 STATIC void
70 dirserv_cache_measured_bw(const measured_bw_line_t *parsed_line,
71  time_t as_of)
72 {
73  mbw_cache_entry_t *e = NULL;
74 
75  tor_assert(parsed_line);
76 
77  /* Allocate a cache if we need */
78  if (!mbw_cache) mbw_cache = digestmap_new();
79 
80  /* Check if we have an existing entry */
81  e = digestmap_get(mbw_cache, parsed_line->node_id);
82  /* If we do, we can re-use it */
83  if (e) {
84  /* Check that we really are newer, and update */
85  if (as_of > e->as_of) {
86  e->mbw_kb = parsed_line->bw_kb;
87  e->as_of = as_of;
88  }
89  } else {
90  /* We'll have to insert a new entry */
91  e = tor_malloc(sizeof(*e));
92  e->mbw_kb = parsed_line->bw_kb;
93  e->as_of = as_of;
94  digestmap_set(mbw_cache, parsed_line->node_id, e);
95  }
96 }
97 
99 void
101 {
102  if (mbw_cache) {
103  /* Free the map and all entries */
104  digestmap_free(mbw_cache, tor_free_);
105  mbw_cache = NULL;
106  }
107 }
108 
110 STATIC void
112 {
113 
114  if (mbw_cache) {
115  /* Iterate through the cache and check each entry */
117  if (now > e->as_of + MAX_MEASUREMENT_AGE) {
118  tor_free(e);
119  MAP_DEL_CURRENT(k);
120  }
122 
123  /* Check if we cleared the whole thing and free if so */
124  if (digestmap_size(mbw_cache) == 0) {
125  digestmap_free(mbw_cache, tor_free_);
126  mbw_cache = 0;
127  }
128  }
129 }
130 
134 int
135 dirserv_query_measured_bw_cache_kb(const char *node_id, long *bw_kb_out,
136  time_t *as_of_out)
137 {
138  mbw_cache_entry_t *v = NULL;
139  int rv = 0;
140 
141  if (mbw_cache && node_id) {
142  v = digestmap_get(mbw_cache, node_id);
143  if (v) {
144  /* Found something */
145  rv = 1;
146  if (bw_kb_out) *bw_kb_out = v->mbw_kb;
147  if (as_of_out) *as_of_out = v->as_of;
148  }
149  }
150 
151  return rv;
152 }
153 
155 int
156 dirserv_has_measured_bw(const char *node_id)
157 {
158  return dirserv_query_measured_bw_cache_kb(node_id, NULL, NULL);
159 }
160 
162 int
164 {
165  if (mbw_cache) return digestmap_size(mbw_cache);
166  else return 0;
167 }
168 
173 uint32_t
175 {
176  int threshold;
177  uint32_t bw_kb = 0;
178  long mbw_kb;
179 
180  tor_assert(ri);
181  /* Check if we have a measured bandwidth, and check the threshold if not */
183  &mbw_kb, NULL))) {
184  threshold = get_options()->MinMeasuredBWsForAuthToIgnoreAdvertised;
185  if (routers_with_measured_bw > threshold) {
186  /* Return zero for unmeasured bandwidth if we are above threshold */
187  bw_kb = 0;
188  } else {
189  /* Return an advertised bandwidth otherwise */
190  bw_kb = router_get_advertised_bandwidth_capped(ri) / 1000;
191  }
192  } else {
193  /* We have the measured bandwidth in mbw */
194  bw_kb = (uint32_t)mbw_kb;
195  }
196 
197  return bw_kb;
198 }
199 
205 int
206 dirserv_read_measured_bandwidths(const char *from_file,
207  smartlist_t *routerstatuses,
208  smartlist_t *bw_file_headers)
209 {
210  FILE *fp = tor_fopen_cloexec(from_file, "r");
211  int applied_lines = 0;
212  time_t file_time, now;
213  int ok;
214  /* This flag will be 1 only when the first successful bw measurement line
215  * has been encountered, so that measured_bw_line_parse don't give warnings
216  * if there are additional header lines, as introduced in Bandwidth List spec
217  * version 1.1.0 */
218  int line_is_after_headers = 0;
219  int rv = -1;
220  char *line = NULL;
221  size_t n = 0;
222 
223  /* Initialise line, so that we can't possibly run off the end. */
224 
225  if (fp == NULL) {
226  log_warn(LD_CONFIG, "Can't open bandwidth file at configured location: %s",
227  from_file);
228  goto err;
229  }
230 
231  /* If fgets fails, line is either unmodified, or indeterminate. */
232  if (tor_getline(&line,&n,fp) <= 0) {
233  log_warn(LD_DIRSERV, "Empty bandwidth file");
234  goto err;
235  }
236 
237  if (!strlen(line) || line[strlen(line)-1] != '\n') {
238  log_warn(LD_DIRSERV, "Long or truncated time in bandwidth file: %s",
239  escaped(line));
240  goto err;
241  }
242 
243  line[strlen(line)-1] = '\0';
244  file_time = (time_t)tor_parse_ulong(line, 10, 0, ULONG_MAX, &ok, NULL);
245  if (!ok) {
246  log_warn(LD_DIRSERV, "Non-integer time in bandwidth file: %s",
247  escaped(line));
248  goto err;
249  }
250 
251  now = time(NULL);
252  if ((now - file_time) > MAX_MEASUREMENT_AGE) {
253  log_warn(LD_DIRSERV, "Bandwidth measurement file stale. Age: %u",
254  (unsigned)(time(NULL) - file_time));
255  goto err;
256  }
257 
258  /* If timestamp was correct and bw_file_headers is not NULL,
259  * add timestamp to bw_file_headers */
260  if (bw_file_headers)
261  smartlist_add_asprintf(bw_file_headers, "timestamp=%lu",
262  (unsigned long)file_time);
263 
264  if (routerstatuses)
265  smartlist_sort(routerstatuses, compare_vote_routerstatus_entries);
266 
267  while (!feof(fp)) {
268  measured_bw_line_t parsed_line;
269  if (tor_getline(&line, &n, fp) >= 0) {
270  if (measured_bw_line_parse(&parsed_line, line,
271  line_is_after_headers) != -1) {
272  /* This condition will be true when the first complete valid bw line
273  * has been encountered, which means the end of the header lines. */
274  line_is_after_headers = 1;
275  /* Also cache the line for dirserv_get_bandwidth_for_router() */
276  dirserv_cache_measured_bw(&parsed_line, file_time);
277  if (measured_bw_line_apply(&parsed_line, routerstatuses) > 0)
278  applied_lines++;
279  /* if the terminator is found, it is the end of header lines, set the
280  * flag but do not store anything */
281  } else if (strcmp(line, BW_FILE_HEADERS_TERMINATOR) == 0) {
282  line_is_after_headers = 1;
283  /* if the line was not a correct relay line nor the terminator and
284  * the end of the header lines has not been detected yet
285  * and it is key_value and bw_file_headers did not reach the maximum
286  * number of headers,
287  * then assume this line is a header and add it to bw_file_headers */
288  } else if (bw_file_headers &&
289  (line_is_after_headers == 0) &&
291  !strchr(line, ' ') &&
292  (smartlist_len(bw_file_headers)
294  line[strlen(line)-1] = '\0';
295  smartlist_add_strdup(bw_file_headers, line);
296  };
297  }
298  }
299 
300  /* Now would be a nice time to clean the cache, too */
302 
303  log_info(LD_DIRSERV,
304  "Bandwidth measurement file successfully read. "
305  "Applied %d measurements.", applied_lines);
306  rv = 0;
307 
308  err:
309  if (line) {
310  // we need to raw_free this buffer because we got it from tor_getdelim()
311  raw_free(line);
312  }
313  if (fp)
314  fclose(fp);
315  return rv;
316 }
317 
331 STATIC int
332 measured_bw_line_parse(measured_bw_line_t *out, const char *orig_line,
333  int line_is_after_headers)
334 {
335  char *line = tor_strdup(orig_line);
336  char *cp = line;
337  int got_bw = 0;
338  int got_node_id = 0;
339  char *strtok_state; /* lame sauce d'jour */
340 
341  if (strlen(line) == 0) {
342  log_warn(LD_DIRSERV, "Empty line in bandwidth file");
343  tor_free(line);
344  return -1;
345  }
346 
347  /* Remove end of line character, so that is not part of the token */
348  if (line[strlen(line) - 1] == '\n') {
349  line[strlen(line) - 1] = '\0';
350  }
351 
352  cp = tor_strtok_r(cp, " \t", &strtok_state);
353 
354  if (!cp) {
355  log_warn(LD_DIRSERV, "Invalid line in bandwidth file: %s",
356  escaped(orig_line));
357  tor_free(line);
358  return -1;
359  }
360 
361  if (orig_line[strlen(orig_line)-1] != '\n') {
362  log_warn(LD_DIRSERV, "Incomplete line in bandwidth file: %s",
363  escaped(orig_line));
364  tor_free(line);
365  return -1;
366  }
367 
368  do {
369  if (strcmpstart(cp, "bw=") == 0) {
370  int parse_ok = 0;
371  char *endptr;
372  if (got_bw) {
373  log_warn(LD_DIRSERV, "Double bw= in bandwidth file line: %s",
374  escaped(orig_line));
375  tor_free(line);
376  return -1;
377  }
378  cp+=strlen("bw=");
379 
380  out->bw_kb = tor_parse_long(cp, 10, 0, LONG_MAX, &parse_ok, &endptr);
381  if (!parse_ok || (*endptr && !TOR_ISSPACE(*endptr))) {
382  log_warn(LD_DIRSERV, "Invalid bandwidth in bandwidth file line: %s",
383  escaped(orig_line));
384  tor_free(line);
385  return -1;
386  }
387  got_bw=1;
388  } else if (strcmpstart(cp, "node_id=$") == 0) {
389  if (got_node_id) {
390  log_warn(LD_DIRSERV, "Double node_id= in bandwidth file line: %s",
391  escaped(orig_line));
392  tor_free(line);
393  return -1;
394  }
395  cp+=strlen("node_id=$");
396 
397  if (strlen(cp) != HEX_DIGEST_LEN ||
398  base16_decode(out->node_id, DIGEST_LEN,
399  cp, HEX_DIGEST_LEN) != DIGEST_LEN) {
400  log_warn(LD_DIRSERV, "Invalid node_id in bandwidth file line: %s",
401  escaped(orig_line));
402  tor_free(line);
403  return -1;
404  }
405  strlcpy(out->node_hex, cp, sizeof(out->node_hex));
406  got_node_id=1;
407  }
408  } while ((cp = tor_strtok_r(NULL, " \t", &strtok_state)));
409 
410  if (got_bw && got_node_id) {
411  tor_free(line);
412  return 0;
413  } else if (line_is_after_headers == 0) {
414  /* There could be additional header lines, therefore do not give warnings
415  * but returns -1 since it's not a complete bw line. */
416  log_debug(LD_DIRSERV, "Missing bw or node_id in bandwidth file line: %s",
417  escaped(orig_line));
418  tor_free(line);
419  return -1;
420  } else {
421  log_warn(LD_DIRSERV, "Incomplete line in bandwidth file: %s",
422  escaped(orig_line));
423  tor_free(line);
424  return -1;
425  }
426 }
427 
433 STATIC int
434 measured_bw_line_apply(measured_bw_line_t *parsed_line,
435  smartlist_t *routerstatuses)
436 {
437  vote_routerstatus_t *rs = NULL;
438  if (!routerstatuses)
439  return 0;
440 
441  rs = smartlist_bsearch(routerstatuses, parsed_line->node_id,
443 
444  if (rs) {
445  rs->has_measured_bw = 1;
446  rs->measured_bw_kb = (uint32_t)parsed_line->bw_kb;
447  } else {
448  log_info(LD_DIRSERV, "Node ID %s not found in routerstatus list",
449  parsed_line->node_hex);
450  }
451 
452  return rs != NULL;
453 }
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:163
#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:36
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 dirserv_read_measured_bandwidths(const char *from_file, smartlist_t *routerstatuses, smartlist_t *bw_file_headers)
Definition: bwauth.c:206
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:156
static int routers_with_measured_bw
Definition: bwauth.c:30
void dirserv_clear_measured_bw_cache(void)
Definition: bwauth.c:100
uint32_t dirserv_get_credible_bandwidth_kb(const routerinfo_t *ri)
Definition: bwauth.c:174
#define MAX_BW_FILE_HEADER_COUNT_IN_VOTE
Definition: bwauth.h:16
STATIC void dirserv_expire_measured_bw_cache(time_t now)
Definition: bwauth.c:111
tor_assert(buffer)
#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:70
#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:332
char identity_digest[DIGEST_LEN]
int dirserv_get_last_n_measured_bws(void)
Definition: bwauth.c:52
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:135
static digestmap_t * mbw_cache
Definition: bwauth.c:65
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.
#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:434
Definition: bwauth.c:58
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