rawソケットを利用することで、普段カーネルしか扱うことが出来ないIPヘッダを操作、
パケットに格納される送信元IPアドレス、送信先IPアドレスをプログラマが任意に設定することが出来ます。
IPヘッダ構成
IPヘッダの構成は、下記のようになります。
各フィールドの意味をそれぞれ記します。
バージョン:プロトコルバージョン IPv4/IPv6
ヘッダ長:Iオプションを含むIPヘッダ長
サービスタイプ(TOS):RFC 1349で決定されているアプリケーション毎に割り当てられる値
RFC 1349は、→http://www.wildpackets.com/elements/rfcs/rfc1349.txt
パケットの優先度判断で利用されることを想定していたとのことですが、今日TCP/IPの実装でサポートされていないらしい。
全データ長:IPデータグラムの全長をバイトで示す
識別:パケットの識別子 通常は、データグラムの送信順に1つずつ増加する。
フラグ:データが分割されているのかどうかを示す情報
フラグメントオフセット:フラグメントデータが、元のデータの何バイト目からのデータなのかを示す
生存期間(TTL):データグラムが通過できるルーターの数の限界を示す。
プロトコル:各種プロトコルを設定する。
ヘッダチェックサム:IPヘッダ部分のみを計算の対象とするチェックサム値。
発信元IPアドレス:その名のとおり。今回はここにプログラムで任意の値を設定する。
発信先IPアドレス:その名のとおり。今回はここにプログラムで任意の値を設定する。
オプション:その名のとおり。現在のところ、
RFC 1108で定義されたセキュリティおよびハンドリング制限事項
レコードルート(各ルーターにIPアドレスを記録させる)
タイムスタンプ(各ルーターにIPアドレスと時間を記録させる)
...etcが定義されている。
IPヘッダを操作するには
IP_HDRINCLをソケットオプションに設定し、IPヘッダを作成し送出することで可能になります。
ソケットオプションの設定
const int on = 1;
if(setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)) < 0)
IPヘッダの作成
IPヘッダ構成に示したデータ構造に一致する構造体を利用し、メンバに適切な値を設定します。
struct ip *ip;
ip = (struct ip *) sendipbuf; // ヘッダを格納する領域取得済みのバッファ
ip->ip_v = 4;
ip->ip_hl = 5;
ip->ip_tos = 0;
ip->ip_len = htons(sizeof(struct ip));
ip->ip_id = htons(240);
ip->ip_off = htons(0x4000);
ip->ip_ttl = 255;
ip->ip_p = IPPROTO_RAW;
ip->ip_sum = 0; // チェックサム 本来は計算しなくてはならないが今回は0を与える。
ip->ip_src.s_addr = src.s_addr; // 送信元IPアドレス
ip->ip_dst.s_addr = dst.s_addr; // 送信先IPアドレス
今回作るプログラム
pingプログラム(※)をベースとして、IPヘッダ改変機能(IPv4)をもつパケット定期送出プログラムを作成してみます。
※ベースとしたpingプログラムは、下記を参照のこと。
IPヘッダ改変機能をもつpingプログラムは、
さすがに不味い気配がプンプンするので、
IPヘッダが改変されたICMPメッセージの送出機能は削除しますw
改変したプログラムは、本ページ下部にzip圧縮したものを張り付けてあります。
プログラム構成
主な修正箇所
main.c
引数で与えられる送信元IPアドレスをネットワークバイトオーダーに変換しグローバル変数に格納
引数で与えられる送信先IPアドレスをネットワークバイトオーダーに変換しグローバル変数に格納
send_v4.c
IPヘッダの作成、送信処理追加
ICMPヘッダ送信部削除
readloop.c
ソケットオプションIP_HDRINCLの設定
パケット受信処理削除
ビルド環境
Ubuntu 12.04.1 LTS
gcc バージョン 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5)
今回のプログラムは、本ページ下部にzip圧縮したものを張り付けてますが、これ単体では、ビルド出来ません。
http://www.kohala.com/start/unpv12e.html
からダウンロード出来る一連のソースを先にビルドして、環境を構築する必要があります。
ネットワーク構成
パケット送出ホスト(192.168.11.8)
OS: Ubuntu 12.04.1 LTS
パケット受信ホスト(192.168.11.11)
OS: mac os x lion 10.7.5
ネットワーク上に、IPアドレス192.168.11.100を持つ機器は存在しない。
パケット送出実行手順
パケット送出ホストにIPアドレス192.168.11.8が割り当てられている状況下で、
IPヘッダ発信元IPアドレスフィールドに、192.168.11.8以外のIPアドレス192.168.11.100を与えます。
rawソケットを取り扱うため、rootで実行する必要があります。
sudo ./ping 192.168.11.100 192.168.11.11
結果ダンプ
パケット送出ホスト(192.168.11.8)側でのwiresharkダンプ
パケット受信ホスト(192.168.11.11)側でのwiresharkダンプ
ダンプした結果をみると、どちらも、存在しないホスト(192.168.11.100)からパケットが送出されていることが分かります。
つまり、プログラムによってIPヘッダの発信元IPアドレスフィールドを変更出来ることが確認できます。