InfraAcademy

InfraAcademy Blog

pingは通るのに、大きなデータだけ固まる。MTUとPath MTU Discoveryの仕組みから、パケットの大きさの問題を切り分ける

| #Linux #ネットワーク #トラブルシューティング
Linuxをブラウザで試してみる

Linux・ネットワーク・AWSを、環境構築なしで実践学習できます

この記事を共有

こんにちは、インフラエンジニアのryuです。

こんな症状に出会ったことはないでしょうか。

ping は通る。SSHでログインもできる。なのに、ls で長い一覧を出した瞬間に画面が固まる。あるいは、Webサイトのトップページは開くのに、大きな画像やファイルのダウンロードだけが途中で止まる。

疎通はあるのだから、ネットワークは正常に見えます。ファイアウォールも、ルーティングも、DNSも問題なさそうです。

こういうとき、疑ってほしいものがあります。パケットの大きさ、つまりMTUの問題です。

今日は、MTUとPath MTU Discoveryの仕組みを押さえたうえで、パケットの大きさが原因のトラブルをどう切り分けるかを見ていきます。

そもそもMTUって何なのでしょうか?

MTUは、Maximum Transmission Unitの略です。ひとつのリンクで一度に送れるIPパケットの最大サイズを表します。

一般的なイーサネットのMTUは1500バイトです。Linuxなら、インターフェースごとの値を次のコマンドで確認できます。

$ ip link show eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000

宅配便にたとえてみましょう。MTUは、その道を走るトラックに積める荷物の最大サイズです。道ごとにトラックの大きさが違っていて、途中にひとつでも小さなトラックしか通れない区間があれば、大きな荷物はそこで止まってしまいます。

経路全体を通して送れる最大サイズのことを、Path MTU(PMTU)と呼びます。途中のリンクのうち、いちばん小さいMTUがそのままPath MTUになります。

MTUとMSSの関係を押さえておこう

MTUと一緒に出てくる言葉に、MSS(Maximum Segment Size)があります。

MSSは、TCPが一度に運ぶデータ部分の最大サイズです。MTUからIPヘッダーとTCPヘッダーを引いた値になります。

項目 IPv4での代表的な値 計算
MTU(イーサネット) 1500バイト ―
IPヘッダー 20バイト オプションなしの場合
TCPヘッダー 20バイト オプションなしの場合
MSS 1460バイト 1500 − 20 − 20

TCPは、接続を始める3ウェイハンドシェイクのときに、お互いのMSSを伝え合います。ハンドシェイクの流れはTCPの3ウェイハンドシェイクとは?で解説しているので、あわせて読むと理解しやすいと思います。

MTUが1500より小さくなる場所

では、経路の途中でMTUが小さくなるのはどんな場所でしょうか。

代表的なのは、パケットを別のヘッダーで包んで運ぶ場所です。PPPoEやVPN、各種のトンネルがこれにあたります。包むためのヘッダーの分だけ、中に入れられるパケットが小さくなります。

たとえば、日本のフレッツ回線でPPPoE接続をする場合、MTUは1454バイトに設定するのが一般的です。ヤマハのルーターのFAQでも、フレッツでのMTUとして1454が案内されています。

逆に、大きくなる場所もあります。AWSのEC2の多くのインスタンスはジャンボフレームに対応しており、VPC内ではMTU 9001で通信できます。ただし、インターネットゲートウェイを出るトラフィックは1500バイトまでです。

Path MTU Discoveryは、どうやって経路のMTUを知るのか?

送信元は、経路の途中のMTUを最初から知っているわけではありません。そこで使われるのが、Path MTU Discovery(PMTUD)です。RFC 1191で定められています。

