#6 批量测试卡死问题排查:路径 MTU 黑洞与超时机制缺陷

2026-10-20

今天生产测试机执行批量测试时发生卡死,1252 条用例全部处于等待状态,执行数为 0。进程持续运行但 CPU 占用为 0,日志停在资源清理阶段的 8 个并发 HTTP 请求,此后不再产生响应。

根因是网络路径 MTU 黑洞与程序缺少超时保护共同作用。 测试机访问土耳其区域的两个边缘 IP 时,TCP 三次握手正常,但 TLS 握手始终无法完成。抓包与 DF Ping 测试表明,该路径 MTU 约为 1483 字节,而双方仍按 1500 字节通信;中间设备静默丢弃超限报文,且未返回 ICMP 分片所需错误信息。由于服务端 TLS 响应报文丢失,连接持续重传,客户端始终收不到完整数据。

程序层面,HTTP 请求未配置 timeout_ms 时显式传入 timeout=None,关闭了 httpx 默认超时机制;同时,PLAN SETUP 钩子缺少看门狗,导致资源清理请求无限等待,整个批次无法进入用例阶段。排查未发现本地代码或网络配置变更,问题大概率来自外部链路,具体责任节点尚待云厂商确认。

修复采取网络止血与代码防御两条路径。 网络侧针对两个故障 IP 将 TCP MSS 限制为 1400,并通过 systemd 实现持久化及定时更新。修改后,两条链路的 TLS 握手分别在 0.577 秒和 0.394 秒内完成,HTTP 返回 401,证明连接已恢复。代码侧补充默认 60 秒请求超时、钩子超时看门狗、区域连通性预检,以及 PLAN HOOK 非阻塞失败机制;129 项单元测试全部通过。

截至记录时,MSS 修复已生效,代码已提交,但尚未部署到生产目录,旧批次仍未终止,新批次也尚未重跑。后续需完成部署与回归验收,并向运维部门和阿里云反馈路径 MTU 问题。

经验总结: 网络故障不可避免,但请求必须有超时、任务必须有隔离与降级机制,前置检查应尽早暴露区域故障,避免单个依赖异常拖垮整批任务。

技术原理:PMTU 黑洞与 TCP MSS 钳制

PMTU(Path Maximum Transmission Unit,路径最大传输单元) 是网络路径上能够传输的最大 IP 报文长度。以太网常见 MTU 为 1500 字节,但跨境网络、隧道或中间设备可能将实际路径 MTU 降低至 1483 字节。如果发送端仍按 1500 字节发送报文,而中间设备静默丢弃超限报文,又未返回 ICMP“需要分片”消息,双方就无法正确调整报文大小,形成 PMTU 黑洞。

TCP 的 MSS(Maximum Segment Size,最大报文段长度)用于限制单个 TCP 段的数据载荷,不包含 IP 和 TCP 头部。IPv4 无选项时,两者通常合计占用 40 字节。因此,MSS 设为 1400 时,单个 IP 报文通常不超过 1440 字节,低于本次实测的 1483 字节路径 MTU,可以避开超限丢包。

# 检查规则是否存在;如果不存在,则追加规则。
iptables -t mangle -C OUTPUT -p tcp --syn -d 100.100.100.100 -j TCPMSS --set-mss 1400 2>/dev/null || \
iptables -t mangle -A OUTPUT -p tcp --syn -d 100.100.100.100 -j TCPMSS --set-mss 1400

Linux 使用 iptables 的 TCPMSS --set-mss 1400 修改本机发出的 SYN 报文中的 MSS 通告值,使对端在后续发送数据时采用更小的 TCP 段。该规则只影响新建连接,不会修复已经建立的卡死连接。需要注意,MSS 是 TCP 载荷上限,并非绝对报文长度保证;TCP 选项、封装开销等因素仍需考虑。

本次实测中,修复前 TCP 连接成功但 TLS 握手超时;钳制 MSS 后,两个故障 IP 均返回 HTTP 401,且 TLS 握手恢复,证明网络连接已恢复。401 表示请求到达应用层但缺少有效鉴权,并不代表业务请求成功。

#4 办公网络中的安全问题

2022-04-14

