diff options
| author | Sergey Kandaurov <pluknet@nginx.com> | 2026-02-25 21:09:21 +0400 |
|---|---|---|
| committer | Sergey Kandaurov <s.kandaurov@f5.com> | 2026-02-26 18:05:07 +0400 |
| commit | e6ffe8384ebf1972faac9b031b9ff6182e79cfd6 (patch) | |
| tree | 9027106b8f2b8d73bc173bfe6ab771eede38a663 /src/event | |
| parent | 2f039e6f7233c7fd9ab88888642410352572c089 (diff) | |
| download | nginx-e6ffe8384ebf1972faac9b031b9ff6182e79cfd6.tar.gz nginx-e6ffe8384ebf1972faac9b031b9ff6182e79cfd6.tar.bz2 | |
QUIC: Stateless Reset rate limiting.
It uses a bloom filter to limit sending Stateless Reset packets no more
than once per second in average for the given address. This allows to
address resource asymmetry from precomputed packets, as well as to limit
potential Stateless Reset exchange.
Diffstat (limited to 'src/event')
| -rw-r--r-- | src/event/quic/ngx_event_quic_output.c | 65 |
1 files changed, 61 insertions, 4 deletions
diff --git a/src/event/quic/ngx_event_quic_output.c b/src/event/quic/ngx_event_quic_output.c index 72119a8ea..f98c834a1 100644 --- a/src/event/quic/ngx_event_quic_output.c +++ b/src/event/quic/ngx_event_quic_output.c @@ -64,6 +64,7 @@ static ssize_t ngx_quic_send(ngx_connection_t *c, u_char *buf, size_t len, struct sockaddr *sockaddr, socklen_t socklen); static void ngx_quic_set_packet_number(ngx_quic_header_t *pkt, ngx_quic_send_ctx_t *ctx); +static ngx_int_t ngx_quic_stateless_reset_filter(ngx_connection_t *c); ngx_int_t @@ -823,10 +824,11 @@ ngx_int_t ngx_quic_send_stateless_reset(ngx_connection_t *c, ngx_quic_conf_t *conf, ngx_quic_header_t *pkt) { - u_char *token; - size_t len, max; - uint16_t rndbytes; - u_char buf[NGX_QUIC_MAX_SR_PACKET]; + u_char *token; + size_t len, max; + uint16_t rndbytes; + ngx_int_t rc; + u_char buf[NGX_QUIC_MAX_SR_PACKET]; ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic handle stateless reset output"); @@ -835,6 +837,11 @@ ngx_quic_send_stateless_reset(ngx_connection_t *c, ngx_quic_conf_t *conf, return NGX_DECLINED; } + rc = ngx_quic_stateless_reset_filter(c); + if (rc != NGX_OK) { + return rc; + } + if (pkt->len <= NGX_QUIC_MIN_SR_PACKET) { len = pkt->len - 1; @@ -870,6 +877,56 @@ ngx_quic_send_stateless_reset(ngx_connection_t *c, ngx_quic_conf_t *conf, } +static ngx_int_t +ngx_quic_stateless_reset_filter(ngx_connection_t *c) +{ + time_t now; + u_char salt; + ngx_uint_t i, n, m, hit; + u_char hash[20]; + + static time_t t; + static u_char rndbyte; + static uint8_t bitmap[65536]; + + now = ngx_time(); + + if (t != now) { + t = now; + + if (RAND_bytes(&rndbyte, 1) != 1) { + return NGX_ERROR; + } + + ngx_memzero(bitmap, sizeof(bitmap)); + } + + hit = 0; + + for (i = 0; i < 3; i++) { + salt = rndbyte + i; + + ngx_quic_address_hash(c->sockaddr, c->socklen, 0, &salt, 1, hash); + + n = hash[0] | hash[1] << 8; + m = 1 << hash[2] % 8; + + if (!(bitmap[n] & m)) { + bitmap[n] |= m; + + } else { + hit++; + } + } + + if (hit == 3) { + return NGX_DECLINED; + } + + return NGX_OK; +} + + ngx_int_t ngx_quic_send_cc(ngx_connection_t *c) { |
