Initial, somewhat dodgy implementation of helper nodes. It has too many XXXXs, it logs too verbosely, and it doesnt do persistence.

svn:r4633
This commit is contained in:
Nick Mathewson 2005-07-22 17:32:25 +00:00
parent 5b4e11fa5d
commit c3a62c1739
6 changed files with 237 additions and 3 deletions

View File

@ -17,6 +17,15 @@ const char circuitbuild_c_id[] = "$Id$";
/** A global list of all circuits at this hop. */
extern circuit_t *global_circuitlist;
typedef struct {
char nickname[MAX_NICKNAME_LEN+1];
char identity[DIGEST_LEN];
time_t down_since;
time_t unlisted_since;
} helper_node_t;
static smartlist_t *helper_nodes;
/********* END VARIABLES ************/
static int circuit_deliver_create_cell(circuit_t *circ,
@ -27,6 +36,10 @@ static int onion_extend_cpath(uint8_t purpose, crypt_path_t **head_ptr,
cpath_build_state_t *state);
static int count_acceptable_routers(smartlist_t *routers);
static int onion_append_hop(crypt_path_t **head_ptr, extend_info_t *choice);
static void pick_helper_nodes(void);
static routerinfo_t *choose_random_helper(void);
static void clear_helper_nodes(void);
static void remove_dead_helpers(void);
/** Iterate over values of circ_id, starting from conn-\>next_circ_id,
* and with the high bit specified by circ_id_type (see
@ -1368,7 +1381,10 @@ choose_good_middle_server(uint8_t purpose,
return choice;
}
/** DOCDOC */
/** DOCDOC
*
* state == null means 'pick a helper.'
*/
static routerinfo_t *
choose_good_entry_server(cpath_build_state_t *state)
{
@ -1376,7 +1392,11 @@ choose_good_entry_server(cpath_build_state_t *state)
smartlist_t *excluded = smartlist_create();
or_options_t *options = get_options();
if ((r = build_state_get_exit_router(state))) {
if (state && options->UseHelperNodes) {
return choose_random_helper();
}
if (state && (r = build_state_get_exit_router(state))) {
smartlist_add(excluded, r);
routerlist_add_family(excluded, r);
}
@ -1400,7 +1420,8 @@ choose_good_entry_server(cpath_build_state_t *state)
}
}
choice = router_choose_random_node(options->EntryNodes, options->ExcludeNodes,
excluded, state->need_uptime, state->need_capacity,
excluded, state ? state->need_uptime : 1,
state ? state->need_capacity : 0,
options->_AllowUnverified & ALLOW_UNVERIFIED_ENTRY,
options->StrictEntryNodes);
smartlist_free(excluded);
@ -1569,3 +1590,203 @@ build_state_get_exit_nickname(cpath_build_state_t *state)
return state->chosen_exit->nickname;
}
/** DOCDOC */
static int
n_live_helpers(void)
{
int n = 0;
SMARTLIST_FOREACH(helper_nodes, helper_node_t *, helper,
if (! helper->down_since && ! helper->unlisted_since)
++n;);
return n;
}
/** DOCDOC */
static void
pick_helper_nodes(void)
{
or_options_t *options = get_options();
if (! options->UseHelperNodes)
return;
if (helper_nodes == NULL)
helper_nodes = smartlist_create();
while (smartlist_len(helper_nodes) < options->NumHelperNodes) {
routerinfo_t *entry = choose_good_entry_server(NULL);
/* XXXX deal with duplicate entries. */
helper_node_t *helper = tor_malloc_zero(sizeof(helper_node_t));
/* XXXX Downgrade this to info before release. */
log_fn(LOG_NOTICE, "Chose '%s' as helper node.", entry->nickname);
strlcpy(helper->nickname, entry->nickname, sizeof(helper->nickname));
memcpy(helper->identity, entry->identity_digest, DIGEST_LEN);
smartlist_add(helper_nodes, helper);
}
}
/** DOCDOC */
static void
clear_helper_nodes(void)
{
SMARTLIST_FOREACH(helper_nodes, helper_node_t *, h, tor_free(h));
smartlist_clear(helper_nodes);
}
#define HELPER_ALLOW_DOWNTIME 48*60*60
#define HELPER_ALLOW_UNLISTED 48*60*60
/** DOCDOC */
static void
remove_dead_helpers(void)
{
char dbuf[HEX_DIGEST_LEN+1];
char tbuf[ISO_TIME_LEN+1];
time_t now = time(NULL);
int i;
for (i = 0; i < smartlist_len(helper_nodes); ) {
helper_node_t *helper = smartlist_get(helper_nodes, i);
char *why = NULL;
time_t since = 0;
if (helper->unlisted_since + HELPER_ALLOW_UNLISTED > now) {
why = "unlisted";
since = helper->unlisted_since;
} else if (helper->down_since + HELPER_ALLOW_DOWNTIME > now) {
why = "down";
since = helper->unlisted_since;
}
if (why) {
base16_encode(dbuf, sizeof(dbuf), helper->identity, DIGEST_LEN);
format_local_iso_time(tbuf, since);
log_fn(LOG_WARN, "Helper node '%s' (%s) has been %s since %s; removing.",
helper->nickname, dbuf, why, tbuf);
tor_free(helper);
smartlist_del(helper_nodes, i);
} else
++i;
}
}
/** DOCDOC */
void
helper_nodes_set_status_from_directory(void)
{
/* Don't call this on startup; only on a fresh download. Otherwise we'll
* think that things are unlisted. */
routerlist_t *routers;
time_t now;
int changed = 0;
if (! helper_nodes)
return;
router_get_routerlist(&routers);
if (! routers)
return;
now = time(NULL);
/*XXXX Most of these warns should be non-warns. */
SMARTLIST_FOREACH(helper_nodes, helper_node_t *, helper,
{
routerinfo_t *r = router_get_by_digest(helper->identity);
if (! r) {
if (! helper->unlisted_since) {
/* Watch out for skew here. XXXX */
helper->unlisted_since = routers->published_on;
++changed;
log_fn(LOG_WARN,"Helper node '%s' is not published in latest directory",
helper->nickname);
}
} else {
if (helper->unlisted_since) {
log_fn(LOG_WARN,"Helper node '%s' is listed again in latest directory",
helper->nickname);
++changed;
}
helper->unlisted_since = 0;
if (! r->is_running) {
if (! helper->down_since) {
helper->down_since = now;
log_fn(LOG_WARN, "Helper node '%s' is now down.", helper->nickname);
++changed;
}
} else {
if (helper->down_since) {
log_fn(LOG_WARN,"Helper node '%s' is up in latest directory",
helper->nickname);
++changed;
}
helper->down_since = 0;
}
}
});
if (changed)
log_fn(LOG_WARN, " (%d/%d helpers are usable)",
n_live_helpers(), smartlist_len(helper_nodes));
remove_dead_helpers();
pick_helper_nodes();
}
/** DOCDOC */
void
helper_node_set_status(const char *digest, int succeeded)
{
if (! helper_nodes)
return;
SMARTLIST_FOREACH(helper_nodes, helper_node_t *, helper,
{
if (!memcmp(helper->identity, digest, DIGEST_LEN)) {
if (succeeded) {
if (helper->down_since) {
/*XXXX shouldn't warn. */
log_fn(LOG_WARN,
"Connection to formerly down helper node '%s' succeeeded. "
"%d/%d helpers usable.", helper->nickname,
n_live_helpers(), smartlist_len(helper_nodes));
}
helper->down_since = 0;
} else if (!helper->down_since) {
helper->down_since = time(NULL);
log_fn(LOG_WARN,
"Connection to helper node '%s' failed. %d/%d helpers usable.",
helper->nickname, n_live_helpers(), smartlist_len(helper_nodes));
}
}
});
}
/** DOCDOC */
static routerinfo_t *
choose_random_helper(void)
{
smartlist_t *live_helpers = smartlist_create();
routerinfo_t *r;
if (! helper_nodes)
pick_helper_nodes();
retry:
SMARTLIST_FOREACH(helper_nodes, helper_node_t *, helper,
if (! helper->down_since && ! helper->unlisted_since) {
if ((r = router_get_by_digest(helper->identity)))
smartlist_add(live_helpers, r);
});
if (! smartlist_len(live_helpers)) {
/* XXXX Is this right? What if network is down? */
log_fn(LOG_WARN, "No functional helper nodes found; picking a new set.");
clear_helper_nodes();
pick_helper_nodes();
goto retry;
}
r = smartlist_choose(live_helpers);
smartlist_free(live_helpers);
return r;
}

