タイミングとパフォーマンス

Nmapの開発を行ううえで、常に優先事項の1つとして念頭に置いているのが「パフォーマンス」である。筆者のローカルネットワーク上のホストにデフォルトスキャン(nmap <hostname>)を実行するのにかかる時間は、5分の1秒である。瞬き1回程度の時間だが、何万、いや何十万というホストをスキャンする場合は、この時間も積算されるわけだ。さらには、UDPスキャンやバージョン検出などの特定のスキャンオプションによって、スキャン時間が大幅に増加する可能性がある。特定のファイアウォール設定、特に応答レート制限の場合も同様である。Nmapには、スキャン速度を上げるための並行処理や高度なアルゴリズムが使用されているが、Nmapをどのように実行するかを最終的に決めるのはユーザである。熟練したユーザは、自分たちの時間的制約を満たしつつ、関心のある情報だけを取得するように、Nmapコマンドに入念な細工を施すものだ。

スキャン時間を改善するテクニックとしては、重要でないテストの省略や、Nmapを最新版にアップグレードする(パフォーマンスの強化は頻繁に行われている)などが挙げられる。タイミング(時間調節)パラメタを最適化すると、かなりの違いが生じる場合もある。この種のオプションを、以下に列挙する。

--min-hostgroup <size>; --max-hostgroup <size> (並列スキャンのグループサイズを調節する)

Nmapには、複数のホストを並行してポートスキャンやバージョンスキャンする能力がある。これは、ターゲットのIP空間をいくつかのグループに分割し、一度に1グループずつスキャンすることで行われる。一般に、グループの規模を大きくするほど、効率がよくなる。デメリットは、グループ全体のスキャンが終了してからでないと、ホストの結果が得られないことである。そのため、グループサイズが50から始めた場合、ユーザがレポートを受け取るのは、(冗長モードで最新情報が提供される場合を除く)最初の50台のホストが完了してからになる。

デフォルトでは、Nmapはこうした競合問題に対して、妥協的なアプローチを採る。すなわち、最初の結果がすぐに得られるように、5程度の小さなグループサイズから始めて、それから1024くらいにまで増やすようにする。初めに設定する数値は、指定するオプションによって決まる。効率上の理由で、Nmapは、UDPスキャンやポート数の少ないTCPスキャンに対しては比較的大きなグループサイズを用いる。

グループサイズの最大値が--max-hostgroupで指定されている場合、Nmapがこのサイズを超過することは決してない。グループサイズの最小値を--min-hostgroupで指定すると、Nmapはグループサイズがこの値よりも小さくならないようにしようとする。Nmapは、オプションで指定された最小値の条件を満たすのに十分な数のターゲットホストが、所定のインターフェース上に残っていない場合は、指定より小さいグループサイズを使わざるを得なくなる場合もある。グループサイズを特定の範囲内に収めるために、最大値と最小値の両方を設定する場合もあるが、これが必要になるケースはめったにない。

このオプションの主な使用法は、スキャンをより高速に完了できるように、グループサイズの最小値に大きな値を指定することである。クラスC規模のネットワークをスキャンするには通常、256を指定する。数多くのポートに対してスキャンを実行する場合は、これ以上の数値を設定しても、あまり効果は期待できない。ポート数がごくわずかのスキャンを行う場合は、ホストのグループサイズを2048以上に設定すると有効だろう。

--min-parallelism <numprobes>; --max-parallelism <numprobes> (プローブの並列処理を調節する)

ホストグループに向けて送信されるプローブの総数を決めるオプションで、ポートスキャンやホスト発見に用いられる。デフォルトでは、Nmapは、ネットワークパフォーマンスに基づいて、常に変化する理想的な並列処理可能数を算出している。パケットが続けて破棄される場合は、Nmapは処理速度を落とし、送信するプローブの数を減らす。理想的なプローブ数は、ネットワークが示すパフォーマンスの高さに応じて、緩やかに増加する。これらのオプションは、この変数に対して上限と下限を設定する。デフォルトでは、理想的な並列処理可能数は、ネットワークの信頼性が低い場合は1まで下がり、最高の状態の場合は数百まで上がる場合もある。

