mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-10 05:03:43 +01:00
add rate limit on BEGIN and RESOLVE cell per circuit
This commit is contained in:
parent
379fb329d9
commit
1b907d13bb
@ -65,6 +65,7 @@
|
||||
#include "core/or/conflux.h"
|
||||
#include "core/or/conflux_pool.h"
|
||||
#include "core/or/crypt_path.h"
|
||||
#include "core/or/dos.h"
|
||||
#include "core/or/extendinfo.h"
|
||||
#include "core/or/status.h"
|
||||
#include "core/or/trace_probes_circuit.h"
|
||||
@ -1130,6 +1131,7 @@ or_circuit_new(circid_t p_circ_id, channel_t *p_chan)
|
||||
cell_queue_init(&circ->p_chan_cells);
|
||||
|
||||
init_circuit_base(TO_CIRCUIT(circ));
|
||||
dos_stream_init_circ_tbf(circ);
|
||||
|
||||
tor_trace(TR_SUBSYS(circuit), TR_EV(new_or), circ);
|
||||
return circ;
|
||||
|
@ -73,6 +73,7 @@
|
||||
#include "core/or/conflux_util.h"
|
||||
#include "core/or/circuitstats.h"
|
||||
#include "core/or/connection_or.h"
|
||||
#include "core/or/dos.h"
|
||||
#include "core/or/extendinfo.h"
|
||||
#include "core/or/policies.h"
|
||||
#include "core/or/reasons.h"
|
||||
@ -3990,6 +3991,7 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ)
|
||||
begin_cell_t bcell;
|
||||
int rv;
|
||||
uint8_t end_reason=0;
|
||||
dos_stream_defense_type_t dos_defense_type;
|
||||
|
||||
assert_circuit_ok(circ);
|
||||
if (!CIRCUIT_IS_ORIGIN(circ)) {
|
||||
@ -4148,6 +4150,35 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ)
|
||||
|
||||
log_debug(LD_EXIT,"about to start the dns_resolve().");
|
||||
|
||||
/* TODO should this be moved higher to protect from a stream DoS on directory
|
||||
* requests, and possibly against an onion service? (for OS, more changes
|
||||
* would be required) */
|
||||
dos_defense_type = dos_stream_new_begin_or_resolve_cell(or_circ);
|
||||
switch (dos_defense_type) {
|
||||
case DOS_STREAM_DEFENSE_NONE:
|
||||
break;
|
||||
case DOS_STREAM_DEFENSE_REFUSE_STREAM:
|
||||
// we don't use END_STREAM_REASON_RESOURCELIMIT because it would make a
|
||||
// client mark us as non-functional until they get a new consensus.
|
||||
relay_send_end_cell_from_edge(rh.stream_id, circ, END_STREAM_REASON_MISC,
|
||||
layer_hint);
|
||||
connection_free_(TO_CONN(n_stream));
|
||||
return 0;
|
||||
case DOS_STREAM_DEFENSE_CLOSE_CIRCUIT:
|
||||
connection_free_(TO_CONN(n_stream));
|
||||
/* TODO we could return REASON_NONE or REASON_RESOURCELIMIT. When closing
|
||||
* circuits, you either get:
|
||||
* - END_CIRC_REASON_NONE: tons of notice level "We tried for 15
|
||||
* seconds to connect to 'target' using exit X. Retrying on a new
|
||||
* circuit."
|
||||
* - END_CIRC_REASON_RESOURCELIMIT: warn level "Guard X is failing
|
||||
* to carry an extremely large amount of streams on its circuits"
|
||||
*
|
||||
* I'm not sure which one we want
|
||||
*/
|
||||
return -END_CIRC_REASON_NONE;
|
||||
}
|
||||
|
||||
/* send it off to the gethostbyname farm */
|
||||
switch (dns_resolve(n_stream)) {
|
||||
case 1: /* resolve worked; now n_stream is attached to circ. */
|
||||
@ -4171,17 +4202,21 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ)
|
||||
* Called when we receive a RELAY_COMMAND_RESOLVE cell 'cell' along the
|
||||
* circuit <b>circ</b>;
|
||||
* begin resolving the hostname, and (eventually) reply with a RESOLVED cell.
|
||||
*
|
||||
* Return -(some circuit end reason) if we want to tear down <b>circ</b>.
|
||||
* Else return 0.
|
||||
*/
|
||||
int
|
||||
connection_exit_begin_resolve(cell_t *cell, or_circuit_t *circ)
|
||||
{
|
||||
edge_connection_t *dummy_conn;
|
||||
relay_header_t rh;
|
||||
dos_stream_defense_type_t dos_defense_type;
|
||||
|
||||
assert_circuit_ok(TO_CIRCUIT(circ));
|
||||
relay_header_unpack(&rh, cell->payload);
|
||||
if (rh.length > RELAY_PAYLOAD_SIZE)
|
||||
return -1;
|
||||
return 0;
|
||||
|
||||
/* Note the RESOLVE stream as seen. */
|
||||
rep_hist_note_exit_stream(RELAY_COMMAND_RESOLVE);
|
||||
@ -4204,6 +4239,18 @@ connection_exit_begin_resolve(cell_t *cell, or_circuit_t *circ)
|
||||
|
||||
dummy_conn->on_circuit = TO_CIRCUIT(circ);
|
||||
|
||||
dos_defense_type = dos_stream_new_begin_or_resolve_cell(circ);
|
||||
switch (dos_defense_type) {
|
||||
case DOS_STREAM_DEFENSE_NONE:
|
||||
break;
|
||||
case DOS_STREAM_DEFENSE_REFUSE_STREAM:
|
||||
dns_send_resolved_error_cell(dummy_conn, RESOLVED_TYPE_ERROR_TRANSIENT);
|
||||
return 0;
|
||||
case DOS_STREAM_DEFENSE_CLOSE_CIRCUIT:
|
||||
/* TODO maybe use REASON_RESOURCELIMIT? See connection_exit_begin_conn() */
|
||||
return -END_CIRC_REASON_NONE;
|
||||
}
|
||||
|
||||
/* send it off to the gethostbyname farm */
|
||||
switch (dns_resolve(dummy_conn)) {
|
||||
case -1: /* Impossible to resolve; a resolved cell was sent. */
|
||||
|
@ -325,7 +325,8 @@ get_param_stream_defense_type(const networkstatus_t *ns)
|
||||
}
|
||||
return networkstatus_get_param(ns, "DoSStreamCreationDefenseType",
|
||||
DOS_STREAM_DEFENSE_TYPE_DEFAULT,
|
||||
DOS_STREAM_DEFENSE_NONE, DOS_STREAM_DEFENSE_MAX);
|
||||
DOS_STREAM_DEFENSE_NONE,
|
||||
DOS_STREAM_DEFENSE_MAX);
|
||||
}
|
||||
|
||||
/* Set circuit creation parameters located in the consensus or their default
|
||||
@ -836,6 +837,41 @@ dos_conn_addr_get_defense_type(const tor_addr_t *addr)
|
||||
return DOS_CONN_DEFENSE_NONE;
|
||||
}
|
||||
|
||||
/* Stream creation public API. */
|
||||
|
||||
/* Return the action to take against a BEGIN or RESOLVE cell. Return
|
||||
* DOS_STREAM_DEFENSE_NONE when no action should be taken.
|
||||
* Increment the appropriate counter when the cell was found to go over a
|
||||
* limit. */
|
||||
dos_stream_defense_type_t
|
||||
dos_stream_new_begin_or_resolve_cell(or_circuit_t *circ)
|
||||
{
|
||||
if (!dos_stream_enabled || circ == NULL)
|
||||
return DOS_STREAM_DEFENSE_NONE;
|
||||
|
||||
token_bucket_ctr_refill(&circ->stream_limiter,
|
||||
(uint32_t) monotime_coarse_absolute_sec());
|
||||
|
||||
if (token_bucket_ctr_get(&circ->stream_limiter) > 0) {
|
||||
token_bucket_ctr_dec(&circ->stream_limiter, 1);
|
||||
return DOS_STREAM_DEFENSE_NONE;
|
||||
}
|
||||
/* if defense type is DOS_STREAM_DEFENSE_NONE but DoSStreamEnabled is true,
|
||||
* we count offending cells as rejected, despite them being actually
|
||||
* accepted. */
|
||||
++stream_num_rejected;
|
||||
return dos_stream_defense_type;
|
||||
}
|
||||
|
||||
/* Initialize the token bucket for stream rate limit on a circuit. */
|
||||
void
|
||||
dos_stream_init_circ_tbf(or_circuit_t *circ)
|
||||
{
|
||||
token_bucket_ctr_init(&circ->stream_limiter, dos_stream_rate,
|
||||
dos_stream_burst,
|
||||
(uint32_t) monotime_coarse_absolute_sec());
|
||||
}
|
||||
|
||||
/* General API */
|
||||
|
||||
/* Take any appropriate actions for the given geoip entry that is about to get
|
||||
|
@ -186,6 +186,10 @@ typedef enum dos_stream_defense_type_t {
|
||||
DOS_STREAM_DEFENSE_MAX = 3,
|
||||
} dos_stream_defense_type_t;
|
||||
|
||||
dos_stream_defense_type_t dos_stream_new_begin_or_resolve_cell(
|
||||
or_circuit_t *circ);
|
||||
void dos_stream_init_circ_tbf(or_circuit_t *circ);
|
||||
|
||||
#ifdef DOS_PRIVATE
|
||||
|
||||
STATIC uint32_t get_param_conn_max_concurrent_count(
|
||||
|
@ -102,6 +102,10 @@ struct or_circuit_t {
|
||||
* used if this is a service introduction circuit at the intro point
|
||||
* (purpose = CIRCUIT_PURPOSE_INTRO_POINT). */
|
||||
token_bucket_ctr_t introduce2_bucket;
|
||||
|
||||
/** RELAY_BEGIN and RELAY_RESOLVE cell bucket controlling how much can go on
|
||||
* this circuit. Only used if this is the end of a circuit on an exit node.*/
|
||||
token_bucket_ctr_t stream_limiter;
|
||||
};
|
||||
|
||||
#endif /* !defined(OR_CIRCUIT_ST_H) */
|
||||
|
@ -2015,8 +2015,7 @@ handle_relay_cell_command(cell_t *cell, circuit_t *circ,
|
||||
circ->purpose);
|
||||
return 0;
|
||||
}
|
||||
connection_exit_begin_resolve(cell, TO_OR_CIRCUIT(circ));
|
||||
return 0;
|
||||
return connection_exit_begin_resolve(cell, TO_OR_CIRCUIT(circ));
|
||||
case RELAY_COMMAND_RESOLVED:
|
||||
if (conn) {
|
||||
log_fn(LOG_PROTOCOL_WARN, domain,
|
||||
|
@ -563,6 +563,12 @@ send_resolved_cell,(edge_connection_t *conn, uint8_t answer_type,
|
||||
connection_edge_send_command(conn, RELAY_COMMAND_RESOLVED, buf, buflen);
|
||||
}
|
||||
|
||||
void
|
||||
dns_send_resolved_error_cell(edge_connection_t *conn, uint8_t answer_type)
|
||||
{
|
||||
send_resolved_cell(conn, answer_type, NULL);
|
||||
}
|
||||
|
||||
/** Send a response to the RESOLVE request of a connection for an in-addr.arpa
|
||||
* address on connection <b>conn</b> which yielded the result <b>hostname</b>.
|
||||
* The answer type will be RESOLVED_HOSTNAME.
|
||||
|
@ -20,6 +20,8 @@ int dns_reset(void);
|
||||
void connection_dns_remove(edge_connection_t *conn);
|
||||
void assert_connection_edge_not_dns_pending(edge_connection_t *conn);
|
||||
int dns_resolve(edge_connection_t *exitconn);
|
||||
void dns_send_resolved_error_cell(edge_connection_t *conn,
|
||||
uint8_t answer_type);
|
||||
int dns_seems_to_be_broken(void);
|
||||
int dns_seems_to_be_broken_for_ipv6(void);
|
||||
void dns_reset_correctness_checks(void);
|
||||
|
@ -365,6 +365,7 @@ test_status_hb_not_in_consensus(void *arg)
|
||||
"with too many cells, [DoSCircuitCreationEnabled disabled], "
|
||||
"[DoSConnectionEnabled disabled], "
|
||||
"[DoSRefuseSingleHopClientRendezvous disabled], "
|
||||
"[DoSStreamCreationEnabled disabled], "
|
||||
"0 INTRODUCE2 rejected.\n");
|
||||
tt_int_op(mock_saved_log_n_entries(), OP_EQ, 6);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user