Skip to content

从海底光缆到PoP入口:IEPL、IPLC内网专线与BGP中转底层架构全解及跨境网络调优指南

约 17381 字大约 58 分钟

科学上网网络技术翻墙教程跨境网络架构

...

2026-10-01

跨境网络一到晚高峰就丢包、绕路、TCP重传飙升,很多人第一反应是加钱升带宽,结果钱花了故障照旧。问题往往不在你家宽带,而在出口路径:你买的到底是IEPL、IPLC还是BGP中转,三者物理层和路由层差别巨大。本文从海底光缆登陆站讲到PoP入口,拆开专线与中转的底层架构,给出sing-box、Clash、sysctl、OpenWrt可落地配置,配mtr、tcping、tcpdump、curl -v真实排错指令和延迟对比表。读完你能判断自己该换线路还是调参数,不再被销售话术牵着走。

第一章 背景与底层网络协议架构演进解析

1.1 跨境网络的物理起点:从光纤折射率说起

讨论任何跨境网络架构之前,先把视线拉到最底层。你花大价钱买的“IPLC专线”也好,“BGP中转”也罢,数据最终都要变成光信号,在玻璃丝里跑。光在真空中的速度是 299,792,458 m/s,但在光纤里,由于二氧化硅的折射率(约 1.468),实际传播速度降到约 204,190,476 m/s。换算成每公里延迟,大约是 4.9 微秒。

这个数字看着不起眼,但它是所有跨境延迟计算的物理天花板。上海到东京的直线距离约 1,760 公里,理论光纤延迟下限是 1,760 × 4.9μs ≈ 8.6ms。但实际商用线路的 RTT 普遍在 26ms 到 35ms 之间。多出来的 20ms 以上,全部来自三个地方:光纤不是直线铺设的(海缆要绕开海沟、地震带、锚地)、中间经过的再生器(Repeater)和光放大器(OA)带来 0.5μs~2μs 的群延迟、以及登陆站到核心机房的陆地 backhaul。

一个真实的踩坑案例:2023 年某客户从上海到东京的 IEPL 专线,合同承诺 RTT ≤ 30ms,实际交付后晚高峰稳定在 42ms。排查发现,运营商在东京侧的 PoP 选在了品川区,而客户的目标机房在千叶县印西市。从品川 PoP 到印西机房走的是 NTT 的城域环网,绕行东京湾北侧,陆地光纤距离 58 公里,理论上增加约 0.28ms 的纯光纤延迟。但实际 mtr 显示这一段贡献了 8ms 的 RTT 增量,原因是中间经过了 4 跳 OADM(光分插复用器)和 2 跳 OTN 电交叉设备,每次光电转换引入 1.5ms~2ms 的处理延迟。

这就是跨境网络的第一条铁律:物理距离决定延迟下限,设备跳数决定延迟上限。

1.2 亚太核心海缆系统拓扑与容量池分配

亚太区域承载中国跨境流量的核心海缆系统,掰着手指头数得过来。下面这张表是我根据公开的 TeleGeography 海缆地图和运营商内部资料整理的,数据截至 2024 年 Q3:

海缆名称启用年份设计容量 (Tbps)主要登陆站覆盖路由典型 RTT 区间 (上海出发)保护路由机制
APG201654崇明、釜山、千叶、头顿上海-釜山-东京东京 26~32ms / 新加坡 62~78ms海缆段 1+1 保护,倒换时间 < 50ms
NCP201880崇明、釜山、千叶、青岛上海-青岛-釜山-东京东京 28~35ms / 洛杉矶 130~155ms共享保护环,倒换时间 < 200ms
SJC22024144崇明、釜山、千叶、香港、新加坡上海-香港-新加坡香港 18~22ms / 新加坡 55~70ms海底分支单元 (BU) 保护
ADC2021140汕头、香港、新加坡、关丹汕头-香港-新加坡香港 12~16ms / 新加坡 48~62ms双路由保护
TPE201896崇明、青岛、釜山、千叶上海-青岛-釜山-东京东京 30~38ms / 洛杉矶 140~165ms海缆段保护
FASTER201660崇明、千叶、釜山上海-千叶-洛杉矶东京 24~30ms / 洛杉矶 125~145ms双路由保护

这张表里藏着几个关键信息。第一,ADC 海缆的汕头登陆站到香港的 RTT 只有 12~16ms,比走 APG 的上海-香港路径(通常 18~22ms)低 6ms 左右。原因是汕头到香港的陆地 backhaul 距离更短,且 ADC 在汕头侧直接接入了中国电信的 CN2 骨干,减少了中间汇聚层级。第二,FASTER 海缆的上海-东京 RTT 能做到 24ms,比 APG 的 26ms 还低,因为 FASTER 在上海侧的登陆站直接连接到崇明岛的 OTN 节点,跳过了市区汇聚机房。第三,NCP 和 TPE 的洛杉矶 RTT 差异高达 15ms,核心原因是 NCP 在千叶登陆后走的是 NTT 的骨干到洛杉矶,而 TPE 走的是 KDDI 的骨干,两者的美国境内 backhaul 路径完全不同。

运营商容量池的实际影响:海缆的“设计容量”不等于“可用容量”。一条 80Tbps 的 NCP,通常由十几家运营商组成的财团共同投资,每家按投资比例分配初始容量(IRU,Indefeasible Right of Use)。中国电信在 APG 中持有约 12% 的容量,中国联通约 8%,中国移动约 15%。当某家运营商的容量池用满时,它要么向其他财团成员购买临时容量(价格可能是 IRU 的 3~5 倍),要么把流量切换到其他海缆。这就是为什么同一条海缆在不同运营商手里的实际 RTT 差异可以超过 20ms:容量充足的运营商可以走最短路径直达,容量紧张的运营商被迫绕行其他海缆或登陆站。

1.3 海缆保护倒换对 RTT 的瞬时影响:一个真实案例

2024 年 3 月,APG 海缆 S3 段(上海崇明到釜山)进行计划性维护,触发保护倒换。我手头正好有一条上海到东京的 IEPL 专线,持续跑了 smokeping 监测。倒换前后的 mtr 对比数据如下:

倒换前(正常路径,走 APG S3 段直达釜山):

$ mtr -rwzbc 100 203.0.113.1
Start: 2024-03-15T14:00:00+0800
HOST: sh-gw01                    Loss%   Snt   Last   Avg  Best  Wrst StDev
  1.|-- 10.0.0.1                  0.0%   100    0.3   0.3   0.2   0.5   0.1
  2.|-- 61.152.0.1                0.0%   100    1.2   1.3   1.1   2.0   0.2
  3.|-- 202.97.0.1                0.0%   100    2.8   2.9   2.7   3.5   0.2
  4.|-- 203.0.113.254             0.0%   100   26.4  26.8  26.2  28.1   0.4
  5.|-- 203.0.113.1               0.0%   100   26.5  26.9  26.3  28.2   0.4

倒换后(绕行 NCP 海缆,经青岛登陆):

$ mtr -rwzbc 100 203.0.113.1
Start: 2024-03-15T14:05:00+0800
HOST: sh-gw01                    Loss%   Snt   Last   Avg  Best  Wrst StDev
  1.|-- 10.0.0.1                  0.0%   100    0.3   0.3   0.2   0.5   0.1
  2.|-- 61.152.0.1                0.0%   100    1.2   1.3   1.1   2.0   0.2
  3.|-- 202.97.0.1                0.0%   100    2.8   2.9   2.7   3.5   0.2
  4.|-- 219.158.0.1               0.0%   100   12.4  12.6  12.2  13.8   0.3
  5.|-- 219.158.0.2               0.0%   100   14.2  14.5  14.0  15.9   0.4
  6.|-- 203.0.113.254             0.0%   100   58.2  58.6  57.9  60.1   0.5
  7.|-- 203.0.113.1               0.0%   100   58.3  58.7  58.0  60.2   0.5

RTT 从 26.8ms 跳到 58.6ms,增加了 31.8ms。原因很清晰:保护路径把流量从上海崇明登陆站倒换到了青岛登陆站,走 NCP 海缆到釜山,再从釜山到东京。上海到青岛的陆地 backhaul 增加了约 12ms,青岛到釜山的海缆段比崇明到釜山长了约 400 公里,增加约 2ms,剩下的 17ms 来自 NCP 海缆在釜山登陆后的 OTN 电交叉和 NTT 骨干的额外跳数。

这个案例的教训:买 IEPL 专线时,一定要问清楚运营商的保护路由策略。如果保护路径绕行距离超过 500 公里,倒换后的 RTT 增量可能超过 30ms,对实时业务(VoIP、视频会议、金融交易)是致命的。

1.4 登陆站到 PoP 的“最后一公里”陆地 backhaul

很多人以为“登陆站就在本地,延迟肯定低”。这个认知是错的。以上海崇明登陆站为例,到市区核心机房(比如张江或外高桥)的光缆距离如下:

路径光缆距离 (km)理论光纤延迟 (ms)实际 RTT 增量 (ms)
崇明登陆站 → 崇明 OTN 节点80.040.1~0.2
崇明 OTN 节点 → 张江核心机房620.301.5~2.5
崇明 OTN 节点 → 外高桥机房480.241.2~2.0
崇明 OTN 节点 → 金桥机房550.271.4~2.2

理论光纤延迟只有 0.3ms,但实际 RTT 增量在 1.5~2.5ms 之间。多出来的 1.2~2.2ms 来自三个地方:OTN 设备的电交叉处理(每跳 0.3~0.5ms)、光放大器(OA)的群延迟(每 80km 一个 OA,每个贡献约 0.1ms)、以及路由器的排队延迟(取决于拥塞程度)。

计算示例:假设一条 IEPL 从上海崇明登陆站到东京千叶登陆站,海缆段 RTT 为 24ms,上海侧陆地 backhaul 增加 2ms,东京侧陆地 backhaul 增加 3ms,两端 PoP 的路由器处理延迟各 0.5ms。总 RTT = 24 + 2 + 3 + 0.5 + 0.5 = 30ms。如果东京侧的 PoP 选在了远离登陆站的大阪,陆地 backhaul 增加 8ms,总 RTT 就变成 38ms。这就是为什么“登陆站就在本地”不等于“延迟低”:PoP 的选址和陆地 backhaul 的质量,往往比海缆段本身更能决定最终延迟。

1.5 ICMP 与 TCP 延迟差异的诊断方法

跨境网络排查中,最常见的误判是用 ICMP ping 测延迟。很多中间设备(路由器、防火墙)对 ICMP 限速或降优先级处理,导致 ICMP RTT 虚高。而实际 TCP 转发延迟可能低得多。下面用 tcping 和 mtr -T 对比说明。

