MINOR: quic: Send stateless reset tokens

Add send_stateless_reset() to send a stateless reset packet. It prepares
a packet to build a 1-RTT packet with quic_stateless_reset_token_cpy()
to copy a stateless reset token derived from the cluster secret with
the destination connection ID received as salt.
Also add QUIC_EV_STATELESS_RST new trace event to at least to have a trace
of the connection which are reset.
diff --git a/include/haproxy/xprt_quic-t.h b/include/haproxy/xprt_quic-t.h
index 4db01fc..86cbe0e 100644
--- a/include/haproxy/xprt_quic-t.h
+++ b/include/haproxy/xprt_quic-t.h
@@ -183,6 +183,8 @@
 #define QUIC_CONN_MAX_PACKET  64
 
 #define QUIC_STATELESS_RESET_TOKEN_LEN 16
+#define QUIC_STATELESS_RESET_PACKET_HEADER_LEN 5
+#define QUIC_STATELESS_RESET_PACKET_MINLEN     (22 + QUIC_HAP_CID_LEN)
 
 #define           QUIC_EV_CONN_NEW       (1ULL << 0)
 #define           QUIC_EV_CONN_INIT      (1ULL << 1)
@@ -227,6 +229,7 @@
 #define           QUIC_EV_CONN_CLOSE     (1ULL << 40)
 #define           QUIC_EV_CONN_ACKSTRM   (1ULL << 41)
 #define           QUIC_EV_CONN_FRMLIST   (1ULL << 42)
+#define           QUIC_EV_STATELESS_RST  (1ULL << 43)
 
 /* Similar to kernel min()/max() definitions. */
 #define QUIC_MIN(a, b) ({ \
diff --git a/src/xprt_quic.c b/src/xprt_quic.c
index a5bfa4a..fef93cc 100644
--- a/src/xprt_quic.c
+++ b/src/xprt_quic.c
@@ -121,6 +121,7 @@
 	{ .mask = QUIC_EV_CONN_CLOSE,    .name = "conn_close",       .desc = "closing conn." },
 	{ .mask = QUIC_EV_CONN_ACKSTRM,  .name = "ack_strm",         .desc = "STREAM ack."},
 	{ .mask = QUIC_EV_CONN_FRMLIST,  .name = "frm_list",         .desc = "frame list"},
+	{ .mask = QUIC_EV_STATELESS_RST, .name = "stateless_reset",  .desc = "stateless reset sent"},
 	{ /* end */ }
 };
 
@@ -611,6 +612,13 @@
 			chunk_appendf(&trace_buf, " len=%llu", (ull)*len);
 	}
 
+	if (mask & QUIC_EV_STATELESS_RST) {
+		const struct quic_cid *cid = a2;
+
+		if (cid)
+			quic_cid_dump(&trace_buf, cid);
+	}
+
 }
 
 /* Returns 1 if the peer has validated <qc> QUIC connection address, 0 if not. */
@@ -4869,6 +4877,51 @@
 	return 0;
 }
 
+/* Send a stateless reset packet depending on <pkt> RX packet information
+ * from <fd> UDP socket to <dst>
+ * Return 1 if succeeded, 0 if not.
+ */
+static int send_stateless_reset(int fd, struct sockaddr_storage *dstaddr,
+                                struct quic_rx_packet *rxpkt)
+{
+	int pktlen, rndlen;
+	unsigned char pkt[64];
+	const socklen_t addrlen = get_addr_len(dstaddr);
+
+	/* 10.3 Stateless Reset (https://www.rfc-editor.org/rfc/rfc9000.html#section-10.3)
+	 * The resulting minimum size of 21 bytes does not guarantee that a Stateless
+	 * Reset is difficult to distinguish from other packets if the recipient requires
+	 * the use of a connection ID. To achieve that end, the endpoint SHOULD ensure
+	 * that all packets it sends are at least 22 bytes longer than the minimum
+	 * connection ID length that it requests the peer to include in its packets,
+	 * adding PADDING frames as necessary. This ensures that any Stateless Reset
+	 * sent by the peer is indistinguishable from a valid packet sent to the endpoint.
+	 * An endpoint that sends a Stateless Reset in response to a packet that is
+	 * 43 bytes or shorter SHOULD send a Stateless Reset that is one byte shorter
+	 * than the packet it responds to.
+	 */
+
+	/* Note that we build at most a 42 bytes QUIC packet to mimic a short packet */
+	pktlen = rxpkt->len <= 43 ? rxpkt->len - 1 : 0;
+	pktlen = QUIC_MAX(QUIC_STATELESS_RESET_PACKET_MINLEN, pktlen);
+	rndlen = pktlen - QUIC_STATELESS_RESET_TOKEN_LEN;
+	/* Put a header of random bytes */
+	if (RAND_bytes(pkt, rndlen) != 1)
+		return 0;
+
+	/* Clear the most significant bit, and set the second one */
+	*pkt = (*pkt & ~0x80) | 0x40;
+	if (!quic_stateless_reset_token_cpy(pkt + rndlen, QUIC_STATELESS_RESET_TOKEN_LEN,
+	                                    rxpkt->dcid.data, rxpkt->dcid.len))
+	    return 0;
+
+	if (sendto(fd, pkt, pktlen, 0, (struct sockaddr *)dstaddr, addrlen) < 0)
+		return 0;
+
+	TRACE_PROTO("stateless reset sent", QUIC_EV_STATELESS_RST, NULL, &rxpkt->dcid);
+	return 1;
+}
+
 /* Generate the token to be used in Retry packets. The token is written to
  * <buf> which is expected to be <len> bytes.
  *
@@ -5360,6 +5413,8 @@
 		if (!qc) {
 			size_t pktlen = end - buf;
 			TRACE_PROTO("Packet dropped", QUIC_EV_CONN_LPKT, NULL, pkt, &pktlen);
+			if (global.cluster_secret && !send_stateless_reset(l->rx.fd, &dgram->saddr, pkt))
+				TRACE_PROTO("stateless reset not sent", QUIC_EV_CONN_LPKT, qc);
 			goto err;
 		}