Linux net.ipv4.tcp_max_syn_backlog 与 syncookies 配合的半连接队列防护

1次阅读

tcp_max_syn_backlog仅在net.ipv4.tcp_syncookies=0时生效,推荐设为1024–4096且≤net.core.somaxconn;启用syncookies后半连接队列被逻辑绕过,该参数仅作备用缓冲。

Linux net.ipv4.tcp_max_syn_backlog 与 syncookies 配合的半连接队列防护

tcp_max_syn_backlog 设置多大才有效?

这个值不是越大越好,它只在 net.ipv4.tcp_syncookies=0 时真正起作用。一旦启用了 syncookies(即 net.ipv4.tcp_syncookies=1),内核就绕过半连接队列,直接用 cookie 机制处理 SYN,此时 tcp_max_syn_backlog 只影响未启用 syncookies 时的初始队列长度,或者 syncookies 关闭后、SYN 洪水未达触发阈值前的缓冲。

  • 实际生效前提是 net.ipv4.tcp_syncookies=0,否则改它没意义
  • 值设太高可能浪费内存,且无法缓解真正的洪水——因为队列满了照样丢包
  • 推荐值通常为 1024–4096,具体看并发连接建立速率和内存预算;超过 8192 很少必要
  • 注意:该值必须 ≤ net.core.somaxconn,否则启动时会被静默截断

syncookies=1 时,半连接队列还存在吗?

存在,但被“逻辑绕过”。启用 net.ipv4.tcp_syncookies=1 后,内核不再把每个 SYN 存入半连接队列,而是直接计算一个加密 cookie 并发回 SYN+ACK;只有客户端返回 ACK 且 cookie 验证通过,才在全连接队列(accept queue)里创建 socket。

  • 半连接队列(SYN queue)依然分配空间,但几乎不填充,ss -s 显示的 SYNs to LISTEN sockets ignored 会明显上升
  • 这种机制牺牲了部分 TCP 特性:比如 TCP 选项(如时间戳、SACK)在 SYN 包里丢失,且无法做源地址验证(如 tcp_tw_reuse/tw_recycle 已废弃,不提)
  • 不是所有场景都适合开 syncookies:高延迟链路可能导致三次握手失败率微升;某些中间设备(如老旧 NAT)可能丢弃带非标准 cookie 的 SYN+ACK

怎么确认 syncookies 正在工作?

最直接的方式是观察内核统计和连接行为:

  • 查看 /proc/net/netstatTcpExtSyncookiesSent 计数是否增长:
    awk '/SyncookiesSent/ {print $2}' /proc/net/netstat
  • ss -s 观察 SYNs to LISTEN sockets dropped 是否稳定,而 SYNs to LISTEN sockets ignored 在上涨
  • 手动触发测试:临时调低 tcp_max_syn_backlog 到 32,再用 hping3 -S -p 80 -i u10000 target_ip 发少量 SYN,若连接仍能建立,说明 syncookies 已接管
  • 注意:如果 TcpExtSyncookiesSent 为 0,先检查 net.ipv4.tcp_syncookies 是否真为 1(有些发行版默认关,或被 systemd-sysctl 覆盖)

为什么改了 tcp_max_syn_backlog 却没效果?

常见原因不是配置没加载,而是根本没走到它控制的路径:

  • net.ipv4.tcp_syncookies 是 1 → 半连接队列被跳过,tcp_max_syn_backlog 不参与决策
  • net.core.somaxconn 更小 → 内核自动把 tcp_max_syn_backlog 截断到该值,cat /proc/sys/net/core/somaxconn 必须 ≥ 你设的 backlog
  • 应用层 listen() 时传入的 backlog 参数更小 → 如 Python 的 socket.listen(128),最终生效的是 min(系统值, 应用传值)
  • 系统启用了 BPF 或 eBPF 流控(如 Cilium、tc ingress Filter),SYN 包在到达 TCP 前就被丢弃,压根不进协议栈判断

syncookies 和 tcp_max_syn_backlog 的配合点很窄:它只在 syncookies 关闭、且洪水尚未压垮队列时提供一点缓冲;一旦开了 syncookies,队列大小就只是个“备用通道”的容量,实际防御靠的是 cookie 生成与验证逻辑本身。这点容易被文档误导。

text=ZqhQzanResources