#include <stdio.h>
#include <stdlib.h>
#include <sys/param.h>
#include <sys/file.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netinet/udp.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <malloc.h>
#include <memory.h>
#include <netdb.h>
#include <string.h>
#include <unistd.h>
//udp packet data payload
struct outdata{
u_char seq;
u_char ttl;
struct timeval tv;
};
typedef struct traceInfo{
char host[128];
int min_ttl;
int max_ttl;
int waittime;
int queries;
int packsize;
int port;
}traceInfo_t;
typedef struct globalInfo{
struct sockaddr_in haddr;
struct sockaddr_in loc_addr;
int sendsock;
int recvsock;
int min_ttl;
int max_ttl;
int waittime;
int queries;
int packlen;
int port;
int ident;
int packet[1024];
}globalInfo_t;
struct sockaddr_in *outip;
struct udphdr *outudp;
struct outdata *outdata;
globalInfo_t g_info;
void tvsub(struct timeval *out, struct timeval *in)
{
if((out->tv_usec -= in->tv_usec) < 0)
{
out->tv_sec = out->tv_sec -1;
out->tv_usec += 1000000;
}
out->tv_sec = out->tv_sec - in->tv_sec;
}
double deltaT(struct timeval *t1p, struct timeval *t2p)
{
double dt;
dt = (double)(t2p->tv_sec - t1p->tv_sec)*1000.0 +
(double)(t2p->tv_usec - t1p->tv_usec)/1000.0;
return dt;
}
int getsockaddr_by_host(struct sockaddr_in *sin, char *host)
{
struct hostent *hent;
struct in_addr addr;
if(!inet_aton(host,&addr))
{
if(!(hent = gethostbyname(host)))
{
printf("gethostbyname error!\n");
return -1;
}
addr = *(struct in_addr *)hent->h_addr;
}
memset(&(sin->sin_addr),0x00,sizeof(sin->sin_addr));
sin->sin_addr = addr;
}
void init_globalinfo(globalInfo_t *ptr, traceInfo_t *traceInfo)
{
if(ptr == NULL || traceInfo == NULL)
return ;
memset(ptr,0,sizeof(*ptr));
ptr->min_ttl = traceInfo->min_ttl;
ptr->max_ttl = traceInfo->max_ttl;
ptr->waittime = traceInfo->waittime;
ptr->queries = traceInfo->queries;
ptr->packlen = traceInfo->packsize;
ptr->port = traceInfo->port;
ptr->ident = (getpid() & 0xffff)|0x8000;
//ptr->haddr = (struct sockaddr_in *)malloc(sizeof(struct sockaddr_in));
//memset(ptr->haddr, 0x00, sizeof(struct sockaddr_in));
getsockaddr_by_host(&(ptr->haddr), traceInfo->host);
ptr->haddr.sin_family = AF_INET;
ptr->haddr.sin_port = ptr->port;
}
void send_probe(globalInfo_t *ptr, int seq, int ttl, struct timeval *tp)
{
int cc;
outdata = (struct outdata *)malloc(sizeof(ptr->packlen));
memset(outdata,0,sizeof(ptr->packlen));
ptr->haddr.sin_port = htons(ptr->port+seq);
outdata->seq = seq;
outdata->ttl = ttl;
outdata->tv = *tp;
if(setsockopt(ptr->sendsock, IPPROTO_IP, IP_TTL,(char *)&ttl,sizeof(ttl)) < 0)
{
printf("set ttl error!\n");
return ;
}
cc = sendto(ptr->sendsock, (char *)outdata, ptr->packlen,0,(struct sockaddr *)&(ptr->haddr),sizeof(ptr->haddr));
if(cc < 0 || cc != ptr->packlen)
{
printf("send packlen error!\n");
fflush(NULL);
return ;
}
}
int wait_for_reply(globalInfo_t *ptr, struct sockaddr_in *from, struct timeval *tp)
{
fd_set fds;
struct timeval now,wait;
struct timezone tz;
int cc = 0;
int sock = ptr->recvsock;
int fromlen = sizeof(*from);
FD_ZERO(&fds);
FD_SET(sock, &fds);
wait.tv_sec = tp->tv_sec + ptr->waittime;
wait.tv_usec = tp->tv_usec;
gettimeofday(&now,&tz);
tvsub(&wait,&now);
if(select(sock+1, &fds, NULL, NULL,&wait) > 0)
cc = recvfrom(sock, (char *)ptr->packet,sizeof(ptr->packet),0,(struct sockaddr *)from, &fromlen);
return cc;
}
int packet_ok(globalInfo_t *ptr,char *buf, int cc, struct sockaddr_in *from,int seq)
{
struct icmp *icmp;
char type, code;
int hlen;
struct ip *ip;
ip = (struct ip *)buf;
hlen = ip->ip_hl << 2;
if(cc < hlen + ICMP_MINLEN)
{
printf("packet too short!!\n");
return -1;
}
cc = cc -hlen;
icmp = (struct icmp *)(buf+hlen);
type = icmp->icmp_type;
code = icmp->icmp_code;
if((type == ICMP_TIMXCEED && code == ICMP_TIMXCEED_INTRANS) ||
type == ICMP_UNREACH || type == ICMP_ECHOREPLY)
{
struct ip *hip;
struct udphdr *hudp;
struct icmp *hicmp;
hip = &(icmp->icmp_ip);
hlen = hip->ip_hl<<2;
hudp = (struct udphdr *)((char *)hip + hlen);
#if 0
printf("this is init!" );
printf("hlen=%d--->cc=%d\n",hlen,cc);
printf("hip->ip_p=%d---->IPPROTO_UDP=%d\n",hip->ip_p,IPPROTO_UDP);
printf("hudp->dest=%d---->htons(ptr->port+seq)=%d\n",
hudp->dest,htons(ptr->port + seq));
#endif
if(hlen +12 <= cc && hip->ip_p == IPPROTO_UDP &&
/*hudp->source == htons(ptr->ident) &&*/ hudp->dest == htons(ptr->port + seq))
return (type == ICMP_TIMXCEED?-1:code+1);
}
return 0;
}
void print(char *buf, int cc, struct sockaddr_in *from )
{
struct ip *ip;
int hlen;
ip = (struct ip *)buf;
hlen = ip->ip_hl << 2;
cc -= hlen;
printf(" %s", inet_ntoa(from->sin_addr));
}
int getByDefaultValue(int value,int min,int max)
{
if( min<value && value < max)
return value;
else if(value > max)
return max;
else if(value < min)
return min;
else
return min;
}
void trace(traceInfo_t *traceInfo)
{
int on = 1;
int ttl,probe;
struct sockaddr_in *from;
int seq = 0;
int code;
int i=0;
int minipacket,packlen;
if(traceInfo == NULL)
return ;
init_globalinfo(&g_info,traceInfo);
minipacket = sizeof(*outip) + sizeof(outdata);
packlen = minipacket + sizeof(*outudp);
g_info.packlen = getByDefaultValue(g_info.packlen,minipacket,32*1024);
from = malloc(sizeof(struct sockaddr_in));
memset(from,0x00,sizeof(struct sockaddr_in));
//setlinebuf(stdout);
#if 0
outip = (struct ip *)malloc((unsigned)packlen);
if(outip == NULL)
{
printf("outip is null!\n");
return ;
}
memset((char *)outip,0,packlen);
outip->ip_v = IPVERSION;
outip->ip_len = packlen;
outip->ip_off = 0;
outp
#endif
if((g_info.sendsock=socket(AF_INET, SOCK_DGRAM, 0)) < 0)
{
printf("create sendsock error!\n");
return ;
}
if((g_info.recvsock = socket(AF_INET,SOCK_RAW,IPPROTO_ICMP)) < 0)
{
printf("create recvsock error!\n");
return ;
}
#if 0
if(setsockopt(g_info.sendsock, IPPROTO_IP, IP_HDRINCL,(char *)&on,sizeof(on)) < 0)
{
printf("setopt error!\n");
return ;
}
#endif
printf("traceroute to %s (%s)\n",traceInfo->host,inet_ntoa(g_info.haddr.sin_addr));
fflush(NULL);
for(ttl=g_info.min_ttl; ttl < g_info.max_ttl; ++ttl)
{
u_int32_t lastaddr = 0;
int gotlastaddr = 0;
int got_there = 0;
int unreachable = 0;
int sentfirst = 0;
printf("%2d\t",ttl);
for(probe = 0; probe<g_info.queries; ++probe)
{
int cc;
struct timeval t1,t2;
struct timezone tz;
struct ip *ip;
gettimeofday(&t1,&tz);
send_probe(&g_info,++seq,ttl,&t1);
++sentfirst;
while((cc = wait_for_reply(&g_info,from,&t1)) != 0)
{
gettimeofday(&t2,&tz);
i = packet_ok(&g_info,(char *)g_info.packet,cc,from,seq);
if(i == 0)
continue;
if(!gotlastaddr || from->sin_addr.s_addr != lastaddr)
{
print((char *)g_info.packet,cc,from);
lastaddr = from->sin_addr.s_addr;
++gotlastaddr;
}
printf(" %.3f ms",deltaT(&t1,&t2));
if(i == -1)
break;
code = i -1;
switch(code)
{
case ICMP_UNREACH_PORT:
++got_there;
break;
case ICMP_UNREACH_NET:
++unreachable;
printf(" !N");
break;
case ICMP_UNREACH_HOST:
++unreachable;
printf(" !H");
break;
case ICMP_UNREACH_PROTOCOL:
++got_there;
printf(" !P");
default:
++unreachable;
printf("!<%d>",code);
break;
}
break;
}
if(cc == 0)
printf(" *");
fflush(NULL);
}
printf("\n");
if(got_there || unreachable > 0 && (unreachable >= g_info.queries -1))
{
break;
}
}
}
int main(void)
{
traceInfo_t traceInfo;
strcpy(traceInfo.host,"www.baidu.com");
traceInfo.min_ttl = 1;
traceInfo.max_ttl = 30;
traceInfo.waittime = 5;
traceInfo.queries = 3;
traceInfo.packsize = 50;
traceInfo.port = 32758 + 666;
trace(&traceInfo);
return 0;
}