# -*- coding: utf-8 -*- """验证 MTU 调优脚本和安装集成。 为什么值得单独回归: 这是这次可用率从 48% 提到 100% 的关键,而且**很容易被后人误改** —— 比如"顺手把接口 MTU 也降一点"(实测那样会掉到 50%)。 所以把关键决策钉在测试里。 """ from __future__ import annotations import re import subprocess import sys from pathlib import Path ROOT = Path(__file__).resolve().parent.parent NET_TUNE = ROOT / "relay" / "net-tune.sh" INSTALL = ROOT / "relay" / "install.sh" passed = 0 failed = 0 def check(name, ok, detail=""): global passed, failed if ok: passed += 1 print(" [PASS] " + name) else: failed += 1 print(" [FAIL] " + name + " " + str(detail)) def main() -> int: print("一、调优脚本本身") check("net-tune.sh 在", NET_TUNE.is_file()) if not NET_TUNE.is_file(): return 1 text = NET_TUNE.read_text(encoding="utf-8") # bash 语法(在 Linux 上才有 bash,本机没有就跳过) check("打开 tcp_mtu_probing", "tcp_mtu_probing=1" in text) check("夹 MSS", "--set-mss" in text) check("MSS 默认值合理(1300~1440)", bool(re.search(r"MSS_VALUE=\$\{MSS_VALUE:-(\d+)\}", text)) and 1300 <= int(re.search(r"MSS_VALUE=\$\{MSS_VALUE:-(\d+)\}", text) .group(1)) <= 1440, re.search(r"MSS_VALUE=.*", text).group(0) if "MSS_VALUE" in text else "") check("OUTPUT 链上夹", "OUTPUT" in text) check("POSTROUTING 链上夹", "POSTROUTING" in text) # ⚠ 这里原来断言的是"把接口 MTU 设回 1500", # 依据是更早的一次实测(接口 MTU 降到 1400 会掉到 50%)。 # # 那个结论后来被推翻了,原因是我们当时漏了一件事: # **MSS 钳制只管 TCP,而 QUIC 走 UDP 完全不受保护。** # 路径 MTU 实测 1472(1500 的包不通),QUIC 默认发 ~1452 的包 # 会被中间设备静默丢掉,而 UDP 丢了不回 ICMP,只能等超时。 # 把接口 MTU 压到 1400 之后,QUIC 建齐 10 条连接从 28 秒缩到 12 秒。 # # 所以现在**故意**设 1400。这个断言跟着改, # 否则它会永久假失败,还会诱导后人把真正的改进改回 1500。 check("把接口 MTU 压到 1400(QUIC 走 UDP,MSS 钳制管不到它)", "mtu 1400" in text or "MTU_VALUE" in text, "接口 MTU 要压到 1400,否则 QUIC 的大包会被静默丢弃") check("注释里说明了为什么改接口 MTU(QUIC 不受 MSS 保护)", "QUIC" in text or "UDP" in text or "MSS 钳制" in text) # 清理旧规则,避免重启后越积越多 check("会先清掉旧 MSS 规则", "-D" in text and "while" in text) print() print("二、安装脚本带上了它") check("install.sh 在", INSTALL.is_file()) install = INSTALL.read_text(encoding="utf-8") check("有 [3c] 网络调优段", "[3c]" in install) check("会拷 net-tune.sh", "net-tune.sh" in install) check("会写 sysctl 配置", "99-lanremote-mtu.conf" in install) check("会注册开机服务", "lanremote-nettune.service" in install) check("服务 enabled", "systemctl enable lanremote-nettune" in install) check("开机服务在隧道之前跑(新连接就用上新参数)", "Before=lanremote-relay.service lanremote-tunnel.service" in install) print() print("三、打包会带上这两个文件") package = (ROOT / "_build" / "package_remote.py").read_text( encoding="utf-8") check("打包脚本扫描整个 relay 目录(不是硬编码文件名)", "iterdir" in package or "rglob" in package, "") check("relay/net-tune.sh 会被打进去(同目录扫描即可)", NET_TUNE.is_file()) print() print("四、说明文档里记了实测数据") check("写了不夹 MSS 的对照(87%)", "87" in text) check("写了 MSS 各档都 100%", "100%" in text) check("写了接口 MTU 1400 更差的教训", "50%" in text) print() print("=" * 58) print(f"通过 {passed} 项,失败 {failed} 项") return 0 if failed == 0 else 1 # ⚠ 绝不能由 nettune 服务去 restart 隧道 —— 那个 unit 有 # Before=lanremote-tunnel.service,脚本在自己还没结束时 restart # 隧道会和 systemd 的顺序约束死锁(实测卡 9 分半,表现是网站挂了 # 而日志里什么都看不出来)。 # 所以 net-tune.sh 必须认 NETTUNE_SERVICE_CALL 这个开关。 tune_src = (ROOT / "relay" / "net-tune.sh").read_text(encoding="utf-8") check("net-tune.sh 认 NETTUNE_SERVICE_CALL(防死锁)", "NETTUNE_SERVICE_CALL" in tune_src, "没有这个开关 -> 服务启动时会卡死") # 两个生成 unit 的地方也要带上 for name in ("deploy.sh", "install.sh"): p = ROOT / "relay" / name if p.is_file(): body = p.read_text(encoding="utf-8") check("%s 的 nettune unit 带 NETTUNE_SERVICE_CALL" % name, "NETTUNE_SERVICE_CALL" in body, "新机器装出来还是会死锁") if __name__ == "__main__": sys.exit(main())