190 Commits

Author SHA1 Message Date
Amaury Denoyelle
251eadfce5 MINOR: mux-quic: activate qmux traces on stdout via macro
This commit is similar to the following one :
  commit 118b2cbf8430a9513947c27a8403ff380e1dcaf2
  MINOR: quic: activate QUIC traces at compilation

If the macro ENABLE_QUIC_STDOUT_TRACES is defined, qmux traces are
outputted automatically on stdout. This is useful for the haproxy-qns
interop docker image.
2022-03-25 14:51:14 +01:00
Amaury Denoyelle
fdcec3644a MINOR: mux-quic: add trace event for qcs_push_frame
Add a new qmux trace event QMUX_EV_QCS_PUSH_FRM. Its only purpose is to
display the meaningful result of a qcs_push_frame invocation.

A dedicated struct qcs_push_frm_trace_arg is defined to pass a series of
extra args for the trace output.
2022-03-25 14:51:14 +01:00
Amaury Denoyelle
fa29f33f2c MINOR: mux-quic: add trace event for frame sending
Define a new qmux event QMUX_EV_SEND_FRM. This allows to pass a
quic_frame as an extra argument. Depending on the frame type, a special
format can be used to log the frame content.

Currently this event is only used in qc_send_max_streams. Thus the
handler is only able to handle MAX_STREAMS frames.
2022-03-25 14:51:14 +01:00
Amaury Denoyelle
4f137577d7 MINOR: mux-quic: replace printfs by traces
Convert all printfs in the mux-quic code with traces.

Note that some meaningul printfs were not converted because they use
extra args in a format-string. This is the case inside qcs_push_frame
and qc_send_max_streams. A dedicated trace event should be implemented
for them to be able to display the extra arguments.
2022-03-25 14:51:14 +01:00
Amaury Denoyelle
dd4fbfb5b4 MINOR: mux-quic: declare the qmux trace module
Declare a new trace module for mux-quic named qmux. It will be used to
convert all printf to regular traces. The handler qmux_trace can uses a
connection and a qcs instance as extra arguments.
2022-03-25 14:51:10 +01:00
Amaury Denoyelle
9296091cf7 MINOR: mux-quic: convert fin on push-frame as boolean
This is only useful to display a clear 0/1 value in the traces. This has
no impact beyond this cosmetic change.
2022-03-25 14:45:45 +01:00
Amaury Denoyelle
1e5e5136ee MINOR: mux-quic: support MAX_DATA frame parsing
This commit is similar to the previous one but with MAX_DATA frames.
This allows to increase the connection level flow-control limit. If the
connection was blocked due to QC_CF_BLK_MFCTL flag, the flag is reseted.
2022-03-23 10:14:14 +01:00
Amaury Denoyelle
8727ff4668 MINOR: mux-quic: support MAX_STREAM_DATA frame parsing
Implement a MUX method to parse MAX_STREAM_DATA. If the limit is greater
than the previous one and the stream was blocked, the flag
QC_SF_BLK_SFCTL is removed.
2022-03-23 10:09:39 +01:00
Amaury Denoyelle
05ce55e582 MEDIUM: mux-quic: respect peer connection data limit
This commit is similar to the previous one, but this time on the
connection level instead of the stream.

When the connection limit is reached, the connection is flagged with
QC_CF_BLK_MFCTL. This flag is checked in qc_send.

qcs_push_frame uses a new parameter which is used to not exceed the
connection flow-limit while calling it repeatdly over multiple streams
instance before transfering data to the transport layer.
2022-03-23 10:05:29 +01:00
Amaury Denoyelle
6ea781919a MEDIUM: mux-quic: respect peer bidirectional stream data limit
Implement the flow-control max-streams-data limit on emission. We ensure
that we never push more than the offset limit set by the peer. When the
limit is reached, the stream is marked as blocked with a new flag
QC_SF_BLK_SFCTL to disable emission.

