Skip to main content

tor_proto/circuit/
circhop.rs

1//! Module exposing structures relating to a reactor's view of a circuit hop.
2
3// TODO(relay): don't import from the client module
4use crate::client::circuit::handshake::RelayCryptLayerProtocol;
5
6use crate::ccparams::CongestionControlParams;
7use crate::circuit::CircParameters;
8use crate::congestion::{CongestionControl, sendme};
9use crate::memquota::{SpecificAccount, StreamAccount};
10use crate::stream::CloseStreamBehavior;
11use crate::stream::SEND_WINDOW_INIT;
12use crate::stream::StreamMpscSender;
13use crate::stream::cmdcheck::{AnyCmdChecker, StreamStatus};
14use crate::stream::flow_ctrl::params::FlowCtrlParameters;
15use crate::stream::flow_ctrl::state::{FlowCtrlHooks, StreamFlowCtrl, StreamRateLimit};
16use crate::stream::flow_ctrl::xon_xoff::reader::DrainRateRequest;
17use crate::stream::queue::{StreamQueueReceiver, stream_queue};
18use crate::streammap::{
19    self, EndSentStreamEnt, OpenStreamEnt, ShouldSendEnd, StreamEntMut, StreamMap,
20};
21use crate::util::notify::{NotifyReceiver, NotifySender};
22use crate::{Error, HopNum, Result};
23
24use derive_deftly::Deftly;
25use postage::watch;
26use safelog::sensitive as sv;
27use tracing::{debug, trace};
28
29use tor_cell::chancell::{BoxedCellBody, CircId};
30use tor_cell::relaycell::extend::{CcRequest, CircRequestExt};
31use tor_cell::relaycell::flow_ctrl::{Xoff, Xon, XonKBpsEwma};
32use tor_cell::relaycell::msg::AnyRelayMsg;
33use tor_cell::relaycell::{
34    AnyRelayMsgOuter, RelayCellDecoder, RelayCellDecoderResult, RelayCellFormat, RelayCmd,
35    StreamId, UnparsedRelayMsg,
36};
37use tor_error::{Bug, ErrorKind, HasKind, internal, into_internal};
38use tor_memquota::derive_deftly_template_HasMemoryCost;
39use tor_memquota::mq_queue::{ChannelSpec as _, MpscSpec};
40use tor_protover::named;
41use tor_rtcompat::DynTimeProvider;
42
43use std::num::NonZeroU32;
44use std::pin::Pin;
45use std::result::Result as StdResult;
46use std::sync::{Arc, Mutex};
47use web_time_compat::Instant;
48
49#[cfg(test)]
50use tor_cell::relaycell::msg::SendmeTag;
51
52#[cfg(feature = "relay")]
53use {
54    crate::ccparams::{Algorithm, AlgorithmDiscriminants},
55    crate::circuit::HandshakeSubprotocols,
56    crate::relay::{CircNetParameters, CongestionControlNetParams},
57};
58
59use cfg_if::cfg_if;
60
61/// The size of the stream's outbound RELAY message queue.
62// TODO(tuning): figure out if this is a good size for this buffer
63const CIRCUIT_BUFFER_SIZE: usize = 128;
64
65/// Type of negotiation that we'll be performing as we establish a hop.
66///
67/// Determines what flavor of extensions we can send and receive, which in turn
68/// limits the hop settings we can negotiate.
69///
70// TODO-CGO: This is likely to be refactored when we finally add support for
71// HsV3+CGO, which will require refactoring
72#[derive(Debug, Clone, Copy, Eq, PartialEq)]
73pub(crate) enum HopNegotiationType {
74    /// We're using a handshake in which extension-based negotiation cannot occur.
75    None,
76    /// We're using the HsV3-ntor handshake, in which the client can send extensions,
77    /// but the server cannot.
78    ///
79    /// As a special case, the default relay encryption protocol is the hsv3
80    /// variant of Tor1.
81    //
82    // We would call this "HalfDuplex" or something, but we do not expect to add
83    // any more handshakes of this type.
84    HsV3,
85    /// We're using a handshake in which both client and relay can send extensions.
86    Full,
87}
88
89/// The settings we use for single hop of a circuit.
90///
91/// Unlike [`CircParameters`], this type is crate-internal.
92/// We construct it based on our settings from the circuit,
93/// and from the hop's actual capabilities.
94/// Then, we negotiate with the hop as part of circuit
95/// creation/extension to determine the actual settings that will be in use.
96/// Finally, we use those settings to construct the negotiated circuit hop.
97//
98// TODO: Relays should probably derive an instance of this type too, as
99// part of the circuit creation handshake.
100#[derive(Clone, Debug)]
101pub(crate) struct HopSettings {
102    /// The negotiated congestion control settings for this hop .
103    pub(crate) ccontrol: CongestionControlParams,
104
105    /// Flow control parameters that will be used for streams on this hop.
106    pub(crate) flow_ctrl_params: FlowCtrlParameters,
107
108    /// Maximum number of permitted incoming relay cells for this hop.
109    pub(crate) n_incoming_cells_permitted: Option<u32>,
110
111    /// Maximum number of permitted outgoing relay cells for this hop.
112    pub(crate) n_outgoing_cells_permitted: Option<u32>,
113
114    /// The relay cell encryption algorithm and cell format for this hop.
115    relay_crypt_protocol: RelayCryptLayerProtocol,
116}
117
118impl HopSettings {
119    /// Construct a new `HopSettings` based on `params` (a set of circuit parameters)
120    /// and `caps` (a set of protocol capabilities for a circuit target).
121    ///
122    /// The resulting settings will represent what the client would prefer to negotiate
123    /// (determined by `params`),
124    /// as modified by what the target relay is believed to support (represented by `caps`).
125    ///
126    /// This represents the `HopSettings` in a pre-negotiation state:
127    /// the circuit negotiation process will modify it.
128    #[allow(clippy::unnecessary_wraps)] // likely to become fallible in the future.
129    pub(crate) fn from_params_and_caps(
130        hoptype: HopNegotiationType,
131        params: &CircParameters,
132        caps: &tor_protover::Protocols,
133    ) -> Result<Self> {
134        let mut ccontrol = params.ccontrol.clone();
135        match ccontrol.alg() {
136            crate::ccparams::Algorithm::FixedWindow(_) => {}
137            crate::ccparams::Algorithm::Vegas(_) => {
138                // If the target doesn't support FLOWCTRL_CC, we can't use Vegas.
139                if !caps.supports_named_subver(named::FLOWCTRL_CC) {
140                    ccontrol.use_fallback_alg();
141                }
142            }
143        };
144        if hoptype == HopNegotiationType::None {
145            ccontrol.use_fallback_alg();
146        }
147        let ccontrol = ccontrol; // drop mut
148
149        // Negotiate CGO if it is supported, if CC is also supported,
150        // and if CGO is available on this relay.
151        let relay_crypt_protocol = match hoptype {
152            HopNegotiationType::None => RelayCryptLayerProtocol::Tor1(RelayCellFormat::V0),
153            HopNegotiationType::HsV3 => {
154                // TODO-CGO: Support CGO when available.
155                cfg_if! {
156                    if #[cfg(all(feature = "hs-common", feature = "flowctl-cc", feature = "counter-galois-onion"))] {
157                        if ccontrol.alg().compatible_with_cgo() && caps.supports_named_subver(named::RELAY_CRYPT_CGO) {
158                            RelayCryptLayerProtocol::Cgo
159                        } else {
160                            RelayCryptLayerProtocol::HsV3(RelayCellFormat::V0)
161                        }
162                    } else if #[cfg(feature = "hs-common")] {
163                            RelayCryptLayerProtocol::HsV3(RelayCellFormat::V0)
164                    } else {
165                        return Err(
166                            tor_error::internal!("Unexpectedly tried to negotiate HsV3 without support!").into(),
167                        );
168                    }
169                }
170            }
171            HopNegotiationType::Full => {
172                cfg_if! {
173                    if #[cfg(all(feature = "flowctl-cc", feature = "counter-galois-onion"))] {
174                        #[allow(clippy::overly_complex_bool_expr)]
175                        if  ccontrol.alg().compatible_with_cgo()
176                            && caps.supports_named_subver(named::RELAY_NEGOTIATE_SUBPROTO)
177                            && caps.supports_named_subver(named::RELAY_CRYPT_CGO)
178                        {
179                            RelayCryptLayerProtocol::Cgo
180                        } else {
181                            RelayCryptLayerProtocol::Tor1(RelayCellFormat::V0)
182                        }
183                    } else {
184                        RelayCryptLayerProtocol::Tor1(RelayCellFormat::V0)
185                    }
186                }
187            }
188        };
189
190        Ok(Self {
191            ccontrol,
192            flow_ctrl_params: params.flow_ctrl.clone(),
193            relay_crypt_protocol,
194            n_incoming_cells_permitted: params.n_incoming_cells_permitted,
195            n_outgoing_cells_permitted: params.n_outgoing_cells_permitted,
196        })
197    }
198
199    /// Build a [`HopSettings`] from the parameters requested during a circuit handshake.
200    //
201    // We disable `unused` warnings at the root of tor-proto,
202    // but it's nice to have here so we re-enable it.
203    #[warn(unused)]
204    #[cfg(feature = "relay")]
205    pub(crate) fn from_handshake_params(
206        circ_net_params: CircNetParameters,
207        cc_algorithm: AlgorithmDiscriminants,
208        subprotos_requested: HandshakeSubprotocols,
209    ) -> StdResult<Self, HandshakeParamsError> {
210        // Unpack everything to make sure that we aren't missing anything
211        // (otherwise clippy would warn).
212        let CircNetParameters {
213            cc:
214                CongestionControlNetParams {
215                    fixed_window,
216                    vegas_exit,
217                    cwnd,
218                    rtt,
219                    flow_ctrl,
220                },
221        } = circ_net_params;
222
223        let HandshakeSubprotocols { relay_crypt_cgo } = subprotos_requested;
224
225        // TODO: We have similar logic in and around `HopSettings` that deals with determining the
226        // crypt protocol and cc algorithm to use. We might want to try to dedup some of this, or
227        // make it more self-contained. This is a bit tricky though since the code is used in
228        // different situations and the inputs are not the same.
229        let (cc_algorithm, relay_crypt_protocol) = match (cc_algorithm, relay_crypt_cgo) {
230            (AlgorithmDiscriminants::FixedWindow, false) => (
231                Algorithm::FixedWindow(fixed_window),
232                RelayCryptLayerProtocol::Tor1(RelayCellFormat::V0),
233            ),
234            (AlgorithmDiscriminants::FixedWindow, true) => {
235                return Err(HandshakeParamsError::IncompatibleParams(
236                    "requested CGO but not congestion control",
237                ));
238            }
239            (AlgorithmDiscriminants::Vegas, false) => (
240                Algorithm::Vegas(vegas_exit),
241                RelayCryptLayerProtocol::Tor1(RelayCellFormat::V0),
242            ),
243            (AlgorithmDiscriminants::Vegas, true) => {
244                (Algorithm::Vegas(vegas_exit), RelayCryptLayerProtocol::Cgo)
245            }
246        };
247
248        // TODO(arti#2442): The builder pattern here seems like a footgun.
249        let ccontrol = CongestionControlParams::builder()
250            .alg(cc_algorithm)
251            .fixed_window_params(fixed_window)
252            .cwnd_params(cwnd)
253            .rtt_params(rtt)
254            .build()
255            .map_err(into_internal!("Could not build `CongestionControlParams`"))?;
256
257        Ok(Self {
258            ccontrol,
259            flow_ctrl_params: flow_ctrl,
260            relay_crypt_protocol,
261            n_incoming_cells_permitted: None,
262            n_outgoing_cells_permitted: None,
263        })
264    }
265
266    /// Return the negotiated relay crypto protocol.
267    pub(crate) fn relay_crypt_protocol(&self) -> RelayCryptLayerProtocol {
268        self.relay_crypt_protocol
269    }
270
271    /// Return the client circuit-creation extensions that we should use in order to negotiate
272    /// these circuit hop parameters.
273    #[allow(clippy::unnecessary_wraps)]
274    pub(crate) fn circuit_request_extensions(&self) -> Result<Vec<CircRequestExt>> {
275        // allow 'unused_mut' because of the combinations of `cfg` conditions below
276        #[allow(unused_mut)]
277        let mut client_extensions = Vec::new();
278
279        #[allow(unused, unused_mut)]
280        let mut cc_extension_set = false;
281
282        if self.ccontrol.is_enabled() {
283            cfg_if::cfg_if! {
284                if #[cfg(feature = "flowctl-cc")] {
285                    client_extensions.push(CircRequestExt::CcRequest(CcRequest::default()));
286                    cc_extension_set = true;
287                } else {
288                    return Err(
289                        tor_error::internal!(
290                            "Congestion control is enabled on this circuit, but 'flowctl-cc' feature is not enabled"
291                        )
292                        .into()
293                    );
294                }
295            }
296        }
297
298        // See whether we need to send a list of required protocol capabilities.
299        // These aren't "negotiated" per se; they're simply demanded.
300        // The relay will refuse the circuit if it doesn't support all of them,
301        // and if any of them isn't supported in the SubprotocolRequest extension.
302        //
303        // (In other words, don't add capabilities here just because you want the
304        // relay to have them! They must be explicitly listed as supported for use
305        // with this extension. For the current list, see
306        // https://spec.torproject.org/tor-spec/create-created-cells.html#subproto-request)
307        //
308        // TODO: Should this use `HandshakeSubprotocols` so that the above comment has some
309        // compile-time checks?
310        #[allow(unused_mut)]
311        let mut required_protocol_capabilities: Vec<tor_protover::NamedSubver> = Vec::new();
312
313        #[cfg(feature = "counter-galois-onion")]
314        if matches!(self.relay_crypt_protocol(), RelayCryptLayerProtocol::Cgo) {
315            if !cc_extension_set {
316                return Err(tor_error::internal!("Tried to negotiate CGO without CC.").into());
317            }
318            required_protocol_capabilities.push(tor_protover::named::RELAY_CRYPT_CGO);
319        }
320
321        if !required_protocol_capabilities.is_empty() {
322            client_extensions.push(CircRequestExt::SubprotocolRequest(
323                required_protocol_capabilities.into_iter().collect(),
324            ));
325        }
326
327        Ok(client_extensions)
328    }
329}
330
331#[cfg(test)]
332impl std::default::Default for CircParameters {
333    fn default() -> Self {
334        Self {
335            extend_by_ed25519_id: true,
336            ccontrol: crate::congestion::test_utils::params::build_cc_fixed_params(),
337            flow_ctrl: FlowCtrlParameters::defaults_for_tests(),
338            n_incoming_cells_permitted: None,
339            n_outgoing_cells_permitted: None,
340        }
341    }
342}
343
344/// An error that can occur when building a [`HopSettings`] using parameters requested during a
345/// circuit handshake.
346#[derive(Clone, Debug, thiserror::Error)]
347pub(crate) enum HandshakeParamsError {
348    /// The provided parameters are incompatible with each other.
349    #[error("The provided handshake parameters are incompatible with each other: {0}")]
350    IncompatibleParams(&'static str),
351    /// An internal error.
352    #[error("Internal error")]
353    Internal(#[from] tor_error::Bug),
354}
355
356impl HasKind for HandshakeParamsError {
357    fn kind(&self) -> ErrorKind {
358        match self {
359            Self::IncompatibleParams(_) => ErrorKind::TorProtocolViolation,
360            Self::Internal(_) => ErrorKind::Internal,
361        }
362    }
363}
364
365impl CircParameters {
366    /// Constructor
367    pub fn new(
368        extend_by_ed25519_id: bool,
369        ccontrol: CongestionControlParams,
370        flow_ctrl: FlowCtrlParameters,
371    ) -> Self {
372        Self {
373            extend_by_ed25519_id,
374            ccontrol,
375            flow_ctrl,
376            n_incoming_cells_permitted: None,
377            n_outgoing_cells_permitted: None,
378        }
379    }
380}
381
382/// Instructions for sending a RELAY cell.
383///
384/// This instructs a circuit reactor to send a RELAY cell to a given target
385/// (a hop, if we are a client, or the client, if we are a relay).
386#[derive(educe::Educe)]
387#[educe(Debug)]
388pub(crate) struct SendRelayCell {
389    /// The hop number, or `None` if we are a relay.
390    pub(crate) hop: Option<HopNum>,
391    /// Whether to use a RELAY_EARLY cell.
392    pub(crate) early: bool,
393    /// The cell to send.
394    pub(crate) cell: AnyRelayMsgOuter,
395}
396
397/// The inbound state of a hop.
398pub(crate) struct CircHopInbound {
399    /// Decodes relay cells received from this hop.
400    decoder: RelayCellDecoder,
401    /// Remaining permitted incoming relay cells from this hop, plus 1.
402    ///
403    /// (In other words, `None` represents no limit,
404    /// `Some(1)` represents an exhausted limit,
405    /// and `Some(n)` means that n-1 more cells may be received.)
406    ///
407    /// If this ever decrements from Some(1), then the circuit must be torn down with an error.
408    n_incoming_cells_permitted: Option<NonZeroU32>,
409}
410
411/// The outbound state of a hop.
412pub(crate) struct CircHopOutbound {
413    /// Congestion control object.
414    ///
415    /// This object is also in charge of handling circuit level SENDME logic for this hop.
416    ccontrol: Arc<Mutex<CongestionControl>>,
417    /// Map from stream IDs to streams.
418    ///
419    /// We store this with the reactor instead of the circuit, since the
420    /// reactor needs it for every incoming cell on a stream, whereas
421    /// the circuit only needs it when allocating new streams.
422    ///
423    /// NOTE: this is behind a mutex because the client reactor polls the `StreamMap`s
424    /// of all hops concurrently, in a `FuturesUnordered`. Without the mutex,
425    /// this wouldn't be possible, because it would mean holding multiple
426    /// mutable references to `self` (the reactor). Note, however,
427    /// that there should never be any contention on this mutex:
428    /// we never create more than one
429    /// `CircHopList::ready_streams_iterator()` stream
430    /// at a time, and we never clone/lock the hop's `StreamMap` outside of it.
431    ///
432    /// Additionally, the stream map of the last hop (join point) of a conflux tunnel
433    /// is shared with all the circuits in the tunnel.
434    map: Arc<Mutex<StreamMap>>,
435    /// Format to use for relay cells.
436    //
437    // When we have packed/fragmented cells, this may be replaced by a RelayCellEncoder.
438    relay_format: RelayCellFormat,
439    /// Flow control parameters for new streams.
440    flow_ctrl_params: Arc<FlowCtrlParameters>,
441    /// Remaining permitted outgoing relay cells from this hop, plus 1.
442    ///
443    /// If this ever decrements from Some(1), then the circuit must be torn down with an error.
444    n_outgoing_cells_permitted: Option<NonZeroU32>,
445}
446
447impl CircHopInbound {
448    /// Create a new [`CircHopInbound`].
449    pub(crate) fn new(decoder: RelayCellDecoder, settings: &HopSettings) -> Self {
450        Self {
451            decoder,
452            n_incoming_cells_permitted: settings.n_incoming_cells_permitted.map(cvt),
453        }
454    }
455
456    /// Parse a RELAY or RELAY_EARLY cell body.
457    ///
458    /// Requires that the cryptographic checks on the message have already been
459    /// performed
460    pub(crate) fn decode(&mut self, cell: BoxedCellBody) -> Result<RelayCellDecoderResult> {
461        self.decoder
462            .decode(cell)
463            .map_err(|e| Error::from_bytes_err(e, "relay cell"))
464    }
465
466    /// Decrement the limit of inbound cells that may be received from this hop; give
467    /// an error if it would reach zero.
468    pub(crate) fn decrement_cell_limit(&mut self) -> Result<()> {
469        try_decrement_cell_limit(&mut self.n_incoming_cells_permitted)
470            .map_err(|_| Error::ExcessInboundCells)
471    }
472}
473
474impl CircHopOutbound {
475    /// Create a new [`CircHopOutbound`].
476    pub(crate) fn new(
477        ccontrol: Arc<Mutex<CongestionControl>>,
478        relay_format: RelayCellFormat,
479        flow_ctrl_params: Arc<FlowCtrlParameters>,
480        settings: &HopSettings,
481    ) -> Self {
482        Self {
483            ccontrol,
484            map: Arc::new(Mutex::new(StreamMap::new())),
485            relay_format,
486            flow_ctrl_params,
487            n_outgoing_cells_permitted: settings.n_outgoing_cells_permitted.map(cvt),
488        }
489    }
490
491    /// Start a stream. Creates an entry in the stream map with the given channels, and sends the
492    /// `message` to the provided hop.
493    pub(crate) fn begin_stream(
494        &mut self,
495        hop: Option<HopNum>,
496        message: AnyRelayMsg,
497        time_prov: &DynTimeProvider,
498        cmd_checker: AnyCmdChecker,
499        memquota: &StreamAccount,
500    ) -> Result<(SendRelayCell, StreamId, ReactorStreamComponents)> {
501        // TODO: This has a lot of duplicated code with `Self::add_ent_with_id()`.
502
503        // A channel for the reactor to inform the writer of a new rate limit.
504        let (rate_limit_tx, rate_limit_rx) = watch::channel_with(StreamRateLimit::MAX);
505
506        // A channel for the reactor to request a new drain rate from the reader.
507        // Typically this notification will be sent after an XOFF is sent so that the reader can
508        // send us a new drain rate when the stream data queue becomes empty.
509        let mut drain_rate_request_tx = NotifySender::new_typed();
510        let drain_rate_request_rx = drain_rate_request_tx.subscribe();
511
512        let flow_ctrl = self.build_flow_ctrl(rate_limit_tx, drain_rate_request_tx)?;
513
514        let stream_queue_max_len = flow_ctrl.inbound_queue_max_len();
515
516        // A queue for inbound RELAY messages.
517        let (sender, receiver) = stream_queue(stream_queue_max_len, memquota, time_prov)?;
518
519        // A queue for outbound RELAY messages.
520        let (msg_tx, msg_rx) = MpscSpec::new(CIRCUIT_BUFFER_SIZE)
521            .new_mq(time_prov.clone(), memquota.as_raw_account())?;
522
523        let r = self.map.lock().expect("lock poisoned").add_ent(
524            sender,
525            msg_rx,
526            flow_ctrl,
527            cmd_checker,
528        )?;
529        let cell = AnyRelayMsgOuter::new(Some(r), message);
530
531        let stream_components = ReactorStreamComponents {
532            stream_inbound_rx: receiver,
533            stream_outbound_tx: msg_tx,
534            rate_limit_rx,
535            drain_rate_request_rx,
536        };
537
538        Ok((
539            SendRelayCell {
540                hop,
541                early: false,
542                cell,
543            },
544            r,
545            stream_components,
546        ))
547    }
548
549    /// Close the stream associated with `id` because the stream was dropped.
550    ///
551    /// If we have not already received an END cell on this stream, send one.
552    /// If no END cell is specified, an END cell with the reason byte set to
553    /// REASON_MISC will be sent.
554    ///
555    // Note(relay): `circ_uniq_id` is an opaque displayable type
556    // because relays use a different circuit ID type
557    // than clients. Eventually, we should probably make
558    // them both use the same ID type, or have a nicer approach here
559    #[allow(clippy::too_many_arguments)]
560    pub(crate) fn close_stream(
561        &mut self,
562        circ_uniq_id: impl std::fmt::Display,
563        circ_id: CircId,
564        id: StreamId,
565        hop: Option<HopNum>,
566        message: CloseStreamBehavior,
567        why: streammap::TerminateReason,
568        expiry: Instant,
569    ) -> Result<Option<SendRelayCell>> {
570        let should_send_end = self
571            .map
572            .lock()
573            .expect("lock poisoned")
574            .terminate(id, why, expiry)?;
575        trace!(
576            circ_uniq_id = %circ_uniq_id,
577            circ_id = %circ_id,
578            stream_id = %id,
579            should_send_end = ?should_send_end,
580            "Ending stream",
581        );
582        // TODO: I am about 80% sure that we only send an END cell if
583        // we didn't already get an END cell.  But I should double-check!
584        if let (ShouldSendEnd::Send, CloseStreamBehavior::SendEnd(end_message)) =
585            (should_send_end, message)
586        {
587            let end_cell = AnyRelayMsgOuter::new(Some(id), end_message.into());
588            let cell = SendRelayCell {
589                hop,
590                early: false,
591                cell: end_cell,
592            };
593
594            return Ok(Some(cell));
595        }
596        Ok(None)
597    }
598
599    /// Check if we should send an XON message.
600    ///
601    /// If we should, then returns the XON message that should be sent.
602    pub(crate) fn maybe_send_xon(
603        &mut self,
604        rate: XonKBpsEwma,
605        id: StreamId,
606    ) -> Result<Option<Xon>> {
607        // the call below will return an error if XON/XOFF aren't supported,
608        // so we check for support here
609        if !self
610            .ccontrol()
611            .lock()
612            .expect("poisoned lock")
613            .uses_xon_xoff()
614        {
615            return Ok(None);
616        }
617
618        let mut map = self.map.lock().expect("lock poisoned");
619        let Some(StreamEntMut::Open(ent)) = map.get_mut(id) else {
620            // stream went away
621            return Ok(None);
622        };
623
624        ent.maybe_send_xon(rate)
625    }
626
627    /// Check if we should send an XOFF message.
628    ///
629    /// If we should, then returns the XOFF message that should be sent.
630    pub(crate) fn maybe_send_xoff(&mut self, id: StreamId) -> Result<Option<Xoff>> {
631        // the call below will return an error if XON/XOFF aren't supported,
632        // so we check for support here
633        if !self
634            .ccontrol()
635            .lock()
636            .expect("poisoned lock")
637            .uses_xon_xoff()
638        {
639            return Ok(None);
640        }
641
642        let mut map = self.map.lock().expect("lock poisoned");
643        let Some(StreamEntMut::Open(ent)) = map.get_mut(id) else {
644            // stream went away
645            return Ok(None);
646        };
647
648        ent.maybe_send_xoff()
649    }
650
651    /// Return the format that is used for relay cells sent to this hop.
652    ///
653    /// For the most part, this format isn't necessary to interact with a CircHop;
654    /// it becomes relevant when we are deciding _what_ we can encode for the hop.
655    pub(crate) fn relay_cell_format(&self) -> RelayCellFormat {
656        self.relay_format
657    }
658
659    /// Delegate to CongestionControl, for testing purposes
660    #[cfg(test)]
661    pub(crate) fn send_window_and_expected_tags(&self) -> (u32, Vec<SendmeTag>) {
662        self.ccontrol()
663            .lock()
664            .expect("poisoned lock")
665            .send_window_and_expected_tags()
666    }
667
668    /// Return the number of open streams on this hop.
669    ///
670    /// WARNING: because this locks the stream map mutex,
671    /// it should never be called from a context where that mutex is already locked.
672    pub(crate) fn n_open_streams(&self) -> usize {
673        self.map.lock().expect("lock poisoned").n_open_streams()
674    }
675
676    /// Return a reference to our CongestionControl object.
677    pub(crate) fn ccontrol(&self) -> &Arc<Mutex<CongestionControl>> {
678        &self.ccontrol
679    }
680
681    /// We're about to send `msg`.
682    ///
683    /// See [`OpenStreamEnt::about_to_send`](crate::streammap::OpenStreamEnt::about_to_send).
684    //
685    // TODO prop340: This should take a cell or similar, not a message.
686    //
687    // Note(relay): `circ_uniq_id` is an opaque displayable type
688    // because relays use a different circuit ID type
689    // than clients. Eventually, we should probably make
690    // them both use the same ID type, or have a nicer approach here
691    pub(crate) fn about_to_send(
692        &mut self,
693        circ_uniq_id: impl std::fmt::Display,
694        circ_id: CircId,
695        stream_id: StreamId,
696        msg: &AnyRelayMsg,
697    ) -> Result<()> {
698        let mut hop_map = self.map.lock().expect("lock poisoned");
699        let Some(StreamEntMut::Open(ent)) = hop_map.get_mut(stream_id) else {
700            // This can happen when we have outgoing data queued when we received an END.
701            // We shouldn't return an error here since it would close the circuit along with all
702            // other streams, and instead we just let the caller send this message anyways.
703            // Also the caller only calls `about_to_send()` for DATA cells,
704            // which means that other non-DATA cells don't hit this code path and are always sent,
705            // and so we should handle all cell types consistently.
706            // TODO: We should drop the message and not send it,
707            // but the caller of `about_to_send()` isn't designed to handle fallible sends
708            // so it would need some refactoring to handle this.
709            debug!(
710                circ_uniq_id = %circ_uniq_id,
711                circ_id = %circ_id,
712                stream_id = %stream_id,
713                "sending a relay cell for non-existent or non-open stream!",
714            );
715            return Ok(());
716        };
717
718        ent.about_to_send(msg)
719    }
720
721    /// Add an entry to this map using the specified StreamId.
722    #[cfg(any(feature = "hs-service", feature = "relay"))]
723    pub(crate) fn add_ent_with_id(
724        &self,
725        time_prov: &DynTimeProvider,
726        stream_id: StreamId,
727        cmd_checker: AnyCmdChecker,
728        memquota: &StreamAccount,
729    ) -> Result<ReactorStreamComponents> {
730        // TODO: This has a lot of duplicated code with `Self::begin_stream()`.
731
732        // A channel for the reactor to inform the writer of a new rate limit.
733        let (rate_limit_tx, rate_limit_rx) = watch::channel_with(StreamRateLimit::MAX);
734
735        // A channel for the reactor to request a new drain rate from the reader.
736        // Typically this notification will be sent after an XOFF is sent so that the reader can
737        // send us a new drain rate when the stream data queue becomes empty.
738        let mut drain_rate_request_tx = NotifySender::new_typed();
739        let drain_rate_request_rx = drain_rate_request_tx.subscribe();
740
741        let flow_ctrl = self.build_flow_ctrl(rate_limit_tx, drain_rate_request_tx)?;
742
743        let stream_queue_max_len = flow_ctrl.inbound_queue_max_len();
744
745        // A queue for inbound RELAY messages.
746        let (sender, receiver) = stream_queue(stream_queue_max_len, memquota, time_prov)?;
747
748        // A queue for outbound RELAY messages.
749        let (msg_tx, msg_rx) = MpscSpec::new(CIRCUIT_BUFFER_SIZE)
750            .new_mq(time_prov.clone(), memquota.as_raw_account())?;
751
752        let mut hop_map = self.map.lock().expect("lock poisoned");
753        hop_map.add_ent_with_id(sender, msg_rx, flow_ctrl, stream_id, cmd_checker)?;
754
755        Ok(ReactorStreamComponents {
756            stream_inbound_rx: receiver,
757            stream_outbound_tx: msg_tx,
758            rate_limit_rx,
759            drain_rate_request_rx,
760        })
761    }
762
763    /// Builds the reactor's flow control handler for a new stream.
764    // TODO: remove the `Result` once we remove the "flowctl-cc" feature
765    #[cfg_attr(feature = "flowctl-cc", expect(clippy::unnecessary_wraps))]
766    fn build_flow_ctrl(
767        &self,
768        rate_limit_updater: watch::Sender<StreamRateLimit>,
769        drain_rate_requester: NotifySender<DrainRateRequest>,
770    ) -> Result<StreamFlowCtrl> {
771        let params = Arc::clone(&self.flow_ctrl_params);
772
773        if self
774            .ccontrol()
775            .lock()
776            .expect("poisoned lock")
777            .uses_stream_sendme()
778        {
779            let window = sendme::StreamSendWindow::new(SEND_WINDOW_INIT);
780            Ok(StreamFlowCtrl::new_window(window))
781        } else {
782            cfg_if::cfg_if! {
783                if #[cfg(feature = "flowctl-cc")] {
784                    // TODO: Currently arti only supports clients, and we don't support connecting
785                    // to onion services while using congestion control, so we hardcode this. In the
786                    // future we will need to somehow tell the `CircHop` this so that we can set it
787                    // correctly, since we don't want to enable this at exits.
788                    let use_sidechannel_mitigations = true;
789
790                    Ok(StreamFlowCtrl::new_xon_xoff(
791                        params,
792                        use_sidechannel_mitigations,
793                        rate_limit_updater,
794                        drain_rate_requester,
795                    ))
796                } else {
797                    drop(params);
798                    drop(rate_limit_updater);
799                    drop(drain_rate_requester);
800                    Err(internal!(
801                        "`CongestionControl` doesn't use sendmes, but 'flowctl-cc' feature not enabled",
802                    ).into())
803                }
804            }
805        }
806    }
807
808    /// Deliver `msg` to the specified open stream entry `ent`.
809    fn deliver_msg_to_stream(
810        streamid: StreamId,
811        ent: &mut OpenStreamEnt,
812        cell_counts_toward_windows: bool,
813        msg: UnparsedRelayMsg,
814    ) -> Result<bool> {
815        use tor_async_utils::SinkTrySend as _;
816        use tor_async_utils::SinkTrySendError as _;
817
818        // The stream for this message exists, and is open.
819
820        // We need to handle SENDME/XON/XOFF messages here, not in the stream's recv() method, or
821        // else we'd never notice them if the stream isn't reading.
822        match msg.cmd() {
823            RelayCmd::SENDME => {
824                ent.put_for_incoming_sendme(msg)?;
825                return Ok(false);
826            }
827            RelayCmd::XON => {
828                ent.handle_incoming_xon(msg)?;
829                return Ok(false);
830            }
831            RelayCmd::XOFF => {
832                ent.handle_incoming_xoff(msg)?;
833                return Ok(false);
834            }
835            _ => {}
836        }
837
838        let message_closes_stream = ent.cmd_checker.check_msg(&msg)? == StreamStatus::Closed;
839
840        if let Err(e) = Pin::new(&mut ent.sink).try_send(msg) {
841            if e.is_full() {
842                cfg_if::cfg_if! {
843                    if #[cfg(not(feature = "flowctl-cc"))] {
844                        // If we get here, we either have a logic bug (!), or an attacker
845                        // is sending us more cells than we asked for via congestion control.
846                        return Err(Error::CircProto(format!(
847                            "Stream sink would block; received too many cells on stream ID {}",
848                            sv(streamid),
849                        )));
850                    } else {
851                        return Err(internal!(
852                            "Stream (ID {}) uses an unbounded queue, but apparently it's full?",
853                            sv(streamid),
854                        )
855                        .into());
856                    }
857                }
858            }
859            if e.is_disconnected() && cell_counts_toward_windows {
860                // the other side of the stream has gone away; remember
861                // that we received a cell that we couldn't queue for it.
862                //
863                // Later this value will be recorded in a half-stream.
864                ent.dropped += 1;
865            }
866        }
867
868        Ok(message_closes_stream)
869    }
870
871    /// Note that we received an END message (or other message indicating the end of
872    /// the stream) on the stream with `id`.
873    ///
874    /// See [`StreamMap::ending_msg_received`](crate::streammap::StreamMap::ending_msg_received).
875    #[cfg(feature = "hs-service")]
876    pub(crate) fn ending_msg_received(&self, stream_id: StreamId) -> Result<()> {
877        let mut hop_map = self.map.lock().expect("lock poisoned");
878
879        hop_map.ending_msg_received(stream_id)?;
880
881        Ok(())
882    }
883
884    /// Handle `msg`, delivering it to the stream with the specified `streamid` if appropriate.
885    ///
886    /// Returns back the provided `msg`, if the message is an incoming stream request
887    /// that needs to be handled by the calling code.
888    ///
889    // TODO: the above is a bit of a code smell -- we should try to avoid passing the msg
890    // back and forth like this.
891    pub(crate) fn handle_msg<F>(
892        &self,
893        possible_proto_violation_err: F,
894        cell_counts_toward_windows: bool,
895        streamid: StreamId,
896        msg: UnparsedRelayMsg,
897        now: Instant,
898    ) -> Result<Option<UnparsedRelayMsg>>
899    where
900        F: FnOnce(StreamId) -> Error,
901    {
902        let mut hop_map = self.map.lock().expect("lock poisoned");
903
904        match hop_map.get_mut(streamid) {
905            Some(StreamEntMut::Open(ent)) => {
906                // Can't have a stream level SENDME when congestion control is enabled.
907                let message_closes_stream =
908                    Self::deliver_msg_to_stream(streamid, ent, cell_counts_toward_windows, msg)?;
909
910                if message_closes_stream {
911                    hop_map.ending_msg_received(streamid)?;
912                }
913            }
914            Some(StreamEntMut::EndSent(EndSentStreamEnt { expiry, .. })) if now >= *expiry => {
915                return Err(possible_proto_violation_err(streamid));
916            }
917            Some(StreamEntMut::EndSent(_))
918                if matches!(
919                    msg.cmd(),
920                    RelayCmd::BEGIN | RelayCmd::BEGIN_DIR | RelayCmd::RESOLVE
921                ) =>
922            {
923                // If the other side is sending us a BEGIN but hasn't yet acknowledged our END
924                // message, just remove the old stream from the map and stop waiting for a
925                // response
926                hop_map.ending_msg_received(streamid)?;
927                return Ok(Some(msg));
928            }
929            Some(StreamEntMut::EndSent(EndSentStreamEnt { half_stream, .. })) => {
930                // We sent an end but maybe the other side hasn't heard.
931
932                match half_stream.handle_msg(msg)? {
933                    StreamStatus::Open => {}
934                    StreamStatus::Closed => {
935                        hop_map.ending_msg_received(streamid)?;
936                    }
937                }
938            }
939            None if matches!(
940                msg.cmd(),
941                RelayCmd::BEGIN | RelayCmd::BEGIN_DIR | RelayCmd::RESOLVE
942            ) =>
943            {
944                return Ok(Some(msg));
945            }
946            _ => {
947                // No stream wants this message, or ever did.
948                return Err(possible_proto_violation_err(streamid));
949            }
950        }
951
952        Ok(None)
953    }
954
955    /// Get the stream map of this hop.
956    pub(crate) fn stream_map(&self) -> &Arc<Mutex<StreamMap>> {
957        &self.map
958    }
959
960    /// Set the stream map of this hop to `map`.
961    ///
962    /// Returns an error if the existing stream map of the hop has any open stream.
963    pub(crate) fn set_stream_map(&mut self, map: Arc<Mutex<StreamMap>>) -> StdResult<(), Bug> {
964        if self.n_open_streams() != 0 {
965            return Err(internal!("Tried to discard existing open streams?!"));
966        }
967
968        self.map = map;
969
970        Ok(())
971    }
972
973    /// Decrement the limit of outbound cells that may be sent to this hop; give
974    /// an error if it would reach zero.
975    pub(crate) fn decrement_cell_limit(&mut self) -> Result<()> {
976        try_decrement_cell_limit(&mut self.n_outgoing_cells_permitted)
977            .map_err(|_| Error::ExcessOutboundCells)
978    }
979}
980
981/// If `val` is `Some(1)`, return Err(());
982/// otherwise decrement it (if it is Some) and return Ok(()).
983#[inline]
984fn try_decrement_cell_limit(val: &mut Option<NonZeroU32>) -> StdResult<(), ()> {
985    // This is a bit verbose, but I've confirmed that it optimizes nicely.
986    match val {
987        Some(x) => {
988            let z = u32::from(*x);
989            if z == 1 {
990                Err(())
991            } else {
992                *x = (z - 1).try_into().expect("NonZeroU32 was zero?!");
993                Ok(())
994            }
995        }
996        None => Ok(()),
997    }
998}
999
1000/// Convert a limit from the form used in a HopSettings to that used here.
1001/// (The format we use here is more compact.)
1002fn cvt(limit: u32) -> NonZeroU32 {
1003    // See "known limitations" comment on n_incoming_cells_permitted.
1004    limit
1005        .saturating_add(1)
1006        .try_into()
1007        .expect("Adding one left it as zero?")
1008}
1009
1010/// A collection of components that can be used to interact with the reactor's view of a Tor stream.
1011//
1012// TODO: We also have a `StreamComponents` type that is used and built outside of the reactor.
1013// It's maybe confusing to have these similar type names, so a better name would be nice.
1014//
1015// TODO(arti#2068): The components we return should maybe depend on what type of flow control is
1016// used, so in the future we might want to make some of these fields optional.
1017#[derive(Debug, Deftly)]
1018#[derive_deftly(HasMemoryCost)]
1019pub(crate) struct ReactorStreamComponents {
1020    /// An MPSC receiver for inbound messages that arrive on the stream.
1021    #[deftly(has_memory_cost(indirect_size = "0"))] // estimate
1022    pub(crate) stream_inbound_rx: StreamQueueReceiver,
1023
1024    /// An MPSC sender for outbound messages to be sent on the stream.
1025    #[deftly(has_memory_cost(indirect_size = "size_of::<AnyRelayMsg>()"))] // estimate
1026    pub(crate) stream_outbound_tx: StreamMpscSender<AnyRelayMsg>,
1027
1028    /// A mechanism to allow the stream's writer to receive rate limit updates from the reactor.
1029    // The `watch::Sender` owns the indirect data.
1030    #[deftly(has_memory_cost(indirect_size = "0"))]
1031    pub(crate) rate_limit_rx: watch::Receiver<StreamRateLimit>,
1032
1033    /// A mechanism to allow the stream's reader to receive drain rate update requests from the
1034    /// reactor.
1035    #[deftly(has_memory_cost(indirect_size = "0"))]
1036    pub(crate) drain_rate_request_rx: NotifyReceiver<DrainRateRequest>,
1037}