tor-commits
  Threads by month 
                
            - ----- 2025 -----
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
October 2012
- 20 participants
- 1288 discussions
 
                        
                    
                        
                            
                                
                            
                            [tor/master] Set reason_for_closing when erroring out	of channel_tls_connect()
                        
                        
by andrea@torproject.org 11 Oct '12
                    by andrea@torproject.org 11 Oct '12
11 Oct '12
                    
                        commit 53454fad956c278921487a9956aa9f022b9cc2c7
Author: Andrea Shepard <andrea(a)torproject.org>
Date:   Tue Oct 9 10:51:10 2012 -0700
    Set reason_for_closing when erroring out of channel_tls_connect()
---
 src/or/channeltls.c |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/src/or/channeltls.c b/src/or/channeltls.c
index 93e0636..339663e 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;
   }
                    
                  
                  
                          
                            
                            1
                            
                          
                          
                            
                            0
                            
                          
                          
                            
    
                          
                        
                    
                    
                        commit 6391f963fb1ff9988d048397cd054b65ff5a52ff
Author: Andrea Shepard <andrea(a)torproject.org>
Date:   Mon Oct 8 21:30:07 2012 -0700
    Conform to existing Doxygen style
---
 src/or/channel.c    |  265 +++-----------------------------------------------
 src/or/channeltls.c |   98 +------------------
 2 files changed, 18 insertions(+), 345 deletions(-)
diff --git a/src/or/channel.c b/src/or/channel.c
index fac4017..57b9e4f 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 <b>circ_id</b> and reason <b>reason</b>
  * onto channel <b>chan</b>.  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 bb6ef9b..bb90ce5 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 <b>addr</b>:<b>port</b> and expect to
  * handshake with an OR with identity digest <b>id_digest</b>, 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
  * <b>cell</b> that just arrived on <b>chan</b>. Increment <b>*time</b>
  * by the number of microseconds used by the call to <b>*func(cell, chan)</b>.
- *
- * @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 <b>command</b> 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
                    
                  
                  
                          
                            
                            1
                            
                          
                          
                            
                            0
                            
                          
                          
                            
    
                          
                        
                     
                        
                    
                        
                            
                                
                            
                            [tor/master] Split channel_t into channel_t and	channel_listener_t; get rid of that big union
                        
                        
by andrea@torproject.org 11 Oct '12
                    by andrea@torproject.org 11 Oct '12
11 Oct '12
                    
                        commit 3f4b95b1a32787464b5877c7d21474801d4b944c
Author: Andrea Shepard <andrea(a)torproject.org>
Date:   Tue Oct 9 00:51:33 2012 -0700
    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(-)
diff --git a/src/or/channel.c b/src/or/channel.c
index 57b9e4f..278daa6 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:
@@ -123,6 +133,30 @@ channel_state_is_valid(channel_state_t state)
 }
 
 /**
+ * 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
  *
  * This function takes two channel states and indicates whether a
@@ -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 ||
@@ -175,6 +204,43 @@ channel_state_can_transition(channel_state_t from, channel_state_t to)
 }
 
 /**
+ * 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;
@@ -946,6 +1093,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
  *
  * Notify the channel code that the channel is being closed due to a non-error
@@ -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;
@@ -975,6 +1164,36 @@ channel_close_from_lower_layer(channel_t *chan)
 }
 
 /**
+ * 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
  *
  * This function is called by the lower layer implementing the transport
@@ -1004,6 +1223,37 @@ channel_close_for_error(channel_t *chan)
 }
 
 /**
+ * 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
  *
  * This function should be called by the lower layer when a channel
@@ -1039,6 +1289,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
  *
  * This function clears the identity digest of the remote endpoint for a
@@ -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");
   }
 }
 
@@ -2250,6 +2603,91 @@ channel_run_cleanup(void)
 }
 
 /**
+ * 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
  *
  * This gets called from tor_free_all() in main.c to clean up on exit.
@@ -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.
@@ -2578,6 +2967,22 @@ channel_describe_transport(channel_t *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
  *
  * Dump statistics for one channel to the log
@@ -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
  */
