[tor-bugs] #25061 [Core Tor/Tor]: Relays consider it a bootstrapping failure if they can't extend for somebody else's circuit

Tor Bug Tracker & Wiki blackhole at torproject.org
Tue Mar 27 16:05:35 UTC 2018


#25061: Relays consider it a bootstrapping failure if they can't extend for
somebody else's circuit
-------------------------------------------------+-------------------------
 Reporter:  arma                                 |          Owner:
                                                 |  catalyst
     Type:  defect                               |         Status:
                                                 |  assigned
 Priority:  Medium                               |      Milestone:  Tor:
                                                 |  0.3.3.x-final
Component:  Core Tor/Tor                         |        Version:
 Severity:  Normal                               |     Resolution:
 Keywords:  backport-032, 033-must, stability,   |  Actual Points:
  033-triage-20180320, 033-included-20180320     |
Parent ID:                                       |         Points:
 Reviewer:                                       |        Sponsor:
-------------------------------------------------+-------------------------

Comment (by arma):

 The hint is that this warn happens through
 control_event_bootstrap_prob_or() which is in four places in
 connection_or.c. That call happens when an OR connection fails, and none
 of those four callsites check whether it's an origin circuit, that is,
 whether it's "our" circuit or somebody else's.

 You'll notice that somewhere along the line we wrapped those calls with
 {{{
    if (!authdir_mode_tests_reachability(options)
 }}}

 Commit d37fae2f is the commit from ancient history to skim, and commit
 818332e7 is the more recent commit in this area that should give you some
 more context.

 The fix might start with:

 * In connection_or.c, when we're considering whether to call
 control_event_bootstrap_prob_or, break that "if
 authdir_mode_tests_reachability" check out its into own function, called
 something like "could this connection be a bootstrap attempt", which
 checks if it's a reachability test, but also does more checks.

 The trouble here is that in these "more checks", we want to check if there
 are any origin circuits pending on that connection attempt. And after some
 digging it looks like we don't actually know that here.

 So the first option I thought of is that in that "could it be" function,
 we want to call circuit_get_all_pending_on_channel(), and then iterate
 over the resulting list to see if any of them are CIRCUIT_IS_ORIGIN().
 That's certainly the most straightforward solution, in that it doesn't go
 mess with other code much. But it's kind of crummy, because we're walking
 another list (twice, in fact) every time there's a connection failure.
 It's not *so* bad though, because somewhere along the line somebody went
 to the trouble of making a separate list just for the circuits that are
 pending a channel attempt -- lucky us.

 My second thought is: wait, we already *do* walk that list, later on, when
 we're trying to handle all of the circuits that were waiting for this
 channel to succeed or fail. That happens in circuit_n_chan_done(). So in
 the
 {{{
       if (!status) { /* chan failed; close circ */
 }}}
 clause in that loop, we could check if it's an origin circuit, and if so
 set a flag that makes us do a bootstrap complaint if the flag was ever set
 to 1.

 But! circuit_n_chan_done() gets called from
 connection_or_about_to_close(), which is way after we know *why* the conn
 closed. So that's still not best.

 Could we add a field to or_connection_t which was why the connection
 failed, and memorize it when it failed, and then use it during
 circuit_n_chan_done() to report bootstrap issues accurately? Maybe --
 let's call that '''approach one'''.

 A third option is: how about we set a flag in or_connection_t, which is
 "has an origin circuit ever been interested in my outcome", and that flag
 starts off as 0, and it gets set to 1 when we're about to add a circuit to
 {{{circuits_pending_chans}}} on a circuit where {{{CIRCUIT_IS_ORIGIN()}}}
 is true.

 But! We only add to {{{circuits_pending_chans}}} inside
 {{{circuit_set_state()}}}, and that function doesn't know which chan or
 conn we're planning to attach this circ to.

 The better callsite would be
 circuit_handle_first_hop(), which is entirely for origin circuits. There
 are two cases we need to handle here. One is when there isn't a good conn
 available, and we decide to launch one in this circuit. That's the easy
 case: if channel_connect_for_circuit() returns an n_chan, we set the "we
 wanted this channel for an origin circuit" flag on it right then. The
 other is if channel_get_for_extend returned NULL but didn't set
 should_launch, meaning there is a conn somewhere out there, but it's not
 ready yet -- and we can't easily get ahold of it from this function.

 For that second case, check out channel_get_for_extend(). There are two
 places that call it: circuit_handle_first_hop(), which is for origin
 circuits, and circuit_extend(), which is for handling EXTEND cells so it
 is explicitly not for origin circuits. If we added a flag to that
 function, to say whether it's an origin circuit we're asking on behalf of,
 then *it* can mark conns that had origin circuits asking about them. The
 only case I can see in that function where we would need to mark the chan
 is when we're incrementing n_inprogress_goodaddr.

 So, let's call that "add a flag to the channel and turn it on when an
 origin circuit inquired" idea '''approach two'''.

--
Ticket URL: <https://trac.torproject.org/projects/tor/ticket/25061#comment:8>
Tor Bug Tracker & Wiki <https://trac.torproject.org/>
The Tor Project: anonymity online


More information about the tor-bugs mailing list