aboutsummaryrefslogtreecommitdiff
path: root/src/or/routerlist.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/or/routerlist.c')
-rw-r--r--src/or/routerlist.c321
1 files changed, 274 insertions, 47 deletions
diff --git a/src/or/routerlist.c b/src/or/routerlist.c
index e42ce0595..7228300ec 100644
--- a/src/or/routerlist.c
+++ b/src/or/routerlist.c
@@ -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,20 +209,22 @@ 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,
- {
- sized_chunk_t *c;
- if (!ri->signed_descriptor) {
- warn(LD_BUG, "Bug! No descriptor stored for router '%s'.",
- ri->nickname);
- goto done;
- }
- c = tor_malloc(sizeof(sized_chunk_t));
- c->bytes = ri->signed_descriptor;
- c->len = ri->signed_descriptor_len;
- smartlist_add(chunk_list, c);
- });
-
+ 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) {
+ warn(LD_BUG, "Bug! No descriptor stored for router '%s'.",
+ ri->nickname);
+ goto done;
+ }
+ c = tor_malloc(sizeof(sized_chunk_t));
+ c->bytes = ri->signed_descriptor;
+ 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;
- });
- if (idx < 0)
- return;
- }
+ 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);
+ }
}