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.
#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;
};
struct qf_crypto {
+ struct list list;
uint64_t offset;
uint64_t len;
const struct quic_enc_level *qel;
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)
{
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 */
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);
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;
#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)
{
}
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;
}
#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>
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.
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);
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);
qel->rx.pkts = EB_ROOT;
LIST_INIT(&qel->rx.pqpkts);
+ LIST_INIT(&qel->rx.crypto_frms);
/* Allocate only one buffer. */
/* TODO: use a pool */