Commit 6d0e611f authored by Roger Dingledine's avatar Roger Dingledine
Browse files

change when circuits are built and expired

not quite happy with it yet


svn:r817
parent f5089681
Loading
Loading
Loading
Loading
+47 −31
Original line number Diff line number Diff line
@@ -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);
    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);
      bestcirc = circ;
        leastdirty = circ;
      }
    }
  return bestcirc;
  }

  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 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
Original line number Diff line number Diff line
@@ -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,15 +549,15 @@ 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) {
@@ -567,31 +569,17 @@ static int connection_ap_handshake_attach_circuit(connection_t *conn) {
  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
Original line number Diff line number Diff line
@@ -335,19 +335,30 @@ 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) {
  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 */
    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(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;
    }
    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.
@@ -359,7 +370,6 @@ static void run_scheduled_events(time_t now) {
  }
  stats_prev_global_read_bucket = global_read_bucket;


  /* 4. We do houskeeping for each connection... */
  for(i=0;i<nfds;i++) {
    run_connection_housekeeping(i, now);
@@ -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
Original line number Diff line number Diff line
@@ -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 ***************************/