#define VOTEFLAGS_PRIVATE
#include "core/or/or.h"
#include "feature/dirauth/voteflags.h"
#include "app/config/config.h"
#include "core/mainloop/mainloop.h"
#include "core/or/policies.h"
#include "feature/dirauth/bwauth.h"
#include "feature/dirauth/reachability.h"
#include "feature/dirauth/dirauth_sys.h"
#include "feature/hibernate/hibernate.h"
#include "feature/nodelist/dirlist.h"
#include "feature/nodelist/networkstatus.h"
#include "feature/nodelist/nodelist.h"
#include "feature/nodelist/routerlist.h"
#include "feature/nodelist/routerset.h"
#include "feature/relay/router.h"
#include "feature/stats/rephist.h"
#include "feature/dirauth/dirauth_options_st.h"
#include "feature/nodelist/node_st.h"
#include "feature/nodelist/routerinfo_st.h"
#include "feature/nodelist/routerlist_st.h"
#include "feature/nodelist/vote_routerstatus_st.h"
#include "lib/container/order.h"
#define UPTIME_TO_GUARANTEE_STABLE (3600*24*30)
#define MTBF_TO_GUARANTEE_STABLE (60*60*24*5)
#define TIME_KNOWN_TO_GUARANTEE_FAMILIAR (8*24*60*60)
#define WFU_TO_GUARANTEE_GUARD (0.98)
static uint32_t stable_uptime = 0;
static double stable_mtbf = 0.0;
static int enough_mtbf_info = 0;
static double guard_wfu = 0.0;
static long guard_tk = 0;
static uint32_t fast_bandwidth_kb = 0;
static uint32_t guard_bandwidth_including_exits_kb = 0;
static uint32_t guard_bandwidth_excluding_exits_kb = 0;
static inline long
real_uptime(const routerinfo_t *router, time_t now)
{
if (now < router->cache_info.published_on)
return router->uptime;
else
return router->uptime + (now - router->cache_info.published_on);
}
static int
dirserv_thinks_router_is_unreliable(time_t now,
const routerinfo_t *router,
int need_uptime, int need_capacity)
{
if (need_uptime) {
if (!enough_mtbf_info) {
long uptime = real_uptime(router, now);
if ((unsigned)uptime < stable_uptime &&
(unsigned)uptime < UPTIME_TO_GUARANTEE_STABLE)
return 1;
} else {
double mtbf =
rep_hist_get_stability(router->cache_info.identity_digest, now);
if (mtbf < stable_mtbf &&
mtbf < MTBF_TO_GUARANTEE_STABLE)
return 1;
}
}
if (need_capacity) {
uint32_t bw_kb = dirserv_get_credible_bandwidth_kb(router);
if (bw_kb < fast_bandwidth_kb)
return 1;
}
return 0;
}
static int
router_is_active(const routerinfo_t *ri, const node_t *node, time_t now)
{
time_t cutoff = now - ROUTER_MAX_AGE_TO_PUBLISH;
if (ri->cache_info.published_on < cutoff) {
return 0;
}
if (!node->is_running || !node->is_valid || ri->is_hibernating) {
return 0;
}
if (!ri->bandwidthcapacity) {
if (get_options()->TestingTorNetwork) {
if (dirauth_get_options()->TestingMinExitFlagThreshold > 0) {
return 0;
}
} else {
return 0;
}
}
return 1;
}
static int
dirserv_thinks_router_is_hs_dir(const routerinfo_t *router,
const node_t *node, time_t now)
{
long uptime;
if (get_uptime() >
dirauth_get_options()->MinUptimeHidServDirectoryV2 * 1.1)
uptime = MIN(rep_hist_get_uptime(router->cache_info.identity_digest, now),
real_uptime(router, now));
else
uptime = real_uptime(router, now);
return (router->wants_to_be_hs_dir &&
router->supports_tunnelled_dir_requests &&
node->is_stable && node->is_fast &&
uptime >= dirauth_get_options()->MinUptimeHidServDirectoryV2 &&
router_is_active(router, node, now));
}
#define ABSOLUTE_MIN_BW_VALUE_TO_CONSIDER_KB 4
static int
router_counts_toward_thresholds(const node_t *node, time_t now,
const digestmap_t *omit_as_sybil,
int require_mbw)
{
int have_mbw =
dirserv_has_measured_bw(node->identity);
uint64_t min_bw_kb = ABSOLUTE_MIN_BW_VALUE_TO_CONSIDER_KB;
const or_options_t *options = get_options();
const dirauth_options_t *dirauth_options = dirauth_get_options();
if (options->TestingTorNetwork) {
min_bw_kb = (int64_t)dirauth_options->TestingMinExitFlagThreshold / 1000;
}
return node->ri && router_is_active(node->ri, node, now) &&
!digestmap_get(omit_as_sybil, node->identity) &&
(dirserv_get_credible_bandwidth_kb(node->ri) >= min_bw_kb) &&
(have_mbw || !require_mbw);
}
void
dirserv_compute_performance_thresholds(digestmap_t *omit_as_sybil)
{
int n_active, n_active_nonexit, n_familiar;
uint32_t *uptimes, *bandwidths_kb, *bandwidths_excluding_exits_kb;
long *tks;
double *mtbfs, *wfus;
const smartlist_t *nodelist;
time_t now = time(NULL);
const or_options_t *options = get_options();
const dirauth_options_t *dirauth_options = dirauth_get_options();
int require_mbw =
(dirserv_get_last_n_measured_bws() >
dirauth_options->MinMeasuredBWsForAuthToIgnoreAdvertised) ? 1 : 0;
stable_uptime = 0;
stable_mtbf = 0;
fast_bandwidth_kb = 0;
guard_bandwidth_including_exits_kb = 0;
guard_bandwidth_excluding_exits_kb = 0;
guard_tk = 0;
guard_wfu = 0;
nodelist_assert_ok();
nodelist = nodelist_get_list();
n_active = n_active_nonexit = 0;
uptimes = tor_calloc(smartlist_len(nodelist), sizeof(uint32_t));
bandwidths_kb = tor_calloc(smartlist_len(nodelist), sizeof(uint32_t));
bandwidths_excluding_exits_kb =
tor_calloc(smartlist_len(nodelist), sizeof(uint32_t));
mtbfs = tor_calloc(smartlist_len(nodelist), sizeof(double));
tks = tor_calloc(smartlist_len(nodelist), sizeof(long));
wfus = tor_calloc(smartlist_len(nodelist), sizeof(double));
SMARTLIST_FOREACH_BEGIN(nodelist, node_t *, node) {
if (options->BridgeAuthoritativeDir &&
node->ri &&
node->ri->purpose != ROUTER_PURPOSE_BRIDGE)
continue;
routerinfo_t *ri = node->ri;
if (ri) {
node->is_exit = (!router_exit_policy_rejects_all(ri) &&
exit_policy_is_general_exit(ri->exit_policy));
}
if (router_counts_toward_thresholds(node, now, omit_as_sybil,
require_mbw)) {
const char *id = node->identity;
uint32_t bw_kb;
tor_assert(ri);
uptimes[n_active] = (uint32_t)real_uptime(ri, now);
mtbfs[n_active] = rep_hist_get_stability(id, now);
tks [n_active] = rep_hist_get_weighted_time_known(id, now);
bandwidths_kb[n_active] = bw_kb = dirserv_get_credible_bandwidth_kb(ri);
if (!node->is_exit || node->is_bad_exit) {
bandwidths_excluding_exits_kb[n_active_nonexit] = bw_kb;
++n_active_nonexit;
}
++n_active;
}
} SMARTLIST_FOREACH_END(node);
if (n_active) {
stable_uptime = median_uint32(uptimes, n_active);
stable_mtbf = median_double(mtbfs, n_active);
fast_bandwidth_kb = find_nth_uint32(bandwidths_kb, n_active, n_active/8);
if (fast_bandwidth_kb < RELAY_REQUIRED_MIN_BANDWIDTH/(2 * 1000))
fast_bandwidth_kb = bandwidths_kb[n_active/4];
guard_bandwidth_including_exits_kb =
third_quartile_uint32(bandwidths_kb, n_active);
guard_tk = find_nth_long(tks, n_active, n_active/8);
}
if (guard_tk > TIME_KNOWN_TO_GUARANTEE_FAMILIAR)
guard_tk = TIME_KNOWN_TO_GUARANTEE_FAMILIAR;
{
#define ABSOLUTE_MIN_VALUE_FOR_FAST_FLAG 4
int32_t min_fast_kb, max_fast_kb, min_fast, max_fast;
min_fast = networkstatus_get_param(NULL, "FastFlagMinThreshold",
ABSOLUTE_MIN_VALUE_FOR_FAST_FLAG,
ABSOLUTE_MIN_VALUE_FOR_FAST_FLAG,
INT32_MAX);
if (options->TestingTorNetwork) {
min_fast = (int32_t)dirauth_options->TestingMinFastFlagThreshold;
}
max_fast = networkstatus_get_param(NULL, "FastFlagMaxThreshold",
INT32_MAX, min_fast, INT32_MAX);
min_fast_kb = min_fast / 1000;
max_fast_kb = max_fast / 1000;
if (fast_bandwidth_kb < (uint32_t)min_fast_kb)
fast_bandwidth_kb = min_fast_kb;
if (fast_bandwidth_kb > (uint32_t)max_fast_kb)
fast_bandwidth_kb = max_fast_kb;
}
{
const uint64_t fast_opt = dirauth_get_options()->AuthDirFastGuarantee;
if (fast_opt && fast_bandwidth_kb > fast_opt / 1000)
fast_bandwidth_kb = (uint32_t)(fast_opt / 1000);
}
n_familiar = 0;
SMARTLIST_FOREACH_BEGIN(nodelist, node_t *, node) {
if (router_counts_toward_thresholds(node, now,
omit_as_sybil, require_mbw)) {
routerinfo_t *ri = node->ri;
const char *id = ri->cache_info.identity_digest;
long tk = rep_hist_get_weighted_time_known(id, now);
if (tk < guard_tk)
continue;
wfus[n_familiar++] = rep_hist_get_weighted_fractional_uptime(id, now);
}
} SMARTLIST_FOREACH_END(node);
if (n_familiar)
guard_wfu = median_double(wfus, n_familiar);
if (guard_wfu > WFU_TO_GUARANTEE_GUARD)
guard_wfu = WFU_TO_GUARANTEE_GUARD;
enough_mtbf_info = rep_hist_have_measured_enough_stability();
if (n_active_nonexit) {
guard_bandwidth_excluding_exits_kb =
find_nth_uint32(bandwidths_excluding_exits_kb,
n_active_nonexit, n_active_nonexit*3/4);
}
log_info(LD_DIRSERV,
"Cutoffs: For Stable, %lu sec uptime, %lu sec MTBF. "
"For Fast: %lu kilobytes/sec. "
"For Guard: WFU %.03f%%, time-known %lu sec, "
"and bandwidth %lu or %lu kilobytes/sec. "
"We%s have enough stability data.",
(unsigned long)stable_uptime,
(unsigned long)stable_mtbf,
(unsigned long)fast_bandwidth_kb,
guard_wfu*100,
(unsigned long)guard_tk,
(unsigned long)guard_bandwidth_including_exits_kb,
(unsigned long)guard_bandwidth_excluding_exits_kb,
enough_mtbf_info ? "" : " don't");
tor_free(uptimes);
tor_free(mtbfs);
tor_free(bandwidths_kb);
tor_free(bandwidths_excluding_exits_kb);
tor_free(tks);
tor_free(wfus);
}
void
dirserv_compute_bridge_flag_thresholds(void)
{
digestmap_t *omit_as_sybil = digestmap_new();
dirserv_compute_performance_thresholds(omit_as_sybil);
digestmap_free(omit_as_sybil, NULL);
}
char *
dirserv_get_flag_thresholds_line(void)
{
char *result=NULL;
const int measured_threshold =
dirauth_get_options()->MinMeasuredBWsForAuthToIgnoreAdvertised;
const int enough_measured_bw =
dirserv_get_last_n_measured_bws() > measured_threshold;
tor_asprintf(&result,
"stable-uptime=%lu stable-mtbf=%lu "
"fast-speed=%lu "
"guard-wfu=%.03f%% guard-tk=%lu "
"guard-bw-inc-exits=%lu guard-bw-exc-exits=%lu "
"enough-mtbf=%d ignoring-advertised-bws=%d",
(unsigned long)stable_uptime,
(unsigned long)stable_mtbf,
(unsigned long)fast_bandwidth_kb*1000,
guard_wfu*100,
(unsigned long)guard_tk,
(unsigned long)guard_bandwidth_including_exits_kb*1000,
(unsigned long)guard_bandwidth_excluding_exits_kb*1000,
enough_mtbf_info ? 1 : 0,
enough_measured_bw ? 1 : 0);
return result;
}
int
running_long_enough_to_decide_unreachable(void)
{
const dirauth_options_t *opts = dirauth_get_options();
return time_of_process_start +
opts->TestingAuthDirTimeToLearnReachability < approx_time();
}
#define REACHABLE_TIMEOUT (45*60)
#define HIBERNATION_PUBLICATION_SKEW (60*60)
void
dirserv_set_router_is_running(routerinfo_t *router, time_t now)
{
int answer;
const or_options_t *options = get_options();
const dirauth_options_t *dirauth_options = dirauth_get_options();
node_t *node = node_get_mutable_by_id(router->cache_info.identity_digest);
tor_assert(node);
if (router_is_me(router)) {
answer = ! we_are_hibernating();
} else if (router->is_hibernating &&
(router->cache_info.published_on +
HIBERNATION_PUBLICATION_SKEW) > node->last_reachable) {
answer = 0;
} else if (options->AssumeReachable) {
answer = 1;
} else {
answer = (now < node->last_reachable + REACHABLE_TIMEOUT &&
(dirauth_options->AuthDirHasIPv6Connectivity != 1 ||
tor_addr_is_null(&router->ipv6_addr) ||
now < node->last_reachable6 + REACHABLE_TIMEOUT));
}
if (!answer && running_long_enough_to_decide_unreachable()) {
time_t when = now;
if (node->last_reachable &&
node->last_reachable + REACHABILITY_TEST_CYCLE_PERIOD < now)
when = node->last_reachable + REACHABILITY_TEST_CYCLE_PERIOD;
rep_hist_note_router_unreachable(router->cache_info.identity_digest, when);
}
node->is_running = answer;
}
static int
should_publish_node_ipv6(const node_t *node, const routerinfo_t *ri,
time_t now)
{
const dirauth_options_t *options = dirauth_get_options();
return options->AuthDirHasIPv6Connectivity == 1 &&
!tor_addr_is_null(&ri->ipv6_addr) &&
((node->last_reachable6 >= now - REACHABLE_TIMEOUT) ||
router_is_me(ri));
}
void
dirauth_set_routerstatus_from_routerinfo(routerstatus_t *rs,
node_t *node,
const routerinfo_t *ri,
time_t now,
int listbadexits)
{
const or_options_t *options = get_options();
uint32_t routerbw_kb = dirserv_get_credible_bandwidth_kb(ri);
node->is_stable = !dirserv_thinks_router_is_unreliable(now, ri, 1, 0);
node->is_fast = !dirserv_thinks_router_is_unreliable(now, ri, 0, 1);
node->is_hs_dir = dirserv_thinks_router_is_hs_dir(ri, node, now);
set_routerstatus_from_routerinfo(rs, node, ri);
const uint64_t bw_opt = dirauth_get_options()->AuthDirGuardBWGuarantee;
if (node->is_fast && node->is_stable &&
ri->supports_tunnelled_dir_requests &&
((bw_opt && routerbw_kb >= bw_opt / 1000) ||
routerbw_kb >= MIN(guard_bandwidth_including_exits_kb,
guard_bandwidth_excluding_exits_kb))) {
long tk = rep_hist_get_weighted_time_known(
node->identity, now);
double wfu = rep_hist_get_weighted_fractional_uptime(
node->identity, now);
rs->is_possible_guard = (wfu >= guard_wfu && tk >= guard_tk) ? 1 : 0;
} else {
rs->is_possible_guard = 0;
}
rs->is_bad_exit = listbadexits && node->is_bad_exit;
rs->is_staledesc =
(ri->cache_info.published_on + DESC_IS_STALE_INTERVAL) < now;
if (! should_publish_node_ipv6(node, ri, now)) {
tor_addr_make_null(&rs->ipv6_addr, AF_INET6);
rs->ipv6_orport = 0;
}
if (options->TestingTorNetwork) {
dirserv_set_routerstatus_testing(rs);
}
}
STATIC void
dirserv_set_routerstatus_testing(routerstatus_t *rs)
{
const dirauth_options_t *options = dirauth_get_options();
tor_assert(get_options()->TestingTorNetwork);
if (routerset_contains_routerstatus(options->TestingDirAuthVoteExit,
rs, 0)) {
rs->is_exit = 1;
} else if (options->TestingDirAuthVoteExitIsStrict) {
rs->is_exit = 0;
}
if (routerset_contains_routerstatus(options->TestingDirAuthVoteGuard,
rs, 0)) {
rs->is_possible_guard = 1;
} else if (options->TestingDirAuthVoteGuardIsStrict) {
rs->is_possible_guard = 0;
}
if (routerset_contains_routerstatus(options->TestingDirAuthVoteHSDir,
rs, 0)) {
rs->is_hs_dir = 1;
} else if (options->TestingDirAuthVoteHSDirIsStrict) {
rs->is_hs_dir = 0;
}
}
void
dirserv_set_bridges_running(time_t now)
{
routerlist_t *rl = router_get_routerlist();
SMARTLIST_FOREACH_BEGIN(rl->routers, routerinfo_t *, ri) {
if (ri->purpose == ROUTER_PURPOSE_BRIDGE)
dirserv_set_router_is_running(ri, now);
} SMARTLIST_FOREACH_END(ri);
}