Currently, this is only implemented for bidirectional streams. It's
required to unify the sending for unidirectional streams via
qcs_push_frame from the H3 layer to respect the flow-control limit for
them.
2022-03-23 10:05:29 +01:00
Amaury Denoyelle
78396e5ee8 MINOR: mux-quic: use shorter name for flow-control fields
Rename the fields used for flow-control in the qcc structure. The
objective is to have shorter name for better readability while keeping
their purpose clear. It will be useful when the flow-control will be
extended with new fields.
2022-03-23 10:05:29 +01:00
Amaury Denoyelle
75d14ad5cb MINOR: mux-quic: add comments for send functions
Add comments on qc_send and qcs_push_frame. Also adjust the return of
qc_send to reflect the total bytes sent. This has no impact as currently
the return value is not checked by the caller.
2022-03-23 10:05:29 +01:00
Amaury Denoyelle
ac74aa531d MINOR: mux-quic: complete trace when stream is not found
Display the ID of the stream not found. This will help to detect when we
received retransmitted frames for an already closed stream.
2022-03-23 09:49:08 +01:00
Amaury Denoyelle
e0320b8aa6 CLEANUP: mux-quic: change comment style to not mess with git conflict
Remove "=======" symbols from the MUX buffer diagram. This is useful to
not mess with git conflict markers when resolving a conflict.
2022-03-23 09:48:43 +01:00
Frédéric Lécaille
f27b66faee BUG/MINOR: mux-quic: Missing I/O handler events initialization
This could lead to a mux erratic behavior. Sometimes the application layer could
not wakeup the mux I/O handler because it estimated it had already subscribed
to write events (see h3_snd_buf() end of implementation).
2022-03-21 11:29:40 +01:00
Frédéric Lécaille
4e22f28feb BUG/MINOR: mux-quic: Access to empty frame list from qc_send_frames()
This was revealed by libasan when each time qc_send_frames() is run at the first
time:

=================================================================
==84177==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7fbaaca2b3c8 at pc 0x560a4fdb7c2e bp 0x7fbaaca2b300 sp 0x7fbaaca2b2f8
READ of size 1 at 0x7fbaaca2b3c8 thread T6
    #0 0x560a4fdb7c2d in qc_send_frames src/mux_quic.c:473
    #1 0x560a4fdb83be in qc_send src/mux_quic.c:563
    #2 0x560a4fdb8a6e in qc_io_cb src/mux_quic.c:638
    #3 0x560a502ab574 in run_tasks_from_lists src/task.c:580
    #4 0x560a502ad589 in process_runnable_tasks src/task.c:883
    #5 0x560a501e3c88 in run_poll_loop src/haproxy.c:2675
    #6 0x560a501e4519 in run_thread_poll_loop src/haproxy.c:2846
    #7 0x7fbabd120ea6 in start_thread nptl/pthread_create.c:477
    #8 0x7fbabcb19dee in __clone (/lib/x86_64-linux-gnu/libc.so.6+0xfddee)

Address 0x7fbaaca2b3c8 is located in stack of thread T6 at offset 56 in frame
    #0 0x560a4fdb7f00 in qc_send src/mux_quic.c:514

  This frame has 1 object(s):
    [32, 48) 'frms' (line 515) <== Memory access at offset 56 overflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
      (longjmp and C++ exceptions *are* supported)
Thread T6 created by T0 here:
    #0 0x7fbabd1bd2a2 in __interceptor_pthread_create ../../../../src/libsanitizer/asan/asan_interceptors.cpp:214
    #1 0x560a5036f9b8 in setup_extra_threads src/thread.c:221
    #2 0x560a501e70fd in main src/haproxy.c:3457
    #3 0x7fbabca42d09 in __libc_start_main ../csu/libc-start.c:308

