Commit e594ce92 authored by Nick Mathewson's avatar Nick Mathewson 🦞
Browse files

Start making directory caches retain old routerinfo_t. The code to remove old...

Start making directory caches retain old routerinfo_t.  The code to remove old ones is definitely some textbook C problem.


svn:r5323
parent 3c36a14b
Loading
Loading
Loading
Loading
+2 −4
Original line number Diff line number Diff line
@@ -491,8 +491,7 @@ directory_remove_invalid(void)
      case FP_REJECT:
        info(LD_DIRSERV, "Router '%s' is now rejected: %s",
            ent->nickname, msg?msg:"");
        routerlist_remove(rl, ent, i--);
        routerinfo_free(ent);
        routerlist_remove(rl, ent, i--, 0);
        changed = 1;
        break;
      case FP_NAMED:
@@ -1476,8 +1475,7 @@ dirserv_orconn_tls_done(const char *address,
      }
    }
    if (drop) {
      routerlist_remove(rl, ri, i--);
      routerinfo_free(ri);
      routerlist_remove(rl, ri, i--, 0);
      directory_set_dirty();
    } else { /* correct nickname and digest. mark this router reachable! */
      info(LD_DIRSERV,"Found router %s to be reachable. Yay.", ri->nickname);
+10 −3
Original line number Diff line number Diff line
@@ -869,9 +869,15 @@ typedef struct networkstatus_t {

/** Contents of a directory of onion routers. */
typedef struct {
  /** List of routerinfo_t. */
  smartlist_t *routers;
  /** Map from server identity digest to a member of routers. */
  digestmap_t *identity_map;
  /** Map from server descriptor digest to a member of routers or of
   * old_routers. */
  digestmap_t *desc_digest_map;
  /** List of routerinfo_t for all currently live routers we know. */
  smartlist_t *routers;
  /** List of routerinfo_t for older router descriptors we're caching. */
  smartlist_t *old_routers;
} routerlist_t;

/** Information on router used when extending a circuit.  (We don't need a
@@ -2141,7 +2147,8 @@ int router_digest_is_trusted_dir(const char *digest);
routerlist_t *router_get_routerlist(void);
void routerlist_reset_warnings(void);
void routerlist_free(routerlist_t *routerlist);
void routerlist_remove(routerlist_t *rl, routerinfo_t *ri, int idx);
void routerlist_remove(routerlist_t *rl, routerinfo_t *ri, int idx,
                       int make_old);
void routerinfo_free(routerinfo_t *router);
void routerstatus_free(routerstatus_t *routerstatus);
void networkstatus_free(networkstatus_t *networkstatus);
+274 −47
Original line number Diff line number Diff line
@@ -193,7 +193,7 @@ router_rebuild_store(int force)
  size_t fname_len;
  smartlist_t *chunk_list = NULL;
  char *fname = NULL;
  int r = -1;
  int r = -1, i;

  if (!force && !router_should_rebuild_store())
    return 0;
@@ -209,7 +209,9 @@ router_rebuild_store(int force)
  tor_snprintf(fname, fname_len, "%s/cached-routers", options->DataDirectory);
  chunk_list = smartlist_create();

  SMARTLIST_FOREACH(routerlist->routers, routerinfo_t *, ri,
  for (i = 0; i < 2; ++i) {
    smartlist_t *lst = (i == 0) ? routerlist->old_routers : routerlist->routers;
    SMARTLIST_FOREACH(lst, routerinfo_t *, ri,
    {
      sized_chunk_t *c;
      if (!ri->signed_descriptor) {
@@ -222,7 +224,7 @@ router_rebuild_store(int force)
      c->len = ri->signed_descriptor_len;
      smartlist_add(chunk_list, c);
    });

  }
  if (write_chunks_to_file(fname, chunk_list, 0)<0) {
    warn(LD_FS, "Error writing router store to disk.");
    goto done;
@@ -1027,7 +1029,9 @@ router_get_routerlist(void)
  if (!routerlist) {
    routerlist = tor_malloc_zero(sizeof(routerlist_t));
    routerlist->routers = smartlist_create();
    routerlist->old_routers = smartlist_create();
    routerlist->identity_map = digestmap_new();
    routerlist->desc_digest_map = digestmap_new();
  }
  return routerlist;
}
@@ -1097,44 +1101,91 @@ routerlist_free(routerlist_t *rl)
{
  tor_assert(rl);
  digestmap_free(rl->identity_map, NULL);
  digestmap_free(rl->desc_digest_map, NULL);
  SMARTLIST_FOREACH(rl->routers, routerinfo_t *, r,
                    routerinfo_free(r));
  SMARTLIST_FOREACH(rl->old_routers, routerinfo_t *, r,
                    routerinfo_free(r));
  smartlist_free(rl->routers);
  smartlist_free(rl->old_routers);
  tor_free(rl);
}

static INLINE int
_routerlist_find_elt(smartlist_t *sl, routerinfo_t *ri, int idx)
{
  if (idx < 0 || smartlist_get(sl, idx) != ri) {
    idx = -1;
    SMARTLIST_FOREACH(sl, routerinfo_t *, r,
                      if (r == ri) {
                        idx = r_sl_idx;
                        break;
                      });
  }
  return -1;
}

/** Insert an item <b>ri</b> into the routerlist <b>rl</b>, updating indices
 * as needed. */
static void
routerlist_insert(routerlist_t *rl, routerinfo_t *ri)
{
  digestmap_set(rl->identity_map, ri->identity_digest, ri);
  digestmap_set(rl->desc_digest_map, ri->signed_descriptor_digest, ri);
  smartlist_add(rl->routers, ri);
  // routerlist_assert_ok(rl);
}

static void
routerlist_insert_old(routerlist_t *rl, routerinfo_t *ri)
{
  if (get_options()->DirPort) {
    digestmap_set(rl->desc_digest_map, ri->signed_descriptor_digest, ri);
    smartlist_add(rl->old_routers, ri);
    // routerlist_assert_ok(rl);
  } else {
    routerinfo_free(ri);
  }
}

/** Remove an item <b>ri</b> into the routerlist <b>rl</b>, updating indices
 * as needed. If <b>idx</b> is nonnegative and smartlist_get(rl-&gt;routers,
 * idx) == ri, we don't need to do a linear search over the list to decide
 * which to remove.  We fill the gap rl-&gt;routers with a later element in
 * the list, if any exists. ri_old is not freed.*/
 * the list, if any exists. ri is freed..*/
void
routerlist_remove(routerlist_t *rl, routerinfo_t *ri, int idx)
routerlist_remove(routerlist_t *rl, routerinfo_t *ri, int idx, int make_old)
{
  routerinfo_t *ri_tmp;
  if (idx < 0 || smartlist_get(rl->routers, idx) != ri) {
    idx = -1;
    SMARTLIST_FOREACH(rl->routers, routerinfo_t *, r,
                      if (r == ri) {
                        idx = r_sl_idx;
                        break;
                      });
  idx = _routerlist_find_elt(rl->routers, ri, idx);
  if (idx < 0)
    return;
  }
  smartlist_del(rl->routers, idx);
  ri_tmp = digestmap_remove(rl->identity_map, ri->identity_digest);
  tor_assert(ri_tmp == ri);
  if (make_old && get_options()->DirPort) {
    smartlist_add(rl->old_routers, ri);
  } else {
    ri_tmp = digestmap_remove(rl->desc_digest_map,
                              ri->signed_descriptor_digest);
    tor_assert(ri_tmp == ri);
    routerinfo_free(ri);
    // routerlist_assert_ok(rl);
  }
}

void
routerlist_remove_old(routerlist_t *rl, routerinfo_t *ri, int idx)
{
  routerinfo_t *ri_tmp;
  idx = _routerlist_find_elt(rl->old_routers, ri, idx);
  if (idx < 0)
    return;
  smartlist_del(rl->old_routers, idx);
  ri_tmp = digestmap_remove(rl->desc_digest_map,
                            ri->signed_descriptor_digest);
  tor_assert(ri_tmp == ri);
  routerinfo_free(ri);
  // routerlist_assert_ok(rl);
}

@@ -1142,19 +1193,12 @@ routerlist_remove(routerlist_t *rl, routerinfo_t *ri, int idx)
 * <b>ri_new</b>, updating all index info.  If <b>idx</b> is nonnegative and
 * smartlist_get(rl-&gt;routers, idx) == ri, we don't need to do a linear
 * search over the list to decide which to remove.  We put ri_new in the same
 * index as ri_old, if possible.  ri_old is not freed.*/
 * index as ri_old, if possible.  ri is freed as appropriate */
static void
routerlist_replace(routerlist_t *rl, routerinfo_t *ri_old,
                   routerinfo_t *ri_new, int idx)
                   routerinfo_t *ri_new, int idx, int make_old)
{
  if (idx < 0 || smartlist_get(rl->routers, idx) != ri_old) {
    idx = -1;
    SMARTLIST_FOREACH(rl->routers, routerinfo_t *, r,
                      if (r == ri_old) {
                        idx = r_sl_idx;
                        break;
                      });
  }
  idx = _routerlist_find_elt(rl->routers, ri_old, idx);
  if (idx >= 0) {
    smartlist_set(rl->routers, idx, ri_new);
  } else {
@@ -1165,6 +1209,18 @@ routerlist_replace(routerlist_t *rl, routerinfo_t *ri_old,
    digestmap_remove(rl->identity_map, ri_old->identity_digest);
  }
  digestmap_set(rl->identity_map, ri_new->identity_digest, ri_new);
  digestmap_set(rl->desc_digest_map, ri_new->signed_descriptor_digest, ri_new);

  if (make_old && get_options()->DirPort) {
    smartlist_add(rl->old_routers, ri_old);
  } else {
    if (memcmp(ri_old->signed_descriptor_digest, ri_new->signed_descriptor_digest,
               DIGEST_LEN)) {
      /* digests don't match; digestmap_set didn't replace */
      digestmap_remove(rl->desc_digest_map, ri_old->signed_descriptor_digest);
    }
    routerinfo_free(ri_old);
  }
}

/** Free all memory held by the rouerlist module */
@@ -1314,7 +1370,19 @@ router_add_to_routerlist(routerinfo_t *router, const char **msg,
  if (!routerlist)
    router_get_routerlist();

  /* XXXX NM If this assert doesn't trigger, we should remove the id_digest
   * local. */
  crypto_pk_get_digest(router->identity_pkey, id_digest);
  tor_assert(!memcmp(id_digest, router->identity_digest, DIGEST_LEN));

  /* Make sure that we haven't already got this exact descriptor. */
  if (digestmap_get(routerlist->desc_digest_map,
                    router->signed_descriptor_digest)) {
    info(LD_DIR, "Dropping descriptor that we already have for router '%s'",
         router->nickname);
    *msg = "Router descriptor was not new.";
    return -1;
  }

  if (authdir) {
    if (authdir_wants_to_reject_router(router, msg))
@@ -1322,7 +1390,7 @@ router_add_to_routerlist(routerinfo_t *router, const char **msg,
    authdir_verified = router->is_verified;
    /*
  } else {
    if (! router->xx_is_recognized) {
    if (! router->xx_is_recognized && !from_cache) {
      log_fn(LOG_WARN, "Dropping unrecognized descriptor for router '%s'",
             router->nickname);
      return -1;
@@ -1340,7 +1408,7 @@ router_add_to_routerlist(routerinfo_t *router, const char **msg,
        /* Same key, but old */
        debug(LD_DIR, "Skipping not-new descriptor for router '%s'",
               router->nickname);
        routerinfo_free(router);
        routerlist_insert_old(routerlist, router);
        *msg = "Router descriptor was not new.";
        return -1;
      } else {
@@ -1369,8 +1437,7 @@ router_add_to_routerlist(routerinfo_t *router, const char **msg,
            router->num_unreachable_notifications++;
          }
        }
        routerlist_replace(routerlist, old_router, router, i);
        routerinfo_free(old_router);
        routerlist_replace(routerlist, old_router, router, i, 1);
        if (!from_cache)
          router_append_to_journal(router->signed_descriptor,
                                   router->signed_descriptor_len);
@@ -1398,8 +1465,7 @@ router_add_to_routerlist(routerinfo_t *router, const char **msg,
                 old_router->nickname);
          connection_mark_for_close(conn);
        }
        routerlist_remove(routerlist, old_router, i--);
        routerinfo_free(old_router);
        routerlist_remove(routerlist, old_router, i--, 0);
      } else if (old_router->is_named) {
        /* Can't replace a verified router with an unverified one. */
        debug(LD_DIR, "Skipping unverified entry for verified router '%s'",
@@ -1420,6 +1486,8 @@ router_add_to_routerlist(routerinfo_t *router, const char **msg,
  return 0;
}

#define MAX_DESCRIPTORS_PER_ROUTER 5

/** Remove any routers from the routerlist that are more than <b>age</b>
 * seconds old.
 */
@@ -1438,10 +1506,150 @@ routerlist_remove_old_routers(int age)
    if (router->published_on <= cutoff) {
      /* Too old.  Remove it. */
      info(LD_DIR,"Forgetting obsolete (too old) routerinfo for router '%s'", router->nickname);
      routerlist_remove(routerlist, router, i--);
      routerinfo_free(router);
      routerlist_remove(routerlist, router, i--, 1);
    }
  }

}

static int
_compare_old_routers_by_identity(const void **_a, const void **_b)
{
  int i;
  const routerinfo_t *r1 = *_a, *r2 = *_b;
  if ((i = memcmp(r1->identity_digest, r2->identity_digest, DIGEST_LEN)))
    return i;
  return r1->published_on - r2->published_on;
}

struct duration_idx_t {
  int duration;
  int idx;
  int old;
};

static int
_compare_duration_idx(const void *_d1, const void *_d2)
{
  const struct duration_idx_t *d1 = _d1;
  const struct duration_idx_t *d2 = _d2;
  return d1->duration - d2->duration;
}

/** The range <b>lo</b> through <b>hi</b> inclusive of routerlist->old_routers
 * must contain routerinfo_t with the same identity and with publication time
 * in ascending order.  Remove members from this range until there are no more
 * than MAX_DESCRIPTORS_PER_ROUTER remaining.  Start by removing the oldest
 * members from before <b>cutoff</b>, then remove members which were current
 * for the lowest amount of time.  The order of members of old_routers at
 * indices <b>lo</b> or higher may be changed.
 */
static void
routerlist_remove_old_cached_routers_with_id(time_t cutoff, int lo, int hi)
{
  int i, n = hi-lo+1, n_extra;
  int n_rmv = 0;
  struct duration_idx_t *lifespans;
  uint8_t *rmv;
  smartlist_t *lst = routerlist->old_routers;
#if 1
  const char *ident;
  tor_assert(hi < smartlist_len(lst));
  tor_assert(lo <= hi);
  ident = ((routerinfo_t*)smartlist_get(lst, lo))->identity_digest;
  for (i = lo+1; i <= hi; ++i) {
    routerinfo_t *r = smartlist_get(lst, i);
    tor_assert(!memcmp(ident, r->identity_digest, DIGEST_LEN));
  }
#endif

  /* Check whether we need to do anything at all. */
  n_extra = n - MAX_DESCRIPTORS_PER_ROUTER;
  if (n_extra <= 0)
    return;


  lifespans = tor_malloc_zero(sizeof(struct duration_idx_t)*n);
  rmv = tor_malloc_zero(sizeof(uint8_t)*n);
  /* Set lifespans to contain the lifespan and index of each server. */
  /* Set rmv[i-lo]=1 if we're going to remove a server for being too old. */
  for (i = lo; i <= hi; ++i) {
    routerinfo_t *r = smartlist_get(lst, i);
    routerinfo_t *r_next;
    lifespans[i].idx = i;
    if (i < hi) {
      r_next = smartlist_get(lst, i+1);
      tor_assert(r->published_on <= r_next->published_on);
      lifespans[i].duration = r_next->published_on - r->published_on;
    } else {
      r_next = NULL;
      lifespans[i].duration = INT_MAX;
    }
    if (r->published_on < cutoff && n_rmv < n_extra) {
      ++n_rmv;
      lifespans[i].old = 1;
      rmv[i-lo] = 1;
    }
  }

  if (n_rmv < n_extra) {
    /**
     * We aren't removing enough servers for being old.  Sort lifespans by
     * the duration of liveness, and remove the ones we're not already going to
     * remove based on how long they were alive.
     **/
    qsort(lifespans, n, sizeof(struct duration_idx_t), _compare_duration_idx);
    for (i = 0; i < n && n_rmv < n_extra; ++i) {
      if (!lifespans[i].old) {
        rmv[lifespans[i].idx-lo] = 1;
        ++n_rmv;
      }
    }
  }

  for (i = hi; i >= lo; ++i) {
    if (rmv[i-lo])
      routerlist_remove_old(routerlist, smartlist_get(lst, i), i);
  }
  tor_free(rmv);
  tor_free(lifespans);
}

void
routerlist_remove_old_cached_routers(void)
{
  int i, hi=-1;
  const char *cur_id = NULL;
  time_t cutoff;
  if (!routerlist)
    return;

  /* First, check whether we have too many router descriptors, total.  We're
   * okay with having too many for some given router, so long as the total
   * number doesn't much exceed
   */
  if (smartlist_len(routerlist->old_routers) <
      smartlist_len(routerlist->routers) * (MAX_DESCRIPTORS_PER_ROUTER - 1))
    return;

  smartlist_sort(routerlist->old_routers, _compare_old_routers_by_identity);

  cutoff = time(NULL) - ROUTER_MAX_AGE;

  /* Iterate through the list from back to front, so when we remove descriptors
   * we don't mess up groups we haven't gotten to. */
  for (i = smartlist_len(routerlist->old_routers)-1; i >= 0; --i) {
    routerinfo_t *r = smartlist_get(routerlist->old_routers, i);
    if (!cur_id) {
      cur_id = r->identity_digest;
      hi = i;
    }
    if (memcmp(cur_id, r->identity_digest, DIGEST_LEN)) {
      routerlist_remove_old_cached_routers_with_id(cutoff, i+1, hi);
      hi = i;
    }
  }
  routerlist_remove_old_cached_routers_with_id(cutoff, 0, hi);
}

/**
@@ -3004,12 +3212,21 @@ static void
routerlist_assert_ok(routerlist_t *rl)
{
  digestmap_iter_t *iter;
  routerinfo_t *r2;
  if (!routerlist)
    return;
  SMARTLIST_FOREACH(rl->routers, routerinfo_t *, r,
  {
    routerinfo_t *r2 = digestmap_get(rl->identity_map,
                                     r->identity_digest);
    r2 = digestmap_get(rl->identity_map, r->identity_digest);
    tor_assert(r == r2);
    r2 = digestmap_get(rl->desc_digest_map, r->signed_descriptor_digest);
    tor_assert(r == r2);
  });
  SMARTLIST_FOREACH(rl->old_routers, routerinfo_t *, r,
  {
    r2 = digestmap_get(rl->identity_map, r->identity_digest);
    tor_assert(r != r2);
    r2 = digestmap_get(rl->desc_digest_map, r->signed_descriptor_digest);
    tor_assert(r == r2);
  });
  iter = digestmap_iter_init(rl->identity_map);
@@ -3022,5 +3239,15 @@ routerlist_assert_ok(routerlist_t *rl)
    tor_assert(!memcmp(r->identity_digest, d, DIGEST_LEN));
    iter = digestmap_iter_next(rl->identity_map, iter);
  }
  iter = digestmap_iter_init(rl->desc_digest_map);
  while (!digestmap_iter_done(iter)) {
    const char *d;
    void *_r;
    routerinfo_t *r;
    digestmap_iter_get(iter, &d, &_r);
    r = _r;
    tor_assert(!memcmp(r->signed_descriptor_digest, d, DIGEST_LEN));
    iter = digestmap_iter_next(rl->desc_digest_map, iter);
  }
}