[or-cvs] Introduce a notion of "internal" circs, which are chosen wi...

Roger Dingledine arma at seul.org
Mon Jan 17 18:13:15 UTC 2005


Update of /home2/or/cvsroot/tor/src/or
In directory moria.mit.edu:/home2/arma/work/onion/cvs/tor/src/or

Modified Files:
	circuitbuild.c circuitlist.c circuituse.c config.c 
	connection_edge.c main.c or.h relay.c rendclient.c 
	rendservice.c rephist.c 
Log Message:
Introduce a notion of 'internal' circs, which are chosen without regard
to the exit policy of the last hop. Intro and rendezvous circs must
be internal circs, to avoid leaking information. Resolve and connect
streams can use internal circs if they want.

New circuit pooling algorithm: make sure to have enough circs around
to satisfy any predicted ports, and also make sure to have 2 internal
circs around if we've required internal circs lately (with high uptime
if we've seen that lately).

Split NewCircuitPeriod config option into NewCircuitPeriod (30 secs),
which describes how often we retry making new circuits if current ones
are dirty, and MaxCircuitDirtiness (10 mins), which describes how long
we're willing to make use of an already-dirty circuit.

Once rendezvous circuits are established, keep using the same circuit as
long as you attach a new stream to it at least every 10 minutes. (So web
browsing doesn't require you to build new rend circs every 30 seconds.)

Cannibalize GENERAL circs to be C_REND, C_INTRO, S_INTRO, and S_REND
circ as necessary, if there are any completed ones lying around when
we try to launch one.

Re-instate the ifdef's to use version-0 style introduce cells, since
there was yet another bug in handling version-1 style. We'll try switching
over again after 0.0.9 is obsolete.

Bugfix: when choosing an exit node for a new non-internal circ, don't take
into account whether it'll be useful for any pending x.onion addresses --
it won't.

Bugfix: we weren't actually publishing the hidden service descriptor when
it became dirty. So we only published it every 20 minutes or so, which
means when you first start your Tor, the hidden service will seem broken.


Index: circuitbuild.c
===================================================================
RCS file: /home2/or/cvsroot/tor/src/or/circuitbuild.c,v
retrieving revision 1.77
retrieving revision 1.78
diff -u -d -r1.77 -r1.78
--- circuitbuild.c	12 Jan 2005 04:58:23 -0000	1.77
+++ circuitbuild.c	17 Jan 2005 18:13:09 -0000	1.78
@@ -23,7 +23,7 @@
 circuit_deliver_create_cell(circuit_t *circ, char *payload);
 static cpath_build_state_t *
 onion_new_cpath_build_state(uint8_t purpose, const char *exit_digest,
-                            int need_uptime, int need_capacity);
+                            int need_uptime, int need_capacity, int internal);
 static int onion_extend_cpath(crypt_path_t **head_ptr,
                        cpath_build_state_t *state, routerinfo_t **router_out);
 static int count_acceptable_routers(smartlist_t *routers);
