Commit Graph

269 Commits

Author SHA1 Message Date
Amaury Denoyelle
b6309456d0 MINOR: mux-quic: use timeout server for backend conns
Use timeout server in qcc_init() as default timeout for backend
connections. No impact for the moment as QUIC backend support is not
implemented.
2022-08-01 14:23:21 +02:00
Amaury Denoyelle
07bf8f4d86 MINOR: mux-quic: save proxy instance into qcc
Store a reference to proxy in the qcc structure. This will be useful to
access to proxy members outside of qcc_init().

Most notably, this change is required to implement timeout refreshing by
using the various timeouts configured at the proxy level.
2022-08-01 14:23:21 +02:00
Amaury Denoyelle
09ec3e09bd BUG/MINOR: mux-quic: do not free conn if attached streams
Ensure via qcc_is_dead() that a connection is not released instance
until all of qcs streams are detached by the upper layer, even if an
error has been reported or the timeout has fired.

On the other side, as qc_detach() always check the connection status,
this should ensure that we do not keep a connection if not necessary.

Without this patch, a qcc instance may be freed with some of its qcs
streams not detached. This is an incorrect behavior and will lead to a
BUG_ON fault. Note however that no occurence of this bug has been
produced currently. This patch is mainly a safety against future
occurences.

This should be backported up to 2.6.
2022-08-01 14:23:19 +02:00
Amaury Denoyelle
4ea5090f55 CLEANUP: mux-quic: remove useless app_ops is_active callback
Timeout in QUIC MUX has evolved from the simple first implementation. At
the beginning, a connection was considered dead unless bidirectional
streams were opened. This was abstracted through an app callback
is_active().

Now this paradigm has been reversed and a connection is considered alive
by default, unless an error has been reported or a timeout has already
been fired. The callback is_active() is thus not used anymore and can be
safely removed to simplify qcc_is_dead().

This commit should be backported to 2.6.
2022-08-01 14:13:51 +02:00
Amaury Denoyelle
d3973853c2 BUG/MINOR: mux-quic: prevent crash if conn released during IO callback
A qcc instance may be freed in the middle of qc_io_cb() if all streams
were purged. This will lead to a crash as qcc instance is reused after
this step. Jump directly to the end of the function to avoid this.

Note that this bug has not been triggered for the moment. This is a
safety fix to prevent it.

This must be backported up to 2.6.
2022-08-01 14:13:51 +02:00
Amaury Denoyelle
72a78e8290 BUG/MEDIUM: mux-quic: fix missing EOI flag to prevent streams leaks
On H3 DATA frame transfer from the client, some streams are not properly
closed by the upper layer, despite all transfer operation completed.
Data integrity is not impacted but this will prevent the stream timeout
to fire and thus keep the owner session opened.

In most cases, sessions are closed on QUIC idle timeout, but it may stay
forever if a client emits PING frames at a regular interval to maintain
it.

This bug is caused by a missing EOI stream desc flag on certain
condition in qc_rcv_buf(). To be triggered, we have to use the
optimization when conn-stream buffer is empty and can be swapped with
qcs buffer. The problem is that it will skip the function body for
default copy but also a condition to check if EOI must be set. Thus this
bug does not happens for every H3 post requets : it requires that
conn-stream buffer is empty on last qc_rcv_buf() invocation.

This was reproduced more frequently when using ngtcp2 client with one or
multiple streams :
$ ngtcp2-client -m POST -d ~/infra/html/10K 127.0.0.1 20443 \
  http://127.0.0.1:20443/post

This may fix at least partially github issue #1801.

This must be backported up to 2.6.
2022-07-29 16:01:21 +02:00
Frédéric Lécaille
a18c3339c8 BUG/MAJOR: mux_quic: fix invalid PROTOCOL_VIOLATION on POST data overlap
Stream data reception is incorrect when dealing with a partially new
offset with some data already consumed out of the RX buffer. In this
case, data length is adjusted but not the data buffer. In most cases,
ncb_add() operation will be rejected as already stored data does not
correspond with the new inserted offset. This will result in an invalid
CONNECTION_CLOSE with PROTOCOL_VIOLATION.

To fix this, buffer pointer is advanced while the length is reduced.

This can be reproduced with a POST request and patching haproxy to call
qcc_recv() multiple times by copying a quic_stream frame with different
offsets.

Must be backported to 2.6.
2022-07-20 15:34:58 +02:00
Amaury Denoyelle
a154dc0290 MINOR: mux-quic: send one last time before release
Call qc_send() on qc_release(). This is mostly useful for application
protocol with a connection closing procedure. Most notably, this will be
useful to implement HTTP/3 GOAWAY emission.
2022-07-15 15:56:13 +02:00
Amaury Denoyelle
c49d5d1a4b CLEANUP: mux-quic: move qc_release()
This change is purely cosmetic. qc_release() function is moved just
before qc_io_cb(). It's cleaner as it brings it closer where it is used.
More importantly, this will be required to be able to use it in
qc_send() function.
2022-07-15 15:56:13 +02:00
Amaury Denoyelle
069288b4c0 MINOR: mux-quic/h3: prepare CONNECTION_CLOSE on release
When MUX is released, a CONNECTION_CLOSE frame should be emitted. This
will ensure that the client does not use anymore a half-dead connection.

App protocol layer is responsible to provide the error code via release
callback. For HTTP/3 NO_ERROR is used as specified in RFC 9114. If no
release callback is provided, generic QUIC NO_ERROR code is used. Note
that a graceful shutdown is used : quic_conn must emit CONNECTION_CLOSE
frame when possible. This will be provided in another patch.

This change should limit the risk of browsers stuck on webpage loading
if MUX has been released. On CONNECTION_CLOSE reception, the client will
reopen a new QUIC connection.
2022-07-15 15:20:33 +02:00
Amaury Denoyelle
d666d740d2 MINOR: mux-quic: support app graceful shutdown
Adjust qcc_emit_cc_app() to allow the delay of emission of a
CONNECTION_CLOSE. This will only set the error code but the quic-conn
layer is not flagged for immediate close. The quic-conn will be
responsible to shut the connection when deemed suitable.

