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.
diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c index e3c7bbc..640dd4a 100644 --- a/src/http/v2/ngx_http_v2.c +++ b/src/http/v2/ngx_http_v2.c
@@ -419,6 +419,14 @@ } while (p != end); + h2c->total_bytes += n; + + if (h2c->total_bytes / 8 > h2c->payload_bytes + 1048576) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, "http2 flood detected"); + ngx_http_v2_finalize_connection(h2c, NGX_HTTP_V2_NO_ERROR); + return; + } + } while (rev->ready); if (ngx_handle_read_event(rev, 0) != NGX_OK) { @@ -963,6 +971,8 @@ stream->in_closed = h2c->state.flags & NGX_HTTP_V2_END_STREAM_FLAG; } + h2c->payload_bytes += size; + if (r->request_body) { rc = ngx_http_v2_process_request_body(r, pos, size, stream->in_closed); @@ -2909,9 +2919,9 @@ "requested control frame is too large: %uz", length); return NULL; } +#endif frame->length = length; -#endif buf->last = ngx_http_v2_write_len_and_type(buf->pos, length, type); @@ -2938,6 +2948,8 @@ frame->next = h2c->free_frames; h2c->free_frames = frame; + h2c->total_bytes += NGX_HTTP_V2_FRAME_HEADER_SIZE + frame->length; + return NGX_OK; } @@ -3723,7 +3735,8 @@ static void ngx_http_v2_run_request(ngx_http_request_t *r) { - ngx_connection_t *fc; + ngx_connection_t *fc; + ngx_http_v2_connection_t *h2c; fc = r->connection; @@ -3755,6 +3768,10 @@ r->headers_in.chunked = 1; } + h2c = r->stream->connection; + + h2c->payload_bytes += r->request_length; + ngx_http_process_request(r); failed:
diff --git a/src/http/v2/ngx_http_v2.h b/src/http/v2/ngx_http_v2.h index 69d55d1..59ddf54 100644 --- a/src/http/v2/ngx_http_v2.h +++ b/src/http/v2/ngx_http_v2.h
@@ -119,6 +119,9 @@ ngx_connection_t *connection; ngx_http_connection_t *http_connection; + off_t total_bytes; + off_t payload_bytes; + ngx_uint_t processing; ngx_uint_t frames; ngx_uint_t idle;
diff --git a/src/http/v2/ngx_http_v2_filter_module.c b/src/http/v2/ngx_http_v2_filter_module.c index 96f5542..a6e5e7d 100644 --- a/src/http/v2/ngx_http_v2_filter_module.c +++ b/src/http/v2/ngx_http_v2_filter_module.c
@@ -1877,6 +1877,8 @@ stream->request->header_size += NGX_HTTP_V2_FRAME_HEADER_SIZE + frame->length; + h2c->payload_bytes += frame->length; + ngx_http_v2_handle_frame(stream, frame); ngx_http_v2_handle_stream(h2c, stream); @@ -1931,6 +1933,8 @@ stream->request->header_size += NGX_HTTP_V2_FRAME_HEADER_SIZE + frame->length; + h2c->payload_bytes += frame->length; + ngx_http_v2_handle_frame(stream, frame); ngx_http_v2_handle_stream(h2c, stream); @@ -2024,6 +2028,8 @@ stream->request->header_size += NGX_HTTP_V2_FRAME_HEADER_SIZE; + h2c->payload_bytes += frame->length; + ngx_http_v2_handle_frame(stream, frame); ngx_http_v2_handle_stream(h2c, stream); @@ -2036,12 +2042,17 @@ ngx_http_v2_handle_frame(ngx_http_v2_stream_t *stream, ngx_http_v2_out_frame_t *frame) { - ngx_http_request_t *r; + ngx_http_request_t *r; + ngx_http_v2_connection_t *h2c; r = stream->request; r->connection->sent += NGX_HTTP_V2_FRAME_HEADER_SIZE + frame->length; + h2c = stream->connection; + + h2c->total_bytes += NGX_HTTP_V2_FRAME_HEADER_SIZE + frame->length; + if (frame->fin) { stream->out_closed = 1; }