6
votes

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?

3

3 Answers

2
votes

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).

2
votes

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

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