This change will allow to implement application graceful shutdown, such
as HTTP/3 with GOAWAY emission. This will allow to emit closing frames
on MUX release. Once all work is done at the lower layer, the quic-conn
should emit a CONNECTION_CLOSE with the registered error code.
2022-07-15 15:06:59 +02:00
Amaury Denoyelle
57e6db7021 MINOR: quic: define a generic QUIC error type
Define a new structure quic_err to abstract a QUIC error type. This
allows to easily differentiate a transport and an application error
code. This simplifies error transmission from QUIC MUX and H3 layers.

This new type is defined in quic_frame module. It is used to replace
<err_code> field in <quic_conn>. QUIC_FL_CONN_APP_ALERT flag is removed
as it is now useless.

Utility functions are defined to be able to quickly instantiate
transport, tls and application errors.
2022-07-15 14:57:49 +02:00
Amaury Denoyelle
a5b5075211 MEDIUM: mux-quic: implement STOP_SENDING handling
Implement support for STOP_SENDING frame parsing. The stream is resetted
as specified by RFC 9000. This will automatically interrupt all future
send operation in qc_send(). A RESET_STREAM will be sent with the code
extracted from the original STOP_SENDING frame.
2022-07-11 16:45:04 +02:00
Amaury Denoyelle
843a1196b3 MEDIUM: mux-quic: implement RESET_STREAM emission
Implement functions to be able to reset a stream via RESET_STREAM.

If needed, a qcs instance is flagged with QC_SF_TO_RESET to schedule a
stream reset. This will interrupt all future send operations.

On stream emission, if a stream is flagged with QC_SF_TO_RESET, a
RESET_STREAM frame is generated and emitted to the transport layer. If
this operation succeeds, the stream is locally closed. If upper layer is
instantiated, error flag is set on it.
2022-07-11 16:45:04 +02:00
Amaury Denoyelle
20d1f84ce4 MINOR: mux-quic: use stream states to mark as detached
Adjust condition to detach a qcs instance : if the stream is not locally
close it is not directly free. This should improve stream closing by
ensuring that either FIN or a RESET_STREAM is sent before destroying it.
2022-07-11 16:41:10 +02:00
Amaury Denoyelle
38e6006da1 MINOR: mux-quic: define basic stream states
Implement a basic state machine to represent stream lifecycle. By
default a stream is idle. It is marked as open when sending or receiving
the first data on a stream.

Bidirectional streams has two states to represent the closing on both
receive and send channels. This distinction does not exists for
unidirectional streams which passed automatically from open to close
state.

This patch is mostly internal and has a limited visible impact. Some
behaviors are slightly updated :
* closed streams are garbage collected at the start of io handler
* send operation is interrupted if a stream is close locally

Outside of this, there is no functional change. However, some additional
BUG_ON guards are implemented to ensure that we do not conduct invalid
operation on a stream. This should strengthen the code safety. Also,
stream states are displayed on trace which should help debugging.
2022-07-11 16:37:21 +02:00
Amaury Denoyelle
b68559a9aa MINOR: mux-quic: support stream opening via MAX_STREAM_DATA
MAX_STREAM_DATA can be used as the first frame of a stream. In this
case, the stream should be opened, if it respects flow-control limit. To
implement this, simply replace plain lookup in stream tree by
qcc_get_qcs() at the start of the parsing function. This automatically
takes care of opening the stream if not already done.

As specified by RFC 9000, if MAX_STREAM_DATA is receive for a
receive-only stream, a STREAM_STATE_ERROR connection error is emitted.
2022-07-11 16:24:03 +02:00
Amaury Denoyelle
57161b7d0c MINOR: mux-quic: do not ack STREAM frames on unrecoverable error
Improve return path for qcc_recv() on STREAM parsing. It returns 0 on
success. On error, a non-zero value is returned which indicates to the
caller that the packet containing the frame should not be acknowledged.

When qcc_recv() generates a CONNECTION_CLOSE or RESET_STREAM, either
directly or via qcc_get_qcs(), an error is returned which ensure that no
acknowledgement is generated. This required an adjustment on
qcc_get_qcs() API which now returns a success/error code. The stream
instance is returned via a new out argument.
2022-07-11 16:24:03 +02:00
Amaury Denoyelle
5fbb8691d4 MINOR: mux-quic: filter send/receive-only streams on frame parsing
Extend the function qcc_get_qcs() to be able to filter send/receive-only
unidirectional streams. A connection error STREAM_STATE_ERROR is emitted
if this new filter does not match.

This will be useful when various frames handlers are converted with
qcc_get_qcs(). Depending on the frame type, it will be easy to filter on
the forbidden stream types as specified in RFC 9000.
2022-07-11 16:24:03 +02:00
Amaury Denoyelle
4561f84ad4 MINOR: mux-quic: implement qcs_alert()
Implement a simple function to notify a possible subscriber or wake up
the upper layer if a special condition happens on a stream. For the
moment, this is only used to replace identical code in
qc_wake_some_streams().
2022-07-11 16:24:03 +02:00
Amaury Denoyelle
392e94e985 MINOR: mux-quic: add traces on frame parsing functions
Add traces for parsing functions for MAX_DATA and MAX_STREAM_DATA.
2022-07-11 16:24:03 +02:00
Amaury Denoyelle
c1a6dfd477 MINOR: mux-quic: rename stream purge function
Rename qc_release_detached_streams() to qc_purge_streams(). The aim is
to have a more generic name. It's expected to complete this function to
add other criteria to purge dead streams.

Also the function documentation has been corrected. It does not return a
number of streams. Instead it is a boolean value, to true if at least
one stream was released.
2022-07-11 16:24:03 +02:00
Amaury Denoyelle
b143723411 REORG: mux-quic: rename stream initialization function
Rename both qcc_open_stream_local/remote() functions to
qcc_init_stream_local/remote(). This change is purely cosmetic. It will
reduces the ambiguity with the soon to be implemented OPEN states for
QCS instances.
2022-07-11 16:24:03 +02:00
Amaury Denoyelle
e53b489826 BUG/MEDIUM: mux-quic: fix server chunked encoding response
QUIC MUX was not able to correctly deal with server response using
chunked transfer-encoding. All data will be transfered correctly to the
client but the FIN bit is missing. The transfer will never stop as the
client will wait indefinitely for the FIN bit.

