When we build
We don't build circuits until we have enough directory info
There's a class of possible attacks where our directory servers only give us information about the relays that they would like us to use. To prevent this attack, we don't build multi-hop circuits (including preemptive circuits, [on-demand circuits(#on-demand), onion-service circuits] or self-testing testing circuits) for real traffic until we have enough directory information to be reasonably confident this attack isn't being done to us.
Here, "enough" directory information is defined as:
* Having a consensus that's been valid at some point in the
last REASONABLY_LIVE_TIME interval (24 hours).
* Having enough descriptors that we could build at least some
fraction F of all bandwidth-weighted paths, without taking
ExitNodes/EntryNodes/etc into account.
(F is set by the PathsNeededToBuildCircuits option,
defaulting to the 'min_paths_for_circs_pct' consensus
parameter, with a final default value of 60%.)
* Having enough descriptors that we could build at least some
fraction F of all bandwidth-weighted paths, _while_ taking
ExitNodes/EntryNodes/etc into account.
(F is as above.)
* Having a descriptor for every one of the first
NUM_USABLE_PRIMARY_GUARDS guards among our primary guards. (see
guard-spec.txt)
We define the "fraction of bandwidth-weighted paths" as the product of these three fractions.
* The fraction of descriptors that we have for nodes with the Guard
flag, weighted by their bandwidth for the guard position.
* The fraction of descriptors that we have for all nodes,
weighted by their bandwidth for the middle position.
* The fraction of descriptors that we have for nodes with the Exit
flag, weighted by their bandwidth for the exit position.
If the consensus has zero weighted bandwidth for a given kind of relay (Guard, Middle, or Exit), Tor instead uses the fraction of relays for which it has the descriptor (not weighted by bandwidth at all).
If the consensus lists zero exit-flagged relays, Tor instead uses the fraction of middle relays.
Clients build circuits preemptively
When running as a client, Tor tries to maintain at least a certain number of clean circuits, so that new streams can be handled quickly. To increase the likelihood of success, Tor tries to predict what circuits will be useful by choosing from among nodes that support the ports we have used in the recent past (by default one hour). Specifically, on startup Tor tries to maintain one clean fast exit circuit that allows connections to port 80, and at least two fast clean stable internal circuits in case we get a resolve request or hidden service request (at least three if we run a hidden service).
After that, Tor will adapt the circuits that it preemptively builds based on the requests it sees from the user: it tries to have two fast clean exit circuits available for every port seen within the past hour (each circuit can be adequate for many predicted ports -- it doesn't need two separate circuits for each port), and it tries to have the above internal circuits available if we've seen resolves or hidden service activity within the past hour. If there are 12 or more clean circuits open, it doesn't open more even if it has more predictions.
Only stable circuits can "cover" a port that is listed in the LongLivedPorts config option. Similarly, hidden service requests to ports listed in LongLivedPorts make us create stable internal circuits.
Note that if there are no requests from the user for an hour, Tor will predict no use and build no preemptive circuits.
The Tor client SHOULD NOT store its list of predicted requests to a persistent medium.
Clients build circuits on demand
Additionally, when a client request exists that no circuit (built or pending) might support, we create a new circuit to support the request. For exit connections, we pick an exit node that will handle the most pending requests (choosing arbitrarily among ties), launch a circuit to end there, and repeat until every unattached request might be supported by a pending or built circuit. For internal circuits, we pick an arbitrary acceptable path, repeating as needed.
Clients consider a circuit to become "dirty" as soon as a stream is attached to it, or some other request is performed over the circuit. If a circuit has been "dirty" for at least MaxCircuitDirtiness seconds, new circuits may not be attached to it.
In some cases we can reuse an already established circuit if it's clean; see "cannibalizing circuits"
for details.
Relays build circuits for testing reachability and bandwidth
Tor relays test reachability of their ORPort once they have successfully built a circuit (on startup and whenever their IP address changes). They build an ordinary fast internal circuit with themselves as the last hop. As soon as any testing circuit succeeds, the Tor relay decides it's reachable and is willing to publish a descriptor.
We launch multiple testing circuits (one at a time), until we have NUM_PARALLEL_TESTING_CIRC (4) such circuits open. Then we do a "bandwidth test" by sending a certain number of relay drop cells down each circuit: BandwidthRate * 10 / CELL_NETWORK_SIZE total cells divided across the four circuits, but never more than CIRCWINDOW_START (1000) cells total. This exercises both outgoing and incoming bandwidth, and helps to jumpstart the observed bandwidth (see dir-spec.txt).
Tor relays also test reachability of their DirPort once they have established a circuit, but they use an ordinary exit circuit for this purpose.
Hidden-service circuits
See section 4 below.
Rate limiting of failed circuits
If we fail to build a circuit N times in a X second period (see "Handling failure" for how this works), we stop building circuits until the X seconds have elapsed. XXXX
When to tear down circuits
Clients should tear down circuits (in general) only when those circuits have no streams on them. Additionally, clients should tear-down stream-less circuits only under one of the following conditions:
- The circuit has never had a stream attached, and it was created too
long in the past (based on CircuitsAvailableTimeout or
cbtlearntimeout, depending on timeout estimate status).
- The circuit is dirty (has had a stream attached), and it has been
dirty for at least MaxCircuitDirtiness.