最も一般的な使用法は、--min-parallelismに1より大きな数値を設定して、パフォーマンスの低いホストやネットワークに対するスキャンの処理速度を上げることである。ただし、軽く使うにはリスクを伴うオプションであり、あまりに大きな値を設定すると、精度に悪影響が及ぶ場合がある。またこれにより、ネットワーク状態に基づいて並列処理可能数を動的に制御するNmapの能力を低下させることにもなる。10程度にしておくのが妥当かと思われるが、この値の調節は最後の手段としてしか行わない。

Nmapがホストに対して一度に2つ以上のプローブを送れないようにするために、--max-parallelismオプションに「1」が設定される場合がある。これは、(下で述べる)--scan-delayオプションと組み合わせて用いると便利である。もっとも、後者のオプションは通常は単独でも十分に役立つものである。

--min-rtt-timeout <time>, --max-rtt-timeout <time>, --initial-rtt-timeout <time> (プローブのタイムアウトを調節する)

Nmapは、プローブを中止もしくは再送信するまで、プローブの応答を待機する時間をどのくらいにするかを決める実行タイムアウト値を保持している。 この値は、それまでに送信したプローブの応答時間に基づいて算出される。 ネットワークの待ち時間が、かなり長くて変化しやすい場合、タイムアウト値は数秒にまで達する可能性もある。 また、反応が鈍いホストをスキャンする際には、控え目な(高い)レベルから始めて、しばらくの間そのままの値にしておく場合もある。

--max-rtt-timeout--initial-rtt-timeoutにデフォルトより小さな値を指定すると、スキャン時間を大幅に短縮できる。 厳重なフィルタ処理が施されたネットワークに対してpingなし(-Pn)スキャンを行う場合は特にそうなる。 とはいえ、あまりアグレッシブに小さくしすぎないように。 小さすぎる値を指定してしまったために、応答が送信されている間に数多くのプローブがタイムアウトして再送信されてしまい、結果的にスキャンの実行に通常より余計に時間がかかる可能性があるからだ。

すべてのホストがローカルネットワーク上にある場合、100ミリ秒(ms)(--max-rtt-timeout 100ms)にするのが、アグレッシブに小さく指定するとしても妥当な値である。 ルーティングが関係してくる場合は、ICMP pingユーティリティか、ファイアウォールを通過できる可能性が高いhping2などのカスタムパケット作成ツールを用いて、最初にネットワーク上のホストにpingを実行する必要がある。 10個程度のパケットを送信してみて、最大往復時間(RTT)を調べること。 --initial-rtt-timeoutの値は、この値を2倍にするとよい。 また、--max-rtt-timeoutの値は、これを3倍か4倍にしたものにするとよいだろう。 筆者は通常、pingで調査した時間の大小に関係なく、最大RTTを100ms未満に設定することはないし、1000msを超える値にすることもない。

--min-rtt-timeoutは、ほとんど使用されないオプションであるが、ネットワークの信頼性があまりに低いために、Nmapのデフォルト値でも小さく設定しすぎになる場合に役立つと思われる。 Nmapは単にタイムアウト時間を指定された最小値まで小さくするだけなので、ネットワークが信頼できると思われる場合は、この要求は異常であり、nmap-devメーリングリストにバグとして報告すべきである。

--host-timeout <time> (遅いターゲットホストを見切る)

ホストのなかには、とにかくスキャンに長い時間がかかるものがある。 理由としては、性能・信頼性の低いネットワークハードウェアやソフトウェア、パケットレート制限、厳重なファイアウォールなどが考えられる。 スキャン対象ホスト全体の数パーセントを占める、最も反応が遅いホストによって、スキャン時間の大半を使われてしまうこともある。このような無駄はカットして、遅いホストは最初から省くほうがよい場合がある。 これは、待機しても構わない時間の最大値を--host-timeoutに指定することで実現できる。 たとえば、30m を指定して、Nmapが1つのホストで30分以上無駄にしないようにできる。 この30分の待ち時間に、Nmapは他のホストも同時にスキャンしているので、完全な時間の損失にはならないことに注意。 タイムアウトするホストはスキップされ、ポートテーブル、OS検出、バージョン検出などの結果は出力されない。

