本篇文章與所涉及的路由優化解法,僅適用於台灣的中華電信(HiNet)家用寬頻環境。如果您使用的是其他國家、地區或不同的 ISP(網際網路服務供應商),因國際骨幹與 Peering 狀況完全不同,請勿盲目套用此設定!
開頭 #
種花電信的 HiNet 對 Fastly 和 Cloudflare 的 peering 有多爛,是最近中華電信用戶們一直討論的老問題了:同一條中華電信的家用固網,GitHub 下載走 IPv4 可以龜到零點幾 MB/s,換 IPv6 又快得像另一家 ISP,還有 Cloudflare CDN 節點永遠分配不到台灣節點、X 圖片 CDN 卡到爆等等問題。
Cloudflare 或 Fastly CDN 與中華電信(HiNet)長達十年的愛恨情仇,核心全圍繞在「對等互連(Peering)費用談不攏」以及「國際頻寬路由繞道(Routing Detour)」。
Cloudflare 甚至還在 Blog 中抱怨過台灣中華電信 (Hinet) 的網路運輸成本是歐美的15倍:https://blog.cloudflare.com/bandwidth-costs-around-the-world/。
“Two Asian locations stand out as being especially expensive: Seoul and Taipei. In these markets, with powerful incumbents (Korea Telecom and HiNet), transit costs 15x as much as in Europe or North America, or 150 units.”
解決方案 #
目前有人測試過且我最想要的解法是把「去這些爛路的流量」送進 Cloudflare 自己的邊緣網路——WARP 隧道,而且其他正常流量完全不動,避免引發其他連線問題。
環境:小米 AX3000T(搭載經過刷機的 OpenWrt 24.10.5)、pbr 1.2.2。前提是你手上有一個 Cloudflare Zero Trust 組織,或可以直接使用 CF warp 免費版的 wgcf,但本文使用 wgcf-teams。
我的實際體驗是:Cloudflare Warp Wireguard 隧道本身十五分鐘就能建好,真正花時間的是它的 OpenWrt 分流,搞壞好幾次。
架構總覽 #
隨便一台電腦 --(wgcf-teams-go 註冊)--> Zero Trust 組織 --> 生成 warp-zt.conf (WireGuard config file)
路由器: warp 介面 (WireGuard) + 專屬防火牆 zone (NAT44 + NAT66)
pbr: CDN-via-WARP (IPv4 目的地清單) --\
CDN-via-WARP6 (IPv6 目的地清單) ----> 出口 Cloudflare warp
其餘流量: 原生路徑 (中華電信寬頻 wan)
步驟一:用 wgcf-teams 產生 WireGuard config #
工具的 GitHub:https://github.com/poscat0x04/wgcf-teams
Zero Trust 後台沒有「匯出 WireGuard 設定給路由器」的功能,它預設所有裝置都裝官方 App。社群工具 wgcf-teams-go 補了這個洞:它用你的 ZT 身份在組織裡註冊一台裝置,直接吐出一份標準 WireGuard config。
先確認 ZT 後台(Settings > WARP Client > Device enrollment)裡自己的登入身份有註冊裝置的權限,並記下 team 名稱——就是登入網址 https://<team>.cloudflareaccess.com 的子網域。
wgcf-teams-go 沒有預編譯的 release,只能用 Go 裝:
# 如果你的電腦上沒有安裝 go 的話記得先安裝
sudo apt install golang-gogo install github.com/PeronGH/wgcf-teams-go@latest產生 wireguard config(<team> 換成自己的 team 名稱):
~/go/bin/wgcf-teams-go <team> > ~/warp-zt.conf
chmod 600 ~/warp-zt.conf執行時它會開瀏覽器到 https://<team>.cloudflareaccess.com/warp,用 ZT 帳號登入後,頁面會給一串 com.cloudflare.warp://...?token=... 的連結(可以右鍵連結按鈕複製),把整串貼回終端機。
注意:token 只有 60 秒壽命,登入完要立刻貼,逾時就重跑一次指令。
產出的 warp-zt.conf 是標準 wg-quick 格式:
# 這只是個示範,請依照實際的 config 為準喔!
[Interface]
PrivateKey = <私鑰,只留在這個檔案裡>
Address = 100.96.0.10/32
Address = 2606:4700:cf1:1000::a/128
MTU = 1420
[Peer]
PublicKey = bmXOC+F1FxEMF9dyiK2H5/1SUtzH0JuVo51h2wPfgyo=
AllowedIPs = 0.0.0.0/0, ::/0
Endpoint = engage.cloudflareclient.com:2408還是提醒一下:PrivateKey 是這條隧道的憑證,不要分享給別人(AI 也最好不要)。
步驟二:路由器安裝 WireGuard 並建立 warp 介面 #
進入 OpenWrt 的 LuCI 的 System > Software 先 Update lists,安裝 luci-proto-wireguard(連帶 kmod-wireguard、wireguard-tools)。接著 Network > Interfaces 新增介面 warp,protocol 選 WireGuard,把剛剛生成的 wg config 的欄位搬過去:
(若 protocol 沒有出現 WireGuard,可以先重載 webui 或重啟路由器喔!)

