#include <haproxy/debug.h>
#include <haproxy/tools.h>
#include <haproxy/ticks.h>
+#include <haproxy/xxhash.h>
#include <haproxy/applet-t.h>
#include <haproxy/cli.h>
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);
+ struct quic_conn *qc,
+ const struct quic_cid *odcid,
+ const struct sockaddr_storage *saddr);
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,
pool_free(pool_head_quic_connection_id, cid);
TRACE_PROTO("CID retired", QUIC_EV_CONN_PSTRM, qc);
- cid = new_quic_cid(&qc->cids, qc);
+ cid = new_quic_cid(&qc->cids, qc, NULL, NULL);
if (!cid) {
TRACE_ERROR("CID allocation error", QUIC_EV_CONN_IO_CB, qc);
}
return ret;
}
+/* Generate a CID directly derived from <orig> CID and <addr> address. The CID
+ * is then marked with the current thread ID.
+ *
+ * Returns a new 64-bits CID value.
+ */
+static uint64_t quic_derive_cid(const struct quic_cid *orig,
+ const struct sockaddr_storage *addr)
+{
+ const struct sockaddr_in *in;
+ const struct sockaddr_in6 *in6;
+ char *buf = trash.area;
+ size_t idx = 0;
+ uint64_t hash;
+
+ /* Prepare buffer for hash using original CID first. */
+ memcpy(buf, orig->data, orig->len);
+ idx += orig->len;
+
+ /* Concatenate client address. */
+ switch (addr->ss_family) {
+ case AF_INET:
+ in = (struct sockaddr_in *)addr;
+
+ memcpy(&buf[idx], &in->sin_addr, sizeof(in->sin_addr));
+ idx += sizeof(in->sin_addr);
+ memcpy(&buf[idx], &in->sin_port, sizeof(in->sin_port));
+ idx += sizeof(in->sin_port);
+ break;
+
+ case AF_INET6:
+ in6 = (struct sockaddr_in6 *)addr;
+
+ memcpy(&buf[idx], &in6->sin6_addr, sizeof(in6->sin6_addr));
+ idx += sizeof(in6->sin6_addr);
+ memcpy(&buf[idx], &in6->sin6_port, sizeof(in6->sin6_port));
+ idx += sizeof(in6->sin6_port);
+ break;
+
+ default:
+ /* TODO to implement */
+ ABORT_NOW();
+ return 0;
+ }
+
+ /* Avoid similar values between multiple haproxy process. */
+ memcpy(&buf[idx], boot_seed, sizeof(boot_seed));
+ idx += sizeof(boot_seed);
+
+ /* Hash the final buffer content. */
+ hash = XXH64(buf, idx, 0);
+
+ /* Mark the current thread id in the CID. */
+ quic_pin_cid_to_tid((uchar *)&hash, tid);
+
+ return hash;
+}
+
/* Allocate a new CID and attach it to <root> ebtree.
*
- * The CID is randomly generated in part with the result altered to be
- * associated with the current thread ID. This means this function must only
- * be called by the quic_conn thread.
+ * 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.
*/
static struct quic_connection_id *new_quic_cid(struct eb_root *root,
- struct quic_conn *qc)
+ struct quic_conn *qc,
+ const struct quic_cid *orig,
+ const struct sockaddr_storage *addr)
{
struct quic_connection_id *cid;
TRACE_ENTER(QUIC_EV_CONN_TXPKT, qc);
+ /* Caller must set either none or both values. */
+ BUG_ON(!!orig != !!addr);
+
cid = pool_alloc(pool_head_quic_connection_id);
if (!cid) {
TRACE_ERROR("cid allocation failed", QUIC_EV_CONN_TXPKT, qc);
}
cid->cid.len = QUIC_HAP_CID_LEN;
- /* TODO: RAND_bytes() should be replaced */
- if (RAND_bytes(cid->cid.data, cid->cid.len) != 1) {
- TRACE_ERROR("RAND_bytes() failed", QUIC_EV_CONN_TXPKT, qc);
- goto err;
+
+ if (!orig) {
+ /* TODO: RAND_bytes() should be replaced */
+ if (RAND_bytes(cid->cid.data, cid->cid.len) != 1) {
+ TRACE_ERROR("RAND_bytes() failed", QUIC_EV_CONN_TXPKT, qc);
+ goto err;
+ }
+ quic_pin_cid_to_tid(cid->cid.data, tid);
+ }
+ else {
+ /* Derive the new CID value from original CID. */
+ const uint64_t hash = quic_derive_cid(orig, addr);
+ memcpy(cid->cid.data, &hash, sizeof(hash));
}
- quic_pin_cid_to_tid(cid->cid.data, tid);
if (quic_stateless_reset_token_init(qc, cid) != 1) {
TRACE_ERROR("quic_stateless_reset_token_init() failed", QUIC_EV_CONN_TXPKT, qc);
goto err;
goto err;
}
- cid = new_quic_cid(&qc->cids, qc);
+ cid = new_quic_cid(&qc->cids, qc, NULL, NULL);
if (!cid) {
qc_frm_free(&frm);
TRACE_ERROR("CID allocation error", QUIC_EV_CONN_IO_CB, qc);
/* Initialize the next CID sequence number to be used for this connection. */
qc->next_cid_seq_num = 0;
- /* Insert the CID for this connection with 0 as sequence number. */
- icid = new_quic_cid(&qc->cids, qc);
+ /* Generate the first connection CID. This is derived from the client
+ * ODCID and address. This allows to retrieve the connection from the
+ * ODCID without storing it in the CID tree. This is an interesting
+ * optimization as the client is expected to stop using its ODCID in
+ * favor of our generated value.
+ */
+ icid = new_quic_cid(&qc->cids, qc, dcid, peer_addr);
if (!icid) {
TRACE_ERROR("Could not allocate a new connection ID", QUIC_EV_CONN_INIT, qc);
goto err;