diff options
Diffstat (limited to 'src/or')
-rw-r--r-- | src/or/addressmap.c | 4 | ||||
-rw-r--r-- | src/or/circuitbuild.c | 30 | ||||
-rw-r--r-- | src/or/circuitbuild.h | 2 | ||||
-rw-r--r-- | src/or/config.c | 23 | ||||
-rw-r--r-- | src/or/config.h | 3 | ||||
-rw-r--r-- | src/or/connection.c | 15 | ||||
-rw-r--r-- | src/or/connection_edge.c | 1 | ||||
-rw-r--r-- | src/or/control.c | 103 | ||||
-rw-r--r-- | src/or/control.h | 3 | ||||
-rw-r--r-- | src/or/dirserv.c | 2 | ||||
-rw-r--r-- | src/or/hibernate.c | 90 | ||||
-rw-r--r-- | src/or/hibernate.h | 2 | ||||
-rw-r--r-- | src/or/hs_descriptor.c | 2 | ||||
-rw-r--r-- | src/or/hs_service.c | 5 | ||||
-rw-r--r-- | src/or/main.c | 195 | ||||
-rw-r--r-- | src/or/main.h | 3 | ||||
-rw-r--r-- | src/or/networkstatus.c | 20 | ||||
-rw-r--r-- | src/or/networkstatus.h | 2 | ||||
-rw-r--r-- | src/or/periodic.c | 2 | ||||
-rw-r--r-- | src/or/rendservice.c | 5 | ||||
-rw-r--r-- | src/or/router.c | 18 | ||||
-rw-r--r-- | src/or/router.h | 1 |
22 files changed, 395 insertions, 136 deletions
diff --git a/src/or/addressmap.c b/src/or/addressmap.c index 3cd153307d..7f861e4d24 100644 --- a/src/or/addressmap.c +++ b/src/or/addressmap.c @@ -960,9 +960,11 @@ addressmap_get_virtual_address(int type) char tmp[TOR_ADDR_BUF_LEN]; tor_addr_to_str(tmp, &addr, sizeof(tmp), 0); if (strmap_get(addressmap, tmp)) { + // LCOV_EXCL_START log_warn(LD_BUG, "%s wasn't in the addressmap, but %s was.", buf, tmp); continue; + // LCOV_EXCL_STOP } return tor_strdup(buf); @@ -971,8 +973,10 @@ addressmap_get_virtual_address(int type) log_warn(LD_CONFIG, "Ran out of virtual addresses!"); return NULL; } else { + // LCOV_EXCL_START log_warn(LD_BUG, "Called with unsupported address type (%d)", type); return NULL; + // LCOV_EXCL_STOP } } diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c index 6881e0ebb8..09102d0c4c 100644 --- a/src/or/circuitbuild.c +++ b/src/or/circuitbuild.c @@ -1130,19 +1130,29 @@ circuit_send_intermediate_onion_skin(origin_circuit_t *circ, return 0; } -/** Our clock just jumped by <b>seconds_elapsed</b>. Assume - * something has also gone wrong with our network: notify the user, - * and abandon all not-yet-used circuits. */ +/** Our clock just jumped by <b>seconds_elapsed</b>. If <b>was_idle</b> is + * true, then the monotonic time matches; otherwise it doesn't. Assume + * something has also gone wrong with our network: notify the user, and + * abandon all not-yet-used circuits. */ void -circuit_note_clock_jumped(int seconds_elapsed) +circuit_note_clock_jumped(int64_t seconds_elapsed, bool was_idle) { int severity = server_mode(get_options()) ? LOG_WARN : LOG_NOTICE; - tor_log(severity, LD_GENERAL, "Your system clock just jumped %d seconds %s; " - "assuming established circuits no longer work.", - seconds_elapsed >=0 ? seconds_elapsed : -seconds_elapsed, - seconds_elapsed >=0 ? "forward" : "backward"); - control_event_general_status(LOG_WARN, "CLOCK_JUMPED TIME=%d", - seconds_elapsed); + if (was_idle) { + tor_log(severity, LD_GENERAL, "Tor has been idle for "I64_FORMAT + " seconds; assuming established circuits no longer work.", + I64_PRINTF_ARG(seconds_elapsed)); + } else { + tor_log(severity, LD_GENERAL, + "Your system clock just jumped "I64_FORMAT" seconds %s; " + "assuming established circuits no longer work.", + I64_PRINTF_ARG( + seconds_elapsed >=0 ? seconds_elapsed : -seconds_elapsed), + seconds_elapsed >=0 ? "forward" : "backward"); + } + control_event_general_status(LOG_WARN, "CLOCK_JUMPED TIME="I64_FORMAT + " IDLE=%d", + I64_PRINTF_ARG(seconds_elapsed), was_idle?1:0); /* so we log when it works again */ note_that_we_maybe_cant_complete_circuits(); control_event_client_status(severity, "CIRCUIT_NOT_ESTABLISHED REASON=%s", diff --git a/src/or/circuitbuild.h b/src/or/circuitbuild.h index ae4aef768a..0184898e29 100644 --- a/src/or/circuitbuild.h +++ b/src/or/circuitbuild.h @@ -29,7 +29,7 @@ void circuit_n_chan_done(channel_t *chan, int status, int inform_testing_reachability(void); int circuit_timeout_want_to_count_circ(const origin_circuit_t *circ); int circuit_send_next_onion_skin(origin_circuit_t *circ); -void circuit_note_clock_jumped(int seconds_elapsed); +void circuit_note_clock_jumped(int64_t seconds_elapsed, bool was_idle); int circuit_extend(cell_t *cell, circuit_t *circ); int circuit_init_cpath_crypto(crypt_path_t *cpath, const char *key_data, size_t key_data_len, diff --git a/src/or/config.c b/src/or/config.c index 9af613e931..94a58f3488 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -1449,9 +1449,9 @@ options_act_reversible(const or_options_t *old_options, char **msg) consider_hibernation(time(NULL)); /* Launch the listeners. (We do this before we setuid, so we can bind to - * ports under 1024.) We don't want to rebind if we're hibernating. If - * networking is disabled, this will close all but the control listeners, - * but disable those. */ + * ports under 1024.) We don't want to rebind if we're hibernating or + * shutting down. If networking is disabled, this will close all but the + * control listeners, but disable those. */ if (!we_are_hibernating()) { if (retry_all_listeners(replaced_listeners, new_listeners, options->DisableNetwork) < 0) { @@ -2001,6 +2001,9 @@ 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 || @@ -8433,3 +8436,17 @@ init_cookie_authentication(const char *fname, const char *header, return retval; } +/** + * Return true if any option is set in <b>options</b> to make us behave + * as a client. + */ +int +options_any_client_port_set(const or_options_t *options) +{ + return (options->SocksPort_set || + options->TransPort_set || + options->NATDPort_set || + options->DNSPort_set || + options->HTTPTunnelPort_set); +} + diff --git a/src/or/config.h b/src/or/config.h index 1d3c27217e..4b41274434 100644 --- a/src/or/config.h +++ b/src/or/config.h @@ -214,6 +214,9 @@ smartlist_t *get_options_from_transport_options_line(const char *line, const char *transport); smartlist_t *get_options_for_server_transport(const char *transport); +/* Port helper functions. */ +int options_any_client_port_set(const or_options_t *options); + #ifdef CONFIG_PRIVATE #define CL_PORT_NO_STREAM_OPTIONS (1u<<0) diff --git a/src/or/connection.c b/src/or/connection.c index fa9881cb8b..5185b45b14 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -1762,13 +1762,13 @@ connection_connect_sockaddr,(connection_t *conn, tor_assert(sa); tor_assert(socket_error); - if (get_options()->DisableNetwork) { - /* We should never even try to connect anyplace if DisableNetwork is set. - * Warn if we do, and refuse to make the connection. + if (net_is_completely_disabled()) { + /* We should never even try to connect anyplace if the network is + * completely shut off. * - * We only check DisableNetwork here, not we_are_hibernating(), since - * we'll still try to fulfill client requests sometimes in the latter case - * (if it is soft hibernation) */ + * (We don't check net_is_disabled() here, since we still sometimes + * want to open connections when we're in soft hibernation.) + */ static ratelim_t disablenet_violated = RATELIM_INIT(30*60); *socket_error = SOCK_ERRNO(ENETUNREACH); log_fn_ratelim(&disablenet_violated, LOG_WARN, LD_BUG, @@ -3440,7 +3440,7 @@ int connection_handle_read(connection_t *conn) { int res; - + update_current_time(time(NULL)); res = connection_handle_read_impl(conn); return res; } @@ -3983,6 +3983,7 @@ int connection_handle_write(connection_t *conn, int force) { int res; + update_current_time(time(NULL)); conn->in_connection_handle_write = 1; res = connection_handle_write_impl(conn, force); conn->in_connection_handle_write = 0; diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c index 5ae1538bfe..28e18aa853 100644 --- a/src/or/connection_edge.c +++ b/src/or/connection_edge.c @@ -3537,6 +3537,7 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ) n_stream->base_.state = EXIT_CONN_STATE_RESOLVEFAILED; /* default to failed, change in dns_resolve if it turns out not to fail */ + /* If we're hibernating or shutting down, we refuse to open new streams. */ if (we_are_hibernating()) { relay_send_end_cell_from_edge(rh.stream_id, circ, END_STREAM_REASON_HIBERNATING, NULL); diff --git a/src/or/control.c b/src/or/control.c index 93b204f9a3..9323173f5d 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -1,3 +1,4 @@ + /* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ @@ -112,6 +113,10 @@ static int disable_log_messages = 0; #define EVENT_IS_INTERESTING(e) \ (!! (global_event_mask & EVENT_MASK_(e))) +/** Macro: true if any event from the bitfield 'e' is interesting. */ +#define ANY_EVENT_IS_INTERESTING(e) \ + (!! (global_event_mask & (e))) + /** If we're using cookie-type authentication, how long should our cookies be? */ #define AUTHENTICATION_COOKIE_LEN 32 @@ -219,6 +224,7 @@ static void set_cached_network_liveness(int liveness); static void flush_queued_events_cb(mainloop_event_t *event, void *arg); static char * download_status_to_string(const download_status_t *dl); +static void control_get_bytes_rw_last_sec(uint64_t *r, uint64_t *w); /** Given a control event code for a message event, return the corresponding * log severity. */ @@ -271,6 +277,7 @@ control_update_global_event_mask(void) smartlist_t *conns = get_connection_array(); event_mask_t old_mask, new_mask; old_mask = global_event_mask; + int any_old_per_sec_events = control_any_per_second_event_enabled(); global_event_mask = 0; SMARTLIST_FOREACH(conns, connection_t *, _conn, @@ -288,10 +295,13 @@ control_update_global_event_mask(void) * we want to hear...*/ control_adjust_event_log_severity(); + /* Macro: true if ev was false before and is true now. */ +#define NEWLY_ENABLED(ev) \ + (! (old_mask & (ev)) && (new_mask & (ev))) + /* ...then, if we've started logging stream or circ bw, clear the * appropriate fields. */ - if (! (old_mask & EVENT_STREAM_BANDWIDTH_USED) && - (new_mask & EVENT_STREAM_BANDWIDTH_USED)) { + if (NEWLY_ENABLED(EVENT_STREAM_BANDWIDTH_USED)) { SMARTLIST_FOREACH(conns, connection_t *, conn, { if (conn->type == CONN_TYPE_AP) { @@ -300,10 +310,18 @@ control_update_global_event_mask(void) } }); } - if (! (old_mask & EVENT_CIRC_BANDWIDTH_USED) && - (new_mask & EVENT_CIRC_BANDWIDTH_USED)) { + if (NEWLY_ENABLED(EVENT_CIRC_BANDWIDTH_USED)) { clear_circ_bw_fields(); } + if (NEWLY_ENABLED(EVENT_BANDWIDTH_USED)) { + uint64_t r, w; + control_get_bytes_rw_last_sec(&r, &w); + } + if (any_old_per_sec_events != control_any_per_second_event_enabled()) { + reschedule_per_second_timer(); + } + +#undef NEWLY_ENABLED } /** Adjust the log severities that result in control_event_logmsg being called @@ -352,6 +370,65 @@ control_event_is_interesting(int event) return EVENT_IS_INTERESTING(event); } +/** Return true if any event that needs to fire once a second is enabled. */ +int +control_any_per_second_event_enabled(void) +{ + return ANY_EVENT_IS_INTERESTING( + EVENT_MASK_(EVENT_BANDWIDTH_USED) | + EVENT_MASK_(EVENT_CELL_STATS) | + EVENT_MASK_(EVENT_CIRC_BANDWIDTH_USED) | + EVENT_MASK_(EVENT_CONN_BW) | + EVENT_MASK_(EVENT_STREAM_BANDWIDTH_USED) + ); +} + +/* The value of 'get_bytes_read()' the previous time that + * control_get_bytes_rw_last_sec() as called. */ +static uint64_t stats_prev_n_read = 0; +/* The value of 'get_bytes_written()' the previous time that + * control_get_bytes_rw_last_sec() as called. */ +static uint64_t stats_prev_n_written = 0; + +/** + * Set <b>n_read</b> and <b>n_written</b> to the total number of bytes read + * and written by Tor since the last call to this function. + * + * Call this only from the main thread. + */ +static void +control_get_bytes_rw_last_sec(uint64_t *n_read, + uint64_t *n_written) +{ + const uint64_t stats_n_bytes_read = get_bytes_read(); + const uint64_t stats_n_bytes_written = get_bytes_written(); + + *n_read = stats_n_bytes_read - stats_prev_n_read; + *n_written = stats_n_bytes_written - stats_prev_n_written; + stats_prev_n_read = stats_n_bytes_read; + stats_prev_n_written = stats_n_bytes_written; +} + +/** + * Run all the controller events (if any) that are scheduled to trigger once + * per second. + */ +void +control_per_second_events(void) +{ + if (!control_any_per_second_event_enabled()) + return; + + uint64_t bytes_read, bytes_written; + control_get_bytes_rw_last_sec(&bytes_read, &bytes_written); + control_event_bandwidth_used((uint32_t)bytes_read,(uint32_t)bytes_written); + + control_event_stream_bandwidth_used(); + control_event_conn_bandwidth_used(); + control_event_circ_bandwidth_used(); + control_event_circuit_cell_stats(); +} + /** Append a NUL-terminated string <b>s</b> to the end of * <b>conn</b>-\>outbuf. */ @@ -1784,24 +1861,24 @@ getinfo_helper_misc(control_connection_t *conn, const char *question, } else if (!strcmp(question, "process/pid")) { int myPid = -1; - #ifdef _WIN32 +#ifdef _WIN32 myPid = _getpid(); - #else +#else myPid = getpid(); - #endif +#endif tor_asprintf(answer, "%d", myPid); } else if (!strcmp(question, "process/uid")) { - #ifdef _WIN32 +#ifdef _WIN32 *answer = tor_strdup("-1"); - #else +#else int myUid = geteuid(); tor_asprintf(answer, "%d", myUid); #endif /* defined(_WIN32) */ } else if (!strcmp(question, "process/user")) { - #ifdef _WIN32 +#ifdef _WIN32 *answer = tor_strdup(""); - #else +#else int myUid = geteuid(); const struct passwd *myPwEntry = tor_getpwuid(myUid); @@ -7035,6 +7112,8 @@ control_event_bootstrap_problem(const char *warn, const char *reason, if (bootstrap_problems >= BOOTSTRAP_PROBLEM_THRESHOLD) dowarn = 1; + /* Don't warn about our bootstrapping status if we are hibernating or + * shutting down. */ if (we_are_hibernating()) dowarn = 0; @@ -7606,6 +7685,8 @@ control_free_all(void) { smartlist_t *queued_events = NULL; + stats_prev_n_read = stats_prev_n_written = 0; + if (authentication_cookie) /* Free the auth cookie */ tor_free(authentication_cookie); if (detached_onion_services) { /* Free the detached onion services */ diff --git a/src/or/control.h b/src/or/control.h index 7f8a0bdb5f..92cbf866dd 100644 --- a/src/or/control.h +++ b/src/or/control.h @@ -40,6 +40,9 @@ int connection_control_process_inbuf(control_connection_t *conn); #define EVENT_NS 0x000F int control_event_is_interesting(int event); +void control_per_second_events(void); +int control_any_per_second_event_enabled(void); + int control_event_circuit_status(origin_circuit_t *circ, circuit_status_event_t e, int reason); int control_event_circuit_purpose_changed(origin_circuit_t *circ, diff --git a/src/or/dirserv.c b/src/or/dirserv.c index bfcec6e105..c01234e0b9 100644 --- a/src/or/dirserv.c +++ b/src/or/dirserv.c @@ -955,7 +955,7 @@ dirserv_set_router_is_running(routerinfo_t *router, time_t now) tor_assert(node); if (router_is_me(router)) { - /* We always know if we are down ourselves. */ + /* We always know if we are shutting down or hibernating ourselves. */ answer = ! we_are_hibernating(); } else if (router->is_hibernating && (router->cache_info.published_on + diff --git a/src/or/hibernate.c b/src/or/hibernate.c index 98f32adb1c..d7d259470f 100644 --- a/src/or/hibernate.c +++ b/src/or/hibernate.c @@ -52,6 +52,10 @@ static time_t hibernate_end_time = 0; * we aren't shutting down. */ static time_t shutdown_time = 0; +/** A timed event that we'll use when it's time to wake up from + * hibernation. */ +static mainloop_event_t *wakeup_event = NULL; + /** Possible accounting periods. */ typedef enum { UNIT_MONTH=1, UNIT_WEEK=2, UNIT_DAY=3, @@ -131,6 +135,8 @@ static time_t start_of_accounting_period_after(time_t now); static time_t start_of_accounting_period_containing(time_t now); static void accounting_set_wakeup_time(void); static void on_hibernate_state_change(hibernate_state_t prev_state); +static void hibernate_schedule_wakeup_event(time_t now, time_t end_time); +static void wakeup_event_callback(mainloop_event_t *ev, void *data); /** * Return the human-readable name for the hibernation state <b>state</b> @@ -877,13 +883,26 @@ hibernate_begin_shutdown(void) hibernate_begin(HIBERNATE_STATE_EXITING, time(NULL)); } -/** Return true iff we are currently hibernating. */ +/** + * Return true iff we are currently hibernating -- that is, if we are in + * any non-live state. + */ MOCK_IMPL(int, we_are_hibernating,(void)) { return hibernate_state != HIBERNATE_STATE_LIVE; } +/** + * Return true iff we are currently _fully_ hibernating -- that is, if we are + * in a state where we expect to handle no network activity at all. + */ +MOCK_IMPL(int, +we_are_fully_hibernating,(void)) +{ + return hibernate_state == HIBERNATE_STATE_DORMANT; +} + /** If we aren't currently dormant, close all connections and become * dormant. */ static void @@ -936,6 +955,63 @@ hibernate_go_dormant(time_t now) or_state_mark_dirty(get_or_state(), get_options()->AvoidDiskWrites ? now+600 : 0); + + hibernate_schedule_wakeup_event(now, hibernate_end_time); +} + +/** + * Schedule a mainloop event at <b>end_time</b> to wake up from a dormant + * state. We can't rely on this happening from second_elapsed_callback, + * since second_elapsed_callback will be shut down when we're dormant. + * + * (Note that We might immediately go back to sleep after we set the next + * wakeup time.) + */ +static void +hibernate_schedule_wakeup_event(time_t now, time_t end_time) +{ + struct timeval delay = { 0, 0 }; + + if (now >= end_time) { + // In these cases we always wait at least a second, to avoid running + // the callback in a tight loop. + delay.tv_sec = 1; + } else { + delay.tv_sec = (end_time - now); + } + + if (!wakeup_event) { + wakeup_event = mainloop_event_postloop_new(wakeup_event_callback, NULL); + } + + mainloop_event_schedule(wakeup_event, &delay); +} + +/** + * Called at the end of the interval, or at the wakeup time of the current + * interval, to exit the dormant state. + **/ +static void +wakeup_event_callback(mainloop_event_t *ev, void *data) +{ + (void) ev; + (void) data; + + const time_t now = time(NULL); + accounting_run_housekeeping(now); + consider_hibernation(now); + if (hibernate_state != HIBERNATE_STATE_DORMANT) { + /* We woke up, so everything's great here */ + return; + } + + /* We're still dormant. */ + if (now < interval_wakeup_time) + hibernate_end_time = interval_wakeup_time; + else + hibernate_end_time = interval_end_time; + + hibernate_schedule_wakeup_event(now, hibernate_end_time); } /** Called when hibernate_end_time has arrived. */ @@ -1124,6 +1200,18 @@ 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 */ +void +accounting_free_all(void) +{ + mainloop_event_free(wakeup_event); + hibernate_state = HIBERNATE_STATE_INITIAL; + hibernate_end_time = 0; + shutdown_time = 0; } #ifdef TOR_UNIT_TESTS diff --git a/src/or/hibernate.h b/src/or/hibernate.h index 85fb42864b..453969d052 100644 --- a/src/or/hibernate.h +++ b/src/or/hibernate.h @@ -25,11 +25,13 @@ void accounting_add_bytes(size_t n_read, size_t n_written, int seconds); int accounting_record_bandwidth_usage(time_t now, or_state_t *state); void hibernate_begin_shutdown(void); MOCK_DECL(int, we_are_hibernating, (void)); +MOCK_DECL(int, we_are_fully_hibernating,(void)); void consider_hibernation(time_t now); int getinfo_helper_accounting(control_connection_t *conn, const char *question, char **answer, const char **errmsg); uint64_t get_accounting_max_total(void); +void accounting_free_all(void); #ifdef HIBERNATE_PRIVATE /** Possible values of hibernate_state */ diff --git a/src/or/hs_descriptor.c b/src/or/hs_descriptor.c index eb48cb0601..096122392d 100644 --- a/src/or/hs_descriptor.c +++ b/src/or/hs_descriptor.c @@ -1897,7 +1897,7 @@ desc_sig_is_valid(const char *b64_sig, } /* Find the start of signature. */ - sig_start = tor_memstr(encoded_desc, encoded_len, "\n" str_signature); + sig_start = tor_memstr(encoded_desc, encoded_len, "\n" str_signature " "); /* Getting here means the token parsing worked for the signature so if we * can't find the start of the signature, we have a code flow issue. */ if (!sig_start) { diff --git a/src/or/hs_service.c b/src/or/hs_service.c index b7296ddcf9..9001a521ab 100644 --- a/src/or/hs_service.c +++ b/src/or/hs_service.c @@ -878,11 +878,6 @@ register_all_services(void) tor_assert(hs_service_staging_list); - /* We'll save us some allocation and computing time. */ - if (smartlist_len(hs_service_staging_list) == 0) { - return; - } - /* Allocate a new map that will replace the current one. */ new_service_map = tor_malloc_zero(sizeof(*new_service_map)); HT_INIT(hs_service_ht, new_service_map); diff --git a/src/or/main.c b/src/or/main.c index c3505a2d91..9dce158b33 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -163,11 +163,6 @@ token_bucket_rw_t global_bucket; /* Token bucket for relayed traffic. */ token_bucket_rw_t global_relayed_bucket; -/* DOCDOC stats_prev_n_read */ -static uint64_t stats_prev_n_read = 0; -/* DOCDOC stats_prev_n_written */ -static uint64_t stats_prev_n_written = 0; - /* XXX we might want to keep stats about global_relayed_*_bucket too. Or not.*/ /** How many bytes have we read since we started the process? */ static uint64_t stats_n_bytes_read = 0; @@ -1258,7 +1253,8 @@ run_connection_housekeeping(int i, time_t now) } else if (we_are_hibernating() && ! have_any_circuits && !connection_get_outbuf_len(conn)) { - /* We're hibernating, there's no circuits, and nothing to flush.*/ + /* We're hibernating or shutting down, there's no circuits, and nothing to + * flush.*/ log_info(LD_OR,"Expiring non-used OR connection to fd %d (%s:%d) " "[Hibernating or exiting].", (int)conn->s,conn->address, conn->port); @@ -1497,7 +1493,7 @@ get_my_roles(const or_options_t *options) int roles = 0; int is_bridge = options->BridgeRelay; - int is_client = any_client_port_set(options); + int is_client = options_any_client_port_set(options); int is_relay = server_mode(options); int is_dirauth = authdir_mode_v3(options); int is_bridgeauth = authdir_mode_bridge(options); @@ -1676,6 +1672,11 @@ static mainloop_event_t *postloop_cleanup_ev=NULL; void mainloop_schedule_postloop_cleanup(void) { + if (PREDICT_UNLIKELY(postloop_cleanup_ev == NULL)) { + // (It's possible that we can get here if we decide to close a connection + // in the earliest stages of our configuration, before we create events.) + return; + } mainloop_event_activate(postloop_cleanup_ev); } @@ -2495,10 +2496,100 @@ hs_service_callback(time_t now, const or_options_t *options) /** Timer: used to invoke second_elapsed_callback() once per second. */ static periodic_timer_t *second_timer = NULL; -/** Number of libevent errors in the last second: we die if we get too many. */ -static int n_libevent_errors = 0; -/** Last time that second_elapsed_callback was called. */ + +/** + * Enable or disable the per-second timer as appropriate, creating it if + * necessary. + */ +void +reschedule_per_second_timer(void) +{ + struct timeval one_second; + one_second.tv_sec = 1; + one_second.tv_usec = 0; + + if (! second_timer) { + second_timer = periodic_timer_new(tor_libevent_get_base(), + &one_second, + second_elapsed_callback, + NULL); + tor_assert(second_timer); + } + + 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); + } +} + +/** Last time that update_current_time was called. */ static time_t current_second = 0; +/** Last time that update_current_time updated current_second. */ +static monotime_coarse_t current_second_last_changed; + +/** + * Set the current time to "now", which should be the value returned by + * time(). Check for clock jumps and track the total number of seconds we + * have been running. + */ +void +update_current_time(time_t now) +{ + if (PREDICT_LIKELY(now == current_second)) { + /* We call this function a lot. Most frequently, the current second + * will not have changed, so we just return. */ + return; + } + + const time_t seconds_elapsed = current_second ? (now - current_second) : 0; + + /* Check the wall clock against the monotonic clock, so we can + * better tell idleness from clock jumps and/or other shenanigans. */ + monotime_coarse_t last_updated; + memcpy(&last_updated, ¤t_second_last_changed, sizeof(last_updated)); + monotime_coarse_get(¤t_second_last_changed); + + /** How much clock jumping do we tolerate? */ +#define NUM_JUMPED_SECONDS_BEFORE_WARN 100 + + /** How much idleness do we tolerate? */ +#define NUM_IDLE_SECONDS_BEFORE_WARN 3600 + + if (seconds_elapsed < -NUM_JUMPED_SECONDS_BEFORE_WARN) { + // moving back in time is always a bad sign. + circuit_note_clock_jumped(seconds_elapsed, false); + } else if (seconds_elapsed >= NUM_JUMPED_SECONDS_BEFORE_WARN) { + /* Compare the monotonic clock to the result of time(). */ + const int32_t monotime_msec_passed = + monotime_coarse_diff_msec32(&last_updated, + ¤t_second_last_changed); + const int monotime_sec_passed = monotime_msec_passed / 1000; + const int discrepancy = monotime_sec_passed - (int)seconds_elapsed; + /* If the monotonic clock deviates from time(NULL), we have a couple of + * possibilities. On some systems, this means we have been suspended or + * sleeping. Everywhere, it can mean that the wall-clock time has + * been changed -- for example, with settimeofday(). + * + * On the other hand, if the monotonic time matches with the wall-clock + * time, we've probably just been idle for a while, with no events firing. + * we tolerate much more of that. + */ + const bool clock_jumped = abs(discrepancy) > 2; + + if (clock_jumped || seconds_elapsed >= NUM_IDLE_SECONDS_BEFORE_WARN) { + circuit_note_clock_jumped(seconds_elapsed, ! clock_jumped); + } + } else if (seconds_elapsed > 0) { + stats_n_seconds_working += seconds_elapsed; + } + + update_approx_time(now); + current_second = now; +} /** Libevent callback: invoked once every second. */ static void @@ -2508,43 +2599,21 @@ second_elapsed_callback(periodic_timer_t *timer, void *arg) * could use Libevent's timers for this rather than checking the current * time against a bunch of timeouts every second. */ time_t now; - size_t bytes_written; - size_t bytes_read; - int seconds_elapsed; (void)timer; (void)arg; - n_libevent_errors = 0; - - /* log_notice(LD_GENERAL, "Tick."); */ now = time(NULL); - update_approx_time(now); - /* the second has rolled over. check more stuff. */ - seconds_elapsed = current_second ? (int)(now - current_second) : 0; - bytes_read = (size_t)(stats_n_bytes_read - stats_prev_n_read); - bytes_written = (size_t)(stats_n_bytes_written - stats_prev_n_written); - stats_prev_n_read = stats_n_bytes_read; - stats_prev_n_written = stats_n_bytes_written; - - control_event_bandwidth_used((uint32_t)bytes_read,(uint32_t)bytes_written); - control_event_stream_bandwidth_used(); - control_event_conn_bandwidth_used(); - control_event_circ_bandwidth_used(); - control_event_circuit_cell_stats(); - -/** If more than this many seconds have elapsed, probably the clock - * jumped: doesn't count. */ -#define NUM_JUMPED_SECONDS_BEFORE_WARN 100 - if (seconds_elapsed < -NUM_JUMPED_SECONDS_BEFORE_WARN || - seconds_elapsed >= NUM_JUMPED_SECONDS_BEFORE_WARN) { - circuit_note_clock_jumped(seconds_elapsed); - } else if (seconds_elapsed > 0) - stats_n_seconds_working += seconds_elapsed; + /* 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); - run_scheduled_events(now); + /* Maybe some controller events are ready to fire */ + control_per_second_events(); - current_second = now; /* remember which second it is, for next time */ + run_scheduled_events(now); } #ifdef HAVE_SYSTEMD_209 @@ -2560,21 +2629,6 @@ systemd_watchdog_callback(periodic_timer_t *timer, void *arg) } #endif /* defined(HAVE_SYSTEMD_209) */ -#ifndef _WIN32 -/** Called when a possibly ignorable libevent error occurs; ensures that we - * don't get into an infinite loop by ignoring too many errors from - * libevent. */ -static int -got_libevent_error(void) -{ - if (++n_libevent_errors > 8) { - log_err(LD_NET, "Too many libevent errors in one second; dying"); - return -1; - } - return 0; -} -#endif /* !defined(_WIN32) */ - #define UPTIME_CUTOFF_FOR_NEW_BANDWIDTH_TEST (6*60*60) /** Called when our IP address seems to have changed. <b>at_interface</b> @@ -2837,17 +2891,7 @@ do_main_loop(void) } /* set up once-a-second callback. */ - if (! second_timer) { - struct timeval one_second; - one_second.tv_sec = 1; - one_second.tv_usec = 0; - - second_timer = periodic_timer_new(tor_libevent_get_base(), - &one_second, - second_elapsed_callback, - NULL); - tor_assert(second_timer); - } + reschedule_per_second_timer(); #ifdef HAVE_SYSTEMD_209 uint64_t watchdog_delay; @@ -2916,6 +2960,11 @@ do_main_loop(void) return run_main_loop_until_done(); } +#ifndef _WIN32 +/** Rate-limiter for EINVAL-type libevent warnings. */ +static ratelim_t libevent_error_ratelim = RATELIM_INIT(10); +#endif + /** * Run the main loop a single time. Return 0 for "exit"; -1 for "exit with * error", and 1 for "run this again." @@ -2981,9 +3030,12 @@ run_main_loop_once(void) return -1; #ifndef _WIN32 } else if (e == EINVAL) { - log_warn(LD_NET, "EINVAL from libevent: should you upgrade libevent?"); - if (got_libevent_error()) + log_fn_ratelim(&libevent_error_ratelim, LOG_WARN, LD_NET, + "EINVAL from libevent: should you upgrade libevent?"); + if (libevent_error_ratelim.n_calls_since_last_time > 8) { + log_err(LD_NET, "Too many libevent errors, too fast: dying"); return -1; + } #endif /* !defined(_WIN32) */ } else { tor_assert_nonfatal_once(! ERRNO_IS_EINPROGRESS(e)); @@ -3029,6 +3081,7 @@ signal_callback(evutil_socket_t fd, short events, void *arg) (void)fd; (void)events; + update_current_time(time(NULL)); process_signal(sig); } @@ -3618,6 +3671,8 @@ tor_free_all(int postfork) hs_free_all(); dos_free_all(); circuitmux_ewma_free_all(); + accounting_free_all(); + if (!postfork) { config_free_all(); or_state_free_all(); @@ -3651,7 +3706,6 @@ tor_free_all(int postfork) memset(&global_bucket, 0, sizeof(global_bucket)); memset(&global_relayed_bucket, 0, sizeof(global_relayed_bucket)); - stats_prev_n_read = stats_prev_n_written = 0; stats_n_bytes_read = stats_n_bytes_written = 0; time_of_process_start = 0; time_of_last_signewnym = 0; @@ -3665,8 +3719,9 @@ tor_free_all(int postfork) should_init_bridge_stats = 1; dns_honesty_first_time = 1; heartbeat_callback_first_time = 1; - n_libevent_errors = 0; current_second = 0; + memset(¤t_second_last_changed, 0, + sizeof(current_second_last_changed)); if (!postfork) { release_lockfile(); diff --git a/src/or/main.h b/src/or/main.h index a312b51e05..9dbbc6e5ee 100644 --- a/src/or/main.h +++ b/src/or/main.h @@ -66,6 +66,8 @@ void reschedule_dirvote(const or_options_t *options); void mainloop_schedule_postloop_cleanup(void); void rescan_periodic_events(const or_options_t *options); +void update_current_time(time_t now); + MOCK_DECL(long,get_uptime,(void)); MOCK_DECL(void,reset_uptime,(void)); @@ -92,6 +94,7 @@ 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); extern time_t time_of_process_start; extern int quiet_level; diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c index 51b2f4af15..998eaf74e6 100644 --- a/src/or/networkstatus.c +++ b/src/or/networkstatus.c @@ -1691,24 +1691,6 @@ networkstatus_set_current_consensus_from_ns(networkstatus_t *c, #endif /* defined(TOR_UNIT_TESTS) */ /** - * Return true if any option is set in <b>options</b> to make us behave - * as a client. - * - * XXXX If we need this elsewhere at any point, we should make it nonstatic - * XXXX and move it into another file. - */ -int -any_client_port_set(const or_options_t *options) -{ - return (options->SocksPort_set || - options->TransPort_set || - options->NATDPort_set || - options->ControlPort_set || - options->DNSPort_set || - options->HTTPTunnelPort_set); -} - -/** * Helper for handle_missing_protocol_warning: handles either the * client case (if <b>is_client</b> is set) or the server case otherwise. */ @@ -1743,7 +1725,7 @@ handle_missing_protocol_warning(const networkstatus_t *c, const or_options_t *options) { const int is_server = server_mode(options); - const int is_client = any_client_port_set(options) || !is_server; + const int is_client = options_any_client_port_set(options) || !is_server; if (is_server) handle_missing_protocol_warning_impl(c, 0); diff --git a/src/or/networkstatus.h b/src/or/networkstatus.h index b133f43dc1..94f85c3c29 100644 --- a/src/or/networkstatus.h +++ b/src/or/networkstatus.h @@ -147,8 +147,6 @@ void vote_routerstatus_free_(vote_routerstatus_t *rs); #define vote_routerstatus_free(rs) \ FREE_AND_NULL(vote_routerstatus_t, vote_routerstatus_free_, (rs)) -int any_client_port_set(const or_options_t *options); - #ifdef NETWORKSTATUS_PRIVATE #ifdef TOR_UNIT_TESTS STATIC int networkstatus_set_current_consensus_from_ns(networkstatus_t *c, diff --git a/src/or/periodic.c b/src/or/periodic.c index 76aa418b35..92fa677f8f 100644 --- a/src/or/periodic.c +++ b/src/or/periodic.c @@ -14,6 +14,7 @@ #include "or.h" #include "compat_libevent.h" #include "config.h" +#include "main.h" #include "periodic.h" /** We disable any interval greater than this number of seconds, on the @@ -48,6 +49,7 @@ periodic_event_dispatch(mainloop_event_t *ev, void *data) } time_t now = time(NULL); + update_current_time(now); const or_options_t *options = get_options(); // log_debug(LD_GENERAL, "Dispatching %s", event->name); int r = event->fn(now, options); diff --git a/src/or/rendservice.c b/src/or/rendservice.c index 43600cd913..92c323b10d 100644 --- a/src/or/rendservice.c +++ b/src/or/rendservice.c @@ -629,10 +629,11 @@ void rend_service_prune_list(void) { smartlist_t *old_service_list = rend_service_list; - /* Don't try to prune anything if we have no staging list. */ + if (!rend_service_staging_list) { - return; + rend_service_staging_list = smartlist_new(); } + rend_service_prune_list_impl_(); if (old_service_list) { /* Every remaining service in the old list have been removed from the diff --git a/src/or/router.c b/src/or/router.c index 996a28a91f..07abf1f8d5 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -1599,15 +1599,24 @@ router_perform_bandwidth_test(int num_circs, time_t now) } } -/** Return true iff our network is in some sense disabled: either we're - * hibernating, entering hibernation, or the network is turned off with - * DisableNetwork. */ +/** Return true iff our network is in some sense disabled or shutting down: + * either we're hibernating, entering hibernation, or the network is turned + * off with DisableNetwork. */ int net_is_disabled(void) { return get_options()->DisableNetwork || we_are_hibernating(); } +/** Return true iff our network is in some sense "completely disabled" either + * we're fully hibernating or the network is turned off with + * DisableNetwork. */ +int +net_is_completely_disabled(void) +{ + return get_options()->DisableNetwork || we_are_fully_hibernating(); +} + /** Return true iff we believe ourselves to be an authoritative * directory server. */ @@ -2268,6 +2277,7 @@ router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e) /* and compute ri->bandwidthburst similarly */ ri->bandwidthburst = get_effective_bwburst(options); + /* Report bandwidth, unless we're hibernating or shutting down */ ri->bandwidthcapacity = hibernating ? 0 : rep_hist_bandwidth_assess(); if (dns_seems_to_be_broken() || has_dns_init_failed()) { @@ -2538,6 +2548,8 @@ check_descriptor_bandwidth_changed(time_t now) return; prev = router_get_my_routerinfo()->bandwidthcapacity; + /* Consider ourselves to have zero bandwidth if we're hibernating or + * shutting down. */ cur = we_are_hibernating() ? 0 : rep_hist_bandwidth_assess(); if ((prev != cur && (!prev || !cur)) || cur > prev*2 || diff --git a/src/or/router.h b/src/or/router.h index 03eca9c65d..0db2c1cfb2 100644 --- a/src/or/router.h +++ b/src/or/router.h @@ -53,6 +53,7 @@ void router_dirport_found_reachable(void); void router_perform_bandwidth_test(int num_circs, time_t now); int net_is_disabled(void); +int net_is_completely_disabled(void); int authdir_mode(const or_options_t *options); int authdir_mode_handles_descs(const or_options_t *options, int purpose); |