@@ -82,7 +82,9 @@
   elements = smartlist_create();
 
   if (verbose) {
-    tor_snprintf(buf, sizeof(buf)-1, "circ (length %d, exit %s):",
+    tor_snprintf(buf, sizeof(buf)-1, "%s%s circ (length %d, exit %s):",
+                 circ->build_state->is_internal ? "internal" : "exit",
+                 circ->build_state->need_uptime ? " (high-uptime)" : "",
                  circ->build_state->desired_path_len,
                  circ->build_state->chosen_exit_name);
     smartlist_add(elements, tor_strdup(buf));
@@ -236,7 +238,7 @@
  */
 circuit_t *
 circuit_establish_circuit(uint8_t purpose, const char *exit_digest,
-                          int need_uptime, int need_capacity) {
+                          int need_uptime, int need_capacity, int internal) {
   routerinfo_t *firsthop;
   connection_t *n_conn;
   circuit_t *circ;
@@ -244,7 +246,7 @@
   circ = circuit_new(0, NULL); /* sets circ->p_circ_id and circ->p_conn */
   circ->state = CIRCUIT_STATE_OR_WAIT;
   circ->build_state = onion_new_cpath_build_state(purpose, exit_digest,
-                                                  need_uptime, need_capacity);
+                                 need_uptime, need_capacity, internal);
   circ->purpose = purpose;
 
   if (! circ->build_state) {
@@ -951,6 +953,7 @@
       if (carray[j]->type != CONN_TYPE_AP ||
           carray[j]->state != AP_CONN_STATE_CIRCUIT_WAIT ||
           carray[j]->marked_for_close ||
+          connection_edge_is_rendezvous_stream(carray[j]) ||
           circuit_stream_is_being_handled(carray[j], 0, MIN_CIRCUITS_HANDLING_STREAM))
         continue; /* Skip everything but APs in CIRCUIT_WAIT */
       if (connection_ap_can_use_exit(carray[j], router)) {
@@ -1080,7 +1083,7 @@
  */
 static cpath_build_state_t *
 onion_new_cpath_build_state(uint8_t purpose, const char *exit_digest,
-                            int need_uptime, int need_capacity)
+                            int need_uptime, int need_capacity, int internal)
 {
   routerlist_t *rl;
   int r;
@@ -1097,6 +1100,7 @@
   info->desired_path_len = r;
   info->need_uptime = need_uptime;
   info->need_capacity = need_capacity;
+  info->is_internal = internal;
   if (exit_digest) { /* the circuit-builder pre-requested one */
     memcpy(info->chosen_exit_digest, exit_digest, DIGEST_LEN);
     exit = router_get_by_digest(exit_digest);
@@ -1121,6 +1125,35 @@
   return info;
 }
 
+/** Take the open circ originating here, give it a new exit destination
+ * to exit_digest (use nickname directly if it's provided, else strdup
+ * out of router->nickname), and get it to send the next extend cell.
+ */
+int
+circuit_append_new_hop(circuit_t *circ, char *nickname, const char *exit_digest) {
+  routerinfo_t *exit = router_get_by_digest(exit_digest);
+  tor_assert(CIRCUIT_IS_ORIGIN(circ));
+  circ->state = CIRCUIT_STATE_BUILDING;
+  tor_free(circ->build_state->chosen_exit_name);
+  if (nickname) {
+    circ->build_state->chosen_exit_name = nickname;
+  } else if (exit) {
+    circ->build_state->chosen_exit_name = tor_strdup(exit->nickname);
+  } else {
+    circ->build_state->chosen_exit_name = tor_malloc(HEX_DIGEST_LEN+1);
+    base16_encode(circ->build_state->chosen_exit_name, HEX_DIGEST_LEN+1,
+                  exit_digest, DIGEST_LEN);
+  }
+  memcpy(circ->build_state->chosen_exit_digest, exit_digest, DIGEST_LEN);
+  ++circ->build_state->desired_path_len;
+  if (circuit_send_next_onion_skin(circ)<0) {
+    log_fn(LOG_WARN, "Couldn't extend circuit to new point '%s'.",                                    circ->build_state->chosen_exit_name);
+    circuit_mark_for_close(circ);
+    return -1;
+  }
+  return 0;
+}
+
 /** Return the number of routers in <b>routers</b> that are currently up
  * and available for building circuits through.
  */

Index: circuitlist.c
===================================================================
RCS file: /home2/or/cvsroot/tor/src/or/circuitlist.c,v
retrieving revision 1.22
retrieving revision 1.23
diff -u -d -r1.22 -r1.23
--- circuitlist.c	29 Nov 2004 22:25:29 -0000	1.22
+++ circuitlist.c	17 Jan 2005 18:13:09 -0000	1.23
@@ -292,41 +292,29 @@
   return NULL;
 }
 
-/** Count the number of circs originating here that aren't open, and
- * that have the specified <b>purpose</b>. */
-int circuit_count_building(uint8_t purpose) {
-  circuit_t *circ;
-  int num=0;
-
-  for (circ=global_circuitlist;circ;circ = circ->next) {
-    if (CIRCUIT_IS_ORIGIN(circ) &&
-       circ->state != CIRCUIT_STATE_OPEN &&
-       circ->purpose == purpose &&
-       !circ->marked_for_close)
-      num++;
-  }
-  return num;
-}
-
-/** Return the circuit that is open, has specified <b>purpose</b>,
- * has a timestamp_dirty value of 0, and was created most recently,
- * or NULL if no circuit fits this description.
+/** Return a circuit that is open, has specified <b>purpose</b>,
+ * has a timestamp_dirty value of 0, and is uptime/capacity/internal
+ * if required; or NULL if no circuit fits this description.
  */
 circuit_t *
-circuit_get_youngest_clean_open(uint8_t purpose) {
+circuit_get_clean_open(uint8_t purpose, int need_uptime,
+                       int need_capacity, int internal) {
   circuit_t *circ;
-  circuit_t *youngest=NULL;
 
-  for (circ=global_circuitlist;circ;circ = circ->next) {
+  log_fn(LOG_DEBUG,"Hunting for a circ to cannibalize: purpose %d, uptime %d, capacity %d, internal %d", purpose, need_uptime, need_capacity, internal);
+
+  for (circ=global_circuitlist; circ; circ = circ->next) {
     if (CIRCUIT_IS_ORIGIN(circ) &&
         circ->state == CIRCUIT_STATE_OPEN &&
         !circ->marked_for_close &&
         circ->purpose == purpose &&
         !circ->timestamp_dirty &&
-        (!youngest || youngest->timestamp_created < circ->timestamp_created))
-      youngest = circ;
+        (!need_uptime || circ->build_state->need_uptime) &&
+        (!need_capacity || circ->build_state->need_capacity) &&
+        (!internal || circ->build_state->is_internal))
+      return circ;
   }
-  return youngest;
+  return NULL;
 }
 
 /** Mark <b>circ</b> to be closed next time we call

Index: circuituse.c
===================================================================
RCS file: /home2/or/cvsroot/tor/src/or/circuituse.c,v
retrieving revision 1.42
retrieving revision 1.43
diff -u -d -r1.42 -r1.43
--- circuituse.c	13 Jan 2005 20:22:37 -0000	1.42
+++ circuituse.c	17 Jan 2005 18:13:09 -0000	1.43
@@ -35,6 +35,9 @@
                                  time_t now)
 {
   routerinfo_t *exitrouter;
+  tor_assert(circ);
+  tor_assert(conn);
+  tor_assert(conn->socks_request);
 
   if (!CIRCUIT_IS_ORIGIN(circ))
     return 0; /* this circ doesn't start at us */
@@ -61,40 +64,36 @@
 
   if (purpose == CIRCUIT_PURPOSE_C_GENERAL)
     if (circ->timestamp_dirty &&
-       circ->timestamp_dirty+get_options()->NewCircuitPeriod <= now)
+       circ->timestamp_dirty+get_options()->MaxCircuitDirtiness <= now)
       return 0;
 
-  if (conn) {
-    /* decide if this circ is suitable for this conn */
+  /* decide if this circ is suitable for this conn */
 
-    /* for rend circs, circ->cpath->prev is not the last router in the
-     * circuit, it's the magical extra bob hop. so just check the nickname
-     * of the one we meant to finish at.
-     */
-    exitrouter = router_get_by_digest(circ->build_state->chosen_exit_digest);
+  /* for rend circs, circ->cpath->prev is not the last router in the
+   * circuit, it's the magical extra bob hop. so just check the nickname
+   * of the one we meant to finish at.
+   */
+  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 (!exitrouter) {
+    log_fn(LOG_INFO,"Skipping broken circ (exit router vanished)");
+    return 0; /* this circuit is screwed and doesn't know it yet */
+  }
 
-    if (!circ->build_state->need_uptime &&
-        smartlist_string_num_isin(get_options()->LongLivedPorts,
-                                  conn->socks_request->port))
-      return 0;
+  if (!circ->build_state->need_uptime &&
+      smartlist_string_num_isin(get_options()->LongLivedPorts,
+                                conn->socks_request->port))
+    return 0;
 
-    if (conn->socks_request &&
-        conn->socks_request->command == SOCKS_COMMAND_RESOLVE) {
-    } else if (purpose == CIRCUIT_PURPOSE_C_GENERAL) {
+  if (conn->socks_request->command == SOCKS_COMMAND_CONNECT) {
+    if (purpose == CIRCUIT_PURPOSE_C_GENERAL) {
       if (!connection_ap_can_use_exit(conn, exitrouter)) {
         /* can't exit from this router */
         return 0;
       }
     } else { /* not general */
-      if (rend_cmp_service_ids(conn->rend_query, circ->rend_query) &&
-          (circ->rend_query[0] || purpose != CIRCUIT_PURPOSE_C_REND_JOINED)) {
-        /* this circ is not for this conn, and it's not suitable
-         * for cannibalizing either */
+      if (rend_cmp_service_ids(conn->rend_query, circ->rend_query)) {
+        /* this circ is not for this conn */
         return 0;
       }
     }
@@ -178,9 +177,8 @@
   return best;
 }
 
-/** Circuits that were born at the end of their second might be expired
- * after 30.1 seconds; circuits born at the beginning might be expired
- * after closer to 31 seconds.
+/** If we find a circuit that isn't open yet and was born this many
+ * seconds ago, then assume something went wrong, and cull it.
  */
 #define MIN_SECONDS_BEFORE_EXPIRING_CIRC 30
 
@@ -289,7 +287,7 @@
         !circ->marked_for_close &&
         circ->purpose == CIRCUIT_PURPOSE_C_GENERAL &&
         (!circ->timestamp_dirty ||
-         circ->timestamp_dirty + get_options()->NewCircuitPeriod < now)) {
+         circ->timestamp_dirty + get_options()->MaxCircuitDirtiness < now)) {
       exitrouter = router_get_by_digest(circ->build_state->chosen_exit_digest);
       if (exitrouter &&
           (!need_uptime || circ->build_state->need_uptime) &&
@@ -305,6 +303,65 @@
   return 0;
 }
 
+/** Don't keep more than 10 unused open circuits around. */
+#define MAX_UNUSED_OPEN_CIRCUITS 10
+
+/** Figure out how many circuits we have open that are clean. Make
+ * sure it's enough for all the upcoming behaviors we predict we'll have.
+ * But if we have too many, close the not-so-useful ones.
+ */
+static void
+circuit_predict_and_launch_new(void)
+{
+  circuit_t *circ;
+  int num=0, num_internal=0, num_uptime_internal=0;
+  int hidserv_needs_uptime=0, hidserv_needs_capacity=1;
+  int port_needs_uptime=0, port_needs_capacity=1;
+  int need_ports, need_hidserv;
+  time_t now = time(NULL);
+
+  /* check if we know of a port that's been requested recently
+   * and no circuit is currently available that can handle it. */
+  need_ports = !circuit_all_predicted_ports_handled(now, &port_needs_uptime,
+                                                    &port_needs_capacity);
+
+  need_hidserv = rep_hist_get_predicted_hidserv(now, &hidserv_needs_uptime,
+                                                &hidserv_needs_capacity);
+
+  for (circ=global_circuitlist;circ;circ = circ->next) {
+    if (!CIRCUIT_IS_ORIGIN(circ))
+      continue;
+    if (circ->marked_for_close)
+      continue; /* don't mess with marked circs */
+    if (circ->timestamp_dirty)
+      continue; /* only count clean circs */
+    if (circ->purpose != CIRCUIT_PURPOSE_C_GENERAL)
+      continue; /* only pay attention to general-purpose circs */
+    num++;
+    if (circ->build_state->is_internal)
+      num_internal++;
+    if (circ->build_state->need_uptime && circ->build_state->is_internal)
+      num_uptime_internal++;
+  }
+
+  if (num < MAX_UNUSED_OPEN_CIRCUITS) {
+    /* perhaps we want another */
+    if (need_ports) {
+      log_fn(LOG_INFO,"Have %d clean circs (%d internal), need another exit circ.",
+        num, num_internal);
+      circuit_launch_by_identity(CIRCUIT_PURPOSE_C_GENERAL, NULL,
+                               port_needs_uptime, port_needs_capacity, 0);
+    } else if (need_hidserv &&
+               ((num_uptime_internal<2 && hidserv_needs_uptime) ||
+                num_internal<2)) {
+      log_fn(LOG_INFO,"Have %d clean circs (%d uptime-internal, %d internal),"
+        " need another hidserv circ.", num, num_uptime_internal, num_internal);
+      circuit_launch_by_identity(CIRCUIT_PURPOSE_C_GENERAL, NULL,
+                               hidserv_needs_uptime, hidserv_needs_capacity, 1);
+    }
+  }
+}
+
 /** Build a new test circuit every 5 minutes */
 #define TESTING_CIRCUIT_INTERVAL 300
 
@@ -315,8 +372,6 @@
  */
 void circuit_build_needed_circs(time_t now) {
   static long time_to_new_circuit = 0;
-  circuit_t *circ;
-  int need_uptime=0, need_capacity=1;
 
   /* launch a new circ for any pending streams that need one */
   connection_ap_attach_pending();
@@ -325,8 +380,6 @@
   if (has_fetched_directory)
     rend_services_introduce();
 
-  circ = circuit_get_youngest_clean_open(CIRCUIT_PURPOSE_C_GENERAL);
-
   if (time_to_new_circuit < now) {
     circuit_reset_failure_count(1);
     time_to_new_circuit = now + get_options()->NewCircuitPeriod;
@@ -334,37 +387,17 @@
       client_dns_clean();
     circuit_expire_old_circuits();
 
+#if 0 /* disable for now, until predict-and-launch-new can cull leftovers */
+    circ = circuit_get_youngest_clean_open(CIRCUIT_PURPOSE_C_GENERAL);
     if (get_options()->RunTesting &&
         circ &&
         circ->timestamp_created + TESTING_CIRCUIT_INTERVAL < now) {
       log_fn(LOG_INFO,"Creating a new testing circuit.");
-      circuit_launch_by_identity(CIRCUIT_PURPOSE_C_GENERAL, NULL, 0, 0);
+      circuit_launch_by_identity(CIRCUIT_PURPOSE_C_GENERAL, NULL, 0, 0, 0);
     }
-  }
-
-#if 0
-/** How many simultaneous in-progress general-purpose circuits do we
- * want to be building at once, if there are no open general-purpose
- * circuits?
- */
-#define CIRCUIT_MIN_BUILDING_GENERAL 5
-  /* if there's no open circ, and less than 5 are on the way,
-   * go ahead and try another. */
-  if (!circ && circuit_count_building(CIRCUIT_PURPOSE_C_GENERAL)
-               < CIRCUIT_MIN_BUILDING_GENERAL) {
-    circuit_launch_by_identity(CIRCUIT_PURPOSE_C_GENERAL, NULL);
-  }
 #endif
-
-  /* if we know of a port that's been requested recently and no
-   * circuit is currently available that can handle it, start one
-   * for that too. */
-  if (!circuit_all_predicted_ports_handled(now, &need_uptime, &need_capacity)) {
-    circuit_launch_by_identity(CIRCUIT_PURPOSE_C_GENERAL, NULL,
-                               need_uptime, need_capacity);
   }
-
-  /* XXX count idle rendezvous circs and build more */
+  circuit_predict_and_launch_new();
 }
 
 /** If the stream <b>conn</b> is a member of any of the linked
@@ -469,24 +502,14 @@
   } /* end switch */
 }
 
-/** Don't keep more than 10 unused open circuits around. */
-#define MAX_UNUSED_OPEN_CIRCUITS 10
-
 /** Find each circuit that has been dirty for too long, and has
  * no streams on it: mark it for close.
- *
- * Also, if there are more than MAX_UNUSED_OPEN_CIRCUITS open and
- * unused circuits, then mark the excess circs for close.
  */
 static void
 circuit_expire_old_circuits(void)
 {
   circuit_t *circ;
   time_t now = time(NULL);
-  smartlist_t *unused_open_circs;
-  int i;
-
-  unused_open_circs = smartlist_create();
 
   for (circ = global_circuitlist; circ; circ = circ->next) {
     if (circ->marked_for_close)
@@ -495,8 +518,8 @@
      * on it, mark it for close.
      */
     if (circ->timestamp_dirty &&
-        circ->timestamp_dirty + get_options()->NewCircuitPeriod < now &&
-        !circ->p_conn && /* we're the origin */
+        circ->timestamp_dirty + get_options()->MaxCircuitDirtiness < now &&
+        CIRCUIT_IS_ORIGIN(circ) &&
         !circ->p_streams /* nothing attached */ ) {
       log_fn(LOG_DEBUG,"Closing n_circ_id %d (dirty %d secs ago, purp %d)",circ->n_circ_id,
              (int)(now - circ->timestamp_dirty), circ->purpose);
@@ -512,23 +535,9 @@
         log_fn(LOG_DEBUG,"Closing circuit that has been unused for %d seconds.",
                (int)(now - circ->timestamp_created));
         circuit_mark_for_close(circ);
-      } else {
-        /* Also, gather a list of open unused general circuits that we created.
-         * Because we add elements to the front of global_circuitlist,
-         * the last elements of unused_open_circs will be the oldest
-         * ones.
-         */
-        smartlist_add(unused_open_circs, circ);
       }
     }
   }
-  for (i = MAX_UNUSED_OPEN_CIRCUITS; i < smartlist_len(unused_open_circs); ++i) {
-    circuit_t *circ = smartlist_get(unused_open_circs, i);
-    log_fn(LOG_DEBUG,"Expiring excess clean circ (n_circ_id %d, purp %d)",
-           circ->n_circ_id, circ->purpose);
-    circuit_mark_for_close(circ);
-  }
-  smartlist_free(unused_open_circs);
 }
 
 /** The circuit <b>circ</b> has just become open. Take the next
@@ -646,13 +655,52 @@
 
 circuit_t *
 circuit_launch_by_identity(uint8_t purpose, const char *exit_digest,
-                           int need_uptime, int need_capacity)
+                           int need_uptime, int need_capacity, int internal)
 {
+  circuit_t *circ;
+
   if (!has_fetched_directory) {
     log_fn(LOG_DEBUG,"Haven't fetched directory yet; canceling circuit launch.");
     return NULL;
   }
 
+  if (purpose != CIRCUIT_PURPOSE_C_GENERAL) {
+    /* see if there are appropriate circs available to cannibalize. */
+    if ((circ = circuit_get_clean_open(CIRCUIT_PURPOSE_C_GENERAL, need_uptime,
+                                       need_capacity, internal))) {
+      log_fn(LOG_INFO,"Cannibalizing circ '%s' for purpose %d",
+             circ->build_state->chosen_exit_name, purpose);
+      circ->purpose = purpose;
+      /* reset the birth date of this circ, else expire_building
+       * will see it and think it's been trying to build since it
+       * began. */
+      circ->timestamp_created = time(NULL);
+      switch (purpose) {
+        case CIRCUIT_PURPOSE_C_ESTABLISH_REND:
+          /* it's ready right now */
+          /* XXX should we call control_event_circuit_status() here? */
+          rend_client_rendcirc_has_opened(circ);
+          break;
+        case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO:
+          /* it's ready right now */
+          rend_service_intro_has_opened(circ);
+          break;
+        case CIRCUIT_PURPOSE_C_INTRODUCING:
+        case CIRCUIT_PURPOSE_S_CONNECT_REND:
+          /* need to add a new hop */
+          tor_assert(exit_digest);
+          if (circuit_append_new_hop(circ, NULL, exit_digest) < 0)
+            return NULL;
+          break;
+        default:
+          log_fn(LOG_WARN,"Bug: unexpected purpose %d when cannibalizing a general circ.",
+                 purpose);
+          return NULL;
+      }
+      return circ;
+    }
+  }
+
   if (did_circs_fail_last_period &&
       n_circuit_failures > MAX_CIRCUIT_FAILURES) {
     /* too many failed circs in a row. don't try. */
@@ -662,13 +710,13 @@
 
   /* try a circ. if it fails, circuit_mark_for_close will increment n_circuit_failures */
   return circuit_establish_circuit(purpose, exit_digest,
-                                   need_uptime, need_capacity);
+                                   need_uptime, need_capacity, internal);
 }
 
 /** Launch a new circuit and return a pointer to it. Return NULL if you failed. */
 circuit_t *
 circuit_launch_by_nickname(uint8_t purpose, const char *exit_nickname,
-                           int need_uptime, int need_capacity)
+                           int need_uptime, int need_capacity, int internal)
 {
   const char *digest = NULL;
 
@@ -681,7 +729,7 @@
     digest = r->identity_digest;
   }
   return circuit_launch_by_identity(purpose, digest,
-                                    need_uptime, need_capacity);
+                                    need_uptime, need_capacity, internal);
 }
 
 /** Record another failure at opening a general circuit. When we have
@@ -762,6 +810,7 @@
   if (!circ) {
     char *exitname=NULL;
     uint8_t new_circ_purpose;
+    int is_internal;
 
     if (desired_circuit_purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) {
       /* need to pick an intro point */
