From 4c45b3d8455ecc14daeaacc02330f60603925d95 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Tue, 19 Feb 2013 06:20:29 -0800 Subject: [PATCH] Add unit test for unmeasured bandwidth clipping in consensus --- src/or/dirvote.c | 2 - src/or/dirvote.h | 4 + src/test/test_dir.c | 390 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 394 insertions(+), 2 deletions(-) diff --git a/src/or/dirvote.c b/src/or/dirvote.c index 96fc8a534f..a3fb03a478 100644 --- a/src/or/dirvote.c +++ b/src/or/dirvote.c @@ -1388,7 +1388,6 @@ networkstatus_compute_consensus(smartlist_t *votes, char *client_versions = NULL, *server_versions = NULL; smartlist_t *flags; const char *flavor_name; -#define DEFAULT_MAX_UNMEASURED_BW 20 uint32_t max_unmeasured_bw = DEFAULT_MAX_UNMEASURED_BW; int64_t G=0, M=0, E=0, D=0, T=0; /* For bandwidth weights */ const routerstatus_format_type_t rs_format = @@ -1612,7 +1611,6 @@ networkstatus_compute_consensus(smartlist_t *votes, } } - /* Add the actual router entries. */ { int *index; /* index[j] is the current index into votes[j]. */ diff --git a/src/or/dirvote.h b/src/or/dirvote.h index a7398743b0..e39526596c 100644 --- a/src/or/dirvote.h +++ b/src/or/dirvote.h @@ -56,6 +56,10 @@ * Unmeasured=1 flag for unmeasured bandwidths */ #define MIN_METHOD_TO_CLIP_UNMEASURED_BW 17 +/** Default bandwidth to clip unmeasured bandwidths to using method >= + * MIN_METHOD_TO_CLIP_UNMEASURED_BW */ +#define DEFAULT_MAX_UNMEASURED_BW 20 + void dirvote_free_all(void); /* vote manipulation */ diff --git a/src/test/test_dir.c b/src/test/test_dir.c index ec89606a0f..269da22fe8 100644 --- a/src/test/test_dir.c +++ b/src/test/test_dir.c @@ -746,6 +746,17 @@ generate_ri_from_rs(const vote_routerstatus_t *vrs) strlen(r->cache_info.signed_descriptor_body); r->exit_policy = smartlist_new(); r->cache_info.published_on = ++published + time(NULL); + if (rs->has_bandwidth) { + /* + * Multiply by 1000 because the routerinfo_t and the routerstatus_t + * seem to use different units (*sigh*) and because we seem stuck on + * icky and perverse decimal kilobytes (*double sigh*) - see + * router_get_advertised_bandwidth_capped() of routerlist.c and + * routerstatus_format_entry() of dirserv.c. + */ + r->bandwidthrate = rs->bandwidth * 1000; + r->bandwidthcapacity = rs->bandwidth * 1000; + } return r; } @@ -1684,6 +1695,384 @@ test_dir_random_weighted(void *testdata) ; } +/* Function pointers for test_dir_clip_unmeasured_bw() */ + +/** + * Generate a routerstatus for clip_unmeasured_bw test; based on the + * v3_networkstatus ones. + */ +static vote_routerstatus_t * +gen_routerstatus_for_umbw(int idx, time_t now) +{ + vote_routerstatus_t *vrs; + routerstatus_t *rs; + tor_addr_t addr_ipv6; + + switch (idx) { + case 0: + /* Generate the first routerstatus. */ + vrs = tor_malloc_zero(sizeof(vote_routerstatus_t)); + rs = &vrs->status; + vrs->version = tor_strdup("0.1.2.14"); + rs->published_on = now-1500; + strlcpy(rs->nickname, "router2", sizeof(rs->nickname)); + memset(rs->identity_digest, 3, DIGEST_LEN); + memset(rs->descriptor_digest, 78, DIGEST_LEN); + rs->addr = 0x99008801; + rs->or_port = 443; + rs->dir_port = 8000; + /* all flags but running cleared */ + rs->is_flagged_running = 1; + /* + * This one has measured bandwidth below the clip cutoff, and + * so shouldn't be clipped; we'll have to test that it isn't + * later. + */ + rs->has_measured_bw = 1; + rs->has_bandwidth = 1; + rs->measured_bw = rs->bandwidth = DEFAULT_MAX_UNMEASURED_BW / 2; + break; + case 1: + /* Generate the second routerstatus. */ + vrs = tor_malloc_zero(sizeof(vote_routerstatus_t)); + rs = &vrs->status; + vrs->version = tor_strdup("0.2.0.5"); + rs->published_on = now-1000; + strlcpy(rs->nickname, "router1", sizeof(rs->nickname)); + memset(rs->identity_digest, 5, DIGEST_LEN); + memset(rs->descriptor_digest, 77, DIGEST_LEN); + rs->addr = 0x99009901; + rs->or_port = 443; + rs->dir_port = 0; + tor_addr_parse(&addr_ipv6, "[1:2:3::4]"); + tor_addr_copy(&rs->ipv6_addr, &addr_ipv6); + rs->ipv6_orport = 4711; + rs->is_exit = rs->is_stable = rs->is_fast = rs->is_flagged_running = + rs->is_valid = rs->is_v2_dir = rs->is_possible_guard = 1; + /* + * This one has measured bandwidth above the clip cutoff, and + * so shouldn't be clipped; we'll have to test that it isn't + * later. + */ + rs->has_measured_bw = 1; + rs->has_bandwidth = 1; + rs->measured_bw = rs->bandwidth = 2 * DEFAULT_MAX_UNMEASURED_BW; + break; + case 2: + /* Generate the third routerstatus. */ + vrs = tor_malloc_zero(sizeof(vote_routerstatus_t)); + rs = &vrs->status; + vrs->version = tor_strdup("0.1.0.3"); + rs->published_on = now-1000; + strlcpy(rs->nickname, "router3", sizeof(rs->nickname)); + memset(rs->identity_digest, 0x33, DIGEST_LEN); + memset(rs->descriptor_digest, 79, DIGEST_LEN); + rs->addr = 0xAA009901; + rs->or_port = 400; + rs->dir_port = 9999; + rs->is_authority = rs->is_exit = rs->is_stable = rs->is_fast = + rs->is_flagged_running = rs->is_valid = rs->is_v2_dir = + rs->is_possible_guard = 1; + /* + * This one has unmeasured bandwidth above the clip cutoff, and + * so should be clipped; we'll have to test that it isn't + * later. + */ + rs->has_measured_bw = 0; + rs->has_bandwidth = 1; + rs->measured_bw = 0; + rs->bandwidth = 2 * DEFAULT_MAX_UNMEASURED_BW; + break; + case 3: + /* Generate a fourth routerstatus that is not running. */ + vrs = tor_malloc_zero(sizeof(vote_routerstatus_t)); + rs = &vrs->status; + vrs->version = tor_strdup("0.1.6.3"); + rs->published_on = now-1000; + strlcpy(rs->nickname, "router4", sizeof(rs->nickname)); + memset(rs->identity_digest, 0x34, DIGEST_LEN); + memset(rs->descriptor_digest, 47, DIGEST_LEN); + rs->addr = 0xC0000203; + rs->or_port = 500; + rs->dir_port = 1999; + /* all flags but running cleared */ + rs->is_flagged_running = 1; + /* + * This one has unmeasured bandwidth below the clip cutoff, and + * so shouldn't be clipped; we'll have to test that it isn't + * later. + */ + rs->has_measured_bw = 0; + rs->has_bandwidth = 1; + rs->measured_bw = 0; + rs->bandwidth = DEFAULT_MAX_UNMEASURED_BW / 2; + break; + case 4: + /* No more for this test; return NULL */ + vrs = NULL; + break; + default: + /* Shouldn't happen */ + test_assert(0); + } + + done: + return vrs; +} + +/** Apply tweaks to the vote list for each voter; for the umbw test this is + * just adding the right consensus methods to let clipping happen */ +static void +vote_tweaks_for_umbw(networkstatus_t *v, int voter, time_t now) +{ + test_assert(v); + (void)voter; + (void)now; + + test_assert(v->supported_methods); + smartlist_clear(v->supported_methods); + /* Method 17 is MIN_METHOD_TO_CLIP_UNMEASURED_BW */ + smartlist_split_string(v->supported_methods, + "1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17", + NULL, 0, -1); + + done: + return; +} + +/** + * Test a parsed vote_routerstatus_t for umbw test. + */ +static void +test_vrs_for_umbw(vote_routerstatus_t *vrs, int voter, time_t now) +{ + routerstatus_t *rs; + tor_addr_t addr_ipv6; + + (void)voter; + test_assert(vrs); + rs = &(vrs->status); + test_assert(rs); + + /* Split out by digests to test */ + if (tor_memeq(rs->identity_digest, + "\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3" + "\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3", + DIGEST_LEN)) { + /* + * Check the first routerstatus - measured bandwidth below the clip + * cutoff. + */ + test_streq(vrs->version, "0.1.2.14"); + test_eq(rs->published_on, now-1500); + test_streq(rs->nickname, "router2"); + test_memeq(rs->identity_digest, + "\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3" + "\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3", + DIGEST_LEN); + test_memeq(rs->descriptor_digest, "NNNNNNNNNNNNNNNNNNNN", DIGEST_LEN); + test_eq(rs->addr, 0x99008801); + test_eq(rs->or_port, 443); + test_eq(rs->dir_port, 8000); + test_assert(rs->has_bandwidth); + test_assert(rs->has_measured_bw); + test_eq(rs->bandwidth, DEFAULT_MAX_UNMEASURED_BW / 2); + test_eq(rs->measured_bw, DEFAULT_MAX_UNMEASURED_BW / 2); + } else if (tor_memeq(rs->identity_digest, + "\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5" + "\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5", + DIGEST_LEN)) { + + /* + * Check the second routerstatus - measured bandwidth above the clip + * cutoff. + */ + test_streq(vrs->version, "0.2.0.5"); + test_eq(rs->published_on, now-1000); + test_streq(rs->nickname, "router1"); + test_memeq(rs->identity_digest, + "\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5" + "\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5", + DIGEST_LEN); + test_memeq(rs->descriptor_digest, "MMMMMMMMMMMMMMMMMMMM", DIGEST_LEN); + test_eq(rs->addr, 0x99009901); + test_eq(rs->or_port, 443); + test_eq(rs->dir_port, 0); + tor_addr_parse(&addr_ipv6, "[1:2:3::4]"); + test_assert(tor_addr_eq(&rs->ipv6_addr, &addr_ipv6)); + test_eq(rs->ipv6_orport, 4711); + test_assert(rs->has_bandwidth); + test_assert(rs->has_measured_bw); + test_eq(rs->bandwidth, DEFAULT_MAX_UNMEASURED_BW * 2); + test_eq(rs->measured_bw, DEFAULT_MAX_UNMEASURED_BW * 2); + } else if (tor_memeq(rs->identity_digest, + "\x33\x33\x33\x33\x33\x33\x33\x33\x33\x33" + "\x33\x33\x33\x33\x33\x33\x33\x33\x33\x33", + DIGEST_LEN)) { + /* + * Check the third routerstatus - unmeasured bandwidth above the clip + * cutoff; this one should be clipped later on in the consensus, but + * appears unclipped in the vote. + */ + test_assert(rs->has_bandwidth); + test_assert(!(rs->has_measured_bw)); + test_eq(rs->bandwidth, DEFAULT_MAX_UNMEASURED_BW * 2); + test_eq(rs->measured_bw, 0); + } else if (tor_memeq(rs->identity_digest, + "\x34\x34\x34\x34\x34\x34\x34\x34\x34\x34" + "\x34\x34\x34\x34\x34\x34\x34\x34\x34\x34", + DIGEST_LEN)) { + /* + * Check the fourth routerstatus - unmeasured bandwidth below the clip + * cutoff; this one should not be clipped. + */ + test_assert(rs->has_bandwidth); + test_assert(!(rs->has_measured_bw)); + test_eq(rs->bandwidth, DEFAULT_MAX_UNMEASURED_BW / 2); + test_eq(rs->measured_bw, 0); + } else { + test_assert(0); + } + + done: + return; +} + +/** + * Test a consensus for v3_networkstatus_test + */ +static void +test_consensus_for_umbw(networkstatus_t *con, time_t now) +{ + (void)now; + + test_assert(con); + test_assert(!con->cert); + /* test_assert(con->consensus_method >= MIN_METHOD_TO_CLIP_UNMEASURED_BW); */ + test_assert(con->consensus_method >= 16); + test_eq(4, smartlist_len(con->routerstatus_list)); + /* There should be four listed routers; all voters saw the same in this */ + + done: + return; +} + +/** + * Test a router list entry for umbw test + */ +static void +test_routerstatus_for_umbw(routerstatus_t *rs, time_t now) +{ + tor_addr_t addr_ipv6; + + test_assert(rs); + + /* There should be four listed routers, as constructed above */ + if (tor_memeq(rs->identity_digest, + "\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3" + "\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3", + DIGEST_LEN)) { + test_memeq(rs->identity_digest, + "\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3" + "\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3", + DIGEST_LEN); + test_memeq(rs->descriptor_digest, "NNNNNNNNNNNNNNNNNNNN", DIGEST_LEN); + test_assert(!rs->is_authority); + test_assert(!rs->is_exit); + test_assert(!rs->is_fast); + test_assert(!rs->is_possible_guard); + test_assert(!rs->is_stable); + /* (If it wasn't running it wouldn't be here) */ + test_assert(rs->is_flagged_running); + test_assert(!rs->is_v2_dir); + test_assert(!rs->is_valid); + test_assert(!rs->is_named); + /* This one should have measured bandwidth below the clip cutoff */ + test_assert(rs->has_bandwidth); + test_assert(rs->has_measured_bw); + test_eq(rs->bandwidth, DEFAULT_MAX_UNMEASURED_BW / 2); + test_eq(rs->measured_bw, DEFAULT_MAX_UNMEASURED_BW / 2); + } else if (tor_memeq(rs->identity_digest, + "\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5" + "\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5", + DIGEST_LEN)) { + /* This one showed up in 3 digests. Twice with ID 'M', once with 'Z'. */ + test_memeq(rs->identity_digest, + "\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5" + "\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5", + DIGEST_LEN); + test_streq(rs->nickname, "router1"); + test_memeq(rs->descriptor_digest, "MMMMMMMMMMMMMMMMMMMM", DIGEST_LEN); + test_eq(rs->published_on, now-1000); + test_eq(rs->addr, 0x99009901); + test_eq(rs->or_port, 443); + test_eq(rs->dir_port, 0); + tor_addr_parse(&addr_ipv6, "[1:2:3::4]"); + test_assert(tor_addr_eq(&rs->ipv6_addr, &addr_ipv6)); + test_eq(rs->ipv6_orport, 4711); + test_assert(!rs->is_authority); + test_assert(rs->is_exit); + test_assert(rs->is_fast); + test_assert(rs->is_possible_guard); + test_assert(rs->is_stable); + test_assert(rs->is_flagged_running); + test_assert(rs->is_v2_dir); + test_assert(rs->is_valid); + test_assert(!rs->is_named); + /* This one should have measured bandwidth above the clip cutoff */ + test_assert(rs->has_bandwidth); + test_assert(rs->has_measured_bw); + test_eq(rs->bandwidth, DEFAULT_MAX_UNMEASURED_BW * 2); + test_eq(rs->measured_bw, DEFAULT_MAX_UNMEASURED_BW * 2); + } else if (tor_memeq(rs->identity_digest, + "\x33\x33\x33\x33\x33\x33\x33\x33\x33\x33" + "\x33\x33\x33\x33\x33\x33\x33\x33\x33\x33", + DIGEST_LEN)) { + /* + * This one should have unmeasured bandwidth above the clip cutoff, + * and so should be clipped + */ + test_assert(rs->has_bandwidth); + test_assert(!(rs->has_measured_bw)); + test_eq(rs->bandwidth, DEFAULT_MAX_UNMEASURED_BW); + test_eq(rs->measured_bw, 0); + } else if (tor_memeq(rs->identity_digest, + "\x34\x34\x34\x34\x34\x34\x34\x34\x34\x34" + "\x34\x34\x34\x34\x34\x34\x34\x34\x34\x34", + DIGEST_LEN)) { + /* + * This one should have unmeasured bandwidth below the clip cutoff, + * and so should not be clipped + */ + test_assert(rs->has_bandwidth); + test_assert(!(rs->has_measured_bw)); + test_eq(rs->bandwidth, DEFAULT_MAX_UNMEASURED_BW / 2); + test_eq(rs->measured_bw, 0); + } else { + /* Weren't expecting this... */ + test_assert(0); + } + + done: + return; +} + +/** + * Compute a consensus involving clipping unmeasured bandwidth with consensus + * method 17; this uses the same test_a_networkstatus() function that the + * v3_networkstatus test uses. + */ + +static void +test_dir_clip_unmeasured_bw(void) +{ + test_a_networkstatus(gen_routerstatus_for_umbw, + vote_tweaks_for_umbw, + test_vrs_for_umbw, + test_consensus_for_umbw, + test_routerstatus_for_umbw); +} + #define DIR_LEGACY(name) \ { #name, legacy_test_helper, TT_FORK, &legacy_setup, test_dir_ ## name } @@ -1701,6 +2090,7 @@ struct testcase_t dir_tests[] = { DIR_LEGACY(v3_networkstatus), DIR(random_weighted), DIR(scale_bw), + DIR_LEGACY(clip_unmeasured_bw), END_OF_TESTCASES }; -- 2.11.4.GIT