所以我的目标是使用 WinSock 和原始套接字来侦听所有 ICMP Time Exceeded 数据包(当 IP 数据包的 TTL 达到 0 时由网关生成)。
我的第一种方法涉及 2 个套接字,一个是 TTL 设置为 2 的 UDP(几乎可以保证 TTL 达到 0;wireshark 证实了这一点),另一个是带有 IPPROTO_ICMP 的 SOCK_RAW。
这种方法不起作用 - 我假设 ICMP 套接字只会返回与已发送数据包匹配的数据包(即回显请求 -> 回显回复)。进一步推动这个方法,我打开了 SIO_RCVALL (混杂模式 - 套接字接收所有内容)。几乎是真的,我开始在那个套接字上接收所有入站和出站数据包,除了 ICMP Time Exceeded (可能还有其他)。这通过让一个线程每 5 秒发送一个 TTL 2 的 UDP 数据包来显示,但是没有返回任何 ICMP 数据包。为了证明 ICMP 确实出现了,我能够观察到来自 cmd 的简单 ping 中涉及的 ICMP 数据包。
我的第二种方法是将 UDP 和 ICMP 放在同一个套接字上。这涉及我制作 IP 和 UDP 标头。Wireshark 显示 UDP 出去了,正如预期的那样,创建数据包(校验和等)没有问题,并且还显示了返回的 ICMP Time Exceeded 数据包,但是我再次看不到任何 ICMP 流量进入我的套接字(除此之外,当我测试 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;
}