mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-24 04:13:28 +01:00
Merge branch 'dormant_v2_squashed'
This commit is contained in:
commit
7d8e0cc9ab
@ -1790,6 +1790,19 @@ The following options are useful only for clients (that is, if
|
||||
Try this many simultaneous connections to download a consensus before
|
||||
waiting for one to complete, timeout, or error out. (Default: 3)
|
||||
|
||||
[[DormantClientTimeout]] **DormantClientTimeout** __N__ **minutes**|**hours**|**days**|**weeks**::
|
||||
If Tor spends this much time without any client activity,
|
||||
enter a dormant state where automatic circuits are not built, and
|
||||
directory information is not fetched.
|
||||
Does not affect servers or onion services. Must be at least 10 minutes.
|
||||
(Default: 24 hours)
|
||||
|
||||
[[DormantTimeoutDisabledByIdleStreams]] **DormantTimeoutDisabledByIdleStreams **0**|**1**::
|
||||
If true, then any open client stream (even one not reading or writing)
|
||||
counts as client activity for the purpose of DormantClientTimeout.
|
||||
If false, then only network activity counts. (Default: 1)
|
||||
|
||||
|
||||
SERVER OPTIONS
|
||||
--------------
|
||||
|
||||
|
@ -389,6 +389,8 @@ static config_var_t option_vars_[] = {
|
||||
OBSOLETE("DynamicDHGroups"),
|
||||
VPORT(DNSPort),
|
||||
OBSOLETE("DNSListenAddress"),
|
||||
V(DormantClientTimeout, INTERVAL, "24 hours"),
|
||||
V(DormantTimeoutDisabledByIdleStreams, BOOL, "1"),
|
||||
/* DoS circuit creation options. */
|
||||
V(DoSCircuitCreationEnabled, AUTOBOOL, "auto"),
|
||||
V(DoSCircuitCreationMinConnections, UINT, "0"),
|
||||
@ -1993,9 +1995,6 @@ options_act(const or_options_t *old_options)
|
||||
finish_daemon(options->DataDirectory);
|
||||
}
|
||||
|
||||
/* See whether we need to enable/disable our once-a-second timer. */
|
||||
reschedule_per_second_timer();
|
||||
|
||||
/* We want to reinit keys as needed before we do much of anything else:
|
||||
keys are important, and other things can depend on them. */
|
||||
if (transition_affects_workers ||
|
||||
@ -3839,6 +3838,10 @@ options_validate(or_options_t *old_options, or_options_t *options,
|
||||
"default.");
|
||||
}
|
||||
|
||||
if (options->DormantClientTimeout < 10*60 && !options->TestingTorNetwork) {
|
||||
REJECT("DormantClientTimeout is too low. It must be at least 10 minutes.");
|
||||
}
|
||||
|
||||
if (options->PathBiasNoticeRate > 1.0) {
|
||||
tor_asprintf(msg,
|
||||
"PathBiasNoticeRate is too high. "
|
||||
|
@ -1072,6 +1072,16 @@ struct or_options_t {
|
||||
|
||||
/** Autobool: Do we refuse single hop client rendezvous? */
|
||||
int DoSRefuseSingleHopClientRendezvous;
|
||||
|
||||
/** Interval: how long without activity does it take for a client
|
||||
* to become dormant?
|
||||
**/
|
||||
int DormantClientTimeout;
|
||||
|
||||
/** Boolean: true if having an idle stream is sufficient to prevent a client
|
||||
* from becoming dormant.
|
||||
**/
|
||||
int DormantTimeoutDisabledByIdleStreams;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -303,6 +303,19 @@ process_signal(int sig)
|
||||
log_heartbeat(time(NULL));
|
||||
control_event_signal(sig);
|
||||
break;
|
||||
case SIGACTIVE:
|
||||
/* "SIGACTIVE" counts as ersatz user activity. */
|
||||
note_user_activity(approx_time());
|
||||
control_event_signal(sig);
|
||||
break;
|
||||
case SIGDORMANT:
|
||||
/* "SIGDORMANT" means to ignore past user activity */
|
||||
log_notice(LD_GENERAL, "Going dormant because of controller request.");
|
||||
reset_user_activity(0);
|
||||
set_network_participation(false);
|
||||
schedule_rescan_periodic_events();
|
||||
control_event_signal(sig);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -472,6 +485,8 @@ static struct {
|
||||
{ SIGNEWNYM, 0, NULL },
|
||||
{ SIGCLEARDNSCACHE, 0, NULL },
|
||||
{ SIGHEARTBEAT, 0, NULL },
|
||||
{ SIGACTIVE, 0, NULL },
|
||||
{ SIGDORMANT, 0, NULL },
|
||||
{ -1, -1, NULL }
|
||||
};
|
||||
|
||||
|
@ -1874,6 +1874,9 @@ connection_init_accepted_conn(connection_t *conn,
|
||||
TO_ENTRY_CONN(conn)->nym_epoch = get_signewnym_epoch();
|
||||
TO_ENTRY_CONN(conn)->socks_request->listener_type = listener->base_.type;
|
||||
|
||||
/* Any incoming connection on an entry port counts as user activity. */
|
||||
note_user_activity(approx_time());
|
||||
|
||||
switch (TO_CONN(listener)->type) {
|
||||
case CONN_TYPE_AP_LISTENER:
|
||||
conn->state = AP_CONN_STATE_SOCKS_WAIT;
|
||||
@ -4426,6 +4429,16 @@ connection_get_by_type_state(int type, int state)
|
||||
CONN_GET_TEMPLATE(conn, conn->type == type && conn->state == state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a connection of type <b>type</b> that is not an internally linked
|
||||
* connection, and is not marked for close.
|
||||
**/
|
||||
MOCK_IMPL(connection_t *,
|
||||
connection_get_by_type_nonlinked,(int type))
|
||||
{
|
||||
CONN_GET_TEMPLATE(conn, conn->type == type && !conn->linked);
|
||||
}
|
||||
|
||||
/** Return a connection of type <b>type</b> that has rendquery equal
|
||||
* to <b>rendquery</b>, and that is not marked for close. If state
|
||||
* is non-zero, conn must be of that state too.
|
||||
|
@ -240,6 +240,7 @@ size_t connection_get_outbuf_len(connection_t *conn);
|
||||
connection_t *connection_get_by_global_id(uint64_t id);
|
||||
|
||||
connection_t *connection_get_by_type(int type);
|
||||
MOCK_DECL(connection_t *,connection_get_by_type_nonlinked,(int type));
|
||||
MOCK_DECL(connection_t *,connection_get_by_type_addr_port_purpose,(int type,
|
||||
const tor_addr_t *addr,
|
||||
uint16_t port, int purpose));
|
||||
|
@ -205,7 +205,6 @@ static void connection_start_reading_from_linked_conn(connection_t *conn);
|
||||
static int connection_should_read_from_linked_conn(connection_t *conn);
|
||||
static void conn_read_callback(evutil_socket_t fd, short event, void *_conn);
|
||||
static void conn_write_callback(evutil_socket_t fd, short event, void *_conn);
|
||||
static void second_elapsed_callback(periodic_timer_t *timer, void *args);
|
||||
static void shutdown_did_not_work_callback(evutil_socket_t fd, short event,
|
||||
void *arg) ATTR_NORETURN;
|
||||
|
||||
@ -1366,78 +1365,91 @@ CALLBACK(save_stability);
|
||||
CALLBACK(save_state);
|
||||
CALLBACK(write_bridge_ns);
|
||||
CALLBACK(write_stats_file);
|
||||
CALLBACK(control_per_second_events);
|
||||
CALLBACK(second_elapsed);
|
||||
|
||||
#undef CALLBACK
|
||||
|
||||
/* Now we declare an array of periodic_event_item_t for each periodic event */
|
||||
#define CALLBACK(name, r, f) PERIODIC_EVENT(name, r, f)
|
||||
#define CALLBACK(name, r, f) \
|
||||
PERIODIC_EVENT(name, PERIODIC_EVENT_ROLE_ ## r, f)
|
||||
#define FL(name) (PERIODIC_EVENT_FLAG_ ## name)
|
||||
|
||||
STATIC periodic_event_item_t periodic_events[] = {
|
||||
/* Everyone needs to run those. */
|
||||
CALLBACK(add_entropy, PERIODIC_EVENT_ROLE_ALL, 0),
|
||||
CALLBACK(check_expired_networkstatus, PERIODIC_EVENT_ROLE_ALL, 0),
|
||||
CALLBACK(clean_caches, PERIODIC_EVENT_ROLE_ALL, 0),
|
||||
CALLBACK(fetch_networkstatus, PERIODIC_EVENT_ROLE_ALL,
|
||||
PERIODIC_EVENT_FLAG_NEED_NET),
|
||||
CALLBACK(heartbeat, PERIODIC_EVENT_ROLE_ALL, 0),
|
||||
CALLBACK(launch_descriptor_fetches, PERIODIC_EVENT_ROLE_ALL,
|
||||
PERIODIC_EVENT_FLAG_NEED_NET),
|
||||
CALLBACK(reset_padding_counts, PERIODIC_EVENT_ROLE_ALL, 0),
|
||||
CALLBACK(retry_listeners, PERIODIC_EVENT_ROLE_ALL,
|
||||
PERIODIC_EVENT_FLAG_NEED_NET),
|
||||
CALLBACK(save_state, PERIODIC_EVENT_ROLE_ALL, 0),
|
||||
CALLBACK(rotate_x509_certificate, PERIODIC_EVENT_ROLE_ALL, 0),
|
||||
CALLBACK(write_stats_file, PERIODIC_EVENT_ROLE_ALL, 0),
|
||||
/* Everyone needs to run these. They need to have very long timeouts for
|
||||
* that to be safe. */
|
||||
CALLBACK(add_entropy, ALL, 0),
|
||||
CALLBACK(heartbeat, ALL, 0),
|
||||
CALLBACK(reset_padding_counts, ALL, 0),
|
||||
|
||||
/* This is a legacy catch-all callback that runs once per second if
|
||||
* we are online and active. */
|
||||
CALLBACK(second_elapsed, NET_PARTICIPANT,
|
||||
FL(NEED_NET)|FL(RUN_ON_DISABLE)),
|
||||
|
||||
/* XXXX Do we have a reason to do this on a callback? Does it do any good at
|
||||
* all? For now, if we're dormant, we can let our listeners decay. */
|
||||
CALLBACK(retry_listeners, NET_PARTICIPANT, FL(NEED_NET)),
|
||||
|
||||
/* We need to do these if we're participating in the Tor network. */
|
||||
CALLBACK(check_expired_networkstatus, NET_PARTICIPANT, 0),
|
||||
CALLBACK(fetch_networkstatus, NET_PARTICIPANT, 0),
|
||||
CALLBACK(launch_descriptor_fetches, NET_PARTICIPANT, FL(NEED_NET)),
|
||||
CALLBACK(rotate_x509_certificate, NET_PARTICIPANT, 0),
|
||||
CALLBACK(check_network_participation, NET_PARTICIPANT, 0),
|
||||
|
||||
/* We need to do these if we're participating in the Tor network, and
|
||||
* immediately before we stop. */
|
||||
CALLBACK(clean_caches, NET_PARTICIPANT, FL(RUN_ON_DISABLE)),
|
||||
CALLBACK(save_state, NET_PARTICIPANT, FL(RUN_ON_DISABLE)),
|
||||
CALLBACK(write_stats_file, NET_PARTICIPANT, FL(RUN_ON_DISABLE)),
|
||||
|
||||
/* Routers (bridge and relay) only. */
|
||||
CALLBACK(check_descriptor, PERIODIC_EVENT_ROLE_ROUTER,
|
||||
PERIODIC_EVENT_FLAG_NEED_NET),
|
||||
CALLBACK(check_ed_keys, PERIODIC_EVENT_ROLE_ROUTER, 0),
|
||||
CALLBACK(check_for_reachability_bw, PERIODIC_EVENT_ROLE_ROUTER,
|
||||
PERIODIC_EVENT_FLAG_NEED_NET),
|
||||
CALLBACK(check_onion_keys_expiry_time, PERIODIC_EVENT_ROLE_ROUTER, 0),
|
||||
CALLBACK(expire_old_ciruits_serverside, PERIODIC_EVENT_ROLE_ROUTER,
|
||||
PERIODIC_EVENT_FLAG_NEED_NET),
|
||||
CALLBACK(reachability_warnings, PERIODIC_EVENT_ROLE_ROUTER,
|
||||
PERIODIC_EVENT_FLAG_NEED_NET),
|
||||
CALLBACK(retry_dns, PERIODIC_EVENT_ROLE_ROUTER, 0),
|
||||
CALLBACK(rotate_onion_key, PERIODIC_EVENT_ROLE_ROUTER, 0),
|
||||
CALLBACK(check_descriptor, ROUTER, FL(NEED_NET)),
|
||||
CALLBACK(check_ed_keys, ROUTER, 0),
|
||||
CALLBACK(check_for_reachability_bw, ROUTER, FL(NEED_NET)),
|
||||
CALLBACK(check_onion_keys_expiry_time, ROUTER, 0),
|
||||
CALLBACK(expire_old_ciruits_serverside, ROUTER, FL(NEED_NET)),
|
||||
CALLBACK(reachability_warnings, ROUTER, FL(NEED_NET)),
|
||||
CALLBACK(retry_dns, ROUTER, 0),
|
||||
CALLBACK(rotate_onion_key, ROUTER, 0),
|
||||
|
||||
/* Authorities (bridge and directory) only. */
|
||||
CALLBACK(downrate_stability, PERIODIC_EVENT_ROLE_AUTHORITIES, 0),
|
||||
CALLBACK(launch_reachability_tests, PERIODIC_EVENT_ROLE_AUTHORITIES,
|
||||
PERIODIC_EVENT_FLAG_NEED_NET),
|
||||
CALLBACK(save_stability, PERIODIC_EVENT_ROLE_AUTHORITIES, 0),
|
||||
CALLBACK(downrate_stability, AUTHORITIES, 0),
|
||||
CALLBACK(launch_reachability_tests, AUTHORITIES, FL(NEED_NET)),
|
||||
CALLBACK(save_stability, AUTHORITIES, 0),
|
||||
|
||||
/* Directory authority only. */
|
||||
CALLBACK(check_authority_cert, PERIODIC_EVENT_ROLE_DIRAUTH, 0),
|
||||
CALLBACK(dirvote, PERIODIC_EVENT_ROLE_DIRAUTH, PERIODIC_EVENT_FLAG_NEED_NET),
|
||||
CALLBACK(check_authority_cert, DIRAUTH, 0),
|
||||
CALLBACK(dirvote, DIRAUTH, FL(NEED_NET)),
|
||||
|
||||
/* Relay only. */
|
||||
CALLBACK(check_canonical_channels, PERIODIC_EVENT_ROLE_RELAY,
|
||||
PERIODIC_EVENT_FLAG_NEED_NET),
|
||||
CALLBACK(check_dns_honesty, PERIODIC_EVENT_ROLE_RELAY,
|
||||
PERIODIC_EVENT_FLAG_NEED_NET),
|
||||
CALLBACK(check_canonical_channels, RELAY, FL(NEED_NET)),
|
||||
CALLBACK(check_dns_honesty, RELAY, FL(NEED_NET)),
|
||||
|
||||
/* Hidden Service service only. */
|
||||
CALLBACK(hs_service, PERIODIC_EVENT_ROLE_HS_SERVICE,
|
||||
PERIODIC_EVENT_FLAG_NEED_NET),
|
||||
CALLBACK(hs_service, HS_SERVICE, FL(NEED_NET)), // XXXX break this down more
|
||||
|
||||
/* Bridge only. */
|
||||
CALLBACK(record_bridge_stats, PERIODIC_EVENT_ROLE_BRIDGE, 0),
|
||||
CALLBACK(record_bridge_stats, BRIDGE, 0),
|
||||
|
||||
/* Client only. */
|
||||
CALLBACK(rend_cache_failure_clean, PERIODIC_EVENT_ROLE_CLIENT, 0),
|
||||
/* XXXX this could be restricted to CLIENT+NET_PARTICIPANT */
|
||||
CALLBACK(rend_cache_failure_clean, NET_PARTICIPANT, FL(RUN_ON_DISABLE)),
|
||||
|
||||
/* Bridge Authority only. */
|
||||
CALLBACK(write_bridge_ns, PERIODIC_EVENT_ROLE_BRIDGEAUTH, 0),
|
||||
CALLBACK(write_bridge_ns, BRIDGEAUTH, 0),
|
||||
|
||||
/* Directory server only. */
|
||||
CALLBACK(clean_consdiffmgr, PERIODIC_EVENT_ROLE_DIRSERVER, 0),
|
||||
CALLBACK(clean_consdiffmgr, DIRSERVER, 0),
|
||||
|
||||
/* Controller with per-second events only. */
|
||||
CALLBACK(control_per_second_events, CONTROLEV, 0),
|
||||
|
||||
END_OF_PERIODIC_EVENTS
|
||||
};
|
||||
#undef CALLBACK
|
||||
#undef FL
|
||||
|
||||
/* These are pointers to members of periodic_events[] that are used to
|
||||
* implement particular callbacks. We keep them separate here so that we
|
||||
@ -1485,7 +1497,7 @@ get_my_roles(const or_options_t *options)
|
||||
{
|
||||
tor_assert(options);
|
||||
|
||||
int roles = 0;
|
||||
int roles = PERIODIC_EVENT_ROLE_ALL;
|
||||
int is_bridge = options->BridgeRelay;
|
||||
int is_relay = server_mode(options);
|
||||
int is_dirauth = authdir_mode_v3(options);
|
||||
@ -1493,6 +1505,8 @@ get_my_roles(const or_options_t *options)
|
||||
int is_hidden_service = !!hs_service_get_num_services() ||
|
||||
!!rend_num_services();
|
||||
int is_dirserver = dir_server_mode(options);
|
||||
int sending_control_events = control_any_per_second_event_enabled();
|
||||
|
||||
/* We also consider tor to have the role of a client if the ControlPort is
|
||||
* set because a lot of things can be done over the control port which
|
||||
* requires tor to have basic functionnalities. */
|
||||
@ -1500,6 +1514,9 @@ get_my_roles(const or_options_t *options)
|
||||
options->ControlPort_set ||
|
||||
options->OwningControllerFD != UINT64_MAX;
|
||||
|
||||
int is_net_participant = is_participating_on_network() ||
|
||||
is_relay || is_hidden_service;
|
||||
|
||||
if (is_bridge) roles |= PERIODIC_EVENT_ROLE_BRIDGE;
|
||||
if (is_client) roles |= PERIODIC_EVENT_ROLE_CLIENT;
|
||||
if (is_relay) roles |= PERIODIC_EVENT_ROLE_RELAY;
|
||||
@ -1507,6 +1524,8 @@ get_my_roles(const or_options_t *options)
|
||||
if (is_bridgeauth) roles |= PERIODIC_EVENT_ROLE_BRIDGEAUTH;
|
||||
if (is_hidden_service) roles |= PERIODIC_EVENT_ROLE_HS_SERVICE;
|
||||
if (is_dirserver) roles |= PERIODIC_EVENT_ROLE_DIRSERVER;
|
||||
if (is_net_participant) roles |= PERIODIC_EVENT_ROLE_NET_PARTICIPANT;
|
||||
if (sending_control_events) roles |= PERIODIC_EVENT_ROLE_CONTROLEV;
|
||||
|
||||
return roles;
|
||||
}
|
||||
@ -1574,6 +1593,30 @@ teardown_periodic_events(void)
|
||||
periodic_events_initialized = 0;
|
||||
}
|
||||
|
||||
static mainloop_event_t *rescan_periodic_events_ev = NULL;
|
||||
|
||||
/** Callback: rescan the periodic event list. */
|
||||
static void
|
||||
rescan_periodic_events_cb(mainloop_event_t *event, void *arg)
|
||||
{
|
||||
(void)event;
|
||||
(void)arg;
|
||||
rescan_periodic_events(get_options());
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule an event that will rescan which periodic events should run.
|
||||
**/
|
||||
MOCK_IMPL(void,
|
||||
schedule_rescan_periodic_events,(void))
|
||||
{
|
||||
if (!rescan_periodic_events_ev) {
|
||||
rescan_periodic_events_ev =
|
||||
mainloop_event_new(rescan_periodic_events_cb, NULL);
|
||||
}
|
||||
mainloop_event_activate(rescan_periodic_events_ev);
|
||||
}
|
||||
|
||||
/** Do a pass at all our periodic events, disable those we don't need anymore
|
||||
* and enable those we need now using the given options. */
|
||||
void
|
||||
@ -1608,9 +1651,13 @@ rescan_periodic_events(const or_options_t *options)
|
||||
periodic_event_enable(item);
|
||||
} else {
|
||||
log_debug(LD_GENERAL, "Disabling periodic event %s", item->name);
|
||||
if (item->flags & PERIODIC_EVENT_FLAG_RUN_ON_DISABLE) {
|
||||
periodic_event_schedule_and_disable(item);
|
||||
} else {
|
||||
periodic_event_disable(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* We just got new options globally set, see if we need to enabled or disable
|
||||
@ -1683,6 +1730,30 @@ mainloop_schedule_postloop_cleanup(void)
|
||||
mainloop_event_activate(postloop_cleanup_ev);
|
||||
}
|
||||
|
||||
/** Event to run 'scheduled_shutdown_cb' */
|
||||
static mainloop_event_t *scheduled_shutdown_ev=NULL;
|
||||
|
||||
/** Callback: run a scheduled shutdown */
|
||||
static void
|
||||
scheduled_shutdown_cb(mainloop_event_t *ev, void *arg)
|
||||
{
|
||||
(void)ev;
|
||||
(void)arg;
|
||||
log_notice(LD_GENERAL, "Clean shutdown finished. Exiting.");
|
||||
tor_shutdown_event_loop_and_exit(0);
|
||||
}
|
||||
|
||||
/** Schedule the mainloop to exit after <b>delay_sec</b> seconds. */
|
||||
void
|
||||
mainloop_schedule_shutdown(int delay_sec)
|
||||
{
|
||||
const struct timeval delay_tv = { delay_sec, 0 };
|
||||
if (! scheduled_shutdown_ev) {
|
||||
scheduled_shutdown_ev = mainloop_event_new(scheduled_shutdown_cb, NULL);
|
||||
}
|
||||
mainloop_event_schedule(scheduled_shutdown_ev, &delay_tv);
|
||||
}
|
||||
|
||||
#define LONGEST_TIMER_PERIOD (30 * 86400)
|
||||
/** Helper: Return the number of seconds between <b>now</b> and <b>next</b>,
|
||||
* clipped to the range [1 second, LONGEST_TIMER_PERIOD]. */
|
||||
@ -1707,16 +1778,16 @@ safe_timer_diff(time_t now, time_t next)
|
||||
}
|
||||
|
||||
/** Perform regular maintenance tasks. This function gets run once per
|
||||
* second by second_elapsed_callback().
|
||||
* second.
|
||||
*/
|
||||
static void
|
||||
run_scheduled_events(time_t now)
|
||||
static int
|
||||
second_elapsed_callback(time_t now, const or_options_t *options)
|
||||
{
|
||||
const or_options_t *options = get_options();
|
||||
|
||||
/* 0. See if we've been asked to shut down and our timeout has
|
||||
* expired; or if our bandwidth limits are exhausted and we
|
||||
* should hibernate; or if it's time to wake up from hibernation.
|
||||
/* 0. See if our bandwidth limits are exhausted and we should hibernate
|
||||
*
|
||||
* Note: we have redundant mechanisms to handle the case where it's
|
||||
* time to wake up from hibernation; or where we have a scheduled
|
||||
* shutdown and it's time to run it, but this will also handle those.
|
||||
*/
|
||||
consider_hibernation(now);
|
||||
|
||||
@ -1726,10 +1797,13 @@ run_scheduled_events(time_t now)
|
||||
if (options->UseBridges && !net_is_disabled()) {
|
||||
/* Note: this check uses net_is_disabled(), not should_delay_dir_fetches()
|
||||
* -- the latter is only for fetching consensus-derived directory info. */
|
||||
// TODO: client
|
||||
// Also, schedule this rather than probing 1x / sec
|
||||
fetch_bridge_descriptors(options, now);
|
||||
}
|
||||
|
||||
if (accounting_is_enabled(options)) {
|
||||
// TODO: refactor or rewrite?
|
||||
accounting_run_housekeeping(now);
|
||||
}
|
||||
|
||||
@ -1740,6 +1814,7 @@ run_scheduled_events(time_t now)
|
||||
*/
|
||||
/* (If our circuit build timeout can ever become lower than a second (which
|
||||
* it can't, currently), we should do this more often.) */
|
||||
// TODO: All expire stuff can become NET_PARTICIPANT, RUN_ON_DISABLE
|
||||
circuit_expire_building();
|
||||
circuit_expire_waiting_for_better_guard();
|
||||
|
||||
@ -1776,6 +1851,9 @@ run_scheduled_events(time_t now)
|
||||
/* 11b. check pending unconfigured managed proxies */
|
||||
if (!net_is_disabled() && pt_proxies_configuration_pending())
|
||||
pt_configure_remaining_proxies();
|
||||
|
||||
/* Run again in a second. */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Periodic callback: rotate the onion keys after the period defined by the
|
||||
@ -1922,6 +2000,55 @@ add_entropy_callback(time_t now, const or_options_t *options)
|
||||
return ENTROPY_INTERVAL;
|
||||
}
|
||||
|
||||
/** Periodic callback: if there has been no network usage in a while,
|
||||
* enter a dormant state. */
|
||||
STATIC int
|
||||
check_network_participation_callback(time_t now, const or_options_t *options)
|
||||
{
|
||||
/* If we're a server, we can't become dormant. */
|
||||
if (server_mode(options)) {
|
||||
goto found_activity;
|
||||
}
|
||||
|
||||
/* If we're running an onion service, we can't become dormant. */
|
||||
/* XXXX this would be nice to change, so that we can be dormant with a
|
||||
* service. */
|
||||
if (hs_service_get_num_services() || rend_num_services()) {
|
||||
goto found_activity;
|
||||
}
|
||||
|
||||
/* If we have any currently open entry streams other than "linked"
|
||||
* connections used for directory requests, those count as user activity.
|
||||
*/
|
||||
if (options->DormantTimeoutDisabledByIdleStreams) {
|
||||
if (connection_get_by_type_nonlinked(CONN_TYPE_AP) != NULL) {
|
||||
goto found_activity;
|
||||
}
|
||||
}
|
||||
|
||||
/* XXXX Make this configurable? */
|
||||
/** How often do we check whether we have had network activity? */
|
||||
#define CHECK_PARTICIPATION_INTERVAL (5*60)
|
||||
|
||||
/* Become dormant if there has been no user activity in a long time.
|
||||
* (The funny checks below are in order to prevent overflow.) */
|
||||
time_t time_since_last_activity = 0;
|
||||
if (get_last_user_activity_time() < now)
|
||||
time_since_last_activity = now - get_last_user_activity_time();
|
||||
if (time_since_last_activity >= options->DormantClientTimeout) {
|
||||
log_notice(LD_GENERAL, "No user activity in a long time: becoming"
|
||||
" dormant.");
|
||||
set_network_participation(false);
|
||||
rescan_periodic_events(options);
|
||||
}
|
||||
|
||||
return CHECK_PARTICIPATION_INTERVAL;
|
||||
|
||||
found_activity:
|
||||
note_user_activity(now);
|
||||
return CHECK_PARTICIPATION_INTERVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Periodic callback: if we're an authority, make sure we test
|
||||
* the routers on the network for reachability.
|
||||
@ -2497,36 +2624,19 @@ hs_service_callback(time_t now, const or_options_t *options)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** Timer: used to invoke second_elapsed_callback() once per second. */
|
||||
static periodic_timer_t *second_timer = NULL;
|
||||
|
||||
/**
|
||||
* Enable or disable the per-second timer as appropriate, creating it if
|
||||
* necessary.
|
||||
/*
|
||||
* Periodic callback: Send once-per-second events to the controller(s).
|
||||
* This is called every second.
|
||||
*/
|
||||
void
|
||||
reschedule_per_second_timer(void)
|
||||
static int
|
||||
control_per_second_events_callback(time_t now, const or_options_t *options)
|
||||
{
|
||||
struct timeval one_second;
|
||||
one_second.tv_sec = 1;
|
||||
one_second.tv_usec = 0;
|
||||
(void) options;
|
||||
(void) now;
|
||||
|
||||
if (! second_timer) {
|
||||
second_timer = periodic_timer_new(tor_libevent_get_base(),
|
||||
&one_second,
|
||||
second_elapsed_callback,
|
||||
NULL);
|
||||
tor_assert(second_timer);
|
||||
}
|
||||
control_per_second_events();
|
||||
|
||||
const bool run_per_second_events =
|
||||
control_any_per_second_event_enabled() || ! net_is_completely_disabled();
|
||||
|
||||
if (run_per_second_events) {
|
||||
periodic_timer_launch(second_timer, &one_second);
|
||||
} else {
|
||||
periodic_timer_disable(second_timer);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** Last time that update_current_time was called. */
|
||||
@ -2565,6 +2675,11 @@ update_current_time(time_t now)
|
||||
if (seconds_elapsed < -NUM_JUMPED_SECONDS_BEFORE_WARN) {
|
||||
// moving back in time is always a bad sign.
|
||||
circuit_note_clock_jumped(seconds_elapsed, false);
|
||||
|
||||
/* Don't go dormant just because we jumped in time. */
|
||||
if (is_participating_on_network()) {
|
||||
reset_user_activity(now);
|
||||
}
|
||||
} else if (seconds_elapsed >= NUM_JUMPED_SECONDS_BEFORE_WARN) {
|
||||
/* Compare the monotonic clock to the result of time(). */
|
||||
const int32_t monotime_msec_passed =
|
||||
@ -2586,6 +2701,11 @@ update_current_time(time_t now)
|
||||
if (clock_jumped || seconds_elapsed >= NUM_IDLE_SECONDS_BEFORE_WARN) {
|
||||
circuit_note_clock_jumped(seconds_elapsed, ! clock_jumped);
|
||||
}
|
||||
|
||||
/* Don't go dormant just because we jumped in time. */
|
||||
if (is_participating_on_network()) {
|
||||
reset_user_activity(now);
|
||||
}
|
||||
} else if (seconds_elapsed > 0) {
|
||||
stats_n_seconds_working += seconds_elapsed;
|
||||
}
|
||||
@ -2594,31 +2714,6 @@ update_current_time(time_t now)
|
||||
current_second = now;
|
||||
}
|
||||
|
||||
/** Libevent callback: invoked once every second. */
|
||||
static void
|
||||
second_elapsed_callback(periodic_timer_t *timer, void *arg)
|
||||
{
|
||||
/* XXXX This could be sensibly refactored into multiple callbacks, and we
|
||||
* could use Libevent's timers for this rather than checking the current
|
||||
* time against a bunch of timeouts every second. */
|
||||
time_t now;
|
||||
(void)timer;
|
||||
(void)arg;
|
||||
|
||||
now = time(NULL);
|
||||
|
||||
/* We don't need to do this once-per-second any more: time-updating is
|
||||
* only in this callback _because it is a callback_. It should be fine
|
||||
* to disable this callback, and the time will still get updated.
|
||||
*/
|
||||
update_current_time(now);
|
||||
|
||||
/* Maybe some controller events are ready to fire */
|
||||
control_per_second_events();
|
||||
|
||||
run_scheduled_events(now);
|
||||
}
|
||||
|
||||
#ifdef HAVE_SYSTEMD_209
|
||||
static periodic_timer_t *systemd_watchdog_timer = NULL;
|
||||
|
||||
@ -2703,15 +2798,18 @@ initialize_mainloop_events(void)
|
||||
int
|
||||
do_main_loop(void)
|
||||
{
|
||||
/* For now, starting Tor always counts as user activity. Later, we might
|
||||
* have an option to control this.
|
||||
*/
|
||||
reset_user_activity(approx_time());
|
||||
set_network_participation(true);
|
||||
|
||||
/* initialize the periodic events first, so that code that depends on the
|
||||
* events being present does not assert.
|
||||
*/
|
||||
initialize_periodic_events();
|
||||
initialize_mainloop_events();
|
||||
|
||||
/* set up once-a-second callback. */
|
||||
reschedule_per_second_timer();
|
||||
|
||||
#ifdef HAVE_SYSTEMD_209
|
||||
uint64_t watchdog_delay;
|
||||
/* set up systemd watchdog notification. */
|
||||
@ -2896,7 +2994,6 @@ tor_mainloop_free_all(void)
|
||||
smartlist_free(connection_array);
|
||||
smartlist_free(closeable_connection_lst);
|
||||
smartlist_free(active_linked_connection_lst);
|
||||
periodic_timer_free(second_timer);
|
||||
teardown_periodic_events();
|
||||
tor_event_free(shutdown_did_not_work_event);
|
||||
tor_event_free(initialize_periodic_events_event);
|
||||
@ -2904,6 +3001,8 @@ tor_mainloop_free_all(void)
|
||||
mainloop_event_free(schedule_active_linked_connections_event);
|
||||
mainloop_event_free(postloop_cleanup_ev);
|
||||
mainloop_event_free(handle_deferred_signewnym_ev);
|
||||
mainloop_event_free(scheduled_shutdown_ev);
|
||||
mainloop_event_free(rescan_periodic_events_ev);
|
||||
|
||||
#ifdef HAVE_SYSTEMD_209
|
||||
periodic_timer_free(systemd_watchdog_timer);
|
||||
|
@ -65,6 +65,7 @@ void reschedule_or_state_save(void);
|
||||
void reschedule_dirvote(const or_options_t *options);
|
||||
void mainloop_schedule_postloop_cleanup(void);
|
||||
void rescan_periodic_events(const or_options_t *options);
|
||||
MOCK_DECL(void, schedule_rescan_periodic_events,(void));
|
||||
|
||||
void update_current_time(time_t now);
|
||||
|
||||
@ -81,11 +82,12 @@ uint64_t get_main_loop_error_count(void);
|
||||
uint64_t get_main_loop_idle_count(void);
|
||||
|
||||
void periodic_events_on_new_options(const or_options_t *options);
|
||||
void reschedule_per_second_timer(void);
|
||||
|
||||
void do_signewnym(time_t);
|
||||
time_t get_last_signewnym_time(void);
|
||||
|
||||
void mainloop_schedule_shutdown(int delay_sec);
|
||||
|
||||
void tor_init_connection_lists(void);
|
||||
void initialize_mainloop_events(void);
|
||||
void tor_mainloop_free_all(void);
|
||||
@ -102,6 +104,9 @@ STATIC void close_closeable_connections(void);
|
||||
STATIC void initialize_periodic_events(void);
|
||||
STATIC void teardown_periodic_events(void);
|
||||
STATIC int get_my_roles(const or_options_t *);
|
||||
STATIC int check_network_participation_callback(time_t now,
|
||||
const or_options_t *options);
|
||||
|
||||
#ifdef TOR_UNIT_TESTS
|
||||
extern smartlist_t *connection_array;
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include "core/or/or.h"
|
||||
#include "core/mainloop/netstatus.h"
|
||||
#include "core/mainloop/mainloop.h"
|
||||
#include "app/config/config.h"
|
||||
#include "feature/hibernate/hibernate.h"
|
||||
|
||||
@ -26,3 +27,75 @@ net_is_completely_disabled(void)
|
||||
{
|
||||
return get_options()->DisableNetwork || we_are_fully_hibernating();
|
||||
}
|
||||
|
||||
/**
|
||||
* The time at which we've last seen "user activity" -- that is, any activity
|
||||
* that should keep us as a participant on the network.
|
||||
*/
|
||||
static time_t last_user_activity_seen = 0;
|
||||
|
||||
/**
|
||||
* True iff we are currently a "network participant" -- that is, we
|
||||
* are building circuits, fetching directory information, and so on.
|
||||
**/
|
||||
static bool participating_on_network = false;
|
||||
|
||||
/**
|
||||
* Record the fact that we have seen "user activity" at the time now. Move
|
||||
* "last activity seen" time forwards, but never backwards.
|
||||
*
|
||||
* If we were previously not participating on the network, set our
|
||||
* participation status to true, and launch periodic events as appropriate.
|
||||
**/
|
||||
void
|
||||
note_user_activity(time_t now)
|
||||
{
|
||||
last_user_activity_seen = MAX(now, last_user_activity_seen);
|
||||
|
||||
if (! participating_on_network) {
|
||||
log_notice(LD_GENERAL, "Tor is no longer dormant.");
|
||||
set_network_participation(true);
|
||||
schedule_rescan_periodic_events();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the time at which "user activitiy" was last seen to <b>now</b>.
|
||||
*
|
||||
* Unlike note_user_actity, this function sets the time without checking
|
||||
* whether it is in the past, and without causing any rescan of periodic events
|
||||
* or change in participation status.
|
||||
*/
|
||||
void
|
||||
reset_user_activity(time_t now)
|
||||
{
|
||||
last_user_activity_seen = now;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the most recent time at which we recorded "user activity".
|
||||
**/
|
||||
time_t
|
||||
get_last_user_activity_time(void)
|
||||
{
|
||||
return last_user_activity_seen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the field that remembers whether we are currently participating on the
|
||||
* network. Does not schedule or un-schedule periodic events.
|
||||
**/
|
||||
void
|
||||
set_network_participation(bool participation)
|
||||
{
|
||||
participating_on_network = participation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true iff we are currently participating on the network.
|
||||
**/
|
||||
bool
|
||||
is_participating_on_network(void)
|
||||
{
|
||||
return participating_on_network;
|
||||
}
|
||||
|
@ -10,4 +10,11 @@
|
||||
int net_is_disabled(void);
|
||||
int net_is_completely_disabled(void);
|
||||
|
||||
void note_user_activity(time_t now);
|
||||
void reset_user_activity(time_t now);
|
||||
time_t get_last_user_activity_time(void);
|
||||
|
||||
void set_network_participation(bool participation);
|
||||
bool is_participating_on_network(void);
|
||||
|
||||
#endif
|
||||
|
@ -45,10 +45,6 @@ periodic_event_dispatch(mainloop_event_t *ev, void *data)
|
||||
periodic_event_item_t *event = data;
|
||||
tor_assert(ev == event->ev);
|
||||
|
||||
if (BUG(!periodic_event_is_enabled(event))) {
|
||||
return;
|
||||
}
|
||||
|
||||
time_t now = time(NULL);
|
||||
update_current_time(now);
|
||||
const or_options_t *options = get_options();
|
||||
@ -57,7 +53,7 @@ periodic_event_dispatch(mainloop_event_t *ev, void *data)
|
||||
int next_interval = 0;
|
||||
|
||||
if (!periodic_event_is_enabled(event)) {
|
||||
/* The event got disabled from inside its callback; no need to
|
||||
/* The event got disabled from inside its callback, or before: no need to
|
||||
* reschedule. */
|
||||
return;
|
||||
}
|
||||
@ -172,3 +168,19 @@ periodic_event_disable(periodic_event_item_t *event)
|
||||
mainloop_event_cancel(event->ev);
|
||||
event->enabled = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable an event, then schedule it to run once.
|
||||
* Do nothing if the event was already disabled.
|
||||
*/
|
||||
void
|
||||
periodic_event_schedule_and_disable(periodic_event_item_t *event)
|
||||
{
|
||||
tor_assert(event);
|
||||
if (!periodic_event_is_enabled(event))
|
||||
return;
|
||||
|
||||
periodic_event_disable(event);
|
||||
|
||||
mainloop_event_activate(event->ev);
|
||||
}
|
||||
|
@ -15,6 +15,10 @@
|
||||
#define PERIODIC_EVENT_ROLE_BRIDGEAUTH (1U << 4)
|
||||
#define PERIODIC_EVENT_ROLE_HS_SERVICE (1U << 5)
|
||||
#define PERIODIC_EVENT_ROLE_DIRSERVER (1U << 6)
|
||||
#define PERIODIC_EVENT_ROLE_CONTROLEV (1U << 7)
|
||||
|
||||
#define PERIODIC_EVENT_ROLE_NET_PARTICIPANT (1U << 8)
|
||||
#define PERIODIC_EVENT_ROLE_ALL (1U << 9)
|
||||
|
||||
/* Helper macro to make it a bit less annoying to defined groups of roles that
|
||||
* are often used. */
|
||||
@ -25,10 +29,6 @@
|
||||
/* Authorities that is both bridge and directory. */
|
||||
#define PERIODIC_EVENT_ROLE_AUTHORITIES \
|
||||
(PERIODIC_EVENT_ROLE_BRIDGEAUTH | PERIODIC_EVENT_ROLE_DIRAUTH)
|
||||
/* All roles. */
|
||||
#define PERIODIC_EVENT_ROLE_ALL \
|
||||
(PERIODIC_EVENT_ROLE_AUTHORITIES | PERIODIC_EVENT_ROLE_CLIENT | \
|
||||
PERIODIC_EVENT_ROLE_HS_SERVICE | PERIODIC_EVENT_ROLE_ROUTER)
|
||||
|
||||
/*
|
||||
* Event flags which can change the behavior of an event.
|
||||
@ -39,6 +39,11 @@
|
||||
* the net_is_disabled() check. */
|
||||
#define PERIODIC_EVENT_FLAG_NEED_NET (1U << 0)
|
||||
|
||||
/* Indicate that if the event is enabled, it needs to be run once before
|
||||
* it becomes disabled.
|
||||
*/
|
||||
#define PERIODIC_EVENT_FLAG_RUN_ON_DISABLE (1U << 1)
|
||||
|
||||
/** Callback function for a periodic event to take action. The return value
|
||||
* influences the next time the function will get called. Return
|
||||
* PERIODIC_EVENT_NO_UPDATE to not update <b>last_action_time</b> and be polled
|
||||
@ -83,6 +88,6 @@ void periodic_event_destroy(periodic_event_item_t *event);
|
||||
void periodic_event_reschedule(periodic_event_item_t *event);
|
||||
void periodic_event_enable(periodic_event_item_t *event);
|
||||
void periodic_event_disable(periodic_event_item_t *event);
|
||||
void periodic_event_schedule_and_disable(periodic_event_item_t *event);
|
||||
|
||||
#endif /* !defined(TOR_PERIODIC_H) */
|
||||
|
||||
|
@ -62,6 +62,7 @@
|
||||
#include "app/config/config.h"
|
||||
#include "core/mainloop/connection.h"
|
||||
#include "core/mainloop/mainloop.h"
|
||||
#include "core/mainloop/netstatus.h"
|
||||
#include "core/or/channel.h"
|
||||
#include "core/or/circuitbuild.h"
|
||||
#include "core/or/circuitlist.h"
|
||||
@ -297,6 +298,11 @@ connection_edge_process_inbuf(edge_connection_t *conn, int package_partial)
|
||||
}
|
||||
return 0;
|
||||
case AP_CONN_STATE_OPEN:
|
||||
if (! conn->base_.linked) {
|
||||
note_user_activity(approx_time());
|
||||
}
|
||||
|
||||
/* falls through. */
|
||||
case EXIT_CONN_STATE_OPEN:
|
||||
if (connection_edge_package_raw_inbuf(conn, package_partial, NULL) < 0) {
|
||||
/* (We already sent an end cell if possible) */
|
||||
@ -751,6 +757,11 @@ connection_edge_flushed_some(edge_connection_t *conn)
|
||||
{
|
||||
switch (conn->base_.state) {
|
||||
case AP_CONN_STATE_OPEN:
|
||||
if (! conn->base_.linked) {
|
||||
note_user_activity(approx_time());
|
||||
}
|
||||
|
||||
/* falls through. */
|
||||
case EXIT_CONN_STATE_OPEN:
|
||||
connection_edge_consider_sending_sendme(conn);
|
||||
break;
|
||||
|
@ -97,6 +97,8 @@ struct curve25519_public_key_t;
|
||||
#define SIGNEWNYM 129
|
||||
#define SIGCLEARDNSCACHE 130
|
||||
#define SIGHEARTBEAT 131
|
||||
#define SIGACTIVE 132
|
||||
#define SIGDORMANT 133
|
||||
|
||||
#if (SIZEOF_CELL_T != 0)
|
||||
/* On Irix, stdlib.h defines a cell_t type, so we need to make sure
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include "core/or/connection_edge.h"
|
||||
#include "feature/control/control.h"
|
||||
#include "core/mainloop/mainloop.h"
|
||||
#include "core/mainloop/netstatus.h"
|
||||
#include "core/or/policies.h"
|
||||
|
||||
#include "feature/control/control_connection_st.h"
|
||||
@ -213,6 +214,9 @@ dnsserv_launch_request(const char *name, int reverse,
|
||||
edge_connection_t *conn;
|
||||
char *q_name;
|
||||
|
||||
/* Launching a request for a user counts as user activity. */
|
||||
note_user_activity(approx_time());
|
||||
|
||||
/* Make a new dummy AP connection, and attach the request to it. */
|
||||
entry_conn = entry_connection_new(CONN_TYPE_AP, AF_INET);
|
||||
entry_conn->entry_cfg.dns_request = 1;
|
||||
|
@ -368,7 +368,7 @@ control_update_global_event_mask(void)
|
||||
control_get_bytes_rw_last_sec(&r, &w);
|
||||
}
|
||||
if (any_old_per_sec_events != control_any_per_second_event_enabled()) {
|
||||
reschedule_per_second_timer();
|
||||
rescan_periodic_events(get_options());
|
||||
}
|
||||
|
||||
#undef NEWLY_ENABLED
|
||||
@ -1681,6 +1681,8 @@ static const struct signal_t signal_table[] = {
|
||||
{ SIGNEWNYM, "NEWNYM" },
|
||||
{ SIGCLEARDNSCACHE, "CLEARDNSCACHE"},
|
||||
{ SIGHEARTBEAT, "HEARTBEAT"},
|
||||
{ SIGACTIVE, "ACTIVE" },
|
||||
{ SIGDORMANT, "DORMANT" },
|
||||
{ 0, NULL },
|
||||
};
|
||||
|
||||
|
@ -66,8 +66,9 @@ static hibernate_state_t hibernate_state = HIBERNATE_STATE_INITIAL;
|
||||
/** If are hibernating, when do we plan to wake up? Set to 0 if we
|
||||
* aren't hibernating. */
|
||||
static time_t hibernate_end_time = 0;
|
||||
/** If we are shutting down, when do we plan finally exit? Set to 0 if
|
||||
* we aren't shutting down. */
|
||||
/** If we are shutting down, when do we plan finally exit? Set to 0 if we
|
||||
* aren't shutting down. (This is obsolete; scheduled shutdowns are supposed
|
||||
* to happen from mainloop_schedule_shutdown() now.) */
|
||||
static time_t shutdown_time = 0;
|
||||
|
||||
/** A timed event that we'll use when it's time to wake up from
|
||||
@ -867,7 +868,13 @@ hibernate_begin(hibernate_state_t new_state, time_t now)
|
||||
log_notice(LD_GENERAL,"Interrupt: we have stopped accepting new "
|
||||
"connections, and will shut down in %d seconds. Interrupt "
|
||||
"again to exit now.", options->ShutdownWaitLength);
|
||||
shutdown_time = time(NULL) + options->ShutdownWaitLength;
|
||||
/* We add an arbitrary delay here so that even if something goes wrong
|
||||
* with the mainloop shutdown code, we can still shutdown from
|
||||
* consider_hibernation() if we call it... but so that the
|
||||
* mainloop_schedule_shutdown() mechanism will be the first one called.
|
||||
*/
|
||||
shutdown_time = time(NULL) + options->ShutdownWaitLength + 5;
|
||||
mainloop_schedule_shutdown(options->ShutdownWaitLength);
|
||||
#ifdef HAVE_SYSTEMD
|
||||
/* tell systemd that we may need more than the default 90 seconds to shut
|
||||
* down so they don't kill us. add some extra time to actually finish
|
||||
@ -1096,11 +1103,12 @@ consider_hibernation(time_t now)
|
||||
hibernate_state_t prev_state = hibernate_state;
|
||||
|
||||
/* If we're in 'exiting' mode, then we just shut down after the interval
|
||||
* elapses. */
|
||||
* elapses. The mainloop was supposed to catch this via
|
||||
* mainloop_schedule_shutdown(), but apparently it didn't. */
|
||||
if (hibernate_state == HIBERNATE_STATE_EXITING) {
|
||||
tor_assert(shutdown_time);
|
||||
if (shutdown_time <= now) {
|
||||
log_notice(LD_GENERAL, "Clean shutdown finished. Exiting.");
|
||||
log_notice(LD_BUG, "Mainloop did not catch shutdown event; exiting.");
|
||||
tor_shutdown_event_loop_and_exit(0);
|
||||
}
|
||||
return; /* if exiting soon, don't worry about bandwidth limits */
|
||||
@ -1112,7 +1120,7 @@ consider_hibernation(time_t now)
|
||||
if (hibernate_end_time > now && accounting_enabled) {
|
||||
/* If we're hibernating, don't wake up until it's time, regardless of
|
||||
* whether we're in a new interval. */
|
||||
return ;
|
||||
return;
|
||||
} else {
|
||||
hibernate_end_time_elapsed(now);
|
||||
}
|
||||
@ -1240,8 +1248,6 @@ on_hibernate_state_change(hibernate_state_t prev_state)
|
||||
if (prev_state != HIBERNATE_STATE_INITIAL) {
|
||||
rescan_periodic_events(get_options());
|
||||
}
|
||||
|
||||
reschedule_per_second_timer();
|
||||
}
|
||||
|
||||
/** Free all resources held by the accounting module */
|
||||
|
@ -3667,8 +3667,8 @@ hs_service_lookup_current_desc(const ed25519_public_key_t *pk)
|
||||
}
|
||||
|
||||
/* Return the number of service we have configured and usable. */
|
||||
unsigned int
|
||||
hs_service_get_num_services(void)
|
||||
MOCK_IMPL(unsigned int,
|
||||
hs_service_get_num_services,(void))
|
||||
{
|
||||
if (hs_service_map == NULL) {
|
||||
return 0;
|
||||
|
@ -310,7 +310,7 @@ hs_service_t *hs_service_new(const or_options_t *options);
|
||||
void hs_service_free_(hs_service_t *service);
|
||||
#define hs_service_free(s) FREE_AND_NULL(hs_service_t, hs_service_free_, (s))
|
||||
|
||||
unsigned int hs_service_get_num_services(void);
|
||||
MOCK_DECL(unsigned int, hs_service_get_num_services,(void));
|
||||
void hs_service_stage_services(const smartlist_t *service_list);
|
||||
int hs_service_load_all_keys(void);
|
||||
int hs_service_get_version_from_key(const hs_service_t *service);
|
||||
|
@ -187,4 +187,3 @@ struct testcase_t compat_libevent_tests[] = {
|
||||
TT_FORK, NULL, NULL },
|
||||
END_OF_TESTCASES
|
||||
};
|
||||
|
||||
|
@ -6,11 +6,20 @@
|
||||
* \brief Tests for functions closely related to the Tor main loop
|
||||
*/
|
||||
|
||||
#define CONFIG_PRIVATE
|
||||
#define MAINLOOP_PRIVATE
|
||||
|
||||
#include "test/test.h"
|
||||
#include "test/log_test_helpers.h"
|
||||
|
||||
#include "core/or/or.h"
|
||||
#include "core/mainloop/connection.h"
|
||||
#include "core/mainloop/mainloop.h"
|
||||
#include "core/mainloop/netstatus.h"
|
||||
|
||||
#include "feature/hs/hs_service.h"
|
||||
|
||||
#include "app/config/config.h"
|
||||
|
||||
static const uint64_t BILLION = 1000000000;
|
||||
|
||||
@ -131,12 +140,146 @@ test_mainloop_update_time_jumps(void *arg)
|
||||
monotime_disable_test_mocking();
|
||||
}
|
||||
|
||||
static int schedule_rescan_called = 0;
|
||||
static void
|
||||
mock_schedule_rescan_periodic_events(void)
|
||||
{
|
||||
++schedule_rescan_called;
|
||||
}
|
||||
|
||||
static void
|
||||
test_mainloop_user_activity(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
const time_t start = 1542658829;
|
||||
update_approx_time(start);
|
||||
|
||||
MOCK(schedule_rescan_periodic_events, mock_schedule_rescan_periodic_events);
|
||||
|
||||
reset_user_activity(start);
|
||||
tt_i64_op(get_last_user_activity_time(), OP_EQ, start);
|
||||
|
||||
set_network_participation(false);
|
||||
|
||||
// reset can move backwards and forwards, but does not change network
|
||||
// participation.
|
||||
reset_user_activity(start-10);
|
||||
tt_i64_op(get_last_user_activity_time(), OP_EQ, start-10);
|
||||
reset_user_activity(start+10);
|
||||
tt_i64_op(get_last_user_activity_time(), OP_EQ, start+10);
|
||||
|
||||
tt_int_op(schedule_rescan_called, OP_EQ, 0);
|
||||
tt_int_op(false, OP_EQ, is_participating_on_network());
|
||||
|
||||
// "note" can only move forward. Calling it from a non-participating
|
||||
// state makes us rescan the periodic callbacks and set participation.
|
||||
note_user_activity(start+20);
|
||||
tt_i64_op(get_last_user_activity_time(), OP_EQ, start+20);
|
||||
tt_int_op(true, OP_EQ, is_participating_on_network());
|
||||
tt_int_op(schedule_rescan_called, OP_EQ, 1);
|
||||
|
||||
// Calling it again will move us forward, but not call rescan again.
|
||||
note_user_activity(start+25);
|
||||
tt_i64_op(get_last_user_activity_time(), OP_EQ, start+25);
|
||||
tt_int_op(true, OP_EQ, is_participating_on_network());
|
||||
tt_int_op(schedule_rescan_called, OP_EQ, 1);
|
||||
|
||||
// We won't move backwards.
|
||||
note_user_activity(start+20);
|
||||
tt_i64_op(get_last_user_activity_time(), OP_EQ, start+25);
|
||||
tt_int_op(true, OP_EQ, is_participating_on_network());
|
||||
tt_int_op(schedule_rescan_called, OP_EQ, 1);
|
||||
|
||||
done:
|
||||
UNMOCK(schedule_rescan_periodic_events);
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
mock_get_num_services(void)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static connection_t *
|
||||
mock_connection_gbtu(int type)
|
||||
{
|
||||
(void) type;
|
||||
return (void *)"hello fellow connections";
|
||||
}
|
||||
|
||||
static void
|
||||
test_mainloop_check_participation(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
or_options_t *options = options_new();
|
||||
const time_t start = 1542658829;
|
||||
const time_t ONE_DAY = 24*60*60;
|
||||
|
||||
// Suppose we've been idle for a day or two
|
||||
reset_user_activity(start - 2*ONE_DAY);
|
||||
set_network_participation(true);
|
||||
check_network_participation_callback(start, options);
|
||||
tt_int_op(is_participating_on_network(), OP_EQ, false);
|
||||
tt_i64_op(get_last_user_activity_time(), OP_EQ, start-2*ONE_DAY);
|
||||
|
||||
// suppose we've been idle for 2 days... but we are a server.
|
||||
reset_user_activity(start - 2*ONE_DAY);
|
||||
options->ORPort_set = 1;
|
||||
set_network_participation(true);
|
||||
check_network_participation_callback(start+2, options);
|
||||
tt_int_op(is_participating_on_network(), OP_EQ, true);
|
||||
tt_i64_op(get_last_user_activity_time(), OP_EQ, start+2);
|
||||
options->ORPort_set = 0;
|
||||
|
||||
// idle for 2 days, but we have a hidden service.
|
||||
reset_user_activity(start - 2*ONE_DAY);
|
||||
set_network_participation(true);
|
||||
MOCK(hs_service_get_num_services, mock_get_num_services);
|
||||
check_network_participation_callback(start+3, options);
|
||||
tt_int_op(is_participating_on_network(), OP_EQ, true);
|
||||
tt_i64_op(get_last_user_activity_time(), OP_EQ, start+3);
|
||||
UNMOCK(hs_service_get_num_services);
|
||||
|
||||
// idle for 2 days but we have at least one user connection
|
||||
MOCK(connection_get_by_type_nonlinked, mock_connection_gbtu);
|
||||
reset_user_activity(start - 2*ONE_DAY);
|
||||
set_network_participation(true);
|
||||
options->DormantTimeoutDisabledByIdleStreams = 1;
|
||||
check_network_participation_callback(start+10, options);
|
||||
tt_int_op(is_participating_on_network(), OP_EQ, true);
|
||||
tt_i64_op(get_last_user_activity_time(), OP_EQ, start+10);
|
||||
|
||||
// as above, but DormantTimeoutDisabledByIdleStreams is not set
|
||||
reset_user_activity(start - 2*ONE_DAY);
|
||||
set_network_participation(true);
|
||||
options->DormantTimeoutDisabledByIdleStreams = 0;
|
||||
check_network_participation_callback(start+13, options);
|
||||
tt_int_op(is_participating_on_network(), OP_EQ, false);
|
||||
tt_i64_op(get_last_user_activity_time(), OP_EQ, start-2*ONE_DAY);
|
||||
UNMOCK(connection_get_by_type_nonlinked);
|
||||
options->DormantTimeoutDisabledByIdleStreams = 1;
|
||||
|
||||
// idle for 2 days but DormantClientTimeout is 3 days
|
||||
reset_user_activity(start - 2*ONE_DAY);
|
||||
set_network_participation(true);
|
||||
options->DormantClientTimeout = ONE_DAY * 3;
|
||||
check_network_participation_callback(start+30, options);
|
||||
tt_int_op(is_participating_on_network(), OP_EQ, true);
|
||||
tt_i64_op(get_last_user_activity_time(), OP_EQ, start-2*ONE_DAY);
|
||||
|
||||
done:
|
||||
or_options_free(options);
|
||||
UNMOCK(hs_service_get_num_services);
|
||||
UNMOCK(connection_get_by_type_nonlinked);
|
||||
}
|
||||
|
||||
#define MAINLOOP_TEST(name) \
|
||||
{ #name, test_mainloop_## name , TT_FORK, NULL, NULL }
|
||||
|
||||
struct testcase_t mainloop_tests[] = {
|
||||
MAINLOOP_TEST(update_time_normal),
|
||||
MAINLOOP_TEST(update_time_jumps),
|
||||
MAINLOOP_TEST(user_activity),
|
||||
MAINLOOP_TEST(check_participation),
|
||||
END_OF_TESTCASES
|
||||
};
|
||||
|
||||
|
@ -425,6 +425,7 @@ get_options_test_data(const char *conf)
|
||||
// with options_init(), but about a dozen tests break when I do that.
|
||||
// Being kinda lame and just fixing the immedate breakage for now..
|
||||
result->opt->ConnectionPadding = -1; // default must be "auto"
|
||||
result->opt->DormantClientTimeout = 1800; // must be over 600.
|
||||
|
||||
rv = config_get_lines(conf, &cl, 1);
|
||||
tt_int_op(rv, OP_EQ, 0);
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "feature/hibernate/hibernate.h"
|
||||
#include "feature/hs/hs_service.h"
|
||||
#include "core/mainloop/mainloop.h"
|
||||
#include "core/mainloop/netstatus.h"
|
||||
#include "core/mainloop/periodic.h"
|
||||
|
||||
/** Helper function: This is replaced in some tests for the event callbacks so
|
||||
@ -50,6 +51,8 @@ test_pe_initialize(void *arg)
|
||||
* need to run the main loop and then wait for a second delaying the unit
|
||||
* tests. Instead, we'll test the callback work indepedently elsewhere. */
|
||||
initialize_periodic_events();
|
||||
set_network_participation(false);
|
||||
rescan_periodic_events(get_options());
|
||||
|
||||
/* Validate that all events have been set up. */
|
||||
for (int i = 0; periodic_events[i].name; ++i) {
|
||||
@ -59,7 +62,9 @@ test_pe_initialize(void *arg)
|
||||
tt_u64_op(item->last_action_time, OP_EQ, 0);
|
||||
/* Every event must have role(s) assign to it. This is done statically. */
|
||||
tt_u64_op(item->roles, OP_NE, 0);
|
||||
tt_uint_op(periodic_event_is_enabled(item), OP_EQ, 0);
|
||||
int should_be_enabled = (item->roles & PERIODIC_EVENT_ROLE_ALL) &&
|
||||
!(item->flags & PERIODIC_EVENT_FLAG_NEED_NET);
|
||||
tt_uint_op(periodic_event_is_enabled(item), OP_EQ, should_be_enabled);
|
||||
}
|
||||
|
||||
done:
|
||||
@ -79,6 +84,8 @@ test_pe_launch(void *arg)
|
||||
* network gets enabled. */
|
||||
consider_hibernation(time(NULL));
|
||||
|
||||
set_network_participation(true);
|
||||
|
||||
/* Hack: We'll set a dumb fn() of each events so they don't get called when
|
||||
* dispatching them. We just want to test the state of the callbacks, not
|
||||
* the whole code path. */
|
||||
@ -90,6 +97,7 @@ test_pe_launch(void *arg)
|
||||
options = get_options_mutable();
|
||||
options->SocksPort_set = 1;
|
||||
periodic_events_on_new_options(options);
|
||||
|
||||
#if 0
|
||||
/* Lets make sure that before intialization, we can't scan the periodic
|
||||
* events list and launch them. Lets try by being a Client. */
|
||||
@ -106,13 +114,12 @@ test_pe_launch(void *arg)
|
||||
/* Now that we've initialized, rescan the list to launch. */
|
||||
periodic_events_on_new_options(options);
|
||||
|
||||
int mask = PERIODIC_EVENT_ROLE_CLIENT|PERIODIC_EVENT_ROLE_ALL|
|
||||
PERIODIC_EVENT_ROLE_NET_PARTICIPANT;
|
||||
for (int i = 0; periodic_events[i].name; ++i) {
|
||||
periodic_event_item_t *item = &periodic_events[i];
|
||||
if (item->roles & PERIODIC_EVENT_ROLE_CLIENT) {
|
||||
tt_int_op(periodic_event_is_enabled(item), OP_EQ, 1);
|
||||
} else {
|
||||
tt_int_op(periodic_event_is_enabled(item), OP_EQ, 0);
|
||||
}
|
||||
int should_be_enabled = !!(item->roles & mask);
|
||||
tt_int_op(periodic_event_is_enabled(item), OP_EQ, should_be_enabled);
|
||||
// enabled or not, the event has not yet been run.
|
||||
tt_u64_op(item->last_action_time, OP_EQ, 0);
|
||||
}
|
||||
@ -124,7 +131,8 @@ test_pe_launch(void *arg)
|
||||
|
||||
unsigned roles = get_my_roles(options);
|
||||
tt_uint_op(roles, OP_EQ,
|
||||
PERIODIC_EVENT_ROLE_RELAY|PERIODIC_EVENT_ROLE_DIRSERVER);
|
||||
PERIODIC_EVENT_ROLE_RELAY|PERIODIC_EVENT_ROLE_DIRSERVER|
|
||||
PERIODIC_EVENT_ROLE_ALL|PERIODIC_EVENT_ROLE_NET_PARTICIPANT);
|
||||
|
||||
for (int i = 0; periodic_events[i].name; ++i) {
|
||||
periodic_event_item_t *item = &periodic_events[i];
|
||||
@ -144,17 +152,23 @@ test_pe_launch(void *arg)
|
||||
/* Disable everything and we'll enable them ALL. */
|
||||
options->SocksPort_set = 0;
|
||||
options->ORPort_set = 0;
|
||||
options->DisableNetwork = 1;
|
||||
set_network_participation(false);
|
||||
periodic_events_on_new_options(options);
|
||||
|
||||
for (int i = 0; periodic_events[i].name; ++i) {
|
||||
periodic_event_item_t *item = &periodic_events[i];
|
||||
tt_int_op(periodic_event_is_enabled(item), OP_EQ, 0);
|
||||
int should_be_enabled = (item->roles & PERIODIC_EVENT_ROLE_ALL) &&
|
||||
!(item->flags & PERIODIC_EVENT_FLAG_NEED_NET);
|
||||
tt_int_op(periodic_event_is_enabled(item), OP_EQ, should_be_enabled);
|
||||
}
|
||||
|
||||
/* Enable everything. */
|
||||
options->SocksPort_set = 1; options->ORPort_set = 1;
|
||||
options->BridgeRelay = 1; options->AuthoritativeDir = 1;
|
||||
options->V3AuthoritativeDir = 1; options->BridgeAuthoritativeDir = 1;
|
||||
options->DisableNetwork = 0;
|
||||
set_network_participation(true);
|
||||
register_dummy_hidden_service(&service);
|
||||
periodic_events_on_new_options(options);
|
||||
/* Note down the reference because we need to remove this service from the
|
||||
@ -165,7 +179,8 @@ test_pe_launch(void *arg)
|
||||
|
||||
for (int i = 0; periodic_events[i].name; ++i) {
|
||||
periodic_event_item_t *item = &periodic_events[i];
|
||||
tt_int_op(periodic_event_is_enabled(item), OP_EQ, 1);
|
||||
tt_int_op(periodic_event_is_enabled(item), OP_EQ,
|
||||
(item->roles != PERIODIC_EVENT_ROLE_CONTROLEV));
|
||||
}
|
||||
|
||||
done:
|
||||
@ -187,42 +202,49 @@ test_pe_get_roles(void *arg)
|
||||
|
||||
or_options_t *options = get_options_mutable();
|
||||
tt_assert(options);
|
||||
set_network_participation(true);
|
||||
|
||||
const int ALL = PERIODIC_EVENT_ROLE_ALL |
|
||||
PERIODIC_EVENT_ROLE_NET_PARTICIPANT;
|
||||
|
||||
/* Nothing configured, should be no roles. */
|
||||
tt_assert(net_is_disabled());
|
||||
roles = get_my_roles(options);
|
||||
tt_int_op(roles, OP_EQ, 0);
|
||||
tt_int_op(roles, OP_EQ, ALL);
|
||||
|
||||
/* Indicate we have a SocksPort, roles should be come Client. */
|
||||
options->SocksPort_set = 1;
|
||||
roles = get_my_roles(options);
|
||||
tt_int_op(roles, OP_EQ, PERIODIC_EVENT_ROLE_CLIENT);
|
||||
tt_int_op(roles, OP_EQ, PERIODIC_EVENT_ROLE_CLIENT|ALL);
|
||||
|
||||
/* Now, we'll add a ORPort so should now be a Relay + Client. */
|
||||
options->ORPort_set = 1;
|
||||
roles = get_my_roles(options);
|
||||
tt_int_op(roles, OP_EQ,
|
||||
(PERIODIC_EVENT_ROLE_CLIENT | PERIODIC_EVENT_ROLE_RELAY |
|
||||
PERIODIC_EVENT_ROLE_DIRSERVER));
|
||||
PERIODIC_EVENT_ROLE_DIRSERVER | ALL));
|
||||
|
||||
/* Now add a Bridge. */
|
||||
options->BridgeRelay = 1;
|
||||
roles = get_my_roles(options);
|
||||
tt_int_op(roles, OP_EQ,
|
||||
(PERIODIC_EVENT_ROLE_CLIENT | PERIODIC_EVENT_ROLE_RELAY |
|
||||
PERIODIC_EVENT_ROLE_BRIDGE | PERIODIC_EVENT_ROLE_DIRSERVER));
|
||||
PERIODIC_EVENT_ROLE_BRIDGE | PERIODIC_EVENT_ROLE_DIRSERVER |
|
||||
ALL));
|
||||
tt_assert(roles & PERIODIC_EVENT_ROLE_ROUTER);
|
||||
/* Unset client so we can solely test Router role. */
|
||||
options->SocksPort_set = 0;
|
||||
roles = get_my_roles(options);
|
||||
tt_int_op(roles, OP_EQ,
|
||||
PERIODIC_EVENT_ROLE_ROUTER | PERIODIC_EVENT_ROLE_DIRSERVER);
|
||||
PERIODIC_EVENT_ROLE_ROUTER | PERIODIC_EVENT_ROLE_DIRSERVER |
|
||||
ALL);
|
||||
|
||||
/* Reset options so we can test authorities. */
|
||||
options->SocksPort_set = 0;
|
||||
options->ORPort_set = 0;
|
||||
options->BridgeRelay = 0;
|
||||
roles = get_my_roles(options);
|
||||
tt_int_op(roles, OP_EQ, 0);
|
||||
tt_int_op(roles, OP_EQ, ALL);
|
||||
|
||||
/* Now upgrade to Dirauth. */
|
||||
options->DirPort_set = 1;
|
||||
@ -230,7 +252,7 @@ test_pe_get_roles(void *arg)
|
||||
options->V3AuthoritativeDir = 1;
|
||||
roles = get_my_roles(options);
|
||||
tt_int_op(roles, OP_EQ,
|
||||
PERIODIC_EVENT_ROLE_DIRAUTH|PERIODIC_EVENT_ROLE_DIRSERVER);
|
||||
PERIODIC_EVENT_ROLE_DIRAUTH|PERIODIC_EVENT_ROLE_DIRSERVER|ALL);
|
||||
tt_assert(roles & PERIODIC_EVENT_ROLE_AUTHORITIES);
|
||||
|
||||
/* Now Bridge Authority. */
|
||||
@ -238,7 +260,7 @@ test_pe_get_roles(void *arg)
|
||||
options->BridgeAuthoritativeDir = 1;
|
||||
roles = get_my_roles(options);
|
||||
tt_int_op(roles, OP_EQ,
|
||||
PERIODIC_EVENT_ROLE_BRIDGEAUTH|PERIODIC_EVENT_ROLE_DIRSERVER);
|
||||
PERIODIC_EVENT_ROLE_BRIDGEAUTH|PERIODIC_EVENT_ROLE_DIRSERVER|ALL);
|
||||
tt_assert(roles & PERIODIC_EVENT_ROLE_AUTHORITIES);
|
||||
|
||||
/* Move that bridge auth to become a relay. */
|
||||
@ -246,7 +268,7 @@ test_pe_get_roles(void *arg)
|
||||
roles = get_my_roles(options);
|
||||
tt_int_op(roles, OP_EQ,
|
||||
(PERIODIC_EVENT_ROLE_BRIDGEAUTH | PERIODIC_EVENT_ROLE_RELAY
|
||||
| PERIODIC_EVENT_ROLE_DIRSERVER));
|
||||
| PERIODIC_EVENT_ROLE_DIRSERVER|ALL));
|
||||
tt_assert(roles & PERIODIC_EVENT_ROLE_AUTHORITIES);
|
||||
|
||||
/* And now an Hidden service. */
|
||||
@ -257,7 +279,8 @@ test_pe_get_roles(void *arg)
|
||||
remove_service(get_hs_service_map(), &service);
|
||||
tt_int_op(roles, OP_EQ,
|
||||
(PERIODIC_EVENT_ROLE_BRIDGEAUTH | PERIODIC_EVENT_ROLE_RELAY |
|
||||
PERIODIC_EVENT_ROLE_HS_SERVICE | PERIODIC_EVENT_ROLE_DIRSERVER));
|
||||
PERIODIC_EVENT_ROLE_HS_SERVICE | PERIODIC_EVENT_ROLE_DIRSERVER |
|
||||
ALL));
|
||||
tt_assert(roles & PERIODIC_EVENT_ROLE_AUTHORITIES);
|
||||
|
||||
done:
|
||||
|
Loading…
Reference in New Issue
Block a user