読者です 読者をやめる 読者になる 読者になる

tracerouteで * (アスタリスク)になる理由

背景

「ネットワークがつながらない!」といったトラブルシューティングをしていると、 tracerouteの結果が、以下のように経路の途中で*になる事象によく遭遇します。
今回はIPヘッダやパケットを見比べながら、なぜ*になるのかを見ていきます。

$ traceroute 8.8.8.8
traceroute to 8.8.8.8 (8.8.8.8), 64 hops max, 52 byte packets
 1  192.168.43.1 (192.168.43.1)  823.037 ms *  3.936 ms
 2  * * *
 3  osk004bb01.iij.net (202.32.116.5)  476.654 ms  132.676 ms  120.725 ms
 4  osk004ix50.iij.net (58.138.107.218)  119.609 ms
    osk004ix50.iij.net (58.138.107.166)  133.204 ms
    osk004ix51.iij.net (58.138.107.222)  117.566 ms
 5  72.14.210.182 (72.14.210.182)  126.614 ms
    210.130.133.86 (210.130.133.86)  115.229 ms  118.843 ms
 6  108.170.243.36 (108.170.243.36)  119.670 ms
    108.170.243.68 (108.170.243.68)  134.219 ms
    108.170.243.132 (108.170.243.132)  135.462 ms
 7  209.85.255.163 (209.85.255.163)  141.621 ms
    216.239.41.199 (216.239.41.199)  132.251 ms  138.545 ms
 8  66.249.95.77 (66.249.95.77)  165.501 ms
    108.170.235.231 (108.170.235.231)  155.299 ms
    66.249.95.77 (66.249.95.77)  172.740 ms
 9  216.239.43.101 (216.239.43.101)  153.654 ms
    72.14.235.77 (72.14.235.77)  175.888 ms
    64.233.175.209 (64.233.175.209)  156.037 ms
10  * * *
11  * * *
12  * * *
13  * * *
14  * * *
15  * * *
16  * * *
17  * * *
18  google-public-dns-a.google.com (8.8.8.8)  159.839 ms  156.416 ms  166.939 ms

※プロバイダによっては*にならない場合もあるようです。 今回はDMMのSIMカードからテザリングしています。
ちなみに8.8.8.8GoogleのパブリックDNSIPアドレスです。覚えやすいですね。

前提知識

IPヘッダのProtocolナンバー

RFC791でIPヘッダのフォーマットは以下のように定められています。

0                   1                   2                   3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Version|  IHL  |Type of Service|          Total Length         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|         Identification        |Flags|      Fragment Offset    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|  Time to Live |    Protocol   |         Header Checksum       |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                       Source Address                          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    Destination Address                        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    Options                    |    Padding    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

上記のProtocolフィールドが1となるものがICMPとなります。
※ProtocolナンバーはRFC790 で規定されています。 TCPUDP(User Datagram)もそれぞれ617がアサインされています。

ping実行時のパケットをWiresharkで見てみると以下のようにICMP(1)となっていることがわかります。

f:id:cipepser:20170305172120p:plain

tracerouteの仕組み

tracerouteは、冒頭のコマンド結果のように、宛先までのホップ情報を教えてくれる便利なコマンドです。
tracerouteは上記のIPヘッダにあるTime to Live(TTL)を利用して実装されています。
TTLはパケットの寿命みたいなものです。誤ったルーティングが設定されたネットワークでは、度々ループが発生します。この時、ネットワークに入ってきたパケットがいつまでもループし続けると、ネットワークの帯域を逼迫させ、通信ができなくなります。それを避けるための方法として、ルータはパケットをフォワードする際にTTLを1ずつデクリメントします。TTLが0になったパケットを受信したルータは、そのパケットをフォワードせず、破棄します。これによってネットワークが輻輳まみれになることを防ぎます。
そしてTTLが0になったときtracerouteの発行元に対して、TTLが0になったことを通知(Time Exceeded Message)します。

※これらはRFC792で以下のように規定されています。

If the gateway processing a datagram finds the time to live field is zero it must discard the datagram. The gateway may also notify the source host via the time exceeded message.

この通知には破棄したルータのIPアドレスが含まれています。 tracerouteではTTLを1つずつインクリメントし、応答があったTime Exceeded Messageに含まれるIPアドレスを表示することで実装されています。

本題

前置きが長くなりましたが、いよいよ本題に入っていきます。
tracerouteは、WindowsではICMPを用いていますが、LinuxではUDPでポート番号33434 〜33534が使われます。tracerouteをFWで通す要件がある場合は、実装する際に注意が必要です。
今回はICMPのTTLをマニュアルで設定することで、tracerouteの仕組みを擬似的に再現し、*の正体を探ります。 そのためtraceroute-Iオプションを付けてICMPでのtraceroute結果を得ておきます。

