深入解析 nghttp2_option_set_max_outbound_ack:HTTP/2 出站 ACK 洪水检测与连接保护机制 深入解析 nghttp2_option_set_max_outbound_ackHTTP/2 出站 ACK 洪水检测与连接保护机制【免费下载链接】fluent-bitFast and Lightweight Logs, Metrics and Traces processor for Linux, BSD, OSX and Windows项目地址: https://gitcode.com/GitHub_Trending/fl/fluent-bit本篇技术指南以 nghttp2HTTP/2 C 库官方 API 参考文档 nghttp2_option_set_max_outbound_ack.rst 为核心结合当前仓库内置的 nghttp2-1.65.0 源码系统讲解该选项的作用、默认值、底层实现与典型使用方式。读完本文你将理解 HTTP/2 会话中出站 SETTINGS ACK / PING ACK 帧为何需要数量上限、超限时库如何判定对端行为异常并关闭会话以及如何在 fluent-bit 所依赖的 HTTP/2 通信栈中正确配置这一保护参数。一、为什么需要限制出站 ACK 帧数量在 HTTP/2 协议中SETTINGS帧与PING帧都具有请求-确认语义收到对方的SETTINGS帧后需要回送SETTINGS ACK收到PING帧后需要回送PING ACK。在正常情况下ACK 帧数量应与对端发来的请求帧数量一一对应。但如果对端peer在短时间内持续发送海量SETTINGS或PING帧而不等待响应本端就会被迫在出站队列outbound queue中积压大量等待发送的 ACK 帧。这种行为通常不是正常业务流量而是典型的协议层洪水攻击flooding attack攻击者通过制造 ACK 回送压力迫使对端消耗大量内存维护出站队列从而拖垮服务。nghttp2 为此提供了一套内建防护为每个会话维护一个出站队列洪水计数器obq_flood_counter_并允许应用程序通过nghttp2_option_set_max_outbound_ack()设定计数器上限从机制上阻止 ACK 洪水。二、函数签名与头文件#include nghttp2/nghttp2.h void nghttp2_option_set_max_outbound_ack(nghttp2_option *option, size_t val)所属头文件nghttp2/nghttp2.h仓库内实际路径为 lib/nghttp2-1.65.0/lib/includes/nghttp2/nghttp2.h公开声明位于该文件第 3154 行附近第一个参数option指向nghttp2_option配置对象的指针该对象通常在创建会话前通过nghttp2_option_new()分配并初始化第二个参数val允许在nghttp2_session对象中保留的最大出站 ACK 帧数量类型为size_t返回值void该 setter 函数不返回错误码。该函数用于创建会话之前配置nghttp2_option会话创建后选项即固定生效。三、参数语义什么是被保留的 ACK 帧原文档明确说明val表示允许在nghttp2_session对象中保留的最大出站SETTINGS ACK与PING ACK帧数量。这里的保留retained指的是已经生成、排入出站队列但尚未被nghttp2_session_send()发送出去或已发送但尚未从队列移除的 ACK 帧。当对端源源不断地发送SETTINGS/PING请求帧时这些 ACK 帧就会在会话对象内不断累积。从源码结构看对应实现位于 lib/nghttp2-1.65.0/lib/nghttp2_session.h第 265267 行定义了计数器的注释与字段/* Counter for detecting flooding in outbound queue. If it exceeds max_outbound_ack, session will be closed. */ size_t obq_flood_counter_;第 268270 行定义了上限字段/* The maximum number of outgoing SETTINGS ACK and PING ACK in outbound queue. */ size_t max_outbound_ack;。两者组合构成了完整的洪水检测状态计数器obq_flood_counter_与上限max_outbound_ack比较一旦达到或超过上限会话即被判定处于被攻击状态。四、核心行为判定对端异常并关闭会话原文档描述了本选项的两个关键行为触发条件当保留的 ACK 帧数量超过设定上限时对端被视为行为异常misbehaving处理结果会话将被关闭session will be closed。需要强调的是这里关闭的不是单个流stream而是整个 HTTP/2 会话连接因此该机制是一道连接级的协议安全防线与流级别的流控如SETTINGS_MAX_CONCURRENT_STREAMS属于不同层面。默认值原文档给出的默认值为1000即val 1000。该默认值在源码中定义为宏NGHTTP2_DEFAULT_MAX_OBQ_FLOOD_ITEM见 lib/nghttp2-1.65.0/lib/nghttp2_session.h 第 100 行#define NGHTTP2_DEFAULT_MAX_OBQ_FLOOD_ITEM 1000也就是说即使应用程序不显式调用本函数nghttp2 也会以内建默认值 1000 启用 ACK 洪水防护。调用本函数的意义在于按业务场景调整灵敏度。五、源码实现剖析5.1 选项结构体中的字段与标志位在 lib/nghttp2-1.65.0/lib/nghttp2_option.h 中第 69 行定义了标志位NGHTTP2_OPT_MAX_OUTBOUND_ACK 1 11用于记录该选项是否被显式设置第 9597 行定义了字段/* NGHTTP2_OPT_MAX_OUTBOUND_ACK */ size_t max_outbound_ack;。nghttp2_option结构体通过opt_set_mask第 110 行按位记录哪些字段被赋值过会话初始化时据此决定采用默认值还是用户指定值。5.2 setter 实现lib/nghttp2-1.65.0/lib/nghttp2_option.c 第 124127 行的实现非常直观void nghttp2_option_set_max_outbound_ack(nghttp2_option *option, size_t val) { option-opt_set_mask | NGHTTP2_OPT_MAX_OUTBOUND_ACK; option-max_outbound_ack val; }两步操作先在掩码中置位NGHTTP2_OPT_MAX_OUTBOUND_ACK再写入数值。该函数本身不做数值合法性校验例如传 0 或极小值会立即使任何待回送的 ACK 都触发洪水判定调用方需要自行确保val合理。5.3 会话创建时的初始化与应用在 lib/nghttp2-1.65.0/lib/nghttp2_session.c 的会话初始化函数中第 491 行先以默认值兜底(*session_ptr)-max_outbound_ack NGHTTP2_DEFAULT_MAX_OBQ_FLOOD_ITEM;第 544546 行再根据掩码覆盖若opt_set_mask中含有NGHTTP2_OPT_MAX_OUTBOUND_ACK则(*session_ptr)-max_outbound_ack option-max_outbound_ack;可见该选项的生效路径为nghttp2_option_set_max_outbound_ack()→nghttp2_option→nghttp2_session_*_new(option)→ 会话内max_outbound_ack字段。5.4 三个关键检查点在会话运行期计数器会在三个位置与上限比较任一位置超标即拒绝处理检查点 1接收 SETTINGS 帧时提前拦截lib/nghttp2-1.65.0/lib/nghttp2_session.c 第 55975601 行在帧解析早期就做防护避免为洪水帧分配内存/* Check the settings flood counter early to be safe */ if (session-obq_flood_counter_ session-max_outbound_ack !(iframe-frame.hd.flags NGHTTP2_FLAG_ACK)) { return NGHTTP2_ERR_FLOODED; }注意此处排除NGHTTP2_FLAG_ACK对端回送的 ACK 帧不计入洪水只有真正的SETTINGS请求帧才推进计数器。检查点 2提交 PING ACK 帧时第 68516854 行nghttp2_session_add_ping()中当携带NGHTTP2_FLAG_ACK标志时先检查计数器超标直接返回NGHTTP2_ERR_FLOODED不再分配出站帧内存。检查点 3提交 SETTINGS ACK 帧时第 69977004 行nghttp2_session_add_settings()中仅当flags NGHTTP2_FLAG_ACK时执行同样的obq_flood_counter_ max_outbound_ack检查超标返回NGHTTP2_ERR_FLOODED。这三个检查点覆盖了接收端提前拦截与发送端提交时拒绝两条路径共同构成对 ACK 洪水的完整防护。结合原文档会话将被关闭的描述可以推断应用程序在收到NGHTTP2_ERR_FLOODED后应主动调用nghttp2_session_terminate_session()或通过GOAWAY关闭会话以彻底断开与异常对端的连接。六、使用方法与代码示例典型使用流程为创建选项对象 → 设置本选项 → 创建会话 → 释放选项对象。以下示例可编译运行于任何集成 nghttp2 1.65.0 的工程#include nghttp2/nghttp2.h #include stdio.h int main(void) { nghttp2_option *opt; nghttp2_session *session; nghttp2_session_callbacks *callbacks; /* 1. 创建选项对象 */ if (nghttp2_option_new(opt) ! 0) { fprintf(stderr, nghttp2_option_new failed\n); return -1; } /* 2. 设置出站 ACK 上限对端一旦积压超过 1000 个待回送 SETTINGS ACK / PING ACK即判定其行为异常 */ nghttp2_option_set_max_outbound_ack(opt, 1000); /* 3. 创建会话回调略按业务填充 */ if (nghttp2_session_callbacks_new(callbacks) ! 0) { return -1; } /* 4. 以配置好的选项创建客户端会话 */ if (nghttp2_session_client_new(session, callbacks, NULL) ! 0) { fprintf(stderr, nghttp2_session_client_new failed\n); return -1; } /* 5. 选项对象完成使命可释放 */ nghttp2_option_del(opt); /* ... 正常收发帧若对端 ACK 洪水触发 send 侧 API 将返回 NGHTTP2_ERR_FLOODED ... */ nghttp2_session_del(session); nghttp2_session_callbacks_del(callbacks); return 0; }配置建议场景建议取值说明常规服务端 / 客户端1000默认足够容忍正常往返时延下的 ACK 积压高并发短连接、弱网环境20005000网络抖动时 ACK 队列天然易积压适当放宽可避免误杀正常对端严格安全加固场景100500更快发现洪水行为但需警惕误判导致的正常连接被关闭内部可信网络可保留默认值无需特殊调整需要留意过小的取值可能把正常高延迟链路误判为洪水因为 ACK 帧在队列中滞留时间与网络往返时延正相关过大的取值则会削弱防护效果允许攻击者在连接关闭前消耗更多内存。因此建议结合业务的 PING/SETTINGS 频率与链路时延实测后确定。七、与相关防护选项的配合从 lib/nghttp2-1.65.0/lib/nghttp2_option.h 的选项枚举可见nghttp2 提供了一组面向协议洪水的同类防护本文选项与它们互补选项防护对象备注NGHTTP2_OPT_MAX_OUTBOUND_ACK本文出站 SETTINGS ACK / PING ACK 积压洪水默认 1000NGHTTP2_OPT_MAX_SETTINGS单个 SETTINGS 帧内条目数过多见nghttp2_option_set_max_settings()检查点在 nghttp2_session.c 第 5613 行附近超标会以NGHTTP2_ENHANCE_YOUR_CALM终止会话NGHTTP2_OPT_MAX_CONTINUATIONSHEADER 帧后跟随的 CONTINUATION 帧数量洪水见nghttp2_option_set_max_continuations()NGHTTP2_OPT_STREAM_RESET_RATE_LIMITRST_STREAM 风暴基于令牌桶限速stream_reset_burst/stream_reset_rateNGHTTP2_OPT_MAX_RESERVED_REMOTE_STREAMS服务端 PUSH_PROMISE 数量洪泛防止对端耗尽本端内存在 nghttp2_session.c 第 487493 行的会话初始化段可以看到这些防护默认值被统一装配如max_concurrent_streams默认 100、max_settings、max_continuations等表明 nghttp2 把连接级资源防护作为会话的内建职责本文选项是其中专门针对 ACK 洪水的一环。八、在 fluent-bit 项目中的关联fluent-bit 以第三方库形式内置了 nghttp2-1.65.0目录 lib/nghttp2-1.65.0其 HTTP/2 通信能力构建于此库之上核心模块为 src/flb_http_client_http2.c 及其头文件 include/fluent-bit/flb_http_client_http2.h。当 fluent-bit 作为客户端向支持 HTTP/2 的上游如 OpenTelemetry Collector、支持 h2 的日志网关发送日志、指标或 Trace 数据时底层会话即由 nghttp2 管理。从源码结构看HTTP/2 会话在 flb_http_client_http2.c 中通过nghttp2_session_client_new()创建而nghttp2_option及其各项防护参数含本选项正是在会话创建之前装配的。对 fluent-bit 这类长时间运行、面向不可信公网上游的采集 Agent 而言出站 ACK 上限直接关系到连接的内存占用边界——上游若异常高频发送 SETTINGS/PING该参数能保证 Agent 侧不会无限积压 ACK 帧导致内存膨胀而是按策略断开异常连接。若需在 fluent-bit 的 HTTP/2 客户端路径上显式调整此防护值思路是在会话创建前构造nghttp2_option、调用本函数并传入再通过nghttp2_session_client_new(session, callbacks, opt)注入不显式设置时将采用上文所述的 1000 默认值。总结nghttp2_option_set_max_outbound_ack()用于设定会话内可保留的最大出站SETTINGS ACK/PING ACK帧数量默认值为 1000该上限配合会话内obq_flood_counter_计数器工作超标时返回NGHTTP2_ERR_FLOODED并最终导致会话关闭用于抵御 ACK 洪水攻击源码实现覆盖接收 SETTINGS 帧提前拦截与提交 PING/SETTINGS ACK 时拒绝三个检查点见 lib/nghttp2-1.65.0/lib/nghttp2_session.c配置时需权衡误判风险与防护强度并可与max_settings、max_continuations、流重置限速等选项组合构建完整的 HTTP/2 连接级防洪水体系在 fluent-bit 中该机制经由内置 nghttp2 库作用于 HTTP/2 客户端会话src/flb_http_client_http2.c是保障长期运行采集进程连接内存边界的底层安全参数之一。【免费下载链接】fluent-bitFast and Lightweight Logs, Metrics and Traces processor for Linux, BSD, OSX and Windows项目地址: https://gitcode.com/GitHub_Trending/fl/fluent-bit创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考