MEDIUM: quic-be: Parse, store and reuse tokens provided by NEW_TOKEN

Add a per thread ist struct to srv_per_thread struct to store the QUIC token to
be reused for subsequent sessions.

Parse at packet level (from qc_parse_ptk_frms()) these tokens and store
them calling qc_try_store_new_token() newly implemented function. This is
this new function which does its best (may fail) to update the tokens.

Modify qc_do_build_pkt() to resend these tokens calling quic_enc_token()
implemented by this patch.
This commit is contained in:
Frederic Lecaille 2025-08-11 16:53:29 +02:00
parent 8f23d4d287
commit 80070fe51c
3 changed files with 103 additions and 12 deletions

View File

@ -276,6 +276,9 @@ struct srv_per_thread {
struct ceb_root *idle_conns; /* Shareable idle connections */ struct ceb_root *idle_conns; /* Shareable idle connections */
struct ceb_root *safe_conns; /* Safe idle connections */ struct ceb_root *safe_conns; /* Safe idle connections */
struct ceb_root *avail_conns; /* Connections in use, but with still new streams available */ struct ceb_root *avail_conns; /* Connections in use, but with still new streams available */
#ifdef USE_QUIC
struct ist quic_retry_token;
#endif
}; };
/* Each server will have one occurrence of this structure per thread group */ /* Each server will have one occurrence of this structure per thread group */

View File

@ -802,6 +802,35 @@ static inline unsigned int quic_ack_delay_ms(struct qf_ack *ack_frm,
return (ack_frm->ack_delay << conn->tx.params.ack_delay_exponent) / 1000; return (ack_frm->ack_delay << conn->tx.params.ack_delay_exponent) / 1000;
} }
/* Client only.
* Do its best to store <tok> token received from a NEW_TOKEN frame into <s>
* server cache for tokens to reuse.
*/
static inline void qc_try_store_new_token(struct server *s,
const unsigned char *tok,
size_t len)
{
struct ist *stok;
char *stok_ptr;
stok = &s->per_thr[tid].quic_retry_token;
stok_ptr = istptr(*stok);
if (len > istlen(*stok)) {
stok_ptr = realloc(stok_ptr, len);
if (stok_ptr)
s->per_thr[tid].quic_retry_token.ptr = stok_ptr;
else {
memset(istptr(*stok), 0, istlen(*stok));
istfree(stok);
}
}
if (stok_ptr) {
memcpy(stok_ptr, tok, len);
stok->len = len;
}
}
/* Parse all the frames of <pkt> QUIC packet for QUIC connection <qc> and <qel> /* Parse all the frames of <pkt> QUIC packet for QUIC connection <qc> and <qel>
* as encryption level. * as encryption level.
* Returns 1 if succeeded, 0 if failed. * Returns 1 if succeeded, 0 if failed.
@ -906,11 +935,16 @@ static int qc_parse_pkt_frms(struct quic_conn *qc, struct quic_rx_packet *pkt,
goto err; goto err;
} }
else { else {
/* TODO NEW_TOKEN not implemented on client side. struct qf_new_token *new_tok_frm = &frm->new_token;
* Note that for now token is not copied into <data> field
* of qf_new_token frame. See quic_parse_new_token_frame() if (!qc->conn) {
* for further explanations. TRACE_ERROR("reject NEW_TOKEN frame (connection closed",
*/ QUIC_EV_CONN_PRSHPKT, qc);
goto err;
}
qc_try_store_new_token(__objt_server(qc->conn->target),
new_tok_frm->r_data, new_tok_frm->len);
} }
break; break;
case QUIC_FT_STREAM_8 ... QUIC_FT_STREAM_F: case QUIC_FT_STREAM_8 ... QUIC_FT_STREAM_F:

View File

@ -1745,6 +1745,66 @@ static inline uint64_t quic_compute_ack_delay_us(unsigned int time_received,
return ((now_ms - time_received) * 1000) >> conn->tx.params.ack_delay_exponent; return ((now_ms - time_received) * 1000) >> conn->tx.params.ack_delay_exponent;
} }
/* Encode the <tok> token data field made of <toklen> bytes at <pos> buffer
* address. <end> points to the byte following the end of <*pos> buffer.
* Note that the type of the frame which embed this token is not encoded.
* Return 1 if succeeded, 0 if not.
*/
static inline int quic_do_enc_token(unsigned char **pos, const unsigned char *end,
const unsigned char *tok, size_t toklen)
{
if (!quic_enc_int(pos, end, toklen) || end - *pos <= toklen)
return 0;
if (toklen) {
memcpy(*pos, tok, toklen);
*pos += toklen;
}
return 1;
}
/* Encode a token depending on <qc> connection type (listener or not).
* For listeners, ony a null byte is encoded (no token).
* For clients, if a RETRY token has been received, it is encoded, if not, if a
* new token has been received (from NEW_TOKEN frame) and could be retrieved
* from cache, it is encoded, if not a null byte is encoded (no token).
*/
static inline int quic_enc_token(struct quic_conn *qc,
unsigned char **pos, const unsigned char *end)
{
int ret = 0;
const unsigned char *tok;
size_t toklen;
if (!qc_is_back(qc)) {
ret = quic_do_enc_token(pos, end, NULL, 0);
}
else if (qc->retry_token) {
tok = qc->retry_token;
toklen = qc->retry_token_len;
ret = quic_do_enc_token(pos, end, tok, toklen);
}
else if (!qc->conn) {
TRACE_ERROR("connection closed", QUIC_EV_CONN_TXPKT, qc);
goto out;
}
else {
struct server *s = __objt_server(qc->conn->target);
struct ist *stok;
stok = &s->per_thr[tid].quic_retry_token;
if (isttest(*stok))
ret = quic_do_enc_token(pos, end, (unsigned char *)istptr(*stok), istlen(*stok));
else
ret = quic_do_enc_token(pos, end, NULL, 0);
}
out:
return ret;
}
/* This function builds a clear packet from <pkt> information (its type) /* This function builds a clear packet from <pkt> information (its type)
* into a buffer with <pos> as position pointer and <qel> as QUIC TLS encryption * into a buffer with <pos> as position pointer and <qel> as QUIC TLS encryption
* level for <conn> QUIC connection and <qel> as QUIC TLS encryption level, * level for <conn> QUIC connection and <qel> as QUIC TLS encryption level,
@ -1828,14 +1888,8 @@ static int qc_do_build_pkt(unsigned char *pos, const unsigned char *end,
/* Encode the token length (0) for an Initial packet. */ /* Encode the token length (0) for an Initial packet. */
if (pkt->type == QUIC_PACKET_TYPE_INITIAL) { if (pkt->type == QUIC_PACKET_TYPE_INITIAL) {
if (!quic_enc_int(&pos, end, qc->retry_token_len) || if (!quic_enc_token(qc, &pos, end))
end - pos <= qc->retry_token_len)
goto no_room; goto no_room;
if (qc->retry_token_len) {
memcpy(pos, qc->retry_token, qc->retry_token_len);
pos += qc->retry_token_len;
}
} }
head_len = pos - beg; head_len = pos - beg;