]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: xprt_qstrm: implement QMux record parsing
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Wed, 8 Apr 2026 12:13:05 +0000 (14:13 +0200)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Fri, 10 Apr 2026 08:20:52 +0000 (10:20 +0200)
This patch implements the new QMux record layer parsing for xprt_qstrm.
This is mostly similar to the MUX code from the previous patch.

Along with this change, a new xprt_qstrm layer accessor exposes the
possible remaining record length after Transport parameters parsing.
This can only occur when xprt_qstrm Rx buffer is not completely emptied
due to other following frames. If stored in the same record, MUX layer
has to know the remaining record length.

Thus, xprt_qstrm_rxrlen() is now used in qmux_init() to preinitialize
<rx.rlen> QCC field.

include/haproxy/xprt_qstrm.h
src/mux_quic.c
src/xprt_qstrm.c

index 536ba1abd0a6397513975287c533f70242338e48..5a992f21f25ba011e7fea37df2fdde59fb8804f8 100644 (file)
@@ -5,5 +5,6 @@ const struct quic_transport_params *xprt_qstrm_lparams(const void *context);
 const struct quic_transport_params *xprt_qstrm_rparams(const void *context);
 
 struct buffer *xprt_qstrm_rxbuf(const void *context);
+size_t xprt_qstrm_rxrlen(const void *context);
 
 #endif /* _HAPROXY_XPRT_QSTRM_H */
index 15302f5fa35154ad908785ffec6f3bc9d955dbbd..0ea65fefc9d6e05ddf2811cce28b0acc4ebea6a1 100644 (file)
@@ -3802,8 +3802,13 @@ static int qmux_init(struct connection *conn, struct proxy *prx,
 
                /* Retrieve data if xprt read too much */
                xprt_buf = xprt_qstrm_rxbuf(conn->xprt_ctx);
-               if (unlikely(b_data(xprt_buf)))
+               if (unlikely(b_data(xprt_buf))) {
                        b_xfer(&qcc->rx.qstrm_buf, xprt_buf, b_data(xprt_buf));
+                       qcc->rx.rlen = xprt_qstrm_rxrlen(conn->xprt_ctx);
+               }
+               else {
+                       qcc->rx.rlen = 0;
+               }
        }
 
        if (conn_is_back(conn)) {
index 01a9265fcb7f64c77e9e1e7ad0967ddd1dcd75bd..5d6ac9adcdbd2cb032f7c0f4b2f7e906903d525e 100644 (file)
@@ -5,6 +5,7 @@
 #include <haproxy/fd.h>
 #include <haproxy/global.h>
 #include <haproxy/mux_quic.h>
+#include <haproxy/mux_quic_qstrm.h>
 #include <haproxy/pool.h>
 #include <haproxy/quic_frame.h>
 #include <haproxy/quic_tp-t.h>
@@ -22,6 +23,7 @@ struct xprt_qstrm_ctx {
 
        struct buffer txbuf;
        struct buffer rxbuf;
+       size_t rxrlen;
 };
 
 DECLARE_STATIC_TYPED_POOL(xprt_qstrm_ctx_pool, "xprt_qstrm_ctx", struct xprt_qstrm_ctx);
@@ -45,11 +47,17 @@ struct buffer *xprt_qstrm_rxbuf(void *context)
        return &ctx->rxbuf;
 }
 
+size_t xprt_qstrm_rxrlen(const void *context)
+{
+       const struct xprt_qstrm_ctx *ctx = context;
+       return ctx->rxrlen;
+}
+
 int conn_recv_qstrm(struct connection *conn, struct xprt_qstrm_ctx *ctx, int flag)
 {
        struct quic_frame frm;
        struct buffer *buf = &ctx->rxbuf;
-       const unsigned char *pos, *end;
+       const unsigned char *pos, *old, *end;
        size_t ret;
 
        if (!conn_ctrl_ready(conn))
@@ -72,18 +80,32 @@ int conn_recv_qstrm(struct connection *conn, struct xprt_qstrm_ctx *ctx, int fla
        if (!b_data(buf))
                goto not_ready;
 
-       pos = (unsigned char *)b_orig(buf);
-       end = (unsigned char *)(b_orig(buf) + b_data(buf));
-       if (!qc_parse_frm_type(&frm, &pos, end, NULL))
+       /* Read record length. */
+       if (!b_quic_dec_int(&ctx->rxrlen, buf, NULL))
                goto not_ready;
 
+       /* Reject too small or too big records. */
+       if (!ctx->rxrlen || ctx->rxrlen > b_size(buf))
+               goto fail;
+       if (ctx->rxrlen > b_data(buf))
+               goto not_ready;
+
+       old = pos = (unsigned char *)b_head(buf);
+       end = pos + ctx->rxrlen;
+       if (!qc_parse_frm_type(&frm, &pos, end, NULL))
+               goto fail;
+
        /* TODO close connection with TRANSPORT_PARAMETER_ERROR if frame not present. */
        BUG_ON(frm.type != QUIC_FT_QX_TRANSPORT_PARAMETERS);
 
        if (!qc_parse_frm_payload(&frm, &pos, end, NULL))
-               goto not_ready;
+               goto fail;
 
        ctx->rparams = frm.qmux_transport_params.params;
+       b_del(buf, pos - old);
+       /* <end> delimiter should guarantee than frame length does not go beyong the record end */
+       BUG_ON(ctx->rxrlen < pos - old);
+       ctx->rxrlen -= (pos - old);
 
        conn->flags &= ~flag;
        return 1;
@@ -235,6 +257,7 @@ static int xprt_qstrm_init(struct connection *conn, void **xprt_ctx)
        ctx->ops_lower = NULL;
 
        ctx->rxbuf = BUF_NULL;
+       ctx->rxrlen = 0;
        ctx->txbuf = BUF_NULL;
 
        memset(&ctx->rparams, 0, sizeof(struct quic_transport_params));