diff options
author | Nick Mathewson <nickm@torproject.org> | 2018-05-01 10:29:05 -0400 |
---|---|---|
committer | Nick Mathewson <nickm@torproject.org> | 2018-05-01 10:29:05 -0400 |
commit | d018bf199c9e566202eef0bb7fb68c2567c4a25e (patch) | |
tree | 83b81729b2c25a21bd2ef61e9350667d2fa56d7e /src | |
parent | 4cf6b67f5e83e1a26b4b2fbdb3b169c137a30687 (diff) | |
parent | d8509b450a1de815399a42b5df8c6b25789429c7 (diff) | |
download | tor-d018bf199c9e566202eef0bb7fb68c2567c4a25e.tar.gz tor-d018bf199c9e566202eef0bb7fb68c2567c4a25e.zip |
Merge remote-tracking branch 'dgoulet/ticket25610_034_01-squashed'
Diffstat (limited to 'src')
35 files changed, 1582 insertions, 1308 deletions
diff --git a/src/or/circuitstats.c b/src/or/circuitstats.c index 6438319273..6015bcf851 100644 --- a/src/or/circuitstats.c +++ b/src/or/circuitstats.c @@ -35,6 +35,7 @@ #include "networkstatus.h" #include "rendclient.h" #include "rendservice.h" +#include "router.h" #include "statefile.h" #include "circuitlist.h" #include "circuituse.h" @@ -125,7 +126,7 @@ circuit_build_times_disabled_(const or_options_t *options, ignore_consensus ? 0 : networkstatus_get_param(NULL, "cbtdisabled", 0, 0, 1); int config_disabled = !options->LearnCircuitBuildTimeout; - int dirauth_disabled = options->AuthoritativeDir; + int dirauth_disabled = authdir_mode(options); int state_disabled = did_last_state_file_write_fail() ? 1 : 0; /* LearnCircuitBuildTimeout and Tor2web/Single Onion Services are * incompatible in two ways: diff --git a/src/or/config.c b/src/or/config.c index 54f3930fc5..3719ac8847 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -79,7 +79,6 @@ #include "confparse.h" #include "cpuworker.h" #include "dirserv.h" -#include "dirvote.h" #include "dns.h" #include "dos.h" #include "entrynodes.h" @@ -110,6 +109,8 @@ #include "procmon.h" +#include "dirauth/dirvote.h" + #ifdef HAVE_SYSTEMD # if defined(__COVERITY__) && !defined(__INCLUDE_LEVEL__) /* Systemd's use of gcc's __INCLUDE_LEVEL__ extension macro appears to confuse diff --git a/src/or/control.c b/src/or/control.c index dda8872182..aab854ecdd 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -76,7 +76,7 @@ #include "router.h" #include "routerlist.h" #include "routerparse.h" -#include "shared_random.h" +#include "shared_random_common.h" #ifndef _WIN32 #include <pwd.h> diff --git a/src/or/dircollate.c b/src/or/dirauth/dircollate.c index dec6f75154..dec6f75154 100644 --- a/src/or/dircollate.c +++ b/src/or/dirauth/dircollate.c diff --git a/src/or/dircollate.h b/src/or/dirauth/dircollate.h index 0584b2fe06..0584b2fe06 100644 --- a/src/or/dircollate.h +++ b/src/or/dirauth/dircollate.h diff --git a/src/or/dirvote.c b/src/or/dirauth/dirvote.c index f3b8a19f00..dc978f26e9 100644 --- a/src/or/dirvote.c +++ b/src/or/dirauth/dirvote.c @@ -10,8 +10,11 @@ #include "directory.h" #include "dirserv.h" #include "dirvote.h" +#include "dirvote_common.h" #include "microdesc.h" #include "networkstatus.h" +#include "nodelist.h" +#include "parsecommon.h" #include "policies.h" #include "protover.h" #include "rephist.h" @@ -92,6 +95,30 @@ static int dirvote_compute_consensuses(void); static int dirvote_publish_consensus(void); /* ===== + * Certificate functions + * ===== */ + +/** Allocate and return a new authority_cert_t with the same contents as + * <b>cert</b>. */ +STATIC authority_cert_t * +authority_cert_dup(authority_cert_t *cert) +{ + authority_cert_t *out = tor_malloc(sizeof(authority_cert_t)); + tor_assert(cert); + + memcpy(out, cert, sizeof(authority_cert_t)); + /* Now copy pointed-to things. */ + out->cache_info.signed_descriptor_body = + tor_strndup(cert->cache_info.signed_descriptor_body, + cert->cache_info.signed_descriptor_len); + out->cache_info.saved_location = SAVED_NOWHERE; + out->identity_key = crypto_pk_dup_key(cert->identity_key); + out->signing_key = crypto_pk_dup_key(cert->signing_key); + + return out; +} + +/* ===== * Voting * =====*/ @@ -351,6 +378,53 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key, * Consensus generation * ===== */ +/** If <b>vrs</b> has a hash made for the consensus method <b>method</b> with + * the digest algorithm <b>alg</b>, decode it and copy it into + * <b>digest256_out</b> and return 0. Otherwise return -1. */ +static int +vote_routerstatus_find_microdesc_hash(char *digest256_out, + const vote_routerstatus_t *vrs, + int method, + digest_algorithm_t alg) +{ + /* XXXX only returns the sha256 method. */ + const vote_microdesc_hash_t *h; + char mstr[64]; + size_t mlen; + char dstr[64]; + + tor_snprintf(mstr, sizeof(mstr), "%d", method); + mlen = strlen(mstr); + tor_snprintf(dstr, sizeof(dstr), " %s=", + crypto_digest_algorithm_get_name(alg)); + + for (h = vrs->microdesc; h; h = h->next) { + const char *cp = h->microdesc_hash_line; + size_t num_len; + /* cp looks like \d+(,\d+)* (digesttype=val )+ . Let's hunt for mstr in + * the first part. */ + while (1) { + num_len = strspn(cp, "1234567890"); + if (num_len == mlen && fast_memeq(mstr, cp, mlen)) { + /* This is the line. */ + char buf[BASE64_DIGEST256_LEN+1]; + /* XXXX ignores extraneous stuff if the digest is too long. This + * seems harmless enough, right? */ + cp = strstr(cp, dstr); + if (!cp) + return -1; + cp += strlen(dstr); + strlcpy(buf, cp, sizeof(buf)); + return digest256_from_base64(digest256_out, buf); + } + if (num_len == 0 || cp[num_len] != ',') + break; + cp += num_len + 1; + } + } + return -1; +} + /** Given a vote <b>vote</b> (not a consensus!), return its associated * networkstatus_voter_info_t. */ static networkstatus_voter_info_t * @@ -363,20 +437,6 @@ get_voter(const networkstatus_t *vote) return smartlist_get(vote->voters, 0); } -/** Return the signature made by <b>voter</b> using the algorithm - * <b>alg</b>, or NULL if none is found. */ -document_signature_t * -voter_get_sig_by_algorithm(const networkstatus_voter_info_t *voter, - digest_algorithm_t alg) -{ - if (!voter->sigs) - return NULL; - SMARTLIST_FOREACH(voter->sigs, document_signature_t *, sig, - if (sig->alg == alg) - return sig); - return NULL; -} - /** Temporary structure used in constructing a list of dir-source entries * for a consensus. One of these is generated for every vote, and one more * for every legacy key in each vote. */ @@ -1328,7 +1388,7 @@ compute_nth_protocol_set(int n, int n_voters, const smartlist_t *votes) * behavior, and make the new behavior conditional on a new-enough * consensus_method. **/ -char * +STATIC char * networkstatus_compute_consensus(smartlist_t *votes, int total_authorities, crypto_pk_t *identity_key, @@ -2372,7 +2432,7 @@ compute_consensus_package_lines(smartlist_t *votes) * new signature is verifiable.) Return the number of signatures added or * changed, or -1 if the document signed by <b>sigs</b> isn't the same * document as <b>target</b>. */ -int +STATIC int networkstatus_add_detached_signatures(networkstatus_t *target, ns_detached_signatures_t *sigs, const char *source, @@ -2456,7 +2516,7 @@ networkstatus_add_detached_signatures(networkstatus_t *target, continue; } - old_sig = voter_get_sig_by_algorithm(target_voter, sig->alg); + old_sig = dirvote_get_voter_sig_by_alg(target_voter, sig->alg); /* If the target already has a good signature from this voter, then skip * this one. */ @@ -2564,7 +2624,7 @@ networkstatus_format_signatures(networkstatus_t *consensus, * corresponding to the signatures on <b>consensuses</b>, which must contain * exactly one FLAV_NS consensus, and no more than one consensus for each * other flavor. */ -char * +STATIC char * networkstatus_get_detached_signatures(smartlist_t *consensuses) { smartlist_t *elements; @@ -2669,213 +2729,6 @@ get_detached_signatures_from_pending_consensuses(pending_consensus_t *pending, return signatures; } -/** Release all storage held in <b>s</b>. */ -void -ns_detached_signatures_free_(ns_detached_signatures_t *s) -{ - if (!s) - return; - if (s->signatures) { - STRMAP_FOREACH(s->signatures, flavor, smartlist_t *, sigs) { - SMARTLIST_FOREACH(sigs, document_signature_t *, sig, - document_signature_free(sig)); - smartlist_free(sigs); - } STRMAP_FOREACH_END; - strmap_free(s->signatures, NULL); - strmap_free(s->digests, tor_free_); - } - - tor_free(s); -} - -/* ===== - * Certificate functions - * ===== */ - -/** Allocate and return a new authority_cert_t with the same contents as - * <b>cert</b>. */ -authority_cert_t * -authority_cert_dup(authority_cert_t *cert) -{ - authority_cert_t *out = tor_malloc(sizeof(authority_cert_t)); - tor_assert(cert); - - memcpy(out, cert, sizeof(authority_cert_t)); - /* Now copy pointed-to things. */ - out->cache_info.signed_descriptor_body = - tor_strndup(cert->cache_info.signed_descriptor_body, - cert->cache_info.signed_descriptor_len); - out->cache_info.saved_location = SAVED_NOWHERE; - out->identity_key = crypto_pk_dup_key(cert->identity_key); - out->signing_key = crypto_pk_dup_key(cert->signing_key); - - return out; -} - -/* ===== - * Vote scheduling - * ===== */ - -/** Set *<b>timing_out</b> to the intervals at which we would like to vote. - * Note that these aren't the intervals we'll use to vote; they're the ones - * that we'll vote to use. */ -void -dirvote_get_preferred_voting_intervals(vote_timing_t *timing_out) -{ - const or_options_t *options = get_options(); - - tor_assert(timing_out); - - timing_out->vote_interval = options->V3AuthVotingInterval; - timing_out->n_intervals_valid = options->V3AuthNIntervalsValid; - timing_out->vote_delay = options->V3AuthVoteDelay; - timing_out->dist_delay = options->V3AuthDistDelay; -} - -/** Return the start of the next interval of size <b>interval</b> (in - * seconds) after <b>now</b>, plus <b>offset</b>. Midnight always - * starts a fresh interval, and if the last interval of a day would be - * truncated to less than half its size, it is rolled into the - * previous interval. */ -time_t -dirvote_get_start_of_next_interval(time_t now, int interval, int offset) -{ - struct tm tm; - time_t midnight_today=0; - time_t midnight_tomorrow; - time_t next; - - tor_gmtime_r(&now, &tm); - tm.tm_hour = 0; - tm.tm_min = 0; - tm.tm_sec = 0; - - if (tor_timegm(&tm, &midnight_today) < 0) { - log_warn(LD_BUG, "Ran into an invalid time when trying to find midnight."); - } - midnight_tomorrow = midnight_today + (24*60*60); - - next = midnight_today + ((now-midnight_today)/interval + 1)*interval; - - /* Intervals never cross midnight. */ - if (next > midnight_tomorrow) - next = midnight_tomorrow; - - /* If the interval would only last half as long as it's supposed to, then - * skip over to the next day. */ - if (next + interval/2 > midnight_tomorrow) - next = midnight_tomorrow; - - next += offset; - if (next - interval > now) - next -= interval; - - return next; -} - -/* Populate and return a new voting_schedule_t that can be used to schedule - * voting. The object is allocated on the heap and it's the responsibility of - * the caller to free it. Can't fail. */ -static voting_schedule_t * -get_voting_schedule(const or_options_t *options, time_t now, int severity) -{ - int interval, vote_delay, dist_delay; - time_t start; - time_t end; - networkstatus_t *consensus; - voting_schedule_t *new_voting_schedule; - - new_voting_schedule = tor_malloc_zero(sizeof(voting_schedule_t)); - - consensus = networkstatus_get_live_consensus(now); - - if (consensus) { - interval = (int)( consensus->fresh_until - consensus->valid_after ); - vote_delay = consensus->vote_seconds; - dist_delay = consensus->dist_seconds; - } else { - interval = options->TestingV3AuthInitialVotingInterval; - vote_delay = options->TestingV3AuthInitialVoteDelay; - dist_delay = options->TestingV3AuthInitialDistDelay; - } - - tor_assert(interval > 0); - - if (vote_delay + dist_delay > interval/2) - vote_delay = dist_delay = interval / 4; - - start = new_voting_schedule->interval_starts = - dirvote_get_start_of_next_interval(now,interval, - options->TestingV3AuthVotingStartOffset); - end = dirvote_get_start_of_next_interval(start+1, interval, - options->TestingV3AuthVotingStartOffset); - - tor_assert(end > start); - - new_voting_schedule->fetch_missing_signatures = start - (dist_delay/2); - new_voting_schedule->voting_ends = start - dist_delay; - new_voting_schedule->fetch_missing_votes = - start - dist_delay - (vote_delay/2); - new_voting_schedule->voting_starts = start - dist_delay - vote_delay; - - { - char tbuf[ISO_TIME_LEN+1]; - format_iso_time(tbuf, new_voting_schedule->interval_starts); - tor_log(severity, LD_DIR,"Choosing expected valid-after time as %s: " - "consensus_set=%d, interval=%d", - tbuf, consensus?1:0, interval); - } - - return new_voting_schedule; -} - -#define voting_schedule_free(s) \ - FREE_AND_NULL(voting_schedule_t, voting_schedule_free_, (s)) - -/** Frees a voting_schedule_t. This should be used instead of the generic - * tor_free. */ -static void -voting_schedule_free_(voting_schedule_t *voting_schedule_to_free) -{ - if (!voting_schedule_to_free) - return; - tor_free(voting_schedule_to_free); -} - -static voting_schedule_t voting_schedule; - -/* Using the time <b>now</b>, return the next voting valid-after time. */ -time_t -dirvote_get_next_valid_after_time(void) -{ - /* This is a safe guard in order to make sure that the voting schedule - * static object is at least initialized. Using this function with a zeroed - * voting schedule can lead to bugs. */ - if (tor_mem_is_zero((const char *) &voting_schedule, - sizeof(voting_schedule))) { - dirvote_recalculate_timing(get_options(), time(NULL)); - voting_schedule.created_on_demand = 1; - } - return voting_schedule.interval_starts; -} - -/** Set voting_schedule to hold the timing for the next vote we should be - * doing. All type of tor do that because HS subsystem needs the timing as - * well to function properly. */ -void -dirvote_recalculate_timing(const or_options_t *options, time_t now) -{ - voting_schedule_t *new_voting_schedule; - - /* get the new voting schedule */ - new_voting_schedule = get_voting_schedule(options, now, LOG_INFO); - tor_assert(new_voting_schedule); - - /* Fill in the global static struct now */ - memcpy(&voting_schedule, new_voting_schedule, sizeof(voting_schedule)); - voting_schedule_free(new_voting_schedule); -} - /** Entry point: Take whatever voting actions are pending as of <b>now</b>. */ void dirvote_act(const or_options_t *options, time_t now) @@ -3798,7 +3651,7 @@ dirvote_get_vote(const char *fp, int flags) /** Construct and return a new microdescriptor from a routerinfo <b>ri</b> * according to <b>consensus_method</b>. **/ -microdesc_t * +STATIC microdesc_t * dirvote_create_microdescriptor(const routerinfo_t *ri, int consensus_method) { microdesc_t *result = NULL; @@ -3893,7 +3746,7 @@ dirvote_create_microdescriptor(const routerinfo_t *ri, int consensus_method) * in a consensus vote document. Write it into the <b>out_len</b>-byte buffer * in <b>out</b>. Return -1 on failure and the number of characters written * on success. */ -ssize_t +static ssize_t dirvote_format_microdesc_vote_line(char *out_buf, size_t out_buf_len, const microdesc_t *md, int consensus_method_low, @@ -4001,50 +3854,653 @@ dirvote_format_all_microdesc_vote_lines(const routerinfo_t *ri, time_t now, return result; } -/** If <b>vrs</b> has a hash made for the consensus method <b>method</b> with - * the digest algorithm <b>alg</b>, decode it and copy it into - * <b>digest256_out</b> and return 0. Otherwise return -1. */ -int -vote_routerstatus_find_microdesc_hash(char *digest256_out, - const vote_routerstatus_t *vrs, - int method, - digest_algorithm_t alg) +/** Parse and extract all SR commits from <b>tokens</b> and place them in + * <b>ns</b>. */ +static void +extract_shared_random_commits(networkstatus_t *ns, smartlist_t *tokens) { - /* XXXX only returns the sha256 method. */ - const vote_microdesc_hash_t *h; - char mstr[64]; - size_t mlen; - char dstr[64]; + smartlist_t *chunks = NULL; - tor_snprintf(mstr, sizeof(mstr), "%d", method); - mlen = strlen(mstr); - tor_snprintf(dstr, sizeof(dstr), " %s=", - crypto_digest_algorithm_get_name(alg)); + tor_assert(ns); + tor_assert(tokens); + /* Commits are only present in a vote. */ + tor_assert(ns->type == NS_TYPE_VOTE); - for (h = vrs->microdesc; h; h = h->next) { - const char *cp = h->microdesc_hash_line; - size_t num_len; - /* cp looks like \d+(,\d+)* (digesttype=val )+ . Let's hunt for mstr in - * the first part. */ - while (1) { - num_len = strspn(cp, "1234567890"); - if (num_len == mlen && fast_memeq(mstr, cp, mlen)) { - /* This is the line. */ - char buf[BASE64_DIGEST256_LEN+1]; - /* XXXX ignores extraneous stuff if the digest is too long. This - * seems harmless enough, right? */ - cp = strstr(cp, dstr); - if (!cp) - return -1; - cp += strlen(dstr); - strlcpy(buf, cp, sizeof(buf)); - return digest256_from_base64(digest256_out, buf); + ns->sr_info.commits = smartlist_new(); + + smartlist_t *commits = find_all_by_keyword(tokens, K_COMMIT); + /* It's normal that a vote might contain no commits even if it participates + * in the SR protocol. Don't treat it as an error. */ + if (commits == NULL) { + goto end; + } + + /* Parse the commit. We do NO validation of number of arguments or ordering + * for forward compatibility, it's the parse commit job to inform us if it's + * supported or not. */ + chunks = smartlist_new(); + SMARTLIST_FOREACH_BEGIN(commits, directory_token_t *, tok) { + /* Extract all arguments and put them in the chunks list. */ + for (int i = 0; i < tok->n_args; i++) { + smartlist_add(chunks, tok->args[i]); + } + sr_commit_t *commit = sr_parse_commit(chunks); + smartlist_clear(chunks); + if (commit == NULL) { + /* Get voter identity so we can warn that this dirauth vote contains + * commit we can't parse. */ + networkstatus_voter_info_t *voter = smartlist_get(ns->voters, 0); + tor_assert(voter); + log_warn(LD_DIR, "SR: Unable to parse commit %s from vote of voter %s.", + escaped(tok->object_body), + hex_str(voter->identity_digest, + sizeof(voter->identity_digest))); + /* Commitment couldn't be parsed. Continue onto the next commit because + * this one could be unsupported for instance. */ + continue; + } + /* Add newly created commit object to the vote. */ + smartlist_add(ns->sr_info.commits, commit); + } SMARTLIST_FOREACH_END(tok); + + end: + smartlist_free(chunks); + smartlist_free(commits); +} + +/* Using the given directory tokens in tokens, parse the shared random commits + * and put them in the given vote document ns. + * + * This also sets the SR participation flag if present in the vote. */ +void +dirvote_parse_sr_commits(networkstatus_t *ns, smartlist_t *tokens) +{ + /* Does this authority participates in the SR protocol? */ + directory_token_t *tok = find_opt_by_keyword(tokens, K_SR_FLAG); + if (tok) { + ns->sr_info.participate = 1; + /* Get the SR commitments and reveals from the vote. */ + extract_shared_random_commits(ns, tokens); + } +} + +/* For the given vote, free the shared random commits if any. */ +void +dirvote_clear_commits(networkstatus_t *ns) +{ + tor_assert(ns->type == NS_TYPE_VOTE); + + if (ns->sr_info.commits) { + SMARTLIST_FOREACH(ns->sr_info.commits, sr_commit_t *, c, + sr_commit_free(c)); + smartlist_free(ns->sr_info.commits); + } +} + +/* The given url is the /tor/status-gove GET directory request. Populates the + * items list with strings that we can compress on the fly and dir_items with + * cached_dir_t objects that have a precompressed deflated version. */ +void +dirvote_dirreq_get_status_vote(const char *url, smartlist_t *items, + smartlist_t *dir_items) +{ + int current; + + url += strlen("/tor/status-vote/"); + current = !strcmpstart(url, "current/"); + url = strchr(url, '/'); + tor_assert(url); + ++url; + if (!strcmp(url, "consensus")) { + const char *item; + tor_assert(!current); /* we handle current consensus specially above, + * since it wants to be spooled. */ + if ((item = dirvote_get_pending_consensus(FLAV_NS))) + smartlist_add(items, (char*)item); + } else if (!current && !strcmp(url, "consensus-signatures")) { + /* XXXX the spec says that we should implement + * current/consensus-signatures too. It doesn't seem to be needed, + * though. */ + const char *item; + if ((item=dirvote_get_pending_detached_signatures())) + smartlist_add(items, (char*)item); + } else if (!strcmp(url, "authority")) { + const cached_dir_t *d; + int flags = DGV_BY_ID | + (current ? DGV_INCLUDE_PREVIOUS : DGV_INCLUDE_PENDING); + if ((d=dirvote_get_vote(NULL, flags))) + smartlist_add(dir_items, (cached_dir_t*)d); + } else { + const cached_dir_t *d; + smartlist_t *fps = smartlist_new(); + int flags; + if (!strcmpstart(url, "d/")) { + url += 2; + flags = DGV_INCLUDE_PENDING | DGV_INCLUDE_PREVIOUS; + } else { + flags = DGV_BY_ID | + (current ? DGV_INCLUDE_PREVIOUS : DGV_INCLUDE_PENDING); + } + dir_split_resource_into_fingerprints(url, fps, NULL, + DSR_HEX|DSR_SORT_UNIQ); + SMARTLIST_FOREACH(fps, char *, fp, { + if ((d = dirvote_get_vote(fp, flags))) + smartlist_add(dir_items, (cached_dir_t*)d); + tor_free(fp); + }); + smartlist_free(fps); + } +} + +/** Get the best estimate of a router's bandwidth for dirauth purposes, + * preferring measured to advertised values if available. */ +static uint32_t +dirserv_get_bandwidth_for_router_kb(const routerinfo_t *ri) +{ + uint32_t bw_kb = 0; + /* + * Yeah, measured bandwidths in measured_bw_line_t are (implicitly + * signed) longs and the ones router_get_advertised_bandwidth() returns + * are uint32_t. + */ + long mbw_kb = 0; + + if (ri) { + /* + * * First try to see if we have a measured bandwidth; don't bother with + * as_of_out here, on the theory that a stale measured bandwidth is still + * better to trust than an advertised one. + */ + if (dirserv_query_measured_bw_cache_kb(ri->cache_info.identity_digest, + &mbw_kb, NULL)) { + /* Got one! */ + bw_kb = (uint32_t)mbw_kb; + } else { + /* If not, fall back to advertised */ + bw_kb = router_get_advertised_bandwidth(ri) / 1000; + } + } + + return bw_kb; +} + +/** Helper for sorting: compares two routerinfos first by address, and then by + * descending order of "usefulness". (An authority is more useful than a + * non-authority; a running router is more useful than a non-running router; + * and a router with more bandwidth is more useful than one with less.) + **/ +static int +compare_routerinfo_by_ip_and_bw_(const void **a, const void **b) +{ + routerinfo_t *first = *(routerinfo_t **)a, *second = *(routerinfo_t **)b; + int first_is_auth, second_is_auth; + uint32_t bw_kb_first, bw_kb_second; + const node_t *node_first, *node_second; + int first_is_running, second_is_running; + + /* we return -1 if first should appear before second... that is, + * if first is a better router. */ + if (first->addr < second->addr) + return -1; + else if (first->addr > second->addr) + return 1; + + /* Potentially, this next bit could cause k n lg n memeq calls. But in + * reality, we will almost never get here, since addresses will usually be + * different. */ + + first_is_auth = + router_digest_is_trusted_dir(first->cache_info.identity_digest); + second_is_auth = + router_digest_is_trusted_dir(second->cache_info.identity_digest); + + if (first_is_auth && !second_is_auth) + return -1; + else if (!first_is_auth && second_is_auth) + return 1; + + node_first = node_get_by_id(first->cache_info.identity_digest); + node_second = node_get_by_id(second->cache_info.identity_digest); + first_is_running = node_first && node_first->is_running; + second_is_running = node_second && node_second->is_running; + + if (first_is_running && !second_is_running) + return -1; + else if (!first_is_running && second_is_running) + return 1; + + bw_kb_first = dirserv_get_bandwidth_for_router_kb(first); + bw_kb_second = dirserv_get_bandwidth_for_router_kb(second); + + if (bw_kb_first > bw_kb_second) + return -1; + else if (bw_kb_first < bw_kb_second) + return 1; + + /* They're equal! Compare by identity digest, so there's a + * deterministic order and we avoid flapping. */ + return fast_memcmp(first->cache_info.identity_digest, + second->cache_info.identity_digest, + DIGEST_LEN); +} + +/** Given a list of routerinfo_t in <b>routers</b>, return a new digestmap_t + * whose keys are the identity digests of those routers that we're going to + * exclude for Sybil-like appearance. */ +static digestmap_t * +get_possible_sybil_list(const smartlist_t *routers) +{ + const or_options_t *options = get_options(); + digestmap_t *omit_as_sybil; + smartlist_t *routers_by_ip = smartlist_new(); + uint32_t last_addr; + int addr_count; + /* Allow at most this number of Tor servers on a single IP address, ... */ + int max_with_same_addr = options->AuthDirMaxServersPerAddr; + if (max_with_same_addr <= 0) + max_with_same_addr = INT_MAX; + + smartlist_add_all(routers_by_ip, routers); + smartlist_sort(routers_by_ip, compare_routerinfo_by_ip_and_bw_); + omit_as_sybil = digestmap_new(); + + last_addr = 0; + addr_count = 0; + SMARTLIST_FOREACH_BEGIN(routers_by_ip, routerinfo_t *, ri) { + if (last_addr != ri->addr) { + last_addr = ri->addr; + addr_count = 1; + } else if (++addr_count > max_with_same_addr) { + digestmap_set(omit_as_sybil, ri->cache_info.identity_digest, ri); + } + } SMARTLIST_FOREACH_END(ri); + + smartlist_free(routers_by_ip); + return omit_as_sybil; +} + +/** Given a platform string as in a routerinfo_t (possibly null), return a + * newly allocated version string for a networkstatus document, or NULL if the + * platform doesn't give a Tor version. */ +static char * +version_from_platform(const char *platform) +{ + if (platform && !strcmpstart(platform, "Tor ")) { + const char *eos = find_whitespace(platform+4); + if (eos && !strcmpstart(eos, " (r")) { + /* XXXX Unify this logic with the other version extraction + * logic in routerparse.c. */ + eos = find_whitespace(eos+1); + } + if (eos) { + return tor_strndup(platform, eos-platform); + } + } + return NULL; +} + +/** Given a (possibly empty) list of config_line_t, each line of which contains + * a list of comma-separated version numbers surrounded by optional space, + * allocate and return a new string containing the version numbers, in order, + * separated by commas. Used to generate Recommended(Client|Server)?Versions + */ +static char * +format_versions_list(config_line_t *ln) +{ + smartlist_t *versions; + char *result; + versions = smartlist_new(); + for ( ; ln; ln = ln->next) { + smartlist_split_string(versions, ln->value, ",", + SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); + } + sort_version_list(versions, 1); + result = smartlist_join_strings(versions,",",0,NULL); + SMARTLIST_FOREACH(versions,char *,s,tor_free(s)); + smartlist_free(versions); + return result; +} + +/** If there are entries in <b>routers</b> with exactly the same ed25519 keys, + * remove the older one. If they are exactly the same age, remove the one + * with the greater descriptor digest. May alter the order of the list. */ +static void +routers_make_ed_keys_unique(smartlist_t *routers) +{ + routerinfo_t *ri2; + digest256map_t *by_ed_key = digest256map_new(); + + SMARTLIST_FOREACH_BEGIN(routers, routerinfo_t *, ri) { + ri->omit_from_vote = 0; + if (ri->cache_info.signing_key_cert == NULL) + continue; /* No ed key */ + const uint8_t *pk = ri->cache_info.signing_key_cert->signing_key.pubkey; + if ((ri2 = digest256map_get(by_ed_key, pk))) { + /* Duplicate; must omit one. Set the omit_from_vote flag in whichever + * one has the earlier published_on. */ + const time_t ri_pub = ri->cache_info.published_on; + const time_t ri2_pub = ri2->cache_info.published_on; + if (ri2_pub < ri_pub || + (ri2_pub == ri_pub && + fast_memcmp(ri->cache_info.signed_descriptor_digest, + ri2->cache_info.signed_descriptor_digest,DIGEST_LEN)<0)) { + digest256map_set(by_ed_key, pk, ri); + ri2->omit_from_vote = 1; + } else { + ri->omit_from_vote = 1; } - if (num_len == 0 || cp[num_len] != ',') - break; - cp += num_len + 1; + } else { + /* Add to map */ + digest256map_set(by_ed_key, pk, ri); + } + } SMARTLIST_FOREACH_END(ri); + + digest256map_free(by_ed_key, NULL); + + /* Now remove every router where the omit_from_vote flag got set. */ + SMARTLIST_FOREACH_BEGIN(routers, const routerinfo_t *, ri) { + if (ri->omit_from_vote) { + SMARTLIST_DEL_CURRENT(routers, ri); } + } SMARTLIST_FOREACH_END(ri); +} + +/** Routerstatus <b>rs</b> is part of a group of routers that are on + * too narrow an IP-space. Clear out its flags since we don't want it be used + * because of its Sybil-like appearance. + * + * Leave its BadExit flag alone though, since if we think it's a bad exit, + * we want to vote that way in case all the other authorities are voting + * Running and Exit. + */ +static void +clear_status_flags_on_sybil(routerstatus_t *rs) +{ + rs->is_authority = rs->is_exit = rs->is_stable = rs->is_fast = + rs->is_flagged_running = rs->is_named = rs->is_valid = + rs->is_hs_dir = rs->is_v2_dir = rs->is_possible_guard = 0; + /* FFFF we might want some mechanism to check later on if we + * missed zeroing any flags: it's easy to add a new flag but + * forget to add it to this clause. */ +} + +/** Return a new networkstatus_t* containing our current opinion. (For v3 + * authorities) */ +networkstatus_t * +dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, + authority_cert_t *cert) +{ + const or_options_t *options = get_options(); + networkstatus_t *v3_out = NULL; + uint32_t addr; + char *hostname = NULL, *client_versions = NULL, *server_versions = NULL; + const char *contact; + smartlist_t *routers, *routerstatuses; + char identity_digest[DIGEST_LEN]; + char signing_key_digest[DIGEST_LEN]; + int listbadexits = options->AuthDirListBadExits; + routerlist_t *rl = router_get_routerlist(); + time_t now = time(NULL); + time_t cutoff = now - ROUTER_MAX_AGE_TO_PUBLISH; + networkstatus_voter_info_t *voter = NULL; + vote_timing_t timing; + digestmap_t *omit_as_sybil = NULL; + const int vote_on_reachability = running_long_enough_to_decide_unreachable(); + smartlist_t *microdescriptors = NULL; + + tor_assert(private_key); + tor_assert(cert); + + if (crypto_pk_get_digest(private_key, signing_key_digest)<0) { + log_err(LD_BUG, "Error computing signing key digest"); + return NULL; } - return -1; + if (crypto_pk_get_digest(cert->identity_key, identity_digest)<0) { + log_err(LD_BUG, "Error computing identity key digest"); + return NULL; + } + if (resolve_my_address(LOG_WARN, options, &addr, NULL, &hostname)<0) { + log_warn(LD_NET, "Couldn't resolve my hostname"); + return NULL; + } + if (!hostname || !strchr(hostname, '.')) { + tor_free(hostname); + hostname = tor_dup_ip(addr); + } + + if (options->VersioningAuthoritativeDir) { + client_versions = format_versions_list(options->RecommendedClientVersions); + server_versions = format_versions_list(options->RecommendedServerVersions); + } + + contact = get_options()->ContactInfo; + if (!contact) + contact = "(none)"; + + /* + * Do this so dirserv_compute_performance_thresholds() and + * set_routerstatus_from_routerinfo() see up-to-date bandwidth info. + */ + if (options->V3BandwidthsFile) { + dirserv_read_measured_bandwidths(options->V3BandwidthsFile, NULL); + } else { + /* + * No bandwidths file; clear the measured bandwidth cache in case we had + * one last time around. + */ + if (dirserv_get_measured_bw_cache_size() > 0) { + dirserv_clear_measured_bw_cache(); + } + } + + /* precompute this part, since we need it to decide what "stable" + * means. */ + SMARTLIST_FOREACH(rl->routers, routerinfo_t *, ri, { + dirserv_set_router_is_running(ri, now); + }); + + routers = smartlist_new(); + smartlist_add_all(routers, rl->routers); + routers_make_ed_keys_unique(routers); + /* After this point, don't use rl->routers; use 'routers' instead. */ + routers_sort_by_identity(routers); + omit_as_sybil = get_possible_sybil_list(routers); + + DIGESTMAP_FOREACH(omit_as_sybil, sybil_id, void *, ignore) { + (void) ignore; + rep_hist_make_router_pessimal(sybil_id, now); + } DIGESTMAP_FOREACH_END; + + /* Count how many have measured bandwidths so we know how to assign flags; + * this must come before dirserv_compute_performance_thresholds() */ + dirserv_count_measured_bws(routers); + + dirserv_compute_performance_thresholds(omit_as_sybil); + + routerstatuses = smartlist_new(); + microdescriptors = smartlist_new(); + + SMARTLIST_FOREACH_BEGIN(routers, routerinfo_t *, ri) { + if (ri->cache_info.published_on >= cutoff) { + routerstatus_t *rs; + vote_routerstatus_t *vrs; + node_t *node = node_get_mutable_by_id(ri->cache_info.identity_digest); + if (!node) + continue; + + vrs = tor_malloc_zero(sizeof(vote_routerstatus_t)); + rs = &vrs->status; + set_routerstatus_from_routerinfo(rs, node, ri, now, + listbadexits); + + if (ri->cache_info.signing_key_cert) { + memcpy(vrs->ed25519_id, + ri->cache_info.signing_key_cert->signing_key.pubkey, + ED25519_PUBKEY_LEN); + } + + if (digestmap_get(omit_as_sybil, ri->cache_info.identity_digest)) + clear_status_flags_on_sybil(rs); + + if (!vote_on_reachability) + rs->is_flagged_running = 0; + + vrs->version = version_from_platform(ri->platform); + if (ri->protocol_list) { + vrs->protocols = tor_strdup(ri->protocol_list); + } else { + vrs->protocols = tor_strdup( + protover_compute_for_old_tor(vrs->version)); + } + vrs->microdesc = dirvote_format_all_microdesc_vote_lines(ri, now, + microdescriptors); + + smartlist_add(routerstatuses, vrs); + } + } SMARTLIST_FOREACH_END(ri); + + { + smartlist_t *added = + microdescs_add_list_to_cache(get_microdesc_cache(), + microdescriptors, SAVED_NOWHERE, 0); + smartlist_free(added); + smartlist_free(microdescriptors); + } + + smartlist_free(routers); + digestmap_free(omit_as_sybil, NULL); + + /* Apply guardfraction information to routerstatuses. */ + if (options->GuardfractionFile) { + dirserv_read_guardfraction_file(options->GuardfractionFile, + routerstatuses); + } + + /* This pass through applies the measured bw lines to the routerstatuses */ + if (options->V3BandwidthsFile) { + dirserv_read_measured_bandwidths(options->V3BandwidthsFile, + routerstatuses); + } else { + /* + * No bandwidths file; clear the measured bandwidth cache in case we had + * one last time around. + */ + if (dirserv_get_measured_bw_cache_size() > 0) { + dirserv_clear_measured_bw_cache(); + } + } + + v3_out = tor_malloc_zero(sizeof(networkstatus_t)); + + v3_out->type = NS_TYPE_VOTE; + dirvote_get_preferred_voting_intervals(&timing); + v3_out->published = now; + { + char tbuf[ISO_TIME_LEN+1]; + networkstatus_t *current_consensus = + networkstatus_get_live_consensus(now); + long last_consensus_interval; /* only used to pick a valid_after */ + if (current_consensus) + last_consensus_interval = current_consensus->fresh_until - + current_consensus->valid_after; + else + last_consensus_interval = options->TestingV3AuthInitialVotingInterval; + v3_out->valid_after = + dirvote_get_start_of_next_interval(now, (int)last_consensus_interval, + options->TestingV3AuthVotingStartOffset); + format_iso_time(tbuf, v3_out->valid_after); + log_notice(LD_DIR,"Choosing valid-after time in vote as %s: " + "consensus_set=%d, last_interval=%d", + tbuf, current_consensus?1:0, (int)last_consensus_interval); + } + v3_out->fresh_until = v3_out->valid_after + timing.vote_interval; + v3_out->valid_until = v3_out->valid_after + + (timing.vote_interval * timing.n_intervals_valid); + v3_out->vote_seconds = timing.vote_delay; + v3_out->dist_seconds = timing.dist_delay; + tor_assert(v3_out->vote_seconds > 0); + tor_assert(v3_out->dist_seconds > 0); + tor_assert(timing.n_intervals_valid > 0); + + v3_out->client_versions = client_versions; + v3_out->server_versions = server_versions; + + /* These are hardwired, to avoid disaster. */ + v3_out->recommended_relay_protocols = + tor_strdup("Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 " + "Link=4 LinkAuth=1 Microdesc=1-2 Relay=2"); + v3_out->recommended_client_protocols = + tor_strdup("Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 " + "Link=4 LinkAuth=1 Microdesc=1-2 Relay=2"); + v3_out->required_client_protocols = + tor_strdup("Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 " + "Link=4 LinkAuth=1 Microdesc=1-2 Relay=2"); + v3_out->required_relay_protocols = + tor_strdup("Cons=1 Desc=1 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 " + "Link=3-4 LinkAuth=1 Microdesc=1 Relay=1-2"); + + /* We are not allowed to vote to require anything we don't have. */ + tor_assert(protover_all_supported(v3_out->required_relay_protocols, NULL)); + tor_assert(protover_all_supported(v3_out->required_client_protocols, NULL)); + + /* We should not recommend anything we don't have. */ + tor_assert_nonfatal(protover_all_supported( + v3_out->recommended_relay_protocols, NULL)); + tor_assert_nonfatal(protover_all_supported( + v3_out->recommended_client_protocols, NULL)); + + v3_out->package_lines = smartlist_new(); + { + config_line_t *cl; + for (cl = get_options()->RecommendedPackages; cl; cl = cl->next) { + if (validate_recommended_package_line(cl->value)) + smartlist_add_strdup(v3_out->package_lines, cl->value); + } + } + + v3_out->known_flags = smartlist_new(); + smartlist_split_string(v3_out->known_flags, + "Authority Exit Fast Guard Stable V2Dir Valid HSDir", + 0, SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); + if (vote_on_reachability) + smartlist_add_strdup(v3_out->known_flags, "Running"); + if (listbadexits) + smartlist_add_strdup(v3_out->known_flags, "BadExit"); + smartlist_sort_strings(v3_out->known_flags); + + if (options->ConsensusParams) { + v3_out->net_params = smartlist_new(); + smartlist_split_string(v3_out->net_params, + options->ConsensusParams, NULL, 0, 0); + smartlist_sort_strings(v3_out->net_params); + } + + voter = tor_malloc_zero(sizeof(networkstatus_voter_info_t)); + voter->nickname = tor_strdup(options->Nickname); + memcpy(voter->identity_digest, identity_digest, DIGEST_LEN); + voter->sigs = smartlist_new(); + voter->address = hostname; + voter->addr = addr; + voter->dir_port = router_get_advertised_dir_port(options, 0); + voter->or_port = router_get_advertised_or_port(options); + voter->contact = tor_strdup(contact); + if (options->V3AuthUseLegacyKey) { + authority_cert_t *c = get_my_v3_legacy_cert(); + if (c) { + if (crypto_pk_get_digest(c->identity_key, voter->legacy_id_digest)) { + log_warn(LD_BUG, "Unable to compute digest of legacy v3 identity key"); + memset(voter->legacy_id_digest, 0, DIGEST_LEN); + } + } + } + + v3_out->voters = smartlist_new(); + smartlist_add(v3_out->voters, voter); + v3_out->cert = authority_cert_dup(cert); + v3_out->routerstatus_list = routerstatuses; + /* Note: networkstatus_digest is unset; it won't get set until we actually + * format the vote. */ + + return v3_out; } diff --git a/src/or/dirvote.h b/src/or/dirauth/dirvote.h index 8a317deb47..f69e872c8e 100644 --- a/src/or/dirvote.h +++ b/src/or/dirauth/dirvote.h @@ -12,8 +12,6 @@ #ifndef TOR_DIRVOTE_H #define TOR_DIRVOTE_H -#include "testsupport.h" - /* * Ideally, assuming synced clocks, we should only need 1 second for each of: * - Vote @@ -86,74 +84,27 @@ * get confused with the above macros.) */ #define DEFAULT_MAX_UNMEASURED_BW_KB 20 -void dirvote_free_all(void); +/* Directory Get Vote (DGV) flags for dirvote_get_vote(). */ +#define DGV_BY_ID 1 +#define DGV_INCLUDE_PENDING 2 +#define DGV_INCLUDE_PREVIOUS 4 + +/* + * Public API. Used outside of the dirauth subsystem. + * + * We need to nullify them if the module is disabled. + */ +#ifdef HAVE_MODULE_DIRAUTH -/* vote manipulation */ -char *networkstatus_compute_consensus(smartlist_t *votes, - int total_authorities, - crypto_pk_t *identity_key, - crypto_pk_t *signing_key, - const char *legacy_identity_key_digest, - crypto_pk_t *legacy_signing_key, - consensus_flavor_t flavor); -int networkstatus_add_detached_signatures(networkstatus_t *target, - ns_detached_signatures_t *sigs, - const char *source, - int severity, - const char **msg_out); -char *networkstatus_get_detached_signatures(smartlist_t *consensuses); -void ns_detached_signatures_free_(ns_detached_signatures_t *s); -#define ns_detached_signatures_free(s) \ - FREE_AND_NULL(ns_detached_signatures_t, ns_detached_signatures_free_, (s)) - -/* cert manipulation */ -authority_cert_t *authority_cert_dup(authority_cert_t *cert); - -/* vote scheduling */ - -/** Scheduling information for a voting interval. */ -typedef struct { - /** When do we generate and distribute our vote for this interval? */ - time_t voting_starts; - /** When do we send an HTTP request for any votes that we haven't - * been posted yet?*/ - time_t fetch_missing_votes; - /** When do we give up on getting more votes and generate a consensus? */ - time_t voting_ends; - /** When do we send an HTTP request for any signatures we're expecting to - * see on the consensus? */ - time_t fetch_missing_signatures; - /** When do we publish the consensus? */ - time_t interval_starts; - - /* True iff we have generated and distributed our vote. */ - int have_voted; - /* True iff we've requested missing votes. */ - int have_fetched_missing_votes; - /* True iff we have built a consensus and sent the signatures around. */ - int have_built_consensus; - /* True iff we've fetched missing signatures. */ - int have_fetched_missing_signatures; - /* True iff we have published our consensus. */ - int have_published_consensus; - - /* True iff this voting schedule was set on demand meaning not through the - * normal vote operation of a dirauth or when a consensus is set. This only - * applies to a directory authority that needs to recalculate the voting - * timings only for the first vote even though this object was initilized - * prior to voting. */ - int created_on_demand; -} voting_schedule_t; - -void dirvote_get_preferred_voting_intervals(vote_timing_t *timing_out); -time_t dirvote_get_start_of_next_interval(time_t now, - int interval, - int offset); -void dirvote_recalculate_timing(const or_options_t *options, time_t now); void dirvote_act(const or_options_t *options, time_t now); -time_t dirvote_get_next_valid_after_time(void); +void dirvote_free_all(void); -/* invoked on timers and by outside triggers. */ +void dirvote_parse_sr_commits(networkstatus_t *ns, smartlist_t *tokens); +void dirvote_clear_commits(networkstatus_t *ns); +void dirvote_dirreq_get_status_vote(const char *url, smartlist_t *items, + smartlist_t *dir_items); + +/* Storing signatures and votes functions */ struct pending_vote_t * dirvote_add_vote(const char *vote_body, const char **msg_out, int *status_out); @@ -161,15 +112,82 @@ int dirvote_add_signatures(const char *detached_signatures_body, const char *source, const char **msg_out); +#else /* HAVE_MODULE_DIRAUTH */ + +static inline void +dirvote_act(const or_options_t *options, time_t now) +{ + (void) options; + (void) now; +} + +static inline void +dirvote_free_all(void) +{ +} + +static inline void +dirvote_parse_sr_commits(networkstatus_t *ns, smartlist_t *tokens) +{ + (void) ns; + (void) tokens; +} + +static inline void +dirvote_clear_commits(networkstatus_t *ns) +{ + (void) ns; +} + +static inline void +dirvote_dirreq_get_status_vote(const char *url, smartlist_t *items, + smartlist_t *dir_items) +{ + (void) url; + (void) items; + (void) dir_items; +} + +static inline struct pending_vote_t * +dirvote_add_vote(const char *vote_body, const char **msg_out, int *status_out) +{ + (void) vote_body; + /* If the dirauth module is disabled, this should NEVER be called else we + * failed to safeguard the dirauth module. */ + tor_assert_nonfatal_unreached(); + + /* We need to send out an error code. */ + *status_out = 400; + *msg_out = "No directory authority support"; + return NULL; +} + +static inline int +dirvote_add_signatures(const char *detached_signatures_body, const char *source, + const char **msg_out) +{ + (void) detached_signatures_body; + (void) source; + (void) msg_out; + /* If the dirauth module is disabled, this should NEVER be called else we + * failed to safeguard the dirauth module. */ + tor_assert_nonfatal_unreached(); + return 0; +} + +#endif /* HAVE_MODULE_DIRAUTH */ + +void dirvote_recalculate_timing(const or_options_t *options, time_t now); /* Item access */ MOCK_DECL(const char*, dirvote_get_pending_consensus, (consensus_flavor_t flav)); MOCK_DECL(const char*, dirvote_get_pending_detached_signatures, (void)); - -#define DGV_BY_ID 1 -#define DGV_INCLUDE_PENDING 2 -#define DGV_INCLUDE_PREVIOUS 4 const cached_dir_t *dirvote_get_vote(const char *fp, int flags); + +/* + * API used _only_ by the dirauth subsystem. + */ + void set_routerstatus_from_routerinfo(routerstatus_t *rs, node_t *node, routerinfo_t *ri, time_t now, @@ -178,26 +196,18 @@ networkstatus_t * dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, authority_cert_t *cert); -microdesc_t *dirvote_create_microdescriptor(const routerinfo_t *ri, - int consensus_method); -ssize_t dirvote_format_microdesc_vote_line(char *out, size_t out_len, - const microdesc_t *md, - int consensus_method_low, - int consensus_method_high); vote_microdesc_hash_t *dirvote_format_all_microdesc_vote_lines( const routerinfo_t *ri, time_t now, smartlist_t *microdescriptors_out); -int vote_routerstatus_find_microdesc_hash(char *digest256_out, - const vote_routerstatus_t *vrs, - int method, - digest_algorithm_t alg); -document_signature_t *voter_get_sig_by_algorithm( - const networkstatus_voter_info_t *voter, - digest_algorithm_t alg); - +/* + * Exposed functions for unit tests. + */ #ifdef DIRVOTE_PRIVATE + +/* Cert manipulation */ +STATIC authority_cert_t *authority_cert_dup(authority_cert_t *cert); STATIC int32_t dirvote_get_intermediate_param_value( const smartlist_t *param_list, const char *keyword, @@ -212,6 +222,25 @@ STATIC int networkstatus_compute_bw_weights_v10(smartlist_t *chunks, int64_t G, int64_t M, int64_t E, int64_t D, int64_t T, int64_t weight_scale); +STATIC +char *networkstatus_compute_consensus(smartlist_t *votes, + int total_authorities, + crypto_pk_t *identity_key, + crypto_pk_t *signing_key, + const char *legacy_identity_key_digest, + crypto_pk_t *legacy_signing_key, + consensus_flavor_t flavor); +STATIC +int networkstatus_add_detached_signatures(networkstatus_t *target, + ns_detached_signatures_t *sigs, + const char *source, + int severity, + const char **msg_out); +STATIC +char *networkstatus_get_detached_signatures(smartlist_t *consensuses); +STATIC microdesc_t *dirvote_create_microdescriptor(const routerinfo_t *ri, + int consensus_method); + #endif /* defined(DIRVOTE_PRIVATE) */ #endif /* !defined(TOR_DIRVOTE_H) */ diff --git a/src/or/shared_random.c b/src/or/dirauth/shared_random.c index 13416d6bc7..7e49204159 100644 --- a/src/or/shared_random.c +++ b/src/or/dirauth/shared_random.c @@ -91,14 +91,17 @@ #include "shared_random.h" #include "config.h" #include "confparse.h" -#include "dirvote.h" +#include "dirvote_common.h" #include "networkstatus.h" #include "routerkeys.h" #include "router.h" #include "routerlist.h" #include "shared_random_state.h" +#include "shared_random_common.h" #include "util.h" +#include "dirauth/dirvote.h" + /* String prefix of shared random values in votes/consensuses. */ static const char previous_srv_str[] = "shared-rand-previous-value"; static const char current_srv_str[] = "shared-rand-current-value"; @@ -498,20 +501,6 @@ get_vote_line_from_commit(const sr_commit_t *commit, sr_phase_t phase) return vote_line; } -/* Convert a given srv object to a string for the control port. This doesn't - * fail and the srv object MUST be valid. */ -static char * -srv_to_control_string(const sr_srv_t *srv) -{ - char *srv_str; - char srv_hash_encoded[SR_SRV_VALUE_BASE64_LEN + 1]; - tor_assert(srv); - - sr_srv_encode(srv_hash_encoded, sizeof(srv_hash_encoded), srv); - tor_asprintf(&srv_str, "%s", srv_hash_encoded); - return srv_str; -} - /* Return a heap allocated string that contains the given <b>srv</b> string * representation formatted for a networkstatus document using the * <b>key</b> as the start of the line. This doesn't return NULL. */ @@ -874,27 +863,6 @@ get_majority_srv_from_votes(const smartlist_t *votes, int current) return the_srv; } -/* Encode the given shared random value and put it in dst. Destination - * buffer must be at least SR_SRV_VALUE_BASE64_LEN plus the NULL byte. */ -void -sr_srv_encode(char *dst, size_t dst_len, const sr_srv_t *srv) -{ - int ret; - /* Extra byte for the NULL terminated char. */ - char buf[SR_SRV_VALUE_BASE64_LEN + 1]; - - tor_assert(dst); - tor_assert(srv); - tor_assert(dst_len >= sizeof(buf)); - - ret = base64_encode(buf, sizeof(buf), (const char *) srv->value, - sizeof(srv->value), 0); - /* Always expect the full length without the NULL byte. */ - tor_assert(ret == (sizeof(buf) - 1)); - tor_assert(ret <= (int) dst_len); - strlcpy(dst, buf, dst_len); -} - /* Free a commit object. */ void sr_commit_free_(sr_commit_t *commit) @@ -1036,55 +1004,6 @@ sr_compute_srv(void) tor_free(reveals); } -/* Parse a list of arguments from a SRV value either from a vote, consensus - * or from our disk state and return a newly allocated srv object. NULL is - * returned on error. - * - * The arguments' order: - * num_reveals, value - */ -sr_srv_t * -sr_parse_srv(const smartlist_t *args) -{ - char *value; - int ok, ret; - uint64_t num_reveals; - sr_srv_t *srv = NULL; - - tor_assert(args); - - if (smartlist_len(args) < 2) { - goto end; - } - - /* First argument is the number of reveal values */ - num_reveals = tor_parse_uint64(smartlist_get(args, 0), - 10, 0, UINT64_MAX, &ok, NULL); - if (!ok) { - goto end; - } - /* Second and last argument is the shared random value it self. */ - value = smartlist_get(args, 1); - if (strlen(value) != SR_SRV_VALUE_BASE64_LEN) { - goto end; - } - - srv = tor_malloc_zero(sizeof(*srv)); - srv->num_reveals = num_reveals; - /* We subtract one byte from the srclen because the function ignores the - * '=' character in the given buffer. This is broken but it's a documented - * behavior of the implementation. */ - ret = base64_decode((char *) srv->value, sizeof(srv->value), value, - SR_SRV_VALUE_BASE64_LEN - 1); - if (ret != sizeof(srv->value)) { - tor_free(srv); - srv = NULL; - goto end; - } - end: - return srv; -} - /* Parse a commit from a vote or from our disk state and return a newly * allocated commit object. NULL is returned on error. * @@ -1352,84 +1271,6 @@ sr_save_and_cleanup(void) sr_cleanup(); } -/* Return the current SRV string representation for the control port. Return a - * newly allocated string on success containing the value else "" if not found - * or if we don't have a valid consensus yet. */ -char * -sr_get_current_for_control(void) -{ - char *srv_str; - const networkstatus_t *c = networkstatus_get_latest_consensus(); - if (c && c->sr_info.current_srv) { - srv_str = srv_to_control_string(c->sr_info.current_srv); - } else { - srv_str = tor_strdup(""); - } - return srv_str; -} - -/* Return the previous SRV string representation for the control port. Return - * a newly allocated string on success containing the value else "" if not - * found or if we don't have a valid consensus yet. */ -char * -sr_get_previous_for_control(void) -{ - char *srv_str; - const networkstatus_t *c = networkstatus_get_latest_consensus(); - if (c && c->sr_info.previous_srv) { - srv_str = srv_to_control_string(c->sr_info.previous_srv); - } else { - srv_str = tor_strdup(""); - } - return srv_str; -} - -/* Return current shared random value from the latest consensus. Caller can - * NOT keep a reference to the returned pointer. Return NULL if none. */ -const sr_srv_t * -sr_get_current(const networkstatus_t *ns) -{ - const networkstatus_t *consensus; - - /* Use provided ns else get a live one */ - if (ns) { - consensus = ns; - } else { - consensus = networkstatus_get_live_consensus(approx_time()); - } - /* Ideally we would never be asked for an SRV without a live consensus. Make - * sure this assumption is correct. */ - tor_assert_nonfatal(consensus); - - if (consensus) { - return consensus->sr_info.current_srv; - } - return NULL; -} - -/* Return previous shared random value from the latest consensus. Caller can - * NOT keep a reference to the returned pointer. Return NULL if none. */ -const sr_srv_t * -sr_get_previous(const networkstatus_t *ns) -{ - const networkstatus_t *consensus; - - /* Use provided ns else get a live one */ - if (ns) { - consensus = ns; - } else { - consensus = networkstatus_get_live_consensus(approx_time()); - } - /* Ideally we would never be asked for an SRV without a live consensus. Make - * sure this assumption is correct. */ - tor_assert_nonfatal(consensus); - - if (consensus) { - return consensus->sr_info.previous_srv; - } - return NULL; -} - #ifdef TOR_UNIT_TESTS /* Set the global value of number of SRV agreements so the test can play diff --git a/src/or/shared_random.h b/src/or/dirauth/shared_random.h index 675a8d8b06..1778ce8f09 100644 --- a/src/or/shared_random.h +++ b/src/or/dirauth/shared_random.h @@ -101,21 +101,48 @@ typedef struct sr_commit_t { /* API */ -/* Public methods: */ +/* Public methods used _outside_ of the module. + * + * We need to nullify them if the module is disabled. */ +#ifdef HAVE_MODULE_DIRAUTH int sr_init(int save_to_disk); void sr_save_and_cleanup(void); void sr_act_post_consensus(const networkstatus_t *consensus); + +#else /* HAVE_MODULE_DIRAUTH */ + +static inline int +sr_init(int save_to_disk) +{ + (void) save_to_disk; + /* Always return success. */ + return 0; +} + +static inline void +sr_save_and_cleanup(void) +{ +} + +static inline void +sr_act_post_consensus(const networkstatus_t *consensus) +{ + (void) consensus; +} + +#endif /* HAVE_MODULE_DIRAUTH */ + +/* Public methods used only by dirauth code. */ + void sr_handle_received_commits(smartlist_t *commits, crypto_pk_t *voter_key); sr_commit_t *sr_parse_commit(const smartlist_t *args); -sr_srv_t *sr_parse_srv(const smartlist_t *args); char *sr_get_string_for_vote(void); char *sr_get_string_for_consensus(const smartlist_t *votes, int32_t num_srv_agreements); void sr_commit_free_(sr_commit_t *commit); #define sr_commit_free(sr) FREE_AND_NULL(sr_commit_t, sr_commit_free_, (sr)) -void sr_srv_encode(char *dst, size_t dst_len, const sr_srv_t *srv); /* Private methods (only used by shared_random_state.c): */ static inline @@ -128,12 +155,6 @@ void sr_compute_srv(void); sr_commit_t *sr_generate_our_commit(time_t timestamp, const authority_cert_t *my_rsa_cert); -char *sr_get_current_for_control(void); -char *sr_get_previous_for_control(void); - -const sr_srv_t *sr_get_current(const networkstatus_t *ns); -const sr_srv_t *sr_get_previous(const networkstatus_t *ns); - #ifdef SHARED_RANDOM_PRIVATE /* Encode */ diff --git a/src/or/shared_random_state.c b/src/or/dirauth/shared_random_state.c index 53782af59a..ac8fb5c151 100644 --- a/src/or/shared_random_state.c +++ b/src/or/dirauth/shared_random_state.c @@ -14,10 +14,13 @@ #include "shared_random.h" #include "config.h" #include "confparse.h" -#include "dirvote.h" +#include "dirvote_common.h" #include "networkstatus.h" #include "router.h" #include "shared_random_state.h" +#include "shared_random_common.h" + +#include "dirauth/dirvote.h" /* Default filename of the shared random state on disk. */ static const char default_fname[] = "sr-state"; @@ -53,10 +56,6 @@ DUMMY_TYPECHECK_INSTANCE(sr_disk_state_t); VAR(#member, conftype, member, initvalue) /* Our persistent state magic number. */ #define SR_DISK_STATE_MAGIC 0x98AB1254 -/* Each protocol phase has 12 rounds */ -#define SHARED_RANDOM_N_ROUNDS 12 -/* Number of phase we have in a protocol. */ -#define SHARED_RANDOM_N_PHASES 2 static int disk_state_validate_cb(void *old_state, void *state, void *default_state, @@ -115,81 +114,6 @@ get_phase_str(sr_phase_t phase) return the_string; } - -/* Return the voting interval of the tor vote subsystem. */ -static int -get_voting_interval(void) -{ - int interval; - networkstatus_t *consensus = networkstatus_get_live_consensus(time(NULL)); - - if (consensus) { - interval = (int)(consensus->fresh_until - consensus->valid_after); - } else { - /* Same for both a testing and real network. We voluntarily ignore the - * InitialVotingInterval since it complexifies things and it doesn't - * affect the SR protocol. */ - interval = get_options()->V3AuthVotingInterval; - } - tor_assert(interval > 0); - return interval; -} - -/* Given the time <b>now</b>, return the start time of the current round of - * the SR protocol. For example, if it's 23:47:08, the current round thus - * started at 23:47:00 for a voting interval of 10 seconds. */ -STATIC time_t -get_start_time_of_current_round(void) -{ - const or_options_t *options = get_options(); - int voting_interval = get_voting_interval(); - /* First, get the start time of the next round */ - time_t next_start = dirvote_get_next_valid_after_time(); - /* Now roll back next_start by a voting interval to find the start time of - the current round. */ - time_t curr_start = dirvote_get_start_of_next_interval( - next_start - voting_interval - 1, - voting_interval, - options->TestingV3AuthVotingStartOffset); - return curr_start; -} - -/** Return the start time of the current SR protocol run. For example, if the - * time is 23/06/2017 23:47:08 and a full SR protocol run is 24 hours, this - * function should return 23/06/2017 00:00:00. */ -time_t -sr_state_get_start_time_of_current_protocol_run(time_t now) -{ - int total_rounds = SHARED_RANDOM_N_ROUNDS * SHARED_RANDOM_N_PHASES; - int voting_interval = get_voting_interval(); - /* Find the time the current round started. */ - time_t beginning_of_current_round = get_start_time_of_current_round(); - - /* Get current SR protocol round */ - int current_round = (now / voting_interval) % total_rounds; - - /* Get start time by subtracting the time elapsed from the beginning of the - protocol run */ - time_t time_elapsed_since_start_of_run = current_round * voting_interval; - return beginning_of_current_round - time_elapsed_since_start_of_run; -} - -/** Return the time (in seconds) it takes to complete a full SR protocol phase - * (e.g. the commit phase). */ -unsigned int -sr_state_get_phase_duration(void) -{ - return SHARED_RANDOM_N_ROUNDS * get_voting_interval(); -} - -/** Return the time (in seconds) it takes to complete a full SR protocol run */ -unsigned int -sr_state_get_protocol_run_duration(void) -{ - int total_protocol_rounds = SHARED_RANDOM_N_ROUNDS * SHARED_RANDOM_N_PHASES; - return total_protocol_rounds * get_voting_interval(); -} - /* Return the time we should expire the state file created at <b>now</b>. * We expire the state file in the beginning of the next protocol run. */ STATIC time_t diff --git a/src/or/shared_random_state.h b/src/or/dirauth/shared_random_state.h index fdbbf4919a..60a326f86c 100644 --- a/src/or/shared_random_state.h +++ b/src/or/dirauth/shared_random_state.h @@ -121,16 +121,11 @@ int sr_state_is_initialized(void); void sr_state_save(void); void sr_state_free_all(void); -time_t sr_state_get_start_time_of_current_protocol_run(time_t now); -unsigned int sr_state_get_phase_duration(void); -unsigned int sr_state_get_protocol_run_duration(void); - #ifdef SHARED_RANDOM_STATE_PRIVATE STATIC int disk_state_load_from_disk_impl(const char *fname); STATIC sr_phase_t get_sr_protocol_phase(time_t valid_after); -STATIC time_t get_start_time_of_current_round(void); STATIC time_t get_state_valid_until_time(time_t now); STATIC const char *get_phase_str(sr_phase_t phase); diff --git a/src/or/directory.c b/src/or/directory.c index 6d38f752a2..2c5ee23f3a 100644 --- a/src/or/directory.c +++ b/src/or/directory.c @@ -20,7 +20,6 @@ #include "compat.h" #include "directory.h" #include "dirserv.h" -#include "dirvote.h" #include "entrynodes.h" #include "geoip.h" #include "hs_cache.h" @@ -41,7 +40,7 @@ #include "routerlist.h" #include "routerparse.h" #include "routerset.h" -#include "shared_random.h" +#include "dirauth/shared_random.h" #if defined(EXPORTMALLINFO) && defined(HAVE_MALLOC_H) && defined(HAVE_MALLINFO) #if !defined(OpenBSD) @@ -49,6 +48,8 @@ #endif #endif +#include "dirauth/dirvote.h" + /** * \file directory.c * \brief Code to send and fetch information from directory authorities and @@ -4437,59 +4438,15 @@ handle_get_status_vote(dir_connection_t *conn, const get_handler_args_t *args) { const char *url = args->url; { - int current; ssize_t body_len = 0; ssize_t estimated_len = 0; + int lifetime = 60; /* XXXX?? should actually use vote intervals. */ /* This smartlist holds strings that we can compress on the fly. */ smartlist_t *items = smartlist_new(); /* This smartlist holds cached_dir_t objects that have a precompressed * deflated version. */ smartlist_t *dir_items = smartlist_new(); - int lifetime = 60; /* XXXX?? should actually use vote intervals. */ - url += strlen("/tor/status-vote/"); - current = !strcmpstart(url, "current/"); - url = strchr(url, '/'); - tor_assert(url); - ++url; - if (!strcmp(url, "consensus")) { - const char *item; - tor_assert(!current); /* we handle current consensus specially above, - * since it wants to be spooled. */ - if ((item = dirvote_get_pending_consensus(FLAV_NS))) - smartlist_add(items, (char*)item); - } else if (!current && !strcmp(url, "consensus-signatures")) { - /* XXXX the spec says that we should implement - * current/consensus-signatures too. It doesn't seem to be needed, - * though. */ - const char *item; - if ((item=dirvote_get_pending_detached_signatures())) - smartlist_add(items, (char*)item); - } else if (!strcmp(url, "authority")) { - const cached_dir_t *d; - int flags = DGV_BY_ID | - (current ? DGV_INCLUDE_PREVIOUS : DGV_INCLUDE_PENDING); - if ((d=dirvote_get_vote(NULL, flags))) - smartlist_add(dir_items, (cached_dir_t*)d); - } else { - const cached_dir_t *d; - smartlist_t *fps = smartlist_new(); - int flags; - if (!strcmpstart(url, "d/")) { - url += 2; - flags = DGV_INCLUDE_PENDING | DGV_INCLUDE_PREVIOUS; - } else { - flags = DGV_BY_ID | - (current ? DGV_INCLUDE_PREVIOUS : DGV_INCLUDE_PENDING); - } - dir_split_resource_into_fingerprints(url, fps, NULL, - DSR_HEX|DSR_SORT_UNIQ); - SMARTLIST_FOREACH(fps, char *, fp, { - if ((d = dirvote_get_vote(fp, flags))) - smartlist_add(dir_items, (cached_dir_t*)d); - tor_free(fp); - }); - smartlist_free(fps); - } + dirvote_dirreq_get_status_vote(url, items, dir_items); if (!smartlist_len(dir_items) && !smartlist_len(items)) { write_short_http_response(conn, 404, "Not found"); goto vote_done; diff --git a/src/or/dirserv.c b/src/or/dirserv.c index 68727f0718..e7b55b152b 100644 --- a/src/or/dirserv.c +++ b/src/or/dirserv.c @@ -18,7 +18,7 @@ #include "control.h" #include "directory.h" #include "dirserv.h" -#include "dirvote.h" +#include "dirvote_common.h" #include "hibernate.h" #include "keypin.h" #include "main.h" @@ -34,6 +34,8 @@ #include "routerset.h" #include "torcert.h" +#include "dirauth/dirvote.h" + /** * \file dirserv.c * \brief Directory server core implementation. Manages directory @@ -74,7 +76,6 @@ static int routers_with_measured_bw = 0; static void directory_remove_invalid(void); -static char *format_versions_list(config_line_t *ln); struct authdir_config_t; static uint32_t dirserv_get_status_impl(const char *fp, const char *nickname, @@ -87,7 +88,6 @@ static const signed_descriptor_t *get_signed_descriptor_by_fp( int extrainfo); static was_router_added_t dirserv_add_extrainfo(extrainfo_t *ei, const char **msg); -static uint32_t dirserv_get_bandwidth_for_router_kb(const routerinfo_t *ri); static uint32_t dirserv_get_credible_bandwidth_kb(const routerinfo_t *ri); static int spooled_resource_lookup_body(const spooled_resource_t *spooled, @@ -921,7 +921,7 @@ list_single_server_status(const routerinfo_t *desc, int is_live) } /* DOCDOC running_long_enough_to_decide_unreachable */ -static inline int +int running_long_enough_to_decide_unreachable(void) { return time_of_process_start @@ -1056,28 +1056,6 @@ list_server_status_v1(smartlist_t *routers, char **router_status_out, return 0; } -/** Given a (possibly empty) list of config_line_t, each line of which contains - * a list of comma-separated version numbers surrounded by optional space, - * allocate and return a new string containing the version numbers, in order, - * separated by commas. Used to generate Recommended(Client|Server)?Versions - */ -static char * -format_versions_list(config_line_t *ln) -{ - smartlist_t *versions; - char *result; - versions = smartlist_new(); - for ( ; ln; ln = ln->next) { - smartlist_split_string(versions, ln->value, ",", - SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); - } - sort_version_list(versions, 1); - result = smartlist_join_strings(versions,",",0,NULL); - SMARTLIST_FOREACH(versions,char *,s,tor_free(s)); - smartlist_free(versions); - return result; -} - /** Return 1 if <b>ri</b>'s descriptor is "active" -- running, valid, * not hibernating, having observed bw greater 0, and not too old. Else * return 0. @@ -1467,6 +1445,24 @@ router_counts_toward_thresholds(const node_t *node, time_t now, (have_mbw || !require_mbw); } +/** 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); +} + /** Look through the routerlist, the Mean Time Between Failure history, and * the Weighted Fractional Uptime history, and use them to set thresholds for * the Stable, Fast, and Guard flags. Update the fields stable_uptime, @@ -1474,7 +1470,7 @@ router_counts_toward_thresholds(const node_t *node, time_t now, * guard_bandwidth_including_exits, and guard_bandwidth_excluding_exits. * * Also, set the is_exit flag of each router appropriately. */ -static void +void dirserv_compute_performance_thresholds(digestmap_t *omit_as_sybil) { int n_active, n_active_nonexit, n_familiar; @@ -1705,7 +1701,7 @@ dirserv_cache_measured_bw(const measured_bw_line_t *parsed_line, } /** Clear and free the measured bandwidth cache */ -STATIC void +void dirserv_clear_measured_bw_cache(void) { if (mbw_cache) { @@ -1737,18 +1733,10 @@ dirserv_expire_measured_bw_cache(time_t now) } } -/** Get the current size of the measured bandwidth cache */ -STATIC int -dirserv_get_measured_bw_cache_size(void) -{ - if (mbw_cache) return digestmap_size(mbw_cache); - else return 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. */ -STATIC int +int dirserv_query_measured_bw_cache_kb(const char *node_id, long *bw_kb_out, time_t *as_of_out) { @@ -1769,61 +1757,18 @@ dirserv_query_measured_bw_cache_kb(const char *node_id, long *bw_kb_out, } /** Predicate wrapper for dirserv_query_measured_bw_cache() */ -STATIC int +int dirserv_has_measured_bw(const char *node_id) { return dirserv_query_measured_bw_cache_kb(node_id, NULL, NULL); } -/** Get the best estimate of a router's bandwidth for dirauth purposes, - * preferring measured to advertised values if available. */ - -static uint32_t -dirserv_get_bandwidth_for_router_kb(const routerinfo_t *ri) -{ - uint32_t bw_kb = 0; - /* - * Yeah, measured bandwidths in measured_bw_line_t are (implicitly - * signed) longs and the ones router_get_advertised_bandwidth() returns - * are uint32_t. - */ - long mbw_kb = 0; - - if (ri) { - /* - * * First try to see if we have a measured bandwidth; don't bother with - * as_of_out here, on the theory that a stale measured bandwidth is still - * better to trust than an advertised one. - */ - if (dirserv_query_measured_bw_cache_kb(ri->cache_info.identity_digest, - &mbw_kb, NULL)) { - /* Got one! */ - bw_kb = (uint32_t)mbw_kb; - } else { - /* If not, fall back to advertised */ - bw_kb = router_get_advertised_bandwidth(ri) / 1000; - } - } - - return bw_kb; -} - -/** 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. */ -static void -dirserv_count_measured_bws(const smartlist_t *routers) +/** Get the current size of the measured bandwidth cache */ +int +dirserv_get_measured_bw_cache_size(void) { - /* 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); + if (mbw_cache) return digestmap_size(mbw_cache); + else return 0; } /** Return the bandwidth we believe for assigning flags; prefer measured @@ -1886,26 +1831,6 @@ dirserv_get_flag_thresholds_line(void) return result; } -/** Given a platform string as in a routerinfo_t (possibly null), return a - * newly allocated version string for a networkstatus document, or NULL if the - * platform doesn't give a Tor version. */ -static char * -version_from_platform(const char *platform) -{ - if (platform && !strcmpstart(platform, "Tor ")) { - const char *eos = find_whitespace(platform+4); - if (eos && !strcmpstart(eos, " (r")) { - /* XXXX Unify this logic with the other version extraction - * logic in routerparse.c. */ - eos = find_whitespace(eos+1); - } - if (eos) { - return tor_strndup(platform, eos-platform); - } - } - return NULL; -} - /** Helper: write the router-status information in <b>rs</b> into a newly * allocated character buffer. Use the same format as in network-status * documents. If <b>version</b> is non-NULL, add a "v" line for the platform. @@ -2094,145 +2019,6 @@ routerstatus_format_entry(const routerstatus_t *rs, const char *version, return result; } -/** Helper for sorting: compares two routerinfos first by address, and then by - * descending order of "usefulness". (An authority is more useful than a - * non-authority; a running router is more useful than a non-running router; - * and a router with more bandwidth is more useful than one with less.) - **/ -static int -compare_routerinfo_by_ip_and_bw_(const void **a, const void **b) -{ - routerinfo_t *first = *(routerinfo_t **)a, *second = *(routerinfo_t **)b; - int first_is_auth, second_is_auth; - uint32_t bw_kb_first, bw_kb_second; - const node_t *node_first, *node_second; - int first_is_running, second_is_running; - - /* we return -1 if first should appear before second... that is, - * if first is a better router. */ - if (first->addr < second->addr) - return -1; - else if (first->addr > second->addr) - return 1; - - /* Potentially, this next bit could cause k n lg n memeq calls. But in - * reality, we will almost never get here, since addresses will usually be - * different. */ - - first_is_auth = - router_digest_is_trusted_dir(first->cache_info.identity_digest); - second_is_auth = - router_digest_is_trusted_dir(second->cache_info.identity_digest); - - if (first_is_auth && !second_is_auth) - return -1; - else if (!first_is_auth && second_is_auth) - return 1; - - node_first = node_get_by_id(first->cache_info.identity_digest); - node_second = node_get_by_id(second->cache_info.identity_digest); - first_is_running = node_first && node_first->is_running; - second_is_running = node_second && node_second->is_running; - - if (first_is_running && !second_is_running) - return -1; - else if (!first_is_running && second_is_running) - return 1; - - bw_kb_first = dirserv_get_bandwidth_for_router_kb(first); - bw_kb_second = dirserv_get_bandwidth_for_router_kb(second); - - if (bw_kb_first > bw_kb_second) - return -1; - else if (bw_kb_first < bw_kb_second) - return 1; - - /* They're equal! Compare by identity digest, so there's a - * deterministic order and we avoid flapping. */ - return fast_memcmp(first->cache_info.identity_digest, - second->cache_info.identity_digest, - DIGEST_LEN); -} - -/** Given a list of routerinfo_t in <b>routers</b>, return a new digestmap_t - * whose keys are the identity digests of those routers that we're going to - * exclude for Sybil-like appearance. */ -static digestmap_t * -get_possible_sybil_list(const smartlist_t *routers) -{ - const or_options_t *options = get_options(); - digestmap_t *omit_as_sybil; - smartlist_t *routers_by_ip = smartlist_new(); - uint32_t last_addr; - int addr_count; - /* Allow at most this number of Tor servers on a single IP address, ... */ - int max_with_same_addr = options->AuthDirMaxServersPerAddr; - if (max_with_same_addr <= 0) - max_with_same_addr = INT_MAX; - - smartlist_add_all(routers_by_ip, routers); - smartlist_sort(routers_by_ip, compare_routerinfo_by_ip_and_bw_); - omit_as_sybil = digestmap_new(); - - last_addr = 0; - addr_count = 0; - SMARTLIST_FOREACH_BEGIN(routers_by_ip, routerinfo_t *, ri) { - if (last_addr != ri->addr) { - last_addr = ri->addr; - addr_count = 1; - } else if (++addr_count > max_with_same_addr) { - digestmap_set(omit_as_sybil, ri->cache_info.identity_digest, ri); - } - } SMARTLIST_FOREACH_END(ri); - - smartlist_free(routers_by_ip); - return omit_as_sybil; -} - -/** If there are entries in <b>routers</b> with exactly the same ed25519 keys, - * remove the older one. If they are exactly the same age, remove the one - * with the greater descriptor digest. May alter the order of the list. */ -static void -routers_make_ed_keys_unique(smartlist_t *routers) -{ - routerinfo_t *ri2; - digest256map_t *by_ed_key = digest256map_new(); - - SMARTLIST_FOREACH_BEGIN(routers, routerinfo_t *, ri) { - ri->omit_from_vote = 0; - if (ri->cache_info.signing_key_cert == NULL) - continue; /* No ed key */ - const uint8_t *pk = ri->cache_info.signing_key_cert->signing_key.pubkey; - if ((ri2 = digest256map_get(by_ed_key, pk))) { - /* Duplicate; must omit one. Set the omit_from_vote flag in whichever - * one has the earlier published_on. */ - const time_t ri_pub = ri->cache_info.published_on; - const time_t ri2_pub = ri2->cache_info.published_on; - if (ri2_pub < ri_pub || - (ri2_pub == ri_pub && - fast_memcmp(ri->cache_info.signed_descriptor_digest, - ri2->cache_info.signed_descriptor_digest,DIGEST_LEN)<0)) { - digest256map_set(by_ed_key, pk, ri); - ri2->omit_from_vote = 1; - } else { - ri->omit_from_vote = 1; - } - } else { - /* Add to map */ - digest256map_set(by_ed_key, pk, ri); - } - } SMARTLIST_FOREACH_END(ri); - - digest256map_free(by_ed_key, NULL); - - /* Now remove every router where the omit_from_vote flag got set. */ - SMARTLIST_FOREACH_BEGIN(routers, const routerinfo_t *, ri) { - if (ri->omit_from_vote) { - SMARTLIST_DEL_CURRENT(routers, ri); - } - } SMARTLIST_FOREACH_END(ri); -} - /** Extract status information from <b>ri</b> and from other authority * functions and store it in <b>rs</b>. <b>rs</b> is zeroed out before it is * set. @@ -2345,25 +2131,6 @@ dirserv_set_routerstatus_testing(routerstatus_t *rs) } } -/** Routerstatus <b>rs</b> is part of a group of routers that are on - * too narrow an IP-space. Clear out its flags since we don't want it be used - * because of its Sybil-like appearance. - * - * Leave its BadExit flag alone though, since if we think it's a bad exit, - * we want to vote that way in case all the other authorities are voting - * Running and Exit. - */ -static void -clear_status_flags_on_sybil(routerstatus_t *rs) -{ - rs->is_authority = rs->is_exit = rs->is_stable = rs->is_fast = - rs->is_flagged_running = rs->is_named = rs->is_valid = - rs->is_hs_dir = rs->is_v2_dir = rs->is_possible_guard = 0; - /* FFFF we might want some mechanism to check later on if we - * missed zeroing any flags: it's easy to add a new flag but - * forget to add it to this clause. */ -} - /** The guardfraction of the guard with identity fingerprint <b>guard_id</b> * is <b>guardfraction_percentage</b>. See if we have a vote routerstatus for * this guard in <b>vote_routerstatuses</b>, and if we do, register the @@ -2857,286 +2624,6 @@ dirserv_read_measured_bandwidths(const char *from_file, return 0; } -/** Return a new networkstatus_t* containing our current opinion. (For v3 - * authorities) */ -networkstatus_t * -dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, - authority_cert_t *cert) -{ - const or_options_t *options = get_options(); - networkstatus_t *v3_out = NULL; - uint32_t addr; - char *hostname = NULL, *client_versions = NULL, *server_versions = NULL; - const char *contact; - smartlist_t *routers, *routerstatuses; - char identity_digest[DIGEST_LEN]; - char signing_key_digest[DIGEST_LEN]; - int listbadexits = options->AuthDirListBadExits; - routerlist_t *rl = router_get_routerlist(); - time_t now = time(NULL); - time_t cutoff = now - ROUTER_MAX_AGE_TO_PUBLISH; - networkstatus_voter_info_t *voter = NULL; - vote_timing_t timing; - digestmap_t *omit_as_sybil = NULL; - const int vote_on_reachability = running_long_enough_to_decide_unreachable(); - smartlist_t *microdescriptors = NULL; - - tor_assert(private_key); - tor_assert(cert); - - if (crypto_pk_get_digest(private_key, signing_key_digest)<0) { - log_err(LD_BUG, "Error computing signing key digest"); - return NULL; - } - if (crypto_pk_get_digest(cert->identity_key, identity_digest)<0) { - log_err(LD_BUG, "Error computing identity key digest"); - return NULL; - } - if (resolve_my_address(LOG_WARN, options, &addr, NULL, &hostname)<0) { - log_warn(LD_NET, "Couldn't resolve my hostname"); - return NULL; - } - if (!hostname || !strchr(hostname, '.')) { - tor_free(hostname); - hostname = tor_dup_ip(addr); - } - - if (options->VersioningAuthoritativeDir) { - client_versions = format_versions_list(options->RecommendedClientVersions); - server_versions = format_versions_list(options->RecommendedServerVersions); - } - - contact = get_options()->ContactInfo; - if (!contact) - contact = "(none)"; - - /* - * Do this so dirserv_compute_performance_thresholds() and - * set_routerstatus_from_routerinfo() see up-to-date bandwidth info. - */ - if (options->V3BandwidthsFile) { - dirserv_read_measured_bandwidths(options->V3BandwidthsFile, NULL); - } else { - /* - * No bandwidths file; clear the measured bandwidth cache in case we had - * one last time around. - */ - if (dirserv_get_measured_bw_cache_size() > 0) { - dirserv_clear_measured_bw_cache(); - } - } - - /* precompute this part, since we need it to decide what "stable" - * means. */ - SMARTLIST_FOREACH(rl->routers, routerinfo_t *, ri, { - dirserv_set_router_is_running(ri, now); - }); - - routers = smartlist_new(); - smartlist_add_all(routers, rl->routers); - routers_make_ed_keys_unique(routers); - /* After this point, don't use rl->routers; use 'routers' instead. */ - routers_sort_by_identity(routers); - omit_as_sybil = get_possible_sybil_list(routers); - - DIGESTMAP_FOREACH(omit_as_sybil, sybil_id, void *, ignore) { - (void) ignore; - rep_hist_make_router_pessimal(sybil_id, now); - } DIGESTMAP_FOREACH_END; - - /* Count how many have measured bandwidths so we know how to assign flags; - * this must come before dirserv_compute_performance_thresholds() */ - dirserv_count_measured_bws(routers); - - dirserv_compute_performance_thresholds(omit_as_sybil); - - routerstatuses = smartlist_new(); - microdescriptors = smartlist_new(); - - SMARTLIST_FOREACH_BEGIN(routers, routerinfo_t *, ri) { - if (ri->cache_info.published_on >= cutoff) { - routerstatus_t *rs; - vote_routerstatus_t *vrs; - node_t *node = node_get_mutable_by_id(ri->cache_info.identity_digest); - if (!node) - continue; - - vrs = tor_malloc_zero(sizeof(vote_routerstatus_t)); - rs = &vrs->status; - set_routerstatus_from_routerinfo(rs, node, ri, now, - listbadexits); - - if (ri->cache_info.signing_key_cert) { - memcpy(vrs->ed25519_id, - ri->cache_info.signing_key_cert->signing_key.pubkey, - ED25519_PUBKEY_LEN); - } - - if (digestmap_get(omit_as_sybil, ri->cache_info.identity_digest)) - clear_status_flags_on_sybil(rs); - - if (!vote_on_reachability) - rs->is_flagged_running = 0; - - vrs->version = version_from_platform(ri->platform); - if (ri->protocol_list) { - vrs->protocols = tor_strdup(ri->protocol_list); - } else { - vrs->protocols = tor_strdup( - protover_compute_for_old_tor(vrs->version)); - } - vrs->microdesc = dirvote_format_all_microdesc_vote_lines(ri, now, - microdescriptors); - - smartlist_add(routerstatuses, vrs); - } - } SMARTLIST_FOREACH_END(ri); - - { - smartlist_t *added = - microdescs_add_list_to_cache(get_microdesc_cache(), - microdescriptors, SAVED_NOWHERE, 0); - smartlist_free(added); - smartlist_free(microdescriptors); - } - - smartlist_free(routers); - digestmap_free(omit_as_sybil, NULL); - - /* Apply guardfraction information to routerstatuses. */ - if (options->GuardfractionFile) { - dirserv_read_guardfraction_file(options->GuardfractionFile, - routerstatuses); - } - - /* This pass through applies the measured bw lines to the routerstatuses */ - if (options->V3BandwidthsFile) { - dirserv_read_measured_bandwidths(options->V3BandwidthsFile, - routerstatuses); - } else { - /* - * No bandwidths file; clear the measured bandwidth cache in case we had - * one last time around. - */ - if (dirserv_get_measured_bw_cache_size() > 0) { - dirserv_clear_measured_bw_cache(); - } - } - - v3_out = tor_malloc_zero(sizeof(networkstatus_t)); - - v3_out->type = NS_TYPE_VOTE; - dirvote_get_preferred_voting_intervals(&timing); - v3_out->published = now; - { - char tbuf[ISO_TIME_LEN+1]; - networkstatus_t *current_consensus = - networkstatus_get_live_consensus(now); - long last_consensus_interval; /* only used to pick a valid_after */ - if (current_consensus) - last_consensus_interval = current_consensus->fresh_until - - current_consensus->valid_after; - else - last_consensus_interval = options->TestingV3AuthInitialVotingInterval; - v3_out->valid_after = - dirvote_get_start_of_next_interval(now, (int)last_consensus_interval, - options->TestingV3AuthVotingStartOffset); - format_iso_time(tbuf, v3_out->valid_after); - log_notice(LD_DIR,"Choosing valid-after time in vote as %s: " - "consensus_set=%d, last_interval=%d", - tbuf, current_consensus?1:0, (int)last_consensus_interval); - } - v3_out->fresh_until = v3_out->valid_after + timing.vote_interval; - v3_out->valid_until = v3_out->valid_after + - (timing.vote_interval * timing.n_intervals_valid); - v3_out->vote_seconds = timing.vote_delay; - v3_out->dist_seconds = timing.dist_delay; - tor_assert(v3_out->vote_seconds > 0); - tor_assert(v3_out->dist_seconds > 0); - tor_assert(timing.n_intervals_valid > 0); - - v3_out->client_versions = client_versions; - v3_out->server_versions = server_versions; - - /* These are hardwired, to avoid disaster. */ - v3_out->recommended_relay_protocols = - tor_strdup("Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 " - "Link=4 LinkAuth=1 Microdesc=1-2 Relay=2"); - v3_out->recommended_client_protocols = - tor_strdup("Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 " - "Link=4 LinkAuth=1 Microdesc=1-2 Relay=2"); - v3_out->required_client_protocols = - tor_strdup("Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 " - "Link=4 LinkAuth=1 Microdesc=1-2 Relay=2"); - v3_out->required_relay_protocols = - tor_strdup("Cons=1 Desc=1 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 " - "Link=3-4 LinkAuth=1 Microdesc=1 Relay=1-2"); - - /* We are not allowed to vote to require anything we don't have. */ - tor_assert(protover_all_supported(v3_out->required_relay_protocols, NULL)); - tor_assert(protover_all_supported(v3_out->required_client_protocols, NULL)); - - /* We should not recommend anything we don't have. */ - tor_assert_nonfatal(protover_all_supported( - v3_out->recommended_relay_protocols, NULL)); - tor_assert_nonfatal(protover_all_supported( - v3_out->recommended_client_protocols, NULL)); - - v3_out->package_lines = smartlist_new(); - { - config_line_t *cl; - for (cl = get_options()->RecommendedPackages; cl; cl = cl->next) { - if (validate_recommended_package_line(cl->value)) - smartlist_add_strdup(v3_out->package_lines, cl->value); - } - } - - v3_out->known_flags = smartlist_new(); - smartlist_split_string(v3_out->known_flags, - "Authority Exit Fast Guard Stable V2Dir Valid HSDir", - 0, SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); - if (vote_on_reachability) - smartlist_add_strdup(v3_out->known_flags, "Running"); - if (listbadexits) - smartlist_add_strdup(v3_out->known_flags, "BadExit"); - smartlist_sort_strings(v3_out->known_flags); - - if (options->ConsensusParams) { - v3_out->net_params = smartlist_new(); - smartlist_split_string(v3_out->net_params, - options->ConsensusParams, NULL, 0, 0); - smartlist_sort_strings(v3_out->net_params); - } - - voter = tor_malloc_zero(sizeof(networkstatus_voter_info_t)); - voter->nickname = tor_strdup(options->Nickname); - memcpy(voter->identity_digest, identity_digest, DIGEST_LEN); - voter->sigs = smartlist_new(); - voter->address = hostname; - voter->addr = addr; - voter->dir_port = router_get_advertised_dir_port(options, 0); - voter->or_port = router_get_advertised_or_port(options); - voter->contact = tor_strdup(contact); - if (options->V3AuthUseLegacyKey) { - authority_cert_t *c = get_my_v3_legacy_cert(); - if (c) { - if (crypto_pk_get_digest(c->identity_key, voter->legacy_id_digest)) { - log_warn(LD_BUG, "Unable to compute digest of legacy v3 identity key"); - memset(voter->legacy_id_digest, 0, DIGEST_LEN); - } - } - } - - v3_out->voters = smartlist_new(); - smartlist_add(v3_out->voters, voter); - v3_out->cert = authority_cert_dup(cert); - v3_out->routerstatus_list = routerstatuses; - /* Note: networkstatus_digest is unset; it won't get set until we actually - * format the vote. */ - - return v3_out; -} - /** As dirserv_get_routerdescs(), but instead of getting signed_descriptor_t * pointers, adds copies of digests to fps_out, and doesn't use the * /tor/server/ prefix. For a /d/ request, adds descriptor digests; for other diff --git a/src/or/dirserv.h b/src/or/dirserv.h index b9af68ff6e..f0b8913c5c 100644 --- a/src/or/dirserv.h +++ b/src/or/dirserv.h @@ -157,6 +157,15 @@ void cached_dir_decref(cached_dir_t *d); cached_dir_t *new_cached_dir(char *s, time_t published); int validate_recommended_package_line(const char *line); +int dirserv_query_measured_bw_cache_kb(const char *node_id, + long *bw_out, + time_t *as_of_out); +void dirserv_clear_measured_bw_cache(void); +int dirserv_has_measured_bw(const char *node_id); +int dirserv_get_measured_bw_cache_size(void); +void dirserv_count_measured_bws(const smartlist_t *routers); +int running_long_enough_to_decide_unreachable(void); +void dirserv_compute_performance_thresholds(digestmap_t *omit_as_sybil); #ifdef DIRSERV_PRIVATE @@ -172,13 +181,7 @@ STATIC int measured_bw_line_apply(measured_bw_line_t *parsed_line, STATIC void dirserv_cache_measured_bw(const measured_bw_line_t *parsed_line, time_t as_of); -STATIC void dirserv_clear_measured_bw_cache(void); STATIC void dirserv_expire_measured_bw_cache(time_t now); -STATIC int dirserv_get_measured_bw_cache_size(void); -STATIC int dirserv_query_measured_bw_cache_kb(const char *node_id, - long *bw_out, - time_t *as_of_out); -STATIC int dirserv_has_measured_bw(const char *node_id); STATIC int dirserv_read_guardfraction_file_from_str(const char *guardfraction_file_str, diff --git a/src/or/dirvote_common.c b/src/or/dirvote_common.c new file mode 100644 index 0000000000..a28a441d64 --- /dev/null +++ b/src/or/dirvote_common.c @@ -0,0 +1,195 @@ +/* Copyright (c) 2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file dirvote_common.c + * \brief This file contains functions that are from the directory authority + * subsystem related to voting specifically but used by many part of + * tor. The full feature is built as part of the dirauth module. + **/ + +#define DIRVOTE_COMMON_PRIVATE +#include "dirvote_common.h" + +#include "or.h" +#include "config.h" +#include "networkstatus.h" + +/* ===== + * Vote scheduling + * ===== */ + +/** Set *<b>timing_out</b> to the intervals at which we would like to vote. + * Note that these aren't the intervals we'll use to vote; they're the ones + * that we'll vote to use. */ +void +dirvote_get_preferred_voting_intervals(vote_timing_t *timing_out) +{ + const or_options_t *options = get_options(); + + tor_assert(timing_out); + + timing_out->vote_interval = options->V3AuthVotingInterval; + timing_out->n_intervals_valid = options->V3AuthNIntervalsValid; + timing_out->vote_delay = options->V3AuthVoteDelay; + timing_out->dist_delay = options->V3AuthDistDelay; +} + +/** Return the start of the next interval of size <b>interval</b> (in + * seconds) after <b>now</b>, plus <b>offset</b>. Midnight always + * starts a fresh interval, and if the last interval of a day would be + * truncated to less than half its size, it is rolled into the + * previous interval. */ +time_t +dirvote_get_start_of_next_interval(time_t now, int interval, int offset) +{ + struct tm tm; + time_t midnight_today=0; + time_t midnight_tomorrow; + time_t next; + + tor_gmtime_r(&now, &tm); + tm.tm_hour = 0; + tm.tm_min = 0; + tm.tm_sec = 0; + + if (tor_timegm(&tm, &midnight_today) < 0) { + log_warn(LD_BUG, "Ran into an invalid time when trying to find midnight."); + } + midnight_tomorrow = midnight_today + (24*60*60); + + next = midnight_today + ((now-midnight_today)/interval + 1)*interval; + + /* Intervals never cross midnight. */ + if (next > midnight_tomorrow) + next = midnight_tomorrow; + + /* If the interval would only last half as long as it's supposed to, then + * skip over to the next day. */ + if (next + interval/2 > midnight_tomorrow) + next = midnight_tomorrow; + + next += offset; + if (next - interval > now) + next -= interval; + + return next; +} + +/* Populate and return a new voting_schedule_t that can be used to schedule + * voting. The object is allocated on the heap and it's the responsibility of + * the caller to free it. Can't fail. */ +static voting_schedule_t * +get_voting_schedule(const or_options_t *options, time_t now, int severity) +{ + int interval, vote_delay, dist_delay; + time_t start; + time_t end; + networkstatus_t *consensus; + voting_schedule_t *new_voting_schedule; + + new_voting_schedule = tor_malloc_zero(sizeof(voting_schedule_t)); + + consensus = networkstatus_get_live_consensus(now); + + if (consensus) { + interval = (int)( consensus->fresh_until - consensus->valid_after ); + vote_delay = consensus->vote_seconds; + dist_delay = consensus->dist_seconds; + } else { + interval = options->TestingV3AuthInitialVotingInterval; + vote_delay = options->TestingV3AuthInitialVoteDelay; + dist_delay = options->TestingV3AuthInitialDistDelay; + } + + tor_assert(interval > 0); + + if (vote_delay + dist_delay > interval/2) + vote_delay = dist_delay = interval / 4; + + start = new_voting_schedule->interval_starts = + dirvote_get_start_of_next_interval(now,interval, + options->TestingV3AuthVotingStartOffset); + end = dirvote_get_start_of_next_interval(start+1, interval, + options->TestingV3AuthVotingStartOffset); + + tor_assert(end > start); + + new_voting_schedule->fetch_missing_signatures = start - (dist_delay/2); + new_voting_schedule->voting_ends = start - dist_delay; + new_voting_schedule->fetch_missing_votes = + start - dist_delay - (vote_delay/2); + new_voting_schedule->voting_starts = start - dist_delay - vote_delay; + + { + char tbuf[ISO_TIME_LEN+1]; + format_iso_time(tbuf, new_voting_schedule->interval_starts); + tor_log(severity, LD_DIR,"Choosing expected valid-after time as %s: " + "consensus_set=%d, interval=%d", + tbuf, consensus?1:0, interval); + } + + return new_voting_schedule; +} + +#define voting_schedule_free(s) \ + FREE_AND_NULL(voting_schedule_t, voting_schedule_free_, (s)) + +/** Frees a voting_schedule_t. This should be used instead of the generic + * tor_free. */ +static void +voting_schedule_free_(voting_schedule_t *voting_schedule_to_free) +{ + if (!voting_schedule_to_free) + return; + tor_free(voting_schedule_to_free); +} + +voting_schedule_t voting_schedule; + +/* Using the time <b>now</b>, return the next voting valid-after time. */ +time_t +dirvote_get_next_valid_after_time(void) +{ + /* This is a safe guard in order to make sure that the voting schedule + * static object is at least initialized. Using this function with a zeroed + * voting schedule can lead to bugs. */ + if (tor_mem_is_zero((const char *) &voting_schedule, + sizeof(voting_schedule))) { + dirvote_recalculate_timing(get_options(), time(NULL)); + voting_schedule.created_on_demand = 1; + } + return voting_schedule.interval_starts; +} + +/** Set voting_schedule to hold the timing for the next vote we should be + * doing. All type of tor do that because HS subsystem needs the timing as + * well to function properly. */ +void +dirvote_recalculate_timing(const or_options_t *options, time_t now) +{ + voting_schedule_t *new_voting_schedule; + + /* get the new voting schedule */ + new_voting_schedule = get_voting_schedule(options, now, LOG_INFO); + tor_assert(new_voting_schedule); + + /* Fill in the global static struct now */ + memcpy(&voting_schedule, new_voting_schedule, sizeof(voting_schedule)); + voting_schedule_free(new_voting_schedule); +} + +/** Return the signature made by <b>voter</b> using the algorithm + * <b>alg</b>, or NULL if none is found. */ +document_signature_t * +dirvote_get_voter_sig_by_alg(const networkstatus_voter_info_t *voter, + digest_algorithm_t alg) +{ + if (!voter->sigs) + return NULL; + SMARTLIST_FOREACH(voter->sigs, document_signature_t *, sig, + if (sig->alg == alg) + return sig); + return NULL; +} + diff --git a/src/or/dirvote_common.h b/src/or/dirvote_common.h new file mode 100644 index 0000000000..857b8497ee --- /dev/null +++ b/src/or/dirvote_common.h @@ -0,0 +1,66 @@ +/* Copyright (c) 2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file dirvote_common.h + * \brief Header file for dirvote_common.c. + **/ + +#ifndef TOR_DIRVOTE_COMMON_H +#define TOR_DIRVOTE_COMMON_H + +#include "or.h" + +/* Dirauth module. */ +#include "dirauth/dirvote.h" + +/** Scheduling information for a voting interval. */ +typedef struct { + /** When do we generate and distribute our vote for this interval? */ + time_t voting_starts; + /** When do we send an HTTP request for any votes that we haven't + * been posted yet?*/ + time_t fetch_missing_votes; + /** When do we give up on getting more votes and generate a consensus? */ + time_t voting_ends; + /** When do we send an HTTP request for any signatures we're expecting to + * see on the consensus? */ + time_t fetch_missing_signatures; + /** When do we publish the consensus? */ + time_t interval_starts; + + /* True iff we have generated and distributed our vote. */ + int have_voted; + /* True iff we've requested missing votes. */ + int have_fetched_missing_votes; + /* True iff we have built a consensus and sent the signatures around. */ + int have_built_consensus; + /* True iff we've fetched missing signatures. */ + int have_fetched_missing_signatures; + /* True iff we have published our consensus. */ + int have_published_consensus; + + /* True iff this voting schedule was set on demand meaning not through the + * normal vote operation of a dirauth or when a consensus is set. This only + * applies to a directory authority that needs to recalculate the voting + * timings only for the first vote even though this object was initilized + * prior to voting. */ + int created_on_demand; +} voting_schedule_t; + +/* Public API. */ + +extern voting_schedule_t voting_schedule; + +void dirvote_get_preferred_voting_intervals(vote_timing_t *timing_out); +time_t dirvote_get_start_of_next_interval(time_t now, + int interval, + int offset); +time_t dirvote_get_next_valid_after_time(void); + +document_signature_t *dirvote_get_voter_sig_by_alg( + const networkstatus_voter_info_t *voter, + digest_algorithm_t alg); + +#endif /* TOR_DIRVOTE_COMMON_H */ + diff --git a/src/or/hs_common.c b/src/or/hs_common.c index c01428099e..46903a2960 100644 --- a/src/or/hs_common.c +++ b/src/or/hs_common.c @@ -28,8 +28,8 @@ #include "rendservice.h" #include "routerset.h" #include "router.h" -#include "shared_random.h" -#include "shared_random_state.h" +#include "shared_random_common.h" +#include "dirauth/shared_random_state.h" /* Trunnel */ #include "ed25519_cert.h" diff --git a/src/or/hs_service.c b/src/or/hs_service.c index 4686eda0b1..c4a473a5f7 100644 --- a/src/or/hs_service.c +++ b/src/or/hs_service.c @@ -24,7 +24,7 @@ #include "router.h" #include "routerkeys.h" #include "routerlist.h" -#include "shared_random_state.h" +#include "shared_random_common.h" #include "statefile.h" #include "hs_circuit.h" diff --git a/src/or/include.am b/src/or/include.am index 9a68df5c3e..bb505937ac 100644 --- a/src/or/include.am +++ b/src/or/include.am @@ -41,10 +41,9 @@ LIBTOR_A_SOURCES = \ src/or/consdiffmgr.c \ src/or/control.c \ src/or/cpuworker.c \ - src/or/dircollate.c \ src/or/directory.c \ src/or/dirserv.c \ - src/or/dirvote.c \ + src/or/dirvote_common.c \ src/or/dns.c \ src/or/dnsserv.c \ src/or/dos.c \ @@ -76,8 +75,6 @@ LIBTOR_A_SOURCES = \ src/or/onion.c \ src/or/onion_fast.c \ src/or/onion_tap.c \ - src/or/shared_random.c \ - src/or/shared_random_state.c \ src/or/transports.c \ src/or/parsecommon.c \ src/or/periodic.c \ @@ -107,6 +104,7 @@ LIBTOR_A_SOURCES = \ src/or/scheduler.c \ src/or/scheduler_kist.c \ src/or/scheduler_vanilla.c \ + src/or/shared_random_common.c \ src/or/statefile.c \ src/or/status.c \ src/or/torcert.c \ @@ -114,8 +112,25 @@ LIBTOR_A_SOURCES = \ src/or/onion_ntor.c \ $(tor_platform_source) +# +# Modules are conditionnally compiled in tor starting here. We add the C files +# only if the modules has been enabled at configure time. We always add the +# source files of every module to libtor-testing.a so we can build the unit +# tests for everything. +# + +# The Directory Authority module. +MODULE_DIRAUTH_SOURCES = \ + src/or/dirauth/dircollate.c \ + src/or/dirauth/dirvote.c \ + src/or/dirauth/shared_random.c \ + src/or/dirauth/shared_random_state.c +if BUILD_MODULE_DIRAUTH +LIBTOR_A_SOURCES += $(MODULE_DIRAUTH_SOURCES) +endif + src_or_libtor_a_SOURCES = $(LIBTOR_A_SOURCES) -src_or_libtor_testing_a_SOURCES = $(LIBTOR_A_SOURCES) +src_or_libtor_testing_a_SOURCES = $(LIBTOR_A_SOURCES) $(MODULE_DIRAUTH_SOURCES) src_or_tor_SOURCES = src/or/tor_main.c AM_CPPFLAGS += -I$(srcdir)/src/or -Isrc/or @@ -185,10 +200,9 @@ ORHEADERS = \ src/or/consdiffmgr.h \ src/or/control.h \ src/or/cpuworker.h \ - src/or/dircollate.h \ src/or/directory.h \ src/or/dirserv.h \ - src/or/dirvote.h \ + src/or/dirvote_common.h \ src/or/dns.h \ src/or/dns_structs.h \ src/or/dnsserv.h \ @@ -225,8 +239,6 @@ ORHEADERS = \ src/or/onion_ntor.h \ src/or/onion_tap.h \ src/or/or.h \ - src/or/shared_random.h \ - src/or/shared_random_state.h \ src/or/transports.h \ src/or/parsecommon.h \ src/or/periodic.h \ @@ -254,11 +266,22 @@ ORHEADERS = \ src/or/routerset.h \ src/or/routerparse.h \ src/or/scheduler.h \ + src/or/shared_random_common.h \ src/or/statefile.h \ src/or/status.h \ src/or/torcert.h \ src/or/tor_api_internal.h +# We add the headers of the modules even though they are disabled so we can +# properly compiled the entry points stub. + +# The Directory Authority module headers. +ORHEADERS += \ + src/or/dirauth/dircollate.h \ + src/or/dirauth/dirvote.h \ + src/or/dirauth/shared_random.h \ + src/or/dirauth/shared_random_state.h + # This may someday want to be an installed file? noinst_HEADERS += src/or/tor_api.h diff --git a/src/or/main.c b/src/or/main.c index f1b8308304..1160f377e4 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -72,7 +72,6 @@ #include "crypto_s2k.h" #include "directory.h" #include "dirserv.h" -#include "dirvote.h" #include "dns.h" #include "dnsserv.h" #include "dos.h" @@ -103,7 +102,7 @@ #include "routerlist.h" #include "routerparse.h" #include "scheduler.h" -#include "shared_random.h" +#include "dirauth/shared_random.h" #include "statefile.h" #include "status.h" #include "tor_api.h" @@ -118,6 +117,8 @@ #include <event2/event.h> +#include "dirauth/dirvote.h" + #ifdef HAVE_SYSTEMD # if defined(__COVERITY__) && !defined(__INCLUDE_LEVEL__) /* Systemd's use of gcc's __INCLUDE_LEVEL__ extension macro appears to confuse diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c index b0db0cecbc..5ca320d284 100644 --- a/src/or/networkstatus.c +++ b/src/or/networkstatus.c @@ -50,7 +50,6 @@ #include "control.h" #include "directory.h" #include "dirserv.h" -#include "dirvote.h" #include "dos.h" #include "entrynodes.h" #include "hibernate.h" @@ -64,11 +63,13 @@ #include "routerlist.h" #include "routerparse.h" #include "scheduler.h" -#include "shared_random.h" +#include "dirauth/shared_random.h" #include "transports.h" #include "torcert.h" #include "channelpadding.h" +#include "dirauth/dirvote.h" + /** Most recently received and validated v3 "ns"-flavored consensus network * status. */ STATIC networkstatus_t *current_ns_consensus = NULL; @@ -365,9 +366,7 @@ networkstatus_vote_free_(networkstatus_t *ns) digestmap_free(ns->desc_digest_map, NULL); if (ns->sr_info.commits) { - SMARTLIST_FOREACH(ns->sr_info.commits, sr_commit_t *, c, - sr_commit_free(c)); - smartlist_free(ns->sr_info.commits); + dirvote_clear_commits(ns); } tor_free(ns->sr_info.previous_srv); tor_free(ns->sr_info.current_srv); @@ -2641,6 +2640,25 @@ networkstatus_check_required_protocols(const networkstatus_t *ns, return 0; } +/** Release all storage held in <b>s</b>. */ +void +ns_detached_signatures_free_(ns_detached_signatures_t *s) +{ + if (!s) + return; + if (s->signatures) { + STRMAP_FOREACH(s->signatures, flavor, smartlist_t *, sigs) { + SMARTLIST_FOREACH(sigs, document_signature_t *, sig, + document_signature_free(sig)); + smartlist_free(sigs); + } STRMAP_FOREACH_END; + strmap_free(s->signatures, NULL); + strmap_free(s->digests, tor_free_); + } + + tor_free(s); +} + /** Free all storage held locally in this module. */ void networkstatus_free_all(void) diff --git a/src/or/networkstatus.h b/src/or/networkstatus.h index 04cf277d2f..bd78292050 100644 --- a/src/or/networkstatus.h +++ b/src/or/networkstatus.h @@ -24,6 +24,9 @@ void routerstatus_free_(routerstatus_t *rs); void networkstatus_vote_free_(networkstatus_t *ns); #define networkstatus_vote_free(ns) \ FREE_AND_NULL(networkstatus_t, networkstatus_vote_free_, (ns)) +void ns_detached_signatures_free_(ns_detached_signatures_t *s); +#define ns_detached_signatures_free(s) \ + FREE_AND_NULL(ns_detached_signatures_t, ns_detached_signatures_free_, (s)) networkstatus_voter_info_t *networkstatus_get_voter_by_id( networkstatus_t *vote, const char *identity); diff --git a/src/or/routerlist.c b/src/or/routerlist.c index 1bfbd9f670..7eb9ec7990 100644 --- a/src/or/routerlist.c +++ b/src/or/routerlist.c @@ -101,7 +101,6 @@ #include "control.h" #include "directory.h" #include "dirserv.h" -#include "dirvote.h" #include "entrynodes.h" #include "fp_pair.h" #include "geoip.h" @@ -122,6 +121,8 @@ #include "sandbox.h" #include "torcert.h" +#include "dirauth/dirvote.h" + // #define DEBUG_ROUTERLIST /****************************************************************************/ diff --git a/src/or/routerparse.c b/src/or/routerparse.c index 1834cfad24..3a55c00cc1 100644 --- a/src/or/routerparse.c +++ b/src/or/routerparse.c @@ -59,7 +59,6 @@ #include "config.h" #include "circuitstats.h" #include "dirserv.h" -#include "dirvote.h" #include "parsecommon.h" #include "policies.h" #include "protover.h" @@ -75,11 +74,15 @@ #include "entrynodes.h" #include "torcert.h" #include "sandbox.h" -#include "shared_random.h" +#include "shared_random_common.h" +#include "dirvote_common.h" +#include "dirauth/shared_random.h" #undef log #include <math.h> +#include "dirauth/dirvote.h" + /****************************************************************************/ /** List of tokens recognized in router descriptors */ @@ -3282,60 +3285,6 @@ networkstatus_verify_bw_weights(networkstatus_t *ns, int consensus_method) return valid; } -/** Parse and extract all SR commits from <b>tokens</b> and place them in - * <b>ns</b>. */ -static void -extract_shared_random_commits(networkstatus_t *ns, smartlist_t *tokens) -{ - smartlist_t *chunks = NULL; - - tor_assert(ns); - tor_assert(tokens); - /* Commits are only present in a vote. */ - tor_assert(ns->type == NS_TYPE_VOTE); - - ns->sr_info.commits = smartlist_new(); - - smartlist_t *commits = find_all_by_keyword(tokens, K_COMMIT); - /* It's normal that a vote might contain no commits even if it participates - * in the SR protocol. Don't treat it as an error. */ - if (commits == NULL) { - goto end; - } - - /* Parse the commit. We do NO validation of number of arguments or ordering - * for forward compatibility, it's the parse commit job to inform us if it's - * supported or not. */ - chunks = smartlist_new(); - SMARTLIST_FOREACH_BEGIN(commits, directory_token_t *, tok) { - /* Extract all arguments and put them in the chunks list. */ - for (int i = 0; i < tok->n_args; i++) { - smartlist_add(chunks, tok->args[i]); - } - sr_commit_t *commit = sr_parse_commit(chunks); - smartlist_clear(chunks); - if (commit == NULL) { - /* Get voter identity so we can warn that this dirauth vote contains - * commit we can't parse. */ - networkstatus_voter_info_t *voter = smartlist_get(ns->voters, 0); - tor_assert(voter); - log_warn(LD_DIR, "SR: Unable to parse commit %s from vote of voter %s.", - escaped(tok->object_body), - hex_str(voter->identity_digest, - sizeof(voter->identity_digest))); - /* Commitment couldn't be parsed. Continue onto the next commit because - * this one could be unsupported for instance. */ - continue; - } - /* Add newly created commit object to the vote. */ - smartlist_add(ns->sr_info.commits, commit); - } SMARTLIST_FOREACH_END(tok); - - end: - smartlist_free(chunks); - smartlist_free(commits); -} - /** Check if a shared random value of type <b>srv_type</b> is in * <b>tokens</b>. If there is, parse it and set it to <b>srv_out</b>. Return * -1 on failure, 0 on success. The resulting srv is allocated on the heap and @@ -3773,13 +3722,7 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out, /* If this is a vote document, check if information about the shared randomness protocol is included, and extract it. */ if (ns->type == NS_TYPE_VOTE) { - /* Does this authority participates in the SR protocol? */ - tok = find_opt_by_keyword(tokens, K_SR_FLAG); - if (tok) { - ns->sr_info.participate = 1; - /* Get the SR commitments and reveals from the vote. */ - extract_shared_random_commits(ns, tokens); - } + dirvote_parse_sr_commits(ns, tokens); } /* For both a vote and consensus, extract the shared random values. */ if (ns->type == NS_TYPE_VOTE || ns->type == NS_TYPE_CONSENSUS) { @@ -3969,7 +3912,7 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out, } } - if (voter_get_sig_by_algorithm(v, sig->alg)) { + if (dirvote_get_voter_sig_by_alg(v, sig->alg)) { /* We already parsed a vote with this algorithm from this voter. Use the first one. */ log_fn(LOG_PROTOCOL_WARN, LD_DIR, "We received a networkstatus " diff --git a/src/or/shared_random_common.c b/src/or/shared_random_common.c new file mode 100644 index 0000000000..7d897d9531 --- /dev/null +++ b/src/or/shared_random_common.c @@ -0,0 +1,259 @@ +/* Copyright (c) 2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file shared_random_common.c + * \brief This file contains functions that are from the shared random + * subsystem but used by many part of tor. The full feature is built + * as part of the dirauth module. + **/ + +#define SHARED_RANDOM_COMMON_PRIVATE +#include "shared_random_common.h" + +#include "config.h" +#include "dirvote_common.h" +#include "networkstatus.h" +#include "util.h" +#include "util_format.h" + +/* Convert a given srv object to a string for the control port. This doesn't + * fail and the srv object MUST be valid. */ +static char * +srv_to_control_string(const sr_srv_t *srv) +{ + char *srv_str; + char srv_hash_encoded[SR_SRV_VALUE_BASE64_LEN + 1]; + tor_assert(srv); + + sr_srv_encode(srv_hash_encoded, sizeof(srv_hash_encoded), srv); + tor_asprintf(&srv_str, "%s", srv_hash_encoded); + return srv_str; +} + +/* Return the voting interval of the tor vote subsystem. */ +int +get_voting_interval(void) +{ + int interval; + networkstatus_t *consensus = networkstatus_get_live_consensus(time(NULL)); + + if (consensus) { + interval = (int)(consensus->fresh_until - consensus->valid_after); + } else { + /* Same for both a testing and real network. We voluntarily ignore the + * InitialVotingInterval since it complexifies things and it doesn't + * affect the SR protocol. */ + interval = get_options()->V3AuthVotingInterval; + } + tor_assert(interval > 0); + return interval; +} + +/* Given the time <b>now</b>, return the start time of the current round of + * the SR protocol. For example, if it's 23:47:08, the current round thus + * started at 23:47:00 for a voting interval of 10 seconds. */ +time_t +get_start_time_of_current_round(void) +{ + const or_options_t *options = get_options(); + int voting_interval = get_voting_interval(); + /* First, get the start time of the next round */ + time_t next_start = dirvote_get_next_valid_after_time(); + /* Now roll back next_start by a voting interval to find the start time of + the current round. */ + time_t curr_start = dirvote_get_start_of_next_interval( + next_start - voting_interval - 1, + voting_interval, + options->TestingV3AuthVotingStartOffset); + return curr_start; +} + +/* + * Public API + */ + +/* Encode the given shared random value and put it in dst. Destination + * buffer must be at least SR_SRV_VALUE_BASE64_LEN plus the NULL byte. */ +void +sr_srv_encode(char *dst, size_t dst_len, const sr_srv_t *srv) +{ + int ret; + /* Extra byte for the NULL terminated char. */ + char buf[SR_SRV_VALUE_BASE64_LEN + 1]; + + tor_assert(dst); + tor_assert(srv); + tor_assert(dst_len >= sizeof(buf)); + + ret = base64_encode(buf, sizeof(buf), (const char *) srv->value, + sizeof(srv->value), 0); + /* Always expect the full length without the NULL byte. */ + tor_assert(ret == (sizeof(buf) - 1)); + tor_assert(ret <= (int) dst_len); + strlcpy(dst, buf, dst_len); +} + +/* Return the current SRV string representation for the control port. Return a + * newly allocated string on success containing the value else "" if not found + * or if we don't have a valid consensus yet. */ +char * +sr_get_current_for_control(void) +{ + char *srv_str; + const networkstatus_t *c = networkstatus_get_latest_consensus(); + if (c && c->sr_info.current_srv) { + srv_str = srv_to_control_string(c->sr_info.current_srv); + } else { + srv_str = tor_strdup(""); + } + return srv_str; +} + +/* Return the previous SRV string representation for the control port. Return + * a newly allocated string on success containing the value else "" if not + * found or if we don't have a valid consensus yet. */ +char * +sr_get_previous_for_control(void) +{ + char *srv_str; + const networkstatus_t *c = networkstatus_get_latest_consensus(); + if (c && c->sr_info.previous_srv) { + srv_str = srv_to_control_string(c->sr_info.previous_srv); + } else { + srv_str = tor_strdup(""); + } + return srv_str; +} + +/* Return current shared random value from the latest consensus. Caller can + * NOT keep a reference to the returned pointer. Return NULL if none. */ +const sr_srv_t * +sr_get_current(const networkstatus_t *ns) +{ + const networkstatus_t *consensus; + + /* Use provided ns else get a live one */ + if (ns) { + consensus = ns; + } else { + consensus = networkstatus_get_live_consensus(approx_time()); + } + /* Ideally we would never be asked for an SRV without a live consensus. Make + * sure this assumption is correct. */ + tor_assert_nonfatal(consensus); + + if (consensus) { + return consensus->sr_info.current_srv; + } + return NULL; +} + +/* Return previous shared random value from the latest consensus. Caller can + * NOT keep a reference to the returned pointer. Return NULL if none. */ +const sr_srv_t * +sr_get_previous(const networkstatus_t *ns) +{ + const networkstatus_t *consensus; + + /* Use provided ns else get a live one */ + if (ns) { + consensus = ns; + } else { + consensus = networkstatus_get_live_consensus(approx_time()); + } + /* Ideally we would never be asked for an SRV without a live consensus. Make + * sure this assumption is correct. */ + tor_assert_nonfatal(consensus); + + if (consensus) { + return consensus->sr_info.previous_srv; + } + return NULL; +} + +/* Parse a list of arguments from a SRV value either from a vote, consensus + * or from our disk state and return a newly allocated srv object. NULL is + * returned on error. + * + * The arguments' order: + * num_reveals, value + */ +sr_srv_t * +sr_parse_srv(const smartlist_t *args) +{ + char *value; + int ok, ret; + uint64_t num_reveals; + sr_srv_t *srv = NULL; + + tor_assert(args); + + if (smartlist_len(args) < 2) { + goto end; + } + + /* First argument is the number of reveal values */ + num_reveals = tor_parse_uint64(smartlist_get(args, 0), + 10, 0, UINT64_MAX, &ok, NULL); + if (!ok) { + goto end; + } + /* Second and last argument is the shared random value it self. */ + value = smartlist_get(args, 1); + if (strlen(value) != SR_SRV_VALUE_BASE64_LEN) { + goto end; + } + + srv = tor_malloc_zero(sizeof(*srv)); + srv->num_reveals = num_reveals; + /* We subtract one byte from the srclen because the function ignores the + * '=' character in the given buffer. This is broken but it's a documented + * behavior of the implementation. */ + ret = base64_decode((char *) srv->value, sizeof(srv->value), value, + SR_SRV_VALUE_BASE64_LEN - 1); + if (ret != sizeof(srv->value)) { + tor_free(srv); + srv = NULL; + goto end; + } + end: + return srv; +} + +/** Return the start time of the current SR protocol run. For example, if the + * time is 23/06/2017 23:47:08 and a full SR protocol run is 24 hours, this + * function should return 23/06/2017 00:00:00. */ +time_t +sr_state_get_start_time_of_current_protocol_run(time_t now) +{ + int total_rounds = SHARED_RANDOM_N_ROUNDS * SHARED_RANDOM_N_PHASES; + int voting_interval = get_voting_interval(); + /* Find the time the current round started. */ + time_t beginning_of_current_round = get_start_time_of_current_round(); + + /* Get current SR protocol round */ + int current_round = (now / voting_interval) % total_rounds; + + /* Get start time by subtracting the time elapsed from the beginning of the + protocol run */ + time_t time_elapsed_since_start_of_run = current_round * voting_interval; + return beginning_of_current_round - time_elapsed_since_start_of_run; +} + +/** Return the time (in seconds) it takes to complete a full SR protocol phase + * (e.g. the commit phase). */ +unsigned int +sr_state_get_phase_duration(void) +{ + return SHARED_RANDOM_N_ROUNDS * get_voting_interval(); +} + +/** Return the time (in seconds) it takes to complete a full SR protocol run */ +unsigned int +sr_state_get_protocol_run_duration(void) +{ + int total_protocol_rounds = SHARED_RANDOM_N_ROUNDS * SHARED_RANDOM_N_PHASES; + return total_protocol_rounds * get_voting_interval(); +} + diff --git a/src/or/shared_random_common.h b/src/or/shared_random_common.h new file mode 100644 index 0000000000..35e08ddf32 --- /dev/null +++ b/src/or/shared_random_common.h @@ -0,0 +1,47 @@ +/* Copyright (c) 2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file shared_random_common.h + * \brief Header file for shared_random_common.c. + **/ + +#ifndef TOR_SHARED_RANDOM_COMMON_H +#define TOR_SHARED_RANDOM_COMMON_H + +/* Dirauth module. */ +#include "dirauth/shared_random.h" + +/* Helper functions. */ +void sr_srv_encode(char *dst, size_t dst_len, const sr_srv_t *srv); +int get_voting_interval(void); + +/* Control port functions. */ +char *sr_get_current_for_control(void); +char *sr_get_previous_for_control(void); + +/* SRV functions. */ +const sr_srv_t *sr_get_current(const networkstatus_t *ns); +const sr_srv_t *sr_get_previous(const networkstatus_t *ns); +sr_srv_t *sr_parse_srv(const smartlist_t *args); + +/* + * Shared Random State API + */ + +/* Each protocol phase has 12 rounds */ +#define SHARED_RANDOM_N_ROUNDS 12 +/* Number of phase we have in a protocol. */ +#define SHARED_RANDOM_N_PHASES 2 + +time_t sr_state_get_start_time_of_current_protocol_run(time_t now); +unsigned int sr_state_get_phase_duration(void); +unsigned int sr_state_get_protocol_run_duration(void); +time_t get_start_time_of_current_round(void); + +#ifdef TOR_UNIT_TESTS + +#endif /* TOR_UNIT_TESTS */ + +#endif /* TOR_SHARED_RANDOM_COMMON_H */ + diff --git a/src/test/test_config.c b/src/test/test_config.c index 7983106a2f..461aa646d6 100644 --- a/src/test/test_config.c +++ b/src/test/test_config.c @@ -24,7 +24,7 @@ #include "control.h" #include "cpuworker.h" #include "dirserv.h" -#include "dirvote.h" +#include "dirauth/dirvote.h" #include "dns.h" #include "entrynodes.h" #include "transports.h" diff --git a/src/test/test_dir.c b/src/test/test_dir.c index 957f646de9..84fdb96e45 100644 --- a/src/test/test_dir.c +++ b/src/test/test_dir.c @@ -25,7 +25,7 @@ #include "crypto_ed25519.h" #include "directory.h" #include "dirserv.h" -#include "dirvote.h" +#include "dirauth/dirvote.h" #include "entrynodes.h" #include "hibernate.h" #include "memarea.h" @@ -35,7 +35,7 @@ #include "routerlist.h" #include "routerparse.h" #include "routerset.h" -#include "shared_random_state.h" +#include "dirauth/shared_random_state.h" #include "test.h" #include "test_dir_common.h" #include "torcert.h" diff --git a/src/test/test_dir_common.c b/src/test/test_dir_common.c index fdf43533a8..02d3295ca6 100644 --- a/src/test/test_dir_common.c +++ b/src/test/test_dir_common.c @@ -9,7 +9,8 @@ #include "test.h" #include "container.h" #include "or.h" -#include "dirvote.h" +#include "dirauth/dirvote.h" +#include "dirvote_common.h" #include "nodelist.h" #include "routerlist.h" #include "test_dir_common.h" diff --git a/src/test/test_dir_handle_get.c b/src/test/test_dir_handle_get.c index 71faf70af2..1bff249df5 100644 --- a/src/test/test_dir_handle_get.c +++ b/src/test/test_dir_handle_get.c @@ -30,7 +30,7 @@ #include "proto_http.h" #include "geoip.h" #include "dirserv.h" -#include "dirvote.h" +#include "dirauth/dirvote.h" #include "log_test_helpers.h" #ifdef _WIN32 diff --git a/src/test/test_hs_common.c b/src/test/test_hs_common.c index 17ba11ca7d..43a2c5e80c 100644 --- a/src/test/test_hs_common.c +++ b/src/test/test_hs_common.c @@ -23,12 +23,12 @@ #include "config.h" #include "networkstatus.h" #include "directory.h" -#include "dirvote.h" +#include "dirauth/dirvote.h" #include "nodelist.h" #include "routerlist.h" #include "statefile.h" #include "circuitlist.h" -#include "shared_random.h" +#include "dirauth/shared_random.h" #include "util.h" /** Test the validation of HS v3 addresses */ diff --git a/src/test/test_hs_service.c b/src/test/test_hs_service.c index 481521520c..fc8e016b52 100644 --- a/src/test/test_hs_service.c +++ b/src/test/test_hs_service.c @@ -34,7 +34,7 @@ #include "circuitlist.h" #include "circuituse.h" #include "crypto.h" -#include "dirvote.h" +#include "dirauth/dirvote.h" #include "networkstatus.h" #include "nodelist.h" #include "relay.h" @@ -51,7 +51,7 @@ #include "main.h" #include "rendservice.h" #include "statefile.h" -#include "shared_random_state.h" +#include "dirauth/shared_random_state.h" /* Trunnel */ #include "hs/cell_establish_intro.h" diff --git a/src/test/test_microdesc.c b/src/test/test_microdesc.c index 3d02fc1a59..4b168f49ed 100644 --- a/src/test/test_microdesc.c +++ b/src/test/test_microdesc.c @@ -5,7 +5,8 @@ #include "or.h" #include "config.h" -#include "dirvote.h" +#define DIRVOTE_PRIVATE +#include "dirauth/dirvote.h" #include "microdesc.h" #include "networkstatus.h" #include "routerlist.h" diff --git a/src/test/test_routerlist.c b/src/test/test_routerlist.c index c19d66ef9d..71b487f35b 100644 --- a/src/test/test_routerlist.c +++ b/src/test/test_routerlist.c @@ -19,7 +19,7 @@ #include "container.h" #include "control.h" #include "directory.h" -#include "dirvote.h" +#include "dirauth/dirvote.h" #include "entrynodes.h" #include "hibernate.h" #include "microdesc.h" @@ -30,7 +30,7 @@ #include "routerlist.h" #include "routerset.h" #include "routerparse.h" -#include "shared_random.h" +#include "dirauth/shared_random.h" #include "statefile.h" #include "test.h" #include "test_dir_common.h" diff --git a/src/test/test_shared_random.c b/src/test/test_shared_random.c index 4fe9ee45f0..95a7ff28f4 100644 --- a/src/test/test_shared_random.c +++ b/src/test/test_shared_random.c @@ -9,13 +9,14 @@ #include "or.h" #include "test.h" #include "config.h" -#include "dirvote.h" -#include "shared_random.h" -#include "shared_random_state.h" +#include "dirauth/dirvote.h" +#include "dirauth/shared_random.h" +#include "dirauth/shared_random_state.h" #include "routerkeys.h" #include "routerlist.h" #include "router.h" #include "routerparse.h" +#include "shared_random_common.h" #include "networkstatus.h" #include "log_test_helpers.h" |