|
@@ -13,6 +13,7 @@
|
|
|
#include "circuitbuild.h"
|
|
|
#include "circuitlist.h"
|
|
|
#include "config.h"
|
|
|
+#include "main.h"
|
|
|
#include "networkstatus.h"
|
|
|
#include "nodelist.h"
|
|
|
#include "relay.h"
|
|
@@ -21,6 +22,7 @@
|
|
|
#include "routerkeys.h"
|
|
|
#include "routerlist.h"
|
|
|
|
|
|
+#include "hs_circuit.h"
|
|
|
#include "hs_common.h"
|
|
|
#include "hs_config.h"
|
|
|
#include "hs_circuit.h"
|
|
@@ -385,6 +387,35 @@ get_node_from_intro_point(const hs_service_intro_point_t *ip)
|
|
|
return node_get_by_id((const char *) ls->u.legacy_id);
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+ * return NULL if the node can't be found for the intro point or the extend
|
|
|
+ * info can't be created for the found node. If direct_conn is set, the extend
|
|
|
+ * info is validated on if we can connect directly. */
|
|
|
+static extend_info_t *
|
|
|
+get_extend_info_from_intro_point(const hs_service_intro_point_t *ip,
|
|
|
+ unsigned int direct_conn)
|
|
|
+{
|
|
|
+ extend_info_t *info = NULL;
|
|
|
+ const node_t *node;
|
|
|
+
|
|
|
+ tor_assert(ip);
|
|
|
+
|
|
|
+ node = get_node_from_intro_point(ip);
|
|
|
+ if (node == NULL) {
|
|
|
+
|
|
|
+ * from the consensus. In that case, the intro point will be removed from
|
|
|
+ * the descriptor during the scheduled events. */
|
|
|
+ goto end;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ * our firewall policy won't allow it so this can return a NULL value. */
|
|
|
+ info = extend_info_from_node(node, direct_conn);
|
|
|
+
|
|
|
+ end:
|
|
|
+ return info;
|
|
|
+}
|
|
|
+
|
|
|
|
|
|
* NULL is returned is no such circuit can be found. */
|
|
|
static origin_circuit_t *
|
|
@@ -1425,14 +1456,149 @@ run_build_descriptor_event(time_t now)
|
|
|
update_all_descriptors(now);
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+ * needed. This considers every descriptor of the service. */
|
|
|
+static void
|
|
|
+launch_intro_point_circuits(hs_service_t *service, time_t now)
|
|
|
+{
|
|
|
+ tor_assert(service);
|
|
|
+
|
|
|
+
|
|
|
+ * circuits using the current map. */
|
|
|
+ FOR_EACH_DESCRIPTOR_BEGIN(service, desc) {
|
|
|
+
|
|
|
+ unsigned int direct_conn = service->config.is_single_onion;
|
|
|
+
|
|
|
+ DIGEST256MAP_FOREACH_MODIFY(desc->intro_points.map, key,
|
|
|
+ hs_service_intro_point_t *, ip) {
|
|
|
+ extend_info_t *ei;
|
|
|
+
|
|
|
+
|
|
|
+ * (established or not). */
|
|
|
+ if (get_intro_circuit(ip)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ ei = get_extend_info_from_intro_point(ip, direct_conn);
|
|
|
+ if (ei == NULL) {
|
|
|
+ if (!direct_conn) {
|
|
|
+
|
|
|
+ * can't get the extend info from the node. Avoid connection and
|
|
|
+ * remove intro point from descriptor in order to recover from this
|
|
|
+ * potential bug. */
|
|
|
+ tor_assert_nonfatal(ei);
|
|
|
+ }
|
|
|
+ MAP_DEL_CURRENT(key);
|
|
|
+ service_intro_point_free(ip);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ ip->circuit_retries++;
|
|
|
+ if (hs_circ_launch_intro_point(service, ip, ei, now) < 0) {
|
|
|
+ log_warn(LD_REND, "Unable to launch intro circuit to node %s "
|
|
|
+ "for service %s.",
|
|
|
+ safe_str_client(extend_info_describe(ei)),
|
|
|
+ safe_str_client(service->onion_address));
|
|
|
+
|
|
|
+ }
|
|
|
+ extend_info_free(ei);
|
|
|
+ } DIGEST256MAP_FOREACH_END;
|
|
|
+ } FOR_EACH_DESCRIPTOR_END;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+ * while. Dynamically calculated based on the configured number of intro
|
|
|
+ * points for the given service and how many descriptor exists. The default
|
|
|
+ * use case of 3 introduction points and two descriptors will allow 28
|
|
|
+ * circuits for a retry period (((3 + 2) + (3 * 3)) * 2). */
|
|
|
+static unsigned int
|
|
|
+get_max_intro_circ_per_period(const hs_service_t *service)
|
|
|
+{
|
|
|
+ unsigned int count = 0;
|
|
|
+ unsigned int multiplier = 0;
|
|
|
+ unsigned int num_wanted_ip;
|
|
|
+
|
|
|
+ tor_assert(service);
|
|
|
+ tor_assert(service->config.num_intro_points <=
|
|
|
+ HS_CONFIG_V3_MAX_INTRO_POINTS);
|
|
|
+
|
|
|
+ num_wanted_ip = service->config.num_intro_points;
|
|
|
+
|
|
|
+
|
|
|
+ * want configured as a torrc option (num_intro_points). We then add an
|
|
|
+ * extra value so we can launch multiple circuits at once and pick the
|
|
|
+ * quickest ones. For instance, we want 3 intros, we add 2 extra so we'll
|
|
|
+ * pick 5 intros and launch 5 circuits. */
|
|
|
+ count += (num_wanted_ip + NUM_INTRO_POINTS_EXTRA);
|
|
|
+
|
|
|
+
|
|
|
+ * point. If we want 3 intros, we'll allow 3 times the number of possible
|
|
|
+ * retry. */
|
|
|
+ count += (num_wanted_ip * MAX_INTRO_POINT_CIRCUIT_RETRIES);
|
|
|
+
|
|
|
+
|
|
|
+ * have none. */
|
|
|
+ multiplier += (service->desc_current) ? 1 : 0;
|
|
|
+ multiplier += (service->desc_next) ? 1 : 0;
|
|
|
+
|
|
|
+ return (count * multiplier);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+ * introduction circuits else 0 if the maximum has been reached for the retry
|
|
|
+ * period of INTRO_CIRC_RETRY_PERIOD. */
|
|
|
+static int
|
|
|
+can_service_launch_intro_circuit(hs_service_t *service, time_t now)
|
|
|
+{
|
|
|
+ tor_assert(service);
|
|
|
+
|
|
|
+
|
|
|
+ if (now > (service->state.intro_circ_retry_started_time +
|
|
|
+ INTRO_CIRC_RETRY_PERIOD)) {
|
|
|
+ service->state.intro_circ_retry_started_time = now;
|
|
|
+ service->state.num_intro_circ_launched = 0;
|
|
|
+ goto allow;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (service->state.num_intro_circ_launched <=
|
|
|
+ get_max_intro_circ_per_period(service)) {
|
|
|
+ goto allow;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ {
|
|
|
+ char *msg;
|
|
|
+ time_t elapsed_time = now - service->state.intro_circ_retry_started_time;
|
|
|
+ static ratelim_t rlimit = RATELIM_INIT(INTRO_CIRC_RETRY_PERIOD);
|
|
|
+ if ((msg = rate_limit_log(&rlimit, now))) {
|
|
|
+ log_info(LD_REND, "Hidden service %s exceeded its circuit launch limit "
|
|
|
+ "of %u per %d seconds. It launched %u circuits in "
|
|
|
+ "the last %ld seconds. Will retry in %ld seconds.",
|
|
|
+ safe_str_client(service->onion_address),
|
|
|
+ get_max_intro_circ_per_period(service),
|
|
|
+ INTRO_CIRC_RETRY_PERIOD,
|
|
|
+ service->state.num_intro_circ_launched, elapsed_time,
|
|
|
+ INTRO_CIRC_RETRY_PERIOD - elapsed_time);
|
|
|
+ tor_free(msg);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ return 0;
|
|
|
+ allow:
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+
|
|
|
|
|
|
* we need for each service. */
|
|
|
static void
|
|
|
run_build_circuit_event(time_t now)
|
|
|
{
|
|
|
-
|
|
|
- * circuits as required by services. */
|
|
|
- if (router_have_consensus_path() == CONSENSUS_PATH_UNKNOWN) {
|
|
|
+
|
|
|
+ * internal circuits as required by services. */
|
|
|
+ if (router_have_consensus_path() == CONSENSUS_PATH_UNKNOWN ||
|
|
|
+ !have_completed_a_circuit()) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
@@ -1443,10 +1609,14 @@ run_build_circuit_event(time_t now)
|
|
|
|
|
|
|
|
|
FOR_EACH_SERVICE_BEGIN(service) {
|
|
|
-
|
|
|
-
|
|
|
- * creation. */
|
|
|
- (void) service;
|
|
|
+
|
|
|
+ * circuit creation so make sure this service is respecting that limit. */
|
|
|
+ if (can_service_launch_intro_circuit(service, now)) {
|
|
|
+
|
|
|
+ launch_intro_point_circuits(service, now);
|
|
|
+
|
|
|
+ * descriptor intro point list and cleanup any extraneous. */
|
|
|
+ }
|
|
|
} FOR_EACH_SERVICE_END;
|
|
|
}
|
|
|
|