Move the predicted ports code out of rephist.c
[tor.git] / src / feature / stats / predict_ports.c
blob8abf34f50f0acc487f689623d35cbd09166f1f2c
1 /* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
2 * Copyright (c) 2007-2018, The Tor Project, Inc. */
3 /* See LICENSE for licensing information */
5 /**
6 * \file predict_ports.c
7 * \brief Remember what ports we've needed so we can have circuits ready.
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 **/
18 #include "core/or/or.h"
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"
29 static size_t predicted_ports_total_alloc = 0;
31 static void predicted_ports_alloc(void);
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;
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;
50 /**
51 * How much time left until we stop predicting circuits?
53 int
54 predicted_ports_prediction_time_remaining(time_t now)
56 time_t seconds_waited;
57 time_t seconds_left;
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;
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;
75 seconds_left = time_diff(seconds_waited, prediction_timeout);
76 if (BUG(seconds_left == TIME_MAX))
77 return INT_MAX;
79 return (int)(seconds_left);
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.
86 static void
87 add_predicted_port(time_t now, uint16_t port)
89 predicted_port_t *pp = tor_malloc(sizeof(predicted_port_t));
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();
97 last_prediction_add_time = now;
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));
104 pp->port = port;
105 pp->time = now;
106 predicted_ports_total_alloc += sizeof(*pp);
107 smartlist_add(predicted_ports_list, pp);
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.
114 void
115 rep_hist_note_used_port(time_t now, uint16_t port)
117 tor_assert(predicted_ports_list);
119 if (!port) /* record nothing */
120 return;
122 SMARTLIST_FOREACH_BEGIN(predicted_ports_list, predicted_port_t *, pp) {
123 if (pp->port == port) {
124 pp->time = now;
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;
133 } SMARTLIST_FOREACH_END(pp);
134 /* it's not there yet; we need to add it */
135 add_predicted_port(now, port);
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.
141 smartlist_t *
142 rep_hist_get_predicted_ports(time_t now)
144 int predicted_circs_relevance_time;
145 smartlist_t *out = smartlist_new();
146 tor_assert(predicted_ports_list);
148 predicted_circs_relevance_time = (int)prediction_timeout;
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);
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)));
161 } SMARTLIST_FOREACH_END(pp);
162 return out;
166 * Take a list of uint16_t *, and remove every port in the list from the
167 * current list of predicted ports.
169 void
170 rep_hist_remove_predicted_ports(const smartlist_t *rmv_ports)
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);
182 } SMARTLIST_FOREACH_END(pp);
183 bitarray_free(remove_ports);
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.
191 void
192 rep_hist_note_used_resolve(time_t now)
194 rep_hist_note_used_port(now, 80);
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;
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)
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();
213 last_prediction_add_time = now;
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));
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;
227 /** Return 1 if we've used an internal circ recently; else return 0. */
229 rep_hist_get_predicted_internal(time_t now, int *need_uptime,
230 int *need_capacity)
232 int predicted_circs_relevance_time;
234 predicted_circs_relevance_time = (int)prediction_timeout;
236 if (!predicted_internal_time) { /* initialize it */
237 predicted_internal_time = now;
238 predicted_internal_uptime_time = now;
239 predicted_internal_capacity_time = now;
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;
250 /** Any ports used lately? These are pre-seeded if we just started
251 * up or if we're running a hidden service. */
253 any_predicted_circuits(time_t now)
255 int predicted_circs_relevance_time;
256 predicted_circs_relevance_time = (int)prediction_timeout;
258 return smartlist_len(predicted_ports_list) ||
259 predicted_internal_time + predicted_circs_relevance_time >= now;
262 /** Return 1 if we have no need for circuits currently, else return 0. */
264 rep_hist_circbuilding_dormant(time_t now)
266 const or_options_t *options = get_options();
268 if (any_predicted_circuits(now))
269 return 0;
271 /* see if we'll still need to build testing circuits */
272 if (server_mode(options) &&
273 (!check_whether_orport_reachable(options) ||
274 !circuit_enough_testing_circs()))
275 return 0;
276 if (!check_whether_dirport_reachable(options))
277 return 0;
279 return 1;
283 * Allocate whatever memory and structs are needed for predicting
284 * which ports will be used. Also seed it with port 80, so we'll build
285 * circuits on start-up.
287 static void
288 predicted_ports_alloc(void)
290 predicted_ports_list = smartlist_new();
293 void
294 predicted_ports_init(void)
296 predicted_ports_alloc();
297 add_predicted_port(time(NULL), 443); // Add a port to get us started
300 /** Free whatever memory is needed for predicting which ports will
301 * be used.
303 void
304 predicted_ports_free_all(void)
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);