したがって、私の目標は、WinSock と raw ソケットを使用して、すべての ICMP Time Exceeded パケット (IP パケットの TTL が 0 に達したときにゲートウェイによって生成される) をリッスンすることです。
私の最初のアプローチには 2 つのソケットがあり、1 つは TTL が 2 に設定された UDP で (TTL が 0 に達することがほぼ保証されています。wireshark はこれを確認しました)、もう 1 つは IPPROTO_ICMP を使用した SOCK_RAW です。
このアプローチは機能しませんでした。ICMP ソケットは、送信されたパケットに一致するパケットのみを返すと思います (つまり、エコー要求 -> エコー応答)。この方法をさらに推し進めて、SIO_RCVALL (プロミスキャス モード - ソケットがすべてを受け取る) をオンにしました。その言葉にほぼ忠実に、ICMP Time Exceeded (およびおそらく他のもの) を除いて、そのソケットですべてのインバウンドおよびアウトバウンド パケットを受信し始めました。これは、1 つのスレッドが 5 秒ごとに TTL 2 の UDP パケットを送信することによって示されますが、ICMP パケットは返されませんでした。ICMP が実際にまったく表示されていることを証明するために、cmd からの単純な ping に含まれる ICMP パケットを観察することができました。
私の 2 番目のアプローチは、UDP と ICMP を同じソケットに配置することでした。これには、IP および UDP ヘッダーの作成が含まれます。Wireshark は、パケット (チェックサムなど) の作成に問題がなく、予想どおりに UDP が送信されていることを示しています。また、ICMP Time Exceeded パケットが返されていることも示しています。繰り返しますが、ICMP が単純な ping で動作していることをテストしたとき)。
だから私の質問は、一体どうやってこれらのパケットを手に入れるのですか? 単純な tracert プログラムのソース コードを見てみましたが、やり方が大きく異なるものは見当たりませんでした。
ソケットの作成・設定
SOCKET s;
if((s = socket(AF_INET, SOCK_RAW, 0)) == SOCKET_ERROR)
{
cout << "socket(AF_INET, SOCK_RAW, 0) failed with error code: " << WSAGetLastError() << endl;
return 1;
}
sockaddr_in source;
memset(&source, 0, sizeof(source));
source.sin_family = AF_INET;
source.sin_port = 0;
source.sin_addr.S_un.S_addr = inet_addr("10.64.0.8");
if(bind(s, (sockaddr*) &source, sizeof(source)) == SOCKET_ERROR)
{
cout << "bind failed with error: " << WSAGetLastError() << endl;
return 1;
}
uint32_t optval = 1;
DWORD bytesReturned;
if (WSAIoctl(s, SIO_RCVALL, &optval, sizeof(optval), NULL, 0, &bytesReturned, NULL, NULL) == SOCKET_ERROR)
{
cout << "WSAIotcl() failed with error code " << WSAGetLastError() << endl;
return 1;
}
if (setsockopt(s, IPPROTO_IP, IP_HDRINCL, (char*) &optval, sizeof(optval)) == SOCKET_ERROR)
{
cout << "Failed to remove IP header. Error code: " << WSAGetLastError() << endl;
return 1;
}
ソケットからの読み取り
if(WSARecvFrom(s, &buffer, 1, &in, &flags, (sockaddr*) &from, &fromSize, &ol, NULL) == SOCKET_ERROR)
{
int error = WSAGetLastError();
if(error != WSA_IO_PENDING)
{
cout << "WSARecvFrom Failed with error code: " << error << endl;
return;
}
}
int rc = WaitForSingleObject(ol.hEvent, INFINITE);
if(rc == WAIT_FAILED)
{
cout << "Wait for object failed" << endl;
return;
}
WSAGetOverlappedResult(s, &ol, &in, false, &flags);
ipv4_header_t* ipHeader = (ipv4_header_t*) buf;
if(ipHeader->dest == addr)
{
cout << "Received Protocol: " << (uint32_t) ipHeader->protocol << endl;
}