Click here to Skip to main content
15,881,803 members
Articles / Desktop Programming / MFC

Traceroute - Using RAW Socket & UDP

Rate me:
Please Sign up or sign in to vote.
4.67/5 (12 votes)
21 May 20076 min read 84.8K   5.1K   46  
Traceroute using RAW Socket and UDP. Raw UDP packets with increasing TTL values are targetted at strange port numbers. The resulting ICMP responses from intermediate hosts contain host identity in the form of its IP address.
/* Traceroute using raw udp packets with ICMP as failover
   By Vinod Vijayan 
   
	 Updation: 
	 15 May 07 Changed the hardcoded hostname(left by mistake) by calling gethostname.
	 16 May 07 Changed the return value on error condition to 1
	           Added check for error in sending packets that may be caused by permission issues(for raw socket) in XP.
			   Changed title from "Raw UDP Traceroute with ICMP as failover" to  
			   "Traceroute - Using RAW Socket & UDP".
			   Changed name of structure packetinfo to traceinfo

 */
#include "stdafx.h"
#include <winsock2.h>
#include <ws2tcpip.h>

// PseudoHeader for calculation of UDP checksum
typedef struct _PseudoHeader  
{
	unsigned int	SourceAddress;
	unsigned int	DestinationAddress;
	unsigned char	Zeros;
	unsigned char	Proto;
	unsigned short	Length;
} PseudoHeader;

typedef PseudoHeader FAR * LPPseudoHeader;

// Structure for IPV4 header
typedef struct _IPHeader
{
    unsigned char  ip_header_len:4;  // 4-bit header length (in 32-bit words) normally=5 (Means 20 Bytes may be 24 also)
    unsigned char  ip_version   :4;  // 4-bit IPv4 version
    unsigned char  ip_tos;           // IP type of service
    unsigned short ip_total_length;  // Total length
    unsigned short ip_id;            // Unique identifier
	unsigned short ip_frag_offset;   // Fragment offset field
	unsigned char  ip_ttl;           // Time to live
    unsigned char  ip_protocol;      // Protocol(TCP,UDP,ICMP etc)
    unsigned short ip_checksum;      // IP checksum
    unsigned int   ip_srcaddr;       // Source address
    unsigned int   ip_destaddr;      // Destination address
} IPHeader;

// Structure for ICMP header
typedef struct icmp_hdr    
{
    unsigned char  type;             // ICMP Error type
    unsigned char  code;             // Type sub code
    unsigned short checksum;
    unsigned short id;
    unsigned short seq;
} ICMP_HDR;

// Structure for UDP header
typedef struct udp_hdr
{
	unsigned short source_port;      // Source port no.
	unsigned short dest_port;        // Dest. port no.
	unsigned short udp_length;       // Udp packet length
	unsigned short udp_checksum;     // Udp checksum (optional)
} UDP_HDR;

// Structure for UDP payload packet
typedef struct udppacket
{
	UDP_HDR  udp;                    // UDP header
	char     messg[35];              // message
} UDP_PACKET;

// Structure for ICMP payload packet
typedef struct icmppacket
{
	ICMP_HDR icmp;                   // ICMP header
	DWORD	dwTime;                  // time
	char	cData[35];               // message
} ICMP_PACKET;

// Structure for storing information regarding a hop trace
typedef struct traceinfo
{
	int packetid;
	int ttl;
	int proto;
	int size;
	unsigned long saddr;
	unsigned long daddr;
} TRACE_INFO;


static int ICMP_SEQNO;
const  int UDP_DEST_PORTNO     = 33434;   // 33434 to 33534
const  int FALLBACK_TO_ICMP    = 1;       // Send ICMP echo requests if UDP fails
int    DESTINATION_UNREACHABLE = 0;
int    MAX_CONQ_ICMP_TIMEOUT   = 2;       // Maximum consequtive ICMP hops that are allowed to tuneout
int    latency;

