Make the DH parameter we use for TLS match the one from Apache's mod_ssl
[tor/rransom.git] / doc / spec / proposals / 156-tracking-blocked-ports.txt
blob1e7b0d963f79962821c98516a3f8f96273b8b06e
1 Filename: 156-tracking-blocked-ports.txt
2 Title: Tracking blocked ports on the client side
3 Version: $Revision$
4 Last-Modified: $Date$
5 Author: Robert Hogan
6 Created: 14-Oct-2008
7 Status: Open
8 Target: 0.2.?
10 Motivation:
11 Tor clients that are behind extremely restrictive firewalls can end up
12 waiting a while for their first successful OR connection to a node on the
13 network.  Worse, the more restrictive their firewall the more susceptible
14 they are to an attacker guessing their entry nodes. Tor routers that
15 are behind extremely restrictive firewalls can only offer a limited,
16 'partitioned' service to other routers and clients on the network. Exit
17 nodes behind extremely restrictive firewalls may advertise ports that they
18 are actually not able to connect to, wasting network resources in circuit
19 constructions that are doomed to fail at the last hop on first use.
21 Proposal:
23 When a client attempts to connect to an entry guard it should avoid
24 further attempts on ports that fail once until it has connected to at
25 least one entry guard successfully. (Maybe it should wait for more than
26 one failure to reduce the skew on the first node selection.) Thereafter
27 it should select entry guards regardless of port and warn the user if
28 it observes that connections to a given port have failed every multiple
29 of 5 times without success or since the last success.
31 Tor should warn the operators of exit, middleman and entry nodes if it
32 observes that connections to a given port have failed a multiple of 5
33 times without success or since the last success. If attempts on a port
34 fail 20 or more times without or since success, Tor should add the port
35 to a 'blocked-ports' entry in its descriptor's extra-info. Some thought
36 needs to be given to what the authorities might do with this information.
38 Related TODO item:
39     "- Automatically determine what ports are reachable and start using
40       those, if circuits aren't working and it's a pattern we
41       recognize ("port 443 worked once and port 9001 keeps not
42       working")."
45 I've had a go at implementing all of this in the attached.
47 Addendum:
48 Just a note on the patch, storing the digest of each router that uses the port
49 is a bit of a memory hog, and its only real purpose is to provide a count of
50 routers using that port when warning the user. That could be achieved when
51 warning the user by iterating through the routerlist instead.
53 Index: src/or/connection_or.c
54 ===================================================================
55 --- src/or/connection_or.c      (revision 17104)
56 +++ src/or/connection_or.c      (working copy)
57 @@ -502,6 +502,9 @@
58  connection_or_connect_failed(or_connection_t *conn,
59                               int reason, const char *msg)
60  {
61 +  if ((reason == END_OR_CONN_REASON_NO_ROUTE) ||
62 +      (reason == END_OR_CONN_REASON_REFUSED))
63 +    or_port_hist_failure(conn->identity_digest,TO_CONN(conn)->port);
64    control_event_or_conn_status(conn, OR_CONN_EVENT_FAILED, reason);
65    if (!authdir_mode_tests_reachability(get_options()))
66      control_event_bootstrap_problem(msg, reason);
67 @@ -580,6 +583,7 @@
68      /* already marked for close */
69      return NULL;
70    }
72    return conn;
73  }
75 @@ -909,6 +913,7 @@
76    control_event_or_conn_status(conn, OR_CONN_EVENT_CONNECTED, 0);
78    if (started_here) {
79 +    or_port_hist_success(TO_CONN(conn)->port);
80      rep_hist_note_connect_succeeded(conn->identity_digest, now);
81      if (entry_guard_register_connect_status(conn->identity_digest,
82                                              1, now) < 0) {
83 Index: src/or/rephist.c
84 ===================================================================
85 --- src/or/rephist.c    (revision 17104)
86 +++ src/or/rephist.c    (working copy)
87 @@ -18,6 +18,7 @@
88  static void bw_arrays_init(void);
89  static void predicted_ports_init(void);
90  static void hs_usage_init(void);
91 +static void or_port_hist_init(void);
93  /** Total number of bytes currently allocated in fields used by rephist.c. */
94  uint64_t rephist_total_alloc=0;
95 @@ -89,6 +90,25 @@
96    digestmap_t *link_history_map;
97  } or_history_t;
99 +/** or_port_hist_t contains our router/client's knowledge of
100 +    all OR ports offered on the network, and how many servers with each port we
101 +    have succeeded or failed to connect to. */
102 +typedef struct {
103 +  /** The port this entry is tracking. */
104 +  uint16_t or_port;
105 +  /** Have we ever connected to this port on another OR?. */
106 +  unsigned int success:1;
107 +  /** The ORs using this port. */
108 +  digestmap_t *ids;
109 +  /** The ORs using this port we have failed to connect to. */
110 +  digestmap_t *failure_ids;
111 +  /** Are we excluding ORs with this port during entry selection?*/
112 +  unsigned int excluded;
113 +} or_port_hist_t;
115 +static unsigned int still_searching = 0;
116 +static smartlist_t *or_port_hists;
118  /** When did we last multiply all routers' weighted_run_length and
119   * total_run_weights by STABILITY_ALPHA? */
120  static time_t stability_last_downrated = 0;
121 @@ -164,6 +184,16 @@
122    tor_free(hist);
125 +/** Helper: free storage held by a single OR port history entry. */
126 +static void
127 +or_port_hist_free(or_port_hist_t *p)
129 +  tor_assert(p);
130 +  digestmap_free(p->ids,NULL);
131 +  digestmap_free(p->failure_ids,NULL);
132 +  tor_free(p);
135  /** Update an or_history_t object <b>hist</b> so that its uptime/downtime
136   * count is up-to-date as of <b>when</b>.
137   */
138 @@ -1639,7 +1669,7 @@
139      tmp_time = smartlist_get(predicted_ports_times, i);
140      if (*tmp_time + PREDICTED_CIRCS_RELEVANCE_TIME < now) {
141        tmp_port = smartlist_get(predicted_ports_list, i);
142 -      log_debug(LD_CIRC, "Expiring predicted port %d", *tmp_port);
143 +      log_debug(LD_HIST, "Expiring predicted port %d", *tmp_port);
144        smartlist_del(predicted_ports_list, i);
145        smartlist_del(predicted_ports_times, i);
146        rephist_total_alloc -= sizeof(uint16_t)+sizeof(time_t);
147 @@ -1821,6 +1851,12 @@
148    tor_free(last_stability_doc);
149    built_last_stability_doc_at = 0;
150    predicted_ports_free();
151 +  if (or_port_hists) {
152 +    SMARTLIST_FOREACH(or_port_hists, or_port_hist_t *, p,
153 +                      or_port_hist_free(p));
154 +    smartlist_free(or_port_hists);
155 +    or_port_hists = NULL;
156 +  }
159  /****************** hidden service usage statistics ******************/
160 @@ -2356,3 +2392,225 @@
161    tor_free(fname);
164 +/** Create a new entry in the port tracking cache for the or_port in
165 +  * <b>ri</b>. */
166 +void
167 +or_port_hist_new(const routerinfo_t *ri)
169 +  or_port_hist_t *result;
170 +  const char *id=ri->cache_info.identity_digest;
172 +  if (!or_port_hists)
173 +    or_port_hist_init();
175 +  SMARTLIST_FOREACH(or_port_hists, or_port_hist_t *, tp,
176 +    {
177 +      /* Cope with routers that change their advertised OR port or are
178 +         dropped from the networkstatus. We don't discard the failures of
179 +         dropped routers because they are still valid when counting
180 +         consecutive failures on a port.*/
181 +      if (digestmap_get(tp->ids, id) && (tp->or_port != ri->or_port)) {
182 +        digestmap_remove(tp->ids, id);
183 +      }
184 +      if (tp->or_port == ri->or_port) {
185 +        if (!(digestmap_get(tp->ids, id)))
186 +          digestmap_set(tp->ids, id, (void*)1);
187 +        return;
188 +      }
189 +    });
191 +  result = tor_malloc_zero(sizeof(or_port_hist_t));
192 +  result->or_port=ri->or_port;
193 +  result->success=0;
194 +  result->ids=digestmap_new();
195 +  digestmap_set(result->ids, id, (void*)1);
196 +  result->failure_ids=digestmap_new();
197 +  result->excluded=0;
198 +  smartlist_add(or_port_hists, result);
201 +/** Create the port tracking cache. */
202 +/*XXX: need to call this when we rebuild/update our network status */
203 +static void
204 +or_port_hist_init(void)
206 +  routerlist_t *rl = router_get_routerlist();
208 +  if (!or_port_hists)
209 +    or_port_hists=smartlist_create();
211 +  if (rl && rl->routers) {
212 +    SMARTLIST_FOREACH(rl->routers, routerinfo_t *, ri,
213 +    {
214 +      or_port_hist_new(ri);
215 +    });
216 +  }
219 +#define NOT_BLOCKED 0
220 +#define FAILURES_OBSERVED 1
221 +#define POSSIBLY_BLOCKED 5
222 +#define PROBABLY_BLOCKED 10
223 +/** Return the list of blocked ports for our router's extra-info.*/
224 +char *
225 +or_port_hist_get_blocked_ports(void)
227 +  char blocked_ports[2048];
228 +  char *bp;
229 +  
230 +  tor_snprintf(blocked_ports,sizeof(blocked_ports),"blocked-ports");
231 +  SMARTLIST_FOREACH(or_port_hists, or_port_hist_t *, tp,
232 +    {
233 +      if (digestmap_size(tp->failure_ids) >= PROBABLY_BLOCKED)
234 +        tor_snprintf(blocked_ports+strlen(blocked_ports),
235 +                     sizeof(blocked_ports)," %u,",tp->or_port);
236 +    });
237 +  if (strlen(blocked_ports) == 13)
238 +    return NULL;
239 +  bp=tor_strdup(blocked_ports);
240 +  bp[strlen(bp)-1]='\n';
241 +  bp[strlen(bp)]='\0';
242 +  return bp;
245 +/** Revert to client-only mode if we have seen to many failures on a port or
246 +  * range of ports.*/
247 +static void
248 +or_port_hist_report_block(unsigned int min_severity)
250 +  or_options_t *options=get_options();
251 +  char failures_observed[2048],possibly_blocked[2048],probably_blocked[2048];
252 +  char port[1024];
254 +  memset(failures_observed,0,sizeof(failures_observed));
255 +  memset(possibly_blocked,0,sizeof(possibly_blocked));
256 +  memset(probably_blocked,0,sizeof(probably_blocked));
258 +  SMARTLIST_FOREACH(or_port_hists, or_port_hist_t *, tp,
259 +    {
260 +      unsigned int failures = digestmap_size(tp->failure_ids);
261 +      if (failures >= min_severity) {
262 +        tor_snprintf(port, sizeof(port), " %u (%u failures %s out of %u on the"
263 +                     " network)",tp->or_port,failures,
264 +                     (!tp->success)?"and no successes": "since last success",
265 +                     digestmap_size(tp->ids));
266 +        if (failures >= PROBABLY_BLOCKED) {
267 +          strlcat(probably_blocked, port, sizeof(probably_blocked));
268 +        } else if (failures >= POSSIBLY_BLOCKED)
269 +          strlcat(possibly_blocked, port, sizeof(possibly_blocked));
270 +        else if (failures >= FAILURES_OBSERVED)
271 +          strlcat(failures_observed, port, sizeof(failures_observed));
272 +      }
273 +    });
275 +  log_warn(LD_HIST,"%s%s%s%s%s%s%s%s",
276 +           server_mode(options) &&
277 +           ((min_severity==FAILURES_OBSERVED) || strlen(probably_blocked))?
278 +           "You should consider disabling your Tor server.":"",
279 +           (min_severity==FAILURES_OBSERVED)?
280 +           "Tor appears to be blocked from connecting to a range of ports "
281 +           "with the result that it cannot connect to one tenth of the Tor "
282 +           "network. ":"",
283 +           strlen(failures_observed)?
284 +           "Tor has observed failures on the following ports: ":"",
285 +           failures_observed,
286 +           strlen(possibly_blocked)?
287 +           "Tor is possibly blocked on the following ports: ":"",
288 +           possibly_blocked,
289 +           strlen(probably_blocked)?
290 +           "Tor is almost certainly blocked on the following ports: ":"",
291 +           probably_blocked);
295 +/** Record the success of our connection to <b>digest</b>'s
296 +  * OR port. */
297 +void
298 +or_port_hist_success(uint16_t or_port)
300 +  SMARTLIST_FOREACH(or_port_hists, or_port_hist_t *, tp,
301 +    {
302 +      if (tp->or_port != or_port)
303 +        continue;
304 +      /*Reset our failure stats so we can notice if this port ever gets
305 +        blocked again.*/
306 +      tp->success=1;
307 +      if (digestmap_size(tp->failure_ids)) {
308 +        digestmap_free(tp->failure_ids,NULL);
309 +        tp->failure_ids=digestmap_new();
310 +      }
311 +      if (still_searching) {
312 +        still_searching=0;
313 +        SMARTLIST_FOREACH(or_port_hists,or_port_hist_t *,t,t->excluded=0;);
314 +      }
315 +      return;
316 +    });
318 +/** Record the failure of our connection to <b>digest</b>'s
319 +  * OR port. Warn, exclude the port from future entry guard selection, or
320 +  * add port to blocked-ports in our server's extra-info as appropriate. */
321 +void
322 +or_port_hist_failure(const char *digest, uint16_t or_port)
324 +  int total_failures=0, ports_excluded=0, report_block=0;
325 +  int total_routers=smartlist_len(router_get_routerlist()->routers);
327 +  SMARTLIST_FOREACH(or_port_hists, or_port_hist_t *, tp,
328 +    {
329 +      ports_excluded += tp->excluded;
330 +      total_failures+=digestmap_size(tp->failure_ids);
331 +      if (tp->or_port != or_port)
332 +        continue;
333 +      /* We're only interested in unique failures */
334 +      if (digestmap_get(tp->failure_ids, digest))
335 +        return;
337 +      total_failures++;
338 +      digestmap_set(tp->failure_ids, digest, (void*)1);
339 +      if (still_searching && !tp->success) {
340 +        tp->excluded=1;
341 +        ports_excluded++;
342 +      }
343 +      if ((digestmap_size(tp->ids) >= POSSIBLY_BLOCKED) &&
344 +         !(digestmap_size(tp->failure_ids) % POSSIBLY_BLOCKED))
345 +        report_block=POSSIBLY_BLOCKED;
346 +    });
348 +  if (total_failures >= (int)(total_routers/10))
349 +    or_port_hist_report_block(FAILURES_OBSERVED);
350 +  else if (report_block)
351 +    or_port_hist_report_block(report_block);
353 +  if (ports_excluded >= smartlist_len(or_port_hists)) {
354 +    log_warn(LD_HIST,"During entry node selection Tor tried every port "
355 +             "offered on the network on at least one server "
356 +             "and didn't manage a single "
357 +             "successful connection. This suggests you are behind an "
358 +             "extremely restrictive firewall. Tor will keep trying to find "
359 +             "a reachable entry node.");
360 +    SMARTLIST_FOREACH(or_port_hists, or_port_hist_t *, tp, tp->excluded=0;);
361 +  }
364 +/** Add any ports marked as excluded in or_port_hist_t to <b>rt</b> */
365 +void
366 +or_port_hist_exclude(routerset_t *rt)
368 +  SMARTLIST_FOREACH(or_port_hists, or_port_hist_t *, tp,
369 +    {
370 +      char portpolicy[9];
371 +      if (tp->excluded) {
372 +        tor_snprintf(portpolicy,sizeof(portpolicy),"*:%u", tp->or_port);
373 +        log_warn(LD_HIST,"Port %u may be blocked, excluding it temporarily "
374 +                          "from entry guard selection.", tp->or_port);
375 +        routerset_parse(rt, portpolicy, "Ports");
376 +      }
377 +    });
380 +/** Allow the exclusion of ports during our search for an entry node. */
381 +void
382 +or_port_hist_search_again(void)
384 +    still_searching=1;
386 Index: src/or/or.h
387 ===================================================================
388 --- src/or/or.h (revision 17104)
389 +++ src/or/or.h (working copy)
390 @@ -3864,6 +3864,13 @@
391  int any_predicted_circuits(time_t now);
392  int rep_hist_circbuilding_dormant(time_t now);
394 +void or_port_hist_failure(const char *digest, uint16_t or_port);
395 +void or_port_hist_success(uint16_t or_port);
396 +void or_port_hist_new(const routerinfo_t *ri);
397 +void or_port_hist_exclude(routerset_t *rt);
398 +void or_port_hist_search_again(void);
399 +char *or_port_hist_get_blocked_ports(void);
401  /** Possible public/private key operations in Tor: used to keep track of where
402   * we're spending our time. */
403  typedef enum {
404 Index: src/or/routerparse.c
405 ===================================================================
406 --- src/or/routerparse.c        (revision 17104)
407 +++ src/or/routerparse.c        (working copy)
408 @@ -1401,6 +1401,8 @@
409      goto err;
410    }
412 +  or_port_hist_new(router);
414    if (!router->platform) {
415      router->platform = tor_strdup("<unknown>");
416    }
417 Index: src/or/router.c
418 ===================================================================
419 --- src/or/router.c     (revision 17104)
420 +++ src/or/router.c     (working copy)
421 @@ -1818,6 +1818,7 @@
422    char published[ISO_TIME_LEN+1];
423    char digest[DIGEST_LEN];
424    char *bandwidth_usage;
425 +  char *blocked_ports;
426    int result;
427    size_t len;
429 @@ -1825,7 +1826,6 @@
430                  extrainfo->cache_info.identity_digest, DIGEST_LEN);
431    format_iso_time(published, extrainfo->cache_info.published_on);
432    bandwidth_usage = rep_hist_get_bandwidth_lines(1);
434    result = tor_snprintf(s, maxlen,
435                          "extra-info %s %s\n"
436                          "published %s\n%s",
437 @@ -1835,6 +1835,16 @@
438    if (result<0)
439      return -1;
441 +  blocked_ports = or_port_hist_get_blocked_ports();
442 +  if (blocked_ports) {
443 +      result = tor_snprintf(s+strlen(s), maxlen-strlen(s),
444 +                            "%s",
445 +                            blocked_ports);
446 +      tor_free(blocked_ports);
447 +      if (result<0)
448 +        return -1;
449 +  }
451    if (should_record_bridge_info(options)) {
452      static time_t last_purged_at = 0;
453      char *geoip_summary;
454 Index: src/or/circuitbuild.c
455 ===================================================================
456 --- src/or/circuitbuild.c       (revision 17104)
457 +++ src/or/circuitbuild.c       (working copy)
458 @@ -62,6 +62,7 @@
460  static void entry_guards_changed(void);
461  static time_t start_of_month(time_t when);
462 +static int num_live_entry_guards(void);
464  /** Iterate over values of circ_id, starting from conn-\>next_circ_id,
465   * and with the high bit specified by conn-\>circ_id_type, until we get
466 @@ -1627,12 +1628,14 @@
467    smartlist_t *excluded;
468    or_options_t *options = get_options();
469    router_crn_flags_t flags = 0;
470 +  routerset_t *_ExcludeNodes;
472    if (state && options->UseEntryGuards &&
473        (purpose != CIRCUIT_PURPOSE_TESTING || options->BridgeRelay)) {
474      return choose_random_entry(state);
475    }
477 +  _ExcludeNodes = routerset_new();
478    excluded = smartlist_create();
480    if (state && (r = build_state_get_exit_router(state))) {
481 @@ -1670,12 +1673,18 @@
482    if (options->_AllowInvalid & ALLOW_INVALID_ENTRY)
483      flags |= CRN_ALLOW_INVALID;
485 +  if (options->ExcludeNodes)
486 +    routerset_union(_ExcludeNodes,options->ExcludeNodes);
488 +  or_port_hist_exclude(_ExcludeNodes);
490    choice = router_choose_random_node(
491             NULL,
492             excluded,
493 -           options->ExcludeNodes,
494 +           _ExcludeNodes,
495             flags);
496    smartlist_free(excluded);
497 +  routerset_free(_ExcludeNodes);
498    return choice;
501 @@ -2727,6 +2736,7 @@
502  entry_guards_update_state(or_state_t *state)
504    config_line_t **next, *line;
505 +  unsigned int have_reachable_entry=0;
506    if (! entry_guards_dirty)
507      return;
509 @@ -2740,6 +2750,7 @@
510        char dbuf[HEX_DIGEST_LEN+1];
511        if (!e->made_contact)
512          continue; /* don't write this one to disk */
513 +      have_reachable_entry=1;
514        *next = line = tor_malloc_zero(sizeof(config_line_t));
515        line->key = tor_strdup("EntryGuard");
516        line->value = tor_malloc(HEX_DIGEST_LEN+MAX_NICKNAME_LEN+2);
517 @@ -2785,6 +2796,11 @@
518    if (!get_options()->AvoidDiskWrites)
519      or_state_mark_dirty(get_or_state(), 0);
520    entry_guards_dirty = 0;
522 +  /* XXX: Is this the place to decide that we no longer have any reachable
523 +    guards? */
524 +  if (!have_reachable_entry)
525 +    or_port_hist_search_again();
528  /** If <b>question</b> is the string "entry-guards", then dump