diff --git a/include/haproxy/quic_tls.h b/include/haproxy/quic_tls.h index 244afe817..02f36657f 100644 --- a/include/haproxy/quic_tls.h +++ b/include/haproxy/quic_tls.h @@ -71,7 +71,7 @@ int quic_tls_encrypt(unsigned char *buf, size_t len, const unsigned char *key, const unsigned char *iv); int quic_tls_decrypt2(unsigned char *out, - unsigned char *in, size_t ilen, + const unsigned char *in, size_t ilen, unsigned char *aad, size_t aad_len, EVP_CIPHER_CTX *ctx, const EVP_CIPHER *aead, const unsigned char *key, const unsigned char *iv); @@ -505,6 +505,7 @@ static inline void quic_tls_discard_keys(struct quic_enc_level *qel) * Return 1 if succeeded or 0 if not. */ static inline int qc_new_isecs(struct quic_conn *qc, + struct quic_tls_ctx *ctx, const struct quic_version *ver, const unsigned char *cid, size_t cidlen, int server) { unsigned char initial_secret[32]; @@ -513,15 +514,13 @@ static inline int qc_new_isecs(struct quic_conn *qc, /* Initial secret to be derived for outgoing packets */ unsigned char tx_init_sec[32]; struct quic_tls_secrets *rx_ctx, *tx_ctx; - struct quic_tls_ctx *ctx; TRACE_ENTER(QUIC_EV_CONN_ISEC); - ctx = &qc->els[QUIC_TLS_ENC_LEVEL_INITIAL].tls_ctx; if (!quic_initial_tls_ctx_init(ctx)) goto err; if (!quic_derive_initial_secret(ctx->rx.md, - qc->version->initial_salt, qc->version->initial_salt_len, + ver->initial_salt, ver->initial_salt_len, initial_secret, sizeof initial_secret, cid, cidlen)) goto err; @@ -534,7 +533,7 @@ static inline int qc_new_isecs(struct quic_conn *qc, rx_ctx = &ctx->rx; tx_ctx = &ctx->tx; - if (!quic_tls_derive_keys(ctx->rx.aead, ctx->rx.hp, ctx->rx.md, qc->version, + if (!quic_tls_derive_keys(ctx->rx.aead, ctx->rx.hp, ctx->rx.md, ver, rx_ctx->key, rx_ctx->keylen, rx_ctx->iv, rx_ctx->ivlen, rx_ctx->hp_key, sizeof rx_ctx->hp_key, @@ -544,7 +543,7 @@ static inline int qc_new_isecs(struct quic_conn *qc, if (!quic_tls_rx_ctx_init(&rx_ctx->ctx, rx_ctx->aead, rx_ctx->key)) goto err; - if (!quic_tls_derive_keys(ctx->tx.aead, ctx->tx.hp, ctx->tx.md, qc->version, + if (!quic_tls_derive_keys(ctx->tx.aead, ctx->tx.hp, ctx->tx.md, ver, tx_ctx->key, tx_ctx->keylen, tx_ctx->iv, tx_ctx->ivlen, tx_ctx->hp_key, sizeof tx_ctx->hp_key, diff --git a/include/haproxy/quic_tp-t.h b/include/haproxy/quic_tp-t.h index 47301db48..5180dfcd8 100644 --- a/include/haproxy/quic_tp-t.h +++ b/include/haproxy/quic_tp-t.h @@ -26,6 +26,12 @@ struct tp_preferred_address { uint8_t stateless_reset_token[QUIC_STATELESS_RESET_TOKEN_LEN]; }; +struct tp_version_information { + uint32_t choosen; + const uint32_t *others; + const struct quic_version *negotiated_version; +}; + /* Default values for the absent transport parameters */ #define QUIC_TP_DFLT_MAX_UDP_PAYLOAD_SIZE 65527 /* bytes */ #define QUIC_TP_DFLT_ACK_DELAY_COMPONENT 3 /* milliseconds */ @@ -39,23 +45,24 @@ struct tp_preferred_address { #define QUIC_TP_DFLT_BACK_MAX_IDLE_TIMEOUT 30000 /* milliseconds */ /* Types of QUIC transport parameters */ -#define QUIC_TP_ORIGINAL_DESTINATION_CONNECTION_ID 0 -#define QUIC_TP_MAX_IDLE_TIMEOUT 1 -#define QUIC_TP_STATELESS_RESET_TOKEN 2 -#define QUIC_TP_MAX_UDP_PAYLOAD_SIZE 3 -#define QUIC_TP_INITIAL_MAX_DATA 4 -#define QUIC_TP_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL 5 -#define QUIC_TP_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE 6 -#define QUIC_TP_INITIAL_MAX_STREAM_DATA_UNI 7 -#define QUIC_TP_INITIAL_MAX_STREAMS_BIDI 8 -#define QUIC_TP_INITIAL_MAX_STREAMS_UNI 9 -#define QUIC_TP_ACK_DELAY_EXPONENT 10 -#define QUIC_TP_MAX_ACK_DELAY 11 -#define QUIC_TP_DISABLE_ACTIVE_MIGRATION 12 -#define QUIC_TP_PREFERRED_ADDRESS 13 -#define QUIC_TP_ACTIVE_CONNECTION_ID_LIMIT 14 -#define QUIC_TP_INITIAL_SOURCE_CONNECTION_ID 15 -#define QUIC_TP_RETRY_SOURCE_CONNECTION_ID 16 +#define QUIC_TP_ORIGINAL_DESTINATION_CONNECTION_ID 0x00 +#define QUIC_TP_MAX_IDLE_TIMEOUT 0x01 +#define QUIC_TP_STATELESS_RESET_TOKEN 0x02 +#define QUIC_TP_MAX_UDP_PAYLOAD_SIZE 0x03 +#define QUIC_TP_INITIAL_MAX_DATA 0x04 +#define QUIC_TP_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL 0x05 +#define QUIC_TP_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE 0x06 +#define QUIC_TP_INITIAL_MAX_STREAM_DATA_UNI 0x07 +#define QUIC_TP_INITIAL_MAX_STREAMS_BIDI 0x08 +#define QUIC_TP_INITIAL_MAX_STREAMS_UNI 0x09 +#define QUIC_TP_ACK_DELAY_EXPONENT 0x0a +#define QUIC_TP_MAX_ACK_DELAY 0x0b +#define QUIC_TP_DISABLE_ACTIVE_MIGRATION 0x0c +#define QUIC_TP_PREFERRED_ADDRESS 0x0d +#define QUIC_TP_ACTIVE_CONNECTION_ID_LIMIT 0x0e +#define QUIC_TP_INITIAL_SOURCE_CONNECTION_ID 0x0f +#define QUIC_TP_RETRY_SOURCE_CONNECTION_ID 0x10 +#define QUIC_TP_DRAFT_VERSION_INFORMATION 0xff73db /* * These defines are not for transport parameter type, but the maximum accepted value for @@ -103,6 +110,7 @@ struct quic_transport_params { /* MUST be present both for servers and clients. */ struct tp_cid initial_source_connection_id; struct tp_preferred_address preferred_address; /* Forbidden for clients */ + struct tp_version_information version_information; }; #endif /* USE_QUIC */ diff --git a/include/haproxy/quic_tp.h b/include/haproxy/quic_tp.h index 5343e2576..b96c9f9c7 100644 --- a/include/haproxy/quic_tp.h +++ b/include/haproxy/quic_tp.h @@ -13,6 +13,7 @@ void quic_transport_params_init(struct quic_transport_params *p, int server); int quic_transport_params_encode(unsigned char *buf, const unsigned char *end, struct quic_transport_params *p, + const struct quic_version *choosen_version, int server); int quic_transport_params_store(struct quic_conn *conn, int server, diff --git a/include/haproxy/xprt_quic-t.h b/include/haproxy/xprt_quic-t.h index d472f62d5..af16656c4 100644 --- a/include/haproxy/xprt_quic-t.h +++ b/include/haproxy/xprt_quic-t.h @@ -286,6 +286,10 @@ struct quic_version { const unsigned char *retry_tag_nonce; }; +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. @@ -625,7 +629,10 @@ enum qc_mux_state { #define QUIC_FL_CONN_DRAINING (1U << 30) #define QUIC_FL_CONN_IMMEDIATE_CLOSE (1U << 31) struct quic_conn { - const struct quic_version *version; + const struct quic_version *original_version; + const struct quic_version *negotiated_version; + /* Negotiated version Initial TLS context */ + struct quic_tls_ctx negotiated_ictx; /* QUIC transport parameters TLS extension */ int tps_tls_ext; /* Thread ID this connection is attached to */ diff --git a/include/haproxy/xprt_quic.h b/include/haproxy/xprt_quic.h index bbfeb04be..cf9e4ee7a 100644 --- a/include/haproxy/xprt_quic.h +++ b/include/haproxy/xprt_quic.h @@ -50,6 +50,7 @@ extern struct pool_head *pool_head_quic_connection_id; +int qc_conn_finalize(struct quic_conn *qc, int server); int ssl_quic_initial_ctx(struct bind_conf *bind_conf); /* Return the long packet type matching with version and */ diff --git a/src/quic_tp.c b/src/quic_tp.c index 69c654114..f7f8d7ddd 100644 --- a/src/quic_tp.c +++ b/src/quic_tp.c @@ -155,6 +155,66 @@ static int quic_transport_param_dec_pref_addr(struct tp_preferred_address *addr, return *buf == end; } +/* Decode into version information received transport parameters from <*buf> + * buffer. must be set to 1 for QUIC clients which receive server + * transport parameters, and 0 for QUIC servers which receive client transport + * parameters. + * Also set the QUIC negotiated version into . + * Return 1 if succeeded, 0 if not. + */ +static int quic_transport_param_dec_version_info(struct tp_version_information *tp, + const unsigned char **buf, + const unsigned char *end, int server) +{ + size_t tp_len = end - *buf; + const uint32_t *ver; + + /* must be a multiple of sizeof(uint32_t) */ + if (tp_len < sizeof tp->choosen || (tp_len & 0x3)) + return 0; + + tp->choosen = ntohl(*(uint32_t *)*buf); + /* Must not be null */ + if (!tp->choosen) + return 0; + + *buf += sizeof tp->choosen; + tp->others = (const uint32_t *)*buf; + + /* Others versions must not be null */ + for (ver = tp->others; ver < (const uint32_t *)end; ver++) { + if (!*ver) + return 0; + } + + if (server) + /* TODO: not supported */ + return 0; + + for (ver = tp->others; ver < (const uint32_t *)end; ver++) { + if (!tp->negotiated_version) { + int i; + + for (i = 0; i < quic_versions_nb; i++) { + if (ntohl(*ver) == quic_versions[i].num) { + tp->negotiated_version = &quic_versions[i]; + break; + } + } + } + + if (preferred_version && ntohl(*ver) == preferred_version->num) { + tp->negotiated_version = preferred_version; + goto out; + } + } + + out: + *buf = end; + + return 1; +} + /* Decode into

struct a transport parameter found in <*buf> buffer with * as type and as length, depending on boolean value which * must be set to 1 for a server (haproxy listener) or 0 for a client (connection @@ -253,6 +313,11 @@ static int quic_transport_param_decode(struct quic_transport_params *p, if (!quic_dec_int(&p->active_connection_id_limit, buf, end)) return 0; break; + case QUIC_TP_DRAFT_VERSION_INFORMATION: + if (!quic_transport_param_dec_version_info(&p->version_information, + buf, *buf + len, server)) + return 0; + break; default: *buf += len; }; @@ -349,6 +414,42 @@ static int quic_transport_param_enc_pref_addr(unsigned char **buf, return 1; } +/* Encode version information transport parameters with as choosen + * version. + * Return 1 if succeeded, 0 if not. + */ +static int quic_transport_param_enc_version_info(unsigned char **buf, + const unsigned char *end, + const struct quic_version *choosen_version, + int server) +{ + int i; + uint64_t tp_len; + uint32_t ver; + + tp_len = sizeof choosen_version->num + quic_versions_nb * sizeof(uint32_t); + if (!quic_transport_param_encode_type_len(buf, end, + QUIC_TP_DRAFT_VERSION_INFORMATION, + tp_len)) + return 0; + + if (end - *buf < tp_len) + return 0; + + /* First: choosen version */ + ver = htonl(choosen_version->num); + memcpy(*buf, &ver, sizeof ver); + *buf += sizeof ver; + /* For servers: all supported version, choosen included */ + for (i = 0; i < quic_versions_nb; i++) { + ver = htonl(quic_versions[i].num); + memcpy(*buf, &ver, sizeof ver); + *buf += sizeof ver; + } + + return 1; +} + /* Encode

transport parameter into depending on value which * must be set to 1 for a server (haproxy listener) or 0 for a client * (connection to a haproxy server). @@ -357,6 +458,7 @@ static int quic_transport_param_enc_pref_addr(unsigned char **buf, int quic_transport_params_encode(unsigned char *buf, const unsigned char *end, struct quic_transport_params *p, + const struct quic_version *choosen_version, int server) { unsigned char *head; @@ -462,6 +564,9 @@ int quic_transport_params_encode(unsigned char *buf, p->active_connection_id_limit)) return 0; + if (!quic_transport_param_enc_version_info(&pos, end, choosen_version, server)) + return 0; + return pos - head; } @@ -515,7 +620,6 @@ int quic_transport_params_store(struct quic_conn *qc, int server, const unsigned char *end) { struct quic_transport_params *tx_params = &qc->tx.params; - struct quic_transport_params *rx_params = &qc->rx.params; /* initialize peer TPs to RFC default value */ quic_dflt_transport_params_cpy(tx_params); @@ -523,18 +627,6 @@ int quic_transport_params_store(struct quic_conn *qc, int server, if (!quic_transport_params_decode(tx_params, server, buf, end)) return 0; - if (tx_params->max_ack_delay) - qc->max_ack_delay = tx_params->max_ack_delay; - - if (tx_params->max_idle_timeout && rx_params->max_idle_timeout) - qc->max_idle_timeout = - QUIC_MIN(tx_params->max_idle_timeout, rx_params->max_idle_timeout); - else - qc->max_idle_timeout = - QUIC_MAX(tx_params->max_idle_timeout, rx_params->max_idle_timeout); - - TRACE_PROTO("\nTX(remote) transp. params.", QUIC_EV_TRANSP_PARAMS, qc, tx_params); - return 1; } diff --git a/src/ssl_sock.c b/src/ssl_sock.c index db255fa9e..01c5bc910 100644 --- a/src/ssl_sock.c +++ b/src/ssl_sock.c @@ -2621,7 +2621,8 @@ int ssl_sock_switchctx_cbk(SSL *ssl, int *al, void *arg) } if (!quic_transport_params_store(qc, 0, extension_data, - extension_data + extension_len)) + extension_data + extension_len) || + !qc_conn_finalize(qc, 0)) goto abort; } #endif /* USE_QUIC */ diff --git a/src/xprt_quic.c b/src/xprt_quic.c index cf88d4877..57f9f53c9 100644 --- a/src/xprt_quic.c +++ b/src/xprt_quic.c @@ -57,7 +57,7 @@ #include /* list of supported QUIC versions by this implementation */ -static const struct quic_version quic_versions[] = { +const struct quic_version quic_versions[] = { { .num = QUIC_PROTOCOL_VERSION_DRAFT_29, .initial_salt = initial_salt_draft_29, @@ -106,7 +106,9 @@ static const struct quic_version quic_versions[] = { }; /* The total number of supported versions */ -static size_t quic_versions_nb = sizeof quic_versions / sizeof *quic_versions; +const size_t quic_versions_nb = sizeof quic_versions / sizeof *quic_versions; +/* Listener only preferred version */ +const struct quic_version *preferred_version; /* trace source and events */ static void quic_trace(enum trace_level level, uint64_t mask, \ @@ -206,8 +208,9 @@ DECLARE_POOL(pool_head_quic_frame, "quic_frame_pool", sizeof(struct quic_frame)) DECLARE_STATIC_POOL(pool_head_quic_arng, "quic_arng_pool", sizeof(struct quic_arng_node)); static struct quic_tx_packet *qc_build_pkt(unsigned char **pos, const unsigned char *buf_end, - struct quic_enc_level *qel, struct list *frms, - struct quic_conn *qc, size_t dglen, int pkt_type, + struct quic_enc_level *qel, struct quic_tls_ctx *ctx, + struct list *frms, struct quic_conn *qc, + const struct quic_version *ver, size_t dglen, int pkt_type, int padding, int probe, int cc, int *err); static struct task *quic_conn_app_io_cb(struct task *t, void *context, unsigned int state); static void qc_idle_timer_do_rearm(struct quic_conn *qc); @@ -642,6 +645,7 @@ static void quic_trace(enum trace_level level, uint64_t mask, const struct trace if (mask & QUIC_EV_CONN_LPKT) { const struct quic_rx_packet *pkt = a2; const uint64_t *len = a3; + const struct quic_version *ver = a4; if (pkt) { chunk_appendf(&trace_buf, " pkt@%p type=0x%02x %s", @@ -652,6 +656,9 @@ static void quic_trace(enum trace_level level, uint64_t mask, const struct trace if (len) chunk_appendf(&trace_buf, " len=%llu", (ull)*len); + + if (ver) + chunk_appendf(&trace_buf, " ver=0x%08x", ver->num); } if (mask & QUIC_EV_STATELESS_RST) { @@ -744,6 +751,8 @@ static int quic_tls_key_update(struct quic_conn *qc) struct quic_tls_secrets *rx, *tx; struct quic_tls_kp *nxt_rx = &qc->ku.nxt_rx; struct quic_tls_kp *nxt_tx = &qc->ku.nxt_tx; + const struct quic_version *ver = + qc->negotiated_version ? qc->negotiated_version : qc->original_version; tls_ctx = &qc->els[QUIC_TLS_ENC_LEVEL_APP].tls_ctx; rx = &tls_ctx->rx; @@ -752,13 +761,13 @@ static int quic_tls_key_update(struct quic_conn *qc) nxt_tx = &qc->ku.nxt_tx; /* Prepare new RX secrets */ - if (!quic_tls_sec_update(rx->md, qc->version, nxt_rx->secret, nxt_rx->secretlen, + if (!quic_tls_sec_update(rx->md, ver, nxt_rx->secret, nxt_rx->secretlen, rx->secret, rx->secretlen)) { TRACE_DEVEL("New RX secret update failed", QUIC_EV_CONN_RWSEC, qc); return 0; } - if (!quic_tls_derive_keys(rx->aead, NULL, rx->md, qc->version, + if (!quic_tls_derive_keys(rx->aead, NULL, rx->md, ver, nxt_rx->key, nxt_rx->keylen, nxt_rx->iv, nxt_rx->ivlen, NULL, 0, nxt_rx->secret, nxt_rx->secretlen)) { @@ -767,13 +776,13 @@ static int quic_tls_key_update(struct quic_conn *qc) } /* Prepare new TX secrets */ - if (!quic_tls_sec_update(tx->md, qc->version, nxt_tx->secret, nxt_tx->secretlen, + if (!quic_tls_sec_update(tx->md, ver, nxt_tx->secret, nxt_tx->secretlen, tx->secret, tx->secretlen)) { TRACE_DEVEL("New TX secret update failed", QUIC_EV_CONN_RWSEC, qc); return 0; } - if (!quic_tls_derive_keys(tx->aead, NULL, tx->md, qc->version, + if (!quic_tls_derive_keys(tx->aead, NULL, tx->md, ver, nxt_tx->key, nxt_tx->keylen, nxt_tx->iv, nxt_tx->ivlen, NULL, 0, nxt_tx->secret, nxt_tx->secretlen)) { @@ -862,6 +871,8 @@ int ha_quic_set_encryption_secrets(SSL *ssl, enum ssl_encryption_level_t level, struct quic_tls_ctx *tls_ctx = &qc->els[ssl_to_quic_enc_level(level)].tls_ctx; const SSL_CIPHER *cipher = SSL_get_current_cipher(ssl); struct quic_tls_secrets *rx, *tx; + const struct quic_version *ver = + qc->negotiated_version ? qc->negotiated_version : qc->original_version; TRACE_ENTER(QUIC_EV_CONN_RWSEC, qc); BUG_ON(secret_len > QUIC_TLS_SECRET_LEN); @@ -882,7 +893,7 @@ int ha_quic_set_encryption_secrets(SSL *ssl, enum ssl_encryption_level_t level, rx->md = tx->md = tls_md(cipher); rx->hp = tx->hp = tls_hp(cipher); - if (!quic_tls_derive_keys(rx->aead, rx->hp, rx->md, qc->version, rx->key, rx->keylen, + if (!quic_tls_derive_keys(rx->aead, rx->hp, rx->md, ver, rx->key, rx->keylen, rx->iv, rx->ivlen, rx->hp_key, sizeof rx->hp_key, read_secret, secret_len)) { TRACE_DEVEL("RX key derivation failed", QUIC_EV_CONN_RWSEC, qc); @@ -904,7 +915,7 @@ int ha_quic_set_encryption_secrets(SSL *ssl, enum ssl_encryption_level_t level, if (!write_secret) goto out; - if (!quic_tls_derive_keys(tx->aead, tx->hp, tx->md, qc->version, tx->key, tx->keylen, + if (!quic_tls_derive_keys(tx->aead, tx->hp, tx->md, ver, tx->key, tx->keylen, tx->iv, tx->ivlen, tx->hp_key, sizeof tx->hp_key, write_secret, secret_len)) { TRACE_DEVEL("TX key derivation failed", QUIC_EV_CONN_RWSEC, qc); @@ -2851,7 +2862,7 @@ static int qc_prep_app_pkts(struct quic_conn *qc, struct qring *qr, end = pos + qc->path->mtu; } - pkt = qc_build_pkt(&pos, end, qel, frms, qc, 0, 0, + pkt = qc_build_pkt(&pos, end, qel, &qel->tls_ctx, frms, qc, NULL, 0, 0, QUIC_PACKET_TYPE_SHORT, probe, cc, &err); switch (err) { case -2: @@ -2939,6 +2950,8 @@ static int qc_prep_pkts(struct quic_conn *qc, struct qring *qr, while (end_buf - pos >= (int)qc->path->mtu + dg_headlen || prv_pkt) { int err, probe, cc; enum quic_pkt_type pkt_type; + struct quic_tls_ctx *tls_ctx; + const struct quic_version *ver; TRACE_POINT(QUIC_EV_CONN_PHPKTS, qc, qel); probe = 0; @@ -2974,8 +2987,20 @@ static int qc_prep_pkts(struct quic_conn *qc, struct qring *qr, } } - cur_pkt = qc_build_pkt(&pos, end, qel, frms, - qc, dglen, padding, pkt_type, probe, cc, &err); + if (qc->negotiated_version) { + ver = qc->negotiated_version; + if (qel == &qc->els[QUIC_TLS_ENC_LEVEL_INITIAL]) + tls_ctx = &qc->negotiated_ictx; + else + tls_ctx = &qel->tls_ctx; + } + else { + ver = qc->original_version; + tls_ctx = &qel->tls_ctx; + } + + cur_pkt = qc_build_pkt(&pos, end, qel, tls_ctx, frms, + qc, ver, dglen, padding, pkt_type, probe, cc, &err); switch (err) { case -2: goto err; @@ -4173,6 +4198,7 @@ static void quic_conn_release(struct quic_conn *qc) quic_tls_ctx_secs_free(&qc->els[i].tls_ctx); quic_conn_enc_level_uninit(&qc->els[i]); } + quic_tls_ctx_secs_free(&qc->negotiated_ictx); app_tls_ctx = &qc->els[QUIC_TLS_ENC_LEVEL_APP].tls_ctx; pool_free(pool_head_quic_tls_secret, app_tls_ctx->rx.secret); @@ -4401,8 +4427,8 @@ static struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4, qc->els[i].pktns = &qc->pktns[quic_tls_pktns(i)]; } - qc->version = qv; - qc->tps_tls_ext = (qc->version->num & 0xff000000) == 0xff000000 ? + qc->original_version = qv; + qc->tps_tls_ext = (qc->original_version->num & 0xff000000) == 0xff000000 ? TLS_EXTENSION_QUIC_TRANSPORT_PARAMETERS_DRAFT: TLS_EXTENSION_QUIC_TRANSPORT_PARAMETERS; /* TX part. */ @@ -4444,19 +4470,13 @@ static struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4, odcid->data, odcid->len, token)) goto err; - qc->enc_params_len = - quic_transport_params_encode(qc->enc_params, - qc->enc_params + sizeof qc->enc_params, - &qc->rx.params, 1); - if (!qc->enc_params_len) - goto err; - if (qc_conn_alloc_ssl_ctx(qc) || !quic_conn_init_timer(qc) || !quic_conn_init_idle_timer_task(qc)) goto err; - if (!qc_new_isecs(qc, dcid->data, dcid->len, 1)) + if (!qc_new_isecs(qc, &qc->els[QUIC_TLS_ENC_LEVEL_INITIAL].tls_ctx, + qc->original_version, dcid->data, dcid->len, 1)) goto err; TRACE_LEAVE(QUIC_EV_CONN_INIT, qc); @@ -4963,8 +4983,8 @@ static int quic_generate_retry_token(unsigned char *buf, size_t len, * of client source connection ID. * Return 1 if succeeded, 0 if not. */ -static int quic_retry_token_check(unsigned char *token, size_t tokenlen, - const uint32_t version, +static int quic_retry_token_check(const unsigned char *token, size_t tokenlen, + const struct quic_version *qv, struct quic_cid *odcid, const struct quic_cid *dcid, struct quic_conn *qc, @@ -4974,7 +4994,7 @@ static int quic_retry_token_check(unsigned char *token, size_t tokenlen, unsigned char aad[sizeof(uint32_t) + sizeof(in_port_t) + sizeof(struct in6_addr) + QUIC_HAP_CID_LEN]; size_t aadlen; - unsigned char *salt; + const unsigned char *salt; unsigned char key[QUIC_TLS_KEY_LEN]; unsigned char iv[QUIC_TLS_IV_LEN]; const unsigned char *sec = (const unsigned char *)global.cluster_secret; @@ -4985,7 +5005,7 @@ static int quic_retry_token_check(unsigned char *token, size_t tokenlen, if (sizeof buf < tokenlen) return 0; - aadlen = quic_generate_retry_token_aad(aad, version, dcid, addr); + aadlen = quic_generate_retry_token_aad(aad, qv->num, dcid, addr); salt = token + tokenlen - QUIC_RETRY_TOKEN_SALTLEN; if (!quic_tls_derive_retry_token_secret(EVP_sha256(), key, sizeof key, iv, sizeof iv, salt, QUIC_RETRY_TOKEN_SALTLEN, sec, seclen)) { @@ -5154,8 +5174,7 @@ static int qc_ssl_sess_init(struct quic_conn *qc, SSL_CTX *ssl_ctx, SSL **ssl, } if (!SSL_set_quic_method(*ssl, &ha_quic_method) || - !SSL_set_ex_data(*ssl, ssl_qc_app_data_index, qc) || - !SSL_set_quic_transport_params(*ssl, qc->enc_params, qc->enc_params_len)) { + !SSL_set_ex_data(*ssl, ssl_qc_app_data_index, qc)) { SSL_free(*ssl); *ssl = NULL; if (!retry--) @@ -5172,6 +5191,61 @@ static int qc_ssl_sess_init(struct quic_conn *qc, SSL_CTX *ssl_ctx, SSL **ssl, return -1; } +/* Finalize QUIC connection: + * - initialize the Initial QUIC TLS context for negotiated version, + * - derive the secrets for this context, + * - encode the transport parameters to be sent, + * - set them into the TLS stack, + * - initialize ->max_ack_delay and max_idle_timeout, + * + * MUST be called after having received the remote transport parameters. + * Return 1 if succeeded, 0 if not. + */ +int qc_conn_finalize(struct quic_conn *qc, int server) +{ + struct quic_transport_params *tx_tp = &qc->tx.params; + struct quic_transport_params *rx_tp = &qc->rx.params; + const struct quic_version *ver; + + if (tx_tp->version_information.negotiated_version && + tx_tp->version_information.negotiated_version != qc->original_version) { + qc->negotiated_version = + qc->tx.params.version_information.negotiated_version; + if (!qc_new_isecs(qc, &qc->negotiated_ictx, qc->negotiated_version, + qc->odcid.data, qc->odcid.len, !server)) + return 0; + + ver = qc->negotiated_version; + } + else { + ver = qc->original_version; + } + + qc->enc_params_len = + quic_transport_params_encode(qc->enc_params, + qc->enc_params + sizeof qc->enc_params, + &qc->rx.params, ver, 1); + if (!qc->enc_params_len) + return 0; + + if (!SSL_set_quic_transport_params(qc->xprt_ctx->ssl, qc->enc_params, qc->enc_params_len)) + return 0; + + if (tx_tp->max_ack_delay) + qc->max_ack_delay = tx_tp->max_ack_delay; + + if (tx_tp->max_idle_timeout && rx_tp->max_idle_timeout) + qc->max_idle_timeout = + QUIC_MIN(tx_tp->max_idle_timeout, rx_tp->max_idle_timeout); + else + qc->max_idle_timeout = + QUIC_MAX(tx_tp->max_idle_timeout, rx_tp->max_idle_timeout); + + TRACE_PROTO("\nTX(remote) transp. params.", QUIC_EV_TRANSP_PARAMS, qc, tx_tp); + + return 1; +} + /* Allocate the ssl_sock_ctx from connection . This creates the tasklet * used to process received packets. The allocated context is stored in * . @@ -5269,6 +5343,7 @@ static void qc_lstnr_pkt_rcv(unsigned char *buf, const unsigned char *end, size_t b_cspace; struct quic_enc_level *qel; uint32_t version; + const struct quic_version *qv = NULL; beg = buf; qc = NULL; @@ -5312,7 +5387,7 @@ static void qc_lstnr_pkt_rcv(unsigned char *buf, const unsigned char *end, if (long_header) { uint64_t len; struct quic_cid odcid; - const struct quic_version *qv; + int check_token = 0; if (!quic_packet_read_long_header(&buf, end, pkt)) { TRACE_PROTO("Packet dropped", QUIC_EV_CONN_LPKT); @@ -5350,11 +5425,11 @@ static void qc_lstnr_pkt_rcv(unsigned char *buf, const unsigned char *end, if (!qv) { /* unsupported version, send Negotiation packet */ if (send_version_negotiation(l->rx.fd, &dgram->saddr, pkt)) { - TRACE_PROTO("Error on Version Negotiation sending", QUIC_EV_CONN_LPKT); + TRACE_PROTO("VN packet not sent", QUIC_EV_CONN_LPKT); goto err; } - TRACE_PROTO("Unsupported QUIC version, send Version Negotiation packet", QUIC_EV_CONN_LPKT); + TRACE_PROTO("VN packet sent", QUIC_EV_CONN_LPKT); goto err; } @@ -5366,7 +5441,8 @@ static void qc_lstnr_pkt_rcv(unsigned char *buf, const unsigned char *end, if (!quic_dec_int(&token_len, (const unsigned char **)&buf, end) || end - buf < token_len) { - TRACE_PROTO("Packet dropped", QUIC_EV_CONN_LPKT); + TRACE_PROTO("Packet dropped", + QUIC_EV_CONN_LPKT, NULL, NULL, NULL, qv); goto drop; } @@ -5376,9 +5452,11 @@ static void qc_lstnr_pkt_rcv(unsigned char *buf, const unsigned char *end, if (global.cluster_secret) { if (!token_len) { if (l->bind_conf->options & BC_O_QUIC_FORCE_RETRY) { - TRACE_PROTO("Initial without token, sending retry", QUIC_EV_CONN_LPKT); + TRACE_PROTO("Initial without token, sending retry", + QUIC_EV_CONN_LPKT, NULL, NULL, NULL, qv); if (send_retry(l->rx.fd, &dgram->saddr, pkt, qv)) { - TRACE_PROTO("Error during Retry generation", QUIC_EV_CONN_LPKT); + TRACE_PROTO("Error during Retry generation", + QUIC_EV_CONN_LPKT, NULL, NULL, NULL, qv); goto err; } @@ -5387,24 +5465,7 @@ static void qc_lstnr_pkt_rcv(unsigned char *buf, const unsigned char *end, } } else { - if (*buf == QUIC_TOKEN_FMT_RETRY) { - if (!quic_retry_token_check(buf, token_len, version, &odcid, - &pkt->scid, qc, &dgram->saddr)) { - HA_ATOMIC_INC(&prx_counters->retry_error); - TRACE_PROTO("Wrong retry token", QUIC_EV_CONN_LPKT); - /* TODO: RFC 9000 8.1.2 A server SHOULD immediately close the connection - * with an INVALID_TOKEN error. - */ - goto drop; - } - - HA_ATOMIC_INC(&prx_counters->retry_validated); - } - else { - /* TODO: New token check */ - TRACE_PROTO("Packet dropped", QUIC_EV_CONN_LPKT); - goto drop; - } + check_token = 1; } } @@ -5414,14 +5475,16 @@ static void qc_lstnr_pkt_rcv(unsigned char *buf, const unsigned char *end, } else if (pkt->type != QUIC_PACKET_TYPE_0RTT) { if (pkt->dcid.len != QUIC_HAP_CID_LEN) { - TRACE_PROTO("Packet dropped", QUIC_EV_CONN_LPKT); + TRACE_PROTO("Packet dropped", + QUIC_EV_CONN_LPKT, NULL, NULL, NULL, qv); goto drop; } } if (!quic_dec_int(&len, (const unsigned char **)&buf, end) || end - buf < len) { - TRACE_PROTO("Packet dropped", QUIC_EV_CONN_LPKT); + TRACE_PROTO("Packet dropped", + QUIC_EV_CONN_LPKT, NULL, NULL, NULL, qv); goto drop; } @@ -5431,20 +5494,44 @@ static void qc_lstnr_pkt_rcv(unsigned char *buf, const unsigned char *end, goto drop_no_conn; qc = retrieve_qc_conn_from_cid(pkt, l, &dgram->saddr); + if (check_token && pkt->token) { + if (*pkt->token == QUIC_TOKEN_FMT_RETRY) { + const struct quic_version *ver = qc ? qc->original_version : qv; + if (!quic_retry_token_check(pkt->token, pkt->token_len, ver, &odcid, + &pkt->scid, qc, &dgram->saddr)) { + HA_ATOMIC_INC(&prx_counters->retry_error); + TRACE_PROTO("Wrong retry token", + QUIC_EV_CONN_LPKT, qc, NULL, NULL, qv); + /* TODO: RFC 9000 8.1.2 A server SHOULD immediately close the connection + * with an INVALID_TOKEN error. + */ + goto drop; + } + + HA_ATOMIC_INC(&prx_counters->retry_validated); + } + else { + /* TODO: New token check */ + TRACE_PROTO("Packet dropped", QUIC_EV_CONN_LPKT, qc, NULL, NULL, qv); + goto drop; + } + } if (!qc) { int ipv4; struct ebmb_node *n = NULL; if (pkt->type != QUIC_PACKET_TYPE_INITIAL) { - TRACE_PROTO("Non Initial packet", QUIC_EV_CONN_LPKT); + TRACE_PROTO("Non Initial packet", QUIC_EV_CONN_LPKT, NULL, NULL, NULL, qv); goto drop; } if (global.cluster_secret && !pkt->token_len && !(l->bind_conf->options & BC_O_QUIC_FORCE_RETRY) && HA_ATOMIC_LOAD(&prx_counters->half_open_conn) >= global.tune.quic_retry_threshold) { - TRACE_PROTO("Initial without token, sending retry", QUIC_EV_CONN_LPKT); + TRACE_PROTO("Initial without token, sending retry", + QUIC_EV_CONN_LPKT, NULL, NULL, NULL, qv); if (send_retry(l->rx.fd, &dgram->saddr, pkt, qv)) { - TRACE_PROTO("Error during Retry generation", QUIC_EV_CONN_LPKT); + TRACE_PROTO("Error during Retry generation", + QUIC_EV_CONN_LPKT, NULL, NULL, NULL, qv); goto err; } @@ -5459,7 +5546,8 @@ static void qc_lstnr_pkt_rcv(unsigned char *buf, const unsigned char *end, * value. This Destination Connection ID MUST be at least 8 bytes in length. */ if (pkt->dcid.len < QUIC_ODCID_MINLEN) { - TRACE_PROTO("dropped packet", QUIC_EV_CONN_LPKT); + TRACE_PROTO("dropped packet", + QUIC_EV_CONN_LPKT, NULL, NULL, NULL, qv); goto drop; } @@ -5543,7 +5631,8 @@ static void qc_lstnr_pkt_rcv(unsigned char *buf, const unsigned char *end, } /* Skip the entire datagram */ pkt->len = end - beg; - TRACE_PROTO("Closing state connection", QUIC_EV_CONN_LPKT, pkt->qc); + TRACE_PROTO("Closing state connection", + QUIC_EV_CONN_LPKT, pkt->qc, NULL, NULL, qv); goto drop; } @@ -5556,7 +5645,7 @@ static void qc_lstnr_pkt_rcv(unsigned char *buf, const unsigned char *end, if (first_pkt && !quic_peer_validated_addr(qc) && qc->flags & QUIC_FL_CONN_ANTI_AMPLIFICATION_REACHED) { TRACE_PROTO("PTO timer must be armed after anti-amplication was reached", - QUIC_EV_CONN_LPKT, qc); + QUIC_EV_CONN_LPKT, qc, NULL, NULL, qv); /* Reset the anti-amplification bit. It will be set again * when sending the next packet if reached again. */ @@ -5568,7 +5657,8 @@ static void qc_lstnr_pkt_rcv(unsigned char *buf, const unsigned char *end, dgram->qc = qc; if (qc->err_code) { - TRACE_PROTO("Connection error", QUIC_EV_CONN_LPKT, qc); + TRACE_PROTO("Connection error", + QUIC_EV_CONN_LPKT, qc, NULL, NULL, qv); goto out; } @@ -5578,7 +5668,8 @@ static void qc_lstnr_pkt_rcv(unsigned char *buf, const unsigned char *end, if (b_cspace < pkt->len) { /* Do not consume buf if space not at the end. */ if (b_tail(&qc->rx.buf) + b_cspace < b_wrap(&qc->rx.buf)) { - TRACE_PROTO("Packet dropped", QUIC_EV_CONN_LPKT, qc); + TRACE_PROTO("Packet dropped", + QUIC_EV_CONN_LPKT, qc, NULL, NULL, qv); goto err; } @@ -5589,18 +5680,19 @@ static void qc_lstnr_pkt_rcv(unsigned char *buf, const unsigned char *end, } b_add(&qc->rx.buf, b_cspace); if (b_contig_space(&qc->rx.buf) < pkt->len) { - TRACE_PROTO("Too big packet", QUIC_EV_CONN_LPKT, qc, pkt, &pkt->len); + TRACE_PROTO("Too big packet", + QUIC_EV_CONN_LPKT, qc, pkt, &pkt->len, qv); qc_list_all_rx_pkts(qc); goto drop; } } if (!qc_try_rm_hp(qc, pkt, payload, beg, end, &qel)) { - TRACE_PROTO("Packet dropped", QUIC_EV_CONN_LPKT, qc); + TRACE_PROTO("Packet dropped", QUIC_EV_CONN_LPKT, qc, NULL, NULL, qv); goto drop; } - TRACE_PROTO("New packet", QUIC_EV_CONN_LPKT, qc, pkt); + TRACE_PROTO("New packet", QUIC_EV_CONN_LPKT, qc, pkt, NULL, qv); if (pkt->aad_len) qc_pkt_insert(pkt, qel); out: @@ -5617,7 +5709,7 @@ static void qc_lstnr_pkt_rcv(unsigned char *buf, const unsigned char *end, drop_no_conn: if (drop_no_conn) HA_ATOMIC_INC(&prx_counters->dropped_pkt); - TRACE_LEAVE(QUIC_EV_CONN_LPKT, qc ? qc : NULL, pkt); + TRACE_LEAVE(QUIC_EV_CONN_LPKT, qc ? qc : NULL, pkt, NULL, qv); return; @@ -5636,24 +5728,25 @@ static void qc_lstnr_pkt_rcv(unsigned char *buf, const unsigned char *end, if (!pkt->len) pkt->len = end - beg; TRACE_DEVEL("Leaving in error", QUIC_EV_CONN_LPKT, - qc ? qc : NULL, pkt); + qc ? qc : NULL, pkt, NULL, qv); } /* This function builds into buffer a QUIC long packet header. * Return 1 if enough room to build this header, 0 if not. */ static int quic_build_packet_long_header(unsigned char **buf, const unsigned char *end, - int type, size_t pn_len, struct quic_conn *conn) + int type, size_t pn_len, + struct quic_conn *conn, const struct quic_version *ver) { - if (end - *buf < sizeof conn->version + conn->dcid.len + conn->scid.len + 3) + if (end - *buf < sizeof ver->num + conn->dcid.len + conn->scid.len + 3) return 0; - type = quic_pkt_type(type, conn->version->num); + type = quic_pkt_type(type, ver->num); /* #0 byte flags */ *(*buf)++ = QUIC_PACKET_FIXED_BIT | QUIC_PACKET_LONG_HEADER_BIT | (type << QUIC_PACKET_TYPE_SHIFT) | (pn_len - 1); /* Version */ - quic_write_uint32(buf, end, conn->version->num); + quic_write_uint32(buf, end, ver->num); *(*buf)++ = conn->dcid.len; /* Destination connection ID */ if (conn->dcid.len) { @@ -6021,7 +6114,7 @@ static int qc_do_build_pkt(unsigned char *pos, const unsigned char *end, int64_t pn, size_t *pn_len, unsigned char **buf_pn, int padding, int cc, int probe, struct quic_enc_level *qel, struct quic_conn *qc, - struct list *frms) + const struct quic_version *ver, struct list *frms) { unsigned char *beg; size_t len, len_sz, len_frms, padding_len; @@ -6064,7 +6157,7 @@ static int qc_do_build_pkt(unsigned char *pos, const unsigned char *end, if ((pkt->type == QUIC_PACKET_TYPE_SHORT && !quic_build_packet_short_header(&pos, end, *pn_len, qc, qel->tls_ctx.flags)) || (pkt->type != QUIC_PACKET_TYPE_SHORT && - !quic_build_packet_long_header(&pos, end, pkt->type, *pn_len, qc))) + !quic_build_packet_long_header(&pos, end, pkt->type, *pn_len, qc, ver))) goto no_room; /* XXX FIXME XXX Encode the token length (0) for an Initial packet. */ @@ -6273,8 +6366,10 @@ static inline void quic_tx_packet_init(struct quic_tx_packet *pkt, int type) */ static struct quic_tx_packet *qc_build_pkt(unsigned char **pos, const unsigned char *buf_end, - struct quic_enc_level *qel, struct list *frms, - struct quic_conn *qc, size_t dglen, int padding, + struct quic_enc_level *qel, + struct quic_tls_ctx *tls_ctx, struct list *frms, + struct quic_conn *qc, const struct quic_version *ver, + size_t dglen, int padding, int pkt_type, int probe, int cc, int *err) { /* The pointer to the packet number field. */ @@ -6282,7 +6377,6 @@ static struct quic_tx_packet *qc_build_pkt(unsigned char **pos, unsigned char *beg, *end, *payload; int64_t pn; size_t pn_len, payload_len, aad_len; - struct quic_tls_ctx *tls_ctx; struct quic_tx_packet *pkt; TRACE_ENTER(QUIC_EV_CONN_HPKT, qc, NULL, qel); @@ -6301,7 +6395,7 @@ static struct quic_tx_packet *qc_build_pkt(unsigned char **pos, pn = qel->pktns->tx.next_pn + 1; if (!qc_do_build_pkt(*pos, buf_end, dglen, pkt, pn, &pn_len, &buf_pn, - padding, cc, probe, qel, qc, frms)) { + padding, cc, probe, qel, qc, ver, frms)) { *err = -1; goto err; } @@ -6311,7 +6405,6 @@ static struct quic_tx_packet *qc_build_pkt(unsigned char **pos, payload_len = end - payload; aad_len = payload - beg; - tls_ctx = &qel->tls_ctx; if (!quic_packet_encrypt(payload, payload_len, beg, aad_len, pn, tls_ctx, qc)) { *err = -2; goto err;