SUMMARY: AddressSanitizer: stack-buffer-overflow src/mux_quic.c:473 in qc_send_frames
2022-03-21 11:29:40 +01:00
Frdric Lcaille
2ee5c8b3dd BUG/MEDIUM: quic: Blocked STREAM when retransmitted
STREAM frames which are not acknowledged in order are inserted in ->tx.acked_frms
tree ordered by the STREAM frame offset values. Then, they are consumed in order
by qcs_try_to_consume(). But, when we retransmit frames, we possibly have to
insert the same STREAM frame node (with the same offset) in this tree.
The problem is when they have different lengths. Unfortunately the restransmitted
frames are not inserted because of the tree nature (EB_ROOT_UNIQUE). If the STREAM
frame which has been successfully inserted has a smaller length than the
retransmitted ones, when it is consumed they are tailing bytes in the STREAM
(retransmitted ones) which indefinitively remains in the STREAM TX buffer
which will never properly be consumed, leading to a blocking state.

At this time this may happen because we sometimes build STREAM frames
with null lengths. But this is another issue.

The solution is to use an EB_ROOT tree to support the insertion of STREAM frames
with the same offset but with different lengths. As qcs_try_to_consume() support
the STREAM frames retransmission this modification should not have any impact.
2022-03-15 10:38:48 +01:00
Amaury Denoyelle
6ccfa3c40f MEDIUM: mux-quic: improve bidir STREAM frames sending
The current implementation of STREAM frames emission has some
limitation. Most notably when we cannot sent all frames in a single
qc_send run.

In this case, frames are left in front of the MUX list. It will be
re-send individually before other frames, possibly another frame from
the same STREAM with new data. An opportunity to merge the frames is
lost here.

This method is now improved. If a frame cannot be send entirely, it is
discarded. On the next qc_send run, we retry to send to this position. A
new field qcs.sent_offset is used to remember this. A new frame list is
used for each qc_send.

The impact of this change is not precisely known. The most notable point
is that it is a more logical method of emission. It might also improve
performance as we do not keep old STREAM frames which might delay other
streams.
2022-03-11 11:37:31 +01:00
Amaury Denoyelle
54445d04e4 MINOR: quic: implement sending confirmation
Implement a new MUX function qcc_notify_send. This function must be
called by the transport layer to confirm the sending of STREAM data to
the MUX.

For the moment, the function has no real purpose. However, it will be
useful to solve limitations on push frame and implement the flow
control.
2022-03-11 11:37:31 +01:00
Amaury Denoyelle
db5d1a1b19 MINOR: mux-quic: improve opportunistic retry sending for STREAM frames
For the moment, the transport layer function qc_send_app_pkts lacks
features. Most notably, it only send up to a single Tx buffer and won't
retry even if there is frames left and its Tx buffer is now empty.

To overcome this limitation, the MUX implements an opportunistic retry
sending mechanism. qc_send_app_pkts is repeatedly called until the
transport layer is blocked on an external condition (such as congestion
control or a sendto syscall error).

The blocking was detected by inspecting the frame list before and after
qc_send_app_pkts. If no frame has been poped by the function, we
considered the transport layer to be blocked and we stop to send. The
MUX is subscribed on the lower layer to send the frames left.

However, in case of STREAM frames, qc_send_app_pkts might use only a
portion of the data and update the frame offset. So, for STREAM frames,
a new mechanism is implemented : if the offset field of the first frame
has not been incremented, it means the transport layer is blocked.

This should improve transfers execution. Before this change, there is a
possibility of interrupted transfer if the mux has not sent everything
possible and is waiting on a transport signaling which will never
happen.

In the future, qc_send_app_pkts should be extended to retry sending by
itself. All this code burden will be removed from the MUX.
2022-03-11 11:37:31 +01:00
Amaury Denoyelle
e2ec9421ea MINOR: mux-quic: prevent push frame for unidir streams
For the moment, unidirectional streams handling is not identical to
bidirectional ones in MUX/H3 layer, both in Rx and Tx path. As a safety,
skip over uni streams in qc_send.

