ネットワーク接続作業を行うと毎回のように何らかの問題が発生します。
pingは通るのに対向先のネットワーク機器にTCPコネクションが張れない、通信が突然切断された、そんな経験をしたことはありませんか?
そういった事態に遭遇したとき、本記事がトラブルの元を突き止める手段のひとつとして参考になれば幸いです。
パケットキャプチャで原因を探る
問題発生の状況として、サーバーへpingは通るけれどssh接続ができない状態を想定します。
このような場合、次のような原因が考えられます。
- サーバーのポート22番が閉じている
- サーバーのFW機能で弾いている
- 経路上のFWで弾いている
原因を探るためパケットキャプチャしたところ、サーバーのIPアドレスからリセットパケットが送信されている事がわかりました。pingは問題なく通ります。
そこで問題です。
リセットパケットを送ったのは誰でしょうか?次のパケットキャプチャの結果から考えてみてください。
サーバ(192.168.40.129)からの応答パケットに注目してみてください。
IP (tos 0x0, ttl 128, id 2356, offset 0, flags [DF], proto: TCP (6), length: 52, bad cksum 0 (->283e)!) 192.168.31.128.49169 > 192.168.40.129.22: S, cksum 0xc978 (incorrect (-> 0x6b25), 2098519388:2098519388(0) win 8192 <mss 1460,nop,wscale 8,nop,nop,sackOK> IP (tos 0x0, ttl 63, id 0, offset 0, flags [DF], proto: TCP (6), length: 40) 192.168.40.129.22 > 192.168.31.128.49169: R, cksum 0xcbe4 (correct), 0:0(0) ack 2098519389 win 0 IP (tos 0x0, ttl 128, id 2357, offset 0, flags [DF], proto: TCP (6), length: 52, bad cksum 0 (->283d)!) 192.168.31.128.49169 > 192.168.40.129.22: S, cksum 0xc978 (incorrect (-> 0x6b25), 2098519388:2098519388(0) win 8192 <mss 1460,nop,wscale 8,nop,nop,sackOK> IP (tos 0x0, ttl 63, id 0, offset 0, flags [DF], proto: TCP (6), length: 40) 192.168.40.129.22 > 192.168.31.128.49169: R, cksum 0xcbe4 (correct), 0:0(0) ack 1 win 0 IP (tos 0x0, ttl 128, id 2358, offset 0, flags [DF], proto: TCP (6), length: 48, bad cksum 0 (->2840)!) 192.168.31.128.49169 > 192.168.40.129.22: S, cksum 0xc974 (incorrect (-> 0x7f34), 2098519388:2098519388(0) win 8192 <mss 1460,nop,nop,sackOK> IP (tos 0x0, ttl 63, id 0, offset 0, flags [DF], proto: TCP (6), length: 40) 192.168.40.129.22 > 192.168.31.128.49169: R, cksum 0xcbe4 (correct), 0:0(0) ack 1 win 0 IP (tos 0x0, ttl 128, id 2359, offset 0, flags [none], proto: ICMP (1), length: 60, bad cksum 0 (->6838)!) 192.168.31.128 > 192.168.40.129: ICMP echo request, id 1, seq 1238, length 40 IP (tos 0x0, ttl 61, id 48987, offset 0, flags [none], proto: ICMP (1), length: 60) 192.168.40.129 > 192.168.31.128: ICMP echo reply, id 1, seq 1238, length 40 IP (tos 0x0, ttl 128, id 2360, offset 0, flags [none], proto: ICMP (1), length: 60, bad cksum 0 (->6837)!) 192.168.31.128 > 192.168.40.129: ICMP echo request, id 1, seq 1239, length 40 IP (tos 0x0, ttl 61, id 48988, offset 0, flags [none], proto: ICMP (1), length: 60) 192.168.40.129 > 192.168.31.128: ICMP echo reply, id 1, seq 1239, length 40 IP (tos 0x0, ttl 128, id 2361, offset 0, flags [none], proto: ICMP (1), length: 60, bad cksum 0 (->6836)!) 192.168.31.128 > 192.168.40.129: ICMP echo request, id 1, seq 1240, length 40 IP (tos 0x0, ttl 61, id 48989, offset 0, flags [none], proto: ICMP (1), length: 60) 192.168.40.129 > 192.168.31.128: ICMP echo reply, id 1, seq 1240, length 40 IP (tos 0x0, ttl 128, id 2362, offset 0, flags [none], proto: ICMP (1), length: 60, bad cksum 0 (->6835)!) 192.168.31.128 > 192.168.40.129: ICMP echo request, id 1, seq 1241, length 40 IP (tos 0x0, ttl 61, id 48990, offset 0, flags [none], proto: ICMP (1), length: 60) 192.168.40.129 > 192.168.31.128: ICMP echo reply, id 1, seq 1241, length 40
答えは次のどちらでしょう??
- サーバーがリセットパケットを送信した
- 経路上にあるネットワーク機器がリセットパケットを送信した
答えは次の章で。IPヘッダーの値に注目してください。
IPヘッダーはウソをつかない
だれがリセットパケットを送信したのかすぐにわかりましたか?
確信を持って即答できなければ、このまま読み進めてください。
正解は2番。「経路上にあるネットワーク機器がリセットパケットを送信した」です。
このような状況で注目するべきは、IPヘッダーの「TTL」値です。

TTLはIPヘッダーに含まれる8ビットの値で、ネットワーク機器を経由するごとに、ひとつずつTTL値が減ります。Tracerouteはこの仕組みを利用して通信経路を調べています。
では実際にTTL値に注目してパケットキャプチャの結果を追いかけてみましょう。
まずキャプチャ結果2行目のTTL値に注目してください。TTL値は63です。このパケットはssh接続が切断されるとき、サーバーからリセットパケットが送信されてきたときのものです。
IP (tos 0x0, ttl 63, id 0, offset 0, flags [DF], proto: TCP (6), length: 40) 192.168.40.129.22 > 192.168.31.128.49169: R, cksum 0xcbe4 (correct), 0:0(0) ack 2098519389 win 0
次に8行目に注目してください。このときのTTL値は61です。これはサーバーからICMP応答を受信したときのものです。
IP (tos 0x0, ttl 61, id 48987, offset 0, flags [none], proto: ICMP (1), length: 60) 192.168.40.129 > 192.168.31.128: ICMP echo reply, id 1, seq 1238, length 40
おかしくありませんか?送信元IPアドレスは同じなのにどうしてTTL値が違うのでしょう?
その答えをつぎのフロー図で説明します。

このフロー図は、pingを実行したときとssh接続がリセットパケットで切断されたときの通信の流れを示しています。
pingを実行したとき、パケットはFWを通過してサーバーがping応答を送信しました。このとき、サーバーが設定した初期TTL値は64です。その後、ルーターとFWを経由してping応答が到着するとTTL値は61まで減算されました。パケットキャプチャの結果と一致しています。
これに対してsshが切断されたときはどうでしょうか?まずFWのポリシーに引っかかってFWで止められます。そしてFWはリセットパケットを送信します。偶然にもサーバーと同じくFWの初期TTL値も64でしたので、ルーターを経由してリセットパケットが送信元へ到達したときTTL値は63に減算されました。
興味深いことに、TTLの初期値はOSのTCP/IP実装に依存します。そして、カーネルパラメータをいじらない限りTTLの初期値は「64」「128」「255」のどれかである事が多いです。そのため、返って来たTTL値をみれば送信元の初期TTL値は簡単に推測できます。初期TTL値はわかっているので、逆算すればホップ数もおのずと推測できます。今回の例でいえばリセットパケットを投げたネットワーク機器(FW)の間にひとつネットワーク機器がある事がわかるはずです。
何もしていないのに切断されるとき
今回はキャプチャ結果を掲載していないのですが、アプリテストをしていると不意に通信切断が発生する事があります。このときの原因で多いのはFWやLB(ロードバランサー)がタイムアウトしてリセットパケットを飛ばした場合です。
リセットパケットで通信が切断された場合はパケットキャプチャをしていれば一発で送信元を特定できる可能性が極めて高いです。経路上でリセットパケットを飛ばしそうなネットワーク機器のパラメータ再確認をするという地道な方法も良いのですが(昔やらされました)、迅速に解決したいならばパケットキャプチャをした方が良いでしょう。
FWやLBの設定が甘いと必ず発生するので試してみてください。
最後に
顧客に「おたくのネットワーク設定ミスじゃないの?」なんて言われたら「2ホップ先にFWがありませんか?そこで弾かれている可能性があります。FWがあるならばポリシーチェックを依頼して頂けますか?」なんて即答できたら良いですね。
IPヘッダーやTCP/UDP/ICMPヘッダーの構造を理解しているとネットワークのトラブルを解決する際に必ず役に立ちます。
少なくても「マスタリングTCP/IP 入門編」は目を通しておく事をおすすめします。入門編とはいっても内容は簡単ではないのですが、きっと役に立つはずです。