// Generic code to calculate the checksum of a packet
unsigned short CalculateChecksum(unsigned short *usBuf, int iSize)
{
	unsigned long usChksum=0;
	//Calculate the checksum
	while (iSize>1)
	{
		usChksum+=*usBuf++;
		iSize-=sizeof(unsigned short);
	}

	//If we have one char left
	if (iSize)
		usChksum+=*(unsigned char*)usBuf;

	//Complete the calculations
	usChksum=(usChksum >> 16) + (usChksum & 0xffff);
	usChksum+=(usChksum >> 16);

	//Return the value (inversed)
	return (unsigned short)(~usChksum);
}

// Calculate UDP checksum using pseudo header
unsigned short CalculatePseudoChecksum(char *buf, int BufLength,unsigned long  dest,unsigned long  src)
{
	//Calculate the checksum
	LPPseudoHeader lpPseudo;
	int PseudoHeaderLength = sizeof(PseudoHeader);

	lpPseudo                     = new PseudoHeader;
	lpPseudo->DestinationAddress = dest;
	lpPseudo->SourceAddress      = src;
	lpPseudo->Zeros              = 0;
	lpPseudo->Proto              = 0x11;
	lpPseudo->Length             = htons(BufLength);

	//Calculate checksum of all
	int iTotalLength;
	iTotalLength                 = PseudoHeaderLength+BufLength;

	char* tmpBuf;
	tmpBuf=new char[iTotalLength];

	//Copy pseudo
	memcpy(tmpBuf,lpPseudo,PseudoHeaderLength);

	//Copy header
	memcpy(tmpBuf+PseudoHeaderLength,buf,BufLength);

	//Calculate the checksum
	unsigned short usChecksum;
	usChecksum                   = CalculateChecksum((unsigned short*)tmpBuf,iTotalLength);

	//Delete all
	delete tmpBuf;
	delete lpPseudo;

	//Return checksum
	return usChecksum;
}

// DNS lookup to get the IP address of the destination or host
int gethostaddr( char* pszHost,sockaddr_in& dest)
{
    // Initialize the destination host info block
    memset( &dest, 0, sizeof( dest ) );

    // Turn first passed parameter into an IP address to ping
    unsigned int addr = inet_addr( pszHost );
    //if its quad Address then OK
    if( addr != INADDR_NONE )
    {
        // It was a dotted quad number, so save result
        dest.sin_addr.s_addr	= addr;
        dest.sin_family			= AF_INET;

    }
    else
	{
        // Not in dotted quad form, so try and look it up
        hostent* hp = gethostbyname( pszHost );
        if ( hp != 0 )
		{
            // Found an address for that host, so save it
            memcpy( &(dest.sin_addr), hp->h_addr, hp->h_length );
            dest.sin_family = hp->h_addrtype;
        }
        else
		{
            // Not a recognized hostname either!
            printf("Failed to resolve %s\n", pszHost);
            return -1;
        }
    }

    return 0;
}

// If the socket is readible then there is a response which can be read.
// If not readible then after timeout reurn no response status
int IsSocketReadible(SOCKET &s)
{
	struct timeval Timeout;
	fd_set readfds;

	readfds.fd_count = 1;
	readfds.fd_array[0] = s;
	Timeout.tv_sec = 1;
    Timeout.tv_usec = 0;

	return(select(1, &readfds, NULL, NULL, &Timeout));
}

//  Returns 1 on reaching destination, else returns 0
int ProcessIpPacket (IPHeader *iphdr, unsigned long dest)
{
	unsigned short iphdrlen;
	sockaddr_in source;
	iphdrlen = iphdr->ip_header_len*4;
	static no_hops;
	
	memset(&source, 0, sizeof(source));
	source.sin_addr.s_addr = iphdr->ip_srcaddr;
	// Destination unreachable is sent by the host that just previously sent an
	// ICMP time exceeded so don't print its information again
    if(!DESTINATION_UNREACHABLE || (source.sin_addr.s_addr == dest))
	printf("\n %2d  %4d ms  %15s \n", ++no_hops, latency, inet_ntoa(source.sin_addr));
	
    /* Another way is to check for ICMP echo reply but this would only be sent 
	   for ICMP echo requests */
	if(source.sin_addr.s_addr == dest)   // Destination reached 
		return(1);

	return(0);
}

