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

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_WAIT或SYN_SENT条目(说明连接无法正常完成或释放) -
conntrack -S:查看各状态计数,重点关注insert_failed是否持续增长(每增 1 表示一个新连接因表满被丢弃) -
conntrack -L -d:过滤特定服务 IP,确认是否集中在某几个后端连接上YOUR_APP_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 交叉比对。










