tracerouteのベストプラクティス
ファイヤウォールの設定が厳しいネットワークや、ロードバランサーが動作するネットワーク、使用するパケットのプロトコルによる違い、等を考えて最も経路発見しやすい方法を考える。
ファイヤウォールの設定が厳しいネットワーク
ファイヤウォールの設定が厳しい場合、ping
すらインターネット上のサーバに通らないことがある。traceroute
も同様で、デフォルトの設定では通らないことが多いが、ファイヤウォールが必ずといっていいほど通しているポートを使えば良い。TCPではHTTPの80番ポート、UDPではDNSの53番ポート、が最も良く知られたポートの内の一つだろう。実際、traceroute
のデフォルト設定ではデフォルトゲートウェイまでしか経路発見できないが、UDPの53番ポートを使うとかなり先まで経路発見できることがある。
ロードバランサーが動作するネットワーク
ECMPによってロードバランスしているネットワーク(インターネット)では、パケットを投げる度に経路が変わってしまう。ただし、ロードバランスには大きく二つの方式があって、インターフェースをフローで振り分けるか、パケットで振り分けるか、の違いがある。フローは5 tuples (IP/Port Dest/Src Protocol) によってインターフェースを振り分ける方式で、IP/UDP/TCPのヘッダで決まる5 tuplesを固定すれば、ロードバランスによる経路の違いは出ないので安定した経路発見が行えるはずだ。
ただし5 tuplesを固定するために、tracerouteがどのように経路発見を行っているのかを確認しておく必要がある。Linux版のtracerouteコマンドはデフォルトはUDPパケットによる経路発見を行う。つまり、UDPパケットのIPヘッダのTTLを一つずつ増やしていくやり方だ。また、受け取ったTime Exceeded
のICMPパケットが、どのUDPパケットに対応するのかを判断できる必要がある。大きく遅延したパケットを受信したり、他のアプリケーションが送信したパケットに対するTime Exceeded
パケットと区別する必要があるし、Linuxのtracerouteのデフォルト動作は以下の通り同時に複数のパケットを投げる。
-N squeries, --sim-queries=squeries Specifies the number of probe packets sent out simultaneously. Sending several probes concurrently can speed up traceroute considerably. The default value is 16. Note that some routers and hosts can use ICMP rate throttling. In such a situation specifying too large number can lead to loss of some responses.
Time Exceeded
のICMPパケットはそのデータ部に、元のパケットのIPヘッダとペイロードの最初の8バイトを含めるので、元のUDPパケットのヘッダに存在するポート番号を識別できるため、tracerouteはこれを生かしてUDPの宛先ポート番号を一つずつ増やしながらUDPパケットを生成する。
Internet Control Message Protocol - Wikipedia
ここで、フローベースのロードバランスに対応しようとすると問題があることが分かる。5 tuplesが変わってしまうとロードバランスの影響を受けるからだ。これに対応したのが Paris Traceroute で、UDPパケットヘッダの宛先ポート番号ではなくチェックサムを使って対応を取る。まるでビットコインのように、UDPパケットのペイロードを用いて任意のチェックサムの値を作り出す。UDPパケットヘッダのチェックサムは最初の8バイトに含まれるので、これもTime Exceeded
のICMPパケットに含まれる。単純にUDPパケットのペイロードにシーケンス番号を書けないのもこれが理由だ。
ただ、tracerouteにもこれと同等と思われるオプションが実装されているため、UDPパケットを使いつつ宛先や送信元のポート番号を任意の値に固定できる。例えば、-U
オプションで宛先ポート番号を53に固定する。これは、DNSで使われるポート番号であり、最も通りやすい(経路途中でファイヤウォールにフィルタで破棄される可能性が少ない)ポートの一つだ。また--sport=
オプションで、送信元ポートを固定することもできる。
両者の違いはChecksumの使い方にあるようだ。tracerouteのパケットをtcpdump -vv
で見てみると[bad udp cksum]
と表示されるのが分かる。宛先ポートや送信元ポートを変更してもChecksumの値を変えておらず固定値にしているようだ。一方、paris-tracerouteの方は[udp sum ok]
と表示されるため、ヘッダに併せてChecksumを正確に作り直していることが分かる。これが、tracerouteの動作の結果としてどう影響するかは分からないが、全く同じ挙動ではないという点は気をつけておいた方がいいだろう。また、paris-tracerouteは完全に全ての経路を把握してから標準出力に出力するので、複数経路を何らかのアルゴリズムで適当な経路を選択しているのかもしれない*1。
なお、Windows版のtracertはICMPパケットを使った経路発見を行う。LinuxのtracerouteでもICMPやTCPを扱えるが、これも細かい仕様は両者で異なると思われるので、同じICMPパケットでも発見できる経路に違いが出るかもしれない。
また、-A
で各ホップの所属するAS番号を取得することができるので活用した方が良い。これは内部でwhois
を使っており*2、whois
はポート45番*3を用いてwhoisサーバとやり取りするので、イントラネットがポート45番をフィルタしていると使うことができず、AS番号の部分が[!!]
という表示になってしまう。
IPアドレスからAS番号調べる簡単な方法あるかな。
— kooshin (@kooshin) 2016年4月26日
traceroute --as-path-lookupsオプションつけた場合は、内部でhttps://t.co/xW9L5eWVYpに問い合わせてた。 pic.twitter.com/BZa35pAJgW
使用するパケットのプロトコルによる違い
上記でほとんど説明したが、総括するとtracerouteにはICMP/UDP/TCPの違いがあって、ネットワークによってはそれぞれで結果が異なることが予想できるのでいくつか試してみるべきだろう。あと、最後にping
で宛先とのRTTを確認して、どの程度まで経路発見が出来たのかを把握しておくのが良い。
実際にやってみた結果を考慮する*4と以下のようになった。ネットワークによって結果は変わってくるので、これが唯一の解答にはならないとは思う。
- ファイヤウォールが厳しい場合はWell-Knownなポートを使ってみる
- ロードバランスによる影響を抑えたい場合は宛先と送信元のポートを固定する
- ウェブサーバに対してTCP80番を使うとCDNの影響により2〜3ホップで届いてしまうことがある(実際に知りたい経路ではない可能性がある)
- 宛先ポートと送信元ポートを固定すると結果が悪くなる場合もあったため、デフォルト設定も含めていくつかのオプションを試すべき