]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MAJOR: quic: implement accept queue
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Wed, 19 Jan 2022 15:01:05 +0000 (16:01 +0100)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Wed, 26 Jan 2022 15:13:54 +0000 (16:13 +0100)
Do not proceed to direct accept when creating a new quic_conn. Wait for
the QUIC handshake to succeeds to insert the quic_conn in the accept
queue. A tasklet is then woken up to call listener_accept to accept the
quic_conn.

The most important effect is that the connection/mux layers are not
instantiated at the same time as the quic_conn. This forces to delay
some process to be sure that the mux is allocated :
* initialization of mux transport parameters
* installation of the app-ops

Also, the mux instance is not checked now to wake up the quic_conn
tasklet. This is safe because the xprt-quic code is now ready to handle
the absence of the connection/mux layers.

Note that this commit has a deep impact as it changes significantly the
lower QUIC architecture. Most notably, it breaks the 0-RTT feature.

include/haproxy/quic_sock.h
include/haproxy/receiver-t.h
include/haproxy/xprt_quic-t.h
src/proto_quic.c
src/quic_sock.c
src/ssl_sock.c
src/xprt_quic.c

index 2a2818bc514b2740ddbd3bc876e72a01e36b60c8..1f2628f0d195249ea7c90d8e89676a0d06b50013 100644 (file)
@@ -39,6 +39,8 @@ int quic_sock_accepting_conn(const struct receiver *rx);
 struct connection *quic_sock_accept_conn(struct listener *l, int *status);
 void quic_sock_fd_iocb(int fd);
 
+void quic_accept_push_qc(struct quic_conn *qc);
+
 #endif /* USE_QUIC */
 #endif /* _HAPROXY_QUIC_SOCK_H */
 
