diff options
Diffstat (limited to 'doc/spec/proposals/156-tracking-blocked-ports.txt')
-rw-r--r-- | doc/spec/proposals/156-tracking-blocked-ports.txt | 527 |
1 files changed, 0 insertions, 527 deletions
diff --git a/doc/spec/proposals/156-tracking-blocked-ports.txt b/doc/spec/proposals/156-tracking-blocked-ports.txt deleted file mode 100644 index 419de7e74c..0000000000 --- a/doc/spec/proposals/156-tracking-blocked-ports.txt +++ /dev/null @@ -1,527 +0,0 @@ -Filename: 156-tracking-blocked-ports.txt -Title: Tracking blocked ports on the client side -Author: Robert Hogan -Created: 14-Oct-2008 -Status: Open -Target: 0.2.? - -Motivation: -Tor clients that are behind extremely restrictive firewalls can end up -waiting a while for their first successful OR connection to a node on the -network. Worse, the more restrictive their firewall the more susceptible -they are to an attacker guessing their entry nodes. Tor routers that -are behind extremely restrictive firewalls can only offer a limited, -'partitioned' service to other routers and clients on the network. Exit -nodes behind extremely restrictive firewalls may advertise ports that they -are actually not able to connect to, wasting network resources in circuit -constructions that are doomed to fail at the last hop on first use. - -Proposal: - -When a client attempts to connect to an entry guard it should avoid -further attempts on ports that fail once until it has connected to at -least one entry guard successfully. (Maybe it should wait for more than -one failure to reduce the skew on the first node selection.) Thereafter -it should select entry guards regardless of port and warn the user if -it observes that connections to a given port have failed every multiple -of 5 times without success or since the last success. - -Tor should warn the operators of exit, middleman and entry nodes if it -observes that connections to a given port have failed a multiple of 5 -times without success or since the last success. If attempts on a port -fail 20 or more times without or since success, Tor should add the port -to a 'blocked-ports' entry in its descriptor's extra-info. Some thought -needs to be given to what the authorities might do with this information. - -Related TODO item: - "- Automatically determine what ports are reachable and start using - those, if circuits aren't working and it's a pattern we - recognize ("port 443 worked once and port 9001 keeps not - working")." - - -I've had a go at implementing all of this in the attached. - -Addendum: -Just a note on the patch, storing the digest of each router that uses the port -is a bit of a memory hog, and its only real purpose is to provide a count of -routers using that port when warning the user. That could be achieved when -warning the user by iterating through the routerlist instead. - -Index: src/or/connection_or.c -=================================================================== ---- src/or/connection_or.c (revision 17104) -+++ src/or/connection_or.c (working copy) -@@ -502,6 +502,9 @@ - connection_or_connect_failed(or_connection_t *conn, - int reason, const char *msg) - { -+ if ((reason == END_OR_CONN_REASON_NO_ROUTE) || -+ (reason == END_OR_CONN_REASON_REFUSED)) -+ or_port_hist_failure(conn->identity_digest,TO_CONN(conn)->port); - control_event_or_conn_status(conn, OR_CONN_EVENT_FAILED, reason); - if (!authdir_mode_tests_reachability(get_options())) - control_event_bootstrap_problem(msg, reason); -@@ -580,6 +583,7 @@ - /* already marked for close */ - return NULL; - } -+ - return conn; - } - -@@ -909,6 +913,7 @@ - control_event_or_conn_status(conn, OR_CONN_EVENT_CONNECTED, 0); - - if (started_here) { -+ or_port_hist_success(TO_CONN(conn)->port); - rep_hist_note_connect_succeeded(conn->identity_digest, now); - if (entry_guard_register_connect_status(conn->identity_digest, - 1, now) < 0) { -Index: src/or/rephist.c -=================================================================== ---- src/or/rephist.c (revision 17104) -+++ src/or/rephist.c (working copy) -@@ -18,6 +18,7 @@ - static void bw_arrays_init(void); - static void predicted_ports_init(void); - static void hs_usage_init(void); -+static void or_port_hist_init(void); - - /** Total number of bytes currently allocated in fields used by rephist.c. */ - uint64_t rephist_total_alloc=0; -@@ -89,6 +90,25 @@ - digestmap_t *link_history_map; - } or_history_t; - -+/** or_port_hist_t contains our router/client's knowledge of -+ all OR ports offered on the network, and how many servers with each port we -+ have succeeded or failed to connect to. */ -+typedef struct { -+ /** The port this entry is tracking. */ -+ uint16_t or_port; -+ /** Have we ever connected to this port on another OR?. */ -+ unsigned int success:1; -+ /** The ORs using this port. */ -+ digestmap_t *ids; -+ /** The ORs using this port we have failed to connect to. */ -+ digestmap_t *failure_ids; -+ /** Are we excluding ORs with this port during entry selection?*/ -+ unsigned int excluded; -+} or_port_hist_t; -+ -+static unsigned int still_searching = 0; -+static smartlist_t *or_port_hists; -+ - /** When did we last multiply all routers' weighted_run_length and - * total_run_weights by STABILITY_ALPHA? */ - static time_t stability_last_downrated = 0; -@@ -164,6 +184,16 @@ - tor_free(hist); - } - -+/** Helper: free storage held by a single OR port history entry. */ -+static void -+or_port_hist_free(or_port_hist_t *p) -+{ -+ tor_assert(p); -+ digestmap_free(p->ids,NULL); -+ digestmap_free(p->failure_ids,NULL); -+ tor_free(p); -+} -+ - /** Update an or_history_t object <b>hist</b> so that its uptime/downtime - * count is up-to-date as of <b>when</b>. - */ -@@ -1639,7 +1669,7 @@ - tmp_time = smartlist_get(predicted_ports_times, i); - if (*tmp_time + PREDICTED_CIRCS_RELEVANCE_TIME < now) { - tmp_port = smartlist_get(predicted_ports_list, i); -- log_debug(LD_CIRC, "Expiring predicted port %d", *tmp_port); -+ log_debug(LD_HIST, "Expiring predicted port %d", *tmp_port); - smartlist_del(predicted_ports_list, i); - smartlist_del(predicted_ports_times, i); - rephist_total_alloc -= sizeof(uint16_t)+sizeof(time_t); -@@ -1821,6 +1851,12 @@ - tor_free(last_stability_doc); - built_last_stability_doc_at = 0; - predicted_ports_free(); -+ if (or_port_hists) { -+ SMARTLIST_FOREACH(or_port_hists, or_port_hist_t *, p, -+ or_port_hist_free(p)); -+ smartlist_free(or_port_hists); -+ or_port_hists = NULL; -+ } - } - - /****************** hidden service usage statistics ******************/ -@@ -2356,3 +2392,225 @@ - tor_free(fname); - } - -+/** Create a new entry in the port tracking cache for the or_port in -+ * <b>ri</b>. */ -+void -+or_port_hist_new(const routerinfo_t *ri) -+{ -+ or_port_hist_t *result; -+ const char *id=ri->cache_info.identity_digest; -+ -+ if (!or_port_hists) -+ or_port_hist_init(); -+ -+ SMARTLIST_FOREACH(or_port_hists, or_port_hist_t *, tp, -+ { -+ /* Cope with routers that change their advertised OR port or are -+ dropped from the networkstatus. We don't discard the failures of -+ dropped routers because they are still valid when counting -+ consecutive failures on a port.*/ -+ if (digestmap_get(tp->ids, id) && (tp->or_port != ri->or_port)) { -+ digestmap_remove(tp->ids, id); -+ } -+ if (tp->or_port == ri->or_port) { -+ if (!(digestmap_get(tp->ids, id))) -+ digestmap_set(tp->ids, id, (void*)1); -+ return; -+ } -+ }); -+ -+ result = tor_malloc_zero(sizeof(or_port_hist_t)); -+ result->or_port=ri->or_port; -+ result->success=0; -+ result->ids=digestmap_new(); -+ digestmap_set(result->ids, id, (void*)1); -+ result->failure_ids=digestmap_new(); -+ result->excluded=0; -+ smartlist_add(or_port_hists, result); -+} -+ -+/** Create the port tracking cache. */ -+/*XXX: need to call this when we rebuild/update our network status */ -+static void -+or_port_hist_init(void) -+{ -+ routerlist_t *rl = router_get_routerlist(); -+ -+ if (!or_port_hists) -+ or_port_hists=smartlist_create(); -+ -+ if (rl && rl->routers) { -+ SMARTLIST_FOREACH(rl->routers, routerinfo_t *, ri, -+ { -+ or_port_hist_new(ri); -+ }); -+ } -+} -+ -+#define NOT_BLOCKED 0 -+#define FAILURES_OBSERVED 1 -+#define POSSIBLY_BLOCKED 5 -+#define PROBABLY_BLOCKED 10 -+/** Return the list of blocked ports for our router's extra-info.*/ -+char * -+or_port_hist_get_blocked_ports(void) -+{ -+ char blocked_ports[2048]; -+ char *bp; -+ -+ tor_snprintf(blocked_ports,sizeof(blocked_ports),"blocked-ports"); -+ SMARTLIST_FOREACH(or_port_hists, or_port_hist_t *, tp, -+ { -+ if (digestmap_size(tp->failure_ids) >= PROBABLY_BLOCKED) -+ tor_snprintf(blocked_ports+strlen(blocked_ports), -+ sizeof(blocked_ports)," %u,",tp->or_port); -+ }); -+ if (strlen(blocked_ports) == 13) -+ return NULL; -+ bp=tor_strdup(blocked_ports); -+ bp[strlen(bp)-1]='\n'; -+ bp[strlen(bp)]='\0'; -+ return bp; -+} -+ -+/** Revert to client-only mode if we have seen to many failures on a port or -+ * range of ports.*/ -+static void -+or_port_hist_report_block(unsigned int min_severity) -+{ -+ or_options_t *options=get_options(); -+ char failures_observed[2048],possibly_blocked[2048],probably_blocked[2048]; -+ char port[1024]; -+ -+ memset(failures_observed,0,sizeof(failures_observed)); -+ memset(possibly_blocked,0,sizeof(possibly_blocked)); -+ memset(probably_blocked,0,sizeof(probably_blocked)); -+ -+ SMARTLIST_FOREACH(or_port_hists, or_port_hist_t *, tp, -+ { -+ unsigned int failures = digestmap_size(tp->failure_ids); -+ if (failures >= min_severity) { -+ tor_snprintf(port, sizeof(port), " %u (%u failures %s out of %u on the" -+ " network)",tp->or_port,failures, -+ (!tp->success)?"and no successes": "since last success", -+ digestmap_size(tp->ids)); -+ if (failures >= PROBABLY_BLOCKED) { -+ strlcat(probably_blocked, port, sizeof(probably_blocked)); -+ } else if (failures >= POSSIBLY_BLOCKED) -+ strlcat(possibly_blocked, port, sizeof(possibly_blocked)); -+ else if (failures >= FAILURES_OBSERVED) -+ strlcat(failures_observed, port, sizeof(failures_observed)); -+ } -+ }); -+ -+ log_warn(LD_HIST,"%s%s%s%s%s%s%s%s", -+ server_mode(options) && -+ ((min_severity==FAILURES_OBSERVED) || strlen(probably_blocked))? -+ "You should consider disabling your Tor server.":"", -+ (min_severity==FAILURES_OBSERVED)? -+ "Tor appears to be blocked from connecting to a range of ports " -+ "with the result that it cannot connect to one tenth of the Tor " -+ "network. ":"", -+ strlen(failures_observed)? -+ "Tor has observed failures on the following ports: ":"", -+ failures_observed, -+ strlen(possibly_blocked)? -+ "Tor is possibly blocked on the following ports: ":"", -+ possibly_blocked, -+ strlen(probably_blocked)? -+ "Tor is almost certainly blocked on the following ports: ":"", -+ probably_blocked); -+ -+} -+ -+/** Record the success of our connection to <b>digest</b>'s -+ * OR port. */ -+void -+or_port_hist_success(uint16_t or_port) -+{ -+ SMARTLIST_FOREACH(or_port_hists, or_port_hist_t *, tp, -+ { -+ if (tp->or_port != or_port) -+ continue; -+ /*Reset our failure stats so we can notice if this port ever gets -+ blocked again.*/ -+ tp->success=1; -+ if (digestmap_size(tp->failure_ids)) { -+ digestmap_free(tp->failure_ids,NULL); -+ tp->failure_ids=digestmap_new(); -+ } -+ if (still_searching) { -+ still_searching=0; -+ SMARTLIST_FOREACH(or_port_hists,or_port_hist_t *,t,t->excluded=0;); -+ } -+ return; -+ }); -+} -+/** Record the failure of our connection to <b>digest</b>'s -+ * OR port. Warn, exclude the port from future entry guard selection, or -+ * add port to blocked-ports in our server's extra-info as appropriate. */ -+void -+or_port_hist_failure(const char *digest, uint16_t or_port) -+{ -+ int total_failures=0, ports_excluded=0, report_block=0; -+ int total_routers=smartlist_len(router_get_routerlist()->routers); -+ -+ SMARTLIST_FOREACH(or_port_hists, or_port_hist_t *, tp, -+ { -+ ports_excluded += tp->excluded; -+ total_failures+=digestmap_size(tp->failure_ids); -+ if (tp->or_port != or_port) -+ continue; -+ /* We're only interested in unique failures */ -+ if (digestmap_get(tp->failure_ids, digest)) -+ return; -+ -+ total_failures++; -+ digestmap_set(tp->failure_ids, digest, (void*)1); -+ if (still_searching && !tp->success) { -+ tp->excluded=1; -+ ports_excluded++; -+ } -+ if ((digestmap_size(tp->ids) >= POSSIBLY_BLOCKED) && -+ !(digestmap_size(tp->failure_ids) % POSSIBLY_BLOCKED)) -+ report_block=POSSIBLY_BLOCKED; -+ }); -+ -+ if (total_failures >= (int)(total_routers/10)) -+ or_port_hist_report_block(FAILURES_OBSERVED); -+ else if (report_block) -+ or_port_hist_report_block(report_block); -+ -+ if (ports_excluded >= smartlist_len(or_port_hists)) { -+ log_warn(LD_HIST,"During entry node selection Tor tried every port " -+ "offered on the network on at least one server " -+ "and didn't manage a single " -+ "successful connection. This suggests you are behind an " -+ "extremely restrictive firewall. Tor will keep trying to find " -+ "a reachable entry node."); -+ SMARTLIST_FOREACH(or_port_hists, or_port_hist_t *, tp, tp->excluded=0;); -+ } -+} -+ -+/** Add any ports marked as excluded in or_port_hist_t to <b>rt</b> */ -+void -+or_port_hist_exclude(routerset_t *rt) -+{ -+ SMARTLIST_FOREACH(or_port_hists, or_port_hist_t *, tp, -+ { -+ char portpolicy[9]; -+ if (tp->excluded) { -+ tor_snprintf(portpolicy,sizeof(portpolicy),"*:%u", tp->or_port); -+ log_warn(LD_HIST,"Port %u may be blocked, excluding it temporarily " -+ "from entry guard selection.", tp->or_port); -+ routerset_parse(rt, portpolicy, "Ports"); -+ } -+ }); -+} -+ -+/** Allow the exclusion of ports during our search for an entry node. */ -+void -+or_port_hist_search_again(void) -+{ -+ still_searching=1; -+} -Index: src/or/or.h -=================================================================== ---- src/or/or.h (revision 17104) -+++ src/or/or.h (working copy) -@@ -3864,6 +3864,13 @@ - int any_predicted_circuits(time_t now); - int rep_hist_circbuilding_dormant(time_t now); - -+void or_port_hist_failure(const char *digest, uint16_t or_port); -+void or_port_hist_success(uint16_t or_port); -+void or_port_hist_new(const routerinfo_t *ri); -+void or_port_hist_exclude(routerset_t *rt); -+void or_port_hist_search_again(void); -+char *or_port_hist_get_blocked_ports(void); -+ - /** Possible public/private key operations in Tor: used to keep track of where - * we're spending our time. */ - typedef enum { -Index: src/or/routerparse.c -=================================================================== ---- src/or/routerparse.c (revision 17104) -+++ src/or/routerparse.c (working copy) -@@ -1401,6 +1401,8 @@ - goto err; - } - -+ or_port_hist_new(router); -+ - if (!router->platform) { - router->platform = tor_strdup("<unknown>"); - } -Index: src/or/router.c -=================================================================== ---- src/or/router.c (revision 17104) -+++ src/or/router.c (working copy) -@@ -1818,6 +1818,7 @@ - char published[ISO_TIME_LEN+1]; - char digest[DIGEST_LEN]; - char *bandwidth_usage; -+ char *blocked_ports; - int result; - size_t len; - -@@ -1825,7 +1826,6 @@ - extrainfo->cache_info.identity_digest, DIGEST_LEN); - format_iso_time(published, extrainfo->cache_info.published_on); - bandwidth_usage = rep_hist_get_bandwidth_lines(1); -- - result = tor_snprintf(s, maxlen, - "extra-info %s %s\n" - "published %s\n%s", -@@ -1835,6 +1835,16 @@ - if (result<0) - return -1; - -+ blocked_ports = or_port_hist_get_blocked_ports(); -+ if (blocked_ports) { -+ result = tor_snprintf(s+strlen(s), maxlen-strlen(s), -+ "%s", -+ blocked_ports); -+ tor_free(blocked_ports); -+ if (result<0) -+ return -1; -+ } -+ - if (should_record_bridge_info(options)) { - static time_t last_purged_at = 0; - char *geoip_summary; -Index: src/or/circuitbuild.c -=================================================================== ---- src/or/circuitbuild.c (revision 17104) -+++ src/or/circuitbuild.c (working copy) -@@ -62,6 +62,7 @@ - - static void entry_guards_changed(void); - static time_t start_of_month(time_t when); -+static int num_live_entry_guards(void); - - /** Iterate over values of circ_id, starting from conn-\>next_circ_id, - * and with the high bit specified by conn-\>circ_id_type, until we get -@@ -1627,12 +1628,14 @@ - smartlist_t *excluded; - or_options_t *options = get_options(); - router_crn_flags_t flags = 0; -+ routerset_t *_ExcludeNodes; - - if (state && options->UseEntryGuards && - (purpose != CIRCUIT_PURPOSE_TESTING || options->BridgeRelay)) { - return choose_random_entry(state); - } - -+ _ExcludeNodes = routerset_new(); - excluded = smartlist_create(); - - if (state && (r = build_state_get_exit_router(state))) { -@@ -1670,12 +1673,18 @@ - if (options->_AllowInvalid & ALLOW_INVALID_ENTRY) - flags |= CRN_ALLOW_INVALID; - -+ if (options->ExcludeNodes) -+ routerset_union(_ExcludeNodes,options->ExcludeNodes); -+ -+ or_port_hist_exclude(_ExcludeNodes); -+ - choice = router_choose_random_node( - NULL, - excluded, -- options->ExcludeNodes, -+ _ExcludeNodes, - flags); - smartlist_free(excluded); -+ routerset_free(_ExcludeNodes); - return choice; - } - -@@ -2727,6 +2736,7 @@ - entry_guards_update_state(or_state_t *state) - { - config_line_t **next, *line; -+ unsigned int have_reachable_entry=0; - if (! entry_guards_dirty) - return; - -@@ -2740,6 +2750,7 @@ - char dbuf[HEX_DIGEST_LEN+1]; - if (!e->made_contact) - continue; /* don't write this one to disk */ -+ have_reachable_entry=1; - *next = line = tor_malloc_zero(sizeof(config_line_t)); - line->key = tor_strdup("EntryGuard"); - line->value = tor_malloc(HEX_DIGEST_LEN+MAX_NICKNAME_LEN+2); -@@ -2785,6 +2796,11 @@ - if (!get_options()->AvoidDiskWrites) - or_state_mark_dirty(get_or_state(), 0); - entry_guards_dirty = 0; -+ -+ /* XXX: Is this the place to decide that we no longer have any reachable -+ guards? */ -+ if (!have_reachable_entry) -+ or_port_hist_search_again(); - } - - /** If <b>question</b> is the string "entry-guards", then dump - |