マエカワの備忘録的な何か

思い立ったが吉日

ユビキタスネットワーク 其の七 20171120

はじめに

 前回説明したIPフォワーディングはルーティングテーブルを参照して宛先情報をもらうというとても簡単なものだった。しかし、この簡単さゆえに少し不親切なぶぶんがあるという。これを解消しようとして生まれたのがICMP。今回扱うものだ。

ICMP

 ICMPは

  • エラー通知
  • 制御

で構成されている。IPの上位概念として存在しているので、IPヘッダの後ろにICMPメッセージという形でくっつく。

ICMPメッセージ

 4バイトで構成されるもの。各ビットの役割分担をまずは紹介。

こんな感じ。これ以降のデータグラムにはタイプやコードごとに異なるデータが格納される。次に、エラー通知や制御についてひとつずつ説明していく。

宛先到達不可

  • タイプ:3
  • コード:1~15
  • 32bit以降は使わない

 一過性でない(どんなに試してみてもダメな)エラーによって発生する。IPデータグラムの紛失を

  • IPヘッダ(クライアントが送信したもの)
  • 8バイト

という形で返してくれる。

ネットワーク・ホスト到達不可

 宛先までデータグラムが送信されない時に発生する。原因としては

  • ルーティングテーブルの不備
  • 次のノードが不在(ARP応答がない)

などが考えられる。
 この機能がないと、クライアントは存在しない相手に永遠にデータを送り続けてしまう。

プロトコル・ポート到達不可

 宛先ノードまでは行ける。が、そこで設定したプロトコルが動かない(プロトコル到達不可)、またはサーバーが動かない(ポート到達不可)場合に発生する。

フラグメント失敗/Path MTU Discovery

問題背景

 ルータ同士がWLANでつながっている場合を考える。本来、UDPでは1500byteまでデータを送信することができる。しかしWLANではLLC(8byte)をくっつける必要があるので、実質1492byteまでしかデータを送れない。
 このネットワークにIPヘッダ20byte、データ1480byteを流そうとする場合、ルータ部分でフラグメントする必要がある。
 しかし、これではルータ部分での処理が重くなってしまうので、ルータでのフラグメントを禁止するのが普通だ(Dont Flagment に1を立てる)*1。これにより、フラグメントしなければならない1492byte以上のパケットが流れてきた場合、ルータ部分でそのパケットを捨てるようになる。
 何が問題なのかというと、クライアント側はDFフラグが立っていることを知らないことだ。DFフラグが立っていること・パケットが捨てられたことを伝える処理をしないとクライアントは永遠にパケットを送り続けてしまう

解決方法

 この問題を解決するために、ルータでパケットが破棄されたと同時にICMPメッセージがクライアントに送られるようになっている。この時のICMPメッセージには

  • 48~63bit:ルータを通れるパケットの大きさ(MTU)

が入っている。これを受けたクライアントは、次の通信からMTUを超えないサイズでIPデータグラムを送るようになる。この手法をPath MTU Discoveryと呼ぶ。

エコー要求・応答/ping

 各パラメータの値は

  • タイプ:8(要求)/0(応答)
  • コード:0
  • 3、4byte目には識別子とシーケンス番号

となっている。この機能は制御に関するもので、

  • ノードとの通信可能性
  • 往復遅延時間

を確認することができる。

確認方法
  1. エコー要求を特定ノードに送信
  2. 同じデータを持つノードがエコー応答を送信元に送信

この2ステップで自分と特定ノードの間にあるルータを確認できる。
 レコードルートオプションを設定することで、最大9個まで特定ノード間のルータを確認することができる。コマンドプロンプト上で"ping -a -r 9 (特定アドレス名)"と入力したら、特定アドレスまでのルータのIPアドレスが出力される。学内で実行してみた結果を載せておく。

>ping -a -r 9 www.edu.cc.uec.ac.jp

sol.edu.cc.uec.ac.jp [130.153.16.7]に ping を送信しています 32 バイトのデータ:
130.153.16.7 からの応答: バイト数 =32 時間 =3ms TTL=63
    ルート: ipc-newres-gw.cc.uec.ac.jp [130.153.16.1] ->
           sol.edu.cc.uec.ac.jp [130.153.16.7] ->
           sol.edu.cc.uec.ac.jp [130.153.16.7] ->
           172.21.0.1
130.153.16.7 からの応答: バイト数 =32 時間 =3ms TTL=63
    ルート: ipc-newres-gw.cc.uec.ac.jp [130.153.16.1] ->
           sol.edu.cc.uec.ac.jp [130.153.16.7] ->
           sol.edu.cc.uec.ac.jp [130.153.16.7] ->
           172.21.0.1
130.153.16.7 からの応答: バイト数 =32 時間 =4ms TTL=63
    ルート: ipc-newres-gw.cc.uec.ac.jp [130.153.16.1] ->
           sol.edu.cc.uec.ac.jp [130.153.16.7] ->
           sol.edu.cc.uec.ac.jp [130.153.16.7] ->
           172.21.0.1
