diff --git a/etc/tempesta_fw.conf b/etc/tempesta_fw.conf index a9373c3e2..b1e803fef 100644 --- a/etc/tempesta_fw.conf +++ b/etc/tempesta_fw.conf @@ -1150,6 +1150,19 @@ # http_max_header_list_size 4096; # +# TAG: max_queued_control_frames +# +# Maximum number of control frames allowed to wait in +# the client connection's write queue, to mitigate control frames flooding +# (PING/SETTINGS/RESET_STREAM). +# +# Syntax: +# max_queued_control_frames SIZE +# +# Example: +# max_queued_control_frames 10000; +# + # # Health monitoring configuration. # diff --git a/fw/http.c b/fw/http.c index 7211fc49c..be0035b92 100644 --- a/fw/http.c +++ b/fw/http.c @@ -7530,6 +7530,34 @@ tfw_cfgop_cleanup_max_header_list_size(TfwCfgSpec *cs) max_header_list_size = 0; } +static int +tfw_cfgop_max_queued_control_frames(TfwCfgSpec *cs, TfwCfgEntry *ce) +{ + int r; + + if (tfw_cfg_check_val_n(ce, 1)) + return -EINVAL; + if (ce->attr_n) { + T_ERR_NL("Unexpected attributes\n"); + return -EINVAL; + } + + r = tfw_cfg_parse_uint(ce->vals[0], &max_queued_control_frames); + if (unlikely(r)) { + T_ERR_NL("Unable to parse 'max_queued_control_frames' value: '%s'\n", + ce->vals[0]); + return -EINVAL; + } + + return 0; +} + +static void +tfw_cfgop_cleanup_max_queued_control_frames(TfwCfgSpec *cs) +{ + max_queued_control_frames = 0; +} + static TfwCfgSpec tfw_http_specs[] = { { .name = "block_action", @@ -7617,6 +7645,13 @@ static TfwCfgSpec tfw_http_specs[] = { .allow_none = true, .cleanup = tfw_cfgop_cleanup_max_header_list_size, }, + { + .name = "max_queued_control_frames", + .deflt = "10000", + .handler = tfw_cfgop_max_queued_control_frames, + .allow_none = true, + .cleanup = tfw_cfgop_cleanup_max_queued_control_frames, + }, { 0 } }; diff --git a/fw/http_frame.c b/fw/http_frame.c index 6fe0712c8..d4977b4d0 100644 --- a/fw/http_frame.c +++ b/fw/http_frame.c @@ -30,6 +30,8 @@ #include "http_msg.h" #include "tcp.h" +unsigned int max_queued_control_frames = 0; + #define FRAME_PREFACE_CLI_MAGIC "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n" #define FRAME_PREFACE_CLI_MAGIC_LEN 24 #define FRAME_WND_UPDATE_SIZE 4 @@ -282,11 +284,14 @@ __tfw_h2_send_frame(TfwH2Ctx *ctx, TfwFrameHdr *hdr, TfwStr *data, TfwH2Conn *conn = container_of(ctx, TfwH2Conn, h2); bool is_control_frame = !hdr->stream_id || hdr->type == HTTP2_RST_STREAM; - // If the peer is causing us to generate a lot of control frames, - // but not reading them from us, assume they are trying to make us - // run out of memory. + /* + * If the peer is causing us to generate a lot of control frames, + * but not reading them from us, assume they are trying to make us + * run out of memory. + */ if (is_control_frame && - atomic_read(&ctx->queued_control_frames) > MAX_QUEUED_CONTROL_FRAMES) { + atomic_read(&ctx->queued_control_frames) + > max_queued_control_frames) { T_ERR("Too many control frames in send queue, closing connection"); r = SS_BLOCK_WITH_RST; goto err; diff --git a/fw/http_frame.h b/fw/http_frame.h index c85c88281..413098621 100644 --- a/fw/http_frame.h +++ b/fw/http_frame.h @@ -24,6 +24,8 @@ #include "http_stream.h" #include "hpack.h" +extern unsigned int max_queued_control_frames; + /* RFC 7540 Section 4.1 frame header constants. */ #define FRAME_HEADER_SIZE 9 #define FRAME_STREAM_ID_MASK ((1U << 31) - 1)