This bug happened because the HTX message representing a chunked encoded
payload contains a final empty block with the EOM flag. However,
emission is skipped by QUIC MUX if there is no data to transfer. To fix
this, the condition was completed to ensure that there is no need to
send the FIN signal. If this is false, data emission will proceed even
if there is no data : this will generate an empty QUIC STREAM frame with
FIN set which will mark the end of the transfer.

To ensure that a FIN STREAM frame is sent only one time,
QC_SF_FIN_STREAM is resetted on send confirmation from the transport in
qcc_streams_sent_done().

This bug was reproduced when dealing with chunked transfer-encoding
response for the HTTP server.

This must be backported up to 2.6.
2022-07-11 16:21:52 +02:00
Amaury Denoyelle
bf91e3922b MINOR: mux-quic: emit FINAL_SIZE_ERROR on invalid STREAM size
Add a check on stream size when the stream is in state Size Known. In
this case, a STREAM frame cannot change the stream size. If this is not
respected, a CONNECTION_CLOSE with FINAL_SIZE_ERROR will be emitted as
specified in the RFC 9000.
2022-07-05 16:44:01 +02:00
Amaury Denoyelle
3f39b40fe0 MINOR: mux-quic: rename qcs flag FIN_RECV to SIZE_KNOWN
Rename QC_SF_FIN_RECV to the more generic name QC_SF_SIZE_KNOWN. This
better align with the QUIC RFC 9000 which uses the "Size Known" state
definition. This change is purely cosmetic.
2022-07-05 16:18:27 +02:00
Amaury Denoyelle
a509ffb505 MEDIUM: mux-quic: refactor streams opening
Review the whole API used to access/instantiate qcs.

A public function qcc_open_stream_local() is available to the
application protocol layer. It allows to easily opening a local stream.
The ID is automatically attributed to the next one available.

For remote streams, qcc_open_stream_remote() has been implemented. It
will automatically take care of allocating streams in a linear way
according to the ID. This function is called via qcc_get_qcs() which can
be used for each qcc_recv*() operations. For the moment, it is only used
for STREAM frames via qcc_recv(), but soon it will be implemented for
other frames types which can also be used to open a new stream.

qcs_new() and qcs_free() has been restricted to the MUX QUIC only as
they are now reserved for internal usage.

This change is a pure refactoring and should not have any noticeable
impact. It clarifies the developer intent and help to ensure that a
stream is not automatically opened when not desired.
2022-07-05 16:18:27 +02:00
Amaury Denoyelle
3abeb57909 MINOR: mux-quic: implement accessor for sedesc
Implement a function <qcs_sc> to easily access to the stconn associated
with a QCS. This takes care of qcs.sd which may be NULL, for example for
unidirectional streams.

It is expected that in the future when implementing
STOP_SENDING/RESET_STREAM, stconn must be notify about the event. This
accessor will allow to easily test if the stconn is instantiated or not.
2022-07-05 11:22:15 +02:00
Amaury Denoyelle
a441ec9c7a CLEANUP: mux-quic: do not export qc_get_ncbuf
qc_get_ncbuf() is only used internally : thus its prototype in QUIC MUX
include is not required.
2022-07-05 11:06:52 +02:00
Amaury Denoyelle
6befccd8a1 BUG/MINOR: mux-quic: do not signal FIN if gap in buffer
Adjust FIN signal on Rx path for the application layer : ensure that the
receive buffer has no gap.

Without this extra condition, FIN was signalled as soon as the STREAM
frame with FIN was received, even if we were still waiting to receive
missing offsets.

This bug could have lead to incomplete requests read from the
application protocol. However, in practice this bug has very little
chance to happen as the application layer ensures that the demuxed frame
length is equivalent to the buffer data size. The only way to happen is
if to receive the FIN STREAM as the H3 demuxer is still processing on a
frame which is not the last one of the stream.

This must be backported up to 2.6. The previous patch on ncbuf is
required for the newly defined function ncb_is_fragmented().
  MINOR: ncbuf: implement ncb_is_fragmented()
2022-07-01 15:55:32 +02:00
Amaury Denoyelle
36d4b5e31d CLEANUP: mux-quic: adjust comment on qcs_consume()
Since a previous refactoring, application protocol layer is not require
anymore to call qcs_consume(). This function is now automatically used
by the MUX itself.
2022-07-01 14:46:24 +02:00
Frédéric Lécaille
628e89cfae BUILD: quic+h3: 32-bit compilation errors fixes
In GH #1760 (which is marked as being a feature), there were compilation
errors on MacOS which could be reproduced in Linux when building 32-bit code
(-m32 gcc option). Most of them were due to variables types mixing in QUIC_MIN macro
or using size_t type in place of uint64_t type.

Must be backported to 2.6.
2022-06-24 12:13:53 +02:00
Amaury Denoyelle
040955fb39 BUG/MEDIUM: mux-quic: fix segfault on flow-control frame cleanup
LIST_ELEM macro was incorrectly used in the loop when purging
flow-control frames from qcc.lfctl.frms on MUX release. This caused a
segfault in qc_release() due to an invalid quic_frame pointer instance.

The occurence of this bug seems fairly rare. To happen, some
flow-control frames must have been allocated but not yet sent just as
the MUX release is triggered.

I did not find a reproducer scenario. Instead, I artificially triggered
it by inserting a quic_frame in qcc.lfctl.frms just before purging it in
qc_release() using the following snippet.

        struct quic_frame *frm;
        frm = pool_zalloc(pool_head_quic_frame);
        LIST_INIT(&frm->reflist);
        frm->type = QUIC_FT_MAX_DATA;
        frm->max_data.max_data = 0;
        LIST_APPEND(&qcc->lfctl.frms, &frm->list);

This should fix github issue #1747.

This must be backported up to 2.6.
2022-06-13 14:43:00 +02:00
Amaury Denoyelle
43c090c1ed BUG/MINOR: mux-quic: fix memleak on frames rejected by transport
When the MUX transfers a big amount of data to the client, the transport
layer may reject some of them because of the congestion controller
limit. Frames built by the MUX are thus dropped, even if streams transferred
data are kept in buffers for future new frames.

Thus, the MUX is required to free rejected frames. This fixes a memory
leak which may grow with important data transfers.

It should be backported to 2.6 after it has been tested and validated.
2022-06-10 17:59:06 +02:00
Amaury Denoyelle
78fa559679 MINOR: mux-quic: complete BUG_ON on TX flow-control enforcing
TX flow-control enforcing is not straightforward : it requires the usage
of several counters at stream and connection level, in part due to the
difficult sending API between MUX and quic-conn layers.

