2. 网络层排查:防火墙、路由、MTU、SYN丢包

连接建立不了,很多人第一反应就是「证书有问题」或者「协议版本不对」。但说实话,我干了这么多年,至少有一半的 TLS 超时问题,根因都在网络层。TCP 三次握手都没走完,TLS 握手根本轮不到上场。

这一章,我们就从网络层入手。我会带你逐个排查四个最常见的坑:防火墙拦截、路由黑洞、MTU 分片故障、SYN 丢包。嗯,每个我都踩过。

2.1 防火墙:你以为放行了,其实没有

防火墙是最常见的「隐形杀手」。你配了规则,它也可能悄悄把包丢了。

2.1.1 状态检测防火墙的「回程丢包」

很多企业用状态检测防火墙。它只允许「已建立连接」的回程包通过。如果客户端发 SYN,防火墙没记录状态,服务器回的 SYN+ACK 就会被拦截。

怎么查?

  • 在客户端抓包,看有没有收到 SYN+ACK。
  • 如果只看到 SYN 发出,没有 SYN+ACK,大概率是防火墙或路由问题。
  • 在服务器端抓包,看有没有收到 SYN。如果服务器收到了但没回,可能是服务器问题;如果回了但客户端没收到,那就是中间设备丢了。
我的习惯: 两边同时抓包,对比时间戳。差一个 RTT 以上,中间肯定有猫腻。

2.1.2 端口只放行了 TCP 443,但没放行 ICMP

你可能觉得「我开了 443 端口啊」。但路径 MTU 探测依赖 ICMP 不可达消息。如果防火墙把 ICMP 全丢了,客户端发大包时就会卡死。

我曾经遇到一个案例:客户端能建连,但一传大文件就断。查了两天,最后发现防火墙把 ICMP Type 3 Code 4 给过滤了。路径 MTU 探测失败,包被静默丢弃。

避坑指南: 检查防火墙 ACL,确保放行 ICMP Type 3(目标不可达),尤其是 Code 4(需要分片但 DF 置位)。

2.2 路由黑洞:包发出去了,但没人知道去哪

路由黑洞比防火墙更隐蔽。防火墙至少会记录日志,路由黑洞是直接静默丢弃。

2.2.1 默认路由缺失

服务器配了默认网关,但网关设备没有回程路由。客户端 SYN 能到服务器,服务器回的 SYN+ACK 却找不到路回去。

排查方法:

  • 在客户端执行 traceroute 或 mtr,看路径是否完整。
  • 在服务器上执行 ip route get <客户端IP>,看路由表怎么选路。
  • 检查中间路由器是否有对称路由要求。

2.2.2 等价多路径(ECMP)哈希不均

多链路负载均衡时,ECMP 根据五元组哈希选路。如果哈希算法导致 SYN 和 SYN+ACK 走了不同路径,而其中一条路径有防火墙拦截,连接就建不起来。

我记得有一次,客户说「10 次连接能成功 7 次,剩下 3 次超时」。我一看,ECMP 有三条链路,其中一条的防火墙规则配错了。哈希到那条链路的连接全挂。

关键点: 连接成功率不是 100% 时,优先怀疑 ECMP 哈希不均或不对称路由。

2.3 MTU 问题:包太大,过不去

MTU 问题在 TLS 握手中很常见。尤其是 TLS 1.3 的 Certificate 消息可能很大,加上 TCP 头、IP 头,很容易超过链路 MTU。

2.3.1 路径 MTU 探测失败

TCP 会尝试探测路径 MTU。客户端发一个 DF 置位的大包,如果中间链路 MTU 更小,路由器会回 ICMP 不可达。但如果 ICMP 被防火墙拦截了,客户端就永远不知道要减小包大小。

症状:

  • 小包(如 SYN)能通,大包(如 TLS ClientHello + 扩展)超时。
  • 连接建立后,传输大文件时随机断开。
  • 抓包看到 TCP 重传,但对方没回 ACK。

