📄 routerlist.c
字号:
int32_t avg_fast, avg_slow;
if (total_exit_bw+total_nonexit_bw) {
/* 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) {
int32_t bw = bandwidths[i];
if (bw>=0)
continue;
is_exit = ((-bw)&2);
is_guard = ((-bw)&4);
bandwidths[i] = ((-bw)&1) ? avg_fast : avg_slow;
if (is_exit)
total_exit_bw += bandwidths[i];
else
total_nonexit_bw += bandwidths[i];
if (is_guard)
total_guard_bw += bandwidths[i];
else
total_nonguard_bw += bandwidths[i];
}
}
/* If there's no bandwidth at all, pick at random. */
if (!(total_exit_bw+total_nonexit_bw)) {
tor_free(bandwidths);
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)
exit_weight = 1.0;
else
exit_weight = 1.0 - all_bw/(3.0*exit_bw);
if (rule == WEIGHT_FOR_GUARD)
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;
total_bw = 0;
for (i=0; i < (unsigned)smartlist_len(sl); i++) {
is_exit = bitarray_is_set(exit_bits, i);
is_guard = bitarray_is_set(guard_bits, i);
if (is_exit && is_guard)
total_bw += ((uint64_t)(bandwidths[i] * exit_weight * guard_weight));
else if (is_guard)
total_bw += ((uint64_t)(bandwidths[i] * guard_weight));
else if (is_exit)
total_bw += ((uint64_t)(bandwidths[i] * exit_weight));
else
total_bw += bandwidths[i];
}
}
log_debug(LD_CIRC, "Total weighted bw = "U64_FORMAT
", exit bw = "U64_FORMAT
", nonexit bw = "U64_FORMAT", exit weight = %lf "
"(for exit == %d)"
", guard bw = "U64_FORMAT
", nonguard bw = "U64_FORMAT", guard weight = %lf "
"(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));
/* Almost done: choose a random value from the bandwidth weights. */
rand_bw = crypto_rand_uint64(total_bw);
/* Last, count through sl until we get to the element we picked */
tmp = 0;
for (i=0; i < (unsigned)smartlist_len(sl); i++) {
is_exit = bitarray_is_set(exit_bits, i);
is_guard = bitarray_is_set(guard_bits, i);
/* Weights can be 0 if not counting guards/exits */
if (is_exit && is_guard)
tmp += ((uint64_t)(bandwidths[i] * exit_weight * guard_weight));
else if (is_guard)
tmp += ((uint64_t)(bandwidths[i] * guard_weight));
else if (is_exit)
tmp += ((uint64_t)(bandwidths[i] * exit_weight));
else
tmp += bandwidths[i];
if (tmp >= rand_bw)
break;
}
if (i == (unsigned)smartlist_len(sl)) {
/* This was once possible due to round-off error, but shouldn't be able
* to occur any longer. */
tor_fragile_assert();
--i;
log_warn(LD_BUG, "Round-off error in computing bandwidth had an effect on "
" which router we chose. Please tell the developers. "
U64_FORMAT " " U64_FORMAT " " U64_FORMAT, U64_PRINTF_ARG(tmp),
U64_PRINTF_ARG(rand_bw), U64_PRINTF_ARG(total_bw));
}
tor_free(bandwidths);
tor_free(exit_bits);
tor_free(guard_bits);
return smartlist_get(sl, i);
}
/** Choose a random element of router list <b>sl</b>, weighted by
* the advertised bandwidth of each router.
*/
routerinfo_t *
routerlist_sl_choose_by_bandwidth(smartlist_t *sl,
bandwidth_weight_rule_t rule)
{
return smartlist_choose_by_bandwidth(sl, rule, 0);
}
/** Choose a random element of status list <b>sl</b>, weighted by
* the advertised bandwidth of each status.
*/
routerstatus_t *
routerstatus_sl_choose_by_bandwidth(smartlist_t *sl)
{
/* We are choosing neither exit nor guard here. Weight accordingly. */
return smartlist_choose_by_bandwidth(sl, NO_WEIGHTING, 1);
}
/** Return a random running router from the routerlist. If any node
* named in <b>preferred</b> is available, pick one of those. Never
* pick a node named in <b>excluded</b>, or whose routerinfo is in
* <b>excludedsmartlist</b>, even if they are the only nodes
* available. If <b>strict</b> is true, never pick any node besides
* those in <b>preferred</b>.
* If <b>need_uptime</b> is non-zero and any router has more than
* a minimum uptime, return one of those.
* If <b>need_capacity</b> is non-zero, weight your choice by the
* advertised capacity of each router.
* If ! <b>allow_invalid</b>, consider only Valid routers.
* If <b>need_guard</b>, consider only Guard routers.
* If <b>weight_for_exit</b>, we weight bandwidths as if picking an exit node,
* otherwise we weight bandwidths for picking a relay node (that is, possibly
* discounting exit nodes).
*/
routerinfo_t *
router_choose_random_node(const char *preferred,
const char *excluded,
smartlist_t *excludedsmartlist,
int need_uptime, int need_capacity,
int need_guard,
int allow_invalid, int strict,
int weight_for_exit)
{
smartlist_t *sl, *excludednodes;
routerinfo_t *choice = NULL, *r;
bandwidth_weight_rule_t rule;
tor_assert(!(weight_for_exit && need_guard));
rule = weight_for_exit ? WEIGHT_FOR_EXIT :
(need_guard ? WEIGHT_FOR_GUARD : NO_WEIGHTING);
excludednodes = smartlist_create();
add_nickname_list_to_smartlist(excludednodes,excluded,0);
if ((r = routerlist_find_my_routerinfo())) {
smartlist_add(excludednodes, r);
routerlist_add_family(excludednodes, r);
}
/* Try the preferred nodes first. Ignore need_uptime and need_capacity
* and need_guard, since the user explicitly asked for these nodes. */
if (preferred) {
sl = smartlist_create();
add_nickname_list_to_smartlist(sl,preferred,1);
smartlist_subtract(sl,excludednodes);
if (excludedsmartlist)
smartlist_subtract(sl,excludedsmartlist);
choice = smartlist_choose(sl);
smartlist_free(sl);
}
if (!choice && !strict) {
/* Then give up on our preferred choices: any node
* will do that has the required attributes. */
sl = smartlist_create();
router_add_running_routers_to_smartlist(sl, allow_invalid,
need_uptime, need_capacity,
need_guard);
smartlist_subtract(sl,excludednodes);
if (excludedsmartlist)
smartlist_subtract(sl,excludedsmartlist);
if (need_capacity || need_guard)
choice = routerlist_sl_choose_by_bandwidth(sl, rule);
else
choice = smartlist_choose(sl);
smartlist_free(sl);
if (!choice && (need_uptime || need_capacity || need_guard)) {
/* try once more -- recurse but with fewer restrictions. */
log_info(LD_CIRC,
"We couldn't find any live%s%s%s routers; falling back "
"to list of all routers.",
need_capacity?", fast":"",
need_uptime?", stable":"",
need_guard?", guard":"");
choice = router_choose_random_node(
NULL, excluded, excludedsmartlist,
0, 0, 0, allow_invalid, 0, weight_for_exit);
}
}
smartlist_free(excludednodes);
if (!choice) {
if (strict) {
log_warn(LD_CIRC, "All preferred nodes were down when trying to choose "
"node, and the Strict[...]Nodes option is set. Failing.");
} else {
log_warn(LD_CIRC,
"No available nodes when trying to choose node. Failing.");
}
}
return choice;
}
/** Return true iff the digest of <b>router</b>'s identity key,
* encoded in hexadecimal, matches <b>hexdigest</b> (which is
* optionally prefixed with a single dollar sign). Return false if
* <b>hexdigest</b> is malformed, or it doesn't match. */
static INLINE int
router_hex_digest_matches(routerinfo_t *router, const char *hexdigest)
{
char digest[DIGEST_LEN];
size_t len;
tor_assert(hexdigest);
if (hexdigest[0] == '$')
++hexdigest;
len = strlen(hexdigest);
if (len < HEX_DIGEST_LEN)
return 0;
else if (len > HEX_DIGEST_LEN &&
(hexdigest[HEX_DIGEST_LEN] == '=' ||
hexdigest[HEX_DIGEST_LEN] == '~')) {
if (strcasecmp(hexdigest+HEX_DIGEST_LEN+1, router->nickname))
return 0;
if (hexdigest[HEX_DIGEST_LEN] == '=' && !router->is_named)
return 0;
}
if (base16_decode(digest, DIGEST_LEN, hexdigest, HEX_DIGEST_LEN)<0)
return 0;
return (!memcmp(digest, router->cache_info.identity_digest, DIGEST_LEN));
}
/** Return true if <b>router</b>'s nickname matches <b>nickname</b>
* (case-insensitive), or if <b>router's</b> identity key digest
* matches a hexadecimal value stored in <b>nickname</b>. Return
* false otherwise. */
static int
router_nickname_matches(routerinfo_t *router, const char *nickname)
{
if (nickname[0]!='$' && !strcasecmp(router->nickname, nickname))
return 1;
return router_hex_digest_matches(router, nickname);
}
/** Return the router in our routerlist whose (case-insensitive)
* nickname or (case-sensitive) hexadecimal key digest is
* <b>nickname</b>. Return NULL if no such router is known.
*/
routerinfo_t *
router_get_by_nickname(const char *nickname, int warn_if_unnamed)
{
int maybedigest;
char digest[DIGEST_LEN];
routerinfo_t *best_match=NULL;
int n_matches = 0;
const char *named_digest = NULL;
tor_assert(nickname);
if (!routerlist)
return NULL;
if (nickname[0] == '$')
return router_get_by_hexdigest(nickname);
if (!strcasecmp(nickname, UNNAMED_ROUTER_NICKNAME))
return NULL;
if (server_mode(get_options()) &&
!strcasecmp(nickname, get_options()->Nickname))
return router_get_my_routerinfo();
maybedigest = (strlen(nickname) >= HEX_DIGEST_LEN) &&
(base16_decode(digest,DIGEST_LEN,nickname,HEX_DIGEST_LEN) == 0);
if ((named_digest = networkstatus_get_router_digest_by_nickname(nickname))) {
return rimap_get(routerlist->identity_map, named_digest);
}
if (networkstatus_nickname_is_unnamed(nickname))
return NULL;
/* If we reach this point, there's no canonical value for the nickname. */
SMARTLIST_FOREACH(routerlist->routers, routerinfo_t *, router,
{
if (!strcasecmp(router->nickname, nickname)) {
++n_matches;
if (n_matches <= 1 || router->is_running)
best_match = router;
} else if (maybedigest &&
!memcmp(digest, router->cache_info.identity_digest, DIGEST_LEN)
) {
if (router_hex_digest_matches(router, nickname))
return router;
/* If we reach this point, we have a ID=name syntax that matches the
* identity but not the name. That isn't an acceptable match. */
}
});
if (best_match) {
if (warn_if_unnamed && n_matches > 1) {
smartlist_t *fps = smartlist_create();
int any_unwarned = 0;
SMARTLIST_FOREACH(routerlist->routers, routerinfo_t *, router,
{
routerstatus_t *rs;
char *desc;
size_t dlen;
char fp[HEX_DIGEST_LEN+1];
if (strcasecmp(router->nickname, nickname))
continue;
rs = router_get_consensus_status_by_id(
router->cache_info.identity_digest);
if (rs && !rs->name_lookup_warned) {
rs->name_lookup_warned = 1;
any_unwarned = 1;
}
base16_encode(fp, sizeof(fp),
router->cache_info.identity_digest, DIGEST_LEN);
dlen = 32 + HEX_DIGEST_LEN + strlen(router->address);
desc = tor_malloc(dlen);
tor_snprintf(desc, dlen, "\"$%s\" for the one at %s:%d",
fp, router->address, router->or_port);
smartlist_add(fps, desc);
});
if (any_unwarned) {
char *alternatives = smartlist_join_strings(fps, "; ",0,NULL);
log_warn(LD_CONFIG,
"There are multiple matches for the nickname \"%s\","
" but none is listed as named by the directory authorities. "
"Choosing one arbitrarily. If you meant one in particular, "
"you should say %s.", nickname, alternatives);
tor_free(alternatives);
}
SMARTLIST_FOREACH(fps, char *, cp, tor_free(cp));
smartlist_free(fps);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -