Mercurial > hg > nginx
comparison src/event/quic/ngx_event_quic.c @ 8763: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 |
comparison
equal
deleted
inserted
replaced
8762:12f18e0bca09 | 8763:4117aa7fa38e |
---|---|
85 | 85 |
86 p = ngx_slprintf(p, last, "%s", qc->shutdown ? " shutdown" : ""); | 86 p = ngx_slprintf(p, last, "%s", qc->shutdown ? " shutdown" : ""); |
87 p = ngx_slprintf(p, last, "%s", qc->closing ? " closing" : ""); | 87 p = ngx_slprintf(p, last, "%s", qc->closing ? " closing" : ""); |
88 p = ngx_slprintf(p, last, "%s", qc->draining ? " draining" : ""); | 88 p = ngx_slprintf(p, last, "%s", qc->draining ? " draining" : ""); |
89 p = ngx_slprintf(p, last, "%s", qc->key_phase ? " kp" : ""); | 89 p = ngx_slprintf(p, last, "%s", qc->key_phase ? " kp" : ""); |
90 p = ngx_slprintf(p, last, "%s", qc->validated? " valid" : ""); | |
91 | 90 |
92 } else { | 91 } else { |
93 p = ngx_slprintf(p, last, " early"); | 92 p = ngx_slprintf(p, last, " early"); |
94 } | 93 } |
95 | 94 |
125 | 124 |
126 | 125 |
127 ngx_int_t | 126 ngx_int_t |
128 ngx_quic_apply_transport_params(ngx_connection_t *c, ngx_quic_tp_t *ctp) | 127 ngx_quic_apply_transport_params(ngx_connection_t *c, ngx_quic_tp_t *ctp) |
129 { | 128 { |
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 if (qc->scid.len != ctp->initial_scid.len | 134 scid.data = qc->socket->cid->id; |
135 || ngx_memcmp(qc->scid.data, ctp->initial_scid.data, qc->scid.len) != 0) | 135 scid.len = qc->socket->cid->len; |
136 | |
137 if (scid.len != ctp->initial_scid.len | |
138 || ngx_memcmp(scid.data, ctp->initial_scid.data, scid.len) != 0) | |
136 { | 139 { |
137 ngx_log_error(NGX_LOG_INFO, c->log, 0, | 140 ngx_log_error(NGX_LOG_INFO, c->log, 0, |
138 "quic client initial_source_connection_id mismatch"); | 141 "quic client initial_source_connection_id mismatch"); |
139 return NGX_ERROR; | 142 return NGX_ERROR; |
140 } | 143 } |
275 | 278 |
276 /* | 279 /* |
277 * qc->latest_rtt = 0 | 280 * qc->latest_rtt = 0 |
278 */ | 281 */ |
279 | 282 |
280 qc->received = pkt->raw->last - pkt->raw->start; | |
281 | |
282 qc->pto.log = c->log; | 283 qc->pto.log = c->log; |
283 qc->pto.data = c; | 284 qc->pto.data = c; |
284 qc->pto.handler = ngx_quic_pto_handler; | 285 qc->pto.handler = ngx_quic_pto_handler; |
285 qc->pto.cancelable = 1; | 286 qc->pto.cancelable = 1; |
286 | 287 |
287 qc->push.log = c->log; | 288 qc->push.log = c->log; |
288 qc->push.data = c; | 289 qc->push.data = c; |
289 qc->push.handler = ngx_quic_push_handler; | 290 qc->push.handler = ngx_quic_push_handler; |
290 qc->push.cancelable = 1; | 291 qc->push.cancelable = 1; |
291 | 292 |
293 qc->path_validation.log = c->log; | |
294 qc->path_validation.data = c; | |
295 qc->path_validation.handler = ngx_quic_path_validation_handler; | |
296 qc->path_validation.cancelable = 1; | |
297 | |
292 qc->conf = conf; | 298 qc->conf = conf; |
293 qc->tp = conf->tp; | 299 qc->tp = conf->tp; |
294 | |
295 if (qc->tp.disable_active_migration) { | |
296 qc->sockaddr = ngx_palloc(c->pool, c->socklen); | |
297 if (qc->sockaddr == NULL) { | |
298 return NULL; | |
299 } | |
300 | |
301 ngx_memcpy(qc->sockaddr, c->sockaddr, c->socklen); | |
302 qc->socklen = c->socklen; | |
303 } | |
304 | 300 |
305 ctp = &qc->ctp; | 301 ctp = &qc->ctp; |
306 | 302 |
307 /* defaults to be used before actual client parameters are received */ | 303 /* defaults to be used before actual client parameters are received */ |
308 ctp->max_udp_payload_size = ngx_quic_max_udp_payload(c); | 304 ctp->max_udp_payload_size = ngx_quic_max_udp_payload(c); |
336 return NULL; | 332 return NULL; |
337 } | 333 } |
338 | 334 |
339 qc->validated = pkt->validated; | 335 qc->validated = pkt->validated; |
340 | 336 |
341 if (ngx_quic_setup_connection_ids(c, qc, pkt) != NGX_OK) { | 337 if (ngx_quic_open_sockets(c, qc, pkt) != NGX_OK) { |
342 return NULL; | 338 return NULL; |
343 } | 339 } |
340 | |
341 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, | |
342 "quic connection created"); | |
344 | 343 |
345 return qc; | 344 return qc; |
346 } | 345 } |
347 | 346 |
348 | 347 |
423 ngx_quic_close_connection(c, NGX_OK); | 422 ngx_quic_close_connection(c, NGX_OK); |
424 } | 423 } |
425 return; | 424 return; |
426 } | 425 } |
427 | 426 |
428 if (qc->tp.disable_active_migration) { | |
429 if (c->socklen != qc->socklen | |
430 || ngx_memcmp(c->sockaddr, qc->sockaddr, c->socklen) != 0) | |
431 { | |
432 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, | |
433 "quic dropping packet from new address"); | |
434 return; | |
435 } | |
436 } | |
437 | |
438 b = c->udp->dgram->buffer; | 427 b = c->udp->dgram->buffer; |
439 | |
440 qc->received += (b->last - b->pos); | |
441 | 428 |
442 rc = ngx_quic_input(c, b, NULL); | 429 rc = ngx_quic_input(c, b, NULL); |
443 | 430 |
444 if (rc == NGX_ERROR) { | 431 if (rc == NGX_ERROR) { |
445 ngx_quic_close_connection(c, NGX_ERROR); | 432 ngx_quic_close_connection(c, NGX_ERROR); |
504 | 491 |
505 static ngx_int_t | 492 static ngx_int_t |
506 ngx_quic_close_quic(ngx_connection_t *c, ngx_int_t rc) | 493 ngx_quic_close_quic(ngx_connection_t *c, ngx_int_t rc) |
507 { | 494 { |
508 ngx_uint_t i; | 495 ngx_uint_t i; |
509 ngx_queue_t *q; | |
510 ngx_quic_send_ctx_t *ctx; | 496 ngx_quic_send_ctx_t *ctx; |
511 ngx_quic_server_id_t *sid; | |
512 ngx_quic_connection_t *qc; | 497 ngx_quic_connection_t *qc; |
513 | 498 |
514 qc = ngx_quic_get_connection(c); | 499 qc = ngx_quic_get_connection(c); |
515 | 500 |
516 if (!qc->closing) { | 501 if (!qc->closing) { |
599 | 584 |
600 if (qc->pto.timer_set) { | 585 if (qc->pto.timer_set) { |
601 ngx_del_timer(&qc->pto); | 586 ngx_del_timer(&qc->pto); |
602 } | 587 } |
603 | 588 |
589 if (qc->path_validation.timer_set) { | |
590 ngx_del_timer(&qc->path_validation); | |
591 } | |
592 | |
604 if (qc->push.posted) { | 593 if (qc->push.posted) { |
605 ngx_delete_posted_event(&qc->push); | 594 ngx_delete_posted_event(&qc->push); |
606 } | 595 } |
607 | 596 |
608 while (!ngx_queue_empty(&qc->server_ids)) { | |
609 q = ngx_queue_head(&qc->server_ids); | |
610 sid = ngx_queue_data(q, ngx_quic_server_id_t, queue); | |
611 | |
612 ngx_queue_remove(q); | |
613 ngx_rbtree_delete(&c->listening->rbtree, &sid->udp.node); | |
614 qc->nserver_ids--; | |
615 } | |
616 | |
617 if (qc->close.timer_set) { | 597 if (qc->close.timer_set) { |
618 return NGX_AGAIN; | 598 return NGX_AGAIN; |
619 } | 599 } |
600 | |
601 ngx_quic_close_sockets(c); | |
620 | 602 |
621 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, | 603 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, |
622 "quic part of connection is terminated"); | 604 "quic part of connection is terminated"); |
623 | 605 |
624 /* may be tested from SSL callback during SSL shutdown */ | 606 /* may be tested from SSL callback during SSL shutdown */ |
799 ngx_log_error(NGX_LOG_INFO, c->log, 0, | 781 ngx_log_error(NGX_LOG_INFO, c->log, 0, |
800 "quic unsupported version: 0x%xD", pkt->version); | 782 "quic unsupported version: 0x%xD", pkt->version); |
801 return NGX_DECLINED; | 783 return NGX_DECLINED; |
802 } | 784 } |
803 | 785 |
786 rc = ngx_quic_check_migration(c, pkt); | |
787 if (rc != NGX_OK) { | |
788 return rc; | |
789 } | |
790 | |
804 if (pkt->level != ssl_encryption_application) { | 791 if (pkt->level != ssl_encryption_application) { |
805 | 792 |
806 if (pkt->version != qc->version) { | 793 if (pkt->version != qc->version) { |
807 ngx_log_error(NGX_LOG_INFO, c->log, 0, | 794 ngx_log_error(NGX_LOG_INFO, c->log, 0, |
808 "quic version mismatch: 0x%xD", pkt->version); | 795 "quic version mismatch: 0x%xD", pkt->version); |
944 return rc; | 931 return rc; |
945 } | 932 } |
946 | 933 |
947 pkt->decrypted = 1; | 934 pkt->decrypted = 1; |
948 | 935 |
936 if (ngx_quic_update_paths(c, pkt) != NGX_OK) { | |
937 return NGX_ERROR; | |
938 } | |
939 | |
949 if (c->ssl == NULL) { | 940 if (c->ssl == NULL) { |
950 if (ngx_quic_init_connection(c) != NGX_OK) { | 941 if (ngx_quic_init_connection(c) != NGX_OK) { |
951 return NGX_ERROR; | 942 return NGX_ERROR; |
952 } | 943 } |
953 } | 944 } |
957 * 4.10.1. The successful use of Handshake packets indicates | 948 * 4.10.1. The successful use of Handshake packets indicates |
958 * that no more Initial packets need to be exchanged | 949 * that no more Initial packets need to be exchanged |
959 */ | 950 */ |
960 ngx_quic_discard_ctx(c, ssl_encryption_initial); | 951 ngx_quic_discard_ctx(c, ssl_encryption_initial); |
961 | 952 |
962 if (qc->validated == 0) { | 953 if (qc->socket->path->state != NGX_QUIC_PATH_VALIDATED) { |
963 qc->validated = 1; | 954 qc->socket->path->state = NGX_QUIC_PATH_VALIDATED; |
964 ngx_post_event(&qc->push, &ngx_posted_events); | 955 ngx_post_event(&qc->push, &ngx_posted_events); |
965 } | 956 } |
966 } | 957 } |
967 | 958 |
968 if (qc->closing) { | 959 if (qc->closing) { |
1013 void | 1004 void |
1014 ngx_quic_discard_ctx(ngx_connection_t *c, enum ssl_encryption_level_t level) | 1005 ngx_quic_discard_ctx(ngx_connection_t *c, enum ssl_encryption_level_t level) |
1015 { | 1006 { |
1016 ngx_queue_t *q; | 1007 ngx_queue_t *q; |
1017 ngx_quic_frame_t *f; | 1008 ngx_quic_frame_t *f; |
1009 ngx_quic_socket_t *qsock; | |
1018 ngx_quic_send_ctx_t *ctx; | 1010 ngx_quic_send_ctx_t *ctx; |
1019 ngx_quic_connection_t *qc; | 1011 ngx_quic_connection_t *qc; |
1020 | 1012 |
1021 qc = ngx_quic_get_connection(c); | 1013 qc = ngx_quic_get_connection(c); |
1022 | 1014 |
1047 ngx_quic_congestion_ack(c, f); | 1039 ngx_quic_congestion_ack(c, f); |
1048 ngx_quic_free_frame(c, f); | 1040 ngx_quic_free_frame(c, f); |
1049 } | 1041 } |
1050 | 1042 |
1051 if (level == ssl_encryption_initial) { | 1043 if (level == ssl_encryption_initial) { |
1052 ngx_quic_clear_temp_server_ids(c); | 1044 /* close temporary listener with odcid */ |
1045 qsock = ngx_quic_find_socket(c, NGX_QUIC_UNSET_PN); | |
1046 if (qsock) { | |
1047 ngx_quic_close_socket(c, qsock); | |
1048 } | |
1053 } | 1049 } |
1054 | 1050 |
1055 ctx->send_ack = 0; | 1051 ctx->send_ack = 0; |
1056 | 1052 |
1057 ngx_quic_set_lost_timer(c); | 1053 ngx_quic_set_lost_timer(c); |
1086 ngx_quic_handle_frames(ngx_connection_t *c, ngx_quic_header_t *pkt) | 1082 ngx_quic_handle_frames(ngx_connection_t *c, ngx_quic_header_t *pkt) |
1087 { | 1083 { |
1088 u_char *end, *p; | 1084 u_char *end, *p; |
1089 ssize_t len; | 1085 ssize_t len; |
1090 ngx_buf_t buf; | 1086 ngx_buf_t buf; |
1091 ngx_uint_t do_close; | 1087 ngx_uint_t do_close, nonprobing; |
1092 ngx_chain_t chain; | 1088 ngx_chain_t chain; |
1093 ngx_quic_frame_t frame; | 1089 ngx_quic_frame_t frame; |
1090 ngx_quic_socket_t *qsock; | |
1094 ngx_quic_connection_t *qc; | 1091 ngx_quic_connection_t *qc; |
1095 | 1092 |
1096 qc = ngx_quic_get_connection(c); | 1093 qc = ngx_quic_get_connection(c); |
1097 | 1094 |
1098 p = pkt->payload.data; | 1095 p = pkt->payload.data; |
1099 end = p + pkt->payload.len; | 1096 end = p + pkt->payload.len; |
1100 | 1097 |
1101 do_close = 0; | 1098 do_close = 0; |
1099 nonprobing = 0; | |
1102 | 1100 |
1103 while (p < end) { | 1101 while (p < end) { |
1104 | 1102 |
1105 c->log->action = "parsing frames"; | 1103 c->log->action = "parsing frames"; |
1106 | 1104 |
1105 ngx_memzero(&frame, sizeof(ngx_quic_frame_t)); | |
1107 ngx_memzero(&buf, sizeof(ngx_buf_t)); | 1106 ngx_memzero(&buf, sizeof(ngx_buf_t)); |
1108 buf.temporary = 1; | 1107 buf.temporary = 1; |
1109 | 1108 |
1110 chain.buf = &buf; | 1109 chain.buf = &buf; |
1111 chain.next = NULL; | 1110 chain.next = NULL; |
1121 ngx_quic_log_frame(c->log, &frame, 0); | 1120 ngx_quic_log_frame(c->log, &frame, 0); |
1122 | 1121 |
1123 c->log->action = "handling frames"; | 1122 c->log->action = "handling frames"; |
1124 | 1123 |
1125 p += len; | 1124 p += len; |
1125 | |
1126 switch (frame.type) { | |
1127 /* probing frames */ | |
1128 case NGX_QUIC_FT_PADDING: | |
1129 case NGX_QUIC_FT_PATH_CHALLENGE: | |
1130 case NGX_QUIC_FT_PATH_RESPONSE: | |
1131 break; | |
1132 | |
1133 /* non-probing frames */ | |
1134 default: | |
1135 nonprobing = 1; | |
1136 break; | |
1137 } | |
1126 | 1138 |
1127 switch (frame.type) { | 1139 switch (frame.type) { |
1128 | 1140 |
1129 case NGX_QUIC_FT_ACK: | 1141 case NGX_QUIC_FT_ACK: |
1130 if (ngx_quic_handle_ack_frame(c, pkt, &frame) != NGX_OK) { | 1142 if (ngx_quic_handle_ack_frame(c, pkt, &frame) != NGX_OK) { |
1311 if (do_close) { | 1323 if (do_close) { |
1312 qc->draining = 1; | 1324 qc->draining = 1; |
1313 ngx_quic_close_connection(c, NGX_OK); | 1325 ngx_quic_close_connection(c, NGX_OK); |
1314 } | 1326 } |
1315 | 1327 |
1328 qsock = ngx_quic_get_socket(c); | |
1329 | |
1330 if (qsock != qc->socket) { | |
1331 | |
1332 if (qsock->path != qc->socket->path && nonprobing) { | |
1333 /* | |
1334 * An endpoint can migrate a connection to a new local | |
1335 * address by sending packets containing non-probing frames | |
1336 * from that address. | |
1337 */ | |
1338 if (ngx_quic_handle_migration(c, pkt) != NGX_OK) { | |
1339 return NGX_ERROR; | |
1340 } | |
1341 } | |
1342 /* | |
1343 * else: packet arrived via non-default socket; | |
1344 * no reason to change active path | |
1345 */ | |
1346 } | |
1347 | |
1316 if (ngx_quic_ack_packet(c, pkt) != NGX_OK) { | 1348 if (ngx_quic_ack_packet(c, pkt) != NGX_OK) { |
1317 return NGX_ERROR; | 1349 return NGX_ERROR; |
1318 } | 1350 } |
1319 | 1351 |
1320 return NGX_OK; | 1352 return NGX_OK; |