Linux 网络诊断实战:从 ss、tcpdump 到 conntrack 表满导致的丢包

Linux 网络诊断实战:从 ss、tcpdump 到 conntrack 表满导致的丢包

1. 从一次线上丢包说起

假设你负责的一台网关服务器,业务方反馈"偶发连接超时,但后端服务日志里看不到请求"。你登录机器,ping 网关是通的,curl 本地回环也正常,服务进程没有重启,CPU 和内存都不高。真正可疑的是:客户端连接成功率从 99.99% 掉到 97%,丢包是随机发生的,不是整条链路全断。

这类问题的麻烦在于,网络故障不一定表现为"完全不通"。它可能表现为 TCP 握手阶段 SYN 丢失、建连后偶尔超时、重传升高但业务还能跑、NAT 场景下连接被错误丢弃。要定位它,不能只靠一个命令,而要按 Linux 收发包的路径逐层排查。

本文围绕一个核心目标:让你先能复述 Linux 网络诊断的整体框架,再用 sstcpdumpconntrack 等工具把每一层的问题落到具体现象和命令输出上。

2. 先记住一个最小模型:包从哪里进来,状态存在哪里

可以先把它理解成三个部分:协议栈状态、抓包观察点、连接跟踪表

  • 协议栈状态:Linux 内核维护 TCP 连接状态,比如 ESTABLISHED、TIME_WAIT、SYN_RECV。ss 就是读取这些状态。
  • 抓包观察点:tcpdump 在网卡或指定接口上把流过该点的报文复制出来,用来确认"包到底有没有到""回复有没有发出去"。
  • 连接跟踪表:Netfilter 的 conntrack 模块记录每条连接的五元组和状态,NAT、防火墙、容器网络都依赖它。表满时,新连接可能被直接丢弃。

把一次"客户端连服务端"的请求完整走一遍:

text 复制代码
客户端应用
   |
   v
[ 客户端协议栈 ] -- SYN --> [ 服务端网卡驱动 ]
   |                              |
   |                              v
   |                       [ conntrack 查表/建表 ]
   |                              |
   |                              v
   |                       [ 服务端协议栈 ]
   |                              |
   |<-- SYN/ACK ------------------|
   |
   v
[ 连接建立,进入 ESTABLISHED ]

这个图帮助理解"包经过哪些位置",但不能替代真实细节:比如内核里的 Netfilter 钩子点、路由决策、socket 接收队列长度、NAT 双向转换,都需要进一步看具体输出。

3. 诊断全局框架:先看状态,再看报文,再看连接跟踪

遇到网络异常时,建议固定按下面顺序推进:

步骤 观察对象 常用命令 主要回答的问题
1 本机连接状态统计 ss -sss -lnt 连接是堆积在哪类状态,端口是否耗尽
2 实时报文 tcpdump -i any -nn SYN、ACK、RST 是否到达或发出
3 连接跟踪表 conntrack -Sdmesg 是否发生表满、插入失败、丢包
4 TCP 重传与 RTT ss -tinstat 是否存在重传、RTT 是否异常升高

这个顺序不是死规矩,但符合"先确认本机状态,再确认网络报文,最后确认内核辅助表"的思路。

text 复制代码
业务异常
   |
   v
ss 看状态分布 ---> TIME_WAIT 过多? SYN_RECV 堆积?
   |
   v
tcpdump 抓关键报文 ---> SYN 有没有到? SYN/ACK 有没有回?
   |
   v
conntrack 看表 ---> 表满? 插入失败? NAT 是否正常?
   |
   v
ss -ti 看重传/RTT ---> 是否链路质量或拥塞问题

4. 用 ss 建立连接状态全景

ss 的作用是读取 socket 统计信息。相比 netstat,它在连接数很大时更快。入门先看整体统计:

bash 复制代码
ss -s

输出中会看到 TCP: 后面的统计,例如 estabclosedorphanedsynrecvtimewaitsynrecv 表示收到了 SYN、回了 SYN/ACK,但还没收到最终 ACK 的半连接数量;timewait 表示主动关闭方留下的状态。

看监听端口和当前连接:

bash 复制代码
ss -lntp
ss -ntp state established
  • -l 只看监听 socket。
  • -n 不做服务名解析,直接显示端口号。
  • -t 只看 TCP。
  • -p 显示进程信息,需要相应权限。

如果看到某台客户端 IP 大量处于 SYN-RECV,可能是 SYN 洪水或客户端异常重试。如果看到大量 TIME_WAIT 集中在某个本地端口,可能是短连接频繁访问同一个目标端口。

这里最容易误解的是:ss -stimewait 数量多,不一定就是故障。TIME_WAIT 是 TCP 正常关闭的一部分。真正需要关注的是它是否耗尽本地端口、是否伴随连接建立失败。

