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 }