TCP, HTTP & Web cho Backend/MTU & fragmentation — vì sao ping được mà app timeout
9/29
Bài 9 / 29~20 phútTCP & UDP Deep DiveMiễn phí lượt xem

MTU & fragmentation — vì sao ping được mà app timeout

MTU, phân mảnh IP, Path MTU Discovery và PMTUD black-hole — khi firewall chặn ICMP khiến gói lớn bị drop âm thầm, ping thông nhưng TLS handshake treo.

TL;DR: MTU (Maximum Transmission Unit) là kích thước tối đa một frame Ethernet có thể mang — thường 1500 byte cho mạng LAN. Gói IP lớn hơn MTU phải bị phân mảnh (fragment) rồi ráp lại ở đích. Path MTU Discovery (PMTUD) tránh phân mảnh bằng cách tìm MTU nhỏ nhất trên toàn tuyến đường — dùng cờ Don't Fragment kết hợp ICMP "Fragmentation Needed". PMTUD black-hole xảy ra khi firewall chặn ICMP đó: sender không biết phải giảm kích thước gói, gói lớn bị drop âm thầm — ping (gói nhỏ) thông nhưng TLS handshake hoặc HTTP response lớn treo không giải thích được.

Bạn đang debug một service có hành vi kỳ lạ: ping tới server thành công, curl HTTP response nhỏ cũng thành công, nhưng khi response lớn hơn (JSON vài KB, file download) thì kết nối treo rồi timeout. Thêm VPN vào thì mọi thứ đều chậm hơn. Không có lỗi rõ ràng trong log.

Đây là dấu hiệu kinh điển của PMTUD black-hole — một trong những vấn đề mạng khó debug nhất vì nó chọn lọc theo kích thước gói, không phải theo protocol hay port.

1. Analogy — Gói hàng qua nhiều hầm đường bộ

Hình dung bạn gửi một thùng hàng lớn từ Hà Nội tới TP.HCM qua xe tải. Tuyến đường đi qua nhiều hầm. Hầm bình thường cao 4.5m, nhưng có một hầm giữa đường chỉ cao 3.2m — xe tải cao quá không vào được.

Hai cách giải quyết:

  1. Phân mảnh tại chỗ: tháo thùng ra, chia làm nhiều kiện nhỏ vừa hầm, gửi từng kiện, ráp lại ở đích. Phức tạp, dễ mất kiện.
  2. Biết trước và điều chỉnh: trước khi lên đường, khảo sát chiều cao tất cả hầm, chọn xe tải vừa hầm thấp nhất. Không cần tháo ra.

TCP/IP làm theo cách thứ hai — đó là PMTUD. Vấn đề xảy ra khi hầm không đặt biển báo chiều cao mà chỉ chặn xe lại không báo gì cả — đó là PMTUD black-hole.

Vận chuyển hàngMạng TCP/IP
Chiều cao tối đa của hầmMTU — kích thước frame tối đa
Chia thùng thành nhiều kiệnPhân mảnh IP (fragmentation)
Khảo sát chiều cao mọi hầmPath MTU Discovery
Hầm chặn xe không báo lý doPMTUD black-hole (ICMP bị chặn)
Xe tải điều chỉnh kích thước tảiTCP MSS clamping
💡 Cách nhớ

MTU = giới hạn kích thước gói của một chặng mạng cụ thể. Path MTU = MTU nhỏ nhất trên toàn tuyến. PMTUD = cơ chế tìm Path MTU trước khi gửi. Black-hole = khi cơ chế đó bị phá vỡ do firewall chặn ICMP.

2. MTU — kích thước frame tối đa

MTU (Maximum Transmission Unit) là số byte tối đa của phần dữ liệu (payload) mà một frame ở tầng Link có thể mang, không tính header tầng Link.

Ethernet IEEE 802.3 chuẩn: MTU = 1500 byte. Đây là giá trị được hard-code vào tiêu chuẩn Ethernet từ năm 1980 và vẫn còn phổ biến tới ngày nay. Một số mạng nội bộ data center dùng Jumbo Frame với MTU 9000 byte để tối ưu throughput — nhưng chỉ hoạt động khi toàn bộ đường mạng hỗ trợ.

