< IPv4 & IPv6 Ping Program Example | RAW Socket Main | IP Header Include Option >


 

 

Raw Sockets 11 Part 3

 

 

What do we have in this chapter 11 part 3?

  1. Traceroute

  2. The Traceroute Program Example

 

Traceroute

 

Another valuable IP networking tool is the Traceroute utility. It allows you to determine the IP addresses of the routers that are traversed to reach a certain host on the network. With Ping, using the record route option in the IPv4 option header also allows you to determine the IPv4 addresses of intermediary routers, but Ping is limited to only 9 hops, the maximum space allocated for addresses in the option header. Also, there is no equivalent option for IPv6. A hop occurs whenever an IP datagram must pass through a router to traverse multiple physical networks.

The idea behind Traceroute is to send a UDP packet to the destination and incrementally change the TTL value. Initially, the TTL value is 1, which means the UDP packet will reach the first router, where the TTL will expire. The expiration will cause the router to generate an ICMP time-exceeded packet. Then the initial TTL value increases by 1, so the UDP packet gets one router farther and an ICMP time-exceeded packet is sent from that router. Collecting each of the ICMP messages gives you a clear path of the IP addresses traversed to reach the endpoint. Once the TTL is incremented enough so that packets actually reach the endpoint, an ICMP port-unreachable message is most likely returned because no process on the recipient is waiting for this message.

Traceroute is a useful utility because it gives you a lot of information about the route to a particular host, which is often a concern when you use multicasting or when you experience routing problems. Fewer applications need to perform Traceroute programmatically than Ping, but certain tasks might require Traceroute-like capabilities.

Two methods can be used to implement the Traceroute program. First, you can use UDP packets and send datagrams, incrementally changing the TTL. Each time the TTL expires, an ICMP message will be returned to you. This method requires one socket of UDP to send the messages and another socket of ICMP to read them. The UDP socket is a normal UDP socket. The ICMP socket is a raw socket, which we already discussed how to create. The TTL of the UDP socket needs to be manipulated via the IP_TTL or IPV6_UNICAST_HOPS socket option. Alternatively, you can create a UDP socket and use the IP_HDRINCL option (discussed later in this chapter) to set the TTL manually within the IP header, but this is quite a lot of work.

The other method is simply to send ICMP packets to the destination, also incrementally changing the TTL. This also results in ICMP error messages being returned when the TTL expires. This method resembles the Ping example because it requires only one socket (of ICMP).

 

The Traceroute Program Example

 

The following program example is a simple Traceroute sample that uses ICMP packets. The traceroute is similar in structure and code to the Ping sample. The only difference is that the TTL value is incremented with each send.

 

Traceroute program example: Creating a new empty Win32 console mode application project

 

Add the following source code.

 

// Sample: IPv4 and IPv6 Trace Route Sample

//

// Files:

//    iphdrv3.h     - IPv4 and IPv6 packet header definitions

//    Ipv4IPv6traceroutesrc.cpp   - this file

//    resolve.cpp   - Common name resolution routine

//    resolve.h     - Header file for common name resolution routines

//

// Description:

//    This sample illustrates how to use raw sockets to send ICMP

//    echo requests and receive their response in order to determine the

//    route to a particular destination. This sample performs

//    both IPv4 and IPv6 trace route operations. When using raw sockets,

//    the protocol value supplied to the socket API is used as the

//    protocol field (or next header field) of the IP packet. Then

//    as a part of the data submitted to sendto, we include both

//    the ICMP request and data. We start by setting the TTL value to one

//    and sending a request. When an intermediate router intercepts the

//    packet the TTL is decremented. If the value is zero, it sends an

//    ICMP TTL expired message which we receive. From the IP packet's

//    source field we have a location in the route to the destination.

//    With each send, the TTL is incremented by one until the specified

//    destination is reached.

//

// Command Line Options/Parameters:

//     Ipv4IPv6traceroute [-a 4|6] [-d] [-h ttl] [-w timeout] [host]

//

//     -a       Address family (IPv4 or IPv6)

//     -d       Do not resolve the addresses to hostnames

//     -h ttl   Maximum TTL value

//     -w time  Timeout in milliseconds to wait for a response

//     host     Hostname or literal address