仕組みを順番に追ってみます。

  1. 送信元は、IPヘッダーのDF(Don't Fragment)ビットを立てて、自分のリンクのMTUいっぱいのパケットを送る
  2. 途中のルーターは、次のリンクに入りきらないパケットを受け取ると、分割せずに捨てる
  3. そのルーターは送信元へ、ICMPの「Destination Unreachable / Fragmentation Needed」を返す。この中に次のリンクのMTUが入っている
  4. 送信元はその値を覚えて、以後はそのサイズに収まるようにパケットを送る

宅配便でいえば、途中の営業所から「この先はこのサイズまでしか運べません」という連絡が来る仕組みです。連絡を受けた送り主は、荷物を小分けにして送り直します。

IPv6では、そもそもルーターがパケットを分割しません。大きすぎるパケットに対しては、ICMPv6のPacket Too Bigが返されます。こちらの手順はRFC 8201にまとめられています。

なぜ「pingは通るのに固まる」が起きるのか?

ここまで読むと、もうお気づきかもしれません。PMTUDは、途中のルーターから返ってくるICMPが届くことを前提にしています。

ところが現場では、セキュリティ対策としてICMPをまとめて遮断しているファイアウォールが少なくありません。するとどうなるでしょうか。

ブラックホールと呼ばれる現象

途中のルーターは大きなパケットを捨て、ICMPで知らせようとします。でも、そのICMPは途中のファイアウォールで捨てられます。

送信元は、パケットが捨てられたことも、小さくすべきことも知りません。同じ大きさのパケットを再送し続け、そのたびに捨てられます。

この状態は、パケットが吸い込まれて何も返ってこない「PMTUDブラックホール」と呼ばれています。RFC 2923でも、接続は確立して転送が始まったように見えるのに、そのまま進まずタイムアウトまで固まる症状として紹介されています。

小さなパケットだけが通る理由

ここで、冒頭の症状を思い出してください。

ping の既定のパケットは小さいので、MTUの制限に引っかかりません。TCPのハンドシェイクも、SSHのログイン直後のやりとりも、パケットは小さいままです。

問題が出るのは、MTUいっぱいのパケットが流れ始めたときです。長いコマンド出力、大きなファイル、サイズの大きいサーバー証明書を含むTLSのハンドシェイクなどがそうです。

つまり、通信の中身が大きくなった瞬間にだけ止まるのが、このトラブルのいちばんの特徴です。症状の見え方をまとめると次のようになります。

操作 パケットの大きさ 結果
ping(既定サイズ) 小さい 通る
TCPの接続確立、SSHのログイン 小さい 通る
長いコマンド出力、ファイル転送 MTUいっぱい 途中で固まる
HTTPSで大きな応答を受け取る MTUいっぱい 読み込みが終わらない

パケットの大きさの問題を、コマンドで切り分けてみよう

では、実際に確かめる方法を見ていきます。通信のどの層で止まっているかを全体から追う手順は、ping・ss・tcpdumpで疎通トラブルを層ごとに切り分けるにまとめました。ここでは、その中でMTUを疑うときの確認に絞ります。

pingでサイズを指定して、DFビット付きで送る

Linuxの ping では、-M do でDFビットを立て、-s でデータ部分のサイズを指定できます。

IPv4では、ICMPヘッダーが8バイト、IPヘッダーが20バイトです。そのため、1500バイトのパケットを送るには -s 1472 を指定します。

# 1500バイト(1472 + 8 + 20)のパケットを、分割禁止で送る
$ ping -M do -s 1472 -c 3 203.0.113.10

# 少しずつ小さくして、通るサイズを探す
$ ping -M do -s 1400 -c 3 203.0.113.10
$ ping -M do -s 1426 -c 3 203.0.113.10

1472では応答がなく、1426なら通る。こうした結果が出たら、経路のどこかにMTUが1454前後の区間があると考えられます。

途中のルーターからICMPが返ってくる環境なら、ping がフラグメントが必要だというメッセージとMTUの値を表示してくれます。何も返らずにただ応答がないなら、ICMPが途中で止められている可能性が高くなります。

tracepathで、経路のどこでMTUが変わるかを見る

もうひとつ便利なのが tracepath です。traceroute のように経路をたどりながら、途中で分かったPath MTUを表示してくれます。管理者権限がなくても実行できるのも利点です。

$ tracepath -n 203.0.113.10
 1?: [LOCALHOST]                      pmtu 1500
 1:  192.168.1.1                       0.512ms
 2:  192.168.1.1                       0.498ms pmtu 1454
 2:  198.51.100.1                      5.231ms
 ...
     Resume: pmtu 1454

出力はイメージです(アドレスは説明用のものです)。pmtu の値が変わった行が、MTUが小さくなる区間の目印です。経路の読み方そのものはルーティングとは?の記事で解説しています。

ssで、実際の接続のMSSとPMTUを確かめる

すでに張られているTCP接続について、Linuxがどのサイズで送っているかは ss -ti で確認できます。

$ ss -ti dst 203.0.113.10
ESTAB 0 0 192.168.1.20:52814 203.0.113.10:443
     cubic wscale:7,7 rto:208 rtt:5.1/2.3 mss:1448 pmtu:1500 rcvmss:536 advmss:1448 ...

pmtu と mss の値を見れば、その接続がどのサイズを前提に動いているかが分かります。本来1454の経路なのに pmtu:1500 のままで再送が増えているなら、PMTUDがうまく働いていないと判断できます。

原因が分かったら、どう対処する?

切り分けができたら、次は対処です。代表的な方法を3つ紹介します。

1. ICMPの必要な種類だけを通す

いちばん素直な対処は、PMTUDに必要なICMPを通すことです。

ICMPを全部止めるのではなく、IPv4のDestination Unreachable(Fragmentation Needed)と、IPv6のPacket Too Bigは許可しておく。これだけでブラックホールは起きにくくなります。

Linuxのファイアウォールの全体像はfirewalld・nftables・iptablesの関係で整理しているので、ルールを見直すときの参考にしてください。

2. MSSクランプで、TCPのサイズを最初から抑える

途中の機器を自分で直せないこともあります。そんなときに使われるのがMSSクランプです。

ルーターやゲートウェイで、通過するTCPのハンドシェイクに書かれたMSSを、経路に合わせた値に書き換えます。最初から小さいサイズで話が始まるので、PMTUDに頼らずに済みます。

# nftables:転送するTCPのSYNのMSSを、経路のMTUに合わせて書き換える
nft add rule inet filter forward tcp flags syn tcp option maxseg size set rt mtu

# iptables での同等の設定
iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu

家庭用やオフィスのルーターでPPPoEを使う場合も、多くの製品がこのMSSの調整機能を持っています。

3. インターフェースのMTUを下げる、またはPLPMTUDを使う

VPNやトンネルのインターフェースなど、MTUが小さいと分かっている場所では、インターフェース自体のMTUを下げる方法もあります。

# 一時的にMTUを変更する(再起動で戻るので、恒久化は各ディストリの設定で)
$ sudo ip link set dev wg0 mtu 1400

もうひとつの考え方が、RFC 4821のPacketization Layer Path MTU Discovery(PLPMTUD)です。ICMPが返ってくるのを待つのではなく、TCPなどが少しずつ大きなパケットを試しながらサイズを探ります。Linuxでは net.ipv4.tcp_mtu_probing というカーネルパラメータで、この探り方を有効にできます。

どの対処も一長一短があります。手を入れられる範囲と影響の大きさを見て選ぶことが大切です。

MTUまわりで、現場でよくある勘違い

最後に、MTUの話をするとよく出てくる勘違いを2つ取り上げておきます。

ひとつ目は、「ICMPは攻撃に使われるから、全部止めたほうが安全」という考え方です。

たしかに、外部からの ping に応答しない設定にしたい場面はあります。ただ、それはエコー要求の話です。ICMPには、ここまで見てきたようにPMTUDを支える大事な種類も含まれています。種類を区別せずにまとめて止めると、セキュリティのつもりが通信障害の原因になってしまいます。

ふたつ目は、「MTUを大きくすれば、どこでも通信が速くなる」という考え方です。

ジャンボフレームは、同じネットワークの中で、経路上のすべての機器が対応しているときに効果を発揮します。対応していない機器がひとつでも挟まれば、そこで大きなパケットは止まります。AWSの例でいえば、VPCの中では9001で送れても、インターネットへ出るときは1500に収める必要があります。

どちらの勘違いにも共通しているのは、MTUを自分のサーバーだけの設定だと考えてしまうことです。MTUは、経路全体で決まるものだと覚えておいてください。

切り分けの結果は、経路図と一緒に残しておく

もうひとつ、実務でのおすすめがあります。MTUの問題を見つけたら、どの区間でいくつに小さくなっていたのかを、ネットワークの経路図に書き込んでおくことです。

MTUのトラブルは、VPNの追加や回線の切り替えのたびに形を変えて戻ってきます。前回どこでつまずいたかが記録に残っていれば、次に同じ症状が出たときの切り分けがぐっと速くなります。

まとめ

今日は、MTUとPath MTU Discoveryの仕組みから、パケットの大きさが原因のトラブルを見てきました。

振り返っておきます。

  • MTUはリンクごとの最大サイズで、経路全体で通せる最大サイズがPath MTU
  • PPPoEやVPNなど、パケットを包む場所ではMTUが小さくなる
  • PMTUDは途中のルーターからのICMPに頼っているため、ICMPを全部止めるとブラックホールが起きる
  • 「小さい通信は通るのに、大きい通信だけ固まる」が最大の手がかり
  • ping -M do -s・tracepath・ss -ti で確かめ、ICMPの許可やMSSクランプで対処する

MTUのトラブルは、疎通確認だけでは見つからないのが厄介なところです。でも、仕組みを一度理解しておけば、症状を聞いた瞬間に「サイズかもしれない」と気づけるようになります。

ネットワークの基礎を順番に学び直したい方は、InfraAcademyのネットワークのロードマップも使ってみてください。ブラウザ上でコマンドを打ちながら、IPやTCPの仕組みを一つずつ確かめられます。

参考

Next Action

記事で読んだ内容を、講座で実装してみましょう

InfraAcademyでは、ブラウザ上でLinuxやネットワークの実践環境を使いながら学習できます。無料で始められる講座から、学習の流れを試せます。

この記事を書いた人

ryu

InfraAcademy運営 / エンジニア

エンジニア歴10年。Linux、ネットワーク、クラウドを中心に、実務で役立つインフラ技術を初心者にもわかりやすく解説しています。

X: @ryu63614894

Related

関連記事

ブログ一覧へ

Roadmap

まずはこの4講座から

ログインすれば無料で始められる講座です。気になったテーマから手を動かして学べます。

講座一覧を見る