2007-12-12 22:09:01 +01:00
|
|
|
/* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
|
2010-02-27 23:13:37 +01:00
|
|
|
* Copyright (c) 2007-2010, The Tor Project, Inc. */
|
2004-03-20 02:48:05 +01:00
|
|
|
/* See LICENSE for licensing information */
|
|
|
|
|
2004-05-10 06:34:48 +02:00
|
|
|
/**
|
|
|
|
* \file rephist.c
|
2005-06-11 20:52:12 +02:00
|
|
|
* \brief Basic history and "reputation" functionality to remember
|
|
|
|
* which servers have worked in the past, how much bandwidth we've
|
2010-06-21 18:52:46 +02:00
|
|
|
* been using, which ports we tend to want, and so on; further,
|
2010-08-02 15:06:14 +02:00
|
|
|
* exit port statistics, cell statistics, and connection statistics.
|
2004-05-10 06:34:48 +02:00
|
|
|
**/
|
2004-05-05 23:32:43 +02:00
|
|
|
|
2004-03-20 02:48:05 +01:00
|
|
|
#include "or.h"
|
2010-07-22 09:46:23 +02:00
|
|
|
#include "circuitlist.h"
|
2010-07-22 10:03:40 +02:00
|
|
|
#include "circuituse.h"
|
2010-07-22 10:22:51 +02:00
|
|
|
#include "config.h"
|
Initial conversion to use node_t throughout our codebase.
A node_t is an abstraction over routerstatus_t, routerinfo_t, and
microdesc_t. It should try to present a consistent interface to all
of them. There should be a node_t for a server whenever there is
* A routerinfo_t for it in the routerlist
* A routerstatus_t in the current_consensus.
(note that a microdesc_t alone isn't enough to make a node_t exist,
since microdescriptors aren't usable on their own.)
There are three ways to get a node_t right now: looking it up by ID,
looking it up by nickname, and iterating over the whole list of
microdescriptors.
All (or nearly all) functions that are supposed to return "a router"
-- especially those used in building connections and circuits --
should return a node_t, not a routerinfo_t or a routerstatus_t.
A node_t should hold all the *mutable* flags about a node. This
patch moves the is_foo flags from routerinfo_t into node_t. The
flags in routerstatus_t remain, but they get set from the consensus
and should not change.
Some other highlights of this patch are:
* Looking up routerinfo and routerstatus by nickname is now
unified and based on the "look up a node by nickname" function.
This tries to look only at the values from current consensus,
and not get confused by the routerinfo_t->is_named flag, which
could get set for other weird reasons. This changes the
behavior of how authorities (when acting as clients) deal with
nodes that have been listed by nickname.
* I tried not to artificially increase the size of the diff here
by moving functions around. As a result, some functions that
now operate on nodes are now in the wrong file -- they should
get moved to nodelist.c once this refactoring settles down.
This moving should happen as part of a patch that moves
functions AND NOTHING ELSE.
* Some old code is now left around inside #if 0/1 blocks, and
should get removed once I've verified that I don't want it
sitting around to see how we used to do things.
There are still some unimplemented functions: these are flagged
with "UNIMPLEMENTED_NODELIST()." I'll work on filling in the
implementation here, piece by piece.
I wish this patch could have been smaller, but there did not seem to
be any piece of it that was independent from the rest. Moving flags
forces many functions that once returned routerinfo_t * to return
node_t *, which forces their friends to change, and so on.
2010-09-29 21:00:41 +02:00
|
|
|
#include "nodelist.h"
|
2010-07-23 22:57:20 +02:00
|
|
|
#include "rephist.h"
|
2010-07-21 16:17:10 +02:00
|
|
|
#include "router.h"
|
2010-07-21 17:08:11 +02:00
|
|
|
#include "routerlist.h"
|
2007-12-17 23:44:11 +01:00
|
|
|
#include "ht.h"
|
2004-03-20 02:48:05 +01:00
|
|
|
|
2004-07-20 22:57:46 +02:00
|
|
|
static void bw_arrays_init(void);
|
2004-12-05 08:10:08 +01:00
|
|
|
static void predicted_ports_init(void);
|
2004-07-20 22:57:46 +02:00
|
|
|
|
2008-02-09 04:11:10 +01:00
|
|
|
/** Total number of bytes currently allocated in fields used by rephist.c. */
|
2005-07-18 08:09:04 +02:00
|
|
|
uint64_t rephist_total_alloc=0;
|
2008-02-09 04:11:10 +01:00
|
|
|
/** Number of or_history_t objects currently allocated. */
|
2005-07-18 08:09:04 +02:00
|
|
|
uint32_t rephist_total_num=0;
|
2005-06-06 19:03:21 +02:00
|
|
|
|
2007-08-21 07:37:24 +02:00
|
|
|
/** If the total weighted run count of all runs for a router ever falls
|
|
|
|
* below this amount, the router can be treated as having 0 MTBF. */
|
2007-08-16 21:32:29 +02:00
|
|
|
#define STABILITY_EPSILON 0.0001
|
2009-05-27 23:55:51 +02:00
|
|
|
/** Value by which to discount all old intervals for MTBF purposes. This
|
2007-08-21 07:37:24 +02:00
|
|
|
* is compounded every STABILITY_INTERVAL. */
|
|
|
|
#define STABILITY_ALPHA 0.95
|
|
|
|
/** Interval at which to discount all old intervals for MTBF purposes. */
|
2007-08-16 21:32:29 +02:00
|
|
|
#define STABILITY_INTERVAL (12*60*60)
|
2007-08-21 07:37:24 +02:00
|
|
|
/* (This combination of ALPHA, INTERVAL, and EPSILON makes it so that an
|
|
|
|
* interval that just ended counts twice as much as one that ended a week ago,
|
|
|
|
* 20X as much as one that ended a month ago, and routers that have had no
|
|
|
|
* uptime data for about half a year will get forgotten.) */
|
2007-08-16 21:32:29 +02:00
|
|
|
|
2004-05-10 06:34:48 +02:00
|
|
|
/** History of an OR-\>OR link. */
|
2004-03-20 02:48:05 +01:00
|
|
|
typedef struct link_history_t {
|
2004-05-10 06:34:48 +02:00
|
|
|
/** When did we start tracking this list? */
|
2004-03-20 02:48:05 +01:00
|
|
|
time_t since;
|
2004-11-21 06:14:46 +01:00
|
|
|
/** When did we most recently note a change to this link */
|
|
|
|
time_t changed;
|
2004-12-01 04:48:14 +01:00
|
|
|
/** How many times did extending from OR1 to OR2 succeed? */
|
2004-03-20 02:48:05 +01:00
|
|
|
unsigned long n_extend_ok;
|
2004-05-10 06:34:48 +02:00
|
|
|
/** How many times did extending from OR1 to OR2 fail? */
|
2004-03-20 02:48:05 +01:00
|
|
|
unsigned long n_extend_fail;
|
|
|
|
} link_history_t;
|
|
|
|
|
2004-05-10 06:34:48 +02:00
|
|
|
/** History of an OR. */
|
2004-03-20 02:48:05 +01:00
|
|
|
typedef struct or_history_t {
|
2004-05-10 06:34:48 +02:00
|
|
|
/** When did we start tracking this OR? */
|
2004-03-20 02:48:05 +01:00
|
|
|
time_t since;
|
2004-11-21 06:14:46 +01:00
|
|
|
/** When did we most recently note a change to this OR? */
|
|
|
|
time_t changed;
|
2004-05-10 06:34:48 +02:00
|
|
|
/** How many times did we successfully connect? */
|
2004-03-20 02:48:05 +01:00
|
|
|
unsigned long n_conn_ok;
|
2004-05-10 06:34:48 +02:00
|
|
|
/** How many times did we try to connect and fail?*/
|
2004-03-20 02:48:05 +01:00
|
|
|
unsigned long n_conn_fail;
|
2004-05-10 06:34:48 +02:00
|
|
|
/** How many seconds have we been connected to this OR before
|
2004-05-05 23:32:43 +02:00
|
|
|
* 'up_since'? */
|
2004-03-20 02:48:05 +01:00
|
|
|
unsigned long uptime;
|
2004-05-10 06:34:48 +02:00
|
|
|
/** How many seconds have we been unable to connect to this OR before
|
2004-05-05 23:32:43 +02:00
|
|
|
* 'down_since'? */
|
2004-03-20 02:48:05 +01:00
|
|
|
unsigned long downtime;
|
2004-05-10 06:34:48 +02:00
|
|
|
/** If nonzero, we have been connected since this time. */
|
2004-03-20 02:48:05 +01:00
|
|
|
time_t up_since;
|
2004-05-10 06:34:48 +02:00
|
|
|
/** If nonzero, we have been unable to connect since this time. */
|
2004-03-20 02:48:05 +01:00
|
|
|
time_t down_since;
|
2007-08-21 07:37:24 +02:00
|
|
|
|
|
|
|
/* === For MTBF tracking: */
|
|
|
|
/** Weighted sum total of all times that this router has been online.
|
|
|
|
*/
|
2007-08-16 21:32:29 +02:00
|
|
|
unsigned long weighted_run_length;
|
2007-08-21 07:37:24 +02:00
|
|
|
/** If the router is now online (according to stability-checking rules),
|
|
|
|
* when did it come online? */
|
2007-08-16 21:32:29 +02:00
|
|
|
time_t start_of_run;
|
2007-08-21 07:37:24 +02:00
|
|
|
/** Sum of weights for runs in weighted_run_length. */
|
2007-08-16 21:32:29 +02:00
|
|
|
double total_run_weights;
|
2007-09-17 20:27:43 +02:00
|
|
|
/* === For fractional uptime tracking: */
|
|
|
|
time_t start_of_downtime;
|
|
|
|
unsigned long weighted_uptime;
|
|
|
|
unsigned long total_weighted_time;
|
2007-08-21 07:37:24 +02:00
|
|
|
|
2004-07-17 00:23:18 +02:00
|
|
|
/** Map from hex OR2 identity digest to a link_history_t for the link
|
2004-05-05 23:32:43 +02:00
|
|
|
* from this OR to OR2. */
|
2005-10-18 22:12:22 +02:00
|
|
|
digestmap_t *link_history_map;
|
2004-03-20 02:48:05 +01:00
|
|
|
} or_history_t;
|
|
|
|
|
2007-08-21 07:37:24 +02:00
|
|
|
/** When did we last multiply all routers' weighted_run_length and
|
|
|
|
* total_run_weights by STABILITY_ALPHA? */
|
2007-08-16 21:32:29 +02:00
|
|
|
static time_t stability_last_downrated = 0;
|
|
|
|
|
2007-08-21 07:37:24 +02:00
|
|
|
/** */
|
|
|
|
static time_t started_tracking_stability = 0;
|
|
|
|
|
2004-07-17 00:23:18 +02:00
|
|
|
/** Map from hex OR identity digest to or_history_t. */
|
2005-10-18 22:12:22 +02:00
|
|
|
static digestmap_t *history_map = NULL;
|
2004-03-20 02:48:05 +01:00
|
|
|
|
2007-08-17 22:31:59 +02:00
|
|
|
/** Return the or_history_t for the OR with identity digest <b>id</b>,
|
|
|
|
* creating it if necessary. */
|
2005-06-11 20:52:12 +02:00
|
|
|
static or_history_t *
|
|
|
|
get_or_history(const char* id)
|
2004-03-20 02:48:05 +01:00
|
|
|
{
|
|
|
|
or_history_t *hist;
|
2004-07-13 20:23:40 +02:00
|
|
|
|
2006-07-23 07:52:27 +02:00
|
|
|
if (tor_mem_is_zero(id, DIGEST_LEN))
|
2004-08-17 09:12:05 +02:00
|
|
|
return NULL;
|
|
|
|
|
2005-10-18 22:12:22 +02:00
|
|
|
hist = digestmap_get(history_map, id);
|
2004-03-20 02:48:05 +01:00
|
|
|
if (!hist) {
|
|
|
|
hist = tor_malloc_zero(sizeof(or_history_t));
|
2005-06-06 19:03:21 +02:00
|
|
|
rephist_total_alloc += sizeof(or_history_t);
|
2005-07-18 08:09:04 +02:00
|
|
|
rephist_total_num++;
|
2005-10-18 22:12:22 +02:00
|
|
|
hist->link_history_map = digestmap_new();
|
2004-11-21 06:14:46 +01:00
|
|
|
hist->since = hist->changed = time(NULL);
|
2005-10-18 22:12:22 +02:00
|
|
|
digestmap_set(history_map, id, hist);
|
2004-03-20 02:48:05 +01:00
|
|
|
}
|
|
|
|
return hist;
|
|
|
|
}
|
|
|
|
|
2004-05-10 06:34:48 +02:00
|
|
|
/** Return the link_history_t for the link from the first named OR to
|
2004-07-17 00:23:18 +02:00
|
|
|
* the second, creating it if necessary. (ORs are identified by
|
2006-07-23 07:18:29 +02:00
|
|
|
* identity digest.)
|
2004-03-20 05:59:29 +01:00
|
|
|
*/
|
2005-06-11 20:52:12 +02:00
|
|
|
static link_history_t *
|
|
|
|
get_link_history(const char *from_id, const char *to_id)
|
2004-03-20 02:48:05 +01:00
|
|
|
{
|
|
|
|
or_history_t *orhist;
|
|
|
|
link_history_t *lhist;
|
2004-07-13 20:23:40 +02:00
|
|
|
orhist = get_or_history(from_id);
|
2004-08-17 09:12:05 +02:00
|
|
|
if (!orhist)
|
|
|
|
return NULL;
|
2006-07-23 07:52:27 +02:00
|
|
|
if (tor_mem_is_zero(to_id, DIGEST_LEN))
|
2004-08-17 09:12:05 +02:00
|
|
|
return NULL;
|
2005-10-18 22:12:22 +02:00
|
|
|
lhist = (link_history_t*) digestmap_get(orhist->link_history_map, to_id);
|
2004-03-20 02:48:05 +01:00
|
|
|
if (!lhist) {
|
|
|
|
lhist = tor_malloc_zero(sizeof(link_history_t));
|
2005-06-06 19:03:21 +02:00
|
|
|
rephist_total_alloc += sizeof(link_history_t);
|
2004-11-21 06:14:46 +01:00
|
|
|
lhist->since = lhist->changed = time(NULL);
|
2005-10-18 22:12:22 +02:00
|
|
|
digestmap_set(orhist->link_history_map, to_id, lhist);
|
2004-03-20 02:48:05 +01:00
|
|
|
}
|
|
|
|
return lhist;
|
|
|
|
}
|
|
|
|
|
2006-07-23 07:18:29 +02:00
|
|
|
/** Helper: free storage held by a single link history entry. */
|
2004-11-21 06:14:46 +01:00
|
|
|
static void
|
|
|
|
_free_link_history(void *val)
|
|
|
|
{
|
2005-06-06 19:03:21 +02:00
|
|
|
rephist_total_alloc -= sizeof(link_history_t);
|
2004-11-21 06:14:46 +01:00
|
|
|
tor_free(val);
|
|
|
|
}
|
|
|
|
|
2006-07-23 07:18:29 +02:00
|
|
|
/** Helper: free storage held by a single OR history entry. */
|
2004-11-21 06:14:46 +01:00
|
|
|
static void
|
2005-02-11 02:26:47 +01:00
|
|
|
free_or_history(void *_hist)
|
2004-11-21 06:14:46 +01:00
|
|
|
{
|
2005-02-11 02:26:47 +01:00
|
|
|
or_history_t *hist = _hist;
|
2005-10-18 22:12:22 +02:00
|
|
|
digestmap_free(hist->link_history_map, _free_link_history);
|
2005-06-06 19:03:21 +02:00
|
|
|
rephist_total_alloc -= sizeof(or_history_t);
|
2005-07-18 08:09:04 +02:00
|
|
|
rephist_total_num--;
|
2004-11-21 06:14:46 +01:00
|
|
|
tor_free(hist);
|
|
|
|
}
|
|
|
|
|
2004-05-10 06:34:48 +02:00
|
|
|
/** Update an or_history_t object <b>hist</b> so that its uptime/downtime
|
|
|
|
* count is up-to-date as of <b>when</b>.
|
2004-03-20 05:59:29 +01:00
|
|
|
*/
|
2005-06-11 20:52:12 +02:00
|
|
|
static void
|
|
|
|
update_or_history(or_history_t *hist, time_t when)
|
2004-03-20 02:48:05 +01:00
|
|
|
{
|
2004-04-25 22:37:37 +02:00
|
|
|
tor_assert(hist);
|
2004-03-20 02:48:05 +01:00
|
|
|
if (hist->up_since) {
|
2004-04-25 22:37:37 +02:00
|
|
|
tor_assert(!hist->down_since);
|
2004-03-20 02:48:05 +01:00
|
|
|
hist->uptime += (when - hist->up_since);
|
|
|
|
hist->up_since = when;
|
|
|
|
} else if (hist->down_since) {
|
|
|
|
hist->downtime += (when - hist->down_since);
|
|
|
|
hist->down_since = when;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-07-23 07:18:29 +02:00
|
|
|
/** Initialize the static data structures for tracking history. */
|
2005-06-11 20:52:12 +02:00
|
|
|
void
|
|
|
|
rep_hist_init(void)
|
2004-03-20 02:48:05 +01:00
|
|
|
{
|
2005-10-18 22:12:22 +02:00
|
|
|
history_map = digestmap_new();
|
2004-07-20 22:57:46 +02:00
|
|
|
bw_arrays_init();
|
2004-12-05 08:10:08 +01:00
|
|
|
predicted_ports_init();
|
2004-03-20 02:48:05 +01:00
|
|
|
}
|
|
|
|
|
2007-08-21 07:37:24 +02:00
|
|
|
/** Helper: note that we are no longer connected to the router with history
|
|
|
|
* <b>hist</b>. If <b>failed</b>, the connection failed; otherwise, it was
|
|
|
|
* closed correctly. */
|
2007-08-16 21:32:29 +02:00
|
|
|
static void
|
|
|
|
mark_or_down(or_history_t *hist, time_t when, int failed)
|
|
|
|
{
|
|
|
|
if (hist->up_since) {
|
|
|
|
hist->uptime += (when - hist->up_since);
|
|
|
|
hist->up_since = 0;
|
|
|
|
}
|
|
|
|
if (failed && !hist->down_since) {
|
|
|
|
hist->down_since = when;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-08-21 07:37:24 +02:00
|
|
|
/** Helper: note that we are connected to the router with history
|
|
|
|
* <b>hist</b>. */
|
2007-08-16 21:32:29 +02:00
|
|
|
static void
|
|
|
|
mark_or_up(or_history_t *hist, time_t when)
|
|
|
|
{
|
|
|
|
if (hist->down_since) {
|
|
|
|
hist->downtime += (when - hist->down_since);
|
|
|
|
hist->down_since = 0;
|
|
|
|
}
|
|
|
|
if (!hist->up_since) {
|
|
|
|
hist->up_since = when;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-07-17 00:23:18 +02:00
|
|
|
/** Remember that an attempt to connect to the OR with identity digest
|
|
|
|
* <b>id</b> failed at <b>when</b>.
|
2004-03-20 05:59:29 +01:00
|
|
|
*/
|
2005-06-11 20:52:12 +02:00
|
|
|
void
|
|
|
|
rep_hist_note_connect_failed(const char* id, time_t when)
|
2004-03-20 02:48:05 +01:00
|
|
|
{
|
|
|
|
or_history_t *hist;
|
2004-07-13 20:23:40 +02:00
|
|
|
hist = get_or_history(id);
|
2004-08-17 09:12:05 +02:00
|
|
|
if (!hist)
|
|
|
|
return;
|
2004-03-20 02:48:05 +01:00
|
|
|
++hist->n_conn_fail;
|
2007-08-16 21:32:29 +02:00
|
|
|
mark_or_down(hist, when, 1);
|
2004-11-21 06:14:46 +01:00
|
|
|
hist->changed = when;
|
2004-03-20 02:48:05 +01:00
|
|
|
}
|
|
|
|
|
2004-07-17 00:23:18 +02:00
|
|
|
/** Remember that an attempt to connect to the OR with identity digest
|
|
|
|
* <b>id</b> succeeded at <b>when</b>.
|
2004-03-20 05:59:29 +01:00
|
|
|
*/
|
2005-06-11 20:52:12 +02:00
|
|
|
void
|
|
|
|
rep_hist_note_connect_succeeded(const char* id, time_t when)
|
2004-03-20 02:48:05 +01:00
|
|
|
{
|
|
|
|
or_history_t *hist;
|
2004-07-13 20:23:40 +02:00
|
|
|
hist = get_or_history(id);
|
2004-08-17 09:12:05 +02:00
|
|
|
if (!hist)
|
|
|
|
return;
|
2004-03-20 02:48:05 +01:00
|
|
|
++hist->n_conn_ok;
|
2007-08-16 21:32:29 +02:00
|
|
|
mark_or_up(hist, when);
|
2004-11-21 06:14:46 +01:00
|
|
|
hist->changed = when;
|
2004-03-20 02:48:05 +01:00
|
|
|
}
|
2004-03-20 05:59:29 +01:00
|
|
|
|
2004-05-10 06:34:48 +02:00
|
|
|
/** Remember that we intentionally closed our connection to the OR
|
2004-07-17 00:23:18 +02:00
|
|
|
* with identity digest <b>id</b> at <b>when</b>.
|
2004-03-20 05:59:29 +01:00
|
|
|
*/
|
2005-06-11 20:52:12 +02:00
|
|
|
void
|
|
|
|
rep_hist_note_disconnect(const char* id, time_t when)
|
2004-03-20 05:59:29 +01:00
|
|
|
{
|
|
|
|
or_history_t *hist;
|
2004-07-13 20:23:40 +02:00
|
|
|
hist = get_or_history(id);
|
2004-08-17 09:12:05 +02:00
|
|
|
if (!hist)
|
|
|
|
return;
|
2007-08-16 21:32:29 +02:00
|
|
|
mark_or_down(hist, when, 0);
|
2004-11-21 06:14:46 +01:00
|
|
|
hist->changed = when;
|
2004-03-20 05:59:29 +01:00
|
|
|
}
|
|
|
|
|
2004-07-17 00:23:18 +02:00
|
|
|
/** Remember that our connection to the OR with identity digest
|
|
|
|
* <b>id</b> had an error and stopped working at <b>when</b>.
|
2004-03-20 05:59:29 +01:00
|
|
|
*/
|
2005-06-11 20:52:12 +02:00
|
|
|
void
|
|
|
|
rep_hist_note_connection_died(const char* id, time_t when)
|
2004-03-20 02:48:05 +01:00
|
|
|
{
|
|
|
|
or_history_t *hist;
|
2004-11-28 10:05:49 +01:00
|
|
|
if (!id) {
|
2007-08-16 21:32:29 +02:00
|
|
|
/* If conn has no identity, it didn't complete its handshake, or something
|
2006-04-18 05:07:24 +02:00
|
|
|
* went wrong. Ignore it.
|
2004-03-20 21:37:49 +01:00
|
|
|
*/
|
|
|
|
return;
|
|
|
|
}
|
2004-07-13 20:23:40 +02:00
|
|
|
hist = get_or_history(id);
|
2004-08-17 09:12:05 +02:00
|
|
|
if (!hist)
|
|
|
|
return;
|
2007-08-16 21:32:29 +02:00
|
|
|
mark_or_down(hist, when, 1);
|
2004-11-21 06:14:46 +01:00
|
|
|
hist->changed = when;
|
2004-03-20 02:48:05 +01:00
|
|
|
}
|
|
|
|
|
2008-09-23 20:24:20 +02:00
|
|
|
/** We have just decided that this router with identity digest <b>id</b> is
|
|
|
|
* reachable, meaning we will give it a "Running" flag for the next while. */
|
2007-08-17 03:29:58 +02:00
|
|
|
void
|
|
|
|
rep_hist_note_router_reachable(const char *id, time_t when)
|
|
|
|
{
|
2007-08-17 22:31:54 +02:00
|
|
|
or_history_t *hist = get_or_history(id);
|
2008-09-23 20:24:20 +02:00
|
|
|
int was_in_run = 1;
|
|
|
|
char tbuf[ISO_TIME_LEN+1];
|
|
|
|
|
2008-09-30 15:59:12 +02:00
|
|
|
tor_assert(hist);
|
|
|
|
|
2007-08-21 07:37:24 +02:00
|
|
|
if (!started_tracking_stability)
|
|
|
|
started_tracking_stability = time(NULL);
|
2008-09-30 15:59:12 +02:00
|
|
|
if (!hist->start_of_run) {
|
2007-08-17 22:31:54 +02:00
|
|
|
hist->start_of_run = when;
|
2008-09-23 20:24:20 +02:00
|
|
|
was_in_run = 0;
|
2007-08-17 22:31:54 +02:00
|
|
|
}
|
2008-09-30 15:59:12 +02:00
|
|
|
if (hist->start_of_downtime) {
|
2008-09-23 20:24:20 +02:00
|
|
|
long down_length;
|
|
|
|
|
|
|
|
format_local_iso_time(tbuf, hist->start_of_downtime);
|
|
|
|
log_info(LD_HIST, "Router %s is now Running; it had been down since %s.",
|
|
|
|
hex_str(id, DIGEST_LEN), tbuf);
|
|
|
|
if (was_in_run)
|
|
|
|
log_info(LD_HIST, " (Paradoxically, it was already Running too.)");
|
|
|
|
|
|
|
|
down_length = when - hist->start_of_downtime;
|
2007-09-17 20:27:43 +02:00
|
|
|
hist->total_weighted_time += down_length;
|
|
|
|
hist->start_of_downtime = 0;
|
2008-09-23 20:24:20 +02:00
|
|
|
} else {
|
|
|
|
format_local_iso_time(tbuf, hist->start_of_run);
|
|
|
|
if (was_in_run)
|
|
|
|
log_debug(LD_HIST, "Router %s is still Running; it has been Running "
|
|
|
|
"since %s", hex_str(id, DIGEST_LEN), tbuf);
|
|
|
|
else
|
2008-09-25 22:22:17 +02:00
|
|
|
log_info(LD_HIST,"Router %s is now Running; it was previously untracked",
|
2008-09-23 20:24:20 +02:00
|
|
|
hex_str(id, DIGEST_LEN));
|
2007-09-17 20:27:43 +02:00
|
|
|
}
|
2007-08-17 03:29:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/** We have just decided that this router is unreachable, meaning
|
|
|
|
* we are taking away its "Running" flag. */
|
|
|
|
void
|
|
|
|
rep_hist_note_router_unreachable(const char *id, time_t when)
|
|
|
|
{
|
2007-08-17 22:31:54 +02:00
|
|
|
or_history_t *hist = get_or_history(id);
|
2008-09-23 20:24:20 +02:00
|
|
|
char tbuf[ISO_TIME_LEN+1];
|
|
|
|
int was_running = 0;
|
2007-08-21 07:37:24 +02:00
|
|
|
if (!started_tracking_stability)
|
|
|
|
started_tracking_stability = time(NULL);
|
2008-09-30 15:59:12 +02:00
|
|
|
|
|
|
|
tor_assert(hist);
|
|
|
|
if (hist->start_of_run) {
|
2008-02-16 00:39:04 +01:00
|
|
|
/*XXXX We could treat failed connections differently from failed
|
2008-04-16 02:12:44 +02:00
|
|
|
* connect attempts. */
|
2007-08-17 22:31:54 +02:00
|
|
|
long run_length = when - hist->start_of_run;
|
2008-09-23 20:24:20 +02:00
|
|
|
format_local_iso_time(tbuf, hist->start_of_run);
|
|
|
|
|
2007-08-17 22:31:54 +02:00
|
|
|
hist->weighted_run_length += run_length;
|
|
|
|
hist->total_run_weights += 1.0;
|
|
|
|
hist->start_of_run = 0;
|
2007-09-17 20:27:43 +02:00
|
|
|
hist->weighted_uptime += run_length;
|
|
|
|
hist->total_weighted_time += run_length;
|
2008-09-23 20:24:20 +02:00
|
|
|
|
|
|
|
was_running = 1;
|
|
|
|
log_info(LD_HIST, "Router %s is now non-Running: it had previously been "
|
|
|
|
"Running since %s. Its total weighted uptime is %lu/%lu.",
|
|
|
|
hex_str(id, DIGEST_LEN), tbuf, hist->weighted_uptime,
|
|
|
|
hist->total_weighted_time);
|
2007-09-17 20:27:43 +02:00
|
|
|
}
|
2008-09-30 15:59:12 +02:00
|
|
|
if (!hist->start_of_downtime) {
|
2007-09-17 20:27:43 +02:00
|
|
|
hist->start_of_downtime = when;
|
2008-09-23 20:24:20 +02:00
|
|
|
|
|
|
|
if (!was_running)
|
|
|
|
log_info(LD_HIST, "Router %s is now non-Running; it was previously "
|
|
|
|
"untracked.", hex_str(id, DIGEST_LEN));
|
|
|
|
} else {
|
|
|
|
if (!was_running) {
|
|
|
|
format_local_iso_time(tbuf, hist->start_of_downtime);
|
|
|
|
|
|
|
|
log_info(LD_HIST, "Router %s is still non-Running; it has been "
|
|
|
|
"non-Running since %s.", hex_str(id, DIGEST_LEN), tbuf);
|
|
|
|
}
|
2007-08-17 22:31:54 +02:00
|
|
|
}
|
2007-08-17 03:29:58 +02:00
|
|
|
}
|
|
|
|
|
2007-08-21 07:37:24 +02:00
|
|
|
/** Helper: Discount all old MTBF data, if it is time to do so. Return
|
|
|
|
* the time at which we should next discount MTBF data. */
|
2007-08-16 21:32:29 +02:00
|
|
|
time_t
|
|
|
|
rep_hist_downrate_old_runs(time_t now)
|
|
|
|
{
|
|
|
|
digestmap_iter_t *orhist_it;
|
|
|
|
const char *digest1;
|
|
|
|
or_history_t *hist;
|
|
|
|
void *hist_p;
|
|
|
|
double alpha = 1.0;
|
|
|
|
|
|
|
|
if (!history_map)
|
|
|
|
history_map = digestmap_new();
|
|
|
|
if (!stability_last_downrated)
|
|
|
|
stability_last_downrated = now;
|
|
|
|
if (stability_last_downrated + STABILITY_INTERVAL > now)
|
|
|
|
return stability_last_downrated + STABILITY_INTERVAL;
|
|
|
|
|
2007-08-21 07:37:24 +02:00
|
|
|
/* Okay, we should downrate the data. By how much? */
|
2007-08-16 21:32:29 +02:00
|
|
|
while (stability_last_downrated + STABILITY_INTERVAL < now) {
|
|
|
|
stability_last_downrated += STABILITY_INTERVAL;
|
|
|
|
alpha *= STABILITY_ALPHA;
|
|
|
|
}
|
|
|
|
|
2008-09-23 20:24:20 +02:00
|
|
|
log_info(LD_HIST, "Discounting all old stability info by a factor of %lf",
|
|
|
|
alpha);
|
|
|
|
|
2007-08-21 07:37:24 +02:00
|
|
|
/* Multiply every w_r_l, t_r_w pair by alpha. */
|
2007-08-16 21:32:29 +02:00
|
|
|
for (orhist_it = digestmap_iter_init(history_map);
|
|
|
|
!digestmap_iter_done(orhist_it);
|
|
|
|
orhist_it = digestmap_iter_next(history_map,orhist_it)) {
|
|
|
|
digestmap_iter_get(orhist_it, &digest1, &hist_p);
|
|
|
|
hist = hist_p;
|
|
|
|
|
|
|
|
hist->weighted_run_length =
|
|
|
|
(unsigned long)(hist->weighted_run_length * alpha);
|
|
|
|
hist->total_run_weights *= alpha;
|
2007-09-17 20:27:43 +02:00
|
|
|
|
2008-02-21 22:15:31 +01:00
|
|
|
hist->weighted_uptime = (unsigned long)(hist->weighted_uptime * alpha);
|
|
|
|
hist->total_weighted_time = (unsigned long)
|
|
|
|
(hist->total_weighted_time * alpha);
|
2007-08-16 21:32:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return stability_last_downrated + STABILITY_INTERVAL;
|
|
|
|
}
|
|
|
|
|
2007-08-21 07:37:24 +02:00
|
|
|
/** Helper: Return the weighted MTBF of the router with history <b>hist</b>. */
|
2007-08-17 22:31:54 +02:00
|
|
|
static double
|
|
|
|
get_stability(or_history_t *hist, time_t when)
|
2007-08-16 21:32:29 +02:00
|
|
|
{
|
2007-08-17 22:31:54 +02:00
|
|
|
unsigned long total = hist->weighted_run_length;
|
|
|
|
double total_weights = hist->total_run_weights;
|
2007-08-16 21:32:29 +02:00
|
|
|
|
|
|
|
if (hist->start_of_run) {
|
2007-08-21 07:37:24 +02:00
|
|
|
/* We're currently in a run. Let total and total_weights hold the values
|
|
|
|
* they would hold if the current run were to end now. */
|
2007-08-16 21:32:29 +02:00
|
|
|
total += (when-hist->start_of_run);
|
|
|
|
total_weights += 1.0;
|
|
|
|
}
|
2007-08-21 07:37:24 +02:00
|
|
|
if (total_weights < STABILITY_EPSILON) {
|
|
|
|
/* Round down to zero, and avoid divide-by-zero. */
|
2007-08-16 21:32:29 +02:00
|
|
|
return 0.0;
|
2007-08-21 07:37:24 +02:00
|
|
|
}
|
2007-08-16 21:32:29 +02:00
|
|
|
|
|
|
|
return total / total_weights;
|
|
|
|
}
|
|
|
|
|
2008-02-16 00:39:04 +01:00
|
|
|
/** Return the total amount of time we've been observing, with each run of
|
|
|
|
* time downrated by the appropriate factor. */
|
2007-11-08 17:58:59 +01:00
|
|
|
static long
|
|
|
|
get_total_weighted_time(or_history_t *hist, time_t when)
|
|
|
|
{
|
|
|
|
long total = hist->total_weighted_time;
|
|
|
|
if (hist->start_of_run) {
|
|
|
|
total += (when - hist->start_of_run);
|
|
|
|
} else if (hist->start_of_downtime) {
|
|
|
|
total += (when - hist->start_of_downtime);
|
|
|
|
}
|
|
|
|
return total;
|
|
|
|
}
|
2007-10-29 00:44:57 +01:00
|
|
|
|
2007-10-04 18:21:58 +02:00
|
|
|
/** Helper: Return the weighted percent-of-time-online of the router with
|
|
|
|
* history <b>hist</b>. */
|
2007-09-17 20:27:43 +02:00
|
|
|
static double
|
|
|
|
get_weighted_fractional_uptime(or_history_t *hist, time_t when)
|
|
|
|
{
|
|
|
|
unsigned long total = hist->total_weighted_time;
|
|
|
|
unsigned long up = hist->weighted_uptime;
|
|
|
|
|
|
|
|
if (hist->start_of_run) {
|
|
|
|
long run_length = (when - hist->start_of_run);
|
|
|
|
up += run_length;
|
|
|
|
total += run_length;
|
|
|
|
} else if (hist->start_of_downtime) {
|
|
|
|
total += (when - hist->start_of_downtime);
|
|
|
|
}
|
2008-09-28 17:48:36 +02:00
|
|
|
|
|
|
|
if (!total) {
|
|
|
|
/* Avoid calling anybody's uptime infinity (which should be impossible if
|
|
|
|
* the code is working), or NaN (which can happen for any router we haven't
|
|
|
|
* observed up or down yet). */
|
|
|
|
return 0.0;
|
|
|
|
}
|
|
|
|
|
2007-09-17 20:27:43 +02:00
|
|
|
return ((double) up) / total;
|
|
|
|
}
|
|
|
|
|
2007-08-21 07:37:24 +02:00
|
|
|
/** Return an estimated MTBF for the router whose identity digest is
|
|
|
|
* <b>id</b>. Return 0 if the router is unknown. */
|
2007-08-17 22:31:54 +02:00
|
|
|
double
|
|
|
|
rep_hist_get_stability(const char *id, time_t when)
|
|
|
|
{
|
|
|
|
or_history_t *hist = get_or_history(id);
|
|
|
|
if (!hist)
|
|
|
|
return 0.0;
|
|
|
|
|
|
|
|
return get_stability(hist, when);
|
|
|
|
}
|
|
|
|
|
2007-10-04 18:21:58 +02:00
|
|
|
/** Return an estimated percent-of-time-online for the router whose identity
|
|
|
|
* digest is <b>id</b>. Return 0 if the router is unknown. */
|
2007-09-17 20:27:43 +02:00
|
|
|
double
|
|
|
|
rep_hist_get_weighted_fractional_uptime(const char *id, time_t when)
|
|
|
|
{
|
|
|
|
or_history_t *hist = get_or_history(id);
|
|
|
|
if (!hist)
|
|
|
|
return 0.0;
|
|
|
|
|
|
|
|
return get_weighted_fractional_uptime(hist, when);
|
|
|
|
}
|
|
|
|
|
2007-11-08 17:58:59 +01:00
|
|
|
/** Return a number representing how long we've known about the router whose
|
|
|
|
* digest is <b>id</b>. Return 0 if the router is unknown.
|
|
|
|
*
|
2009-05-27 23:55:51 +02:00
|
|
|
* Be careful: this measure increases monotonically as we know the router for
|
2007-11-08 17:58:59 +01:00
|
|
|
* longer and longer, but it doesn't increase linearly.
|
|
|
|
*/
|
|
|
|
long
|
|
|
|
rep_hist_get_weighted_time_known(const char *id, time_t when)
|
|
|
|
{
|
|
|
|
or_history_t *hist = get_or_history(id);
|
|
|
|
if (!hist)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return get_total_weighted_time(hist, when);
|
|
|
|
}
|
|
|
|
|
2007-08-21 07:37:24 +02:00
|
|
|
/** Return true if we've been measuring MTBFs for long enough to
|
2007-11-10 08:26:56 +01:00
|
|
|
* pronounce on Stability. */
|
2007-08-21 07:37:24 +02:00
|
|
|
int
|
|
|
|
rep_hist_have_measured_enough_stability(void)
|
|
|
|
{
|
2008-02-16 00:39:04 +01:00
|
|
|
/* XXXX021 This doesn't do so well when we change our opinion
|
2007-08-21 07:37:24 +02:00
|
|
|
* as to whether we're tracking router stability. */
|
|
|
|
return started_tracking_stability < time(NULL) - 4*60*60;
|
|
|
|
}
|
|
|
|
|
2004-07-17 00:23:18 +02:00
|
|
|
/** Remember that we successfully extended from the OR with identity
|
|
|
|
* digest <b>from_id</b> to the OR with identity digest
|
2006-07-23 07:18:29 +02:00
|
|
|
* <b>to_name</b>.
|
2004-03-20 05:59:29 +01:00
|
|
|
*/
|
2005-06-11 20:52:12 +02:00
|
|
|
void
|
|
|
|
rep_hist_note_extend_succeeded(const char *from_id, const char *to_id)
|
2004-03-20 02:48:05 +01:00
|
|
|
{
|
|
|
|
link_history_t *hist;
|
2004-03-20 05:59:29 +01:00
|
|
|
/* log_fn(LOG_WARN, "EXTEND SUCCEEDED: %s->%s",from_name,to_name); */
|
2004-07-13 20:23:40 +02:00
|
|
|
hist = get_link_history(from_id, to_id);
|
2004-08-17 09:12:05 +02:00
|
|
|
if (!hist)
|
|
|
|
return;
|
2004-03-20 02:48:05 +01:00
|
|
|
++hist->n_extend_ok;
|
2004-11-21 06:14:46 +01:00
|
|
|
hist->changed = time(NULL);
|
2004-03-20 02:48:05 +01:00
|
|
|
}
|
2004-03-20 05:59:29 +01:00
|
|
|
|
2004-07-17 00:23:18 +02:00
|
|
|
/** Remember that we tried to extend from the OR with identity digest
|
|
|
|
* <b>from_id</b> to the OR with identity digest <b>to_name</b>, but
|
|
|
|
* failed.
|
2004-03-20 05:59:29 +01:00
|
|
|
*/
|
2005-06-11 20:52:12 +02:00
|
|
|
void
|
|
|
|
rep_hist_note_extend_failed(const char *from_id, const char *to_id)
|
2004-03-20 02:48:05 +01:00
|
|
|
{
|
|
|
|
link_history_t *hist;
|
2004-03-20 05:59:29 +01:00
|
|
|
/* log_fn(LOG_WARN, "EXTEND FAILED: %s->%s",from_name,to_name); */
|
2004-07-13 20:23:40 +02:00
|
|
|
hist = get_link_history(from_id, to_id);
|
2004-08-17 09:12:05 +02:00
|
|
|
if (!hist)
|
|
|
|
return;
|
2004-03-20 02:48:05 +01:00
|
|
|
++hist->n_extend_fail;
|
2004-11-21 06:14:46 +01:00
|
|
|
hist->changed = time(NULL);
|
2004-03-20 02:48:05 +01:00
|
|
|
}
|
|
|
|
|
2004-12-01 03:54:13 +01:00
|
|
|
/** Log all the reliability data we have remembered, with the chosen
|
2004-03-20 05:59:29 +01:00
|
|
|
* severity.
|
|
|
|
*/
|
2005-06-11 20:52:12 +02:00
|
|
|
void
|
|
|
|
rep_hist_dump_stats(time_t now, int severity)
|
2004-03-20 02:48:05 +01:00
|
|
|
{
|
2005-10-18 22:12:22 +02:00
|
|
|
digestmap_iter_t *lhist_it;
|
|
|
|
digestmap_iter_t *orhist_it;
|
|
|
|
const char *name1, *name2, *digest1, *digest2;
|
|
|
|
char hexdigest1[HEX_DIGEST_LEN+1];
|
2004-03-20 02:48:05 +01:00
|
|
|
or_history_t *or_history;
|
|
|
|
link_history_t *link_history;
|
2004-03-29 08:18:04 +02:00
|
|
|
void *or_history_p, *link_history_p;
|
2004-03-20 02:48:05 +01:00
|
|
|
double uptime;
|
|
|
|
char buffer[2048];
|
2004-10-14 04:47:09 +02:00
|
|
|
size_t len;
|
2004-10-27 23:54:44 +02:00
|
|
|
int ret;
|
2004-03-20 05:59:29 +01:00
|
|
|
unsigned long upt, downt;
|
Initial conversion to use node_t throughout our codebase.
A node_t is an abstraction over routerstatus_t, routerinfo_t, and
microdesc_t. It should try to present a consistent interface to all
of them. There should be a node_t for a server whenever there is
* A routerinfo_t for it in the routerlist
* A routerstatus_t in the current_consensus.
(note that a microdesc_t alone isn't enough to make a node_t exist,
since microdescriptors aren't usable on their own.)
There are three ways to get a node_t right now: looking it up by ID,
looking it up by nickname, and iterating over the whole list of
microdescriptors.
All (or nearly all) functions that are supposed to return "a router"
-- especially those used in building connections and circuits --
should return a node_t, not a routerinfo_t or a routerstatus_t.
A node_t should hold all the *mutable* flags about a node. This
patch moves the is_foo flags from routerinfo_t into node_t. The
flags in routerstatus_t remain, but they get set from the consensus
and should not change.
Some other highlights of this patch are:
* Looking up routerinfo and routerstatus by nickname is now
unified and based on the "look up a node by nickname" function.
This tries to look only at the values from current consensus,
and not get confused by the routerinfo_t->is_named flag, which
could get set for other weird reasons. This changes the
behavior of how authorities (when acting as clients) deal with
nodes that have been listed by nickname.
* I tried not to artificially increase the size of the diff here
by moving functions around. As a result, some functions that
now operate on nodes are now in the wrong file -- they should
get moved to nodelist.c once this refactoring settles down.
This moving should happen as part of a patch that moves
functions AND NOTHING ELSE.
* Some old code is now left around inside #if 0/1 blocks, and
should get removed once I've verified that I don't want it
sitting around to see how we used to do things.
There are still some unimplemented functions: these are flagged
with "UNIMPLEMENTED_NODELIST()." I'll work on filling in the
implementation here, piece by piece.
I wish this patch could have been smaller, but there did not seem to
be any piece of it that was independent from the rest. Moving flags
forces many functions that once returned routerinfo_t * to return
node_t *, which forces their friends to change, and so on.
2010-09-29 21:00:41 +02:00
|
|
|
const node_t *node;
|
2004-03-20 02:48:05 +01:00
|
|
|
|
2005-07-25 12:29:21 +02:00
|
|
|
rep_history_clean(now - get_options()->RephistTrackTime);
|
2004-11-21 06:14:46 +01:00
|
|
|
|
2008-09-23 20:24:20 +02:00
|
|
|
log(severity, LD_HIST, "--------------- Dumping history information:");
|
2004-03-20 02:48:05 +01:00
|
|
|
|
2005-12-14 21:40:40 +01:00
|
|
|
for (orhist_it = digestmap_iter_init(history_map);
|
|
|
|
!digestmap_iter_done(orhist_it);
|
2005-10-18 22:12:22 +02:00
|
|
|
orhist_it = digestmap_iter_next(history_map,orhist_it)) {
|
2007-08-17 22:31:54 +02:00
|
|
|
double s;
|
|
|
|
long stability;
|
2005-10-18 22:12:22 +02:00
|
|
|
digestmap_iter_get(orhist_it, &digest1, &or_history_p);
|
2004-03-29 08:18:04 +02:00
|
|
|
or_history = (or_history_t*) or_history_p;
|
2004-03-20 02:48:05 +01:00
|
|
|
|
Initial conversion to use node_t throughout our codebase.
A node_t is an abstraction over routerstatus_t, routerinfo_t, and
microdesc_t. It should try to present a consistent interface to all
of them. There should be a node_t for a server whenever there is
* A routerinfo_t for it in the routerlist
* A routerstatus_t in the current_consensus.
(note that a microdesc_t alone isn't enough to make a node_t exist,
since microdescriptors aren't usable on their own.)
There are three ways to get a node_t right now: looking it up by ID,
looking it up by nickname, and iterating over the whole list of
microdescriptors.
All (or nearly all) functions that are supposed to return "a router"
-- especially those used in building connections and circuits --
should return a node_t, not a routerinfo_t or a routerstatus_t.
A node_t should hold all the *mutable* flags about a node. This
patch moves the is_foo flags from routerinfo_t into node_t. The
flags in routerstatus_t remain, but they get set from the consensus
and should not change.
Some other highlights of this patch are:
* Looking up routerinfo and routerstatus by nickname is now
unified and based on the "look up a node by nickname" function.
This tries to look only at the values from current consensus,
and not get confused by the routerinfo_t->is_named flag, which
could get set for other weird reasons. This changes the
behavior of how authorities (when acting as clients) deal with
nodes that have been listed by nickname.
* I tried not to artificially increase the size of the diff here
by moving functions around. As a result, some functions that
now operate on nodes are now in the wrong file -- they should
get moved to nodelist.c once this refactoring settles down.
This moving should happen as part of a patch that moves
functions AND NOTHING ELSE.
* Some old code is now left around inside #if 0/1 blocks, and
should get removed once I've verified that I don't want it
sitting around to see how we used to do things.
There are still some unimplemented functions: these are flagged
with "UNIMPLEMENTED_NODELIST()." I'll work on filling in the
implementation here, piece by piece.
I wish this patch could have been smaller, but there did not seem to
be any piece of it that was independent from the rest. Moving flags
forces many functions that once returned routerinfo_t * to return
node_t *, which forces their friends to change, and so on.
2010-09-29 21:00:41 +02:00
|
|
|
if ((node = node_get_by_id(digest1)) && node_get_nickname(node))
|
|
|
|
name1 = node_get_nickname(node);
|
2004-07-13 20:23:40 +02:00
|
|
|
else
|
|
|
|
name1 = "(unknown)";
|
2005-10-18 22:12:22 +02:00
|
|
|
base16_encode(hexdigest1, sizeof(hexdigest1), digest1, DIGEST_LEN);
|
2004-03-20 02:48:05 +01:00
|
|
|
update_or_history(or_history, now);
|
2004-03-20 05:59:29 +01:00
|
|
|
upt = or_history->uptime;
|
|
|
|
downt = or_history->downtime;
|
2007-08-17 22:31:54 +02:00
|
|
|
s = get_stability(or_history, now);
|
|
|
|
stability = (long)s;
|
2004-03-20 05:59:29 +01:00
|
|
|
if (upt+downt) {
|
|
|
|
uptime = ((double)upt) / (upt+downt);
|
|
|
|
} else {
|
|
|
|
uptime=1.0;
|
|
|
|
}
|
2008-09-23 20:24:20 +02:00
|
|
|
log(severity, LD_HIST,
|
2007-08-17 22:31:54 +02:00
|
|
|
"OR %s [%s]: %ld/%ld good connections; uptime %ld/%ld sec (%.2f%%); "
|
2007-08-30 23:12:34 +02:00
|
|
|
"wmtbf %lu:%02lu:%02lu",
|
2004-07-13 20:23:40 +02:00
|
|
|
name1, hexdigest1,
|
2004-04-03 04:14:20 +02:00
|
|
|
or_history->n_conn_ok, or_history->n_conn_fail+or_history->n_conn_ok,
|
2007-08-17 22:31:54 +02:00
|
|
|
upt, upt+downt, uptime*100.0,
|
|
|
|
stability/3600, (stability/60)%60, stability%60);
|
2004-03-20 02:48:05 +01:00
|
|
|
|
2005-10-18 22:12:22 +02:00
|
|
|
if (!digestmap_isempty(or_history->link_history_map)) {
|
2004-12-04 09:56:59 +01:00
|
|
|
strlcpy(buffer, " Extend attempts: ", sizeof(buffer));
|
2004-08-18 11:57:50 +02:00
|
|
|
len = strlen(buffer);
|
2005-10-18 22:12:22 +02:00
|
|
|
for (lhist_it = digestmap_iter_init(or_history->link_history_map);
|
|
|
|
!digestmap_iter_done(lhist_it);
|
2005-12-14 21:40:40 +01:00
|
|
|
lhist_it = digestmap_iter_next(or_history->link_history_map,
|
|
|
|
lhist_it)) {
|
2005-10-18 22:12:22 +02:00
|
|
|
digestmap_iter_get(lhist_it, &digest2, &link_history_p);
|
Initial conversion to use node_t throughout our codebase.
A node_t is an abstraction over routerstatus_t, routerinfo_t, and
microdesc_t. It should try to present a consistent interface to all
of them. There should be a node_t for a server whenever there is
* A routerinfo_t for it in the routerlist
* A routerstatus_t in the current_consensus.
(note that a microdesc_t alone isn't enough to make a node_t exist,
since microdescriptors aren't usable on their own.)
There are three ways to get a node_t right now: looking it up by ID,
looking it up by nickname, and iterating over the whole list of
microdescriptors.
All (or nearly all) functions that are supposed to return "a router"
-- especially those used in building connections and circuits --
should return a node_t, not a routerinfo_t or a routerstatus_t.
A node_t should hold all the *mutable* flags about a node. This
patch moves the is_foo flags from routerinfo_t into node_t. The
flags in routerstatus_t remain, but they get set from the consensus
and should not change.
Some other highlights of this patch are:
* Looking up routerinfo and routerstatus by nickname is now
unified and based on the "look up a node by nickname" function.
This tries to look only at the values from current consensus,
and not get confused by the routerinfo_t->is_named flag, which
could get set for other weird reasons. This changes the
behavior of how authorities (when acting as clients) deal with
nodes that have been listed by nickname.
* I tried not to artificially increase the size of the diff here
by moving functions around. As a result, some functions that
now operate on nodes are now in the wrong file -- they should
get moved to nodelist.c once this refactoring settles down.
This moving should happen as part of a patch that moves
functions AND NOTHING ELSE.
* Some old code is now left around inside #if 0/1 blocks, and
should get removed once I've verified that I don't want it
sitting around to see how we used to do things.
There are still some unimplemented functions: these are flagged
with "UNIMPLEMENTED_NODELIST()." I'll work on filling in the
implementation here, piece by piece.
I wish this patch could have been smaller, but there did not seem to
be any piece of it that was independent from the rest. Moving flags
forces many functions that once returned routerinfo_t * to return
node_t *, which forces their friends to change, and so on.
2010-09-29 21:00:41 +02:00
|
|
|
if ((node = node_get_by_id(digest2)) && node_get_nickname(node))
|
|
|
|
name2 = node_get_nickname(node);
|
2004-08-18 11:57:50 +02:00
|
|
|
else
|
|
|
|
name2 = "(unknown)";
|
2004-07-13 20:23:40 +02:00
|
|
|
|
2004-08-18 11:57:50 +02:00
|
|
|
link_history = (link_history_t*) link_history_p;
|
2004-11-09 21:04:00 +01:00
|
|
|
|
2004-10-27 23:54:44 +02:00
|
|
|
ret = tor_snprintf(buffer+len, 2048-len, "%s(%ld/%ld); ", name2,
|
2004-08-18 11:57:50 +02:00
|
|
|
link_history->n_extend_ok,
|
|
|
|
link_history->n_extend_ok+link_history->n_extend_fail);
|
2004-10-27 23:54:44 +02:00
|
|
|
if (ret<0)
|
2004-08-18 11:57:50 +02:00
|
|
|
break;
|
2004-10-27 23:30:47 +02:00
|
|
|
else
|
2004-10-27 23:54:44 +02:00
|
|
|
len += ret;
|
2004-03-20 02:48:05 +01:00
|
|
|
}
|
2008-09-23 20:24:20 +02:00
|
|
|
log(severity, LD_HIST, "%s", buffer);
|
2004-03-20 02:48:05 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-11-21 06:14:46 +01:00
|
|
|
/** Remove history info for routers/links that haven't changed since
|
2006-07-23 07:18:29 +02:00
|
|
|
* <b>before</b>.
|
|
|
|
*/
|
2005-06-11 20:52:12 +02:00
|
|
|
void
|
|
|
|
rep_history_clean(time_t before)
|
2004-11-21 06:14:46 +01:00
|
|
|
{
|
2007-08-17 22:31:54 +02:00
|
|
|
int authority = authdir_mode(get_options());
|
2004-11-21 06:14:46 +01:00
|
|
|
or_history_t *or_history;
|
|
|
|
link_history_t *link_history;
|
|
|
|
void *or_history_p, *link_history_p;
|
2005-10-18 22:12:22 +02:00
|
|
|
digestmap_iter_t *orhist_it, *lhist_it;
|
|
|
|
const char *d1, *d2;
|
2004-11-21 06:14:46 +01:00
|
|
|
|
2005-10-18 22:12:22 +02:00
|
|
|
orhist_it = digestmap_iter_init(history_map);
|
|
|
|
while (!digestmap_iter_done(orhist_it)) {
|
2007-08-17 22:31:54 +02:00
|
|
|
int remove;
|
2005-10-18 22:12:22 +02:00
|
|
|
digestmap_iter_get(orhist_it, &d1, &or_history_p);
|
2004-11-21 06:14:46 +01:00
|
|
|
or_history = or_history_p;
|
2007-08-17 22:31:54 +02:00
|
|
|
|
|
|
|
remove = authority ? (or_history->total_run_weights < STABILITY_EPSILON &&
|
|
|
|
!or_history->start_of_run)
|
|
|
|
: (or_history->changed < before);
|
|
|
|
if (remove) {
|
2005-10-18 22:12:22 +02:00
|
|
|
orhist_it = digestmap_iter_next_rmv(history_map, orhist_it);
|
2005-11-23 05:18:45 +01:00
|
|
|
free_or_history(or_history);
|
2004-11-21 06:14:46 +01:00
|
|
|
continue;
|
|
|
|
}
|
2005-10-18 22:12:22 +02:00
|
|
|
for (lhist_it = digestmap_iter_init(or_history->link_history_map);
|
|
|
|
!digestmap_iter_done(lhist_it); ) {
|
|
|
|
digestmap_iter_get(lhist_it, &d2, &link_history_p);
|
2004-11-21 06:14:46 +01:00
|
|
|
link_history = link_history_p;
|
|
|
|
if (link_history->changed < before) {
|
2005-12-14 21:40:40 +01:00
|
|
|
lhist_it = digestmap_iter_next_rmv(or_history->link_history_map,
|
|
|
|
lhist_it);
|
2005-06-06 19:03:21 +02:00
|
|
|
rephist_total_alloc -= sizeof(link_history_t);
|
2004-11-21 06:14:46 +01:00
|
|
|
tor_free(link_history);
|
|
|
|
continue;
|
|
|
|
}
|
2005-10-18 22:12:22 +02:00
|
|
|
lhist_it = digestmap_iter_next(or_history->link_history_map,lhist_it);
|
2004-11-21 06:14:46 +01:00
|
|
|
}
|
2005-10-18 22:12:22 +02:00
|
|
|
orhist_it = digestmap_iter_next(history_map, orhist_it);
|
2004-11-21 06:14:46 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-06-20 11:25:14 +02:00
|
|
|
/** Write MTBF data to disk. Return 0 on success, negative on failure.
|
|
|
|
*
|
|
|
|
* If <b>missing_means_down</b>, then if we're about to write an entry
|
|
|
|
* that is still considered up but isn't in our routerlist, consider it
|
|
|
|
* to be down. */
|
2007-08-17 22:31:59 +02:00
|
|
|
int
|
2009-06-20 11:25:14 +02:00
|
|
|
rep_hist_record_mtbf_data(time_t now, int missing_means_down)
|
2007-08-17 22:31:59 +02:00
|
|
|
{
|
|
|
|
char time_buf[ISO_TIME_LEN+1];
|
|
|
|
|
|
|
|
digestmap_iter_t *orhist_it;
|
|
|
|
const char *digest;
|
|
|
|
void *or_history_p;
|
|
|
|
or_history_t *hist;
|
2007-08-29 21:02:43 +02:00
|
|
|
open_file_t *open_file = NULL;
|
|
|
|
FILE *f;
|
|
|
|
|
|
|
|
{
|
2007-10-17 18:55:44 +02:00
|
|
|
char *filename = get_datadir_fname("router-stability");
|
2007-08-29 21:02:43 +02:00
|
|
|
f = start_writing_to_stdio_file(filename, OPEN_FLAGS_REPLACE|O_TEXT, 0600,
|
|
|
|
&open_file);
|
|
|
|
tor_free(filename);
|
|
|
|
if (!f)
|
|
|
|
return -1;
|
|
|
|
}
|
2007-08-17 22:31:59 +02:00
|
|
|
|
2007-08-21 07:37:24 +02:00
|
|
|
/* File format is:
|
|
|
|
* FormatLine *KeywordLine Data
|
|
|
|
*
|
|
|
|
* FormatLine = "format 1" NL
|
|
|
|
* KeywordLine = Keyword SP Arguments NL
|
|
|
|
* Data = "data" NL *RouterMTBFLine "." NL
|
|
|
|
* RouterMTBFLine = Fingerprint SP WeightedRunLen SP
|
|
|
|
* TotalRunWeights [SP S=StartRunTime] NL
|
|
|
|
*/
|
2007-08-29 21:02:43 +02:00
|
|
|
#define PUT(s) STMT_BEGIN if (fputs((s),f)<0) goto err; STMT_END
|
|
|
|
#define PRINTF(args) STMT_BEGIN if (fprintf args <0) goto err; STMT_END
|
2007-08-21 07:37:24 +02:00
|
|
|
|
2007-10-10 19:48:58 +02:00
|
|
|
PUT("format 2\n");
|
2007-08-17 22:31:59 +02:00
|
|
|
|
2007-08-20 17:59:31 +02:00
|
|
|
format_iso_time(time_buf, time(NULL));
|
2007-08-29 21:02:43 +02:00
|
|
|
PRINTF((f, "stored-at %s\n", time_buf));
|
2007-08-20 17:59:31 +02:00
|
|
|
|
2007-08-21 07:37:24 +02:00
|
|
|
if (started_tracking_stability) {
|
|
|
|
format_iso_time(time_buf, started_tracking_stability);
|
2007-08-29 21:02:43 +02:00
|
|
|
PRINTF((f, "tracked-since %s\n", time_buf));
|
2007-08-21 07:37:24 +02:00
|
|
|
}
|
2007-08-17 22:31:59 +02:00
|
|
|
if (stability_last_downrated) {
|
|
|
|
format_iso_time(time_buf, stability_last_downrated);
|
2007-08-29 21:02:43 +02:00
|
|
|
PRINTF((f, "last-downrated %s\n", time_buf));
|
2007-08-17 22:31:59 +02:00
|
|
|
}
|
2007-08-20 17:59:31 +02:00
|
|
|
|
2007-08-29 21:02:43 +02:00
|
|
|
PUT("data\n");
|
2007-08-17 22:31:59 +02:00
|
|
|
|
2008-12-18 17:11:24 +01:00
|
|
|
/* XXX Nick: now bridge auths record this for all routers too.
|
2008-02-15 20:20:53 +01:00
|
|
|
* Should we make them record it only for bridge routers? -RD
|
|
|
|
* Not for 0.2.0. -NM */
|
2007-08-17 22:31:59 +02:00
|
|
|
for (orhist_it = digestmap_iter_init(history_map);
|
|
|
|
!digestmap_iter_done(orhist_it);
|
|
|
|
orhist_it = digestmap_iter_next(history_map,orhist_it)) {
|
|
|
|
char dbuf[HEX_DIGEST_LEN+1];
|
|
|
|
const char *t = NULL;
|
|
|
|
digestmap_iter_get(orhist_it, &digest, &or_history_p);
|
|
|
|
hist = (or_history_t*) or_history_p;
|
|
|
|
|
|
|
|
base16_encode(dbuf, sizeof(dbuf), digest, DIGEST_LEN);
|
2009-06-20 11:25:14 +02:00
|
|
|
|
|
|
|
if (missing_means_down && hist->start_of_run &&
|
2010-10-14 17:49:51 +02:00
|
|
|
!router_get_by_id_digest(digest)) {
|
2009-06-20 11:25:14 +02:00
|
|
|
/* We think this relay is running, but it's not listed in our
|
|
|
|
* routerlist. Somehow it fell out without telling us it went
|
|
|
|
* down. Complain and also correct it. */
|
|
|
|
log_info(LD_HIST,
|
|
|
|
"Relay '%s' is listed as up in rephist, but it's not in "
|
|
|
|
"our routerlist. Correcting.", dbuf);
|
|
|
|
rep_hist_note_router_unreachable(digest, now);
|
|
|
|
}
|
|
|
|
|
2007-10-10 19:48:58 +02:00
|
|
|
PRINTF((f, "R %s\n", dbuf));
|
2007-11-08 19:15:49 +01:00
|
|
|
if (hist->start_of_run > 0) {
|
2007-08-17 22:31:59 +02:00
|
|
|
format_iso_time(time_buf, hist->start_of_run);
|
|
|
|
t = time_buf;
|
|
|
|
}
|
2007-10-10 19:48:58 +02:00
|
|
|
PRINTF((f, "+MTBF %lu %.5lf%s%s\n",
|
|
|
|
hist->weighted_run_length, hist->total_run_weights,
|
|
|
|
t ? " S=" : "", t ? t : ""));
|
|
|
|
t = NULL;
|
2007-11-08 19:15:49 +01:00
|
|
|
if (hist->start_of_downtime > 0) {
|
2007-10-10 19:48:58 +02:00
|
|
|
format_iso_time(time_buf, hist->start_of_downtime);
|
|
|
|
t = time_buf;
|
|
|
|
}
|
|
|
|
PRINTF((f, "+WFU %lu %lu%s%s\n",
|
|
|
|
hist->weighted_uptime, hist->total_weighted_time,
|
2007-08-29 21:02:43 +02:00
|
|
|
t ? " S=" : "", t ? t : ""));
|
2007-08-17 22:31:59 +02:00
|
|
|
}
|
|
|
|
|
2007-08-29 21:02:43 +02:00
|
|
|
PUT(".\n");
|
2007-08-17 22:31:59 +02:00
|
|
|
|
2007-08-29 21:02:43 +02:00
|
|
|
#undef PUT
|
|
|
|
#undef PRINTF
|
2007-08-17 22:31:59 +02:00
|
|
|
|
2007-08-29 21:02:43 +02:00
|
|
|
return finish_writing_to_file(open_file);
|
|
|
|
err:
|
|
|
|
abort_writing_to_file(open_file);
|
|
|
|
return -1;
|
2007-08-17 22:31:59 +02:00
|
|
|
}
|
|
|
|
|
2008-12-17 23:58:20 +01:00
|
|
|
/** Format the current tracked status of the router in <b>hist</b> at time
|
|
|
|
* <b>now</b> for analysis; return it in a newly allocated string. */
|
2008-09-26 20:02:48 +02:00
|
|
|
static char *
|
|
|
|
rep_hist_format_router_status(or_history_t *hist, time_t now)
|
|
|
|
{
|
|
|
|
char sor_buf[ISO_TIME_LEN+1];
|
|
|
|
char sod_buf[ISO_TIME_LEN+1];
|
|
|
|
double wfu;
|
|
|
|
double mtbf;
|
|
|
|
int up = 0, down = 0;
|
2010-03-01 03:46:50 +01:00
|
|
|
char *cp = NULL;
|
2008-09-26 20:02:48 +02:00
|
|
|
|
|
|
|
if (hist->start_of_run) {
|
|
|
|
format_iso_time(sor_buf, hist->start_of_run);
|
|
|
|
up = 1;
|
|
|
|
}
|
|
|
|
if (hist->start_of_downtime) {
|
2008-09-26 20:31:22 +02:00
|
|
|
format_iso_time(sod_buf, hist->start_of_downtime);
|
2008-09-26 20:02:48 +02:00
|
|
|
down = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
wfu = get_weighted_fractional_uptime(hist, now);
|
|
|
|
mtbf = get_stability(hist, now);
|
2010-03-01 03:46:50 +01:00
|
|
|
tor_asprintf(&cp,
|
2008-09-26 20:02:48 +02:00
|
|
|
"%s%s%s"
|
|
|
|
"%s%s%s"
|
|
|
|
"wfu %0.3lf\n"
|
|
|
|
" weighted-time %lu\n"
|
|
|
|
" weighted-uptime %lu\n"
|
|
|
|
"mtbf %0.1lf\n"
|
|
|
|
" weighted-run-length %lu\n"
|
|
|
|
" total-run-weights %lf\n",
|
|
|
|
up?"uptime-started ":"", up?sor_buf:"", up?" UTC\n":"",
|
|
|
|
down?"downtime-started ":"", down?sod_buf:"", down?" UTC\n":"",
|
|
|
|
wfu,
|
|
|
|
hist->total_weighted_time,
|
|
|
|
hist->weighted_uptime,
|
|
|
|
mtbf,
|
|
|
|
hist->weighted_run_length,
|
|
|
|
hist->total_run_weights
|
|
|
|
);
|
2010-03-01 03:46:50 +01:00
|
|
|
return cp;
|
2008-09-26 20:02:48 +02:00
|
|
|
}
|
|
|
|
|
2008-12-17 23:58:20 +01:00
|
|
|
/** The last stability analysis document that we created, or NULL if we never
|
|
|
|
* have created one. */
|
2008-09-26 20:02:48 +02:00
|
|
|
static char *last_stability_doc = NULL;
|
2008-12-17 23:58:20 +01:00
|
|
|
/** The last time we created a stability analysis document, or 0 if we never
|
|
|
|
* have created one. */
|
2008-09-26 20:02:48 +02:00
|
|
|
static time_t built_last_stability_doc_at = 0;
|
2008-12-17 23:58:20 +01:00
|
|
|
/** Shortest allowable time between building two stability documents. */
|
2008-09-26 20:02:48 +02:00
|
|
|
#define MAX_STABILITY_DOC_BUILD_RATE (3*60)
|
|
|
|
|
2008-12-17 23:58:20 +01:00
|
|
|
/** Return a pointer to a NUL-terminated document describing our view of the
|
|
|
|
* stability of the routers we've been tracking. Return NULL on failure. */
|
2008-09-26 20:02:48 +02:00
|
|
|
const char *
|
|
|
|
rep_hist_get_router_stability_doc(time_t now)
|
|
|
|
{
|
|
|
|
char *result;
|
|
|
|
smartlist_t *chunks;
|
|
|
|
if (built_last_stability_doc_at + MAX_STABILITY_DOC_BUILD_RATE > now)
|
|
|
|
return last_stability_doc;
|
|
|
|
|
|
|
|
if (!history_map)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
tor_free(last_stability_doc);
|
|
|
|
chunks = smartlist_create();
|
|
|
|
|
2008-09-29 16:40:34 +02:00
|
|
|
if (rep_hist_have_measured_enough_stability()) {
|
|
|
|
smartlist_add(chunks, tor_strdup("we-have-enough-measurements\n"));
|
|
|
|
} else {
|
|
|
|
smartlist_add(chunks, tor_strdup("we-do-not-have-enough-measurements\n"));
|
|
|
|
}
|
|
|
|
|
2008-09-26 20:02:48 +02:00
|
|
|
DIGESTMAP_FOREACH(history_map, id, or_history_t *, hist) {
|
Initial conversion to use node_t throughout our codebase.
A node_t is an abstraction over routerstatus_t, routerinfo_t, and
microdesc_t. It should try to present a consistent interface to all
of them. There should be a node_t for a server whenever there is
* A routerinfo_t for it in the routerlist
* A routerstatus_t in the current_consensus.
(note that a microdesc_t alone isn't enough to make a node_t exist,
since microdescriptors aren't usable on their own.)
There are three ways to get a node_t right now: looking it up by ID,
looking it up by nickname, and iterating over the whole list of
microdescriptors.
All (or nearly all) functions that are supposed to return "a router"
-- especially those used in building connections and circuits --
should return a node_t, not a routerinfo_t or a routerstatus_t.
A node_t should hold all the *mutable* flags about a node. This
patch moves the is_foo flags from routerinfo_t into node_t. The
flags in routerstatus_t remain, but they get set from the consensus
and should not change.
Some other highlights of this patch are:
* Looking up routerinfo and routerstatus by nickname is now
unified and based on the "look up a node by nickname" function.
This tries to look only at the values from current consensus,
and not get confused by the routerinfo_t->is_named flag, which
could get set for other weird reasons. This changes the
behavior of how authorities (when acting as clients) deal with
nodes that have been listed by nickname.
* I tried not to artificially increase the size of the diff here
by moving functions around. As a result, some functions that
now operate on nodes are now in the wrong file -- they should
get moved to nodelist.c once this refactoring settles down.
This moving should happen as part of a patch that moves
functions AND NOTHING ELSE.
* Some old code is now left around inside #if 0/1 blocks, and
should get removed once I've verified that I don't want it
sitting around to see how we used to do things.
There are still some unimplemented functions: these are flagged
with "UNIMPLEMENTED_NODELIST()." I'll work on filling in the
implementation here, piece by piece.
I wish this patch could have been smaller, but there did not seem to
be any piece of it that was independent from the rest. Moving flags
forces many functions that once returned routerinfo_t * to return
node_t *, which forces their friends to change, and so on.
2010-09-29 21:00:41 +02:00
|
|
|
const node_t *node;
|
2008-09-26 20:02:48 +02:00
|
|
|
char dbuf[BASE64_DIGEST_LEN+1];
|
2008-09-29 16:40:34 +02:00
|
|
|
char header_buf[512];
|
2008-09-26 20:02:48 +02:00
|
|
|
char *info;
|
|
|
|
digest_to_base64(dbuf, id);
|
Initial conversion to use node_t throughout our codebase.
A node_t is an abstraction over routerstatus_t, routerinfo_t, and
microdesc_t. It should try to present a consistent interface to all
of them. There should be a node_t for a server whenever there is
* A routerinfo_t for it in the routerlist
* A routerstatus_t in the current_consensus.
(note that a microdesc_t alone isn't enough to make a node_t exist,
since microdescriptors aren't usable on their own.)
There are three ways to get a node_t right now: looking it up by ID,
looking it up by nickname, and iterating over the whole list of
microdescriptors.
All (or nearly all) functions that are supposed to return "a router"
-- especially those used in building connections and circuits --
should return a node_t, not a routerinfo_t or a routerstatus_t.
A node_t should hold all the *mutable* flags about a node. This
patch moves the is_foo flags from routerinfo_t into node_t. The
flags in routerstatus_t remain, but they get set from the consensus
and should not change.
Some other highlights of this patch are:
* Looking up routerinfo and routerstatus by nickname is now
unified and based on the "look up a node by nickname" function.
This tries to look only at the values from current consensus,
and not get confused by the routerinfo_t->is_named flag, which
could get set for other weird reasons. This changes the
behavior of how authorities (when acting as clients) deal with
nodes that have been listed by nickname.
* I tried not to artificially increase the size of the diff here
by moving functions around. As a result, some functions that
now operate on nodes are now in the wrong file -- they should
get moved to nodelist.c once this refactoring settles down.
This moving should happen as part of a patch that moves
functions AND NOTHING ELSE.
* Some old code is now left around inside #if 0/1 blocks, and
should get removed once I've verified that I don't want it
sitting around to see how we used to do things.
There are still some unimplemented functions: these are flagged
with "UNIMPLEMENTED_NODELIST()." I'll work on filling in the
implementation here, piece by piece.
I wish this patch could have been smaller, but there did not seem to
be any piece of it that was independent from the rest. Moving flags
forces many functions that once returned routerinfo_t * to return
node_t *, which forces their friends to change, and so on.
2010-09-29 21:00:41 +02:00
|
|
|
node = node_get_by_id(id);
|
|
|
|
if (node) {
|
|
|
|
char ip[INET_NTOA_BUF_LEN+1];
|
2008-09-29 16:40:34 +02:00
|
|
|
char tbuf[ISO_TIME_LEN+1];
|
2010-10-01 00:37:53 +02:00
|
|
|
time_t published = node_get_published_on(node);
|
Initial conversion to use node_t throughout our codebase.
A node_t is an abstraction over routerstatus_t, routerinfo_t, and
microdesc_t. It should try to present a consistent interface to all
of them. There should be a node_t for a server whenever there is
* A routerinfo_t for it in the routerlist
* A routerstatus_t in the current_consensus.
(note that a microdesc_t alone isn't enough to make a node_t exist,
since microdescriptors aren't usable on their own.)
There are three ways to get a node_t right now: looking it up by ID,
looking it up by nickname, and iterating over the whole list of
microdescriptors.
All (or nearly all) functions that are supposed to return "a router"
-- especially those used in building connections and circuits --
should return a node_t, not a routerinfo_t or a routerstatus_t.
A node_t should hold all the *mutable* flags about a node. This
patch moves the is_foo flags from routerinfo_t into node_t. The
flags in routerstatus_t remain, but they get set from the consensus
and should not change.
Some other highlights of this patch are:
* Looking up routerinfo and routerstatus by nickname is now
unified and based on the "look up a node by nickname" function.
This tries to look only at the values from current consensus,
and not get confused by the routerinfo_t->is_named flag, which
could get set for other weird reasons. This changes the
behavior of how authorities (when acting as clients) deal with
nodes that have been listed by nickname.
* I tried not to artificially increase the size of the diff here
by moving functions around. As a result, some functions that
now operate on nodes are now in the wrong file -- they should
get moved to nodelist.c once this refactoring settles down.
This moving should happen as part of a patch that moves
functions AND NOTHING ELSE.
* Some old code is now left around inside #if 0/1 blocks, and
should get removed once I've verified that I don't want it
sitting around to see how we used to do things.
There are still some unimplemented functions: these are flagged
with "UNIMPLEMENTED_NODELIST()." I'll work on filling in the
implementation here, piece by piece.
I wish this patch could have been smaller, but there did not seem to
be any piece of it that was independent from the rest. Moving flags
forces many functions that once returned routerinfo_t * to return
node_t *, which forces their friends to change, and so on.
2010-09-29 21:00:41 +02:00
|
|
|
node_get_address_string(node,ip,sizeof(ip));
|
2010-10-01 00:37:53 +02:00
|
|
|
if (published > 0)
|
|
|
|
format_iso_time(tbuf, published);
|
|
|
|
else
|
|
|
|
strlcpy(tbuf, "???", sizeof(tbuf));
|
2008-09-29 16:40:34 +02:00
|
|
|
tor_snprintf(header_buf, sizeof(header_buf),
|
|
|
|
"router %s %s %s\n"
|
|
|
|
"published %s\n"
|
2008-09-30 11:32:26 +02:00
|
|
|
"relevant-flags %s%s%s\n"
|
2008-09-29 16:40:34 +02:00
|
|
|
"declared-uptime %ld\n",
|
Initial conversion to use node_t throughout our codebase.
A node_t is an abstraction over routerstatus_t, routerinfo_t, and
microdesc_t. It should try to present a consistent interface to all
of them. There should be a node_t for a server whenever there is
* A routerinfo_t for it in the routerlist
* A routerstatus_t in the current_consensus.
(note that a microdesc_t alone isn't enough to make a node_t exist,
since microdescriptors aren't usable on their own.)
There are three ways to get a node_t right now: looking it up by ID,
looking it up by nickname, and iterating over the whole list of
microdescriptors.
All (or nearly all) functions that are supposed to return "a router"
-- especially those used in building connections and circuits --
should return a node_t, not a routerinfo_t or a routerstatus_t.
A node_t should hold all the *mutable* flags about a node. This
patch moves the is_foo flags from routerinfo_t into node_t. The
flags in routerstatus_t remain, but they get set from the consensus
and should not change.
Some other highlights of this patch are:
* Looking up routerinfo and routerstatus by nickname is now
unified and based on the "look up a node by nickname" function.
This tries to look only at the values from current consensus,
and not get confused by the routerinfo_t->is_named flag, which
could get set for other weird reasons. This changes the
behavior of how authorities (when acting as clients) deal with
nodes that have been listed by nickname.
* I tried not to artificially increase the size of the diff here
by moving functions around. As a result, some functions that
now operate on nodes are now in the wrong file -- they should
get moved to nodelist.c once this refactoring settles down.
This moving should happen as part of a patch that moves
functions AND NOTHING ELSE.
* Some old code is now left around inside #if 0/1 blocks, and
should get removed once I've verified that I don't want it
sitting around to see how we used to do things.
There are still some unimplemented functions: these are flagged
with "UNIMPLEMENTED_NODELIST()." I'll work on filling in the
implementation here, piece by piece.
I wish this patch could have been smaller, but there did not seem to
be any piece of it that was independent from the rest. Moving flags
forces many functions that once returned routerinfo_t * to return
node_t *, which forces their friends to change, and so on.
2010-09-29 21:00:41 +02:00
|
|
|
dbuf, node_get_nickname(node), ip,
|
2008-09-29 16:40:34 +02:00
|
|
|
tbuf,
|
Initial conversion to use node_t throughout our codebase.
A node_t is an abstraction over routerstatus_t, routerinfo_t, and
microdesc_t. It should try to present a consistent interface to all
of them. There should be a node_t for a server whenever there is
* A routerinfo_t for it in the routerlist
* A routerstatus_t in the current_consensus.
(note that a microdesc_t alone isn't enough to make a node_t exist,
since microdescriptors aren't usable on their own.)
There are three ways to get a node_t right now: looking it up by ID,
looking it up by nickname, and iterating over the whole list of
microdescriptors.
All (or nearly all) functions that are supposed to return "a router"
-- especially those used in building connections and circuits --
should return a node_t, not a routerinfo_t or a routerstatus_t.
A node_t should hold all the *mutable* flags about a node. This
patch moves the is_foo flags from routerinfo_t into node_t. The
flags in routerstatus_t remain, but they get set from the consensus
and should not change.
Some other highlights of this patch are:
* Looking up routerinfo and routerstatus by nickname is now
unified and based on the "look up a node by nickname" function.
This tries to look only at the values from current consensus,
and not get confused by the routerinfo_t->is_named flag, which
could get set for other weird reasons. This changes the
behavior of how authorities (when acting as clients) deal with
nodes that have been listed by nickname.
* I tried not to artificially increase the size of the diff here
by moving functions around. As a result, some functions that
now operate on nodes are now in the wrong file -- they should
get moved to nodelist.c once this refactoring settles down.
This moving should happen as part of a patch that moves
functions AND NOTHING ELSE.
* Some old code is now left around inside #if 0/1 blocks, and
should get removed once I've verified that I don't want it
sitting around to see how we used to do things.
There are still some unimplemented functions: these are flagged
with "UNIMPLEMENTED_NODELIST()." I'll work on filling in the
implementation here, piece by piece.
I wish this patch could have been smaller, but there did not seem to
be any piece of it that was independent from the rest. Moving flags
forces many functions that once returned routerinfo_t * to return
node_t *, which forces their friends to change, and so on.
2010-09-29 21:00:41 +02:00
|
|
|
node->is_running ? "Running " : "",
|
|
|
|
node->is_valid ? "Valid " : "",
|
|
|
|
node->ri && node->ri->is_hibernating ? "Hibernating " : "",
|
|
|
|
node_get_declared_uptime(node));
|
2008-09-26 20:02:48 +02:00
|
|
|
} else {
|
|
|
|
tor_snprintf(header_buf, sizeof(header_buf),
|
|
|
|
"router %s {no descriptor}\n", dbuf);
|
|
|
|
}
|
|
|
|
smartlist_add(chunks, tor_strdup(header_buf));
|
|
|
|
info = rep_hist_format_router_status(hist, now);
|
|
|
|
if (info)
|
|
|
|
smartlist_add(chunks, info);
|
2008-09-29 16:40:34 +02:00
|
|
|
|
2008-09-26 20:02:48 +02:00
|
|
|
} DIGESTMAP_FOREACH_END;
|
|
|
|
|
|
|
|
result = smartlist_join_strings(chunks, "", 0, NULL);
|
|
|
|
SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp));
|
|
|
|
smartlist_free(chunks);
|
|
|
|
|
|
|
|
last_stability_doc = result;
|
|
|
|
built_last_stability_doc_at = time(NULL);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2007-10-10 19:48:58 +02:00
|
|
|
/** Helper: return the first j >= i such that !strcmpstart(sl[j], prefix) and
|
|
|
|
* such that no line sl[k] with i <= k < j starts with "R ". Return -1 if no
|
|
|
|
* such line exists. */
|
|
|
|
static int
|
|
|
|
find_next_with(smartlist_t *sl, int i, const char *prefix)
|
|
|
|
{
|
|
|
|
for ( ; i < smartlist_len(sl); ++i) {
|
|
|
|
const char *line = smartlist_get(sl, i);
|
|
|
|
if (!strcmpstart(line, prefix))
|
|
|
|
return i;
|
|
|
|
if (!strcmpstart(line, "R "))
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2008-02-06 20:34:32 +01:00
|
|
|
/** How many bad times has parse_possibly_bad_iso_time parsed? */
|
2007-11-08 19:15:49 +01:00
|
|
|
static int n_bogus_times = 0;
|
2008-02-06 20:34:32 +01:00
|
|
|
/** Parse the ISO-formatted time in <b>s</b> into *<b>time_out</b>, but
|
|
|
|
* rounds any pre-1970 date to Jan 1, 1970. */
|
2007-11-08 19:15:49 +01:00
|
|
|
static int
|
|
|
|
parse_possibly_bad_iso_time(const char *s, time_t *time_out)
|
|
|
|
{
|
|
|
|
int year;
|
|
|
|
char b[5];
|
|
|
|
strlcpy(b, s, sizeof(b));
|
|
|
|
b[4] = '\0';
|
2008-02-06 01:54:47 +01:00
|
|
|
year = (int)tor_parse_long(b, 10, 0, INT_MAX, NULL, NULL);
|
2007-11-08 19:15:49 +01:00
|
|
|
if (year < 1970) {
|
|
|
|
*time_out = 0;
|
|
|
|
++n_bogus_times;
|
|
|
|
return 0;
|
|
|
|
} else
|
|
|
|
return parse_iso_time(s, time_out);
|
|
|
|
}
|
|
|
|
|
2008-02-16 00:39:04 +01:00
|
|
|
/** We've read a time <b>t</b> from a file stored at <b>stored_at</b>, which
|
|
|
|
* says we started measuring at <b>started_measuring</b>. Return a new number
|
|
|
|
* that's about as much before <b>now</b> as <b>t</b> was before
|
|
|
|
* <b>stored_at</b>.
|
|
|
|
*/
|
2007-11-08 19:15:49 +01:00
|
|
|
static INLINE time_t
|
|
|
|
correct_time(time_t t, time_t now, time_t stored_at, time_t started_measuring)
|
|
|
|
{
|
|
|
|
if (t < started_measuring - 24*60*60*365)
|
|
|
|
return 0;
|
|
|
|
else if (t < started_measuring)
|
|
|
|
return started_measuring;
|
|
|
|
else if (t > stored_at)
|
|
|
|
return 0;
|
|
|
|
else {
|
|
|
|
long run_length = stored_at - t;
|
|
|
|
t = now - run_length;
|
|
|
|
if (t < started_measuring)
|
|
|
|
t = started_measuring;
|
|
|
|
return t;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-08-21 07:37:24 +02:00
|
|
|
/** Load MTBF data from disk. Returns 0 on success or recoverable error, -1
|
|
|
|
* on failure. */
|
2007-08-17 22:31:59 +02:00
|
|
|
int
|
2007-08-20 18:34:17 +02:00
|
|
|
rep_hist_load_mtbf_data(time_t now)
|
2007-08-17 22:31:59 +02:00
|
|
|
{
|
|
|
|
/* XXXX won't handle being called while history is already populated. */
|
|
|
|
smartlist_t *lines;
|
|
|
|
const char *line = NULL;
|
|
|
|
int r=0, i;
|
2007-08-21 07:37:24 +02:00
|
|
|
time_t last_downrated = 0, stored_at = 0, tracked_since = 0;
|
|
|
|
time_t latest_possible_start = now;
|
2007-10-10 19:48:58 +02:00
|
|
|
long format = -1;
|
2007-08-17 22:31:59 +02:00
|
|
|
|
|
|
|
{
|
2007-10-17 18:55:44 +02:00
|
|
|
char *filename = get_datadir_fname("router-stability");
|
2007-08-17 22:31:59 +02:00
|
|
|
char *d = read_file_to_str(filename, RFTS_IGNORE_MISSING, NULL);
|
2007-08-20 18:34:17 +02:00
|
|
|
tor_free(filename);
|
2007-08-17 22:31:59 +02:00
|
|
|
if (!d)
|
|
|
|
return -1;
|
|
|
|
lines = smartlist_create();
|
|
|
|
smartlist_split_string(lines, d, "\n", SPLIT_SKIP_SPACE, 0);
|
|
|
|
tor_free(d);
|
|
|
|
}
|
|
|
|
|
2007-10-10 19:48:58 +02:00
|
|
|
{
|
|
|
|
const char *firstline;
|
|
|
|
if (smartlist_len(lines)>4) {
|
|
|
|
firstline = smartlist_get(lines, 0);
|
|
|
|
if (!strcmpstart(firstline, "format "))
|
|
|
|
format = tor_parse_long(firstline+strlen("format "),
|
|
|
|
10, -1, LONG_MAX, NULL, NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (format != 1 && format != 2) {
|
2008-09-23 20:24:20 +02:00
|
|
|
log_warn(LD_HIST,
|
2007-10-10 19:48:58 +02:00
|
|
|
"Unrecognized format in mtbf history file. Skipping.");
|
2007-08-17 22:31:59 +02:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
for (i = 1; i < smartlist_len(lines); ++i) {
|
|
|
|
line = smartlist_get(lines, i);
|
|
|
|
if (!strcmp(line, "data"))
|
|
|
|
break;
|
|
|
|
if (!strcmpstart(line, "last-downrated ")) {
|
|
|
|
if (parse_iso_time(line+strlen("last-downrated "), &last_downrated)<0)
|
2008-09-23 20:24:20 +02:00
|
|
|
log_warn(LD_HIST,"Couldn't parse downrate time in mtbf "
|
2007-08-17 22:31:59 +02:00
|
|
|
"history file.");
|
|
|
|
}
|
2007-08-20 18:34:17 +02:00
|
|
|
if (!strcmpstart(line, "stored-at ")) {
|
|
|
|
if (parse_iso_time(line+strlen("stored-at "), &stored_at)<0)
|
2008-09-23 20:24:20 +02:00
|
|
|
log_warn(LD_HIST,"Couldn't parse stored time in mtbf "
|
2007-08-20 18:34:17 +02:00
|
|
|
"history file.");
|
|
|
|
}
|
2007-08-21 07:37:24 +02:00
|
|
|
if (!strcmpstart(line, "tracked-since ")) {
|
|
|
|
if (parse_iso_time(line+strlen("tracked-since "), &tracked_since)<0)
|
2008-09-23 20:24:20 +02:00
|
|
|
log_warn(LD_HIST,"Couldn't parse started-tracking time in mtbf "
|
2007-08-21 07:37:24 +02:00
|
|
|
"history file.");
|
|
|
|
}
|
2007-08-17 22:31:59 +02:00
|
|
|
}
|
2007-08-20 18:34:17 +02:00
|
|
|
if (last_downrated > now)
|
|
|
|
last_downrated = now;
|
2007-08-21 07:37:24 +02:00
|
|
|
if (tracked_since > now)
|
|
|
|
tracked_since = now;
|
2007-08-20 18:34:17 +02:00
|
|
|
|
|
|
|
if (!stored_at) {
|
2008-09-23 20:24:20 +02:00
|
|
|
log_warn(LD_HIST, "No stored time recorded.");
|
2007-08-20 18:34:17 +02:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2007-08-17 22:31:59 +02:00
|
|
|
if (line && !strcmp(line, "data"))
|
|
|
|
++i;
|
|
|
|
|
2007-11-08 19:15:49 +01:00
|
|
|
n_bogus_times = 0;
|
|
|
|
|
2007-08-17 22:31:59 +02:00
|
|
|
for (; i < smartlist_len(lines); ++i) {
|
|
|
|
char digest[DIGEST_LEN];
|
|
|
|
char hexbuf[HEX_DIGEST_LEN+1];
|
2007-10-10 19:48:58 +02:00
|
|
|
char mtbf_timebuf[ISO_TIME_LEN+1];
|
|
|
|
char wfu_timebuf[ISO_TIME_LEN+1];
|
2007-08-17 22:31:59 +02:00
|
|
|
time_t start_of_run = 0;
|
2007-10-10 19:48:58 +02:00
|
|
|
time_t start_of_downtime = 0;
|
|
|
|
int have_mtbf = 0, have_wfu = 0;
|
|
|
|
long wrl = 0;
|
|
|
|
double trw = 0;
|
|
|
|
long wt_uptime = 0, total_wt_time = 0;
|
2007-08-17 22:31:59 +02:00
|
|
|
int n;
|
|
|
|
or_history_t *hist;
|
|
|
|
line = smartlist_get(lines, i);
|
|
|
|
if (!strcmp(line, "."))
|
|
|
|
break;
|
2007-10-10 19:48:58 +02:00
|
|
|
|
|
|
|
mtbf_timebuf[0] = '\0';
|
|
|
|
wfu_timebuf[0] = '\0';
|
|
|
|
|
|
|
|
if (format == 1) {
|
|
|
|
n = sscanf(line, "%40s %ld %lf S=%10s %8s",
|
|
|
|
hexbuf, &wrl, &trw, mtbf_timebuf, mtbf_timebuf+11);
|
|
|
|
if (n != 3 && n != 5) {
|
2008-09-23 20:24:20 +02:00
|
|
|
log_warn(LD_HIST, "Couldn't scan line %s", escaped(line));
|
2007-10-10 19:48:58 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
have_mtbf = 1;
|
|
|
|
} else {
|
|
|
|
// format == 2.
|
|
|
|
int mtbf_idx, wfu_idx;
|
|
|
|
if (strcmpstart(line, "R ") || strlen(line) < 2+HEX_DIGEST_LEN)
|
|
|
|
continue;
|
|
|
|
strlcpy(hexbuf, line+2, sizeof(hexbuf));
|
|
|
|
mtbf_idx = find_next_with(lines, i+1, "+MTBF ");
|
|
|
|
wfu_idx = find_next_with(lines, i+1, "+WFU ");
|
|
|
|
if (mtbf_idx >= 0) {
|
|
|
|
const char *mtbfline = smartlist_get(lines, mtbf_idx);
|
|
|
|
n = sscanf(mtbfline, "+MTBF %lu %lf S=%10s %8s",
|
|
|
|
&wrl, &trw, mtbf_timebuf, mtbf_timebuf+11);
|
|
|
|
if (n == 2 || n == 4) {
|
|
|
|
have_mtbf = 1;
|
|
|
|
} else {
|
2008-09-23 20:24:20 +02:00
|
|
|
log_warn(LD_HIST, "Couldn't scan +MTBF line %s",
|
2007-10-10 19:48:58 +02:00
|
|
|
escaped(mtbfline));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (wfu_idx >= 0) {
|
|
|
|
const char *wfuline = smartlist_get(lines, wfu_idx);
|
|
|
|
n = sscanf(wfuline, "+WFU %lu %lu S=%10s %8s",
|
|
|
|
&wt_uptime, &total_wt_time,
|
|
|
|
wfu_timebuf, wfu_timebuf+11);
|
|
|
|
if (n == 2 || n == 4) {
|
|
|
|
have_wfu = 1;
|
|
|
|
} else {
|
2008-09-23 20:24:20 +02:00
|
|
|
log_warn(LD_HIST, "Couldn't scan +WFU line %s", escaped(wfuline));
|
2007-10-10 19:48:58 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (wfu_idx > i)
|
|
|
|
i = wfu_idx;
|
|
|
|
if (mtbf_idx > i)
|
|
|
|
i = mtbf_idx;
|
2007-08-17 22:31:59 +02:00
|
|
|
}
|
|
|
|
if (base16_decode(digest, DIGEST_LEN, hexbuf, HEX_DIGEST_LEN) < 0) {
|
2008-09-23 20:24:20 +02:00
|
|
|
log_warn(LD_HIST, "Couldn't hex string %s", escaped(hexbuf));
|
2007-08-17 22:31:59 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
hist = get_or_history(digest);
|
|
|
|
if (!hist)
|
|
|
|
continue;
|
2007-08-20 18:34:17 +02:00
|
|
|
|
2007-10-10 19:48:58 +02:00
|
|
|
if (have_mtbf) {
|
|
|
|
if (mtbf_timebuf[0]) {
|
|
|
|
mtbf_timebuf[10] = ' ';
|
2007-11-08 19:15:49 +01:00
|
|
|
if (parse_possibly_bad_iso_time(mtbf_timebuf, &start_of_run)<0)
|
2008-09-23 20:24:20 +02:00
|
|
|
log_warn(LD_HIST, "Couldn't parse time %s",
|
2007-10-10 19:48:58 +02:00
|
|
|
escaped(mtbf_timebuf));
|
|
|
|
}
|
2007-11-08 19:15:49 +01:00
|
|
|
hist->start_of_run = correct_time(start_of_run, now, stored_at,
|
|
|
|
tracked_since);
|
2007-10-10 19:48:58 +02:00
|
|
|
if (hist->start_of_run < latest_possible_start + wrl)
|
|
|
|
latest_possible_start = hist->start_of_run - wrl;
|
|
|
|
|
|
|
|
hist->weighted_run_length = wrl;
|
|
|
|
hist->total_run_weights = trw;
|
|
|
|
}
|
|
|
|
if (have_wfu) {
|
|
|
|
if (wfu_timebuf[0]) {
|
|
|
|
wfu_timebuf[10] = ' ';
|
2007-11-08 19:15:49 +01:00
|
|
|
if (parse_possibly_bad_iso_time(wfu_timebuf, &start_of_downtime)<0)
|
2008-09-23 20:24:20 +02:00
|
|
|
log_warn(LD_HIST, "Couldn't parse time %s", escaped(wfu_timebuf));
|
2007-10-10 19:48:58 +02:00
|
|
|
}
|
|
|
|
}
|
2007-11-08 19:15:49 +01:00
|
|
|
hist->start_of_downtime = correct_time(start_of_downtime, now, stored_at,
|
|
|
|
tracked_since);
|
2007-10-10 19:48:58 +02:00
|
|
|
hist->weighted_uptime = wt_uptime;
|
|
|
|
hist->total_weighted_time = total_wt_time;
|
2007-08-17 22:31:59 +02:00
|
|
|
}
|
|
|
|
if (strcmp(line, "."))
|
2008-09-23 20:24:20 +02:00
|
|
|
log_warn(LD_HIST, "Truncated MTBF file.");
|
2007-08-17 22:31:59 +02:00
|
|
|
|
2008-02-21 03:18:41 +01:00
|
|
|
if (tracked_since < 86400*365) /* Recover from insanely early value. */
|
2007-08-21 07:37:24 +02:00
|
|
|
tracked_since = latest_possible_start;
|
|
|
|
|
2007-08-17 22:31:59 +02:00
|
|
|
stability_last_downrated = last_downrated;
|
2007-08-21 07:37:24 +02:00
|
|
|
started_tracking_stability = tracked_since;
|
2007-08-17 22:31:59 +02:00
|
|
|
|
|
|
|
goto done;
|
|
|
|
err:
|
|
|
|
r = -1;
|
|
|
|
done:
|
|
|
|
SMARTLIST_FOREACH(lines, char *, cp, tor_free(cp));
|
|
|
|
smartlist_free(lines);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2006-03-12 23:48:18 +01:00
|
|
|
/** For how many seconds do we keep track of individual per-second bandwidth
|
|
|
|
* totals? */
|
2004-07-13 09:42:20 +02:00
|
|
|
#define NUM_SECS_ROLLING_MEASURE 10
|
2007-04-30 19:46:13 +02:00
|
|
|
/** How large are the intervals for which we track and report bandwidth use? */
|
2004-07-20 22:57:46 +02:00
|
|
|
#define NUM_SECS_BW_SUM_INTERVAL (15*60)
|
2006-03-12 23:48:18 +01:00
|
|
|
/** How far in the past do we remember and publish bandwidth use? */
|
|
|
|
#define NUM_SECS_BW_SUM_IS_VALID (24*60*60)
|
2006-07-23 07:18:29 +02:00
|
|
|
/** How many bandwidth usage intervals do we remember? (derived) */
|
2004-07-20 22:57:46 +02:00
|
|
|
#define NUM_TOTALS (NUM_SECS_BW_SUM_IS_VALID/NUM_SECS_BW_SUM_INTERVAL)
|
|
|
|
|
2006-07-23 07:18:29 +02:00
|
|
|
/** Structure to track bandwidth use, and remember the maxima for a given
|
2004-07-20 22:57:46 +02:00
|
|
|
* time period.
|
|
|
|
*/
|
|
|
|
typedef struct bw_array_t {
|
|
|
|
/** Observation array: Total number of bytes transferred in each of the last
|
|
|
|
* NUM_SECS_ROLLING_MEASURE seconds. This is used as a circular array. */
|
2006-01-11 04:57:01 +01:00
|
|
|
uint64_t obs[NUM_SECS_ROLLING_MEASURE];
|
2004-07-20 22:57:46 +02:00
|
|
|
int cur_obs_idx; /**< Current position in obs. */
|
|
|
|
time_t cur_obs_time; /**< Time represented in obs[cur_obs_idx] */
|
2006-01-11 05:04:42 +01:00
|
|
|
uint64_t total_obs; /**< Total for all members of obs except
|
|
|
|
* obs[cur_obs_idx] */
|
|
|
|
uint64_t max_total; /**< Largest value that total_obs has taken on in the
|
|
|
|
* current period. */
|
2005-12-14 21:40:40 +01:00
|
|
|
uint64_t total_in_period; /**< Total bytes transferred in the current
|
|
|
|
* period. */
|
2004-07-20 22:57:46 +02:00
|
|
|
|
|
|
|
/** When does the next period begin? */
|
|
|
|
time_t next_period;
|
|
|
|
/** Where in 'maxima' should the maximum bandwidth usage for the current
|
|
|
|
* period be stored? */
|
|
|
|
int next_max_idx;
|
2004-08-07 04:46:16 +02:00
|
|
|
/** How many values in maxima/totals have been set ever? */
|
|
|
|
int num_maxes_set;
|
|
|
|
/** Circular array of the maximum
|
|
|
|
* bandwidth-per-NUM_SECS_ROLLING_MEASURE usage for the last
|
|
|
|
* NUM_TOTALS periods */
|
2006-01-11 04:57:01 +01:00
|
|
|
uint64_t maxima[NUM_TOTALS];
|
2004-08-07 04:46:16 +02:00
|
|
|
/** Circular array of the total bandwidth usage for the last NUM_TOTALS
|
|
|
|
* periods */
|
2005-09-23 10:29:58 +02:00
|
|
|
uint64_t totals[NUM_TOTALS];
|
2004-07-20 22:57:46 +02:00
|
|
|
} bw_array_t;
|
|
|
|
|
2006-07-23 07:18:29 +02:00
|
|
|
/** Shift the current period of b forward by one. */
|
2005-06-11 20:52:12 +02:00
|
|
|
static void
|
|
|
|
commit_max(bw_array_t *b)
|
|
|
|
{
|
2004-08-07 04:46:16 +02:00
|
|
|
/* Store total from current period. */
|
|
|
|
b->totals[b->next_max_idx] = b->total_in_period;
|
2004-07-20 22:57:46 +02:00
|
|
|
/* Store maximum from current period. */
|
|
|
|
b->maxima[b->next_max_idx++] = b->max_total;
|
|
|
|
/* Advance next_period and next_max_idx */
|
|
|
|
b->next_period += NUM_SECS_BW_SUM_INTERVAL;
|
|
|
|
if (b->next_max_idx == NUM_TOTALS)
|
|
|
|
b->next_max_idx = 0;
|
2004-08-07 04:46:16 +02:00
|
|
|
if (b->num_maxes_set < NUM_TOTALS)
|
|
|
|
++b->num_maxes_set;
|
2004-07-20 22:57:46 +02:00
|
|
|
/* Reset max_total. */
|
|
|
|
b->max_total = 0;
|
2004-08-07 07:13:55 +02:00
|
|
|
/* Reset total_in_period. */
|
|
|
|
b->total_in_period = 0;
|
2004-07-20 22:57:46 +02:00
|
|
|
}
|
|
|
|
|
2006-07-23 07:18:29 +02:00
|
|
|
/** Shift the current observation time of 'b' forward by one second. */
|
2005-06-11 20:52:12 +02:00
|
|
|
static INLINE void
|
|
|
|
advance_obs(bw_array_t *b)
|
|
|
|
{
|
2004-07-20 22:57:46 +02:00
|
|
|
int nextidx;
|
2006-01-11 04:57:01 +01:00
|
|
|
uint64_t total;
|
2004-07-20 22:57:46 +02:00
|
|
|
|
|
|
|
/* Calculate the total bandwidth for the last NUM_SECS_ROLLING_MEASURE
|
|
|
|
* seconds; adjust max_total as needed.*/
|
|
|
|
total = b->total_obs + b->obs[b->cur_obs_idx];
|
|
|
|
if (total > b->max_total)
|
|
|
|
b->max_total = total;
|
|
|
|
|
|
|
|
nextidx = b->cur_obs_idx+1;
|
|
|
|
if (nextidx == NUM_SECS_ROLLING_MEASURE)
|
|
|
|
nextidx = 0;
|
|
|
|
|
|
|
|
b->total_obs = total - b->obs[nextidx];
|
|
|
|
b->obs[nextidx]=0;
|
|
|
|
b->cur_obs_idx = nextidx;
|
|
|
|
|
|
|
|
if (++b->cur_obs_time >= b->next_period)
|
|
|
|
commit_max(b);
|
|
|
|
}
|
|
|
|
|
2008-02-20 03:17:41 +01:00
|
|
|
/** Add <b>n</b> bytes to the number of bytes in <b>b</b> for second
|
|
|
|
* <b>when</b>. */
|
2005-06-11 20:52:12 +02:00
|
|
|
static INLINE void
|
2006-01-02 01:42:19 +01:00
|
|
|
add_obs(bw_array_t *b, time_t when, uint64_t n)
|
2005-06-11 20:52:12 +02:00
|
|
|
{
|
2004-07-21 01:25:00 +02:00
|
|
|
/* Don't record data in the past. */
|
|
|
|
if (when<b->cur_obs_time)
|
|
|
|
return;
|
|
|
|
/* If we're currently adding observations for an earlier second than
|
|
|
|
* 'when', advance b->cur_obs_time and b->cur_obs_idx by an
|
|
|
|
* appropriate number of seconds, and do all the other housekeeping */
|
|
|
|
while (when>b->cur_obs_time)
|
2004-07-20 22:57:46 +02:00
|
|
|
advance_obs(b);
|
2004-07-21 01:25:00 +02:00
|
|
|
|
2004-07-20 22:57:46 +02:00
|
|
|
b->obs[b->cur_obs_idx] += n;
|
2004-08-07 04:46:16 +02:00
|
|
|
b->total_in_period += n;
|
2004-07-20 22:57:46 +02:00
|
|
|
}
|
|
|
|
|
2006-07-23 07:18:29 +02:00
|
|
|
/** Allocate, initialize, and return a new bw_array. */
|
2005-09-30 03:39:24 +02:00
|
|
|
static bw_array_t *
|
|
|
|
bw_array_new(void)
|
2005-06-11 20:52:12 +02:00
|
|
|
{
|
2004-07-20 22:57:46 +02:00
|
|
|
bw_array_t *b;
|
|
|
|
time_t start;
|
|
|
|
b = tor_malloc_zero(sizeof(bw_array_t));
|
2005-06-06 19:03:21 +02:00
|
|
|
rephist_total_alloc += sizeof(bw_array_t);
|
2004-07-20 22:57:46 +02:00
|
|
|
start = time(NULL);
|
|
|
|
b->cur_obs_time = start;
|
|
|
|
b->next_period = start + NUM_SECS_BW_SUM_INTERVAL;
|
|
|
|
return b;
|
|
|
|
}
|
|
|
|
|
2008-12-26 18:35:08 +01:00
|
|
|
/** Recent history of bandwidth observations for read operations. */
|
2004-07-20 22:57:46 +02:00
|
|
|
static bw_array_t *read_array = NULL;
|
2008-12-26 18:35:08 +01:00
|
|
|
/** Recent history of bandwidth observations for write operations. */
|
2004-07-20 22:57:46 +02:00
|
|
|
static bw_array_t *write_array = NULL;
|
2010-07-07 14:55:42 +02:00
|
|
|
/** Recent history of bandwidth observations for read operations for the
|
|
|
|
directory protocol. */
|
|
|
|
static bw_array_t *dir_read_array = NULL;
|
|
|
|
/** Recent history of bandwidth observations for write operations for the
|
|
|
|
directory protocol. */
|
|
|
|
static bw_array_t *dir_write_array = NULL;
|
|
|
|
|
|
|
|
/** Set up [dir-]read_array and [dir-]write_array. */
|
2005-06-11 20:52:12 +02:00
|
|
|
static void
|
|
|
|
bw_arrays_init(void)
|
2004-07-20 22:57:46 +02:00
|
|
|
{
|
|
|
|
read_array = bw_array_new();
|
|
|
|
write_array = bw_array_new();
|
2010-07-07 14:55:42 +02:00
|
|
|
dir_read_array = bw_array_new();
|
|
|
|
dir_write_array = bw_array_new();
|
2004-07-20 22:57:46 +02:00
|
|
|
}
|
2004-07-13 09:42:20 +02:00
|
|
|
|
|
|
|
/** We read <b>num_bytes</b> more bytes in second <b>when</b>.
|
|
|
|
*
|
|
|
|
* Add num_bytes to the current running total for <b>when</b>.
|
|
|
|
*
|
|
|
|
* <b>when</b> can go back to time, but it's safe to ignore calls
|
2004-07-13 18:58:01 +02:00
|
|
|
* earlier than the latest <b>when</b> you've heard of.
|
2004-07-13 09:42:20 +02:00
|
|
|
*/
|
2005-06-11 20:52:12 +02:00
|
|
|
void
|
2008-02-20 03:17:41 +01:00
|
|
|
rep_hist_note_bytes_written(size_t num_bytes, time_t when)
|
2005-06-11 20:52:12 +02:00
|
|
|
{
|
2004-07-13 09:42:20 +02:00
|
|
|
/* Maybe a circular array for recent seconds, and step to a new point
|
|
|
|
* every time a new second shows up. Or simpler is to just to have
|
|
|
|
* a normal array and push down each item every second; it's short.
|
|
|
|
*/
|
|
|
|
/* When a new second has rolled over, compute the sum of the bytes we've
|
|
|
|
* seen over when-1 to when-1-NUM_SECS_ROLLING_MEASURE, and stick it
|
|
|
|
* somewhere. See rep_hist_bandwidth_assess() below.
|
|
|
|
*/
|
2004-07-20 22:57:46 +02:00
|
|
|
add_obs(write_array, when, num_bytes);
|
2004-07-13 09:42:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/** We wrote <b>num_bytes</b> more bytes in second <b>when</b>.
|
|
|
|
* (like rep_hist_note_bytes_written() above)
|
|
|
|
*/
|
2005-06-11 20:52:12 +02:00
|
|
|
void
|
2008-02-20 03:17:41 +01:00
|
|
|
rep_hist_note_bytes_read(size_t num_bytes, time_t when)
|
2005-06-11 20:52:12 +02:00
|
|
|
{
|
2004-07-13 09:42:20 +02:00
|
|
|
/* if we're smart, we can make this func and the one above share code */
|
2004-07-20 22:57:46 +02:00
|
|
|
add_obs(read_array, when, num_bytes);
|
|
|
|
}
|
|
|
|
|
2010-07-07 14:55:42 +02:00
|
|
|
/** We wrote <b>num_bytes</b> more directory bytes in second <b>when</b>.
|
|
|
|
* (like rep_hist_note_bytes_written() above)
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
rep_hist_note_dir_bytes_written(size_t num_bytes, time_t when)
|
|
|
|
{
|
|
|
|
add_obs(dir_write_array, when, num_bytes);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** We read <b>num_bytes</b> more directory bytes in second <b>when</b>.
|
|
|
|
* (like rep_hist_note_bytes_written() above)
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
rep_hist_note_dir_bytes_read(size_t num_bytes, time_t when)
|
|
|
|
{
|
|
|
|
add_obs(dir_read_array, when, num_bytes);
|
|
|
|
}
|
|
|
|
|
2004-07-20 22:57:46 +02:00
|
|
|
/** Helper: Return the largest value in b->maxima. (This is equal to the
|
|
|
|
* most bandwidth used in any NUM_SECS_ROLLING_MEASURE period for the last
|
|
|
|
* NUM_SECS_BW_SUM_IS_VALID seconds.)
|
|
|
|
*/
|
2006-01-11 04:57:01 +01:00
|
|
|
static uint64_t
|
2005-06-11 20:52:12 +02:00
|
|
|
find_largest_max(bw_array_t *b)
|
2004-07-20 22:57:46 +02:00
|
|
|
{
|
2006-01-11 04:57:01 +01:00
|
|
|
int i;
|
|
|
|
uint64_t max;
|
2004-07-20 22:57:46 +02:00
|
|
|
max=0;
|
|
|
|
for (i=0; i<NUM_TOTALS; ++i) {
|
|
|
|
if (b->maxima[i]>max)
|
|
|
|
max = b->maxima[i];
|
|
|
|
}
|
|
|
|
return max;
|
2004-07-13 09:42:20 +02:00
|
|
|
}
|
|
|
|
|
2006-07-23 07:18:29 +02:00
|
|
|
/** Find the largest sums in the past NUM_SECS_BW_SUM_IS_VALID (roughly)
|
2004-07-13 09:42:20 +02:00
|
|
|
* seconds. Find one sum for reading and one for writing. They don't have
|
2008-02-20 03:17:41 +01:00
|
|
|
* to be at the same time.
|
2004-07-13 09:42:20 +02:00
|
|
|
*
|
|
|
|
* Return the smaller of these sums, divided by NUM_SECS_ROLLING_MEASURE.
|
|
|
|
*/
|
2005-06-11 20:52:12 +02:00
|
|
|
int
|
|
|
|
rep_hist_bandwidth_assess(void)
|
|
|
|
{
|
2006-01-11 04:57:01 +01:00
|
|
|
uint64_t w,r;
|
2004-07-20 22:57:46 +02:00
|
|
|
r = find_largest_max(read_array);
|
|
|
|
w = find_largest_max(write_array);
|
|
|
|
if (r>w)
|
2006-07-17 02:39:05 +02:00
|
|
|
return (int)(U64_TO_DBL(w)/NUM_SECS_ROLLING_MEASURE);
|
2004-07-20 22:57:46 +02:00
|
|
|
else
|
2006-07-17 02:39:05 +02:00
|
|
|
return (int)(U64_TO_DBL(r)/NUM_SECS_ROLLING_MEASURE);
|
2004-07-13 09:42:20 +02:00
|
|
|
}
|
|
|
|
|
2010-07-07 14:55:42 +02:00
|
|
|
/** Print the bandwidth history of b (either [dir-]read_array or
|
|
|
|
* [dir-]write_array) into the buffer pointed to by buf. The format is
|
|
|
|
* simply comma separated numbers, from oldest to newest.
|
2005-12-24 00:56:42 +01:00
|
|
|
*
|
|
|
|
* It returns the number of bytes written.
|
|
|
|
*/
|
2005-12-29 21:25:53 +01:00
|
|
|
static size_t
|
2005-12-24 00:56:42 +01:00
|
|
|
rep_hist_fill_bandwidth_history(char *buf, size_t len, bw_array_t *b)
|
|
|
|
{
|
|
|
|
char *cp = buf;
|
|
|
|
int i, n;
|
2007-10-10 17:07:19 +02:00
|
|
|
or_options_t *options = get_options();
|
|
|
|
uint64_t cutoff;
|
2005-12-24 00:56:42 +01:00
|
|
|
|
|
|
|
if (b->num_maxes_set <= b->next_max_idx) {
|
|
|
|
/* We haven't been through the circular array yet; time starts at i=0.*/
|
|
|
|
i = 0;
|
|
|
|
} else {
|
|
|
|
/* We've been around the array at least once. The next i to be
|
|
|
|
overwritten is the oldest. */
|
|
|
|
i = b->next_max_idx;
|
|
|
|
}
|
|
|
|
|
2007-10-10 17:07:19 +02:00
|
|
|
if (options->RelayBandwidthRate) {
|
|
|
|
/* We don't want to report that we used more bandwidth than the max we're
|
|
|
|
* willing to relay; otherwise everybody will know how much traffic
|
|
|
|
* we used ourself. */
|
|
|
|
cutoff = options->RelayBandwidthRate * NUM_SECS_BW_SUM_INTERVAL;
|
|
|
|
} else {
|
|
|
|
cutoff = UINT64_MAX;
|
|
|
|
}
|
|
|
|
|
2005-12-24 00:56:42 +01:00
|
|
|
for (n=0; n<b->num_maxes_set; ++n,++i) {
|
2006-12-07 21:11:36 +01:00
|
|
|
uint64_t total;
|
2007-06-06 15:02:22 +02:00
|
|
|
if (i >= NUM_TOTALS)
|
|
|
|
i -= NUM_TOTALS;
|
|
|
|
tor_assert(i < NUM_TOTALS);
|
2006-12-07 21:11:36 +01:00
|
|
|
/* Round the bandwidth used down to the nearest 1k. */
|
|
|
|
total = b->totals[i] & ~0x3ff;
|
2007-10-10 17:07:19 +02:00
|
|
|
if (total > cutoff)
|
|
|
|
total = cutoff;
|
|
|
|
|
2005-12-24 00:56:42 +01:00
|
|
|
if (n==(b->num_maxes_set-1))
|
2006-12-07 21:11:36 +01:00
|
|
|
tor_snprintf(cp, len-(cp-buf), U64_FORMAT, U64_PRINTF_ARG(total));
|
2005-12-24 00:56:42 +01:00
|
|
|
else
|
2006-12-07 21:11:36 +01:00
|
|
|
tor_snprintf(cp, len-(cp-buf), U64_FORMAT",", U64_PRINTF_ARG(total));
|
2005-12-24 00:56:42 +01:00
|
|
|
cp += strlen(cp);
|
|
|
|
}
|
|
|
|
return cp-buf;
|
|
|
|
}
|
|
|
|
|
2006-07-23 07:18:29 +02:00
|
|
|
/** Allocate and return lines for representing this server's bandwidth
|
2004-11-09 19:22:17 +01:00
|
|
|
* history in its descriptor.
|
2004-08-07 04:46:16 +02:00
|
|
|
*/
|
2004-11-09 19:22:17 +01:00
|
|
|
char *
|
2010-11-13 22:25:19 +01:00
|
|
|
rep_hist_get_bandwidth_lines(void)
|
2004-08-07 04:46:16 +02:00
|
|
|
{
|
|
|
|
char *buf, *cp;
|
|
|
|
char t[ISO_TIME_LEN+1];
|
2005-12-24 00:56:42 +01:00
|
|
|
int r;
|
2010-07-07 14:55:42 +02:00
|
|
|
bw_array_t *b = NULL;
|
|
|
|
const char *desc = NULL;
|
2004-08-07 04:46:16 +02:00
|
|
|
size_t len;
|
|
|
|
|
2010-07-07 14:55:42 +02:00
|
|
|
/* opt [dirreq-](read|write)-history yyyy-mm-dd HH:MM:SS (n s) n,n,n... */
|
|
|
|
len = (67+21*NUM_TOTALS)*4;
|
2004-08-07 04:46:16 +02:00
|
|
|
buf = tor_malloc_zero(len);
|
|
|
|
cp = buf;
|
2010-07-07 14:55:42 +02:00
|
|
|
for (r=0;r<4;++r) {
|
|
|
|
switch (r) {
|
|
|
|
case 0:
|
|
|
|
b = write_array;
|
|
|
|
desc = "write-history";
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
b = read_array;
|
|
|
|
desc = "read-history";
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
b = dir_write_array;
|
|
|
|
desc = "dirreq-write-history";
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
b = dir_read_array;
|
|
|
|
desc = "dirreq-read-history";
|
|
|
|
break;
|
|
|
|
}
|
2004-11-23 10:01:37 +01:00
|
|
|
tor_assert(b);
|
2004-08-07 04:46:16 +02:00
|
|
|
format_iso_time(t, b->next_period-NUM_SECS_BW_SUM_INTERVAL);
|
2010-11-13 22:25:19 +01:00
|
|
|
tor_snprintf(cp, len-(cp-buf), "%s %s (%d s) ",
|
|
|
|
desc, t, NUM_SECS_BW_SUM_INTERVAL);
|
2004-08-07 04:46:16 +02:00
|
|
|
cp += strlen(cp);
|
2005-12-24 00:56:42 +01:00
|
|
|
cp += rep_hist_fill_bandwidth_history(cp, len-(cp-buf), b);
|
2004-10-27 08:25:29 +02:00
|
|
|
strlcat(cp, "\n", len-(cp-buf));
|
2004-08-07 04:46:16 +02:00
|
|
|
++cp;
|
|
|
|
}
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
2005-12-24 23:48:12 +01:00
|
|
|
/** Update <b>state</b> with the newest bandwidth history. */
|
|
|
|
void
|
2005-12-24 00:56:42 +01:00
|
|
|
rep_hist_update_state(or_state_t *state)
|
|
|
|
{
|
|
|
|
int len, r;
|
|
|
|
char *buf, *cp;
|
2010-07-07 14:55:42 +02:00
|
|
|
smartlist_t **s_values = NULL;
|
|
|
|
time_t *s_begins = NULL;
|
|
|
|
int *s_interval = NULL;
|
|
|
|
bw_array_t *b = NULL;
|
2005-12-24 00:56:42 +01:00
|
|
|
|
|
|
|
len = 20*NUM_TOTALS+1;
|
|
|
|
buf = tor_malloc_zero(len);
|
|
|
|
|
2010-07-07 14:55:42 +02:00
|
|
|
for (r=0;r<4;++r) {
|
|
|
|
switch (r) {
|
|
|
|
case 0:
|
|
|
|
b = write_array;
|
|
|
|
s_begins = &state->BWHistoryWriteEnds;
|
|
|
|
s_interval = &state->BWHistoryWriteInterval;
|
|
|
|
s_values = &state->BWHistoryWriteValues;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
b = read_array;
|
|
|
|
s_begins = &state->BWHistoryReadEnds;
|
|
|
|
s_interval = &state->BWHistoryReadInterval;
|
|
|
|
s_values = &state->BWHistoryReadValues;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
b = dir_write_array;
|
|
|
|
s_begins = &state->BWHistoryDirWriteEnds;
|
|
|
|
s_interval = &state->BWHistoryDirWriteInterval;
|
|
|
|
s_values = &state->BWHistoryDirWriteValues;
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
b = dir_read_array;
|
|
|
|
s_begins = &state->BWHistoryDirReadEnds;
|
|
|
|
s_interval = &state->BWHistoryDirReadInterval;
|
|
|
|
s_values = &state->BWHistoryDirReadValues;
|
|
|
|
break;
|
|
|
|
}
|
2005-12-24 00:56:42 +01:00
|
|
|
if (*s_values) {
|
2007-05-17 00:15:14 +02:00
|
|
|
SMARTLIST_FOREACH(*s_values, char *, val, tor_free(val));
|
2005-12-24 00:56:42 +01:00
|
|
|
smartlist_free(*s_values);
|
|
|
|
}
|
2006-12-07 19:57:29 +01:00
|
|
|
if (! server_mode(get_options())) {
|
|
|
|
/* Clients don't need to store bandwidth history persistently;
|
|
|
|
* force these values to the defaults. */
|
2006-12-09 20:40:16 +01:00
|
|
|
/* FFFF we should pull the default out of config.c's state table,
|
|
|
|
* so we don't have two defaults. */
|
2006-12-24 03:45:46 +01:00
|
|
|
if (*s_begins != 0 || *s_interval != 900) {
|
|
|
|
time_t now = time(NULL);
|
|
|
|
time_t save_at = get_options()->AvoidDiskWrites ? now+3600 : now+600;
|
|
|
|
or_state_mark_dirty(state, save_at);
|
|
|
|
}
|
2006-12-07 19:57:29 +01:00
|
|
|
*s_begins = 0;
|
|
|
|
*s_interval = 900;
|
|
|
|
*s_values = smartlist_create();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
*s_begins = b->next_period;
|
|
|
|
*s_interval = NUM_SECS_BW_SUM_INTERVAL;
|
2005-12-24 00:56:42 +01:00
|
|
|
cp = buf;
|
|
|
|
cp += rep_hist_fill_bandwidth_history(cp, len, b);
|
|
|
|
tor_snprintf(cp, len-(cp-buf), cp == buf ? U64_FORMAT : ","U64_FORMAT,
|
|
|
|
U64_PRINTF_ARG(b->total_in_period));
|
|
|
|
*s_values = smartlist_create();
|
2006-01-02 13:19:28 +01:00
|
|
|
if (server_mode(get_options()))
|
|
|
|
smartlist_split_string(*s_values, buf, ",", SPLIT_SKIP_SPACE, 0);
|
2005-12-24 00:56:42 +01:00
|
|
|
}
|
|
|
|
tor_free(buf);
|
2006-12-24 03:45:46 +01:00
|
|
|
if (server_mode(get_options())) {
|
2006-12-07 21:11:30 +01:00
|
|
|
or_state_mark_dirty(get_or_state(), time(NULL)+(2*3600));
|
2006-12-24 03:45:46 +01:00
|
|
|
}
|
2005-12-24 00:56:42 +01:00
|
|
|
}
|
|
|
|
|
2006-07-23 07:18:29 +02:00
|
|
|
/** Set bandwidth history from our saved state. */
|
2005-12-24 00:56:42 +01:00
|
|
|
int
|
2006-03-26 08:51:26 +02:00
|
|
|
rep_hist_load_state(or_state_t *state, char **err)
|
2005-12-24 00:56:42 +01:00
|
|
|
{
|
2010-07-07 14:55:42 +02:00
|
|
|
time_t s_begins = 0, start;
|
2005-12-24 00:56:42 +01:00
|
|
|
time_t now = time(NULL);
|
|
|
|
uint64_t v;
|
|
|
|
int r,i,ok;
|
|
|
|
int all_ok = 1;
|
2010-07-07 14:55:42 +02:00
|
|
|
int s_interval = 0;
|
|
|
|
smartlist_t *s_values = NULL;
|
|
|
|
bw_array_t *b = NULL;
|
2005-12-24 00:56:42 +01:00
|
|
|
|
|
|
|
/* Assert they already have been malloced */
|
|
|
|
tor_assert(read_array && write_array);
|
|
|
|
|
2010-07-07 14:55:42 +02:00
|
|
|
for (r=0;r<4;++r) {
|
|
|
|
switch (r) {
|
|
|
|
case 0:
|
|
|
|
b = write_array;
|
|
|
|
s_begins = state->BWHistoryWriteEnds;
|
|
|
|
s_interval = state->BWHistoryWriteInterval;
|
|
|
|
s_values = state->BWHistoryWriteValues;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
b = read_array;
|
|
|
|
s_begins = state->BWHistoryReadEnds;
|
|
|
|
s_interval = state->BWHistoryReadInterval;
|
|
|
|
s_values = state->BWHistoryReadValues;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
b = dir_write_array;
|
|
|
|
s_begins = state->BWHistoryDirWriteEnds;
|
|
|
|
s_interval = state->BWHistoryDirWriteInterval;
|
|
|
|
s_values = state->BWHistoryDirWriteValues;
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
b = dir_read_array;
|
|
|
|
s_begins = state->BWHistoryDirReadEnds;
|
|
|
|
s_interval = state->BWHistoryDirReadInterval;
|
|
|
|
s_values = state->BWHistoryDirReadValues;
|
|
|
|
break;
|
|
|
|
}
|
2005-12-24 00:56:42 +01:00
|
|
|
if (s_values && s_begins >= now - NUM_SECS_BW_SUM_INTERVAL*NUM_TOTALS) {
|
|
|
|
start = s_begins - s_interval*(smartlist_len(s_values));
|
2007-08-18 20:20:42 +02:00
|
|
|
if (start > now)
|
|
|
|
continue;
|
2005-12-24 00:56:42 +01:00
|
|
|
b->cur_obs_time = start;
|
|
|
|
b->next_period = start + NUM_SECS_BW_SUM_INTERVAL;
|
|
|
|
SMARTLIST_FOREACH(s_values, char *, cp, {
|
|
|
|
v = tor_parse_uint64(cp, 10, 0, UINT64_MAX, &ok, NULL);
|
|
|
|
if (!ok) {
|
|
|
|
all_ok=0;
|
2008-09-23 20:24:20 +02:00
|
|
|
log_notice(LD_HIST, "Could not parse '%s' into a number.'", cp);
|
2005-12-24 00:56:42 +01:00
|
|
|
}
|
2007-08-18 20:20:42 +02:00
|
|
|
if (start < now) {
|
|
|
|
add_obs(b, start, v);
|
|
|
|
start += NUM_SECS_BW_SUM_INTERVAL;
|
|
|
|
}
|
2005-12-24 00:56:42 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Clean up maxima and observed */
|
|
|
|
/* Do we really want to zero this for the purpose of max capacity? */
|
|
|
|
for (i=0; i<NUM_SECS_ROLLING_MEASURE; ++i) {
|
|
|
|
b->obs[i] = 0;
|
|
|
|
}
|
|
|
|
b->total_obs = 0;
|
|
|
|
for (i=0; i<NUM_TOTALS; ++i) {
|
|
|
|
b->maxima[i] = 0;
|
|
|
|
}
|
|
|
|
b->max_total = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!all_ok) {
|
2006-03-26 08:51:26 +02:00
|
|
|
*err = tor_strdup("Parsing of bandwidth history values failed");
|
2005-12-24 00:56:42 +01:00
|
|
|
/* and create fresh arrays */
|
|
|
|
tor_free(read_array);
|
|
|
|
tor_free(write_array);
|
|
|
|
read_array = bw_array_new();
|
|
|
|
write_array = bw_array_new();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-05-30 08:19:06 +02:00
|
|
|
/*********************************************************************/
|
|
|
|
|
2010-10-15 20:02:55 +02:00
|
|
|
typedef struct predicted_port_t {
|
|
|
|
uint16_t port;
|
|
|
|
time_t time;
|
|
|
|
} predicted_port_t;
|
|
|
|
|
2004-12-05 08:10:08 +01:00
|
|
|
/** A list of port numbers that have been used recently. */
|
|
|
|
static smartlist_t *predicted_ports_list=NULL;
|
|
|
|
|
2006-07-23 07:18:29 +02:00
|
|
|
/** We just got an application request for a connection with
|
|
|
|
* port <b>port</b>. Remember it for the future, so we can keep
|
|
|
|
* some circuits open that will exit to this port.
|
|
|
|
*/
|
2005-06-11 20:52:12 +02:00
|
|
|
static void
|
2008-04-16 02:10:39 +02:00
|
|
|
add_predicted_port(time_t now, uint16_t port)
|
2005-06-11 20:52:12 +02:00
|
|
|
{
|
2010-10-15 20:02:55 +02:00
|
|
|
predicted_port_t *pp = tor_malloc(sizeof(predicted_port_t));
|
|
|
|
pp->port = port;
|
|
|
|
pp->time = now;
|
|
|
|
rephist_total_alloc += sizeof(*pp);
|
|
|
|
smartlist_add(predicted_ports_list, pp);
|
2004-12-05 08:10:08 +01:00
|
|
|
}
|
|
|
|
|
2006-07-23 07:18:29 +02:00
|
|
|
/** Initialize whatever memory and structs are needed for predicting
|
|
|
|
* which ports will be used. Also seed it with port 80, so we'll build
|
|
|
|
* circuits on start-up.
|
|
|
|
*/
|
2005-06-11 20:52:12 +02:00
|
|
|
static void
|
|
|
|
predicted_ports_init(void)
|
|
|
|
{
|
2004-12-05 08:10:08 +01:00
|
|
|
predicted_ports_list = smartlist_create();
|
2008-04-16 02:10:39 +02:00
|
|
|
add_predicted_port(time(NULL), 80); /* add one to kickstart us */
|
2004-12-05 08:10:08 +01:00
|
|
|
}
|
|
|
|
|
2006-07-23 07:18:29 +02:00
|
|
|
/** Free whatever memory is needed for predicting which ports will
|
|
|
|
* be used.
|
|
|
|
*/
|
2005-06-11 20:52:12 +02:00
|
|
|
static void
|
|
|
|
predicted_ports_free(void)
|
|
|
|
{
|
2010-10-15 20:02:55 +02:00
|
|
|
rephist_total_alloc -=
|
|
|
|
smartlist_len(predicted_ports_list)*sizeof(predicted_port_t);
|
|
|
|
SMARTLIST_FOREACH(predicted_ports_list, predicted_port_t *, pp, tor_free(pp));
|
2005-02-28 02:59:18 +01:00
|
|
|
smartlist_free(predicted_ports_list);
|
|
|
|
}
|
|
|
|
|
2004-12-05 08:10:08 +01:00
|
|
|
/** Remember that <b>port</b> has been asked for as of time <b>now</b>.
|
|
|
|
* This is used for predicting what sorts of streams we'll make in the
|
2005-11-25 09:08:56 +01:00
|
|
|
* future and making exit circuits to anticipate that.
|
2004-12-05 08:10:08 +01:00
|
|
|
*/
|
2005-06-11 20:52:12 +02:00
|
|
|
void
|
2008-04-16 02:10:39 +02:00
|
|
|
rep_hist_note_used_port(time_t now, uint16_t port)
|
2005-06-11 20:52:12 +02:00
|
|
|
{
|
2004-12-05 08:10:08 +01:00
|
|
|
tor_assert(predicted_ports_list);
|
|
|
|
|
2004-12-07 06:33:55 +01:00
|
|
|
if (!port) /* record nothing */
|
2004-12-05 08:10:08 +01:00
|
|
|
return;
|
|
|
|
|
2010-10-15 20:02:55 +02:00
|
|
|
SMARTLIST_FOREACH_BEGIN(predicted_ports_list, predicted_port_t *, pp) {
|
|
|
|
if (pp->port == port) {
|
|
|
|
pp->time = now;
|
2004-12-05 08:10:08 +01:00
|
|
|
return;
|
|
|
|
}
|
2010-10-15 20:02:55 +02:00
|
|
|
} SMARTLIST_FOREACH_END(pp);
|
2004-12-05 08:10:08 +01:00
|
|
|
/* it's not there yet; we need to add it */
|
2008-04-16 02:10:39 +02:00
|
|
|
add_predicted_port(now, port);
|
2004-12-05 08:10:08 +01:00
|
|
|
}
|
|
|
|
|
2006-03-12 23:48:18 +01:00
|
|
|
/** For this long after we've seen a request for a given port, assume that
|
|
|
|
* we'll want to make connections to the same port in the future. */
|
|
|
|
#define PREDICTED_CIRCS_RELEVANCE_TIME (60*60)
|
2004-12-05 08:10:08 +01:00
|
|
|
|
2010-10-15 20:02:55 +02:00
|
|
|
/** Return a newly allocated pointer to a list of uint16_t * for ports that
|
2004-12-05 08:10:08 +01:00
|
|
|
* are likely to be asked for in the near future.
|
|
|
|
*/
|
2005-06-11 20:52:12 +02:00
|
|
|
smartlist_t *
|
|
|
|
rep_hist_get_predicted_ports(time_t now)
|
|
|
|
{
|
2010-10-15 20:02:55 +02:00
|
|
|
smartlist_t *out = smartlist_create();
|
2004-12-05 08:10:08 +01:00
|
|
|
tor_assert(predicted_ports_list);
|
|
|
|
|
|
|
|
/* clean out obsolete entries */
|
2010-10-15 20:02:55 +02:00
|
|
|
SMARTLIST_FOREACH_BEGIN(predicted_ports_list, predicted_port_t *, pp) {
|
|
|
|
if (pp->time + PREDICTED_CIRCS_RELEVANCE_TIME < now) {
|
|
|
|
log_debug(LD_CIRC, "Expiring predicted port %d", pp->port);
|
|
|
|
|
|
|
|
rephist_total_alloc -= sizeof(predicted_port_t);
|
|
|
|
tor_free(pp);
|
|
|
|
SMARTLIST_DEL_CURRENT(predicted_ports_list, pp);
|
|
|
|
} else {
|
|
|
|
smartlist_add(out, tor_memdup(&pp->port, sizeof(uint16_t)));
|
2004-12-05 08:10:08 +01:00
|
|
|
}
|
2010-10-15 20:02:55 +02:00
|
|
|
} SMARTLIST_FOREACH_END(pp);
|
|
|
|
return out;
|
2004-12-05 08:10:08 +01:00
|
|
|
}
|
|
|
|
|
2005-11-25 09:08:56 +01:00
|
|
|
/** The user asked us to do a resolve. Rather than keeping track of
|
2008-04-16 02:12:44 +02:00
|
|
|
* timings and such of resolves, we fake it for now by treating
|
2005-11-25 09:08:56 +01:00
|
|
|
* it the same way as a connection to port 80. This way we will continue
|
|
|
|
* to have circuits lying around if the user only uses Tor for resolves.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
rep_hist_note_used_resolve(time_t now)
|
|
|
|
{
|
2008-04-16 02:10:39 +02:00
|
|
|
rep_hist_note_used_port(now, 80);
|
2005-11-25 09:08:56 +01:00
|
|
|
}
|
|
|
|
|
Introduce a notion of 'internal' circs, which are chosen without regard
to the exit policy of the last hop. Intro and rendezvous circs must
be internal circs, to avoid leaking information. Resolve and connect
streams can use internal circs if they want.
New circuit pooling algorithm: make sure to have enough circs around
to satisfy any predicted ports, and also make sure to have 2 internal
circs around if we've required internal circs lately (with high uptime
if we've seen that lately).
Split NewCircuitPeriod config option into NewCircuitPeriod (30 secs),
which describes how often we retry making new circuits if current ones
are dirty, and MaxCircuitDirtiness (10 mins), which describes how long
we're willing to make use of an already-dirty circuit.
Once rendezvous circuits are established, keep using the same circuit as
long as you attach a new stream to it at least every 10 minutes. (So web
browsing doesn't require you to build new rend circs every 30 seconds.)
Cannibalize GENERAL circs to be C_REND, C_INTRO, S_INTRO, and S_REND
circ as necessary, if there are any completed ones lying around when
we try to launch one.
Re-instate the ifdef's to use version-0 style introduce cells, since
there was yet another bug in handling version-1 style. We'll try switching
over again after 0.0.9 is obsolete.
Bugfix: when choosing an exit node for a new non-internal circ, don't take
into account whether it'll be useful for any pending x.onion addresses --
it won't.
Bugfix: we weren't actually publishing the hidden service descriptor when
it became dirty. So we only published it every 20 minutes or so, which
means when you first start your Tor, the hidden service will seem broken.
svn:r3360
2005-01-17 19:13:09 +01:00
|
|
|
/** The last time at which we needed an internal circ. */
|
2005-11-25 09:08:56 +01:00
|
|
|
static time_t predicted_internal_time = 0;
|
Introduce a notion of 'internal' circs, which are chosen without regard
to the exit policy of the last hop. Intro and rendezvous circs must
be internal circs, to avoid leaking information. Resolve and connect
streams can use internal circs if they want.
New circuit pooling algorithm: make sure to have enough circs around
to satisfy any predicted ports, and also make sure to have 2 internal
circs around if we've required internal circs lately (with high uptime
if we've seen that lately).
Split NewCircuitPeriod config option into NewCircuitPeriod (30 secs),
which describes how often we retry making new circuits if current ones
are dirty, and MaxCircuitDirtiness (10 mins), which describes how long
we're willing to make use of an already-dirty circuit.
Once rendezvous circuits are established, keep using the same circuit as
long as you attach a new stream to it at least every 10 minutes. (So web
browsing doesn't require you to build new rend circs every 30 seconds.)
Cannibalize GENERAL circs to be C_REND, C_INTRO, S_INTRO, and S_REND
circ as necessary, if there are any completed ones lying around when
we try to launch one.
Re-instate the ifdef's to use version-0 style introduce cells, since
there was yet another bug in handling version-1 style. We'll try switching
over again after 0.0.9 is obsolete.
Bugfix: when choosing an exit node for a new non-internal circ, don't take
into account whether it'll be useful for any pending x.onion addresses --
it won't.
Bugfix: we weren't actually publishing the hidden service descriptor when
it became dirty. So we only published it every 20 minutes or so, which
means when you first start your Tor, the hidden service will seem broken.
svn:r3360
2005-01-17 19:13:09 +01:00
|
|
|
/** The last time we needed an internal circ with good uptime. */
|
2005-11-25 09:08:56 +01:00
|
|
|
static time_t predicted_internal_uptime_time = 0;
|
Introduce a notion of 'internal' circs, which are chosen without regard
to the exit policy of the last hop. Intro and rendezvous circs must
be internal circs, to avoid leaking information. Resolve and connect
streams can use internal circs if they want.
New circuit pooling algorithm: make sure to have enough circs around
to satisfy any predicted ports, and also make sure to have 2 internal
circs around if we've required internal circs lately (with high uptime
if we've seen that lately).
Split NewCircuitPeriod config option into NewCircuitPeriod (30 secs),
which describes how often we retry making new circuits if current ones
are dirty, and MaxCircuitDirtiness (10 mins), which describes how long
we're willing to make use of an already-dirty circuit.
Once rendezvous circuits are established, keep using the same circuit as
long as you attach a new stream to it at least every 10 minutes. (So web
browsing doesn't require you to build new rend circs every 30 seconds.)
Cannibalize GENERAL circs to be C_REND, C_INTRO, S_INTRO, and S_REND
circ as necessary, if there are any completed ones lying around when
we try to launch one.
Re-instate the ifdef's to use version-0 style introduce cells, since
there was yet another bug in handling version-1 style. We'll try switching
over again after 0.0.9 is obsolete.
Bugfix: when choosing an exit node for a new non-internal circ, don't take
into account whether it'll be useful for any pending x.onion addresses --
it won't.
Bugfix: we weren't actually publishing the hidden service descriptor when
it became dirty. So we only published it every 20 minutes or so, which
means when you first start your Tor, the hidden service will seem broken.
svn:r3360
2005-01-17 19:13:09 +01:00
|
|
|
/** The last time we needed an internal circ with good capacity. */
|
2005-11-25 09:08:56 +01:00
|
|
|
static time_t predicted_internal_capacity_time = 0;
|
Introduce a notion of 'internal' circs, which are chosen without regard
to the exit policy of the last hop. Intro and rendezvous circs must
be internal circs, to avoid leaking information. Resolve and connect
streams can use internal circs if they want.
New circuit pooling algorithm: make sure to have enough circs around
to satisfy any predicted ports, and also make sure to have 2 internal
circs around if we've required internal circs lately (with high uptime
if we've seen that lately).
Split NewCircuitPeriod config option into NewCircuitPeriod (30 secs),
which describes how often we retry making new circuits if current ones
are dirty, and MaxCircuitDirtiness (10 mins), which describes how long
we're willing to make use of an already-dirty circuit.
Once rendezvous circuits are established, keep using the same circuit as
long as you attach a new stream to it at least every 10 minutes. (So web
browsing doesn't require you to build new rend circs every 30 seconds.)
Cannibalize GENERAL circs to be C_REND, C_INTRO, S_INTRO, and S_REND
circ as necessary, if there are any completed ones lying around when
we try to launch one.
Re-instate the ifdef's to use version-0 style introduce cells, since
there was yet another bug in handling version-1 style. We'll try switching
over again after 0.0.9 is obsolete.
Bugfix: when choosing an exit node for a new non-internal circ, don't take
into account whether it'll be useful for any pending x.onion addresses --
it won't.
Bugfix: we weren't actually publishing the hidden service descriptor when
it became dirty. So we only published it every 20 minutes or so, which
means when you first start your Tor, the hidden service will seem broken.
svn:r3360
2005-01-17 19:13:09 +01:00
|
|
|
|
|
|
|
/** Remember that we used an internal circ at time <b>now</b>. */
|
2005-06-11 20:52:12 +02:00
|
|
|
void
|
2005-11-25 09:08:56 +01:00
|
|
|
rep_hist_note_used_internal(time_t now, int need_uptime, int need_capacity)
|
2005-06-11 20:52:12 +02:00
|
|
|
{
|
2005-11-25 09:08:56 +01:00
|
|
|
predicted_internal_time = now;
|
Introduce a notion of 'internal' circs, which are chosen without regard
to the exit policy of the last hop. Intro and rendezvous circs must
be internal circs, to avoid leaking information. Resolve and connect
streams can use internal circs if they want.
New circuit pooling algorithm: make sure to have enough circs around
to satisfy any predicted ports, and also make sure to have 2 internal
circs around if we've required internal circs lately (with high uptime
if we've seen that lately).
Split NewCircuitPeriod config option into NewCircuitPeriod (30 secs),
which describes how often we retry making new circuits if current ones
are dirty, and MaxCircuitDirtiness (10 mins), which describes how long
we're willing to make use of an already-dirty circuit.
Once rendezvous circuits are established, keep using the same circuit as
long as you attach a new stream to it at least every 10 minutes. (So web
browsing doesn't require you to build new rend circs every 30 seconds.)
Cannibalize GENERAL circs to be C_REND, C_INTRO, S_INTRO, and S_REND
circ as necessary, if there are any completed ones lying around when
we try to launch one.
Re-instate the ifdef's to use version-0 style introduce cells, since
there was yet another bug in handling version-1 style. We'll try switching
over again after 0.0.9 is obsolete.
Bugfix: when choosing an exit node for a new non-internal circ, don't take
into account whether it'll be useful for any pending x.onion addresses --
it won't.
Bugfix: we weren't actually publishing the hidden service descriptor when
it became dirty. So we only published it every 20 minutes or so, which
means when you first start your Tor, the hidden service will seem broken.
svn:r3360
2005-01-17 19:13:09 +01:00
|
|
|
if (need_uptime)
|
2005-11-25 09:08:56 +01:00
|
|
|
predicted_internal_uptime_time = now;
|
Introduce a notion of 'internal' circs, which are chosen without regard
to the exit policy of the last hop. Intro and rendezvous circs must
be internal circs, to avoid leaking information. Resolve and connect
streams can use internal circs if they want.
New circuit pooling algorithm: make sure to have enough circs around
to satisfy any predicted ports, and also make sure to have 2 internal
circs around if we've required internal circs lately (with high uptime
if we've seen that lately).
Split NewCircuitPeriod config option into NewCircuitPeriod (30 secs),
which describes how often we retry making new circuits if current ones
are dirty, and MaxCircuitDirtiness (10 mins), which describes how long
we're willing to make use of an already-dirty circuit.
Once rendezvous circuits are established, keep using the same circuit as
long as you attach a new stream to it at least every 10 minutes. (So web
browsing doesn't require you to build new rend circs every 30 seconds.)
Cannibalize GENERAL circs to be C_REND, C_INTRO, S_INTRO, and S_REND
circ as necessary, if there are any completed ones lying around when
we try to launch one.
Re-instate the ifdef's to use version-0 style introduce cells, since
there was yet another bug in handling version-1 style. We'll try switching
over again after 0.0.9 is obsolete.
Bugfix: when choosing an exit node for a new non-internal circ, don't take
into account whether it'll be useful for any pending x.onion addresses --
it won't.
Bugfix: we weren't actually publishing the hidden service descriptor when
it became dirty. So we only published it every 20 minutes or so, which
means when you first start your Tor, the hidden service will seem broken.
svn:r3360
2005-01-17 19:13:09 +01:00
|
|
|
if (need_capacity)
|
2005-11-25 09:08:56 +01:00
|
|
|
predicted_internal_capacity_time = now;
|
Introduce a notion of 'internal' circs, which are chosen without regard
to the exit policy of the last hop. Intro and rendezvous circs must
be internal circs, to avoid leaking information. Resolve and connect
streams can use internal circs if they want.
New circuit pooling algorithm: make sure to have enough circs around
to satisfy any predicted ports, and also make sure to have 2 internal
circs around if we've required internal circs lately (with high uptime
if we've seen that lately).
Split NewCircuitPeriod config option into NewCircuitPeriod (30 secs),
which describes how often we retry making new circuits if current ones
are dirty, and MaxCircuitDirtiness (10 mins), which describes how long
we're willing to make use of an already-dirty circuit.
Once rendezvous circuits are established, keep using the same circuit as
long as you attach a new stream to it at least every 10 minutes. (So web
browsing doesn't require you to build new rend circs every 30 seconds.)
Cannibalize GENERAL circs to be C_REND, C_INTRO, S_INTRO, and S_REND
circ as necessary, if there are any completed ones lying around when
we try to launch one.
Re-instate the ifdef's to use version-0 style introduce cells, since
there was yet another bug in handling version-1 style. We'll try switching
over again after 0.0.9 is obsolete.
Bugfix: when choosing an exit node for a new non-internal circ, don't take
into account whether it'll be useful for any pending x.onion addresses --
it won't.
Bugfix: we weren't actually publishing the hidden service descriptor when
it became dirty. So we only published it every 20 minutes or so, which
means when you first start your Tor, the hidden service will seem broken.
svn:r3360
2005-01-17 19:13:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/** Return 1 if we've used an internal circ recently; else return 0. */
|
2005-06-11 20:52:12 +02:00
|
|
|
int
|
2005-12-14 21:40:40 +01:00
|
|
|
rep_hist_get_predicted_internal(time_t now, int *need_uptime,
|
|
|
|
int *need_capacity)
|
2005-06-11 20:52:12 +02:00
|
|
|
{
|
2005-11-25 09:08:56 +01:00
|
|
|
if (!predicted_internal_time) { /* initialize it */
|
|
|
|
predicted_internal_time = now;
|
|
|
|
predicted_internal_uptime_time = now;
|
|
|
|
predicted_internal_capacity_time = now;
|
2005-08-13 02:22:07 +02:00
|
|
|
}
|
2005-11-25 09:08:56 +01:00
|
|
|
if (predicted_internal_time + PREDICTED_CIRCS_RELEVANCE_TIME < now)
|
Introduce a notion of 'internal' circs, which are chosen without regard
to the exit policy of the last hop. Intro and rendezvous circs must
be internal circs, to avoid leaking information. Resolve and connect
streams can use internal circs if they want.
New circuit pooling algorithm: make sure to have enough circs around
to satisfy any predicted ports, and also make sure to have 2 internal
circs around if we've required internal circs lately (with high uptime
if we've seen that lately).
Split NewCircuitPeriod config option into NewCircuitPeriod (30 secs),
which describes how often we retry making new circuits if current ones
are dirty, and MaxCircuitDirtiness (10 mins), which describes how long
we're willing to make use of an already-dirty circuit.
Once rendezvous circuits are established, keep using the same circuit as
long as you attach a new stream to it at least every 10 minutes. (So web
browsing doesn't require you to build new rend circs every 30 seconds.)
Cannibalize GENERAL circs to be C_REND, C_INTRO, S_INTRO, and S_REND
circ as necessary, if there are any completed ones lying around when
we try to launch one.
Re-instate the ifdef's to use version-0 style introduce cells, since
there was yet another bug in handling version-1 style. We'll try switching
over again after 0.0.9 is obsolete.
Bugfix: when choosing an exit node for a new non-internal circ, don't take
into account whether it'll be useful for any pending x.onion addresses --
it won't.
Bugfix: we weren't actually publishing the hidden service descriptor when
it became dirty. So we only published it every 20 minutes or so, which
means when you first start your Tor, the hidden service will seem broken.
svn:r3360
2005-01-17 19:13:09 +01:00
|
|
|
return 0; /* too long ago */
|
2006-09-30 21:22:24 +02:00
|
|
|
if (predicted_internal_uptime_time + PREDICTED_CIRCS_RELEVANCE_TIME >= now)
|
Introduce a notion of 'internal' circs, which are chosen without regard
to the exit policy of the last hop. Intro and rendezvous circs must
be internal circs, to avoid leaking information. Resolve and connect
streams can use internal circs if they want.
New circuit pooling algorithm: make sure to have enough circs around
to satisfy any predicted ports, and also make sure to have 2 internal
circs around if we've required internal circs lately (with high uptime
if we've seen that lately).
Split NewCircuitPeriod config option into NewCircuitPeriod (30 secs),
which describes how often we retry making new circuits if current ones
are dirty, and MaxCircuitDirtiness (10 mins), which describes how long
we're willing to make use of an already-dirty circuit.
Once rendezvous circuits are established, keep using the same circuit as
long as you attach a new stream to it at least every 10 minutes. (So web
browsing doesn't require you to build new rend circs every 30 seconds.)
Cannibalize GENERAL circs to be C_REND, C_INTRO, S_INTRO, and S_REND
circ as necessary, if there are any completed ones lying around when
we try to launch one.
Re-instate the ifdef's to use version-0 style introduce cells, since
there was yet another bug in handling version-1 style. We'll try switching
over again after 0.0.9 is obsolete.
Bugfix: when choosing an exit node for a new non-internal circ, don't take
into account whether it'll be useful for any pending x.onion addresses --
it won't.
Bugfix: we weren't actually publishing the hidden service descriptor when
it became dirty. So we only published it every 20 minutes or so, which
means when you first start your Tor, the hidden service will seem broken.
svn:r3360
2005-01-17 19:13:09 +01:00
|
|
|
*need_uptime = 1;
|
2010-02-14 23:45:45 +01:00
|
|
|
// Always predict that we need capacity.
|
|
|
|
*need_capacity = 1;
|
Introduce a notion of 'internal' circs, which are chosen without regard
to the exit policy of the last hop. Intro and rendezvous circs must
be internal circs, to avoid leaking information. Resolve and connect
streams can use internal circs if they want.
New circuit pooling algorithm: make sure to have enough circs around
to satisfy any predicted ports, and also make sure to have 2 internal
circs around if we've required internal circs lately (with high uptime
if we've seen that lately).
Split NewCircuitPeriod config option into NewCircuitPeriod (30 secs),
which describes how often we retry making new circuits if current ones
are dirty, and MaxCircuitDirtiness (10 mins), which describes how long
we're willing to make use of an already-dirty circuit.
Once rendezvous circuits are established, keep using the same circuit as
long as you attach a new stream to it at least every 10 minutes. (So web
browsing doesn't require you to build new rend circs every 30 seconds.)
Cannibalize GENERAL circs to be C_REND, C_INTRO, S_INTRO, and S_REND
circ as necessary, if there are any completed ones lying around when
we try to launch one.
Re-instate the ifdef's to use version-0 style introduce cells, since
there was yet another bug in handling version-1 style. We'll try switching
over again after 0.0.9 is obsolete.
Bugfix: when choosing an exit node for a new non-internal circ, don't take
into account whether it'll be useful for any pending x.onion addresses --
it won't.
Bugfix: we weren't actually publishing the hidden service descriptor when
it became dirty. So we only published it every 20 minutes or so, which
means when you first start your Tor, the hidden service will seem broken.
svn:r3360
2005-01-17 19:13:09 +01:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2006-07-22 09:15:34 +02:00
|
|
|
/** Any ports used lately? These are pre-seeded if we just started
|
|
|
|
* up or if we're running a hidden service. */
|
|
|
|
int
|
|
|
|
any_predicted_circuits(time_t now)
|
|
|
|
{
|
|
|
|
return smartlist_len(predicted_ports_list) ||
|
|
|
|
predicted_internal_time + PREDICTED_CIRCS_RELEVANCE_TIME >= now;
|
|
|
|
}
|
|
|
|
|
2006-05-30 08:19:06 +02:00
|
|
|
/** Return 1 if we have no need for circuits currently, else return 0. */
|
|
|
|
int
|
2006-06-09 00:36:13 +02:00
|
|
|
rep_hist_circbuilding_dormant(time_t now)
|
2006-05-30 08:19:06 +02:00
|
|
|
{
|
2006-07-22 09:15:34 +02:00
|
|
|
if (any_predicted_circuits(now))
|
2006-06-03 04:56:44 +02:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* see if we'll still need to build testing circuits */
|
2008-02-19 23:01:45 +01:00
|
|
|
if (server_mode(get_options()) &&
|
|
|
|
(!check_whether_orport_reachable() || !circuit_enough_testing_circs()))
|
2006-06-03 04:56:44 +02:00
|
|
|
return 0;
|
|
|
|
if (!check_whether_dirport_reachable())
|
|
|
|
return 0;
|
|
|
|
|
2006-05-30 08:19:06 +02:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2008-12-26 18:35:12 +01:00
|
|
|
/** Structure to track how many times we've done each public key operation. */
|
|
|
|
static struct {
|
|
|
|
/** How many directory objects have we signed? */
|
|
|
|
unsigned long n_signed_dir_objs;
|
|
|
|
/** How many routerdescs have we signed? */
|
|
|
|
unsigned long n_signed_routerdescs;
|
|
|
|
/** How many directory objects have we verified? */
|
|
|
|
unsigned long n_verified_dir_objs;
|
|
|
|
/** How many routerdescs have we verified */
|
|
|
|
unsigned long n_verified_routerdescs;
|
|
|
|
/** How many onionskins have we encrypted to build circuits? */
|
|
|
|
unsigned long n_onionskins_encrypted;
|
|
|
|
/** How many onionskins have we decrypted to do circuit build requests? */
|
|
|
|
unsigned long n_onionskins_decrypted;
|
|
|
|
/** How many times have we done the TLS handshake as a client? */
|
|
|
|
unsigned long n_tls_client_handshakes;
|
|
|
|
/** How many times have we done the TLS handshake as a server? */
|
|
|
|
unsigned long n_tls_server_handshakes;
|
|
|
|
/** How many PK operations have we done as a hidden service client? */
|
|
|
|
unsigned long n_rend_client_ops;
|
|
|
|
/** How many PK operations have we done as a hidden service midpoint? */
|
|
|
|
unsigned long n_rend_mid_ops;
|
|
|
|
/** How many PK operations have we done as a hidden service provider? */
|
|
|
|
unsigned long n_rend_server_ops;
|
|
|
|
} pk_op_counts = {0,0,0,0,0,0,0,0,0,0,0};
|
2006-10-31 20:17:07 +01:00
|
|
|
|
2007-02-24 08:50:38 +01:00
|
|
|
/** Increment the count of the number of times we've done <b>operation</b>. */
|
2006-10-31 20:17:07 +01:00
|
|
|
void
|
|
|
|
note_crypto_pk_op(pk_op_t operation)
|
|
|
|
{
|
|
|
|
switch (operation)
|
|
|
|
{
|
|
|
|
case SIGN_DIR:
|
2008-12-26 18:35:12 +01:00
|
|
|
pk_op_counts.n_signed_dir_objs++;
|
2006-10-31 20:17:07 +01:00
|
|
|
break;
|
|
|
|
case SIGN_RTR:
|
2008-12-26 18:35:12 +01:00
|
|
|
pk_op_counts.n_signed_routerdescs++;
|
2006-10-31 20:17:07 +01:00
|
|
|
break;
|
|
|
|
case VERIFY_DIR:
|
2008-12-26 18:35:12 +01:00
|
|
|
pk_op_counts.n_verified_dir_objs++;
|
2006-10-31 20:17:07 +01:00
|
|
|
break;
|
|
|
|
case VERIFY_RTR:
|
2008-12-26 18:35:12 +01:00
|
|
|
pk_op_counts.n_verified_routerdescs++;
|
2006-10-31 20:17:07 +01:00
|
|
|
break;
|
|
|
|
case ENC_ONIONSKIN:
|
2008-12-26 18:35:12 +01:00
|
|
|
pk_op_counts.n_onionskins_encrypted++;
|
2006-10-31 20:17:07 +01:00
|
|
|
break;
|
|
|
|
case DEC_ONIONSKIN:
|
2008-12-26 18:35:12 +01:00
|
|
|
pk_op_counts.n_onionskins_decrypted++;
|
2006-10-31 20:17:07 +01:00
|
|
|
break;
|
|
|
|
case TLS_HANDSHAKE_C:
|
2008-12-26 18:35:12 +01:00
|
|
|
pk_op_counts.n_tls_client_handshakes++;
|
2006-10-31 20:17:07 +01:00
|
|
|
break;
|
|
|
|
case TLS_HANDSHAKE_S:
|
2008-12-26 18:35:12 +01:00
|
|
|
pk_op_counts.n_tls_server_handshakes++;
|
2006-10-31 20:17:07 +01:00
|
|
|
break;
|
|
|
|
case REND_CLIENT:
|
2008-12-26 18:35:12 +01:00
|
|
|
pk_op_counts.n_rend_client_ops++;
|
2006-10-31 20:17:07 +01:00
|
|
|
break;
|
|
|
|
case REND_MID:
|
2008-12-26 18:35:12 +01:00
|
|
|
pk_op_counts.n_rend_mid_ops++;
|
2006-10-31 20:17:07 +01:00
|
|
|
break;
|
|
|
|
case REND_SERVER:
|
2008-12-26 18:35:12 +01:00
|
|
|
pk_op_counts.n_rend_server_ops++;
|
2006-10-31 20:17:07 +01:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
log_warn(LD_BUG, "Unknown pk operation %d", operation);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-02-16 21:01:02 +01:00
|
|
|
/** Log the number of times we've done each public/private-key operation. */
|
2006-10-31 20:17:07 +01:00
|
|
|
void
|
|
|
|
dump_pk_ops(int severity)
|
|
|
|
{
|
2008-09-23 20:24:20 +02:00
|
|
|
log(severity, LD_HIST,
|
2006-10-31 20:17:07 +01:00
|
|
|
"PK operations: %lu directory objects signed, "
|
|
|
|
"%lu directory objects verified, "
|
|
|
|
"%lu routerdescs signed, "
|
|
|
|
"%lu routerdescs verified, "
|
|
|
|
"%lu onionskins encrypted, "
|
|
|
|
"%lu onionskins decrypted, "
|
|
|
|
"%lu client-side TLS handshakes, "
|
|
|
|
"%lu server-side TLS handshakes, "
|
|
|
|
"%lu rendezvous client operations, "
|
|
|
|
"%lu rendezvous middle operations, "
|
|
|
|
"%lu rendezvous server operations.",
|
2008-12-26 18:35:12 +01:00
|
|
|
pk_op_counts.n_signed_dir_objs,
|
|
|
|
pk_op_counts.n_verified_dir_objs,
|
|
|
|
pk_op_counts.n_signed_routerdescs,
|
|
|
|
pk_op_counts.n_verified_routerdescs,
|
|
|
|
pk_op_counts.n_onionskins_encrypted,
|
|
|
|
pk_op_counts.n_onionskins_decrypted,
|
|
|
|
pk_op_counts.n_tls_client_handshakes,
|
|
|
|
pk_op_counts.n_tls_server_handshakes,
|
|
|
|
pk_op_counts.n_rend_client_ops,
|
|
|
|
pk_op_counts.n_rend_mid_ops,
|
|
|
|
pk_op_counts.n_rend_server_ops);
|
2006-10-31 20:17:07 +01:00
|
|
|
}
|
|
|
|
|
2010-08-04 07:32:10 +02:00
|
|
|
/*** Exit port statistics ***/
|
|
|
|
|
|
|
|
/* Some constants */
|
|
|
|
/** To what multiple should byte numbers be rounded up? */
|
|
|
|
#define EXIT_STATS_ROUND_UP_BYTES 1024
|
|
|
|
/** To what multiple should stream counts be rounded up? */
|
|
|
|
#define EXIT_STATS_ROUND_UP_STREAMS 4
|
|
|
|
/** Number of TCP ports */
|
|
|
|
#define EXIT_STATS_NUM_PORTS 65536
|
2010-11-23 21:09:12 +01:00
|
|
|
/** Top n ports that will be included in exit stats. */
|
|
|
|
#define EXIT_STATS_TOP_N_PORTS 10
|
2010-08-04 07:32:10 +02:00
|
|
|
|
|
|
|
/* The following data structures are arrays and no fancy smartlists or maps,
|
|
|
|
* so that all write operations can be done in constant time. This comes at
|
|
|
|
* the price of some memory (1.25 MB) and linear complexity when writing
|
|
|
|
* stats for measuring relays. */
|
|
|
|
/** Number of bytes read in current period by exit port */
|
|
|
|
static uint64_t *exit_bytes_read = NULL;
|
|
|
|
/** Number of bytes written in current period by exit port */
|
|
|
|
static uint64_t *exit_bytes_written = NULL;
|
|
|
|
/** Number of streams opened in current period by exit port */
|
|
|
|
static uint32_t *exit_streams = NULL;
|
|
|
|
|
|
|
|
/** Start time of exit stats or 0 if we're not collecting exit stats. */
|
|
|
|
static time_t start_of_exit_stats_interval;
|
|
|
|
|
|
|
|
/** Initialize exit port stats. */
|
2005-06-11 20:52:12 +02:00
|
|
|
void
|
2010-08-04 07:32:10 +02:00
|
|
|
rep_hist_exit_stats_init(time_t now)
|
2005-02-11 00:18:39 +01:00
|
|
|
{
|
2010-08-04 07:32:10 +02:00
|
|
|
start_of_exit_stats_interval = now;
|
|
|
|
exit_bytes_read = tor_malloc_zero(EXIT_STATS_NUM_PORTS *
|
|
|
|
sizeof(uint64_t));
|
|
|
|
exit_bytes_written = tor_malloc_zero(EXIT_STATS_NUM_PORTS *
|
|
|
|
sizeof(uint64_t));
|
|
|
|
exit_streams = tor_malloc_zero(EXIT_STATS_NUM_PORTS *
|
|
|
|
sizeof(uint32_t));
|
|
|
|
}
|
|
|
|
|
2010-08-11 14:13:08 +02:00
|
|
|
/** Reset counters for exit port statistics. */
|
|
|
|
void
|
|
|
|
rep_hist_reset_exit_stats(time_t now)
|
|
|
|
{
|
|
|
|
start_of_exit_stats_interval = now;
|
|
|
|
memset(exit_bytes_read, 0, EXIT_STATS_NUM_PORTS * sizeof(uint64_t));
|
|
|
|
memset(exit_bytes_written, 0, EXIT_STATS_NUM_PORTS * sizeof(uint64_t));
|
|
|
|
memset(exit_streams, 0, EXIT_STATS_NUM_PORTS * sizeof(uint32_t));
|
|
|
|
}
|
|
|
|
|
2010-08-04 07:32:10 +02:00
|
|
|
/** Stop collecting exit port stats in a way that we can re-start doing
|
|
|
|
* so in rep_hist_exit_stats_init(). */
|
|
|
|
void
|
|
|
|
rep_hist_exit_stats_term(void)
|
|
|
|
{
|
|
|
|
start_of_exit_stats_interval = 0;
|
2009-08-14 14:33:29 +02:00
|
|
|
tor_free(exit_bytes_read);
|
|
|
|
tor_free(exit_bytes_written);
|
|
|
|
tor_free(exit_streams);
|
2010-08-04 07:32:10 +02:00
|
|
|
}
|
|
|
|
|
2010-11-29 21:27:54 +01:00
|
|
|
/** Helper for qsort: compare two ints. */
|
2010-11-23 21:09:12 +01:00
|
|
|
static int
|
|
|
|
_compare_int(const void *x, const void *y) {
|
|
|
|
return (*(int*)x - *(int*)y);
|
|
|
|
}
|
|
|
|
|
2010-08-11 14:13:08 +02:00
|
|
|
/** Return a newly allocated string containing the exit port statistics
|
|
|
|
* until <b>now</b>, or NULL if we're not collecting exit stats. */
|
|
|
|
char *
|
2010-08-15 14:15:58 +02:00
|
|
|
rep_hist_format_exit_stats(time_t now)
|
2010-08-04 07:32:10 +02:00
|
|
|
{
|
2010-11-23 21:09:12 +01:00
|
|
|
int i, j, top_elements = 0, cur_min_idx = 0, cur_port;
|
|
|
|
uint64_t top_bytes[EXIT_STATS_TOP_N_PORTS];
|
|
|
|
int top_ports[EXIT_STATS_TOP_N_PORTS];
|
|
|
|
uint64_t cur_bytes = 0, other_read = 0, other_written = 0,
|
|
|
|
total_read = 0, total_written = 0;
|
|
|
|
uint32_t total_streams = 0, other_streams = 0;
|
2010-08-11 14:13:08 +02:00
|
|
|
char *buf;
|
|
|
|
smartlist_t *written_strings, *read_strings, *streams_strings;
|
|
|
|
char *written_string, *read_string, *streams_string;
|
2010-08-04 07:32:10 +02:00
|
|
|
char t[ISO_TIME_LEN+1];
|
2010-08-11 14:13:08 +02:00
|
|
|
char *result;
|
2010-08-04 07:32:10 +02:00
|
|
|
|
|
|
|
if (!start_of_exit_stats_interval)
|
2010-08-11 14:13:08 +02:00
|
|
|
return NULL; /* Not initialized. */
|
2010-08-04 07:32:10 +02:00
|
|
|
|
2010-11-23 21:09:12 +01:00
|
|
|
/* Go through all ports to find the n ports that saw most written and
|
2010-11-29 21:27:54 +01:00
|
|
|
* read bytes.
|
|
|
|
*
|
|
|
|
* Invariant: at the end of the loop for iteration i,
|
|
|
|
* total_read is the sum of all exit_bytes_read[0..i]
|
|
|
|
* total_written is the sum of all exit_bytes_written[0..i]
|
|
|
|
* total_stream is the sum of all exit_streams[0..i]
|
|
|
|
*
|
|
|
|
* top_elements = MAX(EXIT_STATS_TOP_N_PORTS,
|
|
|
|
* #{j | 0 <= j <= i && volume(i) > 0})
|
|
|
|
*
|
|
|
|
* For all 0 <= j < top_elements,
|
|
|
|
* top_bytes[j] > 0
|
|
|
|
* 0 <= top_ports[j] <= 65535
|
|
|
|
* top_bytes[j] = volume(top_ports[j])
|
|
|
|
*
|
|
|
|
* There is no j in 0..i and k in 0..top_elements such that:
|
|
|
|
* volume(j) > top_bytes[k] AND j is not in top_ports[0..top_elements]
|
|
|
|
*
|
|
|
|
* There is no j!=cur_min_idx in 0..top_elements such that:
|
|
|
|
* top_bytes[j] < top_bytes[cur_min_idx]
|
|
|
|
*
|
|
|
|
* where volume(x) == exit_bytes_read[x]+exit_bytes_written[x]
|
|
|
|
*
|
|
|
|
* Worst case: O(EXIT_STATS_NUM_PORTS * EXIT_STATS_TOP_N_PORTS)
|
|
|
|
*/
|
2010-08-04 07:32:10 +02:00
|
|
|
for (i = 1; i < EXIT_STATS_NUM_PORTS; i++) {
|
2010-11-23 21:09:12 +01:00
|
|
|
total_read += exit_bytes_read[i];
|
|
|
|
total_written += exit_bytes_written[i];
|
|
|
|
total_streams += exit_streams[i];
|
|
|
|
cur_bytes = exit_bytes_read[i] + exit_bytes_written[i];
|
|
|
|
if (cur_bytes == 0) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (top_elements < EXIT_STATS_TOP_N_PORTS) {
|
|
|
|
top_bytes[top_elements] = cur_bytes;
|
|
|
|
top_ports[top_elements++] = i;
|
|
|
|
} else if (cur_bytes > top_bytes[cur_min_idx]) {
|
|
|
|
top_bytes[cur_min_idx] = cur_bytes;
|
|
|
|
top_ports[cur_min_idx] = i;
|
|
|
|
} else {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
cur_min_idx = 0;
|
|
|
|
for (j = 1; j < top_elements; j++) {
|
|
|
|
if (top_bytes[j] < top_bytes[cur_min_idx]) {
|
|
|
|
cur_min_idx = j;
|
|
|
|
}
|
|
|
|
}
|
2010-08-04 07:32:10 +02:00
|
|
|
}
|
|
|
|
|
2010-11-23 21:09:12 +01:00
|
|
|
/* Add observations of top ports to smartlists. */
|
2010-08-11 14:13:08 +02:00
|
|
|
written_strings = smartlist_create();
|
|
|
|
read_strings = smartlist_create();
|
|
|
|
streams_strings = smartlist_create();
|
2010-11-23 21:09:12 +01:00
|
|
|
other_read = total_read;
|
|
|
|
other_written = total_written;
|
|
|
|
other_streams = total_streams;
|
2010-11-29 21:27:54 +01:00
|
|
|
/* Sort the ports; this puts them out of sync with top_bytes, but we
|
|
|
|
* won't be using top_bytes again anyway */
|
2010-11-23 21:09:12 +01:00
|
|
|
qsort(top_ports, top_elements, sizeof(int), _compare_int);
|
|
|
|
for (j = 0; j < top_elements; j++) {
|
|
|
|
cur_port = top_ports[j];
|
|
|
|
if (exit_bytes_written[cur_port] > 0) {
|
|
|
|
uint64_t num = round_uint64_to_next_multiple_of(
|
|
|
|
exit_bytes_written[cur_port],
|
|
|
|
EXIT_STATS_ROUND_UP_BYTES);
|
|
|
|
num /= 1024;
|
|
|
|
buf = NULL;
|
|
|
|
tor_asprintf(&buf, "%d="U64_FORMAT, cur_port, U64_PRINTF_ARG(num));
|
|
|
|
smartlist_add(written_strings, buf);
|
|
|
|
other_written -= exit_bytes_written[cur_port];
|
|
|
|
}
|
|
|
|
if (exit_bytes_read[cur_port] > 0) {
|
|
|
|
uint64_t num = round_uint64_to_next_multiple_of(
|
|
|
|
exit_bytes_read[cur_port],
|
|
|
|
EXIT_STATS_ROUND_UP_BYTES);
|
|
|
|
num /= 1024;
|
|
|
|
buf = NULL;
|
|
|
|
tor_asprintf(&buf, "%d="U64_FORMAT, cur_port, U64_PRINTF_ARG(num));
|
|
|
|
smartlist_add(read_strings, buf);
|
|
|
|
other_read -= exit_bytes_read[cur_port];
|
|
|
|
}
|
|
|
|
if (exit_streams[cur_port] > 0) {
|
|
|
|
uint32_t num = round_uint32_to_next_multiple_of(
|
|
|
|
exit_streams[cur_port],
|
|
|
|
EXIT_STATS_ROUND_UP_STREAMS);
|
|
|
|
buf = NULL;
|
|
|
|
tor_asprintf(&buf, "%d=%u", cur_port, num);
|
|
|
|
smartlist_add(streams_strings, buf);
|
|
|
|
other_streams -= exit_streams[cur_port];
|
2010-08-04 07:32:10 +02:00
|
|
|
}
|
|
|
|
}
|
2010-11-23 21:09:12 +01:00
|
|
|
|
|
|
|
/* Add observations of other ports in a single element. */
|
2010-08-11 14:13:08 +02:00
|
|
|
other_written = round_uint64_to_next_multiple_of(other_written,
|
|
|
|
EXIT_STATS_ROUND_UP_BYTES);
|
|
|
|
other_written /= 1024;
|
|
|
|
buf = NULL;
|
|
|
|
tor_asprintf(&buf, "other="U64_FORMAT, U64_PRINTF_ARG(other_written));
|
|
|
|
smartlist_add(written_strings, buf);
|
|
|
|
other_read = round_uint64_to_next_multiple_of(other_read,
|
|
|
|
EXIT_STATS_ROUND_UP_BYTES);
|
|
|
|
other_read /= 1024;
|
|
|
|
buf = NULL;
|
|
|
|
tor_asprintf(&buf, "other="U64_FORMAT, U64_PRINTF_ARG(other_read));
|
|
|
|
smartlist_add(read_strings, buf);
|
2010-08-04 07:32:10 +02:00
|
|
|
other_streams = round_uint32_to_next_multiple_of(other_streams,
|
2010-08-11 14:13:08 +02:00
|
|
|
EXIT_STATS_ROUND_UP_STREAMS);
|
|
|
|
buf = NULL;
|
|
|
|
tor_asprintf(&buf, "other=%u", other_streams);
|
|
|
|
smartlist_add(streams_strings, buf);
|
2010-11-23 21:09:12 +01:00
|
|
|
|
|
|
|
/* Join all observations in single strings. */
|
2010-08-11 14:13:08 +02:00
|
|
|
written_string = smartlist_join_strings(written_strings, ",", 0, NULL);
|
|
|
|
read_string = smartlist_join_strings(read_strings, ",", 0, NULL);
|
|
|
|
streams_string = smartlist_join_strings(streams_strings, ",", 0, NULL);
|
|
|
|
SMARTLIST_FOREACH(written_strings, char *, cp, tor_free(cp));
|
|
|
|
SMARTLIST_FOREACH(read_strings, char *, cp, tor_free(cp));
|
|
|
|
SMARTLIST_FOREACH(streams_strings, char *, cp, tor_free(cp));
|
|
|
|
smartlist_free(written_strings);
|
|
|
|
smartlist_free(read_strings);
|
|
|
|
smartlist_free(streams_strings);
|
|
|
|
|
|
|
|
/* Put everything together. */
|
|
|
|
format_iso_time(t, now);
|
|
|
|
tor_asprintf(&result, "exit-stats-end %s (%d s)\n"
|
|
|
|
"exit-kibibytes-written %s\n"
|
|
|
|
"exit-kibibytes-read %s\n"
|
|
|
|
"exit-streams-opened %s\n",
|
|
|
|
t, (unsigned) (now - start_of_exit_stats_interval),
|
|
|
|
written_string,
|
|
|
|
read_string,
|
|
|
|
streams_string);
|
2010-08-15 13:34:57 +02:00
|
|
|
tor_free(written_string);
|
|
|
|
tor_free(read_string);
|
|
|
|
tor_free(streams_string);
|
2010-08-11 14:13:08 +02:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** If 24 hours have passed since the beginning of the current exit port
|
|
|
|
* stats period, write exit stats to $DATADIR/stats/exit-stats (possibly
|
|
|
|
* overwriting an existing file) and reset counters. Return when we would
|
|
|
|
* next want to write exit stats or 0 if we never want to write. */
|
|
|
|
time_t
|
|
|
|
rep_hist_exit_stats_write(time_t now)
|
|
|
|
{
|
|
|
|
char *statsdir = NULL, *filename = NULL, *str = NULL;
|
|
|
|
|
|
|
|
if (!start_of_exit_stats_interval)
|
|
|
|
return 0; /* Not initialized. */
|
|
|
|
if (start_of_exit_stats_interval + WRITE_STATS_INTERVAL > now)
|
|
|
|
goto done; /* Not ready to write. */
|
|
|
|
|
|
|
|
log_info(LD_HIST, "Writing exit port statistics to disk.");
|
|
|
|
|
|
|
|
/* Generate history string. */
|
2010-08-15 14:15:58 +02:00
|
|
|
str = rep_hist_format_exit_stats(now);
|
2010-08-11 14:13:08 +02:00
|
|
|
|
|
|
|
/* Reset counters. */
|
|
|
|
rep_hist_reset_exit_stats(now);
|
|
|
|
|
|
|
|
/* Try to write to disk. */
|
|
|
|
statsdir = get_datadir_fname("stats");
|
|
|
|
if (check_private_dir(statsdir, CPD_CREATE) < 0) {
|
|
|
|
log_warn(LD_HIST, "Unable to create stats/ directory!");
|
2010-08-04 07:32:10 +02:00
|
|
|
goto done;
|
2010-08-11 14:13:08 +02:00
|
|
|
}
|
|
|
|
filename = get_datadir_fname2("stats", "exit-stats");
|
|
|
|
if (write_str_to_file(filename, str, 0) < 0)
|
|
|
|
log_warn(LD_HIST, "Unable to write exit port statistics to disk!");
|
2010-08-04 07:32:10 +02:00
|
|
|
|
|
|
|
done:
|
2010-08-11 14:13:08 +02:00
|
|
|
tor_free(str);
|
2010-08-04 07:32:10 +02:00
|
|
|
tor_free(statsdir);
|
2010-08-11 14:13:08 +02:00
|
|
|
tor_free(filename);
|
2010-08-04 07:32:10 +02:00
|
|
|
return start_of_exit_stats_interval + WRITE_STATS_INTERVAL;
|
|
|
|
}
|
|
|
|
|
2010-08-11 14:13:08 +02:00
|
|
|
/** Note that we wrote <b>num_written</b> bytes and read <b>num_read</b>
|
|
|
|
* bytes to/from an exit connection to <b>port</b>. */
|
2010-08-04 07:32:10 +02:00
|
|
|
void
|
2010-08-11 14:13:08 +02:00
|
|
|
rep_hist_note_exit_bytes(uint16_t port, size_t num_written,
|
|
|
|
size_t num_read)
|
2010-08-04 07:32:10 +02:00
|
|
|
{
|
2010-08-11 14:13:08 +02:00
|
|
|
if (!start_of_exit_stats_interval)
|
|
|
|
return; /* Not initialized. */
|
|
|
|
exit_bytes_written[port] += num_written;
|
|
|
|
exit_bytes_read[port] += num_read;
|
|
|
|
log_debug(LD_HIST, "Written %lu bytes and read %lu bytes to/from an "
|
|
|
|
"exit connection to port %d.",
|
|
|
|
(unsigned long)num_written, (unsigned long)num_read, port);
|
2010-08-04 07:32:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/** Note that we opened an exit stream to <b>port</b>. */
|
|
|
|
void
|
|
|
|
rep_hist_note_exit_stream_opened(uint16_t port)
|
|
|
|
{
|
2010-08-11 14:13:08 +02:00
|
|
|
if (!start_of_exit_stats_interval)
|
|
|
|
return; /* Not initialized. */
|
2010-08-04 07:32:10 +02:00
|
|
|
exit_streams[port]++;
|
|
|
|
log_debug(LD_HIST, "Opened exit stream to port %d", port);
|
2005-02-11 00:18:39 +01:00
|
|
|
}
|
2005-06-09 21:03:31 +02:00
|
|
|
|
2009-07-05 19:53:25 +02:00
|
|
|
/*** cell statistics ***/
|
|
|
|
|
2010-06-21 18:52:46 +02:00
|
|
|
/** Start of the current buffer stats interval or 0 if we're not
|
|
|
|
* collecting buffer statistics. */
|
2009-08-19 15:41:12 +02:00
|
|
|
static time_t start_of_buffer_stats_interval;
|
|
|
|
|
|
|
|
/** Initialize buffer stats. */
|
|
|
|
void
|
|
|
|
rep_hist_buffer_stats_init(time_t now)
|
|
|
|
{
|
|
|
|
start_of_buffer_stats_interval = now;
|
|
|
|
}
|
2009-07-05 19:53:25 +02:00
|
|
|
|
|
|
|
typedef struct circ_buffer_stats_t {
|
|
|
|
uint32_t processed_cells;
|
|
|
|
double mean_num_cells_in_queue;
|
|
|
|
double mean_time_cells_in_queue;
|
|
|
|
} circ_buffer_stats_t;
|
|
|
|
|
|
|
|
/** Holds stats. */
|
|
|
|
smartlist_t *circuits_for_buffer_stats = NULL;
|
|
|
|
|
|
|
|
/** Remember cell statistics for circuit <b>circ</b> at time
|
|
|
|
* <b>end_of_interval</b> and reset cell counters in case the circuit
|
|
|
|
* remains open in the next measurement interval. */
|
|
|
|
void
|
2009-08-19 15:41:12 +02:00
|
|
|
rep_hist_buffer_stats_add_circ(circuit_t *circ, time_t end_of_interval)
|
2009-07-05 19:53:25 +02:00
|
|
|
{
|
|
|
|
circ_buffer_stats_t *stat;
|
|
|
|
time_t start_of_interval;
|
|
|
|
int interval_length;
|
|
|
|
or_circuit_t *orcirc;
|
|
|
|
if (CIRCUIT_IS_ORIGIN(circ))
|
|
|
|
return;
|
|
|
|
orcirc = TO_OR_CIRCUIT(circ);
|
|
|
|
if (!orcirc->processed_cells)
|
|
|
|
return;
|
|
|
|
if (!circuits_for_buffer_stats)
|
|
|
|
circuits_for_buffer_stats = smartlist_create();
|
2010-10-15 18:35:55 +02:00
|
|
|
start_of_interval = (circ->timestamp_created.tv_sec >
|
|
|
|
start_of_buffer_stats_interval) ?
|
|
|
|
circ->timestamp_created.tv_sec :
|
2009-07-05 19:53:25 +02:00
|
|
|
start_of_buffer_stats_interval;
|
|
|
|
interval_length = (int) (end_of_interval - start_of_interval);
|
|
|
|
stat = tor_malloc_zero(sizeof(circ_buffer_stats_t));
|
|
|
|
stat->processed_cells = orcirc->processed_cells;
|
|
|
|
/* 1000.0 for s -> ms; 2.0 because of app-ward and exit-ward queues */
|
|
|
|
stat->mean_num_cells_in_queue = interval_length == 0 ? 0.0 :
|
|
|
|
(double) orcirc->total_cell_waiting_time /
|
|
|
|
(double) interval_length / 1000.0 / 2.0;
|
|
|
|
stat->mean_time_cells_in_queue =
|
|
|
|
(double) orcirc->total_cell_waiting_time /
|
|
|
|
(double) orcirc->processed_cells;
|
|
|
|
smartlist_add(circuits_for_buffer_stats, stat);
|
|
|
|
orcirc->total_cell_waiting_time = 0;
|
|
|
|
orcirc->processed_cells = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Sorting helper: return -1, 1, or 0 based on comparison of two
|
|
|
|
* circ_buffer_stats_t */
|
|
|
|
static int
|
|
|
|
_buffer_stats_compare_entries(const void **_a, const void **_b)
|
|
|
|
{
|
|
|
|
const circ_buffer_stats_t *a = *_a, *b = *_b;
|
|
|
|
if (a->processed_cells < b->processed_cells)
|
|
|
|
return 1;
|
|
|
|
else if (a->processed_cells > b->processed_cells)
|
|
|
|
return -1;
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-06-21 18:52:46 +02:00
|
|
|
/** Stop collecting cell stats in a way that we can re-start doing so in
|
|
|
|
* rep_hist_buffer_stats_init(). */
|
2009-07-05 19:53:25 +02:00
|
|
|
void
|
2010-06-21 18:52:46 +02:00
|
|
|
rep_hist_buffer_stats_term(void)
|
|
|
|
{
|
|
|
|
start_of_buffer_stats_interval = 0;
|
|
|
|
if (!circuits_for_buffer_stats)
|
|
|
|
circuits_for_buffer_stats = smartlist_create();
|
|
|
|
SMARTLIST_FOREACH(circuits_for_buffer_stats, circ_buffer_stats_t *,
|
|
|
|
stat, tor_free(stat));
|
|
|
|
smartlist_clear(circuits_for_buffer_stats);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Write buffer statistics to $DATADIR/stats/buffer-stats and return when
|
|
|
|
* we would next want to write exit stats. */
|
|
|
|
time_t
|
2009-08-19 15:41:12 +02:00
|
|
|
rep_hist_buffer_stats_write(time_t now)
|
2009-07-05 19:53:25 +02:00
|
|
|
{
|
2009-08-19 17:10:40 +02:00
|
|
|
char *statsdir = NULL, *filename = NULL;
|
2009-07-05 19:53:25 +02:00
|
|
|
char written[ISO_TIME_LEN+1];
|
|
|
|
open_file_t *open_file = NULL;
|
|
|
|
FILE *out;
|
|
|
|
#define SHARES 10
|
|
|
|
int processed_cells[SHARES], circs_in_share[SHARES],
|
|
|
|
number_of_circuits, i;
|
|
|
|
double queued_cells[SHARES], time_in_queue[SHARES];
|
|
|
|
smartlist_t *str_build = smartlist_create();
|
2010-03-01 03:46:50 +01:00
|
|
|
char *str = NULL, *buf=NULL;
|
2009-07-05 19:53:25 +02:00
|
|
|
circuit_t *circ;
|
2010-06-21 18:52:46 +02:00
|
|
|
|
|
|
|
if (!start_of_buffer_stats_interval)
|
|
|
|
return 0; /* Not initialized. */
|
|
|
|
if (start_of_buffer_stats_interval + WRITE_STATS_INTERVAL > now)
|
|
|
|
goto done; /* Not ready to write */
|
|
|
|
|
2009-07-05 19:53:25 +02:00
|
|
|
/* add current circuits to stats */
|
|
|
|
for (circ = _circuit_get_global_list(); circ; circ = circ->next)
|
2009-08-19 15:41:12 +02:00
|
|
|
rep_hist_buffer_stats_add_circ(circ, now);
|
2009-07-05 19:53:25 +02:00
|
|
|
/* calculate deciles */
|
|
|
|
memset(processed_cells, 0, SHARES * sizeof(int));
|
|
|
|
memset(circs_in_share, 0, SHARES * sizeof(int));
|
|
|
|
memset(queued_cells, 0, SHARES * sizeof(double));
|
|
|
|
memset(time_in_queue, 0, SHARES * sizeof(double));
|
2009-12-03 10:51:51 +01:00
|
|
|
if (!circuits_for_buffer_stats)
|
|
|
|
circuits_for_buffer_stats = smartlist_create();
|
2009-07-05 19:53:25 +02:00
|
|
|
smartlist_sort(circuits_for_buffer_stats,
|
|
|
|
_buffer_stats_compare_entries);
|
|
|
|
number_of_circuits = smartlist_len(circuits_for_buffer_stats);
|
2009-12-03 10:51:51 +01:00
|
|
|
if (number_of_circuits < 1) {
|
|
|
|
log_info(LD_HIST, "Attempt to write cell statistics to disk failed. "
|
|
|
|
"We haven't seen a single circuit to report about.");
|
|
|
|
goto done;
|
|
|
|
}
|
2009-07-05 19:53:25 +02:00
|
|
|
i = 0;
|
|
|
|
SMARTLIST_FOREACH_BEGIN(circuits_for_buffer_stats,
|
|
|
|
circ_buffer_stats_t *, stat)
|
|
|
|
{
|
|
|
|
int share = i++ * SHARES / number_of_circuits;
|
|
|
|
processed_cells[share] += stat->processed_cells;
|
|
|
|
queued_cells[share] += stat->mean_num_cells_in_queue;
|
|
|
|
time_in_queue[share] += stat->mean_time_cells_in_queue;
|
|
|
|
circs_in_share[share]++;
|
|
|
|
}
|
|
|
|
SMARTLIST_FOREACH_END(stat);
|
|
|
|
/* clear buffer stats history */
|
|
|
|
SMARTLIST_FOREACH(circuits_for_buffer_stats, circ_buffer_stats_t *,
|
|
|
|
stat, tor_free(stat));
|
|
|
|
smartlist_clear(circuits_for_buffer_stats);
|
|
|
|
/* write to file */
|
2009-08-19 17:10:40 +02:00
|
|
|
statsdir = get_datadir_fname("stats");
|
|
|
|
if (check_private_dir(statsdir, CPD_CREATE) < 0)
|
|
|
|
goto done;
|
2009-12-17 11:20:31 +01:00
|
|
|
filename = get_datadir_fname2("stats", "buffer-stats");
|
2009-07-05 19:53:25 +02:00
|
|
|
out = start_writing_to_stdio_file(filename, OPEN_FLAGS_APPEND,
|
|
|
|
0600, &open_file);
|
|
|
|
if (!out)
|
|
|
|
goto done;
|
|
|
|
format_iso_time(written, now);
|
2009-08-14 15:30:24 +02:00
|
|
|
if (fprintf(out, "cell-stats-end %s (%d s)\n", written,
|
2009-08-19 15:41:12 +02:00
|
|
|
(unsigned) (now - start_of_buffer_stats_interval)) < 0)
|
2009-07-05 19:53:25 +02:00
|
|
|
goto done;
|
|
|
|
for (i = 0; i < SHARES; i++) {
|
2010-03-01 03:46:50 +01:00
|
|
|
tor_asprintf(&buf,"%d", !circs_in_share[i] ? 0 :
|
2009-07-05 19:53:25 +02:00
|
|
|
processed_cells[i] / circs_in_share[i]);
|
2010-03-01 03:46:50 +01:00
|
|
|
smartlist_add(str_build, buf);
|
2009-07-05 19:53:25 +02:00
|
|
|
}
|
|
|
|
str = smartlist_join_strings(str_build, ",", 0, NULL);
|
2009-08-14 15:30:24 +02:00
|
|
|
if (fprintf(out, "cell-processed-cells %s\n", str) < 0)
|
2009-07-05 19:53:25 +02:00
|
|
|
goto done;
|
|
|
|
tor_free(str);
|
|
|
|
SMARTLIST_FOREACH(str_build, char *, c, tor_free(c));
|
|
|
|
smartlist_clear(str_build);
|
|
|
|
for (i = 0; i < SHARES; i++) {
|
2010-03-01 03:46:50 +01:00
|
|
|
tor_asprintf(&buf, "%.2f", circs_in_share[i] == 0 ? 0.0 :
|
2009-07-05 19:53:25 +02:00
|
|
|
queued_cells[i] / (double) circs_in_share[i]);
|
2010-03-01 03:46:50 +01:00
|
|
|
smartlist_add(str_build, buf);
|
2009-07-05 19:53:25 +02:00
|
|
|
}
|
|
|
|
str = smartlist_join_strings(str_build, ",", 0, NULL);
|
2009-08-14 15:30:24 +02:00
|
|
|
if (fprintf(out, "cell-queued-cells %s\n", str) < 0)
|
2009-07-05 19:53:25 +02:00
|
|
|
goto done;
|
|
|
|
tor_free(str);
|
|
|
|
SMARTLIST_FOREACH(str_build, char *, c, tor_free(c));
|
|
|
|
smartlist_clear(str_build);
|
|
|
|
for (i = 0; i < SHARES; i++) {
|
2010-03-01 03:46:50 +01:00
|
|
|
tor_asprintf(&buf, "%.0f", circs_in_share[i] == 0 ? 0.0 :
|
2009-07-05 19:53:25 +02:00
|
|
|
time_in_queue[i] / (double) circs_in_share[i]);
|
2010-03-01 03:46:50 +01:00
|
|
|
smartlist_add(str_build, buf);
|
2009-07-05 19:53:25 +02:00
|
|
|
}
|
|
|
|
str = smartlist_join_strings(str_build, ",", 0, NULL);
|
2009-08-14 15:30:24 +02:00
|
|
|
if (fprintf(out, "cell-time-in-queue %s\n", str) < 0)
|
2009-07-05 19:53:25 +02:00
|
|
|
goto done;
|
|
|
|
tor_free(str);
|
|
|
|
SMARTLIST_FOREACH(str_build, char *, c, tor_free(c));
|
|
|
|
smartlist_free(str_build);
|
|
|
|
str_build = NULL;
|
2009-08-14 15:30:24 +02:00
|
|
|
if (fprintf(out, "cell-circuits-per-decile %d\n",
|
2009-07-05 19:53:25 +02:00
|
|
|
(number_of_circuits + SHARES - 1) / SHARES) < 0)
|
|
|
|
goto done;
|
|
|
|
finish_writing_to_file(open_file);
|
|
|
|
open_file = NULL;
|
2009-09-24 21:58:56 +02:00
|
|
|
start_of_buffer_stats_interval = now;
|
2009-07-05 19:53:25 +02:00
|
|
|
done:
|
|
|
|
if (open_file)
|
|
|
|
abort_writing_to_file(open_file);
|
|
|
|
tor_free(filename);
|
2009-08-19 17:10:40 +02:00
|
|
|
tor_free(statsdir);
|
2009-07-05 19:53:25 +02:00
|
|
|
if (str_build) {
|
|
|
|
SMARTLIST_FOREACH(str_build, char *, c, tor_free(c));
|
|
|
|
smartlist_free(str_build);
|
|
|
|
}
|
|
|
|
tor_free(str);
|
|
|
|
#undef SHARES
|
2010-06-21 18:52:46 +02:00
|
|
|
return start_of_buffer_stats_interval + WRITE_STATS_INTERVAL;
|
2009-07-05 19:53:25 +02:00
|
|
|
}
|
|
|
|
|
2010-08-02 15:06:14 +02:00
|
|
|
/*** Connection statistics ***/
|
|
|
|
|
|
|
|
/** Start of the current connection stats interval or 0 if we're not
|
|
|
|
* collecting connection statistics. */
|
|
|
|
static time_t start_of_conn_stats_interval;
|
|
|
|
|
|
|
|
/** Initialize connection stats. */
|
|
|
|
void
|
|
|
|
rep_hist_conn_stats_init(time_t now)
|
|
|
|
{
|
|
|
|
start_of_conn_stats_interval = now;
|
|
|
|
}
|
|
|
|
|
2010-08-15 15:04:33 +02:00
|
|
|
/* Count connections that we read and wrote less than these many bytes
|
|
|
|
* from/to as below threshold. */
|
2010-08-02 15:06:14 +02:00
|
|
|
#define BIDI_THRESHOLD 20480
|
|
|
|
|
2010-08-15 15:04:33 +02:00
|
|
|
/* Count connections that we read or wrote at least this factor as many
|
|
|
|
* bytes from/to than we wrote or read to/from as mostly reading or
|
|
|
|
* writing. */
|
2010-08-02 15:06:14 +02:00
|
|
|
#define BIDI_FACTOR 10
|
|
|
|
|
2010-08-15 15:04:33 +02:00
|
|
|
/* Interval length in seconds for considering read and written bytes for
|
|
|
|
* connection stats. */
|
2010-08-02 15:06:14 +02:00
|
|
|
#define BIDI_INTERVAL 10
|
|
|
|
|
|
|
|
/* Start of next BIDI_INTERVAL second interval. */
|
|
|
|
static time_t bidi_next_interval = 0;
|
|
|
|
|
|
|
|
/* Number of connections that we read and wrote less than BIDI_THRESHOLD
|
|
|
|
* bytes from/to in BIDI_INTERVAL seconds. */
|
|
|
|
static uint32_t below_threshold = 0;
|
|
|
|
|
|
|
|
/* Number of connections that we read at least BIDI_FACTOR times more
|
|
|
|
* bytes from than we wrote to in BIDI_INTERVAL seconds. */
|
|
|
|
static uint32_t mostly_read = 0;
|
|
|
|
|
|
|
|
/* Number of connections that we wrote at least BIDI_FACTOR times more
|
|
|
|
* bytes to than we read from in BIDI_INTERVAL seconds. */
|
|
|
|
static uint32_t mostly_written = 0;
|
|
|
|
|
|
|
|
/* Number of connections that we read and wrote at least BIDI_THRESHOLD
|
|
|
|
* bytes from/to, but not BIDI_FACTOR times more in either direction in
|
|
|
|
* BIDI_INTERVAL seconds. */
|
|
|
|
static uint32_t both_read_and_written = 0;
|
|
|
|
|
|
|
|
/* Entry in a map from connection ID to the number of read and written
|
|
|
|
* bytes on this connection in a BIDI_INTERVAL second interval. */
|
|
|
|
typedef struct bidi_map_entry_t {
|
|
|
|
HT_ENTRY(bidi_map_entry_t) node;
|
|
|
|
uint64_t conn_id; /**< Connection ID */
|
|
|
|
size_t read; /**< Number of read bytes */
|
|
|
|
size_t written; /**< Number of written bytes */
|
|
|
|
} bidi_map_entry_t;
|
|
|
|
|
|
|
|
/** Map of OR connections together with the number of read and written
|
|
|
|
* bytes in the current BIDI_INTERVAL second interval. */
|
|
|
|
static HT_HEAD(bidimap, bidi_map_entry_t) bidi_map =
|
|
|
|
HT_INITIALIZER();
|
|
|
|
|
|
|
|
static int
|
|
|
|
bidi_map_ent_eq(const bidi_map_entry_t *a, const bidi_map_entry_t *b)
|
|
|
|
{
|
|
|
|
return a->conn_id == b->conn_id;
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned
|
|
|
|
bidi_map_ent_hash(const bidi_map_entry_t *entry)
|
|
|
|
{
|
|
|
|
return (unsigned) entry->conn_id;
|
|
|
|
}
|
|
|
|
|
|
|
|
HT_PROTOTYPE(bidimap, bidi_map_entry_t, node, bidi_map_ent_hash,
|
|
|
|
bidi_map_ent_eq);
|
|
|
|
HT_GENERATE(bidimap, bidi_map_entry_t, node, bidi_map_ent_hash,
|
|
|
|
bidi_map_ent_eq, 0.6, malloc, realloc, free);
|
|
|
|
|
|
|
|
static void
|
|
|
|
bidi_map_free(void)
|
|
|
|
{
|
|
|
|
bidi_map_entry_t **ptr, **next, *ent;
|
|
|
|
for (ptr = HT_START(bidimap, &bidi_map); ptr; ptr = next) {
|
|
|
|
ent = *ptr;
|
|
|
|
next = HT_NEXT_RMV(bidimap, &bidi_map, ptr);
|
|
|
|
tor_free(ent);
|
|
|
|
}
|
|
|
|
HT_CLEAR(bidimap, &bidi_map);
|
|
|
|
}
|
|
|
|
|
2010-08-18 15:44:02 +02:00
|
|
|
/** Reset counters for conn statistics. */
|
2010-08-02 15:06:14 +02:00
|
|
|
void
|
2010-08-18 15:44:02 +02:00
|
|
|
rep_hist_reset_conn_stats(time_t now)
|
2010-08-02 15:06:14 +02:00
|
|
|
{
|
2010-08-18 15:44:02 +02:00
|
|
|
start_of_conn_stats_interval = now;
|
2010-08-02 15:06:14 +02:00
|
|
|
below_threshold = 0;
|
|
|
|
mostly_read = 0;
|
|
|
|
mostly_written = 0;
|
|
|
|
both_read_and_written = 0;
|
|
|
|
bidi_map_free();
|
|
|
|
}
|
|
|
|
|
2010-08-18 15:44:02 +02:00
|
|
|
/** Stop collecting connection stats in a way that we can re-start doing
|
|
|
|
* so in rep_hist_conn_stats_init(). */
|
|
|
|
void
|
|
|
|
rep_hist_conn_stats_term(void)
|
|
|
|
{
|
|
|
|
rep_hist_reset_conn_stats(0);
|
|
|
|
}
|
2010-08-02 15:06:14 +02:00
|
|
|
|
|
|
|
/** We read <b>num_read</b> bytes and wrote <b>num_written</b> from/to OR
|
|
|
|
* connection <b>conn_id</b> in second <b>when</b>. If this is the first
|
|
|
|
* observation in a new interval, sum up the last observations. Add bytes
|
|
|
|
* for this connection. */
|
|
|
|
void
|
|
|
|
rep_hist_note_or_conn_bytes(uint64_t conn_id, size_t num_read,
|
|
|
|
size_t num_written, time_t when)
|
|
|
|
{
|
|
|
|
if (!start_of_conn_stats_interval)
|
|
|
|
return;
|
|
|
|
/* Initialize */
|
|
|
|
if (bidi_next_interval == 0)
|
|
|
|
bidi_next_interval = when + BIDI_INTERVAL;
|
|
|
|
/* Sum up last period's statistics */
|
|
|
|
if (when >= bidi_next_interval) {
|
|
|
|
bidi_map_entry_t **ptr, **next, *ent;
|
|
|
|
for (ptr = HT_START(bidimap, &bidi_map); ptr; ptr = next) {
|
|
|
|
ent = *ptr;
|
|
|
|
if (ent->read + ent->written < BIDI_THRESHOLD)
|
|
|
|
below_threshold++;
|
|
|
|
else if (ent->read >= ent->written * BIDI_FACTOR)
|
|
|
|
mostly_read++;
|
|
|
|
else if (ent->written >= ent->read * BIDI_FACTOR)
|
|
|
|
mostly_written++;
|
|
|
|
else
|
|
|
|
both_read_and_written++;
|
|
|
|
next = HT_NEXT_RMV(bidimap, &bidi_map, ptr);
|
|
|
|
tor_free(ent);
|
|
|
|
}
|
|
|
|
while (when >= bidi_next_interval)
|
|
|
|
bidi_next_interval += BIDI_INTERVAL;
|
|
|
|
log_info(LD_GENERAL, "%d below threshold, %d mostly read, "
|
|
|
|
"%d mostly written, %d both read and written.",
|
|
|
|
below_threshold, mostly_read, mostly_written,
|
|
|
|
both_read_and_written);
|
|
|
|
}
|
|
|
|
/* Add this connection's bytes. */
|
|
|
|
if (num_read > 0 || num_written > 0) {
|
|
|
|
bidi_map_entry_t *entry, lookup;
|
|
|
|
lookup.conn_id = conn_id;
|
|
|
|
entry = HT_FIND(bidimap, &bidi_map, &lookup);
|
|
|
|
if (entry) {
|
|
|
|
entry->written += num_written;
|
|
|
|
entry->read += num_read;
|
|
|
|
} else {
|
|
|
|
entry = tor_malloc_zero(sizeof(bidi_map_entry_t));
|
|
|
|
entry->conn_id = conn_id;
|
|
|
|
entry->written = num_written;
|
|
|
|
entry->read = num_read;
|
|
|
|
HT_INSERT(bidimap, &bidi_map, entry);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-08-18 15:44:02 +02:00
|
|
|
/** Return a newly allocated string containing the connection statistics
|
|
|
|
* until <b>now</b>, or NULL if we're not collecting conn stats. */
|
|
|
|
char *
|
|
|
|
rep_hist_format_conn_stats(time_t now)
|
|
|
|
{
|
|
|
|
char *result, written[ISO_TIME_LEN+1];
|
|
|
|
|
|
|
|
if (!start_of_conn_stats_interval)
|
|
|
|
return NULL; /* Not initialized. */
|
|
|
|
|
|
|
|
format_iso_time(written, now);
|
2010-08-24 08:11:17 +02:00
|
|
|
tor_asprintf(&result, "conn-bi-direct %s (%d s) %d,%d,%d,%d\n",
|
2010-08-18 15:44:02 +02:00
|
|
|
written,
|
|
|
|
(unsigned) (now - start_of_conn_stats_interval),
|
|
|
|
below_threshold,
|
|
|
|
mostly_read,
|
|
|
|
mostly_written,
|
|
|
|
both_read_and_written);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** If 24 hours have passed since the beginning of the current conn stats
|
|
|
|
* period, write conn stats to $DATADIR/stats/conn-stats (possibly
|
|
|
|
* overwriting an existing file) and reset counters. Return when we would
|
|
|
|
* next want to write conn stats or 0 if we never want to write. */
|
2010-08-02 15:06:14 +02:00
|
|
|
time_t
|
|
|
|
rep_hist_conn_stats_write(time_t now)
|
|
|
|
{
|
2010-08-18 15:44:02 +02:00
|
|
|
char *statsdir = NULL, *filename = NULL, *str = NULL;
|
2010-08-02 15:06:14 +02:00
|
|
|
|
|
|
|
if (!start_of_conn_stats_interval)
|
|
|
|
return 0; /* Not initialized. */
|
|
|
|
if (start_of_conn_stats_interval + WRITE_STATS_INTERVAL > now)
|
|
|
|
goto done; /* Not ready to write */
|
|
|
|
|
2010-08-18 15:44:02 +02:00
|
|
|
/* Generate history string. */
|
|
|
|
str = rep_hist_format_conn_stats(now);
|
|
|
|
|
|
|
|
/* Reset counters. */
|
|
|
|
rep_hist_reset_conn_stats(now);
|
|
|
|
|
|
|
|
/* Try to write to disk. */
|
2010-08-02 15:06:14 +02:00
|
|
|
statsdir = get_datadir_fname("stats");
|
2010-08-18 15:44:02 +02:00
|
|
|
if (check_private_dir(statsdir, CPD_CREATE) < 0) {
|
|
|
|
log_warn(LD_HIST, "Unable to create stats/ directory!");
|
2010-08-02 15:06:14 +02:00
|
|
|
goto done;
|
2010-08-18 15:44:02 +02:00
|
|
|
}
|
2010-08-02 15:06:14 +02:00
|
|
|
filename = get_datadir_fname2("stats", "conn-stats");
|
2010-08-18 15:44:02 +02:00
|
|
|
if (write_str_to_file(filename, str, 0) < 0)
|
|
|
|
log_warn(LD_HIST, "Unable to write conn stats to disk!");
|
2010-08-02 15:06:14 +02:00
|
|
|
|
|
|
|
done:
|
2010-08-18 15:44:02 +02:00
|
|
|
tor_free(str);
|
2010-08-02 15:06:14 +02:00
|
|
|
tor_free(filename);
|
|
|
|
tor_free(statsdir);
|
|
|
|
return start_of_conn_stats_interval + WRITE_STATS_INTERVAL;
|
|
|
|
}
|
|
|
|
|
2010-08-04 07:32:10 +02:00
|
|
|
/** Free all storage held by the OR/link history caches, by the
|
|
|
|
* bandwidth history arrays, by the port history, or by statistics . */
|
|
|
|
void
|
|
|
|
rep_hist_free_all(void)
|
|
|
|
{
|
|
|
|
digestmap_free(history_map, free_or_history);
|
|
|
|
tor_free(read_array);
|
|
|
|
tor_free(write_array);
|
|
|
|
tor_free(last_stability_doc);
|
|
|
|
tor_free(exit_bytes_read);
|
|
|
|
tor_free(exit_bytes_written);
|
|
|
|
tor_free(exit_streams);
|
|
|
|
built_last_stability_doc_at = 0;
|
|
|
|
predicted_ports_free();
|
2010-08-02 15:06:14 +02:00
|
|
|
bidi_map_free();
|
2010-08-04 07:32:10 +02:00
|
|
|
}
|
|
|
|
|