//

#ifndef WIN32_LEAN_AND_MEAN

#define WIN32_LEAN_AND_MEAN

#endif

 

// Link to ws2_32.lib

#include <winsock2.h>

#include <ws2tcpip.h>

#include <stdio.h>

#include <stdlib.h>

 

#include "resolve.h"

#include "iphdrv3.h"

 

#define DEFAULT_DATA_SIZE      32       // default data size

#define DEFAULT_RECV_TIMEOUT   6000     // six second

#define DEFAULT_TTL            30       // default timeout

 

// Global variables

int   gAddressFamily = AF_UNSPEC,         // Address family to use

      gProtocol      = IPPROTO_ICMP,      // Protocol value

      gTtl           = DEFAULT_TTL,       // Default TTL value

      gTimeout       = DEFAULT_RECV_TIMEOUT; // Amount of data to send

BOOL  bResolve       = TRUE;              // Resolve addresses to host names

char *gDestination   = NULL;              // Destination

 

// Function: usage

// Description:  Print usage information.

int usage(char *progname)

{

    printf("Usage: %s -r <host> [data size]\n", progname);

    printf("       -a 4|6       Address family\n");

    printf("       -d           Do not resolve addresses to hostnames\n");

    printf("       -h ttl       Maximum hops to search for target\n");

    printf("       -w timeout   Wait timeout in milliseconds for each reply\n");

    printf("        host        Remote machine to ping\n");

            printf("\n");

    return 0;

}

 

// Function: InitIcmpHeader

// Description: Helper function to fill in various stuff in our ICMP request.

void InitIcmpHeader(char *buf, int datasize)

{

    ICMP_HDR   *icmp_hdr=NULL;

    char       *datapart=NULL;

 

    icmp_hdr = (ICMP_HDR *)buf;

    icmp_hdr->icmp_type     = ICMPV4_ECHO_REQUEST_TYPE;        // request an ICMP echo

    icmp_hdr->icmp_code     = ICMPV4_ECHO_REQUEST_CODE;

    icmp_hdr->icmp_id       = (USHORT)GetCurrentProcessId();

    icmp_hdr->icmp_checksum = 0;

    icmp_hdr->icmp_sequence = 0;

    icmp_hdr->icmp_timestamp= GetTickCount();

 

    datapart = buf + sizeof(ICMP_HDR);

 

    // Place some junk in the buffer.

    memset(datapart, 'E', datasize);

}

 

// Function: InitIcmp6Header

// Description: Initialize the ICMP6 header as well as the echo request header.

int InitIcmp6Header(char *buf, int datasize)

{

    ICMPV6_HDR          *icmp6_hdr=NULL;

    ICMPV6_ECHO_REQUEST *icmp6_req=NULL;

    char                *datapart=NULL;

 

    // Initialize the ICMP6 header fields

    icmp6_hdr = (ICMPV6_HDR *)buf;

    icmp6_hdr->icmp6_type     = ICMPV6_ECHO_REQUEST_TYPE;

    icmp6_hdr->icmp6_code     = ICMPV6_ECHO_REQUEST_CODE;

    icmp6_hdr->icmp6_checksum = 0;

 

    // Initialize the echo request fields

    icmp6_req = (ICMPV6_ECHO_REQUEST *)(buf + sizeof(ICMPV6_HDR));

    icmp6_req->icmp6_echo_id       = (USHORT)GetCurrentProcessId();

    icmp6_req->icmp6_echo_sequence = 0;

 

    datapart = (char *)buf + sizeof(ICMPV6_HDR) + sizeof(ICMPV6_ECHO_REQUEST);

    memset(datapart, '$', datasize);

 

    return (sizeof(ICMPV6_HDR) + sizeof(ICMPV6_ECHO_REQUEST));

}

 

// Function: checksum

// Description:

//    This function calculates the 16-bit one's complement sum

//    of the supplied buffer (ICMP) header.

USHORT checksum(USHORT *buffer, int size)

{

    unsigned long cksum=0;

 

    while (size > 1)

    {

        cksum += *buffer++;

        size -= sizeof(USHORT);

    }

    if (size)

    {

        cksum += *(UCHAR*)buffer;

    }

    cksum = (cksum >> 16) + (cksum & 0xffff);

    cksum += (cksum >>16);

    return (USHORT)(~cksum);

}

 