# Xem MTU cua cac interface
ip link show
# 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500
# 5: wg0: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1420  (WireGuard)
# 4: ppp0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1492  (PPPoE)

Tại sao 1500 byte? Ethernet ban đầu dùng dây đồng trục chung (bus topology), tất cả máy tranh nhau một dây. Gói quá lớn → một máy chiếm dây quá lâu → các máy khác chờ lâu. 1500 byte là điểm cân bằng giữa hiệu quả (header overhead ít) và công bằng (không chiếm kênh quá lâu). Giá trị này được giữ nguyên khi chuyển sang Ethernet hiện đại để đảm bảo tương thích.

Cấu trúc gói trên đường truyền — phần header chiếm không gian của 1500 byte:

GÓI IP TRONG FRAME ETHERNET (1500 byte payload)
IP header
20 byte
TCP header
20 byte
TCP payload (dữ liệu ứng dụng)
tối đa 1460 byte = MSS
← tổng 1500 byte (Ethernet MTU) →

Đây là lý do MSS (Maximum Segment Size) của TCP thường là 1460 byte = 1500 (MTU) − 20 (IP header) − 20 (TCP header). MSS được trao đổi trong gói SYN/SYN-ACK của handshake — xem bài 02 — TCP reliability.

3. Fragmentation — phân mảnh khi gói quá lớn

Khi một gói IP lớn hơn MTU của chặng tiếp theo, router phải phân mảnh (fragment) gói đó thành nhiều mảnh nhỏ hơn. Mỗi mảnh được đóng gói thành frame riêng và gửi đi.

Cơ chế phân mảnh tầng IP:

  • Mỗi mảnh mang Fragment Offset (offset trong gói gốc tính bằng đơn vị 8 byte) và cờ More Fragments (MF) cho biết còn mảnh tiếp theo.
  • Mảnh cuối có MF = 0.
  • Reassembly (ráp lại) xảy ra tại đích cuối cùng — không phải tại router trung gian (khác IPv4 reassembly trong IPv6 — IPv6 không cho phép fragmentation tại router).
sequenceDiagram
    participant C as Client
    participant R as Router (MTU 1500)
    participant S as Server
    C->>R: IP packet 2800 byte (qua len MTU)
    Note over R: Fragment thanh 2 manh
    R->>S: Fragment 1 (1480 byte data, MF=1, offset=0)
    R->>S: Fragment 2 (1320 byte data, MF=0, offset=185)
    Note over S: Reassemble --> 2800 byte goc

Vấn đề của fragmentation:

  • Hiệu năng kém: router trung gian phải xử lý thêm; đích phải buffer tất cả mảnh trước khi xử lý.
  • Mảnh mồ côi: nếu 1 mảnh bị mất, toàn bộ gói phải truyền lại — TCP retransmit cả packet, không phải từng mảnh.
  • Firewall stateless: nhiều firewall chỉ inspect mảnh đầu tiên (chứa TCP/UDP header); mảnh sau thiếu header → không lọc được.
  • Không thể dùng với IPv6: IPv6 không cho phép router fragment — sender phải tự fragment trước khi gửi.

Đây là lý do TCP hiện đại tránh fragmentation — thay vào đó dùng PMTUD để biết trước MTU và điều chỉnh kích thước segment.

4. Path MTU Discovery — tìm MTU nhỏ nhất trên toàn tuyến

PMTUD (Path MTU Discovery) là cơ chế cho phép TCP sender tự động tìm ra MTU nhỏ nhất trên toàn tuyến đường từ sender tới receiver, mà không cần phân mảnh.

Cơ chế (RFC 1191):

  1. TCP sender đặt cờ Don't Fragment (DF) trong header IP — báo router "đừng phân mảnh gói này".
  2. Sender gửi gói với kích thước lớn nhất có thể (thường = MTU của interface đầu ra).
  3. Nếu một router trên đường thấy gói lớn hơn MTU của chặng tiếp theo, nó không thể fragment (do DF=1) → gửi lại ICMP type 3 code 4: "Fragmentation Needed and DF Set", kèm MTU của chặng đó.
  4. Sender nhận ICMP, cập nhật PMTU cache, giảm kích thước gói và thử lại.
  5. Lặp cho đến khi tìm ra MTU nhỏ nhất.
