# HG changeset patch # User Vladimir Homutov # Date 1601468049 -10800 # Node ID d0d3fc0697a00a01e69e5ad5992ff48d4e64ae38 # Parent a89a58c642ef60842ee4cd2d87e08ebbaf0aeba4 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 diff --git a/src/event/ngx_event_quic.c b/src/event/ngx_event_quic.c --- a/src/event/ngx_event_quic.c +++ b/src/event/ngx_event_quic.c @@ -187,7 +187,7 @@ static ngx_int_t ngx_quic_new_connection 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 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_ 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 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 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, n 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 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 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 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_ 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,163 +1544,155 @@ ngx_quic_input(ngx_connection_t *c, ngx_ /* 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_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_quic_secrets_t *keys; + ngx_ssl_conn_t *ssl_conn; + 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 = "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; + rc = ngx_quic_parse_packet(pkt); + + if (rc == NGX_DECLINED || rc == NGX_ERROR) { + return rc; } 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; + + 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; + } + + 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); - 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_int_t rc; - ngx_ssl_conn_t *ssl_conn; - 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 initial quic packet"; - - 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; - } - - qc = c->quic; - - if (ngx_quic_check_peer(qc, pkt) != NGX_OK) { - return NGX_DECLINED; - } - - if (ngx_quic_parse_initial_header(pkt) != NGX_OK) { - return NGX_DECLINED; - } - - keys = &qc->keys[ssl_encryption_initial]; - - pkt->secret = &keys->client; - pkt->level = ssl_encryption_initial; - 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) { @@ -1843,70 +1701,72 @@ ngx_quic_initial_input(ngx_connection_t return rc; } - return ngx_quic_payload_handler(c, pkt); + if (c->ssl == NULL) { + if (ngx_quic_init_connection(c) != NGX_OK) { + return NGX_ERROR; + } + } + + 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->level != ssl_encryption_application) { + return ngx_quic_payload_handler(c, pkt); + } + + ngx_gettimeofday(&pkt->received); + + /* switch keys on Key Phase change */ + + if (pkt->key_update) { + qc->key_phase ^= 1; + + tmp = *keys; + *keys = *next; + *next = tmp; + } + + rc = ngx_quic_payload_handler(c, pkt); + if (rc != NGX_OK) { + return rc; + } + + /* generate next keys */ + + if (pkt->key_update) { + if (ngx_quic_key_update(c, keys, next) != NGX_OK) { + return NGX_ERROR; + } + } + + return NGX_OK; } -static ngx_int_t -ngx_quic_handshake_input(ngx_connection_t *c, ngx_quic_header_t *pkt) +static void +ngx_quic_discard_ctx(ngx_connection_t *c, enum ssl_encryption_level_t level) { - 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; - } - - /* 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; - } - - 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); - 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); + 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_ 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_ } } - 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 * * 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, diff --git a/src/event/ngx_event_quic_transport.c b/src/event/ngx_event_quic_transport.c --- a/src/event/ngx_event_quic_transport.c +++ b/src/event/ngx_event_quic_transport.c @@ -67,6 +67,12 @@ static u_char *ngx_quic_read_bytes(u_cha 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_head } -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_hea 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_hea } -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_h } -ngx_int_t +static ngx_int_t ngx_quic_parse_handshake_header(ngx_quic_header_t *pkt) { u_char *p, *end; diff --git a/src/event/ngx_event_quic_transport.h b/src/event/ngx_event_quic_transport.h --- a/src/event/ngx_event_quic_transport.h +++ b/src/event/ngx_event_quic_transport.h @@ -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);