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.
This commit is contained in:
Yawning Angel 2015-05-20 17:33:59 +00:00
parent 32bd533dda
commit db7bde08be
7 changed files with 94 additions and 8 deletions

5
changes/feature16052 Normal file
View File

@ -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.

View File

@ -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

View File

@ -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 {

View File

@ -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"),

View File

@ -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 */

View File

@ -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

View File

@ -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,
{