@@ -801,13 +850,18 @@
     else
       new_circ_purpose = desired_circuit_purpose;
 
-    circ = circuit_launch_by_nickname(new_circ_purpose, exitname, need_uptime, 1);
+    is_internal = (new_circ_purpose != CIRCUIT_PURPOSE_C_GENERAL || is_resolve);
+    circ = circuit_launch_by_nickname(new_circ_purpose, exitname, need_uptime,
+                                      1, is_internal);
     tor_free(exitname);
 
-    if (circ &&
-        desired_circuit_purpose != CIRCUIT_PURPOSE_C_GENERAL) {
-      /* then write the service_id into circ */
-      strlcpy(circ->rend_query, conn->rend_query, sizeof(circ->rend_query));
+    if (desired_circuit_purpose != CIRCUIT_PURPOSE_C_GENERAL) {
+      /* help predict this next time */
+      rep_hist_note_used_hidserv(time(NULL), need_uptime, 1);
+      if (circ) {
+        /* write the service_id into circ */
+        strlcpy(circ->rend_query, conn->rend_query, sizeof(circ->rend_query));
+      }
     }
   }
   if (!circ)
@@ -914,6 +968,12 @@
       /* one is already established, attach */
       log_fn(LOG_INFO,"rend joined circ %d already here. attaching. (stream %d sec old)",
              rendcirc->n_circ_id, conn_age);
