tor_proto/circuit/padding.rs
1//! Circuit padding
2//!
3// TODO(DEDUP): we should eventually move client::circuit::padding here
4
5#[cfg(feature = "circ-padding")]
6use {crate::circuit::cell_sender::CircuitCellSender, crate::client::circuit::padding};
7
8/// A possible way to handle a request to send padding.
9#[derive(Copy, Clone, Debug)]
10pub(crate) enum CircPaddingDisposition {
11 /// Enqueue the padding normally.
12 QueuePaddingNormally,
13 /// Enqueue the padding, and allow one cell of data on our outbound queue
14 /// to bypass the current block.
15 QueuePaddingAndBypass,
16 /// Do not take any actual padding action:
17 /// existing data on our outbound queue will count as padding.
18 TreatQueuedCellAsPadding,
19}
20
21/// Determine how exactly to handle a request to handle padding.
22///
23/// This is fairly complicated; see the maybenot documentation for more information.
24///
25// TODO(relay): relays use the same logic as clients here. Is that okay,
26// or do they need to handle SendPadding differently??
27#[cfg(feature = "circ-padding")]
28pub(crate) fn padding_disposition(
29 send_padding: &padding::SendPadding,
30 chan_sender: &CircuitCellSender,
31 padding_block: Option<&padding::StartBlocking>,
32) -> CircPaddingDisposition {
33 use CircPaddingDisposition::*;
34 use padding::Bypass::*;
35 use padding::Replace::*;
36
37 // If true, and we are trying to send Replaceable padding,
38 // we should let any data in the queue count as the queued padding instead,
39 // if it is queued for our target hop (or any subsequent hop).
40 //
41 // TODO circpad: In addition to letting currently-queued data count as padding,
42 // maybenot also permits us to send currently pending data from our streams
43 // (or from our next hop, if we're a relay). We don't have that implemented yet.
44 //
45 // TODO circpad: This will usually be false, since we try not to queue data
46 // when there isn't space to write it. If we someday add internal per-circuit
47 // Buffers to chan_sender, this test is more likely to trigger.
48 let have_queued_cell_for_hop = chan_sender.have_queued_cell_for_hop_or_later(send_padding.hop);
49
50 match padding_block {
51 Some(blocking) if blocking.is_bypassable => {
52 match (
53 send_padding.may_replace_with_data(),
54 send_padding.may_bypass_block(),
55 ) {
56 (NotReplaceable, DoNotBypass) => QueuePaddingNormally,
57 (NotReplaceable, BypassBlocking) => QueuePaddingAndBypass,
58 (Replaceable, DoNotBypass) => {
59 if have_queued_cell_for_hop {
60 TreatQueuedCellAsPadding
61 } else {
62 QueuePaddingNormally
63 }
64 }
65 (Replaceable, BypassBlocking) => {
66 if have_queued_cell_for_hop {
67 TreatQueuedCellAsPadding
68 } else {
69 QueuePaddingAndBypass
70 }
71 }
72 }
73 }
74 Some(_) | None => match send_padding.may_replace_with_data() {
75 Replaceable => {
76 if have_queued_cell_for_hop {
77 TreatQueuedCellAsPadding
78 } else {
79 QueuePaddingNormally
80 }
81 }
82 NotReplaceable => QueuePaddingNormally,
83 },
84 }
85}