ゲームラグ白書 › TCP再送の根本原因
MTUブラックホール(大きいパケットだけ繰り返し失われる) PMTU black hole
原因ID rt-mtu · 主担当 インフラチーム・ネットワークインフラ · 副担当 インフラチーム・サーバーインフラ, ゲーム開発チーム・サーバー開発
図と実験のあるメインページでこのカードを開く →
途中の区間が受け取れるサイズが小さくなったのに「大きすぎる」という通知(ICMP)が遮断されると、大きなパケットは何度送り直しても消え続けます。
なぜ VPN・トンネル区間で最大サイズが小さくなり、サイズ超過の通知はファイアウォールで遮断 → すると 送信側は理由がわからないまま同じ大きなパケットを再送し続け、RTOは2倍ずつ増加 → 画面では 普段は問題ないが、インベントリ・人の多い場所・入場時のロードのように大きなデータがやり取りされる瞬間に、後続の小さなパケットまですべて止まり、最終的に切断や無限ロード
- 症状
- フリーズ, 切断, 接続不可・無限ロード
- 要因
- パケットロス
- 誰に起きるか
- 特定の地域・ISP, 自分だけ
- いつ
- 特定の操作をしたとき, 接続直後・メンテ明け
- 担当
- 主担当 インフラチーム・ネットワークインフラ · 副担当 インフラチーム・サーバーインフラ, ゲーム開発チーム・サーバー開発
- ゲーム開発チームの対応
- サーバー側で直接下げるならソケットの最大セグメントサイズ(TCP_MAXSEG)を設定、ゲームコードでメッセージを細かく分割するだけでは防げない(TCPが送るデータをMSSのサイズでまとめ直すため)。
- インフラチームの対応
- ネットワーク:境界の機器でMSSを調整(clamping)、ファイアウォール・クラウドのネットワークACLでサイズ超過のICMP(タイプ3コード4、fragmentation needed)を許可。サーバー機器・OS:経路MTUの設定、サーバーのファイアウォール・クラウドのセキュリティグループでもサイズ超過のICMPが遮断されていないか確認、最後の安全策としてLinuxのtcp_mtu_probing=1。
- 数値の目安
- 通常は1,500バイトで、トンネルを通ると1,400前後です。同じパケットが5〜6回再送されると、止まる時間は10秒を超えます。
- グラフでは
- 一部だけ高い · 接続ごとのRTO・backoff、地域・通信事業者ごとの切断数
- 確認箇所
- 問題の接続の再送をサーバー側のパケットキャプチャやbcc tcpretrans -s(シーケンス番号を表示)で確認し、ss -tiでその接続のmss・pmtu・backoffを確認。サーバーからそのユーザーのアドレスに、小さなpingとDFを立てた1,500バイトのping(ping -M do -s 1472)を送って比較
- 該当する場合
- MSSいっぱいのパケットが同じシーケンス番号で、間隔を2倍ずつ延ばしながら再送され続け、それより小さなパケットは届く。サイズ超過のICMP(Wiresharkのフィルターicmp.type == 3 and icmp.code == 4)は届かず、小さなpingには応答があるのに、大きなDF付きのpingだけが応答なく消える
- 該当しない場合
- 小さなパケットも一緒に消えるなら、サイズと関係ないパケットロス(「ボトルネックでのキューあふれ」「経路変更・ECMPの不良経路」)。サイズ超過のICMPが届き、ss -tiのpmtuが小さくなるなら、経路MTU探索は正しく動作している
- 確認手段
- インフラのツールで確認(ゲームコード不要)
- もっと詳しく
- tcp_mtu_probing=1は、再送タイムアウトが数秒(tcp_retries1=3に相当)続いた後になって初めてブラックホールと判断し、MSSを1,024バイトに下げます。その間は止まったままになるため、これは最後の安全策にとどめ、事前に防ぐMSSの調整を先に行います。
出典
- RFC 1191: Path MTU discovery IETF
経路MTU探索:大きすぎるパケットはICMP「fragmentation needed and DF set」(タイプ3コード4)で通知 - RFC 2923: TCP Problems with Path MTU Discovery IETF
ICMPが遮断され、大きなパケットだけが消え続けるPMTUブラックホールの問題 - RFC 4821: Packetization Layer Path MTU Discovery IETF
ICMPを使わずにトランスポート層がパケットサイズを探る方法(Linuxのtcp_mtu_probingの土台) - IP Sysctl Linux kernel
tcp_mtu_probing:0は無効、1はブラックホールを検知したときだけ、2は常に(開始時のMSSはtcp_base_mss)。tcp_retries1のデフォルト値は3 - net/ipv4/tcp_timer.c Linux kernel
RTOによる再送がtcp_retries1回続くとブラックホールを検知したとみなし、MTU探索を有効にしてMSSを下げる - include/net/tcp.h Linux kernel
TCP_BASE_MSS = 1,024バイト - RFC 6298: Computing TCP's Retransmission Timer IETF
再送タイマーが満了するたびにRTOを2倍に延ばす - iptables-extensions(8) — Linux manual page netfilter
TCPMSS --clamp-mss-to-pmtu:ICMPを遮断する区間のせいで大きなパケットが止まる問題を、SYNのMSSを調整して回避 - tcp(7) — Linux manual page Linux man-pages
TCP_MAXSEG:送出するパケットの最大セグメントサイズ、接続前に設定すると相手に通知するMSSも変わる - Network maximum transmission unit (MTU) for your EC2 instance AWS
インターネットゲートウェイ・VPNはMTU 1,500、PMTUDにはICMPタイプ3コード4が必要で、セキュリティグループ・ネットワークACLが遮断すると受け取れない - MTU considerations | Cloud VPN Google Cloud
Cloud VPNゲートウェイのMTUは1,460バイト、IPv4トンネルのペイロードMTUは1,406バイト(トンネルを通ると1,400前後) - Demonstrations of tcpretrans, the Linux eBPF/bcc version IO Visor
再送ごとに1行ずつ表示し、-sは再送したパケットのシーケンス番号もあわせて表示 - ss(8) — Linux manual page iproute2
ss -iのmss、pmtu(経路MTU)、backoff(RTOが2倍に延びた回数) - ping(8) — Linux manual page iputils
-M doはDFを立て、カーネルが把握している経路MTUより大きなパケットは送らない、-sはデータサイズ(ICMPヘッダーの8バイトは別) - Display Filter Reference: Internet Control Message Protocol Wireshark
icmp.type・icmp.codeの表示フィルター
あわせて読みたい原因
同じ層:TCP再送の根本原因
同じ症状(フリーズ)を起こすほかの層の原因
図と実験のあるメインページでこのカードを見る