+      /* Mark rendezvous circuits as 'newly dirty' every time you use
+       * them, since the process of rebuilding a rendezvous circ is so
+       * expensive. There is a tradeoffs between linkability and
+       * feasibility, at this point.
+       */
+      rendcirc->timestamp_dirty = time(NULL);
       link_apconn_to_circ(conn, rendcirc);
       if (connection_ap_handshake_send_begin(conn, rendcirc) < 0)
         return 0; /* already marked, let them fade away */
@@ -947,7 +1007,6 @@
       if (introcirc->state == CIRCUIT_STATE_OPEN) {
         log_fn(LOG_INFO,"found open intro circ %d (rend %d); sending introduction. (stream %d sec old)",
                introcirc->n_circ_id, rendcirc->n_circ_id, conn_age);
-        /* XXX here we should cannibalize the rend circ if it's a zero service id */
         if (rend_client_send_introduction(introcirc, rendcirc) < 0) {
           return -1;
         }

Index: config.c
===================================================================
RCS file: /home2/or/cvsroot/tor/src/or/config.c,v
retrieving revision 1.301
retrieving revision 1.302
diff -u -d -r1.301 -r1.302
--- config.c	13 Jan 2005 21:32:08 -0000	1.301
+++ config.c	17 Jan 2005 18:13:09 -0000	1.302
@@ -145,6 +145,7 @@
   VAR("AccountingMax",       MEMUNIT,   AccountingMax,        "0 bytes"),
   VAR("Nickname",            STRING,   Nickname,             NULL),
   VAR("NewCircuitPeriod",    INTERVAL, NewCircuitPeriod,     "30 seconds"),
+  VAR("MaxCircuitDirtiness", INTERVAL, MaxCircuitDirtiness,  "10 minutes"),
   VAR("NumCpus",             UINT,     NumCpus,              "1"),
   VAR("ORPort",              UINT,     ORPort,               "0"),
   VAR("ORBindAddress",       LINELIST, ORBindAddress,        NULL),

Index: connection_edge.c
===================================================================
RCS file: /home2/or/cvsroot/tor/src/or/connection_edge.c,v
retrieving revision 1.267
retrieving revision 1.268
diff -u -d -r1.267 -r1.268
--- connection_edge.c	13 Jan 2005 20:22:38 -0000	1.267
+++ connection_edge.c	17 Jan 2005 18:13:09 -0000	1.268
@@ -281,7 +281,7 @@
      * current streams on it to survive if they can: make it
      * unattractive to use for new streams */
     tor_assert(circ->timestamp_dirty);
-    circ->timestamp_dirty -= options->NewCircuitPeriod;
+    circ->timestamp_dirty -= options->MaxCircuitDirtiness;
     /* give our stream another 15 seconds to try */
     conn->timestamp_lastread += 15;
     /* attaching to a dirty circuit is fine */
@@ -403,14 +403,15 @@
         conn->hold_open_until_flushed = 1;
         return 0;
       }
