Commit Graph

589 Commits

Author SHA1 Message Date
Frédéric Lécaille
2aebaa49b1 BUG/MINOR: quic: Unexpected half open connection counter wrapping
This counter must be incremented only one time by connection and decremented
as soon as the handshake has failed or succeeded. This is a gauge. Under certain
conditions this counter could be decremented twice. For instance
after having received a TLS alert, then upon SSL_do_handshake() failure.
To stop having to deal to all the current combinations which can lead to such a
situation (and the next to come), add a connection flag to denote if this counter
has been already decremented for a connection. So, this counter must be decremented
only if this flag has not been already set.

Must be backported up to 2.6.
2022-06-20 14:57:09 +02:00
Frédéric Lécaille
b1cb958581 BUILD: quic: Wrong HKDF label constant variable initializations
Non constant expressions were used to initialize constant variables leading to
such compilation errors:
	src/xprt_quic.c:66:3: error: initializer element is not a constant expression
   .key_label_len    = strlen(QUIC_HKDF_KEY_LABEL_V1),
Reproduced with CC=gcc-4.9 compilation option.
Fix using macros for each HKDF label.
2022-06-20 14:50:19 +02:00
Frédéric Lécaille
e06f7459fa CLEANUP: quic: Remove any reference to boringssl
I do not think we will support boringssl for QUIC soon ;)
2022-06-16 15:58:48 +02:00
Frédéric Lécaille
301425b880 MEDIUM: quic: Compatible version negotiation implementation (draft-08)
At this time haproxy supported only incompatible version negotiation feature which
consists in sending a Version Negotiation packet after having received a long packet
without compatible value in its version field. This version value is the version
use to build the current packet. This patch does not modify this behavior.

This patch adds the support for compatible version negotiation feature which
allows endpoints to negotiate during the first flight or packets sent by the
client the QUIC version to use for the connection (or after the first flight).
This is done thanks to "version_information" parameter sent by both endpoints.
To be short, the client offers a list of supported versions by preference order.
The server (or haproxy listener) chooses the first version it also supported as
negotiated version.

This implementation has an impact on the tranport parameters handling (in both
direcetions). Indeed, the server must sent its version information, but only
after received and parsed the client transport parameters). So we cannot
encode these parameters at the same time we instantiated a new connection.