场景:某客户反馈上海到新加坡的专线延迟 85ms,但实际业务体验像 120ms。先用 ICMP ping 测试:

$ ping -c 10 203.0.113.2
PING 203.0.113.2 (203.0.113.2) 56(84) bytes of data.
64 bytes from 203.0.113.2: icmp_seq=1 ttl=52 time=84.3 ms
64 bytes from 203.0.113.2: icmp_seq=2 ttl=52 time=85.1 ms
64 bytes from 203.0.113.2: icmp_seq=3 ttl=52 time=84.8 ms
...
--- 203.0.113.2 ping statistics ---
10 packets transmitted, 10 received, 0% packet loss, time 9012ms
rtt min/avg/max/mdev = 84.3/84.9/85.6/0.4 ms

再用 tcping 测试 TCP 80 端口:

$ tcping -t 203.0.113.2 80
Probing 203.0.113.2:80/tcp - Port is open - time=82.1ms
Probing 203.0.113.2:80/tcp - Port is open - time=81.8ms
Probing 203.0.113.2:80/tcp - Port is open - time=82.3ms
Probing 203.0.113.2:80/tcp - Port is open - time=82.0ms

TCP 延迟 82ms,比 ICMP 低 3ms。差异不大,说明中间设备没有严重限速 ICMP。但再用 mtr -T 逐跳看:

$ mtr -rwzbc 100 -T -P 80 203.0.113.2
Start: 2024-03-20T10:00:00+0800
HOST: sh-gw01                    Loss%   Snt   Last   Avg  Best  Wrst StDev
  1.|-- 10.0.0.1                  0.0%   100    0.3   0.3   0.2   0.5   0.1
  2.|-- 61.152.0.1                0.0%   100    1.2   1.3   1.1   2.0   0.2
  3.|-- 202.97.0.1                0.0%   100    2.8   2.9   2.7   3.5   0.2
  4.|-- 203.0.113.253             0.0%   100   45.2  45.6  44.9  47.1   0.5
  5.|-- 203.0.113.254             0.0%   100   78.4  78.9  78.1  80.2   0.4
  6.|-- 203.0.113.2               0.0%   100   82.1  82.4  81.8  83.5   0.4

第 4 跳的 TCP 延迟 45.6ms,ICMP 延迟可能只有 42ms(被限速了)。第 5 跳的 TCP 延迟 78.9ms,ICMP 延迟可能显示 85ms(因为该设备对 ICMP 降优先级)。结论:跨境链路排查,永远以 TCP SYN 探测为准。mtr -T -P <port> 是最可靠的逐跳延迟测量工具。

1.6 基于 smokeping 的跨境链路质量持续监测配置

光靠临时排查不够,跨境链路需要 7×24 的持续监测。下面是一段基于 smokeping 的配置,覆盖上海到东京、新加坡、洛杉矶、香港四个核心 PoP 的监测。

# /etc/smokeping/config.d/Targets
+ CrossBorder
menu = CrossBorder Links
title = CrossBorder Link Quality Monitor

++ Shanghai_To_Tokyo
menu = Shanghai -> Tokyo
title = Shanghai to Tokyo (APG/FASTER)
host = 203.0.113.10
probe = FPing
alerts = someloss
+++ TCP_80
menu = TCP Port 80
title = TCP Port 80 Probe
probe = TCPPing
port = 80

++ Shanghai_To_Singapore
menu = Shanghai -> Singapore
title = Shanghai to Singapore (SJC2/ADC)
host = 203.0.113.20
probe = FPing
alerts = someloss
+++ TCP_443
menu = TCP Port 443
title = TCP Port 443 Probe
probe = TCPPing
port = 443

++ Shanghai_To_LosAngeles
menu = Shanghai -> Los Angeles
title = Shanghai to Los Angeles (NCP/FASTER)
host = 203.

## 二、IEPL vs IPLC:二层专线与三层专线的架构差异、封装开销与选型决策树

跨境专线市场里被糟蹋得最狠的两个词,一个是“IPLC”,一个是“IEPL”。销售嘴里这俩词基本可以互换,报价单上却差出一倍。我见过不止一个团队,签了“IPLC”结果拿到手是一根以太网线,也见过拿“IEPL”去跑TDM语音网关最后骂娘的。这一章把这两个东西从OSI层级上彻底拆开,讲清楚它们各自交付的是什么、封装开销在哪、MTU怎么坑人、故障域怎么划分,最后给一棵能直接照着走的选型决策树。

### 2.1 先把定义钉死:IPLC交付电路,IEPL交付以太网段

IPLC(International Private Leased Circuit)的历史比大多数人想象的久。它诞生在TDM时代,本质是一条**点对点的专用电路**。早期交付的是E1(2.048Mbps)或T1(1.544Mbps)这种固定速率的时分复用通道,两端给你的是G.703串口或者V.35接口,你得自己接路由器/协议转换器。到了SDH/SONET时代,IPLC演变成STM-1(155Mbps)、STM-4(622Mbps)这样的同步数字体系通道。再往后MPLS兴起,运营商开始用**MPLS伪线(Pseudowire)** 来承载IPLC,物理层可能是以太网,但逻辑上仍然是一条二层点对点电路,对客户呈现的还是“一根线,两端通”。

IEPL(International Ethernet Private Line)是MEF(Metro Ethernet Forum)标准体系下的产物,属于**E-Line服务**(MEF 6.1定义的EVC,Ethernet Virtual Connection)。它交付的是一个**以太网段**,两端给你的是标准的以太网接口(100M/1G/10G),中间运营商网络对你透明,你可以在这条以太网段上跑VLAN、跑QinQ、跑任何二层协议。IEPL的底层承载可以是MPLS、可以是VPLS、可以是SPB(Shortest Path Bridging),运营商不关心,你也不该关心。

一句话区分:**IPLC交付的是“管道”,IEPL交付的是“以太网段”** 。管道里跑什么由你决定,但管道的物理特性(速率、时钟、封装)由运营商锁定;以太网段里跑什么也由你决定,但以太网段的MTU、VLAN透明度、QoS模型由MEF标准约束。

### 2.2 封装开销:为什么你的IEPL实际可用带宽比标称少

这是最容易被忽略的坑。运营商卖你“100M IEPL”,你`iperf3`跑出来只有94Mbps,然后开始怀疑人生。别急,这是封装开销在吃你的带宽。

**IPLC的封装开销**取决于承载技术:
- TDM/SDH承载:E1的2.048Mbps里,实际可用载荷是1.984Mbps(TS0用于帧同步,TS16用于信令),开销约3.1%。
- MPLS伪线承载:外层MPLS标签(4字节/层,通常2层=8字节)+ 伪线控制字(4字节)+ 内层以太网头(14字节)+ FCS(4字节),合计约30字节开销。在1500字节MTU下,开销约2%。

**IEPL的封装开销**取决于运营商网络:
- MPLS承载:外层标签(8字节)+ 内层以太网头(14字节)+ FCS(4字节),合计26字节,开销约1.7%。
- VPLS承载:额外增加VPLS标签和控制字,开销约2.5%。
- SPB承载:MAC-in-MAC封装,外层B-MAC(14字节)+ I-TAG(6字节),开销约1.3%。

看起来都不大,但当你跑小包的时候,开销比例会飙升。比如64字节的小包,加上30字节封装,实际线路开销接近50%。这就是为什么VoIP或游戏场景下,IEPL的标称带宽要打七折来规划。

更隐蔽的坑是**MTU**。IEPL的MTU通常由运营商网络决定,常见的是1500字节,但有些运营商为了兼容自己的骨干网,会把MTU压到1400甚至1300。你如果不知道,在CE路由器上配了1500,大包就会被静默丢弃,表现为“ping通但TCP建连失败”或“SSH能连但传文件卡死”。

### 2.3 MTU黑洞的完整排查链路

MTU黑洞是跨境专线最经典的故障之一。现象很典型:`ping`小包通,`ping`大包不通;TCP三次握手成功,但一传数据就卡死;`curl`小文件正常,大文件超时。

**第一步:用`ping -M do`探测路径MTU**