-    }
-
-    if (socks->command == SOCKS_COMMAND_CONNECT && socks->port == 0) {
-      log_fn(LOG_NOTICE,"Application asked to connect to port 0. Refusing.");
-      return -1;
+      rep_hist_note_used_resolve(time(NULL)); /* help predict this next time */
+    } else { /* socks->command == SOCKS_COMMAND_CONNECT */
+      if (socks->port == 0) {
+        log_fn(LOG_NOTICE,"Application asked to connect to port 0. Refusing.");
+        return -1;
+      }
+      rep_hist_note_used_port(socks->port, time(NULL)); /* help predict this next time */
     }
     conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
-    rep_hist_note_used_port(socks->port, time(NULL)); /* help predict this next time */
     return connection_ap_handshake_attach_circuit(conn);
   } else {
     /* it's a hidden-service request */

Index: main.c
===================================================================
RCS file: /home2/or/cvsroot/tor/src/or/main.c,v
retrieving revision 1.425
retrieving revision 1.426
diff -u -d -r1.425 -r1.426
--- main.c	13 Jan 2005 21:32:08 -0000	1.425
+++ main.c	17 Jan 2005 18:13:09 -0000	1.426
@@ -528,8 +528,8 @@
   while ((conn = connection_get_by_type_state(CONN_TYPE_AP,
                                               AP_CONN_STATE_CIRCUIT_WAIT))) {
     conn->has_sent_end = 1; /* it's not connected anywhere, so no need to end */
-    log_fn(LOG_NOTICE,"Network down? Failing connection to '%s'.",
-           conn->socks_request->address);
+    log_fn(LOG_NOTICE,"Network down? Failing connection to '%s:%d'.",
+           conn->socks_request->address, conn->socks_request->port);
     connection_mark_for_close(conn);
   }
 }
