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;