網路上不少文章會叫你在 Linux 的 Exit Node 上開 BBR,說「出口會變快」。先把結論寫在前面:TCP 擁塞控制只作用在由本機發起或終止的 TCP 連線上。kernel 模式下的 Subnet Router 與 Exit Node 只是轉送封包,被轉送連線的擁塞控制在兩端裝置上——在 exit node 上開 BBR,並不會讓經過它的流量變快。例外是走 DERP 中繼的隧道與 userspace networking 模式:這兩種情況下機器的 kernel 會親自經手 TCP 連線,後面會說明。
本文以 Tailscale v1.102(2026 年 10 月)為準;netstack 的行為以 v1.102.5 的原始碼查證。Linux 端的事實以 kernel 原始碼與文件為準。
BBR 是什麼
BBR 是 Google 開發的 Linux TCP 擁塞控制演算法,2016 年進入 kernel(Linux 4.9 起可用)。大多數發行版的預設是 CUBIC。
兩者的差異,照 kernel 原始碼註解的說法:BBR 根據 ACK 估計的交付速率(delivery rate)計算送出速率,核心演算法不直接對丟包或延遲做反應;CUBIC 則是以丟包為主要訊號調整擁塞視窗的 loss-based 演算法。在高延遲、會丟包的路徑上,BBR 通常不會像 CUBIC 一樣把速率砍到很低——這是它常被推薦的原因。
先弄清楚:BBR 影響哪些連線
兩個原則先記住:
- 擁塞控制是 TCP 端點的行為——只有「發起」或「終止」這條 TCP 連線的機器,它的擁塞控制才有作用,轉送封包的機器不算端點。
- 每個方向各看送出方——同一條連線,下載方向的速度由伺服器那端的演算法決定,上傳方向由用戶端那端決定。所以在某台機器開 BBR,影響的是這台機器送出的資料:從它下載才受惠,上傳給它則看對方的演算法。
把這兩個原則套到 Tailscale 節點上,逐種流量看:
- kernel 模式(預設)的 Subnet Router/Exit Node 只是轉送 IP 封包:經過它們的 TCP 連線,端點是 tailnet 裝置與目的地伺服器,擁塞控制在兩端裝置上,跟這台機器無關。假設筆電透過家裡的 exit node 上 YouTube,這條 TCP 連線的速度由筆電和 YouTube 伺服器的擁塞控制決定。
- 節點之間直連時走 WireGuard(UDP):隧道本身沒有 TCP 擁塞控制這回事。
- 經 DERP 中繼時:tailscaled 和 DERP 伺服器之間是一條 kernel 的 TCP 連線(DERP 走 HTTPS、通常是 443)。這條連線上節點送出的方向——相當於 tailnet 裝置上傳出去的隧道流量——會受本機 kernel 擁塞控制影響;下載方向則由 DERP 伺服器決定。節點在 CGNAT/嚴格 NAT 後面打不通直連時就會走這條路,這是 BBR 在 exit node 上可能真的有感的情境。用
tailscale status/tailscale ping檢查,輸出出現relay "xxx"或via DERP(xxx)就是走中繼。 - 由 tailscaled 的 netstack 終止的連線(Tailscale Serve、Funnel、Tailscale SSH 的 tailnet 側):TCP 在 tailscaled 內部的 netstack 終止,kernel 的 sysctl 影響不到它。而且 tailscaled 在原始碼裡明確把 netstack 的擁塞控制寫死成 reno(註解說明是為了避開 gVisor cubic 在擁塞視窗計算上的整數溢位問題,見 gvisor#11632)——所以這一側連「改擁塞控制」的選項都沒有。
- userspace networking 模式下轉送:連線被拆成兩段——tailnet 裝置↔節點這段由 netstack 終止(reno,sysctl 管不到);節點↔目的地這段是 tailscaled 用一般 kernel socket 撥出去的 TCP(原始碼裡的
forwardTCP),節點送出的方向受 kernel 擁塞控制影響。Serve 反向代理到非 loopback 的後端時,後端那段同理。
整理成表(「有」都指這台機器送出的方向):
| 流量 | kernel BBR 有沒有影響 |
|---|---|
| 節點上由 kernel 終止的服務(sshd、nginx 等) | 有(別人從節點下載時) |
| 節點自己對外發起的 TCP | 有(節點上傳的方向) |
| 經 Subnet Router/Exit Node 轉送的連線 | 沒有(端點在兩端裝置) |
| 節點間隧道,直連(WireGuard UDP) | 沒有(走 UDP) |
| 節點間隧道,經 DERP 中繼 | 有(DERP 走 kernel TCP) |
| Serve/Funnel/Tailscale SSH 的 tailnet 側 | 沒有(netstack 固定 reno) |
| userspace 模式轉送的對外側 | 有(tailscaled 用 kernel TCP) |
所以「在 exit node 上開 BBR」真正的效果是:這台機器送出的 TCP 變得抗丟包——機器自己的對外上傳(系統更新、推備份、拉容器映像檔)、別人從機器上直接跑的服務下載;直連轉送的流量不變;走 DERP 中繼時,上傳方向的隧道流量受惠。如果你的 exit node 直連通暢,開 BBR 對 tailnet 裝置多半沒有可感知的差異。
啟用方式
先確認 kernel 支援。BBR 從 Linux 4.9 就有(現在的發行版都遠超過),但可能編成模組還沒載入:
sudo modprobe tcp_bbr
sysctl net.ipv4.tcp_available_congestion_control依 kernel 文件,tcp_available_congestion_control 列出已註冊的演算法,有些以模組形式存在但尚未載入——清單裡沒有 bbr 的話,就是模組沒載入(或核心沒編)。
之後在 /etc/sysctl.d/ 設 tcp_congestion_control=bbr 時不必煩惱模組:kernel 找不到指定的演算法時會自動以 request_module 載入對應模組(原始碼裡的 tcp_ca_find_autoload),所以通常不需要另外寫進 /etc/modules-load.d/;上面的 modprobe 只是當下先確認模組存在、方便檢查而已。
把設定寫進 /etc/sysctl.d/99-bbr.conf:
net.core.default_qdisc=fq
net.ipv4.tcp_congestion_control=bbr套用並確認:
sudo sysctl -p /etc/sysctl.d/99-bbr.conf
sysctl net.ipv4.tcp_congestion_control
sysctl net.core.default_qdiscnet.ipv4.tcp_congestion_control 顯示 bbr 就生效了。
為什麼要一起設 fq:照 kernel 原始碼註解的說法,BBR 建議搭配 fq 佇列管理(man tc-fq)——這樣 pacing 的工作交給 qdisc;沒有 fq 時 TCP 會退回每個 socket 一個高解析度計時器的內建 pacing,比較耗資源。所以 fq 不是硬性必要,而是讓 BBR 跑得省力的標準搭配。
兩個小提醒:
- 擁塞控制是新連線套用:已建立的連線不受影響,而且既有服務的 listener 是在舊設定下建立的,繼續收連線會沿用舊演算法——長跑的服務(nginx、sshd 等)要重啟才會真正用上 bbr。
- 只想給特定服務用的話,也可以不動全系統預設,用
setsockopt(TCP_CONGESTION)在應用程式層指定(iperf3 就有-C bbr這個參數),對照測試時很方便。
用 iperf3 驗證
以下提供兩個測法,實際數據請自行量測——效能結論跟硬體、路徑、對端有關,本文不附任何特定數字。通用的量測方法(先確認直連/DERP、MTU 1280、CPU 瓶頸判讀、參數速查)見〈用 iperf3 量 Tailscale 吞吐量的正確方法〉。
測法一:轉送的流量(預期沒有差異)
- 在節點 A 上啟用 BBR 之前,從 tailnet 裝置 B 啟用 A 當 exit node,對外部的 iperf3 伺服器量一次:
iperf3 -c <server> -t 30(iperf3 預設是 client 上傳、由 B 送出資料) - 在 A 上啟用 BBR,B 重複測量幾輪
這條 TCP 連線的擁塞控制在 B 和外部伺服器上,A 只是轉送——前後數字應該在同一個區間內浮動。想在這個測試裡看到 BBR 的效果,要開 BBR 的是送出資料的那台(B),不是 exit node。
測法二:節點自己的服務(預期有差異)
要量到 A 的擁塞控制,資料必須由 A 送出,所以要用 -R(reverse,改由 server 送):
- 在節點 A 上跑
iperf3 -s - 從 tailnet 裝置 B 直連 A 的 Tailscale IP:
iperf3 -c <A 的 Tailscale IP> -R -t 30——-R讓 server(A)送出資料,A 的擁塞控制作用在送出方向 - 給路徑加一點丟包,讓擁塞控制的差異顯現:丟包要加在送出方 A 的實體網卡上(
sudo tc qdisc add dev "$NETDEV" root netem loss 1%);加在 B 上只會丟到 ACK,效果不同 - 對照
iperf3 -c <A 的 Tailscale IP> -R -t 30 -C cubic與-C bbr各量一輪:-C指定這次連線的擁塞控制,而且 client 的設定會一併傳給 server 套用,所以不必改 sysctl,也不受 A 上既有 listener 繼承舊演算法的影響 - 測完記得清掉:
sudo tc qdisc del dev "$NETDEV" root
順帶一提,轉送 UDP 的效能(exit node、subnet router 的大宗流量)要靠 UDP GRO forwarding 那篇的調校,跟 BBR 是兩件獨立的事。2026 年後續還有一批資料平面的升級(小封包緩衝、multi-queue、netmap caching),見〈2026 年的效能升級:multi-queue 與 netmap caching〉。
常見問題
- 開了 BBR,經過 exit node 的速度變快了? 先確認三件事:是不是同時做了別的變動——最常見的是一起設了 UDP GRO forwarding(真的會加速轉送的 UDP);是不是量測誤差;以及當時是否走 DERP 中繼(用
tailscale ping看得到,走中繼時上傳方向的隧道流量確實會受 BBR 影響)。直連轉送的 TCP 不受本機擁塞控制影響。 tcp_available_congestion_control裡沒有 bbr?sudo modprobe tcp_bbr後再看;模組載入後就會出現。還是沒有就是核心沒編進 BBR(少見)。- 不用 fq 可以嗎? 可以,不是硬性必要;BBR 會退回內建 pacing,只是每個 socket 一個高解析度計時器、比較耗 CPU。
- Serve 的流量開 BBR 有幫助嗎? 沒有——Serve(和 Funnel、Tailscale SSH)的 TCP 連線由 tailscaled 的 netstack 終止,擁塞控制固定是 reno,kernel 的 sysctl 影響不到。