---
title: "BBR：擁塞控制在 Tailscale 節點上的實際效果"
description: "在 Tailscale 節點上啟用 Linux 的 BBR 擁塞控制前，先弄清楚它只影響本機發起或終止的 TCP 連線，對被 Subnet Router 與 Exit Node 轉送的流量沒有影響"
---

> Documentation Index
> Fetch the complete documentation index at: https://tailscale.tw/llms.txt
> Use this file to discover all available pages before exploring further.

# BBR：擁塞控制在 Tailscale 節點上的實際效果

網路上不少文章會叫你在 Linux 的 [Exit Node](/features/exit-node/) 上開 BBR，說「出口會變快」。先把結論寫在前面：**TCP 擁塞控制只作用在由本機發起或終止的 TCP 連線上**。kernel 模式下的 [Subnet Router](/features/subnet-router/) 與 Exit Node 只是轉送封包，被轉送連線的擁塞控制在**兩端裝置**上——在 exit node 上開 BBR，並不會讓經過它的流量變快。例外是走 DERP 中繼的隧道與 userspace networking 模式：這兩種情況下機器的 kernel 會親自經手 TCP 連線，後面會說明。

> 本文以 Tailscale v1.102（2026 年 10 月）為準；netstack 的行為以 v1.102.5 的[原始碼](https://github.com/tailscale/tailscale/blob/main/wgengine/netstack/netstack.go)查證。Linux 端的事實以 kernel 原始碼與文件為準。

## BBR 是什麼

BBR 是 Google 開發的 Linux TCP 擁塞控制演算法，2016 年進入 kernel（[Linux 4.9](https://github.com/torvalds/linux/blob/master/net/ipv4/tcp_bbr.c) 起可用）。大多數發行版的預設是 CUBIC。

兩者的差異，照 kernel [原始碼註解](https://github.com/torvalds/linux/blob/master/net/ipv4/tcp_bbr.c)的說法：BBR 根據 ACK 估計的**交付速率**（delivery rate）計算送出速率，核心演算法**不直接對丟包或延遲做反應**；CUBIC 則是以丟包為主要訊號調整擁塞視窗的 loss-based 演算法。在高延遲、會丟包的路徑上，BBR 通常不會像 CUBIC 一樣把速率砍到很低——這是它常被推薦的原因。

## 先弄清楚：BBR 影響哪些連線

兩個原則先記住：

1. **擁塞控制是 TCP 端點的行為**——只有「發起」或「終止」這條 TCP 連線的機器，它的擁塞控制才有作用，轉送封包的機器不算端點。
2. **每個方向各看送出方**——同一條連線，下載方向的速度由伺服器那端的演算法決定，上傳方向由用戶端那端決定。所以在某台機器開 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](/features/serve/)、Funnel、Tailscale SSH 的 tailnet 側）：TCP 在 tailscaled 內部的 netstack 終止，**kernel 的 sysctl 影響不到它**。而且 tailscaled 在原始碼裡明確把 netstack 的擁塞控制寫死成 reno（註解說明是為了避開 gVisor cubic 在擁塞視窗計算上的整數溢位問題，見 [gvisor#11632](https://github.com/google/gvisor/issues/11632)）——所以這一側連「改擁塞控制」的選項都沒有。
- **[userspace networking 模式](https://tailscale.com/kb/1112/userspace-networking)下轉送**：連線被拆成兩段——tailnet 裝置↔節點這段由 netstack 終止（reno，sysctl 管不到）；節點↔目的地這段是 tailscaled 用一般 kernel socket 撥出去的 TCP（原始碼裡的 [`forwardTCP`](https://github.com/tailscale/tailscale/blob/main/wgengine/netstack/netstack.go)），**節點送出**的方向受 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 就有（現在的發行版都遠超過），但可能編成模組還沒載入：

```bash
sudo modprobe tcp_bbr
sysctl net.ipv4.tcp_available_congestion_control
```

依 [kernel 文件](https://docs.kernel.org/networking/ip-sysctl.html)，`tcp_available_congestion_control` 列出**已註冊**的演算法，有些以模組形式存在但尚未載入——清單裡沒有 `bbr` 的話，就是模組沒載入（或核心沒編）。

之後在 `/etc/sysctl.d/` 設 `tcp_congestion_control=bbr` 時不必煩惱模組：kernel 找不到指定的演算法時會自動以 `request_module` 載入對應模組（[原始碼](https://github.com/torvalds/linux/blob/master/net/ipv4/tcp_cong.c)裡的 `tcp_ca_find_autoload`），所以通常不需要另外寫進 `/etc/modules-load.d/`；上面的 `modprobe` 只是當下先確認模組存在、方便檢查而已。

把設定寫進 `/etc/sysctl.d/99-bbr.conf`：

```ini
net.core.default_qdisc=fq
net.ipv4.tcp_congestion_control=bbr
```

套用並確認：

```bash
sudo sysctl -p /etc/sysctl.d/99-bbr.conf
sysctl net.ipv4.tcp_congestion_control
sysctl net.core.default_qdisc
```

`net.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 吞吐量的正確方法](/performance/iperf-methodology/)〉。

**測法一：轉送的流量（預期沒有差異）**

1. 在節點 A 上啟用 BBR 之前，從 tailnet 裝置 B 啟用 A 當 exit node，對外部的 iperf3 伺服器量一次：`iperf3 -c <server> -t 30`（iperf3 預設是 client 上傳、由 B 送出資料）
2. 在 A 上啟用 BBR，B 重複測量幾輪

這條 TCP 連線的擁塞控制在 B 和外部伺服器上，A 只是轉送——前後數字應該在同一個區間內浮動。想在這個測試裡看到 BBR 的效果，要開 BBR 的是**送出資料的那台**（B），不是 exit node。

**測法二：節點自己的服務（預期有差異）**

要量到 A 的擁塞控制，資料必須由 A 送出，所以要用 `-R`（reverse，改由 server 送）：

1. 在節點 A 上跑 `iperf3 -s`
2. 從 tailnet 裝置 B 直連 A 的 Tailscale IP：`iperf3 -c <A 的 Tailscale IP> -R -t 30`——`-R` 讓 server（A）送出資料，A 的擁塞控制作用在送出方向
3. 給路徑加一點丟包，讓擁塞控制的差異顯現：丟包要加在**送出方 A** 的實體網卡上（`sudo tc qdisc add dev "$NETDEV" root netem loss 1%`）；加在 B 上只會丟到 ACK，效果不同
4. 對照 `iperf3 -c <A 的 Tailscale IP> -R -t 30 -C cubic` 與 `-C bbr` 各量一輪：`-C` 指定這次連線的擁塞控制，而且 client 的設定會一併傳給 server 套用，所以不必改 sysctl，也不受 A 上既有 listener 繼承舊演算法的影響
5. 測完記得清掉：`sudo tc qdisc del dev "$NETDEV" root`

順帶一提，轉送 UDP 的效能（exit node、subnet router 的大宗流量）要靠 [UDP GRO forwarding](/performance/udp-gro-forwarding/) 那篇的調校，跟 BBR 是兩件獨立的事。2026 年後續還有一批資料平面的升級（小封包緩衝、multi-queue、netmap caching），見〈[2026 年的效能升級：multi-queue 與 netmap caching](/performance/multiqueue-netmap-cache/)〉。

## 常見問題

- **開了 BBR，經過 exit node 的速度變快了？** 先確認三件事：是不是同時做了別的變動——最常見的是一起設了 [UDP GRO forwarding](/performance/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 影響不到。

## 參考資料

- [tcp_bbr.c（kernel 原始碼與演算法說明）](https://github.com/torvalds/linux/blob/master/net/ipv4/tcp_bbr.c)
- [ip-sysctl 文件（`tcp_congestion_control` 等）](https://docs.kernel.org/networking/ip-sysctl.html)
- [admin-guide sysctl：`default_qdisc`](https://docs.kernel.org/admin-guide/sysctl/net.html)
- [tailscaled netstack 的擁塞控制設定（固定 reno）](https://github.com/tailscale/tailscale/blob/main/wgengine/netstack/netstack.go)
- [gvisor#11632：cubic 的擁塞視窗溢位問題](https://github.com/google/gvisor/issues/11632)
- [Userspace networking 模式（官方文件）](https://tailscale.com/kb/1112/userspace-networking)
- [iperf3](https://github.com/esnet/iperf)

Source: https://tailscale.tw/performance/bbr/index.mdx