In fact, this change has no impact because qcs.tx.buf is emptied before
we start using qcs_push_frame, which prevents the call to
qcs_push_frame. However, this condition will soon change to improve
bidir streams emission, so an explicit check on stream type must be
done.

It is planified to unify uni and bidir streams handling in a future
stage. When implemented, the check will be removed.
2022-03-11 11:37:31 +01:00
Amaury Denoyelle
c055e30176 MEDIUM: mux-quic: implement MAX_STREAMS emission for bidir streams
Implement the locally flow-control streams limit for opened
bidirectional streams. Add a counter which is used to count the total
number of closed streams. If this number is big enough, emit a
MAX_STREAMS frame to increase the limit of remotely opened bidirectional
streams.

This is the first commit to implement QUIC flow-control. A series of
patches should follow to complete this.

This is required to be able to handle more than 100 client requests.
This should help to validate the Multiplexing interop test.
2022-03-04 17:00:12 +01:00
Amaury Denoyelle
e9c4cc13fc MINOR: mux-quic: retry send opportunistically for remaining frames
This commit should fix the possible transfer interruption caused by the
previous commit. The MUX always retry to send frames if there is
remaining data after a send call on the transport layer. This is useful
if the transport layer is not blocked on the sending path.

In the future, the transport layer should retry by itself the send
operation if no blocking condition exists. The MUX layer will always
subscribe to retry later if remaining frames are reported which indicate
a blocking on the transport layer.
2022-03-04 17:00:12 +01:00
Amaury Denoyelle
2c71fe58f0 MEDIUM: mux-quic: use direct send transport API for STREAMs
Modify the STREAM emission in qc_send. Use the new transport function
qc_send_app_pkts to directly send the list of constructed frames. This
allows to remove the tasklet wakeup on the quic_conn and should reduce
the latency.

If not all frames are send after the transport call, subscribe the MUX
on the lower layer to be able to retry. Currently there is a bug because
the transport layer does not retry to send frames in excess after a
successful sendto. This might cause the transfer to be interrupted.
2022-03-04 17:00:12 +01:00
Amaury Denoyelle
0dc40f06d1 MINOR: mux-quic: complete functions to detect stream type
Improve the functions used to detect the stream characteristics :
uni/bidirectional and local/remote initiated.

Most notably, these functions are now designed to work transparently for
a MUX in the frontend or backend side. For this, we use the connection
to determine the current MUX side. This will be useful if QUIC is
implemented on the server side.
2022-03-04 17:00:12 +01:00
Amaury Denoyelle
749cb647b1 MINOR: mux-quic: refactor transport parameters init
Since QUIC accept handling has been improved, the MUX is initialized
after the handshake completion. Thus its safe to access transport
parameters in qc_init via the quic_conn.

Remove quic_mux_transport_params_update which was called by the
transport for the MUX. This improves the architecture by removing a
direct call from the transport to the MUX.

The deleted function body is not transfered to qc_init because this part
will change heavily in the near future when implementing the
flow-control.
2022-03-04 17:00:12 +01:00
Amaury Denoyelle
1455113e93 CLEANUP: quic: complete ABORT_NOW with a TODO comment
Add a TODO comment to not forget to properly implement error returned by
qcs_push_frame.
2022-03-04 16:56:51 +01:00
Amaury Denoyelle
0e3010b1bb MEDIUM: quic: rearchitecture Rx path for bidirectional STREAM frames
Reorganize the Rx path for STREAM frames on bidirectional streams. A new
function qcc_recv is implemented on the MUX. It will handle the STREAM
frames copy and offset calculation from transport to MUX.

Another function named qcc_decode_qcs from the MUX can be called by
transport each time new STREAM data has been copied.

The architecture is now cleaner with the MUX layer in charge of parsing
the STREAM frames offsets. This is required to be able to implement the
flow-control on the MUX layer.

