Mercurial > hg > nginx-quic
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); |