View File

@ -307,6 +307,9 @@ options_act(void)
return -1;
}
if (options->EntryNodes && strlen(options->EntryNodes))
options->UseHelperNodes = 0;
/* Setuid/setgid as appropriate */
if (options->User || options->Group) {
if (switch_id(options->User, options->Group) != 0) {

View File

@ -309,6 +309,7 @@ connection_about_to_close_connection(connection_t *conn)
if (conn->state != OR_CONN_STATE_OPEN) {
if (connection_or_nonopen_was_started_here(conn)) {
rep_hist_note_connect_failed(conn->identity_digest, time(NULL));
helper_node_set_status(conn->identity_digest, 0);
control_event_or_conn_status(conn, OR_CONN_EVENT_FAILED);
}
} else if (conn->hold_open_until_flushed) {

View File

@ -358,6 +358,7 @@ connection_or_connect(uint32_t addr, uint16_t port, const char *id_digest)
case -1:
if (!options->HttpsProxy)
router_mark_as_down(conn->identity_digest);
helper_node_set_status(conn->identity_digest, 0);
control_event_or_conn_status(conn, OR_CONN_EVENT_FAILED);
connection_free(conn);
return NULL;
@ -527,6 +528,7 @@ connection_or_check_valid_handshake(connection_t *conn, char *digest_rcvd)
log_fn(severity,
"Identity key not as expected for router at %s:%d: wanted %s but got %s",
conn->address, conn->port, conn->nickname+1, d);
helper_node_set_status(conn->identity_digest, 0);
control_event_or_conn_status(conn, OR_CONN_EVENT_FAILED);
as_advertised = 0;
}
@ -535,6 +537,7 @@ connection_or_check_valid_handshake(connection_t *conn, char *digest_rcvd)
log_fn(severity,
"Other side (%s:%d) is '%s', but we tried to connect to '%s'",
conn->address, conn->port, nickname, conn->nickname);
helper_node_set_status(conn->identity_digest, 0);
control_event_or_conn_status(conn, OR_CONN_EVENT_FAILED);
as_advertised = 0;
}
@ -592,6 +595,7 @@ connection_tls_finish_handshake(connection_t *conn)
connection_watch_events(conn, EV_READ);
circuit_n_conn_done(conn, 1); /* send the pending creates, if any. */
rep_hist_note_connect_succeeded(conn->identity_digest, time(NULL));
helper_node_set_status(conn->identity_digest, 1);
control_event_or_conn_status(conn, OR_CONN_EVENT_CONNECTED);
return 0;
}

View File

@ -758,6 +758,7 @@ connection_dir_client_reached_eof(connection_t *conn)
log_fn(LOG_INFO,"updated routers.");
}
/* do things we've been waiting to do */
helper_nodes_set_status_from_directory();
directory_has_arrived(time(NULL), conn->identity_digest);
}
@ -780,6 +781,7 @@ connection_dir_client_reached_eof(connection_t *conn)
router_get_routerlist(&rl);
if (rl) {
routerlist_set_runningrouters(rl,rrs);
helper_nodes_set_status_from_directory();
} else {
running_routers_free(rrs);
}

View File

@ -1252,6 +1252,9 @@ void extend_info_free(extend_info_t *info);
routerinfo_t *build_state_get_exit_router(cpath_build_state_t *state);
const char *build_state_get_exit_nickname(cpath_build_state_t *state);
void helper_node_set_status(const char *digest, int succeeded);
void helper_nodes_set_status_from_directory(void);
/********************************* circuitlist.c ***********************/
circuit_t * _circuit_get_global_list(void);