Squeezing non-relays at the entry node

Roger Dingledine arma at mit.edu
Mon Feb 22 04:34:15 UTC 2010

On Sun, Dec 13, 2009 at 08:23:14PM -0500, Roger Dingledine wrote:
> +  if (r || router_get_consensus_status_by_id(id_digest)) {
> +    /* It's in the consensus, or we have a descriptor for it meaning it
> +     * was probably in a recent consensus. It's a recognized relay:
> +     * give it full bandwidth. */
> +    conn->bandwidthrate = (int)options->BandwidthRate;
> +    conn->read_bucket = conn->bandwidthburst = (int)options->BandwidthBurst;
> +  } else { /* Not a recognized relay. Squeeze it down based on the
> +            * suggested bandwidth parameters in the consensus. */
> As you can see, I'm making it configurable inside the consensus, so we
> can experiment with it rather than rolling it out and then changing our
> minds later. I don't have a good sense of whether it will be a good move,
> but the only way I can imagine to find out is to try it.

I put that feature into Tor Now there's a followup feature
I want to put into

diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c
index 8e2fcf9..ea871a5 100644
--- a/src/or/connection_edge.c
+++ b/src/or/connection_edge.c
@@ -2505,16 +2505,25 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ)
       return 0;
-    if (or_circ && or_circ->is_first_hop &&
-        !get_options()->AllowSingleHopExits) {
+    if (or_circ && or_circ->p_conn && !get_options()->AllowSingleHopExits &&
+        (or_circ->is_first_hop ||
+         (!connection_or_digest_is_known_relay(
+                                       or_circ->p_conn->identity_digest) &&
+          networkstatus_get_param(NULL, "refuseunknownexits", 1)))) {
       /* Don't let clients use us as a single-hop proxy, unless the user
        * has explicitly allowed that in the config.  It attracts attackers
        * and users who'd be better off with, well, single-hop proxies.
-             "Attempt to open a stream on first hop of circuit. Closing.");
+             "Attempt by %s to open a stream %s. Closing.",
+             safe_str(or_circ->p_conn->_base.address),
+             or_circ->is_first_hop ? "on first hop of circuit" :
+                                     "from unknown relay");
       relay_send_end_cell_from_edge(rh.stream_id, circ,
-                                    END_STREAM_REASON_TORPROTOCOL, NULL);
+                                    or_circ->is_first_hop ?
+                                      END_STREAM_REASON_TORPROTOCOL :
+                                      END_STREAM_REASON_MISC,
+                                    NULL);
       return 0;
diff --git a/src/or/connection_or.c b/src/or/connection_or.c
index 1aa0bb3..213ade1 100644
--- a/src/or/connection_or.c
+++ b/src/or/connection_or.c
@@ -322,7 +322,7 @@ connection_or_finished_connecting(or_connection_t *or_conn)
 /** Return 1 if identity digest <b>id_digest</b> is known to be a
  * currently or recently running relay. Otherwise return 0. */
-static int
 connection_or_digest_is_known_relay(const char *id_digest)
   if (router_get_consensus_status_by_id(id_digest))
diff --git a/src/or/or.h b/src/or/or.h
index 434de78..dcf2f3d 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -3528,6 +3528,7 @@ int connection_or_process_inbuf(or_connection_t *conn);
 int connection_or_flushed_some(or_connection_t *conn);
 int connection_or_finished_flushing(or_connection_t *conn);
 int connection_or_finished_connecting(or_connection_t *conn);
+int connection_or_digest_is_known_relay(const char *id_digest);
 void connection_or_connect_failed(or_connection_t *conn,
                                   int reason, const char *msg);

You'll notice that I'm again using the consensus to provide a potential
kill switch for this feature -- first, in case it goes horribly wrong,
and second, so that in the distant future when the network topology
is different, we can turn it off smoothly. I decided to make it on by
default rather than off by default (meaning that we don't put anything
in the consensus until we want to turn it off) on the theory that it
will probably work ok, and we will probably not want to turn it off for
quite a while.

The only other question here is how to fail the stream -- that is, what
reason to send back. We still send back END_STREAM_REASON_TORPROTOCOL in
the case of or_circ->is_first_hop, since that's clearly against what's
written in tor-spec.txt.

Should we reject people not listed in the consensus with
TORPROTOCOL too? The chance of false positives is higher. Check out
edge_reason_is_retriable() in relay.c:

/** Return 1 if reason is something that you should retry if you
 * get the end cell before you've connected; else return 0. */
static int
edge_reason_is_retriable(int reason)
         reason == END_STREAM_REASON_EXITPOLICY ||
         reason == END_STREAM_REASON_MISC;

If we want the client to retry the stream somewhere else (to handle false
positives more smoothly), we want to use one of these. RESOURCELIMIT,
EXITPOLICY, and MISC are plausible choices. If we choose EXITPOLICY or
RESOURCELIMIT (but not MISC), we'll call
which on first glance seems like a good idea -- it means we will avoid
that router in the future on the theory that one broken attempt is an
indication of future results. (Its exit policy will get reset the next
time a descriptor is parsed for it.) But on further thought, if the
false positives here are randomly distributed, we don't actually want
to avoid that router for a whole day. The ...reject_all() idea was
introduced back in directory v1, when you got a new descriptor for the
relay every hour. With the new microdescriptor plan, you might not
refresh the relay's exit policy for a week.

So I went with MISC.

How's my logic? If you like it, I'll try to summarize it in a comment
when I put the patch in.


More information about the tor-dev mailing list