5. 用 tcpdump 把"丢包"落到具体报文

ss 告诉你状态,tcpdump 告诉你报文有没有到。典型用法:

bash 复制代码
tcpdump -i any -nn -c 20 'tcp port 8080 and (tcp[tcpflags] & (tcp-syn|tcp-ack) != 0)'
  • -i any:在所有接口抓包。
  • -nn:不解析主机名和端口名。
  • -c 20:抓 20 个包后退出。
  • 过滤表达式只抓 8080 端口上带 SYN 或 ACK 标志的 TCP 包。

如果你只关心某台客户端:

bash 复制代码
tcpdump -i eth0 -nn 'host 10.0.0.12 and tcp port 8080'

抓包时要注意:在 any 接口抓到的不一定等于网卡实际发送的报文,因为抓包点在内核协议栈中的位置不同。通常可以按"入方向看请求是否到达,出方向看响应是否发出"判断。

一个常见序列是:只看到 SYN,没有看到 SYN/ACK。这说明服务端没有回应,问题可能在服务端协议栈、conntrack 丢包、防火墙 DROP 或监听进程未工作。如果看到 SYN 和 SYN/ACK,但没有最终 ACK,则可能是客户端侧或链路中间丢包。

6. conntrack 表溢出为什么会造成丢包

连接跟踪表可以理解成内核维护的一张"连接登记簿"。每条经过 Netfilter 的连接会记录源 IP、源端口、目的 IP、目的端口、协议和状态。NAT 场景下,它还负责把内网地址映射成外网地址。

查看当前表使用情况:

bash 复制代码
conntrack -S
cat /proc/sys/net/netfilter/nf_conntrack_count
cat /proc/sys/net/netfilter/nf_conntrack_max

如果 nf_conntrack_count 接近 nf_conntrack_max,新连接可能无法插入表,内核会记录类似 nf_conntrack: table full, dropping packet 的日志。

text 复制代码
新连接到达
   |
   v
查 conntrack 表
   |
   +-- 表未满 --> 创建新表项,继续协议栈处理
   |
   +-- 表已满 --> 尝试删除过期项
                     |
                     +-- 删除失败 --> 丢弃报文,记录 table full

这里的设计取舍是:conntrack 为 NAT 和状态防火墙提供了必要信息,但表项数量和超时时间需要根据业务规模调整。表太小会在高并发下丢包,表太大又会占用更多内存。

临时调大表容量:

bash 复制代码
echo 262144 > /proc/sys/net/netfilter/nf_conntrack_max

永久配置建议写入 /etc/sysctl.d/ 下的文件,例如:

ini 复制代码
net.netfilter.nf_conntrack_max = 262144
net.netfilter.nf_conntrack_tcp_timeout_established = 86400
net.netfilter.nf_conntrack_tcp_timeout_time_wait = 60

修改后执行 sysctl -p 生效。注意,调大 nf_conntrack_max 会增加内存占用,不是越大越好。

7. TIME_WAIT 与端口耗尽:短连接高并发下的真实瓶颈

TIME_WAIT 是主动关闭连接的一方在发送最后一个 ACK 后进入的状态,持续 2 倍 MSL。它的作用是保证旧连接报文不会干扰新连接,并确保对端能收到最终 ACK。

问题出在短连接场景:如果一台机器每秒发起大量到同一个目标 IP 和端口的连接,本地临时端口范围有限,TIME_WAIT 会占用端口,最终出现"Cannot assign requested address"。

查看本地端口范围和 TIME_WAIT:

bash 复制代码
cat /proc/sys/net/ipv4/ip_local_port_range
ss -tan state time-wait | wc -l

调优思路有几种:

方案 做法 适用场景 注意点
启用复用 net.ipv4.tcp_tw_reuse = 1 客户端主动关闭多 只对出方向连接生效,依赖时间戳
减少主动关闭 使用长连接/连接池 后端访问下游 需要应用层改造
扩大端口范围 调整 ip_local_port_range 短连接不可避免 端口仍有限
分散目标 多目标 IP 或多端口 下游支持 依赖架构

需要强调:tcp_tw_recycle 在现代内核中已移除或默认关闭,不建议依赖它。真正的工程解法通常是连接池加合理超时,而不是只调内核参数。

8. 重传与 RTT:从 ss -ti 和 nstat 看链路质量

当连接能建立但偶尔超时,需要看重传和往返时间。ss -ti 可以显示每个连接的详细 TCP 信息:

bash 复制代码
ss -tin state established '( dport = :8080 or sport = :8080 )'

输出中会看到 rttrttvarretranscwnd 等字段。retrans 表示重传次数,rtt 表示平滑后的往返时间。

也可以用 nstat 看全局统计:

bash 复制代码
nstat -az | grep -E 'TcpRetransSegs|TcpExtTCPLostRetransmit|TcpExtTCPTimeouts'

