Originally published at https://blog.pathvector.dev/protocol-lab-mss-37/ — part of the free Protocol Lab series.
This post is part of Protocol Lab,一個免費、動手實作的系列課程,透過在容器實驗室中建置與破壞通訊協定來學習網路協定。所有實驗室教材 — 拓撲、設定檔與腳本 — 都放在儲存庫中:github.com/pathvector-studio/protocol-lab。
In Lab #25,我們觀察到 Path MTU Discovery 透過 ICMP 找到較小的鏈路 — 也看到了當 ICMP 被過濾時,它如何變成黑洞。本實驗室是操作上的修正:MSS clamping。路徑上的路由器會改寫通過的 TCP SYN 中的 MSS,讓兩端主機一開始就同意傳送能適配最窄鏈路的區段 — 不需要依賴 PMTUD 正常運作。
Reading guide: rfc-notes/mss-clamping.md
Prerequisite: Lab 25: MTU and Path MTU Discovery
預估時間:35–50 分鐘。
目標
用戶端鏈路 MTU 為 1500;r–server 鏈路 MTU 為 1400:
- 沒有 clamping 時,用戶端的 SYN 會宣告 MSS 1460(其本地 1500 − 40);完整區段對 1400 鏈路來說太大,必須依賴 PMTUD。
-
有 clamping 時,
r會將 SYN 的 MSS 改寫為 1360(1400 − 40),讓兩端傳送的區段永遠能適配。
結束時,您應該能解釋這張表格:
| 伺服器看到的 SYN MSS | 原因 | |
|---|---|---|
| without clamping | 1460 | 用戶端本地 MTU 1500 − 40 |
| with clamping | 1360 |
r 將其 clamp 至 1400-MTU 鏈路(− 40) |
您將學到
- TCP MSS 選項是什麼,以及端點如何從本地 MTU 推導它。
- 為什麼 SYN MSS 對路徑中更深的較小鏈路是盲目的。
- PMTUD 在 ICMP 被過濾時如何黑洞(Lab 25 的複習)。
- MSS clamping 如何在路由器上改寫 SYN 的 MSS 以適配路徑。
- 為什麼這對縮小有效 MTU 的隧道(WireGuard/VXLAN/GRE)很重要。
本實驗室不涵蓋:
- 完整的 PMTUD 內部機制(Lab 25 涵蓋)。
- IPv6 MSS 細節(MSS = MTU − 60,使用較大的最小值)。
- 每路由 MTU 固定或 PLPMTUD(RFC 8899)。
在 RFC 中的閱讀位置
| 參考文件 | 重點 |
|---|---|
| RFC 9293 §3.7.1 | MSS 選項以及如何決定區段大小 |
| RFC 1191 | PMTUD(clamping 所支援的機制) |
| RFC 2923 | PMTUD 黑洞問題 |
| RFC 5737 / RFC 1918 | 確認這裡使用的位址僅限本地使用 |
整體架構
r 位於用戶端與伺服器之間,只有 r–server 鏈路 MTU 為 1400。
client ---- eth1/eth1 ---- r ---- eth2/eth1 ---- server
MTU 1500 MTU 1400 (narrow) MTU 1400
SYN: mss 1460 ---> r clamps ---> mss 1360 at server
Enter fullscreen mode Exit fullscreen mode
在 r 的 FORWARD chain(mangle table)中,我們將 SYN 的 MSS clamp 至 PMTU。
flowchart LR
C["client (MTU 1500)<br/>SYN mss 1460"] --> R["r<br/>TCPMSS --clamp-mss-to-pmtu"]
R -->|"SYN rewritten<br/>mss 1360"| S["server (MTU 1400)"]
Enter fullscreen mode Exit fullscreen mode
10.0.0.0/8 是本地、封閉的範圍。
Note: 這裡全部使用本地/文件位址空間(RFC 1918 / RFC 5737),因此本實驗室不會觸及真正的網際網路。
所需環境
建議環境:
- Linux / WSL2 / Linux 虛擬機
- Docker
- containerlab
使用的映像:
-
nicolaka/netshoot:latest— 內建iptables、tcpdump、curl與python3。
不需要其他映像。
執行實驗室
快速路徑,會自動部署、驗證並拆除:
./scripts/labctl.sh run mss-37
Enter fullscreen mode Exit fullscreen mode
或手動逐步執行:
1. 進入工作目錄
cd protocol-lab/examples/mss-37
Enter fullscreen mode Exit fullscreen mode
2. 部署並啟動 HTTP 伺服器
sudo containerlab deploy -t mss-37.clab.yml
docker exec -d clab-mss-37-server python3 -m http.server 80
docker exec clab-mss-37-r ip -br link show eth2 # mtu 1400
Enter fullscreen mode Exit fullscreen mode
3. 觀察未 clamping 時的 SYN MSS
docker exec -d clab-mss-37-server sh -c 'tcpdump -i eth1 -n -c1 "tcp[tcpflags] & tcp-syn != 0 and tcp[tcpflags] & tcp-ack == 0" > /tmp/syn.txt 2>&1'
docker exec clab-mss-37-client curl -s --max-time 4 http://10.0.8.2/ >/dev/null
docker exec clab-mss-37-server grep -oE 'mss [0-9]+' /tmp/syn.txt
Enter fullscreen mode Exit fullscreen mode
您應該會看到 mss 1460 — 用戶端本地 1500 − 40。
4. 在 r 上啟用 MSS clamping
docker exec clab-mss-37-r iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
Enter fullscreen mode Exit fullscreen mode
5. 再次觀察 SYN MSS
docker exec -d clab-mss-37-server sh -c 'tcpdump -i eth1 -n -c1 "tcp[tcpflags] & tcp-syn != 0 and tcp[tcpflags] & tcp-ack == 0" > /tmp/syn2.txt 2>&1'
docker exec clab-mss-37-client curl -s --max-time 4 http://10.0.8.2/ >/dev/null
docker exec clab-mss-37-server grep -oE 'mss [0-9]+' /tmp/syn2.txt
Enter fullscreen mode Exit fullscreen mode
現在是 mss 1360 — r 已將其 clamp 至 1400 − 40。
預期輸出
-
r的 eth2:mtu 1400。 - 沒有 clamping:伺服器看到 SYN 帶有
mss 1460。 - 有 clamping:伺服器看到
mss 1360。 - mangle FORWARD chain 顯示
TCPMSS clamp to PMTU規則。
運作原理
MSS clamping 的概念是「在一開始就告訴兩端安全的區段大小,而不是依賴 PMTUD」。
- MSS。 TCP 在單一區段中能接受的最大 payload。它只在 SYN 中宣告,數值為 本地 MTU − 40(IPv4):MTU 1500 → 1460。發送端會將其區段上限設為對方宣告的 MSS。
- 盲點。 每個端點只知道自己的介面 MTU。即使路徑中存在更窄的鏈路(這裡是 1400-MTU 的 r–server 鏈路),SYN 的 MSS 也不會反映它。
- PMTUD 與黑洞(Lab 25)。當帶有 DF 旗標的過大區段撞上窄鏈路時,路由器會回傳 ICMP「fragmentation needed」,發送端隨之縮小。但如果 ICMP 被過濾,發送端永遠不會知道;大型區段持續被丟棄,連線掛起 — 形成黑洞。
-
Clamping。 路徑上的路由器會將通過 SYN 中的 MSS 選項 改寫為輸出鏈路的 MTU − 40(若宣告值更大)。在本實驗室中,
r將 1460 降為 1360。兩端之後會以 1360 或更小傳送,因此區段永遠能適配 1400 鏈路 — 即使 PMTUD 失效也不會黑洞。因為 MSS 只在 SYN 中協商,所以只改寫 SYN 就足夠了。
關鍵洞見:路徑上的路由器將端點看不到的路徑最小 MTU 注入 SYN 的 MSS,讓兩端從第一個位元組開始就傳送適配的區段。 這是真實部署中處理隧道縮小有效 MTU 的常見做法。
常見陷阱
- 混淆 MSS 與 MTU。 MTU 是整個 IP 封包;MSS 是 TCP payload。在 IPv4 中,MSS = MTU − 40。
- 認為 MSS 會重新協商。 它只在 SYN 中協商,不會在資料區段中改變 — 這正是改寫 SYN 的 MSS 選項就足夠的原因。
- 假設 PMTUD 就夠了。 ICMP 過濾可能造成黑洞。Clamping 是保險。
- 假設只要降低端點的 MTU 即可。 路徑深處的窄鏈路從端點是看不到的。必須由路徑上的路由器進行改寫。
- 期待它能幫助 UDP。 MSS 是 TCP 的概念;UDP 是另一個問題。
-
Clamp 值。
--clamp-mss-to-pmtu依輸出 MTU 推導;若要固定值,請使用--set-mss。
清理
sudo containerlab destroy -t mss-37.clab.yml --cleanup
Enter fullscreen mode Exit fullscreen mode
若您使用 labctl.sh run mss-37,腳本會在最後自動執行 destroy。
確認理解
- 什麼是 TCP MSS?它與 MTU 有何不同(IPv4 關係為何)?
- MSS 何時協商?能否在 SYN 之外改變?
- 為什麼 SYN 的 MSS 無法反映路徑中更窄的鏈路?
- PMTUD 何時會黑洞?
- MSS clamping 改寫什麼、在哪裡改寫?為什麼本實驗室中 1460 會變成 1360?
- 為什麼 clamping 在隧道(WireGuard/VXLAN/GRE)中如此有價值?
參考文件
- RFC 9293: Transmission Control Protocol
- RFC 1191: Path MTU Discovery
- RFC 2923: TCP Problems with Path MTU Discovery
- RFC 5737: IPv4 Address Blocks Reserved for Documentation
已驗證執行記錄(2026-07-07)
本實驗室已在真實硬體上確認可重現。
環境:
- Ubuntu 26.04 LTS(核心 7.0.0-27-generic,x86_64)
- Docker 29.1.3
- containerlab 0.77.0
- client / r / server:
nicolaka/netshoot:latest(iptables、tcpdump、curl、python3)
執行 PATH="/tmp/pl-shim:$PATH" ./scripts/labctl.sh run mss-37 執行 deploy → verify → destroy,且 verification.json 回傳 "status": "verified"。
沒有 clamping → SYN 帶有 MSS 1460
在伺服器的 eth1 上擷取用戶端的 SYN:
Flags [S], seq ..., options [mss 1460, ...]
Enter fullscreen mode Exit fullscreen mode
這是 MSS 1460,來自用戶端鏈路 MTU 1500 — 對 r–server 鏈路(MTU 1400)來說太大,連線必須依賴 PMTUD。
在 r 上啟用 clamping → SYN 帶有 MSS 1360
mangle FORWARD: TCPMSS tcp flags:0x06/0x02 TCPMSS clamp to PMTU
Enter fullscreen mode Exit fullscreen mode
加入 iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu 後,伺服器擷取到的同一用戶端 SYN 顯示:
mss 1360
Enter fullscreen mode Exit fullscreen mode
r 將 SYN 的 MSS 選項從 1460 → 1360(1400 − 40)改寫,以符合其輸出鏈路(eth2,MTU 1400)。之後兩端都會傳送 1360 位元組或更小的區段,因此所有內容都能適配窄鏈路 — 即使 ICMP 被過濾也不會黑洞。
| 伺服器看到的 SYN MSS | |
|---|---|
| without clamping | 1460 |
| with clamping | 1360 |
清理
containerlab destroy -t mss-37.clab.yml --cleanup
Enter fullscreen mode Exit fullscreen mode
這就是 MSS clamping:路徑路由器上的一條 iptables 規則,讓每個 TCP 連線在傳送第一個資料位元組前就同意安全的區段大小 — 不需要 PMTUD 正常運作。
探索完整的 Protocol Lab 系列:github.com/pathvector-studio/protocol-lab。如果這些實驗室對您有幫助,請在 GitHub 上 ⭐ 為儲存庫按讚 — 這真的能幫助其他人找到這個專案。
接下來,我們將繼續探討當教科書機制失效時,真實網路如何保持可靠。
0 Comments
Log in to join the conversation.No comments yet. Be the first to share your thoughts.