設定頁面最下方可以直接點選 import config,然後把設定檔整個貼上 LuCI 就可以,它會自己解析並填入喔!
重點:Allowed IPs 0.0.0.0/0 ::0/0 + 不勾 Route Allowed IPs,是 pbr 模式的固定搭配。前者讓隧道願意接受 pbr 送來的任何目的地;後者不勾,netifd 才不會把預設路由整條裝進主路由表、把所有裝置都捲進隧道。分流的決定權留給 pbr。
防火牆這邊,直接給 warp 一個專屬 zone(不要借 wan zone,理由在踩坑四):
zone warp:
input REJECT / output ACCEPT / forward REJECT
masq 開、masq6 開、mtu_fix 開
Covered networks: warp
然後在流量規則的頁面增加一條規則允許 lan -> warp

存檔套用後,WireGuard 狀態上出現 Latest handshake、收發數字開始跳,那麼隧道應該就通了。

步驟三:準備目的地清單 #
CDN 的 IP 清單來源全部是官方的:
- Cloudflare IPv4 / IPv6:官網 IP 列表(https://www.cloudflare.com/ips/),15 段 v4、7 段 v6
- Fastly:官方 API https://api.fastly.com/public-ip-list,21 段 v4、3 段 v6
- GitHub:
185.199.108.0/22和2606:50c0::/32——這兩段不在上面任何一份清單裡,但 GitHub 的 release 下載實際走這裡,不加等於白做
目前是合計 37 段 IPv4、11 段 IPv6。
步驟四:pbr 雙政策 #
pbr,全稱是 Policy Based Routing 原則型路由,一種決定路由的方式,由網路管理者決定路由原則,再根據這些原則來決定路由。(維基百科)
OpenWrt 安裝套件就可以實現這個功能。
前提:pbr 已安裝(套件 luci-app-pbr),且 pbr 的 IPv6 support 已開啟(LuCI 在 pbr 的設定頁,或 uci 的 pbr.config.ipv6_enabled='1')。
政策建兩條(LuCI 在 Services > Policy Routing 新增):
CDN-via-WARP src 192.168.0.0/24 -> dest 37 段 IPv4 -> interface warp
CDN-via-WARP6 src (留空) -> dest 11 段 IPv6 -> interface warpv6 政策的來源故意留空:lan 的 IPv6 前綴會隨種花電信重撥而改變,綁來源等於埋一顆定時炸彈;「通常」家裡只有 lan 有 IPv6,留空等效於只套用 lan。
或也可以使用 UCI 一次建立(SSH 登入路由器後整段貼上):
uci add pbr policy
uci set pbr.@policy[-1].name='CDN-via-WARP'
uci set pbr.@policy[-1].interface='warp'
uci set pbr.@policy[-1].src_addr='192.168.0.0/24'
uci set pbr.@policy[-1].dest_addr='173.245.48.0/20 103.21.244.0/22 103.22.200.0/22 103.31.4.0/22 141.101.64.0/18 108.162.192.0/18 190.93.240.0/20 188.114.96.0/20 197.234.240.0/22 198.41.128.0/17 162.158.0.0/15 104.16.0.0/13 104.24.0.0/14 172.64.0.0/13 131.0.72.0/22 23.235.32.0/20 43.249.72.0/22 90.98.0.0/17 90.98.128.0/18 103.244.50.0/24 103.245.222.0/23 103.245.224.0/24 104.156.80.0/20 140.248.64.0/18 140.248.128.0/17 146.75.0.0/17 151.101.0.0/16 157.52.64.0/18 167.82.0.0/17 167.82.128.0/20 167.82.160.0/20 167.82.224.0/20 172.111.64.0/18 185.31.16.0/22 199.27.72.0/21 199.232.0.0/16 185.199.108.0/22'
uci add pbr policy
uci set pbr.@policy[-1].name='CDN-via-WARP6'
uci set pbr.@policy[-1].interface='warp'
uci set pbr.@policy[-1].dest_addr='2400:cb00::/32 2606:4700::/32 2803:f800::/32 2405:b500::/32 2405:8100::/32 2a06:98c0::/29 2c0f:f248::/32 2a04:4e40::/32 2a04:4e42::/31 2a04:4e44::/32 2606:50c0::/32'
uci commit pbr
/etc/init.d/pbr restart重啟輸出裡兩條政策都要是 [✓]、沒有 ERROR。
踩坑一:pbr 的一條政策只能處理同一個 IP 家族。 我最初把 48 段全塞進同一條政策(v4 來源 + v4/v6 目的地混填),重啟後 11 段 IPv6 全部報
Unknown entry in policy 'CDN-via-WARP',v4 卻安安靜靜。翻了 pbr 的 init script 原始碼才定案:它的處理迴圈把來源與目的地按家族分組,來源是 IPv4 群組時直接跳過 IPv6 的目的地群組;輪到 IPv6 來源群組時我的來源篩完是空的、迴圈根本不跑,於是 v6 條目沒有被任何一組處理到,最後統一列為 Unknown。pbr 文件沒寫這個規則,但解法就是拆成上面這對政策。
踩坑二:pbr 的 IPv6 support 預設是關的。 我在改目的地分流之前,先試過「整台筆電走 WARP」的來源政策,當時 IPv6 support 沒開,政策只抓 IPv4,而筆電和瀏覽器都優先走 IPv6——結果大半流量從隧道旁邊溜走、繼續走原生 v6(trace 顯示來源是 HiNet IPv6、
warp=off、colo 還在 SIN),瀏覽器看起來全滅,但 ping 閘道、dig、nslookup 全部正常,差點誤判成 DNS 故障。順帶一提,這段期間還混著一個小插曲:pbr 的uplink_interface6預設值是wan6,但我家的介面叫wan_6,不對齊的話 v6 的路由設定會跟著出怪聲。結論:IPv6 support 先開、介面名先對,再談政策。另外也學到:全機進隧道本來就是過度設計,才改走目的地分流。
踩坑三:如果 DNS Server 不是 dnsmasq,不要用 pbr 的網域模式。 pbr 用網域名稱做分流,靠的是 dnsmasq 把解析結果餵進 nft set;我家的 DNS 是 AdGuard Home 在 :53 回答、dnsmasq:5353 只管 lan 網域,pbr 永遠等不到資料。這篇全程用 IP 段模式。若你的 DNS 也是外包給 AdGuard / Pi-hole 這類服務,要注意喔嘿嘿!
步驟五:驗證 #
政策全綠只是設定層的勝利,封包層要另外驗。用任何一個在 Cloudflare 段內的網站的 trace 做 -4/-6 對照(我用自己的 www.aiixi.cc):
curl -4 -s https://www.aiixi.cc/cdn-cgi/trace | grep -E 'ip|colo|warp'
# 預期: ip=[CF 的 IPV4]、warp=on
curl -6 -s https://www.aiixi.cc/cdn-cgi/trace | grep -E 'ip|colo|warp'
# 預期: ip=[CF 的 IPV6]、warp=on
兩邊的來源都應該是Cloudflare Warp 的 IP而不是你家的種花電信 IP 位址。最後再找一個 GitHub release 的大檔實測下載速度,跟未分流前比較(種花電信直連 Fastly 先前量到的量級約 0.35–0.45 MB/s(當然,也不一定,降速是隨機的))。
這樣一來,Cloudflare CDN 也不會分配給你新加坡、日本或香港這種太遠的節點了,如果分流正常,會直接分配台北的 Cloudflare node 給你,網頁、圖片等等資源載入真的會稍微加快。
你現在正瀏覽的、我的這個網站 footer 也會顯示你連線的 Cloudflare 節點,如果顯示 伺服器:台灣台北 (TPE) 則代表分流大概運作正常。
踩坑四:IPv6 進隧道還需要 NAT66,否則會靜默死亡。 這是我最後採到、也最陰險的一個:政策全綠之後,清單內的網站 v4 通、v6 逾時。原因是 v4 封包進隧道前會被 masquerade 改寫來源成隧道位址(100.96.0.10),Cloudflare 認得、回程找得回來;但 IPv6 預設沒有 NAT,封包帶著 HiNet 的來源位址(2001:b011:…)進隧道,而 WireGuard 的 cryptokey routing 只認對端註冊的隧道位址,來源不符就在 Cloudflare 那端直接丟棄,連錯誤都沒有。修法就是步驟二那個專屬 zone 的
masq6:讓 v6 出隧道前也被改寫成隧道位址。所以不要偷懶沿用 wan zone——它的 masq 只有 v4 那一半。
參考資料 #
[From Rotaeno] Yooh - Secret Illumination [Official Audio]