comparison src/event/ngx_event_quic.c @ 7862:fb7422074258 quic

Added generation of CC frames with error on connection termination. When an error occurs, then c->quic->error field may be populated with an appropriate error code, and the CONNECTION CLOSE frame will be sent to the peer before the connection is closed. Otherwise, the error treated as internal and INTERNAL_ERROR code is sent. The pkt->error field is populated by functions processing packets to indicate an error when it does not fit into pass/fail return status.
author Vladimir Homutov <vl@nginx.com>
date Thu, 14 May 2020 15:54:45 +0300
parents 52d0c4832570
children 81f85c479d7e
comparison
equal deleted inserted replaced
7861:52d0c4832570 7862:fb7422074258
32 */ 32 */
33 #define NGX_QUIC_MAX_BUFFERED 65535 33 #define NGX_QUIC_MAX_BUFFERED 65535
34 34
35 35
36 typedef enum { 36 typedef enum {
37 NGX_QUIC_ST_UNAVAIL, /* connection not ready */
37 NGX_QUIC_ST_INITIAL, /* connection just created */ 38 NGX_QUIC_ST_INITIAL, /* connection just created */
38 NGX_QUIC_ST_HANDSHAKE, /* handshake started */ 39 NGX_QUIC_ST_HANDSHAKE, /* handshake started */
39 NGX_QUIC_ST_EARLY_DATA, /* handshake in progress */ 40 NGX_QUIC_ST_EARLY_DATA, /* handshake in progress */
40 NGX_QUIC_ST_APPLICATION /* handshake complete */ 41 NGX_QUIC_ST_APPLICATION /* handshake complete */
41 } ngx_quic_state_t; 42 } ngx_quic_state_t;
116 ngx_quic_streams_t streams; 117 ngx_quic_streams_t streams;
117 ngx_quic_congestion_t congestion; 118 ngx_quic_congestion_t congestion;
118 119
119 uint64_t cur_streams; 120 uint64_t cur_streams;
120 uint64_t max_streams; 121 uint64_t max_streams;
122
123 ngx_uint_t error;
121 124
122 unsigned send_timer_set:1; 125 unsigned send_timer_set:1;
123 unsigned closing:1; 126 unsigned closing:1;
124 unsigned draining:1; 127 unsigned draining:1;
125 unsigned key_phase:1; 128 unsigned key_phase:1;
403 end = p + client_params_len; 406 end = p + client_params_len;
404 407
405 if (ngx_quic_parse_transport_params(p, end, &qc->ctp, c->log) 408 if (ngx_quic_parse_transport_params(p, end, &qc->ctp, c->log)
406 != NGX_OK) 409 != NGX_OK)
407 { 410 {
411 qc->error = NGX_QUIC_ERR_TRANSPORT_PARAMETER_ERROR;
408 return NGX_ERROR; 412 return NGX_ERROR;
409 } 413 }
410 414
411 if (qc->ctp.max_idle_timeout > 0 415 if (qc->ctp.max_idle_timeout > 0
412 && qc->ctp.max_idle_timeout < qc->tp.max_idle_timeout) 416 && qc->ctp.max_idle_timeout < qc->tp.max_idle_timeout)
415 } 419 }
416 420
417 if (qc->ctp.max_packet_size < NGX_QUIC_MIN_INITIAL_SIZE 421 if (qc->ctp.max_packet_size < NGX_QUIC_MIN_INITIAL_SIZE
418 || qc->ctp.max_packet_size > NGX_QUIC_DEFAULT_MAX_PACKET_SIZE) 422 || qc->ctp.max_packet_size > NGX_QUIC_DEFAULT_MAX_PACKET_SIZE)
419 { 423 {
424 qc->error = NGX_QUIC_ERR_TRANSPORT_PARAMETER_ERROR;
420 ngx_log_error(NGX_LOG_INFO, c->log, 0, 425 ngx_log_error(NGX_LOG_INFO, c->log, 0,
421 "quic maximum packet size is invalid"); 426 "quic maximum packet size is invalid");
422 return NGX_ERROR; 427 return NGX_ERROR;
423 } 428 }
424 429
578 qc = ngx_pcalloc(c->pool, sizeof(ngx_quic_connection_t)); 583 qc = ngx_pcalloc(c->pool, sizeof(ngx_quic_connection_t));
579 if (qc == NULL) { 584 if (qc == NULL) {
580 return NGX_ERROR; 585 return NGX_ERROR;
581 } 586 }
582 587
583 qc->state = NGX_QUIC_ST_INITIAL;
584
585 ngx_rbtree_init(&qc->streams.tree, &qc->streams.sentinel, 588 ngx_rbtree_init(&qc->streams.tree, &qc->streams.sentinel,
586 ngx_quic_rbtree_insert_stream); 589 ngx_quic_rbtree_insert_stream);
587 590
588 for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) { 591 for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) {
589 ngx_queue_init(&qc->send_ctx[i].frames); 592 ngx_queue_init(&qc->send_ctx[i].frames);
606 qc->push.data = c; 609 qc->push.data = c;
607 qc->push.handler = ngx_quic_push_handler; 610 qc->push.handler = ngx_quic_push_handler;
608 qc->push.cancelable = 1; 611 qc->push.cancelable = 1;
609 612
610 c->quic = qc; 613 c->quic = qc;
614 qc->state = NGX_QUIC_ST_UNAVAIL;
611 qc->ssl = ssl; 615 qc->ssl = ssl;
612 qc->tp = *tp; 616 qc->tp = *tp;
613 qc->streams.handler = handler; 617 qc->streams.handler = handler;
614 618
615 ctp = &qc->ctp; 619 ctp = &qc->ctp;
641 &qc->odcid) 645 &qc->odcid)
642 != NGX_OK) 646 != NGX_OK)
643 { 647 {
644 return NGX_ERROR; 648 return NGX_ERROR;
645 } 649 }
650
651 qc->state = NGX_QUIC_ST_INITIAL;
646 652
647 if (pkt->token.len) { 653 if (pkt->token.len) {
648 rc = ngx_quic_validate_token(c, pkt); 654 rc = ngx_quic_validate_token(c, pkt);
649 655
650 if (rc == NGX_ERROR) { 656 if (rc == NGX_ERROR) {
894 900
895 /* Retry token */ 901 /* Retry token */
896 902
897 if (qc->token.len) { 903 if (qc->token.len) {
898 if (pkt->token.len != qc->token.len) { 904 if (pkt->token.len != qc->token.len) {
905 qc->error = NGX_QUIC_ERR_INVALID_TOKEN;
899 return NGX_ERROR; 906 return NGX_ERROR;
900 } 907 }
901 908
902 if (ngx_memcmp(pkt->token.data, qc->token.data, pkt->token.len) != 0) { 909 if (ngx_memcmp(pkt->token.data, qc->token.data, pkt->token.len) != 0) {
910 qc->error = NGX_QUIC_ERR_INVALID_TOKEN;
903 return NGX_ERROR; 911 return NGX_ERROR;
904 } 912 }
905 913
906 return NGX_OK; 914 return NGX_OK;
907 } 915 }
914 iv_len = EVP_CIPHER_iv_length(cipher); 922 iv_len = EVP_CIPHER_iv_length(cipher);
915 923
916 /* sanity checks */ 924 /* sanity checks */
917 925
918 if (pkt->token.len < (size_t) iv_len + EVP_CIPHER_block_size(cipher)) { 926 if (pkt->token.len < (size_t) iv_len + EVP_CIPHER_block_size(cipher)) {
927 qc->error = NGX_QUIC_ERR_INVALID_TOKEN;
919 return NGX_ERROR; 928 return NGX_ERROR;
920 } 929 }
921 930
922 if (pkt->token.len > (size_t) iv_len + NGX_QUIC_MAX_TOKEN_SIZE) { 931 if (pkt->token.len > (size_t) iv_len + NGX_QUIC_MAX_TOKEN_SIZE) {
923 return NGX_ERROR; 932 return NGX_ERROR;
936 p = pkt->token.data + iv_len; 945 p = pkt->token.data + iv_len;
937 len = pkt->token.len - iv_len; 946 len = pkt->token.len - iv_len;
938 947
939 if (EVP_DecryptUpdate(ctx, tdec, &len, p, len) != 1) { 948 if (EVP_DecryptUpdate(ctx, tdec, &len, p, len) != 1) {
940 EVP_CIPHER_CTX_free(ctx); 949 EVP_CIPHER_CTX_free(ctx);
950 qc->error = NGX_QUIC_ERR_INVALID_TOKEN;
941 return NGX_ERROR; 951 return NGX_ERROR;
942 } 952 }
943 953
944 if (EVP_DecryptFinal_ex(ctx, tdec + len, &tlen) <= 0) { 954 if (EVP_DecryptFinal_ex(ctx, tdec + len, &tlen) <= 0) {
945 EVP_CIPHER_CTX_free(ctx); 955 EVP_CIPHER_CTX_free(ctx);
956 qc->error = NGX_QUIC_ERR_INVALID_TOKEN;
946 return NGX_ERROR; 957 return NGX_ERROR;
947 } 958 }
948 959
949 EVP_CIPHER_CTX_free(ctx); 960 EVP_CIPHER_CTX_free(ctx);
950 961
977 988
978 break; 989 break;
979 } 990 }
980 991
981 if (ngx_memcmp(tdec, data, len) != 0) { 992 if (ngx_memcmp(tdec, data, len) != 0) {
993 qc->error = NGX_QUIC_ERR_INVALID_TOKEN;
982 return NGX_ERROR; 994 return NGX_ERROR;
983 } 995 }
984 996
985 ngx_memcpy(&msec, tdec + len, sizeof(msec)); 997 ngx_memcpy(&msec, tdec + len, sizeof(msec));
986 998
1114 ngx_pool_t *pool; 1126 ngx_pool_t *pool;
1115 1127
1116 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, 1128 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
1117 "quic ngx_quic_close_connection, rc: %i", rc); 1129 "quic ngx_quic_close_connection, rc: %i", rc);
1118 1130
1119 if (!c->quic) { 1131 if (!c->quic || c->quic->state == NGX_QUIC_ST_UNAVAIL) {
1120 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, 1132 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
1121 "quic close connection early error"); 1133 "quic close connection early error");
1122 1134
1123 } else if (ngx_quic_close_quic(c, rc) == NGX_AGAIN) { 1135 } else if (ngx_quic_close_quic(c, rc) == NGX_AGAIN) {
1124 return; 1136 return;
1147 1159
1148 1160
1149 static ngx_int_t 1161 static ngx_int_t
1150 ngx_quic_close_quic(ngx_connection_t *c, ngx_int_t rc) 1162 ngx_quic_close_quic(ngx_connection_t *c, ngx_int_t rc)
1151 { 1163 {
1152 ngx_uint_t i; 1164 ngx_uint_t i, err;
1153 ngx_quic_connection_t *qc; 1165 ngx_quic_connection_t *qc;
1154 enum ssl_encryption_level_t level; 1166 enum ssl_encryption_level_t level;
1155 1167
1156 qc = c->quic; 1168 qc = c->quic;
1157 1169
1158 if (!qc->closing) { 1170 if (!qc->closing) {
1171
1172 switch (qc->state) {
1173 case NGX_QUIC_ST_INITIAL:
1174 level = ssl_encryption_initial;
1175 break;
1176
1177 case NGX_QUIC_ST_HANDSHAKE:
1178 level = ssl_encryption_handshake;
1179 break;
1180
1181 default: /* NGX_QUIC_ST_APPLICATION/EARLY_DATA */
1182 level = ssl_encryption_application;
1183 break;
1184 }
1159 1185
1160 if (rc == NGX_OK) { 1186 if (rc == NGX_OK) {
1161 1187
1162 /* 1188 /*
1163 * 10.3. Immediate Close 1189 * 10.3. Immediate Close
1166 * terminate the connection immediately. 1192 * terminate the connection immediately.
1167 */ 1193 */
1168 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, 1194 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
1169 "quic immediate close, drain = %d", qc->draining); 1195 "quic immediate close, drain = %d", qc->draining);
1170 1196
1171 switch (qc->state) {
1172 case NGX_QUIC_ST_INITIAL:
1173 level = ssl_encryption_initial;
1174 break;
1175
1176 case NGX_QUIC_ST_HANDSHAKE:
1177 level = ssl_encryption_handshake;
1178 break;
1179
1180 default: /* NGX_QUIC_ST_APPLICATION/EARLY_DATA */
1181 level = ssl_encryption_application;
1182 break;
1183 }
1184
1185 if (ngx_quic_send_cc(c, level, NGX_QUIC_ERR_NO_ERROR) == NGX_OK) { 1197 if (ngx_quic_send_cc(c, level, NGX_QUIC_ERR_NO_ERROR) == NGX_OK) {
1186 1198
1187 qc->close.log = c->log; 1199 qc->close.log = c->log;
1188 qc->close.data = c; 1200 qc->close.data = c;
1189 qc->close.handler = ngx_quic_close_timer_handler; 1201 qc->close.handler = ngx_quic_close_timer_handler;
1204 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, 1216 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
1205 "quic closing %s connection", 1217 "quic closing %s connection",
1206 qc->draining ? "drained" : "idle"); 1218 qc->draining ? "drained" : "idle");
1207 1219
1208 } else { 1220 } else {
1209 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, 1221 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
1210 "quic immediate close due to fatal error"); 1222 "quic immediate close due to fatal error: %ui",
1223 qc->error);
1224
1225 err = qc->error ? qc->error : NGX_QUIC_ERR_INTERNAL_ERROR;
1226 (void) ngx_quic_send_cc(c, level, err);
1211 } 1227 }
1212 1228
1213 qc->closing = 1; 1229 qc->closing = 1;
1214 } 1230 }
1215 1231
1747 1763
1748 c->log->action = "parsing frames"; 1764 c->log->action = "parsing frames";
1749 1765
1750 len = ngx_quic_parse_frame(pkt, p, end, &frame); 1766 len = ngx_quic_parse_frame(pkt, p, end, &frame);
1751 1767
1752 if (len == NGX_DECLINED) {
1753 /* TODO: handle protocol violation:
1754 * such frame not allowed in this packet
1755 */
1756 return NGX_ERROR;
1757 }
1758
1759 if (len < 0) { 1768 if (len < 0) {
1769 qc->error = pkt->error;
1760 return NGX_ERROR; 1770 return NGX_ERROR;
1761 } 1771 }
1762 1772
1763 c->log->action = "handling frames"; 1773 c->log->action = "handling frames";
1764 1774
1885 } 1895 }
1886 1896
1887 if (p != end) { 1897 if (p != end) {
1888 ngx_log_error(NGX_LOG_INFO, c->log, 0, 1898 ngx_log_error(NGX_LOG_INFO, c->log, 0,
1889 "quic trailing garbage in payload: %ui bytes", end - p); 1899 "quic trailing garbage in payload: %ui bytes", end - p);
1900
1901 qc->error = NGX_QUIC_ERR_FRAME_ENCODING_ERROR;
1890 return NGX_ERROR; 1902 return NGX_ERROR;
1891 } 1903 }
1892 1904
1893 if (do_close) { 1905 if (do_close) {
1894 qc->draining = 1; 1906 qc->draining = 1;
2010 2022
2011 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, 2023 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
2012 "quic ngx_quic_handle_ack_frame level %d", pkt->level); 2024 "quic ngx_quic_handle_ack_frame level %d", pkt->level);
2013 2025
2014 /* 2026 /*
2015 * TODO: If any computed packet number is negative, an endpoint MUST 2027 * If any computed packet number is negative, an endpoint MUST
2016 * generate a connection error of type FRAME_ENCODING_ERROR. 2028 * generate a connection error of type FRAME_ENCODING_ERROR.
2017 * (19.3.1) 2029 * (19.3.1)
2018 */ 2030 */
2019 2031
2020 if (ack->first_range > ack->largest) { 2032 if (ack->first_range > ack->largest) {
2033 c->quic->error = NGX_QUIC_ERR_FRAME_ENCODING_ERROR;
2021 ngx_log_error(NGX_LOG_INFO, c->log, 0, 2034 ngx_log_error(NGX_LOG_INFO, c->log, 0,
2022 "quic invalid first range in ack frame"); 2035 "quic invalid first range in ack frame");
2023 return NGX_ERROR; 2036 return NGX_ERROR;
2024 } 2037 }
2025 2038
2047 return NGX_ERROR; 2060 return NGX_ERROR;
2048 } 2061 }
2049 pos += n; 2062 pos += n;
2050 2063
2051 if (gap >= min) { 2064 if (gap >= min) {
2065 c->quic->error = NGX_QUIC_ERR_FRAME_ENCODING_ERROR;
2052 ngx_log_error(NGX_LOG_INFO, c->log, 0, 2066 ngx_log_error(NGX_LOG_INFO, c->log, 0,
2053 "quic invalid range %ui in ack frame", i); 2067 "quic invalid range %ui in ack frame", i);
2054 return NGX_ERROR; 2068 return NGX_ERROR;
2055 } 2069 }
2056 2070
2057 max = min - 1 - gap; 2071 max = min - 1 - gap;
2058 2072
2059 if (range > max + 1) { 2073 if (range > max + 1) {
2074 c->quic->error = NGX_QUIC_ERR_FRAME_ENCODING_ERROR;
2060 ngx_log_error(NGX_LOG_INFO, c->log, 0, 2075 ngx_log_error(NGX_LOG_INFO, c->log, 0,
2061 "quic invalid range %ui in ack frame", i); 2076 "quic invalid range %ui in ack frame", i);
2062 return NGX_ERROR; 2077 return NGX_ERROR;
2063 } 2078 }
2064 2079
2114 return NGX_OK; 2129 return NGX_OK;
2115 } 2130 }
2116 2131
2117 ngx_log_error(NGX_LOG_INFO, c->log, 0, 2132 ngx_log_error(NGX_LOG_INFO, c->log, 0,
2118 "quic ACK for the packet not in sent queue "); 2133 "quic ACK for the packet not in sent queue ");
2119 /* TODO: handle error properly: PROTOCOL VIOLATION */ 2134
2135 qc->error = NGX_QUIC_ERR_PROTOCOL_VIOLATION;
2136
2120 return NGX_ERROR; 2137 return NGX_ERROR;
2121 } 2138 }
2122 2139
2123 if (!qc->push.timer_set) { 2140 if (!qc->push.timer_set) {
2124 ngx_post_event(&qc->push, &ngx_posted_events); 2141 ngx_post_event(&qc->push, &ngx_posted_events);
2976 ssize_t len; 2993 ssize_t len;
2977 u_char *p; 2994 u_char *p;
2978 ngx_msec_t now; 2995 ngx_msec_t now;
2979 ngx_str_t out, res; 2996 ngx_str_t out, res;
2980 ngx_queue_t *q; 2997 ngx_queue_t *q;
2998 ngx_ssl_conn_t *ssl_conn;
2981 ngx_quic_frame_t *f, *start; 2999 ngx_quic_frame_t *f, *start;
2982 ngx_quic_header_t pkt; 3000 ngx_quic_header_t pkt;
2983 ngx_quic_secrets_t *keys; 3001 ngx_quic_secrets_t *keys;
2984 ngx_quic_send_ctx_t *ctx; 3002 ngx_quic_send_ctx_t *ctx;
2985 ngx_quic_connection_t *qc; 3003 ngx_quic_connection_t *qc;
2988 static u_char dst[NGX_QUIC_DEFAULT_MAX_PACKET_SIZE]; 3006 static u_char dst[NGX_QUIC_DEFAULT_MAX_PACKET_SIZE];
2989 3007
2990 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, 3008 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
2991 "quic ngx_quic_send_frames"); 3009 "quic ngx_quic_send_frames");
2992 3010
3011 ssl_conn = c->ssl ? c->ssl->connection : NULL;
3012
2993 q = ngx_queue_head(frames); 3013 q = ngx_queue_head(frames);
2994 start = ngx_queue_data(q, ngx_quic_frame_t, queue); 3014 start = ngx_queue_data(q, ngx_quic_frame_t, queue);
2995 3015
2996 ctx = ngx_quic_get_send_ctx(c->quic, start->level); 3016 ctx = ngx_quic_get_send_ctx(c->quic, start->level);
2997 3017
3075 "quic packet ready: %ui bytes at level %d" 3095 "quic packet ready: %ui bytes at level %d"
3076 " need_ack: %d number: %L encoded %d:0x%xD", 3096 " need_ack: %d number: %L encoded %d:0x%xD",
3077 out.len, start->level, pkt.need_ack, pkt.number, 3097 out.len, start->level, pkt.need_ack, pkt.number,
3078 pkt.num_len, pkt.trunc); 3098 pkt.num_len, pkt.trunc);
3079 3099
3080 if (ngx_quic_encrypt(&pkt, c->ssl->connection, &res) != NGX_OK) { 3100 if (ngx_quic_encrypt(&pkt, ssl_conn, &res) != NGX_OK) {
3081 return NGX_ERROR; 3101 return NGX_ERROR;
3082 } 3102 }
3083 3103
3084 #ifdef NGX_QUIC_DEBUG_PACKETS 3104 #ifdef NGX_QUIC_DEBUG_PACKETS
3085 ngx_quic_hexdump(c->log, "quic packet to send", res.data, res.len); 3105 ngx_quic_hexdump(c->log, "quic packet to send", res.data, res.len);