// Function: ValidateArgs

// Description: Parse the command line arguments.

void ValidateArgs(int argc, char **argv)

{

    int    i;

 

    for(i=1; i < argc ;i++)

    {

        if ((argv[i][0] == '-') || (argv[i][0] == '/'))

        {

            switch (tolower(argv[i][1]))

            {

                case 'a':        // address family

                    if (i+1 >= argc)

                        usage(argv[0]);

                    if (argv[i+1][0] == '4')

                        gAddressFamily = AF_INET;

                    else if (argv[i+1][0] == '6')

                        gAddressFamily = AF_INET6;

                    else

                        usage(argv[0]);

                    i++;

                    break;

                case 'd':        // Don't resolve addresses

                    bResolve = FALSE;

                    break;

                case 'h':        // Set TTL value

                    if (i+1 >= argc)

                        usage(argv[0]);

                    gTtl = atoi(argv[++i]);

                    break;

                case 'w':        // Timeout in milliseconds for each reply

                    if (i+1 >= argc)

                        usage(argv[0]);

                    gTimeout = atoi(argv[++i]);

                    break;

                default:

                    usage(argv[0]);

                    break;

            }

        }

        else

        {

            gDestination = argv[i];

        }

    }

    return;

}

 

// Function: SetIcmpSequence

// Description: This routine sets the sequence number of the ICMP request packet.

void SetIcmpSequence(char *buf)

{

    ULONG    sequence=0;

 

    sequence = GetTickCount();

    if (gAddressFamily == AF_INET)

    {

        ICMP_HDR    *icmpv4=NULL;

 

        icmpv4 = (ICMP_HDR *)buf;

        icmpv4->icmp_sequence = (USHORT)sequence;

    }

    else if (gAddressFamily == AF_INET6)

    {

        ICMPV6_HDR          *icmpv6=NULL;

        ICMPV6_ECHO_REQUEST *req6=NULL;

 

        icmpv6 = (ICMPV6_HDR *)buf;

        req6   = (ICMPV6_ECHO_REQUEST *)(buf + sizeof(ICMPV6_HDR));

 

        req6->icmp6_echo_sequence = (USHORT)sequence;

    }

}

 

// Function: ComputeIcmp6PseudoHeaderChecksum

// Description:

//    This routine computes the ICMP6 checksum which includes the pseudo

//    header of the IPv6 header (see RFC2460 and RFC2463). The one difficulty

//    here is we have to know the source and destination IPv6 addresses which

//    will be contained in the IPv6 header in order to compute the checksum.

//    To do this we call the SIO_ROUTING_INTERFACE_QUERY ioctl to find which

//    local interface for the outgoing packet.

USHORT ComputeIcmp6PseudoHeaderChecksum(SOCKET s, char *icmppacket, int icmplen, struct addrinfo *dest)

{

    SOCKADDR_STORAGE localif;

    DWORD            bytes;

    char             tmp[65535], *ptr=NULL, proto=0, zero=0;

    int              rc, total, length, i;

 

    // Find out which local interface for the destination

    rc = WSAIoctl(s, SIO_ROUTING_INTERFACE_QUERY, dest->ai_addr,dest->ai_addrlen,

                        (SOCKADDR *)&localif, sizeof(localif), &bytes, NULL, NULL);

    if (rc == SOCKET_ERROR)

    {

        fprintf(stderr, "WSAIoctl() failed with error code %d\n", WSAGetLastError());

        return -1;

    }

    else

        printf("WSAIoctl() is OK!\n");

 

    // We use a temporary buffer to calculate the pseudo header.

    ptr = tmp;

    total = 0;

 

    // Copy source address

    memcpy(ptr, &((SOCKADDR_IN6 *)&localif)->sin6_addr, sizeof(struct in6_addr));

    ptr   += sizeof(struct in6_addr);

    total += sizeof(struct in6_addr);

    // Copy destination address

    memcpy(ptr, &((SOCKADDR_IN6 *)dest->ai_addr)->sin6_addr, sizeof(struct in6_addr));

    ptr   += sizeof(struct in6_addr);

    total += sizeof(struct in6_addr);

    // Copy ICMP packet length

    length = htonl(icmplen);

 

    memcpy(ptr, &length, sizeof(length));

    ptr   += sizeof(length);

    total += sizeof(length);

    // Zero the 3 bytes

    memset(ptr, 0, 3);

    ptr   += 3;

    total += 3;

    // Copy next hop header

    proto = IPPROTO_ICMP6;

 

    memcpy(ptr, &proto, sizeof(proto));

    ptr   += sizeof(proto);

    total += sizeof(proto);

    // Copy the ICMP header and payload

    memcpy(ptr, icmppacket, icmplen);

    ptr   += icmplen;

    total += icmplen;

 

    for(i=0; i < icmplen%2 ;i++)

    {

        *ptr = 0;

        ptr++;

        total++;

    }

    return checksum((USHORT *)tmp, total);

}

 

