diff --git a/changes/ticket25903 b/changes/ticket25903 new file mode 100644 index 000000000..8d358c8a8 --- /dev/null +++ b/changes/ticket25903 @@ -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. diff --git a/src/or/circuituse.c b/src/or/circuituse.c index ec0965828..8e007ce92 100644 --- a/src/or/circuituse.c +++ b/src/or/circuituse.c @@ -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); +} + diff --git a/src/or/circuituse.h b/src/or/circuituse.h index 71c818b97..6458bd690 100644 --- a/src/or/circuituse.h +++ b/src/or/circuituse.h @@ -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 */ diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c index 28e18aa85..046369af6 100644 --- a/src/or/connection_edge.c +++ b/src/or/connection_edge.c @@ -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; diff --git a/src/or/control.c b/src/or/control.c index 9323173f5..0d637dce7 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -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); diff --git a/src/or/or.h b/src/or/or.h index cd77b2105..750c79fd4 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -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. */ diff --git a/src/or/relay.c b/src/or/relay.c index b0b1af445..add5a0419 100644 --- a/src/or/relay.c +++ b/src/or/relay.c @@ -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) { diff --git a/src/or/relay.h b/src/or/relay.h index f304af684..ce0969b46 100644 --- a/src/or/relay.h +++ b/src/or/relay.h @@ -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) */ diff --git a/src/or/rendcommon.c b/src/or/rendcommon.c index 3a7dfe28f..f3fa2f64d 100644 --- a/src/or/rendcommon.c +++ b/src/or/rendcommon.c @@ -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); diff --git a/src/test/test_relaycell.c b/src/test/test_relaycell.c index eea1f5dc8..1db520273 100644 --- a/src/test/test_relaycell.c +++ b/src/test/test_relaycell.c @@ -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 };