MEDIUM: quic: Heavy task mode during handshake

Add a new pool for the CRYPTO data frames received in order.
Add ->rx.crypto_frms list to each encryption level to store such frames
when they are received in order from qc_handle_crypto_frm().
Also set the handshake task (qc_conn_io_cb()) in heavy task mode from
this function after having received such frames. When this task
detects that it is set in heavy mode, it calls qc_ssl_provide_all_quic_data()
newly implemented function to provide the CRYPTO data to the TLS task.
Modify quic_conn_enc_level_uninit() to release these CRYPTO frames
when releasing the encryption level they are in relation with.
This commit is contained in:
Frédéric Lécaille 2023-10-31 15:04:28 +01:00
parent 84d26bcf3f
commit 94d20be138
8 changed files with 92 additions and 3 deletions

View File

@ -35,6 +35,7 @@
#include <haproxy/quic_stream-t.h>
extern struct pool_head *pool_head_quic_frame;
extern struct pool_head *pool_head_qf_crypto;
/* forward declarations from xprt-quic */
struct quic_arngs;
@ -144,6 +145,7 @@ struct qf_stop_sending {
};
struct qf_crypto {
struct list list;
uint64_t offset;
uint64_t len;
const struct quic_enc_level *qel;

View File

@ -34,6 +34,7 @@ int qc_ssl_provide_quic_data(struct ncbuf *ncbuf,
enum ssl_encryption_level_t level,
struct ssl_sock_ctx *ctx,
const unsigned char *data, size_t len);
int qc_ssl_provide_all_quic_data(struct quic_conn *qc, struct ssl_sock_ctx *ctx);
static inline void qc_free_ssl_sock_ctx(struct ssl_sock_ctx **ctx)
{

View File

@ -222,6 +222,8 @@ struct quic_enc_level {
struct eb_root pkts;
/* List of QUIC packets with protected header. */
struct list pqpkts;
/* List of crypto frames received in order. */
struct list crypto_frms;
} rx;
/* TX part */

View File

@ -898,12 +898,18 @@ struct task *quic_conn_io_cb(struct task *t, void *context, unsigned int state)
struct quic_conn *qc = context;
struct buffer *buf = NULL;
int st;
struct tasklet *tl = (struct tasklet *)t;
TRACE_ENTER(QUIC_EV_CONN_IO_CB, qc);
st = qc->state;
TRACE_PROTO("connection state", QUIC_EV_CONN_IO_CB, qc, &st);
if (HA_ATOMIC_LOAD(&tl->state) & TASK_HEAVY) {
HA_ATOMIC_AND(&tl->state, ~TASK_HEAVY);
qc_ssl_provide_all_quic_data(qc, qc->xprt_ctx);
}
/* Retranmissions */
if (qc->flags & QUIC_FL_CONN_RETRANS_NEEDED) {
TRACE_DEVEL("retransmission needed", QUIC_EV_CONN_PHPKTS, qc);
@ -918,6 +924,11 @@ struct task *quic_conn_io_cb(struct task *t, void *context, unsigned int state)
if (!qc_treat_rx_pkts(qc))
goto out;
if (HA_ATOMIC_LOAD(&tl->state) & TASK_HEAVY) {
tasklet_wakeup(tl);
goto out;
}
if (qc->flags & QUIC_FL_CONN_TO_KILL) {
TRACE_DEVEL("connection to be killed", QUIC_EV_CONN_PHPKTS, qc);
goto out;

View File

@ -23,6 +23,7 @@
#include <haproxy/trace.h>
DECLARE_POOL(pool_head_quic_frame, "quic_frame", sizeof(struct quic_frame));
DECLARE_POOL(pool_head_qf_crypto, "qf_crypto", sizeof(struct qf_crypto));
const char *quic_frame_type_string(enum quic_frame_type ft)
{

View File

@ -829,13 +829,22 @@ static int qc_handle_crypto_frm(struct quic_conn *qc,
}
if (crypto_frm->offset == cstream->rx.offset && ncb_is_empty(ncbuf)) {
if (!qc_ssl_provide_quic_data(&qel->cstream->rx.ncbuf, qel->level,
qc->xprt_ctx, crypto_frm->data, crypto_frm->len)) {
// trace already emitted by function above
struct qf_crypto *qf_crypto;
qf_crypto = pool_alloc(pool_head_qf_crypto);
if (!qf_crypto) {
TRACE_ERROR("CRYPTO frame allocation failed", QUIC_EV_CONN_PRSHPKT, qc);
goto leave;
}
qf_crypto->offset = crypto_frm->offset;
qf_crypto->len = crypto_frm->len;
qf_crypto->data = crypto_frm->data;
qf_crypto->qel = qel;
LIST_APPEND(&qel->rx.crypto_frms, &qf_crypto->list);
cstream->rx.offset += crypto_frm->len;
HA_ATOMIC_OR(&qc->wait_event.tasklet->state, TASK_HEAVY);
TRACE_DEVEL("increment crypto level offset", QUIC_EV_CONN_PHPKTS, qc, qel);
goto done;
}

View File

@ -2,6 +2,7 @@
#include <haproxy/ncbuf.h>
#include <haproxy/proxy.h>
#include <haproxy/quic_conn.h>
#include <haproxy/quic_rx.h>
#include <haproxy/quic_sock.h>
#include <haproxy/quic_ssl.h>
#include <haproxy/quic_tls.h>
@ -633,6 +634,60 @@ int qc_ssl_provide_quic_data(struct ncbuf *ncbuf,
return ret;
}
/* Provide all the stored in order CRYPTO data received from the peer to the TLS.
* Return 1 if succeeded, 0 if not.
*/
int qc_ssl_provide_all_quic_data(struct quic_conn *qc, struct ssl_sock_ctx *ctx)
{
int ret = 0;
struct quic_enc_level *qel;
TRACE_ENTER(QUIC_EV_CONN_PHPKTS, qc);
list_for_each_entry(qel, &qc->qel_list, list) {
int ssl_ret;
struct quic_cstream *cstream = qel->cstream;
struct ncbuf *ncbuf;
struct qf_crypto *qf_crypto, *qf_back;
if (!qel->cstream) {
TRACE_DEVEL("no cstream", QUIC_EV_CONN_PHPKTS, qc, qel);
continue;
}
ssl_ret = 1;
ncbuf = &cstream->rx.ncbuf;
list_for_each_entry_safe(qf_crypto, qf_back, &qel->rx.crypto_frms, list) {
ssl_ret = qc_ssl_provide_quic_data(ncbuf, qel->level, ctx,
qf_crypto->data, qf_crypto->len);
/* Free this frame asap */
LIST_DELETE(&qf_crypto->list);
pool_free(pool_head_qf_crypto, qf_crypto);
if (!ssl_ret) {
TRACE_DEVEL("null ssl_ret", QUIC_EV_CONN_PHPKTS, qc, qel);
break;
}
TRACE_DEVEL("buffered crypto data were provided to TLS stack",
QUIC_EV_CONN_PHPKTS, qc, qel);
}
if (ncb_is_empty(ncbuf)) {
TRACE_DEVEL("freeing crypto buf", QUIC_EV_CONN_PHPKTS, qc, qel);
quic_free_ncbuf(ncbuf);
}
if (!ssl_ret)
goto leave;
}
ret = 1;
leave:
TRACE_LEAVE(QUIC_EV_CONN_PHPKTS, qc);
return ret;
}
/* Try to allocate the <*ssl> SSL session object for <qc> QUIC connection
* with <ssl_ctx> as SSL context inherited settings. Also set the transport
* parameters of this session.

View File

@ -114,6 +114,7 @@ void quic_tls_secret_hexdump(struct buffer *buf,
void quic_conn_enc_level_uninit(struct quic_conn *qc, struct quic_enc_level *qel)
{
int i;
struct qf_crypto *qf_crypto, *qfback;
TRACE_ENTER(QUIC_EV_CONN_CLOSE, qc);
@ -123,6 +124,12 @@ void quic_conn_enc_level_uninit(struct quic_conn *qc, struct quic_enc_level *qel
qel->tx.crypto.bufs[i] = NULL;
}
}
list_for_each_entry_safe(qf_crypto, qfback, &qel->rx.crypto_frms, list) {
LIST_DELETE(&qf_crypto->list);
pool_free(pool_head_qf_crypto, qf_crypto);
}
ha_free(&qel->tx.crypto.bufs);
quic_cstream_free(qel->cstream);
@ -160,6 +167,7 @@ static int quic_conn_enc_level_init(struct quic_conn *qc,
qel->rx.pkts = EB_ROOT;
LIST_INIT(&qel->rx.pqpkts);
LIST_INIT(&qel->rx.crypto_frms);
/* Allocate only one buffer. */
/* TODO: use a pool */