From a5da31d186a2441c120b54202038168a9f3be34c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20L=C3=A9caille?= Date: Tue, 14 Dec 2021 19:44:14 +0100 Subject: [PATCH] MINOR: quic: Make xprt support 0-RTT. A client sends a 0-RTT data packet after an Initial one in the same datagram. We must be able to parse such packets just after having parsed the Initial packets. --- include/haproxy/quic_tls.h | 7 +++-- src/xprt_quic.c | 52 ++++++++++++++++++++++++-------------- 2 files changed, 38 insertions(+), 21 deletions(-) diff --git a/include/haproxy/quic_tls.h b/include/haproxy/quic_tls.h index 47946cc22..209576a4e 100644 --- a/include/haproxy/quic_tls.h +++ b/include/haproxy/quic_tls.h @@ -423,13 +423,16 @@ static inline int quic_tls_level_pkt_type(enum quic_tls_enc_level level) /* Set <*level> and <*next_level> depending on QUIC handshake state. */ static inline int quic_get_tls_enc_levels(enum quic_tls_enc_level *level, enum quic_tls_enc_level *next_level, - enum quic_handshake_state state) + enum quic_handshake_state state, int zero_rtt) { switch (state) { case QUIC_HS_ST_SERVER_INITIAL: case QUIC_HS_ST_CLIENT_INITIAL: *level = QUIC_TLS_ENC_LEVEL_INITIAL; - *next_level = QUIC_TLS_ENC_LEVEL_HANDSHAKE; + if (zero_rtt) + *next_level = QUIC_TLS_ENC_LEVEL_EARLY_DATA; + else + *next_level = QUIC_TLS_ENC_LEVEL_HANDSHAKE; break; case QUIC_HS_ST_SERVER_HANDSHAKE: case QUIC_HS_ST_CLIENT_HANDSHAKE: diff --git a/src/xprt_quic.c b/src/xprt_quic.c index b4669dba7..0dfd1082b 100644 --- a/src/xprt_quic.c +++ b/src/xprt_quic.c @@ -2415,7 +2415,7 @@ static int qc_prep_pkts(struct qring *qr, struct ssl_sock_ctx *ctx) TRACE_ENTER(QUIC_EV_CONN_PHPKTS, ctx->conn); qc = ctx->conn->qc; - if (!quic_get_tls_enc_levels(&tel, &next_tel, HA_ATOMIC_LOAD(&qc->state))) { + if (!quic_get_tls_enc_levels(&tel, &next_tel, HA_ATOMIC_LOAD(&qc->state), 0)) { TRACE_DEVEL("unknown enc. levels", QUIC_EV_CONN_PHPKTS, ctx->conn); goto err; } @@ -3082,7 +3082,7 @@ struct task *quic_conn_io_cb(struct task *t, void *context, unsigned int state) struct quic_enc_level *qel, *next_qel; struct quic_tls_ctx *tls_ctx; struct qring *qr; // Tx ring - int prev_st, st, force_ack; + int prev_st, st, force_ack, zero_rtt; ctx = context; qc = ctx->conn->qc; @@ -3090,8 +3090,9 @@ struct task *quic_conn_io_cb(struct task *t, void *context, unsigned int state) st = HA_ATOMIC_LOAD(&qc->state); TRACE_ENTER(QUIC_EV_CONN_HDSHK, ctx->conn, &st); ssl_err = SSL_ERROR_NONE; + zero_rtt = !MT_LIST_ISEMPTY(&qc->els[QUIC_TLS_ENC_LEVEL_EARLY_DATA].rx.pqpkts); start: - if (!quic_get_tls_enc_levels(&tel, &next_tel, st)) + if (!quic_get_tls_enc_levels(&tel, &next_tel, st, zero_rtt)) goto err; qel = &qc->els[tel]; @@ -3113,6 +3114,13 @@ struct task *quic_conn_io_cb(struct task *t, void *context, unsigned int state) if (!qc_treat_rx_pkts(qel, next_qel, ctx, force_ack)) goto err; + if (zero_rtt && next_qel && !MT_LIST_ISEMPTY(&next_qel->rx.pqpkts) && + (next_qel->tls_ctx.rx.flags & QUIC_FL_TLS_SECRETS_SET)) { + qel = next_qel; + next_qel = NULL; + goto next_level; + } + st = HA_ATOMIC_LOAD(&qc->state); if (st >= QUIC_HS_ST_COMPLETE && (prev_st == QUIC_HS_ST_SERVER_INITIAL || prev_st == QUIC_HS_ST_SERVER_HANDSHAKE)) { @@ -3466,7 +3474,9 @@ static inline int quic_packet_read_long_header(unsigned char **buf, const unsign /* Check that the length of this received DCID matches the CID lengths * of our implementation for non Initials packets only. */ - if (pkt->type != QUIC_PACKET_TYPE_INITIAL && dcid_len != QUIC_CID_LEN) + if (pkt->type != QUIC_PACKET_TYPE_INITIAL && + pkt->type != QUIC_PACKET_TYPE_0RTT && + dcid_len != QUIC_CID_LEN) return 0; memcpy(pkt->dcid.data, *buf, dcid_len); @@ -3929,7 +3939,8 @@ static ssize_t qc_lstnr_pkt_rcv(unsigned char **buf, const unsigned char *end, /* For Initial packets, and for servers (QUIC clients connections), * there is no Initial connection IDs storage. */ - if (pkt->type == QUIC_PACKET_TYPE_INITIAL) { + if (pkt->type == QUIC_PACKET_TYPE_INITIAL || + pkt->type == QUIC_PACKET_TYPE_0RTT) { uint64_t token_len; /* DCIDs of first packets coming from clients may have the same values. * Let's distinguish them concatenating the socket addresses to the DCIDs. @@ -3937,20 +3948,22 @@ static ssize_t qc_lstnr_pkt_rcv(unsigned char **buf, const unsigned char *end, quic_cid_saddr_cat(&pkt->dcid, saddr); cids = &l->rx.odcids; - if (!quic_dec_int(&token_len, (const unsigned char **)buf, end) || - end - *buf < token_len) { - TRACE_PROTO("Packet dropped", QUIC_EV_CONN_LPKT); - goto err; - } + if (pkt->type == QUIC_PACKET_TYPE_INITIAL) { + if (!quic_dec_int(&token_len, (const unsigned char **)buf, end) || + end - *buf < token_len) { + TRACE_PROTO("Packet dropped", QUIC_EV_CONN_LPKT); + goto err; + } - /* XXX TO DO XXX 0 value means "the token is not present". - * A server which sends an Initial packet must not set the token. - * So, a client which receives an Initial packet with a token - * MUST discard the packet or generate a connection error with - * PROTOCOL_VIOLATION as type. - * The token must be provided in a Retry packet or NEW_TOKEN frame. - */ - pkt->token_len = token_len; + /* XXX TO DO XXX 0 value means "the token is not present". + * A server which sends an Initial packet must not set the token. + * So, a client which receives an Initial packet with a token + * MUST discard the packet or generate a connection error with + * PROTOCOL_VIOLATION as type. + * The token must be provided in a Retry packet or NEW_TOKEN frame. + */ + pkt->token_len = token_len; + } } else { if (pkt->dcid.len != QUIC_CID_LEN) { @@ -4069,7 +4082,8 @@ static ssize_t qc_lstnr_pkt_rcv(unsigned char **buf, const unsigned char *end, node = &qc->odcid_node; } else { - if (pkt->type == QUIC_PACKET_TYPE_INITIAL && cids == &l->rx.odcids) { + if ((pkt->type == QUIC_PACKET_TYPE_INITIAL || + pkt->type == QUIC_PACKET_TYPE_0RTT) && cids == &l->rx.odcids) { qc = ebmb_entry(node, struct quic_conn, odcid_node); } else {