From a1f1fa6ab23dad9e9571843e106e51e13c64ae1d Mon Sep 17 00:00:00 2001 From: Roger Dingledine Date: Sun, 27 Feb 2005 09:47:01 +0000 Subject: [PATCH] Checkpoint in-progress fixes: Add 'testing' circuit purpose, for reachability testing. Notice when our IP changes, and reset stats. Try to pull down a directory via Tor to see if our DirPort is working. Try to extend a circuit back to us to see if our ORPort is working. Only publish a descriptor if they're both reachable. These mostly work, and I'd better get them in before I cause conflicts. svn:r3703 --- src/or/circuitbuild.c | 2 + src/or/circuituse.c | 34 ++++++++++- src/or/config.c | 6 ++ src/or/directory.c | 64 ++++++++++++--------- src/or/main.c | 112 +++++++----------------------------- src/or/or.h | 34 +++++++---- src/or/router.c | 129 +++++++++++++++++++++++++++++++++++++----- src/or/routerlist.c | 4 +- 8 files changed, 235 insertions(+), 150 deletions(-) diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c index b10d0b1732..c6015a1aaf 100644 --- a/src/or/circuitbuild.c +++ b/src/or/circuitbuild.c @@ -753,6 +753,8 @@ static int new_route_len(double cw, uint8_t purpose, smartlist_t *routers) { #else if (purpose == CIRCUIT_PURPOSE_C_GENERAL) routelen = 3; + else if (purpose == CIRCUIT_PURPOSE_TESTING) + routelen = 3; else if (purpose == CIRCUIT_PURPOSE_C_INTRODUCING) routelen = 4; else if (purpose == CIRCUIT_PURPOSE_C_ESTABLISH_REND) diff --git a/src/or/circuituse.c b/src/or/circuituse.c index 2d69e97566..076c0a6548 100644 --- a/src/or/circuituse.c +++ b/src/or/circuituse.c @@ -541,6 +541,31 @@ circuit_expire_old_circuits(void) } } +/** A testing circuit has completed. Take whatever stats we want. */ +static void +circuit_testing_opened(circuit_t *circ) { + + /* For now, we only use testing circuits to see if our ORPort is + reachable. So, if this circuit ends at us, remember that. */ + routerinfo_t *exit = router_get_by_digest(circ->build_state->chosen_exit_digest); + if(exit && router_is_me(exit)) { + log_fn(LOG_NOTICE,"Your ORPort is reachable from the outside. Excellent."); + router_orport_found_reachable(); + } + circuit_mark_for_close(circ); +} + +/** A testing circuit has failed to build. Take whatever stats we want. */ +static void +circuit_testing_failed(circuit_t *circ, int at_last_hop) { + routerinfo_t *me = router_get_my_routerinfo(); + + if (!at_last_hop) + circuit_launch_by_identity(CIRCUIT_PURPOSE_TESTING, me->identity_digest, 0, 0, 1); + else + log_fn(LOG_NOTICE,"The testing circuit has failed. Guess you're not reachable yet."); +} + /** The circuit circ has just become open. Take the next * step: for rendezvous circuits, we pass circ to the appropriate * function in rendclient or rendservice. For general circuits, we @@ -572,6 +597,9 @@ void circuit_has_opened(circuit_t *circ) { /* at Bob, connecting to rend point */ rend_service_rendezvous_has_opened(circ); break; + case CIRCUIT_PURPOSE_TESTING: + circuit_testing_opened(circ); + break; default: log_fn(LOG_ERR,"unhandled purpose %d",circ->purpose); tor_assert(0); @@ -603,6 +631,9 @@ void circuit_build_failed(circuit_t *circ) { circuit_increment_failure_count(); } break; + case CIRCUIT_PURPOSE_TESTING: + circuit_testing_failed(circ, failed_at_last_hop); + break; case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO: /* at Bob, waiting for introductions */ if (circ->state != CIRCUIT_STATE_OPEN) { @@ -666,7 +697,8 @@ circuit_launch_by_identity(uint8_t purpose, const char *exit_digest, return NULL; } - if (purpose != CIRCUIT_PURPOSE_C_GENERAL) { + if (purpose != CIRCUIT_PURPOSE_C_GENERAL && + purpose != CIRCUIT_PURPOSE_TESTING) { /* see if there are appropriate circs available to cannibalize. */ if ((circ = circuit_get_clean_open(CIRCUIT_PURPOSE_C_GENERAL, need_uptime, need_capacity, internal))) { diff --git a/src/or/config.c b/src/or/config.c index 4ae2476b86..204a477f67 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -933,6 +933,7 @@ resolve_my_address(const char *address, uint32_t *addr) char hostname[256]; int explicit_ip=1; char tmpbuf[INET_NTOA_BUF_LEN]; + static uint32_t old_addr=0; tor_assert(addr); @@ -972,6 +973,11 @@ resolve_my_address(const char *address, uint32_t *addr) log_fn(LOG_DEBUG, "Resolved Address to %s.", tmpbuf); *addr = ntohl(in.s_addr); + if (old_addr && old_addr != *addr) { + log_fn(LOG_NOTICE,"Your IP seems to have changed. Updating."); + server_has_changed_ip(); + } + old_addr = *addr; return 0; } diff --git a/src/or/directory.c b/src/or/directory.c index a9cb64ee7a..7ac34deceb 100644 --- a/src/or/directory.c +++ b/src/or/directory.c @@ -28,19 +28,20 @@ const char directory_c_id[] = "$Id$"; * connection_finished_connecting() in connection.c */ -static void +void directory_initiate_command_router(routerinfo_t *router, uint8_t purpose, - const char *resource, + int private_connection, const char *resource, const char *payload, size_t payload_len); static void directory_initiate_command_trusted_dir(trusted_dir_server_t *dirserv, - uint8_t purpose, const char *resource, + uint8_t purpose, int private_connection, + const char *resource, const char *payload, size_t payload_len); static void directory_initiate_command(const char *address, uint32_t addr, uint16_t port, const char *platform, const char *digest, uint8_t purpose, - const char *resource, + int private_connection, const char *resource, const char *payload, size_t payload_len); static void @@ -111,6 +112,14 @@ int dir_policy_permits_address(uint32_t addr) return 0; } +int purpose_is_private(uint8_t purpose) { + if (purpose == DIR_PURPOSE_FETCH_DIR || + purpose == DIR_PURPOSE_UPLOAD_DIR || + purpose == DIR_PURPOSE_FETCH_RUNNING_LIST) + return 0; + return 1; +} + /** Start a connection to every known directory server, using * connection purpose 'purpose' and uploading the payload 'payload' * (length 'payload_len'). The purpose should be one of @@ -137,8 +146,8 @@ directory_post_to_dirservers(uint8_t purpose, const char *payload, if (!smartlist_string_num_isin(get_options()->FirewallPorts, ds->dir_port)) continue; } - directory_initiate_command_trusted_dir(ds, purpose, NULL, - payload, payload_len); + directory_initiate_command_trusted_dir(ds, purpose, purpose_is_private(purpose), + NULL, payload, payload_len); }); } @@ -159,6 +168,7 @@ directory_get_from_dirserver(uint8_t purpose, const char *resource, int directconn = purpose == DIR_PURPOSE_FETCH_DIR || purpose == DIR_PURPOSE_FETCH_RUNNING_LIST; int fetch_fresh_first = advertised_server_mode(); + int priv = purpose_is_private(purpose); if (directconn) { if (fetch_fresh_first) { @@ -186,9 +196,9 @@ directory_get_from_dirserver(uint8_t purpose, const char *resource, } if (r) - directory_initiate_command_router(r, purpose, resource, NULL, 0); + directory_initiate_command_router(r, purpose, priv, resource, NULL, 0); else if (ds) - directory_initiate_command_trusted_dir(ds, purpose, resource, NULL, 0); + directory_initiate_command_trusted_dir(ds, purpose, priv, resource, NULL, 0); else { log_fn(LOG_NOTICE,"No running dirservers known. Not trying. (purpose %d)", purpose); @@ -210,25 +220,28 @@ directory_get_from_dirserver(uint8_t purpose, const char *resource, * When fetching a rendezvous descriptor, resource is the service ID we * want to fetch. */ -static void +void directory_initiate_command_router(routerinfo_t *router, uint8_t purpose, - const char *resource, + int private_connection, const char *resource, const char *payload, size_t payload_len) { directory_initiate_command(router->address, router->addr, router->dir_port, router->platform, router->identity_digest, - purpose, resource, payload, payload_len); + purpose, private_connection, resource, + payload, payload_len); } /** As directory_initiate_command_router, but send the command to a trusted * directory server dirserv. **/ static void directory_initiate_command_trusted_dir(trusted_dir_server_t *dirserv, - uint8_t purpose, const char *resource, + uint8_t purpose, int private_connection, + const char *resource, const char *payload, size_t payload_len) { directory_initiate_command(dirserv->address, dirserv->addr,dirserv->dir_port, - NULL, dirserv->digest, purpose, resource, payload, payload_len); + NULL, dirserv->digest, purpose, private_connection, resource, + payload, payload_len); } /** Called when we are unable to complete our connection to a @@ -256,7 +269,7 @@ static void directory_initiate_command(const char *address, uint32_t addr, uint16_t dir_port, const char *platform, const char *digest, uint8_t purpose, - const char *resource, + int private_connection, const char *resource, const char *payload, size_t payload_len) { connection_t *conn; @@ -292,17 +305,7 @@ directory_initiate_command(const char *address, uint32_t addr, /* set up conn so it's got all the data we need to remember */ conn->addr = addr; conn->port = dir_port; - - if (get_options()->HttpProxy) { - addr = get_options()->HttpProxyAddr; - dir_port = get_options()->HttpProxyPort; - } - conn->address = tor_strdup(address); - /* conn->nickname = tor_strdup(router->nickname); */ - /* tor_assert(router->identity_pkey); */ - /* conn->identity_pkey = crypto_pk_dup_key(router->identity_pkey); */ - /* crypto_pk_get_digest(conn->identity_pkey, conn->identity_digest); */ memcpy(conn->identity_digest, digest, DIGEST_LEN); conn->purpose = purpose; @@ -310,10 +313,14 @@ directory_initiate_command(const char *address, uint32_t addr, /* give it an initial state */ conn->state = DIR_CONN_STATE_CONNECTING; - if (purpose == DIR_PURPOSE_FETCH_DIR || - purpose == DIR_PURPOSE_UPLOAD_DIR || - purpose == DIR_PURPOSE_FETCH_RUNNING_LIST) { + if (!private_connection) { /* then we want to connect directly */ + + if (get_options()->HttpProxy) { + addr = get_options()->HttpProxyAddr; + dir_port = get_options()->HttpProxyPort; + } + switch (connection_connect(conn, conn->address, addr, dir_port)) { case -1: connection_dir_connect_failed(conn); @@ -703,7 +710,8 @@ connection_dir_client_reached_eof(connection_t *conn) } else { log_fn(LOG_INFO,"updated routers."); } - directory_has_arrived(time(NULL)); /* do things we've been waiting to do */ + /* do things we've been waiting to do */ + directory_has_arrived(time(NULL), conn->identity_digest); } if (conn->purpose == DIR_PURPOSE_FETCH_RUNNING_LIST) { diff --git a/src/or/main.c b/src/or/main.c index 1460f15b62..46d88711b3 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -263,7 +263,6 @@ void connection_watch_events(connection_t *conn, short events) { /** Return true iff conn is listening for read events. */ int connection_is_reading(connection_t *conn) { - int r; tor_assert(conn); return conn->read_event && event_pending(conn->read_event, EV_READ, NULL); @@ -578,8 +577,10 @@ void directory_all_unreachable(time_t now) { } } -/** This function is called whenever we successfully pull down a directory */ -void directory_has_arrived(time_t now) { +/** This function is called whenever we successfully pull down a directory. + * If identity_digest is defined, it contains the digest of the + * router that just gave us this directory. */ +void directory_has_arrived(time_t now, char *identity_digest) { or_options_t *options = get_options(); log_fn(LOG_INFO, "A directory has arrived."); @@ -597,14 +598,26 @@ void directory_has_arrived(time_t now) { if (!time_to_fetch_running_routers) time_to_fetch_running_routers = now + options->StatusFetchPeriod; + if (identity_digest) { /* if this is us, then our dirport is reachable */ + routerinfo_t *router = router_get_by_digest(identity_digest); + if (!router) // XXX + log_fn(LOG_WARN,"Roger, router_get_by_digest doesn't find me."); + if (router && router_is_me(router)) { + log_fn(LOG_NOTICE,"Your DirPort is reachable from the outside. Excellent."); + router_dirport_found_reachable(); + } + } + if (server_mode(options) && !we_are_hibernating()) { /* connect to the appropriate routers */ router_retry_connections(); + if (identity_digest) /* we got a fresh directory */ + consider_testing_reachability(); } } /** Perform regular maintenance tasks for a single connection. This - * function gets run once per second per connection by run_housekeeping. + * function gets run once per second per connection by run_scheduled_events. */ static void run_connection_housekeeping(int i, time_t now) { cell_t cell; @@ -648,81 +661,6 @@ static void run_connection_housekeeping(int i, time_t now) { } } -#define MIN_BW_TO_PUBLISH_DESC 5000 /* 5000 bytes/s sustained */ -#define MIN_UPTIME_TO_PUBLISH_DESC (30*60) /* half an hour */ - -/** Decide if we're a publishable 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_publishable_server(time_t now) { - int bw; - or_options_t *options = get_options(); - - bw = rep_hist_bandwidth_assess(); - router_set_bandwidth_capacity(bw); - - if (options->ClientOnly) - return 0; - if (!options->ORPort) - return 0; - - /* XXX for now, you're only a server if you're a server */ - return server_mode(options); - - /* here, determine if we're reachable */ - if (0) { /* we've recently failed to reach our IP/ORPort from the outside */ - return 0; - } - - if (bw < MIN_BW_TO_PUBLISH_DESC) - return 0; - if (options->AuthoritativeDir) - return 1; - if (stats_n_seconds_working < MIN_UPTIME_TO_PUBLISH_DESC) - return 0; - - return 1; -} - -/** Return true iff we believe ourselves to be an authoritative - * directory server. - */ -int authdir_mode(or_options_t *options) { - return options->AuthoritativeDir != 0; -} - -/** Return true iff we try to stay connected to all ORs at once. - */ -int clique_mode(or_options_t *options) { - return authdir_mode(options); -} - -/** Return true iff we are trying to be a server. - */ -int server_mode(or_options_t *options) { - return (options->ORPort != 0 || options->ORBindAddress); -} - -/** Remember if we've advertised ourselves to the dirservers. */ -static int server_is_advertised=0; - -/** Return true iff we have published our descriptor lately. - */ -int advertised_server_mode(void) { - return server_is_advertised; -} - -/** Return true iff we are trying to be a socks proxy. */ -int proxy_mode(or_options_t *options) { - return (options->SocksPort != 0 || options->SocksBindAddress); -} - /** Perform regular maintenance tasks. This function gets run once per * second by prepare_for_poll. */ @@ -810,13 +748,7 @@ static void run_scheduled_events(time_t now) { } if (time_to_force_upload_descriptor < now) { - if (decide_if_publishable_server(now)) { - server_is_advertised = 1; - router_rebuild_descriptor(1); - router_upload_dir_desc_to_dirservers(1); - } else { - server_is_advertised = 0; - } + consider_publishable_server(now, 1); rend_cache_clean(); /* this should go elsewhere? */ @@ -827,13 +759,7 @@ static void run_scheduled_events(time_t now) { * one is inaccurate. */ if (time_to_check_descriptor < now) { time_to_check_descriptor = now + CHECK_DESCRIPTOR_INTERVAL; - if (decide_if_publishable_server(now)) { - server_is_advertised=1; - router_rebuild_descriptor(0); - router_upload_dir_desc_to_dirservers(0); - } else { - server_is_advertised=0; - } + consider_publishable_server(now, 0); } /** 3a. Every second, we examine pending circuits and prune the diff --git a/src/or/or.h b/src/or/or.h index 21db3f5fc9..705cfd4cf9 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -381,7 +381,9 @@ typedef enum { #define CIRCUIT_PURPOSE_S_CONNECT_REND 15 /** Hidden-service-side circuit purpose: at Bob, rendezvous established. */ #define CIRCUIT_PURPOSE_S_REND_JOINED 16 -#define _CIRCUIT_PURPOSE_MAX 16 +/** A testing circuit; not meant to be used for actual traffic. */ +#define CIRCUIT_PURPOSE_TESTING 17 +#define _CIRCUIT_PURPOSE_MAX 17 /** True iff the circuit purpose p is for a circuit at the OP * that this OP has originated. */ @@ -1378,6 +1380,10 @@ void directory_post_to_dirservers(uint8_t purpose, const char *payload, size_t payload_len); void directory_get_from_dirserver(uint8_t purpose, const char *resource, int retry_if_no_servers); +void directory_initiate_command_router(routerinfo_t *router, uint8_t purpose, + int private_connection, const char *resource, + const char *payload, size_t payload_len); + int parse_http_response(const char *headers, int *code, time_t *date, int *compression); @@ -1455,13 +1461,7 @@ void connection_stop_writing(connection_t *conn); void connection_start_writing(connection_t *conn); void directory_all_unreachable(time_t now); -void directory_has_arrived(time_t now); - -int authdir_mode(or_options_t *options); -int clique_mode(or_options_t *options); -int server_mode(or_options_t *options); -int advertised_server_mode(void); -int proxy_mode(or_options_t *options); +void directory_has_arrived(time_t now, char *identity_digest); int control_signal_act(int the_signal); void handle_signals(int is_parent); @@ -1624,11 +1624,21 @@ void set_identity_key(crypto_pk_env_t *k); crypto_pk_env_t *get_identity_key(void); int identity_key_is_set(void); void dup_onion_keys(crypto_pk_env_t **key, crypto_pk_env_t **last); -int init_keys(void); -crypto_pk_env_t *init_key_from_file(const char *fname); void rotate_onion_key(void); -void router_set_bandwidth_capacity(int bw); -int router_get_bandwidth_capacity(void); +crypto_pk_env_t *init_key_from_file(const char *fname); +int init_keys(void); + +void consider_testing_reachability(void); +void router_orport_found_reachable(void); +void router_dirport_found_reachable(void); +void server_has_changed_ip(void); +void consider_publishable_server(time_t now, int force); + +int authdir_mode(or_options_t *options); +int clique_mode(or_options_t *options); +int server_mode(or_options_t *options); +int advertised_server_mode(void); +int proxy_mode(or_options_t *options); void router_retry_connections(void); int router_is_clique_mode(routerinfo_t *router); diff --git a/src/or/router.c b/src/or/router.c index c8b2d2a83c..de45b309f6 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -143,19 +143,6 @@ void rotate_onion_key(void) log_fn(LOG_WARN, "Couldn't rotate onion key."); } -/** The latest calculated bandwidth usage for our node. */ -static int bw_capacity = 0; -/** Tuck bw away so we can produce it when somebody - * calls router_get_bandwidth_capacity() below. - */ -void router_set_bandwidth_capacity(int bw) { - bw_capacity = bw; -} -/** Return the value we tucked away above, or zero by default. */ -int router_get_bandwidth_capacity(void) { - return bw_capacity; -} - /* Read an RSA secret key key from a file that was once named fname_old, * but is now named fname_new. Rename the file from old to new as needed. */ @@ -382,6 +369,120 @@ int init_keys(void) { return 0; } +/* Keep track of whether we should upload our server descriptor, + * and what type of server we are. + */ + +/** Whether we can reach our ORPort from the outside. */ +static int can_reach_or_port = 0; +/** Whether we can reach our DirPort from the outside. */ +static int can_reach_dir_port = 0; + +void consider_testing_reachability(void) { + routerinfo_t *me = router_get_my_routerinfo(); + + if (!can_reach_or_port) { + circuit_launch_by_identity(CIRCUIT_PURPOSE_TESTING, me->identity_digest, 0, 0, 1); + } + + if (!can_reach_dir_port && me->dir_port) { + if (me) { + directory_initiate_command_router(me, DIR_PURPOSE_FETCH_DIR, 1, NULL, NULL, 0); + } else { + log_fn(LOG_NOTICE,"Delaying checking DirPort reachability; can't build descriptor."); + } + } +} + +/** Annotate that we found our ORPort reachable. */ +void router_orport_found_reachable(void) { + can_reach_or_port = 1; +} + +/** Annotate that we found our DirPort reachable. */ +void router_dirport_found_reachable(void) { + can_reach_dir_port = 1; +} + +/** Our router has just moved to a new IP. Reset stats. */ +void server_has_changed_ip(void) { + stats_n_seconds_working = 0; + can_reach_or_port = 0; + can_reach_dir_port = 0; + mark_my_descriptor_dirty(); +} + +/** Return true iff we believe ourselves to be an authoritative + * directory server. + */ +int authdir_mode(or_options_t *options) { + return options->AuthoritativeDir != 0; +} +/** Return true iff we try to stay connected to all ORs at once. + */ +int clique_mode(or_options_t *options) { + return authdir_mode(options); +} + +/** Return true iff we are trying to be a server. + */ +int server_mode(or_options_t *options) { + return (options->ORPort != 0 || options->ORBindAddress); +} + +/** Remember if we've advertised ourselves to the dirservers. */ +static int server_is_advertised=0; + +/** Return true iff we have published our descriptor lately. + */ +int advertised_server_mode(void) { + return server_is_advertised; +} + +static void set_server_advertised(int s) { + server_is_advertised = s; +} + +/** Return true iff we are trying to be a socks proxy. */ +int proxy_mode(or_options_t *options) { + return (options->SocksPort != 0 || options->SocksBindAddress); +} + +/** Decide if we're a publishable 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 believe we are reachable from the outside. + */ +static int decide_if_publishable_server(time_t now) { + or_options_t *options = get_options(); + + if (options->ClientOnly) + return 0; + if (!server_mode(options)) + return 0; + if (options->AuthoritativeDir) + return 1; + + if (!can_reach_or_port) + return 0; + if (options->DirPort && !can_reach_dir_port) + return 0; + + return 1; +} + +void consider_publishable_server(time_t now, int force) { + if (decide_if_publishable_server(now)) { + set_server_advertised(1); + router_rebuild_descriptor(force); + router_upload_dir_desc_to_dirservers(force); + } else { + set_server_advertised(0); + } +} + /* * Clique maintenance */ @@ -568,7 +669,7 @@ int router_rebuild_descriptor(int force) { ri->platform = tor_strdup(platform); ri->bandwidthrate = (int)options->BandwidthRate; ri->bandwidthburst = (int)options->BandwidthBurst; - ri->bandwidthcapacity = hibernating ? 0 : router_get_bandwidth_capacity(); + ri->bandwidthcapacity = hibernating ? 0 : rep_hist_bandwidth_assess(); router_add_exit_policy_from_config(ri); if (desc_routerinfo) /* inherit values */ ri->is_verified = desc_routerinfo->is_verified; diff --git a/src/or/routerlist.c b/src/or/routerlist.c index 360d46d865..dfd37363f1 100644 --- a/src/or/routerlist.c +++ b/src/or/routerlist.c @@ -66,7 +66,7 @@ int router_reload_router_list(void) if (routerlist && ((routerlist->published_on > time(NULL) - MIN_ONION_KEY_LIFETIME/2) || is_recent)) { - directory_has_arrived(st.st_mtime); /* do things we've been waiting to do */ + directory_has_arrived(st.st_mtime, NULL); /* do things we've been waiting to do */ } tor_free(s); } @@ -634,11 +634,11 @@ routerinfo_t *router_get_by_digest(const char *digest) { routerinfo_t *router; tor_assert(digest); - if (!routerlist) return NULL; if (server_mode(get_options()) && (router = router_get_my_routerinfo()) && !memcmp(digest, router->identity_digest, DIGEST_LEN)) return router; + if (!routerlist) return NULL; for (i=0;irouters);i++) { router = smartlist_get(routerlist->routers, i);