|
@@ -76,38 +76,29 @@
|
|
|
|
|
|
#define REPHIST_PRIVATE
|
|
|
#include "core/or/or.h"
|
|
|
-#include "core/or/circuitlist.h"
|
|
|
-#include "core/or/circuituse.h"
|
|
|
#include "app/config/config.h"
|
|
|
-#include "lib/crypt_ops/crypto_rand.h"
|
|
|
+#include "app/config/statefile.h"
|
|
|
+#include "core/or/circuitlist.h"
|
|
|
+#include "core/or/connection_or.h"
|
|
|
+#include "feature/dirauth/authmode.h"
|
|
|
#include "feature/nodelist/networkstatus.h"
|
|
|
#include "feature/nodelist/nodelist.h"
|
|
|
-#include "feature/stats/rephist.h"
|
|
|
#include "feature/relay/routermode.h"
|
|
|
-#include "feature/relay/selftest.h"
|
|
|
-#include "feature/nodelist/routerlist.h"
|
|
|
-#include "ht.h"
|
|
|
-#include "core/or/channelpadding.h"
|
|
|
-#include "core/or/connection_or.h"
|
|
|
-#include "app/config/statefile.h"
|
|
|
-#include "feature/dirauth/authmode.h"
|
|
|
+#include "feature/stats/predict_ports.h"
|
|
|
+#include "feature/stats/rephist.h"
|
|
|
+#include "lib/container/order.h"
|
|
|
+#include "lib/crypt_ops/crypto_rand.h"
|
|
|
+#include "lib/math/laplace.h"
|
|
|
|
|
|
#include "feature/nodelist/networkstatus_st.h"
|
|
|
#include "core/or/or_circuit_st.h"
|
|
|
#include "app/config/or_state_st.h"
|
|
|
|
|
|
-#include "lib/container/bloomfilt.h"
|
|
|
-#include "lib/container/order.h"
|
|
|
-#include "lib/math/fp.h"
|
|
|
-#include "lib/math/laplace.h"
|
|
|
-#include "lib/time/tvdiff.h"
|
|
|
-
|
|
|
#ifdef HAVE_FCNTL_H
|
|
|
#include <fcntl.h>
|
|
|
#endif
|
|
|
|
|
|
static void bw_arrays_init(void);
|
|
|
-static void predicted_ports_alloc(void);
|
|
|
|
|
|
/** Total number of bytes currently allocated in fields used by rephist.c. */
|
|
|
uint64_t rephist_total_alloc=0;
|
|
@@ -242,7 +233,6 @@ rep_hist_init(void)
|
|
|
{
|
|
|
history_map = digestmap_new();
|
|
|
bw_arrays_init();
|
|
|
- predicted_ports_alloc();
|
|
|
}
|
|
|
|
|
|
/** We have just decided that this router with identity digest <b>id</b> is
|
|
@@ -1537,287 +1527,6 @@ rep_hist_load_state(or_state_t *state, char **err)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-/*********************************************************************/
|
|
|
-
|
|
|
-/** A single predicted port: used to remember which ports we've made
|
|
|
- * connections to, so that we can try to keep making circuits that can handle
|
|
|
- * those ports. */
|
|
|
-typedef struct predicted_port_t {
|
|
|
- /** The port we connected to */
|
|
|
- uint16_t port;
|
|
|
- /** The time at which we last used it */
|
|
|
- time_t time;
|
|
|
-} predicted_port_t;
|
|
|
-
|
|
|
-/** A list of port numbers that have been used recently. */
|
|
|
-static smartlist_t *predicted_ports_list=NULL;
|
|
|
-/** How long do we keep predicting circuits? */
|
|
|
-static time_t prediction_timeout=0;
|
|
|
-/** When was the last time we added a prediction entry (HS or port) */
|
|
|
-static time_t last_prediction_add_time=0;
|
|
|
-
|
|
|
-/**
|
|
|
- * How much time left until we stop predicting circuits?
|
|
|
- */
|
|
|
-int
|
|
|
-predicted_ports_prediction_time_remaining(time_t now)
|
|
|
-{
|
|
|
- time_t seconds_waited;
|
|
|
- time_t seconds_left;
|
|
|
-
|
|
|
- /* Protect against overflow of return value. This can happen if the clock
|
|
|
- * jumps backwards in time. Update the last prediction time (aka last
|
|
|
- * active time) to prevent it. This update is preferable to using monotonic
|
|
|
- * time because it prevents clock jumps into the past from simply causing
|
|
|
- * very long idle timeouts while the monotonic time stands still. */
|
|
|
- seconds_waited = time_diff(last_prediction_add_time, now);
|
|
|
- if (seconds_waited == TIME_MAX) {
|
|
|
- last_prediction_add_time = now;
|
|
|
- seconds_waited = 0;
|
|
|
- }
|
|
|
-
|
|
|
- /* Protect against underflow of the return value. This can happen for very
|
|
|
- * large periods of inactivity/system sleep. */
|
|
|
- if (seconds_waited > prediction_timeout)
|
|
|
- return 0;
|
|
|
-
|
|
|
- seconds_left = time_diff(seconds_waited, prediction_timeout);
|
|
|
- if (BUG(seconds_left == TIME_MAX))
|
|
|
- return INT_MAX;
|
|
|
-
|
|
|
- return (int)(seconds_left);
|
|
|
-}
|
|
|
-
|
|
|
-/** We just got an application request for a connection with
|
|
|
- * port <b>port</b>. Remember it for the future, so we can keep
|
|
|
- * some circuits open that will exit to this port.
|
|
|
- */
|
|
|
-static void
|
|
|
-add_predicted_port(time_t now, uint16_t port)
|
|
|
-{
|
|
|
- predicted_port_t *pp = tor_malloc(sizeof(predicted_port_t));
|
|
|
-
|
|
|
- // If the list is empty, re-randomize predicted ports lifetime
|
|
|
- if (!any_predicted_circuits(now)) {
|
|
|
- prediction_timeout =
|
|
|
- (time_t)channelpadding_get_circuits_available_timeout();
|
|
|
- }
|
|
|
-
|
|
|
- last_prediction_add_time = now;
|
|
|
-
|
|
|
- log_info(LD_CIRC,
|
|
|
- "New port prediction added. Will continue predictive circ building "
|
|
|
- "for %d more seconds.",
|
|
|
- predicted_ports_prediction_time_remaining(now));
|
|
|
-
|
|
|
- pp->port = port;
|
|
|
- pp->time = now;
|
|
|
- rephist_total_alloc += sizeof(*pp);
|
|
|
- smartlist_add(predicted_ports_list, pp);
|
|
|
-}
|
|
|
-
|
|
|
-/**
|
|
|
- * Allocate whatever memory and structs are needed for predicting
|
|
|
- * which ports will be used. Also seed it with port 80, so we'll build
|
|
|
- * circuits on start-up.
|
|
|
- */
|
|
|
-static void
|
|
|
-predicted_ports_alloc(void)
|
|
|
-{
|
|
|
- predicted_ports_list = smartlist_new();
|
|
|
-}
|
|
|
-
|
|
|
-void
|
|
|
-predicted_ports_init(void)
|
|
|
-{
|
|
|
- add_predicted_port(time(NULL), 443); // Add a port to get us started
|
|
|
-}
|
|
|
-
|
|
|
-/** Free whatever memory is needed for predicting which ports will
|
|
|
- * be used.
|
|
|
- */
|
|
|
-static void
|
|
|
-predicted_ports_free_all(void)
|
|
|
-{
|
|
|
- rephist_total_alloc -=
|
|
|
- smartlist_len(predicted_ports_list)*sizeof(predicted_port_t);
|
|
|
- SMARTLIST_FOREACH(predicted_ports_list, predicted_port_t *,
|
|
|
- pp, tor_free(pp));
|
|
|
- smartlist_free(predicted_ports_list);
|
|
|
-}
|
|
|
-
|
|
|
-/** Remember that <b>port</b> has been asked for as of time <b>now</b>.
|
|
|
- * This is used for predicting what sorts of streams we'll make in the
|
|
|
- * future and making exit circuits to anticipate that.
|
|
|
- */
|
|
|
-void
|
|
|
-rep_hist_note_used_port(time_t now, uint16_t port)
|
|
|
-{
|
|
|
- tor_assert(predicted_ports_list);
|
|
|
-
|
|
|
- if (!port) /* record nothing */
|
|
|
- return;
|
|
|
-
|
|
|
- SMARTLIST_FOREACH_BEGIN(predicted_ports_list, predicted_port_t *, pp) {
|
|
|
- if (pp->port == port) {
|
|
|
- pp->time = now;
|
|
|
-
|
|
|
- last_prediction_add_time = now;
|
|
|
- log_info(LD_CIRC,
|
|
|
- "New port prediction added. Will continue predictive circ "
|
|
|
- "building for %d more seconds.",
|
|
|
- predicted_ports_prediction_time_remaining(now));
|
|
|
- return;
|
|
|
- }
|
|
|
- } SMARTLIST_FOREACH_END(pp);
|
|
|
- /* it's not there yet; we need to add it */
|
|
|
- add_predicted_port(now, port);
|
|
|
-}
|
|
|
-
|
|
|
-/** Return a newly allocated pointer to a list of uint16_t * for ports that
|
|
|
- * are likely to be asked for in the near future.
|
|
|
- */
|
|
|
-smartlist_t *
|
|
|
-rep_hist_get_predicted_ports(time_t now)
|
|
|
-{
|
|
|
- int predicted_circs_relevance_time;
|
|
|
- smartlist_t *out = smartlist_new();
|
|
|
- tor_assert(predicted_ports_list);
|
|
|
-
|
|
|
- predicted_circs_relevance_time = (int)prediction_timeout;
|
|
|
-
|
|
|
- /* clean out obsolete entries */
|
|
|
- SMARTLIST_FOREACH_BEGIN(predicted_ports_list, predicted_port_t *, pp) {
|
|
|
- if (pp->time + predicted_circs_relevance_time < now) {
|
|
|
- log_debug(LD_CIRC, "Expiring predicted port %d", pp->port);
|
|
|
-
|
|
|
- rephist_total_alloc -= sizeof(predicted_port_t);
|
|
|
- tor_free(pp);
|
|
|
- SMARTLIST_DEL_CURRENT(predicted_ports_list, pp);
|
|
|
- } else {
|
|
|
- smartlist_add(out, tor_memdup(&pp->port, sizeof(uint16_t)));
|
|
|
- }
|
|
|
- } SMARTLIST_FOREACH_END(pp);
|
|
|
- return out;
|
|
|
-}
|
|
|
-
|
|
|
-/**
|
|
|
- * Take a list of uint16_t *, and remove every port in the list from the
|
|
|
- * current list of predicted ports.
|
|
|
- */
|
|
|
-void
|
|
|
-rep_hist_remove_predicted_ports(const smartlist_t *rmv_ports)
|
|
|
-{
|
|
|
- /* Let's do this on O(N), not O(N^2). */
|
|
|
- bitarray_t *remove_ports = bitarray_init_zero(UINT16_MAX);
|
|
|
- SMARTLIST_FOREACH(rmv_ports, const uint16_t *, p,
|
|
|
- bitarray_set(remove_ports, *p));
|
|
|
- SMARTLIST_FOREACH_BEGIN(predicted_ports_list, predicted_port_t *, pp) {
|
|
|
- if (bitarray_is_set(remove_ports, pp->port)) {
|
|
|
- tor_free(pp);
|
|
|
- rephist_total_alloc -= sizeof(*pp);
|
|
|
- SMARTLIST_DEL_CURRENT(predicted_ports_list, pp);
|
|
|
- }
|
|
|
- } SMARTLIST_FOREACH_END(pp);
|
|
|
- bitarray_free(remove_ports);
|
|
|
-}
|
|
|
-
|
|
|
-/** The user asked us to do a resolve. Rather than keeping track of
|
|
|
- * timings and such of resolves, we fake it for now by treating
|
|
|
- * it the same way as a connection to port 80. This way we will continue
|
|
|
- * to have circuits lying around if the user only uses Tor for resolves.
|
|
|
- */
|
|
|
-void
|
|
|
-rep_hist_note_used_resolve(time_t now)
|
|
|
-{
|
|
|
- rep_hist_note_used_port(now, 80);
|
|
|
-}
|
|
|
-
|
|
|
-/** The last time at which we needed an internal circ. */
|
|
|
-static time_t predicted_internal_time = 0;
|
|
|
-/** The last time we needed an internal circ with good uptime. */
|
|
|
-static time_t predicted_internal_uptime_time = 0;
|
|
|
-/** The last time we needed an internal circ with good capacity. */
|
|
|
-static time_t predicted_internal_capacity_time = 0;
|
|
|
-
|
|
|
-/** Remember that we used an internal circ at time <b>now</b>. */
|
|
|
-void
|
|
|
-rep_hist_note_used_internal(time_t now, int need_uptime, int need_capacity)
|
|
|
-{
|
|
|
- // If the list is empty, re-randomize predicted ports lifetime
|
|
|
- if (!any_predicted_circuits(now)) {
|
|
|
- prediction_timeout = channelpadding_get_circuits_available_timeout();
|
|
|
- }
|
|
|
-
|
|
|
- last_prediction_add_time = now;
|
|
|
-
|
|
|
- log_info(LD_CIRC,
|
|
|
- "New port prediction added. Will continue predictive circ building "
|
|
|
- "for %d more seconds.",
|
|
|
- predicted_ports_prediction_time_remaining(now));
|
|
|
-
|
|
|
- predicted_internal_time = now;
|
|
|
- if (need_uptime)
|
|
|
- predicted_internal_uptime_time = now;
|
|
|
- if (need_capacity)
|
|
|
- predicted_internal_capacity_time = now;
|
|
|
-}
|
|
|
-
|
|
|
-/** Return 1 if we've used an internal circ recently; else return 0. */
|
|
|
-int
|
|
|
-rep_hist_get_predicted_internal(time_t now, int *need_uptime,
|
|
|
- int *need_capacity)
|
|
|
-{
|
|
|
- int predicted_circs_relevance_time;
|
|
|
-
|
|
|
- predicted_circs_relevance_time = (int)prediction_timeout;
|
|
|
-
|
|
|
- if (!predicted_internal_time) { /* initialize it */
|
|
|
- predicted_internal_time = now;
|
|
|
- predicted_internal_uptime_time = now;
|
|
|
- predicted_internal_capacity_time = now;
|
|
|
- }
|
|
|
- if (predicted_internal_time + predicted_circs_relevance_time < now)
|
|
|
- return 0; /* too long ago */
|
|
|
- if (predicted_internal_uptime_time + predicted_circs_relevance_time >= now)
|
|
|
- *need_uptime = 1;
|
|
|
- // Always predict that we need capacity.
|
|
|
- *need_capacity = 1;
|
|
|
- return 1;
|
|
|
-}
|
|
|
-
|
|
|
-/** Any ports used lately? These are pre-seeded if we just started
|
|
|
- * up or if we're running a hidden service. */
|
|
|
-int
|
|
|
-any_predicted_circuits(time_t now)
|
|
|
-{
|
|
|
- int predicted_circs_relevance_time;
|
|
|
- predicted_circs_relevance_time = (int)prediction_timeout;
|
|
|
-
|
|
|
- return smartlist_len(predicted_ports_list) ||
|
|
|
- predicted_internal_time + predicted_circs_relevance_time >= now;
|
|
|
-}
|
|
|
-
|
|
|
-/** Return 1 if we have no need for circuits currently, else return 0. */
|
|
|
-int
|
|
|
-rep_hist_circbuilding_dormant(time_t now)
|
|
|
-{
|
|
|
- const or_options_t *options = get_options();
|
|
|
-
|
|
|
- if (any_predicted_circuits(now))
|
|
|
- return 0;
|
|
|
-
|
|
|
- /* see if we'll still need to build testing circuits */
|
|
|
- if (server_mode(options) &&
|
|
|
- (!check_whether_orport_reachable(options) ||
|
|
|
- !circuit_enough_testing_circs()))
|
|
|
- return 0;
|
|
|
- if (!check_whether_dirport_reachable(options))
|
|
|
- return 0;
|
|
|
-
|
|
|
- return 1;
|
|
|
-}
|
|
|
-
|
|
|
/*** Exit port statistics ***/
|
|
|
|
|
|
/* Some constants */
|