diff --git a/src/or/hs_service.c b/src/or/hs_service.c index 85610de572..42523ed852 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 ba805117eb..be24bb4e31 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. */