Add QUIC_TP_DRAFT_VERSION_INFORMATION(0xff73db) new transport parameter.
Add tp_version_information new C struct to handle this new parameter.
Implement quic_transport_param_enc_version_info() (resp.
quic_transport_param_dec_version_info()) to encode (resp. decode) this
parameter.
Add qc_conn_finalize() which encodes the transport parameters and configure
the TLS stack to send them.
Add ->negotiated_ictx quic_conn C struct new member to store the Initial
QUIC TLS context for the negotiated version. The Initial secrets derivation
is version dependent.
Rename ->version to ->original_version and add ->negotiated_version to
this C struct to reflect the QUIC-VN RFC denomination.
Modify most of the QUIC TLS API functions to pass a version as parameter.
Export the QUIC version definitions to be reused at least from quic_tp.c
(transport parameters.
Move the token check after the QUIC connection lookup. As this is the original
version which is sent into a Retry packet, and because this original version is
stored into the connection, we must check the token after having retreived this
connection.
Add packet version to traces.

See https://datatracker.ietf.org/doc/html/draft-ietf-quic-version-negotiation-08
for more information about this new feature.
2022-06-16 15:58:48 +02:00
Frédéric Lécaille
e17bf77218 MINOR: quic: Released QUIC TLS extension for QUIC v2 draft
This is not clear at all how to distinguish a QUIC draft version number from a
released one. And among these QUIC draft versions, which one must use the draft
QUIC TLS extension.

According to the QUIC implementations which support v2 draft, the TLS extension
(transport parameters) to be used is the released one
(TLS_EXTENSION_QUIC_TRANSPORT_PARAMETERS).

As the unique QUIC draft version we support is 0xff00001d and as at this time the
unique version with 0xff as most significant byte is this latter which must use
the draft TLS extension, we select the draft TLS extension
(TLS_EXTENSION_QUIC_TRANSPORT_PARAMETERS_DRAFT) only for such versions with 0xff
as most signification byte.
2022-06-16 14:56:24 +02:00
Frédéric Lécaille
86845c5171 MEDIUM: quic: Add QUIC v2 draft support
This is becoming difficult to handle the QUIC TLS related definitions
which arrive with a QUIC version (draft or not). So, here we add
quic_version C struct which does not define only the QUIC version number,
but also the QUIC TLS definitions which depend on a QUIC version.
Modify consequently all the QUIC TLS API to reuse these definitions
through new quic_version C struct.
Implement quic_pkt_type() function which return a packet type (0 up to 3)
depending on the QUIC version number.
Stop harding the Retry packet first byte in send_retry(): this is not more
possible because the packet type field depends on the QUIC version.
Also modify quic_build_packet_long_header() for the same reason: the packet
type depends on the QUIC version.
Add a quic_version C struct member to quic_conn C struct.
Modify qc_lstnr_pkt_rcv() to set this member asap.
Remove the version member from quic_rx_packet C struct: a packet is attached
asap to a connection (or dropped) which is the unique object which should
store the QUIC version.
Modify qc_pkt_is_supported_version() to return a supported quic_version C
struct from a version number.
Add Initial salt for QUIC v2 draft (initial_salt_v2_draft).
2022-06-16 14:56:24 +02:00
Frédéric Lécaille
ea0ec27eb4 MINOR: quic: Parse long packet version from qc_parse_hd_form()
This is to prepare the support for QUIC v2 version. The packet type
depends on the version. So, we must parse early enough the version
before defining the type of each packet.
2022-06-16 14:56:24 +02:00
Frédéric Lécaille
3f96a0a4c1 MINOR: quic: Add several nonce and key definitions for Retry tag
The nonce and keys used to cipher the Retry tag depend on the QUIC version.
Add these definitions for 0xff00001d (draft-29) and v2 QUIC version. At least
draft-29 is useful for QUIC tracker tests with "quic-force-retry" enabled
on haproxy side.
Validated with -v 0xff00001d ngtcp2 option.
Could not validate the v2 nonce and key at this time because not supported.
2022-06-16 14:56:24 +02:00
Frédéric Lécaille
01d515e013 BUG/MINOR: quic: Stop hardcoding Retry packet Version field
Use the same version as the one received. This is safe because the
version is treated before anything else sending a Version packet.

Must be backported to 2.6.
2022-06-16 14:56:24 +02:00
Willy Tarreau
8716875ea4 CLEANUP: quic: use task_new_on() for single-threaded tasks
This simply replaces a call to task_new(1<<thr) with task_new_on(thr)
so that we can later isolate the changes required to add more thread
group stuff.
2022-06-14 10:38:03 +02:00
Benoit DOLEZ
69e3f05b15 BUILD: quic: fix anonymous union for gcc-4.4
Building QUIC with gcc-4.4 on el6 shows this error:

src/xprt_quic.c: In function 'qc_release_lost_pkts':
src/xprt_quic.c:1905: error: unknown field 'loss' specified in initializer
compilation terminated due to -Wfatal-errors.
make: *** [src/xprt_quic.o] Error 1
make: *** Waiting for unfinished jobs....

Initializing an anonymous form of union like :
     struct quic_cc_event ev = {
          (...)
          .loss.time_sent = newest_lost->time_sent,
          (...)
     };

generates an error with gcc-4.4 but not when initializing the
fields outside of the declaration.
2022-06-08 11:24:36 +02:00
Amaury Denoyelle
9f17a5aa8a CLEANUP: quic: remove useless check on local UNI stream reception
The MUX now provides a single API for both uni and bidirectional
streams. It is responsible to reject reception on a local unidirectional
stream with the error STREAM_STATE_ERROR. This is already implemented in
qcc_recv(). As such, remove this duplicated check from xprt_quic.c.
2022-05-31 15:21:13 +02:00
Frédéric Lécaille
fdc1b96357 BUG/MINOR: quic: Fix QUIC_EV_CONN_PRSAFRM event traces
This is a quic_frame struct pointer which must be passed as parameter
to TRACE_PROTO() for such an event.
2022-05-31 14:46:02 +02:00
Frédéric Lécaille
e06ca65e8d MINOR: quic: Do not drop packets with RESET_STREAM frames
If the connection client timeout has expired, the mux is released.
If the client decides to initiate a new request, we send a STOP_SENDING
frame. Then, the client endessly sends a RESET_STREAM frame.

At this time, we simulate the fact that we support the RESET_STREAM frame
thanks to this ridiculously minimalistic patch.
2022-05-30 09:59:26 +02:00
Frédéric Lécaille
4df2fe90c8 MINOR: quic: Send STOP_SENDING frames if mux is released
If the connection client timeout has expired, the mux is released.
If the client decides to initiate a new request, we do not ack its
request. This leads the client to endlessly sent it request.

This patch makes a QUIC listener send a STOP_SENDING frame in such
a situation.
2022-05-30 09:59:26 +02:00
Frédéric Lécaille
6f7607ef1f MINOR: h3: Add a statistics module for h3
Add ->inc_err_cnt new callback to qcc_app_ops struct which can
be called from xprt to increment the application level error code counters.
It take the application context as first parameter to be generic and support
new QUIC applications to come.
Add h3_stats.c module with counters for all the frame types and error codes.
2022-05-30 09:59:26 +02:00
Frédéric Lécaille
eb79145f01 MINOR: quic_stats: Add transport new counters (lost, stateless reset, drop)
Add new counters to count the number of dropped packet upon parsing error, lost
sent packets and the number of stateless reset packet sent.
Take the oppportunity of this patch to rename CONN_OPENINGS to QUIC_ST_HALF_OPEN_CONN
(total number of half open connections) and QUIC_ST_HDSHK_FAILS to QUIC_ST_HDSHK_FAIL.
2022-05-30 09:59:26 +02:00
Frédéric Lécaille
91a211fb08 BUG/MINOR: quic: Largest RX packet numbers mixing
When we select the next encryption level in qc_treat_rx_pkts() we
must reset the local largest_pn variable if we do not want to reuse its
previous value for this encryption. This bug could only happend during
handshake step and had no visible impact because this variable
is only used during the header protection removal step which hopefully
supports the packet reordering.
2022-05-30 09:59:26 +02:00
Frédéric Lécaille
3ccea6d276 MINOIR: quic_stats: add QUIC connection errors counters
Add statistical counters for all the transport level connection errrors.
2022-05-30 09:59:26 +02:00
Frédéric Lécaille
c7785b5c26 MINOR: quic: Transport parameters dump
Add quic_transport_params_dump() static inline function to do so for
a quic_transport_parameters struct as parameter.
We use the trace API do dump these transport parameters both
after they have been initialized (RX/local) or received (TX/remote).
2022-05-30 09:59:26 +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
Frédéric Lécaille
57ac3faed7 CLEANUP: quic: No more used handshake output buffer
->obuf quic_conn struct member is no more used.
2022-05-30 09:59:26 +02:00
Frédéric Lécaille
f6954c5c3a MINOR: quic: Ignore out of packet padding.
We do not want to count the out of packet padding as being belonging
to an invalid packet, the firt byte of a QUIC packet being never null.
Some browsers like firefox proceeds this way to add PADDING frames
after an Initial packet and increase the size of their Initial packets.
2022-05-30 09:59:26 +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
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
2208d57ce7 Revert "MINOR: quic: activate QUIC traces at compilation"
This reverts commit 118b2cbf84.

This patch was useful mainly for the docker image of QUIC interop to
have traces on stdout.

A better solution has been found by integrating this patch directly in
the qns repository which is used to build the docker image. Thus, this
hack is not require anymore in the main repository.
2022-05-23 11:06:42 +02:00
Frédéric Lécaille
07968dcea3 BUG/MINOR: quic: Missing <conn_opening> stats counter decrementation
When we receive a CONNECTION_CLOSE frame, we should decrement this counter
if the handshake state was not successful and if we have not received
a TLS alert from the TLS stack.
2022-05-20 20:57:31 +02:00
Frédéric Lécaille
ad9895d133 BUG/MINOR: quic: Fixe a typo in qc_idle_timer_task()
The & operator was confused with | operator :-(
2022-05-20 20:57:31 +02:00
Willy Tarreau
787e92a4fb CLEANUP: listener: replace bind_conf->quic_force_retry with BC_O_QUIC_FORCE_RETRY
It was only set and used once, let's replace it now and take it out of
the ifdef.
2022-05-20 18:41:51 +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
Frédéric Lécaille
dfd1301035 MINOR: quic: Dynamic Retry implementation
We rely on <conn_opening> stats counter and tune.quic.retry_threshold
setting to dynamically start sending Retry packets. We continue to send such packets
when "quic-force-retry" setting is set. The difference is when we receive tokens.
We check them regardless of this setting because the Retry could have been
dynamically started. We must also send Retry packets when we receive Initial
packets without token if the dynamic Retry threshold was reached but only for connection
which are not currently opening or in others words for Initial packets without
connection already instantiated. Indeed, we must not send Retry packets for all
Initial packets without token. For instance a client may have already sent an
Initial packet without receiving Retry packet because the Retry feature was not
started, then the Retry starts on exeeding the threshold value due to others
connections, then finally our client decide to send another Initial packet
(to ACK Initial CRYPTO data for instance). It does this without token. So, for
this already existing connection we must not send a Retry packet.
2022-05-20 17:11:13 +02:00
Frédéric Lécaille
cbd59c7ab6 MINOR: quic: QUIC stats counters handling
First commit to handle the QUIC stats counters. There is nothing special to say
except perhaps for ->conn_openings which is a gauge to count the number of
connection openings. It is incremented after having instantiated a quic_conn
struct, then decremented when the handshake was successful (handshake completed
state) or failed or when the connection timed out without reaching the handshake
completed state.
2022-05-20 17:11:13 +02:00
Frédéric Lécaille
3fd92f69e0 BUG/MINOR: quic: Fix potential memory leak during QUIC connection allocations
Move the code which finalizes the QUIC connections initialisations after
having called qc_new_conn() into this function to benefit from its
error handling to release the memory allocated for QUIC connections
the initialization of which could not be finalized.
2022-05-20 17:11:13 +02:00
Frédéric Lécaille
a89659a752 MINOR: quic: Attach proxy QUIC stats counters to the QUIC connection
Make usage of EXTRA_COUNTERS_GET() do to so from qc_new_conn().
2022-05-20 17:11:13 +02:00
Frédéric Lécaille
6492e66e41 MINOR: quic: Move quic_lstnr_dgram_dispatch() out of xprt_quic.c
Remove this function from xprt_quic.c which for now implements only
"by thread attached to a connection" code.
2022-05-20 16:57:12 +02:00
Frédéric Lécaille
3f3ff47998 MINOR: quic: Retry implementation
Here is the format of a token:
        - format (1 byte)
        - ODCID (from 9 up 21 bytes)
        - creation timestamp (4 bytes)
        - salt (16 bytes)

A format byte is required to distinguish the Retry token from others sent in
NEW_TOKEN frames.

The Retry token is ciphered after having derived a strong secret from the cluster secret
and generated the AEAD AAD, as well as a 16 bytes long salt. This salt is
added to the token. Obviously it is not ciphered. The format byte is not
ciphered too.

The AAD are built by quic_generate_retry_token_aad() which concatenates the version,
the client SCID and the IP address and port. We had to implement quic_saddr_cpy()
to copy the IP address and port to the AAD buffer. Only the Retry SCID is generated
on our side to build a Retry packet, the others fields come from the first packet
received by the client. It must reuse this Retry SCID in response to our Retry packet.
So, we have not to store it on our side. Everything is offloaded to the client (stateless).
quic_generate_retry_token() must be used to generate a Retry packet. It calls
quic_pkt_encrypt() to cipher the token.

quic_generate_retry_check() must be used to check the validity of a Retry token.
It is able to decipher a token which arrives into an Initial packet in response
to a Retry packet. It calls parse_retry_token() after having deciphered the token
to store the ODCID into a local quic_cid struct variable. Finally this ODCID may
be stored into the transport parameter thanks to qc_lstnr_params_init().
The Retry token lifetime is 10 seconds. This lifetime is also checked by
quic_generate_retry_check(). If quic_generate_retry_check() fails, the received
packet is dropped without anymore packet processing at this time.
2022-05-20 16:57:12 +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
80d0572a31 BUG/MEDIUM: quic: fix Rx buffering
The quic-conn manages a buffer to store received QUIC packets. When the
buffer wraps, the gap is filled until the end with junk and packets can
be inserted at the start of the buffer.

On the other end, deletion is implemented via quic_rx_pkts_del().
Packets are removed one by one if their refcount is nul. If junk is
found, the buffer is emptied until its wrap.

This seems to work in most cases but a bug was found in a particular
case : on insertion if buffer gap is not at the end of the buffer. In
this case, the gap was filled, which is useless as now the buffer is
full and the packet cannot be inserted. Worst, on deletion, when junk is
removed there is a risk to removed new packets. This can happens in the
following case :
1. buffer contig space is too small, junk is inserted in the middle of
   it
2. on quic_rx_pkts_del() invocation, a packet is removed, but not the
   next one because its refcount is still positive. When a new packet is
   received, it will be stored after the junk.
3. on next quic_rx_pkts_del(), when junk is removed, all contig data is
   cleared, with newer packets data too.

This will cause a transfer between a client and haproxy to be stalled.
This can be reproduced with big enough POST requests. I triggered it
with ngtcp2 and 10M of posted data.

Hopefully, the solution of this bug is simple. If contig space is not
big enough to store a packet, but the space is not at the end of the
buffer, no junk is inserted and the packet is dropped as we cannot
buffered it. This ensures that junk is only present at the end of the
buffer and when removed no packets data is purged with it.
2022-05-18 15:02:14 +02:00
Amaury Denoyelle
45fce8fcb5 CLEANUP: quic: remove unused quic_rx_strm_frm
quic_rx_strm_frm type was used to buffered STREAM frames received out of
order. Now the MUX is able to deal directly with these frames and
buffered it inside its ncbuf.
2022-05-13 17:29:52 +02:00
Amaury Denoyelle
3db98e9d13 MEDIUM: mux-quic/h3/qpack: use ncbuf for uni streams
This commit is the equivalent for uni-streams of previous commit
  MEDIUM: mux-quic/h3/hq-interop: use ncbuf for bidir streams

All unidirectional streams data is now handle in MUX Rx ncbuf. The
obsolete buffer is not unused and will be cleared in the following
patches.
2022-05-13 17:29:49 +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
Frédéric Lécaille
a54e49d0b1 CLEANUP: quic: wrong use of eb*entry() macro
This wrong use has no consequence because the ->node member fields of
eb*node structs are the first.
2022-05-12 17:48:35 +02:00
Frédéric Lécaille
36b28ed012 MINOR: quic: Short packets always embed a trailing AEAD TAG
We must drop as soon as possible too small 1-RTT packets to be valid QUIC
packets to avoid replying with stateless reset packets.
2022-05-12 17:48:35 +02:00
Frédéric Lécaille
e2fb1bf487 MINOR: quic: Send stateless reset tokens
Add send_stateless_reset() to send a stateless reset packet. It prepares
a packet to build a 1-RTT packet with quic_stateless_reset_token_cpy()
to copy a stateless reset token derived from the cluster secret with
the destination connection ID received as salt.
Also add QUIC_EV_STATELESS_RST new trace event to at least to have a trace
of the connection which are reset.
2022-05-12 17:48:35 +02:00
Frédéric Lécaille
806e6cf392 MINOR: quic: Stateless reset token copy to transport parameters
A server may send the stateless reset token associated to the current
connection from its transport parameters. So, let's copy it from
qc_lstnt_params_init().
2022-05-12 17:48:35 +02:00
Frédéric Lécaille
395a64dd81 MINOR: qc_new_conn() rework for stateless reset
The stateless reset token of a connection is generated from qc_new_conn() when
allocating the first connection ID. A QUIC server can copy it into its transport
parameters to allow the peer to reset the associated connection.
This latter is not easily reachable after having returned from qc_new_conn().
We want to be able to initialize the transport parameters from this function which
has an access to all the information to do so.

Extract the code used to initialize the transport parameters from qc_lstnr_pkt_rcv()
and make it callable from qc_new_conn(). qc_lstnr_params_init() is implemented
to accomplish this task for a haproxy listener.
Modify qc_new_conn() to reduce its the number of parameters.
The source address coming from Initial packets is also copied from qc_new_conn().
2022-05-12 17:48:35 +02:00
Frédéric Lécaille
28a1795515 MINOR: quic: Initialize stateless reset tokens with HKDF secrets
Add quic_stateless_reset_token_init() wrapper function around
quic_hkdf_extract_and_expand() function to derive the stateless reset tokens
attached to the connection IDs from "cluster-secret" configuration setting
and call it each time we instantiate a QUIC connection ID.
2022-05-12 17:48:35 +02:00
Frédéric Lécaille
0226c521b0 MINOR: quic: new_quic_cid() code moving
This function will have to call another one from quic_tls.[ch] soon.
As we do not want to include quic_tls.h from xprt_quic.h because
quic_tls.h already includes xprt_quic.h, let's moving it into
xprt_quic.c.
2022-05-12 17:48:35 +02:00
Frédéric Lécaille
7cc8b3166a MINOR: quic: Add correct ack delay values to ACK frames
A ->time_received new member is added to quic_rx_packet to store the time the
packet are received. ->largest_time_received is added the the packet number
space structure to store this timestamp for the packet with a new largest
packet number to be acknowledged. QUIC_FL_PKTNS_NEW_LARGEST_PN new flag is
added to mark a packet number space as having to acknowledged a packet wih a
new largest packet number. In this case, the packet number space ack delay
must be recalculated.
Add quic_compute_ack_delay_us() function to compute the ack delay from the value
of the time a packet was received. Used only when a packet with a new largest
packet number.
2022-05-12 15:30:14 +02:00
Frédéric Lécaille
8726d633d4 MINOR: quic: Add a debug counter for sendto() errors
As we do not have any task to be wake up by the poller after sendto() error,
we add an sendto() error counter to the quic_conn struct.
Dump its values from qc_send_ppkts().
2022-05-12 15:11:53 +02:00
Frédéric Lécaille
d62240c9e5 BUG/MINOR: quic: Dropped retransmitted STREAM frames
It is possible that we continue to receive retransmitted STREAM frames after
the mux have been released. We rely on the ->rx.streams[].nb_streams counter
to check the stream was closed. If not, at this time we drop the packet.
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
Frédéric Lécaille
1601395063 MINOR: quic: moving code for QUIC loss detection
qc_qc_packet_loss_lookup() is definitively a QUIC loss detection function.
2022-04-29 16:46:56 +02:00
Frédéric Lécaille
88e5741c53 CLEANUP: quic: Remaining fprintf() debug trace
Development remaining trace.
2022-04-29 16:46:56 +02:00
Frédéric Lécaille
1231d3c179 MINOR: quic: Drop 0-RTT packets without secrets
If we received 0-RTT packets and no secrets were provided by the TLS stack
we must drop them.
2022-04-29 16:46:56 +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
Amaury Denoyelle
d46e335683 MEDIUM: quic: do not ACK packet with STREAM if MUX not present
If a packet contains a STREAM frame but the MUX is not allocated, the
frame cannot be enqueued. According to the RFC9000, we must not
acknowledge the packet under this condition.

This may prevents a bug with firefox which keeps trying on refreshing
the web page. This issue has already been detected before closing state
implementation : haproxy wasn't emitted CONNECTION_CLOSE and keeps
acknowledge STREAM frames despite not handle them.

In the future, it might be necessary to respond with a CONNECTION_CLOSE
if the MUX has already been freed.
2022-04-29 16:15:47 +02:00
Frédéric Lécaille
3e26698f89 MINOR: quic: Drop 0-RTT packets if not allowed
Drop the 0-RTT packets for a listener without early data configuration enabled.
2022-04-28 16:22:40 +02:00
Frédéric Lécaille
4646cf3b70 CLEANUP: quic: Rely on the packet length set by qc_lstnr_pkt_rcv()
This function is used to parse the QUIC packets carried by a UDP datagram.
When a correct packet could be found, the ->len RX packet structure value
is set to the packet length value. On the contrary, it is set to the remaining
number of bytes in the UDP datagram if no correct QUIC packet could be found.
So, there is no need to make this function return a status value. It allows
the caller to parse any QUIC packet carried by a UDP datagram without this.
2022-04-28 16:22:40 +02:00
Frédéric Lécaille
87373e7269 BUG/MINOR: quic: Missing Initial packet length check
Any client Initial packet carried in a datagram smaller than QUIC_INITIAL_PACKET_MINLEN(200)
bytes must be discarded. This does not mean we must discard the entire datagram.
So we must at least try to parse the packet length before dropping the packet
and return its length from qc_lstnr_pkt_rcv().
2022-04-28 16:22:40 +02:00
Frédéric Lécaille
77cb38d22d BUG/MEDIUM: quic: Possible crash on STREAM frame loss
A crash is possible under such circumtances:
    - The congestion window is drastically reduced to its miniaml value
    when a quic listener is experiencing extreme packet loss ;
    - we enqueue several STREAM frames to be resent and some of them could not be
    transmitted ;
    - some of the still in flight are acknowledged and trigger the
    stream memory releasing ;
    - when we come back to send the remaing STREAM frames, haproxy
    crashes when it tries to build them.

To fix this issue, we mark the STREAM frame as lost when detected as lost.
Then a lookup if performed for the stream the STREAM frames are attached
to before building them. They are released if the stream is no more available
or the data range of the frame is consumed.
2022-04-28 16:22:40 +02:00
Frédéric Lécaille
dafbde6c8c MINOR: quic: Wake up the mux to probe with new data
When we have to probe the peer, we must first try to send new data. This is done
here waking up the mux after having set the number of maximum number of datagrams
to send to QUIC_MAX_NB_PTO_DGRAMS (2). Of course, this is only the case if the
mux was subscribed to SEND events.
2022-04-28 16:22:40 +02:00
Frédéric Lécaille
d8b798d7ef BUG/MINOR: quic: Traces fix about remaining frames upon packet build failure 2022-04-28 16:22:40 +02:00
Frédéric Lécaille
834399c24a BUG/MINOR: quic: Avoid sending useless PADDING frame
This may happen in rare cases with extreme packet loss (30% for both TX and RX)
which leads the congestion window to decrease down to its minimal value (two
datagrams). Under such circumtances, no ack-eliciting frame can be added to
a packet by qc_build_frms(). In this case we must cancel the packet building
process if there is no ACK or probe (PING frame) to send.
2022-04-28 16:22:40 +02:00
Frédéric Lécaille
573b56b774 BUG/MINOR: quic: Wrong returned status by qc_build_frms()
This function must return a successful status as soon as it could be build
a frame to be embedded by a packet. This behavior was broken by the last
modifications. This was due to a dangerous "ret = 1" statement inside
a loop. This statement must be reach only if we go out of a switch/case
after a "break" statement.
Add comments to mention this information.
2022-04-28 16:22:40 +02:00
Frédéric Lécaille
337108ecda MINOR: quic: Do not send ACK frames when probing
When we are probing, we do not receive packets, furthermore all ACK frames have
already been sent. This is useless to send ACK when probing the peer. This
modification does not reset the flag which marks the connection as requiring an
ACK frame to be sent. If this is the case, this will be taken into an account
by after the probing process.
2022-04-28 16:22:40 +02:00
Frédéric Lécaille
7aef5f4c3f MEDIUM: quic: Enable the new datagram probing process
Make the two I/O handlers quic_conn_io_cb() and quic_conn_app_io_cb() call
qc_dgrams_retransmit() after probing retransmissions need was detected by
the timer task (qc_process_timer()).
We must modify qc_prep_pkts() to support QUIC_TLS_ENC_LEVEL_NONE as <next_tel>
parameter when called from qc_dgrams_retransmit().
2022-04-28 16:22:40 +02:00
Frédéric Lécaille
da342556c3 MEDIUM: quic: Mark copies of acknowledged frames as acknowledged
We call qc_release_frm() to do so from this function everywhere a frame
is released.
2022-04-28 16:22:40 +02:00
Frédéric Lécaille
1809c33d6e MINOR: quic: Mark packets as probing with old data
When probing retranmissions with old data are needed for the connection we
mark the packets as probing with old data to track them when acknowledged:
we do not resend frames with old data when lost.
2022-04-28 16:22:40 +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
96367158ab MEDIUM: quic: qc_requeue_nacked_pkt_tx_frms() rework
This function is used to requeue the TX frames from TX packets which have
been detected as lost. The modifications consist in avoiding resending frames from
duplicated frames used to probe the peer. This is useless. Only the original
frames loss must be taken into an account because detected as lost before
the retransmitted frames. If these latter are also detected as lost, other
duplicated frames would have been retransmitted before their loss detection.
2022-04-28 16:22:40 +02:00
Frédéric Lécaille
e248e378c8 MEDIUM: quic: Retransmission functions rework
qc_prep_fast_retrans() and qc_prep_hdshk_fast_retrans() are modified to
take two list of frames as parameters. Two lists are needed for
qc_prep_hdshk_fast_retrans() to build datagrams with two packets during
handshake. qc_prep_fast_retrans() needs two lists of frames to be used
to send two datagrams with one list by datagram.
2022-04-28 16:22:40 +02:00
Frédéric Lécaille
a9568411e4 MEDIUM: quic: New functions for probing rework
We want to be able to resend frames from list of frames during handshakes to
resend datagrams with the same frames as during the first transmissions.
This leads to decrease drasctically the chances of frame fragmentation due to
variable lengths of the packet fields. Furthermore the frames were not duplicated
when retransmitted from a packet to another one. This must be the case only during
packet loss dectection.

qc_dup_pkt_frms() is there to duplicate the frames from an input list to an output
list. A distinction is made between STREAM frames and the other ones because we
can rely on the "acknowledged offset" the aim of which is to track the number
of bytes which were acknowledged from TX STREAM frames.

qc_release_frm() in addition to release the frame passed as parameter, also mark
the duplicate STREAM frames as acknowledeged.

qc_send_hdshk_pkts() is the qc_send_app_pkts() counterpart to send datagrams from
at most two list of frames to be able to coalesced packets from two different
packet number spaces

qc_dgrams_retransmit() is there to probe the peer with datagrams depending on the
need of the packet number spaces which must be flag with QUIC_FL_PKTNS_PROBE_NEEDED
by the PTO timer task (qc_process_timer()).
2022-04-28 16:22:40 +02:00
Frédéric Lécaille
3ef729a643 MINOR: quic: process_timer() rework
Add QUIC_FL_CONN_RETRANS_NEEDED connection flag definition to mark a quic_conn
struct as needing a retranmission.
Add QUIC_FL_PKTNS_PROBE_NEEDED to mark a packet number space as needing a
datagram probing.
Set these flags from process_timer() to trigger datagram probings.
Do not initiate anymore datagrams probing from any quic encryption level.
This will be done from the I/O handlers (quic_conn_io_cb() during handshakes and
quic_conn_app_io_cb() after handshakes).
2022-04-28 16:22:40 +02:00
Frédéric Lécaille
e87b3ee9f5 MINOR: quic: Add traces about TX frame memory releasing
Add such traces in qc_treat_acked_tx_frm(). This should be helpful to track memory
leak issues for TX frames.
2022-04-28 16:22:40 +02:00
Frédéric Lécaille
b44cbc68a6 MINOR: quic: Do not retransmit frames from coalesced packets
Add QUIC_FL_TX_PACKET_COALESCED flag to mark a TX packet as coalesced with others
to build a datagram.
Ensure we do not directly retransmit frames from such coalesced packets. They must
be retransmitted from their packet number spaces to avoid duplications.
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
Frédéric Lécaille
fc88844d2c MINOR: quic: Improve qc_prep_pkts() flexibility
We want to be able to chosse the list of frames we want to prepare in packets
to be send. This is to modify the retransmission process (to come).
2022-04-28 16:22:40 +02:00
Amaury Denoyelle
03cc62c840 MINOR: quic: decode as much STREAM as possible
Add a loop in the bidi STREAM function. This will call repeatdly
qcc_decode_qcs() and dequeue buffered frames.

This is useful when reception of more data is interrupted because the
MUX buffer was full. qcc_decode_qcs() has probably free some space so it
is useful to immediatly retry reception of buffered frames of the qcs
tree.

This may fix occurences of stalled Rx transfers with large payload.
Note however that there is still room for improvment. The conn-stream
layer is not able at this moment to retrigger demuxing. This is because
the mux io-handler does not treat Rx : this may continue to cause
stalled tranfers.
2022-04-28 16:10:10 +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
Willy Tarreau
7e2e4f8401 CLEANUP: tree-wide: remove 25 occurrences of unneeded fcntl.h
There were plenty of leftovers from old code that were never removed
and that are not needed at all since these files do not use any
definition depending on fcntl.h, let's drop them.
2022-04-26 10:59:48 +02:00
Willy Tarreau
79367f9a8d BUILD: xprt: use an initcall to register the transport layers
Transport layers (raw_sock, ssl_sock, xprt_handshake and xprt_quic)
were using 4 constructors and 2 destructors. The 4 constructors were
replaced with INITCALL and the destructors with REGISTER_POST_DEINIT()
so that we do not depend on this anymore.
2022-04-25 19:18:24 +02:00
Amaury Denoyelle
7586bef6d7 BUG/MINOR: quic: fix use-after-free with trace on ACK consume
When using qc_stream_desc_ack(), the stream instance may be freed if
there is no more data in its buffers. This also means that all frames
still stored waiting for ACK for this stream are freed via
qc_stream_desc_free().

This is particularly important in quic_stream_try_to_consume() where we
loop over the frames tree of the stream. A use-after-free is present in
cas the stream has been freed in the trace "stream consumed" which
dereference the frame. Fix this by first checking if the stream has been
freed or not.

This bug was detected by using ASAN + quic traces enabled.
2022-04-25 15:01:53 +02:00
Frédéric Lécaille
89a2ceb1fb BUG/MEDIUM: quic: Possible crash with released mux
It is possible the xprt layer have to process retransmitted STREAM frames after
the mux was released. In this case, there is no need to try to wake it up.
2022-04-21 15:27:33 +02:00
Amaury Denoyelle
d2f80a2e63 MINOR: quic: limit total stream buffers per connection
MUX streams can now allocate multiple buffers for sending. quic-conn is
responsible to limit the total count of allowed allocated buffers. A
counter is stored in the new field <stream_buf_count>.

For the moment, the value is hardcoded to 30.

On stream buffer allocation failure, the qcc MUX is flagged with
QC_CF_CONN_FULL. The MUX is then woken up as soon as a buffer is freed,
most notably on ACK reception.
2022-04-21 12:04:04 +02:00
Amaury Denoyelle
1b81dda3e0 MINOR: quic-stream: refactor ack management
Acknowledge of STREAM has been complexified with the introduction of
stream multi buffers. Two functions are executing roughly the same set
of instructions in xprt_quic.c.

To simplify this, move the code complexity in a new function
qc_stream_desc_ack(). It will handle offset calculation, removal of
data, freeing oldest buffer and freeing stream instance if required.
The qc_stream_desc API is cleaner as qc_stream_desc_free_buf() ambiguous
function has been removed.
2022-04-21 12:04:04 +02:00
Amaury Denoyelle
a456920491 MEDIUM: quic: implement multi-buffered Tx streams
Complete the qc_stream_desc type to support multiple buffers on
emission. The main objective is to increase the transfer throughput.
The MUX is now able to transfer more data without having to wait ACKs.

To implement this feature, a new type qc_stream_buf is declared. it
encapsulates a buffer with a list element. New functions are defined to
retrieve the current buffer, release it or allocate a new one. Each
buffer is kept in the qc_stream_desc list until all of its data is
acknowledged.

On the MUX side, a qcs uses the current stream buffer to transfer data.
Once the buffer is full, it is released and a new one will be allocated
on a future qc_send() invocation.
2022-04-21 12:03:20 +02:00
Amaury Denoyelle
e4301da5ed MINOR: quic-stream: use distinct tree nodes for quic stream and qcs
Simplify the model qcs/qc_stream_desc. Each types has now its own tree
node, stored respectively in qcc and quic-conn trees. It is still
necessary to mark the stream as detached by the MUX once all data is
transfered to the lower layer.

This might improve slightly the performance on ACK management as now
only the lookup in quic-conn is necessary. On the other hand, memory
size of qcs structure is increased.
2022-04-21 11:05:58 +02:00
Amaury Denoyelle
0cc02a345b REORG: quic: use a dedicated module for qc_stream_desc
Regroup all type definitions and functions related to qc_stream_desc in
the source file src/quic_stream.c.

qc_stream_desc complexity will be increased with the development of Tx
multi-buffers. Having a dedicated module is useful to mix it with
pure transport/quic-conn code.
2022-04-21 11:05:27 +02:00
Amaury Denoyelle
f7ff9cbfe1 BUG/MEDIUM: quic: properly clean frames on stream free
A released qc_stream_desc is freed as soon as all its buffer content has
been acknowledged. However, it may still contains other frames waiting
for ACK pointing to deleted buffer content. This can happen on
retransmission.

When freeing a qc_stream_desc, free all its frames in acked_frms tree to
fix memory leak. This may also possibly fix a crash on retransmission.
Now, the frames are properly removed from a packet. This ensure we do
not retransmit a frame whose buffer is deallocated.
2022-04-15 13:45:28 +02:00
Amaury Denoyelle
5d774dee55 MINOR: quic: emit CONNECTION_CLOSE on app init error
Emit a CONNECTION_CLOSE if the app layer cannot be properly initialized
on qc_xprt_start. This force the quic-conn to enter the closing state
before being closed.

Without this, quic-conn normal operations continue, despite the
app-layer reported as not initialized. This behavior is undefined, in
particular when handling STREAM frames.
2022-04-14 15:09:32 +02:00
Amaury Denoyelle
05d4ae6436 BUG/MINOR: quic: fix return value for error in start
Fix the return value used in quic-conn start callback for error. The
caller expects a negative value in this case.

Without this patch, the quic-conn and the connection stack are not
closed despite an initialization failure error, which is an undefined
behavior and may cause a crash in the end.
2022-04-14 15:08:16 +02:00
Frédéric Lécaille
bc964bd1ae BUG/MINOR: quic: Avoid starting the mux if no ALPN sent by the client
If the client does not sent an ALPN, the SSL ALPN negotiation callback
is not called. However, the handshake is reported as successful. Check
just after SSL_do_handshake if an ALPN was negotiated. If not, emit a
CONNECTION_CLOSE with a TLS alert to close the connection.

This prevent a crash in qcc_install_app_ops() called with null as second
parameter value.
2022-04-13 16:48:43 +02:00
Christopher Faulet
6b0a0fb2f9 CLEANUP: tree-wide: Remove any ref to stream-interfaces
Stream-interfaces are gone. Corresponding files can be safely be removed. In
addition, comments are updated accordingly.
2022-04-13 15:10:16 +02:00
Willy Tarreau
784b868c97 MEDIUM: quic: move conn->qc into conn->handle
It was supposed to be there, and probably was not placed there due to
historic limitations in listener_accept(), but now there does not seem
to be a remaining valid reason for keeping the quic_conn out of the
handle. In addition in new_quic_cli_conn() the handle->fd was incorrectly
set to the listener's FD.
2022-04-11 19:33:04 +02:00
Willy Tarreau
54a1dcb1bb MEDIUM: xprt-quic: implement get_ssl_sock_ctx()
By being able to return the ssl_sock_ctx, we're now enabling the whole
set of SSL sample fetch methods to work on the current SSL context of
the QUIC connection, as seen in the following test showing a request
forwarded to an HTTP/1 server with plenty of SSL headers filled:

00000001:decrypt.clireq[000f:ffffffff]: GET / HTTP/1.1
00000001:decrypt.clihdr[000f:ffffffff]: host: localhost
00000001:decrypt.clihdr[000f:ffffffff]: user-agent: nghttp3/ngtcp2 client
00000001:decrypt.clihdr[000f:ffffffff]: x-src: 127.0.0.1
00000001:decrypt.clihdr[000f:ffffffff]: x-dst: 127.0.0.4
00000001:decrypt.clihdr[000f:ffffffff]: x-ssl_f_serial: D16197E7D3E634E9
00000001:decrypt.clihdr[000f:ffffffff]: x-ssl_f_key_alg: rsaEncryption
00000001:decrypt.clihdr[000f:ffffffff]: x-ssl_f_sig_alg: RSA-SHA1
00000001:decrypt.clihdr[000f:ffffffff]: x-ssl_fc: 1
00000001:decrypt.clihdr[000f:ffffffff]: x-ssl_fc_has_sni: 1
00000001:decrypt.clihdr[000f:ffffffff]: x-ssl_fc_sni: blah
00000001:decrypt.clihdr[000f:ffffffff]: x-ssl_fc_alpn: h3
00000001:decrypt.clihdr[000f:ffffffff]: x-ssl_fc_protocol: TLSv1.3
00000001:decrypt.clihdr[000f:ffffffff]: x-ssl_fc_cipher: TLS_AES_256_GCM_SHA384
00000001:decrypt.clihdr[000f:ffffffff]: x-ssl_fc_alg_keysize: 256
00000001:decrypt.clihdr[000f:ffffffff]: x-ssl_fc_use_keysize: 256
00000001:decrypt.clihdr[000f:ffffffff]: x-forwarded-for: 127.0.0.1

The code is trivial, but this is marked as medium as there's always
the risk that some of the callable functions do not like being called
on such SSL contexts.
2022-04-11 19:33:04 +02:00
Willy Tarreau
325fc63f5a BUILD: xprt-quic: replace ERR_func_error_string() with ERR_peek_error_func()
OpenSSL 3.0 warns that ERR_func_error_string() is deprecated. Using
ERR_peek_error_func() solves it instead, and this function was added to
the compat layer by commit 1effd9aa0 ("MINOR: ssl: Remove call to
ERR_func_error_string with OpenSSLv3").
2022-04-11 18:54:46 +02:00
Frédéric Lécaille
8c7927c6dd MINOR: quic_tls: Make key update use of reusable cipher contexts
We modify the key update feature implementation to support reusable cipher contexts
as this is done for the other cipher contexts for packet decryption and encryption.
To do so we attach a context to the quic_tls_kp struct and initialize it each time
the underlying secret key is updated. Same thing when we rotate the secrets keys,
we rotate the contexts as the same time.
2022-04-08 15:38:29 +02:00
Frédéric Lécaille
3dfd4c4b0d MINOR: quic: Add short packet key phase bit values to traces
This is useful to diagnose key update related issues.
2022-04-08 15:38:29 +02:00
Frédéric Lécaille
9688a8df49 CLEANUP: quic: Do not set any cipher/group from ssl_quic_initial_ctx()
These settings are potentially cancelled by others setting initialization shared
with SSL sock bindings. This will have to be clarified when we will adapt the
QUIC bindings configuration.
2022-04-08 15:38:29 +02:00
Frédéric Lécaille
f2f4a4eee5 MINOR: quic_tls: Stop hardcoding cipher IV lengths
For QUIC AEAD usage, the number of bytes for the IVs is always 12.
2022-04-08 15:38:29 +02:00
Frédéric Lécaille
f4605748f4 MINOR: quic_tls: Add reusable cipher contexts to QUIC TLS contexts
Add ->ctx new member field to quic_tls_secrets struct to store the cipher context
for each QUIC TLS context TX/RX parts.
Add quic_tls_rx_ctx_init() and quic_tls_tx_ctx_init() functions to initialize
these cipher context for RX and TX parts respectively.
Make qc_new_isecs() call these two functions to initialize the cipher contexts
of the Initial secrets. Same thing for ha_quic_set_encryption_secrets() to
initialize the cipher contexts of the subsequent derived secrets (ORTT, Handshake,
1RTT).
Modify quic_tls_decrypt() and quic_tls_encrypt() to always use the same cipher
context without allocating it each time they are called.
2022-04-08 15:38:29 +02:00
Frédéric Lécaille
82851bd3cb BUG/MEDIUM: quic: Possible crash from quic_free_arngs()
All quic_arng_node objects are allocated from "pool_head_quic_arng" memory pool.
They must be deallocated calling pool_free().
2022-04-08 15:38:29 +02:00
Amaury Denoyelle
b515b0af1d MEDIUM: quic: report closing state for the MUX
Define a new API to notify the MUX from the quic-conn when the
connection is about to be closed. This happens in the following cases :
- on idle timeout
- on CONNECTION_CLOSE emission or reception

The MUX wake callback is called on these conditions. The quic-conn
QUIC_FL_NOTIFY_CLOSE is set to only report once. On the MUX side,
connection flags CO_FL_SOCK_RD_SH|CO_FL_SOCK_WR_SH are set to interrupt
future emission/reception.

This patch is the counterpart to
  "MEDIUM: mux-quic: report CO_FL_ERROR on send".
Now the quic-conn is able to report its closing, which may be translated
by the MUX into a CO_FL_ERROR on the connection for the upper layer.
This allows the MUX to properly react to the QUIC closing mechanism for
both idle-timeout and closing/draining states.
2022-04-07 10:37:45 +02:00
Amaury Denoyelle
c9acc31018 BUG/MINOR: fix memleak on quic-conn streams cleaning
When freeing a quic-conn, the streams resources attached to it must be
cleared. This code is already implemented but the streams buffer was not
deallocated.

Fix this by using the function qc_stream_desc_free. This existing
function centralize all operations to properly free all streams
elements, attached both to the MUX and the quic-conn.

This fixes a memory leak which can happen for each released connection.
2022-04-07 10:10:23 +02:00
Amaury Denoyelle
6057b4090e CLEANUP: mux-quic: remove unused QC_CF_CC_RECV
This flag was used to notify the MUX about a CONNECTION_CLOSE frame
reception. It is now unused on the MUX side and can be removed. A new
mechanism to detect quic-conn closing will be soon implemented.
2022-04-07 10:10:23 +02:00
Amaury Denoyelle
e0be573c1b CLEANUP: quic: use static qualifer on quic_close
quic_close can be used through xprt-ops and can thus be kept as a static
symbol.
2022-04-07 10:10:22 +02:00
Amaury Denoyelle
db71e3bd09 BUG/MEDIUM: quic: ensure quic-conn survives to the MUX
Rationalize the lifetime of the quic-conn regarding with the MUX. The
quic-conn must not be freed if the MUX is still allocated.

This simplify the MUX code when accessing the quic-conn and removed
possible segfaults.

To implement this, if the quic-conn timer expired, the quic-conn is
released only if the MUX is not allocated. Else, the quic-conn is
flagged with QUIC_FL_CONN_EXP_TIMER. The MUX is then responsible
to call quic_close() which will free the flagged quic-conn.
2022-04-07 10:10:22 +02:00
Frédéric Lécaille
59bf255806 MINOR: quic: Add closing connection state
New received packets after sending CONNECTION_CLOSE frame trigger a new
CONNECTION_CLOSE frame to be sent. Each time such a frame is sent we
increase the number of packet required to send another CONNECTION_CLOSE
frame.
Rearm only one time the idle timer when sending a CONNECTION_CLOSE frame.
2022-04-06 15:52:35 +02:00
Frédéric Lécaille
47756809fb MINOR: quic: Add draining connection state.
As soon as we receive a CONNECTION_CLOSE frame, we must stop sending packets.
We add QUIC_FL_CONN_DRAINING connection flag to do so.
2022-04-06 15:52:35 +02:00
Frédéric Lécaille
eb2a2da67c BUG/MINOR: quic: Missing TX packet deallocations
Ensure all TX packets are deallocated. There may be remaining ones which
will never be acknowledged or deemed lost.
2022-04-01 16:26:06 +02:00
Frédéric Lécaille
64670884ba BUG/MINOR: quic: Missing ACK range deallocations
free_quic_arngs() was implemented but not used. Let's call it from
quic_conn_release().
2022-04-01 16:26:06 +02:00
Frédéric Lécaille
96fd1633e1 BUG/MINOR: quic: QUIC TLS secrets memory leak
We deallocate these secrets from quic_conn_release().
2022-04-01 16:26:06 +02:00
Frédéric Lécaille
b823bb7f7f MINOR: quic: Add traces about list of frames
This should be useful to have an idea of the list of frames which could be built
towards the list of available frames when building packets.
Same thing about the frames which could not be built because of a lack of room
in the TX buffer.
2022-04-01 16:26:06 +02:00
Frédéric Lécaille
6c01b74ffa MINOR: quic: Useless call to SSL_CTX_set_default_verify_paths()
This call to SSL_CTX_set_default_verify_paths() is useless for haproxy.
2022-04-01 16:26:06 +02:00
Frédéric Lécaille
12fd259363 BUG/MINOR: quic: Too much prepared retransmissions due to anti-amplification
We must not re-enqueue frames if we can detect in advance they will not be
transmitted due to the anti-amplification limit.
2022-04-01 16:26:06 +02:00
Frédéric Lécaille
009016c0cd BUG/MINOR: quic: Non duplicated frames upon fast retransmission
We must duplicate the frames to be sent again from packets which are not deemed
lost.
2022-04-01 16:26:06 +02:00
Frédéric Lécaille
5cfb4edca7 BUG/MINOR: quic: Do not probe from an already probing packet number space
During a handshake, after having prepared a probe upon a PTO expiration from
process_timer(), we wake up the I/O handler to make it send probing packets.
This handler first treat incoming packets  which trigger a fast retransmission
leading to send too much probing (duplicated) packets. In this cas we cancel
the fast retranmission.
2022-04-01 16:26:05 +02:00
Frédéric Lécaille
03235d78ae MINOR: quic: Do not display any timer value from process_timer()
This is confusing to display the connection timer from this function as it is not
supposed to update it. Only qc_set_timer() should do that.
2022-04-01 16:22:52 +02:00
Frédéric Lécaille
05bd92bbc5 BUG/MINOR: quic: Discard Initial packet number space only one time
When discarding a packet number space, we at least reset the PTO backoff counter.
Doing this several times have an impact on the PTO duration calculation.
We must not discard a packet number space several times (this is already the case
for the handshake packet number space).
2022-04-01 16:22:52 +02:00
Frédéric Lécaille
d6570e1789 BUG/MINOR: quic: Missing probing packets when coalescing
Before having a look at the next encryption level to build packets if there is
no more ack-eliciting frames to send we must check we have not to probe from
the current encryption level anymore. If not, we only send one datagram instead
of sending two datagrams giving less chance to recover from packet loss.
2022-04-01 16:22:52 +02:00
Frédéric Lécaille
b002145e9f MEDIUM: quic: Send ACK frames asap
Due to a erroneous interpretation of the RFC 9000 (quic-transport), ACKs frames
were always sent only after having received two ack-eliciting packets.
This could trigger useless retransmissions for tail packets on the peer side.
For now on, we send as soon as possible ACK frames as soon as we have ACK to send,
in the same packets as the ack-eliciting frame packets, and we also send ACK
frames after having received 2 ack-eliciting packets since the last time we sent
an ACK frame with other ack-eliciting frames.
2022-04-01 16:22:52 +02:00
Frédéric Lécaille
205e4f359e CLEANUP: quic: Remove all atomic operations on packet number spaces
As such variables are handled by the QUIC connection I/O handler which runs
always on the thread, there is no need to continue to use such atomic operations
2022-04-01 16:22:47 +02:00
Frédéric Lécaille
fc79006c92 CLEANUP: quic: Remove all atomic operations on quic_conn struct
As the QUIC connections are always handled by the same thread there is no need
anymore to continue to use atomic operations on such variables.
2022-04-01 16:22:44 +02:00
Frédéric Lécaille
f44d19eb91 BUG/MEDIUM: quic: Possible crash in ha_quic_set_encryption_secrets()
This bug has come with this commit:
   1fc5e16c4 MINOR: quic: More accurate immediately close
As mentionned in this commit we do not want to derive anymore secret when in closing
state. But the flag which denote secrets were derived was set. Add a label at
the correct flag to skip the secrets derivation without setting this flag.
2022-04-01 16:22:40 +02:00
Amaury Denoyelle
d8e680cbaf MEDIUM: mux-quic: remove qcs tree node
The new qc_stream_desc type has a tree node for storage. Thus, we can
remove the node in the qcs structure.

When initializing a new stream, it is stored into the qcc streams_by_id
tree. When the MUX releases it, it will freed as soon as its buffer is
emptied. Before this, the quic-conn is responsible to store it inside
its own streams_by_id tree.
2022-03-30 16:26:59 +02:00
Amaury Denoyelle
7272cd76fc MEDIUM: quic: move transport fields from qcs to qc_conn_stream
Move the xprt-buf and ack related fields from qcs to the qc_stream_desc
structure. In exchange, qcs has a pointer to the low-level stream. For
each new qcs, a qc_stream_desc is automatically allocated.

This simplify the transport layer by removing qcs/mux manipulation
during ACK frame parsing. An additional check is done to not notify the
MUX on sending if the stream is already released : this case may now
happen on retransmission.

To complete this change, the quic_stream frame now references the
quic_stream instance instead of a qcs.
2022-03-30 16:19:48 +02:00
Amaury Denoyelle
5c3859c509 MINOR: quic: implement stream descriptor for transport layer
Currently, the mux qcs streams manage the Tx buffering, even after
sending it to the transport layer. Buffers are emptied when
acknowledgement are treated by the transport layer. This complicates the
MUX liberation and we may loose some data after the MUX free.

Change this paradigm by moving the buffering on the transport layer. For
this goal, a new type is implemented as low-level stream at the
transport layer, as a counterpart of qcs mux instances. This structure
is called qc_stream_desc. This will allow to free the qcs/qcc instances
without having to wait for acknowledge reception.

For the moment, the quic-conn is responsible to store the qc_stream_desc
in a new tree named streams_by_id. This will sligthly change in the next
commits to remove the qcs node which has a similar purpose :
qc_stream_desc instances will be shared between the qcc MUX and the
quic-conn.

This patch only introduces the new type definition and the function to
manipulate it. The following commit will bring the rearchitecture in the
qcs structure.
2022-03-30 16:16:07 +02:00
Amaury Denoyelle
95e50fbeff CLEANUP: quic: complete comment on qcs_try_to_consume
Specify the return value usage.
2022-03-30 16:12:18 +02:00
Amaury Denoyelle
50742294f5 MINOR: mux-quic: return qcs instance from qcc_get_qcs
Refactoring on qcc_get_qcs : return the qcs instance instead of the tree
node. This is useful to hide some eb64_entry macros for better
readability.
2022-03-30 16:12:18 +02:00
Amaury Denoyelle
8d5def0bab BUG/MEDIUM: quic: do not use qcs from quic_stream on ACK parsing
The quic_stream frame stores the qcs instance. On ACK parsing, qcs is
accessed to clear the stream buffer. This can cause a segfault if the
MUX or the qcs is already released.

Consider the following scenario :

1. a STREAM frame is generated by the MUX
   transport layer emits the frame with PKN=1
   upper layer has finished the transfer so related qcs is detached

2. transport layer reemits the frame with PKN=2 because ACK was not
   received

3. ACK for PKN=1 is received, stream buffer is cleared
   at this stage, qcs may be freed by the MUX as it is detached

4. ACK for PKN=2 is received
   qcs for STREAM frame is dereferenced which will lead to a crash

To prevent this, qcs is never accessed from the quic_stream during ACK
parsing. Instead, a lookup is done on the MUX streams tree. If the MUX
is already released, no lookup is done. These checks prevents a possible
segfault.

This change may have an impact on the perf as now we are forced to use a
tree lookup operation. If this is the case, an alternative solution may
be to implement a refcount on qcs instances.
2022-03-30 16:12:18 +02:00
Frédéric Lécaille
cc2764e7fe BUG/MINOR: quic: Wrong buffer length passed to generate_retry_token()
After having consumed <i> bytes from <buf>, the remaining available room to be
passed to generate_retry_token() is sizeof(buf) - i.
This bug could be easily reproduced with quic-qo as client which chooses a random
value as ODCID length.
2022-03-23 17:16:20 +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
Frédéric Lécaille
aaf1f19e8b MINOR: quic: Add traces in qc_set_timer() (scheduling)
This should be helpful to diagnose some issues: timer task not
run when it should run.
2022-03-23 09:01:45 +01:00
Frédéric Lécaille
ce69cbc520 MINOR: quic: Add traces about stream TX buffer consumption
This will be helpful to diagnose STREAM blocking states.
2022-03-23 09:01:45 +01:00
Frédéric Lécaille
411aa6daf5 BUG/MINOR: quic: Non initialized variable in quic_build_post_handshake_frames()
<cid> could be accessed before being initialized.
2022-03-21 14:30:23 +01:00
Frédéric Lécaille
44ae75220a BUG/MINOR: quic: Incorrect peer address validation
We must consider the peer address as validated as soon as we received an
handshake packet. An ACK frame in handshake packet was too restrictive.
Rename the concerned flag to reflect this situation.
2022-03-21 14:27:09 +01:00
Frédéric Lécaille
12aa26b6fd BUG/MINOR: quic: 1RTT packets ignored after mux was released
We must be able to handle 1RTT packets after the mux has terminated its job
(qc->mux_state == QC_MUX_RELEASED). So the condition (qc->mux_state != QC_MUX_READY)
in qc_qel_may_rm_hp() is not correct when we want to wait for the mux to be started.
Add a check in qc_parse_pkt_frms() to ensure is started before calling it. All
the STREAM frames will be ignored when the mux will be released.
2022-03-21 14:27:09 +01:00
Frédéric Lécaille
2899fe2460 BUG/MINOR: quic: Missing TX packet initializations
The most important one is the ->flags member which leads to an erratic xprt behavior.
For instance a non ack-eliciting packet could be seen as ack-eliciting leading the
xprt to try to retransmit a packet which are not ack-eliciting. In this case, the
xprt does nothing and remains indefinitively in a blocking state.
2022-03-21 14:27:09 +01:00
Frédéric Lécaille
dcc74ff792 BUG/MINOR: quic: Unsent frame because of qc_build_frms()
There are non already identified rare cases where qc_build_frms() does not manage
to size frames to be encoded in a packet leading qc_build_frm() to fail to add
such frame to the packet to be built. In such cases we must move back such
frames to their origin frame list passed as parameter to qc_build_frms(): <frms>.
because they were added to the packet frame list (but not built). If this
this packet is not retransmitted, the frame is lost for ever! Furthermore we must
not modify the buffer.
2022-03-21 11:29:40 +01:00
Frédéric Lécaille
d64f68fb0a BUG/MINOR: quic: Possible leak in quic_build_post_handshake_frames()
Rework this function to leave the connection passed as parameter in the same state
it was before entering this function.
2022-03-21 11:29:40 +01:00
Frédéric Lécaille
f1f812bfdb BUG/MINOR: quic: Possible crash in parse_retry_token()
We must check the decoded length of this incoming data before copying into our
internal structure. This could lead to crashes.
Reproduced with such a packet captured from QUIC interop.
    {
	    0xc5, 0x00, 0x00, 0x00, 0x01, 0x12, 0xf2, 0x65,
		0x4d, 0x9d, 0x58, 0x90, 0x23, 0x7e, 0x67, 0xef,
		0xf8, 0xef, 0x5b, 0x87, 0x48, 0xbe, 0xde, 0x7a, /* corrupted byte: 0x11, */
		0x01, 0xdc, 0x41, 0xbf, 0xfb, 0x07, 0x39, 0x9f,
		0xfd, 0x96, 0x67, 0x5f, 0x58, 0x03, 0x57, 0x74,
		0xc7, 0x26, 0x00, 0x45, 0x25, 0xdc, 0x7f, 0xf1,
		0x22, 0x1d,
	}
2022-03-21 11:29:40 +01:00
Frédéric Lécaille
e2a1c1b372 MEDIUM: quic: Rework of the TX packets memory handling
The TX packet refcounting had come with the multithreading support but not only.
It is very useful to ease the management of the memory allocated for TX packets
with TX frames attached to. At some locations of the code we have to move TX
frames from a packet to a new one during retranmission when the packet has been
deemed as lost or not. When deemed lost the memory allocated for the paquet must
be released contrary to when its frames are retransmitted when probing (PTO).

For now on, thanks to this patch we handle the TX packets memory this way. We
increment the packet refcount when:
  - we insert it in its packet number space tree,
  - we attache an ack-eliciting frame to it.
And reciprocally we decrement this refcount when:
  - we remove an ack-eliciting frame from the packet,
  - we delete the packet from its packet number space tree.

Note that an optimization WOULD NOT be to fully reuse (without releasing its
memorya TX packet to retransmit its contents (its ack-eliciting frames). Its
information (timestamp, in flight length) to be processed by packet loss detection
and the congestion control.
2022-03-21 11:29:40 +01:00
Frédéric Lécaille
141982a4e1 MEDIUM: quic: Limit the number of ACK ranges
When building a packet with an ACK frame, we store the largest acknowledged
packet number sent in this frame in the packet (quic_tx_packet struc).
When receiving an ack for such a packet we can purge the tree of acknowledged
packet number ranges from the range sent before this largest acknowledged
packet number.
2022-03-21 11:29:40 +01:00
Frédéric Lécaille
8f3ae0272f CLEANUP: quic: "largest_acked_pn" pktns struc member moving
This struct member stores the largest acked packet number which was received. It
is used to build (TX) packet. But this is confusing to store it in the tx packet
of the packet number space structure even if it is used to build and transmit
packets.
2022-03-21 11:29:40 +01:00
Frédéric Lécaille
302c2b1120 MINOR: quic: Code factorization (TX buffer reuse)
Add qc_may_reuse_cbuf() function used by qc_prep_pkts() and qc_prep_app_pkts().
Simplification of the factorized section code: there is no need to check there
is enough room to mark the end of the data in the TX buf. This is done by
the callers (qc_prep_pkts() and qc_prep_app_pkts()). Add a diagram to explain
the conditions which must be verified to be able to reuse a cbuf struct.

This should improve the QUIC stack implementation maintenability.
2022-03-21 11:29:40 +01:00
Frdric Lcaille
e9a974a37a BUG/MAJOR: quic: Possible crash with full congestion control window
This commit reverts this one:
  "d5066dd9d BUG/MEDIUM: quic: qc_prep_app_pkts() retries on qc_build_pkt() failures"

After having filled the congestion control window, qc_build_pkt() always fails.
Then depending on the relative position of the writer and  reader indexes for the
TX buffer, this could lead this function to try to reuse the buffer even if not full.
In such case, we do not always mark the end of the data in this TX buffer. This
is something the reader cannot understand: it reads a false datagram length,
then a wrong packet address from the TX buffer, leading to an invalid pointer
dereferencing.
2022-03-15 10:38:48 +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
Frédéric Lécaille
728b30d750 CLEANUP: quic: Comments fix for qc_prep_(app)pkts() functions
Fix the comments for these two functions about their returned values.
2022-03-11 11:37:31 +01:00