プログラミング

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

このページはこんな方におすすめです
  • 自分でパケットを作って送信してみたい
  • パケットの構造に興味がある

今回はパケットジェネレータのサンプルとしてUDPパケットを送信します。

その際IPヘッダ−とUDPヘッダーを自ら設定します。

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

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

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

パケット送信の流れ

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

  1. Raw Socket(生ソケット)を作成する
  2. IPヘッダーを作成する
  3. UDPヘッダーを作成する
  4. 送信する

IPヘッダー作成のポイント

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

IPヘッダーを含めて送信するためにはRaw Socket(生ソケット)を作成する必要があります。

Raw Socketを使うにはセキュリティの関係上、ルート権限が必要になります。

ソケットオプションについて

IPヘッダーを送信パケットに含めるためソケットオプション(IP_HDRINCL)を設定する必要があります。

Linuxはこのオプションを設定しなくてもIPヘッダーを含めてパケットを送信できますがBSD(macOS)はソケットオプションが必須です。

バイトオーダー

IPヘッダーを作成する上で重要な点は「ip_len」「ip_off」の取り扱いです。

IPヘッダーを作成するときのバイトオーダーについて明記されたドキュメントがないのですが、LinuxはすべてのIPヘッダー値をネットワークバイトオーダーで設定する必要があるのに対して、BSD(macOS)は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など)を設定しても問題なく送信できます。

  • Linuxはすべてネットワークバイトオーダー
  • BSDは ip_len と ip_off をホストバイトオーダー、その他はネットワークバイトオーダーで送信する
  • IPヘッダーのチェックサムはOSが計算する

UDPヘッダー作成のポイント

UDPヘッダーの作成で注意するのはチェックサムの計算です。

UDPはチェックサムを0に設定しても問題なく動作するのですが、チェックサム計算の方法を理解するためにも自分で計算してみましょう。

疑似ヘッダー

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

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

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

パケットの送信方法

パケットの送信にはsendto(2)を使用します。

BSD(macOS)はLinuxと違って厳密なためIPヘッダーで設定したip_lenの値とsendto(2)へ渡す送信サイズに差異があるとエラーになりますから注意してください。

では、次のセクションでサンプルコードと実行例を掲載します。

サンプルコードと実行例

サンプルコード

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

サンプルコードはGitHubからも落とせます。

≫サンプルコード

実行例

実行するためには「送信元IPアドレス」「送信先IPアドレス」「送信先UDPポート番号」「データ文字列」の4つを引数に設定します。

プログラムの実行はmacOSでおこないました。ルート権限が必要であることに注意してください。

パケットの送信先である192.168.2.6でtcpdumpを実行します。

IPヘッダーが正しく設定できていることを確認するために、念のためTOSを1に設定しました。

ご覧のとおりtcpdumpの結果を見るとTOSが1になっています。送信元IPアドレスも1.2.3.4となっていて送信成功であることがわかります。

まとめ

パケットジェネレータの作成はパケットキャプチャに比べると難易度は高いですが、UDPパケットの送信ならばさほど難しくないと思います。

TCPはヘッダー構造が異なるだけでやることは同じですから応用してTCPにも挑戦してみてください。

更にイーサネットヘッダーまで送信できるようになるとLAN内であらゆる事ができるようになります。イーサネットヘッダーの送信については、また別の機会に書きたいと思います。

最後にチェックポイントをまとめておきます。

  • LinuxとBSD(Mac)の仕様の違いに注意する
  • ソケットオプションを忘れない
  • バイトオーダーについて注意する

パケットジェネレーターに関するドキュメントは少ないため経験則に頼る場面が少なくありません。本格的にパケットジェネレータを作成する場合は「UNIXネットワークプログラミング〈Vol.1〉ネットワークAPI・ソケットとXTI」を是非読んでください。

UNIXネットワークプログラミング〈Vol.1〉ネットワークAPI:ソケットとXTI

UNIXネットワークプログラミング〈Vol.1〉ネットワークAPI:ソケットとXTI

W.リチャード スティーヴンス
発売日: 1999/07/01
Amazonの情報を掲載しています

COMMENT

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA


日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)