void test_circuitstats_hoplen(void *arg) { /* Plan: * 0. Test no other opened circs (relaxed timeout) * 1. Check >3 hop circ building w/o timeout * 2. Check >3 hop circs w/ timeouts.. */ struct timeval circ_start_time; origin_circuit_t *threehop = NULL; origin_circuit_t *fourhop = NULL; (void)arg; MOCK(circuit_mark_for_close_, mock_circuit_mark_for_close); circuit_build_times_init(get_circuit_build_times_mutable()); // Let's set a close_ms to 2X the initial timeout, so we can // test relaxed functionality (which uses the close_ms timeout) get_circuit_build_times_mutable()->close_ms *= 2; tor_gettimeofday(&circ_start_time); circ_start_time.tv_sec -= 119; // make us hit "relaxed" cutoff // Test 1: Build a fourhop circuit that should get marked // as relaxed and eventually counted by circuit_expire_building // (but not before) fourhop = subtest_fourhop_circuit(circ_start_time, 0); tt_int_op(fourhop->relaxed_timeout, OP_EQ, 0); tt_int_op(marked_for_close, OP_EQ, 0); circuit_expire_building(); tt_int_op(marked_for_close, OP_EQ, 0); tt_int_op(fourhop->relaxed_timeout, OP_EQ, 1); TO_CIRCUIT(fourhop)->timestamp_began.tv_sec -= 119; circuit_expire_building(); tt_int_op(get_circuit_build_times()->total_build_times, OP_EQ, 1); tt_int_op(marked_for_close, OP_EQ, 1); circuit_free_(TO_CIRCUIT(fourhop)); circuit_build_times_reset(get_circuit_build_times_mutable()); // Test 2: Add a threehop circuit for non-relaxed timeouts threehop = add_opened_threehop(); /* This circuit should not timeout */ tor_gettimeofday(&circ_start_time); circ_start_time.tv_sec -= 59; fourhop = subtest_fourhop_circuit(circ_start_time, 0); circuit_expire_building(); tt_int_op(get_circuit_build_times()->total_build_times, OP_EQ, 1); tt_int_op(TO_CIRCUIT(fourhop)->purpose, OP_NE, CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT); circuit_free_((circuit_t *)fourhop); circuit_build_times_reset(get_circuit_build_times_mutable()); /* Test 3: This circuit should now time out and get marked as a * measurement circuit, but still get counted (and counted only once) */ circ_start_time.tv_sec -= 2; fourhop = subtest_fourhop_circuit(circ_start_time, 0); tt_int_op(TO_CIRCUIT(fourhop)->purpose, OP_EQ, CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT); tt_int_op(get_circuit_build_times()->total_build_times, OP_EQ, 1); circuit_expire_building(); tt_int_op(get_circuit_build_times()->total_build_times, OP_EQ, 1); done: UNMOCK(circuit_mark_for_close_); circuit_free_(TO_CIRCUIT(threehop)); circuit_free_(TO_CIRCUIT(fourhop)); circuit_build_times_free_timeouts(get_circuit_build_times_mutable()); }
/** * This function is called when we get a consensus update. * * It checks to see if we have changed any consensus parameters * that require reallocation or discard of previous stats. */ void circuit_build_times_new_consensus_params(circuit_build_times_t *cbt, networkstatus_t *ns) { int32_t num; /* * First check if we're doing adaptive timeouts at all; nothing to * update if we aren't. */ if (!circuit_build_times_disabled()) { num = circuit_build_times_recent_circuit_count(ns); if (num > 0) { if (num != cbt->liveness.num_recent_circs) { int8_t *recent_circs; log_notice(LD_CIRC, "The Tor Directory Consensus has changed how many " "circuits we must track to detect network failures from %d " "to %d.", cbt->liveness.num_recent_circs, num); tor_assert(cbt->liveness.timeouts_after_firsthop || cbt->liveness.num_recent_circs == 0); /* * Technically this is a circular array that we are reallocating * and memcopying. However, since it only consists of either 1s * or 0s, and is only used in a statistical test to determine when * we should discard our history after a sufficient number of 1's * have been reached, it is fine if order is not preserved or * elements are lost. * * cbtrecentcount should only be changing in cases of severe network * distress anyway, so memory correctness here is paramount over * doing acrobatics to preserve the array. */ recent_circs = tor_malloc_zero(sizeof(int8_t)*num); if (cbt->liveness.timeouts_after_firsthop && cbt->liveness.num_recent_circs > 0) { memcpy(recent_circs, cbt->liveness.timeouts_after_firsthop, sizeof(int8_t)*MIN(num, cbt->liveness.num_recent_circs)); } // Adjust the index if it needs it. if (num < cbt->liveness.num_recent_circs) { cbt->liveness.after_firsthop_idx = MIN(num-1, cbt->liveness.after_firsthop_idx); } tor_free(cbt->liveness.timeouts_after_firsthop); cbt->liveness.timeouts_after_firsthop = recent_circs; cbt->liveness.num_recent_circs = num; } /* else no change, nothing to do */ } else { /* num == 0 */ /* * Weird. This probably shouldn't happen, so log a warning, but try * to do something sensible anyway. */ log_warn(LD_CIRC, "The cbtrecentcircs consensus parameter came back zero! " "This disables adaptive timeouts since we can't keep track of " "any recent circuits."); circuit_build_times_free_timeouts(cbt); } } else { /* * Adaptive timeouts are disabled; this might be because of the * LearnCircuitBuildTimes config parameter, and hence permanent, or * the cbtdisabled consensus parameter, so it may be a new condition. * Treat it like getting num == 0 above and free the circuit history * if we have any. */ circuit_build_times_free_timeouts(cbt); } }