【问题标题】:Writing a basic traceroute script in C用 C 编写一个基本的 traceroute 脚本
【发布时间】:2011-02-03 13:27:24
【问题描述】:

我必须编写一个 trceroute 脚本,但我不确定我的尝试是否正确。

现在我就是这样做的(如果我做错或笨拙,请纠正我):

  1. 获得了 ip- 和 udpheader 的结构
  2. 校验和函数
  3. 打开 2 个套接字:一个用于以 SOCK_RAW 模式发送 UDP 数据包(用于操作 ttl),另一个用于从路由器接收 ICMP 应答。
  4. 使用 sendto() 发送 UDP 数据包
  5. 不知道如何接收和处理 ICMP 答复

有没有比使用 sock_raw 我必须自己定义所有标题内容更舒适的方法来更改 TTL? 打开 ICMP sock 时我应该为 socket() 使用哪些参数? 如何接收 ICMP 应答?

【问题讨论】:

    标签: c udp raw-sockets icmp traceroute


    【解决方案1】:

    您的目标是什么平台?这是来自 OpenBSD source 的 BSD 风格:

    if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0)
        err(5, "icmp socket");
    if ((sndsock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0)
        err(5, "raw socket");
    

    我相信在 Linux 上,您需要将 IP_RECVERRrecvmsg(2)MSG_ERRQUEUE 一起使用,请参阅 ip(7)

    【讨论】:

    • 感谢您的回复。它让我前进了一点。
    【解决方案2】:

    关于设置TTL,可以使用setsockopt()。这是 iputils 在 Linux 上ping 的源代码的摘录:

    if (setsockopt(icmp_sock, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, 1) == -1) {
        perror ("ping: can't set multicast time-to-live");
        exit(2);
    }
    
    if (setsockopt(icmp_sock, IPPROTO_IP, IP_TTL, &ittl, sizeof(ittl)) == -1) {
        perror ("ping: can't set unicast time-to-live");
        exit(2);
    }
    

    【讨论】:

    • 谢谢,这似乎让事情变得容易多了。
    【解决方案3】:

    我遇到了同样的问题并解决了。 你需要

    1. 使用 ICMP 协议创建新套接字
    2. 绑定到特定端口,例如 33434
    3. 收到 ICMP 回复。

    我会展示我的代码。

      // ......create sending socket and fill the udp data...
    
      // create socket to receive ICMP reply
        SOCKET sock = WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL, 0,
                                WSA_FLAG_OVERLAPPED);
    
        // from for receiving data about routing server 
        SOCKADDR_IN server_addr, from;
        int fromlen = sizeof(from);
        memset(&server_addr, 0, sizeof(server_addr));
        server_addr.sin_family = AF_INET;
        server_addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
        server_addr.sin_port = htons(33434);
    
        // Set the receive and send timeout values to a second
        timeout = 1000;
        ret = setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout,
                         sizeof(timeout));
        if (ret == SOCKET_ERROR) {
            printf("setsockopt(SO_RCVTIMEO) failed: %d\n", WSAGetLastError());
            return -1;
        }
        timeout = 1000;
        ret = setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout,
                         sizeof(timeout));
        if (ret == SOCKET_ERROR) {
            printf("setsockopt(SO_SNDTIMEO) failed: %d\n", WSAGetLastError());
            return -1;
        }
    
        // bind to the port 33434
        int err = bind(sock, (SOCKADDR *)&server_addr, sizeof(SOCKADDR));
        if (err != 0) {
            fprintf(stderr, "bind with error: %d\n", WSAGetLastError());
            return 3;
        }
    
        for (ttl = 1; ((ttl < maxhops) && (!done)); ttl++) {
            int bwrote;
            // Set the time to live option on the socket
            set_ttl(sockRaw, ttl);
            // Fill in some more data in the UDP header
            ((UdpHeader *)udp_data)->length = 8;
            ((UdpHeader *)udp_data)->dest_port = htons(33434);
            ((UdpHeader *)udp_data)->source_port = htons(33434);
            ((UdpHeader *)udp_data)->checksum =
                checksum((USHORT *)udp_data, datasize);
            // Send the UDP packet to the destination
            bwrote = sendto(sockRaw, udp_data, datasize, 0, (SOCKADDR *)&dest,
                            sizeof(dest));
            if (bwrote == SOCKET_ERROR) {
                if (WSAGetLastError() == WSAETIMEDOUT) {
                    printf("%2d  Send request timed out.\n", ttl);
                    continue;
                }
                printf("sendto() failed: %d\n", WSAGetLastError());
                return -1;
            }
            // Read a packet back from the destination or a router along the way.
            ret = recvfrom(sock, recvbuf, MAX_PACKET, 0, (struct sockaddr *)&from,
                           &fromlen);
            if (ret == SOCKET_ERROR) {
                if (WSAGetLastError() == WSAETIMEDOUT) {
                    printf("%2d  Receive Request timed out.\n", ttl);
                    continue;
                }
                printf("recvfrom() failed: %d\n", WSAGetLastError());
                return -1;
            }
            /* Decode the response to see if the ICMP response is from a router
             * along the way or whether it has reached the destination. */
            done = decode_resp(recvbuf, ret, &from, ttl);
            Sleep(1000);
        }
    
    

    它可以在我的电脑上运行。 (Windows 10)

    the result in my computer

    【讨论】:

      猜你喜欢
      • 2019-08-23
      • 1970-01-01
      • 1970-01-01
      • 2011-08-27
      • 2013-05-05
      • 2014-01-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多