$ traceroute -I 8.8.8.8
traceroute to 8.8.8.8 (8.8.8.8), 64 hops max, 72 byte packets
 1  * * *
 2  * * *
 3  osk009nasgw111.iij.net (202.32.116.105)  132.515 ms  122.689 ms  119.731 ms
 4  osk004bb01.iij.net (202.32.116.5)  117.981 ms  114.951 ms  121.114 ms
 5  osk004ix51.iij.net (58.138.107.222)  123.171 ms  118.085 ms  122.172 ms
 6  72.14.210.182 (72.14.210.182)  117.047 ms  122.246 ms  123.139 ms
 7  108.170.243.66 (108.170.243.66)  117.916 ms  498.344 ms  149.948 ms
 8  209.85.255.163 (209.85.255.163)  116.059 ms  132.291 ms  121.649 ms
 9  72.14.233.211 (72.14.233.211)  159.789 ms  163.511 ms  173.399 ms
10  72.14.235.77 (72.14.235.77)  154.462 ms  153.933 ms  170.539 ms
11  * * *
12  * * *
13  * * *
14  * * *
15  * * *
16  * * *
17  * * *
18  * * *
19  google-public-dns-a.google.com (8.8.8.8)  168.872 ms  150.776 ms  161.775 ms

この時の通信の様子をWiresharkで見てみると以下のようになります。 TTLを1から順に増やしつつ、それぞれ3回送っていることがわかります。

f:id:cipepser:20170305211345p:plain f:id:cipepser:20170305211351p:plain f:id:cipepser:20170305211358p:plain

勘の良い方はすでにお気づきかと思いますが、 tracerouteの結果が*ではないものはTime Exceeded Messageを返しています。 逆に言えば、*になっている箇所は応答がありません。

さらに詳しく見るためにTTLを設定したpingを打ってみましょう。 -mオプションでpingTTLを設定できます。 長いですが、以下結果です。
192.168.43.11は自ホストのアドレスです。

$ ping -m 1 -c 1 8.8.8.8
PING 8.8.8.8 (8.8.8.8): 56 data bytes

--- 8.8.8.8 ping statistics ---
1 packets transmitted, 0 packets received, 100.0% packet loss
$ ping -m 2 -c 1 8.8.8.8
PING 8.8.8.8 (8.8.8.8): 56 data bytes

--- 8.8.8.8 ping statistics ---
1 packets transmitted, 0 packets received, 100.0% packet loss
$ ping -m 3 -c 1 8.8.8.8
PING 8.8.8.8 (8.8.8.8): 56 data bytes
36 bytes from osk009nasgw111.iij.net (202.32.116.105): Time to live exceeded
Vr HL TOS  Len   ID Flg  off TTL Pro  cks      Src      Dst
 4  5  00 5400 248e   0 0000  01  01 9958 192.168.43.11  8.8.8.8

--- 8.8.8.8 ping statistics ---
1 packets transmitted, 0 packets received, 100.0% packet loss
$ ping -m 4 -c 1 8.8.8.8
PING 8.8.8.8 (8.8.8.8): 56 data bytes
76 bytes from osk004bb01.iij.net (202.32.116.5): Time to live exceeded
Vr HL TOS  Len   ID Flg  off TTL Pro  cks      Src      Dst
 4  5  00 5400 80d2   0 0000  01  01 3d14 192.168.43.11  8.8.8.8

--- 8.8.8.8 ping statistics ---
1 packets transmitted, 0 packets received, 100.0% packet loss
$ ping -m 5 -c 1 8.8.8.8
PING 8.8.8.8 (8.8.8.8): 56 data bytes
36 bytes from osk004ix51.iij.net (58.138.107.222): Time to live exceeded
Vr HL TOS  Len   ID Flg  off TTL Pro  cks      Src      Dst
 4  5  00 5400 1fb3   0 0000  01  01 9e33 192.168.43.11  8.8.8.8

--- 8.8.8.8 ping statistics ---
1 packets transmitted, 0 packets received, 100.0% packet loss
$ ping -m 6 -c 1 8.8.8.8
PING 8.8.8.8 (8.8.8.8): 56 data bytes
36 bytes from 72.14.210.182: Time to live exceeded
Vr HL TOS  Len   ID Flg  off TTL Pro  cks      Src      Dst
 4  5  00 5400 3fb3   0 0000  01  01 7e33 192.168.43.11  8.8.8.8

--- 8.8.8.8 ping statistics ---
1 packets transmitted, 0 packets received, 100.0% packet loss
$ ping -m 7 -c 1 8.8.8.8
PING 8.8.8.8 (8.8.8.8): 56 data bytes
36 bytes from 108.170.243.66: Time to live exceeded
Vr HL TOS  Len   ID Flg  off TTL Pro  cks      Src      Dst
 4  5  80 5400 acc6   0 0000  01  01 10a0 192.168.43.11  8.8.8.8

