MINOR: quic: Convert quic_dgram_read() into a task

quic_dgram_read() parses all the QUIC packets from a UDP datagram. It is the best
candidate to be converted into a task, because is processing data unit is the UDP
datagram received by the QUIC sock i/o handler. If correct, this datagram is
added to the context of a task, quic_lstnr_dghdlr(), a conversion of quic_dgram_read()
into a task. This task pop a datagram from an mt_list and passes it among to
the packet handler (quic_lstnr_pkt_rcv()).
Modify the quic_dgram struct to play the role of the old quic_dgram_ctx struct when
passed to quic_lstnr_pkt_rcv().
Modify the datagram handlers allocation to set their tasks to quic_lstnr_dghdlr().
diff --git a/include/haproxy/xprt_quic-t.h b/include/haproxy/xprt_quic-t.h
index 0c486af..3791160 100644
--- a/include/haproxy/xprt_quic-t.h
+++ b/include/haproxy/xprt_quic-t.h
@@ -422,10 +422,15 @@
 
 /* QUIC datagram */
 struct quic_dgram {
+	void *owner;
 	unsigned char *buf;
 	size_t len;
+	unsigned char *dcid;
+	size_t dcid_len;
 	struct sockaddr_storage saddr;
+	struct quic_conn *qc;
 	struct list list;
+	struct mt_list mt_list;
 };
 
 /* The QUIC packet numbers are 62-bits integers */
diff --git a/include/haproxy/xprt_quic.h b/include/haproxy/xprt_quic.h
index b76d6f3..22c7a24 100644
--- a/include/haproxy/xprt_quic.h
+++ b/include/haproxy/xprt_quic.h
@@ -1184,6 +1184,7 @@
 
 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);
+struct task *quic_lstnr_dghdlr(struct task *t, void *ctx, unsigned int state);
 int quic_lstnr_dgram_read(unsigned char *buf, size_t len, void *owner,
                           struct sockaddr_storage *saddr, struct list *dgrams);
 
diff --git a/src/proto_quic.c b/src/proto_quic.c
index aa44ce4..f7f9f7d 100644
--- a/src/proto_quic.c
+++ b/src/proto_quic.c
@@ -45,7 +45,7 @@
 #include <haproxy/quic_sock.h>
 #include <haproxy/sock_inet.h>
 #include <haproxy/tools.h>
-#include <haproxy/xprt_quic-t.h>
+#include <haproxy/xprt_quic.h>
 
 
 static void quic_add_listener(struct protocol *proto, struct listener *listener);
@@ -630,6 +630,7 @@
 
 		dghdlr->task->tid = i;
 		dghdlr->task->context = dghdlr;
+		dghdlr->task->process = quic_lstnr_dghdlr;
 		dghdlr->odcids = EB_ROOT_UNIQUE;
 		dghdlr->cids = EB_ROOT_UNIQUE;
 		MT_LIST_INIT(&dghdlr->dgrams);
diff --git a/src/xprt_quic.c b/src/xprt_quic.c
index 24c2153..95c1458 100644
--- a/src/xprt_quic.c
+++ b/src/xprt_quic.c
@@ -4191,9 +4191,8 @@
 }
 
 static ssize_t qc_lstnr_pkt_rcv(unsigned char *buf, const unsigned char *end,
-                                struct quic_rx_packet *pkt,
-                                struct quic_dgram_ctx *dgram_ctx,
-                                struct sockaddr_storage *saddr)
+                                struct quic_rx_packet *pkt, int first_pkt,
+                                struct quic_dgram *dgram)
 {
 	unsigned char *beg, *payload;
 	struct quic_conn *qc, *qc_to_purge = NULL;
@@ -4222,7 +4221,7 @@
 		goto err;
 	}
 
-	l = dgram_ctx->owner;
+	l = dgram->owner;
 	/* Header form */
 	qc_parse_hd_form(pkt, *buf++, &long_header);
 	if (long_header) {
@@ -4233,6 +4232,16 @@
 			goto err;
 		}
 