// Function: ComputeIcmpChecksum

// Description:

//    This routine computes the checksum for the ICMP request. For IPv4 its

//    easy, just compute the checksum for the ICMP packet and data. For IPv6,

//    its more complicated. The pseudo checksum has to be computed for IPv6

//    which includes the ICMP6 packet and data plus portions of the IPv6

//    header which is difficult since we aren't building our own IPv6 header.

void ComputeIcmpChecksum(SOCKET s, char *buf, int packetlen, struct addrinfo *dest)

{

    if (gAddressFamily == AF_INET)

    {

        ICMP_HDR    *icmpv4=NULL;

 

        icmpv4 = (ICMP_HDR *)buf;

        icmpv4->icmp_checksum = 0;

        icmpv4->icmp_checksum = checksum((USHORT *)buf, packetlen);

    }

    else if (gAddressFamily == AF_INET6)

    {

        ICMPV6_HDR  *icmpv6=NULL;

 

        icmpv6 = (ICMPV6_HDR *)buf;

        icmpv6->icmp6_checksum = 0;

        icmpv6->icmp6_checksum = ComputeIcmp6PseudoHeaderChecksum(s, buf, packetlen, dest);

    }

}

 

// Function: PostRecvfrom

// Description: This routine posts an overlapped WSARecvFrom on the raw socket.

int PostRecvfrom(SOCKET s, char *buf, int buflen, SOCKADDR *from, int *fromlen, WSAOVERLAPPED *ol)

{

    WSABUF  wbuf;

    DWORD   flags, bytes;

    int     rc;

 

    wbuf.buf = buf;

    wbuf.len = buflen;

 

    flags = 0;

 

    rc = WSARecvFrom(s, &wbuf, 1, &bytes, &flags, from, fromlen, ol, NULL);

    if (rc == SOCKET_ERROR)

    {

        if (WSAGetLastError() != WSA_IO_PENDING)

        {

            fprintf(stderr, "WSARecvFrom() failed with error code %d\n", WSAGetLastError());

            return SOCKET_ERROR;

        }

    }

    else

            printf("WSARecvFrom() is OK!\n");

 

    return NO_ERROR;

}

 

// Function: AnalyzePacket

// Description:

//    This routines finds the ICMP packet within the encapsulated header and

//    verifies that the ICMP packet is a TTL expired or echo reply message. If not then an error is returned.

int AnalyzePacket(char *buf, int bytes)

