tor

The Tor anonymity network
git clone https://git.dasho.dev/tor.git
Log | Files | Refs | README | LICENSE

predict_ports.c (9862B)


      1 /* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
      2 * Copyright (c) 2007-2021, The Tor Project, Inc. */
      3 /* See LICENSE for licensing information */
      4 
      5 /**
      6 * \file predict_ports.c
      7 * \brief Remember what ports we've needed so we can have circuits ready.
      8 *
      9 * Predicted ports are used by clients to remember how long it's been
     10 * since they opened an exit connection to each given target
     11 * port. Clients use this information in order to try to keep circuits
     12 * open to exit nodes that can connect to the ports that they care
     13 * about.  (The predicted ports mechanism also handles predicted circuit
     14 * usage that _isn't_ port-specific, such as resolves, internal circuits,
     15 * and so on.)
     16 **/
     17 
     18 #include "core/or/or.h"
     19 
     20 #include "app/config/config.h"
     21 #include "core/or/channelpadding.h"
     22 #include "core/or/circuituse.h"
     23 #include "feature/relay/routermode.h"
     24 #include "feature/relay/selftest.h"
     25 #include "feature/stats/predict_ports.h"
     26 #include "lib/container/bitarray.h"
     27 #include "lib/time/tvdiff.h"
     28 
     29 static size_t predicted_ports_total_alloc = 0;
     30 
     31 static void predicted_ports_alloc(void);
     32 
     33 /** A single predicted port: used to remember which ports we've made
     34 * connections to, so that we can try to keep making circuits that can handle
     35 * those ports. */
     36 typedef struct predicted_port_t {
     37  /** The port we connected to */
     38  uint16_t port;
     39  /** The time at which we last used it */
     40  time_t time;
     41 } predicted_port_t;
     42 
     43 /** A list of port numbers that have been used recently. */
     44 static smartlist_t *predicted_ports_list=NULL;
     45 /** How long do we keep predicting circuits? */
     46 static time_t prediction_timeout=0;
     47 /** When was the last time we added a prediction entry (HS or port) */
     48 static time_t last_prediction_add_time=0;
     49 
     50 /**
     51 * How much time left until we stop predicting circuits?
     52 */
     53 int
     54 predicted_ports_prediction_time_remaining(time_t now)
     55 {
     56  time_t seconds_waited;
     57  time_t seconds_left;
     58 
     59  /* Protect against overflow of return value. This can happen if the clock
     60   * jumps backwards in time. Update the last prediction time (aka last
     61   * active time) to prevent it. This update is preferable to using monotonic
     62   * time because it prevents clock jumps into the past from simply causing
     63   * very long idle timeouts while the monotonic time stands still. */
     64  seconds_waited = time_diff(last_prediction_add_time, now);
     65  if (seconds_waited == TIME_MAX) {
     66    last_prediction_add_time = now;
     67    seconds_waited = 0;
     68  }
     69 
     70  /* Protect against underflow of the return value. This can happen for very
     71   * large periods of inactivity/system sleep. */
     72  if (seconds_waited > prediction_timeout)
     73    return 0;
     74 
     75  seconds_left = time_diff(seconds_waited, prediction_timeout);
     76  if (BUG(seconds_left == TIME_MAX))
     77    return INT_MAX;
     78 
     79  return (int)(seconds_left);
     80 }
     81 
     82 /** We just got an application request for a connection with
     83 * port <b>port</b>. Remember it for the future, so we can keep
     84 * some circuits open that will exit to this port.
     85 */
     86 static void
     87 add_predicted_port(time_t now, uint16_t port)
     88 {
     89  predicted_port_t *pp = tor_malloc(sizeof(predicted_port_t));
     90 
     91  //  If the list is empty, re-randomize predicted ports lifetime
     92  if (!any_predicted_circuits(now)) {
     93    prediction_timeout =
     94     (time_t)channelpadding_get_circuits_available_timeout();
     95  }
     96 
     97  last_prediction_add_time = now;
     98 
     99  log_info(LD_CIRC,
    100          "New port prediction added. Will continue predictive circ building "
    101          "for %d more seconds.",
    102          predicted_ports_prediction_time_remaining(now));
    103 
    104  pp->port = port;
    105  pp->time = now;
    106  predicted_ports_total_alloc += sizeof(*pp);
    107  smartlist_add(predicted_ports_list, pp);
    108 }
    109 
    110 /** Remember that <b>port</b> has been asked for as of time <b>now</b>.
    111 * This is used for predicting what sorts of streams we'll make in the
    112 * future and making exit circuits to anticipate that.
    113 */
    114 void
    115 rep_hist_note_used_port(time_t now, uint16_t port)
    116 {
    117  tor_assert(predicted_ports_list);
    118 
    119  if (!port) /* record nothing */
    120    return;
    121 
    122  SMARTLIST_FOREACH_BEGIN(predicted_ports_list, predicted_port_t *, pp) {
    123    if (pp->port == port) {
    124      pp->time = now;
    125 
    126      last_prediction_add_time = now;
    127      log_info(LD_CIRC,
    128               "New port prediction added. Will continue predictive circ "
    129               "building for %d more seconds.",
    130               predicted_ports_prediction_time_remaining(now));
    131      return;
    132    }
    133  } SMARTLIST_FOREACH_END(pp);
    134  /* it's not there yet; we need to add it */
    135  add_predicted_port(now, port);
    136 }
    137 
    138 /** Return a newly allocated pointer to a list of uint16_t * for ports that
    139 * are likely to be asked for in the near future.
    140 */
    141 smartlist_t *
    142 rep_hist_get_predicted_ports(time_t now)
    143 {
    144  int predicted_circs_relevance_time;
    145  smartlist_t *out = smartlist_new();
    146  tor_assert(predicted_ports_list);
    147 
    148  predicted_circs_relevance_time = (int)prediction_timeout;
    149 
    150  /* clean out obsolete entries */
    151  SMARTLIST_FOREACH_BEGIN(predicted_ports_list, predicted_port_t *, pp) {
    152    if (pp->time + predicted_circs_relevance_time < now) {
    153      log_debug(LD_CIRC, "Expiring predicted port %d", pp->port);
    154 
    155      predicted_ports_total_alloc -= sizeof(predicted_port_t);
    156      tor_free(pp);
    157      SMARTLIST_DEL_CURRENT(predicted_ports_list, pp);
    158    } else {
    159      smartlist_add(out, tor_memdup(&pp->port, sizeof(uint16_t)));
    160    }
    161  } SMARTLIST_FOREACH_END(pp);
    162  return out;
    163 }
    164 
    165 /**
    166 * Take a list of uint16_t *, and remove every port in the list from the
    167 * current list of predicted ports.
    168 */
    169 void
    170 rep_hist_remove_predicted_ports(const smartlist_t *rmv_ports)
    171 {
    172  /* Let's do this on O(N), not O(N^2). */
    173  bitarray_t *remove_ports = bitarray_init_zero(UINT16_MAX);
    174  SMARTLIST_FOREACH(rmv_ports, const uint16_t *, p,
    175                    bitarray_set(remove_ports, *p));
    176  SMARTLIST_FOREACH_BEGIN(predicted_ports_list, predicted_port_t *, pp) {
    177    if (bitarray_is_set(remove_ports, pp->port)) {
    178      tor_free(pp);
    179      predicted_ports_total_alloc -= sizeof(*pp);
    180      SMARTLIST_DEL_CURRENT(predicted_ports_list, pp);
    181    }
    182  } SMARTLIST_FOREACH_END(pp);
    183  bitarray_free(remove_ports);
    184 }
    185 
    186 /** The user asked us to do a resolve. Rather than keeping track of
    187 * timings and such of resolves, we fake it for now by treating
    188 * it the same way as a connection to port 80. This way we will continue
    189 * to have circuits lying around if the user only uses Tor for resolves.
    190 */
    191 void
    192 rep_hist_note_used_resolve(time_t now)
    193 {
    194  rep_hist_note_used_port(now, 80);
    195 }
    196 
    197 /** The last time at which we needed an internal circ. */
    198 static time_t predicted_internal_time = 0;
    199 /** The last time we needed an internal circ with good uptime. */
    200 static time_t predicted_internal_uptime_time = 0;
    201 /** The last time we needed an internal circ with good capacity. */
    202 static time_t predicted_internal_capacity_time = 0;
    203 
    204 /** Remember that we used an internal circ at time <b>now</b>. */
    205 void
    206 rep_hist_note_used_internal(time_t now, int need_uptime, int need_capacity)
    207 {
    208  // If the list is empty, re-randomize predicted ports lifetime
    209  if (!any_predicted_circuits(now)) {
    210    prediction_timeout = channelpadding_get_circuits_available_timeout();
    211  }
    212 
    213  last_prediction_add_time = now;
    214 
    215  log_info(LD_CIRC,
    216          "New port prediction added. Will continue predictive circ building "
    217          "for %d more seconds.",
    218          predicted_ports_prediction_time_remaining(now));
    219 
    220  predicted_internal_time = now;
    221  if (need_uptime)
    222    predicted_internal_uptime_time = now;
    223  if (need_capacity)
    224    predicted_internal_capacity_time = now;
    225 }
    226 
    227 /** Return 1 if we've used an internal circ recently; else return 0. */
    228 int
    229 rep_hist_get_predicted_internal(time_t now, int *need_uptime,
    230                                int *need_capacity)
    231 {
    232  int predicted_circs_relevance_time;
    233 
    234  predicted_circs_relevance_time = (int)prediction_timeout;
    235 
    236  if (!predicted_internal_time) { /* initialize it */
    237    predicted_internal_time = now;
    238    predicted_internal_uptime_time = now;
    239    predicted_internal_capacity_time = now;
    240  }
    241  if (predicted_internal_time + predicted_circs_relevance_time < now)
    242    return 0; /* too long ago */
    243  if (predicted_internal_uptime_time + predicted_circs_relevance_time >= now)
    244    *need_uptime = 1;
    245  // Always predict that we need capacity.
    246  *need_capacity = 1;
    247  return 1;
    248 }
    249 
    250 /** Any ports used lately? These are pre-seeded if we just started
    251 * up or if we're running a hidden service. */
    252 int
    253 any_predicted_circuits(time_t now)
    254 {
    255  int predicted_circs_relevance_time;
    256  predicted_circs_relevance_time = (int)prediction_timeout;
    257 
    258  return smartlist_len(predicted_ports_list) ||
    259         predicted_internal_time + predicted_circs_relevance_time >= now;
    260 }
    261 
    262 /** Return 1 if we have no need for circuits currently, else return 0. */
    263 int
    264 rep_hist_circbuilding_dormant(time_t now)
    265 {
    266  const or_options_t *options = get_options();
    267 
    268  if (any_predicted_circuits(now))
    269    return 0;
    270 
    271  /* see if we'll still need to build testing circuits */
    272  if (server_mode(options) &&
    273      (!router_all_orports_seem_reachable(options) ||
    274       !circuit_enough_testing_circs()))
    275    return 0;
    276 
    277  return 1;
    278 }
    279 
    280 /**
    281 * Allocate whatever memory and structs are needed for predicting
    282 * which ports will be used. Also seed it with port 80, so we'll build
    283 * circuits on start-up.
    284 */
    285 static void
    286 predicted_ports_alloc(void)
    287 {
    288  predicted_ports_list = smartlist_new();
    289 }
    290 
    291 void
    292 predicted_ports_init(void)
    293 {
    294  predicted_ports_alloc();
    295  add_predicted_port(time(NULL), 443); // Add a port to get us started
    296 }
    297 
    298 /** Free whatever memory is needed for predicting which ports will
    299 * be used.
    300 */
    301 void
    302 predicted_ports_free_all(void)
    303 {
    304  if (!predicted_ports_list)
    305    return;
    306  predicted_ports_total_alloc -=
    307    smartlist_len(predicted_ports_list)*sizeof(predicted_port_t);
    308  SMARTLIST_FOREACH(predicted_ports_list, predicted_port_t *,
    309                    pp, tor_free(pp));
    310  smartlist_free(predicted_ports_list);
    311 }