]> git.kaiwu.me - nginx.git/commitdiff
HTTP/2: traffic-based flood detection.
authorMaxim Dounin <mdounin@mdounin.ru>
Wed, 18 Sep 2019 17:28:12 +0000 (20:28 +0300)
committerMaxim Dounin <mdounin@mdounin.ru>
Wed, 18 Sep 2019 17:28:12 +0000 (20:28 +0300)
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.

src/http/v2/ngx_http_v2.c
src/http/v2/ngx_http_v2.h
src/http/v2/ngx_http_v2_filter_module.c

index e3c7bbc34296000674e0e623479cf22121f6445b..640dd4ae73c0fab737d2740aedfeb43e886e2c35 100644 (file)
@@ -419,6 +419,14 @@ ngx_http_v2_read_handler(ngx_event_t *rev)
 
         } 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 @@ ngx_http_v2_state_read_data(ngx_http_v2_connection_t *h2c, u_char *pos,
         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 @@ ngx_http_v2_get_frame(ngx_http_v2_connection_t *h2c, size_t length,
                       "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 @@ ngx_http_v2_frame_handler(ngx_http_v2_connection_t *h2c,
     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 @@ ngx_http_v2_construct_cookie_header(ngx_http_request_t *r)
 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 @@ ngx_http_v2_run_request(ngx_http_request_t *r)
         r->headers_in.chunked = 1;
     }
 
+    h2c = r->stream->connection;
+
+    h2c->payload_bytes += r->request_length;
+
     ngx_http_process_request(r);
 
 failed:
index 69d55d1cb4a713540bb082c6faf91b9a22903030..59ddf54e2cb7c1cb345e9b5731ee9d526d01e9d0 100644 (file)
@@ -119,6 +119,9 @@ struct ngx_http_v2_connection_s {
     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;
index 96f55426d9d81b1e585253a03b59fc154a89baf7..a6e5e7d4f7b1531eca0b71f4f5299c0ad6e6da46 100644 (file)
@@ -1877,6 +1877,8 @@ ngx_http_v2_headers_frame_handler(ngx_http_v2_connection_t *h2c,
     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 @@ ngx_http_v2_push_frame_handler(ngx_http_v2_connection_t *h2c,
     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 @@ done:
 
     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 @@ static ngx_inline void
 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;
     }