comparison src/http/v2/ngx_http_v2.c @ 7569:80359395b345

HTTP/2: traffic-based flood detection. With this patch, all traffic over an HTTP/2 connection is counted in the h2c->total_bytes field, and payload traffic is counted in the h2c->payload_bytes field. As long as total traffic is many times larger than payload traffic, we consider this to be a flood.
author Maxim Dounin <mdounin@mdounin.ru>
date Wed, 18 Sep 2019 20:28:12 +0300
parents 2e61e4b6bcd9
children d6cf51af8a3d
comparison
equal deleted inserted replaced
7568:2e61e4b6bcd9 7569:80359395b345
417 return; 417 return;
418 } 418 }
419 419
420 } while (p != end); 420 } while (p != end);
421 421
422 h2c->total_bytes += n;
423
424 if (h2c->total_bytes / 8 > h2c->payload_bytes + 1048576) {
425 ngx_log_error(NGX_LOG_INFO, c->log, 0, "http2 flood detected");
426 ngx_http_v2_finalize_connection(h2c, NGX_HTTP_V2_NO_ERROR);
427 return;
428 }
429
422 } while (rev->ready); 430 } while (rev->ready);
423 431
424 if (ngx_handle_read_event(rev, 0) != NGX_OK) { 432 if (ngx_handle_read_event(rev, 0) != NGX_OK) {
425 ngx_http_v2_finalize_connection(h2c, NGX_HTTP_V2_INTERNAL_ERROR); 433 ngx_http_v2_finalize_connection(h2c, NGX_HTTP_V2_INTERNAL_ERROR);
426 return; 434 return;
960 968
961 if (size >= h2c->state.length) { 969 if (size >= h2c->state.length) {
962 size = h2c->state.length; 970 size = h2c->state.length;
963 stream->in_closed = h2c->state.flags & NGX_HTTP_V2_END_STREAM_FLAG; 971 stream->in_closed = h2c->state.flags & NGX_HTTP_V2_END_STREAM_FLAG;
964 } 972 }
973
974 h2c->payload_bytes += size;
965 975
966 if (r->request_body) { 976 if (r->request_body) {
967 rc = ngx_http_v2_process_request_body(r, pos, size, stream->in_closed); 977 rc = ngx_http_v2_process_request_body(r, pos, size, stream->in_closed);
968 978
969 if (rc != NGX_OK) { 979 if (rc != NGX_OK) {
2907 { 2917 {
2908 ngx_log_error(NGX_LOG_ALERT, h2c->connection->log, 0, 2918 ngx_log_error(NGX_LOG_ALERT, h2c->connection->log, 0,
2909 "requested control frame is too large: %uz", length); 2919 "requested control frame is too large: %uz", length);
2910 return NULL; 2920 return NULL;
2911 } 2921 }
2922 #endif
2912 2923
2913 frame->length = length; 2924 frame->length = length;
2914 #endif
2915 2925
2916 buf->last = ngx_http_v2_write_len_and_type(buf->pos, length, type); 2926 buf->last = ngx_http_v2_write_len_and_type(buf->pos, length, type);
2917 2927
2918 *buf->last++ = flags; 2928 *buf->last++ = flags;
2919 2929
2935 return NGX_AGAIN; 2945 return NGX_AGAIN;
2936 } 2946 }
2937 2947
2938 frame->next = h2c->free_frames; 2948 frame->next = h2c->free_frames;
2939 h2c->free_frames = frame; 2949 h2c->free_frames = frame;
2950
2951 h2c->total_bytes += NGX_HTTP_V2_FRAME_HEADER_SIZE + frame->length;
2940 2952
2941 return NGX_OK; 2953 return NGX_OK;
2942 } 2954 }
2943 2955
2944 2956
3721 3733
3722 3734
3723 static void 3735 static void
3724 ngx_http_v2_run_request(ngx_http_request_t *r) 3736 ngx_http_v2_run_request(ngx_http_request_t *r)
3725 { 3737 {
3726 ngx_connection_t *fc; 3738 ngx_connection_t *fc;
3739 ngx_http_v2_connection_t *h2c;
3727 3740
3728 fc = r->connection; 3741 fc = r->connection;
3729 3742
3730 if (ngx_http_v2_construct_request_line(r) != NGX_OK) { 3743 if (ngx_http_v2_construct_request_line(r) != NGX_OK) {
3731 goto failed; 3744 goto failed;
3752 } 3765 }
3753 3766
3754 if (r->headers_in.content_length_n == -1 && !r->stream->in_closed) { 3767 if (r->headers_in.content_length_n == -1 && !r->stream->in_closed) {
3755 r->headers_in.chunked = 1; 3768 r->headers_in.chunked = 1;
3756 } 3769 }
3770
3771 h2c = r->stream->connection;
3772
3773 h2c->payload_bytes += r->request_length;
3757 3774
3758 ngx_http_process_request(r); 3775 ngx_http_process_request(r);
3759 3776
3760 failed: 3777 failed:
3761 3778