commit feed375f194d389dbc4c624e09bdd9161931e23a
Author: David Goulet <dgou...@torproject.org>
Date:   Tue May 9 14:31:17 2017 -0400

    prop224: Implement a service intro point failure cache
    
    Imagine a Tor network where you have only 8 nodes available due to some
    reasons. And your hidden service wants 8 introduction points. Everything is
    fine but then a node goes down bringing the network to 7. The service will
    retry 3 times that node and then give up but keep it in a failure cache for 
5
    minutes (INTRO_CIRC_RETRY_PERIOD) so it doesn't retry it non stop and 
exhaust
    the maximum number of circuit retry.
    
    In the real public network today, this is unlikely to happen unless the
    ExcludeNodes list is extremely restrictive.
    
    Signed-off-by: David Goulet <dgou...@torproject.org>
---
 src/or/hs_service.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
 src/or/hs_service.h |  6 ++++
 2 files changed, 86 insertions(+), 1 deletion(-)

diff --git a/src/or/hs_service.c b/src/or/hs_service.c
index 85610de57..42523ed85 100644
--- a/src/or/hs_service.c
+++ b/src/or/hs_service.c
@@ -889,6 +889,7 @@ service_descriptor_free(hs_service_descriptor_t *desc)
   smartlist_free(desc->hsdir_missing_info);
   /* Cleanup all intro points. */
   digest256map_free(desc->intro_points.map, service_intro_point_free_);
+  digestmap_free(desc->intro_points.failed_id, tor_free_);
   tor_free(desc);
 }
 
@@ -900,10 +901,71 @@ service_descriptor_new(void)
   sdesc->desc = tor_malloc_zero(sizeof(hs_descriptor_t));
   /* Initialize the intro points map. */
   sdesc->intro_points.map = digest256map_new();
+  sdesc->intro_points.failed_id = digestmap_new();
   sdesc->hsdir_missing_info = smartlist_new();
   return sdesc;
 }
 
+/* From the given service, remove all expired failing intro points for each
+ * descriptor. */
+static void
+remove_expired_failing_intro(hs_service_t *service, time_t now)
+{
+  tor_assert(service);
+
+  /* For both descriptors, cleanup the failing intro points list. */
+  FOR_EACH_DESCRIPTOR_BEGIN(service, desc) {
+    DIGESTMAP_FOREACH_MODIFY(desc->intro_points.failed_id, key, time_t *, t) {
+      time_t failure_time = *t;
+      if ((failure_time + INTRO_CIRC_RETRY_PERIOD) <= now) {
+        MAP_DEL_CURRENT(key);
+        tor_free(t);
+      }
+    } DIGESTMAP_FOREACH_END;
+  } FOR_EACH_DESCRIPTOR_END;
+}
+
+/* For the given descriptor desc, put all node_t object found from its failing
+ * intro point list and put them in the given node_list. */
+static void
+setup_intro_point_exclude_list(const hs_service_descriptor_t *desc,
+                               smartlist_t *node_list)
+{
+  tor_assert(desc);
+  tor_assert(node_list);
+
+  DIGESTMAP_FOREACH(desc->intro_points.failed_id, key, time_t *, t) {
+    (void) t; /* Make gcc happy. */
+    const node_t *node = node_get_by_id(key);
+    if (node) {
+      smartlist_add(node_list, (void *) node);
+    }
+  } DIGESTMAP_FOREACH_END;
+}
+
+/* For the given failing intro point ip, we add its time of failure to the
+ * failed map and index it by identity digest (legacy ID) in the descriptor
+ * desc failed id map. */
+static void
+remember_failing_intro_point(const hs_service_intro_point_t *ip,
+                             hs_service_descriptor_t *desc, time_t now)
+{
+  time_t *time_of_failure, *prev_ptr;
+  const hs_desc_link_specifier_t *legacy_ls;
+
+  tor_assert(ip);
+  tor_assert(desc);
+
+  time_of_failure = tor_malloc_zero(sizeof(time_t));
+  *time_of_failure = now;
+  legacy_ls = get_link_spec_by_type(ip, LS_LEGACY_ID);
+  tor_assert(legacy_ls);
+  prev_ptr = digestmap_set(desc->intro_points.failed_id,
+                           (const char *) legacy_ls->u.legacy_id,
+                           time_of_failure);
+  tor_free(prev_ptr);
+}
+
 /* Copy the descriptor link specifier object from src to dst. */
 static void
 link_specifier_copy(hs_desc_link_specifier_t *dst,
@@ -1318,6 +1380,9 @@ pick_needed_intro_points(hs_service_t *service,
                        hs_service_intro_point_t *, ip) {
     smartlist_add(exclude_nodes, (void *) get_node_from_intro_point(ip));
   } DIGEST256MAP_FOREACH_END;
+  /* Also, add the failing intro points that our descriptor encounteered in
+   * the exclude node list. */
+  setup_intro_point_exclude_list(desc, exclude_nodes);
 
   for (i = 0; i < num_needed_ip; i++) {
     hs_service_intro_point_t *ip;
@@ -1462,9 +1527,19 @@ cleanup_intro_points(hs_service_t *service, time_t now)
        * reached the maximum number of retry with a non existing circuit. */
       if (has_expired || node == NULL ||
           (ocirc == NULL &&
-           ip->circuit_retries >= MAX_INTRO_POINT_CIRCUIT_RETRIES)) {
+           ip->circuit_retries > MAX_INTRO_POINT_CIRCUIT_RETRIES)) {
+        /* Remove intro point from descriptor map. We'll add it to the failed
+         * map if we retried it too many times. */
         MAP_DEL_CURRENT(key);
+
+        /* We've retried too many times, remember it has a failed intro point
+         * so we don't pick it up again. It will be retried in
+         * INTRO_CIRC_RETRY_PERIOD seconds. */
+        if (ip->circuit_retries >= MAX_INTRO_POINT_CIRCUIT_RETRIES) {
+          remember_failing_intro_point(ip, desc, now);
+        }
         service_intro_point_free(ip);
+
         /* XXX: Legacy code does NOT do that, it keeps the circuit open until
          * a new descriptor is uploaded and then closed all expiring intro
          * point circuit. Here, we close immediately and because we just
@@ -1550,6 +1625,10 @@ run_housekeeping_event(time_t now)
     /* Cleanup invalid intro points from the service descriptor. */
     cleanup_intro_points(service, now);
 
+    /* Remove expired failing intro point from the descriptor failed list. We
+     * reset them at each INTRO_CIRC_RETRY_PERIOD. */
+    remove_expired_failing_intro(service, now);
+
     /* At this point, the service is now ready to go through the scheduled
      * events guaranteeing a valid state. Intro points might be missing from
      * the descriptors after the cleanup but the update/build process will
diff --git a/src/or/hs_service.h b/src/or/hs_service.h
index ba805117e..be24bb4e3 100644
--- a/src/or/hs_service.h
+++ b/src/or/hs_service.h
@@ -87,6 +87,12 @@ typedef struct hs_service_intropoints_t {
   /* Contains the current hs_service_intro_point_t objects indexed by
    * authentication public key. */
   digest256map_t *map;
+
+  /* Contains node's identity key digest that were introduction point for this
+   * descriptor but were retried to many times. We keep those so we avoid
+   * re-picking them over and over for a circuit retry period.
+   * XXX: Once we have #22173, change this to only use ed25519 identity. */
+  digestmap_t *failed_id;
 } hs_service_intropoints_t;
 
 /* Representation of a service descriptor. */



_______________________________________________
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits

Reply via email to