如何利用 Cloudflare WARP 解決 GitHub 下載太慢的問題

家裡 HiNet 連 GitHub 常常很慢。這篇用 wgcf 產生 WARP 的 WireGuard 設定,在 RouterOS v7 上只讓 GitHub 的流量走 WARP,其他照舊走 HiNet,並附上可重跑、可復原的完整設定。

發佈
閱讀
約 6 分鐘
字數
2,556

家裡的網路是 HiNet,唯獨連 GitHub 常常很慢。網頁還勉強能開,但只要碰到 raw.githubusercontent.com、release 檔案這類下載,就很容易卡住。

同樣的東西,開手機熱點或掛上 Cloudflare WARP 就順了。既然 WARP 會快,就讓路由器只把 GitHub 的流量送進 WARP,其他流量完全不動。今天實際部署成功,順手整理成這篇。

之前也寫過一篇用 RouterOS 處理 X 影片轉圈圈的:如何改善網路品質 - X/推特 影片。
這篇是同一台路由器的另一個問題。

環境

  • 路由器:MikroTik RB450Gx4
  • 系統:RouterOS 7.24.2
  • WAN:HiNet PPPoE,介面名稱 pppoe-dynamic-out
  • LAN:interface list 叫 LAN

要 v7 才有內建 WireGuard,v6 用不了。

思路

  • 目的地是 GitHub 的連線,走 WARP。
  • 其他連線,照舊走 HiNet。
  • WARP 掛了,GitHub 的連線自動退回 HiNet。

這就是 policy-based routing,依照連線的目的地決定走哪一張路由表:

flowchart LR
    C["LAN 用戶端"] --> M{"mangle prerouting<br/>目的地在 warp-gh 清單?"}
    M -->|"否"| MAIN["main 路由表"]
    MAIN --> WAN["pppoe-dynamic-out<br/>HiNet"]
    M -->|"是:mark-connection<br/>再 mark-routing"| T["路由表 via-warp"]
    T -->|"distance=1(primary)"| WG["wg-warp<br/>WireGuard"]
    T -.->|"distance=2<br/>WARP 掛掉時"| WAN
    WG --> CF["Cloudflare WARP"]
    CF --> GH["GitHub"]
    WAN --> NET["其他網站"]
    NW["netwatch<br/>ping 1.0.0.1"] -.->|"down 時停用 primary"| T

準備:用 wgcf 拿到 WARP 的 WireGuard 設定

WARP 官方只有 App,路由器裝不了,但底層就是 WireGuard。wgcf 是非官方的開源工具,能註冊一台免費的 WARP 裝置,並匯出標準的 WireGuard profile。在電腦上跑一次就好:

1
2
wgcf register --accept-tos   # 註冊裝置,產生 wgcf-account.toml
wgcf generate # 產生 wgcf-profile.conf

這兩個檔案都含私鑰,別放進 git,也別貼到任何地方。

RouterOS 設定逐段解說

以下依序逐段解說完整設定,數值取自 wgcf-profile.conf,私鑰用 <PRIVATE_KEY> 代替,位址與 endpoint 以你自己的為準。動手前先用 /system backup save 備份一份,弄壞了可以還原。

先清掉舊的,讓設定可以重跑

1
2
3
4
5
6
7
8
9
10
11
12
/tool netwatch remove [find comment~"warp-gh"]
/ip firewall mangle remove [find comment~"warp-gh"]
/ip firewall nat remove [find comment~"warp-gh"]
/ip firewall address-list remove [find comment~"warp-gh"]
/ipv6 firewall filter remove [find comment~"warp-gh"]
/ipv6 firewall filter remove [find comment~"warp-gh"]
/ipv6 firewall address-list remove [find comment~"warp-gh"]
/ip route remove [find comment~"warp-gh"]
/ip address remove [find comment~"warp-gh"]
/interface wireguard peers remove [find comment~"warp-gh"]
/interface wireguard remove [find comment~"warp-gh"]
/routing table remove [find comment~"warp-gh"]

這些指令建立的每個物件都帶 comment=warp-gh(primary 路由是 warp-gh:primary,所以用 ~ 比對)。開頭先全刪再重建,重跑就不會疊出重複規則。順序是先刪引用別人的,最後才刪介面與路由表。

WireGuard 介面

1
2
3
4
5
6
/interface wireguard
add name=wg-warp mtu=1280 listen-port=13232 private-key="<PRIVATE_KEY>" comment=warp-gh
/interface wireguard peers
add interface=wg-warp public-key="bmXOC+F1FxEMF9dyiK2H5/1SUtzH0JuVo51h2wPfgyo=" endpoint-address=engage.cloudflareclient.com endpoint-port=2408 allowed-address=0.0.0.0/0 persistent-keepalive=25s comment=warp-gh
/ip address
add address=172.16.0.2/32 interface=wg-warp comment=warp-gh
  • mtu=1280:wgcf 作者建議的值。WireGuard 要吃 header,外面又是 PPPoE 的 1492,設小一點比較不會被切或被丟。
  • allowed-address=0.0.0.0/0:要轉送任意網站只能全開,哪些流量要進來交給後面的路由表決定。
  • persistent-keepalive=25s:我們在 NAT 後面,定期送封包避免對應過期。

