使用WinSock侦听任意ICMP超时(TTL = 0)报文

Listening for arbitrary ICMP Time Exceeded (TTL = 0) packets with WinSock

本文关键字:TTL 报文 超时 WinSock 任意 ICMP 使用      更新时间:2023-10-16

所以我的目标是使用WinSock和原始套接字来侦听所有ICMP超时数据包(当IP数据包的TTL达到0时由网关生成)。

我的第一种方法涉及2套接字,一个是UDP与TTL设置为2(几乎保证TTL达到0;wireshark确认了这一点),另一个是带有IPPROTO_ICMP的SOCK_RAW。

这种方法不起作用——我假设ICMP套接字只会返回与发送的数据包匹配的数据包(即,echo request -> echo reply)。将此方法进一步推进,我打开SIO_RCVALL(混杂模式-套接字接收所有内容)。几乎言出必行,我开始接收该套接字上的所有入站和出站数据包,除了ICMP超时(可能还有其他)。这可以通过让一个线程每5秒发送一个TTL为2的UDP数据包来显示,但是没有返回ICMP数据包。为了证明ICMP确实出现了,我可以从cmd命令中观察到一个简单的ping所涉及的ICMP数据包。

我的第二种方法是把UDP和ICMP放在同一个套接字上。这涉及到我制作IP和UDP报头。Wireshark显示出UDP输出,正如预期的那样,没有创建数据包(校验和等)的问题,并且还显示返回的ICMP超时数据包,然而,我再一次看不到任何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;     
    }

什么类型的机器监听你的UDP数据包?有些主机防火墙拒绝发送ICMP消息