Commit cd3da3fc authored by Roger Dingledine's avatar Roger Dingledine
Browse files

o clients choose nodes proportional to advertised bandwidth

o and/or while avoiding unreliable nodes, depending on goals
o 'fascistfirewall' option to pick dirservers on port 80 and ORs on
  port 443.
o if a begin failed due to exit policy, but we believe the IP should                       have been allowed, switch that router to exitpolicy reject *:* until                     we get our next directory.


svn:r2231
parent 10c73764
......@@ -482,12 +482,6 @@ int circuit_extend(cell_t *cell, circuit_t *circ) {
id_digest = router->identity_digest;
} else { /* new format */
router = router_get_by_digest(id_digest);
#if 0
if(router) { /* addr/port might be different */
circ->n_addr = router->addr;
circ->n_port = router->or_port;
}
#endif
}
tor_assert(id_digest);
......@@ -947,11 +941,12 @@ static routerinfo_t *choose_good_exit_server_general(routerlist_t *dir)
static routerinfo_t *choose_good_exit_server(uint8_t purpose, routerlist_t *dir)
{
routerinfo_t *r;
/* XXX one day, consider picking chosen_exit knowing what's in EntryNodes */
switch(purpose) {
case CIRCUIT_PURPOSE_C_GENERAL:
return choose_good_exit_server_general(dir);
case CIRCUIT_PURPOSE_C_ESTABLISH_REND:
r = router_choose_random_node(options.RendNodes, options.RendExcludeNodes, NULL);
r = router_choose_random_node(options.RendNodes, options.RendExcludeNodes, NULL, 0, 1);
return r;
default:
log_fn(LOG_WARN,"unhandled purpose %d", purpose);
......@@ -1049,6 +1044,7 @@ static int count_acceptable_routers(smartlist_t *routers) {
return num;
}
#if 0
/** Go through smartlist <b>sl</b> of routers, and remove all elements that
* have the same onion key as twin.
*/
......@@ -1066,6 +1062,7 @@ static void remove_twins_from_smartlist(smartlist_t *sl, routerinfo_t *twin) {
}
}
}
#endif
/** Add <b>new_hop</b> to the end of the doubly-linked-list <b>head_ptr</b>.
*
......@@ -1084,6 +1081,61 @@ void onion_append_to_cpath(crypt_path_t **head_ptr, crypt_path_t *new_hop)
}
}
static routerinfo_t *choose_good_middle_server(cpath_build_state_t *state,
crypt_path_t *head,
int cur_len)
{
int i;
routerinfo_t *r, *choice;
crypt_path_t *cpath;
smartlist_t *excluded = smartlist_create();
log_fn(LOG_DEBUG, "Contemplating intermediate hop: random choice.");
excluded = smartlist_create();
if((r = router_get_by_digest(state->chosen_exit_digest)))
smartlist_add(excluded, r);
if((r = router_get_my_routerinfo()))
smartlist_add(excluded, r);
for (i = 0, cpath = head; i < cur_len; ++i, cpath=cpath->next) {
r = router_get_by_digest(cpath->identity_digest);
tor_assert(r);
smartlist_add(excluded, r);
}
choice = router_choose_random_node("", options.ExcludeNodes, excluded, 0, 1);
smartlist_free(excluded);
return choice;
}
static routerinfo_t *choose_good_entry_server(cpath_build_state_t *state)
{
routerinfo_t *r, *choice;
smartlist_t *excluded = smartlist_create();
if((r = router_get_by_digest(state->chosen_exit_digest)))
smartlist_add(excluded, r);
if((r = router_get_my_routerinfo()))
smartlist_add(excluded, r);
if(options.FascistFirewall) {
/* exclude all ORs that listen on the wrong port */
routerlist_t *rl;
int i;
router_get_routerlist(&rl);
if(!rl)
return NULL;
for(i=0; i < smartlist_len(rl->routers); i++) {
r = smartlist_get(rl->routers, i);
if(r->or_port != REQUIRED_FIREWALL_ORPORT)
smartlist_add(excluded, r);
}
}
choice = router_choose_random_node(options.EntryNodes,
options.ExcludeNodes, excluded, 0, 1);
smartlist_free(excluded);
return choice;
}
/** Choose a suitable next hop in the cpath <b>head_ptr</b>,
* based on <b>state</b>. Add the hop info to head_ptr, and return a
* pointer to the chosen router in <b>router_out</b>.
......@@ -1094,10 +1146,8 @@ onion_extend_cpath(crypt_path_t **head_ptr, cpath_build_state_t
{
int cur_len;
crypt_path_t *cpath, *hop;
routerinfo_t *r;
routerinfo_t *choice;
int i;
smartlist_t *sl, *excludednodes;
smartlist_t *excludednodes;
tor_assert(head_ptr);
tor_assert(router_out);
......@@ -1122,58 +1172,17 @@ onion_extend_cpath(crypt_path_t **head_ptr, cpath_build_state_t
add_nickname_list_to_smartlist(excludednodes,options.ExcludeNodes);
if(cur_len == state->desired_path_len - 1) { /* Picking last node */
log_fn(LOG_DEBUG, "Contemplating last hop: choice already made: %s",
state->chosen_exit_name);
choice = router_get_by_digest(state->chosen_exit_digest);
smartlist_free(excludednodes);
if(!choice) {
log_fn(LOG_WARN,"Our chosen exit %s is no longer in the directory? Discarding this circuit.",
state->chosen_exit_name);
return -1;
}
} else if(cur_len == 0) { /* picking first node */
/* try the nodes in EntryNodes first */
sl = smartlist_create();
add_nickname_list_to_smartlist(sl,options.EntryNodes);
/* XXX one day, consider picking chosen_exit knowing what's in EntryNodes */
remove_twins_from_smartlist(sl,router_get_by_digest(state->chosen_exit_digest));
remove_twins_from_smartlist(sl,router_get_my_routerinfo());
smartlist_subtract(sl,excludednodes);
choice = smartlist_choose(sl);
smartlist_free(sl);
if(!choice) {
sl = smartlist_create();
router_add_running_routers_to_smartlist(sl);
remove_twins_from_smartlist(sl,router_get_by_digest(state->chosen_exit_digest));
remove_twins_from_smartlist(sl,router_get_my_routerinfo());
smartlist_subtract(sl,excludednodes);
choice = smartlist_choose(sl);
smartlist_free(sl);
}
smartlist_free(excludednodes);
if(!choice) {
log_fn(LOG_WARN,"No acceptable routers while picking entry node. Discarding this circuit.");
return -1;
}
choice = choose_good_entry_server(state);
} else {
log_fn(LOG_DEBUG, "Contemplating intermediate hop: random choice.");
sl = smartlist_create();
router_add_running_routers_to_smartlist(sl);
remove_twins_from_smartlist(sl,router_get_by_digest(state->chosen_exit_digest));
remove_twins_from_smartlist(sl,router_get_my_routerinfo());
for (i = 0, cpath = *head_ptr; i < cur_len; ++i, cpath=cpath->next) {
r = router_get_by_digest(cpath->identity_digest);
tor_assert(r);
remove_twins_from_smartlist(sl,r);
}
smartlist_subtract(sl,excludednodes);
choice = smartlist_choose(sl);
smartlist_free(sl);
smartlist_free(excludednodes);
if(!choice) {
log_fn(LOG_WARN,"No acceptable routers while picking intermediate node. Discarding this circuit.");
return -1;
}
choice = choose_good_middle_server(state, *head_ptr, cur_len);
}
smartlist_free(excludednodes);
if(!choice) {
log_fn(LOG_WARN,"Failed to find node for hop %d of our path. Discarding this circuit.", cur_len);
return -1;
}
log_fn(LOG_DEBUG,"Chose router %s for hop %d (exit is %s)",
......
......@@ -207,6 +207,8 @@ static int config_assign(or_options_t *options, struct config_line_t *list) {
config_compare(list, "ExitPolicy", CONFIG_TYPE_LINELIST, &options->ExitPolicy) ||
config_compare(list, "ExcludeNodes", CONFIG_TYPE_STRING, &options->ExcludeNodes) ||
config_compare(list, "FascistFirewall",CONFIG_TYPE_BOOL, &options->FascistFirewall) ||
config_compare(list, "Group", CONFIG_TYPE_STRING, &options->Group) ||
config_compare(list, "IgnoreVersion", CONFIG_TYPE_BOOL, &options->IgnoreVersion) ||
......@@ -885,6 +887,7 @@ int config_init_logs(or_options_t *options)
return 0;
}
/** XXX008 DOCDOC */
void
config_parse_exit_policy(struct config_line_t *cfg,
struct exit_policy_t **dest)
......
......@@ -419,7 +419,7 @@ static int decide_if_publishable_server(time_t now) {
int bw;
bw = rep_hist_bandwidth_assess();
router_set_advertised_bandwidth(bw);
router_set_bandwidth_capacity(bw);
if(options.ClientOnly)
return 0;
......
......@@ -409,6 +409,10 @@
#define CELL_RELAY 3
#define CELL_DESTROY 4
/* people behind fascist firewalls use only these ports */
#define REQUIRED_FIREWALL_DIRPORT 80
#define REQUIRED_FIREWALL_ORPORT 443
/* legal characters in a nickname */
#define LEGAL_NICKNAME_CHARACTERS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
......@@ -581,9 +585,10 @@ typedef struct {
* bucket per second? */
uint32_t bandwidthburst; /**< How large is this OR's token bucket? */
/** How many bytes/s is this router known to handle? */
uint32_t advertisedbandwidth;
uint32_t bandwidthcapacity;
struct exit_policy_t *exit_policy; /**< What streams will this OR permit
* to exit? */
int uptime; /**< How many seconds the router claims to have been up */
/* local info */
int is_running; /**< As far as we know, is this OR currently running? */
time_t status_set_at; /**< When did we last update is_running? */
......@@ -852,6 +857,7 @@ typedef struct {
int IgnoreVersion; /**< If true, run no matter what versions of Tor the
* directory recommends. */
int RunAsDaemon; /**< If true, run in the background. (Unix only) */
int FascistFirewall; /**< Whether to prefer ORs reachable on 80/443. */
int DirFetchPostPeriod; /**< How often do we fetch new directories
* and post server descriptros to the directory
* server? */
......@@ -1351,8 +1357,8 @@ 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_advertised_bandwidth(int bw);
int router_get_advertised_bandwidth(void);
void router_set_bandwidth_capacity(int bw);
int router_get_bandwidth_capacity(void);
void router_retry_connections(void);
int router_is_clique_mode(routerinfo_t *router);
......@@ -1374,7 +1380,8 @@ void add_nickname_list_to_smartlist(struct smartlist_t *sl, const char *list);
void router_add_running_routers_to_smartlist(struct smartlist_t *sl);
int router_nickname_matches(routerinfo_t *router, const char *nickname);
routerinfo_t *router_choose_random_node(char *preferred, char *excluded,
struct smartlist_t *excludedsmartlist);
struct smartlist_t *excludedsmartlist,
int preferuptime, int preferbandwidth);
routerinfo_t *router_get_by_addr_port(uint32_t addr, uint16_t port);
routerinfo_t *router_get_by_nickname(const char *nickname);
routerinfo_t *router_get_by_hexdigest(const char *hexdigest);
......
......@@ -492,15 +492,34 @@ connection_edge_process_relay_cell_not_open(
connection_t *conn, crypt_path_t *layer_hint) {
uint32_t addr;
int reason;
routerinfo_t *exitrouter;
if(rh->command == RELAY_COMMAND_END) {
reason = *(cell->payload+RELAY_HEADER_SIZE);
/* We have to check this here, since we aren't connected yet. */
if (rh->length >= 5 && reason == END_STREAM_REASON_EXITPOLICY) {
if(conn->type != CONN_TYPE_AP) {
log_fn(LOG_WARN,"Got an end because of exitpolicy, but we're not an AP. Closing.");
return -1;
}
log_fn(LOG_INFO,"Address %s refused due to exit policy. Retrying.",
conn->socks_request->address);
addr = ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE+1));
client_dns_set_entry(conn->socks_request->address, addr);
/* check if he *ought* to have allowed it */
exitrouter = router_get_by_digest(circ->build_state->chosen_exit_digest);
if(!exitrouter) {
log_fn(LOG_INFO,"Skipping broken circ (exit router vanished)");
return 0; /* this circuit is screwed and doesn't know it yet */
}
if(connection_ap_can_use_exit(conn, exitrouter)) {
log_fn(LOG_WARN,"Exitrouter %s seems to be more restrictive than its exit policy. Not using this router as exit for now,", exitrouter->nickname);
exit_policy_free(exitrouter->exit_policy);
exitrouter->exit_policy =
router_parse_exit_policy_from_string("reject *:*");
}
conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
circuit_detach_stream(circ,conn);
if(connection_ap_handshake_attach_circuit(conn) >= 0)
......
......@@ -822,7 +822,7 @@ void rend_services_introduce(void) {
for (j=prev_intro_nodes; j < NUM_INTRO_POINTS; ++j) {
router = router_choose_random_node(service->intro_prefer_nodes,
service->intro_exclude_nodes,
exclude_routers);
exclude_routers, 1, 0);
if (!router) {
log_fn(LOG_WARN, "Could only establish %d introduction points for %s",
smartlist_len(service->intro_nodes), service->service_id);
......
......@@ -132,16 +132,16 @@ void rotate_onion_key(void)
}
/** The latest calculated bandwidth usage for our node. */
static int advertised_bw = 0;
static int bw_capacity = 0;
/** Tuck <b>bw</b> away so we can produce it when somebody
* calls router_get_advertised_bandwidth() below.
* calls router_get_bandwidth_capacity() below.
*/
void router_set_advertised_bandwidth(int bw) {
advertised_bw = bw;
void router_set_bandwidth_capacity(int bw) {
bw_capacity = bw;
}
/** Return the value we tucked away above, or zero by default. */
int router_get_advertised_bandwidth(void) {
return advertised_bw;
int router_get_bandwidth_capacity(void) {
return bw_capacity;
}
/* Read an RSA secret key key from a file that was once named fname_old,
......@@ -535,7 +535,7 @@ int router_rebuild_descriptor(void) {
ri->platform = tor_strdup(platform);
ri->bandwidthrate = options.BandwidthRate;
ri->bandwidthburst = options.BandwidthBurst;
ri->advertisedbandwidth = router_get_advertised_bandwidth();
ri->bandwidthcapacity = router_get_bandwidth_capacity();
ri->exit_policy = NULL; /* zero it out first */
router_add_exit_policy_from_config(ri);
ri->is_trusted_dir = authdir_mode();
......@@ -652,7 +652,7 @@ int router_dump_router_to_string(char *s, int maxlen, routerinfo_t *router,
stats_n_seconds_uptime,
(int) router->bandwidthrate,
(int) router->bandwidthburst,
(int) router->advertisedbandwidth,
(int) router->bandwidthcapacity,
onion_pkey, identity_pkey,
bandwidth_usage);
......
......@@ -59,7 +59,8 @@ routerinfo_t *router_pick_directory_server(int requireauth, int requireothers) {
if(choice)
return choice;
log_fn(LOG_WARN,"No dirservers known. Reloading and trying again.");
log_fn(LOG_WARN,"Still no dirservers %s. Reloading and trying again.",
options.FascistFirewall ? "reachable" : "known");
has_fetched_directory=0; /* reset it */
routerlist_clear_trusted_directories();
if(options.RouterFile) {
......@@ -97,6 +98,9 @@ router_pick_directory_server_impl(int requireauth, int requireothers)
continue;
if(requireothers && router_is_me(router))
continue;
if(options.FascistFirewall &&
router->dir_port != REQUIRED_FIREWALL_DIRPORT)
continue;
smartlist_add(sl, router);
}
......@@ -206,13 +210,78 @@ void router_add_running_routers_to_smartlist(smartlist_t *sl) {
}
}
/** How many seconds a router must be up before we'll use it for
* reliability-critical node positions.
*/
#define ROUTER_REQUIRED_MIN_UPTIME 0
/* XXX008 change this once we parse router->uptime */
static void
routerlist_sl_remove_unreliable_routers(smartlist_t *sl)
{
int i;
routerinfo_t *router;
for (i = 0; i < smartlist_len(sl); ++i) {
router = smartlist_get(sl, i);
if(router->uptime < ROUTER_REQUIRED_MIN_UPTIME) {
log(LOG_DEBUG, "Router %s has insufficient uptime; deleting.",
router->nickname);
smartlist_del(sl, i--);
}
}
}
static routerinfo_t *
routerlist_sl_choose_by_bandwidth(smartlist_t *sl)
{
int i;
routerinfo_t *router;
smartlist_t *bandwidths;
uint32_t this_bw, tmp, total_bw=0, rand_bw;
uint32_t *p;
bandwidths = smartlist_create();
for (i = 0; i < smartlist_len(sl); ++i) {
router = smartlist_get(sl, i);
/* give capacity a default, until 0.0.7 is obsolete */
tmp = (router->bandwidthcapacity == 0) ? 200000 : router->bandwidthcapacity;
this_bw = (tmp < router->bandwidthrate) ? tmp : router->bandwidthrate;
p = tor_malloc(sizeof(uint32_t));
*p = this_bw;
smartlist_add(bandwidths, p);
total_bw += this_bw;
// log_fn(LOG_INFO,"Recording bw %d for node %s.", this_bw, router->nickname);
}
if(!total_bw)
return NULL;
rand_bw = crypto_pseudo_rand_int(total_bw);
// log_fn(LOG_INFO,"Total bw %d. Randomly chose %d.", total_bw, rand_bw);
tmp = 0;
for(i=0; ; i++) {
tor_assert(i < smartlist_len(sl));
p = smartlist_get(bandwidths, i);
tmp += *p;
router = smartlist_get(sl, i);
// log_fn(LOG_INFO,"Considering %s. tmp = %d.", router->nickname, tmp);
if(tmp >= rand_bw)
break;
}
SMARTLIST_FOREACH(bandwidths, uint32_t*, p, tor_free(p));
smartlist_free(bandwidths);
router = smartlist_get(sl, i);
log_fn(LOG_INFO,"Picked %s.", router->nickname);
return router;
}
/** Return a random running router from the routerlist. If any node
* named in <b>preferred</b> is available, pick one of those. Never pick a
* node named in <b>excluded</b>, or whose routerinfo is in
* <b>excludedsmartlist</b>, even if they are the only nodes available.
*/
routerinfo_t *router_choose_random_node(char *preferred, char *excluded,
smartlist_t *excludedsmartlist)
smartlist_t *excludedsmartlist,
int preferuptime, int preferbandwidth)
{
smartlist_t *sl, *excludednodes;
routerinfo_t *choice;
......@@ -220,13 +289,18 @@ routerinfo_t *router_choose_random_node(char *preferred, char *excluded,
excludednodes = smartlist_create();
add_nickname_list_to_smartlist(excludednodes,excluded);
/* try the nodes in RendNodes first */
/* try the preferred nodes first */
sl = smartlist_create();
add_nickname_list_to_smartlist(sl,preferred);
smartlist_subtract(sl,excludednodes);
if(excludedsmartlist)
smartlist_subtract(sl,excludedsmartlist);
choice = smartlist_choose(sl);
if(preferuptime)
routerlist_sl_remove_unreliable_routers(sl);
if(preferbandwidth)
choice = routerlist_sl_choose_by_bandwidth(sl);
else
choice = smartlist_choose(sl);
smartlist_free(sl);
if(!choice) {
sl = smartlist_create();
......@@ -234,7 +308,10 @@ routerinfo_t *router_choose_random_node(char *preferred, char *excluded,
smartlist_subtract(sl,excludednodes);
if(excludedsmartlist)
smartlist_subtract(sl,excludedsmartlist);
choice = smartlist_choose(sl);
if(preferuptime)
routerlist_sl_remove_unreliable_routers(sl);
if(preferbandwidth)
choice = routerlist_sl_choose_by_bandwidth(sl);
smartlist_free(sl);
}
smartlist_free(excludednodes);
......
......@@ -741,7 +741,7 @@ routerinfo_t *router_parse_entry_from_string(const char *s,
router->bandwidthrate = atoi(tok->args[0]);
router->bandwidthburst = atoi(tok->args[1]);
if(tok->n_args > 2)
router->advertisedbandwidth = atoi(tok->args[2]);
router->bandwidthcapacity = atoi(tok->args[2]);
bw_set = 1;
}
......@@ -903,6 +903,7 @@ static int router_add_exit_policy(routerinfo_t *router,directory_token_t *tok)
/** Given a K_ACCEPT or K_REJECT token and a router, create a new exit_policy_t
* corresponding to the token, and add it to <b>router</b> */
/* XXX008 NICK DOCDOC: there is no <b>router</b> */
static struct exit_policy_t *
router_parse_exit_policy(directory_token_t *tok) {
......
......@@ -737,7 +737,7 @@ test_dir_format()
r1.identity_pkey = pk2;
r1.bandwidthrate = 1000;
r1.bandwidthburst = 5000;
r1.advertisedbandwidth = 10000;
r1.bandwidthcapacity = 10000;
r1.exit_policy = NULL;
r1.nickname = "Magri";
r1.platform = tor_strdup(platform);
......@@ -762,7 +762,7 @@ test_dir_format()
r2.dir_port = 0;
r2.onion_pkey = pk2;
r2.identity_pkey = pk1;
r2.bandwidthrate = r2.bandwidthburst = r2.advertisedbandwidth = 3000;
r2.bandwidthrate = r2.bandwidthburst = r2.bandwidthcapacity = 3000;
r2.exit_policy = &ex1;
r2.nickname = "Fred";
......@@ -815,7 +815,7 @@ test_dir_format()
test_eq(rp1->dir_port, r1.dir_port);
test_eq(rp1->bandwidthrate, r1.bandwidthrate);
test_eq(rp1->bandwidthburst, r1.bandwidthburst);
test_eq(rp1->advertisedbandwidth, r1.advertisedbandwidth);
test_eq(rp1->bandwidthcapacity, r1.bandwidthcapacity);
test_assert(crypto_pk_cmp_keys(rp1->onion_pkey, pk1) == 0);
test_assert(crypto_pk_cmp_keys(rp1->identity_pkey, pk2) == 0);
test_assert(rp1->exit_policy == NULL);
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment