]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: quic-be: Datagrams and packet parsing support
authorFrederic Lecaille <flecaille@haproxy.com>
Wed, 10 Jan 2024 15:48:51 +0000 (16:48 +0100)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Wed, 11 Jun 2025 16:37:34 +0000 (18:37 +0200)
Modify quic_dgram_parse() to stop passing it a listener as third parameter.
In place the object type address of the connection socket owner is passed
to support the haproxy servers with QUIC as transport protocol.
qc_owner_obj_type() is implemented to return this address.
qc_counters() is also implemented to return the QUIC specific counters of
the proxy of owner of the connection.
quic_rx_pkt_parse() called by quic_dgram_parse() is also modify to use
the object type address used by this latter as last parameter. It is
also modified to send Retry packet only from listeners. A QUIC client
(connection to haproxy QUIC servers) must drop the Initial packets with
non null token length. It is also not supposed to receive O-RTT packets
which are dropped.

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

index 77d9f69beee05b8a8b75e06cf21b5635211b5679..409ebf2c3701023fbc065038890e97667099bd29 100644 (file)
@@ -164,6 +164,29 @@ static inline void quic_free_ncbuf(struct ncbuf *ncbuf)
        *ncbuf = NCBUF_NULL;
 }
 
+/* Return the address of the connection owner object type. */
+static inline enum obj_type *qc_owner_obj_type(struct quic_conn *qc)
+{
+       return qc_is_listener(qc) ? &qc->li->obj_type :
+               &objt_server(qc->conn->target)->obj_type;
+}
+
+/* Return the address of the QUIC counters attached to the proxy of
+ * the owner of the connection whose object type address is <o> for
+ * listener and servers, or NULL for others object type.
+ */
+static inline void *qc_counters(enum obj_type *o, const struct stats_module *m)
+{
+       struct proxy *p;
+       struct listener *l = objt_listener(o);
+       struct server *s = objt_server(o);
+
+       p = l ? l->bind_conf->frontend :
+               s ? s->proxy : NULL;
+
+       return p ? EXTRA_COUNTERS_GET(p->extra_counters_fe, m) : NULL;
+}
+
 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);
index 3e65acb0867efe900ad76f707db062e16e45a6e7..af575b5481651f79a66788cfdd1340aad6aa3bcd 100644 (file)
@@ -26,7 +26,7 @@
 #include <haproxy/quic_rx-t.h>
 
 int quic_dgram_parse(struct quic_dgram *dgram, struct quic_conn *from_qc,
-                     struct listener *li);
+                     enum obj_type *obj_type);
 int qc_treat_rx_pkts(struct quic_conn *qc);
 int qc_parse_hd_form(struct quic_rx_packet *pkt,
                      unsigned char **pos, const unsigned char *end);
