mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-24 04:13:28 +01:00
checkpoint: start working on bandwidth tracking and
letting clients evaluate whether they're suitable servers svn:r2037
This commit is contained in:
parent
017d7d1fb3
commit
86e0ede07e
@ -193,6 +193,7 @@ static int config_assign(or_options_t *options, struct config_line_t *list) {
|
|||||||
config_compare(list, "BandwidthRate", CONFIG_TYPE_INT, &options->BandwidthRate) ||
|
config_compare(list, "BandwidthRate", CONFIG_TYPE_INT, &options->BandwidthRate) ||
|
||||||
config_compare(list, "BandwidthBurst", CONFIG_TYPE_INT, &options->BandwidthBurst) ||
|
config_compare(list, "BandwidthBurst", CONFIG_TYPE_INT, &options->BandwidthBurst) ||
|
||||||
|
|
||||||
|
config_compare(list, "ClientOnly", CONFIG_TYPE_BOOL, &options->ClientOnly) ||
|
||||||
config_compare(list, "ContactInfo", CONFIG_TYPE_STRING, &options->ContactInfo) ||
|
config_compare(list, "ContactInfo", CONFIG_TYPE_STRING, &options->ContactInfo) ||
|
||||||
|
|
||||||
config_compare(list, "DebugLogFile", CONFIG_TYPE_STRING, &options->DebugLogFile) ||
|
config_compare(list, "DebugLogFile", CONFIG_TYPE_STRING, &options->DebugLogFile) ||
|
||||||
@ -678,6 +679,10 @@ int getconfig(int argc, char **argv, or_options_t *options) {
|
|||||||
result = -1;
|
result = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* XXX008 if AuthDir and !ORPort then fail */
|
||||||
|
|
||||||
|
/* XXX008 if AuthDir and ClientOnly then fail */
|
||||||
|
|
||||||
if(options->SocksPort > 1 &&
|
if(options->SocksPort > 1 &&
|
||||||
(options->PathlenCoinWeight < 0.0 || options->PathlenCoinWeight >= 1.0)) {
|
(options->PathlenCoinWeight < 0.0 || options->PathlenCoinWeight >= 1.0)) {
|
||||||
log(LOG_WARN,"PathlenCoinWeight option must be >=0.0 and <1.0.");
|
log(LOG_WARN,"PathlenCoinWeight option must be >=0.0 and <1.0.");
|
||||||
|
@ -820,10 +820,12 @@ int connection_outbuf_too_full(connection_t *conn) {
|
|||||||
*/
|
*/
|
||||||
int connection_handle_write(connection_t *conn) {
|
int connection_handle_write(connection_t *conn) {
|
||||||
int e, len=sizeof(e);
|
int e, len=sizeof(e);
|
||||||
|
int result;
|
||||||
|
time_t now = time(NULL);
|
||||||
|
|
||||||
tor_assert(!connection_is_listener(conn));
|
tor_assert(!connection_is_listener(conn));
|
||||||
|
|
||||||
conn->timestamp_lastwritten = time(NULL);
|
conn->timestamp_lastwritten = now;
|
||||||
|
|
||||||
/* Sometimes, "writeable" means "connected". */
|
/* Sometimes, "writeable" means "connected". */
|
||||||
if (connection_state_is_connecting(conn)) {
|
if (connection_state_is_connecting(conn)) {
|
||||||
@ -859,7 +861,8 @@ int connection_handle_write(connection_t *conn) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* else open, or closing */
|
/* else open, or closing */
|
||||||
switch(flush_buf_tls(conn->tls, conn->outbuf, &conn->outbuf_flushlen)) {
|
result = flush_buf_tls(conn->tls, conn->outbuf, &conn->outbuf_flushlen);
|
||||||
|
switch(result) {
|
||||||
case TOR_TLS_ERROR:
|
case TOR_TLS_ERROR:
|
||||||
case TOR_TLS_CLOSE:
|
case TOR_TLS_CLOSE:
|
||||||
log_fn(LOG_INFO,"tls error. breaking.");
|
log_fn(LOG_INFO,"tls error. breaking.");
|
||||||
@ -888,7 +891,8 @@ int connection_handle_write(connection_t *conn) {
|
|||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (flush_buf(conn->s, conn->outbuf, &conn->outbuf_flushlen) < 0) {
|
result = flush_buf(conn->s, conn->outbuf, &conn->outbuf_flushlen);
|
||||||
|
if (result < 0) {
|
||||||
connection_close_immediate(conn); /* Don't flush; connection is dead. */
|
connection_close_immediate(conn); /* Don't flush; connection is dead. */
|
||||||
conn->has_sent_end = 1;
|
conn->has_sent_end = 1;
|
||||||
connection_mark_for_close(conn);
|
connection_mark_for_close(conn);
|
||||||
@ -896,6 +900,10 @@ int connection_handle_write(connection_t *conn) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(result > 0) { /* remember it */
|
||||||
|
rep_hist_note_bytes_written(result, now);
|
||||||
|
}
|
||||||
|
|
||||||
if(!connection_wants_to_flush(conn)) { /* it's done flushing */
|
if(!connection_wants_to_flush(conn)) { /* it's done flushing */
|
||||||
if(connection_finished_flushing(conn) < 0) {
|
if(connection_finished_flushing(conn) < 0) {
|
||||||
/* already marked */
|
/* already marked */
|
||||||
|
@ -366,16 +366,14 @@ static void run_connection_housekeeping(int i, time_t now) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* check connections to see whether we should send a keepalive, expire, or wait */
|
|
||||||
if(!connection_speaks_cells(conn))
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* If we haven't written to an OR connection for a while, then either nuke
|
/* If we haven't written to an OR connection for a while, then either nuke
|
||||||
the connection or send a keepalive, depending. */
|
the connection or send a keepalive, depending. */
|
||||||
if(now >= conn->timestamp_lastwritten + options.KeepalivePeriod) {
|
if(connection_speaks_cells(conn) &&
|
||||||
|
now >= conn->timestamp_lastwritten + options.KeepalivePeriod) {
|
||||||
if((!options.ORPort && !circuit_get_by_conn(conn)) ||
|
if((!options.ORPort && !circuit_get_by_conn(conn)) ||
|
||||||
(!connection_state_is_open(conn))) {
|
(!connection_state_is_open(conn))) {
|
||||||
/* we're an onion proxy, with no circuits; or our handshake has expired. kill it. */
|
/* we're an onion proxy, with no circuits;
|
||||||
|
* or our handshake has expired. kill it. */
|
||||||
log_fn(LOG_INFO,"Expiring connection to %d (%s:%d).",
|
log_fn(LOG_INFO,"Expiring connection to %d (%s:%d).",
|
||||||
i,conn->address, conn->port);
|
i,conn->address, conn->port);
|
||||||
/* flush anything waiting, e.g. a destroy for a just-expired circ */
|
/* flush anything waiting, e.g. a destroy for a just-expired circ */
|
||||||
@ -392,6 +390,36 @@ static void run_connection_housekeeping(int i, time_t now) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define MIN_BW_TO_PUBLISH_DESC 5000 /* 5000 bytes sustained */
|
||||||
|
#define MIN_UPTIME_TO_PUBLISH_DESC (30*60) /* half an hour */
|
||||||
|
|
||||||
|
/** Decide if we're a server or just a client. We are a server if:
|
||||||
|
* - We have the AuthoritativeDirectory option set.
|
||||||
|
* or
|
||||||
|
* - We don't have the ClientOnly option set; and
|
||||||
|
* - We have ORPort set; and
|
||||||
|
* - We have been up for at least MIN_UPTIME_TO_PUBLISH_DESC seconds; and
|
||||||
|
* - We have processed some suitable minimum bandwidth recently; and
|
||||||
|
* - We believe we are reachable from the outside.
|
||||||
|
*/
|
||||||
|
static int decide_if_server(time_t now) {
|
||||||
|
|
||||||
|
if(options.AuthoritativeDir)
|
||||||
|
return 1;
|
||||||
|
if(options.ClientOnly)
|
||||||
|
return 0;
|
||||||
|
if(!options.ORPort)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* here, determine if we're reachable */
|
||||||
|
|
||||||
|
if(stats_n_seconds_uptime < MIN_UPTIME_TO_PUBLISH_DESC)
|
||||||
|
return 0;
|
||||||
|
if(rep_hist_bandwidth_assess(now) < MIN_BW_TO_PUBLISH_DESC)
|
||||||
|
return 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
/** Perform regular maintenance tasks. This function gets run once per
|
/** Perform regular maintenance tasks. This function gets run once per
|
||||||
* second by prepare_for_poll.
|
* second by prepare_for_poll.
|
||||||
*/
|
*/
|
||||||
@ -401,7 +429,6 @@ static void run_scheduled_events(time_t now) {
|
|||||||
static time_t last_rotated_certificate = 0;
|
static time_t last_rotated_certificate = 0;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
|
||||||
/** 1a. Every MIN_ONION_KEY_LIFETIME seconds, rotate the onion keys,
|
/** 1a. Every MIN_ONION_KEY_LIFETIME seconds, rotate the onion keys,
|
||||||
* shut down and restart all cpuworkers, and update the directory if
|
* shut down and restart all cpuworkers, and update the directory if
|
||||||
* necessary.
|
* necessary.
|
||||||
@ -430,37 +457,42 @@ static void run_scheduled_events(time_t now) {
|
|||||||
* XXXX them at all. */
|
* XXXX them at all. */
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 1c. Every DirFetchPostPeriod seconds, we get a new directory and upload
|
/** 2. Every DirFetchPostPeriod seconds, we get a new directory and upload
|
||||||
* our descriptor (if any). */
|
* our descriptor (if we've passed our internal checks). */
|
||||||
if(time_to_fetch_directory < now) {
|
if(time_to_fetch_directory < now) {
|
||||||
/* it's time to fetch a new directory and/or post our descriptor */
|
|
||||||
if(options.ORPort) {
|
if(decide_if_server(now)) {
|
||||||
router_rebuild_descriptor();
|
router_rebuild_descriptor();
|
||||||
router_upload_dir_desc_to_dirservers();
|
router_upload_dir_desc_to_dirservers();
|
||||||
}
|
}
|
||||||
|
|
||||||
routerlist_remove_old_routers(); /* purge obsolete entries */
|
routerlist_remove_old_routers(); /* purge obsolete entries */
|
||||||
|
|
||||||
if(options.AuthoritativeDir) {
|
if(options.AuthoritativeDir) {
|
||||||
/* We're a directory; dump any old descriptors. */
|
/* We're a directory; dump any old descriptors. */
|
||||||
dirserv_remove_old_servers();
|
dirserv_remove_old_servers();
|
||||||
/* dirservers try to reconnect too, in case connections have failed */
|
/* dirservers try to reconnect too, in case connections have failed */
|
||||||
router_retry_connections();
|
router_retry_connections();
|
||||||
}
|
}
|
||||||
|
|
||||||
directory_get_from_dirserver(DIR_PURPOSE_FETCH_DIR, NULL, 0);
|
directory_get_from_dirserver(DIR_PURPOSE_FETCH_DIR, NULL, 0);
|
||||||
/* Force an upload of our descriptors every DirFetchPostPeriod seconds. */
|
|
||||||
|
/* Force an upload of our rend descriptors every DirFetchPostPeriod seconds. */
|
||||||
rend_services_upload(1);
|
rend_services_upload(1);
|
||||||
last_uploaded_services = now;
|
last_uploaded_services = now;
|
||||||
rend_cache_clean(); /* should this go elsewhere? */
|
rend_cache_clean(); /* should this go elsewhere? */
|
||||||
|
|
||||||
time_to_fetch_directory = now + options.DirFetchPostPeriod;
|
time_to_fetch_directory = now + options.DirFetchPostPeriod;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 2. Every second, we examine pending circuits and prune the
|
/** 3a. Every second, we examine pending circuits and prune the
|
||||||
* ones which have been pending for more than a few seconds.
|
* ones which have been pending for more than a few seconds.
|
||||||
* We do this before step 3, so it can try building more if
|
* We do this before step 3, so it can try building more if
|
||||||
* it's not comfortable with the number of available circuits.
|
* it's not comfortable with the number of available circuits.
|
||||||
*/
|
*/
|
||||||
circuit_expire_building(now);
|
circuit_expire_building(now);
|
||||||
|
|
||||||
/** 2b. Also look at pending streams and prune the ones that 'began'
|
/** 3b. Also look at pending streams and prune the ones that 'began'
|
||||||
* a long time ago but haven't gotten a 'connected' yet.
|
* a long time ago but haven't gotten a 'connected' yet.
|
||||||
* Do this before step 3, so we can put them back into pending
|
* Do this before step 3, so we can put them back into pending
|
||||||
* state to be picked up by the new circuit.
|
* state to be picked up by the new circuit.
|
||||||
@ -468,11 +500,11 @@ static void run_scheduled_events(time_t now) {
|
|||||||
connection_ap_expire_beginning();
|
connection_ap_expire_beginning();
|
||||||
|
|
||||||
|
|
||||||
/** 2c. And expire connections that we've held open for too long.
|
/** 3c. And expire connections that we've held open for too long.
|
||||||
*/
|
*/
|
||||||
connection_expire_held_open();
|
connection_expire_held_open();
|
||||||
|
|
||||||
/** 3. Every second, we try a new circuit if there are no valid
|
/** 4. Every second, we try a new circuit if there are no valid
|
||||||
* circuits. Every NewCircuitPeriod seconds, we expire circuits
|
* circuits. Every NewCircuitPeriod seconds, we expire circuits
|
||||||
* that became dirty more than NewCircuitPeriod seconds ago,
|
* that became dirty more than NewCircuitPeriod seconds ago,
|
||||||
* and we make a new circ if there are no clean circuits.
|
* and we make a new circ if there are no clean circuits.
|
||||||
@ -480,22 +512,22 @@ static void run_scheduled_events(time_t now) {
|
|||||||
if(has_fetched_directory)
|
if(has_fetched_directory)
|
||||||
circuit_build_needed_circs(now);
|
circuit_build_needed_circs(now);
|
||||||
|
|
||||||
/** 4. We do housekeeping for each connection... */
|
/** 5. We do housekeeping for each connection... */
|
||||||
for(i=0;i<nfds;i++) {
|
for(i=0;i<nfds;i++) {
|
||||||
run_connection_housekeeping(i, now);
|
run_connection_housekeeping(i, now);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 5. And remove any marked circuits... */
|
/** 6. And remove any marked circuits... */
|
||||||
circuit_close_all_marked();
|
circuit_close_all_marked();
|
||||||
|
|
||||||
/** 6. And upload service descriptors for any services whose intro points
|
/** 7. And upload service descriptors for any services whose intro points
|
||||||
* have changed in the last second. */
|
* have changed in the last second. */
|
||||||
if (last_uploaded_services < now-5) {
|
if (last_uploaded_services < now-5) {
|
||||||
rend_services_upload(0);
|
rend_services_upload(0);
|
||||||
last_uploaded_services = now;
|
last_uploaded_services = now;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 7. and blow away any connections that need to die. have to do this now,
|
/** 8. and blow away any connections that need to die. have to do this now,
|
||||||
* because if we marked a conn for close and left its socket -1, then
|
* because if we marked a conn for close and left its socket -1, then
|
||||||
* we'll pass it to poll/select and bad things will happen.
|
* we'll pass it to poll/select and bad things will happen.
|
||||||
*/
|
*/
|
||||||
@ -510,6 +542,7 @@ static void run_scheduled_events(time_t now) {
|
|||||||
static int prepare_for_poll(void) {
|
static int prepare_for_poll(void) {
|
||||||
static long current_second = 0; /* from previous calls to gettimeofday */
|
static long current_second = 0; /* from previous calls to gettimeofday */
|
||||||
connection_t *conn;
|
connection_t *conn;
|
||||||
|
int bytes_read;
|
||||||
struct timeval now;
|
struct timeval now;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
@ -517,8 +550,12 @@ static int prepare_for_poll(void) {
|
|||||||
|
|
||||||
/* Check how much bandwidth we've consumed, and increment the token
|
/* Check how much bandwidth we've consumed, and increment the token
|
||||||
* buckets. */
|
* buckets. */
|
||||||
stats_n_bytes_read += stats_prev_global_read_bucket-global_read_bucket;
|
bytes_read = stats_prev_global_read_bucket - global_read_bucket;
|
||||||
|
stats_n_bytes_read += bytes_read;
|
||||||
connection_bucket_refill(&now);
|
connection_bucket_refill(&now);
|
||||||
|
if (bytes_read > 0) {
|
||||||
|
rep_hist_note_bytes_read(bytes_read, now.tv_sec);
|
||||||
|
}
|
||||||
stats_prev_global_read_bucket = global_read_bucket;
|
stats_prev_global_read_bucket = global_read_bucket;
|
||||||
|
|
||||||
if(now.tv_sec > current_second) { /* the second has rolled over. check more stuff. */
|
if(now.tv_sec > current_second) { /* the second has rolled over. check more stuff. */
|
||||||
|
@ -840,6 +840,7 @@ typedef struct {
|
|||||||
int SocksPort; /**< Port to listen on for SOCKS connections. */
|
int SocksPort; /**< Port to listen on for SOCKS connections. */
|
||||||
int DirPort; /**< Port to listen on for directory connections. */
|
int DirPort; /**< Port to listen on for directory connections. */
|
||||||
int AuthoritativeDir; /**< Boolean: is this an authoritative directory? */
|
int AuthoritativeDir; /**< Boolean: is this an authoritative directory? */
|
||||||
|
int ClientOnly; /**< Boolean: should we never evolve into a server role? */
|
||||||
int MaxConn; /**< Maximum number of simultaneous connections. */
|
int MaxConn; /**< Maximum number of simultaneous connections. */
|
||||||
int TrafficShaping; /**< Unused. */
|
int TrafficShaping; /**< Unused. */
|
||||||
int LinkPadding; /**< Unused. */
|
int LinkPadding; /**< Unused. */
|
||||||
|
@ -287,6 +287,54 @@ void write_rep_history(const char *filename)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define NUM_SECS_ROLLING_MEASURE 10
|
||||||
|
#define NUM_SECS_BW_SUM_IS_VALID (12*60*60) /* half a day */
|
||||||
|
|
||||||
|
/** 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
|
||||||
|
* earlier that the latest <b>when</b> you've heard of.
|
||||||
|
*/
|
||||||
|
void rep_hist_note_bytes_written(int num_bytes, time_t when) {
|
||||||
|
/* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/** We wrote <b>num_bytes</b> more bytes in second <b>when</b>.
|
||||||
|
* (like rep_hist_note_bytes_written() above)
|
||||||
|
*/
|
||||||
|
void rep_hist_note_bytes_read(int num_bytes, time_t when) {
|
||||||
|
/* if we're smart, we can make this func and the one above share code */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the largest sums in the past NUM_SECS_BW_SUM_IS_VALID (roughly)
|
||||||
|
* seconds. Find one sum for reading and one for writing. They don't have
|
||||||
|
* to be at the same time).
|
||||||
|
*
|
||||||
|
* Return the smaller of these sums, divided by NUM_SECS_ROLLING_MEASURE.
|
||||||
|
*/
|
||||||
|
int rep_hist_bandwidth_assess(time_t when) {
|
||||||
|
/* To get a handle on space complexity, I promise I will call this
|
||||||
|
* function at most every options.DirFetchPostPeriod seconds. So in
|
||||||
|
* rep_hist_note_bytes_foo() above, you could keep a running max sum
|
||||||
|
* for the current period, and when the period ends you can tuck it away
|
||||||
|
* in a circular array of more managable size. We lose a bit of precision,
|
||||||
|
* but this is all guesswork anyway.
|
||||||
|
*/
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Local Variables:
|
Local Variables:
|
||||||
mode:c
|
mode:c
|
||||||
|
Loading…
Reference in New Issue
Block a user