# The algorithm ## The guards listed in the current consensus. {#GUARDS} By {set:GUARDS} we mean the set of all guards in the current consensus that are usable for all circuits and directory requests. (They must have the flags: Stable, Fast, V2Dir, Guard.) **Rationale** We require all guards to have the flags that we potentially need from any guard, so that all guards are usable for all circuits. ## The Sampled Guard Set. {#SAMPLED} We maintain a set, {set:SAMPLED_GUARDS}, that persists across invocations of Tor. It is a subset of the nodes ordered by a sample idx that we have seen listed as a guard in the consensus at some point. For each such guard, we record persistently: ```text - {pvar:ADDED_ON_DATE}: The date on which it was added to sampled_guards. We set this value to a point in the past, using RAND(now, {GUARD_LIFETIME}/10). See Appendix [RANDOM] below. - {pvar:ADDED_BY_VERSION}: The version of Tor that added it to sampled_guards. - {pvar:IS_LISTED}: Whether it was listed as a usable Guard in the _most recent_ consensus we have seen. - {pvar:FIRST_UNLISTED_AT}: If IS_LISTED is false, the publication date of the earliest consensus in which this guard was listed such that we have not seen it listed in any later consensus. Otherwise "None." We randomize this to a point in the past, based on RAND(added_at_time, {REMOVE_UNLISTED_GUARDS_AFTER} / 5) ``` For each guard in {SAMPLED_GUARDS}, we also record this data, non-persistently: ```text - {tvar:last_tried_connect}: A 'last tried to connect at' time. Default 'never'. - {tvar:is_reachable}: an "is reachable" tristate, with possible values { , , }. Default '.' [Note: "yes" is not strictly necessary, but I'm making it distinct from "maybe" anyway, to make our logic clearer. A guard is "maybe" reachable if it's worth trying. A guard is "yes" reachable if we tried it and succeeded.] [Note 2: This variable is, in fact, a combination of different context-sensitive variables depending on the _purpose_ for which we are selecting a guard. When we are selecing a guard for an ordinary circuit, we look at the regular {is_reachable}. But when we are selecting the guard for a one-hop directory circuit, we also look at an instance of {is_reachable} that tracks whether downloads of the types we are making have failed recently.] - {tvar:failing_since}: The first time when we failed to connect to this guard. Defaults to "never". Reset to "never" when we successfully connect to this guard. - {tvar:is_pending} A "pending" flag. This indicates that we are trying to build an exploratory circuit through the guard, and we don't know whether it will succeed. - {tvar:pending_since}: A timestamp. Set whenever we set {tvar:is_pending} to true; cleared whenever we set {tvar:is_pending} to false. NOTE ``` We require that {SAMPLED_GUARDS} contain at least {MIN_FILTERED_SAMPLE} guards from the consensus (if possible), but not more than {MAX_SAMPLE_THRESHOLD} of the number of guards in the consensus, and not more than {MAX_SAMPLE_SIZE} in total. (But if the maximum would be smaller than {MIN_FILTERED_SAMPLE}, we set the maximum at {MIN_FILTERED_SAMPLE}.) To add a new guard to {SAMPLED_GUARDS}, pick an entry at random from ({GUARDS} - {SAMPLED_GUARDS}), according to the path selection rules. We remove an entry from {SAMPLED_GUARDS} if: ```text * We have a live consensus, and {IS_LISTED} is false, and {FIRST_UNLISTED_AT} is over {REMOVE_UNLISTED_GUARDS_AFTER} days in the past. OR * We have a live consensus, and {ADDED_ON_DATE} is over {GUARD_LIFETIME} ago, *and* {CONFIRMED_ON_DATE} is either "never", or over {GUARD_CONFIRMED_MIN_LIFETIME} ago. ``` Note that {SAMPLED_GUARDS} does not depend on our configuration. It is possible that we can't actually connect to any of these guards. **Rationale** The {SAMPLED_GUARDS} set is meant to limit the total number of guards that a client will connect to in a given period. The upper limit on its size prevents us from considering too many guards. The first expiration mechanism is there so that our {SAMPLED_GUARDS} list does not accumulate so many dead guards that we cannot add new ones. The second expiration mechanism makes us rotate our guards slowly over time. Ordering the {SAMPLED_GUARDS} set in the order in which we sampled those guards and picking guards from that set according to this ordering improves load-balancing. It is closer to offer the expected usage of the guard nodes as per the path selection rules. The ordering also improves on another objective of this proposal: trying to resist an adversary pushing clients over compromised guards, since the adversary would need the clients to exhaust all their initial {SAMPLED_GUARDS} set before having a chance to use a newly deployed adversary node. ## The Usable Sample {#FILTERED} We maintain another set, {set:FILTERED_GUARDS}, that does not persist. It is derived from: ```text - {SAMPLED_GUARDS} - our current configuration, - the path bias information. ``` A guard is a member of {set:FILTERED_GUARDS} if and only if all of the following are true: ```text - It is a member of {SAMPLED_GUARDS}, with {IS_LISTED} set to true. - It is not disabled because of path bias issues. - It is not disabled because of ReachableAddresses policy, the ClientUseIPv4 setting, the ClientUseIPv6 setting, the FascistFirewall setting, or some other option that prevents using some addresses. - It is not disabled because of ExcludeNodes. - It is a bridge if UseBridges is true; or it is not a bridge if UseBridges is false. - Is included in EntryNodes if EntryNodes is set and UseBridges is not. (But see 2.B above). ``` We have an additional subset, {set:USABLE_FILTERED_GUARDS}, which is defined to be the subset of {FILTERED_GUARDS} where {is_reachable} is `` or ``. We try to maintain a requirement that {USABLE_FILTERED_GUARDS} contain at least {MIN_FILTERED_SAMPLE} elements: ```text Whenever we are going to sample from {USABLE_FILTERED_GUARDS}, and it contains fewer than {MIN_FILTERED_SAMPLE} elements, we add new elements to {SAMPLED_GUARDS} until one of the following is true: * {USABLE_FILTERED_GUARDS} is large enough, OR * {SAMPLED_GUARDS} is at its maximum size. ** Rationale ** ``` These filters are applied _after_ sampling: if we applied them before the sampling, then our sample would reflect the set of filtering restrictions that we had in the past. ## The confirmed-guard list. {#CONFIRMED} \[formerly USED_GUARDS\] We maintain a persistent ordered list, {list:CONFIRMED_GUARDS}. It contains guards that we have used before, in our preference order of using them. It is a subset of {SAMPLED_GUARDS}. For each guard in this list, we store persistently: - {pvar:IDENTITY} Its fingerprint. ```text - {pvar:CONFIRMED_ON_DATE} When we added this guard to {CONFIRMED_GUARDS}. Randomized to a point in the past as RAND(now, {GUARD_LIFETIME}/10). ``` We append new members to {CONFIRMED_GUARDS} when we mark a circuit built through a guard as "for user traffic." Whenever we remove a member from {SAMPLED_GUARDS}, we also remove it from {CONFIRMED_GUARDS}. ```text [Note: You can also regard the {CONFIRMED_GUARDS} list as a total ordering defined over a subset of {SAMPLED_GUARDS}.] ``` Definition: we call Guard A "higher priority" than another Guard B if, when A and B are both reachable, we would rather use A. We define priority as follows: ```text * Every guard in {CONFIRMED_GUARDS} has a higher priority than every guard not in {CONFIRMED_GUARDS}. * Among guards in {CONFIRMED_GUARDS}, the one appearing earlier on the {CONFIRMED_GUARDS} list has a higher priority. * Among guards that do not appear in {CONFIRMED_GUARDS}, {is_pending}==true guards have higher priority. * Among those, the guard with earlier {last_tried_connect} time has higher priority. * Finally, among guards that do not appear in {CONFIRMED_GUARDS} with {is_pending==false}, all have equal priority. ** Rationale ** ``` We add elements to this ordering when we have actually used them for building a usable circuit. We could mark them at some other time (such as when we attempt to connect to them, or when we actually connect to them), but this approach keeps us from committing to a guard before we actually use it for sensitive traffic. ## The Primary guards {#PRIMARY} We keep a run-time non-persistent ordered list of {list:PRIMARY_GUARDS}. It is a subset of {FILTERED_GUARDS}. It contains {N_PRIMARY_GUARDS} elements. To compute primary guards, take the ordered intersection of {CONFIRMED_GUARDS} and {FILTERED_GUARDS}, and take the first {N_PRIMARY_GUARDS} elements. If there are fewer than {N_PRIMARY_GUARDS} elements, append additional elements to PRIMARY_GUARDS chosen from ({FILTERED_GUARDS} - {CONFIRMED_GUARDS}), ordered in "sample order" (that is, by {ADDED_ON_DATE}). Once an element has been added to {PRIMARY_GUARDS}, we do not remove it until it is replaced by some element from {CONFIRMED_GUARDS}. That is: if a non-primary guard becomes confirmed and not every primary guard is confirmed, then the list of primary guards list is regenerated, first from the confirmed guards (as before), and then from any non-confirmed primary guards. Note that {PRIMARY_GUARDS} do not have to be in {USABLE_FILTERED_GUARDS}: they might be unreachable. **Rationale** These guards are treated differently from other guards. If one of them is usable, then we use it right away. For other guards {FILTERED_GUARDS}, if it's usable, then before using it we might first double-check whether perhaps one of the primary guards is usable after all. ## Retrying guards. {#RETRYING} (We run this process as frequently as needed. It can be done once a second, or just-in-time.) If a primary sampled guard's {is_reachable} status is ``, then we decide whether to update its {is_reachable} status to `` based on its {last_tried_connect} time, its {failing_since} time, and the {PRIMARY_GUARDS_RETRY_SCHED} schedule. If a non-primary sampled guard's {is_reachable} status is ``, then we decide whether to update its {is_reachable} status to `` based on its {last_tried_connect} time, its {failing_since} time, and the {GUARDS_RETRY_SCHED} schedule. **Rationale** An observation that a guard has been 'unreachable' only lasts for a given amount of time, since we can't infer that it's unreachable now from the fact that it was unreachable a few minutes ago. ## Circuit status {#CIRC-STATUS} Sometimes the guard selection algorithm will return a guard that we would always be happy to use; but in other cases, the guard selection algorithm can return a guard that we shouldn't use without gathering additional information. From the point of view of guard selection, every circuit we build is considered to be in one of these states: * `` * `` * `` * `` You may only attach streams to `` circuits. (Additionally, you may only send RENDEZVOUS messages, ESTABLISH_INTRO messages, and INTRODUCE messages on `` circuits.) The per-circuit state machine is: * New circuits are `` or ``. * A `` circuit may become ``, or may fail. * A `` circuit may become ``; may become ``; or may fail. * A `` circuit will become ``, or will be closed, or will fail. * A `` circuit remains `` until it fails or is closed. ```mermaid stateDiagram-v2 [*] --> usable_on_completion [*] --> usable_if_no_better_guard usable_on_completion --> complete usable_on_completion --> failed usable_if_no_better_guard --> usable_on_completion usable_if_no_better_guard --> waiting_for_better_guard usable_if_no_better_guard --> failed waiting_for_better_guard --> complete waiting_for_better_guard --> failed waiting_for_better_guard --> closed complete --> failed complete --> closed failed --> [*] closed --> [*] ``` Each of these transitions is described in sections below. ## Selecting guards for circuits. \[Section:SELECTING\] {#SELECTING} Now that we have described the various lists of guards, we can explain how guards are chosen for each circuit. We keep, as global transient state: * {tvar:last_time_on_internet} -- the last time at which we successfully used a circuit or connected to a guard. At startup we set this to "infinitely far in the past." As an input to the algorithm, we take a list of _restrictions_. These may include specific relays or families that we need to avoid. Here is the algorithm. It is given as a series of sub-algorithms, in decreasing order of preference from best case to worst case. When we want to build a circuit, and we need to pick a guard: * In the base case, if any entry in PRIMARY_GUARDS has {is_reachable} status of `` or ``, consider only such guards. Call them the "reachable primary guards". Start by considering the the first {NUM_USABLE_PRIMARY_GUARDS} (or {NUM_USABLE_PRIMARY_DIRECTORY_GUARDS}) reachable primary guards. Remove any guards that do not follow our path selection restrictions, to build a temporary list. If that temporary list contains at least one guard, choose a guard from that list uniformly at random. If the temporary list contains no guards, return the first guard from our reachable primary guard that does obey the path restriction. When selecting a guard according to this approach, its circuit is ``. \[Note: We do not use {is_pending} on primary guards, since we are willing to try to build multiple circuits through them before we know for sure whether they work, and since we will not use any non-primary guards until we are sure that the primary guards are all down. (XX is this good?)\] * Otherwise, if the ordered intersection of {CONFIRMED_GUARDS} and {USABLE_FILTERED_GUARDS} is nonempty, return the first entry in that intersection that has {is_pending} set to false. Set its value of {is_pending} to true, and set its {pending_since} to the current time. The circuit is now ``. (If all entries have {is_pending} true, pick the first one.) * Otherwise, if there is no such entry, select a member from {USABLE_FILTERED_GUARDS} in sample order. Set its {is_pending} field to true, and set its {pending_since} to the current time. The circuit is ``. * Otherwise, in the worst case, if USABLE_FILTERED_GUARDS is empty, we have exhausted all the sampled guards. In this case we proceed by marking all guards as `` reachable so that we can keep on trying circuits. Whenever we select a guard for a new circuit attempt, we update the {last_tried_connect} time for the guard to 'now.' In some cases (for example, when we need a certain directory feature, or when we need to avoid using a certain exit as a guard), we need to restrict the guards that we use for a single circuit. When this happens, we remember the restrictions that applied when choosing the guard for that circuit, since we will need them later (see \[UPDATE_WAITING\].). **Rationale** We're getting to the core of the algorithm here. Our main goals are to make sure that 1. If it's possible to use a primary guard, we do. 2. We probably use the first primary guard. So we only try non-primary guards if we're pretty sure that all the primary guards are down, and we only try a given primary guard if the earlier primary guards seem down. When we _do_ try non-primary guards, however, we only build one circuit through each, to give it a chance to succeed or fail. If ever such a circuit succeeds, we don't use it until we're pretty sure that it's the best guard we're getting. (see below). \[XXX timeout.\] ## When a circuit fails. {#ON_FAIL} When a circuit fails in a way that makes us conclude that a guard is not reachable, we take the following steps: ```text * Set the guard's {is_reachable} status to . If it had {is_pending} set to true, we make it non-pending and clear {pending_since}. * Close the circuit, of course. (This removes it from consideration by the algorithm in [UPDATE_WAITING].) * Update the list of waiting circuits. (See [UPDATE_WAITING] below.) ``` \[Note: the existing Tor logic will cause us to create more circuits in response to some of these steps; and also see \[ON_CONSENSUS\].\] \[Note 2: In the case of a one-hop circuit made for a directory request, it is possible for the request to fail _after_ the circuit is built: for example, if we ask for the latest consensus and we are told "404". In this case, we mark the appropriate directory-specific `{is_reachable}` instance for that guard to ``.\] > C tor implements the above "note 2" by treating requests for > directory guards for as if they had an > extra type of restriction, rather than a separate instance of > `{is_reachable}`. (For more on restrictions, see ["Selecting > Guards"](#SELECTING) above.) This requires the C tor > impementation to special-case this restriction type, so that > it is treated the same way as an `{is_reachable}` variable. **Rationale** See \[SELECTING\] above for rationale. ## When a circuit succeeds {#ON_SUCCESS} When a circuit succeeds in a way that makes us conclude that a guard _was_ reachable, we take these steps: ```text * We set its {is_reachable} status to . * We set its {failing_since} to "never". * If the guard was {is_pending}, we clear the {is_pending} flag and set {pending_since} to false. * If the guard was not a member of {CONFIRMED_GUARDS}, we add it to the end of {CONFIRMED_GUARDS}. * If this circuit was , this circuit is now . You may attach streams to this circuit, and use it for hidden services. * If this circuit was , it is now . You may not yet attach streams to it. Then check whether the {last_time_on_internet} is more than {INTERNET_LIKELY_DOWN_INTERVAL} seconds ago: * If it is, then mark all {PRIMARY_GUARDS} as "maybe" reachable. * If it is not, update the list of waiting circuits. (See [UPDATE_WAITING] below) ``` \[Note: the existing Tor logic will cause us to create more circuits in response to some of these steps; and see \[ON_CONSENSUS\].\] **Rationale** See \[SELECTING\] above for rationale. ## Updating the list of waiting circuits {#UPDATE_WAITING} We run this procedure whenever it's possible that a `` circuit might be ready to be called ``. ```text * If any circuit C1 is , AND: * All primary guards have reachable status of . * There is no circuit C2 that "blocks" C1. Then, upgrade C1 to . Definition: In the algorithm above, C2 "blocks" C1 if: * C2 obeys all the restrictions that C1 had to obey, AND * C2 has higher priority than C1, AND * Either C2 is , or C2 is , or C2 has been for no more than {NONPRIMARY_GUARD_CONNECT_TIMEOUT} seconds. We run this procedure periodically: * If any circuit stays in for more than {NONPRIMARY_GUARD_IDLE_TIMEOUT} seconds, time it out. **Rationale** ``` If we open a connection to a guard, we might want to use it immediately (if we're sure that it's the best we can do), or we might want to wait a little while to see if some other circuit which we like better will finish. When we mark a circuit ``, we don't close the lower-priority circuits immediately: we might decide to use them after all if the `` circuit goes down before {NONPRIMARY_GUARD_IDLE_TIMEOUT} seconds. ### Without a list of waiting circuits {#NO_CIRCLIST} As an alternative to the section \[SECTION:UPDATE_WAITING\] above, this section presents a new way to maintain guard status independently of tracking individual circuit status. This formulation gives a result equivalent or similar to the approach above, but simplifies the necessary communications between the guard and circuit subsystems. As before, when all primary guards are Unreachable, we need to try non-primary guards. We select the first such guard (in preference order) that is neither Unreachable nor Pending. Whenever we give out such a guard, if the guard's status is Unknown, then we call that guard "Pending" with its {is_pending} flag, until the attempt to use it succeeds or fails. We remember when the guard became Pending with the {pending_since variable}. After completing a circuit, the implementation must check whether its guard is usable. A guard's usability status may be "usable", "unusable", or "unknown". A guard is usable according to these rules: 1. Primary guards are always usable. 1. Non-primary guards are usable _for a given circuit_ if every guard earlier in the preference list is either unsuitable for that circuit (e.g. because of family restrictions), or marked as Unreachable, or has been pending for at least `{NONPRIMARY_GUARD_CONNECT_TIMEOUT}`. Non-primary guards are not usable _for a given circuit_ if some guard earlier in the preference list is suitable for the circuit _and_ Reachable. Non-primary guards are unusable if they have not become usable after `{NONPRIMARY_GUARD_IDLE_TIMEOUT}` seconds. 1. If a circuit's guard is not usable or unusable immediately, the circuit is not discarded; instead, it is kept (but not used) until the guard becomes usable or unusable. ## Whenever we get a new consensus. {#ON_CONSENSUS} We update {GUARDS}. For every guard in {SAMPLED_GUARDS}, we update {IS_LISTED} and {FIRST_UNLISTED_AT}. \[\*\*\] We remove entries from {SAMPLED_GUARDS} if appropriate, according to the sampled-guards expiration rules. If they were in {CONFIRMED_GUARDS}, we also remove them from {CONFIRMED_GUARDS}. We recompute {FILTERED_GUARDS}, and everything that derives from it, including {USABLE_FILTERED_GUARDS}, and {PRIMARY_GUARDS}. (Whenever one of the configuration options that affects the filter is updated, we repeat the process above, starting at the \[\*\*\] line.) ```text 4.11. Deciding whether to generate a new circuit. [Section:NEW_CIRCUIT_NEEDED] ``` We generate a new circuit when we don't have enough circuits either built or in-progress to handle a given stream, or an expected stream. For the purpose of this rule, we say that `` circuits are neither built nor in-progress; that `` circuits are built; and that the other states are in-progress. ```text 4.12. When we are missing descriptors. [Section:MISSING_DESCRIPTORS] ``` We need either a router descriptor or a microdescriptor in order to build a circuit through a guard. If we do not have such a descriptor for a guard, we can still use the guard for one-hop directory fetches, but not for longer circuits. (Also, when we are missing descriptors for our first {NUM_USABLE_PRIMARY_GUARDS} primary guards, we don't build circuits at all until we have fetched them.)