index 2398cd8cbea016ef9dcd365e13e7e8b20671188c..b1b0a39abc141ebfde1f505cf81e5a12d7289029 100644 (file)
@@ -1877,8 +1877,8 @@ static struct quic_conn *quic_rx_pkt_retrieve_conn(struct quic_rx_packet *pkt,
 /* Parse a QUIC packet starting at <pos>. Data won't be read after <end> even
  * if the packet is incomplete. This function will populate fields of <pkt>
  * instance, most notably its length. <dgram> is the UDP datagram which
- * contains the parsed packet. <l> is the listener instance on which it was
- * received.
+ * contains the parsed packet. <o> is the address object type address of the
+ * object which receives this received packet.
  *
  * Returns 0 on success else non-zero. Packet length is guaranteed to be set to
  * the real packet value or to cover all data between <pos> and <end> : this is
@@ -1886,16 +1886,15 @@ static struct quic_conn *quic_rx_pkt_retrieve_conn(struct quic_rx_packet *pkt,
  */
 static int quic_rx_pkt_parse(struct quic_rx_packet *pkt,
                              unsigned char *pos, const unsigned char *end,
-                             struct quic_dgram *dgram, struct listener *l)
+                             struct quic_dgram *dgram, enum obj_type *o)
 {
        const unsigned char *beg = pos;
-       struct proxy *prx;
        struct quic_counters *prx_counters;
+       struct listener *l = objt_listener(o);
 
        TRACE_ENTER(QUIC_EV_CONN_LPKT);
 
-       prx = l->bind_conf->frontend;
-       prx_counters = EXTRA_COUNTERS_GET(prx->extra_counters_fe, &quic_stats_module);
+       prx_counters = qc_counters(o, &quic_stats_module);
 
        if (end <= pos) {
                TRACE_PROTO("Packet dropped", QUIC_EV_CONN_LPKT);
@@ -1951,8 +1950,10 @@ static int quic_rx_pkt_parse(struct quic_rx_packet *pkt,
                        goto drop;
                }
 
-               /* RFC9000 6. Version Negotiation */
-               if (!pkt->version) {
+               /* RFC9000 6. Version Negotiation. A Version Negotiation packet is
+                * sent only by servers.
+                */
+               if (l && !pkt->version) {
                         /* unsupported version, send Negotiation packet */
                        if (send_version_negotiation(l->rx.fd, &dgram->saddr, pkt)) {
                                TRACE_ERROR("VN packet not sent", QUIC_EV_CONN_LPKT);
@@ -1976,6 +1977,13 @@ static int quic_rx_pkt_parse(struct quic_rx_packet *pkt,
                                goto drop;
                        }
 
+                       if (!l && pkt->token_len) {
+                               /* A server must sent Initial packets with a null token length. */
+                               TRACE_PROTO("Packet dropped",
+                                           QUIC_EV_CONN_LPKT, NULL, NULL, NULL, pkt->version);
+                               goto drop;
+                       }
+
                        pkt->token = pos;
                        pkt->token_len = token_len;
                        pos += pkt->token_len;
@@ -2001,6 +2009,11 @@ static int quic_rx_pkt_parse(struct quic_rx_packet *pkt,
                pkt->pn_offset = pos - beg;
                pkt->len = pkt->pn_offset + len;
 
+               /* Interrupt parsing after packet length retrieval : this
+                * ensures that only the packet is dropped but not the whole
+                * datagram.
+                */
+
                /* RFC 9000. Initial Datagram Size
                 *
                 * A server MUST discard an Initial packet that is carried in a UDP datagram
@@ -2014,11 +2027,8 @@ static int quic_rx_pkt_parse(struct quic_rx_packet *pkt,
                        goto drop;
                }
 
-               /* Interrupt parsing after packet length retrieval : this
-                * ensures that only the packet is dropped but not the whole
-                * datagram.
-                */
-               if (pkt->type == QUIC_PACKET_TYPE_0RTT && !l->bind_conf->ssl_conf.early_data) {
+               /* O-RTT packet are not sent by servers. */
+               if (pkt->type == QUIC_PACKET_TYPE_0RTT && (!l || !l->bind_conf->ssl_conf.early_data)) {
                        TRACE_PROTO("RX 0-RTT packet not supported", QUIC_EV_CONN_LPKT);
                        goto drop;
                }
@@ -2242,7 +2252,8 @@ static void qc_rx_pkt_handle(struct quic_conn *qc, struct quic_rx_packet *pkt,
  * this function.
  *
  * If datagram has been received on a quic-conn owned FD, <from_qc> must be set
- * to the connection instance. <li> is the attached listener. The caller is
+ * to the connection instance. <o> is the object type address of the object
+ * (listener or server) receiving the datagram. The caller is
  * responsible to ensure that the first packet is destined to this connection
  * by comparing CIDs.
  *
@@ -2254,7 +2265,7 @@ static void qc_rx_pkt_handle(struct quic_conn *qc, struct quic_rx_packet *pkt,
  * the datagram may not have been parsed.
  */
 int quic_dgram_parse(struct quic_dgram *dgram, struct quic_conn *from_qc,
-                     struct listener *li)
+                     enum obj_type *o)
 {
        struct quic_rx_packet *pkt;
        struct quic_conn *qc = NULL;
@@ -2292,7 +2303,7 @@ int quic_dgram_parse(struct quic_dgram *dgram, struct quic_conn *from_qc,
                        pkt->flags |= QUIC_FL_RX_PACKET_DGRAM_FIRST;
 
                quic_rx_packet_refinc(pkt);
-               if (quic_rx_pkt_parse(pkt, pos, end, dgram, li))
+               if (quic_rx_pkt_parse(pkt, pos, end, dgram, o))
                        goto next;
 
                /* Search quic-conn instance for first packet of the datagram.
@@ -2301,6 +2312,7 @@ int quic_dgram_parse(struct quic_dgram *dgram, struct quic_conn *from_qc,
                 */
                if (!qc) {
                        int new_tid = -1;
+                       struct listener *li = objt_listener(o);
 
                        qc = from_qc ? from_qc : quic_rx_pkt_retrieve_conn(pkt, dgram, li, &new_tid);
                        /* qc is NULL if receiving a non Initial packet for an
index 6aabb24a18378103e0634bdba8a91a4e00ef834a..f3c6341ce3be4ceffcfbce99de58d436d0ca4278 100644 (file)
@@ -928,7 +928,7 @@ int qc_rcv_buf(struct quic_conn *qc)
                        continue;
                }
 
-               quic_dgram_parse(new_dgram, qc, qc->li);
+               quic_dgram_parse(new_dgram, qc, qc_owner_obj_type(qc));
                /* A datagram must always be consumed after quic_parse_dgram(). */
                BUG_ON(new_dgram->buf);
        } while (ret > 0);