DAY 263

高鐵 Wi-Fi 連不上的兇手

SLIDES · 7
Day 263 高鐵 Wi-Fi 連不上的兇手 — 投影片 1Day 263 高鐵 Wi-Fi 連不上的兇手 — 投影片 2Day 263 高鐵 Wi-Fi 連不上的兇手 — 投影片 3Day 263 高鐵 Wi-Fi 連不上的兇手 — 投影片 4Day 263 高鐵 Wi-Fi 連不上的兇手 — 投影片 5Day 263 高鐵 Wi-Fi 連不上的兇手 — 投影片 6Day 263 高鐵 Wi-Fi 連不上的兇手 — 投影片 7
1 / 7

你如果跟我一樣,在高鐵上連不上 Wi-Fi,我最近的解法或許能幫到你。

症狀是 Wi-Fi 圖示顯示連上 THSR_freeWIFI_ALL 了,登入頁面從來沒跳出來,瀏覽器開任何網站都在轉圈。同一台 MacBook Air 在咖啡廳、在家裡都正常,這半年在高鐵上一直是這樣。

主因最後跟高鐵沒關係,是我自己電腦上開著的 Tailscale(把自己的幾台機器連成一個私有網路的 VPN)。

這篇分享:怎麼修、公共 Wi-Fi 的登入頁到底怎麼運作,以及 VPN 為什麼會把它整個擋在外面。

先給能用的做法

上車之後照這個順序:

  1. 先把 Tailscale 整個關掉——選單列圖示 → Quit,不是按「中斷連線」
  2. THSR_freeWIFI_ALL
  3. 登入頁沒自動跳出來的話,開 Safari 打 http://neverssl.com(刻意維持純 http 的網站,瀏覽器不會把它轉去 https)
  4. 完成登入,確認真的有網路
  5. 再把 Tailscale 開回來

第 3 步用 Safari,是因為 macOS 內建的登入視窗本來就是同一套 WebKit 引擎,登入頁比較不會出狀況;neverssl.com 本來就沒有 HSTS,其實用哪個瀏覽器打都連得到。

另一個原因:把那個網路的「私密 Wi-Fi 位址」關掉(系統設定 → Wi-Fi → 該網路的「詳細資訊…」→ 私密 Wi-Fi 位址:關閉,或至少改成「固定」)。高鐵是用 MAC 位址記住你登入過,設成「輪替」的話,位址每次重連都在變,會一直被丟回登入頁。

登入頁是怎麼跳出來的

公共 Wi-Fi 的登入頁(captive portal)運作方式,本質上是一次刻意安排的中間人攻擊。你連上去的時候:

  1. 閘道發給你一組 IP(這是 DHCP 在做的事),順便把它自己的 DNS 伺服器也塞給你
  2. 閘道把所有流量都擋掉,只放行 DNS 跟它自己的登入頁
  3. 常見的做法是它的 DNS 伺服器對每個查詢都說謊——不管你問哪個網域,答案都是登入頁的 IP(也有的閘道不碰 DNS,直接在 HTTP 層回一個 302,把你轉去登入頁)
  4. 你的瀏覽器連任何網站,拿到的都是登入頁

macOS 怎麼知道自己連到的是這種網路?它會送一個探測封包:GET http://captive.apple.com/hotspot-detect.html,預期回來的內容就是 Success 這個字。三種結果:

探測封包拿回什麼 macOS 判定 你看到
Success 真的有網路 沒事,直接能用
被導去一個登入頁 這是要登入的網路 登入視窗自己彈出來
逾時、沒有回應 這個網路壞了 什麼都沒有

我的狀況是第三列。登入頁就在那裡等著要騙我,而我的電腦從頭到尾沒去問它。

兇手是 Tailscale

關掉 Tailscale,其他什麼都沒改,馬上就連上了。

卡住的地方在 DNS。Tailscale 會把自己的解析器 100.100.100.100 設成系統的第一順位,排在 Wi-Fi 那端給的那台前面,scutil --dns 看得到 utun 那組排在 en0 前面。

路由表上也沒有任何要我走 VPN 的設定。netstat 列出的預設路由有兩條(這份輸出是我事後在手機熱點上重現同一個狀況抓的,高鐵當下的閘道 IP 不是這個,兩條 default 的結構一樣):

$ netstat -rn -f inet | grep default
default            172.20.10.1        UGScg      en0
default            link#26            UCSIg      utun2

en0 是 Wi-Fi,utun2 是 Tailscale 建的虛擬網卡。而且這時候我沒有設 exit node,tailnet 裡也沒有任何一台機器在宣告子網路路由:

$ tailscale status --json | jq '.ExitNodeStatus'
null

$ tailscale status --json | jq '.Peer[]? | select((.PrimaryRoutes//[])|length>0)'
(空的)

沒有任何一項「我要求流量走 VPN」的設定,那條通道還是把 DNS 查詢接走了。接下來就是一個死結:

  1. macOS 要解析 captive.apple.com
  2. 查詢被送去 Tailscale 的 100.100.100.100,而不是高鐵那端給的那台
  3. 那台解析器要等通道通了才連得到,而通道自己得先連上 Tailscale 的控制伺服器跟中繼節點——那同樣需要網路
  4. captive.apple.com 解不出來,探測封包逾時
  5. macOS 把逾時解讀成「這個網路壞了」,於是連登入視窗都不會跳

登入頁一直在那裡,我的封包沒有一個到得了。

其他 VPN 也一樣

這件事跟 Tailscale 本身沒什麼關係。任何會在你電腦上建一條通道、而那條通道自己也需要網路才建得起來的 VPN,在飯店、機場、車站這種要先登入的 Wi-Fi 上,都會卡在同一個地方。順序永遠是:先關 VPN、登入、再開回來。

公共 Wi-Fi 得先把你攔下來,才願意給你網路;VPN 整天在做的事,就是讓誰都攔不到你。得先讓它攔你一次,你才出得去。


macOS 的 captive portal 探測網址:http://captive.apple.com/hotspot-detect.html

延伸閱讀
看完整 262 篇 →