Mercurial > hg > nginx
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 } |