From 838743654c1bed2bfe22789ff53a1993c005f176 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Mon, 30 Jul 2012 21:49:39 -0700 Subject: Add channel.c/channel.h for bug 6465 Note: this is a squashed commit; see branch bug6465_rebased_v2 of user/andrea/tor.git for full history of the following 90 commits: Add channel.c/channel.h for bug 6465 Fix make check-spaces in new channel.c/channel.h Make sure new channel.h is in nodist_HEADERS and Makefile.nmake is up to date too Add channel_state_t and state utility functions Add channel_change_state() Better comments in channel.h Add CHANNEL_STATE_LISTENING for channel_t Fix wide line in channel.c Add structures/prototypes for incoming cell handling Implement channel_queue_cell() and channel_queue_var_cell() Implement channel_process_cells() Fix asserts in channel_queue_cell() and channel_queue_var_cell() Add descriptive comments for channel_queue_cell() and channel_queue_var_cell() Implement channel cell handler getters/setters Queue outgoing writes when not in writeable state Drain queues and test assertions when changing channel_t state Add log_debug() messages for channel_t stuff Add log_debug() messages for channel_t stuff Add some channel_t metadata Add time_t client_used to channel_t Add channel_touched_by_client() Declare a few channel_t metadata queries we'll have to implement later for use by circuitbuild.c Add next_circ_id/circ_id_type to channel_t for use by circuitbuild.c Count n_circuits in channel_t Channel timestamp calls Add create timestamp for channel.h Declare some new metadata queries on channel_t Add get_real_remote_descr() prototype Move active_circuits stuff to channel_t, some other or.h and channel.h changes Make channel_t refcounted and use global lists of active channels Update channel_request_close() and channel_change_state() for channel_t registration mechanism Handle closing channels sensibly Add global_identifier for channels, channel_init() internal use function Add timestamp_last_added_nonpadding to channel_t Better comments in channel_init() Correctly handle next_circ_id in channel_init() Correctly handle next_circ_id in channel_init() and even compile this time Appease make check-spaces Update timestamps when writing cells to channel_t Add channel_flush_some_cells() to call channel_flush_from_first_active_circuit() Add registered channel lookup functions Get rid of client_used in or_connection_t; it's in channel_t now Get rid of circ_id_type in or_connection_t; implement channel_set_circ_id_type() Eliminate is_bad_for_new_circs in or_connection_t; implement getter/setter for it in channel_t Eliminate next_circ_id in or_connection_t in favor of channel_t Handle packed cells in channel_t for relay.c Add channel_identity_map and related functions Handle add/remove from channel identity map on state transitions Implement channel_is_local() and channel_mark_local() Implement channel_is_client() and channel_mark_client() Implement channel_is_outgoing() and channel_mark_outgoing() Eliminate declaration for redundant channel_nonopen_was_started_here() Add channel timestamps Add channel timestamps, fix some make-check-spaces complaints Remove redundant channel_was_started_here() function and initiated_remotely bit Rename channel_get_remote_descr()/channel_get_real_remote_descr() to something clearer in channel.h Replace channel_get_write_queue_len() with sufficient and easier to implement channel_has_queued_writes() in channel.h Change return type of channel_is_bad_for_new_circs() to int for consistency Implement channel_has_queued_writes() Rename channel_touched_by_client() and client_used field for consistency with other timestamps in channel.{c,h} Implement channel_get_actual_remote_descr() and channel_get_canonical_remote_descr() in channel.{c,h} Implement channel_matches_extend_info() in channel.{c,h} Implement channel_get_for_extend() and channel_is_better() in channel.{c,h} Make channel_is_better() public in channel.{c,h} Implement channel_matches_target_addr_for_extend() in channel.{c,h} Implement channel_is_canonical_is_reliable() in channel.{c,h} Demoronize get_remote_descr() method prototype - what the hell was I thinking there? Timestamp channels in the right places in channel.c Add missing tor_assert() in channel.c Check if the lower layer accepted a cell in channel_write_cell() et al. of channel.c Implement channel_flush_cells() in channel.c (w00t, it builds at last) Call channel_timestamp_drained() at the right places in channel.c Implement channel_run_cleanup() Support optional channel_get_remote_addr() method and use it for GeoIP in channel_do_open_actions() Get rid of channel refcounting; it'll be too complicated to handle it properly with all the pointers from circuits to channels, and closing from channel_run_cleanup() will work okay just like with connections Doxygenate channel.c Appease make check-spaces in channel.c Fix superfluous semicolons in channel.c Add/remove channels from identity digest map in all the right places in channel.c The cell queues on channel_t must be empty when going to a CLOSED or ERROR state Appease make check-spaces in channel.c Add channel_clear/set_identity_digest() and some better logging to channel.{c,h} Fix better logging to channel.c Avoid SIGSEGV testing for queue emptiness in channel_flush_some_cells_from_outgoing_queue() Remove TODO about checking cell queue in channel_free(); no need for it Appease make check-spaces in channel.c Add channel_free_all() and support functions Check nullness of active_circuit_pqueue in channel_free() Fix SMARTLIST_FOREACH_END usage in channel_process_cells() Rearrange channel_t struct to use a union distinguishing listener from cell-bearing channels in channel.{c,h} --- src/common/torlog.h | 4 +- src/or/Makefile.nmake | 8 +- src/or/channel.c | 3420 +++++++++++++++++++++++++++++++++++++++++++++++++ src/or/channel.h | 399 ++++++ src/or/include.am | 2 + src/or/or.h | 128 +- 6 files changed, 3924 insertions(+), 37 deletions(-) create mode 100644 src/or/channel.c create mode 100644 src/or/channel.h (limited to 'src') diff --git a/src/common/torlog.h b/src/common/torlog.h index 28890a44af..fd34db1ac9 100644 --- a/src/common/torlog.h +++ b/src/common/torlog.h @@ -94,8 +94,10 @@ #define LD_HANDSHAKE (1u<<19) /** Heartbeat messages */ #define LD_HEARTBEAT (1u<<20) +/** Abstract channel_t code */ +#define LD_CHANNEL (1u<<21) /** Number of logging domains in the code. */ -#define N_LOGGING_DOMAINS 21 +#define N_LOGGING_DOMAINS 22 /** This log message is not safe to send to a callback-based logger * immediately. Used as a flag, not a log domain. */ diff --git a/src/or/Makefile.nmake b/src/or/Makefile.nmake index 3181e79c20..5915364b6f 100644 --- a/src/or/Makefile.nmake +++ b/src/or/Makefile.nmake @@ -8,10 +8,10 @@ LIBS = ..\..\..\build-alpha\lib\libevent.a \ ..\..\..\build-alpha\lib\libz.a \ ws2_32.lib advapi32.lib shell32.lib -LIBTOR_OBJECTS = buffers.obj circuitbuild.obj circuitlist.obj circuituse.obj \ - command.obj config.obj connection.obj connection_edge.obj \ - connection_or.obj control.obj cpuworker.obj directory.obj \ - dirserv.obj dirvote.obj dns.obj dnsserv.obj geoip.obj \ +LIBTOR_OBJECTS = buffers.obj channel.obj circuitbuild.obj \ + circuitlist.obj circuituse.obj command.obj config.obj connection.obj + connection_edge.obj connection_or.obj control.obj cpuworker.obj \ + directory.obj dirserv.obj dirvote.obj dns.obj dnsserv.obj geoip.obj \ hibernate.obj main.obj microdesc.obj networkstatus.obj \ nodelist.obj onion.obj policies.obj reasons.obj relay.obj \ rendclient.obj rendcommon.obj rendmid.obj rendservice.obj \ diff --git a/src/or/channel.c b/src/or/channel.c new file mode 100644 index 0000000000..0268fceebd --- /dev/null +++ b/src/or/channel.c @@ -0,0 +1,3420 @@ +/* * Copyright (c) 2012, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file channel.c + * \brief OR-to-OR channel abstraction layer + **/ + +/* + * Define this so channel.h gives us things only channel_t subclasses + * should touch. + */ + +#define _TOR_CHANNEL_INTERNAL + +#include "or.h" +#include "channel.h" +#include "channeltls.h" +#include "circuitbuild.h" +#include "circuitlist.h" +#include "geoip.h" +#include "nodelist.h" +#include "relay.h" +#include "rephist.h" +#include "router.h" +#include "routerlist.h" + +/* Cell queue structure */ + +typedef struct cell_queue_entry_s cell_queue_entry_t; +struct cell_queue_entry_s { + enum { + CELL_QUEUE_FIXED, + CELL_QUEUE_VAR, + CELL_QUEUE_PACKED + } type; + union { + struct { + cell_t *cell; + } fixed; + struct { + var_cell_t *var_cell; + } var; + struct { + packed_cell_t *packed_cell; + } packed; + } u; +}; + +/* Global lists of channels */ + +/* All channel_t instances */ +static smartlist_t *all_channels = NULL; + +/* All channel_t instances not in ERROR or CLOSED states */ +static smartlist_t *active_channels = NULL; + +/* All channel_t instances in LISTENING state */ +static smartlist_t *listening_channels = NULL; + +/* All channel_t instances in ERROR or CLOSED states */ +static smartlist_t *finished_channels = NULL; + +/* Counter for ID numbers */ +static uint64_t n_channels_allocated = 0; + +/* Digest->channel map + * + * Similar to the one used in connection_or.c, this maps from the identity + * digest of a remote endpoint to a channel_t to that endpoint. Channels + * should be placed here when registered and removed when they close or error. + * If more than one channel exists, follow the next_with_same_id pointer + * as a linked list. + */ +static digestmap_t *channel_identity_map = NULL; + +/* Functions to maintain the digest map */ +static void channel_add_to_digest_map(channel_t *chan); +static void channel_remove_from_digest_map(channel_t *chan); + +/* + * Flush cells from just the outgoing queue without trying to get them + * from circuits; used internall by channel_flush_some_cells(). + */ +static ssize_t +channel_flush_some_cells_from_outgoing_queue(channel_t *chan, + ssize_t num_cells); + +/*********************************** + * Channel state utility functions * + **********************************/ + +/** + * Indicate whether a given channel state is valid + * + * @param state A channel state + * @return A boolean value indicating whether state is a valid channel state + */ + +int +channel_state_is_valid(channel_state_t state) +{ + int is_valid; + + switch (state) { + case CHANNEL_STATE_CLOSED: + case CHANNEL_STATE_CLOSING: + case CHANNEL_STATE_ERROR: + case CHANNEL_STATE_LISTENING: + case CHANNEL_STATE_MAINT: + case CHANNEL_STATE_OPENING: + case CHANNEL_STATE_OPEN: + is_valid = 1; + break; + case CHANNEL_STATE_LAST: + default: + is_valid = 0; + } + + return is_valid; +} + +/** + * Indicate whether a channel state transition is valid + * + * This function takes two channel states and indicates whether a + * transition between them is permitted (see the state definitions and + * transition table in or.h at the channel_state_t typedef). + * + * @param from Proposed state to transition from + * @param to Proposed state to transition to + * @return A boolean value indicating whether the posposed transition is valid + */ + +int +channel_state_can_transition(channel_state_t from, channel_state_t to) +{ + int is_valid; + + switch (from) { + case CHANNEL_STATE_CLOSED: + is_valid = (to == CHANNEL_STATE_LISTENING || + to == CHANNEL_STATE_OPENING); + break; + case CHANNEL_STATE_CLOSING: + is_valid = (to == CHANNEL_STATE_CLOSED || + to == CHANNEL_STATE_ERROR); + break; + case CHANNEL_STATE_ERROR: + is_valid = 0; + break; + case CHANNEL_STATE_LISTENING: + is_valid = (to == CHANNEL_STATE_CLOSING || + to == CHANNEL_STATE_ERROR); + break; + case CHANNEL_STATE_MAINT: + is_valid = (to == CHANNEL_STATE_CLOSING || + to == CHANNEL_STATE_ERROR || + to == CHANNEL_STATE_OPEN); + break; + case CHANNEL_STATE_OPENING: + is_valid = (to == CHANNEL_STATE_CLOSING || + to == CHANNEL_STATE_ERROR || + to == CHANNEL_STATE_OPEN); + break; + case CHANNEL_STATE_OPEN: + is_valid = (to == CHANNEL_STATE_CLOSING || + to == CHANNEL_STATE_ERROR || + to == CHANNEL_STATE_MAINT); + break; + case CHANNEL_STATE_LAST: + default: + is_valid = 0; + } + + return is_valid; +} + +/** + * Return a human-readable description for a channel state + * + * @param state A channel state + * @return A pointer to a string with a human-readable description of state + */ + +const char * +channel_state_to_string(channel_state_t state) +{ + const char *descr; + + switch (state) { + case CHANNEL_STATE_CLOSED: + descr = "closed"; + break; + case CHANNEL_STATE_CLOSING: + descr = "closing"; + break; + case CHANNEL_STATE_ERROR: + descr = "channel error"; + break; + case CHANNEL_STATE_LISTENING: + descr = "listening"; + break; + case CHANNEL_STATE_MAINT: + descr = "temporarily suspended for maintenance"; + break; + case CHANNEL_STATE_OPENING: + descr = "opening"; + break; + case CHANNEL_STATE_OPEN: + descr = "open"; + break; + case CHANNEL_STATE_LAST: + default: + descr = "unknown or invalid channel state"; + } + + return descr; +} + +/*************************************** + * Channel registration/unregistration * + ***************************************/ + +/** + * Register a channel + * + * This function registers a newly created channel in the global lists/maps + * of active channels. + * + * @param chan A pointer to an unregistered channel + */ + +void +channel_register(channel_t *chan) +{ + tor_assert(chan); + + /* No-op if already registered */ + if (chan->registered) return; + + if (chan->is_listener) { + log_debug(LD_CHANNEL, + "Registering listener channel %p (ID %lu) in state %s (%d)", + chan, chan->global_identifier, + channel_state_to_string(chan->state), chan->state); + } else { + log_debug(LD_CHANNEL, + "Registering cell channel %p (ID %lu) in state %s (%d) " + "with digest %s", + chan, chan->global_identifier, + channel_state_to_string(chan->state), chan->state, + hex_str(chan->u.cell_chan.identity_digest, DIGEST_LEN)); + } + + /* Make sure we have all_channels, then add it */ + if (!all_channels) all_channels = smartlist_new(); + smartlist_add(all_channels, chan); + + /* Is it finished? */ + if (chan->state == CHANNEL_STATE_CLOSED || + chan->state == CHANNEL_STATE_ERROR) { + /* Put it in the finished list, creating it if necessary */ + if (!finished_channels) finished_channels = smartlist_new(); + smartlist_add(finished_channels, chan); + } else { + /* Put it in the active list, creating it if necessary */ + if (!active_channels) active_channels = smartlist_new(); + smartlist_add(active_channels, chan); + + /* Is it a listener? */ + if (chan->is_listener && + chan->state == CHANNEL_STATE_LISTENING) { + /* Put it in the listening list, creating it if necessary */ + if (!listening_channels) listening_channels = smartlist_new(); + smartlist_add(listening_channels, chan); + } else if (chan->state != CHANNEL_STATE_CLOSING) { + if (!(chan->is_listener)) { + /* It should have a digest set */ + if (!tor_digest_is_zero(chan->u.cell_chan.identity_digest)) { + /* Yeah, we're good, add it to the map */ + channel_add_to_digest_map(chan); + } else { + log_info(LD_CHANNEL, + "Channel %p (global ID %lu) in state %s (%d) registered " + "with no identity digest", + chan, chan->global_identifier, + channel_state_to_string(chan->state), chan->state); + } + } + } + } + + /* Mark it as registered */ + chan->registered = 1; +} + +/** + * Unregister a channel + * + * This function removes a channel from the global lists and maps and is used + * when freeing a closed/errored channel. + * + * @param chan A pointer to a channel to be unregistered + */ + +void +channel_unregister(channel_t *chan) +{ + tor_assert(chan); + + /* No-op if not registered */ + if (!(chan->registered)) return; + + /* Is it finished? */ + if (chan->state == CHANNEL_STATE_CLOSED || + chan->state == CHANNEL_STATE_ERROR) { + /* Get it out of the finished list */ + if (finished_channels) smartlist_remove(finished_channels, chan); + } else { + /* Get it out of the active list */ + if (active_channels) smartlist_remove(active_channels, chan); + + /* Is it listening? */ + if (chan->state == CHANNEL_STATE_LISTENING) { + /* Get it out of the listening list */ + if (listening_channels) smartlist_remove(listening_channels, chan); + } + } + + /* Get it out of all_channels */ + if (all_channels) smartlist_remove(all_channels, chan); + + /* Mark it as unregistered */ + chan->registered = 0; + + if (!(chan->is_listener)) { + /* Should it be in the digest map? */ + if (!tor_digest_is_zero(chan->u.cell_chan.identity_digest) && + !(chan->state == CHANNEL_STATE_LISTENING || + chan->state == CHANNEL_STATE_CLOSING || + chan->state == CHANNEL_STATE_CLOSED || + chan->state == CHANNEL_STATE_ERROR)) { + /* Remove it */ + channel_remove_from_digest_map(chan); + } + } +} + +/********************************* + * Channel digest map maintenance + *********************************/ + +/** + * Add a channel to the digest map + * + * This function adds a channel to the digest map and inserts it into the + * correct linked list if channels with that remote endpoint identity digest + * already exist. + * + * @param chan A pointer to a channel to add + */ + +static void +channel_add_to_digest_map(channel_t *chan) +{ + channel_t *tmp; + + tor_assert(chan); + tor_assert(!(chan->is_listener)); + + /* Assert that the state makes sense */ + tor_assert(!(chan->state == CHANNEL_STATE_LISTENING || + chan->state == CHANNEL_STATE_CLOSING || + chan->state == CHANNEL_STATE_CLOSED || + chan->state == CHANNEL_STATE_ERROR)); + + /* Assert that there is a digest */ + tor_assert(!tor_digest_is_zero(chan->u.cell_chan.identity_digest)); + + /* Allocate the identity map if we have to */ + if (!channel_identity_map) channel_identity_map = digestmap_new(); + + /* Insert it */ + tmp = digestmap_set(channel_identity_map, + chan->u.cell_chan.identity_digest, + chan); + if (tmp) { + tor_assert(!(tmp->is_listener)); + /* There already was one, this goes at the head of the list */ + chan->u.cell_chan.next_with_same_id = tmp; + chan->u.cell_chan.prev_with_same_id = NULL; + tmp->u.cell_chan.prev_with_same_id = chan; + } else { + /* First with this digest */ + chan->u.cell_chan.next_with_same_id = NULL; + chan->u.cell_chan.prev_with_same_id = NULL; + } + + log_debug(LD_CHANNEL, + "Added channel %p (%lu) to identity map in state %s (%d) " + "with digest %s", + chan, chan->global_identifier, + channel_state_to_string(chan->state), chan->state, + hex_str(chan->u.cell_chan.identity_digest, DIGEST_LEN)); +} + +/** + * Remove a channel from the digest map + * + * This function removes a channel from the digest map and the linked list of + * channels for that digest if more than one exists. + * + * @param chan A pointer to a channel to remove + */ + +static void +channel_remove_from_digest_map(channel_t *chan) +{ + channel_t *tmp, *head; + + tor_assert(chan); + tor_assert(!(chan->is_listener)); + + /* Assert that there is a digest */ + tor_assert(!tor_digest_is_zero(chan->u.cell_chan.identity_digest)); + + /* Make sure we have a map */ + if (!channel_identity_map) { + /* + * No identity map, so we can't find it by definition. This + * case is similar to digestmap_get() failing below. + */ + log_warn(LD_BUG, + "Trying to remove channel %p (%lu) with digest %s from " + "identity map, but didn't have any identity map", + chan, chan->global_identifier, + hex_str(chan->u.cell_chan.identity_digest, DIGEST_LEN)); + /* Clear out its next/prev pointers */ + if (chan->u.cell_chan.next_with_same_id) { + tor_assert(!(chan->u.cell_chan.next_with_same_id->is_listener)); + chan->u.cell_chan.next_with_same_id->u.cell_chan.prev_with_same_id + = chan->u.cell_chan.prev_with_same_id; + } + if (chan->u.cell_chan.prev_with_same_id) { + tor_assert(!(chan->u.cell_chan.prev_with_same_id->is_listener)); + chan->u.cell_chan.prev_with_same_id->u.cell_chan.next_with_same_id + = chan->u.cell_chan.next_with_same_id; + } + chan->u.cell_chan.next_with_same_id = NULL; + chan->u.cell_chan.prev_with_same_id = NULL; + + return; + } + + /* Look for it in the map */ + tmp = digestmap_get(channel_identity_map, chan->u.cell_chan.identity_digest); + if (tmp) { + /* Okay, it's here */ + head = tmp; /* Keep track of list head */ + /* Look for this channel */ + while (tmp && tmp != chan) { + tor_assert(!(tmp->is_listener)); + tmp = tmp->u.cell_chan.next_with_same_id; + } + + if (tmp == chan) { + /* Found it, good */ + if (chan->u.cell_chan.next_with_same_id) { + tor_assert(!(chan->u.cell_chan.next_with_same_id->is_listener)); + chan->u.cell_chan.next_with_same_id->u.cell_chan.prev_with_same_id + = chan->u.cell_chan.prev_with_same_id; + } + /* else we're the tail of the list */ + if (chan->u.cell_chan.prev_with_same_id) { + tor_assert(!(chan->u.cell_chan.prev_with_same_id->is_listener)); + /* We're not the head of the list, so we can *just* unlink */ + chan->u.cell_chan.prev_with_same_id->u.cell_chan.next_with_same_id + = chan->u.cell_chan.next_with_same_id; + } else { + /* We're the head, so we have to point the digest map entry at our + * next if we have one, or remove it if we're also the tail */ + if (chan->u.cell_chan.next_with_same_id) { + tor_assert(!(chan->u.cell_chan.next_with_same_id->is_listener)); + digestmap_set(channel_identity_map, + chan->u.cell_chan.identity_digest, + chan->u.cell_chan.next_with_same_id); + } else { + digestmap_remove(channel_identity_map, + chan->u.cell_chan.identity_digest); + } + } + + /* NULL out its next/prev pointers, and we're finished */ + chan->u.cell_chan.next_with_same_id = NULL; + chan->u.cell_chan.prev_with_same_id = NULL; + + log_debug(LD_CHANNEL, + "Removed channel %p (%lu) from identity map in state %s (%d) " + "with digest %s", + chan, chan->global_identifier, + channel_state_to_string(chan->state), chan->state, + hex_str(chan->u.cell_chan.identity_digest, DIGEST_LEN)); + } else { + /* This is not good */ + log_warn(LD_BUG, + "Trying to remove channel %p (%lu) with digest %s from " + "identity map, but couldn't find it in the list for that " + "digest", + chan, chan->global_identifier, + hex_str(chan->u.cell_chan.identity_digest, DIGEST_LEN)); + /* Unlink it and hope for the best */ + if (chan->u.cell_chan.next_with_same_id) { + tor_assert(!(chan->u.cell_chan.next_with_same_id->is_listener)); + chan->u.cell_chan.next_with_same_id->u.cell_chan.prev_with_same_id + = chan->u.cell_chan.prev_with_same_id; + } + if (chan->u.cell_chan.prev_with_same_id) { + tor_assert(!(chan->u.cell_chan.prev_with_same_id->is_listener)); + chan->u.cell_chan.prev_with_same_id->u.cell_chan.next_with_same_id + = chan->u.cell_chan.next_with_same_id; + } + chan->u.cell_chan.next_with_same_id = NULL; + chan->u.cell_chan.prev_with_same_id = NULL; + } + } else { + /* Shouldn't happen */ + log_warn(LD_BUG, + "Trying to remove channel %p (%lu) with digest %s from " + "identity map, but couldn't find any with that digest", + chan, chan->global_identifier, + hex_str(chan->u.cell_chan.identity_digest, DIGEST_LEN)); + /* Clear out its next/prev pointers */ + if (chan->u.cell_chan.next_with_same_id) { + tor_assert(!(chan->u.cell_chan.next_with_same_id->is_listener)); + chan->u.cell_chan.next_with_same_id->u.cell_chan.prev_with_same_id + = chan->u.cell_chan.prev_with_same_id; + } + if (chan->u.cell_chan.prev_with_same_id) { + chan->u.cell_chan.prev_with_same_id->u.cell_chan.next_with_same_id + = chan->u.cell_chan.next_with_same_id; + } + chan->u.cell_chan.next_with_same_id = NULL; + chan->u.cell_chan.prev_with_same_id = NULL; + } +} + +/**************************** + * Channel lookup functions * + ***************************/ + +/** + * Find channel by global ID + * + * This function searches for a channel by the global_identifier assigned + * at initialization time. This identifier is unique for the lifetime of the + * Tor process. + * + * @param global_identifier The global_identifier value to search for + * @return A pointer to the channel with that global identifier, or NULL if + * none exists. + */ + +channel_t * +channel_find_by_global_id(uint64_t global_identifier) +{ + channel_t *rv = NULL; + + if (all_channels && smartlist_len(all_channels) > 0) { + SMARTLIST_FOREACH_BEGIN(all_channels, channel_t *, curr) { + if (curr->global_identifier == global_identifier) { + rv = curr; + break; + } + } SMARTLIST_FOREACH_END(curr); + } + + return rv; +} + +/** + * Find channel by digest of the remote endpoint + * + * This function looks up a channel by the digest of its remote endpoint in + * the channel digest map. It's possible that more than one channel to a + * given endpoint exists. Use channel_next_with_digest() and + * channel_prev_with_digest() to walk the list. + * + * @param identity_digest A digest to search for + * @return A channel pointer, or NULL if no channel to this endpoint exists. + */ + +channel_t * +channel_find_by_remote_digest(const char *identity_digest) +{ + channel_t *rv = NULL, *tmp; + + tor_assert(identity_digest); + + /* Search for it in the identity map */ + if (channel_identity_map) { + tmp = digestmap_get(channel_identity_map, identity_digest); + rv = tmp; + } + + return rv; +} + +/** + * Find channel by remote nickname + * + * This function looks up a channel by the nickname of the remote + * endpoint. It's possible that more than one channel to that endpoint + * nickname exists, but there is not currently any supported way to iterate + * them. Use digests. + * + * @param nickname A node nickname + * @return A channel pointer to a channel to a node with that nickname, or + * NULL if none is available. + */ + +channel_t * +channel_find_by_remote_nickname(const char *nickname) +{ + channel_t *rv = NULL; + + tor_assert(nickname); + + if (all_channels && smartlist_len(all_channels) > 0) { + SMARTLIST_FOREACH_BEGIN(all_channels, channel_t *, curr) { + if (!(curr->is_listener)) { + if (curr->u.cell_chan.nickname && + strncmp(curr->u.cell_chan.nickname, nickname, + MAX_NICKNAME_LEN) == 0) { + rv = curr; + break; + } + } + } SMARTLIST_FOREACH_END(curr); + } + + return rv; +} + +/** + * Next channel with digest + * + * This function takes a channel and finds the next channel in the list + * with the same digest. + * + * @param chan Channel pointer to iterate + * @return A pointer to the next channel after chan with the same remote + * endpoint identity digest, or NULL if none exists. + */ + +channel_t * +channel_next_with_digest(channel_t *chan) +{ + channel_t *rv = NULL; + + tor_assert(chan); + tor_assert(!(chan->is_listener)); + + if (chan->u.cell_chan.next_with_same_id) + rv = chan->u.cell_chan.next_with_same_id; + + return rv; +} + +/** + * Previous channel with digest + * + * This function takes a channel and finds the previos channel in the list + * with the same digest. + * + * @param chan Channel pointer to iterate + * @return A pointer to the previous channel after chan with the same remote + * endpoint identity digest, or NULL if none exists. + */ + +channel_t * +channel_prev_with_digest(channel_t *chan) +{ + channel_t *rv = NULL; + + tor_assert(chan); + tor_assert(!(chan->is_listener)); + + if (chan->u.cell_chan.prev_with_same_id) + rv = chan->u.cell_chan.prev_with_same_id; + + return rv; +} + +/** + * Internal-only channel init function for cell channels + * + * This function should be called by subclasses to set up some per-channel + * variables. I.e., this is the superclass constructor. Before this, the + * channel should be allocated with tor_malloc_zero(). + * + * @param chan Pointer to a channel to initialize. + */ + +void +channel_init_for_cells(channel_t *chan) +{ + tor_assert(chan); + + /* Assign an ID and bump the counter */ + chan->global_identifier = n_channels_allocated++; + + /* Mark as a non-listener */ + chan->is_listener = 0; + + /* Init timestamp */ + chan->u.cell_chan.timestamp_last_added_nonpadding = time(NULL); + + /* Init next_circ_id */ + chan->u.cell_chan.next_circ_id = crypto_rand_int(1 << 15); + + /* Timestamp it */ + channel_timestamp_created(chan); +} + +/** + * Internal-only channel init function for listener channels + * + * This function should be called by subclasses to set up some per-channel + * variables. I.e., this is the superclass constructor. Before this, the + * channel should be allocated with tor_malloc_zero(). + * + * @param chan Pointer to a channel to initialize. + */ + +void +channel_init_listener(channel_t *chan) +{ + tor_assert(chan); + + /* Assign an ID and bump the counter */ + chan->global_identifier = n_channels_allocated++; + + /* Mark as a listener */ + chan->is_listener = 1; + + /* Timestamp it */ + channel_timestamp_created(chan); +} + +/** + * Internal-only channel free function + * + * Nothing outside of channel.c should call this; it frees channels after + * they have closed and been unregistered. + * + * @param chan Channel to free + */ + +void +channel_free(channel_t *chan) +{ + tor_assert(chan); + /* It must be closed or errored */ + tor_assert(chan->state == CHANNEL_STATE_CLOSED || + chan->state == CHANNEL_STATE_ERROR); + /* It must be deregistered */ + tor_assert(!(chan->registered)); + + /* Call a free method if there is one */ + if (chan->free) chan->free(chan); + + if (!(chan->is_listener)) { + channel_clear_remote_end(chan); + + if (chan->u.cell_chan.active_circuit_pqueue) { + smartlist_free(chan->u.cell_chan.active_circuit_pqueue); + chan->u.cell_chan.active_circuit_pqueue = NULL; + } + } + + /* We're in CLOSED or ERROR, so the cell queue is already empty */ + + tor_free(chan); +} + +/** + * Internal-only forcible channel free function + * + * This is like channel_free, but doesn't do the state/registration asserts; + * it should only be used from channel_free_all() when shutting down. + */ + +void +channel_force_free(channel_t *chan) +{ + cell_queue_entry_t *tmp = NULL; + channel_t *tmpchan = NULL; + + tor_assert(chan); + + /* Call a free method if there is one */ + if (chan->free) chan->free(chan); + + if (chan->is_listener) { + /* + * The incoming list just gets emptied and freed; we request close on + * any channels we find there, but since we got called while shutting + * down they will get deregistered and freed elsewhere anyway. + */ + if (chan->u.listener.incoming_list) { + SMARTLIST_FOREACH_BEGIN(chan->u.listener.incoming_list, + channel_t *, qchan) { + tmpchan = qchan; + SMARTLIST_DEL_CURRENT(chan->u.listener.incoming_list, qchan); + channel_request_close(tmpchan); + } SMARTLIST_FOREACH_END(qchan); + + smartlist_free(chan->u.listener.incoming_list); + chan->u.listener.incoming_list = NULL; + } + } else { + channel_clear_remote_end(chan); + smartlist_free(chan->u.cell_chan.active_circuit_pqueue); + + /* We might still have a cell queue; kill it */ + if (chan->u.cell_chan.cell_queue) { + SMARTLIST_FOREACH_BEGIN(chan->u.cell_chan.cell_queue, + cell_queue_entry_t *, q) { + tmp = q; + SMARTLIST_DEL_CURRENT(chan->u.cell_chan.cell_queue, q); + tor_free(q); + } SMARTLIST_FOREACH_END(q); + + smartlist_free(chan->u.cell_chan.cell_queue); + chan->u.cell_chan.cell_queue = NULL; + } + + /* Outgoing cell queue is similar, but we can have to free packed cells */ + if (chan->u.cell_chan.outgoing_queue) { + SMARTLIST_FOREACH_BEGIN(chan->u.cell_chan.outgoing_queue, + cell_queue_entry_t *, q) { + tmp = q; + SMARTLIST_DEL_CURRENT(chan->u.cell_chan.outgoing_queue, q); + if (tmp->type == CELL_QUEUE_PACKED) { + if (tmp->u.packed.packed_cell) { + packed_cell_free(tmp->u.packed.packed_cell); + } + } + tor_free(tmp); + } SMARTLIST_FOREACH_END(q); + + smartlist_free(chan->u.cell_chan.outgoing_queue); + chan->u.cell_chan.outgoing_queue = NULL; + } + } + + tor_free(chan); +} + +/** + * Return the current registered listener for a channel + * + * This function returns a function pointer to the current registered + * handler for new incoming channels on a listener channel. + * + * @param chan Channel to get listener for + * @return Function pointer to an incoming channel handler + */ + +void +(* channel_get_listener(channel_t *chan)) + (channel_t *, channel_t *) +{ + tor_assert(chan); + tor_assert(chan->is_listener); + + if (chan->state == CHANNEL_STATE_LISTENING) + return chan->u.listener.listener; + + return NULL; +} + +/** + * Set the listener for a channel + * + * This function sets the handler for new incoming channels on a listener + * channel. + * + * @param chan Listener channel to set handler on + * @param listener Function pointer to new incoming channel handler + */ + +void +channel_set_listener(channel_t *chan, + void (*listener)(channel_t *, channel_t *) ) +{ + tor_assert(chan); + tor_assert(chan->is_listener); + tor_assert(chan->state == CHANNEL_STATE_LISTENING); + + log_debug(LD_CHANNEL, + "Setting listener callback for channel %p to %p", + chan, listener); + + chan->u.listener.listener = listener; + if (chan->u.listener.listener) channel_process_incoming(chan); +} + +/** + * Return the fixed-length cell handler for a channel + * + * This function gets the handler for incoming fixed-length cells installed + * on a channel. + * + * @param chan Channel to get the fixed-length cell handler for + * @return A function pointer to chan's fixed-length cell handler, if any. + */ + +void +(* channel_get_cell_handler(channel_t *chan)) + (channel_t *, cell_t *) +{ + tor_assert(chan); + tor_assert(!(chan->is_listener)); + + if (chan->state == CHANNEL_STATE_OPENING || + chan->state == CHANNEL_STATE_OPEN || + chan->state == CHANNEL_STATE_MAINT) + return chan->u.cell_chan.cell_handler; + + return NULL; +} + +/** + * Return the variable-length cell handler for a channel + * + * This function gets the handler for incoming variable-length cells + * installed on a channel. + * + * @param chan Channel to get the variable-length cell handler for + * @return A function pointer to chan's variable-length cell handler, if any. + */ + +void +(* channel_get_var_cell_handler(channel_t *chan)) + (channel_t *, var_cell_t *) +{ + tor_assert(chan); + tor_assert(!(chan->is_listener)); + + if (chan->state == CHANNEL_STATE_OPENING || + chan->state == CHANNEL_STATE_OPEN || + chan->state == CHANNEL_STATE_MAINT) + return chan->u.cell_chan.var_cell_handler; + + return NULL; +} + +/** + * Set the fixed-length cell handler for a channel + * + * This function sets the fixed-length cell handler for a channel and + * processes any incoming cells that had been blocked in the queue because + * none was available. + * + * @param chan Channel to set the fixed-length cell handler for + * @param cell_handler Function pointer to new fixed-length cell handler + */ + +void +channel_set_cell_handler(channel_t *chan, + void (*cell_handler)(channel_t *, cell_t *)) +{ + int changed = 0; + + tor_assert(chan); + tor_assert(!(chan->is_listener)); + tor_assert(chan->state == CHANNEL_STATE_OPENING || + chan->state == CHANNEL_STATE_OPEN || + chan->state == CHANNEL_STATE_MAINT); + + log_debug(LD_CHANNEL, + "Setting cell_handler callback for channel %p to %p", + chan, cell_handler); + + /* + * Keep track whether we've changed it so we know if there's any point in + * re-running the queue. + */ + if (cell_handler != chan->u.cell_chan.cell_handler) changed = 1; + + /* Change it */ + chan->u.cell_chan.cell_handler = cell_handler; + + /* Re-run the queue if we have one and there's any reason to */ + if (chan->u.cell_chan.cell_queue && + (smartlist_len(chan->u.cell_chan.cell_queue) > 0) && + changed && + chan->u.cell_chan.cell_handler) channel_process_cells(chan); +} + +/** + * Set the both cell handlers for a channel + * + * This function sets both the fixed-length and variable length cell handlers + * for a channel and processes any incoming cells that had been blocked in the + * queue because none were available. + * + * @param chan Channel to set the fixed-length cell handler for + * @param cell_handler Function pointer to new fixed-length cell handler + * @param var_cell_handler Function pointer to new variable-length cell + handler + */ + +void +channel_set_cell_handlers(channel_t *chan, + void (*cell_handler)(channel_t *, cell_t *), + void (*var_cell_handler)(channel_t *, + var_cell_t *)) +{ + int try_again = 0; + + tor_assert(chan); + tor_assert(!(chan->is_listener)); + tor_assert(chan->state == CHANNEL_STATE_OPENING || + chan->state == CHANNEL_STATE_OPEN || + chan->state == CHANNEL_STATE_MAINT); + + log_debug(LD_CHANNEL, + "Setting cell_handler callback for channel %p to %p", + chan, cell_handler); + log_debug(LD_CHANNEL, + "Setting var_cell_handler callback for channel %p to %p", + chan, var_cell_handler); + + /* Should we try the queue? */ + if (cell_handler && + cell_handler != chan->u.cell_chan.cell_handler) try_again = 1; + if (var_cell_handler && + var_cell_handler != chan->u.cell_chan.var_cell_handler) try_again = 1; + + /* Change them */ + chan->u.cell_chan.cell_handler = cell_handler; + chan->u.cell_chan.var_cell_handler = var_cell_handler; + + /* Re-run the queue if we have one and there's any reason to */ + if (chan->u.cell_chan.cell_queue && + (smartlist_len(chan->u.cell_chan.cell_queue) > 0) && + try_again && + (chan->u.cell_chan.cell_handler || + chan->u.cell_chan.var_cell_handler)) channel_process_cells(chan); +} + +/** + * Set the variable-length cell handler for a channel + * + * This function sets the variable-length cell handler for a channel and + * processes any incoming cells that had been blocked in the queue because + * none was available. + * + * @param chan Channel to set the variable-length cell handler for + * @param cell_handler Function pointer to new variable-length cell handler + */ + +void +channel_set_var_cell_handler(channel_t *chan, + void (*var_cell_handler)(channel_t *, + var_cell_t *)) +{ + int changed = 0; + + tor_assert(chan); + tor_assert(!(chan->is_listener)); + tor_assert(chan->state == CHANNEL_STATE_OPENING || + chan->state == CHANNEL_STATE_OPEN || + chan->state == CHANNEL_STATE_MAINT); + + log_debug(LD_CHANNEL, + "Setting var_cell_handler callback for channel %p to %p", + chan, var_cell_handler); + + /* + * Keep track whether we've changed it so we know if there's any point in + * re-running the queue. + */ + if (var_cell_handler != chan->u.cell_chan.var_cell_handler) changed = 1; + + /* Change it */ + chan->u.cell_chan.var_cell_handler = var_cell_handler; + + /* Re-run the queue if we have one and there's any reason to */ + if (chan->u.cell_chan.cell_queue && + (smartlist_len(chan->u.cell_chan.cell_queue) > 0) && + changed && chan->u.cell_chan.var_cell_handler) + channel_process_cells(chan); +} + +/** + * Request a channel be closed + * + * This function tries to close a channel_t; it will go into the CLOSING + * state, and eventually the lower layer should put it into the CLOSED or + * ERROR state. Then, channel_run_cleanup() will eventually free it. + * + * @param chan Channel to close + */ + +void +channel_request_close(channel_t *chan) +{ + tor_assert(chan != NULL); + tor_assert(chan->close != NULL); + + /* If it's already in CLOSING, CLOSED or ERROR, this is a no-op */ + if (chan->state == CHANNEL_STATE_CLOSING || + chan->state == CHANNEL_STATE_CLOSED || + chan->state == CHANNEL_STATE_ERROR) return; + + log_debug(LD_CHANNEL, + "Closing channel %p by request", + chan); + + /* Note closing by request from above */ + chan->reason_for_closing = CHANNEL_CLOSE_REQUESTED; + + /* Change state to CLOSING */ + channel_change_state(chan, CHANNEL_STATE_CLOSING); + + /* Tell the lower layer */ + chan->close(chan); + + /* + * It's up to the lower layer to change state to CLOSED or ERROR when we're + * ready; we'll try to free channels that are in the finished list from + * channel_run_cleanup(). The lower layer should do this by calling + * channel_closed(). + */ +} + +/** + * Close a channel from the lower layer + * + * Notify the channel code that the channel is being closed due to a non-error + * condition in the lower layer. This does not call the close() method, since + * the lower layer already knows. + * + * @param chan Channel to notify for close + */ + +void +channel_close_from_lower_layer(channel_t *chan) +{ + tor_assert(chan != NULL); + + /* If it's already in CLOSING, CLOSED or ERROR, this is a no-op */ + if (chan->state == CHANNEL_STATE_CLOSING || + chan->state == CHANNEL_STATE_CLOSED || + chan->state == CHANNEL_STATE_ERROR) return; + + log_debug(LD_CHANNEL, + "Closing channel %p due to lower-layer event", + chan); + + /* Note closing by event from below */ + chan->reason_for_closing = CHANNEL_CLOSE_FROM_BELOW; + + /* Change state to CLOSING */ + channel_change_state(chan, CHANNEL_STATE_CLOSING); +} + +/** + * Notify that the channel is being closed due to an error condition + * + * This function is called by the lower layer implementing the transport + * when a channel must be closed due to an error condition. This does not + * call the channel's close method, since the lower layer already knows. + * + * @param chan Channel to notify for error + */ + +void +channel_close_for_error(channel_t *chan) +{ + tor_assert(chan != NULL); + + /* If it's already in CLOSING, CLOSED or ERROR, this is a no-op */ + if (chan->state == CHANNEL_STATE_CLOSING || + chan->state == CHANNEL_STATE_CLOSED || + chan->state == CHANNEL_STATE_ERROR) return; + + log_debug(LD_CHANNEL, + "Closing channel %p due to lower-layer error", + chan); + + /* Note closing by event from below */ + chan->reason_for_closing = CHANNEL_CLOSE_FOR_ERROR; + + /* Change state to CLOSING */ + channel_change_state(chan, CHANNEL_STATE_CLOSING); +} + +/** + * Notify that the lower layer is finished closing the channel + * + * This function should be called by the lower layer when a channel + * is finished closing and it should be regarded as inactive and + * freed by the channel code. + * + * @param chan Channel to notify closure on + */ + +void +channel_closed(channel_t *chan) +{ + tor_assert(chan); + tor_assert(chan->state == CHANNEL_STATE_CLOSING || + chan->state == CHANNEL_STATE_CLOSED || + chan->state == CHANNEL_STATE_ERROR); + + /* No-op if already inactive */ + if (chan->state == CHANNEL_STATE_CLOSED || + chan->state == CHANNEL_STATE_ERROR) return; + + if (chan->reason_for_closing == CHANNEL_CLOSE_FOR_ERROR) { + /* Inform any pending (not attached) circs that they should + * give up. */ + circuit_n_chan_done(chan, 0); + } + /* Now close all the attached circuits on it. */ + circuit_unlink_all_from_channel(chan, END_CIRC_REASON_CHANNEL_CLOSED); + + if (chan->reason_for_closing != CHANNEL_CLOSE_FOR_ERROR) { + channel_change_state(chan, CHANNEL_STATE_CLOSED); + } else { + channel_change_state(chan, CHANNEL_STATE_ERROR); + } +} + +/** + * Clear the identity_digest of a channel + * + * This function clears the identity digest of the remote endpoint for a + * channel; this is intended for use by the lower layer. + * + * @param chan Channel to clear + */ + +void +channel_clear_identity_digest(channel_t *chan) +{ + int state_not_in_map; + + tor_assert(chan); + tor_assert(!(chan->is_listener)); + + log_debug(LD_CHANNEL, + "Clearing remote endpoint digest on channel %p with " + "global ID %lu", + chan, chan->global_identifier); + + state_not_in_map = + (chan->state == CHANNEL_STATE_LISTENING || + chan->state == CHANNEL_STATE_CLOSING || + chan->state == CHANNEL_STATE_CLOSED || + chan->state == CHANNEL_STATE_ERROR); + + if (!state_not_in_map && chan->registered && + !tor_digest_is_zero(chan->u.cell_chan.identity_digest)) + /* if it's registered get it out of the digest map */ + channel_remove_from_digest_map(chan); + + memset(chan->u.cell_chan.identity_digest, 0, + sizeof(chan->u.cell_chan.identity_digest)); +} + +/** + * Set the identity_digest of a channel + * + * This function sets the identity digest of the remote endpoint for a + * channel; this is intended for use by the lower layer. + * + * @param chan Channel to clear + * @param identity_digest New identity digest for chan + */ + +void +channel_set_identity_digest(channel_t *chan, + const char *identity_digest) +{ + int was_in_digest_map, should_be_in_digest_map, state_not_in_map; + + tor_assert(chan); + tor_assert(!(chan->is_listener)); + + log_debug(LD_CHANNEL, + "Setting remote endpoint digest on channel %p with " + "global ID %lu to digest %s", + chan, chan->global_identifier, + identity_digest ? + hex_str(identity_digest, DIGEST_LEN) : "(null)"); + + state_not_in_map = + (chan->state == CHANNEL_STATE_LISTENING || + chan->state == CHANNEL_STATE_CLOSING || + chan->state == CHANNEL_STATE_CLOSED || + chan->state == CHANNEL_STATE_ERROR); + was_in_digest_map = + !state_not_in_map && + chan->registered && + !tor_digest_is_zero(chan->u.cell_chan.identity_digest); + should_be_in_digest_map = + !state_not_in_map && + chan->registered && + (identity_digest && + !tor_digest_is_zero(identity_digest)); + + if (was_in_digest_map) + /* We should always remove it; we'll add it back if we're writing + * in a new digest. + */ + channel_remove_from_digest_map(chan); + + if (identity_digest) { + memcpy(chan->u.cell_chan.identity_digest, + identity_digest, + sizeof(chan->u.cell_chan.identity_digest)); + } else { + memset(chan->u.cell_chan.identity_digest, 0, + sizeof(chan->u.cell_chan.identity_digest)); + } + + /* Put it in the digest map if we should */ + if (should_be_in_digest_map) + channel_add_to_digest_map(chan); +} + +/** + * Clear the remote end metadata (identity_digest/nickname) of a channel + * + * This function clears all the remote end info from a channel; this is + * intended for use by the lower layer. + * + * @param chan Channel to clear + */ + +void +channel_clear_remote_end(channel_t *chan) +{ + int state_not_in_map; + + tor_assert(chan); + tor_assert(!(chan->is_listener)); + + log_debug(LD_CHANNEL, + "Clearing remote endpoint identity on channel %p with " + "global ID %lu", + chan, chan->global_identifier); + + state_not_in_map = + (chan->state == CHANNEL_STATE_LISTENING || + chan->state == CHANNEL_STATE_CLOSING || + chan->state == CHANNEL_STATE_CLOSED || + chan->state == CHANNEL_STATE_ERROR); + + if (!state_not_in_map && chan->registered && + !tor_digest_is_zero(chan->u.cell_chan.identity_digest)) + /* if it's registered get it out of the digest map */ + channel_remove_from_digest_map(chan); + + memset(chan->u.cell_chan.identity_digest, 0, + sizeof(chan->u.cell_chan.identity_digest)); + tor_free(chan->u.cell_chan.nickname); +} + +/** + * Set the remote end metadata (identity_digest/nickname) of a channel + * + * This function sets new remote end info on a channel; this is intended + * for use by the lower layer. + * + * @chan Channel to set data on + * @chan identity_digest New identity digest for chan + * @chan nickname New remote nickname for chan + */ + +void +channel_set_remote_end(channel_t *chan, + const char *identity_digest, + const char *nickname) +{ + int was_in_digest_map, should_be_in_digest_map, state_not_in_map; + + tor_assert(chan); + tor_assert(!(chan->is_listener)); + + log_debug(LD_CHANNEL, + "Setting remote endpoint identity on channel %p with " + "global ID %lu to nickname %s, digest %s", + chan, chan->global_identifier, + nickname ? nickname : "(null)", + identity_digest ? + hex_str(identity_digest, DIGEST_LEN) : "(null)"); + + state_not_in_map = + (chan->state == CHANNEL_STATE_LISTENING || + chan->state == CHANNEL_STATE_CLOSING || + chan->state == CHANNEL_STATE_CLOSED || + chan->state == CHANNEL_STATE_ERROR); + was_in_digest_map = + !state_not_in_map && + chan->registered && + !tor_digest_is_zero(chan->u.cell_chan.identity_digest); + should_be_in_digest_map = + !state_not_in_map && + chan->registered && + (identity_digest && + !tor_digest_is_zero(identity_digest)); + + if (was_in_digest_map) + /* We should always remove it; we'll add it back if we're writing + * in a new digest. + */ + channel_remove_from_digest_map(chan); + + if (identity_digest) { + memcpy(chan->u.cell_chan.identity_digest, + identity_digest, + sizeof(chan->u.cell_chan.identity_digest)); + + } else { + memset(chan->u.cell_chan.identity_digest, 0, + sizeof(chan->u.cell_chan.identity_digest)); + } + + tor_free(chan->u.cell_chan.nickname); + if (nickname) + chan->u.cell_chan.nickname = tor_strdup(nickname); + + /* Put it in the digest map if we should */ + if (should_be_in_digest_map) + channel_add_to_digest_map(chan); +} + +/** + * Write a cell to a channel + * + * Write a fixed-length cell to a channel using the write_cell() method. + * This is equivalent to the pre-channels connection_or_write_cell_to_buf(). + * + * @param chan Channel to write a cell to + * @param cell Cell to write to chan + */ + +void +channel_write_cell(channel_t *chan, cell_t *cell) +{ + cell_queue_entry_t *q; + int sent = 0; + + tor_assert(chan); + tor_assert(!(chan->is_listener)); + tor_assert(cell); + tor_assert(chan->u.cell_chan.write_cell); + + /* Assert that the state makes sense for a cell write */ + tor_assert(chan->state == CHANNEL_STATE_OPENING || + chan->state == CHANNEL_STATE_OPEN || + chan->state == CHANNEL_STATE_MAINT); + + log_debug(LD_CHANNEL, + "Writing cell_t %p to channel %p with global ID %lu", + cell, chan, chan->global_identifier); + + /* Increment the timestamp unless it's padding */ + if (!(cell->command == CELL_PADDING || + cell->command == CELL_VPADDING)) { + chan->u.cell_chan.timestamp_last_added_nonpadding = approx_time(); + } + + /* Can we send it right out? If so, try */ + if (!(chan->u.cell_chan.outgoing_queue && + (smartlist_len(chan->u.cell_chan.outgoing_queue) > 0)) && + chan->state == CHANNEL_STATE_OPEN) { + if (chan->u.cell_chan.write_cell(chan, cell)) { + sent = 1; + /* Timestamp for transmission */ + channel_timestamp_xmit(chan); + /* If we're here the queue is empty, so it's drained too */ + channel_timestamp_drained(chan); + } + } + + if (!sent) { + /* Not sent, queue it */ + if (!(chan->u.cell_chan.outgoing_queue)) + chan->u.cell_chan.outgoing_queue = smartlist_new(); + q = tor_malloc(sizeof(*q)); + q->type = CELL_QUEUE_FIXED; + q->u.fixed.cell = cell; + smartlist_add(chan->u.cell_chan.outgoing_queue, q); + /* Try to process the queue? */ + if (chan->state == CHANNEL_STATE_OPEN) channel_flush_cells(chan); + } +} + +/** + * Write a packed cell to a channel + * + * Write a packed cell to a channel using the write_cell() method. + * + * @param chan Channel to write a cell to + * @param packed_cell Cell to write to chan + */ + +void +channel_write_packed_cell(channel_t *chan, packed_cell_t *packed_cell) +{ + cell_queue_entry_t *q; + int sent = 0; + + tor_assert(chan); + tor_assert(!(chan->is_listener)); + tor_assert(packed_cell); + tor_assert(chan->u.cell_chan.write_packed_cell); + + /* Assert that the state makes sense for a cell write */ + tor_assert(chan->state == CHANNEL_STATE_OPENING || + chan->state == CHANNEL_STATE_OPEN || + chan->state == CHANNEL_STATE_MAINT); + + log_debug(LD_CHANNEL, + "Writing packed_cell_t %p to channel %p with global ID %lu", + packed_cell, chan, chan->global_identifier); + + /* Increment the timestamp */ + chan->u.cell_chan.timestamp_last_added_nonpadding = approx_time(); + + /* Can we send it right out? If so, try */ + if (!(chan->u.cell_chan.outgoing_queue && + (smartlist_len(chan->u.cell_chan.outgoing_queue) > 0)) && + chan->state == CHANNEL_STATE_OPEN) { + if (chan->u.cell_chan.write_packed_cell(chan, packed_cell)) { + sent = 1; + /* Timestamp for transmission */ + channel_timestamp_xmit(chan); + /* If we're here the queue is empty, so it's drained too */ + channel_timestamp_drained(chan); + } + } + + if (!sent) { + /* Not sent, queue it */ + if (!(chan->u.cell_chan.outgoing_queue)) + chan->u.cell_chan.outgoing_queue = smartlist_new(); + q = tor_malloc(sizeof(*q)); + q->type = CELL_QUEUE_PACKED; + q->u.packed.packed_cell = packed_cell; + smartlist_add(chan->u.cell_chan.outgoing_queue, q); + /* Try to process the queue? */ + if (chan->state == CHANNEL_STATE_OPEN) channel_flush_cells(chan); + } +} + +/** + * Write a variable-length cell to a channel + * + * Write a variable-length cell to a channel using the write_cell() method. + * This is equivalent to the pre-channels + * connection_or_write_var_cell_to_buf(). + * + * @param chan Channel to write a cell to + * @param var_cell Cell to write to chan + */ + +void +channel_write_var_cell(channel_t *chan, var_cell_t *var_cell) +{ + cell_queue_entry_t *q; + int sent = 0; + + tor_assert(chan); + tor_assert(!(chan->is_listener)); + tor_assert(var_cell); + tor_assert(chan->u.cell_chan.write_var_cell); + + /* Assert that the state makes sense for a cell write */ + tor_assert(chan->state == CHANNEL_STATE_OPENING || + chan->state == CHANNEL_STATE_OPEN || + chan->state == CHANNEL_STATE_MAINT); + + log_debug(LD_CHANNEL, + "Writing var_cell_t %p to channel %p with global ID %lu", + var_cell, chan, chan->global_identifier); + + /* Increment the timestamp unless it's padding */ + if (!(var_cell->command == CELL_PADDING || + var_cell->command == CELL_VPADDING)) { + chan->u.cell_chan.timestamp_last_added_nonpadding = approx_time(); + } + + /* Can we send it right out? If so, then try */ + if (!(chan->u.cell_chan.outgoing_queue && + (smartlist_len(chan->u.cell_chan.outgoing_queue) > 0)) && + chan->state == CHANNEL_STATE_OPEN) { + if (chan->u.cell_chan.write_var_cell(chan, var_cell)) { + sent = 1; + /* Timestamp for transmission */ + channel_timestamp_xmit(chan); + /* If we're here the queue is empty, so it's drained too */ + channel_timestamp_drained(chan); + } + } + + if (!sent) { + /* Not sent, queue it */ + if (!(chan->u.cell_chan.outgoing_queue)) + chan->u.cell_chan.outgoing_queue = smartlist_new(); + q = tor_malloc(sizeof(*q)); + q->type = CELL_QUEUE_VAR; + q->u.var.var_cell = var_cell; + smartlist_add(chan->u.cell_chan.outgoing_queue, q); + /* Try to process the queue? */ + if (chan->state == CHANNEL_STATE_OPEN) channel_flush_cells(chan); + } +} + +/** + * Change channel state + * + * This internal and subclass use only function is used to change channel + * state, performing all transition validity checks and whatever actions + * are appropriate to the state transition in question. + * + * @param chan Channel to change state on + * @param to_state State to change chan to + */ + +void +channel_change_state(channel_t *chan, channel_state_t to_state) +{ + channel_state_t from_state; + unsigned char was_active, is_active, was_listening, is_listening; + unsigned char was_in_id_map, is_in_id_map; + + tor_assert(chan); + from_state = chan->state; + + tor_assert(channel_state_is_valid(from_state)); + tor_assert(channel_state_is_valid(to_state)); + tor_assert(channel_state_can_transition(chan->state, to_state)); + + if (chan->is_listener) { + tor_assert(from_state == CHANNEL_STATE_LISTENING || + from_state == CHANNEL_STATE_CLOSING || + from_state == CHANNEL_STATE_CLOSED || + from_state == CHANNEL_STATE_ERROR); + tor_assert(to_state == CHANNEL_STATE_LISTENING || + to_state == CHANNEL_STATE_CLOSING || + to_state == CHANNEL_STATE_CLOSED || + to_state == CHANNEL_STATE_ERROR); + } else { + tor_assert(from_state != CHANNEL_STATE_LISTENING); + tor_assert(to_state != CHANNEL_STATE_LISTENING); + } + + /* Check for no-op transitions */ + if (from_state == to_state) { + log_debug(LD_CHANNEL, + "Got no-op transition from \"%s\" to itself on channel %p", + channel_state_to_string(to_state), + chan); + return; + } + + /* If we're going to a closing or closed state, we must have a reason set */ + if (to_state == CHANNEL_STATE_CLOSING || + to_state == CHANNEL_STATE_CLOSED || + to_state == CHANNEL_STATE_ERROR) { + tor_assert(chan->reason_for_closing != CHANNEL_NOT_CLOSING); + } + + /* + * We need to maintain the queues here for some transitions: + * when we enter CHANNEL_STATE_OPEN (especially from CHANNEL_STATE_MAINT) + * we may have a backlog of cells to transmit, so drain the queues in + * that case, and when going to CHANNEL_STATE_CLOSED the subclass + * should have made sure to finish sending things (or gone to + * CHANNEL_STATE_ERROR if not possible), so we assert for that here. + */ + + log_debug(LD_CHANNEL, + "Changing state of channel %p from \"%s\" to \"%s\"", + chan, + channel_state_to_string(chan->state), + channel_state_to_string(to_state)); + + chan->state = to_state; + + /* Need to add to the right lists if the channel is registered */ + if (chan->registered) { + was_active = !(from_state == CHANNEL_STATE_CLOSED || + from_state == CHANNEL_STATE_ERROR); + is_active = !(to_state == CHANNEL_STATE_CLOSED || + to_state == CHANNEL_STATE_ERROR); + + /* Need to take off active list and put on finished list? */ + if (was_active && !is_active) { + if (active_channels) smartlist_remove(active_channels, chan); + if (!finished_channels) finished_channels = smartlist_new(); + smartlist_add(finished_channels, chan); + } + /* Need to put on active list? */ + else if (!was_active && is_active) { + if (finished_channels) smartlist_remove(finished_channels, chan); + if (!active_channels) active_channels = smartlist_new(); + smartlist_add(active_channels, chan); + } + + was_listening = (from_state == CHANNEL_STATE_LISTENING); + is_listening = (to_state == CHANNEL_STATE_LISTENING); + + /* Need to put on listening list? */ + if (!was_listening && is_listening) { + if (!listening_channels) listening_channels = smartlist_new(); + smartlist_add(listening_channels, chan); + } + /* Need to remove from listening list? */ + else if (was_listening && !is_listening) { + if (listening_channels) smartlist_remove(listening_channels, chan); + } + + if (!(chan->is_listener) && + !tor_digest_is_zero(chan->u.cell_chan.identity_digest)) { + /* Now we need to handle the identity map */ + was_in_id_map = !(from_state == CHANNEL_STATE_LISTENING || + from_state == CHANNEL_STATE_CLOSING || + from_state == CHANNEL_STATE_CLOSED || + from_state == CHANNEL_STATE_ERROR); + is_in_id_map = !(to_state == CHANNEL_STATE_LISTENING || + to_state == CHANNEL_STATE_CLOSING || + to_state == CHANNEL_STATE_CLOSED || + to_state == CHANNEL_STATE_ERROR); + + if (!was_in_id_map && is_in_id_map) channel_add_to_digest_map(chan); + else if (was_in_id_map && !is_in_id_map) + channel_remove_from_digest_map(chan); + } + } + + /* Tell circuits if we opened and stuff */ + if (to_state == CHANNEL_STATE_OPEN) channel_do_open_actions(chan); + + if (!(chan->is_listener) && + to_state == CHANNEL_STATE_OPEN) { + /* Check for queued cells to process */ + if (chan->u.cell_chan.cell_queue && + smartlist_len(chan->u.cell_chan.cell_queue) > 0) + channel_process_cells(chan); + if (chan->u.cell_chan.outgoing_queue && + smartlist_len(chan->u.cell_chan.outgoing_queue) > 0) + channel_flush_cells(chan); + } else if (to_state == CHANNEL_STATE_CLOSED || + to_state == CHANNEL_STATE_ERROR) { + /* Assert that all queues are empty */ + if (chan->is_listener) { + tor_assert(!(chan->u.listener.incoming_list) || + smartlist_len(chan->u.listener.incoming_list) == 0); + } else { + tor_assert(!(chan->u.cell_chan.cell_queue) || + smartlist_len(chan->u.cell_chan.cell_queue) == 0); + tor_assert(!(chan->u.cell_chan.outgoing_queue) || + smartlist_len(chan->u.cell_chan.outgoing_queue) == 0); + } + } +} + +/** + * Try to flush cells to the lower layer + * + * this is called by the lower layer to indicate that it wants more cells; + * it will try to write up to num_cells cells from the channel's cell queue or + * from circuits active on that channel, or as many as it has available if + * num_cells == -1. + * + * @param chan Channel to flush from + * @param num_cells Maximum number of cells to flush, or -1 for unlimited + * @return Number of cells flushed + */ + +#define MAX_CELLS_TO_GET_FROM_CIRCUITS_FOR_UNLIMITED 256 + +ssize_t +channel_flush_some_cells(channel_t *chan, ssize_t num_cells) +{ + unsigned int unlimited = 0; + ssize_t flushed = 0; + int num_cells_from_circs; + + tor_assert(chan); + tor_assert(!(chan->is_listener)); + + if (num_cells < 0) unlimited = 1; + if (!unlimited && num_cells <= flushed) goto done; + + /* If we aren't in CHANNEL_STATE_OPEN, nothing goes through */ + if (chan->state == CHANNEL_STATE_OPEN) { + /* Try to flush as much as we can that's already queued */ + flushed += channel_flush_some_cells_from_outgoing_queue(chan, + (unlimited ? -1 : num_cells - flushed)); + if (!unlimited && num_cells <= flushed) goto done; + + if (chan->u.cell_chan.active_circuits) { + /* Try to get more cells from any active circuits */ + num_cells_from_circs = + channel_flush_from_first_active_circuit(chan, + (unlimited ? MAX_CELLS_TO_GET_FROM_CIRCUITS_FOR_UNLIMITED : + (num_cells - flushed))); + + /* If it claims we got some, process the queue again */ + if (num_cells_from_circs > 0) { + flushed += channel_flush_some_cells_from_outgoing_queue(chan, + (unlimited ? -1 : num_cells - flushed)); + } + } + } + + done: + return flushed; +} + +/** + * Flush cells from just the channel's out going cell queue + * + * This gets called from channel_flush_some_cells() above to flush cells + * just from the queue without trying for active_circuits. + * + * @param chan Channel to flush from + * @param num_cells Maximum number of cells to flush, or -1 for unlimited + * @return Number of cells flushed + */ + +static ssize_t +channel_flush_some_cells_from_outgoing_queue(channel_t *chan, + ssize_t num_cells) +{ + unsigned int unlimited = 0; + ssize_t flushed = 0; + cell_queue_entry_t *q = NULL; + + tor_assert(chan); + tor_assert(!(chan->is_listener)); + tor_assert(chan->u.cell_chan.write_cell); + tor_assert(chan->u.cell_chan.write_packed_cell); + tor_assert(chan->u.cell_chan.write_var_cell); + + if (num_cells < 0) unlimited = 1; + if (!unlimited && num_cells <= flushed) return 0; + + /* If we aren't in CHANNEL_STATE_OPEN, nothing goes through */ + if (chan->state == CHANNEL_STATE_OPEN) { + while ((unlimited || num_cells > flushed) && + (chan->u.cell_chan.outgoing_queue && + (smartlist_len(chan->u.cell_chan.outgoing_queue) > 0))) { + /* + * Ewww, smartlist_del_keeporder() is O(n) in list length; maybe a + * a linked list would make more sense for the queue. + */ + + /* Get the head of the queue */ + q = smartlist_get(chan->u.cell_chan.outgoing_queue, 0); + /* That shouldn't happen; bail out */ + if (q) { + /* + * Okay, we have a good queue entry, try to give it to the lower + * layer. + */ + switch (q->type) { + case CELL_QUEUE_FIXED: + if (q->u.fixed.cell) { + if (chan->u.cell_chan.write_cell(chan, + q->u.fixed.cell)) { + tor_free(q); + ++flushed; + channel_timestamp_xmit(chan); + } + /* Else couldn't write it; leave it on the queue */ + } else { + /* This shouldn't happen */ + log_info(LD_CHANNEL, + "Saw broken cell queue entry of type CELL_QUEUE_FIXED " + "with no cell on channel %p.", + chan); + /* Throw it away */ + tor_free(q); + } + break; + case CELL_QUEUE_PACKED: + if (q->u.packed.packed_cell) { + if (chan->u.cell_chan.write_packed_cell(chan, + q->u.packed.packed_cell)) { + tor_free(q); + ++flushed; + channel_timestamp_xmit(chan); + } + /* Else couldn't write it; leave it on the queue */ + } else { + /* This shouldn't happen */ + log_info(LD_CHANNEL, + "Saw broken cell queue entry of type CELL_QUEUE_PACKED " + "with no cell on channel %p.", + chan); + /* Throw it away */ + tor_free(q); + } + break; + case CELL_QUEUE_VAR: + if (q->u.var.var_cell) { + if (chan->u.cell_chan.write_var_cell(chan, + q->u.var.var_cell)) { + tor_free(q); + ++flushed; + channel_timestamp_xmit(chan); + } + /* Else couldn't write it; leave it on the queue */ + } else { + /* This shouldn't happen */ + log_info(LD_CHANNEL, + "Saw broken cell queue entry of type CELL_QUEUE_VAR " + "with no cell on channel %p.", + chan); + /* Throw it away */ + tor_free(q); + } + break; + default: + /* Unknown type, log and free it */ + log_info(LD_CHANNEL, + "Saw an unknown cell queue entry type %d on channel %p; " + "ignoring it. Someone should fix this.", + q->type, chan); + tor_free(q); /* tor_free() NULLs it out */ + } + } else { + /* This shouldn't happen; log and throw it away */ + log_info(LD_CHANNEL, + "Saw a NULL entry in the outgoing cell queue on channel %p; " + "this is definitely a bug.", + chan); + /* q is already NULL, so we know to delete that queue entry */ + } + + /* if q got NULLed out, we used it and should remove the queue entry */ + if (!q) smartlist_del_keeporder(chan->u.cell_chan.outgoing_queue, 0); + /* No cell removed from list, so we can't go on any further */ + else break; + } + } + + /* Did we drain the queue? */ + if (!(chan->u.cell_chan.outgoing_queue) || + smartlist_len(chan->u.cell_chan.outgoing_queue) == 0) { + /* Timestamp it */ + channel_timestamp_drained(chan); + } + + return flushed; +} + +/** + * Try to flush as many cells as we possibly can from the queue + * + * This tries to flush as many cells from the queue as the lower layer + * will take. It just calls channel_flush_some_cells_from_outgoing_queue() + * in unlimited mode. + * + * @param chan Channel to flush + */ + +void +channel_flush_cells(channel_t *chan) +{ + channel_flush_some_cells_from_outgoing_queue(chan, -1); +} + +/** + * Check if any cells are available + * + * This gets used from the lower layer to check if any more cells are + * available. + * + * @param chan Channel to check on + * @return 1 if cells are available, 0 otherwise + */ + +int +channel_more_to_flush(channel_t *chan) +{ + tor_assert(chan); + tor_assert(!(chan->is_listener)); + + /* Check if we have any queued */ + if (chan->u.cell_chan.cell_queue && + smartlist_len(chan->u.cell_chan.cell_queue) > 0) return 1; + + /* Check if any circuits would like to queue some */ + if (chan->u.cell_chan.active_circuits) return 1; + + /* Else no */ + return 0; +} + +/** + * Notify the channel we're done flushing the output in the lower layer + * + * Connection.c will call this when we've flushed the output; there's some + * dirreq-related maintenance to do. + * + * @param chan Channel to notify + */ + +void +channel_notify_flushed(channel_t *chan) +{ + tor_assert(chan); + tor_assert(!(chan->is_listener)); + + if (chan->u.cell_chan.dirreq_id != 0) + geoip_change_dirreq_state(chan->u.cell_chan.dirreq_id, + DIRREQ_TUNNELED, + DIRREQ_CHANNEL_BUFFER_FLUSHED); +} + +/** + * Process the queue of incoming channels on a listener + * + * Use a listener's registered callback to process as many entries in the + * queue of incoming channels as possible. + * + * @param listener Pointer to a listening channel. + */ + +void +channel_process_incoming(channel_t *listener) +{ + tor_assert(listener); + tor_assert(listener->is_listener); + /* + * CHANNEL_STATE_CLOSING permitted because we drain the queue while + * closing a listener. + */ + tor_assert(listener->state == CHANNEL_STATE_LISTENING || + listener->state == CHANNEL_STATE_CLOSING); + tor_assert(listener->u.listener.listener); + + log_debug(LD_CHANNEL, + "Processing queue of incoming connections for listening " + "channel %p (global ID %lu)", + listener, listener->global_identifier); + + if (!(listener->u.listener.incoming_list)) return; + + SMARTLIST_FOREACH_BEGIN(listener->u.listener.incoming_list, + channel_t *, chan) { + tor_assert(chan); + tor_assert(!(chan->is_listener)); + + log_debug(LD_CHANNEL, + "Handling incoming connection %p (%lu) for listener %p (%lu)", + chan, chan->global_identifier, + listener, listener->global_identifier); + /* Make sure this is set correctly */ + channel_mark_incoming(chan); + listener->u.listener.listener(listener, chan); + SMARTLIST_DEL_CURRENT(listener->u.listener.incoming_list, chan); + } SMARTLIST_FOREACH_END(chan); + + tor_assert(smartlist_len(listener->u.listener.incoming_list) == 0); + smartlist_free(listener->u.listener.incoming_list); + listener->u.listener.incoming_list = NULL; +} + +/** + * Take actions required when a channel becomes open + * + * Handle actions we should do when we know a channel is open; a lot of + * this comes from the old connection_or_set_state_open() of connection_or.c. + * + * Because of this mechanism, future channel_t subclasses should take care + * not to change a channel to from CHANNEL_STATE_OPENING to CHANNEL_STATE_OPEN + * until there is positive confirmation that the network is operational. + * In particular, anything UDP-based should not make this transition until a + * packet is received from the other side. + * + * @param chan Channel that has become open + */ + +void +channel_do_open_actions(channel_t *chan) +{ + tor_addr_t remote_addr; + int started_here, not_using = 0; + time_t now = time(NULL); + + tor_assert(chan); + tor_assert(!(chan->is_listener)); + + started_here = channel_is_outgoing(chan); + + if (started_here) { + circuit_build_times_network_is_live(&circ_times); + rep_hist_note_connect_succeeded(chan->u.cell_chan.identity_digest, now); + if (entry_guard_register_connect_status( + chan->u.cell_chan.identity_digest, 1, 0, now) < 0) { + /* Close any circuits pending on this channel. We leave it in state + * 'open' though, because it didn't actually *fail* -- we just + * chose not to use it. */ + log_debug(LD_OR, + "New entry guard was reachable, but closing this " + "connection so we can retry the earlier entry guards."); + circuit_n_chan_done(chan, 0); + not_using = 1; + } + router_set_status(chan->u.cell_chan.identity_digest, 1); + } else { + /* only report it to the geoip module if it's not a known router */ + if (!router_get_by_id_digest(chan->u.cell_chan.identity_digest)) { + if (channel_get_addr_if_possible(chan, &remote_addr)) { + geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &remote_addr, + now); + } + /* Otherwise the underlying transport can't tell us this, so skip it */ + } + } + + if (!not_using) circuit_n_chan_done(chan, 1); +} + +/** + * Queue an incoming channel on a listener + * + * Internal and subclass use only function to queue an incoming channel from + * a listening one. A subclass of channel_t should call this when a new + * incoming channel is created. + * + * @param listener Listening channel to queue on + * @param incoming New incoming channel + */ + +void +channel_queue_incoming(channel_t *listener, channel_t *incoming) +{ + int need_to_queue = 0; + + tor_assert(listener); + tor_assert(listener->is_listener); + tor_assert(listener->state == CHANNEL_STATE_LISTENING); + tor_assert(incoming); + tor_assert(!(incoming->is_listener)); + /* + * Other states are permitted because subclass might process activity + * on a channel at any time while it's queued, but a listener returning + * another listener makes no sense. + */ + tor_assert(incoming->state != CHANNEL_STATE_LISTENING); + + log_debug(LD_CHANNEL, + "Queueing incoming channel %p on listening channel %p", + incoming, listener); + + /* Do we need to queue it, or can we just call the listener right away? */ + if (!(listener->u.listener.listener)) need_to_queue = 1; + if (listener->u.listener.incoming_list && + (smartlist_len(listener->u.listener.incoming_list) > 0)) + need_to_queue = 1; + + /* If we need to queue and have no queue, create one */ + if (need_to_queue && !(listener->u.listener.incoming_list)) { + listener->u.listener.incoming_list = smartlist_new(); + } + + /* If we don't need to queue, process it right away */ + if (!need_to_queue) { + tor_assert(listener->u.listener.listener); + listener->u.listener.listener(listener, incoming); + } + /* + * Otherwise, we need to queue; queue and then process the queue if + * we can. + */ + else { + tor_assert(listener->u.listener.incoming_list); + smartlist_add(listener->u.listener.incoming_list, incoming); + if (listener->u.listener.listener) channel_process_incoming(listener); + } +} + +/** + * Process queued incoming cells + * + * Process as many queued cells as we can from the incoming + * cell queue. + * + * @param chan Channel to process incoming cell queue on + */ + +void +channel_process_cells(channel_t *chan) +{ + tor_assert(chan); + tor_assert(!(chan->is_listener)); + tor_assert(chan->state == CHANNEL_STATE_CLOSING || + chan->state == CHANNEL_STATE_MAINT || + chan->state == CHANNEL_STATE_OPEN); + + log_debug(LD_CHANNEL, + "Processing as many incoming cells as we can for channel %p", + chan); + + /* Nothing we can do if we have no registered cell handlers */ + if (!(chan->u.cell_chan.cell_handler || + chan->u.cell_chan.var_cell_handler)) return; + /* Nothing we can do if we have no cells */ + if (!(chan->u.cell_chan.cell_queue)) return; + + /* + * Process cells until we're done or find one we have no current handler + * for. + */ + SMARTLIST_FOREACH_BEGIN(chan->u.cell_chan.cell_queue, + cell_queue_entry_t *, q) { + tor_assert(q); + tor_assert(q->type == CELL_QUEUE_FIXED || + q->type == CELL_QUEUE_VAR); + + if (q->type == CELL_QUEUE_FIXED && + chan->u.cell_chan.cell_handler) { + /* Handle a fixed-length cell */ + tor_assert(q->u.fixed.cell); + log_debug(LD_CHANNEL, + "Processing incoming cell_t %p for channel %p", + q->u.fixed.cell, chan); + chan->u.cell_chan.cell_handler(chan, q->u.fixed.cell); + SMARTLIST_DEL_CURRENT(chan->u.cell_chan.cell_queue, q); + tor_free(q); + } else if (q->type == CELL_QUEUE_VAR && + chan->u.cell_chan.var_cell_handler) { + /* Handle a variable-length cell */ + tor_assert(q->u.var.var_cell); + log_debug(LD_CHANNEL, + "Processing incoming var_cell_t %p for channel %p", + q->u.var.var_cell, chan); + chan->u.cell_chan.var_cell_handler(chan, q->u.var.var_cell); + SMARTLIST_DEL_CURRENT(chan->u.cell_chan.cell_queue, q); + tor_free(q); + } else { + /* Can't handle this one */ + break; + } + } SMARTLIST_FOREACH_END(q); + + /* If the list is empty, free it */ + if (smartlist_len(chan->u.cell_chan.cell_queue) == 0 ) { + smartlist_free(chan->u.cell_chan.cell_queue); + chan->u.cell_chan.cell_queue = NULL; + } +} + +/** + * Queue incoming cell + * + * This should be called by a channel_t subclass to queue an incoming fixed- + * length cell for processing, and process it if possible. + * + * @param chan Channel the cell is arriving on + * @param cell Incoming cell to queue and process + */ + +void +channel_queue_cell(channel_t *chan, cell_t *cell) +{ + int need_to_queue = 0; + cell_queue_entry_t *q; + + tor_assert(chan); + tor_assert(!(chan->is_listener)); + tor_assert(cell); + tor_assert(chan->state == CHANNEL_STATE_OPEN); + + /* Do we need to queue it, or can we just call the handler right away? */ + if (!(chan->u.cell_chan.cell_handler)) need_to_queue = 1; + if (chan->u.cell_chan.cell_queue && + (smartlist_len(chan->u.cell_chan.cell_queue) > 0)) + need_to_queue = 1; + + /* If we need to queue and have no queue, create one */ + if (need_to_queue && !(chan->u.cell_chan.cell_queue)) { + chan->u.cell_chan.cell_queue = smartlist_new(); + } + + /* Timestamp for receiving */ + channel_timestamp_recv(chan); + + /* If we don't need to queue we can just call cell_handler */ + if (!need_to_queue) { + tor_assert(chan->u.cell_chan.cell_handler); + log_debug(LD_CHANNEL, + "Directly handling incoming cell_t %p for channel %p", + cell, chan); + chan->u.cell_chan.cell_handler(chan, cell); + } else { + /* Otherwise queue it and then process the queue if possible. */ + tor_assert(chan->u.cell_chan.cell_queue); + q = tor_malloc(sizeof(*q)); + q->type = CELL_QUEUE_FIXED; + q->u.fixed.cell = cell; + log_debug(LD_CHANNEL, + "Queueing incoming cell_t %p for channel %p", + cell, chan); + smartlist_add(chan->u.cell_chan.cell_queue, q); + if (chan->u.cell_chan.cell_handler || + chan->u.cell_chan.var_cell_handler) { + channel_process_cells(chan); + } + } +} + +/** + * Queue incoming variable-length cell + * + * This should be called by a channel_t subclass to queue an incoming + * variable-length cell for processing, and process it if possible. + * + * @param chan Channel the cell is arriving on + * @param var_cell Incoming cell to queue and process + */ + +void +channel_queue_var_cell(channel_t *chan, var_cell_t *var_cell) +{ + int need_to_queue = 0; + cell_queue_entry_t *q; + + tor_assert(chan); + tor_assert(!(chan->is_listener)); + tor_assert(var_cell); + tor_assert(chan->state == CHANNEL_STATE_OPEN); + + /* Do we need to queue it, or can we just call the handler right away? */ + if (!(chan->u.cell_chan.var_cell_handler)) need_to_queue = 1; + if (chan->u.cell_chan.cell_queue && + (smartlist_len(chan->u.cell_chan.cell_queue) > 0)) + need_to_queue = 1; + + /* If we need to queue and have no queue, create one */ + if (need_to_queue && !(chan->u.cell_chan.cell_queue)) { + chan->u.cell_chan.cell_queue = smartlist_new(); + } + + /* Timestamp for receiving */ + channel_timestamp_recv(chan); + + /* If we don't need to queue we can just call cell_handler */ + if (!need_to_queue) { + tor_assert(chan->u.cell_chan.var_cell_handler); + log_debug(LD_CHANNEL, + "Directly handling incoming var_cell_t %p for channel %p", + var_cell, chan); + chan->u.cell_chan.var_cell_handler(chan, var_cell); + } else { + /* Otherwise queue it and then process the queue if possible. */ + tor_assert(chan->u.cell_chan.cell_queue); + q = tor_malloc(sizeof(*q)); + q->type = CELL_QUEUE_VAR; + q->u.var.var_cell = var_cell; + log_debug(LD_CHANNEL, + "Queueing incoming var_cell_t %p for channel %p", + var_cell, chan); + smartlist_add(chan->u.cell_chan.cell_queue, q); + if (chan->u.cell_chan.cell_handler || + chan->u.cell_chan.var_cell_handler) { + channel_process_cells(chan); + } + } +} + +/** + * Send destroy cell on a channel + * + * Write a destroy cell with circ ID circ_id and reason reason + * onto channel chan. Don't perform range-checking on reason: + * we may want to propagate reasons from other cells. + * + * @param circ_id Circuit ID to destroy + * @param chan Channel to send on + * @param reason Reason code + * @return Always 0 + */ + +int +channel_send_destroy(circid_t circ_id, channel_t *chan, int reason) +{ + cell_t cell; + + tor_assert(chan); + tor_assert(!(chan->is_listener)); + + memset(&cell, 0, sizeof(cell_t)); + cell.circ_id = circ_id; + cell.command = CELL_DESTROY; + cell.payload[0] = (uint8_t) reason; + log_debug(LD_OR,"Sending destroy (circID %d).", circ_id); + + channel_write_cell(chan, &cell); + + return 0; +} + +/** + * Channel cleanup + * + * This gets called periodically from run_scheduled_events() in main.c; + * it cleans up after closed channels. + */ + +void +channel_run_cleanup(void) +{ + channel_t *tmp = NULL; + + /* Check if we need to do anything */ + if (!finished_channels || smartlist_len(finished_channels) == 0) return; + + /* Iterate through finished_channels and get rid of them */ + SMARTLIST_FOREACH_BEGIN(finished_channels, channel_t *, curr) { + tmp = curr; + /* Remove it from the list */ + SMARTLIST_DEL_CURRENT(finished_channels, curr); + /* Also unregister it */ + channel_unregister(tmp); + /* ... and free it */ + channel_free(tmp); + } SMARTLIST_FOREACH_END(curr); +} + +/** + * Close all channels and free everything + * + * This gets called from tor_free_all() in main.c to clean up on exit. + * It will close all registered channels and free associated storage, + * then free the all_channels, active_channels, listening_channels and + * finished_channels lists and also channel_identity_map. + */ + +void +channel_free_all(void) +{ + channel_t *tmp = NULL; + + log_debug(LD_CHANNEL, + "Shutting down channels..."); + + /* First, let's go for finished channels */ + if (finished_channels) { + SMARTLIST_FOREACH_BEGIN(finished_channels, channel_t *, curr) { + tmp = curr; + /* Remove it from the list */ + SMARTLIST_DEL_CURRENT(finished_channels, curr); + /* Deregister and free it */ + tor_assert(tmp); + log_debug(LD_CHANNEL, + "Cleaning up finished channel %p (ID %lu) in state %s (%d)", + tmp, tmp->global_identifier, + channel_state_to_string(tmp->state), tmp->state); + channel_unregister(tmp); + channel_free(tmp); + } SMARTLIST_FOREACH_END(curr); + + smartlist_free(finished_channels); + finished_channels = NULL; + tmp = NULL; + } + + /* Now the listeners */ + if (listening_channels) { + SMARTLIST_FOREACH_BEGIN(listening_channels, channel_t *, curr) { + tmp = curr; + /* Remove it from the list */ + SMARTLIST_DEL_CURRENT(listening_channels, curr); + /* Close, deregister and free it */ + tor_assert(tmp); + log_debug(LD_CHANNEL, + "Cleaning up listening channel %p (ID %lu) in state %s (%d)", + tmp, tmp->global_identifier, + channel_state_to_string(tmp->state), tmp->state); + /* + * We have to unregister first so we don't put it in finished_channels + * and allocate that again on close. + */ + channel_unregister(tmp); + channel_request_close(tmp); + channel_force_free(tmp); + } SMARTLIST_FOREACH_END(curr); + + smartlist_free(listening_channels); + listening_channels = NULL; + } + + /* Now all active channels */ + if (active_channels) { + SMARTLIST_FOREACH_BEGIN(active_channels, channel_t *, curr) { + tmp = curr; + /* Remove it from the list */ + SMARTLIST_DEL_CURRENT(active_channels, curr); + /* Close, deregister and free it */ + tor_assert(tmp); + log_debug(LD_CHANNEL, + "Cleaning up active channel %p (ID %lu) in state %s (%d)", + tmp, tmp->global_identifier, + channel_state_to_string(tmp->state), tmp->state); + /* + * We have to unregister first so we don't put it in finished_channels + * and allocate that again on close. + */ + channel_unregister(tmp); + channel_request_close(tmp); + channel_force_free(tmp); + } SMARTLIST_FOREACH_END(curr); + + smartlist_free(active_channels); + active_channels = NULL; + } + + /* Now all channels, in case any are left over */ + if (all_channels) { + SMARTLIST_FOREACH_BEGIN(all_channels, channel_t *, curr) { + tmp = curr; + /* Remove it from the list */ + SMARTLIST_DEL_CURRENT(all_channels, curr); + /* Close, deregister and free it */ + tor_assert(tmp); + log_debug(LD_CHANNEL, + "Cleaning up leftover channel %p (ID %lu) in state %s (%d)", + tmp, tmp->global_identifier, + channel_state_to_string(tmp->state), tmp->state); + channel_unregister(tmp); + if (!(tmp->state == CHANNEL_STATE_CLOSING || + tmp->state == CHANNEL_STATE_CLOSED || + tmp->state == CHANNEL_STATE_ERROR)) { + channel_request_close(tmp); + } + channel_force_free(tmp); + } SMARTLIST_FOREACH_END(curr); + + smartlist_free(all_channels); + all_channels = NULL; + } + + /* Now free channel_identity_map */ + if (channel_identity_map) { + log_debug(LD_CHANNEL, + "Freeing channel_identity_map"); + /* Geez, anything still left over just won't die ... let it leak then */ + digestmap_free(channel_identity_map, NULL); + channel_identity_map = NULL; + } + + log_debug(LD_CHANNEL, + "Done cleaning up after channels"); +} + +/** + * Connect to a given addr/port/digest + * + * This sets up a new outgoing channel; in the future if multiple + * channel_t subclasses are available, this is where the selection policy + * should go. It may also be desirable to fold port into tor_addr_t + * or make a new type including a tor_addr_t and port, so we have a + * single abstract object encapsulating all the protocol details of + * how to contact an OR. + * + * @param addr Address of remote node to establish a channel to + * @param port ORport of remote OR + * @param id_digest Identity digest of remote OR + * @return New channel, or NULL if failure + */ + +channel_t * +channel_connect(const tor_addr_t *addr, uint16_t port, + const char *id_digest) +{ + return channel_tls_connect(addr, port, id_digest); +} + +/** + * Decide which of two channels to prefer for extending a circuit + * + * This function is called while extending a circuit and returns true iff + * a is 'better' than b. The most important criterion here is that a + * canonical channel is always better than a non-canonical one, but the + * number of circuits and the age are used as tie-breakers. + * + * This is based on the former connection_or_is_better() of connection_or.c + * + * @param now Current time to use for deciding grace period for new channels + * @param a Channel A for comparison + * @param b Channel B for comparison + * @param forgive_new_connections Whether to use grace period for new channels + * @return 1 iff a is better than b + */ + +int +channel_is_better(time_t now, channel_t *a, channel_t *b, + int forgive_new_connections) +{ + int a_grace, b_grace; + int a_is_canonical, b_is_canonical; + int a_has_circs, b_has_circs; + + /* + * Do not definitively deprecate a new channel with no circuits on it + * until this much time has passed. + */ +#define NEW_CHAN_GRACE_PERIOD (15*60) + + tor_assert(a); + tor_assert(b); + tor_assert(!(a->is_listener)); + tor_assert(!(b->is_listener)); + + /* Check if one is canonical and the other isn't first */ + a_is_canonical = channel_is_canonical(a); + b_is_canonical = channel_is_canonical(b); + + if (a_is_canonical && !b_is_canonical) return 1; + if (!a_is_canonical && b_is_canonical) return 0; + + /* + * Okay, if we're here they tied on canonicity. Next we check if + * they have any circuits, and if one does and the other doesn't, + * we prefer the one that does, unless we are forgiving and the + * one that has no circuits is in its grace period. + */ + + a_has_circs = (a->u.cell_chan.n_circuits > 0); + b_has_circs = (b->u.cell_chan.n_circuits > 0); + a_grace = (forgive_new_connections && + (now < channel_when_created(a) + NEW_CHAN_GRACE_PERIOD)); + b_grace = (forgive_new_connections && + (now < channel_when_created(b) + NEW_CHAN_GRACE_PERIOD)); + + if (a_has_circs && !b_has_circs && !b_grace) return 1; + if (!a_has_circs && b_has_circs && !a_grace) return 0; + + /* They tied on circuits too; just prefer whichever is newer */ + + if (channel_when_created(a) > channel_when_created(b)) return 1; + else return 0; +} + +/** + * Get a channel to extend a circuit + * + * Pick a suitable channel to extend a circuit to given the desired digest + * the address we believe is correct for that digest; this tries to see + * if we already have one for the requested endpoint, but if there is no good + * channel, set *msg_out to a message describing the channel's state + * and our next action, and set *launch_out to a boolean indicated whether + * the caller should try to launch a new channel with channel_connect(). + * + * @param digest Endpoint digest we want + * @param target_addr Endpoint address we want + * @param msg_out Write out status message here if we fail + * @param launch_out Write 1 here if caller should try to connect a new + * channel. + * @return Pointer to selected channel, or NULL if none available + */ + +channel_t * +channel_get_for_extend(const char *digest, + const tor_addr_t *target_addr, + const char **msg_out, + int *launch_out) +{ + channel_t *chan, *best = NULL; + int n_inprogress_goodaddr = 0, n_old = 0; + int n_noncanonical = 0, n_possible = 0; + time_t now = approx_time(); + + tor_assert(msg_out); + tor_assert(launch_out); + + if (!channel_identity_map) { + *msg_out = "Router not connected (nothing is). Connecting."; + *launch_out = 1; + return NULL; + } + + chan = channel_find_by_remote_digest(digest); + + /* Walk the list, unrefing the old one and refing the new at each + * iteration. + */ + for (; chan; chan = channel_next_with_digest(chan)) { + tor_assert(!(chan->is_listener)); + tor_assert(tor_memeq(chan->u.cell_chan.identity_digest, + digest, DIGEST_LEN)); + + if (chan->state == CHANNEL_STATE_CLOSING || + chan->state == CHANNEL_STATE_CLOSED || + chan->state == CHANNEL_STATE_ERROR || + chan->state == CHANNEL_STATE_LISTENING) + continue; + + /* Never return a channel on which the other end appears to be + * a client. */ + if (channel_is_client(chan)) { + continue; + } + + /* Never return a non-open connection. */ + if (chan->state != CHANNEL_STATE_OPEN) { + /* If the address matches, don't launch a new connection for this + * circuit. */ + if (!channel_matches_target_addr_for_extend(chan, target_addr)) + ++n_inprogress_goodaddr; + continue; + } + + /* Never return a connection that shouldn't be used for circs. */ + if (channel_is_bad_for_new_circs(chan)) { + ++n_old; + continue; + } + + /* Never return a non-canonical connection using a recent link protocol + * if the address is not what we wanted. + * + * The channel_is_canonical_is_reliable() function asks the lower layer + * if we should trust channel_is_canonical(). The below is from the + * comments of the old circuit_or_get_for_extend() and applies when + * the lower-layer transport is channel_tls_t. + * + * (For old link protocols, we can't rely on is_canonical getting + * set properly if we're talking to the right address, since we might + * have an out-of-date descriptor, and we will get no NETINFO cell to + * tell us about the right address.) + */ + if (!channel_is_canonical(chan) && + channel_is_canonical_is_reliable(chan) && + !channel_matches_target_addr_for_extend(chan, target_addr)) { + ++n_noncanonical; + continue; + } + + ++n_possible; + + if (!best) { + best = chan; /* If we have no 'best' so far, this one is good enough. */ + continue; + } + + if (channel_is_better(now, chan, best, 0)) + best = chan; + } + + if (best) { + *msg_out = "Connection is fine; using it."; + *launch_out = 0; + return best; + } else if (n_inprogress_goodaddr) { + *msg_out = "Connection in progress; waiting."; + *launch_out = 0; + return NULL; + } else if (n_old || n_noncanonical) { + *msg_out = "Connections all too old, or too non-canonical. " + " Launching a new one."; + *launch_out = 1; + return NULL; + } else { + *msg_out = "Not connected. Connecting."; + *launch_out = 1; + return NULL; + } +} + +/** + * Return text description of the remote endpoint + * + * This function return a test provided by the lower layer of the remote + * endpoint for this channel; it should specify the actual address connected + * to/from. + * + * @param chan Channel to describe + * @return Pointer to string description + */ + +const char * +channel_get_actual_remote_descr(channel_t *chan) +{ + tor_assert(chan); + tor_assert(!(chan->is_listener)); + tor_assert(chan->u.cell_chan.get_remote_descr); + + /* Param 1 indicates the actual description */ + return chan->u.cell_chan.get_remote_descr(chan, 1); +} + +/** + * Return text description of the remote endpoint canonical address + * + * This function return a test provided by the lower layer of the remote + * endpoint for this channel; it should use the known canonical address for + * this OR's identity digest if possible. + * + * @param chan Channel to describe + * @return Pointer to string description + */ + +const char * +channel_get_canonical_remote_descr(channel_t *chan) +{ + tor_assert(chan); + tor_assert(!(chan->is_listener)); + tor_assert(chan->u.cell_chan.get_remote_descr); + + /* Param 0 indicates the canonicalized description */ + return chan->u.cell_chan.get_remote_descr(chan, 0); +} + +/** + * Get remote address if possible + * + * Write the remote address out to a tor_addr_t if the underlying transport + * supports this operation. + * + * @param chan Channel to request remote address from + * @param addr_out Write the address out here + * @return 1 if successful, 0 if not + */ + +int +channel_get_addr_if_possible(channel_t *chan, tor_addr_t *addr_out) +{ + tor_assert(chan); + tor_assert(!(chan->is_listener)); + tor_assert(addr_out); + + if (chan->u.cell_chan.get_remote_addr) + return chan->u.cell_chan.get_remote_addr(chan, addr_out); + /* Else no support, method not implemented */ + else return 0; +} + +/** + * Check if there are outgoing queue writes on this channel + * + * Indicate if either we have queued cells, or if not, whether the underlying + * lower-layer transport thinks it has an output queue. + * + * @param chan Channel to query + * @return 1 if there are queued writes, 0 otherwise + */ + +int +channel_has_queued_writes(channel_t *chan) +{ + int has_writes = 0; + + tor_assert(chan); + tor_assert(!(chan->is_listener)); + tor_assert(chan->u.cell_chan.has_queued_writes); + + if (chan->u.cell_chan.outgoing_queue && + smartlist_len(chan->u.cell_chan.outgoing_queue) > 0) { + has_writes = 1; + } else { + /* Check with the lower layer */ + has_writes = chan->u.cell_chan.has_queued_writes(chan); + } + + return has_writes; +} + +/** + * Check the is_bad_for_new_circs flag + * + * This function returns the is_bad_for_new_circs flag of the specified + * channel. + * + * @param chan Channel to get flag on + * @return Flag value + */ + +int +channel_is_bad_for_new_circs(channel_t *chan) +{ + tor_assert(chan); + tor_assert(!(chan->is_listener)); + + return chan->u.cell_chan.is_bad_for_new_circs; +} + +/** + * Mark a channel as bad for new circuits + * + * Set the is_bad_for_new_circs_flag on chan. + * + * @param chan Channel to mark + */ + +void +channel_mark_bad_for_new_circs(channel_t *chan) +{ + tor_assert(chan); + tor_assert(!(chan->is_listener)); + + chan->u.cell_chan.is_bad_for_new_circs = 1; +} + +/** + * Get the client flag + * + * This returns the client flag of a channel, which will be set if + * command_process_create_cell() in command.c thinks this is a connection + * from a client. + * + * @param chan Channel to query flag + * @return Flag value + */ + +int +channel_is_client(channel_t *chan) +{ + tor_assert(chan); + tor_assert(!(chan->is_listener)); + + return chan->u.cell_chan.is_client; +} + +/** + * Set the client flag + * + * Mark a channel as being from a client + * + * @param chan Channel to mark + */ + +void +channel_mark_client(channel_t *chan) +{ + tor_assert(chan); + tor_assert(!(chan->is_listener)); + + chan->u.cell_chan.is_client = 1; +} + +/** + * Get the canonical flag for a channel + * + * This returns the is_canonical for a channel; this flag is determined by + * the lower layer and can't be set in a transport-independent way. + * + * @param chan Channel to query + * @return Flag value + */ + +int +channel_is_canonical(channel_t *chan) +{ + tor_assert(chan); + tor_assert(!(chan->is_listener)); + tor_assert(chan->u.cell_chan.is_canonical); + + return chan->u.cell_chan.is_canonical(chan, 0); +} + +/** + * Test if the canonical flag is reliable + * + * This function asks if the lower layer thinks it's safe to trust the + * result of channel_is_canonical() + * + * @param chan Channel to query + * @return 1 if the lower layer thinks the is_canonical flag is reliable, 0 + * otherwise. + */ + +int +channel_is_canonical_is_reliable(channel_t *chan) +{ + tor_assert(chan); + tor_assert(!(chan->is_listener)); + tor_assert(chan->u.cell_chan.is_canonical); + + return chan->u.cell_chan.is_canonical(chan, 1); +} + +/** + * Test incoming flag + * + * This function gets the incoming flag; this is set when a listener spawns + * a channel. If this returns true the channel was remotely initiated. + * + * @param chan Channel to query + * @return Flag value + */ + +int +channel_is_incoming(channel_t *chan) +{ + tor_assert(chan); + tor_assert(!(chan->is_listener)); + + return chan->u.cell_chan.is_incoming; +} + +/** + * Set the incoming flag + * + * This function is called when a channel arrives on a listening channel + * to mark it as incoming. + * + * @param chan Channel to mark + */ + +void +channel_mark_incoming(channel_t *chan) +{ + tor_assert(chan); + tor_assert(!(chan->is_listener)); + + chan->u.cell_chan.is_incoming = 1; +} + +/** + * Test local flag + * + * This function gets the local flag; the lower layer should set this when + * setting up the channel if is_local_addr() is true for all of the + * destinations it will communicate with on behalf of this channel. It's + * used to decide whether to declare the network reachable when seeing incoming + * traffic on the channel. + * + * @param chan Channel to query + * @return Flag value + */ + +int +channel_is_local(channel_t *chan) +{ + tor_assert(chan); + tor_assert(!(chan->is_listener)); + + return chan->u.cell_chan.is_local; +} + +/** + * Set the local flag + * + * This internal-only function should be called by the lower layer if the + * channel is to a local address. See channel_is_local() above or the + * description of the is_local bit in channel.h + * + * @param chan Channel to mark + */ + +void +channel_mark_local(channel_t *chan) +{ + tor_assert(chan); + tor_assert(!(chan->is_listener)); + + chan->u.cell_chan.is_local = 1; +} + +/** + * Test outgoing flag + * + * This function gets the outgoing flag; this is the inverse of the incoming + * bit set when a listener spawns a channel. If this returns true the channel + * was locally initiated. + * + * @param chan Channel to query + * @return Flag value + */ + +int +channel_is_outgoing(channel_t *chan) +{ + tor_assert(chan); + tor_assert(!(chan->is_listener)); + + return !(chan->u.cell_chan.is_incoming); +} + +/** + * Mark a channel as outgoing + * + * This function clears the incoming flag and thus marks a channel as + * outgoing. + * + * @param chan Channel to mark + */ + +void +channel_mark_outgoing(channel_t *chan) +{ + tor_assert(chan); + tor_assert(!(chan->is_listener)); + + chan->u.cell_chan.is_incoming = 0; +} + +/********************* + * Timestamp updates * + ********************/ + +/** + * Update the created timestamp + * + * This updates the channel's created timestamp and should only be called + * from channel_init(). + * + * @param chan Channel to update + */ + +void +channel_timestamp_created(channel_t *chan) +{ + time_t now = time(NULL); + + tor_assert(chan); + + chan->timestamp_created = now; +} + +/** + * Update the last active timestamp. + * + * This function updates the channe's last active timestamp; it should be + * called by the lower layer whenever there is activity on the channel which + * does not lead to a cell being transmitted or received; the active timestamp + * is also updated from channel_timestamp_recv() and channel_timestamp_xmit(), + * but it should be updated for things like the v3 handshake and stuff that + * produce activity only visible to the lower layer. + * + * @param chan Channel to update + */ + +void +channel_timestamp_active(channel_t *chan) +{ + time_t now = time(NULL); + + tor_assert(chan); + + chan->timestamp_active = now; +} + +/** + * Update client timestamp + * + * This function is called by relay.c to timestamp a channel that appears to + * be used as a client. + * + * @param chan Channel to update + */ + +void +channel_timestamp_client(channel_t *chan) +{ + time_t now = time(NULL); + + tor_assert(chan); + tor_assert(!(chan->is_listener)); + + chan->u.cell_chan.timestamp_client = now; +} + +/** + * Update the last drained timestamp + * + * This is called whenever we transmit a cell which leaves the outgoing cell + * queue completely empty. It also updates the xmit time and the active time. + * + * @param chan Channel to update + */ + +void +channel_timestamp_drained(channel_t *chan) +{ + time_t now = time(NULL); + + tor_assert(chan); + tor_assert(!(chan->is_listener)); + + chan->timestamp_active = now; + chan->u.cell_chan.timestamp_drained = now; + chan->u.cell_chan.timestamp_xmit = now; +} + +/** + * Update the recv timestamp + * + * This is called whenever we get an incoming cell from the lower layer. + * This also updates the active timestamp. + * + * @param chan Channel to update + */ + +void +channel_timestamp_recv(channel_t *chan) +{ + time_t now = time(NULL); + + tor_assert(chan); + tor_assert(!(chan->is_listener)); + + chan->timestamp_active = now; + chan->u.cell_chan.timestamp_recv = now; +} + +/** + * Update the xmit timestamp + * This is called whenever we pass an outgoing cell to the lower layer. This + * also updates the active timestamp. + * + * @param chan Channel to update + */ + +void +channel_timestamp_xmit(channel_t *chan) +{ + time_t now = time(NULL); + + tor_assert(chan); + tor_assert(!(chan->is_listener)); + + chan->timestamp_active = now; + chan->u.cell_chan.timestamp_xmit = now; +} + +/*************************************************************** + * Timestamp queries - see above for definitions of timestamps * + **************************************************************/ + +/** + * Query created timestamp + * + * @param chan Channel to query + * @return Created timestamp value for chan + */ + +time_t +channel_when_created(channel_t *chan) +{ + tor_assert(chan); + + return chan->timestamp_created; +} + +/** + * Query last active timestamp + * + * @param chan Channel to query + * @return Last active timestamp value for chan + */ + +time_t +channel_when_last_active(channel_t *chan) +{ + tor_assert(chan); + + return chan->timestamp_active; +} + +/** + * Query client timestamp + * + * @param chan Channel to query + * @return Client timestamp value for chan + */ + +time_t +channel_when_last_client(channel_t *chan) +{ + tor_assert(chan); + tor_assert(!(chan->is_listener)); + + return chan->u.cell_chan.timestamp_client; +} + +/** + * Query drained timestamp + * + * @param chan Channel to query + * @return drained timestamp value for chan + */ + +time_t +channel_when_last_drained(channel_t *chan) +{ + tor_assert(chan); + tor_assert(!(chan->is_listener)); + + return chan->u.cell_chan.timestamp_drained; +} + +/** + * Query recv timestamp + * + * @param chan Channel to query + * @return Recv timestamp value for chan + */ + +time_t +channel_when_last_recv(channel_t *chan) +{ + tor_assert(chan); + tor_assert(!(chan->is_listener)); + + return chan->u.cell_chan.timestamp_recv; +} + +/** + * Query xmit timestamp + * + * @param chan Channel to query + * @return Xmit timestamp value for chan + */ + +time_t +channel_when_last_xmit(channel_t *chan) +{ + tor_assert(chan); + tor_assert(!(chan->is_listener)); + + return chan->u.cell_chan.timestamp_xmit; +} + +/** + * Check if a channel matches an extend_info_t + * + * This function calls the lower layer and asks if this channel matches a + * given extend_info_t. + * + * @param chan Channel to test + * @param extend_info Pointer to extend_info_t to match + * @return 1 if they match, 0 otherwise + */ + +int +channel_matches_extend_info(channel_t *chan, extend_info_t *extend_info) +{ + tor_assert(chan); + tor_assert(!(chan->is_listener)); + tor_assert(chan->u.cell_chan.matches_extend_info); + tor_assert(extend_info); + + return chan->u.cell_chan.matches_extend_info(chan, extend_info); +} + +/** + * Check if a channel matches a given target address + * + * This function calls into the lower layer and asks if this channel thinks + * it matches a given target address for circuit extension purposes. + * + * @param chan Channel to test + * @param target Address to match + * @return 1 if they match, 0 otherwise + */ + +int +channel_matches_target_addr_for_extend(channel_t *chan, + const tor_addr_t *target) +{ + tor_assert(chan); + tor_assert(!(chan->is_listener)); + tor_assert(chan->u.cell_chan.matches_target); + tor_assert(target); + + return chan->u.cell_chan.matches_target(chan, target); +} + +/** + * Set up circuit ID generation + * + * This is called when setting up a channel and replaces the old + * connection_or_set_circid_type() + * + * @param chan Channel to set up + * @param identity_rcvd Remote end's identity public key + */ + +void +channel_set_circid_type(channel_t *chan, crypto_pk_t *identity_rcvd) +{ + int started_here; + crypto_pk_t *our_identity; + + tor_assert(chan); + tor_assert(!(chan->is_listener)); + + started_here = channel_is_outgoing(chan); + our_identity = started_here ? + get_tlsclient_identity_key() : get_server_identity_key(); + + if (identity_rcvd) { + if (crypto_pk_cmp_keys(our_identity, identity_rcvd) < 0) { + chan->u.cell_chan.circ_id_type = CIRC_ID_TYPE_LOWER; + } else { + chan->u.cell_chan.circ_id_type = CIRC_ID_TYPE_HIGHER; + } + } else { + chan->u.cell_chan.circ_id_type = CIRC_ID_TYPE_NEITHER; + } +} + diff --git a/src/or/channel.h b/src/or/channel.h new file mode 100644 index 0000000000..18d9a81209 --- /dev/null +++ b/src/or/channel.h @@ -0,0 +1,399 @@ +/* * Copyright (c) 2012, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file channel.h + * \brief Header file for channel.c + **/ + +#ifndef _TOR_CHANNEL_H +#define _TOR_CHANNEL_H + +#include "or.h" + +/* + * Channel struct; see thw channel_t typedef in or.h. A channel is an + * abstract interface for the OR-to-OR connection, similar to connection_or_t, + * but without the strong coupling to the underlying TLS implementation. They + * are constructed by calling a protocol-specific function to open a channel + * to a particular node, and once constructed support the abstract operations + * defined below. + */ + +struct channel_s { + /* Current channel state */ + channel_state_t state; + + /* Globally unique ID number for a channel over the lifetime of a Tor + * process. + */ + uint64_t global_identifier; + + /* Should we expect to see this channel in the channel lists? */ + unsigned char registered:1; + + /** Set this if this channel is created in CHANNEL_STATE_LISTEN, so + * lower-layer close methods that see the channel in CHANNEL_STATE_CLOSING + * know. + */ + unsigned int is_listener:1; + + /** Why did we close? + */ + enum { + CHANNEL_NOT_CLOSING = 0, + CHANNEL_CLOSE_REQUESTED, + CHANNEL_CLOSE_FROM_BELOW, + CHANNEL_CLOSE_FOR_ERROR + } reason_for_closing; + + /* Timestamps for both cell channels and listeners */ + time_t timestamp_created; /* Channel created */ + time_t timestamp_active; /* Any activity */ + + /* Methods implemented by the lower layer */ + + /* Free a channel */ + void (*free)(channel_t *); + /* Close an open channel */ + void (*close)(channel_t *); + + union { + struct { + /* Registered listen handler to call on incoming connection */ + void (*listener)(channel_t *, channel_t *); + + /* List of pending incoming connections */ + smartlist_t *incoming_list; + } listener; + struct { + /* Registered handlers for incoming cells */ + void (*cell_handler)(channel_t *, cell_t *); + void (*var_cell_handler)(channel_t *, var_cell_t *); + + /* Methods implemented by the lower layer */ + + /* + * Ask the underlying transport what the remote endpoint address is, in + * a tor_addr_t. This is optional and subclasses may leave this NULL. + * If they implement it, they should write the address out to the + * provided tor_addr_t *, and return 1 if successful or 0 if no address + * available. + */ + int (*get_remote_addr)(channel_t *, tor_addr_t *); + /* + * Get a text description of the remote endpoint; canonicalized if the + * arg is 0, or the one we originally connected to/received from if it's + * 1. + */ + const char * (*get_remote_descr)(channel_t *, int); + /* Check if the lower layer has queued writes */ + int (*has_queued_writes)(channel_t *); + /* + * If the second param is zero, ask the lower layer if this is + * 'canonical', for a transport-specific definition of canonical; if + * it is 1, ask if the answer to the preceding query is safe to rely + * on. + */ + int (*is_canonical)(channel_t *, int); + /* Check if this channel matches a specified extend_info_t */ + int (*matches_extend_info)(channel_t *, extend_info_t *); + /* Check if this channel matches a target address when extending */ + int (*matches_target)(channel_t *, const tor_addr_t *); + /* Write a cell to an open channel */ + int (*write_cell)(channel_t *, cell_t *); + /* Write a packed cell to an open channel */ + int (*write_packed_cell)(channel_t *, packed_cell_t *); + /* Write a variable-length cell to an open channel */ + int (*write_var_cell)(channel_t *, var_cell_t *); + + /* + * Hash of the public RSA key for the other side's identity key, or + * zeroes if the other side hasn't shown us a valid identity key. + */ + char identity_digest[DIGEST_LEN]; + /* Nickname of the OR on the other side, or NULL if none. */ + char *nickname; + + /* + * Linked list of channels with the same identity digest, for the + * digest->channel map + */ + channel_t *next_with_same_id, *prev_with_same_id; + + /* List of incoming cells to handle */ + smartlist_t *cell_queue; + + /* List of queued outgoing cells */ + smartlist_t *outgoing_queue; + + /* + * When we last used this conn for any client traffic. If not + * recent, we can rate limit it further. + */ + time_t client_used; + + /* Circuit stuff for use by relay.c */ + + /* + * Double-linked ring of circuits with queued cells waiting for room to + * free up on this connection's outbuf. Every time we pull cells from + * a circuit, we advance this pointer to the next circuit in the ring. + */ + struct circuit_t *active_circuits; + /* + * Priority queue of cell_ewma_t for circuits with queued cells waiting + * for room to free up on this connection's outbuf. Kept in heap order + * according to EWMA. + * + * This is redundant with active_circuits; if we ever decide only to use + * the cell_ewma algorithm for choosing circuits, we can remove + * active_circuits. + */ + smartlist_t *active_circuit_pqueue; + /* + * The tick on which the cell_ewma_ts in active_circuit_pqueue last had + * their ewma values rescaled. + */ + unsigned active_circuit_pqueue_last_recalibrated; + + /* Circuit ID generation stuff for use by circuitbuild.c */ + + /* + * When we send CREATE cells along this connection, which half of the + * space should we use? + */ + circ_id_type_t circ_id_type:2; + /* + * Which circ_id do we try to use next on this connection? This is + * always in the range 0..1<<15-1. + */ + circid_t next_circ_id; + + /* How many circuits use this connection as p_chan or n_chan? */ + int n_circuits; + + /* + * True iff this channel shouldn't get any new circs attached to it, + * because the connection is too old, or because there's a better one. + * More generally, this flag is used to note an unhealthy connection; + * for example, if a bad connection fails we shouldn't assume that the + * router itself has a problem. + */ + unsigned int is_bad_for_new_circs:1; + + /** True iff we have decided that the other end of this connection + * is a client. Channels with this flag set should never be used + * to satisfy an EXTEND request. */ + unsigned int is_client:1; + + /** Set if the channel was initiated remotely (came from a listener) */ + unsigned int is_incoming:1; + + /** Set by lower layer if this is local; i.e., everything it communicates + * with for this channel returns true for is_local_addr(). This is used + * to decide whether to declare reachability when we receive something on + * this channel in circuitbuild.c + */ + unsigned int is_local:1; + + /** Channel timestamps for cell channels */ + time_t timestamp_client; /* Client used this, according to relay.c */ + time_t timestamp_drained; /* Output queue empty */ + time_t timestamp_recv; /* Cell received from lower layer */ + time_t timestamp_xmit; /* Cell sent to lower layer */ + + /* Timestamp for relay.c */ + time_t timestamp_last_added_nonpadding; + + /** Unique ID for measuring direct network status requests;vtunneled ones + * come over a circuit_t, which has a dirreq_id field as well, but is a + * distinct namespace. */ + uint64_t dirreq_id; + } cell_chan; + } u; +}; + +/* Channel state manipulations */ + +int channel_state_is_valid(channel_state_t state); +int channel_state_can_transition(channel_state_t from, channel_state_t to); +const char * channel_state_to_string(channel_state_t state); + +/* Abstract channel operations */ + +void channel_request_close(channel_t *chan); +void channel_write_cell(channel_t *chan, cell_t *cell); +void channel_write_packed_cell(channel_t *chan, packed_cell_t *cell); +void channel_write_var_cell(channel_t *chan, var_cell_t *cell); + +/* Channel callback registrations */ + +/* Listener callback */ +void (* channel_get_listener(channel_t *chan))(channel_t *, channel_t *); +void channel_set_listener(channel_t *chan, + void (*listener)(channel_t *, channel_t *) ); + +/* Incoming cell callbacks */ +void (* channel_get_cell_handler(channel_t *chan)) + (channel_t *, cell_t *); +void (* channel_get_var_cell_handler(channel_t *chan)) + (channel_t *, var_cell_t *); +void channel_set_cell_handler(channel_t *chan, + void (*cell_handler)(channel_t *, cell_t *)); +void channel_set_cell_handlers(channel_t *chan, + void (*cell_handler)(channel_t *, cell_t *), + void (*var_cell_handler)(channel_t *, + var_cell_t *)); +void channel_set_var_cell_handler(channel_t *chan, + void (*var_cell_handler)(channel_t *, + var_cell_t *)); + +/* Clean up closed channels periodically; called from run_scheduled_events() + * in main.c + */ +void channel_run_cleanup(void); + +/* Close all channels and deallocate everything */ +void channel_free_all(void); + +#ifdef _TOR_CHANNEL_INTERNAL + +/* Channel operations for subclasses and internal use only */ + +/* Initialize a newly allocated channel - do this first in subclass + * constructors. + */ + +void channel_init_for_cells(channel_t *chan); +void channel_init_listener(channel_t *chan); + +/* Channel registration/unregistration */ +void channel_register(channel_t *chan); +void channel_unregister(channel_t *chan); + +/* Close from below */ +void channel_close_from_lower_layer(channel_t *chan); +void channel_close_for_error(channel_t *chan); +void channel_closed(channel_t *chan); + +/* Free a channel */ +void channel_free(channel_t *chan); +void channel_force_free(channel_t *chan); + +/* State/metadata setters */ + +void channel_change_state(channel_t *chan, channel_state_t to_state); +void channel_clear_identity_digest(channel_t *chan); +void channel_clear_remote_end(channel_t *chan); +void channel_mark_local(channel_t *chan); +void channel_mark_incoming(channel_t *chan); +void channel_mark_outgoing(channel_t *chan); +void channel_set_identity_digest(channel_t *chan, + const char *identity_digest); +void channel_set_remote_end(channel_t *chan, + const char *identity_digest, + const char *nickname); + +/* Timestamp updates */ +void channel_timestamp_created(channel_t *chan); +void channel_timestamp_active(channel_t *chan); +void channel_timestamp_drained(channel_t *chan); +void channel_timestamp_recv(channel_t *chan); +void channel_timestamp_xmit(channel_t *chan); + +/* Incoming channel handling */ +void channel_process_incoming(channel_t *listener); +void channel_queue_incoming(channel_t *listener, channel_t *incoming); + +/* Incoming cell handling */ +void channel_process_cells(channel_t *chan); +void channel_queue_cell(channel_t *chan, cell_t *cell); +void channel_queue_var_cell(channel_t *chan, var_cell_t *var_cell); + +/* Outgoing cell handling */ +void channel_flush_cells(channel_t *chan); + +/* Request from lower layer for more cells if available */ +ssize_t channel_flush_some_cells(channel_t *chan, ssize_t num_cells); + +/* Query if data available on this channel */ +int channel_more_to_flush(channel_t *chan); + +/* Notify flushed outgoing for dirreq handling */ +void channel_notify_flushed(channel_t *chan); + +/* Handle stuff we need to do on open like notifying circuits */ +void channel_do_open_actions(channel_t *chan); + +#endif + +/* Helper functions to perform operations on channels */ + +int channel_send_destroy(circid_t circ_id, channel_t *chan, + int reason); + +/* + * Outside abstract interfaces that should eventually get turned into + * something transport/address format independent. + */ + +channel_t * channel_connect(const tor_addr_t *addr, uint16_t port, + const char *id_digest); + +channel_t * channel_get_for_extend(const char *digest, + const tor_addr_t *target_addr, + const char **msg_out, + int *launch_out); + +/* Ask which of two channels is better for circuit-extension purposes */ +int channel_is_better(time_t now, + channel_t *a, channel_t *b, + int forgive_new_connections); + +/** Channel lookups + */ + +channel_t * channel_find_by_global_id(uint64_t global_identifier); +channel_t * channel_find_by_remote_digest(const char *identity_digest); +channel_t * channel_find_by_remote_nickname(const char *nickname); + +/** For things returned by channel_find_by_remote_digest(), walk the list. + */ + +channel_t * channel_next_with_digest(channel_t *chan); +channel_t * channel_prev_with_digest(channel_t *chan); + +/* + * Metadata queries/updates + */ + +const char * channel_get_actual_remote_descr(channel_t *chan); +int channel_get_addr_if_possible(channel_t *chan, tor_addr_t *addr_out); +const char * channel_get_canonical_remote_descr(channel_t *chan); +int channel_has_queued_writes(channel_t *chan); +int channel_is_bad_for_new_circs(channel_t *chan); +void channel_mark_bad_for_new_circs(channel_t *chan); +int channel_is_canonical(channel_t *chan); +int channel_is_canonical_is_reliable(channel_t *chan); +int channel_is_client(channel_t *chan); +int channel_is_local(channel_t *chan); +int channel_is_incoming(channel_t *chan); +int channel_is_outgoing(channel_t *chan); +void channel_mark_client(channel_t *chan); +int channel_matches_extend_info(channel_t *chan, extend_info_t *extend_info); +int channel_matches_target_addr_for_extend(channel_t *chan, + const tor_addr_t *target); +void channel_set_circid_type(channel_t *chan, crypto_pk_t *identity_rcvd); +void channel_timestamp_client(channel_t *chan); + +/* Timestamp queries */ +time_t channel_when_created(channel_t *chan); +time_t channel_when_last_active(channel_t *chan); +time_t channel_when_last_client(channel_t *chan); +time_t channel_when_last_drained(channel_t *chan); +time_t channel_when_last_recv(channel_t *chan); +time_t channel_when_last_xmit(channel_t *chan); + +#endif + diff --git a/src/or/include.am b/src/or/include.am index e9811ec962..b9032d97eb 100644 --- a/src/or/include.am +++ b/src/or/include.am @@ -17,6 +17,7 @@ endif src_or_libtor_a_SOURCES = \ src/or/buffers.c \ + src/or/channel.c \ src/or/circuitbuild.c \ src/or/circuitlist.c \ src/or/circuituse.c \ @@ -86,6 +87,7 @@ src_or_tor_LDADD = src/or/libtor.a src/common/libor.a src/common/libor-crypto.a ORHEADERS = \ src/or/buffers.h \ + src/or/channel.h \ src/or/circuitbuild.h \ src/or/circuitlist.h \ src/or/circuituse.h \ diff --git a/src/or/or.h b/src/or/or.h index f4e1c5eae0..a916ac86e5 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -1202,29 +1202,19 @@ typedef struct or_connection_t { int tls_error; /**< Last tor_tls error code. */ /** When we last used this conn for any client traffic. If not * recent, we can rate limit it further. */ - time_t client_used; tor_addr_t real_addr; /**< The actual address that this connection came from * or went to. The addr field is prone to * getting overridden by the address from the router * descriptor matching identity_digest. */ - circ_id_type_t circ_id_type:2; /**< When we send CREATE cells along this - * connection, which half of the space should - * we use? */ /** Should this connection be used for extending circuits to the server * matching the identity_digest field? Set to true if we're pretty * sure we aren't getting MITMed, either because we're connected to an * address listed in a server descriptor, or because an authenticated * NETINFO cell listed the address we're connected to as recognized. */ unsigned int is_canonical:1; - /** True iff this connection shouldn't get any new circs attached to it, - * because the connection is too old, or because there's a better one. - * More generally, this flag is used to note an unhealthy connection; - * for example, if a bad connection fails we shouldn't assume that the - * router itself has a problem. - */ - unsigned int is_bad_for_new_circs:1; + /** True iff we have decided that the other end of this connection * is a client. Connections with this flag set should never be used * to satisfy an EXTEND request. */ @@ -1234,9 +1224,6 @@ typedef struct or_connection_t { unsigned int proxy_type:2; /**< One of PROXY_NONE...PROXY_SOCKS5 */ uint8_t link_proto; /**< What protocol version are we using? 0 for * "none negotiated yet." */ - circid_t next_circ_id; /**< Which circ_id do we try to use next on - * this connection? This is always in the - * range 0..1<<15-1. */ or_handshake_state_t *handshake_state; /**< If we are setting this connection * up, state information to do so. */ @@ -1261,21 +1248,6 @@ typedef struct or_connection_t { int n_circuits; /**< How many circuits use this connection as p_conn or * n_conn ? */ - /** Double-linked ring of circuits with queued cells waiting for room to - * free up on this connection's outbuf. Every time we pull cells from a - * circuit, we advance this pointer to the next circuit in the ring. */ - struct circuit_t *active_circuits; - /** Priority queue of cell_ewma_t for circuits with queued cells waiting for - * room to free up on this connection's outbuf. Kept in heap order - * according to EWMA. - * - * This is redundant with active_circuits; if we ever decide only to use the - * cell_ewma algorithm for choosing circuits, we can remove active_circuits. - */ - smartlist_t *active_circuit_pqueue; - /** The tick on which the cell_ewma_ts in active_circuit_pqueue last had - * their ewma values rescaled. */ - unsigned active_circuit_pqueue_last_recalibrated; struct or_connection_t *next_with_same_id; /**< Next connection with same * identity digest as this one. */ } or_connection_t; @@ -1548,6 +1520,98 @@ static INLINE listener_connection_t *TO_LISTENER_CONN(connection_t *c) return DOWNCAST(listener_connection_t, c); } +/* channel_t typedef; struct channel_s is in channel.h */ + +typedef struct channel_s channel_t; + +/* channel states for channel_t */ + +typedef enum { + /* + * Closed state - channel is inactive + * + * Permitted transitions from: + * - CHANNEL_STATE_CLOSING + * Permitted transitions to: + * - CHANNEL_STATE_LISTENING + * - CHANNEL_STATE_OPENING + */ + CHANNEL_STATE_CLOSED = 0, + /* + * Listening state - channel is listening for incoming connections + * + * Permitted transitions from: + * - CHANNEL_STATE_CLOSED + * Permitted transitions to: + * - CHANNEL_STATE_CLOSING + * - CHANNEL_STATE_ERROR + */ + CHANNEL_STATE_LISTENING, + /* + * Opening state - channel is trying to connect + * + * Permitted transitions from: + * - CHANNEL_STATE_CLOSED + * Permitted transitions to: + * - CHANNEL_STATE_CLOSING + * - CHANNEL_STATE_ERROR + * - CHANNEL_STATE_OPEN + */ + CHANNEL_STATE_OPENING, + /* + * Open state - channel is active and ready for use + * + * Permitted transitions from: + * - CHANNEL_STATE_MAINT + * - CHANNEL_STATE_OPENING + * Permitted transitions to: + * - CHANNEL_STATE_CLOSING + * - CHANNEL_STATE_ERROR + * - CHANNEL_STATE_MAINT + */ + CHANNEL_STATE_OPEN, + /* + * Maintenance state - channel is temporarily offline for subclass specific + * maintenance activities such as TLS renegotiation. + * + * Permitted transitions from: + * - CHANNEL_STATE_OPEN + * Permitted transitions to: + * - CHANNEL_STATE_CLOSING + * - CHANNEL_STATE_ERROR + * - CHANNEL_STATE_OPEN + */ + CHANNEL_STATE_MAINT, + /* + * Closing state - channel is shutting down + * + * Permitted transitions from: + * - CHANNEL_STATE_MAINT + * - CHANNEL_STATE_OPEN + * Permitted transitions to: + * - CHANNEL_STATE_CLOSED, + * - CHANNEL_STATE_ERROR + */ + CHANNEL_STATE_CLOSING, + /* + * Error state - channel has experienced a permanent error + * + * Permitted transitions from: + * - CHANNEL_STATE_CLOSING + * - CHANNEL_STATE_LISTENING + * - CHANNEL_STATE_MAINT + * - CHANNEL_STATE_OPENING + * - CHANNEL_STATE_OPEN + * Permitted transitions to: + * - None + */ + CHANNEL_STATE_ERROR, + /* + * Placeholder for maximum state value + */ + CHANNEL_STATE_LAST +} channel_state_t; + /* Conditional macros to help write code that works whether bufferevents are disabled or not. @@ -2478,8 +2542,8 @@ typedef struct { /** The EWMA of the cell count. */ double cell_count; /** True iff this is the cell count for a circuit's previous - * connection. */ - unsigned int is_for_p_conn : 1; + * channel. */ + unsigned int is_for_p_chan : 1; /** The position of the circuit within the OR connection's priority * queue. */ int heap_index; @@ -2589,7 +2653,7 @@ typedef struct circuit_t { uint64_t dirreq_id; /** The EWMA count for the number of cells flushed from the - * n_conn_cells queue. Used to determine which circuit to flush from next. + * n_chan_cells queue. Used to determine which circuit to flush from next. */ cell_ewma_t n_cell_ewma; } circuit_t; -- cgit v1.2.3-54-g00ecf From 7f952da55334d3a3693d1c6e8531fd96730265db Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Wed, 1 Aug 2012 02:38:43 -0700 Subject: Fix make check-spaces in circuitbuild.c and router.h --- src/or/router.h | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/or/router.h b/src/or/router.h index c43c308496..39640855e0 100644 --- a/src/or/router.h +++ b/src/or/router.h @@ -86,13 +86,18 @@ int router_pick_published_address(const or_options_t *options, uint32_t *addr); int router_rebuild_descriptor(int force); int router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router, crypto_pk_t *ident_key); -int extrainfo_dump_to_string(char **s, extrainfo_t *extrainfo, - crypto_pk_t *ident_key); void router_get_prim_orport(const routerinfo_t *router, - tor_addr_port_t *ap_out); + tor_addr_port_t *addr_port_out); +void router_get_pref_orport(const routerinfo_t *router, + tor_addr_port_t *addr_port_out); +void router_get_pref_ipv6_orport(const routerinfo_t *router, + tor_addr_port_t *addr_port_out); +int router_ipv6_preferred(const routerinfo_t *router); int router_has_addr(const routerinfo_t *router, const tor_addr_t *addr); int router_has_orport(const routerinfo_t *router, const tor_addr_port_t *orport); +int extrainfo_dump_to_string(char **s, extrainfo_t *extrainfo, + crypto_pk_t *ident_key); int is_legal_nickname(const char *s); int is_legal_nickname_or_hexdigest(const char *s); int is_legal_hexdigest(const char *s); -- cgit v1.2.3-54-g00ecf From 15303c32ec9d84aff8de5ed9df28e779c36c6e5c Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Wed, 1 Aug 2012 04:18:42 -0700 Subject: Initial channeltls.c/channeltls.h for bug 6465 --- src/or/Makefile.nmake | 2 +- src/or/channel.h | 5 + src/or/channeltls.c | 1894 ++++++++++++++++++++++++++++++++++++++++++++++++ src/or/channeltls.h | 50 ++ src/or/connection.c | 80 +- src/or/connection_or.c | 568 ++++++++------- src/or/connection_or.h | 14 +- src/or/include.am | 2 + src/or/or.h | 208 +++--- 9 files changed, 2431 insertions(+), 392 deletions(-) create mode 100644 src/or/channeltls.c create mode 100644 src/or/channeltls.h (limited to 'src') diff --git a/src/or/Makefile.nmake b/src/or/Makefile.nmake index 5915364b6f..b145fdcae6 100644 --- a/src/or/Makefile.nmake +++ b/src/or/Makefile.nmake @@ -8,7 +8,7 @@ LIBS = ..\..\..\build-alpha\lib\libevent.a \ ..\..\..\build-alpha\lib\libz.a \ ws2_32.lib advapi32.lib shell32.lib -LIBTOR_OBJECTS = buffers.obj channel.obj circuitbuild.obj \ +LIBTOR_OBJECTS = buffers.obj channel.obj channeltls.obj circuitbuild.obj \ circuitlist.obj circuituse.obj command.obj config.obj connection.obj connection_edge.obj connection_or.obj control.obj cpuworker.obj \ directory.obj dirserv.obj dirvote.obj dns.obj dnsserv.obj geoip.obj \ diff --git a/src/or/channel.h b/src/or/channel.h index 18d9a81209..242a078532 100644 --- a/src/or/channel.h +++ b/src/or/channel.h @@ -38,6 +38,11 @@ struct channel_s { */ unsigned int is_listener:1; + /** Unique ID for measuring direct network status requests;vtunneled ones + * come over a circuit_t, which has a dirreq_id field as well, but is a + * distinct namespace. */ + uint64_t dirreq_id; + /** Why did we close? */ enum { diff --git a/src/or/channeltls.c b/src/or/channeltls.c new file mode 100644 index 0000000000..352037c14a --- /dev/null +++ b/src/or/channeltls.c @@ -0,0 +1,1894 @@ +/* * Copyright (c) 2012, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file channeltls.c + * \brief channel_t concrete subclass using or_connection_t + **/ + +/* + * Define this so channel.h gives us things only channel_t subclasses + * should touch. + */ + +#define _TOR_CHANNEL_INTERNAL + +#include "or.h" +#include "channel.h" +#include "channeltls.h" +#include "config.h" +#include "connection.h" +#include "connection_or.h" +#include "control.h" +#include "relay.h" +#include "router.h" +#include "routerlist.h" + +/** How many CELL_PADDING cells have we received, ever? */ +uint64_t stats_n_padding_cells_processed = 0; +/** How many CELL_VERSIONS cells have we received, ever? */ +uint64_t stats_n_versions_cells_processed = 0; +/** How many CELL_NETINFO cells have we received, ever? */ +uint64_t stats_n_netinfo_cells_processed = 0; +/** How many CELL_VPADDING cells have we received, ever? */ +uint64_t stats_n_vpadding_cells_processed = 0; +/** How many CELL_CERTS cells have we received, ever? */ +uint64_t stats_n_certs_cells_processed = 0; +/** How many CELL_AUTH_CHALLENGE cells have we received, ever? */ +uint64_t stats_n_auth_challenge_cells_processed = 0; +/** How many CELL_AUTHENTICATE cells have we received, ever? */ +uint64_t stats_n_authenticate_cells_processed = 0; +/** How many CELL_AUTHORIZE cells have we received, ever? */ +uint64_t stats_n_authorize_cells_processed = 0; + +/** Active listener, if any */ +channel_tls_t *channel_tls_listener = NULL; + +/* channel_tls_t method declarations */ + +static void channel_tls_close_method(channel_t *chan); +static int +channel_tls_get_remote_addr_method(channel_t *chan, tor_addr_t *addr_out); +static const char * +channel_tls_get_remote_descr_method(channel_t *chan, int req); +static int channel_tls_has_queued_writes_method(channel_t *chan); +static int channel_tls_is_canonical_method(channel_t *chan, int req); +static int +channel_tls_matches_extend_info_method(channel_t *chan, + extend_info_t *extend_info); +static int channel_tls_matches_target_method(channel_t *chan, + const tor_addr_t *target); +static int channel_tls_write_cell_method(channel_t *chan, + cell_t *cell); +static int channel_tls_write_packed_cell_method(channel_t *chan, + packed_cell_t *packed_cell); +static int channel_tls_write_var_cell_method(channel_t *chan, + var_cell_t *var_cell); + +/** Handle incoming cells for the handshake stuff here rather than + * passing them on up. */ + +static void channel_tls_process_versions_cell(var_cell_t *cell, + channel_tls_t *tlschan); +static void channel_tls_process_netinfo_cell(cell_t *cell, + channel_tls_t *tlschan); +static void channel_tls_process_certs_cell(var_cell_t *cell, + channel_tls_t *tlschan); +static void channel_tls_process_auth_challenge_cell(var_cell_t *cell, + channel_tls_t *tlschan); +static void channel_tls_process_authenticate_cell(var_cell_t *cell, + channel_tls_t *tlschan); +static int command_allowed_before_handshake(uint8_t command); +static int enter_v3_handshake_with_cell(var_cell_t *cell, + channel_tls_t *tlschan); + +/** + * Start a new TLS channel + * + * Launch a new OR connection to addr:port and expect to + * handshake with an OR with identity digest id_digest, and wrap + * it in a channel_tls_t. + * + * @param addr Address to connect on + * @param port Port to connect on + * @param id_digest Identity digest we want + * @return The launched channel, or NULL if it failed. + */ + +channel_t * +channel_tls_connect(const tor_addr_t *addr, uint16_t port, + const char *id_digest) +{ + channel_tls_t *tlschan = tor_malloc_zero(sizeof(*tlschan)); + channel_t *chan = TLS_CHAN_TO_BASE(tlschan); + channel_init_for_cells(chan); + chan->state = CHANNEL_STATE_OPENING; + chan->close = channel_tls_close_method; + chan->u.cell_chan.get_remote_addr = channel_tls_get_remote_addr_method; + chan->u.cell_chan.get_remote_descr = channel_tls_get_remote_descr_method; + chan->u.cell_chan.has_queued_writes = channel_tls_has_queued_writes_method; + chan->u.cell_chan.is_canonical = channel_tls_is_canonical_method; + chan->u.cell_chan.matches_extend_info = + channel_tls_matches_extend_info_method; + chan->u.cell_chan.matches_target = channel_tls_matches_target_method; + chan->u.cell_chan.write_cell = channel_tls_write_cell_method; + chan->u.cell_chan.write_packed_cell = channel_tls_write_packed_cell_method; + chan->u.cell_chan.write_var_cell = channel_tls_write_var_cell_method; + + log_debug(LD_CHANNEL, + "In channel_tls_connect() for channel %p (global id %lu)", + tlschan, chan->global_identifier); + + if (is_local_addr(addr)) channel_mark_local(chan); + channel_mark_outgoing(chan); + + chan->u.cell_chan.active_circuit_pqueue = smartlist_new(); + chan->u.cell_chan.active_circuit_pqueue_last_recalibrated = + cell_ewma_get_tick(); + + /* Set up or_connection stuff */ + connection_or_connect(addr, port, id_digest, tlschan); + /* connection_or_connect() will fill in tlschan->conn */ + if (!(tlschan->conn)) { + channel_change_state(chan, CHANNEL_STATE_ERROR); + goto err; + } + + log_debug(LD_CHANNEL, + "Got orconn %p for channel with global id %lu", + tlschan->conn, chan->global_identifier); + + goto done; + + err: + smartlist_free(chan->u.cell_chan.active_circuit_pqueue); + tor_free(tlschan); + chan = NULL; + + done: + /* If we got one, we should register it */ + if (chan) channel_register(chan); + + return chan; +} + +/** + * Return the current channel_tls_t listener + * + * Returns the current listening channel for incoming TLS connections, or + * NULL if none has been established + * + * @return TLS listener + */ + +channel_t * +channel_tls_get_listener(void) +{ + return TLS_CHAN_TO_BASE(channel_tls_listener); +} + +/** + * Start a channel_tls_t listener if necessary + * + * Return the current channel_tls_t listener, or start one if we haven't yet, + * and return that. + * + * @return TLS listener + */ + +channel_t * +channel_tls_start_listener(void) +{ + channel_tls_t *listener; + channel_t *lchan; + + if (!channel_tls_listener) { + listener = tor_malloc_zero(sizeof(*listener)); + lchan = TLS_CHAN_TO_BASE(listener); + channel_init_listener(lchan); + lchan->state = CHANNEL_STATE_LISTENING; + lchan->close = channel_tls_close_method; + + channel_tls_listener = listener; + + log_debug(LD_CHANNEL, + "Starting TLS listener channel %p with global id %lu", + lchan, lchan->global_identifier); + } else lchan = TLS_CHAN_TO_BASE(channel_tls_listener); + + return lchan; +} + +/** + * Free everything on shutdown + * + * Not much to do here, since channel_free_all() takes care of a lot, but let's + * get rid of the listener. + */ + +void +channel_tls_free_all(void) +{ + channel_t *base = NULL; + + log_debug(LD_CHANNEL, + "Shutting down TLS channels..."); + + if (channel_tls_listener) { + base = TLS_CHAN_TO_BASE(channel_tls_listener); + channel_unregister(base); + channel_request_close(base); + channel_free(base); + channel_tls_listener = NULL; + } + + log_debug(LD_CHANNEL, + "Done shutting down TLS channels"); +} + +/** + * Create a new channel around an incoming or_connection_t + * + * @param orconn New or_connection_t + * @return A channel to queue on the TLS listener + */ + +channel_t * +channel_tls_handle_incoming(or_connection_t *orconn) +{ + channel_tls_t *tlschan = tor_malloc_zero(sizeof(*tlschan)); + channel_t *chan = TLS_CHAN_TO_BASE(tlschan); + + tor_assert(orconn); + tor_assert(!(orconn->chan)); + + channel_init_for_cells(chan); + chan->state = CHANNEL_STATE_OPENING; + chan->close = channel_tls_close_method; + chan->u.cell_chan.get_remote_descr = channel_tls_get_remote_descr_method; + chan->u.cell_chan.has_queued_writes = channel_tls_has_queued_writes_method; + chan->u.cell_chan.is_canonical = channel_tls_is_canonical_method; + chan->u.cell_chan.matches_extend_info = + channel_tls_matches_extend_info_method; + chan->u.cell_chan.matches_target = channel_tls_matches_target_method; + chan->u.cell_chan.write_cell = channel_tls_write_cell_method; + chan->u.cell_chan.write_packed_cell = channel_tls_write_packed_cell_method; + chan->u.cell_chan.write_var_cell = channel_tls_write_var_cell_method; + + /* Link the channel and orconn to each other */ + tlschan->conn = orconn; + orconn->chan = tlschan; + + if (is_local_addr(&(TO_CONN(orconn)->addr))) channel_mark_local(chan); + channel_mark_incoming(chan); + + chan->u.cell_chan.active_circuit_pqueue = smartlist_new(); + chan->u.cell_chan.active_circuit_pqueue_last_recalibrated = + cell_ewma_get_tick(); + + /* If we got one, we should register it */ + if (chan) channel_register(chan); + + return chan; +} + +/******************************************** + * Method implementations for channel_tls_t * + *******************************************/ + +/** + * Close a channel_tls_t + * + * This implements the close method for channel_tls_t + * + * @param chan Channel to close + */ + +static void +channel_tls_close_method(channel_t *chan) +{ + channel_tls_t *tlschan = BASE_CHAN_TO_TLS(chan); + channel_t *tmp = NULL; + + tor_assert(tlschan); + + if (chan->is_listener) { + /* + * Listeners we just go ahead and change state through to CLOSED, but + * make sure to check if they're channel_tls_listener to NULL it out. + */ + if (chan == TLS_CHAN_TO_BASE(channel_tls_listener)) + channel_tls_listener = NULL; + + if (!(chan->state == CHANNEL_STATE_CLOSING || + chan->state == CHANNEL_STATE_CLOSED || + chan->state == CHANNEL_STATE_ERROR)) { + channel_change_state(chan, CHANNEL_STATE_CLOSING); + } + + if (chan->u.listener.incoming_list) { + SMARTLIST_FOREACH_BEGIN(chan->u.listener.incoming_list, + channel_t *, ichan) { + tmp = ichan; + SMARTLIST_DEL_CURRENT(chan->u.listener.incoming_list, ichan); + channel_request_close(tmp); + } SMARTLIST_FOREACH_END(ichan); + + smartlist_free(chan->u.listener.incoming_list); + chan->u.listener.incoming_list = NULL; + } + + if (!(chan->state == CHANNEL_STATE_CLOSED || + chan->state == CHANNEL_STATE_ERROR)) { + channel_change_state(chan, CHANNEL_STATE_CLOSED); + } + } else { + if (tlschan->conn) connection_or_close_normally(tlschan->conn, 1); + else { + /* Weird - we'll have to change the state ourselves, I guess */ + log_info(LD_CHANNEL, + "Tried to close channel_tls_t %p with NULL conn", + tlschan); + channel_change_state(chan, CHANNEL_STATE_ERROR); + } + } +} + +/** + * Get the remote address of a channel_tls_t + * + * This implements the get_remote_addr method for channel_tls_t; copy the + * remote endpoint of the channel to addr_out and return 1 (always + * succeeds for this transport). + * + * @param chan Channel to query + * @param addr_out Write the address out here + * @return Always succeeds and returns 1 + */ + +static int +channel_tls_get_remote_addr_method(channel_t *chan, tor_addr_t *addr_out) +{ + channel_tls_t *tlschan = BASE_CHAN_TO_TLS(chan); + + tor_assert(tlschan); + tor_assert(addr_out); + tor_assert(tlschan->conn); + + tor_addr_copy(addr_out, &(TO_CONN(tlschan->conn)->addr)); + + return 1; +} + +/** + * Get endpoint description of a channel_tls_t + * + * This implements the get_remote_descr method for channel_tls_t; it returns + * a text description of the remote endpoint of the channel suitable for use + * in log messages. The req parameter is 0 for the canonical address or 1 for + * the actual address seen. + * + * @param chan Channel to query + * @param req Request type (0 for canonical, 1 for actual) + * @return Pointer to string containing description + */ + +static const char * +channel_tls_get_remote_descr_method(channel_t *chan, int req) +{ +#define MAX_DESCR_LEN 32 + + static char buf[MAX_DESCR_LEN + 1]; + channel_tls_t *tlschan = BASE_CHAN_TO_TLS(chan); + connection_t *conn; + const char *answer = NULL; + char *addr_str; + + tor_assert(tlschan); + tor_assert(tlschan->conn); + + conn = TO_CONN(tlschan->conn); + + switch (req) { + case 0: + /* Canonical address */ + tor_snprintf(buf, MAX_DESCR_LEN + 1, + "%s:%u", conn->address, conn->port); + answer = buf; + break; + case 1: + /* Actual address */ + addr_str = tor_dup_addr(&(tlschan->conn->real_addr)); + tor_snprintf(buf, MAX_DESCR_LEN + 1, + "%s:%u", addr_str, conn->port); + tor_free(addr_str); + answer = buf; + break; + default: + /* Something's broken in channel.c */ + tor_assert(1); + } + + return answer; +} + +/** + * Tell the upper layer if we have queued writes + * + * This implements the has_queued_writes method for channel_tls _t; it returns + * 1 iff we have queued writes on the outbuf of the underlying or_connection_t. + * + * @param chan Channel to query + * @return Whether we have queued writes on the outbuf + */ + +static int +channel_tls_has_queued_writes_method(channel_t *chan) +{ + size_t outbuf_len; + channel_tls_t *tlschan = BASE_CHAN_TO_TLS(chan); + + tor_assert(tlschan); + tor_assert(tlschan->conn); + + outbuf_len = connection_get_outbuf_len(TO_CONN(tlschan->conn)); + + return (outbuf_len > 0); +} + +/** + * Tell the upper layer if we're canonical + * + * This implements the is_canonical method for channel_tls_t; if req is zero, + * it returns whether this is a canonical channel, and if it is one it returns + * whether that can be relied upon. + * + * @param chan Channel to query + * @param req Request type (0 for is_canonical, 1 for is_canonical_reliable) + * @return Query response + */ + +static int +channel_tls_is_canonical_method(channel_t *chan, int req) +{ + int answer = 0; + channel_tls_t *tlschan = BASE_CHAN_TO_TLS(chan); + + tor_assert(tlschan); + tor_assert(tlschan->conn); + + switch (req) { + case 0: + answer = tlschan->conn->is_canonical; + break; + case 1: + /* + * Is the is_canonical bit reliable? In protocols version 2 and up + * we get the canonical address from a NETINFO cell, but in older + * versions it might be based on an obsolete descriptor. + */ + answer = (tlschan->conn->link_proto >= 2); + break; + default: + /* This shouldn't happen; channel.c is broken if it does */ + tor_assert(1); + } + + return answer; +} + +/** + * Check if we match an extend_info_t + * + * This implements the matches_extend_info method for channel_tls_t; the upper + * layer wants to know if this channel matches an extend_info_t. + * + * @param chan Channel to test + * @param extend_info The extend_info_t to match + * @return 1 if this channel matches, 0 otherwise + */ + +static int +channel_tls_matches_extend_info_method(channel_t *chan, + extend_info_t *extend_info) +{ + channel_tls_t *tlschan = BASE_CHAN_TO_TLS(chan); + + tor_assert(tlschan); + tor_assert(extend_info); + + return (tor_addr_eq(&(extend_info->addr), + &(TO_CONN(tlschan->conn)->addr)) && + (extend_info->port == TO_CONN(tlschan->conn)->port)); +} + +/** + * Check if we match a target address + * + * This implements the matches_target method for channel_tls _t; the upper + * layer wants to know if this channel matches a target address when extending + * a circuit. + * + * @param chan Channel to test + * @param target Address to match + * @return 1 if this channel matches, 0 otherwise + */ + +static int +channel_tls_matches_target_method(channel_t *chan, + const tor_addr_t *target) +{ + channel_tls_t *tlschan = BASE_CHAN_TO_TLS(chan); + + tor_assert(tlschan); + tor_assert(target); + tor_assert(tlschan->conn); + + return tor_addr_compare(&(tlschan->conn->real_addr), + target, CMP_EXACT); +} + +/** + * Write a cell to a channel_tls_t + * + * This implements the write_cell method for channel_tls_t; given a + * channel_tls_t and a cell_t, transmit the cell_t. + * + * @param chan Channel to transmit on + * @param cell Cell to transmit + * @return Always succeeds and returns 1 + */ + +static int +channel_tls_write_cell_method(channel_t *chan, cell_t *cell) +{ + channel_tls_t *tlschan = BASE_CHAN_TO_TLS(chan); + + tor_assert(tlschan); + tor_assert(cell); + tor_assert(tlschan->conn); + + connection_or_write_cell_to_buf(cell, tlschan->conn); + + return 1; +} + +/** + * Write a packed cell to a channel_tls_t + * + * This implements the write_packed_cell method for channel_tls_t; given a + * channel_tls_t and a packed_cell_t, transmit the packed_cell_t. + * + * @param chan Channel to transmit on + * @param packed_cell Cell to transmit + * @return Always succeeds and returns 1 + */ + +static int +channel_tls_write_packed_cell_method(channel_t *chan, + packed_cell_t *packed_cell) +{ + channel_tls_t *tlschan = BASE_CHAN_TO_TLS(chan); + + tor_assert(tlschan); + tor_assert(packed_cell); + tor_assert(tlschan->conn); + + connection_write_to_buf(packed_cell->body, CELL_NETWORK_SIZE, + TO_CONN(tlschan->conn)); + + /* This is where the cell is finished; used to be done from relay.c */ + packed_cell_free(packed_cell); + + return 1; +} + +/** + * Write a variable-length cell to a channel_tls_t + * + * This implements the write_var_cell method for channel_tls_t; given a + * channel_tls_t and a var_cell_t, transmit the var_cell_t. + * + * @param chan Channel to transmit on + * @param var_cell Cell to transmit + * @return Always succeeds and returns 1 + */ + +static int +channel_tls_write_var_cell_method(channel_t *chan, var_cell_t *var_cell) +{ + channel_tls_t *tlschan = BASE_CHAN_TO_TLS(chan); + + tor_assert(tlschan); + tor_assert(var_cell); + tor_assert(tlschan->conn); + + connection_or_write_var_cell_to_buf(var_cell, tlschan->conn); + + return 1; +} + +/******************************************************* + * Functions for handling events on an or_connection_t * + ******************************************************/ + +/** + * Handle orconn state changes + * + * This function will be called by connection_or.c when the or_connection_t + * associated with this channel_tls_t changes state. + * + * @param chan Channel controlling the or_connection_t + * @param conn The or_connection_t changing state + * @param old_state The old state of conn + * @param state The new state of conn + */ + +void +channel_tls_handle_state_change_on_orconn(channel_tls_t *chan, + or_connection_t *conn, + uint8_t old_state, + uint8_t state) +{ + channel_t *base_chan; + + tor_assert(chan); + tor_assert(conn); + tor_assert(conn->chan == chan); + tor_assert(chan->conn == conn); + /* -Werror appeasement */ + tor_assert(old_state == old_state); + + base_chan = TLS_CHAN_TO_BASE(chan); + + /* Make sure the base connection state makes sense - shouldn't be error, + * closed or listening. */ + + tor_assert(base_chan->state == CHANNEL_STATE_OPENING || + base_chan->state == CHANNEL_STATE_OPEN || + base_chan->state == CHANNEL_STATE_MAINT || + base_chan->state == CHANNEL_STATE_CLOSING); + + /* Did we just go to state open? */ + if (state == OR_CONN_STATE_OPEN) { + /* + * We can go to CHANNEL_STATE_OPEN from CHANNEL_STATE_OPENING or + * CHANNEL_STATE_MAINT on this. + */ + channel_change_state(base_chan, CHANNEL_STATE_OPEN); + } else { + /* + * Not open, so from CHANNEL_STATE_OPEN we go to CHANNEL_STATE_MAINT, + * otherwise no change. + */ + if (base_chan->state == CHANNEL_STATE_OPEN) { + channel_change_state(base_chan, CHANNEL_STATE_MAINT); + } + } +} + +/** + * Try to flush cells from a channel_tls_t + * + * Try to flush up to about num_cells cells, and return how many we flushed. + * + * @param chan Channel to flush + * @param num_cells Maximum number of cells + * @return Number of cells actually flushed + */ + +ssize_t +channel_tls_flush_some_cells(channel_tls_t *chan, ssize_t num_cells) +{ + ssize_t flushed = 0; + + tor_assert(chan); + + if (flushed >= num_cells) goto done; + + /* + * If channel_tls_t ever buffers anything below the channel_t layer, flush + * that first here. + */ + + flushed += channel_flush_some_cells(TLS_CHAN_TO_BASE(chan), + num_cells - flushed); + + /* + * If channel_tls_t ever buffers anything below the channel_t layer, check + * how much we actually got and push it on down here. + */ + + done: + return flushed; +} + +/** + * Check if a channel_tls_t has anything to flush + * + * Return true if there is any more to flush on this channel (cells in queue + * or active circuits). + * + * @param chan Channel to test + * @return 1 if chan has anything to flush, 0 otherwise + */ + +int +channel_tls_more_to_flush(channel_tls_t *chan) +{ + tor_assert(chan); + + /* + * If channel_tls_t ever buffers anything below channel_t, the + * check for that should go here first. + */ + + return channel_more_to_flush(TLS_CHAN_TO_BASE(chan)); +} + +#ifdef KEEP_TIMING_STATS + +/** + * Timing states wrapper + * + * This is a wrapper function around the actual function that processes the + * cell that just arrived on chan. Increment *time + * by the number of microseconds used by the call to *func(cell, chan). + * + * @param cell Incoming cell to process + * @param chan Channel it arrived on + * @param time Increment this by the number of microseconds it took to handle + * this cell + * @param func Function pointer to cell handling function + */ + +static void +channel_tls_time_process_cell(cell_t *cell, channel_tls_t *chan, int *time, + void (*func)(cell_t *, channel_tls_t *)) +{ + struct timeval start, end; + long time_passed; + + tor_gettimeofday(&start); + + (*func)(cell, chan); + + tor_gettimeofday(&end); + time_passed = tv_udiff(&start, &end) ; + + if (time_passed > 10000) { /* more than 10ms */ + log_debug(LD_OR,"That call just took %ld ms.",time_passed/1000); + } + + if (time_passed < 0) { + log_info(LD_GENERAL,"That call took us back in time!"); + time_passed = 0; + } + + *time += time_passed; +} +#endif + +/** + * Handle an incoming cell on a channel_tls_t + * + * This is called from connection_or.c to handle an arriving cell; it checks + * for cell types specific to the handshake for this transport protocol and + * handles them, and queues all other cells to the channel_t layer, which + * eventually will hand them off to command.c. + * + * @param cell Cell to handle + * @param conn The or_connection_t cell arrived on + */ + +void +channel_tls_handle_cell(cell_t *cell, or_connection_t *conn) +{ + channel_tls_t *chan; + int handshaking; + +#ifdef KEEP_TIMING_STATS +#define PROCESS_CELL(tp, cl, cn) STMT_BEGIN { \ + ++num ## tp; \ + channel_tls_time_process_cell(cl, cn, & tp ## time , \ + channel_tls_process_ ## tp ## _cell); \ + } STMT_END +#else +#define PROCESS_CELL(tp, cl, cn) channel_tls_process_ ## tp ## _cell(cl, cn) +#endif + + tor_assert(cell); + tor_assert(conn); + + chan = conn->chan; + + if (!chan) { + log_warn(LD_CHANNEL, + "Got a cell_t on an OR connection with no channel"); + return; + } + + tor_assert(!(TLS_CHAN_TO_BASE(chan)->is_listener)); + + handshaking = (TO_CONN(conn)->state != OR_CONN_STATE_OPEN); + + if (conn->_base.marked_for_close) + return; + + /* Reject all but VERSIONS and NETINFO when handshaking. */ + /* (VERSIONS should actually be impossible; it's variable-length.) */ + if (handshaking && cell->command != CELL_VERSIONS && + cell->command != CELL_NETINFO) { + log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, + "Received unexpected cell command %d in chan state %s / " + "conn state %s; closing the connection.", + (int)cell->command, + channel_state_to_string(TLS_CHAN_TO_BASE(chan)->state), + conn_state_to_string(CONN_TYPE_OR, TO_CONN(conn)->state)); + connection_or_close_for_error(conn, 0); + return; + } + + if (conn->_base.state == OR_CONN_STATE_OR_HANDSHAKING_V3) + or_handshake_state_record_cell(conn->handshake_state, cell, 1); + + switch (cell->command) { + case CELL_PADDING: + ++stats_n_padding_cells_processed; + /* do nothing */ + break; + case CELL_VERSIONS: + tor_fragile_assert(); + break; + case CELL_NETINFO: + ++stats_n_netinfo_cells_processed; + PROCESS_CELL(netinfo, cell, chan); + break; + case CELL_CREATE: + case CELL_CREATE_FAST: + case CELL_CREATED: + case CELL_CREATED_FAST: + case CELL_RELAY: + case CELL_RELAY_EARLY: + case CELL_DESTROY: + /* + * These are all transport independent and we pass them up through the + * channel_t mechanism. They are ultimately handled in command.c. + */ + channel_queue_cell(TLS_CHAN_TO_BASE(chan), cell); + break; + default: + log_fn(LOG_INFO, LD_PROTOCOL, + "Cell of unknown type (%d) received in channeltls.c. " + "Dropping.", + cell->command); + break; + } +} + +/** + * Handle an incoming variable-length cell on a channel_tls_t + * + * Process a var_cell that was just received on conn. Keep + * internal statistics about how many of each cell we've processed so far + * this second, and the total number of microseconds it took to + * process each type of cell. All the var_cell commands are handshake- + * related and live below the channel_t layer, so no variable-length + * cells ever get delivered in the current implementation, but I've left + * the mechanism in place for future use. + * + * @param var_cell Incoming cell to handle + * @param conn The or_connection_t var_cell arrived on + */ + +void +channel_tls_handle_var_cell(var_cell_t *var_cell, or_connection_t *conn) +{ + channel_tls_t *chan; + int handshaking; + +#ifdef KEEP_TIMING_STATS + /* how many of each cell have we seen so far this second? needs better + * name. */ + static int num_versions = 0, num_certs = 0; + static time_t current_second = 0; /* from previous calls to time */ + time_t now = time(NULL); + + if (current_second == 0) current_second = now; + if (now > current_second) { /* the second has rolled over */ + /* print stats */ + log_info(LD_OR, + "At end of second: %d versions (%d ms), %d certs (%d ms)", + num_versions, versions_time / ((now - current_second) * 1000), + num_certs, certs_time / ((now - current_second) * 1000)); + + num_versions = num_certs = 0; + versions_time = certs_time = 0; + + /* remember which second it is, for next time */ + current_second = now; + } +#endif + + tor_assert(var_cell); + tor_assert(conn); + + chan = conn->chan; + + if (!chan) { + log_warn(LD_CHANNEL, + "Got a var_cell_t on an OR connection with no channel"); + return; + } + + tor_assert(!(TLS_CHAN_TO_BASE(chan)->is_listener)); + + handshaking = (TO_CONN(conn)->state != OR_CONN_STATE_OPEN); + + if (TO_CONN(conn)->marked_for_close) + return; + + switch (TO_CONN(conn)->state) { + case OR_CONN_STATE_OR_HANDSHAKING_V2: + if (var_cell->command != CELL_VERSIONS) { + log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, + "Received a cell with command %d in unexpected " + "orconn state \"%s\" [%d], channel state \"%s\" [%d]; " + "closing the connection.", + (int)(var_cell->command), + conn_state_to_string(CONN_TYPE_OR, TO_CONN(conn)->state), + TO_CONN(conn)->state, + channel_state_to_string(TLS_CHAN_TO_BASE(chan)->state), + (int)(TLS_CHAN_TO_BASE(chan)->state)); + /* + * The code in connection_or.c will tell channel_t to close for + * error; it will go to CHANNEL_STATE_CLOSING, and then to + * CHANNEL_STATE_ERROR when conn is closed. + */ + connection_or_close_for_error(conn, 0); + return; + } + break; + case OR_CONN_STATE_TLS_HANDSHAKING: + /* If we're using bufferevents, it's entirely possible for us to + * notice "hey, data arrived!" before we notice "hey, the handshake + * finished!" And we need to be accepting both at once to handle both + * the v2 and v3 handshakes. */ + + /* fall through */ + case OR_CONN_STATE_TLS_SERVER_RENEGOTIATING: + if (!(command_allowed_before_handshake(var_cell->command))) { + log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, + "Received a cell with command %d in unexpected " + "orconn state \"%s\" [%d], channel state \"%s\" [%d]; " + "closing the connection.", + (int)(var_cell->command), + conn_state_to_string(CONN_TYPE_OR, TO_CONN(conn)->state), + (int)(TO_CONN(conn)->state), + channel_state_to_string(TLS_CHAN_TO_BASE(chan)->state), + (int)(TLS_CHAN_TO_BASE(chan)->state)); + /* see above comment about CHANNEL_STATE_ERROR */ + connection_or_close_for_error(conn, 0); + return; + } else { + if (enter_v3_handshake_with_cell(var_cell, chan) < 0) + return; + } + break; + case OR_CONN_STATE_OR_HANDSHAKING_V3: + if (var_cell->command != CELL_AUTHENTICATE) + or_handshake_state_record_var_cell(conn->handshake_state, var_cell, 1); + break; /* Everything is allowed */ + case OR_CONN_STATE_OPEN: + if (conn->link_proto < 3) { + log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, + "Received a variable-length cell with command %d in orconn " + "state %s [%d], channel state %s [%d] with link protocol %d; " + "ignoring it.", + (int)(var_cell->command), + conn_state_to_string(CONN_TYPE_OR, TO_CONN(conn)->state), + (int)(TO_CONN(conn)->state), + channel_state_to_string(TLS_CHAN_TO_BASE(chan)->state), + (int)(TLS_CHAN_TO_BASE(chan)->state), + (int)(conn->link_proto)); + return; + } + break; + default: + log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, + "Received var-length cell with command %d in unexpected " + "orconn state \"%s\" [%d], channel state \"%s\" [%d]; " + "ignoring it.", + (int)(var_cell->command), + conn_state_to_string(CONN_TYPE_OR, TO_CONN(conn)->state), + (int)(TO_CONN(conn)->state), + channel_state_to_string(TLS_CHAN_TO_BASE(chan)->state), + (int)(TLS_CHAN_TO_BASE(chan)->state)); + return; + } + + /* Now handle the cell */ + + switch (var_cell->command) { + case CELL_VERSIONS: + ++stats_n_versions_cells_processed; + PROCESS_CELL(versions, var_cell, chan); + break; + case CELL_VPADDING: + ++stats_n_vpadding_cells_processed; + /* Do nothing */ + break; + case CELL_CERTS: + ++stats_n_certs_cells_processed; + PROCESS_CELL(certs, var_cell, chan); + break; + case CELL_AUTH_CHALLENGE: + ++stats_n_auth_challenge_cells_processed; + PROCESS_CELL(auth_challenge, var_cell, chan); + break; + case CELL_AUTHENTICATE: + ++stats_n_authenticate_cells_processed; + PROCESS_CELL(authenticate, var_cell, chan); + break; + case CELL_AUTHORIZE: + ++stats_n_authorize_cells_processed; + /* Ignored so far. */ + break; + default: + log_fn(LOG_INFO, LD_PROTOCOL, + "Variable-length cell of unknown type (%d) received.", + (int)(var_cell->command)); + break; + } +} + +/** + * Check if this cell type is allowed before the handshake is finished + * + * Return true if command is a cell command that's allowed to start a + * V3 handshake. + * + * @param command Cell type to check + */ + +static int +command_allowed_before_handshake(uint8_t command) +{ + switch (command) { + case CELL_VERSIONS: + case CELL_VPADDING: + case CELL_AUTHORIZE: + return 1; + default: + return 0; + } +} + +/** + * Start a V3 handshake on an incoming connection + * + * Called when we as a server receive an appropriate cell while waiting + * either for a cell or a TLS handshake. Set the connection's state to + * "handshaking_v3', initializes the or_handshake_state field as needed, + * and add the cell to the hash of incoming cells.) + * + * @param cell Incoming cell initiating the handshake + * @param chan Channel cell was received on + * @return 0 on success; return -1 and mark the connection on failure. + */ + +static int +enter_v3_handshake_with_cell(var_cell_t *cell, channel_tls_t *chan) +{ + int started_here = 0; + + tor_assert(cell); + tor_assert(chan); + tor_assert(!(TLS_CHAN_TO_BASE(chan)->is_listener)); + tor_assert(chan->conn); + + started_here = connection_or_nonopen_was_started_here(chan->conn); + + tor_assert(TO_CONN(chan->conn)->state == OR_CONN_STATE_TLS_HANDSHAKING || + TO_CONN(chan->conn)->state == + OR_CONN_STATE_TLS_SERVER_RENEGOTIATING); + + if (started_here) { + log_fn(LOG_PROTOCOL_WARN, LD_OR, + "Received a cell while TLS-handshaking, not in " + "OR_HANDSHAKING_V3, on a connection we originated."); + } + chan->conn->_base.state = OR_CONN_STATE_OR_HANDSHAKING_V3; + if (connection_init_or_handshake_state(chan->conn, started_here) < 0) { + connection_or_close_for_error(chan->conn, 0); + return -1; + } + or_handshake_state_record_var_cell(chan->conn->handshake_state, cell, 1); + return 0; +} + +/** + * Process a 'versions' cell. + * + * This function is called to handle an incoming VERSIONS cell; the current + * link protocol version must be 0 to indicate that no version has yet been + * negotiated. We compare the versions in the cell to the list of versions + * we support, pick the highest version we have in common, and continue the + * negotiation from there. + * + * @param cell Incoming VERSIONS cell + * @param chan Channel that cell arrived on + */ + +static void +channel_tls_process_versions_cell(var_cell_t *cell, channel_tls_t *chan) +{ + int highest_supported_version = 0; + const uint8_t *cp, *end; + int started_here = 0; + + tor_assert(cell); + tor_assert(chan); + tor_assert(!(TLS_CHAN_TO_BASE(chan)->is_listener)); + tor_assert(chan->conn); + + started_here = connection_or_nonopen_was_started_here(chan->conn); + + if (chan->conn->link_proto != 0 || + (chan->conn->handshake_state && + chan->conn->handshake_state->received_versions)) { + log_fn(LOG_PROTOCOL_WARN, LD_OR, + "Received a VERSIONS cell on a connection with its version " + "already set to %d; dropping", + (int)(chan->conn->link_proto)); + return; + } + switch (chan->conn->_base.state) + { + case OR_CONN_STATE_OR_HANDSHAKING_V2: + case OR_CONN_STATE_OR_HANDSHAKING_V3: + break; + case OR_CONN_STATE_TLS_HANDSHAKING: + case OR_CONN_STATE_TLS_SERVER_RENEGOTIATING: + default: + log_fn(LOG_PROTOCOL_WARN, LD_OR, + "VERSIONS cell while in unexpected state"); + return; + } + + tor_assert(chan->conn->handshake_state); + end = cell->payload + cell->payload_len; + for (cp = cell->payload; cp+1 < end; ++cp) { + uint16_t v = ntohs(get_uint16(cp)); + if (is_or_protocol_version_known(v) && v > highest_supported_version) + highest_supported_version = v; + } + if (!highest_supported_version) { + log_fn(LOG_PROTOCOL_WARN, LD_OR, + "Couldn't find a version in common between my version list and the " + "list in the VERSIONS cell; closing connection."); + connection_or_close_for_error(chan->conn, 0); + return; + } else if (highest_supported_version == 1) { + /* Negotiating version 1 makes no sense, since version 1 has no VERSIONS + * cells. */ + log_fn(LOG_PROTOCOL_WARN, LD_OR, + "Used version negotiation protocol to negotiate a v1 connection. " + "That's crazily non-compliant. Closing connection."); + connection_or_close_for_error(chan->conn, 0); + return; + } else if (highest_supported_version < 3 && + chan->conn->_base.state == OR_CONN_STATE_OR_HANDSHAKING_V3) { + log_fn(LOG_PROTOCOL_WARN, LD_OR, + "Negotiated link protocol 2 or lower after doing a v3 TLS " + "handshake. Closing connection."); + connection_or_close_for_error(chan->conn, 0); + return; + } + + chan->conn->link_proto = highest_supported_version; + chan->conn->handshake_state->received_versions = 1; + + if (chan->conn->link_proto == 2) { + log_info(LD_OR, + "Negotiated version %d with %s:%d; sending NETINFO.", + highest_supported_version, + safe_str_client(chan->conn->_base.address), + chan->conn->_base.port); + + if (connection_or_send_netinfo(chan->conn) < 0) { + connection_or_close_for_error(chan->conn, 0); + return; + } + } else { + const int send_versions = !started_here; + /* If we want to authenticate, send a CERTS cell */ + const int send_certs = !started_here || public_server_mode(get_options()); + /* If we're a relay that got a connection, ask for authentication. */ + const int send_chall = !started_here && public_server_mode(get_options()); + /* If our certs cell will authenticate us, we can send a netinfo cell + * right now. */ + const int send_netinfo = !started_here; + const int send_any = + send_versions || send_certs || send_chall || send_netinfo; + tor_assert(chan->conn->link_proto >= 3); + + log_info(LD_OR, + "Negotiated version %d with %s:%d; %s%s%s%s%s", + highest_supported_version, + safe_str_client(chan->conn->_base.address), + chan->conn->_base.port, + send_any ? "Sending cells:" : "Waiting for CERTS cell", + send_versions ? " VERSIONS" : "", + send_certs ? " CERTS" : "", + send_chall ? " AUTH_CHALLENGE" : "", + send_netinfo ? " NETINFO" : ""); + +#ifdef DISABLE_V3_LINKPROTO_SERVERSIDE + if (1) { + connection_or_close_normally(chan->conn, 1); + return; + } +#endif + + if (send_versions) { + if (connection_or_send_versions(chan->conn, 1) < 0) { + log_warn(LD_OR, "Couldn't send versions cell"); + connection_or_close_for_error(chan->conn, 0); + return; + } + } + if (send_certs) { + if (connection_or_send_certs_cell(chan->conn) < 0) { + log_warn(LD_OR, "Couldn't send certs cell"); + connection_or_close_for_error(chan->conn, 0); + return; + } + } + if (send_chall) { + if (connection_or_send_auth_challenge_cell(chan->conn) < 0) { + log_warn(LD_OR, "Couldn't send auth_challenge cell"); + connection_or_close_for_error(chan->conn, 0); + return; + } + } + if (send_netinfo) { + if (connection_or_send_netinfo(chan->conn) < 0) { + log_warn(LD_OR, "Couldn't send netinfo cell"); + connection_or_close_for_error(chan->conn, 0); + return; + } + } + } +} + +/** + * Process a 'netinfo' cell + * + * This function is called to handle an incoming NETINFO cell; read and act + * on its contents, and set the connection state to "open". + * + * @param cell Incoming NETINFO cell + * @param chan Channel that cell arrived on + */ + +static void +channel_tls_process_netinfo_cell(cell_t *cell, channel_tls_t *chan) +{ + time_t timestamp; + uint8_t my_addr_type; + uint8_t my_addr_len; + const uint8_t *my_addr_ptr; + const uint8_t *cp, *end; + uint8_t n_other_addrs; + time_t now = time(NULL); + + long apparent_skew = 0; + tor_addr_t my_apparent_addr = TOR_ADDR_NULL; + + tor_assert(cell); + tor_assert(chan); + tor_assert(!(TLS_CHAN_TO_BASE(chan)->is_listener)); + tor_assert(chan->conn); + + if (chan->conn->link_proto < 2) { + log_fn(LOG_PROTOCOL_WARN, LD_OR, + "Received a NETINFO cell on %s connection; dropping.", + chan->conn->link_proto == 0 ? "non-versioned" : "a v1"); + return; + } + if (chan->conn->_base.state != OR_CONN_STATE_OR_HANDSHAKING_V2 && + chan->conn->_base.state != OR_CONN_STATE_OR_HANDSHAKING_V3) { + log_fn(LOG_PROTOCOL_WARN, LD_OR, + "Received a NETINFO cell on non-handshaking connection; dropping."); + return; + } + tor_assert(chan->conn->handshake_state && + chan->conn->handshake_state->received_versions); + + if (chan->conn->_base.state == OR_CONN_STATE_OR_HANDSHAKING_V3) { + tor_assert(chan->conn->link_proto >= 3); + if (chan->conn->handshake_state->started_here) { + if (!(chan->conn->handshake_state->authenticated)) { + log_fn(LOG_PROTOCOL_WARN, LD_OR, + "Got a NETINFO cell from server, " + "but no authentication. Closing the connection."); + connection_or_close_for_error(chan->conn, 0); + return; + } + } else { + /* we're the server. If the client never authenticated, we have + some housekeeping to do.*/ + if (!(chan->conn->handshake_state->authenticated)) { + tor_assert(tor_digest_is_zero( + (const char*)(chan->conn->handshake_state-> + authenticated_peer_id))); + channel_set_circid_type(TLS_CHAN_TO_BASE(chan), NULL); + + connection_or_init_conn_from_address(chan->conn, + &(chan->conn->_base.addr), + chan->conn->_base.port, + (const char*)(chan->conn->handshake_state-> + authenticated_peer_id), + 0); + } + } + } + + /* Decode the cell. */ + timestamp = ntohl(get_uint32(cell->payload)); + if (labs(now - chan->conn->handshake_state->sent_versions_at) < 180) { + apparent_skew = now - timestamp; + } + + my_addr_type = (uint8_t) cell->payload[4]; + my_addr_len = (uint8_t) cell->payload[5]; + my_addr_ptr = (uint8_t*) cell->payload + 6; + end = cell->payload + CELL_PAYLOAD_SIZE; + cp = cell->payload + 6 + my_addr_len; + if (cp >= end) { + log_fn(LOG_PROTOCOL_WARN, LD_OR, + "Addresses too long in netinfo cell; closing connection."); + connection_or_close_for_error(chan->conn, 0); + return; + } else if (my_addr_type == RESOLVED_TYPE_IPV4 && my_addr_len == 4) { + tor_addr_from_ipv4n(&my_apparent_addr, get_uint32(my_addr_ptr)); + } else if (my_addr_type == RESOLVED_TYPE_IPV6 && my_addr_len == 16) { + tor_addr_from_ipv6_bytes(&my_apparent_addr, (const char *) my_addr_ptr); + } + + n_other_addrs = (uint8_t) *cp++; + while (n_other_addrs && cp < end-2) { + /* Consider all the other addresses; if any matches, this connection is + * "canonical." */ + tor_addr_t addr; + const uint8_t *next = + decode_address_from_payload(&addr, cp, (int)(end-cp)); + if (next == NULL) { + log_fn(LOG_PROTOCOL_WARN, LD_OR, + "Bad address in netinfo cell; closing connection."); + connection_or_close_for_error(chan->conn, 0); + return; + } + if (tor_addr_eq(&addr, &(chan->conn->real_addr))) { + chan->conn->is_canonical = 1; + break; + } + cp = next; + --n_other_addrs; + } + + /* Act on apparent skew. */ + /** Warn when we get a netinfo skew with at least this value. */ +#define NETINFO_NOTICE_SKEW 3600 + if (labs(apparent_skew) > NETINFO_NOTICE_SKEW && + router_get_by_id_digest(chan->conn->identity_digest)) { + char dbuf[64]; + int severity; + /*XXXX be smarter about when everybody says we are skewed. */ + if (router_digest_is_trusted_dir(chan->conn->identity_digest)) + severity = LOG_WARN; + else + severity = LOG_INFO; + format_time_interval(dbuf, sizeof(dbuf), apparent_skew); + log_fn(severity, LD_GENERAL, + "Received NETINFO cell with skewed time from " + "server at %s:%d. It seems that our clock is %s by %s, or " + "that theirs is %s. Tor requires an accurate clock to work: " + "please check your time and date settings.", + chan->conn->_base.address, + (int)(chan->conn->_base.port), + apparent_skew > 0 ? "ahead" : "behind", + dbuf, + apparent_skew > 0 ? "behind" : "ahead"); + if (severity == LOG_WARN) /* only tell the controller if an authority */ + control_event_general_status(LOG_WARN, + "CLOCK_SKEW SKEW=%ld SOURCE=OR:%s:%d", + apparent_skew, + chan->conn->_base.address, + chan->conn->_base.port); + } + + /* XXX maybe act on my_apparent_addr, if the source is sufficiently + * trustworthy. */ + + if (connection_or_set_state_open(chan->conn) < 0) { + log_fn(LOG_PROTOCOL_WARN, LD_OR, + "Got good NETINFO cell from %s:%d; but " + "was unable to make the OR connection become open.", + safe_str_client(chan->conn->_base.address), + chan->conn->_base.port); + connection_or_close_for_error(chan->conn, 0); + } else { + log_info(LD_OR, + "Got good NETINFO cell from %s:%d; OR connection is now " + "open, using protocol version %d. Its ID digest is %s. " + "Our address is apparently %s.", + safe_str_client(chan->conn->_base.address), + chan->conn->_base.port, + (int)(chan->conn->link_proto), + hex_str(TLS_CHAN_TO_BASE(chan)->u.cell_chan.identity_digest, + DIGEST_LEN), + tor_addr_is_null(&my_apparent_addr) ? + "" : fmt_and_decorate_addr(&my_apparent_addr)); + } + assert_connection_ok(TO_CONN(chan->conn),time(NULL)); +} + +/** + * Process a CERTS cell from a channel. + * + * This function is called to process an incoming CERTS cell on a + * channel_tls_t: + * + * If the other side should not have sent us a CERTS cell, or the cell is + * malformed, or it is supposed to authenticate the TLS key but it doesn't, + * then mark the connection. + * + * If the cell has a good cert chain and we're doing a v3 handshake, then + * store the certificates in or_handshake_state. If this is the client side + * of the connection, we then authenticate the server or mark the connection. + * If it's the server side, wait for an AUTHENTICATE cell. + * + * @param cell Incoming CERTS cell + * @param chan Channel that cell arrived on + */ + +static void +channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan) +{ + tor_cert_t *link_cert = NULL; + tor_cert_t *id_cert = NULL; + tor_cert_t *auth_cert = NULL; + uint8_t *ptr; + int n_certs, i; + int send_netinfo = 0; + + tor_assert(cell); + tor_assert(chan); + tor_assert(!(TLS_CHAN_TO_BASE(chan)->is_listener)); + tor_assert(chan->conn); + +#define ERR(s) \ + do { \ + log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, \ + "Received a bad CERTS cell from %s:%d: %s", \ + safe_str(chan->conn->_base.address), \ + chan->conn->_base.port, (s)); \ + connection_or_close_for_error(chan->conn, 0); \ + return; \ + } while (0) + + if (chan->conn->_base.state != OR_CONN_STATE_OR_HANDSHAKING_V3) + ERR("We're not doing a v3 handshake!"); + if (chan->conn->link_proto < 3) + ERR("We're not using link protocol >= 3"); + if (chan->conn->handshake_state->received_certs_cell) + ERR("We already got one"); + if (chan->conn->handshake_state->authenticated) { + /* Should be unreachable, but let's make sure. */ + ERR("We're already authenticated!"); + } + if (cell->payload_len < 1) + ERR("It had no body"); + if (cell->circ_id) + ERR("It had a nonzero circuit ID"); + + n_certs = cell->payload[0]; + ptr = cell->payload + 1; + for (i = 0; i < n_certs; ++i) { + uint8_t cert_type; + uint16_t cert_len; + if (ptr + 3 > cell->payload + cell->payload_len) { + goto truncated; + } + cert_type = *ptr; + cert_len = ntohs(get_uint16(ptr+1)); + if (ptr + 3 + cert_len > cell->payload + cell->payload_len) { + goto truncated; + } + if (cert_type == OR_CERT_TYPE_TLS_LINK || + cert_type == OR_CERT_TYPE_ID_1024 || + cert_type == OR_CERT_TYPE_AUTH_1024) { + tor_cert_t *cert = tor_cert_decode(ptr + 3, cert_len); + if (!cert) { + log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, + "Received undecodable certificate in CERTS cell from %s:%d", + safe_str(chan->conn->_base.address), + chan->conn->_base.port); + } else { + if (cert_type == OR_CERT_TYPE_TLS_LINK) { + if (link_cert) { + tor_cert_free(cert); + ERR("Too many TLS_LINK certificates"); + } + link_cert = cert; + } else if (cert_type == OR_CERT_TYPE_ID_1024) { + if (id_cert) { + tor_cert_free(cert); + ERR("Too many ID_1024 certificates"); + } + id_cert = cert; + } else if (cert_type == OR_CERT_TYPE_AUTH_1024) { + if (auth_cert) { + tor_cert_free(cert); + ERR("Too many AUTH_1024 certificates"); + } + auth_cert = cert; + } else { + tor_cert_free(cert); + } + } + } + ptr += 3 + cert_len; + continue; + + truncated: + ERR("It ends in the middle of a certificate"); + } + + if (chan->conn->handshake_state->started_here) { + int severity; + if (! (id_cert && link_cert)) + ERR("The certs we wanted were missing"); + /* Okay. We should be able to check the certificates now. */ + if (! tor_tls_cert_matches_key(chan->conn->tls, link_cert)) { + ERR("The link certificate didn't match the TLS public key"); + } + /* Note that this warns more loudly about time and validity if we were + * _trying_ to connect to an authority, not necessarily if we _did_ connect + * to one. */ + if (router_digest_is_trusted_dir( + TLS_CHAN_TO_BASE(chan)->u.cell_chan.identity_digest)) + severity = LOG_WARN; + else + severity = LOG_PROTOCOL_WARN; + + if (! tor_tls_cert_is_valid(severity, link_cert, id_cert, 0)) + ERR("The link certificate was not valid"); + if (! tor_tls_cert_is_valid(severity, id_cert, id_cert, 1)) + ERR("The ID certificate was not valid"); + + chan->conn->handshake_state->authenticated = 1; + { + const digests_t *id_digests = tor_cert_get_id_digests(id_cert); + crypto_pk_t *identity_rcvd; + if (!id_digests) + ERR("Couldn't compute digests for key in ID cert"); + + identity_rcvd = tor_tls_cert_get_key(id_cert); + if (!identity_rcvd) + ERR("Internal error: Couldn't get RSA key from ID cert."); + memcpy(chan->conn->handshake_state->authenticated_peer_id, + id_digests->d[DIGEST_SHA1], DIGEST_LEN); + channel_set_circid_type(TLS_CHAN_TO_BASE(chan), identity_rcvd); + crypto_pk_free(identity_rcvd); + } + + if (connection_or_client_learned_peer_id(chan->conn, + chan->conn->handshake_state->authenticated_peer_id) < 0) + ERR("Problem setting or checking peer id"); + + log_info(LD_OR, + "Got some good certificates from %s:%d: Authenticated it.", + safe_str(chan->conn->_base.address), chan->conn->_base.port); + + chan->conn->handshake_state->id_cert = id_cert; + id_cert = NULL; + + if (!public_server_mode(get_options())) { + /* If we initiated the connection and we are not a public server, we + * aren't planning to authenticate at all. At this point we know who we + * are talking to, so we can just send a netinfo now. */ + send_netinfo = 1; + } + } else { + if (! (id_cert && auth_cert)) + ERR("The certs we wanted were missing"); + + /* Remember these certificates so we can check an AUTHENTICATE cell */ + if (! tor_tls_cert_is_valid(LOG_PROTOCOL_WARN, auth_cert, id_cert, 1)) + ERR("The authentication certificate was not valid"); + if (! tor_tls_cert_is_valid(LOG_PROTOCOL_WARN, id_cert, id_cert, 1)) + ERR("The ID certificate was not valid"); + + log_info(LD_OR, + "Got some good certificates from %s:%d: " + "Waiting for AUTHENTICATE.", + safe_str(chan->conn->_base.address), + chan->conn->_base.port); + /* XXXX check more stuff? */ + + chan->conn->handshake_state->id_cert = id_cert; + chan->conn->handshake_state->auth_cert = auth_cert; + id_cert = auth_cert = NULL; + } + + chan->conn->handshake_state->received_certs_cell = 1; + + if (send_netinfo) { + if (connection_or_send_netinfo(chan->conn) < 0) { + log_warn(LD_OR, "Couldn't send netinfo cell"); + connection_or_close_for_error(chan->conn, 0); + goto err; + } + } + + err: + tor_cert_free(id_cert); + tor_cert_free(link_cert); + tor_cert_free(auth_cert); +#undef ERR +} + +/** + * Process an AUTH_CHALLENGE cell from a channel_tls_t + * + * This function is called to handle an incoming AUTH_CHALLENGE cell on a + * channel_tls_t; if we weren't supposed to get one (for example, because we're + * not the originator of the channel), or it's ill-formed, or we aren't doing + * a v3 handshake, mark the channel. If the cell is well-formed but we don't + * want to authenticate, just drop it. If the cell is well-formed *and* we + * want to authenticate, send an AUTHENTICATE cell and then a NETINFO cell. + * + * @param cell Incoming AUTH_CHALLENGE cell to handle + * @param chan Channel that cell arrived on + */ + +static void +channel_tls_process_auth_challenge_cell(var_cell_t *cell, channel_tls_t *chan) +{ + int n_types, i, use_type = -1; + uint8_t *cp; + + tor_assert(cell); + tor_assert(chan); + tor_assert(!(TLS_CHAN_TO_BASE(chan)->is_listener)); + tor_assert(chan->conn); + +#define ERR(s) \ + do { \ + log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, \ + "Received a bad AUTH_CHALLENGE cell from %s:%d: %s", \ + safe_str(chan->conn->_base.address), \ + chan->conn->_base.port, (s)); \ + connection_or_close_for_error(chan->conn, 0); \ + return; \ + } while (0) + + if (chan->conn->_base.state != OR_CONN_STATE_OR_HANDSHAKING_V3) + ERR("We're not currently doing a v3 handshake"); + if (chan->conn->link_proto < 3) + ERR("We're not using link protocol >= 3"); + if (!(chan->conn->handshake_state->started_here)) + ERR("We didn't originate this connection"); + if (chan->conn->handshake_state->received_auth_challenge) + ERR("We already received one"); + if (!(chan->conn->handshake_state->received_certs_cell)) + ERR("We haven't gotten a CERTS cell yet"); + if (cell->payload_len < OR_AUTH_CHALLENGE_LEN + 2) + ERR("It was too short"); + if (cell->circ_id) + ERR("It had a nonzero circuit ID"); + + n_types = ntohs(get_uint16(cell->payload + OR_AUTH_CHALLENGE_LEN)); + if (cell->payload_len < OR_AUTH_CHALLENGE_LEN + 2 + 2*n_types) + ERR("It looks truncated"); + + /* Now see if there is an authentication type we can use */ + cp = cell->payload+OR_AUTH_CHALLENGE_LEN + 2; + for (i = 0; i < n_types; ++i, cp += 2) { + uint16_t authtype = ntohs(get_uint16(cp)); + if (authtype == AUTHTYPE_RSA_SHA256_TLSSECRET) + use_type = authtype; + } + + chan->conn->handshake_state->received_auth_challenge = 1; + + if (! public_server_mode(get_options())) { + /* If we're not a public server then we don't want to authenticate on a + connection we originated, and we already sent a NETINFO cell when we + got the CERTS cell. We have nothing more to do. */ + return; + } + + if (use_type >= 0) { + log_info(LD_OR, + "Got an AUTH_CHALLENGE cell from %s:%d: Sending " + "authentication", + safe_str(chan->conn->_base.address), + chan->conn->_base.port); + + if (connection_or_send_authenticate_cell(chan->conn, use_type) < 0) { + log_warn(LD_OR, + "Couldn't send authenticate cell"); + connection_or_close_for_error(chan->conn, 0); + return; + } + } else { + log_info(LD_OR, + "Got an AUTH_CHALLENGE cell from %s:%d, but we don't " + "know any of its authentication types. Not authenticating.", + safe_str(chan->conn->_base.address), + chan->conn->_base.port); + } + + if (connection_or_send_netinfo(chan->conn) < 0) { + log_warn(LD_OR, "Couldn't send netinfo cell"); + connection_or_close_for_error(chan->conn, 0); + return; + } + +#undef ERR +} + +/** + * Process an AUTHENTICATE cell from a channel_tls_t + * + * If it's ill-formed or we weren't supposed to get one or we're not doing a + * v3 handshake, then mark the connection. If it does not authenticate the + * other side of the connection successfully (because it isn't signed right, + * we didn't get a CERTS cell, etc) mark the connection. Otherwise, accept + * the identity of the router on the other side of the connection. + * + * @param cell Incoming AUTHENTICATE cell + * @param chan Channel that cell arrived on + */ + +static void +channel_tls_process_authenticate_cell(var_cell_t *cell, channel_tls_t *chan) +{ + uint8_t expected[V3_AUTH_FIXED_PART_LEN]; + const uint8_t *auth; + int authlen; + + tor_assert(cell); + tor_assert(chan); + tor_assert(!(TLS_CHAN_TO_BASE(chan)->is_listener)); + tor_assert(chan->conn); + +#define ERR(s) \ + do { \ + log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, \ + "Received a bad AUTHENTICATE cell from %s:%d: %s", \ + safe_str(chan->conn->_base.address), \ + chan->conn->_base.port, (s)); \ + connection_or_close_for_error(chan->conn, 0); \ + return; \ + } while (0) + + if (chan->conn->_base.state != OR_CONN_STATE_OR_HANDSHAKING_V3) + ERR("We're not doing a v3 handshake"); + if (chan->conn->link_proto < 3) + ERR("We're not using link protocol >= 3"); + if (chan->conn->handshake_state->started_here) + ERR("We originated this connection"); + if (chan->conn->handshake_state->received_authenticate) + ERR("We already got one!"); + if (chan->conn->handshake_state->authenticated) { + /* Should be impossible given other checks */ + ERR("The peer is already authenticated"); + } + if (!(chan->conn->handshake_state->received_certs_cell)) + ERR("We never got a certs cell"); + if (chan->conn->handshake_state->auth_cert == NULL) + ERR("We never got an authentication certificate"); + if (chan->conn->handshake_state->id_cert == NULL) + ERR("We never got an identity certificate"); + if (cell->payload_len < 4) + ERR("Cell was way too short"); + + auth = cell->payload; + { + uint16_t type = ntohs(get_uint16(auth)); + uint16_t len = ntohs(get_uint16(auth+2)); + if (4 + len > cell->payload_len) + ERR("Authenticator was truncated"); + + if (type != AUTHTYPE_RSA_SHA256_TLSSECRET) + ERR("Authenticator type was not recognized"); + + auth += 4; + authlen = len; + } + + if (authlen < V3_AUTH_BODY_LEN + 1) + ERR("Authenticator was too short"); + + if (connection_or_compute_authenticate_cell_body( + chan->conn, expected, sizeof(expected), NULL, 1) < 0) + ERR("Couldn't compute expected AUTHENTICATE cell body"); + + if (tor_memneq(expected, auth, sizeof(expected))) + ERR("Some field in the AUTHENTICATE cell body was not as expected"); + + { + crypto_pk_t *pk = tor_tls_cert_get_key( + chan->conn->handshake_state->auth_cert); + char d[DIGEST256_LEN]; + char *signed_data; + size_t keysize; + int signed_len; + + if (!pk) + ERR("Internal error: couldn't get RSA key from AUTH cert."); + crypto_digest256(d, (char*)auth, V3_AUTH_BODY_LEN, DIGEST_SHA256); + + keysize = crypto_pk_keysize(pk); + signed_data = tor_malloc(keysize); + signed_len = crypto_pk_public_checksig(pk, signed_data, keysize, + (char*)auth + V3_AUTH_BODY_LEN, + authlen - V3_AUTH_BODY_LEN); + crypto_pk_free(pk); + if (signed_len < 0) { + tor_free(signed_data); + ERR("Signature wasn't valid"); + } + if (signed_len < DIGEST256_LEN) { + tor_free(signed_data); + ERR("Not enough data was signed"); + } + /* Note that we deliberately allow *more* than DIGEST256_LEN bytes here, + * in case they're later used to hold a SHA3 digest or something. */ + if (tor_memneq(signed_data, d, DIGEST256_LEN)) { + tor_free(signed_data); + ERR("Signature did not match data to be signed."); + } + tor_free(signed_data); + } + + /* Okay, we are authenticated. */ + chan->conn->handshake_state->received_authenticate = 1; + chan->conn->handshake_state->authenticated = 1; + chan->conn->handshake_state->digest_received_data = 0; + { + crypto_pk_t *identity_rcvd = + tor_tls_cert_get_key(chan->conn->handshake_state->id_cert); + const digests_t *id_digests = + tor_cert_get_id_digests(chan->conn->handshake_state->id_cert); + + /* This must exist; we checked key type when reading the cert. */ + tor_assert(id_digests); + + memcpy(chan->conn->handshake_state->authenticated_peer_id, + id_digests->d[DIGEST_SHA1], DIGEST_LEN); + + channel_set_circid_type(TLS_CHAN_TO_BASE(chan), identity_rcvd); + crypto_pk_free(identity_rcvd); + + connection_or_init_conn_from_address(chan->conn, + &(chan->conn->_base.addr), + chan->conn->_base.port, + (const char*)(chan->conn->handshake_state-> + authenticated_peer_id), + 0); + + log_info(LD_OR, + "Got an AUTHENTICATE cell from %s:%d: Looks good.", + safe_str(chan->conn->_base.address), + chan->conn->_base.port); + } + +#undef ERR +} + diff --git a/src/or/channeltls.h b/src/or/channeltls.h new file mode 100644 index 0000000000..3b7d6a7a1f --- /dev/null +++ b/src/or/channeltls.h @@ -0,0 +1,50 @@ +/* * Copyright (c) 2012, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file channeltls.h + * \brief Header file for channeltls.c + **/ + +#ifndef _TOR_CHANNEL_TLS_H +#define _TOR_CHANNEL_TLS_H + +#include "or.h" +#include "channel.h" + +#define BASE_CHAN_TO_TLS(c) ((channel_tls_t *)(c)) +#define TLS_CHAN_TO_BASE(c) ((channel_t *)(c)) + +#ifdef _TOR_CHANNEL_INTERNAL + +struct channel_tls_s { + /* Base channel_t struct */ + channel_t _base; + /* or_connection_t pointer */ + or_connection_t *conn; +}; + +#endif /* _TOR_CHANNEL_INTERNAL */ + +channel_t * channel_tls_connect(const tor_addr_t *addr, uint16_t port, + const char *id_digest); +channel_t * channel_tls_get_listener(void); +channel_t * channel_tls_start_listener(void); +channel_t * channel_tls_handle_incoming(or_connection_t *orconn); + +/* Things for connection_or.c to call back into */ +ssize_t channel_tls_flush_some_cells(channel_tls_t *chan, ssize_t num_cells); +int channel_tls_more_to_flush(channel_tls_t *chan); +void channel_tls_handle_cell(cell_t *cell, or_connection_t *conn); +void channel_tls_handle_state_change_on_orconn(channel_tls_t *chan, + or_connection_t *conn, + uint8_t old_state, + uint8_t state); +void channel_tls_handle_var_cell(var_cell_t *var_cell, + or_connection_t *conn); + +/* Cleanup at shutdown */ +void channel_tls_free_all(void); + +#endif + diff --git a/src/or/connection.c b/src/or/connection.c index d64c676bfb..8e9c70191d 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -12,6 +12,13 @@ #include "or.h" #include "buffers.h" +/* + * Define this so we get channel internal functions, since we're implementing + * part of a subclass (channel_tls_t). + */ +#define _TOR_CHANNEL_INTERNAL +#include "channel.h" +#include "channeltls.h" #include "circuitbuild.h" #include "circuitlist.h" #include "circuituse.h" @@ -257,7 +264,6 @@ or_connection_new(int socket_family) connection_init(now, TO_CONN(or_conn), CONN_TYPE_OR, socket_family); or_conn->timestamp_last_added_nonpadding = time(NULL); - or_conn->next_circ_id = crypto_rand_int(1<<15); or_conn->active_circuit_pqueue = smartlist_new(); or_conn->active_circuit_pqueue_last_recalibrated = cell_ewma_get_tick(); @@ -693,6 +699,16 @@ _connection_mark_for_close(connection_t *conn, int line, const char *file) return; } + if (conn->type == CONN_TYPE_OR) { + /* + * Bad news if this happens without telling the controlling channel; do + * this so we can find things that call this wrongly when the asserts hit. + */ + log_debug(LD_CHANNEL, + "Calling connection_mark_for_close on an OR conn at %s:%d", + file, line); + } + conn->marked_for_close = line; conn->marked_for_close_file = file; add_connection_to_closeable_list(conn); @@ -1281,12 +1297,19 @@ static int connection_init_accepted_conn(connection_t *conn, const listener_connection_t *listener) { + int rv; + connection_start_reading(conn); switch (conn->type) { case CONN_TYPE_OR: control_event_or_conn_status(TO_OR_CONN(conn), OR_CONN_EVENT_NEW, 0); - return connection_tls_start_handshake(TO_OR_CONN(conn), 1); + rv = connection_tls_start_handshake(TO_OR_CONN(conn), 1); + if (rv < 0) { + connection_or_close_for_error(TO_OR_CONN(conn), 0); + } + return rv; + break; case CONN_TYPE_AP: TO_ENTRY_CONN(conn)->isolation_flags = listener->isolation_flags; TO_ENTRY_CONN(conn)->session_group = listener->session_group; @@ -2091,7 +2114,8 @@ static int connection_counts_as_relayed_traffic(connection_t *conn, time_t now) { if (conn->type == CONN_TYPE_OR && - TO_OR_CONN(conn)->client_used + CLIENT_IDLE_TIME_FOR_PRIORITY < now) + connection_or_client_used(TO_OR_CONN(conn)) + + CLIENT_IDLE_TIME_FOR_PRIORITY < now) return 1; if (conn->type == CONN_TYPE_DIR && DIR_CONN_IS_SERVER(conn)) return 1; @@ -2688,11 +2712,14 @@ connection_handle_read_impl(connection_t *conn) before = buf_datalen(conn->inbuf); if (connection_read_to_buf(conn, &max_to_read, &socket_error) < 0) { /* There's a read error; kill the connection.*/ - if (conn->type == CONN_TYPE_OR && - conn->state == OR_CONN_STATE_CONNECTING) { - connection_or_connect_failed(TO_OR_CONN(conn), - errno_to_orconn_end_reason(socket_error), - tor_socket_strerror(socket_error)); + if (conn->type == CONN_TYPE_OR) { + connection_or_notify_error(TO_OR_CONN(conn), + socket_error != 0 ? + errno_to_orconn_end_reason(socket_error) : + END_OR_CONN_REASON_CONNRESET, + socket_error != 0 ? + tor_socket_strerror(socket_error) : + "(unknown, errno was 0)"); } if (CONN_IS_EDGE(conn)) { edge_connection_t *edge_conn = TO_EDGE_CONN(conn); @@ -3214,9 +3241,9 @@ connection_handle_write_impl(connection_t *conn, int force) if (CONN_IS_EDGE(conn)) connection_edge_end_errno(TO_EDGE_CONN(conn)); if (conn->type == CONN_TYPE_OR) - connection_or_connect_failed(TO_OR_CONN(conn), - errno_to_orconn_end_reason(e), - tor_socket_strerror(e)); + connection_or_notify_error(TO_OR_CONN(conn), + errno_to_orconn_end_reason(e), + tor_socket_strerror(e)); connection_close_immediate(conn); connection_mark_for_close(conn); @@ -3241,6 +3268,10 @@ connection_handle_write_impl(connection_t *conn, int force) connection_stop_writing(conn); if (connection_tls_continue_handshake(or_conn) < 0) { /* Don't flush; connection is dead. */ + connection_or_notify_error(or_conn, + END_OR_CONN_REASON_MISC, + "TLS error in connection_tls_" + "continue_handshake()"); connection_close_immediate(conn); connection_mark_for_close(conn); return -1; @@ -3254,19 +3285,23 @@ connection_handle_write_impl(connection_t *conn, int force) result = flush_buf_tls(or_conn->tls, conn->outbuf, max_to_write, &conn->outbuf_flushlen); - /* If we just flushed the last bytes, check if this tunneled dir - * request is done. */ + /* If we just flushed the last bytes, tell the channel on the + * or_conn to check if it needs to geoip_change_dirreq_state() */ /* XXXX move this to flushed_some or finished_flushing -NM */ - if (buf_datalen(conn->outbuf) == 0 && conn->dirreq_id) - geoip_change_dirreq_state(conn->dirreq_id, DIRREQ_TUNNELED, - DIRREQ_OR_CONN_BUFFER_FLUSHED); + if (buf_datalen(conn->outbuf) == 0 && or_conn->chan) + channel_notify_flushed(TLS_CHAN_TO_BASE(or_conn->chan)); switch (result) { CASE_TOR_TLS_ERROR_ANY: case TOR_TLS_CLOSE: - log_info(LD_NET,result!=TOR_TLS_CLOSE? + log_info(LD_NET, result != TOR_TLS_CLOSE ? "tls error. breaking.":"TLS connection closed on flush"); /* Don't flush; connection is dead. */ + connection_or_notify_error(or_conn, + END_OR_CONN_REASON_MISC, + result != TOR_TLS_CLOSE ? + "TLS error in during flush" : + "TLS closed during flush"); connection_close_immediate(conn); connection_mark_for_close(conn); return -1; @@ -3325,8 +3360,16 @@ connection_handle_write_impl(connection_t *conn, int force) if (result > 0) { /* If we wrote any bytes from our buffer, then call the appropriate * functions. */ - if (connection_flushed_some(conn) < 0) + if (connection_flushed_some(conn) < 0) { + if (connection_speaks_cells(conn)) { + connection_or_notify_error(TO_OR_CONN(conn), + END_OR_CONN_REASON_MISC, + "Got error back from " + "connection_flushed_some()"); + } + connection_mark_for_close(conn); + } } if (!connection_wants_to_flush(conn)) { /* it's done flushing */ @@ -4125,7 +4168,6 @@ assert_connection_ok(connection_t *conn, time_t now) case CONN_TYPE_OR: tor_assert(conn->state >= _OR_CONN_STATE_MIN); tor_assert(conn->state <= _OR_CONN_STATE_MAX); - tor_assert(TO_OR_CONN(conn)->n_circuits >= 0); break; case CONN_TYPE_EXIT: tor_assert(conn->state >= _EXIT_CONN_STATE_MIN); diff --git a/src/or/connection_or.c b/src/or/connection_or.c index dbd8757669..a3df7759e0 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -12,6 +12,13 @@ #include "or.h" #include "buffers.h" +/* + * Define this so we get channel internal functions, since we're implementing + * part of a subclass (channel_tls_t). + */ +#define _TOR_CHANNEL_INTERNAL +#include "channel.h" +#include "channeltls.h" #include "circuitbuild.h" #include "circuitlist.h" #include "command.h" @@ -43,6 +50,17 @@ static int connection_or_check_valid_tls_handshake(or_connection_t *conn, static void connection_or_tls_renegotiated_cb(tor_tls_t *tls, void *_conn); +static unsigned int +connection_or_is_bad_for_new_circs(or_connection_t *or_conn); +static void connection_or_mark_bad_for_new_circs(or_connection_t *or_conn); + +/* + * Call this when changing connection state, so notifications to the owning + * channel can be handled. + */ + +static void connection_or_change_state(or_connection_t *conn, uint8_t state); + #ifdef USE_BUFFEREVENTS static void connection_or_handle_event_cb(struct bufferevent *bufev, short event, void *arg); @@ -127,8 +145,11 @@ connection_or_set_identity_digest(or_connection_t *conn, const char *digest) return; /* If the identity was set previously, remove the old mapping. */ - if (! tor_digest_is_zero(conn->identity_digest)) + if (! tor_digest_is_zero(conn->identity_digest)) { connection_or_remove_from_identity_map(conn); + if (conn->chan) + channel_clear_identity_digest(TLS_CHAN_TO_BASE(conn->chan)); + } memcpy(conn->identity_digest, digest, DIGEST_LEN); @@ -139,6 +160,10 @@ connection_or_set_identity_digest(or_connection_t *conn, const char *digest) tmp = digestmap_set(orconn_identity_map, digest, conn); conn->next_with_same_id = tmp; + /* Deal with channels */ + if (conn->chan) + channel_set_identity_digest(TLS_CHAN_TO_BASE(conn->chan), digest); + #if 1 /* Testing code to check for bugs in representation. */ for (; tmp; tmp = tmp->next_with_same_id) { @@ -282,6 +307,40 @@ connection_or_report_broken_states(int severity, int domain) smartlist_free(items); } +/** Call this to change or_connection_t states, so the owning channel_tls_t can + * be notified. + */ + +static void +connection_or_change_state(or_connection_t *conn, uint8_t state) +{ + uint8_t old_state; + + tor_assert(conn); + + old_state = conn->_base.state; + conn->_base.state = state; + + if (conn->chan) + channel_tls_handle_state_change_on_orconn(conn->chan, conn, + old_state, state); +} + +/** Return the number of circuits using an or_connection_t; this used to + * be an or_connection_t field, but it got moved to channel_t and we + * shouldn't maintain two copies. */ + +int +connection_or_get_num_circuits(or_connection_t *conn) +{ + tor_assert(conn); + + if (conn->chan) { + tor_assert(!(TLS_CHAN_TO_BASE(conn->chan)->is_listener)); + return TLS_CHAN_TO_BASE(conn->chan)->u.cell_chan.n_circuits; + } else return 0; +} + /**************************************************************/ /** Pack the cell_t host-order structure src into network-order @@ -345,8 +404,11 @@ var_cell_free(var_cell_t *cell) int connection_or_reached_eof(or_connection_t *conn) { + tor_assert(conn); + log_info(LD_OR,"OR connection reached EOF. Closing."); - connection_mark_for_close(TO_CONN(conn)); + connection_or_close_normally(conn, 1); + return 0; } @@ -375,9 +437,12 @@ connection_or_process_inbuf(or_connection_t *conn) tor_assert(TO_CONN(conn)->proxy_state == PROXY_CONNECTED); if (connection_tls_start_handshake(conn, 0) < 0) ret = -1; + /* Touch the channel's active timestamp if there is one */ + if (conn->chan) + channel_timestamp_active(TLS_CHAN_TO_BASE(conn->chan)); } if (ret < 0) { - connection_mark_for_close(TO_CONN(conn)); + connection_or_close_for_error(conn, 0); } return ret; @@ -410,7 +475,7 @@ connection_or_process_inbuf(or_connection_t *conn) connection_or_nonopen_was_started_here(conn) ? "to" : "from", conn->_base.address, conn->_base.port, conn_state_to_string(conn->_base.type, conn->_base.state)); - connection_mark_for_close(TO_CONN(conn)); + connection_or_close_for_error(conn, 0); ret = -1; } @@ -430,18 +495,31 @@ connection_or_process_inbuf(or_connection_t *conn) int connection_or_flushed_some(or_connection_t *conn) { - size_t datalen = connection_get_outbuf_len(TO_CONN(conn)); + size_t datalen, temp; + ssize_t n, flushed; + /* If we're under the low water mark, add cells until we're just over the * high water mark. */ + datalen = connection_get_outbuf_len(TO_CONN(conn)); if (datalen < OR_CONN_LOWWATER) { - ssize_t n = CEIL_DIV(OR_CONN_HIGHWATER - datalen, CELL_NETWORK_SIZE); - time_t now = approx_time(); - while (conn->active_circuits && n > 0) { - int flushed; - flushed = connection_or_flush_from_first_active_circuit(conn, 1, now); - n -= flushed; + while ((conn->chan) && channel_tls_more_to_flush(conn->chan)) { + /* Compute how many more cells we want at most */ + n = CEIL_DIV(OR_CONN_HIGHWATER - datalen, CELL_NETWORK_SIZE); + /* Bail out if we don't want any more */ + if (n <= 0) break; + /* We're still here; try to flush some more cells */ + flushed = channel_tls_flush_some_cells(conn->chan, n); + /* Bail out if it says it didn't flush anything */ + if (flushed <= 0) break; + /* How much in the outbuf now? */ + temp = connection_get_outbuf_len(TO_CONN(conn)); + /* Bail out if we didn't actually increase the outbuf size */ + if (temp <= datalen) break; + /* Update datalen for the next iteration */ + datalen = temp; } } + return 0; } @@ -480,6 +558,7 @@ connection_or_finished_connecting(or_connection_t *or_conn) { const int proxy_type = or_conn->proxy_type; connection_t *conn; + tor_assert(or_conn); conn = TO_CONN(or_conn); tor_assert(conn->state == OR_CONN_STATE_CONNECTING); @@ -491,18 +570,18 @@ connection_or_finished_connecting(or_connection_t *or_conn) if (proxy_type != PROXY_NONE) { /* start proxy handshake */ if (connection_proxy_connect(conn, proxy_type) < 0) { - connection_mark_for_close(conn); + connection_or_close_for_error(or_conn, 0); return -1; } connection_start_reading(conn); - conn->state = OR_CONN_STATE_PROXY_HANDSHAKING; + connection_or_change_state(or_conn, OR_CONN_STATE_PROXY_HANDSHAKING); return 0; } if (connection_tls_start_handshake(or_conn, 0) < 0) { /* TLS handshaking error of some kind. */ - connection_mark_for_close(conn); + connection_or_close_for_error(or_conn, 0); return -1; } return 0; @@ -516,11 +595,14 @@ connection_or_about_to_close(or_connection_t *or_conn) time_t now = time(NULL); connection_t *conn = TO_CONN(or_conn); + /* Tell the controlling channel we're closed */ + if (or_conn->chan) { + channel_closed(TLS_CHAN_TO_BASE(or_conn->chan)); + or_conn->chan = NULL; + } + /* Remember why we're closing this connection. */ if (conn->state != OR_CONN_STATE_OPEN) { - /* Inform any pending (not attached) circs that they should - * give up. */ - circuit_n_conn_done(TO_OR_CONN(conn), 0); /* now mark things down as needed */ if (connection_or_nonopen_was_started_here(or_conn)) { const or_options_t *options = get_options(); @@ -548,9 +630,6 @@ connection_or_about_to_close(or_connection_t *or_conn) control_event_or_conn_status(or_conn, OR_CONN_EVENT_CLOSED, tls_error_to_orconn_end_reason(or_conn->tls_error)); } - /* Now close all the attached circuits on it. */ - circuit_unlink_all_from_or_conn(TO_OR_CONN(conn), - END_CIRC_REASON_OR_CONN_CLOSED); } /** Return 1 if identity digest id_digest is known to be a @@ -708,152 +787,26 @@ connection_or_init_conn_from_address(or_connection_t *conn, } } -/** Return true iff a is "better" than b for new circuits. - * - * A more canonical connection is always better than a less canonical - * connection. That aside, a connection is better if it has circuits and the - * other does not, or if it was created more recently. - * - * Requires that both input connections are open; not is_bad_for_new_circs, - * and not impossibly non-canonical. - * - * If forgive_new_connections is true, then we do not call - * abetter than b simply because b has no circuits, - * unless b is also relatively old. - */ -static int -connection_or_is_better(time_t now, - const or_connection_t *a, - const or_connection_t *b, - int forgive_new_connections) -{ - int newer; -/** Do not definitively deprecate a new connection with no circuits on it - * until this much time has passed. */ -#define NEW_CONN_GRACE_PERIOD (15*60) - - if (b->is_canonical && !a->is_canonical) - return 0; /* A canonical connection is better than a non-canonical - * one, no matter how new it is or which has circuits. */ - - newer = b->_base.timestamp_created < a->_base.timestamp_created; - - if ( - /* We prefer canonical connections regardless of newness. */ - (!b->is_canonical && a->is_canonical) || - /* If both have circuits we prefer the newer: */ - (b->n_circuits && a->n_circuits && newer) || - /* If neither has circuits we prefer the newer: */ - (!b->n_circuits && !a->n_circuits && newer)) - return 1; +/** These just pass all the is_bad_for_new_circs manipulation on to + * channel_t */ - /* If one has no circuits and the other does... */ - if (!b->n_circuits && a->n_circuits) { - /* Then it's bad, unless it's in its grace period and we're forgiving. */ - if (forgive_new_connections && - now < b->_base.timestamp_created + NEW_CONN_GRACE_PERIOD) - return 0; - else - return 1; - } +static unsigned int +connection_or_is_bad_for_new_circs(or_connection_t *or_conn) +{ + tor_assert(or_conn); - return 0; + if (or_conn->chan) + return channel_is_bad_for_new_circs(TLS_CHAN_TO_BASE(or_conn->chan)); + else return 0; } -/** Return the OR connection we should use to extend a circuit to the router - * whose identity is digest, and whose address we believe (or have been - * told in an extend cell) is target_addr. If there is no good - * connection, set *msg_out to a message describing the connection's - * state and our next action, and set launch_out to a boolean for - * whether we should launch a new connection or not. - */ -or_connection_t * -connection_or_get_for_extend(const char *digest, - const tor_addr_t *target_addr, - const char **msg_out, - int *launch_out) +static void +connection_or_mark_bad_for_new_circs(or_connection_t *or_conn) { - or_connection_t *conn, *best=NULL; - int n_inprogress_goodaddr = 0, n_old = 0, n_noncanonical = 0, n_possible = 0; - time_t now = approx_time(); - - tor_assert(msg_out); - tor_assert(launch_out); - - if (!orconn_identity_map) { - *msg_out = "Router not connected (nothing is). Connecting."; - *launch_out = 1; - return NULL; - } - - conn = digestmap_get(orconn_identity_map, digest); - - for (; conn; conn = conn->next_with_same_id) { - tor_assert(conn->_base.magic == OR_CONNECTION_MAGIC); - tor_assert(conn->_base.type == CONN_TYPE_OR); - tor_assert(tor_memeq(conn->identity_digest, digest, DIGEST_LEN)); - if (conn->_base.marked_for_close) - continue; - /* Never return a connection on which the other end appears to be - * a client. */ - if (conn->is_connection_with_client) { - continue; - } - /* Never return a non-open connection. */ - if (conn->_base.state != OR_CONN_STATE_OPEN) { - /* If the address matches, don't launch a new connection for this - * circuit. */ - if (!tor_addr_compare(&conn->real_addr, target_addr, CMP_EXACT)) - ++n_inprogress_goodaddr; - continue; - } - /* Never return a connection that shouldn't be used for circs. */ - if (conn->is_bad_for_new_circs) { - ++n_old; - continue; - } - /* Never return a non-canonical connection using a recent link protocol - * if the address is not what we wanted. - * - * (For old link protocols, we can't rely on is_canonical getting - * set properly if we're talking to the right address, since we might - * have an out-of-date descriptor, and we will get no NETINFO cell to - * tell us about the right address.) */ - if (!conn->is_canonical && conn->link_proto >= 2 && - tor_addr_compare(&conn->real_addr, target_addr, CMP_EXACT)) { - ++n_noncanonical; - continue; - } - - ++n_possible; - - if (!best) { - best = conn; /* If we have no 'best' so far, this one is good enough. */ - continue; - } - - if (connection_or_is_better(now, conn, best, 0)) - best = conn; - } + tor_assert(or_conn); - if (best) { - *msg_out = "Connection is fine; using it."; - *launch_out = 0; - return best; - } else if (n_inprogress_goodaddr) { - *msg_out = "Connection in progress; waiting."; - *launch_out = 0; - return NULL; - } else if (n_old || n_noncanonical) { - *msg_out = "Connections all too old, or too non-canonical. " - " Launching a new one."; - *launch_out = 1; - return NULL; - } else { - *msg_out = "Not connected. Connecting."; - *launch_out = 1; - return NULL; - } + if (or_conn->chan) + channel_mark_bad_for_new_circs(TLS_CHAN_TO_BASE(or_conn->chan)); } /** How old do we let a connection to an OR get before deciding it's @@ -874,8 +827,8 @@ connection_or_get_for_extend(const char *digest, * - all open non-canonical connections for which a 'better' non-canonical * connection exists to the same router at the same address. * - * See connection_or_is_better() for our idea of what makes one OR connection - * better than another. + * See channel_is_better() in channel.c for our idea of what makes one OR + * connection better than another. */ static void connection_or_group_set_badness(or_connection_t *head, int force) @@ -888,7 +841,7 @@ connection_or_group_set_badness(or_connection_t *head, int force) * everything else is. */ for (or_conn = head; or_conn; or_conn = or_conn->next_with_same_id) { if (or_conn->_base.marked_for_close || - or_conn->is_bad_for_new_circs) + connection_or_is_bad_for_new_circs(or_conn)) continue; if (force || or_conn->_base.timestamp_created + TIME_BEFORE_OR_CONN_IS_TOO_OLD @@ -898,10 +851,10 @@ connection_or_group_set_badness(or_connection_t *head, int force) "(fd %d, %d secs old).", or_conn->_base.address, or_conn->_base.port, or_conn->_base.s, (int)(now - or_conn->_base.timestamp_created)); - or_conn->is_bad_for_new_circs = 1; + connection_or_mark_bad_for_new_circs(or_conn); } - if (or_conn->is_bad_for_new_circs) { + if (connection_or_is_bad_for_new_circs(or_conn)) { ++n_old; } else if (or_conn->_base.state != OR_CONN_STATE_OPEN) { ++n_inprogress; @@ -916,7 +869,7 @@ connection_or_group_set_badness(or_connection_t *head, int force) * expire everything that's worse, and find the very best if we can. */ for (or_conn = head; or_conn; or_conn = or_conn->next_with_same_id) { if (or_conn->_base.marked_for_close || - or_conn->is_bad_for_new_circs) + connection_or_is_bad_for_new_circs(or_conn)) continue; /* This one doesn't need to be marked bad. */ if (or_conn->_base.state != OR_CONN_STATE_OPEN) continue; /* Don't mark anything bad until we have seen what happens @@ -930,12 +883,17 @@ connection_or_group_set_badness(or_connection_t *head, int force) "another connection to that OR that is.", or_conn->_base.address, or_conn->_base.port, or_conn->_base.s, (int)(now - or_conn->_base.timestamp_created)); - or_conn->is_bad_for_new_circs = 1; + connection_or_mark_bad_for_new_circs(or_conn); continue; } - if (!best || connection_or_is_better(now, or_conn, best, 0)) + if (!best || + channel_is_better(now, + TLS_CHAN_TO_BASE(or_conn->chan), + TLS_CHAN_TO_BASE(best->chan), + 0)) { best = or_conn; + } } if (!best) @@ -957,10 +915,13 @@ connection_or_group_set_badness(or_connection_t *head, int force) */ for (or_conn = head; or_conn; or_conn = or_conn->next_with_same_id) { if (or_conn->_base.marked_for_close || - or_conn->is_bad_for_new_circs || + connection_or_is_bad_for_new_circs(or_conn) || or_conn->_base.state != OR_CONN_STATE_OPEN) continue; - if (or_conn != best && connection_or_is_better(now, best, or_conn, 1)) { + if (or_conn != best && + channel_is_better(now, + TLS_CHAN_TO_BASE(best->chan), + TLS_CHAN_TO_BASE(or_conn->chan), 1)) { /* This isn't the best conn, _and_ the best conn is better than it, even when we're being forgiving. */ if (best->is_canonical) { @@ -971,7 +932,7 @@ connection_or_group_set_badness(or_connection_t *head, int force) or_conn->_base.address, or_conn->_base.port, or_conn->_base.s, (int)(now - or_conn->_base.timestamp_created), best->_base.s, (int)(now - best->_base.timestamp_created)); - or_conn->is_bad_for_new_circs = 1; + connection_or_mark_bad_for_new_circs(or_conn); } else if (!tor_addr_compare(&or_conn->real_addr, &best->real_addr, CMP_EXACT)) { log_info(LD_OR, @@ -981,7 +942,7 @@ connection_or_group_set_badness(or_connection_t *head, int force) or_conn->_base.address, or_conn->_base.port, or_conn->_base.s, (int)(now - or_conn->_base.timestamp_created), best->_base.s, (int)(now - best->_base.timestamp_created)); - or_conn->is_bad_for_new_circs = 1; + connection_or_mark_bad_for_new_circs(or_conn); } } } @@ -1019,8 +980,43 @@ connection_or_connect_failed(or_connection_t *conn, control_event_bootstrap_problem(msg, reason); } +/** conn got an error in connection_handle_read_impl() or + * connection_handle_write_impl() and is going to die soon. + * + * reason specifies the or_conn_end_reason for the failure; + * msg specifies the strerror-style error message. + */ +void +connection_or_notify_error(or_connection_t *conn, + int reason, const char *msg) +{ + channel_t *chan; + + tor_assert(conn); + + /* If we're connecting, call connect_failed() too */ + if (TO_CONN(conn)->state == OR_CONN_STATE_CONNECTING) + connection_or_connect_failed(conn, reason, msg); + + /* Tell the controlling channel if we have one */ + if (conn->chan) { + chan = TLS_CHAN_TO_BASE(conn->chan); + /* This shouldn't ever happen in the listening state */ + tor_assert(chan->state != CHANNEL_STATE_LISTENING); + /* Don't transition if we're already in closing, closed or error */ + if (!(chan->state == CHANNEL_STATE_CLOSING || + chan->state == CHANNEL_STATE_CLOSED || + chan->state == CHANNEL_STATE_ERROR)) { + channel_close_for_error(chan); + } + } + + /* No need to mark for error because connection.c is about to do that */ +} + /** Launch a new OR connection to addr:port and expect to - * handshake with an OR with identity digest id_digest. + * handshake with an OR with identity digest id_digest. Optionally, + * pass in a pointer to a channel using this connection. * * If id_digest is me, do nothing. If we're already connected to it, * return that connection. If the connect() is in progress, set the @@ -1035,7 +1031,8 @@ connection_or_connect_failed(or_connection_t *conn, */ or_connection_t * connection_or_connect(const tor_addr_t *_addr, uint16_t port, - const char *id_digest) + const char *id_digest, + channel_tls_t *chan) { or_connection_t *conn; const or_options_t *options = get_options(); @@ -1058,9 +1055,17 @@ connection_or_connect(const tor_addr_t *_addr, uint16_t port, conn = or_connection_new(tor_addr_family(&addr)); - /* set up conn so it's got all the data we need to remember */ + /* + * Set up conn so it's got all the data we need to remember for channels + * + * This stuff needs to happen before connection_or_init_conn_from_address() + * so connection_or_set_identity_digest() and such know where to look to + * keep the channel up to date. + */ + conn->chan = chan; + chan->conn = conn; connection_or_init_conn_from_address(conn, &addr, port, id_digest, 1); - conn->_base.state = OR_CONN_STATE_CONNECTING; + connection_or_change_state(conn, OR_CONN_STATE_CONNECTING); control_event_or_conn_status(conn, OR_CONN_EVENT_LAUNCHED, 0); conn->is_outgoing = 1; @@ -1129,6 +1134,56 @@ connection_or_connect(const tor_addr_t *_addr, uint16_t port, return conn; } +/** Mark orconn for close and transition the associated channel, if any, to + * the closing state. + */ + +void +connection_or_close_normally(or_connection_t *orconn, int flush) +{ + channel_t *chan = NULL; + + tor_assert(orconn); + if (flush) connection_mark_and_flush(TO_CONN(orconn)); + else connection_mark_for_close(TO_CONN(orconn)); + if (orconn->chan) { + chan = TLS_CHAN_TO_BASE(orconn->chan); + /* This shouldn't ever happen in the listening state */ + tor_assert(chan->state != CHANNEL_STATE_LISTENING); + /* Don't transition if we're already in closing, closed or error */ + if (!(chan->state == CHANNEL_STATE_CLOSING || + chan->state == CHANNEL_STATE_CLOSED || + chan->state == CHANNEL_STATE_ERROR)) { + channel_close_from_lower_layer(chan); + } + } +} + +/** Mark orconn for close and transition the associated channel, if any, to + * the error state. + */ + +void +connection_or_close_for_error(or_connection_t *orconn, int flush) +{ + channel_t *chan = NULL; + + tor_assert(orconn); + if (flush) connection_mark_and_flush(TO_CONN(orconn)); + else connection_mark_for_close(TO_CONN(orconn)); + if (orconn->chan) { + chan = TLS_CHAN_TO_BASE(orconn->chan); + /* This shouldn't ever happen in the listening state */ + tor_assert(chan->state != CHANNEL_STATE_LISTENING); + /* Don't transition if we're already in closing, closed or error */ + if (!(chan->state == CHANNEL_STATE_CLOSING || + chan->state == CHANNEL_STATE_CLOSED || + chan->state == CHANNEL_STATE_ERROR)) { + channel_close_for_error(chan); + } + } +} + /** Begin the tls handshake with conn. receiving is 0 if * we initiated the connection, else it's 1. * @@ -1140,7 +1195,23 @@ connection_or_connect(const tor_addr_t *_addr, uint16_t port, int connection_tls_start_handshake(or_connection_t *conn, int receiving) { - conn->_base.state = OR_CONN_STATE_TLS_HANDSHAKING; + channel_t *chan_listener, *chan; + + /* Incoming connections will need a new channel passed to the + * channel_tls_listener */ + if (receiving) { + /* It shouldn't already be set */ + tor_assert(!(conn->chan)); + chan_listener = channel_tls_get_listener(); + if (!chan_listener) { + chan_listener = channel_tls_start_listener(); + command_setup_listener(chan_listener); + } + chan = channel_tls_handle_incoming(conn); + channel_queue_incoming(chan_listener, chan); + } + + connection_or_change_state(conn, OR_CONN_STATE_TLS_HANDSHAKING); tor_assert(!conn->tls); conn->tls = tor_tls_new(conn->_base.s, receiving); if (!conn->tls) { @@ -1201,7 +1272,7 @@ connection_or_tls_renegotiated_cb(tor_tls_t *tls, void *_conn) if (connection_tls_finish_handshake(conn) < 0) { /* XXXX_TLS double-check that it's ok to do this from inside read. */ /* XXXX_TLS double-check that this verifies certificates. */ - connection_mark_for_close(TO_CONN(conn)); + connection_or_close_for_error(conn, 0); } } @@ -1242,7 +1313,8 @@ connection_tls_continue_handshake(or_connection_t *conn) } else { log_debug(LD_OR, "Done with initial SSL handshake (client-side)." " Requesting renegotiation."); - conn->_base.state = OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING; + connection_or_change_state(conn, + OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING); goto again; } } @@ -1254,7 +1326,8 @@ connection_tls_continue_handshake(or_connection_t *conn) tor_tls_set_renegotiate_callback(conn->tls, connection_or_tls_renegotiated_cb, conn); - conn->_base.state = OR_CONN_STATE_TLS_SERVER_RENEGOTIATING; + connection_or_change_state(conn, + OR_CONN_STATE_TLS_SERVER_RENEGOTIATING); connection_stop_writing(TO_CONN(conn)); connection_start_reading(TO_CONN(conn)); return 0; @@ -1287,7 +1360,7 @@ connection_or_handle_event_cb(struct bufferevent *bufev, short event, if (conn->_base.state == OR_CONN_STATE_TLS_HANDSHAKING) { if (tor_tls_finish_handshake(conn->tls) < 0) { log_warn(LD_OR, "Problem finishing handshake"); - connection_mark_for_close(TO_CONN(conn)); + connection_or_close_for_error(conn, 0); return; } } @@ -1298,14 +1371,15 @@ connection_or_handle_event_cb(struct bufferevent *bufev, short event, if (tor_tls_received_v3_certificate(conn->tls)) { log_info(LD_OR, "Client got a v3 cert!"); if (connection_or_launch_v3_or_handshake(conn) < 0) - connection_mark_for_close(TO_CONN(conn)); + connection_or_close_for_error(conn, 0); return; } else { - conn->_base.state = OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING; + connection_or_change_state(conn, + OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING); tor_tls_unblock_renegotiation(conn->tls); if (bufferevent_ssl_renegotiate(conn->_base.bufev)<0) { log_warn(LD_OR, "Start_renegotiating went badly."); - connection_mark_for_close(TO_CONN(conn)); + connection_or_close_for_error(conn, 0); } tor_tls_unblock_renegotiation(conn->tls); return; /* ???? */ @@ -1320,7 +1394,8 @@ connection_or_handle_event_cb(struct bufferevent *bufev, short event, tor_tls_set_renegotiate_callback(conn->tls, connection_or_tls_renegotiated_cb, conn); - conn->_base.state = OR_CONN_STATE_TLS_SERVER_RENEGOTIATING; + connection_or_change_state(conn, + OR_CONN_STATE_TLS_SERVER_RENEGOTIATING); } else if (handshakes == 2) { /* v2 handshake, as a server. Two handshakes happened already, * so we treat renegotiation as done. @@ -1329,18 +1404,18 @@ connection_or_handle_event_cb(struct bufferevent *bufev, short event, } else if (handshakes > 2) { log_warn(LD_OR, "More than two handshakes done on connection. " "Closing."); - connection_mark_for_close(TO_CONN(conn)); + connection_or_close_for_error(conn, 0); } else { log_warn(LD_BUG, "We were unexpectedly told that a connection " "got %d handshakes. Closing.", handshakes); - connection_mark_for_close(TO_CONN(conn)); + connection_or_close_for_error(conn, 0); } return; } } connection_watch_events(TO_CONN(conn), READ_EVENT|WRITE_EVENT); if (connection_tls_finish_handshake(conn) < 0) - connection_mark_for_close(TO_CONN(conn)); /* ???? */ + connection_or_close_for_error(conn, 0); /* ???? */ return; } @@ -1370,29 +1445,6 @@ connection_or_nonopen_was_started_here(or_connection_t *conn) return !tor_tls_is_server(conn->tls); } -/** Set the circid_type field of conn (which determines which part of - * the circuit ID space we're willing to use) based on comparing our ID to - * identity_rcvd */ -void -connection_or_set_circid_type(or_connection_t *conn, - crypto_pk_t *identity_rcvd) -{ - const int started_here = connection_or_nonopen_was_started_here(conn); - crypto_pk_t *our_identity = - started_here ? get_tlsclient_identity_key() : - get_server_identity_key(); - - if (identity_rcvd) { - if (crypto_pk_cmp_keys(our_identity, identity_rcvd)<0) { - conn->circ_id_type = CIRC_ID_TYPE_LOWER; - } else { - conn->circ_id_type = CIRC_ID_TYPE_HIGHER; - } - } else { - conn->circ_id_type = CIRC_ID_TYPE_NEITHER; - } -} - /** Conn just completed its handshake. Return 0 if all is well, and * return -1 if he is lying, broken, or otherwise something is wrong. * @@ -1470,7 +1522,8 @@ connection_or_check_valid_tls_handshake(or_connection_t *conn, memset(digest_rcvd_out, 0, DIGEST_LEN); } - connection_or_set_circid_type(conn, identity_rcvd); + tor_assert(conn->chan); + channel_set_circid_type(TLS_CHAN_TO_BASE(conn->chan), identity_rcvd); crypto_pk_free(identity_rcvd); if (started_here) @@ -1547,6 +1600,19 @@ connection_or_client_learned_peer_id(or_connection_t *conn, return 0; } +/** Return when a client used this, for connection.c, since client_used + * is now one of the timestamps of channel_t */ + +time_t +connection_or_client_used(or_connection_t *conn) +{ + tor_assert(conn); + + if (conn->chan) { + return channel_when_last_client(TLS_CHAN_TO_BASE(conn->chan)); + } else return 0; +} + /** The v1/v2 TLS handshake is finished. * * Make sure we are happy with the person we just handshaked with. @@ -1588,7 +1654,7 @@ connection_tls_finish_handshake(or_connection_t *conn) tor_tls_block_renegotiation(conn->tls); return connection_or_set_state_open(conn); } else { - conn->_base.state = OR_CONN_STATE_OR_HANDSHAKING_V2; + connection_or_change_state(conn, OR_CONN_STATE_OR_HANDSHAKING_V2); if (connection_init_or_handshake_state(conn, started_here) < 0) return -1; if (!started_here) { @@ -1613,7 +1679,7 @@ connection_or_launch_v3_or_handshake(or_connection_t *conn) circuit_build_times_network_is_live(&circ_times); - conn->_base.state = OR_CONN_STATE_OR_HANDSHAKING_V3; + connection_or_change_state(conn, OR_CONN_STATE_OR_HANDSHAKING_V3); if (connection_init_or_handshake_state(conn, 1) < 0) return -1; @@ -1732,35 +1798,9 @@ or_handshake_state_record_var_cell(or_handshake_state_t *state, int connection_or_set_state_open(or_connection_t *conn) { - int started_here = connection_or_nonopen_was_started_here(conn); - time_t now = time(NULL); - conn->_base.state = OR_CONN_STATE_OPEN; + connection_or_change_state(conn, OR_CONN_STATE_OPEN); control_event_or_conn_status(conn, OR_CONN_EVENT_CONNECTED, 0); - if (started_here) { - circuit_build_times_network_is_live(&circ_times); - rep_hist_note_connect_succeeded(conn->identity_digest, now); - if (entry_guard_register_connect_status(conn->identity_digest, - 1, 0, now) < 0) { - /* Close any circuits pending on this conn. We leave it in state - * 'open' though, because it didn't actually *fail* -- we just - * chose not to use it. (Otherwise - * connection_about_to_close_connection() will call a big pile of - * functions to indicate we shouldn't try it again.) */ - log_debug(LD_OR, "New entry guard was reachable, but closing this " - "connection so we can retry the earlier entry guards."); - circuit_n_conn_done(conn, 0); - return -1; - } - router_set_status(conn->identity_digest, 1); - } else { - /* only report it to the geoip module if it's not a known router */ - if (!router_get_by_id_digest(conn->identity_digest)) { - geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &TO_CONN(conn)->addr, - now); - } - } - or_handshake_state_free(conn->handshake_state); conn->handshake_state = NULL; IF_HAS_BUFFEREVENT(TO_CONN(conn), { @@ -1769,8 +1809,6 @@ connection_or_set_state_open(or_connection_t *conn) connection_start_reading(TO_CONN(conn)); } - circuit_n_conn_done(conn, 1); /* send the pending creates, if any. */ - return 0; } @@ -1790,6 +1828,10 @@ connection_or_write_cell_to_buf(const cell_t *cell, or_connection_t *conn) connection_write_to_buf(networkcell.body, CELL_NETWORK_SIZE, TO_CONN(conn)); + /* Touch the channel's active timestamp if there is one */ + if (conn->chan) + channel_timestamp_active(TLS_CHAN_TO_BASE(conn->chan)); + if (conn->_base.state == OR_CONN_STATE_OR_HANDSHAKING_V3) or_handshake_state_record_cell(conn->handshake_state, cell, 0); @@ -1816,6 +1858,10 @@ connection_or_write_var_cell_to_buf(const var_cell_t *cell, or_handshake_state_record_var_cell(conn->handshake_state, cell, 0); if (cell->command != CELL_PADDING) conn->timestamp_last_added_nonpadding = approx_time(); + + /* Touch the channel's active timestamp if there is one */ + if (conn->chan) + channel_timestamp_active(TLS_CHAN_TO_BASE(conn->chan)); } /** See whether there's a variable-length cell waiting on or_conn's @@ -1852,8 +1898,13 @@ connection_or_process_cells_from_inbuf(or_connection_t *conn) if (connection_fetch_var_cell_from_buf(conn, &var_cell)) { if (!var_cell) return 0; /* not yet. */ + + /* Touch the channel's active timestamp if there is one */ + if (conn->chan) + channel_timestamp_active(TLS_CHAN_TO_BASE(conn->chan)); + circuit_build_times_network_is_live(&circ_times); - command_process_var_cell(var_cell, conn); + channel_tls_handle_var_cell(var_cell, conn); var_cell_free(var_cell); } else { char buf[CELL_NETWORK_SIZE]; @@ -1862,6 +1913,10 @@ connection_or_process_cells_from_inbuf(or_connection_t *conn) < CELL_NETWORK_SIZE) /* whole response available? */ return 0; /* not yet */ + /* Touch the channel's active timestamp if there is one */ + if (conn->chan) + channel_timestamp_active(TLS_CHAN_TO_BASE(conn->chan)); + circuit_build_times_network_is_live(&circ_times); connection_fetch_from_buf(buf, CELL_NETWORK_SIZE, TO_CONN(conn)); @@ -1869,34 +1924,11 @@ connection_or_process_cells_from_inbuf(or_connection_t *conn) * network-order string) */ cell_unpack(&cell, buf); - command_process_cell(&cell, conn); + channel_tls_handle_cell(&cell, conn); } } } -/** Write a destroy cell with circ ID circ_id and reason reason - * onto OR connection conn. Don't perform range-checking on reason: - * we may want to propagate reasons from other cells. - * - * Return 0. - */ -int -connection_or_send_destroy(circid_t circ_id, or_connection_t *conn, int reason) -{ - cell_t cell; - - tor_assert(conn); - - memset(&cell, 0, sizeof(cell_t)); - cell.circ_id = circ_id; - cell.command = CELL_DESTROY; - cell.payload[0] = (uint8_t) reason; - log_debug(LD_OR,"Sending destroy (circID %d).", circ_id); - - connection_or_write_cell_to_buf(&cell, conn); - return 0; -} - /** Array of recognized link protocol versions. */ static const uint16_t or_protocol_versions[] = { 1, 2, 3 }; /** Number of versions in or_protocol_versions. */ diff --git a/src/or/connection_or.h b/src/or/connection_or.h index 3e98f5cce1..22126b05fe 100644 --- a/src/or/connection_or.h +++ b/src/or/connection_or.h @@ -33,8 +33,14 @@ void connection_or_update_token_buckets(smartlist_t *conns, void connection_or_connect_failed(or_connection_t *conn, int reason, const char *msg); +void connection_or_notify_error(or_connection_t *conn, + int reason, const char *msg); or_connection_t *connection_or_connect(const tor_addr_t *addr, uint16_t port, - const char *id_digest); + const char *id_digest, + channel_tls_t *chan); + +void connection_or_close_normally(or_connection_t *orconn, int flush); +void connection_or_close_for_error(or_connection_t *orconn, int flush); void connection_or_report_broken_states(int severity, int domain); @@ -50,8 +56,8 @@ void connection_or_init_conn_from_address(or_connection_t *conn, int started_here); int connection_or_client_learned_peer_id(or_connection_t *conn, const uint8_t *peer_id); -void connection_or_set_circid_type(or_connection_t *conn, - crypto_pk_t *identity_rcvd); +time_t connection_or_client_used(or_connection_t *conn); +int connection_or_get_num_circuits(or_connection_t *conn); void or_handshake_state_free(or_handshake_state_t *state); void or_handshake_state_record_cell(or_handshake_state_t *state, const cell_t *cell, @@ -65,8 +71,6 @@ void connection_or_write_cell_to_buf(const cell_t *cell, or_connection_t *conn); void connection_or_write_var_cell_to_buf(const var_cell_t *cell, or_connection_t *conn); -int connection_or_send_destroy(circid_t circ_id, or_connection_t *conn, - int reason); int connection_or_send_versions(or_connection_t *conn, int v3_plus); int connection_or_send_netinfo(or_connection_t *conn); int connection_or_send_certs_cell(or_connection_t *conn); diff --git a/src/or/include.am b/src/or/include.am index b9032d97eb..c323575320 100644 --- a/src/or/include.am +++ b/src/or/include.am @@ -18,6 +18,7 @@ endif src_or_libtor_a_SOURCES = \ src/or/buffers.c \ src/or/channel.c \ + src/or/channeltls.c \ src/or/circuitbuild.c \ src/or/circuitlist.c \ src/or/circuituse.c \ @@ -88,6 +89,7 @@ src_or_tor_LDADD = src/or/libtor.a src/common/libor.a src/common/libor-crypto.a ORHEADERS = \ src/or/buffers.h \ src/or/channel.h \ + src/or/channeltls.h \ src/or/circuitbuild.h \ src/or/circuitlist.h \ src/or/circuituse.h \ diff --git a/src/or/or.h b/src/or/or.h index a916ac86e5..2eee6ec464 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -879,6 +879,102 @@ typedef uint16_t circid_t; /** Identifies a stream on a circuit */ typedef uint16_t streamid_t; +/* channel_t typedef; struct channel_s is in channel.h */ + +typedef struct channel_s channel_t; + +/* channel states for channel_t */ + +typedef enum { + /* + * Closed state - channel is inactive + * + * Permitted transitions from: + * - CHANNEL_STATE_CLOSING + * Permitted transitions to: + * - CHANNEL_STATE_LISTENING + * - CHANNEL_STATE_OPENING + */ + CHANNEL_STATE_CLOSED = 0, + /* + * Listening state - channel is listening for incoming connections + * + * Permitted transitions from: + * - CHANNEL_STATE_CLOSED + * Permitted transitions to: + * - CHANNEL_STATE_CLOSING + * - CHANNEL_STATE_ERROR + */ + CHANNEL_STATE_LISTENING, + /* + * Opening state - channel is trying to connect + * + * Permitted transitions from: + * - CHANNEL_STATE_CLOSED + * Permitted transitions to: + * - CHANNEL_STATE_CLOSING + * - CHANNEL_STATE_ERROR + * - CHANNEL_STATE_OPEN + */ + CHANNEL_STATE_OPENING, + /* + * Open state - channel is active and ready for use + * + * Permitted transitions from: + * - CHANNEL_STATE_MAINT + * - CHANNEL_STATE_OPENING + * Permitted transitions to: + * - CHANNEL_STATE_CLOSING + * - CHANNEL_STATE_ERROR + * - CHANNEL_STATE_MAINT + */ + CHANNEL_STATE_OPEN, + /* + * Maintenance state - channel is temporarily offline for subclass specific + * maintenance activities such as TLS renegotiation. + * + * Permitted transitions from: + * - CHANNEL_STATE_OPEN + * Permitted transitions to: + * - CHANNEL_STATE_CLOSING + * - CHANNEL_STATE_ERROR + * - CHANNEL_STATE_OPEN + */ + CHANNEL_STATE_MAINT, + /* + * Closing state - channel is shutting down + * + * Permitted transitions from: + * - CHANNEL_STATE_MAINT + * - CHANNEL_STATE_OPEN + * Permitted transitions to: + * - CHANNEL_STATE_CLOSED, + * - CHANNEL_STATE_ERROR + */ + CHANNEL_STATE_CLOSING, + /* + * Error state - channel has experienced a permanent error + * + * Permitted transitions from: + * - CHANNEL_STATE_CLOSING + * - CHANNEL_STATE_LISTENING + * - CHANNEL_STATE_MAINT + * - CHANNEL_STATE_OPENING + * - CHANNEL_STATE_OPEN + * Permitted transitions to: + * - None + */ + CHANNEL_STATE_ERROR, + /* + * Placeholder for maximum state value + */ + CHANNEL_STATE_LAST +} channel_state_t; + +/* TLS channel stuff */ + +typedef struct channel_tls_s channel_tls_t; + /** Parsed onion routing cell. All communication between nodes * is via cells. */ typedef struct cell_t { @@ -1061,9 +1157,6 @@ typedef struct connection_t { /** Unique identifier for this connection on this Tor instance. */ uint64_t global_identifier; - - /** Unique ID for measuring tunneled network status requests. */ - uint64_t dirreq_id; } connection_t; /** Subtype of connection_t; used for a listener socket. */ @@ -1203,6 +1296,9 @@ typedef struct or_connection_t { /** When we last used this conn for any client traffic. If not * recent, we can rate limit it further. */ + /* Channel using this connection */ + channel_tls_t *chan; + tor_addr_t real_addr; /**< The actual address that this connection came from * or went to. The addr field is prone to * getting overridden by the address from the router @@ -1245,8 +1341,6 @@ typedef struct or_connection_t { /* XXXX we could share this among all connections. */ struct ev_token_bucket_cfg *bucket_cfg; #endif - int n_circuits; /**< How many circuits use this connection as p_conn or - * n_conn ? */ struct or_connection_t *next_with_same_id; /**< Next connection with same * identity digest as this one. */ @@ -1299,6 +1393,10 @@ typedef struct edge_connection_t { * cells. */ unsigned int edge_blocked_on_circ:1; + /** Unique ID for directory requests; this used to be in connection_t, but + * that's going away and being used on channels instead. We still tag + * edge connections with dirreq_id from circuits, so it's copied here. */ + uint64_t dirreq_id; } edge_connection_t; /** Subtype of edge_connection_t for an "entry connection" -- that is, a SOCKS @@ -1421,6 +1519,10 @@ typedef struct dir_connection_t { char identity_digest[DIGEST_LEN]; /**< Hash of the public RSA key for * the directory server's signing key. */ + /** Unique ID for directory requests; this used to be in connection_t, but + * that's going away and being used on channels instead. The dirserver still + * needs this for the incoming side, so it's moved here. */ + uint64_t dirreq_id; } dir_connection_t; /** Subtype of connection_t for an connection to a controller. */ @@ -1520,98 +1622,6 @@ static INLINE listener_connection_t *TO_LISTENER_CONN(connection_t *c) return DOWNCAST(listener_connection_t, c); } -/* channel_t typedef; struct channel_s is in channel.h */ - -typedef struct channel_s channel_t; - -/* channel states for channel_t */ - -typedef enum { - /* - * Closed state - channel is inactive - * - * Permitted transitions from: - * - CHANNEL_STATE_CLOSING - * Permitted transitions to: - * - CHANNEL_STATE_LISTENING - * - CHANNEL_STATE_OPENING - */ - CHANNEL_STATE_CLOSED = 0, - /* - * Listening state - channel is listening for incoming connections - * - * Permitted transitions from: - * - CHANNEL_STATE_CLOSED - * Permitted transitions to: - * - CHANNEL_STATE_CLOSING - * - CHANNEL_STATE_ERROR - */ - CHANNEL_STATE_LISTENING, - /* - * Opening state - channel is trying to connect - * - * Permitted transitions from: - * - CHANNEL_STATE_CLOSED - * Permitted transitions to: - * - CHANNEL_STATE_CLOSING - * - CHANNEL_STATE_ERROR - * - CHANNEL_STATE_OPEN - */ - CHANNEL_STATE_OPENING, - /* - * Open state - channel is active and ready for use - * - * Permitted transitions from: - * - CHANNEL_STATE_MAINT - * - CHANNEL_STATE_OPENING - * Permitted transitions to: - * - CHANNEL_STATE_CLOSING - * - CHANNEL_STATE_ERROR - * - CHANNEL_STATE_MAINT - */ - CHANNEL_STATE_OPEN, - /* - * Maintenance state - channel is temporarily offline for subclass specific - * maintenance activities such as TLS renegotiation. - * - * Permitted transitions from: - * - CHANNEL_STATE_OPEN - * Permitted transitions to: - * - CHANNEL_STATE_CLOSING - * - CHANNEL_STATE_ERROR - * - CHANNEL_STATE_OPEN - */ - CHANNEL_STATE_MAINT, - /* - * Closing state - channel is shutting down - * - * Permitted transitions from: - * - CHANNEL_STATE_MAINT - * - CHANNEL_STATE_OPEN - * Permitted transitions to: - * - CHANNEL_STATE_CLOSED, - * - CHANNEL_STATE_ERROR - */ - CHANNEL_STATE_CLOSING, - /* - * Error state - channel has experienced a permanent error - * - * Permitted transitions from: - * - CHANNEL_STATE_CLOSING - * - CHANNEL_STATE_LISTENING - * - CHANNEL_STATE_MAINT - * - CHANNEL_STATE_OPENING - * - CHANNEL_STATE_OPEN - * Permitted transitions to: - * - None - */ - CHANNEL_STATE_ERROR, - /* - * Placeholder for maximum state value - */ - CHANNEL_STATE_LAST -} channel_state_t; - /* Conditional macros to help write code that works whether bufferevents are disabled or not. @@ -4199,10 +4209,10 @@ typedef enum { /** Flushed last cell from queue of the circuit that initiated a * tunneled request to the outbuf of the OR connection. */ DIRREQ_CIRC_QUEUE_FLUSHED = 3, - /** Flushed last byte from buffer of the OR connection belonging to the + /** Flushed last byte from buffer of the channel belonging to the * circuit that initiated a tunneled request; completes a tunneled * request. */ - DIRREQ_OR_CONN_BUFFER_FLUSHED = 4 + DIRREQ_CHANNEL_BUFFER_FLUSHED = 4 } dirreq_state_t; #define WRITE_STATS_INTERVAL (24*60*60) -- cgit v1.2.3-54-g00ecf From 32337502f11e6c84e4db8591f5f81c4fc6d1da58 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Wed, 8 Aug 2012 06:02:47 -0700 Subject: Use channel_t rather than or_connection_t for circuits --- src/or/circuitbuild.c | 188 ++++++++++++--------- src/or/circuitbuild.h | 2 +- src/or/circuitlist.c | 456 +++++++++++++++++++++++++++++--------------------- src/or/circuitlist.h | 25 +-- src/or/circuituse.c | 40 +++-- src/or/main.c | 1 + src/or/or.h | 44 ++--- src/or/reasons.c | 4 +- src/or/relay.h | 14 +- 9 files changed, 446 insertions(+), 328 deletions(-) (limited to 'src') diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c index 56c2c22d01..5ef67bd9df 100644 --- a/src/or/circuitbuild.c +++ b/src/or/circuitbuild.c @@ -12,9 +12,11 @@ #define CIRCUIT_PRIVATE #include "or.h" +#include "channel.h" #include "circuitbuild.h" #include "circuitlist.h" #include "circuituse.h" +#include "command.h" #include "config.h" #include "confparse.h" #include "connection.h" @@ -125,6 +127,9 @@ static int unit_tests = 0; /********* END VARIABLES ************/ +static channel_t * channel_connect_for_circuit(const tor_addr_t *addr, + uint16_t port, + const char *id_digest); static int circuit_deliver_create_cell(circuit_t *circ, uint8_t cell_type, const char *payload); static int onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit); @@ -141,6 +146,22 @@ static void bridge_free(bridge_info_t *bridge); static int entry_guard_inc_first_hop_count(entry_guard_t *guard); static void pathbias_count_success(origin_circuit_t *circ); +/** This function tries to get a channel to the specified endpoint, + * and then calls command_setup_channel() to give it the right + * callbacks. + */ +static channel_t * +channel_connect_for_circuit(const tor_addr_t *addr, uint16_t port, + const char *id_digest) +{ + channel_t *chan; + + chan = channel_connect(addr, port, id_digest); + if (chan) command_setup_channel(chan); + + return chan; +} + /** * This function decides if CBT learning should be disabled. It returns * true if one or more of the following four conditions are met: @@ -1683,26 +1704,30 @@ circuit_build_times_set_timeout(circuit_build_times_t *cbt) * Return it, or 0 if can't get a unique circ_id. */ static circid_t -get_unique_circ_id_by_conn(or_connection_t *conn) +get_unique_circ_id_by_chan(channel_t *chan) { circid_t test_circ_id; circid_t attempts=0; circid_t high_bit; - tor_assert(conn); - if (conn->circ_id_type == CIRC_ID_TYPE_NEITHER) { - log_warn(LD_BUG, "Trying to pick a circuit ID for a connection from " + tor_assert(chan); + tor_assert(!(chan->is_listener)); + + if (chan->u.cell_chan.circ_id_type == CIRC_ID_TYPE_NEITHER) { + log_warn(LD_BUG, + "Trying to pick a circuit ID for a connection from " "a client with no identity."); return 0; } - high_bit = (conn->circ_id_type == CIRC_ID_TYPE_HIGHER) ? 1<<15 : 0; + high_bit = + (chan->u.cell_chan.circ_id_type == CIRC_ID_TYPE_HIGHER) ? 1<<15 : 0; do { /* Sequentially iterate over test_circ_id=1...1<<15-1 until we find a * circID such that (high_bit|test_circ_id) is not already used. */ - test_circ_id = conn->next_circ_id++; + test_circ_id = chan->u.cell_chan.next_circ_id++; if (test_circ_id == 0 || test_circ_id >= 1<<15) { test_circ_id = 1; - conn->next_circ_id = 2; + chan->u.cell_chan.next_circ_id = 2; } if (++attempts > 1<<15) { /* Make sure we don't loop forever if all circ_id's are used. This @@ -1712,7 +1737,7 @@ get_unique_circ_id_by_conn(or_connection_t *conn) return 0; } test_circ_id |= high_bit; - } while (circuit_id_in_use_on_orconn(test_circ_id, conn)); + } while (circuit_id_in_use_on_channel(test_circ_id, chan)); return test_circ_id; } @@ -1891,9 +1916,9 @@ onion_populate_cpath(origin_circuit_t *circ) origin_circuit_t * origin_circuit_init(uint8_t purpose, int flags) { - /* sets circ->p_circ_id and circ->p_conn */ + /* sets circ->p_circ_id and circ->p_chan */ origin_circuit_t *circ = origin_circuit_new(); - circuit_set_state(TO_CIRCUIT(circ), CIRCUIT_STATE_OR_WAIT); + circuit_set_state(TO_CIRCUIT(circ), CIRCUIT_STATE_CHAN_WAIT); circ->build_state = tor_malloc_zero(sizeof(cpath_build_state_t)); circ->build_state->onehop_tunnel = ((flags & CIRCLAUNCH_ONEHOP_TUNNEL) ? 1 : 0); @@ -1945,7 +1970,7 @@ int circuit_handle_first_hop(origin_circuit_t *circ) { crypt_path_t *firsthop; - or_connection_t *n_conn; + channel_t *n_chan; int err_reason = 0; const char *msg = NULL; int should_launch = 0; @@ -1959,12 +1984,12 @@ circuit_handle_first_hop(origin_circuit_t *circ) fmt_addr(&firsthop->extend_info->addr), firsthop->extend_info->port); - n_conn = connection_or_get_for_extend(firsthop->extend_info->identity_digest, - &firsthop->extend_info->addr, - &msg, - &should_launch); + n_chan = channel_get_for_extend(firsthop->extend_info->identity_digest, + &firsthop->extend_info->addr, + &msg, + &should_launch); - if (!n_conn) { + if (!n_chan) { /* not currently connected in a useful way. */ log_info(LD_CIRC, "Next router is %s: %s", safe_str_client(extend_info_describe(firsthop->extend_info)), @@ -1974,10 +1999,11 @@ circuit_handle_first_hop(origin_circuit_t *circ) if (should_launch) { if (circ->build_state->onehop_tunnel) control_event_bootstrap(BOOTSTRAP_STATUS_CONN_DIR, 0); - n_conn = connection_or_connect(&firsthop->extend_info->addr, - firsthop->extend_info->port, - firsthop->extend_info->identity_digest); - if (!n_conn) { /* connect failed, forget the whole thing */ + n_chan = channel_connect_for_circuit( + &firsthop->extend_info->addr, + firsthop->extend_info->port, + firsthop->extend_info->identity_digest); + if (!n_chan) { /* connect failed, forget the whole thing */ log_info(LD_CIRC,"connect to firsthop failed. Closing."); return -END_CIRC_REASON_CONNECTFAILED; } @@ -1985,13 +2011,13 @@ circuit_handle_first_hop(origin_circuit_t *circ) log_debug(LD_CIRC,"connecting in progress (or finished). Good."); /* return success. The onion/circuit/etc will be taken care of - * automatically (may already have been) whenever n_conn reaches + * automatically (may already have been) whenever n_chan reaches * OR_CONN_STATE_OPEN. */ return 0; } else { /* it's already open. use it. */ tor_assert(!circ->_base.n_hop); - circ->_base.n_conn = n_conn; + circ->_base.n_chan = n_chan; log_debug(LD_CIRC,"Conn open. Delivering first onion skin."); if ((err_reason = circuit_send_next_onion_skin(circ)) < 0) { log_info(LD_CIRC,"circuit_send_next_onion_skin failed."); @@ -2007,48 +2033,51 @@ circuit_handle_first_hop(origin_circuit_t *circ) * Status is 1 if connect succeeded, or 0 if connect failed. */ void -circuit_n_conn_done(or_connection_t *or_conn, int status) +circuit_n_chan_done(channel_t *chan, int status) { smartlist_t *pending_circs; int err_reason = 0; - log_debug(LD_CIRC,"or_conn to %s/%s, status=%d", - or_conn->nickname ? or_conn->nickname : "NULL", - or_conn->_base.address, status); + tor_assert(chan); + tor_assert(!(chan->is_listener)); + + log_debug(LD_CIRC,"chan to %s/%s, status=%d", + chan->u.cell_chan.nickname ? + chan->u.cell_chan.nickname : "NULL", + channel_get_canonical_remote_descr(chan), status); pending_circs = smartlist_new(); - circuit_get_all_pending_on_or_conn(pending_circs, or_conn); + circuit_get_all_pending_on_channel(pending_circs, chan); SMARTLIST_FOREACH_BEGIN(pending_circs, circuit_t *, circ) { /* These checks are redundant wrt get_all_pending_on_or_conn, but I'm * leaving them in in case it's possible for the status of a circuit to * change as we're going down the list. */ - if (circ->marked_for_close || circ->n_conn || !circ->n_hop || - circ->state != CIRCUIT_STATE_OR_WAIT) + if (circ->marked_for_close || circ->n_chan || !circ->n_hop || + circ->state != CIRCUIT_STATE_CHAN_WAIT) continue; if (tor_digest_is_zero(circ->n_hop->identity_digest)) { /* Look at addr/port. This is an unkeyed connection. */ - if (!tor_addr_eq(&circ->n_hop->addr, &or_conn->_base.addr) || - circ->n_hop->port != or_conn->_base.port) + if (!channel_matches_extend_info(chan, circ->n_hop)) continue; } else { /* We expected a key. See if it's the right one. */ - if (tor_memneq(or_conn->identity_digest, + if (tor_memneq(chan->u.cell_chan.identity_digest, circ->n_hop->identity_digest, DIGEST_LEN)) continue; } - if (!status) { /* or_conn failed; close circ */ - log_info(LD_CIRC,"or_conn failed. Closing circ."); - circuit_mark_for_close(circ, END_CIRC_REASON_OR_CONN_CLOSED); + if (!status) { /* chan failed; close circ */ + log_info(LD_CIRC,"Channel failed; closing circ."); + circuit_mark_for_close(circ, END_CIRC_REASON_CHANNEL_CLOSED); continue; } log_debug(LD_CIRC, "Found circ, sending create cell."); /* circuit_deliver_create_cell will set n_circ_id and add us to - * orconn_circuid_circuit_map, so we don't need to call - * set_circid_orconn here. */ - circ->n_conn = or_conn; + * chan_circuid_circuit_map, so we don't need to call + * set_circid_chan here. */ + circ->n_chan = chan; extend_info_free(circ->n_hop); circ->n_hop = NULL; @@ -2064,13 +2093,13 @@ circuit_n_conn_done(or_connection_t *or_conn, int status) } } else { /* pull the create cell out of circ->onionskin, and send it */ - tor_assert(circ->n_conn_onionskin); + tor_assert(circ->n_chan_onionskin); if (circuit_deliver_create_cell(circ,CELL_CREATE, - circ->n_conn_onionskin)<0) { + circ->n_chan_onionskin)<0) { circuit_mark_for_close(circ, END_CIRC_REASON_RESOURCELIMIT); continue; } - tor_free(circ->n_conn_onionskin); + tor_free(circ->n_chan_onionskin); circuit_set_state(circ, CIRCUIT_STATE_OPEN); } } @@ -2079,7 +2108,7 @@ circuit_n_conn_done(or_connection_t *or_conn, int status) smartlist_free(pending_circs); } -/** Find a new circid that isn't currently in use on the circ->n_conn +/** Find a new circid that isn't currently in use on the circ->n_chan * for the outgoing * circuit circ, and deliver a cell of type cell_type * (either CELL_CREATE or CELL_CREATE_FAST) with payload payload @@ -2094,29 +2123,29 @@ circuit_deliver_create_cell(circuit_t *circ, uint8_t cell_type, circid_t id; tor_assert(circ); - tor_assert(circ->n_conn); + tor_assert(circ->n_chan); tor_assert(payload); tor_assert(cell_type == CELL_CREATE || cell_type == CELL_CREATE_FAST); - id = get_unique_circ_id_by_conn(circ->n_conn); + id = get_unique_circ_id_by_chan(circ->n_chan); if (!id) { log_warn(LD_CIRC,"failed to get unique circID."); return -1; } log_debug(LD_CIRC,"Chosen circID %u.", id); - circuit_set_n_circid_orconn(circ, id, circ->n_conn); + circuit_set_n_circid_chan(circ, id, circ->n_chan); memset(&cell, 0, sizeof(cell_t)); cell.command = cell_type; cell.circ_id = circ->n_circ_id; memcpy(cell.payload, payload, ONIONSKIN_CHALLENGE_LEN); - append_cell_to_circuit_queue(circ, circ->n_conn, &cell, + append_cell_to_circuit_queue(circ, circ->n_chan, &cell, CELL_DIRECTION_OUT, 0); if (CIRCUIT_IS_ORIGIN(circ)) { /* mark it so it gets better rate limiting treatment. */ - circ->n_conn->client_used = time(NULL); + channel_timestamp_client(circ->n_chan); } return 0; @@ -2218,7 +2247,8 @@ circuit_send_next_onion_skin(origin_circuit_t *circ) else control_event_bootstrap(BOOTSTRAP_STATUS_CIRCUIT_CREATE, 0); - node = node_get_by_id(circ->_base.n_conn->identity_digest); + tor_assert(!(circ->_base.n_chan->is_listener)); + node = node_get_by_id(circ->_base.n_chan->u.cell_chan.identity_digest); fast = should_use_create_fast_for_circuit(circ); if (!fast) { /* We are an OR and we know the right onion key: we should @@ -2386,7 +2416,7 @@ circuit_note_clock_jumped(int seconds_elapsed) int circuit_extend(cell_t *cell, circuit_t *circ) { - or_connection_t *n_conn; + channel_t *n_chan; relay_header_t rh; char *onionskin; char *id_digest=NULL; @@ -2396,9 +2426,9 @@ circuit_extend(cell_t *cell, circuit_t *circ) const char *msg = NULL; int should_launch = 0; - if (circ->n_conn) { + if (circ->n_chan) { log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, - "n_conn already set. Bug/attack. Closing."); + "n_chan already set. Bug/attack. Closing."); return -1; } if (circ->n_hop) { @@ -2457,19 +2487,22 @@ circuit_extend(cell_t *cell, circuit_t *circ) /* Next, check if we're being asked to connect to the hop that the * extend cell came from. There isn't any reason for that, and it can * assist circular-path attacks. */ - if (tor_memeq(id_digest, TO_OR_CIRCUIT(circ)->p_conn->identity_digest, - DIGEST_LEN)) { + tor_assert(!(TO_OR_CIRCUIT(circ)->p_chan->is_listener)); + if (tor_memeq(id_digest, + TO_OR_CIRCUIT(circ)->p_chan-> + u.cell_chan.identity_digest, + DIGEST_LEN)) { log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Client asked me to extend back to the previous hop."); return -1; } - n_conn = connection_or_get_for_extend(id_digest, - &n_addr, - &msg, - &should_launch); + n_chan = channel_get_for_extend(id_digest, + &n_addr, + &msg, + &should_launch); - if (!n_conn) { + if (!n_chan) { log_debug(LD_CIRC|LD_OR,"Next router (%s:%d): %s", fmt_addr(&n_addr), (int)n_port, msg?msg:"????"); @@ -2478,31 +2511,32 @@ circuit_extend(cell_t *cell, circuit_t *circ) NULL /*onion_key*/, &n_addr, n_port); - circ->n_conn_onionskin = tor_malloc(ONIONSKIN_CHALLENGE_LEN); - memcpy(circ->n_conn_onionskin, onionskin, ONIONSKIN_CHALLENGE_LEN); - circuit_set_state(circ, CIRCUIT_STATE_OR_WAIT); + circ->n_chan_onionskin = tor_malloc(ONIONSKIN_CHALLENGE_LEN); + memcpy(circ->n_chan_onionskin, onionskin, ONIONSKIN_CHALLENGE_LEN); + circuit_set_state(circ, CIRCUIT_STATE_CHAN_WAIT); if (should_launch) { /* we should try to open a connection */ - n_conn = connection_or_connect(&n_addr, n_port, id_digest); - if (!n_conn) { - log_info(LD_CIRC,"Launching n_conn failed. Closing circuit."); + n_chan = channel_connect_for_circuit(&n_addr, n_port, id_digest); + if (!n_chan) { + log_info(LD_CIRC,"Launching n_chan failed. Closing circuit."); circuit_mark_for_close(circ, END_CIRC_REASON_CONNECTFAILED); return 0; } log_debug(LD_CIRC,"connecting in progress (or finished). Good."); } /* return success. The onion/circuit/etc will be taken care of - * automatically (may already have been) whenever n_conn reaches + * automatically (may already have been) whenever n_chan reaches * OR_CONN_STATE_OPEN. */ return 0; } tor_assert(!circ->n_hop); /* Connection is already established. */ - circ->n_conn = n_conn; - log_debug(LD_CIRC,"n_conn is %s:%u", - n_conn->_base.address,n_conn->_base.port); + circ->n_chan = n_chan; + log_debug(LD_CIRC, + "n_chan is %s", + channel_get_canonical_remote_descr(n_chan)); if (circuit_deliver_create_cell(circ, CELL_CREATE, onionskin) < 0) return -1; @@ -2699,8 +2733,9 @@ pathbias_count_first_hop(origin_circuit_t *circ) if (!circ->has_opened) { entry_guard_t *guard; + tor_assert(!(circ->_base.n_chan->is_listener)); guard = entry_guard_get_by_id_digest( - circ->_base.n_conn->identity_digest); + circ->_base.n_chan->u.cell_chan.identity_digest); if (guard) { if (circ->path_state == PATH_STATE_NEW_CIRC) { circ->path_state = PATH_STATE_DID_FIRST_HOP; @@ -2770,6 +2805,7 @@ pathbias_count_success(origin_circuit_t *circ) static ratelim_t success_notice_limit = RATELIM_INIT(SUCCESS_NOTICE_INTERVAL); char *rate_msg = NULL; + entry_guard_t *guard = NULL; /* We can't do path bias accounting without entry guards. * Testing and controller circuits also have no guards. */ @@ -2804,8 +2840,10 @@ pathbias_count_success(origin_circuit_t *circ) /* Don't count cannibalized/reused circs for path bias */ if (!circ->has_opened) { - entry_guard_t *guard = - entry_guard_get_by_id_digest(circ->_base.n_conn->identity_digest); + tor_assert(!(circ->_base.n_chan->is_listener)); + guard = + entry_guard_get_by_id_digest(circ->_base.n_chan-> + u.cell_chan.identity_digest); if (guard) { if (circ->path_state == PATH_STATE_DID_FIRST_HOP) { @@ -3021,7 +3059,7 @@ circuit_truncated(origin_circuit_t *circ, crypt_path_t *layer, int reason) * just give up. */ circuit_mark_for_close(TO_CIRCUIT(circ), - END_CIRC_REASON_FLAG_REMOTE|reason); + END_CIRC_REASON_FLAG_REMOTE|END_CIRC_REASON_CHANNEL_CLOSED|reason); return 0; #if 0 @@ -3095,12 +3133,12 @@ onionskin_answer(or_circuit_t *circ, uint8_t cell_type, const char *payload, circ->is_first_hop = (cell_type == CELL_CREATED_FAST); append_cell_to_circuit_queue(TO_CIRCUIT(circ), - circ->p_conn, &cell, CELL_DIRECTION_IN, 0); + circ->p_chan, &cell, CELL_DIRECTION_IN, 0); log_debug(LD_CIRC,"Finished sending '%s' cell.", circ->is_first_hop ? "created_fast" : "created"); - if (!is_local_addr(&circ->p_conn->_base.addr) && - !connection_or_nonopen_was_started_here(circ->p_conn)) { + if (!channel_is_local(circ->p_chan) && + !channel_is_outgoing(circ->p_chan)) { /* record that we could process create cells from a non-local conn * that we didn't initiate; presumably this means that create cells * can reach us too. */ diff --git a/src/or/circuitbuild.h b/src/or/circuitbuild.h index 8ec48beb0b..0abffc5166 100644 --- a/src/or/circuitbuild.h +++ b/src/or/circuitbuild.h @@ -22,7 +22,7 @@ origin_circuit_t *circuit_establish_circuit(uint8_t purpose, extend_info_t *exit, int flags); int circuit_handle_first_hop(origin_circuit_t *circ); -void circuit_n_conn_done(or_connection_t *or_conn, int status); +void circuit_n_chan_done(channel_t *chan, int status); int inform_testing_reachability(void); int circuit_timeout_want_to_count_circ(origin_circuit_t *circ); int circuit_send_next_onion_skin(origin_circuit_t *circ); diff --git a/src/or/circuitlist.c b/src/or/circuitlist.c index d9b74bd4c2..4746f912de 100644 --- a/src/or/circuitlist.c +++ b/src/or/circuitlist.c @@ -10,6 +10,7 @@ **/ #include "or.h" +#include "channel.h" #include "circuitbuild.h" #include "circuitlist.h" #include "circuituse.h" @@ -34,8 +35,8 @@ /** A global list of all circuits at this hop. */ circuit_t *global_circuitlist=NULL; -/** A list of all the circuits in CIRCUIT_STATE_OR_WAIT. */ -static smartlist_t *circuits_pending_or_conns=NULL; +/** A list of all the circuits in CIRCUIT_STATE_CHAN_WAIT. */ +static smartlist_t *circuits_pending_chans = NULL; static void circuit_free(circuit_t *circ); static void circuit_free_cpath(crypt_path_t *cpath); @@ -44,154 +45,156 @@ static void cpath_ref_decref(crypt_path_reference_t *cpath_ref); /********* END VARIABLES ************/ -/** A map from OR connection and circuit ID to circuit. (Lookup performance is +/** A map from channel and circuit ID to circuit. (Lookup performance is * very important here, since we need to do it every time a cell arrives.) */ -typedef struct orconn_circid_circuit_map_t { - HT_ENTRY(orconn_circid_circuit_map_t) node; - or_connection_t *or_conn; +typedef struct chan_circid_circuit_map_t { + HT_ENTRY(chan_circid_circuit_map_t) node; + channel_t *chan; circid_t circ_id; circuit_t *circuit; -} orconn_circid_circuit_map_t; +} chan_circid_circuit_map_t; -/** Helper for hash tables: compare the OR connection and circuit ID for a and +/** Helper for hash tables: compare the channel and circuit ID for a and * b, and return less than, equal to, or greater than zero appropriately. */ static INLINE int -_orconn_circid_entries_eq(orconn_circid_circuit_map_t *a, - orconn_circid_circuit_map_t *b) +_chan_circid_entries_eq(chan_circid_circuit_map_t *a, + chan_circid_circuit_map_t *b) { - return a->or_conn == b->or_conn && a->circ_id == b->circ_id; + return a->chan == b->chan && a->circ_id == b->circ_id; } /** Helper: return a hash based on circuit ID and the pointer value of - * or_conn in a. */ + * chan in a. */ static INLINE unsigned int -_orconn_circid_entry_hash(orconn_circid_circuit_map_t *a) +_chan_circid_entry_hash(chan_circid_circuit_map_t *a) { - return (((unsigned)a->circ_id)<<8) ^ (unsigned)(uintptr_t)(a->or_conn); + return (((unsigned)a->circ_id)<<8) ^ (unsigned)(uintptr_t)(a->chan); } -/** Map from [orconn,circid] to circuit. */ -static HT_HEAD(orconn_circid_map, orconn_circid_circuit_map_t) - orconn_circid_circuit_map = HT_INITIALIZER(); -HT_PROTOTYPE(orconn_circid_map, orconn_circid_circuit_map_t, node, - _orconn_circid_entry_hash, _orconn_circid_entries_eq) -HT_GENERATE(orconn_circid_map, orconn_circid_circuit_map_t, node, - _orconn_circid_entry_hash, _orconn_circid_entries_eq, 0.6, +/** Map from [chan,circid] to circuit. */ +static HT_HEAD(chan_circid_map, chan_circid_circuit_map_t) + chan_circid_map = HT_INITIALIZER(); +HT_PROTOTYPE(chan_circid_map, chan_circid_circuit_map_t, node, + _chan_circid_entry_hash, _chan_circid_entries_eq) +HT_GENERATE(chan_circid_map, chan_circid_circuit_map_t, node, + _chan_circid_entry_hash, _chan_circid_entries_eq, 0.6, malloc, realloc, free) -/** The most recently returned entry from circuit_get_by_circid_orconn; +/** The most recently returned entry from circuit_get_by_circid_chan; * used to improve performance when many cells arrive in a row from the * same circuit. */ -orconn_circid_circuit_map_t *_last_circid_orconn_ent = NULL; +chan_circid_circuit_map_t *_last_circid_chan_ent = NULL; -/** Implementation helper for circuit_set_{p,n}_circid_orconn: A circuit ID - * and/or or_connection for circ has just changed from old_conn, old_id - * to conn, id. Adjust the conn,circid map as appropriate, removing +/** Implementation helper for circuit_set_{p,n}_circid_channel: A circuit ID + * and/or channel for circ has just changed from old_chan, old_id + * to chan, id. Adjust the chan,circid map as appropriate, removing * the old entry (if any) and adding a new one. */ static void -circuit_set_circid_orconn_helper(circuit_t *circ, int direction, - circid_t id, - or_connection_t *conn) +circuit_set_circid_chan_helper(circuit_t *circ, int direction, + circid_t id, + channel_t *chan) { - orconn_circid_circuit_map_t search; - orconn_circid_circuit_map_t *found; - or_connection_t *old_conn, **conn_ptr; + chan_circid_circuit_map_t search; + chan_circid_circuit_map_t *found; + channel_t *old_chan, **chan_ptr; circid_t old_id, *circid_ptr; int was_active, make_active; + if (chan) tor_assert(!(chan->is_listener)); + if (direction == CELL_DIRECTION_OUT) { - conn_ptr = &circ->n_conn; + chan_ptr = &circ->n_chan; circid_ptr = &circ->n_circ_id; - was_active = circ->next_active_on_n_conn != NULL; - make_active = circ->n_conn_cells.n > 0; + was_active = circ->next_active_on_n_chan != NULL; + make_active = circ->n_chan_cells.n > 0; } else { or_circuit_t *c = TO_OR_CIRCUIT(circ); - conn_ptr = &c->p_conn; + chan_ptr = &c->p_chan; circid_ptr = &c->p_circ_id; - was_active = c->next_active_on_p_conn != NULL; - make_active = c->p_conn_cells.n > 0; + was_active = c->next_active_on_p_chan != NULL; + make_active = c->p_chan_cells.n > 0; } - old_conn = *conn_ptr; + old_chan = *chan_ptr; old_id = *circid_ptr; - if (id == old_id && conn == old_conn) + if (id == old_id && chan == old_chan) return; - if (_last_circid_orconn_ent && - ((old_id == _last_circid_orconn_ent->circ_id && - old_conn == _last_circid_orconn_ent->or_conn) || - (id == _last_circid_orconn_ent->circ_id && - conn == _last_circid_orconn_ent->or_conn))) { - _last_circid_orconn_ent = NULL; + if (_last_circid_chan_ent && + ((old_id == _last_circid_chan_ent->circ_id && + old_chan == _last_circid_chan_ent->chan) || + (id == _last_circid_chan_ent->circ_id && + chan == _last_circid_chan_ent->chan))) { + _last_circid_chan_ent = NULL; } - if (old_conn) { /* we may need to remove it from the conn-circid map */ - tor_assert(old_conn->_base.magic == OR_CONNECTION_MAGIC); + if (old_chan) { /* we may need to remove it from the conn-circid map */ + tor_assert(!(old_chan->is_listener)); search.circ_id = old_id; - search.or_conn = old_conn; - found = HT_REMOVE(orconn_circid_map, &orconn_circid_circuit_map, &search); + search.chan = old_chan; + found = HT_REMOVE(chan_circid_map, &chan_circid_map, &search); if (found) { tor_free(found); - --old_conn->n_circuits; + --old_chan->u.cell_chan.n_circuits; } - if (was_active && old_conn != conn) - make_circuit_inactive_on_conn(circ,old_conn); + if (was_active && old_chan != chan) + make_circuit_inactive_on_chan(circ, old_chan); } /* Change the values only after we have possibly made the circuit inactive - * on the previous conn. */ - *conn_ptr = conn; + * on the previous chan. */ + *chan_ptr = chan; *circid_ptr = id; - if (conn == NULL) + if (chan == NULL) return; /* now add the new one to the conn-circid map */ search.circ_id = id; - search.or_conn = conn; - found = HT_FIND(orconn_circid_map, &orconn_circid_circuit_map, &search); + search.chan = chan; + found = HT_FIND(chan_circid_map, &chan_circid_map, &search); if (found) { found->circuit = circ; } else { - found = tor_malloc_zero(sizeof(orconn_circid_circuit_map_t)); + found = tor_malloc_zero(sizeof(chan_circid_circuit_map_t)); found->circ_id = id; - found->or_conn = conn; + found->chan = chan; found->circuit = circ; - HT_INSERT(orconn_circid_map, &orconn_circid_circuit_map, found); + HT_INSERT(chan_circid_map, &chan_circid_map, found); } - if (make_active && old_conn != conn) - make_circuit_active_on_conn(circ,conn); + if (make_active && old_chan != chan) + make_circuit_active_on_chan(circ,chan); - ++conn->n_circuits; + ++chan->u.cell_chan.n_circuits; } /** Set the p_conn field of a circuit circ, along * with the corresponding circuit ID, and add the circuit as appropriate - * to the (orconn,id)-\>circuit map. */ + * to the (chan,id)-\>circuit map. */ void -circuit_set_p_circid_orconn(or_circuit_t *circ, circid_t id, - or_connection_t *conn) +circuit_set_p_circid_chan(or_circuit_t *circ, circid_t id, + channel_t *chan) { - circuit_set_circid_orconn_helper(TO_CIRCUIT(circ), CELL_DIRECTION_IN, - id, conn); + circuit_set_circid_chan_helper(TO_CIRCUIT(circ), CELL_DIRECTION_IN, + id, chan); - if (conn) - tor_assert(bool_eq(circ->p_conn_cells.n, circ->next_active_on_p_conn)); + if (chan) + tor_assert(bool_eq(circ->p_chan_cells.n, circ->next_active_on_p_chan)); } /** Set the n_conn field of a circuit circ, along * with the corresponding circuit ID, and add the circuit as appropriate - * to the (orconn,id)-\>circuit map. */ + * to the (chan,id)-\>circuit map. */ void -circuit_set_n_circid_orconn(circuit_t *circ, circid_t id, - or_connection_t *conn) +circuit_set_n_circid_chan(circuit_t *circ, circid_t id, + channel_t *chan) { - circuit_set_circid_orconn_helper(circ, CELL_DIRECTION_OUT, id, conn); + circuit_set_circid_chan_helper(circ, CELL_DIRECTION_OUT, id, chan); - if (conn) - tor_assert(bool_eq(circ->n_conn_cells.n, circ->next_active_on_n_conn)); + if (chan) + tor_assert(bool_eq(circ->n_chan_cells.n, circ->next_active_on_n_chan)); } /** Change the state of circ to state, adding it to or removing @@ -202,18 +205,18 @@ circuit_set_state(circuit_t *circ, uint8_t state) tor_assert(circ); if (state == circ->state) return; - if (!circuits_pending_or_conns) - circuits_pending_or_conns = smartlist_new(); - if (circ->state == CIRCUIT_STATE_OR_WAIT) { + if (!circuits_pending_chans) + circuits_pending_chans = smartlist_new(); + if (circ->state == CIRCUIT_STATE_CHAN_WAIT) { /* remove from waiting-circuit list. */ - smartlist_remove(circuits_pending_or_conns, circ); + smartlist_remove(circuits_pending_chans, circ); } - if (state == CIRCUIT_STATE_OR_WAIT) { + if (state == CIRCUIT_STATE_CHAN_WAIT) { /* add to waiting-circuit list. */ - smartlist_add(circuits_pending_or_conns, circ); + smartlist_add(circuits_pending_chans, circ); } if (state == CIRCUIT_STATE_OPEN) - tor_assert(!circ->n_conn_onionskin); + tor_assert(!circ->n_chan_onionskin); circ->state = state; } @@ -232,31 +235,31 @@ circuit_add(circuit_t *circ) } } -/** Append to out all circuits in state OR_WAIT waiting for +/** Append to out all circuits in state CHAN_WAIT waiting for * the given connection. */ void -circuit_get_all_pending_on_or_conn(smartlist_t *out, or_connection_t *or_conn) +circuit_get_all_pending_on_channel(smartlist_t *out, channel_t *chan) { tor_assert(out); - tor_assert(or_conn); + tor_assert(chan); + tor_assert(!(chan->is_listener)); - if (!circuits_pending_or_conns) + if (!circuits_pending_chans) return; - SMARTLIST_FOREACH_BEGIN(circuits_pending_or_conns, circuit_t *, circ) { + SMARTLIST_FOREACH_BEGIN(circuits_pending_chans, circuit_t *, circ) { if (circ->marked_for_close) continue; if (!circ->n_hop) continue; - tor_assert(circ->state == CIRCUIT_STATE_OR_WAIT); + tor_assert(circ->state == CIRCUIT_STATE_CHAN_WAIT); if (tor_digest_is_zero(circ->n_hop->identity_digest)) { /* Look at addr/port. This is an unkeyed connection. */ - if (!tor_addr_eq(&circ->n_hop->addr, &or_conn->_base.addr) || - circ->n_hop->port != or_conn->_base.port) + if (!channel_matches_extend_info(chan, circ->n_hop)) continue; } else { /* We expected a key. See if it's the right one. */ - if (tor_memneq(or_conn->identity_digest, + if (tor_memneq(chan->u.cell_chan.identity_digest, circ->n_hop->identity_digest, DIGEST_LEN)) continue; } @@ -264,19 +267,24 @@ circuit_get_all_pending_on_or_conn(smartlist_t *out, or_connection_t *or_conn) } SMARTLIST_FOREACH_END(circ); } -/** Return the number of circuits in state OR_WAIT, waiting for the given - * connection. */ +/** Return the number of circuits in state CHAN_WAIT, waiting for the given + * channel. */ int -circuit_count_pending_on_or_conn(or_connection_t *or_conn) +circuit_count_pending_on_channel(channel_t *chan) { int cnt; smartlist_t *sl = smartlist_new(); - circuit_get_all_pending_on_or_conn(sl, or_conn); + + tor_assert(chan); + tor_assert(!(chan->is_listener)); + + circuit_get_all_pending_on_channel(sl, chan); cnt = smartlist_len(sl); smartlist_free(sl); log_debug(LD_CIRC,"or_conn to %s at %s, %d pending circs", - or_conn->nickname ? or_conn->nickname : "NULL", - or_conn->_base.address, + chan->u.cell_chan.nickname ? + chan->u.cell_chan.nickname : "NULL", + channel_get_canonical_remote_descr(chan), cnt); return cnt; } @@ -324,7 +332,7 @@ circuit_state_to_string(int state) switch (state) { case CIRCUIT_STATE_BUILDING: return "doing handshakes"; case CIRCUIT_STATE_ONIONSKIN_PENDING: return "processing the onion"; - case CIRCUIT_STATE_OR_WAIT: return "connecting to server"; + case CIRCUIT_STATE_CHAN_WAIT: return "connecting to server"; case CIRCUIT_STATE_OPEN: return "open"; default: log_warn(LD_BUG, "Unknown circuit state %d", state); @@ -522,7 +530,7 @@ init_circuit_base(circuit_t *circ) circ->n_cell_ewma.last_adjusted_tick = cell_ewma_get_tick(); circ->n_cell_ewma.cell_count = 0.0; circ->n_cell_ewma.heap_index = -1; - circ->n_cell_ewma.is_for_p_conn = 0; + circ->n_cell_ewma.is_for_p_chan = 0; circuit_add(circ); } @@ -556,7 +564,7 @@ origin_circuit_new(void) /** Allocate a new or_circuit_t, connected to p_conn as * p_circ_id. If p_conn is NULL, the circuit is unattached. */ or_circuit_t * -or_circuit_new(circid_t p_circ_id, or_connection_t *p_conn) +or_circuit_new(circid_t p_circ_id, channel_t *p_chan) { /* CircIDs */ or_circuit_t *circ; @@ -564,8 +572,8 @@ or_circuit_new(circid_t p_circ_id, or_connection_t *p_conn) circ = tor_malloc_zero(sizeof(or_circuit_t)); circ->_base.magic = OR_CIRCUIT_MAGIC; - if (p_conn) - circuit_set_p_circid_orconn(circ, p_circ_id, p_conn); + if (p_chan) + circuit_set_p_circid_chan(circ, p_circ_id, p_chan); circ->remaining_relay_early_cells = MAX_RELAY_EARLY_CELLS_PER_CIRCUIT; @@ -576,7 +584,7 @@ or_circuit_new(circid_t p_circ_id, or_connection_t *p_conn) /* Initialize the cell counts to 0 */ circ->p_cell_ewma.cell_count = 0.0; circ->p_cell_ewma.last_adjusted_tick = cell_ewma_get_tick(); - circ->p_cell_ewma.is_for_p_conn = 1; + circ->p_cell_ewma.is_for_p_chan = 1; /* It's not in any heap yet. */ circ->p_cell_ewma.heap_index = -1; @@ -641,22 +649,22 @@ circuit_free(circuit_t *circ) } /* remove from map. */ - circuit_set_p_circid_orconn(ocirc, 0, NULL); + circuit_set_p_circid_chan(ocirc, 0, NULL); /* Clear cell queue _after_ removing it from the map. Otherwise our * "active" checks will be violated. */ - cell_queue_clear(ô->p_conn_cells); + cell_queue_clear(ô->p_chan_cells); } extend_info_free(circ->n_hop); - tor_free(circ->n_conn_onionskin); + tor_free(circ->n_chan_onionskin); /* Remove from map. */ - circuit_set_n_circid_orconn(circ, 0, NULL); + circuit_set_n_circid_chan(circ, 0, NULL); /* Clear cell queue _after_ removing it from the map. Otherwise our * "active" checks will be violated. */ - cell_queue_clear(&circ->n_conn_cells); + cell_queue_clear(&circ->n_chan_cells); memset(mem, 0xAA, memlen); /* poison memory */ tor_free(mem); @@ -702,10 +710,10 @@ circuit_free_all(void) global_circuitlist = next; } - smartlist_free(circuits_pending_or_conns); - circuits_pending_or_conns = NULL; + smartlist_free(circuits_pending_chans); + circuits_pending_chans = NULL; - HT_CLEAR(orconn_circid_map, &orconn_circid_circuit_map); + HT_CLEAR(chan_circid_map, &chan_circid_map); } /** Deallocate space associated with the cpath node victim. */ @@ -742,8 +750,12 @@ cpath_ref_decref(crypt_path_reference_t *cpath_ref) * of information about circuit circ. */ static void -circuit_dump_details(int severity, circuit_t *circ, int conn_array_index, - const char *type, int this_circid, int other_circid) +circuit_dump_conn_details(int severity, + circuit_t *circ, + int conn_array_index, + const char *type, + int this_circid, + int other_circid) { log(severity, LD_CIRC, "Conn %d has %s circuit: circID %d (other side %d), " "state %d (%s), born %ld:", @@ -764,50 +776,102 @@ circuit_dump_by_conn(connection_t *conn, int severity) circuit_t *circ; edge_connection_t *tmpconn; - for (circ=global_circuitlist;circ;circ = circ->next) { + for (circ = global_circuitlist; circ; circ = circ->next) { circid_t n_circ_id = circ->n_circ_id, p_circ_id = 0; - if (circ->marked_for_close) + + if (circ->marked_for_close) { continue; + } - if (! CIRCUIT_IS_ORIGIN(circ)) + if (!CIRCUIT_IS_ORIGIN(circ)) { p_circ_id = TO_OR_CIRCUIT(circ)->p_circ_id; + } - if (! CIRCUIT_IS_ORIGIN(circ) && TO_OR_CIRCUIT(circ)->p_conn && - TO_CONN(TO_OR_CIRCUIT(circ)->p_conn) == conn) - circuit_dump_details(severity, circ, conn->conn_array_index, "App-ward", - p_circ_id, n_circ_id); if (CIRCUIT_IS_ORIGIN(circ)) { for (tmpconn=TO_ORIGIN_CIRCUIT(circ)->p_streams; tmpconn; tmpconn=tmpconn->next_stream) { if (TO_CONN(tmpconn) == conn) { - circuit_dump_details(severity, circ, conn->conn_array_index, - "App-ward", p_circ_id, n_circ_id); + circuit_dump_conn_details(severity, circ, conn->conn_array_index, + "App-ward", p_circ_id, n_circ_id); } } } - if (circ->n_conn && TO_CONN(circ->n_conn) == conn) - circuit_dump_details(severity, circ, conn->conn_array_index, "Exit-ward", - n_circ_id, p_circ_id); + if (! CIRCUIT_IS_ORIGIN(circ)) { for (tmpconn=TO_OR_CIRCUIT(circ)->n_streams; tmpconn; tmpconn=tmpconn->next_stream) { if (TO_CONN(tmpconn) == conn) { - circuit_dump_details(severity, circ, conn->conn_array_index, - "Exit-ward", n_circ_id, p_circ_id); + circuit_dump_conn_details(severity, circ, conn->conn_array_index, + "Exit-ward", n_circ_id, p_circ_id); } } } - if (!circ->n_conn && circ->n_hop && - tor_addr_eq(&circ->n_hop->addr, &conn->addr) && - circ->n_hop->port == conn->port && - conn->type == CONN_TYPE_OR && - tor_memeq(TO_OR_CONN(conn)->identity_digest, - circ->n_hop->identity_digest, DIGEST_LEN)) { - circuit_dump_details(severity, circ, conn->conn_array_index, - (circ->state == CIRCUIT_STATE_OPEN && - !CIRCUIT_IS_ORIGIN(circ)) ? - "Endpoint" : "Pending", - n_circ_id, p_circ_id); + } +} + +/** A helper function for circuit_dump_by_chan() below. Log a bunch + * of information about circuit circ. + */ +static void +circuit_dump_chan_details(int severity, + circuit_t *circ, + channel_t *chan, + const char *type, + int this_circid, + int other_circid) +{ + log(severity, LD_CIRC, "Conn %p has %s circuit: circID %d (other side %d), " + "state %d (%s), born %ld:", + chan, type, this_circid, other_circid, circ->state, + circuit_state_to_string(circ->state), + (long)circ->timestamp_created.tv_sec); + if (CIRCUIT_IS_ORIGIN(circ)) { /* circ starts at this node */ + circuit_log_path(severity, LD_CIRC, TO_ORIGIN_CIRCUIT(circ)); + } +} + +/** Log, at severity severity, information about each circuit + * that is connected to chan. + */ +void +circuit_dump_by_chan(channel_t *chan, int severity) +{ + circuit_t *circ; + + tor_assert(chan); + tor_assert(!(chan->is_listener)); + + for (circ = global_circuitlist; circ; circ = circ->next) { + circid_t n_circ_id = circ->n_circ_id, p_circ_id = 0; + + if (circ->marked_for_close) { + continue; + } + + if (!CIRCUIT_IS_ORIGIN(circ)) { + p_circ_id = TO_OR_CIRCUIT(circ)->p_circ_id; + } + + if (! CIRCUIT_IS_ORIGIN(circ) && TO_OR_CIRCUIT(circ)->p_chan && + TO_OR_CIRCUIT(circ)->p_chan == chan) { + circuit_dump_chan_details(severity, circ, chan, "App-ward", + p_circ_id, n_circ_id); + } + + if (circ->n_chan && circ->n_chan == chan) { + circuit_dump_chan_details(severity, circ, chan, "Exit-ward", + n_circ_id, p_circ_id); + } + + if (!circ->n_chan && circ->n_hop && + channel_matches_extend_info(chan, circ->n_hop) && + tor_memeq(chan->u.cell_chan.identity_digest, + circ->n_hop->identity_digest, DIGEST_LEN)) { + circuit_dump_chan_details(severity, circ, chan, + (circ->state == CIRCUIT_STATE_OPEN && + !CIRCUIT_IS_ORIGIN(circ)) ? + "Endpoint" : "Pending", + n_circ_id, p_circ_id); } } } @@ -832,27 +896,37 @@ circuit_get_by_global_id(uint32_t id) /** Return a circ such that: * - circ-\>n_circ_id or circ-\>p_circ_id is equal to circ_id, and - * - circ is attached to conn, either as p_conn or n_conn. + * - circ is attached to chan, either as p_chan or n_chan. * Return NULL if no such circuit exists. */ static INLINE circuit_t * -circuit_get_by_circid_orconn_impl(circid_t circ_id, or_connection_t *conn) +circuit_get_by_circid_channel_impl(circid_t circ_id, channel_t *chan) { - orconn_circid_circuit_map_t search; - orconn_circid_circuit_map_t *found; + chan_circid_circuit_map_t search; + chan_circid_circuit_map_t *found; - if (_last_circid_orconn_ent && - circ_id == _last_circid_orconn_ent->circ_id && - conn == _last_circid_orconn_ent->or_conn) { - found = _last_circid_orconn_ent; + if (_last_circid_chan_ent && + circ_id == _last_circid_chan_ent->circ_id && + chan == _last_circid_chan_ent->chan) { + found = _last_circid_chan_ent; } else { search.circ_id = circ_id; - search.or_conn = conn; - found = HT_FIND(orconn_circid_map, &orconn_circid_circuit_map, &search); - _last_circid_orconn_ent = found; + search.chan = chan; + found = HT_FIND(chan_circid_map, &chan_circid_map, &search); + _last_circid_chan_ent = found; } - if (found && found->circuit) + if (found && found->circuit) { + log_debug(LD_CIRC, + "circuit_get_by_circid_channel_impl() returning circuit %p for" + " circ_id %d, channel ID %lu (%p)", + found->circuit, circ_id, chan->global_identifier, chan); return found->circuit; + } + + log_debug(LD_CIRC, + "circuit_get_by_circid_channel_impl() found nothing for" + " circ_id %d, channel ID %lu (%p)", + circ_id, chan->global_identifier, chan); return NULL; /* The rest of this checks for bugs. Disabled by default. */ @@ -862,15 +936,15 @@ circuit_get_by_circid_orconn_impl(circid_t circ_id, or_connection_t *conn) for (circ=global_circuitlist;circ;circ = circ->next) { if (! CIRCUIT_IS_ORIGIN(circ)) { or_circuit_t *or_circ = TO_OR_CIRCUIT(circ); - if (or_circ->p_conn == conn && or_circ->p_circ_id == circ_id) { + if (or_circ->p_chan == chan && or_circ->p_circ_id == circ_id) { log_warn(LD_BUG, - "circuit matches p_conn, but not in hash table (Bug!)"); + "circuit matches p_chan, but not in hash table (Bug!)"); return circ; } } - if (circ->n_conn == conn && circ->n_circ_id == circ_id) { + if (circ->n_chan == chan && circ->n_circ_id == circ_id) { log_warn(LD_BUG, - "circuit matches n_conn, but not in hash table (Bug!)"); + "circuit matches n_chan, but not in hash table (Bug!)"); return circ; } } @@ -880,14 +954,14 @@ circuit_get_by_circid_orconn_impl(circid_t circ_id, or_connection_t *conn) /** Return a circ such that: * - circ-\>n_circ_id or circ-\>p_circ_id is equal to circ_id, and - * - circ is attached to conn, either as p_conn or n_conn. + * - circ is attached to chan, either as p_chan or n_chan. * - circ is not marked for close. * Return NULL if no such circuit exists. */ circuit_t * -circuit_get_by_circid_orconn(circid_t circ_id, or_connection_t *conn) +circuit_get_by_circid_channel(circid_t circ_id, channel_t *chan) { - circuit_t *circ = circuit_get_by_circid_orconn_impl(circ_id, conn); + circuit_t *circ = circuit_get_by_circid_channel_impl(circ_id, chan); if (!circ || circ->marked_for_close) return NULL; else @@ -895,11 +969,11 @@ circuit_get_by_circid_orconn(circid_t circ_id, or_connection_t *conn) } /** Return true iff the circuit ID circ_id is currently used by a - * circuit, marked or not, on conn. */ + * circuit, marked or not, on chan. */ int -circuit_id_in_use_on_orconn(circid_t circ_id, or_connection_t *conn) +circuit_id_in_use_on_channel(circid_t circ_id, channel_t *chan) { - return circuit_get_by_circid_orconn_impl(circ_id, conn) != NULL; + return circuit_get_by_circid_channel_impl(circ_id, chan) != NULL; } /** Return the circuit that a given edge connection is using. */ @@ -916,27 +990,27 @@ circuit_get_by_edge_conn(edge_connection_t *conn) return circ; } -/** For each circuit that has conn as n_conn or p_conn, unlink the - * circuit from the orconn,circid map, and mark it for close if it hasn't +/** For each circuit that has chan as n_chan or p_chan, unlink the + * circuit from the chan,circid map, and mark it for close if it hasn't * been marked already. */ void -circuit_unlink_all_from_or_conn(or_connection_t *conn, int reason) +circuit_unlink_all_from_channel(channel_t *chan, int reason) { circuit_t *circ; - connection_or_unlink_all_active_circs(conn); + channel_unlink_all_active_circs(chan); for (circ = global_circuitlist; circ; circ = circ->next) { int mark = 0; - if (circ->n_conn == conn) { - circuit_set_n_circid_orconn(circ, 0, NULL); + if (circ->n_chan == chan) { + circuit_set_n_circid_chan(circ, 0, NULL); mark = 1; } if (! CIRCUIT_IS_ORIGIN(circ)) { or_circuit_t *or_circ = TO_OR_CIRCUIT(circ); - if (or_circ->p_conn == conn) { - circuit_set_p_circid_orconn(or_circ, 0, NULL); + if (or_circ->p_chan == chan) { + circuit_set_p_circid_chan(or_circ, 0, NULL); mark = 1; } } @@ -1267,9 +1341,9 @@ _circuit_mark_for_close(circuit_t *circ, int reason, int line, circuit_rep_hist_note_result(ocirc); } } - if (circ->state == CIRCUIT_STATE_OR_WAIT) { - if (circuits_pending_or_conns) - smartlist_remove(circuits_pending_or_conns, circ); + if (circ->state == CIRCUIT_STATE_CHAN_WAIT) { + if (circuits_pending_chans) + smartlist_remove(circuits_pending_chans, circ); } if (CIRCUIT_IS_ORIGIN(circ)) { control_event_circuit_status(TO_ORIGIN_CIRCUIT(circ), @@ -1306,9 +1380,9 @@ _circuit_mark_for_close(circuit_t *circ, int reason, int line, INTRO_POINT_FAILURE_UNREACHABLE); } } - if (circ->n_conn) { - circuit_clear_cell_queue(circ, circ->n_conn); - connection_or_send_destroy(circ->n_circ_id, circ->n_conn, reason); + if (circ->n_chan) { + circuit_clear_cell_queue(circ, circ->n_chan); + channel_send_destroy(circ->n_circ_id, circ->n_chan, reason); } if (! CIRCUIT_IS_ORIGIN(circ)) { @@ -1333,9 +1407,9 @@ _circuit_mark_for_close(circuit_t *circ, int reason, int line, conn->on_circuit = NULL; } - if (or_circ->p_conn) { - circuit_clear_cell_queue(circ, or_circ->p_conn); - connection_or_send_destroy(or_circ->p_circ_id, or_circ->p_conn, reason); + if (or_circ->p_chan) { + circuit_clear_cell_queue(circ, or_circ->p_chan); + channel_send_destroy(or_circ->p_circ_id, or_circ->p_chan, reason); } } else { origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ); @@ -1438,22 +1512,22 @@ assert_circuit_ok(const circuit_t *c) or_circ = TO_OR_CIRCUIT(nonconst_circ); } - if (c->n_conn) { + if (c->n_chan) { tor_assert(!c->n_hop); if (c->n_circ_id) { /* We use the _impl variant here to make sure we don't fail on marked * circuits, which would not be returned by the regular function. */ - circuit_t *c2 = circuit_get_by_circid_orconn_impl(c->n_circ_id, - c->n_conn); + circuit_t *c2 = circuit_get_by_circid_channel_impl(c->n_circ_id, + c->n_chan); tor_assert(c == c2); } } - if (or_circ && or_circ->p_conn) { + if (or_circ && or_circ->p_chan) { if (or_circ->p_circ_id) { /* ibid */ - circuit_t *c2 = circuit_get_by_circid_orconn_impl(or_circ->p_circ_id, - or_circ->p_conn); + circuit_t *c2 = circuit_get_by_circid_channel_impl(or_circ->p_circ_id, + or_circ->p_chan); tor_assert(c == c2); } } @@ -1464,7 +1538,7 @@ assert_circuit_ok(const circuit_t *c) tor_assert(c->deliver_window >= 0); tor_assert(c->package_window >= 0); if (c->state == CIRCUIT_STATE_OPEN) { - tor_assert(!c->n_conn_onionskin); + tor_assert(!c->n_chan_onionskin); if (or_circ) { tor_assert(or_circ->n_crypto); tor_assert(or_circ->p_crypto); @@ -1472,12 +1546,12 @@ assert_circuit_ok(const circuit_t *c) tor_assert(or_circ->p_digest); } } - if (c->state == CIRCUIT_STATE_OR_WAIT && !c->marked_for_close) { - tor_assert(circuits_pending_or_conns && - smartlist_isin(circuits_pending_or_conns, c)); + if (c->state == CIRCUIT_STATE_CHAN_WAIT && !c->marked_for_close) { + tor_assert(circuits_pending_chans && + smartlist_isin(circuits_pending_chans, c)); } else { - tor_assert(!circuits_pending_or_conns || - !smartlist_isin(circuits_pending_or_conns, c)); + tor_assert(!circuits_pending_chans || + !smartlist_isin(circuits_pending_chans, c)); } if (origin_circ && origin_circ->cpath) { assert_cpath_ok(origin_circ->cpath); diff --git a/src/or/circuitlist.h b/src/or/circuitlist.h index 6e7735476b..f0f4b2a6df 100644 --- a/src/or/circuitlist.h +++ b/src/or/circuitlist.h @@ -18,20 +18,21 @@ const char *circuit_purpose_to_controller_string(uint8_t purpose); const char *circuit_purpose_to_controller_hs_state_string(uint8_t purpose); const char *circuit_purpose_to_string(uint8_t purpose); void circuit_dump_by_conn(connection_t *conn, int severity); -void circuit_set_p_circid_orconn(or_circuit_t *circ, circid_t id, - or_connection_t *conn); -void circuit_set_n_circid_orconn(circuit_t *circ, circid_t id, - or_connection_t *conn); +void circuit_dump_by_chan(channel_t *chan, int severity); +void circuit_set_p_circid_chan(or_circuit_t *circ, circid_t id, + channel_t *chan); +void circuit_set_n_circid_chan(circuit_t *circ, circid_t id, + channel_t *chan); void circuit_set_state(circuit_t *circ, uint8_t state); void circuit_close_all_marked(void); int32_t circuit_initial_package_window(void); origin_circuit_t *origin_circuit_new(void); -or_circuit_t *or_circuit_new(circid_t p_circ_id, or_connection_t *p_conn); -circuit_t *circuit_get_by_circid_orconn(circid_t circ_id, - or_connection_t *conn); -int circuit_id_in_use_on_orconn(circid_t circ_id, or_connection_t *conn); +or_circuit_t *or_circuit_new(circid_t p_circ_id, channel_t *p_chan); +circuit_t *circuit_get_by_circid_channel(circid_t circ_id, + channel_t *chan); +int circuit_id_in_use_on_channel(circid_t circ_id, channel_t *chan); circuit_t *circuit_get_by_edge_conn(edge_connection_t *conn); -void circuit_unlink_all_from_or_conn(or_connection_t *conn, int reason); +void circuit_unlink_all_from_channel(channel_t *chan, int reason); origin_circuit_t *circuit_get_by_global_id(uint32_t id); origin_circuit_t *circuit_get_ready_rend_circ_by_rend_data( const rend_data_t *rend_data); @@ -47,9 +48,9 @@ void _circuit_mark_for_close(circuit_t *circ, int reason, int line, const char *file); int circuit_get_cpath_len(origin_circuit_t *circ); crypt_path_t *circuit_get_cpath_hop(origin_circuit_t *circ, int hopnum); -void circuit_get_all_pending_on_or_conn(smartlist_t *out, - or_connection_t *or_conn); -int circuit_count_pending_on_or_conn(or_connection_t *or_conn); +void circuit_get_all_pending_on_channel(smartlist_t *out, + channel_t *chan); +int circuit_count_pending_on_channel(channel_t *chan); #define circuit_mark_for_close(c, reason) \ _circuit_mark_for_close((c), (reason), __LINE__, _SHORT_FILE_) diff --git a/src/or/circuituse.c b/src/or/circuituse.c index 79e1ed0fbb..e4e1b8ab63 100644 --- a/src/or/circuituse.c +++ b/src/or/circuituse.c @@ -10,6 +10,7 @@ **/ #include "or.h" +#include "channel.h" #include "circuitbuild.h" #include "circuitlist.h" #include "circuituse.h" @@ -53,7 +54,7 @@ circuit_is_acceptable(const origin_circuit_t *origin_circ, tor_assert(conn); tor_assert(conn->socks_request); - if (must_be_open && (circ->state != CIRCUIT_STATE_OPEN || !circ->n_conn)) + if (must_be_open && (circ->state != CIRCUIT_STATE_OPEN || !circ->n_chan)) return 0; /* ignore non-open circs */ if (circ->marked_for_close) return 0; @@ -565,9 +566,9 @@ circuit_expire_building(void) continue; } - if (victim->n_conn) - log_info(LD_CIRC,"Abandoning circ %s:%d:%d (state %d:%s, purpose %d)", - victim->n_conn->_base.address, victim->n_conn->_base.port, + if (victim->n_chan) + log_info(LD_CIRC,"Abandoning circ %s:%d (state %d:%s, purpose %d)", + channel_get_canonical_remote_descr(victim->n_chan), victim->n_circ_id, victim->state, circuit_state_to_string(victim->state), victim->purpose); @@ -977,13 +978,13 @@ circuit_expire_old_circuits_serverside(time_t now) /* If the circuit has been idle for too long, and there are no streams * on it, and it ends here, and it used a create_fast, mark it for close. */ - if (or_circ->is_first_hop && !circ->n_conn && + if (or_circ->is_first_hop && !circ->n_chan && !or_circ->n_streams && !or_circ->resolving_streams && - or_circ->p_conn && - or_circ->p_conn->timestamp_last_added_nonpadding <= cutoff) { + or_circ->p_chan && + channel_when_last_xmit(or_circ->p_chan) <= cutoff) { log_info(LD_CIRC, "Closing circ_id %d (empty %d secs ago)", or_circ->p_circ_id, - (int)(now - or_circ->p_conn->timestamp_last_added_nonpadding)); + (int)(now - channel_when_last_xmit(or_circ->p_chan))); circuit_mark_for_close(circ, END_CIRC_REASON_FINISHED); } } @@ -1163,6 +1164,7 @@ circuit_try_attaching_streams(origin_circuit_t *circ) void circuit_build_failed(origin_circuit_t *circ) { + channel_t *n_chan = NULL; /* we should examine circ and see if it failed because of * the last hop or an earlier hop. then use this info below. */ @@ -1179,11 +1181,13 @@ circuit_build_failed(origin_circuit_t *circ) /* We failed at the first hop. If there's an OR connection * to blame, blame it. Also, avoid this relay for a while, and * fail any one-hop directory fetches destined for it. */ - const char *n_conn_id = circ->cpath->extend_info->identity_digest; + const char *n_chan_id = circ->cpath->extend_info->identity_digest; int already_marked = 0; - if (circ->_base.n_conn) { - or_connection_t *n_conn = circ->_base.n_conn; - if (n_conn->is_bad_for_new_circs) { + if (circ->_base.n_chan) { + n_chan = circ->_base.n_chan; + tor_assert(!(n_chan->is_listener)); + + if (n_chan->u.cell_chan.is_bad_for_new_circs) { /* We only want to blame this router when a fresh healthy * connection fails. So don't mark this router as newly failed, * since maybe this was just an old circuit attempt that's @@ -1195,18 +1199,18 @@ circuit_build_failed(origin_circuit_t *circ) } log_info(LD_OR, "Our circuit failed to get a response from the first hop " - "(%s:%d). I'm going to try to rotate to a better connection.", - n_conn->_base.address, n_conn->_base.port); - n_conn->is_bad_for_new_circs = 1; + "(%s). I'm going to try to rotate to a better connection.", + channel_get_canonical_remote_descr(n_chan)); + n_chan->u.cell_chan.is_bad_for_new_circs = 1; } else { log_info(LD_OR, "Our circuit died before the first hop with no connection"); } - if (n_conn_id && !already_marked) { - entry_guard_register_connect_status(n_conn_id, 0, 1, time(NULL)); + if (n_chan_id && !already_marked) { + entry_guard_register_connect_status(n_chan_id, 0, 1, time(NULL)); /* if there are any one-hop streams waiting on this circuit, fail * them now so they can retry elsewhere. */ - connection_ap_fail_onehop(n_conn_id, circ->build_state); + connection_ap_fail_onehop(n_chan_id, circ->build_state); } } diff --git a/src/or/main.c b/src/or/main.c index 74bb696725..a1b1502e4f 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -2148,6 +2148,7 @@ dumpstats(int severity) } } } + /* TODO add dump for channels with circuit_dump_by_chan() */ circuit_dump_by_conn(conn, severity); /* dump info about all the circuits * using this conn */ } SMARTLIST_FOREACH_END(conn); diff --git a/src/or/or.h b/src/or/or.h index 2eee6ec464..4d2ab21d7d 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -445,9 +445,9 @@ typedef enum { #define CIRCUIT_STATE_BUILDING 0 /** Circuit state: Waiting to process the onionskin. */ #define CIRCUIT_STATE_ONIONSKIN_PENDING 1 -/** Circuit state: I'd like to deliver a create, but my n_conn is still +/** Circuit state: I'd like to deliver a create, but my n_chan is still * connecting. */ -#define CIRCUIT_STATE_OR_WAIT 2 +#define CIRCUIT_STATE_CHAN_WAIT 2 /** Circuit state: onionskin(s) processed, ready to send/receive cells. */ #define CIRCUIT_STATE_OPEN 3 @@ -674,7 +674,7 @@ typedef enum { #define END_CIRC_REASON_RESOURCELIMIT 5 #define END_CIRC_REASON_CONNECTFAILED 6 #define END_CIRC_REASON_OR_IDENTITY 7 -#define END_CIRC_REASON_OR_CONN_CLOSED 8 +#define END_CIRC_REASON_CHANNEL_CLOSED 8 #define END_CIRC_REASON_FINISHED 9 #define END_CIRC_REASON_TIMEOUT 10 #define END_CIRC_REASON_DESTROYED 11 @@ -2590,9 +2590,9 @@ typedef struct circuit_t { * ORIGIN_CIRCUIT_MAGIC or OR_CIRCUIT_MAGIC. */ /** Queue of cells waiting to be transmitted on n_conn. */ - cell_queue_t n_conn_cells; - /** The OR connection that is next in this circuit. */ - or_connection_t *n_conn; + cell_queue_t n_chan_cells; + /** The channel that is next in this circuit. */ + channel_t *n_chan; /** The circuit_id used in the next (forward) hop of this circuit. */ circid_t n_circ_id; @@ -2600,12 +2600,12 @@ typedef struct circuit_t { * the circuit has attached to a connection. */ extend_info_t *n_hop; - /** True iff we are waiting for n_conn_cells to become less full before + /** True iff we are waiting for n_chan_cells to become less full before * allowing p_streams to add any more cells. (Origin circuit only.) */ - unsigned int streams_blocked_on_n_conn : 1; - /** True iff we are waiting for p_conn_cells to become less full before + unsigned int streams_blocked_on_n_chan : 1; + /** True iff we are waiting for p_chan_cells to become less full before * allowing n_streams to add any more cells. (OR circuit only.) */ - unsigned int streams_blocked_on_p_conn : 1; + unsigned int streams_blocked_on_p_chan : 1; uint8_t state; /**< Current status of this circuit. */ uint8_t purpose; /**< Why are we creating this circuit? */ @@ -2620,10 +2620,10 @@ typedef struct circuit_t { * more. */ int deliver_window; - /** For storage while n_conn is pending - * (state CIRCUIT_STATE_OR_WAIT). When defined, it is always + /** For storage while n_chan is pending + * (state CIRCUIT_STATE_CHAN_WAIT). When defined, it is always * length ONIONSKIN_CHALLENGE_LEN. */ - char *n_conn_onionskin; + char *n_chan_onionskin; /** When was this circuit created? We keep this timestamp with a higher * resolution than most so that the circuit-build-time tracking code can @@ -2652,11 +2652,11 @@ typedef struct circuit_t { /** Next circuit in the doubly-linked ring of circuits waiting to add * cells to n_conn. NULL if we have no cells pending, or if we're not * linked to an OR connection. */ - struct circuit_t *next_active_on_n_conn; + struct circuit_t *next_active_on_n_chan; /** Previous circuit in the doubly-linked ring of circuits waiting to add * cells to n_conn. NULL if we have no cells pending, or if we're not * linked to an OR connection. */ - struct circuit_t *prev_active_on_n_conn; + struct circuit_t *prev_active_on_n_chan; struct circuit_t *next; /**< Next circuit in linked list of all circuits. */ /** Unique ID for measuring tunneled network status requests. */ @@ -2828,20 +2828,20 @@ typedef struct or_circuit_t { circuit_t _base; /** Next circuit in the doubly-linked ring of circuits waiting to add - * cells to p_conn. NULL if we have no cells pending, or if we're not + * cells to p_chan. NULL if we have no cells pending, or if we're not * linked to an OR connection. */ - struct circuit_t *next_active_on_p_conn; + struct circuit_t *next_active_on_p_chan; /** Previous circuit in the doubly-linked ring of circuits waiting to add - * cells to p_conn. NULL if we have no cells pending, or if we're not + * cells to p_chan. NULL if we have no cells pending, or if we're not * linked to an OR connection. */ - struct circuit_t *prev_active_on_p_conn; + struct circuit_t *prev_active_on_p_chan; /** The circuit_id used in the previous (backward) hop of this circuit. */ circid_t p_circ_id; /** Queue of cells waiting to be transmitted on p_conn. */ - cell_queue_t p_conn_cells; - /** The OR connection that is previous in this circuit. */ - or_connection_t *p_conn; + cell_queue_t p_chan_cells; + /** The channel that is previous in this circuit. */ + channel_t *p_chan; /** Linked list of Exit streams associated with this circuit. */ edge_connection_t *n_streams; /** Linked list of Exit streams associated with this circuit that are diff --git a/src/or/reasons.c b/src/or/reasons.c index c51d8ee6f5..a04cd869a2 100644 --- a/src/or/reasons.c +++ b/src/or/reasons.c @@ -323,8 +323,8 @@ circuit_end_reason_to_control_string(int reason) return "CONNECTFAILED"; case END_CIRC_REASON_OR_IDENTITY: return "OR_IDENTITY"; - case END_CIRC_REASON_OR_CONN_CLOSED: - return "OR_CONN_CLOSED"; + case END_CIRC_REASON_CHANNEL_CLOSED: + return "CHANNEL_CLOSED"; case END_CIRC_REASON_FINISHED: return "FINISHED"; case END_CIRC_REASON_TIMEOUT: diff --git a/src/or/relay.h b/src/or/relay.h index 41675e2106..a41465521a 100644 --- a/src/or/relay.h +++ b/src/or/relay.h @@ -45,15 +45,15 @@ void cell_queue_clear(cell_queue_t *queue); void cell_queue_append(cell_queue_t *queue, packed_cell_t *cell); void cell_queue_append_packed_copy(cell_queue_t *queue, const cell_t *cell); -void append_cell_to_circuit_queue(circuit_t *circ, or_connection_t *orconn, +void append_cell_to_circuit_queue(circuit_t *circ, channel_t *chan, cell_t *cell, cell_direction_t direction, streamid_t fromstream); -void connection_or_unlink_all_active_circs(or_connection_t *conn); -int connection_or_flush_from_first_active_circuit(or_connection_t *conn, - int max, time_t now); +void channel_unlink_all_active_circs(channel_t *chan); +int channel_flush_from_first_active_circuit(channel_t *conn, + int max, time_t now); void assert_active_circuits_ok(or_connection_t *orconn); -void make_circuit_inactive_on_conn(circuit_t *circ, or_connection_t *conn); -void make_circuit_active_on_conn(circuit_t *circ, or_connection_t *conn); +void make_circuit_inactive_on_chan(circuit_t *circ, channel_t *chan); +void make_circuit_active_on_chan(circuit_t *circ, channel_t *chan); int append_address_to_payload(uint8_t *payload_out, const tor_addr_t *addr); const uint8_t *decode_address_from_payload(tor_addr_t *addr_out, @@ -62,7 +62,7 @@ const uint8_t *decode_address_from_payload(tor_addr_t *addr_out, unsigned cell_ewma_get_tick(void); void cell_ewma_set_scale_factor(const or_options_t *options, const networkstatus_t *consensus); -void circuit_clear_cell_queue(circuit_t *circ, or_connection_t *orconn); +void circuit_clear_cell_queue(circuit_t *circ, channel_t *chan); #ifdef RELAY_PRIVATE int relay_crypt(circuit_t *circ, cell_t *cell, cell_direction_t cell_direction, -- cgit v1.2.3-54-g00ecf From 519c971f6a3b89f1e81cda3c0290d4d943ec0d78 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Thu, 23 Aug 2012 19:30:49 -0700 Subject: Use channel_t in cmd.c --- src/or/command.c | 1046 ++++++------------------------------------------------ src/or/command.h | 8 +- 2 files changed, 107 insertions(+), 947 deletions(-) (limited to 'src') diff --git a/src/or/command.c b/src/or/command.c index a5ae2399f0..c7a1b1ac07 100644 --- a/src/or/command.c +++ b/src/or/command.c @@ -12,10 +12,13 @@ /* In-points to command.c: * * - command_process_cell(), called from - * connection_or_process_cells_from_inbuf() in connection_or.c + * incoming cell handlers of channel_t instances; + * callbacks registered in command_setup_channel(), + * called when channels are created in circuitbuild.c */ #include "or.h" +#include "channel.h" #include "circuitbuild.h" #include "circuitlist.h" #include "command.h" @@ -31,8 +34,6 @@ #include "router.h" #include "routerlist.h" -/** How many CELL_PADDING cells have we received, ever? */ -uint64_t stats_n_padding_cells_processed = 0; /** How many CELL_CREATE cells have we received, ever? */ uint64_t stats_n_create_cells_processed = 0; /** How many CELL_CREATED cells have we received, ever? */ @@ -41,38 +42,16 @@ uint64_t stats_n_created_cells_processed = 0; uint64_t stats_n_relay_cells_processed = 0; /** How many CELL_DESTROY cells have we received, ever? */ uint64_t stats_n_destroy_cells_processed = 0; -/** How many CELL_VERSIONS cells have we received, ever? */ -uint64_t stats_n_versions_cells_processed = 0; -/** How many CELL_NETINFO cells have we received, ever? */ -uint64_t stats_n_netinfo_cells_processed = 0; -/** How many CELL_VPADDING cells have we received, ever? */ -uint64_t stats_n_vpadding_cells_processed = 0; -/** How many CELL_CERTS cells have we received, ever? */ -uint64_t stats_n_certs_cells_processed = 0; -/** How many CELL_AUTH_CHALLENGE cells have we received, ever? */ -uint64_t stats_n_auth_challenge_cells_processed = 0; -/** How many CELL_AUTHENTICATE cells have we received, ever? */ -uint64_t stats_n_authenticate_cells_processed = 0; -/** How many CELL_AUTHORIZE cells have we received, ever? */ -uint64_t stats_n_authorize_cells_processed = 0; +/* Handle an incoming channel */ +static void command_handle_incoming_channel(channel_t *listener, + channel_t *chan); /* These are the main functions for processing cells */ -static void command_process_create_cell(cell_t *cell, or_connection_t *conn); -static void command_process_created_cell(cell_t *cell, or_connection_t *conn); -static void command_process_relay_cell(cell_t *cell, or_connection_t *conn); -static void command_process_destroy_cell(cell_t *cell, or_connection_t *conn); -static void command_process_versions_cell(var_cell_t *cell, - or_connection_t *conn); -static void command_process_netinfo_cell(cell_t *cell, or_connection_t *conn); -static void command_process_certs_cell(var_cell_t *cell, - or_connection_t *conn); -static void command_process_auth_challenge_cell(var_cell_t *cell, - or_connection_t *conn); -static void command_process_authenticate_cell(var_cell_t *cell, - or_connection_t *conn); -static int enter_v3_handshake_with_cell(var_cell_t *cell, - or_connection_t *conn); +static void command_process_create_cell(cell_t *cell, channel_t *chan); +static void command_process_created_cell(cell_t *cell, channel_t *chan); +static void command_process_relay_cell(cell_t *cell, channel_t *chan); +static void command_process_destroy_cell(cell_t *cell, channel_t *chan); #ifdef KEEP_TIMING_STATS /** This is a wrapper function around the actual function that processes the @@ -80,15 +59,15 @@ static int enter_v3_handshake_with_cell(var_cell_t *cell, * by the number of microseconds used by the call to *func(cell, conn). */ static void -command_time_process_cell(cell_t *cell, or_connection_t *conn, int *time, - void (*func)(cell_t *, or_connection_t *)) +command_time_process_cell(cell_t *cell, channel_t *chan, int *time, + void (*func)(cell_t *, channel_t *)) { struct timeval start, end; long time_passed; tor_gettimeofday(&start); - (*func)(cell, conn); + (*func)(cell, chan); tor_gettimeofday(&end); time_passed = tv_udiff(&start, &end) ; @@ -104,15 +83,14 @@ command_time_process_cell(cell_t *cell, or_connection_t *conn, int *time, } #endif -/** Process a cell that was just received on conn. Keep internal +/** Process a cell that was just received on chan. Keep internal * statistics about how many of each cell we've processed so far * this second, and the total number of microseconds it took to * process each type of cell. */ void -command_process_cell(cell_t *cell, or_connection_t *conn) +command_process_cell(channel_t *chan, cell_t *cell) { - int handshaking = (conn->_base.state != OR_CONN_STATE_OPEN); #ifdef KEEP_TIMING_STATS /* how many of each cell have we seen so far this second? needs better * name. */ @@ -152,255 +130,114 @@ command_process_cell(cell_t *cell, or_connection_t *conn) #define PROCESS_CELL(tp, cl, cn) command_process_ ## tp ## _cell(cl, cn) #endif - if (conn->_base.marked_for_close) - return; - - /* Reject all but VERSIONS and NETINFO when handshaking. */ - /* (VERSIONS should actually be impossible; it's variable-length.) */ - if (handshaking && cell->command != CELL_VERSIONS && - cell->command != CELL_NETINFO) { - log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, - "Received unexpected cell command %d in state %s; closing the " - "connection.", - (int)cell->command, - conn_state_to_string(CONN_TYPE_OR,conn->_base.state)); - connection_mark_for_close(TO_CONN(conn)); - return; - } - - if (conn->_base.state == OR_CONN_STATE_OR_HANDSHAKING_V3) - or_handshake_state_record_cell(conn->handshake_state, cell, 1); - switch (cell->command) { - case CELL_PADDING: - ++stats_n_padding_cells_processed; - /* do nothing */ - break; case CELL_CREATE: case CELL_CREATE_FAST: ++stats_n_create_cells_processed; - PROCESS_CELL(create, cell, conn); + PROCESS_CELL(create, cell, chan); break; case CELL_CREATED: case CELL_CREATED_FAST: ++stats_n_created_cells_processed; - PROCESS_CELL(created, cell, conn); + PROCESS_CELL(created, cell, chan); break; case CELL_RELAY: case CELL_RELAY_EARLY: ++stats_n_relay_cells_processed; - PROCESS_CELL(relay, cell, conn); + PROCESS_CELL(relay, cell, chan); break; case CELL_DESTROY: ++stats_n_destroy_cells_processed; - PROCESS_CELL(destroy, cell, conn); - break; - case CELL_VERSIONS: - tor_fragile_assert(); - break; - case CELL_NETINFO: - ++stats_n_netinfo_cells_processed; - PROCESS_CELL(netinfo, cell, conn); + PROCESS_CELL(destroy, cell, chan); break; default: log_fn(LOG_INFO, LD_PROTOCOL, - "Cell of unknown type (%d) received. Dropping.", cell->command); + "Cell of unknown or unexpected type (%d) received. " + "Dropping.", + cell->command); break; } } -/** Return true if command is a cell command that's allowed to start a - * V3 handshake. */ -static int -command_allowed_before_handshake(uint8_t command) -{ - switch (command) { - case CELL_VERSIONS: - case CELL_VPADDING: - case CELL_AUTHORIZE: - return 1; - default: - return 0; - } -} - -/** Process a cell that was just received on conn. Keep internal - * statistics about how many of each cell we've processed so far - * this second, and the total number of microseconds it took to - * process each type of cell. +/** Process an incoming var_cell from a channel; in the current protocol all + * the var_cells are handshake-related and handles below the channel layer, + * so this just logs a warning and drops the cell. */ + void -command_process_var_cell(var_cell_t *cell, or_connection_t *conn) +command_process_var_cell(channel_t *chan, var_cell_t *var_cell) { -#ifdef KEEP_TIMING_STATS - /* how many of each cell have we seen so far this second? needs better - * name. */ - static int num_versions=0, num_certs=0; - - time_t now = time(NULL); - - if (now > current_second) { /* the second has rolled over */ - /* print stats */ - log_info(LD_OR, - "At end of second: %d versions (%d ms), %d certs (%d ms)", - num_versions, versions_time/1000, - num_certs, certs_time/1000); - - num_versions = num_certs = 0; - versions_time = certs_time = 0; + tor_assert(chan); + tor_assert(var_cell); - /* remember which second it is, for next time */ - current_second = now; - } -#endif - - if (conn->_base.marked_for_close) - return; - - switch (conn->_base.state) - { - case OR_CONN_STATE_OR_HANDSHAKING_V2: - if (cell->command != CELL_VERSIONS) { - log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, - "Received a cell with command %d in state %s; " - "closing the connection.", - (int)cell->command, - conn_state_to_string(CONN_TYPE_OR,conn->_base.state)); - connection_mark_for_close(TO_CONN(conn)); - return; - } - break; - case OR_CONN_STATE_TLS_HANDSHAKING: - /* If we're using bufferevents, it's entirely possible for us to - * notice "hey, data arrived!" before we notice "hey, the handshake - * finished!" And we need to be accepting both at once to handle both - * the v2 and v3 handshakes. */ - - /* fall through */ - case OR_CONN_STATE_TLS_SERVER_RENEGOTIATING: - if (! command_allowed_before_handshake(cell->command)) { - log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, - "Received a cell with command %d in state %s; " - "closing the connection.", - (int)cell->command, - conn_state_to_string(CONN_TYPE_OR,conn->_base.state)); - connection_mark_for_close(TO_CONN(conn)); - return; - } else { - if (enter_v3_handshake_with_cell(cell, conn)<0) - return; - } - break; - case OR_CONN_STATE_OR_HANDSHAKING_V3: - if (cell->command != CELL_AUTHENTICATE) - or_handshake_state_record_var_cell(conn->handshake_state, cell, 1); - break; /* Everything is allowed */ - case OR_CONN_STATE_OPEN: - if (conn->link_proto < 3) { - log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, - "Received a variable-length cell with command %d in state %s " - "with link protocol %d; ignoring it.", - (int)cell->command, - conn_state_to_string(CONN_TYPE_OR,conn->_base.state), - (int)conn->link_proto); - return; - } - break; - default: - log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, - "Received var-length cell with command %d in unexpected state " - "%s [%d]; ignoring it.", - (int)cell->command, - conn_state_to_string(CONN_TYPE_OR,conn->_base.state), - (int)conn->_base.state); - return; - } - - switch (cell->command) { - case CELL_VERSIONS: - ++stats_n_versions_cells_processed; - PROCESS_CELL(versions, cell, conn); - break; - case CELL_VPADDING: - ++stats_n_vpadding_cells_processed; - /* Do nothing */ - break; - case CELL_CERTS: - ++stats_n_certs_cells_processed; - PROCESS_CELL(certs, cell, conn); - break; - case CELL_AUTH_CHALLENGE: - ++stats_n_auth_challenge_cells_processed; - PROCESS_CELL(auth_challenge, cell, conn); - break; - case CELL_AUTHENTICATE: - ++stats_n_authenticate_cells_processed; - PROCESS_CELL(authenticate, cell, conn); - break; - case CELL_AUTHORIZE: - ++stats_n_authorize_cells_processed; - /* Ignored so far. */ - break; - default: - log_fn(LOG_INFO, LD_PROTOCOL, - "Variable-length cell of unknown type (%d) received.", - cell->command); - break; - } + log_info(LD_PROTOCOL, + "Received unexpected var_cell above the channel layer of type %d" + "; dropping it.", + var_cell->command); } -/** Process a 'create' cell that just arrived from conn. Make a +/** Process a 'create' cell that just arrived from chan. Make a * new circuit with the p_circ_id specified in cell. Put the circuit in state * onionskin_pending, and pass the onionskin to the cpuworker. Circ will get * picked up again when the cpuworker finishes decrypting it. */ static void -command_process_create_cell(cell_t *cell, or_connection_t *conn) +command_process_create_cell(cell_t *cell, channel_t *chan) { or_circuit_t *circ; const or_options_t *options = get_options(); int id_is_high; + tor_assert(cell); + tor_assert(chan); + tor_assert(!(chan->is_listener)); + + log_debug(LD_OR, + "Got a CREATE cell for circ_id %d on channel %lu (%p)", + cell->circ_id, chan->global_identifier, chan); + if (we_are_hibernating()) { log_info(LD_OR, "Received create cell but we're shutting down. Sending back " "destroy."); - connection_or_send_destroy(cell->circ_id, conn, + channel_send_destroy(cell->circ_id, chan, END_CIRC_REASON_HIBERNATING); return; } if (!server_mode(options) || - (!public_server_mode(options) && conn->is_outgoing)) { + (!public_server_mode(options) && channel_is_outgoing(chan))) { log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, - "Received create cell (type %d) from %s:%d, but we're connected " + "Received create cell (type %d) from %s, but we're connected " "to it as a client. " "Sending back a destroy.", - (int)cell->command, conn->_base.address, conn->_base.port); - connection_or_send_destroy(cell->circ_id, conn, - END_CIRC_REASON_TORPROTOCOL); + (int)cell->command, channel_get_canonical_remote_descr(chan)); + channel_send_destroy(cell->circ_id, chan, + END_CIRC_REASON_TORPROTOCOL); return; } /* If the high bit of the circuit ID is not as expected, close the * circ. */ id_is_high = cell->circ_id & (1<<15); - if ((id_is_high && conn->circ_id_type == CIRC_ID_TYPE_HIGHER) || - (!id_is_high && conn->circ_id_type == CIRC_ID_TYPE_LOWER)) { + if ((id_is_high && + chan->u.cell_chan.circ_id_type == CIRC_ID_TYPE_HIGHER) || + (!id_is_high && + chan->u.cell_chan.circ_id_type == CIRC_ID_TYPE_LOWER)) { log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Received create cell with unexpected circ_id %d. Closing.", cell->circ_id); - connection_or_send_destroy(cell->circ_id, conn, - END_CIRC_REASON_TORPROTOCOL); + channel_send_destroy(cell->circ_id, chan, + END_CIRC_REASON_TORPROTOCOL); return; } - if (circuit_id_in_use_on_orconn(cell->circ_id, conn)) { - const node_t *node = node_get_by_id(conn->identity_digest); + if (circuit_id_in_use_on_channel(cell->circ_id, chan)) { + const node_t *node = node_get_by_id(chan->u.cell_chan.identity_digest); log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Received CREATE cell (circID %d) for known circ. " "Dropping (age %d).", - cell->circ_id, (int)(time(NULL) - conn->_base.timestamp_created)); + cell->circ_id, (int)(time(NULL) - channel_when_created(chan))); if (node) { char *p = esc_for_log(node_get_platform(node)); log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, @@ -411,7 +248,7 @@ command_process_create_cell(cell_t *cell, or_connection_t *conn) return; } - circ = or_circuit_new(cell->circ_id, conn); + circ = or_circuit_new(cell->circ_id, chan); circ->_base.purpose = CIRCUIT_PURPOSE_OR; circuit_set_state(TO_CIRCUIT(circ), CIRCUIT_STATE_ONIONSKIN_PENDING); if (cell->command == CELL_CREATE) { @@ -442,7 +279,7 @@ command_process_create_cell(cell_t *cell, or_connection_t *conn) /* Make sure we never try to use the OR connection on which we * received this cell to satisfy an EXTEND request, */ - conn->is_connection_with_client = 1; + channel_mark_client(chan); if (fast_server_handshake(cell->payload, (uint8_t*)reply, (uint8_t*)keys, sizeof(keys))<0) { @@ -458,7 +295,7 @@ command_process_create_cell(cell_t *cell, or_connection_t *conn) } } -/** Process a 'created' cell that just arrived from conn. +/** Process a 'created' cell that just arrived from chan. * Find the circuit * that it's intended for. If we're not the origin of the circuit, package * the 'created' cell in an 'extended' relay cell and pass it back. If we @@ -467,11 +304,11 @@ command_process_create_cell(cell_t *cell, or_connection_t *conn) * extend to the next hop in the circuit if necessary. */ static void -command_process_created_cell(cell_t *cell, or_connection_t *conn) +command_process_created_cell(cell_t *cell, channel_t *chan) { circuit_t *circ; - circ = circuit_get_by_circid_orconn(cell->circ_id, conn); + circ = circuit_get_by_circid_channel(cell->circ_id, chan); if (!circ) { log_info(LD_OR, @@ -518,17 +355,17 @@ command_process_created_cell(cell_t *cell, or_connection_t *conn) * circuit_receive_relay_cell() for actual processing. */ static void -command_process_relay_cell(cell_t *cell, or_connection_t *conn) +command_process_relay_cell(cell_t *cell, channel_t *chan) { circuit_t *circ; int reason, direction; - circ = circuit_get_by_circid_orconn(cell->circ_id, conn); + circ = circuit_get_by_circid_channel(cell->circ_id, chan); if (!circ) { log_debug(LD_OR, - "unknown circuit %d on connection from %s:%d. Dropping.", - cell->circ_id, conn->_base.address, conn->_base.port); + "unknown circuit %d on connection from %s. Dropping.", + cell->circ_id, channel_get_canonical_remote_descr(chan)); return; } @@ -541,7 +378,7 @@ command_process_relay_cell(cell_t *cell, or_connection_t *conn) if (CIRCUIT_IS_ORIGIN(circ)) { /* if we're a relay and treating connections with recent local * traffic better, then this is one of them. */ - conn->client_used = time(NULL); + channel_timestamp_client(chan); } if (!CIRCUIT_IS_ORIGIN(circ) && @@ -562,10 +399,10 @@ command_process_relay_cell(cell_t *cell, or_connection_t *conn) or_circuit_t *or_circ = TO_OR_CIRCUIT(circ); if (or_circ->remaining_relay_early_cells == 0) { log_fn(LOG_PROTOCOL_WARN, LD_OR, - "Received too many RELAY_EARLY cells on circ %d from %s:%d." + "Received too many RELAY_EARLY cells on circ %d from %s." " Closing circuit.", - cell->circ_id, safe_str(conn->_base.address), - conn->_base.port); + cell->circ_id, + safe_str(channel_get_canonical_remote_descr(chan))); circuit_mark_for_close(circ, END_CIRC_REASON_TORPROTOCOL); return; } @@ -582,7 +419,7 @@ command_process_relay_cell(cell_t *cell, or_connection_t *conn) } /** Process a 'destroy' cell that just arrived from - * conn. Find the circ that it refers to (if any). + * chan. Find the circ that it refers to (if any). * * If the circ is in state * onionskin_pending, then call onion_pending_remove() to remove it @@ -595,15 +432,15 @@ command_process_relay_cell(cell_t *cell, or_connection_t *conn) * and passes the destroy cell onward if necessary). */ static void -command_process_destroy_cell(cell_t *cell, or_connection_t *conn) +command_process_destroy_cell(cell_t *cell, channel_t *chan) { circuit_t *circ; int reason; - circ = circuit_get_by_circid_orconn(cell->circ_id, conn); + circ = circuit_get_by_circid_channel(cell->circ_id, chan); if (!circ) { - log_info(LD_OR,"unknown circuit %d on connection from %s:%d. Dropping.", - cell->circ_id, conn->_base.address, conn->_base.port); + log_info(LD_OR,"unknown circuit %d on connection from %s. Dropping.", + cell->circ_id, channel_get_canonical_remote_descr(chan)); return; } log_debug(LD_OR,"Received for circID %d.",cell->circ_id); @@ -613,10 +450,10 @@ command_process_destroy_cell(cell_t *cell, or_connection_t *conn) if (!CIRCUIT_IS_ORIGIN(circ) && cell->circ_id == TO_OR_CIRCUIT(circ)->p_circ_id) { /* the destroy came from behind */ - circuit_set_p_circid_orconn(TO_OR_CIRCUIT(circ), 0, NULL); + circuit_set_p_circid_chan(TO_OR_CIRCUIT(circ), 0, NULL); circuit_mark_for_close(circ, reason|END_CIRC_REASON_FLAG_REMOTE); } else { /* the destroy came from ahead */ - circuit_set_n_circid_orconn(circ, 0, NULL); + circuit_set_n_circid_chan(circ, 0, NULL); if (CIRCUIT_IS_ORIGIN(circ)) { circuit_mark_for_close(circ, reason|END_CIRC_REASON_FLAG_REMOTE); } else { @@ -629,724 +466,43 @@ command_process_destroy_cell(cell_t *cell, or_connection_t *conn) } } -/** Called when we as a server receive an appropriate cell while waiting - * either for a cell or a TLS handshake. Set the connection's state to - * "handshaking_v3', initializes the or_handshake_state field as needed, - * and add the cell to the hash of incoming cells.) - * - * Return 0 on success; return -1 and mark the connection on failure. +/** Callback to handle a new channel; call command_setup_channel() to give + * it the right cell handlers. */ -static int -enter_v3_handshake_with_cell(var_cell_t *cell, or_connection_t *conn) -{ - const int started_here = connection_or_nonopen_was_started_here(conn); - tor_assert(conn->_base.state == OR_CONN_STATE_TLS_HANDSHAKING || - conn->_base.state == OR_CONN_STATE_TLS_SERVER_RENEGOTIATING); - - if (started_here) { - log_fn(LOG_PROTOCOL_WARN, LD_OR, - "Received a cell while TLS-handshaking, not in " - "OR_HANDSHAKING_V3, on a connection we originated."); - } - conn->_base.state = OR_CONN_STATE_OR_HANDSHAKING_V3; - if (connection_init_or_handshake_state(conn, started_here) < 0) { - connection_mark_for_close(TO_CONN(conn)); - return -1; - } - or_handshake_state_record_var_cell(conn->handshake_state, cell, 1); - return 0; -} - -/** Process a 'versions' cell. The current link protocol version must be 0 - * to indicate that no version has yet been negotiated. We compare the - * versions in the cell to the list of versions we support, pick the - * highest version we have in common, and continue the negotiation from - * there. - */ static void -command_process_versions_cell(var_cell_t *cell, or_connection_t *conn) +command_handle_incoming_channel(channel_t *listener, channel_t *chan) { - int highest_supported_version = 0; - const uint8_t *cp, *end; - const int started_here = connection_or_nonopen_was_started_here(conn); - if (conn->link_proto != 0 || - (conn->handshake_state && conn->handshake_state->received_versions)) { - log_fn(LOG_PROTOCOL_WARN, LD_OR, - "Received a VERSIONS cell on a connection with its version " - "already set to %d; dropping", (int) conn->link_proto); - return; - } - switch (conn->_base.state) - { - case OR_CONN_STATE_OR_HANDSHAKING_V2: - case OR_CONN_STATE_OR_HANDSHAKING_V3: - break; - case OR_CONN_STATE_TLS_HANDSHAKING: - case OR_CONN_STATE_TLS_SERVER_RENEGOTIATING: - default: - log_fn(LOG_PROTOCOL_WARN, LD_OR, - "VERSIONS cell while in unexpected state"); - return; - } - - tor_assert(conn->handshake_state); - end = cell->payload + cell->payload_len; - for (cp = cell->payload; cp+1 < end; ++cp) { - uint16_t v = ntohs(get_uint16(cp)); - if (is_or_protocol_version_known(v) && v > highest_supported_version) - highest_supported_version = v; - } - if (!highest_supported_version) { - log_fn(LOG_PROTOCOL_WARN, LD_OR, - "Couldn't find a version in common between my version list and the " - "list in the VERSIONS cell; closing connection."); - connection_mark_for_close(TO_CONN(conn)); - return; - } else if (highest_supported_version == 1) { - /* Negotiating version 1 makes no sense, since version 1 has no VERSIONS - * cells. */ - log_fn(LOG_PROTOCOL_WARN, LD_OR, - "Used version negotiation protocol to negotiate a v1 connection. " - "That's crazily non-compliant. Closing connection."); - connection_mark_for_close(TO_CONN(conn)); - return; - } else if (highest_supported_version < 3 && - conn->_base.state == OR_CONN_STATE_OR_HANDSHAKING_V3) { - log_fn(LOG_PROTOCOL_WARN, LD_OR, - "Negotiated link protocol 2 or lower after doing a v3 TLS " - "handshake. Closing connection."); - connection_mark_for_close(TO_CONN(conn)); - return; - } - - conn->link_proto = highest_supported_version; - conn->handshake_state->received_versions = 1; - - if (conn->link_proto == 2) { - log_info(LD_OR, "Negotiated version %d with %s:%d; sending NETINFO.", - highest_supported_version, - safe_str_client(conn->_base.address), - conn->_base.port); - - if (connection_or_send_netinfo(conn) < 0) { - connection_mark_for_close(TO_CONN(conn)); - return; - } - } else { - const int send_versions = !started_here; - /* If we want to authenticate, send a CERTS cell */ - const int send_certs = !started_here || public_server_mode(get_options()); - /* If we're a relay that got a connection, ask for authentication. */ - const int send_chall = !started_here && public_server_mode(get_options()); - /* If our certs cell will authenticate us, we can send a netinfo cell - * right now. */ - const int send_netinfo = !started_here; - const int send_any = - send_versions || send_certs || send_chall || send_netinfo; - tor_assert(conn->link_proto >= 3); - - log_info(LD_OR, "Negotiated version %d with %s:%d; %s%s%s%s%s", - highest_supported_version, - safe_str_client(conn->_base.address), - conn->_base.port, - send_any ? "Sending cells:" : "Waiting for CERTS cell", - send_versions ? " VERSIONS" : "", - send_certs ? " CERTS" : "", - send_chall ? " AUTH_CHALLENGE" : "", - send_netinfo ? " NETINFO" : ""); - -#ifdef DISABLE_V3_LINKPROTO_SERVERSIDE - if (1) { - connection_mark_for_close(TO_CONN(conn)); - return; - } -#endif - - if (send_versions) { - if (connection_or_send_versions(conn, 1) < 0) { - log_warn(LD_OR, "Couldn't send versions cell"); - connection_mark_for_close(TO_CONN(conn)); - return; - } - } - if (send_certs) { - if (connection_or_send_certs_cell(conn) < 0) { - log_warn(LD_OR, "Couldn't send certs cell"); - connection_mark_for_close(TO_CONN(conn)); - return; - } - } - if (send_chall) { - if (connection_or_send_auth_challenge_cell(conn) < 0) { - log_warn(LD_OR, "Couldn't send auth_challenge cell"); - connection_mark_for_close(TO_CONN(conn)); - return; - } - } - if (send_netinfo) { - if (connection_or_send_netinfo(conn) < 0) { - log_warn(LD_OR, "Couldn't send netinfo cell"); - connection_mark_for_close(TO_CONN(conn)); - return; - } - } - } -} - -/** Process a 'netinfo' cell: read and act on its contents, and set the - * connection state to "open". */ -static void -command_process_netinfo_cell(cell_t *cell, or_connection_t *conn) -{ - time_t timestamp; - uint8_t my_addr_type; - uint8_t my_addr_len; - const uint8_t *my_addr_ptr; - const uint8_t *cp, *end; - uint8_t n_other_addrs; - time_t now = time(NULL); - - long apparent_skew = 0; - tor_addr_t my_apparent_addr = TOR_ADDR_NULL; + tor_assert(listener); + tor_assert(chan); - if (conn->link_proto < 2) { - log_fn(LOG_PROTOCOL_WARN, LD_OR, - "Received a NETINFO cell on %s connection; dropping.", - conn->link_proto == 0 ? "non-versioned" : "a v1"); - return; - } - if (conn->_base.state != OR_CONN_STATE_OR_HANDSHAKING_V2 && - conn->_base.state != OR_CONN_STATE_OR_HANDSHAKING_V3) { - log_fn(LOG_PROTOCOL_WARN, LD_OR, - "Received a NETINFO cell on non-handshaking connection; dropping."); - return; - } - tor_assert(conn->handshake_state && - conn->handshake_state->received_versions); - - if (conn->_base.state == OR_CONN_STATE_OR_HANDSHAKING_V3) { - tor_assert(conn->link_proto >= 3); - if (conn->handshake_state->started_here) { - if (!conn->handshake_state->authenticated) { - log_fn(LOG_PROTOCOL_WARN, LD_OR, "Got a NETINFO cell from server, " - "but no authentication. Closing the connection."); - connection_mark_for_close(TO_CONN(conn)); - return; - } - } else { - /* we're the server. If the client never authenticated, we have - some housekeeping to do.*/ - if (!conn->handshake_state->authenticated) { - tor_assert(tor_digest_is_zero( - (const char*)conn->handshake_state->authenticated_peer_id)); - connection_or_set_circid_type(conn, NULL); - - connection_or_init_conn_from_address(conn, - &conn->_base.addr, - conn->_base.port, - (const char*)conn->handshake_state->authenticated_peer_id, - 0); - } - } - } - - /* Decode the cell. */ - timestamp = ntohl(get_uint32(cell->payload)); - if (labs(now - conn->handshake_state->sent_versions_at) < 180) { - apparent_skew = now - timestamp; - } - - my_addr_type = (uint8_t) cell->payload[4]; - my_addr_len = (uint8_t) cell->payload[5]; - my_addr_ptr = (uint8_t*) cell->payload + 6; - end = cell->payload + CELL_PAYLOAD_SIZE; - cp = cell->payload + 6 + my_addr_len; - if (cp >= end) { - log_fn(LOG_PROTOCOL_WARN, LD_OR, - "Addresses too long in netinfo cell; closing connection."); - connection_mark_for_close(TO_CONN(conn)); - return; - } else if (my_addr_type == RESOLVED_TYPE_IPV4 && my_addr_len == 4) { - tor_addr_from_ipv4n(&my_apparent_addr, get_uint32(my_addr_ptr)); - } else if (my_addr_type == RESOLVED_TYPE_IPV6 && my_addr_len == 16) { - tor_addr_from_ipv6_bytes(&my_apparent_addr, (const char *) my_addr_ptr); - } - - n_other_addrs = (uint8_t) *cp++; - while (n_other_addrs && cp < end-2) { - /* Consider all the other addresses; if any matches, this connection is - * "canonical." */ - tor_addr_t addr; - const uint8_t *next = - decode_address_from_payload(&addr, cp, (int)(end-cp)); - if (next == NULL) { - log_fn(LOG_PROTOCOL_WARN, LD_OR, - "Bad address in netinfo cell; closing connection."); - connection_mark_for_close(TO_CONN(conn)); - return; - } - if (tor_addr_eq(&addr, &conn->real_addr)) { - conn->is_canonical = 1; - break; - } - cp = next; - --n_other_addrs; - } - - /* Act on apparent skew. */ - /** Warn when we get a netinfo skew with at least this value. */ -#define NETINFO_NOTICE_SKEW 3600 - if (labs(apparent_skew) > NETINFO_NOTICE_SKEW && - router_get_by_id_digest(conn->identity_digest)) { - char dbuf[64]; - int severity; - /*XXXX be smarter about when everybody says we are skewed. */ - if (router_digest_is_trusted_dir(conn->identity_digest)) - severity = LOG_WARN; - else - severity = LOG_INFO; - format_time_interval(dbuf, sizeof(dbuf), apparent_skew); - log_fn(severity, LD_GENERAL, "Received NETINFO cell with skewed time from " - "server at %s:%d. It seems that our clock is %s by %s, or " - "that theirs is %s. Tor requires an accurate clock to work: " - "please check your time and date settings.", - conn->_base.address, (int)conn->_base.port, - apparent_skew>0 ? "ahead" : "behind", dbuf, - apparent_skew>0 ? "behind" : "ahead"); - if (severity == LOG_WARN) /* only tell the controller if an authority */ - control_event_general_status(LOG_WARN, - "CLOCK_SKEW SKEW=%ld SOURCE=OR:%s:%d", - apparent_skew, - conn->_base.address, conn->_base.port); - } - - /* XXX maybe act on my_apparent_addr, if the source is sufficiently - * trustworthy. */ - - if (connection_or_set_state_open(conn)<0) { - log_fn(LOG_PROTOCOL_WARN, LD_OR, "Got good NETINFO cell from %s:%d; but " - "was unable to make the OR connection become open.", - safe_str_client(conn->_base.address), - conn->_base.port); - connection_mark_for_close(TO_CONN(conn)); - } else { - log_info(LD_OR, "Got good NETINFO cell from %s:%d; OR connection is now " - "open, using protocol version %d. Its ID digest is %s. " - "Our address is apparently %s.", - safe_str_client(conn->_base.address), - conn->_base.port, (int)conn->link_proto, - hex_str(conn->identity_digest, DIGEST_LEN), - tor_addr_is_null(&my_apparent_addr) ? - "" : fmt_and_decorate_addr(&my_apparent_addr)); - } - assert_connection_ok(TO_CONN(conn),time(NULL)); + command_setup_channel(chan); } -/** Process a CERTS cell from an OR connection. - * - * If the other side should not have sent us a CERTS cell, or the cell is - * malformed, or it is supposed to authenticate the TLS key but it doesn't, - * then mark the connection. - * - * If the cell has a good cert chain and we're doing a v3 handshake, then - * store the certificates in or_handshake_state. If this is the client side - * of the connection, we then authenticate the server or mark the connection. - * If it's the server side, wait for an AUTHENTICATE cell. +/** Given a channel, install the right handlers to process incoming + * cells on it. */ -static void -command_process_certs_cell(var_cell_t *cell, or_connection_t *conn) -{ -#define ERR(s) \ - do { \ - log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, \ - "Received a bad CERTS cell from %s:%d: %s", \ - safe_str(conn->_base.address), conn->_base.port, (s)); \ - connection_mark_for_close(TO_CONN(conn)); \ - goto err; \ - } while (0) - - tor_cert_t *link_cert = NULL; - tor_cert_t *id_cert = NULL; - tor_cert_t *auth_cert = NULL; - - uint8_t *ptr; - int n_certs, i; - int send_netinfo = 0; - - if (conn->_base.state != OR_CONN_STATE_OR_HANDSHAKING_V3) - ERR("We're not doing a v3 handshake!"); - if (conn->link_proto < 3) - ERR("We're not using link protocol >= 3"); - if (conn->handshake_state->received_certs_cell) - ERR("We already got one"); - if (conn->handshake_state->authenticated) { - /* Should be unreachable, but let's make sure. */ - ERR("We're already authenticated!"); - } - if (cell->payload_len < 1) - ERR("It had no body"); - if (cell->circ_id) - ERR("It had a nonzero circuit ID"); - - n_certs = cell->payload[0]; - ptr = cell->payload + 1; - for (i = 0; i < n_certs; ++i) { - uint8_t cert_type; - uint16_t cert_len; - if (ptr + 3 > cell->payload + cell->payload_len) { - goto truncated; - } - cert_type = *ptr; - cert_len = ntohs(get_uint16(ptr+1)); - if (ptr + 3 + cert_len > cell->payload + cell->payload_len) { - goto truncated; - } - if (cert_type == OR_CERT_TYPE_TLS_LINK || - cert_type == OR_CERT_TYPE_ID_1024 || - cert_type == OR_CERT_TYPE_AUTH_1024) { - tor_cert_t *cert = tor_cert_decode(ptr + 3, cert_len); - if (!cert) { - log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, - "Received undecodable certificate in CERTS cell from %s:%d", - safe_str(conn->_base.address), conn->_base.port); - } else { - if (cert_type == OR_CERT_TYPE_TLS_LINK) { - if (link_cert) { - tor_cert_free(cert); - ERR("Too many TLS_LINK certificates"); - } - link_cert = cert; - } else if (cert_type == OR_CERT_TYPE_ID_1024) { - if (id_cert) { - tor_cert_free(cert); - ERR("Too many ID_1024 certificates"); - } - id_cert = cert; - } else if (cert_type == OR_CERT_TYPE_AUTH_1024) { - if (auth_cert) { - tor_cert_free(cert); - ERR("Too many AUTH_1024 certificates"); - } - auth_cert = cert; - } else { - tor_cert_free(cert); - } - } - } - ptr += 3 + cert_len; - continue; - - truncated: - ERR("It ends in the middle of a certificate"); - } - - if (conn->handshake_state->started_here) { - int severity; - if (! (id_cert && link_cert)) - ERR("The certs we wanted were missing"); - /* Okay. We should be able to check the certificates now. */ - if (! tor_tls_cert_matches_key(conn->tls, link_cert)) { - ERR("The link certificate didn't match the TLS public key"); - } - /* Note that this warns more loudly about time and validity if we were - * _trying_ to connect to an authority, not necessarily if we _did_ connect - * to one. */ - if (router_digest_is_trusted_dir(conn->identity_digest)) - severity = LOG_WARN; - else - severity = LOG_PROTOCOL_WARN; - - if (! tor_tls_cert_is_valid(severity, link_cert, id_cert, 0)) - ERR("The link certificate was not valid"); - if (! tor_tls_cert_is_valid(severity, id_cert, id_cert, 1)) - ERR("The ID certificate was not valid"); - - conn->handshake_state->authenticated = 1; - { - const digests_t *id_digests = tor_cert_get_id_digests(id_cert); - crypto_pk_t *identity_rcvd; - if (!id_digests) - ERR("Couldn't compute digests for key in ID cert"); - - identity_rcvd = tor_tls_cert_get_key(id_cert); - if (!identity_rcvd) - ERR("Internal error: Couldn't get RSA key from ID cert."); - memcpy(conn->handshake_state->authenticated_peer_id, - id_digests->d[DIGEST_SHA1], DIGEST_LEN); - connection_or_set_circid_type(conn, identity_rcvd); - crypto_pk_free(identity_rcvd); - } - - if (connection_or_client_learned_peer_id(conn, - conn->handshake_state->authenticated_peer_id) < 0) - ERR("Problem setting or checking peer id"); - - log_info(LD_OR, "Got some good certificates from %s:%d: Authenticated it.", - safe_str(conn->_base.address), conn->_base.port); - - conn->handshake_state->id_cert = id_cert; - id_cert = NULL; - - if (!public_server_mode(get_options())) { - /* If we initiated the connection and we are not a public server, we - * aren't planning to authenticate at all. At this point we know who we - * are talking to, so we can just send a netinfo now. */ - send_netinfo = 1; - } - } else { - if (! (id_cert && auth_cert)) - ERR("The certs we wanted were missing"); - - /* Remember these certificates so we can check an AUTHENTICATE cell */ - if (! tor_tls_cert_is_valid(LOG_PROTOCOL_WARN, auth_cert, id_cert, 1)) - ERR("The authentication certificate was not valid"); - if (! tor_tls_cert_is_valid(LOG_PROTOCOL_WARN, id_cert, id_cert, 1)) - ERR("The ID certificate was not valid"); - - log_info(LD_OR, "Got some good certificates from %s:%d: " - "Waiting for AUTHENTICATE.", - safe_str(conn->_base.address), conn->_base.port); - /* XXXX check more stuff? */ - conn->handshake_state->id_cert = id_cert; - conn->handshake_state->auth_cert = auth_cert; - id_cert = auth_cert = NULL; - } - - conn->handshake_state->received_certs_cell = 1; - - if (send_netinfo) { - if (connection_or_send_netinfo(conn) < 0) { - log_warn(LD_OR, "Couldn't send netinfo cell"); - connection_mark_for_close(TO_CONN(conn)); - goto err; - } - } - - err: - tor_cert_free(id_cert); - tor_cert_free(link_cert); - tor_cert_free(auth_cert); -#undef ERR -} - -/** Process an AUTH_CHALLENGE cell from an OR connection. - * - * If we weren't supposed to get one (for example, because we're not the - * originator of the connection), or it's ill-formed, or we aren't doing a v3 - * handshake, mark the connection. If the cell is well-formed but we don't - * want to authenticate, just drop it. If the cell is well-formed *and* we - * want to authenticate, send an AUTHENTICATE cell and then a NETINFO cell. */ -static void -command_process_auth_challenge_cell(var_cell_t *cell, or_connection_t *conn) +void +command_setup_channel(channel_t *chan) { - int n_types, i, use_type = -1; - uint8_t *cp; - -#define ERR(s) \ - do { \ - log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, \ - "Received a bad AUTH_CHALLENGE cell from %s:%d: %s", \ - safe_str(conn->_base.address), conn->_base.port, (s)); \ - connection_mark_for_close(TO_CONN(conn)); \ - return; \ - } while (0) - - if (conn->_base.state != OR_CONN_STATE_OR_HANDSHAKING_V3) - ERR("We're not currently doing a v3 handshake"); - if (conn->link_proto < 3) - ERR("We're not using link protocol >= 3"); - if (! conn->handshake_state->started_here) - ERR("We didn't originate this connection"); - if (conn->handshake_state->received_auth_challenge) - ERR("We already received one"); - if (! conn->handshake_state->received_certs_cell) - ERR("We haven't gotten a CERTS cell yet"); - if (cell->payload_len < OR_AUTH_CHALLENGE_LEN + 2) - ERR("It was too short"); - if (cell->circ_id) - ERR("It had a nonzero circuit ID"); - - n_types = ntohs(get_uint16(cell->payload + OR_AUTH_CHALLENGE_LEN)); - if (cell->payload_len < OR_AUTH_CHALLENGE_LEN + 2 + 2*n_types) - ERR("It looks truncated"); - - /* Now see if there is an authentication type we can use */ - cp=cell->payload+OR_AUTH_CHALLENGE_LEN+2; - for (i=0; i < n_types; ++i, cp += 2) { - uint16_t authtype = ntohs(get_uint16(cp)); - if (authtype == AUTHTYPE_RSA_SHA256_TLSSECRET) - use_type = authtype; - } - - conn->handshake_state->received_auth_challenge = 1; - - if (! public_server_mode(get_options())) { - /* If we're not a public server then we don't want to authenticate on a - connection we originated, and we already sent a NETINFO cell when we - got the CERTS cell. We have nothing more to do. */ - return; - } - - if (use_type >= 0) { - log_info(LD_OR, "Got an AUTH_CHALLENGE cell from %s:%d: Sending " - "authentication", - safe_str(conn->_base.address), conn->_base.port); - - if (connection_or_send_authenticate_cell(conn, use_type) < 0) { - log_warn(LD_OR, "Couldn't send authenticate cell"); - connection_mark_for_close(TO_CONN(conn)); - return; - } - } else { - log_info(LD_OR, "Got an AUTH_CHALLENGE cell from %s:%d, but we don't " - "know any of its authentication types. Not authenticating.", - safe_str(conn->_base.address), conn->_base.port); - } - - if (connection_or_send_netinfo(conn) < 0) { - log_warn(LD_OR, "Couldn't send netinfo cell"); - connection_mark_for_close(TO_CONN(conn)); - return; - } + tor_assert(chan); -#undef ERR + channel_set_cell_handlers(chan, + command_process_cell, + command_process_var_cell); } -/** Process an AUTHENTICATE cell from an OR connection. - * - * If it's ill-formed or we weren't supposed to get one or we're not doing a - * v3 handshake, then mark the connection. If it does not authenticate the - * other side of the connection successfully (because it isn't signed right, - * we didn't get a CERTS cell, etc) mark the connection. Otherwise, accept - * the identity of the router on the other side of the connection. +/** Given a listener, install the right handler to process incoming + * channels on it. */ -static void -command_process_authenticate_cell(var_cell_t *cell, or_connection_t *conn) -{ - uint8_t expected[V3_AUTH_FIXED_PART_LEN]; - const uint8_t *auth; - int authlen; - -#define ERR(s) \ - do { \ - log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, \ - "Received a bad AUTHENTICATE cell from %s:%d: %s", \ - safe_str(conn->_base.address), conn->_base.port, (s)); \ - connection_mark_for_close(TO_CONN(conn)); \ - return; \ - } while (0) - - if (conn->_base.state != OR_CONN_STATE_OR_HANDSHAKING_V3) - ERR("We're not doing a v3 handshake"); - if (conn->link_proto < 3) - ERR("We're not using link protocol >= 3"); - if (conn->handshake_state->started_here) - ERR("We originated this connection"); - if (conn->handshake_state->received_authenticate) - ERR("We already got one!"); - if (conn->handshake_state->authenticated) { - /* Should be impossible given other checks */ - ERR("The peer is already authenticated"); - } - if (! conn->handshake_state->received_certs_cell) - ERR("We never got a certs cell"); - if (conn->handshake_state->auth_cert == NULL) - ERR("We never got an authentication certificate"); - if (conn->handshake_state->id_cert == NULL) - ERR("We never got an identity certificate"); - if (cell->payload_len < 4) - ERR("Cell was way too short"); - - auth = cell->payload; - { - uint16_t type = ntohs(get_uint16(auth)); - uint16_t len = ntohs(get_uint16(auth+2)); - if (4 + len > cell->payload_len) - ERR("Authenticator was truncated"); - - if (type != AUTHTYPE_RSA_SHA256_TLSSECRET) - ERR("Authenticator type was not recognized"); - - auth += 4; - authlen = len; - } - - if (authlen < V3_AUTH_BODY_LEN + 1) - ERR("Authenticator was too short"); - - if (connection_or_compute_authenticate_cell_body( - conn, expected, sizeof(expected), NULL, 1) < 0) - ERR("Couldn't compute expected AUTHENTICATE cell body"); - - if (tor_memneq(expected, auth, sizeof(expected))) - ERR("Some field in the AUTHENTICATE cell body was not as expected"); - - { - crypto_pk_t *pk = tor_tls_cert_get_key( - conn->handshake_state->auth_cert); - char d[DIGEST256_LEN]; - char *signed_data; - size_t keysize; - int signed_len; - if (!pk) - ERR("Internal error: couldn't get RSA key from AUTH cert."); - crypto_digest256(d, (char*)auth, V3_AUTH_BODY_LEN, DIGEST_SHA256); - - keysize = crypto_pk_keysize(pk); - signed_data = tor_malloc(keysize); - signed_len = crypto_pk_public_checksig(pk, signed_data, keysize, - (char*)auth + V3_AUTH_BODY_LEN, - authlen - V3_AUTH_BODY_LEN); - crypto_pk_free(pk); - if (signed_len < 0) { - tor_free(signed_data); - ERR("Signature wasn't valid"); - } - if (signed_len < DIGEST256_LEN) { - tor_free(signed_data); - ERR("Not enough data was signed"); - } - /* Note that we deliberately allow *more* than DIGEST256_LEN bytes here, - * in case they're later used to hold a SHA3 digest or something. */ - if (tor_memneq(signed_data, d, DIGEST256_LEN)) { - tor_free(signed_data); - ERR("Signature did not match data to be signed."); - } - tor_free(signed_data); - } - - /* Okay, we are authenticated. */ - conn->handshake_state->received_authenticate = 1; - conn->handshake_state->authenticated = 1; - conn->handshake_state->digest_received_data = 0; - { - crypto_pk_t *identity_rcvd = - tor_tls_cert_get_key(conn->handshake_state->id_cert); - const digests_t *id_digests = - tor_cert_get_id_digests(conn->handshake_state->id_cert); - - /* This must exist; we checked key type when reading the cert. */ - tor_assert(id_digests); - - memcpy(conn->handshake_state->authenticated_peer_id, - id_digests->d[DIGEST_SHA1], DIGEST_LEN); - - connection_or_set_circid_type(conn, identity_rcvd); - crypto_pk_free(identity_rcvd); - - connection_or_init_conn_from_address(conn, - &conn->_base.addr, - conn->_base.port, - (const char*)conn->handshake_state->authenticated_peer_id, - 0); - - log_info(LD_OR, "Got an AUTHENTICATE cell from %s:%d: Looks good.", - safe_str(conn->_base.address), conn->_base.port); - } +void +command_setup_listener(channel_t *listener) +{ + tor_assert(listener); + tor_assert(listener->state == CHANNEL_STATE_LISTENING); -#undef ERR + channel_set_listener(listener, command_handle_incoming_channel); } diff --git a/src/or/command.h b/src/or/command.h index 078ccc9f5d..eddce8741b 100644 --- a/src/or/command.h +++ b/src/or/command.h @@ -12,8 +12,12 @@ #ifndef _TOR_COMMAND_H #define _TOR_COMMAND_H -void command_process_cell(cell_t *cell, or_connection_t *conn); -void command_process_var_cell(var_cell_t *cell, or_connection_t *conn); +#include "channel.h" + +void command_process_cell(channel_t *chan, cell_t *cell); +void command_process_var_cell(channel_t *chan, var_cell_t *cell); +void command_setup_channel(channel_t *chan); +void command_setup_listener(channel_t *chan); extern uint64_t stats_n_padding_cells_processed; extern uint64_t stats_n_create_cells_processed; -- cgit v1.2.3-54-g00ecf From 4768c0efe3e9471cc367c3740d1a4ba0ab79626c Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Sat, 25 Aug 2012 11:55:39 -0700 Subject: Support channel_t in connection_edge.c --- src/or/connection_edge.c | 50 +++++++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 24 deletions(-) (limited to 'src') diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c index ade3b48df2..9bcfca30de 100644 --- a/src/or/connection_edge.c +++ b/src/or/connection_edge.c @@ -11,6 +11,7 @@ #include "or.h" #include "buffers.h" +#include "channel.h" #include "circuitlist.h" #include "circuituse.h" #include "config.h" @@ -3068,27 +3069,30 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ) tor_free(address); return 0; } - if (or_circ && or_circ->p_conn && !options->AllowSingleHopExits && - (or_circ->is_first_hop || - (!connection_or_digest_is_known_relay( - or_circ->p_conn->identity_digest) && + if (or_circ && or_circ->p_chan) { + tor_assert(!(or_circ->p_chan->is_listener)); + if (!options->AllowSingleHopExits && + (or_circ->is_first_hop || + (!connection_or_digest_is_known_relay( + or_circ->p_chan->u.cell_chan.identity_digest) && should_refuse_unknown_exits(options)))) { - /* Don't let clients use us as a single-hop proxy, unless the user - * has explicitly allowed that in the config. It attracts attackers - * and users who'd be better off with, well, single-hop proxies. - */ - log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, - "Attempt by %s to open a stream %s. Closing.", - safe_str(or_circ->p_conn->_base.address), - or_circ->is_first_hop ? "on first hop of circuit" : - "from unknown relay"); - relay_send_end_cell_from_edge(rh.stream_id, circ, - or_circ->is_first_hop ? - END_STREAM_REASON_TORPROTOCOL : - END_STREAM_REASON_MISC, - NULL); - tor_free(address); - return 0; + /* Don't let clients use us as a single-hop proxy, unless the user + * has explicitly allowed that in the config. It attracts attackers + * and users who'd be better off with, well, single-hop proxies. + */ + log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, + "Attempt by %s to open a stream %s. Closing.", + safe_str(channel_get_canonical_remote_descr(or_circ->p_chan)), + or_circ->is_first_hop ? "on first hop of circuit" : + "from unknown relay"); + relay_send_end_cell_from_edge(rh.stream_id, circ, + or_circ->is_first_hop ? + END_STREAM_REASON_TORPROTOCOL : + END_STREAM_REASON_MISC, + NULL); + tor_free(address); + return 0; + } } } else if (rh.command == RELAY_COMMAND_BEGIN_DIR) { if (!directory_permits_begindir_requests(options) || @@ -3101,8 +3105,8 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ) * caller might want to know whether his IP address has changed, and * we might already have corrected _base.addr[ess] for the relay's * canonical IP address. */ - if (or_circ && or_circ->p_conn) - address = tor_dup_addr(&or_circ->p_conn->real_addr); + if (or_circ && or_circ->p_chan) + address = tor_strdup(channel_get_actual_remote_descr(or_circ->p_chan)); else address = tor_strdup("127.0.0.1"); port = 1; /* XXXX This value is never actually used anywhere, and there @@ -3178,8 +3182,6 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ) if (rh.command == RELAY_COMMAND_BEGIN_DIR) { tor_assert(or_circ); - if (or_circ->p_conn && !tor_addr_is_null(&or_circ->p_conn->real_addr)) - tor_addr_copy(&n_stream->_base.addr, &or_circ->p_conn->real_addr); return connection_exit_connect_dir(n_stream); } -- cgit v1.2.3-54-g00ecf From e136f7ccb4e671e33b6c92a48df819082291f5c1 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Sat, 25 Aug 2012 14:30:01 -0700 Subject: Convert relay.c/relay.h to channel_t Note: this is a squashed commit; see branch bug6465_rebased_v2 of user/andrea/tor.git for full history of the following 10 commits: Convert relay.c/relay.h to channel_t Updating the timestamp if n_flushed > 0 at the end of channel_flush_from_first_active_circuit() was redundant since channel_write_cell() et al. do it themselves. Get rid of now-unnecessary time parameter in channel_flush_from_first_active_circuit() Get rid of now-unnecessary time parameter in channel_flush_from_first_active_circuit() in connection_or.c Add non-inlined external call for channeltls.c to free a packed_cell_t Appease make check-spaces in relay.c Replace channel_get_write_queue_len() with sufficient and easier to implement channel_has_queued_writes() in relay.c Rename channel_touched_by_client() and client_used field for consistency with other timestamps in relay.c Don't double-free packed cells in relay.c (channel_t Tor now bootstraps and works as a client) Rearrange channel_t struct to use a union distinguishing listener from cell-bearing channels in relay.c --- src/or/connection.c | 4 - src/or/relay.c | 405 ++++++++++++++++++++++++++++++---------------------- src/or/relay.h | 8 +- 3 files changed, 236 insertions(+), 181 deletions(-) (limited to 'src') diff --git a/src/or/connection.c b/src/or/connection.c index 8e9c70191d..db234f1cb1 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -265,9 +265,6 @@ or_connection_new(int socket_family) or_conn->timestamp_last_added_nonpadding = time(NULL); - or_conn->active_circuit_pqueue = smartlist_new(); - or_conn->active_circuit_pqueue_last_recalibrated = cell_ewma_get_tick(); - return or_conn; } @@ -508,7 +505,6 @@ _connection_free(connection_t *conn) or_conn->tls = NULL; or_handshake_state_free(or_conn->handshake_state); or_conn->handshake_state = NULL; - smartlist_free(or_conn->active_circuit_pqueue); tor_free(or_conn->nickname); } if (conn->type == CONN_TYPE_AP) { diff --git a/src/or/relay.c b/src/or/relay.c index 4a50b710dd..3850562917 100644 --- a/src/or/relay.c +++ b/src/or/relay.c @@ -14,6 +14,7 @@ #define RELAY_PRIVATE #include "or.h" #include "buffers.h" +#include "channel.h" #include "circuitbuild.h" #include "circuitlist.h" #include "config.h" @@ -166,7 +167,7 @@ int circuit_receive_relay_cell(cell_t *cell, circuit_t *circ, cell_direction_t cell_direction) { - or_connection_t *or_conn=NULL; + channel_t *chan = NULL; crypt_path_t *layer_hint=NULL; char recognized=0; int reason; @@ -213,17 +214,17 @@ circuit_receive_relay_cell(cell_t *cell, circuit_t *circ, /* not recognized. pass it on. */ if (cell_direction == CELL_DIRECTION_OUT) { cell->circ_id = circ->n_circ_id; /* switch it */ - or_conn = circ->n_conn; + chan = circ->n_chan; } else if (! CIRCUIT_IS_ORIGIN(circ)) { cell->circ_id = TO_OR_CIRCUIT(circ)->p_circ_id; /* switch it */ - or_conn = TO_OR_CIRCUIT(circ)->p_conn; + chan = TO_OR_CIRCUIT(circ)->p_chan; } else { log_fn(LOG_PROTOCOL_WARN, LD_OR, "Dropping unrecognized inbound cell on origin circuit."); return 0; } - if (!or_conn) { + if (!chan) { // XXXX Can this splice stuff be done more cleanly? if (! CIRCUIT_IS_ORIGIN(circ) && TO_OR_CIRCUIT(circ)->rend_splice && @@ -254,7 +255,7 @@ circuit_receive_relay_cell(cell_t *cell, circuit_t *circ, * we might kill the circ before we relay * the cells. */ - append_cell_to_circuit_queue(circ, or_conn, cell, cell_direction, 0); + append_cell_to_circuit_queue(circ, chan, cell, cell_direction, 0); return 0; } @@ -353,13 +354,13 @@ circuit_package_relay_cell(cell_t *cell, circuit_t *circ, cell_direction_t cell_direction, crypt_path_t *layer_hint, streamid_t on_stream) { - or_connection_t *conn; /* where to send the cell */ + channel_t *chan; /* where to send the cell */ if (cell_direction == CELL_DIRECTION_OUT) { crypt_path_t *thishop; /* counter for repeated crypts */ - conn = circ->n_conn; - if (!CIRCUIT_IS_ORIGIN(circ) || !conn) { - log_warn(LD_BUG,"outgoing relay cell has n_conn==NULL. Dropping."); + chan = circ->n_chan; + if (!CIRCUIT_IS_ORIGIN(circ) || !chan) { + log_warn(LD_BUG,"outgoing relay cell has n_chan==NULL. Dropping."); return 0; /* just drop it */ } @@ -388,14 +389,14 @@ circuit_package_relay_cell(cell_t *cell, circuit_t *circ, return 0; /* just drop it */ } or_circ = TO_OR_CIRCUIT(circ); - conn = or_circ->p_conn; + chan = or_circ->p_chan; relay_set_digest(or_circ->p_digest, cell); if (relay_crypt_one_payload(or_circ->p_crypto, cell->payload, 1) < 0) return -1; } ++stats_n_relay_cells_relayed; - append_cell_to_circuit_queue(circ, conn, cell, cell_direction, on_stream); + append_cell_to_circuit_queue(circ, chan, cell, cell_direction, on_stream); return 0; } @@ -561,9 +562,9 @@ relay_send_command_from_edge(streamid_t stream_id, circuit_t *circ, geoip_change_dirreq_state(circ->dirreq_id, DIRREQ_TUNNELED, DIRREQ_END_CELL_SENT); - if (cell_direction == CELL_DIRECTION_OUT && circ->n_conn) { + if (cell_direction == CELL_DIRECTION_OUT && circ->n_chan) { /* if we're using relaybandwidthrate, this conn wants priority */ - circ->n_conn->client_used = approx_time(); + channel_timestamp_client(circ->n_chan); } if (cell_direction == CELL_DIRECTION_OUT) { @@ -1095,7 +1096,8 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ, * and linked. */ static uint64_t next_id = 0; circ->dirreq_id = ++next_id; - TO_CONN(TO_OR_CIRCUIT(circ)->p_conn)->dirreq_id = circ->dirreq_id; + tor_assert(!(TO_OR_CIRCUIT(circ)->p_chan->is_listener)); + TO_OR_CIRCUIT(circ)->p_chan->u.cell_chan.dirreq_id = circ->dirreq_id; } return connection_exit_begin_conn(cell, circ); @@ -1230,12 +1232,12 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ, "'truncate' unsupported at origin. Dropping."); return 0; } - if (circ->n_conn) { + if (circ->n_chan) { uint8_t trunc_reason = get_uint8(cell->payload + RELAY_HEADER_SIZE); - circuit_clear_cell_queue(circ, circ->n_conn); - connection_or_send_destroy(circ->n_circ_id, circ->n_conn, - trunc_reason); - circuit_set_n_circid_orconn(circ, 0, NULL); + circuit_clear_cell_queue(circ, circ->n_chan); + channel_send_destroy(circ->n_circ_id, circ->n_chan, + trunc_reason); + circuit_set_n_circid_chan(circ, 0, NULL); } log_debug(LD_EXIT, "Processed 'truncate', replying."); { @@ -1594,10 +1596,10 @@ circuit_resume_edge_reading_helper(edge_connection_t *first_conn, * needed to fill the cell queue. */ int max_to_package = circ->package_window; if (CIRCUIT_IS_ORIGIN(circ)) { - cells_on_queue = circ->n_conn_cells.n; + cells_on_queue = circ->n_chan_cells.n; } else { or_circuit_t *or_circ = TO_OR_CIRCUIT(circ); - cells_on_queue = or_circ->p_conn_cells.n; + cells_on_queue = or_circ->p_chan_cells.n; } if (CELL_QUEUE_HIGHWATER_SIZE - cells_on_queue < max_to_package) max_to_package = CELL_QUEUE_HIGHWATER_SIZE - cells_on_queue; @@ -1842,6 +1844,13 @@ packed_cell_new(void) return mp_pool_get(cell_pool); } +/** Return a packed cell used outside by channel_t lower layer */ +void +packed_cell_free(packed_cell_t *cell) +{ + packed_cell_free_unchecked(cell); +} + /** Log current statistics for cell pool allocation at log level * severity. */ void @@ -1851,9 +1860,9 @@ dump_cell_pool_usage(int severity) int n_circs = 0; int n_cells = 0; for (c = _circuit_get_global_list(); c; c = c->next) { - n_cells += c->n_conn_cells.n; + n_cells += c->n_chan_cells.n; if (!CIRCUIT_IS_ORIGIN(c)) - n_cells += TO_OR_CIRCUIT(c)->p_conn_cells.n; + n_cells += TO_OR_CIRCUIT(c)->p_chan_cells.n; ++n_circs; } log(severity, LD_MM, "%d cells allocated on %d circuits. %d cells leaked.", @@ -1964,35 +1973,35 @@ cell_queue_pop(cell_queue_t *queue) return cell; } -/** Return a pointer to the "next_active_on_{n,p}_conn" pointer of circ, - * depending on whether conn matches n_conn or p_conn. */ +/** Return a pointer to the "next_active_on_{n,p}_chan" pointer of circ, + * depending on whether chan matches n_chan or p_chan. */ static INLINE circuit_t ** -next_circ_on_conn_p(circuit_t *circ, or_connection_t *conn) +next_circ_on_chan_p(circuit_t *circ, channel_t *chan) { tor_assert(circ); - tor_assert(conn); - if (conn == circ->n_conn) { - return &circ->next_active_on_n_conn; + tor_assert(chan); + if (chan == circ->n_chan) { + return &circ->next_active_on_n_chan; } else { or_circuit_t *orcirc = TO_OR_CIRCUIT(circ); - tor_assert(conn == orcirc->p_conn); - return &orcirc->next_active_on_p_conn; + tor_assert(chan == orcirc->p_chan); + return &orcirc->next_active_on_p_chan; } } -/** Return a pointer to the "prev_active_on_{n,p}_conn" pointer of circ, - * depending on whether conn matches n_conn or p_conn. */ +/** Return a pointer to the "prev_active_on_{n,p}_chan" pointer of circ, + * depending on whether chan matches n_chan or p_chan. */ static INLINE circuit_t ** -prev_circ_on_conn_p(circuit_t *circ, or_connection_t *conn) +prev_circ_on_chan_p(circuit_t *circ, channel_t *chan) { tor_assert(circ); - tor_assert(conn); - if (conn == circ->n_conn) { - return &circ->prev_active_on_n_conn; + tor_assert(chan); + if (chan == circ->n_chan) { + return &circ->prev_active_on_n_chan; } else { or_circuit_t *orcirc = TO_OR_CIRCUIT(circ); - tor_assert(conn == orcirc->p_conn); - return &orcirc->prev_active_on_p_conn; + tor_assert(chan == orcirc->p_chan); + return &orcirc->prev_active_on_p_chan; } } @@ -2013,7 +2022,7 @@ compare_cell_ewma_counts(const void *p1, const void *p2) static circuit_t * cell_ewma_to_circuit(cell_ewma_t *ewma) { - if (ewma->is_for_p_conn) { + if (ewma->is_for_p_chan) { /* This is an or_circuit_t's p_cell_ewma. */ or_circuit_t *orcirc = SUBTYPE_P(ewma, or_circuit_t, p_cell_ewma); return TO_CIRCUIT(orcirc); @@ -2162,165 +2171,203 @@ scale_single_cell_ewma(cell_ewma_t *ewma, unsigned cur_tick) ewma->last_adjusted_tick = cur_tick; } -/** Adjust the cell count of every active circuit on conn so +/** Adjust the cell count of every active circuit on chan so * that they are scaled with respect to cur_tick */ static void -scale_active_circuits(or_connection_t *conn, unsigned cur_tick) +scale_active_circuits(channel_t *chan, unsigned cur_tick) { + double factor; + + tor_assert(chan); + tor_assert(!(chan->is_listener)); - double factor = get_scale_factor( - conn->active_circuit_pqueue_last_recalibrated, - cur_tick); + factor = + get_scale_factor( + chan->u.cell_chan.active_circuit_pqueue_last_recalibrated, + cur_tick); /** Ordinarily it isn't okay to change the value of an element in a heap, * but it's okay here, since we are preserving the order. */ - SMARTLIST_FOREACH(conn->active_circuit_pqueue, cell_ewma_t *, e, { + SMARTLIST_FOREACH_BEGIN( + chan->u.cell_chan.active_circuit_pqueue, + cell_ewma_t *, e) { tor_assert(e->last_adjusted_tick == - conn->active_circuit_pqueue_last_recalibrated); + chan->u.cell_chan.active_circuit_pqueue_last_recalibrated); e->cell_count *= factor; e->last_adjusted_tick = cur_tick; - }); - conn->active_circuit_pqueue_last_recalibrated = cur_tick; + } SMARTLIST_FOREACH_END(e); + chan->u.cell_chan.active_circuit_pqueue_last_recalibrated = cur_tick; } -/** Rescale ewma to the same scale as conn, and add it to - * conn's priority queue of active circuits */ +/** Rescale ewma to the same scale as chan, and add it to + * chan's priority queue of active circuits */ static void -add_cell_ewma_to_conn(or_connection_t *conn, cell_ewma_t *ewma) +add_cell_ewma_to_chan(channel_t *chan, cell_ewma_t *ewma) { + tor_assert(chan); + tor_assert(!(chan->is_listener)); + tor_assert(ewma); tor_assert(ewma->heap_index == -1); - scale_single_cell_ewma(ewma, - conn->active_circuit_pqueue_last_recalibrated); - smartlist_pqueue_add(conn->active_circuit_pqueue, + scale_single_cell_ewma( + ewma, + chan->u.cell_chan.active_circuit_pqueue_last_recalibrated); + + smartlist_pqueue_add(chan->u.cell_chan.active_circuit_pqueue, compare_cell_ewma_counts, STRUCT_OFFSET(cell_ewma_t, heap_index), ewma); } -/** Remove ewma from conn's priority queue of active circuits */ +/** Remove ewma from chan's priority queue of active circuits */ static void -remove_cell_ewma_from_conn(or_connection_t *conn, cell_ewma_t *ewma) +remove_cell_ewma_from_chan(channel_t *chan, cell_ewma_t *ewma) { + tor_assert(chan); + tor_assert(!(chan->is_listener)); + tor_assert(ewma); tor_assert(ewma->heap_index != -1); - smartlist_pqueue_remove(conn->active_circuit_pqueue, + + smartlist_pqueue_remove(chan->u.cell_chan.active_circuit_pqueue, compare_cell_ewma_counts, STRUCT_OFFSET(cell_ewma_t, heap_index), ewma); } -/** Remove and return the first cell_ewma_t from conn's priority queue of +/** Remove and return the first cell_ewma_t from chan's priority queue of * active circuits. Requires that the priority queue is nonempty. */ static cell_ewma_t * -pop_first_cell_ewma_from_conn(or_connection_t *conn) +pop_first_cell_ewma_from_chan(channel_t *chan) { - return smartlist_pqueue_pop(conn->active_circuit_pqueue, + tor_assert(chan); + tor_assert(!(chan->is_listener)); + + return smartlist_pqueue_pop(chan->u.cell_chan.active_circuit_pqueue, compare_cell_ewma_counts, STRUCT_OFFSET(cell_ewma_t, heap_index)); } /** Add circ to the list of circuits with pending cells on - * conn. No effect if circ is already linked. */ + * chan. No effect if circ is already linked. */ void -make_circuit_active_on_conn(circuit_t *circ, or_connection_t *conn) +make_circuit_active_on_chan(circuit_t *circ, channel_t *chan) { - circuit_t **nextp = next_circ_on_conn_p(circ, conn); - circuit_t **prevp = prev_circ_on_conn_p(circ, conn); + circuit_t **nextp = NULL, **prevp = NULL; + + tor_assert(chan); + tor_assert(!(chan->is_listener)); + tor_assert(circ); + + nextp = next_circ_on_chan_p(circ, chan); + prevp = prev_circ_on_chan_p(circ, chan); if (*nextp && *prevp) { /* Already active. */ return; } - assert_active_circuits_ok_paranoid(conn); + assert_active_circuits_ok_paranoid(chan); - if (! conn->active_circuits) { - conn->active_circuits = circ; + if (!(chan->u.cell_chan.active_circuits)) { + chan->u.cell_chan.active_circuits = circ; *prevp = *nextp = circ; } else { - circuit_t *head = conn->active_circuits; - circuit_t *old_tail = *prev_circ_on_conn_p(head, conn); - *next_circ_on_conn_p(old_tail, conn) = circ; + circuit_t *head = chan->u.cell_chan.active_circuits; + circuit_t *old_tail = *prev_circ_on_chan_p(head, chan); + *next_circ_on_chan_p(old_tail, chan) = circ; *nextp = head; - *prev_circ_on_conn_p(head, conn) = circ; + *prev_circ_on_chan_p(head, chan) = circ; *prevp = old_tail; } - if (circ->n_conn == conn) { - add_cell_ewma_to_conn(conn, &circ->n_cell_ewma); + if (circ->n_chan == chan) { + add_cell_ewma_to_chan(chan, &circ->n_cell_ewma); } else { or_circuit_t *orcirc = TO_OR_CIRCUIT(circ); - tor_assert(conn == orcirc->p_conn); - add_cell_ewma_to_conn(conn, &orcirc->p_cell_ewma); + tor_assert(chan == orcirc->p_chan); + add_cell_ewma_to_chan(chan, &orcirc->p_cell_ewma); } - assert_active_circuits_ok_paranoid(conn); + assert_active_circuits_ok_paranoid(chan); } /** Remove circ from the list of circuits with pending cells on - * conn. No effect if circ is already unlinked. */ + * chan. No effect if circ is already unlinked. */ void -make_circuit_inactive_on_conn(circuit_t *circ, or_connection_t *conn) +make_circuit_inactive_on_chan(circuit_t *circ, channel_t *chan) { - circuit_t **nextp = next_circ_on_conn_p(circ, conn); - circuit_t **prevp = prev_circ_on_conn_p(circ, conn); - circuit_t *next = *nextp, *prev = *prevp; + circuit_t **nextp = NULL, **prevp = NULL; + circuit_t *next = NULL, *prev = NULL; + + tor_assert(chan); + tor_assert(!(chan->is_listener)); + tor_assert(circ); + + nextp = next_circ_on_chan_p(circ, chan); + prevp = prev_circ_on_chan_p(circ, chan); + next = *nextp; + prev = *prevp; if (!next && !prev) { /* Already inactive. */ return; } - assert_active_circuits_ok_paranoid(conn); + assert_active_circuits_ok_paranoid(chan); tor_assert(next && prev); - tor_assert(*prev_circ_on_conn_p(next, conn) == circ); - tor_assert(*next_circ_on_conn_p(prev, conn) == circ); + tor_assert(*prev_circ_on_chan_p(next, chan) == circ); + tor_assert(*next_circ_on_chan_p(prev, chan) == circ); if (next == circ) { - conn->active_circuits = NULL; + chan->u.cell_chan.active_circuits = NULL; } else { - *prev_circ_on_conn_p(next, conn) = prev; - *next_circ_on_conn_p(prev, conn) = next; - if (conn->active_circuits == circ) - conn->active_circuits = next; + *prev_circ_on_chan_p(next, chan) = prev; + *next_circ_on_chan_p(prev, chan) = next; + if (chan->u.cell_chan.active_circuits == circ) + chan->u.cell_chan.active_circuits = next; } *prevp = *nextp = NULL; - if (circ->n_conn == conn) { - remove_cell_ewma_from_conn(conn, &circ->n_cell_ewma); + if (circ->n_chan == chan) { + remove_cell_ewma_from_chan(chan, &circ->n_cell_ewma); } else { or_circuit_t *orcirc = TO_OR_CIRCUIT(circ); - tor_assert(conn == orcirc->p_conn); - remove_cell_ewma_from_conn(conn, &orcirc->p_cell_ewma); + tor_assert(chan == orcirc->p_chan); + remove_cell_ewma_from_chan(chan, &orcirc->p_cell_ewma); } - assert_active_circuits_ok_paranoid(conn); + assert_active_circuits_ok_paranoid(chan); } /** Remove all circuits from the list of circuits with pending cells on - * conn. */ + * chan. */ void -connection_or_unlink_all_active_circs(or_connection_t *orconn) +channel_unlink_all_active_circs(channel_t *chan) { - circuit_t *head = orconn->active_circuits; - circuit_t *cur = head; + circuit_t *head = NULL, *cur = NULL; + + tor_assert(chan); + tor_assert(!(chan->is_listener)); + + cur = head = chan->u.cell_chan.active_circuits; if (! head) return; do { - circuit_t *next = *next_circ_on_conn_p(cur, orconn); - *prev_circ_on_conn_p(cur, orconn) = NULL; - *next_circ_on_conn_p(cur, orconn) = NULL; + circuit_t *next = *next_circ_on_chan_p(cur, chan); + *prev_circ_on_chan_p(cur, chan) = NULL; + *next_circ_on_chan_p(cur, chan) = NULL; cur = next; } while (cur != head); - orconn->active_circuits = NULL; + chan->u.cell_chan.active_circuits = NULL; - SMARTLIST_FOREACH(orconn->active_circuit_pqueue, cell_ewma_t *, e, + SMARTLIST_FOREACH(chan->u.cell_chan.active_circuit_pqueue, + cell_ewma_t *, e, e->heap_index = -1); - smartlist_clear(orconn->active_circuit_pqueue); + smartlist_clear(chan->u.cell_chan.active_circuit_pqueue); } /** Block (if block is true) or unblock (if block is false) - * every edge connection that is using circ to write to orconn, + * every edge connection that is using circ to write to chan, * and start or stop reading as appropriate. * * If stream_id is nonzero, block only the edge connection whose @@ -2329,17 +2376,17 @@ connection_or_unlink_all_active_circs(or_connection_t *orconn) * Returns the number of streams whose status we changed. */ static int -set_streams_blocked_on_circ(circuit_t *circ, or_connection_t *orconn, +set_streams_blocked_on_circ(circuit_t *circ, channel_t *chan, int block, streamid_t stream_id) { edge_connection_t *edge = NULL; int n = 0; - if (circ->n_conn == orconn) { - circ->streams_blocked_on_n_conn = block; + if (circ->n_chan == chan) { + circ->streams_blocked_on_n_chan = block; if (CIRCUIT_IS_ORIGIN(circ)) edge = TO_ORIGIN_CIRCUIT(circ)->p_streams; } else { - circ->streams_blocked_on_p_conn = block; + circ->streams_blocked_on_p_chan = block; tor_assert(!CIRCUIT_IS_ORIGIN(circ)); edge = TO_OR_CIRCUIT(circ)->n_streams; } @@ -2374,12 +2421,11 @@ set_streams_blocked_on_circ(circuit_t *circ, or_connection_t *orconn, } /** Pull as many cells as possible (but no more than max) from the - * queue of the first active circuit on conn, and write them to - * conn->outbuf. Return the number of cells written. Advance + * queue of the first active circuit on chan, and write them to + * chan->outbuf. Return the number of cells written. Advance * the active circuit pointer to the next active circuit in the ring. */ int -connection_or_flush_from_first_active_circuit(or_connection_t *conn, int max, - time_t now) +channel_flush_from_first_active_circuit(channel_t *chan, int max) { int n_flushed; cell_queue_t *queue; @@ -2393,9 +2439,12 @@ connection_or_flush_from_first_active_circuit(or_connection_t *conn, int max, cell_ewma_t *cell_ewma = NULL; double ewma_increment = -1; - circ = conn->active_circuits; + tor_assert(chan); + tor_assert(!(chan->is_listener)); + + circ = chan->u.cell_chan.active_circuits; if (!circ) return 0; - assert_active_circuits_ok_paranoid(conn); + assert_active_circuits_ok_paranoid(chan); /* See if we're doing the ewma circuit selection algorithm. */ if (ewma_enabled) { @@ -2404,28 +2453,28 @@ connection_or_flush_from_first_active_circuit(or_connection_t *conn, int max, tor_gettimeofday_cached(&now_hires); tick = cell_ewma_tick_from_timeval(&now_hires, &fractional_tick); - if (tick != conn->active_circuit_pqueue_last_recalibrated) { - scale_active_circuits(conn, tick); + if (tick != chan->u.cell_chan.active_circuit_pqueue_last_recalibrated) { + scale_active_circuits(chan, tick); } ewma_increment = pow(ewma_scale_factor, -fractional_tick); - cell_ewma = smartlist_get(conn->active_circuit_pqueue, 0); + cell_ewma = smartlist_get(chan->u.cell_chan.active_circuit_pqueue, 0); circ = cell_ewma_to_circuit(cell_ewma); } - if (circ->n_conn == conn) { - queue = &circ->n_conn_cells; - streams_blocked = circ->streams_blocked_on_n_conn; + if (circ->n_chan == chan) { + queue = &circ->n_chan_cells; + streams_blocked = circ->streams_blocked_on_n_chan; } else { - queue = &TO_OR_CIRCUIT(circ)->p_conn_cells; - streams_blocked = circ->streams_blocked_on_p_conn; + queue = &TO_OR_CIRCUIT(circ)->p_chan_cells; + streams_blocked = circ->streams_blocked_on_p_chan; } - tor_assert(*next_circ_on_conn_p(circ,conn)); + tor_assert(*next_circ_on_chan_p(circ, chan)); for (n_flushed = 0; n_flushed < max && queue->head; ) { packed_cell_t *cell = cell_queue_pop(queue); - tor_assert(*next_circ_on_conn_p(circ,conn)); + tor_assert(*next_circ_on_chan_p(circ, chan)); /* Calculate the exact time that this cell has spent in the queue. */ if (get_options()->CellStatistics && !CIRCUIT_IS_ORIGIN(circ)) { @@ -2462,14 +2511,18 @@ connection_or_flush_from_first_active_circuit(or_connection_t *conn, int max, /* If we just flushed our queue and this circuit is used for a * tunneled directory request, possibly advance its state. */ - if (queue->n == 0 && TO_CONN(conn)->dirreq_id) - geoip_change_dirreq_state(TO_CONN(conn)->dirreq_id, + if (queue->n == 0 && chan->u.cell_chan.dirreq_id) + geoip_change_dirreq_state(chan->u.cell_chan.dirreq_id, DIRREQ_TUNNELED, DIRREQ_CIRC_QUEUE_FLUSHED); - connection_write_to_buf(cell->body, CELL_NETWORK_SIZE, TO_CONN(conn)); + channel_write_packed_cell(chan, cell); + /* + * Don't packed_cell_free_unchecked(cell) here because the channel will + * do so when it gets out of the channel queue (probably already did, in + * which case that was an immediate double-free bug). + */ - packed_cell_free_unchecked(cell); ++n_flushed; if (cell_ewma) { cell_ewma_t *tmp; @@ -2477,44 +2530,43 @@ connection_or_flush_from_first_active_circuit(or_connection_t *conn, int max, /* We pop and re-add the cell_ewma_t here, not above, since we need to * re-add it immediately to keep the priority queue consistent with * the linked-list implementation */ - tmp = pop_first_cell_ewma_from_conn(conn); + tmp = pop_first_cell_ewma_from_chan(chan); tor_assert(tmp == cell_ewma); - add_cell_ewma_to_conn(conn, cell_ewma); + add_cell_ewma_to_chan(chan, cell_ewma); } - if (!ewma_enabled && circ != conn->active_circuits) { + if (!ewma_enabled && circ != chan->u.cell_chan.active_circuits) { /* If this happens, the current circuit just got made inactive by * a call in connection_write_to_buf(). That's nothing to worry about: - * circuit_make_inactive_on_conn() already advanced conn->active_circuits + * circuit_make_inactive_on_conn() already advanced chan->active_circuits * for us. */ - assert_active_circuits_ok_paranoid(conn); + assert_active_circuits_ok_paranoid(chan); goto done; } } - tor_assert(*next_circ_on_conn_p(circ,conn)); - assert_active_circuits_ok_paranoid(conn); - conn->active_circuits = *next_circ_on_conn_p(circ, conn); + tor_assert(*next_circ_on_chan_p(circ, chan)); + assert_active_circuits_ok_paranoid(chan); + chan->u.cell_chan.active_circuits = *next_circ_on_chan_p(circ, chan); /* Is the cell queue low enough to unblock all the streams that are waiting * to write to this circuit? */ if (streams_blocked && queue->n <= CELL_QUEUE_LOWWATER_SIZE) - set_streams_blocked_on_circ(circ, conn, 0, 0); /* unblock streams */ + set_streams_blocked_on_circ(circ, chan, 0, 0); /* unblock streams */ /* Did we just run out of cells on this circuit's queue? */ if (queue->n == 0) { log_debug(LD_GENERAL, "Made a circuit inactive."); - make_circuit_inactive_on_conn(circ, conn); + make_circuit_inactive_on_chan(circ, chan); } done: - if (n_flushed) - conn->timestamp_last_added_nonpadding = now; + return n_flushed; } -/** Add cell to the queue of circ writing to orconn +/** Add cell to the queue of circ writing to chan * transmitting in direction. */ void -append_cell_to_circuit_queue(circuit_t *circ, or_connection_t *orconn, +append_cell_to_circuit_queue(circuit_t *circ, channel_t *chan, cell_t *cell, cell_direction_t direction, streamid_t fromstream) { @@ -2524,12 +2576,12 @@ append_cell_to_circuit_queue(circuit_t *circ, or_connection_t *orconn, return; if (direction == CELL_DIRECTION_OUT) { - queue = &circ->n_conn_cells; - streams_blocked = circ->streams_blocked_on_n_conn; + queue = &circ->n_chan_cells; + streams_blocked = circ->streams_blocked_on_n_chan; } else { or_circuit_t *orcirc = TO_OR_CIRCUIT(circ); - queue = &orcirc->p_conn_cells; - streams_blocked = circ->streams_blocked_on_p_conn; + queue = &orcirc->p_chan_cells; + streams_blocked = circ->streams_blocked_on_p_chan; } cell_queue_append_packed_copy(queue, cell); @@ -2537,27 +2589,27 @@ append_cell_to_circuit_queue(circuit_t *circ, or_connection_t *orconn, /* If we have too many cells on the circuit, we should stop reading from * the edge streams for a while. */ if (!streams_blocked && queue->n >= CELL_QUEUE_HIGHWATER_SIZE) - set_streams_blocked_on_circ(circ, orconn, 1, 0); /* block streams */ + set_streams_blocked_on_circ(circ, chan, 1, 0); /* block streams */ if (streams_blocked && fromstream) { /* This edge connection is apparently not blocked; block it. */ - set_streams_blocked_on_circ(circ, orconn, 1, fromstream); + set_streams_blocked_on_circ(circ, chan, 1, fromstream); } if (queue->n == 1) { /* This was the first cell added to the queue. We need to make this * circuit active. */ log_debug(LD_GENERAL, "Made a circuit active."); - make_circuit_active_on_conn(circ, orconn); + make_circuit_active_on_chan(circ, chan); } - if (! connection_get_outbuf_len(TO_CONN(orconn))) { + 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."); - connection_or_flush_from_first_active_circuit(orconn, 1, approx_time()); + channel_flush_from_first_active_circuit(chan, 1); } } @@ -2621,21 +2673,21 @@ decode_address_from_payload(tor_addr_t *addr_out, const uint8_t *payload, return payload + 2 + payload[1]; } -/** Remove all the cells queued on circ for orconn. */ +/** Remove all the cells queued on circ for chan. */ void -circuit_clear_cell_queue(circuit_t *circ, or_connection_t *orconn) +circuit_clear_cell_queue(circuit_t *circ, channel_t *chan) { cell_queue_t *queue; - if (circ->n_conn == orconn) { - queue = &circ->n_conn_cells; + if (circ->n_chan == chan) { + queue = &circ->n_chan_cells; } else { or_circuit_t *orcirc = TO_OR_CIRCUIT(circ); - tor_assert(orcirc->p_conn == orconn); - queue = &orcirc->p_conn_cells; + tor_assert(orcirc->p_chan == chan); + queue = &orcirc->p_chan_cells; } if (queue->n) - make_circuit_inactive_on_conn(circ,orconn); + make_circuit_inactive_on_chan(circ, chan); cell_queue_clear(queue); } @@ -2643,36 +2695,41 @@ circuit_clear_cell_queue(circuit_t *circ, or_connection_t *orconn) /** Fail with an assert if the active circuits ring on orconn is * corrupt. */ void -assert_active_circuits_ok(or_connection_t *orconn) +assert_active_circuits_ok(channel_t *chan) { - circuit_t *head = orconn->active_circuits; - circuit_t *cur = head; + circuit_t *head = NULL, *cur = NULL; int n = 0; + + tor_assert(chan); + tor_assert(!(chan->is_listener)); + + cur = head = chan->u.cell_chan.active_circuits; + if (! head) return; do { - circuit_t *next = *next_circ_on_conn_p(cur, orconn); - circuit_t *prev = *prev_circ_on_conn_p(cur, orconn); + circuit_t *next = *next_circ_on_chan_p(cur, chan); + circuit_t *prev = *prev_circ_on_chan_p(cur, chan); cell_ewma_t *ewma; tor_assert(next); tor_assert(prev); - tor_assert(*next_circ_on_conn_p(prev, orconn) == cur); - tor_assert(*prev_circ_on_conn_p(next, orconn) == cur); - if (orconn == cur->n_conn) { + tor_assert(*next_circ_on_chan_p(prev, chan) == cur); + tor_assert(*prev_circ_on_chan_p(next, chan) == cur); + if (chan == cur->n_chan) { ewma = &cur->n_cell_ewma; - tor_assert(!ewma->is_for_p_conn); + tor_assert(!ewma->is_for_p_chan); } else { ewma = &TO_OR_CIRCUIT(cur)->p_cell_ewma; - tor_assert(ewma->is_for_p_conn); + tor_assert(ewma->is_for_p_chan); } tor_assert(ewma->heap_index != -1); - tor_assert(ewma == smartlist_get(orconn->active_circuit_pqueue, + tor_assert(ewma == smartlist_get(chan->u.cell_chan.active_circuit_pqueue, ewma->heap_index)); n++; cur = next; } while (cur != head); - tor_assert(n == smartlist_len(orconn->active_circuit_pqueue)); + tor_assert(n == smartlist_len(chan->u.cell_chan.active_circuit_pqueue)); } /** Return 1 if we shouldn't restart reading on this circuit, even if @@ -2682,9 +2739,9 @@ static int circuit_queue_streams_are_blocked(circuit_t *circ) { if (CIRCUIT_IS_ORIGIN(circ)) { - return circ->streams_blocked_on_n_conn; + return circ->streams_blocked_on_n_chan; } else { - return circ->streams_blocked_on_p_conn; + return circ->streams_blocked_on_p_chan; } } diff --git a/src/or/relay.h b/src/or/relay.h index a41465521a..7f96d59d15 100644 --- a/src/or/relay.h +++ b/src/or/relay.h @@ -41,6 +41,9 @@ void free_cell_pool(void); void clean_cell_pool(void); void dump_cell_pool_usage(int severity); +/* For channeltls.c */ +void packed_cell_free(packed_cell_t *cell); + void cell_queue_clear(cell_queue_t *queue); void cell_queue_append(cell_queue_t *queue, packed_cell_t *cell); void cell_queue_append_packed_copy(cell_queue_t *queue, const cell_t *cell); @@ -49,9 +52,8 @@ void append_cell_to_circuit_queue(circuit_t *circ, channel_t *chan, cell_t *cell, cell_direction_t direction, streamid_t fromstream); void channel_unlink_all_active_circs(channel_t *chan); -int channel_flush_from_first_active_circuit(channel_t *conn, - int max, time_t now); -void assert_active_circuits_ok(or_connection_t *orconn); +int channel_flush_from_first_active_circuit(channel_t *chan, int max); +void assert_active_circuits_ok(channel_t *chan); void make_circuit_inactive_on_chan(circuit_t *circ, channel_t *chan); void make_circuit_active_on_chan(circuit_t *circ, channel_t *chan); -- cgit v1.2.3-54-g00ecf From 35924435d22c2469ecbe06156c8069a928859d63 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Sat, 25 Aug 2012 15:27:50 -0700 Subject: Make reachabiity test in dirserv.c use channel_t --- src/or/dirserv.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/or/dirserv.c b/src/or/dirserv.c index 0d98324bfd..1480cb60dd 100644 --- a/src/or/dirserv.c +++ b/src/or/dirserv.c @@ -8,6 +8,8 @@ #include "buffers.h" #include "config.h" #include "confparse.h" +#include "channel.h" +#include "channeltls.h" #include "connection.h" #include "connection_or.h" #include "control.h" @@ -3410,8 +3412,8 @@ dirserv_single_reachability_test(time_t now, routerinfo_t *router) log_debug(LD_OR,"Testing reachability of %s at %s:%u.", router->nickname, router->address, router->or_port); tor_addr_from_ipv4h(&router_addr, router->addr); - connection_or_connect(&router_addr, router->or_port, - router->cache_info.identity_digest); + channel_tls_connect(&router_addr, router->or_port, + router->cache_info.identity_digest); /* Possible IPv6. */ if (get_options()->AuthDirHasIPv6Connectivity == 1 && @@ -3421,8 +3423,8 @@ dirserv_single_reachability_test(time_t now, routerinfo_t *router) router->nickname, tor_addr_to_str(addrstr, &router->ipv6_addr, sizeof(addrstr), 1), router->ipv6_orport); - connection_or_connect(&router->ipv6_addr, router->ipv6_orport, - router->cache_info.identity_digest); + channel_tls_connect(&router->ipv6_addr, router->ipv6_orport, + router->cache_info.identity_digest); } } -- cgit v1.2.3-54-g00ecf From 6cce6241dd4405f6cf21057f9913e07633cf18bb Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Thu, 30 Aug 2012 15:27:55 -0700 Subject: Query circuit count from associated channel of or_conn in control.c --- src/or/control.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/or/control.c b/src/or/control.c index 74c6acc000..5c05fe159d 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -12,6 +12,8 @@ #include "or.h" #include "buffers.h" +#include "channel.h" +#include "channeltls.h" #include "circuitbuild.h" #include "circuitlist.h" #include "circuituse.h" @@ -3822,7 +3824,11 @@ control_event_or_conn_status(or_connection_t *conn, or_conn_status_event_t tp, log_warn(LD_BUG, "Unrecognized status code %d", (int)tp); return 0; } - ncircs = circuit_count_pending_on_or_conn(conn); + if (conn->chan) { + ncircs = circuit_count_pending_on_channel(TLS_CHAN_TO_BASE(conn->chan)); + } else { + ncircs = 0; + } ncircs += conn->n_circuits; if (ncircs && (tp == OR_CONN_EVENT_FAILED || tp == OR_CONN_EVENT_CLOSED)) { tor_snprintf(ncircs_buf, sizeof(ncircs_buf), "%sNCIRCS=%d", -- cgit v1.2.3-54-g00ecf From 77dac97354974e8a819d8e35ad4e7a76199999b4 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Thu, 30 Aug 2012 15:47:05 -0700 Subject: Use channel_t in cpuworker.c Note: this is a squashed commit; see branch bug6465_rebased_v2 of user/andrea/tor.git for full history of the following 2 commits: Use channel_t in cpuworker.c Fix bug in channel_t usage in cpuworker.c that was killing relaying on channel_t-ized Tor. The tags passed to the worker now have a channel ID, not a connection ID. --- src/or/cpuworker.c | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/or/cpuworker.c b/src/or/cpuworker.c index 0255227e7b..a78518fd02 100644 --- a/src/or/cpuworker.c +++ b/src/or/cpuworker.c @@ -14,6 +14,8 @@ #include "or.h" #include "buffers.h" +#include "channel.h" +#include "channeltls.h" #include "circuitbuild.h" #include "circuitlist.h" #include "config.h" @@ -68,19 +70,20 @@ connection_cpu_finished_flushing(connection_t *conn) /** Pack global_id and circ_id; set *tag to the result. (See note on * cpuworker_main for wire format.) */ static void -tag_pack(char *tag, uint64_t conn_id, circid_t circ_id) +tag_pack(char *tag, uint64_t chan_id, circid_t circ_id) { /*XXXX RETHINK THIS WHOLE MESS !!!! !NM NM NM NM*/ - set_uint64(tag, conn_id); + /*XXXX DOUBLEPLUSTHIS!!!! AS AS AS AS*/ + set_uint64(tag, chan_id); set_uint16(tag+8, circ_id); } /** Unpack tag into addr, port, and circ_id. */ static void -tag_unpack(const char *tag, uint64_t *conn_id, circid_t *circ_id) +tag_unpack(const char *tag, uint64_t *chan_id, circid_t *circ_id) { - *conn_id = get_uint64(tag); + *chan_id = get_uint64(tag); *circ_id = get_uint16(tag+8); } @@ -131,10 +134,9 @@ connection_cpu_process_inbuf(connection_t *conn) { char success; char buf[LEN_ONION_RESPONSE]; - uint64_t conn_id; + uint64_t chan_id; circid_t circ_id; - connection_t *tmp_conn; - or_connection_t *p_conn = NULL; + channel_t *p_chan = NULL; circuit_t *circ; tor_assert(conn); @@ -152,15 +154,15 @@ connection_cpu_process_inbuf(connection_t *conn) connection_fetch_from_buf(buf,LEN_ONION_RESPONSE-1,conn); /* parse out the circ it was talking about */ - tag_unpack(buf, &conn_id, &circ_id); + tag_unpack(buf, &chan_id, &circ_id); circ = NULL; - tmp_conn = connection_get_by_global_id(conn_id); - if (tmp_conn && !tmp_conn->marked_for_close && - tmp_conn->type == CONN_TYPE_OR) - p_conn = TO_OR_CONN(tmp_conn); + log_debug(LD_OR, + "Unpacking cpuworker reply, chan_id is %lu, circ_id is %d", + chan_id, circ_id); + p_chan = channel_find_by_global_id(chan_id); - if (p_conn) - circ = circuit_get_by_circid_orconn(circ_id, p_conn); + if (p_chan) + circ = circuit_get_by_circid_channel(circ_id, p_chan); if (success == 0) { log_debug(LD_OR, @@ -475,12 +477,12 @@ assign_onionskin_to_cpuworker(connection_t *cpuworker, tor_assert(cpuworker); - if (!circ->p_conn) { - log_info(LD_OR,"circ->p_conn gone. Failing circ."); + if (!circ->p_chan) { + log_info(LD_OR,"circ->p_chan gone. Failing circ."); tor_free(onionskin); return -1; } - tag_pack(tag, circ->p_conn->_base.global_identifier, + tag_pack(tag, circ->p_chan->global_identifier, circ->p_circ_id); cpuworker->state = CPUWORKER_STATE_BUSY_ONION; -- cgit v1.2.3-54-g00ecf From 8b14db9628f0e8982e894034e86c8efdd78cff32 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Sun, 2 Sep 2012 21:13:25 -0700 Subject: Switch onion.c over to channel_t --- src/or/onion.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/or/onion.c b/src/or/onion.c index ff1556c3e1..17d8e777ad 100644 --- a/src/or/onion.c +++ b/src/or/onion.c @@ -106,7 +106,7 @@ onion_next_task(char **onionskin_out) return NULL; /* no onions pending, we're done */ tor_assert(ol_list->circ); - tor_assert(ol_list->circ->p_conn); /* make sure it's still valid */ + tor_assert(ol_list->circ->p_chan); /* make sure it's still valid */ tor_assert(ol_length > 0); circ = ol_list->circ; *onionskin_out = ol_list->onionskin; -- cgit v1.2.3-54-g00ecf From 28f108bcceab59fcf9f27e33065f64bfdb0f159a Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Tue, 4 Sep 2012 00:33:16 -0700 Subject: Use dirreq_id from channel_t when appropriate --- src/or/channel.h | 5 ----- src/or/connection_edge.c | 4 ++-- src/or/directory.c | 8 ++++---- src/or/geoip.c | 6 +++--- 4 files changed, 9 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/or/channel.h b/src/or/channel.h index 242a078532..18d9a81209 100644 --- a/src/or/channel.h +++ b/src/or/channel.h @@ -38,11 +38,6 @@ struct channel_s { */ unsigned int is_listener:1; - /** Unique ID for measuring direct network status requests;vtunneled ones - * come over a circuit_t, which has a dirreq_id field as well, but is a - * distinct namespace. */ - uint64_t dirreq_id; - /** Why did we close? */ enum { diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c index 9bcfca30de..834f9707ce 100644 --- a/src/or/connection_edge.c +++ b/src/or/connection_edge.c @@ -3124,7 +3124,7 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ) /* Remember the tunneled request ID in the new edge connection, so that * we can measure download times. */ - TO_CONN(n_stream)->dirreq_id = circ->dirreq_id; + n_stream->dirreq_id = circ->dirreq_id; n_stream->_base.purpose = EXIT_PURPOSE_CONNECT; @@ -3366,7 +3366,7 @@ connection_exit_connect_dir(edge_connection_t *exitconn) /* Note that the new dir conn belongs to the same tunneled request as * the edge conn, so that we can measure download times. */ - TO_CONN(dirconn)->dirreq_id = TO_CONN(exitconn)->dirreq_id; + dirconn->dirreq_id = exitconn->dirreq_id; connection_link_connections(TO_CONN(dirconn), TO_CONN(exitconn)); diff --git a/src/or/directory.c b/src/or/directory.c index 7df91fb57e..8003444c6f 100644 --- a/src/or/directory.c +++ b/src/or/directory.c @@ -2855,8 +2855,8 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers, geoip_note_ns_response(act, GEOIP_SUCCESS); /* Note that a request for a network status has started, so that we * can measure the download time later on. */ - if (TO_CONN(conn)->dirreq_id) - geoip_start_dirreq(TO_CONN(conn)->dirreq_id, dlen, act, + if (conn->dirreq_id) + geoip_start_dirreq(conn->dirreq_id, dlen, act, DIRREQ_TUNNELED); else geoip_start_dirreq(TO_CONN(conn)->global_identifier, dlen, act, @@ -3529,8 +3529,8 @@ connection_dir_finished_flushing(dir_connection_t *conn) /* Note that we have finished writing the directory response. For direct * connections this means we're done, for tunneled connections its only * an intermediate step. */ - if (TO_CONN(conn)->dirreq_id) - geoip_change_dirreq_state(TO_CONN(conn)->dirreq_id, DIRREQ_TUNNELED, + if (conn->dirreq_id) + geoip_change_dirreq_state(conn->dirreq_id, DIRREQ_TUNNELED, DIRREQ_FLUSHING_DIR_CONN_FINISHED); else geoip_change_dirreq_state(TO_CONN(conn)->global_identifier, diff --git a/src/or/geoip.c b/src/or/geoip.c index 6b7cc82b82..a995f8b802 100644 --- a/src/or/geoip.c +++ b/src/or/geoip.c @@ -578,7 +578,7 @@ _c_hist_compare(const void **_a, const void **_b) * failed, the others as still running. */ #define DIRREQ_TIMEOUT (10*60) -/** Entry in a map from either conn->global_identifier for direct requests +/** Entry in a map from either chan->global_identifier for direct requests * or a unique circuit identifier for tunneled requests to request time, * response size, and completion time of a network status request. Used to * measure download times of requests to derive average client @@ -586,7 +586,7 @@ _c_hist_compare(const void **_a, const void **_b) typedef struct dirreq_map_entry_t { HT_ENTRY(dirreq_map_entry_t) node; /** Unique identifier for this network status request; this is either the - * conn->global_identifier of the dir conn (direct request) or a new + * chan->global_identifier of the dir channel (direct request) or a new * locally unique identifier of a circuit (tunneled request). This ID is * only unique among other direct or tunneled requests, respectively. */ uint64_t dirreq_id; @@ -705,7 +705,7 @@ geoip_change_dirreq_state(uint64_t dirreq_id, dirreq_type_t type, if ((type == DIRREQ_DIRECT && new_state == DIRREQ_FLUSHING_DIR_CONN_FINISHED) || (type == DIRREQ_TUNNELED && - new_state == DIRREQ_OR_CONN_BUFFER_FLUSHED)) { + new_state == DIRREQ_CHANNEL_BUFFER_FLUSHED)) { tor_gettimeofday(&ent->completion_time); ent->completed = 1; } -- cgit v1.2.3-54-g00ecf From f0f87cb68a22feaf552a18b521d3313d843f8793 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Tue, 4 Sep 2012 01:44:19 -0700 Subject: Convert rendmid.c to channel_t --- src/or/rendmid.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/or/rendmid.c b/src/or/rendmid.c index bacd0ef93e..00bbd28fa6 100644 --- a/src/or/rendmid.c +++ b/src/or/rendmid.c @@ -35,7 +35,7 @@ rend_mid_establish_intro(or_circuit_t *circ, const uint8_t *request, "Received an ESTABLISH_INTRO request on circuit %d", circ->p_circ_id); - if (circ->_base.purpose != CIRCUIT_PURPOSE_OR || circ->_base.n_conn) { + if (circ->_base.purpose != CIRCUIT_PURPOSE_OR || circ->_base.n_chan) { log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Rejecting ESTABLISH_INTRO on non-OR or non-edge circuit."); reason = END_CIRC_REASON_TORPROTOCOL; @@ -142,7 +142,7 @@ rend_mid_introduce(or_circuit_t *circ, const uint8_t *request, log_info(LD_REND, "Received an INTRODUCE1 request on circuit %d", circ->p_circ_id); - if (circ->_base.purpose != CIRCUIT_PURPOSE_OR || circ->_base.n_conn) { + if (circ->_base.purpose != CIRCUIT_PURPOSE_OR || circ->_base.n_chan) { log_warn(LD_PROTOCOL, "Rejecting INTRODUCE1 on non-OR or non-edge circuit %d.", circ->p_circ_id); @@ -224,7 +224,7 @@ rend_mid_establish_rendezvous(or_circuit_t *circ, const uint8_t *request, log_info(LD_REND, "Received an ESTABLISH_RENDEZVOUS request on circuit %d", circ->p_circ_id); - if (circ->_base.purpose != CIRCUIT_PURPOSE_OR || circ->_base.n_conn) { + if (circ->_base.purpose != CIRCUIT_PURPOSE_OR || circ->_base.n_chan) { log_warn(LD_PROTOCOL, "Tried to establish rendezvous on non-OR or non-edge circuit."); goto err; @@ -277,7 +277,7 @@ rend_mid_rendezvous(or_circuit_t *circ, const uint8_t *request, char hexid[9]; int reason = END_CIRC_REASON_INTERNAL; - if (circ->_base.purpose != CIRCUIT_PURPOSE_OR || circ->_base.n_conn) { + if (circ->_base.purpose != CIRCUIT_PURPOSE_OR || circ->_base.n_chan) { log_info(LD_REND, "Tried to complete rendezvous on non-OR or non-edge circuit %d.", circ->p_circ_id); -- cgit v1.2.3-54-g00ecf From 9ad7ba9f2267a9ee34fafda9356f1fa86633f00f Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Fri, 7 Sep 2012 04:40:42 -0700 Subject: Use connection_or_get_num_circuits() in control.c --- src/or/control.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/or/control.c b/src/or/control.c index 5c05fe159d..75a8f5939a 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -3829,7 +3829,7 @@ control_event_or_conn_status(or_connection_t *conn, or_conn_status_event_t tp, } else { ncircs = 0; } - ncircs += conn->n_circuits; + ncircs += connection_or_get_num_circuits(conn); if (ncircs && (tp == OR_CONN_EVENT_FAILED || tp == OR_CONN_EVENT_CLOSED)) { tor_snprintf(ncircs_buf, sizeof(ncircs_buf), "%sNCIRCS=%d", reason ? " " : "", ncircs); -- cgit v1.2.3-54-g00ecf From cb62a0b69a7d67b427224ca4c3075b49853a3a1f Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Fri, 7 Sep 2012 04:42:13 -0700 Subject: Use channel_is_bad_for_new_circs(), connection_or_get_num_circs() in main.c --- src/or/main.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/or/main.c b/src/or/main.c index a1b1502e4f..f53bcf3574 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -13,6 +13,8 @@ #define MAIN_PRIVATE #include "or.h" #include "buffers.h" +#include "channel.h" +#include "channeltls.h" #include "circuitbuild.h" #include "circuitlist.h" #include "circuituse.h" @@ -1046,7 +1048,8 @@ run_connection_housekeeping(int i, time_t now) tor_assert(conn->outbuf); #endif - if (or_conn->is_bad_for_new_circs && !or_conn->n_circuits) { + if (channel_is_bad_for_new_circs(TLS_CHAN_TO_BASE(or_conn->chan)) && + !connection_or_get_num_circuits(or_conn)) { /* It's bad for new circuits, and has no unmarked circuits on it: * mark it now. */ log_info(LD_OR, @@ -1064,14 +1067,15 @@ run_connection_housekeeping(int i, time_t now) (int)conn->s,conn->address, conn->port); connection_mark_for_close(conn); } - } else if (we_are_hibernating() && !or_conn->n_circuits && + } else if (we_are_hibernating() && + !connection_or_get_num_circuits(or_conn) && !connection_get_outbuf_len(conn)) { /* We're hibernating, there's no circuits, and nothing to flush.*/ log_info(LD_OR,"Expiring non-used OR connection to fd %d (%s:%d) " "[Hibernating or exiting].", (int)conn->s,conn->address, conn->port); connection_mark_and_flush(conn); - } else if (!or_conn->n_circuits && + } else if (!connection_or_get_num_circuits(or_conn) && now >= or_conn->timestamp_last_added_nonpadding + IDLE_OR_CONN_TIMEOUT) { log_info(LD_OR,"Expiring non-used OR connection to fd %d (%s:%d) " -- cgit v1.2.3-54-g00ecf From f06880c855a6ac4dd824bed7efcd0bd0436bda32 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Fri, 7 Sep 2012 14:38:16 -0700 Subject: Add LD_CHANNEL log domain in log.c --- src/common/log.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/common/log.c b/src/common/log.c index 5e2e6b5b50..ef2e69d5ac 100644 --- a/src/common/log.c +++ b/src/common/log.c @@ -907,7 +907,7 @@ log_level_to_string(int level) static const char *domain_list[] = { "GENERAL", "CRYPTO", "NET", "CONFIG", "FS", "PROTOCOL", "MM", "HTTP", "APP", "CONTROL", "CIRC", "REND", "BUG", "DIR", "DIRSERV", - "OR", "EDGE", "ACCT", "HIST", "HANDSHAKE", "HEARTBEAT", NULL + "OR", "EDGE", "ACCT", "HIST", "HANDSHAKE", "HEARTBEAT", "CHANNEL", NULL }; /** Return a bitmask for the log domain for which domain is the name, -- cgit v1.2.3-54-g00ecf From 07f9e8fc7d5de57b878118a5179c26eee8466a39 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Sat, 8 Sep 2012 18:58:03 -0700 Subject: Call channel_run_cleanup() in main.c, and include a comment explaining how closing or_connections related to channels --- src/or/main.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'src') diff --git a/src/or/main.c b/src/or/main.c index f53bcf3574..491c48ea7c 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -400,6 +400,18 @@ connection_unlink(connection_t *conn) if (conn->type == CONN_TYPE_OR) { if (!tor_digest_is_zero(TO_OR_CONN(conn)->identity_digest)) connection_or_remove_from_identity_map(TO_OR_CONN(conn)); + /* connection_unlink() can only get called if the connection + * was already on the closeable list, and it got there by + * connection_mark_for_close(), which was called from + * connection_or_close_normally() or + * connection_or_close_for_error(), so the channel should + * already be in CHANNEL_STATE_CLOSING, and then the + * connection_about_to_close_connection() goes to + * connection_or_about_to_close(), which calls channel_closed() + * to notify the channel_t layer, and closed the channel, so + * nothing more to do here to deal with the channel associated + * with an orconn. + */ } connection_free(conn); } @@ -1525,6 +1537,9 @@ run_scheduled_events(time_t now) * flush it. */ or_state_save(now); + /** 8c. Do channel cleanup just like for connections */ + channel_run_cleanup(); + /** 9. and if we're a server, check whether our DNS is telling stories to * us. */ if (!net_is_disabled() && -- cgit v1.2.3-54-g00ecf From 72251385b07a4d6ed2f5d7d972d57a8deb899c4d Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Mon, 10 Sep 2012 01:31:08 -0700 Subject: Call connection_or_close_normally() rather than using connection_mark_for_close()/connection_mark_and_flush() in run_connection_housekeeping() of main.c so that channels get sent to the CLOSING state correctly (avoids an assert otherwise) --- src/or/main.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/or/main.c b/src/or/main.c index 491c48ea7c..b87cf52edb 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -1071,13 +1071,13 @@ run_connection_housekeeping(int i, time_t now) connection_or_connect_failed(TO_OR_CONN(conn), END_OR_CONN_REASON_TIMEOUT, "Tor gave up on the connection"); - connection_mark_and_flush(conn); + connection_or_close_normally(TO_OR_CONN(conn), 1); } else if (!connection_state_is_open(conn)) { if (past_keepalive) { /* We never managed to actually get this connection open and happy. */ log_info(LD_OR,"Expiring non-open OR connection to fd %d (%s:%d).", (int)conn->s,conn->address, conn->port); - connection_mark_for_close(conn); + connection_or_close_normally(TO_OR_CONN(conn), 0); } } else if (we_are_hibernating() && !connection_or_get_num_circuits(or_conn) && @@ -1086,14 +1086,14 @@ run_connection_housekeeping(int i, time_t now) log_info(LD_OR,"Expiring non-used OR connection to fd %d (%s:%d) " "[Hibernating or exiting].", (int)conn->s,conn->address, conn->port); - connection_mark_and_flush(conn); + connection_or_close_normally(TO_OR_CONN(conn), 1); } else if (!connection_or_get_num_circuits(or_conn) && now >= or_conn->timestamp_last_added_nonpadding + IDLE_OR_CONN_TIMEOUT) { log_info(LD_OR,"Expiring non-used OR connection to fd %d (%s:%d) " "[idle %d].", (int)conn->s,conn->address, conn->port, (int)(now - or_conn->timestamp_last_added_nonpadding)); - connection_mark_for_close(conn); + connection_or_close_normally(TO_OR_CONN(conn), 0); } else if ( now >= or_conn->timestamp_lastempty + options->KeepalivePeriod*10 && now >= conn->timestamp_lastwritten + options->KeepalivePeriod*10) { @@ -1103,7 +1103,7 @@ run_connection_housekeeping(int i, time_t now) (int)conn->s, conn->address, conn->port, (int)connection_get_outbuf_len(conn), (int)(now-conn->timestamp_lastwritten)); - connection_mark_for_close(conn); + connection_or_close_normally(TO_OR_CONN(conn), 0); } else if (past_keepalive && !connection_get_outbuf_len(conn)) { /* send a padding cell */ log_fn(LOG_DEBUG,LD_OR,"Sending keepalive to (%s:%d)", -- cgit v1.2.3-54-g00ecf From a9a75ee59a719f938b02d48c6df3db649cf32cb9 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Wed, 12 Sep 2012 16:31:08 -0700 Subject: Call channel_tls_free_all() and channel_free_all() from tor_free_all in main.c --- src/or/main.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/or/main.c b/src/or/main.c index b87cf52edb..908e490741 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -2476,6 +2476,8 @@ tor_free_all(int postfork) circuit_free_all(); entry_guards_free_all(); pt_free_all(); + channel_tls_free_all(); + channel_free_all(); connection_free_all(); buf_shrink_freelists(1); memarea_clear_freelist(); -- cgit v1.2.3-54-g00ecf From 7138a4adac9592edbb73d3983cc51db153c76edf Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Thu, 13 Sep 2012 02:44:21 -0700 Subject: Keep better statistics about channels and dump them from dumpstats() on SIGUSR1 --- src/or/channel.c | 400 +++++++++++++++++++++++++++++++++++++++++++++++++++- src/or/channel.h | 33 ++++- src/or/channeltls.c | 43 ++++++ src/or/main.c | 4 +- 4 files changed, 472 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/or/channel.c b/src/or/channel.c index 0268fceebd..cf08e3b435 100644 --- a/src/or/channel.c +++ b/src/or/channel.c @@ -1488,6 +1488,8 @@ channel_write_cell(channel_t *chan, cell_t *cell) channel_timestamp_xmit(chan); /* If we're here the queue is empty, so it's drained too */ channel_timestamp_drained(chan); + /* Update the counter */ + ++(chan->u.cell_chan.n_cells_xmitted); } } @@ -1546,6 +1548,8 @@ channel_write_packed_cell(channel_t *chan, packed_cell_t *packed_cell) channel_timestamp_xmit(chan); /* If we're here the queue is empty, so it's drained too */ channel_timestamp_drained(chan); + /* Update the counter */ + ++(chan->u.cell_chan.n_cells_xmitted); } } @@ -1609,6 +1613,8 @@ channel_write_var_cell(channel_t *chan, var_cell_t *var_cell) channel_timestamp_xmit(chan); /* If we're here the queue is empty, so it's drained too */ channel_timestamp_drained(chan); + /* Update the counter */ + ++(chan->u.cell_chan.n_cells_xmitted); } } @@ -1883,6 +1889,7 @@ channel_flush_some_cells_from_outgoing_queue(channel_t *chan, tor_free(q); ++flushed; channel_timestamp_xmit(chan); + ++(chan->u.cell_chan.n_cells_xmitted); } /* Else couldn't write it; leave it on the queue */ } else { @@ -1902,6 +1909,7 @@ channel_flush_some_cells_from_outgoing_queue(channel_t *chan, tor_free(q); ++flushed; channel_timestamp_xmit(chan); + ++(chan->u.cell_chan.n_cells_xmitted); } /* Else couldn't write it; leave it on the queue */ } else { @@ -1921,6 +1929,7 @@ channel_flush_some_cells_from_outgoing_queue(channel_t *chan, tor_free(q); ++flushed; channel_timestamp_xmit(chan); + ++(chan->u.cell_chan.n_cells_xmitted); } /* Else couldn't write it; leave it on the queue */ } else { @@ -2179,6 +2188,11 @@ channel_queue_incoming(channel_t *listener, channel_t *incoming) listener->u.listener.incoming_list = smartlist_new(); } + /* Bump the counter and timestamp it */ + channel_timestamp_active(listener); + channel_timestamp_accepted(listener); + ++(listener->u.listener.n_accepted); + /* If we don't need to queue, process it right away */ if (!need_to_queue) { tor_assert(listener->u.listener.listener); @@ -2301,6 +2315,9 @@ channel_queue_cell(channel_t *chan, cell_t *cell) /* Timestamp for receiving */ channel_timestamp_recv(chan); + /* Update the counter */ + ++(chan->u.cell_chan.n_cells_recved); + /* If we don't need to queue we can just call cell_handler */ if (!need_to_queue) { tor_assert(chan->u.cell_chan.cell_handler); @@ -2360,6 +2377,9 @@ channel_queue_var_cell(channel_t *chan, var_cell_t *var_cell) /* Timestamp for receiving */ channel_timestamp_recv(chan); + /* Update the counter */ + ++(chan->u.cell_chan.n_cells_recved); + /* If we don't need to queue we can just call cell_handler */ if (!need_to_queue) { tor_assert(chan->u.cell_chan.var_cell_handler); @@ -2416,6 +2436,41 @@ channel_send_destroy(circid_t circ_id, channel_t *chan, int reason) return 0; } +/** + * Channel statistics + * + * This is called from dumpstats() in main.c and spams the log with + * statistics on channels. + */ + +void +channel_dumpstats(int severity) +{ + if (all_channels && smartlist_len(all_channels) > 0) { + log(severity, LD_GENERAL, + "Dumping statistics about %d channels:", + smartlist_len(all_channels)); + log(severity, LD_GENERAL, + "%d are active, %d are listeners, and %d are done and " + "waiting for cleanup", + (active_channels != NULL) ? + smartlist_len(active_channels) : 0, + (listening_channels != NULL) ? + smartlist_len(listening_channels) : 0, + (finished_channels != NULL) ? + smartlist_len(finished_channels) : 0); + + SMARTLIST_FOREACH(all_channels, channel_t *, chan, + channel_dump_statistics(chan, severity)); + + log(severity, LD_GENERAL, + "Done spamming about channels now"); + } else { + log(severity, LD_GENERAL, + "No channels to dump"); + } +} + /** * Channel cleanup * @@ -2784,6 +2839,265 @@ channel_get_for_extend(const char *digest, } } +/** + * Describe the transport subclass + * + * Invoke a method to get a string description of the lower-layer + * transport for this channel. + */ + +const char * +channel_describe_transport(channel_t *chan) +{ + tor_assert(chan); + tor_assert(chan->describe_transport); + + return chan->describe_transport(chan); +} + +/** + * Dump channel statistics + * + * Dump statistics for one channel to the log + */ + +void +channel_dump_statistics(channel_t *chan, int severity) +{ + double avg, interval, age; + time_t now = time(NULL); + tor_addr_t remote_addr; + int have_remote_addr; + char *remote_addr_str; + + tor_assert(chan); + + age = (double)(now - chan->timestamp_created); + + log(severity, LD_GENERAL, + "Channel %lu (at %p) with transport %s is in state %s (%d) and %s", + chan->global_identifier, chan, + channel_describe_transport(chan), + channel_state_to_string(chan->state), chan->state, + chan->is_listener ? + "listens for incoming connections" : + "transports cells"); + log(severity, LD_GENERAL, + " * Channel %lu was created at %lu (%lu seconds ago) and last active " + "at %lu (%lu seconds ago)", + chan->global_identifier, + chan->timestamp_created, now - chan->timestamp_created, + chan->timestamp_active, now - chan->timestamp_active); + if (chan->is_listener) { + log(severity, LD_GENERAL, + " * Listener channel %lu last accepted an incoming channel at %lu " + "(%lu seconds ago) and has accepted %lu channels in total", + chan->global_identifier, + chan->u.listener.timestamp_accepted, + now - chan->u.listener.timestamp_accepted, + chan->u.listener.n_accepted); + + /* + * If it's sensible to do so, get the rate of incoming channels on this + * listener + */ + if (now > chan->timestamp_created && + chan->timestamp_created > 0 && + chan->u.listener.n_accepted > 0) { + avg = (double)(chan->u.listener.n_accepted) / age; + if (avg >= 1.0) { + log(severity, LD_GENERAL, + " * Listener channel %lu has averaged %f incoming channels per " + "second", + chan->global_identifier, avg); + } else if (avg >= 0.0) { + interval = 1.0 / avg; + log(severity, LD_GENERAL, + " * Listener channel %lu has averaged %f seconds between " + "incoming channels", + chan->global_identifier, interval); + } + } + } else { + /* Handle digest and nickname */ + if (!tor_digest_is_zero(chan->u.cell_chan.identity_digest)) { + if (chan->u.cell_chan.nickname) { + log(severity, LD_GENERAL, + " * Cell-bearing channel %lu says it is connected to an OR " + "with digest %s and nickname %s", + chan->global_identifier, + hex_str(chan->u.cell_chan.identity_digest, DIGEST_LEN), + chan->u.cell_chan.nickname); + } else { + log(severity, LD_GENERAL, + " * Cell-bearing channel %lu says it is connected to an OR " + "with digest %s and no known nickname", + chan->global_identifier, + hex_str(chan->u.cell_chan.identity_digest, DIGEST_LEN)); + } + } else { + if (chan->u.cell_chan.nickname) { + log(severity, LD_GENERAL, + " * Cell-bearing channel %lu does not know the digest of the " + "OR it is connected to, but reports its nickname is %s", + chan->global_identifier, + chan->u.cell_chan.nickname); + } else { + log(severity, LD_GENERAL, + " * Cell-bearing channel %lu does not know the digest or the " + "nickname of the OR it is connected to", + chan->global_identifier); + } + } + + /* Handle remote address and descriptions */ + have_remote_addr = channel_get_addr_if_possible(chan, &remote_addr); + if (have_remote_addr) { + remote_addr_str = tor_dup_addr(&remote_addr); + log(severity, LD_GENERAL, + " * Cell-bearing channel %lu says its remote address is %s, " + "and gives a canonical description of \"%s\" and an " + "actual description of \"%s\"", + chan->global_identifier, + remote_addr_str, + channel_get_canonical_remote_descr(chan), + channel_get_actual_remote_descr(chan)); + tor_free(remote_addr_str); + } else { + log(severity, LD_GENERAL, + " * Cell-bearing channel %lu does not know its remote address, " + "but gives a canonical description of \"%s\" and an " + "actual description of \"%s\"", + chan->global_identifier, + channel_get_canonical_remote_descr(chan), + channel_get_actual_remote_descr(chan)); + } + + /* Handle marks */ + log(severity, LD_GENERAL, + " * Cell-bearing channel %lu has these marks: %s %s %s " + "%s %s %s", + chan->global_identifier, + channel_is_bad_for_new_circs(chan) ? + "bad_for_new_circs" : "!bad_for_new_circs", + channel_is_canonical(chan) ? + "canonical" : "!canonical", + channel_is_canonical_is_reliable(chan) ? + "is_canonical_is_reliable" : + "!is_canonical_is_reliable", + channel_is_client(chan) ? + "client" : "!client", + channel_is_local(chan) ? + "local" : "!local", + channel_is_incoming(chan) ? + "incoming" : "outgoing"); + + /* Describe queues */ + log(severity, LD_GENERAL, + " * Cell-bearing channel %lu has %d queued incoming cells " + "and %d queued outgoing cells", + chan->global_identifier, + (chan->u.cell_chan.cell_queue != NULL) ? + smartlist_len(chan->u.cell_chan.cell_queue) : 0, + (chan->u.cell_chan.outgoing_queue != NULL) ? + smartlist_len(chan->u.cell_chan.outgoing_queue) : 0); + + /* Describe circuits */ + log(severity, LD_GENERAL, + " * Cell-bearing channel %lu has %d active circuits out of %d" + " in total", + chan->global_identifier, + (chan->u.cell_chan.active_circuit_pqueue != NULL) ? + smartlist_len(chan->u.cell_chan.active_circuit_pqueue) : 0, + chan->u.cell_chan.n_circuits); + /* TODO better circuit info once circuit structure refactor is finished */ + + /* Describe timestamps */ + log(severity, LD_GENERAL, + " * Cell-bearing channel %lu was last used by a client at %lu " + "(%lu seconds ago)", + chan->global_identifier, + chan->u.cell_chan.timestamp_client, + now - chan->u.cell_chan.timestamp_client); + log(severity, LD_GENERAL, + " * Cell-bearing channel %lu was last drained at %lu " + "(%lu seconds ago)", + chan->global_identifier, + chan->u.cell_chan.timestamp_drained, + now - chan->u.cell_chan.timestamp_drained); + log(severity, LD_GENERAL, + " * Cell-bearing channel %lu last received a cell at %lu " + "(%lu seconds ago)", + chan->global_identifier, + chan->u.cell_chan.timestamp_recv, + now - chan->u.cell_chan.timestamp_recv); + log(severity, LD_GENERAL, + " * Cell-bearing channel %lu last trasmitted a cell at %lu " + "(%lu seconds ago)", + chan->global_identifier, + chan->u.cell_chan.timestamp_xmit, + now - chan->u.cell_chan.timestamp_xmit); + + /* Describe counters and rates */ + log(severity, LD_GENERAL, + " * Cell-bearing channel %lu has received %lu cells and " + " transmitted %lu", + chan->global_identifier, + chan->u.cell_chan.n_cells_recved, + chan->u.cell_chan.n_cells_xmitted); + if (now > chan->timestamp_created && + chan->timestamp_created > 0) { + if (chan->u.cell_chan.n_cells_recved > 0) { + avg = (double)(chan->u.cell_chan.n_cells_recved) / age; + if (avg >= 1.0) { + log(severity, LD_GENERAL, + " * Cell-bearing channel %lu has averaged %f cells received " + "per second", + chan->global_identifier, avg); + } else if (avg >= 0.0) { + interval = 1.0 / avg; + log(severity, LD_GENERAL, + " * Cell-bearing channel %lu has averaged %f seconds between " + "received cells", + chan->global_identifier, interval); + } + } + if (chan->u.cell_chan.n_cells_xmitted > 0) { + avg = (double)(chan->u.cell_chan.n_cells_xmitted) / age; + if (avg >= 1.0) { + log(severity, LD_GENERAL, + " * Cell-bearing channel %lu has averaged %f cells transmitted " + "per second", + chan->global_identifier, avg); + } else if (avg >= 0.0) { + interval = 1.0 / avg; + log(severity, LD_GENERAL, + " * Cell-bearing channel %lu has averaged %f seconds between " + "transmitted cells", + chan->global_identifier, interval); + } + } + } + } + + /* Dump anything the lower layer has to say */ + channel_dump_transport_statistics(chan, severity); +} + +/** + * Invoke transport-specific stats dump + * + * If there is a lower-layer statistics dump method, invoke it + */ + +void +channel_dump_transport_statistics(channel_t *chan, int severity) +{ + tor_assert(chan); + + if (chan->dumpstats) chan->dumpstats(chan, severity); +} + /** * Return text description of the remote endpoint * @@ -3138,7 +3452,7 @@ channel_timestamp_created(channel_t *chan) /** * Update the last active timestamp. * - * This function updates the channe's last active timestamp; it should be + * This function updates the channel's last active timestamp; it should be * called by the lower layer whenever there is activity on the channel which * does not lead to a cell being transmitted or received; the active timestamp * is also updated from channel_timestamp_recv() and channel_timestamp_xmit(), @@ -3158,6 +3472,26 @@ channel_timestamp_active(channel_t *chan) chan->timestamp_active = now; } +/** + * Update the last accepted timestamp. + * + * This function updates the channel's last accepted timestamp; it should be + * called whenever a new incoming channel is accepted on a listener. + * + * @param chan Channel to update + */ + +void +channel_timestamp_accepted(channel_t *chan) +{ + time_t now = time(NULL); + + tor_assert(chan); + tor_assert(chan->is_listener); + + chan->u.listener.timestamp_accepted = now; +} + /** * Update client timestamp * @@ -3275,6 +3609,22 @@ channel_when_last_active(channel_t *chan) return chan->timestamp_active; } +/** + * Query last accepted timestamp + * + * @param chan Channel to query + * @return Last accepted timestamp value for chan + */ + +time_t +channel_when_last_accepted(channel_t *chan) +{ + tor_assert(chan); + tor_assert(chan->is_listener); + + return chan->u.listener.timestamp_accepted; +} + /** * Query client timestamp * @@ -3339,6 +3689,54 @@ channel_when_last_xmit(channel_t *chan) return chan->u.cell_chan.timestamp_xmit; } +/** + * Query accepted counter + * + * @param chan Channel to query + * @return Number of incoming channels accepted by this listener + */ + +uint64_t +channel_count_accepted(channel_t *chan) +{ + tor_assert(chan); + + if (chan->is_listener) return chan->u.listener.n_accepted; + else return 0; +} + +/** + * Query received cell counter + * + * @param chan Channel to query + * @return Number of cells received by this channel + */ + +uint64_t +channel_count_recved(channel_t *chan) +{ + tor_assert(chan); + + if (!(chan->is_listener)) return chan->u.cell_chan.n_cells_recved; + else return 0; +} + +/** + * Query transmitted cell counter + * + * @param chan Channel to query + * @return Number of cells transmitted by this channel + */ + +uint64_t +channel_count_xmitted(channel_t *chan) +{ + tor_assert(chan); + + if (!(chan->is_listener)) return chan->u.cell_chan.n_cells_xmitted; + else return 0; +} + /** * Check if a channel matches an extend_info_t * diff --git a/src/or/channel.h b/src/or/channel.h index 18d9a81209..ed69b9200e 100644 --- a/src/or/channel.h +++ b/src/or/channel.h @@ -57,6 +57,10 @@ struct channel_s { void (*free)(channel_t *); /* Close an open channel */ void (*close)(channel_t *); + /* Describe the transport subclass for this channel */ + const char * (*describe_transport)(channel_t *); + /* Optional method to dump transport-specific statistics on the channel */ + void (*dumpstats)(channel_t *, int); union { struct { @@ -65,6 +69,12 @@ struct channel_s { /* List of pending incoming connections */ smartlist_t *incoming_list; + + /* Timestamps for listeners */ + time_t timestamp_accepted; + + /* Counters for listeners */ + uint64_t n_accepted; } listener; struct { /* Registered handlers for incoming cells */ @@ -127,12 +137,6 @@ struct channel_s { /* List of queued outgoing cells */ smartlist_t *outgoing_queue; - /* - * When we last used this conn for any client traffic. If not - * recent, we can rate limit it further. - */ - time_t client_used; - /* Circuit stuff for use by relay.c */ /* @@ -210,6 +214,10 @@ struct channel_s { * come over a circuit_t, which has a dirreq_id field as well, but is a * distinct namespace. */ uint64_t dirreq_id; + + /** Channel counters for cell channels */ + uint64_t n_cells_recved; + uint64_t n_cells_xmitted; } cell_chan; } u; }; @@ -257,6 +265,9 @@ void channel_run_cleanup(void); /* Close all channels and deallocate everything */ void channel_free_all(void); +/* Dump some statistics in the log */ +void channel_dumpstats(int severity); + #ifdef _TOR_CHANNEL_INTERNAL /* Channel operations for subclasses and internal use only */ @@ -297,6 +308,7 @@ void channel_set_remote_end(channel_t *chan, /* Timestamp updates */ void channel_timestamp_created(channel_t *chan); +void channel_timestamp_accepted(channel_t *chan); void channel_timestamp_active(channel_t *chan); void channel_timestamp_drained(channel_t *chan); void channel_timestamp_recv(channel_t *chan); @@ -368,6 +380,9 @@ channel_t * channel_prev_with_digest(channel_t *chan); * Metadata queries/updates */ +const char * channel_describe_transport(channel_t *chan); +void channel_dump_statistics(channel_t *chan, int severity); +void channel_dump_transport_statistics(channel_t *chan, int severity); const char * channel_get_actual_remote_descr(channel_t *chan); int channel_get_addr_if_possible(channel_t *chan, tor_addr_t *addr_out); const char * channel_get_canonical_remote_descr(channel_t *chan); @@ -389,11 +404,17 @@ void channel_timestamp_client(channel_t *chan); /* Timestamp queries */ time_t channel_when_created(channel_t *chan); +time_t channel_when_last_accepted(channel_t *chan); time_t channel_when_last_active(channel_t *chan); time_t channel_when_last_client(channel_t *chan); time_t channel_when_last_drained(channel_t *chan); time_t channel_when_last_recv(channel_t *chan); time_t channel_when_last_xmit(channel_t *chan); +/* Counter queries */ +uint64_t channel_count_accepted(channel_t *chan); +uint64_t channel_count_recved(channel_t *chan); +uint64_t channel_count_xmitted(channel_t *chan); + #endif diff --git a/src/or/channeltls.c b/src/or/channeltls.c index 352037c14a..8a1b5ebc9f 100644 --- a/src/or/channeltls.c +++ b/src/or/channeltls.c @@ -47,6 +47,7 @@ channel_tls_t *channel_tls_listener = NULL; /* channel_tls_t method declarations */ static void channel_tls_close_method(channel_t *chan); +static const char * channel_tls_describe_transport_method(channel_t *chan); static int channel_tls_get_remote_addr_method(channel_t *chan, tor_addr_t *addr_out); static const char * @@ -104,6 +105,7 @@ channel_tls_connect(const tor_addr_t *addr, uint16_t port, channel_init_for_cells(chan); chan->state = CHANNEL_STATE_OPENING; chan->close = channel_tls_close_method; + chan->describe_transport = channel_tls_describe_transport_method; chan->u.cell_chan.get_remote_addr = channel_tls_get_remote_addr_method; chan->u.cell_chan.get_remote_descr = channel_tls_get_remote_descr_method; chan->u.cell_chan.has_queued_writes = channel_tls_has_queued_writes_method; @@ -188,12 +190,15 @@ channel_tls_start_listener(void) channel_init_listener(lchan); lchan->state = CHANNEL_STATE_LISTENING; lchan->close = channel_tls_close_method; + lchan->describe_transport = channel_tls_describe_transport_method; channel_tls_listener = listener; log_debug(LD_CHANNEL, "Starting TLS listener channel %p with global id %lu", lchan, lchan->global_identifier); + + channel_register(lchan); } else lchan = TLS_CHAN_TO_BASE(channel_tls_listener); return lchan; @@ -245,6 +250,7 @@ channel_tls_handle_incoming(or_connection_t *orconn) channel_init_for_cells(chan); chan->state = CHANNEL_STATE_OPENING; chan->close = channel_tls_close_method; + chan->describe_transport = channel_tls_describe_transport_method; chan->u.cell_chan.get_remote_descr = channel_tls_get_remote_descr_method; chan->u.cell_chan.has_queued_writes = channel_tls_has_queued_writes_method; chan->u.cell_chan.is_canonical = channel_tls_is_canonical_method; @@ -334,6 +340,43 @@ channel_tls_close_method(channel_t *chan) } } +/** + * Describe the transport for a channel_tls_t + * + * This returns the string "TLS channel on connection " to the upper + * layer. + */ + +static const char * +channel_tls_describe_transport_method(channel_t *chan) +{ + static char *buf = NULL; + uint64_t id; + channel_tls_t *tlschan; + const char *rv = NULL; + + tor_assert(chan); + + if (chan->is_listener) { + rv = "TLS channel (listening)"; + } else { + tlschan = BASE_CHAN_TO_TLS(chan); + + if (tlschan->conn) { + id = TO_CONN(tlschan->conn)->global_identifier; + + if (buf) tor_free(buf); + tor_asprintf(&buf, "TLS channel (connection %lu)", id); + + rv = buf; + } else { + rv = "TLS channel (no connection)"; + } + } + + return rv; +} + /** * Get the remote address of a channel_tls_t * diff --git a/src/or/main.c b/src/or/main.c index 908e490741..2a3e0e140e 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -2167,10 +2167,12 @@ dumpstats(int severity) } } } - /* TODO add dump for channels with circuit_dump_by_chan() */ circuit_dump_by_conn(conn, severity); /* dump info about all the circuits * using this conn */ } SMARTLIST_FOREACH_END(conn); + + channel_dumpstats(severity); + log(severity, LD_NET, "Cells processed: "U64_FORMAT" padding\n" " "U64_FORMAT" create\n" -- cgit v1.2.3-54-g00ecf From 71ba517e0c557d0ae35af7a5f99c8756f336a416 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Mon, 8 Oct 2012 18:24:04 -0700 Subject: Check return value from connection_or_connect() in channel_tls_connect() It's possible for connection_or_connect() to fail and return NULL after it sets tlschan->conn, so not checking leaves a channel hanging around in CHANNEL_STATE_OPENING with a pointer to a freed or_connection_t forever. --- src/or/channeltls.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/or/channeltls.c b/src/or/channeltls.c index 8a1b5ebc9f..03792dd796 100644 --- a/src/or/channeltls.c +++ b/src/or/channeltls.c @@ -129,7 +129,7 @@ channel_tls_connect(const tor_addr_t *addr, uint16_t port, cell_ewma_get_tick(); /* Set up or_connection stuff */ - connection_or_connect(addr, port, id_digest, tlschan); + tlschan->conn = connection_or_connect(addr, port, id_digest, tlschan); /* connection_or_connect() will fill in tlschan->conn */ if (!(tlschan->conn)) { channel_change_state(chan, CHANNEL_STATE_ERROR); -- cgit v1.2.3-54-g00ecf From e709fe320a872d8ddbeb62c029cec0246eeabc39 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Mon, 8 Oct 2012 19:48:06 -0700 Subject: Use U64_FORMAT/U64_PRINTF_ARG rather than %lu for channel_t --- src/or/channel.c | 286 +++++++++++++++++++++++++++------------------------ src/or/channeltls.c | 18 ++-- src/or/circuitlist.c | 10 +- src/or/command.c | 6 +- src/or/cpuworker.c | 5 +- 5 files changed, 177 insertions(+), 148 deletions(-) (limited to 'src') diff --git a/src/or/channel.c b/src/or/channel.c index cf08e3b435..2fe4466e53 100644 --- a/src/or/channel.c +++ b/src/or/channel.c @@ -241,14 +241,15 @@ channel_register(channel_t *chan) if (chan->is_listener) { log_debug(LD_CHANNEL, - "Registering listener channel %p (ID %lu) in state %s (%d)", - chan, chan->global_identifier, + "Registering listener channel %p (ID " U64_FORMAT ") " + "in state %s (%d)", + chan, U64_PRINTF_ARG(chan->global_identifier), channel_state_to_string(chan->state), chan->state); } else { log_debug(LD_CHANNEL, - "Registering cell channel %p (ID %lu) in state %s (%d) " - "with digest %s", - chan, chan->global_identifier, + "Registering cell channel %p (ID " U64_FORMAT ") " + "in state %s (%d) with digest %s", + chan, U64_PRINTF_ARG(chan->global_identifier), channel_state_to_string(chan->state), chan->state, hex_str(chan->u.cell_chan.identity_digest, DIGEST_LEN)); } @@ -282,9 +283,9 @@ channel_register(channel_t *chan) channel_add_to_digest_map(chan); } else { log_info(LD_CHANNEL, - "Channel %p (global ID %lu) in state %s (%d) registered " - "with no identity digest", - chan, chan->global_identifier, + "Channel %p (global ID " U64_FORMAT ") " + "in state %s (%d) registered with no identity digest", + chan, U64_PRINTF_ARG(chan->global_identifier), channel_state_to_string(chan->state), chan->state); } } @@ -398,9 +399,9 @@ channel_add_to_digest_map(channel_t *chan) } log_debug(LD_CHANNEL, - "Added channel %p (%lu) to identity map in state %s (%d) " - "with digest %s", - chan, chan->global_identifier, + "Added channel %p (global ID " U64_FORMAT ") " + "to identity map in state %s (%d) with digest %s", + chan, U64_PRINTF_ARG(chan->global_identifier), channel_state_to_string(chan->state), chan->state, hex_str(chan->u.cell_chan.identity_digest, DIGEST_LEN)); } @@ -432,9 +433,10 @@ channel_remove_from_digest_map(channel_t *chan) * case is similar to digestmap_get() failing below. */ log_warn(LD_BUG, - "Trying to remove channel %p (%lu) with digest %s from " - "identity map, but didn't have any identity map", - chan, chan->global_identifier, + "Trying to remove channel %p (global ID " U64_FORMAT ") " + "with digest %s from identity map, but didn't have any identity " + "map", + chan, U64_PRINTF_ARG(chan->global_identifier), hex_str(chan->u.cell_chan.identity_digest, DIGEST_LEN)); /* Clear out its next/prev pointers */ if (chan->u.cell_chan.next_with_same_id) { @@ -496,18 +498,18 @@ channel_remove_from_digest_map(channel_t *chan) chan->u.cell_chan.prev_with_same_id = NULL; log_debug(LD_CHANNEL, - "Removed channel %p (%lu) from identity map in state %s (%d) " - "with digest %s", - chan, chan->global_identifier, + "Removed channel %p (global ID " U64_FORMAT ") from " + "identity map in state %s (%d) with digest %s", + chan, U64_PRINTF_ARG(chan->global_identifier), channel_state_to_string(chan->state), chan->state, hex_str(chan->u.cell_chan.identity_digest, DIGEST_LEN)); } else { /* This is not good */ log_warn(LD_BUG, - "Trying to remove channel %p (%lu) with digest %s from " - "identity map, but couldn't find it in the list for that " - "digest", - chan, chan->global_identifier, + "Trying to remove channel %p (global ID " U64_FORMAT ") " + "with digest %s from identity map, but couldn't find it in " + "the list for that digest", + chan, U64_PRINTF_ARG(chan->global_identifier), hex_str(chan->u.cell_chan.identity_digest, DIGEST_LEN)); /* Unlink it and hope for the best */ if (chan->u.cell_chan.next_with_same_id) { @@ -526,9 +528,10 @@ channel_remove_from_digest_map(channel_t *chan) } else { /* Shouldn't happen */ log_warn(LD_BUG, - "Trying to remove channel %p (%lu) with digest %s from " - "identity map, but couldn't find any with that digest", - chan, chan->global_identifier, + "Trying to remove channel %p (global ID " U64_FORMAT ") with " + "digest %s from identity map, but couldn't find any with " + "that digest", + chan, U64_PRINTF_ARG(chan->global_identifier), hex_str(chan->u.cell_chan.identity_digest, DIGEST_LEN)); /* Clear out its next/prev pointers */ if (chan->u.cell_chan.next_with_same_id) { @@ -1256,8 +1259,8 @@ channel_clear_identity_digest(channel_t *chan) log_debug(LD_CHANNEL, "Clearing remote endpoint digest on channel %p with " - "global ID %lu", - chan, chan->global_identifier); + "global ID " U64_FORMAT, + chan, U64_PRINTF_ARG(chan->global_identifier)); state_not_in_map = (chan->state == CHANNEL_STATE_LISTENING || @@ -1295,8 +1298,8 @@ channel_set_identity_digest(channel_t *chan, log_debug(LD_CHANNEL, "Setting remote endpoint digest on channel %p with " - "global ID %lu to digest %s", - chan, chan->global_identifier, + "global ID " U64_FORMAT " to digest %s", + chan, U64_PRINTF_ARG(chan->global_identifier), identity_digest ? hex_str(identity_digest, DIGEST_LEN) : "(null)"); @@ -1354,8 +1357,8 @@ channel_clear_remote_end(channel_t *chan) log_debug(LD_CHANNEL, "Clearing remote endpoint identity on channel %p with " - "global ID %lu", - chan, chan->global_identifier); + "global ID " U64_FORMAT, + chan, U64_PRINTF_ARG(chan->global_identifier)); state_not_in_map = (chan->state == CHANNEL_STATE_LISTENING || @@ -1396,8 +1399,8 @@ channel_set_remote_end(channel_t *chan, log_debug(LD_CHANNEL, "Setting remote endpoint identity on channel %p with " - "global ID %lu to nickname %s, digest %s", - chan, chan->global_identifier, + "global ID " U64_FORMAT " to nickname %s, digest %s", + chan, U64_PRINTF_ARG(chan->global_identifier), nickname ? nickname : "(null)", identity_digest ? hex_str(identity_digest, DIGEST_LEN) : "(null)"); @@ -1469,8 +1472,9 @@ channel_write_cell(channel_t *chan, cell_t *cell) chan->state == CHANNEL_STATE_MAINT); log_debug(LD_CHANNEL, - "Writing cell_t %p to channel %p with global ID %lu", - cell, chan, chan->global_identifier); + "Writing cell_t %p to channel %p with global ID " + U64_FORMAT, + cell, chan, U64_PRINTF_ARG(chan->global_identifier)); /* Increment the timestamp unless it's padding */ if (!(cell->command == CELL_PADDING || @@ -1532,8 +1536,10 @@ channel_write_packed_cell(channel_t *chan, packed_cell_t *packed_cell) chan->state == CHANNEL_STATE_MAINT); log_debug(LD_CHANNEL, - "Writing packed_cell_t %p to channel %p with global ID %lu", - packed_cell, chan, chan->global_identifier); + "Writing packed_cell_t %p to channel %p with global ID " + U64_FORMAT, + packed_cell, chan, + U64_PRINTF_ARG(chan->global_identifier)); /* Increment the timestamp */ chan->u.cell_chan.timestamp_last_added_nonpadding = approx_time(); @@ -1594,8 +1600,10 @@ channel_write_var_cell(channel_t *chan, var_cell_t *var_cell) chan->state == CHANNEL_STATE_MAINT); log_debug(LD_CHANNEL, - "Writing var_cell_t %p to channel %p with global ID %lu", - var_cell, chan, chan->global_identifier); + "Writing var_cell_t %p to channel %p with global ID " + U64_FORMAT, + var_cell, chan, + U64_PRINTF_ARG(chan->global_identifier)); /* Increment the timestamp unless it's padding */ if (!(var_cell->command == CELL_PADDING || @@ -2064,8 +2072,8 @@ channel_process_incoming(channel_t *listener) log_debug(LD_CHANNEL, "Processing queue of incoming connections for listening " - "channel %p (global ID %lu)", - listener, listener->global_identifier); + "channel %p (global ID " U64_FORMAT ")", + listener, U64_PRINTF_ARG(listener->global_identifier)); if (!(listener->u.listener.incoming_list)) return; @@ -2075,9 +2083,12 @@ channel_process_incoming(channel_t *listener) tor_assert(!(chan->is_listener)); log_debug(LD_CHANNEL, - "Handling incoming connection %p (%lu) for listener %p (%lu)", - chan, chan->global_identifier, - listener, listener->global_identifier); + "Handling incoming connection %p (" U64_FORMAT ") " + "for listener %p (" U64_FORMAT ")", + chan, + U64_PRINTF_ARG(chan->global_identifier), + listener, + U64_PRINTF_ARG(listener->global_identifier)); /* Make sure this is set correctly */ channel_mark_incoming(chan); listener->u.listener.listener(listener, chan); @@ -2524,8 +2535,9 @@ channel_free_all(void) /* Deregister and free it */ tor_assert(tmp); log_debug(LD_CHANNEL, - "Cleaning up finished channel %p (ID %lu) in state %s (%d)", - tmp, tmp->global_identifier, + "Cleaning up finished channel %p (ID " U64_FORMAT ") " + "in state %s (%d)", + tmp, U64_PRINTF_ARG(tmp->global_identifier), channel_state_to_string(tmp->state), tmp->state); channel_unregister(tmp); channel_free(tmp); @@ -2545,8 +2557,9 @@ channel_free_all(void) /* Close, deregister and free it */ tor_assert(tmp); log_debug(LD_CHANNEL, - "Cleaning up listening channel %p (ID %lu) in state %s (%d)", - tmp, tmp->global_identifier, + "Cleaning up listening channel %p (ID " U64_FORMAT ") " + "in state %s (%d)", + tmp, U64_PRINTF_ARG(tmp->global_identifier), channel_state_to_string(tmp->state), tmp->state); /* * We have to unregister first so we don't put it in finished_channels @@ -2570,8 +2583,9 @@ channel_free_all(void) /* Close, deregister and free it */ tor_assert(tmp); log_debug(LD_CHANNEL, - "Cleaning up active channel %p (ID %lu) in state %s (%d)", - tmp, tmp->global_identifier, + "Cleaning up active channel %p (ID " U64_FORMAT ") " + "in state %s (%d)", + tmp, U64_PRINTF_ARG(tmp->global_identifier), channel_state_to_string(tmp->state), tmp->state); /* * We have to unregister first so we don't put it in finished_channels @@ -2595,8 +2609,9 @@ channel_free_all(void) /* Close, deregister and free it */ tor_assert(tmp); log_debug(LD_CHANNEL, - "Cleaning up leftover channel %p (ID %lu) in state %s (%d)", - tmp, tmp->global_identifier, + "Cleaning up leftover channel %p (ID " U64_FORMAT ") " + "in state %s (%d)", + tmp, U64_PRINTF_ARG(tmp->global_identifier), channel_state_to_string(tmp->state), tmp->state); channel_unregister(tmp); if (!(tmp->state == CHANNEL_STATE_CLOSING || @@ -2875,27 +2890,32 @@ channel_dump_statistics(channel_t *chan, int severity) age = (double)(now - chan->timestamp_created); log(severity, LD_GENERAL, - "Channel %lu (at %p) with transport %s is in state %s (%d) and %s", - chan->global_identifier, chan, + "Channel " U64_FORMAT " (at %p) with transport %s is in state " + "%s (%d) and %s", + U64_PRINTF_ARG(chan->global_identifier), chan, channel_describe_transport(chan), channel_state_to_string(chan->state), chan->state, chan->is_listener ? "listens for incoming connections" : "transports cells"); log(severity, LD_GENERAL, - " * Channel %lu was created at %lu (%lu seconds ago) and last active " - "at %lu (%lu seconds ago)", - chan->global_identifier, - chan->timestamp_created, now - chan->timestamp_created, - chan->timestamp_active, now - chan->timestamp_active); + " * Channel " U64_FORMAT " was created at " U64_FORMAT + " (" U64_FORMAT " seconds ago) " + "and last active at " U64_FORMAT " (" U64_FORMAT " seconds ago)", + U64_PRINTF_ARG(chan->global_identifier), + U64_PRINTF_ARG(chan->timestamp_created), + U64_PRINTF_ARG(now - chan->timestamp_created), + U64_PRINTF_ARG(chan->timestamp_active), + U64_PRINTF_ARG(now - chan->timestamp_active)); if (chan->is_listener) { log(severity, LD_GENERAL, - " * Listener channel %lu last accepted an incoming channel at %lu " - "(%lu seconds ago) and has accepted %lu channels in total", - chan->global_identifier, - chan->u.listener.timestamp_accepted, - now - chan->u.listener.timestamp_accepted, - chan->u.listener.n_accepted); + " * Listener channel " U64_FORMAT " last accepted an incoming " + "channel at " U64_FORMAT " (" U64_FORMAT " seconds ago) " + "and has accepted " U64_FORMAT " channels in total", + U64_PRINTF_ARG(chan->global_identifier), + U64_PRINTF_ARG(chan->u.listener.timestamp_accepted), + U64_PRINTF_ARG(now - chan->u.listener.timestamp_accepted), + U64_PRINTF_ARG(chan->u.listener.n_accepted)); /* * If it's sensible to do so, get the rate of incoming channels on this @@ -2907,15 +2927,15 @@ channel_dump_statistics(channel_t *chan, int severity) avg = (double)(chan->u.listener.n_accepted) / age; if (avg >= 1.0) { log(severity, LD_GENERAL, - " * Listener channel %lu has averaged %f incoming channels per " - "second", - chan->global_identifier, avg); + " * Listener channel " U64_FORMAT " has averaged %f incoming " + "channels per second", + U64_PRINTF_ARG(chan->global_identifier), avg); } else if (avg >= 0.0) { interval = 1.0 / avg; log(severity, LD_GENERAL, - " * Listener channel %lu has averaged %f seconds between " - "incoming channels", - chan->global_identifier, interval); + " * Listener channel " U64_FORMAT " has averaged %f seconds " + "between incoming channels", + U64_PRINTF_ARG(chan->global_identifier), interval); } } } else { @@ -2923,30 +2943,30 @@ channel_dump_statistics(channel_t *chan, int severity) if (!tor_digest_is_zero(chan->u.cell_chan.identity_digest)) { if (chan->u.cell_chan.nickname) { log(severity, LD_GENERAL, - " * Cell-bearing channel %lu says it is connected to an OR " - "with digest %s and nickname %s", - chan->global_identifier, + " * Cell-bearing channel " U64_FORMAT " says it is connected " + "to an OR with digest %s and nickname %s", + U64_PRINTF_ARG(chan->global_identifier), hex_str(chan->u.cell_chan.identity_digest, DIGEST_LEN), chan->u.cell_chan.nickname); } else { log(severity, LD_GENERAL, - " * Cell-bearing channel %lu says it is connected to an OR " - "with digest %s and no known nickname", - chan->global_identifier, + " * Cell-bearing channel " U64_FORMAT " says it is connected " + "to an OR with digest %s and no known nickname", + U64_PRINTF_ARG(chan->global_identifier), hex_str(chan->u.cell_chan.identity_digest, DIGEST_LEN)); } } else { if (chan->u.cell_chan.nickname) { log(severity, LD_GENERAL, - " * Cell-bearing channel %lu does not know the digest of the " - "OR it is connected to, but reports its nickname is %s", - chan->global_identifier, + " * Cell-bearing channel " U64_FORMAT " does not know the digest" + " of the OR it is connected to, but reports its nickname is %s", + U64_PRINTF_ARG(chan->global_identifier), chan->u.cell_chan.nickname); } else { log(severity, LD_GENERAL, - " * Cell-bearing channel %lu does not know the digest or the " - "nickname of the OR it is connected to", - chan->global_identifier); + " * Cell-bearing channel " U64_FORMAT " does not know the digest" + " or the nickname of the OR it is connected to", + U64_PRINTF_ARG(chan->global_identifier)); } } @@ -2955,29 +2975,29 @@ channel_dump_statistics(channel_t *chan, int severity) if (have_remote_addr) { remote_addr_str = tor_dup_addr(&remote_addr); log(severity, LD_GENERAL, - " * Cell-bearing channel %lu says its remote address is %s, " - "and gives a canonical description of \"%s\" and an " + " * Cell-bearing channel " U64_FORMAT " says its remote address" + " is %s, and gives a canonical description of \"%s\" and an " "actual description of \"%s\"", - chan->global_identifier, + U64_PRINTF_ARG(chan->global_identifier), remote_addr_str, channel_get_canonical_remote_descr(chan), channel_get_actual_remote_descr(chan)); tor_free(remote_addr_str); } else { log(severity, LD_GENERAL, - " * Cell-bearing channel %lu does not know its remote address, " - "but gives a canonical description of \"%s\" and an " + " * Cell-bearing channel " U64_FORMAT " does not know its remote " + "address, but gives a canonical description of \"%s\" and an " "actual description of \"%s\"", - chan->global_identifier, + U64_PRINTF_ARG(chan->global_identifier), channel_get_canonical_remote_descr(chan), channel_get_actual_remote_descr(chan)); } /* Handle marks */ log(severity, LD_GENERAL, - " * Cell-bearing channel %lu has these marks: %s %s %s " + " * Cell-bearing channel " U64_FORMAT " has these marks: %s %s %s " "%s %s %s", - chan->global_identifier, + U64_PRINTF_ARG(chan->global_identifier), channel_is_bad_for_new_circs(chan) ? "bad_for_new_circs" : "!bad_for_new_circs", channel_is_canonical(chan) ? @@ -2994,9 +3014,9 @@ channel_dump_statistics(channel_t *chan, int severity) /* Describe queues */ log(severity, LD_GENERAL, - " * Cell-bearing channel %lu has %d queued incoming cells " - "and %d queued outgoing cells", - chan->global_identifier, + " * Cell-bearing channel " U64_FORMAT " has %d queued incoming cells" + " and %d queued outgoing cells", + U64_PRINTF_ARG(chan->global_identifier), (chan->u.cell_chan.cell_queue != NULL) ? smartlist_len(chan->u.cell_chan.cell_queue) : 0, (chan->u.cell_chan.outgoing_queue != NULL) ? @@ -3004,9 +3024,9 @@ channel_dump_statistics(channel_t *chan, int severity) /* Describe circuits */ log(severity, LD_GENERAL, - " * Cell-bearing channel %lu has %d active circuits out of %d" - " in total", - chan->global_identifier, + " * Cell-bearing channel " U64_FORMAT " has %d active circuits out of" + " %d in total", + U64_PRINTF_ARG(chan->global_identifier), (chan->u.cell_chan.active_circuit_pqueue != NULL) ? smartlist_len(chan->u.cell_chan.active_circuit_pqueue) : 0, chan->u.cell_chan.n_circuits); @@ -3014,67 +3034,67 @@ channel_dump_statistics(channel_t *chan, int severity) /* Describe timestamps */ log(severity, LD_GENERAL, - " * Cell-bearing channel %lu was last used by a client at %lu " - "(%lu seconds ago)", - chan->global_identifier, - chan->u.cell_chan.timestamp_client, - now - chan->u.cell_chan.timestamp_client); + " * Cell-bearing channel " U64_FORMAT " was last used by a " + "client at " U64_FORMAT " (" U64_FORMAT " seconds ago)", + U64_PRINTF_ARG(chan->global_identifier), + U64_PRINTF_ARG(chan->u.cell_chan.timestamp_client), + U64_PRINTF_ARG(now - chan->u.cell_chan.timestamp_client)); log(severity, LD_GENERAL, - " * Cell-bearing channel %lu was last drained at %lu " - "(%lu seconds ago)", - chan->global_identifier, - chan->u.cell_chan.timestamp_drained, - now - chan->u.cell_chan.timestamp_drained); + " * Cell-bearing channel " U64_FORMAT " was last drained at " + U64_FORMAT " (" U64_FORMAT " seconds ago)", + U64_PRINTF_ARG(chan->global_identifier), + U64_PRINTF_ARG(chan->u.cell_chan.timestamp_drained), + U64_PRINTF_ARG(now - chan->u.cell_chan.timestamp_drained)); log(severity, LD_GENERAL, - " * Cell-bearing channel %lu last received a cell at %lu " - "(%lu seconds ago)", - chan->global_identifier, - chan->u.cell_chan.timestamp_recv, - now - chan->u.cell_chan.timestamp_recv); + " * Cell-bearing channel " U64_FORMAT " last received a cell " + "at " U64_FORMAT " (" U64_FORMAT " seconds ago)", + U64_PRINTF_ARG(chan->global_identifier), + U64_PRINTF_ARG(chan->u.cell_chan.timestamp_recv), + U64_PRINTF_ARG(now - chan->u.cell_chan.timestamp_recv)); log(severity, LD_GENERAL, - " * Cell-bearing channel %lu last trasmitted a cell at %lu " - "(%lu seconds ago)", - chan->global_identifier, - chan->u.cell_chan.timestamp_xmit, - now - chan->u.cell_chan.timestamp_xmit); + " * Cell-bearing channel " U64_FORMAT " last trasmitted a cell " + "at " U64_FORMAT " (" U64_FORMAT " seconds ago)", + U64_PRINTF_ARG(chan->global_identifier), + U64_PRINTF_ARG(chan->u.cell_chan.timestamp_xmit), + U64_PRINTF_ARG(now - chan->u.cell_chan.timestamp_xmit)); /* Describe counters and rates */ log(severity, LD_GENERAL, - " * Cell-bearing channel %lu has received %lu cells and " - " transmitted %lu", - chan->global_identifier, - chan->u.cell_chan.n_cells_recved, - chan->u.cell_chan.n_cells_xmitted); + " * Cell-bearing channel " U64_FORMAT " has received " + U64_FORMAT " cells and transmitted " U64_FORMAT, + U64_PRINTF_ARG(chan->global_identifier), + U64_PRINTF_ARG(chan->u.cell_chan.n_cells_recved), + U64_PRINTF_ARG(chan->u.cell_chan.n_cells_xmitted)); if (now > chan->timestamp_created && chan->timestamp_created > 0) { if (chan->u.cell_chan.n_cells_recved > 0) { avg = (double)(chan->u.cell_chan.n_cells_recved) / age; if (avg >= 1.0) { log(severity, LD_GENERAL, - " * Cell-bearing channel %lu has averaged %f cells received " - "per second", - chan->global_identifier, avg); + " * Cell-bearing channel " U64_FORMAT " has averaged %f " + "cells received per second", + U64_PRINTF_ARG(chan->global_identifier), avg); } else if (avg >= 0.0) { interval = 1.0 / avg; log(severity, LD_GENERAL, - " * Cell-bearing channel %lu has averaged %f seconds between " - "received cells", - chan->global_identifier, interval); + " * Cell-bearing channel " U64_FORMAT " has averaged %f " + "seconds between received cells", + U64_PRINTF_ARG(chan->global_identifier), interval); } } if (chan->u.cell_chan.n_cells_xmitted > 0) { avg = (double)(chan->u.cell_chan.n_cells_xmitted) / age; if (avg >= 1.0) { log(severity, LD_GENERAL, - " * Cell-bearing channel %lu has averaged %f cells transmitted " - "per second", - chan->global_identifier, avg); + " * Cell-bearing channel " U64_FORMAT " has averaged %f " + "cells transmitted per second", + U64_PRINTF_ARG(chan->global_identifier), avg); } else if (avg >= 0.0) { interval = 1.0 / avg; log(severity, LD_GENERAL, - " * Cell-bearing channel %lu has averaged %f seconds between " - "transmitted cells", - chan->global_identifier, interval); + " * Cell-bearing channel " U64_FORMAT " has averaged %f " + "seconds between transmitted cells", + U64_PRINTF_ARG(chan->global_identifier), interval); } } } diff --git a/src/or/channeltls.c b/src/or/channeltls.c index 03792dd796..9fd7e6c339 100644 --- a/src/or/channeltls.c +++ b/src/or/channeltls.c @@ -118,8 +118,10 @@ channel_tls_connect(const tor_addr_t *addr, uint16_t port, chan->u.cell_chan.write_var_cell = channel_tls_write_var_cell_method; log_debug(LD_CHANNEL, - "In channel_tls_connect() for channel %p (global id %lu)", - tlschan, chan->global_identifier); + "In channel_tls_connect() for channel %p " + "(global id " U64_FORMAT ")", + tlschan, + U64_PRINTF_ARG(chan->global_identifier)); if (is_local_addr(addr)) channel_mark_local(chan); channel_mark_outgoing(chan); @@ -137,8 +139,8 @@ channel_tls_connect(const tor_addr_t *addr, uint16_t port, } log_debug(LD_CHANNEL, - "Got orconn %p for channel with global id %lu", - tlschan->conn, chan->global_identifier); + "Got orconn %p for channel with global id " U64_FORMAT, + tlschan->conn, U64_PRINTF_ARG(chan->global_identifier)); goto done; @@ -195,8 +197,8 @@ channel_tls_start_listener(void) channel_tls_listener = listener; log_debug(LD_CHANNEL, - "Starting TLS listener channel %p with global id %lu", - lchan, lchan->global_identifier); + "Starting TLS listener channel %p with global id " U64_FORMAT, + lchan, U64_PRINTF_ARG(lchan->global_identifier)); channel_register(lchan); } else lchan = TLS_CHAN_TO_BASE(channel_tls_listener); @@ -366,7 +368,9 @@ channel_tls_describe_transport_method(channel_t *chan) id = TO_CONN(tlschan->conn)->global_identifier; if (buf) tor_free(buf); - tor_asprintf(&buf, "TLS channel (connection %lu)", id); + tor_asprintf(&buf, + "TLS channel (connection " U64_FORMAT ")", + U64_PRINTF_ARG(id)); rv = buf; } else { diff --git a/src/or/circuitlist.c b/src/or/circuitlist.c index 4746f912de..68cd19e152 100644 --- a/src/or/circuitlist.c +++ b/src/or/circuitlist.c @@ -918,15 +918,17 @@ circuit_get_by_circid_channel_impl(circid_t circ_id, channel_t *chan) if (found && found->circuit) { log_debug(LD_CIRC, "circuit_get_by_circid_channel_impl() returning circuit %p for" - " circ_id %d, channel ID %lu (%p)", - found->circuit, circ_id, chan->global_identifier, chan); + " circ_id %d, channel ID " U64_FORMAT " (%p)", + found->circuit, circ_id, + U64_PRINTF_ARG(chan->global_identifier), chan); return found->circuit; } log_debug(LD_CIRC, "circuit_get_by_circid_channel_impl() found nothing for" - " circ_id %d, channel ID %lu (%p)", - circ_id, chan->global_identifier, chan); + " circ_id %d, channel ID " U64_FORMAT " (%p)", + circ_id, + U64_PRINTF_ARG(chan->global_identifier), chan); return NULL; /* The rest of this checks for bugs. Disabled by default. */ diff --git a/src/or/command.c b/src/or/command.c index c7a1b1ac07..351ab08549 100644 --- a/src/or/command.c +++ b/src/or/command.c @@ -193,8 +193,10 @@ command_process_create_cell(cell_t *cell, channel_t *chan) tor_assert(!(chan->is_listener)); log_debug(LD_OR, - "Got a CREATE cell for circ_id %d on channel %lu (%p)", - cell->circ_id, chan->global_identifier, chan); + "Got a CREATE cell for circ_id %d on channel " U64_FORMAT + " (%p)", + cell->circ_id, + U64_PRINTF_ARG(chan->global_identifier), chan); if (we_are_hibernating()) { log_info(LD_OR, diff --git a/src/or/cpuworker.c b/src/or/cpuworker.c index a78518fd02..119892d710 100644 --- a/src/or/cpuworker.c +++ b/src/or/cpuworker.c @@ -157,8 +157,9 @@ connection_cpu_process_inbuf(connection_t *conn) tag_unpack(buf, &chan_id, &circ_id); circ = NULL; log_debug(LD_OR, - "Unpacking cpuworker reply, chan_id is %lu, circ_id is %d", - chan_id, circ_id); + "Unpacking cpuworker reply, chan_id is " U64_FORMAT + ", circ_id is %d", + U64_PRINTF_ARG(chan_id), circ_id); p_chan = channel_find_by_global_id(chan_id); if (p_chan) -- cgit v1.2.3-54-g00ecf From 64e6f6687c8fc275ddc207fa43f29c4213d3cad2 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Mon, 8 Oct 2012 19:50:41 -0700 Subject: channel_free() should be a no-op --- src/or/channel.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/or/channel.c b/src/or/channel.c index 2fe4466e53..690bfb92a7 100644 --- a/src/or/channel.c +++ b/src/or/channel.c @@ -763,7 +763,8 @@ channel_init_listener(channel_t *chan) void channel_free(channel_t *chan) { - tor_assert(chan); + if (!chan) return; + /* It must be closed or errored */ tor_assert(chan->state == CHANNEL_STATE_CLOSED || chan->state == CHANNEL_STATE_ERROR); -- cgit v1.2.3-54-g00ecf From 965c9de498ab7f6c7ce3dce133bb34456f3d668e Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Mon, 8 Oct 2012 19:52:04 -0700 Subject: Abolish superfluous channel_find_by_remote_nickname() --- src/or/channel.c | 36 ------------------------------------ src/or/channel.h | 1 - 2 files changed, 37 deletions(-) (limited to 'src') diff --git a/src/or/channel.c b/src/or/channel.c index 690bfb92a7..9b814bb655 100644 --- a/src/or/channel.c +++ b/src/or/channel.c @@ -609,42 +609,6 @@ channel_find_by_remote_digest(const char *identity_digest) return rv; } -/** - * Find channel by remote nickname - * - * This function looks up a channel by the nickname of the remote - * endpoint. It's possible that more than one channel to that endpoint - * nickname exists, but there is not currently any supported way to iterate - * them. Use digests. - * - * @param nickname A node nickname - * @return A channel pointer to a channel to a node with that nickname, or - * NULL if none is available. - */ - -channel_t * -channel_find_by_remote_nickname(const char *nickname) -{ - channel_t *rv = NULL; - - tor_assert(nickname); - - if (all_channels && smartlist_len(all_channels) > 0) { - SMARTLIST_FOREACH_BEGIN(all_channels, channel_t *, curr) { - if (!(curr->is_listener)) { - if (curr->u.cell_chan.nickname && - strncmp(curr->u.cell_chan.nickname, nickname, - MAX_NICKNAME_LEN) == 0) { - rv = curr; - break; - } - } - } SMARTLIST_FOREACH_END(curr); - } - - return rv; -} - /** * Next channel with digest * diff --git a/src/or/channel.h b/src/or/channel.h index ed69b9200e..70759818fe 100644 --- a/src/or/channel.h +++ b/src/or/channel.h @@ -368,7 +368,6 @@ int channel_is_better(time_t now, channel_t * channel_find_by_global_id(uint64_t global_identifier); channel_t * channel_find_by_remote_digest(const char *identity_digest); -channel_t * channel_find_by_remote_nickname(const char *nickname); /** For things returned by channel_find_by_remote_digest(), walk the list. */ -- cgit v1.2.3-54-g00ecf From 341928c807b1d16383710b8b2728ed963746bb9d Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Mon, 8 Oct 2012 19:53:05 -0700 Subject: Simplify channel_find_by_remote_digest() --- src/or/channel.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/or/channel.c b/src/or/channel.c index 9b814bb655..c5bf60d065 100644 --- a/src/or/channel.c +++ b/src/or/channel.c @@ -596,14 +596,13 @@ channel_find_by_global_id(uint64_t global_identifier) channel_t * channel_find_by_remote_digest(const char *identity_digest) { - channel_t *rv = NULL, *tmp; + channel_t *rv = NULL; tor_assert(identity_digest); /* Search for it in the identity map */ if (channel_identity_map) { - tmp = digestmap_get(channel_identity_map, identity_digest); - rv = tmp; + rv = digestmap_get(channel_identity_map, identity_digest); } return rv; -- cgit v1.2.3-54-g00ecf From bb92a2d7a8560e7028faad024c3307080449aea4 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Mon, 8 Oct 2012 19:54:47 -0700 Subject: Remove orphaned comment in channel_flush_some_cells_from_outgoing_queue() --- src/or/channel.c | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/or/channel.c b/src/or/channel.c index c5bf60d065..114b5a885e 100644 --- a/src/or/channel.c +++ b/src/or/channel.c @@ -1847,7 +1847,6 @@ channel_flush_some_cells_from_outgoing_queue(channel_t *chan, /* Get the head of the queue */ q = smartlist_get(chan->u.cell_chan.outgoing_queue, 0); - /* That shouldn't happen; bail out */ if (q) { /* * Okay, we have a good queue entry, try to give it to the lower -- cgit v1.2.3-54-g00ecf From 17356fe7fd96af9285f8f9507b111373fd6fb891 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Mon, 8 Oct 2012 20:02:42 -0700 Subject: Eliminate unnecessary SMARTLIST_DEL_CURRENT() invocations in channel.c, channeltls.c --- src/or/channel.c | 88 +++++++++++++++++++---------------------------------- src/or/channeltls.c | 5 +-- 2 files changed, 32 insertions(+), 61 deletions(-) (limited to 'src') diff --git a/src/or/channel.c b/src/or/channel.c index 114b5a885e..b3ab2f0733 100644 --- a/src/or/channel.c +++ b/src/or/channel.c @@ -761,9 +761,6 @@ channel_free(channel_t *chan) void channel_force_free(channel_t *chan) { - cell_queue_entry_t *tmp = NULL; - channel_t *tmpchan = NULL; - tor_assert(chan); /* Call a free method if there is one */ @@ -778,9 +775,7 @@ channel_force_free(channel_t *chan) if (chan->u.listener.incoming_list) { SMARTLIST_FOREACH_BEGIN(chan->u.listener.incoming_list, channel_t *, qchan) { - tmpchan = qchan; - SMARTLIST_DEL_CURRENT(chan->u.listener.incoming_list, qchan); - channel_request_close(tmpchan); + channel_request_close(qchan); } SMARTLIST_FOREACH_END(qchan); smartlist_free(chan->u.listener.incoming_list); @@ -794,8 +789,6 @@ channel_force_free(channel_t *chan) if (chan->u.cell_chan.cell_queue) { SMARTLIST_FOREACH_BEGIN(chan->u.cell_chan.cell_queue, cell_queue_entry_t *, q) { - tmp = q; - SMARTLIST_DEL_CURRENT(chan->u.cell_chan.cell_queue, q); tor_free(q); } SMARTLIST_FOREACH_END(q); @@ -807,14 +800,12 @@ channel_force_free(channel_t *chan) if (chan->u.cell_chan.outgoing_queue) { SMARTLIST_FOREACH_BEGIN(chan->u.cell_chan.outgoing_queue, cell_queue_entry_t *, q) { - tmp = q; - SMARTLIST_DEL_CURRENT(chan->u.cell_chan.outgoing_queue, q); - if (tmp->type == CELL_QUEUE_PACKED) { - if (tmp->u.packed.packed_cell) { - packed_cell_free(tmp->u.packed.packed_cell); + if (q->type == CELL_QUEUE_PACKED) { + if (q->u.packed.packed_cell) { + packed_cell_free(q->u.packed.packed_cell); } } - tor_free(tmp); + tor_free(q); } SMARTLIST_FOREACH_END(q); smartlist_free(chan->u.cell_chan.outgoing_queue); @@ -2055,10 +2046,8 @@ channel_process_incoming(channel_t *listener) /* Make sure this is set correctly */ channel_mark_incoming(chan); listener->u.listener.listener(listener, chan); - SMARTLIST_DEL_CURRENT(listener->u.listener.incoming_list, chan); } SMARTLIST_FOREACH_END(chan); - tor_assert(smartlist_len(listener->u.listener.incoming_list) == 0); smartlist_free(listener->u.listener.incoming_list); listener->u.listener.incoming_list = NULL; } @@ -2484,53 +2473,44 @@ channel_run_cleanup(void) void channel_free_all(void) { - channel_t *tmp = NULL; - log_debug(LD_CHANNEL, "Shutting down channels..."); /* First, let's go for finished channels */ if (finished_channels) { SMARTLIST_FOREACH_BEGIN(finished_channels, channel_t *, curr) { - tmp = curr; - /* Remove it from the list */ - SMARTLIST_DEL_CURRENT(finished_channels, curr); /* Deregister and free it */ - tor_assert(tmp); + tor_assert(curr); log_debug(LD_CHANNEL, "Cleaning up finished channel %p (ID " U64_FORMAT ") " "in state %s (%d)", - tmp, U64_PRINTF_ARG(tmp->global_identifier), - channel_state_to_string(tmp->state), tmp->state); - channel_unregister(tmp); - channel_free(tmp); + curr, U64_PRINTF_ARG(curr->global_identifier), + channel_state_to_string(curr->state), curr->state); + channel_unregister(curr); + channel_free(curr); } SMARTLIST_FOREACH_END(curr); smartlist_free(finished_channels); finished_channels = NULL; - tmp = NULL; } /* Now the listeners */ if (listening_channels) { SMARTLIST_FOREACH_BEGIN(listening_channels, channel_t *, curr) { - tmp = curr; - /* Remove it from the list */ - SMARTLIST_DEL_CURRENT(listening_channels, curr); /* Close, deregister and free it */ - tor_assert(tmp); + tor_assert(curr); log_debug(LD_CHANNEL, "Cleaning up listening channel %p (ID " U64_FORMAT ") " "in state %s (%d)", - tmp, U64_PRINTF_ARG(tmp->global_identifier), - channel_state_to_string(tmp->state), tmp->state); + curr, U64_PRINTF_ARG(curr->global_identifier), + channel_state_to_string(curr->state), curr->state); /* * We have to unregister first so we don't put it in finished_channels * and allocate that again on close. */ - channel_unregister(tmp); - channel_request_close(tmp); - channel_force_free(tmp); + channel_unregister(curr); + channel_request_close(curr); + channel_force_free(curr); } SMARTLIST_FOREACH_END(curr); smartlist_free(listening_channels); @@ -2540,23 +2520,20 @@ channel_free_all(void) /* Now all active channels */ if (active_channels) { SMARTLIST_FOREACH_BEGIN(active_channels, channel_t *, curr) { - tmp = curr; - /* Remove it from the list */ - SMARTLIST_DEL_CURRENT(active_channels, curr); /* Close, deregister and free it */ - tor_assert(tmp); + tor_assert(curr); log_debug(LD_CHANNEL, "Cleaning up active channel %p (ID " U64_FORMAT ") " "in state %s (%d)", - tmp, U64_PRINTF_ARG(tmp->global_identifier), - channel_state_to_string(tmp->state), tmp->state); + curr, U64_PRINTF_ARG(curr->global_identifier), + channel_state_to_string(curr->state), curr->state); /* * We have to unregister first so we don't put it in finished_channels * and allocate that again on close. */ - channel_unregister(tmp); - channel_request_close(tmp); - channel_force_free(tmp); + channel_unregister(curr); + channel_request_close(curr); + channel_force_free(curr); } SMARTLIST_FOREACH_END(curr); smartlist_free(active_channels); @@ -2566,23 +2543,20 @@ channel_free_all(void) /* Now all channels, in case any are left over */ if (all_channels) { SMARTLIST_FOREACH_BEGIN(all_channels, channel_t *, curr) { - tmp = curr; - /* Remove it from the list */ - SMARTLIST_DEL_CURRENT(all_channels, curr); /* Close, deregister and free it */ - tor_assert(tmp); + tor_assert(curr); log_debug(LD_CHANNEL, "Cleaning up leftover channel %p (ID " U64_FORMAT ") " "in state %s (%d)", - tmp, U64_PRINTF_ARG(tmp->global_identifier), - channel_state_to_string(tmp->state), tmp->state); - channel_unregister(tmp); - if (!(tmp->state == CHANNEL_STATE_CLOSING || - tmp->state == CHANNEL_STATE_CLOSED || - tmp->state == CHANNEL_STATE_ERROR)) { - channel_request_close(tmp); + curr, U64_PRINTF_ARG(curr->global_identifier), + channel_state_to_string(curr->state), curr->state); + channel_unregister(curr); + if (!(curr->state == CHANNEL_STATE_CLOSING || + curr->state == CHANNEL_STATE_CLOSED || + curr->state == CHANNEL_STATE_ERROR)) { + channel_request_close(curr); } - channel_force_free(tmp); + channel_force_free(curr); } SMARTLIST_FOREACH_END(curr); smartlist_free(all_channels); diff --git a/src/or/channeltls.c b/src/or/channeltls.c index 9fd7e6c339..883aef15f9 100644 --- a/src/or/channeltls.c +++ b/src/or/channeltls.c @@ -296,7 +296,6 @@ static void channel_tls_close_method(channel_t *chan) { channel_tls_t *tlschan = BASE_CHAN_TO_TLS(chan); - channel_t *tmp = NULL; tor_assert(tlschan); @@ -317,9 +316,7 @@ channel_tls_close_method(channel_t *chan) if (chan->u.listener.incoming_list) { SMARTLIST_FOREACH_BEGIN(chan->u.listener.incoming_list, channel_t *, ichan) { - tmp = ichan; - SMARTLIST_DEL_CURRENT(chan->u.listener.incoming_list, ichan); - channel_request_close(tmp); + channel_request_close(ichan); } SMARTLIST_FOREACH_END(ichan); smartlist_free(chan->u.listener.incoming_list); -- cgit v1.2.3-54-g00ecf From e877d02fdde73934e727346a9da11018681a4c14 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Mon, 8 Oct 2012 20:06:40 -0700 Subject: Eliminate unnecessary channel_set_cell_handler(), channel_set_var_cell_handler() in channel.c --- src/or/channel.c | 89 +------------------------------------------------------- src/or/channel.h | 5 ---- 2 files changed, 1 insertion(+), 93 deletions(-) (limited to 'src') diff --git a/src/or/channel.c b/src/or/channel.c index b3ab2f0733..f987caed52 100644 --- a/src/or/channel.c +++ b/src/or/channel.c @@ -916,50 +916,7 @@ void } /** - * Set the fixed-length cell handler for a channel - * - * This function sets the fixed-length cell handler for a channel and - * processes any incoming cells that had been blocked in the queue because - * none was available. - * - * @param chan Channel to set the fixed-length cell handler for - * @param cell_handler Function pointer to new fixed-length cell handler - */ - -void -channel_set_cell_handler(channel_t *chan, - void (*cell_handler)(channel_t *, cell_t *)) -{ - int changed = 0; - - tor_assert(chan); - tor_assert(!(chan->is_listener)); - tor_assert(chan->state == CHANNEL_STATE_OPENING || - chan->state == CHANNEL_STATE_OPEN || - chan->state == CHANNEL_STATE_MAINT); - - log_debug(LD_CHANNEL, - "Setting cell_handler callback for channel %p to %p", - chan, cell_handler); - - /* - * Keep track whether we've changed it so we know if there's any point in - * re-running the queue. - */ - if (cell_handler != chan->u.cell_chan.cell_handler) changed = 1; - - /* Change it */ - chan->u.cell_chan.cell_handler = cell_handler; - - /* Re-run the queue if we have one and there's any reason to */ - if (chan->u.cell_chan.cell_queue && - (smartlist_len(chan->u.cell_chan.cell_queue) > 0) && - changed && - chan->u.cell_chan.cell_handler) channel_process_cells(chan); -} - -/** - * Set the both cell handlers for a channel + * Set both cell handlers for a channel * * This function sets both the fixed-length and variable length cell handlers * for a channel and processes any incoming cells that had been blocked in the @@ -1010,50 +967,6 @@ channel_set_cell_handlers(channel_t *chan, chan->u.cell_chan.var_cell_handler)) channel_process_cells(chan); } -/** - * Set the variable-length cell handler for a channel - * - * This function sets the variable-length cell handler for a channel and - * processes any incoming cells that had been blocked in the queue because - * none was available. - * - * @param chan Channel to set the variable-length cell handler for - * @param cell_handler Function pointer to new variable-length cell handler - */ - -void -channel_set_var_cell_handler(channel_t *chan, - void (*var_cell_handler)(channel_t *, - var_cell_t *)) -{ - int changed = 0; - - tor_assert(chan); - tor_assert(!(chan->is_listener)); - tor_assert(chan->state == CHANNEL_STATE_OPENING || - chan->state == CHANNEL_STATE_OPEN || - chan->state == CHANNEL_STATE_MAINT); - - log_debug(LD_CHANNEL, - "Setting var_cell_handler callback for channel %p to %p", - chan, var_cell_handler); - - /* - * Keep track whether we've changed it so we know if there's any point in - * re-running the queue. - */ - if (var_cell_handler != chan->u.cell_chan.var_cell_handler) changed = 1; - - /* Change it */ - chan->u.cell_chan.var_cell_handler = var_cell_handler; - - /* Re-run the queue if we have one and there's any reason to */ - if (chan->u.cell_chan.cell_queue && - (smartlist_len(chan->u.cell_chan.cell_queue) > 0) && - changed && chan->u.cell_chan.var_cell_handler) - channel_process_cells(chan); -} - /** * Request a channel be closed * diff --git a/src/or/channel.h b/src/or/channel.h index 70759818fe..fbcd38d370 100644 --- a/src/or/channel.h +++ b/src/or/channel.h @@ -247,15 +247,10 @@ void (* channel_get_cell_handler(channel_t *chan)) (channel_t *, cell_t *); void (* channel_get_var_cell_handler(channel_t *chan)) (channel_t *, var_cell_t *); -void channel_set_cell_handler(channel_t *chan, - void (*cell_handler)(channel_t *, cell_t *)); void channel_set_cell_handlers(channel_t *chan, void (*cell_handler)(channel_t *, cell_t *), void (*var_cell_handler)(channel_t *, var_cell_t *)); -void channel_set_var_cell_handler(channel_t *chan, - void (*var_cell_handler)(channel_t *, - var_cell_t *)); /* Clean up closed channels periodically; called from run_scheduled_events() * in main.c -- cgit v1.2.3-54-g00ecf From 123a08e4a33f6750c3857c5c7ed3515bb196db53 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Mon, 8 Oct 2012 20:08:18 -0700 Subject: Simplify channel_next_with_digest() in channel.c --- src/or/channel.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'src') diff --git a/src/or/channel.c b/src/or/channel.c index f987caed52..2ca9a13238 100644 --- a/src/or/channel.c +++ b/src/or/channel.c @@ -622,15 +622,10 @@ channel_find_by_remote_digest(const char *identity_digest) channel_t * channel_next_with_digest(channel_t *chan) { - channel_t *rv = NULL; - tor_assert(chan); tor_assert(!(chan->is_listener)); - if (chan->u.cell_chan.next_with_same_id) - rv = chan->u.cell_chan.next_with_same_id; - - return rv; + return chan->u.cell_chan.next_with_same_id; } /** -- cgit v1.2.3-54-g00ecf From 8a41dd20cb43b16548b49f9eff581dba0ed9a9a1 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Mon, 8 Oct 2012 20:10:13 -0700 Subject: Make channel_force_free() static --- src/or/channel.c | 4 +++- src/or/channel.h | 1 - 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/or/channel.c b/src/or/channel.c index 2ca9a13238..469dc5d617 100644 --- a/src/or/channel.c +++ b/src/or/channel.c @@ -86,6 +86,8 @@ static ssize_t channel_flush_some_cells_from_outgoing_queue(channel_t *chan, ssize_t num_cells); +static void channel_force_free(channel_t *chan); + /*********************************** * Channel state utility functions * **********************************/ @@ -753,7 +755,7 @@ channel_free(channel_t *chan) * it should only be used from channel_free_all() when shutting down. */ -void +static void channel_force_free(channel_t *chan) { tor_assert(chan); diff --git a/src/or/channel.h b/src/or/channel.h index fbcd38d370..fac2b3bb9f 100644 --- a/src/or/channel.h +++ b/src/or/channel.h @@ -285,7 +285,6 @@ void channel_closed(channel_t *chan); /* Free a channel */ void channel_free(channel_t *chan); -void channel_force_free(channel_t *chan); /* State/metadata setters */ -- cgit v1.2.3-54-g00ecf From d61e58e1ba594c57afdb4bf49169f59b0a56a7f4 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Mon, 8 Oct 2012 20:14:04 -0700 Subject: s/channel_request_close()/channel_mark_for_close()/g for consistency --- src/or/channel.c | 12 ++++++------ src/or/channel.h | 2 +- src/or/channeltls.c | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/or/channel.c b/src/or/channel.c index 469dc5d617..5b40028a67 100644 --- a/src/or/channel.c +++ b/src/or/channel.c @@ -772,7 +772,7 @@ channel_force_free(channel_t *chan) if (chan->u.listener.incoming_list) { SMARTLIST_FOREACH_BEGIN(chan->u.listener.incoming_list, channel_t *, qchan) { - channel_request_close(qchan); + channel_mark_for_close(qchan); } SMARTLIST_FOREACH_END(qchan); smartlist_free(chan->u.listener.incoming_list); @@ -965,7 +965,7 @@ channel_set_cell_handlers(channel_t *chan, } /** - * Request a channel be closed + * Mark a channel to be closed * * This function tries to close a channel_t; it will go into the CLOSING * state, and eventually the lower layer should put it into the CLOSED or @@ -975,7 +975,7 @@ channel_set_cell_handlers(channel_t *chan, */ void -channel_request_close(channel_t *chan) +channel_mark_for_close(channel_t *chan) { tor_assert(chan != NULL); tor_assert(chan->close != NULL); @@ -2419,7 +2419,7 @@ channel_free_all(void) * and allocate that again on close. */ channel_unregister(curr); - channel_request_close(curr); + channel_mark_for_close(curr); channel_force_free(curr); } SMARTLIST_FOREACH_END(curr); @@ -2442,7 +2442,7 @@ channel_free_all(void) * and allocate that again on close. */ channel_unregister(curr); - channel_request_close(curr); + channel_mark_for_close(curr); channel_force_free(curr); } SMARTLIST_FOREACH_END(curr); @@ -2464,7 +2464,7 @@ channel_free_all(void) if (!(curr->state == CHANNEL_STATE_CLOSING || curr->state == CHANNEL_STATE_CLOSED || curr->state == CHANNEL_STATE_ERROR)) { - channel_request_close(curr); + channel_mark_for_close(curr); } channel_force_free(curr); } SMARTLIST_FOREACH_END(curr); diff --git a/src/or/channel.h b/src/or/channel.h index fac2b3bb9f..182639e939 100644 --- a/src/or/channel.h +++ b/src/or/channel.h @@ -230,7 +230,7 @@ const char * channel_state_to_string(channel_state_t state); /* Abstract channel operations */ -void channel_request_close(channel_t *chan); +void channel_mark_for_close(channel_t *chan); void channel_write_cell(channel_t *chan, cell_t *cell); void channel_write_packed_cell(channel_t *chan, packed_cell_t *cell); void channel_write_var_cell(channel_t *chan, var_cell_t *cell); diff --git a/src/or/channeltls.c b/src/or/channeltls.c index 883aef15f9..bb6ef9b4d5 100644 --- a/src/or/channeltls.c +++ b/src/or/channeltls.c @@ -224,7 +224,7 @@ channel_tls_free_all(void) if (channel_tls_listener) { base = TLS_CHAN_TO_BASE(channel_tls_listener); channel_unregister(base); - channel_request_close(base); + channel_mark_for_close(base); channel_free(base); channel_tls_listener = NULL; } @@ -316,7 +316,7 @@ channel_tls_close_method(channel_t *chan) if (chan->u.listener.incoming_list) { SMARTLIST_FOREACH_BEGIN(chan->u.listener.incoming_list, channel_t *, ichan) { - channel_request_close(ichan); + channel_mark_for_close(ichan); } SMARTLIST_FOREACH_END(ichan); smartlist_free(chan->u.listener.incoming_list); -- cgit v1.2.3-54-g00ecf From 4686638743919513baa72001d4676996f5e53c14 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Mon, 8 Oct 2012 20:15:09 -0700 Subject: Fix typo in comment --- src/or/channel.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/or/channel.h b/src/or/channel.h index 182639e939..8fb29d8508 100644 --- a/src/or/channel.h +++ b/src/or/channel.h @@ -12,7 +12,7 @@ #include "or.h" /* - * Channel struct; see thw channel_t typedef in or.h. A channel is an + * Channel struct; see the channel_t typedef in or.h. A channel is an * abstract interface for the OR-to-OR connection, similar to connection_or_t, * but without the strong coupling to the underlying TLS implementation. They * are constructed by calling a protocol-specific function to open a channel -- cgit v1.2.3-54-g00ecf From ee4e88e4d9891a24334870616067d21580f95704 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Mon, 8 Oct 2012 20:20:28 -0700 Subject: s/cell_queue/incoming_queue/g in channel.c for consistency with outgoing_queue --- src/or/channel.c | 66 ++++++++++++++++++++++++++++---------------------------- src/or/channel.h | 2 +- 2 files changed, 34 insertions(+), 34 deletions(-) (limited to 'src') diff --git a/src/or/channel.c b/src/or/channel.c index 5b40028a67..cdb0830c87 100644 --- a/src/or/channel.c +++ b/src/or/channel.c @@ -783,14 +783,14 @@ channel_force_free(channel_t *chan) smartlist_free(chan->u.cell_chan.active_circuit_pqueue); /* We might still have a cell queue; kill it */ - if (chan->u.cell_chan.cell_queue) { - SMARTLIST_FOREACH_BEGIN(chan->u.cell_chan.cell_queue, + if (chan->u.cell_chan.incoming_queue) { + SMARTLIST_FOREACH_BEGIN(chan->u.cell_chan.incoming_queue, cell_queue_entry_t *, q) { tor_free(q); } SMARTLIST_FOREACH_END(q); - smartlist_free(chan->u.cell_chan.cell_queue); - chan->u.cell_chan.cell_queue = NULL; + smartlist_free(chan->u.cell_chan.incoming_queue); + chan->u.cell_chan.incoming_queue = NULL; } /* Outgoing cell queue is similar, but we can have to free packed cells */ @@ -957,8 +957,8 @@ channel_set_cell_handlers(channel_t *chan, chan->u.cell_chan.var_cell_handler = var_cell_handler; /* Re-run the queue if we have one and there's any reason to */ - if (chan->u.cell_chan.cell_queue && - (smartlist_len(chan->u.cell_chan.cell_queue) > 0) && + if (chan->u.cell_chan.incoming_queue && + (smartlist_len(chan->u.cell_chan.incoming_queue) > 0) && try_again && (chan->u.cell_chan.cell_handler || chan->u.cell_chan.var_cell_handler)) channel_process_cells(chan); @@ -1633,8 +1633,8 @@ channel_change_state(channel_t *chan, channel_state_t to_state) if (!(chan->is_listener) && to_state == CHANNEL_STATE_OPEN) { /* Check for queued cells to process */ - if (chan->u.cell_chan.cell_queue && - smartlist_len(chan->u.cell_chan.cell_queue) > 0) + if (chan->u.cell_chan.incoming_queue && + smartlist_len(chan->u.cell_chan.incoming_queue) > 0) channel_process_cells(chan); if (chan->u.cell_chan.outgoing_queue && smartlist_len(chan->u.cell_chan.outgoing_queue) > 0) @@ -1646,8 +1646,8 @@ channel_change_state(channel_t *chan, channel_state_t to_state) tor_assert(!(chan->u.listener.incoming_list) || smartlist_len(chan->u.listener.incoming_list) == 0); } else { - tor_assert(!(chan->u.cell_chan.cell_queue) || - smartlist_len(chan->u.cell_chan.cell_queue) == 0); + tor_assert(!(chan->u.cell_chan.incoming_queue) || + smartlist_len(chan->u.cell_chan.incoming_queue) == 0); tor_assert(!(chan->u.cell_chan.outgoing_queue) || smartlist_len(chan->u.cell_chan.outgoing_queue) == 0); } @@ -1881,8 +1881,8 @@ channel_more_to_flush(channel_t *chan) tor_assert(!(chan->is_listener)); /* Check if we have any queued */ - if (chan->u.cell_chan.cell_queue && - smartlist_len(chan->u.cell_chan.cell_queue) > 0) return 1; + if (chan->u.cell_chan.incoming_queue && + smartlist_len(chan->u.cell_chan.incoming_queue) > 0) return 1; /* Check if any circuits would like to queue some */ if (chan->u.cell_chan.active_circuits) return 1; @@ -2108,13 +2108,13 @@ channel_process_cells(channel_t *chan) if (!(chan->u.cell_chan.cell_handler || chan->u.cell_chan.var_cell_handler)) return; /* Nothing we can do if we have no cells */ - if (!(chan->u.cell_chan.cell_queue)) return; + if (!(chan->u.cell_chan.incoming_queue)) return; /* * Process cells until we're done or find one we have no current handler * for. */ - SMARTLIST_FOREACH_BEGIN(chan->u.cell_chan.cell_queue, + SMARTLIST_FOREACH_BEGIN(chan->u.cell_chan.incoming_queue, cell_queue_entry_t *, q) { tor_assert(q); tor_assert(q->type == CELL_QUEUE_FIXED || @@ -2128,7 +2128,7 @@ channel_process_cells(channel_t *chan) "Processing incoming cell_t %p for channel %p", q->u.fixed.cell, chan); chan->u.cell_chan.cell_handler(chan, q->u.fixed.cell); - SMARTLIST_DEL_CURRENT(chan->u.cell_chan.cell_queue, q); + SMARTLIST_DEL_CURRENT(chan->u.cell_chan.incoming_queue, q); tor_free(q); } else if (q->type == CELL_QUEUE_VAR && chan->u.cell_chan.var_cell_handler) { @@ -2138,7 +2138,7 @@ channel_process_cells(channel_t *chan) "Processing incoming var_cell_t %p for channel %p", q->u.var.var_cell, chan); chan->u.cell_chan.var_cell_handler(chan, q->u.var.var_cell); - SMARTLIST_DEL_CURRENT(chan->u.cell_chan.cell_queue, q); + SMARTLIST_DEL_CURRENT(chan->u.cell_chan.incoming_queue, q); tor_free(q); } else { /* Can't handle this one */ @@ -2147,9 +2147,9 @@ channel_process_cells(channel_t *chan) } SMARTLIST_FOREACH_END(q); /* If the list is empty, free it */ - if (smartlist_len(chan->u.cell_chan.cell_queue) == 0 ) { - smartlist_free(chan->u.cell_chan.cell_queue); - chan->u.cell_chan.cell_queue = NULL; + if (smartlist_len(chan->u.cell_chan.incoming_queue) == 0 ) { + smartlist_free(chan->u.cell_chan.incoming_queue); + chan->u.cell_chan.incoming_queue = NULL; } } @@ -2176,13 +2176,13 @@ channel_queue_cell(channel_t *chan, cell_t *cell) /* Do we need to queue it, or can we just call the handler right away? */ if (!(chan->u.cell_chan.cell_handler)) need_to_queue = 1; - if (chan->u.cell_chan.cell_queue && - (smartlist_len(chan->u.cell_chan.cell_queue) > 0)) + if (chan->u.cell_chan.incoming_queue && + (smartlist_len(chan->u.cell_chan.incoming_queue) > 0)) need_to_queue = 1; /* If we need to queue and have no queue, create one */ - if (need_to_queue && !(chan->u.cell_chan.cell_queue)) { - chan->u.cell_chan.cell_queue = smartlist_new(); + if (need_to_queue && !(chan->u.cell_chan.incoming_queue)) { + chan->u.cell_chan.incoming_queue = smartlist_new(); } /* Timestamp for receiving */ @@ -2200,14 +2200,14 @@ channel_queue_cell(channel_t *chan, cell_t *cell) chan->u.cell_chan.cell_handler(chan, cell); } else { /* Otherwise queue it and then process the queue if possible. */ - tor_assert(chan->u.cell_chan.cell_queue); + tor_assert(chan->u.cell_chan.incoming_queue); q = tor_malloc(sizeof(*q)); q->type = CELL_QUEUE_FIXED; q->u.fixed.cell = cell; log_debug(LD_CHANNEL, "Queueing incoming cell_t %p for channel %p", cell, chan); - smartlist_add(chan->u.cell_chan.cell_queue, q); + smartlist_add(chan->u.cell_chan.incoming_queue, q); if (chan->u.cell_chan.cell_handler || chan->u.cell_chan.var_cell_handler) { channel_process_cells(chan); @@ -2238,13 +2238,13 @@ channel_queue_var_cell(channel_t *chan, var_cell_t *var_cell) /* Do we need to queue it, or can we just call the handler right away? */ if (!(chan->u.cell_chan.var_cell_handler)) need_to_queue = 1; - if (chan->u.cell_chan.cell_queue && - (smartlist_len(chan->u.cell_chan.cell_queue) > 0)) + if (chan->u.cell_chan.incoming_queue && + (smartlist_len(chan->u.cell_chan.incoming_queue) > 0)) need_to_queue = 1; /* If we need to queue and have no queue, create one */ - if (need_to_queue && !(chan->u.cell_chan.cell_queue)) { - chan->u.cell_chan.cell_queue = smartlist_new(); + if (need_to_queue && !(chan->u.cell_chan.incoming_queue)) { + chan->u.cell_chan.incoming_queue = smartlist_new(); } /* Timestamp for receiving */ @@ -2262,14 +2262,14 @@ channel_queue_var_cell(channel_t *chan, var_cell_t *var_cell) chan->u.cell_chan.var_cell_handler(chan, var_cell); } else { /* Otherwise queue it and then process the queue if possible. */ - tor_assert(chan->u.cell_chan.cell_queue); + tor_assert(chan->u.cell_chan.incoming_queue); q = tor_malloc(sizeof(*q)); q->type = CELL_QUEUE_VAR; q->u.var.var_cell = var_cell; log_debug(LD_CHANNEL, "Queueing incoming var_cell_t %p for channel %p", var_cell, chan); - smartlist_add(chan->u.cell_chan.cell_queue, q); + smartlist_add(chan->u.cell_chan.incoming_queue, q); if (chan->u.cell_chan.cell_handler || chan->u.cell_chan.var_cell_handler) { channel_process_cells(chan); @@ -2864,8 +2864,8 @@ channel_dump_statistics(channel_t *chan, int severity) " * Cell-bearing channel " U64_FORMAT " has %d queued incoming cells" " and %d queued outgoing cells", U64_PRINTF_ARG(chan->global_identifier), - (chan->u.cell_chan.cell_queue != NULL) ? - smartlist_len(chan->u.cell_chan.cell_queue) : 0, + (chan->u.cell_chan.incoming_queue != NULL) ? + smartlist_len(chan->u.cell_chan.incoming_queue) : 0, (chan->u.cell_chan.outgoing_queue != NULL) ? smartlist_len(chan->u.cell_chan.outgoing_queue) : 0); diff --git a/src/or/channel.h b/src/or/channel.h index 8fb29d8508..696ae07c0f 100644 --- a/src/or/channel.h +++ b/src/or/channel.h @@ -132,7 +132,7 @@ struct channel_s { channel_t *next_with_same_id, *prev_with_same_id; /* List of incoming cells to handle */ - smartlist_t *cell_queue; + smartlist_t *incoming_queue; /* List of queued outgoing cells */ smartlist_t *outgoing_queue; -- cgit v1.2.3-54-g00ecf From 89a00ee63ae61e707343432dd9f74702cf60ac3c Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Mon, 8 Oct 2012 20:49:19 -0700 Subject: Use typedefs for function pointer return values and s/listener/listener_fn/ for distinctness --- src/or/channel.c | 25 +++++++++++-------------- src/or/channel.h | 32 +++++++++++++++++++------------- src/or/command.c | 2 +- 3 files changed, 31 insertions(+), 28 deletions(-) (limited to 'src') diff --git a/src/or/channel.c b/src/or/channel.c index cdb0830c87..9fbfecb1d9 100644 --- a/src/or/channel.c +++ b/src/or/channel.c @@ -823,9 +823,8 @@ channel_force_free(channel_t *chan) * @return Function pointer to an incoming channel handler */ -void -(* channel_get_listener(channel_t *chan)) - (channel_t *, channel_t *) +channel_listener_fn_ptr +channel_get_listener_fn(channel_t *chan) { tor_assert(chan); tor_assert(chan->is_listener); @@ -847,8 +846,8 @@ void */ void -channel_set_listener(channel_t *chan, - void (*listener)(channel_t *, channel_t *) ) +channel_set_listener_fn(channel_t *chan, + channel_listener_fn_ptr listener) { tor_assert(chan); tor_assert(chan->is_listener); @@ -872,9 +871,8 @@ channel_set_listener(channel_t *chan, * @return A function pointer to chan's fixed-length cell handler, if any. */ -void -(* channel_get_cell_handler(channel_t *chan)) - (channel_t *, cell_t *) +channel_cell_handler_fn_ptr +channel_get_cell_handler(channel_t *chan) { tor_assert(chan); tor_assert(!(chan->is_listener)); @@ -897,9 +895,8 @@ void * @return A function pointer to chan's variable-length cell handler, if any. */ -void -(* channel_get_var_cell_handler(channel_t *chan)) - (channel_t *, var_cell_t *) +channel_var_cell_handler_fn_ptr +channel_get_var_cell_handler(channel_t *chan) { tor_assert(chan); tor_assert(!(chan->is_listener)); @@ -927,9 +924,9 @@ void void channel_set_cell_handlers(channel_t *chan, - void (*cell_handler)(channel_t *, cell_t *), - void (*var_cell_handler)(channel_t *, - var_cell_t *)) + channel_cell_handler_fn_ptr cell_handler, + channel_var_cell_handler_fn_ptr + var_cell_handler) { int try_again = 0; diff --git a/src/or/channel.h b/src/or/channel.h index 696ae07c0f..70ea30fcf1 100644 --- a/src/or/channel.h +++ b/src/or/channel.h @@ -11,6 +11,11 @@ #include "or.h" +/* Channel handler function pointer typedefs */ +typedef void (*channel_listener_fn_ptr)(channel_t *, channel_t *); +typedef void (*channel_cell_handler_fn_ptr)(channel_t *, cell_t *); +typedef void (*channel_var_cell_handler_fn_ptr)(channel_t *, var_cell_t *); + /* * Channel struct; see the channel_t typedef in or.h. A channel is an * abstract interface for the OR-to-OR connection, similar to connection_or_t, @@ -65,7 +70,7 @@ struct channel_s { union { struct { /* Registered listen handler to call on incoming connection */ - void (*listener)(channel_t *, channel_t *); + channel_listener_fn_ptr listener; /* List of pending incoming connections */ smartlist_t *incoming_list; @@ -78,8 +83,8 @@ struct channel_s { } listener; struct { /* Registered handlers for incoming cells */ - void (*cell_handler)(channel_t *, cell_t *); - void (*var_cell_handler)(channel_t *, var_cell_t *); + channel_cell_handler_fn_ptr cell_handler; + channel_var_cell_handler_fn_ptr var_cell_handler; /* Methods implemented by the lower layer */ @@ -238,19 +243,20 @@ void channel_write_var_cell(channel_t *chan, var_cell_t *cell); /* Channel callback registrations */ /* Listener callback */ -void (* channel_get_listener(channel_t *chan))(channel_t *, channel_t *); -void channel_set_listener(channel_t *chan, - void (*listener)(channel_t *, channel_t *) ); +channel_listener_fn_ptr channel_get_listener_fn(channel_t *chan); +void channel_set_listener_fn(channel_t *chan, + channel_listener_fn_ptr listener); /* Incoming cell callbacks */ -void (* channel_get_cell_handler(channel_t *chan)) - (channel_t *, cell_t *); -void (* channel_get_var_cell_handler(channel_t *chan)) - (channel_t *, var_cell_t *); +channel_cell_handler_fn_ptr channel_get_cell_handler(channel_t *chan); + +channel_var_cell_handler_fn_ptr +channel_get_var_cell_handler(channel_t *chan); + void channel_set_cell_handlers(channel_t *chan, - void (*cell_handler)(channel_t *, cell_t *), - void (*var_cell_handler)(channel_t *, - var_cell_t *)); + channel_cell_handler_fn_ptr cell_handler, + channel_var_cell_handler_fn_ptr + var_cell_handler); /* Clean up closed channels periodically; called from run_scheduled_events() * in main.c diff --git a/src/or/command.c b/src/or/command.c index 351ab08549..2fb70b5887 100644 --- a/src/or/command.c +++ b/src/or/command.c @@ -505,6 +505,6 @@ command_setup_listener(channel_t *listener) tor_assert(listener); tor_assert(listener->state == CHANNEL_STATE_LISTENING); - channel_set_listener(listener, command_handle_incoming_channel); + channel_set_listener_fn(listener, command_handle_incoming_channel); } -- cgit v1.2.3-54-g00ecf From 06a76d1db49d7c9c386368583928cde11b509519 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Mon, 8 Oct 2012 21:16:59 -0700 Subject: Refactor channel_write_cell()/channel_write_packed_cell()/channel_write_var_cell() to eliminate redundant code --- src/or/channel.c | 217 +++++++++++++++++++++++++++---------------------------- 1 file changed, 106 insertions(+), 111 deletions(-) (limited to 'src') diff --git a/src/or/channel.c b/src/or/channel.c index 9fbfecb1d9..fac4017927 100644 --- a/src/or/channel.c +++ b/src/or/channel.c @@ -74,6 +74,8 @@ static uint64_t n_channels_allocated = 0; */ static digestmap_t *channel_identity_map = NULL; +static int cell_queue_entry_is_padding(cell_queue_entry_t *q); + /* Functions to maintain the digest map */ static void channel_add_to_digest_map(channel_t *chan); static void channel_remove_from_digest_map(channel_t *chan); @@ -85,8 +87,9 @@ static void channel_remove_from_digest_map(channel_t *chan); static ssize_t channel_flush_some_cells_from_outgoing_queue(channel_t *chan, ssize_t num_cells); - static void channel_force_free(channel_t *chan); +static void +channel_write_cell_queue_entry(channel_t *chan, cell_queue_entry_t *q); /*********************************** * Channel state utility functions * @@ -1308,39 +1311,56 @@ channel_set_remote_end(channel_t *chan, } /** - * Write a cell to a channel - * - * Write a fixed-length cell to a channel using the write_cell() method. - * This is equivalent to the pre-channels connection_or_write_cell_to_buf(). - * - * @param chan Channel to write a cell to - * @param cell Cell to write to chan + * Check whether a cell queue entry is padding; this is a helper function + * for channel_write_cell_queue_entry() */ -void -channel_write_cell(channel_t *chan, cell_t *cell) +static int +cell_queue_entry_is_padding(cell_queue_entry_t *q) { - cell_queue_entry_t *q; - int sent = 0; + tor_assert(q); + + if (q->type == CELL_QUEUE_FIXED) { + if (q->u.fixed.cell) { + if (q->u.fixed.cell->command == CELL_PADDING || + q->u.fixed.cell->command == CELL_VPADDING) { + return 1; + } + } + } else if (q->type == CELL_QUEUE_VAR) { + if (q->u.var.var_cell) { + if (q->u.var.var_cell->command == CELL_PADDING || + q->u.var.var_cell->command == CELL_VPADDING) { + return 1; + } + } + } + + return 0; +} + +/** + * Given a cell_queue_entry_t filled out by the caller, try to send the cell + * and queue it if we can't. + */ + +static void +channel_write_cell_queue_entry(channel_t *chan, cell_queue_entry_t *q) +{ + int result = 0, sent = 0; + cell_queue_entry_t *tmp = NULL; tor_assert(chan); tor_assert(!(chan->is_listener)); - tor_assert(cell); - tor_assert(chan->u.cell_chan.write_cell); + tor_assert(q); /* Assert that the state makes sense for a cell write */ tor_assert(chan->state == CHANNEL_STATE_OPENING || chan->state == CHANNEL_STATE_OPEN || chan->state == CHANNEL_STATE_MAINT); - log_debug(LD_CHANNEL, - "Writing cell_t %p to channel %p with global ID " - U64_FORMAT, - cell, chan, U64_PRINTF_ARG(chan->global_identifier)); - /* Increment the timestamp unless it's padding */ - if (!(cell->command == CELL_PADDING || - cell->command == CELL_VPADDING)) { + if (!cell_queue_entry_is_padding(q)) { chan->u.cell_chan.timestamp_last_added_nonpadding = approx_time(); } @@ -1348,7 +1368,31 @@ channel_write_cell(channel_t *chan, cell_t *cell) if (!(chan->u.cell_chan.outgoing_queue && (smartlist_len(chan->u.cell_chan.outgoing_queue) > 0)) && chan->state == CHANNEL_STATE_OPEN) { - if (chan->u.cell_chan.write_cell(chan, cell)) { + /* Pick the right write function for this cell type and save the result */ + switch (q->type) { + case CELL_QUEUE_FIXED: + tor_assert(chan->u.cell_chan.write_cell); + tor_assert(q->u.fixed.cell); + result = chan->u.cell_chan.write_cell(chan, q->u.fixed.cell); + break; + case CELL_QUEUE_PACKED: + tor_assert(chan->u.cell_chan.write_packed_cell); + tor_assert(q->u.packed.packed_cell); + result = chan-> + u.cell_chan.write_packed_cell(chan, + q->u.packed.packed_cell); + break; + case CELL_QUEUE_VAR: + tor_assert(chan->u.cell_chan.write_var_cell); + tor_assert(q->u.var.var_cell); + result = chan->u.cell_chan.write_var_cell(chan, q->u.var.var_cell); + break; + default: + tor_assert(1); + } + + /* Check if we got it out */ + if (result > 0) { sent = 1; /* Timestamp for transmission */ channel_timestamp_xmit(chan); @@ -1363,39 +1407,56 @@ channel_write_cell(channel_t *chan, cell_t *cell) /* Not sent, queue it */ if (!(chan->u.cell_chan.outgoing_queue)) chan->u.cell_chan.outgoing_queue = smartlist_new(); - q = tor_malloc(sizeof(*q)); - q->type = CELL_QUEUE_FIXED; - q->u.fixed.cell = cell; - smartlist_add(chan->u.cell_chan.outgoing_queue, q); + /* + * We have to copy the queue entry passed in, since the caller probably + * used the stack. + */ + tmp = tor_malloc(sizeof(*tmp)); + memcpy(tmp, q, sizeof(*tmp)); + smartlist_add(chan->u.cell_chan.outgoing_queue, tmp); /* Try to process the queue? */ if (chan->state == CHANNEL_STATE_OPEN) channel_flush_cells(chan); } } +/** + * Write a cell to a channel + * + * Write a fixed-length cell to a channel using the write_cell() method. + * This is equivalent to the pre-channels connection_or_write_cell_to_buf(). + */ + +void +channel_write_cell(channel_t *chan, cell_t *cell) +{ + cell_queue_entry_t q; + + tor_assert(chan); + tor_assert(cell); + + log_debug(LD_CHANNEL, + "Writing cell_t %p to channel %p with global ID " + U64_FORMAT, + cell, chan, U64_PRINTF_ARG(chan->global_identifier)); + + q.type = CELL_QUEUE_FIXED; + q.u.fixed.cell = cell; + channel_write_cell_queue_entry(chan, &q); +} + /** * Write a packed cell to a channel * * Write a packed cell to a channel using the write_cell() method. - * - * @param chan Channel to write a cell to - * @param packed_cell Cell to write to chan */ void channel_write_packed_cell(channel_t *chan, packed_cell_t *packed_cell) { - cell_queue_entry_t *q; - int sent = 0; + cell_queue_entry_t q; tor_assert(chan); - tor_assert(!(chan->is_listener)); tor_assert(packed_cell); - tor_assert(chan->u.cell_chan.write_packed_cell); - - /* Assert that the state makes sense for a cell write */ - tor_assert(chan->state == CHANNEL_STATE_OPENING || - chan->state == CHANNEL_STATE_OPEN || - chan->state == CHANNEL_STATE_MAINT); log_debug(LD_CHANNEL, "Writing packed_cell_t %p to channel %p with global ID " @@ -1403,35 +1464,9 @@ channel_write_packed_cell(channel_t *chan, packed_cell_t *packed_cell) packed_cell, chan, U64_PRINTF_ARG(chan->global_identifier)); - /* Increment the timestamp */ - chan->u.cell_chan.timestamp_last_added_nonpadding = approx_time(); - - /* Can we send it right out? If so, try */ - if (!(chan->u.cell_chan.outgoing_queue && - (smartlist_len(chan->u.cell_chan.outgoing_queue) > 0)) && - chan->state == CHANNEL_STATE_OPEN) { - if (chan->u.cell_chan.write_packed_cell(chan, packed_cell)) { - sent = 1; - /* Timestamp for transmission */ - channel_timestamp_xmit(chan); - /* If we're here the queue is empty, so it's drained too */ - channel_timestamp_drained(chan); - /* Update the counter */ - ++(chan->u.cell_chan.n_cells_xmitted); - } - } - - if (!sent) { - /* Not sent, queue it */ - if (!(chan->u.cell_chan.outgoing_queue)) - chan->u.cell_chan.outgoing_queue = smartlist_new(); - q = tor_malloc(sizeof(*q)); - q->type = CELL_QUEUE_PACKED; - q->u.packed.packed_cell = packed_cell; - smartlist_add(chan->u.cell_chan.outgoing_queue, q); - /* Try to process the queue? */ - if (chan->state == CHANNEL_STATE_OPEN) channel_flush_cells(chan); - } + q.type = CELL_QUEUE_PACKED; + q.u.packed.packed_cell = packed_cell; + channel_write_cell_queue_entry(chan, &q); } /** @@ -1440,26 +1475,15 @@ channel_write_packed_cell(channel_t *chan, packed_cell_t *packed_cell) * Write a variable-length cell to a channel using the write_cell() method. * This is equivalent to the pre-channels * connection_or_write_var_cell_to_buf(). - * - * @param chan Channel to write a cell to - * @param var_cell Cell to write to chan */ void channel_write_var_cell(channel_t *chan, var_cell_t *var_cell) { - cell_queue_entry_t *q; - int sent = 0; + cell_queue_entry_t q; tor_assert(chan); - tor_assert(!(chan->is_listener)); tor_assert(var_cell); - tor_assert(chan->u.cell_chan.write_var_cell); - - /* Assert that the state makes sense for a cell write */ - tor_assert(chan->state == CHANNEL_STATE_OPENING || - chan->state == CHANNEL_STATE_OPEN || - chan->state == CHANNEL_STATE_MAINT); log_debug(LD_CHANNEL, "Writing var_cell_t %p to channel %p with global ID " @@ -1467,38 +1491,9 @@ channel_write_var_cell(channel_t *chan, var_cell_t *var_cell) var_cell, chan, U64_PRINTF_ARG(chan->global_identifier)); - /* Increment the timestamp unless it's padding */ - if (!(var_cell->command == CELL_PADDING || - var_cell->command == CELL_VPADDING)) { - chan->u.cell_chan.timestamp_last_added_nonpadding = approx_time(); - } - - /* Can we send it right out? If so, then try */ - if (!(chan->u.cell_chan.outgoing_queue && - (smartlist_len(chan->u.cell_chan.outgoing_queue) > 0)) && - chan->state == CHANNEL_STATE_OPEN) { - if (chan->u.cell_chan.write_var_cell(chan, var_cell)) { - sent = 1; - /* Timestamp for transmission */ - channel_timestamp_xmit(chan); - /* If we're here the queue is empty, so it's drained too */ - channel_timestamp_drained(chan); - /* Update the counter */ - ++(chan->u.cell_chan.n_cells_xmitted); - } - } - - if (!sent) { - /* Not sent, queue it */ - if (!(chan->u.cell_chan.outgoing_queue)) - chan->u.cell_chan.outgoing_queue = smartlist_new(); - q = tor_malloc(sizeof(*q)); - q->type = CELL_QUEUE_VAR; - q->u.var.var_cell = var_cell; - smartlist_add(chan->u.cell_chan.outgoing_queue, q); - /* Try to process the queue? */ - if (chan->state == CHANNEL_STATE_OPEN) channel_flush_cells(chan); - } + q.type = CELL_QUEUE_VAR; + q.u.var.var_cell = var_cell; + channel_write_cell_queue_entry(chan, &q); } /** -- cgit v1.2.3-54-g00ecf From 6391f963fb1ff9988d048397cd054b65ff5a52ff Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Mon, 8 Oct 2012 21:30:07 -0700 Subject: Conform to existing Doxygen style --- src/or/channel.c | 265 ++++------------------------------------------------ src/or/channeltls.c | 98 +------------------ 2 files changed, 18 insertions(+), 345 deletions(-) (limited to 'src') diff --git a/src/or/channel.c b/src/or/channel.c index fac4017927..57b9e4feb5 100644 --- a/src/or/channel.c +++ b/src/or/channel.c @@ -97,9 +97,6 @@ channel_write_cell_queue_entry(channel_t *chan, cell_queue_entry_t *q); /** * Indicate whether a given channel state is valid - * - * @param state A channel state - * @return A boolean value indicating whether state is a valid channel state */ int @@ -131,10 +128,6 @@ channel_state_is_valid(channel_state_t state) * This function takes two channel states and indicates whether a * transition between them is permitted (see the state definitions and * transition table in or.h at the channel_state_t typedef). - * - * @param from Proposed state to transition from - * @param to Proposed state to transition to - * @return A boolean value indicating whether the posposed transition is valid */ int @@ -183,9 +176,6 @@ channel_state_can_transition(channel_state_t from, channel_state_t to) /** * Return a human-readable description for a channel state - * - * @param state A channel state - * @return A pointer to a string with a human-readable description of state */ const char * @@ -232,8 +222,6 @@ channel_state_to_string(channel_state_t state) * * This function registers a newly created channel in the global lists/maps * of active channels. - * - * @param chan A pointer to an unregistered channel */ void @@ -306,8 +294,6 @@ channel_register(channel_t *chan) * * This function removes a channel from the global lists and maps and is used * when freeing a closed/errored channel. - * - * @param chan A pointer to a channel to be unregistered */ void @@ -363,8 +349,6 @@ channel_unregister(channel_t *chan) * This function adds a channel to the digest map and inserts it into the * correct linked list if channels with that remote endpoint identity digest * already exist. - * - * @param chan A pointer to a channel to add */ static void @@ -416,8 +400,6 @@ channel_add_to_digest_map(channel_t *chan) * * This function removes a channel from the digest map and the linked list of * channels for that digest if more than one exists. - * - * @param chan A pointer to a channel to remove */ static void @@ -563,10 +545,6 @@ channel_remove_from_digest_map(channel_t *chan) * This function searches for a channel by the global_identifier assigned * at initialization time. This identifier is unique for the lifetime of the * Tor process. - * - * @param global_identifier The global_identifier value to search for - * @return A pointer to the channel with that global identifier, or NULL if - * none exists. */ channel_t * @@ -593,9 +571,6 @@ channel_find_by_global_id(uint64_t global_identifier) * the channel digest map. It's possible that more than one channel to a * given endpoint exists. Use channel_next_with_digest() and * channel_prev_with_digest() to walk the list. - * - * @param identity_digest A digest to search for - * @return A channel pointer, or NULL if no channel to this endpoint exists. */ channel_t * @@ -614,14 +589,10 @@ channel_find_by_remote_digest(const char *identity_digest) } /** - * Next channel with digest + * Get next channel with digest * * This function takes a channel and finds the next channel in the list * with the same digest. - * - * @param chan Channel pointer to iterate - * @return A pointer to the next channel after chan with the same remote - * endpoint identity digest, or NULL if none exists. */ channel_t * @@ -634,14 +605,10 @@ channel_next_with_digest(channel_t *chan) } /** - * Previous channel with digest + * Get previous channel with digest * * This function takes a channel and finds the previos channel in the list * with the same digest. - * - * @param chan Channel pointer to iterate - * @return A pointer to the previous channel after chan with the same remote - * endpoint identity digest, or NULL if none exists. */ channel_t * @@ -659,13 +626,11 @@ channel_prev_with_digest(channel_t *chan) } /** - * Internal-only channel init function for cell channels + * Initialize a cell channel * * This function should be called by subclasses to set up some per-channel * variables. I.e., this is the superclass constructor. Before this, the * channel should be allocated with tor_malloc_zero(). - * - * @param chan Pointer to a channel to initialize. */ void @@ -690,13 +655,11 @@ channel_init_for_cells(channel_t *chan) } /** - * Internal-only channel init function for listener channels + * Initialize a listener channel * * This function should be called by subclasses to set up some per-channel * variables. I.e., this is the superclass constructor. Before this, the * channel should be allocated with tor_malloc_zero(). - * - * @param chan Pointer to a channel to initialize. */ void @@ -715,12 +678,8 @@ channel_init_listener(channel_t *chan) } /** - * Internal-only channel free function - * - * Nothing outside of channel.c should call this; it frees channels after - * they have closed and been unregistered. - * - * @param chan Channel to free + * Free a channel; nothing outside of channel.c and subclasses should call + * this - it frees channels after they have closed and been unregistered. */ void @@ -752,10 +711,9 @@ channel_free(channel_t *chan) } /** - * Internal-only forcible channel free function - * - * This is like channel_free, but doesn't do the state/registration asserts; - * it should only be used from channel_free_all() when shutting down. + * Free a channel and skip the state/reigstration asserts; this internal- + * use-only function should be called only from channel_free_all() when + * shutting down the Tor process. */ static void @@ -821,9 +779,6 @@ channel_force_free(channel_t *chan) * * This function returns a function pointer to the current registered * handler for new incoming channels on a listener channel. - * - * @param chan Channel to get listener for - * @return Function pointer to an incoming channel handler */ channel_listener_fn_ptr @@ -843,9 +798,6 @@ channel_get_listener_fn(channel_t *chan) * * This function sets the handler for new incoming channels on a listener * channel. - * - * @param chan Listener channel to set handler on - * @param listener Function pointer to new incoming channel handler */ void @@ -869,9 +821,6 @@ channel_set_listener_fn(channel_t *chan, * * This function gets the handler for incoming fixed-length cells installed * on a channel. - * - * @param chan Channel to get the fixed-length cell handler for - * @return A function pointer to chan's fixed-length cell handler, if any. */ channel_cell_handler_fn_ptr @@ -893,9 +842,6 @@ channel_get_cell_handler(channel_t *chan) * * This function gets the handler for incoming variable-length cells * installed on a channel. - * - * @param chan Channel to get the variable-length cell handler for - * @return A function pointer to chan's variable-length cell handler, if any. */ channel_var_cell_handler_fn_ptr @@ -918,11 +864,6 @@ channel_get_var_cell_handler(channel_t *chan) * This function sets both the fixed-length and variable length cell handlers * for a channel and processes any incoming cells that had been blocked in the * queue because none were available. - * - * @param chan Channel to set the fixed-length cell handler for - * @param cell_handler Function pointer to new fixed-length cell handler - * @param var_cell_handler Function pointer to new variable-length cell - handler */ void @@ -965,13 +906,11 @@ channel_set_cell_handlers(channel_t *chan, } /** - * Mark a channel to be closed + * Mark a channel for closure * * This function tries to close a channel_t; it will go into the CLOSING * state, and eventually the lower layer should put it into the CLOSED or * ERROR state. Then, channel_run_cleanup() will eventually free it. - * - * @param chan Channel to close */ void @@ -1012,8 +951,6 @@ channel_mark_for_close(channel_t *chan) * Notify the channel code that the channel is being closed due to a non-error * condition in the lower layer. This does not call the close() method, since * the lower layer already knows. - * - * @param chan Channel to notify for close */ void @@ -1043,8 +980,6 @@ channel_close_from_lower_layer(channel_t *chan) * This function is called by the lower layer implementing the transport * when a channel must be closed due to an error condition. This does not * call the channel's close method, since the lower layer already knows. - * - * @param chan Channel to notify for error */ void @@ -1074,8 +1009,6 @@ channel_close_for_error(channel_t *chan) * This function should be called by the lower layer when a channel * is finished closing and it should be regarded as inactive and * freed by the channel code. - * - * @param chan Channel to notify closure on */ void @@ -1110,8 +1043,6 @@ channel_closed(channel_t *chan) * * This function clears the identity digest of the remote endpoint for a * channel; this is intended for use by the lower layer. - * - * @param chan Channel to clear */ void @@ -1147,9 +1078,6 @@ channel_clear_identity_digest(channel_t *chan) * * This function sets the identity digest of the remote endpoint for a * channel; this is intended for use by the lower layer. - * - * @param chan Channel to clear - * @param identity_digest New identity digest for chan */ void @@ -1208,8 +1136,6 @@ channel_set_identity_digest(channel_t *chan, * * This function clears all the remote end info from a channel; this is * intended for use by the lower layer. - * - * @param chan Channel to clear */ void @@ -1246,10 +1172,6 @@ channel_clear_remote_end(channel_t *chan) * * This function sets new remote end info on a channel; this is intended * for use by the lower layer. - * - * @chan Channel to set data on - * @chan identity_digest New identity digest for chan - * @chan nickname New remote nickname for chan */ void @@ -1340,6 +1262,8 @@ cell_queue_entry_is_padding(cell_queue_entry_t *q) } /** + * Write to a channel based on a cell_queue_entry_t + * * Given a cell_queue_entry_t filled out by the caller, try to send the cell * and queue it if we can't. */ @@ -1502,9 +1426,6 @@ channel_write_var_cell(channel_t *chan, var_cell_t *var_cell) * This internal and subclass use only function is used to change channel * state, performing all transition validity checks and whatever actions * are appropriate to the state transition in question. - * - * @param chan Channel to change state on - * @param to_state State to change chan to */ void @@ -1653,10 +1574,6 @@ channel_change_state(channel_t *chan, channel_state_t to_state) * it will try to write up to num_cells cells from the channel's cell queue or * from circuits active on that channel, or as many as it has available if * num_cells == -1. - * - * @param chan Channel to flush from - * @param num_cells Maximum number of cells to flush, or -1 for unlimited - * @return Number of cells flushed */ #define MAX_CELLS_TO_GET_FROM_CIRCUITS_FOR_UNLIMITED 256 @@ -1701,14 +1618,10 @@ channel_flush_some_cells(channel_t *chan, ssize_t num_cells) } /** - * Flush cells from just the channel's out going cell queue + * Flush cells from just the channel's outgoing cell queue * * This gets called from channel_flush_some_cells() above to flush cells * just from the queue without trying for active_circuits. - * - * @param chan Channel to flush from - * @param num_cells Maximum number of cells to flush, or -1 for unlimited - * @return Number of cells flushed */ static ssize_t @@ -1841,13 +1754,11 @@ channel_flush_some_cells_from_outgoing_queue(channel_t *chan, } /** - * Try to flush as many cells as we possibly can from the queue + * Flush as many cells as we possibly can from the queue * * This tries to flush as many cells from the queue as the lower layer * will take. It just calls channel_flush_some_cells_from_outgoing_queue() * in unlimited mode. - * - * @param chan Channel to flush */ void @@ -1861,9 +1772,6 @@ channel_flush_cells(channel_t *chan) * * This gets used from the lower layer to check if any more cells are * available. - * - * @param chan Channel to check on - * @return 1 if cells are available, 0 otherwise */ int @@ -1888,8 +1796,6 @@ channel_more_to_flush(channel_t *chan) * * Connection.c will call this when we've flushed the output; there's some * dirreq-related maintenance to do. - * - * @param chan Channel to notify */ void @@ -1909,8 +1815,6 @@ channel_notify_flushed(channel_t *chan) * * Use a listener's registered callback to process as many entries in the * queue of incoming channels as possible. - * - * @param listener Pointer to a listening channel. */ void @@ -1965,8 +1869,6 @@ channel_process_incoming(channel_t *listener) * until there is positive confirmation that the network is operational. * In particular, anything UDP-based should not make this transition until a * packet is received from the other side. - * - * @param chan Channel that has become open */ void @@ -2016,9 +1918,6 @@ channel_do_open_actions(channel_t *chan) * Internal and subclass use only function to queue an incoming channel from * a listening one. A subclass of channel_t should call this when a new * incoming channel is created. - * - * @param listener Listening channel to queue on - * @param incoming New incoming channel */ void @@ -2079,8 +1978,6 @@ channel_queue_incoming(channel_t *listener, channel_t *incoming) * * Process as many queued cells as we can from the incoming * cell queue. - * - * @param chan Channel to process incoming cell queue on */ void @@ -2150,9 +2047,6 @@ channel_process_cells(channel_t *chan) * * This should be called by a channel_t subclass to queue an incoming fixed- * length cell for processing, and process it if possible. - * - * @param chan Channel the cell is arriving on - * @param cell Incoming cell to queue and process */ void @@ -2212,9 +2106,6 @@ channel_queue_cell(channel_t *chan, cell_t *cell) * * This should be called by a channel_t subclass to queue an incoming * variable-length cell for processing, and process it if possible. - * - * @param chan Channel the cell is arriving on - * @param var_cell Incoming cell to queue and process */ void @@ -2275,11 +2166,6 @@ channel_queue_var_cell(channel_t *chan, var_cell_t *var_cell) * Write a destroy cell with circ ID circ_id and reason reason * onto channel chan. Don't perform range-checking on reason: * we may want to propagate reasons from other cells. - * - * @param circ_id Circuit ID to destroy - * @param chan Channel to send on - * @param reason Reason code - * @return Always 0 */ int @@ -2302,7 +2188,7 @@ channel_send_destroy(circid_t circ_id, channel_t *chan, int reason) } /** - * Channel statistics + * Dump channel statistics to the log * * This is called from dumpstats() in main.c and spams the log with * statistics on channels. @@ -2337,7 +2223,7 @@ channel_dumpstats(int severity) } /** - * Channel cleanup + * Clean up channels * * This gets called periodically from run_scheduled_events() in main.c; * it cleans up after closed channels. @@ -2487,11 +2373,6 @@ channel_free_all(void) * or make a new type including a tor_addr_t and port, so we have a * single abstract object encapsulating all the protocol details of * how to contact an OR. - * - * @param addr Address of remote node to establish a channel to - * @param port ORport of remote OR - * @param id_digest Identity digest of remote OR - * @return New channel, or NULL if failure */ channel_t * @@ -2510,12 +2391,6 @@ channel_connect(const tor_addr_t *addr, uint16_t port, * number of circuits and the age are used as tie-breakers. * * This is based on the former connection_or_is_better() of connection_or.c - * - * @param now Current time to use for deciding grace period for new channels - * @param a Channel A for comparison - * @param b Channel B for comparison - * @param forgive_new_connections Whether to use grace period for new channels - * @return 1 iff a is better than b */ int @@ -2576,13 +2451,6 @@ channel_is_better(time_t now, channel_t *a, channel_t *b, * channel, set *msg_out to a message describing the channel's state * and our next action, and set *launch_out to a boolean indicated whether * the caller should try to launch a new channel with channel_connect(). - * - * @param digest Endpoint digest we want - * @param target_addr Endpoint address we want - * @param msg_out Write out status message here if we fail - * @param launch_out Write 1 here if caller should try to connect a new - * channel. - * @return Pointer to selected channel, or NULL if none available */ channel_t * @@ -2869,7 +2737,6 @@ channel_dump_statistics(channel_t *chan, int severity) (chan->u.cell_chan.active_circuit_pqueue != NULL) ? smartlist_len(chan->u.cell_chan.active_circuit_pqueue) : 0, chan->u.cell_chan.n_circuits); - /* TODO better circuit info once circuit structure refactor is finished */ /* Describe timestamps */ log(severity, LD_GENERAL, @@ -2963,9 +2830,6 @@ channel_dump_transport_statistics(channel_t *chan, int severity) * This function return a test provided by the lower layer of the remote * endpoint for this channel; it should specify the actual address connected * to/from. - * - * @param chan Channel to describe - * @return Pointer to string description */ const char * @@ -2985,9 +2849,6 @@ channel_get_actual_remote_descr(channel_t *chan) * This function return a test provided by the lower layer of the remote * endpoint for this channel; it should use the known canonical address for * this OR's identity digest if possible. - * - * @param chan Channel to describe - * @return Pointer to string description */ const char * @@ -3006,10 +2867,6 @@ channel_get_canonical_remote_descr(channel_t *chan) * * Write the remote address out to a tor_addr_t if the underlying transport * supports this operation. - * - * @param chan Channel to request remote address from - * @param addr_out Write the address out here - * @return 1 if successful, 0 if not */ int @@ -3030,9 +2887,6 @@ channel_get_addr_if_possible(channel_t *chan, tor_addr_t *addr_out) * * Indicate if either we have queued cells, or if not, whether the underlying * lower-layer transport thinks it has an output queue. - * - * @param chan Channel to query - * @return 1 if there are queued writes, 0 otherwise */ int @@ -3060,9 +2914,6 @@ channel_has_queued_writes(channel_t *chan) * * This function returns the is_bad_for_new_circs flag of the specified * channel. - * - * @param chan Channel to get flag on - * @return Flag value */ int @@ -3078,8 +2929,6 @@ channel_is_bad_for_new_circs(channel_t *chan) * Mark a channel as bad for new circuits * * Set the is_bad_for_new_circs_flag on chan. - * - * @param chan Channel to mark */ void @@ -3097,9 +2946,6 @@ channel_mark_bad_for_new_circs(channel_t *chan) * This returns the client flag of a channel, which will be set if * command_process_create_cell() in command.c thinks this is a connection * from a client. - * - * @param chan Channel to query flag - * @return Flag value */ int @@ -3115,8 +2961,6 @@ channel_is_client(channel_t *chan) * Set the client flag * * Mark a channel as being from a client - * - * @param chan Channel to mark */ void @@ -3133,9 +2977,6 @@ channel_mark_client(channel_t *chan) * * This returns the is_canonical for a channel; this flag is determined by * the lower layer and can't be set in a transport-independent way. - * - * @param chan Channel to query - * @return Flag value */ int @@ -3153,10 +2994,6 @@ channel_is_canonical(channel_t *chan) * * This function asks if the lower layer thinks it's safe to trust the * result of channel_is_canonical() - * - * @param chan Channel to query - * @return 1 if the lower layer thinks the is_canonical flag is reliable, 0 - * otherwise. */ int @@ -3174,9 +3011,6 @@ channel_is_canonical_is_reliable(channel_t *chan) * * This function gets the incoming flag; this is set when a listener spawns * a channel. If this returns true the channel was remotely initiated. - * - * @param chan Channel to query - * @return Flag value */ int @@ -3193,8 +3027,6 @@ channel_is_incoming(channel_t *chan) * * This function is called when a channel arrives on a listening channel * to mark it as incoming. - * - * @param chan Channel to mark */ void @@ -3214,9 +3046,6 @@ channel_mark_incoming(channel_t *chan) * destinations it will communicate with on behalf of this channel. It's * used to decide whether to declare the network reachable when seeing incoming * traffic on the channel. - * - * @param chan Channel to query - * @return Flag value */ int @@ -3234,8 +3063,6 @@ channel_is_local(channel_t *chan) * This internal-only function should be called by the lower layer if the * channel is to a local address. See channel_is_local() above or the * description of the is_local bit in channel.h - * - * @param chan Channel to mark */ void @@ -3253,9 +3080,6 @@ channel_mark_local(channel_t *chan) * This function gets the outgoing flag; this is the inverse of the incoming * bit set when a listener spawns a channel. If this returns true the channel * was locally initiated. - * - * @param chan Channel to query - * @return Flag value */ int @@ -3272,8 +3096,6 @@ channel_is_outgoing(channel_t *chan) * * This function clears the incoming flag and thus marks a channel as * outgoing. - * - * @param chan Channel to mark */ void @@ -3294,8 +3116,6 @@ channel_mark_outgoing(channel_t *chan) * * This updates the channel's created timestamp and should only be called * from channel_init(). - * - * @param chan Channel to update */ void @@ -3317,8 +3137,6 @@ channel_timestamp_created(channel_t *chan) * is also updated from channel_timestamp_recv() and channel_timestamp_xmit(), * but it should be updated for things like the v3 handshake and stuff that * produce activity only visible to the lower layer. - * - * @param chan Channel to update */ void @@ -3336,8 +3154,6 @@ channel_timestamp_active(channel_t *chan) * * This function updates the channel's last accepted timestamp; it should be * called whenever a new incoming channel is accepted on a listener. - * - * @param chan Channel to update */ void @@ -3356,8 +3172,6 @@ channel_timestamp_accepted(channel_t *chan) * * This function is called by relay.c to timestamp a channel that appears to * be used as a client. - * - * @param chan Channel to update */ void @@ -3376,8 +3190,6 @@ channel_timestamp_client(channel_t *chan) * * This is called whenever we transmit a cell which leaves the outgoing cell * queue completely empty. It also updates the xmit time and the active time. - * - * @param chan Channel to update */ void @@ -3398,8 +3210,6 @@ channel_timestamp_drained(channel_t *chan) * * This is called whenever we get an incoming cell from the lower layer. * This also updates the active timestamp. - * - * @param chan Channel to update */ void @@ -3418,8 +3228,6 @@ channel_timestamp_recv(channel_t *chan) * Update the xmit timestamp * This is called whenever we pass an outgoing cell to the lower layer. This * also updates the active timestamp. - * - * @param chan Channel to update */ void @@ -3440,9 +3248,6 @@ channel_timestamp_xmit(channel_t *chan) /** * Query created timestamp - * - * @param chan Channel to query - * @return Created timestamp value for chan */ time_t @@ -3455,9 +3260,6 @@ channel_when_created(channel_t *chan) /** * Query last active timestamp - * - * @param chan Channel to query - * @return Last active timestamp value for chan */ time_t @@ -3470,9 +3272,6 @@ channel_when_last_active(channel_t *chan) /** * Query last accepted timestamp - * - * @param chan Channel to query - * @return Last accepted timestamp value for chan */ time_t @@ -3486,9 +3285,6 @@ channel_when_last_accepted(channel_t *chan) /** * Query client timestamp - * - * @param chan Channel to query - * @return Client timestamp value for chan */ time_t @@ -3502,9 +3298,6 @@ channel_when_last_client(channel_t *chan) /** * Query drained timestamp - * - * @param chan Channel to query - * @return drained timestamp value for chan */ time_t @@ -3518,9 +3311,6 @@ channel_when_last_drained(channel_t *chan) /** * Query recv timestamp - * - * @param chan Channel to query - * @return Recv timestamp value for chan */ time_t @@ -3534,9 +3324,6 @@ channel_when_last_recv(channel_t *chan) /** * Query xmit timestamp - * - * @param chan Channel to query - * @return Xmit timestamp value for chan */ time_t @@ -3550,9 +3337,6 @@ channel_when_last_xmit(channel_t *chan) /** * Query accepted counter - * - * @param chan Channel to query - * @return Number of incoming channels accepted by this listener */ uint64_t @@ -3566,9 +3350,6 @@ channel_count_accepted(channel_t *chan) /** * Query received cell counter - * - * @param chan Channel to query - * @return Number of cells received by this channel */ uint64_t @@ -3582,9 +3363,6 @@ channel_count_recved(channel_t *chan) /** * Query transmitted cell counter - * - * @param chan Channel to query - * @return Number of cells transmitted by this channel */ uint64_t @@ -3601,10 +3379,6 @@ channel_count_xmitted(channel_t *chan) * * This function calls the lower layer and asks if this channel matches a * given extend_info_t. - * - * @param chan Channel to test - * @param extend_info Pointer to extend_info_t to match - * @return 1 if they match, 0 otherwise */ int @@ -3623,10 +3397,6 @@ channel_matches_extend_info(channel_t *chan, extend_info_t *extend_info) * * This function calls into the lower layer and asks if this channel thinks * it matches a given target address for circuit extension purposes. - * - * @param chan Channel to test - * @param target Address to match - * @return 1 if they match, 0 otherwise */ int @@ -3646,9 +3416,6 @@ channel_matches_target_addr_for_extend(channel_t *chan, * * This is called when setting up a channel and replaces the old * connection_or_set_circid_type() - * - * @param chan Channel to set up - * @param identity_rcvd Remote end's identity public key */ void diff --git a/src/or/channeltls.c b/src/or/channeltls.c index bb6ef9b4d5..bb90ce5e49 100644 --- a/src/or/channeltls.c +++ b/src/or/channeltls.c @@ -89,11 +89,6 @@ static int enter_v3_handshake_with_cell(var_cell_t *cell, * Launch a new OR connection to addr:port and expect to * handshake with an OR with identity digest id_digest, and wrap * it in a channel_tls_t. - * - * @param addr Address to connect on - * @param port Port to connect on - * @param id_digest Identity digest we want - * @return The launched channel, or NULL if it failed. */ channel_t * @@ -161,8 +156,6 @@ channel_tls_connect(const tor_addr_t *addr, uint16_t port, * * Returns the current listening channel for incoming TLS connections, or * NULL if none has been established - * - * @return TLS listener */ channel_t * @@ -176,8 +169,6 @@ channel_tls_get_listener(void) * * Return the current channel_tls_t listener, or start one if we haven't yet, * and return that. - * - * @return TLS listener */ channel_t * @@ -235,9 +226,6 @@ channel_tls_free_all(void) /** * Create a new channel around an incoming or_connection_t - * - * @param orconn New or_connection_t - * @return A channel to queue on the TLS listener */ channel_t * @@ -288,8 +276,6 @@ channel_tls_handle_incoming(or_connection_t *orconn) * Close a channel_tls_t * * This implements the close method for channel_tls_t - * - * @param chan Channel to close */ static void @@ -384,10 +370,6 @@ channel_tls_describe_transport_method(channel_t *chan) * This implements the get_remote_addr method for channel_tls_t; copy the * remote endpoint of the channel to addr_out and return 1 (always * succeeds for this transport). - * - * @param chan Channel to query - * @param addr_out Write the address out here - * @return Always succeeds and returns 1 */ static int @@ -411,10 +393,6 @@ channel_tls_get_remote_addr_method(channel_t *chan, tor_addr_t *addr_out) * a text description of the remote endpoint of the channel suitable for use * in log messages. The req parameter is 0 for the canonical address or 1 for * the actual address seen. - * - * @param chan Channel to query - * @param req Request type (0 for canonical, 1 for actual) - * @return Pointer to string containing description */ static const char * @@ -461,9 +439,6 @@ channel_tls_get_remote_descr_method(channel_t *chan, int req) * * This implements the has_queued_writes method for channel_tls _t; it returns * 1 iff we have queued writes on the outbuf of the underlying or_connection_t. - * - * @param chan Channel to query - * @return Whether we have queued writes on the outbuf */ static int @@ -486,10 +461,6 @@ channel_tls_has_queued_writes_method(channel_t *chan) * This implements the is_canonical method for channel_tls_t; if req is zero, * it returns whether this is a canonical channel, and if it is one it returns * whether that can be relied upon. - * - * @param chan Channel to query - * @param req Request type (0 for is_canonical, 1 for is_canonical_reliable) - * @return Query response */ static int @@ -526,10 +497,6 @@ channel_tls_is_canonical_method(channel_t *chan, int req) * * This implements the matches_extend_info method for channel_tls_t; the upper * layer wants to know if this channel matches an extend_info_t. - * - * @param chan Channel to test - * @param extend_info The extend_info_t to match - * @return 1 if this channel matches, 0 otherwise */ static int @@ -552,10 +519,6 @@ channel_tls_matches_extend_info_method(channel_t *chan, * This implements the matches_target method for channel_tls _t; the upper * layer wants to know if this channel matches a target address when extending * a circuit. - * - * @param chan Channel to test - * @param target Address to match - * @return 1 if this channel matches, 0 otherwise */ static int @@ -577,10 +540,6 @@ channel_tls_matches_target_method(channel_t *chan, * * This implements the write_cell method for channel_tls_t; given a * channel_tls_t and a cell_t, transmit the cell_t. - * - * @param chan Channel to transmit on - * @param cell Cell to transmit - * @return Always succeeds and returns 1 */ static int @@ -602,10 +561,6 @@ channel_tls_write_cell_method(channel_t *chan, cell_t *cell) * * This implements the write_packed_cell method for channel_tls_t; given a * channel_tls_t and a packed_cell_t, transmit the packed_cell_t. - * - * @param chan Channel to transmit on - * @param packed_cell Cell to transmit - * @return Always succeeds and returns 1 */ static int @@ -632,10 +587,6 @@ channel_tls_write_packed_cell_method(channel_t *chan, * * This implements the write_var_cell method for channel_tls_t; given a * channel_tls_t and a var_cell_t, transmit the var_cell_t. - * - * @param chan Channel to transmit on - * @param var_cell Cell to transmit - * @return Always succeeds and returns 1 */ static int @@ -657,15 +608,10 @@ channel_tls_write_var_cell_method(channel_t *chan, var_cell_t *var_cell) ******************************************************/ /** - * Handle orconn state changes + * Handle an orconn state change * * This function will be called by connection_or.c when the or_connection_t * associated with this channel_tls_t changes state. - * - * @param chan Channel controlling the or_connection_t - * @param conn The or_connection_t changing state - * @param old_state The old state of conn - * @param state The new state of conn */ void @@ -712,13 +658,9 @@ channel_tls_handle_state_change_on_orconn(channel_tls_t *chan, } /** - * Try to flush cells from a channel_tls_t + * Flush cells from a channel_tls_t * * Try to flush up to about num_cells cells, and return how many we flushed. - * - * @param chan Channel to flush - * @param num_cells Maximum number of cells - * @return Number of cells actually flushed */ ssize_t @@ -752,9 +694,6 @@ channel_tls_flush_some_cells(channel_tls_t *chan, ssize_t num_cells) * * Return true if there is any more to flush on this channel (cells in queue * or active circuits). - * - * @param chan Channel to test - * @return 1 if chan has anything to flush, 0 otherwise */ int @@ -778,12 +717,6 @@ channel_tls_more_to_flush(channel_tls_t *chan) * This is a wrapper function around the actual function that processes the * cell that just arrived on chan. Increment *time * by the number of microseconds used by the call to *func(cell, chan). - * - * @param cell Incoming cell to process - * @param chan Channel it arrived on - * @param time Increment this by the number of microseconds it took to handle - * this cell - * @param func Function pointer to cell handling function */ static void @@ -820,9 +753,6 @@ channel_tls_time_process_cell(cell_t *cell, channel_tls_t *chan, int *time, * for cell types specific to the handshake for this transport protocol and * handles them, and queues all other cells to the channel_t layer, which * eventually will hand them off to command.c. - * - * @param cell Cell to handle - * @param conn The or_connection_t cell arrived on */ void @@ -920,9 +850,6 @@ channel_tls_handle_cell(cell_t *cell, or_connection_t *conn) * related and live below the channel_t layer, so no variable-length * cells ever get delivered in the current implementation, but I've left * the mechanism in place for future use. - * - * @param var_cell Incoming cell to handle - * @param conn The or_connection_t var_cell arrived on */ void @@ -1091,8 +1018,6 @@ channel_tls_handle_var_cell(var_cell_t *var_cell, or_connection_t *conn) * * Return true if command is a cell command that's allowed to start a * V3 handshake. - * - * @param command Cell type to check */ static int @@ -1115,10 +1040,6 @@ command_allowed_before_handshake(uint8_t command) * either for a cell or a TLS handshake. Set the connection's state to * "handshaking_v3', initializes the or_handshake_state field as needed, * and add the cell to the hash of incoming cells.) - * - * @param cell Incoming cell initiating the handshake - * @param chan Channel cell was received on - * @return 0 on success; return -1 and mark the connection on failure. */ static int @@ -1159,9 +1080,6 @@ enter_v3_handshake_with_cell(var_cell_t *cell, channel_tls_t *chan) * negotiated. We compare the versions in the cell to the list of versions * we support, pick the highest version we have in common, and continue the * negotiation from there. - * - * @param cell Incoming VERSIONS cell - * @param chan Channel that cell arrived on */ static void @@ -1311,9 +1229,6 @@ channel_tls_process_versions_cell(var_cell_t *cell, channel_tls_t *chan) * * This function is called to handle an incoming NETINFO cell; read and act * on its contents, and set the connection state to "open". - * - * @param cell Incoming NETINFO cell - * @param chan Channel that cell arrived on */ static void @@ -1493,9 +1408,6 @@ channel_tls_process_netinfo_cell(cell_t *cell, channel_tls_t *chan) * store the certificates in or_handshake_state. If this is the client side * of the connection, we then authenticate the server or mark the connection. * If it's the server side, wait for an AUTHENTICATE cell. - * - * @param cell Incoming CERTS cell - * @param chan Channel that cell arrived on */ static void @@ -1694,9 +1606,6 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan) * a v3 handshake, mark the channel. If the cell is well-formed but we don't * want to authenticate, just drop it. If the cell is well-formed *and* we * want to authenticate, send an AUTHENTICATE cell and then a NETINFO cell. - * - * @param cell Incoming AUTH_CHALLENGE cell to handle - * @param chan Channel that cell arrived on */ static void @@ -1794,9 +1703,6 @@ channel_tls_process_auth_challenge_cell(var_cell_t *cell, channel_tls_t *chan) * other side of the connection successfully (because it isn't signed right, * we didn't get a CERTS cell, etc) mark the connection. Otherwise, accept * the identity of the router on the other side of the connection. - * - * @param cell Incoming AUTHENTICATE cell - * @param chan Channel that cell arrived on */ static void -- cgit v1.2.3-54-g00ecf From 3f4b95b1a32787464b5877c7d21474801d4b944c Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Tue, 9 Oct 2012 00:51:33 -0700 Subject: Split channel_t into channel_t and channel_listener_t; get rid of that big union --- src/or/channel.c | 2123 ++++++++++++++++++++++++++++------------------ src/or/channel.h | 419 +++++---- src/or/channeltls.c | 241 +++--- src/or/channeltls.h | 4 +- src/or/circuitbuild.c | 31 +- src/or/circuitlist.c | 19 +- src/or/circuituse.c | 5 +- src/or/command.c | 17 +- src/or/command.h | 2 +- src/or/connection_edge.c | 3 +- src/or/connection_or.c | 14 +- src/or/main.c | 2 + src/or/or.h | 65 +- src/or/relay.c | 68 +- 14 files changed, 1788 insertions(+), 1225 deletions(-) (limited to 'src') diff --git a/src/or/channel.c b/src/or/channel.c index 57b9e4feb5..278daa6263 100644 --- a/src/or/channel.c +++ b/src/or/channel.c @@ -55,12 +55,18 @@ static smartlist_t *all_channels = NULL; /* All channel_t instances not in ERROR or CLOSED states */ static smartlist_t *active_channels = NULL; -/* All channel_t instances in LISTENING state */ -static smartlist_t *listening_channels = NULL; - /* All channel_t instances in ERROR or CLOSED states */ static smartlist_t *finished_channels = NULL; +/* All channel_listener_t instances */ +static smartlist_t *all_listeners = NULL; + +/* All channel_listener_t instances in LISTENING state */ +static smartlist_t *active_listeners = NULL; + +/* All channel_listener_t instances in LISTENING state */ +static smartlist_t *finished_listeners = NULL; + /* Counter for ID numbers */ static uint64_t n_channels_allocated = 0; @@ -89,6 +95,11 @@ channel_flush_some_cells_from_outgoing_queue(channel_t *chan, ssize_t num_cells); static void channel_force_free(channel_t *chan); static void +channel_free_list(smartlist_t *channels, int mark_for_close); +static void +channel_listener_free_list(smartlist_t *channels, int mark_for_close); +static void channel_listener_force_free(channel_listener_t *chan_l); +static void channel_write_cell_queue_entry(channel_t *chan, cell_queue_entry_t *q); /*********************************** @@ -108,7 +119,6 @@ channel_state_is_valid(channel_state_t state) case CHANNEL_STATE_CLOSED: case CHANNEL_STATE_CLOSING: case CHANNEL_STATE_ERROR: - case CHANNEL_STATE_LISTENING: case CHANNEL_STATE_MAINT: case CHANNEL_STATE_OPENING: case CHANNEL_STATE_OPEN: @@ -122,6 +132,30 @@ channel_state_is_valid(channel_state_t state) return is_valid; } +/** + * Indicate whether a given channel listener state is valid + */ + +int +channel_listener_state_is_valid(channel_listener_state_t state) +{ + int is_valid; + + switch (state) { + case CHANNEL_LISTENER_STATE_CLOSED: + case CHANNEL_LISTENER_STATE_LISTENING: + case CHANNEL_LISTENER_STATE_CLOSING: + case CHANNEL_LISTENER_STATE_ERROR: + is_valid = 1; + break; + case CHANNEL_LISTENER_STATE_LAST: + default: + is_valid = 0; + } + + return is_valid; +} + /** * Indicate whether a channel state transition is valid * @@ -137,8 +171,7 @@ channel_state_can_transition(channel_state_t from, channel_state_t to) switch (from) { case CHANNEL_STATE_CLOSED: - is_valid = (to == CHANNEL_STATE_LISTENING || - to == CHANNEL_STATE_OPENING); + is_valid = (to == CHANNEL_STATE_OPENING); break; case CHANNEL_STATE_CLOSING: is_valid = (to == CHANNEL_STATE_CLOSED || @@ -147,10 +180,6 @@ channel_state_can_transition(channel_state_t from, channel_state_t to) case CHANNEL_STATE_ERROR: is_valid = 0; break; - case CHANNEL_STATE_LISTENING: - is_valid = (to == CHANNEL_STATE_CLOSING || - to == CHANNEL_STATE_ERROR); - break; case CHANNEL_STATE_MAINT: is_valid = (to == CHANNEL_STATE_CLOSING || to == CHANNEL_STATE_ERROR || @@ -174,6 +203,43 @@ channel_state_can_transition(channel_state_t from, channel_state_t to) return is_valid; } +/** + * Indicate whether a channel listener state transition is valid + * + * This function takes two channel listener states and indicates whether a + * transition between them is permitted (see the state definitions and + * transition table in or.h at the channel_listener_state_t typedef). + */ + +int +channel_listener_state_can_transition(channel_listener_state_t from, + channel_listener_state_t to) +{ + int is_valid; + + switch (from) { + case CHANNEL_LISTENER_STATE_CLOSED: + is_valid = (to == CHANNEL_LISTENER_STATE_LISTENING); + break; + case CHANNEL_LISTENER_STATE_CLOSING: + is_valid = (to == CHANNEL_LISTENER_STATE_CLOSED || + to == CHANNEL_LISTENER_STATE_ERROR); + break; + case CHANNEL_LISTENER_STATE_ERROR: + is_valid = 0; + break; + case CHANNEL_LISTENER_STATE_LISTENING: + is_valid = (to == CHANNEL_LISTENER_STATE_CLOSING || + to == CHANNEL_LISTENER_STATE_ERROR); + break; + case CHANNEL_LISTENER_STATE_LAST: + default: + is_valid = 0; + } + + return is_valid; +} + /** * Return a human-readable description for a channel state */ @@ -193,9 +259,6 @@ channel_state_to_string(channel_state_t state) case CHANNEL_STATE_ERROR: descr = "channel error"; break; - case CHANNEL_STATE_LISTENING: - descr = "listening"; - break; case CHANNEL_STATE_MAINT: descr = "temporarily suspended for maintenance"; break; @@ -213,6 +276,36 @@ channel_state_to_string(channel_state_t state) return descr; } +/** + * Return a human-readable description for a channel listenier state + */ + +const char * +channel_listener_state_to_string(channel_listener_state_t state) +{ + const char *descr; + + switch (state) { + case CHANNEL_LISTENER_STATE_CLOSED: + descr = "closed"; + break; + case CHANNEL_LISTENER_STATE_CLOSING: + descr = "closing"; + break; + case CHANNEL_LISTENER_STATE_ERROR: + descr = "channel listener error"; + break; + case CHANNEL_LISTENER_STATE_LISTENING: + descr = "listening"; + break; + case CHANNEL_LISTENER_STATE_LAST: + default: + descr = "unknown or invalid channel listener state"; + } + + return descr; +} + /*************************************** * Channel registration/unregistration * ***************************************/ @@ -232,20 +325,12 @@ channel_register(channel_t *chan) /* No-op if already registered */ if (chan->registered) return; - if (chan->is_listener) { - log_debug(LD_CHANNEL, - "Registering listener channel %p (ID " U64_FORMAT ") " - "in state %s (%d)", - chan, U64_PRINTF_ARG(chan->global_identifier), - channel_state_to_string(chan->state), chan->state); - } else { - log_debug(LD_CHANNEL, - "Registering cell channel %p (ID " U64_FORMAT ") " - "in state %s (%d) with digest %s", - chan, U64_PRINTF_ARG(chan->global_identifier), - channel_state_to_string(chan->state), chan->state, - hex_str(chan->u.cell_chan.identity_digest, DIGEST_LEN)); - } + log_debug(LD_CHANNEL, + "Registering channel %p (ID " U64_FORMAT ") " + "in state %s (%d) with digest %s", + chan, U64_PRINTF_ARG(chan->global_identifier), + channel_state_to_string(chan->state), chan->state, + hex_str(chan->identity_digest, DIGEST_LEN)); /* Make sure we have all_channels, then add it */ if (!all_channels) all_channels = smartlist_new(); @@ -262,25 +347,17 @@ channel_register(channel_t *chan) if (!active_channels) active_channels = smartlist_new(); smartlist_add(active_channels, chan); - /* Is it a listener? */ - if (chan->is_listener && - chan->state == CHANNEL_STATE_LISTENING) { - /* Put it in the listening list, creating it if necessary */ - if (!listening_channels) listening_channels = smartlist_new(); - smartlist_add(listening_channels, chan); - } else if (chan->state != CHANNEL_STATE_CLOSING) { - if (!(chan->is_listener)) { - /* It should have a digest set */ - if (!tor_digest_is_zero(chan->u.cell_chan.identity_digest)) { - /* Yeah, we're good, add it to the map */ - channel_add_to_digest_map(chan); - } else { - log_info(LD_CHANNEL, - "Channel %p (global ID " U64_FORMAT ") " - "in state %s (%d) registered with no identity digest", - chan, U64_PRINTF_ARG(chan->global_identifier), - channel_state_to_string(chan->state), chan->state); - } + if (chan->state != CHANNEL_STATE_CLOSING) { + /* It should have a digest set */ + if (!tor_digest_is_zero(chan->identity_digest)) { + /* Yeah, we're good, add it to the map */ + channel_add_to_digest_map(chan); + } else { + log_info(LD_CHANNEL, + "Channel %p (global ID " U64_FORMAT ") " + "in state %s (%d) registered with no identity digest", + chan, U64_PRINTF_ARG(chan->global_identifier), + channel_state_to_string(chan->state), chan->state); } } } @@ -312,12 +389,6 @@ channel_unregister(channel_t *chan) } else { /* Get it out of the active list */ if (active_channels) smartlist_remove(active_channels, chan); - - /* Is it listening? */ - if (chan->state == CHANNEL_STATE_LISTENING) { - /* Get it out of the listening list */ - if (listening_channels) smartlist_remove(listening_channels, chan); - } } /* Get it out of all_channels */ @@ -326,17 +397,88 @@ channel_unregister(channel_t *chan) /* Mark it as unregistered */ chan->registered = 0; - if (!(chan->is_listener)) { - /* Should it be in the digest map? */ - if (!tor_digest_is_zero(chan->u.cell_chan.identity_digest) && - !(chan->state == CHANNEL_STATE_LISTENING || - chan->state == CHANNEL_STATE_CLOSING || - chan->state == CHANNEL_STATE_CLOSED || - chan->state == CHANNEL_STATE_ERROR)) { - /* Remove it */ - channel_remove_from_digest_map(chan); - } + /* Should it be in the digest map? */ + if (!tor_digest_is_zero(chan->identity_digest) && + !(chan->state == CHANNEL_STATE_CLOSING || + chan->state == CHANNEL_STATE_CLOSED || + chan->state == CHANNEL_STATE_ERROR)) { + /* Remove it */ + channel_remove_from_digest_map(chan); + } +} + +/** + * Register a channel listener + * + * This function registers a newly created channel listner in the global + * lists/maps of active channel listeners. + */ + +void +channel_listener_register(channel_listener_t *chan_l) +{ + tor_assert(chan_l); + + /* No-op if already registered */ + if (chan_l->registered) return; + + log_debug(LD_CHANNEL, + "Registering channel listener %p (ID " U64_FORMAT ") " + "in state %s (%d)", + chan_l, U64_PRINTF_ARG(chan_l->global_identifier), + channel_listener_state_to_string(chan_l->state), + chan_l->state); + + /* Make sure we have all_channels, then add it */ + if (!all_listeners) all_listeners = smartlist_new(); + smartlist_add(all_listeners, chan_l); + + /* Is it finished? */ + if (chan_l->state == CHANNEL_LISTENER_STATE_CLOSED || + chan_l->state == CHANNEL_LISTENER_STATE_ERROR) { + /* Put it in the finished list, creating it if necessary */ + if (!finished_listeners) finished_listeners = smartlist_new(); + smartlist_add(finished_listeners, chan_l); + } else { + /* Put it in the active list, creating it if necessary */ + if (!active_listeners) active_listeners = smartlist_new(); + smartlist_add(active_listeners, chan_l); + } + + /* Mark it as registered */ + chan_l->registered = 1; +} + +/** + * Unregister a channel listener + * + * This function removes a channel listener from the global lists and maps + * and is used when freeing a closed/errored channel listener. + */ + +void +channel_listener_unregister(channel_listener_t *chan_l) +{ + tor_assert(chan_l); + + /* No-op if not registered */ + if (!(chan_l->registered)) return; + + /* Is it finished? */ + if (chan_l->state == CHANNEL_LISTENER_STATE_CLOSED || + chan_l->state == CHANNEL_LISTENER_STATE_ERROR) { + /* Get it out of the finished list */ + if (finished_listeners) smartlist_remove(finished_listeners, chan_l); + } else { + /* Get it out of the active list */ + if (active_listeners) smartlist_remove(active_listeners, chan_l); } + + /* Get it out of all_channels */ + if (all_listeners) smartlist_remove(all_listeners, chan_l); + + /* Mark it as unregistered */ + chan_l->registered = 0; } /********************************* @@ -357,34 +499,31 @@ channel_add_to_digest_map(channel_t *chan) channel_t *tmp; tor_assert(chan); - tor_assert(!(chan->is_listener)); /* Assert that the state makes sense */ - tor_assert(!(chan->state == CHANNEL_STATE_LISTENING || - chan->state == CHANNEL_STATE_CLOSING || + tor_assert(!(chan->state == CHANNEL_STATE_CLOSING || chan->state == CHANNEL_STATE_CLOSED || chan->state == CHANNEL_STATE_ERROR)); /* Assert that there is a digest */ - tor_assert(!tor_digest_is_zero(chan->u.cell_chan.identity_digest)); + tor_assert(!tor_digest_is_zero(chan->identity_digest)); /* Allocate the identity map if we have to */ if (!channel_identity_map) channel_identity_map = digestmap_new(); /* Insert it */ tmp = digestmap_set(channel_identity_map, - chan->u.cell_chan.identity_digest, + chan->identity_digest, chan); if (tmp) { - tor_assert(!(tmp->is_listener)); /* There already was one, this goes at the head of the list */ - chan->u.cell_chan.next_with_same_id = tmp; - chan->u.cell_chan.prev_with_same_id = NULL; - tmp->u.cell_chan.prev_with_same_id = chan; + chan->next_with_same_id = tmp; + chan->prev_with_same_id = NULL; + tmp->prev_with_same_id = chan; } else { /* First with this digest */ - chan->u.cell_chan.next_with_same_id = NULL; - chan->u.cell_chan.prev_with_same_id = NULL; + chan->next_with_same_id = NULL; + chan->prev_with_same_id = NULL; } log_debug(LD_CHANNEL, @@ -392,7 +531,7 @@ channel_add_to_digest_map(channel_t *chan) "to identity map in state %s (%d) with digest %s", chan, U64_PRINTF_ARG(chan->global_identifier), channel_state_to_string(chan->state), chan->state, - hex_str(chan->u.cell_chan.identity_digest, DIGEST_LEN)); + hex_str(chan->identity_digest, DIGEST_LEN)); } /** @@ -408,10 +547,9 @@ channel_remove_from_digest_map(channel_t *chan) channel_t *tmp, *head; tor_assert(chan); - tor_assert(!(chan->is_listener)); /* Assert that there is a digest */ - tor_assert(!tor_digest_is_zero(chan->u.cell_chan.identity_digest)); + tor_assert(!tor_digest_is_zero(chan->identity_digest)); /* Make sure we have a map */ if (!channel_identity_map) { @@ -424,72 +562,62 @@ channel_remove_from_digest_map(channel_t *chan) "with digest %s from identity map, but didn't have any identity " "map", chan, U64_PRINTF_ARG(chan->global_identifier), - hex_str(chan->u.cell_chan.identity_digest, DIGEST_LEN)); + hex_str(chan->identity_digest, DIGEST_LEN)); /* Clear out its next/prev pointers */ - if (chan->u.cell_chan.next_with_same_id) { - tor_assert(!(chan->u.cell_chan.next_with_same_id->is_listener)); - chan->u.cell_chan.next_with_same_id->u.cell_chan.prev_with_same_id - = chan->u.cell_chan.prev_with_same_id; + if (chan->next_with_same_id) { + chan->next_with_same_id->prev_with_same_id = chan->prev_with_same_id; } - if (chan->u.cell_chan.prev_with_same_id) { - tor_assert(!(chan->u.cell_chan.prev_with_same_id->is_listener)); - chan->u.cell_chan.prev_with_same_id->u.cell_chan.next_with_same_id - = chan->u.cell_chan.next_with_same_id; + if (chan->prev_with_same_id) { + chan->prev_with_same_id->next_with_same_id = chan->next_with_same_id; } - chan->u.cell_chan.next_with_same_id = NULL; - chan->u.cell_chan.prev_with_same_id = NULL; + chan->next_with_same_id = NULL; + chan->prev_with_same_id = NULL; return; } /* Look for it in the map */ - tmp = digestmap_get(channel_identity_map, chan->u.cell_chan.identity_digest); + tmp = digestmap_get(channel_identity_map, chan->identity_digest); if (tmp) { /* Okay, it's here */ head = tmp; /* Keep track of list head */ /* Look for this channel */ while (tmp && tmp != chan) { - tor_assert(!(tmp->is_listener)); - tmp = tmp->u.cell_chan.next_with_same_id; + tmp = tmp->next_with_same_id; } if (tmp == chan) { /* Found it, good */ - if (chan->u.cell_chan.next_with_same_id) { - tor_assert(!(chan->u.cell_chan.next_with_same_id->is_listener)); - chan->u.cell_chan.next_with_same_id->u.cell_chan.prev_with_same_id - = chan->u.cell_chan.prev_with_same_id; + if (chan->next_with_same_id) { + chan->next_with_same_id->prev_with_same_id = chan->prev_with_same_id; } /* else we're the tail of the list */ - if (chan->u.cell_chan.prev_with_same_id) { - tor_assert(!(chan->u.cell_chan.prev_with_same_id->is_listener)); + if (chan->prev_with_same_id) { /* We're not the head of the list, so we can *just* unlink */ - chan->u.cell_chan.prev_with_same_id->u.cell_chan.next_with_same_id - = chan->u.cell_chan.next_with_same_id; + chan->prev_with_same_id->next_with_same_id = chan->next_with_same_id; } else { /* We're the head, so we have to point the digest map entry at our * next if we have one, or remove it if we're also the tail */ - if (chan->u.cell_chan.next_with_same_id) { - tor_assert(!(chan->u.cell_chan.next_with_same_id->is_listener)); + if (chan->next_with_same_id) { digestmap_set(channel_identity_map, - chan->u.cell_chan.identity_digest, - chan->u.cell_chan.next_with_same_id); + chan->identity_digest, + chan->next_with_same_id); } else { digestmap_remove(channel_identity_map, - chan->u.cell_chan.identity_digest); + chan->identity_digest); } } /* NULL out its next/prev pointers, and we're finished */ - chan->u.cell_chan.next_with_same_id = NULL; - chan->u.cell_chan.prev_with_same_id = NULL; + chan->next_with_same_id = NULL; + chan->prev_with_same_id = NULL; log_debug(LD_CHANNEL, "Removed channel %p (global ID " U64_FORMAT ") from " "identity map in state %s (%d) with digest %s", chan, U64_PRINTF_ARG(chan->global_identifier), channel_state_to_string(chan->state), chan->state, - hex_str(chan->u.cell_chan.identity_digest, DIGEST_LEN)); + hex_str(chan->identity_digest, DIGEST_LEN)); } else { /* This is not good */ log_warn(LD_BUG, @@ -497,20 +625,16 @@ channel_remove_from_digest_map(channel_t *chan) "with digest %s from identity map, but couldn't find it in " "the list for that digest", chan, U64_PRINTF_ARG(chan->global_identifier), - hex_str(chan->u.cell_chan.identity_digest, DIGEST_LEN)); + hex_str(chan->identity_digest, DIGEST_LEN)); /* Unlink it and hope for the best */ - if (chan->u.cell_chan.next_with_same_id) { - tor_assert(!(chan->u.cell_chan.next_with_same_id->is_listener)); - chan->u.cell_chan.next_with_same_id->u.cell_chan.prev_with_same_id - = chan->u.cell_chan.prev_with_same_id; + if (chan->next_with_same_id) { + chan->next_with_same_id->prev_with_same_id = chan->prev_with_same_id; } - if (chan->u.cell_chan.prev_with_same_id) { - tor_assert(!(chan->u.cell_chan.prev_with_same_id->is_listener)); - chan->u.cell_chan.prev_with_same_id->u.cell_chan.next_with_same_id - = chan->u.cell_chan.next_with_same_id; + if (chan->prev_with_same_id) { + chan->prev_with_same_id->next_with_same_id = chan->next_with_same_id; } - chan->u.cell_chan.next_with_same_id = NULL; - chan->u.cell_chan.prev_with_same_id = NULL; + chan->next_with_same_id = NULL; + chan->prev_with_same_id = NULL; } } else { /* Shouldn't happen */ @@ -519,19 +643,16 @@ channel_remove_from_digest_map(channel_t *chan) "digest %s from identity map, but couldn't find any with " "that digest", chan, U64_PRINTF_ARG(chan->global_identifier), - hex_str(chan->u.cell_chan.identity_digest, DIGEST_LEN)); + hex_str(chan->identity_digest, DIGEST_LEN)); /* Clear out its next/prev pointers */ - if (chan->u.cell_chan.next_with_same_id) { - tor_assert(!(chan->u.cell_chan.next_with_same_id->is_listener)); - chan->u.cell_chan.next_with_same_id->u.cell_chan.prev_with_same_id - = chan->u.cell_chan.prev_with_same_id; + if (chan->next_with_same_id) { + chan->next_with_same_id->prev_with_same_id = chan->prev_with_same_id; } - if (chan->u.cell_chan.prev_with_same_id) { - chan->u.cell_chan.prev_with_same_id->u.cell_chan.next_with_same_id - = chan->u.cell_chan.next_with_same_id; + if (chan->prev_with_same_id) { + chan->prev_with_same_id->next_with_same_id = chan->next_with_same_id; } - chan->u.cell_chan.next_with_same_id = NULL; - chan->u.cell_chan.prev_with_same_id = NULL; + chan->next_with_same_id = NULL; + chan->prev_with_same_id = NULL; } } @@ -599,9 +720,8 @@ channel_t * channel_next_with_digest(channel_t *chan) { tor_assert(chan); - tor_assert(!(chan->is_listener)); - return chan->u.cell_chan.next_with_same_id; + return chan->next_with_same_id; } /** @@ -614,19 +734,13 @@ channel_next_with_digest(channel_t *chan) channel_t * channel_prev_with_digest(channel_t *chan) { - channel_t *rv = NULL; - tor_assert(chan); - tor_assert(!(chan->is_listener)); - if (chan->u.cell_chan.prev_with_same_id) - rv = chan->u.cell_chan.prev_with_same_id; - - return rv; + return chan->prev_with_same_id; } /** - * Initialize a cell channel + * Initialize a channel * * This function should be called by subclasses to set up some per-channel * variables. I.e., this is the superclass constructor. Before this, the @@ -634,47 +748,41 @@ channel_prev_with_digest(channel_t *chan) */ void -channel_init_for_cells(channel_t *chan) +channel_init(channel_t *chan) { tor_assert(chan); /* Assign an ID and bump the counter */ chan->global_identifier = n_channels_allocated++; - /* Mark as a non-listener */ - chan->is_listener = 0; - /* Init timestamp */ - chan->u.cell_chan.timestamp_last_added_nonpadding = time(NULL); + chan->timestamp_last_added_nonpadding = time(NULL); /* Init next_circ_id */ - chan->u.cell_chan.next_circ_id = crypto_rand_int(1 << 15); + chan->next_circ_id = crypto_rand_int(1 << 15); /* Timestamp it */ channel_timestamp_created(chan); } /** - * Initialize a listener channel + * Initialize a channel listener * * This function should be called by subclasses to set up some per-channel * variables. I.e., this is the superclass constructor. Before this, the - * channel should be allocated with tor_malloc_zero(). + * channel listener should be allocated with tor_malloc_zero(). */ void -channel_init_listener(channel_t *chan) +channel_init_listener(channel_listener_t *chan_l) { - tor_assert(chan); + tor_assert(chan_l); /* Assign an ID and bump the counter */ - chan->global_identifier = n_channels_allocated++; - - /* Mark as a listener */ - chan->is_listener = 1; + chan_l->global_identifier = n_channels_allocated++; /* Timestamp it */ - channel_timestamp_created(chan); + channel_listener_timestamp_created(chan_l); } /** @@ -696,13 +804,11 @@ channel_free(channel_t *chan) /* Call a free method if there is one */ if (chan->free) chan->free(chan); - if (!(chan->is_listener)) { - channel_clear_remote_end(chan); + channel_clear_remote_end(chan); - if (chan->u.cell_chan.active_circuit_pqueue) { - smartlist_free(chan->u.cell_chan.active_circuit_pqueue); - chan->u.cell_chan.active_circuit_pqueue = NULL; - } + if (chan->active_circuit_pqueue) { + smartlist_free(chan->active_circuit_pqueue); + chan->active_circuit_pqueue = NULL; } /* We're in CLOSED or ERROR, so the cell queue is already empty */ @@ -711,7 +817,35 @@ channel_free(channel_t *chan) } /** - * Free a channel and skip the state/reigstration asserts; this internal- + * Free a channel listener; nothing outside of channel.c and subclasses + * should call this - it frees channel listeners after they have closed and + * been unregistered. + */ + +void +channel_listener_free(channel_listener_t *chan_l) +{ + if (!chan_l) return; + + /* It must be closed or errored */ + tor_assert(chan_l->state == CHANNEL_LISTENER_STATE_CLOSED || + chan_l->state == CHANNEL_LISTENER_STATE_ERROR); + /* It must be deregistered */ + tor_assert(!(chan_l->registered)); + + /* Call a free method if there is one */ + if (chan_l->free) chan_l->free(chan_l); + + /* + * We're in CLOSED or ERROR, so the incoming channel queue is already + * empty. + */ + + tor_free(chan_l); +} + +/** + * Free a channel and skip the state/registration asserts; this internal- * use-only function should be called only from channel_free_all() when * shutting down the Tor process. */ @@ -724,96 +858,111 @@ channel_force_free(channel_t *chan) /* Call a free method if there is one */ if (chan->free) chan->free(chan); - if (chan->is_listener) { - /* - * The incoming list just gets emptied and freed; we request close on - * any channels we find there, but since we got called while shutting - * down they will get deregistered and freed elsewhere anyway. - */ - if (chan->u.listener.incoming_list) { - SMARTLIST_FOREACH_BEGIN(chan->u.listener.incoming_list, - channel_t *, qchan) { - channel_mark_for_close(qchan); - } SMARTLIST_FOREACH_END(qchan); - - smartlist_free(chan->u.listener.incoming_list); - chan->u.listener.incoming_list = NULL; - } - } else { - channel_clear_remote_end(chan); - smartlist_free(chan->u.cell_chan.active_circuit_pqueue); - - /* We might still have a cell queue; kill it */ - if (chan->u.cell_chan.incoming_queue) { - SMARTLIST_FOREACH_BEGIN(chan->u.cell_chan.incoming_queue, - cell_queue_entry_t *, q) { - tor_free(q); - } SMARTLIST_FOREACH_END(q); - - smartlist_free(chan->u.cell_chan.incoming_queue); - chan->u.cell_chan.incoming_queue = NULL; - } + channel_clear_remote_end(chan); + smartlist_free(chan->active_circuit_pqueue); + + /* We might still have a cell queue; kill it */ + if (chan->incoming_queue) { + SMARTLIST_FOREACH_BEGIN(chan->incoming_queue, + cell_queue_entry_t *, q) { + tor_free(q); + } SMARTLIST_FOREACH_END(q); + + smartlist_free(chan->incoming_queue); + chan->incoming_queue = NULL; + } - /* Outgoing cell queue is similar, but we can have to free packed cells */ - if (chan->u.cell_chan.outgoing_queue) { - SMARTLIST_FOREACH_BEGIN(chan->u.cell_chan.outgoing_queue, - cell_queue_entry_t *, q) { - if (q->type == CELL_QUEUE_PACKED) { - if (q->u.packed.packed_cell) { - packed_cell_free(q->u.packed.packed_cell); - } + /* Outgoing cell queue is similar, but we can have to free packed cells */ + if (chan->outgoing_queue) { + SMARTLIST_FOREACH_BEGIN(chan->outgoing_queue, + cell_queue_entry_t *, q) { + if (q->type == CELL_QUEUE_PACKED) { + if (q->u.packed.packed_cell) { + packed_cell_free(q->u.packed.packed_cell); } - tor_free(q); - } SMARTLIST_FOREACH_END(q); + } + tor_free(q); + } SMARTLIST_FOREACH_END(q); - smartlist_free(chan->u.cell_chan.outgoing_queue); - chan->u.cell_chan.outgoing_queue = NULL; - } + smartlist_free(chan->outgoing_queue); + chan->outgoing_queue = NULL; } tor_free(chan); } /** - * Return the current registered listener for a channel + * Free a channel listener and skip the state/reigstration asserts; this + * internal-use-only function should be called only from channel_free_all() + * when shutting down the Tor process. + */ + +static void +channel_listener_force_free(channel_listener_t *chan_l) +{ + tor_assert(chan_l); + + /* Call a free method if there is one */ + if (chan_l->free) chan_l->free(chan_l); + + /* + * The incoming list just gets emptied and freed; we request close on + * any channels we find there, but since we got called while shutting + * down they will get deregistered and freed elsewhere anyway. + */ + if (chan_l->incoming_list) { + SMARTLIST_FOREACH_BEGIN(chan_l->incoming_list, + channel_t *, qchan) { + channel_mark_for_close(qchan); + } SMARTLIST_FOREACH_END(qchan); + + smartlist_free(chan_l->incoming_list); + chan_l->incoming_list = NULL; + } + + tor_free(chan_l); +} + +/** + * Return the current registered listener for a channel listener * * This function returns a function pointer to the current registered - * handler for new incoming channels on a listener channel. + * handler for new incoming channels on a channel listener. */ channel_listener_fn_ptr -channel_get_listener_fn(channel_t *chan) +channel_listener_get_listener_fn(channel_listener_t *chan_l) { - tor_assert(chan); - tor_assert(chan->is_listener); + tor_assert(chan_l); - if (chan->state == CHANNEL_STATE_LISTENING) - return chan->u.listener.listener; + if (chan_l->state == CHANNEL_LISTENER_STATE_LISTENING) + return chan_l->listener; return NULL; } /** - * Set the listener for a channel + * Set the listener for a channel listener * - * This function sets the handler for new incoming channels on a listener - * channel. + * This function sets the handler for new incoming channels on a channel + * listener. */ void -channel_set_listener_fn(channel_t *chan, - channel_listener_fn_ptr listener) +channel_listener_set_listener_fn(channel_listener_t *chan_l, + channel_listener_fn_ptr listener) { - tor_assert(chan); - tor_assert(chan->is_listener); - tor_assert(chan->state == CHANNEL_STATE_LISTENING); + tor_assert(chan_l); + tor_assert(chan_l->state == CHANNEL_LISTENER_STATE_LISTENING); log_debug(LD_CHANNEL, - "Setting listener callback for channel %p to %p", - chan, listener); + "Setting listener callback for channel listener %p " + "(global ID " U64_FORMAT ") to %p", + chan_l, U64_PRINTF_ARG(chan_l->global_identifier), + listener); - chan->u.listener.listener = listener; - if (chan->u.listener.listener) channel_process_incoming(chan); + chan_l->listener = listener; + if (chan_l->listener) channel_listener_process_incoming(chan_l); } /** @@ -827,12 +976,11 @@ channel_cell_handler_fn_ptr channel_get_cell_handler(channel_t *chan) { tor_assert(chan); - tor_assert(!(chan->is_listener)); if (chan->state == CHANNEL_STATE_OPENING || chan->state == CHANNEL_STATE_OPEN || chan->state == CHANNEL_STATE_MAINT) - return chan->u.cell_chan.cell_handler; + return chan->cell_handler; return NULL; } @@ -848,12 +996,11 @@ channel_var_cell_handler_fn_ptr channel_get_var_cell_handler(channel_t *chan) { tor_assert(chan); - tor_assert(!(chan->is_listener)); if (chan->state == CHANNEL_STATE_OPENING || chan->state == CHANNEL_STATE_OPEN || chan->state == CHANNEL_STATE_MAINT) - return chan->u.cell_chan.var_cell_handler; + return chan->var_cell_handler; return NULL; } @@ -875,7 +1022,6 @@ channel_set_cell_handlers(channel_t *chan, int try_again = 0; tor_assert(chan); - tor_assert(!(chan->is_listener)); tor_assert(chan->state == CHANNEL_STATE_OPENING || chan->state == CHANNEL_STATE_OPEN || chan->state == CHANNEL_STATE_MAINT); @@ -889,20 +1035,20 @@ channel_set_cell_handlers(channel_t *chan, /* Should we try the queue? */ if (cell_handler && - cell_handler != chan->u.cell_chan.cell_handler) try_again = 1; + cell_handler != chan->cell_handler) try_again = 1; if (var_cell_handler && - var_cell_handler != chan->u.cell_chan.var_cell_handler) try_again = 1; + var_cell_handler != chan->var_cell_handler) try_again = 1; /* Change them */ - chan->u.cell_chan.cell_handler = cell_handler; - chan->u.cell_chan.var_cell_handler = var_cell_handler; + chan->cell_handler = cell_handler; + chan->var_cell_handler = var_cell_handler; /* Re-run the queue if we have one and there's any reason to */ - if (chan->u.cell_chan.incoming_queue && - (smartlist_len(chan->u.cell_chan.incoming_queue) > 0) && + if (chan->incoming_queue && + (smartlist_len(chan->incoming_queue) > 0) && try_again && - (chan->u.cell_chan.cell_handler || - chan->u.cell_chan.var_cell_handler)) channel_process_cells(chan); + (chan->cell_handler || + chan->var_cell_handler)) channel_process_cells(chan); } /** @@ -925,8 +1071,9 @@ channel_mark_for_close(channel_t *chan) chan->state == CHANNEL_STATE_ERROR) return; log_debug(LD_CHANNEL, - "Closing channel %p by request", - chan); + "Closing channel %p (global ID " U64_FORMAT ") " + "by request", + chan, U64_PRINTF_ARG(chan->global_identifier)); /* Note closing by request from above */ chan->reason_for_closing = CHANNEL_CLOSE_REQUESTED; @@ -945,6 +1092,47 @@ channel_mark_for_close(channel_t *chan) */ } +/** + * Mark a channel listener for closure + * + * This function tries to close a channel_listener_t; it will go into the + * CLOSING state, and eventually the lower layer should put it into the CLOSED + * or ERROR state. Then, channel_run_cleanup() will eventually free it. + */ + +void +channel_listener_mark_for_close(channel_listener_t *chan_l) +{ + tor_assert(chan_l != NULL); + tor_assert(chan_l->close != NULL); + + /* If it's already in CLOSING, CLOSED or ERROR, this is a no-op */ + if (chan_l->state == CHANNEL_LISTENER_STATE_CLOSING || + chan_l->state == CHANNEL_LISTENER_STATE_CLOSED || + chan_l->state == CHANNEL_LISTENER_STATE_ERROR) return; + + log_debug(LD_CHANNEL, + "Closing channel listener %p (global ID " U64_FORMAT ") " + "by request", + chan_l, U64_PRINTF_ARG(chan_l->global_identifier)); + + /* Note closing by request from above */ + chan_l->reason_for_closing = CHANNEL_LISTENER_CLOSE_REQUESTED; + + /* Change state to CLOSING */ + channel_listener_change_state(chan_l, CHANNEL_LISTENER_STATE_CLOSING); + + /* Tell the lower layer */ + chan_l->close(chan_l); + + /* + * It's up to the lower layer to change state to CLOSED or ERROR when we're + * ready; we'll try to free channels that are in the finished list from + * channel_run_cleanup(). The lower layer should do this by calling + * channel_listener_closed(). + */ +} + /** * Close a channel from the lower layer * @@ -964,8 +1152,9 @@ channel_close_from_lower_layer(channel_t *chan) chan->state == CHANNEL_STATE_ERROR) return; log_debug(LD_CHANNEL, - "Closing channel %p due to lower-layer event", - chan); + "Closing channel %p (global ID " U64_FORMAT ") " + "due to lower-layer event", + chan, U64_PRINTF_ARG(chan->global_identifier)); /* Note closing by event from below */ chan->reason_for_closing = CHANNEL_CLOSE_FROM_BELOW; @@ -974,6 +1163,36 @@ channel_close_from_lower_layer(channel_t *chan) channel_change_state(chan, CHANNEL_STATE_CLOSING); } +/** + * Close a channel listener from the lower layer + * + * Notify the channel code that the channel listener is being closed due to a + * non-error condition in the lower layer. This does not call the close() + * method, since the lower layer already knows. + */ + +void +channel_listener_close_from_lower_layer(channel_listener_t *chan_l) +{ + tor_assert(chan_l != NULL); + + /* If it's already in CLOSING, CLOSED or ERROR, this is a no-op */ + if (chan_l->state == CHANNEL_LISTENER_STATE_CLOSING || + chan_l->state == CHANNEL_LISTENER_STATE_CLOSED || + chan_l->state == CHANNEL_LISTENER_STATE_ERROR) return; + + log_debug(LD_CHANNEL, + "Closing channel listener %p (global ID " U64_FORMAT ") " + "due to lower-layer event", + chan_l, U64_PRINTF_ARG(chan_l->global_identifier)); + + /* Note closing by event from below */ + chan_l->reason_for_closing = CHANNEL_LISTENER_CLOSE_FROM_BELOW; + + /* Change state to CLOSING */ + channel_listener_change_state(chan_l, CHANNEL_LISTENER_STATE_CLOSING); +} + /** * Notify that the channel is being closed due to an error condition * @@ -1003,6 +1222,37 @@ channel_close_for_error(channel_t *chan) channel_change_state(chan, CHANNEL_STATE_CLOSING); } +/** + * Notify that the channel listener is being closed due to an error condition + * + * This function is called by the lower layer implementing the transport + * when a channel listener must be closed due to an error condition. This + * does not call the channel listener's close method, since the lower layer + * already knows. + */ + +void +channel_listener_close_for_error(channel_listener_t *chan_l) +{ + tor_assert(chan_l != NULL); + + /* If it's already in CLOSING, CLOSED or ERROR, this is a no-op */ + if (chan_l->state == CHANNEL_LISTENER_STATE_CLOSING || + chan_l->state == CHANNEL_LISTENER_STATE_CLOSED || + chan_l->state == CHANNEL_LISTENER_STATE_ERROR) return; + + log_debug(LD_CHANNEL, + "Closing channel listener %p (global ID " U64_FORMAT ") " + "due to lower-layer error", + chan_l, U64_PRINTF_ARG(chan_l->global_identifier)); + + /* Note closing by event from below */ + chan_l->reason_for_closing = CHANNEL_LISTENER_CLOSE_FOR_ERROR; + + /* Change state to CLOSING */ + channel_listener_change_state(chan_l, CHANNEL_LISTENER_STATE_CLOSING); +} + /** * Notify that the lower layer is finished closing the channel * @@ -1038,6 +1288,33 @@ channel_closed(channel_t *chan) } } +/** + * Notify that the lower layer is finished closing the channel listener + * + * This function should be called by the lower layer when a channel listener + * is finished closing and it should be regarded as inactive and + * freed by the channel code. + */ + +void +channel_listener_closed(channel_listener_t *chan_l) +{ + tor_assert(chan_l); + tor_assert(chan_l->state == CHANNEL_LISTENER_STATE_CLOSING || + chan_l->state == CHANNEL_LISTENER_STATE_CLOSED || + chan_l->state == CHANNEL_LISTENER_STATE_ERROR); + + /* No-op if already inactive */ + if (chan_l->state == CHANNEL_LISTENER_STATE_CLOSED || + chan_l->state == CHANNEL_LISTENER_STATE_ERROR) return; + + if (chan_l->reason_for_closing != CHANNEL_LISTENER_CLOSE_FOR_ERROR) { + channel_listener_change_state(chan_l, CHANNEL_LISTENER_STATE_CLOSED); + } else { + channel_listener_change_state(chan_l, CHANNEL_LISTENER_STATE_ERROR); + } +} + /** * Clear the identity_digest of a channel * @@ -1051,7 +1328,6 @@ channel_clear_identity_digest(channel_t *chan) int state_not_in_map; tor_assert(chan); - tor_assert(!(chan->is_listener)); log_debug(LD_CHANNEL, "Clearing remote endpoint digest on channel %p with " @@ -1059,18 +1335,17 @@ channel_clear_identity_digest(channel_t *chan) chan, U64_PRINTF_ARG(chan->global_identifier)); state_not_in_map = - (chan->state == CHANNEL_STATE_LISTENING || - chan->state == CHANNEL_STATE_CLOSING || + (chan->state == CHANNEL_STATE_CLOSING || chan->state == CHANNEL_STATE_CLOSED || chan->state == CHANNEL_STATE_ERROR); if (!state_not_in_map && chan->registered && - !tor_digest_is_zero(chan->u.cell_chan.identity_digest)) + !tor_digest_is_zero(chan->identity_digest)) /* if it's registered get it out of the digest map */ channel_remove_from_digest_map(chan); - memset(chan->u.cell_chan.identity_digest, 0, - sizeof(chan->u.cell_chan.identity_digest)); + memset(chan->identity_digest, 0, + sizeof(chan->identity_digest)); } /** @@ -1087,7 +1362,6 @@ channel_set_identity_digest(channel_t *chan, int was_in_digest_map, should_be_in_digest_map, state_not_in_map; tor_assert(chan); - tor_assert(!(chan->is_listener)); log_debug(LD_CHANNEL, "Setting remote endpoint digest on channel %p with " @@ -1097,14 +1371,13 @@ channel_set_identity_digest(channel_t *chan, hex_str(identity_digest, DIGEST_LEN) : "(null)"); state_not_in_map = - (chan->state == CHANNEL_STATE_LISTENING || - chan->state == CHANNEL_STATE_CLOSING || + (chan->state == CHANNEL_STATE_CLOSING || chan->state == CHANNEL_STATE_CLOSED || chan->state == CHANNEL_STATE_ERROR); was_in_digest_map = !state_not_in_map && chan->registered && - !tor_digest_is_zero(chan->u.cell_chan.identity_digest); + !tor_digest_is_zero(chan->identity_digest); should_be_in_digest_map = !state_not_in_map && chan->registered && @@ -1118,12 +1391,12 @@ channel_set_identity_digest(channel_t *chan, channel_remove_from_digest_map(chan); if (identity_digest) { - memcpy(chan->u.cell_chan.identity_digest, + memcpy(chan->identity_digest, identity_digest, - sizeof(chan->u.cell_chan.identity_digest)); + sizeof(chan->identity_digest)); } else { - memset(chan->u.cell_chan.identity_digest, 0, - sizeof(chan->u.cell_chan.identity_digest)); + memset(chan->identity_digest, 0, + sizeof(chan->identity_digest)); } /* Put it in the digest map if we should */ @@ -1144,7 +1417,6 @@ channel_clear_remote_end(channel_t *chan) int state_not_in_map; tor_assert(chan); - tor_assert(!(chan->is_listener)); log_debug(LD_CHANNEL, "Clearing remote endpoint identity on channel %p with " @@ -1152,19 +1424,18 @@ channel_clear_remote_end(channel_t *chan) chan, U64_PRINTF_ARG(chan->global_identifier)); state_not_in_map = - (chan->state == CHANNEL_STATE_LISTENING || - chan->state == CHANNEL_STATE_CLOSING || + (chan->state == CHANNEL_STATE_CLOSING || chan->state == CHANNEL_STATE_CLOSED || chan->state == CHANNEL_STATE_ERROR); if (!state_not_in_map && chan->registered && - !tor_digest_is_zero(chan->u.cell_chan.identity_digest)) + !tor_digest_is_zero(chan->identity_digest)) /* if it's registered get it out of the digest map */ channel_remove_from_digest_map(chan); - memset(chan->u.cell_chan.identity_digest, 0, - sizeof(chan->u.cell_chan.identity_digest)); - tor_free(chan->u.cell_chan.nickname); + memset(chan->identity_digest, 0, + sizeof(chan->identity_digest)); + tor_free(chan->nickname); } /** @@ -1182,7 +1453,6 @@ channel_set_remote_end(channel_t *chan, int was_in_digest_map, should_be_in_digest_map, state_not_in_map; tor_assert(chan); - tor_assert(!(chan->is_listener)); log_debug(LD_CHANNEL, "Setting remote endpoint identity on channel %p with " @@ -1193,14 +1463,13 @@ channel_set_remote_end(channel_t *chan, hex_str(identity_digest, DIGEST_LEN) : "(null)"); state_not_in_map = - (chan->state == CHANNEL_STATE_LISTENING || - chan->state == CHANNEL_STATE_CLOSING || + (chan->state == CHANNEL_STATE_CLOSING || chan->state == CHANNEL_STATE_CLOSED || chan->state == CHANNEL_STATE_ERROR); was_in_digest_map = !state_not_in_map && chan->registered && - !tor_digest_is_zero(chan->u.cell_chan.identity_digest); + !tor_digest_is_zero(chan->identity_digest); should_be_in_digest_map = !state_not_in_map && chan->registered && @@ -1214,18 +1483,18 @@ channel_set_remote_end(channel_t *chan, channel_remove_from_digest_map(chan); if (identity_digest) { - memcpy(chan->u.cell_chan.identity_digest, + memcpy(chan->identity_digest, identity_digest, - sizeof(chan->u.cell_chan.identity_digest)); + sizeof(chan->identity_digest)); } else { - memset(chan->u.cell_chan.identity_digest, 0, - sizeof(chan->u.cell_chan.identity_digest)); + memset(chan->identity_digest, 0, + sizeof(chan->identity_digest)); } - tor_free(chan->u.cell_chan.nickname); + tor_free(chan->nickname); if (nickname) - chan->u.cell_chan.nickname = tor_strdup(nickname); + chan->nickname = tor_strdup(nickname); /* Put it in the digest map if we should */ if (should_be_in_digest_map) @@ -1275,7 +1544,6 @@ channel_write_cell_queue_entry(channel_t *chan, cell_queue_entry_t *q) cell_queue_entry_t *tmp = NULL; tor_assert(chan); - tor_assert(!(chan->is_listener)); tor_assert(q); /* Assert that the state makes sense for a cell write */ @@ -1285,31 +1553,29 @@ channel_write_cell_queue_entry(channel_t *chan, cell_queue_entry_t *q) /* Increment the timestamp unless it's padding */ if (!cell_queue_entry_is_padding(q)) { - chan->u.cell_chan.timestamp_last_added_nonpadding = approx_time(); + chan->timestamp_last_added_nonpadding = approx_time(); } /* Can we send it right out? If so, try */ - if (!(chan->u.cell_chan.outgoing_queue && - (smartlist_len(chan->u.cell_chan.outgoing_queue) > 0)) && + if (!(chan->outgoing_queue && + (smartlist_len(chan->outgoing_queue) > 0)) && chan->state == CHANNEL_STATE_OPEN) { /* Pick the right write function for this cell type and save the result */ switch (q->type) { case CELL_QUEUE_FIXED: - tor_assert(chan->u.cell_chan.write_cell); + tor_assert(chan->write_cell); tor_assert(q->u.fixed.cell); - result = chan->u.cell_chan.write_cell(chan, q->u.fixed.cell); + result = chan->write_cell(chan, q->u.fixed.cell); break; case CELL_QUEUE_PACKED: - tor_assert(chan->u.cell_chan.write_packed_cell); + tor_assert(chan->write_packed_cell); tor_assert(q->u.packed.packed_cell); - result = chan-> - u.cell_chan.write_packed_cell(chan, - q->u.packed.packed_cell); + result = chan->write_packed_cell(chan, q->u.packed.packed_cell); break; case CELL_QUEUE_VAR: - tor_assert(chan->u.cell_chan.write_var_cell); + tor_assert(chan->write_var_cell); tor_assert(q->u.var.var_cell); - result = chan->u.cell_chan.write_var_cell(chan, q->u.var.var_cell); + result = chan->write_var_cell(chan, q->u.var.var_cell); break; default: tor_assert(1); @@ -1323,21 +1589,21 @@ channel_write_cell_queue_entry(channel_t *chan, cell_queue_entry_t *q) /* If we're here the queue is empty, so it's drained too */ channel_timestamp_drained(chan); /* Update the counter */ - ++(chan->u.cell_chan.n_cells_xmitted); + ++(chan->n_cells_xmitted); } } if (!sent) { /* Not sent, queue it */ - if (!(chan->u.cell_chan.outgoing_queue)) - chan->u.cell_chan.outgoing_queue = smartlist_new(); + if (!(chan->outgoing_queue)) + chan->outgoing_queue = smartlist_new(); /* * We have to copy the queue entry passed in, since the caller probably * used the stack. */ tmp = tor_malloc(sizeof(*tmp)); memcpy(tmp, q, sizeof(*tmp)); - smartlist_add(chan->u.cell_chan.outgoing_queue, tmp); + smartlist_add(chan->outgoing_queue, tmp); /* Try to process the queue? */ if (chan->state == CHANNEL_STATE_OPEN) channel_flush_cells(chan); } @@ -1432,7 +1698,7 @@ void channel_change_state(channel_t *chan, channel_state_t to_state) { channel_state_t from_state; - unsigned char was_active, is_active, was_listening, is_listening; + unsigned char was_active, is_active; unsigned char was_in_id_map, is_in_id_map; tor_assert(chan); @@ -1442,26 +1708,13 @@ channel_change_state(channel_t *chan, channel_state_t to_state) tor_assert(channel_state_is_valid(to_state)); tor_assert(channel_state_can_transition(chan->state, to_state)); - if (chan->is_listener) { - tor_assert(from_state == CHANNEL_STATE_LISTENING || - from_state == CHANNEL_STATE_CLOSING || - from_state == CHANNEL_STATE_CLOSED || - from_state == CHANNEL_STATE_ERROR); - tor_assert(to_state == CHANNEL_STATE_LISTENING || - to_state == CHANNEL_STATE_CLOSING || - to_state == CHANNEL_STATE_CLOSED || - to_state == CHANNEL_STATE_ERROR); - } else { - tor_assert(from_state != CHANNEL_STATE_LISTENING); - tor_assert(to_state != CHANNEL_STATE_LISTENING); - } - /* Check for no-op transitions */ if (from_state == to_state) { log_debug(LD_CHANNEL, - "Got no-op transition from \"%s\" to itself on channel %p", + "Got no-op transition from \"%s\" to itself on channel %p" + "(global ID " U64_FORMAT ")", channel_state_to_string(to_state), - chan); + chan, U64_PRINTF_ARG(chan->global_identifier)); return; } @@ -1482,8 +1735,10 @@ channel_change_state(channel_t *chan, channel_state_t to_state) */ log_debug(LD_CHANNEL, - "Changing state of channel %p from \"%s\" to \"%s\"", + "Changing state of channel %p (global ID " U64_FORMAT + ") from \"%s\" to \"%s\"", chan, + U64_PRINTF_ARG(chan->global_identifier), channel_state_to_string(chan->state), channel_state_to_string(to_state)); @@ -1509,28 +1764,12 @@ channel_change_state(channel_t *chan, channel_state_t to_state) smartlist_add(active_channels, chan); } - was_listening = (from_state == CHANNEL_STATE_LISTENING); - is_listening = (to_state == CHANNEL_STATE_LISTENING); - - /* Need to put on listening list? */ - if (!was_listening && is_listening) { - if (!listening_channels) listening_channels = smartlist_new(); - smartlist_add(listening_channels, chan); - } - /* Need to remove from listening list? */ - else if (was_listening && !is_listening) { - if (listening_channels) smartlist_remove(listening_channels, chan); - } - - if (!(chan->is_listener) && - !tor_digest_is_zero(chan->u.cell_chan.identity_digest)) { + if (!tor_digest_is_zero(chan->identity_digest)) { /* Now we need to handle the identity map */ - was_in_id_map = !(from_state == CHANNEL_STATE_LISTENING || - from_state == CHANNEL_STATE_CLOSING || + was_in_id_map = !(from_state == CHANNEL_STATE_CLOSING || from_state == CHANNEL_STATE_CLOSED || from_state == CHANNEL_STATE_ERROR); - is_in_id_map = !(to_state == CHANNEL_STATE_LISTENING || - to_state == CHANNEL_STATE_CLOSING || + is_in_id_map = !(to_state == CHANNEL_STATE_CLOSING || to_state == CHANNEL_STATE_CLOSED || to_state == CHANNEL_STATE_ERROR); @@ -1541,30 +1780,110 @@ channel_change_state(channel_t *chan, channel_state_t to_state) } /* Tell circuits if we opened and stuff */ - if (to_state == CHANNEL_STATE_OPEN) channel_do_open_actions(chan); + if (to_state == CHANNEL_STATE_OPEN) { + channel_do_open_actions(chan); - if (!(chan->is_listener) && - to_state == CHANNEL_STATE_OPEN) { /* Check for queued cells to process */ - if (chan->u.cell_chan.incoming_queue && - smartlist_len(chan->u.cell_chan.incoming_queue) > 0) + if (chan->incoming_queue && + smartlist_len(chan->incoming_queue) > 0) channel_process_cells(chan); - if (chan->u.cell_chan.outgoing_queue && - smartlist_len(chan->u.cell_chan.outgoing_queue) > 0) + if (chan->outgoing_queue && + smartlist_len(chan->outgoing_queue) > 0) channel_flush_cells(chan); } else if (to_state == CHANNEL_STATE_CLOSED || to_state == CHANNEL_STATE_ERROR) { /* Assert that all queues are empty */ - if (chan->is_listener) { - tor_assert(!(chan->u.listener.incoming_list) || - smartlist_len(chan->u.listener.incoming_list) == 0); - } else { - tor_assert(!(chan->u.cell_chan.incoming_queue) || - smartlist_len(chan->u.cell_chan.incoming_queue) == 0); - tor_assert(!(chan->u.cell_chan.outgoing_queue) || - smartlist_len(chan->u.cell_chan.outgoing_queue) == 0); + tor_assert(!(chan->incoming_queue) || + smartlist_len(chan->incoming_queue) == 0); + tor_assert(!(chan->outgoing_queue) || + smartlist_len(chan->outgoing_queue) == 0); + } +} + +/** + * Change channel listener state + * + * This internal and subclass use only function is used to change channel + * listener state, performing all transition validity checks and whatever + * actions are appropriate to the state transition in question. + */ + +void +channel_listener_change_state(channel_listener_t *chan_l, + channel_listener_state_t to_state) +{ + channel_listener_state_t from_state; + unsigned char was_active, is_active; + + tor_assert(chan_l); + from_state = chan_l->state; + + tor_assert(channel_listener_state_is_valid(from_state)); + tor_assert(channel_listener_state_is_valid(to_state)); + tor_assert(channel_listener_state_can_transition(chan_l->state, to_state)); + + /* Check for no-op transitions */ + if (from_state == to_state) { + log_debug(LD_CHANNEL, + "Got no-op transition from \"%s\" to itself on channel " + "listener %p (global ID " U64_FORMAT ")", + channel_listener_state_to_string(to_state), + chan_l, U64_PRINTF_ARG(chan_l->global_identifier)); + return; + } + + /* If we're going to a closing or closed state, we must have a reason set */ + if (to_state == CHANNEL_LISTENER_STATE_CLOSING || + to_state == CHANNEL_LISTENER_STATE_CLOSED || + to_state == CHANNEL_LISTENER_STATE_ERROR) { + tor_assert(chan_l->reason_for_closing != CHANNEL_LISTENER_NOT_CLOSING); + } + + /* + * We need to maintain the queues here for some transitions: + * when we enter CHANNEL_STATE_OPEN (especially from CHANNEL_STATE_MAINT) + * we may have a backlog of cells to transmit, so drain the queues in + * that case, and when going to CHANNEL_STATE_CLOSED the subclass + * should have made sure to finish sending things (or gone to + * CHANNEL_STATE_ERROR if not possible), so we assert for that here. + */ + + log_debug(LD_CHANNEL, + "Changing state of channel listener %p (global ID " U64_FORMAT + "from \"%s\" to \"%s\"", + chan_l, U64_PRINTF_ARG(chan_l->global_identifier), + channel_listener_state_to_string(chan_l->state), + channel_listener_state_to_string(to_state)); + + chan_l->state = to_state; + + /* Need to add to the right lists if the channel listener is registered */ + if (chan_l->registered) { + was_active = !(from_state == CHANNEL_LISTENER_STATE_CLOSED || + from_state == CHANNEL_LISTENER_STATE_ERROR); + is_active = !(to_state == CHANNEL_LISTENER_STATE_CLOSED || + to_state == CHANNEL_LISTENER_STATE_ERROR); + + /* Need to take off active list and put on finished list? */ + if (was_active && !is_active) { + if (active_listeners) smartlist_remove(active_listeners, chan_l); + if (!finished_listeners) finished_listeners = smartlist_new(); + smartlist_add(finished_listeners, chan_l); + } + /* Need to put on active list? */ + else if (!was_active && is_active) { + if (finished_listeners) smartlist_remove(finished_listeners, chan_l); + if (!active_listeners) active_listeners = smartlist_new(); + smartlist_add(active_listeners, chan_l); } } + + if (to_state == CHANNEL_LISTENER_STATE_CLOSED || + to_state == CHANNEL_LISTENER_STATE_ERROR) { + /* Assert that the queue is empty */ + tor_assert(!(chan_l->incoming_list) || + smartlist_len(chan_l->incoming_list) == 0); + } } /** @@ -1586,7 +1905,6 @@ channel_flush_some_cells(channel_t *chan, ssize_t num_cells) int num_cells_from_circs; tor_assert(chan); - tor_assert(!(chan->is_listener)); if (num_cells < 0) unlimited = 1; if (!unlimited && num_cells <= flushed) goto done; @@ -1598,7 +1916,7 @@ channel_flush_some_cells(channel_t *chan, ssize_t num_cells) (unlimited ? -1 : num_cells - flushed)); if (!unlimited && num_cells <= flushed) goto done; - if (chan->u.cell_chan.active_circuits) { + if (chan->active_circuits) { /* Try to get more cells from any active circuits */ num_cells_from_circs = channel_flush_from_first_active_circuit(chan, @@ -1633,10 +1951,9 @@ channel_flush_some_cells_from_outgoing_queue(channel_t *chan, cell_queue_entry_t *q = NULL; tor_assert(chan); - tor_assert(!(chan->is_listener)); - tor_assert(chan->u.cell_chan.write_cell); - tor_assert(chan->u.cell_chan.write_packed_cell); - tor_assert(chan->u.cell_chan.write_var_cell); + tor_assert(chan->write_cell); + tor_assert(chan->write_packed_cell); + tor_assert(chan->write_var_cell); if (num_cells < 0) unlimited = 1; if (!unlimited && num_cells <= flushed) return 0; @@ -1644,15 +1961,15 @@ channel_flush_some_cells_from_outgoing_queue(channel_t *chan, /* If we aren't in CHANNEL_STATE_OPEN, nothing goes through */ if (chan->state == CHANNEL_STATE_OPEN) { while ((unlimited || num_cells > flushed) && - (chan->u.cell_chan.outgoing_queue && - (smartlist_len(chan->u.cell_chan.outgoing_queue) > 0))) { + (chan->outgoing_queue && + (smartlist_len(chan->outgoing_queue) > 0))) { /* * Ewww, smartlist_del_keeporder() is O(n) in list length; maybe a * a linked list would make more sense for the queue. */ /* Get the head of the queue */ - q = smartlist_get(chan->u.cell_chan.outgoing_queue, 0); + q = smartlist_get(chan->outgoing_queue, 0); if (q) { /* * Okay, we have a good queue entry, try to give it to the lower @@ -1661,60 +1978,63 @@ channel_flush_some_cells_from_outgoing_queue(channel_t *chan, switch (q->type) { case CELL_QUEUE_FIXED: if (q->u.fixed.cell) { - if (chan->u.cell_chan.write_cell(chan, + if (chan->write_cell(chan, q->u.fixed.cell)) { tor_free(q); ++flushed; channel_timestamp_xmit(chan); - ++(chan->u.cell_chan.n_cells_xmitted); + ++(chan->n_cells_xmitted); } /* Else couldn't write it; leave it on the queue */ } else { /* This shouldn't happen */ log_info(LD_CHANNEL, "Saw broken cell queue entry of type CELL_QUEUE_FIXED " - "with no cell on channel %p.", - chan); + "with no cell on channel %p " + "(global ID " U64_FORMAT ").", + chan, U64_PRINTF_ARG(chan->global_identifier)); /* Throw it away */ tor_free(q); } break; case CELL_QUEUE_PACKED: if (q->u.packed.packed_cell) { - if (chan->u.cell_chan.write_packed_cell(chan, + if (chan->write_packed_cell(chan, q->u.packed.packed_cell)) { tor_free(q); ++flushed; channel_timestamp_xmit(chan); - ++(chan->u.cell_chan.n_cells_xmitted); + ++(chan->n_cells_xmitted); } /* Else couldn't write it; leave it on the queue */ } else { /* This shouldn't happen */ log_info(LD_CHANNEL, "Saw broken cell queue entry of type CELL_QUEUE_PACKED " - "with no cell on channel %p.", - chan); + "with no cell on channel %p " + "(global ID " U64_FORMAT ").", + chan, U64_PRINTF_ARG(chan->global_identifier)); /* Throw it away */ tor_free(q); } break; case CELL_QUEUE_VAR: if (q->u.var.var_cell) { - if (chan->u.cell_chan.write_var_cell(chan, + if (chan->write_var_cell(chan, q->u.var.var_cell)) { tor_free(q); ++flushed; channel_timestamp_xmit(chan); - ++(chan->u.cell_chan.n_cells_xmitted); + ++(chan->n_cells_xmitted); } /* Else couldn't write it; leave it on the queue */ } else { /* This shouldn't happen */ log_info(LD_CHANNEL, "Saw broken cell queue entry of type CELL_QUEUE_VAR " - "with no cell on channel %p.", - chan); + "with no cell on channel %p " + "(global ID " U64_FORMAT ").", + chan, U64_PRINTF_ARG(chan->global_identifier)); /* Throw it away */ tor_free(q); } @@ -1722,30 +2042,31 @@ channel_flush_some_cells_from_outgoing_queue(channel_t *chan, default: /* Unknown type, log and free it */ log_info(LD_CHANNEL, - "Saw an unknown cell queue entry type %d on channel %p; " - "ignoring it. Someone should fix this.", - q->type, chan); + "Saw an unknown cell queue entry type %d on channel %p " + "(global ID " U64_FORMAT "; ignoring it." + " Someone should fix this.", + q->type, chan, U64_PRINTF_ARG(chan->global_identifier)); tor_free(q); /* tor_free() NULLs it out */ } } else { /* This shouldn't happen; log and throw it away */ log_info(LD_CHANNEL, - "Saw a NULL entry in the outgoing cell queue on channel %p; " - "this is definitely a bug.", - chan); + "Saw a NULL entry in the outgoing cell queue on channel %p " + "(global ID " U64_FORMAT "); this is definitely a bug.", + chan, U64_PRINTF_ARG(chan->global_identifier)); /* q is already NULL, so we know to delete that queue entry */ } /* if q got NULLed out, we used it and should remove the queue entry */ - if (!q) smartlist_del_keeporder(chan->u.cell_chan.outgoing_queue, 0); + if (!q) smartlist_del_keeporder(chan->outgoing_queue, 0); /* No cell removed from list, so we can't go on any further */ else break; } } /* Did we drain the queue? */ - if (!(chan->u.cell_chan.outgoing_queue) || - smartlist_len(chan->u.cell_chan.outgoing_queue) == 0) { + if (!(chan->outgoing_queue) || + smartlist_len(chan->outgoing_queue) == 0) { /* Timestamp it */ channel_timestamp_drained(chan); } @@ -1778,14 +2099,13 @@ int channel_more_to_flush(channel_t *chan) { tor_assert(chan); - tor_assert(!(chan->is_listener)); /* Check if we have any queued */ - if (chan->u.cell_chan.incoming_queue && - smartlist_len(chan->u.cell_chan.incoming_queue) > 0) return 1; + if (chan->incoming_queue && + smartlist_len(chan->incoming_queue) > 0) return 1; /* Check if any circuits would like to queue some */ - if (chan->u.cell_chan.active_circuits) return 1; + if (chan->active_circuits) return 1; /* Else no */ return 0; @@ -1802,10 +2122,9 @@ void channel_notify_flushed(channel_t *chan) { tor_assert(chan); - tor_assert(!(chan->is_listener)); - if (chan->u.cell_chan.dirreq_id != 0) - geoip_change_dirreq_state(chan->u.cell_chan.dirreq_id, + if (chan->dirreq_id != 0) + geoip_change_dirreq_state(chan->dirreq_id, DIRREQ_TUNNELED, DIRREQ_CHANNEL_BUFFER_FLUSHED); } @@ -1818,32 +2137,31 @@ channel_notify_flushed(channel_t *chan) */ void -channel_process_incoming(channel_t *listener) +channel_listener_process_incoming(channel_listener_t *listener) { tor_assert(listener); - tor_assert(listener->is_listener); + /* - * CHANNEL_STATE_CLOSING permitted because we drain the queue while - * closing a listener. + * CHANNEL_LISTENER_STATE_CLOSING permitted because we drain the queue + * while closing a listener. */ - tor_assert(listener->state == CHANNEL_STATE_LISTENING || - listener->state == CHANNEL_STATE_CLOSING); - tor_assert(listener->u.listener.listener); + tor_assert(listener->state == CHANNEL_LISTENER_STATE_LISTENING || + listener->state == CHANNEL_LISTENER_STATE_CLOSING); + tor_assert(listener->listener); log_debug(LD_CHANNEL, - "Processing queue of incoming connections for listening " - "channel %p (global ID " U64_FORMAT ")", + "Processing queue of incoming connections for channel " + "listener %p (global ID " U64_FORMAT ")", listener, U64_PRINTF_ARG(listener->global_identifier)); - if (!(listener->u.listener.incoming_list)) return; + if (!(listener->incoming_list)) return; - SMARTLIST_FOREACH_BEGIN(listener->u.listener.incoming_list, + SMARTLIST_FOREACH_BEGIN(listener->incoming_list, channel_t *, chan) { tor_assert(chan); - tor_assert(!(chan->is_listener)); log_debug(LD_CHANNEL, - "Handling incoming connection %p (" U64_FORMAT ") " + "Handling incoming channel %p (" U64_FORMAT ") " "for listener %p (" U64_FORMAT ")", chan, U64_PRINTF_ARG(chan->global_identifier), @@ -1851,11 +2169,11 @@ channel_process_incoming(channel_t *listener) U64_PRINTF_ARG(listener->global_identifier)); /* Make sure this is set correctly */ channel_mark_incoming(chan); - listener->u.listener.listener(listener, chan); + listener->listener(listener, chan); } SMARTLIST_FOREACH_END(chan); - smartlist_free(listener->u.listener.incoming_list); - listener->u.listener.incoming_list = NULL; + smartlist_free(listener->incoming_list); + listener->incoming_list = NULL; } /** @@ -1879,15 +2197,14 @@ channel_do_open_actions(channel_t *chan) time_t now = time(NULL); tor_assert(chan); - tor_assert(!(chan->is_listener)); started_here = channel_is_outgoing(chan); if (started_here) { circuit_build_times_network_is_live(&circ_times); - rep_hist_note_connect_succeeded(chan->u.cell_chan.identity_digest, now); + rep_hist_note_connect_succeeded(chan->identity_digest, now); if (entry_guard_register_connect_status( - chan->u.cell_chan.identity_digest, 1, 0, now) < 0) { + chan->identity_digest, 1, 0, now) < 0) { /* Close any circuits pending on this channel. We leave it in state * 'open' though, because it didn't actually *fail* -- we just * chose not to use it. */ @@ -1897,10 +2214,10 @@ channel_do_open_actions(channel_t *chan) circuit_n_chan_done(chan, 0); not_using = 1; } - router_set_status(chan->u.cell_chan.identity_digest, 1); + router_set_status(chan->identity_digest, 1); } else { /* only report it to the geoip module if it's not a known router */ - if (!router_get_by_id_digest(chan->u.cell_chan.identity_digest)) { + if (!router_get_by_id_digest(chan->identity_digest)) { if (channel_get_addr_if_possible(chan, &remote_addr)) { geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &remote_addr, now); @@ -1916,60 +2233,55 @@ channel_do_open_actions(channel_t *chan) * Queue an incoming channel on a listener * * Internal and subclass use only function to queue an incoming channel from - * a listening one. A subclass of channel_t should call this when a new + * a listener. A subclass of channel_listener_t should call this when a new * incoming channel is created. */ void -channel_queue_incoming(channel_t *listener, channel_t *incoming) +channel_listener_queue_incoming(channel_listener_t *listener, + channel_t *incoming) { int need_to_queue = 0; tor_assert(listener); - tor_assert(listener->is_listener); - tor_assert(listener->state == CHANNEL_STATE_LISTENING); + tor_assert(listener->state == CHANNEL_LISTENER_STATE_LISTENING); tor_assert(incoming); - tor_assert(!(incoming->is_listener)); - /* - * Other states are permitted because subclass might process activity - * on a channel at any time while it's queued, but a listener returning - * another listener makes no sense. - */ - tor_assert(incoming->state != CHANNEL_STATE_LISTENING); log_debug(LD_CHANNEL, - "Queueing incoming channel %p on listening channel %p", - incoming, listener); + "Queueing incoming channel %p (global ID " U64_FORMAT ") on " + "channel listener %p (global ID " U64_FORMAT ")", + incoming, U64_PRINTF_ARG(incoming->global_identifier), + listener, U64_PRINTF_ARG(listener->global_identifier)); /* Do we need to queue it, or can we just call the listener right away? */ - if (!(listener->u.listener.listener)) need_to_queue = 1; - if (listener->u.listener.incoming_list && - (smartlist_len(listener->u.listener.incoming_list) > 0)) + if (!(listener->listener)) need_to_queue = 1; + if (listener->incoming_list && + (smartlist_len(listener->incoming_list) > 0)) need_to_queue = 1; /* If we need to queue and have no queue, create one */ - if (need_to_queue && !(listener->u.listener.incoming_list)) { - listener->u.listener.incoming_list = smartlist_new(); + if (need_to_queue && !(listener->incoming_list)) { + listener->incoming_list = smartlist_new(); } /* Bump the counter and timestamp it */ - channel_timestamp_active(listener); - channel_timestamp_accepted(listener); - ++(listener->u.listener.n_accepted); + channel_listener_timestamp_active(listener); + channel_listener_timestamp_accepted(listener); + ++(listener->n_accepted); /* If we don't need to queue, process it right away */ if (!need_to_queue) { - tor_assert(listener->u.listener.listener); - listener->u.listener.listener(listener, incoming); + tor_assert(listener->listener); + listener->listener(listener, incoming); } /* * Otherwise, we need to queue; queue and then process the queue if * we can. */ else { - tor_assert(listener->u.listener.incoming_list); - smartlist_add(listener->u.listener.incoming_list, incoming); - if (listener->u.listener.listener) channel_process_incoming(listener); + tor_assert(listener->incoming_list); + smartlist_add(listener->incoming_list, incoming); + if (listener->listener) channel_listener_process_incoming(listener); } } @@ -1984,7 +2296,6 @@ void channel_process_cells(channel_t *chan) { tor_assert(chan); - tor_assert(!(chan->is_listener)); tor_assert(chan->state == CHANNEL_STATE_CLOSING || chan->state == CHANNEL_STATE_MAINT || chan->state == CHANNEL_STATE_OPEN); @@ -1994,40 +2305,44 @@ channel_process_cells(channel_t *chan) chan); /* Nothing we can do if we have no registered cell handlers */ - if (!(chan->u.cell_chan.cell_handler || - chan->u.cell_chan.var_cell_handler)) return; + if (!(chan->cell_handler || + chan->var_cell_handler)) return; /* Nothing we can do if we have no cells */ - if (!(chan->u.cell_chan.incoming_queue)) return; + if (!(chan->incoming_queue)) return; /* * Process cells until we're done or find one we have no current handler * for. */ - SMARTLIST_FOREACH_BEGIN(chan->u.cell_chan.incoming_queue, + SMARTLIST_FOREACH_BEGIN(chan->incoming_queue, cell_queue_entry_t *, q) { tor_assert(q); tor_assert(q->type == CELL_QUEUE_FIXED || q->type == CELL_QUEUE_VAR); if (q->type == CELL_QUEUE_FIXED && - chan->u.cell_chan.cell_handler) { + chan->cell_handler) { /* Handle a fixed-length cell */ tor_assert(q->u.fixed.cell); log_debug(LD_CHANNEL, - "Processing incoming cell_t %p for channel %p", - q->u.fixed.cell, chan); - chan->u.cell_chan.cell_handler(chan, q->u.fixed.cell); - SMARTLIST_DEL_CURRENT(chan->u.cell_chan.incoming_queue, q); + "Processing incoming cell_t %p for channel %p (global ID " + U64_FORMAT ")", + q->u.fixed.cell, chan, + U64_PRINTF_ARG(chan->global_identifier)); + chan->cell_handler(chan, q->u.fixed.cell); + SMARTLIST_DEL_CURRENT(chan->incoming_queue, q); tor_free(q); } else if (q->type == CELL_QUEUE_VAR && - chan->u.cell_chan.var_cell_handler) { + chan->var_cell_handler) { /* Handle a variable-length cell */ tor_assert(q->u.var.var_cell); log_debug(LD_CHANNEL, - "Processing incoming var_cell_t %p for channel %p", - q->u.var.var_cell, chan); - chan->u.cell_chan.var_cell_handler(chan, q->u.var.var_cell); - SMARTLIST_DEL_CURRENT(chan->u.cell_chan.incoming_queue, q); + "Processing incoming var_cell_t %p for channel %p (global ID " + U64_FORMAT ")", + q->u.var.var_cell, chan, + U64_PRINTF_ARG(chan->global_identifier)); + chan->var_cell_handler(chan, q->u.var.var_cell); + SMARTLIST_DEL_CURRENT(chan->incoming_queue, q); tor_free(q); } else { /* Can't handle this one */ @@ -2036,9 +2351,9 @@ channel_process_cells(channel_t *chan) } SMARTLIST_FOREACH_END(q); /* If the list is empty, free it */ - if (smartlist_len(chan->u.cell_chan.incoming_queue) == 0 ) { - smartlist_free(chan->u.cell_chan.incoming_queue); - chan->u.cell_chan.incoming_queue = NULL; + if (smartlist_len(chan->incoming_queue) == 0 ) { + smartlist_free(chan->incoming_queue); + chan->incoming_queue = NULL; } } @@ -2056,46 +2371,49 @@ channel_queue_cell(channel_t *chan, cell_t *cell) cell_queue_entry_t *q; tor_assert(chan); - tor_assert(!(chan->is_listener)); tor_assert(cell); tor_assert(chan->state == CHANNEL_STATE_OPEN); /* Do we need to queue it, or can we just call the handler right away? */ - if (!(chan->u.cell_chan.cell_handler)) need_to_queue = 1; - if (chan->u.cell_chan.incoming_queue && - (smartlist_len(chan->u.cell_chan.incoming_queue) > 0)) + if (!(chan->cell_handler)) need_to_queue = 1; + if (chan->incoming_queue && + (smartlist_len(chan->incoming_queue) > 0)) need_to_queue = 1; /* If we need to queue and have no queue, create one */ - if (need_to_queue && !(chan->u.cell_chan.incoming_queue)) { - chan->u.cell_chan.incoming_queue = smartlist_new(); + if (need_to_queue && !(chan->incoming_queue)) { + chan->incoming_queue = smartlist_new(); } /* Timestamp for receiving */ channel_timestamp_recv(chan); /* Update the counter */ - ++(chan->u.cell_chan.n_cells_recved); + ++(chan->n_cells_recved); /* If we don't need to queue we can just call cell_handler */ if (!need_to_queue) { - tor_assert(chan->u.cell_chan.cell_handler); + tor_assert(chan->cell_handler); log_debug(LD_CHANNEL, - "Directly handling incoming cell_t %p for channel %p", - cell, chan); - chan->u.cell_chan.cell_handler(chan, cell); + "Directly handling incoming cell_t %p for channel %p " + "(global ID " U64_FORMAT ")", + cell, chan, + U64_PRINTF_ARG(chan->global_identifier)); + chan->cell_handler(chan, cell); } else { /* Otherwise queue it and then process the queue if possible. */ - tor_assert(chan->u.cell_chan.incoming_queue); + tor_assert(chan->incoming_queue); q = tor_malloc(sizeof(*q)); q->type = CELL_QUEUE_FIXED; q->u.fixed.cell = cell; log_debug(LD_CHANNEL, - "Queueing incoming cell_t %p for channel %p", - cell, chan); - smartlist_add(chan->u.cell_chan.incoming_queue, q); - if (chan->u.cell_chan.cell_handler || - chan->u.cell_chan.var_cell_handler) { + "Queueing incoming cell_t %p for channel %p " + "(global ID " U64_FORMAT ")", + cell, chan, + U64_PRINTF_ARG(chan->global_identifier)); + smartlist_add(chan->incoming_queue, q); + if (chan->cell_handler || + chan->var_cell_handler) { channel_process_cells(chan); } } @@ -2115,46 +2433,49 @@ channel_queue_var_cell(channel_t *chan, var_cell_t *var_cell) cell_queue_entry_t *q; tor_assert(chan); - tor_assert(!(chan->is_listener)); tor_assert(var_cell); tor_assert(chan->state == CHANNEL_STATE_OPEN); /* Do we need to queue it, or can we just call the handler right away? */ - if (!(chan->u.cell_chan.var_cell_handler)) need_to_queue = 1; - if (chan->u.cell_chan.incoming_queue && - (smartlist_len(chan->u.cell_chan.incoming_queue) > 0)) + if (!(chan->var_cell_handler)) need_to_queue = 1; + if (chan->incoming_queue && + (smartlist_len(chan->incoming_queue) > 0)) need_to_queue = 1; /* If we need to queue and have no queue, create one */ - if (need_to_queue && !(chan->u.cell_chan.incoming_queue)) { - chan->u.cell_chan.incoming_queue = smartlist_new(); + if (need_to_queue && !(chan->incoming_queue)) { + chan->incoming_queue = smartlist_new(); } /* Timestamp for receiving */ channel_timestamp_recv(chan); /* Update the counter */ - ++(chan->u.cell_chan.n_cells_recved); + ++(chan->n_cells_recved); /* If we don't need to queue we can just call cell_handler */ if (!need_to_queue) { - tor_assert(chan->u.cell_chan.var_cell_handler); + tor_assert(chan->var_cell_handler); log_debug(LD_CHANNEL, - "Directly handling incoming var_cell_t %p for channel %p", - var_cell, chan); - chan->u.cell_chan.var_cell_handler(chan, var_cell); + "Directly handling incoming var_cell_t %p for channel %p " + "(global ID " U64_FORMAT ")", + var_cell, chan, + U64_PRINTF_ARG(chan->global_identifier)); + chan->var_cell_handler(chan, var_cell); } else { /* Otherwise queue it and then process the queue if possible. */ - tor_assert(chan->u.cell_chan.incoming_queue); + tor_assert(chan->incoming_queue); q = tor_malloc(sizeof(*q)); q->type = CELL_QUEUE_VAR; q->u.var.var_cell = var_cell; log_debug(LD_CHANNEL, - "Queueing incoming var_cell_t %p for channel %p", - var_cell, chan); - smartlist_add(chan->u.cell_chan.incoming_queue, q); - if (chan->u.cell_chan.cell_handler || - chan->u.cell_chan.var_cell_handler) { + "Queueing incoming var_cell_t %p for channel %p " + "(global ID " U64_FORMAT ")", + var_cell, chan, + U64_PRINTF_ARG(chan->global_identifier)); + smartlist_add(chan->incoming_queue, q); + if (chan->cell_handler || + chan->var_cell_handler) { channel_process_cells(chan); } } @@ -2174,13 +2495,16 @@ channel_send_destroy(circid_t circ_id, channel_t *chan, int reason) cell_t cell; tor_assert(chan); - tor_assert(!(chan->is_listener)); memset(&cell, 0, sizeof(cell_t)); cell.circ_id = circ_id; cell.command = CELL_DESTROY; cell.payload[0] = (uint8_t) reason; - log_debug(LD_OR,"Sending destroy (circID %d).", circ_id); + log_debug(LD_OR, + "Sending destroy (circID %d) on channel %p " + "(global ID " U64_FORMAT ")", + circ_id, chan, + U64_PRINTF_ARG(chan->global_identifier)); channel_write_cell(chan, &cell); @@ -2202,23 +2526,52 @@ channel_dumpstats(int severity) "Dumping statistics about %d channels:", smartlist_len(all_channels)); log(severity, LD_GENERAL, - "%d are active, %d are listeners, and %d are done and " - "waiting for cleanup", + "%d are active, and %d are done and waiting for cleanup", (active_channels != NULL) ? smartlist_len(active_channels) : 0, - (listening_channels != NULL) ? - smartlist_len(listening_channels) : 0, (finished_channels != NULL) ? smartlist_len(finished_channels) : 0); - SMARTLIST_FOREACH(all_channels, channel_t *, chan, - channel_dump_statistics(chan, severity)); + SMARTLIST_FOREACH(all_channels, channel_t *, chan, + channel_dump_statistics(chan, severity)); + + log(severity, LD_GENERAL, + "Done spamming about channels now"); + } else { + log(severity, LD_GENERAL, + "No channels to dump"); + } +} + +/** + * Dump channel listener statistics to the log + * + * This is called from dumpstats() in main.c and spams the log with + * statistics on channel listeners. + */ + +void +channel_listener_dumpstats(int severity) +{ + if (all_listeners && smartlist_len(all_listeners) > 0) { + log(severity, LD_GENERAL, + "Dumping statistics about %d channel listeners:", + smartlist_len(all_listeners)); + log(severity, LD_GENERAL, + "%d are active and %d are done and waiting for cleanup", + (active_listeners != NULL) ? + smartlist_len(active_listeners) : 0, + (finished_listeners != NULL) ? + smartlist_len(finished_listeners) : 0); + + SMARTLIST_FOREACH(all_listeners, channel_listener_t *, chan_l, + channel_listener_dump_statistics(chan_l, severity)); log(severity, LD_GENERAL, - "Done spamming about channels now"); + "Done spamming about channel listeners now"); } else { log(severity, LD_GENERAL, - "No channels to dump"); + "No channel listeners to dump"); } } @@ -2249,6 +2602,91 @@ channel_run_cleanup(void) } SMARTLIST_FOREACH_END(curr); } +/** + * Clean up channel listeners + * + * This gets called periodically from run_scheduled_events() in main.c; + * it cleans up after closed channel listeners. + */ + +void +channel_listener_run_cleanup(void) +{ + channel_listener_t *tmp = NULL; + + /* Check if we need to do anything */ + if (!finished_listeners || smartlist_len(finished_listeners) == 0) return; + + /* Iterate through finished_channels and get rid of them */ + SMARTLIST_FOREACH_BEGIN(finished_listeners, channel_listener_t *, curr) { + tmp = curr; + /* Remove it from the list */ + SMARTLIST_DEL_CURRENT(finished_listeners, curr); + /* Also unregister it */ + channel_listener_unregister(tmp); + /* ... and free it */ + channel_listener_free(tmp); + } SMARTLIST_FOREACH_END(curr); +} + +/** + * Free a list of channels for channel_free_all() + */ + +static void +channel_free_list(smartlist_t *channels, int mark_for_close) +{ + if (!channels) return; + + SMARTLIST_FOREACH_BEGIN(channels, channel_t *, curr) { + /* Deregister and free it */ + tor_assert(curr); + log_debug(LD_CHANNEL, + "Cleaning up channel %p (global ID " U64_FORMAT ") " + "in state %s (%d)", + curr, U64_PRINTF_ARG(curr->global_identifier), + channel_state_to_string(curr->state), curr->state); + channel_unregister(curr); + if (mark_for_close) { + if (!(curr->state == CHANNEL_STATE_CLOSING || + curr->state == CHANNEL_STATE_CLOSED || + curr->state == CHANNEL_STATE_ERROR)) { + channel_mark_for_close(curr); + } + channel_force_free(curr); + } else channel_free(curr); + } SMARTLIST_FOREACH_END(curr); +} + +/** + * Free a list of channel listeners for channel_free_all() + */ + +static void +channel_listener_free_list(smartlist_t *listeners, int mark_for_close) +{ + if (!listeners) return; + + SMARTLIST_FOREACH_BEGIN(listeners, channel_listener_t *, curr) { + /* Deregister and free it */ + tor_assert(curr); + log_debug(LD_CHANNEL, + "Cleaning up channel listener %p (global ID " U64_FORMAT ") " + "in state %s (%d)", + curr, U64_PRINTF_ARG(curr->global_identifier), + channel_listener_state_to_string(curr->state), curr->state); + channel_listener_unregister(curr); + if (mark_for_close) { + if (!(curr->state == CHANNEL_LISTENER_STATE_CLOSING || + curr->state == CHANNEL_LISTENER_STATE_CLOSED || + curr->state == CHANNEL_LISTENER_STATE_ERROR)) { + channel_listener_mark_for_close(curr); + } + channel_listener_force_free(curr); + } else channel_listener_free(curr); + } SMARTLIST_FOREACH_END(curr); +} + /** * Close all channels and free everything * @@ -2266,91 +2704,46 @@ channel_free_all(void) /* First, let's go for finished channels */ if (finished_channels) { - SMARTLIST_FOREACH_BEGIN(finished_channels, channel_t *, curr) { - /* Deregister and free it */ - tor_assert(curr); - log_debug(LD_CHANNEL, - "Cleaning up finished channel %p (ID " U64_FORMAT ") " - "in state %s (%d)", - curr, U64_PRINTF_ARG(curr->global_identifier), - channel_state_to_string(curr->state), curr->state); - channel_unregister(curr); - channel_free(curr); - } SMARTLIST_FOREACH_END(curr); - + channel_free_list(finished_channels, 0); smartlist_free(finished_channels); finished_channels = NULL; } - /* Now the listeners */ - if (listening_channels) { - SMARTLIST_FOREACH_BEGIN(listening_channels, channel_t *, curr) { - /* Close, deregister and free it */ - tor_assert(curr); - log_debug(LD_CHANNEL, - "Cleaning up listening channel %p (ID " U64_FORMAT ") " - "in state %s (%d)", - curr, U64_PRINTF_ARG(curr->global_identifier), - channel_state_to_string(curr->state), curr->state); - /* - * We have to unregister first so we don't put it in finished_channels - * and allocate that again on close. - */ - channel_unregister(curr); - channel_mark_for_close(curr); - channel_force_free(curr); - } SMARTLIST_FOREACH_END(curr); - - smartlist_free(listening_channels); - listening_channels = NULL; + /* Now the finished listeners */ + if (finished_listeners) { + channel_listener_free_list(finished_listeners, 0); + smartlist_free(finished_listeners); + finished_listeners = NULL; } /* Now all active channels */ if (active_channels) { - SMARTLIST_FOREACH_BEGIN(active_channels, channel_t *, curr) { - /* Close, deregister and free it */ - tor_assert(curr); - log_debug(LD_CHANNEL, - "Cleaning up active channel %p (ID " U64_FORMAT ") " - "in state %s (%d)", - curr, U64_PRINTF_ARG(curr->global_identifier), - channel_state_to_string(curr->state), curr->state); - /* - * We have to unregister first so we don't put it in finished_channels - * and allocate that again on close. - */ - channel_unregister(curr); - channel_mark_for_close(curr); - channel_force_free(curr); - } SMARTLIST_FOREACH_END(curr); - + channel_free_list(active_channels, 1); smartlist_free(active_channels); active_channels = NULL; } + /* Now all active listeners */ + if (active_listeners) { + channel_listener_free_list(active_listeners, 1); + smartlist_free(active_listeners); + active_listeners = NULL; + } + /* Now all channels, in case any are left over */ if (all_channels) { - SMARTLIST_FOREACH_BEGIN(all_channels, channel_t *, curr) { - /* Close, deregister and free it */ - tor_assert(curr); - log_debug(LD_CHANNEL, - "Cleaning up leftover channel %p (ID " U64_FORMAT ") " - "in state %s (%d)", - curr, U64_PRINTF_ARG(curr->global_identifier), - channel_state_to_string(curr->state), curr->state); - channel_unregister(curr); - if (!(curr->state == CHANNEL_STATE_CLOSING || - curr->state == CHANNEL_STATE_CLOSED || - curr->state == CHANNEL_STATE_ERROR)) { - channel_mark_for_close(curr); - } - channel_force_free(curr); - } SMARTLIST_FOREACH_END(curr); - + channel_free_list(all_channels, 1); smartlist_free(all_channels); all_channels = NULL; } + /* Now all listeners, in case any are left over */ + if (all_listeners) { + channel_listener_free_list(all_listeners, 1); + smartlist_free(all_listeners); + all_listeners = NULL; + } + /* Now free channel_identity_map */ if (channel_identity_map) { log_debug(LD_CHANNEL, @@ -2409,8 +2802,6 @@ channel_is_better(time_t now, channel_t *a, channel_t *b, tor_assert(a); tor_assert(b); - tor_assert(!(a->is_listener)); - tor_assert(!(b->is_listener)); /* Check if one is canonical and the other isn't first */ a_is_canonical = channel_is_canonical(a); @@ -2426,8 +2817,8 @@ channel_is_better(time_t now, channel_t *a, channel_t *b, * one that has no circuits is in its grace period. */ - a_has_circs = (a->u.cell_chan.n_circuits > 0); - b_has_circs = (b->u.cell_chan.n_circuits > 0); + a_has_circs = (a->n_circuits > 0); + b_has_circs = (b->n_circuits > 0); a_grace = (forgive_new_connections && (now < channel_when_created(a) + NEW_CHAN_GRACE_PERIOD)); b_grace = (forgive_new_connections && @@ -2479,14 +2870,12 @@ channel_get_for_extend(const char *digest, * iteration. */ for (; chan; chan = channel_next_with_digest(chan)) { - tor_assert(!(chan->is_listener)); - tor_assert(tor_memeq(chan->u.cell_chan.identity_digest, + tor_assert(tor_memeq(chan->identity_digest, digest, DIGEST_LEN)); if (chan->state == CHANNEL_STATE_CLOSING || chan->state == CHANNEL_STATE_CLOSED || - chan->state == CHANNEL_STATE_ERROR || - chan->state == CHANNEL_STATE_LISTENING) + chan->state == CHANNEL_STATE_ERROR) continue; /* Never return a channel on which the other end appears to be @@ -2562,7 +2951,7 @@ channel_get_for_extend(const char *digest, } /** - * Describe the transport subclass + * Describe the transport subclass for a channel * * Invoke a method to get a string description of the lower-layer * transport for this channel. @@ -2577,6 +2966,22 @@ channel_describe_transport(channel_t *chan) return chan->describe_transport(chan); } +/** + * Describe the transport subclass for a channel listener + * + * Invoke a method to get a string description of the lower-layer + * transport for this channel listener. + */ + +const char * +channel_listener_describe_transport(channel_listener_t *chan_l) +{ + tor_assert(chan_l); + tor_assert(chan_l->describe_transport); + + return chan_l->describe_transport(chan_l); +} + /** * Dump channel statistics * @@ -2598,13 +3003,10 @@ channel_dump_statistics(channel_t *chan, int severity) log(severity, LD_GENERAL, "Channel " U64_FORMAT " (at %p) with transport %s is in state " - "%s (%d) and %s", + "%s (%d)", U64_PRINTF_ARG(chan->global_identifier), chan, channel_describe_transport(chan), - channel_state_to_string(chan->state), chan->state, - chan->is_listener ? - "listens for incoming connections" : - "transports cells"); + channel_state_to_string(chan->state), chan->state); log(severity, LD_GENERAL, " * Channel " U64_FORMAT " was created at " U64_FORMAT " (" U64_FORMAT " seconds ago) " @@ -2614,204 +3016,239 @@ channel_dump_statistics(channel_t *chan, int severity) U64_PRINTF_ARG(now - chan->timestamp_created), U64_PRINTF_ARG(chan->timestamp_active), U64_PRINTF_ARG(now - chan->timestamp_active)); - if (chan->is_listener) { + + /* Handle digest and nickname */ + if (!tor_digest_is_zero(chan->identity_digest)) { + if (chan->nickname) { + log(severity, LD_GENERAL, + " * Channel " U64_FORMAT " says it is connected " + "to an OR with digest %s and nickname %s", + U64_PRINTF_ARG(chan->global_identifier), + hex_str(chan->identity_digest, DIGEST_LEN), + chan->nickname); + } else { + log(severity, LD_GENERAL, + " * Channel " U64_FORMAT " says it is connected " + "to an OR with digest %s and no known nickname", + U64_PRINTF_ARG(chan->global_identifier), + hex_str(chan->identity_digest, DIGEST_LEN)); + } + } else { + if (chan->nickname) { + log(severity, LD_GENERAL, + " * Channel " U64_FORMAT " does not know the digest" + " of the OR it is connected to, but reports its nickname is %s", + U64_PRINTF_ARG(chan->global_identifier), + chan->nickname); + } else { + log(severity, LD_GENERAL, + " * Channel " U64_FORMAT " does not know the digest" + " or the nickname of the OR it is connected to", + U64_PRINTF_ARG(chan->global_identifier)); + } + } + + /* Handle remote address and descriptions */ + have_remote_addr = channel_get_addr_if_possible(chan, &remote_addr); + if (have_remote_addr) { + remote_addr_str = tor_dup_addr(&remote_addr); log(severity, LD_GENERAL, - " * Listener channel " U64_FORMAT " last accepted an incoming " - "channel at " U64_FORMAT " (" U64_FORMAT " seconds ago) " - "and has accepted " U64_FORMAT " channels in total", + " * Channel " U64_FORMAT " says its remote address" + " is %s, and gives a canonical description of \"%s\" and an " + "actual description of \"%s\"", + U64_PRINTF_ARG(chan->global_identifier), + remote_addr_str, + channel_get_canonical_remote_descr(chan), + channel_get_actual_remote_descr(chan)); + tor_free(remote_addr_str); + } else { + log(severity, LD_GENERAL, + " * Channel " U64_FORMAT " does not know its remote " + "address, but gives a canonical description of \"%s\" and an " + "actual description of \"%s\"", U64_PRINTF_ARG(chan->global_identifier), - U64_PRINTF_ARG(chan->u.listener.timestamp_accepted), - U64_PRINTF_ARG(now - chan->u.listener.timestamp_accepted), - U64_PRINTF_ARG(chan->u.listener.n_accepted)); + channel_get_canonical_remote_descr(chan), + channel_get_actual_remote_descr(chan)); + } - /* - * If it's sensible to do so, get the rate of incoming channels on this - * listener - */ - if (now > chan->timestamp_created && - chan->timestamp_created > 0 && - chan->u.listener.n_accepted > 0) { - avg = (double)(chan->u.listener.n_accepted) / age; + /* Handle marks */ + log(severity, LD_GENERAL, + " * Channel " U64_FORMAT " has these marks: %s %s %s " + "%s %s %s", + U64_PRINTF_ARG(chan->global_identifier), + channel_is_bad_for_new_circs(chan) ? + "bad_for_new_circs" : "!bad_for_new_circs", + channel_is_canonical(chan) ? + "canonical" : "!canonical", + channel_is_canonical_is_reliable(chan) ? + "is_canonical_is_reliable" : + "!is_canonical_is_reliable", + channel_is_client(chan) ? + "client" : "!client", + channel_is_local(chan) ? + "local" : "!local", + channel_is_incoming(chan) ? + "incoming" : "outgoing"); + + /* Describe queues */ + log(severity, LD_GENERAL, + " * Channel " U64_FORMAT " has %d queued incoming cells" + " and %d queued outgoing cells", + U64_PRINTF_ARG(chan->global_identifier), + (chan->incoming_queue != NULL) ? + smartlist_len(chan->incoming_queue) : 0, + (chan->outgoing_queue != NULL) ? + smartlist_len(chan->outgoing_queue) : 0); + + /* Describe circuits */ + log(severity, LD_GENERAL, + " * Channel " U64_FORMAT " has %d active circuits out of" + " %d in total", + U64_PRINTF_ARG(chan->global_identifier), + (chan->active_circuit_pqueue != NULL) ? + smartlist_len(chan->active_circuit_pqueue) : 0, + chan->n_circuits); + + /* Describe timestamps */ + log(severity, LD_GENERAL, + " * Channel " U64_FORMAT " was last used by a " + "client at " U64_FORMAT " (" U64_FORMAT " seconds ago)", + U64_PRINTF_ARG(chan->global_identifier), + U64_PRINTF_ARG(chan->timestamp_client), + U64_PRINTF_ARG(now - chan->timestamp_client)); + log(severity, LD_GENERAL, + " * Channel " U64_FORMAT " was last drained at " + U64_FORMAT " (" U64_FORMAT " seconds ago)", + U64_PRINTF_ARG(chan->global_identifier), + U64_PRINTF_ARG(chan->timestamp_drained), + U64_PRINTF_ARG(now - chan->timestamp_drained)); + log(severity, LD_GENERAL, + " * Channel " U64_FORMAT " last received a cell " + "at " U64_FORMAT " (" U64_FORMAT " seconds ago)", + U64_PRINTF_ARG(chan->global_identifier), + U64_PRINTF_ARG(chan->timestamp_recv), + U64_PRINTF_ARG(now - chan->timestamp_recv)); + log(severity, LD_GENERAL, + " * Channel " U64_FORMAT " last trasmitted a cell " + "at " U64_FORMAT " (" U64_FORMAT " seconds ago)", + U64_PRINTF_ARG(chan->global_identifier), + U64_PRINTF_ARG(chan->timestamp_xmit), + U64_PRINTF_ARG(now - chan->timestamp_xmit)); + + /* Describe counters and rates */ + log(severity, LD_GENERAL, + " * Channel " U64_FORMAT " has received " + U64_FORMAT " cells and transmitted " U64_FORMAT, + U64_PRINTF_ARG(chan->global_identifier), + U64_PRINTF_ARG(chan->n_cells_recved), + U64_PRINTF_ARG(chan->n_cells_xmitted)); + if (now > chan->timestamp_created && + chan->timestamp_created > 0) { + if (chan->n_cells_recved > 0) { + avg = (double)(chan->n_cells_recved) / age; if (avg >= 1.0) { log(severity, LD_GENERAL, - " * Listener channel " U64_FORMAT " has averaged %f incoming " - "channels per second", + " * Channel " U64_FORMAT " has averaged %f " + "cells received per second", U64_PRINTF_ARG(chan->global_identifier), avg); } else if (avg >= 0.0) { interval = 1.0 / avg; log(severity, LD_GENERAL, - " * Listener channel " U64_FORMAT " has averaged %f seconds " - "between incoming channels", + " * Channel " U64_FORMAT " has averaged %f " + "seconds between received cells", U64_PRINTF_ARG(chan->global_identifier), interval); } } - } else { - /* Handle digest and nickname */ - if (!tor_digest_is_zero(chan->u.cell_chan.identity_digest)) { - if (chan->u.cell_chan.nickname) { - log(severity, LD_GENERAL, - " * Cell-bearing channel " U64_FORMAT " says it is connected " - "to an OR with digest %s and nickname %s", - U64_PRINTF_ARG(chan->global_identifier), - hex_str(chan->u.cell_chan.identity_digest, DIGEST_LEN), - chan->u.cell_chan.nickname); - } else { - log(severity, LD_GENERAL, - " * Cell-bearing channel " U64_FORMAT " says it is connected " - "to an OR with digest %s and no known nickname", - U64_PRINTF_ARG(chan->global_identifier), - hex_str(chan->u.cell_chan.identity_digest, DIGEST_LEN)); - } - } else { - if (chan->u.cell_chan.nickname) { + if (chan->n_cells_xmitted > 0) { + avg = (double)(chan->n_cells_xmitted) / age; + if (avg >= 1.0) { log(severity, LD_GENERAL, - " * Cell-bearing channel " U64_FORMAT " does not know the digest" - " of the OR it is connected to, but reports its nickname is %s", - U64_PRINTF_ARG(chan->global_identifier), - chan->u.cell_chan.nickname); - } else { + " * Channel " U64_FORMAT " has averaged %f " + "cells transmitted per second", + U64_PRINTF_ARG(chan->global_identifier), avg); + } else if (avg >= 0.0) { + interval = 1.0 / avg; log(severity, LD_GENERAL, - " * Cell-bearing channel " U64_FORMAT " does not know the digest" - " or the nickname of the OR it is connected to", - U64_PRINTF_ARG(chan->global_identifier)); + " * Channel " U64_FORMAT " has averaged %f " + "seconds between transmitted cells", + U64_PRINTF_ARG(chan->global_identifier), interval); } } + } - /* Handle remote address and descriptions */ - have_remote_addr = channel_get_addr_if_possible(chan, &remote_addr); - if (have_remote_addr) { - remote_addr_str = tor_dup_addr(&remote_addr); - log(severity, LD_GENERAL, - " * Cell-bearing channel " U64_FORMAT " says its remote address" - " is %s, and gives a canonical description of \"%s\" and an " - "actual description of \"%s\"", - U64_PRINTF_ARG(chan->global_identifier), - remote_addr_str, - channel_get_canonical_remote_descr(chan), - channel_get_actual_remote_descr(chan)); - tor_free(remote_addr_str); - } else { - log(severity, LD_GENERAL, - " * Cell-bearing channel " U64_FORMAT " does not know its remote " - "address, but gives a canonical description of \"%s\" and an " - "actual description of \"%s\"", - U64_PRINTF_ARG(chan->global_identifier), - channel_get_canonical_remote_descr(chan), - channel_get_actual_remote_descr(chan)); - } + /* Dump anything the lower layer has to say */ + channel_dump_transport_statistics(chan, severity); +} - /* Handle marks */ - log(severity, LD_GENERAL, - " * Cell-bearing channel " U64_FORMAT " has these marks: %s %s %s " - "%s %s %s", - U64_PRINTF_ARG(chan->global_identifier), - channel_is_bad_for_new_circs(chan) ? - "bad_for_new_circs" : "!bad_for_new_circs", - channel_is_canonical(chan) ? - "canonical" : "!canonical", - channel_is_canonical_is_reliable(chan) ? - "is_canonical_is_reliable" : - "!is_canonical_is_reliable", - channel_is_client(chan) ? - "client" : "!client", - channel_is_local(chan) ? - "local" : "!local", - channel_is_incoming(chan) ? - "incoming" : "outgoing"); - - /* Describe queues */ - log(severity, LD_GENERAL, - " * Cell-bearing channel " U64_FORMAT " has %d queued incoming cells" - " and %d queued outgoing cells", - U64_PRINTF_ARG(chan->global_identifier), - (chan->u.cell_chan.incoming_queue != NULL) ? - smartlist_len(chan->u.cell_chan.incoming_queue) : 0, - (chan->u.cell_chan.outgoing_queue != NULL) ? - smartlist_len(chan->u.cell_chan.outgoing_queue) : 0); +/** + * Dump channel listener statistics + * + * Dump statistics for one channel listener to the log + */ - /* Describe circuits */ - log(severity, LD_GENERAL, - " * Cell-bearing channel " U64_FORMAT " has %d active circuits out of" - " %d in total", - U64_PRINTF_ARG(chan->global_identifier), - (chan->u.cell_chan.active_circuit_pqueue != NULL) ? - smartlist_len(chan->u.cell_chan.active_circuit_pqueue) : 0, - chan->u.cell_chan.n_circuits); +void +channel_listener_dump_statistics(channel_listener_t *chan_l, int severity) +{ + double avg, interval, age; + time_t now = time(NULL); - /* Describe timestamps */ - log(severity, LD_GENERAL, - " * Cell-bearing channel " U64_FORMAT " was last used by a " - "client at " U64_FORMAT " (" U64_FORMAT " seconds ago)", - U64_PRINTF_ARG(chan->global_identifier), - U64_PRINTF_ARG(chan->u.cell_chan.timestamp_client), - U64_PRINTF_ARG(now - chan->u.cell_chan.timestamp_client)); - log(severity, LD_GENERAL, - " * Cell-bearing channel " U64_FORMAT " was last drained at " - U64_FORMAT " (" U64_FORMAT " seconds ago)", - U64_PRINTF_ARG(chan->global_identifier), - U64_PRINTF_ARG(chan->u.cell_chan.timestamp_drained), - U64_PRINTF_ARG(now - chan->u.cell_chan.timestamp_drained)); - log(severity, LD_GENERAL, - " * Cell-bearing channel " U64_FORMAT " last received a cell " - "at " U64_FORMAT " (" U64_FORMAT " seconds ago)", - U64_PRINTF_ARG(chan->global_identifier), - U64_PRINTF_ARG(chan->u.cell_chan.timestamp_recv), - U64_PRINTF_ARG(now - chan->u.cell_chan.timestamp_recv)); - log(severity, LD_GENERAL, - " * Cell-bearing channel " U64_FORMAT " last trasmitted a cell " - "at " U64_FORMAT " (" U64_FORMAT " seconds ago)", - U64_PRINTF_ARG(chan->global_identifier), - U64_PRINTF_ARG(chan->u.cell_chan.timestamp_xmit), - U64_PRINTF_ARG(now - chan->u.cell_chan.timestamp_xmit)); + tor_assert(chan_l); - /* Describe counters and rates */ - log(severity, LD_GENERAL, - " * Cell-bearing channel " U64_FORMAT " has received " - U64_FORMAT " cells and transmitted " U64_FORMAT, - U64_PRINTF_ARG(chan->global_identifier), - U64_PRINTF_ARG(chan->u.cell_chan.n_cells_recved), - U64_PRINTF_ARG(chan->u.cell_chan.n_cells_xmitted)); - if (now > chan->timestamp_created && - chan->timestamp_created > 0) { - if (chan->u.cell_chan.n_cells_recved > 0) { - avg = (double)(chan->u.cell_chan.n_cells_recved) / age; - if (avg >= 1.0) { - log(severity, LD_GENERAL, - " * Cell-bearing channel " U64_FORMAT " has averaged %f " - "cells received per second", - U64_PRINTF_ARG(chan->global_identifier), avg); - } else if (avg >= 0.0) { - interval = 1.0 / avg; - log(severity, LD_GENERAL, - " * Cell-bearing channel " U64_FORMAT " has averaged %f " - "seconds between received cells", - U64_PRINTF_ARG(chan->global_identifier), interval); - } - } - if (chan->u.cell_chan.n_cells_xmitted > 0) { - avg = (double)(chan->u.cell_chan.n_cells_xmitted) / age; - if (avg >= 1.0) { - log(severity, LD_GENERAL, - " * Cell-bearing channel " U64_FORMAT " has averaged %f " - "cells transmitted per second", - U64_PRINTF_ARG(chan->global_identifier), avg); - } else if (avg >= 0.0) { - interval = 1.0 / avg; - log(severity, LD_GENERAL, - " * Cell-bearing channel " U64_FORMAT " has averaged %f " - "seconds between transmitted cells", - U64_PRINTF_ARG(chan->global_identifier), interval); - } - } + age = (double)(now - chan_l->timestamp_created); + + log(severity, LD_GENERAL, + "Channel listener " U64_FORMAT " (at %p) with transport %s is in " + "state %s (%d)", + U64_PRINTF_ARG(chan_l->global_identifier), chan_l, + channel_listener_describe_transport(chan_l), + channel_listener_state_to_string(chan_l->state), chan_l->state); + log(severity, LD_GENERAL, + " * Channel listener " U64_FORMAT " was created at " U64_FORMAT + " (" U64_FORMAT " seconds ago) " + "and last active at " U64_FORMAT " (" U64_FORMAT " seconds ago)", + U64_PRINTF_ARG(chan_l->global_identifier), + U64_PRINTF_ARG(chan_l->timestamp_created), + U64_PRINTF_ARG(now - chan_l->timestamp_created), + U64_PRINTF_ARG(chan_l->timestamp_active), + U64_PRINTF_ARG(now - chan_l->timestamp_active)); + + log(severity, LD_GENERAL, + " * Channel listener " U64_FORMAT " last accepted an incoming " + "channel at " U64_FORMAT " (" U64_FORMAT " seconds ago) " + "and has accepted " U64_FORMAT " channels in total", + U64_PRINTF_ARG(chan_l->global_identifier), + U64_PRINTF_ARG(chan_l->timestamp_accepted), + U64_PRINTF_ARG(now - chan_l->timestamp_accepted), + U64_PRINTF_ARG(chan_l->n_accepted)); + + /* + * If it's sensible to do so, get the rate of incoming channels on this + * listener + */ + if (now > chan_l->timestamp_created && + chan_l->timestamp_created > 0 && + chan_l->n_accepted > 0) { + avg = (double)(chan_l->n_accepted) / age; + if (avg >= 1.0) { + log(severity, LD_GENERAL, + " * Channel listener " U64_FORMAT " has averaged %f incoming " + "channels per second", + U64_PRINTF_ARG(chan_l->global_identifier), avg); + } else if (avg >= 0.0) { + interval = 1.0 / avg; + log(severity, LD_GENERAL, + " * Channel listener " U64_FORMAT " has averaged %f seconds " + "between incoming channels", + U64_PRINTF_ARG(chan_l->global_identifier), interval); } } /* Dump anything the lower layer has to say */ - channel_dump_transport_statistics(chan, severity); + channel_listener_dump_transport_statistics(chan_l, severity); } /** - * Invoke transport-specific stats dump + * Invoke transport-specific stats dump for channel * * If there is a lower-layer statistics dump method, invoke it */ @@ -2824,6 +3261,21 @@ channel_dump_transport_statistics(channel_t *chan, int severity) if (chan->dumpstats) chan->dumpstats(chan, severity); } +/** + * Invoke transport-specific stats dump for channel listener + * + * If there is a lower-layer statistics dump method, invoke it + */ + +void +channel_listener_dump_transport_statistics(channel_listener_t *chan_l, + int severity) +{ + tor_assert(chan_l); + + if (chan_l->dumpstats) chan_l->dumpstats(chan_l, severity); +} + /** * Return text description of the remote endpoint * @@ -2836,11 +3288,10 @@ const char * channel_get_actual_remote_descr(channel_t *chan) { tor_assert(chan); - tor_assert(!(chan->is_listener)); - tor_assert(chan->u.cell_chan.get_remote_descr); + tor_assert(chan->get_remote_descr); /* Param 1 indicates the actual description */ - return chan->u.cell_chan.get_remote_descr(chan, 1); + return chan->get_remote_descr(chan, 1); } /** @@ -2855,11 +3306,10 @@ const char * channel_get_canonical_remote_descr(channel_t *chan) { tor_assert(chan); - tor_assert(!(chan->is_listener)); - tor_assert(chan->u.cell_chan.get_remote_descr); + tor_assert(chan->get_remote_descr); /* Param 0 indicates the canonicalized description */ - return chan->u.cell_chan.get_remote_descr(chan, 0); + return chan->get_remote_descr(chan, 0); } /** @@ -2873,11 +3323,10 @@ int channel_get_addr_if_possible(channel_t *chan, tor_addr_t *addr_out) { tor_assert(chan); - tor_assert(!(chan->is_listener)); tor_assert(addr_out); - if (chan->u.cell_chan.get_remote_addr) - return chan->u.cell_chan.get_remote_addr(chan, addr_out); + if (chan->get_remote_addr) + return chan->get_remote_addr(chan, addr_out); /* Else no support, method not implemented */ else return 0; } @@ -2895,15 +3344,14 @@ channel_has_queued_writes(channel_t *chan) int has_writes = 0; tor_assert(chan); - tor_assert(!(chan->is_listener)); - tor_assert(chan->u.cell_chan.has_queued_writes); + tor_assert(chan->has_queued_writes); - if (chan->u.cell_chan.outgoing_queue && - smartlist_len(chan->u.cell_chan.outgoing_queue) > 0) { + if (chan->outgoing_queue && + smartlist_len(chan->outgoing_queue) > 0) { has_writes = 1; } else { /* Check with the lower layer */ - has_writes = chan->u.cell_chan.has_queued_writes(chan); + has_writes = chan->has_queued_writes(chan); } return has_writes; @@ -2920,9 +3368,8 @@ int channel_is_bad_for_new_circs(channel_t *chan) { tor_assert(chan); - tor_assert(!(chan->is_listener)); - return chan->u.cell_chan.is_bad_for_new_circs; + return chan->is_bad_for_new_circs; } /** @@ -2935,9 +3382,8 @@ void channel_mark_bad_for_new_circs(channel_t *chan) { tor_assert(chan); - tor_assert(!(chan->is_listener)); - chan->u.cell_chan.is_bad_for_new_circs = 1; + chan->is_bad_for_new_circs = 1; } /** @@ -2952,9 +3398,8 @@ int channel_is_client(channel_t *chan) { tor_assert(chan); - tor_assert(!(chan->is_listener)); - return chan->u.cell_chan.is_client; + return chan->is_client; } /** @@ -2967,9 +3412,8 @@ void channel_mark_client(channel_t *chan) { tor_assert(chan); - tor_assert(!(chan->is_listener)); - chan->u.cell_chan.is_client = 1; + chan->is_client = 1; } /** @@ -2983,10 +3427,9 @@ int channel_is_canonical(channel_t *chan) { tor_assert(chan); - tor_assert(!(chan->is_listener)); - tor_assert(chan->u.cell_chan.is_canonical); + tor_assert(chan->is_canonical); - return chan->u.cell_chan.is_canonical(chan, 0); + return chan->is_canonical(chan, 0); } /** @@ -3000,10 +3443,9 @@ int channel_is_canonical_is_reliable(channel_t *chan) { tor_assert(chan); - tor_assert(!(chan->is_listener)); - tor_assert(chan->u.cell_chan.is_canonical); + tor_assert(chan->is_canonical); - return chan->u.cell_chan.is_canonical(chan, 1); + return chan->is_canonical(chan, 1); } /** @@ -3017,9 +3459,8 @@ int channel_is_incoming(channel_t *chan) { tor_assert(chan); - tor_assert(!(chan->is_listener)); - return chan->u.cell_chan.is_incoming; + return chan->is_incoming; } /** @@ -3033,9 +3474,8 @@ void channel_mark_incoming(channel_t *chan) { tor_assert(chan); - tor_assert(!(chan->is_listener)); - chan->u.cell_chan.is_incoming = 1; + chan->is_incoming = 1; } /** @@ -3052,9 +3492,8 @@ int channel_is_local(channel_t *chan) { tor_assert(chan); - tor_assert(!(chan->is_listener)); - return chan->u.cell_chan.is_local; + return chan->is_local; } /** @@ -3069,9 +3508,8 @@ void channel_mark_local(channel_t *chan) { tor_assert(chan); - tor_assert(!(chan->is_listener)); - chan->u.cell_chan.is_local = 1; + chan->is_local = 1; } /** @@ -3086,9 +3524,8 @@ int channel_is_outgoing(channel_t *chan) { tor_assert(chan); - tor_assert(!(chan->is_listener)); - return !(chan->u.cell_chan.is_incoming); + return !(chan->is_incoming); } /** @@ -3102,9 +3539,8 @@ void channel_mark_outgoing(channel_t *chan) { tor_assert(chan); - tor_assert(!(chan->is_listener)); - chan->u.cell_chan.is_incoming = 0; + chan->is_incoming = 0; } /********************* @@ -3112,7 +3548,7 @@ channel_mark_outgoing(channel_t *chan) ********************/ /** - * Update the created timestamp + * Update the created timestamp for a channel * * This updates the channel's created timestamp and should only be called * from channel_init(). @@ -3129,7 +3565,24 @@ channel_timestamp_created(channel_t *chan) } /** - * Update the last active timestamp. + * Update the created timestamp for a channel listener + * + * This updates the channel listener's created timestamp and should only be + * called from channel_init_listener(). + */ + +void +channel_listener_timestamp_created(channel_listener_t *chan_l) +{ + time_t now = time(NULL); + + tor_assert(chan_l); + + chan_l->timestamp_created = now; +} + +/** + * Update the last active timestamp for a channel * * This function updates the channel's last active timestamp; it should be * called by the lower layer whenever there is activity on the channel which @@ -3149,22 +3602,37 @@ channel_timestamp_active(channel_t *chan) chan->timestamp_active = now; } +/** + * Update the last active timestamp for a channel listener + */ + +void +channel_listener_timestamp_active(channel_listener_t *chan_l) +{ + time_t now = time(NULL); + + tor_assert(chan_l); + + chan_l->timestamp_active = now; +} + /** * Update the last accepted timestamp. * - * This function updates the channel's last accepted timestamp; it should be - * called whenever a new incoming channel is accepted on a listener. + * This function updates the channel listener's last accepted timestamp; it + * should be called whenever a new incoming channel is accepted on a + * listener. */ void -channel_timestamp_accepted(channel_t *chan) +channel_listener_timestamp_accepted(channel_listener_t *chan_l) { time_t now = time(NULL); - tor_assert(chan); - tor_assert(chan->is_listener); + tor_assert(chan_l); - chan->u.listener.timestamp_accepted = now; + chan_l->timestamp_active = now; + chan_l->timestamp_accepted = now; } /** @@ -3180,9 +3648,8 @@ channel_timestamp_client(channel_t *chan) time_t now = time(NULL); tor_assert(chan); - tor_assert(!(chan->is_listener)); - chan->u.cell_chan.timestamp_client = now; + chan->timestamp_client = now; } /** @@ -3198,11 +3665,10 @@ channel_timestamp_drained(channel_t *chan) time_t now = time(NULL); tor_assert(chan); - tor_assert(!(chan->is_listener)); chan->timestamp_active = now; - chan->u.cell_chan.timestamp_drained = now; - chan->u.cell_chan.timestamp_xmit = now; + chan->timestamp_drained = now; + chan->timestamp_xmit = now; } /** @@ -3218,10 +3684,9 @@ channel_timestamp_recv(channel_t *chan) time_t now = time(NULL); tor_assert(chan); - tor_assert(!(chan->is_listener)); chan->timestamp_active = now; - chan->u.cell_chan.timestamp_recv = now; + chan->timestamp_recv = now; } /** @@ -3236,10 +3701,9 @@ channel_timestamp_xmit(channel_t *chan) time_t now = time(NULL); tor_assert(chan); - tor_assert(!(chan->is_listener)); chan->timestamp_active = now; - chan->u.cell_chan.timestamp_xmit = now; + chan->timestamp_xmit = now; } /*************************************************************** @@ -3247,7 +3711,7 @@ channel_timestamp_xmit(channel_t *chan) **************************************************************/ /** - * Query created timestamp + * Query created timestamp for a channel */ time_t @@ -3259,7 +3723,19 @@ channel_when_created(channel_t *chan) } /** - * Query last active timestamp + * Query created timestamp for a channel listener + */ + +time_t +channel_listener_when_created(channel_listener_t *chan_l) +{ + tor_assert(chan_l); + + return chan_l->timestamp_created; +} + +/** + * Query last active timestamp for a channel */ time_t @@ -3271,16 +3747,27 @@ channel_when_last_active(channel_t *chan) } /** - * Query last accepted timestamp + * Query last active timestamp for a channel listener */ time_t -channel_when_last_accepted(channel_t *chan) +channel_listener_when_last_active(channel_listener_t *chan_l) { - tor_assert(chan); - tor_assert(chan->is_listener); + tor_assert(chan_l); + + return chan_l->timestamp_active; +} + +/** + * Query last accepted timestamp for a channel listener + */ + +time_t +channel_listener_when_last_accepted(channel_listener_t *chan_l) +{ + tor_assert(chan_l); - return chan->u.listener.timestamp_accepted; + return chan_l->timestamp_accepted; } /** @@ -3291,9 +3778,8 @@ time_t channel_when_last_client(channel_t *chan) { tor_assert(chan); - tor_assert(!(chan->is_listener)); - return chan->u.cell_chan.timestamp_client; + return chan->timestamp_client; } /** @@ -3304,9 +3790,8 @@ time_t channel_when_last_drained(channel_t *chan) { tor_assert(chan); - tor_assert(!(chan->is_listener)); - return chan->u.cell_chan.timestamp_drained; + return chan->timestamp_drained; } /** @@ -3317,9 +3802,8 @@ time_t channel_when_last_recv(channel_t *chan) { tor_assert(chan); - tor_assert(!(chan->is_listener)); - return chan->u.cell_chan.timestamp_recv; + return chan->timestamp_recv; } /** @@ -3330,9 +3814,8 @@ time_t channel_when_last_xmit(channel_t *chan) { tor_assert(chan); - tor_assert(!(chan->is_listener)); - return chan->u.cell_chan.timestamp_xmit; + return chan->timestamp_xmit; } /** @@ -3340,12 +3823,11 @@ channel_when_last_xmit(channel_t *chan) */ uint64_t -channel_count_accepted(channel_t *chan) +channel_listener_count_accepted(channel_listener_t *chan_l) { - tor_assert(chan); + tor_assert(chan_l); - if (chan->is_listener) return chan->u.listener.n_accepted; - else return 0; + return chan_l->n_accepted; } /** @@ -3357,8 +3839,7 @@ channel_count_recved(channel_t *chan) { tor_assert(chan); - if (!(chan->is_listener)) return chan->u.cell_chan.n_cells_recved; - else return 0; + return chan->n_cells_recved; } /** @@ -3370,8 +3851,7 @@ channel_count_xmitted(channel_t *chan) { tor_assert(chan); - if (!(chan->is_listener)) return chan->u.cell_chan.n_cells_xmitted; - else return 0; + return chan->n_cells_xmitted; } /** @@ -3385,11 +3865,10 @@ int channel_matches_extend_info(channel_t *chan, extend_info_t *extend_info) { tor_assert(chan); - tor_assert(!(chan->is_listener)); - tor_assert(chan->u.cell_chan.matches_extend_info); + tor_assert(chan->matches_extend_info); tor_assert(extend_info); - return chan->u.cell_chan.matches_extend_info(chan, extend_info); + return chan->matches_extend_info(chan, extend_info); } /** @@ -3404,11 +3883,10 @@ channel_matches_target_addr_for_extend(channel_t *chan, const tor_addr_t *target) { tor_assert(chan); - tor_assert(!(chan->is_listener)); - tor_assert(chan->u.cell_chan.matches_target); + tor_assert(chan->matches_target); tor_assert(target); - return chan->u.cell_chan.matches_target(chan, target); + return chan->matches_target(chan, target); } /** @@ -3425,7 +3903,6 @@ channel_set_circid_type(channel_t *chan, crypto_pk_t *identity_rcvd) crypto_pk_t *our_identity; tor_assert(chan); - tor_assert(!(chan->is_listener)); started_here = channel_is_outgoing(chan); our_identity = started_here ? @@ -3433,12 +3910,12 @@ channel_set_circid_type(channel_t *chan, crypto_pk_t *identity_rcvd) if (identity_rcvd) { if (crypto_pk_cmp_keys(our_identity, identity_rcvd) < 0) { - chan->u.cell_chan.circ_id_type = CIRC_ID_TYPE_LOWER; + chan->circ_id_type = CIRC_ID_TYPE_LOWER; } else { - chan->u.cell_chan.circ_id_type = CIRC_ID_TYPE_HIGHER; + chan->circ_id_type = CIRC_ID_TYPE_HIGHER; } } else { - chan->u.cell_chan.circ_id_type = CIRC_ID_TYPE_NEITHER; + chan->circ_id_type = CIRC_ID_TYPE_NEITHER; } } diff --git a/src/or/channel.h b/src/or/channel.h index 70ea30fcf1..c31806cbd4 100644 --- a/src/or/channel.h +++ b/src/or/channel.h @@ -12,7 +12,7 @@ #include "or.h" /* Channel handler function pointer typedefs */ -typedef void (*channel_listener_fn_ptr)(channel_t *, channel_t *); +typedef void (*channel_listener_fn_ptr)(channel_listener_t *, channel_t *); typedef void (*channel_cell_handler_fn_ptr)(channel_t *, cell_t *); typedef void (*channel_var_cell_handler_fn_ptr)(channel_t *, var_cell_t *); @@ -37,12 +37,6 @@ struct channel_s { /* Should we expect to see this channel in the channel lists? */ unsigned char registered:1; - /** Set this if this channel is created in CHANNEL_STATE_LISTEN, so - * lower-layer close methods that see the channel in CHANNEL_STATE_CLOSING - * know. - */ - unsigned int is_listener:1; - /** Why did we close? */ enum { @@ -67,171 +61,210 @@ struct channel_s { /* Optional method to dump transport-specific statistics on the channel */ void (*dumpstats)(channel_t *, int); - union { - struct { - /* Registered listen handler to call on incoming connection */ - channel_listener_fn_ptr listener; - - /* List of pending incoming connections */ - smartlist_t *incoming_list; - - /* Timestamps for listeners */ - time_t timestamp_accepted; - - /* Counters for listeners */ - uint64_t n_accepted; - } listener; - struct { - /* Registered handlers for incoming cells */ - channel_cell_handler_fn_ptr cell_handler; - channel_var_cell_handler_fn_ptr var_cell_handler; - - /* Methods implemented by the lower layer */ - - /* - * Ask the underlying transport what the remote endpoint address is, in - * a tor_addr_t. This is optional and subclasses may leave this NULL. - * If they implement it, they should write the address out to the - * provided tor_addr_t *, and return 1 if successful or 0 if no address - * available. - */ - int (*get_remote_addr)(channel_t *, tor_addr_t *); - /* - * Get a text description of the remote endpoint; canonicalized if the - * arg is 0, or the one we originally connected to/received from if it's - * 1. - */ - const char * (*get_remote_descr)(channel_t *, int); - /* Check if the lower layer has queued writes */ - int (*has_queued_writes)(channel_t *); - /* - * If the second param is zero, ask the lower layer if this is - * 'canonical', for a transport-specific definition of canonical; if - * it is 1, ask if the answer to the preceding query is safe to rely - * on. - */ - int (*is_canonical)(channel_t *, int); - /* Check if this channel matches a specified extend_info_t */ - int (*matches_extend_info)(channel_t *, extend_info_t *); - /* Check if this channel matches a target address when extending */ - int (*matches_target)(channel_t *, const tor_addr_t *); - /* Write a cell to an open channel */ - int (*write_cell)(channel_t *, cell_t *); - /* Write a packed cell to an open channel */ - int (*write_packed_cell)(channel_t *, packed_cell_t *); - /* Write a variable-length cell to an open channel */ - int (*write_var_cell)(channel_t *, var_cell_t *); - - /* - * Hash of the public RSA key for the other side's identity key, or - * zeroes if the other side hasn't shown us a valid identity key. - */ - char identity_digest[DIGEST_LEN]; - /* Nickname of the OR on the other side, or NULL if none. */ - char *nickname; - - /* - * Linked list of channels with the same identity digest, for the - * digest->channel map - */ - channel_t *next_with_same_id, *prev_with_same_id; - - /* List of incoming cells to handle */ - smartlist_t *incoming_queue; - - /* List of queued outgoing cells */ - smartlist_t *outgoing_queue; - - /* Circuit stuff for use by relay.c */ - - /* - * Double-linked ring of circuits with queued cells waiting for room to - * free up on this connection's outbuf. Every time we pull cells from - * a circuit, we advance this pointer to the next circuit in the ring. - */ - struct circuit_t *active_circuits; - /* - * Priority queue of cell_ewma_t for circuits with queued cells waiting - * for room to free up on this connection's outbuf. Kept in heap order - * according to EWMA. - * - * This is redundant with active_circuits; if we ever decide only to use - * the cell_ewma algorithm for choosing circuits, we can remove - * active_circuits. - */ - smartlist_t *active_circuit_pqueue; - /* - * The tick on which the cell_ewma_ts in active_circuit_pqueue last had - * their ewma values rescaled. - */ - unsigned active_circuit_pqueue_last_recalibrated; - - /* Circuit ID generation stuff for use by circuitbuild.c */ - - /* - * When we send CREATE cells along this connection, which half of the - * space should we use? - */ - circ_id_type_t circ_id_type:2; - /* - * Which circ_id do we try to use next on this connection? This is - * always in the range 0..1<<15-1. - */ - circid_t next_circ_id; - - /* How many circuits use this connection as p_chan or n_chan? */ - int n_circuits; - - /* - * True iff this channel shouldn't get any new circs attached to it, - * because the connection is too old, or because there's a better one. - * More generally, this flag is used to note an unhealthy connection; - * for example, if a bad connection fails we shouldn't assume that the - * router itself has a problem. - */ - unsigned int is_bad_for_new_circs:1; - - /** True iff we have decided that the other end of this connection - * is a client. Channels with this flag set should never be used - * to satisfy an EXTEND request. */ - unsigned int is_client:1; - - /** Set if the channel was initiated remotely (came from a listener) */ - unsigned int is_incoming:1; - - /** Set by lower layer if this is local; i.e., everything it communicates - * with for this channel returns true for is_local_addr(). This is used - * to decide whether to declare reachability when we receive something on - * this channel in circuitbuild.c - */ - unsigned int is_local:1; - - /** Channel timestamps for cell channels */ - time_t timestamp_client; /* Client used this, according to relay.c */ - time_t timestamp_drained; /* Output queue empty */ - time_t timestamp_recv; /* Cell received from lower layer */ - time_t timestamp_xmit; /* Cell sent to lower layer */ - - /* Timestamp for relay.c */ - time_t timestamp_last_added_nonpadding; - - /** Unique ID for measuring direct network status requests;vtunneled ones - * come over a circuit_t, which has a dirreq_id field as well, but is a - * distinct namespace. */ - uint64_t dirreq_id; - - /** Channel counters for cell channels */ - uint64_t n_cells_recved; - uint64_t n_cells_xmitted; - } cell_chan; - } u; + /* Registered handlers for incoming cells */ + channel_cell_handler_fn_ptr cell_handler; + channel_var_cell_handler_fn_ptr var_cell_handler; + + /* Methods implemented by the lower layer */ + + /* + * Ask the underlying transport what the remote endpoint address is, in + * a tor_addr_t. This is optional and subclasses may leave this NULL. + * If they implement it, they should write the address out to the + * provided tor_addr_t *, and return 1 if successful or 0 if no address + * available. + */ + int (*get_remote_addr)(channel_t *, tor_addr_t *); + /* + * Get a text description of the remote endpoint; canonicalized if the + * arg is 0, or the one we originally connected to/received from if it's + * 1. + */ + const char * (*get_remote_descr)(channel_t *, int); + /* Check if the lower layer has queued writes */ + int (*has_queued_writes)(channel_t *); + /* + * If the second param is zero, ask the lower layer if this is + * 'canonical', for a transport-specific definition of canonical; if + * it is 1, ask if the answer to the preceding query is safe to rely + * on. + */ + int (*is_canonical)(channel_t *, int); + /* Check if this channel matches a specified extend_info_t */ + int (*matches_extend_info)(channel_t *, extend_info_t *); + /* Check if this channel matches a target address when extending */ + int (*matches_target)(channel_t *, const tor_addr_t *); + /* Write a cell to an open channel */ + int (*write_cell)(channel_t *, cell_t *); + /* Write a packed cell to an open channel */ + int (*write_packed_cell)(channel_t *, packed_cell_t *); + /* Write a variable-length cell to an open channel */ + int (*write_var_cell)(channel_t *, var_cell_t *); + + /* + * Hash of the public RSA key for the other side's identity key, or + * zeroes if the other side hasn't shown us a valid identity key. + */ + char identity_digest[DIGEST_LEN]; + /* Nickname of the OR on the other side, or NULL if none. */ + char *nickname; + + /* + * Linked list of channels with the same identity digest, for the + * digest->channel map + */ + channel_t *next_with_same_id, *prev_with_same_id; + + /* List of incoming cells to handle */ + smartlist_t *incoming_queue; + + /* List of queued outgoing cells */ + smartlist_t *outgoing_queue; + + /* Circuit stuff for use by relay.c */ + + /* + * Double-linked ring of circuits with queued cells waiting for room to + * free up on this connection's outbuf. Every time we pull cells from + * a circuit, we advance this pointer to the next circuit in the ring. + */ + struct circuit_t *active_circuits; + /* + * Priority queue of cell_ewma_t for circuits with queued cells waiting + * for room to free up on this connection's outbuf. Kept in heap order + * according to EWMA. + * + * This is redundant with active_circuits; if we ever decide only to use + * the cell_ewma algorithm for choosing circuits, we can remove + * active_circuits. + */ + smartlist_t *active_circuit_pqueue; + /* + * The tick on which the cell_ewma_ts in active_circuit_pqueue last had + * their ewma values rescaled. + */ + unsigned active_circuit_pqueue_last_recalibrated; + + /* Circuit ID generation stuff for use by circuitbuild.c */ + + /* + * When we send CREATE cells along this connection, which half of the + * space should we use? + */ + circ_id_type_t circ_id_type:2; + /* + * Which circ_id do we try to use next on this connection? This is + * always in the range 0..1<<15-1. + */ + circid_t next_circ_id; + + /* How many circuits use this connection as p_chan or n_chan? */ + int n_circuits; + + /* + * True iff this channel shouldn't get any new circs attached to it, + * because the connection is too old, or because there's a better one. + * More generally, this flag is used to note an unhealthy connection; + * for example, if a bad connection fails we shouldn't assume that the + * router itself has a problem. + */ + unsigned int is_bad_for_new_circs:1; + + /** True iff we have decided that the other end of this connection + * is a client. Channels with this flag set should never be used + * to satisfy an EXTEND request. */ + unsigned int is_client:1; + + /** Set if the channel was initiated remotely (came from a listener) */ + unsigned int is_incoming:1; + + /** Set by lower layer if this is local; i.e., everything it communicates + * with for this channel returns true for is_local_addr(). This is used + * to decide whether to declare reachability when we receive something on + * this channel in circuitbuild.c + */ + unsigned int is_local:1; + + /** Channel timestamps for cell channels */ + time_t timestamp_client; /* Client used this, according to relay.c */ + time_t timestamp_drained; /* Output queue empty */ + time_t timestamp_recv; /* Cell received from lower layer */ + time_t timestamp_xmit; /* Cell sent to lower layer */ + + /* Timestamp for relay.c */ + time_t timestamp_last_added_nonpadding; + + /** Unique ID for measuring direct network status requests;vtunneled ones + * come over a circuit_t, which has a dirreq_id field as well, but is a + * distinct namespace. */ + uint64_t dirreq_id; + + /** Channel counters for cell channels */ + uint64_t n_cells_recved; + uint64_t n_cells_xmitted; +}; + +struct channel_listener_s { + /* Current channel listener state */ + channel_listener_state_t state; + + /* Globally unique ID number for a channel over the lifetime of a Tor + * process. + */ + uint64_t global_identifier; + + /* Should we expect to see this channel in the channel lists? */ + unsigned char registered:1; + + /** Why did we close? + */ + enum { + CHANNEL_LISTENER_NOT_CLOSING = 0, + CHANNEL_LISTENER_CLOSE_REQUESTED, + CHANNEL_LISTENER_CLOSE_FROM_BELOW, + CHANNEL_LISTENER_CLOSE_FOR_ERROR + } reason_for_closing; + + /* Timestamps for both cell channels and listeners */ + time_t timestamp_created; /* Channel created */ + time_t timestamp_active; /* Any activity */ + + /* Methods implemented by the lower layer */ + + /* Free a channel */ + void (*free)(channel_listener_t *); + /* Close an open channel */ + void (*close)(channel_listener_t *); + /* Describe the transport subclass for this channel */ + const char * (*describe_transport)(channel_listener_t *); + /* Optional method to dump transport-specific statistics on the channel */ + void (*dumpstats)(channel_listener_t *, int); + + /* Registered listen handler to call on incoming connection */ + channel_listener_fn_ptr listener; + + /* List of pending incoming connections */ + smartlist_t *incoming_list; + + /* Timestamps for listeners */ + time_t timestamp_accepted; + + /* Counters for listeners */ + uint64_t n_accepted; }; /* Channel state manipulations */ int channel_state_is_valid(channel_state_t state); +int channel_listener_state_is_valid(channel_listener_state_t state); + int channel_state_can_transition(channel_state_t from, channel_state_t to); +int channel_listener_state_can_transition(channel_listener_state_t from, + channel_listener_state_t to); + const char * channel_state_to_string(channel_state_t state); +const char * +channel_listener_state_to_string(channel_listener_state_t state); /* Abstract channel operations */ @@ -240,12 +273,16 @@ void channel_write_cell(channel_t *chan, cell_t *cell); void channel_write_packed_cell(channel_t *chan, packed_cell_t *cell); void channel_write_var_cell(channel_t *chan, var_cell_t *cell); +void channel_listener_mark_for_close(channel_listener_t *chan_l); + /* Channel callback registrations */ /* Listener callback */ -channel_listener_fn_ptr channel_get_listener_fn(channel_t *chan); -void channel_set_listener_fn(channel_t *chan, - channel_listener_fn_ptr listener); +channel_listener_fn_ptr +channel_listener_get_listener_fn(channel_listener_t *chan); + +void channel_listener_set_listener_fn(channel_listener_t *chan, + channel_listener_fn_ptr listener); /* Incoming cell callbacks */ channel_cell_handler_fn_ptr channel_get_cell_handler(channel_t *chan); @@ -258,16 +295,18 @@ void channel_set_cell_handlers(channel_t *chan, channel_var_cell_handler_fn_ptr var_cell_handler); -/* Clean up closed channels periodically; called from run_scheduled_events() - * in main.c +/* Clean up closed channels and channel listeners periodically; these are + * called from run_scheduled_events() in main.c. */ void channel_run_cleanup(void); +void channel_listener_run_cleanup(void); /* Close all channels and deallocate everything */ void channel_free_all(void); /* Dump some statistics in the log */ void channel_dumpstats(int severity); +void channel_listener_dumpstats(int severity); #ifdef _TOR_CHANNEL_INTERNAL @@ -277,20 +316,29 @@ void channel_dumpstats(int severity); * constructors. */ -void channel_init_for_cells(channel_t *chan); -void channel_init_listener(channel_t *chan); +void channel_init(channel_t *chan); +void channel_init_listener(channel_listener_t *chan); /* Channel registration/unregistration */ void channel_register(channel_t *chan); void channel_unregister(channel_t *chan); +/* Channel listener registration/unregistration */ +void channel_listener_register(channel_listener_t *chan_l); +void channel_listener_unregister(channel_listener_t *chan_l); + /* Close from below */ void channel_close_from_lower_layer(channel_t *chan); void channel_close_for_error(channel_t *chan); void channel_closed(channel_t *chan); +void channel_listener_close_from_lower_layer(channel_listener_t *chan_l); +void channel_listener_close_for_error(channel_listener_t *chan_l); +void channel_listener_closed(channel_listener_t *chan_l); + /* Free a channel */ void channel_free(channel_t *chan); +void channel_listener_free(channel_listener_t *chan_l); /* State/metadata setters */ @@ -306,17 +354,24 @@ void channel_set_remote_end(channel_t *chan, const char *identity_digest, const char *nickname); +void channel_listener_change_state(channel_listener_t *chan_l, + channel_listener_state_t to_state); + /* Timestamp updates */ void channel_timestamp_created(channel_t *chan); -void channel_timestamp_accepted(channel_t *chan); void channel_timestamp_active(channel_t *chan); void channel_timestamp_drained(channel_t *chan); void channel_timestamp_recv(channel_t *chan); void channel_timestamp_xmit(channel_t *chan); +void channel_listener_timestamp_created(channel_listener_t *chan_l); +void channel_listener_timestamp_active(channel_listener_t *chan_l); +void channel_listener_timestamp_accepted(channel_listener_t *chan_l); + /* Incoming channel handling */ -void channel_process_incoming(channel_t *listener); -void channel_queue_incoming(channel_t *listener, channel_t *incoming); +void channel_listener_process_incoming(channel_listener_t *listener); +void channel_listener_queue_incoming(channel_listener_t *listener, + channel_t *incoming); /* Incoming cell handling */ void channel_process_cells(channel_t *chan); @@ -401,19 +456,29 @@ int channel_matches_target_addr_for_extend(channel_t *chan, void channel_set_circid_type(channel_t *chan, crypto_pk_t *identity_rcvd); void channel_timestamp_client(channel_t *chan); +const char * channel_listener_describe_transport(channel_listener_t *chan_l); +void channel_listener_dump_statistics(channel_listener_t *chan_l, + int severity); +void channel_listener_dump_transport_statistics(channel_listener_t *chan_l, + int severity); + /* Timestamp queries */ time_t channel_when_created(channel_t *chan); -time_t channel_when_last_accepted(channel_t *chan); time_t channel_when_last_active(channel_t *chan); time_t channel_when_last_client(channel_t *chan); time_t channel_when_last_drained(channel_t *chan); time_t channel_when_last_recv(channel_t *chan); time_t channel_when_last_xmit(channel_t *chan); +time_t channel_listener_when_created(channel_listener_t *chan_l); +time_t channel_listener_when_last_active(channel_listener_t *chan_l); +time_t channel_listener_when_last_accepted(channel_listener_t *chan_l); + /* Counter queries */ -uint64_t channel_count_accepted(channel_t *chan); uint64_t channel_count_recved(channel_t *chan); uint64_t channel_count_xmitted(channel_t *chan); +uint64_t channel_listener_count_accepted(channel_listener_t *chan_l); + #endif diff --git a/src/or/channeltls.c b/src/or/channeltls.c index bb90ce5e49..93e06364de 100644 --- a/src/or/channeltls.c +++ b/src/or/channeltls.c @@ -42,7 +42,7 @@ uint64_t stats_n_authenticate_cells_processed = 0; uint64_t stats_n_authorize_cells_processed = 0; /** Active listener, if any */ -channel_tls_t *channel_tls_listener = NULL; +channel_listener_t *channel_tls_listener = NULL; /* channel_tls_t method declarations */ @@ -66,6 +66,12 @@ static int channel_tls_write_packed_cell_method(channel_t *chan, static int channel_tls_write_var_cell_method(channel_t *chan, var_cell_t *var_cell); +/* channel_listener_tls_t method declarations */ + +static void channel_tls_listener_close_method(channel_listener_t *chan_l); +static const char * +channel_tls_listener_describe_transport_method(channel_listener_t *chan_l); + /** Handle incoming cells for the handshake stuff here rather than * passing them on up. */ @@ -97,20 +103,19 @@ channel_tls_connect(const tor_addr_t *addr, uint16_t port, { channel_tls_t *tlschan = tor_malloc_zero(sizeof(*tlschan)); channel_t *chan = TLS_CHAN_TO_BASE(tlschan); - channel_init_for_cells(chan); + channel_init(chan); chan->state = CHANNEL_STATE_OPENING; chan->close = channel_tls_close_method; chan->describe_transport = channel_tls_describe_transport_method; - chan->u.cell_chan.get_remote_addr = channel_tls_get_remote_addr_method; - chan->u.cell_chan.get_remote_descr = channel_tls_get_remote_descr_method; - chan->u.cell_chan.has_queued_writes = channel_tls_has_queued_writes_method; - chan->u.cell_chan.is_canonical = channel_tls_is_canonical_method; - chan->u.cell_chan.matches_extend_info = - channel_tls_matches_extend_info_method; - chan->u.cell_chan.matches_target = channel_tls_matches_target_method; - chan->u.cell_chan.write_cell = channel_tls_write_cell_method; - chan->u.cell_chan.write_packed_cell = channel_tls_write_packed_cell_method; - chan->u.cell_chan.write_var_cell = channel_tls_write_var_cell_method; + chan->get_remote_addr = channel_tls_get_remote_addr_method; + chan->get_remote_descr = channel_tls_get_remote_descr_method; + chan->has_queued_writes = channel_tls_has_queued_writes_method; + chan->is_canonical = channel_tls_is_canonical_method; + chan->matches_extend_info = channel_tls_matches_extend_info_method; + chan->matches_target = channel_tls_matches_target_method; + chan->write_cell = channel_tls_write_cell_method; + chan->write_packed_cell = channel_tls_write_packed_cell_method; + chan->write_var_cell = channel_tls_write_var_cell_method; log_debug(LD_CHANNEL, "In channel_tls_connect() for channel %p " @@ -121,9 +126,8 @@ channel_tls_connect(const tor_addr_t *addr, uint16_t port, if (is_local_addr(addr)) channel_mark_local(chan); channel_mark_outgoing(chan); - chan->u.cell_chan.active_circuit_pqueue = smartlist_new(); - chan->u.cell_chan.active_circuit_pqueue_last_recalibrated = - cell_ewma_get_tick(); + chan->active_circuit_pqueue = smartlist_new(); + chan->active_circuit_pqueue_last_recalibrated = cell_ewma_get_tick(); /* Set up or_connection stuff */ tlschan->conn = connection_or_connect(addr, port, id_digest, tlschan); @@ -140,7 +144,7 @@ channel_tls_connect(const tor_addr_t *addr, uint16_t port, goto done; err: - smartlist_free(chan->u.cell_chan.active_circuit_pqueue); + smartlist_free(chan->active_circuit_pqueue); tor_free(tlschan); chan = NULL; @@ -154,14 +158,14 @@ channel_tls_connect(const tor_addr_t *addr, uint16_t port, /** * Return the current channel_tls_t listener * - * Returns the current listening channel for incoming TLS connections, or + * Returns the current channel listener for incoming TLS connections, or * NULL if none has been established */ -channel_t * +channel_listener_t * channel_tls_get_listener(void) { - return TLS_CHAN_TO_BASE(channel_tls_listener); + return channel_tls_listener; } /** @@ -171,30 +175,29 @@ channel_tls_get_listener(void) * and return that. */ -channel_t * +channel_listener_t * channel_tls_start_listener(void) { - channel_tls_t *listener; - channel_t *lchan; + channel_listener_t *listener; if (!channel_tls_listener) { listener = tor_malloc_zero(sizeof(*listener)); - lchan = TLS_CHAN_TO_BASE(listener); - channel_init_listener(lchan); - lchan->state = CHANNEL_STATE_LISTENING; - lchan->close = channel_tls_close_method; - lchan->describe_transport = channel_tls_describe_transport_method; + channel_init_listener(listener); + listener->state = CHANNEL_LISTENER_STATE_LISTENING; + listener->close = channel_tls_listener_close_method; + listener->describe_transport = + channel_tls_listener_describe_transport_method; channel_tls_listener = listener; log_debug(LD_CHANNEL, - "Starting TLS listener channel %p with global id " U64_FORMAT, - lchan, U64_PRINTF_ARG(lchan->global_identifier)); + "Starting TLS channel listener %p with global id " U64_FORMAT, + listener, U64_PRINTF_ARG(listener->global_identifier)); - channel_register(lchan); - } else lchan = TLS_CHAN_TO_BASE(channel_tls_listener); + channel_listener_register(listener); + } else listener = channel_tls_listener; - return lchan; + return listener; } /** @@ -207,16 +210,13 @@ channel_tls_start_listener(void) void channel_tls_free_all(void) { - channel_t *base = NULL; - log_debug(LD_CHANNEL, "Shutting down TLS channels..."); if (channel_tls_listener) { - base = TLS_CHAN_TO_BASE(channel_tls_listener); - channel_unregister(base); - channel_mark_for_close(base); - channel_free(base); + channel_listener_unregister(channel_tls_listener); + channel_listener_mark_for_close(channel_tls_listener); + channel_listener_free(channel_tls_listener); channel_tls_listener = NULL; } @@ -237,19 +237,18 @@ channel_tls_handle_incoming(or_connection_t *orconn) tor_assert(orconn); tor_assert(!(orconn->chan)); - channel_init_for_cells(chan); + channel_init(chan); chan->state = CHANNEL_STATE_OPENING; chan->close = channel_tls_close_method; chan->describe_transport = channel_tls_describe_transport_method; - chan->u.cell_chan.get_remote_descr = channel_tls_get_remote_descr_method; - chan->u.cell_chan.has_queued_writes = channel_tls_has_queued_writes_method; - chan->u.cell_chan.is_canonical = channel_tls_is_canonical_method; - chan->u.cell_chan.matches_extend_info = - channel_tls_matches_extend_info_method; - chan->u.cell_chan.matches_target = channel_tls_matches_target_method; - chan->u.cell_chan.write_cell = channel_tls_write_cell_method; - chan->u.cell_chan.write_packed_cell = channel_tls_write_packed_cell_method; - chan->u.cell_chan.write_var_cell = channel_tls_write_var_cell_method; + chan->get_remote_descr = channel_tls_get_remote_descr_method; + chan->has_queued_writes = channel_tls_has_queued_writes_method; + chan->is_canonical = channel_tls_is_canonical_method; + chan->matches_extend_info = channel_tls_matches_extend_info_method; + chan->matches_target = channel_tls_matches_target_method; + chan->write_cell = channel_tls_write_cell_method; + chan->write_packed_cell = channel_tls_write_packed_cell_method; + chan->write_var_cell = channel_tls_write_var_cell_method; /* Link the channel and orconn to each other */ tlschan->conn = orconn; @@ -258,9 +257,8 @@ channel_tls_handle_incoming(or_connection_t *orconn) if (is_local_addr(&(TO_CONN(orconn)->addr))) channel_mark_local(chan); channel_mark_incoming(chan); - chan->u.cell_chan.active_circuit_pqueue = smartlist_new(); - chan->u.cell_chan.active_circuit_pqueue_last_recalibrated = - cell_ewma_get_tick(); + chan->active_circuit_pqueue = smartlist_new(); + chan->active_circuit_pqueue_last_recalibrated = cell_ewma_get_tick(); /* If we got one, we should register it */ if (chan) channel_register(chan); @@ -285,43 +283,13 @@ channel_tls_close_method(channel_t *chan) tor_assert(tlschan); - if (chan->is_listener) { - /* - * Listeners we just go ahead and change state through to CLOSED, but - * make sure to check if they're channel_tls_listener to NULL it out. - */ - if (chan == TLS_CHAN_TO_BASE(channel_tls_listener)) - channel_tls_listener = NULL; - - if (!(chan->state == CHANNEL_STATE_CLOSING || - chan->state == CHANNEL_STATE_CLOSED || - chan->state == CHANNEL_STATE_ERROR)) { - channel_change_state(chan, CHANNEL_STATE_CLOSING); - } - - if (chan->u.listener.incoming_list) { - SMARTLIST_FOREACH_BEGIN(chan->u.listener.incoming_list, - channel_t *, ichan) { - channel_mark_for_close(ichan); - } SMARTLIST_FOREACH_END(ichan); - - smartlist_free(chan->u.listener.incoming_list); - chan->u.listener.incoming_list = NULL; - } - - if (!(chan->state == CHANNEL_STATE_CLOSED || - chan->state == CHANNEL_STATE_ERROR)) { - channel_change_state(chan, CHANNEL_STATE_CLOSED); - } - } else { - if (tlschan->conn) connection_or_close_normally(tlschan->conn, 1); - else { - /* Weird - we'll have to change the state ourselves, I guess */ - log_info(LD_CHANNEL, - "Tried to close channel_tls_t %p with NULL conn", - tlschan); - channel_change_state(chan, CHANNEL_STATE_ERROR); - } + if (tlschan->conn) connection_or_close_normally(tlschan->conn, 1); + else { + /* Weird - we'll have to change the state ourselves, I guess */ + log_info(LD_CHANNEL, + "Tried to close channel_tls_t %p with NULL conn", + tlschan); + channel_change_state(chan, CHANNEL_STATE_ERROR); } } @@ -342,23 +310,19 @@ channel_tls_describe_transport_method(channel_t *chan) tor_assert(chan); - if (chan->is_listener) { - rv = "TLS channel (listening)"; - } else { - tlschan = BASE_CHAN_TO_TLS(chan); + tlschan = BASE_CHAN_TO_TLS(chan); - if (tlschan->conn) { - id = TO_CONN(tlschan->conn)->global_identifier; + if (tlschan->conn) { + id = TO_CONN(tlschan->conn)->global_identifier; - if (buf) tor_free(buf); - tor_asprintf(&buf, - "TLS channel (connection " U64_FORMAT ")", - U64_PRINTF_ARG(id)); + if (buf) tor_free(buf); + tor_asprintf(&buf, + "TLS channel (connection " U64_FORMAT ")", + U64_PRINTF_ARG(id)); - rv = buf; - } else { - rv = "TLS channel (no connection)"; - } + rv = buf; + } else { + rv = "TLS channel (no connection)"; } return rv; @@ -603,6 +567,65 @@ channel_tls_write_var_cell_method(channel_t *chan, var_cell_t *var_cell) return 1; } +/************************************************* + * Method implementations for channel_listener_t * + ************************************************/ + +/** + * Close a channel_listener_t + * + * This implements the close method for channel_listener_t + */ + +static void +channel_tls_listener_close_method(channel_listener_t *chan_l) +{ + tor_assert(chan_l); + + /* + * Listeners we just go ahead and change state through to CLOSED, but + * make sure to check if they're channel_tls_listener to NULL it out. + */ + if (chan_l == channel_tls_listener) + channel_tls_listener = NULL; + + if (!(chan_l->state == CHANNEL_LISTENER_STATE_CLOSING || + chan_l->state == CHANNEL_LISTENER_STATE_CLOSED || + chan_l->state == CHANNEL_LISTENER_STATE_ERROR)) { + channel_listener_change_state(chan_l, CHANNEL_LISTENER_STATE_CLOSING); + } + + if (chan_l->incoming_list) { + SMARTLIST_FOREACH_BEGIN(chan_l->incoming_list, + channel_t *, ichan) { + channel_mark_for_close(ichan); + } SMARTLIST_FOREACH_END(ichan); + + smartlist_free(chan_l->incoming_list); + chan_l->incoming_list = NULL; + } + + if (!(chan_l->state == CHANNEL_LISTENER_STATE_CLOSED || + chan_l->state == CHANNEL_LISTENER_STATE_ERROR)) { + channel_listener_change_state(chan_l, CHANNEL_LISTENER_STATE_CLOSED); + } +} + +/** + * Describe the transport for a channel_listener_t + * + * This returns the string "TLS channel (listening)" to the upper + * layer. + */ + +static const char * +channel_tls_listener_describe_transport_method(channel_listener_t *chan_l) +{ + tor_assert(chan_l); + + return "TLS channel (listening)"; +} + /******************************************************* * Functions for handling events on an or_connection_t * ******************************************************/ @@ -782,8 +805,6 @@ channel_tls_handle_cell(cell_t *cell, or_connection_t *conn) return; } - tor_assert(!(TLS_CHAN_TO_BASE(chan)->is_listener)); - handshaking = (TO_CONN(conn)->state != OR_CONN_STATE_OPEN); if (conn->_base.marked_for_close) @@ -892,8 +913,6 @@ channel_tls_handle_var_cell(var_cell_t *var_cell, or_connection_t *conn) return; } - tor_assert(!(TLS_CHAN_TO_BASE(chan)->is_listener)); - handshaking = (TO_CONN(conn)->state != OR_CONN_STATE_OPEN); if (TO_CONN(conn)->marked_for_close) @@ -1049,7 +1068,6 @@ enter_v3_handshake_with_cell(var_cell_t *cell, channel_tls_t *chan) tor_assert(cell); tor_assert(chan); - tor_assert(!(TLS_CHAN_TO_BASE(chan)->is_listener)); tor_assert(chan->conn); started_here = connection_or_nonopen_was_started_here(chan->conn); @@ -1091,7 +1109,6 @@ channel_tls_process_versions_cell(var_cell_t *cell, channel_tls_t *chan) tor_assert(cell); tor_assert(chan); - tor_assert(!(TLS_CHAN_TO_BASE(chan)->is_listener)); tor_assert(chan->conn); started_here = connection_or_nonopen_was_started_here(chan->conn); @@ -1247,7 +1264,6 @@ channel_tls_process_netinfo_cell(cell_t *cell, channel_tls_t *chan) tor_assert(cell); tor_assert(chan); - tor_assert(!(TLS_CHAN_TO_BASE(chan)->is_listener)); tor_assert(chan->conn); if (chan->conn->link_proto < 2) { @@ -1386,7 +1402,7 @@ channel_tls_process_netinfo_cell(cell_t *cell, channel_tls_t *chan) safe_str_client(chan->conn->_base.address), chan->conn->_base.port, (int)(chan->conn->link_proto), - hex_str(TLS_CHAN_TO_BASE(chan)->u.cell_chan.identity_digest, + hex_str(TLS_CHAN_TO_BASE(chan)->identity_digest, DIGEST_LEN), tor_addr_is_null(&my_apparent_addr) ? "" : fmt_and_decorate_addr(&my_apparent_addr)); @@ -1422,7 +1438,6 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan) tor_assert(cell); tor_assert(chan); - tor_assert(!(TLS_CHAN_TO_BASE(chan)->is_listener)); tor_assert(chan->conn); #define ERR(s) \ @@ -1515,7 +1530,7 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan) * _trying_ to connect to an authority, not necessarily if we _did_ connect * to one. */ if (router_digest_is_trusted_dir( - TLS_CHAN_TO_BASE(chan)->u.cell_chan.identity_digest)) + TLS_CHAN_TO_BASE(chan)->identity_digest)) severity = LOG_WARN; else severity = LOG_PROTOCOL_WARN; @@ -1616,7 +1631,6 @@ channel_tls_process_auth_challenge_cell(var_cell_t *cell, channel_tls_t *chan) tor_assert(cell); tor_assert(chan); - tor_assert(!(TLS_CHAN_TO_BASE(chan)->is_listener)); tor_assert(chan->conn); #define ERR(s) \ @@ -1714,7 +1728,6 @@ channel_tls_process_authenticate_cell(var_cell_t *cell, channel_tls_t *chan) tor_assert(cell); tor_assert(chan); - tor_assert(!(TLS_CHAN_TO_BASE(chan)->is_listener)); tor_assert(chan->conn); #define ERR(s) \ diff --git a/src/or/channeltls.h b/src/or/channeltls.h index 3b7d6a7a1f..b38e12adcc 100644 --- a/src/or/channeltls.h +++ b/src/or/channeltls.h @@ -28,8 +28,8 @@ struct channel_tls_s { channel_t * channel_tls_connect(const tor_addr_t *addr, uint16_t port, const char *id_digest); -channel_t * channel_tls_get_listener(void); -channel_t * channel_tls_start_listener(void); +channel_listener_t * channel_tls_get_listener(void); +channel_listener_t * channel_tls_start_listener(void); channel_t * channel_tls_handle_incoming(or_connection_t *orconn); /* Things for connection_or.c to call back into */ diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c index 5ef67bd9df..749985f904 100644 --- a/src/or/circuitbuild.c +++ b/src/or/circuitbuild.c @@ -1711,23 +1711,22 @@ get_unique_circ_id_by_chan(channel_t *chan) circid_t high_bit; tor_assert(chan); - tor_assert(!(chan->is_listener)); - if (chan->u.cell_chan.circ_id_type == CIRC_ID_TYPE_NEITHER) { + if (chan->circ_id_type == CIRC_ID_TYPE_NEITHER) { log_warn(LD_BUG, "Trying to pick a circuit ID for a connection from " "a client with no identity."); return 0; } high_bit = - (chan->u.cell_chan.circ_id_type == CIRC_ID_TYPE_HIGHER) ? 1<<15 : 0; + (chan->circ_id_type == CIRC_ID_TYPE_HIGHER) ? 1<<15 : 0; do { /* Sequentially iterate over test_circ_id=1...1<<15-1 until we find a * circID such that (high_bit|test_circ_id) is not already used. */ - test_circ_id = chan->u.cell_chan.next_circ_id++; + test_circ_id = chan->next_circ_id++; if (test_circ_id == 0 || test_circ_id >= 1<<15) { test_circ_id = 1; - chan->u.cell_chan.next_circ_id = 2; + chan->next_circ_id = 2; } if (++attempts > 1<<15) { /* Make sure we don't loop forever if all circ_id's are used. This @@ -2039,11 +2038,9 @@ circuit_n_chan_done(channel_t *chan, int status) int err_reason = 0; tor_assert(chan); - tor_assert(!(chan->is_listener)); log_debug(LD_CIRC,"chan to %s/%s, status=%d", - chan->u.cell_chan.nickname ? - chan->u.cell_chan.nickname : "NULL", + chan->nickname ? chan->nickname : "NULL", channel_get_canonical_remote_descr(chan), status); pending_circs = smartlist_new(); @@ -2064,7 +2061,7 @@ circuit_n_chan_done(channel_t *chan, int status) continue; } else { /* We expected a key. See if it's the right one. */ - if (tor_memneq(chan->u.cell_chan.identity_digest, + if (tor_memneq(chan->identity_digest, circ->n_hop->identity_digest, DIGEST_LEN)) continue; } @@ -2247,8 +2244,7 @@ circuit_send_next_onion_skin(origin_circuit_t *circ) else control_event_bootstrap(BOOTSTRAP_STATUS_CIRCUIT_CREATE, 0); - tor_assert(!(circ->_base.n_chan->is_listener)); - node = node_get_by_id(circ->_base.n_chan->u.cell_chan.identity_digest); + node = node_get_by_id(circ->_base.n_chan->identity_digest); fast = should_use_create_fast_for_circuit(circ); if (!fast) { /* We are an OR and we know the right onion key: we should @@ -2487,10 +2483,8 @@ circuit_extend(cell_t *cell, circuit_t *circ) /* Next, check if we're being asked to connect to the hop that the * extend cell came from. There isn't any reason for that, and it can * assist circular-path attacks. */ - tor_assert(!(TO_OR_CIRCUIT(circ)->p_chan->is_listener)); if (tor_memeq(id_digest, - TO_OR_CIRCUIT(circ)->p_chan-> - u.cell_chan.identity_digest, + TO_OR_CIRCUIT(circ)->p_chan->identity_digest, DIGEST_LEN)) { log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Client asked me to extend back to the previous hop."); @@ -2733,9 +2727,8 @@ pathbias_count_first_hop(origin_circuit_t *circ) if (!circ->has_opened) { entry_guard_t *guard; - tor_assert(!(circ->_base.n_chan->is_listener)); - guard = entry_guard_get_by_id_digest( - circ->_base.n_chan->u.cell_chan.identity_digest); + guard = + entry_guard_get_by_id_digest(circ->_base.n_chan->identity_digest); if (guard) { if (circ->path_state == PATH_STATE_NEW_CIRC) { circ->path_state = PATH_STATE_DID_FIRST_HOP; @@ -2840,10 +2833,8 @@ pathbias_count_success(origin_circuit_t *circ) /* Don't count cannibalized/reused circs for path bias */ if (!circ->has_opened) { - tor_assert(!(circ->_base.n_chan->is_listener)); guard = - entry_guard_get_by_id_digest(circ->_base.n_chan-> - u.cell_chan.identity_digest); + entry_guard_get_by_id_digest(circ->_base.n_chan->identity_digest); if (guard) { if (circ->path_state == PATH_STATE_DID_FIRST_HOP) { diff --git a/src/or/circuitlist.c b/src/or/circuitlist.c index 68cd19e152..cf6020de06 100644 --- a/src/or/circuitlist.c +++ b/src/or/circuitlist.c @@ -102,8 +102,6 @@ circuit_set_circid_chan_helper(circuit_t *circ, int direction, circid_t old_id, *circid_ptr; int was_active, make_active; - if (chan) tor_assert(!(chan->is_listener)); - if (direction == CELL_DIRECTION_OUT) { chan_ptr = &circ->n_chan; circid_ptr = &circ->n_circ_id; @@ -131,13 +129,12 @@ circuit_set_circid_chan_helper(circuit_t *circ, int direction, } if (old_chan) { /* we may need to remove it from the conn-circid map */ - tor_assert(!(old_chan->is_listener)); search.circ_id = old_id; search.chan = old_chan; found = HT_REMOVE(chan_circid_map, &chan_circid_map, &search); if (found) { tor_free(found); - --old_chan->u.cell_chan.n_circuits; + --old_chan->n_circuits; } if (was_active && old_chan != chan) make_circuit_inactive_on_chan(circ, old_chan); @@ -167,7 +164,7 @@ circuit_set_circid_chan_helper(circuit_t *circ, int direction, if (make_active && old_chan != chan) make_circuit_active_on_chan(circ,chan); - ++chan->u.cell_chan.n_circuits; + ++chan->n_circuits; } /** Set the p_conn field of a circuit circ, along @@ -242,7 +239,6 @@ circuit_get_all_pending_on_channel(smartlist_t *out, channel_t *chan) { tor_assert(out); tor_assert(chan); - tor_assert(!(chan->is_listener)); if (!circuits_pending_chans) return; @@ -259,8 +255,8 @@ circuit_get_all_pending_on_channel(smartlist_t *out, channel_t *chan) continue; } else { /* We expected a key. See if it's the right one. */ - if (tor_memneq(chan->u.cell_chan.identity_digest, - circ->n_hop->identity_digest, DIGEST_LEN)) + if (tor_memneq(chan->identity_digest, + circ->n_hop->identity_digest, DIGEST_LEN)) continue; } smartlist_add(out, circ); @@ -276,14 +272,12 @@ circuit_count_pending_on_channel(channel_t *chan) smartlist_t *sl = smartlist_new(); tor_assert(chan); - tor_assert(!(chan->is_listener)); circuit_get_all_pending_on_channel(sl, chan); cnt = smartlist_len(sl); smartlist_free(sl); log_debug(LD_CIRC,"or_conn to %s at %s, %d pending circs", - chan->u.cell_chan.nickname ? - chan->u.cell_chan.nickname : "NULL", + chan->nickname ? chan->nickname : "NULL", channel_get_canonical_remote_descr(chan), cnt); return cnt; @@ -839,7 +833,6 @@ circuit_dump_by_chan(channel_t *chan, int severity) circuit_t *circ; tor_assert(chan); - tor_assert(!(chan->is_listener)); for (circ = global_circuitlist; circ; circ = circ->next) { circid_t n_circ_id = circ->n_circ_id, p_circ_id = 0; @@ -865,7 +858,7 @@ circuit_dump_by_chan(channel_t *chan, int severity) if (!circ->n_chan && circ->n_hop && channel_matches_extend_info(chan, circ->n_hop) && - tor_memeq(chan->u.cell_chan.identity_digest, + tor_memeq(chan->identity_digest, circ->n_hop->identity_digest, DIGEST_LEN)) { circuit_dump_chan_details(severity, circ, chan, (circ->state == CIRCUIT_STATE_OPEN && diff --git a/src/or/circuituse.c b/src/or/circuituse.c index e4e1b8ab63..be79b30bc9 100644 --- a/src/or/circuituse.c +++ b/src/or/circuituse.c @@ -1185,9 +1185,8 @@ circuit_build_failed(origin_circuit_t *circ) int already_marked = 0; if (circ->_base.n_chan) { n_chan = circ->_base.n_chan; - tor_assert(!(n_chan->is_listener)); - if (n_chan->u.cell_chan.is_bad_for_new_circs) { + if (n_chan->is_bad_for_new_circs) { /* We only want to blame this router when a fresh healthy * connection fails. So don't mark this router as newly failed, * since maybe this was just an old circuit attempt that's @@ -1201,7 +1200,7 @@ circuit_build_failed(origin_circuit_t *circ) "Our circuit failed to get a response from the first hop " "(%s). I'm going to try to rotate to a better connection.", channel_get_canonical_remote_descr(n_chan)); - n_chan->u.cell_chan.is_bad_for_new_circs = 1; + n_chan->is_bad_for_new_circs = 1; } else { log_info(LD_OR, "Our circuit died before the first hop with no connection"); diff --git a/src/or/command.c b/src/or/command.c index 2fb70b5887..e175e23ca9 100644 --- a/src/or/command.c +++ b/src/or/command.c @@ -44,7 +44,7 @@ uint64_t stats_n_relay_cells_processed = 0; uint64_t stats_n_destroy_cells_processed = 0; /* Handle an incoming channel */ -static void command_handle_incoming_channel(channel_t *listener, +static void command_handle_incoming_channel(channel_listener_t *listener, channel_t *chan); /* These are the main functions for processing cells */ @@ -190,7 +190,6 @@ command_process_create_cell(cell_t *cell, channel_t *chan) tor_assert(cell); tor_assert(chan); - tor_assert(!(chan->is_listener)); log_debug(LD_OR, "Got a CREATE cell for circ_id %d on channel " U64_FORMAT @@ -223,9 +222,9 @@ command_process_create_cell(cell_t *cell, channel_t *chan) * circ. */ id_is_high = cell->circ_id & (1<<15); if ((id_is_high && - chan->u.cell_chan.circ_id_type == CIRC_ID_TYPE_HIGHER) || + chan->circ_id_type == CIRC_ID_TYPE_HIGHER) || (!id_is_high && - chan->u.cell_chan.circ_id_type == CIRC_ID_TYPE_LOWER)) { + chan->circ_id_type == CIRC_ID_TYPE_LOWER)) { log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Received create cell with unexpected circ_id %d. Closing.", cell->circ_id); @@ -235,7 +234,7 @@ command_process_create_cell(cell_t *cell, channel_t *chan) } if (circuit_id_in_use_on_channel(cell->circ_id, chan)) { - const node_t *node = node_get_by_id(chan->u.cell_chan.identity_digest); + const node_t *node = node_get_by_id(chan->identity_digest); log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Received CREATE cell (circID %d) for known circ. " "Dropping (age %d).", @@ -473,7 +472,7 @@ command_process_destroy_cell(cell_t *cell, channel_t *chan) */ static void -command_handle_incoming_channel(channel_t *listener, channel_t *chan) +command_handle_incoming_channel(channel_listener_t *listener, channel_t *chan) { tor_assert(listener); tor_assert(chan); @@ -500,11 +499,11 @@ command_setup_channel(channel_t *chan) */ void -command_setup_listener(channel_t *listener) +command_setup_listener(channel_listener_t *listener) { tor_assert(listener); - tor_assert(listener->state == CHANNEL_STATE_LISTENING); + tor_assert(listener->state == CHANNEL_LISTENER_STATE_LISTENING); - channel_set_listener_fn(listener, command_handle_incoming_channel); + channel_listener_set_listener_fn(listener, command_handle_incoming_channel); } diff --git a/src/or/command.h b/src/or/command.h index eddce8741b..f9a0ef20b7 100644 --- a/src/or/command.h +++ b/src/or/command.h @@ -17,7 +17,7 @@ void command_process_cell(channel_t *chan, cell_t *cell); void command_process_var_cell(channel_t *chan, var_cell_t *cell); void command_setup_channel(channel_t *chan); -void command_setup_listener(channel_t *chan); +void command_setup_listener(channel_listener_t *chan_l); extern uint64_t stats_n_padding_cells_processed; extern uint64_t stats_n_create_cells_processed; diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c index 834f9707ce..45f3a06f38 100644 --- a/src/or/connection_edge.c +++ b/src/or/connection_edge.c @@ -3070,11 +3070,10 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ) return 0; } if (or_circ && or_circ->p_chan) { - tor_assert(!(or_circ->p_chan->is_listener)); if (!options->AllowSingleHopExits && (or_circ->is_first_hop || (!connection_or_digest_is_known_relay( - or_circ->p_chan->u.cell_chan.identity_digest) && + or_circ->p_chan->identity_digest) && should_refuse_unknown_exits(options)))) { /* Don't let clients use us as a single-hop proxy, unless the user * has explicitly allowed that in the config. It attracts attackers diff --git a/src/or/connection_or.c b/src/or/connection_or.c index a3df7759e0..bf69711691 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -336,8 +336,7 @@ connection_or_get_num_circuits(or_connection_t *conn) tor_assert(conn); if (conn->chan) { - tor_assert(!(TLS_CHAN_TO_BASE(conn->chan)->is_listener)); - return TLS_CHAN_TO_BASE(conn->chan)->u.cell_chan.n_circuits; + return TLS_CHAN_TO_BASE(conn->chan)->n_circuits; } else return 0; } @@ -1001,8 +1000,6 @@ connection_or_notify_error(or_connection_t *conn, /* Tell the controlling channel if we have one */ if (conn->chan) { chan = TLS_CHAN_TO_BASE(conn->chan); - /* This shouldn't ever happen in the listening state */ - tor_assert(chan->state != CHANNEL_STATE_LISTENING); /* Don't transition if we're already in closing, closed or error */ if (!(chan->state == CHANNEL_STATE_CLOSING || chan->state == CHANNEL_STATE_CLOSED || @@ -1148,8 +1145,6 @@ connection_or_close_normally(or_connection_t *orconn, int flush) else connection_mark_for_close(TO_CONN(orconn)); if (orconn->chan) { chan = TLS_CHAN_TO_BASE(orconn->chan); - /* This shouldn't ever happen in the listening state */ - tor_assert(chan->state != CHANNEL_STATE_LISTENING); /* Don't transition if we're already in closing, closed or error */ if (!(chan->state == CHANNEL_STATE_CLOSING || chan->state == CHANNEL_STATE_CLOSED || @@ -1173,8 +1168,6 @@ connection_or_close_for_error(or_connection_t *orconn, int flush) else connection_mark_for_close(TO_CONN(orconn)); if (orconn->chan) { chan = TLS_CHAN_TO_BASE(orconn->chan); - /* This shouldn't ever happen in the listening state */ - tor_assert(chan->state != CHANNEL_STATE_LISTENING); /* Don't transition if we're already in closing, closed or error */ if (!(chan->state == CHANNEL_STATE_CLOSING || chan->state == CHANNEL_STATE_CLOSED || @@ -1195,7 +1188,8 @@ connection_or_close_for_error(or_connection_t *orconn, int flush) int connection_tls_start_handshake(or_connection_t *conn, int receiving) { - channel_t *chan_listener, *chan; + channel_listener_t *chan_listener; + channel_t *chan; /* Incoming connections will need a new channel passed to the * channel_tls_listener */ @@ -1208,7 +1202,7 @@ connection_tls_start_handshake(or_connection_t *conn, int receiving) command_setup_listener(chan_listener); } chan = channel_tls_handle_incoming(conn); - channel_queue_incoming(chan_listener, chan); + channel_listener_queue_incoming(chan_listener, chan); } connection_or_change_state(conn, OR_CONN_STATE_TLS_HANDSHAKING); diff --git a/src/or/main.c b/src/or/main.c index 2a3e0e140e..e0c89a919b 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -1539,6 +1539,7 @@ run_scheduled_events(time_t now) /** 8c. Do channel cleanup just like for connections */ channel_run_cleanup(); + channel_listener_run_cleanup(); /** 9. and if we're a server, check whether our DNS is telling stories to * us. */ @@ -2172,6 +2173,7 @@ dumpstats(int severity) } SMARTLIST_FOREACH_END(conn); channel_dumpstats(severity); + channel_listener_dumpstats(severity); log(severity, LD_NET, "Cells processed: "U64_FORMAT" padding\n" diff --git a/src/or/or.h b/src/or/or.h index 4d2ab21d7d..5987eefd88 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -883,6 +883,10 @@ typedef uint16_t streamid_t; typedef struct channel_s channel_t; +/* channel_listener_t typedef; struct channel_listener_s is in channel.h */ + +typedef struct channel_listener_s channel_listener_t; + /* channel states for channel_t */ typedef enum { @@ -892,20 +896,9 @@ typedef enum { * Permitted transitions from: * - CHANNEL_STATE_CLOSING * Permitted transitions to: - * - CHANNEL_STATE_LISTENING * - CHANNEL_STATE_OPENING */ CHANNEL_STATE_CLOSED = 0, - /* - * Listening state - channel is listening for incoming connections - * - * Permitted transitions from: - * - CHANNEL_STATE_CLOSED - * Permitted transitions to: - * - CHANNEL_STATE_CLOSING - * - CHANNEL_STATE_ERROR - */ - CHANNEL_STATE_LISTENING, /* * Opening state - channel is trying to connect * @@ -957,7 +950,6 @@ typedef enum { * * Permitted transitions from: * - CHANNEL_STATE_CLOSING - * - CHANNEL_STATE_LISTENING * - CHANNEL_STATE_MAINT * - CHANNEL_STATE_OPENING * - CHANNEL_STATE_OPEN @@ -971,6 +963,55 @@ typedef enum { CHANNEL_STATE_LAST } channel_state_t; +/* channel listener states for channel_listener_t */ + +typedef enum { + /* + * Closed state - channel listener is inactive + * + * Permitted transitions from: + * - CHANNEL_LISTENER_STATE_CLOSING + * Permitted transitions to: + * - CHANNEL_LISTENER_STATE_LISTENING + */ + CHANNEL_LISTENER_STATE_CLOSED = 0, + /* + * Listening state - channel listener is listening for incoming + * connections + * + * Permitted transitions from: + * - CHANNEL_LISTENER_STATE_CLOSED + * Permitted transitions to: + * - CHANNEL_LISTENER_STATE_CLOSING + * - CHANNEL_LISTENER_STATE_ERROR + */ + CHANNEL_LISTENER_STATE_LISTENING, + /* + * Closing state - channel listener is shutting down + * + * Permitted transitions from: + * - CHANNEL_LISTENER_STATE_LISTENING + * Permitted transitions to: + * - CHANNEL_LISTENER_STATE_CLOSED, + * - CHANNEL_LISTENER_STATE_ERROR + */ + CHANNEL_LISTENER_STATE_CLOSING, + /* + * Error state - channel listener has experienced a permanent error + * + * Permitted transitions from: + * - CHANNEL_STATE_CLOSING + * - CHANNEL_STATE_LISTENING + * Permitted transitions to: + * - None + */ + CHANNEL_LISTENER_STATE_ERROR, + /* + * Placeholder for maximum state value + */ + CHANNEL_LISTENER_STATE_LAST +} channel_listener_state_t; + /* TLS channel stuff */ typedef struct channel_tls_s channel_tls_t; diff --git a/src/or/relay.c b/src/or/relay.c index 3850562917..60f696cd47 100644 --- a/src/or/relay.c +++ b/src/or/relay.c @@ -1096,8 +1096,7 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ, * and linked. */ static uint64_t next_id = 0; circ->dirreq_id = ++next_id; - tor_assert(!(TO_OR_CIRCUIT(circ)->p_chan->is_listener)); - TO_OR_CIRCUIT(circ)->p_chan->u.cell_chan.dirreq_id = circ->dirreq_id; + TO_OR_CIRCUIT(circ)->p_chan->dirreq_id = circ->dirreq_id; } return connection_exit_begin_conn(cell, circ); @@ -2179,23 +2178,22 @@ scale_active_circuits(channel_t *chan, unsigned cur_tick) double factor; tor_assert(chan); - tor_assert(!(chan->is_listener)); factor = get_scale_factor( - chan->u.cell_chan.active_circuit_pqueue_last_recalibrated, + chan->active_circuit_pqueue_last_recalibrated, cur_tick); /** Ordinarily it isn't okay to change the value of an element in a heap, * but it's okay here, since we are preserving the order. */ SMARTLIST_FOREACH_BEGIN( - chan->u.cell_chan.active_circuit_pqueue, + chan->active_circuit_pqueue, cell_ewma_t *, e) { tor_assert(e->last_adjusted_tick == - chan->u.cell_chan.active_circuit_pqueue_last_recalibrated); + chan->active_circuit_pqueue_last_recalibrated); e->cell_count *= factor; e->last_adjusted_tick = cur_tick; } SMARTLIST_FOREACH_END(e); - chan->u.cell_chan.active_circuit_pqueue_last_recalibrated = cur_tick; + chan->active_circuit_pqueue_last_recalibrated = cur_tick; } /** Rescale ewma to the same scale as chan, and add it to @@ -2204,15 +2202,14 @@ static void add_cell_ewma_to_chan(channel_t *chan, cell_ewma_t *ewma) { tor_assert(chan); - tor_assert(!(chan->is_listener)); tor_assert(ewma); tor_assert(ewma->heap_index == -1); scale_single_cell_ewma( ewma, - chan->u.cell_chan.active_circuit_pqueue_last_recalibrated); + chan->active_circuit_pqueue_last_recalibrated); - smartlist_pqueue_add(chan->u.cell_chan.active_circuit_pqueue, + smartlist_pqueue_add(chan->active_circuit_pqueue, compare_cell_ewma_counts, STRUCT_OFFSET(cell_ewma_t, heap_index), ewma); @@ -2223,11 +2220,10 @@ static void remove_cell_ewma_from_chan(channel_t *chan, cell_ewma_t *ewma) { tor_assert(chan); - tor_assert(!(chan->is_listener)); tor_assert(ewma); tor_assert(ewma->heap_index != -1); - smartlist_pqueue_remove(chan->u.cell_chan.active_circuit_pqueue, + smartlist_pqueue_remove(chan->active_circuit_pqueue, compare_cell_ewma_counts, STRUCT_OFFSET(cell_ewma_t, heap_index), ewma); @@ -2239,9 +2235,8 @@ static cell_ewma_t * pop_first_cell_ewma_from_chan(channel_t *chan) { tor_assert(chan); - tor_assert(!(chan->is_listener)); - return smartlist_pqueue_pop(chan->u.cell_chan.active_circuit_pqueue, + return smartlist_pqueue_pop(chan->active_circuit_pqueue, compare_cell_ewma_counts, STRUCT_OFFSET(cell_ewma_t, heap_index)); } @@ -2254,7 +2249,6 @@ make_circuit_active_on_chan(circuit_t *circ, channel_t *chan) circuit_t **nextp = NULL, **prevp = NULL; tor_assert(chan); - tor_assert(!(chan->is_listener)); tor_assert(circ); nextp = next_circ_on_chan_p(circ, chan); @@ -2267,11 +2261,11 @@ make_circuit_active_on_chan(circuit_t *circ, channel_t *chan) assert_active_circuits_ok_paranoid(chan); - if (!(chan->u.cell_chan.active_circuits)) { - chan->u.cell_chan.active_circuits = circ; + if (!(chan->active_circuits)) { + chan->active_circuits = circ; *prevp = *nextp = circ; } else { - circuit_t *head = chan->u.cell_chan.active_circuits; + circuit_t *head = chan->active_circuits; circuit_t *old_tail = *prev_circ_on_chan_p(head, chan); *next_circ_on_chan_p(old_tail, chan) = circ; *nextp = head; @@ -2299,7 +2293,6 @@ make_circuit_inactive_on_chan(circuit_t *circ, channel_t *chan) circuit_t *next = NULL, *prev = NULL; tor_assert(chan); - tor_assert(!(chan->is_listener)); tor_assert(circ); nextp = next_circ_on_chan_p(circ, chan); @@ -2319,12 +2312,12 @@ make_circuit_inactive_on_chan(circuit_t *circ, channel_t *chan) tor_assert(*next_circ_on_chan_p(prev, chan) == circ); if (next == circ) { - chan->u.cell_chan.active_circuits = NULL; + chan->active_circuits = NULL; } else { *prev_circ_on_chan_p(next, chan) = prev; *next_circ_on_chan_p(prev, chan) = next; - if (chan->u.cell_chan.active_circuits == circ) - chan->u.cell_chan.active_circuits = next; + if (chan->active_circuits == circ) + chan->active_circuits = next; } *prevp = *nextp = NULL; @@ -2347,9 +2340,8 @@ channel_unlink_all_active_circs(channel_t *chan) circuit_t *head = NULL, *cur = NULL; tor_assert(chan); - tor_assert(!(chan->is_listener)); - cur = head = chan->u.cell_chan.active_circuits; + cur = head = chan->active_circuits; if (! head) return; do { @@ -2358,12 +2350,12 @@ channel_unlink_all_active_circs(channel_t *chan) *next_circ_on_chan_p(cur, chan) = NULL; cur = next; } while (cur != head); - chan->u.cell_chan.active_circuits = NULL; + chan->active_circuits = NULL; - SMARTLIST_FOREACH(chan->u.cell_chan.active_circuit_pqueue, + SMARTLIST_FOREACH(chan->active_circuit_pqueue, cell_ewma_t *, e, e->heap_index = -1); - smartlist_clear(chan->u.cell_chan.active_circuit_pqueue); + smartlist_clear(chan->active_circuit_pqueue); } /** Block (if block is true) or unblock (if block is false) @@ -2440,9 +2432,8 @@ channel_flush_from_first_active_circuit(channel_t *chan, int max) double ewma_increment = -1; tor_assert(chan); - tor_assert(!(chan->is_listener)); - circ = chan->u.cell_chan.active_circuits; + circ = chan->active_circuits; if (!circ) return 0; assert_active_circuits_ok_paranoid(chan); @@ -2453,13 +2444,13 @@ channel_flush_from_first_active_circuit(channel_t *chan, int max) tor_gettimeofday_cached(&now_hires); tick = cell_ewma_tick_from_timeval(&now_hires, &fractional_tick); - if (tick != chan->u.cell_chan.active_circuit_pqueue_last_recalibrated) { + if (tick != chan->active_circuit_pqueue_last_recalibrated) { scale_active_circuits(chan, tick); } ewma_increment = pow(ewma_scale_factor, -fractional_tick); - cell_ewma = smartlist_get(chan->u.cell_chan.active_circuit_pqueue, 0); + cell_ewma = smartlist_get(chan->active_circuit_pqueue, 0); circ = cell_ewma_to_circuit(cell_ewma); } @@ -2511,8 +2502,8 @@ channel_flush_from_first_active_circuit(channel_t *chan, int max) /* If we just flushed our queue and this circuit is used for a * tunneled directory request, possibly advance its state. */ - if (queue->n == 0 && chan->u.cell_chan.dirreq_id) - geoip_change_dirreq_state(chan->u.cell_chan.dirreq_id, + if (queue->n == 0 && chan->dirreq_id) + geoip_change_dirreq_state(chan->dirreq_id, DIRREQ_TUNNELED, DIRREQ_CIRC_QUEUE_FLUSHED); @@ -2534,7 +2525,7 @@ channel_flush_from_first_active_circuit(channel_t *chan, int max) tor_assert(tmp == cell_ewma); add_cell_ewma_to_chan(chan, cell_ewma); } - if (!ewma_enabled && circ != chan->u.cell_chan.active_circuits) { + if (!ewma_enabled && circ != chan->active_circuits) { /* If this happens, the current circuit just got made inactive by * a call in connection_write_to_buf(). That's nothing to worry about: * circuit_make_inactive_on_conn() already advanced chan->active_circuits @@ -2546,7 +2537,7 @@ channel_flush_from_first_active_circuit(channel_t *chan, int max) } tor_assert(*next_circ_on_chan_p(circ, chan)); assert_active_circuits_ok_paranoid(chan); - chan->u.cell_chan.active_circuits = *next_circ_on_chan_p(circ, chan); + chan->active_circuits = *next_circ_on_chan_p(circ, chan); /* Is the cell queue low enough to unblock all the streams that are waiting * to write to this circuit? */ @@ -2701,9 +2692,8 @@ assert_active_circuits_ok(channel_t *chan) int n = 0; tor_assert(chan); - tor_assert(!(chan->is_listener)); - cur = head = chan->u.cell_chan.active_circuits; + cur = head = chan->active_circuits; if (! head) return; @@ -2723,13 +2713,13 @@ assert_active_circuits_ok(channel_t *chan) tor_assert(ewma->is_for_p_chan); } tor_assert(ewma->heap_index != -1); - tor_assert(ewma == smartlist_get(chan->u.cell_chan.active_circuit_pqueue, + tor_assert(ewma == smartlist_get(chan->active_circuit_pqueue, ewma->heap_index)); n++; cur = next; } while (cur != head); - tor_assert(n == smartlist_len(chan->u.cell_chan.active_circuit_pqueue)); + tor_assert(n == smartlist_len(chan->active_circuit_pqueue)); } /** Return 1 if we shouldn't restart reading on this circuit, even if -- cgit v1.2.3-54-g00ecf From 53454fad956c278921487a9956aa9f022b9cc2c7 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Tue, 9 Oct 2012 10:51:10 -0700 Subject: Set reason_for_closing when erroring out of channel_tls_connect() --- src/or/channeltls.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/or/channeltls.c b/src/or/channeltls.c index 93e06364de..339663e00a 100644 --- a/src/or/channeltls.c +++ b/src/or/channeltls.c @@ -133,6 +133,7 @@ channel_tls_connect(const tor_addr_t *addr, uint16_t port, tlschan->conn = connection_or_connect(addr, port, id_digest, tlschan); /* connection_or_connect() will fill in tlschan->conn */ if (!(tlschan->conn)) { + chan->reason_for_closing = CHANNEL_CLOSE_FOR_ERROR; channel_change_state(chan, CHANNEL_STATE_ERROR); goto err; } -- cgit v1.2.3-54-g00ecf From 1c3362dcdcf39f4b9da8c71567412349d111d08f Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Tue, 9 Oct 2012 11:35:08 -0700 Subject: Use cell_queue_entry_new/free() functions in channel.c --- src/or/channel.c | 157 +++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 135 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/or/channel.c b/src/or/channel.c index 278daa6263..e87f4de1aa 100644 --- a/src/or/channel.c +++ b/src/or/channel.c @@ -18,6 +18,7 @@ #include "channeltls.h" #include "circuitbuild.h" #include "circuitlist.h" +#include "connection_or.h" /* For var_cell_free() */ #include "geoip.h" #include "nodelist.h" #include "relay.h" @@ -80,7 +81,13 @@ static uint64_t n_channels_allocated = 0; */ static digestmap_t *channel_identity_map = NULL; +static cell_queue_entry_t * cell_queue_entry_dup(cell_queue_entry_t *q); +static void cell_queue_entry_free(cell_queue_entry_t *q, int handed_off); static int cell_queue_entry_is_padding(cell_queue_entry_t *q); +static cell_queue_entry_t * +cell_queue_entry_new_fixed(cell_t *cell); +static cell_queue_entry_t * +cell_queue_entry_new_var(var_cell_t *var_cell); /* Functions to maintain the digest map */ static void channel_add_to_digest_map(channel_t *chan); @@ -865,7 +872,7 @@ channel_force_free(channel_t *chan) if (chan->incoming_queue) { SMARTLIST_FOREACH_BEGIN(chan->incoming_queue, cell_queue_entry_t *, q) { - tor_free(q); + cell_queue_entry_free(q, 0); } SMARTLIST_FOREACH_END(q); smartlist_free(chan->incoming_queue); @@ -876,12 +883,7 @@ channel_force_free(channel_t *chan) if (chan->outgoing_queue) { SMARTLIST_FOREACH_BEGIN(chan->outgoing_queue, cell_queue_entry_t *, q) { - if (q->type == CELL_QUEUE_PACKED) { - if (q->u.packed.packed_cell) { - packed_cell_free(q->u.packed.packed_cell); - } - } - tor_free(q); + cell_queue_entry_free(q, 0); } SMARTLIST_FOREACH_END(q); smartlist_free(chan->outgoing_queue); @@ -1501,6 +1503,79 @@ channel_set_remote_end(channel_t *chan, channel_add_to_digest_map(chan); } +/** + * Duplicate a cell queue entry; this is a shallow copy intended for use + * in channel_write_cell_queue_entry(). + */ + +static cell_queue_entry_t * +cell_queue_entry_dup(cell_queue_entry_t *q) +{ + cell_queue_entry_t *rv = NULL; + + tor_assert(q); + + rv = tor_malloc(sizeof(*rv)); + memcpy(rv, q, sizeof(*rv)); + + return rv; +} + +/** + * Free a cell_queue_entry_t; the handed_off parameter indicates whether + * the contents were passed to the lower layer (it is responsible for + * them) or not (we should free). + */ + +static void +cell_queue_entry_free(cell_queue_entry_t *q, int handed_off) { + if (!q) return; + + if (!handed_off) { + /* + * If we handed it off, the recipient becomes responsible (or + * with packed cells the channel_t subclass calls packed_cell + * free after writing out its contents; see, e.g., + * channel_tls_write_packed_cell_method(). Otherwise, we have + * to take care of it here if possible. + */ + switch (q->type) { + case CELL_QUEUE_FIXED: + if (q->u.fixed.cell) { + /* + * There doesn't seem to be a cell_free() function anywhere in the + * pre-channel code; just use tor_free() + */ + tor_free(q->u.fixed.cell); + } + break; + case CELL_QUEUE_PACKED: + if (q->u.packed.packed_cell) { + packed_cell_free(q->u.packed.packed_cell); + } + break; + case CELL_QUEUE_VAR: + if (q->u.var.var_cell) { + /* + * This one's in connection_or.c; it'd be nice to figure out the + * whole flow of cells from one end to the other and factor the + * cell memory management functions like this out of the specific + * TLS lower layer. + */ + var_cell_free(q->u.var.var_cell); + } + break; + default: + /* + * Nothing we can do if we don't know the type; this will + * have been warned about elsewhere. + */ + break; + } + } + tor_free(q); +} + /** * Check whether a cell queue entry is padding; this is a helper function * for channel_write_cell_queue_entry() @@ -1530,6 +1605,42 @@ cell_queue_entry_is_padding(cell_queue_entry_t *q) return 0; } +/** + * Allocate a new cell queue entry for a fixed-size cell + */ + +static cell_queue_entry_t * +cell_queue_entry_new_fixed(cell_t *cell) +{ + cell_queue_entry_t *q = NULL; + + tor_assert(cell); + + q = tor_malloc(sizeof(*q)); + q->type = CELL_QUEUE_FIXED; + q->u.fixed.cell = cell; + + return q; +} + +/** + * Allocate a new cell queue entry for a variable-size cell + */ + +static cell_queue_entry_t * +cell_queue_entry_new_var(var_cell_t *var_cell) +{ + cell_queue_entry_t *q = NULL; + + tor_assert(var_cell); + + q = tor_malloc(sizeof(*q)); + q->type = CELL_QUEUE_VAR; + q->u.var.var_cell = var_cell; + + return q; +} + /** * Write to a channel based on a cell_queue_entry_t * @@ -1601,8 +1712,7 @@ channel_write_cell_queue_entry(channel_t *chan, cell_queue_entry_t *q) * We have to copy the queue entry passed in, since the caller probably * used the stack. */ - tmp = tor_malloc(sizeof(*tmp)); - memcpy(tmp, q, sizeof(*tmp)); + tmp = cell_queue_entry_dup(q); smartlist_add(chan->outgoing_queue, tmp); /* Try to process the queue? */ if (chan->state == CHANNEL_STATE_OPEN) channel_flush_cells(chan); @@ -1980,10 +2090,11 @@ channel_flush_some_cells_from_outgoing_queue(channel_t *chan, if (q->u.fixed.cell) { if (chan->write_cell(chan, q->u.fixed.cell)) { - tor_free(q); ++flushed; channel_timestamp_xmit(chan); ++(chan->n_cells_xmitted); + cell_queue_entry_free(q, 1); + q = NULL; } /* Else couldn't write it; leave it on the queue */ } else { @@ -1994,17 +2105,19 @@ channel_flush_some_cells_from_outgoing_queue(channel_t *chan, "(global ID " U64_FORMAT ").", chan, U64_PRINTF_ARG(chan->global_identifier)); /* Throw it away */ - tor_free(q); + cell_queue_entry_free(q, 0); + q = NULL; } break; case CELL_QUEUE_PACKED: if (q->u.packed.packed_cell) { if (chan->write_packed_cell(chan, q->u.packed.packed_cell)) { - tor_free(q); ++flushed; channel_timestamp_xmit(chan); ++(chan->n_cells_xmitted); + cell_queue_entry_free(q, 1); + q = NULL; } /* Else couldn't write it; leave it on the queue */ } else { @@ -2015,17 +2128,19 @@ channel_flush_some_cells_from_outgoing_queue(channel_t *chan, "(global ID " U64_FORMAT ").", chan, U64_PRINTF_ARG(chan->global_identifier)); /* Throw it away */ - tor_free(q); + cell_queue_entry_free(q, 0); + q = NULL; } break; case CELL_QUEUE_VAR: if (q->u.var.var_cell) { if (chan->write_var_cell(chan, q->u.var.var_cell)) { - tor_free(q); ++flushed; channel_timestamp_xmit(chan); ++(chan->n_cells_xmitted); + cell_queue_entry_free(q, 1); + q = NULL; } /* Else couldn't write it; leave it on the queue */ } else { @@ -2036,7 +2151,8 @@ channel_flush_some_cells_from_outgoing_queue(channel_t *chan, "(global ID " U64_FORMAT ").", chan, U64_PRINTF_ARG(chan->global_identifier)); /* Throw it away */ - tor_free(q); + cell_queue_entry_free(q, 0); + q = NULL; } break; default: @@ -2046,7 +2162,8 @@ channel_flush_some_cells_from_outgoing_queue(channel_t *chan, "(global ID " U64_FORMAT "; ignoring it." " Someone should fix this.", q->type, chan, U64_PRINTF_ARG(chan->global_identifier)); - tor_free(q); /* tor_free() NULLs it out */ + cell_queue_entry_free(q, 0); + q = NULL; } } else { /* This shouldn't happen; log and throw it away */ @@ -2403,9 +2520,7 @@ channel_queue_cell(channel_t *chan, cell_t *cell) } else { /* Otherwise queue it and then process the queue if possible. */ tor_assert(chan->incoming_queue); - q = tor_malloc(sizeof(*q)); - q->type = CELL_QUEUE_FIXED; - q->u.fixed.cell = cell; + q = cell_queue_entry_new_fixed(cell); log_debug(LD_CHANNEL, "Queueing incoming cell_t %p for channel %p " "(global ID " U64_FORMAT ")", @@ -2465,9 +2580,7 @@ channel_queue_var_cell(channel_t *chan, var_cell_t *var_cell) } else { /* Otherwise queue it and then process the queue if possible. */ tor_assert(chan->incoming_queue); - q = tor_malloc(sizeof(*q)); - q->type = CELL_QUEUE_VAR; - q->u.var.var_cell = var_cell; + q = cell_queue_entry_new_var(var_cell); log_debug(LD_CHANNEL, "Queueing incoming var_cell_t %p for channel %p " "(global ID " U64_FORMAT ")", -- cgit v1.2.3-54-g00ecf From f00b44ef8c82eb52fad19fd2218f485f1c5aceca Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Tue, 9 Oct 2012 11:38:15 -0700 Subject: Improve comments on channel_write_*() --- src/or/channel.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/or/channel.c b/src/or/channel.c index e87f4de1aa..334f843ebd 100644 --- a/src/or/channel.c +++ b/src/or/channel.c @@ -1723,7 +1723,9 @@ channel_write_cell_queue_entry(channel_t *chan, cell_queue_entry_t *q) * Write a cell to a channel * * Write a fixed-length cell to a channel using the write_cell() method. - * This is equivalent to the pre-channels connection_or_write_cell_to_buf(). + * This is equivalent to the pre-channels connection_or_write_cell_to_buf(); + * it is called by the transport-independent code to deliver a cell to a + * channel for transmission. */ void @@ -1747,7 +1749,9 @@ channel_write_cell(channel_t *chan, cell_t *cell) /** * Write a packed cell to a channel * - * Write a packed cell to a channel using the write_cell() method. + * Write a packed cell to a channel using the write_cell() method. This is + * called by the transport-independent code to deliver a packed cell to a + * channel for transmission. */ void @@ -1774,7 +1778,8 @@ channel_write_packed_cell(channel_t *chan, packed_cell_t *packed_cell) * * Write a variable-length cell to a channel using the write_cell() method. * This is equivalent to the pre-channels - * connection_or_write_var_cell_to_buf(). + * connection_or_write_var_cell_to_buf(); it's called by the transport- + * independent code to deliver a var_cell to a channel for transmission. */ void -- cgit v1.2.3-54-g00ecf From bddfb9ffa85a0fe73545fb231a0847c4101758c1 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Tue, 9 Oct 2012 14:16:51 -0700 Subject: Add magic number for type-checking channel casts --- src/or/channel.h | 3 +++ src/or/channeltls.c | 37 +++++++++++++++++++++++++++++++++++-- src/or/channeltls.h | 11 +++++++++-- 3 files changed, 47 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/or/channel.h b/src/or/channel.h index c31806cbd4..27fba8fc00 100644 --- a/src/or/channel.h +++ b/src/or/channel.h @@ -26,6 +26,9 @@ typedef void (*channel_var_cell_handler_fn_ptr)(channel_t *, var_cell_t *); */ struct channel_s { + /* Magic number for type-checking cast macros */ + uint32_t magic; + /* Current channel state */ channel_state_t state; diff --git a/src/or/channeltls.c b/src/or/channeltls.c index 339663e00a..5d6a7a912f 100644 --- a/src/or/channeltls.c +++ b/src/or/channeltls.c @@ -102,8 +102,9 @@ channel_tls_connect(const tor_addr_t *addr, uint16_t port, const char *id_digest) { channel_tls_t *tlschan = tor_malloc_zero(sizeof(*tlschan)); - channel_t *chan = TLS_CHAN_TO_BASE(tlschan); + channel_t *chan = &(tlschan->_base); channel_init(chan); + chan->magic = TLS_CHAN_MAGIC; chan->state = CHANNEL_STATE_OPENING; chan->close = channel_tls_close_method; chan->describe_transport = channel_tls_describe_transport_method; @@ -233,12 +234,13 @@ channel_t * channel_tls_handle_incoming(or_connection_t *orconn) { channel_tls_t *tlschan = tor_malloc_zero(sizeof(*tlschan)); - channel_t *chan = TLS_CHAN_TO_BASE(tlschan); + channel_t *chan = &(tlschan->_base); tor_assert(orconn); tor_assert(!(orconn->chan)); channel_init(chan); + chan->magic = TLS_CHAN_MAGIC; chan->state = CHANNEL_STATE_OPENING; chan->close = channel_tls_close_method; chan->describe_transport = channel_tls_describe_transport_method; @@ -267,6 +269,37 @@ channel_tls_handle_incoming(or_connection_t *orconn) return chan; } +/********* + * Casts * + ********/ + +/** + * Cast a channel_tls_t to a channel_t. + */ + +channel_t * +channel_tls_to_base(channel_tls_t *tlschan) +{ + if (!tlschan) return NULL; + + return &(tlschan->_base); +} + +/** + * Cast a channel_t to a channel_tls_t, with appropriate type-checking + * asserts. + */ + +channel_tls_t * +channel_tls_from_base(channel_t *chan) +{ + if (!chan) return NULL; + + tor_assert(chan->magic == TLS_CHAN_MAGIC); + + return (channel_tls_t *)(chan); +} + /******************************************** * Method implementations for channel_tls_t * *******************************************/ diff --git a/src/or/channeltls.h b/src/or/channeltls.h index b38e12adcc..ca2fc88940 100644 --- a/src/or/channeltls.h +++ b/src/or/channeltls.h @@ -12,8 +12,10 @@ #include "or.h" #include "channel.h" -#define BASE_CHAN_TO_TLS(c) ((channel_tls_t *)(c)) -#define TLS_CHAN_TO_BASE(c) ((channel_t *)(c)) +#define BASE_CHAN_TO_TLS(c) (channel_tls_from_base((c))) +#define TLS_CHAN_TO_BASE(c) (channel_tls_to_base((c))) + +#define TLS_CHAN_MAGIC 0x8a192427U #ifdef _TOR_CHANNEL_INTERNAL @@ -32,6 +34,11 @@ channel_listener_t * channel_tls_get_listener(void); channel_listener_t * channel_tls_start_listener(void); channel_t * channel_tls_handle_incoming(or_connection_t *orconn); +/* Casts */ + +channel_t * channel_tls_to_base(channel_tls_t *tlschan); +channel_tls_t * channel_tls_from_base(channel_t *chan); + /* Things for connection_or.c to call back into */ ssize_t channel_tls_flush_some_cells(channel_tls_t *chan, ssize_t num_cells); int channel_tls_more_to_flush(channel_tls_t *chan); -- cgit v1.2.3-54-g00ecf From c684076fc7f685d6e0cd97f426d1474749f1da8b Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Fri, 21 Sep 2012 14:45:32 -0700 Subject: Add circuitmux.c, circuitmux.h --- src/or/Makefile.nmake | 12 ++++----- src/or/circuitmux.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/or/circuitmux.h | 50 ++++++++++++++++++++++++++++++++++++++ src/or/include.am | 2 ++ src/or/or.h | 13 +++++++++- 5 files changed, 137 insertions(+), 7 deletions(-) create mode 100644 src/or/circuitmux.c create mode 100644 src/or/circuitmux.h (limited to 'src') diff --git a/src/or/Makefile.nmake b/src/or/Makefile.nmake index b145fdcae6..9f948d661f 100644 --- a/src/or/Makefile.nmake +++ b/src/or/Makefile.nmake @@ -9,12 +9,12 @@ LIBS = ..\..\..\build-alpha\lib\libevent.a \ ws2_32.lib advapi32.lib shell32.lib LIBTOR_OBJECTS = buffers.obj channel.obj channeltls.obj circuitbuild.obj \ - circuitlist.obj circuituse.obj command.obj config.obj connection.obj - connection_edge.obj connection_or.obj control.obj cpuworker.obj \ - directory.obj dirserv.obj dirvote.obj dns.obj dnsserv.obj geoip.obj \ - hibernate.obj main.obj microdesc.obj networkstatus.obj \ - nodelist.obj onion.obj policies.obj reasons.obj relay.obj \ - rendclient.obj rendcommon.obj rendmid.obj rendservice.obj \ + circuitlist.obj circuitmux.obj circuituse.obj command.obj config.obj \ + connection.obj connection_edge.obj connection_or.obj control.obj \ + cpuworker.obj directory.obj dirserv.obj dirvote.obj dns.obj \ + dnsserv.obj geoip.obj hibernate.obj main.obj microdesc.obj \ + networkstatus.obj nodelist.obj onion.obj policies.obj reasons.obj \ + relay.obj rendclient.obj rendcommon.obj rendmid.obj rendservice.obj \ rephist.obj router.obj routerlist.obj routerparse.obj status.obj \ config_codedigest.obj ntmain.obj diff --git a/src/or/circuitmux.c b/src/or/circuitmux.c new file mode 100644 index 0000000000..45b72f27b4 --- /dev/null +++ b/src/or/circuitmux.c @@ -0,0 +1,67 @@ +/* * Copyright (c) 2012, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file circuitmux.c + * \brief Circuit mux/cell selection abstraction + **/ + +#include "or.h" +#include "circuitmux.h" + +/* + * A circuitmux is a collection of circuits; it tracks which subset + * of the attached circuits are 'active' (i.e., have cells available + * to transmit) and how many cells on each. It expoes three distinct + * interfaces to other components: + * + * To channels, which each have a circuitmux_t, the supported operations + * are: + * + * circuitmux_flush_cells(): + * + * Retrieve a cell from one of the active circuits, chosen according to + * the circuitmux_t's cell selection policy. + * + * circuitmux_unlink_all(): + * + * The channel is closing down, all circuits must be detached. + * + * To circuits, the exposed operations are: + * + * TODO + * + * To circuit selection policies, the exposed operations are: + * + * TODO + * + * General status inquiries? + * + */ + +struct circuitmux_s { + /* + * Double-linked ring of circuits with queued cells waiting for room to + * free up on this connection's outbuf. Every time we pull cells from + * a circuit, we advance this pointer to the next circuit in the ring. + */ + struct circuit_t *active_circuits; + + /* + * Priority queue of cell_ewma_t for circuits with queued cells waiting + * for room to free up on this connection's outbuf. Kept in heap order + * according to EWMA. + * + * This is redundant with active_circuits; if we ever decide only to use + * the cell_ewma algorithm for choosing circuits, we can remove + * active_circuits. + */ + smartlist_t *active_circuit_pqueue; + + /* + * The tick on which the cell_ewma_ts in active_circuit_pqueue last had + * their ewma values rescaled. + */ + unsigned active_circuit_pqueue_last_recalibrated; +}; + diff --git a/src/or/circuitmux.h b/src/or/circuitmux.h new file mode 100644 index 0000000000..c5f95268e7 --- /dev/null +++ b/src/or/circuitmux.h @@ -0,0 +1,50 @@ +/* * Copyright (c) 2012, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file circuitmux.h + * \brief Header file for circuitmux.c + **/ + +#ifndef _TOR_CIRCUITMUX_H +#define _TOR_CIRCUITMUX_H + +#include "or.h" + +/* Consistency check */ +void circuitmux_assert_okay(circuitmux_t *cmux); + +/* Create/destroy */ +circuitmux_t * circuitmux_alloc(void); +void circuitmux_detach_all_circuits(circuitmux_t *cmux); +void circuitmux_free(circuitmux_t *cmux); + +/* Status inquiries */ +cell_direction_t circuitmux_attached_circuit_direction( + circuitmux_t *cmux, + circuit_t *circ); +int circuitmux_is_circuit_attached(circuitmux_t *cmux, circuit_t *circ); +int circuitmux_is_circuit_active(circuitmux_t *cmux, circuit_t *circ); +unsigned int circuitmux_num_cells_for_circuit(circuitmux_t *cmux, + circuit_t *circ); +unsigned int circuitmux_num_cells(circuitmux_t *cmux); +unsigned int circuitmux_num_circuits(circuitmux_t *cmux); +unsigned int circuitmux_num_active_circuits(circuitmux_t *cmux); + +/* Channel interface */ +circuit_t * circuitmux_get_first_active_circuit(circuitmux_t *cmux); +void circuitmux_notify_xmit_cells(circuitmux_t *cmux, circuit_t *circ, + unsigned int n_cells); + +/* Circuit interface */ +void circuitmux_attach_circuit(circuitmux_t *cmux, circuit_t *circ, + cell_direction_t direction); +void circuitmux_detach_circuit(circuitmux_t *cmux, circuit_t *circ); +void circuitmux_clear_num_cells(circuitmux_t *cmux, circuit_t *circ); +void circuitmux_add_to_num_cells(circuitmux_t *cmux, circuit_t *circ, + unsigned int n_cells); +void circuitmux_set_num_cells(circuitmux_t *cmux, circuit_t *circ, + unsigned int n_cells); + +#endif /* _TOR_CIRCUITMUX_H */ + diff --git a/src/or/include.am b/src/or/include.am index c323575320..3ec94e5be3 100644 --- a/src/or/include.am +++ b/src/or/include.am @@ -21,6 +21,7 @@ src_or_libtor_a_SOURCES = \ src/or/channeltls.c \ src/or/circuitbuild.c \ src/or/circuitlist.c \ + src/or/circuitmux.c \ src/or/circuituse.c \ src/or/command.c \ src/or/config.c \ @@ -92,6 +93,7 @@ ORHEADERS = \ src/or/channeltls.h \ src/or/circuitbuild.h \ src/or/circuitlist.h \ + src/or/circuitmux.h \ src/or/circuituse.h \ src/or/command.h \ src/or/config.h \ diff --git a/src/or/or.h b/src/or/or.h index 5987eefd88..87ee7bb7f4 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -1016,6 +1016,10 @@ typedef enum { typedef struct channel_tls_s channel_tls_t; +/* circuitmux_t typedef; struct circuitmux_s is in circuitmux.h */ + +typedef struct circuitmux_s circuitmux_t; + /** Parsed onion routing cell. All communication between nodes * is via cells. */ typedef struct cell_t { @@ -2634,7 +2638,14 @@ typedef struct circuit_t { cell_queue_t n_chan_cells; /** The channel that is next in this circuit. */ channel_t *n_chan; - /** The circuit_id used in the next (forward) hop of this circuit. */ + + /** + * The circuit_id used in the next (forward) hop of this circuit; + * this is unique to n_chan, but this ordered pair is globally + * unique: + * + * (n_chan->global_identifier, n_circ_id) + */ circid_t n_circ_id; /** The hop to which we want to extend this circuit. Should be NULL if -- cgit v1.2.3-54-g00ecf From b208539b8047a12fb2f1f794c9932fddd577dfdb Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Fri, 21 Sep 2012 14:46:22 -0700 Subject: Use circuitmux_t in channels and when relaying cells --- src/or/channel.c | 49 ++++++--- src/or/channel.h | 33 ++---- src/or/channeltls.c | 17 ++- src/or/circuitlist.c | 38 +++++-- src/or/connection_or.c | 2 +- src/or/or.h | 42 ++++++-- src/or/relay.c | 283 +++++++++++++++++++++++-------------------------- src/or/relay.h | 7 +- 8 files changed, 258 insertions(+), 213 deletions(-) (limited to 'src') diff --git a/src/or/channel.c b/src/or/channel.c index 334f843ebd..8241556b57 100644 --- a/src/or/channel.c +++ b/src/or/channel.c @@ -19,6 +19,7 @@ #include "circuitbuild.h" #include "circuitlist.h" #include "connection_or.h" /* For var_cell_free() */ +#include "circuitmux.h" #include "geoip.h" #include "nodelist.h" #include "relay.h" @@ -813,9 +814,10 @@ channel_free(channel_t *chan) channel_clear_remote_end(chan); - if (chan->active_circuit_pqueue) { - smartlist_free(chan->active_circuit_pqueue); - chan->active_circuit_pqueue = NULL; + if (chan->cmux) { + circuitmux_detach_all_circuits(chan->cmux); + circuitmux_free(chan->cmux); + chan->cmux = NULL; } /* We're in CLOSED or ERROR, so the cell queue is already empty */ @@ -866,7 +868,6 @@ channel_force_free(channel_t *chan) if (chan->free) chan->free(chan); channel_clear_remote_end(chan); - smartlist_free(chan->active_circuit_pqueue); /* We might still have a cell queue; kill it */ if (chan->incoming_queue) { @@ -2031,12 +2032,13 @@ channel_flush_some_cells(channel_t *chan, ssize_t num_cells) (unlimited ? -1 : num_cells - flushed)); if (!unlimited && num_cells <= flushed) goto done; - if (chan->active_circuits) { + if (circuitmux_num_cells(chan->cmux) > 0) { /* Try to get more cells from any active circuits */ - num_cells_from_circs = - channel_flush_from_first_active_circuit(chan, - (unlimited ? MAX_CELLS_TO_GET_FROM_CIRCUITS_FOR_UNLIMITED : - (num_cells - flushed))); + num_cells_from_circs = channel_flush_from_first_active_circuit( + chan, + (unlimited ? + MAX_CELLS_TO_GET_FROM_CIRCUITS_FOR_UNLIMITED : + (num_cells - flushed))); /* If it claims we got some, process the queue again */ if (num_cells_from_circs > 0) { @@ -2227,7 +2229,7 @@ channel_more_to_flush(channel_t *chan) smartlist_len(chan->incoming_queue) > 0) return 1; /* Check if any circuits would like to queue some */ - if (chan->active_circuits) return 1; + if (circuitmux_num_cells(chan->cmux) > 0) return 1; /* Else no */ return 0; @@ -2935,8 +2937,8 @@ channel_is_better(time_t now, channel_t *a, channel_t *b, * one that has no circuits is in its grace period. */ - a_has_circs = (a->n_circuits > 0); - b_has_circs = (b->n_circuits > 0); + a_has_circs = (channel_num_circuits(a) > 0); + b_has_circs = (channel_num_circuits(b) > 0); a_grace = (forgive_new_connections && (now < channel_when_created(a) + NEW_CHAN_GRACE_PERIOD)); b_grace = (forgive_new_connections && @@ -3223,9 +3225,10 @@ channel_dump_statistics(channel_t *chan, int severity) " * Channel " U64_FORMAT " has %d active circuits out of" " %d in total", U64_PRINTF_ARG(chan->global_identifier), - (chan->active_circuit_pqueue != NULL) ? - smartlist_len(chan->active_circuit_pqueue) : 0, - chan->n_circuits); + (chan->cmux != NULL) ? + circuitmux_num_active_circuits(chan->cmux) : 0, + (chan->cmux != NULL) ? + circuitmux_num_circuits(chan->cmux) : 0); /* Describe timestamps */ log(severity, LD_GENERAL, @@ -4007,6 +4010,22 @@ channel_matches_target_addr_for_extend(channel_t *chan, return chan->matches_target(chan, target); } +/** + * Return the total number of circuits used by a channel + * + * @param chan Channel to query + * @return Number of circuits using this as n_chan or p_chan + */ + +unsigned int +channel_num_circuits(channel_t *chan) +{ + tor_assert(chan); + + return chan->num_n_circuits + + chan->num_p_circuits; +} + /** * Set up circuit ID generation * diff --git a/src/or/channel.h b/src/or/channel.h index 27fba8fc00..4d3db41cef 100644 --- a/src/or/channel.h +++ b/src/or/channel.h @@ -10,6 +10,7 @@ #define _TOR_CHANNEL_H #include "or.h" +#include "circuitmux.h" /* Channel handler function pointer typedefs */ typedef void (*channel_listener_fn_ptr)(channel_listener_t *, channel_t *); @@ -99,7 +100,7 @@ struct channel_s { int (*matches_target)(channel_t *, const tor_addr_t *); /* Write a cell to an open channel */ int (*write_cell)(channel_t *, cell_t *); - /* Write a packed cell to an open channel */ + /* Write a packed cell to an open channel */ int (*write_packed_cell)(channel_t *, packed_cell_t *); /* Write a variable-length cell to an open channel */ int (*write_var_cell)(channel_t *, var_cell_t *); @@ -124,29 +125,8 @@ struct channel_s { /* List of queued outgoing cells */ smartlist_t *outgoing_queue; - /* Circuit stuff for use by relay.c */ - - /* - * Double-linked ring of circuits with queued cells waiting for room to - * free up on this connection's outbuf. Every time we pull cells from - * a circuit, we advance this pointer to the next circuit in the ring. - */ - struct circuit_t *active_circuits; - /* - * Priority queue of cell_ewma_t for circuits with queued cells waiting - * for room to free up on this connection's outbuf. Kept in heap order - * according to EWMA. - * - * This is redundant with active_circuits; if we ever decide only to use - * the cell_ewma algorithm for choosing circuits, we can remove - * active_circuits. - */ - smartlist_t *active_circuit_pqueue; - /* - * The tick on which the cell_ewma_ts in active_circuit_pqueue last had - * their ewma values rescaled. - */ - unsigned active_circuit_pqueue_last_recalibrated; + /* Circuit mux for circuits sending on this channel */ + circuitmux_t *cmux; /* Circuit ID generation stuff for use by circuitbuild.c */ @@ -161,8 +141,8 @@ struct channel_s { */ circid_t next_circ_id; - /* How many circuits use this connection as p_chan or n_chan? */ - int n_circuits; + /* For how many circuits are we n_chan? What about p_chan? */ + unsigned int num_n_circuits, num_p_circuits; /* * True iff this channel shouldn't get any new circs attached to it, @@ -456,6 +436,7 @@ void channel_mark_client(channel_t *chan); int channel_matches_extend_info(channel_t *chan, extend_info_t *extend_info); int channel_matches_target_addr_for_extend(channel_t *chan, const tor_addr_t *target); +unsigned int channel_num_circuits(channel_t *chan); void channel_set_circid_type(channel_t *chan, crypto_pk_t *identity_rcvd); void channel_timestamp_client(channel_t *chan); diff --git a/src/or/channeltls.c b/src/or/channeltls.c index 5d6a7a912f..036d14f3e4 100644 --- a/src/or/channeltls.c +++ b/src/or/channeltls.c @@ -16,6 +16,7 @@ #include "or.h" #include "channel.h" #include "channeltls.h" +#include "circuitmux.h" #include "config.h" #include "connection.h" #include "connection_or.h" @@ -127,8 +128,11 @@ channel_tls_connect(const tor_addr_t *addr, uint16_t port, if (is_local_addr(addr)) channel_mark_local(chan); channel_mark_outgoing(chan); - chan->active_circuit_pqueue = smartlist_new(); - chan->active_circuit_pqueue_last_recalibrated = cell_ewma_get_tick(); + chan->cmux = circuitmux_alloc(); + /* TODO get rid of this and set policy once we have them + chan->cmux->active_circuit_pqueue_last_recalibrated = + cell_ewma_get_tick(); + */ /* Set up or_connection stuff */ tlschan->conn = connection_or_connect(addr, port, id_digest, tlschan); @@ -146,7 +150,7 @@ channel_tls_connect(const tor_addr_t *addr, uint16_t port, goto done; err: - smartlist_free(chan->active_circuit_pqueue); + circuitmux_free(chan->cmux); tor_free(tlschan); chan = NULL; @@ -260,8 +264,11 @@ channel_tls_handle_incoming(or_connection_t *orconn) if (is_local_addr(&(TO_CONN(orconn)->addr))) channel_mark_local(chan); channel_mark_incoming(chan); - chan->active_circuit_pqueue = smartlist_new(); - chan->active_circuit_pqueue_last_recalibrated = cell_ewma_get_tick(); + chan->cmux = circuitmux_alloc(); + /* TODO set cmux policy + chan->active_circuit_pqueue_last_recalibrated = + cell_ewma_get_tick(); + */ /* If we got one, we should register it */ if (chan) channel_register(chan); diff --git a/src/or/circuitlist.c b/src/or/circuitlist.c index cf6020de06..bec3dc8175 100644 --- a/src/or/circuitlist.c +++ b/src/or/circuitlist.c @@ -134,10 +134,20 @@ circuit_set_circid_chan_helper(circuit_t *circ, int direction, found = HT_REMOVE(chan_circid_map, &chan_circid_map, &search); if (found) { tor_free(found); - --old_chan->n_circuits; + if (direction == CELL_DIRECTION_OUT) { + /* One fewer circuits use old_chan as n_chan */ + --(old_chan->num_n_circuits); + } else { + /* One fewer circuits use old_chan as p_chan */ + --(old_chan->num_p_circuits); + } + } + + /* If we're changing channels, detach the circuit */ + if (old_chan != chan) { + tor_assert(old_chan->cmux); + circuitmux_detach_circuit(old_chan->cmux, circ); } - if (was_active && old_chan != chan) - make_circuit_inactive_on_chan(circ, old_chan); } /* Change the values only after we have possibly made the circuit inactive @@ -161,10 +171,26 @@ circuit_set_circid_chan_helper(circuit_t *circ, int direction, found->circuit = circ; HT_INSERT(chan_circid_map, &chan_circid_map, found); } + + /* Attach to the circuitmux if we're changing channels */ + if (old_chan != chan) { + tor_assert(chan->cmux); + circuitmux_attach_circuit(chan->cmux, circ, direction); + } + + /* + * This is a no-op if we have no cells, but if we do it marks us active to + * the circuitmux + */ if (make_active && old_chan != chan) - make_circuit_active_on_chan(circ,chan); + update_circuit_on_cmux(circ, direction); - ++chan->n_circuits; + /* Adjust circuit counts on new channel */ + if (direction == CELL_DIRECTION_OUT) { + ++chan->num_n_circuits; + } else { + ++chan->num_p_circuits; + } } /** Set the p_conn field of a circuit circ, along @@ -994,7 +1020,7 @@ circuit_unlink_all_from_channel(channel_t *chan, int reason) { circuit_t *circ; - channel_unlink_all_active_circs(chan); + channel_unlink_all_circuits(chan); for (circ = global_circuitlist; circ; circ = circ->next) { int mark = 0; diff --git a/src/or/connection_or.c b/src/or/connection_or.c index bf69711691..f143e9b76b 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -336,7 +336,7 @@ connection_or_get_num_circuits(or_connection_t *conn) tor_assert(conn); if (conn->chan) { - return TLS_CHAN_TO_BASE(conn->chan)->n_circuits; + return channel_num_circuits(TLS_CHAN_TO_BASE(conn->chan)); } else return 0; } diff --git a/src/or/or.h b/src/or/or.h index 87ee7bb7f4..a9b036171a 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -2634,8 +2634,6 @@ typedef struct circuit_t { uint32_t magic; /**< For memory and type debugging: must equal * ORIGIN_CIRCUIT_MAGIC or OR_CIRCUIT_MAGIC. */ - /** Queue of cells waiting to be transmitted on n_conn. */ - cell_queue_t n_chan_cells; /** The channel that is next in this circuit. */ channel_t *n_chan; @@ -2643,13 +2641,36 @@ typedef struct circuit_t { * The circuit_id used in the next (forward) hop of this circuit; * this is unique to n_chan, but this ordered pair is globally * unique: +<<<<<<< HEAD * +======= + * +>>>>>>> f1e8169... Use circuitmux_t in channels and when relaying cells * (n_chan->global_identifier, n_circ_id) */ circid_t n_circ_id; - /** The hop to which we want to extend this circuit. Should be NULL if - * the circuit has attached to a connection. */ + /** + * Circuit mux associated with n_chan to which this circuit is attached; + * NULL if we have no n_chan. + */ + circuitmux_t *mux; + + /** Queue of cells waiting to be transmitted on n_chan */ + cell_queue_t n_chan_cells; + + /** + * The hop to which we want to extend this circuit. Should be NULL if + * the circuit has attached to a connection. + * + * TODO: + * - If this is NULL, we have extended. Is it true that if this is + * NULL then n_chan is not NULL? + * - If n_chan is NULL, then what is n_circ_id? + * - It doesn't matter, because we'll only ever attach to a circuitmux_t + * when n_chan is not NULL, and that's what needs to use a unique ID + * for circuits. + */ extend_info_t *n_hop; /** True iff we are waiting for n_chan_cells to become less full before @@ -2701,6 +2722,15 @@ typedef struct circuit_t { const char *marked_for_close_file; /**< For debugging: in which file was this * circuit marked for close? */ + /** Unique ID for measuring tunneled network status requests. */ + uint64_t dirreq_id; + + /** TODO is this *all* circuits or all circuits on n_chan? */ + struct circuit_t *next; /**< Next circuit in linked list of all circuits. */ + + /** TODO all this from here on down should go away in favor of + * circuitmux_t. + */ /** Next circuit in the doubly-linked ring of circuits waiting to add * cells to n_conn. NULL if we have no cells pending, or if we're not * linked to an OR connection. */ @@ -2709,10 +2739,6 @@ typedef struct circuit_t { * cells to n_conn. NULL if we have no cells pending, or if we're not * linked to an OR connection. */ struct circuit_t *prev_active_on_n_chan; - struct circuit_t *next; /**< Next circuit in linked list of all circuits. */ - - /** Unique ID for measuring tunneled network status requests. */ - uint64_t dirreq_id; /** The EWMA count for the number of cells flushed from the * n_chan_cells queue. Used to determine which circuit to flush from next. diff --git a/src/or/relay.c b/src/or/relay.c index 60f696cd47..f0792437c3 100644 --- a/src/or/relay.c +++ b/src/or/relay.c @@ -1779,10 +1779,10 @@ circuit_consider_sending_sendme(circuit_t *circ, crypt_path_t *layer_hint) } #ifdef ACTIVE_CIRCUITS_PARANOIA -#define assert_active_circuits_ok_paranoid(conn) \ - assert_active_circuits_ok(conn) +#define assert_cmux_ok_paranoid(chan) \ + assert_cmux_okay(chan) #else -#define assert_active_circuits_ok_paranoid(conn) +#define assert_cmux_ok_paranoid(chan) #endif /** The total number of cells we have allocated from the memory pool. */ @@ -2004,6 +2004,7 @@ prev_circ_on_chan_p(circuit_t *circ, channel_t *chan) } } +#if 0 /** Helper for sorting cell_ewma_t values in their priority queue. */ static int compare_cell_ewma_counts(const void *p1, const void *p2) @@ -2240,122 +2241,61 @@ pop_first_cell_ewma_from_chan(channel_t *chan) compare_cell_ewma_counts, STRUCT_OFFSET(cell_ewma_t, heap_index)); } +#endif -/** Add circ to the list of circuits with pending cells on - * chan. No effect if circ is already linked. */ +/** + * Update the number of cells available on the circuit's n_chan or p_chan's + * circuit mux. + */ void -make_circuit_active_on_chan(circuit_t *circ, channel_t *chan) +update_circuit_on_cmux(circuit_t *circ, cell_direction_t direction) { - circuit_t **nextp = NULL, **prevp = NULL; + channel_t *chan = NULL; + or_circuit_t *or_circ = NULL; + circuitmux_t *cmux = NULL; - tor_assert(chan); tor_assert(circ); - nextp = next_circ_on_chan_p(circ, chan); - prevp = prev_circ_on_chan_p(circ, chan); - - if (*nextp && *prevp) { - /* Already active. */ - return; - } - - assert_active_circuits_ok_paranoid(chan); - - if (!(chan->active_circuits)) { - chan->active_circuits = circ; - *prevp = *nextp = circ; - } else { - circuit_t *head = chan->active_circuits; - circuit_t *old_tail = *prev_circ_on_chan_p(head, chan); - *next_circ_on_chan_p(old_tail, chan) = circ; - *nextp = head; - *prev_circ_on_chan_p(head, chan) = circ; - *prevp = old_tail; - } - - if (circ->n_chan == chan) { - add_cell_ewma_to_chan(chan, &circ->n_cell_ewma); + /* Okay, get the channel */ + if (direction == CELL_DIRECTION_OUT) { + chan = circ->n_chan; } else { - or_circuit_t *orcirc = TO_OR_CIRCUIT(circ); - tor_assert(chan == orcirc->p_chan); - add_cell_ewma_to_chan(chan, &orcirc->p_cell_ewma); + or_circ = TO_OR_CIRCUIT(circ); + chan = or_circ->p_chan; } - assert_active_circuits_ok_paranoid(chan); -} - -/** Remove circ from the list of circuits with pending cells on - * chan. No effect if circ is already unlinked. */ -void -make_circuit_inactive_on_chan(circuit_t *circ, channel_t *chan) -{ - circuit_t **nextp = NULL, **prevp = NULL; - circuit_t *next = NULL, *prev = NULL; - tor_assert(chan); - tor_assert(circ); + tor_assert(chan->cmux); - nextp = next_circ_on_chan_p(circ, chan); - prevp = prev_circ_on_chan_p(circ, chan); - next = *nextp; - prev = *prevp; + /* Now get the cmux */ + cmux = chan->cmux; - if (!next && !prev) { - /* Already inactive. */ - return; - } - - assert_active_circuits_ok_paranoid(chan); - - tor_assert(next && prev); - tor_assert(*prev_circ_on_chan_p(next, chan) == circ); - tor_assert(*next_circ_on_chan_p(prev, chan) == circ); + /* Cmux sanity check */ + tor_assert(circuitmux_is_circuit_attached(cmux, circ)); + tor_assert(circuitmux_attached_circuit_direction(cmux, circ) == direction); - if (next == circ) { - chan->active_circuits = NULL; - } else { - *prev_circ_on_chan_p(next, chan) = prev; - *next_circ_on_chan_p(prev, chan) = next; - if (chan->active_circuits == circ) - chan->active_circuits = next; - } - *prevp = *nextp = NULL; + assert_cmux_ok_paranoid(chan); - if (circ->n_chan == chan) { - remove_cell_ewma_from_chan(chan, &circ->n_cell_ewma); + /* Update the number of cells we have for the circuit mux */ + if (direction == CELL_DIRECTION_OUT) { + circuitmux_set_num_cells(cmux, circ, circ->n_chan_cells.n); } else { - or_circuit_t *orcirc = TO_OR_CIRCUIT(circ); - tor_assert(chan == orcirc->p_chan); - remove_cell_ewma_from_chan(chan, &orcirc->p_cell_ewma); + circuitmux_set_num_cells(cmux, circ, or_circ->p_chan_cells.n); } - assert_active_circuits_ok_paranoid(chan); + assert_cmux_ok_paranoid(chan); } -/** Remove all circuits from the list of circuits with pending cells on - * chan. */ +/** Remove all circuits from the cmux on chan. */ void -channel_unlink_all_active_circs(channel_t *chan) +channel_unlink_all_circuits(channel_t *chan) { - circuit_t *head = NULL, *cur = NULL; - tor_assert(chan); + tor_assert(chan->cmux); - cur = head = chan->active_circuits; - if (! head) - return; - do { - circuit_t *next = *next_circ_on_chan_p(cur, chan); - *prev_circ_on_chan_p(cur, chan) = NULL; - *next_circ_on_chan_p(cur, chan) = NULL; - cur = next; - } while (cur != head); - chan->active_circuits = NULL; - - SMARTLIST_FOREACH(chan->active_circuit_pqueue, - cell_ewma_t *, e, - e->heap_index = -1); - smartlist_clear(chan->active_circuit_pqueue); + circuitmux_detach_all_circuits(chan->cmux); + chan->num_n_circuits = 0; + chan->num_p_circuits = 0; } /** Block (if block is true) or unblock (if block is false) @@ -2419,53 +2359,71 @@ set_streams_blocked_on_circ(circuit_t *circ, channel_t *chan, int channel_flush_from_first_active_circuit(channel_t *chan, int max) { - int n_flushed; + circuitmux_t *cmux = NULL; + int n_flushed = 0; cell_queue_t *queue; circuit_t *circ; + or_circuit_t *or_circ; int streams_blocked; + packed_cell_t *cell; +#if 0 /* The current (hi-res) time */ struct timeval now_hires; /* The EWMA cell counter for the circuit we're flushing. */ cell_ewma_t *cell_ewma = NULL; double ewma_increment = -1; +#endif + /* Get the cmux */ tor_assert(chan); + tor_assert(chan->cmux); + cmux = chan->cmux; + + /* Main loop: pick a circuit, send a cell, update the cmux */ + while (n_flushed < max) { + circ = circuitmux_get_first_active_circuit(cmux); + /* If it returns NULL, no cells left to send */ + if (!circ) break; + assert_cmux_ok_paranoid(chan); + +#if 0 + /* This will go in circuitmux_get_first_active_circuit() */ + /* See if we're doing the ewma circuit selection algorithm. */ + if (ewma_enabled) { + unsigned tick; + double fractional_tick; + tor_gettimeofday_cached(&now_hires); + tick = cell_ewma_tick_from_timeval(&now_hires, &fractional_tick); + + if (tick != chan->active_circuit_pqueue_last_recalibrated) { + scale_active_circuits(chan, tick); + } - circ = chan->active_circuits; - if (!circ) return 0; - assert_active_circuits_ok_paranoid(chan); - - /* See if we're doing the ewma circuit selection algorithm. */ - if (ewma_enabled) { - unsigned tick; - double fractional_tick; - tor_gettimeofday_cached(&now_hires); - tick = cell_ewma_tick_from_timeval(&now_hires, &fractional_tick); + ewma_increment = pow(ewma_scale_factor, -fractional_tick); - if (tick != chan->active_circuit_pqueue_last_recalibrated) { - scale_active_circuits(chan, tick); + cell_ewma = smartlist_get(chan->active_circuit_pqueue, 0); + circ = cell_ewma_to_circuit(cell_ewma); } +#endif - ewma_increment = pow(ewma_scale_factor, -fractional_tick); - - cell_ewma = smartlist_get(chan->active_circuit_pqueue, 0); - circ = cell_ewma_to_circuit(cell_ewma); - } - - if (circ->n_chan == chan) { - queue = &circ->n_chan_cells; - streams_blocked = circ->streams_blocked_on_n_chan; - } else { - queue = &TO_OR_CIRCUIT(circ)->p_chan_cells; - streams_blocked = circ->streams_blocked_on_p_chan; - } - tor_assert(*next_circ_on_chan_p(circ, chan)); + if (circ->n_chan == chan) { + queue = &circ->n_chan_cells; + streams_blocked = circ->streams_blocked_on_n_chan; + } else { + or_circ = TO_OR_CIRCUIT(circ); + tor_assert(or_circ->p_chan == chan); + queue = &TO_OR_CIRCUIT(circ)->p_chan_cells; + streams_blocked = circ->streams_blocked_on_p_chan; + } - for (n_flushed = 0; n_flushed < max && queue->head; ) { - packed_cell_t *cell = cell_queue_pop(queue); - tor_assert(*next_circ_on_chan_p(circ, chan)); + /* + * Get just one cell here; once we've sent it, that can change the circuit + * selection, so we have to loop around for another even if this circuit + * has more than one. + */ + cell = cell_queue_pop(queue); /* Calculate the exact time that this cell has spent in the queue. */ if (get_options()->CellStatistics && !CIRCUIT_IS_ORIGIN(circ)) { @@ -2481,8 +2439,8 @@ channel_flush_from_first_active_circuit(channel_t *chan, int max) "Looks like the CellStatistics option was " "recently enabled."); } else { - or_circuit_t *orcirc = TO_OR_CIRCUIT(circ); insertion_time_elem_t *elem = it_queue->first; + or_circ = TO_OR_CIRCUIT(circ); cell_waiting_time = (uint32_t)((flushed * 10L + SECONDS_IN_A_DAY * 1000L - elem->insertion_time * 10L) % @@ -2495,8 +2453,8 @@ channel_flush_from_first_active_circuit(channel_t *chan, int max) it_queue->last = NULL; mp_pool_release(elem); } - orcirc->total_cell_waiting_time += cell_waiting_time; - orcirc->processed_cells++; + or_circ->total_cell_waiting_time += cell_waiting_time; + or_circ->processed_cells++; } } @@ -2507,14 +2465,34 @@ channel_flush_from_first_active_circuit(channel_t *chan, int max) DIRREQ_TUNNELED, DIRREQ_CIRC_QUEUE_FLUSHED); + /* Now send the cell */ channel_write_packed_cell(chan, cell); + cell = NULL; + /* * Don't packed_cell_free_unchecked(cell) here because the channel will * do so when it gets out of the channel queue (probably already did, in * which case that was an immediate double-free bug). */ + /* Update the counter */ ++n_flushed; + + /* + * Now update the cmux; tell it we've just sent a cell, and how many + * we have left. + */ + circuitmux_notify_xmit_cells(cmux, circ, 1); + circuitmux_set_num_cells(cmux, circ, queue->n); + if (queue->n == 0) + log_debug(LD_GENERAL, "Made a circuit inactive."); + + /* Is the cell queue low enough to unblock all the streams that are waiting + * to write to this circuit? */ + if (streams_blocked && queue->n <= CELL_QUEUE_LOWWATER_SIZE) + set_streams_blocked_on_circ(circ, chan, 0, 0); /* unblock streams */ + +#if 0 if (cell_ewma) { cell_ewma_t *tmp; cell_ewma->cell_count += ewma_increment; @@ -2534,22 +2512,13 @@ channel_flush_from_first_active_circuit(channel_t *chan, int max) assert_active_circuits_ok_paranoid(chan); goto done; } - } - tor_assert(*next_circ_on_chan_p(circ, chan)); - assert_active_circuits_ok_paranoid(chan); - chan->active_circuits = *next_circ_on_chan_p(circ, chan); - - /* Is the cell queue low enough to unblock all the streams that are waiting - * to write to this circuit? */ - if (streams_blocked && queue->n <= CELL_QUEUE_LOWWATER_SIZE) - set_streams_blocked_on_circ(circ, chan, 0, 0); /* unblock streams */ +#endif - /* Did we just run out of cells on this circuit's queue? */ - if (queue->n == 0) { - log_debug(LD_GENERAL, "Made a circuit inactive."); - make_circuit_inactive_on_chan(circ, chan); + /* If n_flushed < max still, loop around and pick another circuit */ } - done: + + /* Okay, we're done sending now */ + assert_cmux_ok_paranoid(chan); return n_flushed; } @@ -2587,11 +2556,11 @@ append_cell_to_circuit_queue(circuit_t *circ, channel_t *chan, set_streams_blocked_on_circ(circ, chan, 1, fromstream); } + update_circuit_on_cmux(circ, direction); if (queue->n == 1) { - /* This was the first cell added to the queue. We need to make this + /* This was the first cell added to the queue. We just made this * circuit active. */ log_debug(LD_GENERAL, "Made a circuit active."); - make_circuit_active_on_chan(circ, chan); } if (!channel_has_queued_writes(chan)) { @@ -2669,20 +2638,37 @@ void circuit_clear_cell_queue(circuit_t *circ, channel_t *chan) { cell_queue_t *queue; + cell_direction_t direction; + if (circ->n_chan == chan) { queue = &circ->n_chan_cells; + direction = CELL_DIRECTION_OUT; } else { or_circuit_t *orcirc = TO_OR_CIRCUIT(circ); tor_assert(orcirc->p_chan == chan); queue = &orcirc->p_chan_cells; + direction = CELL_DIRECTION_IN; } - if (queue->n) - make_circuit_inactive_on_chan(circ, chan); - + /* Clear the queue */ cell_queue_clear(queue); + + /* Update the cell counter in the cmux */ + update_circuit_on_cmux(circ, direction); +} + +/** Fail with an assert if the circuit mux on chan is corrupt + */ +void +assert_circuit_mux_okay(channel_t *chan) +{ + tor_assert(chan); + tor_assert(chan->cmux); + + circuitmux_assert_okay(chan->cmux); } +#if 0 /** Fail with an assert if the active circuits ring on orconn is * corrupt. */ void @@ -2721,6 +2707,7 @@ assert_active_circuits_ok(channel_t *chan) tor_assert(n == smartlist_len(chan->active_circuit_pqueue)); } +#endif /** Return 1 if we shouldn't restart reading on this circuit, even if * we get a SENDME. Else return 0. diff --git a/src/or/relay.h b/src/or/relay.h index 7f96d59d15..ef5074bbe4 100644 --- a/src/or/relay.h +++ b/src/or/relay.h @@ -51,11 +51,10 @@ void cell_queue_append_packed_copy(cell_queue_t *queue, const cell_t *cell); void append_cell_to_circuit_queue(circuit_t *circ, channel_t *chan, cell_t *cell, cell_direction_t direction, streamid_t fromstream); -void channel_unlink_all_active_circs(channel_t *chan); +void channel_unlink_all_circuits(channel_t *chan); int channel_flush_from_first_active_circuit(channel_t *chan, int max); -void assert_active_circuits_ok(channel_t *chan); -void make_circuit_inactive_on_chan(circuit_t *circ, channel_t *chan); -void make_circuit_active_on_chan(circuit_t *circ, channel_t *chan); +void assert_circuit_mux_okay(channel_t *chan); +void update_circuit_on_cmux(circuit_t *circ, cell_direction_t direction); int append_address_to_payload(uint8_t *payload_out, const tor_addr_t *addr); const uint8_t *decode_address_from_payload(tor_addr_t *addr_out, -- cgit v1.2.3-54-g00ecf From e4a11b890e7c5fe45dc1f5f271fbd8130ccc9c55 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Mon, 24 Sep 2012 08:52:05 -0700 Subject: Implement circuitmux_alloc()/circuitmux_free() and chanid/circid->muxinfo hash table --- src/or/circuitmux.c | 144 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 143 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/or/circuitmux.c b/src/or/circuitmux.c index 45b72f27b4..d4866caecb 100644 --- a/src/or/circuitmux.c +++ b/src/or/circuitmux.c @@ -9,6 +9,33 @@ #include "or.h" #include "circuitmux.h" +/* + * Private typedefs for circuitmux.c + */ + +/* + * Map of muxinfos for circuitmux_t to use; struct is defined below (name + * of struct must match HT_HEAD line). + */ +typedef struct chanid_circid_muxinfo_map chanid_circid_muxinfo_map_t; + +/* + * Hash table entry (yeah, calling it chanid_circid_muxinfo_s seems to + * break the hash table code). + */ +typedef struct chanid_circid_muxinfo_t chanid_circid_muxinfo_t; + +/* + * Anything the mux wants to store per-circuit in the map; right now just + * a count of queued cells. + */ + +typedef struct circuit_muxinfo_s circuit_muxinfo_t; + +/* + * Structures for circuitmux.c + */ + /* * A circuitmux is a collection of circuits; it tracks which subset * of the attached circuits are 'active' (i.e., have cells available @@ -40,6 +67,14 @@ */ struct circuitmux_s { + /* Keep count of attached, active circuits */ + unsigned int n_circuits, n_active_circuits; + + /* + * Map from (channel ID, circuit ID) pairs to circuit_muxinfo_t + */ + chanid_circid_muxinfo_map_t *chanid_circid_map; + /* * Double-linked ring of circuits with queued cells waiting for room to * free up on this connection's outbuf. Every time we pull cells from @@ -62,6 +97,113 @@ struct circuitmux_s { * The tick on which the cell_ewma_ts in active_circuit_pqueue last had * their ewma values rescaled. */ - unsigned active_circuit_pqueue_last_recalibrated; + unsigned int active_circuit_pqueue_last_recalibrated; +}; + +/* + * This struct holds whatever we want to store per attached circuit on a + * circuitmux_t; right now, just the count of queued cells. + */ + +struct circuit_muxinfo_s { + unsigned int cell_count; +}; + +/* + * A map from channel ID and circuit ID to a circuit_muxinfo_t for that + * circuit. + */ + +struct chanid_circid_muxinfo_t { + HT_ENTRY(chanid_circid_muxinfo_t) node; + uint64_t chan_id; + circid_t circ_id; + circuit_muxinfo_t muxinfo; }; +/* + * Static function declarations + */ + +static INLINE int +chanid_circid_entries_eq(chanid_circid_muxinfo_t *a, + chanid_circid_muxinfo_t *b); +static INLINE unsigned int +chanid_circid_entry_hash(chanid_circid_muxinfo_t *a); + +/* Function definitions */ + +/** + * Helper for chanid_circid_cell_count_map_t hash table: compare the channel + * ID and circuit ID for a and b, and return less than, equal to, or greater + * than zero appropriately. + */ + +static INLINE int +chanid_circid_entries_eq(chanid_circid_muxinfo_t *a, + chanid_circid_muxinfo_t *b) +{ + return a->chan_id == b->chan_id && a->circ_id == b->circ_id; +} + +/** + * Helper: return a hash based on circuit ID and channel ID in a. + */ + +static INLINE unsigned int +chanid_circid_entry_hash(chanid_circid_muxinfo_t *a) +{ + return (((unsigned int)(a->circ_id) << 8) ^ + ((unsigned int)((a->chan_id >> 32) & 0xffffffff)) ^ + ((unsigned int)(a->chan_id & 0xffffffff))); +} + +/* Declare the struct chanid_circid_muxinfo_map type */ +HT_HEAD(chanid_circid_muxinfo_map, chanid_circid_muxinfo_t); + +/* Emit a bunch of hash table stuff */ +HT_PROTOTYPE(chanid_circid_muxinfo_map, chanid_circid_muxinfo_t, node, + chanid_circid_entry_hash, chanid_circid_entries_eq); +HT_GENERATE(chanid_circid_muxinfo_map, chanid_circid_muxinfo_t, node, + chanid_circid_entry_hash, chanid_circid_entries_eq, 0.6, + malloc, realloc, free); + +/** + * Allocate a new circuitmux_t + */ + +circuitmux_t * +circuitmux_alloc(void) +{ + circuitmux_t *rv = NULL; + + rv = tor_malloc_zero(sizeof(*rv)); + rv->chanid_circid_map = tor_malloc_zero(sizeof(*( rv->chanid_circid_map))); + HT_INIT(chanid_circid_muxinfo_map, rv->chanid_circid_map); + + return rv; +} + +/** + * Free a circuitmux_t; the circuits must be detached first with + * circuitmux_detach_all_circuits(). + */ + +void +circuitmux_free(circuitmux_t *cmux) +{ + if (!cmux) return; + + tor_assert(cmux->n_circuits == 0); + tor_assert(cmux->n_active_circuits == 0); + + smartlist_free(cmux->active_circuit_pqueue); + + if (cmux->chanid_circid_map) { + HT_CLEAR(chanid_circid_muxinfo_map, cmux->chanid_circid_map); + tor_free(cmux->chanid_circid_map); + } + + tor_free(cmux); +} + -- cgit v1.2.3-54-g00ecf From 3c41d7f414511aeb6e9e0fd6bfb9be1af539840a Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Wed, 26 Sep 2012 00:56:26 -0700 Subject: Implement circuitmux_attach_circuit() in circuitmux.c --- src/or/circuitmux.c | 238 +++++++++++++++++++++++++++++++++++++++++++++++++++- src/or/or.h | 4 - 2 files changed, 236 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/or/circuitmux.c b/src/or/circuitmux.c index d4866caecb..6508048d2e 100644 --- a/src/or/circuitmux.c +++ b/src/or/circuitmux.c @@ -7,6 +7,7 @@ **/ #include "or.h" +#include "channel.h" #include "circuitmux.h" /* @@ -63,13 +64,16 @@ typedef struct circuit_muxinfo_s circuit_muxinfo_t; * TODO * * General status inquiries? - * + * */ struct circuitmux_s { /* Keep count of attached, active circuits */ unsigned int n_circuits, n_active_circuits; + /* Total number of queued cells on all circuits */ + unsigned int n_cells; + /* * Map from (channel ID, circuit ID) pairs to circuit_muxinfo_t */ @@ -102,11 +106,14 @@ struct circuitmux_s { /* * This struct holds whatever we want to store per attached circuit on a - * circuitmux_t; right now, just the count of queued cells. + * circuitmux_t; right now, just the count of queued cells and the direction. */ struct circuit_muxinfo_s { + /* Count of cells on this circuit at last update */ unsigned int cell_count; + /* Direction of flow */ + cell_direction_t direction; }; /* @@ -168,6 +175,10 @@ HT_GENERATE(chanid_circid_muxinfo_map, chanid_circid_muxinfo_t, node, chanid_circid_entry_hash, chanid_circid_entries_eq, 0.6, malloc, realloc, free); +/* + * Circuitmux alloc/free functions + */ + /** * Allocate a new circuitmux_t */ @@ -207,3 +218,226 @@ circuitmux_free(circuitmux_t *cmux) tor_free(cmux); } +/* + * Circuitmux/circuit attachment status inquiry functions + */ + +/** + * Query the direction of an attached circuit + */ + +cell_direction_t +circuitmux_attached_circuit_direction(circuitmux_t *cmux, circuit_t *circ) +{ + chanid_circid_muxinfo_t *hashent = NULL; + + /* Try to find a map entry */ + hashent = circuitmux_find_map_entry(cmux, circ); + + /* + * This function should only be called on attached circuits; assert that + * we had a map entry. + */ + tor_assert(hashent); + + /* Return the direction from the map entry */ + return hashent->muxinfo.direction; +} + +/** + * Find an entry in the cmux's map for this circuit or return NULL if there + * is none. + */ + +static chanid_circid_muxinfo_t * +circuitmux_find_map_entry(circuitmux_t *cmux, circuit_t *circ) +{ + chanid_circid_muxinfo_t search, *hashent = NULL; + + /* Sanity-check parameters */ + tor_assert(cmux); + tor_assert(cmux->chanid_circid_map); + tor_assert(circ); + tor_assert(circ->n_chan); + + /* Okay, let's see if it's attached for n_chan/n_circ_id */ + search.chan_id = circ->n_chan->global_identifier; + search.circ_id = circ->n_circ_id; + + /* Query */ + hashent = HT_FIND(chanid_circid_muxinfo_map, cmux->chanid_circid_map, + &search); + + /* Found something? */ + if (hashent) { + /* + * Assert that the direction makes sense for a hashent we found by + * n_chan/n_circ_id before we return it. + */ + tor_assert(hashent->muxinfo.direction == CELL_DIRECTION_OUT); + } else { + /* Not there, have we got a p_chan/p_circ_id to try? */ + if (circ->magic == OR_CIRCUIT_MAGIC) { + search.circ_id = TO_OR_CIRCUIT(circ)->p_circ_id; + /* Check for p_chan */ + if (TO_OR_CIRCUIT(circ)->p_chan) { + search.chan_id = TO_OR_CIRCUIT(circ)->p_chan->global_identifier; + /* Okay, search for that */ + hashent = HT_FIND(chanid_circid_muxinfo_map, cmux->chanid_circid_map, + &search); + /* Find anything? */ + if (hashent) { + /* Assert that the direction makes sense before we return it */ + tor_assert(hashent->muxinfo.direction == CELL_DIRECTION_IN); + } + } + } + } + + /* Okay, hashent is it if it was there */ + return hashent; +} + +/** + * Query whether a circuit is attached to a circuitmux + */ + +int +circuitmux_is_circuit_attached(circuitmux_t *cmux, circuit_t *circ) +{ + chanid_circid_muxinfo_t *hashent = NULL; + + /* Look if it's in the circuit map */ + hashent = circuitmux_find_map_entry(cmux, circ); + + return (hashent != NULL); +} + +/* + * Functions for circuit code to call to update circuit status + */ + +/** + * Attach a circuit to a circuitmux, for the specified direction. + */ + +void +circuitmux_attach_circuit(circuitmux_t *cmux, circuit_t *circ, + cell_direction_t direction) +{ + channel_t *chan = NULL; + uint64_t channel_id; + circid_t circ_id; + chanid_circid_muxinfo_t search, *hashent = NULL; + unsigned int cell_count; + + tor_assert(cmux); + tor_assert(circ); + tor_assert(direction == CELL_DIRECTION_IN || + direction == CELL_DIRECTION_OUT); + + /* + * Figure out which channel we're using, and get the circuit's current + * cell count and circuit ID. + */ + if (direction == CELL_DIRECTION_OUT) { + /* It's n_chan */ + chan = circ->n_chan; + cell_count = circ->n_chan_cells.n; + circ_id = circ->n_circ_id; + } else { + /* We want p_chan */ + chan = TO_OR_CIRCUIT(circ)->p_chan; + cell_count = TO_OR_CIRCUIT(circ)->p_chan_cells.n; + circ_id = TO_OR_CIRCUIT(circ)->p_circ_id; + } + /* Assert that we did get a channel */ + tor_assert(chan); + /* Assert that the circuit ID is sensible */ + tor_assert(circ_id != 0); + + /* Get the channel ID */ + channel_id = chan->global_identifier; + + /* See if we already have this one */ + search.chan_id = channel_id; + search.circ_id = circ_id; + hashent = HT_FIND(chanid_circid_muxinfo_map, cmux->chanid_circid_map, + &search); + + if (hashent) { + /* + * This circuit was already attached to this cmux; make sure the + * directions match and update the cell count and active circuit count. + */ + log_info(LD_CIRC, + "Circuit %u on channel " U64_FORMAT " was already attached to " + "cmux %p (trying to attach to %p)", + circ_id, U64_PRINTF_ARG(channel_id), + circ->mux, cmux); + + /* + * The mux pointer on the circuit should match this cmux, and the + * direction in result should match; otherwise assert. + */ + tor_assert(circ->mux == cmux); + tor_assert(hashent->muxinfo.direction == direction); + + /* + * Looks okay; just update the cell count and active circuits if we must + */ + if (hashent->muxinfo.cell_count > 0 && cell_count == 0) { + --(cmux->n_active_circuits); + } else if (hashent->muxinfo.cell_count == 0 && cell_count > 0) { + ++(cmux->n_active_circuits); + } + cmux->n_cells -= hashent->muxinfo.cell_count; + cmux->n_cells += cell_count; + hashent->muxinfo.cell_count = cell_count; + + /* TODO update active_circuits / active_circuit_pqueue */ + } else { + /* + * New circuit; add an entry and update the circuit/active circuit + * counts. + */ + log_debug(LD_CIRC, + "Attaching circuit %u on channel " U64_FORMAT " to cmux %p", + circ_id, U64_PRINTF_ARG(channel_id), cmux); + + /* Assert that the circuit doesn't already have a mux */ + tor_assert(circ->mux == NULL); + + /* Insert it in the map */ + hashent = tor_malloc_zero(sizeof(*hashent)); + hashent->chan_id = channel_id; + hashent->circ_id = circ_id; + hashent->muxinfo.cell_count = cell_count; + hashent->muxinfo.direction = direction; + HT_INSERT(chanid_circid_muxinfo_map, cmux->chanid_circid_map, + hashent); + + /* Set the circuit's mux */ + circ->mux = cmux; + + /* Make sure the next/prev pointers are NULL */ + if (direction == CELL_DIRECTION_OUT) { + circ->next_active_on_n_chan = NULL; + circ->prev_active_on_n_chan = NULL; + } else { + TO_OR_CIRCUIT(circ)->next_active_on_p_chan = NULL; + TO_OR_CIRCUIT(circ)->prev_active_on_p_chan = NULL; + } + + /* Update counters */ + ++(cmux->n_circuits); + if (cell_count > 0) { + ++(cmux->n_active_circuits); + circuitmux_make_circuit_active(cmux, circ, direction); + } + cmux->n_cells += cell_count; + + /* TODO update active_circuits / active_circuit_pqueue */ + } +} + diff --git a/src/or/or.h b/src/or/or.h index a9b036171a..3555a08c71 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -2641,11 +2641,7 @@ typedef struct circuit_t { * The circuit_id used in the next (forward) hop of this circuit; * this is unique to n_chan, but this ordered pair is globally * unique: -<<<<<<< HEAD * -======= - * ->>>>>>> f1e8169... Use circuitmux_t in channels and when relaying cells * (n_chan->global_identifier, n_circ_id) */ circid_t n_circ_id; -- cgit v1.2.3-54-g00ecf From 7e5c358d38cae7769426f1427f23ce6002cda980 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Wed, 26 Sep 2012 10:54:08 -0700 Subject: Implement circuitmux_attached_circuit_direction(), circuitmux_find_map_entry() and circuitmux_is_circuit_attached() in circuitmux.c --- src/or/circuitmux.c | 97 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) (limited to 'src') diff --git a/src/or/circuitmux.c b/src/or/circuitmux.c index 6508048d2e..629fbcf333 100644 --- a/src/or/circuitmux.c +++ b/src/or/circuitmux.c @@ -137,6 +137,8 @@ chanid_circid_entries_eq(chanid_circid_muxinfo_t *a, chanid_circid_muxinfo_t *b); static INLINE unsigned int chanid_circid_entry_hash(chanid_circid_muxinfo_t *a); +static chanid_circid_muxinfo_t * +circuitmux_find_map_entry(circuitmux_t *cmux, circuit_t *circ); /* Function definitions */ @@ -441,3 +443,98 @@ circuitmux_attach_circuit(circuitmux_t *cmux, circuit_t *circ, } } +/* + * Circuitmux/circuit attachment status inquiry functions + */ + +/** + * Query the direction of an attached circuit + */ + +cell_direction_t +circuitmux_attached_circuit_direction(circuitmux_t *cmux, circuit_t *circ) +{ + chanid_circid_muxinfo_t *hashent = NULL; + + /* Try to find a map entry */ + hashent = circuitmux_find_map_entry(cmux, circ); + + /* + * This function should only be called on attached circuits; assert that + * we had a map entry. + */ + tor_assert(hashent); + + /* Return the direction from the map entry */ + return hashent->muxinfo.direction; +} + +/** + * Find an entry in the cmux's map for this circuit or return NULL if there + * is none. + */ + +static chanid_circid_muxinfo_t * +circuitmux_find_map_entry(circuitmux_t *cmux, circuit_t *circ) +{ + chanid_circid_muxinfo_t search, *hashent = NULL; + + /* Sanity-check parameters */ + tor_assert(cmux); + tor_assert(cmux->chanid_circid_map); + tor_assert(circ); + tor_assert(circ->n_chan); + + /* Okay, let's see if it's attached for n_chan/n_circ_id */ + search.chan_id = circ->n_chan->global_identifier; + search.circ_id = circ->n_circ_id; + + /* Query */ + hashent = HT_FIND(chanid_circid_muxinfo_map, cmux->chanid_circid_map, + &search); + + /* Found something? */ + if (hashent) { + /* + * Assert that the direction makes sense for a hashent we found by + * n_chan/n_circ_id before we return it. + */ + tor_assert(hashent->muxinfo.direction == CELL_DIRECTION_OUT); + } else { + /* Not there, have we got a p_chan/p_circ_id to try? */ + if (circ->magic == OR_CIRCUIT_MAGIC) { + search.circ_id = TO_OR_CIRCUIT(circ)->p_circ_id; + /* Check for p_chan */ + if (TO_OR_CIRCUIT(circ)->p_chan) { + search.chan_id = TO_OR_CIRCUIT(circ)->p_chan->global_identifier; + /* Okay, search for that */ + hashent = HT_FIND(chanid_circid_muxinfo_map, cmux->chanid_circid_map, + &search); + /* Find anything? */ + if (hashent) { + /* Assert that the direction makes sense before we return it */ + tor_assert(hashent->muxinfo.direction == CELL_DIRECTION_IN); + } + } + } + } + + /* Okay, hashent is it if it was there */ + return hashent; +} + +/** + * Query whether a circuit is attached to a circuitmux + */ + +int +circuitmux_is_circuit_attached(circuitmux_t *cmux, circuit_t *circ) +{ + chanid_circid_muxinfo_t *hashent = NULL; + + /* Look if it's in the circuit map */ + hashent = circuitmux_find_map_entry(cmux, circ); + + return (hashent != NULL); +} + -- cgit v1.2.3-54-g00ecf From c3ebd0340cca67dc4dd44bd18849100ebba39051 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Wed, 26 Sep 2012 11:40:33 -0700 Subject: Implement circuitmux_detach_circuit() in circuitmux.c --- src/or/circuitmux.c | 98 ++++++++++++++++------------------------------------- 1 file changed, 30 insertions(+), 68 deletions(-) (limited to 'src') diff --git a/src/or/circuitmux.c b/src/or/circuitmux.c index 629fbcf333..2a7d075aed 100644 --- a/src/or/circuitmux.c +++ b/src/or/circuitmux.c @@ -443,98 +443,60 @@ circuitmux_attach_circuit(circuitmux_t *cmux, circuit_t *circ, } } -/* - * Circuitmux/circuit attachment status inquiry functions - */ - /** - * Query the direction of an attached circuit + * Detach a circuit from a circuitmux and update all counters as needed; + * no-op if not attached. */ -cell_direction_t -circuitmux_attached_circuit_direction(circuitmux_t *cmux, circuit_t *circ) +void +circuitmux_detach_circuit(circuitmux_t *cmux, circuit_t *circ) { - chanid_circid_muxinfo_t *hashent = NULL; - - /* Try to find a map entry */ - hashent = circuitmux_find_map_entry(cmux, circ); - + chanid_circid_muxinfo_t search, *hashent = NULL; /* - * This function should only be called on attached circuits; assert that - * we had a map entry. + * Use this to keep track of whether we found it for n_chan or + * p_chan for consistency checking. */ - tor_assert(hashent); - - /* Return the direction from the map entry */ - return hashent->muxinfo.direction; -} - -/** - * Find an entry in the cmux's map for this circuit or return NULL if there - * is none. - */ + cell_direction_t last_searched_direction; -static chanid_circid_muxinfo_t * -circuitmux_find_map_entry(circuitmux_t *cmux, circuit_t *circ) -{ - chanid_circid_muxinfo_t search, *hashent = NULL; - - /* Sanity-check parameters */ tor_assert(cmux); tor_assert(cmux->chanid_circid_map); tor_assert(circ); tor_assert(circ->n_chan); - /* Okay, let's see if it's attached for n_chan/n_circ_id */ + /* See if we have it for n_chan/n_circ_id */ search.chan_id = circ->n_chan->global_identifier; search.circ_id = circ->n_circ_id; + hashent = HT_REMOVE(chanid_circid_muxinfo_map, cmux->chanid_circid_map, + &search); + last_searched_direction = CELL_DIRECTION_OUT; - /* Query */ - hashent = HT_FIND(chanid_circid_muxinfo_map, cmux->chanid_circid_map, - &search); - - /* Found something? */ - if (hashent) { - /* - * Assert that the direction makes sense for a hashent we found by - * n_chan/n_circ_id before we return it. - */ - tor_assert(hashent->muxinfo.direction == CELL_DIRECTION_OUT); - } else { - /* Not there, have we got a p_chan/p_circ_id to try? */ + /* Got one? If not, see if it's an or_circuit_t and try p_chan/p_circ_id */ + if (!hashent) { if (circ->magic == OR_CIRCUIT_MAGIC) { search.circ_id = TO_OR_CIRCUIT(circ)->p_circ_id; - /* Check for p_chan */ if (TO_OR_CIRCUIT(circ)->p_chan) { search.chan_id = TO_OR_CIRCUIT(circ)->p_chan->global_identifier; - /* Okay, search for that */ - hashent = HT_FIND(chanid_circid_muxinfo_map, cmux->chanid_circid_map, - &search); - /* Find anything? */ - if (hashent) { - /* Assert that the direction makes sense before we return it */ - tor_assert(hashent->muxinfo.direction == CELL_DIRECTION_IN); - } + hashent = HT_REMOVE(chanid_circid_muxinfo_map, + cmux->chanid_circid_map, + &search); + last_searched_direction = CELL_DIRECTION_IN; } } } - /* Okay, hashent is it if it was there */ - return hashent; -} - -/** - * Query whether a circuit is attached to a circuitmux - */ - -int -circuitmux_is_circuit_attached(circuitmux_t *cmux, circuit_t *circ) -{ - chanid_circid_muxinfo_t *hashent = NULL; + /* If hashent isn't NULL, we just removed it from the map */ + if (hashent) { + /* Update counters */ + --(cmux->n_circuits); + if (hashent->muxinfo.cell_count > 0) --(cmux->n_active_circuits); + cmux->n_cells -= hashent->muxinfo.cell_count; + /* TODO update active_circuits / active_circuit_pqueue */ - /* Look if it's in the circuit map */ - hashent = circuitmux_find_map_entry(cmux, circ); + /* Consistency check: the direction must match the direction searched */ + tor_assert(last_searched_direction == hashent->muxinfo.direction); - return (hashent != NULL); + /* Free the hash entry */ + tor_free(hashent); + } } -- cgit v1.2.3-54-g00ecf From 8004448635d63fa829d2a836214c42c1609c3f01 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Wed, 26 Sep 2012 11:51:39 -0700 Subject: Handle n_mux/p_mux properly in circuitmux.c --- src/or/circuitmux.c | 30 +++++++++++++++++++++--------- src/or/or.h | 7 ++++++- 2 files changed, 27 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/or/circuitmux.c b/src/or/circuitmux.c index 2a7d075aed..ede2486bfb 100644 --- a/src/or/circuitmux.c +++ b/src/or/circuitmux.c @@ -340,7 +340,8 @@ circuitmux_attach_circuit(circuitmux_t *cmux, circuit_t *circ, /* * Figure out which channel we're using, and get the circuit's current - * cell count and circuit ID. + * cell count and circuit ID; assert that the circuit is not already + * attached to another mux. */ if (direction == CELL_DIRECTION_OUT) { /* It's n_chan */ @@ -376,13 +377,16 @@ circuitmux_attach_circuit(circuitmux_t *cmux, circuit_t *circ, "Circuit %u on channel " U64_FORMAT " was already attached to " "cmux %p (trying to attach to %p)", circ_id, U64_PRINTF_ARG(channel_id), - circ->mux, cmux); + ((direction == CELL_DIRECTION_OUT) ? + circ->n_mux : TO_OR_CIRCUIT(circ)->p_mux), + cmux); /* - * The mux pointer on the circuit should match this cmux, and the - * direction in result should match; otherwise assert. + * The mux pointer on this circuit and the direction in result should + * match; otherwise assert. */ - tor_assert(circ->mux == cmux); + if (direction == CELL_DIRECTION_OUT) tor_assert(circ->n_mux == cmux); + else tor_assert(TO_OR_CIRCUIT(circ)->p_mux == cmux); tor_assert(hashent->muxinfo.direction == direction); /* @@ -407,8 +411,12 @@ circuitmux_attach_circuit(circuitmux_t *cmux, circuit_t *circ, "Attaching circuit %u on channel " U64_FORMAT " to cmux %p", circ_id, U64_PRINTF_ARG(channel_id), cmux); - /* Assert that the circuit doesn't already have a mux */ - tor_assert(circ->mux == NULL); + /* + * Assert that the circuit doesn't already have a mux for this + * direction. + */ + if (direction == CELL_DIRECTION_OUT) tor_assert(circ->n_mux == NULL); + else tor_assert(TO_OR_CIRCUIT(circ)->p_mux == NULL); /* Insert it in the map */ hashent = tor_malloc_zero(sizeof(*hashent)); @@ -419,8 +427,9 @@ circuitmux_attach_circuit(circuitmux_t *cmux, circuit_t *circ, HT_INSERT(chanid_circid_muxinfo_map, cmux->chanid_circid_map, hashent); - /* Set the circuit's mux */ - circ->mux = cmux; + /* Set the circuit's mux for this direction */ + if (direction == CELL_DIRECTION_OUT) circ->n_mux = cmux; + else TO_OR_CIRCUIT(circ)->p_mux = cmux; /* Make sure the next/prev pointers are NULL */ if (direction == CELL_DIRECTION_OUT) { @@ -494,6 +503,9 @@ circuitmux_detach_circuit(circuitmux_t *cmux, circuit_t *circ) /* Consistency check: the direction must match the direction searched */ tor_assert(last_searched_direction == hashent->muxinfo.direction); + /* Clear the circuit's mux for this direction */ + if (last_searched_direction == CELL_DIRECTION_OUT) circ->n_mux = NULL; + else TO_OR_CIRCUIT(circ)->p_mux = NULL; /* Free the hash entry */ tor_free(hashent); diff --git a/src/or/or.h b/src/or/or.h index 3555a08c71..a0fcf8f490 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -2650,7 +2650,7 @@ typedef struct circuit_t { * Circuit mux associated with n_chan to which this circuit is attached; * NULL if we have no n_chan. */ - circuitmux_t *mux; + circuitmux_t *n_mux; /** Queue of cells waiting to be transmitted on n_chan */ cell_queue_t n_chan_cells; @@ -2916,6 +2916,11 @@ typedef struct or_circuit_t { cell_queue_t p_chan_cells; /** The channel that is previous in this circuit. */ channel_t *p_chan; + /** + * Circuit mux associated with p_chan to which this circuit is attached; + * NULL if we have no p_chan. + */ + circuitmux_t *p_mux; /** Linked list of Exit streams associated with this circuit. */ edge_connection_t *n_streams; /** Linked list of Exit streams associated with this circuit that are -- cgit v1.2.3-54-g00ecf From a9deec3550ed51b3cd2a6a51104745def56b5e7a Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Wed, 26 Sep 2012 12:23:58 -0700 Subject: Implement circuitmux_clear_num_cells() and circuitmux_set_num_cells() in circuitmux.c, remove unneeded circuitmux_add_to_num_cells() from circuitmux.h --- src/or/circuitmux.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/or/circuitmux.h | 2 -- 2 files changed, 50 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/or/circuitmux.c b/src/or/circuitmux.c index ede2486bfb..f6cdbf1e56 100644 --- a/src/or/circuitmux.c +++ b/src/or/circuitmux.c @@ -512,3 +512,53 @@ circuitmux_detach_circuit(circuitmux_t *cmux, circuit_t *circ) } } +/** + * Clear the cell counter for a circuit on a circuitmux + */ + +void +circuitmux_clear_num_cells(circuitmux_t *cmux, circuit_t *circ) +{ + /* This is the same as setting the cell count to zero */ + circuitmux_set_num_cells(cmux, circ, 0); +} + +/** + * Set the cell counter for a circuit on a circuitmux + */ + +void +circuitmux_set_num_cells(circuitmux_t *cmux, circuit_t *circ, + unsigned int n_cells) +{ + chanid_circid_muxinfo_t *hashent = NULL; + + tor_assert(cmux); + tor_assert(circ); + + /* Search for this circuit's entry */ + hashent = circuitmux_find_map_entry(cmux, circ); + /* Assert that we found one */ + tor_assert(hashent); + + /* Update cmux cell counter */ + cmux->n_cells -= hashent->muxinfo.cell_count; + cmux->n_cells += n_cells; + + /* + * Update cmux active circuit counter: is the old cell count > 0 and the + * new cell count == 0 ? + */ + if (hashent->muxinfo.cell_count > 0 && n_cells == 0) { + --(cmux->n_active_circuits); + /* TODO update active_circuits / active_circuit_pqueue */ + /* Is the old cell count == 0 and the new cell count > 0 ? */ + } else if (hashent->muxinfo.cell_count == 0 && n_cells > 0) { + ++(cmux->n_active_circuits); + /* TODO update active_circuits / active_circuit_pqueue */ + } + + /* Update hash entry cell counter */ + hashent->muxinfo.cell_count = n_cells; +} + diff --git a/src/or/circuitmux.h b/src/or/circuitmux.h index c5f95268e7..ade544f3b2 100644 --- a/src/or/circuitmux.h +++ b/src/or/circuitmux.h @@ -41,8 +41,6 @@ void circuitmux_attach_circuit(circuitmux_t *cmux, circuit_t *circ, cell_direction_t direction); void circuitmux_detach_circuit(circuitmux_t *cmux, circuit_t *circ); void circuitmux_clear_num_cells(circuitmux_t *cmux, circuit_t *circ); -void circuitmux_add_to_num_cells(circuitmux_t *cmux, circuit_t *circ, - unsigned int n_cells); void circuitmux_set_num_cells(circuitmux_t *cmux, circuit_t *circ, unsigned int n_cells); -- cgit v1.2.3-54-g00ecf From 9da04141e23505074d6867e579fea130bbb00964 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Wed, 26 Sep 2012 12:56:30 -0700 Subject: Implement circuitmux_is_circuit_active(), circuitmux_num_cells_for_circuit(), circuitmux_num_cells(), circuitmux_num_active_circuits() and circuitmux_num_circuits() in circuitmux.c --- src/or/circuitmux.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) (limited to 'src') diff --git a/src/or/circuitmux.c b/src/or/circuitmux.c index f6cdbf1e56..c6402945c3 100644 --- a/src/or/circuitmux.c +++ b/src/or/circuitmux.c @@ -315,6 +315,91 @@ circuitmux_is_circuit_attached(circuitmux_t *cmux, circuit_t *circ) return (hashent != NULL); } +/** + * Query whether a circuit is active on a circuitmux + */ + +int +circuitmux_is_circuit_active(circuitmux_t *cmux, circuit_t *circ) +{ + chanid_circid_muxinfo_t *hashent = NULL; + int is_active = 0; + + tor_assert(cmux); + tor_assert(circ); + + /* Look if it's in the circuit map */ + hashent = circuitmux_find_map_entry(cmux, circ); + if (hashent) { + /* Check the number of cells on this circuit */ + is_active = (hashent->muxinfo.cell_count > 0); + } + /* else not attached, so not active */ + + return is_active; +} + +/** + * Query number of available cells for a circuit on a circuitmux + */ + +unsigned int +circuitmux_num_cells_for_circuit(circuitmux_t *cmux, circuit_t *circ) +{ + chanid_circid_muxinfo_t *hashent = NULL; + unsigned int n_cells = 0; + + tor_assert(cmux); + tor_assert(circ); + + /* Look if it's in the circuit map */ + hashent = circuitmux_find_map_entry(cmux, circ); + if (hashent) { + /* Just get the cell count for this circuit */ + n_cells = hashent->muxinfo.cell_count; + } + /* else not attached, so 0 cells */ + + return n_cells; +} + + +/** + * Query total number of available cells on a circuitmux + */ + +unsigned int +circuitmux_num_cells(circuitmux_t *cmux) +{ + tor_assert(cmux); + + return cmux->n_cells; +} + +/** + * Query total number of circuits active on a circuitmux + */ + +unsigned int +circuitmux_num_active_circuits(circuitmux_t *cmux) +{ + tor_assert(cmux); + + return cmux->n_active_circuits; +} + +/** + * Query total number of circuits attached to a circuitmux + */ + +unsigned int +circuitmux_num_circuits(circuitmux_t *cmux) +{ + tor_assert(cmux); + + return cmux->n_circuits; +} + /* * Functions for circuit code to call to update circuit status */ -- cgit v1.2.3-54-g00ecf From fd31dd440c8feb31dc7b4e0641bd78d36a65e4e3 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Wed, 26 Sep 2012 13:38:32 -0700 Subject: Implement circuitmux_detach_all_circuits() in circuitmux.c --- src/or/circuitmux.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) (limited to 'src') diff --git a/src/or/circuitmux.c b/src/or/circuitmux.c index c6402945c3..61fd30d752 100644 --- a/src/or/circuitmux.c +++ b/src/or/circuitmux.c @@ -8,6 +8,7 @@ #include "or.h" #include "channel.h" +#include "circuitlist.h" #include "circuitmux.h" /* @@ -197,6 +198,74 @@ circuitmux_alloc(void) return rv; } +/** + * Detach all circuits from a circuitmux (use before circuitmux_free()) + */ + +void +circuitmux_detach_all_circuits(circuitmux_t *cmux) +{ + chanid_circid_muxinfo_t **i = NULL, *to_remove; + channel_t *chan = NULL; + circuit_t *circ = NULL; + + tor_assert(cmux); + + i = HT_START(chanid_circid_muxinfo_map, cmux->chanid_circid_map); + while (i) { + to_remove = *i; + i = HT_NEXT_RMV(chanid_circid_muxinfo_map, cmux->chanid_circid_map, i); + if (to_remove) { + /* Find a channel and circuit */ + chan = channel_find_by_global_id(to_remove->chan_id); + if (chan) { + circ = circuit_get_by_circid_channel(to_remove->circ_id, chan); + if (circ) { + /* Clear the circuit's mux for this direction */ + if (to_remove->muxinfo.direction == CELL_DIRECTION_OUT) { + /* Clear n_mux */ + circ->n_mux = NULL; + } else if (circ->magic == OR_CIRCUIT_MAGIC) { + /* + * It has a sensible p_chan and direction == CELL_DIRECTION_IN, + * so clear p_mux. + */ + TO_OR_CIRCUIT(circ)->p_mux = NULL; + } else { + /* Complain and move on */ + log_warn(LD_CIRC, + "Circuit %d/channel " U64_FORMAT " had direction == " + "CELL_DIRECTION_IN, but isn't an or_circuit_t", + to_remove->circ_id, + U64_PRINTF_ARG(to_remove->chan_id)); + } + + /* TODO update active_circuits / active_circuit_pqueue */ + } else { + /* Complain and move on */ + log_warn(LD_CIRC, + "Couldn't find circuit %d (for channel " U64_FORMAT ")", + to_remove->circ_id, + U64_PRINTF_ARG(to_remove->chan_id)); + } + } else { + /* Complain and move on */ + log_warn(LD_CIRC, + "Couldn't find channel " U64_FORMAT " (for circuit id %d)", + U64_PRINTF_ARG(to_remove->chan_id), + to_remove->circ_id); + } + + /* Free it */ + tor_free(to_remove); + } + } + + cmux->n_circuits = 0; + cmux->n_active_circuits = 0; + cmux->n_cells = 0; +} + /** * Free a circuitmux_t; the circuits must be detached first with * circuitmux_detach_all_circuits(). -- cgit v1.2.3-54-g00ecf From 38fa3b7e4489dafdb74ebef96f5a684c1f29eb1e Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Wed, 26 Sep 2012 15:04:29 -0700 Subject: Implement circuitmux_make_circuit_inactive(), circuitmux_make_circuit_active() and linked list helper functions in circuitmux.c --- src/or/circuitmux.c | 239 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 227 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/or/circuitmux.c b/src/or/circuitmux.c index 61fd30d752..77bb56891c 100644 --- a/src/or/circuitmux.c +++ b/src/or/circuitmux.c @@ -85,7 +85,7 @@ struct circuitmux_s { * free up on this connection's outbuf. Every time we pull cells from * a circuit, we advance this pointer to the next circuit in the ring. */ - struct circuit_t *active_circuits; + struct circuit_t *active_circuits_head, *active_circuits_tail; /* * Priority queue of cell_ewma_t for circuits with queued cells waiting @@ -140,9 +140,49 @@ static INLINE unsigned int chanid_circid_entry_hash(chanid_circid_muxinfo_t *a); static chanid_circid_muxinfo_t * circuitmux_find_map_entry(circuitmux_t *cmux, circuit_t *circ); +static void +circuitmux_make_circuit_active(circuitmux_t *cmux, circuit_t *circ, + cell_direction_t direction); +static void +circuitmux_make_circuit_inactive(circuitmux_t *cmux, circuit_t *circ, + cell_direction_t direction); +static INLINE circuit_t ** +circuitmux_next_active_circ_p(circuitmux_t *cmux, circuit_t *circ); +static INLINE circuit_t ** +circuitmux_prev_active_circ_p(circuitmux_t *cmux, circuit_t *circ); /* Function definitions */ +/** + * Linked list helpers + */ + +static INLINE circuit_t ** +circuitmux_next_active_circ_p(circuitmux_t *cmux, circuit_t *circ) +{ + tor_assert(cmux); + tor_assert(circ); + + if (circ->n_mux == cmux) return &(circ->next_active_on_n_chan); + else { + tor_assert(TO_OR_CIRCUIT(circ)->p_mux == cmux); + return &(TO_OR_CIRCUIT(circ)->next_active_on_p_chan); + } +} + +static INLINE circuit_t ** +circuitmux_prev_active_circ_p(circuitmux_t *cmux, circuit_t *circ) +{ + tor_assert(cmux); + tor_assert(circ); + + if (circ->n_mux == cmux) return &(circ->prev_active_on_n_chan); + else { + tor_assert(TO_OR_CIRCUIT(circ)->p_mux == cmux); + return &(TO_OR_CIRCUIT(circ)->prev_active_on_p_chan); + } +} + /** * Helper for chanid_circid_cell_count_map_t hash table: compare the channel * ID and circuit ID for a and b, and return less than, equal to, or greater @@ -225,12 +265,16 @@ circuitmux_detach_all_circuits(circuitmux_t *cmux) if (to_remove->muxinfo.direction == CELL_DIRECTION_OUT) { /* Clear n_mux */ circ->n_mux = NULL; + /* Update active_circuits et al. */ + circuitmux_make_circuit_inactive(cmux, circ, CELL_DIRECTION_OUT); } else if (circ->magic == OR_CIRCUIT_MAGIC) { /* * It has a sensible p_chan and direction == CELL_DIRECTION_IN, * so clear p_mux. */ TO_OR_CIRCUIT(circ)->p_mux = NULL; + /* Update active_circuits et al. */ + circuitmux_make_circuit_inactive(cmux, circ, CELL_DIRECTION_IN); } else { /* Complain and move on */ log_warn(LD_CIRC, @@ -239,8 +283,6 @@ circuitmux_detach_all_circuits(circuitmux_t *cmux) to_remove->circ_id, U64_PRINTF_ARG(to_remove->chan_id)); } - - /* TODO update active_circuits / active_circuit_pqueue */ } else { /* Complain and move on */ log_warn(LD_CIRC, @@ -432,7 +474,6 @@ circuitmux_num_cells_for_circuit(circuitmux_t *cmux, circuit_t *circ) return n_cells; } - /** * Query total number of available cells on a circuitmux */ @@ -548,14 +589,14 @@ circuitmux_attach_circuit(circuitmux_t *cmux, circuit_t *circ, */ if (hashent->muxinfo.cell_count > 0 && cell_count == 0) { --(cmux->n_active_circuits); + circuitmux_make_circuit_inactive(cmux, circ, direction); } else if (hashent->muxinfo.cell_count == 0 && cell_count > 0) { ++(cmux->n_active_circuits); + circuitmux_make_circuit_active(cmux, circ, direction); } cmux->n_cells -= hashent->muxinfo.cell_count; cmux->n_cells += cell_count; hashent->muxinfo.cell_count = cell_count; - - /* TODO update active_circuits / active_circuit_pqueue */ } else { /* * New circuit; add an entry and update the circuit/active circuit @@ -601,8 +642,6 @@ circuitmux_attach_circuit(circuitmux_t *cmux, circuit_t *circ, circuitmux_make_circuit_active(cmux, circ, direction); } cmux->n_cells += cell_count; - - /* TODO update active_circuits / active_circuit_pqueue */ } } @@ -651,9 +690,11 @@ circuitmux_detach_circuit(circuitmux_t *cmux, circuit_t *circ) if (hashent) { /* Update counters */ --(cmux->n_circuits); - if (hashent->muxinfo.cell_count > 0) --(cmux->n_active_circuits); + if (hashent->muxinfo.cell_count > 0) { + --(cmux->n_active_circuits); + circuitmux_make_circuit_inactive(cmux, circ, last_searched_direction); + } cmux->n_cells -= hashent->muxinfo.cell_count; - /* TODO update active_circuits / active_circuit_pqueue */ /* Consistency check: the direction must match the direction searched */ tor_assert(last_searched_direction == hashent->muxinfo.direction); @@ -666,6 +707,180 @@ circuitmux_detach_circuit(circuitmux_t *cmux, circuit_t *circ) } } +/** + * Make a circuit active; update active list and policy-specific info, but + * we don't mess with the counters or hash table here. + */ + +static void +circuitmux_make_circuit_active(circuitmux_t *cmux, circuit_t *circ, + cell_direction_t direction) +{ + circuit_t **next_active = NULL, **prev_active = NULL, **next_prev = NULL; + circuitmux_t *circuit_cmux = NULL; + channel_t *chan = NULL; + circid_t circ_id; + int already_active; + + tor_assert(cmux); + tor_assert(circ); + tor_assert(direction == CELL_DIRECTION_OUT || + direction == CELL_DIRECTION_IN); + + /* Get the right set of active list links for this direction */ + if (direction == CELL_DIRECTION_OUT) { + next_active = &(circ->next_active_on_n_chan); + prev_active = &(circ->prev_active_on_n_chan); + circuit_cmux = circ->n_mux; + chan = circ->n_chan; + circ_id = circ->n_circ_id; + } else { + next_active = &(TO_OR_CIRCUIT(circ)->next_active_on_p_chan); + prev_active = &(TO_OR_CIRCUIT(circ)->prev_active_on_p_chan); + circuit_cmux = TO_OR_CIRCUIT(circ)->p_mux; + chan = TO_OR_CIRCUIT(circ)->p_chan; + circ_id = TO_OR_CIRCUIT(circ)->p_circ_id; + } + + /* Assert that it is attached to this mux and a channel */ + tor_assert(cmux == circuit_cmux); + tor_assert(chan != NULL); + + /* + * Check if the circuit really was inactive; if it's active, at least one + * of the next_active and prev_active pointers will not be NULL, or this + * circuit will be either the head or tail of the list for this cmux. + */ + already_active = (*prev_active != NULL || *next_active != NULL || + cmux->active_circuits_head == circ || + cmux->active_circuits_tail == circ); + + /* If we're already active, log a warning and finish */ + if (already_active) { + log_warn(LD_CIRC, + "Circuit %d on channel " U64_FORMAT " was already active", + circ_id, U64_PRINTF_ARG(chan->global_identifier)); + return; + } + + /* + * This is going at the head of the list; if the old head is not NULL, + * then its prev pointer should point to this. + */ + *next_active = cmux->active_circuits_head; /* Next is old head */ + *prev_active = NULL; /* Prev is NULL (this will be the head) */ + if (cmux->active_circuits_head) { + /* The list had an old head; update its prev pointer */ + next_prev = + circuitmux_prev_active_circ_p(cmux, cmux->active_circuits_head); + tor_assert(next_prev); + *next_prev = circ; + } else { + /* The list was empty; this becomes the tail as well */ + cmux->active_circuits_tail = circ; + } + /* This becomes the new head of the list */ + cmux->active_circuits_head = circ; + + /* TODO policy-specific notifications */ +} + +/** + * Make a circuit inactive; update active list and policy-specific info, but + * we don't mess with the counters or hash table here. + */ + +static void +circuitmux_make_circuit_inactive(circuitmux_t *cmux, circuit_t *circ, + cell_direction_t direction) +{ + circuit_t **next_active = NULL, **prev_active = NULL; + circuit_t **next_prev = NULL, **prev_next = NULL; + circuitmux_t *circuit_cmux = NULL; + channel_t *chan = NULL; + circid_t circ_id; + int already_inactive; + + tor_assert(cmux); + tor_assert(circ); + tor_assert(direction == CELL_DIRECTION_OUT || + direction == CELL_DIRECTION_IN); + + /* Get the right set of active list links for this direction */ + if (direction == CELL_DIRECTION_OUT) { + next_active = &(circ->next_active_on_n_chan); + prev_active = &(circ->prev_active_on_n_chan); + circuit_cmux = circ->n_mux; + chan = circ->n_chan; + circ_id = circ->n_circ_id; + } else { + next_active = &(TO_OR_CIRCUIT(circ)->next_active_on_p_chan); + prev_active = &(TO_OR_CIRCUIT(circ)->prev_active_on_p_chan); + circuit_cmux = TO_OR_CIRCUIT(circ)->p_mux; + chan = TO_OR_CIRCUIT(circ)->p_chan; + circ_id = TO_OR_CIRCUIT(circ)->p_circ_id; + } + + /* Assert that it is attached to this mux and a channel */ + tor_assert(cmux == circuit_cmux); + tor_assert(chan != NULL); + + /* + * Check if the circuit really was active; if it's inactive, the + * next_active and prev_active pointers will be NULL and this circuit + * will not be the head or tail of the list for this cmux. + */ + already_inactive = (*prev_active == NULL && *next_active == NULL && + cmux->active_circuits_head != circ && + cmux->active_circuits_tail != circ); + + /* If we're already inactive, log a warning and finish */ + if (already_inactive) { + log_warn(LD_CIRC, + "Circuit %d on channel " U64_FORMAT " was already inactive", + circ_id, U64_PRINTF_ARG(chan->global_identifier)); + return; + } + + /* Remove from the list; first get next_prev and prev_next */ + if (*next_active) { + /* + * If there's a next circuit, its previous circuit becomes this + * circuit's previous circuit. + */ + next_prev = circuitmux_prev_active_circ_p(cmux, *next_active); + } else { + /* Else, the tail becomes this circuit's previous circuit */ + next_prev = &(cmux->active_circuits_tail); + } + + /* Got next_prev, now prev_next */ + if (*prev_active) { + /* + * If there's a previous circuit, its next circuit becomes this circuit's + * next circuit. + */ + prev_next = circuitmux_next_active_circ_p(cmux, *prev_active); + } else { + /* Else, the head becomes this circuit's next circuit */ + prev_next = &(cmux->active_circuits_head); + } + + /* Assert that we got sensible values for the next/prev pointers */ + tor_assert(next_prev != NULL); + tor_assert(prev_next != NULL); + + /* Update the next/prev pointers - this removes circ from the list */ + *next_prev = *prev_active; + *prev_next = *next_active; + + /* Now null out prev_active/next_active */ + *prev_active = NULL; + *next_active = NULL; + + /* TODO policy-specific notifications */ +} + /** * Clear the cell counter for a circuit on a circuitmux */ @@ -705,11 +920,11 @@ circuitmux_set_num_cells(circuitmux_t *cmux, circuit_t *circ, */ if (hashent->muxinfo.cell_count > 0 && n_cells == 0) { --(cmux->n_active_circuits); - /* TODO update active_circuits / active_circuit_pqueue */ + circuitmux_make_circuit_inactive(cmux, circ, hashent->muxinfo.direction); /* Is the old cell count == 0 and the new cell count > 0 ? */ } else if (hashent->muxinfo.cell_count == 0 && n_cells > 0) { ++(cmux->n_active_circuits); - /* TODO update active_circuits / active_circuit_pqueue */ + circuitmux_make_circuit_active(cmux, circ, hashent->muxinfo.direction); } /* Update hash entry cell counter */ -- cgit v1.2.3-54-g00ecf From 35f5259df4feacbb77b14c6da30ec137a21763bb Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Wed, 26 Sep 2012 15:05:10 -0700 Subject: Eliminate linked list helper functions in relay.c which are no longer used --- src/or/relay.c | 32 -------------------------------- 1 file changed, 32 deletions(-) (limited to 'src') diff --git a/src/or/relay.c b/src/or/relay.c index f0792437c3..e3b383ba76 100644 --- a/src/or/relay.c +++ b/src/or/relay.c @@ -1972,38 +1972,6 @@ cell_queue_pop(cell_queue_t *queue) return cell; } -/** Return a pointer to the "next_active_on_{n,p}_chan" pointer of circ, - * depending on whether chan matches n_chan or p_chan. */ -static INLINE circuit_t ** -next_circ_on_chan_p(circuit_t *circ, channel_t *chan) -{ - tor_assert(circ); - tor_assert(chan); - if (chan == circ->n_chan) { - return &circ->next_active_on_n_chan; - } else { - or_circuit_t *orcirc = TO_OR_CIRCUIT(circ); - tor_assert(chan == orcirc->p_chan); - return &orcirc->next_active_on_p_chan; - } -} - -/** Return a pointer to the "prev_active_on_{n,p}_chan" pointer of circ, - * depending on whether chan matches n_chan or p_chan. */ -static INLINE circuit_t ** -prev_circ_on_chan_p(circuit_t *circ, channel_t *chan) -{ - tor_assert(circ); - tor_assert(chan); - if (chan == circ->n_chan) { - return &circ->prev_active_on_n_chan; - } else { - or_circuit_t *orcirc = TO_OR_CIRCUIT(circ); - tor_assert(chan == orcirc->p_chan); - return &orcirc->prev_active_on_p_chan; - } -} - #if 0 /** Helper for sorting cell_ewma_t values in their priority queue. */ static int -- cgit v1.2.3-54-g00ecf From 851734d3245dfd56be02f6f4a78a3b776f4ebedc Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Thu, 27 Sep 2012 11:38:27 -0700 Subject: Implement circuitmux policy basic notifications mechanism --- src/or/circuitmux.c | 59 ++++++++++++++++++++++++++++++++++++++--------------- src/or/circuitmux.h | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/or/circuitmux.c b/src/or/circuitmux.c index 77bb56891c..860ba64e5d 100644 --- a/src/or/circuitmux.c +++ b/src/or/circuitmux.c @@ -88,21 +88,14 @@ struct circuitmux_s { struct circuit_t *active_circuits_head, *active_circuits_tail; /* - * Priority queue of cell_ewma_t for circuits with queued cells waiting - * for room to free up on this connection's outbuf. Kept in heap order - * according to EWMA. - * - * This is redundant with active_circuits; if we ever decide only to use - * the cell_ewma algorithm for choosing circuits, we can remove - * active_circuits. + * Circuitmux policy; if this is non-NULL, it can override the built- + * in round-robin active circuits behavior. This is how EWMA works in + * the new circuitmux_t world. */ - smartlist_t *active_circuit_pqueue; + const circuitmux_policy_t *policy; - /* - * The tick on which the cell_ewma_ts in active_circuit_pqueue last had - * their ewma values rescaled. - */ - unsigned int active_circuit_pqueue_last_recalibrated; + /* Policy-specific data */ + circuitmux_policy_data_t *policy_data; }; /* @@ -115,6 +108,8 @@ struct circuit_muxinfo_s { unsigned int cell_count; /* Direction of flow */ cell_direction_t direction; + /* Policy-specific data */ + circuitmux_policy_circ_data_t *policy_data; }; /* @@ -321,7 +316,13 @@ circuitmux_free(circuitmux_t *cmux) tor_assert(cmux->n_circuits == 0); tor_assert(cmux->n_active_circuits == 0); - smartlist_free(cmux->active_circuit_pqueue); + /* Free policy-specific data if we have any */ + if (cmux->policy && cmux->policy->free_cmux_data) { + if (cmux->policy_data) { + cmux->policy->free_cmux_data(cmux, cmux->policy_data); + cmux->policy_data = NULL; + } + } else tor_assert(cmux->policy_data == NULL); if (cmux->chanid_circid_map) { HT_CLEAR(chanid_circid_muxinfo_map, cmux->chanid_circid_map); @@ -718,6 +719,7 @@ circuitmux_make_circuit_active(circuitmux_t *cmux, circuit_t *circ, { circuit_t **next_active = NULL, **prev_active = NULL, **next_prev = NULL; circuitmux_t *circuit_cmux = NULL; + chanid_circid_muxinfo_t *hashent = NULL; channel_t *chan = NULL; circid_t circ_id; int already_active; @@ -782,7 +784,19 @@ circuitmux_make_circuit_active(circuitmux_t *cmux, circuit_t *circ, /* This becomes the new head of the list */ cmux->active_circuits_head = circ; - /* TODO policy-specific notifications */ + /* Policy-specific notification */ + if (cmux->policy && + cmux->policy->notify_circ_active && + cmux->policy_data) { + /* Okay, we need to check the circuit for policy data now */ + hashent = circuitmux_find_map_entry(cmux, circ); + /* We should have found something */ + tor_assert(hashent); + /* Check for policy data for the circuit and notify */ + if (hashent->muxinfo.policy_data) + cmux->policy->notify_circ_active(cmux, cmux->policy_data, + circ, hashent->muxinfo.policy_data); + } } /** @@ -797,6 +811,7 @@ circuitmux_make_circuit_inactive(circuitmux_t *cmux, circuit_t *circ, circuit_t **next_active = NULL, **prev_active = NULL; circuit_t **next_prev = NULL, **prev_next = NULL; circuitmux_t *circuit_cmux = NULL; + chanid_circid_muxinfo_t *hashent = NULL; channel_t *chan = NULL; circid_t circ_id; int already_inactive; @@ -878,7 +893,19 @@ circuitmux_make_circuit_inactive(circuitmux_t *cmux, circuit_t *circ, *prev_active = NULL; *next_active = NULL; - /* TODO policy-specific notifications */ + /* Policy-specific notification */ + if (cmux->policy && + cmux->policy->notify_circ_inactive && + cmux->policy_data) { + /* Okay, we need to check the circuit for policy data now */ + hashent = circuitmux_find_map_entry(cmux, circ); + /* We should have found something */ + tor_assert(hashent); + /* Check for policy data for the circuit and notify */ + if (hashent->muxinfo.policy_data) + cmux->policy->notify_circ_inactive(cmux, cmux->policy_data, + circ, hashent->muxinfo.policy_data); + } } /** diff --git a/src/or/circuitmux.h b/src/or/circuitmux.h index ade544f3b2..e344b6ed91 100644 --- a/src/or/circuitmux.h +++ b/src/or/circuitmux.h @@ -11,6 +11,57 @@ #include "or.h" +typedef struct circuitmux_policy_s circuitmux_policy_t; +typedef struct circuitmux_policy_data_s circuitmux_policy_data_t; +typedef struct circuitmux_policy_circ_data_s circuitmux_policy_circ_data_t; + +struct circuitmux_policy_s { + /* Allocate cmux-wide policy-specific data */ + circuitmux_policy_data_t * (*alloc_cmux_data)(circuitmux_t *cmux); + /* Free cmux-wide policy-specific data */ + void (*free_cmux_data)(circuitmux_t *cmux, + circuitmux_policy_data_t *pol_data); + /* Allocate circuit policy-specific data for a newly attached circuit */ + circuitmux_policy_circ_data_t * + (*alloc_circ_data)(circuitmux_t *cmux, + circuitmux_policy_data_t *pol_data, + circuit_t *circ, + cell_direction_t direction, + unsigned int cell_count); + /* Free circuit policy-specific data */ + void (*free_circ_data)(circuitmux_t *cmux, + circuitmux_policy_data_t *pol_data, + circuit_t *circ, + circuitmux_policy_circ_data_t *pol_circ_data); + /* Notify that a circuit has become active/inactive */ + void (*notify_circ_active)(circuitmux_t *cmux, + circuitmux_policy_data_t *pol_data, + circuit_t *circ, + circuitmux_policy_circ_data_t *pol_circ_data); + void (*notify_circ_inactive)(circuitmux_t *cmux, + circuitmux_policy_data_t *pol_data, + circuit_t *circ, + circuitmux_policy_circ_data_t *pol_circ_data); +}; + +/* + * Circuitmux policy implementations can subclass this to store circuitmux- + * wide data; it just has the magic number in the base struct. + */ + +struct circuitmux_policy_data_s { + uint32_t magic; +}; + +/* + * Circuitmux policy implementations can subclass this to store circuit- + * specific data; it just has the magic number in the base struct. + */ + +struct circuitmux_policy_circ_data_s { + uint32_t magic; +}; + /* Consistency check */ void circuitmux_assert_okay(circuitmux_t *cmux); @@ -19,6 +70,12 @@ circuitmux_t * circuitmux_alloc(void); void circuitmux_detach_all_circuits(circuitmux_t *cmux); void circuitmux_free(circuitmux_t *cmux); +/* Policy control */ +void circuitmux_clear_policy(circuitmux_t *cmux); +const circuitmux_policy_t * circuitmux_get_policy(circuitmux_t *cmux); +void circuitmux_set_policy(circuitmux_t *cmux, + const circuitmux_policy_t *pol); + /* Status inquiries */ cell_direction_t circuitmux_attached_circuit_direction( circuitmux_t *cmux, -- cgit v1.2.3-54-g00ecf From 930e3d611a7983dc0d5558b21b79e81f72cb7158 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Thu, 27 Sep 2012 12:00:21 -0700 Subject: Allocate and free circuit-specific policy data in the right places --- src/or/circuitmux.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 56 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/or/circuitmux.c b/src/or/circuitmux.c index 860ba64e5d..5a5739fcd9 100644 --- a/src/or/circuitmux.c +++ b/src/or/circuitmux.c @@ -260,7 +260,10 @@ circuitmux_detach_all_circuits(circuitmux_t *cmux) if (to_remove->muxinfo.direction == CELL_DIRECTION_OUT) { /* Clear n_mux */ circ->n_mux = NULL; - /* Update active_circuits et al. */ + /* + * Update active_circuits et al.; this does policy notifies, so + * comes before freeing policy data + */ circuitmux_make_circuit_inactive(cmux, circ, CELL_DIRECTION_OUT); } else if (circ->magic == OR_CIRCUIT_MAGIC) { /* @@ -268,7 +271,10 @@ circuitmux_detach_all_circuits(circuitmux_t *cmux) * so clear p_mux. */ TO_OR_CIRCUIT(circ)->p_mux = NULL; - /* Update active_circuits et al. */ + /* + * Update active_circuits et al.; this does policy notifies, so + * comes before freeing policy data + */ circuitmux_make_circuit_inactive(cmux, circ, CELL_DIRECTION_IN); } else { /* Complain and move on */ @@ -278,6 +284,24 @@ circuitmux_detach_all_circuits(circuitmux_t *cmux) to_remove->circ_id, U64_PRINTF_ARG(to_remove->chan_id)); } + + + /* Free policy-specific data if we have it */ + if (to_remove->muxinfo.policy_data) { + /* + * If we have policy data, assert that we have the means to + * free it + */ + tor_assert(cmux->policy); + tor_assert(cmux->policy_data); + tor_assert(cmux->policy->free_circ_data); + /* Call free_circ_data() */ + cmux->policy->free_circ_data(cmux, + cmux->policy_data, + circ, + to_remove->muxinfo.policy_data); + to_remove->muxinfo.policy_data = NULL; + } } else { /* Complain and move on */ log_warn(LD_CIRC, @@ -293,6 +317,9 @@ circuitmux_detach_all_circuits(circuitmux_t *cmux) to_remove->circ_id); } + /* Assert that we don't have un-freed policy data for this circuit */ + tor_assert(to_remove->muxinfo.policy_data == NULL); + /* Free it */ tor_free(to_remove); } @@ -620,6 +647,18 @@ circuitmux_attach_circuit(circuitmux_t *cmux, circuit_t *circ, hashent->circ_id = circ_id; hashent->muxinfo.cell_count = cell_count; hashent->muxinfo.direction = direction; + /* Allocate policy specific circuit data if we need it */ + if (cmux->policy && cmux->policy_data && + cmux->policy->alloc_circ_data) { + hashent->muxinfo.policy_data = + cmux->policy->alloc_circ_data(cmux, + cmux->policy_data, + circ, + direction, + cell_count); + /* If we wanted policy data, it's an error not to get any */ + tor_assert(hashent->muxinfo.policy_data); + } HT_INSERT(chanid_circid_muxinfo_map, cmux->chanid_circid_map, hashent); @@ -693,10 +732,25 @@ circuitmux_detach_circuit(circuitmux_t *cmux, circuit_t *circ) --(cmux->n_circuits); if (hashent->muxinfo.cell_count > 0) { --(cmux->n_active_circuits); + /* This does policy notifies, so comes before freeing policy data */ circuitmux_make_circuit_inactive(cmux, circ, last_searched_direction); } cmux->n_cells -= hashent->muxinfo.cell_count; + /* Free policy-specific data if we have it */ + if (hashent->muxinfo.policy_data) { + /* If we have policy data, assert that we have the means to free it */ + tor_assert(cmux->policy); + tor_assert(cmux->policy_data); + tor_assert(cmux->policy->free_circ_data); + /* Call free_circ_data() */ + cmux->policy->free_circ_data(cmux, + cmux->policy_data, + circ, + hashent->muxinfo.policy_data); + hashent->muxinfo.policy_data = NULL; + } + /* Consistency check: the direction must match the direction searched */ tor_assert(last_searched_direction == hashent->muxinfo.direction); /* Clear the circuit's mux for this direction */ -- cgit v1.2.3-54-g00ecf From eade7a37cdc9c128d66d2c2c74d7056516fde364 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Thu, 27 Sep 2012 12:20:05 -0700 Subject: Add cmux policy notify_set_n_cells() and notify_xmit_cells() callbacks --- src/or/circuitmux.c | 13 +++++++++++++ src/or/circuitmux.h | 11 +++++++++++ 2 files changed, 24 insertions(+) (limited to 'src') diff --git a/src/or/circuitmux.c b/src/or/circuitmux.c index 5a5739fcd9..53d3a1ba7a 100644 --- a/src/or/circuitmux.c +++ b/src/or/circuitmux.c @@ -995,6 +995,19 @@ circuitmux_set_num_cells(circuitmux_t *cmux, circuit_t *circ, cmux->n_cells -= hashent->muxinfo.cell_count; cmux->n_cells += n_cells; + /* Do we need to notify a cmux policy? */ + if (cmux->policy && cmux->policy_data && + cmux->policy->notify_set_n_cells) { + /* Yeah; assert that we have circuit policy data */ + tor_assert(hashent->muxinfo.policy_data); + /* ... and call notify_set_n_cells */ + cmux->policy->notify_set_n_cells(cmux, + cmux->policy_data, + circ, + hashent->muxinfo.policy_data, + n_cells); + } + /* * Update cmux active circuit counter: is the old cell count > 0 and the * new cell count == 0 ? diff --git a/src/or/circuitmux.h b/src/or/circuitmux.h index e344b6ed91..dcacb8e15d 100644 --- a/src/or/circuitmux.h +++ b/src/or/circuitmux.h @@ -42,6 +42,17 @@ struct circuitmux_policy_s { circuitmux_policy_data_t *pol_data, circuit_t *circ, circuitmux_policy_circ_data_t *pol_circ_data); + /* Notify of arriving/transmitted cells on a circuit */ + void (*notify_set_n_cells)(circuitmux_t *cmux, + circuitmux_policy_data_t *pol_data, + circuit_t *circ, + circuitmux_policy_circ_data_t *pol_circ_data, + unsigned int n_cells); + void (*notify_xmit_cells)(circuitmux_t *cmux, + circuitmux_policy_data_t *pol_data, + circuit_t *circ, + circuitmux_policy_circ_data_t *pol_circ_data, + unsigned int n_cells); }; /* -- cgit v1.2.3-54-g00ecf From 86d9d85dfc177b7a24051f64a5c35997de5c9ac9 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Thu, 27 Sep 2012 13:42:47 -0700 Subject: Implement circuitmux_clear_policy(), circuitmux_get_policy() and circuitmux_set_policy() in circuitmux.c --- src/or/circuitmux.c | 159 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 158 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/or/circuitmux.c b/src/or/circuitmux.c index 53d3a1ba7a..4e335692ac 100644 --- a/src/or/circuitmux.c +++ b/src/or/circuitmux.c @@ -343,7 +343,13 @@ circuitmux_free(circuitmux_t *cmux) tor_assert(cmux->n_circuits == 0); tor_assert(cmux->n_active_circuits == 0); - /* Free policy-specific data if we have any */ + /* + * Free policy-specific data if we have any; we don't + * need to do circuitmux_set_policy(cmux, NULL) to cover + * the circuits because they would have been handled in + * circuitmux_detach_all_circuits() before this was + * called. + */ if (cmux->policy && cmux->policy->free_cmux_data) { if (cmux->policy_data) { cmux->policy->free_cmux_data(cmux, cmux->policy_data); @@ -359,6 +365,157 @@ circuitmux_free(circuitmux_t *cmux) tor_free(cmux); } +/* + * Circuitmux policy control functions + */ + +/** + * Remove any policy installed on cmux; all policy data will be freed and + * cmux behavior will revert to the built-in round-robin active_circuits + * mechanism. + */ + +void +circuitmux_clear_policy(circuitmux_t *cmux) +{ + tor_assert(cmux); + + /* Internally, this is just setting policy to NULL */ + if (cmux->policy) { + circuitmux_set_policy(cmux, NULL); + } +} + +/** + * Return the policy currently installed on a circuitmux_t + */ + +const circuitmux_policy_t * +circuitmux_get_policy(circuitmux_t *cmux) +{ + tor_assert(cmux); + + return cmux->policy; +} + +/** + * Set policy; allocate for new policy, detach all circuits from old policy + * if any, attach them to new policy, and free old policy data. + */ + +void +circuitmux_set_policy(circuitmux_t *cmux, + const circuitmux_policy_t *pol) +{ + const circuitmux_policy_t *old_pol = NULL, *new_pol = NULL; + circuitmux_policy_data_t *old_pol_data = NULL, *new_pol_data = NULL; + chanid_circid_muxinfo_t **i = NULL; + channel_t *chan = NULL; + uint64_t last_chan_id_searched = 0; + circuit_t *circ = NULL; + + tor_assert(cmux); + + /* Set up variables */ + old_pol = cmux->policy; + old_pol_data = cmux->policy_data; + new_pol = pol; + + /* Check if this is the trivial case */ + if (old_pol == new_pol) return; + + /* Allocate data for new policy, if any */ + if (new_pol && new_pol->alloc_cmux_data) { + /* + * If alloc_cmux_data is not null, then we expect to get some policy + * data. Assert that we also have free_cmux_data so we can free it + * when the time comes, and allocate it. + */ + tor_assert(new_pol->free_cmux_data); + new_pol_data = new_pol->alloc_cmux_data(cmux); + tor_assert(new_pol_data); + } + + /* Install new policy and new policy data on cmux */ + cmux->policy = new_pol; + cmux->policy_data = new_pol_data; + + /* Iterate over all circuits, attaching/detaching each one */ + i = HT_START(chanid_circid_muxinfo_map, cmux->chanid_circid_map); + while (i) { + /* Assert that this entry isn't NULL */ + tor_assert(*i); + + /* + * Get the channel; since normal case is all circuits on the mux share a + * channel, we cache last_chan_id_searched + */ + if (!chan || last_chan_id_searched != (*i)->chan_id) { + chan = channel_find_by_global_id((*i)->chan_id); + last_chan_id_searched = (*i)->chan_id; + } + tor_assert(chan); + + /* Get the circuit */ + circ = circuit_get_by_circid_channel((*i)->circ_id, chan); + tor_assert(circ); + + /* Need to tell old policy it becomes inactive (i.e., it is active) ? */ + if (old_pol && old_pol->notify_circ_inactive && + (*i)->muxinfo.cell_count > 0) { + old_pol->notify_circ_inactive(cmux, old_pol_data, circ, + (*i)->muxinfo.policy_data); + } + + /* Need to free old policy data? */ + if ((*i)->muxinfo.policy_data) { + /* Assert that we have the means to free it if we have policy data */ + tor_assert(old_pol); + tor_assert(old_pol->free_circ_data); + /* Free it */ + old_pol->free_circ_data(cmux, old_pol_data, circ, + (*i)->muxinfo.policy_data); + (*i)->muxinfo.policy_data = NULL; + } + + /* Need to allocate new policy data? */ + if (new_pol && new_pol->alloc_circ_data) { + /* + * If alloc_circ_data is not null, we expect to get some per-circuit + * policy data. Assert that we also have free_circ_data so we can + * free it when the time comes, and allocate it. + */ + tor_assert(new_pol->free_circ_data); + (*i)->muxinfo.policy_data = + new_pol->alloc_circ_data(cmux, new_pol_data, circ, + (*i)->muxinfo.direction, + (*i)->muxinfo.cell_count); + } + + /* Need to make active on new policy? */ + if (new_pol && new_pol->notify_circ_active && + (*i)->muxinfo.cell_count > 0) { + new_pol->notify_circ_active(cmux, new_pol_data, circ, + (*i)->muxinfo.policy_data); + } + + /* Advance to next circuit map entry */ + i = HT_NEXT(chanid_circid_muxinfo_map, cmux->chanid_circid_map, i); + } + + /* Free data for old policy, if any */ + if (old_pol_data) { + /* + * If we had old policy data, we should have an old policy and a free + * function for it. + */ + tor_assert(old_pol); + tor_assert(old_pol->free_cmux_data); + old_pol->free_cmux_data(cmux, old_pol_data); + old_pol_data = NULL; + } +} + /* * Circuitmux/circuit attachment status inquiry functions */ -- cgit v1.2.3-54-g00ecf From debef8f0cd8fbe72fb40bd811c636bb66c7a9001 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Thu, 27 Sep 2012 13:51:04 -0700 Subject: Consistently allow policy alloc_cmux_data() and alloc_circ_data() functions to return NULL if the policy does not use this in circuitmux.c --- src/or/circuitmux.c | 35 ++++++++++++++--------------------- 1 file changed, 14 insertions(+), 21 deletions(-) (limited to 'src') diff --git a/src/or/circuitmux.c b/src/or/circuitmux.c index 4e335692ac..b5dda9e11f 100644 --- a/src/or/circuitmux.c +++ b/src/or/circuitmux.c @@ -293,7 +293,6 @@ circuitmux_detach_all_circuits(circuitmux_t *cmux) * free it */ tor_assert(cmux->policy); - tor_assert(cmux->policy_data); tor_assert(cmux->policy->free_circ_data); /* Call free_circ_data() */ cmux->policy->free_circ_data(cmux, @@ -805,8 +804,10 @@ circuitmux_attach_circuit(circuitmux_t *cmux, circuit_t *circ, hashent->muxinfo.cell_count = cell_count; hashent->muxinfo.direction = direction; /* Allocate policy specific circuit data if we need it */ - if (cmux->policy && cmux->policy_data && - cmux->policy->alloc_circ_data) { + if (cmux->policy && cmux->policy->alloc_circ_data) { + /* Assert that we have the means to free policy-specific data */ + tor_assert(cmux->policy->free_circ_data); + /* Allocate it */ hashent->muxinfo.policy_data = cmux->policy->alloc_circ_data(cmux, cmux->policy_data, @@ -898,7 +899,6 @@ circuitmux_detach_circuit(circuitmux_t *cmux, circuit_t *circ) if (hashent->muxinfo.policy_data) { /* If we have policy data, assert that we have the means to free it */ tor_assert(cmux->policy); - tor_assert(cmux->policy_data); tor_assert(cmux->policy->free_circ_data); /* Call free_circ_data() */ cmux->policy->free_circ_data(cmux, @@ -997,16 +997,14 @@ circuitmux_make_circuit_active(circuitmux_t *cmux, circuit_t *circ, /* Policy-specific notification */ if (cmux->policy && - cmux->policy->notify_circ_active && - cmux->policy_data) { + cmux->policy->notify_circ_active) { /* Okay, we need to check the circuit for policy data now */ hashent = circuitmux_find_map_entry(cmux, circ); /* We should have found something */ tor_assert(hashent); - /* Check for policy data for the circuit and notify */ - if (hashent->muxinfo.policy_data) - cmux->policy->notify_circ_active(cmux, cmux->policy_data, - circ, hashent->muxinfo.policy_data); + /* Notify */ + cmux->policy->notify_circ_active(cmux, cmux->policy_data, + circ, hashent->muxinfo.policy_data); } } @@ -1106,16 +1104,14 @@ circuitmux_make_circuit_inactive(circuitmux_t *cmux, circuit_t *circ, /* Policy-specific notification */ if (cmux->policy && - cmux->policy->notify_circ_inactive && - cmux->policy_data) { + cmux->policy->notify_circ_inactive) { /* Okay, we need to check the circuit for policy data now */ hashent = circuitmux_find_map_entry(cmux, circ); /* We should have found something */ tor_assert(hashent); - /* Check for policy data for the circuit and notify */ - if (hashent->muxinfo.policy_data) - cmux->policy->notify_circ_inactive(cmux, cmux->policy_data, - circ, hashent->muxinfo.policy_data); + /* Notify */ + cmux->policy->notify_circ_inactive(cmux, cmux->policy_data, + circ, hashent->muxinfo.policy_data); } } @@ -1153,11 +1149,8 @@ circuitmux_set_num_cells(circuitmux_t *cmux, circuit_t *circ, cmux->n_cells += n_cells; /* Do we need to notify a cmux policy? */ - if (cmux->policy && cmux->policy_data && - cmux->policy->notify_set_n_cells) { - /* Yeah; assert that we have circuit policy data */ - tor_assert(hashent->muxinfo.policy_data); - /* ... and call notify_set_n_cells */ + if (cmux->policy && cmux->policy->notify_set_n_cells) { + /* Call notify_set_n_cells */ cmux->policy->notify_set_n_cells(cmux, cmux->policy_data, circ, -- cgit v1.2.3-54-g00ecf From aff77eb1a9dcbdf723d3f8e74e6502e5047bbe63 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Thu, 27 Sep 2012 15:04:46 -0700 Subject: Implement circuitmux_notify_xmit_cells() and circuitmux_move_active_circ_to_tail() helper in circuitmux.c --- src/or/circuitmux.c | 133 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) (limited to 'src') diff --git a/src/or/circuitmux.c b/src/or/circuitmux.c index b5dda9e11f..deb5bf12a6 100644 --- a/src/or/circuitmux.c +++ b/src/or/circuitmux.c @@ -141,6 +141,9 @@ circuitmux_make_circuit_active(circuitmux_t *cmux, circuit_t *circ, static void circuitmux_make_circuit_inactive(circuitmux_t *cmux, circuit_t *circ, cell_direction_t direction); +static INLINE void +circuitmux_move_active_circ_to_tail(circuitmux_t *cmux, circuit_t *circ, + cell_direction_t direction); static INLINE circuit_t ** circuitmux_next_active_circ_p(circuitmux_t *cmux, circuit_t *circ); static INLINE circuit_t ** @@ -152,6 +155,72 @@ circuitmux_prev_active_circ_p(circuitmux_t *cmux, circuit_t *circ); * Linked list helpers */ +static INLINE void +circuitmux_move_active_circ_to_tail(circuitmux_t *cmux, circuit_t *circ, + cell_direction_t direction) +{ + circuit_t **next_p = NULL, **prev_p = NULL; + circuit_t **next_prev = NULL, **prev_next = NULL; + or_circuit_t *or_circ = NULL; + + tor_assert(cmux); + tor_assert(circ); + + /* Figure out our next_p and prev_p for this cmux/direction */ + if (direction) { + if (direction == CELL_DIRECTION_OUT) { + tor_assert(circ->n_mux == cmux); + next_p = &(circ->next_active_on_n_chan); + prev_p = &(circ->prev_active_on_n_chan); + } else { + or_circ = TO_OR_CIRCUIT(circ); + tor_assert(or_circ->p_mux == cmux); + next_p = &(or_circ->next_active_on_p_chan); + prev_p = &(or_circ->prev_active_on_p_chan); + } + } else { + if (circ->n_mux == cmux) { + next_p = &(circ->next_active_on_n_chan); + prev_p = &(circ->prev_active_on_n_chan); + direction = CELL_DIRECTION_OUT; + } else { + or_circ = TO_OR_CIRCUIT(circ); + tor_assert(or_circ->p_mux == cmux); + next_p = &(or_circ->next_active_on_p_chan); + prev_p = &(or_circ->prev_active_on_p_chan); + direction = CELL_DIRECTION_IN; + } + } + tor_assert(next_p); + tor_assert(prev_p); + + /* Check if this really is an active circuit */ + if ((*next_p == NULL && *prev_p == NULL) && + !(circ == cmux->active_circuits_head || + circ == cmux->active_circuits_tail)) { + /* Not active, no-op */ + return; + } + + /* Check if this is already the tail */ + if (circ == cmux->active_circuits_tail) return; + + /* Okay, we have to move it; figure out next_prev and prev_next */ + if (*next_p) next_prev = circuitmux_prev_active_circ_p(cmux, *next_p); + if (*prev_p) prev_next = circuitmux_next_active_circ_p(cmux, *prev_p); + /* Adjust the previous node's next pointer, if any */ + if (prev_next) *prev_next = *next_p; + /* Otherwise, we were the head */ + else cmux->active_circuits_head = *next_p; + /* Adjust the next node's previous pointer, if any */ + if (next_prev) *next_prev = *prev_p; + /* Adjust our next and prev pointers */ + *next_p = NULL; + *prev_p = cmux->active_circuits_tail; + /* Set the tail to this circuit */ + cmux->active_circuits_tail = circ; +} + static INLINE circuit_t ** circuitmux_next_active_circ_p(circuitmux_t *cmux, circuit_t *circ) { @@ -1175,3 +1244,67 @@ circuitmux_set_num_cells(circuitmux_t *cmux, circuit_t *circ, hashent->muxinfo.cell_count = n_cells; } +/* + * Functions for channel code to call to get a circuit to transmit from or + * notify that cells have been transmitted. + */ + +void +circuitmux_notify_xmit_cells(circuitmux_t *cmux, circuit_t *circ, + unsigned int n_cells) +{ + chanid_circid_muxinfo_t *hashent = NULL; + int becomes_inactive = 0; + + tor_assert(cmux); + tor_assert(circ); + + if (n_cells == 0) return; + + /* + * To handle this, we have to: + * + * 1.) Adjust the circuit's cell counter in the cmux hash table + * 2.) Move the circuit to the tail of the active_circuits linked list + * for this cmux, or make the circuit inactive if the cell count + * went to zero. + * 3.) Call cmux->policy->notify_xmit_cells(), if any + */ + + /* Find the hash entry */ + hashent = circuitmux_find_map_entry(cmux, circ); + /* Assert that we found one */ + tor_assert(hashent); + + /* Adjust the cell counter and assert that we had that many cells to send */ + tor_assert(n_cells <= hashent->muxinfo.cell_count); + hashent->muxinfo.cell_count -= n_cells; + /* Do we need to make the circuit inactive? */ + if (hashent->muxinfo.cell_count == 0) becomes_inactive = 1; + + /* If we aren't making it inactive later, move it to the tail of the list */ + if (!becomes_inactive) { + circuitmux_move_active_circ_to_tail(cmux, circ, + hashent->muxinfo.direction); + } + + /* + * We call notify_xmit_cells() before making the circuit inactive if needed, + * so the policy can always count on this coming in on an active circuit. + */ + if (cmux->policy && cmux->policy->notify_xmit_cells) { + cmux->policy->notify_xmit_cells(cmux, cmux->policy_data, circ, + hashent->muxinfo.policy_data, + n_cells); + } + + /* + * Now make the circuit inactive if needed; this will call the policy's + * notify_circ_inactive() if present. + */ + if (becomes_inactive) { + --(cmux->n_active_circuits); + circuitmux_make_circuit_inactive(cmux, circ, hashent->muxinfo.direction); + } +} + -- cgit v1.2.3-54-g00ecf From 741bc97b2414eb8ecfe0367bb75aea376e9ce73b Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Thu, 27 Sep 2012 15:09:37 -0700 Subject: Add comment for circuitmux_move_active_circ_to_tail() in circuitmux.c --- src/or/circuitmux.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src') diff --git a/src/or/circuitmux.c b/src/or/circuitmux.c index deb5bf12a6..c01f38f599 100644 --- a/src/or/circuitmux.c +++ b/src/or/circuitmux.c @@ -155,6 +155,11 @@ circuitmux_prev_active_circ_p(circuitmux_t *cmux, circuit_t *circ); * Linked list helpers */ +/** + * Move an active circuit to the tail of the cmux's active circuits list; + * used by circuitmux_notify_xmit_cells(). + */ + static INLINE void circuitmux_move_active_circ_to_tail(circuitmux_t *cmux, circuit_t *circ, cell_direction_t direction) -- cgit v1.2.3-54-g00ecf From c4e5e4727ddcf639a3a50856f74f24f373262baf Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Thu, 27 Sep 2012 15:17:48 -0700 Subject: Add comment for circuitmux_notify_xmit_cells() in circuitmux.c --- src/or/circuitmux.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src') diff --git a/src/or/circuitmux.c b/src/or/circuitmux.c index c01f38f599..824bf0b0ca 100644 --- a/src/or/circuitmux.c +++ b/src/or/circuitmux.c @@ -1254,6 +1254,11 @@ circuitmux_set_num_cells(circuitmux_t *cmux, circuit_t *circ, * notify that cells have been transmitted. */ +/** + * Notify the circuitmux that cells have been sent on a circuit; this + * is called from channel.c. + */ + void circuitmux_notify_xmit_cells(circuitmux_t *cmux, circuit_t *circ, unsigned int n_cells) -- cgit v1.2.3-54-g00ecf From 4a14c6aa67df6ced083e2d6a6e66dbf443a40e75 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Fri, 28 Sep 2012 16:56:46 -0700 Subject: Implement circuitmux_get_first_active_circuit() in circuitmux.c; add pick_active_circuit() to circuitmux_policy_t in circuitmux.h --- src/or/circuitmux.c | 32 ++++++++++++++++++++++++++++++-- src/or/circuitmux.h | 3 +++ 2 files changed, 33 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/or/circuitmux.c b/src/or/circuitmux.c index 824bf0b0ca..4500c6ab21 100644 --- a/src/or/circuitmux.c +++ b/src/or/circuitmux.c @@ -359,7 +359,6 @@ circuitmux_detach_all_circuits(circuitmux_t *cmux) U64_PRINTF_ARG(to_remove->chan_id)); } - /* Free policy-specific data if we have it */ if (to_remove->muxinfo.policy_data) { /* @@ -1254,6 +1253,35 @@ circuitmux_set_num_cells(circuitmux_t *cmux, circuit_t *circ, * notify that cells have been transmitted. */ +/** + * Pick a circuit to send from, using the active circuits list or a + * circuitmux policy if one is available. This is called from channel.c. + */ + +circuit_t * +circuitmux_get_first_active_circuit(circuitmux_t *cmux) +{ + circuit_t *circ = NULL; + + tor_assert(cmux); + + if (cmux->n_active_circuits > 0) { + /* We also must have a cell available for this to be the case */ + tor_assert(cmux->n_cells > 0); + /* Do we have a policy-provided circuit selector? */ + if (cmux->policy && cmux->policy->pick_active_circuit) { + circ = cmux->policy->pick_active_circuit(cmux, cmux->policy_data); + } + /* Fall back on the head of the active circuits list */ + if (!circ) { + tor_assert(cmux->active_circuits_head); + circ = cmux->active_circuits_head; + } + } else tor_assert(cmux->n_cells == 0); + + return circ; +} + /** * Notify the circuitmux that cells have been sent on a circuit; this * is called from channel.c. @@ -1310,7 +1338,7 @@ circuitmux_notify_xmit_cells(circuitmux_t *cmux, circuit_t *circ, /* * Now make the circuit inactive if needed; this will call the policy's - * notify_circ_inactive() if present. + * notify_circ_inactive() if present. */ if (becomes_inactive) { --(cmux->n_active_circuits); diff --git a/src/or/circuitmux.h b/src/or/circuitmux.h index dcacb8e15d..f4eb73e391 100644 --- a/src/or/circuitmux.h +++ b/src/or/circuitmux.h @@ -53,6 +53,9 @@ struct circuitmux_policy_s { circuit_t *circ, circuitmux_policy_circ_data_t *pol_circ_data, unsigned int n_cells); + /* Choose a circuit */ + circuit_t * (*pick_active_circuit)(circuitmux_t *cmux, + circuitmux_policy_data_t *pol_data); }; /* -- cgit v1.2.3-54-g00ecf From 34591c61efcdd241e0c89006eeb9ae4cdf7565c0 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Sun, 30 Sep 2012 21:24:04 -0700 Subject: Implement circuitmux_assert_okay() and helper functions in circuitmux.c --- src/or/circuitmux.c | 286 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 286 insertions(+) (limited to 'src') diff --git a/src/or/circuitmux.c b/src/or/circuitmux.c index 4500c6ab21..b16eb63acc 100644 --- a/src/or/circuitmux.c +++ b/src/or/circuitmux.c @@ -110,6 +110,8 @@ struct circuit_muxinfo_s { cell_direction_t direction; /* Policy-specific data */ circuitmux_policy_circ_data_t *policy_data; + /* Mark bit for consistency checker */ + unsigned int mark:1; }; /* @@ -148,6 +150,9 @@ static INLINE circuit_t ** circuitmux_next_active_circ_p(circuitmux_t *cmux, circuit_t *circ); static INLINE circuit_t ** circuitmux_prev_active_circ_p(circuitmux_t *cmux, circuit_t *circ); +static void circuitmux_assert_okay_pass_one(circuitmux_t *cmux); +static void circuitmux_assert_okay_pass_two(circuitmux_t *cmux); +static void circuitmux_assert_okay_pass_three(circuitmux_t *cmux); /* Function definitions */ @@ -1346,3 +1351,284 @@ circuitmux_notify_xmit_cells(circuitmux_t *cmux, circuit_t *circ, } } +/* + * Circuitmux consistency checking assertions + */ + +/** + * Check that circuitmux data structures are consistent and fail with an + * assert if not. + */ + +void +circuitmux_assert_okay(circuitmux_t *cmux) +{ + tor_assert(cmux); + + /* + * Pass 1: iterate the hash table; for each entry: + * a) Check that the circuit has this cmux for n_mux or p_mux + * b) If the cell_count is > 0, set the mark bit; otherwise clear it + * c) Also check activeness (cell_count > 0 should be active) + * d) Count the number of circuits, active circuits and queued cells + * and at the end check that they match the counters in the cmux. + * + * Pass 2: iterate the active circuits list; for each entry, + * make sure the circuit is attached to this mux and appears + * in the hash table. Make sure the mark bit is 1, and clear + * it in the hash table entry. Consistency-check the linked + * list pointers. + * + * Pass 3: iterate the hash table again; assert if any active circuits + * (mark bit set to 1) are discovered that weren't cleared in pass 2 + * (don't appear in the linked list). + */ + + circuitmux_assert_okay_pass_one(cmux); + circuitmux_assert_okay_pass_two(cmux); + circuitmux_assert_okay_pass_three(cmux); +} + +/** + * Do the first pass of circuitmux_assert_okay(); see the comment in that + * function. + */ + +static void +circuitmux_assert_okay_pass_one(circuitmux_t *cmux) +{ + chanid_circid_muxinfo_t **i = NULL; + uint64_t chan_id; + channel_t *chan; + circid_t circ_id; + circuit_t *circ; + or_circuit_t *or_circ; + unsigned int circ_is_active; + circuit_t **next_p, **prev_p; + unsigned int n_circuits, n_active_circuits, n_cells; + + tor_assert(cmux); + tor_assert(cmux->chanid_circid_map); + + /* Reset the counters */ + n_circuits = n_active_circuits = n_cells = 0; + /* Start iterating the hash table */ + i = HT_START(chanid_circid_muxinfo_map, cmux->chanid_circid_map); + while (i) { + /* Assert that the hash table entry isn't null */ + tor_assert(*i); + + /* Get the channel and circuit id */ + chan_id = (*i)->chan_id; + circ_id = (*i)->circ_id; + + /* Find the channel and circuit, assert that they exist */ + chan = channel_find_by_global_id(chan_id); + tor_assert(chan); + circ = circuit_get_by_circid_channel(circ_id, chan); + tor_assert(circ); + + /* Clear the circ_is_active bit to start */ + circ_is_active = 0; + + /* Assert that we know which direction this is going */ + tor_assert((*i)->muxinfo.direction == CELL_DIRECTION_OUT || + (*i)->muxinfo.direction == CELL_DIRECTION_IN); + + if ((*i)->muxinfo.direction == CELL_DIRECTION_OUT) { + /* We should be n_mux on this circuit */ + tor_assert(cmux == circ->n_mux); + tor_assert(chan == circ->n_chan); + /* Get next and prev for next test */ + next_p = &(circ->next_active_on_n_chan); + prev_p = &(circ->prev_active_on_n_chan); + } else { + /* This should be an or_circuit_t and we should be p_mux */ + or_circ = TO_OR_CIRCUIT(circ); + tor_assert(cmux == or_circ->p_mux); + tor_assert(chan == or_circ->p_chan); + /* Get next and prev for next test */ + next_p = &(or_circ->next_active_on_p_chan); + prev_p = &(or_circ->prev_active_on_p_chan); + } + + /* + * Should this circuit be active? I.e., does the mux know about > 0 + * cells on it? + */ + circ_is_active = ((*i)->muxinfo.cell_count > 0); + + /* It should be in the linked list iff it's active */ + if (circ_is_active) { + /* Either we have a next link or we are the tail */ + tor_assert(*next_p || (circ == cmux->active_circuits_tail)); + /* Either we have a prev link or we are the head */ + tor_assert(*prev_p || (circ == cmux->active_circuits_head)); + /* Increment the active circuits counter */ + ++n_active_circuits; + } else { + /* Shouldn't be in list, so no next or prev link */ + tor_assert(!(*next_p)); + tor_assert(!(*prev_p)); + /* And can't be head or tail */ + tor_assert(circ != cmux->active_circuits_head); + tor_assert(circ != cmux->active_circuits_tail); + } + + /* Increment the circuits counter */ + ++n_circuits; + /* Adjust the cell counter */ + n_cells += (*i)->muxinfo.cell_count; + + /* Set the mark bit to circ_is_active */ + (*i)->muxinfo.mark = circ_is_active; + + /* Advance to the next entry */ + i = HT_NEXT(chanid_circid_muxinfo_map, cmux->chanid_circid_map, i); + } + + /* Now check the counters */ + tor_assert(n_cells == cmux->n_cells); + tor_assert(n_circuits == cmux->n_circuits); + tor_assert(n_active_circuits == cmux->n_active_circuits); +} + +/** + * Do the second pass of circuitmux_assert_okay(); see the comment in that + * function. + */ + +static void +circuitmux_assert_okay_pass_two(circuitmux_t *cmux) +{ + circuit_t *curr_circ, *prev_circ = NULL, *next_circ; + or_circuit_t *curr_or_circ; + uint64_t curr_chan_id; + circid_t curr_circ_id; + circuit_t **next_p, **prev_p; + channel_t *chan; + unsigned int n_active_circuits = 0; + cell_direction_t direction; + chanid_circid_muxinfo_t search, *hashent = NULL; + + tor_assert(cmux); + tor_assert(cmux->chanid_circid_map); + + /* + * Walk the linked list of active circuits in cmux; keep track of the + * previous circuit seen for consistency checking purposes. Count them + * to make sure the number in the linked list matches + * cmux->n_active_circuits. + */ + curr_circ = cmux->active_circuits_head; + while (curr_circ) { + /* Reset some things */ + chan = NULL; + curr_or_circ = NULL; + next_circ = NULL; + next_p = prev_p = NULL; + direction = 0; + + /* Figure out if this is n_mux or p_mux */ + if (cmux == curr_circ->n_mux) { + /* Get next_p and prev_p */ + next_p = &(curr_circ->next_active_on_n_chan); + prev_p = &(curr_circ->prev_active_on_n_chan); + /* Get the channel */ + chan = curr_circ->n_chan; + /* Get the circuit id */ + curr_circ_id = curr_circ->n_circ_id; + /* Remember the direction */ + direction = CELL_DIRECTION_OUT; + } else { + /* We must be p_mux and this must be an or_circuit_t */ + curr_or_circ = TO_OR_CIRCUIT(curr_circ); + tor_assert(cmux == curr_or_circ->p_mux); + /* Get next_p and prev_p */ + next_p = &(curr_or_circ->next_active_on_p_chan); + prev_p = &(curr_or_circ->prev_active_on_p_chan); + /* Get the channel */ + chan = curr_or_circ->p_chan; + /* Get the circuit id */ + curr_circ_id = curr_or_circ->p_circ_id; + /* Remember the direction */ + direction = CELL_DIRECTION_IN; + } + + /* Assert that we got a channel and get the channel ID */ + tor_assert(chan); + curr_chan_id = chan->global_identifier; + + /* Assert that prev_p points to last circuit we saw */ + tor_assert(*prev_p == prev_circ); + /* If that's NULL, assert that we are the head */ + if (!(*prev_p)) tor_assert(curr_circ == cmux->active_circuits_head); + + /* Get the next circuit */ + next_circ = *next_p; + /* If it's NULL, assert that we are the tail */ + if (!(*next_p)) tor_assert(curr_circ == cmux->active_circuits_tail); + + /* Now find the hash table entry for this circuit */ + search.chan_id = curr_chan_id; + search.circ_id = curr_circ_id; + hashent = HT_FIND(chanid_circid_muxinfo_map, cmux->chanid_circid_map, + &search); + + /* Assert that we have one */ + tor_assert(hashent); + + /* Assert that the direction matches */ + tor_assert(direction == hashent->muxinfo.direction); + + /* Assert that the hash entry got marked in pass one */ + tor_assert(hashent->muxinfo.mark); + + /* Clear the mark */ + hashent->muxinfo.mark = 0; + + /* Increment the counter */ + ++n_active_circuits; + + /* Advance to the next active circuit and update prev_circ */ + prev_circ = curr_circ; + curr_circ = next_circ; + } + + /* Assert that the counter matches the cmux */ + tor_assert(n_active_circuits == cmux->n_active_circuits); +} + +/** + * Do the third pass of circuitmux_assert_okay(); see the comment in that + * function. + */ + +static void +circuitmux_assert_okay_pass_three(circuitmux_t *cmux) +{ + chanid_circid_muxinfo_t **i = NULL; + + tor_assert(cmux); + tor_assert(cmux->chanid_circid_map); + + /* Start iterating the hash table */ + i = HT_START(chanid_circid_muxinfo_map, cmux->chanid_circid_map); + + /* Advance through each entry */ + while (i) { + /* Assert that it isn't null */ + tor_assert(*i); + + /* + * Assert that this entry is not marked - i.e., that either we didn't + * think it should be active in pass one or we saw it in the active + * circuits linked list. + */ + tor_assert(!((*i)->muxinfo.mark)); + + /* Advance to the next entry */ + i = HT_NEXT(chanid_circid_muxinfo_map, cmux->chanid_circid_map, i); + } +} + -- cgit v1.2.3-54-g00ecf From 184560c4f6fa8262fe1316d5e169a16b47b11e6b Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Sun, 30 Sep 2012 21:41:25 -0700 Subject: Add initial circuitmux_ewma.{c,h} --- src/or/Makefile.nmake | 16 ++++++++-------- src/or/circuitmux_ewma.c | 12 ++++++++++++ src/or/circuitmux_ewma.h | 18 ++++++++++++++++++ src/or/include.am | 2 ++ 4 files changed, 40 insertions(+), 8 deletions(-) create mode 100644 src/or/circuitmux_ewma.c create mode 100644 src/or/circuitmux_ewma.h (limited to 'src') diff --git a/src/or/Makefile.nmake b/src/or/Makefile.nmake index 9f948d661f..677618e74f 100644 --- a/src/or/Makefile.nmake +++ b/src/or/Makefile.nmake @@ -9,14 +9,14 @@ LIBS = ..\..\..\build-alpha\lib\libevent.a \ ws2_32.lib advapi32.lib shell32.lib LIBTOR_OBJECTS = buffers.obj channel.obj channeltls.obj circuitbuild.obj \ - circuitlist.obj circuitmux.obj circuituse.obj command.obj config.obj \ - connection.obj connection_edge.obj connection_or.obj control.obj \ - cpuworker.obj directory.obj dirserv.obj dirvote.obj dns.obj \ - dnsserv.obj geoip.obj hibernate.obj main.obj microdesc.obj \ - networkstatus.obj nodelist.obj onion.obj policies.obj reasons.obj \ - relay.obj rendclient.obj rendcommon.obj rendmid.obj rendservice.obj \ - rephist.obj router.obj routerlist.obj routerparse.obj status.obj \ - config_codedigest.obj ntmain.obj + circuitlist.obj circuitmux.obj circuitmux_ewma.obj circuituse.obj \ + command.obj config.obj connection.obj connection_edge.obj \ + connection_or.obj control.obj cpuworker.obj directory.obj \ + dirserv.obj dirvote.obj dns.obj dnsserv.obj geoip.obj hibernate.obj \ + main.obj microdesc.obj networkstatus.obj nodelist.obj onion.obj \ + policies.obj reasons.obj relay.obj rendclient.obj rendcommon.obj \ + rendmid.obj rendservice.obj rephist.obj router.obj routerlist.obj \ + routerparse.obj status.obj config_codedigest.obj ntmain.obj libtor.lib: $(LIBTOR_OBJECTS) lib $(LIBTOR_OBJECTS) /out:libtor.lib diff --git a/src/or/circuitmux_ewma.c b/src/or/circuitmux_ewma.c new file mode 100644 index 0000000000..b31e908b06 --- /dev/null +++ b/src/or/circuitmux_ewma.c @@ -0,0 +1,12 @@ +/* * Copyright (c) 2012, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file circuitmux_ewma.c + * \brief EWMA circuit selection as a circuitmux_t policy + **/ + +#include "or.h" +#include "circuitmux.h" +#include "circuitmux_ewma.h" + diff --git a/src/or/circuitmux_ewma.h b/src/or/circuitmux_ewma.h new file mode 100644 index 0000000000..fcbe29836f --- /dev/null +++ b/src/or/circuitmux_ewma.h @@ -0,0 +1,18 @@ +/* * Copyright (c) 2012, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file circuitmux_ewma.h + * \brief Header file for circuitmux_ewma.c + **/ + +#ifndef _TOR_CIRCUITMUX_EWMA_H +#define _TOR_CIRCUITMUX_EWMA_H + +#include "or.h" +#include "circuitmux.h" + +/* TODO */ + +#endif /* _TOR_CIRCUITMUX_EWMA_H */ + diff --git a/src/or/include.am b/src/or/include.am index 3ec94e5be3..90dea44ba8 100644 --- a/src/or/include.am +++ b/src/or/include.am @@ -22,6 +22,7 @@ src_or_libtor_a_SOURCES = \ src/or/circuitbuild.c \ src/or/circuitlist.c \ src/or/circuitmux.c \ + src/or/circuitmux_ewma.c \ src/or/circuituse.c \ src/or/command.c \ src/or/config.c \ @@ -94,6 +95,7 @@ ORHEADERS = \ src/or/circuitbuild.h \ src/or/circuitlist.h \ src/or/circuitmux.h \ + src/or/circuitmux_ewma.h \ src/or/circuituse.h \ src/or/command.h \ src/or/config.h \ -- cgit v1.2.3-54-g00ecf From e1de2f1437ffe67ee0ae74ae6ac1d432f3981542 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Mon, 1 Oct 2012 01:48:52 -0700 Subject: Add upcasts for circuitmux_policy_data_t and circuitmux_policy_circ_data_t to circuitmux.h --- src/or/circuitmux.h | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'src') diff --git a/src/or/circuitmux.h b/src/or/circuitmux.h index f4eb73e391..a3eacaf7d4 100644 --- a/src/or/circuitmux.h +++ b/src/or/circuitmux.h @@ -76,6 +76,23 @@ struct circuitmux_policy_circ_data_s { uint32_t magic; }; +/* + * Upcast #defines for the above types + */ + +/** + * Convert a circuitmux_policy_data_t subtype to a circuitmux_policy_data_t. + */ + +#define TO_CMUX_POL_DATA(x) (&((x)->_base)) + +/** + * Convert a circuitmux_policy_circ_data_t subtype to a + * circuitmux_policy_circ_data_t. + */ + +#define TO_CMUX_POL_CIRC_DATA(x) (&((x)->_base)) + /* Consistency check */ void circuitmux_assert_okay(circuitmux_t *cmux); -- cgit v1.2.3-54-g00ecf From b7d5784c542f180c0fe7dc6ba999415cdbef3abe Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Mon, 1 Oct 2012 01:50:05 -0700 Subject: Remove ewma setup code in init_circuit_base()/or_circuit_new() of circuitlist.c; it gets allocated when the circuit is attached to a circuitmux_t with that policy now --- src/or/circuitlist.c | 16 ---------------- 1 file changed, 16 deletions(-) (limited to 'src') diff --git a/src/or/circuitlist.c b/src/or/circuitlist.c index bec3dc8175..cfef0aa55d 100644 --- a/src/or/circuitlist.c +++ b/src/or/circuitlist.c @@ -546,12 +546,6 @@ init_circuit_base(circuit_t *circ) circ->package_window = circuit_initial_package_window(); circ->deliver_window = CIRCWINDOW_START; - /* Initialize the cell_ewma_t structure */ - circ->n_cell_ewma.last_adjusted_tick = cell_ewma_get_tick(); - circ->n_cell_ewma.cell_count = 0.0; - circ->n_cell_ewma.heap_index = -1; - circ->n_cell_ewma.is_for_p_chan = 0; - circuit_add(circ); } @@ -599,16 +593,6 @@ or_circuit_new(circid_t p_circ_id, channel_t *p_chan) init_circuit_base(TO_CIRCUIT(circ)); - /* Initialize the cell_ewma_t structure */ - - /* Initialize the cell counts to 0 */ - circ->p_cell_ewma.cell_count = 0.0; - circ->p_cell_ewma.last_adjusted_tick = cell_ewma_get_tick(); - circ->p_cell_ewma.is_for_p_chan = 1; - - /* It's not in any heap yet. */ - circ->p_cell_ewma.heap_index = -1; - return circ; } -- cgit v1.2.3-54-g00ecf From 14fe0d585916f4d8fdba64b419778c47ffd64b16 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Mon, 1 Oct 2012 01:51:31 -0700 Subject: Remove EWMA code from relay.{c,h}; it goes to a circuitmux policy now --- src/or/relay.c | 332 --------------------------------------------------------- src/or/relay.h | 3 - 2 files changed, 335 deletions(-) (limited to 'src') diff --git a/src/or/relay.c b/src/or/relay.c index e3b383ba76..d0341625f7 100644 --- a/src/or/relay.c +++ b/src/or/relay.c @@ -10,7 +10,6 @@ * receiving from circuits, plus queuing on circuits. **/ -#include #define RELAY_PRIVATE #include "or.h" #include "buffers.h" @@ -1972,245 +1971,6 @@ cell_queue_pop(cell_queue_t *queue) return cell; } -#if 0 -/** Helper for sorting cell_ewma_t values in their priority queue. */ -static int -compare_cell_ewma_counts(const void *p1, const void *p2) -{ - const cell_ewma_t *e1=p1, *e2=p2; - if (e1->cell_count < e2->cell_count) - return -1; - else if (e1->cell_count > e2->cell_count) - return 1; - else - return 0; -} - -/** Given a cell_ewma_t, return a pointer to the circuit containing it. */ -static circuit_t * -cell_ewma_to_circuit(cell_ewma_t *ewma) -{ - if (ewma->is_for_p_chan) { - /* This is an or_circuit_t's p_cell_ewma. */ - or_circuit_t *orcirc = SUBTYPE_P(ewma, or_circuit_t, p_cell_ewma); - return TO_CIRCUIT(orcirc); - } else { - /* This is some circuit's n_cell_ewma. */ - return SUBTYPE_P(ewma, circuit_t, n_cell_ewma); - } -} - -/* ==== Functions for scaling cell_ewma_t ==== - - When choosing which cells to relay first, we favor circuits that have been - quiet recently. This gives better latency on connections that aren't - pushing lots of data, and makes the network feel more interactive. - - Conceptually, we take an exponentially weighted mean average of the number - of cells a circuit has sent, and allow active circuits (those with cells to - relay) to send cells in reverse order of their exponentially-weighted mean - average (EWMA) cell count. [That is, a cell sent N seconds ago 'counts' - F^N times as much as a cell sent now, for 0now, compute the cell_ewma tick in which it occurs - * and the fraction of the tick that has elapsed between the start of the tick - * and now. Return the former and store the latter in - * *remainder_out. - * - * These tick values are not meant to be shared between Tor instances, or used - * for other purposes. */ -static unsigned -cell_ewma_tick_from_timeval(const struct timeval *now, - double *remainder_out) -{ - unsigned res = (unsigned) (now->tv_sec / EWMA_TICK_LEN); - /* rem */ - double rem = (now->tv_sec % EWMA_TICK_LEN) + - ((double)(now->tv_usec)) / 1.0e6; - *remainder_out = rem / EWMA_TICK_LEN; - return res; -} - -/** Compute and return the current cell_ewma tick. */ -unsigned -cell_ewma_get_tick(void) -{ - return ((unsigned)approx_time() / EWMA_TICK_LEN); -} - -/** The per-tick scale factor to be used when computing cell-count EWMA - * values. (A cell sent N ticks before the start of the current tick - * has value ewma_scale_factor ** N.) - */ -static double ewma_scale_factor = 0.1; -/* DOCDOC ewma_enabled */ -static int ewma_enabled = 0; - -/*DOCDOC*/ -#define EPSILON 0.00001 -/*DOCDOC*/ -#define LOG_ONEHALF -0.69314718055994529 - -/** Adjust the global cell scale factor based on options */ -void -cell_ewma_set_scale_factor(const or_options_t *options, - const networkstatus_t *consensus) -{ - int32_t halflife_ms; - double halflife; - const char *source; - if (options && options->CircuitPriorityHalflife >= -EPSILON) { - halflife = options->CircuitPriorityHalflife; - source = "CircuitPriorityHalflife in configuration"; - } else if (consensus && (halflife_ms = networkstatus_get_param( - consensus, "CircuitPriorityHalflifeMsec", - -1, -1, INT32_MAX)) >= 0) { - halflife = ((double)halflife_ms)/1000.0; - source = "CircuitPriorityHalflifeMsec in consensus"; - } else { - halflife = EWMA_DEFAULT_HALFLIFE; - source = "Default value"; - } - - if (halflife <= EPSILON) { - /* The cell EWMA algorithm is disabled. */ - ewma_scale_factor = 0.1; - ewma_enabled = 0; - log_info(LD_OR, - "Disabled cell_ewma algorithm because of value in %s", - source); - } else { - /* convert halflife into halflife-per-tick. */ - halflife /= EWMA_TICK_LEN; - /* compute per-tick scale factor. */ - ewma_scale_factor = exp( LOG_ONEHALF / halflife ); - ewma_enabled = 1; - log_info(LD_OR, - "Enabled cell_ewma algorithm because of value in %s; " - "scale factor is %f per %d seconds", - source, ewma_scale_factor, EWMA_TICK_LEN); - } -} - -/** Return the multiplier necessary to convert the value of a cell sent in - * 'from_tick' to one sent in 'to_tick'. */ -static INLINE double -get_scale_factor(unsigned from_tick, unsigned to_tick) -{ - /* This math can wrap around, but that's okay: unsigned overflow is - well-defined */ - int diff = (int)(to_tick - from_tick); - return pow(ewma_scale_factor, diff); -} - -/** Adjust the cell count of ewma so that it is scaled with respect to - * cur_tick */ -static void -scale_single_cell_ewma(cell_ewma_t *ewma, unsigned cur_tick) -{ - double factor = get_scale_factor(ewma->last_adjusted_tick, cur_tick); - ewma->cell_count *= factor; - ewma->last_adjusted_tick = cur_tick; -} - -/** Adjust the cell count of every active circuit on chan so - * that they are scaled with respect to cur_tick */ -static void -scale_active_circuits(channel_t *chan, unsigned cur_tick) -{ - double factor; - - tor_assert(chan); - - factor = - get_scale_factor( - chan->active_circuit_pqueue_last_recalibrated, - cur_tick); - /** Ordinarily it isn't okay to change the value of an element in a heap, - * but it's okay here, since we are preserving the order. */ - SMARTLIST_FOREACH_BEGIN( - chan->active_circuit_pqueue, - cell_ewma_t *, e) { - tor_assert(e->last_adjusted_tick == - chan->active_circuit_pqueue_last_recalibrated); - e->cell_count *= factor; - e->last_adjusted_tick = cur_tick; - } SMARTLIST_FOREACH_END(e); - chan->active_circuit_pqueue_last_recalibrated = cur_tick; -} - -/** Rescale ewma to the same scale as chan, and add it to - * chan's priority queue of active circuits */ -static void -add_cell_ewma_to_chan(channel_t *chan, cell_ewma_t *ewma) -{ - tor_assert(chan); - tor_assert(ewma); - tor_assert(ewma->heap_index == -1); - - scale_single_cell_ewma( - ewma, - chan->active_circuit_pqueue_last_recalibrated); - - smartlist_pqueue_add(chan->active_circuit_pqueue, - compare_cell_ewma_counts, - STRUCT_OFFSET(cell_ewma_t, heap_index), - ewma); -} - -/** Remove ewma from chan's priority queue of active circuits */ -static void -remove_cell_ewma_from_chan(channel_t *chan, cell_ewma_t *ewma) -{ - tor_assert(chan); - tor_assert(ewma); - tor_assert(ewma->heap_index != -1); - - smartlist_pqueue_remove(chan->active_circuit_pqueue, - compare_cell_ewma_counts, - STRUCT_OFFSET(cell_ewma_t, heap_index), - ewma); -} - -/** Remove and return the first cell_ewma_t from chan's priority queue of - * active circuits. Requires that the priority queue is nonempty. */ -static cell_ewma_t * -pop_first_cell_ewma_from_chan(channel_t *chan) -{ - tor_assert(chan); - - return smartlist_pqueue_pop(chan->active_circuit_pqueue, - compare_cell_ewma_counts, - STRUCT_OFFSET(cell_ewma_t, heap_index)); -} -#endif - /** * Update the number of cells available on the circuit's n_chan or p_chan's * circuit mux. @@ -2335,15 +2095,6 @@ channel_flush_from_first_active_circuit(channel_t *chan, int max) int streams_blocked; packed_cell_t *cell; -#if 0 - /* The current (hi-res) time */ - struct timeval now_hires; - - /* The EWMA cell counter for the circuit we're flushing. */ - cell_ewma_t *cell_ewma = NULL; - double ewma_increment = -1; -#endif - /* Get the cmux */ tor_assert(chan); tor_assert(chan->cmux); @@ -2356,26 +2107,6 @@ channel_flush_from_first_active_circuit(channel_t *chan, int max) if (!circ) break; assert_cmux_ok_paranoid(chan); -#if 0 - /* This will go in circuitmux_get_first_active_circuit() */ - /* See if we're doing the ewma circuit selection algorithm. */ - if (ewma_enabled) { - unsigned tick; - double fractional_tick; - tor_gettimeofday_cached(&now_hires); - tick = cell_ewma_tick_from_timeval(&now_hires, &fractional_tick); - - if (tick != chan->active_circuit_pqueue_last_recalibrated) { - scale_active_circuits(chan, tick); - } - - ewma_increment = pow(ewma_scale_factor, -fractional_tick); - - cell_ewma = smartlist_get(chan->active_circuit_pqueue, 0); - circ = cell_ewma_to_circuit(cell_ewma); - } -#endif - if (circ->n_chan == chan) { queue = &circ->n_chan_cells; streams_blocked = circ->streams_blocked_on_n_chan; @@ -2460,28 +2191,6 @@ channel_flush_from_first_active_circuit(channel_t *chan, int max) if (streams_blocked && queue->n <= CELL_QUEUE_LOWWATER_SIZE) set_streams_blocked_on_circ(circ, chan, 0, 0); /* unblock streams */ -#if 0 - if (cell_ewma) { - cell_ewma_t *tmp; - cell_ewma->cell_count += ewma_increment; - /* We pop and re-add the cell_ewma_t here, not above, since we need to - * re-add it immediately to keep the priority queue consistent with - * the linked-list implementation */ - tmp = pop_first_cell_ewma_from_chan(chan); - tor_assert(tmp == cell_ewma); - add_cell_ewma_to_chan(chan, cell_ewma); - } - if (!ewma_enabled && circ != chan->active_circuits) { - /* If this happens, the current circuit just got made inactive by - * a call in connection_write_to_buf(). That's nothing to worry about: - * circuit_make_inactive_on_conn() already advanced chan->active_circuits - * for us. - */ - assert_active_circuits_ok_paranoid(chan); - goto done; - } -#endif - /* If n_flushed < max still, loop around and pick another circuit */ } @@ -2636,47 +2345,6 @@ assert_circuit_mux_okay(channel_t *chan) circuitmux_assert_okay(chan->cmux); } -#if 0 -/** Fail with an assert if the active circuits ring on orconn is - * corrupt. */ -void -assert_active_circuits_ok(channel_t *chan) -{ - circuit_t *head = NULL, *cur = NULL; - int n = 0; - - tor_assert(chan); - - cur = head = chan->active_circuits; - - if (! head) - return; - do { - circuit_t *next = *next_circ_on_chan_p(cur, chan); - circuit_t *prev = *prev_circ_on_chan_p(cur, chan); - cell_ewma_t *ewma; - tor_assert(next); - tor_assert(prev); - tor_assert(*next_circ_on_chan_p(prev, chan) == cur); - tor_assert(*prev_circ_on_chan_p(next, chan) == cur); - if (chan == cur->n_chan) { - ewma = &cur->n_cell_ewma; - tor_assert(!ewma->is_for_p_chan); - } else { - ewma = &TO_OR_CIRCUIT(cur)->p_cell_ewma; - tor_assert(ewma->is_for_p_chan); - } - tor_assert(ewma->heap_index != -1); - tor_assert(ewma == smartlist_get(chan->active_circuit_pqueue, - ewma->heap_index)); - n++; - cur = next; - } while (cur != head); - - tor_assert(n == smartlist_len(chan->active_circuit_pqueue)); -} -#endif - /** Return 1 if we shouldn't restart reading on this circuit, even if * we get a SENDME. Else return 0. */ diff --git a/src/or/relay.h b/src/or/relay.h index ef5074bbe4..5759c51801 100644 --- a/src/or/relay.h +++ b/src/or/relay.h @@ -60,9 +60,6 @@ int append_address_to_payload(uint8_t *payload_out, const tor_addr_t *addr); const uint8_t *decode_address_from_payload(tor_addr_t *addr_out, const uint8_t *payload, int payload_len); -unsigned cell_ewma_get_tick(void); -void cell_ewma_set_scale_factor(const or_options_t *options, - const networkstatus_t *consensus); void circuit_clear_cell_queue(circuit_t *circ, channel_t *chan); #ifdef RELAY_PRIVATE -- cgit v1.2.3-54-g00ecf From 5f9d37e74ec269be8d496e8e0d4157875ff1fe59 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Mon, 1 Oct 2012 01:52:39 -0700 Subject: Add circuitmux.h, circuitmux_ewma.h includes now necessary in config.c --- src/or/config.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/or/config.c b/src/or/config.c index bac2b2e642..babbfb0a9c 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -14,6 +14,8 @@ #include "or.h" #include "circuitbuild.h" #include "circuitlist.h" +#include "circuitmux.h" +#include "circuitmux_ewma.h" #include "config.h" #include "connection.h" #include "connection_edge.h" -- cgit v1.2.3-54-g00ecf From d194b8602ae0bcf6ffe711903b110193bdbcbab4 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Mon, 1 Oct 2012 01:53:24 -0700 Subject: Remove cell_ewma_t from or.h and from circuit_t/or_circuit_t; all that goes to the new circuitmux_ewma.c now --- src/or/or.h | 36 ------------------------------------ 1 file changed, 36 deletions(-) (limited to 'src') diff --git a/src/or/or.h b/src/or/or.h index a0fcf8f490..dad81eddc6 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -2581,29 +2581,6 @@ typedef struct { time_t expiry_time; } cpath_build_state_t; -/** - * The cell_ewma_t structure keeps track of how many cells a circuit has - * transferred recently. It keeps an EWMA (exponentially weighted moving - * average) of the number of cells flushed from the circuit queue onto a - * connection in connection_or_flush_from_first_active_circuit(). - */ -typedef struct { - /** The last 'tick' at which we recalibrated cell_count. - * - * A cell sent at exactly the start of this tick has weight 1.0. Cells sent - * since the start of this tick have weight greater than 1.0; ones sent - * earlier have less weight. */ - unsigned last_adjusted_tick; - /** The EWMA of the cell count. */ - double cell_count; - /** True iff this is the cell count for a circuit's previous - * channel. */ - unsigned int is_for_p_chan : 1; - /** The position of the circuit within the OR connection's priority - * queue. */ - int heap_index; -} cell_ewma_t; - #define ORIGIN_CIRCUIT_MAGIC 0x35315243u #define OR_CIRCUIT_MAGIC 0x98ABC04Fu @@ -2721,12 +2698,8 @@ typedef struct circuit_t { /** Unique ID for measuring tunneled network status requests. */ uint64_t dirreq_id; - /** TODO is this *all* circuits or all circuits on n_chan? */ struct circuit_t *next; /**< Next circuit in linked list of all circuits. */ - /** TODO all this from here on down should go away in favor of - * circuitmux_t. - */ /** Next circuit in the doubly-linked ring of circuits waiting to add * cells to n_conn. NULL if we have no cells pending, or if we're not * linked to an OR connection. */ @@ -2735,11 +2708,6 @@ typedef struct circuit_t { * cells to n_conn. NULL if we have no cells pending, or if we're not * linked to an OR connection. */ struct circuit_t *prev_active_on_n_chan; - - /** The EWMA count for the number of cells flushed from the - * n_chan_cells queue. Used to determine which circuit to flush from next. - */ - cell_ewma_t n_cell_ewma; } circuit_t; /** Largest number of relay_early cells that we can send on a given @@ -2977,10 +2945,6 @@ typedef struct or_circuit_t { * exit-ward queues of this circuit; reset every time when writing * buffer stats to disk. */ uint64_t total_cell_waiting_time; - - /** The EWMA count for the number of cells flushed from the - * p_conn_cells queue. */ - cell_ewma_t p_cell_ewma; } or_circuit_t; /** Convert a circuit subtype to a circuit_t. */ -- cgit v1.2.3-54-g00ecf From 0af2eab9a2698e6a50aec4238fc529f152b77a89 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Mon, 1 Oct 2012 01:53:59 -0700 Subject: Add circuitmux.h, circuitmux_ewma.h includes now necessary in networkstatus.c --- src/or/networkstatus.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c index 0cc6a21085..619099b826 100644 --- a/src/or/networkstatus.c +++ b/src/or/networkstatus.c @@ -12,6 +12,8 @@ #include "or.h" #include "circuitbuild.h" +#include "circuitmux.h" +#include "circuitmux_ewma.h" #include "config.h" #include "connection.h" #include "connection_or.h" -- cgit v1.2.3-54-g00ecf From bbb2c31d26f2eb1ad46433bcfd321d960a6c7862 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Mon, 1 Oct 2012 01:54:42 -0700 Subject: Move all EWMA code into circuitmux.c and implement circuitmux_policy_t methods using it --- src/or/circuitmux_ewma.c | 666 +++++++++++++++++++++++++++++++++++++++++++++++ src/or/circuitmux_ewma.h | 12 +- 2 files changed, 677 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/or/circuitmux_ewma.c b/src/or/circuitmux_ewma.c index b31e908b06..6d4fcc98cf 100644 --- a/src/or/circuitmux_ewma.c +++ b/src/or/circuitmux_ewma.c @@ -6,7 +6,673 @@ * \brief EWMA circuit selection as a circuitmux_t policy **/ +#define _TOR_CIRCUITMUX_EWMA_C + +#include + #include "or.h" #include "circuitmux.h" #include "circuitmux_ewma.h" +#include "networkstatus.h" + +/*** EWMA parameter #defines ***/ + +/** How long does a tick last (seconds)? */ +#define EWMA_TICK_LEN 10 + +/** The default per-tick scale factor, if it hasn't been overridden by a + * consensus or a configuration setting. zero means "disabled". */ +#define EWMA_DEFAULT_HALFLIFE 0.0 + +/*** Some useful constant #defines ***/ + +/*DOCDOC*/ +#define EPSILON 0.00001 +/*DOCDOC*/ +#define LOG_ONEHALF -0.69314718055994529 + +/*** EWMA structures ***/ + +typedef struct cell_ewma_s cell_ewma_t; +typedef struct ewma_policy_data_s ewma_policy_data_t; +typedef struct ewma_policy_circ_data_s ewma_policy_circ_data_t; + +/** + * The cell_ewma_t structure keeps track of how many cells a circuit has + * transferred recently. It keeps an EWMA (exponentially weighted moving + * average) of the number of cells flushed from the circuit queue onto a + * connection in channel_flush_from_first_active_circuit(). + */ + +struct cell_ewma_s { + /** The last 'tick' at which we recalibrated cell_count. + * + * A cell sent at exactly the start of this tick has weight 1.0. Cells sent + * since the start of this tick have weight greater than 1.0; ones sent + * earlier have less weight. */ + unsigned int last_adjusted_tick; + /** The EWMA of the cell count. */ + double cell_count; + /** True iff this is the cell count for a circuit's previous + * channel. */ + unsigned int is_for_p_chan : 1; + /** The position of the circuit within the OR connection's priority + * queue. */ + int heap_index; +}; + +struct ewma_policy_data_s { + circuitmux_policy_data_t _base; + + /** + * Priority queue of cell_ewma_t for circuits with queued cells waiting + * for room to free up on the channel that owns this circuitmux. Kept + * in heap order according to EWMA. This was formerly in channel_t, and + * in or_connection_t before that. + */ + smartlist_t *active_circuit_pqueue; + + /** + * The tick on which the cell_ewma_ts in active_circuit_pqueue last had + * their ewma values rescaled. This was formerly in channel_t, and in + * or_connection_t before that. + */ + unsigned int active_circuit_pqueue_last_recalibrated; +}; + +struct ewma_policy_circ_data_s { + circuitmux_policy_circ_data_t _base; + + /** + * The EWMA count for the number of cells flushed from this circuit + * onto this circuitmux. Used to determine which circuit to flush + * from next. This was formerly in circuit_t and or_circuit_t. + */ + cell_ewma_t cell_ewma; + + /** + * Pointer back to the circuit_t this is for; since we're separating + * out circuit selection policy like this, we can't attach cell_ewma_t + * to the circuit_t any more, so we can't use SUBTYPE_P directly to a + * circuit_t like before; instead get it here. + */ + circuit_t *circ; +}; + +#define EWMA_POL_DATA_MAGIC 0x2fd8b16aU +#define EWMA_POL_CIRC_DATA_MAGIC 0x761e7747U + +/*** Downcasts for the above types ***/ + +static ewma_policy_data_t * +TO_EWMA_POL_DATA(circuitmux_policy_data_t *); + +static ewma_policy_circ_data_t * +TO_EWMA_POL_CIRC_DATA(circuitmux_policy_circ_data_t *); + +/** + * Downcast a circuitmux_policy_data_t to an ewma_policy_data_t and assert + * if the cast is impossible. + */ + +static INLINE ewma_policy_data_t * +TO_EWMA_POL_DATA(circuitmux_policy_data_t *pol) +{ + if (!pol) return NULL; + else { + tor_assert(pol->magic == EWMA_POL_DATA_MAGIC); + return DOWNCAST(ewma_policy_data_t, pol); + } +} + +/** + * Downcast a circuitmux_policy_circ_data_t to an ewma_policy_circ_data_t + * and assert if the cast is impossible. + */ + +static INLINE ewma_policy_circ_data_t * +TO_EWMA_POL_CIRC_DATA(circuitmux_policy_circ_data_t *pol) +{ + if (!pol) return NULL; + else { + tor_assert(pol->magic == EWMA_POL_CIRC_DATA_MAGIC); + return DOWNCAST(ewma_policy_circ_data_t, pol); + } +} + +/*** Static declarations for circuitmux_ewma.c ***/ + +static void add_cell_ewma(ewma_policy_data_t *pol, cell_ewma_t *ewma); +static int compare_cell_ewma_counts(const void *p1, const void *p2); +static unsigned cell_ewma_tick_from_timeval(const struct timeval *now, + double *remainder_out); +static circuit_t * cell_ewma_to_circuit(cell_ewma_t *ewma); +static INLINE double get_scale_factor(unsigned from_tick, unsigned to_tick); +static cell_ewma_t * pop_first_cell_ewma(ewma_policy_data_t *pol); +static void remove_cell_ewma(ewma_policy_data_t *pol, cell_ewma_t *ewma); +static void scale_single_cell_ewma(cell_ewma_t *ewma, unsigned cur_tick); +static void scale_active_circuits(ewma_policy_data_t *pol, + unsigned cur_tick); + +/*** Circuitmux policy methods ***/ + +static circuitmux_policy_data_t * ewma_alloc_cmux_data(circuitmux_t *cmux); +static void ewma_free_cmux_data(circuitmux_t *cmux, + circuitmux_policy_data_t *pol_data); +static circuitmux_policy_circ_data_t * +ewma_alloc_circ_data(circuitmux_t *cmux, circuitmux_policy_data_t *pol_data, + circuit_t *circ, cell_direction_t direction, + unsigned int cell_count); +static void +ewma_free_circ_data(circuitmux_t *cmux, + circuitmux_policy_data_t *pol_data, + circuit_t *circ, + circuitmux_policy_circ_data_t *pol_circ_data); +static void +ewma_notify_circ_active(circuitmux_t *cmux, + circuitmux_policy_data_t *pol_data, + circuit_t *circ, + circuitmux_policy_circ_data_t *pol_circ_data); +static void +ewma_notify_circ_inactive(circuitmux_t *cmux, + circuitmux_policy_data_t *pol_data, + circuit_t *circ, + circuitmux_policy_circ_data_t *pol_circ_data); +static void +ewma_notify_xmit_cells(circuitmux_t *cmux, + circuitmux_policy_data_t *pol_data, + circuit_t *circ, + circuitmux_policy_circ_data_t *pol_circ_data, + unsigned int n_cells); +static circuit_t * +ewma_pick_active_circuit(circuitmux_t *cmux, + circuitmux_policy_data_t *pol_data); + +/*** EWMA global variables ***/ + +/** The per-tick scale factor to be used when computing cell-count EWMA + * values. (A cell sent N ticks before the start of the current tick + * has value ewma_scale_factor ** N.) + */ +static double ewma_scale_factor = 0.1; +/* DOCDOC ewma_enabled */ +static int ewma_enabled = 0; + +/*** EWMA circuitmux_policy_t method table ***/ + +circuitmux_policy_t ewma_policy = { .alloc_cmux_data = ewma_alloc_cmux_data, + .free_cmux_data = ewma_free_cmux_data, + .alloc_circ_data = ewma_alloc_circ_data, + .free_circ_data = ewma_free_circ_data, + .notify_circ_active = ewma_notify_circ_active, + .notify_circ_inactive = ewma_notify_circ_inactive, + .notify_set_n_cells = NULL, /* EWMA doesn't need this */ + .notify_xmit_cells = ewma_notify_xmit_cells, + .pick_active_circuit = ewma_pick_active_circuit +}; + +/*** EWMA method implementations using the below EWMA helper functions ***/ + +/** + * Allocate an ewma_policy_data_t and upcast it to a circuitmux_policy_data_t; + * this is called when setting the policy on a circuitmux_t to ewma_policy. + */ + +static circuitmux_policy_data_t * +ewma_alloc_cmux_data(circuitmux_t *cmux) +{ + ewma_policy_data_t *pol = NULL; + + tor_assert(cmux); + + pol = tor_malloc_zero(sizeof(*pol)); + pol->active_circuit_pqueue = smartlist_new(); + pol->active_circuit_pqueue_last_recalibrated = cell_ewma_get_tick(); + + return TO_CMUX_POL_DATA(pol); +} + +/** + * Free an ewma_policy_data_t allocated with ewma_alloc_cmux_data() + */ + +static void +ewma_free_cmux_data(circuitmux_t *cmux, + circuitmux_policy_data_t *pol_data) +{ + ewma_policy_data_t *pol = NULL; + + tor_assert(cmux); + if (!pol_data) return; + + pol = TO_EWMA_POL_DATA(pol_data); + + smartlist_free(pol->active_circuit_pqueue); + tor_free(pol); +} + +/** + * Allocate an ewma_policy_circ_data_t and upcast it to a + * circuitmux_policy_data_t; this is called when attaching a circuit to a + * circuitmux_t with ewma_policy. + */ + +static circuitmux_policy_circ_data_t * +ewma_alloc_circ_data(circuitmux_t *cmux, + circuitmux_policy_data_t *pol_data, + circuit_t *circ, + cell_direction_t direction, + unsigned int cell_count) +{ + ewma_policy_data_t *pol = NULL; + ewma_policy_circ_data_t *cdata = NULL; + + tor_assert(cmux); + tor_assert(pol_data); + tor_assert(circ); + tor_assert(direction == CELL_DIRECTION_OUT || + direction == CELL_DIRECTION_IN); + /* Shut the compiler up */ + tor_assert(cell_count == cell_count); + + pol = TO_EWMA_POL_DATA(pol_data); + + cdata = tor_malloc_zero(sizeof(*cdata)); + cdata->circ = circ; + + /* + * Initialize the cell_ewma_t structure (formerly in + * init_circuit_base()) + */ + cdata->cell_ewma.last_adjusted_tick = cell_ewma_get_tick(); + cdata->cell_ewma.cell_count = 0.0; + cdata->cell_ewma.heap_index = -1; + if (direction == CELL_DIRECTION_IN) { + cdata->cell_ewma.is_for_p_chan = 1; + } else { + cdata->cell_ewma.is_for_p_chan = 0; + } + + return TO_CMUX_POL_CIRC_DATA(cdata); +} + +/** + * Free an ewma_policy_circ_data_t allocated with ewma_alloc_circ_data() + */ + +static void +ewma_free_circ_data(circuitmux_t *cmux, + circuitmux_policy_data_t *pol_data, + circuit_t *circ, + circuitmux_policy_circ_data_t *pol_circ_data) + +{ + ewma_policy_data_t *pol = NULL; + ewma_policy_circ_data_t *cdata = NULL; + + tor_assert(cmux); + tor_assert(circ); + tor_assert(pol_data); + pol = TO_EWMA_POL_DATA(pol_data); + + if (!pol_circ_data) return; + + cdata = TO_EWMA_POL_CIRC_DATA(pol_circ_data); + + tor_free(cdata); +} + +/** + * Handle circuit activation; this inserts the circuit's cell_ewma into + * the active_circuits_pqueue. + */ + +static void +ewma_notify_circ_active(circuitmux_t *cmux, + circuitmux_policy_data_t *pol_data, + circuit_t *circ, + circuitmux_policy_circ_data_t *pol_circ_data) +{ + ewma_policy_data_t *pol = NULL; + ewma_policy_circ_data_t *cdata = NULL; + + tor_assert(cmux); + tor_assert(pol_data); + tor_assert(circ); + tor_assert(pol_circ_data); + + pol = TO_EWMA_POL_DATA(pol_data); + cdata = TO_EWMA_POL_CIRC_DATA(pol_circ_data); + + add_cell_ewma(pol, &(cdata->cell_ewma)); +} + +/** + * Handle circuit deactivation; this removes the circuit's cell_ewma from + * the active_circuits_pqueue. + */ + +static void +ewma_notify_circ_inactive(circuitmux_t *cmux, + circuitmux_policy_data_t *pol_data, + circuit_t *circ, + circuitmux_policy_circ_data_t *pol_circ_data) +{ + ewma_policy_data_t *pol = NULL; + ewma_policy_circ_data_t *cdata = NULL; + + tor_assert(cmux); + tor_assert(pol_data); + tor_assert(circ); + tor_assert(pol_circ_data); + + pol = TO_EWMA_POL_DATA(pol_data); + cdata = TO_EWMA_POL_CIRC_DATA(pol_circ_data); + + remove_cell_ewma(pol, &(cdata->cell_ewma)); +} + +/** + * Update cell_ewma for this circuit after we've sent some cells, and + * remove/reinsert it in the queue. This used to be done (brokenly, + * see bug 6816) in channel_flush_from_first_active_circuit(). + */ + +static void +ewma_notify_xmit_cells(circuitmux_t *cmux, + circuitmux_policy_data_t *pol_data, + circuit_t *circ, + circuitmux_policy_circ_data_t *pol_circ_data, + unsigned int n_cells) +{ + ewma_policy_data_t *pol = NULL; + ewma_policy_circ_data_t *cdata = NULL; + unsigned int tick; + double fractional_tick, ewma_increment; + /* The current (hi-res) time */ + struct timeval now_hires; + cell_ewma_t *cell_ewma, *tmp; + + tor_assert(cmux); + tor_assert(pol_data); + tor_assert(circ); + tor_assert(pol_circ_data); + tor_assert(n_cells > 0); + + pol = TO_EWMA_POL_DATA(pol_data); + cdata = TO_EWMA_POL_CIRC_DATA(pol_circ_data); + + /* Rescale the EWMAs if needed */ + tor_gettimeofday_cached(&now_hires); + tick = cell_ewma_tick_from_timeval(&now_hires, &fractional_tick); + + if (tick != pol->active_circuit_pqueue_last_recalibrated) { + scale_active_circuits(pol, tick); + } + + /* How much do we adjust the cell count in cell_ewma by? */ + ewma_increment = + ((double)(n_cells)) * pow(ewma_scale_factor, -fractional_tick); + + /* Do the adjustment */ + cell_ewma = &(cdata->cell_ewma); + cell_ewma->cell_count += ewma_increment; + + /* + * Since we just sent on this circuit, it should be at the head of + * the queue. Pop the head, assert that it matches, then re-add. + */ + tmp = pop_first_cell_ewma(pol); + tor_assert(tmp == cell_ewma); + add_cell_ewma(pol, cell_ewma); +} + +/** + * Pick the preferred circuit to send from; this will be the one with + * the lowest EWMA value in the priority queue. This used to be done + * in channel_flush_from_first_active_circuit(). + */ + +static circuit_t * +ewma_pick_active_circuit(circuitmux_t *cmux, + circuitmux_policy_data_t *pol_data) +{ + ewma_policy_data_t *pol = NULL; + circuit_t *circ = NULL; + cell_ewma_t *cell_ewma = NULL; + + tor_assert(cmux); + tor_assert(pol_data); + + pol = TO_EWMA_POL_DATA(pol_data); + + if (smartlist_len(pol->active_circuit_pqueue) > 0) { + /* Get the head of the queue */ + cell_ewma = smartlist_get(pol->active_circuit_pqueue, 0); + circ = cell_ewma_to_circuit(cell_ewma); + } + + return circ; +} + +/** Helper for sorting cell_ewma_t values in their priority queue. */ +static int +compare_cell_ewma_counts(const void *p1, const void *p2) +{ + const cell_ewma_t *e1 = p1, *e2 = p2; + + if (e1->cell_count < e2->cell_count) + return -1; + else if (e1->cell_count > e2->cell_count) + return 1; + else + return 0; +} + +/** Given a cell_ewma_t, return a pointer to the circuit containing it. */ +static circuit_t * +cell_ewma_to_circuit(cell_ewma_t *ewma) +{ + ewma_policy_circ_data_t *cdata = NULL; + + tor_assert(ewma); + cdata = SUBTYPE_P(ewma, ewma_policy_circ_data_t, cell_ewma); + tor_assert(cdata); + + return cdata->circ; +} + +/* ==== Functions for scaling cell_ewma_t ==== + + When choosing which cells to relay first, we favor circuits that have been + quiet recently. This gives better latency on connections that aren't + pushing lots of data, and makes the network feel more interactive. + + Conceptually, we take an exponentially weighted mean average of the number + of cells a circuit has sent, and allow active circuits (those with cells to + relay) to send cells in reverse order of their exponentially-weighted mean + average (EWMA) cell count. [That is, a cell sent N seconds ago 'counts' + F^N times as much as a cell sent now, for 0now, compute the cell_ewma tick in which it occurs + * and the fraction of the tick that has elapsed between the start of the tick + * and now. Return the former and store the latter in + * *remainder_out. + * + * These tick values are not meant to be shared between Tor instances, or used + * for other purposes. */ +static unsigned +cell_ewma_tick_from_timeval(const struct timeval *now, + double *remainder_out) +{ + unsigned res = (unsigned) (now->tv_sec / EWMA_TICK_LEN); + /* rem */ + double rem = (now->tv_sec % EWMA_TICK_LEN) + + ((double)(now->tv_usec)) / 1.0e6; + *remainder_out = rem / EWMA_TICK_LEN; + return res; +} + +/** Compute and return the current cell_ewma tick. */ +unsigned int +cell_ewma_get_tick(void) +{ + return ((unsigned)approx_time() / EWMA_TICK_LEN); +} + +/** Adjust the global cell scale factor based on options */ +void +cell_ewma_set_scale_factor(const or_options_t *options, + const networkstatus_t *consensus) +{ + int32_t halflife_ms; + double halflife; + const char *source; + if (options && options->CircuitPriorityHalflife >= -EPSILON) { + halflife = options->CircuitPriorityHalflife; + source = "CircuitPriorityHalflife in configuration"; + } else if (consensus && (halflife_ms = networkstatus_get_param( + consensus, "CircuitPriorityHalflifeMsec", + -1, -1, INT32_MAX)) >= 0) { + halflife = ((double)halflife_ms)/1000.0; + source = "CircuitPriorityHalflifeMsec in consensus"; + } else { + halflife = EWMA_DEFAULT_HALFLIFE; + source = "Default value"; + } + + if (halflife <= EPSILON) { + /* The cell EWMA algorithm is disabled. */ + ewma_scale_factor = 0.1; + ewma_enabled = 0; + log_info(LD_OR, + "Disabled cell_ewma algorithm because of value in %s", + source); + } else { + /* convert halflife into halflife-per-tick. */ + halflife /= EWMA_TICK_LEN; + /* compute per-tick scale factor. */ + ewma_scale_factor = exp( LOG_ONEHALF / halflife ); + ewma_enabled = 1; + log_info(LD_OR, + "Enabled cell_ewma algorithm because of value in %s; " + "scale factor is %f per %d seconds", + source, ewma_scale_factor, EWMA_TICK_LEN); + } +} + +/** Return the multiplier necessary to convert the value of a cell sent in + * 'from_tick' to one sent in 'to_tick'. */ +static INLINE double +get_scale_factor(unsigned from_tick, unsigned to_tick) +{ + /* This math can wrap around, but that's okay: unsigned overflow is + well-defined */ + int diff = (int)(to_tick - from_tick); + return pow(ewma_scale_factor, diff); +} + +/** Adjust the cell count of ewma so that it is scaled with respect to + * cur_tick */ +static void +scale_single_cell_ewma(cell_ewma_t *ewma, unsigned cur_tick) +{ + double factor = get_scale_factor(ewma->last_adjusted_tick, cur_tick); + ewma->cell_count *= factor; + ewma->last_adjusted_tick = cur_tick; +} + +/** Adjust the cell count of every active circuit on chan so + * that they are scaled with respect to cur_tick */ +static void +scale_active_circuits(ewma_policy_data_t *pol, unsigned cur_tick) +{ + double factor; + + tor_assert(pol); + tor_assert(pol->active_circuit_pqueue); + + factor = + get_scale_factor( + pol->active_circuit_pqueue_last_recalibrated, + cur_tick); + /** Ordinarily it isn't okay to change the value of an element in a heap, + * but it's okay here, since we are preserving the order. */ + SMARTLIST_FOREACH_BEGIN( + pol->active_circuit_pqueue, + cell_ewma_t *, e) { + tor_assert(e->last_adjusted_tick == + pol->active_circuit_pqueue_last_recalibrated); + e->cell_count *= factor; + e->last_adjusted_tick = cur_tick; + } SMARTLIST_FOREACH_END(e); + pol->active_circuit_pqueue_last_recalibrated = cur_tick; +} + +/** Rescale ewma to the same scale as pol, and add it to + * pol's priority queue of active circuits */ +static void +add_cell_ewma(ewma_policy_data_t *pol, cell_ewma_t *ewma) +{ + tor_assert(pol); + tor_assert(pol->active_circuit_pqueue); + tor_assert(ewma); + tor_assert(ewma->heap_index == -1); + + scale_single_cell_ewma( + ewma, + pol->active_circuit_pqueue_last_recalibrated); + + smartlist_pqueue_add(pol->active_circuit_pqueue, + compare_cell_ewma_counts, + STRUCT_OFFSET(cell_ewma_t, heap_index), + ewma); +} + +/** Remove ewma from pol's priority queue of active circuits */ +static void +remove_cell_ewma(ewma_policy_data_t *pol, cell_ewma_t *ewma) +{ + tor_assert(pol); + tor_assert(pol->active_circuit_pqueue); + tor_assert(ewma); + tor_assert(ewma->heap_index != -1); + + smartlist_pqueue_remove(pol->active_circuit_pqueue, + compare_cell_ewma_counts, + STRUCT_OFFSET(cell_ewma_t, heap_index), + ewma); +} + +/** Remove and return the first cell_ewma_t from pol's priority queue of + * active circuits. Requires that the priority queue is nonempty. */ +static cell_ewma_t * +pop_first_cell_ewma(ewma_policy_data_t *pol) +{ + tor_assert(pol); + tor_assert(pol->active_circuit_pqueue); + + return smartlist_pqueue_pop(pol->active_circuit_pqueue, + compare_cell_ewma_counts, + STRUCT_OFFSET(cell_ewma_t, heap_index)); +} diff --git a/src/or/circuitmux_ewma.h b/src/or/circuitmux_ewma.h index fcbe29836f..0968461c99 100644 --- a/src/or/circuitmux_ewma.h +++ b/src/or/circuitmux_ewma.h @@ -12,7 +12,17 @@ #include "or.h" #include "circuitmux.h" -/* TODO */ +/* Everything but circuitmux_ewma.c should see this extern */ +#ifndef _TOR_CIRCUITMUX_EWMA_C + +extern circuitmux_policy_t ewma_policy; + +#endif /* !(_TOR_CIRCUITMUX_EWMA_C) */ + +/* Externally visible EWMA functions */ +unsigned int cell_ewma_get_tick(void); +void cell_ewma_set_scale_factor(const or_options_t *options, + const networkstatus_t *consensus); #endif /* _TOR_CIRCUITMUX_EWMA_H */ -- cgit v1.2.3-54-g00ecf From 2565710fd72f6f6ddf42e049c0973b848a86719e Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Mon, 1 Oct 2012 01:58:13 -0700 Subject: Fix comment on circuit_t.n_hop and remove circuitmux TODO in or.h --- src/or/or.h | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) (limited to 'src') diff --git a/src/or/or.h b/src/or/or.h index dad81eddc6..96e0299614 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -2634,15 +2634,7 @@ typedef struct circuit_t { /** * The hop to which we want to extend this circuit. Should be NULL if - * the circuit has attached to a connection. - * - * TODO: - * - If this is NULL, we have extended. Is it true that if this is - * NULL then n_chan is not NULL? - * - If n_chan is NULL, then what is n_circ_id? - * - It doesn't matter, because we'll only ever attach to a circuitmux_t - * when n_chan is not NULL, and that's what needs to use a unique ID - * for circuits. + * the circuit has attached to a channel. */ extend_info_t *n_hop; -- cgit v1.2.3-54-g00ecf From 60ec46a2ec6fef36ec1891e89e0d73572eb60bfd Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Mon, 1 Oct 2012 01:59:40 -0700 Subject: Update circuitmux-related TODOs in channeltls.c --- src/or/channeltls.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/or/channeltls.c b/src/or/channeltls.c index 036d14f3e4..bc7e3ec9d3 100644 --- a/src/or/channeltls.c +++ b/src/or/channeltls.c @@ -129,10 +129,7 @@ channel_tls_connect(const tor_addr_t *addr, uint16_t port, channel_mark_outgoing(chan); chan->cmux = circuitmux_alloc(); - /* TODO get rid of this and set policy once we have them - chan->cmux->active_circuit_pqueue_last_recalibrated = - cell_ewma_get_tick(); - */ + /* TODO set cmux policy */ /* Set up or_connection stuff */ tlschan->conn = connection_or_connect(addr, port, id_digest, tlschan); @@ -265,10 +262,7 @@ channel_tls_handle_incoming(or_connection_t *orconn) channel_mark_incoming(chan); chan->cmux = circuitmux_alloc(); - /* TODO set cmux policy - chan->active_circuit_pqueue_last_recalibrated = - cell_ewma_get_tick(); - */ + /* TODO set cmux policy */ /* If we got one, we should register it */ if (chan) channel_register(chan); -- cgit v1.2.3-54-g00ecf From 96a6eff8fe59325e58f0aa7eec7efece36980099 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Mon, 1 Oct 2012 10:39:40 -0700 Subject: Fix circuitmux attach/detach logic in circuit_set_circid_chan_helper(); it's possible for id to be zero (not assigned yet) and shouldn't be attached then --- src/or/circuitlist.c | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/or/circuitlist.c b/src/or/circuitlist.c index cfef0aa55d..a4dcec68a2 100644 --- a/src/or/circuitlist.c +++ b/src/or/circuitlist.c @@ -100,7 +100,7 @@ circuit_set_circid_chan_helper(circuit_t *circ, int direction, chan_circid_circuit_map_t *found; channel_t *old_chan, **chan_ptr; circid_t old_id, *circid_ptr; - int was_active, make_active; + int was_active, make_active, attached = 0; if (direction == CELL_DIRECTION_OUT) { chan_ptr = &circ->n_chan; @@ -128,7 +128,19 @@ circuit_set_circid_chan_helper(circuit_t *circ, int direction, _last_circid_chan_ent = NULL; } - if (old_chan) { /* we may need to remove it from the conn-circid map */ + if (old_chan) { + /* + * If we're changing channels or ID and had an old channel and a non + * zero old ID (i.e., we should have been attached), detach the circuit. + * ID changes require this because circuitmux hashes on (channel_id, + * circuit_id). + */ + if (id != 0 && (old_chan != chan || old_id != id)) { + tor_assert(old_chan->cmux); + circuitmux_detach_circuit(old_chan->cmux, circ); + } + + /* we may need to remove it from the conn-circid map */ search.circ_id = old_id; search.chan = old_chan; found = HT_REMOVE(chan_circid_map, &chan_circid_map, &search); @@ -142,12 +154,6 @@ circuit_set_circid_chan_helper(circuit_t *circ, int direction, --(old_chan->num_p_circuits); } } - - /* If we're changing channels, detach the circuit */ - if (old_chan != chan) { - tor_assert(old_chan->cmux); - circuitmux_detach_circuit(old_chan->cmux, circ); - } } /* Change the values only after we have possibly made the circuit inactive @@ -172,17 +178,21 @@ circuit_set_circid_chan_helper(circuit_t *circ, int direction, HT_INSERT(chan_circid_map, &chan_circid_map, found); } - /* Attach to the circuitmux if we're changing channels */ - if (old_chan != chan) { + /* + * Attach to the circuitmux if we're changing channels or IDs and + * have a new channel and ID to use. + */ + if (chan && id != 0 && (old_chan != chan || old_id != id)) { tor_assert(chan->cmux); circuitmux_attach_circuit(chan->cmux, circ, direction); + attached = 1; } /* * This is a no-op if we have no cells, but if we do it marks us active to * the circuitmux */ - if (make_active && old_chan != chan) + if (make_active && attached) update_circuit_on_cmux(circ, direction); /* Adjust circuit counts on new channel */ -- cgit v1.2.3-54-g00ecf From 7598e669e866f2c1aa61e430c86a06f10dd03cc6 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Mon, 1 Oct 2012 10:58:27 -0700 Subject: Adjust the circuitmux_t counter correctly in circuitmux_notify_xmit_cells() --- src/or/circuitmux.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/or/circuitmux.c b/src/or/circuitmux.c index b16eb63acc..fd598407eb 100644 --- a/src/or/circuitmux.c +++ b/src/or/circuitmux.c @@ -1324,6 +1324,8 @@ circuitmux_notify_xmit_cells(circuitmux_t *cmux, circuit_t *circ, hashent->muxinfo.cell_count -= n_cells; /* Do we need to make the circuit inactive? */ if (hashent->muxinfo.cell_count == 0) becomes_inactive = 1; + /* Adjust the mux cell counter */ + cmux->n_cells -= n_cells; /* If we aren't making it inactive later, move it to the tail of the list */ if (!becomes_inactive) { -- cgit v1.2.3-54-g00ecf From 6830c9c232ff8d93461d18aa4fc729861804e00e Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Mon, 1 Oct 2012 12:01:05 -0700 Subject: Add a bunch of paranoid-mode expensive asserts incircuitmux.c --- src/or/circuitmux.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/or/circuitmux.c b/src/or/circuitmux.c index fd598407eb..2bf511fe44 100644 --- a/src/or/circuitmux.c +++ b/src/or/circuitmux.c @@ -126,6 +126,17 @@ struct chanid_circid_muxinfo_t { circuit_muxinfo_t muxinfo; }; +/* + * Internal-use #defines + */ + +#ifdef CMUX_PARANOIA +#define circuitmux_assert_okay_paranoid(cmux) \ + circuitmux_assert_okay(cmux) +#else +#define circuitmux_assert_okay_paranoid(cmux) +#endif + /* * Static function declarations */ @@ -176,6 +187,8 @@ circuitmux_move_active_circ_to_tail(circuitmux_t *cmux, circuit_t *circ, tor_assert(cmux); tor_assert(circ); + circuitmux_assert_okay_paranoid(cmux); + /* Figure out our next_p and prev_p for this cmux/direction */ if (direction) { if (direction == CELL_DIRECTION_OUT) { @@ -229,6 +242,8 @@ circuitmux_move_active_circ_to_tail(circuitmux_t *cmux, circuit_t *circ, *prev_p = cmux->active_circuits_tail; /* Set the tail to this circuit */ cmux->active_circuits_tail = circ; + + circuitmux_assert_okay_paranoid(cmux); } static INLINE circuit_t ** @@ -324,6 +339,7 @@ circuitmux_detach_all_circuits(circuitmux_t *cmux) circuit_t *circ = NULL; tor_assert(cmux); + circuitmux_assert_okay_paranoid(cmux); i = HT_START(chanid_circid_muxinfo_map, cmux->chanid_circid_map); while (i) { @@ -794,6 +810,7 @@ circuitmux_attach_circuit(circuitmux_t *cmux, circuit_t *circ, tor_assert(circ); tor_assert(direction == CELL_DIRECTION_IN || direction == CELL_DIRECTION_OUT); + circuitmux_assert_okay_paranoid(cmux); /* * Figure out which channel we're using, and get the circuit's current @@ -919,6 +936,8 @@ circuitmux_attach_circuit(circuitmux_t *cmux, circuit_t *circ, } cmux->n_cells += cell_count; } + + circuitmux_assert_okay_paranoid(cmux); } /** @@ -940,6 +959,7 @@ circuitmux_detach_circuit(circuitmux_t *cmux, circuit_t *circ) tor_assert(cmux->chanid_circid_map); tor_assert(circ); tor_assert(circ->n_chan); + circuitmux_assert_okay_paranoid(cmux); /* See if we have it for n_chan/n_circ_id */ search.chan_id = circ->n_chan->global_identifier; @@ -995,6 +1015,8 @@ circuitmux_detach_circuit(circuitmux_t *cmux, circuit_t *circ) /* Free the hash entry */ tor_free(hashent); } + + circuitmux_assert_okay_paranoid(cmux); } /** @@ -1017,6 +1039,11 @@ circuitmux_make_circuit_active(circuitmux_t *cmux, circuit_t *circ, tor_assert(circ); tor_assert(direction == CELL_DIRECTION_OUT || direction == CELL_DIRECTION_IN); + /* + * Don't circuitmux_assert_okay_paranoid(cmux) here because the cell count + * already got changed and we have to update the list for it to be consistent + * again. + */ /* Get the right set of active list links for this direction */ if (direction == CELL_DIRECTION_OUT) { @@ -1084,6 +1111,8 @@ circuitmux_make_circuit_active(circuitmux_t *cmux, circuit_t *circ, cmux->policy->notify_circ_active(cmux, cmux->policy_data, circ, hashent->muxinfo.policy_data); } + + circuitmux_assert_okay_paranoid(cmux); } /** @@ -1107,6 +1136,11 @@ circuitmux_make_circuit_inactive(circuitmux_t *cmux, circuit_t *circ, tor_assert(circ); tor_assert(direction == CELL_DIRECTION_OUT || direction == CELL_DIRECTION_IN); + /* + * Don't circuitmux_assert_okay_paranoid(cmux) here because the cell count + * already got changed and we have to update the list for it to be consistent + * again. + */ /* Get the right set of active list links for this direction */ if (direction == CELL_DIRECTION_OUT) { @@ -1191,6 +1225,8 @@ circuitmux_make_circuit_inactive(circuitmux_t *cmux, circuit_t *circ, cmux->policy->notify_circ_inactive(cmux, cmux->policy_data, circ, hashent->muxinfo.policy_data); } + + circuitmux_assert_okay_paranoid(cmux); } /** @@ -1217,6 +1253,8 @@ circuitmux_set_num_cells(circuitmux_t *cmux, circuit_t *circ, tor_assert(cmux); tor_assert(circ); + circuitmux_assert_okay_paranoid(cmux); + /* Search for this circuit's entry */ hashent = circuitmux_find_map_entry(cmux, circ); /* Assert that we found one */ @@ -1242,15 +1280,22 @@ circuitmux_set_num_cells(circuitmux_t *cmux, circuit_t *circ, */ if (hashent->muxinfo.cell_count > 0 && n_cells == 0) { --(cmux->n_active_circuits); + hashent->muxinfo.cell_count = n_cells; circuitmux_make_circuit_inactive(cmux, circ, hashent->muxinfo.direction); /* Is the old cell count == 0 and the new cell count > 0 ? */ } else if (hashent->muxinfo.cell_count == 0 && n_cells > 0) { ++(cmux->n_active_circuits); + hashent->muxinfo.cell_count = n_cells; circuitmux_make_circuit_active(cmux, circ, hashent->muxinfo.direction); + } else { + /* + * Update the entry cell count like this so we can put a + * circuitmux_assert_okay_paranoid inside make_circuit_(in)active() too. + */ + hashent->muxinfo.cell_count = n_cells; } - /* Update hash entry cell counter */ - hashent->muxinfo.cell_count = n_cells; + circuitmux_assert_okay_paranoid(cmux); } /* @@ -1301,6 +1346,7 @@ circuitmux_notify_xmit_cells(circuitmux_t *cmux, circuit_t *circ, tor_assert(cmux); tor_assert(circ); + circuitmux_assert_okay_paranoid(cmux); if (n_cells == 0) return; @@ -1351,6 +1397,8 @@ circuitmux_notify_xmit_cells(circuitmux_t *cmux, circuit_t *circ, --(cmux->n_active_circuits); circuitmux_make_circuit_inactive(cmux, circ, hashent->muxinfo.direction); } + + circuitmux_assert_okay_paranoid(cmux); } /* -- cgit v1.2.3-54-g00ecf From 3d092ffbdd023a2be0307cb8a52bd48e2c85b257 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Mon, 1 Oct 2012 13:06:10 -0700 Subject: Handle closing circuits correctly with circuitmux_t --- src/or/circuitlist.c | 17 ++++++--- src/or/circuitmux.c | 102 ++++++++++++++++++++++++++++----------------------- 2 files changed, 68 insertions(+), 51 deletions(-) (limited to 'src') diff --git a/src/or/circuitlist.c b/src/or/circuitlist.c index a4dcec68a2..a87f989f34 100644 --- a/src/or/circuitlist.c +++ b/src/or/circuitlist.c @@ -131,11 +131,12 @@ circuit_set_circid_chan_helper(circuit_t *circ, int direction, if (old_chan) { /* * If we're changing channels or ID and had an old channel and a non - * zero old ID (i.e., we should have been attached), detach the circuit. - * ID changes require this because circuitmux hashes on (channel_id, - * circuit_id). + * zero old ID and weren't marked for close (i.e., we should have been + * attached), detach the circuit. ID changes require this because + * circuitmux hashes on (channel_id, circuit_id). */ - if (id != 0 && (old_chan != chan || old_id != id)) { + if (id != 0 && (old_chan != chan || old_id != id) && + !(circ->marked_for_close)) { tor_assert(old_chan->cmux); circuitmux_detach_circuit(old_chan->cmux, circ); } @@ -180,9 +181,11 @@ circuit_set_circid_chan_helper(circuit_t *circ, int direction, /* * Attach to the circuitmux if we're changing channels or IDs and - * have a new channel and ID to use. + * have a new channel and ID to use and the circuit is not marked for + * close. */ - if (chan && id != 0 && (old_chan != chan || old_id != id)) { + if (chan && id != 0 && (old_chan != chan || old_id != id) && + !(circ->marked_for_close)) { tor_assert(chan->cmux); circuitmux_attach_circuit(chan->cmux, circ, direction); attached = 1; @@ -1398,6 +1401,7 @@ _circuit_mark_for_close(circuit_t *circ, int reason, int line, if (circ->n_chan) { circuit_clear_cell_queue(circ, circ->n_chan); channel_send_destroy(circ->n_circ_id, circ->n_chan, reason); + circuitmux_detach_circuit(circ->n_chan->cmux, circ); } if (! CIRCUIT_IS_ORIGIN(circ)) { @@ -1425,6 +1429,7 @@ _circuit_mark_for_close(circuit_t *circ, int reason, int line, if (or_circ->p_chan) { circuit_clear_cell_queue(circ, or_circ->p_chan); channel_send_destroy(or_circ->p_circ_id, or_circ->p_chan, reason); + circuitmux_detach_circuit(or_circ->p_chan->cmux, circ); } } else { origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ); diff --git a/src/or/circuitmux.c b/src/or/circuitmux.c index 2bf511fe44..48336d8311 100644 --- a/src/or/circuitmux.c +++ b/src/or/circuitmux.c @@ -130,6 +130,8 @@ struct chanid_circid_muxinfo_t { * Internal-use #defines */ +#define CMUX_PARANOIA + #ifdef CMUX_PARANOIA #define circuitmux_assert_okay_paranoid(cmux) \ circuitmux_assert_okay(cmux) @@ -1476,53 +1478,63 @@ circuitmux_assert_okay_pass_one(circuitmux_t *cmux) chan = channel_find_by_global_id(chan_id); tor_assert(chan); circ = circuit_get_by_circid_channel(circ_id, chan); - tor_assert(circ); - - /* Clear the circ_is_active bit to start */ - circ_is_active = 0; - - /* Assert that we know which direction this is going */ - tor_assert((*i)->muxinfo.direction == CELL_DIRECTION_OUT || - (*i)->muxinfo.direction == CELL_DIRECTION_IN); - - if ((*i)->muxinfo.direction == CELL_DIRECTION_OUT) { - /* We should be n_mux on this circuit */ - tor_assert(cmux == circ->n_mux); - tor_assert(chan == circ->n_chan); - /* Get next and prev for next test */ - next_p = &(circ->next_active_on_n_chan); - prev_p = &(circ->prev_active_on_n_chan); - } else { - /* This should be an or_circuit_t and we should be p_mux */ - or_circ = TO_OR_CIRCUIT(circ); - tor_assert(cmux == or_circ->p_mux); - tor_assert(chan == or_circ->p_chan); - /* Get next and prev for next test */ - next_p = &(or_circ->next_active_on_p_chan); - prev_p = &(or_circ->prev_active_on_p_chan); - } + if (circ) { + /* Clear the circ_is_active bit to start */ + circ_is_active = 0; + + /* Assert that we know which direction this is going */ + tor_assert((*i)->muxinfo.direction == CELL_DIRECTION_OUT || + (*i)->muxinfo.direction == CELL_DIRECTION_IN); + + if ((*i)->muxinfo.direction == CELL_DIRECTION_OUT) { + /* We should be n_mux on this circuit */ + tor_assert(cmux == circ->n_mux); + tor_assert(chan == circ->n_chan); + /* Get next and prev for next test */ + next_p = &(circ->next_active_on_n_chan); + prev_p = &(circ->prev_active_on_n_chan); + } else { + /* This should be an or_circuit_t and we should be p_mux */ + or_circ = TO_OR_CIRCUIT(circ); + tor_assert(cmux == or_circ->p_mux); + tor_assert(chan == or_circ->p_chan); + /* Get next and prev for next test */ + next_p = &(or_circ->next_active_on_p_chan); + prev_p = &(or_circ->prev_active_on_p_chan); + } - /* - * Should this circuit be active? I.e., does the mux know about > 0 - * cells on it? - */ - circ_is_active = ((*i)->muxinfo.cell_count > 0); - - /* It should be in the linked list iff it's active */ - if (circ_is_active) { - /* Either we have a next link or we are the tail */ - tor_assert(*next_p || (circ == cmux->active_circuits_tail)); - /* Either we have a prev link or we are the head */ - tor_assert(*prev_p || (circ == cmux->active_circuits_head)); - /* Increment the active circuits counter */ - ++n_active_circuits; + /* + * Should this circuit be active? I.e., does the mux know about > 0 + * cells on it? + */ + circ_is_active = ((*i)->muxinfo.cell_count > 0); + + /* It should be in the linked list iff it's active */ + if (circ_is_active) { + /* Either we have a next link or we are the tail */ + tor_assert(*next_p || (circ == cmux->active_circuits_tail)); + /* Either we have a prev link or we are the head */ + tor_assert(*prev_p || (circ == cmux->active_circuits_head)); + /* Increment the active circuits counter */ + ++n_active_circuits; + } else { + /* Shouldn't be in list, so no next or prev link */ + tor_assert(!(*next_p)); + tor_assert(!(*prev_p)); + /* And can't be head or tail */ + tor_assert(circ != cmux->active_circuits_head); + tor_assert(circ != cmux->active_circuits_tail); + } } else { - /* Shouldn't be in list, so no next or prev link */ - tor_assert(!(*next_p)); - tor_assert(!(*prev_p)); - /* And can't be head or tail */ - tor_assert(circ != cmux->active_circuits_head); - tor_assert(circ != cmux->active_circuits_tail); + /* + * circuit_get_by_circid_channel() doesn't want us to have a circuit + * that's marked for close, but we can test for that case with + * circuit_id_in_use_on_channel(). Assert if it really, really isn't + * there. + */ + tor_assert(circuit_id_in_use_on_channel(circ_id, chan)); + /* It definitely isn't active */ + circ_is_active = 0; } /* Increment the circuits counter */ -- cgit v1.2.3-54-g00ecf From c097fb33fc081165a55f0b9417d7c8bf5dc91cd6 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Mon, 1 Oct 2012 14:22:04 -0700 Subject: Detach circuits from circuitmux early when freeing channel so they can find the channel by ID --- src/or/channel.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src') diff --git a/src/or/channel.c b/src/or/channel.c index 8241556b57..4ad5bf15f3 100644 --- a/src/or/channel.c +++ b/src/or/channel.c @@ -2766,6 +2766,10 @@ channel_free_list(smartlist_t *channels, int mark_for_close) "in state %s (%d)", curr, U64_PRINTF_ARG(curr->global_identifier), channel_state_to_string(curr->state), curr->state); + /* Detach circuits early so they can find the channel */ + if (curr->cmux) { + circuitmux_detach_all_circuits(curr->cmux); + } channel_unregister(curr); if (mark_for_close) { if (!(curr->state == CHANNEL_STATE_CLOSING || -- cgit v1.2.3-54-g00ecf From b28119e6a86fb910c02f53ee5e0154f8d15dc1e4 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Mon, 1 Oct 2012 14:22:53 -0700 Subject: Fix detach when setting circuit ID to 0 bug in circuit_set_circid_chan_helper() and add circuit_get_by_circid_channel_even_if_marked() --- src/or/circuitlist.c | 14 +++++++++++++- src/or/circuitlist.h | 3 +++ 2 files changed, 16 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/or/circuitlist.c b/src/or/circuitlist.c index a87f989f34..51406001a7 100644 --- a/src/or/circuitlist.c +++ b/src/or/circuitlist.c @@ -135,7 +135,7 @@ circuit_set_circid_chan_helper(circuit_t *circ, int direction, * attached), detach the circuit. ID changes require this because * circuitmux hashes on (channel_id, circuit_id). */ - if (id != 0 && (old_chan != chan || old_id != id) && + if (old_id != 0 && (old_chan != chan || old_id != id) && !(circ->marked_for_close)) { tor_assert(old_chan->cmux); circuitmux_detach_circuit(old_chan->cmux, circ); @@ -986,6 +986,18 @@ circuit_get_by_circid_channel(circid_t circ_id, channel_t *chan) return circ; } +/** Return a circ such that: + * - circ-\>n_circ_id or circ-\>p_circ_id is equal to circ_id, and + * - circ is attached to chan, either as p_chan or n_chan. + * Return NULL if no such circuit exists. + */ +circuit_t * +circuit_get_by_circid_channel_even_if_marked(circid_t circ_id, + channel_t *chan) +{ + return circuit_get_by_circid_channel_impl(circ_id, chan); +} + /** Return true iff the circuit ID circ_id is currently used by a * circuit, marked or not, on chan. */ int diff --git a/src/or/circuitlist.h b/src/or/circuitlist.h index f0f4b2a6df..89e957c70a 100644 --- a/src/or/circuitlist.h +++ b/src/or/circuitlist.h @@ -30,6 +30,9 @@ origin_circuit_t *origin_circuit_new(void); or_circuit_t *or_circuit_new(circid_t p_circ_id, channel_t *p_chan); circuit_t *circuit_get_by_circid_channel(circid_t circ_id, channel_t *chan); +circuit_t * +circuit_get_by_circid_channel_even_if_marked(circid_t circ_id, + channel_t *chan); int circuit_id_in_use_on_channel(circid_t circ_id, channel_t *chan); circuit_t *circuit_get_by_edge_conn(edge_connection_t *conn); void circuit_unlink_all_from_channel(channel_t *chan, int reason); -- cgit v1.2.3-54-g00ecf From 1498a6e84a871b2d1b05ab64e2a564a23e03e668 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Mon, 1 Oct 2012 14:24:08 -0700 Subject: Fix some circuitmux-related asserts in relay.c --- src/or/relay.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/or/relay.c b/src/or/relay.c index d0341625f7..7c81273a0a 100644 --- a/src/or/relay.c +++ b/src/or/relay.c @@ -1779,7 +1779,7 @@ circuit_consider_sending_sendme(circuit_t *circ, crypt_path_t *layer_hint) #ifdef ACTIVE_CIRCUITS_PARANOIA #define assert_cmux_ok_paranoid(chan) \ - assert_cmux_okay(chan) + assert_circuit_mux_okay(chan) #else #define assert_cmux_ok_paranoid(chan) #endif @@ -2117,6 +2117,9 @@ channel_flush_from_first_active_circuit(channel_t *chan, int max) streams_blocked = circ->streams_blocked_on_p_chan; } + /* Circuitmux told us this was active, so it should have cells */ + tor_assert(queue->n > 0); + /* * Get just one cell here; once we've sent it, that can change the circuit * selection, so we have to loop around for another even if this circuit -- cgit v1.2.3-54-g00ecf From a0200c1f6e5b85a70a3f2bdc16bd7dd8b50eeb30 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Mon, 1 Oct 2012 14:25:52 -0700 Subject: Use circuit_get_by_circid_channel_even_if_marked() and fix some asserts in circuitmux.c --- src/or/circuitmux.c | 115 ++++++++++++++++++++++++---------------------------- 1 file changed, 54 insertions(+), 61 deletions(-) (limited to 'src') diff --git a/src/or/circuitmux.c b/src/or/circuitmux.c index 48336d8311..e27a7900e3 100644 --- a/src/or/circuitmux.c +++ b/src/or/circuitmux.c @@ -130,8 +130,6 @@ struct chanid_circid_muxinfo_t { * Internal-use #defines */ -#define CMUX_PARANOIA - #ifdef CMUX_PARANOIA #define circuitmux_assert_okay_paranoid(cmux) \ circuitmux_assert_okay(cmux) @@ -341,7 +339,11 @@ circuitmux_detach_all_circuits(circuitmux_t *cmux) circuit_t *circ = NULL; tor_assert(cmux); - circuitmux_assert_okay_paranoid(cmux); + /* + * Don't circuitmux_assert_okay_paranoid() here; this gets called when + * channels are being freed and have already been unregistered, so + * the channel ID lookups it does will fail. + */ i = HT_START(chanid_circid_muxinfo_map, cmux->chanid_circid_map); while (i) { @@ -351,7 +353,9 @@ circuitmux_detach_all_circuits(circuitmux_t *cmux) /* Find a channel and circuit */ chan = channel_find_by_global_id(to_remove->chan_id); if (chan) { - circ = circuit_get_by_circid_channel(to_remove->circ_id, chan); + circ = + circuit_get_by_circid_channel_even_if_marked(to_remove->circ_id, + chan); if (circ) { /* Clear the circuit's mux for this direction */ if (to_remove->muxinfo.direction == CELL_DIRECTION_OUT) { @@ -552,7 +556,7 @@ circuitmux_set_policy(circuitmux_t *cmux, tor_assert(chan); /* Get the circuit */ - circ = circuit_get_by_circid_channel((*i)->circ_id, chan); + circ = circuit_get_by_circid_channel_even_if_marked((*i)->circ_id, chan); tor_assert(circ); /* Need to tell old policy it becomes inactive (i.e., it is active) ? */ @@ -1477,64 +1481,53 @@ circuitmux_assert_okay_pass_one(circuitmux_t *cmux) /* Find the channel and circuit, assert that they exist */ chan = channel_find_by_global_id(chan_id); tor_assert(chan); - circ = circuit_get_by_circid_channel(circ_id, chan); - if (circ) { - /* Clear the circ_is_active bit to start */ - circ_is_active = 0; - - /* Assert that we know which direction this is going */ - tor_assert((*i)->muxinfo.direction == CELL_DIRECTION_OUT || - (*i)->muxinfo.direction == CELL_DIRECTION_IN); - - if ((*i)->muxinfo.direction == CELL_DIRECTION_OUT) { - /* We should be n_mux on this circuit */ - tor_assert(cmux == circ->n_mux); - tor_assert(chan == circ->n_chan); - /* Get next and prev for next test */ - next_p = &(circ->next_active_on_n_chan); - prev_p = &(circ->prev_active_on_n_chan); - } else { - /* This should be an or_circuit_t and we should be p_mux */ - or_circ = TO_OR_CIRCUIT(circ); - tor_assert(cmux == or_circ->p_mux); - tor_assert(chan == or_circ->p_chan); - /* Get next and prev for next test */ - next_p = &(or_circ->next_active_on_p_chan); - prev_p = &(or_circ->prev_active_on_p_chan); - } + circ = circuit_get_by_circid_channel_even_if_marked(circ_id, chan); + tor_assert(circ); + /* Clear the circ_is_active bit to start */ + circ_is_active = 0; + + /* Assert that we know which direction this is going */ + tor_assert((*i)->muxinfo.direction == CELL_DIRECTION_OUT || + (*i)->muxinfo.direction == CELL_DIRECTION_IN); + + if ((*i)->muxinfo.direction == CELL_DIRECTION_OUT) { + /* We should be n_mux on this circuit */ + tor_assert(cmux == circ->n_mux); + tor_assert(chan == circ->n_chan); + /* Get next and prev for next test */ + next_p = &(circ->next_active_on_n_chan); + prev_p = &(circ->prev_active_on_n_chan); + } else { + /* This should be an or_circuit_t and we should be p_mux */ + or_circ = TO_OR_CIRCUIT(circ); + tor_assert(cmux == or_circ->p_mux); + tor_assert(chan == or_circ->p_chan); + /* Get next and prev for next test */ + next_p = &(or_circ->next_active_on_p_chan); + prev_p = &(or_circ->prev_active_on_p_chan); + } - /* - * Should this circuit be active? I.e., does the mux know about > 0 - * cells on it? - */ - circ_is_active = ((*i)->muxinfo.cell_count > 0); - - /* It should be in the linked list iff it's active */ - if (circ_is_active) { - /* Either we have a next link or we are the tail */ - tor_assert(*next_p || (circ == cmux->active_circuits_tail)); - /* Either we have a prev link or we are the head */ - tor_assert(*prev_p || (circ == cmux->active_circuits_head)); - /* Increment the active circuits counter */ - ++n_active_circuits; - } else { - /* Shouldn't be in list, so no next or prev link */ - tor_assert(!(*next_p)); - tor_assert(!(*prev_p)); - /* And can't be head or tail */ - tor_assert(circ != cmux->active_circuits_head); - tor_assert(circ != cmux->active_circuits_tail); - } + /* + * Should this circuit be active? I.e., does the mux know about > 0 + * cells on it? + */ + circ_is_active = ((*i)->muxinfo.cell_count > 0); + + /* It should be in the linked list iff it's active */ + if (circ_is_active) { + /* Either we have a next link or we are the tail */ + tor_assert(*next_p || (circ == cmux->active_circuits_tail)); + /* Either we have a prev link or we are the head */ + tor_assert(*prev_p || (circ == cmux->active_circuits_head)); + /* Increment the active circuits counter */ + ++n_active_circuits; } else { - /* - * circuit_get_by_circid_channel() doesn't want us to have a circuit - * that's marked for close, but we can test for that case with - * circuit_id_in_use_on_channel(). Assert if it really, really isn't - * there. - */ - tor_assert(circuit_id_in_use_on_channel(circ_id, chan)); - /* It definitely isn't active */ - circ_is_active = 0; + /* Shouldn't be in list, so no next or prev link */ + tor_assert(!(*next_p)); + tor_assert(!(*prev_p)); + /* And can't be head or tail */ + tor_assert(circ != cmux->active_circuits_head); + tor_assert(circ != cmux->active_circuits_tail); } /* Increment the circuits counter */ -- cgit v1.2.3-54-g00ecf From 903cc8acd12ae2484ba2ad2a7ac1cb1b9bb88638 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Mon, 1 Oct 2012 14:55:33 -0700 Subject: Allow n_chan to be NULL in circuitmux_find_map_entry(); it can be but with non-NULL p_chan when extending a circuit --- src/or/circuitmux.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/or/circuitmux.c b/src/or/circuitmux.c index e27a7900e3..b782fdbe84 100644 --- a/src/or/circuitmux.c +++ b/src/or/circuitmux.c @@ -655,15 +655,17 @@ circuitmux_find_map_entry(circuitmux_t *cmux, circuit_t *circ) tor_assert(cmux); tor_assert(cmux->chanid_circid_map); tor_assert(circ); - tor_assert(circ->n_chan); - /* Okay, let's see if it's attached for n_chan/n_circ_id */ - search.chan_id = circ->n_chan->global_identifier; - search.circ_id = circ->n_circ_id; + /* Check if we have n_chan */ + if (circ->n_chan) { + /* Okay, let's see if it's attached for n_chan/n_circ_id */ + search.chan_id = circ->n_chan->global_identifier; + search.circ_id = circ->n_circ_id; - /* Query */ - hashent = HT_FIND(chanid_circid_muxinfo_map, cmux->chanid_circid_map, - &search); + /* Query */ + hashent = HT_FIND(chanid_circid_muxinfo_map, cmux->chanid_circid_map, + &search); + } /* Found something? */ if (hashent) { -- cgit v1.2.3-54-g00ecf From 13972aee78542f654bcbbdaf09a49df9fed75738 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Mon, 1 Oct 2012 17:33:41 -0700 Subject: Fix broken circuitmux_move_active_circ_to_tail(); don't assume n_chan is not NULL in circuitmux_detach_circuit() --- src/or/circuitmux.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/or/circuitmux.c b/src/or/circuitmux.c index b782fdbe84..d4ea643679 100644 --- a/src/or/circuitmux.c +++ b/src/or/circuitmux.c @@ -182,6 +182,7 @@ circuitmux_move_active_circ_to_tail(circuitmux_t *cmux, circuit_t *circ, { circuit_t **next_p = NULL, **prev_p = NULL; circuit_t **next_prev = NULL, **prev_next = NULL; + circuit_t **tail_next = NULL; or_circuit_t *or_circ = NULL; tor_assert(cmux); @@ -237,9 +238,18 @@ circuitmux_move_active_circ_to_tail(circuitmux_t *cmux, circuit_t *circ, else cmux->active_circuits_head = *next_p; /* Adjust the next node's previous pointer, if any */ if (next_prev) *next_prev = *prev_p; + /* We're out of the list; now re-attach at the tail */ /* Adjust our next and prev pointers */ *next_p = NULL; *prev_p = cmux->active_circuits_tail; + /* Set the next pointer of the tail, or the head if none */ + if (cmux->active_circuits_tail) { + tail_next = circuitmux_next_active_circ_p(cmux, + cmux->active_circuits_tail); + *tail_next = circ; + } else { + cmux->active_circuits_head = circ; + } /* Set the tail to this circuit */ cmux->active_circuits_tail = circ; @@ -966,15 +976,16 @@ circuitmux_detach_circuit(circuitmux_t *cmux, circuit_t *circ) tor_assert(cmux); tor_assert(cmux->chanid_circid_map); tor_assert(circ); - tor_assert(circ->n_chan); circuitmux_assert_okay_paranoid(cmux); /* See if we have it for n_chan/n_circ_id */ - search.chan_id = circ->n_chan->global_identifier; - search.circ_id = circ->n_circ_id; - hashent = HT_REMOVE(chanid_circid_muxinfo_map, cmux->chanid_circid_map, - &search); - last_searched_direction = CELL_DIRECTION_OUT; + if (circ->n_chan) { + search.chan_id = circ->n_chan->global_identifier; + search.circ_id = circ->n_circ_id; + hashent = HT_REMOVE(chanid_circid_muxinfo_map, cmux->chanid_circid_map, + &search); + last_searched_direction = CELL_DIRECTION_OUT; + } /* Got one? If not, see if it's an or_circuit_t and try p_chan/p_circ_id */ if (!hashent) { -- cgit v1.2.3-54-g00ecf From 8afe41b481caa984fa6b4a0fbbf626d4cd22ce59 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Mon, 1 Oct 2012 20:05:03 -0700 Subject: Implement channel_set_cmux_policy_everywhere() --- src/or/channel.c | 16 ++++++++++++++++ src/or/channel.h | 3 +++ 2 files changed, 19 insertions(+) (limited to 'src') diff --git a/src/or/channel.c b/src/or/channel.c index 4ad5bf15f3..880fa63ca5 100644 --- a/src/or/channel.c +++ b/src/or/channel.c @@ -2695,6 +2695,22 @@ channel_listener_dumpstats(int severity) } } +/** + * Set the cmux policy on all active channels + */ + +void +channel_set_cmux_policy_everywhere(circuitmux_policy_t *pol) +{ + if (!active_channels) return; + + SMARTLIST_FOREACH_BEGIN(active_channels, channel_t *, curr) { + if (curr->cmux) { + circuitmux_set_policy(curr->cmux, pol); + } + } SMARTLIST_FOREACH_END(curr); +} + /** * Clean up channels * diff --git a/src/or/channel.h b/src/or/channel.h index 4d3db41cef..cb9835a9f5 100644 --- a/src/or/channel.h +++ b/src/or/channel.h @@ -291,6 +291,9 @@ void channel_free_all(void); void channel_dumpstats(int severity); void channel_listener_dumpstats(int severity); +/* Set the cmux policy on all active channels */ +void channel_set_cmux_policy_everywhere(circuitmux_policy_t *pol); + #ifdef _TOR_CHANNEL_INTERNAL /* Channel operations for subclasses and internal use only */ -- cgit v1.2.3-54-g00ecf From 0c4f717b3e83e86fc8646d3803c1e435c6af246d Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Mon, 1 Oct 2012 20:08:36 -0700 Subject: Implement cell_ewma_enabled() --- src/or/circuitmux_ewma.c | 8 ++++++++ src/or/circuitmux_ewma.h | 1 + 2 files changed, 9 insertions(+) (limited to 'src') diff --git a/src/or/circuitmux_ewma.c b/src/or/circuitmux_ewma.c index 6d4fcc98cf..c918d6ff8a 100644 --- a/src/or/circuitmux_ewma.c +++ b/src/or/circuitmux_ewma.c @@ -520,6 +520,7 @@ cell_ewma_to_circuit(cell_ewma_t *ewma) * * These tick values are not meant to be shared between Tor instances, or used * for other purposes. */ + static unsigned cell_ewma_tick_from_timeval(const struct timeval *now, double *remainder_out) @@ -532,6 +533,13 @@ cell_ewma_tick_from_timeval(const struct timeval *now, return res; } +/** Tell the caller whether ewma_enabled is set */ +int +cell_ewma_enabled(void) +{ + return ewma_enabled; +} + /** Compute and return the current cell_ewma tick. */ unsigned int cell_ewma_get_tick(void) diff --git a/src/or/circuitmux_ewma.h b/src/or/circuitmux_ewma.h index 0968461c99..eec16f3729 100644 --- a/src/or/circuitmux_ewma.h +++ b/src/or/circuitmux_ewma.h @@ -20,6 +20,7 @@ extern circuitmux_policy_t ewma_policy; #endif /* !(_TOR_CIRCUITMUX_EWMA_C) */ /* Externally visible EWMA functions */ +int cell_ewma_enabled(void); unsigned int cell_ewma_get_tick(void); void cell_ewma_set_scale_factor(const or_options_t *options, const networkstatus_t *consensus); -- cgit v1.2.3-54-g00ecf From bb62281ba48d946e2f948a9c3088f1d0ca481d57 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Mon, 1 Oct 2012 20:27:01 -0700 Subject: Set circuitmux policy on new channels in channeltls.c --- src/or/channeltls.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/or/channeltls.c b/src/or/channeltls.c index bc7e3ec9d3..764de80a78 100644 --- a/src/or/channeltls.c +++ b/src/or/channeltls.c @@ -17,6 +17,7 @@ #include "channel.h" #include "channeltls.h" #include "circuitmux.h" +#include "circuitmux_ewma.h" #include "config.h" #include "connection.h" #include "connection_or.h" @@ -129,7 +130,9 @@ channel_tls_connect(const tor_addr_t *addr, uint16_t port, channel_mark_outgoing(chan); chan->cmux = circuitmux_alloc(); - /* TODO set cmux policy */ + if (cell_ewma_enabled()) { + circuitmux_set_policy(chan->cmux, &ewma_policy); + } /* Set up or_connection stuff */ tlschan->conn = connection_or_connect(addr, port, id_digest, tlschan); @@ -262,7 +265,9 @@ channel_tls_handle_incoming(or_connection_t *orconn) channel_mark_incoming(chan); chan->cmux = circuitmux_alloc(); - /* TODO set cmux policy */ + if (cell_ewma_enabled()) { + circuitmux_set_policy(chan->cmux, &ewma_policy); + } /* If we got one, we should register it */ if (chan) channel_register(chan); -- cgit v1.2.3-54-g00ecf From 9d615cc5c0a645060781797b23a73df158a1dfb6 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Mon, 1 Oct 2012 20:27:51 -0700 Subject: Set circuitmux policy on existing active channels when ewma_enabled changes --- src/or/config.c | 11 +++++++++++ src/or/networkstatus.c | 16 +++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/or/config.c b/src/or/config.c index babbfb0a9c..81528c8275 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -12,6 +12,7 @@ #define CONFIG_PRIVATE #include "or.h" +#include "channel.h" #include "circuitbuild.h" #include "circuitlist.h" #include "circuitmux.h" @@ -1169,6 +1170,7 @@ options_act(const or_options_t *old_options) char *msg=NULL; const int transition_affects_workers = old_options && options_transition_affects_workers(old_options, options); + int old_ewma_enabled; /* disable ptrace and later, other basic debugging techniques */ { @@ -1376,8 +1378,17 @@ options_act(const or_options_t *old_options) connection_bucket_init(); #endif + old_ewma_enabled = cell_ewma_enabled(); /* Change the cell EWMA settings */ cell_ewma_set_scale_factor(options, networkstatus_get_latest_consensus()); + /* If we just enabled ewma, set the cmux policy on all active channels */ + if (cell_ewma_enabled() && !old_ewma_enabled) { + channel_set_cmux_policy_everywhere(&ewma_policy); + } else if (!cell_ewma_enabled() && old_ewma_enabled) { + /* Turn it off everywhere */ + channel_set_cmux_policy_everywhere(NULL); + } + /* Update the BridgePassword's hashed version as needed. We store this as a * digest so that we can do side-channel-proof comparisons on it. diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c index 619099b826..0f6161e2e8 100644 --- a/src/or/networkstatus.c +++ b/src/or/networkstatus.c @@ -11,6 +11,7 @@ */ #include "or.h" +#include "channel.h" #include "circuitbuild.h" #include "circuitmux.h" #include "circuitmux_ewma.h" @@ -1636,6 +1637,7 @@ networkstatus_set_current_consensus(const char *consensus, consensus_waiting_for_certs_t *waiting = NULL; time_t current_valid_after = 0; int free_consensus = 1; /* Free 'c' at the end of the function */ + int old_ewma_enabled; if (flav < 0) { /* XXXX we don't handle unrecognized flavors yet. */ @@ -1829,7 +1831,19 @@ networkstatus_set_current_consensus(const char *consensus, dirvote_recalculate_timing(options, now); routerstatus_list_update_named_server_map(); - cell_ewma_set_scale_factor(options, current_consensus); + + /* Update ewma and adjust policy if needed; first cache the old value */ + old_ewma_enabled = cell_ewma_enabled(); + /* Change the cell EWMA settings */ + cell_ewma_set_scale_factor(options, networkstatus_get_latest_consensus()); + /* If we just enabled ewma, set the cmux policy on all active channels */ + if (cell_ewma_enabled() && !old_ewma_enabled) { + channel_set_cmux_policy_everywhere(&ewma_policy); + } else if (!cell_ewma_enabled() && old_ewma_enabled) { + /* Turn it off everywhere */ + channel_set_cmux_policy_everywhere(NULL); + } + /* XXXX024 this call might be unnecessary here: can changing the * current consensus really alter our view of any OR's rate limits? */ -- cgit v1.2.3-54-g00ecf From c9607694c971a1f77531437ec61dc38c7d5d9ee3 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Mon, 1 Oct 2012 20:30:33 -0700 Subject: Correctly set magic numbers on ewma policy data/circuit data when allocating --- src/or/circuitmux_ewma.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/or/circuitmux_ewma.c b/src/or/circuitmux_ewma.c index c918d6ff8a..9c26e1b2f3 100644 --- a/src/or/circuitmux_ewma.c +++ b/src/or/circuitmux_ewma.c @@ -226,6 +226,7 @@ ewma_alloc_cmux_data(circuitmux_t *cmux) tor_assert(cmux); pol = tor_malloc_zero(sizeof(*pol)); + pol->_base.magic = EWMA_POL_DATA_MAGIC; pol->active_circuit_pqueue = smartlist_new(); pol->active_circuit_pqueue_last_recalibrated = cell_ewma_get_tick(); @@ -278,6 +279,7 @@ ewma_alloc_circ_data(circuitmux_t *cmux, pol = TO_EWMA_POL_DATA(pol_data); cdata = tor_malloc_zero(sizeof(*cdata)); + cdata->_base.magic = EWMA_POL_CIRC_DATA_MAGIC; cdata->circ = circ; /* -- cgit v1.2.3-54-g00ecf From 49d534e524090a0270a9d43822a42e5676b3c63f Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Mon, 1 Oct 2012 21:03:38 -0700 Subject: New and improved circuitmux_detach_all_circuits(), now without the stupid --- src/or/circuitmux.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/or/circuitmux.c b/src/or/circuitmux.c index d4ea643679..7be68583c1 100644 --- a/src/or/circuitmux.c +++ b/src/or/circuitmux.c @@ -369,24 +369,24 @@ circuitmux_detach_all_circuits(circuitmux_t *cmux) if (circ) { /* Clear the circuit's mux for this direction */ if (to_remove->muxinfo.direction == CELL_DIRECTION_OUT) { - /* Clear n_mux */ - circ->n_mux = NULL; /* * Update active_circuits et al.; this does policy notifies, so * comes before freeing policy data */ circuitmux_make_circuit_inactive(cmux, circ, CELL_DIRECTION_OUT); + /* Clear n_mux */ + circ->n_mux = NULL; } else if (circ->magic == OR_CIRCUIT_MAGIC) { - /* - * It has a sensible p_chan and direction == CELL_DIRECTION_IN, - * so clear p_mux. - */ - TO_OR_CIRCUIT(circ)->p_mux = NULL; /* * Update active_circuits et al.; this does policy notifies, so * comes before freeing policy data */ circuitmux_make_circuit_inactive(cmux, circ, CELL_DIRECTION_IN); + /* + * It has a sensible p_chan and direction == CELL_DIRECTION_IN, + * so clear p_mux. + */ + TO_OR_CIRCUIT(circ)->p_mux = NULL; } else { /* Complain and move on */ log_warn(LD_CIRC, -- cgit v1.2.3-54-g00ecf From c9e48ded5d05750fa5654aa6f9d9138265b52a37 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Tue, 2 Oct 2012 20:08:30 -0700 Subject: Bring summary comment block in circuitmux.c up to date --- src/or/circuitmux.c | 46 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/or/circuitmux.c b/src/or/circuitmux.c index 7be68583c1..7d4992e0cb 100644 --- a/src/or/circuitmux.c +++ b/src/or/circuitmux.c @@ -47,24 +47,52 @@ typedef struct circuit_muxinfo_s circuit_muxinfo_t; * To channels, which each have a circuitmux_t, the supported operations * are: * - * circuitmux_flush_cells(): + * circuitmux_get_first_active_circuit(): * - * Retrieve a cell from one of the active circuits, chosen according to - * the circuitmux_t's cell selection policy. + * Pick one of the circuitmux's active circuits to send cells from. * - * circuitmux_unlink_all(): + * circuitmux_notify_xmit_cells(): * - * The channel is closing down, all circuits must be detached. + * Notify the circuitmux that cells have been sent on a circuit. * * To circuits, the exposed operations are: * - * TODO + * circuitmux_attach_circuit(): * - * To circuit selection policies, the exposed operations are: + * Attach a circuit to the circuitmux; this will allocate any policy- + * specific data wanted for this circuit and add it to the active + * circuits list if it has queued cells. * - * TODO + * circuitmux_detach_circuit(): * - * General status inquiries? + * Detach a circuit from the circuitmux, freeing associated structures. + * + * circuitmux_clear_num_cells(): + * + * Clear the circuitmux's cell counter for this circuit. + * + * circuitmux_set_num_cells(): + * + * Set the circuitmux's cell counter for this circuit. + * + * See circuitmux.h for the circuitmux_policy_t data structure, which contains + * a table of function pointers implementing a circuit selection policy, and + * circuitmux_ewma.c for an example of a circuitmux policy. Circuitmux + * policies can be manipulated with: + * + * circuitmux_get_policy(): + * + * Return the current policy for a circuitmux_t, if any. + * + * circuitmux_clear_policy(): + * + * Remove a policy installed on a circuitmux_t, freeing all associated + * data. The circuitmux will revert to the built-in round-robin behavior. + * + * circuitmux_set_policy(): + * + * Install a policy on a circuitmux_t; the appropriate callbacks will be + * made to attach all existing circuits to the new policy. * */ -- cgit v1.2.3-54-g00ecf From 1bc9a040f70cd27f06fac3a9e1b07729442c2f7d Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Fri, 5 Oct 2012 21:57:42 -0700 Subject: Fix 'warning: circuit was already inactive' and assert in circuitmux_make_circuit_inactive() during circuitmux_detach_all_circuits() --- src/or/circuitmux.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/or/circuitmux.c b/src/or/circuitmux.c index 7d4992e0cb..44d736a7c3 100644 --- a/src/or/circuitmux.c +++ b/src/or/circuitmux.c @@ -386,7 +386,6 @@ circuitmux_detach_all_circuits(circuitmux_t *cmux) i = HT_START(chanid_circid_muxinfo_map, cmux->chanid_circid_map); while (i) { to_remove = *i; - i = HT_NEXT_RMV(chanid_circid_muxinfo_map, cmux->chanid_circid_map, i); if (to_remove) { /* Find a channel and circuit */ chan = channel_find_by_global_id(to_remove->chan_id); @@ -401,7 +400,11 @@ circuitmux_detach_all_circuits(circuitmux_t *cmux) * Update active_circuits et al.; this does policy notifies, so * comes before freeing policy data */ - circuitmux_make_circuit_inactive(cmux, circ, CELL_DIRECTION_OUT); + + if (to_remove->muxinfo.cell_count > 0) { + circuitmux_make_circuit_inactive(cmux, circ, CELL_DIRECTION_OUT); + } + /* Clear n_mux */ circ->n_mux = NULL; } else if (circ->magic == OR_CIRCUIT_MAGIC) { @@ -409,7 +412,11 @@ circuitmux_detach_all_circuits(circuitmux_t *cmux) * Update active_circuits et al.; this does policy notifies, so * comes before freeing policy data */ - circuitmux_make_circuit_inactive(cmux, circ, CELL_DIRECTION_IN); + + if (to_remove->muxinfo.cell_count > 0) { + circuitmux_make_circuit_inactive(cmux, circ, CELL_DIRECTION_IN); + } + /* * It has a sensible p_chan and direction == CELL_DIRECTION_IN, * so clear p_mux. @@ -456,10 +463,12 @@ circuitmux_detach_all_circuits(circuitmux_t *cmux) /* Assert that we don't have un-freed policy data for this circuit */ tor_assert(to_remove->muxinfo.policy_data == NULL); - - /* Free it */ - tor_free(to_remove); } + + i = HT_NEXT_RMV(chanid_circid_muxinfo_map, cmux->chanid_circid_map, i); + + /* Free it */ + tor_free(to_remove); } cmux->n_circuits = 0; -- cgit v1.2.3-54-g00ecf From bec776480d673dc85afd2a3db052f3d8eb81a5c4 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Sat, 6 Oct 2012 05:45:24 -0700 Subject: Don't remove circuitmux hash table entries in circuitmux_detach_circuit() until after circuitmux_make_circuit_inactive() --- src/or/circuitmux.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/or/circuitmux.c b/src/or/circuitmux.c index 44d736a7c3..6a6fa6d2d7 100644 --- a/src/or/circuitmux.c +++ b/src/or/circuitmux.c @@ -1019,7 +1019,7 @@ circuitmux_detach_circuit(circuitmux_t *cmux, circuit_t *circ) if (circ->n_chan) { search.chan_id = circ->n_chan->global_identifier; search.circ_id = circ->n_circ_id; - hashent = HT_REMOVE(chanid_circid_muxinfo_map, cmux->chanid_circid_map, + hashent = HT_FIND(chanid_circid_muxinfo_map, cmux->chanid_circid_map, &search); last_searched_direction = CELL_DIRECTION_OUT; } @@ -1030,7 +1030,7 @@ circuitmux_detach_circuit(circuitmux_t *cmux, circuit_t *circ) search.circ_id = TO_OR_CIRCUIT(circ)->p_circ_id; if (TO_OR_CIRCUIT(circ)->p_chan) { search.chan_id = TO_OR_CIRCUIT(circ)->p_chan->global_identifier; - hashent = HT_REMOVE(chanid_circid_muxinfo_map, + hashent = HT_FIND(chanid_circid_muxinfo_map, cmux->chanid_circid_map, &search); last_searched_direction = CELL_DIRECTION_IN; @@ -1038,7 +1038,10 @@ circuitmux_detach_circuit(circuitmux_t *cmux, circuit_t *circ) } } - /* If hashent isn't NULL, we just removed it from the map */ + /* + * If hashent isn't NULL, we have a circuit to detach; don't remove it from + * the map until later of circuitmux_make_circuit_inactive() breaks. + */ if (hashent) { /* Update counters */ --(cmux->n_circuits); @@ -1068,6 +1071,9 @@ circuitmux_detach_circuit(circuitmux_t *cmux, circuit_t *circ) if (last_searched_direction == CELL_DIRECTION_OUT) circ->n_mux = NULL; else TO_OR_CIRCUIT(circ)->p_mux = NULL; + /* Now remove it from the map */ + HT_REMOVE(chanid_circid_muxinfo_map, cmux->chanid_circid_map, hashent); + /* Free the hash entry */ tor_free(hashent); } -- cgit v1.2.3-54-g00ecf From 217352c3624aa62384af57a2e7046e577671b915 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Wed, 10 Oct 2012 00:19:57 -0700 Subject: Make channel_flush_some_cells() compile cleanly on machines with ssize_t larger than int per sjumrdoch comment --- src/or/channel.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/or/channel.c b/src/or/channel.c index 880fa63ca5..1ca5e1a333 100644 --- a/src/or/channel.c +++ b/src/or/channel.c @@ -2018,7 +2018,7 @@ channel_flush_some_cells(channel_t *chan, ssize_t num_cells) { unsigned int unlimited = 0; ssize_t flushed = 0; - int num_cells_from_circs; + int num_cells_from_circs, clamped_num_cells; tor_assert(chan); @@ -2033,12 +2033,20 @@ channel_flush_some_cells(channel_t *chan, ssize_t num_cells) if (!unlimited && num_cells <= flushed) goto done; if (circuitmux_num_cells(chan->cmux) > 0) { + /* Calculate number of cells, including clamp */ + if (unlimited) { + clamped_num_cells = MAX_CELLS_TO_GET_FROM_CIRCUITS_FOR_UNLIMITED; + } else { + if (num_cells - flushed > + MAX_CELLS_TO_GET_FROM_CIRCUITS_FOR_UNLIMITED) { + clamped_num_cells = MAX_CELLS_TO_GET_FROM_CIRCUITS_FOR_UNLIMITED; + } else { + clamped_num_cells = (int)(num_cells - flushed); + } + } /* Try to get more cells from any active circuits */ num_cells_from_circs = channel_flush_from_first_active_circuit( - chan, - (unlimited ? - MAX_CELLS_TO_GET_FROM_CIRCUITS_FOR_UNLIMITED : - (num_cells - flushed))); + chan, clamped_num_cells); /* If it claims we got some, process the queue again */ if (num_cells_from_circs > 0) { -- cgit v1.2.3-54-g00ecf From 5543c5b202b045bc5b7d8d4f127c54decd17e943 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Wed, 10 Oct 2012 00:48:36 -0700 Subject: Fix formatting in various places after 6465/6816 work --- src/common/crypto.c | 6 ++++-- src/or/channel.c | 5 +++-- src/or/circuitmux.c | 2 +- src/or/circuitmux_ewma.c | 6 +++--- src/or/config.c | 1 - src/or/networkstatus.c | 1 - 6 files changed, 11 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/common/crypto.c b/src/common/crypto.c index 2de624c7d0..87a86afd4a 100644 --- a/src/common/crypto.c +++ b/src/common/crypto.c @@ -244,8 +244,10 @@ crypto_global_init(int useAccel, const char *accelName, const char *accelDir) } if (SSLeay() < OPENSSL_V_SERIES(1,0,0)) { - log_notice(LD_CRYPTO, "Your OpenSSL version seems to be %s. We " - "recommend 1.0.0 or later.", crypto_openssl_get_version_str()); + log_notice(LD_CRYPTO, + "Your OpenSSL version seems to be %s. We recommend 1.0.0 " + "or later.", + crypto_openssl_get_version_str()); } if (useAccel > 0) { diff --git a/src/or/channel.c b/src/or/channel.c index 1ca5e1a333..570bb37b14 100644 --- a/src/or/channel.c +++ b/src/or/channel.c @@ -1518,7 +1518,7 @@ cell_queue_entry_dup(cell_queue_entry_t *q) rv = tor_malloc(sizeof(*rv)); memcpy(rv, q, sizeof(*rv)); - + return rv; } @@ -1529,7 +1529,8 @@ cell_queue_entry_dup(cell_queue_entry_t *q) */ static void -cell_queue_entry_free(cell_queue_entry_t *q, int handed_off) { +cell_queue_entry_free(cell_queue_entry_t *q, int handed_off) +{ if (!q) return; if (!handed_off) { diff --git a/src/or/circuitmux.c b/src/or/circuitmux.c index 6a6fa6d2d7..f3b6b7cd7b 100644 --- a/src/or/circuitmux.c +++ b/src/or/circuitmux.c @@ -412,7 +412,7 @@ circuitmux_detach_all_circuits(circuitmux_t *cmux) * Update active_circuits et al.; this does policy notifies, so * comes before freeing policy data */ - + if (to_remove->muxinfo.cell_count > 0) { circuitmux_make_circuit_inactive(cmux, circ, CELL_DIRECTION_IN); } diff --git a/src/or/circuitmux_ewma.c b/src/or/circuitmux_ewma.c index 9c26e1b2f3..82ad5c7413 100644 --- a/src/or/circuitmux_ewma.c +++ b/src/or/circuitmux_ewma.c @@ -168,7 +168,7 @@ ewma_free_circ_data(circuitmux_t *cmux, circuitmux_policy_data_t *pol_data, circuit_t *circ, circuitmux_policy_circ_data_t *pol_circ_data); -static void +static void ewma_notify_circ_active(circuitmux_t *cmux, circuitmux_policy_data_t *pol_data, circuit_t *circ, @@ -329,7 +329,7 @@ ewma_free_circ_data(circuitmux_t *cmux, * the active_circuits_pqueue. */ -static void +static void ewma_notify_circ_active(circuitmux_t *cmux, circuitmux_policy_data_t *pol_data, circuit_t *circ, @@ -354,7 +354,7 @@ ewma_notify_circ_active(circuitmux_t *cmux, * the active_circuits_pqueue. */ -static void +static void ewma_notify_circ_inactive(circuitmux_t *cmux, circuitmux_policy_data_t *pol_data, circuit_t *circ, diff --git a/src/or/config.c b/src/or/config.c index 81528c8275..3e92c43748 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -1389,7 +1389,6 @@ options_act(const or_options_t *old_options) channel_set_cmux_policy_everywhere(NULL); } - /* Update the BridgePassword's hashed version as needed. We store this as a * digest so that we can do side-channel-proof comparisons on it. */ diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c index 0f6161e2e8..91720b5835 100644 --- a/src/or/networkstatus.c +++ b/src/or/networkstatus.c @@ -1844,7 +1844,6 @@ networkstatus_set_current_consensus(const char *consensus, channel_set_cmux_policy_everywhere(NULL); } - /* XXXX024 this call might be unnecessary here: can changing the * current consensus really alter our view of any OR's rate limits? */ connection_or_update_token_buckets(get_connection_array(), options); -- cgit v1.2.3-54-g00ecf From bd28322d3815a03ca04beb6d00052d613dbe226f Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 10 Oct 2012 21:25:52 -0400 Subject: Remove variables; fix gcc 4.7 warnings My GCC warns when variables are assigned to but never used. There were a few like that in the 6816/6465 branches. --- src/or/channel.c | 3 +-- src/or/channeltls.c | 3 --- src/or/circuitlist.c | 4 +--- src/or/circuitmux_ewma.c | 5 ----- 4 files changed, 2 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/or/channel.c b/src/or/channel.c index 570bb37b14..b52db405e1 100644 --- a/src/or/channel.c +++ b/src/or/channel.c @@ -552,7 +552,7 @@ channel_add_to_digest_map(channel_t *chan) static void channel_remove_from_digest_map(channel_t *chan) { - channel_t *tmp, *head; + channel_t *tmp; tor_assert(chan); @@ -588,7 +588,6 @@ channel_remove_from_digest_map(channel_t *chan) tmp = digestmap_get(channel_identity_map, chan->identity_digest); if (tmp) { /* Okay, it's here */ - head = tmp; /* Keep track of list head */ /* Look for this channel */ while (tmp && tmp != chan) { tmp = tmp->next_with_same_id; diff --git a/src/or/channeltls.c b/src/or/channeltls.c index 764de80a78..1a2956b755 100644 --- a/src/or/channeltls.c +++ b/src/or/channeltls.c @@ -917,7 +917,6 @@ void channel_tls_handle_var_cell(var_cell_t *var_cell, or_connection_t *conn) { channel_tls_t *chan; - int handshaking; #ifdef KEEP_TIMING_STATS /* how many of each cell have we seen so far this second? needs better @@ -953,8 +952,6 @@ channel_tls_handle_var_cell(var_cell_t *var_cell, or_connection_t *conn) return; } - handshaking = (TO_CONN(conn)->state != OR_CONN_STATE_OPEN); - if (TO_CONN(conn)->marked_for_close) return; diff --git a/src/or/circuitlist.c b/src/or/circuitlist.c index 51406001a7..b73822635d 100644 --- a/src/or/circuitlist.c +++ b/src/or/circuitlist.c @@ -100,18 +100,16 @@ circuit_set_circid_chan_helper(circuit_t *circ, int direction, chan_circid_circuit_map_t *found; channel_t *old_chan, **chan_ptr; circid_t old_id, *circid_ptr; - int was_active, make_active, attached = 0; + int make_active, attached = 0; if (direction == CELL_DIRECTION_OUT) { chan_ptr = &circ->n_chan; circid_ptr = &circ->n_circ_id; - was_active = circ->next_active_on_n_chan != NULL; make_active = circ->n_chan_cells.n > 0; } else { or_circuit_t *c = TO_OR_CIRCUIT(circ); chan_ptr = &c->p_chan; circid_ptr = &c->p_circ_id; - was_active = c->next_active_on_p_chan != NULL; make_active = c->p_chan_cells.n > 0; } old_chan = *chan_ptr; diff --git a/src/or/circuitmux_ewma.c b/src/or/circuitmux_ewma.c index 82ad5c7413..c280bf4ab0 100644 --- a/src/or/circuitmux_ewma.c +++ b/src/or/circuitmux_ewma.c @@ -265,7 +265,6 @@ ewma_alloc_circ_data(circuitmux_t *cmux, cell_direction_t direction, unsigned int cell_count) { - ewma_policy_data_t *pol = NULL; ewma_policy_circ_data_t *cdata = NULL; tor_assert(cmux); @@ -276,8 +275,6 @@ ewma_alloc_circ_data(circuitmux_t *cmux, /* Shut the compiler up */ tor_assert(cell_count == cell_count); - pol = TO_EWMA_POL_DATA(pol_data); - cdata = tor_malloc_zero(sizeof(*cdata)); cdata->_base.magic = EWMA_POL_CIRC_DATA_MAGIC; cdata->circ = circ; @@ -309,13 +306,11 @@ ewma_free_circ_data(circuitmux_t *cmux, circuitmux_policy_circ_data_t *pol_circ_data) { - ewma_policy_data_t *pol = NULL; ewma_policy_circ_data_t *cdata = NULL; tor_assert(cmux); tor_assert(circ); tor_assert(pol_data); - pol = TO_EWMA_POL_DATA(pol_data); if (!pol_circ_data) return; -- cgit v1.2.3-54-g00ecf