QUIC: packet processing refactoring.

All packet header parsing is now performed by ngx_quic_parse_packet()
function, located in the ngx_quic_transport.c file.

The packet processing is centralized in the ngx_quic_process_packet()
function which decides if the packet should be accepted, ignored or
connection should be closed, depending on the connection state.

As a result of refactoring, behavior has changed in some places:

 - minimal size of Initial packet is now always tested
 - connection IDs are always tested in existing connections
 - old keys are discarded on encryption level switch
This commit is contained in:
Vladimir Homutov 2020-09-30 15:14:09 +03:00
parent 5b112f3ad6
commit 19c1c5f206
3 changed files with 275 additions and 511 deletions

View file

@ -187,7 +187,7 @@ static ngx_int_t ngx_quic_new_connection(ngx_connection_t *c, ngx_ssl_t *ssl,
static ngx_int_t ngx_quic_negotiate_version(ngx_connection_t *c,
ngx_quic_header_t *inpkt);
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_send_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);
@ -201,20 +201,14 @@ static void ngx_quic_close_timer_handler(ngx_event_t *ev);
static ngx_int_t ngx_quic_close_streams(ngx_connection_t *c,
ngx_quic_connection_t *qc);
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,
ngx_quic_header_t *pkt);
static ngx_int_t ngx_quic_early_input(ngx_connection_t *c,
ngx_quic_header_t *pkt);
static ngx_int_t ngx_quic_input(ngx_connection_t *c, ngx_buf_t *b,
ngx_ssl_t *ssl, ngx_quic_conf_t *conf);
static ngx_int_t ngx_quic_process_packet(ngx_connection_t *c, ngx_ssl_t *ssl,
ngx_quic_conf_t *conf, ngx_quic_header_t *pkt);
static void ngx_quic_discard_ctx(ngx_connection_t *c,
enum ssl_encryption_level_t level);
static ngx_int_t ngx_quic_check_peer(ngx_quic_connection_t *qc,
ngx_quic_header_t *pkt);
static ngx_int_t ngx_quic_app_input(ngx_connection_t *c,
ngx_quic_header_t *pkt);
static ngx_int_t ngx_quic_payload_handler(ngx_connection_t *c,
ngx_quic_header_t *pkt);
static ngx_int_t ngx_quic_send_ack(ngx_connection_t *c, ngx_quic_header_t *pkt);
@ -630,24 +624,13 @@ ngx_quic_send_alert(ngx_ssl_conn_t *ssl_conn, enum ssl_encryption_level_t level,
void
ngx_quic_run(ngx_connection_t *c, ngx_ssl_t *ssl, ngx_quic_conf_t *conf)
{
ngx_int_t rc;
ngx_buf_t *b;
ngx_quic_header_t pkt;
ngx_int_t rc;
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic run");
c->log->action = "QUIC initialization";
ngx_memzero(&pkt, sizeof(ngx_quic_header_t));
b = c->buffer;
pkt.log = c->log;
pkt.raw = b;
pkt.data = b->start;
pkt.len = b->last - b->start;
rc = ngx_quic_new_connection(c, ssl, conf, &pkt);
rc = ngx_quic_input(c, c->buffer, ssl, conf);
if (rc != NGX_OK) {
ngx_quic_close_connection(c, rc == NGX_DECLINED ? NGX_DONE : NGX_ERROR);
return;
@ -666,46 +649,10 @@ static ngx_int_t
ngx_quic_new_connection(ngx_connection_t *c, ngx_ssl_t *ssl,
ngx_quic_conf_t *conf, ngx_quic_header_t *pkt)
{
ngx_int_t rc;
ngx_uint_t i;
ngx_quic_tp_t *ctp;
ngx_quic_secrets_t *keys;
ngx_quic_send_ctx_t *ctx;
ngx_quic_client_id_t *cid;
ngx_quic_connection_t *qc;
static u_char buf[NGX_QUIC_MAX_UDP_PAYLOAD_SIZE];
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_ERROR;
}
if (ngx_quic_parse_long_header(pkt) != NGX_OK) {
return NGX_ERROR;
}
if (pkt->version != NGX_QUIC_VERSION) {
return ngx_quic_negotiate_version(c, pkt);
}
if (!ngx_quic_pkt_in(pkt->flags)) {
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"quic invalid initial packet: 0x%xd", pkt->flags);
return NGX_ERROR;
}
if (ngx_quic_parse_initial_header(pkt) != NGX_OK) {
return NGX_ERROR;
}
if (pkt->dcid.len < NGX_QUIC_CID_LEN_MIN) {
/* 7.2. Negotiating Connection IDs */
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"quic too short dcid in initial packet: length %i",
pkt->dcid.len);
return NGX_ERROR;
}
c->log->action = "creating new quic connection";
@ -804,15 +751,6 @@ ngx_quic_new_connection(ngx_connection_t *c, ngx_ssl_t *ssl,
qc->nclient_ids++;
qc->curr_seqnum = 0;
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;
}
qc->initialized = 1;
if (ngx_terminate || ngx_exiting) {
@ -820,60 +758,6 @@ ngx_quic_new_connection(ngx_connection_t *c, ngx_ssl_t *ssl,
return NGX_ERROR;
}
if (pkt->token.len) {
rc = ngx_quic_validate_token(c, pkt);
if (rc == NGX_ERROR) {
ngx_log_error(NGX_LOG_INFO, c->log, 0, "quic invalid token");
return NGX_ERROR;
}
if (rc == NGX_DECLINED) {
ngx_log_error(NGX_LOG_INFO, c->log, 0, "quic expired token");
return ngx_quic_retry(c);
}
/* NGX_OK */
qc->validated = 1;
} else if (conf->retry) {
return ngx_quic_retry(c);
}
pkt->secret = &keys->client;
pkt->level = ssl_encryption_initial;
pkt->plaintext = buf;
ctx = ngx_quic_get_send_ctx(qc, pkt->level);
rc = ngx_quic_decrypt(pkt, NULL, &ctx->largest_pn);
if (rc != NGX_OK) {
qc->error = pkt->error;
qc->error_reason = "failed to decrypt packet";
return rc;
}
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);
rc = ngx_quic_input(c, pkt->raw);
if (rc == NGX_ERROR) {
return NGX_ERROR;
}
/* rc == NGX_OK || rc == NGX_DECLINED */
return NGX_OK;
}
@ -939,7 +823,7 @@ ngx_quic_new_dcid(ngx_connection_t *c, ngx_str_t *odcid)
static ngx_int_t
ngx_quic_retry(ngx_connection_t *c)
ngx_quic_send_retry(ngx_connection_t *c)
{
ssize_t len;
ngx_str_t res, token;
@ -1194,6 +1078,7 @@ ngx_quic_validate_token(ngx_connection_t *c, ngx_quic_header_t *pkt)
ngx_memcpy(&msec, tdec + len, sizeof(msec));
if (ngx_current_msec - msec > NGX_QUIC_RETRY_LIFETIME) {
ngx_log_error(NGX_LOG_INFO, c->log, 0, "quic expired token");
return NGX_DECLINED;
}
@ -1201,6 +1086,8 @@ ngx_quic_validate_token(ngx_connection_t *c, ngx_quic_header_t *pkt)
bad_token:
ngx_log_error(NGX_LOG_INFO, c->log, 0, "quic invalid token");
qc->error = NGX_QUIC_ERR_INVALID_TOKEN;
qc->error_reason = "invalid_token";
@ -1339,7 +1226,7 @@ ngx_quic_input_handler(ngx_event_t *rev)
b.last += n;
qc->received += n;
rc = ngx_quic_input(c, &b);
rc = ngx_quic_input(c, &b, NULL, NULL);
if (rc == NGX_ERROR) {
ngx_quic_close_connection(c, NGX_ERROR);
@ -1603,7 +1490,8 @@ ngx_quic_close_streams(ngx_connection_t *c, ngx_quic_connection_t *qc)
static ngx_int_t
ngx_quic_input(ngx_connection_t *c, ngx_buf_t *b)
ngx_quic_input(ngx_connection_t *c, ngx_buf_t *b, ngx_ssl_t *ssl,
ngx_quic_conf_t *conf)
{
u_char *p;
ngx_int_t rc;
@ -1625,29 +1513,7 @@ ngx_quic_input(ngx_connection_t *c, ngx_buf_t *b)
pkt.flags = p[0];
pkt.raw->pos++;
if (c->quic->in_retry) {
rc = ngx_quic_retry_input(c, &pkt);
} else if (ngx_quic_long_pkt(pkt.flags)) {
if (ngx_quic_pkt_in(pkt.flags)) {
rc = ngx_quic_initial_input(c, &pkt);
} else if (ngx_quic_pkt_hs(pkt.flags)) {
rc = ngx_quic_handshake_input(c, &pkt);
} else if (ngx_quic_pkt_zrtt(pkt.flags)) {
rc = ngx_quic_early_input(c, &pkt);
} else {
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"quic unknown long packet type");
rc = NGX_DECLINED;
}
} else {
rc = ngx_quic_app_input(c, &pkt);
}
rc = ngx_quic_process_packet(c, ssl, conf, &pkt);
if (rc == NGX_ERROR) {
return NGX_ERROR;
@ -1678,164 +1544,156 @@ ngx_quic_input(ngx_connection_t *c, ngx_buf_t *b)
/* b->pos is at header end, adjust by actual packet length */
b->pos = pkt.data + pkt.len;
p = ngx_quic_skip_zero_padding(b);
/* firefox workaround: skip zero padding at the end of quic packet */
while (b->pos < b->last && *(b->pos) == 0) {
b->pos++;
}
p = b->pos;
}
return good ? NGX_OK : NGX_DECLINED;
}
/* firefox workaround: skip zero padding at the end of quic packet */
static ngx_inline u_char *
ngx_quic_skip_zero_padding(ngx_buf_t *b)
{
while (b->pos < b->last && *(b->pos) == 0) {
b->pos++;
}
return b->pos;
}
static ngx_int_t
ngx_quic_retry_input(ngx_connection_t *c, ngx_quic_header_t *pkt)
{
ngx_int_t rc;
ngx_quic_secrets_t *keys;
ngx_quic_send_ctx_t *ctx;
ngx_quic_connection_t *qc;
static u_char buf[NGX_QUIC_MAX_UDP_PAYLOAD_SIZE];
c->log->action = "retrying quic connection";
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_DECLINED;
}
if (ngx_quic_parse_long_header(pkt) != NGX_OK) {
return NGX_DECLINED;
}
if (pkt->version != NGX_QUIC_VERSION) {
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"quic unsupported version: 0x%xD", pkt->version);
return NGX_DECLINED;
}
if (ngx_quic_pkt_zrtt(pkt->flags)) {
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"quic discard inflight 0-RTT packet");
return NGX_DECLINED;
}
if (!ngx_quic_pkt_in(pkt->flags)) {
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"quic invalid initial packet: 0x%xd", pkt->flags);
return NGX_DECLINED;
}
if (ngx_quic_parse_initial_header(pkt) != NGX_OK) {
return NGX_DECLINED;
}
if (!pkt->token.len) {
return NGX_DECLINED;
}
if (ngx_quic_new_dcid(c, &pkt->dcid) != NGX_OK) {
return NGX_ERROR;
}
qc = c->quic;
qc->tp.initial_scid = c->quic->dcid;
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;
}
qc->validated = 1;
pkt->secret = &keys->client;
pkt->level = ssl_encryption_initial;
pkt->plaintext = buf;
ctx = ngx_quic_get_send_ctx(qc, pkt->level);
rc = ngx_quic_decrypt(pkt, NULL, &ctx->largest_pn);
if (rc != NGX_OK) {
qc->error = pkt->error;
qc->error_reason = "failed to decrypt packet";
return rc;
}
if (ngx_quic_init_connection(c) != NGX_OK) {
return NGX_ERROR;
}
if (ngx_quic_payload_handler(c, pkt) != NGX_OK) {
return NGX_ERROR;
}
return NGX_OK;
}
static ngx_int_t
ngx_quic_initial_input(ngx_connection_t *c, ngx_quic_header_t *pkt)
ngx_quic_process_packet(ngx_connection_t *c, ngx_ssl_t *ssl,
ngx_quic_conf_t *conf, ngx_quic_header_t *pkt)
{
ngx_int_t rc;
ngx_ssl_conn_t *ssl_conn;
ngx_quic_secrets_t *keys;
ngx_quic_secrets_t *keys, *next, tmp;
ngx_quic_send_ctx_t *ctx;
ngx_quic_connection_t *qc;
static u_char buf[NGX_QUIC_MAX_UDP_PAYLOAD_SIZE];
c->log->action = "processing initial quic packet";
rc = ngx_quic_parse_packet(pkt);
ssl_conn = c->ssl->connection;
if (ngx_quic_parse_long_header(pkt) != NGX_OK) {
return NGX_DECLINED;
}
if (pkt->version != NGX_QUIC_VERSION) {
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"quic unsupported version: 0x%xD", pkt->version);
return NGX_DECLINED;
if (rc == NGX_DECLINED || rc == NGX_ERROR) {
return rc;
}
qc = c->quic;
if (ngx_quic_check_peer(qc, pkt) != NGX_OK) {
if (qc) {
if (rc == NGX_ABORT) {
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"quic unsupported version: 0x%xD", pkt->version);
return NGX_DECLINED;
}
if (ngx_quic_check_peer(qc, pkt) != NGX_OK) {
return NGX_DECLINED;
}
if (qc->in_retry) {
c->log->action = "retrying quic connection";
if (pkt->level != ssl_encryption_initial) {
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"quic discard late retry packet");
return NGX_DECLINED;
}
if (!pkt->token.len) {
return NGX_DECLINED;
}
if (ngx_quic_new_dcid(c, &pkt->dcid) != NGX_OK) {
return NGX_ERROR;
}
qc->tp.initial_scid = qc->dcid;
qc->in_retry = 0;
keys = &qc->keys[ssl_encryption_initial];
if (ngx_quic_set_initial_secret(c->pool, &keys->client,
&keys->server, &qc->odcid)
!= NGX_OK)
{
return NGX_ERROR;
}
if (ngx_quic_validate_token(c, pkt) != NGX_OK) {
return NGX_ERROR;
}
qc->validated = 1;
}
} else {
if (rc == NGX_ABORT) {
return ngx_quic_negotiate_version(c, pkt);
}
if (pkt->level == ssl_encryption_initial) {
if (pkt->dcid.len < NGX_QUIC_CID_LEN_MIN) {
/* 7.2. Negotiating Connection IDs */
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"quic too short dcid in initial"
" packet: length %i", pkt->dcid.len);
return NGX_ERROR;
}
rc = ngx_quic_new_connection(c, ssl, conf, pkt);
if (rc != NGX_OK) {
return rc;
}
if (pkt->token.len) {
if (ngx_quic_validate_token(c, pkt) != NGX_OK) {
return NGX_ERROR;
}
} else if (conf->retry) {
return ngx_quic_send_retry(c);
}
qc = c->quic;
keys = &qc->keys[ssl_encryption_initial];
if (ngx_quic_set_initial_secret(c->pool, &keys->client,
&keys->server, &qc->odcid)
!= NGX_OK)
{
return NGX_ERROR;
}
} else if (pkt->level == ssl_encryption_application) {
return NGX_DECLINED;
} else {
return NGX_ERROR;
}
}
keys = &qc->keys[pkt->level];
if (keys->client.key.len == 0) {
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"quic no level %d keys yet, ignoring packet", pkt->level);
return NGX_DECLINED;
}
if (ngx_quic_parse_initial_header(pkt) != NGX_OK) {
return NGX_DECLINED;
}
keys = &qc->keys[ssl_encryption_initial];
next = &qc->next_key;
pkt->secret = &keys->client;
pkt->level = ssl_encryption_initial;
pkt->next = &next->client;
pkt->key_phase = qc->key_phase;
pkt->plaintext = buf;
ctx = ngx_quic_get_send_ctx(qc, pkt->level);
ssl_conn = c->ssl ? c->ssl->connection : NULL;
rc = ngx_quic_decrypt(pkt, ssl_conn, &ctx->largest_pn);
if (rc != NGX_OK) {
qc->error = pkt->error;
@ -1843,70 +1701,72 @@ ngx_quic_initial_input(ngx_connection_t *c, ngx_quic_header_t *pkt)
return rc;
}
return ngx_quic_payload_handler(c, pkt);
}
static ngx_int_t
ngx_quic_handshake_input(ngx_connection_t *c, ngx_quic_header_t *pkt)
{
ngx_int_t rc;
ngx_queue_t *q;
ngx_quic_frame_t *f;
ngx_quic_secrets_t *keys;
ngx_quic_send_ctx_t *ctx;
ngx_quic_connection_t *qc;
static u_char buf[NGX_QUIC_MAX_UDP_PAYLOAD_SIZE];
c->log->action = "processing handshake quic packet";
qc = c->quic;
keys = &c->quic->keys[ssl_encryption_handshake];
if (keys->client.key.len == 0) {
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"quic no read keys yet, packet ignored");
return NGX_DECLINED;
if (c->ssl == NULL) {
if (ngx_quic_init_connection(c) != NGX_OK) {
return NGX_ERROR;
}
}
/* extract cleartext data into pkt */
if (ngx_quic_parse_long_header(pkt) != NGX_OK) {
return NGX_DECLINED;
if (pkt->level == ssl_encryption_handshake) {
/*
* 4.10.1. The successful use of Handshake packets indicates
* that no more Initial packets need to be exchanged
*/
ngx_quic_discard_ctx(c, ssl_encryption_initial);
qc->validated = 1;
}
if (pkt->version != NGX_QUIC_VERSION) {
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"quic unsupported version: 0x%xD", pkt->version);
return NGX_DECLINED;
if (pkt->level != ssl_encryption_application) {
return ngx_quic_payload_handler(c, pkt);
}
if (ngx_quic_check_peer(qc, pkt) != NGX_OK) {
return NGX_DECLINED;
ngx_gettimeofday(&pkt->received);
/* switch keys on Key Phase change */
if (pkt->key_update) {
qc->key_phase ^= 1;
tmp = *keys;
*keys = *next;
*next = tmp;
}
if (ngx_quic_parse_handshake_header(pkt) != NGX_OK) {
return NGX_DECLINED;
}
pkt->secret = &keys->client;
pkt->level = ssl_encryption_handshake;
pkt->plaintext = buf;
ctx = ngx_quic_get_send_ctx(qc, pkt->level);
rc = ngx_quic_decrypt(pkt, c->ssl->connection, &ctx->largest_pn);
rc = ngx_quic_payload_handler(c, pkt);
if (rc != NGX_OK) {
qc->error = pkt->error;
qc->error_reason = "failed to decrypt packet";
return rc;
}
/*
* 4.10.1. The successful use of Handshake packets indicates
* that no more Initial packets need to be exchanged
*/
ctx = ngx_quic_get_send_ctx(c->quic, ssl_encryption_initial);
/* generate next keys */
if (pkt->key_update) {
if (ngx_quic_key_update(c, keys, next) != NGX_OK) {
return NGX_ERROR;
}
}
return NGX_OK;
}
static void
ngx_quic_discard_ctx(ngx_connection_t *c, enum ssl_encryption_level_t level)
{
ngx_queue_t *q;
ngx_quic_frame_t *f;
ngx_quic_send_ctx_t *ctx;
ngx_quic_connection_t *qc;
qc = c->quic;
if (qc->keys[level].client.key.len == 0) {
return;
}
qc->keys[level].client.key.len = 0;
qc->pto_count = 0;
ctx = ngx_quic_get_send_ctx(qc, level);
while (!ngx_queue_empty(&ctx->sent)) {
q = ngx_queue_head(&ctx->sent);
@ -1916,83 +1776,37 @@ ngx_quic_handshake_input(ngx_connection_t *c, ngx_quic_header_t *pkt)
ngx_quic_congestion_ack(c, f);
ngx_quic_free_frame(c, f);
}
qc->validated = 1;
qc->pto_count = 0;
return ngx_quic_payload_handler(c, pkt);
}
static ngx_int_t
ngx_quic_early_input(ngx_connection_t *c, ngx_quic_header_t *pkt)
{
ngx_int_t rc;
ngx_quic_secrets_t *keys;
ngx_quic_send_ctx_t *ctx;
ngx_quic_connection_t *qc;
static u_char buf[NGX_QUIC_MAX_UDP_PAYLOAD_SIZE];
c->log->action = "processing early data quic packet";
qc = c->quic;
/* extract cleartext data into pkt */
if (ngx_quic_parse_long_header(pkt) != NGX_OK) {
return NGX_DECLINED;
}
if (pkt->version != NGX_QUIC_VERSION) {
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"quic unsupported version: 0x%xD", pkt->version);
return NGX_DECLINED;
}
if (ngx_quic_check_peer(qc, pkt) != NGX_OK) {
return NGX_DECLINED;
}
if (ngx_quic_parse_handshake_header(pkt) != NGX_OK) {
return NGX_DECLINED;
}
keys = &c->quic->keys[ssl_encryption_early_data];
if (keys->client.key.len == 0) {
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"quic no 0-RTT keys yet, packet ignored");
return NGX_DECLINED;
}
pkt->secret = &keys->client;
pkt->level = ssl_encryption_early_data;
pkt->plaintext = buf;
ctx = ngx_quic_get_send_ctx(qc, pkt->level);
rc = ngx_quic_decrypt(pkt, c->ssl->connection, &ctx->largest_pn);
if (rc != NGX_OK) {
qc->error = pkt->error;
qc->error_reason = "failed to decrypt packet";
return rc;
}
return ngx_quic_payload_handler(c, pkt);
}
static ngx_int_t
ngx_quic_check_peer(ngx_quic_connection_t *qc, ngx_quic_header_t *pkt)
{
ngx_str_t *dcid;
ngx_queue_t *q;
ngx_quic_send_ctx_t *ctx;
ngx_quic_client_id_t *cid;
dcid = (pkt->level == ssl_encryption_early_data) ? &qc->odcid : &qc->dcid;
if (pkt->dcid.len == dcid->len
&& ngx_memcmp(pkt->dcid.data, dcid->data, dcid->len) == 0)
{
if (pkt->level == ssl_encryption_application) {
return NGX_OK;
}
goto found;
}
/*
* a packet sent in response to an initial client packet might be lost,
* thus check also for old dcid
*/
ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_initial);
if (ngx_quic_pkt_zrtt(pkt->flags)
|| (ngx_quic_pkt_in(pkt->flags) && ctx->largest_ack == (uint64_t) -1))
if (pkt->level == ssl_encryption_initial
&& ctx->largest_ack == (uint64_t) -1)
{
if (pkt->dcid.len == qc->odcid.len
&& ngx_memcmp(pkt->dcid.data, qc->odcid.data, qc->odcid.len) == 0)
@ -2001,14 +1815,6 @@ ngx_quic_check_peer(ngx_quic_connection_t *qc, ngx_quic_header_t *pkt)
}
}
if (!ngx_quic_pkt_zrtt(pkt->flags)) {
if (pkt->dcid.len == qc->dcid.len
&& ngx_memcmp(pkt->dcid.data, qc->dcid.data, qc->dcid.len) == 0)
{
goto found;
}
}
ngx_log_error(NGX_LOG_INFO, pkt->log, 0, "quic unexpected quic dcid");
return NGX_ERROR;
@ -2027,80 +1833,8 @@ found:
}
}
ngx_log_error(NGX_LOG_INFO, pkt->log, 0, "quic unexpected quic scid");
return NGX_ERROR;
}
static ngx_int_t
ngx_quic_app_input(ngx_connection_t *c, ngx_quic_header_t *pkt)
{
ngx_int_t rc;
ngx_quic_secrets_t *keys, *next, tmp;
ngx_quic_send_ctx_t *ctx;
ngx_quic_connection_t *qc;
static u_char buf[NGX_QUIC_MAX_UDP_PAYLOAD_SIZE];
c->log->action = "processing application data quic packet";
qc = c->quic;
keys = &c->quic->keys[ssl_encryption_application];
next = &c->quic->next_key;
if (keys->client.key.len == 0) {
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"quic no read keys yet, packet ignored");
return NGX_DECLINED;
}
if (ngx_quic_parse_short_header(pkt, &qc->dcid) != NGX_OK) {
return NGX_DECLINED;
}
pkt->secret = &keys->client;
pkt->next = &next->client;
pkt->key_phase = c->quic->key_phase;
pkt->level = ssl_encryption_application;
pkt->plaintext = buf;
ctx = ngx_quic_get_send_ctx(qc, pkt->level);
rc = ngx_quic_decrypt(pkt, c->ssl->connection, &ctx->largest_pn);
if (rc != NGX_OK) {
qc->error = pkt->error;
qc->error_reason = "failed to decrypt packet";
return rc;
}
ngx_gettimeofday(&pkt->received);
/* switch keys on Key Phase change */
if (pkt->key_update) {
c->quic->key_phase ^= 1;
tmp = *keys;
*keys = *next;
*next = tmp;
}
rc = ngx_quic_payload_handler(c, pkt);
if (rc == NGX_ERROR) {
return NGX_ERROR;
}
/* generate next keys */
if (pkt->key_update) {
if (ngx_quic_key_update(c, keys, next) != NGX_OK) {
return NGX_ERROR;
}
}
return rc;
ngx_log_error(NGX_LOG_INFO, pkt->log, 0, "quic unexpected quic scid");
return NGX_ERROR;
}
@ -2981,9 +2715,7 @@ static ngx_int_t
ngx_quic_crypto_input(ngx_connection_t *c, ngx_quic_frame_t *frame, void *data)
{
int n, sslerr;
ngx_queue_t *q;
ngx_ssl_conn_t *ssl_conn;
ngx_quic_send_ctx_t *ctx;
ngx_quic_crypto_frame_t *f;
f = &frame->u.crypto;
@ -3060,18 +2792,7 @@ ngx_quic_crypto_input(ngx_connection_t *c, ngx_quic_frame_t *frame, void *data)
* 4.10.2 An endpoint MUST discard its handshake keys
* when the TLS handshake is confirmed
*/
ctx = ngx_quic_get_send_ctx(c->quic, ssl_encryption_handshake);
while (!ngx_queue_empty(&ctx->sent)) {
q = ngx_queue_head(&ctx->sent);
ngx_queue_remove(q);
frame = ngx_queue_data(q, ngx_quic_frame_t, queue);
ngx_quic_congestion_ack(c, frame);
ngx_quic_free_frame(c, frame);
}
c->quic->pto_count = 0;
ngx_quic_discard_ctx(c, ssl_encryption_handshake);
}
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,

