MINOR: quic: RETIRE_CONNECTION_ID frame handling (RX)

Add ->curr_cid_seq_num new quic_conn struct frame to store the connection
ID sequence number currently used by the connection.
Implement qc_handle_retire_connection_id_frm() to handle this RX frame.
Implement qc_retire_connection_seq_num() to remove a connection ID from its
sequence number.
Implement qc_build_new_connection_id_frm to allocate a new NEW_CONNECTION_ID
frame from a CID.
Modify qc_parse_pkt_frms() which parses the frames of an RX packet to handle
the case of the RETIRE_CONNECTION_ID frame.

Must be backported to 2.7.
diff --git a/include/haproxy/quic_conn-t.h b/include/haproxy/quic_conn-t.h
index e302fb5..f5fc207 100644
--- a/include/haproxy/quic_conn-t.h
+++ b/include/haproxy/quic_conn-t.h
@@ -657,6 +657,7 @@
 	struct quic_cid scid; /* first SCID of our endpoint - not updated when a new SCID is used */
 	struct eb_root cids; /* tree of quic_connection_id - used to match a received packet DCID with a connection */
 	uint64_t next_cid_seq_num;
+	uint64_t curr_cid_seq_num;
 
 	struct quic_enc_level els[QUIC_TLS_ENC_LEVEL_MAX];
 	struct quic_pktns pktns[QUIC_TLS_PKTNS_MAX];
diff --git a/src/quic_conn.c b/src/quic_conn.c
index 80d80f2..91afd74 100644
--- a/src/quic_conn.c
+++ b/src/quic_conn.c
@@ -220,6 +220,8 @@
 DECLARE_POOL(pool_head_quic_frame, "quic_frame", sizeof(struct quic_frame));
 DECLARE_STATIC_POOL(pool_head_quic_arng, "quic_arng", sizeof(struct quic_arng_node));
 
+static struct quic_connection_id *new_quic_cid(struct eb_root *root,
+                                               struct quic_conn *qc);
 static struct quic_tx_packet *qc_build_pkt(unsigned char **pos, const unsigned char *buf_end,
                                            struct quic_enc_level *qel, struct quic_tls_ctx *ctx,
                                            struct list *frms, struct quic_conn *qc,
@@ -2948,6 +2950,88 @@
 	return ret;
 }
 
