1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
|
//! Code for implementing flow control (stream-level).
use std::sync::Arc;
use postage::watch;
use tor_cell::relaycell::flow_ctrl::{Xoff, Xon, XonKBpsEwma};
use tor_cell::relaycell::msg::AnyRelayMsg;
use tor_cell::relaycell::{RelayMsg, UnparsedRelayMsg};
use super::params::FlowCtrlParameters;
use super::window::state::{HalfStreamWindowFlowCtrl, WindowFlowCtrl};
use super::xon_xoff::reader::DrainRateRequest;
#[cfg(feature = "flowctl-cc")]
use super::xon_xoff::state::{HalfStreamXonXoffFlowCtrl, XonXoffFlowCtrl};
use crate::Result;
use crate::congestion::sendme;
use crate::util::notify::NotifySender;
/// Private internals of [`StreamFlowCtrl`].
#[enum_dispatch::enum_dispatch]
#[derive(Debug)]
enum StreamFlowCtrlInner {
/// "legacy" sendme-window-based flow control.
Window(WindowFlowCtrl),
/// XON/XOFF flow control.
#[cfg(feature = "flowctl-cc")]
XonXoff(XonXoffFlowCtrl),
}
/// Manages the circuit reactor's flow control for a stream.
///
/// Note that the flow control logic can be distributed across multiple parts of Arti.
/// For example some flow control logic will exist in the circuit reactor,
/// but other logic will exist in the stream's `DataStream`.
/// So this doesn't include all flow control logic.
#[derive(Debug)]
pub(crate) struct StreamFlowCtrl {
/// Private internal enum.
inner: StreamFlowCtrlInner,
}
impl StreamFlowCtrl {
/// Returns a new sendme-window-based [`StreamFlowCtrl`].
pub(crate) fn new_window(window: sendme::StreamSendWindow) -> Self {
Self {
inner: StreamFlowCtrlInner::Window(WindowFlowCtrl::new(window)),
}
}
/// Returns a new xon/xoff-based [`StreamFlowCtrl`].
#[cfg(feature = "flowctl-cc")]
pub(crate) fn new_xon_xoff(
params: Arc<FlowCtrlParameters>,
use_sidechannel_mitigations: bool,
rate_limit_updater: watch::Sender<StreamRateLimit>,
drain_rate_requester: NotifySender<DrainRateRequest>,
) -> Self {
Self {
inner: StreamFlowCtrlInner::XonXoff(XonXoffFlowCtrl::new(
params,
use_sidechannel_mitigations,
rate_limit_updater,
drain_rate_requester,
)),
}
}
/// Once this end of the stream is closed and the stream becomes a
/// half-stream (`HalfStream`),
/// this method will turn the flow control object into a version
/// that is designed to be used for half-streams.
pub(crate) fn half_stream(self) -> HalfStreamFlowCtrl {
let inner = match self.inner {
StreamFlowCtrlInner::Window(x) => {
HalfStreamFlowCtrlInner::Window(HalfStreamWindowFlowCtrl::new(x))
}
#[cfg(feature = "flowctl-cc")]
StreamFlowCtrlInner::XonXoff(x) => {
HalfStreamFlowCtrlInner::XonXoff(HalfStreamXonXoffFlowCtrl::new(x))
}
};
HalfStreamFlowCtrl { inner }
}
}
// forward all trait methods to the inner enum
impl FlowCtrlHooks for StreamFlowCtrl {
fn can_send<M: RelayMsg>(&self, msg: &M) -> bool {
self.inner.can_send(msg)
}
fn about_to_send(&mut self, msg: &AnyRelayMsg) -> Result<()> {
self.inner.about_to_send(msg)
}
fn put_for_incoming_sendme(&mut self, msg: UnparsedRelayMsg) -> Result<()> {
self.inner.put_for_incoming_sendme(msg)
}
fn handle_incoming_xon(&mut self, msg: UnparsedRelayMsg) -> Result<()> {
self.inner.handle_incoming_xon(msg)
}
fn handle_incoming_xoff(&mut self, msg: UnparsedRelayMsg) -> Result<()> {
self.inner.handle_incoming_xoff(msg)
}
fn maybe_send_xon(&mut self, rate: XonKBpsEwma, buffer_len: usize) -> Result<Option<Xon>> {
self.inner.maybe_send_xon(rate, buffer_len)
}
fn maybe_send_xoff(&mut self, buffer_len: usize) -> Result<Option<Xoff>> {
self.inner.maybe_send_xoff(buffer_len)
}
fn inbound_queue_max_len(&self) -> usize {
self.inner.inbound_queue_max_len()
}
}
/// Methods that can be called on a [`StreamFlowCtrl`].
///
/// We use a trait so that we can use `enum_dispatch` on the inner [`StreamFlowCtrlInner`] enum.
#[enum_dispatch::enum_dispatch(StreamFlowCtrlInner)]
pub(crate) trait FlowCtrlHooks {
/// Whether this stream is ready to send `msg`.
fn can_send<M: RelayMsg>(&self, msg: &M) -> bool;
/// Inform the flow control code that we're about to send `msg`.
/// Returns an error if the message should not be sent,
/// and the circuit should be closed.
// TODO: Consider having this method wrap the message in a type that
// "proves" we've applied flow control. This would make it easier to apply
// flow control earlier, e.g. in `OpenStreamEntStream`, without introducing
// ambiguity in the sending function as to whether flow control has already
// been applied or not.
fn about_to_send(&mut self, msg: &AnyRelayMsg) -> Result<()>;
/// Handle an incoming sendme.
///
/// On success, return the number of cells left in the window.
///
/// On failure, return an error: the caller should close the stream or
/// circuit with a protocol error.
///
/// Takes the [`UnparsedRelayMsg`] so that we don't even try to decode it if we're not using the
/// correct type of flow control.
fn put_for_incoming_sendme(&mut self, msg: UnparsedRelayMsg) -> Result<()>;
/// Handle an incoming XON message.
///
/// Takes the [`UnparsedRelayMsg`] so that we don't even try to decode it if we're not using the
/// correct type of flow control.
fn handle_incoming_xon(&mut self, msg: UnparsedRelayMsg) -> Result<()>;
/// Handle an incoming XOFF message.
///
/// Takes the [`UnparsedRelayMsg`] so that we don't even try to decode it if we're not using the
/// correct type of flow control.
fn handle_incoming_xoff(&mut self, msg: UnparsedRelayMsg) -> Result<()>;
/// Check if we should send an XON message.
///
/// If we should, then returns the XON message that should be sent.
/// Returns an error if XON/XOFF messages aren't supported for this type of flow control.
fn maybe_send_xon(&mut self, rate: XonKBpsEwma, buffer_len: usize) -> Result<Option<Xon>>;
/// Check if we should send an XOFF message.
///
/// If we should, then returns the XOFF message that should be sent.
/// Returns an error if XON/XOFF messages aren't supported for this type of flow control.
fn maybe_send_xoff(&mut self, buffer_len: usize) -> Result<Option<Xoff>>;
/// The max queue length that should be used for stream messages incoming from the Tor network.
///
/// This is the queue length between the user-facing stream reader (`DataReader`)
/// and the circuit reactor.
///
/// If the queue would ever exceed this many messages, the stream should be closed.
fn inbound_queue_max_len(&self) -> usize;
}
/// Manages flow control for a half-stream (`HalfStream`).
#[derive(Debug)]
pub(crate) struct HalfStreamFlowCtrl {
/// Private internal enum.
inner: HalfStreamFlowCtrlInner,
}
/// Private internals of [`HalfStreamFlowCtrl`].
#[enum_dispatch::enum_dispatch]
#[derive(Debug)]
enum HalfStreamFlowCtrlInner {
/// "legacy" sendme-window-based flow control.
Window(HalfStreamWindowFlowCtrl),
/// XON/XOFF flow control.
#[cfg(feature = "flowctl-cc")]
XonXoff(HalfStreamXonXoffFlowCtrl),
}
/// Methods that can be called on a [`HalfStreamFlowCtrl`].
///
/// We use a trait so that we can use `enum_dispatch` on the inner [`HalfStreamFlowCtrlInner`] enum.
/// While this may seem unnecessary since this trait currently only has two methods,
/// it's consistent with the [`FlowCtrlHooks`] trait above.
#[enum_dispatch::enum_dispatch(HalfStreamFlowCtrlInner)]
pub(crate) trait HalfStreamFlowCtrlHooks {
/// Handle some number of dropped stream messages.
///
/// We don't know what kinds of stream messages were dropped, only the number of them.
///
/// This method exists because currently the stream entry may drop some incoming stream
/// messages and they would never be processed by this flow control object otherwise.
fn handle_incoming_dropped(&mut self, msg_count: u16) -> Result<()>;
/// Handle an incoming message.
///
/// If it's a flow control message, it will be consumed and `None` will be returned.
/// Otherwise the original message will be returned.
///
/// Takes the [`UnparsedRelayMsg`] so that we don't even try to decode it if we're not using the
/// correct type of flow control.
fn handle_incoming_msg(&mut self, msg: UnparsedRelayMsg) -> Result<Option<UnparsedRelayMsg>>;
}
// forward all trait methods to the inner enum
impl HalfStreamFlowCtrlHooks for HalfStreamFlowCtrl {
fn handle_incoming_dropped(&mut self, msg_count: u16) -> Result<()> {
self.inner.handle_incoming_dropped(msg_count)
}
fn handle_incoming_msg(&mut self, msg: UnparsedRelayMsg) -> Result<Option<UnparsedRelayMsg>> {
self.inner.handle_incoming_msg(msg)
}
}
/// A newtype wrapper for a tor stream rate limit that makes the units explicit.
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub(crate) struct StreamRateLimit {
/// The rate in bytes/s.
rate: u64,
}
impl StreamRateLimit {
/// A maximum rate limit.
pub(crate) const MAX: Self = Self::new_bytes_per_sec(u64::MAX);
/// A rate limit of 0.
pub(crate) const ZERO: Self = Self::new_bytes_per_sec(0);
/// A new [`StreamRateLimit`] with `rate` bytes/s.
pub(crate) const fn new_bytes_per_sec(rate: u64) -> Self {
Self { rate }
}
/// The rate in bytes/s.
pub(crate) const fn bytes_per_sec(&self) -> u64 {
self.rate
}
}
impl std::fmt::Display for StreamRateLimit {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{} bytes/s", self.rate)
}
}
|