// Process the ICMP response. Extract the IP header and other information
int ProcessIcmpPacket(char* Buffer , int Size, unsigned long dest)
{
	unsigned short iphdrlen;
	int dest_reached = -1;	
	IPHeader *iphdr  = (IPHeader *)Buffer;
	iphdrlen         = iphdr->ip_header_len*4;
    ICMP_HDR *icmpheader=(ICMP_HDR*)(Buffer+iphdrlen);

	if((unsigned int)(icmpheader->type)==11)
		/*printf("  (TTL Expired)\n")*/;
	else if((unsigned int)(icmpheader->type)==0) 
		/*printf("  (ICMP Echo Reply)\n")*/;
	else if((unsigned int)(icmpheader->type)==3)
		DESTINATION_UNREACHABLE = 1;

	dest_reached     = ProcessIpPacket(iphdr,dest);
	
	return dest_reached;
}

// Find the response protocol
int ProcessPacket(char* Buffer, int size, unsigned long dest)
{
	IPHeader *ip     = (IPHeader *)Buffer;
	int dest_reached = -1;

	switch (ip->ip_protocol) //Check the Protocol and do accordingly...
	{
		case 1:  //ICMP Protocol
			dest_reached = ProcessIcmpPacket(Buffer,size,dest);
			break;
		
		case 2:  //IGMP Protocol
			break;
		
		case 6:  //TCP Protocol
			break;
		
		case 17: //UDP Protocol
			break;
		
		default: //Some Other Protocol like ARP etc.
			break;
	}

	return dest_reached;
	
}

// Receive response from a readible socket and process it
int processResponse(SOCKET &s, unsigned long dest)
{
	char Buffer[1500];
	int  mangobyte;
	int  dest_reached = -1;
	
	if (Buffer == NULL)
	{
		printf("malloc() failed.\n");
		return dest_reached;
	}

	 mangobyte = recvfrom(s,Buffer,sizeof(Buffer),0,0,0); 

	if(mangobyte > 0)	
		dest_reached = ProcessPacket(Buffer, mangobyte, dest);
	else 
	{
		printf( "recvfrom() failed.\n");
	}
	return dest_reached;
}


/*void getSourceDestAddr(u16 *src_addr,u16 *dest_addr,sockaddr_in dest,sockaddr_in src)
{
    int i=0;

	src_addr[i]  =  src.sin_addr.S_un.S_un_b.s_b1;
	dest_addr[i++] = dest.sin_addr.S_un.S_un_b.s_b1;

	src_addr[i]  =  src.sin_addr.S_un.S_un_b.s_b2;
	dest_addr[i++] = dest.sin_addr.S_un.S_un_b.s_b2;

	src_addr[i]  =  src.sin_addr.S_un.S_un_b.s_b3;
	dest_addr[i++] = dest.sin_addr.S_un.S_un_b.s_b3;

	src_addr[i]  =  src.sin_addr.S_un.S_un_b.s_b4;
	dest_addr[i] = dest.sin_addr.S_un.S_un_b.s_b4;

}*/

// Construct the IP header for the trace
void construct_ip_hdr(char *pack_data,TRACE_INFO trace)
{
	//Clear the data buffer
	memset(pack_data,0,trace.size);
	int size = sizeof(pack_data);

	IPHeader *ip           = (IPHeader *)(pack_data);

	ip->ip_version         = 4;
	ip->ip_header_len      = 5;     // in words (size is always 20 bytes for IPv4)
	ip->ip_tos             = 0x0;
	ip->ip_total_length    = htons(trace.size);
	ip->ip_frag_offset     = 0x0;
	ip->ip_protocol        = trace.proto; 
	ip->ip_destaddr        = trace.daddr;
	ip->ip_srcaddr         = trace.saddr;
	ip->ip_checksum        = 0;    // Don't worry, Kernel will do the needful
	ip->ip_id              = htons(trace.packetid);
	ip->ip_ttl             = trace.ttl;
}


/*
  First construct the IP header.
  Then construct the protocol packet
*/
int construct_proto_pack(char *packdata,TRACE_INFO trace)
{
  IPHeader *ip        = (IPHeader *)(packdata); 
  int packet_length   = -1;
  static portincr     = 0;

  construct_ip_hdr(packdata,trace);

  if(trace.proto == IPPROTO_UDP)
  {
	  //struct udp_hdr *udp           = (struct udp_hdr *)(ip + 1);
	  if(portincr > 100)       // 33434 to 33534
		  portincr = 0;

      UDP_PACKET *udp_pack          = (UDP_PACKET *) (ip + 1);

      strcpy(udp_pack->messg, "Hello there!");
	  udp_pack->udp.dest_port       = htons(UDP_DEST_PORTNO + portincr++);
	  udp_pack->udp.source_port     = htons(0);
	  udp_pack->udp.udp_checksum    = 0; 
	  udp_pack->udp.udp_length      = htons(sizeof(*udp_pack));

	  // UDP Packet length = UDP header len + data len
	  int udp_hdr_n_data_len        = sizeof(*udp_pack);
	  packet_length                 = ((int)(ip + 1)  - (int)ip)    + udp_hdr_n_data_len;

	  ip->ip_total_length           = packet_length;

	  udp_pack->udp.udp_checksum    = CalculatePseudoChecksum((char *)udp_pack,udp_hdr_n_data_len,trace.daddr,trace.saddr);
  }
  else if(trace.proto == IPPROTO_ICMP)
  {
	  //struct icmp_hdr *icmp         = (struct icmp_hdr *)(ip + 1);
	  ICMP_PACKET     *icmp_pack    = (ICMP_PACKET *) (ip + 1);

	  icmp_pack->icmp.code          = 0;
	  icmp_pack->icmp.type          = 8;
	  icmp_pack->icmp.checksum      = 0;
	  icmp_pack->icmp.id            = ICMP_SEQNO;
	  icmp_pack->icmp.seq           = ICMP_SEQNO++;
      icmp_pack->dwTime             = GetTickCount();
      strcpy(icmp_pack->cData, "Hello there!");

	  // ICMP Packet length = ICMP header len + data len
	  int icmp_hdr_n_data_len       = sizeof(*icmp_pack);
      packet_length                 = ((int)(ip + 1)  - (int)ip)     + icmp_hdr_n_data_len;

	  ip->ip_total_length           = packet_length;

	  icmp_pack->icmp.checksum      = CalculateChecksum((USHORT *)icmp_pack,icmp_hdr_n_data_len);
  }
  else
	  return (-1);   // Protocol not supported

  return(packet_length);
}

// Populate the packinfo variable with parameters for a particular hop
void settraceinfo(TRACE_INFO &traceinfo,int packetid,int ttl,int proto,unsigned long dest, unsigned long src, int size)
{
	traceinfo.packetid = packetid; 
	traceinfo.ttl      = ttl;
	traceinfo.proto    = proto;
	traceinfo.saddr    = src;
	traceinfo.daddr    = dest;
	traceinfo.size     = size;
}

