]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: quic: move receive out of FD handler to quic-conn io-cb
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Wed, 16 Nov 2022 10:01:02 +0000 (11:01 +0100)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Fri, 2 Dec 2022 13:45:43 +0000 (14:45 +0100)
This change is the second part for reception on QUIC connection socket.
All operations inside the FD handler has been delayed to quic-conn
tasklet via the new function qc_rcv_buf().

With this change, buffer management on reception has been simplified. It
is now possible to use a local buffer inside qc_rcv_buf() instead of
quic_receiver_buf().

This change is part of quic-conn owned socket implementation.
It may be backported to 2.7 after a period of observation.

include/haproxy/quic_conn.h
include/haproxy/quic_sock.h
src/quic_conn.c
src/quic_sock.c

index 47e256ef2d64d4b784069c5a8bf8b6eda9652c2f..6e4774d4437a137aadfd541199ad297ae611204f 100644 (file)
@@ -743,6 +743,7 @@ void chunk_frm_appendf(struct buffer *buf, const struct quic_frame *frm);
 void quic_set_connection_close(struct quic_conn *qc, const struct quic_err err);
 void quic_set_tls_alert(struct quic_conn *qc, int alert);
 int quic_set_app_ops(struct quic_conn *qc, const unsigned char *alpn, size_t alpn_len);
+int qc_check_dcid(struct quic_conn *qc, unsigned char *dcid, size_t dcid_len);
 int quic_get_dgram_dcid(unsigned char *buf, const unsigned char *end,
                         unsigned char **dcid, size_t *dcid_len);
 int qc_send_mux(struct quic_conn *qc, struct list *frms);
index 3bb87a25907a9a8d3614c36643d74dafbd4b5c7b..f7b0eb5eb203369a0a8100ada9740f05edf8b3c8 100644 (file)
@@ -45,6 +45,7 @@ struct task *quic_lstnr_dghdlr(struct task *t, void *ctx, unsigned int state);
 void quic_lstnr_sock_fd_iocb(int fd);
 int qc_snd_buf(struct quic_conn *qc, const struct buffer *buf, size_t count,
                int flags);
+int qc_rcv_buf(struct quic_conn *qc);
 
 /* Set default value for <qc> socket as uninitialized. */
 static inline void qc_init_fd(struct quic_conn *qc)