To strengthen this part and ensures it behaves as expected, some
existing BUG_ON statements were adjusted and new one were added. This
should help to catch errors as early as possible, as in the case with
github issue #1738.
2022-06-10 17:40:42 +02:00
Amaury Denoyelle
b9e0640405 BUG/MEDIUM: mux-quic: fix flow control connection Tx level
The flow control enforced at connection level is incorrectly calculated.
There is a risk of exceeding the limit. In most cases, this results in a
segfault produced by a BUG_ON which is here to catch this kind of error.
If not compiled with DEBUG_STRICT, this should generate a connection
closed by the client due to the flow control overflow.

The problem is encountered when transfered payload is big enough to fill
the transport congestion window. In this case, some data are rejected by
the transport layer and kept by the MUX to be reemitted later. However,
these preserved data are not counted on the connection flow control when
resubmitted, which gradually amplify the gap between expected and real
consumed flow control.

To fix this, handle the flow-control at the connection level in the same
way as the stream level. A new field qcc.tx.offsets is incremented as
soon as data are transfered between stream TX buffers. The field
qcc.tx.sent_offsets is preserved to count bytes handled by the transport
layer and stop the MUX transfer if limit is reached.

As already stated, this bug can occur during transfers with enough
emitted data, over multiple streams. When using a single stream, the
flow control at the stream level hides it.

The BUG_ON crash is reproduced systematically with quiche client :
$ quiche-client --no-verify --http-version HTTP/3 -n 10000 https://127.0.0.1:20443/10K

This must be backported up to 2.6 when confirmed to work as expected.

This should fix github issue #1738.
2022-06-10 17:30:41 +02:00
Amaury Denoyelle
1f21ebdd76 MINOR: mux-quic/h3: adjust demuxing function return values
Clean the API used by decode_qcs() and transcoder internal functions.
Parsing functions now returns a ssize_t which represents the number of
consumed bytes or a negative error code. The total consumed bytes is
returned via decode_qcs().

The API is now unified and cleaner. The MUX can thus simply use the
return value of decode_qcs() instead of substracting the data bytes in
the buffer before and after the call. Transcoders functions are not
anymore obliged to remove consumed bytes from the buffer which was not
obvious.
2022-06-07 18:15:47 +02:00
Amaury Denoyelle
62eef85961 MINOR: mux-quic: simplify decode_qcs API
Slightly modify decode_qcs function used by transcoders. The MUX now
gives a buffer instance on which each transcoder is free to work on it.
At the return of the function, the MUX removes consume data from its own
buffer.

This reduces the number of invocation to qcs_consume at the end of a
full demuxing process. The API is also cleaner with the transcoders not
responsible of calling it with the risk of having the input buffer
freed if empty.
2022-06-07 18:15:47 +02:00
Amaury Denoyelle
c0156790e6 MINOR: h3: add h3c pointer into h3s instance
As a mirror to qcc/qcs types, add a h3c pointer into h3s struct. This
should help to clean up H3 code and avoid to use qcs.qcc.ctx to retrieve
the h3c instance.
2022-06-07 18:13:11 +02:00
Frédéric Lécaille
748ece68b8 MINOR: quic: QUIC transport parameters split.
Make the transport parameters be standlone as much as possible as
it consists only in encoding/decoding data into/from buffers.
Reduce the size of xprt_quic.h. Unfortunalety, I think we will
have to continue to include <xprt_quic-t.h> to use the trace API
into this module.
2022-05-30 09:59:26 +02:00
Willy Tarreau
d7b7e0df9a CLEANUP: mux-quic: rename the "endp" field to "sd"
The stream endpoint descriptor that was named "endp" is now called "sd"
both in the qcs struct and in the few functions using this.
2022-05-27 19:33:35 +02:00
Willy Tarreau
3215e731b6 CLEANUP: quic/h3: rename all occurrences of stconn "cs" to "sc"
Function arguments and local variables called "cs" were renamed to "sc"
to avoid future confusion. The "nb_cs" stream-connector counter was
renamed to "nb_sc" and qc_attach_cs() was renamed to qc_attach_sc().
2022-05-27 19:33:35 +02:00
Willy Tarreau
cb086c6de1 REORG: stconn: rename conn_stream.{c,h} to stconn.{c,h}
There's no more reason for keepin the code and definitions in conn_stream,
let's move all that to stconn. The alphabetical ordering of include files
was adjusted.
2022-05-27 19:33:35 +02:00
Willy Tarreau
6fe2b42e45 CLEANUP: stconn: rename cs_mux() to sc_mux_strm()
The function doesn't return a pointer to the mux but to the mux stream
(h1s, h2s etc). Let's adjust its name to reflect this. It's rarely used,
the name can be enlarged a bit. And of course s/cs/sc to accommodate for
the updated name.
2022-05-27 19:33:34 +02:00
Willy Tarreau
2f2318df87 MEDIUM: stconn: merge the app_ops and the data_cb fields
For historical reasons (stream-interface and connections), we used to
require two independent fields for the application level callbacks and
the transport-level functions. Over time the distinction faded away so
much that the low-level functions became specific to the application
and conversely. For example, applets may only work with streams on top
since they rely on the channels, and the stream-level functions differ
between applets and connections. Right now the application level only
contains a wake() callback and the low-level ones contain the functions
that act at the lower level to perform the shutr/shutw and at the upper
level to notify about readability and writability. Let's just merge them
together into a single set and get rid of this confusing distinction.
Note that the check ops do not define any app-level function since these
are only called by streams.
2022-05-27 19:33:34 +02:00
Willy Tarreau
c105492bf5 CLEANUP: stdesc: rename the stream connector ->cs field to ->sc
This is a rename of this field. Most of the places were in muxes, but
were already factored with the previous series adding *_sc().
2022-05-27 19:33:34 +02:00
Willy Tarreau
4596fe20d9 CLEANUP: conn_stream: tree-wide rename to stconn (stream connector)
This renames the "struct conn_stream" to "struct stconn" and updates
the descriptions in all comments (and the rare help descriptions) to
"stream connector" or "connector". This touches a lot of files but
the change is minimal. The local variables were not even renamed, so
there's still a lot of "cs" everywhere.
2022-05-27 19:33:34 +02:00
Willy Tarreau
ea59b0201c CLEANUP: conn_stream: rename cs_endpoint to sedesc (stream endpoint descriptor)
After some discussion we found that the cs_endpoint was precisely the
descriptor for a stream endpoint, hence the naturally coming name,
stream endpoint constructor.