sequenceDiagram
    participant C as Client (MTU 1500)
    participant R as Router (MTU 1400)
    participant S as Server
    C->>R: IP 1500 byte, DF=1
    Note over R: MTU 1400 < 1500, khong the fragment (DF=1)
    R-->>C: ICMP Type 3 Code 4: "Frag Needed, MTU=1400"
    Note over C: Cap nhat PMTU cache: 1400
    C->>R: IP 1400 byte, DF=1
    R->>S: IP 1400 byte (vua MTU)
    S-->>C: ACK

Test PMTU thủ công:

# Test MTU toi destination (goi 1472 byte data + 28 byte IP/ICMP header = 1500)
ping -M do -s 1472 8.8.8.8
# PING 8.8.8.8 (8.8.8.8) 1472(1500) bytes of data.
# 1472 bytes from 8.8.8.8: icmp_seq=1 ttl=118 time=15.2 ms

# Neu MTU thap hon:
ping -M do -s 1472 vpn-server
# ping: local error: message too long, mtu=1420
# -> MTU toi vpn-server chi 1420

# Xem PMTU cache
ip route get 8.8.8.8
# 8.8.8.8 via 192.168.1.1 dev eth0 src 192.168.1.100
# (cache co the co them: cache mtu 1420)

5. PMTUD black-hole — khi ICMP bị chặn

PMTUD black-hole là tình trạng PMTUD bị phá vỡ vì firewall hoặc security group chặn gói ICMP type 3 code 4 ("Fragmentation Needed").

Cơ chế gây ra black-hole:

sequenceDiagram
    participant C as Client
    participant F as Firewall (chặn ICMP)
    participant R as Router (MTU 1400)
    participant S as Server
    C->>R: IP 1500 byte, DF=1
    R-->>F: ICMP: "Frag Needed, MTU=1400"
    Note over F: DROP ICMP (firewall rule)
    Note over C: Khong nhan duoc ICMP, van giu MTU 1500
    C->>R: IP 1500 byte, DF=1 (retry)
    R-->>F: ICMP (bi drop lai)
    Note over C: TCP retransmit... -> ETIMEDOUT

Tại sao ping vẫn thông? ping gửi gói ICMP nhỏ — mặc định 56 byte data (84 byte tổng). Gói này nhỏ hơn MTU của mọi chặng → không cần fragmentation → PMTUD không kích hoạt → không cần ICMP type 3 → ping thành công.

Nhưng TLS handshake hoặc HTTP response lớn thất bại:

  • TLS ClientHello thường 300-500 byte — qua được.
  • TLS Certificate từ server thường 2-4 KB — bị router muốn fragment nhưng DF=1 → gửi ICMP → firewall chặn → client không nhận → treo.
  • HTTP response lớn (JSON, HTML) tương tự.

Các môi trường hay gặp PMTUD black-hole:

  • VPN (WireGuard, IPsec, OpenVPN): thêm header VPN vào mỗi gói → MTU hiệu dụng giảm. WireGuard giảm 60 byte (header 40 byte + overhead), IPsec giảm 50-85 byte. Interface MTU thường đặt 1420 hoặc 1280 nhưng nếu không config đúng → black-hole.
  • PPPoE (ADSL/FTTH nhiều ISP Việt Nam): thêm 8 byte header PPPoE → MTU giảm từ 1500 xuống 1492.
  • GRE tunnel, VXLAN: thêm header → giảm MTU.
  • Firewall corporate block "all ICMP": quản trị viên chặn hết ICMP vì sợ ICMP flood, vô tình chặn cả type 3 code 4 cần thiết cho PMTUD.

6. Chẩn đoán và fix PMTUD black-hole

Bước 1: Xác nhận là black-hole bằng ping DF bit:

# Test voi payload tang dan cho den khi that bai
ping -M do -s 1400 target-server   # OK?
ping -M do -s 1450 target-server   # OK?
ping -M do -s 1472 target-server   # Fail? -> MTU nam o dau do

# Ket qua:
# s=1400 OK, s=1450 FAIL -> MTU khoang 1428 (1450 + 28 header = 1478 > MTU)

Bước 2: Tìm MTU của interface và tunnel:

ip link show
# eth0: mtu 1500
# wg0: mtu 1420

# Voi Docker
ip link show docker0
# docker0: mtu 1500  (nhung container co the khac)

Bước 3: Fix — ba cách tiếp cận:

a) MSS Clamping (khuyến nghị cho router/firewall): Thay vì phụ thuộc vào PMTUD, router rewrite trường MSS trong SYN/SYN-ACK để TCP tự giới hạn segment size:

# iptables MSS clamping (Linux router/VPN server)
iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN \
  -j TCPMSS --clamp-mss-to-pmtu

# Hoac set manual (vi du voi WireGuard MTU 1420):
# MSS = 1420 - 40 (IP+TCP header) = 1380
iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN \
  -j TCPMSS --set-mss 1380

b) Giảm MTU của interface:

# Set MTU cho interface
ip link set dev wg0 mtu 1380

# Permanent (systemd-networkd)
# /etc/systemd/network/wg0.network
# [Link]
# MTUBytes=1380

c) Allow ICMP type 3 qua firewall:

# iptables: allow ICMP fragmentation-needed
iptables -A INPUT  -p icmp --icmp-type fragmentation-needed -j ACCEPT
iptables -A OUTPUT -p icmp --icmp-type fragmentation-needed -j ACCEPT
iptables -A FORWARD -p icmp --icmp-type fragmentation-needed -j ACCEPT

# AWS Security Group: add inbound rule
# Type: Custom ICMP - IPv4, Protocol: Destination Unreachable (3)
⚠️ VPN và container thường là nguyên nhân

WireGuard mặc định MTU 1420 byte; OpenVPN thường 1500 nhưng cần tính lại với overhead. Docker và Kubernetes tạo virtual interface với MTU bằng interface host — nếu host là VM trong cloud với MTU 9000 (jumbo) nhưng external network chỉ 1500, container gửi gói 9000 byte ra ngoài → black-hole. Khi deploy service mới và thấy "ping OK nhưng response lớn fail", kiểm tra MTU của tất cả interface trên đường đi trước.

7. Pitfall — nhầm lẫn thường gặp

Nhầm 1: "Ping được là network ổn, lỗi do code." ✅ Ping dùng ICMP gói nhỏ — không đụng fragmentation, không đụng PMTUD. Hoàn toàn có thể ping thông mà TCP data lớn vẫn timeout. Test bằng ping -M do -s 1472 hoặc nc -zv để kiểm tra TCP thật sự.

Nhầm 2: "Tôi đã cấu hình VPN với MTU 1420, nên ổn rồi." ✅ MTU 1420 trên interface WireGuard là đúng, nhưng MSS clamping có được cấu hình chưa? Nếu TCP hai đầu thương lượng MSS dựa trên interface đầu ra (không phải WireGuard), segment có thể vẫn lớn hơn 1420 − 40 = 1380. Cần cả MSS clamping MTU đúng.

Nhầm 3: "Fragmentation giải quyết được hết, không cần lo MTU." ✅ Fragmentation là cơ chế fallback tệ: hiệu năng kém, không dùng được với IPv6, firewall stateless không inspect được. TCP hiện đại cố tránh fragmentation bằng PMTUD. Firewall chặn ICMP type 3 code 4 phá vỡ cơ chế đó — đây là root cause của black-hole.

Nhầm 4: "Chặn tất cả ICMP để an toàn." ✅ ICMP type 3 code 4 ("Fragmentation Needed") là cần thiết cho PMTUD — chặn nó phá vỡ TCP cho gói lớn. Chặn hết ICMP là over-blocking. Rule đúng: block ICMP echo (ping) nếu muốn ẩn host, nhưng allow ICMP type 3 và type 11 (TTL exceeded — cần cho traceroute).