index 65f4629ddb1aaa6ced92bf5b3dc02ca02bd50fc9..f86ad4b2a0bd17e50aff2d2e720c1fcbb981eabf 100644 (file)
@@ -4310,6 +4310,9 @@ struct task *quic_conn_app_io_cb(struct task *t, void *context, unsigned int sta
        TRACE_ENTER(QUIC_EV_CONN_IO_CB, qc);
        TRACE_STATE("connection handshake state", QUIC_EV_CONN_IO_CB, qc, &qc->state);
 
+       if (qc_test_fd(qc))
+               qc_rcv_buf(qc);
+
        /* Retranmissions */
        if (qc->flags & QUIC_FL_CONN_RETRANS_NEEDED) {
                TRACE_STATE("retransmission needed", QUIC_EV_CONN_IO_CB, qc);
@@ -4393,7 +4396,10 @@ struct task *quic_conn_io_cb(struct task *t, void *context, unsigned int state)
        zero_rtt = st < QUIC_HS_ST_COMPLETE &&
                quic_tls_has_rx_sec(eqel) &&
                (!LIST_ISEMPTY(&eqel->rx.pqpkts) || qc_el_rx_pkts(eqel));
- start:
+
+       if (qc_test_fd(qc))
+               qc_rcv_buf(qc);
+
        if (st >= QUIC_HS_ST_COMPLETE &&
            qc_el_rx_pkts(&qc->els[QUIC_TLS_ENC_LEVEL_HANDSHAKE])) {
                TRACE_DEVEL("remaining Handshake packets", QUIC_EV_CONN_PHPKTS, qc);
@@ -7422,6 +7428,37 @@ int quic_dgram_parse(struct quic_dgram *dgram, struct quic_conn *from_qc,
        return -1;
 }
 
+/* Check if connection ID <dcid> of length <dcid_len> belongs to <qc> local
+ * CIDs. This can be used to determine if a datagram is addressed to the right
+ * connection instance.
+ *
+ * Returns a boolean value.
+ */
+int qc_check_dcid(struct quic_conn *qc, unsigned char *dcid, size_t dcid_len)
+{
+       struct ebmb_node *node;
+       struct quic_connection_id *id;
+
+       /* For ODCID, address is concatenated to it after qc.odcid.len so this
+        * comparison is safe.
+        */
+       if ((qc->scid.len == dcid_len &&
+            memcmp(qc->scid.data, dcid, dcid_len) == 0) ||
+           (qc->odcid.len == dcid_len &&
+            memcmp(qc->odcid.data, dcid, dcid_len)) == 0) {
+               return 1;
+       }
+
+       node = ebmb_lookup(&quic_dghdlrs[tid].cids, dcid, dcid_len);
+       if (node) {
+               id = ebmb_entry(node, struct quic_connection_id, node);
+               if (qc == id->qc)
+                       return 1;
+       }
+
+       return 0;
+}
+
 /* Retrieve the DCID from a QUIC datagram or packet with <buf> as first octet.
  * Returns 1 if succeeded, 0 if not.
  */
index f1207b7502a724b5abc6fdd383c1d745434b50fd..5b10d8a8ad86f0ad72be0193814a0f35f25fbabd 100644 (file)
@@ -22,6 +22,7 @@
 #include <haproxy/api.h>
 #include <haproxy/buf.h>
 #include <haproxy/connection.h>
+#include <haproxy/dynbuf.h>
 #include <haproxy/fd.h>
 #include <haproxy/freq_ctr.h>
 #include <haproxy/global-t.h>
@@ -487,84 +488,14 @@ void quic_lstnr_sock_fd_iocb(int fd)
 /* FD-owned quic-conn socket callback. */
 static void quic_conn_sock_fd_iocb(int fd)
 {
-       struct quic_conn *qc;
+       struct quic_conn *qc = fdtab[fd].owner;
 
-       struct sockaddr_storage saddr = {0}, daddr = {0};
-       struct quic_receiver_buf *rxbuf;
-       struct quic_transport_params *params;
-       struct quic_dgram *new_dgram;
-       struct buffer *buf;
-       size_t max_sz;
-       size_t cspace;
-       unsigned char *dgram_buf;
-       struct listener *l;
-       ssize_t ret = 0;
-
-       qc = fdtab[fd].owner;
-       l = qc->li;
        TRACE_ENTER(QUIC_EV_CONN_RCV, qc);
 
-       new_dgram = NULL;
-       rxbuf = MT_LIST_POP(&l->rx.rxbuf_list, typeof(rxbuf), rxbuf_el);
-       if (!rxbuf)
-               return;
-
-       buf = &rxbuf->buf;
-       new_dgram = quic_rxbuf_purge_dgrams(rxbuf);
-
-       params = &l->bind_conf->quic_params;
-       max_sz = params->max_udp_payload_size;
-       cspace = b_contig_space(buf);
-       if (cspace < max_sz) {
-               struct quic_dgram *dgram;
-
-               /* Do no mark <buf> as full, and do not try to consume it
-                * if the contiguous remaining space is not at the end
-                */
-               if (b_tail(buf) + cspace < b_wrap(buf))
-                       goto end;
-
-               /* Allocate a fake datagram, without data to locate
-                * the end of the RX buffer (required during purging).
-                */
-               dgram = pool_alloc(pool_head_quic_dgram);
-               if (!dgram)
-                       goto end;
-
-               /* Initialize only the useful members of this fake datagram. */
-               dgram->buf = NULL;
-               dgram->len = cspace;
-               /* Append this datagram only to the RX buffer list. It will
-                * not be treated by any datagram handler.
-                */
-               LIST_APPEND(&rxbuf->dgram_list, &dgram->recv_list);
-
-               /* Consume the remaining space */
-               b_add(buf, cspace);
-               if (b_contig_space(buf) < max_sz)
-                       goto end;
-       }
-
-       dgram_buf = (unsigned char *)b_tail(buf);
-       ret = quic_recv(qc->fd, dgram_buf, max_sz,
-                       (struct sockaddr *)&saddr, sizeof(saddr),
-                       (struct sockaddr *)&daddr, sizeof(daddr),
-                       get_net_port(&qc->local_addr));
-       if (ret <= 0)
-               goto end;
-
-       b_add(buf, ret);
-       if (!quic_lstnr_dgram_dispatch(dgram_buf, ret, l, &qc->peer_addr, &qc->local_addr,
-                                      new_dgram, &rxbuf->dgram_list)) {
-               b_del(buf, ret);
-       }
-       new_dgram = NULL;
+       tasklet_wakeup_after(NULL, qc->wait_event.tasklet);
+       fd_stop_recv(fd);
 
- end:
-       pool_free(pool_head_quic_dgram, new_dgram);
-       MT_LIST_APPEND(&l->rx.rxbuf_list, &rxbuf->rxbuf_el);
        TRACE_LEAVE(QUIC_EV_CONN_RCV, qc);
-       return;
 }
 
 /* Send a datagram stored into <buf> buffer with <sz> as size.
@@ -627,6 +558,96 @@ int qc_snd_buf(struct quic_conn *qc, const struct buffer *buf, size_t sz,
        return 0;
 }
 
+/* Receive datagram on <qc> FD-owned socket.
+ *
+ * Returns the total number of bytes read or a negative value on error.
+ */
+int qc_rcv_buf(struct quic_conn *qc)
+{
+       struct sockaddr_storage saddr = {0}, daddr = {0};
+       struct quic_transport_params *params;
+       struct quic_dgram *new_dgram = NULL;
+       struct buffer buf = BUF_NULL;
+       size_t max_sz;
+       unsigned char *dgram_buf;
+       struct listener *l;
+       ssize_t ret = 0;
+
+       /* Do not call this if quic-conn FD is uninitialized. */
+       BUG_ON(qc->fd < 0);
+
+       TRACE_ENTER(QUIC_EV_CONN_RCV, qc);
+       l = qc->li;
+
+       params = &l->bind_conf->quic_params;
+       max_sz = params->max_udp_payload_size;
+
+       do {
+               if (!b_alloc(&buf))
+                       break; /* TODO subscribe for memory again available. */
+
+               b_reset(&buf);
+               BUG_ON(b_contig_space(&buf) < max_sz);
+
+               /* Allocate datagram on first loop or after requeuing. */
+               if (!new_dgram && !(new_dgram = pool_alloc(pool_head_quic_dgram)))
+                       break; /* TODO subscribe for memory again available. */
+
+               dgram_buf = (unsigned char *)b_tail(&buf);
+               ret = quic_recv(qc->fd, dgram_buf, max_sz,
+                               (struct sockaddr *)&saddr, sizeof(saddr),
+                               (struct sockaddr *)&daddr, sizeof(daddr),
+                               get_net_port(&qc->local_addr));
+               if (ret <= 0) {
+                       /* Subscribe FD for future reception. */
+                       fd_want_recv(qc->fd);
+                       break;
+               }
+
+               b_add(&buf, ret);
+
+               new_dgram->buf = dgram_buf;
+               new_dgram->len = ret;
+               new_dgram->dcid_len = 0;
+               new_dgram->dcid = NULL;
+               new_dgram->saddr = saddr;
+               new_dgram->daddr = daddr;
+               new_dgram->qc = NULL;  /* set later via quic_dgram_parse() */
+
+               TRACE_DEVEL("read datagram", QUIC_EV_CONN_RCV, qc, new_dgram);
+
+               if (!quic_get_dgram_dcid(new_dgram->buf,
+                                        new_dgram->buf + new_dgram->len,
+                                        &new_dgram->dcid, &new_dgram->dcid_len)) {
+                       continue;
+               }
+
+               if (!qc_check_dcid(qc, new_dgram->dcid, new_dgram->dcid_len)) {
+                       /* Datagram received by error on the connection FD, dispatch it
+                        * to its associated quic-conn.
+                        *
+                        * TODO count redispatch datagrams.
+                        */
+                       TRACE_STATE("wrong datagram on quic-conn socket, prepare to requeue it", QUIC_EV_CONN_RCV, qc);
+                       ABORT_NOW();
+               }
+
+               quic_dgram_parse(new_dgram, qc, qc->li);
+               /* A datagram must always be consumed after quic_parse_dgram(). */
+               BUG_ON(new_dgram->buf);
+       } while (ret > 0);
+
+       pool_free(pool_head_quic_dgram, new_dgram);
+
+       if (b_size(&buf)) {
+               b_free(&buf);
+               offer_buffers(NULL, 1);
+       }
+
+       TRACE_LEAVE(QUIC_EV_CONN_RCV, qc);
+       return ret;
+}
+
 /* Allocate a socket file-descriptor specific for QUIC connection <qc>.
  * Endpoint addresses are specified by the two following arguments : <src> is
  * the local address and <dst> is the remote one.