This patch renames only the type everywhere and the new/init/free functions
to remain consistent with it. Future patches will address field names and
argument names in various code areas.
2022-05-27 19:33:34 +02:00
Willy Tarreau
65d0597b2b CLEANUP: conn_stream: rename the cs_endpoint's target to "se"
That's the "stream endpoint" pointer. Let's change it now while it's
not much spread. The function __cs_endp_target() wasn't yet renamed
because that will change more globally soon.
2022-05-27 19:33:34 +02:00
Willy Tarreau
b605c4213f CLEANUP: conn_stream: rename the stream endpoint flags CS_EP_* to SE_FL_*
Let's now use the new flag names for the stream endpoint.
2022-05-27 19:33:34 +02:00
Willy Tarreau
d56377c5eb CLEANUP: conn_stream: apply endp_flags.cocci tree-wide
This changes all main uses of endp->flags to the se_fl_*() equivalent
by applying coccinelle script endp_flags.cocci. The se_fl_*() functions
themselves were manually excluded from the change, of course.

Note: 144 locations were touched, manually reviewed and found to be OK.

The script was applied with all includes:

  spatch --in-place --recursive-includes -I include --sp-file $script $files
2022-05-27 19:33:34 +02:00
Amaury Denoyelle
849b24f15b MINOR: h3: abort read on unknown uni stream
As specified by HTTP/3 draft, an unknown unidirectional stream can be
aborted. To do this, use a new flag QC_SF_READ_ABORTED. When the MUX
detects this flag, QCS instance is automatically freed.

Previously, such streams were instead automatically drained. By aborting
them, we economize some useless memcpy instruction. On future data
reception, QCS instance is not found in the tree and considered as
already closed. The frame payload is thus deleted without copying it.
2022-05-25 15:41:25 +02:00
Amaury Denoyelle
f8db5aaf78 MEDIUM: quic: refactor uni streams RX
The whole QUIC stack is impacted by this change :
* at quic-conn level, a single function is now used to handle uni and
  bidirectional streams. It uses qcc_recv() function from MUX.
* at MUX level, qc_recv() io-handler function does not skip uni streams
* most changes are conducted at app layer. Most notably, all received
  data is handle by decode_qcs operation.

Now that decode_qcs is the single app read function, the H3 layer can be
simplified. Uni streams parsing was extracted from h3_attach_ruqs() to
h3_decode_qcs().

h3_decode_qcs() is able to deal with all HTTP/3 frame types. It first
check if the frame is valid for the H3 stream type. Most notably,
SETTINGS parsing was moved from h3_control_recv() into h3_decode_qcs().

This commit has some major benefits besides removing duplicated code.
Mainly, QUIC flow control is now enforced for uni streams as with bidi
streams. Also, an unknown frame received on control stream does not set
an error : it is now silently ignored as required by the specification.

Some cleaning in H3 code is already done with this patch :
h3_control_recv() and h3_attach_ruqs() are removed as they are now
unused. A final patch should clean up the unneeded remaining bit.
2022-05-25 15:41:25 +02:00
Amaury Denoyelle
3236a8e85c MINOR: h3: define stream type
Define a new enum h3s_t. This is used to differentiate between the
different stream types used in a HTTP/3 connection, including the QPACK
encoder/decoder streams.

For the moment, only bidirectional streams is positioned. This patch
will be useful to unify reception of uni streams with bidirectional
ones.
2022-05-25 15:41:25 +02:00
Amaury Denoyelle
c6195d77b4 BUG/MINOR: mux-quic: refactor uni streams TX/send H3 SETTINGS
Remove the unneeded skip over unidirectional streams in qc_send(). This
unify sending for both uni and bidi streams.

In fact, the only local unidirectional streams in use for the moment is
the H3 Control stream responsible of SETTINGS emission. The frame was
already properly generated in qcs.tx.buf, but not send due to stream
skip in qc_send(). Now, there is no need to ignore uni streams so remove
this condition.

This fixes the emission of H3 settings which is now properly emitted.

Uni and bidi streams use the same set of funtcions for sending. One of
the most notable gain is that flow-control is now enforced for uni
streams.
2022-05-25 15:41:25 +02:00
Amaury Denoyelle
6754d7e2ed MINOR: mux-quic: emit STREAM_STATE_ERROR in qcc_recv
Emit STREAM_STATE_ERROR connection error in two cases :
* if receiving data for send-only stream
* if receiving data on a locally initiated stream not open yet

For the moment the first case cannot be encoutered as uni streams
reception does not use qcc_recv(). However, this will be soon
implemented with the unification between bidi and uni streams.
2022-05-25 15:41:25 +02:00
Amaury Denoyelle
5c4373a47b MINOR: mux-quic: disable read on CONNECTION_CLOSE emission
Similar to sending, read operations are disabled when a CONNECTION_CLOSE
frame has been emitted.

Most notably, this prevents unneeded loop demuxing when the H3 layer has
issue an error and cannot process the buffer payload anymore.

Note that read is not prevented for unidirectional streams for the
moment. This will supported soon with the unification of bidir and uni
streams treatment.
2022-05-25 15:41:25 +02:00
Amaury Denoyelle
f9e190e49a MINOR: quic: support CONNECTION_CLOSE_APP emission
Complete quic-conn API for error reporting. A new parameter <app> is
defined in the function quic_set_connection_close(). This will transform
the frame into a CONNECTION_CLOSE_APP type.

This type of frame will be generated by the applicative layer, h3 or
hq-interop for the moment. A new function qcc_emit_cc_app() is exported
by the MUX layer for them.
2022-05-25 15:41:25 +02:00
Amaury Denoyelle
0ffd6e7e64 MINOR: mux-quic: adjust return value of decode_qcs
Use 0 for success of decode_qcs operation else non-zero. This is to
follow the same model which is in use in most of the function in MUX/H3
code.
2022-05-25 15:41:25 +02:00
Amaury Denoyelle
e1cad8bc03 MINOR: mux-quic: add traces in qc_recv()
Just add traces in qc_recv() similarly to qc_send() function.
2022-05-25 15:41:25 +02:00
Amaury Denoyelle
1c25b18e17 MINOR: mux-quic: delay cs_endpoint allocation
Do not allocate cs_endpoint for every QCS instances in qcs_new().
Instead, this is delayed to qc_attach_cs() function.