@@ -2825,6 +3262,21 @@ channel_dump_transport_statistics(channel_t *chan, int 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
  *
  * This function return a test provided by the lower layer of the remote
@@ -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
@@ -3150,21 +3603,36 @@ channel_timestamp_active(channel_t *chan)
 }
 
 /**
+ * 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 70ea30f..c31806c 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 bb90ce5..93e0636 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) ?
              "<none>" : 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 3b7d6a7..b38e12a 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 5ef67bd..749985f 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 68cd19e..cf6020d 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 <b>circ</b>, 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 e4e1b8a..be79b30 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 2fb70b5..e175e23 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 eddce87..f9a0ef2 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 834f970..45f3a06 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 a3df775..bf69711 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 2a3e0e1..e0c89a9 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 4d2ab21..5987eef 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,21 +896,10 @@ 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
    *
    * Permitted transitions from:
@@ -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 3850562..60f696c 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 <b>ewma</b> to the same scale as <b>chan</b>, 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 <b>block</b> is true) or unblock (if <b>block</b> 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
                    
                  
                  
                          
                            
                            1
                            
                          
                          
                            
                            0
                            
                          
                          
                            
    
                          
                        
                     
                        
                    
                        
                            
                                
                            
                            [tor/master] Refactor	channel_write_cell()/channel_write_packed_cell()/channel_write_var_cell()	to eliminate redundant code
                        
                        
by andrea@torproject.org 11 Oct '12
                    by andrea@torproject.org 11 Oct '12
11 Oct '12
                    
                        commit 06a76d1db49d7c9c386368583928cde11b509519
Author: Andrea Shepard <andrea(a)torproject.org>
Date:   Mon Oct 8 21:16:59 2012 -0700
    Refactor channel_write_cell()/channel_write_packed_cell()/channel_write_var_cell() to eliminate redundant code
---
 src/or/channel.c |  217 ++++++++++++++++++++++++++---------------------------
 1 files changed, 106 insertions(+), 111 deletions(-)
diff --git a/src/or/channel.c b/src/or/channel.c
index 9fbfecb..fac4017 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);
 }
 
 /**
                    
                  
                  
                          
                            
                            1
                            
                          
                          
                            
                            0
                            
                          
                          
                            
    
                          
                        
                     
                        
                    11 Oct '12
                    
                        commit bddfb9ffa85a0fe73545fb231a0847c4101758c1
Author: Andrea Shepard <andrea(a)torproject.org>
Date:   Tue Oct 9 14:16:51 2012 -0700
    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(-)
diff --git a/src/or/channel.h b/src/or/channel.h
index c31806c..27fba8f 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 339663e..5d6a7a9 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 b38e12a..ca2fc88 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);
                    
                  
                  
                          
                            
                            1
                            
                          
                          
                            
                            0
                            
                          
                          
                            
    
                          
                        
                     
                        
                    
                        
                            
                                
                            
                            [tor/master] Use circuitmux_t in channels and when	relaying cells
                        
                        
by andrea@torproject.org 11 Oct '12
                    by andrea@torproject.org 11 Oct '12
11 Oct '12
                    
                        commit b208539b8047a12fb2f1f794c9932fddd577dfdb
Author: Andrea Shepard <andrea(a)torproject.org>
Date:   Fri Sep 21 14:46:22 2012 -0700
    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(-)
diff --git a/src/or/channel.c b/src/or/channel.c
index 334f843..8241556 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,
@@ -4008,6 +4011,22 @@ channel_matches_target_addr_for_extend(channel_t *chan,
 }
 
 /**
+ * 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
  *
  * This is called when setting up a channel and replaces the old
diff --git a/src/or/channel.h b/src/or/channel.h
index 27fba8f..4d3db41 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 5d6a7a9..036d14f 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 cf6020d..bec3dc8 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 <b>circ</b>, 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 bf69711..f143e9b 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 87ee7bb..a9b0361 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 60f696c..f079243 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 <b>circ</b> to the list of circuits with pending cells on
- * <b>chan</b>.  No effect if <b>circ</b> 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 <b>circ</b> from the list of circuits with pending cells on
- * <b>chan</b>.  No effect if <b>circ</b> 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
- * <b>chan</b>. */
+/** Remove all circuits from the cmux on <b>chan</b>. */
 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 <b>block</b> is true) or unblock (if <b>block</b> 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 <b>orconn</b> 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 7f96d59..ef5074b 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,
                    
                  
                  
                          
                            
                            1
                            
                          
                          
                            
                            0
                            
                          
                          
                            
    
                          
                        
                     
                        
                    
                        
                            
                                
                            
                            [tor/master] Use cell_queue_entry_new/free()	functions in channel.c
                        
                        
by andrea@torproject.org 11 Oct '12
                    by andrea@torproject.org 11 Oct '12
11 Oct '12
                    
                        commit 1c3362dcdcf39f4b9da8c71567412349d111d08f
Author: Andrea Shepard <andrea(a)torproject.org>
Date:   Tue Oct 9 11:35:08 2012 -0700
    Use cell_queue_entry_new/free() functions in channel.c
---
 src/or/channel.c |  157 ++++++++++++++++++++++++++++++++++++++++++++++--------
 1 files changed, 135 insertions(+), 22 deletions(-)
diff --git a/src/or/channel.c b/src/or/channel.c
index 278daa6..e87f4de 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);
@@ -1502,6 +1504,79 @@ channel_set_remote_end(channel_t *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()
  */
@@ -1531,6 +1606,42 @@ cell_queue_entry_is_padding(cell_queue_entry_t *q)
 }
 
 /**
+ * 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
  *
  * Given a cell_queue_entry_t filled out by the caller, try to send the cell
@@ -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 ")",
                    
                  
                  
                          
                            
                            1
                            
                          
                          
                            
                            0
                            
                          
                          
                            
    
                          
                        
                    
                    
                        commit f00b44ef8c82eb52fad19fd2218f485f1c5aceca
Author: Andrea Shepard <andrea(a)torproject.org>
Date:   Tue Oct 9 11:38:15 2012 -0700
    Improve comments on channel_write_*()
---
 src/or/channel.c |   11 ++++++++---
 1 files changed, 8 insertions(+), 3 deletions(-)
diff --git a/src/or/channel.c b/src/or/channel.c
index e87f4de..334f843 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
                    
                  
                  
                          
                            
                            1
                            
                          
                          
                            
                            0
                            
                          
                          
                            
    
                          
                        
                    
                    
                        commit c684076fc7f685d6e0cd97f426d1474749f1da8b
Author: Andrea Shepard <andrea(a)torproject.org>
Date:   Fri Sep 21 14:45:32 2012 -0700
    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(-)
diff --git a/src/or/Makefile.nmake b/src/or/Makefile.nmake
index b145fdc..9f948d6 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 0000000..45b72f2
--- /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 0000000..c5f9526
--- /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 c323575..3ec94e5 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 5987eef..87ee7bb 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
                    
                  
                  
                          
                            
                            1
                            
                          
                          
                            
                            0
                            
                          
                          
                            
    
                          
                        
                     
                        
                    
                        
                            
                                
                            
                            [tor/master] Implement	circuitmux_alloc()/circuitmux_free() and	chanid/circid->muxinfo hash table
                        
                        
by andrea@torproject.org 11 Oct '12
                    by andrea@torproject.org 11 Oct '12
11 Oct '12
                    
                        commit e4a11b890e7c5fe45dc1f5f271fbd8130ccc9c55
Author: Andrea Shepard <andrea(a)torproject.org>
Date:   Mon Sep 24 08:52:05 2012 -0700
    Implement circuitmux_alloc()/circuitmux_free() and chanid/circid->muxinfo hash table
---
 src/or/circuitmux.c |  144 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 143 insertions(+), 1 deletions(-)
diff --git a/src/or/circuitmux.c b/src/or/circuitmux.c
index 45b72f2..d4866ca 100644
--- a/src/or/circuitmux.c
+++ b/src/or/circuitmux.c
@@ -10,6 +10,33 @@
 #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
  * to transmit) and how many cells on each.  It expoes three distinct
@@ -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);
+}
+
                    
                  
                  
                          
                            
                            1
                            
                          
                          
                            
                            0