專用路由表與容錯

1
2
3
4
5
6
7
8
/routing table
add name=via-warp fib comment=warp-gh
/ip route
add dst-address=0.0.0.0/0 gateway=wg-warp routing-table=via-warp distance=1 comment=warp-gh:primary
add dst-address=0.0.0.0/0 gateway=pppoe-dynamic-out routing-table=via-warp distance=2 comment=warp-gh
add dst-address=1.0.0.1/32 gateway=wg-warp comment=warp-gh
/tool netwatch
add host=1.0.0.1 type=icmp interval=10s timeout=2s comment=warp-gh up-script="/ip route enable [find comment=\"warp-gh:primary\"]" down-script="/ip route disable [find comment=\"warp-gh:primary\"]"

via-warp 是獨立的路由表,有兩條預設路由:distance=1 走 wg-warp,平常用這條;distance=2 走 HiNet,primary 被停用時才接手。

WireGuard 是無連線的,對面掛了介面照樣是 running,RouterOS 不會發現。所以用 netwatch 每 10 秒 ping 一次 1.0.0.1,不通就停用 primary,恢復再打開。

探測目標必須固定走 WARP,所以那條 1.0.0.1/32 放在 main 表。如果探測跟著 via-warp 走,primary 一停用 ping 就改走 HiNet,永遠是 up,故障就偵測不到了。代價是 LAN 裡連 1.0.0.1 的流量也會走 WARP,WARP 掛掉時它就不通。家裡有設備把 DNS 設成 1.0.0.1 要留意,或把探測目標換成別的位址。

目的地清單:address-list

1
2
3
4
5
6
7
8
/ip firewall address-list
add list=warp-gh address=github.com comment=warp-gh
add list=warp-gh address=codeload.github.com comment=warp-gh
add list=warp-gh address=objects.githubusercontent.com comment=warp-gh
add list=warp-gh address=release-assets.githubusercontent.com comment=warp-gh
add list=warp-gh address=raw.githubusercontent.com comment=warp-gh
add list=warp-gh address=pkg-containers.githubusercontent.com comment=warp-gh
add list=warp-gh address=avatars.githubusercontent.com comment=warp-gh

v7 的 address-list 可以直接填網域,路由器會自己解析,把 IP 以動態項目放進清單,TTL 到了再更新。這次 7 個網域解析出 17 筆 IP。

比對看的是 IP 不是網域,用戶端用自己的 DNS 或 DoH 拿到不同 IP 就抓不到,家裡設備都用路由器當 DNS 的話問題不大。

也可以把 Fastly 整個公開網段都丟進清單,不怕 DNS 不一致。但 Reddit、PyPI 等一大票網站也在 Fastly 上,會被一起送進 WARP,我只想動 GitHub,所以我用 fqdn 模式。

mangle:先標連線,再標路由

1
2
3
4
5
6
/ip firewall mangle
add chain=prerouting in-interface-list=LAN dst-address-list=warp-gh connection-state=new connection-mark=no-mark action=mark-connection new-connection-mark=warp-gh passthrough=yes comment=warp-gh
add chain=prerouting in-interface-list=LAN connection-mark=warp-gh action=mark-routing new-routing-mark=via-warp passthrough=no comment=warp-gh
add chain=forward out-interface=wg-warp protocol=tcp tcp-flags=syn action=change-mss new-mss=clamp-to-pmtu passthrough=yes comment=warp-gh
/ip firewall nat
add chain=srcnat out-interface=wg-warp action=masquerade comment=warp-gh

這是分流的核心。

  1. 第一條只看新連線:從 LAN 進來、目的地在 warp-gh 清單的,在連線上貼 warp-gh 標籤,之後同一條連線不用再比對清單。
  2. 第二條把帶標籤連線的封包標上 routing mark via-warp,路由決策就改查那張表。必須放 prerouting,因為 routing mark 要在路由決策之前貼;in-interface-list=LAN 讓路由器自己的流量不受影響。
  3. 第三條是 MSS clamp。wg-warp 的 MTU 只有 1280,把 SYN 的 MSS 改成配合 path MTU,避免網頁開得了、大檔案卻卡住。
  4. masquerade:WARP 只認 wgcf 分配的 172.16.0.2,LAN 私有 IP 送過去不會被理,出 tunnel 要把來源換掉。

FastTrack:被標記的連線不能走捷徑

1
2
/ip firewall filter
set [find action=fasttrack-connection] connection-mark=no-mark

