]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: quic: support multiple random CID generation for BE side
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Fri, 7 Nov 2025 10:59:24 +0000 (11:59 +0100)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Mon, 17 Nov 2025 09:11:04 +0000 (10:11 +0100)
When a new backend connection is instantiated, a CID is first randomly
generated. It will serve as the first DCID for incoming packets from the
server. Prior to this patch, if the generated CID caused a collision
with an other entries from another connection, an error is reported and
the connection cannot be allocated.

This patch improves this procedure by implementing retries when a
collision occurs. Now, at most three attemps will be performed before
giving up. This is the same procedure already performed for CIDs
instantiated after RETIRE_CONNECTION_ID frame parsing.

Along with this functional change, qc_new_conn() is refactored for
backend instantiation. The CID generation is extracted from it and the
value is passed as an argument. This is considered cleaner as the code
is more similar between frontend and backend sides.

src/quic_conn.c
src/xprt_quic.c

index cf0d6f2ab5eb0c6dec57fb0df431d2ec2ea214bc..88adc84f8493f1846e0591fbac9706f190bb7259 100644 (file)
@@ -1108,9 +1108,9 @@ struct task *qc_process_timer(struct task *task, void *ctx, unsigned int state)
  * into the Retry token sent to the client before instantiated this connection.
  * Endpoints addresses are specified via <local_addr> and <peer_addr>.
  * Returns the connection if succeeded, NULL if not.
- * For QUIC clients, <dcid>, <scid>, <token_odcid>, <conn_id> must be null,
- * and <token> value must be 0. This is the responsibility of the caller to ensure
- * this is the case.
+ * For QUIC clients, <dcid>, <scid> and <token_odcid> must be null, and <token>
+ * value must be 0. This is the responsibility of the caller to ensure this is
+ * the case.
  */
 struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
                               struct quic_cid *dcid, struct quic_cid *scid,
@@ -1243,8 +1243,6 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
        }
        /* QUIC Client (outgoing connection to servers) */
        else {
-               struct quic_connection_id *conn_cid = NULL;
-
                qc->flags = QUIC_FL_CONN_IS_BACK|QUIC_FL_CONN_PEER_VALIDATED_ADDR;
                /* Duplicate GSO status on server to connection */
                if (HA_ATOMIC_LOAD(&srv->flags) & SRV_F_UDP_GSO_NOTSUPP)
@@ -1263,26 +1261,7 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
                memcpy(&qc->odcid, qc->dcid.data, sizeof(qc->dcid.data));
                qc->odcid.len = qc->dcid.len;
 
-               conn_cid = quic_cid_alloc();
-               if (!conn_cid) {
-                       TRACE_ERROR("error on CID allocation", QUIC_EV_CONN_INIT, qc);
-                       goto err;
-               }
-
-               if (quic_cid_generate_random(conn_cid)) {
-                       TRACE_ERROR("error on CID generation", QUIC_EV_CONN_INIT, qc);
-                       pool_free(pool_head_quic_connection_id, conn_cid);
-                       goto err;
-               }
-
-               if (quic_cid_insert(conn_cid, NULL)) {
-                       pool_free(pool_head_quic_connection_id, conn_cid);
-                       goto err;
-               }
-
-               quic_cid_register_seq_num(conn_cid, qc);
                dcid = &qc->dcid;
-               conn_id = conn_cid;
        }
        qc->err = quic_err_transport(QC_ERR_NO_ERROR);
 
@@ -1420,15 +1399,6 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
        return qc;
 
  err:
-       if (!l && !conn_id) {
-               /* For QUIC clients, <conn_id> is locally used and initialized to <conn_cid>
-                * value as soon as this latter is attached to the CIDs tree. It must
-                * be freed only if it has not been attached to this tree. This is
-                * quic_conn_release() which free this CID when it is attached to the tree.
-                */
-               pool_free(pool_head_quic_connection_id, conn_id);
-       }
-
        quic_conn_release(qc);
 
        /* Decrement global counters. Done only for errors happening before or
index 5ab196a3b8fe5d6ec07b97e559ab9964a274d195..7c21cd71dc9433583b6912bcf18b089218595e12 100644 (file)
@@ -125,26 +125,56 @@ static int qc_conn_init(struct connection *conn, void **xprt_ctx)
 {
        int ret = -1;
        struct quic_conn *qc = NULL;
+       struct quic_connection_id *conn_id;
 
        TRACE_ENTER(QUIC_EV_CONN_NEW, qc);
 
        if (objt_listener(conn->target)) {
                qc = conn->handle.qc;
+               if (!qc)
+                       goto out;
        }
        else {
+               int retry_rand_cid = 3; /* Number of random retries on CID collision. */
                int ipv4 = conn->dst->ss_family == AF_INET;
                struct server *srv = objt_server(conn->target);
+
+               conn_id = quic_cid_alloc();
+               if (!conn_id) {
+                       TRACE_ERROR("error on CID allocation", QUIC_EV_CONN_NEW);
+                       goto out;
+               }
+
+               while (retry_rand_cid--) {
+                       if (quic_cid_generate_random(conn_id)) {
+                               TRACE_ERROR("error on CID generation", QUIC_EV_CONN_NEW);
+                               pool_free(pool_head_quic_connection_id, conn_id);
+                               goto out;
+                       }
+
+                       if (quic_cid_insert(conn_id, NULL) == 0)
+                               break;
+               }
+
+               if (retry_rand_cid < 0) {
+                       TRACE_ERROR("CID pool exhausted", QUIC_EV_CONN_NEW);
+                       pool_free(pool_head_quic_connection_id, conn_id);
+                       goto out;
+               }
+
                qc = qc_new_conn(quic_version_1, ipv4, NULL, NULL, NULL,
-                                NULL, NULL, &srv->addr, 0, srv);
-               if (qc) {
-                       conn->flags |= CO_FL_SSL_WAIT_HS | CO_FL_WAIT_L6_CONN;
-                       conn->handle.qc = qc;
-                       qc->conn = conn;
+                                conn_id, NULL, &srv->addr, 0, srv);
+               if (!qc) {
+                       pool_free(pool_head_quic_connection_id, conn_id);
+                       goto out;
                }
-       }
 
-       if (!qc)
-               goto out;
+               quic_cid_register_seq_num(conn_id, qc);
+
+               conn->flags |= CO_FL_SSL_WAIT_HS | CO_FL_WAIT_L6_CONN;
+               conn->handle.qc = qc;
+               qc->conn = conn;
+       }
 
        ret = 0;
        /* Ensure thread connection migration is finalized ASAP. */