ping が実際に送るものと MTU
このラボはVM上で動きます
2つのネームスペース(pa・pb)をvethでつなぎ、その上でICMPとUDPを見ます。2つ目のラボと同じ形ですが、今回の主役はフレームではなく、パケットサイズとエラーメッセージです。
目標
pingが送るICMP echoを捕まえ、MTUを超えたパケットがフラグメント化されたり拒否されたりする様子を見て、UDPが閉じたポートをどう知らせるか(ICMP port unreachable)を確認します。
なぜ重要なのか
「pingは通るのに、大きなファイルだけ止まる」「SSHは通るのに、Webページが半分しか表示されない」。ほとんどいつもMTUです。小さなパケットは通り、大きなパケットだけがどこかで捨てられます。DFビットのためフラグメント化もできず、そのことを知らせるICMPをファイアウォールがブロックしているのが、よくある事故の形です。VPN・オーバーレイネットワーク・トンネルがヘッダーを付け足すたびにこの問題が再発するので、1500と1472がどこから来る数字なのかを、手で数えてみます。
ステップ
- ネームスペース
pa・pbとvethpa0↔pb0を作成し、10.60.0.1/24・10.60.0.2/24を付けて、お互いにpingが通るようにしてください。 paからpbへping -c 3を実行した結果を/root/icmp/ping.txtに保存してください。pa0でICMPをキャプチャしながらpingを1回送り、echo requestとecho replyを含む出力を/root/icmp/echo.txtに保存してください。ping -M doでフラグメント化を禁止したままサイズを変えて、通過する最大ペイロードを探し、/root/icmp/mtu.txtにmtu=・max_payload=の2行と失敗メッセージを保存してください。- フラグメント化を許可したまま、ペイロード3000バイトのpingをキャプチャして、フラグメント化されたパケットが見える出力を
/root/icmp/frag.txtに保存してください。 pbでUDPポート9001を待ち受けるnc -luを起動し(受信したものは/root/icmp/udp.txtへ)、paからhello-udpを送ってください。paからpbの閉じたUDPポート9999へデータグラムを送りながらキャプチャして、udp port 9999 unreachableが見える出力を/root/icmp/unreach.txtに保存してください。pa0のMTUを1400に下げ、/root/icmp/report.mdに、mtu=1400・max_payload=の2行と、その数がなぜそうなるのか、そしてフラグメント化がなぜ問題になるのかを書いてください。
参考
ping -M do -s <바이트>: DFをオンにしてペイロードサイズを決めます(プレースホルダーはバイト数です)。MTU 1500 = IPヘッダー20 + ICMPヘッダー8 + ペイロード。- キャプチャは
ip netns exec pa timeout 5 tcpdump -i pa0 -nn -l icmp > 파일 2>&1 &のように起動しておいてから送ります(プレースホルダーはファイル名です)。 - UDPサーバー:
ip netns exec pb setsid timeout 20 nc -lu 10.60.0.2 9001 > /root/icmp/udp.txt 2>&1 < /dev/null &。クライアント:echo hello-udp | ip netns exec pa nc -u -q 1 -w 1 10.60.0.2 9001。 - MTUの変更:
ip -n pa link set pa0 mtu 1400。 - よくある間違い:
-s 1500を指定して、なぜ通らないのかと首をかしげることです。ヘッダーの28バイトを引く必要があります。
2台をつなぐ
ネームスペースpa・pbとvethpa0↔pb0を作成し、10.60.0.1/24・10.60.0.2/24を付けて、お互いにpingが通るようにしてください。
2つ目のラボと同じ順序です: netns add → link add veth → link set netns → addr add → link set up(loを含む)。ip netns exec pa ping -c 1 10.60.0.2で確認します。
pingを3回
paからpbへping -c 3を実行した結果を /root/icmp/ping.txt に保存してください。
ip netns exec pa ping -c 3 10.60.0.2 > /root/icmp/ping.txt。統計の行に3 receivedがある必要があります。time=が往復時間(RTT)です。
echoを捕まえる
pa0でICMPをキャプチャしながらpingを1回送り、echo requestとecho replyを含む出力を /root/icmp/echo.txt に保存してください。
ip netns exec pa timeout 5 tcpdump -i pa0 -nn -l icmp > /root/icmp/echo.txt 2>&1 &のあと1秒待って、ip netns exec pa ping -c 1 10.60.0.2を実行します。ファイルに、ICMP echo requestとICMP echo replyが1行ずつある必要があります。
フラグメント化の禁止
ping -M doでフラグメント化を禁止したままサイズを変えて、通過する最大ペイロードを探し、/root/icmp/mtu.txt にmtu=・max_payload=の2行と失敗メッセージを保存してください。
ip -n pa link show pa0のmtuは1500です。ping -M do -s 1472は通り、-s 1473はmessage too long, mtu=1500で失敗します。失敗したpingの出力(2>&1)を、2行の下に貼り付けてください。
フラグメント化させる
フラグメント化を許可したまま、ペイロード3000バイトのpingをキャプチャして、フラグメント化されたパケットが見える出力を /root/icmp/frag.txt に保存してください。
キャプチャを起動して、ip netns exec pa ping -c 1 -s 3000 10.60.0.2を実行します。3028バイトのパケットが1500バイトのフラグメントに分かれ、最初のフラグメントだけがICMP echo request、残りはip-proto-1と表示されます(ICMPヘッダーが最初のフラグメントにしかないためです)。
UDPにはハンドシェイクがない
pbでUDPポート9001を待ち受けるnc -luを起動し(受信したものは /root/icmp/udp.txt へ)、paからhello-udpを送ってください。
サーバー: ip netns exec pb setsid timeout 20 nc -lu 10.60.0.2 9001 > /root/icmp/udp.txt 2>&1 < /dev/null &。クライアント: echo hello-udp | ip netns exec pa nc -u -q 1 -w 1 10.60.0.2 9001。ファイルにhello-udpが入っていれば完了です。
閉じたUDPポートはICMPで答える
paからpbの閉じたUDPポート9999へデータグラムを送りながらキャプチャして、udp port 9999 unreachableが見える出力を /root/icmp/unreach.txt に保存してください。
キャプチャフィルターを'icmp or udp'にして、echo x | ip netns exec pa nc -u -q 1 -w 1 10.60.0.2 9999を実行します。UDPデータグラムの行の後に、ICMP 10.60.0.2 udp port 9999 unreachableが続きます。
MTUを下げてまとめる
pa0のMTUを1400に下げ、/root/icmp/report.md に、mtu=1400・max_payload=の2行と、その数がなぜそうなるのか、そしてフラグメント化がなぜ問題になるのかを書いてください。
ip -n pa link set pa0 mtu 1400のあと、ping -M do -s 1372は通り、-s 1373は通りません。max_payloadは1400 - 28です。説明には、MTUとフラグメント(またはfragment)という語を含める必要があります。