如果重传率持续升高,可能是链路拥塞、中间设备丢包、MTU 问题或对端处理慢。RTT 突然升高通常说明路径上出现排队或绕行。

这里要区分:偶发重传 在公网环境很常见,不一定是故障;持续重传加业务超时 才需要处理。诊断时把 ss -ti 的单连接视角和 nstat 的全局视角结合。

9. 完整示例一:复现并观察 conntrack 表满导致的丢包

目标:在一台测试机上人为制造 conntrack 表压力,观察表满时的丢包日志。

前置环境:Linux 测试机,具备 root 权限,已安装 conntrack-toolsiperf3(可选)。

步骤:

bash 复制代码
# 1. 记录当前最大值
cat /proc/sys/net/netfilter/nf_conntrack_max

# 2. 临时调小最大值,制造表满条件
echo 1024 > /proc/sys/net/netfilter/nf_conntrack_max

# 3. 持续观察日志
dmesg -w | grep -i conntrack

另开终端制造大量短连接:

bash 复制代码
for i in $(seq 1 2000); do
  curl -s -o /dev/null --connect-timeout 1 http://127.0.0.1:8080/ &
done
wait

预期结果:在 dmesg 中看到 nf_conntrack: table full, dropping packet,部分 curl 失败。

验证与清理:

bash 复制代码
cat /proc/sys/net/netfilter/nf_conntrack_count
echo 262144 > /proc/sys/net/netfilter/nf_conntrack_max

容易改错的地方:直接把最大值调得很小可能影响本机其他服务,测试机要隔离;生产环境不要用这种方式复现。

10. 完整示例二:用 ss 与 tcpdump 定位 SYN 重传

目标:模拟服务端未监听,观察客户端 SYN 重传,并用 tcpdump 验证。

前置环境:Linux 机器,安装 tcpdump

步骤:

bash 复制代码
# 终端 A:抓包
tcpdump -i lo -nn 'tcp port 9999 and (tcp[tcpflags] & tcp-syn != 0)'
bash 复制代码
# 终端 B:向未监听端口发起连接
curl -s --connect-timeout 3 http://127.0.0.1:9999/ || echo "connect failed"

预期结果:tcpdump 看到多个 SYN 包,间隔大约 1 秒、2 秒、4 秒,这是 TCP 的指数退避重传。

也可以同时观察:

bash 复制代码
ss -tan state syn-sent

如果连接很快失败,可能看到 syn-sent 短暂出现后消失。

适用范围:验证"请求到底有没有发出去"以及"对端有没有回 SYN/ACK"。边界是回环接口不涉及物理链路,不能模拟中间网络设备丢包。

11. 完整示例三:生产排障脚本,采集网络诊断快照

目标:写一个可复用的 shell 脚本,在故障时一次性采集状态、报文统计和 conntrack 信息。

前置环境:Linux 服务器,具备 root 权限,安装 sstcpdumpconntrack-tools

脚本 net-diag.sh

bash 复制代码
#!/bin/bash
set -euo pipefail

OUT_DIR="/tmp/net-diag-$(date +%Y%m%d%H%M%S)"
mkdir -p "$OUT_DIR"

echo "[1/6] ss summary"
ss -s > "$OUT_DIR/ss-summary.txt"

echo "[2/6] listening sockets"
ss -lntp > "$OUT_DIR/ss-listen.txt"

echo "[3/6] established sockets"
ss -ntp state established > "$OUT_DIR/ss-established.txt"

echo "[4/6] timewait count"
ss -tan state time-wait | wc -l > "$OUT_DIR/timewait-count.txt"

echo "[5/6] conntrack stats"
conntrack -S > "$OUT_DIR/conntrack-stats.txt" 2>/dev/null || echo "conntrack not available" > "$OUT_DIR/conntrack-stats.txt"
cat /proc/sys/net/netfilter/nf_conntrack_count > "$OUT_DIR/conntrack-count.txt" 2>/dev/null || true
cat /proc/sys/net/netfilter/nf_conntrack_max > "$OUT_DIR/conntrack-max.txt" 2>/dev/null || true

echo "[6/6] tcp retrans stats"
nstat -az | grep -E 'TcpRetransSegs|TcpExtTCPTimeouts' > "$OUT_DIR/tcp-retrans.txt" 2>/dev/null || true

echo "done: $OUT_DIR"

执行:

bash 复制代码
chmod +x net-diag.sh
sudo ./net-diag.sh

预期结果:在 /tmp/net-diag-时间戳/ 下得到一组文本文件,可以打包给同事或留档对比。

关键点:脚本要加 set -euo pipefail,避免某条命令失败后继续;生产机器上抓包要控制时长和数量,避免磁盘被写满。

12. 常见误区:别把正常状态当成故障