{

    int     hdrlen=0, routes=0, rc;

 

    rc = NO_ERROR;

    if (gAddressFamily == AF_INET)

    {

        IPV4_HDR        *v4hdr=NULL;

        ICMP_HDR        *icmphdr=NULL;

 

        v4hdr = (IPV4_HDR *)buf;

        hdrlen = (v4hdr->ip_verlen & 0x0F) * 4;

 

        if (v4hdr->ip_protocol == IPPROTO_ICMP)

        {

            icmphdr = (ICMP_HDR *)&buf[hdrlen];

 

            if ((icmphdr->icmp_type != ICMPV4_TIMEOUT) &&

                    (icmphdr->icmp_type != ICMPV4_ECHO_REPLY_TYPE) &&

                    (icmphdr->icmp_code != ICMPV4_ECHO_REPLY_CODE) )

            {

                printf("Received ICMP message type %d instead of TTL expired!\n", icmphdr->icmp_type);

                rc = SOCKET_ERROR;

            }

        }

    }

    else if (gAddressFamily == AF_INET6)

    {

        IPV6_HDR        *v6hdr=NULL;

        ICMPV6_HDR      *icmp6=NULL;

 

        v6hdr = (IPV6_HDR *)buf;

 

        if (v6hdr->ipv6_nexthdr == IPPROTO_ICMP6)

        {

            icmp6 = (ICMPV6_HDR *)&buf[sizeof(IPV6_HDR)];

 

            if ((icmp6->icmp6_type != ICMPV6_TIME_EXCEEDED_TYPE) &&

                (icmp6->icmp6_code != ICMPV6_TIME_EXCEEDED_CODE) &&

                (icmp6->icmp6_type != ICMPV6_ECHO_REPLY_TYPE) &&

                (icmp6->icmp6_code != ICMPV6_ECHO_REPLY_CODE) )

            {

                printf("Received ICMP6 message type %d instead of TTL expired!\n", icmp6->icmp6_type);

                rc = SOCKET_ERROR;

            }

        }

    }

    return rc;

}

 

// Function: SetTtl

// Description: Sets the TTL on the socket.

int SetTtl(SOCKET s, int ttl)

{

    int     optlevel, option, rc;

 

    rc = NO_ERROR;

    if (gAddressFamily == AF_INET)

    {

        optlevel = IPPROTO_IP;

        option   = IP_TTL;

    }

    else if (gAddressFamily == AF_INET6)

    {

        optlevel = IPPROTO_IPV6;

        option   = IPV6_UNICAST_HOPS;

    }

    else

    {

        rc = SOCKET_ERROR;

    }

 

    if (rc == NO_ERROR)

    {

        rc = setsockopt(s, optlevel, option, (char *)&ttl, sizeof(ttl));

    }

 

    if (rc == SOCKET_ERROR)

    {

        fprintf(stderr, "SetTtl(): setsockopt() failed with error code %d\n", WSAGetLastError());

    }

    else

        printf("SetTtl(): setsockopt() should be fine!\n");

 

    return rc;

}

 

// Function: IsSockaddrEqual

// Description:

//    This routines compares two SOCKADDR structure to determine

//    whether the address portion of them are equal. Zero is returned

//    for equal; non-zero for not equal.

int IsSockaddrEqual(SOCKADDR *sa1, SOCKADDR *sa2)

{

    int rc;

 

    rc = 1;

    if (sa1->sa_family == sa2->sa_family)

    {

        if (sa1->sa_family == AF_INET)

        {

            rc = memcmp(

                    &((SOCKADDR_IN *)sa1)->sin_addr,

                    &((SOCKADDR_IN *)sa2)->sin_addr,

                    sizeof(struct in_addr)

                    );

            rc = rc;

        }

        else if (sa1->sa_family == AF_INET6)

        {

            rc = memcmp(

                    &((SOCKADDR_IN6 *)sa1)->sin6_addr,

                    &((SOCKADDR_IN6 *)sa2)->sin6_addr,

                    sizeof(struct in6_addr)

                    );

            rc = rc;

        }

    }

    return rc;

}

 

// Function: main

// Description:

//    Setup the ICMP raw socket and create the ICMP header. Add

//    the appropriate IP option header and start sending ICMP

//    echo requests to the endpoint. For each send and receive we

//    set a timeout value so that we don't wait forever for a

//    response in case the endpoint is not responding. When we

//    receive a packet decode it.

int main(int argc, char **argv)

