Skip to content
This repository was archived by the owner on Feb 16, 2026. It is now read-only.

Commit 656d020

Browse files
committed
h2: Add a rate limit facility for h/2 RST handling
This adds parameters h2_rst_allowance and h2_rst_allowance_period, which govern the rate of which we allow clients to reset h/2 streams. If the limit is exceeded the connection is closed. Mitigates: #3996
1 parent 7332bdf commit 656d020

5 files changed

Lines changed: 94 additions & 0 deletions

File tree

bin/varnishd/http2/cache_http2.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,8 @@ struct h2_sess {
193193
VTAILQ_HEAD(,h2_req) txqueue;
194194

195195
h2_error error;
196+
double rst_budget;
197+
vtim_real last_rst;
196198
};
197199

198200
#define ASSERT_RXTHR(h2) do {assert(h2->rxthr == pthread_self());} while(0)

bin/varnishd/http2/cache_http2_proto.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,8 @@ h2_rx_push_promise(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2)
323323
static h2_error v_matchproto_(h2_rxframe_f)
324324
h2_rx_rst_stream(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2)
325325
{
326+
vtim_real now;
327+
vtim_dur d;
326328

327329
CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
328330
ASSERT_RXTHR(h2);
@@ -333,6 +335,26 @@ h2_rx_rst_stream(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2)
333335
if (r2 == NULL)
334336
return (0);
335337
h2_kill_req(wrk, h2, r2, h2_streamerror(vbe32dec(h2->rxf_data)));
338+
339+
if (cache_param->h2_rapid_reset_limit == 0)
340+
return (0);
341+
342+
now = VTIM_real();
343+
d = now - h2->last_rst;
344+
h2->rst_budget += cache_param->h2_rapid_reset_limit * d /
345+
cache_param->h2_rapid_reset_period;
346+
h2->rst_budget = vmin_t(double, h2->rst_budget,
347+
cache_param->h2_rapid_reset_limit);
348+
h2->last_rst = now;
349+
350+
if (h2->rst_budget < 1.0) {
351+
Lck_Lock(&h2->sess->mtx);
352+
VSLb(h2->vsl, SLT_Error, "H2: Hit RST limit. Closing session.");
353+
Lck_Unlock(&h2->sess->mtx);
354+
return (H2CE_ENHANCE_YOUR_CALM);
355+
}
356+
h2->rst_budget -= 1.0;
357+
336358
return (0);
337359
}
338360

bin/varnishd/http2/cache_http2_session.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,9 @@ h2_init_sess(struct sess *sp,
127127
h2_local_settings(&h2->local_settings);
128128
h2->remote_settings = H2_proto_settings;
129129
h2->decode = decode;
130+
h2->rst_budget = cache_param->h2_rapid_reset_limit;
131+
h2->last_rst = sp->t_open;
132+
AZ(isnan(h2->last_rst));
130133

131134
AZ(VHT_Init(h2->dectbl, h2->local_settings.header_table_size));
132135

bin/varnishtest/tests/r03996.vtc

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
varnishtest "h2 rapid reset"
2+
3+
server s1 {
4+
rxreq
5+
txresp
6+
} -start
7+
8+
varnish v1 -cliok "param.set feature +http2"
9+
varnish v1 -cliok "param.set debug +syncvsl"
10+
varnish v1 -cliok "param.set h2_rapid_reset_limit 3"
11+
12+
varnish v1 -vcl+backend {} -start
13+
14+
client c1 {
15+
stream 0 {
16+
rxgoaway
17+
expect goaway.err == ENHANCE_YOUR_CALM
18+
} -start
19+
20+
stream 1 {
21+
txreq
22+
txrst
23+
} -run
24+
stream 3 {
25+
txreq
26+
txrst
27+
} -run
28+
stream 5 {
29+
txreq
30+
txrst
31+
} -run
32+
stream 7 {
33+
txreq
34+
txrst
35+
} -run
36+
37+
stream 0 -wait
38+
} -run
39+

include/tbl/params.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1257,6 +1257,34 @@ PARAM_SIMPLE(
12571257
"HTTP2 maximum size of an uncompressed header list."
12581258
)
12591259

1260+
PARAM_SIMPLE(
1261+
/* name */ h2_rapid_reset_limit,
1262+
/* typ */ uint,
1263+
/* min */ "0",
1264+
/* max */ NULL,
1265+
/* def */ "0",
1266+
/* units */ NULL,
1267+
/* descr */
1268+
"HTTP2 RST Allowance.\n"
1269+
"Specifies the maximum number of allowed stream resets issued by\n"
1270+
"a client over a time period before the connection is closed.\n"
1271+
"Setting this parameter to 0 disables the limit.",
1272+
/* flags */ EXPERIMENTAL,
1273+
)
1274+
1275+
PARAM_SIMPLE(
1276+
/* name */ h2_rapid_reset_period,
1277+
/* typ */ timeout,
1278+
/* min */ "1.000",
1279+
/* max */ NULL,
1280+
/* def */ "60.000",
1281+
/* units */ "seconds",
1282+
/* descr */
1283+
"HTTP2 sliding window duration for h2_rapid_reset_limit.",
1284+
/* flags */ EXPERIMENTAL|WIZARD,
1285+
)
1286+
1287+
12601288
/*--------------------------------------------------------------------
12611289
* Memory pool parameters
12621290
*/

0 commit comments

Comments
 (0)