Note that as a convenience, a STREAM frame is not partially copied to
the MUX buffer. This simplify the implementation for the moment but it
may change in the future to optimize the STREAM frames handling.

For the moment, only bidirectional streams benefit from this change. In
the future, it may be extended to unidirectional streams to unify the
STREAM frames processing.
2022-03-01 11:07:27 +01:00
Amaury Denoyelle
642ab06313 MINOR: quic: adjust buffer handling for STREAM transmission
Simplify the data manipulation of STREAM frames on TX. Only stream data
and len field are used to generate a valid STREAM frames from the
buffer. Do not use the offset field, which required that a single buffer
instance should be shared for every frames on a single stream.
2022-02-25 15:06:17 +01:00
Christopher Faulet
1329f2a12a REORG: conn_stream: move conn-stream stuff in dedicated files
Move code dealing with the conn-streams in dedicated files.
2022-02-24 11:00:02 +01:00
Amaury Denoyelle
c0b66ca73c MINOR: mux-quic: fix uninitialized return on qc_send
This should fix the github issue #1562.
2022-02-21 18:46:58 +01:00
Amaury Denoyelle
ea3e0355da MINOR: mux-quic: fix a possible null dereference in qc_timeout_task
The qcc instance should be tested as it is implied by a previous test
that it may be NULL. In this case, qc_timeout_task can be stopped.

This should fix github issue #1559.
2022-02-21 10:05:16 +01:00
Amaury Denoyelle
eb53e5baa1 MINOR: mux-quic: set EOS on rcv_buf
Flags EOI/EOS must be set on conn-stream when transfering the last data
of a stream in rcv_buf. This is activated if qcs HTX buffer has the EOM
flag and has been fully transfered.
2022-02-15 17:10:51 +01:00
Amaury Denoyelle
9a327a7c3f MINOR: mux-quic: implement rcv_buf
Implement the stream rcv_buf operation on QUIC mux.

A new buffer is stored in qcs structure named app_buf. This new buffer
will contains HTX and will be filled for example on H3 DATA frame
parsing.

The rcv_buf operation transfer as much as possible data from the HTX
from app_buf to the conn-stream buffer. This is mainly identical to
mux-h2. This is required to support HTTP POST data.
2022-02-15 17:10:51 +01:00
Amaury Denoyelle
0e0969d6cf MINOR: mux-quic: release idle conns on process stopping
Implement the idle frontend connection cleanup for QUIC mux. Each
connection is registered on the mux_stopping_list. On process closing,
the mux is notified via a new function qc_wake. This function immediatly
release the connection if the parent proxy is stopped.

This allows to quickly close the process even if there is QUIC
connection stucked on timeout.
2022-02-01 15:42:32 +01:00
Amaury Denoyelle
1136e9243a MEDIUM: mux-quic: delay the closing with the timeout
Do not close immediatly the connection if there is no bidirectional
stream opened. Schedule instead the mux timeout when this condition is
verified. On the timer expiration, the mux/connection can be freed.
2022-02-01 15:19:35 +01:00
Amaury Denoyelle
aebe26f8ba MINOR: mux-quic: create a timeout task
This task will be used to schedule a timer when there is no activity on
the mux. The timeout is set via the "timeout client" from the
configuration file.

The timeout task process schedule the timeout only on specific
conditions. Currently, it's done if there is no opened bidirectional
stream.

For now this task is not used. This will be implemented in the following
commit.
2022-02-01 15:19:35 +01:00
Amaury Denoyelle
d975148776 MINOR: mux-quic: do not consider CONNECTION_CLOSE for the moment
Remove the condition on CONNECTION_CLOSE reception to close immediately
streams. It can cause some crash as the QUIC xprt layer still access the
qcs to send data and handle ACK.