@@ -818,7 +818,7 @@
 
   /** 4. Every second, we try a new circuit if there are no valid
    *    circuits. Every NewCircuitPeriod seconds, we expire circuits
-   *    that became dirty more than NewCircuitPeriod seconds ago,
+   *    that became dirty more than MaxCircuitDirtiness seconds ago,
    *    and we make a new circ if there are no clean circuits.
    */
   if (has_fetched_directory && !we_are_hibernating())
@@ -833,7 +833,7 @@
   circuit_close_all_marked();
 
   /** 7. And upload service descriptors if necessary. */
-  if (!we_are_hibernating())
+  if (has_fetched_directory && !we_are_hibernating())
     rend_consider_services_upload(now);
 
   /** 8. and blow away any connections that need to die. have to do this now,

Index: or.h
===================================================================
RCS file: /home2/or/cvsroot/tor/src/or/or.h,v
retrieving revision 1.524
retrieving revision 1.525
diff -u -d -r1.524 -r1.525
--- or.h	13 Jan 2005 20:22:38 -0000	1.524
+++ or.h	17 Jan 2005 18:13:09 -0000	1.525
@@ -731,6 +731,8 @@
   int need_uptime;
   /** Whether every node in the circ must have adequate capacity. */
   int need_capacity;
+  /** Whether the last hop was picked with exiting in mind. */
+  int is_internal;
   /** The crypt_path_t to append after rendezvous: used for rendezvous. */
   struct crypt_path_t *pending_final_cpath;
   /** How many times has building a circuit for this task failed? */
@@ -955,6 +957,8 @@
                          * them? */
   int NewCircuitPeriod; /**< How long do we use a circuit before building
                          * a new one? */
+  int MaxCircuitDirtiness; /**< Never use circs that were first used more than
+                                this interval ago. */
   uint64_t BandwidthRate; /**< How much bandwidth, on average, are we willing to
                            * use in a second? */
   uint64_t BandwidthBurst; /**< How much bandwidth, at maximum, are we willing to
@@ -1052,7 +1056,7 @@
 void circuit_rep_hist_note_result(circuit_t *circ);
 void circuit_dump_by_conn(connection_t *conn, int severity);
 circuit_t *circuit_establish_circuit(uint8_t purpose, const char *exit_digest,
-                                     int need_uptime, int need_capacity);
+                                     int need_uptime, int need_capacity, int internal);
 void circuit_n_conn_done(connection_t *or_conn, int status);
 int circuit_send_next_onion_skin(circuit_t *circ);
 int circuit_extend(cell_t *cell, circuit_t *circ);
@@ -1063,6 +1067,7 @@
 int circuit_all_predicted_ports_handled(time_t now, int *need_uptime,
                                         int *need_capacity);
 
+int circuit_append_new_hop(circuit_t *circ, char *nickname, const char *exit_digest);
 void onion_append_to_cpath(crypt_path_t **head_ptr, crypt_path_t *new_hop);
 
 /********************************* circuitlist.c ***********************/
@@ -1076,8 +1081,8 @@
 circuit_t *circuit_get_next_by_pk_and_purpose(circuit_t *start,
                                          const char *digest, uint8_t purpose);
 circuit_t *circuit_get_rendezvous(const char *cookie);
-int circuit_count_building(uint8_t purpose);
-circuit_t *circuit_get_youngest_clean_open(uint8_t purpose);
+circuit_t *circuit_get_clean_open(uint8_t purpose, int need_uptime,
+                                  int need_capacity, int internal);
 int _circuit_mark_for_close(circuit_t *circ);
 
 #define circuit_mark_for_close(c)                                       \
@@ -1106,9 +1111,9 @@
 void circuit_has_opened(circuit_t *circ);
 void circuit_build_failed(circuit_t *circ);
 circuit_t *circuit_launch_by_nickname(uint8_t purpose, const char *exit_nickname,
-                                      int need_uptime, int need_capacity);
+                                      int need_uptime, int need_capacity, int is_internal);
 circuit_t *circuit_launch_by_identity(uint8_t purpose, const char *exit_digest,
-                                      int need_uptime, int need_capacity);
+                                      int need_uptime, int need_capacity, int is_internal);
 void circuit_reset_failure_count(int timeout);
 int connection_ap_handshake_attach_circuit(connection_t *conn);
 
@@ -1472,8 +1477,13 @@
 int rep_hist_bandwidth_assess(void);
 char *rep_hist_get_bandwidth_lines(void);
 void rep_history_clean(time_t before);
+
 void rep_hist_note_used_port(uint16_t port, time_t now);
 smartlist_t *rep_hist_get_predicted_ports(time_t now);
+void rep_hist_note_used_hidserv(time_t now, int need_uptime, int need_capacity);
+int rep_hist_get_predicted_hidserv(time_t now, int *need_uptime, int *need_capacity);
+void rep_hist_note_used_resolve(time_t now);
+int rep_hist_get_predicted_resolve(time_t now);
 
 /********************************* rendclient.c ***************************/
 

Index: relay.c
===================================================================
RCS file: /home2/or/cvsroot/tor/src/or/relay.c,v
retrieving revision 1.35
retrieving revision 1.36
diff -u -d -r1.35 -r1.36
--- relay.c	6 Jan 2005 19:19:13 -0000	1.35
+++ relay.c	17 Jan 2005 18:13:09 -0000	1.36
@@ -547,7 +547,7 @@
         conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
         circuit_detach_stream(circ,conn);
         tor_assert(circ->timestamp_dirty);
-        circ->timestamp_dirty -= get_options()->NewCircuitPeriod;
+        circ->timestamp_dirty -= get_options()->MaxCircuitDirtiness;
         /* make sure not to expire/retry the stream quite yet */
         conn->timestamp_lastread = time(NULL);
         if (connection_ap_handshake_attach_circuit(conn) >= 0)

