From 0fc0d45745e2a904647a00a7e8409b3230af82af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20L=C3=A9caille?= Date: Thu, 23 Nov 2023 19:21:03 +0100 Subject: [PATCH] REORG: quic: Add a new module to handle QUIC connection IDs Move quic_cid and quic_connnection_id from quic_conn-t.h to new quic_cid-t.h header. Move defintions of quic_stateless_reset_token_init(), quic_derive_cid(), new_quic_cid(), quic_get_cid_tid() and retrieve_qc_conn_from_cid() to quic_cid.c new C file. --- Makefile | 2 +- include/haproxy/quic_cid-t.h | 37 +++++ include/haproxy/quic_cid.h | 94 ++++++++++++ include/haproxy/quic_conn-t.h | 31 +--- include/haproxy/quic_conn.h | 63 +------- src/quic_cid.c | 261 ++++++++++++++++++++++++++++++++++ src/quic_conn.c | 193 ------------------------- src/quic_rx.c | 58 +------- src/quic_sock.c | 1 + src/quic_tx.c | 1 + 10 files changed, 398 insertions(+), 343 deletions(-) create mode 100644 include/haproxy/quic_cid-t.h create mode 100644 include/haproxy/quic_cid.h create mode 100644 src/quic_cid.c diff --git a/Makefile b/Makefile index 03f7e7165..48734b98f 100644 --- a/Makefile +++ b/Makefile @@ -620,7 +620,7 @@ OPTIONS_OBJS += src/quic_conn.o src/mux_quic.o src/h3.o src/xprt_quic.o \ src/h3_stats.o src/qmux_http.o src/cfgparse-quic.o \ src/cbuf.o src/quic_cc.o src/quic_cc_nocc.o src/quic_ack.o \ src/quic_trace.o src/quic_cli.o src/quic_ssl.o \ - src/quic_rx.o src/quic_tx.o + src/quic_rx.o src/quic_tx.o src/quic_cid.o endif ifneq ($(USE_QUIC_OPENSSL_COMPAT),) diff --git a/include/haproxy/quic_cid-t.h b/include/haproxy/quic_cid-t.h new file mode 100644 index 000000000..d355baeae --- /dev/null +++ b/include/haproxy/quic_cid-t.h @@ -0,0 +1,37 @@ +#ifndef _HAPROXY_QUIC_CID_T_H +#define _HAPROXY_QUIC_CID_T_H + +#include + +/* QUIC connection ID maximum length for version 1. */ +#define QUIC_CID_MAXLEN 20 /* bytes */ + +/* QUIC connection id data. + * + * This struct is used by ebmb_node structs as last member of flexible arrays. + * So do not change the order of the member of quic_cid struct. + * member must be the first one. + */ +struct quic_cid { + unsigned char data[QUIC_CID_MAXLEN]; + unsigned char len; /* size of QUIC CID */ +}; + +/* 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; + 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 */ + uint tid; /* Attached Thread ID for the connection. */ +}; + +#endif /* _HAPROXY_QUIC_CID_T_H */ diff --git a/include/haproxy/quic_cid.h b/include/haproxy/quic_cid.h new file mode 100644 index 000000000..9e02e5b53 --- /dev/null +++ b/include/haproxy/quic_cid.h @@ -0,0 +1,94 @@ +#ifndef _HAPROXY_QUIC_CID_H +#define _HAPROXY_QUIC_CID_H + +#ifdef USE_QUIC +#ifndef USE_OPENSSL +#error "Must define USE_OPENSSL" +#endif + +#include + +#include +#include +#include +#include +#include + +struct quic_connection_id *new_quic_cid(struct eb_root *root, + struct quic_conn *qc, + const struct quic_cid *orig, + const struct sockaddr_storage *addr); +int quic_get_cid_tid(const unsigned char *cid, size_t cid_len, + const struct sockaddr_storage *cli_addr, + unsigned char *pos, size_t len); +struct quic_cid quic_derive_cid(const struct quic_cid *orig, + const struct sockaddr_storage *addr); +struct quic_conn *retrieve_qc_conn_from_cid(struct quic_rx_packet *pkt, + struct listener *l, + struct sockaddr_storage *saddr, + int *new_tid); + +/* Copy QUIC CID to . + * This is the responsibility of the caller to check there is enough room in + * to copy . + * Always succeeds. + */ +static inline void quic_cid_cpy(struct quic_cid *dst, const struct quic_cid *src) +{ + memcpy(dst->data, src->data, src->len); + dst->len = src->len; +} + +/* Dump the QUIC connection ID value if present (non null length). Used only for + * debugging purposes. + * Always succeeds. + */ +static inline void quic_cid_dump(struct buffer *buf, + const struct quic_cid *cid) +{ + int i; + + chunk_appendf(buf, "(%d", cid->len); + if (cid->len) + chunk_appendf(buf, ","); + for (i = 0; i < cid->len; i++) + chunk_appendf(buf, "%02x", cid->data[i]); + chunk_appendf(buf, ")"); +} + +/* Return tree index where is stored. */ +static inline uchar _quic_cid_tree_idx(const unsigned char *cid) +{ + return cid[0]; +} + +/* Return tree index where is stored. */ +static inline uchar quic_cid_tree_idx(const struct quic_cid *cid) +{ + return _quic_cid_tree_idx(cid->data); +} + +/* Insert into global CID tree as a thread-safe operation. */ +static inline void quic_cid_insert(struct quic_connection_id *conn_id) +{ + const uchar idx = quic_cid_tree_idx(&conn_id->cid); + struct quic_cid_tree *tree = &quic_cid_trees[idx]; + + HA_RWLOCK_WRLOCK(QC_CID_LOCK, &tree->lock); + ebmb_insert(&tree->root, &conn_id->node, conn_id->cid.len); + HA_RWLOCK_WRUNLOCK(QC_CID_LOCK, &tree->lock); +} + +/* Remove from global CID tree as a thread-safe operation. */ +static inline void quic_cid_delete(struct quic_connection_id *conn_id) +{ + const uchar idx = quic_cid_tree_idx(&conn_id->cid); + struct quic_cid_tree __maybe_unused *tree = &quic_cid_trees[idx]; + + HA_RWLOCK_WRLOCK(QC_CID_LOCK, &tree->lock); + ebmb_delete(&conn_id->node); + HA_RWLOCK_WRUNLOCK(QC_CID_LOCK, &tree->lock); +} + +#endif /* USE_QUIC */ +#endif /* _HAPROXY_QUIC_CID_H */ diff --git a/include/haproxy/quic_conn-t.h b/include/haproxy/quic_conn-t.h index 86d80a7b5..edf2e304e 100644 --- a/include/haproxy/quic_conn-t.h +++ b/include/haproxy/quic_conn-t.h @@ -33,6 +33,7 @@ #include #include +#include #include #include #include @@ -61,8 +62,6 @@ typedef unsigned long long ull; #define QUIC_HAP_CID_LEN 8 /* Common definitions for short and long QUIC packet headers. */ -/* QUIC connection ID maximum length for version 1. */ -#define QUIC_CID_MAXLEN 20 /* bytes */ /* QUIC original destination connection ID minial length */ #define QUIC_ODCID_MINLEN 8 /* bytes */ /* @@ -219,34 +218,6 @@ extern const struct quic_version quic_versions[]; extern const size_t quic_versions_nb; extern const struct quic_version *preferred_version; -/* QUIC connection id data. - * - * This struct is used by ebmb_node structs as last member of flexible arrays. - * So do not change the order of the member of quic_cid struct. - * member must be the first one. - */ -struct quic_cid { - unsigned char data[QUIC_CID_MAXLEN]; - unsigned char len; /* size of QUIC CID */ -}; - -/* 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; - 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 */ - uint tid; /* Attached Thread ID for the connection. */ -}; - /* unused: 0x01 */ /* Flag the packet number space as requiring an ACK frame to be sent. */ #define QUIC_FL_PKTNS_ACK_REQUIRED (1UL << 1) diff --git a/include/haproxy/quic_conn.h b/include/haproxy/quic_conn.h index 95f871b97..ec8d4b24e 100644 --- a/include/haproxy/quic_conn.h +++ b/include/haproxy/quic_conn.h @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -109,17 +110,6 @@ static inline int qc_is_listener(struct quic_conn *qc) return qc->flags & QUIC_FL_CONN_LISTENER; } -/* Copy QUIC CID to . - * This is the responsibility of the caller to check there is enough room in - * to copy . - * Always succeeds. - */ -static inline void quic_cid_cpy(struct quic_cid *dst, const struct quic_cid *src) -{ - memcpy(dst->data, src->data, src->len); - dst->len = src->len; -} - /* Copy socket address data into buffer. * This is the responsibility of the caller to check the output buffer is big * enough to contain these socket address data. @@ -153,57 +143,6 @@ static inline size_t quic_saddr_cpy(unsigned char *buf, return p - buf; } -/* Dump the QUIC connection ID value if present (non null length). Used only for - * debugging purposes. - * Always succeeds. - */ -static inline void quic_cid_dump(struct buffer *buf, - const struct quic_cid *cid) -{ - int i; - - chunk_appendf(buf, "(%d", cid->len); - if (cid->len) - chunk_appendf(buf, ","); - for (i = 0; i < cid->len; i++) - chunk_appendf(buf, "%02x", cid->data[i]); - chunk_appendf(buf, ")"); -} - -/* Return tree index where is stored. */ -static inline uchar _quic_cid_tree_idx(const unsigned char *cid) -{ - return cid[0]; -} - -/* Return tree index where is stored. */ -static inline uchar quic_cid_tree_idx(const struct quic_cid *cid) -{ - return _quic_cid_tree_idx(cid->data); -} - -/* Insert into global CID tree as a thread-safe operation. */ -static inline void quic_cid_insert(struct quic_connection_id *conn_id) -{ - const uchar idx = quic_cid_tree_idx(&conn_id->cid); - struct quic_cid_tree *tree = &quic_cid_trees[idx]; - - HA_RWLOCK_WRLOCK(QC_CID_LOCK, &tree->lock); - ebmb_insert(&tree->root, &conn_id->node, conn_id->cid.len); - HA_RWLOCK_WRUNLOCK(QC_CID_LOCK, &tree->lock); -} - -/* Remove from global CID tree as a thread-safe operation. */ -static inline void quic_cid_delete(struct quic_connection_id *conn_id) -{ - const uchar idx = quic_cid_tree_idx(&conn_id->cid); - struct quic_cid_tree __maybe_unused *tree = &quic_cid_trees[idx]; - - HA_RWLOCK_WRLOCK(QC_CID_LOCK, &tree->lock); - ebmb_delete(&conn_id->node); - HA_RWLOCK_WRUNLOCK(QC_CID_LOCK, &tree->lock); -} - /* Free the CIDs attached to QUIC connection. */ static inline void free_quic_conn_cids(struct quic_conn *conn) { diff --git a/src/quic_cid.c b/src/quic_cid.c new file mode 100644 index 000000000..bef90f693 --- /dev/null +++ b/src/quic_cid.c @@ -0,0 +1,261 @@ +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +/* Initialize the stateless reset token attached to connection ID. + * Returns 1 if succeeded, 0 if not. + */ +static int quic_stateless_reset_token_init(struct quic_connection_id *conn_id) +{ + /* Output secret */ + unsigned char *token = conn_id->stateless_reset_token; + size_t tokenlen = sizeof conn_id->stateless_reset_token; + /* Salt */ + const unsigned char *cid = conn_id->cid.data; + size_t cidlen = conn_id->cid.len; + + return quic_stateless_reset_token_cpy(token, tokenlen, cid, cidlen); +} + +/* Generate a CID directly derived from CID and address. + * + * Returns the derived CID. + */ +struct quic_cid quic_derive_cid(const struct quic_cid *orig, + const struct sockaddr_storage *addr) +{ + struct quic_cid cid; + const struct sockaddr_in *in; + const struct sockaddr_in6 *in6; + char *pos = trash.area; + size_t idx = 0; + uint64_t hash; + int i; + + /* Prepare buffer for hash using original CID first. */ + memcpy(pos, orig->data, orig->len); + idx += orig->len; + + /* Concatenate client address. */ + switch (addr->ss_family) { + case AF_INET: + in = (struct sockaddr_in *)addr; + + memcpy(&pos[idx], &in->sin_addr, sizeof(in->sin_addr)); + idx += sizeof(in->sin_addr); + memcpy(&pos[idx], &in->sin_port, sizeof(in->sin_port)); + idx += sizeof(in->sin_port); + break; + + case AF_INET6: + in6 = (struct sockaddr_in6 *)addr; + + memcpy(&pos[idx], &in6->sin6_addr, sizeof(in6->sin6_addr)); + idx += sizeof(in6->sin6_addr); + memcpy(&pos[idx], &in6->sin6_port, sizeof(in6->sin6_port)); + idx += sizeof(in6->sin6_port); + break; + + default: + /* TODO to implement */ + ABORT_NOW(); + } + + /* Avoid similar values between multiple haproxy process. */ + memcpy(&pos[idx], boot_seed, sizeof(boot_seed)); + idx += sizeof(boot_seed); + + /* Hash the final buffer content. */ + hash = XXH64(pos, idx, 0); + + for (i = 0; i < sizeof(hash); ++i) + cid.data[i] = hash >> ((sizeof(hash) * 7) - (8 * i)); + cid.len = sizeof(hash); + + return cid; +} + +/* Allocate a new CID and attach it to ebtree. + * + * If and params are non null, the new CID value is directly + * derived from them. Else a random value is generated. The CID is then marked + * with the current thread ID. + * + * Returns the new CID if succeeded, NULL if not. + */ +struct quic_connection_id *new_quic_cid(struct eb_root *root, + struct quic_conn *qc, + const struct quic_cid *orig, + const struct sockaddr_storage *addr) +{ + struct quic_connection_id *conn_id; + + TRACE_ENTER(QUIC_EV_CONN_TXPKT, qc); + + /* Caller must set either none or both values. */ + BUG_ON(!!orig != !!addr); + + conn_id = pool_alloc(pool_head_quic_connection_id); + if (!conn_id) { + TRACE_ERROR("cid allocation failed", QUIC_EV_CONN_TXPKT, qc); + goto err; + } + + conn_id->cid.len = QUIC_HAP_CID_LEN; + + if (!orig) { + if (quic_newcid_from_hash64) + quic_newcid_from_hash64(conn_id->cid.data, conn_id->cid.len, qc->hash64, + global.cluster_secret, sizeof(global.cluster_secret)); + else if (RAND_bytes(conn_id->cid.data, conn_id->cid.len) != 1) { + /* TODO: RAND_bytes() should be replaced */ + TRACE_ERROR("RAND_bytes() failed", QUIC_EV_CONN_TXPKT, qc); + goto err; + } + } + else { + /* Derive the new CID value from original CID. */ + conn_id->cid = quic_derive_cid(orig, addr); + } + + if (quic_stateless_reset_token_init(conn_id) != 1) { + TRACE_ERROR("quic_stateless_reset_token_init() failed", QUIC_EV_CONN_TXPKT, qc); + goto err; + } + + conn_id->qc = qc; + HA_ATOMIC_STORE(&conn_id->tid, tid); + + conn_id->seq_num.key = qc ? qc->next_cid_seq_num++ : 0; + conn_id->retire_prior_to = 0; + /* insert the allocated CID in the quic_conn tree */ + if (root) + eb64_insert(root, &conn_id->seq_num); + + TRACE_LEAVE(QUIC_EV_CONN_TXPKT, qc); + return conn_id; + + err: + pool_free(pool_head_quic_connection_id, conn_id); + TRACE_LEAVE(QUIC_EV_CONN_TXPKT, qc); + return NULL; +} + +/* Retrieve the thread ID associated to QUIC connection ID of length + * . CID may be not found on the CID tree because it is an ODCID. In + * this case, it will derived using client address as hash + * parameter. However, this is done only if points to an INITIAL or 0RTT + * packet of length . + * + * Returns the thread ID or a negative error code. + */ +int quic_get_cid_tid(const unsigned char *cid, size_t cid_len, + const struct sockaddr_storage *cli_addr, + unsigned char *pos, size_t len) +{ + struct quic_cid_tree *tree; + struct quic_connection_id *conn_id; + struct ebmb_node *node; + + tree = &quic_cid_trees[_quic_cid_tree_idx(cid)]; + HA_RWLOCK_RDLOCK(QC_CID_LOCK, &tree->lock); + node = ebmb_lookup(&tree->root, cid, cid_len); + HA_RWLOCK_RDUNLOCK(QC_CID_LOCK, &tree->lock); + + if (!node) { + struct quic_cid orig, derive_cid; + struct quic_rx_packet pkt; + + if (!qc_parse_hd_form(&pkt, &pos, pos + len)) + goto not_found; + + if (pkt.type != QUIC_PACKET_TYPE_INITIAL && + pkt.type != QUIC_PACKET_TYPE_0RTT) { + goto not_found; + } + + memcpy(orig.data, cid, cid_len); + orig.len = cid_len; + derive_cid = quic_derive_cid(&orig, cli_addr); + + tree = &quic_cid_trees[quic_cid_tree_idx(&derive_cid)]; + HA_RWLOCK_RDLOCK(QC_CID_LOCK, &tree->lock); + node = ebmb_lookup(&tree->root, cid, cid_len); + HA_RWLOCK_RDUNLOCK(QC_CID_LOCK, &tree->lock); + } + + if (!node) + goto not_found; + + conn_id = ebmb_entry(node, struct quic_connection_id, node); + return HA_ATOMIC_LOAD(&conn_id->tid); + + not_found: + return -1; +} + +/* Retrieve a quic_conn instance from the DCID field. If the packet is an + * INITIAL or 0RTT type, we may have to use client address if an ODCID + * is used. + * + * Returns the instance or NULL if not found. + */ +struct quic_conn *retrieve_qc_conn_from_cid(struct quic_rx_packet *pkt, + struct listener *l, + struct sockaddr_storage *saddr, + int *new_tid) +{ + struct quic_conn *qc = NULL; + struct ebmb_node *node; + struct quic_connection_id *conn_id; + struct quic_cid_tree *tree; + uint conn_id_tid; + + TRACE_ENTER(QUIC_EV_CONN_RXPKT); + *new_tid = -1; + + /* First look into DCID tree. */ + tree = &quic_cid_trees[_quic_cid_tree_idx(pkt->dcid.data)]; + HA_RWLOCK_RDLOCK(QC_CID_LOCK, &tree->lock); + node = ebmb_lookup(&tree->root, pkt->dcid.data, pkt->dcid.len); + + /* If not found on an Initial/0-RTT packet, it could be because an + * ODCID is reused by the client. Calculate the derived CID value to + * retrieve it from the DCID tree. + */ + if (!node && (pkt->type == QUIC_PACKET_TYPE_INITIAL || + pkt->type == QUIC_PACKET_TYPE_0RTT)) { + const struct quic_cid derive_cid = quic_derive_cid(&pkt->dcid, saddr); + + HA_RWLOCK_RDUNLOCK(QC_CID_LOCK, &tree->lock); + + tree = &quic_cid_trees[quic_cid_tree_idx(&derive_cid)]; + HA_RWLOCK_RDLOCK(QC_CID_LOCK, &tree->lock); + node = ebmb_lookup(&tree->root, derive_cid.data, derive_cid.len); + } + + if (!node) + goto end; + + conn_id = ebmb_entry(node, struct quic_connection_id, node); + conn_id_tid = HA_ATOMIC_LOAD(&conn_id->tid); + if (conn_id_tid != tid) { + *new_tid = conn_id_tid; + goto end; + } + qc = conn_id->qc; + + end: + HA_RWLOCK_RDUNLOCK(QC_CID_LOCK, &tree->lock); + TRACE_LEAVE(QUIC_EV_CONN_RXPKT, qc); + return qc; +} + + diff --git a/src/quic_conn.c b/src/quic_conn.c index 0f5377a20..2b175d83d 100644 --- a/src/quic_conn.c +++ b/src/quic_conn.c @@ -32,7 +32,6 @@ #include #include #include -#include #include #include @@ -485,198 +484,6 @@ int quic_stateless_reset_token_cpy(unsigned char *pos, size_t len, return ret; } -/* Initialize the stateless reset token attached to connection ID. - * Returns 1 if succeeded, 0 if not. - */ -static int quic_stateless_reset_token_init(struct quic_connection_id *conn_id) -{ - /* Output secret */ - unsigned char *token = conn_id->stateless_reset_token; - size_t tokenlen = sizeof conn_id->stateless_reset_token; - /* Salt */ - const unsigned char *cid = conn_id->cid.data; - size_t cidlen = conn_id->cid.len; - - return quic_stateless_reset_token_cpy(token, tokenlen, cid, cidlen); -} - -/* Generate a CID directly derived from CID and address. - * - * Returns the derived CID. - */ -struct quic_cid quic_derive_cid(const struct quic_cid *orig, - const struct sockaddr_storage *addr) -{ - struct quic_cid cid; - const struct sockaddr_in *in; - const struct sockaddr_in6 *in6; - char *pos = trash.area; - size_t idx = 0; - uint64_t hash; - int i; - - /* Prepare buffer for hash using original CID first. */ - memcpy(pos, orig->data, orig->len); - idx += orig->len; - - /* Concatenate client address. */ - switch (addr->ss_family) { - case AF_INET: - in = (struct sockaddr_in *)addr; - - memcpy(&pos[idx], &in->sin_addr, sizeof(in->sin_addr)); - idx += sizeof(in->sin_addr); - memcpy(&pos[idx], &in->sin_port, sizeof(in->sin_port)); - idx += sizeof(in->sin_port); - break; - - case AF_INET6: - in6 = (struct sockaddr_in6 *)addr; - - memcpy(&pos[idx], &in6->sin6_addr, sizeof(in6->sin6_addr)); - idx += sizeof(in6->sin6_addr); - memcpy(&pos[idx], &in6->sin6_port, sizeof(in6->sin6_port)); - idx += sizeof(in6->sin6_port); - break; - - default: - /* TODO to implement */ - ABORT_NOW(); - } - - /* Avoid similar values between multiple haproxy process. */ - memcpy(&pos[idx], boot_seed, sizeof(boot_seed)); - idx += sizeof(boot_seed); - - /* Hash the final buffer content. */ - hash = XXH64(pos, idx, 0); - - for (i = 0; i < sizeof(hash); ++i) - cid.data[i] = hash >> ((sizeof(hash) * 7) - (8 * i)); - cid.len = sizeof(hash); - - return cid; -} - -/* Retrieve the thread ID associated to QUIC connection ID of length - * . CID may be not found on the CID tree because it is an ODCID. In - * this case, it will derived using client address as hash - * parameter. However, this is done only if points to an INITIAL or 0RTT - * packet of length . - * - * Returns the thread ID or a negative error code. - */ -int quic_get_cid_tid(const unsigned char *cid, size_t cid_len, - const struct sockaddr_storage *cli_addr, - unsigned char *pos, size_t len) -{ - struct quic_cid_tree *tree; - struct quic_connection_id *conn_id; - struct ebmb_node *node; - - tree = &quic_cid_trees[_quic_cid_tree_idx(cid)]; - HA_RWLOCK_RDLOCK(QC_CID_LOCK, &tree->lock); - node = ebmb_lookup(&tree->root, cid, cid_len); - HA_RWLOCK_RDUNLOCK(QC_CID_LOCK, &tree->lock); - - if (!node) { - struct quic_cid orig, derive_cid; - struct quic_rx_packet pkt; - - if (!qc_parse_hd_form(&pkt, &pos, pos + len)) - goto not_found; - - if (pkt.type != QUIC_PACKET_TYPE_INITIAL && - pkt.type != QUIC_PACKET_TYPE_0RTT) { - goto not_found; - } - - memcpy(orig.data, cid, cid_len); - orig.len = cid_len; - derive_cid = quic_derive_cid(&orig, cli_addr); - - tree = &quic_cid_trees[quic_cid_tree_idx(&derive_cid)]; - HA_RWLOCK_RDLOCK(QC_CID_LOCK, &tree->lock); - node = ebmb_lookup(&tree->root, cid, cid_len); - HA_RWLOCK_RDUNLOCK(QC_CID_LOCK, &tree->lock); - } - - if (!node) - goto not_found; - - conn_id = ebmb_entry(node, struct quic_connection_id, node); - return HA_ATOMIC_LOAD(&conn_id->tid); - - not_found: - return -1; -} - -/* Allocate a new CID and attach it to ebtree. - * - * If and params are non null, the new CID value is directly - * derived from them. Else a random value is generated. The CID is then marked - * with the current thread ID. - * - * Returns the new CID if succeeded, NULL if not. - */ -struct quic_connection_id *new_quic_cid(struct eb_root *root, - struct quic_conn *qc, - const struct quic_cid *orig, - const struct sockaddr_storage *addr) -{ - struct quic_connection_id *conn_id; - - TRACE_ENTER(QUIC_EV_CONN_TXPKT, qc); - - /* Caller must set either none or both values. */ - BUG_ON(!!orig != !!addr); - - conn_id = pool_alloc(pool_head_quic_connection_id); - if (!conn_id) { - TRACE_ERROR("cid allocation failed", QUIC_EV_CONN_TXPKT, qc); - goto err; - } - - conn_id->cid.len = QUIC_HAP_CID_LEN; - - if (!orig) { - if (quic_newcid_from_hash64) - quic_newcid_from_hash64(conn_id->cid.data, conn_id->cid.len, qc->hash64, - global.cluster_secret, sizeof(global.cluster_secret)); - else if (RAND_bytes(conn_id->cid.data, conn_id->cid.len) != 1) { - /* TODO: RAND_bytes() should be replaced */ - TRACE_ERROR("RAND_bytes() failed", QUIC_EV_CONN_TXPKT, qc); - goto err; - } - } - else { - /* Derive the new CID value from original CID. */ - conn_id->cid = quic_derive_cid(orig, addr); - } - - if (quic_stateless_reset_token_init(conn_id) != 1) { - TRACE_ERROR("quic_stateless_reset_token_init() failed", QUIC_EV_CONN_TXPKT, qc); - goto err; - } - - conn_id->qc = qc; - HA_ATOMIC_STORE(&conn_id->tid, tid); - - conn_id->seq_num.key = qc ? qc->next_cid_seq_num++ : 0; - conn_id->retire_prior_to = 0; - /* insert the allocated CID in the quic_conn tree */ - if (root) - eb64_insert(root, &conn_id->seq_num); - - TRACE_LEAVE(QUIC_EV_CONN_TXPKT, qc); - return conn_id; - - err: - pool_free(pool_head_quic_connection_id, conn_id); - TRACE_LEAVE(QUIC_EV_CONN_TXPKT, qc); - return NULL; -} - /* QUIC connection packet handler task (post handshake) */ struct task *quic_conn_app_io_cb(struct task *t, void *context, unsigned int state) { diff --git a/src/quic_rx.c b/src/quic_rx.c index 3374e1a9b..c717b73e1 100644 --- a/src/quic_rx.c +++ b/src/quic_rx.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -1840,63 +1841,6 @@ static int quic_retry_token_check(struct quic_rx_packet *pkt, goto leave; } -/* Retrieve a quic_conn instance from the DCID field. If the packet is an - * INITIAL or 0RTT type, we may have to use client address if an ODCID - * is used. - * - * Returns the instance or NULL if not found. - */ -static struct quic_conn *retrieve_qc_conn_from_cid(struct quic_rx_packet *pkt, - struct listener *l, - struct sockaddr_storage *saddr, - int *new_tid) -{ - struct quic_conn *qc = NULL; - struct ebmb_node *node; - struct quic_connection_id *conn_id; - struct quic_cid_tree *tree; - uint conn_id_tid; - - TRACE_ENTER(QUIC_EV_CONN_RXPKT); - *new_tid = -1; - - /* First look into DCID tree. */ - tree = &quic_cid_trees[_quic_cid_tree_idx(pkt->dcid.data)]; - HA_RWLOCK_RDLOCK(QC_CID_LOCK, &tree->lock); - node = ebmb_lookup(&tree->root, pkt->dcid.data, pkt->dcid.len); - - /* If not found on an Initial/0-RTT packet, it could be because an - * ODCID is reused by the client. Calculate the derived CID value to - * retrieve it from the DCID tree. - */ - if (!node && (pkt->type == QUIC_PACKET_TYPE_INITIAL || - pkt->type == QUIC_PACKET_TYPE_0RTT)) { - const struct quic_cid derive_cid = quic_derive_cid(&pkt->dcid, saddr); - - HA_RWLOCK_RDUNLOCK(QC_CID_LOCK, &tree->lock); - - tree = &quic_cid_trees[quic_cid_tree_idx(&derive_cid)]; - HA_RWLOCK_RDLOCK(QC_CID_LOCK, &tree->lock); - node = ebmb_lookup(&tree->root, derive_cid.data, derive_cid.len); - } - - if (!node) - goto end; - - conn_id = ebmb_entry(node, struct quic_connection_id, node); - conn_id_tid = HA_ATOMIC_LOAD(&conn_id->tid); - if (conn_id_tid != tid) { - *new_tid = conn_id_tid; - goto end; - } - qc = conn_id->qc; - - end: - HA_RWLOCK_RDUNLOCK(QC_CID_LOCK, &tree->lock); - TRACE_LEAVE(QUIC_EV_CONN_RXPKT, qc); - return qc; -} - /* Check that all the bytes between included and address * excluded are null. This is the responsibility of the caller to * check that there is at least one byte between end . diff --git a/src/quic_sock.c b/src/quic_sock.c index afffdf302..323fe6cbd 100644 --- a/src/quic_sock.c +++ b/src/quic_sock.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include diff --git a/src/quic_tx.c b/src/quic_tx.c index 4c1157f63..7953c0481 100644 --- a/src/quic_tx.c +++ b/src/quic_tx.c @@ -16,6 +16,7 @@ #include #include +#include #include #include #include