]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: quic-be: QUIC connection allocation adaptation (qc_new_conn())
authorFrederic Lecaille <flecaille@haproxy.com>
Fri, 5 Jan 2024 15:32:42 +0000 (16:32 +0100)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Wed, 11 Jun 2025 16:37:34 +0000 (18:37 +0200)
For haproxy QUIC servers (or QUIC clients), the peer is considered as validated.
This is a property which is more specific to QUIC servers (haproxy QUIC listeners).
No <odcid> is used for the QUIC client connection. It is used only on the QUIC server side.
The <token_odcid> is also not used on the QUIC client side. It must be embedded into
the transport parameters only on the QUIC server side.
The quic_conn is created before the socket allocation. So, the local address is
zeroed.
Initilize the transport parameter with qc_srv_params_init().
Stop hardcoding the <server> parameter passed value to qc_new_isecs() to correctly
initialize the Initial secrets.

src/quic_conn.c

index 64df8bf04bbf6a581e7fa7c5322ef4a7c199a793..47cf5ad19b78543364b7467e88fabe7fae16a83d 100644 (file)
@@ -1036,13 +1036,23 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
                               struct connection *conn)
 {
        struct quic_conn *qc = NULL;
-       struct listener *l = server ? owner : NULL;
-       struct proxy *prx = l ? l->bind_conf->frontend : NULL;
+       struct listener *l = NULL;
+       struct server *srv = NULL;
+       struct proxy *prx = NULL;
        struct quic_cc_algo *cc_algo = NULL;
        unsigned int next_actconn = 0, next_sslconn = 0, next_handshake = 0;
 
        TRACE_ENTER(QUIC_EV_CONN_INIT);
 
+       if (server) {
+               l = owner;
+               prx = l->bind_conf->frontend;
+       }
+       else {
+               srv = owner;
+               prx = srv->proxy;
+       }
+
        next_actconn = increment_actconn();
        if (!next_actconn) {
                _HA_ATOMIC_INC(&maxconn_reached);
@@ -1070,6 +1080,13 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
                goto err;
        }
 
+       qc->cids = pool_alloc(pool_head_quic_cids);
+       if (!qc->cids) {
+               TRACE_ERROR("Could not allocate a new CID tree", QUIC_EV_CONN_INIT, qc);
+               goto err;
+       }
+
+       *qc->cids = EB_ROOT;
        /* Now that quic_conn instance is allocated, quic_conn_release() will
         * ensure global accounting is decremented.
         */
@@ -1087,7 +1104,6 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
        qc->streams_by_id = EB_ROOT_UNIQUE;
 
        /* Required to call free_quic_conn_cids() from quic_conn_release() */
-       qc->cids = NULL;
        qc->tx.cc_buf_area = NULL;
        qc_init_fd(qc);
 
@@ -1122,16 +1138,12 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
        /* Packet number spaces */
        qc->ipktns = qc->hpktns = qc->apktns = NULL;
        LIST_INIT(&qc->pktns_list);
-
-       /* Required to safely call quic_conn_prx_cntrs_update() from quic_conn_release(). */
-       qc->prx_counters = NULL;
+       qc->prx_counters = EXTRA_COUNTERS_GET(prx->extra_counters_fe, &quic_stats_module);
 
        /* QUIC Server (or listener). */
        if (server) {
                cc_algo = l->bind_conf->quic_cc_algo;
 
-               qc->prx_counters = EXTRA_COUNTERS_GET(prx->extra_counters_fe,
-                                                     &quic_stats_module);
                qc->flags = QUIC_FL_CONN_LISTENER;
                /* Mark this connection as having not received any token when 0-RTT is enabled. */
                if (l->bind_conf->ssl_conf.early_data && !token)
@@ -1143,28 +1155,53 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
                qc->dcid = *scid;
                qc->tx.buf = BUF_NULL;
                qc->li = l;
+               conn_id->qc = qc;
        }
        /* QUIC Client (outgoing connection to servers) */
        else {
+               struct quic_connection_id *conn_cid = NULL;
+
+               qc->flags = QUIC_FL_CONN_PEER_VALIDATED_ADDR;
                qc->state = QUIC_HS_ST_CLIENT_INITIAL;
-               if (dcid->len)
-                       memcpy(qc->dcid.data, dcid->data, dcid->len);
-               qc->dcid.len = dcid->len;
+
+               memset(&qc->odcid, 0, sizeof qc->odcid);
+               qc->odcid.len = 0;
+               /* This is the original connection ID from the peer server
+                * point of view.
+                */
+               if (RAND_bytes(qc->dcid.data, sizeof(qc->dcid.data)) != 1)
+                       goto err;
+
+               qc->dcid.len = sizeof(qc->dcid.data);
+
+               conn_cid = new_quic_cid(qc->cids, qc, NULL, NULL);
+               if (!conn_cid)
+                       goto err;
+
+               _quic_cid_insert(conn_cid);
+               dcid = &qc->dcid;
+               conn_id = conn_cid;
+
+               qc->tx.buf = BUF_NULL;
                qc->li = NULL;
+               qc->next_cid_seq_num = 1;
+               conn->handle.qc = qc;
        }
        qc->mux_state = QC_MUX_NULL;
        qc->err = quic_err_transport(QC_ERR_NO_ERROR);
 
-       /* If connection is instantiated due to an INITIAL packet with an
+       /* Listener only: if connection is instantiated due to an INITIAL packet with an
         * already checked token, consider the peer address as validated.
         */
-       if (token) {
-               TRACE_STATE("validate peer address due to initial token",
-                           QUIC_EV_CONN_INIT, qc);
-               qc->flags |= QUIC_FL_CONN_PEER_VALIDATED_ADDR;
-       }
-       else {
-               HA_ATOMIC_INC(&qc->prx_counters->half_open_conn);
+       if (server) {
+               if (token_odcid->len) {
+                       TRACE_STATE("validate peer address due to initial token",
+                                               QUIC_EV_CONN_INIT, qc);
+                       qc->flags |= QUIC_FL_CONN_PEER_VALIDATED_ADDR;
+               }
+               else {
+                       HA_ATOMIC_INC(&qc->prx_counters->half_open_conn);
+               }
        }
 
        /* Now proceeds to allocation of qc members. */
@@ -1174,16 +1211,8 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
                goto err;
        }
 
-       qc->cids = pool_alloc(pool_head_quic_cids);
-       if (!qc->cids) {
-               TRACE_ERROR("Could not allocate a new CID tree", QUIC_EV_CONN_INIT, qc);
-               goto err;
-       }
-       *qc->cids = EB_ROOT;
-
-       conn_id->qc = qc;
-
-       if (HA_ATOMIC_LOAD(&l->rx.quic_mode) == QUIC_SOCK_MODE_CONN &&
+       /* Listener only */
+       if (l && HA_ATOMIC_LOAD(&l->rx.quic_mode) == QUIC_SOCK_MODE_CONN &&
            (quic_tune.options & QUIC_TUNE_SOCK_PER_CONN) &&
            is_addr(local_addr)) {
                TRACE_USER("Allocate a socket for QUIC connection", QUIC_EV_CONN_INIT, qc);
@@ -1233,17 +1262,25 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
        qc->max_ack_delay = 0;
        /* Only one path at this time (multipath not supported) */
        qc->path = &qc->paths[0];
-       quic_cc_path_init(qc->path, ipv4, server ? l->bind_conf->max_cwnd : 0,
+       quic_cc_path_init(qc->path, ipv4,
+                         server ? l->bind_conf->max_cwnd : 0,
                          cc_algo ? cc_algo : default_quic_cc_algo, qc);
 
-       memcpy(&qc->local_addr, local_addr, sizeof(qc->local_addr));
+       if (local_addr)
+               memcpy(&qc->local_addr, local_addr, sizeof(qc->local_addr));
+       else
+               memset(&qc->local_addr, 0, sizeof(qc->local_addr));
        memcpy(&qc->peer_addr, peer_addr, sizeof qc->peer_addr);
 
-       if (server && !qc_lstnr_params_init(qc, &l->bind_conf->quic_params,
-                                           conn_id->stateless_reset_token,
-                                           dcid->data, dcid->len,
-                                           qc->scid.data, qc->scid.len, token_odcid))
-               goto err;
+       if (server) {
+               qc_lstnr_params_init(qc, &l->bind_conf->quic_params,
+                                    conn_id->stateless_reset_token,
+                                    qc->dcid.data, qc->dcid.len,
+                                    qc->scid.data, qc->scid.len, token_odcid);
+       }
+       else {
+               qc_srv_params_init(qc, &srv->quic_params, qc->scid.data, qc->scid.len);
+       }
 
        /* Initialize the idle timeout of the connection at the "max_idle_timeout"
         * value from local transport parameters.
@@ -1269,7 +1306,7 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
            !quic_conn_init_idle_timer_task(qc, prx))
                goto err;
 
-       if (!qc_new_isecs(qc, &qc->iel->tls_ctx, qc->original_version, dcid->data, dcid->len, 1))
+       if (!qc_new_isecs(qc, &qc->iel->tls_ctx, qc->original_version, dcid->data, dcid->len, server))
                goto err;
 
        /* Counters initialization */
@@ -1283,6 +1320,7 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
        return qc;
 
  err:
+       pool_free(pool_head_quic_connection_id, conn_id);
        quic_conn_release(qc);
 
        /* Decrement global counters. Done only for errors happening before or