Tearing down circuits

Circuits are torn down when an unrecoverable error occurs along the circuit, or when all streams on a circuit are closed and the circuit's intended lifetime is over.

ORs SHOULD also tear down circuits which attempt to create:

  • streams with RELAY_BEGIN, or
  • rendezvous points with ESTABLISH_RENDEZVOUS, ending at the first hop. Letting Tor be used as a single hop proxy makes exit and rendezvous nodes a more attractive target for compromise.

ORs MAY use multiple methods to check if they are the first hop:

   * If an OR sees a circuit created with CREATE_FAST, the OR is sure to be
     the first hop of a circuit.
   * If an OR is the responder, and the initiator:
     * did not authenticate the link, or
     * authenticated with a key that is not in the consensus,
     then the OR is probably the first hop of a circuit (or the second hop of
     a circuit via a bridge relay).

   Circuits may be torn down either completely or hop-by-hop.

To tear down a circuit completely, an OR or OP sends a DESTROY cell to the adjacent nodes on that circuit, using the appropriate direction's circID.

Upon receiving an outgoing DESTROY cell, an OR frees resources associated with the corresponding circuit. If it's not the end of the circuit, it sends a DESTROY cell for that circuit to the next OR in the circuit. If the node is the end of the circuit, then it tears down any associated edge connections (see Calculating the 'Digest' field).

After a DESTROY cell has been processed, an OR ignores all data or destroy cells for the corresponding circuit.

To tear down part of a circuit, the OP may send a RELAY_TRUNCATE cell signaling a given OR (Stream ID zero). That OR sends a DESTROY cell to the next node in the circuit, and replies to the OP with a RELAY_TRUNCATED cell.

[Note: If an OR receives a TRUNCATE cell and it has any RELAY cells still queued on the circuit for the next node it will drop them without sending them. This is not considered conformant behavior, but it probably won't get fixed until a later version of Tor. Thus, clients SHOULD NOT send a TRUNCATE cell to a node running any current version of Tor if a) they have sent relay cells through that node, and b) they aren't sure whether those cells have been sent on yet.]

   When an unrecoverable error occurs along one a circuit, the nodes
   must report it as follows:
     * If possible, send a DESTROY cell to ORs _away_ from the client.
     * If possible, send *either* a DESTROY cell towards the client, or
       a RELAY_TRUNCATED cell towards the client.

Current versions of Tor do not reuse truncated RELAY_TRUNCATED circuits: An OP, upon receiving a RELAY_TRUNCATED, will send forward a DESTROY cell in order to entirely tear down the circuit. Because of this, we recommend that relays should send DESTROY towards the client, not RELAY_TRUNCATED.

     In tor versions before, and, relays would
     handle an inbound DESTROY by sending the client a RELAY_TRUNCATED
     message.  Beginning with those versions, relays now propagate
     DESTROY cells in either direction, in order to tell every
     intermediary ORs to stop queuing data on the circuit.  The earlier
     behavior created queuing pressure on the intermediary ORs.

The payload of a DESTROY and RELAY_TRUNCATED cell contains a single octet, describing the reason that the circuit was closed. RELAY_TRUNCATED cells, and DESTROY cells sent _towards the client, should contain the actual reason from the list of error codes below. Reasons in DESTROY cell SHOULD NOT be propagated downward or upward, due to potential side channel risk: An OR receiving a DESTROY command should use the DESTROYED reason for its next cell. An OP should always use the NONE reason for its own DESTROY cells.

The error codes are:

     0 -- NONE            (No reason given.)
     1 -- PROTOCOL        (Tor protocol violation.)
     2 -- INTERNAL        (Internal error.)
     3 -- REQUESTED       (A client sent a TRUNCATE command.)
     4 -- HIBERNATING     (Not currently operating; trying to save bandwidth.)
     5 -- RESOURCELIMIT   (Out of memory, sockets, or circuit IDs.)
     6 -- CONNECTFAILED   (Unable to reach relay.)
     7 -- OR_IDENTITY     (Connected to relay, but its OR identity was not
                           as expected.)
     8 -- CHANNEL_CLOSED  (The OR connection that was carrying this circuit
     9 -- FINISHED        (The circuit has expired for being dirty or old.)
    10 -- TIMEOUT         (Circuit construction took too long)
    11 -- DESTROYED       (The circuit was destroyed w/o client TRUNCATE)
    12 -- NOSUCHSERVICE   (Request for unknown hidden service)