mirror of https://github.com/openssl/openssl
Add design document for the QUIC Stream Receive Buffers module
Reviewed-by: Paul Dale <pauli@openssl.org> Reviewed-by: Hugo Landau <hlandau@openssl.org> (Merged from https://github.com/openssl/openssl/pull/19149)
This commit is contained in:
parent
e5a7536eae
commit
fb8bdbe3eb
|
@ -0,0 +1,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.
|
Loading…
Reference in New Issue