CTExcel UK 大陆激活:链路搭建与逐层排查记录
论坛不让发链接,涉及的网址都改成了文字描述,命令里的接口地址去掉了协议头,
自己补上 https 前缀就能用。域名要是被系统吞了,按文中给的格式拼一下。
当前进展:IMS 在 IWLAN 上注册成功(registrationState=HOME)、能收运营商短信、
拨号能到对端并被响应,激活短信还没收到。
测试环境
- 承载机(插卡):Android 16,联发科平台,接近原生的 ROM,支持 eSIM
- 网关:已 root 的 Android 手机
- 节点:伦敦 VPS,Debian 13,1 核 512MB,sing-box 跑 Hysteria2
- 卡:CTExcel UK eSIM,MCC/MNC = 23433
- 工具:adb、tcpdump、curl
下面的命令和现象都出自这套环境,没试过的会标出来。
一、原理
CTExcel UK 是跑在 EE 上的 MVNO(经 Transatel),SIM 上报 MCC/MNC = 23433。
新卡出厂时语音服务没开通、号码也没下发,要在英国本地网络上产生一次使用才会开户。
人在大陆收不到 EE 信号,入口是 VoWiFi。它的流量结构是:
设备 --IKEv2 (UDP 500)--> 运营商 ePDG 网关
--NAT-T 后 ESP 封装进 UDP 4500-->
隧道建立后,隧道内跑 SIP REGISTER 完成 IMS 注册
注册成功才谈得上打电话、发短信
两条硬要求:
- 得是 UDP,500 / 4500 不能被屏蔽。不少机场的服务端压根不转发 UDP。
- ePDG 域名得解析成真实 IP。IPsec 要对端的真实地址,解析到 fake-ip 段
(198.18.0.0/16)就建不起来。
IP 是不是家宽/原生,论坛上说法不一,有人用公共节点成功、也有人用英国家宽半个月没成。
这套链路用的是数据中心 IP,隧道正常建起来了。
ePDG 域名构造规则固定,换运营商照套:
epdg.epc.mnc<MNC 补足三位>.mcc<MCC>.pub.3gppnetwork.org
CTExcel (23433) -> epdg.epc.mnc033.mcc234.pub.3gppnetwork.org
Vodafone UK (23415) -> epdg.epc.mnc015.mcc234.pub.3gppnetwork.org
查真实 IP 用 DoH,别用本机 dig(原因见坑 5):
curl -s "1.1.1.1/dns-query?name=epdg.epc.mnc033.mcc234.pub.3gppnetwork.org&type=A" -H 'accept: application/dns-json'
(前面补上 https 协议头)写这篇时返回 31.94.76.1 / .2 / .6 / .9 / .10,是 EE 的 ePDG 集群。
地址会变,用时现查。
二、链路架构
承载机(插卡,飞行模式 + WiFi,定位改到伦敦)
| WiFi
网关(root 安卓,全局代理 + 转发,出口在英国)
| Hysteria2 (QUIC / UDP)
英国 VPS
|
ePDG (UDP 500 & 4500)
分两台设备是因为承载机得整机流量都走英国。在它自己身上装代理 app,能不能盖住
modem 发起的 IPsec 流量要看这台机器 IWLAN 数据栈的实现,有些实现会绕过系统的
VpnService。放到设备外面转发就没这个不确定性。
三、节点与协议
实测过两种:
- Trojan over TLS(机场节点):UDP 上下行都是 0 字节,隧道建不起来
- Hysteria2(自建):正常,双向 ESP
Trojan 那次的连接记录:
udp://epdg.epc.mnc033.mcc234.pub.3gppnetwork.org/31.94.76.1:4500 0B 上 0B 下
udp://epdg.epc.mnc033.mcc234.pub.3gppnetwork.org/31.94.76.1:500 0B 上 0B 下
同一时刻同一节点的 TCP:
tcp://mtalk.google.com/...:5228 1KB 上 1.6KB 下
tcp://.../443 134KB 上 17.7KB 下
TCP 有几百 KB 流量,UDP 一个字节都出不去。会话记录建了,包没送出去,
服务端没开 UDP 转发。换成 Hysteria2 之后,同一台承载机立刻建起了隧道。
别的协议没试。Hysteria2 / TUIC 是 QUIC 原生 UDP,Shadowsocks 有 UDP relay,
VLESS / VMess 看服务端配置。
自建的话,伦敦机房的 VPS 按小时计费(0.006 到 0.007 美元一小时),装 sing-box 跑
Hysteria2,激活完删掉,整件事不到 1 美元。配置拿最低档就够,这条链路传的是 IKE
协商包和 SIP 信令,数据量以 KB 计。
服务端两点:
-
outbound 强制 IPv4。sing-box 1.12 起 outbound 的 domain_strategy 已废弃,
还写旧字段会直接 FATAL 起不来,要写成:
"outbounds": [{
"type": "direct",
"domain_resolver": { "server": "local", "strategy": "ipv4_only" }
}]
-
防火墙放行 Hysteria2 自己的 UDP 端口就行。出站默认全通,不用为 500/4500
单独开洞,那是节点出去的方向。
四、客户端配置
DNS:ePDG 得解析出真实 IP
fake-ip 模式下是这样:
$ ping epdg.epc.mnc033.mcc234.pub.3gppnetwork.org
PING ... (198.18.0.127) <- 假地址,IPsec 建不上去
两种改法都试过,ePDG 都能解析到真实地址。
改法 A,保留 fake-ip、放行 3gpp 域名:
dns:
enable: true
ipv6: false
enhanced-mode: fake-ip
fake-ip-filter:
- "+.3gppnetwork.org"
改法 B,全局改成 redir-host:在客户端把 DNS 模式从 fake-ip 改成 redir-host。
注意 FlClash 里「覆写 DNS」是总开关,DNS mode 是它下面的一项。总开关关着的时候,
设的 redirHost 不生效,走的是订阅文件里的 dns 段。
IPv6
客户端 ipv6: false,服务端 outbound 强制 IPv4。承载机要是拿到全局 IPv6,
就存在绕过隧道直连的路径,浏览器里查到的是英国 IP,IMS 流量却从本地出去了。
规则(只有规则模式需要)
全局模式下 rules 整段不参与选路。规则模式的话这几条放在 GEOIP,CN 前面:
- DOMAIN-SUFFIX,3gppnetwork.org,PROXY
- IP-CIDR,31.94.76.0/24,PROXY,no-resolve
- DST-PORT,500,PROXY
- DST-PORT,4500,PROXY
Surge 的等价写法:
AND,((GEOIP,GB),(AND,((PROTOCOL,UDP),(OR,((DEST-PORT,500),(DEST-PORT,4500))))))
五、网关
这一层的目标:承载机连的那个 WiFi,公网出口在英国。
用的是 root 安卓机加 VPN Hotspot。论坛上也有人拿电脑开热点配 TUN 全局、或者用
软路由旁路由做这一层,原理一样,没试过,配法看对应的帖子。
安卓自带的热点和 USB 共享,流量是绕过 VPN/tun 的,连热点的设备走的是裸的上行。
要让热点客户端也走代理得用 VPN Hotspot(开源,需要 root,GitHub 上搜 Mygod/VPNHotspot,
F-Droid 包名 be.mygod.vpnhotspot_foss)。应用商店里有蹭名字的同名应用,
包名认准 be.mygod.vpnhotspot。
用法:网关机上代理客户端开全局模式加英国节点,开系统热点,然后在 VPN Hotspot 的
Tethering 列表里把对应接口(这台是 wlan2)的开关打开。
开发者选项里的「网络共享硬件加速」要关掉,VPN Hotspot 界面上也提示了这条。
开着的时候部分转发会被硬件 offload、绕过 iptables 规则,现象有迷惑性:
网页查 IP 显示英国、看着一切正常,但部分流量直连出去了。
配好之后承载机连上,浏览器开 ipinfo 那类 IP 查询站看国家。
六、承载机配置
定位
论坛上的成功案例多数都改了定位。iOS 那边用的是 WLOC 类工具,手头没 iOS 设备,
用的是 Android 自带的模拟位置:
- 装 FakeTraveler(开源,F-Droid 包名 cl.coders.faketraveler)
- 开发者选项 -> 选择模拟位置信息应用 -> FakeTraveler
- app 里选点,勾上持续 mock,Apply
- 系统定位得打开(location_mode=3),关着的话模拟位置不生效
- 电池设置里设成「不受限制」,免得被系统杀掉
坐标用的是节点出口 IP 自己报的位置(IP 查询站返回的 loc 字段),这样定位跟 IP 归属对得上。
Android 的模拟位置只管 GPS / fused provider,不改 Wi-Fi 扫描到的 BSSID 列表。
Google 的网络定位用的正是周围 WiFi 的 BSSID,所以还得去 设置 -> 位置 -> 位置服务,
把 WLAN 扫描和蓝牙扫描都关了。
飞行模式
论坛主流经验是全程开着飞行模式。这台(联发科平台)测下来是反的:
- 开飞行模式:节点侧抓包 10 秒 0 个包,registrationState 退回 UNKNOWN
- 关飞行模式:隧道持续活跃
但最后把状态推到 HOME 的那一次,恰恰是开着飞行模式的(同时配了模拟定位)。
两种配置的表现都记录过,没看出确定的规律。
其他
- 关蓝牙
- 系统语言、地区、时区设成目标国
- Private DNS 设成「关闭」。默认的 opportunistic 会尝试对上游做 DoT,
存在绕开代理 DNS 处理的路径
激活流程
下面 10 步是综合论坛多个成功案例的共性顺序:
1. 关闭 GPS,或设好模拟定位
2. 连上网关热点,确认出口 IP 在目标国
3. 关闭要激活的那张卡(eSIM 就是关掉 profile 开关)
4. 关闭 Wi-Fi Calling 选项
5. 关机(不是重启)
6. 开机,连上热点,确认能上网
7. 打开卡
8. 打开 Wi-Fi Calling
9. 等 VoWiFi 标识出现(10 秒到几分钟)
10. 再等 2 分钟,拨 888
实际走的是第 1、2、3、5、6、7、10 步。第 4 和第 8 步没做,Wi-Fi Calling 的开关
全程一直开着,没关掉再打开,HOME 就是在这个顺序下拿到的。
论坛上多数成功案例的描述是「888 没打通、秒挂断或 busy,但拨号那一刻激活短信就来了」,
也就是说结果的标志是激活短信,不是 888 通没通。
七、逐层排查
这条链路有七八层,中间断了哪一层,表现出来都是同一个症状:WiFi Calling 拉不起来。
下面是每层用什么看的,以及看到什么算通、什么算不通。
论坛上的成功配置互相打架的地方不少(飞行模式开还是关、要不要改定位、家宽还是机场),
这套环境也没归纳出规律,只能一层层确认。这些检查点也可以直接丢给 AI,
让它对着具体环境逐条过。
第 1 层:出口 IP
承载机浏览器打开 IP 查询站看国家。顺带两个泄露点:test-ipv6 那个站看有没有全局
IPv6,browserleaks 的 dns 页看 DNS 有没有落到本地解析。
第 2 层:ePDG 解析
adb shell ping -c1 epdg.epc.mnc033.mcc234.pub.3gppnetwork.org
返回 31.94.76.x 这类真实地址就是通了;返回 198.18.x.x 说明 fake-ip 没绕过去。
ePDG 一般不回 ICMP,看到「无回应」是正常的,这儿只看解析出来的地址。
第 3 层:IKE 到底送出去没有,节点侧抓包
SSH 到 VPS:
tcpdump -i any -n "udp port 500 or udp port 4500"
隧道正常建起来是这样:
Out <节点IP>.41637 > 31.94.76.1.4500: UDP-encap: ESP(spi=0x040201da,seq=0x3), length 1300
In 31.94.76.1.4500 > <节点IP>.41637: UDP-encap: ESP(spi=0xbe6d9dea,seq=0x3), length 100
Out ... NONESP-encap: isakmp: child_sa inf2[I]
In ... NONESP-encap: isakmp: child_sa inf2[R]
怎么读:
- 双向都有 ESP,说明 IKE 协商完成,进入数据阶段了
- SPI 成对(出站一个、入站一个),说明 Child SA 建起来了
- seq 连续递增,说明隧道在正常传
- child_sa inf2[I] 和 [R],说明 INFORMATIONAL 交换有请求有响应
不通的话:只有 Out 没有 In,是包出去了对端不理,查下 IP 地理位置或者节点是不是被墙;
一个包都没有,是根本没送出去,回去看协议。
第 4 层:IMS 注册状态
adb shell dumpsys telephony.registry | sed -n "1,80p" | grep -oE "transportType=WLAN registrationState=[A-Z_]+|mOperatorAlphaLong=[A-Za-z]*"
得取输出的前段(head 或 sed -n "1,80p")。dumpsys telephony.registry 前面是当前状态
(mServiceState),后面几百行是历史事件日志,用 tail 会读到历史记录里某个瞬时状态。
- registrationState=HOME 加 mOperatorAlphaLong=运营商名:IMS 在 IWLAN 上注册成功
- NOT_REG_SEARCHING:正在尝试
- UNKNOWN 加 mOperatorAlphaLong=null:没在注册
第 5 层:定位到底生效没有
adb shell dumpsys location | grep "last location"
四个 provider(fused、network、gps、passive)都该显示设的坐标,并且带 mock 标记。
输出里的 et=+4d1h5m 是 elapsed-since-boot,不是「多久之前」。要跟
cat /proc/uptime 对比,et 接近 uptime 说明位置是刚注入的。一台连续开机四天的设备
显示 et=+4d,意思是「1 分钟前」。
手动触发 IMS 重新注册
不用重启手机(-s 后面是 SIM 槽位,双卡机注意选对):
adb shell cmd phone ims disable -s 1
adb shell cmd phone ims enable -s 1
掉线后用这个几十秒就能拉回 HOME,比关机重启快,也不会丢别的状态。
八、踩过的坑
-
机场 Trojan 节点的 UDP 送不出去。连接记录 0B 上 0B 下,同节点 TCP 有几百 KB 流量。
换 Hysteria2 后立刻建起隧道。
-
fake-ip 跟 IPsec 不兼容。ePDG 解析到 198.18.0.127,IKE 无从谈起。
加 fake-ip-filter 或者改 redir-host。
-
「覆写 DNS」是总开关。FlClash 里 DNS mode = redirHost 只在「覆写 DNS」打开的时候
才生效,总开关关着的时候实际走的是订阅里的 fake-ip。
-
网络共享硬件加速会绕过 iptables。开着的时候部分流量 offload 到硬件、
不经过转发规则,现象是各项看着都正常但就是不成。
-
本机 dig 的结果可能是假的。本机开着增强模式的代理(Surge 之类)时,
连 dig @8.8.8.8 都会被拦下来返回 fake-ip。查 ePDG 真实地址得用 DoH。
-
dumpsys telephony.registry 要读前段,用 tail 会读到历史日志。
-
availableServices 不反映 IMS 能力。这字段主要对应蜂窝域的服务状态,
这台机器上它全程只有 [DATA] 和 [EMERGENCY],但设备照样收到了运营商短信、
拨号也能到对端。
-
EMERGENCY 从一开始就在。紧急呼叫走独立通道,不需要 IMS 完整注册,
所以它在不能说明语音通道通了。另外紧急号码会调动真实的应急资源,
不适合拿来测连通性。
-
飞行模式的行为因机型而异。论坛主流经验是必须开,这台(联发科平台)开了之后
IMS 停摆、抓包 0 包。
-
模拟位置管不了 Wi-Fi 定位。FakeTraveler 只改 GPS 和 fused provider,
Google 的网络定位用的是周围 WiFi 的 BSSID,得另外把 WLAN 扫描和蓝牙扫描关掉。
-
飞行模式下发不出短信。Android 短信 app 在飞行模式下直接拒绝发送,
这是系统 UI 层的限制,同一时刻收短信完全正常。
-
eSIM profile 导不出来。一旦下载进设备的 eUICC 安全域就没法提取,
这是 eSIM 的安全设计,换设备只能让运营商重新发放。扫码那一下就把 profile 消耗掉了。
九、当前进展
已经确认通了的:
- UDP 500/4500 转发:节点侧抓包双向 ESP
- IPsec / IKEv2 隧道:Child SA 建立,seq 连续
- ePDG 真实解析
- 出口地理位置
- IPv6 泄露已封堵
- 设备定位伪装:四 provider 全 mock
- IMS 在 IWLAN 注册:registrationState=HOME
- 接收运营商短信:收到 welcome SMS
- 拨号到达对端:888 返回 busy,对端有响应
还没成的:
registrationState=HOME 说明 SIM 的 IMSI 和密钥已经过了运营商鉴权,网络层面各项都对。
剩下的是运营商后台给这张卡开通语音服务。
论坛上成功案例的收尾多数落在时间上:没打通但等了几个小时就成了、挂了两天收到激活短信、
反复关机重启四次才好。
社区还有一条 4G 模组的路线:大疆一代 4G 模块本体是 Quectel EG25-G,改一条 AT 命令
就能被识别成 EC25,配 VoHive 平台用。它能用 AT 命令直接发短信和拨号,不经过 Android
UI 层那道限制,也能看到 SIP 层的响应码。前提是得有一份能写进 eUICC 白卡的 profile,
已经装进手机的那份挪不过去(见坑 12)。这条路线没试过。
十、命令速查
查 ePDG 真实 IP(前面补 https):
curl -s "1.1.1.1/dns-query?name=epdg.epc.mnc033.mcc234.pub.3gppnetwork.org&type=A" -H 'accept: application/dns-json'
承载机,ePDG 解析(该是真实 IP,不是 198.18.x.x):
adb shell ping -c1 epdg.epc.mnc033.mcc234.pub.3gppnetwork.org
承载机,IMS 注册状态(取前段,别用 tail):
adb shell dumpsys telephony.registry | sed -n "1,80p" | grep -oE "transportType=WLAN registrationState=[A-Z_]+|mOperatorAlphaLong=[A-Za-z]*"
承载机,定位注入没有(四 provider 都该带 mock):
adb shell dumpsys location | grep "last location"
adb shell cat /proc/uptime
承载机,模拟位置授权给了谁:
adb shell appops query-op --user 0 MOCK_LOCATION allow
承载机,触发 IMS 重新注册:
adb shell cmd phone ims disable -s 1 && adb shell cmd phone ims enable -s 1
承载机,确认没有全局 IPv6:
adb shell ip -6 addr show scope global
节点侧,抓 IKE / ESP:
tcpdump -i any -n "udp port 500 or udp port 4500"
多台设备同时连 adb 的话用 adb -s 序列号 指定,adb devices -l 能看到型号。