[tor-commits] [tor/master] Merge remote branch 'origin/maint-0.2.2'

nickm at torproject.org nickm at torproject.org
Tue Mar 8 21:10:30 UTC 2011


commit f9bb3ced51422bc8f6c3702a169a799614bbce02
Merge: d5907e7 0d78a16
Author: Nick Mathewson <nickm at torproject.org>
Date:   Tue Mar 8 16:10:40 2011 -0500

    Merge remote branch 'origin/maint-0.2.2'
    
    Resolved trivial one-line conflicts.
    
    Conflicts:
    	src/or/dirserv.c
    	src/or/rephist.c

 changes/bug1035        |   13 +++++++++
 doc/HACKING            |   13 ++++-----
 src/or/connection_or.c |    3 --
 src/or/dirserv.c       |   19 +++++++++----
 src/or/rephist.c       |   65 ++++++++++++++++++++++++++++++++++++++++++------
 src/or/rephist.h       |    3 +-
 6 files changed, 91 insertions(+), 25 deletions(-)

diff --combined src/or/connection_or.c
index 5e28f94,2d24444..01cc160
--- a/src/or/connection_or.c
+++ b/src/or/connection_or.c
@@@ -22,17 -22,12 +22,17 @@@
  #include "geoip.h"
  #include "main.h"
  #include "networkstatus.h"
 +#include "nodelist.h"
  #include "reasons.h"
  #include "relay.h"
  #include "rephist.h"
  #include "router.h"
  #include "routerlist.h"
  
 +#ifdef USE_BUFFEREVENTS
 +#include <event2/bufferevent_ssl.h>
 +#endif
 +
  static int connection_tls_finish_handshake(or_connection_t *conn);
  static int connection_or_process_cells_from_inbuf(or_connection_t *conn);
  static int connection_or_send_versions(or_connection_t *conn);
@@@ -42,14 -37,6 +42,14 @@@ static int connection_or_check_valid_tl
                                                     int started_here,
                                                     char *digest_rcvd_out);
  
 +static void connection_or_tls_renegotiated_cb(tor_tls_t *tls, void *_conn);
 +
 +#ifdef USE_BUFFEREVENTS
 +static void connection_or_handle_event_cb(struct bufferevent *bufev,
 +                                          short event, void *arg);
 +#include <event2/buffer.h>/*XXXX REMOVE */
 +#endif
 +
  /**************************************************************/
  
  /** Map from identity digest of connected OR or desired OR to a connection_t
@@@ -191,8 -178,7 +191,8 @@@ var_cell_pack_header(const var_cell_t *
  var_cell_t *
  var_cell_new(uint16_t payload_len)
  {
 -  var_cell_t *cell = tor_malloc(sizeof(var_cell_t)+payload_len-1);
 +  size_t size = STRUCT_OFFSET(var_cell_t, payload) + payload_len;
 +  var_cell_t *cell = tor_malloc(size);
    cell->payload_len = payload_len;
    cell->command = 0;
    cell->circ_id = 0;
@@@ -241,14 -227,6 +241,14 @@@ connection_or_process_inbuf(or_connecti
        }
  
        return ret;
 +#ifdef USE_BUFFEREVENTS
 +    case OR_CONN_STATE_TLS_SERVER_RENEGOTIATING:
 +      if (tor_tls_server_got_renegotiate(conn->tls))
 +        connection_or_tls_renegotiated_cb(conn->tls, conn);
 +      if (conn->_base.marked_for_close)
 +        return 0;
 +      /* fall through. */
 +#endif
      case OR_CONN_STATE_OPEN:
      case OR_CONN_STATE_OR_HANDSHAKING:
        return connection_or_process_cells_from_inbuf(conn);
