[tor-commits] [tor/master] Add "HiddenServiceMaxStreams" as a per-HS tunable.

nickm at torproject.org nickm at torproject.org
Thu May 21 14:48:59 UTC 2015


commit db7bde08be59398488624bc377d1d5318182ee45
Author: Yawning Angel <yawning at schwanenlied.me>
Date:   Wed May 20 17:33:59 2015 +0000

    Add "HiddenServiceMaxStreams" as a per-HS tunable.
    
    When set, this limits the maximum number of simultaneous streams per
    rendezvous circuit on the server side of a HS, with further RELAY_BEGIN
    cells being silently ignored.
    
    This can be modified via "HiddenServiceMaxStreamsCloseCircuit", which
    if set will cause offending rendezvous circuits to be torn down instead.
    
    Addresses part of #16052.
---
 changes/feature16052     |    5 +++++
 doc/tor.1.txt            |   10 +++++++++
 src/or/circuituse.c      |   27 ++++++++++++++++-------
 src/or/config.c          |    2 ++
 src/or/connection_edge.c |    2 ++
 src/or/or.h              |    3 +++
 src/or/rendservice.c     |   53 ++++++++++++++++++++++++++++++++++++++++++++++
 7 files changed, 94 insertions(+), 8 deletions(-)

diff --git a/changes/feature16052 b/changes/feature16052
new file mode 100644
index 0000000..cd09b58
--- /dev/null
+++ b/changes/feature16052
@@ -0,0 +1,5 @@
+  o Minor features (hidden service):
+    - Add the new options "HiddenServiceMaxStreams" and
+      "HiddenServiceMaxStreamsCloseCircuit" to allow hidden services to limit
+      the maximum number of simultaneous streams per circuit, and optionally
+      tear down the circuit when the limit is exceeded. Part of ticket 16052.
diff --git a/doc/tor.1.txt b/doc/tor.1.txt
index 2bb5f94..13f2bdd 100644
--- a/doc/tor.1.txt
+++ b/doc/tor.1.txt
@@ -2149,6 +2149,16 @@ The following options are used to configure a hidden service.
    not an authorization mechanism; it is instead meant to be a mild
    inconvenience to port-scanners.) (Default: 0)
 
+[[HiddenServiceMaxStreams]] **HiddenServiceMaxStreams** __N__::
+   The maximum number of simultaneous streams (connections) per rendezvous
+   circuit. (Setting this to 0 will allow an unlimited number of simultanous
+   streams.) (Default: 0)
+
+[[HiddenServiceMaxStreamsCloseCircuit]] **HiddenServiceMaxStreamsCloseCircuit** **0**|**1**::
+   If set to 1, then exceeding **HiddenServiceMaxStreams** will cause the
+   offending rendezvous circuit to be torn down, as opposed to stream creation
+   requests that exceed the limit being silently ignored. (Default: 0)
+
 [[RendPostPeriod]] **RendPostPeriod** __N__ **seconds**|**minutes**|**hours**|**days**|**weeks**::
     Every time the specified period elapses, Tor uploads any rendezvous
     service descriptors to the directory servers. This information  is also