+
+/* Remove the connection ID from with <seq_num> as sequence number.
+ * Return 1 if a connection ID was effectively removed, 0 if not.
+ */
+static int qc_retire_connection_seq_num(struct quic_conn *qc, uint64_t seq_num)
+{
+	struct eb64_node *node;
+	struct quic_connection_id *cid;
+
+	TRACE_ENTER(QUIC_EV_CONN_PRSHPKT, qc);
+	node = eb64_lookup(&qc->cids, seq_num);
+	if (!node)
+		return 0;
+
+	cid = eb64_entry(node, struct quic_connection_id, seq_num);
+	ebmb_delete(&cid->node);
+	eb64_delete(&cid->seq_num);
+	pool_free(pool_head_quic_connection_id, cid);
+	TRACE_PROTO("CID retired", QUIC_EV_CONN_PSTRM, qc);
+
+	TRACE_LEAVE(QUIC_EV_CONN_PRSHPKT, qc);
+	return 1;
+}
+
+/* Allocate a new connection ID for <qc> connection and build a NEW_CONNECTION_ID
+ * frame to be sent.
+ * Return 1 if succeeded, 0 if not.
+ */
+static int qc_build_new_connection_id_frm(struct quic_conn *qc,
+                                          struct quic_connection_id *cid)
+{
+	int ret = 0;
+	struct quic_frame *frm;
+	struct quic_enc_level *qel;
+
+	TRACE_ENTER(QUIC_EV_CONN_PRSHPKT, qc);
+
+	qel = &qc->els[QUIC_TLS_ENC_LEVEL_APP];
+	frm = qc_frm_alloc(QUIC_FT_NEW_CONNECTION_ID);
+	if (!frm) {
+		TRACE_ERROR("frame allocation error", QUIC_EV_CONN_IO_CB, qc);
+		goto leave;
+	}
+
+	quic_connection_id_to_frm_cpy(frm, cid);
+	LIST_APPEND(&qel->pktns->tx.frms, &frm->list);
+	ret = 1;
+ leave:
+	TRACE_LEAVE(QUIC_EV_CONN_PRSHPKT, qc);
+	return ret;
+}
+
+
+/* Handle RETIRE_CONNECTION_ID frame from <frm> frame.
+ * Return 1 if succeeded, 0 if not.
+ */
+static int qc_handle_retire_connection_id_frm(struct quic_conn *qc,
+                                              struct quic_frame *frm)
+{
+	int ret = 0;
+	struct quic_retire_connection_id *rcid = &frm->retire_connection_id;
+
+	TRACE_ENTER(QUIC_EV_CONN_PRSHPKT, qc);
+
+	if (rcid->seq_num >= qc->next_cid_seq_num) {
+		TRACE_PROTO("CID seq. number too big", QUIC_EV_CONN_PSTRM, qc, frm);
+		quic_set_connection_close(qc, quic_err_transport(QC_ERR_PROTOCOL_VIOLATION));
+		goto leave;
+	}
+
+	if (rcid->seq_num == qc->curr_cid_seq_num) {
+		TRACE_PROTO("cannot retire the current CID", QUIC_EV_CONN_PSTRM, qc, frm);
+		quic_set_connection_close(qc, quic_err_transport(QC_ERR_PROTOCOL_VIOLATION));
+		goto leave;
+	}
+
+	ret = 1;
+ leave:
+	TRACE_LEAVE(QUIC_EV_CONN_PRSHPKT, qc);
+	return ret;
+}
+
 /* Parse all the frames of <pkt> QUIC packet for QUIC connection <qc> and <qel>
  * as encryption level.
  * Returns 1 if succeeded, 0 if failed.
@@ -3087,9 +3171,29 @@
 			HA_ATOMIC_INC(&qc->prx_counters->streams_data_blocked_uni);
 			break;
 		case QUIC_FT_NEW_CONNECTION_ID:
-		case QUIC_FT_RETIRE_CONNECTION_ID:
 			/* XXX TO DO XXX */
 			break;
+		case QUIC_FT_RETIRE_CONNECTION_ID:
+		{
+			struct quic_connection_id *cid;
+
+			if (!qc_handle_retire_connection_id_frm(qc, &frm))
+				goto leave;
+
+			if (!qc_retire_connection_seq_num(qc, frm.retire_connection_id.seq_num))
+				break;
+
+			cid = new_quic_cid(&qc->cids, qc);
+			if (!cid) {
+				TRACE_ERROR("CID allocation error", QUIC_EV_CONN_IO_CB, qc);
+			}
+			else {
+				/* insert the allocated CID in the receiver datagram handler tree */
+				ebmb_insert(&quic_dghdlrs[tid].cids, &cid->node, cid->cid.len);
+				qc_build_new_connection_id_frm(qc, cid);
+			}
+			break;
+		}
 		case QUIC_FT_CONNECTION_CLOSE:
 		case QUIC_FT_CONNECTION_CLOSE_APP:
 			/* Increment the error counters */
@@ -5190,6 +5294,8 @@
 		goto err;
 	}
 
+	/* The sequence number of the current CID is the same as for <icid>. */
+	qc->curr_cid_seq_num = icid->seq_num.key;
 	if ((global.tune.options & GTUNE_QUIC_SOCK_PER_CONN) &&
 	    is_addr(local_addr)) {
 		TRACE_USER("Allocate a socket for QUIC connection", QUIC_EV_CONN_INIT, qc);