int main(int argc, char* argv[])
{
  sockaddr_in dest, src;
  TRACE_INFO traceinfo;
  char data[100];
  char hostname[70];

  ICMP_SEQNO      = 1;
  
  // Start Winsock up
  WSAData wsaData;

  int char_size = sizeof(unsigned char);

  if(!(argc == 2))
  {
	  printf("Usage: %s %s \n",argv[0],"target_name");
	  return 1;
  }

  if (WSAStartup(MAKEWORD(2, 1), &wsaData) != 0) 
  {
     printf("Failed to find Winsock 2.1 or better");
     return 1;
  }
	
  // Create a raw IP socket so that it can send/capture UDP and ICMP packets
  SOCKET sock = socket(AF_INET, SOCK_RAW,IPPROTO_IP);
  

  if(sock == INVALID_SOCKET) {
   printf("\nCouldn't create socket");
   return 1;
  }

  // provide our own IP header and not let the kernel provide one  

  int on = 1;
  if (setsockopt(sock, IPPROTO_IP, IP_HDRINCL, (const char *)&on, sizeof(on)) == SOCKET_ERROR) {
    printf("\nCouldn't set IP_HDRINCL");
    return 1;
  }

  if(gethostaddr(argv[1],dest) < 0)     // destination domain name or IP address
  {
	printf("\n Couldn't resolve host %s \n", argv[1]);
    return 1;
  }

   //Retrive the local hostname
  if (gethostname(hostname, sizeof(hostname)) == SOCKET_ERROR) 
  {
     printf("\nCannot retrieve hostname");
	 return 1;
  }


  if(gethostaddr(hostname,src) < 0)               // Hostname  or IP address
  {
    return 1;
  }

  int packetid           = GetCurrentProcessId();
  int written            = 0;
  int proto              = IPPROTO_UDP;    // UDP ping
  int pack_size          = sizeof(data);
  int sockreadible       = 0;
  int dest_reached       = 0;
  int count              = 0;
  int timed_out_cnt      = 0;
  ULONG RequestTime      = 0;
  ULONG ResponseTime     = 0;

  printf("\n Tracing route to %s [%s] \n", argv[1], inet_ntoa(dest.sin_addr));
  
  while(dest_reached == 0)
  {
	  settraceinfo(traceinfo, packetid + count, count+1, proto, dest.sin_addr.s_addr, src.sin_addr.s_addr, pack_size);
	  if((pack_size = construct_proto_pack(data, traceinfo)) > 0)
	  {
		  RequestTime      = GetTickCount();
		  written          = sendto(sock,data,pack_size,0,(struct sockaddr *)&dest,sizeof(dest));

		  if (written == SOCKET_ERROR) 
		  {
			  printf("\n Sending packet failed. Check permissions on this system");
			  printf("\n Admin rights are required on XP");
			  return 1;
		  }

		  sockreadible     = IsSocketReadible(sock);
		  
		  if(sockreadible)
		  {
			  timed_out_cnt = 0;
			  ResponseTime  = GetTickCount();
			  latency       = ResponseTime - RequestTime;
			  dest_reached  = processResponse(sock,dest.sin_addr.s_addr);

			  // If destination is unreachable then switch to ICMP
			  if((DESTINATION_UNREACHABLE && (proto != IPPROTO_ICMP) && FALLBACK_TO_ICMP)) {
				  proto                   = IPPROTO_ICMP;
				  DESTINATION_UNREACHABLE = 0;
			  }
			  else if (DESTINATION_UNREACHABLE){
				  printf("\n Destination unreachable, so cannot proceed further");
				  return 1;
			  }

			  count ++;
		  }
		  else if(written > 0 && proto == IPPROTO_ICMP)
		  {
			  //request timed out even with ICMP packets then stop after MAX_CONQ_ICMP_TIMEOUT hops
			  timed_out_cnt ++;
			  // If after consequtive MAX_CONQ_ICMP_TIMEOUT hops, request has timed out then stop
			  if(timed_out_cnt >= MAX_CONQ_ICMP_TIMEOUT) {
			    printf("\n Request has timed out even after %d hops, so not proceeding further\n",MAX_CONQ_ICMP_TIMEOUT);
			    return 1;
			  }
			  else {
			   printf("\n Request timed out");
               count ++;
			  }
			  
		  }
		  else if(written > 0 && FALLBACK_TO_ICMP)
		  {
              // Try again with ICMP if UDP has failed for the ttl
			  if(proto != IPPROTO_UDP)
				  count ++;
			  proto          = IPPROTO_ICMP; // Don't go with UDP hereafter
		  }
		  else if(FALLBACK_TO_ICMP == 0)
			  count ++;

	  }
  }
	  
  closesocket(sock);
  WSACleanup();
  return 0;
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Technical Lead
India India
Likes painting and photography too.

Comments and Discussions