diff --git a/src/feature/dirauth/dirvote.c b/src/feature/dirauth/dirvote.c index 4f8cf2b8e6..9c13f762e2 100644 --- a/src/feature/dirauth/dirvote.c +++ b/src/feature/dirauth/dirvote.c @@ -1479,6 +1479,21 @@ compute_nth_protocol_set(int n, int n_voters, const smartlist_t *votes) return result; } +/** Helper: Takes a smartlist of `const char *` flags, and a flag to remove. + * + * Removes that flag if it is present in the list. Doesn't free it. + */ +static void +remove_flag(smartlist_t *sl, const char *flag) +{ + /* We can't use smartlist_string_remove() here, since that doesn't preserve + * order, and since it frees elements from the string. */ + + int idx = smartlist_string_pos(sl, flag); + if (idx >= 0) + smartlist_del_keeporder(sl, idx); +} + /** Given a list of vote networkstatus_t in votes, our public * authority identity_key, our private authority signing_key, * and the number of total_authorities that we believe exist in our @@ -1633,6 +1648,9 @@ networkstatus_compute_consensus(smartlist_t *votes, tor_free(votesec_list); tor_free(distsec_list); } + // True if anybody is voting on the BadExit flag. + const bool badexit_flag_is_listed = + smartlist_contains_string(flags, "BadExit"); chunks = smartlist_new(); @@ -1924,7 +1942,7 @@ networkstatus_compute_consensus(smartlist_t *votes, const char *chosen_name = NULL; int exitsummary_disagreement = 0; int is_named = 0, is_unnamed = 0, is_running = 0, is_valid = 0; - int is_guard = 0, is_exit = 0, is_bad_exit = 0; + int is_guard = 0, is_exit = 0, is_bad_exit = 0, is_middle_only = 0; int naming_conflict = 0; int n_listing = 0; char microdesc_digest[DIGEST256_LEN]; @@ -2055,7 +2073,6 @@ networkstatus_compute_consensus(smartlist_t *votes, } /* Set the flags. */ - smartlist_add(chosen_flags, (char*)"s"); /* for the start of the line. */ SMARTLIST_FOREACH_BEGIN(flags, const char *, fl) { if (!strcmp(fl, "Named")) { if (is_named) @@ -2077,6 +2094,8 @@ networkstatus_compute_consensus(smartlist_t *votes, is_running = 1; else if (!strcmp(fl, "BadExit")) is_bad_exit = 1; + else if (!strcmp(fl, "MiddleOnly")) + is_middle_only = 1; else if (!strcmp(fl, "Valid")) is_valid = 1; } @@ -2093,6 +2112,22 @@ networkstatus_compute_consensus(smartlist_t *votes, if (!is_valid) continue; + /* Starting with consensus method 32, we handle the middle-only + * flag specially: when it is present, we clear some flags, and + * set others. */ + if (is_middle_only && consensus_method >= MIN_METHOD_FOR_MIDDLEONLY) { + remove_flag(chosen_flags, "Exit"); + remove_flag(chosen_flags, "V2Dir"); + remove_flag(chosen_flags, "Guard"); + remove_flag(chosen_flags, "HSDir"); + is_exit = is_guard = 0; + if (! is_bad_exit && badexit_flag_is_listed) { + is_bad_exit = 1; + smartlist_add(chosen_flags, (char *)"BadExit"); + smartlist_sort_strings(chosen_flags); // restore order. + } + } + /* Pick the version. */ if (smartlist_len(versions)) { sort_version_list(versions, 0); @@ -2253,6 +2288,8 @@ networkstatus_compute_consensus(smartlist_t *votes, smartlist_add_asprintf(chunks, "m %s\n", m); } /* Next line is all flags. The "\n" is missing. */ + smartlist_add_asprintf(chunks, "s%s", + smartlist_len(chosen_flags)?" ":""); smartlist_add(chunks, smartlist_join_strings(chosen_flags, " ", 0, NULL)); /* Now the version line. */ diff --git a/src/feature/dirauth/dirvote.h b/src/feature/dirauth/dirvote.h index d6a2d9cc75..9cb7fe1694 100644 --- a/src/feature/dirauth/dirvote.h +++ b/src/feature/dirauth/dirvote.h @@ -53,7 +53,7 @@ #define MIN_SUPPORTED_CONSENSUS_METHOD 28 /** The highest consensus method that we currently support. */ -#define MAX_SUPPORTED_CONSENSUS_METHOD 31 +#define MAX_SUPPORTED_CONSENSUS_METHOD 32 /** * Lowest consensus method where microdescriptor lines are put in canonical @@ -70,6 +70,10 @@ */ #define MIN_METHOD_FOR_CORRECT_BWWEIGHTSCALE 31 +/** Lowest consensus method for which we handle the MiddleOnly flag specially. + */ +#define MIN_METHOD_FOR_MIDDLEONLY 32 + /** Default bandwidth to clip unmeasured bandwidths to using method >= * MIN_METHOD_TO_CLIP_UNMEASURED_BW. (This is not a consensus method; do not * get confused with the above macros.) */