MEDIUM: quic: handle CIDs to rattach received packets to connection
Change the way the CIDs are organized to rattach received packets DCID
to QUIC connection. This is necessary to be able to handle multiple DCID
to one connection.
For this, the quic_connection_id structure has been extended. When
allocated, they are inserted in the receiver CID tree instead of the
quic_conn directly. When receiving a packet, the receiver tree is
inspected to retrieve the quic_connection_id. The quic_connection_id
contains now contains a reference to the QUIC connection.
diff --git a/include/haproxy/xprt_quic-t.h b/include/haproxy/xprt_quic-t.h
index ee7b19d..91188c3 100644
--- a/include/haproxy/xprt_quic-t.h
+++ b/include/haproxy/xprt_quic-t.h
@@ -257,12 +257,20 @@
unsigned char len;
};
-/* The data structure used to build a set of connection IDs for each connection. */
+/* QUIC connection id attached to a QUIC connection.
+ *
+ * This structure is used to match received packets DCIDs with the
+ * corresponding QUIC connection.
+ */
struct quic_connection_id {
struct eb64_node seq_num;
uint64_t retire_prior_to;
- struct quic_cid cid;
unsigned char stateless_reset_token[QUIC_STATELESS_RESET_TOKEN_LEN];
+
+ struct ebmb_node node; /* node for receiver tree, cid.data as key */
+ struct quic_cid cid; /* CID data */
+
+ struct quic_conn *qc; /* QUIC connection using this CID */
};
struct preferred_address {
@@ -629,7 +637,7 @@
struct quic_cid odcid;
struct quic_cid dcid; /* DCID of our endpoint - not updated whan a new DCID is used */
- struct ebmb_node scid_node;
+ struct ebmb_node scid_node; /* used only for client side (backend) */
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 */
diff --git a/include/haproxy/xprt_quic.h b/include/haproxy/xprt_quic.h
index 2b59c37..018b304 100644
--- a/include/haproxy/xprt_quic.h
+++ b/include/haproxy/xprt_quic.h
@@ -29,6 +29,7 @@
#include <stdint.h>
#include <import/eb64tree.h>
+#include <import/ebmbtree.h>
#include <haproxy/buf.h>
#include <haproxy/chunk.h>
@@ -136,6 +137,13 @@
struct quic_connection_id *cid;
cid = eb64_entry(&node->node, struct quic_connection_id, seq_num);
+
+ /* remove the CID from the receiver tree */
+ HA_RWLOCK_WRLOCK(QUIC_LOCK, &conn->li->rx.cids_lock);
+ ebmb_delete(&cid->node);
+ HA_RWLOCK_WRUNLOCK(QUIC_LOCK, &conn->li->rx.cids_lock);
+
+ /* remove the CID from the quic_conn tree */
node = eb64_next(node);
eb64_delete(&cid->seq_num);
pool_free(pool_head_quic_connection_id, cid);
@@ -163,6 +171,7 @@
* Returns the new CID if succeeded, NULL if not.
*/
static inline struct quic_connection_id *new_quic_cid(struct eb_root *root,
+ struct quic_conn *qc,
int seq_num)
{
struct quic_connection_id *cid;
@@ -179,6 +188,8 @@
goto err;
}
+ cid->qc = qc;
+
cid->seq_num.key = seq_num;
cid->retire_prior_to = 0;
/* insert the allocated CID in the quic_conn tree */
diff --git a/src/xprt_quic.c b/src/xprt_quic.c
index c597405..c2d753d 100644
--- a/src/xprt_quic.c
+++ b/src/xprt_quic.c
@@ -2439,12 +2439,21 @@
for (i = 1; i < qc->tx.params.active_connection_id_limit; i++) {
struct quic_connection_id *cid;
+ struct listener *l = __objt_listener(qc->conn->target);
frm = pool_alloc(pool_head_quic_frame);
- cid = new_quic_cid(&qc->cids, i);
- if (!frm || !cid)
+ if (!frm)
goto err;
+ cid = new_quic_cid(&qc->cids, qc, i);
+ if (!cid)
+ goto err;
+
+ /* insert the allocated CID in the receiver tree */
+ HA_RWLOCK_WRLOCK(QUIC_LOCK, &l->rx.cids_lock);
+ ebmb_insert(&l->rx.cids, &cid->node, cid->cid.len);
+ HA_RWLOCK_WRUNLOCK(QUIC_LOCK, &l->rx.cids_lock);
+
quic_connection_id_to_frm_cpy(frm, cid);
MT_LIST_APPEND(&qel->pktns->tx.frms, &frm->mt_list);
}
@@ -2970,6 +2979,7 @@
HA_RWLOCK_WRLOCK(QUIC_LOCK, &conn->li->rx.cids_lock);
ebmb_delete(&conn->odcid_node);
ebmb_delete(&conn->scid_node);
+
HA_RWLOCK_WRUNLOCK(QUIC_LOCK, &conn->li->rx.cids_lock);
for (i = 0; i < QUIC_TLS_ENC_LEVEL_MAX; i++)
@@ -3051,6 +3061,7 @@
/* Initial CID. */
struct quic_connection_id *icid;
char *buf_area;
+ struct listener *l = NULL;
TRACE_ENTER(QUIC_EV_CONN_INIT);
qc = pool_zalloc(pool_head_quic_conn);
@@ -3068,7 +3079,7 @@
qc->cids = EB_ROOT;
/* QUIC Server (or listener). */
if (server) {
- struct listener *l = owner;
+ l = owner;
HA_ATOMIC_STORE(&qc->state, QUIC_HS_ST_SERVER_INITIAL);
/* Copy the initial DCID. */
@@ -3094,12 +3105,19 @@
/* Initialize the output buffer */
qc->obuf.pos = qc->obuf.data;
- icid = new_quic_cid(&qc->cids, 0);
+ icid = new_quic_cid(&qc->cids, qc, 0);
if (!icid) {
TRACE_PROTO("Could not allocate a new connection ID", QUIC_EV_CONN_INIT);
goto err;
}
+ /* insert the allocated CID in the receiver tree */
+ if (server) {
+ HA_RWLOCK_WRLOCK(QUIC_LOCK, &l->rx.cids_lock);
+ ebmb_insert(&l->rx.cids, &icid->node, icid->cid.len);
+ HA_RWLOCK_WRUNLOCK(QUIC_LOCK, &l->rx.cids_lock);
+ }
+
/* Select our SCID which is the first CID with 0 as sequence number. */
qc->scid = icid->cid;
@@ -3765,10 +3783,6 @@
HA_RWLOCK_WRLOCK(QUIC_LOCK, &l->rx.cids_lock);
/* Insert the DCID the QUIC client has chosen (only for listeners) */
n = ebmb_insert(&l->rx.odcids, &qc->odcid_node, qc->odcid.len);
- if (n == &qc->odcid_node) {
- /* Insert our SCID, the connection ID for the QUIC client. */
- ebmb_insert(&l->rx.cids, &qc->scid_node, qc->scid.len);
- }
HA_RWLOCK_WRUNLOCK(QUIC_LOCK, &l->rx.cids_lock);
/* If the insertion failed, it means that another
@@ -3792,15 +3806,20 @@
node = &qc->odcid_node;
}
else {
- if (pkt->type == QUIC_PACKET_TYPE_INITIAL && cids == &l->rx.odcids)
+ if (pkt->type == QUIC_PACKET_TYPE_INITIAL && cids == &l->rx.odcids) {
qc = ebmb_entry(node, struct quic_conn, odcid_node);
- else
- qc = ebmb_entry(node, struct quic_conn, scid_node);
+ }
+ else {
+ struct quic_connection_id *cid = ebmb_entry(node, struct quic_connection_id, node);
+ qc = cid->qc;
+ }
pkt->qc = qc;
conn_ctx = qc->conn->xprt_ctx;
}
}
else {
+ struct quic_connection_id *cid;
+
if (end - *buf < QUIC_CID_LEN) {
TRACE_PROTO("Packet dropped", QUIC_EV_CONN_LPKT);
goto err;
@@ -3813,7 +3832,8 @@
goto err;
}
- qc = ebmb_entry(node, struct quic_conn, scid_node);
+ cid = ebmb_entry(node, struct quic_connection_id, node);
+ qc = cid->qc;
conn_ctx = qc->conn->xprt_ctx;
*buf += QUIC_CID_LEN;
pkt->qc = qc;