{

    WSADATA            wsd;

    WSAOVERLAPPED      recvol;

    SOCKET             s=INVALID_SOCKET;

    char              *icmpbuf=NULL, recvbuf[0xFFFF], hopname[512];

    struct addrinfo   *dest=NULL, *local=NULL;

    SOCKADDR_STORAGE   from;

    DWORD              bytes, flags;

    int                packetlen=0, recvbuflen=0xFFFF, hopbuflen=512,fromlen,notdone, time=0, ttl,rc;

 

    if(argc < 2)

    {

         usage(argv[0]);

         exit(1);

    }

 

    // Load Winsock

    if (WSAStartup(MAKEWORD(2,2), &wsd) != 0)

    {

        printf("WSAStartup() failed with error code %d\n", WSAGetLastError());

        return -1;

    }

    else

        printf("WSAStartup() is pretty fine!\n");

 

    // Parse the command line

    ValidateArgs(argc, argv);

 

    // Resolve the destination address

    dest = ResolveAddress(gDestination, "0", gAddressFamily, 0, 0);

    if (dest == NULL)

    {

        printf("Bad name %s\n", gDestination);

        return -1;

    }

    else

        printf("ResolveAddress() is OK!\n");

 

    gAddressFamily = dest->ai_family;

 

    if (gAddressFamily == AF_INET)

        gProtocol = IPPROTO_ICMP;

    else if (gAddressFamily == AF_INET6)

        gProtocol = IPPROTO_ICMP6;

 

    // Get the bind address

    local = ResolveAddress(NULL, "0", gAddressFamily, 0, 0);

    if (local == NULL)

    {

        printf("Unable to obtain the bind address!\n");

        return -1;

    }

    else

        printf("ResolveAddress() is fine!\n");

 

    // Create the raw socket

    s = socket(gAddressFamily, SOCK_RAW, gProtocol);

    if (s == INVALID_SOCKET)

    {

        printf("socket() failed with error code %d\n", WSAGetLastError());

        return -1;

    }

    else

       printf("socket() is OK!\n");

 

    // Figure out the size of the ICMP header and payload

    if (gAddressFamily == AF_INET)

        packetlen += sizeof(ICMP_HDR);

    else if (gAddressFamily == AF_INET6)

        packetlen += sizeof(ICMPV6_HDR) + sizeof(ICMPV6_ECHO_REQUEST);

 

    // Add in the data size

    packetlen += DEFAULT_DATA_SIZE;

 

    // Allocate the buffer that will contain the ICMP request

    icmpbuf = (char *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, packetlen);

    if (icmpbuf == NULL)

    {

        fprintf(stderr, "HeapAlloc() for ICMP buffer failed with error code %d\n", GetLastError());

        return -1;

    }

    else

        printf("HeapAlloc() for ICMP buffer is OK!\n");

 

    // Initialize the ICMP headers

    if (gAddressFamily == AF_INET)

    {

        InitIcmpHeader(icmpbuf, DEFAULT_DATA_SIZE);

    }

    else if (gAddressFamily == AF_INET6)

    {

        InitIcmp6Header(icmpbuf, DEFAULT_DATA_SIZE);

    }

 

    // Bind the socket -- need to do this since we post a receive first

    rc = bind(s, local->ai_addr, local->ai_addrlen);

    if (rc == SOCKET_ERROR)

    {

        fprintf(stderr, "bind() failed with error code %d\n", WSAGetLastError());

        return -1;

    }

    else

        printf("bind() is OK!\n");

 

    // Setup the receive operation

    memset(&recvol, 0, sizeof(recvol));

    recvol.hEvent = WSACreateEvent();

 

    // Post the first overlapped receive

    fromlen = sizeof(from);

    PostRecvfrom(s, recvbuf, recvbuflen, (SOCKADDR *)&from, &fromlen, &recvol);

 

    printf("\nTraceroute to %s [", gDestination);

    PrintAddress(dest->ai_addr, dest->ai_addrlen);

    printf("]\nover a maximum of %d hops\n\n", gTtl);

 

    ttl = 1;

 

    // Start sending the ICMP requests

    do

    {

        notdone = 1;

 

        SetTtl(s, ttl);

 

        // Set the sequence number and compute the checksum

        SetIcmpSequence(icmpbuf);

        ComputeIcmpChecksum(s, icmpbuf, packetlen, dest);

        // Send the ICMP echo request

        time = GetTickCount();

        rc = sendto(s, icmpbuf, packetlen, 0, dest->ai_addr, dest->ai_addrlen);

        if (rc == SOCKET_ERROR)

        {

            fprintf(stderr, "sendto() failed with error code %d\n", WSAGetLastError());

            return -1;

        }

        else

            printf("sendto() is OK\n");

 

        // Wait for a response

        rc = WaitForSingleObject((HANDLE)recvol.hEvent, gTimeout);

        if (rc == WAIT_FAILED)

        {

            fprintf(stderr, "WaitForSingleObject() failed with error code %d\n", GetLastError());

            return -1;

        }

        else if (rc == WAIT_TIMEOUT)

        {

            printf("Request timed out.\n");

        }

        else

        {

            printf("WaitForSingleObject() should be fine lol!\n");

            // Check for an error

            rc = WSAGetOverlappedResult(s, &recvol, &bytes, FALSE, &flags);

            if (rc == FALSE)

            {

                fprintf(stderr, "WSAGetOverlappedResult() failed with error code %d\n", WSAGetLastError());

            }

            else

                printf("WSAGetOverlappedResult() looks OK!\n");

 

            time = time - GetTickCount();

            WSAResetEvent(recvol.hEvent);

            // See if we got an ICMP ttl expired or echo reply, if not ignore and receive again.

            if (AnalyzePacket(recvbuf, bytes) == NO_ERROR)

            {

                if (bResolve)

                {

                    if(ReverseLookup((SOCKADDR *)&from, fromlen, hopname, hopbuflen) != NO_ERROR)

                    {

                            printf("ReverseLookup() failed lor!\n");

                     }

                     else

                     {

                           printf("TTL:%d   Time:%d ms   Hop name: %s [", ttl, time, hopname);

                           PrintAddress((SOCKADDR *)&from, fromlen);

                           printf("]\n");

                      }

                }

                else

                {

                    printf("TTL:%d   Time:%d ms   ", ttl, time);

                    PrintAddress((SOCKADDR *)&from, fromlen);

                    printf("\n");

                }

                // See if the response is from the desired destination

                notdone = IsSockaddrEqual(dest->ai_addr, (SOCKADDR *)&from);

                // Increment the TTL

                ttl++;

            }

            // Post another receive

            if (notdone)

            {

                fromlen = sizeof(from);

                PostRecvfrom(s, recvbuf, recvbuflen, (SOCKADDR *)&from, &fromlen, &recvol);

            }

        }

        Sleep(1000);

    } while ((notdone) && (ttl < gTtl));

 

    // Cleanup

    printf("Cleaning up stage...\n");

    freeaddrinfo(dest);

    freeaddrinfo(local);

    if (s != INVALID_SOCKET)

        closesocket(s);

    HeapFree(GetProcessHeap(), 0, icmpbuf);

    WSACleanup();

    return 0;

}

 

