IP ヘルパーの ICMP 関数の使用方法
WinSock 及び IP Helper 関数等では低レベルな機能を利用できるため、非常に柔軟に様々な状況に対応することが可能です。
しかし、低レベルであるが故に、パワフルであることのトレードオフとして、一から色々と実装しなければならず面倒だったりします。
IP Helper 関数群に分類される API として、IP アドレスデータの変換系の API やユーティリティの API がある他、 ARP や ICMP などのプロトコルをサポートするための API もあります。
ここでは ICMP Echo、すなわち Ping を簡単に利用するための IP Helper 関数をみてみましょう。
エラー処理等はかなり端折ってます。assert でチェックしているのみですので、その点ご了承くださいませ。
ICMP 関数の使い方
ICMP 関数で Echo Request を送信して Reply を受け取る手順は次の通りです。
- IP アドレスの準備 (IPAddr への変換)
- バッファの準備
- ICMP ハンドルをオープン
- IcmpSendEcho で Echo Request を送信して受信 (ここは同期処理)
- 必要に応じて結果の確認
- バッファの解放
- ICMP ハンドルを閉じる
この手順を実装すると次のようになります。
#include "stdafx.h"
static char* REQDATA = "abcdefghijklmnop";
int main(int argc, char* argv[])
{
IPAddr ipaddr;
HANDLE hIcmp = NULL;
DWORD ret;
char* pReply = NULL;
DWORD cbReply = 0;
DWORD cbRequest = strlen(REQDATA);
// Destination Address
ipaddr = inet_addr("127.0.0.1");
// Reply Buffer
cbReply = sizeof(ICMP_ECHO_REPLY) + cbRequest;
pReply = (char*) malloc(cbReply);
assert(pReply);
// Sending Icmp Request
hIcmp = IcmpCreateFile();
assert( INVALID_HANDLE_VALUE != hIcmp );
ret = IcmpSendEcho(
hIcmp,
ipaddr,
REQDATA,
cbRequest,
NULL,
pReply,
cbReply,
5000);
if(ret){
PICMP_ECHO_REPLY p = (PICMP_ECHO_REPLY) pReply;
printf("Status: %d\n", p->Status);
printf("Round Trip Time: %d\n", p->RoundTripTime);
printf("Data Size: %d\n", p->DataSize);
PrintHexDump(p->DataSize, (PBYTE) p->Data);
}
free(pReply);
IcmpCloseHandle(hIcmp);
return 0;
}
珍しく Visual Studio で試したのでインクルードしているファイルが atdafx.h なんてなってますが、 その中で、assert.h、WinSock2.h、IPHlpApi.h、IcmpAPI.h をインクルードしています。
また、ビルドの設定で ws_win32.lib と iphlpapi.lib をリンクしています。
これを実行すると、ICMP Echo が確認できるはずです。試すときは IP アドレスを適当に変えてください。
念のためネットワークモニタでパケットをとると、確かに 16 バイトの abcd...nop というデータが送られ、それが送り返されたことがわかります。
ちなみに参考のため、コマンドラインから ping コマンドを用いて一度だけ、16 バイトのデータを送るには次のようにします。
> ping -n 1 -l 16 127.0.0.1
Linux ではオプションが少し違って次の通り。
$ ping -c 1 -s 16 127.0.0.1