From: Frédéric Lécaille Date: Tue, 31 Oct 2023 14:04:28 +0000 (+0100) Subject: MEDIUM: quic: Heavy task mode during handshake X-Git-Tag: v2.9-dev10~131 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=94d20be1388023bff36d795f501571adfefe8c75;p=thirdparty%2Fhaproxy.git 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. --- diff --git a/include/haproxy/quic_frame-t.h b/include/haproxy/quic_frame-t.h index b2aab70b09..5e91f93208 100644 --- a/include/haproxy/quic_frame-t.h +++ b/include/haproxy/quic_frame-t.h @@ -35,6 +35,7 @@ #include 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; diff --git a/include/haproxy/quic_ssl.h b/include/haproxy/quic_ssl.h index f31cafc3d8..f564770abe 100644 --- a/include/haproxy/quic_ssl.h +++ b/include/haproxy/quic_ssl.h @@ -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) { diff --git a/include/haproxy/quic_tls-t.h b/include/haproxy/quic_tls-t.h index ef3232aea1..a83efe2799 100644 --- a/include/haproxy/quic_tls-t.h +++ b/include/haproxy/quic_tls-t.h @@ -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 */ diff --git a/src/quic_conn.c b/src/quic_conn.c index f25bf4c9ff..7515e8d00e 100644 --- a/src/quic_conn.c +++ b/src/quic_conn.c @@ -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; diff --git a/src/quic_frame.c b/src/quic_frame.c index 0a52b74cb0..61d2c935ec 100644 --- a/src/quic_frame.c +++ b/src/quic_frame.c @@ -23,6 +23,7 @@ #include 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) { diff --git a/src/quic_rx.c b/src/quic_rx.c index c921c97513..69067bc471 100644 --- a/src/quic_rx.c +++ b/src/quic_rx.c @@ -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; } diff --git a/src/quic_ssl.c b/src/quic_ssl.c index 1a0d5d64df..fc3c26fe76 100644 --- a/src/quic_ssl.c +++ b/src/quic_ssl.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -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 QUIC connection * with as SSL context inherited settings. Also set the transport * parameters of this session. diff --git a/src/quic_tls.c b/src/quic_tls.c index f5ac9d9099..da25632e62 100644 --- a/src/quic_tls.c +++ b/src/quic_tls.c @@ -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 */