In effect, with H3 as app protocol, cs_endpoint will be allocated on
HEADERS parsing. Thus, no cs_endpoint is allocated for H3 unidirectional
streams which do not convey any HTTP data.
2022-05-25 15:41:25 +02:00
Amaury Denoyelle
93fba32430 MINOR: mux-quic: do not alloc quic_stream_desc for uni remote stream
qc_stream_desc type is required for sending. Thus, it is not required
for an unidirectional remote stream where only receive will be
performed.
2022-05-25 15:41:25 +02:00
Amaury Denoyelle
019a182ebf Revert "MINOR: mux-quic: activate qmux traces on stdout via macro"
This reverts commit 251eadfce5.

This patch is similar to the previous revert for QUIC-MUX traces.
2022-05-23 11:06:42 +02:00
Amaury Denoyelle
d0c62328af BUG/MEDIUM: mux-quic: adjust buggy proxy closing support
The wake handler detects if the frontend is closed. This can happen if
the proxy has been disabled individually or even on process soft-stop.
Before this patch, in this condition QCS instances were freed before
being detached from the cs_endpoint. This clearly violates the haproxy
connection architecture and cause a BUG_ON statement crash in cs_free().

To handle this properly, cs_endpoint is notified by setting RD_SH|WR_SH
on connection flags. The cs_endpoint will thus use the detach operation
which allows the QCS instance to be freed.

This code allows the soft-stop process to complete as soon as possible.
However, the client is not notified about the connection closing. It
should be done by emitting a H3 GOAWAY + CONNECTION_CLOSE. Sadly, this
is impossible at this stage because the listener sockets are closed so
the quic-conn cannot use it to emit new frames. At this stage the client
will most probably detect connection closing on its idle timeout
expiration.

Thus, to completely support proxy closing/soft-stop, important
architecture changes are required in QUIC socket management. This is
also linked with the reload feature.
2022-05-23 11:05:52 +02:00
Willy Tarreau
b5821e12ce MINOR: connection: add flag MX_FL_FRAMED to mark muxes relying on framed xprt
In order to be able to check compatibility between muxes and transport
layers, we'll need a new flag to tag muxes that work on framed transport
layers like QUIC. Only QUIC has this flag now.
2022-05-20 18:41:55 +02:00
Amaury Denoyelle
cc3d7166f4 MINOR: mux-quic: close connection on error if different data at offset
As specified by the RFC reception of different STREAM data for the same
offset should be treated with a CONNECTION_CLOSE with error
PROTOCOL_VIOLATION.

Use ncbuf API to detect this case : if add operation fails with
NCB_RET_DATA_REJ with add mode NCB_ADD_COMPARE.
2022-05-20 17:56:00 +02:00
Amaury Denoyelle
209404bff1 MINOR: mux-quic: emit STREAM_LIMIT_ERROR
Send a CONNECTION_CLOSE on reception of a STREAM frame for a STREAM id
exceeding the maximum value enforced. Only implemented for bidirectional
streams for the moment.
2022-05-20 17:52:07 +02:00
Amaury Denoyelle
d46b0f52ae MINOR: mux-quic: emit FLOW_CONTROL_ERROR
Send a CONNECTION_CLOSE if the peer emits more data than authorized by
our flow-control. This is implemented for both stream and connection
level.

Fields have been added in qcc/qcs structures to differentiate received
offsets for limit enforcing with consumed offsets for sending of
MAX_DATA/MAX_STREAM_DATA frames.
2022-05-20 17:47:09 +02:00
Amaury Denoyelle
9fab9fd7e5 MINOR: quic/mux-quic: define CONNECTION_CLOSE send API
Define an API to easily set a CONNECTION_CLOSE. This will mainly be
useful for the MUX when an error is detected which require to close the
whole connection.

On the MUX side, a new flag is added when a CONNECTION_CLOSE has been
prepared. This will disable add future send operations.
2022-05-20 17:26:56 +02:00
Amaury Denoyelle
8b87b15c22 MINOR: mux-quic: free RX buf if empty
Release the QCS RX buffer if emptied afer qcs_consume(). This improves
memory usage and avoids a QCS to keep an allocated buffer, particularly
when no data is received anymore. Buffer is automatically reallocated if
needed via qc_get_ncbuf().
2022-05-18 16:25:07 +02:00
Amaury Denoyelle
7313f5ee7e BUG/MINOR: mux-quic: support nul buffer with qc_free_ncbuf()
qc_free_ncbuf() may now be used with a NCBUF_NULL buffer as parameter.
This is useful when using this function on a QCS with no allocated
buffer. This case was not reproduced for the moment, but it will soon
become more present as buffers will be released if emptied.

Also a call to offer_buffers() is added to conform with the dynamic
buffer management of haproxy.
2022-05-18 16:25:07 +02:00
Amaury Denoyelle
c830e1e904 MINOR: mux-quic: implement MAX_DATA emission
This commit is similar to the previous one but deals with MAX_DATA for
connection-level data flow control. It uses the same function
qcc_consume_qcs() to update flow control level and generate a MAX_DATA
frame if needed.
2022-05-18 16:25:07 +02:00
Amaury Denoyelle
a977355aa1 MINOR: mux-quic: implement MAX_STREAM_DATA emission
Send MAX_STREAM_DATA frames when at least half of the allocated
flow-control has been demuxed, frame and cleared. This is necessary to
support QUIC STREAM with received data greater than a buffer.

Transcoders must use the new function qcc_consume_qcs() to empty the QCS
buffer. This will allow to monitor current flow-control level and
generate a MAX_STREAM_DATA frame if required. This frame will be emitted
via qc_io_cb().
2022-05-18 16:25:07 +02:00
Amaury Denoyelle
c985cb167d MINOR: mux-quic: reorganize flow-control frames emission
Adjust the mechanism for MAX_STREAMS_BIDI emission. When a bidirectional
stream is removed, current flow-control level is checked. If needed, a
MAX_STREAMS_BIDI frame is generated and inserted in a new list in the
QCS instance. The new frames will be emitted at the start of qc_send().