diff --git a/src/or/circuituse.c b/src/or/circuituse.c
index b54a4d2..a429a7d 100644
--- a/src/or/circuituse.c
+++ b/src/or/circuituse.c
@@ -1189,17 +1189,28 @@ circuit_detach_stream(circuit_t *circ, edge_connection_t *conn)
 
   if (CIRCUIT_IS_ORIGIN(circ)) {
     origin_circuit_t *origin_circ = TO_ORIGIN_CIRCUIT(circ);
+    int removed = 0;
     if (conn == origin_circ->p_streams) {
       origin_circ->p_streams = conn->next_stream;
-      return;
+      removed = 1;
+    } else {
+      for (prevconn = origin_circ->p_streams;
+           prevconn && prevconn->next_stream && prevconn->next_stream != conn;
+           prevconn = prevconn->next_stream)
+        ;
+      if (prevconn && prevconn->next_stream) {
+        prevconn->next_stream = conn->next_stream;
+        removed = 1;
+      }
     }
-
-    for (prevconn = origin_circ->p_streams;
-         prevconn && prevconn->next_stream && prevconn->next_stream != conn;
-         prevconn = prevconn->next_stream)
-      ;
-    if (prevconn && prevconn->next_stream) {
-      prevconn->next_stream = conn->next_stream;
+    if (removed) {
+      /* If the stream was removed, and it was a rend stream, decrement the
+       * number of streams on the circuit associated with the rend service.
+       */
+      if (circ->purpose == CIRCUIT_PURPOSE_S_REND_JOINED) {
+        tor_assert(origin_circ->rend_data);
+        origin_circ->rend_data->nr_streams--;
+      }
       return;
     }
   } else {
diff --git a/src/or/config.c b/src/or/config.c
index 1030448..1c04578 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -286,6 +286,8 @@ static config_var_t option_vars_[] = {
   VAR("HiddenServiceVersion",LINELIST_S, RendConfigLines,    NULL),
   VAR("HiddenServiceAuthorizeClient",LINELIST_S,RendConfigLines, NULL),
   VAR("HiddenServiceAllowUnknownPorts",LINELIST_S, RendConfigLines, NULL),
+  VAR("HiddenServiceMaxStreams",LINELIST_S, RendConfigLines, NULL),
+  VAR("HiddenServiceMaxStreamsCloseCircuit",LINELIST_S, RendConfigLines, NULL),
   V(HiddenServiceStatistics,     BOOL,     "0"),
   V(HidServAuth,                 LINELIST, NULL),
   V(CloseHSClientCircuitsImmediatelyOnTimeout, BOOL, "0"),
diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c
index cc6e3d7..c63c350 100644
--- a/src/or/connection_edge.c
+++ b/src/or/connection_edge.c
@@ -2860,6 +2860,8 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ)
     origin_circ->p_streams = n_stream;
     assert_circuit_ok(circ);
 
+    origin_circ->rend_data->nr_streams++;
+
     connection_exit_connect(n_stream);
 
     /* For path bias: This circuit was used successfully */
diff --git a/src/or/or.h b/src/or/or.h
index 5bb080f..af34967 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -818,6 +818,9 @@ typedef struct rend_data_t {
   /** List of HSDir fingerprints on which this request has been sent to.
    * This contains binary identity digest of the directory. */
   smartlist_t *hsdirs_fp;
+
+  /** Number of streams associated with this rendezvous circuit. */
+  int nr_streams;
 } rend_data_t;
 
 /** Time interval for tracking replays of DH public keys received in
diff --git a/src/or/rendservice.c b/src/or/rendservice.c
index daca4cc..5d2225e 100644
--- a/src/or/rendservice.c
+++ b/src/or/rendservice.c
@@ -147,6 +147,13 @@ typedef struct rend_service_t {
   /** If true, we don't close circuits for making requests to unsupported
    * ports. */
   int allow_unknown_ports;
+  /** The maximum number of simultanious streams-per-circuit that are allowed
+   * to be established, or 0 if no limit is set.
+   */
+  int max_streams_per_circuit;
+  /** If true, we close circuits that exceed the max_streams_per_circuit
+   * limit.  */
+  int max_streams_close_circuit;
 } rend_service_t;
 
 /** Returns a escaped string representation of the service, <b>s</b>.
@@ -539,6 +546,33 @@ rend_config_services(const or_options_t *options, int validate_only)
         log_info(LD_CONFIG,
                  "HiddenServiceDirGroupReadable=%d for %s",
                  service->dir_group_readable, service->directory);
+    } else if (!strcasecmp(line->key, "HiddenServiceMaxStreams")) {
+      service->max_streams_per_circuit = (int)tor_parse_long(line->value,
+                                                    10, 0, 65535, &ok, NULL);
+      if (!ok) {
+        log_warn(LD_CONFIG,
+                 "HiddenServiceMaxStreams should be between 0 and %d, not %s",
+                 65535, line->value);
+        rend_service_free(service);
+        return -1;
+      }
+      log_info(LD_CONFIG,
+               "HiddenServiceMaxStreams=%d for %s",
+               service->max_streams_per_circuit, service->directory);
+    } else if (!strcasecmp(line->key, "HiddenServiceMaxStreamsCloseCircuit")) {
+      service->max_streams_close_circuit = (int)tor_parse_long(line->value,
+                                                        10, 0, 1, &ok, NULL);
+      if (!ok) {
+        log_warn(LD_CONFIG,
+                 "HiddenServiceMaxStreamsCloseCircuit should be 0 or 1, not %s",
+                 line->value);
+        rend_service_free(service);
+        return -1;
+      }
+      log_info(LD_CONFIG,
+               "HiddenServiceMaxStreamsCloseCircuit=%d for %s",
+               (int)service->max_streams_close_circuit, service->directory);
+
     } else if (!strcasecmp(line->key, "HiddenServiceAuthorizeClient")) {
       /* Parse auth type and comma-separated list of client names and add a
        * rend_authorized_client_t for each client to the service's list
@@ -3795,6 +3829,25 @@ rend_service_set_connection_addr_port(edge_connection_t *conn,
              serviceid, (unsigned)circ->base_.n_circ_id);
     return -2;
   }
+  if (service->max_streams_per_circuit > 0) {
+    /* Enforce the streams-per-circuit limit, and refuse to provide a
+     * mapping if this circuit will exceed the limit. */
+#define MAX_STREAM_WARN_INTERVAL 600
+    static struct ratelim_t stream_ratelim =
+        RATELIM_INIT(MAX_STREAM_WARN_INTERVAL);
+    if (circ->rend_data->nr_streams >= service->max_streams_per_circuit) {
+      log_fn_ratelim(&stream_ratelim, LOG_WARN, LD_REND,
+                     "Maximum streams per circuit limit reached on rendezvous "
+                     "circuit %u; %s.  Circuit has %d out of %d streams.",
+                     (unsigned)circ->base_.n_circ_id,
+                     service->max_streams_close_circuit ?
+                       "closing circuit" :
+                       "ignoring open stream request",
+                     circ->rend_data->nr_streams,
+                     service->max_streams_per_circuit);
+      return service->max_streams_close_circuit ? -2 : -1;
+    }
+  }
   matching_ports = smartlist_new();
   SMARTLIST_FOREACH(service->ports, rend_service_port_config_t *, p,
   {





More information about the tor-commits mailing list