1 /* Copyright 2004-2005 Roger Dingledine, Nick Mathewson. */
2 /* See LICENSE for licensing information */
4 const char rephist_c_id
[] =
9 * \brief Basic history and "reputation" functionality to remember
10 * which servers have worked in the past, how much bandwidth we've
11 * been using, which ports we tend to want, and so on.
16 static void bw_arrays_init(void);
17 static void predicted_ports_init(void);
19 uint64_t rephist_total_alloc
=0;
20 uint32_t rephist_total_num
=0;
22 /** History of an OR-\>OR link. */
23 typedef struct link_history_t
{
24 /** When did we start tracking this list? */
26 /** When did we most recently note a change to this link */
28 /** How many times did extending from OR1 to OR2 succeed? */
29 unsigned long n_extend_ok
;
30 /** How many times did extending from OR1 to OR2 fail? */
31 unsigned long n_extend_fail
;
34 /** History of an OR. */
35 typedef struct or_history_t
{
36 /** When did we start tracking this OR? */
38 /** When did we most recently note a change to this OR? */
40 /** How many times did we successfully connect? */
41 unsigned long n_conn_ok
;
42 /** How many times did we try to connect and fail?*/
43 unsigned long n_conn_fail
;
44 /** How many seconds have we been connected to this OR before
47 /** How many seconds have we been unable to connect to this OR before
49 unsigned long downtime
;
50 /** If nonzero, we have been connected since this time. */
52 /** If nonzero, we have been unable to connect since this time. */
54 /** Map from hex OR2 identity digest to a link_history_t for the link
55 * from this OR to OR2. */
56 digestmap_t
*link_history_map
;
59 /** Map from hex OR identity digest to or_history_t. */
60 static digestmap_t
*history_map
= NULL
;
62 /** Return the or_history_t for the named OR, creating it if necessary.
65 get_or_history(const char* id
)
69 if (!memcmp(id
, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", DIGEST_LEN
))
72 hist
= digestmap_get(history_map
, id
);
74 hist
= tor_malloc_zero(sizeof(or_history_t
));
75 rephist_total_alloc
+= sizeof(or_history_t
);
77 hist
->link_history_map
= digestmap_new();
78 hist
->since
= hist
->changed
= time(NULL
);
79 digestmap_set(history_map
, id
, hist
);
84 /** Return the link_history_t for the link from the first named OR to
85 * the second, creating it if necessary. (ORs are identified by
88 static link_history_t
*
89 get_link_history(const char *from_id
, const char *to_id
)
92 link_history_t
*lhist
;
93 orhist
= get_or_history(from_id
);
96 if (!memcmp(to_id
, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", DIGEST_LEN
))
98 lhist
= (link_history_t
*) digestmap_get(orhist
->link_history_map
, to_id
);
100 lhist
= tor_malloc_zero(sizeof(link_history_t
));
101 rephist_total_alloc
+= sizeof(link_history_t
);
102 lhist
->since
= lhist
->changed
= time(NULL
);
103 digestmap_set(orhist
->link_history_map
, to_id
, lhist
);
108 /** Helper: free storage held by a single link history entry */
110 _free_link_history(void *val
)
112 rephist_total_alloc
-= sizeof(link_history_t
);
116 /** Helper: free storage held by a single OR history entry */
118 free_or_history(void *_hist
)
120 or_history_t
*hist
= _hist
;
121 digestmap_free(hist
->link_history_map
, _free_link_history
);
122 rephist_total_alloc
-= sizeof(or_history_t
);
127 /** Update an or_history_t object <b>hist</b> so that its uptime/downtime
128 * count is up-to-date as of <b>when</b>.
131 update_or_history(or_history_t
*hist
, time_t when
)
134 if (hist
->up_since
) {
135 tor_assert(!hist
->down_since
);
136 hist
->uptime
+= (when
- hist
->up_since
);
137 hist
->up_since
= when
;
138 } else if (hist
->down_since
) {
139 hist
->downtime
+= (when
- hist
->down_since
);
140 hist
->down_since
= when
;
144 /** Initialize the static data structures for tracking history.
149 history_map
= digestmap_new();
151 predicted_ports_init();
154 /** Remember that an attempt to connect to the OR with identity digest
155 * <b>id</b> failed at <b>when</b>.
158 rep_hist_note_connect_failed(const char* id
, time_t when
)
161 hist
= get_or_history(id
);
165 if (hist
->up_since
) {
166 hist
->uptime
+= (when
- hist
->up_since
);
169 if (!hist
->down_since
)
170 hist
->down_since
= when
;
171 hist
->changed
= when
;
174 /** Remember that an attempt to connect to the OR with identity digest
175 * <b>id</b> succeeded at <b>when</b>.
178 rep_hist_note_connect_succeeded(const char* id
, time_t when
)
181 hist
= get_or_history(id
);
185 if (hist
->down_since
) {
186 hist
->downtime
+= (when
- hist
->down_since
);
187 hist
->down_since
= 0;
190 hist
->up_since
= when
;
191 hist
->changed
= when
;
194 /** Remember that we intentionally closed our connection to the OR
195 * with identity digest <b>id</b> at <b>when</b>.
198 rep_hist_note_disconnect(const char* id
, time_t when
)
201 hist
= get_or_history(id
);
205 if (hist
->up_since
) {
206 hist
->uptime
+= (when
- hist
->up_since
);
209 hist
->changed
= when
;
212 /** Remember that our connection to the OR with identity digest
213 * <b>id</b> had an error and stopped working at <b>when</b>.
216 rep_hist_note_connection_died(const char* id
, time_t when
)
220 /* XXXX009 Well, everybody has an ID now. Hm. */
221 /* If conn has no nickname, it's either an OP, or it is an OR
222 * which didn't complete its handshake (or did and was unapproved).
227 hist
= get_or_history(id
);
230 if (hist
->up_since
) {
231 hist
->uptime
+= (when
- hist
->up_since
);
234 if (!hist
->down_since
)
235 hist
->down_since
= when
;
236 hist
->changed
= when
;
239 /** Remember that we successfully extended from the OR with identity
240 * digest <b>from_id</b> to the OR with identity digest
244 rep_hist_note_extend_succeeded(const char *from_id
, const char *to_id
)
246 link_history_t
*hist
;
247 /* log_fn(LOG_WARN, "EXTEND SUCCEEDED: %s->%s",from_name,to_name); */
248 hist
= get_link_history(from_id
, to_id
);
252 hist
->changed
= time(NULL
);
255 /** Remember that we tried to extend from the OR with identity digest
256 * <b>from_id</b> to the OR with identity digest <b>to_name</b>, but
260 rep_hist_note_extend_failed(const char *from_id
, const char *to_id
)
262 link_history_t
*hist
;
263 /* log_fn(LOG_WARN, "EXTEND FAILED: %s->%s",from_name,to_name); */
264 hist
= get_link_history(from_id
, to_id
);
267 ++hist
->n_extend_fail
;
268 hist
->changed
= time(NULL
);
271 /** Log all the reliability data we have remembered, with the chosen
275 rep_hist_dump_stats(time_t now
, int severity
)
277 digestmap_iter_t
*lhist_it
;
278 digestmap_iter_t
*orhist_it
;
279 const char *name1
, *name2
, *digest1
, *digest2
;
280 char hexdigest1
[HEX_DIGEST_LEN
+1];
281 or_history_t
*or_history
;
282 link_history_t
*link_history
;
283 void *or_history_p
, *link_history_p
;
288 unsigned long upt
, downt
;
291 rep_history_clean(now
- get_options()->RephistTrackTime
);
293 log(severity
, LD_GENERAL
, "--------------- Dumping history information:");
295 for (orhist_it
= digestmap_iter_init(history_map
);
296 !digestmap_iter_done(orhist_it
);
297 orhist_it
= digestmap_iter_next(history_map
,orhist_it
)) {
298 digestmap_iter_get(orhist_it
, &digest1
, &or_history_p
);
299 or_history
= (or_history_t
*) or_history_p
;
301 if ((r
= router_get_by_digest(digest1
)))
305 base16_encode(hexdigest1
, sizeof(hexdigest1
), digest1
, DIGEST_LEN
);
306 update_or_history(or_history
, now
);
307 upt
= or_history
->uptime
;
308 downt
= or_history
->downtime
;
310 uptime
= ((double)upt
) / (upt
+downt
);
314 log(severity
, LD_GENERAL
,
315 "OR %s [%s]: %ld/%ld good connections; uptime %ld/%ld sec (%.2f%%)",
317 or_history
->n_conn_ok
, or_history
->n_conn_fail
+or_history
->n_conn_ok
,
318 upt
, upt
+downt
, uptime
*100.0);
320 if (!digestmap_isempty(or_history
->link_history_map
)) {
321 strlcpy(buffer
, " Extend attempts: ", sizeof(buffer
));
322 len
= strlen(buffer
);
323 for (lhist_it
= digestmap_iter_init(or_history
->link_history_map
);
324 !digestmap_iter_done(lhist_it
);
325 lhist_it
= digestmap_iter_next(or_history
->link_history_map
,
327 digestmap_iter_get(lhist_it
, &digest2
, &link_history_p
);
328 if ((r
= router_get_by_digest(digest2
)))
333 link_history
= (link_history_t
*) link_history_p
;
335 ret
= tor_snprintf(buffer
+len
, 2048-len
, "%s(%ld/%ld); ", name2
,
336 link_history
->n_extend_ok
,
337 link_history
->n_extend_ok
+link_history
->n_extend_fail
);
343 log(severity
, LD_GENERAL
, "%s", buffer
);
348 /** Remove history info for routers/links that haven't changed since
351 rep_history_clean(time_t before
)
353 or_history_t
*or_history
;
354 link_history_t
*link_history
;
355 void *or_history_p
, *link_history_p
;
356 digestmap_iter_t
*orhist_it
, *lhist_it
;
359 orhist_it
= digestmap_iter_init(history_map
);
360 while (!digestmap_iter_done(orhist_it
)) {
361 digestmap_iter_get(orhist_it
, &d1
, &or_history_p
);
362 or_history
= or_history_p
;
363 if (or_history
->changed
< before
) {
364 orhist_it
= digestmap_iter_next_rmv(history_map
, orhist_it
);
365 free_or_history(or_history
);
368 for (lhist_it
= digestmap_iter_init(or_history
->link_history_map
);
369 !digestmap_iter_done(lhist_it
); ) {
370 digestmap_iter_get(lhist_it
, &d2
, &link_history_p
);
371 link_history
= link_history_p
;
372 if (link_history
->changed
< before
) {
373 lhist_it
= digestmap_iter_next_rmv(or_history
->link_history_map
,
375 rephist_total_alloc
-= sizeof(link_history_t
);
376 tor_free(link_history
);
379 lhist_it
= digestmap_iter_next(or_history
->link_history_map
,lhist_it
);
381 orhist_it
= digestmap_iter_next(history_map
, orhist_it
);
385 #define NUM_SECS_ROLLING_MEASURE 10
386 #define NUM_SECS_BW_SUM_IS_VALID (24*60*60) /* one day */
387 #define NUM_SECS_BW_SUM_INTERVAL (15*60)
388 #define NUM_TOTALS (NUM_SECS_BW_SUM_IS_VALID/NUM_SECS_BW_SUM_INTERVAL)
391 * Structure to track bandwidth use, and remember the maxima for a given
394 typedef struct bw_array_t
{
395 /** Observation array: Total number of bytes transferred in each of the last
396 * NUM_SECS_ROLLING_MEASURE seconds. This is used as a circular array. */
397 uint64_t obs
[NUM_SECS_ROLLING_MEASURE
];
398 int cur_obs_idx
; /**< Current position in obs. */
399 time_t cur_obs_time
; /**< Time represented in obs[cur_obs_idx] */
400 uint64_t total_obs
; /**< Total for all members of obs except
401 * obs[cur_obs_idx] */
402 uint64_t max_total
; /**< Largest value that total_obs has taken on in the
404 uint64_t total_in_period
; /**< Total bytes transferred in the current
407 /** When does the next period begin? */
409 /** Where in 'maxima' should the maximum bandwidth usage for the current
410 * period be stored? */
412 /** How many values in maxima/totals have been set ever? */
414 /** Circular array of the maximum
415 * bandwidth-per-NUM_SECS_ROLLING_MEASURE usage for the last
416 * NUM_TOTALS periods */
417 uint64_t maxima
[NUM_TOTALS
];
418 /** Circular array of the total bandwidth usage for the last NUM_TOTALS
420 uint64_t totals
[NUM_TOTALS
];
423 /** Shift the current period of b forward by one.
426 commit_max(bw_array_t
*b
)
428 /* Store total from current period. */
429 b
->totals
[b
->next_max_idx
] = b
->total_in_period
;
430 /* Store maximum from current period. */
431 b
->maxima
[b
->next_max_idx
++] = b
->max_total
;
432 /* Advance next_period and next_max_idx */
433 b
->next_period
+= NUM_SECS_BW_SUM_INTERVAL
;
434 if (b
->next_max_idx
== NUM_TOTALS
)
436 if (b
->num_maxes_set
< NUM_TOTALS
)
438 /* Reset max_total. */
440 /* Reset total_in_period. */
441 b
->total_in_period
= 0;
444 /** Shift the current observation time of 'b' forward by one second.
447 advance_obs(bw_array_t
*b
)
452 /* Calculate the total bandwidth for the last NUM_SECS_ROLLING_MEASURE
453 * seconds; adjust max_total as needed.*/
454 total
= b
->total_obs
+ b
->obs
[b
->cur_obs_idx
];
455 if (total
> b
->max_total
)
456 b
->max_total
= total
;
458 nextidx
= b
->cur_obs_idx
+1;
459 if (nextidx
== NUM_SECS_ROLLING_MEASURE
)
462 b
->total_obs
= total
- b
->obs
[nextidx
];
464 b
->cur_obs_idx
= nextidx
;
466 if (++b
->cur_obs_time
>= b
->next_period
)
470 /** Add 'n' bytes to the number of bytes in b for second 'when'.
473 add_obs(bw_array_t
*b
, time_t when
, uint64_t n
)
475 /* Don't record data in the past. */
476 if (when
<b
->cur_obs_time
)
478 /* If we're currently adding observations for an earlier second than
479 * 'when', advance b->cur_obs_time and b->cur_obs_idx by an
480 * appropriate number of seconds, and do all the other housekeeping */
481 while (when
>b
->cur_obs_time
)
484 b
->obs
[b
->cur_obs_idx
] += n
;
485 b
->total_in_period
+= n
;
488 /** Allocate, initialize, and return a new bw_array.
495 b
= tor_malloc_zero(sizeof(bw_array_t
));
496 rephist_total_alloc
+= sizeof(bw_array_t
);
498 b
->cur_obs_time
= start
;
499 b
->next_period
= start
+ NUM_SECS_BW_SUM_INTERVAL
;
503 static bw_array_t
*read_array
= NULL
;
504 static bw_array_t
*write_array
= NULL
;
506 /** Set up read_array and write_array
511 read_array
= bw_array_new();
512 write_array
= bw_array_new();
515 /** We read <b>num_bytes</b> more bytes in second <b>when</b>.
517 * Add num_bytes to the current running total for <b>when</b>.
519 * <b>when</b> can go back to time, but it's safe to ignore calls
520 * earlier than the latest <b>when</b> you've heard of.
523 rep_hist_note_bytes_written(int num_bytes
, time_t when
)
525 /* Maybe a circular array for recent seconds, and step to a new point
526 * every time a new second shows up. Or simpler is to just to have
527 * a normal array and push down each item every second; it's short.
529 /* When a new second has rolled over, compute the sum of the bytes we've
530 * seen over when-1 to when-1-NUM_SECS_ROLLING_MEASURE, and stick it
531 * somewhere. See rep_hist_bandwidth_assess() below.
533 add_obs(write_array
, when
, num_bytes
);
536 /** We wrote <b>num_bytes</b> more bytes in second <b>when</b>.
537 * (like rep_hist_note_bytes_written() above)
540 rep_hist_note_bytes_read(int num_bytes
, time_t when
)
542 /* if we're smart, we can make this func and the one above share code */
543 add_obs(read_array
, when
, num_bytes
);
546 /** Helper: Return the largest value in b->maxima. (This is equal to the
547 * most bandwidth used in any NUM_SECS_ROLLING_MEASURE period for the last
548 * NUM_SECS_BW_SUM_IS_VALID seconds.)
551 find_largest_max(bw_array_t
*b
)
556 for (i
=0; i
<NUM_TOTALS
; ++i
) {
557 if (b
->maxima
[i
]>max
)
564 * Find the largest sums in the past NUM_SECS_BW_SUM_IS_VALID (roughly)
565 * seconds. Find one sum for reading and one for writing. They don't have
566 * to be at the same time).
568 * Return the smaller of these sums, divided by NUM_SECS_ROLLING_MEASURE.
571 rep_hist_bandwidth_assess(void)
574 r
= find_largest_max(read_array
);
575 w
= find_largest_max(write_array
);
577 return (int)(w
/(double)NUM_SECS_ROLLING_MEASURE
);
579 return (int)(r
/(double)NUM_SECS_ROLLING_MEASURE
);
585 * Print the bandwidth history of b (either read_array or write_array)
586 * into the buffer pointed to by buf. The format is simply comma
587 * separated numbers, from oldest to newest.
589 * It returns the number of bytes written.
592 rep_hist_fill_bandwidth_history(char *buf
, size_t len
, bw_array_t
*b
)
597 if (b
->num_maxes_set
<= b
->next_max_idx
) {
598 /* We haven't been through the circular array yet; time starts at i=0.*/
601 /* We've been around the array at least once. The next i to be
602 overwritten is the oldest. */
606 for (n
=0; n
<b
->num_maxes_set
; ++n
,++i
) {
607 while (i
>= NUM_TOTALS
) i
-= NUM_TOTALS
;
608 if (n
==(b
->num_maxes_set
-1))
609 tor_snprintf(cp
, len
-(cp
-buf
), U64_FORMAT
,
610 U64_PRINTF_ARG(b
->totals
[i
]));
612 tor_snprintf(cp
, len
-(cp
-buf
), U64_FORMAT
",",
613 U64_PRINTF_ARG(b
->totals
[i
]));
620 * Allocate and return lines for representing this server's bandwidth
621 * history in its descriptor.
624 rep_hist_get_bandwidth_lines(void)
627 char t
[ISO_TIME_LEN
+1];
632 /* opt (read|write)-history yyyy-mm-dd HH:MM:SS (n s) n,n,n,n,n... */
633 len
= (60+20*NUM_TOTALS
)*2;
634 buf
= tor_malloc_zero(len
);
637 b
= r
?read_array
:write_array
;
639 format_iso_time(t
, b
->next_period
-NUM_SECS_BW_SUM_INTERVAL
);
640 tor_snprintf(cp
, len
-(cp
-buf
), "opt %s %s (%d s) ",
641 r
? "read-history" : "write-history", t
,
642 NUM_SECS_BW_SUM_INTERVAL
);
644 cp
+= rep_hist_fill_bandwidth_history(cp
, len
-(cp
-buf
), b
);
645 strlcat(cp
, "\n", len
-(cp
-buf
));
651 /** Update <b>state</b> with the newest bandwidth history. */
653 rep_hist_update_state(or_state_t
*state
)
657 smartlist_t
**s_values
;
662 len
= 20*NUM_TOTALS
+1;
663 buf
= tor_malloc_zero(len
);
666 b
= r
?read_array
:write_array
;
667 s_begins
= r
?&state
->BWHistoryReadEnds
:&state
->BWHistoryWriteEnds
;
668 s_interval
= r
?&state
->BWHistoryReadInterval
:&state
->BWHistoryWriteInterval
;
669 s_values
= r
?&state
->BWHistoryReadValues
:&state
->BWHistoryWriteValues
;
671 *s_begins
= b
->next_period
;
672 *s_interval
= NUM_SECS_BW_SUM_INTERVAL
;
674 SMARTLIST_FOREACH(*s_values
, char *, cp
, tor_free(cp
));
675 smartlist_free(*s_values
);
678 cp
+= rep_hist_fill_bandwidth_history(cp
, len
, b
);
679 tor_snprintf(cp
, len
-(cp
-buf
), cp
== buf
? U64_FORMAT
: ","U64_FORMAT
,
680 U64_PRINTF_ARG(b
->total_in_period
));
681 *s_values
= smartlist_create();
682 if (server_mode(get_options()))
683 smartlist_split_string(*s_values
, buf
, ",", SPLIT_SKIP_SPACE
, 0);
689 /** Set bandwidth history from our saved state.
692 rep_hist_load_state(or_state_t
*state
, const char **err
)
694 time_t s_begins
, start
;
695 time_t now
= time(NULL
);
700 smartlist_t
*s_values
;
703 /* Assert they already have been malloced */
704 tor_assert(read_array
&& write_array
);
707 b
= r
?read_array
:write_array
;
708 s_begins
= r
?state
->BWHistoryReadEnds
:state
->BWHistoryWriteEnds
;
709 s_interval
= r
?state
->BWHistoryReadInterval
:state
->BWHistoryWriteInterval
;
710 s_values
= r
?state
->BWHistoryReadValues
:state
->BWHistoryWriteValues
;
711 if (s_values
&& s_begins
>= now
- NUM_SECS_BW_SUM_INTERVAL
*NUM_TOTALS
) {
712 start
= s_begins
- s_interval
*(smartlist_len(s_values
));
714 b
->cur_obs_time
= start
;
715 b
->next_period
= start
+ NUM_SECS_BW_SUM_INTERVAL
;
716 SMARTLIST_FOREACH(s_values
, char *, cp
, {
717 v
= tor_parse_uint64(cp
, 10, 0, UINT64_MAX
, &ok
, NULL
);
720 notice(LD_GENERAL
, "Could not parse '%s' into a number.'", cp
);
722 add_obs(b
, start
, v
);
723 start
+= NUM_SECS_BW_SUM_INTERVAL
;
727 /* Clean up maxima and observed */
728 /* Do we really want to zero this for the purpose of max capacity? */
729 for (i
=0; i
<NUM_SECS_ROLLING_MEASURE
; ++i
) {
733 for (i
=0; i
<NUM_TOTALS
; ++i
) {
741 *err
= "Parsing of bandwidth history values failed";
742 /* and create fresh arrays */
743 tor_free(read_array
);
744 tor_free(write_array
);
745 read_array
= bw_array_new();
746 write_array
= bw_array_new();
752 /** A list of port numbers that have been used recently. */
753 static smartlist_t
*predicted_ports_list
=NULL
;
754 /** The corresponding most recently used time for each port. */
755 static smartlist_t
*predicted_ports_times
=NULL
;
759 add_predicted_port(uint16_t port
, time_t now
)
761 /* XXXX we could just use uintptr_t here, I think. */
762 uint16_t *tmp_port
= tor_malloc(sizeof(uint16_t));
763 time_t *tmp_time
= tor_malloc(sizeof(time_t));
766 rephist_total_alloc
+= sizeof(uint16_t) + sizeof(time_t);
767 smartlist_add(predicted_ports_list
, tmp_port
);
768 smartlist_add(predicted_ports_times
, tmp_time
);
773 predicted_ports_init(void)
775 predicted_ports_list
= smartlist_create();
776 predicted_ports_times
= smartlist_create();
777 add_predicted_port(80, time(NULL
)); /* add one to kickstart us */
782 predicted_ports_free(void)
784 rephist_total_alloc
-= smartlist_len(predicted_ports_list
)*sizeof(uint16_t);
785 SMARTLIST_FOREACH(predicted_ports_list
, char *, cp
, tor_free(cp
));
786 smartlist_free(predicted_ports_list
);
787 rephist_total_alloc
-= smartlist_len(predicted_ports_times
)*sizeof(time_t);
788 SMARTLIST_FOREACH(predicted_ports_times
, char *, cp
, tor_free(cp
));
789 smartlist_free(predicted_ports_times
);
792 /** Remember that <b>port</b> has been asked for as of time <b>now</b>.
793 * This is used for predicting what sorts of streams we'll make in the
794 * future and making exit circuits to anticipate that.
797 rep_hist_note_used_port(uint16_t port
, time_t now
)
803 tor_assert(predicted_ports_list
);
804 tor_assert(predicted_ports_times
);
806 if (!port
) /* record nothing */
809 for (i
= 0; i
< smartlist_len(predicted_ports_list
); ++i
) {
810 tmp_port
= smartlist_get(predicted_ports_list
, i
);
811 tmp_time
= smartlist_get(predicted_ports_times
, i
);
812 if (*tmp_port
== port
) {
817 /* it's not there yet; we need to add it */
818 add_predicted_port(port
, now
);
821 #define PREDICTED_CIRCS_RELEVANCE_TIME (3600) /* 1 hour */
823 /** Return a pointer to the list of port numbers that
824 * are likely to be asked for in the near future.
826 * The caller promises not to mess with it.
829 rep_hist_get_predicted_ports(time_t now
)
835 tor_assert(predicted_ports_list
);
836 tor_assert(predicted_ports_times
);
838 /* clean out obsolete entries */
839 for (i
= 0; i
< smartlist_len(predicted_ports_list
); ++i
) {
840 tmp_time
= smartlist_get(predicted_ports_times
, i
);
841 if (*tmp_time
+ PREDICTED_CIRCS_RELEVANCE_TIME
< now
) {
842 tmp_port
= smartlist_get(predicted_ports_list
, i
);
843 debug(LD_CIRC
, "Expiring predicted port %d", *tmp_port
);
844 smartlist_del(predicted_ports_list
, i
);
845 smartlist_del(predicted_ports_times
, i
);
846 rephist_total_alloc
-= sizeof(uint16_t)+sizeof(time_t);
852 return predicted_ports_list
;
855 /** The user asked us to do a resolve. Rather than keeping track of
856 * timings and such of resolves, we fake it for now by making treating
857 * it the same way as a connection to port 80. This way we will continue
858 * to have circuits lying around if the user only uses Tor for resolves.
861 rep_hist_note_used_resolve(time_t now
)
863 rep_hist_note_used_port(80, now
);
868 rep_hist_get_predicted_resolve(time_t now
)
874 /** The last time at which we needed an internal circ. */
875 static time_t predicted_internal_time
= 0;
876 /** The last time we needed an internal circ with good uptime. */
877 static time_t predicted_internal_uptime_time
= 0;
878 /** The last time we needed an internal circ with good capacity. */
879 static time_t predicted_internal_capacity_time
= 0;
881 /** Remember that we used an internal circ at time <b>now</b>. */
883 rep_hist_note_used_internal(time_t now
, int need_uptime
, int need_capacity
)
885 predicted_internal_time
= now
;
887 predicted_internal_uptime_time
= now
;
889 predicted_internal_capacity_time
= now
;
892 /** Return 1 if we've used an internal circ recently; else return 0. */
894 rep_hist_get_predicted_internal(time_t now
, int *need_uptime
,
897 if (!predicted_internal_time
) { /* initialize it */
898 predicted_internal_time
= now
;
899 predicted_internal_uptime_time
= now
;
900 predicted_internal_capacity_time
= now
;
902 if (predicted_internal_time
+ PREDICTED_CIRCS_RELEVANCE_TIME
< now
)
903 return 0; /* too long ago */
904 if (predicted_internal_uptime_time
+ PREDICTED_CIRCS_RELEVANCE_TIME
< now
)
906 if (predicted_internal_capacity_time
+ PREDICTED_CIRCS_RELEVANCE_TIME
< now
)
911 /** Free all storage held by the OR/link history caches, by the
912 * bandwidth history arrays, or by the port history. */
914 rep_hist_free_all(void)
916 digestmap_free(history_map
, free_or_history
);
917 tor_free(read_array
);
918 tor_free(write_array
);
919 predicted_ports_free();