Address validation using Retry packets.
The behaviour is toggled with the new directive "quic_retry on|off". QUIC token construction is made suitable for issuing with NEW_TOKEN.
This commit is contained in:
parent
92324d157c
commit
fbff14f583
6 changed files with 497 additions and 9 deletions
|
@ -123,6 +123,7 @@ struct ngx_quic_connection_s {
|
|||
unsigned closing:1;
|
||||
unsigned draining:1;
|
||||
unsigned key_phase:1;
|
||||
unsigned in_retry:1;
|
||||
};
|
||||
|
||||
|
||||
|
@ -154,6 +155,10 @@ static ngx_int_t ngx_quic_new_connection(ngx_connection_t *c, ngx_ssl_t *ssl,
|
|||
ngx_quic_tp_t *tp, ngx_quic_header_t *pkt,
|
||||
ngx_connection_handler_pt handler);
|
||||
static ngx_int_t ngx_quic_new_dcid(ngx_connection_t *c, ngx_str_t *odcid);
|
||||
static ngx_int_t ngx_quic_retry(ngx_connection_t *c);
|
||||
static ngx_int_t ngx_quic_new_token(ngx_connection_t *c, ngx_str_t *token);
|
||||
static ngx_int_t ngx_quic_validate_token(ngx_connection_t *c,
|
||||
ngx_quic_header_t *pkt);
|
||||
static ngx_int_t ngx_quic_init_connection(ngx_connection_t *c);
|
||||
static void ngx_quic_input_handler(ngx_event_t *rev);
|
||||
|
||||
|
@ -165,6 +170,8 @@ static ngx_int_t ngx_quic_close_streams(ngx_connection_t *c,
|
|||
|
||||
static ngx_int_t ngx_quic_input(ngx_connection_t *c, ngx_buf_t *b);
|
||||
static ngx_inline u_char *ngx_quic_skip_zero_padding(ngx_buf_t *b);
|
||||
static ngx_int_t ngx_quic_retry_input(ngx_connection_t *c,
|
||||
ngx_quic_header_t *pkt);
|
||||
static ngx_int_t ngx_quic_initial_input(ngx_connection_t *c,
|
||||
ngx_quic_header_t *pkt);
|
||||
static ngx_int_t ngx_quic_handshake_input(ngx_connection_t *c,
|
||||
|
@ -524,7 +531,8 @@ ngx_quic_run(ngx_connection_t *c, ngx_ssl_t *ssl, ngx_quic_tp_t *tp,
|
|||
return;
|
||||
}
|
||||
|
||||
ngx_add_timer(c->read, c->quic->tp.max_idle_timeout);
|
||||
ngx_add_timer(c->read, c->quic->in_retry ? NGX_QUIC_RETRY_TIMEOUT
|
||||
: c->quic->tp.max_idle_timeout);
|
||||
|
||||
c->read->handler = ngx_quic_input_handler;
|
||||
|
||||
|
@ -625,13 +633,6 @@ ngx_quic_new_connection(ngx_connection_t *c, ngx_ssl_t *ssl, ngx_quic_tp_t *tp,
|
|||
}
|
||||
ngx_memcpy(qc->scid.data, pkt->scid.data, qc->scid.len);
|
||||
|
||||
qc->token.len = pkt->token.len;
|
||||
qc->token.data = ngx_pnalloc(c->pool, qc->token.len);
|
||||
if (qc->token.data == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
ngx_memcpy(qc->token.data, pkt->token.data, qc->token.len);
|
||||
|
||||
keys = &c->quic->keys[ssl_encryption_initial];
|
||||
|
||||
if (ngx_quic_set_initial_secret(c->pool, &keys->client, &keys->server,
|
||||
|
@ -641,6 +642,10 @@ ngx_quic_new_connection(ngx_connection_t *c, ngx_ssl_t *ssl, ngx_quic_tp_t *tp,
|
|||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (tp->retry) {
|
||||
return ngx_quic_retry(c);
|
||||
}
|
||||
|
||||
pkt->secret = &keys->client;
|
||||
pkt->level = ssl_encryption_initial;
|
||||
pkt->plaintext = buf;
|
||||
|
@ -706,6 +711,270 @@ ngx_quic_new_dcid(ngx_connection_t *c, ngx_str_t *odcid)
|
|||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_quic_retry(ngx_connection_t *c)
|
||||
{
|
||||
ssize_t len;
|
||||
ngx_str_t res, token;
|
||||
ngx_quic_header_t pkt;
|
||||
u_char buf[NGX_QUIC_RETRY_BUFFER_SIZE];
|
||||
|
||||
if (ngx_quic_new_token(c, &token) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_memzero(&pkt, sizeof(ngx_quic_header_t));
|
||||
pkt.flags = NGX_QUIC_PKT_FIXED_BIT | NGX_QUIC_PKT_LONG | NGX_QUIC_PKT_RETRY;
|
||||
pkt.log = c->log;
|
||||
pkt.odcid = c->quic->odcid;
|
||||
pkt.dcid = c->quic->scid;
|
||||
pkt.scid = c->quic->dcid;
|
||||
pkt.token = token;
|
||||
|
||||
res.data = buf;
|
||||
|
||||
if (ngx_quic_encrypt(&pkt, NULL, &res) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
#ifdef NGX_QUIC_DEBUG_PACKETS
|
||||
ngx_quic_hexdump(c->log, "quic packet to send", res.data, res.len);
|
||||
#endif
|
||||
|
||||
len = c->send(c, res.data, res.len);
|
||||
if (len == NGX_ERROR || (size_t) len != res.len) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
c->quic->token = token;
|
||||
c->quic->tp.original_connection_id = c->quic->odcid;
|
||||
c->quic->in_retry = 1;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_quic_new_token(ngx_connection_t *c, ngx_str_t *token)
|
||||
{
|
||||
int len, iv_len;
|
||||
u_char *data, *p, *key, *iv;
|
||||
ngx_msec_t now;
|
||||
EVP_CIPHER_CTX *ctx;
|
||||
const EVP_CIPHER *cipher;
|
||||
struct sockaddr_in *sin;
|
||||
#if (NGX_HAVE_INET6)
|
||||
struct sockaddr_in6 *sin6;
|
||||
#endif
|
||||
u_char in[NGX_QUIC_MAX_TOKEN_SIZE];
|
||||
|
||||
switch (c->sockaddr->sa_family) {
|
||||
|
||||
#if (NGX_HAVE_INET6)
|
||||
case AF_INET6:
|
||||
sin6 = (struct sockaddr_in6 *) c->sockaddr;
|
||||
|
||||
len = sizeof(struct in6_addr);
|
||||
data = sin6->sin6_addr.s6_addr;
|
||||
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if (NGX_HAVE_UNIX_DOMAIN)
|
||||
case AF_UNIX:
|
||||
|
||||
len = ngx_min(c->addr_text.len, NGX_QUIC_MAX_TOKEN_SIZE - sizeof(now));
|
||||
data = c->addr_text.data;
|
||||
|
||||
break;
|
||||
#endif
|
||||
|
||||
default: /* AF_INET */
|
||||
sin = (struct sockaddr_in *) c->sockaddr;
|
||||
|
||||
len = sizeof(in_addr_t);
|
||||
data = (u_char *) &sin->sin_addr;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
p = ngx_cpymem(in, data, len);
|
||||
|
||||
now = ngx_current_msec;
|
||||
len += sizeof(now);
|
||||
ngx_memcpy(p, &now, sizeof(now));
|
||||
|
||||
cipher = EVP_aes_256_cbc();
|
||||
iv_len = EVP_CIPHER_iv_length(cipher);
|
||||
|
||||
token->len = iv_len + len + EVP_CIPHER_block_size(cipher);
|
||||
token->data = ngx_pnalloc(c->pool, token->len);
|
||||
if (token->data == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ctx = EVP_CIPHER_CTX_new();
|
||||
if (ctx == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
key = c->quic->tp.token_key;
|
||||
iv = token->data;
|
||||
|
||||
if (RAND_bytes(iv, iv_len) <= 0
|
||||
|| !EVP_EncryptInit_ex(ctx, cipher, NULL, key, iv))
|
||||
{
|
||||
EVP_CIPHER_CTX_free(ctx);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
token->len = iv_len;
|
||||
|
||||
if (EVP_EncryptUpdate(ctx, token->data + token->len, &len, in, len) != 1) {
|
||||
EVP_CIPHER_CTX_free(ctx);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
token->len += len;
|
||||
|
||||
if (EVP_EncryptFinal_ex(ctx, token->data + token->len, &len) <= 0) {
|
||||
EVP_CIPHER_CTX_free(ctx);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
token->len += len;
|
||||
|
||||
EVP_CIPHER_CTX_free(ctx);
|
||||
|
||||
#ifdef NGX_QUIC_DEBUG_PACKETS
|
||||
ngx_quic_hexdump(c->log, "quic new token", token->data, token->len);
|
||||
#endif
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_quic_validate_token(ngx_connection_t *c, ngx_quic_header_t *pkt)
|
||||
{
|
||||
int len, tlen, iv_len;
|
||||
u_char *key, *iv, *p, *data;
|
||||
ngx_msec_t msec;
|
||||
EVP_CIPHER_CTX *ctx;
|
||||
const EVP_CIPHER *cipher;
|
||||
struct sockaddr_in *sin;
|
||||
#if (NGX_HAVE_INET6)
|
||||
struct sockaddr_in6 *sin6;
|
||||
#endif
|
||||
ngx_quic_connection_t *qc;
|
||||
u_char tdec[NGX_QUIC_MAX_TOKEN_SIZE];
|
||||
|
||||
if (pkt->token.len == 0) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
qc = c->quic;
|
||||
|
||||
/* Retry token */
|
||||
|
||||
if (qc->token.len) {
|
||||
if (pkt->token.len != qc->token.len) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (ngx_memcmp(pkt->token.data, qc->token.data, pkt->token.len) != 0) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
/* NEW_TOKEN in a previous connection */
|
||||
|
||||
cipher = EVP_aes_256_cbc();
|
||||
key = c->quic->tp.token_key;
|
||||
iv = pkt->token.data;
|
||||
iv_len = EVP_CIPHER_iv_length(cipher);
|
||||
|
||||
/* sanity checks */
|
||||
|
||||
if (pkt->token.len < (size_t) iv_len + EVP_CIPHER_block_size(cipher)) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (pkt->token.len > (size_t) iv_len + NGX_QUIC_MAX_TOKEN_SIZE) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ctx = EVP_CIPHER_CTX_new();
|
||||
if (ctx == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (!EVP_DecryptInit_ex(ctx, cipher, NULL, key, iv)) {
|
||||
EVP_CIPHER_CTX_free(ctx);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
p = pkt->token.data + iv_len;
|
||||
len = pkt->token.len - iv_len;
|
||||
|
||||
if (EVP_DecryptUpdate(ctx, tdec, &len, p, len) != 1) {
|
||||
EVP_CIPHER_CTX_free(ctx);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (EVP_DecryptFinal_ex(ctx, tdec + len, &tlen) <= 0) {
|
||||
EVP_CIPHER_CTX_free(ctx);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
EVP_CIPHER_CTX_free(ctx);
|
||||
|
||||
switch (c->sockaddr->sa_family) {
|
||||
|
||||
#if (NGX_HAVE_INET6)
|
||||
case AF_INET6:
|
||||
sin6 = (struct sockaddr_in6 *) c->sockaddr;
|
||||
|
||||
len = sizeof(struct in6_addr);
|
||||
data = sin6->sin6_addr.s6_addr;
|
||||
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if (NGX_HAVE_UNIX_DOMAIN)
|
||||
case AF_UNIX:
|
||||
|
||||
len = ngx_min(c->addr_text.len, NGX_QUIC_MAX_TOKEN_SIZE - sizeof(msec));
|
||||
data = c->addr_text.data;
|
||||
|
||||
break;
|
||||
#endif
|
||||
|
||||
default: /* AF_INET */
|
||||
sin = (struct sockaddr_in *) c->sockaddr;
|
||||
|
||||
len = sizeof(in_addr_t);
|
||||
data = (u_char *) &sin->sin_addr;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (ngx_memcmp(tdec, data, len) != 0) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_memcpy(&msec, tdec + len, sizeof(msec));
|
||||
|
||||
if (ngx_current_msec - msec > NGX_QUIC_RETRY_LIFETIME) {
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_quic_init_connection(ngx_connection_t *c)
|
||||
{
|
||||
|
@ -776,6 +1045,7 @@ ngx_quic_input_handler(ngx_event_t *rev)
|
|||
b.start = buf;
|
||||
b.end = buf + sizeof(buf);
|
||||
b.pos = b.last = b.start;
|
||||
b.memory = 1;
|
||||
|
||||
c = rev->data;
|
||||
qc = c->quic;
|
||||
|
@ -1047,6 +1317,10 @@ ngx_quic_input(ngx_connection_t *c, ngx_buf_t *b)
|
|||
pkt.log = c->log;
|
||||
pkt.flags = p[0];
|
||||
|
||||
if (c->quic->in_retry) {
|
||||
return ngx_quic_retry_input(c, &pkt);
|
||||
}
|
||||
|
||||
/* TODO: check current state */
|
||||
if (ngx_quic_long_pkt(pkt.flags)) {
|
||||
|
||||
|
@ -1110,6 +1384,93 @@ ngx_quic_skip_zero_padding(ngx_buf_t *b)
|
|||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_quic_retry_input(ngx_connection_t *c, ngx_quic_header_t *pkt)
|
||||
{
|
||||
ngx_quic_secrets_t *keys;
|
||||
ngx_quic_send_ctx_t *ctx;
|
||||
ngx_quic_connection_t *qc;
|
||||
static u_char buf[NGX_QUIC_DEFAULT_MAX_PACKET_SIZE];
|
||||
|
||||
c->log->action = "retrying quic connection";
|
||||
|
||||
qc = c->quic;
|
||||
|
||||
if (ngx_buf_size(pkt->raw) < NGX_QUIC_MIN_INITIAL_SIZE) {
|
||||
ngx_log_error(NGX_LOG_INFO, c->log, 0,
|
||||
"quic UDP datagram is too small for initial packet");
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
if (ngx_quic_parse_long_header(pkt) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (ngx_quic_pkt_zrtt(pkt->flags)) {
|
||||
ngx_log_error(NGX_LOG_INFO, c->log, 0,
|
||||
"quic discard inflight 0-RTT packet");
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
if (!ngx_quic_pkt_in(pkt->flags)) {
|
||||
ngx_log_error(NGX_LOG_INFO, c->log, 0,
|
||||
"quic invalid initial packet: 0x%xi", pkt->flags);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (ngx_quic_parse_initial_header(pkt) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (ngx_quic_new_dcid(c, &pkt->dcid) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
qc = c->quic;
|
||||
|
||||
keys = &c->quic->keys[ssl_encryption_initial];
|
||||
|
||||
if (ngx_quic_set_initial_secret(c->pool, &keys->client, &keys->server,
|
||||
&qc->odcid)
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
c->quic->in_retry = 0;
|
||||
|
||||
if (ngx_quic_validate_token(c, pkt) != NGX_OK) {
|
||||
ngx_log_error(NGX_LOG_INFO, c->log, 0, "quic invalid token");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
pkt->secret = &keys->client;
|
||||
pkt->level = ssl_encryption_initial;
|
||||
pkt->plaintext = buf;
|
||||
|
||||
ctx = ngx_quic_get_send_ctx(qc, pkt->level);
|
||||
|
||||
if (ngx_quic_decrypt(pkt, NULL, &ctx->largest_pn) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (ngx_quic_init_connection(c) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (ngx_quic_payload_handler(c, pkt) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
/* pos is at header end, adjust by actual packet length */
|
||||
pkt->raw->pos += pkt->len;
|
||||
|
||||
(void) ngx_quic_skip_zero_padding(pkt->raw);
|
||||
|
||||
return ngx_quic_input(c, pkt->raw);
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_quic_initial_input(ngx_connection_t *c, ngx_quic_header_t *pkt)
|
||||
{
|
||||
|
|
|
@ -23,6 +23,13 @@
|
|||
#define NGX_QUIC_DEFAULT_ACK_DELAY_EXPONENT 3
|
||||
#define NGX_QUIC_DEFAULT_MAX_ACK_DELAY 25
|
||||
|
||||
#define NGX_QUIC_RETRY_TIMEOUT 3000
|
||||
#define NGX_QUIC_RETRY_LIFETIME 30000
|
||||
#define NGX_QUIC_RETRY_BUFFER_SIZE 128
|
||||
/* 1 flags + 4 version + 3 x (1 + 20) s/o/dcid + itag + token(44) */
|
||||
#define NGX_QUIC_MAX_TOKEN_SIZE 32
|
||||
/* sizeof(struct in6_addr) + sizeof(ngx_msec_t) up to AES-256 block size */
|
||||
|
||||
#define NGX_QUIC_HARDCODED_PTO 1000 /* 1s, TODO: collect */
|
||||
#define NGX_QUIC_CC_MIN_INTERVAL 1000 /* 1s */
|
||||
|
||||
|
@ -49,9 +56,12 @@ typedef struct {
|
|||
ngx_uint_t ack_delay_exponent;
|
||||
ngx_uint_t disable_active_migration;
|
||||
ngx_uint_t active_connection_id_limit;
|
||||
ngx_str_t original_connection_id;
|
||||
|
||||
ngx_flag_t retry;
|
||||
u_char token_key[32]; /* AES 256 */
|
||||
|
||||
/* TODO */
|
||||
ngx_uint_t original_connection_id;
|
||||
u_char stateless_reset_token[16];
|
||||
void *preferred_address;
|
||||
} ngx_quic_tp_t;
|
||||
|
|
|
@ -57,6 +57,8 @@ static ngx_int_t ngx_quic_create_long_packet(ngx_quic_header_t *pkt,
|
|||
ngx_ssl_conn_t *ssl_conn, ngx_str_t *res);
|
||||
static ngx_int_t ngx_quic_create_short_packet(ngx_quic_header_t *pkt,
|
||||
ngx_ssl_conn_t *ssl_conn, ngx_str_t *res);
|
||||
static ngx_int_t ngx_quic_create_retry_packet(ngx_quic_header_t *pkt,
|
||||
ngx_str_t *res);
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
|
@ -891,6 +893,53 @@ ngx_quic_create_short_packet(ngx_quic_header_t *pkt, ngx_ssl_conn_t *ssl_conn,
|
|||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_quic_create_retry_packet(ngx_quic_header_t *pkt, ngx_str_t *res)
|
||||
{
|
||||
u_char *start;
|
||||
ngx_str_t ad, itag;
|
||||
ngx_quic_secret_t secret;
|
||||
ngx_quic_ciphers_t ciphers;
|
||||
|
||||
/* 5.8. Retry Packet Integrity */
|
||||
static u_char key[16] =
|
||||
"\x4d\x32\xec\xdb\x2a\x21\x33\xc8"
|
||||
"\x41\xe4\x04\x3d\xf2\x7d\x44\x30";
|
||||
static u_char nonce[12] =
|
||||
"\x4d\x16\x11\xd0\x55\x13"
|
||||
"\xa5\x52\xc5\x87\xd5\x75";
|
||||
static ngx_str_t in = ngx_string("");
|
||||
|
||||
ad.data = res->data;
|
||||
ad.len = ngx_quic_create_retry_itag(pkt, ad.data, &start);
|
||||
|
||||
itag.data = ad.data + ad.len;
|
||||
|
||||
#ifdef NGX_QUIC_DEBUG_CRYPTO
|
||||
ngx_quic_hexdump(pkt->log, "quic retry itag", ad.data, ad.len);
|
||||
#endif
|
||||
|
||||
if (ngx_quic_ciphers(NULL, &ciphers, pkt->level) == NGX_ERROR) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
secret.key.len = sizeof(key);
|
||||
secret.key.data = key;
|
||||
secret.iv.len = sizeof(nonce);
|
||||
|
||||
if (ngx_quic_tls_seal(ciphers.c, &secret, &itag, nonce, &in, &ad, pkt->log)
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
res->len = itag.data + itag.len - start;
|
||||
res->data = start;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static uint64_t
|
||||
ngx_quic_parse_pn(u_char **pos, ngx_int_t len, u_char *mask,
|
||||
uint64_t *largest_pn)
|
||||
|
@ -952,6 +1001,10 @@ ngx_quic_encrypt(ngx_quic_header_t *pkt, ngx_ssl_conn_t *ssl_conn,
|
|||
return ngx_quic_create_short_packet(pkt, ssl_conn, res);
|
||||
}
|
||||
|
||||
if (ngx_quic_pkt_retry(pkt->flags)) {
|
||||
return ngx_quic_create_retry_packet(pkt, res);
|
||||
}
|
||||
|
||||
return ngx_quic_create_long_packet(pkt, ssl_conn, res);
|
||||
}
|
||||
|
||||
|
|
|
@ -385,6 +385,35 @@ ngx_quic_create_short_header(ngx_quic_header_t *pkt, u_char *out,
|
|||
}
|
||||
|
||||
|
||||
size_t
|
||||
ngx_quic_create_retry_itag(ngx_quic_header_t *pkt, u_char *out,
|
||||
u_char **start)
|
||||
{
|
||||
u_char *p;
|
||||
|
||||
p = out;
|
||||
|
||||
*p++ = pkt->odcid.len;
|
||||
p = ngx_cpymem(p, pkt->odcid.data, pkt->odcid.len);
|
||||
|
||||
*start = p;
|
||||
|
||||
*p++ = 0xff;
|
||||
|
||||
p = ngx_quic_write_uint32(p, NGX_QUIC_VERSION);
|
||||
|
||||
*p++ = pkt->dcid.len;
|
||||
p = ngx_cpymem(p, pkt->dcid.data, pkt->dcid.len);
|
||||
|
||||
*p++ = pkt->scid.len;
|
||||
p = ngx_cpymem(p, pkt->scid.data, pkt->scid.len);
|
||||
|
||||
p = ngx_cpymem(p, pkt->token.data, pkt->token.len);
|
||||
|
||||
return p - out;
|
||||
}
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_quic_parse_short_header(ngx_quic_header_t *pkt, ngx_str_t *dcid)
|
||||
{
|
||||
|
@ -1553,6 +1582,12 @@ ngx_quic_create_transport_params(u_char *pos, u_char *end, ngx_quic_tp_t *tp)
|
|||
len += ngx_quic_tp_len(NGX_QUIC_TP_MAX_IDLE_TIMEOUT,
|
||||
tp->max_idle_timeout);
|
||||
|
||||
if (tp->retry) {
|
||||
len += ngx_quic_varint_len(NGX_QUIC_TP_ORIGINAL_CONNECTION_ID);
|
||||
len += ngx_quic_varint_len(tp->original_connection_id.len);
|
||||
len += tp->original_connection_id.len;
|
||||
}
|
||||
|
||||
if (pos == NULL) {
|
||||
return len;
|
||||
}
|
||||
|
@ -1581,6 +1616,13 @@ ngx_quic_create_transport_params(u_char *pos, u_char *end, ngx_quic_tp_t *tp)
|
|||
ngx_quic_tp_vint(NGX_QUIC_TP_MAX_IDLE_TIMEOUT,
|
||||
tp->max_idle_timeout);
|
||||
|
||||
if (tp->retry) {
|
||||
ngx_quic_build_int(&p, NGX_QUIC_TP_ORIGINAL_CONNECTION_ID);
|
||||
ngx_quic_build_int(&p, tp->original_connection_id.len);
|
||||
p = ngx_cpymem(p, tp->original_connection_id.data,
|
||||
tp->original_connection_id.len);
|
||||
}
|
||||
|
||||
return p - pos;
|
||||
}
|
||||
|
||||
|
|
|
@ -280,6 +280,7 @@ typedef struct {
|
|||
size_t len;
|
||||
|
||||
/* cleartext fields */
|
||||
ngx_str_t odcid; /* retry packet tag */
|
||||
ngx_str_t dcid;
|
||||
ngx_str_t scid;
|
||||
uint64_t pn;
|
||||
|
@ -303,6 +304,9 @@ ngx_int_t ngx_quic_parse_short_header(ngx_quic_header_t *pkt,
|
|||
size_t ngx_quic_create_short_header(ngx_quic_header_t *pkt, u_char *out,
|
||||
size_t pkt_len, u_char **pnp);
|
||||
|
||||
size_t ngx_quic_create_retry_itag(ngx_quic_header_t *pkt, u_char *out,
|
||||
u_char **start);
|
||||
|
||||
ngx_int_t ngx_quic_parse_initial_header(ngx_quic_header_t *pkt);
|
||||
ngx_int_t ngx_quic_parse_handshake_header(ngx_quic_header_t *pkt);
|
||||
|
||||
|
|
|
@ -111,6 +111,13 @@ static ngx_command_t ngx_http_v3_commands[] = {
|
|||
offsetof(ngx_http_v3_srv_conf_t, quic.active_connection_id_limit),
|
||||
&ngx_http_v3_active_connection_id_limit_bounds },
|
||||
|
||||
{ ngx_string("quic_retry"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
|
||||
ngx_conf_set_flag_slot,
|
||||
NGX_HTTP_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_http_v3_srv_conf_t, quic.retry),
|
||||
NULL },
|
||||
|
||||
ngx_null_command
|
||||
};
|
||||
|
||||
|
@ -257,6 +264,8 @@ ngx_http_v3_create_srv_conf(ngx_conf_t *cf)
|
|||
v3cf->quic.disable_active_migration = NGX_CONF_UNSET_UINT;
|
||||
v3cf->quic.active_connection_id_limit = NGX_CONF_UNSET_UINT;
|
||||
|
||||
v3cf->quic.retry = NGX_CONF_UNSET;
|
||||
|
||||
return v3cf;
|
||||
}
|
||||
|
||||
|
@ -310,6 +319,15 @@ ngx_http_v3_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
|
|||
ngx_conf_merge_uint_value(conf->quic.active_connection_id_limit,
|
||||
prev->quic.active_connection_id_limit, 2);
|
||||
|
||||
ngx_conf_merge_value(conf->quic.retry, prev->quic.retry, 0);
|
||||
|
||||
if (conf->quic.retry) {
|
||||
if (RAND_bytes(conf->quic.token_key, sizeof(conf->quic.token_key)) <= 0) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue