5

所以我的目标是使用 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;     
    }
4

1 回答 1

0

侦听您的 UDP 数据包的机器类型是什么?一些主机防火墙拒绝发送 ICMP 消息

于 2013-03-14T11:46:23.887 回答