123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572 |
- /* Copyright (c) 2001 Matej Pfajfar.
- * Copyright (c) 2001-2004, Roger Dingledine.
- * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2016, The Tor Project, Inc. */
- /* See LICENSE for licensing information */
- /**
- * \file circpathbias.c
- *
- * \brief Code to track success/failure rates of circuits built through
- * different tor nodes, in an attempt to detect attacks where
- * an attacker deliberately causes circuits to fail until the client
- * choses a path they like.
- *
- * This code is currently configured in a warning-only mode, though false
- * positives appear to be rare in practice. There is also support for
- * disabling really bad guards, but it's quite experimental and may have bad
- * anonymity effects.
- *
- * The information here is associated with the entry_guard_t object for
- * each guard, and stored persistently in the state file.
- */
- #include "or.h"
- #include "channel.h"
- #include "circpathbias.h"
- #include "circuitbuild.h"
- #include "circuitlist.h"
- #include "circuituse.h"
- #include "circuitstats.h"
- #include "connection_edge.h"
- #include "config.h"
- #include "entrynodes.h"
- #include "networkstatus.h"
- #include "relay.h"
- static void pathbias_count_successful_close(origin_circuit_t *circ);
- static void pathbias_count_collapse(origin_circuit_t *circ);
- static void pathbias_count_use_failed(origin_circuit_t *circ);
- static void pathbias_measure_use_rate(entry_guard_t *guard);
- static void pathbias_measure_close_rate(entry_guard_t *guard);
- static void pathbias_scale_use_rates(entry_guard_t *guard);
- static void pathbias_scale_close_rates(entry_guard_t *guard);
- static int entry_guard_inc_circ_attempt_count(entry_guard_t *guard);
- /** Increment the number of times we successfully extended a circuit to
- * <b>guard</b>, first checking if the failure rate is high enough that
- * we should eliminate the guard. Return -1 if the guard looks no good;
- * return 0 if the guard looks fine.
- */
- static int
- entry_guard_inc_circ_attempt_count(entry_guard_t *guard)
- {
- guard_pathbias_t *pb = entry_guard_get_pathbias_state(guard);
- entry_guards_changed();
- pathbias_measure_close_rate(guard);
- if (pb->path_bias_disabled)
- return -1;
- pathbias_scale_close_rates(guard);
- pb->circ_attempts++;
- log_info(LD_CIRC, "Got success count %f/%f for guard %s",
- pb->circ_successes, pb->circ_attempts,
- entry_guard_describe(guard));
- return 0;
- }
- /** The minimum number of circuit attempts before we start
- * thinking about warning about path bias and dropping guards */
- static int
- pathbias_get_min_circs(const or_options_t *options)
- {
- #define DFLT_PATH_BIAS_MIN_CIRC 150
- if (options->PathBiasCircThreshold >= 5)
- return options->PathBiasCircThreshold;
- else
- return networkstatus_get_param(NULL, "pb_mincircs",
- DFLT_PATH_BIAS_MIN_CIRC,
- 5, INT32_MAX);
- }
- /** The circuit success rate below which we issue a notice */
- static double
- pathbias_get_notice_rate(const or_options_t *options)
- {
- #define DFLT_PATH_BIAS_NOTICE_PCT 70
- if (options->PathBiasNoticeRate >= 0.0)
- return options->PathBiasNoticeRate;
- else
- return networkstatus_get_param(NULL, "pb_noticepct",
- DFLT_PATH_BIAS_NOTICE_PCT, 0, 100)/100.0;
- }
- /** The circuit success rate below which we issue a warn */
- static double
- pathbias_get_warn_rate(const or_options_t *options)
- {
- #define DFLT_PATH_BIAS_WARN_PCT 50
- if (options->PathBiasWarnRate >= 0.0)
- return options->PathBiasWarnRate;
- else
- return networkstatus_get_param(NULL, "pb_warnpct",
- DFLT_PATH_BIAS_WARN_PCT, 0, 100)/100.0;
- }
- /* XXXX I'd like to have this be static again, but entrynodes.c needs it. */
- /**
- * The extreme rate is the rate at which we would drop the guard,
- * if pb_dropguard is also set. Otherwise we just warn.
- */
- double
- pathbias_get_extreme_rate(const or_options_t *options)
- {
- #define DFLT_PATH_BIAS_EXTREME_PCT 30
- if (options->PathBiasExtremeRate >= 0.0)
- return options->PathBiasExtremeRate;
- else
- return networkstatus_get_param(NULL, "pb_extremepct",
- DFLT_PATH_BIAS_EXTREME_PCT, 0, 100)/100.0;
- }
- /* XXXX I'd like to have this be static again, but entrynodes.c needs it. */
- /**
- * If 1, we actually disable use of guards that fall below
- * the extreme_pct.
- */
- int
- pathbias_get_dropguards(const or_options_t *options)
- {
- #define DFLT_PATH_BIAS_DROP_GUARDS 0
- if (options->PathBiasDropGuards >= 0)
- return options->PathBiasDropGuards;
- else
- return networkstatus_get_param(NULL, "pb_dropguards",
- DFLT_PATH_BIAS_DROP_GUARDS, 0, 1);
- }
- /**
- * This is the number of circuits at which we scale our
- * counts by mult_factor/scale_factor. Note, this count is
- * not exact, as we only perform the scaling in the event
- * of no integer truncation.
- */
- static int
- pathbias_get_scale_threshold(const or_options_t *options)
- {
- #define DFLT_PATH_BIAS_SCALE_THRESHOLD 300
- if (options->PathBiasScaleThreshold >= 10)
- return options->PathBiasScaleThreshold;
- else
- return networkstatus_get_param(NULL, "pb_scalecircs",
- DFLT_PATH_BIAS_SCALE_THRESHOLD, 10,
- INT32_MAX);
- }
- /**
- * Compute the path bias scaling ratio from the consensus
- * parameters pb_multfactor/pb_scalefactor.
- *
- * Returns a value in (0, 1.0] which we multiply our pathbias
- * counts with to scale them down.
- */
- static double
- pathbias_get_scale_ratio(const or_options_t *options)
- {
- /*
- * The scale factor is the denominator for our scaling
- * of circuit counts for our path bias window.
- *
- * Note that our use of doubles for the path bias state
- * file means that powers of 2 work best here.
- */
- int denominator = networkstatus_get_param(NULL, "pb_scalefactor",
- 2, 2, INT32_MAX);
- (void) options;
- /**
- * The mult factor is the numerator for our scaling
- * of circuit counts for our path bias window. It
- * allows us to scale by fractions.
- */
- return networkstatus_get_param(NULL, "pb_multfactor",
- 1, 1, denominator)/((double)denominator);
- }
- /** The minimum number of circuit usage attempts before we start
- * thinking about warning about path use bias and dropping guards */
- static int
- pathbias_get_min_use(const or_options_t *options)
- {
- #define DFLT_PATH_BIAS_MIN_USE 20
- if (options->PathBiasUseThreshold >= 3)
- return options->PathBiasUseThreshold;
- else
- return networkstatus_get_param(NULL, "pb_minuse",
- DFLT_PATH_BIAS_MIN_USE,
- 3, INT32_MAX);
- }
- /** The circuit use success rate below which we issue a notice */
- static double
- pathbias_get_notice_use_rate(const or_options_t *options)
- {
- #define DFLT_PATH_BIAS_NOTICE_USE_PCT 80
- if (options->PathBiasNoticeUseRate >= 0.0)
- return options->PathBiasNoticeUseRate;
- else
- return networkstatus_get_param(NULL, "pb_noticeusepct",
- DFLT_PATH_BIAS_NOTICE_USE_PCT,
- 0, 100)/100.0;
- }
- /**
- * The extreme use rate is the rate at which we would drop the guard,
- * if pb_dropguard is also set. Otherwise we just warn.
- */
- double
- pathbias_get_extreme_use_rate(const or_options_t *options)
- {
- #define DFLT_PATH_BIAS_EXTREME_USE_PCT 60
- if (options->PathBiasExtremeUseRate >= 0.0)
- return options->PathBiasExtremeUseRate;
- else
- return networkstatus_get_param(NULL, "pb_extremeusepct",
- DFLT_PATH_BIAS_EXTREME_USE_PCT,
- 0, 100)/100.0;
- }
- /**
- * This is the number of circuits at which we scale our
- * use counts by mult_factor/scale_factor. Note, this count is
- * not exact, as we only perform the scaling in the event
- * of no integer truncation.
- */
- static int
- pathbias_get_scale_use_threshold(const or_options_t *options)
- {
- #define DFLT_PATH_BIAS_SCALE_USE_THRESHOLD 100
- if (options->PathBiasScaleUseThreshold >= 10)
- return options->PathBiasScaleUseThreshold;
- else
- return networkstatus_get_param(NULL, "pb_scaleuse",
- DFLT_PATH_BIAS_SCALE_USE_THRESHOLD,
- 10, INT32_MAX);
- }
- /**
- * Convert a Guard's path state to string.
- */
- const char *
- pathbias_state_to_string(path_state_t state)
- {
- switch (state) {
- case PATH_STATE_NEW_CIRC:
- return "new";
- case PATH_STATE_BUILD_ATTEMPTED:
- return "build attempted";
- case PATH_STATE_BUILD_SUCCEEDED:
- return "build succeeded";
- case PATH_STATE_USE_ATTEMPTED:
- return "use attempted";
- case PATH_STATE_USE_SUCCEEDED:
- return "use succeeded";
- case PATH_STATE_USE_FAILED:
- return "use failed";
- case PATH_STATE_ALREADY_COUNTED:
- return "already counted";
- }
- return "unknown";
- }
- /**
- * This function decides if a circuit has progressed far enough to count
- * as a circuit "attempt". As long as end-to-end tagging is possible,
- * we assume the adversary will use it over hop-to-hop failure. Therefore,
- * we only need to account bias for the last hop. This should make us
- * much more resilient to ambient circuit failure, and also make that
- * failure easier to measure (we only need to measure Exit failure rates).
- */
- static int
- pathbias_is_new_circ_attempt(origin_circuit_t *circ)
- {
- #define N2N_TAGGING_IS_POSSIBLE
- #ifdef N2N_TAGGING_IS_POSSIBLE
- /* cpath is a circular list. We want circs with more than one hop,
- * and the second hop must be waiting for keys still (it's just
- * about to get them). */
- return circ->cpath &&
- circ->cpath->next != circ->cpath &&
- circ->cpath->next->state == CPATH_STATE_AWAITING_KEYS;
- #else
- /* If tagging attacks are no longer possible, we probably want to
- * count bias from the first hop. However, one could argue that
- * timing-based tagging is still more useful than per-hop failure.
- * In which case, we'd never want to use this.
- */
- return circ->cpath &&
- circ->cpath->state == CPATH_STATE_AWAITING_KEYS;
- #endif
- }
- /**
- * Decide if the path bias code should count a circuit.
- *
- * @returns 1 if we should count it, 0 otherwise.
- */
- static int
- pathbias_should_count(origin_circuit_t *circ)
- {
- #define PATHBIAS_COUNT_INTERVAL (600)
- static ratelim_t count_limit =
- RATELIM_INIT(PATHBIAS_COUNT_INTERVAL);
- char *rate_msg = NULL;
- /* We can't do path bias accounting without entry guards.
- * Testing and controller circuits also have no guards.
- *
- * We also don't count server-side rends, because their
- * endpoint could be chosen maliciously.
- * Similarly, we can't count client-side intro attempts,
- * because clients can be manipulated into connecting to
- * malicious intro points. */
- if (get_options()->UseEntryGuards == 0 ||
- circ->base_.purpose == CIRCUIT_PURPOSE_TESTING ||
- circ->base_.purpose == CIRCUIT_PURPOSE_CONTROLLER ||
- circ->base_.purpose == CIRCUIT_PURPOSE_S_CONNECT_REND ||
- circ->base_.purpose == CIRCUIT_PURPOSE_S_REND_JOINED ||
- (circ->base_.purpose >= CIRCUIT_PURPOSE_C_INTRODUCING &&
- circ->base_.purpose <= CIRCUIT_PURPOSE_C_INTRODUCE_ACKED)) {
- /* Check to see if the shouldcount result has changed due to a
- * unexpected purpose change that would affect our results.
- *
- * The reason we check the path state too here is because for the
- * cannibalized versions of these purposes, we count them as successful
- * before their purpose change.
- */
- if (circ->pathbias_shouldcount == PATHBIAS_SHOULDCOUNT_COUNTED
- && circ->path_state != PATH_STATE_ALREADY_COUNTED) {
- log_info(LD_BUG,
- "Circuit %d is now being ignored despite being counted "
- "in the past. Purpose is %s, path state is %s",
- circ->global_identifier,
- circuit_purpose_to_string(circ->base_.purpose),
- pathbias_state_to_string(circ->path_state));
- }
- circ->pathbias_shouldcount = PATHBIAS_SHOULDCOUNT_IGNORED;
- return 0;
- }
- /* Completely ignore one hop circuits */
- if (circ->build_state->onehop_tunnel ||
- circ->build_state->desired_path_len == 1) {
- /* Check for inconsistency */
- if (circ->build_state->desired_path_len != 1 ||
- !circ->build_state->onehop_tunnel) {
- if ((rate_msg = rate_limit_log(&count_limit, approx_time()))) {
- log_info(LD_BUG,
- "One-hop circuit has length %d. Path state is %s. "
- "Circuit is a %s currently %s.%s",
- circ->build_state->desired_path_len,
- pathbias_state_to_string(circ->path_state),
- circuit_purpose_to_string(circ->base_.purpose),
- circuit_state_to_string(circ->base_.state),
- rate_msg);
- tor_free(rate_msg);
- }
- tor_fragile_assert();
- }
- /* Check to see if the shouldcount result has changed due to a
- * unexpected change that would affect our results */
- if (circ->pathbias_shouldcount == PATHBIAS_SHOULDCOUNT_COUNTED) {
- log_info(LD_BUG,
- "One-hop circuit %d is now being ignored despite being counted "
- "in the past. Purpose is %s, path state is %s",
- circ->global_identifier,
- circuit_purpose_to_string(circ->base_.purpose),
- pathbias_state_to_string(circ->path_state));
- }
- circ->pathbias_shouldcount = PATHBIAS_SHOULDCOUNT_IGNORED;
- return 0;
- }
- /* Check to see if the shouldcount result has changed due to a
- * unexpected purpose change that would affect our results */
- if (circ->pathbias_shouldcount == PATHBIAS_SHOULDCOUNT_IGNORED) {
- log_info(LD_BUG,
- "Circuit %d is now being counted despite being ignored "
- "in the past. Purpose is %s, path state is %s",
- circ->global_identifier,
- circuit_purpose_to_string(circ->base_.purpose),
- pathbias_state_to_string(circ->path_state));
- }
- circ->pathbias_shouldcount = PATHBIAS_SHOULDCOUNT_COUNTED;
- return 1;
- }
- /**
- * Check our circuit state to see if this is a successful circuit attempt.
- * If so, record it in the current guard's path bias circ_attempt count.
- *
- * Also check for several potential error cases for bug #6475.
- */
- int
- pathbias_count_build_attempt(origin_circuit_t *circ)
- {
- #define CIRC_ATTEMPT_NOTICE_INTERVAL (600)
- static ratelim_t circ_attempt_notice_limit =
- RATELIM_INIT(CIRC_ATTEMPT_NOTICE_INTERVAL);
- char *rate_msg = NULL;
- if (!pathbias_should_count(circ)) {
- return 0;
- }
- if (pathbias_is_new_circ_attempt(circ)) {
- /* Help track down the real cause of bug #6475: */
- if (circ->has_opened && circ->path_state != PATH_STATE_BUILD_ATTEMPTED) {
- if ((rate_msg = rate_limit_log(&circ_attempt_notice_limit,
- approx_time()))) {
- log_info(LD_BUG,
- "Opened circuit is in strange path state %s. "
- "Circuit is a %s currently %s.%s",
- pathbias_state_to_string(circ->path_state),
- circuit_purpose_to_string(circ->base_.purpose),
- circuit_state_to_string(circ->base_.state),
- rate_msg);
- tor_free(rate_msg);
- }
- }
- /* Don't re-count cannibalized circs.. */
- if (!circ->has_opened) {
- entry_guard_t *guard = NULL;
- if (circ->cpath && circ->cpath->extend_info) {
- guard = entry_guard_get_by_id_digest(
- circ->cpath->extend_info->identity_digest);
- } else if (circ->base_.n_chan) {
- guard =
- entry_guard_get_by_id_digest(circ->base_.n_chan->identity_digest);
- }
- if (guard) {
- if (circ->path_state == PATH_STATE_NEW_CIRC) {
- circ->path_state = PATH_STATE_BUILD_ATTEMPTED;
- if (entry_guard_inc_circ_attempt_count(guard) < 0) {
- /* Bogus guard; we already warned. */
- return -END_CIRC_REASON_TORPROTOCOL;
- }
- } else {
- if ((rate_msg = rate_limit_log(&circ_attempt_notice_limit,
- approx_time()))) {
- log_info(LD_BUG,
- "Unopened circuit has strange path state %s. "
- "Circuit is a %s currently %s.%s",
- pathbias_state_to_string(circ->path_state),
- circuit_purpose_to_string(circ->base_.purpose),
- circuit_state_to_string(circ->base_.state),
- rate_msg);
- tor_free(rate_msg);
- }
- }
- } else {
- if ((rate_msg = rate_limit_log(&circ_attempt_notice_limit,
- approx_time()))) {
- log_info(LD_CIRC,
- "Unopened circuit has no known guard. "
- "Circuit is a %s currently %s.%s",
- circuit_purpose_to_string(circ->base_.purpose),
- circuit_state_to_string(circ->base_.state),
- rate_msg);
- tor_free(rate_msg);
- }
- }
- }
- }
- return 0;
- }
- /**
- * Check our circuit state to see if this is a successful circuit
- * completion. If so, record it in the current guard's path bias
- * success count.
- *
- * Also check for several potential error cases for bug #6475.
- */
- void
- pathbias_count_build_success(origin_circuit_t *circ)
- {
- #define SUCCESS_NOTICE_INTERVAL (600)
- static ratelim_t success_notice_limit =
- RATELIM_INIT(SUCCESS_NOTICE_INTERVAL);
- char *rate_msg = NULL;
- entry_guard_t *guard = NULL;
- if (!pathbias_should_count(circ)) {
- return;
- }
- /* Don't count cannibalized/reused circs for path bias
- * "build" success, since they get counted under "use" success. */
- if (!circ->has_opened) {
- if (circ->cpath && circ->cpath->extend_info) {
- guard = entry_guard_get_by_id_digest(
- circ->cpath->extend_info->identity_digest);
- }
- if (guard) {
- guard_pathbias_t *pb = entry_guard_get_pathbias_state(guard);
- if (circ->path_state == PATH_STATE_BUILD_ATTEMPTED) {
- circ->path_state = PATH_STATE_BUILD_SUCCEEDED;
- pb->circ_successes++;
- entry_guards_changed();
- log_info(LD_CIRC, "Got success count %f/%f for guard %s",
- pb->circ_successes, pb->circ_attempts,
- entry_guard_describe(guard));
- } else {
- if ((rate_msg = rate_limit_log(&success_notice_limit,
- approx_time()))) {
- log_info(LD_BUG,
- "Succeeded circuit is in strange path state %s. "
- "Circuit is a %s currently %s.%s",
- pathbias_state_to_string(circ->path_state),
- circuit_purpose_to_string(circ->base_.purpose),
- circuit_state_to_string(circ->base_.state),
- rate_msg);
- tor_free(rate_msg);
- }
- }
- if (pb->circ_attempts < pb->circ_successes) {
- log_notice(LD_BUG, "Unexpectedly high successes counts (%f/%f) "
- "for guard %s",
- pb->circ_successes, pb->circ_attempts,
- entry_guard_describe(guard));
- }
- /* In rare cases, CIRCUIT_PURPOSE_TESTING can get converted to
- * CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT and have no guards here.
- * No need to log that case. */
- } else if (circ->base_.purpose != CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT) {
- if ((rate_msg = rate_limit_log(&success_notice_limit,
- approx_time()))) {
- log_info(LD_CIRC,
- "Completed circuit has no known guard. "
- "Circuit is a %s currently %s.%s",
- circuit_purpose_to_string(circ->base_.purpose),
- circuit_state_to_string(circ->base_.state),
- rate_msg);
- tor_free(rate_msg);
- }
- }
- } else {
- if (circ->path_state < PATH_STATE_BUILD_SUCCEEDED) {
- if ((rate_msg = rate_limit_log(&success_notice_limit,
- approx_time()))) {
- log_info(LD_BUG,
- "Opened circuit is in strange path state %s. "
- "Circuit is a %s currently %s.%s",
- pathbias_state_to_string(circ->path_state),
- circuit_purpose_to_string(circ->base_.purpose),
- circuit_state_to_string(circ->base_.state),
- rate_msg);
- tor_free(rate_msg);
- }
- }
- }
- }
- /**
- * Record an attempt to use a circuit. Changes the circuit's
- * path state and update its guard's usage counter.
- *
- * Used for path bias usage accounting.
- */
- void
- pathbias_count_use_attempt(origin_circuit_t *circ)
- {
- if (!pathbias_should_count(circ)) {
- return;
- }
- if (circ->path_state < PATH_STATE_BUILD_SUCCEEDED) {
- log_notice(LD_BUG,
- "Used circuit is in strange path state %s. "
- "Circuit is a %s currently %s.",
- pathbias_state_to_string(circ->path_state),
- circuit_purpose_to_string(circ->base_.purpose),
- circuit_state_to_string(circ->base_.state));
- } else if (circ->path_state < PATH_STATE_USE_ATTEMPTED) {
- entry_guard_t *guard = entry_guard_get_by_id_digest(
- circ->cpath->extend_info->identity_digest);
- if (guard) {
- guard_pathbias_t *pb = entry_guard_get_pathbias_state(guard);
- pathbias_measure_use_rate(guard);
- pathbias_scale_use_rates(guard);
- pb->use_attempts++;
- entry_guards_changed();
- log_debug(LD_CIRC,
- "Marked circuit %d (%f/%f) as used for guard %s.",
- circ->global_identifier,
- pb->use_successes, pb->use_attempts,
- entry_guard_describe(guard));
- }
- circ->path_state = PATH_STATE_USE_ATTEMPTED;
- } else {
- /* Harmless but educational log message */
- log_info(LD_CIRC,
- "Used circuit %d is already in path state %s. "
- "Circuit is a %s currently %s.",
- circ->global_identifier,
- pathbias_state_to_string(circ->path_state),
- circuit_purpose_to_string(circ->base_.purpose),
- circuit_state_to_string(circ->base_.state));
- }
- return;
- }
- /**
- * Check the circuit's path state is appropriate and mark it as
- * successfully used. Used for path bias usage accounting.
- *
- * We don't actually increment the guard's counters until
- * pathbias_check_close(), because the circuit can still transition
- * back to PATH_STATE_USE_ATTEMPTED if a stream fails later (this
- * is done so we can probe the circuit for liveness at close).
- */
- void
- pathbias_mark_use_success(origin_circuit_t *circ)
- {
- if (!pathbias_should_count(circ)) {
- return;
- }
- if (circ->path_state < PATH_STATE_USE_ATTEMPTED) {
- log_notice(LD_BUG,
- "Used circuit %d is in strange path state %s. "
- "Circuit is a %s currently %s.",
- circ->global_identifier,
- pathbias_state_to_string(circ->path_state),
- circuit_purpose_to_string(circ->base_.purpose),
- circuit_state_to_string(circ->base_.state));
- pathbias_count_use_attempt(circ);
- }
- /* We don't do any accounting at the guard until actual circuit close */
- circ->path_state = PATH_STATE_USE_SUCCEEDED;
- return;
- }
- /**
- * If a stream ever detatches from a circuit in a retriable way,
- * we need to mark this circuit as still needing either another
- * successful stream, or in need of a probe.
- *
- * An adversary could let the first stream request succeed (ie the
- * resolve), but then tag and timeout the remainder (via cell
- * dropping), forcing them on new circuits.
- *
- * Rolling back the state will cause us to probe such circuits, which
- * should lead to probe failures in the event of such tagging due to
- * either unrecognized cells coming in while we wait for the probe,
- * or the cipher state getting out of sync in the case of dropped cells.
- */
- void
- pathbias_mark_use_rollback(origin_circuit_t *circ)
- {
- if (circ->path_state == PATH_STATE_USE_SUCCEEDED) {
- log_info(LD_CIRC,
- "Rolling back pathbias use state to 'attempted' for detached "
- "circuit %d", circ->global_identifier);
- circ->path_state = PATH_STATE_USE_ATTEMPTED;
- }
- }
- /**
- * Actually count a circuit success towards a guard's usage counters
- * if the path state is appropriate.
- */
- static void
- pathbias_count_use_success(origin_circuit_t *circ)
- {
- entry_guard_t *guard;
- if (!pathbias_should_count(circ)) {
- return;
- }
- if (circ->path_state != PATH_STATE_USE_SUCCEEDED) {
- log_notice(LD_BUG,
- "Successfully used circuit %d is in strange path state %s. "
- "Circuit is a %s currently %s.",
- circ->global_identifier,
- pathbias_state_to_string(circ->path_state),
- circuit_purpose_to_string(circ->base_.purpose),
- circuit_state_to_string(circ->base_.state));
- } else {
- guard = entry_guard_get_by_id_digest(
- circ->cpath->extend_info->identity_digest);
- if (guard) {
- guard_pathbias_t *pb = entry_guard_get_pathbias_state(guard);
- pb->use_successes++;
- entry_guards_changed();
- if (pb->use_attempts < pb->use_successes) {
- log_notice(LD_BUG, "Unexpectedly high use successes counts (%f/%f) "
- "for guard %s",
- pb->use_successes, pb->use_attempts,
- entry_guard_describe(guard));
- }
- log_debug(LD_CIRC,
- "Marked circuit %d (%f/%f) as used successfully for guard %s",
- circ->global_identifier, pb->use_successes,
- pb->use_attempts,
- entry_guard_describe(guard));
- }
- }
- return;
- }
- /**
- * Send a probe down a circuit that the client attempted to use,
- * but for which the stream timed out/failed. The probe is a
- * RELAY_BEGIN cell with a 0.a.b.c destination address, which
- * the exit will reject and reply back, echoing that address.
- *
- * The reason for such probes is because it is possible to bias
- * a user's paths simply by causing timeouts, and these timeouts
- * are not possible to differentiate from unresponsive servers.
- *
- * The probe is sent at the end of the circuit lifetime for two
- * reasons: to prevent cryptographic taggers from being able to
- * drop cells to cause timeouts, and to prevent easy recognition
- * of probes before any real client traffic happens.
- *
- * Returns -1 if we couldn't probe, 0 otherwise.
- */
- static int
- pathbias_send_usable_probe(circuit_t *circ)
- {
- /* Based on connection_ap_handshake_send_begin() */
- char payload[CELL_PAYLOAD_SIZE];
- int payload_len;
- origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ);
- crypt_path_t *cpath_layer = NULL;
- char *probe_nonce = NULL;
- tor_assert(ocirc);
- cpath_layer = ocirc->cpath->prev;
- if (cpath_layer->state != CPATH_STATE_OPEN) {
- /* This can happen for cannibalized circuits. Their
- * last hop isn't yet open */
- log_info(LD_CIRC,
- "Got pathbias probe request for unopened circuit %d. "
- "Opened %d, len %d", ocirc->global_identifier,
- ocirc->has_opened, ocirc->build_state->desired_path_len);
- return -1;
- }
- /* We already went down this road. */
- if (circ->purpose == CIRCUIT_PURPOSE_PATH_BIAS_TESTING &&
- ocirc->pathbias_probe_id) {
- log_info(LD_CIRC,
- "Got pathbias probe request for circuit %d with "
- "outstanding probe", ocirc->global_identifier);
- return -1;
- }
- /* Can't probe if the channel isn't open */
- if (circ->n_chan == NULL ||
- (!CHANNEL_IS_OPEN(circ->n_chan)
- && !CHANNEL_IS_MAINT(circ->n_chan))) {
- log_info(LD_CIRC,
- "Skipping pathbias probe for circuit %d: Channel is not open.",
- ocirc->global_identifier);
- return -1;
- }
- circuit_change_purpose(circ, CIRCUIT_PURPOSE_PATH_BIAS_TESTING);
- /* Update timestamp for when circuit_expire_building() should kill us */
- tor_gettimeofday(&circ->timestamp_began);
- /* Generate a random address for the nonce */
- crypto_rand((char*)ô->pathbias_probe_nonce,
- sizeof(ocirc->pathbias_probe_nonce));
- ocirc->pathbias_probe_nonce &= 0x00ffffff;
- probe_nonce = tor_dup_ip(ocirc->pathbias_probe_nonce);
- tor_snprintf(payload,RELAY_PAYLOAD_SIZE, "%s:25", probe_nonce);
- payload_len = (int)strlen(payload)+1;
- // XXX: need this? Can we assume ipv4 will always be supported?
- // If not, how do we tell?
- //if (payload_len <= RELAY_PAYLOAD_SIZE - 4 && edge_conn->begincell_flags) {
- // set_uint32(payload + payload_len, htonl(edge_conn->begincell_flags));
- // payload_len += 4;
- //}
- /* Generate+Store stream id, make sure it's non-zero */
- ocirc->pathbias_probe_id = get_unique_stream_id_by_circ(ocirc);
- if (ocirc->pathbias_probe_id==0) {
- log_warn(LD_CIRC,
- "Ran out of stream IDs on circuit %u during "
- "pathbias probe attempt.", ocirc->global_identifier);
- tor_free(probe_nonce);
- return -1;
- }
- log_info(LD_CIRC,
- "Sending pathbias testing cell to %s:25 on stream %d for circ %d.",
- probe_nonce, ocirc->pathbias_probe_id, ocirc->global_identifier);
- tor_free(probe_nonce);
- /* Send a test relay cell */
- if (relay_send_command_from_edge(ocirc->pathbias_probe_id, circ,
- RELAY_COMMAND_BEGIN, payload,
- payload_len, cpath_layer) < 0) {
- log_notice(LD_CIRC,
- "Failed to send pathbias probe cell on circuit %d.",
- ocirc->global_identifier);
- return -1;
- }
- /* Mark it freshly dirty so it doesn't get expired in the meantime */
- circ->timestamp_dirty = time(NULL);
- return 0;
- }
- /**
- * Check the response to a pathbias probe, to ensure the
- * cell is recognized and the nonce and other probe
- * characteristics are as expected.
- *
- * If the response is valid, return 0. Otherwise return < 0.
- */
- int
- pathbias_check_probe_response(circuit_t *circ, const cell_t *cell)
- {
- /* Based on connection_edge_process_relay_cell() */
- relay_header_t rh;
- int reason;
- uint32_t ipv4_host;
- origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ);
- tor_assert(cell);
- tor_assert(ocirc);
- tor_assert(circ->purpose == CIRCUIT_PURPOSE_PATH_BIAS_TESTING);
- relay_header_unpack(&rh, cell->payload);
- reason = rh.length > 0 ?
- get_uint8(cell->payload+RELAY_HEADER_SIZE) : END_STREAM_REASON_MISC;
- if (rh.command == RELAY_COMMAND_END &&
- reason == END_STREAM_REASON_EXITPOLICY &&
- ocirc->pathbias_probe_id == rh.stream_id) {
- /* Check length+extract host: It is in network order after the reason code.
- * See connection_edge_end(). */
- if (rh.length < 9) { /* reason+ipv4+dns_ttl */
- log_notice(LD_PROTOCOL,
- "Short path bias probe response length field (%d).", rh.length);
- return - END_CIRC_REASON_TORPROTOCOL;
- }
- ipv4_host = ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE+1));
- /* Check nonce */
- if (ipv4_host == ocirc->pathbias_probe_nonce) {
- pathbias_mark_use_success(ocirc);
- circuit_mark_for_close(circ, END_CIRC_REASON_FINISHED);
- log_info(LD_CIRC,
- "Got valid path bias probe back for circ %d, stream %d.",
- ocirc->global_identifier, ocirc->pathbias_probe_id);
- return 0;
- } else {
- log_notice(LD_CIRC,
- "Got strange probe value 0x%x vs 0x%x back for circ %d, "
- "stream %d.", ipv4_host, ocirc->pathbias_probe_nonce,
- ocirc->global_identifier, ocirc->pathbias_probe_id);
- return -1;
- }
- }
- log_info(LD_CIRC,
- "Got another cell back back on pathbias probe circuit %d: "
- "Command: %d, Reason: %d, Stream-id: %d",
- ocirc->global_identifier, rh.command, reason, rh.stream_id);
- return -1;
- }
- /**
- * Check if a circuit was used and/or closed successfully.
- *
- * If we attempted to use the circuit to carry a stream but failed
- * for whatever reason, or if the circuit mysteriously died before
- * we could attach any streams, record these two cases.
- *
- * If we *have* successfully used the circuit, or it appears to
- * have been closed by us locally, count it as a success.
- *
- * Returns 0 if we're done making decisions with the circ,
- * or -1 if we want to probe it first.
- */
- int
- pathbias_check_close(origin_circuit_t *ocirc, int reason)
- {
- circuit_t *circ = ô->base_;
- if (!pathbias_should_count(ocirc)) {
- return 0;
- }
- switch (ocirc->path_state) {
- /* If the circuit was closed after building, but before use, we need
- * to ensure we were the ones who tried to close it (and not a remote
- * actor). */
- case PATH_STATE_BUILD_SUCCEEDED:
- if (reason & END_CIRC_REASON_FLAG_REMOTE) {
- /* Remote circ close reasons on an unused circuit all could be bias */
- log_info(LD_CIRC,
- "Circuit %d remote-closed without successful use for reason %d. "
- "Circuit purpose %d currently %d,%s. Len %d.",
- ocirc->global_identifier,
- reason, circ->purpose, ocirc->has_opened,
- circuit_state_to_string(circ->state),
- ocirc->build_state->desired_path_len);
- pathbias_count_collapse(ocirc);
- } else if ((reason & ~END_CIRC_REASON_FLAG_REMOTE)
- == END_CIRC_REASON_CHANNEL_CLOSED &&
- circ->n_chan &&
- circ->n_chan->reason_for_closing
- != CHANNEL_CLOSE_REQUESTED) {
- /* If we didn't close the channel ourselves, it could be bias */
- /* XXX: Only count bias if the network is live?
- * What about clock jumps/suspends? */
- log_info(LD_CIRC,
- "Circuit %d's channel closed without successful use for reason "
- "%d, channel reason %d. Circuit purpose %d currently %d,%s. Len "
- "%d.", ocirc->global_identifier,
- reason, circ->n_chan->reason_for_closing,
- circ->purpose, ocirc->has_opened,
- circuit_state_to_string(circ->state),
- ocirc->build_state->desired_path_len);
- pathbias_count_collapse(ocirc);
- } else {
- pathbias_count_successful_close(ocirc);
- }
- break;
- /* If we tried to use a circuit but failed, we should probe it to ensure
- * it has not been tampered with. */
- case PATH_STATE_USE_ATTEMPTED:
- /* XXX: Only probe and/or count failure if the network is live?
- * What about clock jumps/suspends? */
- if (pathbias_send_usable_probe(circ) == 0)
- return -1;
- else
- pathbias_count_use_failed(ocirc);
- /* Any circuit where there were attempted streams but no successful
- * streams could be bias */
- log_info(LD_CIRC,
- "Circuit %d closed without successful use for reason %d. "
- "Circuit purpose %d currently %d,%s. Len %d.",
- ocirc->global_identifier,
- reason, circ->purpose, ocirc->has_opened,
- circuit_state_to_string(circ->state),
- ocirc->build_state->desired_path_len);
- break;
- case PATH_STATE_USE_SUCCEEDED:
- pathbias_count_successful_close(ocirc);
- pathbias_count_use_success(ocirc);
- break;
- case PATH_STATE_USE_FAILED:
- pathbias_count_use_failed(ocirc);
- break;
- case PATH_STATE_NEW_CIRC:
- case PATH_STATE_BUILD_ATTEMPTED:
- case PATH_STATE_ALREADY_COUNTED:
- default:
- // Other states are uninteresting. No stats to count.
- break;
- }
- ocirc->path_state = PATH_STATE_ALREADY_COUNTED;
- return 0;
- }
- /**
- * Count a successfully closed circuit.
- */
- static void
- pathbias_count_successful_close(origin_circuit_t *circ)
- {
- entry_guard_t *guard = NULL;
- if (!pathbias_should_count(circ)) {
- return;
- }
- if (circ->cpath && circ->cpath->extend_info) {
- guard = entry_guard_get_by_id_digest(
- circ->cpath->extend_info->identity_digest);
- }
- if (guard) {
- guard_pathbias_t *pb = entry_guard_get_pathbias_state(guard);
- /* In the long run: circuit_success ~= successful_circuit_close +
- * circ_failure + stream_failure */
- pb->successful_circuits_closed++;
- entry_guards_changed();
- } else if (circ->base_.purpose != CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT) {
- /* In rare cases, CIRCUIT_PURPOSE_TESTING can get converted to
- * CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT and have no guards here.
- * No need to log that case. */
- log_info(LD_CIRC,
- "Successfully closed circuit has no known guard. "
- "Circuit is a %s currently %s",
- circuit_purpose_to_string(circ->base_.purpose),
- circuit_state_to_string(circ->base_.state));
- }
- }
- /**
- * Count a circuit that fails after it is built, but before it can
- * carry any traffic.
- *
- * This is needed because there are ways to destroy a
- * circuit after it has successfully completed. Right now, this is
- * used for purely informational/debugging purposes.
- */
- static void
- pathbias_count_collapse(origin_circuit_t *circ)
- {
- entry_guard_t *guard = NULL;
- if (!pathbias_should_count(circ)) {
- return;
- }
- if (circ->cpath && circ->cpath->extend_info) {
- guard = entry_guard_get_by_id_digest(
- circ->cpath->extend_info->identity_digest);
- }
- if (guard) {
- guard_pathbias_t *pb = entry_guard_get_pathbias_state(guard);
- pb->collapsed_circuits++;
- entry_guards_changed();
- } else if (circ->base_.purpose != CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT) {
- /* In rare cases, CIRCUIT_PURPOSE_TESTING can get converted to
- * CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT and have no guards here.
- * No need to log that case. */
- log_info(LD_CIRC,
- "Destroyed circuit has no known guard. "
- "Circuit is a %s currently %s",
- circuit_purpose_to_string(circ->base_.purpose),
- circuit_state_to_string(circ->base_.state));
- }
- }
- /**
- * Count a known failed circuit (because we could not probe it).
- *
- * This counter is informational.
- */
- static void
- pathbias_count_use_failed(origin_circuit_t *circ)
- {
- entry_guard_t *guard = NULL;
- if (!pathbias_should_count(circ)) {
- return;
- }
- if (circ->cpath && circ->cpath->extend_info) {
- guard = entry_guard_get_by_id_digest(
- circ->cpath->extend_info->identity_digest);
- }
- if (guard) {
- guard_pathbias_t *pb = entry_guard_get_pathbias_state(guard);
- pb->unusable_circuits++;
- entry_guards_changed();
- } else if (circ->base_.purpose != CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT) {
- /* In rare cases, CIRCUIT_PURPOSE_TESTING can get converted to
- * CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT and have no guards here.
- * No need to log that case. */
- /* XXX note cut-and-paste code in this function compared to nearby
- * functions. Would be nice to refactor. -RD */
- log_info(LD_CIRC,
- "Stream-failing circuit has no known guard. "
- "Circuit is a %s currently %s",
- circuit_purpose_to_string(circ->base_.purpose),
- circuit_state_to_string(circ->base_.state));
- }
- }
- /**
- * Count timeouts for path bias log messages.
- *
- * These counts are purely informational.
- */
- void
- pathbias_count_timeout(origin_circuit_t *circ)
- {
- entry_guard_t *guard = NULL;
- if (!pathbias_should_count(circ)) {
- return;
- }
- /* For hidden service circs, they can actually be used
- * successfully and then time out later (because
- * the other side declines to use them). */
- if (circ->path_state == PATH_STATE_USE_SUCCEEDED) {
- return;
- }
- if (circ->cpath && circ->cpath->extend_info) {
- guard = entry_guard_get_by_id_digest(
- circ->cpath->extend_info->identity_digest);
- }
- if (guard) {
- guard_pathbias_t *pb = entry_guard_get_pathbias_state(guard);
- pb->timeouts++;
- entry_guards_changed();
- }
- }
- /**
- * Helper function to count all of the currently opened circuits
- * for a guard that are in a given path state range. The state
- * range is inclusive on both ends.
- */
- static int
- pathbias_count_circs_in_states(entry_guard_t *guard,
- path_state_t from,
- path_state_t to)
- {
- int open_circuits = 0;
- /* Count currently open circuits. Give them the benefit of the doubt. */
- SMARTLIST_FOREACH_BEGIN(circuit_get_global_list(), circuit_t *, circ) {
- origin_circuit_t *ocirc = NULL;
- if (!CIRCUIT_IS_ORIGIN(circ) || /* didn't originate here */
- circ->marked_for_close) /* already counted */
- continue;
- ocirc = TO_ORIGIN_CIRCUIT(circ);
- if (!ocirc->cpath || !ocirc->cpath->extend_info)
- continue;
- if (ocirc->path_state >= from &&
- ocirc->path_state <= to &&
- pathbias_should_count(ocirc) &&
- fast_memeq(entry_guard_get_rsa_id_digest(guard),
- ocirc->cpath->extend_info->identity_digest,
- DIGEST_LEN)) {
- log_debug(LD_CIRC, "Found opened circuit %d in path_state %s",
- ocirc->global_identifier,
- pathbias_state_to_string(ocirc->path_state));
- open_circuits++;
- }
- }
- SMARTLIST_FOREACH_END(circ);
- return open_circuits;
- }
- /**
- * Return the number of circuits counted as successfully closed for
- * this guard.
- *
- * Also add in the currently open circuits to give them the benefit
- * of the doubt.
- */
- double
- pathbias_get_close_success_count(entry_guard_t *guard)
- {
- guard_pathbias_t *pb = entry_guard_get_pathbias_state(guard);
- return pb->successful_circuits_closed +
- pathbias_count_circs_in_states(guard,
- PATH_STATE_BUILD_SUCCEEDED,
- PATH_STATE_USE_SUCCEEDED);
- }
- /**
- * Return the number of circuits counted as successfully used
- * this guard.
- *
- * Also add in the currently open circuits that we are attempting
- * to use to give them the benefit of the doubt.
- */
- double
- pathbias_get_use_success_count(entry_guard_t *guard)
- {
- guard_pathbias_t *pb = entry_guard_get_pathbias_state(guard);
- return pb->use_successes +
- pathbias_count_circs_in_states(guard,
- PATH_STATE_USE_ATTEMPTED,
- PATH_STATE_USE_SUCCEEDED);
- }
- /**
- * Check the path bias use rate against our consensus parameter limits.
- *
- * Emits a log message if the use success rates are too low.
- *
- * If pathbias_get_dropguards() is set, we also disable the use of
- * very failure prone guards.
- */
- static void
- pathbias_measure_use_rate(entry_guard_t *guard)
- {
- const or_options_t *options = get_options();
- guard_pathbias_t *pb = entry_guard_get_pathbias_state(guard);
- if (pb->use_attempts > pathbias_get_min_use(options)) {
- /* Note: We rely on the < comparison here to allow us to set a 0
- * rate and disable the feature entirely. If refactoring, don't
- * change to <= */
- if (pathbias_get_use_success_count(guard)/pb->use_attempts
- < pathbias_get_extreme_use_rate(options)) {
- /* Dropping is currently disabled by default. */
- if (pathbias_get_dropguards(options)) {
- if (!pb->path_bias_disabled) {
- log_warn(LD_CIRC,
- "Your Guard %s is failing to carry an extremely large "
- "amount of stream on its circuits. "
- "To avoid potential route manipulation attacks, Tor has "
- "disabled use of this guard. "
- "Use counts are %ld/%ld. Success counts are %ld/%ld. "
- "%ld circuits completed, %ld were unusable, %ld collapsed, "
- "and %ld timed out. "
- "For reference, your timeout cutoff is %ld seconds.",
- entry_guard_describe(guard),
- tor_lround(pathbias_get_use_success_count(guard)),
- tor_lround(pb->use_attempts),
- tor_lround(pathbias_get_close_success_count(guard)),
- tor_lround(pb->circ_attempts),
- tor_lround(pb->circ_successes),
- tor_lround(pb->unusable_circuits),
- tor_lround(pb->collapsed_circuits),
- tor_lround(pb->timeouts),
- tor_lround(get_circuit_build_close_time_ms()/1000));
- pb->path_bias_disabled = 1;
- entry_guard_mark_bad(guard);
- return;
- }
- } else if (!pb->path_bias_use_extreme) {
- pb->path_bias_use_extreme = 1;
- log_warn(LD_CIRC,
- "Your Guard %s is failing to carry an extremely large "
- "amount of streams on its circuits. "
- "This could indicate a route manipulation attack, network "
- "overload, bad local network connectivity, or a bug. "
- "Use counts are %ld/%ld. Success counts are %ld/%ld. "
- "%ld circuits completed, %ld were unusable, %ld collapsed, "
- "and %ld timed out. "
- "For reference, your timeout cutoff is %ld seconds.",
- entry_guard_describe(guard),
- tor_lround(pathbias_get_use_success_count(guard)),
- tor_lround(pb->use_attempts),
- tor_lround(pathbias_get_close_success_count(guard)),
- tor_lround(pb->circ_attempts),
- tor_lround(pb->circ_successes),
- tor_lround(pb->unusable_circuits),
- tor_lround(pb->collapsed_circuits),
- tor_lround(pb->timeouts),
- tor_lround(get_circuit_build_close_time_ms()/1000));
- }
- } else if (pathbias_get_use_success_count(guard)/pb->use_attempts
- < pathbias_get_notice_use_rate(options)) {
- if (!pb->path_bias_use_noticed) {
- pb->path_bias_use_noticed = 1;
- log_notice(LD_CIRC,
- "Your Guard %s is failing to carry more streams on its "
- "circuits than usual. "
- "Most likely this means the Tor network is overloaded "
- "or your network connection is poor. "
- "Use counts are %ld/%ld. Success counts are %ld/%ld. "
- "%ld circuits completed, %ld were unusable, %ld collapsed, "
- "and %ld timed out. "
- "For reference, your timeout cutoff is %ld seconds.",
- entry_guard_describe(guard),
- tor_lround(pathbias_get_use_success_count(guard)),
- tor_lround(pb->use_attempts),
- tor_lround(pathbias_get_close_success_count(guard)),
- tor_lround(pb->circ_attempts),
- tor_lround(pb->circ_successes),
- tor_lround(pb->unusable_circuits),
- tor_lround(pb->collapsed_circuits),
- tor_lround(pb->timeouts),
- tor_lround(get_circuit_build_close_time_ms()/1000));
- }
- }
- }
- }
- /**
- * Check the path bias circuit close status rates against our consensus
- * parameter limits.
- *
- * Emits a log message if the use success rates are too low.
- *
- * If pathbias_get_dropguards() is set, we also disable the use of
- * very failure prone guards.
- *
- * XXX: This function shares similar log messages and checks to
- * pathbias_measure_use_rate(). It may be possible to combine them
- * eventually, especially if we can ever remove the need for 3
- * levels of closure warns (if the overall circuit failure rate
- * goes down with ntor). One way to do so would be to multiply
- * the build rate with the use rate to get an idea of the total
- * fraction of the total network paths the user is able to use.
- * See ticket #8159.
- */
- static void
- pathbias_measure_close_rate(entry_guard_t *guard)
- {
- const or_options_t *options = get_options();
- guard_pathbias_t *pb = entry_guard_get_pathbias_state(guard);
- if (pb->circ_attempts > pathbias_get_min_circs(options)) {
- /* Note: We rely on the < comparison here to allow us to set a 0
- * rate and disable the feature entirely. If refactoring, don't
- * change to <= */
- if (pathbias_get_close_success_count(guard)/pb->circ_attempts
- < pathbias_get_extreme_rate(options)) {
- /* Dropping is currently disabled by default. */
- if (pathbias_get_dropguards(options)) {
- if (!pb->path_bias_disabled) {
- log_warn(LD_CIRC,
- "Your Guard %s is failing an extremely large "
- "amount of circuits. "
- "To avoid potential route manipulation attacks, Tor has "
- "disabled use of this guard. "
- "Success counts are %ld/%ld. Use counts are %ld/%ld. "
- "%ld circuits completed, %ld were unusable, %ld collapsed, "
- "and %ld timed out. "
- "For reference, your timeout cutoff is %ld seconds.",
- entry_guard_describe(guard),
- tor_lround(pathbias_get_close_success_count(guard)),
- tor_lround(pb->circ_attempts),
- tor_lround(pathbias_get_use_success_count(guard)),
- tor_lround(pb->use_attempts),
- tor_lround(pb->circ_successes),
- tor_lround(pb->unusable_circuits),
- tor_lround(pb->collapsed_circuits),
- tor_lround(pb->timeouts),
- tor_lround(get_circuit_build_close_time_ms()/1000));
- pb->path_bias_disabled = 1;
- entry_guard_mark_bad(guard);
- return;
- }
- } else if (!pb->path_bias_extreme) {
- pb->path_bias_extreme = 1;
- log_warn(LD_CIRC,
- "Your Guard %s is failing an extremely large "
- "amount of circuits. "
- "This could indicate a route manipulation attack, "
- "extreme network overload, or a bug. "
- "Success counts are %ld/%ld. Use counts are %ld/%ld. "
- "%ld circuits completed, %ld were unusable, %ld collapsed, "
- "and %ld timed out. "
- "For reference, your timeout cutoff is %ld seconds.",
- entry_guard_describe(guard),
- tor_lround(pathbias_get_close_success_count(guard)),
- tor_lround(pb->circ_attempts),
- tor_lround(pathbias_get_use_success_count(guard)),
- tor_lround(pb->use_attempts),
- tor_lround(pb->circ_successes),
- tor_lround(pb->unusable_circuits),
- tor_lround(pb->collapsed_circuits),
- tor_lround(pb->timeouts),
- tor_lround(get_circuit_build_close_time_ms()/1000));
- }
- } else if (pathbias_get_close_success_count(guard)/pb->circ_attempts
- < pathbias_get_warn_rate(options)) {
- if (!pb->path_bias_warned) {
- pb->path_bias_warned = 1;
- log_warn(LD_CIRC,
- "Your Guard %s is failing a very large "
- "amount of circuits. "
- "Most likely this means the Tor network is "
- "overloaded, but it could also mean an attack against "
- "you or potentially the guard itself. "
- "Success counts are %ld/%ld. Use counts are %ld/%ld. "
- "%ld circuits completed, %ld were unusable, %ld collapsed, "
- "and %ld timed out. "
- "For reference, your timeout cutoff is %ld seconds.",
- entry_guard_describe(guard),
- tor_lround(pathbias_get_close_success_count(guard)),
- tor_lround(pb->circ_attempts),
- tor_lround(pathbias_get_use_success_count(guard)),
- tor_lround(pb->use_attempts),
- tor_lround(pb->circ_successes),
- tor_lround(pb->unusable_circuits),
- tor_lround(pb->collapsed_circuits),
- tor_lround(pb->timeouts),
- tor_lround(get_circuit_build_close_time_ms()/1000));
- }
- } else if (pathbias_get_close_success_count(guard)/pb->circ_attempts
- < pathbias_get_notice_rate(options)) {
- if (!pb->path_bias_noticed) {
- pb->path_bias_noticed = 1;
- log_notice(LD_CIRC,
- "Your Guard %s is failing more circuits than "
- "usual. "
- "Most likely this means the Tor network is overloaded. "
- "Success counts are %ld/%ld. Use counts are %ld/%ld. "
- "%ld circuits completed, %ld were unusable, %ld collapsed, "
- "and %ld timed out. "
- "For reference, your timeout cutoff is %ld seconds.",
- entry_guard_describe(guard),
- tor_lround(pathbias_get_close_success_count(guard)),
- tor_lround(pb->circ_attempts),
- tor_lround(pathbias_get_use_success_count(guard)),
- tor_lround(pb->use_attempts),
- tor_lround(pb->circ_successes),
- tor_lround(pb->unusable_circuits),
- tor_lround(pb->collapsed_circuits),
- tor_lround(pb->timeouts),
- tor_lround(get_circuit_build_close_time_ms()/1000));
- }
- }
- }
- }
- /**
- * This function scales the path bias use rates if we have
- * more data than the scaling threshold. This allows us to
- * be more sensitive to recent measurements.
- *
- * XXX: The attempt count transfer stuff here might be done
- * better by keeping separate pending counters that get
- * transfered at circuit close. See ticket #8160.
- */
- static void
- pathbias_scale_close_rates(entry_guard_t *guard)
- {
- const or_options_t *options = get_options();
- guard_pathbias_t *pb = entry_guard_get_pathbias_state(guard);
- /* If we get a ton of circuits, just scale everything down */
- if (pb->circ_attempts > pathbias_get_scale_threshold(options)) {
- double scale_ratio = pathbias_get_scale_ratio(options);
- int opened_attempts = pathbias_count_circs_in_states(guard,
- PATH_STATE_BUILD_ATTEMPTED, PATH_STATE_BUILD_ATTEMPTED);
- int opened_built = pathbias_count_circs_in_states(guard,
- PATH_STATE_BUILD_SUCCEEDED,
- PATH_STATE_USE_FAILED);
- /* Verify that the counts are sane before and after scaling */
- int counts_are_sane = (pb->circ_attempts >= pb->circ_successes);
- pb->circ_attempts -= (opened_attempts+opened_built);
- pb->circ_successes -= opened_built;
- pb->circ_attempts *= scale_ratio;
- pb->circ_successes *= scale_ratio;
- pb->timeouts *= scale_ratio;
- pb->successful_circuits_closed *= scale_ratio;
- pb->collapsed_circuits *= scale_ratio;
- pb->unusable_circuits *= scale_ratio;
- pb->circ_attempts += (opened_attempts+opened_built);
- pb->circ_successes += opened_built;
- entry_guards_changed();
- log_info(LD_CIRC,
- "Scaled pathbias counts to (%f,%f)/%f (%d/%d open) for guard "
- "%s",
- pb->circ_successes, pb->successful_circuits_closed,
- pb->circ_attempts, opened_built, opened_attempts,
- entry_guard_describe(guard));
- /* Have the counts just become invalid by this scaling attempt? */
- if (counts_are_sane && pb->circ_attempts < pb->circ_successes) {
- log_notice(LD_BUG,
- "Scaling has mangled pathbias counts to %f/%f (%d/%d open) "
- "for guard %s",
- pb->circ_successes, pb->circ_attempts, opened_built,
- opened_attempts,
- entry_guard_describe(guard));
- }
- }
- }
- /**
- * This function scales the path bias circuit close rates if we have
- * more data than the scaling threshold. This allows us to be more
- * sensitive to recent measurements.
- *
- * XXX: The attempt count transfer stuff here might be done
- * better by keeping separate pending counters that get
- * transfered at circuit close. See ticket #8160.
- */
- void
- pathbias_scale_use_rates(entry_guard_t *guard)
- {
- const or_options_t *options = get_options();
- guard_pathbias_t *pb = entry_guard_get_pathbias_state(guard);
- /* If we get a ton of circuits, just scale everything down */
- if (pb->use_attempts > pathbias_get_scale_use_threshold(options)) {
- double scale_ratio = pathbias_get_scale_ratio(options);
- int opened_attempts = pathbias_count_circs_in_states(guard,
- PATH_STATE_USE_ATTEMPTED, PATH_STATE_USE_SUCCEEDED);
- /* Verify that the counts are sane before and after scaling */
- int counts_are_sane = (pb->use_attempts >= pb->use_successes);
- pb->use_attempts -= opened_attempts;
- pb->use_attempts *= scale_ratio;
- pb->use_successes *= scale_ratio;
- pb->use_attempts += opened_attempts;
- log_info(LD_CIRC,
- "Scaled pathbias use counts to %f/%f (%d open) for guard %s",
- pb->use_successes, pb->use_attempts, opened_attempts,
- entry_guard_describe(guard));
- /* Have the counts just become invalid by this scaling attempt? */
- if (counts_are_sane && pb->use_attempts < pb->use_successes) {
- log_notice(LD_BUG,
- "Scaling has mangled pathbias usage counts to %f/%f "
- "(%d open) for guard %s",
- pb->circ_successes, pb->circ_attempts,
- opened_attempts, entry_guard_describe(guard));
- }
- entry_guards_changed();
- }
- }
|