0 は特別な値で タイムアウトをしない を指定できる。 ホストのタイムアウトを15分にデフォルトで指定する T5 タイミングテンプレートの挙動を上書きする際に有用である。

--script-timeout <time>

スクリプトの中にはコンマ数秒で完了するものもあるが、スクリプトの性質、渡された引数、ネットワークやアプリケーションの状態などによって、数時間以上かかるものもある。 --script-timeout オプションは、スクリプトの実行時間に上限を設定する。 設定された時間を超えたスクリプトは終了し、何も表示されません。 デバッグ (-d) オプションを指定すると、各タイムアウトの詳細が表示される。 ホストおよびサービススクリプトの場合、スクリプトは1つの対象ホストまたはポートのみをスキャンし、タイムアウト時間は次のスクリプトのためにリセットされる。

0 は特別な値で タイムアウトをしない を意味する。 タイムアウトを10分とデフォルトで指定しているT5 タイミングテンプレートを上書きするために使用できる。
--scan-delay <time>; --max-scan-delay <time> (プローブ間の遅滞時間を調節する)

指定したホスト宛てに送られる各プローブの送信間隔において、指定した時間だけNmapを待機状態にする。 これは、が行われている場合に特に役に立つ。 Solarisマシンは(制限が特に厳しく)、通常はUDPスキャンのプローブパケットに対して、ICMPメッセージの応答を毎秒1回しか返さない。 Nmapがそれ以上のパケットを送ってもすべて無駄になる。--scan-delay は次のように設定する。 1sとすると、Nmapはその遅い速度に保たれる。Nmapは毎秒1回という遅いレートを保つことになる。 Nmapは、レート制限を検出し、それに応じてスキャン遅延を調整しようとするが、どの程度のレートが最適であるかがすでにわかっている場合は明示的に指定してもよい。

Nmapがレート制限に対処するためにスキャン遅延を増加させた場合、スキャンの速度は大幅に低下する。 --max-scan-delayは、Nmapが許容する最大の遅延時間を指定できる。 --max-scan-delay に少ない時間を指定すれば、スキャンを高速化できるが、リスクがある。 時間を少なく設定しすぎると、対象が厳格なレート制限を実装している場合に、無駄なパケット再送信や、ポートの取りこぼしが発生する可能性がある。

--scan-delay には、使用法がもうひとつある。 それは、閾値ベースの侵入検知・防御システム(IDS/IPS)の回避である。

--min-rate <number>; --max-rate <number> (スキャンレートを細かく制御)

Nmapのスキャン速度の自動制御は、適切なスキャン速度を見つけるのに効果的である。 しかし、適切なスキャン速度が事前にわかっている場合や、ある時間までにスキャンが終了することを保証しなければならない場合もある。 あるいは、Nmapがあまりに速くスキャンしないようにしなければならないかもしれない。 --min-rate--max-rate オプションは、このような状況に対応するために用意されている。

--min-rate オプションが与えられると、Nmap は与えられたレートと同じかそれよりも速くパケットを送信するよう最善を尽くす。 引数は、1 秒あたりの送信パケット数を表す正の実数である。 例えば、--min-rate 300 を指定すると、Nmap は送信レートを毎秒300パケット以上に維持しようとする。 最小レートを指定しても、条件が許す限り、Nmapがより速くなることはない。

同様に、--max-rateは送信レートを最大値を指定できる。 例えば、高速なネットワークを対象に毎秒100パケットの送信に制限するには、--max-rate 100を使用する。 10秒に1パケットという遅いスキャンを行うには、--max-rate 0.1を使用する。 --min-rate--max-rate を一緒に使うと、レートをある範囲内に保つことができる。

これらの2つのオプションは全体的なもので、個々のホストにではなく、スキャン全体に影響する。 ポートスキャンとホスト発見にのみ影響する。 OS検出のような他の機能では、独自のタイミングが実装されている。