@@@ -270,7 -248,7 +270,7 @@@
  int
  connection_or_flushed_some(or_connection_t *conn)
  {
 -  size_t datalen = buf_datalen(conn->_base.outbuf);
 +  size_t datalen = connection_get_outbuf_len(TO_CONN(conn));
    /* If we're under the low water mark, add cells until we're just over the
     * high water mark. */
    if (datalen < OR_CONN_LOWWATER) {
@@@ -303,6 -281,7 +303,6 @@@ connection_or_finished_flushing(or_conn
      case OR_CONN_STATE_PROXY_HANDSHAKING:
      case OR_CONN_STATE_OPEN:
      case OR_CONN_STATE_OR_HANDSHAKING:
 -      connection_stop_writing(TO_CONN(conn));
        break;
      default:
        log_err(LD_BUG,"Called in unexpected state %d.", conn->_base.state);
@@@ -363,7 -342,7 +363,7 @@@ connection_or_digest_is_known_relay(con
  {
    if (router_get_consensus_status_by_id(id_digest))
      return 1; /* It's in the consensus: "yes" */
 -  if (router_get_by_digest(id_digest))
 +  if (router_get_by_id_digest(id_digest))
      return 1; /* Not in the consensus, but we have a descriptor for
                 * it. Probably it was in a recent consensus. "Yes". */
    return 0;
@@@ -400,22 -379,6 +400,22 @@@ connection_or_update_token_buckets_help
  
    conn->bandwidthrate = rate;
    conn->bandwidthburst = burst;
 +#ifdef USE_BUFFEREVENTS
 +  {
 +    const struct timeval *tick = tor_libevent_get_one_tick_timeout();
 +    struct ev_token_bucket_cfg *cfg, *old_cfg;
 +    int rate_per_tick = rate / TOR_LIBEVENT_TICKS_PER_SECOND;
 +    cfg = ev_token_bucket_cfg_new(rate_per_tick, burst, rate_per_tick,
 +                                  burst, tick);
 +    old_cfg = conn->bucket_cfg;
 +    if (conn->_base.bufev)
 +      bufferevent_set_rate_limit(conn->_base.bufev, cfg);
 +    if (old_cfg)
 +      ev_token_bucket_cfg_free(old_cfg);
 +    conn->bucket_cfg = cfg;
 +    (void) reset; /* No way to do this with libevent yet. */
 +  }
 +#else
    if (reset) { /* set up the token buckets to be full */
      conn->read_bucket = conn->write_bucket = burst;
      return;
@@@ -426,7 -389,6 +426,7 @@@
      conn->read_bucket = burst;
    if (conn->write_bucket > burst)
      conn->write_bucket = burst;
 +#endif
  }
  
  /** Either our set of relays or our per-conn rate limits have changed.
@@@ -450,7 -412,7 +450,7 @@@ connection_or_init_conn_from_address(or
                                       const char *id_digest,
                                       int started_here)
  {
 -  routerinfo_t *r = router_get_by_digest(id_digest);
 +  const node_t *r = node_get_by_id(id_digest);
    connection_or_set_identity_digest(conn, id_digest);
    connection_or_update_token_buckets_helper(conn, 1, get_options());
  
@@@ -458,10 -420,8 +458,10 @@@
    tor_addr_copy(&conn->_base.addr, addr);
    tor_addr_copy(&conn->real_addr, addr);
    if (r) {
 +    tor_addr_t node_addr;
 +    node_get_addr(r, &node_addr);
      /* XXXX proposal 118 will make this more complex. */
 -    if (tor_addr_eq_ipv4h(&conn->_base.addr, r->addr))
 +    if (tor_addr_eq(&conn->_base.addr, &node_addr))
        conn->is_canonical = 1;
      if (!started_here) {
        /* Override the addr/port, so our log messages will make sense.
@@@ -474,12 -434,12 +474,12 @@@
         * right IP address and port 56244, that wouldn't be as helpful. now we
         * log the "right" port too, so we know if it's moria1 or moria2.
         */
 -      tor_addr_from_ipv4h(&conn->_base.addr, r->addr);
 -      conn->_base.port = r->or_port;
 +      tor_addr_copy(&conn->_base.addr, &node_addr);
 +      conn->_base.port = node_get_orport(r);
      }
 -    conn->nickname = tor_strdup(r->nickname);
 +    conn->nickname = tor_strdup(node_get_nickname(r));
      tor_free(conn->_base.address);
 -    conn->_base.address = tor_strdup(r->address);
 +    conn->_base.address = tor_dup_addr(&node_addr);
    } else {
      const char *n;
      /* If we're an authoritative directory server, we may know a
@@@ -899,7 -859,6 +899,7 @@@ in
  connection_tls_start_handshake(or_connection_t *conn, int receiving)
  {
    conn->_base.state = OR_CONN_STATE_TLS_HANDSHAKING;
 +  tor_assert(!conn->tls);
    conn->tls = tor_tls_new(conn->_base.s, receiving);
    tor_tls_set_logged_address(conn->tls, // XXX client and relay?
        escaped_safe_str(conn->_base.address));
@@@ -907,38 -866,12 +907,38 @@@
      log_warn(LD_BUG,"tor_tls_new failed. Closing.");
      return -1;
    }
 +#ifdef USE_BUFFEREVENTS
 +  if (connection_type_uses_bufferevent(TO_CONN(conn))) {
 +    const int filtering = get_options()->_UseFilteringSSLBufferevents;
 +    struct bufferevent *b =
 +      tor_tls_init_bufferevent(conn->tls, conn->_base.bufev, conn->_base.s,
 +                               receiving, filtering);
 +    if (!b) {
 +      log_warn(LD_BUG,"tor_tls_init_bufferevent failed. Closing.");
 +      return -1;
 +    }
 +    conn->_base.bufev = b;
 +    if (conn->bucket_cfg)
 +      bufferevent_set_rate_limit(conn->_base.bufev, conn->bucket_cfg);
 +    connection_enable_rate_limiting(TO_CONN(conn));
 +
 +    connection_configure_bufferevent_callbacks(TO_CONN(conn));
 +    bufferevent_setcb(b,
 +                      connection_handle_read_cb,
 +                      connection_handle_write_cb,
 +                      connection_or_handle_event_cb,/* overriding this one*/
 +                      TO_CONN(conn));
 +  }
 +#endif
    connection_start_reading(TO_CONN(conn));
    log_debug(LD_HANDSHAKE,"starting TLS handshake on fd %d", conn->_base.s);
    note_crypto_pk_op(receiving ? TLS_HANDSHAKE_S : TLS_HANDSHAKE_C);
  
 -  if (connection_tls_continue_handshake(conn) < 0) {
 -    return -1;
 +  IF_HAS_BUFFEREVENT(TO_CONN(conn), {
 +    /* ???? */;
 +  }) ELSE_IF_NO_BUFFEREVENT {
 +    if (connection_tls_continue_handshake(conn) < 0)
 +      return -1;
    }
    return 0;
  }
@@@ -1023,78 -956,6 +1023,78 @@@ connection_tls_continue_handshake(or_co
    return 0;
  }
  
 +#ifdef USE_BUFFEREVENTS
 +static void
 +connection_or_handle_event_cb(struct bufferevent *bufev, short event,
 +                              void *arg)
 +{
 +  struct or_connection_t *conn = TO_OR_CONN(arg);
 +
 +  /* XXXX cut-and-paste code; should become a function. */
 +  if (event & BEV_EVENT_CONNECTED) {
 +    if (conn->_base.state == OR_CONN_STATE_TLS_HANDSHAKING) {
 +      if (tor_tls_finish_handshake(conn->tls) < 0) {
 +        log_warn(LD_OR, "Problem finishing handshake");
 +        connection_mark_for_close(TO_CONN(conn));
 +        return;
 +      }
 +    }
 +
 +    if (! tor_tls_used_v1_handshake(conn->tls)) {
 +      if (!tor_tls_is_server(conn->tls)) {
 +        if (conn->_base.state == OR_CONN_STATE_TLS_HANDSHAKING) {
 +          conn->_base.state = OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING;
 +          tor_tls_unblock_renegotiation(conn->tls);
 +          if (bufferevent_ssl_renegotiate(conn->_base.bufev)<0) {
 +            log_warn(LD_OR, "Start_renegotiating went badly.");
 +            connection_mark_for_close(TO_CONN(conn));
 +          }
 +          tor_tls_unblock_renegotiation(conn->tls);
 +          return; /* ???? */
 +        }
 +      } else if (tor_tls_get_num_server_handshakes(conn->tls) == 1) {
 +        /* improved handshake, as a server. Only got one handshake, so
 +         * wait for the next one. */
 +        tor_tls_set_renegotiate_callback(conn->tls,
 +                                         connection_or_tls_renegotiated_cb,
 +                                         conn);
 +        conn->_base.state = OR_CONN_STATE_TLS_SERVER_RENEGOTIATING;
 +        /* return 0; */
 +        return; /* ???? */
 +      } else {
 +        const int handshakes = tor_tls_get_num_server_handshakes(conn->tls);
 +        tor_assert(handshakes >= 2);
 +        if (handshakes == 2) {
 +          /* improved handshake, as a server.  Two handshakes happened already,
 +           * so we treat renegotiation as done.
 +           */
 +          connection_or_tls_renegotiated_cb(conn->tls, conn);
 +        } else {
 +          log_warn(LD_OR, "More than two handshakes done on connection. "
 +                   "Closing.");
 +          connection_mark_for_close(TO_CONN(conn));
 +        }
 +        return;
 +      }
 +    }
 +    connection_watch_events(TO_CONN(conn), READ_EVENT|WRITE_EVENT);
 +    if (connection_tls_finish_handshake(conn) < 0)
 +      connection_mark_for_close(TO_CONN(conn)); /* ???? */
 +    return;
 +  }
 +
 +  if (event & BEV_EVENT_ERROR) {
 +    unsigned long err;
 +    while ((err = bufferevent_get_openssl_error(bufev))) {
 +      tor_tls_log_one_error(conn->tls, err, LOG_WARN, LD_OR,
 +                            "handshaking (with bufferevent)");
 +    }
 +  }
 +
 +  connection_handle_event_cb(bufev, event, arg);
 +}
 +#endif
 +
  /** Return 1 if we initiated this connection, or 0 if it started
   * out as an incoming connection.
   */
@@@ -1237,9 -1098,6 +1237,6 @@@ connection_or_check_valid_tls_handshake
        as_advertised = 0;
      }
      if (authdir_mode_tests_reachability(options)) {
-       /* We initiated this connection to address:port.  Drop all routers
-        * with the same address:port and a different key.
-        */
        dirserv_orconn_tls_done(conn->_base.address, conn->_base.port,
                                digest_rcvd_out, as_advertised);
      }
@@@ -1266,9 -1124,7 +1263,9 @@@ connection_tls_finish_handshake(or_conn
    char digest_rcvd[DIGEST_LEN];
    int started_here = connection_or_nonopen_was_started_here(conn);
  
 -  log_debug(LD_HANDSHAKE,"tls handshake with %s done. verifying.",
 +  log_debug(LD_HANDSHAKE,"%s tls handshake on %p with %s done. verifying.",
 +            started_here?"outgoing":"incoming",
 +            conn,
              safe_str_client(conn->_base.address));
  
    directory_set_dirty();
@@@ -1349,7 -1205,7 +1346,7 @@@ connection_or_set_state_open(or_connect
      router_set_status(conn->identity_digest, 1);
    } else {
      /* only report it to the geoip module if it's not a known router */
 -    if (!router_get_by_digest(conn->identity_digest)) {
 +    if (!router_get_by_id_digest(conn->identity_digest)) {
        if (tor_addr_family(&TO_CONN(conn)->addr) == AF_INET) {
          /*XXXX IP6 support ipv6 geoip.*/
          uint32_t a = tor_addr_to_ipv4h(&TO_CONN(conn)->addr);
@@@ -1360,12 -1216,8 +1357,12 @@@
  
    or_handshake_state_free(conn->handshake_state);
    conn->handshake_state = NULL;
 +  IF_HAS_BUFFEREVENT(TO_CONN(conn), {
 +    connection_watch_events(TO_CONN(conn), READ_EVENT|WRITE_EVENT);
 +  }) ELSE_IF_NO_BUFFEREVENT {
 +    connection_start_reading(TO_CONN(conn));
 +  }
  
 -  connection_start_reading(TO_CONN(conn));
    circuit_n_conn_done(conn, 1); /* send the pending creates, if any. */
  
    return 0;
@@@ -1410,18 -1262,12 +1407,18 @@@ connection_or_write_var_cell_to_buf(con
      conn->timestamp_last_added_nonpadding = approx_time();
  }
  
 -/** See whether there's a variable-length cell waiting on <b>conn</b>'s
 +/** See whether there's a variable-length cell waiting on <b>or_conn</b>'s
   * inbuf.  Return values as for fetch_var_cell_from_buf(). */
  static int
 -connection_fetch_var_cell_from_buf(or_connection_t *conn, var_cell_t **out)
 +connection_fetch_var_cell_from_buf(or_connection_t *or_conn, var_cell_t **out)
  {
 -  return fetch_var_cell_from_buf(conn->_base.inbuf, out, conn->link_proto);
 +  connection_t *conn = TO_CONN(or_conn);
 +  IF_HAS_BUFFEREVENT(conn, {
 +    struct evbuffer *input = bufferevent_get_input(conn->bufev);
 +    return fetch_var_cell_from_evbuffer(input, out, or_conn->link_proto);
 +  }) ELSE_IF_NO_BUFFEREVENT {
 +    return fetch_var_cell_from_buf(conn->inbuf, out, or_conn->link_proto);
 +  }
  }
  
  /** Process cells from <b>conn</b>'s inbuf.
@@@ -1439,7 -1285,7 +1436,7 @@@ connection_or_process_cells_from_inbuf(
    while (1) {
      log_debug(LD_OR,
                "%d: starting, inbuf_datalen %d (%d pending in tls object).",
 -              conn->_base.s,(int)buf_datalen(conn->_base.inbuf),
 +              conn->_base.s,(int)connection_get_inbuf_len(TO_CONN(conn)),
                tor_tls_get_pending_bytes(conn->tls));
      if (connection_fetch_var_cell_from_buf(conn, &var_cell)) {
        if (!var_cell)
@@@ -1450,8 -1296,8 +1447,8 @@@
      } else {
        char buf[CELL_NETWORK_SIZE];
        cell_t cell;
 -      if (buf_datalen(conn->_base.inbuf) < CELL_NETWORK_SIZE) /* whole response
 -                                                                 available? */
 +      if (connection_get_inbuf_len(TO_CONN(conn))
 +          < CELL_NETWORK_SIZE) /* whole response available? */
          return 0; /* not yet */
  
        circuit_build_times_network_is_live(&circ_times);
@@@ -1538,7 -1384,7 +1535,7 @@@ connection_or_send_netinfo(or_connectio
  {
    cell_t cell;
    time_t now = time(NULL);
 -  routerinfo_t *me;
 +  const routerinfo_t *me;
    int len;
    uint8_t *out;
  
diff --combined src/or/dirserv.c
index bf61059,aeeab45..4f06de1
--- a/src/or/dirserv.c
+++ b/src/or/dirserv.c
@@@ -16,7 -16,6 +16,7 @@@
  #include "hibernate.h"
  #include "microdesc.h"
  #include "networkstatus.h"
 +#include "nodelist.h"
  #include "policies.h"
  #include "rephist.h"
  #include "router.h"
@@@ -66,6 -65,8 +66,6 @@@ static char *format_versions_list(confi
  struct authdir_config_t;
  static int add_fingerprint_to_dir(const char *nickname, const char *fp,
                                    struct authdir_config_t *list);
 -static uint32_t dirserv_router_get_status(const routerinfo_t *router,
 -                                          const char **msg);
  static uint32_t
  dirserv_get_status_impl(const char *fp, const char *nickname,
                          const char *address,
@@@ -73,8 -74,7 +73,8 @@@
                          const char *platform, const char *contact,
                          const char **msg, int should_log);
  static void clear_cached_dir(cached_dir_t *d);
 -static signed_descriptor_t *get_signed_descriptor_by_fp(const char *fp,
 +static const signed_descriptor_t *get_signed_descriptor_by_fp(
 +                                                        const char *fp,
                                                          int extrainfo,
                                                          time_t publish_cutoff);
  static int dirserv_add_extrainfo(extrainfo_t *ei, const char **msg);
@@@ -303,7 -303,7 +303,7 @@@ dirserv_load_fingerprint_file(void
   *
   * If the status is 'FP_REJECT' and <b>msg</b> is provided, set
   * *<b>msg</b> to an explanation of why. */
 -static uint32_t
 +uint32_t
  dirserv_router_get_status(const routerinfo_t *router, const char **msg)
  {
    char d[DIGEST_LEN];
@@@ -325,7 -325,7 +325,7 @@@
  /** Return true if there is no point in downloading the router described by
   * <b>rs</b> because this directory would reject it. */
  int
 -dirserv_would_reject_router(routerstatus_t *rs)
 +dirserv_would_reject_router(const routerstatus_t *rs)
  {
    uint32_t res;
  
@@@ -360,7 -360,7 +360,7 @@@ dirserv_get_name_status(const char *id_
    return 0;
  }
  
 -/** Helper: As dirserv_get_router_status, but takes the router fingerprint
 +/** Helper: As dirserv_router_get_status, but takes the router fingerprint
   * (hex, no spaces), nickname, address (used for logging only), IP address, OR
   * port, platform (logging only) and contact info (logging only) as arguments.
   *
@@@ -375,7 -375,7 +375,7 @@@ dirserv_get_status_impl(const char *id_
                          const char **msg, int should_log)
  {
    int reject_unlisted = get_options()->AuthDirRejectUnlisted;
 -  uint32_t result = 0;
 +  uint32_t result;
    router_status_t *status_by_digest;
  
    if (!fingerprint_list)
@@@ -539,7 -539,7 +539,7 @@@ dirserv_router_has_valid_address(router
   */
  int
  authdir_wants_to_reject_router(routerinfo_t *ri, const char **msg,
 -                               int complain)
 +                               int complain, int *valid_out)
  {
    /* Okay.  Now check whether the fingerprint is recognized. */
    uint32_t status = dirserv_router_get_status(ri, msg);
@@@ -580,24 -580,15 +580,24 @@@
      *msg = "Rejected: Address is not an IP, or IP is a private address.";
      return -1;
    }
 -  /* Okay, looks like we're willing to accept this one. */
 -  ri->is_named = (status & FP_NAMED) ? 1 : 0;
 -  ri->is_valid = (status & FP_INVALID) ? 0 : 1;
 -  ri->is_bad_directory = (status & FP_BADDIR) ? 1 : 0;
 -  ri->is_bad_exit = (status & FP_BADEXIT) ? 1 : 0;
 +
 +  *valid_out = ! (status & FP_INVALID);
  
    return 0;
  }
  
 +/** Update the relevant flags of <b>node</b> based on our opinion as a
 + * directory authority in <b>authstatus</b>, as returned by
 + * dirserv_router_get_status or equivalent.  */
 +void
 +dirserv_set_node_flags_from_authoritative_status(node_t *node,
 +                                                 uint32_t authstatus)
 +{
 +  node->is_valid = (authstatus & FP_INVALID) ? 0 : 1;
 +  node->is_bad_directory = (authstatus & FP_BADDIR) ? 1 : 0;
 +  node->is_bad_exit = (authstatus & FP_BADEXIT) ? 1 : 0;
 +}
 +
  /** True iff <b>a</b> is more severe than <b>b</b>. */
  static int
  WRA_MORE_SEVERE(was_router_added_t a, was_router_added_t b)
@@@ -722,7 -713,7 +722,7 @@@ dirserv_add_descriptor(routerinfo_t *ri
     * from this server.  (We do this here and not in router_add_to_routerlist
     * because we want to be able to accept the newest router descriptor that
     * another authority has, so we all converge on the same one.) */
 -  ri_old = router_get_by_digest(ri->cache_info.identity_digest);
 +  ri_old = router_get_mutable_by_digest(ri->cache_info.identity_digest);
    if (ri_old && ri_old->cache_info.published_on < ri->cache_info.published_on
        && router_differences_are_cosmetic(ri_old, ri)
        && !router_is_me(ri)) {
@@@ -766,7 -757,8 +766,7 @@@
      routerlist_descriptors_added(changed, 0);
      smartlist_free(changed);
      if (!*msg) {
 -      *msg =  ri->is_valid ? "Descriptor for valid server accepted" :
 -        "Descriptor for invalid server accepted";
 +      *msg =  "Descriptor accepted";
      }
      log_info(LD_DIRSERV,
               "Added descriptor from '%s' (source: %s): %s.",
@@@ -781,12 -773,12 +781,12 @@@
  static was_router_added_t
  dirserv_add_extrainfo(extrainfo_t *ei, const char **msg)
  {
 -  routerinfo_t *ri;
 +  const routerinfo_t *ri;
    int r;
    tor_assert(msg);
    *msg = NULL;
  
 -  ri = router_get_by_digest(ei->cache_info.identity_digest);
 +  ri = router_get_by_id_digest(ei->cache_info.identity_digest);
    if (!ri) {
      *msg = "No corresponding router descriptor for extra-info descriptor";
      extrainfo_free(ei);
@@@ -821,65 -813,54 +821,65 @@@
  static void
  directory_remove_invalid(void)
  {
 -  int i;
    int changed = 0;
    routerlist_t *rl = router_get_routerlist();
 +  smartlist_t *nodes = smartlist_create();
 +  smartlist_add_all(nodes, nodelist_get_list());
  
 -  routerlist_assert_ok(rl);
 -
 -  for (i = 0; i < smartlist_len(rl->routers); ++i) {
 +  SMARTLIST_FOREACH_BEGIN(nodes, node_t *, node) {
      const char *msg;
 -    routerinfo_t *ent = smartlist_get(rl->routers, i);
 -    uint32_t r = dirserv_router_get_status(ent, &msg);
 +    routerinfo_t *ent = node->ri;
 +    uint32_t r;
 +    if (!ent)
 +      continue;
 +    r = dirserv_router_get_status(ent, &msg);
      if (r & FP_REJECT) {
        log_info(LD_DIRSERV, "Router '%s' is now rejected: %s",
                 ent->nickname, msg?msg:"");
        routerlist_remove(rl, ent, 0, time(NULL));
        changed = 1;
        continue;
      }
 -    if (bool_neq((r & FP_NAMED), ent->is_named)) {
 +#if 0
 +    if (bool_neq((r & FP_NAMED), ent->auth_says_is_named)) {
        log_info(LD_DIRSERV,
                 "Router '%s' is now %snamed.", ent->nickname,
                 (r&FP_NAMED)?"":"un");
        ent->is_named = (r&FP_NAMED)?1:0;
        changed = 1;
      }
 -    if (bool_neq((r & FP_INVALID), !ent->is_valid)) {
 +    if (bool_neq((r & FP_UNNAMED), ent->auth_says_is_unnamed)) {
 +      log_info(LD_DIRSERV,
 +               "Router '%s' is now %snamed. (FP_UNNAMED)", ent->nickname,
 +               (r&FP_NAMED)?"":"un");
 +      ent->is_named = (r&FP_NUNAMED)?0:1;
 +      changed = 1;
 +    }
 +#endif
 +    if (bool_neq((r & FP_INVALID), !node->is_valid)) {
        log_info(LD_DIRSERV, "Router '%s' is now %svalid.", ent->nickname,
                 (r&FP_INVALID) ? "in" : "");
 -      ent->is_valid = (r&FP_INVALID)?0:1;
 +      node->is_valid = (r&FP_INVALID)?0:1;
        changed = 1;
      }
 -    if (bool_neq((r & FP_BADDIR), ent->is_bad_directory)) {
 +    if (bool_neq((r & FP_BADDIR), node->is_bad_directory)) {
        log_info(LD_DIRSERV, "Router '%s' is now a %s directory", ent->nickname,
                 (r & FP_BADDIR) ? "bad" : "good");
 -      ent->is_bad_directory = (r&FP_BADDIR) ? 1: 0;
 +      node->is_bad_directory = (r&FP_BADDIR) ? 1: 0;
        changed = 1;
      }
 -    if (bool_neq((r & FP_BADEXIT), ent->is_bad_exit)) {
 +    if (bool_neq((r & FP_BADEXIT), node->is_bad_exit)) {
        log_info(LD_DIRSERV, "Router '%s' is now a %s exit", ent->nickname,
                 (r & FP_BADEXIT) ? "bad" : "good");
 -      ent->is_bad_exit = (r&FP_BADEXIT) ? 1: 0;
 +      node->is_bad_exit = (r&FP_BADEXIT) ? 1: 0;
        changed = 1;
      }
 -  }
 +  } SMARTLIST_FOREACH_END(node);
    if (changed)
      directory_set_dirty();
  
    routerlist_assert_ok(rl);
 +  smartlist_free(nodes);
  }
  
  /** Mark the directory as <b>dirty</b> -- when we're next asked for a
@@@ -918,11 -899,10 +918,11 @@@ directory_set_dirty(void
   * as running iff <b>is_live</b> is true.
   */
  static char *
 -list_single_server_status(routerinfo_t *desc, int is_live)
 +list_single_server_status(const routerinfo_t *desc, int is_live)
  {
    char buf[MAX_NICKNAME_LEN+HEX_DIGEST_LEN+4]; /* !nickname=$hexdigest\0 */
    char *cp;
 +  const node_t *node;
  
    tor_assert(desc);
  
@@@ -930,8 -910,7 +930,8 @@@
    if (!is_live) {
      *cp++ = '!';
    }
 -  if (desc->is_valid) {
 +  node = node_get_by_id(desc->cache_info.identity_digest);
 +  if (node && node->is_valid) {
      strlcpy(cp, desc->nickname, sizeof(buf)-(cp-buf));
      cp += strlen(cp);
      *cp++ = '=';
@@@ -970,8 -949,6 +970,8 @@@ dirserv_set_router_is_running(routerinf
      unreachable.
     */
    int answer;
 +  node_t *node = node_get_mutable_by_id(router->cache_info.identity_digest);
 +  tor_assert(node);
  
    if (router_is_me(router)) {
      /* We always know if we are down ourselves. */
@@@ -996,7 -973,7 +996,7 @@@
      rep_hist_note_router_unreachable(router->cache_info.identity_digest, now);
    }
  
 -  router->is_running = answer;
 +  node->is_running = answer;
  }
  
  /** Based on the routerinfo_ts in <b>routers</b>, allocate the
@@@ -1024,8 -1001,6 +1024,8 @@@ list_server_status_v1(smartlist_t *rout
    rs_entries = smartlist_create();
  
    SMARTLIST_FOREACH_BEGIN(routers, routerinfo_t *, ri) {
 +    const node_t *node = node_get_by_id(ri->cache_info.identity_digest);
 +    tor_assert(node);
      if (authdir) {
        /* Update router status in routerinfo_t. */
        dirserv_set_router_is_running(ri, now);
@@@ -1033,13 -1008,12 +1033,13 @@@
      if (for_controller) {
        char name_buf[MAX_VERBOSE_NICKNAME_LEN+2];
        char *cp = name_buf;
 -      if (!ri->is_running)
 +      if (!node->is_running)
          *cp++ = '!';
        router_get_verbose_nickname(cp, ri);
        smartlist_add(rs_entries, tor_strdup(name_buf));
      } else if (ri->cache_info.published_on >= cutoff) {
 -      smartlist_add(rs_entries, list_single_server_status(ri, ri->is_running));
 +      smartlist_add(rs_entries, list_single_server_status(ri,
 +                                                          node->is_running));
      }
    } SMARTLIST_FOREACH_END(ri);
  
@@@ -1077,12 -1051,12 +1077,12 @@@ format_versions_list(config_line_t *ln
   * not hibernating, and not too old. Else return 0.
   */
  static int
 -router_is_active(routerinfo_t *ri, time_t now)
 +router_is_active(const routerinfo_t *ri, const node_t *node, time_t now)
  {
    time_t cutoff = now - ROUTER_MAX_AGE_TO_PUBLISH;
    if (ri->cache_info.published_on < cutoff)
      return 0;
 -  if (!ri->is_running || !ri->is_valid || ri->is_hibernating)
 +  if (!node->is_running || !node->is_valid || ri->is_hibernating)
      return 0;
    return 1;
  }
@@@ -1183,7 -1157,7 +1183,7 @@@ dirserv_dump_directory_to_string(char *
  int
  directory_fetches_from_authorities(or_options_t *options)
  {
 -  routerinfo_t *me;
 +  const routerinfo_t *me;
    uint32_t addr;
    int refuseunknown;
    if (options->FetchDirInfoEarly)
@@@ -1295,8 -1269,7 +1295,8 @@@ static cached_dir_t cached_runningroute
   * cached_dir_t. */
  static digestmap_t *cached_v2_networkstatus = NULL;
  
 -/** Map from flavor name to the v3 consensuses that we're currently serving. */
 +/** Map from flavor name to the cached_dir_t for the v3 consensuses that we're
 + * currently serving. */
  static strmap_t *cached_consensuses = NULL;
  
  /** Possibly replace the contents of <b>d</b> with the value of
@@@ -1749,7 -1722,7 +1749,7 @@@ static uint64_t total_exit_bandwidth = 
  /** Helper: estimate the uptime of a router given its stated uptime and the
   * amount of time since it last stated its stated uptime. */
  static INLINE long
 -real_uptime(routerinfo_t *router, time_t now)
 +real_uptime(const routerinfo_t *router, time_t now)
  {
    if (now < router->cache_info.published_on)
      return router->uptime;
@@@ -1800,8 -1773,7 +1800,8 @@@ dirserv_thinks_router_is_unreliable(tim
   * been set.
   */
  static int
 -dirserv_thinks_router_is_hs_dir(routerinfo_t *router, time_t now)
 +dirserv_thinks_router_is_hs_dir(const routerinfo_t *router,
 +                                const node_t *node, time_t now)
  {
    long uptime = real_uptime(router, now);
  
@@@ -1811,7 -1783,7 +1811,7 @@@
     * version is too old. */
    return (router->wants_to_be_hs_dir && router->dir_port &&
            uptime > get_options()->MinUptimeHidServDirectoryV2 &&
 -          router->is_running);
 +          node->is_running);
  }
  
  /** Look through the routerlist, the Mean Time Between Failure history, and
@@@ -1859,22 -1831,19 +1859,22 @@@ dirserv_compute_performance_thresholds(
    /* Weighted fractional uptime for each active router. */
    wfus = tor_malloc(sizeof(double)*smartlist_len(rl->routers));
  
 +  nodelist_assert_ok();
 +
    /* Now, fill in the arrays. */
 -  SMARTLIST_FOREACH(rl->routers, routerinfo_t *, ri, {
 -    if (router_is_active(ri, now)) {
 +  SMARTLIST_FOREACH_BEGIN(nodelist_get_list(), node_t *, node) {
 +    routerinfo_t *ri = node->ri;
 +    if (ri && router_is_active(ri, node, now)) {
        const char *id = ri->cache_info.identity_digest;
        uint32_t bw;
 -      ri->is_exit = (!router_exit_policy_rejects_all(ri) &&
 -                    exit_policy_is_general_exit(ri->exit_policy));
 +      node->is_exit = (!router_exit_policy_rejects_all(ri) &&
 +                       exit_policy_is_general_exit(ri->exit_policy));
        uptimes[n_active] = (uint32_t)real_uptime(ri, now);
        mtbfs[n_active] = rep_hist_get_stability(id, now);
        tks  [n_active] = rep_hist_get_weighted_time_known(id, now);
        bandwidths[n_active] = bw = router_get_advertised_bandwidth(ri);
        total_bandwidth += bw;
 -      if (ri->is_exit && !ri->is_bad_exit) {
 +      if (node->is_exit && !node->is_bad_exit) {
          total_exit_bandwidth += bw;
        } else {
          bandwidths_excluding_exits[n_active_nonexit] = bw;
@@@ -1882,7 -1851,7 +1882,7 @@@
        }
        ++n_active;
      }
 -  });
 +  } SMARTLIST_FOREACH_END(node);
  
    /* Now, compute thresholds. */
    if (n_active) {
@@@ -1908,17 -1877,15 +1908,17 @@@
    /* Now that we have a time-known that 7/8 routers are known longer than,
     * fill wfus with the wfu of every such "familiar" router. */
    n_familiar = 0;
 -  SMARTLIST_FOREACH(rl->routers, routerinfo_t *, ri, {
 -      if (router_is_active(ri, now)) {
 +
 +  SMARTLIST_FOREACH_BEGIN(nodelist_get_list(), node_t *, node) {
 +      routerinfo_t *ri = node->ri;
 +      if (ri && router_is_active(ri, node, now)) {
          const char *id = ri->cache_info.identity_digest;
          long tk = rep_hist_get_weighted_time_known(id, now);
          if (tk < guard_tk)
            continue;
          wfus[n_familiar++] = rep_hist_get_weighted_fractional_uptime(id, now);
        }
 -    });
 +  } SMARTLIST_FOREACH_END(node);
    if (n_familiar)
      guard_wfu = median_double(wfus, n_familiar);
    if (guard_wfu > WFU_TO_GUARANTEE_GUARD)
@@@ -1989,20 -1956,24 +1989,20 @@@ version_from_platform(const char *platf
   */
  int
  routerstatus_format_entry(char *buf, size_t buf_len,
 -                          routerstatus_t *rs, const char *version,
 +                          const routerstatus_t *rs, const char *version,
                            routerstatus_format_type_t format)
  {
    int r;
 -  struct in_addr in;
    char *cp;
    char *summary;
  
    char published[ISO_TIME_LEN+1];
 -  char ipaddr[INET_NTOA_BUF_LEN];
    char identity64[BASE64_DIGEST_LEN+1];
    char digest64[BASE64_DIGEST_LEN+1];
  
    format_iso_time(published, rs->published_on);
    digest_to_base64(identity64, rs->identity_digest);
    digest_to_base64(digest64, rs->descriptor_digest);
 -  in.s_addr = htonl(rs->addr);
 -  tor_inet_ntoa(&in, ipaddr, sizeof(ipaddr));
  
    r = tor_snprintf(buf, buf_len,
                     "r %s %s %s%s%s %s %d %d\n",
@@@ -2011,7 -1982,7 +2011,7 @@@
                     (format==NS_V3_CONSENSUS_MICRODESC)?"":digest64,
                     (format==NS_V3_CONSENSUS_MICRODESC)?"":" ",
                     published,
 -                   ipaddr,
 +                   fmt_addr32(rs->addr),
                     (int)rs->or_port,
                     (int)rs->dir_port);
    if (r<0) {
@@@ -2039,7 -2010,7 +2039,7 @@@
                     rs->is_possible_guard?" Guard":"",
                     rs->is_hs_dir?" HSDir":"",
                     rs->is_named?" Named":"",
 -                   rs->is_running?" Running":"",
 +                   rs->is_flagged_running?" Running":"",
                     rs->is_stable?" Stable":"",
                     rs->is_unnamed?" Unnamed":"",
                     rs->is_v2_dir?" V2Dir":"",
@@@ -2061,7 -2032,7 +2061,7 @@@
    }
  
    if (format != NS_V2) {
 -    routerinfo_t* desc = router_get_by_digest(rs->identity_digest);
 +    const routerinfo_t* desc = router_get_by_id_digest(rs->identity_digest);
      uint32_t bw;
  
      if (format != NS_CONTROL_PORT) {
@@@ -2157,8 -2128,6 +2157,8 @@@ _compare_routerinfo_by_ip_and_bw(const 
    routerinfo_t *first = *(routerinfo_t **)a, *second = *(routerinfo_t **)b;
    int first_is_auth, second_is_auth;
    uint32_t bw_first, bw_second;
 +  const node_t *node_first, *node_second;
 +  int first_is_running, second_is_running;
  
    /* we return -1 if first should appear before second... that is,
     * if first is a better router. */
@@@ -2181,14 -2150,9 +2181,14 @@@
    else if (!first_is_auth && second_is_auth)
      return 1;
  
 -  else if (first->is_running && !second->is_running)
 +  node_first = node_get_by_id(first->cache_info.identity_digest);
 +  node_second = node_get_by_id(second->cache_info.identity_digest);
 +  first_is_running = node_first && node_first->is_running;
 +  second_is_running = node_second && node_second->is_running;
 +
 +  if (first_is_running && !second_is_running)
      return -1;
 -  else if (!first->is_running && second->is_running)
 +  else if (!first_is_running && second_is_running)
      return 1;
  
    bw_first = router_get_advertised_bandwidth(first);
@@@ -2257,9 -2221,7 +2257,9 @@@ get_possible_sybil_list(const smartlist
   */
  void
  set_routerstatus_from_routerinfo(routerstatus_t *rs,
 -                                 routerinfo_t *ri, time_t now,
 +                                 node_t *node,
 +                                 routerinfo_t *ri,
 +                                 time_t now,
                                   int naming, int listbadexits,
                                   int listbaddirs)
  {
@@@ -2271,46 -2233,48 +2271,46 @@@
      router_digest_is_trusted_dir(ri->cache_info.identity_digest);
  
    /* Already set by compute_performance_thresholds. */
 -  rs->is_exit = ri->is_exit;
 -  rs->is_stable = ri->is_stable =
 -    router_is_active(ri, now) &&
 +  rs->is_exit = node->is_exit;
 +  rs->is_stable = node->is_stable =
 +    router_is_active(ri, node, now) &&
      !dirserv_thinks_router_is_unreliable(now, ri, 1, 0) &&
      !unstable_version;
 -  rs->is_fast = ri->is_fast =
 -    router_is_active(ri, now) &&
 +  rs->is_fast = node->is_fast =
 +    router_is_active(ri, node, now) &&
      !dirserv_thinks_router_is_unreliable(now, ri, 0, 1);
 -  rs->is_running = ri->is_running; /* computed above */
 +  rs->is_flagged_running = node->is_running; /* computed above */
  
    if (naming) {
      uint32_t name_status = dirserv_get_name_status(
 -                         ri->cache_info.identity_digest, ri->nickname);
 +                                              node->identity, ri->nickname);
      rs->is_named = (naming && (name_status & FP_NAMED)) ? 1 : 0;
      rs->is_unnamed = (naming && (name_status & FP_UNNAMED)) ? 1 : 0;
    }
 -  rs->is_valid = ri->is_valid;
 +  rs->is_valid = node->is_valid;
  
 -  if (rs->is_fast &&
 +  if (node->is_fast &&
        (router_get_advertised_bandwidth(ri) >= BANDWIDTH_TO_GUARANTEE_GUARD ||
         router_get_advertised_bandwidth(ri) >=
                                MIN(guard_bandwidth_including_exits,
                                    guard_bandwidth_excluding_exits))) {
 -    long tk = rep_hist_get_weighted_time_known(
 -                                      ri->cache_info.identity_digest, now);
 -    double wfu = rep_hist_get_weighted_fractional_uptime(
 -                                      ri->cache_info.identity_digest, now);
 +    long tk = rep_hist_get_weighted_time_known(node->identity, now);
 +    double wfu = rep_hist_get_weighted_fractional_uptime(node->identity, now);
      rs->is_possible_guard = (wfu >= guard_wfu && tk >= guard_tk) ? 1 : 0;
    } else {
      rs->is_possible_guard = 0;
    }
 -  rs->is_bad_directory = listbaddirs && ri->is_bad_directory;
 -  rs->is_bad_exit = listbadexits && ri->is_bad_exit;
 -  ri->is_hs_dir = dirserv_thinks_router_is_hs_dir(ri, now);
 -  rs->is_hs_dir = ri->is_hs_dir;
 +  rs->is_bad_directory = listbaddirs && node->is_bad_directory;
 +  rs->is_bad_exit = listbadexits && node->is_bad_exit;
 +  node->is_hs_dir = dirserv_thinks_router_is_hs_dir(ri, node, now);
 +  rs->is_hs_dir = node->is_hs_dir;
    rs->is_v2_dir = ri->dir_port != 0;
  
    if (!strcasecmp(ri->nickname, UNNAMED_ROUTER_NICKNAME))
      rs->is_named = rs->is_unnamed = 0;
  
    rs->published_on = ri->cache_info.published_on;
 -  memcpy(rs->identity_digest, ri->cache_info.identity_digest, DIGEST_LEN);
 +  memcpy(rs->identity_digest, node->identity, DIGEST_LEN);
    memcpy(rs->descriptor_digest, ri->cache_info.signed_descriptor_digest,
           DIGEST_LEN);
    rs->addr = ri->addr;
@@@ -2327,7 -2291,7 +2327,7 @@@ static voi
  clear_status_flags_on_sybil(routerstatus_t *rs)
  {
    rs->is_authority = rs->is_exit = rs->is_stable = rs->is_fast =
 -    rs->is_running = rs->is_named = rs->is_valid = rs->is_v2_dir =
 +    rs->is_flagged_running = rs->is_named = rs->is_valid = rs->is_v2_dir =
      rs->is_hs_dir = rs->is_possible_guard = rs->is_bad_exit =
      rs->is_bad_directory = 0;
    /* FFFF we might want some mechanism to check later on if we
@@@ -2335,6 -2299,18 +2335,6 @@@
     * forget to add it to this clause. */
  }
  
 -/** Clear all the status flags in routerinfo <b>router</b>. We put this
 - * function here because it's eerily similar to
 - * clear_status_flags_on_sybil() above. One day we should merge them. */
 -void
 -router_clear_status_flags(routerinfo_t *router)
 -{
 -  router->is_valid = router->is_running = router->is_hs_dir =
 -    router->is_fast = router->is_stable =
 -    router->is_possible_guard = router->is_exit =
 -    router->is_bad_exit = router->is_bad_directory = 0;
 -}
 -
  /**
   * Helper function to parse out a line in the measured bandwidth file
   * into a measured_bw_line_t output structure. Returns -1 on failure
@@@ -2452,7 -2428,7 +2452,7 @@@ dirserv_read_measured_bandwidths(const 
                                   smartlist_t *routerstatuses)
  {
    char line[256];
 -  FILE *fp = fopen(from_file, "r");
 +  FILE *fp = tor_fopen_cloexec(from_file, "r");
    int applied_lines = 0;
    time_t file_time;
    int ok;
@@@ -2582,20 -2558,17 +2582,20 @@@ dirserv_generate_networkstatus_vote_obj
        routerstatus_t *rs;
        vote_routerstatus_t *vrs;
        microdesc_t *md;
 +      node_t *node = node_get_mutable_by_id(ri->cache_info.identity_digest);
 +      if (!node)
 +        continue;
  
        vrs = tor_malloc_zero(sizeof(vote_routerstatus_t));
        rs = &vrs->status;
 -      set_routerstatus_from_routerinfo(rs, ri, now,
 +      set_routerstatus_from_routerinfo(rs, node, ri, now,
                                         naming, listbadexits, listbaddirs);
  
        if (digestmap_get(omit_as_sybil, ri->cache_info.identity_digest))
          clear_status_flags_on_sybil(rs);
  
        if (!vote_on_reachability)
 -        rs->is_running = 0;
 +        rs->is_flagged_running = 0;
  
        vrs->version = version_from_platform(ri->platform);
        md = dirvote_create_microdescriptor(ri);
@@@ -2726,8 -2699,10 +2726,8 @@@ generate_v2_networkstatus_opinion(void
    char *outp, *endp;
    or_options_t *options = get_options();
    char fingerprint[FINGERPRINT_LEN+1];
 -  char ipaddr[INET_NTOA_BUF_LEN];
    char published[ISO_TIME_LEN+1];
    char digest[DIGEST_LEN];
 -  struct in_addr in;
    uint32_t addr;
    crypto_pk_env_t *private_key;
    routerlist_t *rl = router_get_routerlist();
@@@ -2748,6 -2723,8 +2748,6 @@@
      log_warn(LD_NET, "Couldn't resolve my hostname");
      goto done;
    }
 -  in.s_addr = htonl(addr);
 -  tor_inet_ntoa(&in, ipaddr, sizeof(ipaddr));
  
    format_iso_time(published, now);
  
@@@ -2793,7 -2770,7 +2793,7 @@@
                 "dir-options%s%s%s%s\n"
                 "%s" /* client version line, server version line. */
                 "dir-signing-key\n%s",
 -               hostname, ipaddr, (int)options->DirPort,
 +               hostname, fmt_addr32(addr), (int)options->DirPort,
                 fingerprint,
                 contact,
                 published,
@@@ -2824,12 -2801,8 +2824,12 @@@
      if (ri->cache_info.published_on >= cutoff) {
        routerstatus_t rs;
        char *version = version_from_platform(ri->platform);
 -
 -      set_routerstatus_from_routerinfo(&rs, ri, now,
 +      node_t *node = node_get_mutable_by_id(ri->cache_info.identity_digest);
 +      if (!node) {
 +        tor_free(version);
 +        continue;
 +      }
 +      set_routerstatus_from_routerinfo(&rs, node, ri, now,
                                         naming, listbadexits, listbaddirs);
  
        if (digestmap_get(omit_as_sybil, ri->cache_info.identity_digest))
@@@ -2912,7 -2885,7 +2912,7 @@@ dirserv_get_networkstatus_v2_fingerprin
  
    if (!strcmp(key,"authority")) {
      if (authdir_mode_v2(get_options())) {
 -      routerinfo_t *me = router_get_my_routerinfo();
 +      const routerinfo_t *me = router_get_my_routerinfo();
        if (me)
          smartlist_add(result,
                        tor_memdup(me->cache_info.identity_digest, DIGEST_LEN));
@@@ -3000,7 -2973,7 +3000,7 @@@ dirserv_get_routerdesc_fingerprints(sma
      /* Treat "all" requests as if they were unencrypted */
      for_unencrypted_conn = 1;
    } else if (!strcmp(key, "authority")) {
 -    routerinfo_t *ri = router_get_my_routerinfo();
 +    const routerinfo_t *ri = router_get_my_routerinfo();
      if (ri)
        smartlist_add(fps_out,
                      tor_memdup(ri->cache_info.identity_digest, DIGEST_LEN));
@@@ -3020,8 -2993,8 +3020,8 @@@
  
    if (for_unencrypted_conn) {
      /* Remove anything that insists it not be sent unencrypted. */
 -    SMARTLIST_FOREACH(fps_out, char *, cp, {
 -        signed_descriptor_t *sd;
 +    SMARTLIST_FOREACH_BEGIN(fps_out, char *, cp) {
 +        const signed_descriptor_t *sd;
          if (by_id)
            sd = get_signed_descriptor_by_fp(cp,is_extrainfo,0);
          else if (is_extrainfo)
@@@ -3032,7 -3005,7 +3032,7 @@@
            tor_free(cp);
            SMARTLIST_DEL_CURRENT(fps_out, cp);
          }
 -      });
 +    } SMARTLIST_FOREACH_END(cp);
    }
  
    if (!smartlist_len(fps_out)) {
@@@ -3071,9 -3044,9 +3071,9 @@@ dirserv_get_routerdescs(smartlist_t *de
      SMARTLIST_FOREACH(rl->routers, routerinfo_t *, r,
                        smartlist_add(descs_out, &(r->cache_info)));
    } else if (!strcmp(key, "/tor/server/authority")) {
 -    routerinfo_t *ri = router_get_my_routerinfo();
 +    const routerinfo_t *ri = router_get_my_routerinfo();
      if (ri)
 -      smartlist_add(descs_out, &(ri->cache_info));
 +      smartlist_add(descs_out, (void*) &(ri->cache_info));
    } else if (!strcmpstart(key, "/tor/server/d/")) {
      smartlist_t *digests = smartlist_create();
      key += strlen("/tor/server/d/");
@@@ -3097,17 -3070,17 +3097,17 @@@
         {
           if (router_digest_is_me(d)) {
             /* make sure desc_routerinfo exists */
 -           routerinfo_t *ri = router_get_my_routerinfo();
 +           const routerinfo_t *ri = router_get_my_routerinfo();
             if (ri)
 -             smartlist_add(descs_out, &(ri->cache_info));
 +             smartlist_add(descs_out, (void*) &(ri->cache_info));
           } else {
 -           routerinfo_t *ri = router_get_by_digest(d);
 +           const routerinfo_t *ri = router_get_by_id_digest(d);
             /* Don't actually serve a descriptor that everyone will think is
              * expired.  This is an (ugly) workaround to keep buggy 0.1.1.10
              * Tors from downloading descriptors that they will throw away.
              */
             if (ri && ri->cache_info.published_on > cutoff)
 -             smartlist_add(descs_out, &(ri->cache_info));
 +             smartlist_add(descs_out, (void*) &(ri->cache_info));
           }
         });
      SMARTLIST_FOREACH(digests, char *, d, tor_free(d));
@@@ -3143,20 -3116,27 +3143,27 @@@ dirserv_orconn_tls_done(const char *add
    tor_assert(address);
    tor_assert(digest_rcvd);
  
-   SMARTLIST_FOREACH(rl->routers, routerinfo_t *, ri, {
+   /* XXX023 Doing a loop like this is stupid.  We should just look up the
+    * router by digest_rcvd, and see if address, orport, and as_advertised
+    * match up. -NM */
+   SMARTLIST_FOREACH_BEGIN(rl->routers, routerinfo_t *, ri) {
      if (!strcasecmp(address, ri->address) && or_port == ri->or_port &&
          as_advertised &&
          !memcmp(ri->cache_info.identity_digest, digest_rcvd, DIGEST_LEN)) {
        /* correct digest. mark this router reachable! */
        if (!bridge_auth || ri->purpose == ROUTER_PURPOSE_BRIDGE) {
-         log_info(LD_DIRSERV, "Found router %s to be reachable. Yay.",
-                  ri->nickname);
-         rep_hist_note_router_reachable(digest_rcvd, now);
+         tor_addr_t addr, *addrp=NULL;
+         log_info(LD_DIRSERV, "Found router %s to be reachable at %s:%d. Yay.",
+                  ri->nickname, address, ri->or_port );
+         if (tor_addr_from_str(&addr, ri->address) != -1)
+           addrp = &addr;
+         else
+           log_warn(LD_BUG, "Couldn't parse IP address \"%s\"", ri->address);
+         rep_hist_note_router_reachable(digest_rcvd, addrp, or_port, now);
          ri->last_reachable = now;
        }
      }
-   });
- 
+   } SMARTLIST_FOREACH_END(ri);
    /* FFFF Maybe we should reinstate the code that dumps routers with the same
     * addr/port but with nonmatching keys, but instead of dumping, we should
     * skip testing. */
@@@ -3166,8 -3146,7 +3173,8 @@@
   * an upload or a download.  Used to decide whether to relaunch reachability
   * testing for the server. */
  int
 -dirserv_should_launch_reachability_test(routerinfo_t *ri, routerinfo_t *ri_old)
 +dirserv_should_launch_reachability_test(const routerinfo_t *ri,
 +                                        const routerinfo_t *ri_old)
  {
    if (!authdir_mode_handles_descs(get_options(), ri->purpose))
      return 0;
@@@ -3291,7 -3270,7 +3298,7 @@@ dirserv_remove_old_statuses(smartlist_
   * its extra-info document if <b>extrainfo</b> is true. Return
   * NULL if not found or if the descriptor is older than
   * <b>publish_cutoff</b>. */
 -static signed_descriptor_t *
 +static const signed_descriptor_t *
  get_signed_descriptor_by_fp(const char *fp, int extrainfo,
                              time_t publish_cutoff)
  {
@@@ -3301,7 -3280,7 +3308,7 @@@
      else
        return &(router_get_my_routerinfo()->cache_info);
    } else {
 -    routerinfo_t *ri = router_get_by_digest(fp);
 +    const routerinfo_t *ri = router_get_by_id_digest(fp);
      if (ri &&
          ri->cache_info.published_on > publish_cutoff) {
        if (extrainfo)
@@@ -3369,7 -3348,7 +3376,7 @@@ dirserv_estimate_data_size(smartlist_t 
    tor_assert(fps);
    if (is_serverdescs) {
      int n = smartlist_len(fps);
 -    routerinfo_t *me = router_get_my_routerinfo();
 +    const routerinfo_t *me = router_get_my_routerinfo();
      result = (me?me->cache_info.signed_descriptor_len:2048) * n;
      if (compressed)
        result /= 2; /* observed compressibility is between 35 and 55%. */
@@@ -3433,10 -3412,10 +3440,10 @@@ connection_dirserv_add_servers_to_outbu
    time_t publish_cutoff = time(NULL)-ROUTER_MAX_AGE_TO_PUBLISH;
  
    while (smartlist_len(conn->fingerprint_stack) &&
 -         buf_datalen(conn->_base.outbuf) < DIRSERV_BUFFER_MIN) {
 +         connection_get_outbuf_len(TO_CONN(conn)) < DIRSERV_BUFFER_MIN) {
      const char *body;
      char *fp = smartlist_pop_last(conn->fingerprint_stack);
 -    signed_descriptor_t *sd = NULL;
 +    const signed_descriptor_t *sd = NULL;
      if (by_fp) {
        sd = get_signed_descriptor_by_fp(fp, extra, publish_cutoff);
      } else {
@@@ -3494,7 -3473,7 +3501,7 @@@ connection_dirserv_add_microdescs_to_ou
  {
    microdesc_cache_t *cache = get_microdesc_cache();
    while (smartlist_len(conn->fingerprint_stack) &&
 -         buf_datalen(conn->_base.outbuf) < DIRSERV_BUFFER_MIN) {
 +         connection_get_outbuf_len(TO_CONN(conn)) < DIRSERV_BUFFER_MIN) {
      char *fp256 = smartlist_pop_last(conn->fingerprint_stack);
      microdesc_t *md = microdesc_cache_lookup_by_digest256(cache, fp256);
      tor_free(fp256);
@@@ -3533,7 -3512,7 +3540,7 @@@ connection_dirserv_add_dir_bytes_to_out
    ssize_t bytes;
    int64_t remaining;
  
 -  bytes = DIRSERV_BUFFER_MIN - buf_datalen(conn->_base.outbuf);
 +  bytes = DIRSERV_BUFFER_MIN - connection_get_outbuf_len(TO_CONN(conn));
    tor_assert(bytes > 0);
    tor_assert(conn->cached_dir);
    if (bytes < 8192)
@@@ -3572,7 -3551,7 +3579,7 @@@ static in
  connection_dirserv_add_networkstatus_bytes_to_outbuf(dir_connection_t *conn)
  {
  
 -  while (buf_datalen(conn->_base.outbuf) < DIRSERV_BUFFER_MIN) {
 +  while (connection_get_outbuf_len(TO_CONN(conn)) < DIRSERV_BUFFER_MIN) {
      if (conn->cached_dir) {
        int uncompressing = (conn->zlib_state != NULL);
        int r = connection_dirserv_add_dir_bytes_to_outbuf(conn);
@@@ -3618,7 -3597,7 +3625,7 @@@ connection_dirserv_flushed_some(dir_con
  {
    tor_assert(conn->_base.state == DIR_CONN_STATE_SERVER_WRITING);
  
 -  if (buf_datalen(conn->_base.outbuf) >= DIRSERV_BUFFER_MIN)
 +  if (connection_get_outbuf_len(TO_CONN(conn)) >= DIRSERV_BUFFER_MIN)
      return 0;
  
    switch (conn->dir_spool_src) {
diff --combined src/or/rephist.c
index 0c5f937,53214d6..be91422
--- a/src/or/rephist.c
+++ b/src/or/rephist.c
@@@ -7,14 -7,14 +7,15 @@@
   * \brief Basic history and "reputation" functionality to remember
   *    which servers have worked in the past, how much bandwidth we've
   *    been using, which ports we tend to want, and so on; further,
 - *    exit port statistics and cell statistics.
 + *    exit port statistics, cell statistics, and connection statistics.
   **/
  
  #include "or.h"
  #include "circuitlist.h"
  #include "circuituse.h"
  #include "config.h"
+ #include "networkstatus.h"
 +#include "nodelist.h"
  #include "rephist.h"
  #include "router.h"
  #include "routerlist.h"
@@@ -74,6 -74,13 +75,13 @@@ typedef struct or_history_t 
    /** If nonzero, we have been unable to connect since this time. */
    time_t down_since;
  
+   /** The address at which we most recently connected to this OR
+    * successfully. */
+   tor_addr_t last_reached_addr;
+ 
+   /** The port at which we most recently connected to this OR successfully */
+   uint16_t last_reached_port;
+ 
    /* === For MTBF tracking: */
    /** Weighted sum total of all times that this router has been online.
     */
@@@ -120,6 -127,7 +128,7 @@@ get_or_history(const char* id
      rephist_total_num++;
      hist->link_history_map = digestmap_new();
      hist->since = hist->changed = time(NULL);
+     tor_addr_make_unspec(&hist->last_reached_addr);
      digestmap_set(history_map, id, hist);
    }
    return hist;
@@@ -290,13 -298,20 +299,20 @@@ rep_hist_note_connection_died(const cha
  /** We have just decided that this router with identity digest <b>id</b> is
   * reachable, meaning we will give it a "Running" flag for the next while. */
  void
- rep_hist_note_router_reachable(const char *id, time_t when)
+ rep_hist_note_router_reachable(const char *id, const tor_addr_t *at_addr,
+                                const uint16_t at_port, time_t when)
  {
    or_history_t *hist = get_or_history(id);
    int was_in_run = 1;
    char tbuf[ISO_TIME_LEN+1];
+   int addr_changed, port_changed;
  
    tor_assert(hist);
+   tor_assert((!at_addr && !at_port) || (at_addr && at_port));
+ 
+   addr_changed = at_addr &&
+     tor_addr_compare(at_addr, &hist->last_reached_addr, CMP_EXACT) != 0;
+   port_changed = at_port && at_port != hist->last_reached_port;
  
    if (!started_tracking_stability)
      started_tracking_stability = time(NULL);
@@@ -316,6 -331,27 +332,27 @@@
      down_length = when - hist->start_of_downtime;
      hist->total_weighted_time += down_length;
      hist->start_of_downtime = 0;
+   } else if (addr_changed || port_changed) {
+     /* If we're reachable, but the address changed, treat this as some
+      * downtime. */
+     int penalty = get_options()->TestingTorNetwork ? 240 : 3600;
+     networkstatus_t *ns;
+ 
+     if ((ns = networkstatus_get_latest_consensus())) {
+       int fresh_interval = (int)(ns->fresh_until - ns->valid_after);
+       int live_interval = (int)(ns->valid_until - ns->valid_after);
+       /* on average, a descriptor addr change takes .5 intervals to make it
+        * into a consensus, and half a liveness period to make it to
+        * clients. */
+       penalty = (int)(fresh_interval + live_interval) / 2;
+     }
+     format_local_iso_time(tbuf, hist->start_of_run);
+     log_info(LD_HIST,"Router %s still seems Running, but its address appears "
+              "to have changed since the last time it was reachable.  I'm "
+              "going to treat it as having been down for %d seconds",
+              hex_str(id, DIGEST_LEN), penalty);
+     rep_hist_note_router_unreachable(id, when-penalty);
+     rep_hist_note_router_reachable(id, NULL, 0, when);
    } else {
      format_local_iso_time(tbuf, hist->start_of_run);
      if (was_in_run)
@@@ -325,6 -361,10 +362,10 @@@
        log_info(LD_HIST,"Router %s is now Running; it was previously untracked",
                 hex_str(id, DIGEST_LEN));
    }
+   if (at_addr)
+     tor_addr_copy(&hist->last_reached_addr, at_addr);
+   if (at_port)
+     hist->last_reached_port = at_port;
  }
  
  /** We have just decided that this router is unreachable, meaning
@@@ -345,12 -385,20 +386,20 @@@ rep_hist_note_router_unreachable(const 
      long run_length = when - hist->start_of_run;
      format_local_iso_time(tbuf, hist->start_of_run);
  
-     hist->weighted_run_length += run_length;
      hist->total_run_weights += 1.0;
      hist->start_of_run = 0;
-     hist->weighted_uptime += run_length;
-     hist->total_weighted_time += run_length;
- 
+     if (run_length < 0) {
+       unsigned long penalty = -run_length;
+ #define SUBTRACT_CLAMPED(var, penalty) \
+       do { (var) = (var) < (penalty) ? 0 : (var) - (penalty); } while (0)
+ 
+       SUBTRACT_CLAMPED(hist->weighted_run_length, penalty);
+       SUBTRACT_CLAMPED(hist->weighted_uptime, penalty);
+     } else {
+       hist->weighted_run_length += run_length;
+       hist->weighted_uptime += run_length;
+       hist->total_weighted_time += run_length;
+     }
      was_running = 1;
      log_info(LD_HIST, "Router %s is now non-Running: it had previously been "
               "Running since %s.  Its total weighted uptime is %lu/%lu.",
@@@ -423,7 -471,7 +472,7 @@@ rep_hist_downrate_old_runs(time_t now
  static double
  get_stability(or_history_t *hist, time_t when)
  {
-   unsigned long total = hist->weighted_run_length;
+   long total = hist->weighted_run_length;
    double total_weights = hist->total_run_weights;
  
    if (hist->start_of_run) {
@@@ -459,8 -507,8 +508,8 @@@ get_total_weighted_time(or_history_t *h
  static double
  get_weighted_fractional_uptime(or_history_t *hist, time_t when)
  {
-   unsigned long total = hist->total_weighted_time;
-   unsigned long up = hist->weighted_uptime;
+   long total = hist->total_weighted_time;
+   long up = hist->weighted_uptime;
  
    if (hist->start_of_run) {
      long run_length = (when - hist->start_of_run);
@@@ -580,7 -628,7 +629,7 @@@ rep_hist_dump_stats(time_t now, int sev
    size_t len;
    int ret;
    unsigned long upt, downt;
 -  routerinfo_t *r;
 +  const node_t *node;
  
    rep_history_clean(now - get_options()->RephistTrackTime);
  
@@@ -594,8 -642,8 +643,8 @@@
      digestmap_iter_get(orhist_it, &digest1, &or_history_p);
      or_history = (or_history_t*) or_history_p;
  
 -    if ((r = router_get_by_digest(digest1)))
 -      name1 = r->nickname;
 +    if ((node = node_get_by_id(digest1)) && node_get_nickname(node))
 +      name1 = node_get_nickname(node);
      else
        name1 = "(unknown)";
      base16_encode(hexdigest1, sizeof(hexdigest1), digest1, DIGEST_LEN);
@@@ -625,8 -673,8 +674,8 @@@
             lhist_it = digestmap_iter_next(or_history->link_history_map,
                                            lhist_it)) {
          digestmap_iter_get(lhist_it, &digest2, &link_history_p);
 -        if ((r = router_get_by_digest(digest2)))
 -          name2 = r->nickname;
 +        if ((node = node_get_by_id(digest2)) && node_get_nickname(node))
 +          name2 = node_get_nickname(node);
          else
            name2 = "(unknown)";
  
@@@ -757,7 -805,7 +806,7 @@@ rep_hist_record_mtbf_data(time_t now, i
      base16_encode(dbuf, sizeof(dbuf), digest, DIGEST_LEN);
  
      if (missing_means_down && hist->start_of_run &&
 -        !router_get_by_digest(digest)) {
 +        !router_get_by_id_digest(digest)) {
        /* We think this relay is running, but it's not listed in our
         * routerlist. Somehow it fell out without telling us it went
         * down. Complain and also correct it. */
@@@ -872,32 -920,28 +921,32 @@@ rep_hist_get_router_stability_doc(time_
    }
  
    DIGESTMAP_FOREACH(history_map, id, or_history_t *, hist) {
 -    routerinfo_t *ri;
 +    const node_t *node;
      char dbuf[BASE64_DIGEST_LEN+1];
      char header_buf[512];
      char *info;
      digest_to_base64(dbuf, id);
 -    ri = router_get_by_digest(id);
 -    if (ri) {
 -      char *ip = tor_dup_ip(ri->addr);
 +    node = node_get_by_id(id);
 +    if (node) {
 +      char ip[INET_NTOA_BUF_LEN+1];
        char tbuf[ISO_TIME_LEN+1];
 -      format_iso_time(tbuf, ri->cache_info.published_on);
 +      time_t published = node_get_published_on(node);
 +      node_get_address_string(node,ip,sizeof(ip));
 +      if (published > 0)
 +        format_iso_time(tbuf, published);
 +      else
 +        strlcpy(tbuf, "???", sizeof(tbuf));
        tor_snprintf(header_buf, sizeof(header_buf),
                     "router %s %s %s\n"
                     "published %s\n"
                     "relevant-flags %s%s%s\n"
                     "declared-uptime %ld\n",
 -                   dbuf, ri->nickname, ip,
 +                   dbuf, node_get_nickname(node), ip,
                     tbuf,
 -                   ri->is_running ? "Running " : "",
 -                   ri->is_valid ? "Valid " : "",
 -                   ri->is_hibernating ? "Hibernating " : "",
 -                   ri->uptime);
 -      tor_free(ip);
 +                   node->is_running ? "Running " : "",
 +                   node->is_valid ? "Valid " : "",
 +                   node->ri && node->ri->is_hibernating ? "Hibernating " : "",
 +                   node_get_declared_uptime(node));
      } else {
        tor_snprintf(header_buf, sizeof(header_buf),
                     "router %s {no descriptor}\n", dbuf);
@@@ -1682,13 -1726,10 +1731,13 @@@ rep_hist_load_state(or_state_t *state, 
  
  /*********************************************************************/
  
 +typedef struct predicted_port_t {
 +  uint16_t port;
 +  time_t time;
 +} predicted_port_t;
 +
  /** A list of port numbers that have been used recently. */
  static smartlist_t *predicted_ports_list=NULL;
 -/** The corresponding most recently used time for each port. */
 -static smartlist_t *predicted_ports_times=NULL;
  
  /** We just got an application request for a connection with
   * port <b>port</b>. Remember it for the future, so we can keep
@@@ -1697,11 -1738,14 +1746,11 @@@
  static void
  add_predicted_port(time_t now, uint16_t port)
  {
 -  /* XXXX we could just use uintptr_t here, I think. */
 -  uint16_t *tmp_port = tor_malloc(sizeof(uint16_t));
 -  time_t *tmp_time = tor_malloc(sizeof(time_t));
 -  *tmp_port = port;
 -  *tmp_time = now;
 -  rephist_total_alloc += sizeof(uint16_t) + sizeof(time_t);
 -  smartlist_add(predicted_ports_list, tmp_port);
 -  smartlist_add(predicted_ports_times, tmp_time);
 +  predicted_port_t *pp = tor_malloc(sizeof(predicted_port_t));
 +  pp->port = port;
 +  pp->time = now;
 +  rephist_total_alloc += sizeof(*pp);
 +  smartlist_add(predicted_ports_list, pp);
  }
  
  /** Initialize whatever memory and structs are needed for predicting
@@@ -1712,6 -1756,7 +1761,6 @@@ static voi
  predicted_ports_init(void)
  {
    predicted_ports_list = smartlist_create();
 -  predicted_ports_times = smartlist_create();
    add_predicted_port(time(NULL), 80); /* add one to kickstart us */
  }
  
@@@ -1721,11 -1766,12 +1770,11 @@@
  static void
  predicted_ports_free(void)
  {
 -  rephist_total_alloc -= smartlist_len(predicted_ports_list)*sizeof(uint16_t);
 -  SMARTLIST_FOREACH(predicted_ports_list, char *, cp, tor_free(cp));
 +  rephist_total_alloc -=
 +    smartlist_len(predicted_ports_list)*sizeof(predicted_port_t);
 +  SMARTLIST_FOREACH(predicted_ports_list, predicted_port_t *,
 +                    pp, tor_free(pp));
    smartlist_free(predicted_ports_list);
 -  rephist_total_alloc -= smartlist_len(predicted_ports_times)*sizeof(time_t);
 -  SMARTLIST_FOREACH(predicted_ports_times, char *, cp, tor_free(cp));
 -  smartlist_free(predicted_ports_times);
  }
  
  /** Remember that <b>port</b> has been asked for as of time <b>now</b>.
@@@ -1735,17 -1781,24 +1784,17 @@@
  void
  rep_hist_note_used_port(time_t now, uint16_t port)
  {
 -  int i;
 -  uint16_t *tmp_port;
 -  time_t *tmp_time;
 -
    tor_assert(predicted_ports_list);
 -  tor_assert(predicted_ports_times);
  
    if (!port) /* record nothing */
      return;
  
 -  for (i = 0; i < smartlist_len(predicted_ports_list); ++i) {
 -    tmp_port = smartlist_get(predicted_ports_list, i);
 -    tmp_time = smartlist_get(predicted_ports_times, i);
 -    if (*tmp_port == port) {
 -      *tmp_time = now;
 +  SMARTLIST_FOREACH_BEGIN(predicted_ports_list, predicted_port_t *, pp) {
 +    if (pp->port == port) {
 +      pp->time = now;
        return;
      }
 -  }
 +  } SMARTLIST_FOREACH_END(pp);
    /* it's not there yet; we need to add it */
    add_predicted_port(now, port);
  }
@@@ -1754,28 -1807,36 +1803,28 @@@
   * we'll want to make connections to the same port in the future.  */
  #define PREDICTED_CIRCS_RELEVANCE_TIME (60*60)
  
 -/** Return a pointer to the list of port numbers that
 +/** Return a newly allocated pointer to a list of uint16_t * for ports that
   * are likely to be asked for in the near future.
 - *
 - * The caller promises not to mess with it.
   */
  smartlist_t *
  rep_hist_get_predicted_ports(time_t now)
  {
 -  int i;
 -  uint16_t *tmp_port;
 -  time_t *tmp_time;
 -
 +  smartlist_t *out = smartlist_create();
    tor_assert(predicted_ports_list);
 -  tor_assert(predicted_ports_times);
  
    /* 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_CIRCS_RELEVANCE_TIME < now) {
 -      tmp_port = smartlist_get(predicted_ports_list, i);
 -      log_debug(LD_CIRC, "Expiring predicted port %d", *tmp_port);
 -      smartlist_del(predicted_ports_list, i);
 -      smartlist_del(predicted_ports_times, i);
 -      rephist_total_alloc -= sizeof(uint16_t)+sizeof(time_t);
 -      tor_free(tmp_port);
 -      tor_free(tmp_time);
 -      i--;
 +  SMARTLIST_FOREACH_BEGIN(predicted_ports_list, predicted_port_t *, pp) {
 +    if (pp->time + PREDICTED_CIRCS_RELEVANCE_TIME < now) {
 +      log_debug(LD_CIRC, "Expiring predicted port %d", pp->port);
 +
 +      rephist_total_alloc -= sizeof(predicted_port_t);
 +      tor_free(pp);
 +      SMARTLIST_DEL_CURRENT(predicted_ports_list, pp);
 +    } else {
 +      smartlist_add(out, tor_memdup(&pp->port, sizeof(uint16_t)));
      }
 -  }
 -  return predicted_ports_list;
 +  } SMARTLIST_FOREACH_END(pp);
 +  return out;
  }
  
  /** The user asked us to do a resolve. Rather than keeping track of
@@@ -2011,9 -2072,7 +2060,9 @@@ rep_hist_exit_stats_term(void
    tor_free(exit_streams);
  }
  
 -/** Helper for qsort: compare two ints. */
 +/** Helper for qsort: compare two ints.  Does not handle overflow properly,
 + * but works fine for sorting an array of port numbers, which is what we use
 + * it for. */
  static int
  _compare_int(const void *x, const void *y)
  {
@@@ -2261,6 -2320,7 +2310,6 @@@ typedef struct circ_buffer_stats_t 
    uint32_t processed_cells;
    double mean_num_cells_in_queue;
    double mean_time_cells_in_queue;
 -  uint32_t local_circ_id;
  } circ_buffer_stats_t;
  
  /** Holds stats. */
@@@ -2283,9 -2343,9 +2332,9 @@@ rep_hist_buffer_stats_add_circ(circuit_
      return;
    if (!circuits_for_buffer_stats)
      circuits_for_buffer_stats = smartlist_create();
 -  start_of_interval = circ->timestamp_created >
 -      start_of_buffer_stats_interval ?
 -        circ->timestamp_created :
 +  start_of_interval = (circ->timestamp_created.tv_sec >
 +                       start_of_buffer_stats_interval) ?
 +        circ->timestamp_created.tv_sec :
          start_of_buffer_stats_interval;
    interval_length = (int) (end_of_interval - start_of_interval);
    stat = tor_malloc_zero(sizeof(circ_buffer_stats_t));
@@@ -2451,227 -2511,6 +2500,227 @@@ rep_hist_buffer_stats_write(time_t now
    return start_of_buffer_stats_interval + WRITE_STATS_INTERVAL;
  }
  
 +/*** Connection statistics ***/
 +
 +/** Start of the current connection stats interval or 0 if we're not
 + * collecting connection statistics. */
 +static time_t start_of_conn_stats_interval;
 +
 +/** Initialize connection stats. */
 +void
 +rep_hist_conn_stats_init(time_t now)
 +{
 +  start_of_conn_stats_interval = now;
 +}
 +
 +/* Count connections that we read and wrote less than these many bytes
 + * from/to as below threshold. */
 +#define BIDI_THRESHOLD 20480
 +
 +/* Count connections that we read or wrote at least this factor as many
 + * bytes from/to than we wrote or read to/from as mostly reading or
 + * writing. */
 +#define BIDI_FACTOR 10
 +
 +/* Interval length in seconds for considering read and written bytes for
 + * connection stats. */
 +#define BIDI_INTERVAL 10
 +
 +/* Start of next BIDI_INTERVAL second interval. */
 +static time_t bidi_next_interval = 0;
 +
 +/* Number of connections that we read and wrote less than BIDI_THRESHOLD
 + * bytes from/to in BIDI_INTERVAL seconds. */
 +static uint32_t below_threshold = 0;
 +
 +/* Number of connections that we read at least BIDI_FACTOR times more
 + * bytes from than we wrote to in BIDI_INTERVAL seconds. */
 +static uint32_t mostly_read = 0;
 +
 +/* Number of connections that we wrote at least BIDI_FACTOR times more
 + * bytes to than we read from in BIDI_INTERVAL seconds. */
 +static uint32_t mostly_written = 0;
 +
 +/* Number of connections that we read and wrote at least BIDI_THRESHOLD
 + * bytes from/to, but not BIDI_FACTOR times more in either direction in
 + * BIDI_INTERVAL seconds. */
 +static uint32_t both_read_and_written = 0;
 +
 +/* Entry in a map from connection ID to the number of read and written
 + * bytes on this connection in a BIDI_INTERVAL second interval. */
 +typedef struct bidi_map_entry_t {
 +  HT_ENTRY(bidi_map_entry_t) node;
 +  uint64_t conn_id; /**< Connection ID */
 +  size_t read; /**< Number of read bytes */
 +  size_t written; /**< Number of written bytes */
 +} bidi_map_entry_t;
 +
 +/** Map of OR connections together with the number of read and written
 + * bytes in the current BIDI_INTERVAL second interval. */
 +static HT_HEAD(bidimap, bidi_map_entry_t) bidi_map =
 +     HT_INITIALIZER();
 +
 +static int
 +bidi_map_ent_eq(const bidi_map_entry_t *a, const bidi_map_entry_t *b)
 +{
 +  return a->conn_id == b->conn_id;
 +}
 +
 +static unsigned
 +bidi_map_ent_hash(const bidi_map_entry_t *entry)
 +{
 +  return (unsigned) entry->conn_id;
 +}
 +
 +HT_PROTOTYPE(bidimap, bidi_map_entry_t, node, bidi_map_ent_hash,
 +             bidi_map_ent_eq);
 +HT_GENERATE(bidimap, bidi_map_entry_t, node, bidi_map_ent_hash,
 +            bidi_map_ent_eq, 0.6, malloc, realloc, free);
 +
 +static void
 +bidi_map_free(void)
 +{
 +  bidi_map_entry_t **ptr, **next, *ent;
 +  for (ptr = HT_START(bidimap, &bidi_map); ptr; ptr = next) {
 +    ent = *ptr;
 +    next = HT_NEXT_RMV(bidimap, &bidi_map, ptr);
 +    tor_free(ent);
 +  }
 +  HT_CLEAR(bidimap, &bidi_map);
 +}
 +
 +/** Reset counters for conn statistics. */
 +void
 +rep_hist_reset_conn_stats(time_t now)
 +{
 +  start_of_conn_stats_interval = now;
 +  below_threshold = 0;
 +  mostly_read = 0;
 +  mostly_written = 0;
 +  both_read_and_written = 0;
 +  bidi_map_free();
 +}
 +
 +/** Stop collecting connection stats in a way that we can re-start doing
 + * so in rep_hist_conn_stats_init(). */
 +void
 +rep_hist_conn_stats_term(void)
 +{
 +  rep_hist_reset_conn_stats(0);
 +}
 +
 +/** We read <b>num_read</b> bytes and wrote <b>num_written</b> from/to OR
 + * connection <b>conn_id</b> in second <b>when</b>. If this is the first
 + * observation in a new interval, sum up the last observations. Add bytes
 + * for this connection. */
 +void
 +rep_hist_note_or_conn_bytes(uint64_t conn_id, size_t num_read,
 +                            size_t num_written, time_t when)
 +{
 +  if (!start_of_conn_stats_interval)
 +    return;
 +  /* Initialize */
 +  if (bidi_next_interval == 0)
 +    bidi_next_interval = when + BIDI_INTERVAL;
 +  /* Sum up last period's statistics */
 +  if (when >= bidi_next_interval) {
 +    bidi_map_entry_t **ptr, **next, *ent;
 +    for (ptr = HT_START(bidimap, &bidi_map); ptr; ptr = next) {
 +      ent = *ptr;
 +      if (ent->read + ent->written < BIDI_THRESHOLD)
 +        below_threshold++;
 +      else if (ent->read >= ent->written * BIDI_FACTOR)
 +        mostly_read++;
 +      else if (ent->written >= ent->read * BIDI_FACTOR)
 +        mostly_written++;
 +      else
 +        both_read_and_written++;
 +      next = HT_NEXT_RMV(bidimap, &bidi_map, ptr);
 +      tor_free(ent);
 +    }
 +    while (when >= bidi_next_interval)
 +      bidi_next_interval += BIDI_INTERVAL;
 +    log_info(LD_GENERAL, "%d below threshold, %d mostly read, "
 +             "%d mostly written, %d both read and written.",
 +             below_threshold, mostly_read, mostly_written,
 +             both_read_and_written);
 +  }
 +  /* Add this connection's bytes. */
 +  if (num_read > 0 || num_written > 0) {
 +    bidi_map_entry_t *entry, lookup;
 +    lookup.conn_id = conn_id;
 +    entry = HT_FIND(bidimap, &bidi_map, &lookup);
 +    if (entry) {
 +      entry->written += num_written;
 +      entry->read += num_read;
 +    } else {
 +      entry = tor_malloc_zero(sizeof(bidi_map_entry_t));
 +      entry->conn_id = conn_id;
 +      entry->written = num_written;
 +      entry->read = num_read;
 +      HT_INSERT(bidimap, &bidi_map, entry);
 +    }
 +  }
 +}
 +
 +/** Return a newly allocated string containing the connection statistics
 + * until <b>now</b>, or NULL if we're not collecting conn stats. */
 +char *
 +rep_hist_format_conn_stats(time_t now)
 +{
 +  char *result, written[ISO_TIME_LEN+1];
 +
 +  if (!start_of_conn_stats_interval)
 +    return NULL; /* Not initialized. */
 +
 +  format_iso_time(written, now);
 +  tor_asprintf(&result, "conn-bi-direct %s (%d s) %d,%d,%d,%d\n",
 +               written,
 +               (unsigned) (now - start_of_conn_stats_interval),
 +               below_threshold,
 +               mostly_read,
 +               mostly_written,
 +               both_read_and_written);
 +  return result;
 +}
 +
 +/** If 24 hours have passed since the beginning of the current conn stats
 + * period, write conn stats to $DATADIR/stats/conn-stats (possibly
 + * overwriting an existing file) and reset counters.  Return when we would
 + * next want to write conn stats or 0 if we never want to write. */
 +time_t
 +rep_hist_conn_stats_write(time_t now)
 +{
 +  char *statsdir = NULL, *filename = NULL, *str = NULL;
 +
 +  if (!start_of_conn_stats_interval)
 +    return 0; /* Not initialized. */
 +  if (start_of_conn_stats_interval + WRITE_STATS_INTERVAL > now)
 +    goto done; /* Not ready to write */
 +
 +  /* Generate history string. */
 +  str = rep_hist_format_conn_stats(now);
 +
 +  /* Reset counters. */
 +  rep_hist_reset_conn_stats(now);
 +
 +  /* Try to write to disk. */
 +  statsdir = get_datadir_fname("stats");
 +  if (check_private_dir(statsdir, CPD_CREATE) < 0) {
 +    log_warn(LD_HIST, "Unable to create stats/ directory!");
 +    goto done;
 +  }
 +  filename = get_datadir_fname2("stats", "conn-stats");
 +  if (write_str_to_file(filename, str, 0) < 0)
 +    log_warn(LD_HIST, "Unable to write conn stats to disk!");
 +
 + done:
 +  tor_free(str);
 +  tor_free(filename);
 +  tor_free(statsdir);
 +  return start_of_conn_stats_interval + WRITE_STATS_INTERVAL;
 +}
 +
  /** Free all storage held by the OR/link history caches, by the
   * bandwidth history arrays, by the port history, or by statistics . */
  void
@@@ -2686,6 -2525,5 +2735,6 @@@ rep_hist_free_all(void
    tor_free(exit_streams);
    built_last_stability_doc_at = 0;
    predicted_ports_free();
 +  bidi_map_free();
  }
  
diff --combined src/or/rephist.h
index cb0ebf0,5f6b9f9..cb70e15
--- a/src/or/rephist.h
+++ b/src/or/rephist.h
@@@ -33,7 -33,8 +33,8 @@@ void rep_hist_update_state(or_state_t *
  int rep_hist_load_state(or_state_t *state, char **err);
  void rep_history_clean(time_t before);
  
- void rep_hist_note_router_reachable(const char *id, time_t when);
+ void rep_hist_note_router_reachable(const char *id, const tor_addr_t *at_addr,
+                                     uint16_t at_port, time_t when);
  void rep_hist_note_router_unreachable(const char *id, time_t when);
  int rep_hist_record_mtbf_data(time_t now, int missing_means_down);
  int rep_hist_load_mtbf_data(time_t now);
@@@ -76,13 -77,5 +77,13 @@@ void rep_hist_buffer_stats_add_circ(cir
  time_t rep_hist_buffer_stats_write(time_t now);
  void rep_hist_buffer_stats_term(void);
  
 +void rep_hist_conn_stats_init(time_t now);
 +void rep_hist_note_or_conn_bytes(uint64_t conn_id, size_t num_read,
 +                                 size_t num_written, time_t when);
 +void rep_hist_reset_conn_stats(time_t now);
 +char *rep_hist_format_conn_stats(time_t now);
 +time_t rep_hist_conn_stats_write(time_t now);
 +void rep_hist_conn_stats_term(void);
 +
  #endif
  



More information about the tor-commits mailing list