同事发来消息,说是我的电脑经过安全扫描,判断有风险。提了以下几条改进意见:

  1. SMB 共享,风险高,希望限制访问,并强制执行消息签名。
    改进:其实现在我很少使用 SMB 共享了,直接关闭了事。
    PS: 其实在用 SMB 共享,所以重新开启 smbd 并禁止匿名访问。

  2. 开启了 IP 转发,风险中,建议关闭。
    IP 转发功能对我的工作是非常必要的,不能关闭
    之前的文章, 2021/05/09, VPN 与 NAT 有讲过这个问题。
    改进:调整 iptables 规则,only ACCEPT 指定链路 FORWARD

  3. 3Proxy HTTP 代理(端口 10809)只做透明请求远程溢出,风险高,建议升级 3Proxy 版本。
    我没有安装什么 3Proxy, 这个端口是其他 HTTP 代理服务的端口(不排除底层使用了 3Proxy)
    改进:我直接将其改成监听 127.0.0.1 了事

#3 VPN 与 NAT

2021-05-09

我公司办公环境需要用到 OpenVPN,之前允许一个账号在两处登录,现在处于安全考虑只允许一处登录了。
然后,我就不方便了,因为我办公环境有两台电脑(台式机 Windows,笔记本 Ubuntu)都需要接入 VPN。

#2 iptables (2): 基本操作

2021-04-04

命令参数

iptables -[ACD] chain rule-specification [options]
iptables -I chain [rulenum] rule-specification [options]
iptables -R chain rulenum rule-specification [options]
iptables -D chain rulenum [options]
iptables -[LS] [chain [rulenum]] [options]
iptables -[FZ] [chain] [options]
iptables -[NX] chain
iptables -E old-chain-name new-chain-name
iptables -P chain target [options]

链相关

  • --new -N [chain] 链:创建
  • --delete-chain -X [chain] 链:删除
  • --rename-chain -E old-chain new-chain 链:更名
  • --list -L [chain [rulenum]] 列出指定链或所有链中的所有规则(表格形式)
  • --list-rules -S [chain [rulenum]] 同上,不过是按照规则定义的格式列出 很好用
  • --flush -F [chain] 清空规则
  • --zero -Z [chain [rulenum]] 计数器清零(数据包计数器,流量计数器)
  • --policy -P chain target 修改策略

规则相关

  • --append -A chain rule-specification 附加规则
  • --check -C chain rule-specification 检查规则是否存在
  • --delete -D chain rule-specification 删除匹配规则
  • --delete -D chain rulenum 删除 指定序号的 规则
  • --insert -I chain [rulenum] 插入到指定位置(默认插入到第一个)
  • --replace -R chain rulenum 替换 指定序号的 规则

规则说明

常用命令

查看

sudo iptables -S
sudo iptables -t nat -S POSTROUTING

sudo iptables -nL
sudo iptables -nL INPUT

sudo iptables -nL --line-numbers

sudo iptables -Z
sudo iptables -Z INPUT
sudo iptables -Z INPUT 1

# 如果是这么定义的话:
#             -A INPUT -m conntrack --ctstate INVALID -j DROP
sudo iptables -D INPUT -m conntrack --ctstate INVALID -j DROP
sudo iptables -D INPUT 3

访问拦截

# 对指定端口放行
sudo iptables -I INPUT -p tcp --dport 1022 -j ACCEPT

# 禁止来自无线网络的流量访问某端口(突发奇想的一个小例子)
sudo iptables -A PREROUTINE -i wlp6s0 --dport 22 -j DROP

NAT

iptables -t nat -A POSTROUTING -d 192.168.0.102 -j SNAT --to 192.168.0.1
iptables -t nat -A PREROUTING -d 202.202.202.2 -j DNAT --to-destination 192.168.0.102
iptables -t nat -D PREROUTING -p tcp --dport 8080 -i eth2.2 -j REDIRECT --to 80

对指定流量打标记

内核可以给这个包加上一个标记(可能存在包的数据结构中,总之,只对本地环境有效),可以实现流量的统计、限制等其他复杂的控制。

标记值最大可以到 2^32

iptables -t mangle -A PREROUTING -s 192.168.1.3 -j MARK --set-mark 60
iptables -t mangle -A PREROUTING -p tcp --dport 22 -j MARK --set-mark 2

//打标记
iptables -t mangle -A PREROUTING -j MARK --set-mark 33
//匹配标记
iptables -t nat -A PREROUTING -m mark --mark 33  -j ACCEPT
//or-mark
iptables -t mangle -A PREROUTING -j MARK --or-mark 0x400
//掩码匹配
iptables -t nat -A PREROUTING -m mark --mark 0x400/0x400  -j ACCEPT

iptables -t mangle -A INPUT -m state --state NEW -j MARK --set-mark 1
iptables -t mangle -A INPUT -j CONNMARK --save-mark
iptables -t mangle -A INPUT -j CONNMARK --restore-mark