Index: rendclient.c
===================================================================
RCS file: /home2/or/cvsroot/tor/src/or/rendclient.c,v
retrieving revision 1.73
retrieving revision 1.74
diff -u -d -r1.73 -r1.74
--- rendclient.c	13 Jan 2005 20:21:11 -0000	1.73
+++ rendclient.c	17 Jan 2005 18:13:09 -0000	1.74
@@ -93,11 +93,22 @@
   }
 
   /* write the remaining items into tmp */
+#if 0
   tmp[0] = 1; /* version 1 of the cell format */
-  strncpy(tmp+1, rendcirc->build_state->chosen_exit_name, (MAX_HEX_NICKNAME_LEN+1)); /* nul pads */
+  /* nul pads */
+  strncpy(tmp+1, rendcirc->build_state->chosen_exit_name, (MAX_HEX_NICKNAME_LEN+1));
   memcpy(tmp+1+MAX_HEX_NICKNAME_LEN+1, rendcirc->rend_cookie, REND_COOKIE_LEN);
+#else
+  strncpy(tmp, rendcirc->build_state->chosen_exit_name, (MAX_NICKNAME_LEN+1)); /* nul pads */
+  memcpy(tmp+MAX_NICKNAME_LEN+1, rendcirc->rend_cookie, REND_COOKIE_LEN);
+#endif
   if (crypto_dh_get_public(cpath->handshake_state,
+#if 0
                            tmp+1+MAX_HEX_NICKNAME_LEN+1+REND_COOKIE_LEN,
+#else
+                           tmp+MAX_NICKNAME_LEN+1+REND_COOKIE_LEN,
+#endif
+
                            DH_KEY_LEN)<0) {
     log_fn(LOG_WARN, "Couldn't extract g^x");
     goto err;
@@ -106,7 +117,11 @@
   /*XXX maybe give crypto_pk_public_hybrid_encrypt a max_len arg,
    * to avoid buffer overflows? */
   r = crypto_pk_public_hybrid_encrypt(entry->parsed->pk, payload+DIGEST_LEN, tmp,
+#if 0
                            1+MAX_HEX_NICKNAME_LEN+1+REND_COOKIE_LEN+DH_KEY_LEN,
+#else
+                           MAX_NICKNAME_LEN+1+REND_COOKIE_LEN+DH_KEY_LEN,
+#endif
                                       PK_PKCS1_OAEP_PADDING, 0);
   if (r<0) {
     log_fn(LOG_WARN,"hybrid pk encrypt failed.");
@@ -176,11 +191,13 @@
     /* Locate the rend circ which is waiting to hear about this ack,
      * and tell it.
      */
-    log_fn(LOG_INFO,"Received ack. Telling rend circ.");
+    log_fn(LOG_INFO,"Received ack. Telling rend circ...");
     rendcirc = circuit_get_by_rend_query_and_purpose(
                circ->rend_query, CIRCUIT_PURPOSE_C_REND_READY);
     if (rendcirc) { /* remember the ack */
       rendcirc->purpose = CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED;
+    } else {
+      log_fn(LOG_INFO,"...Found no rend circ. Dropping on the floor.");
     }
     /* close the circuit: we won't need it anymore. */
     circ->purpose = CIRCUIT_PURPOSE_C_INTRODUCE_ACKED;
@@ -199,7 +216,8 @@
       routerinfo_t *r;
       nickname = rend_client_get_random_intro(circ->rend_query);
       tor_assert(nickname);
-      log_fn(LOG_INFO,"Got nack for %s from %s, extending to %s.", circ->rend_query, circ->build_state->chosen_exit_name, nickname);
+      log_fn(LOG_INFO,"Got nack for %s from %s, extending to %s.",
+             circ->rend_query, circ->build_state->chosen_exit_name, nickname);
       if (!(r = router_get_by_nickname(nickname))) {
         log_fn(LOG_WARN, "Advertised intro point '%s' for %s is not known. Closing.",
                nickname, circ->rend_query);
@@ -209,18 +227,8 @@
       }
       log_fn(LOG_INFO, "Chose new intro point %s for %s (circ %d)",
              nickname, circ->rend_query, circ->n_circ_id);
-      circ->state = CIRCUIT_STATE_BUILDING;
-      tor_free(circ->build_state->chosen_exit_name);
-      /* no need to strdup, since rend_client_get_random_intro() made
-       * it just for us: */
-      circ->build_state->chosen_exit_name = nickname;
-      memcpy(circ->build_state->chosen_exit_digest, r->identity_digest, DIGEST_LEN);
-      ++circ->build_state->desired_path_len;
-      if (circuit_send_next_onion_skin(circ)<0) {
-        log_fn(LOG_WARN, "Couldn't extend circuit to new intro point.");
-        circuit_mark_for_close(circ);
+      if (circuit_append_new_hop(circ, nickname, r->identity_digest) < 0)
         return -1;
-      }
     }
   }
   return 0;

Index: rendservice.c
===================================================================
RCS file: /home2/or/cvsroot/tor/src/or/rendservice.c,v
retrieving revision 1.114
retrieving revision 1.115
diff -u -d -r1.114 -r1.115
--- rendservice.c	14 Jan 2005 04:35:53 -0000	1.114
+++ rendservice.c	17 Jan 2005 18:13:09 -0000	1.115
@@ -50,7 +50,7 @@
   int n_intro_circuits_launched; /**< count of intro circuits we have
                                   * established in this period. */
   rend_service_descriptor_t *desc;
-  int desc_is_dirty;
+  time_t desc_is_dirty;
   time_t next_upload_time;
 } rend_service_t;
 
@@ -363,7 +363,7 @@
  ******/
 
 /** Respond to an INTRODUCE2 cell by launching a circuit to the chosen
- * rendezvous points.
+ * rendezvous point.
  */
 int
 rend_service_introduce(circuit_t *circuit, const char *request, size_t request_len)
@@ -381,6 +381,7 @@
   char hexcookie[9];
   int version;
   size_t nickname_field_len;
+  int circ_needs_uptime;
 
   base32_encode(serviceid, REND_SERVICE_ID_LEN+1,
                 circuit->rend_pk_digest,10);
@@ -452,6 +453,7 @@
   /* Okay, now we know that a nickname is at the start of the buffer. */
   ptr = rp_nickname+nickname_field_len;
   len -= nickname_field_len;
