MINOR: quic: define new functions for frame alloc

Define two utility functions for quic_frame allocation :
* qc_frm_alloc() is used to allocate a new frame
* qc_frm_dup() is used to allocate a new frame by duplicating an
  existing one

Theses functions are useful to centralize quic_frame initialization.
Note that pool_zalloc() is replaced by a proper pool_alloc() + explicit
initialization code.

This commit will simplify implementation of the per frame retransmission
limitation. Indeed, a new counter will be added in quic_frame structure
which must be initialized to 0.

This should be backported up to 2.7.
This commit is contained in:
Amaury Denoyelle 2023-01-27 17:47:49 +01:00
parent 1dac018d9f
commit 40c24f1a10
6 changed files with 85 additions and 57 deletions

View File

@ -197,7 +197,7 @@ static inline void free_quic_conn_cids(struct quic_conn *conn)
} }
} }
/* Copy <src> new connection ID information to <to> NEW_CONNECTION_ID frame data. /* Copy <src> new connection ID information to <dst> NEW_CONNECTION_ID frame.
* Always succeeds. * Always succeeds.
*/ */
static inline void quic_connection_id_to_frm_cpy(struct quic_frame *dst, static inline void quic_connection_id_to_frm_cpy(struct quic_frame *dst,
@ -205,7 +205,6 @@ static inline void quic_connection_id_to_frm_cpy(struct quic_frame *dst,
{ {
struct quic_new_connection_id *to = &dst->new_connection_id; struct quic_new_connection_id *to = &dst->new_connection_id;
dst->type = QUIC_FT_NEW_CONNECTION_ID;
to->seq_num = src->seq_num.key; to->seq_num = src->seq_num.key;
to->retire_prior_to = src->retire_prior_to; to->retire_prior_to = src->retire_prior_to;
to->cid.len = src->cid.len; to->cid.len = src->cid.len;

View File

@ -239,9 +239,9 @@ struct quic_connection_close_app {
}; };
struct quic_frame { struct quic_frame {
struct list list; struct list list; /* List elem from parent elem (typically a Tx packet instance, a PKTNS or a MUX element). */
struct quic_tx_packet *pkt; struct quic_tx_packet *pkt; /* Last Tx packet used to send the frame. */
unsigned char type; unsigned char type; /* QUIC frame type. */
union { union {
struct quic_padding padding; struct quic_padding padding;
struct quic_ack ack; struct quic_ack ack;
@ -266,10 +266,10 @@ struct quic_frame {
struct quic_connection_close connection_close; struct quic_connection_close connection_close;
struct quic_connection_close_app connection_close_app; struct quic_connection_close_app connection_close_app;
}; };
struct quic_frame *origin; struct quic_frame *origin; /* Parent frame. Set if frame is a duplicate (used for retransmission). */
struct list reflist; struct list reflist; /* List head containing duplicated children frames. */
struct list ref; struct list ref; /* List elem from parent frame reflist. Set if frame is a duplicate (used for retransmission). */
unsigned int flags; unsigned int flags; /* QUIC_FL_TX_FRAME_* */
}; };

View File

@ -176,5 +176,58 @@ static inline struct quic_err quic_err_app(uint64_t code)
return (struct quic_err){ .code = code, .app = 1 }; return (struct quic_err){ .code = code, .app = 1 };
} }
/* Allocate a quic_frame with type <type>.
*
* Returns the allocated frame or NULL on failure.
*/
static inline struct quic_frame *qc_frm_alloc(int type)
{
struct quic_frame *frm = NULL;
frm = pool_alloc(pool_head_quic_frame);
if (!frm)
return NULL;
frm->type = type;
LIST_INIT(&frm->list);
LIST_INIT(&frm->reflist);
LIST_INIT(&frm->ref);
frm->pkt = NULL;
frm->origin = NULL;
frm->flags = 0;
return frm;
}
/* Allocate a quic_frame by duplicating <origin> frame. This will create a new
* frame of the same type with the same content. Internal fields such as packet
* owner and flags are however resetted for the newly allocated frame.
*
* Returns the allocated frame or NULL on failure.
*/
static inline struct quic_frame *qc_frm_dup(struct quic_frame *origin)
{
struct quic_frame *frm = NULL;
frm = pool_alloc(pool_head_quic_frame);
if (!frm)
return NULL;
*frm = *origin;
/* Reinit all internal members. */
LIST_INIT(&frm->list);
LIST_INIT(&frm->reflist);
frm->pkt = NULL;
frm->flags = 0;
/* Attach <frm> to <origin>. */
LIST_APPEND(&origin->reflist, &frm->ref);
frm->origin = origin;
return frm;
}
#endif /* USE_QUIC */ #endif /* USE_QUIC */
#endif /* _HAPROXY_QUIC_FRAME_H */ #endif /* _HAPROXY_QUIC_FRAME_H */

View File

@ -11,6 +11,7 @@
#include <haproxy/qmux_http.h> #include <haproxy/qmux_http.h>
#include <haproxy/qmux_trace.h> #include <haproxy/qmux_trace.h>
#include <haproxy/quic_conn.h> #include <haproxy/quic_conn.h>
#include <haproxy/quic_frame.h>
#include <haproxy/quic_stream.h> #include <haproxy/quic_stream.h>
#include <haproxy/quic_tp-t.h> #include <haproxy/quic_tp-t.h>
#include <haproxy/ssl_sock-t.h> #include <haproxy/ssl_sock-t.h>
@ -704,13 +705,11 @@ static void qcs_consume(struct qcs *qcs, uint64_t bytes)
if (qcs->rx.msd - qcs->rx.offset < qcs->rx.msd_init / 2) { if (qcs->rx.msd - qcs->rx.offset < qcs->rx.msd_init / 2) {
TRACE_DATA("increase stream credit via MAX_STREAM_DATA", QMUX_EV_QCS_RECV, qcc->conn, qcs); TRACE_DATA("increase stream credit via MAX_STREAM_DATA", QMUX_EV_QCS_RECV, qcc->conn, qcs);
frm = pool_zalloc(pool_head_quic_frame); frm = qc_frm_alloc(QUIC_FT_MAX_STREAM_DATA);
BUG_ON(!frm); /* TODO handle this properly */ BUG_ON(!frm); /* TODO handle this properly */
qcs->rx.msd = qcs->rx.offset + qcs->rx.msd_init; qcs->rx.msd = qcs->rx.offset + qcs->rx.msd_init;
LIST_INIT(&frm->reflist);
frm->type = QUIC_FT_MAX_STREAM_DATA;
frm->max_stream_data.id = qcs->id; frm->max_stream_data.id = qcs->id;
frm->max_stream_data.max_stream_data = qcs->rx.msd; frm->max_stream_data.max_stream_data = qcs->rx.msd;
@ -722,13 +721,11 @@ static void qcs_consume(struct qcs *qcs, uint64_t bytes)
qcc->lfctl.offsets_consume += bytes; qcc->lfctl.offsets_consume += bytes;
if (qcc->lfctl.md - qcc->lfctl.offsets_consume < qcc->lfctl.md_init / 2) { if (qcc->lfctl.md - qcc->lfctl.offsets_consume < qcc->lfctl.md_init / 2) {
TRACE_DATA("increase conn credit via MAX_DATA", QMUX_EV_QCS_RECV, qcc->conn, qcs); TRACE_DATA("increase conn credit via MAX_DATA", QMUX_EV_QCS_RECV, qcc->conn, qcs);
frm = pool_zalloc(pool_head_quic_frame); frm = qc_frm_alloc(QUIC_FT_MAX_DATA);
BUG_ON(!frm); /* TODO handle this properly */ BUG_ON(!frm); /* TODO handle this properly */
qcc->lfctl.md = qcc->lfctl.offsets_consume + qcc->lfctl.md_init; qcc->lfctl.md = qcc->lfctl.offsets_consume + qcc->lfctl.md_init;
LIST_INIT(&frm->reflist);
frm->type = QUIC_FT_MAX_DATA;
frm->max_data.max_data = qcc->lfctl.md; frm->max_data.max_data = qcc->lfctl.md;
LIST_APPEND(&qcs->qcc->lfctl.frms, &frm->list); LIST_APPEND(&qcs->qcc->lfctl.frms, &frm->list);
@ -1283,11 +1280,9 @@ static int qcc_release_remote_stream(struct qcc *qcc, uint64_t id)
++qcc->lfctl.cl_bidi_r; ++qcc->lfctl.cl_bidi_r;
if (qcc->lfctl.cl_bidi_r > qcc->lfctl.ms_bidi_init / 2) { if (qcc->lfctl.cl_bidi_r > qcc->lfctl.ms_bidi_init / 2) {
TRACE_DATA("increase max stream limit with MAX_STREAMS_BIDI", QMUX_EV_QCC_SEND, qcc->conn); TRACE_DATA("increase max stream limit with MAX_STREAMS_BIDI", QMUX_EV_QCC_SEND, qcc->conn);
frm = pool_zalloc(pool_head_quic_frame); frm = qc_frm_alloc(QUIC_FT_MAX_STREAMS_BIDI);
BUG_ON(!frm); /* TODO handle this properly */ BUG_ON(!frm); /* TODO handle this properly */
LIST_INIT(&frm->reflist);
frm->type = QUIC_FT_MAX_STREAMS_BIDI;
frm->max_streams_bidi.max_streams = qcc->lfctl.ms_bidi + frm->max_streams_bidi.max_streams = qcc->lfctl.ms_bidi +
qcc->lfctl.cl_bidi_r; qcc->lfctl.cl_bidi_r;
LIST_APPEND(&qcc->lfctl.frms, &frm->list); LIST_APPEND(&qcc->lfctl.frms, &frm->list);
@ -1424,14 +1419,12 @@ static int qcs_build_stream_frm(struct qcs *qcs, struct buffer *out, char fin,
BUG_ON(qcc->tx.sent_offsets + total > qcc->rfctl.md); BUG_ON(qcc->tx.sent_offsets + total > qcc->rfctl.md);
TRACE_PROTO("sending STREAM frame", QMUX_EV_QCS_SEND, qcc->conn, qcs); TRACE_PROTO("sending STREAM frame", QMUX_EV_QCS_SEND, qcc->conn, qcs);
frm = pool_zalloc(pool_head_quic_frame); frm = qc_frm_alloc(QUIC_FT_STREAM_8);
if (!frm) { if (!frm) {
TRACE_ERROR("frame alloc failure", QMUX_EV_QCS_SEND, qcc->conn, qcs); TRACE_ERROR("frame alloc failure", QMUX_EV_QCS_SEND, qcc->conn, qcs);
goto err; goto err;
} }
LIST_INIT(&frm->reflist);
frm->type = QUIC_FT_STREAM_8;
frm->stream.stream = qcs->stream; frm->stream.stream = qcs->stream;
frm->stream.id = qcs->id; frm->stream.id = qcs->id;
frm->stream.buf = out; frm->stream.buf = out;
@ -1599,14 +1592,12 @@ static int qcs_send_reset(struct qcs *qcs)
TRACE_ENTER(QMUX_EV_QCS_SEND, qcs->qcc->conn, qcs); TRACE_ENTER(QMUX_EV_QCS_SEND, qcs->qcc->conn, qcs);
frm = pool_zalloc(pool_head_quic_frame); frm = qc_frm_alloc(QUIC_FT_RESET_STREAM);
if (!frm) { if (!frm) {
TRACE_LEAVE(QMUX_EV_QCS_SEND, qcs->qcc->conn, qcs); TRACE_LEAVE(QMUX_EV_QCS_SEND, qcs->qcc->conn, qcs);
return 1; return 1;
} }
LIST_INIT(&frm->reflist);
frm->type = QUIC_FT_RESET_STREAM;
frm->reset_stream.id = qcs->id; frm->reset_stream.id = qcs->id;
frm->reset_stream.app_error_code = qcs->err; frm->reset_stream.app_error_code = qcs->err;
frm->reset_stream.final_size = qcs->tx.sent_offset; frm->reset_stream.final_size = qcs->tx.sent_offset;
@ -1657,14 +1648,12 @@ static int qcs_send_stop_sending(struct qcs *qcs)
goto done; goto done;
} }
frm = pool_zalloc(pool_head_quic_frame); frm = qc_frm_alloc(QUIC_FT_STOP_SENDING);
if (!frm) { if (!frm) {
TRACE_LEAVE(QMUX_EV_QCS_SEND, qcs->qcc->conn, qcs); TRACE_LEAVE(QMUX_EV_QCS_SEND, qcs->qcc->conn, qcs);
return 1; return 1;
} }
LIST_INIT(&frm->reflist);
frm->type = QUIC_FT_STOP_SENDING;
frm->stop_sending.id = qcs->id; frm->stop_sending.id = qcs->id;
frm->stop_sending.app_error_code = qcs->err; frm->stop_sending.app_error_code = qcs->err;

View File

@ -1122,14 +1122,12 @@ static int quic_crypto_data_cpy(struct quic_conn *qc, struct quic_enc_level *qel
found->crypto.len += cf_len; found->crypto.len += cf_len;
} }
else { else {
frm = pool_zalloc(pool_head_quic_frame); frm = qc_frm_alloc(QUIC_FT_CRYPTO);
if (!frm) { if (!frm) {
TRACE_ERROR("Could not allocate quic frame", QUIC_EV_CONN_ADDDATA, qc); TRACE_ERROR("Could not allocate quic frame", QUIC_EV_CONN_ADDDATA, qc);
goto leave; goto leave;
} }
LIST_INIT(&frm->reflist);
frm->type = QUIC_FT_CRYPTO;
frm->crypto.offset = cf_offset; frm->crypto.offset = cf_offset;
frm->crypto.len = cf_len; frm->crypto.len = cf_len;
frm->crypto.qel = qel; frm->crypto.qel = qel;
@ -2433,30 +2431,26 @@ static void qc_dup_pkt_frms(struct quic_conn *qc,
break; break;
} }
dup_frm = pool_alloc(pool_head_quic_frame); /* If <frm> is already a copy of another frame, we must take
* its original frame as source for the copy.
*/
origin = frm->origin ? frm->origin : frm;
dup_frm = qc_frm_dup(origin);
if (!dup_frm) { if (!dup_frm) {
TRACE_ERROR("could not duplicate frame", QUIC_EV_CONN_PRSAFRM, qc, frm); TRACE_ERROR("could not duplicate frame", QUIC_EV_CONN_PRSAFRM, qc, frm);
break; break;
} }
/* If <frm> is already a copy of another frame, we must take
* its original frame as source for the copy.
*/
origin = frm->origin ? frm->origin : frm;
TRACE_DEVEL("built probing frame", QUIC_EV_CONN_PRSAFRM, qc, origin); TRACE_DEVEL("built probing frame", QUIC_EV_CONN_PRSAFRM, qc, origin);
if (origin->pkt) if (origin->pkt) {
TRACE_DEVEL("duplicated from packet", QUIC_EV_CONN_PRSAFRM, TRACE_DEVEL("duplicated from packet", QUIC_EV_CONN_PRSAFRM,
qc, NULL, &origin->pkt->pn_node.key); qc, NULL, &origin->pkt->pn_node.key);
}
else { else {
/* <origin> is a frame which was sent from a packet detected as lost. */ /* <origin> is a frame which was sent from a packet detected as lost. */
TRACE_DEVEL("duplicated from lost packet", QUIC_EV_CONN_PRSAFRM, qc); TRACE_DEVEL("duplicated from lost packet", QUIC_EV_CONN_PRSAFRM, qc);
} }
*dup_frm = *origin;
dup_frm->pkt = NULL;
dup_frm->origin = origin;
dup_frm->flags = 0;
LIST_INIT(&dup_frm->reflist);
LIST_APPEND(&origin->reflist, &dup_frm->ref);
LIST_APPEND(&tmp, &dup_frm->list); LIST_APPEND(&tmp, &dup_frm->list);
} }
@ -2614,17 +2608,14 @@ static int qc_stop_sending_frm_enqueue(struct quic_conn *qc, uint64_t id)
* at this time. * at this time.
*/ */
app_error_code = H3_REQUEST_REJECTED; app_error_code = H3_REQUEST_REJECTED;
// fixme: zalloc frm = qc_frm_alloc(QUIC_FT_STOP_SENDING);
frm = pool_zalloc(pool_head_quic_frame);
if (!frm) { if (!frm) {
TRACE_ERROR("failed to allocate quic_frame", QUIC_EV_CONN_PRSHPKT, qc); TRACE_ERROR("failed to allocate quic_frame", QUIC_EV_CONN_PRSHPKT, qc);
goto out; goto out;
} }
frm->type = QUIC_FT_STOP_SENDING;
frm->stop_sending.id = id; frm->stop_sending.id = id;
frm->stop_sending.app_error_code = app_error_code; frm->stop_sending.app_error_code = app_error_code;
LIST_INIT(&frm->reflist);
LIST_APPEND(&qel->pktns->tx.frms, &frm->list); LIST_APPEND(&qel->pktns->tx.frms, &frm->list);
ret = 1; ret = 1;
out: out:
@ -3548,14 +3539,12 @@ static int quic_build_post_handshake_frames(struct quic_conn *qc)
qel = &qc->els[QUIC_TLS_ENC_LEVEL_APP]; qel = &qc->els[QUIC_TLS_ENC_LEVEL_APP];
/* Only servers must send a HANDSHAKE_DONE frame. */ /* Only servers must send a HANDSHAKE_DONE frame. */
if (qc_is_listener(qc)) { if (qc_is_listener(qc)) {
frm = pool_zalloc(pool_head_quic_frame); frm = qc_frm_alloc(QUIC_FT_HANDSHAKE_DONE);
if (!frm) { if (!frm) {
TRACE_ERROR("frame allocation error", QUIC_EV_CONN_IO_CB, qc); TRACE_ERROR("frame allocation error", QUIC_EV_CONN_IO_CB, qc);
goto leave; goto leave;
} }
LIST_INIT(&frm->reflist);
frm->type = QUIC_FT_HANDSHAKE_DONE;
LIST_APPEND(&frm_list, &frm->list); LIST_APPEND(&frm_list, &frm->list);
} }
@ -3569,13 +3558,12 @@ static int quic_build_post_handshake_frames(struct quic_conn *qc)
for (i = first; i < max; i++) { for (i = first; i < max; i++) {
struct quic_connection_id *cid; struct quic_connection_id *cid;
frm = pool_zalloc(pool_head_quic_frame); frm = qc_frm_alloc(QUIC_FT_NEW_CONNECTION_ID);
if (!frm) { if (!frm) {
TRACE_ERROR("frame allocation error", QUIC_EV_CONN_IO_CB, qc); TRACE_ERROR("frame allocation error", QUIC_EV_CONN_IO_CB, qc);
goto err; goto err;
} }
LIST_INIT(&frm->reflist);
cid = new_quic_cid(&qc->cids, qc, i); cid = new_quic_cid(&qc->cids, qc, i);
if (!cid) { if (!cid) {
pool_free(pool_head_quic_frame, frm); pool_free(pool_head_quic_frame, frm);
@ -6736,14 +6724,12 @@ static inline int qc_build_frms(struct list *outlist, struct list *inlist,
else { else {
struct quic_frame *new_cf; struct quic_frame *new_cf;
new_cf = pool_zalloc(pool_head_quic_frame); new_cf = qc_frm_alloc(QUIC_FT_CRYPTO);
if (!new_cf) { if (!new_cf) {
TRACE_ERROR("No memory for new crypto frame", QUIC_EV_CONN_BCFRMS, qc); TRACE_ERROR("No memory for new crypto frame", QUIC_EV_CONN_BCFRMS, qc);
continue; continue;
} }
LIST_INIT(&new_cf->reflist);
new_cf->type = QUIC_FT_CRYPTO;
new_cf->crypto.len = dlen; new_cf->crypto.len = dlen;
new_cf->crypto.offset = cf->crypto.offset; new_cf->crypto.offset = cf->crypto.offset;
new_cf->crypto.qel = qel; new_cf->crypto.qel = qel;
@ -6846,14 +6832,12 @@ static inline int qc_build_frms(struct list *outlist, struct list *inlist,
struct quic_frame *new_cf; struct quic_frame *new_cf;
struct buffer cf_buf; struct buffer cf_buf;
new_cf = pool_zalloc(pool_head_quic_frame); new_cf = qc_frm_alloc(cf->type);
if (!new_cf) { if (!new_cf) {
TRACE_ERROR("No memory for new STREAM frame", QUIC_EV_CONN_BCFRMS, qc); TRACE_ERROR("No memory for new STREAM frame", QUIC_EV_CONN_BCFRMS, qc);
continue; continue;
} }
LIST_INIT(&new_cf->reflist);
new_cf->type = cf->type;
new_cf->stream.stream = cf->stream.stream; new_cf->stream.stream = cf->stream.stream;
new_cf->stream.buf = cf->stream.buf; new_cf->stream.buf = cf->stream.buf;
new_cf->stream.id = cf->stream.id; new_cf->stream.id = cf->stream.id;

View File

@ -10,6 +10,9 @@
#include <string.h> #include <string.h>
#include <import/eb64tree.h> #include <import/eb64tree.h>
#include <haproxy/buf-t.h>
#include <haproxy/chunk.h>
#include <haproxy/pool.h>
#include <haproxy/quic_conn-t.h> #include <haproxy/quic_conn-t.h>
#include <haproxy/quic_enc.h> #include <haproxy/quic_enc.h>
#include <haproxy/quic_frame.h> #include <haproxy/quic_frame.h>