從 4G 時代開始,蜂窩網絡的語音功能不再支持傳統的分組交換系統,而是以 IP 網絡替代。這意味著,目前在純 LTE 或更高級網絡(NR)上的語音通話本質上都是 VoIP 實現,這就帶來了一個機遇:為什麼一定要在運營商的蜂窩網絡內實現 VoIP 呢?我們完全可以在外部網絡訪問這一基於 IP 的核心網並使用移動語音和多媒體功能。這就是 UMA(Unlicensed Mobile Access),也就是我們今天所知道的「Wi-Fi Call / VoWiFi」。
Wi-Fi Call 的本質是建立一個到運營商 GAN(通用接入網)的 IPSec 隧道,並且由此連接到 IMS 核心網來傳輸語音和多媒體業務。
現狀#
要使用 Wi-Fi Call 非常簡單,對於 iOS,我們只需要在設置 > 蜂窩網絡 > 運營商 > Wi-Fi 通話中啟用即可。而 Android 則由於系統碎片化而存在差異,但一般是在「通話」App 的設置中打開 Wi-Fi 通話。
要注意的是,運營商並不總是(或者說,永遠不)平等對待客戶。與 eSIM 類似,很多運營商只為後付費客戶(準確來說是有長期服務合約的客戶)提供這一功能,而預付費客戶則無法使用。
當你身處當地時,Wi-Fi Call 的唯一作用可能只是幫助你在蜂窩信號不佳的地區獲得穩定的通話和短信,只有極少數運營商可能為使用 Wi-Fi Call 的客戶提供區別於蜂窩網絡的優惠套餐。
然而,當你身處海外時情況就有所不同了。由於 Wi-Fi Call 直接接入運營商核心網,避免了漫遊,因此可以幫你省下一筆可觀的漫遊費用。然而對運營商來說,這就導致他們損失了一筆收入。這導致他們會設下限制以阻止你在海外使用 Wi-Fi Call。某些運營商也可能因為當地監管原因或其他因素阻止他們的客戶在特定地區使用 Wi-Fi Call。
改變#
運營商如何得知你的位置?一般而言主要有兩種:一是移動設備內置的訪問限制,二是你訪問運營商 IMS 時的 IP Geo 信息。
移動設備限制#
現代的 iOS 和 Android 設備都會有每個運營商提供的網絡配置文件,在其中定義了有關運營商和網絡連接的各種信息。例如是否允許連接到 4G / 5G 網絡、網絡接入點(APN)、網絡運營商顯示名稱。而 iOS 的 IPCC(iOS Carrier Profiles)則提供了更多功能,這其中就包括運營商對 Wi-Fi Call 的區域限制。
例如,香港的運營商一般都限制只能在香港內使用 Wi-Fi Call。你可能會想我開一個香港的代理去連接運營商核心網不就行了嗎?但實際上你會發現無論你用什麼代理都不會連上 Wi-Fi Call。這是因為 iOS 中的 IPCC 限制了只允許設備認為當前處於香港時才會嘗試去連接 Wi-Fi Call,而 iOS 的「地區檢測」其實與 GPS 無關,而是另外一套判斷機制:
- 對於有蜂窩網絡連接的設備,通過基站信息判斷位置
- 對於沒有蜂窩網絡連接的設備,通過網絡信息判斷位置
當你有一個已經連接到蜂窩網絡的設備時,iOS 很容易會從基站獲取到 MNC / MCC,便可以知道你目前所處的地區。而當你使用不具有蜂窩網絡功能的設備,或是處於無網絡 / 飛行模式等狀態時,iOS 就會採用基於網絡的地區檢測,具體來說,是通過 https://gspe1-ssl.ls.apple.com/pep/gcc 所返回的國家代碼來判斷。
因此,當我們把設備置於飛行模式,並且讓 Apple 的網絡地區檢測 API 通過我們指定地區的網絡代理去訪問時,就可以繞過運營商設置在我們設備上 Wi-Fi Call 限制。
那麼有沒有更直接的方案呢?有,只需要在設置 > 隱私和安全性 > 定位服務 > 系統服務中,關閉「Wi-Fi 通話」(中國大陸銷售機型上被稱為「WLAN 通話」)的定位權限即可。當然,你仍然需要讓手機處於飛行模式以繞過基於蜂窩網絡基站的地區檢測。
2023-08-25 更新:經過後續實踐,此選項並不影響 iOS 的 WiFi Call 檢測,推測此功能可能是用於 e-911 的地理位置檢測和傳輸。
GAN 接入限制#
由於設備限制大多數情況下只在 iOS 設備有效,部分運營商為了更徹底的限制,還在 GAN 接入時限制了連入的 IP Geo。例如,香港的 ClubSIM 不但在 IPCC 中限制了僅限設備在香港時才能接入,還限制了只有香港 IP 才可以訪問。
此外,在中國大陸的讀者還需要擔心國家網絡防火牆(GFW)問題。2022 年下半年 GFW 曾經屏蔽了部分 T-Mobile 的 Wi-Fi Call 接入點的 IP 地址,導致很多人的 Wi-Fi Call 時斷時續甚至根本無法連上。不得不採用各種辦法來尋找並嘗試連上那些還沒有被屏蔽的 IP。
繞過 IP 限制當然很簡單,我們只需要網絡代理就可以解決。但是在默認情況下,Wi-Fi Call 的 IPSec 流量並不通過本機 VPN(包括傳統 VPN 和應用程序創建的 VPN 接口)進行路由。Android 用戶可以在 root 後通過 iptables 解決此問題,但 iOS 在 16.4 之前沒有辦法做到這一點,不過事情在 iOS 16.4 之後發生了變化。iOS 提供了新的 API 以允許將蜂窩 IP 服務的流量通過 Network Extension 路由,而 Surge for iOS 率先支持了這一點。如果讀者同時滿足 iOS 16.4+ 及擁有 Surge 最新功能訂閱的情況下,只需要添加如下 Module 即可實現:
(2023-08-25 更新:目前此功能在新版 Surge 上不做工,退回到最初添加此功能的 Surge 版本也不做工,懷疑 iOS 可能又限制了 NE 對系統底層連接的接管)
#!name= Enable Wi-Fi Call for Local VPN
#!desc= 允許在本地 VPN 上代理 Wi-Fi Call 流量
#!system=ios
[General]
include-all-networks = true
include-cellular-services = true
如果你希望更精細化地控制 Wi-Fi Call 流量,例如在雙卡手機上使用了不同國家的 SIM 卡的情況。那麼只需要參考下文的 Surge 分流規則即可。
對於設備本身就在使用網關代理(網關層面的透明代理,包括各種旁路由方案)的讀者而言,只需要在網關代理上添加幾條規則即可實現。但如果你因為各種原因不願意設備的流量全部通過網關代理,那麼我們就需要一點小小的路由魔法。
策略路由#
在本文的最前面提到,Wi-Fi Call 的本質是一個 IPSec 隧道,而 IPSec 隧道使用的是 500 UDP 和 4500 UDP 端口,我們只需要將這兩個端口的流量在網關上進行標記,並轉發給代理或旁路由就可以了。如果你使用旁路網關代理方案,並且主網關是基於 Linux 的控制平面,那麼可以參考本文作者的方案。本文作者使用 UniFi Dream Machine Pro 作為主網關,使用 Surge for Mac 作為旁路代理。但如果你理解了這部分內容,其思路對任何設備都是相通的。
#!/usr/bin/env bash
# Pre Env
touch /run/routing-zero-wifi-call.lock
if [ -f /run/routing-zero-wifi-call.lock ]; then
iptables -t mangle -D PREROUTING -p udp -m multiport --dports 500,4500 -s 10.1.1.0/24 ! -d 10.1.1.0/24 -j WIFI_CALL
iptables -t mangle -F WIFI_CALL
iptables -t mangle -X WIFI_CALL
ip r flush table wificall
ip ru d fwmark 0x65
sed -i "/wificall/d" /etc/iproute2/rt_tables
fi
# Create Routing Table
echo "101 wificall" >> /etc/iproute2/rt_tables
# Create Routing Rules
ip ru a fwmark 0x65 lookup wificall
ip r a default via 10.1.1.233 dev br0 table wificall
# Set Firewall Mark
iptables -t mangle -N WIFI_CALL
iptables -t mangle -A WIFI_CALL -s 10.1.1.233/32 -j RETURN
iptables -t mangle -A WIFI_CALL -p udp -m multiport --dports 500,4500 -j MARK --set-mark 0x65
iptables -t mangle -A PREROUTING -p udp -m multiport --dports 500,4500 -s 10.1.1.0/24 ! -d 10.1.1.0/24 -j WIFI_CALL
這其中,10.1.1.0/24
是 LAN 網絡的 IP 段,10.1.1.233
是 Surge for Mac 所在設備的 IP 地址。我們的思路就是讓整個子網下,除旁路代理網關發送的,所有目標端口為 500 和 4500 UDP 端口且目標 IP 不是本地網絡的流量,都轉發給旁路網關處理。方式是給所有滿足上述條件的流量設置防火牆標記(FwMark),並且添加策略路由來讓主網關把有這些標記的流量轉發給旁路代理網關。
之後我們需要為代理程序添加一些規則,來使得所有 500 UDP 和 4500 UDP 的流量根據其目標 IP Geo 來使用對應國家的代理來轉發,以本站作者使用 US / HK / UK 卡的 Wi-Fi Call 為例:
[Proxy Group]
US Wi-Fi Call = select, US Proxy 1, US Proxy 1
HK Wi-Fi Call = select, HK Proxy 1, HK Proxy 2
UK Wi-Fi Call = select, UK Proxy 1, UK Proxy 2
...
[Rule]
AND,((GEOIP,US),(AND,((PROTOCOL,UDP),(OR,((DEST-PORT,500),(DEST-PORT,4500)))))), US Wi-Fi Call
AND,((GEOIP,HK),(AND,((PROTOCOL,UDP),(OR,((DEST-PORT,500),(DEST-PORT,4500)))))), HK Wi-Fi Call
AND,((GEOIP,GB),(AND,((PROTOCOL,UDP),(OR,((DEST-PORT,500),(DEST-PORT,4500)))))), UK Wi-Fi Call
...
Clash 和其他主流的代理軟件都有類似的邏輯判斷規則,可以嘗試自行修改使用。
IMS 狀態#
要注意的是,部分運營商,例如香港全部運營商(CSL 系除外,包括 ClubSIM 等子品牌),都只實現了語音接入,而沒有接入多媒體系統。這代表當你完全沒有接入到蜂窩網絡時,你是無法收到短信的!這些運營商非常字面意義地給客戶實現了 Wi-Fi「Call」。
在設置 > 通用 > 關於本機中,點擊「運營商」就可以切換顯示信息,並可以在 Wi-Fi Call 狀態下查看 IMS 狀態。
以下為 2023.04.14 更新
世事無常,在發布了 iOS 16.4 更新後同時更新了 CMHK 54.0 的 IPCC,該 IPCC 允許 CMHK 在 Wi-Fi Call 狀態在收發短信,終於變成了完整體!