+		/* When multiple QUIC packets are coalesced on the same UDP datagram,
+		 * they must have the same DCID.
+		 */
+		if (!first_pkt &&
+		    (pkt->dcid.len != dgram->dcid_len ||
+		     memcmp(dgram->dcid, pkt->dcid.data, pkt->dcid.len))) {
+			TRACE_PROTO("Packet dropped", QUIC_EV_CONN_LPKT, qc);
+			goto err;
+		}
+
 		/* Retry of Version Negotiation packets are only sent by servers */
 		if (pkt->type == QUIC_PACKET_TYPE_RETRY || !pkt->version) {
 			TRACE_PROTO("Packet dropped", QUIC_EV_CONN_LPKT);
@@ -4242,7 +4251,7 @@
 		/* RFC9000 6. Version Negotiation */
 		if (!qc_pkt_is_supported_version(pkt)) {
 			 /* unsupported version, send Negotiation packet */
-			if (send_version_negotiation(l->rx.fd, saddr, pkt)) {
+			if (send_version_negotiation(l->rx.fd, &dgram->saddr, pkt)) {
 				TRACE_PROTO("Error on Version Negotiation sending", QUIC_EV_CONN_LPKT);
 				goto err;
 			}
@@ -4273,7 +4282,7 @@
 			 */
 			if (!token_len && l->bind_conf->quic_force_retry) {
 				TRACE_PROTO("Initial without token, sending retry", QUIC_EV_CONN_LPKT);
-				if (send_retry(l->rx.fd, saddr, pkt)) {
+				if (send_retry(l->rx.fd, &dgram->saddr, pkt)) {
 					TRACE_PROTO("Error during Retry generation", QUIC_EV_CONN_LPKT);
 					goto err;
 				}
@@ -4301,7 +4310,7 @@
 		payload = buf;
 		pkt->len = len + payload - beg;
 
-		qc = retrieve_qc_conn_from_cid(pkt, l, saddr);
+		qc = retrieve_qc_conn_from_cid(pkt, l, &dgram->saddr);
 		if (!qc) {
 			int ipv4;
 			struct quic_cid *odcid;
@@ -4314,8 +4323,8 @@
 				goto err;
 			}
 
-			pkt->saddr = *saddr;
-			ipv4 = saddr->ss_family == AF_INET;
+			pkt->saddr = dgram->saddr;
+			ipv4 = dgram->saddr.ss_family == AF_INET;
 			qc = qc_new_conn(pkt->version, ipv4,
 			                 pkt->dcid.data, pkt->dcid.len, pkt->dcid.addrlen,
 			                 pkt->scid.data, pkt->scid.len, 1, l);
@@ -4407,13 +4416,24 @@
 
 		memcpy(pkt->dcid.data, buf, QUIC_HAP_CID_LEN);
 		pkt->dcid.len = QUIC_HAP_CID_LEN;
+
+		/* When multiple QUIC packets are coalesced on the same UDP datagram,
+		 * they must have the same DCID.
+		 */
+		if (!first_pkt &&
+		    (pkt->dcid.len != dgram->dcid_len ||
+		     memcmp(dgram->dcid, pkt->dcid.data, pkt->dcid.len))) {
+			TRACE_PROTO("Packet dropped", QUIC_EV_CONN_LPKT, qc);
+			goto err;
+		}
+
 		buf += QUIC_HAP_CID_LEN;
 
 		/* A short packet is the last one of a UDP datagram. */
 		payload = buf;
 		pkt->len = end - beg;
 
-		qc = retrieve_qc_conn_from_cid(pkt, l, saddr);
+		qc = retrieve_qc_conn_from_cid(pkt, l, &dgram->saddr);
 		if (!qc) {
 			size_t pktlen = end - buf;
 			TRACE_PROTO("Packet dropped", QUIC_EV_CONN_LPKT, NULL, pkt, &pktlen);
@@ -4430,27 +4450,19 @@
 	 * This check must be done after the final update to pkt.len to
 	 * properly drop the packet on failure.
 	 */
-	if (!dgram_ctx->dcid.len) {
-		memcpy(dgram_ctx->dcid.data, pkt->dcid.data, pkt->dcid.len);
-		dgram_ctx->dcid.len = pkt->dcid.len;
-		if (!quic_peer_validated_addr(qc) &&
-		    HA_ATOMIC_LOAD(&qc->flags) & QUIC_FL_CONN_ANTI_AMPLIFICATION_REACHED) {
-			TRACE_PROTO("PTO timer must be armed after anti-amplication was reached",
-			            QUIC_EV_CONN_LPKT, qc);
-			/* Reset the anti-amplification bit. It will be set again
-			 * when sending the next packet if reached again.
-			 */
-			HA_ATOMIC_BTR(&qc->flags, QUIC_FL_CONN_ANTI_AMPLIFICATION_REACHED_BIT);
-			HA_ATOMIC_OR(&qc->flags, QUIC_FL_CONN_IO_CB_WAKEUP_BIT);
-			io_cb_wakeup = 1;
-		}
-	}
-	else if (memcmp(dgram_ctx->dcid.data, pkt->dcid.data, pkt->dcid.len)) {
-		TRACE_PROTO("Packet dropped", QUIC_EV_CONN_LPKT, qc);
-		goto err;
+	if (first_pkt && !quic_peer_validated_addr(qc) &&
+	    HA_ATOMIC_LOAD(&qc->flags) & QUIC_FL_CONN_ANTI_AMPLIFICATION_REACHED) {
+		TRACE_PROTO("PTO timer must be armed after anti-amplication was reached",
+					QUIC_EV_CONN_LPKT, qc);
+		/* Reset the anti-amplification bit. It will be set again
+		 * when sending the next packet if reached again.
+		 */
+		HA_ATOMIC_BTR(&qc->flags, QUIC_FL_CONN_ANTI_AMPLIFICATION_REACHED_BIT);
+		HA_ATOMIC_OR(&qc->flags, QUIC_FL_CONN_IO_CB_WAKEUP_BIT);
+		io_cb_wakeup = 1;
 	}
-	dgram_ctx->qc = qc;
 
+	dgram->qc = qc;
 
 	if (HA_ATOMIC_LOAD(&qc->err_code)) {
 		TRACE_PROTO("Connection error", QUIC_EV_CONN_LPKT, qc);
@@ -5360,19 +5372,20 @@
  * as owner calling <func> function.
  * Return the number of bytes read if succeeded, -1 if not.
  */
-static ssize_t quic_dgram_read(unsigned char *buf, size_t len, void *owner,
-                               struct sockaddr_storage *saddr, qpkt_read_func *func)
+struct task *quic_lstnr_dghdlr(struct task *t, void *ctx, unsigned int state)
 {
 	unsigned char *pos;
 	const unsigned char *end;
-	struct quic_dgram_ctx dgram_ctx = {
-		.qc = NULL,
-		.dcid.len = 0,
-		.owner = owner,
-	};
+	struct quic_dghdlr *dghdlr = ctx;
+	struct quic_dgram *dgram;
+	int first_pkt = 1;
 
-	pos = buf;
-	end = pos + len;
+	dgram = MT_LIST_POP(&dghdlr->dgrams, typeof(dgram), mt_list);
+	if (!dgram)
+		goto err;
+
+	pos = dgram->buf;
+	end = pos + dgram->len;
 	do {
 		int ret;
 		struct quic_rx_packet *pkt;
@@ -5382,7 +5395,8 @@
 			goto err;
 
 		quic_rx_packet_refinc(pkt);
-		ret = func(pos, end, pkt, &dgram_ctx, saddr);
+		ret = qc_lstnr_pkt_rcv(pos, end, pkt, first_pkt, dgram);
+		first_pkt = 0;
 		pos += pkt->len;
 		quic_rx_packet_refdec(pkt);
 		if (ret == -1)
@@ -5393,13 +5407,13 @@
 	/* Increasing the received bytes counter by the UDP datagram length
 	 * if this datagram could be associated to a connection.
 	 */
-	if (dgram_ctx.qc)
-		dgram_ctx.qc->rx.bytes += len;
+	if (dgram->qc)
+		dgram->qc->rx.bytes += dgram->len;
 
-	return pos - buf;
+	return t;
 
  err:
-	return -1;
+	return t;
 }
 
 /* Retreive the DCID from a QUIC datagram or packet with <buf> as first octet.
@@ -5441,22 +5455,33 @@
 	struct quic_dgram *dgram;
 	unsigned char *dcid;
 	size_t dcid_len;
+	int tid;
+	struct listener *l = owner;
 
 	if (!len || !quic_get_dgram_dcid(buf, buf + len, &dcid, &dcid_len))
-		goto out;
+		goto err;
 
 	dgram = pool_alloc(pool_head_quic_dgram);
 	if (!dgram)
-		goto out;
+		goto err;
 
+	tid = quic_get_cid_tid(dcid);
+	/* All the members must be initialized! */
+	dgram->owner = owner;
 	dgram->buf = buf;
 	dgram->len = len;
+	dgram->dcid = dcid;
+	dgram->dcid_len = dcid_len;
 	dgram->saddr = *saddr;
+	dgram->qc = NULL;
 	LIST_APPEND(dgrams, &dgram->list);
+	MT_LIST_APPEND(&l->rx.dghdlrs[tid]->dgrams, &dgram->mt_list);
 
-	return quic_dgram_read(buf, len, owner, saddr, qc_lstnr_pkt_rcv);
+	tasklet_wakeup(l->rx.dghdlrs[tid]->task);
 
- out:
+	return 1;
+
+ err:
 	return 0;
 }