実際のスキャン速度が指定した最小値を下回る可能性があるのは2つの条件である。 1つは、最小値がNmapが送信できる最速レートよりも速い場合であり、これはハードウェアに依存する。 この場合、Nmapは単に可能な限り高速にパケットを送信するが、このような高速なレートでは精度が低下する可能性が高いので注意が必要である。 2つ目のケースは、Nmapが何も送信しない場合である。たとえば、スキャンの最後にプローブが送信され、Nmapがタイムアウトや応答を待っているときである。 スキャンの終了時やホストグループ間でスキャンレートが低下するのは正常な現象である。 予測できない遅延を補うために、送信レートが一時的に最大値を超えることがあるが、平均してレートは最大値以下にとどまるだろう。

最小レートの指定は慎重に行う必要がある。 ネットワークが対応できる速度よりも速くスキャンすると、精度が低下する可能性がある。 場合によっては、速いレートを使うと、遅いレートを使ったときよりもスキャンが長くなることがある。 これはNmapの 適応伝送 アルゴリズムが、過剰なスキャン速度によるネットワークの輻輳を検知し、精度を上げるために再送信の回数を増やすからである。 そのため、パケットが高いレートで送信されても、全体としてはより多くのパケットが送信される。 総スキャン時間の上限の設定は、--max-retriesで再送信の回数を制限できる

--defeat-rst-ratelimit

多くのホストは、送信するICMPエラーメッセージ(ポート到達不能エラーなど)の数を減らすために、長い間レート制限 を使用してきた。 現在、いくつかのシステムは、生成するRST(リセット)パケットに同様のレート制限を適用している。 このようなレート制限を反映させるためにタイミングを調整するため、Nmapの速度が大幅に低下することがある。 --defeat-rst-ratelimit を指定することにより、Nmapにこれらのレート制限を無視させることができる (SYN スキャンなど、応答がないポートを open として扱わないスキャンのために)。

このオプションを使用すると、Nmapがレート制限されたRST応答を十分に待たないために、一部のポートが無応答に見えるため、精度が低下する可能性がある。 SYNスキャンでは、無応答に見えたポートは filtered となり、RSTパケット受信時に見られるclosed 状態にはならない。 このオプションは、開いているポートにしか関心がなく、closedfiltered を区別することに余分の時間を割けない場合に有用である。

--defeat-icmp-ratelimit

--defeat-rst-ratelimit と同様に、 --defeat-icmp-ratelimit オプションは精度と引き換えに速度を優先し、ICMP エラーメッセージをレート制限するホストに対して UDP スキャン速度を向上させるものである。 このオプションは、Nmap がポート到達不能メッセージを受信するために遅延しないようにするので、応答しないポートには、デフォルトの open|filtered の代わりに closed|filtered というラベルが付くことになる。 これは、UDP で実際に応答するポートだけを open として扱う。 多くの UDP サービスはこの方法では応答しないので、 このオプションは --defeat-rst-ratelimit よりも不正確である可能性が高い。

--nsock-engine iocp|epoll|kqueue|poll|select

与えられたnsock IO多重化エンジンの使用を強制する。 select(2) ベースのフォールバックエンジンのみが、システム上で利用可能と保証されている。 エンジンの名前は、利用するIO管理機能の名前にちなんで付けられている。 現在実装されているエンジンは、epollkqueuepollselect だが、どのプラットフォームにも存在しないものもないとは言い切れない。 デフォルトでは、Nmapは「最適な」エンジン、すなわち、このリストの中で最初にサポートされたエンジンを使用する。 nmap -Vを使って、自分のプラットフォームでどのエンジンがサポートされているかを確認できる。

-T paranoid|sneaky|polite|normal|aggressive|insane (タイミングテンプレートを設定する)