Add the iphdrv3.h header file.

 

-------------------------------------------------------------

Traceroute program example: Adding the iphdrv3.h header file to the project

 

Add the source code.

 

// Sample: Protocol header definitions used by trace route (ICMP raw sockets)

// Files:

//      iphdrv3.h     - this file

// Description:

//      This routine contains protocol header structure definitions used by

//      the trace route sample.

//

// Set alignment boundary to 1 byte

#include <pshpack1.h>

 

// IPv4 header

typedef struct ip_hdr

{

    unsigned char  ip_verlen;        // 4-bit IPv4 version, 4-bit header length (in 32-bit words)

    unsigned char  ip_tos;           // IP type of service

    unsigned short ip_totallength;   // Total length

    unsigned short ip_id;            // Unique identifier

    unsigned short ip_offset;        // Fragment offset field

    unsigned char  ip_ttl;           // Time to live

    unsigned char  ip_protocol;      // Protocol(TCP,UDP etc)

    unsigned short ip_checksum;      // IP checksum

    unsigned int   ip_srcaddr;       // Source address

    unsigned int   ip_destaddr;      // Source address

} IPV4_HDR, *PIPV4_HDR, FAR * LPIPV4_HDR;

 

// IPv4 option header

typedef struct ipv4_option_hdr

{

    unsigned char   opt_code;           // option type

    unsigned char   opt_len;            // length of the option header

    unsigned char   opt_ptr;            // offset into options

    unsigned long   opt_addr[9];        // list of IPv4 addresses

} IPV4_OPTION_HDR, *PIPV4_OPTION_HDR, FAR *LPIPV4_OPTION_HDR;

 

// ICMP header

typedef struct icmp_hdr

