LCOV - code coverage report
Current view: top level - feature/stats - predict_ports.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 61 111 55.0 %
Date: 2021-11-24 03:28:48 Functions: 9 13 69.2 %

          Line data    Source code
       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         204 : predicted_ports_prediction_time_remaining(time_t now)
      55             : {
      56         204 :   time_t seconds_waited;
      57         204 :   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         204 :   seconds_waited = time_diff(last_prediction_add_time, now);
      65         204 :   if (seconds_waited == TIME_MAX) {
      66           0 :     last_prediction_add_time = now;
      67           0 :     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         204 :   if (seconds_waited > prediction_timeout)
      73             :     return 0;
      74             : 
      75         204 :   seconds_left = time_diff(seconds_waited, prediction_timeout);
      76         204 :   if (BUG(seconds_left == TIME_MAX))
      77           0 :     return INT_MAX;
      78             : 
      79         204 :   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         203 : add_predicted_port(time_t now, uint16_t port)
      88             : {
      89         203 :   predicted_port_t *pp = tor_malloc(sizeof(predicted_port_t));
      90             : 
      91             :   //  If the list is empty, re-randomize predicted ports lifetime
      92         203 :   if (!any_predicted_circuits(now)) {
      93         406 :     prediction_timeout =
      94         203 :      (time_t)channelpadding_get_circuits_available_timeout();
      95             :   }
      96             : 
      97         203 :   last_prediction_add_time = now;
      98             : 
      99         203 :   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         203 :   pp->port = port;
     105         203 :   pp->time = now;
     106         203 :   predicted_ports_total_alloc += sizeof(*pp);
     107         203 :   smartlist_add(predicted_ports_list, pp);
     108         203 : }
     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           0 : rep_hist_note_used_port(time_t now, uint16_t port)
     116             : {
     117           0 :   tor_assert(predicted_ports_list);
     118             : 
     119           0 :   if (!port) /* record nothing */
     120             :     return;
     121             : 
     122           0 :   SMARTLIST_FOREACH_BEGIN(predicted_ports_list, predicted_port_t *, pp) {
     123           0 :     if (pp->port == port) {
     124           0 :       pp->time = now;
     125             : 
     126           0 :       last_prediction_add_time = now;
     127           0 :       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           0 :       return;
     132             :     }
     133           0 :   } SMARTLIST_FOREACH_END(pp);
     134             :   /* it's not there yet; we need to add it */
     135           0 :   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           3 : rep_hist_get_predicted_ports(time_t now)
     143             : {
     144           3 :   int predicted_circs_relevance_time;
     145           3 :   smartlist_t *out = smartlist_new();
     146           3 :   tor_assert(predicted_ports_list);
     147             : 
     148           3 :   predicted_circs_relevance_time = (int)prediction_timeout;
     149             : 
     150             :   /* clean out obsolete entries */
     151           6 :   SMARTLIST_FOREACH_BEGIN(predicted_ports_list, predicted_port_t *, pp) {
     152           3 :     if (pp->time + predicted_circs_relevance_time < now) {
     153           0 :       log_debug(LD_CIRC, "Expiring predicted port %d", pp->port);
     154             : 
     155           0 :       predicted_ports_total_alloc -= sizeof(predicted_port_t);
     156           0 :       tor_free(pp);
     157           0 :       SMARTLIST_DEL_CURRENT(predicted_ports_list, pp);
     158             :     } else {
     159           3 :       smartlist_add(out, tor_memdup(&pp->port, sizeof(uint16_t)));
     160             :     }
     161           3 :   } SMARTLIST_FOREACH_END(pp);
     162           3 :   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           0 : rep_hist_remove_predicted_ports(const smartlist_t *rmv_ports)
     171             : {
     172             :   /* Let's do this on O(N), not O(N^2). */
     173           0 :   bitarray_t *remove_ports = bitarray_init_zero(UINT16_MAX);
     174           0 :   SMARTLIST_FOREACH(rmv_ports, const uint16_t *, p,
     175             :                     bitarray_set(remove_ports, *p));
     176           0 :   SMARTLIST_FOREACH_BEGIN(predicted_ports_list, predicted_port_t *, pp) {
     177           0 :     if (bitarray_is_set(remove_ports, pp->port)) {
     178           0 :       tor_free(pp);
     179           0 :       predicted_ports_total_alloc -= sizeof(*pp);
     180           0 :       SMARTLIST_DEL_CURRENT(predicted_ports_list, pp);
     181             :     }
     182           0 :   } SMARTLIST_FOREACH_END(pp);
     183           0 :   bitarray_free(remove_ports);
     184           0 : }
     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           0 : rep_hist_note_used_resolve(time_t now)
     193             : {
     194           0 :   rep_hist_note_used_port(now, 80);
     195           0 : }
     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           1 : 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           1 :   if (!any_predicted_circuits(now)) {
     210           0 :     prediction_timeout = channelpadding_get_circuits_available_timeout();
     211             :   }
     212             : 
     213           1 :   last_prediction_add_time = now;
     214             : 
     215           1 :   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           1 :   predicted_internal_time = now;
     221           1 :   if (need_uptime)
     222           0 :     predicted_internal_uptime_time = now;
     223           1 :   if (need_capacity)
     224           1 :     predicted_internal_capacity_time = now;
     225           1 : }
     226             : 
     227             : /** Return 1 if we've used an internal circ recently; else return 0. */
     228             : int
     229           0 : rep_hist_get_predicted_internal(time_t now, int *need_uptime,
     230             :                                 int *need_capacity)
     231             : {
     232           0 :   int predicted_circs_relevance_time;
     233             : 
     234           0 :   predicted_circs_relevance_time = (int)prediction_timeout;
     235             : 
     236           0 :   if (!predicted_internal_time) { /* initialize it */
     237           0 :     predicted_internal_time = now;
     238           0 :     predicted_internal_uptime_time = now;
     239           0 :     predicted_internal_capacity_time = now;
     240             :   }
     241           0 :   if (predicted_internal_time + predicted_circs_relevance_time < now)
     242             :     return 0; /* too long ago */
     243           0 :   if (predicted_internal_uptime_time + predicted_circs_relevance_time >= now)
     244           0 :     *need_uptime = 1;
     245             :   // Always predict that we need capacity.
     246           0 :   *need_capacity = 1;
     247           0 :   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         225 : any_predicted_circuits(time_t now)
     254             : {
     255         225 :   int predicted_circs_relevance_time;
     256         225 :   predicted_circs_relevance_time = (int)prediction_timeout;
     257             : 
     258         225 :   return smartlist_len(predicted_ports_list) ||
     259         203 :          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          21 : rep_hist_circbuilding_dormant(time_t now)
     265             : {
     266          21 :   const or_options_t *options = get_options();
     267             : 
     268          21 :   if (any_predicted_circuits(now))
     269             :     return 0;
     270             : 
     271             :   /* see if we'll still need to build testing circuits */
     272           0 :   if (server_mode(options) &&
     273           0 :       (!router_all_orports_seem_reachable(options) ||
     274           0 :        !circuit_enough_testing_circs()))
     275           0 :     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         203 : predicted_ports_alloc(void)
     287             : {
     288         203 :   predicted_ports_list = smartlist_new();
     289         203 : }
     290             : 
     291             : void
     292         203 : predicted_ports_init(void)
     293             : {
     294         203 :   predicted_ports_alloc();
     295         203 :   add_predicted_port(time(NULL), 443); // Add a port to get us started
     296         203 : }
     297             : 
     298             : /** Free whatever memory is needed for predicting which ports will
     299             :  * be used.
     300             :  */
     301             : void
     302         235 : predicted_ports_free_all(void)
     303             : {
     304         235 :   if (!predicted_ports_list)
     305             :     return;
     306         194 :   predicted_ports_total_alloc -=
     307         194 :     smartlist_len(predicted_ports_list)*sizeof(predicted_port_t);
     308         388 :   SMARTLIST_FOREACH(predicted_ports_list, predicted_port_t *,
     309             :                     pp, tor_free(pp));
     310         194 :   smartlist_free(predicted_ports_list);
     311             : }

Generated by: LCOV version 1.14