Loading changes/bug13126 0 → 100644 +10 −0 Original line number Diff line number Diff line o Code simplification and refactoring: - Remove our old, non-weighted bandwidth-based node selection code. Previously, we used it as a fallback when we couldn't perform weighted bandwidth-based node selection. But that would only happen in the cases where we had no consensus, or when we had a consensus generated by buggy or ancient directory authorities. In either case, it's better to use the more modern, better maintained algorithm, with reasonable defaults for the weights. Closes ticket 13126. No newline at end of file src/or/or.h +1 −1 Original line number Diff line number Diff line Loading @@ -5025,7 +5025,7 @@ typedef enum was_router_added_t { ROUTER_WAS_NOT_WANTED = -6, /* Router descriptor was rejected because it was older than * OLD_ROUTER_DESC_MAX_AGE. */ ROUTER_WAS_TOO_OLD = -7, ROUTER_WAS_TOO_OLD = -7, /* note contrast with 'NOT_NEW' */ } was_router_added_t; /********************************* routerparse.c ************************/ Loading src/or/routerlist.c +14 −222 Original line number Diff line number Diff line Loading @@ -2030,9 +2030,10 @@ compute_weighted_bandwidths(const smartlist_t *sl, if (Wg < 0 || Wm < 0 || We < 0 || Wd < 0 || Wgb < 0 || Wmb < 0 || Wdb < 0 || Web < 0) { log_debug(LD_CIRC, "Got negative bandwidth weights. Defaulting to old selection" "Got negative bandwidth weights. Defaulting to naive selection" " algorithm."); return -1; // Use old algorithm. Wg = Wm = We = Wd = weight_scale; Wgb = Wmb = Web = Wdb = weight_scale; } Wg /= weight_scale; Loading @@ -2048,6 +2049,7 @@ compute_weighted_bandwidths(const smartlist_t *sl, bandwidths = tor_calloc(smartlist_len(sl), sizeof(u64_dbl_t)); // Cycle through smartlist and total the bandwidth. static int warned_missing_bw = 0; SMARTLIST_FOREACH_BEGIN(sl, const node_t *, node) { int is_exit = 0, is_guard = 0, is_dir = 0, this_bw = 0; double weight = 1; Loading @@ -2056,15 +2058,18 @@ compute_weighted_bandwidths(const smartlist_t *sl, is_dir = node_is_dir(node); if (node->rs) { if (!node->rs->has_bandwidth) { tor_free(bandwidths); /* This should never happen, unless all the authorites downgrade * to 0.2.0 or rogue routerstatuses get inserted into our consensus. */ if (! warned_missing_bw) { log_warn(LD_BUG, "Consensus is not listing bandwidths. Defaulting back to " "old router selection algorithm."); return -1; "Consensus is missing some bandwidths. Using a naive " "router selection algorithm"); warned_missing_bw = 1; } this_bw = 30000; /* Chosen arbitrarily */ } else { this_bw = kb_to_bytes(node->rs->bandwidth_kb); } } else if (node->ri) { /* bridge or other descriptor not in our consensus */ this_bw = bridge_get_advertised_bandwidth_bounded(node->ri); Loading Loading @@ -2141,226 +2146,13 @@ frac_nodes_with_descriptors(const smartlist_t *sl, return present / total; } /** Helper function: * choose a random node_t element of smartlist <b>sl</b>, weighted by * the advertised bandwidth of each element. * * If <b>rule</b>==WEIGHT_FOR_EXIT. we're picking an exit node: consider all * nodes' bandwidth equally regardless of their Exit status, since there may * be some in the list because they exit to obscure ports. If * <b>rule</b>==NO_WEIGHTING, we're picking a non-exit node: weight * exit-node's bandwidth less depending on the smallness of the fraction of * Exit-to-total bandwidth. If <b>rule</b>==WEIGHT_FOR_GUARD, we're picking a * guard node: consider all guard's bandwidth equally. Otherwise, weight * guards proportionally less. */ static const node_t * smartlist_choose_node_by_bandwidth(const smartlist_t *sl, bandwidth_weight_rule_t rule) { unsigned int i; u64_dbl_t *bandwidths; int is_exit; int is_guard; int is_fast; double total_nonexit_bw = 0, total_exit_bw = 0; double total_nonguard_bw = 0, total_guard_bw = 0; double exit_weight; double guard_weight; int n_unknown = 0; bitarray_t *fast_bits; bitarray_t *exit_bits; bitarray_t *guard_bits; // This function does not support WEIGHT_FOR_DIR // or WEIGHT_FOR_MID if (rule == WEIGHT_FOR_DIR || rule == WEIGHT_FOR_MID) { rule = NO_WEIGHTING; } /* Can't choose exit and guard at same time */ tor_assert(rule == NO_WEIGHTING || rule == WEIGHT_FOR_EXIT || rule == WEIGHT_FOR_GUARD); if (smartlist_len(sl) == 0) { log_info(LD_CIRC, "Empty routerlist passed in to old node selection for rule %s", bandwidth_weight_rule_to_string(rule)); return NULL; } /* First count the total bandwidth weight, and make a list * of each value. We use UINT64_MAX to indicate "unknown". */ bandwidths = tor_calloc(smartlist_len(sl), sizeof(u64_dbl_t)); fast_bits = bitarray_init_zero(smartlist_len(sl)); exit_bits = bitarray_init_zero(smartlist_len(sl)); guard_bits = bitarray_init_zero(smartlist_len(sl)); /* Iterate over all the routerinfo_t or routerstatus_t, and */ SMARTLIST_FOREACH_BEGIN(sl, const node_t *, node) { /* first, learn what bandwidth we think i has */ int is_known = 1; uint32_t this_bw = 0; i = node_sl_idx; is_exit = node_is_good_exit(node); is_guard = node->is_possible_guard; if (node->rs) { if (node->rs->has_bandwidth) { this_bw = kb_to_bytes(node->rs->bandwidth_kb); } else { /* guess */ is_known = 0; } } else if (node->ri) { /* Must be a bridge if we're willing to use it */ this_bw = bridge_get_advertised_bandwidth_bounded(node->ri); } if (is_exit) bitarray_set(exit_bits, i); if (is_guard) bitarray_set(guard_bits, i); if (node->is_fast) bitarray_set(fast_bits, i); if (is_known) { bandwidths[i].dbl = this_bw; if (is_guard) total_guard_bw += this_bw; else total_nonguard_bw += this_bw; if (is_exit) total_exit_bw += this_bw; else total_nonexit_bw += this_bw; } else { ++n_unknown; bandwidths[i].dbl = -1.0; } } SMARTLIST_FOREACH_END(node); #define EPSILON .1 /* Now, fill in the unknown values. */ if (n_unknown) { int32_t avg_fast, avg_slow; if (total_exit_bw+total_nonexit_bw < EPSILON) { /* if there's some bandwidth, there's at least one known router, * so no worries about div by 0 here */ int n_known = smartlist_len(sl)-n_unknown; avg_fast = avg_slow = (int32_t) ((total_exit_bw+total_nonexit_bw)/((uint64_t) n_known)); } else { avg_fast = 40000; avg_slow = 20000; } for (i=0; i<(unsigned)smartlist_len(sl); ++i) { if (bandwidths[i].dbl >= 0.0) continue; is_fast = bitarray_is_set(fast_bits, i); is_exit = bitarray_is_set(exit_bits, i); is_guard = bitarray_is_set(guard_bits, i); bandwidths[i].dbl = is_fast ? avg_fast : avg_slow; if (is_exit) total_exit_bw += bandwidths[i].dbl; else total_nonexit_bw += bandwidths[i].dbl; if (is_guard) total_guard_bw += bandwidths[i].dbl; else total_nonguard_bw += bandwidths[i].dbl; } } /* If there's no bandwidth at all, pick at random. */ if (total_exit_bw+total_nonexit_bw < EPSILON) { tor_free(bandwidths); tor_free(fast_bits); tor_free(exit_bits); tor_free(guard_bits); return smartlist_choose(sl); } /* Figure out how to weight exits and guards */ { double all_bw = U64_TO_DBL(total_exit_bw+total_nonexit_bw); double exit_bw = U64_TO_DBL(total_exit_bw); double guard_bw = U64_TO_DBL(total_guard_bw); /* * For detailed derivation of this formula, see * http://archives.seul.org/or/dev/Jul-2007/msg00056.html */ if (rule == WEIGHT_FOR_EXIT || total_exit_bw<EPSILON) exit_weight = 1.0; else exit_weight = 1.0 - all_bw/(3.0*exit_bw); if (rule == WEIGHT_FOR_GUARD || total_guard_bw<EPSILON) guard_weight = 1.0; else guard_weight = 1.0 - all_bw/(3.0*guard_bw); if (exit_weight <= 0.0) exit_weight = 0.0; if (guard_weight <= 0.0) guard_weight = 0.0; for (i=0; i < (unsigned)smartlist_len(sl); i++) { tor_assert(bandwidths[i].dbl >= 0.0); is_exit = bitarray_is_set(exit_bits, i); is_guard = bitarray_is_set(guard_bits, i); if (is_exit && is_guard) bandwidths[i].dbl *= exit_weight * guard_weight; else if (is_guard) bandwidths[i].dbl *= guard_weight; else if (is_exit) bandwidths[i].dbl *= exit_weight; } } #if 0 log_debug(LD_CIRC, "Total weighted bw = "U64_FORMAT ", exit bw = "U64_FORMAT ", nonexit bw = "U64_FORMAT", exit weight = %f " "(for exit == %d)" ", guard bw = "U64_FORMAT ", nonguard bw = "U64_FORMAT", guard weight = %f " "(for guard == %d)", U64_PRINTF_ARG(total_bw), U64_PRINTF_ARG(total_exit_bw), U64_PRINTF_ARG(total_nonexit_bw), exit_weight, (int)(rule == WEIGHT_FOR_EXIT), U64_PRINTF_ARG(total_guard_bw), U64_PRINTF_ARG(total_nonguard_bw), guard_weight, (int)(rule == WEIGHT_FOR_GUARD)); #endif scale_array_elements_to_u64(bandwidths, smartlist_len(sl), NULL); { int idx = choose_array_element_by_weight(bandwidths, smartlist_len(sl)); tor_free(bandwidths); tor_free(fast_bits); tor_free(exit_bits); tor_free(guard_bits); return idx < 0 ? NULL : smartlist_get(sl, idx); } } /** Choose a random element of status list <b>sl</b>, weighted by * the advertised bandwidth of each node */ const node_t * node_sl_choose_by_bandwidth(const smartlist_t *sl, bandwidth_weight_rule_t rule) { /*XXXX MOVE */ const node_t *ret; if ((ret = smartlist_choose_node_by_bandwidth_weights(sl, rule))) { return ret; } else { return smartlist_choose_node_by_bandwidth(sl, rule); } return smartlist_choose_node_by_bandwidth_weights(sl, rule); } /** Return a random running node from the nodelist. Never Loading Loading
changes/bug13126 0 → 100644 +10 −0 Original line number Diff line number Diff line o Code simplification and refactoring: - Remove our old, non-weighted bandwidth-based node selection code. Previously, we used it as a fallback when we couldn't perform weighted bandwidth-based node selection. But that would only happen in the cases where we had no consensus, or when we had a consensus generated by buggy or ancient directory authorities. In either case, it's better to use the more modern, better maintained algorithm, with reasonable defaults for the weights. Closes ticket 13126. No newline at end of file
src/or/or.h +1 −1 Original line number Diff line number Diff line Loading @@ -5025,7 +5025,7 @@ typedef enum was_router_added_t { ROUTER_WAS_NOT_WANTED = -6, /* Router descriptor was rejected because it was older than * OLD_ROUTER_DESC_MAX_AGE. */ ROUTER_WAS_TOO_OLD = -7, ROUTER_WAS_TOO_OLD = -7, /* note contrast with 'NOT_NEW' */ } was_router_added_t; /********************************* routerparse.c ************************/ Loading
src/or/routerlist.c +14 −222 Original line number Diff line number Diff line Loading @@ -2030,9 +2030,10 @@ compute_weighted_bandwidths(const smartlist_t *sl, if (Wg < 0 || Wm < 0 || We < 0 || Wd < 0 || Wgb < 0 || Wmb < 0 || Wdb < 0 || Web < 0) { log_debug(LD_CIRC, "Got negative bandwidth weights. Defaulting to old selection" "Got negative bandwidth weights. Defaulting to naive selection" " algorithm."); return -1; // Use old algorithm. Wg = Wm = We = Wd = weight_scale; Wgb = Wmb = Web = Wdb = weight_scale; } Wg /= weight_scale; Loading @@ -2048,6 +2049,7 @@ compute_weighted_bandwidths(const smartlist_t *sl, bandwidths = tor_calloc(smartlist_len(sl), sizeof(u64_dbl_t)); // Cycle through smartlist and total the bandwidth. static int warned_missing_bw = 0; SMARTLIST_FOREACH_BEGIN(sl, const node_t *, node) { int is_exit = 0, is_guard = 0, is_dir = 0, this_bw = 0; double weight = 1; Loading @@ -2056,15 +2058,18 @@ compute_weighted_bandwidths(const smartlist_t *sl, is_dir = node_is_dir(node); if (node->rs) { if (!node->rs->has_bandwidth) { tor_free(bandwidths); /* This should never happen, unless all the authorites downgrade * to 0.2.0 or rogue routerstatuses get inserted into our consensus. */ if (! warned_missing_bw) { log_warn(LD_BUG, "Consensus is not listing bandwidths. Defaulting back to " "old router selection algorithm."); return -1; "Consensus is missing some bandwidths. Using a naive " "router selection algorithm"); warned_missing_bw = 1; } this_bw = 30000; /* Chosen arbitrarily */ } else { this_bw = kb_to_bytes(node->rs->bandwidth_kb); } } else if (node->ri) { /* bridge or other descriptor not in our consensus */ this_bw = bridge_get_advertised_bandwidth_bounded(node->ri); Loading Loading @@ -2141,226 +2146,13 @@ frac_nodes_with_descriptors(const smartlist_t *sl, return present / total; } /** Helper function: * choose a random node_t element of smartlist <b>sl</b>, weighted by * the advertised bandwidth of each element. * * If <b>rule</b>==WEIGHT_FOR_EXIT. we're picking an exit node: consider all * nodes' bandwidth equally regardless of their Exit status, since there may * be some in the list because they exit to obscure ports. If * <b>rule</b>==NO_WEIGHTING, we're picking a non-exit node: weight * exit-node's bandwidth less depending on the smallness of the fraction of * Exit-to-total bandwidth. If <b>rule</b>==WEIGHT_FOR_GUARD, we're picking a * guard node: consider all guard's bandwidth equally. Otherwise, weight * guards proportionally less. */ static const node_t * smartlist_choose_node_by_bandwidth(const smartlist_t *sl, bandwidth_weight_rule_t rule) { unsigned int i; u64_dbl_t *bandwidths; int is_exit; int is_guard; int is_fast; double total_nonexit_bw = 0, total_exit_bw = 0; double total_nonguard_bw = 0, total_guard_bw = 0; double exit_weight; double guard_weight; int n_unknown = 0; bitarray_t *fast_bits; bitarray_t *exit_bits; bitarray_t *guard_bits; // This function does not support WEIGHT_FOR_DIR // or WEIGHT_FOR_MID if (rule == WEIGHT_FOR_DIR || rule == WEIGHT_FOR_MID) { rule = NO_WEIGHTING; } /* Can't choose exit and guard at same time */ tor_assert(rule == NO_WEIGHTING || rule == WEIGHT_FOR_EXIT || rule == WEIGHT_FOR_GUARD); if (smartlist_len(sl) == 0) { log_info(LD_CIRC, "Empty routerlist passed in to old node selection for rule %s", bandwidth_weight_rule_to_string(rule)); return NULL; } /* First count the total bandwidth weight, and make a list * of each value. We use UINT64_MAX to indicate "unknown". */ bandwidths = tor_calloc(smartlist_len(sl), sizeof(u64_dbl_t)); fast_bits = bitarray_init_zero(smartlist_len(sl)); exit_bits = bitarray_init_zero(smartlist_len(sl)); guard_bits = bitarray_init_zero(smartlist_len(sl)); /* Iterate over all the routerinfo_t or routerstatus_t, and */ SMARTLIST_FOREACH_BEGIN(sl, const node_t *, node) { /* first, learn what bandwidth we think i has */ int is_known = 1; uint32_t this_bw = 0; i = node_sl_idx; is_exit = node_is_good_exit(node); is_guard = node->is_possible_guard; if (node->rs) { if (node->rs->has_bandwidth) { this_bw = kb_to_bytes(node->rs->bandwidth_kb); } else { /* guess */ is_known = 0; } } else if (node->ri) { /* Must be a bridge if we're willing to use it */ this_bw = bridge_get_advertised_bandwidth_bounded(node->ri); } if (is_exit) bitarray_set(exit_bits, i); if (is_guard) bitarray_set(guard_bits, i); if (node->is_fast) bitarray_set(fast_bits, i); if (is_known) { bandwidths[i].dbl = this_bw; if (is_guard) total_guard_bw += this_bw; else total_nonguard_bw += this_bw; if (is_exit) total_exit_bw += this_bw; else total_nonexit_bw += this_bw; } else { ++n_unknown; bandwidths[i].dbl = -1.0; } } SMARTLIST_FOREACH_END(node); #define EPSILON .1 /* Now, fill in the unknown values. */ if (n_unknown) { int32_t avg_fast, avg_slow; if (total_exit_bw+total_nonexit_bw < EPSILON) { /* if there's some bandwidth, there's at least one known router, * so no worries about div by 0 here */ int n_known = smartlist_len(sl)-n_unknown; avg_fast = avg_slow = (int32_t) ((total_exit_bw+total_nonexit_bw)/((uint64_t) n_known)); } else { avg_fast = 40000; avg_slow = 20000; } for (i=0; i<(unsigned)smartlist_len(sl); ++i) { if (bandwidths[i].dbl >= 0.0) continue; is_fast = bitarray_is_set(fast_bits, i); is_exit = bitarray_is_set(exit_bits, i); is_guard = bitarray_is_set(guard_bits, i); bandwidths[i].dbl = is_fast ? avg_fast : avg_slow; if (is_exit) total_exit_bw += bandwidths[i].dbl; else total_nonexit_bw += bandwidths[i].dbl; if (is_guard) total_guard_bw += bandwidths[i].dbl; else total_nonguard_bw += bandwidths[i].dbl; } } /* If there's no bandwidth at all, pick at random. */ if (total_exit_bw+total_nonexit_bw < EPSILON) { tor_free(bandwidths); tor_free(fast_bits); tor_free(exit_bits); tor_free(guard_bits); return smartlist_choose(sl); } /* Figure out how to weight exits and guards */ { double all_bw = U64_TO_DBL(total_exit_bw+total_nonexit_bw); double exit_bw = U64_TO_DBL(total_exit_bw); double guard_bw = U64_TO_DBL(total_guard_bw); /* * For detailed derivation of this formula, see * http://archives.seul.org/or/dev/Jul-2007/msg00056.html */ if (rule == WEIGHT_FOR_EXIT || total_exit_bw<EPSILON) exit_weight = 1.0; else exit_weight = 1.0 - all_bw/(3.0*exit_bw); if (rule == WEIGHT_FOR_GUARD || total_guard_bw<EPSILON) guard_weight = 1.0; else guard_weight = 1.0 - all_bw/(3.0*guard_bw); if (exit_weight <= 0.0) exit_weight = 0.0; if (guard_weight <= 0.0) guard_weight = 0.0; for (i=0; i < (unsigned)smartlist_len(sl); i++) { tor_assert(bandwidths[i].dbl >= 0.0); is_exit = bitarray_is_set(exit_bits, i); is_guard = bitarray_is_set(guard_bits, i); if (is_exit && is_guard) bandwidths[i].dbl *= exit_weight * guard_weight; else if (is_guard) bandwidths[i].dbl *= guard_weight; else if (is_exit) bandwidths[i].dbl *= exit_weight; } } #if 0 log_debug(LD_CIRC, "Total weighted bw = "U64_FORMAT ", exit bw = "U64_FORMAT ", nonexit bw = "U64_FORMAT", exit weight = %f " "(for exit == %d)" ", guard bw = "U64_FORMAT ", nonguard bw = "U64_FORMAT", guard weight = %f " "(for guard == %d)", U64_PRINTF_ARG(total_bw), U64_PRINTF_ARG(total_exit_bw), U64_PRINTF_ARG(total_nonexit_bw), exit_weight, (int)(rule == WEIGHT_FOR_EXIT), U64_PRINTF_ARG(total_guard_bw), U64_PRINTF_ARG(total_nonguard_bw), guard_weight, (int)(rule == WEIGHT_FOR_GUARD)); #endif scale_array_elements_to_u64(bandwidths, smartlist_len(sl), NULL); { int idx = choose_array_element_by_weight(bandwidths, smartlist_len(sl)); tor_free(bandwidths); tor_free(fast_bits); tor_free(exit_bits); tor_free(guard_bits); return idx < 0 ? NULL : smartlist_get(sl, idx); } } /** Choose a random element of status list <b>sl</b>, weighted by * the advertised bandwidth of each node */ const node_t * node_sl_choose_by_bandwidth(const smartlist_t *sl, bandwidth_weight_rule_t rule) { /*XXXX MOVE */ const node_t *ret; if ((ret = smartlist_choose_node_by_bandwidth_weights(sl, rule))) { return ret; } else { return smartlist_choose_node_by_bandwidth(sl, rule); } return smartlist_choose_node_by_bandwidth_weights(sl, rule); } /** Return a random running node from the nodelist. Never Loading