mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-12-01 15:51:01 +01:00
On Initial packet parsing, a new quic_conn instance is allocated via qc_new_conn(). Then a CID is allocated with its value derivated from client ODCID. On CID tree insert, a collision can occur if another thread was already parsing an Initial packet from the same client. In this case, the connection is released and the packet will be requeued to the other thread. Originally, CID collision check was performed prior to quic_conn allocation. This was changed by the commit below, as this could cause issue on quic_conn alloc failure. commit 4ae29be18c5b212dd2a1a8e9fa0ee2fcb9dbb4b3 BUG/MINOR: quic: Possible endless loop in quic_lstnr_dghdlr() However, this procedure is less optimal. Indeed, qc_new_conn() performs many steps, thus it could be better to skip it on Initial CID collision, which can happen frequently. This patch restores the older order of operations, with CID collision check prior to quic_conn allocation. To ensure this does not cause again the same bug, the CID is removed in case of quic_conn alloc failure. This should prevent any loop as it ensures that a CID found in the global tree does not point to a NULL quic_conn, unless if CID is attach to a foreign thread. When this thread will parse a re-enqueued packet, either the quic_conn is already allocated or the CID has been removed, triggering a fresh CID and quic_conn allocation procedure.
112 lines
3.5 KiB
C
112 lines
3.5 KiB
C
#ifndef _HAPROXY_QUIC_CID_H
|
|
#define _HAPROXY_QUIC_CID_H
|
|
|
|
#ifdef USE_QUIC
|
|
#ifndef USE_OPENSSL
|
|
#error "Must define USE_OPENSSL"
|
|
#endif
|
|
|
|
#include <import/ebmbtree.h>
|
|
|
|
#include <haproxy/buf-t.h>
|
|
#include <haproxy/chunk.h>
|
|
#include <haproxy/quic_conn-t.h>
|
|
#include <haproxy/quic_cid-t.h>
|
|
#include <haproxy/quic_rx-t.h>
|
|
#include <haproxy/proto_quic.h>
|
|
|
|
extern struct quic_cid_tree *quic_cid_trees;
|
|
|
|
struct quic_connection_id *quic_cid_alloc(void);
|
|
|
|
int quic_cid_generate(struct quic_connection_id *conn_id, uint64_t hash);
|
|
|
|
int quic_cid_derive_from_odcid(struct quic_connection_id *conn_id,
|
|
const struct quic_cid *orig,
|
|
const struct sockaddr_storage *addr);
|
|
|
|
void quic_cid_register_seq_num(struct quic_connection_id *conn_id,
|
|
struct quic_conn *qc);
|
|
|
|
int quic_cid_insert(struct quic_connection_id *conn_id, int *new_tid);
|
|
int quic_cmp_cid_conn(const unsigned char *cid, size_t cid_len,
|
|
struct quic_conn *qc);
|
|
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_conn *retrieve_qc_conn_from_cid(struct quic_rx_packet *pkt,
|
|
struct sockaddr_storage *saddr,
|
|
int *new_tid);
|
|
int qc_build_new_connection_id_frm(struct quic_conn *qc,
|
|
struct quic_connection_id *conn_id);
|
|
|
|
/* Copy <src> QUIC CID to <dst>.
|
|
* This is the responsibility of the caller to check there is enough room in
|
|
* <dst> to copy <src>.
|
|
* 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 <cid> is stored. */
|
|
static inline uchar _quic_cid_tree_idx(const unsigned char *cid)
|
|
{
|
|
return cid[0];
|
|
}
|
|
|
|
/* Return tree index where <cid> is stored. */
|
|
static inline uchar quic_cid_tree_idx(const struct quic_cid *cid)
|
|
{
|
|
return _quic_cid_tree_idx(cid->data);
|
|
}
|
|
|
|
/* Remove <conn_id> 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);
|
|
}
|
|
|
|
/* Copy <src> new connection ID information to <dst> NEW_CONNECTION_ID frame.
|
|
* Always succeeds.
|
|
*/
|
|
static inline void quic_connection_id_to_frm_cpy(struct quic_frame *dst,
|
|
struct quic_connection_id *src)
|
|
{
|
|
struct qf_new_connection_id *ncid_frm = &dst->new_connection_id;
|
|
|
|
ncid_frm->seq_num = src->seq_num.key;
|
|
ncid_frm->retire_prior_to = src->retire_prior_to;
|
|
ncid_frm->cid.len = src->cid.len;
|
|
ncid_frm->cid.data = src->cid.data;
|
|
ncid_frm->stateless_reset_token = src->stateless_reset_token;
|
|
}
|
|
|
|
#endif /* USE_QUIC */
|
|
#endif /* _HAPROXY_QUIC_CID_H */
|