8. Liên hệ các bài khác

  • Bài 02 — TCP reliability: MSS được trao đổi trong SYN/SYN-ACK của handshake — đây là cơ chế TCP tự giới hạn segment size dựa trên MTU, trước khi cần PMTUD.
  • Bài 07 — Lỗi mạng thường gặp: PMTUD black-hole biểu hiện như ETIMEDOUT — gói bị drop âm thầm giống firewall DROP. Bài 07 cung cấp bảng tra để phân biệt các loại timeout.
  • Khoá Foundations — Mô hình TCP/IP 4 tầng: MTU là khái niệm tầng Link (Layer 2), nhưng phân mảnh xảy ra ở tầng Internet (Layer 3). Hiểu phân tầng giúp định vị đúng nơi MTU áp dụng.

9. 📚 Deep Dive — RFC và tài liệu gốc

📚 RFC & tài liệu tham khảo

Đọc khi muốn đi tới tận gốc:

Ghi chú: RFC 1191 là nền tảng cần đọc để hiểu cơ chế gốc. RFC 8899 giải thích tại sao QUIC (HTTP/3) tránh được vấn đề PMTUD black-hole — QUIC thực hiện PLPMTUD trong tầng ứng dụng, không phụ thuộc ICMP tầng mạng.

10. Tóm tắt

  • MTU là kích thước frame tối đa một chặng mạng có thể mang. Ethernet thường 1500 byte; VPN/tunnel giảm xuống 1420 hoặc ít hơn do thêm header.
  • MSS = MTU − 40 byte (IP + TCP header) = thường 1460 byte — kích thước tối đa của một TCP segment. Được thương lượng trong SYN/SYN-ACK.
  • Fragmentation xảy ra khi gói lớn hơn MTU của chặng tiếp theo. Tốn kém, không dùng được với IPv6 — TCP hiện đại tránh bằng cách dùng PMTUD.
  • PMTUD đặt cờ DF=1 và dựa vào ICMP type 3 code 4 từ router để tìm MTU nhỏ nhất trên toàn tuyến.
  • PMTUD black-hole xảy ra khi firewall chặn ICMP type 3 code 4: gói lớn bị drop im lặng, sender không biết giảm kích thước. Biểu hiện: ping thông, gói nhỏ thông, gói lớn (TLS certificate, HTTP response lớn) treo.
  • Hay gặp với VPN: WireGuard, IPsec, PPPoE đều thêm overhead, giảm MTU hiệu dụng. Cần cấu hình MSS clamping và MTU đúng trên interface tunnel.
  • Fix: MSS clamping tại router/VPN (--clamp-mss-to-pmtu), giảm MTU interface, hoặc allow ICMP type 3 qua firewall.
  • Test: ping -M do -s 1472 <host> để xác nhận MTU có vấn đề không.

11. Tự kiểm tra

