Merge branch 'bug25903_v5_squashed'

This commit is contained in:
Nick Mathewson 2018-05-14 14:25:07 -04:00
commit a394a2dd86
10 changed files with 377 additions and 11 deletions

6
changes/ticket25903 Normal file
View File

@ -0,0 +1,6 @@
o Minor features (control port):
- Introduce new fields to the CIRC_BW event. There are two new fields in
each of the read and written directions. The DELIVERED fields report the
total valid data on the circuit, as measured by the payload sizes of
verified and error-checked relay command cells. The OVERHEAD fields
report the total unused bytes in each of these cells. Closes ticket 25903.

View File

@ -3106,3 +3106,41 @@ mark_circuit_unusable_for_new_conns(origin_circuit_t *circ)
circ->unusable_for_new_conns = 1;
}
/**
* Add relay_body_len and RELAY_PAYLOAD_SIZE-relay_body_len to
* the valid delivered written fields and the overhead field,
* respectively.
*/
void
circuit_sent_valid_data(origin_circuit_t *circ, uint16_t relay_body_len)
{
if (!circ) return;
tor_assert_nonfatal(relay_body_len <= RELAY_PAYLOAD_SIZE);
circ->n_delivered_written_circ_bw =
tor_add_u32_nowrap(circ->n_delivered_written_circ_bw, relay_body_len);
circ->n_overhead_written_circ_bw =
tor_add_u32_nowrap(circ->n_overhead_written_circ_bw,
RELAY_PAYLOAD_SIZE-relay_body_len);
}
/**
* Add relay_body_len and RELAY_PAYLOAD_SIZE-relay_body_len to
* the valid delivered read field and the overhead field,
* respectively.
*/
void
circuit_read_valid_data(origin_circuit_t *circ, uint16_t relay_body_len)
{
if (!circ) return;
tor_assert_nonfatal(relay_body_len <= RELAY_PAYLOAD_SIZE);
circ->n_delivered_read_circ_bw =
tor_add_u32_nowrap(circ->n_delivered_read_circ_bw, relay_body_len);
circ->n_overhead_read_circ_bw =
tor_add_u32_nowrap(circ->n_overhead_read_circ_bw,
RELAY_PAYLOAD_SIZE-relay_body_len);
}

View File

@ -65,6 +65,8 @@ void mark_circuit_unusable_for_new_conns(origin_circuit_t *circ);
int circuit_purpose_is_hidden_service(uint8_t);
int circuit_should_use_vanguards(uint8_t);
void circuit_sent_valid_data(origin_circuit_t *circ, uint16_t relay_body_len);
void circuit_read_valid_data(origin_circuit_t *circ, uint16_t relay_body_len);
#ifdef TOR_UNIT_TESTS
/* Used only by circuituse.c and test_circuituse.c */

View File

@ -3527,10 +3527,17 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ)
n_stream->deliver_window = STREAMWINDOW_START;
if (circ->purpose == CIRCUIT_PURPOSE_S_REND_JOINED) {
int ret;
tor_free(address);
/* We handle this circuit and stream in this function for all supported
* hidden service version. */
return handle_hs_exit_conn(circ, n_stream);
ret = handle_hs_exit_conn(circ, n_stream);
if (ret == 0) {
/* This was a valid cell. Count it as delivered + overhead. */
circuit_read_valid_data(origin_circ, rh.length);
}
return ret;
}
tor_strlower(address);
n_stream->base_.address = address;

View File

@ -265,6 +265,8 @@ clear_circ_bw_fields(void)
continue;
ocirc = TO_ORIGIN_CIRCUIT(circ);
ocirc->n_written_circ_bw = ocirc->n_read_circ_bw = 0;
ocirc->n_overhead_written_circ_bw = ocirc->n_overhead_read_circ_bw = 0;
ocirc->n_delivered_written_circ_bw = ocirc->n_delivered_read_circ_bw = 0;
}
SMARTLIST_FOREACH_END(circ);
}
@ -5990,13 +5992,20 @@ control_event_circ_bandwidth_used(void)
tor_gettimeofday(&now);
format_iso_time_nospace_usec(tbuf, &now);
send_control_event(EVENT_CIRC_BANDWIDTH_USED,
"650 CIRC_BW ID=%d READ=%lu WRITTEN=%lu "
"TIME=%s\r\n",
"650 CIRC_BW ID=%d READ=%lu WRITTEN=%lu TIME=%s "
"DELIVERED_READ=%lu OVERHEAD_READ=%lu "
"DELIVERED_WRITTEN=%lu OVERHEAD_WRITTEN=%lu\r\n",
ocirc->global_identifier,
(unsigned long)ocirc->n_read_circ_bw,
(unsigned long)ocirc->n_written_circ_bw,
tbuf);
tbuf,
(unsigned long)ocirc->n_delivered_read_circ_bw,
(unsigned long)ocirc->n_overhead_read_circ_bw,
(unsigned long)ocirc->n_delivered_written_circ_bw,
(unsigned long)ocirc->n_overhead_written_circ_bw);
ocirc->n_written_circ_bw = ocirc->n_read_circ_bw = 0;
ocirc->n_overhead_written_circ_bw = ocirc->n_overhead_read_circ_bw = 0;
ocirc->n_delivered_written_circ_bw = ocirc->n_delivered_read_circ_bw = 0;
}
SMARTLIST_FOREACH_END(circ);

