#define QUIC_FL_CONN_IPKTNS_DCD (1U << 15) /* Initial packet number space discarded */
#define QUIC_FL_CONN_HPKTNS_DCD (1U << 16) /* Handshake packet number space discarded */
#define QUIC_FL_CONN_PEER_VALIDATED_ADDR (1U << 17) /* Peer address is considered as validated for this connection. */
+#define QUIC_FL_CONN_NO_TOKEN_RCVD (1U << 18) /* Client dit not send any token */
+#define QUIC_FL_CONN_SEND_RETRY (1U << 19) /* A send retry packet must be sent */
/* gap here */
#define QUIC_FL_CONN_TO_KILL (1U << 24) /* Unusable connection, to be killed */
#define QUIC_FL_CONN_TX_TP_RECEIVED (1U << 25) /* Peer transport parameters have been received (used for the transmitting part) */
#include <haproxy/quic_sock.h>
#include <haproxy/quic_stats.h>
#include <haproxy/quic_stream.h>
+#include <haproxy/quic_token.h>
#include <haproxy/quic_tp.h>
#include <haproxy/quic_trace.h>
#include <haproxy/quic_tx.h>
}
LIST_APPEND(&frm_list, &frm->list);
+
+#ifdef HAVE_SSL_0RTT_QUIC
+ if (qc->li->bind_conf->ssl_conf.early_data) {
+ size_t new_token_frm_len;
+
+ frm = qc_frm_alloc(QUIC_FT_NEW_TOKEN);
+ if (!frm) {
+ TRACE_ERROR("frame allocation error", QUIC_EV_CONN_IO_CB, qc);
+ goto leave;
+ }
+
+ new_token_frm_len =
+ quic_generate_token(frm->new_token.data,
+ sizeof(frm->new_token.data), &qc->peer_addr);
+ if (!new_token_frm_len) {
+ TRACE_ERROR("token generation failed", QUIC_EV_CONN_IO_CB, qc);
+ goto leave;
+ }
+
+ BUG_ON(new_token_frm_len != sizeof(frm->new_token.data));
+ frm->new_token.len = new_token_frm_len;
+ LIST_APPEND(&frm_list, &frm->list);
+ }
+#endif
}
/* Initialize <max> connection IDs minus one: there is
HA_ATOMIC_AND(&tl->state, ~TASK_HEAVY);
}
+ if (qc->flags & QUIC_FL_CONN_TO_KILL) {
+ TRACE_DEVEL("connection to be killed", QUIC_EV_CONN_PHPKTS, qc);
+ goto out;
+ }
+
/* Retranmissions */
if (qc->flags & QUIC_FL_CONN_RETRANS_NEEDED) {
TRACE_DEVEL("retransmission needed", QUIC_EV_CONN_PHPKTS, qc);
quic_nictx_free(qc);
}
- if ((qc->flags & QUIC_FL_CONN_CLOSING) && qc->mux_state != QC_MUX_READY) {
+ if (qc->flags & QUIC_FL_CONN_SEND_RETRY) {
+ struct quic_counters *prx_counters;
+ struct proxy *prx = qc->li->bind_conf->frontend;
+ struct quic_rx_packet pkt = {
+ .scid = qc->dcid,
+ .dcid = qc->odcid,
+ };
+
+ prx_counters = EXTRA_COUNTERS_GET(prx->extra_counters_fe, &quic_stats_module);
+ if (send_retry(qc->li->rx.fd, &qc->peer_addr, &pkt, qc->original_version)) {
+ TRACE_ERROR("Error during Retry generation",
+ QUIC_EV_CONN_LPKT, NULL, NULL, NULL, qc->original_version);
+ }
+ else
+ HA_ATOMIC_INC(&prx_counters->retry_sent);
+ }
+
+ if ((qc->flags & (QUIC_FL_CONN_CLOSING|QUIC_FL_CONN_TO_KILL)) &&
+ qc->mux_state != QC_MUX_READY) {
quic_conn_release(qc);
qc = NULL;
}
* for QUIC servers (or haproxy listeners).
* <dcid> is the destination connection ID, <scid> is the source connection ID.
* This latter <scid> CID as the same value on the wire as the one for <conn_id>
- * which is the first CID of this connection but a different internal representation used to build
+ * which is the first CID of this connection but a different internal
+ * representation used to build
* NEW_CONNECTION_ID frames. This is the responsibility of the caller to insert
* <conn_id> in the CIDs tree for this connection (qc->cids).
- * <token> is the token found to be used for this connection with <token_len> as
- * length. Endpoints addresses are specified via <local_addr> and <peer_addr>.
+ * <token> is a boolean denoting if a token was received for this connection
+ * from an Initial packet.
+ * <token_odcid> is the original destination connection ID which was embedded
+ * into the Retry token sent to the client before instantiated this connection.
+ * Endpoints addresses are specified via <local_addr> and <peer_addr>.
* Returns the connection if succeeded, NULL if not.
*/
struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
qc->prx_counters = EXTRA_COUNTERS_GET(prx->extra_counters_fe,
&quic_stats_module);
qc->flags = QUIC_FL_CONN_LISTENER;
+ /* Mark this connection as having not received any token when 0-RTT is enabled. */
+ if (l->bind_conf->ssl_conf.early_data && !token)
+ qc->flags |= QUIC_FL_CONN_NO_TOKEN_RCVD;
qc->state = QUIC_HS_ST_SERVER_INITIAL;
/* Copy the client original DCID. */
qc->odcid = *dcid;
/* If connection is instantiated due to an INITIAL packet with an
* already checked token, consider the peer address as validated.
*/
- if (token_odcid->len) {
+ if (token) {
TRACE_STATE("validate peer address due to initial token",
QUIC_EV_CONN_INIT, qc);
qc->flags |= QUIC_FL_CONN_PEER_VALIDATED_ADDR;
TRACE_ENTER(QUIC_EV_CONN_LPKT, qc);
/* The caller must ensure this. */
- BUG_ON(!pkt->token_len);
+ BUG_ON(!pkt->token_len || *pkt->token != QUIC_TOKEN_FMT_RETRY);
prx = l->bind_conf->frontend;
prx_counters = EXTRA_COUNTERS_GET(prx->extra_counters_fe, &quic_stats_module);
- if (*pkt->token != QUIC_TOKEN_FMT_RETRY) {
- /* TODO: New token check */
- TRACE_PROTO("Packet dropped", QUIC_EV_CONN_LPKT, qc, NULL, NULL, pkt->version);
- goto leave;
- }
-
if (sizeof buf < tokenlen) {
TRACE_ERROR("too short buffer", QUIC_EV_CONN_LPKT, qc);
goto err;
#include <haproxy/quic_stream.h>
#include <haproxy/quic_ssl.h>
#include <haproxy/quic_tls.h>
+#include <haproxy/quic_token.h>
#include <haproxy/quic_trace.h>
#include <haproxy/quic_tx.h>
#include <haproxy/ssl_sock.h>
return pos == end;
}
+/* Validate the token, retry or not (provided by NEW_TOKEN) parsed into
+ * <pkt> RX packet from <dgram> datagram.
+ * Return 1 if succeded, 0 if not.
+ */
+static inline int quic_token_validate(struct quic_rx_packet *pkt,
+ struct quic_dgram *dgram,
+ struct listener *l, struct quic_conn *qc,
+ struct quic_cid *odcid)
+{
+ int ret = 0;
+
+ TRACE_ENTER(QUIC_EV_CONN_LPKT, qc);
+
+ switch (*pkt->token) {
+ case QUIC_TOKEN_FMT_RETRY:
+ ret = quic_retry_token_check(pkt, dgram, l, qc, odcid);
+ break;
+ case QUIC_TOKEN_FMT_NEW:
+ ret = quic_token_check(pkt, dgram, qc);
+ if (!ret) {
+ /* Fallback to a retry token in case of any error. */
+ dgram->flags |= QUIC_DGRAM_FL_SEND_RETRY;
+ }
+ break;
+ default:
+ TRACE_PROTO("Packet dropped", QUIC_EV_CONN_LPKT, qc, NULL, NULL, pkt->version);
+ break;
+ }
+
+ if (!ret)
+ goto err;
+
+ ret = 1;
+ leave:
+ TRACE_LEAVE(QUIC_EV_CONN_LPKT, qc);
+ return ret;
+ err:
+ TRACE_DEVEL("leaving in error", QUIC_EV_CONN_LPKT, qc);
+ goto leave;
+}
+
/* Find the associated connection to the packet <pkt> or create a new one if
* this is an Initial packet. <dgram> is the datagram containing the packet and
* <l> is the listener instance on which it was received.
}
if (pkt->token_len) {
- /* Validate the token only when connection is unknown. */
- if (!quic_retry_token_check(pkt, dgram, l, qc, &token_odcid))
+ TRACE_PROTO("Initial with token", QUIC_EV_CONN_LPKT, NULL, NULL, NULL, pkt->version);
+ /* Validate the token, retry or not only when connection is unknown. */
+ if (!quic_token_validate(pkt, dgram, l, qc, &token_odcid)) {
+ if (dgram->flags & QUIC_DGRAM_FL_SEND_RETRY) {
+ if (send_retry(l->rx.fd, &dgram->saddr, pkt, pkt->version)) {
+ TRACE_ERROR("Error during Retry generation",
+ QUIC_EV_CONN_LPKT, NULL, NULL, NULL, pkt->version);
+ }
+ else
+ HA_ATOMIC_INC(&prx_counters->retry_sent);
+
+ goto out;
+ }
+
goto err;
+ }
+ }
+ else {
+ TRACE_PROTO("Initial without token", QUIC_EV_CONN_LPKT, NULL, NULL, NULL, pkt->version);
}
if (!quic_init_exec_rules(l, dgram)) {
TRACE_ENTER(QUIC_EV_CONN_ADDDATA, qc);
+ TRACE_PROTO("ha_quic_add_handshake_data() called", QUIC_EV_CONN_IO_CB, qc, NULL, ssl);
+
+#ifdef HAVE_SSL_0RTT_QUIC
+ /* Detect asap if some 0-RTT data were accepted for this connection.
+ * If this is the case and no token was provided, interrupt the useless
+ * secrets derivations. A Retry packet must be sent, and this connection
+ * must be killed.
+ * Note that QUIC_FL_CONN_NO_TOKEN_RCVD is possibly set only for when 0-RTT is
+ * enabled for the connection.
+ */
+ if ((qc->flags & QUIC_FL_CONN_NO_TOKEN_RCVD) && qc_ssl_eary_data_accepted(ssl)) {
+ TRACE_PROTO("connection to be killed", QUIC_EV_CONN_ADDDATA, qc);
+ qc->flags |= QUIC_FL_CONN_TO_KILL|QUIC_FL_CONN_SEND_RETRY;
+ goto leave;
+ }
+#endif
+
if (qc->flags & QUIC_FL_CONN_TO_KILL) {
TRACE_PROTO("connection to be killed", QUIC_EV_CONN_ADDDATA, qc);
goto out;
state = qc->state;
if (state < QUIC_HS_ST_COMPLETE) {
ssl_err = SSL_do_handshake(ctx->ssl);
+ TRACE_PROTO("SSL_do_handshake() called", QUIC_EV_CONN_IO_CB, qc, NULL, ctx->ssl);
if (qc->flags & QUIC_FL_CONN_TO_KILL) {
- TRACE_DEVEL("connection to be killed", QUIC_EV_CONN_IO_CB, qc);
+ TRACE_DEVEL("connection to be killed", QUIC_EV_CONN_IO_CB, qc, &state, ctx->ssl);
goto leave;
}