前節で述べたような、タイミングのきめ細やかな制御はとても効果が大きいが、分かりにくいと感じるユーザもいるようだ。 さらには、最適化を試みているスキャンを実行するよりも、適切な数値を選ぶほうが時間がかかるという事態に陥る可能性もある。 そこでNmapには、6つのタイミングテンプレートを用いたもっと簡単なアプローチが用意されている。 テンプレートは、-Tオプションと番号(0 - 5)か名前で指定できる。 テンプレート名は、paranoid (0)、 sneaky (1)、 polite (2)、 normal (3)、 aggressive (4)、insane (5)である。 最初の2つは、IDS回避用のテンプレートである。Politeモードは、スキャン処理速度を落とし、帯域幅とターゲットマシンのリソースの使用量を少なくするためのものである。 Normalモードはデフォルトなので、-T3としても特に何もしない。Aggressiveモードは、ユーザが適度に高速で信頼性の高いネットワーク上にいることを想定して、スキャン速度を上げる。 最後にInsaneモードは、 非常に高速なネットワーク上にいるか、あるいは速度と引き換えに精度の一部を犠牲にしても構わない場合を想定したモードである。

これらのテンプレートを利用すると、ユーザは、的確なタイミング値の選定はNmapに任せつつ、どの程度アグレッシブなスキャンを実行したいかを指定できる。 また、今のところきめ細かい制御のオプションが存在しない、速度の微調整の一部をこのテンプレートで行うこともできる。 例えば、-T4は、TCPポートに対するスキャン処理の動的な遅延時間が10msを越えないようにすることができ、また-T5では、この値の上限が5msに制限される。 テンプレートを最初に指定する場合に限り、きめ細かい制御オプションとテンプレートを組み合わせて用いることができる。 そうしないと、テンプレートの標準値がユーザの指定した値で上書きされてしまう。適度に最近の信頼性が高いネットワークをスキャンする場合は、-T4がお勧めである。 きめ細かい制御オプションを追加する場合でも、このオプションを(コマンドラインの最初に)付けておくことで、テンプレートによって有効になる細部にわたる最適化のメリットを享受できる。

適正なブロードバンド接続やイーサネット接続の環境にいる場合は、常時-T4を利用することをお勧めする。 -T5を好む人もいるが、私にはアグレッシブすぎるように思われる。 -T2を指定しているユーザもたまにいるが、ホストをクラッシュさせる可能性が低いと見ているからか、自分のことを全般的に礼儀正しい(polite)と思っているからのようだ。 こうしたユーザは単に、「-T Polite」が実際にはいかに遅いものであるかを理解していないだけだ。 Politeモードは、デフォルトスキャンの10倍の時間がかかる。デフォルトのタイミングオプション(-T3)に関しては、マシンのクラッシュや帯域幅が問題になることはめったにないので、慎重なスキャンユーザには通常はこれを勧めている。 タイミング値をあれこれ操作して、これらの問題を軽減しようとするよりも、バージョン検出を省略するほうがずっと効率的である。

-T0-T1は、IDSの警告を回避するには役立つかもしれないが、何千ものマシンやポートをスキャンするには非常に長い時間がかかる。 そのように長いスキャンを行う場合は、あらかじめ用意された-T0-T1の値に頼るよりも、必要に応じて的確なタイミング値を設定するほうが好ましいだろう。

T0の主な効果は、スキャンを連続的に実行して一度に1つのポートしかスキャンされないようにすることと、各プローブを送信する間に5分間待機することである。 T1T2は似ているが、それぞれプローブ間の待機時間が15秒と0.4秒しかない。 T3はNmapのデフォルト動作で、並列処理が含まれる。T4--max-rtt-timeout 1250ms --min-rtt-timeout 100ms --initial-rtt-timeout 500ms --max-retries 6に相当し、TCPおよびSCTPスキャンの最大遅延時間を10msに設定する。 T5--max-rtt-timeout 300ms --min-rtt-timeout 50ms --initial-rtt-timeout 250ms --max-retries 2 --host-timeout 15m --script-timeout 10m --max-scan-delayに相当し、TCPおよびSCTPスキャンの最大遅延時間を5msに設定する。 UDPの最大スキャン遅延時間は T4T5 では設定できないが、 --max-scan-delay オプションで設定できる。