diff options
Diffstat (limited to 'src/or/relay.c')
-rw-r--r-- | src/or/relay.c | 233 |
1 files changed, 111 insertions, 122 deletions
diff --git a/src/or/relay.c b/src/or/relay.c index ce9fb934e2..29f34ca033 100644 --- a/src/or/relay.c +++ b/src/or/relay.c @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -26,19 +26,18 @@ #include "control.h" #include "geoip.h" #include "main.h" -#ifdef ENABLE_MEMPOOLS -#include "mempool.h" -#endif #include "networkstatus.h" #include "nodelist.h" #include "onion.h" #include "policies.h" #include "reasons.h" #include "relay.h" +#include "rendcache.h" #include "rendcommon.h" #include "router.h" #include "routerlist.h" #include "routerparse.h" +#include "scheduler.h" static edge_connection_t *relay_lookup_conn(circuit_t *circ, cell_t *cell, cell_direction_t cell_direction, @@ -149,20 +148,15 @@ relay_digest_matches(crypto_digest_t *digest, cell_t *cell) * * If <b>encrypt_mode</b> is 1 then encrypt, else decrypt. * - * Return -1 if the crypto fails, else return 0. + * Returns 0. */ static int relay_crypt_one_payload(crypto_cipher_t *cipher, uint8_t *in, int encrypt_mode) { - int r; (void)encrypt_mode; - r = crypto_cipher_crypt_inplace(cipher, (char*) in, CELL_PAYLOAD_SIZE); + crypto_cipher_crypt_inplace(cipher, (char*) in, CELL_PAYLOAD_SIZE); - if (r) { - log_warn(LD_BUG,"Error during relay encryption"); - return -1; - } return 0; } @@ -210,8 +204,7 @@ circuit_receive_relay_cell(cell_t *cell, circuit_t *circ, return 0; } - conn = relay_lookup_conn(circ, cell, cell_direction, - layer_hint); + conn = relay_lookup_conn(circ, cell, cell_direction, layer_hint); if (cell_direction == CELL_DIRECTION_OUT) { ++stats_n_relay_cells_delivered; log_debug(LD_OR,"Sending away from origin."); @@ -262,12 +255,12 @@ circuit_receive_relay_cell(cell_t *cell, circuit_t *circ, if (! CIRCUIT_IS_ORIGIN(circ) && TO_OR_CIRCUIT(circ)->rend_splice && cell_direction == CELL_DIRECTION_OUT) { - or_circuit_t *splice = TO_OR_CIRCUIT(circ)->rend_splice; + or_circuit_t *splice_ = TO_OR_CIRCUIT(circ)->rend_splice; tor_assert(circ->purpose == CIRCUIT_PURPOSE_REND_ESTABLISHED); - tor_assert(splice->base_.purpose == CIRCUIT_PURPOSE_REND_ESTABLISHED); - cell->circ_id = splice->p_circ_id; + tor_assert(splice_->base_.purpose == CIRCUIT_PURPOSE_REND_ESTABLISHED); + cell->circ_id = splice_->p_circ_id; cell->command = CELL_RELAY; /* can't be relay_early anyway */ - if ((reason = circuit_receive_relay_cell(cell, TO_CIRCUIT(splice), + if ((reason = circuit_receive_relay_cell(cell, TO_CIRCUIT(splice_), CELL_DIRECTION_IN)) < 0) { log_warn(LD_REND, "Error relaying cell across rendezvous; closing " "circuits"); @@ -390,6 +383,11 @@ circuit_package_relay_cell(cell_t *cell, circuit_t *circ, { channel_t *chan; /* where to send the cell */ + if (circ->marked_for_close) { + /* Circuit is marked; send nothing. */ + return 0; + } + if (cell_direction == CELL_DIRECTION_OUT) { crypt_path_t *thishop; /* counter for repeated crypts */ chan = circ->n_chan; @@ -703,6 +701,12 @@ connection_edge_send_command(edge_connection_t *fromconn, return -1; } + if (circ->marked_for_close) { + /* The circuit has been marked, but not freed yet. When it's freed, it + * will mark this connection for close. */ + return -1; + } + return relay_send_command_from_edge(fromconn->stream_id, circ, relay_command, payload, payload_len, cpath_layer); @@ -803,8 +807,10 @@ connection_ap_process_end_not_open( return 0; } - if ((tor_addr_family(&addr) == AF_INET && !conn->ipv4_traffic_ok) || - (tor_addr_family(&addr) == AF_INET6 && !conn->ipv6_traffic_ok)) { + if ((tor_addr_family(&addr) == AF_INET && + !conn->entry_cfg.ipv4_traffic) || + (tor_addr_family(&addr) == AF_INET6 && + !conn->entry_cfg.ipv6_traffic)) { log_fn(LOG_PROTOCOL_WARN, LD_APP, "Got an EXITPOLICY failure on a connection with a " "mismatched family. Closing."); @@ -833,7 +839,7 @@ connection_ap_process_end_not_open( } } } - /* check if he *ought* to have allowed it */ + /* check if the exit *ought* to have allowed it */ adjust_exit_policy_from_exitpolicy_failure(circ, conn, @@ -864,6 +870,7 @@ connection_ap_process_end_not_open( break; /* break means it'll close, below */ /* Else fall through: expire this circuit, clear the * chosen_exit_name field, and try again. */ + /* Falls through. */ case END_STREAM_REASON_RESOLVEFAILED: case END_STREAM_REASON_TIMEOUT: case END_STREAM_REASON_MISC: @@ -1155,11 +1162,11 @@ connection_ap_handshake_socks_got_resolved_cell(entry_connection_t *conn, addr_hostname = addr; } } else if (tor_addr_family(&addr->addr) == AF_INET) { - if (!addr_ipv4 && conn->ipv4_traffic_ok) { + if (!addr_ipv4 && conn->entry_cfg.ipv4_traffic) { addr_ipv4 = addr; } } else if (tor_addr_family(&addr->addr) == AF_INET6) { - if (!addr_ipv6 && conn->ipv6_traffic_ok) { + if (!addr_ipv6 && conn->entry_cfg.ipv6_traffic) { addr_ipv6 = addr; } } @@ -1180,7 +1187,7 @@ connection_ap_handshake_socks_got_resolved_cell(entry_connection_t *conn, return; } - if (conn->prefer_ipv6_traffic) { + if (conn->entry_cfg.prefer_ipv6) { addr_best = addr_ipv6 ? addr_ipv6 : addr_ipv4; } else { addr_best = addr_ipv4 ? addr_ipv4 : addr_ipv6; @@ -1304,8 +1311,12 @@ connection_edge_process_relay_cell_not_open( "Got 'connected' while not in state connect_wait. Dropping."); return 0; } + CONNECTION_AP_EXPECT_NONPENDING(entry_conn); conn->base_.state = AP_CONN_STATE_OPEN; - log_info(LD_APP,"'connected' received after %d seconds.", + log_info(LD_APP,"'connected' received for circid %u streamid %d " + "after %d seconds.", + (unsigned)circ->n_circ_id, + rh->stream_id, (int)(time(NULL) - conn->base_.timestamp_lastread)); if (connected_cell_parse(rh, cell, &addr, &ttl) < 0) { log_fn(LOG_PROTOCOL_WARN, LD_APP, @@ -1326,8 +1337,8 @@ connection_edge_process_relay_cell_not_open( return 0; } - if ((family == AF_INET && ! entry_conn->ipv4_traffic_ok) || - (family == AF_INET6 && ! entry_conn->ipv6_traffic_ok)) { + if ((family == AF_INET && ! entry_conn->entry_cfg.ipv4_traffic) || + (family == AF_INET6 && ! entry_conn->entry_cfg.ipv6_traffic)) { log_fn(LOG_PROTOCOL_WARN, LD_APP, "Got a connected cell to %s with unsupported address family." " Closing.", fmt_addr(&addr)); @@ -1375,7 +1386,7 @@ connection_edge_process_relay_cell_not_open( /* This is definitely a success, so forget about any pending data we * had sent. */ if (entry_conn->pending_optimistic_data) { - generic_buffer_free(entry_conn->pending_optimistic_data); + buf_free(entry_conn->pending_optimistic_data); entry_conn->pending_optimistic_data = NULL; } @@ -1644,8 +1655,9 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ, } if ((reason = circuit_finish_handshake(TO_ORIGIN_CIRCUIT(circ), &extended_cell.created_cell)) < 0) { - log_warn(domain,"circuit_finish_handshake failed."); - return reason; + circuit_mark_for_close(circ, -reason); + return 0; /* We don't want to cause a warning, so we mark the circuit + * here. */ } } if ((reason=circuit_send_next_onion_skin(TO_ORIGIN_CIRCUIT(circ)))<0) { @@ -1698,7 +1710,9 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ, return -END_CIRC_REASON_TORPROTOCOL; } log_info(domain, - "'connected' received, no conn attached anymore. Ignoring."); + "'connected' received on circid %u for streamid %d, " + "no conn attached anymore. Ignoring.", + (unsigned)circ->n_circ_id, rh.stream_id); return 0; case RELAY_COMMAND_SENDME: if (!rh.stream_id) { @@ -1875,7 +1889,7 @@ connection_edge_package_raw_inbuf(edge_connection_t *conn, int package_partial, entry_conn->sending_optimistic_data != NULL; if (PREDICT_UNLIKELY(sending_from_optimistic)) { - bytes_to_process = generic_buffer_len(entry_conn->sending_optimistic_data); + bytes_to_process = buf_datalen(entry_conn->sending_optimistic_data); if (PREDICT_UNLIKELY(!bytes_to_process)) { log_warn(LD_BUG, "sending_optimistic_data was non-NULL but empty"); bytes_to_process = connection_get_inbuf_len(TO_CONN(conn)); @@ -1903,9 +1917,9 @@ connection_edge_package_raw_inbuf(edge_connection_t *conn, int package_partial, /* XXXX We could be more efficient here by sometimes packing * previously-sent optimistic data in the same cell with data * from the inbuf. */ - generic_buffer_get(entry_conn->sending_optimistic_data, payload, length); - if (!generic_buffer_len(entry_conn->sending_optimistic_data)) { - generic_buffer_free(entry_conn->sending_optimistic_data); + fetch_from_buf(payload, length, entry_conn->sending_optimistic_data); + if (!buf_datalen(entry_conn->sending_optimistic_data)) { + buf_free(entry_conn->sending_optimistic_data); entry_conn->sending_optimistic_data = NULL; } } else { @@ -1920,8 +1934,8 @@ connection_edge_package_raw_inbuf(edge_connection_t *conn, int package_partial, /* This is new optimistic data; remember it in case we need to detach and retry */ if (!entry_conn->pending_optimistic_data) - entry_conn->pending_optimistic_data = generic_buffer_new(); - generic_buffer_add(entry_conn->pending_optimistic_data, payload, length); + entry_conn->pending_optimistic_data = buf_new(); + write_to_buf(payload, length, entry_conn->pending_optimistic_data); } if (connection_edge_send_command(conn, RELAY_COMMAND_DATA, @@ -2249,62 +2263,12 @@ circuit_consider_sending_sendme(circuit_t *circ, crypt_path_t *layer_hint) /** The total number of cells we have allocated. */ static size_t total_cells_allocated = 0; -#ifdef ENABLE_MEMPOOLS -/** A memory pool to allocate packed_cell_t objects. */ -static mp_pool_t *cell_pool = NULL; - -/** Allocate structures to hold cells. */ -void -init_cell_pool(void) -{ - tor_assert(!cell_pool); - cell_pool = mp_pool_new(sizeof(packed_cell_t), 128*1024); -} - -/** Free all storage used to hold cells (and insertion times/commands if we - * measure cell statistics and/or if CELL_STATS events are enabled). */ -void -free_cell_pool(void) -{ - /* Maybe we haven't called init_cell_pool yet; need to check for it. */ - if (cell_pool) { - mp_pool_destroy(cell_pool); - cell_pool = NULL; - } -} - -/** Free excess storage in cell pool. */ -void -clean_cell_pool(void) -{ - tor_assert(cell_pool); - mp_pool_clean(cell_pool, 0, 1); -} - -#define relay_alloc_cell() \ - mp_pool_get(cell_pool) -#define relay_free_cell(cell) \ - mp_pool_release(cell) - -#define RELAY_CELL_MEM_COST (sizeof(packed_cell_t) + MP_POOL_ITEM_OVERHEAD) - -#else /* !ENABLE_MEMPOOLS case */ - -#define relay_alloc_cell() \ - tor_malloc_zero(sizeof(packed_cell_t)) -#define relay_free_cell(cell) \ - tor_free(cell) - -#define RELAY_CELL_MEM_COST (sizeof(packed_cell_t)) - -#endif /* ENABLE_MEMPOOLS */ - /** Release storage held by <b>cell</b>. */ -static INLINE void +static inline void packed_cell_free_unchecked(packed_cell_t *cell) { --total_cells_allocated; - relay_free_cell(cell); + tor_free(cell); } /** Allocate and return a new packed_cell_t. */ @@ -2312,7 +2276,7 @@ STATIC packed_cell_t * packed_cell_new(void) { ++total_cells_allocated; - return relay_alloc_cell(); + return tor_malloc_zero(sizeof(packed_cell_t)); } /** Return a packed cell used outside by channel_t lower layer */ @@ -2329,25 +2293,22 @@ packed_cell_free(packed_cell_t *cell) void dump_cell_pool_usage(int severity) { - circuit_t *c; int n_circs = 0; int n_cells = 0; - TOR_LIST_FOREACH(c, circuit_get_global_list(), head) { + SMARTLIST_FOREACH_BEGIN(circuit_get_global_list(), circuit_t *, c) { n_cells += c->n_chan_cells.n; if (!CIRCUIT_IS_ORIGIN(c)) n_cells += TO_OR_CIRCUIT(c)->p_chan_cells.n; ++n_circs; } + SMARTLIST_FOREACH_END(c); tor_log(severity, LD_MM, "%d cells allocated on %d circuits. %d cells leaked.", n_cells, n_circs, (int)total_cells_allocated - n_cells); -#ifdef ENABLE_MEMPOOLS - mp_pool_log_status(cell_pool, severity); -#endif } /** Allocate a new copy of packed <b>cell</b>. */ -static INLINE packed_cell_t * +static inline packed_cell_t * packed_cell_copy(const cell_t *cell, int wide_circ_ids) { packed_cell_t *c = packed_cell_new(); @@ -2372,14 +2333,12 @@ cell_queue_append_packed_copy(circuit_t *circ, cell_queue_t *queue, int exitward, const cell_t *cell, int wide_circ_ids, int use_stats) { - struct timeval now; packed_cell_t *copy = packed_cell_copy(cell, wide_circ_ids); (void)circ; (void)exitward; (void)use_stats; - tor_gettimeofday_cached_monotonic(&now); - copy->inserted_time = (uint32_t)tv_to_msec(&now); + copy->inserted_time = (uint32_t) monotime_coarse_absolute_msec(); cell_queue_append(queue, copy); } @@ -2458,14 +2417,11 @@ destroy_cell_queue_append(destroy_cell_queue_t *queue, circid_t circid, uint8_t reason) { - struct timeval now; - destroy_cell_t *cell = tor_malloc_zero(sizeof(destroy_cell_t)); cell->circid = circid; cell->reason = reason; - tor_gettimeofday_cached_monotonic(&now); /* Not yet used, but will be required for OOM handling. */ - cell->inserted_time = (uint32_t)tv_to_msec(&now); + cell->inserted_time = (uint32_t) monotime_coarse_absolute_msec(); TOR_SIMPLEQ_INSERT_TAIL(&queue->head, cell, next); ++queue->n; @@ -2492,16 +2448,22 @@ destroy_cell_to_packed_cell(destroy_cell_t *inp, int wide_circ_ids) size_t packed_cell_mem_cost(void) { - return RELAY_CELL_MEM_COST; + return sizeof(packed_cell_t); } -/** DOCDOC */ +/* DOCDOC */ STATIC size_t cell_queues_get_total_allocation(void) { return total_cells_allocated * packed_cell_mem_cost(); } +/** How long after we've been low on memory should we try to conserve it? */ +#define MEMORY_PRESSURE_INTERVAL (30*60) + +/** The time at which we were last low on memory. */ +static time_t last_time_under_memory_pressure = 0; + /** Check whether we've got too much space used for cells. If so, * call the OOM handler and return 1. Otherwise, return 0. */ STATIC int @@ -2509,13 +2471,38 @@ cell_queues_check_size(void) { size_t alloc = cell_queues_get_total_allocation(); alloc += buf_get_total_allocation(); - if (alloc >= get_options()->MaxMemInQueues) { - circuits_handle_oom(alloc); - return 1; + alloc += tor_zlib_get_total_allocation(); + const size_t rend_cache_total = rend_cache_get_total_allocation(); + alloc += rend_cache_total; + if (alloc >= get_options()->MaxMemInQueues_low_threshold) { + last_time_under_memory_pressure = approx_time(); + if (alloc >= get_options()->MaxMemInQueues) { + /* If we're spending over 20% of the memory limit on hidden service + * descriptors, free them until we're down to 10%. + */ + if (rend_cache_total > get_options()->MaxMemInQueues / 5) { + const size_t bytes_to_remove = + rend_cache_total - (size_t)(get_options()->MaxMemInQueues / 10); + rend_cache_clean_v2_descs_as_dir(time(NULL), bytes_to_remove); + alloc -= rend_cache_total; + alloc += rend_cache_get_total_allocation(); + } + circuits_handle_oom(alloc); + return 1; + } } return 0; } +/** Return true if we've been under memory pressure in the last + * MEMORY_PRESSURE_INTERVAL seconds. */ +int +have_been_under_memory_pressure(void) +{ + return last_time_under_memory_pressure + MEMORY_PRESSURE_INTERVAL + < approx_time(); +} + /** * Update the number of cells available on the circuit's n_chan or p_chan's * circuit mux. @@ -2546,7 +2533,7 @@ update_circuit_on_cmux_(circuit_t *circ, cell_direction_t direction, /* Cmux sanity check */ if (! circuitmux_is_circuit_attached(cmux, circ)) { - log_warn(LD_BUG, "called on non-attachd circuit from %s:%d", + log_warn(LD_BUG, "called on non-attached circuit from %s:%d", file, lineno); return; } @@ -2615,7 +2602,7 @@ set_streams_blocked_on_circ(circuit_t *circ, channel_t *chan, edge->edge_blocked_on_circ = block; } - if (!conn->read_event && !HAS_BUFFEREVENT(conn)) { + if (!conn->read_event) { /* This connection is a placeholder for something; probably a DNS * request. It can't actually stop or start reading.*/ continue; @@ -2660,8 +2647,8 @@ packed_cell_get_circid(const packed_cell_t *cell, int wide_circ_ids) * queue of the first active circuit on <b>chan</b>, and write them to * <b>chan</b>->outbuf. Return the number of cells written. Advance * the active circuit pointer to the next active circuit in the ring. */ -int -channel_flush_from_first_active_circuit(channel_t *chan, int max) +MOCK_IMPL(int, +channel_flush_from_first_active_circuit, (channel_t *chan, int max)) { circuitmux_t *cmux = NULL; int n_flushed = 0; @@ -2714,6 +2701,15 @@ channel_flush_from_first_active_circuit(channel_t *chan, int max) } /* Circuitmux told us this was active, so it should have cells */ + if (/*BUG(*/ queue->n == 0 /*)*/) { + log_warn(LD_BUG, "Found a supposedly active circuit with no cells " + "to send. Trying to recover."); + circuitmux_set_num_cells(cmux, circ, 0); + if (! circ->marked_for_close) + circuit_mark_for_close(circ, END_CIRC_REASON_INTERNAL); + continue; + } + tor_assert(queue->n > 0); /* @@ -2727,9 +2723,8 @@ channel_flush_from_first_active_circuit(channel_t *chan, int max) if (get_options()->CellStatistics || get_options()->TestingEnableCellStatsEvent) { uint32_t msec_waiting; - struct timeval tvnow; - tor_gettimeofday_cached(&tvnow); - msec_waiting = ((uint32_t)tv_to_msec(&tvnow)) - cell->inserted_time; + uint32_t msec_now = (uint32_t)monotime_coarse_absolute_msec(); + msec_waiting = msec_now - cell->inserted_time; if (get_options()->CellStatistics && !CIRCUIT_IS_ORIGIN(circ)) { or_circ = TO_OR_CIRCUIT(circ); @@ -2946,14 +2941,8 @@ append_cell_to_circuit_queue(circuit_t *circ, channel_t *chan, log_debug(LD_GENERAL, "Made a circuit active."); } - if (!channel_has_queued_writes(chan)) { - /* There is no data at all waiting to be sent on the outbuf. Add a - * cell, so that we can notice when it gets flushed, flushed_some can - * get called, and we can start putting more data onto the buffer then. - */ - log_debug(LD_GENERAL, "Primed a buffer."); - channel_flush_from_first_active_circuit(chan, 1); - } + /* New way: mark this as having waiting cells for the scheduler */ + scheduler_channel_has_waiting_cells(chan); } /** Append an encoded value of <b>addr</b> to <b>payload_out</b>, which must |