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.
This commit is contained in:
parent
1564ec0a93
commit
0fc0d45745
2
Makefile
2
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),)
|
||||
|
37
include/haproxy/quic_cid-t.h
Normal file
37
include/haproxy/quic_cid-t.h
Normal file
@ -0,0 +1,37 @@
|
||||
#ifndef _HAPROXY_QUIC_CID_T_H
|
||||
#define _HAPROXY_QUIC_CID_T_H
|
||||
|
||||
#include <haproxy/quic_tp-t.h>
|
||||
|
||||
/* 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.
|
||||
* <data> 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 */
|
94
include/haproxy/quic_cid.h
Normal file
94
include/haproxy/quic_cid.h
Normal file
@ -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 <import/ebmbtree.h>
|
||||
|
||||
#include <haproxy/buf-t.h>
|
||||
#include <haproxy/chunk.h>
|
||||
#include <haproxy/quic_conn-t.h>
|
||||
#include <haproxy/quic_rx-t.h>
|
||||
#include <haproxy/proto_quic.h>
|
||||
|
||||
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 <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);
|
||||
}
|
||||
|
||||
/* Insert <conn_id> 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 <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);
|
||||
}
|
||||
|
||||
#endif /* USE_QUIC */
|
||||
#endif /* _HAPROXY_QUIC_CID_H */
|
@ -33,6 +33,7 @@
|
||||
|
||||
#include <haproxy/openssl-compat.h>
|
||||
#include <haproxy/mux_quic-t.h>
|
||||
#include <haproxy/quic_cid-t.h>
|
||||
#include <haproxy/quic_cc-t.h>
|
||||
#include <haproxy/quic_loss-t.h>
|
||||
#include <haproxy/quic_openssl_compat-t.h>
|
||||
@ -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.
|
||||
* <data> 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)
|
||||
|
@ -40,6 +40,7 @@
|
||||
#include <haproxy/listener.h>
|
||||
#include <haproxy/proto_quic.h>
|
||||
#include <haproxy/quic_cc.h>
|
||||
#include <haproxy/quic_cid.h>
|
||||
#include <haproxy/quic_conn-t.h>
|
||||
#include <haproxy/quic_enc.h>
|
||||
#include <haproxy/quic_frame.h>
|
||||
@ -109,17 +110,6 @@ static inline int qc_is_listener(struct quic_conn *qc)
|
||||
return qc->flags & QUIC_FL_CONN_LISTENER;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* Copy <saddr> socket address data into <buf> 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 <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);
|
||||
}
|
||||
|
||||
/* Insert <conn_id> 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 <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);
|
||||
}
|
||||
|
||||
/* Free the CIDs attached to <conn> QUIC connection. */
|
||||
static inline void free_quic_conn_cids(struct quic_conn *conn)
|
||||
{
|
||||
|
261
src/quic_cid.c
Normal file
261
src/quic_cid.c
Normal file
@ -0,0 +1,261 @@
|
||||
#include <import/eb64tree.h>
|
||||
#include <import/ebmbtree.h>
|
||||
|
||||
#include <haproxy/pool.h>
|
||||
#include <haproxy/quic_cid.h>
|
||||
#include <haproxy/quic_conn.h>
|
||||
#include <haproxy/quic_rx-t.h>
|
||||
#include <haproxy/quic_trace.h>
|
||||
#include <haproxy/trace.h>
|
||||
#include <haproxy/xxhash.h>
|
||||
|
||||
/* Initialize the stateless reset token attached to <conn_id> 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 <orig> CID and <addr> 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 <root> ebtree.
|
||||
*
|
||||
* If <orig> and <addr> 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 <cid> of length
|
||||
* <cid_len>. CID may be not found on the CID tree because it is an ODCID. In
|
||||
* this case, it will derived using client address <cli_addr> as hash
|
||||
* parameter. However, this is done only if <pos> points to an INITIAL or 0RTT
|
||||
* packet of length <len>.
|
||||
*
|
||||
* 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 <pkt> DCID field. If the packet is an
|
||||
* INITIAL or 0RTT type, we may have to use client address <saddr> 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;
|
||||
}
|
||||
|
||||
|
193
src/quic_conn.c
193
src/quic_conn.c
@ -32,7 +32,6 @@
|
||||
#include <haproxy/debug.h>
|
||||
#include <haproxy/tools.h>
|
||||
#include <haproxy/ticks.h>
|
||||
#include <haproxy/xxhash.h>
|
||||
|
||||
#include <haproxy/connection.h>
|
||||
#include <haproxy/fd.h>
|
||||
@ -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 <conn_id> 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 <orig> CID and <addr> 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 <cid> of length
|
||||
* <cid_len>. CID may be not found on the CID tree because it is an ODCID. In
|
||||
* this case, it will derived using client address <cli_addr> as hash
|
||||
* parameter. However, this is done only if <pos> points to an INITIAL or 0RTT
|
||||
* packet of length <len>.
|
||||
*
|
||||
* 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 <root> ebtree.
|
||||
*
|
||||
* If <orig> and <addr> 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)
|
||||
{
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include <haproxy/ncbuf.h>
|
||||
#include <haproxy/proto_quic.h>
|
||||
#include <haproxy/quic_ack.h>
|
||||
#include <haproxy/quic_cid.h>
|
||||
#include <haproxy/quic_sock.h>
|
||||
#include <haproxy/quic_stream.h>
|
||||
#include <haproxy/quic_ssl.h>
|
||||
@ -1840,63 +1841,6 @@ static int quic_retry_token_check(struct quic_rx_packet *pkt,
|
||||
goto leave;
|
||||
}
|
||||
|
||||
/* Retrieve a quic_conn instance from the <pkt> DCID field. If the packet is an
|
||||
* INITIAL or 0RTT type, we may have to use client address <saddr> 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 <pos> included and <end> address
|
||||
* excluded are null. This is the responsibility of the caller to
|
||||
* check that there is at least one byte between <pos> end <end>.
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include <haproxy/pool.h>
|
||||
#include <haproxy/proto_quic.h>
|
||||
#include <haproxy/proxy-t.h>
|
||||
#include <haproxy/quic_cid.h>
|
||||
#include <haproxy/quic_conn.h>
|
||||
#include <haproxy/quic_rx.h>
|
||||
#include <haproxy/quic_sock.h>
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
#include <haproxy/pool.h>
|
||||
#include <haproxy/trace.h>
|
||||
#include <haproxy/quic_cid.h>
|
||||
#include <haproxy/quic_sock.h>
|
||||
#include <haproxy/quic_tls.h>
|
||||
#include <haproxy/quic_trace.h>
|
||||
|
Loading…
Reference in New Issue
Block a user