View file

@ -67,6 +67,12 @@ static u_char *ngx_quic_read_bytes(u_char *pos, u_char *end, size_t len,
static u_char *ngx_quic_copy_bytes(u_char *pos, u_char *end, size_t len,
u_char *dst);
static ngx_int_t ngx_quic_parse_long_header(ngx_quic_header_t *pkt);
static ngx_int_t ngx_quic_parse_short_header(ngx_quic_header_t *pkt,
size_t dcid_len);
static ngx_int_t ngx_quic_parse_initial_header(ngx_quic_header_t *pkt);
static ngx_int_t ngx_quic_parse_handshake_header(ngx_quic_header_t *pkt);
static ngx_int_t ngx_quic_frame_allowed(ngx_quic_header_t *pkt,
ngx_uint_t frame_type);
static size_t ngx_quic_create_ack(u_char *p, ngx_quic_ack_frame_t *ack);
@ -245,6 +251,52 @@ ngx_quic_error_text(uint64_t error_code)
ngx_int_t
ngx_quic_parse_packet(ngx_quic_header_t *pkt)
{
if (!ngx_quic_long_pkt(pkt->flags)) {
pkt->level = ssl_encryption_application;
return ngx_quic_parse_short_header(pkt, NGX_QUIC_SERVER_CID_LEN);
}
if (ngx_quic_parse_long_header(pkt) != NGX_OK) {
return NGX_DECLINED;
}
if (pkt->version != NGX_QUIC_VERSION) {
return NGX_ABORT;
}
if (ngx_quic_pkt_in(pkt->flags)) {
if (pkt->len < NGX_QUIC_MIN_INITIAL_SIZE) {
ngx_log_error(NGX_LOG_INFO, pkt->log, 0,
"quic UDP datagram is too small for initial packet");
return NGX_DECLINED;
}
pkt->level = ssl_encryption_initial;
return ngx_quic_parse_initial_header(pkt);
}
if (ngx_quic_pkt_hs(pkt->flags)) {
pkt->level = ssl_encryption_handshake;
} else if (ngx_quic_pkt_zrtt(pkt->flags)) {
pkt->level = ssl_encryption_early_data;
} else {
ngx_log_error(NGX_LOG_INFO, pkt->log, 0,
"quic unknown long packet type");
return NGX_DECLINED;
}
return ngx_quic_parse_handshake_header(pkt);
}
static ngx_int_t
ngx_quic_parse_long_header(ngx_quic_header_t *pkt)
{
u_char *p, *end;
@ -456,8 +508,8 @@ ngx_quic_create_retry_itag(ngx_quic_header_t *pkt, u_char *out,
}
ngx_int_t
ngx_quic_parse_short_header(ngx_quic_header_t *pkt, ngx_str_t *dcid)
static ngx_int_t
ngx_quic_parse_short_header(ngx_quic_header_t *pkt, size_t dcid_len)
{
u_char *p, *end;
@ -472,14 +524,9 @@ ngx_quic_parse_short_header(ngx_quic_header_t *pkt, ngx_str_t *dcid)
return NGX_ERROR;
}
if (ngx_memcmp(p, dcid->data, dcid->len) != 0) {
ngx_log_error(NGX_LOG_INFO, pkt->log, 0, "unexpected quic dcid");
return NGX_ERROR;
}
pkt->dcid.len = dcid_len;
pkt->dcid.len = dcid->len;
p = ngx_quic_read_bytes(p, end, dcid->len, &pkt->dcid.data);
p = ngx_quic_read_bytes(p, end, dcid_len, &pkt->dcid.data);
if (p == NULL) {
ngx_log_error(NGX_LOG_INFO, pkt->log, 0,
"quic packet is too small to read dcid");
@ -492,7 +539,7 @@ ngx_quic_parse_short_header(ngx_quic_header_t *pkt, ngx_str_t *dcid)
}
ngx_int_t
static ngx_int_t
ngx_quic_parse_initial_header(ngx_quic_header_t *pkt)
{
u_char *p, *end;
@ -548,7 +595,7 @@ ngx_quic_parse_initial_header(ngx_quic_header_t *pkt)
}
ngx_int_t
static ngx_int_t
ngx_quic_parse_handshake_header(ngx_quic_header_t *pkt)
{
u_char *p, *end;

View file

@ -319,23 +319,19 @@ typedef struct {
u_char *ngx_quic_error_text(uint64_t error_code);
ngx_int_t ngx_quic_parse_packet(ngx_quic_header_t *pkt);
size_t ngx_quic_create_version_negotiation(ngx_quic_header_t *pkt, u_char *out);
ngx_int_t ngx_quic_parse_long_header(ngx_quic_header_t *pkt);
size_t ngx_quic_create_long_header(ngx_quic_header_t *pkt, u_char *out,
size_t pkt_len, u_char **pnp);
ngx_int_t ngx_quic_parse_short_header(ngx_quic_header_t *pkt,
ngx_str_t *dcid);
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);
ssize_t ngx_quic_parse_frame(ngx_quic_header_t *pkt, u_char *start, u_char *end,
ngx_quic_frame_t *frame);
ssize_t ngx_quic_create_frame(u_char *p, ngx_quic_frame_t *f);