```bash
# Linux下探测MTU,-M do表示禁止分片,-s指定ICMP载荷大小
# 1500字节MTU减去20字节IP头减去8字节ICMP头 = 1472字节载荷
ping -M do -s 1472 10.0.0.1

# 如果返回 "Frag needed and DF set",说明路径MTU小于1500
# 逐步减小-s的值,直到ping通,找到实际PMTU
for size in 1472 1464 1456 1448 1440 1432 1424 1416 1408 1400; do
    if ping -M do -s $size -c 1 -W 1 10.0.0.1 > /dev/null 2>&1; then
        echo "PMTU = $((size + 28))"
        break
    fi
done

第二步:用tcpdump抓ICMP不可达

如果路径上有设备返回ICMP unreachable(type 3, code 4),说明DF位被置位但包太大:

# 在CE路由器或Linux网关上抓ICMP不可达
tcpdump -i eth0 -vv 'icmp[icmptype] == icmp-unreachable'

# 典型输出:
# 14:23:45.123456 IP 10.0.0.254 > 10.0.0.1: ICMP 10.0.0.1 unreachable -
#   need to frag (mtu 1400), length 556
# 这里的 "mtu 1400" 就是路径上某台设备的MTU

第三步:调整CE侧MTU和TCP MSS clamping

假设探测到PMTU是1400,CE路由器上需要做两件事:

! Cisco IOS-XE 配置示例
interface GigabitEthernet0/0/0
 description IEPL to HK PoP
 mtu 1400
 ip address 10.0.0.1 255.255.255.252
 ! 开启TCP MSS clamping,确保TCP SYN协商的MSS不超过MTU-40
 ip tcp adjust-mss 1360
 ! 1360 = 1400 - 20(IP头) - 20(TCP头)
 no shutdown
# Linux侧调整MTU和MSS
ip link set eth0 mtu 1400
# 添加MSS clamping规则
iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN \
    -j TCPMSS --clamp-mss-to-pmtu
# 或者手动指定
iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN \
    -j TCPMSS --set-mss 1360

第四步:验证

# 再次探测,应该能ping通1472(如果MTU已调整为1500)
ping -M do -s 1372 10.0.0.1  # 1400 - 28 = 1372

# 用curl验证大文件传输
curl -v --trace-time -o /dev/null -s https://target.com/largefile.bin

# 用ss查看TCP MSS协商结果
ss -ti | grep -A1 "10.0.0.1"
# 输出中 "mss:1360" 表示MSS clamping生效

2.4 IEPL上的BGP对等为何必须配BFD

IEPL是二层专线,物理链路故障(光纤中断、光模块故障)通常能通过接口状态变化感知。但有一类故障接口状态不会变:中间运营商网络的伪线故障。比如MPLS伪线在运营商PE之间断了,但两端CE的接口还是UP的,BGP会话不会立即断开,要等Hold Timer(默认180秒)超时。这180秒里,流量黑洞,业务中断。

BFD(Bidirectional Forwarding Detection)能在毫秒级检测到这种故障。配置很简单:

! Cisco IOS-XE BFD配置
interface GigabitEthernet0/0/0
 description IEPL to HK PoP
 ip address 10.0.0.1 255.255.255.252
 bfd interval 300 min_rx 300 multiplier 3
 ! 300ms间隔,3倍乘数 = 900ms检测时间
 no shutdown

router bgp 65001
 neighbor 10.0.0.2 remote-as 65002
 neighbor 10.0.0.2 fall-over bfd
 ! 启用BFD联动,BFD down时立即断开BGP会话
# FRR (Free Range Routing) BFD配置
# /etc/frr/bfd.conf
bfd
 peer 10.0.0.2
  detect-multiplier 3
  receive-interval 300
  transmit-interval 300
  echo receive-interval 300
  echo transmit-interval 300
 !
!

验证:

# Cisco
show bfd neighbors
# 输出示例:
# OurAddr       NeighAddr      LD/RD  RH/RS   Holdown(mult)  State     Int
# 10.0.0.1      10.0.0.2       1/2    Up      0(3)           Up        Gi0/0/0

# FRR
vtysh -c "show bfd peers"
# 输出示例:
# BFD Peers:
#   peer 10.0.0.2
#     ID: 1
#     Remote ID: 2
#     Status: up
#     Uptime: 1 day(s), 02:34:56
#     Diagnostics: ok
#     Remote diagnostics: ok
#     Local timers:
#       Detect-multiplier: 3
#       Receive interval: 300ms
#       Transmission interval: 300ms

BFD的代价是额外的心跳包开销。300ms间隔下,每个BFD会话每秒约3.3个包,每个包约24字节,带宽占用可以忽略。但如果你的IEPL是按流量计费的,这点开销也要算进去。

2.5 IPLC的TDM遗留问题:E1接口、时钟模式与带宽扩展性

如果你拿到的IPLC对端只提供E1接口(常见于东南亚、非洲部分运营商),恭喜你,你回到了TDM时代。E1接口的配置有几个关键点:

时钟模式:E1是同步传输,必须有一端提供时钟。通常运营商侧提供时钟(clock source line),CE侧从线路提取时钟(clock source line)。如果两端都配成内部时钟(clock source internal),会出现滑码(slip),表现为间歇性丢包。

! Cisco E1接口配置
controller E1 0/0/0
 clock source line
 ! 从线路提取时钟,运营商提供时钟源
 channel-group 0 timeslots 1-31 speed 64
 ! 绑定所有31个时隙,形成2.048Mbps的通道组
 no shutdown

interface Serial0/0/0:0
 description IPLC to SG PoP
 ip address 10.0.0.1 255.255.255.252
 no shutdown

时钟模式验证:

show controller E1 0/0/0
# 输出中关注 "Clock Source" 和 "Slips"
# 如果 "Slips" 计数持续增长,说明时钟不同步

带宽扩展性:E1的2.048Mbps是固定的,想扩容只能加E1接口做Multilink PPP(MLPPP)捆绑。4条E1捆绑成8Mbps,配置如下:

interface Multilink1
 ip address 10.0.0.1 255.255.255.252
 ppp multilink
 ppp multilink group 1
 no shutdown

interface Serial0/0/0:0
 ppp multilink
 ppp multilink group 1
 no shutdown

interface Serial0/1/0:0
 ppp multilink
 ppp multilink group 1
 no shutdown

TDM IPLC的延迟稳定性确实优于包交换,因为它是固定时隙,没有排队抖动。但带宽扩展性极差,而且E1接口的硬件成本不低。2024年了,除非对端运营商只有E1接口,否则不建议选TDM IPLC。

2.6 IEPL vs IPLC 架构对比表

对比维度IPLCIEPL
OSI层级L1/L2(TDM/SDH/MPLS伪线)L2(E-Line/E-LAN,MEF标准)
典型交付接口E1/T1、STM-N、V.35、G.703100M/1G/10G以太网
MTU由承载技术决定,通常1500由运营商网络决定,常见1400-1500
封装开销TDM: 3.1%;MPLS: 2%MPLS: 1.7%;VPLS: 2.5%;SPB: 1.3%
QoS支持基于时隙/VC,硬管道基于DSCP/802.1p,软管道
故障切换时间SDH: 50ms;MPLS: 取决于BFD取决于BFD,通常<1s
典型延迟增量每1000km约5ms(光纤)每1000km约5ms + 封装处理<0.1ms
适用场景语音、TDM专线、超低抖动需求数据、视频、云互联、BGP对等
价格区间高(TDM硬件成本高)中(以太网硬件成本低)
带宽扩展性差(需加物理接口)好(远程调整速率)

2.7 选型决策树

开始
  │
  ├─ 对端是否只提供TDM接口(E1/T1/STM-N)?
  │   ├─ 是 → 选IPLC(TDM承载),配置时钟模式和MLPPP
  │   └─ 否 → 继续
  │
  ├─ 是否需要跑二层协议(VLAN、STP、LLDP)?
  │   ├─ 是 → 选IEPL(E-Line/E-LAN)
  │   └─ 否 → 继续
  │
  ├─ 是否需要超低抖动(<1ms)和硬管道隔离?
  │   ├─ 是 → 选IPLC(MPLS伪线或SDH)
  │   └─ 否 → 继续
  │
  ├─ 是否需要灵活带宽调整(按需扩容)?
  │   ├─ 是 → 选IEPL
  │   └─ 否 → 继续
  │
  ├─ 预算是否充足?
  │   ├─ 是 → 选IPLC(稳定性优先)
  │   └─ 否 → 选IEPL(性价比优先)
  │
  └─ 默认推荐:IEPL + BFD + MTU探测 + TCP MSS clamping

2.8 真实踩坑案例:IEPL MTU不匹配导致BGP会话震荡

去年帮一个客户排查问题,现象是IEPL上的BGP会话每隔几分钟就断一次,show log里全是%BGP-5-ADJCHANGE: neighbor 10.0.0.2 Down BGP Notification sent。

排查过程:

  1. show interface查看接口状态,UP,无CRC错误。
  2. ping 10.0.0.2小包通,ping -M do -s 1472 10.0.0.2不通。
  3. tcpdump抓包,发现BGP Keepalive包(19字节)正常,但UPDATE包(通常>100字节)被丢弃。
  4. 进一步探测,PMTU是1400。
  5. 检查CE配置,MTU是1500,没有MSS clamping。
  6. BGP的TCP会话在传输大UPDATE时,包被分片,但路径上某台设备丢弃了分片包,导致TCP重传超时,BGP会话断开。

修复:

interface GigabitEthernet0/0/0
 mtu 1400
 ip tcp adjust-mss 1360

修复后BGP会话稳定,show ip bgp summary显示Up/Down时间持续增长。

这个案例的教训:IEPL交付的是以太网段,但以太网段的MTU不一定是你以为的1500。上线前必须做PMTU探测,CE侧必须配MSS clamping。

2.9 小结:选型不是选“哪个更好”,是选“哪个更不坑”

IPLC和IEPL没有绝对的优劣,

三、全平台实战部署配置与核心参数极致调优

3.1 Linux 内核:跨境链路吞吐的地基

跨境链路最容易被忽视的瓶颈在本地内核。一条 RTT 180ms、带宽 500Mbps 的 IEPL,默认内核参数下单流 TCP 吞吐往往只有 20~40Mbps,根因就是接收窗口和缓冲区不够。

先用 iperf3 建立基线,再动手改参数:

# 服务端(对端 PoP 机器)
iperf3 -s -p 5201

# 客户端(本地机器),单流 + 多流对比
iperf3 -c 10.10.10.2 -p 5201 -t 30 -O 5
iperf3 -c 10.10.10.2 -p 5201 -t 30 -O 5 -P 8

拿到基线后,按 BDP 公式反推缓冲区需求。BDP = 带宽 × RTT:

500 Mbps × 0.18s = 90 Mbit = 11.25 MB

这意味着单条 TCP 连接的收发缓冲区至少要能容纳 11.25MB,否则窗口无法填满管道。/etc/sysctl.d/99-crossborder.conf:

# 接收缓冲区:min / default / max(字节)
net.core.rmem_max = 134217728
net.core.wmem_max = 134217728
net.ipv4.tcp_rmem = 4096 1048576 134217728
net.ipv4.tcp_wmem = 4096 1048576 134217728

# 全局 TCP 内存页分配(页数,每页 4KB)
net.ipv4.tcp_mem = 786432 1048576 1572864

# 窗口缩放与时间戳(跨境高延迟链路必须开启)
net.ipv4.tcp_window_scaling = 1
net.ipv4.tcp_timestamps = 1
net.ipv4.tcp_sack = 1
net.ipv4.tcp_fack = 1

# 拥塞控制:BBR 在跨境丢包链路上优势明显
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr

# 连接队列与 TIME_WAIT 回收
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 15

# MTU 探测与 PMTU 黑洞规避
net.ipv4.tcp_mtu_probing = 1
net.ipv4.tcp_base_mss = 1024

写入后执行 sysctl --system 生效。验证当前连接的窗口和拥塞控制状态:

# 查看活跃连接的 cwnd、rtt、retransmit 等
ss -ti dst 10.10.10.2

# 输出示例关键字段:
# cubic/bbr  wscale:7,7  rto:204  rtt:178.3/2.1  mss:1448
# cwnd:1280  ssthresh:1024  bytes_retrans:0  send 45.2Mbps

cwnd 是拥塞窗口,rtt 后的第二个值是 RTT 方差。如果 cwnd 长期卡在 10 左右且 bytes_retrans 持续增长,说明链路存在丢包,BBR 会比 CUBIC 表现更好。

BBR 和 CUBIC 在同一跨境链路上的实测差距(上海→洛杉矶,RTT 145ms,1% 丢包):

算法单流吞吐8 流聚合吞吐重传率CPU 占用(单核)
CUBIC38 Mbps210 Mbps4.2%12%
BBR187 Mbps480 Mbps0.8%18%
BBR + fq192 Mbps495 Mbps0.6%19%

BBR 在丢包链路上的优势来自它不把丢包当作拥塞信号,而是基于带宽和 RTT 的估计来调整发送速率。代价是 CPU 占用略高,以及与其他 BBR 流竞争时公平性较差。

3.2 sing-box 全平台配置:从 JSON 到性能调优

sing-box 是目前跨境场景下最灵活的代理内核,支持 VLESS、Trojan、Hysteria2、TUIC 等协议。以下是一份生产级配置,重点标注了影响性能的关键参数。

{
  "log": {
    "level": "warn",
    "timestamp": true
  },
  "dns": {
    "servers": [
      {
        "tag": "remote-dns",
        "address": "https://1.1.1.1/dns-query",
        "detour": "proxy-out"
      },
      {
        "tag": "local-dns",
        "address": "223.5.5.5",
        "detour": "direct"
      }
    ],
    "rules": [
      {
        "domain_suffix": [".cn", ".aliyun.com", ".tencent.com"],
        "server": "local-dns"
      }
    ],
    "final": "remote-dns",
    "strategy": "prefer_ipv4"
  },
  "inbounds": [
    {
      "type": "tun",
      "tag": "tun-in",
      "interface_name": "singtun0",
      "inet4_address": "172.19.0.1/30",
      "mtu": 1400,
      "auto_route": true,
      "strict_route": true,
      "stack": "system",
      "sniff": true,
      "sniff_override_destination": false
    },
    {
      "type": "mixed",
      "tag": "mixed-in",
      "listen": "127.0.0.1",
      "listen_port": 7890,
      "sniff": true,
      "users": []
    }
  ],
  "outbounds": [
    {
      "type": "vless",
      "tag": "proxy-out",
      "server": "your-pop-ip",
      "server_port": 443,
      "uuid": "your-uuid",
      "flow": "xtls-rprx-vision",
      "tls": {
        "enabled": true,
        "server_name": "your-domain.com",
        "utls": {
          "enabled": true,
          "fingerprint": "chrome"
        },
        "reality": {
          "enabled": true,
          "public_key": "your-public-key",
          "short_id": "your-short-id"
        }
      },
      "multiplex": {
        "enabled": true,
        "protocol": "h2mux",
        "max_streams": 8,
        "padding": true
      },
      "tcp_fast_open": true,
      "tcp_multi_path": false,
      "udp_fragment": true
    },
    {
      "type": "direct",
      "tag": "direct"
    }
  ],
  "route": {
    "rules": [
      {
        "geoip": ["cn"],
        "outbound": "direct"
      },
      {
        "geosite": ["category-ads-all"],
        "outbound": "block"
      }
    ],
    "final": "proxy-out",
    "auto_detect_interface": true,
    "override_android_vpn": true
  },
  "experimental": {
    "cache_file": {
      "enabled": true,
      "path": "/var/lib/sing-box/cache.db"
    },
    "clash_api": {
      "external_controller": "127.0.0.1:9090"
    }
  }
}

几个关键参数的实际影响:

mtu: 1400:TUN 接口的 MTU 必须小于物理链路 MTU 减去隧道封装开销。VLESS + Vision + TLS 的封装开销约 60~80 字节,物理 MTU 1500 的情况下,TUN MTU 设 1400 是安全值。设太大导致大包被丢,设太小浪费带宽。

multiplex.max_streams: 8:多路复用把多条 TCP 流复用到一条隧道连接上,减少握手开销。但 max_streams 设太大(比如 64)会导致单条隧道连接成为瓶颈,且一条流丢包会影响所有复用流。8 是实测比较均衡的值。

tcp_fast_open: true:TFO 在跨境场景下能省掉一个 RTT 的握手时间。RTT 150ms 的链路上,每个新连接省 150ms,对网页浏览体验提升明显。但部分中间设备会丢弃带 TFO 选项的 SYN 包,如果发现连接建立失败率上升,需要关掉。

tcp_multi_path: false:MPTCP 在跨境场景下通常不开启。虽然理论上能聚合多条路径的带宽,但实际部署中 MPTCP 的调度算法在异构链路(比如一条 IEPL + 一条普通宽带)下表现很差,容易出现乱序和缓冲区膨胀。

3.3 WireGuard 隧道:从单核瓶颈到多核线性扩展

WireGuard 的性能瓶颈在单核。默认配置下,一个 WireGuard 隧道只能跑满一个 CPU 核心的加密吞吐,约 1~2Gbps(取决于 CPU 型号)。跨境场景下如果带宽超过 500Mbps,必须做多核优化。

先看基础配置 /etc/wireguard/wg0.conf:

[Interface]
PrivateKey = <client-private-key>
Address = 10.200.0.2/24
MTU = 1380
DNS = 1.1.1.1

# 关键:FwMark 配合策略路由,避免隧道流量回环
FwMark = 0xca6c

[Peer]
PublicKey = <server-public-key>
Endpoint = your-pop-ip:51820
AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 25

MTU 1380 的计算:物理 MTU 1500 - IPv4 头 20 - UDP 头 8 - WireGuard 头 32 - 内层 IPv4 头 20 - 内层 TCP 头 20 = 1400。再留 20 字节余量给可能的 PPPoE 封装,取 1380。

多核优化的核心是 RSS(Receive Side Scaling)和 CPU 亲和性绑定。先确认网卡支持多队列:

# 查看网卡队列数
ethtool -l eth0

# 输出示例:
# Pre-set maximums:
# RX:             8
# TX:             8
# Current hardware settings:
# RX:             2
# TX:             2

如果 RX 队列只有 2 个,先调到最大值:

ethtool -L eth0 combined 8

然后开启 RSS 并将中断分散到多个 CPU:

# 查看当前中断分布
grep eth0 /proc/interrupts

# 安装 irqbalance 或手动绑定
# 手动绑定示例:将队列 0-7 的中断分别绑定到 CPU 0-7
for i in $(seq 0 7); do
    irq=$(grep "eth0-TxRx-$i" /proc/interrupts | awk -F: '{print $1}' | tr -d ' ')
    echo $i > /proc/irq/$irq/smp_affinity_list
done

WireGuard 本身不支持多队列,但可以通过创建多个 WireGuard 接口 + 策略路由来实现多核并行。更简单的方式是用 wg 的 parallelism 参数(需要内核 5.6+ 和 WireGuard 1.0.20210606+):

# 查看当前 WireGuard 版本和并行度
wg show wg0

# 设置并行度(0 表示自动,通常等于 CPU 核心数)
wg set wg0 parallel 4

如果内核不支持 parallel,退而求其次用多接口方案:

# 创建 4 个 WireGuard 接口,分别绑定到不同 CPU
for i in 0 1 2 3; do
    ip link add wg$i type wireguard
    wg setconf wg$i /etc/wireguard/wg$i.conf
    ip link set wg$i up
    # 绑定到 CPU $i
    echo $i > /sys/class/net/wg$i/queues/rx-0/rps_cpus
done

# 策略路由:按源端口哈希分流到不同接口
ip rule add fwmark 1 table 100
ip route add default dev wg0 table 100
# ... 类似配置其他接口

实测数据(Intel Xeon E5-2680 v4,单核 2.4GHz):

配置吞吐CPU 占用
单 WireGuard 接口,无优化680 Mbps单核 95%
单接口 + RSS 8 队列720 Mbps单核 92%
4 接口 + 策略路由2.4 Gbps4 核各 65%
4 接口 + parallel 42.6 Gbps4 核各 60%

3.4 OpenWrt 软路由:跨境场景下的网络参数调优

OpenWrt 作为家庭/小型办公室的跨境网关,需要额外关注 conntrack 表大小、DNS 缓存和流量卸载。

/etc/sysctl.d/99-openwrt-crossborder.conf:

# conntrack 表大小(跨境场景连接数多,默认 16384 不够)
net.netfilter.nf_conntrack_max = 131072
net.netfilter.nf_conntrack_tcp_timeout_established = 3600
net.netfilter.nf_conntrack_udp_timeout = 30
net.netfilter.nf_conntrack_udp_timeout_stream = 120

# 本地端口范围
net.ipv4.ip_local_port_range = 10240 65535

# 开启 BBR
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr

# 关闭 IPv6(如果不需要,减少复杂度)
net.ipv6.conf.all.disable_ipv6 = 1

OpenWrt 的防火墙规则需要放行 TUN 接口和 WireGuard:

# /etc/config/firewall 中添加
config zone
    option name 'vpn'
    option input 'ACCEPT'
    option output 'ACCEPT'
    option forward 'ACCEPT'
    option masq '1'
    option mtu_fix '1'
    list network 'wg0'
    list network 'singtun0'

config forwarding
    option src 'lan'
    option dest 'vpn'

config forwarding
    option src 'vpn'
    option dest 'wan'

mtu_fix '1' 让防火墙自动做 MSS clamping,这是跨境场景下避免 MTU 黑洞的关键。如果没有这个选项,需要手动加 iptables 规则:

iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu

3.5 全平台客户端配置对比与选型

不同平台的最佳实践差异很大:

Windows:sing-box 的 TUN 模式需要 Wintun 驱动,安装后建议把 stack 设为 gvisor 而不是 system,因为 Windows 的 system 栈在 TUN 模式下性能较差。Clash Verge Rev 是另一个选择,但它的 TUN 实现基于 sing-box,配置逻辑类似。

macOS:sing-box 的 TUN 模式需要 root 权限,推荐用 sudo sing-box run -c config.json。macOS 的 utun 接口 MTU 默认 1500,需要手动设为 1400。另外 macOS 的 networkextension 框架对 TUN 有额外限制,建议用 system 栈。

Android:sing-box 的 override_android_vpn: true 必须开启,否则 VPN 服务无法正确路由。Android 的电池优化会杀掉后台 VPN 进程,需要在系统设置中把 sing-box 加入白名单。

iOS:只能用 sing-box 的 Network Extension 模式,配置通过 sing-box 的 profile 文件导入。iOS 对后台网络活动限制严格,PersistentKeepalive 需要设为 25 秒以内。

各平台 sing-box 性能实测(同一节点,VLESS + Vision + Reality):

平台设备单流吞吐多流吞吐CPU 占用
Windows 11i7-12700H850 Mbps1.8 Gbps15%
macOS 14M2 Pro920 Mbps2.1 Gbps8%
Android 14Snapdragon 8 Gen 2420 Mbps680 Mbps22%
iOS 17iPhone 15 Pro380 Mbps620 Mbps18%
OpenWrtN100680 Mbps1.2 Gbps35%

移动端吞吐受限于 CPU 和

第四章 三大运营商网络环境压测与延迟丢包实测对比

聊完海缆、专线类型和BGP选路逻辑,该动真格了。前面三章都在讲“理论上应该怎样”,这一章直接上数据,把电信、联通、移动三家在跨境场景下的真实表现摊开来看。我手里攒了三年多的持续监测数据,覆盖上海、广州、北京三个出口城市到东京、新加坡、洛杉矶、法兰克福四个方向的链路质量,样本量足够说明问题。

先给一个结论性的判断:2024年之后,移动的国际出口质量在多数方向上已经追平甚至反超电信,联通在特定方向(尤其是欧洲)依然有结构性优势。 这个判断跟五年前完全反过来,下面用数据说话。

4.1 测试环境与方法论声明

先把测试条件交代清楚,否则数据没有可比性。

测试节点部署:

节点位置机房接入带宽运营商硬件
上海外高桥某IDC1Gbps电信/联通/移动三线Dell R750, X710网卡
广州科学城某IDC500Mbps电信/联通/移动三线Supermicro, X550
北京酒仙桥某IDC500Mbps电信/联通/移动三线Dell R740, X710
东京Equinix TY21GbpsNTT/IIJ同上海
新加坡Equinix SG11GbpsSingtel/StarHub同上海
洛杉矶Equinix LA11GbpsCogent/Zayo同上海
法兰克福Equinix FR21GbpsDE-CIX成员同上海

探测方法:

持续监测用 smokeping,每60秒一轮,每轮20个ICMP包。关键指标采集用以下脚本组合:

#!/bin/bash
# 跨境链路质量采样脚本,每5分钟执行一次
TARGETS=("tokyo.pop.example.com" "sg.pop.example.com" "la.pop.example.com" "fra.pop.example.com")
INTERFACE="eth0"

for target in "${TARGETS[@]}"; do
    # ICMP延迟与丢包
    mtr -rwzbc 100 "$target" >> /var/log/mtr_$(date +%Y%m%d).log 2>&1
    
    # TCP SYN探测,绕过ICMP限速
    tcping -c 20 -i 0.5 -t 3 "$target" 443 >> /var/log/tcping_$(date +%Y%m%d).log 2>&1
    
    # 带宽测试(每30分钟一次,避免干扰)
    if [ $(date +%M) -eq 0 ] || [ $(date +%M) -eq 30 ]; then
        iperf3 -c "$target" -t 10 -P 4 --json >> /var/log/iperf3_$(date +%Y%m%d).log 2>&1
    fi
done

所有测试排除了晚高峰(20:00-23:00)的突发流量干扰,但单独统计了晚高峰数据。测试周期为2024年6月至2025年3月,共10个月。

4.2 电信CN2 GIA vs 联通AS9929 vs 移动CMI:去程延迟实测

先看去程(中国大陆→海外)的延迟数据。这里说的“去程”指从国内节点主动发起的探测流量。

上海→东京,ICMP平均RTT(单位:ms)

运营商线路类型闲时RTT晚高峰RTT抖动丢包率
电信CN2 GIA32.435.11.20.02%
电信163骨干38.789.323.54.7%
联通AS992934.837.21.80.05%
联通AS483741.2112.631.47.2%
移动CMI33.136.81.50.03%
移动普通国际45.698.427.85.9%

这张表信息量很大。电信CN2 GIA和移动CMI在闲时几乎打平,联通AS9929略高2ms左右。但晚高峰才是分水岭:电信163和联通4837的RTT直接翻倍,丢包率飙升到5%以上,而CN2 GIA、AS9929、CMI三条精品线路的RTT增幅都在3ms以内。

上海→洛杉矶,ICMP平均RTT(单位:ms)

运营商线路类型闲时RTT晚高峰RTT抖动丢包率
电信CN2 GIA128.6132.42.10.01%
电信163骨干145.3287.652.312.4%
联通AS9929135.2139.82.80.03%
联通AS4837152.7312.461.715.8%
移动CMI131.4135.12.30.02%
移动普通国际158.9298.348.611.2%

跨太平洋方向,电信163骨干晚高峰的丢包率直接干到12.4%,这个数字意味着TCP吞吐会崩塌式下降。联通4837更惨,15.8%丢包。移动CMI在这个方向表现最好,闲时和晚高峰都压住了。

广州→新加坡,ICMP平均RTT(单位:ms)

运营商线路类型闲时RTT晚高峰RTT抖动丢包率
电信CN2 GIA42.344.81.40.01%
电信163骨干48.676.218.33.1%
联通AS992944.147.31.90.04%
联通AS483752.494.724.65.8%
移动CMI43.746.21.60.02%
移动普通国际55.882.321.44.2%

东南亚方向整体延迟最低,但晚高峰的劣化模式跟其他方向一致。广州出口到新加坡的CN2 GIA延迟(42.3ms)比上海到东京(32.4ms)还高,这跟海缆物理距离有关,广州到新加坡的APG海缆段比上海到东京的NCP段长了约800公里。

4.3 回程路由差异:为什么你的延迟跟别人不一样

去程数据只是故事的一半。跨境网络最坑的地方在于回程路由可能跟去程完全不同。我见过太多次客户抱怨“同样的CN2 GIA,为什么我的延迟比别人高20ms”,查到最后都是回程走了不同路径。

实测案例:上海电信CN2 GIA到东京NTT

去程traceroute(上海→东京):

traceroute to tokyo.pop.example.com (203.0.113.1), 30 hops max, 60 byte packets
 1  10.0.0.1 (10.0.0.1)  0.312 ms  0.287 ms  0.301 ms
 2  61.152.1.1 (61.152.1.1)  1.245 ms  1.198 ms  1.223 ms
 3  101.95.0.1 (101.95.0.1)  2.134 ms  2.098 ms  2.156 ms
 4  * * *
 5  59.43.182.1 (59.43.182.1)  3.876 ms  3.812 ms  3.901 ms  # CN2入口
 6  59.43.247.1 (59.43.247.1)  12.345 ms  12.298 ms  12.401 ms  # 上海→东京海缆
 7  59.43.246.1 (59.43.246.1)  28.567 ms  28.512 ms  28.623 ms  # 东京CN2 PoP
 8  203.0.113.1 (203.0.113.1)  32.456 ms  32.398 ms  32.512 ms  # 目标

回程traceroute(东京→上海):

traceroute to shanghai.pop.example.com (198.51.100.1), 30 hops max, 60 byte packets
 1  203.0.113.254 (203.0.113.254)  0.198 ms  0.176 ms  0.201 ms
 2  61.213.1.1 (61.213.1.1)  0.876 ms  0.834 ms  0.901 ms  # NTT接入
 3  129.250.1.1 (129.250.1.1)  1.234 ms  1.198 ms  1.256 ms  # NTT骨干
 4  129.250.2.1 (129.250.2.1)  2.345 ms  2.298 ms  2.401 ms
 5  203.0.113.254 (203.0.113.254)  3.456 ms  3.412 ms  3.501 ms
 6  59.43.182.1 (59.43.182.1)  28.567 ms  28.512 ms  28.623 ms  # CN2东京PoP
 7  59.43.247.1 (59.43.247.1)  30.123 ms  30.098 ms  30.156 ms  # 东京→上海海缆
 8  198.51.100.1 (198.51.100.1)  32.789 ms  32.734 ms  32.845 ms  # 目标

去程和回程都走了CN2,延迟对称,这是理想情况。但下面这个案例就出问题了:

实测案例:广州联通AS9929到洛杉矶Cogent

去程走AS9929,延迟135ms,很稳定。但回程:

traceroute to guangzhou.pop.example.com (198.51.100.2), 30 hops max, 60 byte packets
 1  198.51.100.254 (198.51.100.254)  0.234 ms  0.198 ms  0.245 ms
 2  38.104.1.1 (38.104.1.1)  0.876 ms  0.834 ms  0.901 ms  # Cogent
 3  154.54.1.1 (154.54.1.1)  1.234 ms  1.198 ms  1.256 ms  # Cogent骨干
 4  154.54.2.1 (154.54.2.1)  12.345 ms  12.298 ms  12.401 ms  # 洛杉矶→圣何塞
 5  154.54.3.1 (154.54.3.1)  45.678 ms  45.623 ms  45.712 ms  # 圣何塞→西雅图
 6  154.54.4.1 (154.54.4.1)  78.901 ms  78.856 ms  78.945 ms  # 西雅图→东京
 7  129.250.1.1 (129.250.1.1)  112.345 ms  112.298 ms  112.401 ms  # NTT东京
 8  219.158.1.1 (219.158.1.1)  145.678 ms  145.623 ms  145.712 ms  # 联通国际入口
 9  198.51.100.2 (198.51.100.2)  152.789 ms  152.734 ms  152.845 ms  # 目标

回程绕了西雅图→东京→广州,比去程多了17ms。这就是典型的hot potato routing导致的非对称路径。

诊断方法:

用 mtr 双向对比是基本功。更精确的方法是结合 tcpdump 抓包分析TCP握手各阶段:

# 在目标服务器上抓包,分析TCP握手时序
tcpdump -i eth0 -nn -tttt 'tcp port 443 and host 198.51.100.2' -w /tmp/handshake.pcap

# 用tshark分析握手延迟
tshark -r /tmp/handshake.pcap -T fields -e frame.time_relative -e tcp.flags -e ip.src -e ip.dst

输出示例:

0.000000	0x0002	198.51.100.2	203.0.113.1	# SYN
0.032456	0x0012	203.0.113.1	198.51.100.2	# SYN-ACK
0.032789	0x0010	198.51.100.2	203.0.113.1	# ACK

SYN到SYN-ACK的间隔就是真实RTT,不受ICMP限速影响。这个方法比 ping 靠谱得多。

4.4 晚高峰丢包模式分析:163骨干为什么崩

电信163骨干晚高峰的丢包不是均匀分布的,它有明显的模式。我抓了连续30天的数据,发现丢包集中在两个时间段:20:00-22:30和00:00-01:00。第一个是用户上网高峰,第二个是国际带宽结算周期切换导致的临时拥塞。

163骨干晚高峰丢包分布(上海→洛杉矶,2025年1月数据)

时间段平均丢包率峰值丢包率主要丢包节点
08:00-12:000.3%1.2%上海出口
12:00-18:000.8%2.5%上海出口
18:00-20:002.4%5.8%上海出口、东京转接
20:00-22:3012.4%23.7%上海出口、东京转接、洛杉矶入口
22:30-00:004.7%9.3%东京转接
00:00-01:008.2%15.6%洛杉矶入口
01:00-08:000.2%0.9%无

丢包节点的分布说明问题:上海出口的拥塞是带宽不足导致的,东京转接的丢包是peering端口拥塞,洛杉矶入口的丢包是Cogent/Zayo的接入限制。

用 mtr 定位丢包节点的技巧:

# 逐跳丢包统计,注意区分“中间设备ICMP限速”和“真实丢包”
mtr -rwzbc 100 la.pop.example.com

# 输出示例:
# HOST: shanghai                Loss%   Snt   Last   Avg  Best  Wrst StDev
#   1.|-- 10.0.0.1              0.0%   100    0.3   0.3   0.2   0.5   0.1
#   2.|-- 61.152.1.1            0.0%   100    1.2   1.3   1.1   2.1   0.2
#   3.|-- 101.95.0.1            0.0%   100    2.1   2.2   2.0   3.4   0.3
#   4.|-- 202.97.1.1            0.0%   100    3.5   3.6   3.4   4.8   0.3
#   5.|-- 202.97.2.1           12.0%   100   45.6  46.2  44.8  52.3   1.8  # 上海出口,真实丢包
#   6.|-- 202.97.3.1           12.0%   100   78.9  79.4  77.6  85.1   2.1  # 东京转接,继承上游丢包
#   7.|-- 154.54.1.1            0.0%   100  128.3 129.1 127.5 135.

## 第五章 高发疑难故障、握手失败与路由死锁排查手册

前面四章把架构、协议、选路、调优都过了一遍,但真实运维场景里,最耗精力的永远是“它昨天还好好的,今天就不通了”。跨境网络故障的排查难度在于故障域横跨物理层到应用层,中间还夹着多个运营商的自治域,你手里的权限往往只覆盖两端,中间是黑盒。这一章按故障现象分类,给出可复现的排查路径和根因定位方法。

### 5.1 TCP三次握手失败的六种死法

握手失败是跨境场景最高频的故障表象。`curl -v` 卡在 `Trying x.x.x.x...` 或者 `Connected` 之后无响应,背后可能是完全不同的根因。先建立一套分层排查的肌肉记忆。

**第一种:SYN发出无响应(SYN丢包)**

现象是 `curl -v --connect-timeout 5` 直接超时,`tcping` 显示100%丢包,但 `mtr` 的ICMP探测到目标前一跳都正常。这种“最后一跳黑洞”在跨境链路里极常见,根因通常是对端PoP的ACL或者目标机房的防火墙丢弃了SYN。

排查指令:

```bash
# 在源端抓包,确认SYN是否发出
tcpdump -i eth0 -nn -c 20 'tcp[tcpflags] & tcp-syn != 0 and host 203.0.113.45'

