conntrack 表满后新连接失败但 ss -ant 显示 ESTABLISHED 正常的隐藏原因

1次阅读

ss -ant 显示 ESTABLISHED 是 TCP socket 状态,而 conntrack 表满仅影响需跟踪的连接;本地回环、未启用模块或 liberal 模式下连接可能根本不进 conntrack 表。

conntrack 表满后新连接失败但 ss -ant 显示 ESTABLISHED 正常的隐藏原因

conntrack 表满时为什么 ss -ant 还能看到大量 ESTABLISHED?

因为 ss -ant 显示的是 socket 状态,而 conntrack 跟踪的是网络连接的四元组(源IP/端口、目标IP/端口、协议、方向)状态机。ESTABLISHED socket 只说明本地 TCP 状态是 ESTABLISHED,不意味着该连接一定在 conntrack 表里——它可能压根没被跟踪,或已被提前删除。

常见触发场景:

  • 内核未启用连接跟踪(nf_conntrack_enable=0 或模块未加载),但用户误以为开启着
  • 连接是本地 loopback(127.0.0.1)且启用了 .net.netfilter.nf_conntrack_tcp_be_liberal=1,部分连接跳过跟踪
  • 连接建立后很快关闭,但 conntrack 条目因 timeout 未及时回收,新连接又不断涌入,表满后新 SYN 报文被丢弃,老 ESTABLISHED socket 却还活着

如何确认 conntrack 表是否真满、以及哪些连接卡在表里?

直接查当前使用量和上限:

cat /proc/sys/net/netfilter/nf_conntrack_count cat /proc/sys/net/netfilter/nf_conntrack_max

若前者接近后者(比如 >95%),基本可判定表满。进一步看积类型:

  • conntrack -L | head -20:观察是否有大量 TIME_WaiTSYN_SENT 条目(说明连接无法正常完成或释放)
  • conntrack -S:查看各状态计数,重点关注 insert_failed 是否持续增长(每增 1 表示一个新连接因表满被丢弃)
  • conntrack -L -d YOUR_app_IP:过滤特定服务 IP,确认是否集中在某几个后端连接上

为什么新连接失败却看不到 SYN 被 drop?

conntrack 表满时,内核在 nf_conntrack_invert_tuple()resolve_normal_ct() 阶段直接返回 -ENOMEM,后续 netfilter hook(如 iptables input)甚至收不到该包——它在连接跟踪模块就终止了,不会进入 conntrack 日志、iptables LOG target,也不会触发 tcpdump 在 raw socket 层捕获(除非用 tcpdump -i any 并确保抓到 ingress 路径最前端)。

验证方式:

  • 开启 conntrack 日志:echo 1 > /proc/sys/net/netfilter/nf_conntrack_log_invalid,然后 dmesg -w 观察是否刷出 nf_conntrack: can't allocate new conntrack
  • perf trace -e 'net:*' -F 100 抓内核事件,搜索 nf_conntrack_alloc 返回 -12(-ENOMEM)

临时缓解与长期配置建议

临时救急可以清空非活跃连接(慎用!别清 ESTABLISHED):

conntrack -F -v  # 清空所有(含 ESTABLISHED,不推荐) conntrack -D --state INVALID,UNREPLIED,ASSUred,TIME_WAIT  # 更安全

长期必须调参:

  • 增大上限:sysctl -w net.netfilter.nf_conntrack_max=131072(按内存估算:每个条目约 320 字节,128K ≈ 40MB)
  • 缩短超时:sysctl -w net.netfilter.nf_conntrack_tcp_timeout_time_wait=30(默认 120s)
  • 避免跟踪不需要的流量:用 iptables -t raw -A PREROUTING -i lo -j NOTRACK 跳过本地回环
  • 检查是否有连接泄漏:比如短连接服务未复用连接、http keepalive 关闭、客户端频繁重连

真正棘手的是那些“看不见”的连接:它们没进 conntrack,也不在 ss 的监听队列里,却占着端口、消耗 fd、让新连接卡在 SYN_RCVD 后再也进不来——这时候得结合 /proc/net/{tcp,tcp6}lsof -i 交叉比对。

text=ZqhQzanResources