summaryrefslogtreecommitdiffstats
path: root/doc/designs/quic-design/stream-receive-buffers.md
blob: 2de8cd02e2c3bfc99b3d38a04e184c4d4618bc6f (plain)
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
Stream Receive Buffers
======================

This is a QUIC specific module that retains the received stream data
until the application reads it with SSL_read() or any future stream read
calls.

Receive Buffers requirements for MVP
------------------------------------

These are the requirements that were identified for MVP:

- As packets with stream frames are received in arbitrary frames the
  received data must be stored until all the data with earlier offsets
  are received.
- As packets can be received before application calls SSL_read() to read
  the data the data must be stored.
- The application should be able to set the limit on how much data should
  be stored. The flow controller should be used to limit the peer to not send
  more data. Without the flow control limit a rogue peer could trigger
  a DoS via unlimited flow of incoming stream data frames.
- After the data is passed via SSL_read() to the application the stored
  data can be released and flow control limit can be raised.
- As the peer can recreate stream data frames when resending them, the
  implementation must be able to handle properly frames with partially
  or fully overlapping data with previously received frames.

Optional Receive Buffers requirements
-------------------------------------

These are optional features of the stream receive buffers implementation.
They are not required for MVP but they are otherwise desirable:

- To support a single copy operation with a future stream read call
  the received data should not be copied out of the decrypted packets to
  store the data. The only information actually stored would be a list
  of offset, length, and pointers to data, along with a pointer to the
  decrypted QUIC packet that stores the actual frame.

Proposed new public API calls
-----------------------------

```C
int SSL_set_max_stored_stream_data(SSL *stream, size_t length);
```

This function adjusts the current data flow control limit on the `stream`
to allow storing `length` bytes of quic stream data before it is read by
the application.

OpenSSL handles sending MAX_STREAM_DATA frames appropriately when the
application reads the stored data.

```C
int SSL_set_max_unprocessed_packet_data(SSL *connection,
                                        size_t length);
```

This sets the limit on unprocessed quic packet data `length` in bytes that
is allowed to be allocated for the `connection`.
See the [Other considerations](#other-considerations) section below.

Interfaces to other QUIC implementation modules
-----------------------------------------------

### Front End I/O API

SSL_read() copies data out of the stored buffers if available and
eventually triggers release of stored unprocessed packet(s).

SSL_peek(), SSL_pending(), SSL_has_pending() peek into the stored
buffers for any information about the stored data.

### RX Depacketizer

The Receive Buffers module obtains the stream data via the ssl_queue_data()
callback.

The module uses ossl_qrx_pkt_wrap_up_ref() and ossl_qrx_pkt_wrap_release()
functions to keep and release decrypted packets with unprocessed data.

### Flow Control

The Receive Buffers module provides an appropriate value for the Flow
Control module to send MAX_DATA and MAX_STREAM_DATA frames. Details
TBD.

### QUIC Read Record Layer

The Receive Buffers module needs to know whether it should stop holding
the decrypted quic packets and start copying the stream data due to
the limit reached. See the `SSL_set_max_unprocessed_quic_packet_data()`
function above and the [Other considerations](#other-considerations) section
below. Details TBD.

Implementation details
----------------------

TBD

Other considerations
--------------------

The peer is allowed to recreate the stream data frames. As we aim for
a single-copy operation a rogue peer could use this to override the stored
data limits by sending duplicate frames with only slight changes in the
offset. For example: 1st frame - offset 0 length 1000, 2nd frame -
offset 1 length 1000, 3rd frame - offset 2 length 1000, and so on. We
would have to keep the packet data for all these frames which would
effectively raise the stream data flow control limit quadratically.

And this is not the only way how a rogue peer could make us occupy much
more data than what is allowed by the stream data flow control limit
in the single-copy scenario.

Although intuitively the MAX_DATA flow control limit might be used to
somehow limit the allocated packet buffer size, it is defined as sum
of allowed data to be sent across all the streams in the connection instead.
The packet buffer will contain much more data than just the stream frames
especially with a rogue peer, that means MAX_DATA limit cannot be used
to limit the memory occupied by packet buffers.

To resolve this problem, we fall back to copying the data off the
decrypted packet buffer once we reach a limit on unprocessed decrypted
packets. We might also consider falling back to copying the data in case
we receive stream data frames that are partially overlapping and one frame
not being a subrange of the other.

Because in MVP only a single bidirectional stream to receive
any data will be supported, the MAX_DATA flow control limit should be equal
to MAX_STREAM_DATA limit for that stream.