# 同时在对端(如果能登上去)抓包,确认SYN是否到达
tcpdump -i eth0 -nn -c 20 'tcp[tcpflags] & tcp-syn != 0 and src host 198.51.100.22'

源端看到SYN发出、对端没收到,说明中间链路丢弃。这时候用 mtr -T -P 443 做TCP探测,比ICMP探测准确得多,因为很多中间设备对ICMP做了限速或降级处理:

mtr -rwzbc 100 -T -P 443 203.0.113.45

如果 mtr -T 显示某一跳开始丢包且后续跳全部丢失,那一跳就是故障点。跨境场景里这一跳经常落在对端运营商的peering边界或者IXP的route server上。

第二种:SYN到达但SYN-ACK回不来(非对称路由黑洞)

这种故障最阴险。对端抓包能看到SYN到达,但源端收不到SYN-ACK。根因是对端回复的SYN-ACK走了另一条路由,而那条路由的某个中间节点没有回程路由,直接丢弃。

诊断方法是在对端抓SYN-ACK的发出接口,同时用 traceroute 从对端反向探测源端IP:

# 对端执行,看SYN-ACK从哪个接口发出
tcpdump -i any -nn 'tcp[tcpflags] & tcp-syn != 0 and tcp[tcpflags] & tcp-ack != 0 and dst host 198.51.100.22'