Tự kiểm tra
Q1
Vì sao MTU Ethernet là 1500 byte và giá trị này còn tồn tại đến ngày nay?
1500 byte là điểm cân bằng từ thời Ethernet bus topology (dây đồng trục chung): đủ lớn để overhead header (40 byte IP+TCP) không chiếm tỷ lệ quá cao, đủ nhỏ để không chiếm kênh chia sẻ quá lâu gây starvation cho máy khác. Giá trị được giữ nguyên tới nay vì tương thích ngược — nếu thay đổi, các thiết bị cũ và firewall không đồng nhất về MTU sẽ gây fragmentation hoặc black-hole trên diện rộng.
Q2
Tại sao TCP MSS thường là 1460 byte, không phải 1500 byte?
MSS là kích thước tối đa của phần dữ liệu trong một TCP segment, không tính header. Khi frame Ethernet có MTU 1500 byte, phần payload đó phải chứa cả IP header (20 byte tối thiểu) và TCP header (20 byte tối thiểu). Vậy tối đa còn lại cho dữ liệu TCP là 1500 − 20 − 20 = 1460 byte = MSS. MSS được trao đổi trong gói SYN và SYN-ACK của handshake để hai bên biết giới hạn này ngay từ đầu.
Q3
Bạn cài WireGuard VPN với MTU mặc định 1420 trên interface wg0. Kết nối qua VPN hoạt động nhưng các trang web nặng load chậm hoặc treo. Nghi ngờ gì và kiểm tra thế nào?
Nghi ngờ PMTUD black-hole hoặc MSS không được clamp đúng. WireGuard thêm overhead header ~60 byte, nên MTU hiệu dụng cho data là 1420 − 40 (IP+TCP) = 1380 byte. Nếu TCP sender thương lượng MSS dựa trên interface khác (1460) thay vì wg0 (1380), segment gửi qua VPN sẽ vượt MTU → router muốn fragment nhưng DF=1 → gửi ICMP type 3 → nếu ICMP bị chặn → black-hole. Kiểm tra: ping -M do -s 1372 vpn-server (1372 + 28 header = 1400 < 1420). Fix: cấu hình MSS clamping với --clamp-mss-to-pmtu hoặc set MSS thủ công 1380.
Q4
Vì sao IPv6 không cho phép router fragment gói tin, trong khi IPv4 thì cho phép?
IPv6 được thiết kế lại từ đầu với mục tiêu hiệu năng cao hơn. Fragmentation tại router tốn CPU và memory của router, làm chậm forwarding. Trong IPv6, sender chịu trách nhiệm đảm bảo gói không vượt Path MTU — dùng PMTUD hoặc PLPMTUD. Router IPv6 đơn giản hơn (không cần logic fragmentation), nhanh hơn. Nếu sender gửi gói quá lớn, router IPv6 trả ICMPv6 "Packet Too Big" (tương đương ICMP type 3 code 4 của IPv4) và drop gói — không fragment.
Q5
Ping tới server thành công nhưng TLS handshake bị treo. Giải thích cơ chế PMTUD black-hole gây ra điều này.
ICMP echo request (ping) thường nhỏ (84 byte) — nhỏ hơn MTU của mọi chặng → không cần fragmentation → PMTUD không kích hoạt → ping thành công. Nhưng TLS Certificate từ server thường 2-4 KB. TCP gửi gói lớn với DF=1. Một router trên đường có MTU thấp hơn, muốn fragment nhưng không thể (DF=1), gửi ICMP type 3 code 4 về client. Firewall chặn ICMP đó → client không bao giờ nhận được thông báo giảm MTU → tiếp tục gửi gói 1500 byte → tiếp tục bị drop âm thầm → TCP retransmit → cuối cùng ETIMEDOUT. Ping không đụng đến path này nên "thành công".
Q6
So sánh hai cách fix PMTUD black-hole: MSS clamping và giảm MTU interface. Khi nào dùng cái nào?
  • Giảm MTU interface: cấu hình trực tiếp trên interface VPN/tunnel (ví dụ ip link set wg0 mtu 1380). Đơn giản, hiệu quả khi bạn kiểm soát được endpoint. Nhưng cần cấu hình đúng trên cả hai đầu và persist qua reboot.
  • MSS clamping (--clamp-mss-to-pmtu): router rewrite trường MSS trong SYN/SYN-ACK để TCP tự giới hạn. Hoạt động ngay cả khi sender và receiver không biết MTU thấp — router lo hết. Ưu tiên cho VPN gateway hoặc firewall phải handle nhiều client.

Trong thực tế: dùng cả hai — giảm MTU interface để đúng về mặt nguyên lý, thêm MSS clamping làm backstop. Cho phép ICMP type 3 qua firewall là fix căn gốc nếu có thể.

Bài tiếp theo: Module 1 — Tổng kết & cheat sheet

Bài này đáng gửi cho bạn học cùng?

Copy link đã gắn nguồn — dán group, chat, hoặc LinkedIn.

Bài này có giúp bạn hiểu bản chất không?

Hỏi đáp về bài này

Chưa có câu hỏi

Đặt câu hỏi

Có gì chưa rõ trong bài? Đặt câu hỏi đầu tiên — câu trả lời từ cộng đồng giúp bạn (và người sau).

Đặt câu hỏi đầu tiên

Bài tiếp theo

Module 1 — Tổng kết & cheat sheet