{

    unsigned char   icmp_type;

    unsigned char   icmp_code;

    unsigned short  icmp_checksum;

    unsigned short  icmp_id;

    unsigned short  icmp_sequence;

    unsigned long   icmp_timestamp;

} ICMP_HDR, *PICMP_HDR, FAR *LPICMP_HDR;

 

// IPv6 protocol header

typedef struct ipv6_hdr

{

    unsigned long   ipv6_vertcflow;        // 4-bit IPv6 version, 8-bit traffic class, 20-bit flow label

    unsigned short  ipv6_payloadlen;       // payload length

    unsigned char   ipv6_nexthdr;          // next header protocol value

    unsigned char   ipv6_hoplimit;         // TTL

    struct in6_addr ipv6_srcaddr;          // Source address

    struct in6_addr ipv6_destaddr;         // Destination address

} IPV6_HDR, *PIPV6_HDR, FAR * LPIPV6_HDR;

 

// IPv6 fragment header

typedef struct ipv6_fragment_hdr

{

    unsigned char   ipv6_frag_nexthdr;

    unsigned char   ipv6_frag_reserved;

    unsigned short  ipv6_frag_offset;

    unsigned long   ipv6_frag_id;

} IPV6_FRAGMENT_HDR, *PIPV6_FRAGMENT_HDR, FAR * LPIPV6_FRAGMENT_HDR;

 

// ICMPv6 header

typedef struct icmpv6_hdr {

    unsigned char   icmp6_type;

    unsigned char   icmp6_code;

    unsigned short  icmp6_checksum;

} ICMPV6_HDR;

 

// ICMPv6 echo request body

typedef struct icmpv6_echo_request

{

    unsigned short  icmp6_echo_id;

    unsigned short  icmp6_echo_sequence;

} ICMPV6_ECHO_REQUEST;

 

// Define the UDP header

typedef struct udp_hdr

{

    unsigned short src_portno;       // Source port no.

    unsigned short dst_portno;       // Dest. port no.

    unsigned short udp_length;       // Udp packet length

    unsigned short udp_checksum;     // Udp checksum (optional)

} UDP_HDR, *PUDP_HDR;

 

#define IP_RECORD_ROUTE     0x7

 

// ICMP6 protocol value

#define IPPROTO_ICMP6       58

 

// ICMP types and codes

#define ICMPV4_ECHO_REQUEST_TYPE   8

#define ICMPV4_ECHO_REQUEST_CODE   0

#define ICMPV4_ECHO_REPLY_TYPE     0

#define ICMPV4_ECHO_REPLY_CODE     0

 

#define ICMPV4_DESTUNREACH    3

#define ICMPV4_SRCQUENCH      4

#define ICMPV4_REDIRECT       5

#define ICMPV4_ECHO           8

#define ICMPV4_TIMEOUT       11

#define ICMPV4_PARMERR       12

 

// ICMP6 types and codes

#define ICMPV6_ECHO_REQUEST_TYPE   128

#define ICMPV6_ECHO_REQUEST_CODE   0

#define ICMPV6_ECHO_REPLY_TYPE     129

#define ICMPV6_ECHO_REPLY_CODE     0

#define ICMPV6_TIME_EXCEEDED_TYPE  3

#define ICMPV6_TIME_EXCEEDED_CODE  0

 

// Restore byte alignment to compile default

#include <poppack.h>

 

Add the resolve.h and its definition, resolve.cpp that were created in the previous program example.

 

Traceroute program example: Copying the header and its definition files.

 

 

Traceroute program example: Pasting the header and its definition files.

 

 

Traceroute program example: Adding the header and its definition files to the existing project.

 

 

Traceroute program example: Selecting the header and its definition files.

 

Traceroute program example: The added header and its definition files seen in Solution explorer.

 

Build and run the project.

 

Traceroute program example: A sample output without any argument

 

Ipv4IPv6traceroute -a 4 -d -h 100 -w 100 www.google.com

 

Traceroute program example: A sample output with IPv4 and some other arguments

 

Ipv4IPv6traceroute -a 6 -d -h 100 -w 200 www.ipv6.org

 

Traceroute program example: A sample output with IPv6 and some other arguments

 

 

 


< IPv4 & IPv6 Ping Program Example | RAW Socket Main | IP Header Include Option >