This has no impact on the current MAX_STREAMS_BIDI behavior. However,
this mechanism is more flexible and will allow to implement quickly
MAX_STREAM_DATA/MAX_DATA emission.
2022-05-18 15:52:44 +02:00
Amaury Denoyelle
3a0864067a MINOR: mux-quic: remove qcc_decode_qcs() call in XPRT
Slightly change the interface for qcc_recv() between MUX and XPRT. The
MUX is now responsible to call qcc_decode_qcs(). This is cleaner as now
the XPRT does not have to deal with an extra QCS parameter and the MUX
will call qcc_decode_qcs() only if really needed.

This change is possible since there is no extra buffering for
out-of-order STREAM frames and the XPRT does not have to handle buffered
frames.
2022-05-18 15:50:57 +02:00
Amaury Denoyelle
37c2e4a65a MEDIUM: mux-quic: implement recv on io-cb
Previously, qc_io_cb() of mux-quic only dealt with TX. Add support for
RX in it. This is done through a new function qc_recv(qcc). It loops
over all QCS instances and call qcc_decode_qcs(qcs).

This has no impact from the quic-conn layer as qcc_decode_qcs(qcs) is
called directly. However, this allows to have a resume point when demux
is blocked on the upper layer HTX full buffer.

Note that for the moment, only RX for bidirectional streams is managed
in qc_io_cb(). Unidirectional streams use their own mechanism for both
TX/RX. It should be unified in the near future in a refactoring.
2022-05-18 15:43:22 +02:00
Amaury Denoyelle
00f87bbaa3 CLEANUP: mux-quic: remove unused fields for Rx
Rx has been simplified since the conversion of buffer to a ncbuf. The
old buffer can now be removed. The frms tree is also removed. It was
used previously to stored out-of-order received STREAM frames. Now the
MUX is able to buffer them directly into the ncbuf.
2022-05-13 17:29:52 +02:00
Amaury Denoyelle
1290f1ebfb MEDIUM: mux-quic/h3/hq-interop: use ncbuf for bidir streams
Add a ncbuf for data reception on qcs. Thanks to this, the MUX is able
to buffered all received frame directly into the buffer. Flow control
parameters will be used to ensure there is never an overflow.

This change will simplify Rx path with the future deletion of acked
frames tree previously used for frames out of order.
2022-05-13 17:28:46 +02:00
Willy Tarreau
df94313e0e BUG/MEDIUM: mux-quic: fix a thinko in the latest cs/endpoint cleanup
Fred & Amaury found that I messed up with qc_detach() in commit 4201ab791
("CLEANUP: muxes: make mux->attach/detach take a conn_stream endpoint"),
causing a segv in this case with endp->cs == NULL being passed to
__cs_mux(). It obviously ought to have been endp->target like in other
muxes.

No backport needed.
2022-05-13 16:34:40 +02:00
Willy Tarreau
4201ab791d CLEANUP: muxes: make mux->attach/detach take a conn_stream endpoint
The mux ->detach() function currently takes a conn_stream. This causes
an awkward situation where the caller cs_detach_endp() has to partially
mark it as released but not completely so that ->detach() finds its
endpoint and context, and it cannot be done later since it's possible
that ->detach() deletes the endpoint. As such the endpoint link between
the conn_stream and the mux's stream is in a transient situation while
we'd like it to be clean so that the mux's ->detach() code can call any
regular function it wants that knows the regular semantics of the
relation between the CS and the endpoint.

A better approach consists in slightly modifying the detach() API to
better match the reality, which is that the endpoint is detached but
still alive and that it's the only part the function is interested in.

As such, this patch modifies the function to take an endpoint there,
and by analogy (or simplicity) does the same for ->attach(), even
though it looks less important there since we're always attaching an
endpoint to a conn_stream anyway. It is possible that in the future
the API could evolve to use more endpoints that provide a bit more
flexibility in the API, but at this point we don't need to go further.
2022-05-13 14:28:48 +02:00
Willy Tarreau
01c2a4a86f MINOR: mux-quic: remove the now unneeded conn_stream from the qcs
Since we always have a valid endpoint we can safely use it to access
the conn_stream and stop using qcs->cs. That's one less pointer to
care about.
2022-05-13 14:28:48 +02:00
Willy Tarreau
15b0721ca5 CLEANUP: mux-quic: always take the endp from the qcs not the cs
At a few places the endpoint pointer was retrieved from the conn_stream
while it's safer and more long-term proof to take it from the qcs. Let's
just do that.
2022-05-13 14:27:57 +02:00
Frédéric Lécaille
b57de07a21 BUG/MINOR: mux_quic: Dropped packet upon retransmission for closed streams
We rely on the largest ID which was used to open streams to know if the
stream we received STREAM frames for is closed or not. If closed, we return the
same status as the one for a STREAM frame which  was a already received one for
on open stream.
2022-05-03 10:13:40 +02:00
Frédéric Lécaille
664741e1c5 MINOR: quic: Make the quic_conn be aware of the number of streams
This is required when the retransmitted frame types when the mux is released.
We add a counter for the number of streams which were opened or closed by the mux.
After the mux has been released, we can rely on this counter to know if the STREAM
frames are retransmitted ones or not.
2022-05-03 10:13:40 +02:00
Amaury Denoyelle
f1fc0b393b MINOR: mux-quic: support full request channel buffer
If the request channel buffer is full, H3 demuxing must be interrupted
on the stream until some read is performed. This condition is reported
if the HTX stream buffer qcs.rx.app_buf is full.

In this case, qcs instance is marked with a new flag QC_SF_DEM_FULL.
This flag cause the H3 demuxing to be interrupted. It is cleared when
the HTX buffer is read by the conn-stream layer through rcv_buf
operation.

When the flag is cleared, the MUX tasklet is woken up. However, as MUX
iocb does not treat Rx for the moment, this is useless. It must be fix
to prevent possible freeze on POST transfers.

In practice, for the moment the HTX buffer is never full as the current
Rx code is limited by the quic-conn receive buffer size and the
incomplete flow-control implementation. So for now this patch is not
testable under the current conditions.
2022-05-02 11:19:02 +02:00
Amaury Denoyelle
74cf237ecd MEDIUM: quic: do not ack packet with invalid STREAM
If the MUX cannot handle immediately nor buffer a STREAM frame, the
packet containing it must not be acknowledge. This is in conformance
with the RFC9000.

