Mercurial > hg > nginx-quic
diff src/event/quic/ngx_event_quic.c @ 8423:4117aa7fa38e quic
QUIC: connection migration.
The patch adds proper transitions between multiple networking addresses that
can be used by a single quic connection. New networking paths are validated
using PATH_CHALLENGE/PATH_RESPONSE frames.
author | Vladimir Homutov <vl@nginx.com> |
---|---|
date | Thu, 29 Apr 2021 15:35:02 +0300 |
parents | 46161c610919 |
children | 901126931bd5 |
line wrap: on
line diff
--- a/src/event/quic/ngx_event_quic.c +++ b/src/event/quic/ngx_event_quic.c @@ -87,7 +87,6 @@ ngx_quic_connstate_dbg(ngx_connection_t p = ngx_slprintf(p, last, "%s", qc->closing ? " closing" : ""); p = ngx_slprintf(p, last, "%s", qc->draining ? " draining" : ""); p = ngx_slprintf(p, last, "%s", qc->key_phase ? " kp" : ""); - p = ngx_slprintf(p, last, "%s", qc->validated? " valid" : ""); } else { p = ngx_slprintf(p, last, " early"); @@ -127,12 +126,16 @@ ngx_quic_connstate_dbg(ngx_connection_t ngx_int_t ngx_quic_apply_transport_params(ngx_connection_t *c, ngx_quic_tp_t *ctp) { + ngx_str_t scid; ngx_quic_connection_t *qc; qc = ngx_quic_get_connection(c); - if (qc->scid.len != ctp->initial_scid.len - || ngx_memcmp(qc->scid.data, ctp->initial_scid.data, qc->scid.len) != 0) + scid.data = qc->socket->cid->id; + scid.len = qc->socket->cid->len; + + if (scid.len != ctp->initial_scid.len + || ngx_memcmp(scid.data, ctp->initial_scid.data, scid.len) != 0) { ngx_log_error(NGX_LOG_INFO, c->log, 0, "quic client initial_source_connection_id mismatch"); @@ -277,8 +280,6 @@ ngx_quic_new_connection(ngx_connection_t * qc->latest_rtt = 0 */ - qc->received = pkt->raw->last - pkt->raw->start; - qc->pto.log = c->log; qc->pto.data = c; qc->pto.handler = ngx_quic_pto_handler; @@ -289,19 +290,14 @@ ngx_quic_new_connection(ngx_connection_t qc->push.handler = ngx_quic_push_handler; qc->push.cancelable = 1; + qc->path_validation.log = c->log; + qc->path_validation.data = c; + qc->path_validation.handler = ngx_quic_path_validation_handler; + qc->path_validation.cancelable = 1; + qc->conf = conf; qc->tp = conf->tp; - if (qc->tp.disable_active_migration) { - qc->sockaddr = ngx_palloc(c->pool, c->socklen); - if (qc->sockaddr == NULL) { - return NULL; - } - - ngx_memcpy(qc->sockaddr, c->sockaddr, c->socklen); - qc->socklen = c->socklen; - } - ctp = &qc->ctp; /* defaults to be used before actual client parameters are received */ @@ -338,10 +334,13 @@ ngx_quic_new_connection(ngx_connection_t qc->validated = pkt->validated; - if (ngx_quic_setup_connection_ids(c, qc, pkt) != NGX_OK) { + if (ngx_quic_open_sockets(c, qc, pkt) != NGX_OK) { return NULL; } + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic connection created"); + return qc; } @@ -425,20 +424,8 @@ ngx_quic_input_handler(ngx_event_t *rev) return; } - if (qc->tp.disable_active_migration) { - if (c->socklen != qc->socklen - || ngx_memcmp(c->sockaddr, qc->sockaddr, c->socklen) != 0) - { - ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, - "quic dropping packet from new address"); - return; - } - } - b = c->udp->dgram->buffer; - qc->received += (b->last - b->pos); - rc = ngx_quic_input(c, b, NULL); if (rc == NGX_ERROR) { @@ -506,9 +493,7 @@ static ngx_int_t ngx_quic_close_quic(ngx_connection_t *c, ngx_int_t rc) { ngx_uint_t i; - ngx_queue_t *q; ngx_quic_send_ctx_t *ctx; - ngx_quic_server_id_t *sid; ngx_quic_connection_t *qc; qc = ngx_quic_get_connection(c); @@ -601,23 +586,20 @@ ngx_quic_close_quic(ngx_connection_t *c, ngx_del_timer(&qc->pto); } - if (qc->push.posted) { - ngx_delete_posted_event(&qc->push); + if (qc->path_validation.timer_set) { + ngx_del_timer(&qc->path_validation); } - while (!ngx_queue_empty(&qc->server_ids)) { - q = ngx_queue_head(&qc->server_ids); - sid = ngx_queue_data(q, ngx_quic_server_id_t, queue); - - ngx_queue_remove(q); - ngx_rbtree_delete(&c->listening->rbtree, &sid->udp.node); - qc->nserver_ids--; + if (qc->push.posted) { + ngx_delete_posted_event(&qc->push); } if (qc->close.timer_set) { return NGX_AGAIN; } + ngx_quic_close_sockets(c); + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic part of connection is terminated"); @@ -801,6 +783,11 @@ ngx_quic_process_packet(ngx_connection_t return NGX_DECLINED; } + rc = ngx_quic_check_migration(c, pkt); + if (rc != NGX_OK) { + return rc; + } + if (pkt->level != ssl_encryption_application) { if (pkt->version != qc->version) { @@ -946,6 +933,10 @@ ngx_quic_process_payload(ngx_connection_ pkt->decrypted = 1; + if (ngx_quic_update_paths(c, pkt) != NGX_OK) { + return NGX_ERROR; + } + if (c->ssl == NULL) { if (ngx_quic_init_connection(c) != NGX_OK) { return NGX_ERROR; @@ -959,8 +950,8 @@ ngx_quic_process_payload(ngx_connection_ */ ngx_quic_discard_ctx(c, ssl_encryption_initial); - if (qc->validated == 0) { - qc->validated = 1; + if (qc->socket->path->state != NGX_QUIC_PATH_VALIDATED) { + qc->socket->path->state = NGX_QUIC_PATH_VALIDATED; ngx_post_event(&qc->push, &ngx_posted_events); } } @@ -1015,6 +1006,7 @@ ngx_quic_discard_ctx(ngx_connection_t *c { ngx_queue_t *q; ngx_quic_frame_t *f; + ngx_quic_socket_t *qsock; ngx_quic_send_ctx_t *ctx; ngx_quic_connection_t *qc; @@ -1049,7 +1041,11 @@ ngx_quic_discard_ctx(ngx_connection_t *c } if (level == ssl_encryption_initial) { - ngx_quic_clear_temp_server_ids(c); + /* close temporary listener with odcid */ + qsock = ngx_quic_find_socket(c, NGX_QUIC_UNSET_PN); + if (qsock) { + ngx_quic_close_socket(c, qsock); + } } ctx->send_ack = 0; @@ -1088,9 +1084,10 @@ ngx_quic_handle_frames(ngx_connection_t u_char *end, *p; ssize_t len; ngx_buf_t buf; - ngx_uint_t do_close; + ngx_uint_t do_close, nonprobing; ngx_chain_t chain; ngx_quic_frame_t frame; + ngx_quic_socket_t *qsock; ngx_quic_connection_t *qc; qc = ngx_quic_get_connection(c); @@ -1099,11 +1096,13 @@ ngx_quic_handle_frames(ngx_connection_t end = p + pkt->payload.len; do_close = 0; + nonprobing = 0; while (p < end) { c->log->action = "parsing frames"; + ngx_memzero(&frame, sizeof(ngx_quic_frame_t)); ngx_memzero(&buf, sizeof(ngx_buf_t)); buf.temporary = 1; @@ -1125,6 +1124,19 @@ ngx_quic_handle_frames(ngx_connection_t p += len; switch (frame.type) { + /* probing frames */ + case NGX_QUIC_FT_PADDING: + case NGX_QUIC_FT_PATH_CHALLENGE: + case NGX_QUIC_FT_PATH_RESPONSE: + break; + + /* non-probing frames */ + default: + nonprobing = 1; + break; + } + + switch (frame.type) { case NGX_QUIC_FT_ACK: if (ngx_quic_handle_ack_frame(c, pkt, &frame) != NGX_OK) { @@ -1313,6 +1325,26 @@ ngx_quic_handle_frames(ngx_connection_t ngx_quic_close_connection(c, NGX_OK); } + qsock = ngx_quic_get_socket(c); + + if (qsock != qc->socket) { + + if (qsock->path != qc->socket->path && nonprobing) { + /* + * An endpoint can migrate a connection to a new local + * address by sending packets containing non-probing frames + * from that address. + */ + if (ngx_quic_handle_migration(c, pkt) != NGX_OK) { + return NGX_ERROR; + } + } + /* + * else: packet arrived via non-default socket; + * no reason to change active path + */ + } + if (ngx_quic_ack_packet(c, pkt) != NGX_OK) { return NGX_ERROR; }