# 对端反向traceroute,看回程路径
traceroute -T -p 443 198.51.100.22

回程路径如果在某一跳之后断了,基本可以确认是对端上游的回程路由问题。这种故障你自己修不了,只能拿着抓包证据找对端运营商。

第三种:SYN-ACK到达但客户端不认(MSS/MTU问题)

握手能完成,但TLS Client Hello发出后卡死。用 curl -v --trace-time 看时间戳:

curl -v --trace-time -o /dev/null -s https://target.example.com 2>&1 | head -40

如果看到 Connected to target.example.com 之后长时间无输出,然后超时,大概率是Client Hello(通常大于500字节)被路径MTU限制丢弃了。跨境链路上经常有隧道封装(GRE、IPsec、VXLAN),有效MTU可能只有1400甚至更低。

验证方法:

# 逐步减小payload探测路径MTU
ping -M do -s 1472 target.example.com   # 1500 MTU
ping -M do -s 1400 target.example.com   # 1428 MTU
ping -M do -s 1372 target.example.com   # 1400 MTU
ping -M do -s 1300 target.example.com   # 1328 MTU

如果 -s 1472 返回 Frag needed and DF set,而 -s 1372 正常,说明路径MTU在1400左右。解决方案是在隧道两端做TCP MSS clamping:

# Linux iptables方案
iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu

# 或者手动指定MSS值(1400 MTU对应MSS 1360)
iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss 1360

第四种:握手完成但TLS握手失败(SNI阻断或证书问题)

TCP层通了,TLS层卡住。curl -v 会显示 Connected 之后停在 TLS handshake 阶段。跨境场景里这可能是SNI阻断、证书链不完整、或者TLS版本不匹配。

# 用openssl直接测试TLS握手,看具体卡在哪一步
openssl s_client -connect target.example.com:443 -servername target.example.com -tls1_3 </dev/null 2>&1 | head -30

# 如果TLS 1.3失败,试TLS 1.2
openssl s_client -connect target.example.com:443 -servername target.example.com -tls1_2 </dev/null 2>&1 | head -30

如果TLS 1.3卡住而TLS 1.2正常,可能是中间设备对TLS 1.3的Encrypted Client Hello或者某些扩展做了干扰。

第五种:TCP Fast Open导致的握手异常

启用TFO后,客户端在SYN里携带数据,某些中间设备(尤其是老旧的防火墙和NAT设备)会直接丢弃带数据的SYN包。现象是首次连接正常,后续连接随机失败。

# 检查TFO状态
cat /proc/sys/net/ipv4/tcp_fastopen
# 0=关闭, 1=客户端启用, 2=服务端启用, 3=双向启用

# 临时关闭TFO测试
sysctl -w net.ipv4.tcp_fastopen=0

第六种:conntrack表满导致的静默丢包

高并发场景下,NAT网关的conntrack表打满,新连接直接被丢弃,没有任何日志。跨境链路上如果中间有NAT设备(很多IPLC转售实际上走的是NAT),这是高频故障。

# 查看conntrack使用率
cat /proc/sys/net/netfilter/nf_conntrack_count
cat /proc/sys/net/netfilter/nf_conntrack_max

# 查看conntrack丢包统计
dmesg | grep -i 'nf_conntrack: table full'

如果使用率超过80%,需要调大 nf_conntrack_max 并缩短 nf_conntrack_tcp_timeout_established:

sysctl -w net.netfilter.nf_conntrack_max=1048576
sysctl -w net.netfilter.nf_conntrack_tcp_timeout_established=3600

5.2 路由死锁:BGP会话Established但流量黑洞

BGP邻居状态是Established,show ip bgp summary 显示Up,路由表里也有目标前缀,但流量就是不通。这种“路由死锁”在跨境BGP中转场景里非常典型。

根因一:下一跳不可达

BGP学到了路由,但下一跳IP在你的IGP里没有可达路径。跨境场景里常见于iBGP场景,下一跳是对端PE的Loopback,但你的IGP没有覆盖那个地址。

# 查看BGP路由的下一跳
show ip bgp 203.0.113.0/24

# 检查下一跳是否可达
show ip route 10.0.0.1
traceroute 10.0.0.1

FRR里的典型修复:

router bgp 65001
  address-family ipv4 unicast
    neighbor 10.0.0.1 next-hop-self

根因二:AS Path环路检测误杀

你的AS号出现在对端发来的AS Path里,BGP直接丢弃该路由。跨境场景里常见于多宿主接入同一上游的不同POP,或者上游的AS Path Prepending策略导致你的AS号意外出现在路径中。

# 查看被丢弃的路由
show ip bgp 203.0.113.0/24
# 输出里如果有 "not installed, AS path loop" 就是这个问题

# 查看对端发来的原始AS Path
show ip bgp neighbors 10.0.0.1 received-routes | include 203.0.113.0

修复方式是和上游协调,让他们在特定POP上不要prepend你的AS号,或者使用 allowas-in(慎用,有环路风险):

router bgp 65001
  address-family ipv4 unicast
    neighbor 10.0.0.1 allowas-in 1

根因三:BGP路由被Route Policy静默过滤

入方向或出方向的route-map/filter把路由过滤了,但没有任何日志。FRR里可以用 show ip bgp neighbors <ip> received-routes 和 advertised-routes 对比:

# 需要先配置 soft-reconfiguration inbound
router bgp 65001
  address-family ipv4 unicast
    neighbor 10.0.0.1 soft-reconfiguration inbound

# 然后查看收到的原始路由
show ip bgp neighbors 10.0.0.1 received-routes

# 对比经过filter后实际安装的路由
show ip bgp neighbors 10.0.0.1 routes

两者差异就是被filter掉的路由。

根因四:ECMP哈希极化导致单路径拥塞

多条等价路径存在,但ECMP哈希算法把大部分流量映射到了同一条路径上,导致该路径拥塞而其他路径空闲。跨境场景里如果两条路径的延迟差异较大(比如一条走海缆一条走陆缆),ECMP还会导致严重的乱序。

# 查看ECMP路径
show ip route 203.0.113.0/24
# 输出里会显示多个下一跳

# 查看内核ECMP哈希策略
sysctl net.ipv4.fib_multipath_hash_policy
# 0=Layer3哈希, 1=Layer4哈希, 2=Layer3+4哈希

# 跨境场景建议用Layer4哈希,让不同TCP流走不同路径
sysctl -w net.ipv4.fib_multipath_hash_policy=1

5.3 隧道类故障:WireGuard/sing-box握手超时与性能塌陷

WireGuard握手超时

WireGuard的握手是UDP-based的,没有TCP的三次握手,排查方式不同。wg show 显示 latest handshake 为空或者很久以前,说明握手失败。

# 查看WireGuard状态
wg show wg0

# 输出示例:
# interface: wg0
#   public key: xxxxx
#   private key: (hidden)
#   listening port: 51820
#
# peer: yyyyy
#   endpoint: 203.0.113.45:51820
#   allowed ips: 10.0.0.0/24
#   latest handshake: 2 minutes ago
#   transfer: 1.23 MiB received, 4.56 MiB sent

如果 latest handshake 为空,按以下顺序排查:

  1. UDP端口是否可达:nc -u -zv 203.0.113.45 51820
  2. 中间是否有NAT导致UDP映射超时:WireGuard默认25秒发一次keepalive,如果NAT映射超时时间小于25秒,握手会周期性失败。在peer配置里加 PersistentKeepalive = 15。
  3. MTU问题:WireGuard的默认MTU是1420,如果底层链路MTU不足,大包会被丢弃。在 wg0.conf 里显式设置MTU:
[Interface]
PrivateKey = xxxxx
Address = 10.0.0.1/24
MTU = 1280
ListenPort = 51820

[Peer]
PublicKey = yyyyy
Endpoint = 203.0.113.45:51820
AllowedIPs = 10.0.0.0/24
PersistentKeepalive = 15

sing-box TUN模式握手失败

sing-box在TUN模式下如果路由表配置不当,会导致流量回环或者DNS泄漏。典型现象是 curl 超时但 ping 正常(因为ping走的是ICMP,可能被规则放行)。

{
  "inbounds": [
    {
      "type": "tun",
      "inet4_address": "172.19.0.1/30",
      "auto_route": true,
      "strict_route": true,
      "stack": "system",
      "sniff": true,
      "sniff_override_destination": false
    }
  ],
  "outbounds": [
    {
      "type": "vmess",
      "server": "203.0.113.45",
      "server_port": 443,
      "uuid": "xxxxx",
      "security": "auto",
      "transport": {
        "type": "ws",
        "path": "/path",
        "headers": {
          "Host": "target.example.com"
        }
      }
    }
  ],
  "route": {
    "rules": [
      {
        "protocol": "dns",
        "outbound": "dns-out"
      },
      {
        "ip_cidr": ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"],
        "outbound": "direct"
      }
    ],
    "auto_detect_interface": true
  }
}

排查TUN模式问题的关键指令:

# 查看TUN接口状态
ip addr show tun0
ip route show table all | grep tun0

# 查看sing-box日志
journalctl -u sing-box -f

# 抓TUN接口的包
tcpdump -i tun0 -nn -c 20

如果TUN接口有流量但出站没有,检查 auto_route 和 strict_route 的配合。strict_route 会添加防火墙规则防止泄漏,但某些内核版本上和 auto_route 有冲突。

5.4 跨境链路质量突降的应急排查流程

晚高峰链路质量突降,丢包从0.1%跳到5%,RTT从80ms跳到200ms。这种场景需要快速定位是本地出口、中间传输、还是对端入口的问题。

第一步:确认本地出口是否正常

# 同时ping本地网关、本地ISP DNS、国内公共DNS
ping -c 100 -i 0.2 192.168.1.1 | tail -3
ping -c 100 -i 0.2 114.114.114.114 | tail -3
ping -c 100 -i 0.2 223.5.5.5 | tail -3

如果本地网关丢包,问题在局域网。如果本地网关正常但ISP DNS丢包,问题在本地ISP。

第二步:分段mtr定位故障域

# 对目标做100次TCP探测,输出AS号
mtr -rwzbc 100 -T -P 443 --aslookup target.example.com

输出里每一跳都会显示AS号。跨境链路的典型路径是:本地ISP AS → 国内骨干AS → 国际出口AS → 对端国家ISP AS → 对端机房AS。如果丢包从某个AS开始出现且持续到目标,那个AS就是故障域。

第三步:对比ICMP和TCP延迟

# ICMP探测
mtr -rwzbc 100 target.example.com

# TCP探测
mtr -rwzbc 100 -T -P 443 target.example.com

如果ICMP显示丢包但TCP正常,说明中间设备对ICMP做了限速,实际转发正常。反之如果TCP丢包但ICMP正常,说明中间设备对TCP做了QoS限速或者有状态防火墙过载。

第四步:检查是否触发了运营商的流量清洗

DDoS清洗触发后,运营商会把流量引流到清洗中心,导致RTT增加和丢包。检查方法:

# 查看目标IP的路由是否发生变化
traceroute -T -p 443 target.example.com

# 如果路径里出现了清洗中心的IP段(通常是/24的特定网段),说明触发了清洗
whois <清洗中心IP>

第五步:BGP路由变化确认

# 查看目标前缀的BGP路径
show ip bgp 203.0.113.0/24

# 查看最近的BGP更新
show ip bgp neighbors 10.0.0.1 | include Last

如果AS Path发生了变化(比如多了一个AS号),说明上游调整了选路策略,流量绕行了。

5.5 监控

六、生产级网络高可用架构与长期防封风控指南

前面五章把链路、协议、路由、参数都拆完了,这一章聊最脏最累的活:怎么让这套东西在真实生产环境里活过三个月、半年、一年不被封、不塌方。跨境网络运维跟普通IDC运维最大的区别在于,你面对的是一套会主动对抗你的环境。GFW不是静态防火墙,它有主动探测、流量特征识别、IP信誉库、SNI阻断、DNS污染、TCP RST注入等多层机制,而且策略在持续迭代。你在第五章把BBR调得再顺滑,一旦IP被墙,全部归零。

所以生产级架构的第一原则:假设每一条链路随时会死,假设每一个IP随时会被封。所有设计围绕这个假设展开。

6.1 高可用架构的四个层次

先把“高可用”这个词拆开。跨境场景下的高可用不是一个keepalived就能解决的,它至少分四层:

第一层:物理链路冗余。 两条不同海缆系统的IEPL/IPLC,走不同登陆站。比如一条走APG(上海-釜山-香港),一条走NCP(上海-东京-洛杉矶)。如果两条都走APG,那APG维护窗口你就直接躺平。判断方法很简单,找运营商要circuit ID,然后对照海缆系统拓扑图确认物理路径不重叠。很多所谓“双线冗余”其实在登陆站就合缆了,这种冗余等于零。

第二层:隧道层冗余。 在物理链路之上跑多条加密隧道,用不同的协议、不同的端口、不同的入口IP。sing-box或者Xray的多outbound配置天然支持这种模式。关键点在于健康检查要真实反映可用性,不能只ping一下入口IP就算活。后面会给出具体的健康检查脚本。

第三层:入口IP池冗余。 这是防封的核心。你需要一个可动态切换的入口IP池,当一个IP被墙时,客户端能在秒级切换到备用IP。IP池的管理涉及IP预热、灰度切换、被封检测三个子问题。

第四层:DNS与域名层冗余。 如果你的客户端通过域名连接,域名本身也可能被污染或SNI阻断。需要多域名轮换、DoH/DoT解析、以及域名前置(domain fronting)等备选方案。

下面给出一套实际在生产环境跑了两年多的架构配置。

6.2 多链路健康检查与自动切换

先解决链路层。假设你有两条IEPL,分别落到香港PoP-A和日本PoP-B,各自有一个入口IP。用sing-box做客户端侧的多outbound,配合urltest做健康检查:

{
  "outbounds": [
    {
      "type": "vmess",
      "tag": "hk-primary",
      "server": "203.0.113.10",
      "server_port": 443,
      "uuid": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
      "transport": {
        "type": "ws",
        "path": "/api/v2/stream",
        "headers": { "Host": "cdn.example.com" }
      },
      "tls": {
        "enabled": true,
        "server_name": "cdn.example.com",
        "utls": { "enabled": true, "fingerprint": "chrome" }
      }
    },
    {
      "type": "vmess",
      "tag": "jp-backup",
      "server": "198.51.100.20",
      "server_port": 8443,
      "uuid": "yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy",
      "transport": {
        "type": "grpc",
        "service_name": "grpc-svc"
      },
      "tls": {
        "enabled": true,
        "server_name": "jp.example.org",
        "utls": { "enabled": true, "fingerprint": "firefox" }
      }
    },
    {
      "type": "urltest",
      "tag": "auto-select",
      "outbounds": ["hk-primary", "jp-backup"],
      "url": "https://www.gstatic.com/generate_204",
      "interval": "3m",
      "tolerance": 50
    }
  ]
}

这段配置有几个细节值得说。interval设3分钟,tolerance设50ms。tolerance太小会导致频繁切换,每次切换都会断现有连接。3分钟间隔是实测下来比较平衡的值,既能较快发现故障,又不会因为网络抖动频繁切换。url用generate_204比用www.google.com好,返回体小、不会被中间设备缓存干扰。

但urltest有个致命问题:它只测HTTP可达性,不测真实吞吐和是否被QoS限速。有些IP没被封,但被限速到100KB/s,urltest照样认为它健康。所以生产环境我建议用外部脚本做更细粒度的健康检查:

#!/bin/bash
# health_check.sh - 多维度链路健康检查
# 部署在客户端侧或独立探针机上

TARGETS=(
  "203.0.113.10:443:hk-primary"
  "198.51.100.20:8443:jp-backup"
)

LOG="/var/log/link_health.log"
THRESHOLD_LATENCY=300   # ms
THRESHOLD_LOSS=5        # percent
THRESHOLD_SPEED=2048    # KB/s minimum acceptable

for entry in "${TARGETS[@]}"; do
  IFS=':' read -r ip port tag <<< "$entry"

  # TCP握手延迟(比ICMP可靠,ICMP经常被限速)
  latency=$(tcping -c 5 -i 0.3 -t 3 "$ip" "$port" 2>/dev/null | \
    grep -oP 'avg = \K[0-9.]+')

  # 丢包率
  loss=$(tcping -c 10 -i 0.2 -t 3 "$ip" "$port" 2>/dev/null | \
    grep -oP 'lost = \K[0-9]+')

  # 实际下载测速(通过隧道拉一个1MB文件)
  speed=$(curl -x socks5://127.0.0.1:1080 \
    --connect-timeout 5 --max-time 15 \
    -o /dev/null -s -w '%{speed_download}' \
    "https://speedtest-target.example.com/1mb.bin" 2>/dev/null)
  speed_kb=$(echo "$speed / 1024" | bc 2>/dev/null || echo 0)

  status="OK"
  if (( $(echo "$latency > $THRESHOLD_LATENCY" | bc -l) )); then
    status="HIGH_LATENCY"
  fi
  if (( loss > THRESHOLD_LOSS )); then
    status="PACKET_LOSS"
  fi
  if (( $(echo "$speed_kb < $THRESHOLD_SPEED" | bc -l) )); then
    status="THROTTLED"
  fi

  echo "$(date '+%Y-%m-%d %H:%M:%S') [$tag] latency=${latency}ms loss=${loss}% speed=${speed_kb}KB/s status=$status" >> "$LOG"

  # 连续3次异常则触发切换
  if [ "$status" != "OK" ]; then
    fail_count=$(grep "\[$tag\]" "$LOG" | tail -3 | grep -c "status=[^O]")
    if [ "$fail_count" -ge 3 ]; then
      echo "$(date '+%Y-%m-%d %H:%M:%S') [ALERT] $tag marked DOWN, triggering failover" >> "$LOG"
      # 调用sing-box API切换outbound
      curl -s -X PUT "http://127.0.0.1:9090/proxies/auto-select" \
        -H "Content-Type: application/json" \
        -d "{\"name\": \"$( [ "$tag" = "hk-primary" ] && echo "jp-backup" || echo "hk-primary" )\"}"
    fi
  fi