+  len -= rp_nickname - buf; /* also remove header space used by version, if any */
   if (len != REND_COOKIE_LEN+DH_KEY_LEN) {
     log_fn(LOG_WARN, "Bad length %u for INTRODUCE2 cell.", (int)len);
     return -1;
@@ -471,11 +473,16 @@
     goto err;
   }
 
+  circ_needs_uptime = rend_service_requires_uptime(service);
+
+  /* help predict this next time */
+  rep_hist_note_used_hidserv(time(NULL), circ_needs_uptime, 1);
+
   /* Launch a circuit to alice's chosen rendezvous point.
    */
   for (i=0;i<MAX_REND_FAILURES;i++) {
     launched = circuit_launch_by_nickname(CIRCUIT_PURPOSE_S_CONNECT_REND, rp_nickname,
-                                           rend_service_requires_uptime(service), 1);
+                                          circ_needs_uptime, 1, 1);
     if (launched)
       break;
   }
@@ -540,7 +547,7 @@
          oldstate->chosen_exit_name);
 
   newcirc = circuit_launch_by_nickname(CIRCUIT_PURPOSE_S_CONNECT_REND,
-                               oldstate->chosen_exit_name, 0, 1);
+                               oldstate->chosen_exit_name, 0, 1, 1);
   if (!newcirc) {
     log_fn(LOG_WARN,"Couldn't relaunch rendezvous circuit to %s",
            oldstate->chosen_exit_name);
@@ -568,8 +575,10 @@
   log_fn(LOG_INFO, "Launching circuit to introduction point %s for service %s",
          nickname, service->service_id);
 
+  rep_hist_note_used_hidserv(time(NULL), 1, 0);
+
   ++service->n_intro_circuits_launched;
-  launched = circuit_launch_by_nickname(CIRCUIT_PURPOSE_S_ESTABLISH_INTRO, nickname, 1, 0);
+  launched = circuit_launch_by_nickname(CIRCUIT_PURPOSE_S_ESTABLISH_INTRO, nickname, 1, 0, 1);
   if (!launched) {
     log_fn(LOG_WARN, "Can't launch circuit to establish introduction at '%s'",
            nickname);
@@ -661,7 +670,7 @@
            circuit->n_circ_id);
     goto err;
   }
-  service->desc_is_dirty = 1;
+  service->desc_is_dirty = time(NULL);
   circuit->purpose = CIRCUIT_PURPOSE_S_INTRO;
 
   return 0;
@@ -849,7 +858,8 @@
                 intro, service->service_id);
         tor_free(intro);
         smartlist_del(service->intro_nodes,j--);
-        changed = service->desc_is_dirty = 1;
+        changed = 1;
+        service->desc_is_dirty = now;
       }
       smartlist_add(intro_routers, router);
     }
@@ -931,9 +941,9 @@
     }
     if (service->next_upload_time < now ||
         (service->desc_is_dirty &&
-         service->next_upload_time < now-5)) {
+         service->desc_is_dirty < now-5)) {
       /* if it's time, or if the directory servers have a wrong service
-       * descriptor and this has been the case for 5 seconds, upload a
+       * descriptor and ours has been stable for 5 seconds, upload a
        * new one. */
       upload_service_descriptor(service);
       service->next_upload_time = now + rendpostperiod;

Index: rephist.c
===================================================================
RCS file: /home2/or/cvsroot/tor/src/or/rephist.c,v
retrieving revision 1.51
retrieving revision 1.52
diff -u -d -r1.51 -r1.52
--- rephist.c	7 Dec 2004 15:29:54 -0000	1.51
+++ rephist.c	17 Jan 2005 18:13:09 -0000	1.52
@@ -666,7 +666,7 @@
   add_predicted_port(port, now);
 }
 
-#define PREDICTED_PORTS_RELEVANCE_TIME (6*3600) /* 6 hours */
+#define PREDICTED_CIRCS_RELEVANCE_TIME (3600) /* 1 hour */
 
 /** Return a pointer to the list of port numbers that
  * are likely to be asked for in the near future.
@@ -684,7 +684,7 @@
   /* clean out obsolete entries */
   for (i = 0; i < smartlist_len(predicted_ports_list); ++i) {
     tmp_time = smartlist_get(predicted_ports_times, i);
-    if (*tmp_time + PREDICTED_PORTS_RELEVANCE_TIME < now) {
+    if (*tmp_time + PREDICTED_CIRCS_RELEVANCE_TIME < now) {
       tmp_port = smartlist_get(predicted_ports_list, i);
       log_fn(LOG_DEBUG, "Expiring predicted port %d", *tmp_port);
       smartlist_del(predicted_ports_list, i);
@@ -697,3 +697,36 @@
   return predicted_ports_list;
 }
 
+/** The last time at which we needed an internal circ. */
+static time_t predicted_hidserv_time = 0;
+/** The last time we needed an internal circ with good uptime. */
+static time_t predicted_hidserv_uptime_time = 0;
+/** The last time we needed an internal circ with good capacity. */
+static time_t predicted_hidserv_capacity_time = 0;
+
+/** Remember that we used an internal circ at time <b>now</b>. */
+void rep_hist_note_used_hidserv(time_t now, int need_uptime, int need_capacity) {
+  predicted_hidserv_time = now;
+  if (need_uptime)
+    predicted_hidserv_uptime_time = now;
+  if (need_capacity)
+    predicted_hidserv_capacity_time = now;
+}
+
+/** Return 1 if we've used an internal circ recently; else return 0. */
+int rep_hist_get_predicted_hidserv(time_t now, int *need_uptime, int *need_capacity) {
+  if (!predicted_hidserv_time) /* initialize it */
+    predicted_hidserv_time = now;
+  if (predicted_hidserv_time + PREDICTED_CIRCS_RELEVANCE_TIME < now)
+    return 0; /* too long ago */
+  if (predicted_hidserv_uptime_time + PREDICTED_CIRCS_RELEVANCE_TIME < now)
+    *need_uptime = 1;
+  if (predicted_hidserv_capacity_time + PREDICTED_CIRCS_RELEVANCE_TIME < now)
+    *need_capacity = 1;
+  return 1;
+}
+
+/* not used yet */
+void rep_hist_note_used_resolve(time_t now) { }
+int rep_hist_get_predicted_resolve(time_t now) { return 0; }
+



More information about the tor-commits mailing list