diff --git a/include/haproxy/quic_conn-t.h b/include/haproxy/quic_conn-t.h index 0e3e2734a..24e319d73 100644 --- a/include/haproxy/quic_conn-t.h +++ b/include/haproxy/quic_conn-t.h @@ -510,6 +510,21 @@ struct q_buf { struct list pkts; }; +/* Crypto data stream (one by encryption level) */ +struct quic_cstream { + struct { + uint64_t offset; /* absolute current base offset of ncbuf */ + struct ncbuf ncbuf; /* receive buffer - can handle out-of-order offset frames */ + } rx; + struct { + uint64_t offset; /* last offset of data ready to be sent */ + uint64_t sent_offset; /* last offset sent by transport layer */ + struct buffer buf; /* transmit buffer before sending via xprt */ + } tx; + + struct qc_stream_desc *desc; +}; + struct quic_enc_level { enum ssl_encryption_level_t level; struct quic_tls_ctx tls_ctx; @@ -536,6 +551,8 @@ struct quic_enc_level { uint64_t offset; } crypto; } tx; + /* Crypto data stream */ + struct quic_cstream *cstream; struct quic_pktns *pktns; }; diff --git a/src/quic_conn.c b/src/quic_conn.c index 65c584df5..6fc1e30b3 100644 --- a/src/quic_conn.c +++ b/src/quic_conn.c @@ -210,6 +210,7 @@ DECLARE_POOL(pool_head_quic_rx_packet, "quic_rx_packet", sizeof(struct quic_rx_p DECLARE_POOL(pool_head_quic_tx_packet, "quic_tx_packet", sizeof(struct quic_tx_packet)); DECLARE_STATIC_POOL(pool_head_quic_rx_crypto_frm, "quic_rx_crypto_frm", sizeof(struct quic_rx_crypto_frm)); DECLARE_STATIC_POOL(pool_head_quic_crypto_buf, "quic_crypto_buf", sizeof(struct quic_crypto_buf)); +DECLARE_STATIC_POOL(pool_head_quic_cstream, "quic_cstream", sizeof(struct quic_cstream)); DECLARE_POOL(pool_head_quic_frame, "quic_frame", sizeof(struct quic_frame)); DECLARE_STATIC_POOL(pool_head_quic_arng, "quic_arng", sizeof(struct quic_arng_node)); @@ -4396,6 +4397,55 @@ struct task *quic_conn_io_cb(struct task *t, void *context, unsigned int state) return t; } +/* Release the memory allocated for CRYPTO stream */ +void quic_cstream_free(struct quic_cstream *cs) +{ + if (!cs) { + /* This is the case for ORTT encryption level */ + return; + } + + qc_stream_desc_release(cs->desc); + pool_free(pool_head_quic_cstream, cs); +} + +/* Allocate a new QUIC stream for . + * Return it if succeeded, NULL if not. + */ +struct quic_cstream *quic_cstream_new(struct quic_conn *qc) +{ + struct quic_cstream *cs, *ret_cs = NULL; + + TRACE_ENTER(QUIC_EV_CONN_LPKT, qc); + cs = pool_alloc(pool_head_quic_cstream); + if (!cs) { + TRACE_ERROR("crypto stream allocation failed", QUIC_EV_CONN_INIT, qc); + goto leave; + } + + cs->rx.offset = 0; + cs->rx.ncbuf = NCBUF_NULL; + cs->rx.offset = 0; + + cs->tx.offset = 0; + cs->tx.sent_offset = 0; + cs->tx.buf = BUF_NULL; + cs->desc = qc_stream_desc_new((uint64_t)-1, -1, cs, qc); + if (!cs->desc) { + TRACE_ERROR("crypto stream allocation failed", QUIC_EV_CONN_INIT, qc); + goto err; + } + + ret_cs = cs; + leave: + TRACE_LEAVE(QUIC_EV_CONN_LPKT, qc); + return ret_cs; + + err: + pool_free(pool_head_quic_cstream, cs); + goto leave; +} + /* Uninitialize QUIC encryption level. Never fails. */ static void quic_conn_enc_level_uninit(struct quic_conn *qc, struct quic_enc_level *qel) { @@ -4410,6 +4460,7 @@ static void quic_conn_enc_level_uninit(struct quic_conn *qc, struct quic_enc_lev } } ha_free(&qel->tx.crypto.bufs); + quic_cstream_free(qel->cstream); TRACE_LEAVE(QUIC_EV_CONN_CLOSE, qc); } @@ -4453,6 +4504,14 @@ static int quic_conn_enc_level_init(struct quic_conn *qc, qel->tx.crypto.sz = 0; qel->tx.crypto.offset = 0; + /* No CRYPTO data for early data TLS encryption level */ + if (level == QUIC_TLS_ENC_LEVEL_EARLY_DATA) + qel->cstream = NULL; + else { + qel->cstream = quic_cstream_new(qc); + if (!qel->cstream) + goto err; + } ret = 1; leave: diff --git a/src/quic_stream.c b/src/quic_stream.c index f88fbfcec..692f4d583 100644 --- a/src/quic_stream.c +++ b/src/quic_stream.c @@ -18,7 +18,8 @@ DECLARE_STATIC_POOL(pool_head_quic_stream_buf, "qc_stream_buf", /* Allocate a new stream descriptor with id . The caller is responsible to - * store the stream in the appropriate tree. + * store the stream in the appropriate tree. -1 special value must be used for + * a CRYPTO data stream, the type being ignored. * * Returns the newly allocated instance on success or else NULL. */ @@ -31,9 +32,14 @@ struct qc_stream_desc *qc_stream_desc_new(uint64_t id, enum qcs_type type, void if (!stream) return NULL; - stream->by_id.key = id; - eb64_insert(&qc->streams_by_id, &stream->by_id); - qc->rx.strms[type].nb_streams++; + if (id == (uint64_t)-1) { + stream->by_id.key = (uint64_t)-1; + } + else { + stream->by_id.key = id; + eb64_insert(&qc->streams_by_id, &stream->by_id); + qc->rx.strms[type].nb_streams++; + } stream->qc = qc; stream->buf = NULL; @@ -195,7 +201,8 @@ void qc_stream_desc_free(struct qc_stream_desc *stream, int closing) qc_release_frm(qc, frm); } - eb64_delete(&stream->by_id); + if (stream->by_id.key != (uint64_t)-1) + eb64_delete(&stream->by_id); pool_free(pool_head_quic_stream_desc, stream); }