index 7c04d4871416f821dd3ca9306f7b5e89dbcf2bb3..ddf3c3564d4b48df9d1d5b6690d663ed386bba86 100644 (file)
@@ -65,7 +65,6 @@ struct receiver {
        struct rx_settings *settings;    /* points to the settings used by this receiver */
        struct list proto_list;          /* list in the protocol header */
 #ifdef USE_QUIC
-       struct mt_list pkts;             /* QUIC Initial packets to accept new connections */
        struct eb_root odcids;           /* QUIC original destination connection IDs. */
        struct eb_root cids;             /* QUIC connection IDs. */
        __decl_thread(HA_RWLOCK_T cids_lock); /* RW lock for connection IDs tree accesses */
index bbf1b99ac1739395e28ce9feb9f13fd2a170b909..2ae067f11688c09e920b5f5e095026a017175cb7 100644 (file)
@@ -732,6 +732,7 @@ struct quic_conn {
        struct quic_path *path;
 
        struct listener *li; /* only valid for frontend connections */
+       struct mt_list accept_list; /* chaining element used for accept, only valid for frontend connections */
        /* MUX */
        struct qcc *qcc;
        struct task *timer_task;
index dc3d26219f644ed07826f08d3e6d8bf853e25e98..4307ceee1900bf666f64d575db81d98250db7bc6 100644 (file)
@@ -520,7 +520,6 @@ static void quic_add_listener(struct protocol *proto, struct listener *listener)
 {
        listener->flags |= LI_F_QUIC_LISTENER;
 
-       MT_LIST_INIT(&listener->rx.pkts);
        listener->rx.odcids = EB_ROOT_UNIQUE;
        listener->rx.cids = EB_ROOT_UNIQUE;
        listener->rx.flags |= RX_F_LOCAL_ACCEPT;
index 866eaa28784ca2067360db786d72d1ba7639fa76..ee6a2fad7d48e674d55f97f0712032f757fda067 100644 (file)
@@ -140,30 +140,25 @@ int quic_sock_accepting_conn(const struct receiver *rx)
 struct connection *quic_sock_accept_conn(struct listener *l, int *status)
 {
        struct quic_conn *qc;
-       struct quic_rx_packet *pkt;
-       int ret;
+       struct li_per_thread *lthr = &l->per_thr[tid];
 
-       qc = NULL;
-       pkt = MT_LIST_POP(&l->rx.pkts, struct quic_rx_packet *, rx_list);
-       /* Should never happen. */
-       if (!pkt)
-               goto err;
+       qc = MT_LIST_POP(&lthr->quic_accept.conns, struct quic_conn *, accept_list);
+       if (!qc)
+               goto done;
 
-       qc = pkt->qc;
-       if (!new_quic_cli_conn(qc, l, &pkt->saddr))
+       if (!new_quic_cli_conn(qc, l, &qc->peer_addr))
                goto err;
 
-       ret = CO_AC_DONE;
-
  done:
-       if (status)
-               *status = ret;
-
+       *status = CO_AC_DONE;
        return qc ? qc->conn : NULL;
 
  err:
-       ret = CO_AC_PAUSE;
-       goto done;
+       /* in case of error reinsert the element to process it later. */
+       MT_LIST_INSERT(&lthr->quic_accept.conns, &qc->accept_list);
+
+       *status = CO_AC_PAUSE;
+       return NULL;
 }
 
 /* Function called on a read event from a listening socket. It tries
@@ -228,6 +223,32 @@ void quic_sock_fd_iocb(int fd)
 /* per-thread accept queues */
 struct quic_accept_queue *quic_accept_queues;
 
+/* Install <qc> on the queue ready to be accepted. The queue task is then woken
+ * up.
+ */
+void quic_accept_push_qc(struct quic_conn *qc)
+{
+       struct quic_accept_queue *queue = &quic_accept_queues[qc->tid];
+       struct li_per_thread *lthr = &qc->li->per_thr[qc->tid];
+
+       BUG_ON(MT_LIST_INLIST(&qc->accept_list));
+
+       /* 1. insert the listener in the accept queue
+        *
+        * Use TRY_APPEND as there is a possible race even with INLIST if
+        * multiple threads try to add the same listener instance from several
+        * quic_conn.
+        */
+       if (!MT_LIST_INLIST(&(lthr->quic_accept.list)))
+               MT_LIST_TRY_APPEND(&queue->listeners, &(lthr->quic_accept.list));
+
+       /* 2. insert the quic_conn in the listener per-thread queue. */
+       MT_LIST_APPEND(&lthr->quic_accept.conns, &qc->accept_list);
+
+       /* 3. wake up the queue tasklet */
+       tasklet_wakeup(quic_accept_queues[qc->tid].tasklet);
+}
+
 /* Tasklet handler to accept QUIC connections. Call listener_accept on every
  * listener instances registered in the accept queue.
  */
index 572fa76476fbc12d4532fe7e0896870bf6e4c26f..0b65d888ea522de3bbc9984d9f6ed02a7784b7b2 100644 (file)
@@ -2523,8 +2523,6 @@ int ssl_sock_switchctx_cbk(SSL *ssl, int *al, void *arg)
                if (!quic_transport_params_store(qc, 0, extension_data,
                                                 extension_data + extension_len))
                        goto abort;
-
-               quic_mux_transport_params_update(qc->qcc);
        }
 #endif /* USE_QUIC */
 
index 26e085f148b3777ad879829415023f089d8a4af1..588d8a24a2057f7667088c9827ece26ec20eac55 100644 (file)
@@ -44,6 +44,7 @@
 #include <haproxy/quic_cc.h>
 #include <haproxy/quic_frame.h>
 #include <haproxy/quic_loss.h>
+#include <haproxy/quic_sock.h>
 #include <haproxy/cbuf.h>
 #include <haproxy/quic_tls.h>
 #include <haproxy/sink.h>
@@ -1048,12 +1049,6 @@ int quic_set_app_ops(struct quic_conn *qc, const unsigned char *alpn, size_t alp
        else
                return 0;
 
-       if (qcc_install_app_ops(qc->qcc, qc->app_ops))
-               return 0;
-
-       /* mux-quic can now be considered ready. */
-       qc->mux_state = QC_MUX_READY;
-
        return 1;
 }
 
@@ -1892,10 +1887,14 @@ static inline int qc_provide_cdata(struct quic_enc_level *el,
                }
 
                TRACE_PROTO("SSL handshake OK", QUIC_EV_CONN_HDSHK, qc, &state);
-               if (qc_is_listener(ctx->qc))
+               if (qc_is_listener(ctx->qc)) {
                        HA_ATOMIC_STORE(&qc->state, QUIC_HS_ST_CONFIRMED);
-               else
+                       /* The connection is ready to be accepted. */
+                       quic_accept_push_qc(qc);
+               }
+               else {
                        HA_ATOMIC_STORE(&qc->state, QUIC_HS_ST_COMPLETE);
+               }
        } else {
                ssl_err = SSL_process_quic_post_handshake(ctx->ssl);
                if (ssl_err != 1) {
@@ -3662,6 +3661,9 @@ static struct quic_conn *qc_new_conn(unsigned int version, int ipv4,
        qc->path = &qc->paths[0];
        quic_path_init(qc->path, ipv4, default_quic_cc_algo, qc);
 
+       /* required to use MTLIST_IN_LIST */
+       MT_LIST_INIT(&qc->accept_list);
+
        TRACE_LEAVE(QUIC_EV_CONN_INIT, qc);
 
        return qc;
@@ -4557,9 +4559,6 @@ static ssize_t qc_lstnr_pkt_rcv(unsigned char *buf, const unsigned char *end,
                        if (likely(!qc_to_purge)) {
                                /* Enqueue this packet. */
                                pkt->qc = qc;
-                               MT_LIST_APPEND(&l->rx.pkts, &pkt->rx_list);
-                               /* Try to accept a new connection. */
-                               listener_accept(l);
                        }
                        else {
                                quic_conn_drop(qc_to_purge);
@@ -4663,7 +4662,7 @@ static ssize_t qc_lstnr_pkt_rcv(unsigned char *buf, const unsigned char *end,
         * initialized.
         */
        conn_ctx = HA_ATOMIC_LOAD(&qc->xprt_ctx);
-       if (conn_ctx && HA_ATOMIC_LOAD(&qc->qcc))
+       if (conn_ctx)
                tasklet_wakeup(conn_ctx->wait_event.tasklet);
 
        TRACE_LEAVE(QUIC_EV_CONN_LPKT, qc ? qc : NULL, pkt);
@@ -5484,6 +5483,15 @@ static int qc_xprt_start(struct connection *conn, void *ctx)
                return 0;
        }
 
+       quic_mux_transport_params_update(qc->qcc);
+       if (qcc_install_app_ops(qc->qcc, qc->app_ops)) {
+               TRACE_PROTO("Cannot install app layer", QUIC_EV_CONN_LPKT, qc);
+               return 0;
+       }
+
+       /* mux-quic can now be considered ready. */
+       qc->mux_state = QC_MUX_READY;
+
        tasklet_wakeup(qctx->wait_event.tasklet);
        return 1;
 }