From: Amaury Denoyelle Date: Wed, 8 Apr 2026 08:31:57 +0000 (+0200) Subject: MEDIUM: mux-quic: implement QMux record parsing X-Git-Tag: v3.4-dev9~111 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=5271cdaca3f14c8750b3da2f4cee0adc91750d66;p=thirdparty%2Fhaproxy.git MEDIUM: mux-quic: implement QMux record parsing This is the first patch of a serie which aims to support the new Record layer defined by the draft 01 of QMux protocol. https://www.ietf.org/archive/id/draft-ietf-quic-qmux-01.html#name-qmux-records This patch deals with QMux reception at the MUX layer. The function qcc_qstrm_recv() is adapted to read record headers before frame parsing. This requires to keep the last record length read in a new QCC field named . Frames are only parsed once a full record is received. One of the advantage of the record layer is that it can only contains whole frame without truncation. --- diff --git a/include/haproxy/mux_quic-t.h b/include/haproxy/mux_quic-t.h index 51686c0da..a8c2f5c83 100644 --- a/include/haproxy/mux_quic-t.h +++ b/include/haproxy/mux_quic-t.h @@ -93,6 +93,7 @@ struct qcc { } tx; struct { struct buffer qstrm_buf; + size_t rlen; /* last record length read */ } rx; uint64_t largest_bidi_r; /* largest remote bidi stream ID opened. */ diff --git a/src/mux_quic_qstrm.c b/src/mux_quic_qstrm.c index 4d6107c0d..3b4b44f46 100644 --- a/src/mux_quic_qstrm.c +++ b/src/mux_quic_qstrm.c @@ -93,8 +93,9 @@ int qcc_qstrm_recv(struct qcc *qcc) { struct connection *conn = qcc->conn; struct buffer *buf = &qcc->rx.qstrm_buf; + struct buffer buf_rec; int total = 0, frm_ret; - size_t ret; + size_t ret = 1; TRACE_ENTER(QMUX_EV_QCC_RECV, qcc->conn); @@ -103,14 +104,9 @@ int qcc_qstrm_recv(struct qcc *qcc) /* Wrapping is not supported for QMux reception. */ BUG_ON(b_data(buf) != b_contig_data(buf, 0)); - /* Checks if there is no more room before wrapping position. */ - if (b_head(buf) + b_contig_data(buf, 0) == b_wrap(buf)) { - if (!b_room(buf)) { - /* TODO frame bigger than buffer, connection must be closed */ - ABORT_NOW(); - } - - /* Realign data in the buffer to have more room. */ + /* If current record is too big, realign buffer for more room. */ + if (b_head(buf) + qcc->rx.rlen > b_wrap(buf)) { + BUG_ON(qcc->rx.rlen > b_size(buf)); /* TODO max_record_size */ memmove(b_orig(buf), b_head(buf), b_data(buf)); buf->head = 0; } @@ -119,23 +115,42 @@ int qcc_qstrm_recv(struct qcc *qcc) b_realign_if_empty(buf); } - ret = conn->xprt->rcv_buf(conn, conn->xprt_ctx, buf, b_contig_space(buf), NULL, 0, 0); - BUG_ON(conn->flags & CO_FL_ERROR); + if ((!b_data(buf) && !qcc->rx.rlen) || qcc->rx.rlen > b_data(buf)) { + /* Previous realign operation should ensure send cannot result in data wrapping. */ + BUG_ON(b_data(buf) && b_tail(buf) == b_orig(buf)); + ret = conn->xprt->rcv_buf(conn, conn->xprt_ctx, buf, b_contig_space(buf), NULL, 0, 0); + BUG_ON(conn->flags & CO_FL_ERROR); /* TODO handle errors */ + /* Previous realign operation should ensure send cannot result in data wrapping. */ + BUG_ON(b_data(buf) != b_contig_data(buf, 0)); + } - total += ret; - while (b_data(buf)) { - frm_ret = qstrm_parse_frm(qcc, buf); + if (b_data(buf) && !qcc->rx.rlen) { + int ret2 = b_quic_dec_int(&qcc->rx.rlen, buf, NULL); + BUG_ON(!ret2); /* TODO incomplete record length */ + if (b_head(buf) + qcc->rx.rlen > b_wrap(buf)) + goto recv; + BUG_ON(b_data(buf) < qcc->rx.rlen); /* TODO incomplete record */ + } + + /* TODO realign necessary if record boundary at the extreme end of the buffer */ + BUG_ON(!qcc->rx.rlen && b_data(buf) && b_tail(buf) == b_orig(buf)); + + while (qcc->rx.rlen && b_data(buf) >= qcc->rx.rlen) { + buf_rec = b_make(b_orig(buf), b_size(buf), + b_head_ofs(buf), qcc->rx.rlen); + frm_ret = qstrm_parse_frm(qcc, &buf_rec); BUG_ON(frm_ret < 0); /* TODO handle fatal errors */ if (!frm_ret) { - /* Checks if wrapping position is reached, requires realign. */ - if (b_head(buf) + b_contig_data(buf, 0) == b_wrap(buf)) - goto recv; - /* Truncated frame read but room still left, subscribe to retry later. */ - break; + /* emit FRAME_ENCODING_ERROR */ + ABORT_NOW(); } + /* A frame cannot be bigger than a record thanks to delimitation. */ + BUG_ON(qcc->rx.rlen < frm_ret); b_del(buf, frm_ret); + qcc->rx.rlen -= frm_ret; + total += frm_ret; } } while (ret > 0);