預設的 FastTrack 規則會讓已建立的連線跳過 mangle,mark-routing 就失效,連線跑到一半又回到 main 表走 HiNet。所以加上 connection-mark=no-mark:沒標記的照樣加速,warp-gh 的連線不加速。GitHub 流量不大,對 RB450Gx4 沒什麼負擔。

這行改的是既有規則,沒有 warp-gh comment,所以復原時不會被移除,留著也不影響其他流量。

IPv6:直接拒絕,讓用戶端退回 IPv4

1
2
3
4
5
6
7
8
9
10
11
/ipv6 firewall address-list
add list=warp-gh-v6 address=github.com comment=warp-gh
add list=warp-gh-v6 address=codeload.github.com comment=warp-gh
add list=warp-gh-v6 address=objects.githubusercontent.com comment=warp-gh
add list=warp-gh-v6 address=release-assets.githubusercontent.com comment=warp-gh
add list=warp-gh-v6 address=raw.githubusercontent.com comment=warp-gh
add list=warp-gh-v6 address=pkg-containers.githubusercontent.com comment=warp-gh
add list=warp-gh-v6 address=avatars.githubusercontent.com comment=warp-gh
/ipv6 firewall filter
add chain=forward in-interface-list=LAN dst-address-list=warp-gh-v6 protocol=tcp action=reject reject-with=tcp-reset comment=warp-gh
add chain=forward in-interface-list=LAN dst-address-list=warp-gh-v6 action=reject reject-with=icmp-admin-prohibited comment=warp-gh

HiNet 有給 IPv6,而上面的分流都是 IPv4,用戶端走 IPv6 連 GitHub 就會繞過整套設定。所以不做 IPv6 分流,直接對這些目的地的 IPv6 連線 reject,用戶端會立刻失敗、退回 IPv4,接著被 mangle 抓到。不需要這段可以不加。

驗證

1
2
/interface wireguard peers print detail where comment="warp-gh"
/tool netwatch print where comment="warp-gh"

peer 的 last-handshake 有數值就代表 tunnel 通了,netwatch 要是 up。

1
2
/ip firewall address-list print where list=warp-gh
/ip firewall mangle print stats where comment="warp-gh"

我這邊 7 個網域解析出 17 筆 IP。抓幾個檔案之後,mark-connection 命中 3 個連線、mark-routing 命中 102 個封包,表示流量確實被導進 via-warp。

設定前後比對

分流靠的是 mangle 那條 mark-connection,停用它就等於回到 HiNet 直連,不用動其他設定:

1
2
3
4
# 停用:GitHub 流量走原本的 HiNet 直連
/ip firewall mangle disable [find comment="warp-gh" and action=mark-connection]
# 啟用:GitHub 流量走 WARP
/ip firewall mangle enable [find comment="warp-gh" and action=mark-connection]

規則只對新連線貼標籤,切換後要重新下載。測試方法:

  • 同一台 Mac、同一條 HiNet PPPoE。
  • 下載 GitHub release 檔 gh_2.102.0_macOS_arm64.zip(13.7 MB,github.com/cli/cli)。
  • 每輪先停用規則量一次(HiNet 直連),再啟用量一次(WARP),交替 3 輪。
  • curl 的 --max-time 設 180 秒。
輪HiNet 直連WARP
1114.66 s,0.12 MB/s1.29 s,10.62 MB/s
2180 s 逾時未完成,約 0.06 MB/s1.60 s,8.55 MB/s
3180 s 逾時未完成,約 0.06 MB/s2.39 s,5.72 MB/s

直連大約只有 0.06–0.12 MB/s,三輪裡有兩輪三分鐘內下載不完;走 WARP 是 5.7–10.6 MB/s,快了大概兩個數量級。這只是某個時段的結果,換個時間數字可能差很多,想知道自己家的情況,照上面切換規則量一次就好。

復原

所有物件都帶 warp-gh comment,用第一段那組 remove [find comment~"warp-gh"] 就能全部移除。FastTrack 那條是改既有規則,留著無害。真的弄壞了,還有動手前做的備份可以還原(/system backup load,會重新開機)。

踩坑與心得

  • WAN 介面名稱別寫死:WAN 介面名稱每台不同,我原本以為是 pppoe-out1,實機卻叫 pppoe-dynamic-out,那條 distance=2 路由的 gateway 要填自己的,名稱錯了這條路由就加不進去,WARP 掛掉時沒有退路。用 DHCP 或固定 IP 的人要填上游 gateway 的 IP。
  • fqdn 模式不是萬能:GitHub 換新下載網域,或用戶端用別的 DNS,就會漏網。某個網站還是慢,先用 /ip firewall address-list print 看它的 IP 在不在清單裡。

也許你也會想看看

輸入關鍵字開始搜尋 · ↑↓ 選擇 · Enter 開啟