Browse Source

change when circuits are built and expired

not quite happy with it yet


svn:r817
Roger Dingledine 21 years ago
parent
commit
6d0e611fde
4 changed files with 92 additions and 75 deletions
  1. 47 31
      src/or/circuit.c
  2. 17 29
      src/or/connection_edge.c
  3. 24 12
      src/or/main.c
  4. 4 3
      src/or/or.h

+ 47 - 31
src/or/circuit.c

@@ -205,18 +205,45 @@ circuit_t *circuit_get_by_conn(connection_t *conn) {
   return NULL;
 }
 
-circuit_t *circuit_get_newest_open(void) {
-  circuit_t *circ, *bestcirc=NULL;
+/* Find the newest circ that conn can use, preferably one which is
+ * dirty and not too old.
+ * If !conn, return newest open.
+ */
+circuit_t *circuit_get_newest_open(connection_t *conn) {
+  circuit_t *circ, *newest=NULL, *leastdirty=NULL;
 
   for(circ=global_circuitlist;circ;circ = circ->next) {
-    if(circ->cpath && circ->state == CIRCUIT_STATE_OPEN && circ->n_conn && (!bestcirc ||
-      bestcirc->timestamp_created < circ->timestamp_created)) {
-      log_fn(LOG_DEBUG,"Choosing circuit %s:%d:%d.", circ->n_conn->address, circ->n_port, circ->n_circ_id);
-      assert(circ->n_circ_id);
-      bestcirc = circ;
+    if(conn && connection_ap_can_use_exit(conn,
+       router_get_by_addr_port(circ->cpath->prev->addr, circ->cpath->prev->port)) < 0) {
+      log_fn(LOG_DEBUG,"Skipping %s:%d:%d because we couldn't exit there.",
+             circ->n_conn->address, circ->n_port, circ->n_circ_id);
+      continue;
     }
+    if(circ->cpath && circ->state == CIRCUIT_STATE_OPEN && circ->n_conn) {
+      if(!newest || newest->timestamp_created < circ->timestamp_created) {
+        assert(circ->n_circ_id);
+        newest = circ;
+      }
+      if(conn && circ->timestamp_dirty &&
+         (!leastdirty || leastdirty->timestamp_dirty < circ->timestamp_dirty)) {
+        assert(circ->n_circ_id);
+        leastdirty = circ;
+      }
+    }
+  }
+
+  if(leastdirty &&
+     leastdirty->timestamp_dirty+options.NewCircuitPeriod > time(NULL)) {
+    log_fn(LOG_DEBUG,"Choosing in-use circuit %s:%d:%d.",
+           leastdirty->n_conn->address, leastdirty->n_port, leastdirty->n_circ_id);
+    return leastdirty;
+  }
+  if(newest) {
+    log_fn(LOG_DEBUG,"Choosing circuit %s:%d:%d.",
+           newest->n_conn->address, newest->n_port, newest->n_circ_id);
+    return newest;
   }
-  return bestcirc;
+  return NULL;
 }
 
 int circuit_deliver_relay_cell(cell_t *cell, circuit_t *circ,
@@ -491,13 +518,8 @@ int circuit_consider_sending_sendme(circuit_t *circ, int edge_type, crypt_path_t
 
 void circuit_close(circuit_t *circ) {
   connection_t *conn;
-  circuit_t *youngest=NULL;
 
   assert(circ);
-  if(options.SocksPort) {
-    youngest = circuit_get_newest_open();
-    log_fn(LOG_DEBUG,"youngest %d, circ %d.",(int)youngest, (int)circ);
-  }
   circuit_remove(circ);
   if(circ->n_conn)
     connection_send_destroy(circ->n_circ_id, circ->n_conn);
@@ -509,11 +531,6 @@ void circuit_close(circuit_t *circ) {
   for(conn=circ->p_streams; conn; conn=conn->next_stream) {
     connection_send_destroy(circ->p_circ_id, conn); 
   }
-  if(options.SocksPort && youngest == circ) { /* check this after we've sent the destroys, to reduce races */
-    /* our current circuit just died. Launch another one pronto. */
-    log_fn(LOG_INFO,"Youngest circuit dying. Launching a replacement.");
-    circuit_launch_new(1);
-  }
   circuit_free(circ);
 }
 
@@ -606,15 +623,15 @@ void circuit_dump_by_conn(connection_t *conn, int severity) {
 
 void circuit_expire_unused_circuits(void) {
   circuit_t *circ, *tmpcirc;
-  circuit_t *youngest;
-
-  youngest = circuit_get_newest_open();
+  time_t now = time(NULL);
 
   circ = global_circuitlist;
   while(circ) {
     tmpcirc = circ;
     circ = circ->next;
-    if(tmpcirc != youngest && !tmpcirc->p_conn && !tmpcirc->p_streams) {
+    if(tmpcirc->timestamp_dirty &&
+       tmpcirc->timestamp_dirty + options.NewCircuitPeriod < now &&
+       !tmpcirc->p_conn && !tmpcirc->p_streams) {
       log_fn(LOG_DEBUG,"Closing n_circ_id %d",tmpcirc->n_circ_id);
       circuit_close(tmpcirc);
     }
@@ -624,34 +641,33 @@ void circuit_expire_unused_circuits(void) {
 /* failure_status code: negative means reset failures to 0. Other values mean
  * add that value to the current number of failures, then if we don't have too
  * many failures on record, try to make a new circuit.
+ *
+ * Return -1 if you aren't going to try to make a circuit, 0 if you did try.
  */
-void circuit_launch_new(int failure_status) {
+int circuit_launch_new(int failure_status) {
   static int failures=0;
 
   if(!options.SocksPort) /* we're not an application proxy. no need for circuits. */
-    return;
+    return -1;
 
   if(failure_status == -1) { /* I was called because a circuit succeeded */
     failures = 0;
-    return;
+    return -1;
   }
 
   failures += failure_status;
 
-retry_circuit:
-
   if(failures > 5) {
-    log_fn(LOG_INFO,"Giving up for now, %d failures.", failures);
-    return;
+    return -1;
   }
 
   if(circuit_establish_circuit() < 0) {
     failures++;
-    goto retry_circuit;
+    return 0;
   }
 
   failures = 0;
-  return;
+  return 0;
 }
 
 int circuit_establish_circuit(void) {

+ 17 - 29
src/or/connection_edge.c

@@ -18,7 +18,6 @@ static void connection_edge_consider_sending_sendme(connection_t *conn);
 
 static uint32_t client_dns_lookup_entry(const char *address);
 static void client_dns_set_entry(const char *address, uint32_t val);
-static void client_dns_clean(void);
 
 int connection_edge_process_inbuf(connection_t *conn) {
 
@@ -255,7 +254,8 @@ int connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ, connection
         addr = ntohl(*cell->payload+RELAY_HEADER_SIZE+1);
         client_dns_set_entry(conn->socks_request->address, addr);
         conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
-        /* XXX Build another circuit as required */
+        if(connection_ap_handshake_attach_circuit(conn) < 0)
+          circuit_launch_new(1); /* Build another circuit to handle this stream */
         return 0;
       }
       log_fn(LOG_INFO,"end cell (%s) for stream %d. Removing stream.",
@@ -489,10 +489,12 @@ void connection_ap_attach_pending(void)
     if (carray[i]->type != CONN_TYPE_AP || 
         carray[i]->type != AP_CONN_STATE_CIRCUIT_WAIT)
       continue;
-    if (connection_ap_handshake_attach_circuit(carray[i])) {
-      need_new_circuit = 1; /* XXX act on this. */
+    if (connection_ap_handshake_attach_circuit(carray[i])<0) {
+      need_new_circuit = 1;
     }
   }
+  if(need_new_circuit)
+    circuit_launch_new(1);
 }
 
 static void connection_edge_consider_sending_sendme(connection_t *conn) {
@@ -547,51 +549,37 @@ static int connection_ap_handshake_process_socks(connection_t *conn) {
   } /* else socks handshake is done, continue processing */
 
   conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
-  if (connection_ap_handshake_attach_circuit(conn)) {
-    /* XXX we need a circuit */
-  }      
+  if (connection_ap_handshake_attach_circuit(conn)<0) {
+    circuit_launch_new(1);
+  }
   return 0;
 }
 
 /* Try to find a live circuit.  If we don't find one, tell 'conn' to
  * stop reading and return 0.  Otherwise, associate the CONN_TYPE_AP
- * connection 'conn' with the newest live circuit, start sending a
+ * connection 'conn' with a safe live circuit, start sending a
  * BEGIN cell down the circuit, and return 1.
  */
 static int connection_ap_handshake_attach_circuit(connection_t *conn) {
   circuit_t *circ;
-  
+
   assert(conn);
   assert(conn->type == CONN_TYPE_AP);
   assert(conn->state == AP_CONN_STATE_CIRCUIT_WAIT);
   assert(conn->socks_request);
-  
+
   /* find the circuit that we should use, if there is one. */
-  circ = circuit_get_newest_open();
+  circ = circuit_get_newest_open(conn);
 
   if(!circ) {
-    log_fn(LOG_INFO,"No circuit ready for edge connection; delaying.");
-    connection_stop_reading(conn);
-    /* XXX both this and the start_reading below can go away if we
-     *     remove our notion that we shouldn't read from a socks
-     *     client until we're connected. the socks spec promises that it
-     *     won't write. is that good enough?
-     */
-    return -1;
-  }
-  if (connection_ap_can_use_exit(conn,
-                  router_get_by_addr_port(circ->cpath->prev->addr,
-                                          circ->cpath->prev->port)) < 0) {
-    /* The exit on this circuit will _definitely_ reject this connection. */
-    log_fn(LOG_INFO,"Most recent circuit will reject AP connection; delaying.");
-    /* XXX Build another circuit. */
-    connection_stop_reading(conn);
+    log_fn(LOG_INFO,"No safe circuit ready for edge connection; delaying.");
+    connection_stop_reading(conn); /* don't read until the connected cell arrives */
     return -1;
   }
 
   connection_start_reading(conn);
 
-  circ->dirty = 1;
+  circ->timestamp_dirty = time(NULL);
 
   /* add it into the linked list of streams on this circuit */
   log_fn(LOG_DEBUG,"attaching new conn to circ. n_circ_id %d.", circ->n_circ_id);
@@ -893,7 +881,7 @@ static void client_dns_set_entry(const char *address, uint32_t val)
   }
 }
 
-static void client_dns_clean(void)
+void client_dns_clean(void)
 {
   struct client_dns_entry **expired_entries;
   int n_expired_entries = 0;

+ 24 - 12
src/or/main.c

@@ -335,20 +335,31 @@ static void run_scheduled_events(time_t now) {
     time_to_fetch_directory = now + options.DirFetchPostPeriod;
   }
 
-  /* 2. Every NewCircuitPeriod seconds, we expire old circuits and make a 
-   *    new one as needed.
+  /* 2. 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,
+   *    and we make a new circ if there are no clean circuits.
    */
-  if(options.SocksPort && time_to_new_circuit < now) {
-    circuit_expire_unused_circuits();
-    circuit_launch_new(-1); /* tell it to forget about previous failures */
-    circ = circuit_get_newest_open();
-    if(!circ || circ->dirty) {
-      log_fn(LOG_INFO,"Youngest circuit %s; launching replacement.", circ ? "dirty" : "missing");
-      circuit_launch_new(0); /* make an onion and lay the circuit */
+  if(options.SocksPort) {
+    circ = circuit_get_newest_open(NULL);
+    if(time_to_new_circuit < now) {
+      client_dns_clean();
+      circuit_expire_unused_circuits();
+      circuit_launch_new(-1); /* tell it to forget about previous failures */
+      if(circ && circ->timestamp_dirty) {
+        log_fn(LOG_INFO,"Youngest circuit dirty; launching replacement.");
+        circuit_launch_new(0); /* make a new circuit */
+      }
+      time_to_new_circuit = now + options.NewCircuitPeriod;
     }
-    time_to_new_circuit = now + options.NewCircuitPeriod;
+    if(!circ) {
+      circuit_launch_new(1);
+    }
+    /* XXX also check if we have any circuit_pending streams and we're not
+     * currently building a circuit for them.
+     */
   }
-  
+
   /* 3. Every second, we check how much bandwidth we've consumed and 
    *    increment global_read_bucket.
    */
@@ -358,7 +369,6 @@ static void run_scheduled_events(time_t now) {
     log_fn(LOG_DEBUG,"global_read_bucket now %d.", global_read_bucket);
   }
   stats_prev_global_read_bucket = global_read_bucket;
-  
 
   /* 4. We do houskeeping for each connection... */
   for(i=0;i<nfds;i++) {
@@ -368,6 +378,8 @@ static void run_scheduled_events(time_t now) {
   /* 5. and blow away any connections that need to die. can't do this later
    * because we might open up a circuit and not realize we're about to cull
    * the connection it's running over.
+   * XXX we can remove this step once we audit circuit-building to make sure
+   *     it doesn't pick a marked-for-close conn. -RD
    */
   for(i=0;i<nfds;i++)
     conn_close_if_marked(i);

+ 4 - 3
src/or/or.h

@@ -419,7 +419,7 @@ struct circuit_t {
 
   char onionskin[DH_ONIONSKIN_LEN]; /* for storage while onionskin pending */
   long timestamp_created;
-  uint8_t dirty; /* whether this circuit has been used yet */
+  long timestamp_dirty; /* when the circuit was first used, or 0 if clean */
 
   uint8_t state;
 
@@ -515,7 +515,7 @@ void circuit_free_cpath(crypt_path_t *cpath);
 circuit_t *circuit_enumerate_by_naddr_nport(circuit_t *start, uint32_t naddr, uint16_t nport);
 circuit_t *circuit_get_by_circ_id_conn(circ_id_t circ_id, connection_t *conn);
 circuit_t *circuit_get_by_conn(connection_t *conn);
-circuit_t *circuit_get_newest_open(void);
+circuit_t *circuit_get_newest_open(connection_t *conn);
 
 int circuit_deliver_relay_cell(cell_t *cell, circuit_t *circ,
                                int cell_direction, crypt_path_t *layer_hint);
@@ -533,7 +533,7 @@ void circuit_about_to_close_connection(connection_t *conn);
 void circuit_dump_by_conn(connection_t *conn, int severity);
 
 void circuit_expire_unused_circuits(void);
-void circuit_launch_new(int failure_status);
+int circuit_launch_new(int failure_status);
 int circuit_establish_circuit(void);
 void circuit_n_conn_open(connection_t *or_conn);
 int circuit_send_next_onion_skin(circuit_t *circ);
@@ -631,6 +631,7 @@ extern uint64_t stats_n_data_cells_received;
 extern uint64_t stats_n_data_bytes_received;
 
 void client_dns_init(void);
+void client_dns_clean(void);
 
 /********************************* connection_or.c ***************************/