bwauth.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500
  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. /**
  6. * \file bwauth.c
  7. * \brief Code to read and apply bandwidth authority data.
  8. **/
  9. #define BWAUTH_PRIVATE
  10. #include "core/or/or.h"
  11. #include "feature/dirauth/bwauth.h"
  12. #include "app/config/config.h"
  13. #include "feature/nodelist/networkstatus.h"
  14. #include "feature/nodelist/routerlist.h"
  15. #include "feature/dirparse/ns_parse.h"
  16. #include "feature/nodelist/routerinfo_st.h"
  17. #include "feature/nodelist/vote_routerstatus_st.h"
  18. #include "lib/crypt_ops/crypto_format.h"
  19. #include "lib/encoding/keyval.h"
  20. /** Total number of routers with measured bandwidth; this is set by
  21. * dirserv_count_measured_bs() before the loop in
  22. * dirserv_generate_networkstatus_vote_obj() and checked by
  23. * dirserv_get_credible_bandwidth() and
  24. * dirserv_compute_performance_thresholds() */
  25. static int routers_with_measured_bw = 0;
  26. /** Look through the routerlist, and using the measured bandwidth cache count
  27. * how many measured bandwidths we know. This is used to decide whether we
  28. * ever trust advertised bandwidths for purposes of assigning flags. */
  29. void
  30. dirserv_count_measured_bws(const smartlist_t *routers)
  31. {
  32. /* Initialize this first */
  33. routers_with_measured_bw = 0;
  34. /* Iterate over the routerlist and count measured bandwidths */
  35. SMARTLIST_FOREACH_BEGIN(routers, const routerinfo_t *, ri) {
  36. /* Check if we know a measured bandwidth for this one */
  37. if (dirserv_has_measured_bw(ri->cache_info.identity_digest)) {
  38. ++routers_with_measured_bw;
  39. }
  40. } SMARTLIST_FOREACH_END(ri);
  41. }
  42. /** Return the last-computed result from dirserv_count_mesured_bws(). */
  43. int
  44. dirserv_get_last_n_measured_bws(void)
  45. {
  46. return routers_with_measured_bw;
  47. }
  48. /** Measured bandwidth cache entry */
  49. typedef struct mbw_cache_entry_s {
  50. long mbw_kb;
  51. time_t as_of;
  52. } mbw_cache_entry_t;
  53. /** Measured bandwidth cache - keys are identity_digests, values are
  54. * mbw_cache_entry_t *. */
  55. static digestmap_t *mbw_cache = NULL;
  56. /** Store a measured bandwidth cache entry when reading the measured
  57. * bandwidths file. */
  58. STATIC void
  59. dirserv_cache_measured_bw(const measured_bw_line_t *parsed_line,
  60. time_t as_of)
  61. {
  62. mbw_cache_entry_t *e = NULL;
  63. tor_assert(parsed_line);
  64. /* Allocate a cache if we need */
  65. if (!mbw_cache) mbw_cache = digestmap_new();
  66. /* Check if we have an existing entry */
  67. e = digestmap_get(mbw_cache, parsed_line->node_id);
  68. /* If we do, we can re-use it */
  69. if (e) {
  70. /* Check that we really are newer, and update */
  71. if (as_of > e->as_of) {
  72. e->mbw_kb = parsed_line->bw_kb;
  73. e->as_of = as_of;
  74. }
  75. } else {
  76. /* We'll have to insert a new entry */
  77. e = tor_malloc(sizeof(*e));
  78. e->mbw_kb = parsed_line->bw_kb;
  79. e->as_of = as_of;
  80. digestmap_set(mbw_cache, parsed_line->node_id, e);
  81. }
  82. }
  83. /** Clear and free the measured bandwidth cache */
  84. void
  85. dirserv_clear_measured_bw_cache(void)
  86. {
  87. if (mbw_cache) {
  88. /* Free the map and all entries */
  89. digestmap_free(mbw_cache, tor_free_);
  90. mbw_cache = NULL;
  91. }
  92. }
  93. /** Scan the measured bandwidth cache and remove expired entries */
  94. STATIC void
  95. dirserv_expire_measured_bw_cache(time_t now)
  96. {
  97. if (mbw_cache) {
  98. /* Iterate through the cache and check each entry */
  99. DIGESTMAP_FOREACH_MODIFY(mbw_cache, k, mbw_cache_entry_t *, e) {
  100. if (now > e->as_of + MAX_MEASUREMENT_AGE) {
  101. tor_free(e);
  102. MAP_DEL_CURRENT(k);
  103. }
  104. } DIGESTMAP_FOREACH_END;
  105. /* Check if we cleared the whole thing and free if so */
  106. if (digestmap_size(mbw_cache) == 0) {
  107. digestmap_free(mbw_cache, tor_free_);
  108. mbw_cache = 0;
  109. }
  110. }
  111. }
  112. /** Query the cache by identity digest, return value indicates whether
  113. * we found it. The bw_out and as_of_out pointers receive the cached
  114. * bandwidth value and the time it was cached if not NULL. */
  115. int
  116. dirserv_query_measured_bw_cache_kb(const char *node_id, long *bw_kb_out,
  117. time_t *as_of_out)
  118. {
  119. mbw_cache_entry_t *v = NULL;
  120. int rv = 0;
  121. if (mbw_cache && node_id) {
  122. v = digestmap_get(mbw_cache, node_id);
  123. if (v) {
  124. /* Found something */
  125. rv = 1;
  126. if (bw_kb_out) *bw_kb_out = v->mbw_kb;
  127. if (as_of_out) *as_of_out = v->as_of;
  128. }
  129. }
  130. return rv;
  131. }
  132. /** Predicate wrapper for dirserv_query_measured_bw_cache() */
  133. int
  134. dirserv_has_measured_bw(const char *node_id)
  135. {
  136. return dirserv_query_measured_bw_cache_kb(node_id, NULL, NULL);
  137. }
  138. /** Get the current size of the measured bandwidth cache */
  139. int
  140. dirserv_get_measured_bw_cache_size(void)
  141. {
  142. if (mbw_cache) return digestmap_size(mbw_cache);
  143. else return 0;
  144. }
  145. /** Return the bandwidth we believe for assigning flags; prefer measured
  146. * over advertised, and if we have above a threshold quantity of measured
  147. * bandwidths, we don't want to ever give flags to unmeasured routers, so
  148. * return 0. */
  149. uint32_t
  150. dirserv_get_credible_bandwidth_kb(const routerinfo_t *ri)
  151. {
  152. int threshold;
  153. uint32_t bw_kb = 0;
  154. long mbw_kb;
  155. tor_assert(ri);
  156. /* Check if we have a measured bandwidth, and check the threshold if not */
  157. if (!(dirserv_query_measured_bw_cache_kb(ri->cache_info.identity_digest,
  158. &mbw_kb, NULL))) {
  159. threshold = get_options()->MinMeasuredBWsForAuthToIgnoreAdvertised;
  160. if (routers_with_measured_bw > threshold) {
  161. /* Return zero for unmeasured bandwidth if we are above threshold */
  162. bw_kb = 0;
  163. } else {
  164. /* Return an advertised bandwidth otherwise */
  165. bw_kb = router_get_advertised_bandwidth_capped(ri) / 1000;
  166. }
  167. } else {
  168. /* We have the measured bandwidth in mbw */
  169. bw_kb = (uint32_t)mbw_kb;
  170. }
  171. return bw_kb;
  172. }
  173. /**
  174. * Read the measured bandwidth list <b>from_file</b>:
  175. * - store all the headers in <b>bw_file_headers</b>,
  176. * - apply bandwidth lines to the list of vote_routerstatus_t in
  177. * <b>routerstatuses</b>,
  178. * - cache bandwidth lines for dirserv_get_bandwidth_for_router(),
  179. * - expire old entries in the measured bandwidth cache, and
  180. * - store the DIGEST_SHA256 of the contents of the file in <b>digest_out</b>.
  181. *
  182. * Returns -1 on error, 0 otherwise.
  183. *
  184. * If the file can't be read, or is empty:
  185. * - <b>bw_file_headers</b> is empty,
  186. * - <b>routerstatuses</b> is not modified,
  187. * - the measured bandwidth cache is not modified, and
  188. * - <b>digest_out</b> is the zero-byte digest.
  189. *
  190. * Otherwise, if there is an error later in the file:
  191. * - <b>bw_file_headers</b> contains all the headers up to the error,
  192. * - <b>routerstatuses</b> is updated with all the relay lines up to the error,
  193. * - the measured bandwidth cache is updated with all the relay lines up to
  194. * the error,
  195. * - if the timestamp is valid and recent, old entries in the measured
  196. * bandwidth cache are expired, and
  197. * - <b>digest_out</b> is the digest up to the first read error (if any).
  198. * The digest is taken over all the readable file contents, even if the
  199. * file is outdated or unparseable.
  200. */
  201. int
  202. dirserv_read_measured_bandwidths(const char *from_file,
  203. smartlist_t *routerstatuses,
  204. smartlist_t *bw_file_headers,
  205. uint8_t *digest_out)
  206. {
  207. FILE *fp = tor_fopen_cloexec(from_file, "r");
  208. int applied_lines = 0;
  209. time_t file_time, now;
  210. int ok;
  211. /* This flag will be 1 only when the first successful bw measurement line
  212. * has been encountered, so that measured_bw_line_parse don't give warnings
  213. * if there are additional header lines, as introduced in Bandwidth List spec
  214. * version 1.1.0 */
  215. int line_is_after_headers = 0;
  216. int rv = -1;
  217. char *line = NULL;
  218. size_t n = 0;
  219. crypto_digest_t *digest = crypto_digest256_new(DIGEST_SHA256);
  220. if (fp == NULL) {
  221. log_warn(LD_CONFIG, "Can't open bandwidth file at configured location: %s",
  222. from_file);
  223. goto err;
  224. }
  225. if (tor_getline(&line,&n,fp) <= 0) {
  226. log_warn(LD_DIRSERV, "Empty bandwidth file");
  227. goto err;
  228. }
  229. /* If the line could be gotten, add it to the digest */
  230. crypto_digest_add_bytes(digest, (const char *) line, strlen(line));
  231. if (!strlen(line) || line[strlen(line)-1] != '\n') {
  232. log_warn(LD_DIRSERV, "Long or truncated time in bandwidth file: %s",
  233. escaped(line));
  234. /* Continue adding lines to the digest. */
  235. goto continue_digest;
  236. }
  237. line[strlen(line)-1] = '\0';
  238. file_time = (time_t)tor_parse_ulong(line, 10, 0, ULONG_MAX, &ok, NULL);
  239. if (!ok) {
  240. log_warn(LD_DIRSERV, "Non-integer time in bandwidth file: %s",
  241. escaped(line));
  242. goto continue_digest;
  243. }
  244. now = approx_time();
  245. if ((now - file_time) > MAX_MEASUREMENT_AGE) {
  246. log_warn(LD_DIRSERV, "Bandwidth measurement file stale. Age: %u",
  247. (unsigned)(time(NULL) - file_time));
  248. goto continue_digest;
  249. }
  250. /* If timestamp was correct and bw_file_headers is not NULL,
  251. * add timestamp to bw_file_headers */
  252. if (bw_file_headers)
  253. smartlist_add_asprintf(bw_file_headers, "timestamp=%lu",
  254. (unsigned long)file_time);
  255. if (routerstatuses)
  256. smartlist_sort(routerstatuses, compare_vote_routerstatus_entries);
  257. while (!feof(fp)) {
  258. measured_bw_line_t parsed_line;
  259. if (tor_getline(&line, &n, fp) >= 0) {
  260. crypto_digest_add_bytes(digest, (const char *) line, strlen(line));
  261. if (measured_bw_line_parse(&parsed_line, line,
  262. line_is_after_headers) != -1) {
  263. /* This condition will be true when the first complete valid bw line
  264. * has been encountered, which means the end of the header lines. */
  265. line_is_after_headers = 1;
  266. /* Also cache the line for dirserv_get_bandwidth_for_router() */
  267. dirserv_cache_measured_bw(&parsed_line, file_time);
  268. if (measured_bw_line_apply(&parsed_line, routerstatuses) > 0)
  269. applied_lines++;
  270. /* if the terminator is found, it is the end of header lines, set the
  271. * flag but do not store anything */
  272. } else if (strcmp(line, BW_FILE_HEADERS_TERMINATOR) == 0) {
  273. line_is_after_headers = 1;
  274. /* if the line was not a correct relay line nor the terminator and
  275. * the end of the header lines has not been detected yet
  276. * and it is key_value and bw_file_headers did not reach the maximum
  277. * number of headers,
  278. * then assume this line is a header and add it to bw_file_headers */
  279. } else if (bw_file_headers &&
  280. (line_is_after_headers == 0) &&
  281. string_is_key_value(LOG_DEBUG, line) &&
  282. !strchr(line, ' ') &&
  283. (smartlist_len(bw_file_headers)
  284. < MAX_BW_FILE_HEADER_COUNT_IN_VOTE)) {
  285. line[strlen(line)-1] = '\0';
  286. smartlist_add_strdup(bw_file_headers, line);
  287. };
  288. }
  289. }
  290. /* Now would be a nice time to clean the cache, too */
  291. dirserv_expire_measured_bw_cache(now);
  292. log_info(LD_DIRSERV,
  293. "Bandwidth measurement file successfully read. "
  294. "Applied %d measurements.", applied_lines);
  295. rv = 0;
  296. continue_digest:
  297. /* Continue parsing lines to return the digest of the Bandwidth File. */
  298. while (!feof(fp)) {
  299. if (tor_getline(&line, &n, fp) >= 0) {
  300. crypto_digest_add_bytes(digest, (const char *) line, strlen(line));
  301. }
  302. }
  303. err:
  304. if (line) {
  305. // we need to raw_free this buffer because we got it from tor_getdelim()
  306. raw_free(line);
  307. }
  308. if (fp)
  309. fclose(fp);
  310. if (digest_out)
  311. crypto_digest_get_digest(digest, (char *) digest_out, DIGEST256_LEN);
  312. crypto_digest_free(digest);
  313. return rv;
  314. }
  315. /**
  316. * Helper function to parse out a line in the measured bandwidth file
  317. * into a measured_bw_line_t output structure.
  318. *
  319. * If <b>line_is_after_headers</b> is true, then if we encounter an incomplete
  320. * bw line, return -1 and warn, since we are after the headers and we should
  321. * only parse bw lines. Return 0 otherwise.
  322. *
  323. * If <b>line_is_after_headers</b> is false then it means that we are not past
  324. * the header block yet. If we encounter an incomplete bw line, return -1 but
  325. * don't warn since there could be additional header lines coming. If we
  326. * encounter a proper bw line, return 0 (and we got past the headers).
  327. *
  328. * If the line contains "vote=0", stop parsing it, and return -1, so that the
  329. * line is ignored during voting.
  330. */
  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. if (strlen(line) == 0) {
  341. log_warn(LD_DIRSERV, "Empty line in bandwidth file");
  342. tor_free(line);
  343. return -1;
  344. }
  345. /* Remove end of line character, so that is not part of the token */
  346. if (line[strlen(line) - 1] == '\n') {
  347. line[strlen(line) - 1] = '\0';
  348. }
  349. cp = tor_strtok_r(cp, " \t", &strtok_state);
  350. if (!cp) {
  351. log_warn(LD_DIRSERV, "Invalid line in bandwidth file: %s",
  352. escaped(orig_line));
  353. tor_free(line);
  354. return -1;
  355. }
  356. if (orig_line[strlen(orig_line)-1] != '\n') {
  357. log_warn(LD_DIRSERV, "Incomplete line in bandwidth file: %s",
  358. escaped(orig_line));
  359. tor_free(line);
  360. return -1;
  361. }
  362. do {
  363. // If the line contains vote=0, ignore it.
  364. if (strcmpstart(cp, "vote=0") == 0) {
  365. log_debug(LD_DIRSERV, "Ignoring bandwidth file line that contains "
  366. "vote=0: %s",escaped(orig_line));
  367. tor_free(line);
  368. return -1;
  369. } else 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. out->bw_kb = tor_parse_long(cp, 10, 0, LONG_MAX, &parse_ok, &endptr);
  380. if (!parse_ok || (*endptr && !TOR_ISSPACE(*endptr))) {
  381. log_warn(LD_DIRSERV, "Invalid bandwidth in bandwidth file line: %s",
  382. escaped(orig_line));
  383. tor_free(line);
  384. return -1;
  385. }
  386. got_bw=1;
  387. } else if (strcmpstart(cp, "node_id=$") == 0) {
  388. if (got_node_id) {
  389. log_warn(LD_DIRSERV, "Double node_id= in bandwidth file line: %s",
  390. escaped(orig_line));
  391. tor_free(line);
  392. return -1;
  393. }
  394. cp+=strlen("node_id=$");
  395. if (strlen(cp) != HEX_DIGEST_LEN ||
  396. base16_decode(out->node_id, DIGEST_LEN,
  397. cp, HEX_DIGEST_LEN) != DIGEST_LEN) {
  398. log_warn(LD_DIRSERV, "Invalid node_id in bandwidth file line: %s",
  399. escaped(orig_line));
  400. tor_free(line);
  401. return -1;
  402. }
  403. strlcpy(out->node_hex, cp, sizeof(out->node_hex));
  404. got_node_id=1;
  405. }
  406. } while ((cp = tor_strtok_r(NULL, " \t", &strtok_state)));
  407. if (got_bw && got_node_id) {
  408. tor_free(line);
  409. return 0;
  410. } else if (line_is_after_headers == 0) {
  411. /* There could be additional header lines, therefore do not give warnings
  412. * but returns -1 since it's not a complete bw line. */
  413. log_debug(LD_DIRSERV, "Missing bw or node_id in bandwidth file line: %s",
  414. escaped(orig_line));
  415. tor_free(line);
  416. return -1;
  417. } else {
  418. log_warn(LD_DIRSERV, "Incomplete line in bandwidth file: %s",
  419. escaped(orig_line));
  420. tor_free(line);
  421. return -1;
  422. }
  423. }
  424. /**
  425. * Helper function to apply a parsed measurement line to a list
  426. * of bandwidth statuses. Returns true if a line is found,
  427. * false otherwise.
  428. */
  429. STATIC int
  430. measured_bw_line_apply(measured_bw_line_t *parsed_line,
  431. smartlist_t *routerstatuses)
  432. {
  433. vote_routerstatus_t *rs = NULL;
  434. if (!routerstatuses)
  435. return 0;
  436. rs = smartlist_bsearch(routerstatuses, parsed_line->node_id,
  437. compare_digest_to_vote_routerstatus_entry);
  438. if (rs) {
  439. rs->has_measured_bw = 1;
  440. rs->measured_bw_kb = (uint32_t)parsed_line->bw_kb;
  441. } else {
  442. log_info(LD_DIRSERV, "Node ID %s not found in routerstatus list",
  443. parsed_line->node_hex);
  444. }
  445. return rs != NULL;
  446. }