130.153.16.7 からの応答: バイト数 =32 時間 =4ms TTL=63
    ルート: ipc-newres-gw.cc.uec.ac.jp [130.153.16.1] ->
           sol.edu.cc.uec.ac.jp [130.153.16.7] ->
           sol.edu.cc.uec.ac.jp [130.153.16.7] ->
           172.21.0.1

130.153.16.7 の ping 統計:
    パケット数: 送信 = 4、受信 = 4、損失 = 0 (0% の損失)、
ラウンド トリップの概算時間 (ミリ秒):
    最小 = 3ms、最大 = 4ms、平均 = 3ms

これを見ると、学内サーバまでは一つのルータを経由していることがわかると思う。出力されるIPアドレスは各ルータの出口側のインターフェースのもの。同じIPアドレスが並んでいるということは、そこで折り返しているということだ。

時間経過

 各パラメータは以下の通り。

  • タイプ:11
  • コード:0(途中でTTLが0になった場合)/1(宛先で時間経過)

 IPデータグラムのTTLが0になると、パケットを捨ててICMPメッセージをクライアントに送信する。

Traceroute

 TTLを1から1ずつ増やしながら、UDPを送信ポート番号にはめちゃくちゃな値を入れておく。これを行うことで、

  • 目的地につかないうちは時間超過のICMPが返ってくる
  • 目的地についたらポート到達不可のICMPメッセージが返ってくる

つまり、最終的に目的アドレスまでのルータ経由数が確認できるというわけだ。しかし、これでは行きしか確認できない。もしかしたら行きと帰りで違うルータを経由しているかもしれない。
 こんな時のために、ソースルーティングオプションが存在する。このオプションは2種類。

  • ルーズ:最低限通るノードを指定
  • ストリクト:途中のノードをすべて指定

ルーズのほうを使い、「自分→調べたいアドレス→自分」という設定をすると、自分と特定アドレスとの往復に必要なノード数を調べることができる。

 ちょっと遊んでみましょう。弊学内から千葉大学までの道順を"tracert"というコマンドを使って調べてみます。

>tracert www.chiba-u.ac.jp

www.chiba-u.jp [133.82.251.21] へのルートをトレースしています
経由するホップ数は最大 30 です:

  1     *        *        *     要求がタイムアウトしました。
  2     *        *        *     要求がタイムアウトしました。
  3     *        *        *     要求がタイムアウトしました。
  4     *        *        *     要求がタイムアウトしました。
  5     *        *        *     要求がタイムアウトしました。
  6     *        *        *     要求がタイムアウトしました。
  7     *        *        *     要求がタイムアウトしました。
  8     *        *        *     要求がタイムアウトしました。
  9     *        *        *     要求がタイムアウトしました。
 10     *        *        *     要求がタイムアウトしました。
 11     *        *        *     要求がタイムアウトしました。
 12     *        *        *     要求がタイムアウトしました。
 13     *        *        *     要求がタイムアウトしました。
 14     *        *        *     要求がタイムアウトしました。
 15     *        *        *     要求がタイムアウトしました。
 16     *        *        *     要求がタイムアウトしました。
 17     *        *        *     要求がタイムアウトしました。
 18     *        *        *     要求がタイムアウトしました。
 19     *        *        *     要求がタイムアウトしました。
 20     *        *        *     要求がタイムアウトしました。
 21     *        *        *     要求がタイムアウトしました。
 22     *        *        *     要求がタイムアウトしました。
 23     *        *        *     要求がタイムアウトしました。
 24     *        *        *     要求がタイムアウトしました。
 25     *        *        *     要求がタイムアウトしました。
 26     *        *        *     要求がタイムアウトしました。
 27     *        *        *     要求がタイムアウトしました。
 28     *        *        *     要求がタイムアウトしました。
 29     *        *        *     要求がタイムアウトしました。
 30     *        *        *     要求がタイムアウトしました。

トレースを完了しました。

嘘だろ…。30個以上のルータを経由しなければならないのか…。この「要求がタイムアウトしました。」ってのが、途中のルータで時間超過になったことを表しています。不安になったので、学内サーバにもつないでみます。

>tracert www.edu.cc.uec.ac.jp

sol.edu.cc.uec.ac.jp [130.153.16.7] へのルートをトレースしています
経由するホップ数は最大 30 です:

  1     *        *        *     要求がタイムアウトしました。
  2     2 ms     1 ms     1 ms  sol.edu.cc.uec.ac.jp [130.153.16.7]

トレースを完了しました。

良かった…。さっき出てきた結果と同じように、ルータを一つ経由して学内サーバにつながっていることが分かります。

 ということで、今回はこれで終わり。こんな感じで自分で試せると何やってるのかわかりやすくていいですね。


*1:IPv4ではルータでのフラグメントが可能だったが、IPv6ではできないようになっているそうです。