diff options
Diffstat (limited to 'src/or')
-rw-r--r-- | src/or/Makefile.am | 4 | ||||
-rw-r--r-- | src/or/circuitbuild.c | 901 | ||||
-rw-r--r-- | src/or/circuitlist.c | 17 | ||||
-rw-r--r-- | src/or/circuituse.c | 62 | ||||
-rw-r--r-- | src/or/command.c | 10 | ||||
-rw-r--r-- | src/or/config.c | 69 | ||||
-rw-r--r-- | src/or/connection.c | 12 | ||||
-rw-r--r-- | src/or/connection_or.c | 5 | ||||
-rw-r--r-- | src/or/control.c | 8 | ||||
-rw-r--r-- | src/or/directory.c | 9 | ||||
-rw-r--r-- | src/or/dirserv.c | 11 | ||||
-rw-r--r-- | src/or/dirvote.c | 105 | ||||
-rw-r--r-- | src/or/eventdns.c | 6 | ||||
-rw-r--r-- | src/or/eventdns.h | 4 | ||||
-rw-r--r-- | src/or/geoip.c | 4 | ||||
-rw-r--r-- | src/or/main.c | 17 | ||||
-rw-r--r-- | src/or/networkstatus.c | 31 | ||||
-rw-r--r-- | src/or/or.h | 175 | ||||
-rw-r--r-- | src/or/relay.c | 13 | ||||
-rw-r--r-- | src/or/rendclient.c | 9 | ||||
-rw-r--r-- | src/or/rendservice.c | 15 | ||||
-rw-r--r-- | src/or/router.c | 25 | ||||
-rw-r--r-- | src/or/routerparse.c | 59 | ||||
-rw-r--r-- | src/or/test.c | 258 |
24 files changed, 1675 insertions, 154 deletions
diff --git a/src/or/Makefile.am b/src/or/Makefile.am index 7d6c9eb0b..e9916d518 100644 --- a/src/or/Makefile.am +++ b/src/or/Makefile.am @@ -41,14 +41,14 @@ AM_CPPFLAGS = -DSHARE_DATADIR="\"$(datadir)\"" \ tor_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ @TOR_LDFLAGS_libevent@ tor_LDADD = ../common/libor.a ../common/libor-crypto.a \ ../common/libor-event.a \ - -lz -levent -lssl -lcrypto @TOR_LIB_WS32@ @TOR_LIB_GDI@ + -lz -lm -levent -lssl -lcrypto @TOR_LIB_WS32@ @TOR_LIB_GDI@ test_SOURCES = $(COMMON_SRC) test_data.c test.c test_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \ @TOR_LDFLAGS_libevent@ test_LDADD = ../common/libor.a ../common/libor-crypto.a \ ../common/libor-event.a \ - -lz -levent -lssl -lcrypto @TOR_LIB_WS32@ @TOR_LIB_GDI@ + -lz -lm -levent -lssl -lcrypto @TOR_LIB_WS32@ @TOR_LIB_GDI@ noinst_HEADERS = or.h eventdns.h eventdns_tor.h micro-revision.i diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c index 479ecfbfe..b1de02416 100644 --- a/src/or/circuitbuild.c +++ b/src/or/circuitbuild.c @@ -9,9 +9,49 @@ * \brief The actual details of building circuits. **/ +#define CIRCUIT_PRIVATE + #include "or.h" +#include "crypto.h" + +/* + * This madness is needed because if we simply #undef log + * before including or.h or log.h, we get linker collisions + * and random segfaults due to memory corruption (and + * not even at calls to log() either!) + */ + /* XXX022 somebody should rename Tor's log() function, so we can + * remove this wart. -RD */ +#undef log + +/* + * Linux doesn't provide lround in math.h by default, but mac os does... + * It's best just to leave math.h out of the picture entirely. + */ +//#define log math_h_log +//#include <math.h> +//#undef log +long int lround(double x); +double ln(double x); +double log(double x); +double pow(double x, double y); + +double +ln(double x) +{ + return log(x); +} + +#define log _log /********* START VARIABLES **********/ +/** Global list of circuit build times */ +// FIXME: Add this as a member for entry_guard_t instead of global? +// Then we could do per-guard statistics, as guards are likely to +// vary in their own latency. The downside of this is that guards +// can change frequently, so we'd be building a lot more circuits +// most likely. +circuit_build_times_t circ_times; /** A global list of all circuits at this hop. */ extern circuit_t *global_circuitlist; @@ -47,6 +87,10 @@ static smartlist_t *entry_guards = NULL; * and those changes need to be flushed to disk. */ static int entry_guards_dirty = 0; +/** If set, we're running the unit tests: we should avoid clobbering + * our state file or accessing get_options() or get_or_state() */ +static int unit_tests = 0; + /********* END VARIABLES ************/ static int circuit_deliver_create_cell(circuit_t *circ, @@ -60,6 +104,796 @@ static int onion_append_hop(crypt_path_t **head_ptr, extend_info_t *choice); static void entry_guards_changed(void); static time_t start_of_month(time_t when); +/** Make a note that we're running unit tests (rather than running Tor + * itself), so we avoid clobbering our state file. */ +void +circuitbuild_running_unit_tests(void) +{ + unit_tests = 1; +} + +/** + * Return the initial default or configured timeout in milliseconds + */ +static double +circuit_build_times_get_initial_timeout(void) +{ + double timeout; + if (!unit_tests && get_options()->CircuitBuildTimeout) { + timeout = get_options()->CircuitBuildTimeout*1000; + if (timeout < BUILD_TIMEOUT_MIN_VALUE) { + log_warn(LD_CIRC, "Config CircuitBuildTimeout too low. Setting to %ds", + BUILD_TIMEOUT_MIN_VALUE/1000); + timeout = BUILD_TIMEOUT_MIN_VALUE; + } + } else { + timeout = BUILD_TIMEOUT_INITIAL_VALUE; + } + return timeout; +} + +/** + * Reset the build time state. + * + * Leave estimated parameters, timeout and network liveness intact + * for future use. + */ +void +circuit_build_times_reset(circuit_build_times_t *cbt) +{ + memset(cbt->circuit_build_times, 0, sizeof(cbt->circuit_build_times)); + cbt->pre_timeouts = 0; + cbt->total_build_times = 0; + cbt->build_times_idx = 0; + cbt->have_computed_timeout = 0; +} + +/** + * Initialize the buildtimes structure for first use. + * + * Sets the initial timeout value based to either the + * config setting or BUILD_TIMEOUT_INITIAL_VALUE. + */ +void +circuit_build_times_init(circuit_build_times_t *cbt) +{ + memset(cbt, 0, sizeof(*cbt)); + cbt->timeout_ms = circuit_build_times_get_initial_timeout(); +} + +/** + * Rewind our timeout history by n positions. + */ +static void +circuit_build_times_rewind_history(circuit_build_times_t *cbt, int n) +{ + int i = 0; + + if (cbt->pre_timeouts) { + if (cbt->pre_timeouts > n) { + cbt->pre_timeouts -= n; + } else { + cbt->pre_timeouts = 0; + } + log_info(LD_CIRC, + "Rewound history by %d places. Current index: %d. Total: %d. " + "Pre-timeouts: %d", n, cbt->build_times_idx, + cbt->total_build_times, cbt->pre_timeouts); + + tor_assert(cbt->build_times_idx == 0); + tor_assert(cbt->total_build_times == 0); + return; + } + + cbt->build_times_idx -= n; + cbt->build_times_idx %= NCIRCUITS_TO_OBSERVE; + + for (i = 0; i < n; i++) { + cbt->circuit_build_times[(i+cbt->build_times_idx)%NCIRCUITS_TO_OBSERVE]=0; + } + + if (cbt->total_build_times > n) { + cbt->total_build_times -= n; + } else { + cbt->total_build_times = 0; + } + + log_info(LD_CIRC, + "Rewound history by %d places. Current index: %d. " + "Total: %d", n, cbt->build_times_idx, cbt->total_build_times); +} + +/** + * Add a timeoutout value to the set of build times. Time units + * are milliseconds + * + * circuit_build_times is a circular array, so loop around when + * array is full. + */ +int +circuit_build_times_add_time(circuit_build_times_t *cbt, build_time_t time) +{ + if (time > BUILD_TIME_MAX) { + log_notice(LD_CIRC, + "Circuit build time of %ums exceeds max. Capping at 65536ms", time); + time = BUILD_TIME_MAX; + } else if (time <= 0) { + log_err(LD_CIRC, "Circuit build time is %u!", time); + return -1; + } + + // XXX: Probably want to demote this to debug for the release. + log_info(LD_CIRC, "Adding circuit build time %u", time); + + cbt->circuit_build_times[cbt->build_times_idx] = time; + cbt->build_times_idx = (cbt->build_times_idx + 1) % NCIRCUITS_TO_OBSERVE; + if (cbt->total_build_times < NCIRCUITS_TO_OBSERVE) + cbt->total_build_times++; + + if ((cbt->total_build_times % BUILD_TIMES_SAVE_STATE_EVERY) == 0) { + /* Save state every n circuit builds */ + if (!unit_tests && !get_options()->AvoidDiskWrites) + or_state_mark_dirty(get_or_state(), 0); + } + + return 0; +} + +/** + * Return maximum circuit build time + */ +static build_time_t +circuit_build_times_max(circuit_build_times_t *cbt) +{ + int i = 0; + build_time_t max_build_time = 0; + for (i = 0; i < NCIRCUITS_TO_OBSERVE; i++) { + if (cbt->circuit_build_times[i] > max_build_time) + max_build_time = cbt->circuit_build_times[i]; + } + return max_build_time; +} + +#if 0 +/** Return minimum circuit build time */ +build_time_t +circuit_build_times_min(circuit_build_times_t *cbt) +{ + int i = 0; + build_time_t min_build_time = BUILD_TIME_MAX; + for (i = 0; i < NCIRCUITS_TO_OBSERVE; i++) { + if (cbt->circuit_build_times[i] && /* 0 <-> uninitialized */ + cbt->circuit_build_times[i] < min_build_time) + min_build_time = cbt->circuit_build_times[i]; + } + if (min_build_time == BUILD_TIME_MAX) { + log_warn(LD_CIRC, "No build times less than BUILD_TIME_MAX!"); + } + return min_build_time; +} +#endif + +/** + * Calculate and return a histogram for the set of build times. + * + * Returns an allocated array of histrogram bins representing + * the frequency of index*BUILDTIME_BIN_WIDTH millisecond + * build times. Also outputs the number of bins in nbins. + * + * The return value must be freed by the caller. + */ +static uint32_t * +circuit_build_times_create_histogram(circuit_build_times_t *cbt, + build_time_t *nbins) +{ + uint32_t *histogram; + build_time_t max_build_time = circuit_build_times_max(cbt); + int i, c; + + *nbins = 1 + (max_build_time / BUILDTIME_BIN_WIDTH); + histogram = tor_malloc_zero(*nbins * sizeof(build_time_t)); + + // calculate histogram + for (i = 0; i < NCIRCUITS_TO_OBSERVE; i++) { + if (cbt->circuit_build_times[i] == 0) continue; /* 0 <-> uninitialized */ + + c = (cbt->circuit_build_times[i] / BUILDTIME_BIN_WIDTH); + histogram[c]++; + } + + return histogram; +} + +/** + * Return the most frequent build time (rounded to BUILDTIME_BIN_WIDTH ms). + * + * Ties go in favor of the slower time. + */ +static build_time_t +circuit_build_times_mode(circuit_build_times_t *cbt) +{ + build_time_t i, nbins, max_bin=0; + uint32_t *histogram = circuit_build_times_create_histogram(cbt, &nbins); + + for (i = 0; i < nbins; i++) { + if (histogram[i] >= histogram[max_bin]) { + max_bin = i; + } + } + + tor_free(histogram); + + return max_bin*BUILDTIME_BIN_WIDTH+BUILDTIME_BIN_WIDTH/2; +} + +/** + * Output a histogram of current circuit build times to + * the or_state_t state structure. + */ +void +circuit_build_times_update_state(circuit_build_times_t *cbt, + or_state_t *state) +{ + uint32_t *histogram; + build_time_t i = 0; + build_time_t nbins = 0; + config_line_t **next, *line; + + histogram = circuit_build_times_create_histogram(cbt, &nbins); + // write to state + config_free_lines(state->BuildtimeHistogram); + next = &state->BuildtimeHistogram; + *next = NULL; + + state->TotalBuildTimes = cbt->total_build_times; + + for (i = 0; i < nbins; i++) { + // compress the histogram by skipping the blanks + if (histogram[i] == 0) continue; + *next = line = tor_malloc_zero(sizeof(config_line_t)); + line->key = tor_strdup("CircuitBuildTimeBin"); + line->value = tor_malloc(25); + tor_snprintf(line->value, 25, "%d %d", + i*BUILDTIME_BIN_WIDTH+BUILDTIME_BIN_WIDTH/2, histogram[i]); + next = &(line->next); + } + + if (!unit_tests) { + if (!get_options()->AvoidDiskWrites) + or_state_mark_dirty(get_or_state(), 0); + } + + if (histogram) tor_free(histogram); +} + +/** + * Shuffle the build times array. + * + * Stolen from http://en.wikipedia.org/wiki/Fisher\u2013Yates_shuffle + */ +static void +circuit_build_times_shuffle_array(circuit_build_times_t *cbt) +{ + int n = cbt->total_build_times; + + /* This code can only be run on a compact array */ + tor_assert(cbt->total_build_times == cbt->build_times_idx); + while (n-- > 1) { + int k = crypto_rand_int(n + 1); /* 0 <= k <= n. */ + build_time_t tmp = cbt->circuit_build_times[k]; + cbt->circuit_build_times[k] = cbt->circuit_build_times[n]; + cbt->circuit_build_times[n] = tmp; + } +} + +/** + * Load histogram from <b>state</b>, shuffling the resulting array + * after we do so. Use this result to estimate parameters and + * calculate the timeout. + * + * Returns -1 and sets msg on error. Msg must be freed by the caller. + */ +int +circuit_build_times_parse_state(circuit_build_times_t *cbt, + or_state_t *state, char **msg) +{ + int tot_values = 0, N = 0; + config_line_t *line; + int i; + *msg = NULL; + circuit_build_times_init(cbt); + + /* We don't support decreasing the table size yet */ + tor_assert(state->TotalBuildTimes <= NCIRCUITS_TO_OBSERVE); + + for (line = state->BuildtimeHistogram; line; line = line->next) { + smartlist_t *args = smartlist_create(); + smartlist_split_string(args, line->value, " ", + SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); + if (smartlist_len(args) < 2) { + *msg = tor_strdup("Unable to parse circuit build times: " + "Too few arguments to CircuitBuildTime"); + SMARTLIST_FOREACH(args, char*, cp, tor_free(cp)); + smartlist_free(args); + break; + } else { + const char *ms_str = smartlist_get(args,0); + const char *count_str = smartlist_get(args,1); + uint32_t count, k; + build_time_t ms; + int ok; + ms = (build_time_t)tor_parse_ulong(ms_str, 0, 0, + BUILD_TIME_MAX, &ok, NULL); + if (!ok) { + *msg = tor_strdup("Unable to parse circuit build times: " + "Unparsable bin number"); + break; + } + count = (uint32_t)tor_parse_ulong(count_str, 0, 0, + UINT32_MAX, &ok, NULL); + if (!ok) { + *msg = tor_strdup("Unable to parse circuit build times: " + "Unparsable bin count"); + break; + } + + for (k = 0; k < count; k++) { + circuit_build_times_add_time(cbt, ms); + } + N++; + SMARTLIST_FOREACH(args, char*, cp, tor_free(cp)); + smartlist_free(args); + } + + } + + circuit_build_times_shuffle_array(cbt); + + /* Verify that we didn't overwrite any indexes */ + for (i=0; i < NCIRCUITS_TO_OBSERVE; i++) { + if (!cbt->circuit_build_times[i]) + break; + tot_values++; + } + log_info(LD_CIRC, + "Loaded %d/%d values from %d lines in circuit time histogram", + tot_values, cbt->total_build_times, N); + tor_assert(cbt->total_build_times == state->TotalBuildTimes); + tor_assert(tot_values == cbt->total_build_times); + circuit_build_times_set_timeout(cbt); + return *msg ? -1 : 0; +} + +/** + * Estimates the Xm and Alpha parameters using + * http://en.wikipedia.org/wiki/Pareto_distribution#Parameter_estimation + * + * The notable difference is that we use mode instead of min to estimate Xm. + * This is because our distribution is frechet-like. We claim this is + * an acceptable approximation because we are only concerned with the + * accuracy of the CDF of the tail. + */ +void +circuit_build_times_update_alpha(circuit_build_times_t *cbt) +{ + build_time_t *x=cbt->circuit_build_times; + double a = 0; + int n=0,i=0; + + /* http://en.wikipedia.org/wiki/Pareto_distribution#Parameter_estimation */ + /* We sort of cheat here and make our samples slightly more pareto-like + * and less frechet-like. */ + cbt->Xm = circuit_build_times_mode(cbt); + + for (i=0; i< NCIRCUITS_TO_OBSERVE; i++) { + if (!x[i]) { + continue; + } + + if (x[i] < cbt->Xm) { + a += ln(cbt->Xm); + } else { + a += ln(x[i]); + } + n++; + } + + if (n!=cbt->total_build_times) { + log_err(LD_CIRC, "Discrepancy in build times count: %d vs %d", n, + cbt->total_build_times); + } + tor_assert(n==cbt->total_build_times); + + a -= n*ln(cbt->Xm); + a = n/a; + + cbt->alpha = a; +} + +/** + * This is the Pareto Quantile Function. It calculates the point x + * in the distribution such that F(x) = quantile (ie quantile*100% + * of the mass of the density function is below x on the curve). + * + * We use it to calculate the timeout and also to generate synthetic + * values of time for circuits that timeout before completion. + * + * See http://en.wikipedia.org/wiki/Quantile_function, + * http://en.wikipedia.org/wiki/Inverse_transform_sampling and + * http://en.wikipedia.org/wiki/Pareto_distribution#Generating_a_ + * random_sample_from_Pareto_distribution + * That's right. I'll cite wikipedia all day long. + * + * Return value is in milliseconds. + */ +double +circuit_build_times_calculate_timeout(circuit_build_times_t *cbt, + double quantile) +{ + double ret; + tor_assert(quantile >= 0); + tor_assert(1.0-quantile > 0); + tor_assert(cbt->Xm > 0); + + ret = cbt->Xm/pow(1.0-quantile,1.0/cbt->alpha); + if (ret > INT32_MAX) { + ret = INT32_MAX; + } + tor_assert(ret > 0); + return ret; +} + +/** Pareto CDF */ +double +circuit_build_times_cdf(circuit_build_times_t *cbt, double x) +{ + double ret; + tor_assert(cbt->Xm > 0); + ret = 1.0-pow(cbt->Xm/x,cbt->alpha); + tor_assert(0 <= ret && ret <= 1.0); + return ret; +} + +/** + * Generate a synthetic time using our distribution parameters. + * + * The return value will be within the [q_lo, q_hi) quantile points + * on the CDF. + */ +build_time_t +circuit_build_times_generate_sample(circuit_build_times_t *cbt, + double q_lo, double q_hi) +{ + uint64_t r = crypto_rand_uint64(UINT64_MAX-1); + build_time_t ret; + double u; + + /* Generate between [q_lo, q_hi) */ + q_hi -= 1.0/(INT32_MAX); + + tor_assert(q_lo >= 0); + tor_assert(q_hi < 1); + tor_assert(q_lo < q_hi); + + u = q_lo + ((q_hi-q_lo)*r)/(1.0*UINT64_MAX); + + tor_assert(0 <= u && u < 1.0); + /* circuit_build_times_calculate_timeout returns <= INT32_MAX */ + ret = (build_time_t)lround(circuit_build_times_calculate_timeout(cbt, u)); + tor_assert(ret > 0); + return ret; +} + +/** Generate points in [cutoff, 1.0) on the CDF. */ +void +circuit_build_times_add_timeout_worker(circuit_build_times_t *cbt, + double quantile_cutoff) +{ + build_time_t gentime = circuit_build_times_generate_sample(cbt, + quantile_cutoff, MAX_SYNTHETIC_QUANTILE); + + if (gentime < (build_time_t)lround(cbt->timeout_ms)) { + log_warn(LD_CIRC, + "Generated a synthetic timeout LESS than the current timeout: " + "%ums vs %lfms using Xm: %d a: %lf, q: %lf", + gentime, cbt->timeout_ms, cbt->Xm, cbt->alpha, quantile_cutoff); + } else if (gentime > BUILD_TIME_MAX) { + gentime = BUILD_TIME_MAX; + log_info(LD_CIRC, + "Generated a synthetic timeout larger than the max: %u", + gentime); + } else { + log_info(LD_CIRC, "Generated synthetic circuit build time %u for timeout", + gentime); + } + + circuit_build_times_add_time(cbt, gentime); +} + +/** + * Estimate an initial alpha parameter by solving the quantile + * function with a quantile point and a specific timeout value. + */ +void +circuit_build_times_initial_alpha(circuit_build_times_t *cbt, + double quantile, double timeout_ms) +{ + // Q(u) = Xm/((1-u)^(1/a)) + // Q(0.8) = Xm/((1-0.8))^(1/a)) = CircBuildTimeout + // CircBuildTimeout = Xm/((1-0.8))^(1/a)) + // CircBuildTimeout = Xm*((1-0.8))^(-1/a)) + // ln(CircBuildTimeout) = ln(Xm)+ln(((1-0.8)))*(-1/a) + // -ln(1-0.8)/(ln(CircBuildTimeout)-ln(Xm))=a + tor_assert(quantile > 0); + tor_assert(cbt->Xm > 0); + cbt->alpha = ln(1.0-quantile)/(ln(cbt->Xm)-ln(timeout_ms)); + tor_assert(cbt->alpha > 0); +} + +/** + * Generate synthetic timeout values for the timeouts + * that have happened before we estimated our parameters. + */ +static void +circuit_build_times_count_pretimeouts(circuit_build_times_t *cbt) +{ + /* Store a timeout as a random position past the current + * cutoff on the pareto curve */ + if (cbt->pre_timeouts) { + double timeout_quantile = 1.0- + ((double)cbt->pre_timeouts)/ + (cbt->pre_timeouts+cbt->total_build_times); + cbt->Xm = circuit_build_times_mode(cbt); + tor_assert(cbt->Xm > 0); + /* Use current timeout to get an estimate on alpha */ + circuit_build_times_initial_alpha(cbt, timeout_quantile, + cbt->timeout_ms); + while (cbt->pre_timeouts-- != 0) { + circuit_build_times_add_timeout_worker(cbt, timeout_quantile); + } + cbt->pre_timeouts = 0; + } +} + +/** + * Returns true if we need circuits to be built + */ +int +circuit_build_times_needs_circuits(circuit_build_times_t *cbt) +{ + /* Return true if < MIN_CIRCUITS_TO_OBSERVE */ + if (cbt->total_build_times < MIN_CIRCUITS_TO_OBSERVE) + return 1; + return 0; +} + +/** + * Returns true if we should build a timeout test circuit + * right now. + */ +int +circuit_build_times_needs_circuits_now(circuit_build_times_t *cbt) +{ + return circuit_build_times_needs_circuits(cbt) && + approx_time()-cbt->last_circ_at > BUILD_TIMES_TEST_FREQUENCY; +} + +/** + * Called to indicate that the network showed some signs of liveness. + */ +void +circuit_build_times_network_is_live(circuit_build_times_t *cbt) +{ + cbt->liveness.network_last_live = approx_time(); + cbt->liveness.nonlive_discarded = 0; + cbt->liveness.nonlive_timeouts = 0; +} + +/** + * Called to indicate that we completed a circuit. Because this circuit + * succeeded, it doesn't count as a timeout-after-the-first-hop. + */ +void +circuit_build_times_network_circ_success(circuit_build_times_t *cbt) +{ + cbt->liveness.timeouts_after_firsthop[cbt->liveness.after_firsthop_idx] = 0; + cbt->liveness.after_firsthop_idx++; + cbt->liveness.after_firsthop_idx %= RECENT_CIRCUITS; +} + +/** + * A circuit just timed out. If there has been no recent network activity + * at all, but this circuit was launched back when we thought the network + * was live, increment the number of "nonlive" circuit timeouts. + * + * Also distinguish between whether it failed before the first hop + * and record that in our history for later deciding if the network has + * changed. + */ +static void +circuit_build_times_network_timeout(circuit_build_times_t *cbt, + int did_onehop, time_t start_time) +{ + time_t now = time(NULL); + /* + * Check if this is a timeout that was for a circuit that spent its + * entire existence during a time where we have had no network activity. + * + * Also double check that it is a valid timeout after we have possibly + * just recently reset cbt->timeout_ms. + */ + if (cbt->liveness.network_last_live <= start_time && + start_time <= (now - cbt->timeout_ms/1000.0)) { + cbt->liveness.nonlive_timeouts++; + } + + /* Check for one-hop timeout */ + if (did_onehop) { + cbt->liveness.timeouts_after_firsthop[cbt->liveness.after_firsthop_idx]=1; + cbt->liveness.after_firsthop_idx++; + cbt->liveness.after_firsthop_idx %= RECENT_CIRCUITS; + } +} + +/** + * Returns false if the network has not received a cell or tls handshake + * in the past NETWORK_NOTLIVE_TIMEOUT_COUNT circuits. + * + * Also has the side effect of rewinding the circuit time history + * in the case of recent liveness changes. + */ +int +circuit_build_times_network_check_live(circuit_build_times_t *cbt) +{ + time_t now = approx_time(); + if (cbt->liveness.nonlive_timeouts >= NETWORK_NONLIVE_DISCARD_COUNT) { + if (!cbt->liveness.nonlive_discarded) { + cbt->liveness.nonlive_discarded = 1; + log_notice(LD_CIRC, "Network is no longer live (too many recent " + "circuit timeouts). Dead for %ld seconds.", + (long int)(now - cbt->liveness.network_last_live)); + /* Only discard NETWORK_NONLIVE_TIMEOUT_COUNT-1 because we stopped + * counting after that */ + circuit_build_times_rewind_history(cbt, NETWORK_NONLIVE_TIMEOUT_COUNT-1); + } + return 0; + } else if (cbt->liveness.nonlive_timeouts >= NETWORK_NONLIVE_TIMEOUT_COUNT) { + if (cbt->timeout_ms < circuit_build_times_get_initial_timeout()) { + log_notice(LD_CIRC, + "Network is flaky. No activity for %ld seconds. " + "Temporarily raising timeout to %lds.", + (long int)(now - cbt->liveness.network_last_live), + lround(circuit_build_times_get_initial_timeout()/1000)); + cbt->timeout_ms = circuit_build_times_get_initial_timeout(); + } + + return 0; + } + + return 1; +} + +/** + * Returns true if we have seen more than MAX_RECENT_TIMEOUT_COUNT of + * the past RECENT_CIRCUITS time out after the first hop. Used to detect + * if the network connection has changed significantly. + * + * Also resets the entire timeout history in this case and causes us + * to restart the process of building test circuits and estimating a + * new timeout. + */ +int +circuit_build_times_network_check_changed(circuit_build_times_t *cbt) +{ + int total_build_times = cbt->total_build_times; + int timeout_count=0; + int i; + + /* how many of our recent circuits made it to the first hop but then + * timed out? */ + for (i = 0; i < RECENT_CIRCUITS; i++) { + timeout_count += cbt->liveness.timeouts_after_firsthop[i]; + } + + /* If 75% of our recent circuits are timing out after the first hop, + * we need to re-estimate a new initial alpha and timeout. */ + if (timeout_count < MAX_RECENT_TIMEOUT_COUNT) { + return 0; + } + + circuit_build_times_reset(cbt); + memset(cbt->liveness.timeouts_after_firsthop, 0, + sizeof(cbt->liveness.timeouts_after_firsthop)); + cbt->liveness.after_firsthop_idx = 0; + + /* Check to see if this has happened before. If so, double the timeout + * to give people on abysmally bad network connections a shot at access */ + if (cbt->timeout_ms >= circuit_build_times_get_initial_timeout()) { + cbt->timeout_ms *= 2; + } else { + cbt->timeout_ms = circuit_build_times_get_initial_timeout(); + } + + log_notice(LD_CIRC, + "Network connection speed appears to have changed. Resetting " + "timeout to %lds after %d timeouts and %d buildtimes.", + lround(cbt->timeout_ms/1000), timeout_count, total_build_times); + + return 1; +} + +/** + * Store a timeout as a synthetic value. + * + * Returns true if the store was successful and we should possibly + * update our timeout estimate. + */ +int +circuit_build_times_add_timeout(circuit_build_times_t *cbt, + int did_onehop, + time_t start_time) +{ + circuit_build_times_network_timeout(cbt, did_onehop, start_time); + + /* Only count timeouts if network is live.. */ + if (!circuit_build_times_network_check_live(cbt)) { + return 0; + } + + /* If there are a ton of timeouts, we should reduce + * the circuit build timeout */ + if (circuit_build_times_network_check_changed(cbt)) { + return 0; + } + + if (!cbt->have_computed_timeout) { + /* Store a timeout before we have enough data */ + cbt->pre_timeouts++; + log_info(LD_CIRC, + "Not enough circuits yet to calculate a new build timeout." + " Need %d more.", + MIN_CIRCUITS_TO_OBSERVE-cbt->total_build_times); + return 0; + } + + circuit_build_times_count_pretimeouts(cbt); + circuit_build_times_add_timeout_worker(cbt, BUILDTIMEOUT_QUANTILE_CUTOFF); + + return 1; +} + +/** + * Estimate a new timeout based on history and set our timeout + * variable accordingly. + */ +void +circuit_build_times_set_timeout(circuit_build_times_t *cbt) +{ + if (cbt->total_build_times < MIN_CIRCUITS_TO_OBSERVE) { + return; + } + + circuit_build_times_count_pretimeouts(cbt); + circuit_build_times_update_alpha(cbt); + + cbt->timeout_ms = circuit_build_times_calculate_timeout(cbt, + BUILDTIMEOUT_QUANTILE_CUTOFF); + + cbt->have_computed_timeout = 1; + + if (cbt->timeout_ms < BUILD_TIMEOUT_MIN_VALUE) { + log_warn(LD_CIRC, "Set buildtimeout to low value %lfms. Setting to %dms", + cbt->timeout_ms, BUILD_TIMEOUT_MIN_VALUE); + cbt->timeout_ms = BUILD_TIMEOUT_MIN_VALUE; + } + + log_info(LD_CIRC, + "Set circuit build timeout to %lds (%lfms, Xm: %d, a: %lf) " + "based on %d circuit times", lround(cbt->timeout_ms/1000), + cbt->timeout_ms, cbt->Xm, cbt->alpha, cbt->total_build_times); + +} + /** Iterate over values of circ_id, starting from conn-\>next_circ_id, * and with the high bit specified by conn-\>circ_id_type, until we get * a circ_id that is not in use by any other circuit on that conn. @@ -527,9 +1361,16 @@ inform_testing_reachability(void) routerinfo_t *me = router_get_my_routerinfo(); if (!me) return 0; - if (me->dir_port) + control_event_server_status(LOG_NOTICE, + "CHECKING_REACHABILITY ORADDRESS=%s:%d", + me->address, me->or_port); + if (me->dir_port) { tor_snprintf(dirbuf, sizeof(dirbuf), " and DirPort %s:%d", me->address, me->dir_port); + control_event_server_status(LOG_NOTICE, + "CHECKING_REACHABILITY DIRADDRESS=%s:%d", + me->address, me->dir_port); + } log(LOG_NOTICE, LD_OR, "Now checking whether ORPort %s:%d%s %s reachable... " "(this may take up to %d minutes -- look for log " "messages indicating success)", @@ -537,6 +1378,7 @@ inform_testing_reachability(void) me->dir_port ? dirbuf : "", me->dir_port ? "are" : "is", TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT/60); + return 1; } @@ -633,8 +1475,17 @@ circuit_send_next_onion_skin(origin_circuit_t *circ) log_debug(LD_CIRC,"starting to send subsequent skin."); hop = onion_next_hop_in_cpath(circ->cpath); if (!hop) { + struct timeval end; + long timediff; + tor_gettimeofday(&end); + timediff = tv_mdiff(&circ->_base.highres_created, &end); + if (timediff > INT32_MAX) + timediff = INT32_MAX; /* done building the circuit. whew. */ circuit_set_state(TO_CIRCUIT(circ), CIRCUIT_STATE_OPEN); + circuit_build_times_add_time(&circ_times, (build_time_t)timediff); + circuit_build_times_network_circ_success(&circ_times); + circuit_build_times_set_timeout(&circ_times); log_info(LD_CIRC,"circuit built!"); circuit_reset_failure_count(0); if (circ->build_state->onehop_tunnel) @@ -1436,13 +2287,16 @@ choose_good_exit_server(uint8_t purpose, routerlist_t *dir, /** Log a warning if the user specified an exit for the circuit that * has been excluded from use by ExcludeNodes or ExcludeExitNodes. */ static void -warn_if_last_router_excluded(uint8_t purpose, const extend_info_t *exit) +warn_if_last_router_excluded(origin_circuit_t *circ, const extend_info_t *exit) { or_options_t *options = get_options(); routerset_t *rs = options->ExcludeNodes; const char *description; - int severity; int domain = LD_CIRC; + uint8_t purpose = circ->_base.purpose; + + if (circ->build_state->onehop_tunnel) + return; switch (purpose) { @@ -1455,48 +2309,40 @@ warn_if_last_router_excluded(uint8_t purpose, const extend_info_t *exit) (int)purpose); return; case CIRCUIT_PURPOSE_C_GENERAL: + if (circ->build_state->is_internal) + return; description = "Requested exit node"; rs = options->_ExcludeExitNodesUnion; - severity = LOG_WARN; break; case CIRCUIT_PURPOSE_C_INTRODUCING: case CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT: case CIRCUIT_PURPOSE_C_INTRODUCE_ACKED: - description = "Introduction point for hidden service"; - severity = LOG_INFO; - break; + case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO: + case CIRCUIT_PURPOSE_S_CONNECT_REND: + case CIRCUIT_PURPOSE_S_REND_JOINED: + case CIRCUIT_PURPOSE_TESTING: + return; case CIRCUIT_PURPOSE_C_ESTABLISH_REND: case CIRCUIT_PURPOSE_C_REND_READY: case CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED: case CIRCUIT_PURPOSE_C_REND_JOINED: description = "Chosen rendezvous point"; - severity = LOG_WARN; domain = LD_BUG; break; - case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO: - description = "Chosen introduction point"; - severity = LOG_INFO; - break; - case CIRCUIT_PURPOSE_S_CONNECT_REND: - case CIRCUIT_PURPOSE_S_REND_JOINED: - description = "Client-selected rendezvous point"; - severity = LOG_INFO; - break; - case CIRCUIT_PURPOSE_TESTING: - description = "Target for testing circuit"; - severity = LOG_INFO; - break; case CIRCUIT_PURPOSE_CONTROLLER: rs = options->_ExcludeExitNodesUnion; description = "Controller-selected circuit target"; - severity = LOG_WARN; break; } - if (routerset_contains_extendinfo(rs, exit)) - log_fn(severity, domain, "%s '%s' is in ExcludeNodes%s. Using anyway.", + if (routerset_contains_extendinfo(rs, exit)) { + log_fn(LOG_WARN, domain, "%s '%s' is in ExcludeNodes%s. Using anyway " + "(circuit purpose %d).", description,exit->nickname, - rs==options->ExcludeNodes?"":" or ExcludeExitNodes."); + rs==options->ExcludeNodes?"":" or ExcludeExitNodes", + (int)purpose); + circuit_log_path(LOG_WARN, domain, circ); + } return; } @@ -1521,7 +2367,7 @@ onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit) } if (exit) { /* the circuit-builder pre-requested one */ - warn_if_last_router_excluded(circ->_base.purpose, exit); + warn_if_last_router_excluded(circ, exit); log_info(LD_CIRC,"Using requested exit node '%s'", exit->nickname); exit = extend_info_dup(exit); } else { /* we have to decide one */ @@ -1568,6 +2414,7 @@ int circuit_extend_to_new_exit(origin_circuit_t *circ, extend_info_t *exit) { int err_reason = 0; + warn_if_last_router_excluded(circ, exit); circuit_append_new_exit(circ, exit); circuit_set_state(TO_CIRCUIT(circ), CIRCUIT_STATE_BUILDING); if ((err_reason = circuit_send_next_onion_skin(circ))<0) { @@ -1825,7 +2672,7 @@ onion_append_hop(crypt_path_t **head_ptr, extend_info_t *choice) hop->extend_info = extend_info_dup(choice); - hop->package_window = CIRCWINDOW_START; + hop->package_window = circuit_initial_package_window(); hop->deliver_window = CIRCWINDOW_START; return 0; diff --git a/src/or/circuitlist.c b/src/or/circuitlist.c index 065559620..259666732 100644 --- a/src/or/circuitlist.c +++ b/src/or/circuitlist.c @@ -361,14 +361,27 @@ circuit_purpose_to_controller_string(uint8_t purpose) } } +/** Pick a reasonable package_window to start out for our circuits. + * Originally this was hard-coded at 1000, but now the consensus votes + * on the answer. See proposal 168. */ +int32_t +circuit_initial_package_window(void) +{ + networkstatus_t *consensus = networkstatus_get_latest_consensus(); + if (consensus) + return networkstatus_get_param(consensus, "circwindow", CIRCWINDOW_START); + return CIRCWINDOW_START; +} + /** Initialize the common elements in a circuit_t, and add it to the global * list. */ static void init_circuit_base(circuit_t *circ) { circ->timestamp_created = time(NULL); + tor_gettimeofday(&circ->highres_created); - circ->package_window = CIRCWINDOW_START; + circ->package_window = circuit_initial_package_window(); circ->deliver_window = CIRCWINDOW_START; circuit_add(circ); @@ -395,6 +408,8 @@ origin_circuit_new(void) init_circuit_base(TO_CIRCUIT(circ)); + circ_times.last_circ_at = approx_time(); + return circ; } diff --git a/src/or/circuituse.c b/src/or/circuituse.c index a2110a4da..1022ae16c 100644 --- a/src/or/circuituse.c +++ b/src/or/circuituse.c @@ -20,6 +20,8 @@ extern circuit_t *global_circuitlist; /* from circuitlist.c */ static void circuit_expire_old_circuits(time_t now); static void circuit_increment_failure_count(void); +long int lround(double x); + /** Return 1 if <b>circ</b> could be returned by circuit_get_best(). * Else return 0. */ @@ -263,16 +265,18 @@ circuit_conforms_to_options(const origin_circuit_t *circ, void circuit_expire_building(time_t now) { - circuit_t *victim, *circ = global_circuitlist; - time_t general_cutoff = now - get_options()->CircuitBuildTimeout; - time_t begindir_cutoff = now - get_options()->CircuitBuildTimeout/2; + circuit_t *victim, *next_circ = global_circuitlist; + /* circ_times.timeout is BUILD_TIMEOUT_INITIAL_VALUE if we haven't + * decided on a customized one yet */ + time_t general_cutoff = now - lround(circ_times.timeout_ms/1000); + time_t begindir_cutoff = now - lround(circ_times.timeout_ms/2000); time_t introcirc_cutoff = begindir_cutoff; cpath_build_state_t *build_state; - while (circ) { + while (next_circ) { time_t cutoff; - victim = circ; - circ = circ->next; + victim = next_circ; + next_circ = next_circ->next; if (!CIRCUIT_IS_ORIGIN(victim) || /* didn't originate here */ victim->marked_for_close) /* don't mess with marked circs */ continue; @@ -343,6 +347,12 @@ circuit_expire_building(time_t now) continue; break; } + } else { /* circuit not open, consider recording failure as timeout */ + int first_hop_succeeded = TO_ORIGIN_CIRCUIT(victim)->cpath && + TO_ORIGIN_CIRCUIT(victim)->cpath->state == CPATH_STATE_OPEN; + if (circuit_build_times_add_timeout(&circ_times, first_hop_succeeded, + victim->timestamp_created)) + circuit_build_times_set_timeout(&circ_times); } if (victim->n_conn) @@ -431,11 +441,11 @@ circuit_stream_is_being_handled(edge_connection_t *conn, } /** Don't keep more than this many unused open circuits around. */ -#define MAX_UNUSED_OPEN_CIRCUITS 12 +#define MAX_UNUSED_OPEN_CIRCUITS 14 /** Figure out how many circuits we have open that are clean. Make * sure it's enough for all the upcoming behaviors we predict we'll have. - * But if we have too many, close the not-so-useful ones. + * But put an upper bound on the total number of circuits. */ static void circuit_predict_and_launch_new(void) @@ -517,6 +527,19 @@ circuit_predict_and_launch_new(void) circuit_launch_by_router(CIRCUIT_PURPOSE_C_GENERAL, NULL, flags); return; } + + /* Finally, check to see if we still need more circuits to learn + * a good build timeout. But if we're close to our max number we + * want, don't do another -- we want to leave a few slots open so + * we can still build circuits preemptively as needed. */ + if (num < MAX_UNUSED_OPEN_CIRCUITS-2 && + circuit_build_times_needs_circuits_now(&circ_times)) { + flags = CIRCLAUNCH_NEED_CAPACITY; + log_info(LD_CIRC, + "Have %d clean circs need another buildtime test circ.", num); + circuit_launch_by_router(CIRCUIT_PURPOSE_C_GENERAL, NULL, flags); + return; + } } /** Build a new test circuit every 5 minutes */ @@ -624,6 +647,11 @@ circuit_detach_stream(circuit_t *circ, edge_connection_t *conn) tor_fragile_assert(); } +/** If we haven't yet decided on a good timeout value for circuit + * building, we close idles circuits aggressively so we can get more + * data points. */ +#define IDLE_TIMEOUT_WHILE_LEARNING (10*60) + /** Find each circuit that has been unused for too long, or dirty * for too long and has no streams on it: mark it for close. */ @@ -631,7 +659,15 @@ static void circuit_expire_old_circuits(time_t now) { circuit_t *circ; - time_t cutoff = now - get_options()->CircuitIdleTimeout; + time_t cutoff; + + if (circuit_build_times_needs_circuits(&circ_times)) { + /* Circuits should be shorter lived if we need more of them + * for learning a good build timeout */ + cutoff = now - IDLE_TIMEOUT_WHILE_LEARNING; + } else { + cutoff = now - get_options()->CircuitIdleTimeout; + } for (circ = global_circuitlist; circ; circ = circ->next) { if (circ->marked_for_close || ! CIRCUIT_IS_ORIGIN(circ)) @@ -724,17 +760,12 @@ circuit_testing_opened(origin_circuit_t *circ) static void circuit_testing_failed(origin_circuit_t *circ, int at_last_hop) { - routerinfo_t *me = router_get_my_routerinfo(); if (server_mode(get_options()) && check_whether_orport_reachable()) return; - if (!me) - return; log_info(LD_GENERAL, "Our testing circuit (to see if your ORPort is reachable) " "has failed. I'll try again later."); - control_event_server_status(LOG_WARN, "REACHABILITY_FAILED ORADDRESS=%s:%d", - me->address, me->or_port); /* These aren't used yet. */ (void)circ; @@ -811,6 +842,9 @@ circuit_build_failed(origin_circuit_t *circ) "(%s:%d). I'm going to try to rotate to a better connection.", n_conn->_base.address, n_conn->_base.port); n_conn->is_bad_for_new_circs = 1; + } else { + log_info(LD_OR, + "Our circuit died before the first hop with no connection"); } if (n_conn_id) { entry_guard_register_connect_status(n_conn_id, 0, 1, time(NULL)); diff --git a/src/or/command.c b/src/or/command.c index c36874be5..67e463723 100644 --- a/src/or/command.c +++ b/src/or/command.c @@ -575,7 +575,7 @@ command_process_netinfo_cell(cell_t *cell, or_connection_t *conn) /* Consider all the other addresses; if any matches, this connection is * "canonical." */ tor_addr_t addr; - const char *next = decode_address_from_payload(&addr, cp, end-cp); + const char *next = decode_address_from_payload(&addr, cp, (int)(end-cp)); if (next == NULL) { log_fn(LOG_PROTOCOL_WARN, LD_OR, "Bad address in netinfo cell; closing connection."); @@ -610,9 +610,11 @@ command_process_netinfo_cell(cell_t *cell, or_connection_t *conn) conn->_base.address, (int)conn->_base.port, apparent_skew>0 ? "ahead" : "behind", dbuf, apparent_skew>0 ? "behind" : "ahead"); - control_event_general_status(LOG_WARN, - "CLOCK_SKEW SKEW=%ld SOURCE=OR:%s:%d", - apparent_skew, conn->_base.address, conn->_base.port); + if (severity == LOG_WARN) /* only tell the controller if an authority */ + control_event_general_status(LOG_WARN, + "CLOCK_SKEW SKEW=%ld SOURCE=OR:%s:%d", + apparent_skew, + conn->_base.address, conn->_base.port); } /* XXX maybe act on my_apparent_addr, if the source is sufficiently diff --git a/src/or/config.c b/src/or/config.c index b7317c75d..474b887ba 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -164,10 +164,11 @@ static config_var_t _option_vars[] = { V(BridgeRecordUsageByCountry, BOOL, "1"), V(BridgeRelay, BOOL, "0"), V(CellStatistics, BOOL, "0"), - V(CircuitBuildTimeout, INTERVAL, "1 minute"), + V(CircuitBuildTimeout, INTERVAL, "0"), V(CircuitIdleTimeout, INTERVAL, "1 hour"), V(ClientDNSRejectInternalAddresses, BOOL,"1"), V(ClientOnly, BOOL, "0"), + V(ConsensusParams, STRING, NULL), V(ConnLimit, UINT, "1000"), V(ConstrainedSockets, BOOL, "0"), V(ConstrainedSockSize, MEMUNIT, "8192"), @@ -408,6 +409,10 @@ static config_var_t _state_vars[] = { V(LastRotatedOnionKey, ISOTIME, NULL), V(LastWritten, ISOTIME, NULL), + V(TotalBuildTimes, UINT, NULL), + VAR("CircuitBuildTimeBin", LINELIST_S, BuildtimeHistogram, NULL), + VAR("BuildtimeHistogram", LINELIST_V, BuildtimeHistogram, NULL), + { NULL, CONFIG_TYPE_OBSOLETE, 0, NULL } }; @@ -596,6 +601,10 @@ static config_var_description_t options_description[] = { /* Hidden service options: HiddenService: dir,excludenodes, nodes, * options, port. PublishHidServDescriptor */ + /* Circuit build time histogram options */ + { "CircuitBuildTimeBin", "Histogram of recent circuit build times"}, + { "TotalBuildTimes", "Total number of buildtimes in histogram"}, + /* Nonpersistent options: __LeaveStreamsUnattached, __AllDirActionsPrivate */ { NULL, NULL }, }; @@ -1506,7 +1515,10 @@ expand_abbrev(config_format_t *fmt, const char *option, int command_line, fmt->abbrevs[i].abbreviated, fmt->abbrevs[i].full); } - return fmt->abbrevs[i].full; + /* Keep going through the list in case we want to rewrite it more. + * (We could imagine recursing here, but I don't want to get the + * user into an infinite loop if we craft our list wrong.) */ + option = fmt->abbrevs[i].full; } } return option; @@ -2521,7 +2533,8 @@ is_local_addr(const tor_addr_t *addr) * the same /24 as last_resolved_addr will be the same as checking whether * it was on net 0, which is already done by is_internal_IP. */ - if ((last_resolved_addr & 0xffffff00ul) == (ip & 0xffffff00ul)) + if ((last_resolved_addr & (uint32_t)0xffffff00ul) + == (ip & (uint32_t)0xffffff00ul)) return 1; } return 0; @@ -2909,11 +2922,6 @@ compute_publishserverdescriptor(or_options_t *options) /** Highest allowable value for RendPostPeriod. */ #define MAX_DIR_PERIOD (MIN_ONION_KEY_LIFETIME/2) -/** Lowest allowable value for CircuitBuildTimeout; values too low will - * increase network load because of failing connections being retried, and - * might prevent users from connecting to the network at all. */ -#define MIN_CIRCUIT_BUILD_TIMEOUT 30 - /** Lowest allowable value for MaxCircuitDirtiness; if this is too low, Tor * will generate too many circuits and potentially overload the network. */ #define MIN_MAX_CIRCUIT_DIRTINESS 10 @@ -3360,12 +3368,6 @@ options_validate(or_options_t *old_options, or_options_t *options, options->RendPostPeriod = MAX_DIR_PERIOD; } - if (options->CircuitBuildTimeout < MIN_CIRCUIT_BUILD_TIMEOUT) { - log(LOG_WARN, LD_CONFIG, "CircuitBuildTimeout option is too short; " - "raising to %d seconds.", MIN_CIRCUIT_BUILD_TIMEOUT); - options->CircuitBuildTimeout = MIN_CIRCUIT_BUILD_TIMEOUT; - } - if (options->MaxCircuitDirtiness < MIN_MAX_CIRCUIT_DIRTINESS) { log(LOG_WARN, LD_CONFIG, "MaxCircuitDirtiness option is too short; " "raising to %d seconds.", MIN_MAX_CIRCUIT_DIRTINESS); @@ -4280,7 +4282,7 @@ options_init_from_string(const char *cf, err: config_free(&options_format, newoptions); if (*msg) { - int len = strlen(*msg)+256; + int len = (int)strlen(*msg)+256; char *newmsg = tor_malloc(len); tor_snprintf(newmsg, len, "Failed to parse/validate config: %s", *msg); @@ -4860,35 +4862,28 @@ config_parse_units(const char *val, struct unit_table_t *u, int *ok) uint64_t v = 0; double d = 0; int use_float = 0; - - smartlist_t *sl; + char *cp; tor_assert(ok); - sl = smartlist_create(); - smartlist_split_string(sl, val, NULL, - SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 3); - if (smartlist_len(sl) < 1 || smartlist_len(sl) > 2) { - *ok = 0; - goto done; - } - - v = tor_parse_uint64(smartlist_get(sl,0), 10, 0, UINT64_MAX, ok, NULL); - if (!*ok) { - int r = sscanf(smartlist_get(sl,0), "%lf", &d); - if (r == 0 || d < 0) + v = tor_parse_uint64(val, 10, 0, UINT64_MAX, ok, &cp); + if (!*ok || (cp && *cp == '.')) { + d = tor_parse_double(val, 0, UINT64_MAX, ok, &cp); + if (!*ok) goto done; use_float = 1; } - if (smartlist_len(sl) == 1) { + if (!cp) { *ok = 1; v = use_float ? DBL_TO_U64(d) : v; goto done; } + cp = (char*) eat_whitespace(cp); + for ( ;u->unit;++u) { - if (!strcasecmp(u->unit, smartlist_get(sl,1))) { + if (!strcasecmp(u->unit, cp)) { if (use_float) v = u->multiplier * d; else @@ -4897,11 +4892,9 @@ config_parse_units(const char *val, struct unit_table_t *u, int *ok) goto done; } } - log_warn(LD_CONFIG, "Unknown unit '%s'.", (char*)smartlist_get(sl,1)); + log_warn(LD_CONFIG, "Unknown unit '%s'.", cp); *ok = 0; done: - SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp)); - smartlist_free(sl); if (*ok) return v; @@ -4916,7 +4909,8 @@ config_parse_units(const char *val, struct unit_table_t *u, int *ok) static uint64_t config_parse_memunit(const char *s, int *ok) { - return config_parse_units(s, memory_units, ok); + uint64_t u = config_parse_units(s, memory_units, ok); + return u; } /** Parse a string in the format "number unit", where unit is a unit of time. @@ -5066,6 +5060,10 @@ or_state_set(or_state_t *new_state) log_warn(LD_GENERAL,"Unparseable bandwidth history state: %s",err); tor_free(err); } + if (circuit_build_times_parse_state(&circ_times, global_state, &err) < 0) { + log_warn(LD_GENERAL,"%s",err); + tor_free(err); + } } /** Reload the persistent state from disk, generating a new state as needed. @@ -5198,6 +5196,7 @@ or_state_save(time_t now) * to avoid redundant writes. */ entry_guards_update_state(global_state); rep_hist_update_state(global_state); + circuit_build_times_update_state(&circ_times, global_state); if (accounting_is_enabled(get_options())) accounting_run_housekeeping(now); diff --git a/src/or/connection.c b/src/or/connection.c index 49c94c111..18464d209 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -2346,7 +2346,7 @@ loop_again: return -1; } if (conn->linked_conn) { - /* The other side's handle_write will never actually get called, so + /* The other side's handle_write() will never actually get called, so * we need to invoke the appropriate callbacks ourself. */ connection_t *linked = conn->linked_conn; @@ -2363,7 +2363,7 @@ loop_again: if (!buf_datalen(linked->outbuf) && conn->active_on_link) connection_stop_reading_from_linked_conn(conn); } - /* If we hit the EOF, call connection_reached_eof. */ + /* If we hit the EOF, call connection_reached_eof(). */ if (!conn->marked_for_close && conn->inbuf_reached_eof && connection_reached_eof(conn) < 0) { @@ -2589,7 +2589,7 @@ connection_handle_write(connection_t *conn, int force) return 0; /* do nothing */ if (conn->in_flushed_some) { - log_warn(LD_BUG, "called recursively from inside conn->in_flushed_some()"); + log_warn(LD_BUG, "called recursively from inside conn->in_flushed_some"); return 0; } @@ -2678,8 +2678,8 @@ connection_handle_write(connection_t *conn, int force) if (!connection_is_reading(conn)) { connection_stop_writing(conn); conn->write_blocked_on_bw = 1; - /* we'll start reading again when the next second arrives, - * and then also start writing again. + /* we'll start reading again when we get more tokens in our + * read bucket; then we'll start writing again too. */ } /* else no problem, we're already reading */ @@ -3067,7 +3067,7 @@ client_check_address_changed(int sock) return; } - /* Okay. If we've used this address previously, we're okay. */ + /* If we've used this address previously, we're okay. */ ip_out = ntohl(out_addr.sin_addr.s_addr); SMARTLIST_FOREACH(outgoing_addrs, uint32_t*, ip_ptr, if (*ip_ptr == ip_out) return; diff --git a/src/or/connection_or.c b/src/or/connection_or.c index 8c8b5496a..aa26bf8f4 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -1036,6 +1036,8 @@ connection_tls_finish_handshake(or_connection_t *conn) digest_rcvd) < 0) return -1; + circuit_build_times_network_is_live(&circ_times); + if (tor_tls_used_v1_handshake(conn->tls)) { conn->link_proto = 1; if (!started_here) { @@ -1087,6 +1089,7 @@ connection_or_set_state_open(or_connection_t *conn) control_event_or_conn_status(conn, OR_CONN_EVENT_CONNECTED, 0); if (started_here) { + circuit_build_times_network_is_live(&circ_times); rep_hist_note_connect_succeeded(conn->identity_digest, now); if (entry_guard_register_connect_status(conn->identity_digest, 1, 0, now) < 0) { @@ -1187,6 +1190,7 @@ connection_or_process_cells_from_inbuf(or_connection_t *conn) if (connection_fetch_var_cell_from_buf(conn, &var_cell)) { if (!var_cell) return 0; /* not yet. */ + circuit_build_times_network_is_live(&circ_times); command_process_var_cell(var_cell, conn); var_cell_free(var_cell); } else { @@ -1196,6 +1200,7 @@ connection_or_process_cells_from_inbuf(or_connection_t *conn) available? */ return 0; /* not yet */ + circuit_build_times_network_is_live(&circ_times); connection_fetch_from_buf(buf, CELL_NETWORK_SIZE, TO_CONN(conn)); /* retrieve cell info from buf (create the host-order struct from the diff --git a/src/or/control.c b/src/or/control.c index 068628afd..67ee37ae5 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -1696,7 +1696,11 @@ getinfo_helper_events(control_connection_t *control_conn, *answer = tor_strdup(has_completed_circuit ? "1" : "0"); } else if (!strcmp(question, "status/enough-dir-info")) { *answer = tor_strdup(router_have_minimum_dir_info() ? "1" : "0"); - } else if (!strcmp(question, "status/good-server-descriptor")) { + } else if (!strcmp(question, "status/good-server-descriptor") || + !strcmp(question, "status/accepted-server-descriptor")) { + /* They're equivalent for now, until we can figure out how to make + * good-server-descriptor be what we want. See comment in + * control-spec.txt. */ *answer = tor_strdup(directories_have_accepted_server_descriptor() ? "1" : "0"); } else if (!strcmp(question, "status/reachability-succeeded/or")) { @@ -2495,7 +2499,7 @@ handle_control_resolve(control_connection_t *conn, uint32_t len, int is_reverse = 0; (void) len; /* body is nul-terminated; it's safe to ignore the length */ - if (!(conn->event_mask & (1L<<EVENT_ADDRMAP))) { + if (!(conn->event_mask & ((uint32_t)1L<<EVENT_ADDRMAP))) { log_warn(LD_CONTROL, "Controller asked us to resolve an address, but " "isn't listening for ADDRMAP events. It probably won't see " "the answer."); diff --git a/src/or/directory.c b/src/or/directory.c index d37da18e6..5fe2de4ee 100644 --- a/src/or/directory.c +++ b/src/or/directory.c @@ -554,11 +554,6 @@ void connection_dir_request_failed(dir_connection_t *conn) { if (directory_conn_is_self_reachability_test(conn)) { - routerinfo_t *me = router_get_my_routerinfo(); - if (me) - control_event_server_status(LOG_WARN, - "REACHABILITY_FAILED DIRADDRESS=%s:%d", - me->address, me->dir_port); return; /* this was a test fetch. don't retry. */ } if (entry_list_can_grow(get_options())) @@ -886,7 +881,7 @@ static char * directory_get_consensus_url(int supports_conditional_consensus) { char *url; - int len; + size_t len; if (supports_conditional_consensus) { char *authority_id_list; @@ -2337,7 +2332,7 @@ client_likes_consensus(networkstatus_t *v, const char *want_url) need_at_least = smartlist_len(want_authorities)/2+1; SMARTLIST_FOREACH(want_authorities, const char *, d, { char want_digest[DIGEST_LEN]; - int want_len = strlen(d)/2; + size_t want_len = strlen(d)/2; if (want_len > DIGEST_LEN) want_len = DIGEST_LEN; diff --git a/src/or/dirserv.c b/src/or/dirserv.c index a240fd4bb..f6aef01e8 100644 --- a/src/or/dirserv.c +++ b/src/or/dirserv.c @@ -1864,7 +1864,7 @@ version_from_platform(const char *platform) * NS_V2 - Output an entry suitable for a V2 NS opinion document * NS_V3_CONSENSUS - Output the first portion of a V3 NS consensus entry * NS_V3_VOTE - Output a complete V3 NS vote - * NS_CONTROL_PORT - Output a NS docunent for the control port + * NS_CONTROL_PORT - Output a NS document for the control port */ int routerstatus_format_entry(char *buf, size_t buf_len, @@ -2324,7 +2324,7 @@ measured_bw_line_apply(measured_bw_line_t *parsed_line, if (rs) { rs->has_measured_bw = 1; - rs->measured_bw = parsed_line->bw; + rs->measured_bw = (uint32_t)parsed_line->bw; } else { log_info(LD_DIRSERV, "Node ID %s not found in routerstatus list", parsed_line->node_hex); @@ -2553,6 +2553,13 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_env_t *private_key, } smartlist_sort_strings(v3_out->known_flags); + if (options->ConsensusParams) { + v3_out->net_params = smartlist_create(); + 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); diff --git a/src/or/dirvote.c b/src/or/dirvote.c index 58b02da5c..358246ae9 100644 --- a/src/or/dirvote.c +++ b/src/or/dirvote.c @@ -24,7 +24,9 @@ static int dirvote_publish_consensus(void); static char *make_consensus_method_list(int low, int high); /** The highest consensus method that we currently support. */ -#define MAX_SUPPORTED_CONSENSUS_METHOD 6 +#define MAX_SUPPORTED_CONSENSUS_METHOD 7 + +#define MIN_METHOD_FOR_PARAMS 7 /* ===== * Voting @@ -97,6 +99,7 @@ format_networkstatus_vote(crypto_pk_env_t *private_signing_key, char fu[ISO_TIME_LEN+1]; char vu[ISO_TIME_LEN+1]; char *flags = smartlist_join_strings(v3_ns->known_flags, " ", 0, NULL); + char *params; authority_cert_t *cert = v3_ns->cert; char *methods = make_consensus_method_list(1, MAX_SUPPORTED_CONSENSUS_METHOD); @@ -105,6 +108,11 @@ format_networkstatus_vote(crypto_pk_env_t *private_signing_key, format_iso_time(fu, v3_ns->fresh_until); format_iso_time(vu, v3_ns->valid_until); + if (v3_ns->net_params) + params = smartlist_join_strings(v3_ns->net_params, " ", 0, NULL); + else + params = tor_strdup(""); + tor_assert(cert); tor_snprintf(status, len, "network-status-version 3\n" @@ -117,6 +125,7 @@ format_networkstatus_vote(crypto_pk_env_t *private_signing_key, "voting-delay %d %d\n" "%s" /* versions */ "known-flags %s\n" + "params %s\n" "dir-source %s %s %s %s %d %d\n" "contact %s\n", v3_ns->type == NS_TYPE_VOTE ? "vote" : "opinion", @@ -125,9 +134,11 @@ format_networkstatus_vote(crypto_pk_env_t *private_signing_key, v3_ns->vote_seconds, v3_ns->dist_seconds, version_lines, flags, + params, voter->nickname, fingerprint, voter->address, ipaddr, voter->dir_port, voter->or_port, voter->contact); + tor_free(params); tor_free(flags); tor_free(methods); outp = status + strlen(status); @@ -507,6 +518,89 @@ compute_consensus_versions_list(smartlist_t *lst, int n_versioning) return result; } +/** Helper: given a list of valid networkstatus_t, return a new string + * containing the contents of the consensus network parameter set. + */ +/* private */ char * +dirvote_compute_params(smartlist_t *votes) +{ + int i; + int32_t *vals; + + int cur_param_len; + const char *cur_param; + const char *eq; + char *result; + + const int n_votes = smartlist_len(votes); + smartlist_t *output; + smartlist_t *param_list = smartlist_create(); + + /* We require that the parameter lists in the votes are well-formed: that + is, that their keywords are unique and sorted, and that their values are + between INT32_MIN and INT32_MAX inclusive. This should be guaranteed by + the parsing code. */ + + vals = tor_malloc(sizeof(int)*n_votes); + + SMARTLIST_FOREACH_BEGIN(votes, networkstatus_t *, v) { + if (!v->net_params) + continue; + smartlist_add_all(param_list, v->net_params); + } SMARTLIST_FOREACH_END(v); + + if (smartlist_len(param_list) == 0) { + tor_free(vals); + smartlist_free(param_list); + return NULL; + } + + smartlist_sort_strings(param_list); + i = 0; + cur_param = smartlist_get(param_list, 0); + eq = strchr(cur_param, '='); + tor_assert(eq); + cur_param_len = (int)(eq+1 - cur_param); + + output = smartlist_create(); + + SMARTLIST_FOREACH_BEGIN(param_list, const char *, param) { + const char *next_param; + int ok=0; + eq = strchr(param, '='); + tor_assert(i<n_votes); + vals[i++] = (int32_t) + tor_parse_long(eq+1, 10, INT32_MIN, INT32_MAX, &ok, NULL); + tor_assert(ok); + + if (param_sl_idx+1 == smartlist_len(param_list)) + next_param = NULL; + else + next_param = smartlist_get(param_list, param_sl_idx+1); + if (!next_param || strncmp(next_param, param, cur_param_len)) { + /* We've reached the end of a series. */ + int32_t median = median_int32(vals, i); + char *out_string = tor_malloc(64+cur_param_len); + memcpy(out_string, param, cur_param_len); + tor_snprintf(out_string+cur_param_len,64, "%ld", (long)median); + smartlist_add(output, out_string); + + i = 0; + if (next_param) { + eq = strchr(next_param, '='); + cur_param_len = (int)(eq+1 - next_param); + } + } + } SMARTLIST_FOREACH_END(param); + + result = smartlist_join_strings(output, " ", 0, NULL); + SMARTLIST_FOREACH(output, char *, cp, tor_free(cp)); + smartlist_free(output); + smartlist_free(param_list); + tor_free(vals); + return result; +} + /** Given a list of vote networkstatus_t in <b>votes</b>, our public * authority <b>identity_key</b>, our private authority <b>signing_key</b>, * and the number of <b>total_authorities</b> that we believe exist in our @@ -659,6 +753,15 @@ networkstatus_compute_consensus(smartlist_t *votes, tor_free(flaglist); } + if (consensus_method >= MIN_METHOD_FOR_PARAMS) { + char *params = dirvote_compute_params(votes); + if (params) { + smartlist_add(chunks, tor_strdup("params ")); + smartlist_add(chunks, params); + smartlist_add(chunks, tor_strdup("\n")); + } + } + /* Sort the votes. */ smartlist_sort(votes, _compare_votes_by_authority_id); /* Add the authority sections. */ diff --git a/src/or/eventdns.c b/src/or/eventdns.c index b413b6ae9..edccb4bfa 100644 --- a/src/or/eventdns.c +++ b/src/or/eventdns.c @@ -2332,7 +2332,7 @@ out1: /* exported function */ int -evdns_nameserver_add(unsigned long int address) { +evdns_nameserver_add(uint32_t address) { struct sockaddr_in sin; memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; @@ -2363,13 +2363,13 @@ evdns_nameserver_ip_add(const char *ip_as_string) { cp = strchr(ip_as_string, ':'); if (*ip_as_string == '[') { - int len; + size_t len; if (!(cp = strchr(ip_as_string, ']'))) { log(EVDNS_LOG_DEBUG, "Nameserver missing closing ]"); return 4; } len = cp-(ip_as_string + 1); - if (len > (int)sizeof(buf)-1) { + if (len > sizeof(buf)-1) { log(EVDNS_LOG_DEBUG, "[Nameserver] does not fit in buffer."); return 4; } diff --git a/src/or/eventdns.h b/src/or/eventdns.h index 734bacf2d..bf3b64d08 100644 --- a/src/or/eventdns.h +++ b/src/or/eventdns.h @@ -112,7 +112,7 @@ * * API reference: * - * int evdns_nameserver_add(unsigned long int address) + * int evdns_nameserver_add(uint32_t address) * Add a nameserver. The address should be an IP address in * network byte order. The type of address is chosen so that * it matches in_addr.s_addr. @@ -258,7 +258,7 @@ typedef void (*evdns_callback_type) (int result, char type, int count, int ttl, int evdns_init(void); void evdns_shutdown(int fail_requests); const char *evdns_err_to_string(int err); -int evdns_nameserver_add(unsigned long int address); +int evdns_nameserver_add(uint32_t address); int evdns_count_nameservers(void); int evdns_clear_nameservers_and_suspend(void); int evdns_resume(void); diff --git a/src/or/geoip.c b/src/or/geoip.c index befc3d9e0..00e608214 100644 --- a/src/or/geoip.c +++ b/src/or/geoip.c @@ -340,7 +340,7 @@ geoip_determine_shares(time_t now) ((double) (now - last_time_determined_shares)); v3_share_times_seconds += v3_share * ((double) (now - last_time_determined_shares)); - share_seconds += now - last_time_determined_shares; + share_seconds += (int)(now - last_time_determined_shares); } last_time_determined_shares = now; } @@ -768,7 +768,7 @@ geoip_get_dirreq_history(geoip_client_action_t action, time_diff = 1; /* Avoid DIV/0; "instant" answers are impossible * by law of nature or something, but a milisecond * is a bit greater than "instantly" */ - bytes_per_second = 1000 * ent->response_size / time_diff; + bytes_per_second = (uint32_t)(1000 * ent->response_size / time_diff); dltimes[ent_sl_idx] = bytes_per_second; } SMARTLIST_FOREACH_END(ent); median_uint32(dltimes, complete); /* sorts as a side effect. */ diff --git a/src/or/main.c b/src/or/main.c index 5701727ad..c33d1b39b 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -1240,17 +1240,26 @@ second_elapsed_callback(int fd, short event, void *args) TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT) { /* every 20 minutes, check and complain if necessary */ routerinfo_t *me = router_get_my_routerinfo(); - if (me && !check_whether_orport_reachable()) + if (me && !check_whether_orport_reachable()) { log_warn(LD_CONFIG,"Your server (%s:%d) has not managed to confirm that " "its ORPort is reachable. Please check your firewalls, ports, " "address, /etc/hosts file, etc.", me->address, me->or_port); - if (me && !check_whether_dirport_reachable()) + control_event_server_status(LOG_WARN, + "REACHABILITY_FAILED ORADDRESS=%s:%d", + me->address, me->or_port); + } + + if (me && !check_whether_dirport_reachable()) { log_warn(LD_CONFIG, "Your server (%s:%d) has not managed to confirm that its " "DirPort is reachable. Please check your firewalls, ports, " "address, /etc/hosts file, etc.", me->address, me->dir_port); + control_event_server_status(LOG_WARN, + "REACHABILITY_FAILED DIRADDRESS=%s:%d", + me->address, me->dir_port); + } } /** If more than this many seconds have elapsed, probably the clock @@ -1653,7 +1662,7 @@ dumpstats(int severity) { time_t now = time(NULL); time_t elapsed; - int rbuf_cap, wbuf_cap, rbuf_len, wbuf_len; + size_t rbuf_cap, wbuf_cap, rbuf_len, wbuf_len; log(severity, LD_GENERAL, "Dumping stats:"); @@ -1689,7 +1698,7 @@ dumpstats(int severity) log(severity, LD_GENERAL, "Conn %d: %d/%d bytes used on OpenSSL read buffer; " "%d/%d bytes used on write buffer.", - i, rbuf_len, rbuf_cap, wbuf_len, wbuf_cap); + i, (int)rbuf_len, (int)rbuf_cap, (int)wbuf_len, (int)wbuf_cap); } } } diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c index b02299960..fd38df4e6 100644 --- a/src/or/networkstatus.c +++ b/src/or/networkstatus.c @@ -286,6 +286,10 @@ networkstatus_vote_free(networkstatus_t *ns) SMARTLIST_FOREACH(ns->known_flags, char *, c, tor_free(c)); smartlist_free(ns->known_flags); } + if (ns->net_params) { + SMARTLIST_FOREACH(ns->net_params, char *, c, tor_free(c)); + smartlist_free(ns->net_params); + } if (ns->supported_methods) { SMARTLIST_FOREACH(ns->supported_methods, char *, c, tor_free(c)); smartlist_free(ns->supported_methods); @@ -1889,6 +1893,33 @@ networkstatus_dump_bridge_status_to_file(time_t now) tor_free(status); } +/** Return the value of a integer parameter from the networkstatus <b>ns</b> + * whose name is <b>param_name</b>. Return <b>default_val</b> if ns is NULL, + * or if it has no parameter called <b>param_name</b>. */ +int32_t +networkstatus_get_param(networkstatus_t *ns, const char *param_name, + int32_t default_val) +{ + size_t name_len; + + if (!ns || !ns->net_params) + return default_val; + + name_len = strlen(param_name); + + SMARTLIST_FOREACH_BEGIN(ns->net_params, const char *, p) { + if (!strcmpstart(p, param_name) && p[name_len] == '=') { + int ok=0; + long v = tor_parse_long(p+name_len+1, 10, INT32_MIN, + INT32_MAX, &ok, NULL); + if (ok) + return (int32_t) v; + } + } SMARTLIST_FOREACH_END(p); + + return default_val; +} + /** If <b>question</b> is a string beginning with "ns/" in a format the * control interface expects for a GETINFO question, set *<b>answer</b> to a * newly-allocated string containing networkstatus lines for the appropriate diff --git a/src/or/or.h b/src/or/or.h index aaae9053e..a7db06f71 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -1672,6 +1672,10 @@ typedef struct networkstatus_t { * not listed here, the voter has no opinion on what its value should be. */ smartlist_t *known_flags; + /** List of key=value strings for the parameters in this vote or + * consensus, sorted by key. */ + smartlist_t *net_params; + /** List of networkstatus_voter_info_t. For a vote, only one element * is included. For a consensus, one element is included for every voter * whose vote contributed to the consensus. */ @@ -1866,9 +1870,9 @@ typedef struct crypt_path_t { struct crypt_path_t *prev; /**< Link to previous crypt_path_t in the * circuit. */ - int package_window; /**< How many bytes are we allowed to originate ending + int package_window; /**< How many cells are we allowed to originate ending * at this step? */ - int deliver_window; /**< How many bytes are we willing to deliver originating + int deliver_window; /**< How many cells are we willing to deliver originating * at this step? */ } crypt_path_t; @@ -1973,6 +1977,7 @@ typedef struct circuit_t { time_t timestamp_created; /**< When was this circuit created? */ time_t timestamp_dirty; /**< When the circuit was first used, or 0 if the * circuit is clean. */ + struct timeval highres_created; /**< When exactly was the circuit created? */ uint16_t marked_for_close; /**< Should we close this circuit at the end of * the main loop? (If true, holds the line number @@ -2583,6 +2588,10 @@ typedef struct { /** Location of bandwidth measurement file */ char *V3BandwidthsFile; + /** Authority only: key=value pairs that we add to our networkstatus + * consensus vote on the 'params' line. */ + char *ConsensusParams; + /** The length of time that we think an initial consensus should be fresh. * Only altered on testing networks. */ int TestingV3AuthInitialVotingInterval; @@ -2675,6 +2684,10 @@ typedef struct { int BWHistoryWriteInterval; smartlist_t *BWHistoryWriteValues; + /** Build time histogram */ + config_line_t * BuildtimeHistogram; + uint16_t TotalBuildTimes; + /** What version of Tor wrote this state file? */ char *TorVersion; @@ -2844,6 +2857,155 @@ void bridges_retry_all(void); void entry_guards_free_all(void); +/* Circuit Build Timeout "public" functions and structures. */ + +/** Maximum quantile to use to generate synthetic timeouts. + * We want to stay a bit short of 1.0, because longtail is + * loooooooooooooooooooooooooooooooooooooooooooooooooooong. */ +#define MAX_SYNTHETIC_QUANTILE 0.985 + +/** Minimum circuits before estimating a timeout */ +#define MIN_CIRCUITS_TO_OBSERVE 500 + +/** Total size of the circuit timeout history to accumulate. + * 5000 is approx 1.5 weeks worth of continual-use circuits. */ +#define NCIRCUITS_TO_OBSERVE 5000 + +/** Width of the histogram bins in milliseconds */ +#define BUILDTIME_BIN_WIDTH ((build_time_t)50) + +/** Cutoff point on the CDF for our timeout estimation. + * TODO: This should be moved to the consensus */ +#define BUILDTIMEOUT_QUANTILE_CUTOFF 0.8 + +/** A build_time_t is milliseconds */ +typedef uint32_t build_time_t; +#define BUILD_TIME_MAX ((build_time_t)(INT32_MAX)) + +/** Lowest allowable value for CircuitBuildTimeout in milliseconds */ +#define BUILD_TIMEOUT_MIN_VALUE (3*1000) + +/** Initial circuit build timeout in milliseconds */ +#define BUILD_TIMEOUT_INITIAL_VALUE (60*1000) + +/** How often in seconds should we build a test circuit */ +#define BUILD_TIMES_TEST_FREQUENCY 60 + +/** Save state every 10 circuits */ +#define BUILD_TIMES_SAVE_STATE_EVERY 10 + +/* Circuit Build Timeout network liveness constants */ + +/** + * How many circuits count as recent when considering if the + * connection has gone gimpy or changed. + */ +#define RECENT_CIRCUITS 20 + +/** + * Have we received a cell in the last N circ attempts? + * + * This tells us when to temporarily switch back to + * BUILD_TIMEOUT_INITIAL_VALUE until we start getting cells, + * at which point we switch back to computing the timeout from + * our saved history. + */ +#define NETWORK_NONLIVE_TIMEOUT_COUNT (lround(RECENT_CIRCUITS*0.15)) + +/** + * This tells us when to toss out the last streak of N timeouts. + * + * If instead we start getting cells, we switch back to computing the timeout + * from our saved history. + */ +#define NETWORK_NONLIVE_DISCARD_COUNT (lround(NETWORK_NONLIVE_TIMEOUT_COUNT*2)) + +/** + * Maximum count of timeouts that finish the first hop in the past + * RECENT_CIRCUITS before calculating a new timeout. + * + * This tells us to abandon timeout history and set + * the timeout back to BUILD_TIMEOUT_INITIAL_VALUE. + */ +#define MAX_RECENT_TIMEOUT_COUNT (lround(RECENT_CIRCUITS*0.75)) + +/** Information about the state of our local network connection */ +typedef struct { + /** The timestamp we last completed a TLS handshake or received a cell */ + time_t network_last_live; + /** If the network is not live, how many timeouts has this caused? */ + int nonlive_timeouts; + /** If the network is not live, have we yet discarded our history? */ + int nonlive_discarded; + /** Circular array of circuits that have made it to the first hop. Slot is + * 1 if circuit timed out, 0 if circuit succeeded */ + int8_t timeouts_after_firsthop[RECENT_CIRCUITS]; + /** Index into circular array. */ + int after_firsthop_idx; +} network_liveness_t; + +/** Structure for circuit build times history */ +typedef struct { + /** The circular array of recorded build times in milliseconds */ + build_time_t circuit_build_times[NCIRCUITS_TO_OBSERVE]; + /** Current index in the circuit_build_times circular array */ + int build_times_idx; + /** Total number of build times accumulated. Maxes at NCIRCUITS_TO_OBSERVE */ + int total_build_times; + /** Information about the state of our local network connection */ + network_liveness_t liveness; + /** Last time we built a circuit. Used to decide to build new test circs */ + time_t last_circ_at; + /** Number of timeouts that have happened before estimating pareto + * parameters */ + int pre_timeouts; + /** "Minimum" value of our pareto distribution (actually mode) */ + build_time_t Xm; + /** alpha exponent for pareto dist. */ + double alpha; + /** Have we computed a timeout? */ + int have_computed_timeout; + /** The exact value for that timeout in milliseconds */ + double timeout_ms; +} circuit_build_times_t; + +extern circuit_build_times_t circ_times; +void circuit_build_times_update_state(circuit_build_times_t *cbt, + or_state_t *state); +int circuit_build_times_parse_state(circuit_build_times_t *cbt, + or_state_t *state, char **msg); +int circuit_build_times_add_timeout(circuit_build_times_t *cbt, + int did_onehop, time_t start_time); +void circuit_build_times_set_timeout(circuit_build_times_t *cbt); +int circuit_build_times_add_time(circuit_build_times_t *cbt, + build_time_t time); +int circuit_build_times_needs_circuits(circuit_build_times_t *cbt); +int circuit_build_times_needs_circuits_now(circuit_build_times_t *cbt); +void circuit_build_times_init(circuit_build_times_t *cbt); + +#ifdef CIRCUIT_PRIVATE +double circuit_build_times_calculate_timeout(circuit_build_times_t *cbt, + double quantile); +build_time_t circuit_build_times_generate_sample(circuit_build_times_t *cbt, + double q_lo, double q_hi); +void circuit_build_times_initial_alpha(circuit_build_times_t *cbt, + double quantile, double time_ms); +void circuit_build_times_update_alpha(circuit_build_times_t *cbt); +double circuit_build_times_cdf(circuit_build_times_t *cbt, double x); +void circuit_build_times_add_timeout_worker(circuit_build_times_t *cbt, + double quantile_cutoff); +void circuitbuild_running_unit_tests(void); +void circuit_build_times_reset(circuit_build_times_t *cbt); + +/* Network liveness functions */ +int circuit_build_times_network_check_changed(circuit_build_times_t *cbt); +#endif + +/* Network liveness functions */ +void circuit_build_times_network_is_live(circuit_build_times_t *cbt); +int circuit_build_times_network_check_live(circuit_build_times_t *cbt); +void circuit_build_times_network_circ_success(circuit_build_times_t *cbt); + /********************************* circuitlist.c ***********************/ circuit_t * _circuit_get_global_list(void); @@ -2856,6 +3018,7 @@ void circuit_set_n_circid_orconn(circuit_t *circ, circid_t id, or_connection_t *conn); void circuit_set_state(circuit_t *circ, uint8_t state); void circuit_close_all_marked(void); +int32_t circuit_initial_package_window(void); origin_circuit_t *origin_circuit_new(void); or_circuit_t *or_circuit_new(circid_t p_circ_id, or_connection_t *p_conn); circuit_t *circuit_get_by_circid_orconn(circid_t circ_id, @@ -3661,9 +3824,9 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_env_t *private_key, authority_cert_t *cert); #ifdef DIRVOTE_PRIVATE -char * -format_networkstatus_vote(crypto_pk_env_t *private_key, - networkstatus_t *v3_ns); +char *format_networkstatus_vote(crypto_pk_env_t *private_key, + networkstatus_t *v3_ns); +char *dirvote_compute_params(smartlist_t *votes); #endif /********************************* dns.c ***************************/ @@ -3956,6 +4119,8 @@ void signed_descs_update_status_from_consensus_networkstatus( char *networkstatus_getinfo_helper_single(routerstatus_t *rs); char *networkstatus_getinfo_by_purpose(const char *purpose_string, time_t now); void networkstatus_dump_bridge_status_to_file(time_t now); +int32_t networkstatus_get_param(networkstatus_t *ns, const char *param_name, + int32_t default_val); int getinfo_helper_networkstatus(control_connection_t *conn, const char *question, char **answer); void networkstatus_free_all(void); diff --git a/src/or/relay.c b/src/or/relay.c index c81b8311a..653aa594c 100644 --- a/src/or/relay.c +++ b/src/or/relay.c @@ -1635,7 +1635,8 @@ cell_queue_append_packed_copy(cell_queue_t *queue, const cell_t *cell) it_pool = mp_pool_new(sizeof(insertion_time_elem_t), 1024); tor_gettimeofday(&now); #define SECONDS_IN_A_DAY 86400L - added = (now.tv_sec % SECONDS_IN_A_DAY) * 100L + now.tv_usec / 10000L; + added = (uint32_t)(((now.tv_sec % SECONDS_IN_A_DAY) * 100L) + + ((uint32_t)now.tv_usec / (uint32_t)10000L)); if (!it_queue) { it_queue = tor_malloc_zero(sizeof(insertion_time_queue_t)); queue->insertion_times = it_queue; @@ -1879,15 +1880,17 @@ connection_or_flush_from_first_active_circuit(or_connection_t *conn, int max, uint32_t cell_waiting_time; insertion_time_queue_t *it_queue = queue->insertion_times; tor_gettimeofday(&now); - flushed = (now.tv_sec % SECONDS_IN_A_DAY) * 100L + - now.tv_usec / 10000L; + flushed = (uint32_t)((now.tv_sec % SECONDS_IN_A_DAY) * 100L + + (uint32_t)now.tv_usec / (uint32_t)10000L); if (!it_queue || !it_queue->first) { log_warn(LD_BUG, "Cannot determine insertion time of cell."); } else { or_circuit_t *orcirc = TO_OR_CIRCUIT(circ); insertion_time_elem_t *elem = it_queue->first; - cell_waiting_time = (flushed * 10L + SECONDS_IN_A_DAY * 1000L - - elem->insertion_time * 10L) % (SECONDS_IN_A_DAY * 1000L); + cell_waiting_time = + (uint32_t)((flushed * 10L + SECONDS_IN_A_DAY * 1000L - + elem->insertion_time * 10L) % + (SECONDS_IN_A_DAY * 1000L)); #undef SECONDS_IN_A_DAY elem->counter--; if (elem->counter < 1) { diff --git a/src/or/rendclient.c b/src/or/rendclient.c index f65c20f2a..0ade46807 100644 --- a/src/or/rendclient.c +++ b/src/or/rendclient.c @@ -91,8 +91,9 @@ rend_client_send_introduction(origin_circuit_t *introcirc, } }); if (!intro_key) { - log_warn(LD_BUG, "Internal error: could not find intro key; we " - "only have a v2 rend desc with %d intro points.", + log_info(LD_REND, "Our introduction point knowledge changed in " + "mid-connect! Could not find intro key; we only have a " + "v2 rend desc with %d intro points. Giving up.", smartlist_len(entry->parsed->intro_nodes)); goto err; } @@ -128,7 +129,7 @@ rend_client_send_introduction(origin_circuit_t *introcirc, REND_DESC_COOKIE_LEN); v3_shift += 2+REND_DESC_COOKIE_LEN; } - set_uint32(tmp+v3_shift+1, htonl(time(NULL))); + set_uint32(tmp+v3_shift+1, htonl((uint32_t)time(NULL))); v3_shift += 4; } /* if version 2 only write version number */ else if (entry->parsed->protocols & (1<<2)) { @@ -644,7 +645,7 @@ rend_client_receive_rendezvous(origin_circuit_t *circ, const char *request, /* set the windows to default. these are the windows * that alice thinks bob has. */ - hop->package_window = CIRCWINDOW_START; + hop->package_window = circuit_initial_package_window(); hop->deliver_window = CIRCWINDOW_START; onion_append_to_cpath(&circ->cpath, hop); diff --git a/src/or/rendservice.c b/src/or/rendservice.c index e8330094f..daf8247e5 100644 --- a/src/or/rendservice.c +++ b/src/or/rendservice.c @@ -264,7 +264,7 @@ rend_config_services(or_options_t *options, int validate_only) for (line = options->RendConfigLines; line; line = line->next) { if (!strcasecmp(line->key, "HiddenServiceDir")) { - if (service) { + if (service) { /* register the one we just finished parsing */ if (validate_only) rend_service_free(service); else @@ -921,7 +921,7 @@ rend_service_introduce(origin_circuit_t *circuit, const char *request, len = r; if (*buf == 3) { /* Version 3 INTRODUCE2 cell. */ - time_t ts = 0, now = time(NULL); + time_t ts = 0; v3_shift = 1; auth_type = buf[1]; switch (auth_type) { @@ -944,13 +944,12 @@ rend_service_introduce(origin_circuit_t *circuit, const char *request, } /* Check timestamp. */ - memcpy((char*)&ts, buf+1+v3_shift, sizeof(uint32_t)); + ts = ntohl(get_uint32(buf+1+v3_shift)); v3_shift += 4; - ts = ntohl(ts); if ((now - ts) < -1 * REND_REPLAY_TIME_INTERVAL / 2 || (now - ts) > REND_REPLAY_TIME_INTERVAL / 2) { log_warn(LD_REND, "INTRODUCE2 cell is too %s. Discarding.", - (now - ts) < 0 ? "old" : "new"); + (now - ts) < 0 ? "old" : "new"); return -1; } } @@ -1101,7 +1100,7 @@ rend_service_introduce(origin_circuit_t *circuit, const char *request, circ_needs_uptime = rend_service_requires_uptime(service); /* help predict this next time */ - rep_hist_note_used_internal(time(NULL), circ_needs_uptime, 1); + rep_hist_note_used_internal(now, circ_needs_uptime, 1); /* Launch a circuit to alice's chosen rendezvous point. */ @@ -1137,7 +1136,7 @@ rend_service_introduce(origin_circuit_t *circuit, const char *request, launched->build_state->pending_final_cpath = cpath = tor_malloc_zero(sizeof(crypt_path_t)); cpath->magic = CRYPT_PATH_MAGIC; - launched->build_state->expiry_time = time(NULL) + MAX_REND_TIMEOUT; + launched->build_state->expiry_time = now + MAX_REND_TIMEOUT; cpath->dh_handshake_state = dh; dh = NULL; @@ -1477,7 +1476,7 @@ rend_service_rendezvous_has_opened(origin_circuit_t *circuit) /* set the windows to default. these are the windows * that bob thinks alice has. */ - hop->package_window = CIRCWINDOW_START; + hop->package_window = circuit_initial_package_window(); hop->deliver_window = CIRCWINDOW_START; onion_append_to_cpath(&circuit->cpath, hop); diff --git a/src/or/router.c b/src/or/router.c index ea7a430fa..085a597ea 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -772,9 +772,6 @@ consider_testing_reachability(int test_or, int test_dir) me->address, me->or_port); circuit_launch_by_router(CIRCUIT_PURPOSE_TESTING, me, CIRCLAUNCH_NEED_CAPACITY|CIRCLAUNCH_IS_INTERNAL); - control_event_server_status(LOG_NOTICE, - "CHECKING_REACHABILITY ORADDRESS=%s:%d", - me->address, me->or_port); } tor_addr_from_ipv4h(&addr, me->addr); @@ -790,10 +787,6 @@ consider_testing_reachability(int test_or, int test_dir) DIR_PURPOSE_FETCH_SERVERDESC, ROUTER_PURPOSE_GENERAL, 1, "authority.z", NULL, 0, 0); - - control_event_server_status(LOG_NOTICE, - "CHECKING_REACHABILITY DIRADDRESS=%s:%d", - me->address, me->dir_port); } } @@ -809,8 +802,11 @@ router_orport_found_reachable(void) " Publishing server descriptor." : ""); can_reach_or_port = 1; mark_my_descriptor_dirty(); - if (!me) + if (!me) { /* should never happen */ + log_warn(LD_BUG, "ORPort found reachable, but I have no routerinfo " + "yet. Failing to inform controller of success."); return; + } control_event_server_status(LOG_NOTICE, "REACHABILITY_SUCCEEDED ORADDRESS=%s:%d", me->address, me->or_port); @@ -828,8 +824,11 @@ router_dirport_found_reachable(void) can_reach_dir_port = 1; if (!me || decide_to_advertise_dirport(get_options(), me->dir_port)) mark_my_descriptor_dirty(); - if (!me) + if (!me) { /* should never happen */ + log_warn(LD_BUG, "DirPort found reachable, but I have no routerinfo " + "yet. Failing to inform controller of success."); return; + } control_event_server_status(LOG_NOTICE, "REACHABILITY_SUCCEEDED DIRADDRESS=%s:%d", me->address, me->dir_port); @@ -1909,7 +1908,7 @@ extrainfo_dump_to_string(char *s, size_t maxlen, extrainfo_t *extrainfo, if (options->DirReqStatistics && load_stats_file("stats"PATH_SEPARATOR"dirreq-stats", "dirreq-stats-end", since, &contents) > 0) { - int pos = strlen(s); + size_t pos = strlen(s); if (strlcpy(s + pos, contents, maxlen - strlen(s)) != strlen(contents)) { log_warn(LD_DIR, "Could not write dirreq-stats to extra-info " @@ -1921,7 +1920,7 @@ extrainfo_dump_to_string(char *s, size_t maxlen, extrainfo_t *extrainfo, if (options->EntryStatistics && load_stats_file("stats"PATH_SEPARATOR"entry-stats", "entry-stats-end", since, &contents) > 0) { - int pos = strlen(s); + size_t pos = strlen(s); if (strlcpy(s + pos, contents, maxlen - strlen(s)) != strlen(contents)) { log_warn(LD_DIR, "Could not write entry-stats to extra-info " @@ -1933,7 +1932,7 @@ extrainfo_dump_to_string(char *s, size_t maxlen, extrainfo_t *extrainfo, if (options->CellStatistics && load_stats_file("stats"PATH_SEPARATOR"buffer-stats", "cell-stats-end", since, &contents) > 0) { - int pos = strlen(s); + size_t pos = strlen(s); if (strlcpy(s + pos, contents, maxlen - strlen(s)) != strlen(contents)) { log_warn(LD_DIR, "Could not write buffer-stats to extra-info " @@ -1945,7 +1944,7 @@ extrainfo_dump_to_string(char *s, size_t maxlen, extrainfo_t *extrainfo, if (options->ExitPortStatistics && load_stats_file("stats"PATH_SEPARATOR"exit-stats", "exit-stats-end", since, &contents) > 0) { - int pos = strlen(s); + size_t pos = strlen(s); if (strlcpy(s + pos, contents, maxlen - strlen(s)) != strlen(contents)) { log_warn(LD_DIR, "Could not write exit-stats to extra-info " diff --git a/src/or/routerparse.c b/src/or/routerparse.c index 9e5f37b4b..e35ece06d 100644 --- a/src/or/routerparse.c +++ b/src/or/routerparse.c @@ -102,6 +102,7 @@ typedef enum { K_VOTING_DELAY, K_KNOWN_FLAGS, + K_PARAMS, K_VOTE_DIGEST, K_CONSENSUS_DIGEST, K_CONSENSUS_METHODS, @@ -433,6 +434,7 @@ static token_rule_t networkstatus_token_table[] = { T1("valid-until", K_VALID_UNTIL, CONCAT_ARGS, NO_OBJ ), T1("voting-delay", K_VOTING_DELAY, GE(2), NO_OBJ ), T1("known-flags", K_KNOWN_FLAGS, ARGS, NO_OBJ ), + T01("params", K_PARAMS, ARGS, NO_OBJ ), T( "fingerprint", K_FINGERPRINT, CONCAT_ARGS, NO_OBJ ), CERTIFICATE_MEMBERS @@ -470,6 +472,7 @@ static token_rule_t networkstatus_consensus_token_table[] = { T01("client-versions", K_CLIENT_VERSIONS, CONCAT_ARGS, NO_OBJ ), T01("server-versions", K_SERVER_VERSIONS, CONCAT_ARGS, NO_OBJ ), T01("consensus-method", K_CONSENSUS_METHOD, EQ(1), NO_OBJ), + T01("params", K_PARAMS, ARGS, NO_OBJ ), END_OF_TABLE }; @@ -2002,8 +2005,9 @@ routerstatus_parse_entry_from_string(memarea_t *area, for (i=0; i < tok->n_args; ++i) { if (!strcmpstart(tok->args[i], "Bandwidth=")) { int ok; - rs->bandwidth = tor_parse_ulong(strchr(tok->args[i], '=')+1, 10, - 0, UINT32_MAX, &ok, NULL); + rs->bandwidth = (uint32_t)tor_parse_ulong(strchr(tok->args[i], '=')+1, + 10, 0, UINT32_MAX, + &ok, NULL); if (!ok) { log_warn(LD_DIR, "Invalid Bandwidth %s", escaped(tok->args[i])); goto err; @@ -2011,8 +2015,9 @@ routerstatus_parse_entry_from_string(memarea_t *area, rs->has_bandwidth = 1; } else if (!strcmpstart(tok->args[i], "Measured=")) { int ok; - rs->measured_bw = tor_parse_ulong(strchr(tok->args[i], '=')+1, 10, - 0, UINT32_MAX, &ok, NULL); + rs->measured_bw = + (uint32_t)tor_parse_ulong(strchr(tok->args[i], '=')+1, + 10, 0, UINT32_MAX, &ok, NULL); if (!ok) { log_warn(LD_DIR, "Invalid Measured Bandwidth %s", escaped(tok->args[i])); @@ -2406,6 +2411,34 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out, goto err; } + tok = find_opt_by_keyword(tokens, K_PARAMS); + if (tok) { + inorder = 1; + ns->net_params = smartlist_create(); + for (i = 0; i < tok->n_args; ++i) { + int ok=0; + char *eq = strchr(tok->args[i], '='); + if (!eq) { + log_warn(LD_DIR, "Bad element '%s' in params", escaped(tok->args[i])); + goto err; + } + tor_parse_long(eq+1, 10, INT32_MIN, INT32_MAX, &ok, NULL); + if (!ok) { + log_warn(LD_DIR, "Bad element '%s' in params", escaped(tok->args[i])); + goto err; + } + if (i > 0 && strcmp(tok->args[i-1], tok->args[i]) >= 0) { + log_warn(LD_DIR, "%s >= %s", tok->args[i-1], tok->args[i]); + inorder = 0; + } + smartlist_add(ns->net_params, tor_strdup(tok->args[i])); + } + if (!inorder) { + log_warn(LD_DIR, "params not in order"); + goto err; + } + } + ns->voters = smartlist_create(); SMARTLIST_FOREACH_BEGIN(tokens, directory_token_t *, _tok) { @@ -2605,6 +2638,14 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out, } else { if (tok->object_size >= INT_MAX) goto err; + /* We already parsed a vote from this voter. Use the first one. */ + if (v->signature) { + log_fn(LOG_PROTOCOL_WARN, LD_DIR, "We received a networkstatus " + "that contains two votes from the same voter. Ignoring " + "the second vote."); + continue; + } + v->signature = tor_memdup(tok->object_body, tok->object_size); v->signature_len = (int) tok->object_size; } @@ -2614,6 +2655,10 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out, if (! n_signatures) { log_warn(LD_DIR, "No signatures on networkstatus vote."); goto err; + } else if (ns->type == NS_TYPE_VOTE && n_signatures != 1) { + log_warn(LD_DIR, "Received more than one signature on a " + "network-status vote."); + goto err; } if (eos_out) @@ -3516,9 +3561,11 @@ tor_version_parse(const char *s, tor_version_t *out) if (! close_paren) return -1; cp += 5; - hexlen = (close_paren-cp); + if (close_paren-cp > HEX_DIGEST_LEN) + return -1; + hexlen = (int)(close_paren-cp); memset(digest, 0, sizeof(digest)); - if (hexlen > HEX_DIGEST_LEN || hexlen == 0 || (hexlen % 2) == 1) + if ( hexlen == 0 || (hexlen % 2) == 1) return -1; if (base16_decode(digest, hexlen/2, cp, hexlen)) return -1; diff --git a/src/or/test.c b/src/or/test.c index d4afdeeb3..9bb54fadb 100644 --- a/src/or/test.c +++ b/src/or/test.c @@ -37,6 +37,15 @@ const char tor_git_revision[] = ""; #define GEOIP_PRIVATE #define MEMPOOL_PRIVATE #define ROUTER_PRIVATE +#define CIRCUIT_PRIVATE + +/* + * Linux doesn't provide lround in math.h by default, but mac os does... + * It's best just to leave math.h out of the picture entirely. + */ +//#include <math.h> +long int lround(double x); +double fabs(double x); #include "or.h" #include "test.h" @@ -410,7 +419,7 @@ test_crypto_dh(void) char p2[DH_BYTES]; char s1[DH_BYTES]; char s2[DH_BYTES]; - int s1len, s2len; + ssize_t s1len, s2len; test_eq(crypto_dh_get_bytes(dh1), DH_BYTES); test_eq(crypto_dh_get_bytes(dh2), DH_BYTES); @@ -1114,6 +1123,24 @@ test_util(void) tor_parse_uint64("12345678901",10,500,INT32_MAX, &i, &cp)); test_assert(i == 0); + { + /* Test tor_parse_double. */ + double d = tor_parse_double("10", 0, UINT64_MAX,&i,NULL); + test_assert(i == 1); + test_assert(DBL_TO_U64(d) == 10); + d = tor_parse_double("0", 0, UINT64_MAX,&i,NULL); + test_assert(i == 1); + test_assert(DBL_TO_U64(d) == 0); + d = tor_parse_double(" ", 0, UINT64_MAX,&i,NULL); + test_assert(i == 0); + d = tor_parse_double(".0a", 0, UINT64_MAX,&i,NULL); + test_assert(i == 0); + d = tor_parse_double(".0a", 0, UINT64_MAX,&i,&cp); + test_assert(i == 1); + d = tor_parse_double("-.0", 0, UINT64_MAX,&i,NULL); + test_assert(i == 1); + } + /* Test failing snprintf cases */ test_eq(-1, tor_snprintf(buf, 0, "Foo")); test_eq(-1, tor_snprintf(buf, 2, "Foo")); @@ -3337,6 +3364,220 @@ done: return; } +static void +test_dirutil_param_voting(void) +{ + networkstatus_t vote1, vote2, vote3, vote4; + smartlist_t *votes = smartlist_create(); + char *res = NULL; + + /* dirvote_compute_params only looks at the net_params field of the votes, + so that's all we need to set. + */ + memset(&vote1, 0, sizeof(vote1)); + memset(&vote2, 0, sizeof(vote2)); + memset(&vote3, 0, sizeof(vote3)); + memset(&vote4, 0, sizeof(vote4)); + vote1.net_params = smartlist_create(); + vote2.net_params = smartlist_create(); + vote3.net_params = smartlist_create(); + vote4.net_params = smartlist_create(); + smartlist_split_string(vote1.net_params, + "ab=90 abcd=20 cw=50 x-yz=-99", NULL, 0, 0); + smartlist_split_string(vote2.net_params, + "ab=27 cw=5 x-yz=88", NULL, 0, 0); + smartlist_split_string(vote3.net_params, + "abcd=20 c=60 cw=500 x-yz=-9 zzzzz=101", NULL, 0, 0); + smartlist_split_string(vote4.net_params, + "ab=900 abcd=200 c=1 cw=51 x-yz=100", NULL, 0, 0); + test_eq(100, networkstatus_get_param(&vote4, "x-yz", 50)); + test_eq(222, networkstatus_get_param(&vote4, "foobar", 222)); + + smartlist_add(votes, &vote1); + smartlist_add(votes, &vote2); + smartlist_add(votes, &vote3); + smartlist_add(votes, &vote4); + + res = dirvote_compute_params(votes); + test_streq(res, + "ab=90 abcd=20 c=1 cw=50 x-yz=-9 zzzzz=101"); + + done: + tor_free(res); + SMARTLIST_FOREACH(vote1.net_params, char *, cp, tor_free(cp)); + SMARTLIST_FOREACH(vote2.net_params, char *, cp, tor_free(cp)); + SMARTLIST_FOREACH(vote3.net_params, char *, cp, tor_free(cp)); + SMARTLIST_FOREACH(vote4.net_params, char *, cp, tor_free(cp)); + smartlist_free(vote1.net_params); + smartlist_free(vote2.net_params); + smartlist_free(vote3.net_params); + smartlist_free(vote4.net_params); + + return; +} + +static void +test_circuit_timeout(void) +{ + /* Plan: + * 1. Generate 1000 samples + * 2. Estimate parameters + * 3. If difference, repeat + * 4. Save state + * 5. load state + * 6. Estimate parameters + * 7. compare differences + */ + circuit_build_times_t initial; + circuit_build_times_t estimate; + circuit_build_times_t final; + double timeout1, timeout2; + or_state_t state; + char *msg; + int i, runs; + circuit_build_times_init(&initial); + circuit_build_times_init(&estimate); + circuit_build_times_init(&final); + + memset(&state, 0, sizeof(or_state_t)); + + circuitbuild_running_unit_tests(); +#define timeout0 (build_time_t)(30*1000.0) + initial.Xm = 750; + circuit_build_times_initial_alpha(&initial, BUILDTIMEOUT_QUANTILE_CUTOFF, + timeout0); + do { + int n = 0; + for (i=0; i < MIN_CIRCUITS_TO_OBSERVE; i++) { + if (circuit_build_times_add_time(&estimate, + circuit_build_times_generate_sample(&initial, 0, 1)) == 0) { + n++; + } + } + circuit_build_times_update_alpha(&estimate); + timeout1 = circuit_build_times_calculate_timeout(&estimate, + BUILDTIMEOUT_QUANTILE_CUTOFF); + circuit_build_times_set_timeout(&estimate); + log_warn(LD_CIRC, "Timeout is %lf, Xm is %d", timeout1, estimate.Xm); + /* XXX: 5% distribution error may not be the right metric */ + } while (fabs(circuit_build_times_cdf(&initial, timeout0) - + circuit_build_times_cdf(&initial, timeout1)) > 0.05 + /* 5% error */ + && estimate.total_build_times < NCIRCUITS_TO_OBSERVE); + + test_assert(estimate.total_build_times < NCIRCUITS_TO_OBSERVE); + + circuit_build_times_update_state(&estimate, &state); + test_assert(circuit_build_times_parse_state(&final, &state, &msg) == 0); + + circuit_build_times_update_alpha(&final); + timeout2 = circuit_build_times_calculate_timeout(&final, + BUILDTIMEOUT_QUANTILE_CUTOFF); + + circuit_build_times_set_timeout(&final); + log_warn(LD_CIRC, "Timeout is %lf, Xm is %d", timeout2, final.Xm); + + test_assert(fabs(circuit_build_times_cdf(&initial, timeout0) - + circuit_build_times_cdf(&initial, timeout2)) < 0.05); + + for (runs = 0; runs < 50; runs++) { + int build_times_idx = 0; + int total_build_times = 0; + + final.timeout_ms = BUILD_TIMEOUT_INITIAL_VALUE; + estimate.timeout_ms = BUILD_TIMEOUT_INITIAL_VALUE; + + for (i = 0; i < RECENT_CIRCUITS*2; i++) { + circuit_build_times_network_circ_success(&estimate); + circuit_build_times_add_time(&estimate, + circuit_build_times_generate_sample(&estimate, 0, + BUILDTIMEOUT_QUANTILE_CUTOFF)); + estimate.have_computed_timeout = 1; + circuit_build_times_network_circ_success(&estimate); + circuit_build_times_add_time(&final, + circuit_build_times_generate_sample(&final, 0, + BUILDTIMEOUT_QUANTILE_CUTOFF)); + final.have_computed_timeout = 1; + } + + test_assert(!circuit_build_times_network_check_changed(&estimate)); + test_assert(!circuit_build_times_network_check_changed(&final)); + + /* Reset liveness to be non-live */ + final.liveness.network_last_live = 0; + estimate.liveness.network_last_live = 0; + + build_times_idx = estimate.build_times_idx; + total_build_times = estimate.total_build_times; + for (i = 0; i < NETWORK_NONLIVE_TIMEOUT_COUNT; i++) { + test_assert(circuit_build_times_network_check_live(&estimate)); + test_assert(circuit_build_times_network_check_live(&final)); + + if (circuit_build_times_add_timeout(&estimate, 0, + (time_t)(approx_time()-estimate.timeout_ms/1000.0-1))) + estimate.have_computed_timeout = 1; + if (circuit_build_times_add_timeout(&final, 0, + (time_t)(approx_time()-final.timeout_ms/1000.0-1))) + final.have_computed_timeout = 1; + } + + test_assert(!circuit_build_times_network_check_live(&estimate)); + test_assert(!circuit_build_times_network_check_live(&final)); + + for ( ; i < NETWORK_NONLIVE_DISCARD_COUNT; i++) { + if (circuit_build_times_add_timeout(&estimate, 0, + (time_t)(approx_time()-estimate.timeout_ms/1000.0-1))) + estimate.have_computed_timeout = 1; + + if (i < NETWORK_NONLIVE_DISCARD_COUNT-1) { + if (circuit_build_times_add_timeout(&final, 0, + (time_t)(approx_time()-final.timeout_ms/1000.0-1))) + final.have_computed_timeout = 1; + } + } + + test_assert(!circuit_build_times_network_check_live(&estimate)); + test_assert(!circuit_build_times_network_check_live(&final)); + + log_info(LD_CIRC, "idx: %d %d, tot: %d %d", + build_times_idx, estimate.build_times_idx, + total_build_times, estimate.total_build_times); + + /* Check rollback index. Should match top of loop. */ + test_assert(build_times_idx == estimate.build_times_idx); + test_assert(total_build_times == estimate.total_build_times); + + /* Now simulate that the network has become live and we need + * a change */ + circuit_build_times_network_is_live(&estimate); + circuit_build_times_network_is_live(&final); + + for (i = 0; i < MAX_RECENT_TIMEOUT_COUNT; i++) { + if (circuit_build_times_add_timeout(&estimate, 1, approx_time()-1)) + estimate.have_computed_timeout = 1; + + if (i < MAX_RECENT_TIMEOUT_COUNT-1) { + if (circuit_build_times_add_timeout(&final, 1, approx_time()-1)) + final.have_computed_timeout = 1; + } + } + + test_assert(estimate.liveness.after_firsthop_idx == 0); + test_assert(final.liveness.after_firsthop_idx == + MAX_RECENT_TIMEOUT_COUNT-1); + + test_assert(circuit_build_times_network_check_live(&estimate)); + test_assert(circuit_build_times_network_check_live(&final)); + + if (circuit_build_times_add_timeout(&final, 1, approx_time()-1)) + final.have_computed_timeout = 1; + + } + +done: + return; +} + extern const char AUTHORITY_CERT_1[]; extern const char AUTHORITY_SIGNKEY_1[]; extern const char AUTHORITY_CERT_2[]; @@ -3494,6 +3735,9 @@ test_v3_networkstatus(void) crypto_pk_get_digest(cert1->identity_key, voter->identity_digest); smartlist_add(vote->voters, voter); vote->cert = authority_cert_dup(cert1); + vote->net_params = smartlist_create(); + smartlist_split_string(vote->net_params, "circuitwindow=101 foo=990", + NULL, 0, 0); vote->routerstatus_list = smartlist_create(); /* add the first routerstatus. */ vrs = tor_malloc_zero(sizeof(vote_routerstatus_t)); @@ -3635,6 +3879,9 @@ test_v3_networkstatus(void) vote->dist_seconds = 300; authority_cert_free(vote->cert); vote->cert = authority_cert_dup(cert2); + vote->net_params = smartlist_create(); + smartlist_split_string(vote->net_params, "bar=2000000000 circuitwindow=20", + NULL, 0, 0); tor_free(vote->client_versions); tor_free(vote->server_versions); voter = smartlist_get(vote->voters, 0); @@ -3673,6 +3920,9 @@ test_v3_networkstatus(void) vote->dist_seconds = 250; authority_cert_free(vote->cert); vote->cert = authority_cert_dup(cert3); + vote->net_params = smartlist_create(); + smartlist_split_string(vote->net_params, "circuitwindow=80 foo=660", + NULL, 0, 0); smartlist_add(vote->supported_methods, tor_strdup("4")); vote->client_versions = tor_strdup("0.1.2.14,0.1.2.17"); vote->server_versions = tor_strdup("0.1.2.10,0.1.2.15,0.1.2.16"); @@ -3729,6 +3979,10 @@ test_v3_networkstatus(void) test_streq(cp, "Authority:Exit:Fast:Guard:MadeOfCheese:MadeOfTin:" "Running:Stable:V2Dir:Valid"); tor_free(cp); + cp = smartlist_join_strings(con->net_params, ":", 0, NULL); + test_streq(cp, "bar=2000000000:circuitwindow=80:foo=660"); + tor_free(cp); + test_eq(4, smartlist_len(con->voters)); /*3 voters, 1 legacy key.*/ /* The voter id digests should be in this order. */ test_assert(memcmp(cert2->cache_info.identity_digest, @@ -4848,6 +5102,8 @@ static struct { ENT(dir_format), ENT(dirutil), SUBENT(dirutil, measured_bw), + SUBENT(dirutil, param_voting), + ENT(circuit_timeout), ENT(v3_networkstatus), ENT(policies), ENT(rend_fns), |