2.3.2 手动调整 MSS

如果路径 MTU 探测不可靠,可以手动限制 TCP MSS。

# Linux 上设置接口的 MSS 钳制
iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN \
  -j TCPMSS --set-mss 1400

# 或者在网卡上直接设置 MTU
ip link set dev eth0 mtu 1400
我的建议: 内网环境直接设 MTU 1400,外网环境用 1400 或 1350。别用 1500,99% 的路径 MTU 问题都能避免。

2.4 SYN 丢包:三次握手的第一步就卡住了

SYN 丢包是最直接的。客户端发了 SYN,服务器没收到,或者服务器回了 SYN+ACK 但客户端没收到。

2.4.1 服务器半连接队列满

服务器收到 SYN 后,会把它放入半连接队列(SYN Queue)。如果队列满了,新 SYN 会被直接丢弃。

原因:

  • 并发连接数过高,超出系统限制。
  • 遭受 SYN Flood 攻击。
  • 应用程序处理慢,全连接队列(Accept Queue)也满了。

排查方法:

  • 查看 netstat -s | grep -i "SYN",看 SYN 丢包计数。
  • 查看 ss -lnt 的 Send-Q 和 Recv-Q。
  • 调整内核参数:
# 增大半连接队列大小
sysctl -w net.ipv4.tcp_max_syn_backlog=65536

# 启用 SYN Cookie(缓解 SYN Flood)
sysctl -w net.ipv4.tcp_syncookies=1

# 减小 SYN+ACK 重试次数
sysctl -w net.ipv4.tcp_synack_retries=2

2.4.2 客户端 SYN 重试超时

客户端发 SYN 后,如果没收到 SYN+ACK,会重试。默认重试次数是 tcp_syn_retries(Linux 默认 6 次,约 127 秒)。如果网络间歇性丢包,客户端可能重试到超时才放弃。

我遇到过最离谱的一次:客户说「连接偶尔超时,但 ping 是通的」。我抓包发现,SYN 每 3 秒重发一次,重发了 6 次后放弃。中间有一次 SYN+ACK 回来了,但客户端已经放弃了那个连接。原因是中间路由器有 1% 的丢包率,刚好丢的是 SYN+ACK。

避坑指南: 不要只看 ping。ping 通不代表 TCP 能通。用 tcping 或 hping3 模拟 TCP 连接测试。

2.5 网络层排查流程图

下面这张图总结了我排查网络层问题的思路。你可以照着这个顺序走,基本不会漏。

网络层排查流程图 网络层排查流程图 TLS 连接超时 步骤1:两端同时抓包 收到 SYN+ACK? 是 检查 MTU / 应用层 否 排查防火墙 / 路由 检查防火墙 ACL 检查路由 / ECMP 检查 SYN 队列 / 丢包 定位根因,修复 排查要点 • 两边同时抓包 • 对比时间戳 • 检查 ICMP 放行 • 用 tcping 测试

2.6 常用排查命令速查表

场景 命令 说明
抓包 tcpdump -i eth0 port 443 -w dump.pcap 两端同时抓,对比时间
路由跟踪 mtr --tcp --port 443 <目标IP> TCP 模式,避免 ICMP 被拦截
SYN 丢包 netstat -s | grep -i "SYN" 看 SYN 丢包计数
半连接队列 ss -lnt | grep 443 看 Recv-Q 是否堆积
MTU 探测 ping -M do -s 1472 <目标IP> DF 置位,测试最大包长
防火墙规则 iptables -L -n -v 查看包计数,判断是否命中

2.7 小结

网络层排查,说白了就是「包在哪里丢了」。抓包是最直接的手段。两边同时抓,对比时间戳,看谁没收到谁的包。

我个人习惯的排查顺序是:先看防火墙,再看路由,然后查 MTU,最后查 SYN 队列。这个顺序覆盖了 90% 的网络层问题。