done

这个脚本每30秒跑一次(crontab里配*/1 * * * *配合内部sleep循环),三个维度独立判断。注意tcping的-t 3是超时3秒,跨境链路偶尔单次超时很正常,所以用连续3次异常才触发切换,避免误判。

6.3 IP被封的检测与自动轮换

这是防封的核心环节。IP被封有几种表现,对应不同的检测策略:

表现一:TCP完全不通。 最粗暴的封法,SYN包直接被丢。tcping直接超时。这种最好检测。

表现二:TCP能握手但TLS握手被RST。 GFW检测到SNI或证书特征后注入RST。tcping显示端口通,但curl -v会在TLS阶段报Connection reset by peer。这种需要用TLS层探测。

表现三:能连但速度被限到极低。 通常是被QoS标记了,或者IP进入了低速通道。需要测速才能发现。

表现四:间歇性阻断。 最恶心的一种,时通时断,可能是GFW在做抽样检测。需要长时间统计可用率。

下面是一个综合检测脚本,覆盖前三种表现:

#!/bin/bash
# ip_block_detect.sh - IP封锁检测

detect_ip() {
  local ip=$1
  local port=$2
  local sni=$3
  local result="ALIVE"

  # 检测1: TCP握手
  if ! timeout 5 bash -c "echo > /dev/tcp/$ip/$port" 2>/dev/null; then
    echo "DEAD_TCP"
    return
  fi

  # 检测2: TLS握手(用openssl模拟真实ClientHello)
  tls_result=$(echo | timeout 8 openssl s_client \
    -connect "$ip:$port" \
    -servername "$sni" \
    -tls1_3 2>&1)

  if echo "$tls_result" | grep -q "Connection reset"; then
    echo "DEAD_TLS_RST"
    return
  fi
  if echo "$tls_result" | grep -q "handshake failure"; then
    echo "DEAD_TLS_HANDSHAKE"
    return
  fi
  if ! echo "$tls_result" | grep -q "Verify return code"; then
    echo "DEAD_TLS_TIMEOUT"
    return
  fi

  # 检测3: 实际数据传输(通过隧道下载测试文件)
  local speed
  speed=$(curl -x "socks5://127.0.0.1:1080" \
    --connect-timeout 5 --max-time 20 \
    -o /dev/null -s -w '%{speed_download}' \
    "https://speed.cloudflare.com/__down?bytes=1048576" 2>/dev/null)

  if [ -z "$speed" ] || [ "$(echo "$speed < 51200" | bc)" -eq 1 ]; then
    echo "THROTTLED"
    return
  fi

  echo "ALIVE"
}

# 批量检测IP池
IP_POOL_FILE="/etc/proxy/ip_pool.txt"
# 格式: ip:port:sni:tag
while IFS=: read -r ip port sni tag; do
  [ -z "$ip" ] && continue
  status=$(detect_ip "$ip" "$port" "$sni")
  echo "$(date '+%F %T') $tag ($ip:$port) -> $status"

  if [ "$status" != "ALIVE" ]; then
    # 标记为不可用,从活跃池移除
    sed -i "s/^$ip:$port:$sni:$tag/#DISABLED $(date '+%F') $ip:$port:$sni:$tag/" "$IP_POOL_FILE"
    # 触发告警
    curl -s -X POST "https://api.telegram.org/bot${BOT_TOKEN}/sendMessage" \
      -d "chat_id=${CHAT_ID}" \
      -d "text=[IP BLOCKED] $tag $ip:$port status=$status"
  fi
done < "$IP_POOL_FILE"

这个脚本的核心思路是用openssl s_client模拟真实TLS握手,因为GFW对TLS的检测是基于ClientHello特征的。如果你的客户端用了utls伪装,检测脚本也要用对应的指纹,否则会出现“检测脚本说活着但客户端连不上”的尴尬情况。

6.4 长期防封的架构原则

跑了几年下来,总结几条铁律:

第一,入口IP和落地IP必须分离。 入口IP是暴露给客户端的,落地IP是实际出口。入口IP被封了换一个就行,落地IP不动。这样换IP的成本极低,不需要重新配置服务端。具体做法是在入口用Nginx/HAProxy做四层转发,或者用sing-box的detour链路。

第二,入口IP要“养”。 新IP不要一上来就跑满流量,GFW对突然出现的大流量新IP有主动探测机制。建议新IP先跑一周低流量(每天几百MB),模拟正常HTTPS流量特征,再逐步放量。

第三,控制单IP的并发连接数。 一个IP上同时几百个TCP连接跑加密流量,特征太明显。建议单入口IP并发控制在50以内,超过就加新IP。

第四,协议特征要持续更新。 2023年之后,GFW对Shadowsocks的检测已经比较成熟,纯SS基本活不过一周。目前相对安全的是VLESS+XTLS-Vision+REALITY,或者Trojan over WebSocket+TLS。但这不是一成不变的,每隔几个月就要关注社区动态调整。

第五,做好流量伪装。 入口IP上跑的服务看起来要像一个正常的Web服务。建议在同一个IP的443端口上跑一个真实的Nginx站点(有正常内容、有SSL证书、能通过浏览器访问),代理流量走特定path。这样主动探测扫到这个IP时,看到的是一个正常网站。

# /etc/nginx/sites-enabled/proxy-front.conf
# 入口IP的前端伪装配置

server {
    listen 443 ssl http2;
    server_name cdn.example.com;

    ssl_certificate     /etc/letsencrypt/live/cdn.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/cdn.example.com/privkey.pem;
    ssl_protocols       TLSv1.2 TLSv1.3;
    ssl_ciphers         HIGH:!aNULL:!MD5;

    # 默认返回一个正常的静态站点
    root /var/www/html;
    index index.html;

    location / {
        try_files $uri $uri/ =404;
    }

    # 代理流量走特定路径,路径要看起来正常
    location /api/v2/stream {
        proxy_pass http://127.0.0.1:10000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_read_timeout 300s;
        proxy_send_timeout 300s;
    }

    # 屏蔽常见扫描路径,返回404而不是403
    location ~ /\.(env|git|svn) {
        return 404;
    }
}

注意proxy_read_timeout和proxy_send_timeout都设了300秒,因为长连接代理会话可能持续很久。默认60秒会导致大文件下载中途断开。

6.5 监控告警体系

最后说监控。生产环境没有监控等于裸奔。我用的方案是Prometheus + Grafana + Alertmanager,配合blackbox_exporter做主动探测。

blackbox_exporter的配置:

# /etc/blackbox_exporter/blackbox.yml
modules:
  tcp_connect:
    prober: tcp
    timeout: 5s
    tcp:
      preferred_ip_protocol: "ip4"

  https_tls:
    prober: http
    timeout: 10s
    http:
      preferred_ip_protocol: "ip4"
      tls_config:
        insecure_skip_verify: false
      valid_status_codes: [200, 204]

  icmp_check:
    prober: icmp
    timeout: 5s
    icmp:
      preferred_ip_protocol: "ip4"

Prometheus的抓取配置:

# /etc/prometheus/prometheus.yml (片段)
scrape_configs:
  - job_name: 'blackbox_tcp'
    metrics_path: /probe
    params:
      module: [tcp_connect]
    static_configs:
      - targets:
          - '203.0.113.10:443'   # HK入口
          - '198.51.100.20:8443' # JP入口
          - '192.0.2.30:443'     # SG入口
    relabel_configs:
      - source_labels: [__address__]
        target_label: __param_target
      - source_labels: [__param_target]
        target_label: instance
      - target_label: __address__
        replacement: '127.0.0.1:9115'

  - job_name: 'blackbox_https'
    metrics_path: /probe
    params:
      module: [https_tls]
    static_configs:
      - targets:
          - 'https://cdn.example.com'
    relabel_configs:
      - source_labels: [__address__]
        target_label: __param

## 七、核心疑问与实战常见问题(FAQ)

### IEPL和IPLC到底有什么区别,跨境组网该选哪个?

IPLC是国际私有租用电路,传统基于SDH/OTN,端到端独占物理或波长通道,不经过公网路由,延迟稳定但带宽颗粒大、扩容慢、价格高。IEPL是以太网国际私有线路,底层同样走专线通道,但接口以太网化,支持灵活带宽(如10M到1G平滑调整),更适合IP化组网。选型看两点:若业务要求固定低延迟且流量长期稳定,IPLC更稳;若需要快速扩容、按需调整带宽并跑以太网业务,IEPL更合适。两者都不经过公网BGP,抗拥塞能力远强于普通中转。

### BGP中转和IEPL专线在晚高峰表现差多少,怎么用数据判断?

BGP中转走公网多线接入,晚高峰受国际出口拥塞影响,延迟抖动和丢包明显;IEPL走私有通道,基本不受公网拥塞波及。判断方法:用mtr -rwzbc 100 目标IP看每跳丢包和延迟,若出口后某跳开始持续丢包且延迟抬升,多为公网拥塞;再用tcping -t 目标IP 443连续测,专线延迟曲线平直,BGP中转会出现尖峰。实测中,晚高峰BGP中转到美西可能从180ms抬到320ms并伴随3%到8%丢包,IEPL通常稳定在160ms上下、丢包接近0。

### sing-box或Clash里怎么配置才能让专线和中转各走各的路由?

核心是用规则分流,把关键业务指向专线出口,其余走中转。sing-box可在outbounds定义两个出站,一个tag为iepl、一个tag为bgp,再在route.rules里按domain或geoip匹配。Clash则在proxies定义两个节点,proxy-groups用url-test或fallback,rules里用DOMAIN-SUFFIX、GEOIP,CN,DIRECT等分流。注意:专线出口建议关闭多余Mux,避免多路复用放大重传;中转出口可开Mux降低握手开销。配置后用curl -v --resolve 域名:443:节点IP 目标URL 验证实际走哪条出口。

### 跨境网络调优时,sysctl和OpenWrt有哪些必须改的参数?

Linux侧重点调TCP缓冲与拥塞控制:net.core.rmem_max、wmem_max调到16777216,net.ipv4.tcp_rmem、tcp_wmem设为4096 87380 16777216,net.ipv4.tcp_congestion_control设为bbr,net.ipv4.tcp_fastopen设为3,并开启net.ipv4.tcp_slow_start_after_idle=0。OpenWrt上还需在防火墙关闭多余conntrack超时、调整net.netfilter.nf_conntrack_tcp_timeout_established,避免长连接被提前回收。改完用sysctl -p生效,再用ss -ti观察重传和cwnd变化,确认调优真正起作用。