View File

@ -3260,16 +3260,36 @@ typedef struct origin_circuit_t {
* associated with this circuit. */
edge_connection_t *p_streams;
/** Bytes read from any attached stream since last call to
/** Bytes read on this circuit since last call to
* control_event_circ_bandwidth_used(). Only used if we're configured
* to emit CIRC_BW events. */
uint32_t n_read_circ_bw;
/** Bytes written to any attached stream since last call to
/** Bytes written to on this circuit since last call to
* control_event_circ_bandwidth_used(). Only used if we're configured
* to emit CIRC_BW events. */
uint32_t n_written_circ_bw;
/** Total known-valid relay cell bytes since last call to
* control_event_circ_bandwidth_used(). Only used if we're configured
* to emit CIRC_BW events. */
uint32_t n_delivered_read_circ_bw;
/** Total written relay cell bytes since last call to
* control_event_circ_bandwidth_used(). Only used if we're configured
* to emit CIRC_BW events. */
uint32_t n_delivered_written_circ_bw;
/** Total overhead data in all known-valid relay data cells since last
* call to control_event_circ_bandwidth_used(). Only used if we're
* configured to emit CIRC_BW events. */
uint32_t n_overhead_read_circ_bw;
/** Total written overhead data in all relay data cells since last call to
* control_event_circ_bandwidth_used(). Only used if we're configured
* to emit CIRC_BW events. */
uint32_t n_overhead_written_circ_bw;
/** Build state for this circuit. It includes the intended path
* length, the chosen exit router, rendezvous information, etc.
*/

View File

@ -85,9 +85,6 @@ static edge_connection_t *relay_lookup_conn(circuit_t *circ, cell_t *cell,
cell_direction_t cell_direction,
crypt_path_t *layer_hint);
static int connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
edge_connection_t *conn,
crypt_path_t *layer_hint);
static void circuit_consider_sending_sendme(circuit_t *circ,
crypt_path_t *layer_hint);
static void circuit_resume_edge_reading(circuit_t *circ,
@ -613,6 +610,10 @@ relay_send_command_from_edge_,(streamid_t stream_id, circuit_t *circ,
tor_free(commands);
smartlist_free(commands_list);
}
/* Let's assume we're well-behaved: Anything that we decide to send is
* valid, delivered data. */
circuit_sent_valid_data(origin_circ, rh.length);
}
if (circuit_package_relay_cell(&cell, circ, cell_direction, cpath_layer,
@ -742,6 +743,9 @@ connection_ap_process_end_not_open(
}
}
/* This end cell is now valid. */
circuit_read_valid_data(circ, rh->length);
if (rh->length == 0) {
reason = END_STREAM_REASON_MISC;
}
@ -1232,6 +1236,12 @@ connection_edge_process_resolved_cell(edge_connection_t *conn,
}
}
/* This is valid data at this point. Count it */
if (conn->on_circuit && CIRCUIT_IS_ORIGIN(conn->on_circuit)) {
circuit_read_valid_data(TO_ORIGIN_CIRCUIT(conn->on_circuit),
rh->length);
}
connection_ap_handshake_socks_got_resolved_cell(entry_conn,
errcode,
resolved_addresses);
@ -1328,6 +1338,9 @@ connection_edge_process_relay_cell_not_open(
entry_conn->chosen_exit_name, ttl);
remap_event_helper(entry_conn, &addr);
/* This is valid data at this point. Count it */
circuit_read_valid_data(TO_ORIGIN_CIRCUIT(circ), rh->length);
}
circuit_log_path(LOG_INFO,LD_APP,TO_ORIGIN_CIRCUIT(circ));
/* don't send a socks reply to transparent conns */
@ -1398,7 +1411,7 @@ connection_edge_process_relay_cell_not_open(
*
* Return -reason if you want to warn and tear down the circuit, else 0.
*/
static int
STATIC int
connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
edge_connection_t *conn,
crypt_path_t *layer_hint)
@ -1498,7 +1511,6 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
circ->dirreq_id = ++next_id;
TO_OR_CIRCUIT(circ)->p_chan->dirreq_id = circ->dirreq_id;
}
return connection_exit_begin_conn(cell, circ);
case RELAY_COMMAND_DATA:
++stats_n_data_cells_received;
@ -1534,6 +1546,10 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
"(relay data) conn deliver_window below 0. Killing.");
return -END_CIRC_REASON_TORPROTOCOL;
}
/* Total all valid application bytes delivered */
if (CIRCUIT_IS_ORIGIN(circ)) {
circuit_read_valid_data(TO_ORIGIN_CIRCUIT(circ), rh.length);
}
stats_n_data_bytes_received += rh.length;
connection_buf_add((char*)(cell->payload + RELAY_HEADER_SIZE),
@ -1586,6 +1602,11 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
/* only mark it if not already marked. it's possible to
* get the 'end' right around when the client hangs up on us. */
connection_mark_and_flush(TO_CONN(conn));
/* Total all valid application bytes delivered */
if (CIRCUIT_IS_ORIGIN(circ)) {
circuit_read_valid_data(TO_ORIGIN_CIRCUIT(circ), rh.length);
}
}
return 0;
case RELAY_COMMAND_EXTEND:
@ -1651,6 +1672,10 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
log_info(domain,"circuit_send_next_onion_skin() failed.");
return reason;
}
/* Total all valid bytes delivered. */
if (CIRCUIT_IS_ORIGIN(circ)) {
circuit_read_valid_data(TO_ORIGIN_CIRCUIT(circ), rh.length);
}
return 0;
case RELAY_COMMAND_TRUNCATE:
if (layer_hint) {
@ -1716,6 +1741,16 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
log_debug(LD_APP,"circ-level sendme at origin, packagewindow %d.",
layer_hint->package_window);
circuit_resume_edge_reading(circ, layer_hint);
/* We count circuit-level sendme's as valid delivered data because
* they are rate limited. Note that we cannot count stream
* sendme's because the other end could send as many as they like.
*/
if (CIRCUIT_IS_ORIGIN(circ)) {
circuit_read_valid_data(TO_ORIGIN_CIRCUIT(circ),
rh.length);
}
} else {
if (circ->package_window + CIRCWINDOW_INCREMENT >
CIRCWINDOW_START_MAX) {

View File

@ -114,6 +114,10 @@ STATIC packed_cell_t *packed_cell_new(void);
STATIC packed_cell_t *cell_queue_pop(cell_queue_t *queue);
STATIC destroy_cell_t *destroy_cell_queue_pop(destroy_cell_queue_t *queue);
STATIC int cell_queues_check_size(void);
STATIC int connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
edge_connection_t *conn,
crypt_path_t *layer_hint);
#endif /* defined(RELAY_PRIVATE) */
#endif /* !defined(TOR_RELAY_H) */

View File

@ -12,6 +12,7 @@
#include "or.h"
#include "circuitbuild.h"
#include "circuituse.h"
#include "config.h"
#include "control.h"
#include "crypto_rand.h"
@ -809,6 +810,11 @@ rend_process_relay_cell(circuit_t *circ, const crypt_path_t *layer_hint,
tor_fragile_assert();
}
if (r == 0 && origin_circ) {
/* This was a valid cell. Count it as delivered + overhead. */
circuit_read_valid_data(origin_circ, length);
}
if (r == -2)
log_info(LD_PROTOCOL, "Dropping cell (type %d) for wrong circuit type.",
command);

View File

@ -4,9 +4,14 @@
/* Unit tests for handling different kinds of relay cell */
#define RELAY_PRIVATE
#define CIRCUITLIST_PRIVATE
#include "or.h"
#include "main.h"
#include "config.h"
#include "connection.h"
#include "crypto.h"
#include "circuitbuild.h"
#include "circuitlist.h"
#include "connection_edge.h"
#include "relay.h"
#include "test.h"
@ -20,6 +25,11 @@ static uint8_t srm_answer[512];
static int srm_ttl;
static time_t srm_expires;
void connection_free_minimal(connection_t*);
int connected_cell_format_payload(uint8_t *payload_out,
const tor_addr_t *addr,
uint32_t ttl);
/* Mock replacement for connection_ap_hannshake_socks_resolved() */
static void
socks_resolved_mock(entry_connection_t *conn,
@ -60,6 +70,234 @@ mark_unattached_mock(entry_connection_t *conn, int endreason,
(void) file;
}
/* Helper: Return a newly allocated and initialized origin circuit with
* purpose and flags. A default HS identifier is set to an ed25519
* authentication key for introduction point. */
static origin_circuit_t *
helper_create_origin_circuit(int purpose, int flags)
{
origin_circuit_t *circ = NULL;
circ = origin_circuit_init(purpose, flags);
tor_assert(circ);
circ->cpath = tor_malloc_zero(sizeof(crypt_path_t));
circ->cpath->magic = CRYPT_PATH_MAGIC;
circ->cpath->state = CPATH_STATE_OPEN;
circ->cpath->package_window = circuit_initial_package_window();
circ->cpath->deliver_window = CIRCWINDOW_START;
circ->cpath->prev = circ->cpath;
/* Create a default HS identifier. */
circ->hs_ident = tor_malloc_zero(sizeof(hs_ident_circuit_t));
return circ;
}
static void
mock_connection_mark_unattached_ap_(entry_connection_t *conn, int endreason,
int line, const char *file)
{
(void) line;
(void) file;
conn->edge_.end_reason = endreason;
}
static void
mock_mark_for_close(connection_t *conn,
int line, const char *file)
{
(void)line;
(void)file;
conn->marked_for_close = 1;
return;
}
static void
mock_start_reading(connection_t *conn)
{
(void)conn;
return;
}
static void
test_circbw_relay(void *arg)
{
cell_t cell;
relay_header_t rh;
tor_addr_t addr;
edge_connection_t *edgeconn;
entry_connection_t *entryconn;
origin_circuit_t *circ;
int delivered = 0;
int overhead = 0;
(void)arg;
#define PACK_CELL(id, cmd, body_s) do { \
memset(&cell, 0, sizeof(cell)); \
memset(&rh, 0, sizeof(rh)); \
memcpy(cell.payload+RELAY_HEADER_SIZE, (body_s), sizeof((body_s))-1); \
rh.length = sizeof((body_s))-1; \
rh.command = (cmd); \
rh.stream_id = (id); \
relay_header_pack((uint8_t*)&cell.payload, &rh); \
} while (0)
#define ASSERT_COUNTED_BW() do { \
tt_int_op(circ->n_delivered_read_circ_bw, OP_EQ, delivered+rh.length); \
tt_int_op(circ->n_overhead_read_circ_bw, OP_EQ, \
overhead+RELAY_PAYLOAD_SIZE-rh.length); \
delivered = circ->n_delivered_read_circ_bw; \
overhead = circ->n_overhead_read_circ_bw; \
} while (0)
#define ASSERT_UNCOUNTED_BW() do { \
tt_int_op(circ->n_delivered_read_circ_bw, OP_EQ, delivered); \
tt_int_op(circ->n_overhead_read_circ_bw, OP_EQ, overhead); \
} while (0)
MOCK(connection_mark_unattached_ap_, mock_connection_mark_unattached_ap_);
MOCK(connection_start_reading, mock_start_reading);
MOCK(connection_mark_for_close_internal_, mock_mark_for_close);
entryconn = entry_connection_new(CONN_TYPE_AP, AF_INET);
edgeconn = ENTRY_TO_EDGE_CONN(entryconn);
edgeconn->base_.state = AP_CONN_STATE_CONNECT_WAIT;
edgeconn->deliver_window = 1000;
circ = helper_create_origin_circuit(CIRCUIT_PURPOSE_C_GENERAL, 0);
edgeconn->cpath_layer = circ->cpath;
circ->cpath->state = CPATH_STATE_AWAITING_KEYS;
circ->cpath->deliver_window = 1000;
/* Stream id 0: Not counted */
PACK_CELL(0, RELAY_COMMAND_END, "Data1234");
connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), edgeconn,
circ->cpath);
ASSERT_UNCOUNTED_BW();
/* Stream id 1: Counted */
PACK_CELL(1, RELAY_COMMAND_END, "Data1234");
connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), edgeconn,
circ->cpath);
ASSERT_COUNTED_BW();
/* Properly formatted connect cell: counted */
PACK_CELL(1, RELAY_COMMAND_CONNECTED, "Data1234");
tor_addr_parse(&addr, "30.40.50.60");
rh.length = connected_cell_format_payload(cell.payload+RELAY_HEADER_SIZE,
&addr, 1024);
relay_header_pack((uint8_t*)&cell.payload, &rh); \
connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), edgeconn,
circ->cpath);
ASSERT_COUNTED_BW();
/* Properly formatted resolved cell in correct state: counted */
edgeconn->base_.state = AP_CONN_STATE_RESOLVE_WAIT;
entryconn->socks_request->command = SOCKS_COMMAND_RESOLVE;
edgeconn->on_circuit = TO_CIRCUIT(circ);
PACK_CELL(1, RELAY_COMMAND_RESOLVED,
"\x04\x04\x12\x00\x00\x01\x00\x00\x02\x00");
connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), edgeconn,
circ->cpath);
ASSERT_COUNTED_BW();
edgeconn->base_.state = AP_CONN_STATE_OPEN;
entryconn->socks_request->has_finished = 1;
/* Connected cell after open: not counted */
PACK_CELL(1, RELAY_COMMAND_CONNECTED, "Data1234");
connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), edgeconn,
circ->cpath);
ASSERT_UNCOUNTED_BW();
/* Resolved cell after open: not counted */
PACK_CELL(1, RELAY_COMMAND_RESOLVED, "Data1234");
connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), edgeconn,
circ->cpath);
ASSERT_UNCOUNTED_BW();
/* Drop cell: not counted */
PACK_CELL(1, RELAY_COMMAND_DROP, "Data1234");
connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), edgeconn,
circ->cpath);
ASSERT_UNCOUNTED_BW();
/* Data cell on stream 0: not counted */
PACK_CELL(0, RELAY_COMMAND_DATA, "Data1234");
connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), edgeconn,
circ->cpath);
ASSERT_UNCOUNTED_BW();
/* Data cell on open connection: counted */
ENTRY_TO_CONN(entryconn)->marked_for_close = 0;
PACK_CELL(1, RELAY_COMMAND_DATA, "Data1234");
connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), edgeconn,
circ->cpath);
ASSERT_COUNTED_BW();
/* Sendme on stream: not counted */
ENTRY_TO_CONN(entryconn)->outbuf_flushlen = 0;
PACK_CELL(1, RELAY_COMMAND_SENDME, "Data1234");
connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), edgeconn,
circ->cpath);
ASSERT_UNCOUNTED_BW();
/* Sendme on circuit with full window: not counted */
PACK_CELL(0, RELAY_COMMAND_SENDME, "Data1234");
connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), edgeconn,
circ->cpath);
ASSERT_UNCOUNTED_BW();
/* Sendme on circuit with non-full window: counted */
PACK_CELL(0, RELAY_COMMAND_SENDME, "Data1234");
circ->cpath->package_window = 900;
connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), edgeconn,
circ->cpath);
ASSERT_COUNTED_BW();
/* End cell on non-closed connection: counted */
PACK_CELL(1, RELAY_COMMAND_END, "Data1234");
connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), edgeconn,
circ->cpath);
ASSERT_COUNTED_BW();
/* End cell on connection that already got one: not counted */
PACK_CELL(1, RELAY_COMMAND_END, "Data1234");
connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), NULL,
circ->cpath);
ASSERT_UNCOUNTED_BW();
/* Invalid extended cell: not counted */
PACK_CELL(1, RELAY_COMMAND_EXTENDED2, "Data1234");
connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), NULL,
circ->cpath);
ASSERT_UNCOUNTED_BW();
/* Invalid extended cell: not counted */
PACK_CELL(1, RELAY_COMMAND_EXTENDED, "Data1234");
connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), NULL,
circ->cpath);
ASSERT_UNCOUNTED_BW();
/* Invalid HS cell: not counted */
PACK_CELL(1, RELAY_COMMAND_ESTABLISH_INTRO, "Data1234");
connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), NULL,
circ->cpath);
ASSERT_UNCOUNTED_BW();
/* "Valid" HS cell in expected state: counted */
TO_CIRCUIT(circ)->purpose = CIRCUIT_PURPOSE_C_ESTABLISH_REND;
PACK_CELL(1, RELAY_COMMAND_RENDEZVOUS_ESTABLISHED, "Data1234");
connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), NULL,
circ->cpath);
ASSERT_COUNTED_BW();
done:
UNMOCK(connection_start_reading);
UNMOCK(connection_mark_unattached_ap_);
UNMOCK(connection_mark_for_close_internal_);
circuit_free_(TO_CIRCUIT(circ));
connection_free_minimal(ENTRY_TO_CONN(entryconn));
}
/* Tests for connection_edge_process_resolved_cell().
The point of ..process_resolved_cell() is to handle an incoming cell
@ -244,6 +482,7 @@ test_relaycell_resolved(void *arg)
struct testcase_t relaycell_tests[] = {
{ "resolved", test_relaycell_resolved, TT_FORK, NULL, NULL },
{ "circbw", test_circbw_relay, TT_FORK, NULL, NULL },
END_OF_TESTCASES
};