comparison src/event/ngx_event_quic.c @ 8355:ad3a6f069498 quic

Added proper handling of connection close phases. There are following flags in quic connection: closing - true, when a connection close is initiated, for whatever reason draining - true, when a CC frame is received from peer The following state machine is used for closing: +------------------+ | I/HS/AD | +------------------+ | | | | | V | | immediate close initiated: | | reasons: close by top-level protocol, fatal error | | + sends CC (probably with app-level message) | | + starts close_timer: 3 * PTO (current probe timeout) | | | | | V | | +---------+ - Reply to input with CC (rate-limited) | | | CLOSING | - Close/Reset all streams | | +---------+ | | | | | V V | | receives CC | | | | idle | | timer | | | V | | +----------+ | - MUST NOT send anything (MAY send a single CC) | | DRAINING | | - if not already started, starts close_timer: 3 * PTO | +----------+ | - if not already done, close all streams | | | | | | | close_timer fires | | V V +------------------------+ | CLOSED | - clean up all the resources, drop connection +------------------------+ state completely The ngx_quic_close_connection() function gets an "rc" argument, that signals reason of connection closing: NGX_OK - initiated by application (i.e. http/3), follow state machine NGX_DONE - timedout (while idle or draining) NGX_ERROR - fatal error, destroy connection immediately The PTO calculations are not yet implemented, hardcoded value of 5s is used.
author Vladimir Homutov <vl@nginx.com>
date Thu, 23 Apr 2020 13:41:08 +0300
parents d11bc25fc4c3
children 42198f77ac85
comparison
equal deleted inserted replaced
8354:d11bc25fc4c3 8355:ad3a6f069498
92 92
93 ngx_ssl_t *ssl; 93 ngx_ssl_t *ssl;
94 94
95 ngx_event_t push; 95 ngx_event_t push;
96 ngx_event_t retry; 96 ngx_event_t retry;
97 ngx_event_t close;
97 ngx_queue_t free_frames; 98 ngx_queue_t free_frames;
99 ngx_msec_t last_cc;
98 100
99 #if (NGX_DEBUG) 101 #if (NGX_DEBUG)
100 ngx_uint_t nframes; 102 ngx_uint_t nframes;
101 #endif 103 #endif
102 104
106 uint64_t cur_streams; 108 uint64_t cur_streams;
107 uint64_t max_streams; 109 uint64_t max_streams;
108 110
109 unsigned send_timer_set:1; 111 unsigned send_timer_set:1;
110 unsigned closing:1; 112 unsigned closing:1;
113 unsigned draining:1;
111 unsigned key_phase:1; 114 unsigned key_phase:1;
112 }; 115 };
113 116
114 117
115 typedef ngx_int_t (*ngx_quic_frame_handler_pt)(ngx_connection_t *c, 118 typedef ngx_int_t (*ngx_quic_frame_handler_pt)(ngx_connection_t *c,
140 ngx_quic_tp_t *tp, ngx_quic_header_t *pkt, 143 ngx_quic_tp_t *tp, ngx_quic_header_t *pkt,
141 ngx_connection_handler_pt handler); 144 ngx_connection_handler_pt handler);
142 static ngx_int_t ngx_quic_init_connection(ngx_connection_t *c); 145 static ngx_int_t ngx_quic_init_connection(ngx_connection_t *c);
143 static void ngx_quic_input_handler(ngx_event_t *rev); 146 static void ngx_quic_input_handler(ngx_event_t *rev);
144 147
145 static void ngx_quic_close_connection(ngx_connection_t *c); 148 static void ngx_quic_close_connection(ngx_connection_t *c, ngx_int_t rc);
146 static ngx_int_t ngx_quic_close_quic(ngx_connection_t *c); 149 static ngx_int_t ngx_quic_close_quic(ngx_connection_t *c, ngx_int_t rc);
150 static void ngx_quic_close_timer_handler(ngx_event_t *ev);
147 static ngx_int_t ngx_quic_close_streams(ngx_connection_t *c, 151 static ngx_int_t ngx_quic_close_streams(ngx_connection_t *c,
148 ngx_quic_connection_t *qc); 152 ngx_quic_connection_t *qc);
149 153
150 static ngx_int_t ngx_quic_input(ngx_connection_t *c, ngx_buf_t *b); 154 static ngx_int_t ngx_quic_input(ngx_connection_t *c, ngx_buf_t *b);
151 static ngx_int_t ngx_quic_initial_input(ngx_connection_t *c, 155 static ngx_int_t ngx_quic_initial_input(ngx_connection_t *c,
156 ngx_quic_header_t *pkt); 160 ngx_quic_header_t *pkt);
157 static ngx_int_t ngx_quic_app_input(ngx_connection_t *c, 161 static ngx_int_t ngx_quic_app_input(ngx_connection_t *c,
158 ngx_quic_header_t *pkt); 162 ngx_quic_header_t *pkt);
159 static ngx_int_t ngx_quic_payload_handler(ngx_connection_t *c, 163 static ngx_int_t ngx_quic_payload_handler(ngx_connection_t *c,
160 ngx_quic_header_t *pkt); 164 ngx_quic_header_t *pkt);
165 static ngx_int_t ngx_quic_send_cc(ngx_connection_t *c,
166 enum ssl_encryption_level_t level, ngx_uint_t err);
161 167
162 static ngx_int_t ngx_quic_handle_ack_frame(ngx_connection_t *c, 168 static ngx_int_t ngx_quic_handle_ack_frame(ngx_connection_t *c,
163 ngx_quic_header_t *pkt, ngx_quic_ack_frame_t *f); 169 ngx_quic_header_t *pkt, ngx_quic_ack_frame_t *f);
164 static ngx_int_t ngx_quic_handle_ack_frame_range(ngx_connection_t *c, 170 static ngx_int_t ngx_quic_handle_ack_frame_range(ngx_connection_t *c,
165 ngx_quic_send_ctx_t *ctx, uint64_t min, uint64_t max); 171 ngx_quic_send_ctx_t *ctx, uint64_t min, uint64_t max);
433 static int 439 static int
434 ngx_quic_send_alert(ngx_ssl_conn_t *ssl_conn, enum ssl_encryption_level_t level, 440 ngx_quic_send_alert(ngx_ssl_conn_t *ssl_conn, enum ssl_encryption_level_t level,
435 uint8_t alert) 441 uint8_t alert)
436 { 442 {
437 ngx_connection_t *c; 443 ngx_connection_t *c;
438 ngx_quic_frame_t *frame;
439 444
440 c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn); 445 c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
441 446
442 ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, 447 ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
443 "ngx_quic_send_alert(), lvl=%d, alert=%d", 448 "ngx_quic_send_alert(), lvl=%d, alert=%d",
444 (int) level, (int) alert); 449 (int) level, (int) alert);
445 450
446 frame = ngx_quic_alloc_frame(c, 0); 451 if (c->quic == NULL) {
447 if (frame == NULL) { 452 return 1;
448 return 0; 453 }
449 } 454
450 455 if (ngx_quic_send_cc(c, level, 0x100 + alert) != NGX_OK) {
451 frame->level = level;
452 frame->type = NGX_QUIC_FT_CONNECTION_CLOSE;
453 frame->u.close.error_code = 0x100 + alert;
454 ngx_sprintf(frame->info, "cc from send_alert level=%d", frame->level);
455
456 ngx_quic_queue_frame(c->quic, frame);
457
458 if (ngx_quic_output(c) != NGX_OK) {
459 return 0; 456 return 0;
460 } 457 }
461 458
462 return 1; 459 return 1;
463 } 460 }
482 pkt.raw = b; 479 pkt.raw = b;
483 pkt.data = b->start; 480 pkt.data = b->start;
484 pkt.len = b->last - b->start; 481 pkt.len = b->last - b->start;
485 482
486 if (ngx_quic_new_connection(c, ssl, tp, &pkt, handler) != NGX_OK) { 483 if (ngx_quic_new_connection(c, ssl, tp, &pkt, handler) != NGX_OK) {
487 ngx_quic_close_connection(c); 484 ngx_quic_close_connection(c, NGX_ERROR);
488 return; 485 return;
489 } 486 }
490 487
491 ngx_add_timer(c->read, c->quic->tp.max_idle_timeout); 488 ngx_add_timer(c->read, c->quic->tp.max_idle_timeout);
492 489
719 c = rev->data; 716 c = rev->data;
720 qc = c->quic; 717 qc = c->quic;
721 718
722 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, rev->log, 0, "quic input handler"); 719 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, rev->log, 0, "quic input handler");
723 720
724 if (qc->closing) {
725 ngx_quic_close_connection(c);
726 return;
727 }
728
729 if (rev->timedout) { 721 if (rev->timedout) {
730 ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); 722 ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
731 ngx_quic_close_connection(c); 723 ngx_quic_close_connection(c, NGX_DONE);
732 return; 724 return;
733 } 725 }
734 726
735 if (c->close) { 727 if (c->close) {
736 ngx_quic_close_connection(c); 728 ngx_quic_close_connection(c, NGX_ERROR);
737 return; 729 return;
738 } 730 }
739 731
740 n = c->recv(c, b.start, b.end - b.start); 732 n = c->recv(c, b.start, b.end - b.start);
741 733
742 if (n == NGX_AGAIN) { 734 if (n == NGX_AGAIN) {
735 if (qc->closing) {
736 ngx_quic_close_connection(c, NGX_OK);
737 }
743 return; 738 return;
744 } 739 }
745 740
746 if (n == NGX_ERROR) { 741 if (n == NGX_ERROR) {
747 c->read->eof = 1; 742 c->read->eof = 1;
748 ngx_quic_close_connection(c); 743 ngx_quic_close_connection(c, NGX_ERROR);
749 return; 744 return;
750 } 745 }
751 746
752 b.last += n; 747 b.last += n;
753 748
754 if (ngx_quic_input(c, &b) != NGX_OK) { 749 if (ngx_quic_input(c, &b) != NGX_OK) {
755 ngx_quic_close_connection(c); 750 ngx_quic_close_connection(c, NGX_ERROR);
756 return; 751 return;
757 } 752 }
758 753
759 qc->send_timer_set = 0; 754 qc->send_timer_set = 0;
760 ngx_add_timer(rev, qc->tp.max_idle_timeout); 755 ngx_add_timer(rev, qc->tp.max_idle_timeout);
761 } 756 }
762 757
763 758
764 static void 759 static void
765 ngx_quic_close_connection(ngx_connection_t *c) 760 ngx_quic_close_connection(ngx_connection_t *c, ngx_int_t rc)
766 { 761 {
767 ngx_pool_t *pool; 762 ngx_pool_t *pool;
768 763
769 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "close quic connection"); 764 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
770 765 "close quic connection, rc: %i", rc);
771 if (c->quic && ngx_quic_close_quic(c) == NGX_AGAIN) { 766
767 if (!c->quic) {
768 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
769 "close quic connection: early error");
770
771 } else if (ngx_quic_close_quic(c, rc) == NGX_AGAIN) {
772 return; 772 return;
773 } 773 }
774 774
775 if (c->ssl) { 775 if (c->ssl) {
776 (void) ngx_ssl_shutdown(c); 776 (void) ngx_ssl_shutdown(c);
793 ngx_destroy_pool(pool); 793 ngx_destroy_pool(pool);
794 } 794 }
795 795
796 796
797 static ngx_int_t 797 static ngx_int_t
798 ngx_quic_close_quic(ngx_connection_t *c) 798 ngx_quic_close_quic(ngx_connection_t *c, ngx_int_t rc)
799 { 799 {
800 ngx_uint_t i; 800 ngx_uint_t i;
801 ngx_quic_connection_t *qc; 801 ngx_quic_connection_t *qc;
802 enum ssl_encryption_level_t level;
802 803
803 qc = c->quic; 804 qc = c->quic;
804 805
805 qc->closing = 1; 806 if (!qc->closing) {
807
808 if (rc == NGX_OK) {
809
810 /*
811 * 10.3. Immediate Close
812 *
813 * An endpoint sends a CONNECTION_CLOSE frame (Section 19.19) to
814 * terminate the connection immediately.
815 */
816 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
817 "quic immediate close, drain = %d", qc->draining);
818
819 switch (qc->state) {
820 case NGX_QUIC_ST_INITIAL:
821 level = ssl_encryption_initial;
822 break;
823
824 case NGX_QUIC_ST_HANDSHAKE:
825 level = ssl_encryption_handshake;
826 break;
827
828 default: /* NGX_QUIC_ST_APPLICATION/EARLY_DATA */
829 level = ssl_encryption_application;
830 break;
831 }
832
833 if (ngx_quic_send_cc(c, level, NGX_QUIC_ERR_NO_ERROR) == NGX_OK) {
834
835 qc->close.log = c->log;
836 qc->close.data = c;
837 qc->close.handler = ngx_quic_close_timer_handler;
838 qc->close.cancelable = 1;
839
840 ngx_add_timer(&qc->close, 3 * NGX_QUIC_HARDCODED_PTO);
841 }
842
843 } else if (rc == NGX_DONE) {
844
845 /*
846 * 10.2. Idle Timeout
847 *
848 * If the idle timeout is enabled by either peer, a connection is
849 * silently closed and its state is discarded when it remains idle
850 */
851
852 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
853 "quic closing %s connection",
854 qc->draining ? "drained" : "idle");
855
856 } else {
857 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
858 "quic immediate close due to fatal error");
859 }
860
861 qc->closing = 1;
862 }
863
864 if (rc == NGX_ERROR && qc->close.timer_set) {
865 /* do not wait for timer in case of fatal error */
866 ngx_del_timer(&qc->close);
867 }
806 868
807 if (ngx_quic_close_streams(c, qc) == NGX_AGAIN) { 869 if (ngx_quic_close_streams(c, qc) == NGX_AGAIN) {
870 return NGX_AGAIN;
871 }
872
873 if (qc->close.timer_set) {
808 return NGX_AGAIN; 874 return NGX_AGAIN;
809 } 875 }
810 876
811 for (i = 0; i < NGX_QUIC_ENCRYPTION_LAST; i++) { 877 for (i = 0; i < NGX_QUIC_ENCRYPTION_LAST; i++) {
812 ngx_quic_free_frames(c, &qc->crypto[i].frames); 878 ngx_quic_free_frames(c, &qc->crypto[i].frames);
823 889
824 if (qc->retry.timer_set) { 890 if (qc->retry.timer_set) {
825 ngx_del_timer(&qc->retry); 891 ngx_del_timer(&qc->retry);
826 } 892 }
827 893
894 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
895 "quic part of connection is terminated");
896
897 /* may be tested from SSL callback during SSL shutdown */
898 c->quic = NULL;
899
828 return NGX_OK; 900 return NGX_OK;
901 }
902
903
904 static void
905 ngx_quic_close_timer_handler(ngx_event_t *ev)
906 {
907 ngx_connection_t *c;
908
909 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0, "close timer");
910
911 c = ev->data;
912 ngx_quic_close_connection(c, NGX_DONE);
829 } 913 }
830 914
831 915
832 static ngx_int_t 916 static ngx_int_t
833 ngx_quic_close_streams(ngx_connection_t *c, ngx_quic_connection_t *qc) 917 ngx_quic_close_streams(ngx_connection_t *c, ngx_quic_connection_t *qc)
1201 ssize_t len; 1285 ssize_t len;
1202 ngx_uint_t ack_this, do_close; 1286 ngx_uint_t ack_this, do_close;
1203 ngx_quic_frame_t frame, *ack_frame; 1287 ngx_quic_frame_t frame, *ack_frame;
1204 ngx_quic_connection_t *qc; 1288 ngx_quic_connection_t *qc;
1205 1289
1206
1207 qc = c->quic; 1290 qc = c->quic;
1291
1292 if (qc->closing) {
1293 /*
1294 * 10.1 Closing and Draining Connection States
1295 * ... delayed or reordered packets are properly discarded.
1296 *
1297 * An endpoint retains only enough information to generate
1298 * a packet containing a CONNECTION_CLOSE frame and to identify
1299 * packets as belonging to the connection.
1300 */
1301 return ngx_quic_send_cc(c, pkt->level, NGX_QUIC_ERR_NO_ERROR);
1302 }
1208 1303
1209 p = pkt->payload.data; 1304 p = pkt->payload.data;
1210 end = p + pkt->payload.len; 1305 end = p + pkt->payload.len;
1211 1306
1212 ack_this = 0; 1307 ack_this = 0;
1337 "trailing garbage in payload: %ui bytes", end - p); 1432 "trailing garbage in payload: %ui bytes", end - p);
1338 return NGX_ERROR; 1433 return NGX_ERROR;
1339 } 1434 }
1340 1435
1341 if (do_close) { 1436 if (do_close) {
1342 return NGX_DONE; 1437 qc->draining = 1;
1438 ngx_quic_close_connection(c, NGX_OK);
1439 return NGX_OK;
1343 } 1440 }
1344 1441
1345 if (ack_this == 0) { 1442 if (ack_this == 0) {
1346 /* do not ack packets with ACKs and PADDING */ 1443 /* do not ack packets with ACKs and PADDING */
1347 return NGX_OK; 1444 return NGX_OK;
1368 1465
1369 ngx_sprintf(ack_frame->info, "ACK for PN=%d from frame handler level=%d", pkt->pn, ack_frame->level); 1466 ngx_sprintf(ack_frame->info, "ACK for PN=%d from frame handler level=%d", pkt->pn, ack_frame->level);
1370 ngx_quic_queue_frame(qc, ack_frame); 1467 ngx_quic_queue_frame(qc, ack_frame);
1371 1468
1372 return NGX_OK; 1469 return NGX_OK;
1470 }
1471
1472
1473 static ngx_int_t
1474 ngx_quic_send_cc(ngx_connection_t *c, enum ssl_encryption_level_t level,
1475 ngx_uint_t err)
1476 {
1477 ngx_quic_frame_t *frame;
1478 ngx_quic_connection_t *qc;
1479
1480 qc = c->quic;
1481
1482 if (qc->draining) {
1483 return NGX_OK;
1484 }
1485
1486 if (qc->closing
1487 && ngx_current_msec - qc->last_cc < NGX_QUIC_CC_MIN_INTERVAL)
1488 {
1489 /* dot not send CC too often */
1490 return NGX_OK;
1491 }
1492
1493 frame = ngx_quic_alloc_frame(c, 0);
1494 if (frame == NULL) {
1495 return NGX_ERROR;
1496 }
1497
1498 frame->level = level;
1499 frame->type = NGX_QUIC_FT_CONNECTION_CLOSE;
1500 frame->u.close.error_code = err;
1501 ngx_sprintf(frame->info, "cc from send_cc err=%ui level=%d", err,
1502 frame->level);
1503
1504 ngx_quic_queue_frame(c->quic, frame);
1505
1506 qc->last_cc = ngx_current_msec;
1507
1508 return ngx_quic_output(c);
1373 } 1509 }
1374 1510
1375 1511
1376 static ngx_int_t 1512 static ngx_int_t
1377 ngx_quic_handle_ack_frame(ngx_connection_t *c, ngx_quic_header_t *pkt, 1513 ngx_quic_handle_ack_frame(ngx_connection_t *c, ngx_quic_header_t *pkt,
2155 if (rc == NGX_OK) { 2291 if (rc == NGX_OK) {
2156 /* 2292 /*
2157 * frames are moved into the sent queue 2293 * frames are moved into the sent queue
2158 * to wait for ack/be retransmitted 2294 * to wait for ack/be retransmitted
2159 */ 2295 */
2160 ngx_queue_add(&ctx->sent, &range); 2296 if (qc->closing) {
2297 /* if we are closing, any ack will be discarded */
2298 ngx_quic_free_frames(c, &range);
2299
2300 } else {
2301 ngx_queue_add(&ctx->sent, &range);
2302 }
2161 2303
2162 } else if (rc == NGX_DONE) { 2304 } else if (rc == NGX_DONE) {
2163 2305
2164 /* no ack is expected for this frames, can free them */ 2306 /* no ack is expected for this frames, can free them */
2165 ngx_quic_free_frames(c, &range); 2307 ngx_quic_free_frames(c, &range);
2361 2503
2362 wait = 0; 2504 wait = 0;
2363 2505
2364 for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) { 2506 for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) {
2365 if (ngx_quic_retransmit(c, &qc->send_ctx[i], &nswait) != NGX_OK) { 2507 if (ngx_quic_retransmit(c, &qc->send_ctx[i], &nswait) != NGX_OK) {
2366 ngx_quic_close_connection(c); 2508 ngx_quic_close_connection(c, NGX_ERROR);
2367 return; 2509 return;
2368 } 2510 }
2369 2511
2370 if (i == 0) { 2512 if (i == 0) {
2371 wait = nswait; 2513 wait = nswait;
2389 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0, "push timer"); 2531 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0, "push timer");
2390 2532
2391 c = ev->data; 2533 c = ev->data;
2392 2534
2393 if (ngx_quic_output(c) != NGX_OK) { 2535 if (ngx_quic_output(c) != NGX_OK) {
2394 ngx_quic_close_connection(c); 2536 ngx_quic_close_connection(c, NGX_ERROR);
2395 return; 2537 return;
2396 } 2538 }
2397 } 2539 }
2398 2540
2399 2541
2807 2949
2808 ngx_rbtree_delete(&qc->streams.tree, &qs->node); 2950 ngx_rbtree_delete(&qc->streams.tree, &qs->node);
2809 ngx_quic_free_frames(pc, &qs->fs.frames); 2951 ngx_quic_free_frames(pc, &qs->fs.frames);
2810 2952
2811 if (qc->closing) { 2953 if (qc->closing) {
2954 /* schedule handler call to continue ngx_quic_close_connection() */
2812 ngx_post_event(pc->read, &ngx_posted_events); 2955 ngx_post_event(pc->read, &ngx_posted_events);
2813 return; 2956 return;
2814 } 2957 }
2815 2958
2816 if ((qs->id & 0x03) == NGX_QUIC_STREAM_UNIDIRECTIONAL) { 2959 if ((qs->id & 0x03) == NGX_QUIC_STREAM_UNIDIRECTIONAL) {