diff src/http/v3/ngx_http_v3_streams.c @ 8881:72b304f6207c quic

HTTP/3: traffic-based flood detection. With this patch, all traffic over HTTP/3 bidi and uni streams is counted in the h3c->total_bytes field, and payload traffic is counted in the h3c->payload_bytes field. As long as total traffic is many times larger than payload traffic, we consider this to be a flood. Request header traffic is counted as if all fields are literal. Response header traffic is counted as is.
author Roman Arutyunyan <arut@nginx.com>
date Thu, 07 Oct 2021 13:22:42 +0300
parents 531075860fe2
children 925572184d4a
line wrap: on
line diff
--- a/src/http/v3/ngx_http_v3_streams.c
+++ b/src/http/v3/ngx_http_v3_streams.c
@@ -171,6 +171,7 @@ ngx_http_v3_uni_read_handler(ngx_event_t
     ngx_buf_t                  b;
     ngx_int_t                  rc;
     ngx_connection_t          *c;
+    ngx_http_v3_session_t     *h3c;
     ngx_http_v3_uni_stream_t  *us;
 
     c = rev->data;
@@ -207,6 +208,14 @@ ngx_http_v3_uni_read_handler(ngx_event_t
         b.pos = buf;
         b.last = buf + n;
 
+        h3c = ngx_http_v3_get_session(c);
+        h3c->total_bytes += n;
+
+        if (ngx_http_v3_check_flood(c) != NGX_OK) {
+            ngx_http_v3_close_uni_stream(c);
+            return;
+        }
+
         rc = ngx_http_v3_parse_uni(c, &us->parse, &b);
 
         if (rc == NGX_DONE) {
@@ -282,6 +291,9 @@ ngx_http_v3_create_push_stream(ngx_conne
     p = (u_char *) ngx_http_v3_encode_varlen_int(p, push_id);
     n = p - buf;
 
+    h3c = ngx_http_v3_get_session(c);
+    h3c->total_bytes += n;
+
     if (sc->send(sc, buf, n) != (ssize_t) n) {
         goto failed;
     }
@@ -291,7 +303,6 @@ ngx_http_v3_create_push_stream(ngx_conne
         goto failed;
     }
 
-    h3c = ngx_http_v3_get_session(c);
     h3c->npushing++;
 
     cln->handler = ngx_http_v3_push_cleanup;
@@ -383,6 +394,9 @@ ngx_http_v3_get_uni_stream(ngx_connectio
 
     n = (u_char *) ngx_http_v3_encode_varlen_int(buf, type) - buf;
 
+    h3c = ngx_http_v3_get_session(c);
+    h3c->total_bytes += n;
+
     if (sc->send(sc, buf, n) != (ssize_t) n) {
         goto failed;
     }
@@ -403,6 +417,7 @@ ngx_http_v3_send_settings(ngx_connection
     u_char                  *p, buf[NGX_HTTP_V3_VARLEN_INT_LEN * 6];
     size_t                   n;
     ngx_connection_t        *cc;
+    ngx_http_v3_session_t   *h3c;
     ngx_http_v3_srv_conf_t  *h3scf;
 
     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 send settings");
@@ -431,6 +446,9 @@ ngx_http_v3_send_settings(ngx_connection
     p = (u_char *) ngx_http_v3_encode_varlen_int(p, h3scf->max_blocked_streams);
     n = p - buf;
 
+    h3c = ngx_http_v3_get_session(c);
+    h3c->total_bytes += n;
+
     if (cc->send(cc, buf, n) != (ssize_t) n) {
         goto failed;
     }
@@ -448,9 +466,10 @@ failed:
 ngx_int_t
 ngx_http_v3_send_goaway(ngx_connection_t *c, uint64_t id)
 {
-    u_char            *p, buf[NGX_HTTP_V3_VARLEN_INT_LEN * 3];
-    size_t             n;
-    ngx_connection_t  *cc;
+    u_char                 *p, buf[NGX_HTTP_V3_VARLEN_INT_LEN * 3];
+    size_t                  n;
+    ngx_connection_t       *cc;
+    ngx_http_v3_session_t  *h3c;
 
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 send goaway %uL", id);
 
@@ -465,6 +484,9 @@ ngx_http_v3_send_goaway(ngx_connection_t
     p = (u_char *) ngx_http_v3_encode_varlen_int(p, id);
     n = p - buf;
 
+    h3c = ngx_http_v3_get_session(c);
+    h3c->total_bytes += n;
+
     if (cc->send(cc, buf, n) != (ssize_t) n) {
         goto failed;
     }
@@ -482,9 +504,10 @@ failed:
 ngx_int_t
 ngx_http_v3_send_ack_section(ngx_connection_t *c, ngx_uint_t stream_id)
 {
-    u_char             buf[NGX_HTTP_V3_PREFIX_INT_LEN];
-    size_t             n;
-    ngx_connection_t  *dc;
+    u_char                  buf[NGX_HTTP_V3_PREFIX_INT_LEN];
+    size_t                  n;
+    ngx_connection_t       *dc;
+    ngx_http_v3_session_t  *h3c;
 
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
                    "http3 client ack section %ui", stream_id);
@@ -497,6 +520,9 @@ ngx_http_v3_send_ack_section(ngx_connect
     buf[0] = 0x80;
     n = (u_char *) ngx_http_v3_encode_prefix_int(buf, stream_id, 7) - buf;
 
+    h3c = ngx_http_v3_get_session(c);
+    h3c->total_bytes += n;
+
     if (dc->send(dc, buf, n) != (ssize_t) n) {
         ngx_http_v3_close_uni_stream(dc);
         return NGX_ERROR;
@@ -509,9 +535,10 @@ ngx_http_v3_send_ack_section(ngx_connect
 ngx_int_t
 ngx_http_v3_send_cancel_stream(ngx_connection_t *c, ngx_uint_t stream_id)
 {
-    u_char             buf[NGX_HTTP_V3_PREFIX_INT_LEN];
-    size_t             n;
-    ngx_connection_t  *dc;
+    u_char                  buf[NGX_HTTP_V3_PREFIX_INT_LEN];
+    size_t                  n;
+    ngx_connection_t       *dc;
+    ngx_http_v3_session_t  *h3c;
 
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
                    "http3 client cancel stream %ui", stream_id);
@@ -524,6 +551,9 @@ ngx_http_v3_send_cancel_stream(ngx_conne
     buf[0] = 0x40;
     n = (u_char *) ngx_http_v3_encode_prefix_int(buf, stream_id, 6) - buf;
 
+    h3c = ngx_http_v3_get_session(c);
+    h3c->total_bytes += n;
+
     if (dc->send(dc, buf, n) != (ssize_t) n) {
         ngx_http_v3_close_uni_stream(dc);
         return NGX_ERROR;
@@ -536,9 +566,10 @@ ngx_http_v3_send_cancel_stream(ngx_conne
 ngx_int_t
 ngx_http_v3_send_inc_insert_count(ngx_connection_t *c, ngx_uint_t inc)
 {
-    u_char             buf[NGX_HTTP_V3_PREFIX_INT_LEN];
-    size_t             n;
-    ngx_connection_t  *dc;
+    u_char                  buf[NGX_HTTP_V3_PREFIX_INT_LEN];
+    size_t                  n;
+    ngx_connection_t       *dc;
+    ngx_http_v3_session_t  *h3c;
 
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
                    "http3 client increment insert count %ui", inc);
@@ -551,6 +582,9 @@ ngx_http_v3_send_inc_insert_count(ngx_co
     buf[0] = 0;
     n = (u_char *) ngx_http_v3_encode_prefix_int(buf, inc, 6) - buf;
 
+    h3c = ngx_http_v3_get_session(c);
+    h3c->total_bytes += n;
+
     if (dc->send(dc, buf, n) != (ssize_t) n) {
         ngx_http_v3_close_uni_stream(dc);
         return NGX_ERROR;