Creating a Multi-Threaded Trace Route

.

Environment: VC6, NT4/Win2K—should work on any 32-bit Windows operating system that has Winsock2 libraries.

Overview

Sample code for a network "ping" is relatively easy to find. CodeGuru has several good examples and the SDK sample code has another good example. So what about trace route? With a little research I discovered that doing a trace route is really no more difficult than a ping. With a few minor modifications to the ping sample, it is possible to do a multi-threaded trace route. Trace route is really just a ping with the "time-to-live" option set to any number from 1 to the number of hops it takes to get to the destination. So, if it takes 10 hops to get to the destination and the "time-to-live" option is set to 5, the IP address that gets returned is the address of the fifth router along the way. The logical question is: "How many hops does it take?" Well, you don't really know until you get there. Basically, you just increment the time-to-live option until the returned IP address matches the destination IP address. The router names can be obtained by a simple call to "gethostbyaddr".

To call this code "multi-threaded" is a bit of a misnomer. The execution of the pings and ultimately the trace route happens in a single thread; the user interface is in a separate thread. All that is accomplished by this approach is that the user may stop the processing and view updates asynchronously.

This code also relies heavily on the Winsock-2 library (ws2_32.lib).

An Outline of the Approach

  1. This process depends on three variables: destination address, returned address (coming back from ping), and ttl (time-to-live).
  2. Get the destination address by calling "gethostbyname". Set the returned address to 0 and ttl to 1.
  3. While the returned address is not equal to the destination address, do the following: call ping, increment the ttl, and get the name of the returned address.

The Sample Code, Implementing the Approach

int TRThread::RunThread(void)
{
  int result = 0; 

  ClearAllItems();
  InitSockLibrary();

  struct hostent *hHost;
  struct sockaddr_in sin;
 
  // get the IP address of the destination
  HOSTENT * ent = gethostbyname(m_address);
  if (CheckKillFlag())
  {
    return result;
  }

  if (ent && ent->h_addrtype == AF_INET)
  {
    in_addr * final_ptr = (struct in_addr*)ent->h_addr_list[0];
    unsigned long ipfinal = final_ptr->S_un.S_addr;

    // initialize returned IP address and time-to-live (ttl)
    int ttl = 1;
    unsigned long ipback = 0;
    unsigned long ms = 0;

    while (ipback != ipfinal){
      hHost = 0; TRDeviceItem * item = new TRDeviceItem;

      // By checking the "kill flag" often, we are trying to
      // provide a responsive and well behaved worker thread.
        if (CheckKillFlag())
        {
          break;
        }

      // send a ping to the destination address, knowing that it
      // will only make it thru (ttl) routers before getting sent
      // back.
        if (Ping(m_address,ttl,ipback,ms))
        {
          sin.sin_family = AF_INET;
          sin.sin_addr.S_un.S_addr = ipback;

        // get the name of the router that bounced the ping back
        // (ipback)
        hHost = gethostbyaddr((char*)&sin.sin_addr, 4, PF_INET);

        // set up an item for the linked list
        item->SetIPAddress((unsigned long)ipback);
        item->SetResponseTime(ms);

        // if the router information is not null, extract the
        // router name
        if (hHost)
        {
          item->SetName(hHost->h_name);
        }
      }
      ttl++;
      if (CheckKillFlag())
      {
        break;
      }

      // synchronize block, locking access to linked list before
      // changing anything
      Wait();
      {
        // add the item to the linked list
        if (m_first_item == 0)
        {
          m_first_item = m_current_item = item;
        }
        else
        {
          m_current_item->SetNextItem(item);
          m_current_item = item;
        }

        m_total_items++;
        result++;
      }
      Release();

      // The base class (WorkerThread) stores a synchronized
      // variable usually used to calculate the percentage of
      // work done. This time we are using that variable to
      // store the number of hops so far.
      SetTotal(m_total_items); // If we're over the maximum number
                               // of hops, let's break outta here.
      if (ttl > MAX_HOPS)
      {
        break;
      }
    }
  }

  return result;
}

Some Information about the Sample Project

The sample project is basically an SDI application with a ListView. The trace route executes in a thread class derived from WorkerThread and the view class uses a timer to check the thread for updates. To execute the program, simply type in a Web site or Internet address and click Trace.

Downloads

Download demo project - 31 Kb



About the Author

Andy McGovern

Andy McGovern - Software Developer/Engineer. Special interests: astronomy, image processing, orbital mechanics, mathematics. Currently work at the Johns Hopkins University Applied Physics Laboratory on the science operations team for the CRISM instrument on the Mars Reconnaissance Orbiter.