The whole interface and buffering between QUIC xprt and mux must be
properly reorganized to better handle this case. Once this is done, it
may have some sense to free the qcs streams on CONNECTION_CLOSE
reception.
2022-02-01 15:19:35 +01:00
Amaury Denoyelle
ce1f30dac8 MINOR: mux-quic: properly initialize qcc flags
Set qcc.flags to 0 on qc_init.
2022-02-01 15:19:35 +01:00
Amaury Denoyelle
6a4aebfbfc MINOR: mux-quic: add comment
Explain the qc_release_detached_streams function purpose and interface.
Most notably the return code which is the count of released streams.
2022-02-01 10:56:43 +01:00
Frédéric Lécaille
82468ea98e MINOR: quic: Remove the packet number space TX MT_LIST
There is no need to use an MT_LIST to store frames to send from a packet
number space. This is a reminiscence for multi-threading support for the TX part.
2022-01-20 16:43:06 +01:00
Frédéric Lécaille
b80b20c6ff MINOR: quic: Do not wakeup the I/O handler before the mux is started
If we wakeup the I/O handler before the mux is started, it is possible
it has enough time to parse the ClientHello TLS message and update the
mux transport parameters, leading to a crash.
So, we initialize ->qcc quic_conn struct member at the very last time,
when the mux if fully initialized. The condition to wakeup the I/O handler
from lstnr_rcv_pkt() is: xprt context and mux both initialized.
Note that if the xprt context is initialized, it implies its tasklet is
initialized. So, we do not check anymore this latter condition.
2022-01-12 18:08:29 +01:00
Frédéric Lécaille
19cd46e6e5 MINOR: quic: Reset ->conn quic_conn struct member when calling qc_release()
There may be remaining locations where ->conn quic_conn struct member
is used. So let's reset this.
Add a trace to have an idead when this connection is released.
2022-01-11 16:12:31 +01:00
Ilya Shipitsin
5e87bcf870 CLEANUP: assorted typo fixes in the code and comments This is 29th iteration of typo fixes 2022-01-03 14:40:58 +01:00
Frédéric Lécaille
677b99dca7 MINOR: quic: Add stream IDs to qcs_push_frame() traces
This is only for debug purpose.
2021-12-21 16:06:03 +01:00
Amaury Denoyelle
8a5b27a9b9 REORG: quic: move mux function outside of xprt
Move qcc_get_qcs() function from xprt_quic.c to mux_quic.c. This
function is used to retrieve the qcs instance from a qcc with a stream
id. This clearly belongs to the mux-quic layer.
2021-12-21 15:51:40 +01:00
Amaury Denoyelle
fdbf63e86e MINOR: mux-quic: fix trace on stream creation
Replace non-initialized qcs.by_id.key by the id to report the proper
stream ID on stream creation.
2021-12-17 09:55:01 +01:00
Amaury Denoyelle
f3b0ba7dc9 BUG/MINOR: mux-quic: properly initialize flow control
Initialize all flow control members on the qcc instance. Without this,
the value are undefined and it may be possible to have errors about
reached streams limit.
2021-12-08 15:26:16 +01:00
Amaury Denoyelle
5154e7a252 MINOR: quic: notify the mux on CONNECTION_CLOSE
The xprt layer is reponsible to notify the mux of a CONNECTION_CLOSE
reception. In this case the flag QC_CF_CC_RECV is positionned on the
qcc and the mux tasklet is waken up.

One of the notable effect of the QC_CF_CC_RECV is that each qcs will be
released even if they have remaining data in their send buffers.
2021-12-08 15:26:16 +01:00
Amaury Denoyelle
2873a31c81 MINOR: mux-quic: do not release qcs if there is remaining data to send
A qcs is not freed if there is remaining data in its buffer. In this
case, the flag QC_SF_DETACH is positionned.

The qcc io handler is responsible to remove the qcs if the QC_SF_DETACH
is set and their buffers are empty.
2021-12-08 15:26:16 +01:00