パケットジェネレータのつくり方

パケットジェネレータのサンプルとしてUDPパケットを送信します。パケットをつくって送信するためにIPヘッダ−、UDPヘッダーを自ら設定します。

パケットジェネレータの作成はパケットキャプチャと比べて難易度が上がります。パケットキャプチャの場合はネットワークを流れるIPヘッダーやUDPヘッダーを単純に表示させるだけでしたが、パケットジェネレータは自らヘッダーの値を設定しなければなりません。そのためには、ヘッダーの構造だけでなくヘッダー値の意味を理解していなくてはなりません。

今回はパケットジェネレータの作成に必要な知識を羅列し、最後にサンプルコードと実行例を掲載します。サンプルコードのコンパイルと実行はOS X El Capitan‎とCentOS 7でおこなっています。

スポンサーリンク

パケット送信の流れ

パケットの送信は以下のような流れになります。

  • Raw Socket(生ソケット)を作成する
  • IPヘッダーを作成する
  • UDPヘッダーを作成する
  • 送信する

Raw Socket(生ソケット)を作成する

IPデータグラムを送信するためにRaw Socket(生ソケット)を作成する必要があります。今回はUDPパケットを作成して送信しますが、IPヘッダーも含めるため通常のソケットでは実現することができません。IPヘッダーを含めて送信するためにはRaw Socket(生ソケット)が必要です。セキュリティの関係上、ルート権限が必要になります。

ソケットオプション

ブロードキャストパケットを送信する際などにソケットオプションを設定しますが、今回はIPヘッダーを送信パケットに含める必要があるため、ソケットオプション(IP_HDRINCL)を設定しています。なお、Linuxはこのオプションを設定しなくてもIPヘッダーを含めてパケットを送信できます。BSD(Mac OS X)はソケットオプションが必須です。

IPヘッダー作成

基本的にIPヘッダーの構造がわかっていれば問題ありません。重要な点は「ip_len」「ip_off」の取り扱いです。IPヘッダーを作成するときのバイトオーダーについて明記されたドキュメントがないのですが、LinuxはすべてのIPヘッダー値をネットワークバイトオーダーで設定する必要があるのに対して、BSD(Mac OS X)はip_lenとip_offをホストバイトオーダーで設定し、それ以外はネットワークバイトオーダーで設定する必要があります(UNIXネットワークプログラミング 第2版 Vol.1 p.635「25.3 rawソケットからの出力」)。

ただし、Linuxはip_lenを設定しても無視し、sendto(2)で引数にとる送信サイズをip_lenへ自動的に設定します。そのため、今回のサンプルではip_lenとip_offは両方ともホストバイトオーダーで設定しています。

補足情報として、NmapはLinuxとBSDのバイトオーダー設定方法の差異を吸収するBSDFIX/BSDUFIXというマクロを使用していましたが、3.90からこのマクロを削除しています(Nmap Change Log)。

また、チェックサムを計算して設定していますが、IPヘッダーのチェックサムはOSがパケット送信時に設定しますので、適当な値(123など)を設定しても問題なく送信できます。

UDPヘッダー作成

UDPヘッダーの作成で注意するのは、チェックサムの計算です。UDPはチェックサムを0にしても良いのですが、チェックサム計算の方法を理解するためにも、自分で計算してみましょう。

疑似ヘッダー

UDPのチェックサム計算には疑似ヘッダーというものを使います。

疑似ヘッダーの末尾に、送信するUDPヘッダーとデータを付加します。そして疑似ヘッダーとUDPヘッダー、データを含めてチェックサムの計算をおこないます。つまり、次のようなデータ構造を用意してチェックサムの計算をおこないます。

チェックサムの計算に必要な関数は、BSDのping.cを流用します。

パケット送信

パケットの送信にはsendto(2)を使用します。BSD(Mac OS X)はLinuxと違って厳密なため、IPヘッダーで設定したip_lenの値とsendto(2)へ渡す送信サイズに差異があるとエラーになりますから注意してください。それ以外は特別なことはありません。では、次のセクションでサンプルコードと実行例を掲載します。

サンプルコードと実行例

サンプルコード

UDPはヘッダー構造が単純ですが、それでもコード行数はキャプチャに比べてかなり増えています。なお、コードを単純にする目的でIPヘッダーやUDPヘッダー値の大半をハードコーディングしています。慣れてきたら引数でヘッダー値を設定できるようにすると、本格的なパケットジェネレータとなってくるはずです。

実行例

実行するためには「送信元IPアドレス」「送信先IPアドレス」「送信先UDPポート番号」「データ文字列」の4つを引数に設定します。プログラムの実行はMacでおこないました。ルート権限が必要であることに注意してください。

パケットの送信先である192.168.2.6はリモートのCentOS 7なので、CentOS 7でtcpdumpの結果を確認します。

IPヘッダーが正しく設定できていることを確認するために、念のためTOSを1に設定しました。ご覧のとおりtcpdumpの結果からTOSが1になっていることが判ります。また、送信元IPアドレスも1.2.3.4となっています。

まとめ

いかがでしたか?パケットキャプチャに比べると難易度は高いですが、UDPパケットの送信ならばさほど難しくないと思います。TCPはヘッダー構造が異なるだけでやる事は同じですから、応用してTCPにも挑戦してみてください。更にイーサネットヘッダーまで送信できるようになると、LAN内であらゆる事ができるようになります。イーサネットヘッダーの送信については、また別の機会に書きたいと思います。

スポンサーリンク