commit 3adb689fbc28434efa6a3b599163b1d5b6cae3e9 Merge: 41cd05562 4ab2e9a59 Author: teor teor@torproject.org Date: Thu Mar 21 12:04:30 2019 +1000
Merge branch 'ticket29806_034_squashed' into ticket29806_035_squashed_merged
Copy and paste the vote=0 code from the old src/or/dirserv.c to the new src/feature/dirauth/bwauth.c.
changes/ticket29806 | 7 ++++++ src/feature/dirauth/bwauth.c | 8 ++++++- src/test/test_dir.c | 57 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 1 deletion(-)
diff --cc src/feature/dirauth/bwauth.c index a31050ff9,000000000..12f9399e9 mode 100644,000000..100644 --- a/src/feature/dirauth/bwauth.c +++ b/src/feature/dirauth/bwauth.c @@@ -1,453 -1,0 +1,459 @@@ +/* Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2019, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file bwauth.c + * \brief Code to read and apply bandwidth authority data. + **/ + +#define BWAUTH_PRIVATE +#include "core/or/or.h" +#include "feature/dirauth/bwauth.h" + +#include "app/config/config.h" +#include "feature/nodelist/networkstatus.h" +#include "feature/nodelist/routerlist.h" +#include "feature/dirparse/ns_parse.h" + +#include "feature/nodelist/routerinfo_st.h" +#include "feature/nodelist/vote_routerstatus_st.h" + +#include "lib/encoding/keyval.h" + +/** Total number of routers with measured bandwidth; this is set by + * dirserv_count_measured_bs() before the loop in + * dirserv_generate_networkstatus_vote_obj() and checked by + * dirserv_get_credible_bandwidth() and + * dirserv_compute_performance_thresholds() */ +static int routers_with_measured_bw = 0; + +/** Look through the routerlist, and using the measured bandwidth cache count + * how many measured bandwidths we know. This is used to decide whether we + * ever trust advertised bandwidths for purposes of assigning flags. */ +void +dirserv_count_measured_bws(const smartlist_t *routers) +{ + /* Initialize this first */ + routers_with_measured_bw = 0; + + /* Iterate over the routerlist and count measured bandwidths */ + SMARTLIST_FOREACH_BEGIN(routers, const routerinfo_t *, ri) { + /* Check if we know a measured bandwidth for this one */ + if (dirserv_has_measured_bw(ri->cache_info.identity_digest)) { + ++routers_with_measured_bw; + } + } SMARTLIST_FOREACH_END(ri); +} + +/** Return the last-computed result from dirserv_count_mesured_bws(). */ +int +dirserv_get_last_n_measured_bws(void) +{ + return routers_with_measured_bw; +} + +/** Measured bandwidth cache entry */ +typedef struct mbw_cache_entry_s { + long mbw_kb; + time_t as_of; +} mbw_cache_entry_t; + +/** Measured bandwidth cache - keys are identity_digests, values are + * mbw_cache_entry_t *. */ +static digestmap_t *mbw_cache = NULL; + +/** Store a measured bandwidth cache entry when reading the measured + * bandwidths file. */ +STATIC void +dirserv_cache_measured_bw(const measured_bw_line_t *parsed_line, + time_t as_of) +{ + mbw_cache_entry_t *e = NULL; + + tor_assert(parsed_line); + + /* Allocate a cache if we need */ + if (!mbw_cache) mbw_cache = digestmap_new(); + + /* Check if we have an existing entry */ + e = digestmap_get(mbw_cache, parsed_line->node_id); + /* If we do, we can re-use it */ + if (e) { + /* Check that we really are newer, and update */ + if (as_of > e->as_of) { + e->mbw_kb = parsed_line->bw_kb; + e->as_of = as_of; + } + } else { + /* We'll have to insert a new entry */ + e = tor_malloc(sizeof(*e)); + e->mbw_kb = parsed_line->bw_kb; + e->as_of = as_of; + digestmap_set(mbw_cache, parsed_line->node_id, e); + } +} + +/** Clear and free the measured bandwidth cache */ +void +dirserv_clear_measured_bw_cache(void) +{ + if (mbw_cache) { + /* Free the map and all entries */ + digestmap_free(mbw_cache, tor_free_); + mbw_cache = NULL; + } +} + +/** Scan the measured bandwidth cache and remove expired entries */ +STATIC void +dirserv_expire_measured_bw_cache(time_t now) +{ + + if (mbw_cache) { + /* Iterate through the cache and check each entry */ + DIGESTMAP_FOREACH_MODIFY(mbw_cache, k, mbw_cache_entry_t *, e) { + if (now > e->as_of + MAX_MEASUREMENT_AGE) { + tor_free(e); + MAP_DEL_CURRENT(k); + } + } DIGESTMAP_FOREACH_END; + + /* Check if we cleared the whole thing and free if so */ + if (digestmap_size(mbw_cache) == 0) { + digestmap_free(mbw_cache, tor_free_); + mbw_cache = 0; + } + } +} + +/** Query the cache by identity digest, return value indicates whether + * we found it. The bw_out and as_of_out pointers receive the cached + * bandwidth value and the time it was cached if not NULL. */ +int +dirserv_query_measured_bw_cache_kb(const char *node_id, long *bw_kb_out, + time_t *as_of_out) +{ + mbw_cache_entry_t *v = NULL; + int rv = 0; + + if (mbw_cache && node_id) { + v = digestmap_get(mbw_cache, node_id); + if (v) { + /* Found something */ + rv = 1; + if (bw_kb_out) *bw_kb_out = v->mbw_kb; + if (as_of_out) *as_of_out = v->as_of; + } + } + + return rv; +} + +/** Predicate wrapper for dirserv_query_measured_bw_cache() */ +int +dirserv_has_measured_bw(const char *node_id) +{ + return dirserv_query_measured_bw_cache_kb(node_id, NULL, NULL); +} + +/** Get the current size of the measured bandwidth cache */ +int +dirserv_get_measured_bw_cache_size(void) +{ + if (mbw_cache) return digestmap_size(mbw_cache); + else return 0; +} + +/** Return the bandwidth we believe for assigning flags; prefer measured + * over advertised, and if we have above a threshold quantity of measured + * bandwidths, we don't want to ever give flags to unmeasured routers, so + * return 0. */ +uint32_t +dirserv_get_credible_bandwidth_kb(const routerinfo_t *ri) +{ + int threshold; + uint32_t bw_kb = 0; + long mbw_kb; + + tor_assert(ri); + /* Check if we have a measured bandwidth, and check the threshold if not */ + if (!(dirserv_query_measured_bw_cache_kb(ri->cache_info.identity_digest, + &mbw_kb, NULL))) { + threshold = get_options()->MinMeasuredBWsForAuthToIgnoreAdvertised; + if (routers_with_measured_bw > threshold) { + /* Return zero for unmeasured bandwidth if we are above threshold */ + bw_kb = 0; + } else { + /* Return an advertised bandwidth otherwise */ + bw_kb = router_get_advertised_bandwidth_capped(ri) / 1000; + } + } else { + /* We have the measured bandwidth in mbw */ + bw_kb = (uint32_t)mbw_kb; + } + + return bw_kb; +} + +/** + * Read the measured bandwidth list file, apply it to the list of + * vote_routerstatus_t and store all the headers in <b>bw_file_headers</b>. + * Returns -1 on error, 0 otherwise. + */ +int +dirserv_read_measured_bandwidths(const char *from_file, + smartlist_t *routerstatuses, + smartlist_t *bw_file_headers) +{ + FILE *fp = tor_fopen_cloexec(from_file, "r"); + int applied_lines = 0; + time_t file_time, now; + int ok; + /* This flag will be 1 only when the first successful bw measurement line + * has been encountered, so that measured_bw_line_parse don't give warnings + * if there are additional header lines, as introduced in Bandwidth List spec + * version 1.1.0 */ + int line_is_after_headers = 0; + int rv = -1; + char *line = NULL; + size_t n = 0; + + /* Initialise line, so that we can't possibly run off the end. */ + + if (fp == NULL) { + log_warn(LD_CONFIG, "Can't open bandwidth file at configured location: %s", + from_file); + goto err; + } + + /* If fgets fails, line is either unmodified, or indeterminate. */ + if (tor_getline(&line,&n,fp) <= 0) { + log_warn(LD_DIRSERV, "Empty bandwidth file"); + goto err; + } + + if (!strlen(line) || line[strlen(line)-1] != '\n') { + log_warn(LD_DIRSERV, "Long or truncated time in bandwidth file: %s", + escaped(line)); + goto err; + } + + line[strlen(line)-1] = '\0'; + file_time = (time_t)tor_parse_ulong(line, 10, 0, ULONG_MAX, &ok, NULL); + if (!ok) { + log_warn(LD_DIRSERV, "Non-integer time in bandwidth file: %s", + escaped(line)); + goto err; + } + + now = time(NULL); + if ((now - file_time) > MAX_MEASUREMENT_AGE) { + log_warn(LD_DIRSERV, "Bandwidth measurement file stale. Age: %u", + (unsigned)(time(NULL) - file_time)); + goto err; + } + + /* If timestamp was correct and bw_file_headers is not NULL, + * add timestamp to bw_file_headers */ + if (bw_file_headers) + smartlist_add_asprintf(bw_file_headers, "timestamp=%lu", + (unsigned long)file_time); + + if (routerstatuses) + smartlist_sort(routerstatuses, compare_vote_routerstatus_entries); + + while (!feof(fp)) { + measured_bw_line_t parsed_line; + if (tor_getline(&line, &n, fp) >= 0) { + if (measured_bw_line_parse(&parsed_line, line, + line_is_after_headers) != -1) { + /* This condition will be true when the first complete valid bw line + * has been encountered, which means the end of the header lines. */ + line_is_after_headers = 1; + /* Also cache the line for dirserv_get_bandwidth_for_router() */ + dirserv_cache_measured_bw(&parsed_line, file_time); + if (measured_bw_line_apply(&parsed_line, routerstatuses) > 0) + applied_lines++; + /* if the terminator is found, it is the end of header lines, set the + * flag but do not store anything */ + } else if (strcmp(line, BW_FILE_HEADERS_TERMINATOR) == 0) { + line_is_after_headers = 1; + /* if the line was not a correct relay line nor the terminator and + * the end of the header lines has not been detected yet + * and it is key_value and bw_file_headers did not reach the maximum + * number of headers, + * then assume this line is a header and add it to bw_file_headers */ + } else if (bw_file_headers && + (line_is_after_headers == 0) && + string_is_key_value(LOG_DEBUG, line) && + !strchr(line, ' ') && + (smartlist_len(bw_file_headers) + < MAX_BW_FILE_HEADER_COUNT_IN_VOTE)) { + line[strlen(line)-1] = '\0'; + smartlist_add_strdup(bw_file_headers, line); + }; + } + } + + /* Now would be a nice time to clean the cache, too */ + dirserv_expire_measured_bw_cache(now); + + log_info(LD_DIRSERV, + "Bandwidth measurement file successfully read. " + "Applied %d measurements.", applied_lines); + rv = 0; + + err: + if (line) { + // we need to raw_free this buffer because we got it from tor_getdelim() + raw_free(line); + } + if (fp) + fclose(fp); + return rv; +} + +/** + * Helper function to parse out a line in the measured bandwidth file + * into a measured_bw_line_t output structure. + * + * If <b>line_is_after_headers</b> is true, then if we encounter an incomplete + * bw line, return -1 and warn, since we are after the headers and we should + * only parse bw lines. Return 0 otherwise. + * + * If <b>line_is_after_headers</b> is false then it means that we are not past + * the header block yet. If we encounter an incomplete bw line, return -1 but + * don't warn since there could be additional header lines coming. If we + * encounter a proper bw line, return 0 (and we got past the headers). + */ +STATIC int +measured_bw_line_parse(measured_bw_line_t *out, const char *orig_line, + int line_is_after_headers) +{ + char *line = tor_strdup(orig_line); + char *cp = line; + int got_bw = 0; + int got_node_id = 0; + char *strtok_state; /* lame sauce d'jour */ + + if (strlen(line) == 0) { + log_warn(LD_DIRSERV, "Empty line in bandwidth file"); + tor_free(line); + return -1; + } + + /* Remove end of line character, so that is not part of the token */ + if (line[strlen(line) - 1] == '\n') { + line[strlen(line) - 1] = '\0'; + } + + cp = tor_strtok_r(cp, " \t", &strtok_state); + + if (!cp) { + log_warn(LD_DIRSERV, "Invalid line in bandwidth file: %s", + escaped(orig_line)); + tor_free(line); + return -1; + } + + if (orig_line[strlen(orig_line)-1] != '\n') { + log_warn(LD_DIRSERV, "Incomplete line in bandwidth file: %s", + escaped(orig_line)); + tor_free(line); + return -1; + } + + do { - if (strcmpstart(cp, "bw=") == 0) { ++ // If the line contains vote=0, ignore it. ++ if (strcmpstart(cp, "vote=0") == 0) { ++ log_debug(LD_DIRSERV, "Ignoring bandwidth file line that contains " ++ "vote=0: %s",escaped(orig_line)); ++ tor_free(line); ++ return -1; ++ } else if (strcmpstart(cp, "bw=") == 0) { + int parse_ok = 0; + char *endptr; + if (got_bw) { + log_warn(LD_DIRSERV, "Double bw= in bandwidth file line: %s", + escaped(orig_line)); + tor_free(line); + return -1; + } + cp+=strlen("bw="); + + out->bw_kb = tor_parse_long(cp, 10, 0, LONG_MAX, &parse_ok, &endptr); + if (!parse_ok || (*endptr && !TOR_ISSPACE(*endptr))) { + log_warn(LD_DIRSERV, "Invalid bandwidth in bandwidth file line: %s", + escaped(orig_line)); + tor_free(line); + return -1; + } + got_bw=1; + } else if (strcmpstart(cp, "node_id=$") == 0) { + if (got_node_id) { + log_warn(LD_DIRSERV, "Double node_id= in bandwidth file line: %s", + escaped(orig_line)); + tor_free(line); + return -1; + } + cp+=strlen("node_id=$"); + + if (strlen(cp) != HEX_DIGEST_LEN || + base16_decode(out->node_id, DIGEST_LEN, + cp, HEX_DIGEST_LEN) != DIGEST_LEN) { + log_warn(LD_DIRSERV, "Invalid node_id in bandwidth file line: %s", + escaped(orig_line)); + tor_free(line); + return -1; + } + strlcpy(out->node_hex, cp, sizeof(out->node_hex)); + got_node_id=1; + } + } while ((cp = tor_strtok_r(NULL, " \t", &strtok_state))); + + if (got_bw && got_node_id) { + tor_free(line); + return 0; + } else if (line_is_after_headers == 0) { + /* There could be additional header lines, therefore do not give warnings + * but returns -1 since it's not a complete bw line. */ + log_debug(LD_DIRSERV, "Missing bw or node_id in bandwidth file line: %s", + escaped(orig_line)); + tor_free(line); + return -1; + } else { + log_warn(LD_DIRSERV, "Incomplete line in bandwidth file: %s", + escaped(orig_line)); + tor_free(line); + return -1; + } +} + +/** + * Helper function to apply a parsed measurement line to a list + * of bandwidth statuses. Returns true if a line is found, + * false otherwise. + */ +STATIC int +measured_bw_line_apply(measured_bw_line_t *parsed_line, + smartlist_t *routerstatuses) +{ + vote_routerstatus_t *rs = NULL; + if (!routerstatuses) + return 0; + + rs = smartlist_bsearch(routerstatuses, parsed_line->node_id, + compare_digest_to_vote_routerstatus_entry); + + if (rs) { + rs->has_measured_bw = 1; + rs->measured_bw_kb = (uint32_t)parsed_line->bw_kb; + } else { + log_info(LD_DIRSERV, "Node ID %s not found in routerstatus list", + parsed_line->node_hex); + } + + return rs != NULL; +} diff --cc src/test/test_dir.c index 9d163872d,52d3ef159..cbb414ed6 --- a/src/test/test_dir.c +++ b/src/test/test_dir.c @@@ -1708,286 -1677,94 +1727,324 @@@ test_dir_dirserv_read_measured_bandwidt "pid_error=4.11374090719 pid_error_sum=4.11374090719 " "pid_bw=57136645 pid_delta=2.12168374577 circ_fail=0.2 " "scanner=/filepath\n"; - - tor_asprintf(&content, "%ld\n%s", (long)timestamp, torflow_relay_lines); + const char *relay_lines_v110 = + "node_id=$68A483E05A2ABDCA6DA5A3EF8DB5177638A27F80 " + "master_key_ed25519=YaqV4vbvPYKucElk297eVdNArDz9HtIwUoIeo0+cVIpQ " + "bw=760 nick=Test rtt=380 time=2018-05-08T16:13:26\n"; + const char *relay_lines_bad = + "node_id=$68A483E05A2ABDCA6DA5A3EF8DB5177638A\n"; + + tor_asprintf(&header_lines_v100, "%ld\n", (long)timestamp); + tor_asprintf(&header_lines_v110_no_terminator, "%ld\n%s", (long)timestamp, + header_lines_v110_no_terminator_no_timestamp); + tor_asprintf(&header_lines_v110, "%s%s", + header_lines_v110_no_terminator, BW_FILE_HEADERS_TERMINATOR); + + tor_asprintf(&bw_file_headers_str_v100, "timestamp=%ld",(long)timestamp); + tor_asprintf(&bw_file_headers_str_v110, "timestamp=%ld %s", + (long)timestamp, bw_file_headers_str_v110_no_timestamp); + tor_asprintf(&bw_file_headers_str_bad, "%s " + "node_id=$68A483E05A2ABDCA6DA5A3EF8DB5177638A", + bw_file_headers_str_v110); + + for (i=0; i<MAX_BW_FILE_HEADER_COUNT_IN_VOTE; i++) { + strlcat(header_lines_long, "foo=bar\n", + sizeof(header_lines_long)); + } + /* 8 is the number of v110 lines in header_lines_v110 */ + for (i=0; i<MAX_BW_FILE_HEADER_COUNT_IN_VOTE - 8 - 1; i++) { + strlcat(bw_file_headers_str_long, "foo=bar ", + sizeof(bw_file_headers_str_long)); + } + strlcat(bw_file_headers_str_long, "foo=bar", + sizeof(bw_file_headers_str_long)); + tor_asprintf(&bw_file_headers_str_extra, + "%s %s", + bw_file_headers_str_v110, + bw_file_headers_str_long); + + /* Test an empty bandwidth file. bw_file_headers will be empty string */ + write_str_to_file(fname, "", 0); + setup_capture_of_logs(LOG_WARN); + tt_int_op(-1, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL, + bw_file_headers)); + expect_log_msg("Empty bandwidth file\n"); + teardown_capture_of_logs(); + bw_file_headers_str = smartlist_join_strings(bw_file_headers, " ", 0, NULL); + tt_str_op("", OP_EQ, bw_file_headers_str); + SMARTLIST_FOREACH(bw_file_headers, char *, c, tor_free(c)); + smartlist_free(bw_file_headers); + tor_free(bw_file_headers_str); + + /* Test bandwidth file with only timestamp. + * bw_file_headers will be empty string */ + bw_file_headers = smartlist_new(); + tor_asprintf(&content, "%ld", (long)timestamp); write_str_to_file(fname, content, 0); tor_free(content); - tt_int_op(0, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL)); - - /* Test Torflow complete file including v1.1.0 headers */ - const char *v110_header_lines= - "version=1.1.0\n" - "software=sbws\n" - "software_version=0.1.0\n" - "generator_started=2018-05-08T16:13:25\n" - "earliest_bandwidth=2018-05-08T16:13:26\n" - "====\n"; - - tor_asprintf(&content, "%ld\n%s%s", (long)timestamp, v110_header_lines, - torflow_relay_lines); + tt_int_op(-1, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL, + bw_file_headers)); + bw_file_headers_str = smartlist_join_strings(bw_file_headers, " ", 0, NULL); + tt_str_op("", OP_EQ, bw_file_headers_str); + SMARTLIST_FOREACH(bw_file_headers, char *, c, tor_free(c)); + smartlist_free(bw_file_headers); + tor_free(bw_file_headers_str); + + /* Test v1.0.0 bandwidth file headers */ + write_str_to_file(fname, header_lines_v100, 0); + bw_file_headers = smartlist_new(); + tt_int_op(0, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL, + bw_file_headers)); + bw_file_headers_str = smartlist_join_strings(bw_file_headers, " ", 0, NULL); + tt_str_op(bw_file_headers_str_v100, OP_EQ, bw_file_headers_str); + SMARTLIST_FOREACH(bw_file_headers, char *, c, tor_free(c)); + smartlist_free(bw_file_headers); + tor_free(bw_file_headers_str); + + /* Test v1.0.0 complete bandwidth file */ + bw_file_headers = smartlist_new(); + tor_asprintf(&content, "%s%s", header_lines_v100, relay_lines_v100); write_str_to_file(fname, content, 0); tor_free(content); - tt_int_op(0, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL)); - - /* Test Torflow with additional headers afer a correct bw line */ - tor_asprintf(&content, "%ld\n%s%s", (long)timestamp, torflow_relay_lines, - v110_header_lines); + tt_int_op(0, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL, + bw_file_headers)); + bw_file_headers_str = smartlist_join_strings(bw_file_headers, " ", 0, NULL); + tt_str_op(bw_file_headers_str_v100, OP_EQ, bw_file_headers_str); + SMARTLIST_FOREACH(bw_file_headers, char *, c, tor_free(c)); + smartlist_free(bw_file_headers); + tor_free(bw_file_headers_str); + + /* Test v1.0.0 complete bandwidth file with NULL bw_file_headers. */ + tor_asprintf(&content, "%s%s", header_lines_v100, relay_lines_v100); write_str_to_file(fname, content, 0); tor_free(content); - tt_int_op(0, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL)); + tt_int_op(0, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL, NULL));
- /* Test Torflow with additional headers afer a correct bw line and more - * bw lines after the headers. */ - tor_asprintf(&content, "%ld\n%s%s%s", (long)timestamp, torflow_relay_lines, - v110_header_lines, torflow_relay_lines); + /* Test bandwidth file including v1.1.0 bandwidth headers and + * v1.0.0 relay lines. bw_file_headers will contain the v1.1.0 headers. */ + bw_file_headers = smartlist_new(); + tor_asprintf(&content, "%s%s%s", header_lines_v100, header_lines_v110, + relay_lines_v100); write_str_to_file(fname, content, 0); tor_free(content); - tt_int_op(0, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL)); - - /* Test sbws file */ - const char *sbws_relay_lines= - "node_id=$68A483E05A2ABDCA6DA5A3EF8DB5177638A27F80 " - "master_key_ed25519=YaqV4vbvPYKucElk297eVdNArDz9HtIwUoIeo0+cVIpQ " - "bw=760 nick=Test rtt=380 time=2018-05-08T16:13:26\n"; - - tor_asprintf(&content, "%ld\n%s%s", (long)timestamp, v110_header_lines, - sbws_relay_lines); + tt_int_op(0, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL, + bw_file_headers)); + bw_file_headers_str = smartlist_join_strings(bw_file_headers, " ", 0, NULL); + tt_str_op(bw_file_headers_str_v110, OP_EQ, bw_file_headers_str); + SMARTLIST_FOREACH(bw_file_headers, char *, c, tor_free(c)); + smartlist_free(bw_file_headers); + tor_free(bw_file_headers_str); + + /* Test v1.0.0 complete bandwidth file with v1.1.0 headers at the end. + * bw_file_headers will contain only v1.0.0 headers and the additional + * headers will be interpreted as malformed relay lines. */ + bw_file_headers = smartlist_new(); + tor_asprintf(&content, "%s%s%s", header_lines_v100, relay_lines_v100, + header_lines_v110); write_str_to_file(fname, content, 0); tor_free(content); - tt_int_op(0, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL)); + tt_int_op(0, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL, + bw_file_headers)); + bw_file_headers_str = smartlist_join_strings(bw_file_headers, " ", 0, NULL); + tt_str_op(bw_file_headers_str_v100, OP_EQ, bw_file_headers_str); + SMARTLIST_FOREACH(bw_file_headers, char *, c, tor_free(c)); + smartlist_free(bw_file_headers); + tor_free(bw_file_headers_str); + + /* Test v1.0.0 complete bandwidth file, the v1.1.0 headers and more relay + * lines. bw_file_headers will contain only v1.0.0 headers, the additional + * headers will be interpreted as malformed relay lines and the last relay + * lines will be correctly interpreted as relay lines. */ + bw_file_headers = smartlist_new(); + tor_asprintf(&content, "%s%s%s%s", header_lines_v100, relay_lines_v100, + header_lines_v110, relay_lines_v100); + write_str_to_file(fname, content, 0); + tor_free(content); + tt_int_op(0, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL, + bw_file_headers)); + bw_file_headers_str = smartlist_join_strings(bw_file_headers, " ", 0, NULL); + tt_str_op(bw_file_headers_str_v100, OP_EQ, bw_file_headers_str); + SMARTLIST_FOREACH(bw_file_headers, char *, c, tor_free(c)); + smartlist_free(bw_file_headers); + tor_free(bw_file_headers_str); + + /* Test v1.1.0 bandwidth headers without terminator */ + bw_file_headers = smartlist_new(); + write_str_to_file(fname, header_lines_v110_no_terminator, 0); + tt_int_op(0, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL, + bw_file_headers)); + bw_file_headers_str = smartlist_join_strings(bw_file_headers, " ", 0, NULL); + tt_str_op(bw_file_headers_str_v110, OP_EQ, bw_file_headers_str); + SMARTLIST_FOREACH(bw_file_headers, char *, c, tor_free(c)); + smartlist_free(bw_file_headers); + tor_free(bw_file_headers_str); + + /* Test v1.1.0 bandwidth headers with terminator */ + bw_file_headers = smartlist_new(); + write_str_to_file(fname, header_lines_v110, 0); + tt_int_op(0, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL, + bw_file_headers)); + bw_file_headers_str = smartlist_join_strings(bw_file_headers, " ", 0, NULL); + tt_str_op(bw_file_headers_str_v110, OP_EQ, bw_file_headers_str); + SMARTLIST_FOREACH(bw_file_headers, char *, c, tor_free(c)); + smartlist_free(bw_file_headers); + tor_free(bw_file_headers_str); + + /* Test v1.1.0 bandwidth file without terminator, then relay lines. + * bw_file_headers will contain the v1.1.0 headers. */ + bw_file_headers = smartlist_new(); + tor_asprintf(&content, "%s%s", + header_lines_v110_no_terminator, relay_lines_v110); + write_str_to_file(fname, content, 0); + tor_free(content); + tt_int_op(0, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL, + bw_file_headers)); + bw_file_headers_str = smartlist_join_strings(bw_file_headers, " ", 0, NULL); + tt_str_op(bw_file_headers_str_v110, OP_EQ, bw_file_headers_str); + SMARTLIST_FOREACH(bw_file_headers, char *, c, tor_free(c)); + smartlist_free(bw_file_headers); + tor_free(bw_file_headers_str); + + /* Test v1.1.0 bandwidth headers with terminator, then relay lines + * bw_file_headers will contain the v1.1.0 headers. */ + bw_file_headers = smartlist_new(); + tor_asprintf(&content, "%s%s", + header_lines_v110, relay_lines_v110); + write_str_to_file(fname, content, 0); + tor_free(content); + tt_int_op(0, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL, + bw_file_headers)); + bw_file_headers_str = smartlist_join_strings(bw_file_headers, " ", 0, NULL); + tt_str_op(bw_file_headers_str_v110, OP_EQ, bw_file_headers_str); + SMARTLIST_FOREACH(bw_file_headers, char *, c, tor_free(c)); + smartlist_free(bw_file_headers); + tor_free(bw_file_headers_str); + + /* Test v1.1.0 bandwidth headers with terminator, then bad relay lines, + * then terminator, then relay_lines_bad. + * bw_file_headers will contain the v1.1.0 headers. */ + bw_file_headers = smartlist_new(); + tor_asprintf(&content, "%s%s%s%s", header_lines_v110, relay_lines_bad, + BW_FILE_HEADERS_TERMINATOR, relay_lines_bad); + write_str_to_file(fname, content, 0); + tor_free(content); + tt_int_op(0, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL, + bw_file_headers)); + bw_file_headers_str = smartlist_join_strings(bw_file_headers, " ", 0, NULL); + tt_str_op(bw_file_headers_str_v110, OP_EQ, bw_file_headers_str); + SMARTLIST_FOREACH(bw_file_headers, char *, c, tor_free(c)); + smartlist_free(bw_file_headers); + tor_free(bw_file_headers_str); + + /* Test v1.1.0 bandwidth headers without terminator, then bad relay lines, + * then relay lines. bw_file_headers will contain the v1.1.0 headers and + * the bad relay lines. */ + bw_file_headers = smartlist_new(); + tor_asprintf(&content, "%s%s%s", + header_lines_v110_no_terminator, relay_lines_bad, + relay_lines_v110); + write_str_to_file(fname, content, 0); + tor_free(content); + tt_int_op(0, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL, + bw_file_headers)); + bw_file_headers_str = smartlist_join_strings(bw_file_headers, " ", 0, NULL); + tt_str_op(bw_file_headers_str_bad, OP_EQ, bw_file_headers_str); + SMARTLIST_FOREACH(bw_file_headers, char *, c, tor_free(c)); + smartlist_free(bw_file_headers); + tor_free(bw_file_headers_str); + + /* Test v1.1.0 bandwidth headers without terminator, + * then many bad relay lines, then relay lines. + * bw_file_headers will contain the v1.1.0 headers and the bad relay lines + * to a maximum of MAX_BW_FILE_HEADER_COUNT_IN_VOTE header lines. */ + bw_file_headers = smartlist_new(); + tor_asprintf(&content, "%s%s%s", + header_lines_v110_no_terminator, header_lines_long, + relay_lines_v110); + write_str_to_file(fname, content, 0); + tor_free(content); + tt_int_op(0, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL, + bw_file_headers)); + tt_int_op(MAX_BW_FILE_HEADER_COUNT_IN_VOTE, OP_EQ, + smartlist_len(bw_file_headers)); + bw_file_headers_str = smartlist_join_strings(bw_file_headers, " ", 0, NULL); + tt_str_op(bw_file_headers_str_extra, OP_EQ, bw_file_headers_str); + SMARTLIST_FOREACH(bw_file_headers, char *, c, tor_free(c)); + smartlist_free(bw_file_headers); + tor_free(bw_file_headers_str); + + /* Test v1.1.0 bandwidth headers without terminator, + * then many bad relay lines, then relay lines. + * bw_file_headers will contain the v1.1.0 headers and the bad relay lines. + * Force bw_file_headers to have more than MAX_BW_FILE_HEADER_COUNT_IN_VOTE + * This test is needed while there is not dirvote test. */ + bw_file_headers = smartlist_new(); + tor_asprintf(&content, "%s%s%s", + header_lines_v110_no_terminator, header_lines_long, + relay_lines_v110); + write_str_to_file(fname, content, 0); + tor_free(content); + tt_int_op(0, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL, + bw_file_headers)); + tt_int_op(MAX_BW_FILE_HEADER_COUNT_IN_VOTE, OP_EQ, + smartlist_len(bw_file_headers)); + /* force bw_file_headers to be bigger than + * MAX_BW_FILE_HEADER_COUNT_IN_VOTE */ + char line[8] = "foo=bar\0"; + smartlist_add_strdup(bw_file_headers, line); + tt_int_op(MAX_BW_FILE_HEADER_COUNT_IN_VOTE, OP_LT, + smartlist_len(bw_file_headers)); + SMARTLIST_FOREACH(bw_file_headers, char *, c, tor_free(c)); + smartlist_free(bw_file_headers); + tor_free(bw_file_headers_str);
+ /* Test v1.x.x bandwidth line with vote=0. + * It will be ignored it and logged it at debug level. */ + const char *relay_lines_ignore = + "node_id=$68A483E05A2ABDCA6DA5A3EF8DB5177638A27F80 bw=1024 vote=0\n" + "node_id=$68A483E05A2ABDCA6DA5A3EF8DB5177638A27F80 bw=1024 vote=0" + "unmeasured=1\n" + "node_id=$68A483E05A2ABDCA6DA5A3EF8DB5177638A27F80 bw=1024 vote=0" + "unmeasured=0\n"; + + /* Create the bandwidth file */ + tor_asprintf(&content, "%ld\n%s", (long)timestamp, relay_lines_ignore); + write_str_to_file(fname, content, 0); + tor_free(content); + + /* Read the bandwidth file */ + setup_full_capture_of_logs(LOG_DEBUG); + tt_int_op(0, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL)); + expect_log_msg_containing("Ignoring bandwidth file line"); + teardown_capture_of_logs(); + + /* Test v1.x.x bandwidth line with "vote=1" or "unmeasured=1" or + * "unmeasured=0". + * They will not be ignored. */ + /* Create the bandwidth file */ + const char *relay_lines_vote = + "node_id=$68A483E05A2ABDCA6DA5A3EF8DB5177638A27F80 bw=1024 vote=1\n" + "node_id=$68A483E05A2ABDCA6DA5A3EF8DB5177638A27F80 bw=1024 unmeasured=0\n" + "node_id=$68A483E05A2ABDCA6DA5A3EF8DB5177638A27F80 bw=1024 unmeasured=1\n"; + tor_asprintf(&content, "%ld\n%s", (long)timestamp, relay_lines_vote); + write_str_to_file(fname, content, 0); + tor_free(content); + + /* Read the bandwidth file */ + setup_full_capture_of_logs(LOG_DEBUG); + tt_int_op(0, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL)); + expect_log_msg_not_containing("Ignoring bandwidth file line"); + teardown_capture_of_logs(); + done: tor_free(fname); + tor_free(header_lines_v100); + tor_free(header_lines_v110_no_terminator); + tor_free(header_lines_v110); + tor_free(bw_file_headers_str_v100); + tor_free(bw_file_headers_str_v110); + tor_free(bw_file_headers_str_bad); + tor_free(bw_file_headers_str_extra); }
#define MBWC_INIT_TIME 1000