iptables -t mangle -A PREROUTING -mttl --ttl-eq 64 -j MARK --set-mark 10
iptables -t mangle -A PREROUTING -mttl --ttl-eq 123 -j MARK --set-mark 20
iptables -t filter -A FORWARD -m mark--mark 10 -j ACCEPT
iptables -t filter -A FORWARD -m mark--mark 20 -j DROP

iptables -t mangle -A QOS_MARK_FORWARD_eth1 -j CONNMARK --restore-mark --nfmask 0xfffff --ctmask 0xfffff
iptables -t mangle -A QOS_MARK_FORWARD_eth1 -m mark --mark 0x0/0xfffff -j QOS_RULES_FORWARD_eth1
iptables -t mangle -A QOS_RULES_FORWARD_eth1 -j CONNMARK --save-mark --nfmask 0xfffff --ctmask 0xfffff

iptables -t mangle -A POSTROUTING -m iprange --src-range 192.168.0.2-192.168.0.200 -j MARK --or-mark 0x1

-m mark
-m connmark
-j MARK
-j CONNMARK
-j CONNSECMARK
-j SECMARK

  • --set-mark value 设置nfmark值
  • --and-mark value nfmark与value与运算
  • --or-mark value nfmark与value或运算

匹配

  • [!] --mark value[/mask]
iptables -t mangle -A INPUT -m mark --mark 1

限速

思路:hashlimit 模块

屏蔽指定网站

利用 string 模块 (xt_string.ko) 做域名匹配:

sudo iptables -A OUTPUT -m string --string baidu.com --algo bm -j LOG --log-prefix "iptables-test:blocked:baidu.com: "
sudo iptables -A OUTPUT -m string --string baidu.com --algo bm -j DROP
sudo iptables -vnL
curl https://www.baidu.com/
tail -f /var/log/syslog | grep "iptables-test"
sudo iptables -F OUTPUT

sudo iptables -A FORWARD -m string --string baidu.com --algo bm -j LOG --log-prefix "iptables-test:blocked:baidu.com: "
sudo iptables -A FORWARD -m string --string baidu.com --algo bm -j DROP

HTTPS 居然也可以生效,其原理我还不清楚,到底是匹配到了包的哪一部分包含了 baidu.com 呢?

提示:

  1. 这个规则也可以放到 INPUT 链上。
  2. 如果加上 -p tcp –dport 80 可能更加精确,免得别的什么包里面包含了 baidu.com 被拦截。
  3. 可以加多个 string 参数。

如果是 HTTP 的话,在我的理解范围之内,可以做到更细致的匹配,比如匹配到路径,甚至 Cookie。

string match options:
--from                       Offset to start searching from
--to                         Offset to stop searching
--algo                       Algorithm
--icase                      Ignore case (default: 0)
[!] --string string          Match a string in a packet
[!] --hex-string string      Match a hex string in a packet

algo 的选项:bm, kmp,参考 man iptables-extensions。

参考资料与拓展阅读

#1 iptables (1): 基础知识

2021-04-03

iptables 的历史变更,发展趋势,和其他技术的关联我也曾花了不少时间去了解,可以说还是很不全面,就不献丑了(或者以后再来说说)。

这里就单单讲一下 iptables。

基本思想

在网络数据传输过程中有几个关键点埋上钩子,每个钩子上绑定了一个规则链(CHAIN)。
我们在这些链上注册一些处理规则(RULE),也就是 条件 加 操作。
网络包走到埋点的地方时,内核会逐个规则检查,如果符合条件就会执行预定操作。

此外:

  1. 这些处理规则按照不同类型,分成了五类,存在五张不同的表中。
  2. 每个规则链可以设置一个预定的操作(文档中称之为:策略 Policy)。

五个点

  • PREROUTING 路由:内核收到网络包之后,判断该包是否是自己的,应该给哪个程序
  • INPUT 用户程序收到包之前(内核判断是自己的包之后,传输到用户程序)
  • OUTPUT 程序往外发包
  • FORWARD 这个包不是自己的,然后需要中转出去
  • POSTROUTING 路由:系统判断该包应该怎么发出

PS:可以自定义新的链,然后再别的链中引用。我所处理的任务都没有到需要创建新链的地步,所以没有接触过。

五张表

应该说是五个功能。

  • filter 过滤,即判断这个包。默认
  • nat 地址转换
  • mangle 数据包修改,日常网络管理应该不会用到
  • raw 数据包修改(在内核处理之前)
  • security unknown

操作类型

  • ACCEPT 放行
  • DROP 拦截
  • REJECT 拦截,但是告知对方
  • MARK 加标记

NAT

  • SNAT 源地址转换
  • DNAT 目的地址转换
  • REDIRECT 端口转换

参考资料与拓展阅读