现象 常见误解 更合理的判断
大量 TIME_WAIT 认为内核有 bug 先看是否耗尽端口、是否短连接过多
SYN_RECV 多 直接认定被攻击 结合来源 IP、半连接队列、SYN cookie
偶发重传 认为链路完全不可用 看重传率是否持续,业务是否超时
conntrack 表满 只调大 max 同时看超时时间、业务连接模型
tcpdump 没抓到包 认为没有流量 抓包点、过滤表达式、权限都可能影响

最常见的错误是"看到指标升高就改参数"。更稳妥的方式是先确认指标是否对应真实业务损失,再决定是否调整。

13. 生产实践建议:按条件选择手段

当现象是连接建立失败、且本机是 NAT 网关时,优先查 conntrack 表。当现象是短连接高并发、本地端口耗尽时,优先查 TIME_WAIT 和端口范围。当现象是连接能建立但偶发超时,优先查重传和 RTT。当现象是"服务端说没收到请求"时,优先用 tcpdump 确认报文是否到达。

参数调整要遵循小步验证:先记录基线,再修改,再观察 ssnstatdmesg 的变化。不要一次改多个参数,否则无法归因。

对于后端服务,优先使用长连接和连接池,减少 TIME_WAIT 和 conntrack 表项压力。对于网关和入口服务,监控 nf_conntrack_count / nf_conntrack_max 比例,设置告警阈值。

14. 排障清单:从现象到命令

  1. 连接超时,先执行 ss -s 看状态分布。
  2. 怀疑端口耗尽,执行 ss -tan state time-wait | wc -l
  3. 怀疑报文没到,执行 tcpdump -i any -nn 'tcp port <端口>'
  4. 怀疑 conntrack 表满,执行 conntrack -Sdmesg | grep -i conntrack
  5. 怀疑链路质量,执行 ss -tinnstat -az
  6. 采集完成后,把命令输出和业务时间点对齐,避免只看单点数据。

15. 面试/复盘问题

  • TIME_WAIT 为什么存在?它解决什么问题?
  • conntrack 表满时,内核为什么会丢包,而不是排队?
  • tcpdump -i anytcpdump -i eth0 的抓包点有什么差异?
  • 重传率高一定是网络故障吗?如何结合业务判断?
  • 如果让你设计一个高并发短连接客户端,你会如何避免端口耗尽?

16. 总结

把 Linux 网络诊断记成一条链路:ss 看本机连接状态,tcpdump 看报文有没有到,conntrack 看连接跟踪表是否成为瓶颈,ss -tinstat 看重传与 RTT。遇到丢包时,先确认是握手阶段、建连后、还是表项资源问题,再选对应命令。

真正有效的排障不是背命令,而是能说清楚"谁在什么条件下做了什么,结果怎样"。当你能把一次请求从应用、协议栈、网卡到 conntrack 的路径复述出来,剩下的就是按证据缩小范围。

17. 参考资料

  • Linux man-pages: ss(8), tcpdump(8), conntrack(8)
  • Linux 内核文档: networking/netfilter/nf_conntrack-sysctl.txt
  • RFC 793: Transmission Control Protocol
  • RFC 1122: Requirements for Internet Hosts
  • 《TCP/IP 详解 卷1:协议》,W. Richard Stevens
  • 《Linux 高性能服务器编程》,游双
相关推荐
玄芯散人2 天前
【筑基·055】TCP vs UDP:可靠和快速的取舍
网络协议·tcpdump
那年窗外下的雪.6 天前
AIDC 学习日志|第 24 天|设备输出反推与 MAC Flapping 定位
网络协议·学习·tcp/ip·http·macos·tcpdump
RisunJan1 个月前
Linux命令-tcpdump(网络数据包捕获)
linux·网络·tcpdump
Q741_1471 个月前
TcpDump 使用笔记
网络·c++·笔记·测试工具·tcpdump
测试运维日常笔记1 个月前
tcpdump(Linux服务器)配合 Wireshark(本地分析)使用手顺与常用过滤语法详解
linux·服务器·tcpdump
gwf2161 个月前
Mellanox ConnectX网卡RDMA性能优化全指南:深度解析(固件调优、中断亲和与GPUDirect必知必会)
网络协议·tcp/ip·网络安全·性能优化·tcp·tcpdump
酷可达拉斯1 个月前
Linux操作系统-tcpdump抓包定位网络问题实战
linux·运维·服务器·网络·tcpdump
gwf2162 个月前
Soft-RoCE与Soft-iWARP深度解析:无硬件RDMA学习环境搭建(零基础必知必会)
人工智能·python·tcp/ip·tcp·tcpdump
雾里0不看花2 个月前
【App Service Linux】在Linux App Service中安装 tcpdump 并抓取网络包
linux·网络·tcpdump