用 C 语言编写基本的路由跟踪脚本

发布于 2024-10-15 13:20:53 字数 380 浏览 12 评论 0原文

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

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

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

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

I have to write a trceroute script but I'm not sure if my attempts are correct.

Right now I'm doing it like that (please correct me if I'm doing wrong or clumsy):

  1. Got an struct for ip- and udpheader
  2. A checksum function
  3. Opening 2 sockets: One for sending UDP-packets in SOCK_RAW mode (to manipulate ttl) and one to receive ICMP-answers from the routers.
  4. Using sendto() to send UDP packet
  5. Having no clue how to receive and process an ICMP answer

Are there any more comfortable ways to change the TTL than using sock_raw where I have to define all header stuff by myself?
What parameters should I use for socket() when opening ICMP sock?
How to receive the ICMP answer?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

停顿的约定 2024-10-22 13:20:53

您的目标平台是什么?这是来自 OpenBSD 源代码的 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)

What platform are you targeting? Here's a BSD flavor from OpenBSD source:

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");

On Linux, I believe, you need to use IP_RECVERR and recvmsg(2) with the MSG_ERRQUEUE, see ip(7).

各自安好 2024-10-22 13:20:53

至于设置 TTL,您可以使用 setsockopt()< /a>.以下是 Linux 上 ping iputils 源代码的摘录:

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);
}

As far as setting the TTL is concerned, you can use setsockopt(). Here's an extract from the iputils' source for ping on Linux:

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);
}
自在安然 2024-10-22 13:20:53

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

  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)

我电脑中的结果

I met the same problem and solved it.
You need to

  1. create a new socket using ICMP protocol
  2. bind to a specific port like 33434
  3. receive ICMP reply.

I will show my code.

  // ......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);
    }

and it works on my computer. (Windows 10)

the result in my computer

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文