--- 8.8.8.8 ping statistics ---
1 packets transmitted, 0 packets received, 100.0% packet loss
$ ping -m 8 -c 1 8.8.8.8
PING 8.8.8.8 (8.8.8.8): 56 data bytes
148 bytes from 209.85.255.163: Time to live exceeded
Vr HL TOS  Len   ID Flg  off TTL Pro  cks      Src      Dst
 4  5  80 5400 5d6e   0 0000  01  01 5ff8 192.168.43.11  8.8.8.8

--- 8.8.8.8 ping statistics ---
1 packets transmitted, 0 packets received, 100.0% packet loss
$ ping -m 9 -c 1 8.8.8.8
PING 8.8.8.8 (8.8.8.8): 56 data bytes
148 bytes from 72.14.233.211: Time to live exceeded
Vr HL TOS  Len   ID Flg  off TTL Pro  cks      Src      Dst
 4  5  80 5400 13e9   0 0000  02  01 a87d 192.168.43.11  8.8.8.8

--- 8.8.8.8 ping statistics ---
1 packets transmitted, 0 packets received, 100.0% packet loss
$ ping -m 10 -c 1 8.8.8.8
PING 8.8.8.8 (8.8.8.8): 56 data bytes
36 bytes from 72.14.235.77: Time to live exceeded
Vr HL TOS  Len   ID Flg  off TTL Pro  cks      Src      Dst
 4  5  80 5400 6b01   0 0000  01  01 5265 192.168.43.11  8.8.8.8

--- 8.8.8.8 ping statistics ---
1 packets transmitted, 0 packets received, 100.0% packet loss
$ ping -m 11 -c 1 8.8.8.8
PING 8.8.8.8 (8.8.8.8): 56 data bytes

--- 8.8.8.8 ping statistics ---
1 packets transmitted, 0 packets received, 100.0% packet loss
$ ping -m 12 -c 1 8.8.8.8
PING 8.8.8.8 (8.8.8.8): 56 data bytes

--- 8.8.8.8 ping statistics ---
1 packets transmitted, 0 packets received, 100.0% packet loss
$ ping -m 13 -c 1 8.8.8.8
PING 8.8.8.8 (8.8.8.8): 56 data bytes

--- 8.8.8.8 ping statistics ---
1 packets transmitted, 0 packets received, 100.0% packet loss
$ ping -m 14 -c 1 8.8.8.8
PING 8.8.8.8 (8.8.8.8): 56 data bytes

--- 8.8.8.8 ping statistics ---
1 packets transmitted, 0 packets received, 100.0% packet loss
$ ping -m 15 -c 1 8.8.8.8
PING 8.8.8.8 (8.8.8.8): 56 data bytes

--- 8.8.8.8 ping statistics ---
1 packets transmitted, 0 packets received, 100.0% packet loss
$ ping -m 16 -c 1 8.8.8.8
PING 8.8.8.8 (8.8.8.8): 56 data bytes

--- 8.8.8.8 ping statistics ---
1 packets transmitted, 0 packets received, 100.0% packet loss
$ ping -m 17 -c 1 8.8.8.8
PING 8.8.8.8 (8.8.8.8): 56 data bytes

--- 8.8.8.8 ping statistics ---
1 packets transmitted, 0 packets received, 100.0% packet loss
$ ping -m 18 -c 1 8.8.8.8
PING 8.8.8.8 (8.8.8.8): 56 data bytes

--- 8.8.8.8 ping statistics ---
1 packets transmitted, 0 packets received, 100.0% packet loss
$ ping -m 19 -c 1 8.8.8.8
PING 8.8.8.8 (8.8.8.8): 56 data bytes
64 bytes from 8.8.8.8: icmp_seq=0 ttl=46 time=166.696 ms

--- 8.8.8.8 ping statistics ---
1 packets transmitted, 1 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 166.696/166.696/166.696/0.000 ms

確かにtracerouteの結果が*ではないものはTime Exceeded Messageを返していることがわかります。 しかもその応答をTTL=0となったホップが返しているので、送信元アドレス(=tracerouteで表示するアドレス)もわかりました。
逆に*になっている箇所はpacket lossで応答なしのため、表示することができず、代替手段として*を表示するしかないようですね。
またTTL=19の最終の宛先では、通常のping応答と同様のメッセージが返ってきているので、これを自分でtracerouteを実装するとしたら、「応答があったこと」を条件にループ止めるなどで実装できそうですね。

結論

tracerouteの結果、経路の途中で*になるのは、そのホップが応答を返さないとき(フォワードのみする)。

Reference