qcc_recv() return codes have been adjusted to differentiate an invalid
frame with an already fully received offset which must be acknowledged.
2022-04-29 16:16:19 +02:00
Frédéric Lécaille
3e3a621447 MINOR: quic: old data distinction for qc_send_app_pkt()
Modify qc_send_app_pkt() to distinguish the case where it sends new data
against the case where it sends old data during probing retransmissions.
We add <old_data> boolean parameter to this function to do so. The mux
never directly send old data when probing retransmissions are needed by
the connection.
2022-04-28 16:22:40 +02:00
Frédéric Lécaille
b917191817 MINOR: quic: Prepare quic_frame struct duplication
We want to track the frames which have been duplicated during retransmissions so
that to avoid uselessly retransmitting frames which would already have been
acknowledged. ->origin new member is there to store the frame from which a copy
was done, ->reflist is a list to store the frames which are copies.
Also ensure all the frames are zeroed and that their ->reflist list member is
initialized.
Add QUIC_FL_TX_FRAME_ACKED flag definition to mark a TX frame as acknowledged.
2022-04-28 16:22:40 +02:00
Amaury Denoyelle
47447af1ef MINOR: mux-quic: add a app-layer context in qcs
Define 2 new callback for qcc_app_ops : attach and detach. They are
called when a qcs instance is respectively allocated and freed. If
implemented, they can allocate a custom context stored in the new
abstract field ctx of qcs.

For now, h3 and hq-interop does not use these new callbacks. They will
be soon implemented by the h3 layer to allocate a context used for
stateful demuxing.

This change is required to support the demuxing of H3 frames bigger than
a buffer.
2022-04-28 15:44:19 +02:00
Amaury Denoyelle
3df8ca0a4d MINOR: mux-quic: partially copy Rx frame if almost full buf
Improve the reception for STREAM frames. In qcc_recv(), if the frame is
bigger than the remaining space in rx buffer, do not reject it wholly.
Instead, copy as much data as possible. The rest of the data is
buffered.

This is necessary to handle H3 frames bigger than a buffer. The H3 code
does not demux until the frame is complete or the buffer is full.
Without this, the transfer on payload larger than the Rx buffer can
rapidly freeze.
2022-04-28 15:42:21 +02:00
Amaury Denoyelle
44d0912f7b MINOR: mux-quic: count local flow-control stream limit on reception
Add new qcs fields to count the sum of bytes received for each stream.
This is necessary to enforce flow-control for reception on the peer.

For the moment, the implementation is partial. No MAX_STREAM_DATA or
FLOW_CONTROL_ERROR are emitted. BUG_ON statements are here as a
remainder.

This means that for the moment we do not support POST payloads greater
that the initial max-stream-data announced (256k currently).

At least, we now ensure that we never buffer a frame which overflows the
flow-control limit : this ensures that the memory consumption per stream
should stay under control.
2022-04-28 14:56:14 +02:00
Amaury Denoyelle
17014a64bf BUG/MINOR: mux-quic: fix leak if cs alloc failure
qcs and its field are not properly freed if the conn-stream allocation
fail in qcs_new(). Fix this by having a proper deinit code with a
dedicated label.
2022-04-28 14:47:53 +02:00
Amaury Denoyelle
fe8f555f5c MINOR: mux-quic: adjust comment on emission function
Comments were not properly edited since the splitting of functions for
stream emission. Also "payload" argument has been renamed to "in" as it
better reflects the function purpose.
2022-04-28 14:47:21 +02:00
Amaury Denoyelle
b50f311c50 BUG/MINOR: mux-quic: fix build in release mode
Fix build when not using DEBUG_STRICT. 'ret' is reported as unused as it
is only tested in a BUG_ON statement.
2022-04-28 14:42:39 +02:00
Amaury Denoyelle
f6df6b440a BUG/MINOR: mux-quic: fix POST with abortonclose
Remove CS_EP_EOS set erroneously on qc_rcv_buf().

This fixes POST with abortonclose. Previously, request was preemptively
aborted by haproxy due to the incorrect EOS flag.

For the moment, EOS flag is not set anymore. It should be set to warn
about a premature close from the client.
2022-04-22 18:20:28 +02:00
Amaury Denoyelle
b710415e75 BUG/MEDIUM: mux-quic: fix stalled POST requets
Add a missing notify RECV for conn-stream in qcc_decode_qcs(). This
fixes stalled POST requests which may occur with some clients such as
ngtcp2.
2022-04-22 18:18:34 +02:00
Amaury Denoyelle
3eb892fd65 BUG/MINOR: mux-quic: remove dead code in qcs_xfer_data()
Since previous patch
 MINOR: mux-quic: split xfer and STREAM frames build
there is no way to report an error in qcs_xfer_data().

This should fix github issue #1669.
2022-04-22 09:51:56 +02:00
Amaury Denoyelle
a3daaec5a6 BUG/MINOR: mux-quic: handle null timeout
Do not initialize mux task timeout if timeout client is set to 0 in the
configuration. Check for the task before queuing it in qc_io_cb() or
qc_detach().

This fix a crash when timeout client is 0 or undefined.
2022-04-21 16:35:46 +02:00
Amaury Denoyelle
f3e03a4066 BUG/MINOR: mux-quic: unsubscribe on release
Unsubscribe from lower layer on qc_release. This ensures that the lower
layer won't wake up a null tasklet after the MUX has been released and
may prevent a crash.
2022-04-21 16:34:15 +02:00
Amaury Denoyelle
1b2dba531d MINOR: mux-quic: implement immediate send retry
Complete qc_send function. After having processed each qcs emission, it
will now retry send on qcs where transfer can continue. This is useful
when qc_stream_desc buffer is full and there is still data present in
qcs buf.

To implement this, each eligible qcs is inserted in a new list
<qcc.send_retry_list>. This is done on send notification from the
transport layer through qcc_streams_sent_done(). Retry emission until
send_retry_list is empty or the transport layer cannot proceed more
data.

Several send operations are now called on two different places. Thus a
new _qc_send_qcs() function is defined to factorize the code.

This change should maximize the throughput during QUIC transfers.
2022-04-21 12:04:04 +02:00