comparison src/event/quic/ngx_event_quic.c @ 8971:1e2f4e9c8195 quic

QUIC: reworked migration handling. The quic connection now holds active, backup and probe paths instead of sockets. The number of migration paths is now limited and cannot be inflated by a bad client or an attacker. The client id is now associated with path rather than socket. This allows to simplify processing of output and connection ids handling. New migration abandons any previously started migrations. This allows to free consumed client ids and request new for use in future migrations and make progress in case when connection id limit is hit during migration. A path now can be revalidated without losing its state. The patch also fixes various issues with NAT rebinding case handling: - paths are now validated (previously, there was no validation and paths were left in limited state) - attempt to reuse id on different path is now again verified (this was broken in 40445fc7c403) - former path is now validated in case of apparent migration
author Vladimir Homutov <vl@nginx.com>
date Wed, 19 Jan 2022 22:39:24 +0300
parents 03b40440c13d
children c37ea624c307
comparison
equal deleted inserted replaced
8970:7106a918a277 8971:1e2f4e9c8195
129 ngx_str_t scid; 129 ngx_str_t scid;
130 ngx_quic_connection_t *qc; 130 ngx_quic_connection_t *qc;
131 131
132 qc = ngx_quic_get_connection(c); 132 qc = ngx_quic_get_connection(c);
133 133
134 scid.data = qc->socket->cid->id; 134 scid.data = qc->path->cid->id;
135 scid.len = qc->socket->cid->len; 135 scid.len = qc->path->cid->len;
136 136
137 if (scid.len != ctp->initial_scid.len 137 if (scid.len != ctp->initial_scid.len
138 || ngx_memcmp(scid.data, ctp->initial_scid.data, scid.len) != 0) 138 || ngx_memcmp(scid.data, ctp->initial_scid.data, scid.len) != 0)
139 { 139 {
140 ngx_log_error(NGX_LOG_INFO, c->log, 0, 140 ngx_log_error(NGX_LOG_INFO, c->log, 0,
371 q != ngx_queue_sentinel(&qc->client_ids); 371 q != ngx_queue_sentinel(&qc->client_ids);
372 q = ngx_queue_next(q)) 372 q = ngx_queue_next(q))
373 { 373 {
374 cid = ngx_queue_data(q, ngx_quic_client_id_t, queue); 374 cid = ngx_queue_data(q, ngx_quic_client_id_t, queue);
375 375
376 if (cid->seqnum == 0 || cid->refcnt == 0) { 376 if (cid->seqnum == 0 || !cid->used) {
377 /* 377 /*
378 * No stateless reset token in initial connection id. 378 * No stateless reset token in initial connection id.
379 * Don't accept a token from an unused connection id. 379 * Don't accept a token from an unused connection id.
380 */ 380 */
381 continue; 381 continue;
671 { 671 {
672 size_t size; 672 size_t size;
673 u_char *p, *start; 673 u_char *p, *start;
674 ngx_int_t rc; 674 ngx_int_t rc;
675 ngx_uint_t good; 675 ngx_uint_t good;
676 ngx_quic_path_t *path;
676 ngx_quic_header_t pkt; 677 ngx_quic_header_t pkt;
677 ngx_quic_connection_t *qc; 678 ngx_quic_connection_t *qc;
678 679
679 good = 0; 680 good = 0;
681 path = NULL;
680 682
681 size = b->last - b->pos; 683 size = b->last - b->pos;
682 684
683 p = start = b->pos; 685 p = start = b->pos;
684 686
688 pkt.raw = b; 690 pkt.raw = b;
689 pkt.data = p; 691 pkt.data = p;
690 pkt.len = b->last - p; 692 pkt.len = b->last - p;
691 pkt.log = c->log; 693 pkt.log = c->log;
692 pkt.first = (p == start) ? 1 : 0; 694 pkt.first = (p == start) ? 1 : 0;
695 pkt.path = path;
693 pkt.flags = p[0]; 696 pkt.flags = p[0];
694 pkt.raw->pos++; 697 pkt.raw->pos++;
695 698
696 rc = ngx_quic_handle_packet(c, conf, &pkt); 699 rc = ngx_quic_handle_packet(c, conf, &pkt);
697 700
717 } 720 }
718 721
719 if (rc == NGX_OK) { 722 if (rc == NGX_OK) {
720 good = 1; 723 good = 1;
721 } 724 }
725
726 path = pkt.path; /* preserve packet path from 1st packet */
722 727
723 /* NGX_OK || NGX_DECLINED */ 728 /* NGX_OK || NGX_DECLINED */
724 729
725 /* 730 /*
726 * we get NGX_DECLINED when there are no keys [yet] available 731 * we get NGX_DECLINED when there are no keys [yet] available
823 "quic version mismatch: 0x%xD", pkt->version); 828 "quic version mismatch: 0x%xD", pkt->version);
824 return NGX_DECLINED; 829 return NGX_DECLINED;
825 } 830 }
826 831
827 if (pkt->first) { 832 if (pkt->first) {
828 if (ngx_quic_find_path(c, c->udp->dgram->sockaddr, 833 if (ngx_cmp_sockaddr(c->udp->dgram->sockaddr,
829 c->udp->dgram->socklen) 834 c->udp->dgram->socklen,
830 == NULL) 835 qc->path->sockaddr, qc->path->socklen, 1)
836 != NGX_OK)
831 { 837 {
832 /* packet comes from unknown path, possibly migration */ 838 /* packet comes from unknown path, possibly migration */
833 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, 839 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
834 "quic too early migration attempt"); 840 "quic too early migration attempt");
835 return NGX_DECLINED; 841 return NGX_DONE;
836 } 842 }
837 } 843 }
838 844
839 if (ngx_quic_check_csid(qc, pkt) != NGX_OK) { 845 if (ngx_quic_check_csid(qc, pkt) != NGX_OK) {
840 return NGX_DECLINED; 846 return NGX_DECLINED;
989 return rc; 995 return rc;
990 } 996 }
991 997
992 pkt->decrypted = 1; 998 pkt->decrypted = 1;
993 999
994 if (pkt->first) { 1000 c->log->action = "handling decrypted packet";
995 if (ngx_quic_update_paths(c, pkt) != NGX_OK) { 1001
996 return NGX_ERROR; 1002 if (pkt->path == NULL) {
1003 rc = ngx_quic_set_path(c, pkt);
1004 if (rc != NGX_OK) {
1005 return rc;
997 } 1006 }
998 } 1007 }
999 1008
1000 if (c->ssl == NULL) { 1009 if (c->ssl == NULL) {
1001 if (ngx_quic_init_connection(c) != NGX_OK) { 1010 if (ngx_quic_init_connection(c) != NGX_OK) {
1010 * The successful use of Handshake packets indicates 1019 * The successful use of Handshake packets indicates
1011 * that no more Initial packets need to be exchanged 1020 * that no more Initial packets need to be exchanged
1012 */ 1021 */
1013 ngx_quic_discard_ctx(c, ssl_encryption_initial); 1022 ngx_quic_discard_ctx(c, ssl_encryption_initial);
1014 1023
1015 if (qc->socket->path->state != NGX_QUIC_PATH_VALIDATED) { 1024 if (!qc->path->validated) {
1016 qc->socket->path->state = NGX_QUIC_PATH_VALIDATED; 1025 qc->path->validated = 1;
1017 qc->socket->path->limited = 0; 1026 qc->path->limited = 0;
1027 ngx_quic_path_dbg(c, "in handshake", qc->path);
1018 ngx_post_event(&qc->push, &ngx_posted_events); 1028 ngx_post_event(&qc->push, &ngx_posted_events);
1019 } 1029 }
1020 } 1030 }
1021 1031
1022 if (qc->closing) { 1032 if (qc->closing) {
1151 ssize_t len; 1161 ssize_t len;
1152 ngx_buf_t buf; 1162 ngx_buf_t buf;
1153 ngx_uint_t do_close, nonprobing; 1163 ngx_uint_t do_close, nonprobing;
1154 ngx_chain_t chain; 1164 ngx_chain_t chain;
1155 ngx_quic_frame_t frame; 1165 ngx_quic_frame_t frame;
1156 ngx_quic_socket_t *qsock;
1157 ngx_quic_connection_t *qc; 1166 ngx_quic_connection_t *qc;
1158 1167
1159 qc = ngx_quic_get_connection(c); 1168 qc = ngx_quic_get_connection(c);
1160 1169
1161 p = pkt->payload.data; 1170 p = pkt->payload.data;
1333 1342
1334 break; 1343 break;
1335 1344
1336 case NGX_QUIC_FT_PATH_CHALLENGE: 1345 case NGX_QUIC_FT_PATH_CHALLENGE:
1337 1346
1338 if (ngx_quic_handle_path_challenge_frame(c, &frame.u.path_challenge) 1347 if (ngx_quic_handle_path_challenge_frame(c, pkt,
1348 &frame.u.path_challenge)
1339 != NGX_OK) 1349 != NGX_OK)
1340 { 1350 {
1341 return NGX_ERROR; 1351 return NGX_ERROR;
1342 } 1352 }
1343 1353
1392 if (do_close) { 1402 if (do_close) {
1393 qc->draining = 1; 1403 qc->draining = 1;
1394 ngx_quic_close_connection(c, NGX_OK); 1404 ngx_quic_close_connection(c, NGX_OK);
1395 } 1405 }
1396 1406
1397 qsock = ngx_quic_get_socket(c); 1407 if (pkt->path != qc->path && nonprobing) {
1398 1408
1399 if (qsock != qc->socket) {
1400
1401 if (qsock->path != qc->socket->path && nonprobing) {
1402 /*
1403 * RFC 9000, 9.2. Initiating Connection Migration
1404 *
1405 * An endpoint can migrate a connection to a new local
1406 * address by sending packets containing non-probing frames
1407 * from that address.
1408 */
1409 if (ngx_quic_handle_migration(c, pkt) != NGX_OK) {
1410 return NGX_ERROR;
1411 }
1412 }
1413 /* 1409 /*
1414 * else: packet arrived via non-default socket; 1410 * RFC 9000, 9.2. Initiating Connection Migration
1415 * no reason to change active path 1411 *
1412 * An endpoint can migrate a connection to a new local
1413 * address by sending packets containing non-probing frames
1414 * from that address.
1416 */ 1415 */
1416 if (ngx_quic_handle_migration(c, pkt) != NGX_OK) {
1417 return NGX_ERROR;
1418 }
1417 } 1419 }
1418 1420
1419 if (ngx_quic_ack_packet(c, pkt) != NGX_OK) { 1421 if (ngx_quic_ack_packet(c, pkt) != NGX_OK) {
1420 return NGX_ERROR; 1422 return NGX_ERROR;
1421 } 1423 }