UNP1:Linux下实现traceroute程序
这个程序敲得我快吐血了.....在敲代码和调试过程中遇到了N个纠结的问题.....我还差的远呢,继续努力~
-------------------------以下为正文--------------------------
traceroute程序,简单的说就是找到本地与目的地之间route节点。它的实现方法是通过不断发送ttl递增的请求包来实现。具体的原理可以参照TCP/IP详解,里面讲的很透彻~
以下为实现代码(相比书中做了一些改动)
trace.h
1 #include<stdio.h>
2 #include<sys/time.h>
3 #include<errno.h>
4 #include<signal.h>
5 #include<time.h>
6 #include<stdlib.h>
7 #include<unistd.h>
8 #include<netdb.h>
9 #include<string.h>
10 #include<strings.h>
11 #include<sys/socket.h>
12 #include<arpa/inet.h>
13 #include<netinet/in_systm.h>
14 #include<netinet/ip.h>
15 #include<netinet/ip_icmp.h>
16 #include<netinet/udp.h>
17 #include<netinet/in_systm.h>
18 #define BUFSIZE 1500
19 struct rec{
20 u_short rec_seq;
21 u_short rec_ttl;
22 struct timeval rec_tv;
23 };
24
25 char recvbuf[BUFSIZE];
26 char sendbuf[BUFSIZE];
27
28 int datalen;
29 char *host;
30 u_short sport,dport;
31 int nsent;
32 pid_t pid;
33 int probe,nprobes;
34 int sendfd,recvfd;
35 int ttl,max_ttl;
36 int verbose;
37
38 const char* icmpcode_v4(int);
39 int recv_v4(int,struct timeval*);
40 void sig_alrm(int);
41 void traceloop(void);
42 void tv_sub(struct timeval*,struct timeval*);
43
44 struct proto{
45 const char*(*icmpcode)(int);
46 int (*recv)(int,struct timeval*);
47 struct sockaddr *sasend; //dest addr, the destination
48 struct sockaddr *sarecv; //recv addr, store who send the message
49 struct sockaddr *salast;
50 struct sockaddr *sabind; //bind the source port
51 socklen_t salen;
52 int icmpproto;
53 int ttllevel;
54 int ttloptname;
55 }*pr;
main.c
1 #include"trace.h"
2 struct proto proto_v4={icmpcode_v4,recv_v4,NULL,NULL,NULL,NULL,0,IPPROTO_ICMP,IPPROTO_IP,IP_TTL};
3
4 int datalen=sizeof(struct rec);
5 int max_ttl=30;
6 int nprobes=3;
7 u_short dport=32768+666;//hope the port of dest is not used
8
9 struct addrinfo *host_serv(const char *host,const char *serv,int family,int socktype){
10 int n;
11 struct addrinfo hints,*res;
12 bzero(&hints,sizeof(hints));
13 hints.ai_flags=AI_CANONNAME;
14 hints.ai_family=family;
15 hints.ai_socktype=socktype;
16 if((n=getaddrinfo(host,serv,&hints,&res))!=0){
17 return NULL;
18 }
19 return (res);
20 }
21 int main(int argc,char *argv[]){
22 int c;
23 struct addrinfo *ai;
24 struct sigaction s_action;
25 char h[20]={0};
26 while((c=getopt(argc,argv,"m:v"))!=-1){
27 switch(c){
28 case 'm':
29 if((max_ttl=atoi(optarg))<0){
30 printf("invalid input\n");
31 }
32 break;
33 case 'v':
34 verbose++;
35 break;
36 case '?':
37 printf("unrecognized\n");
38 return -1;
39 }
40 }
41 if(optind!=argc-1){
42 printf("error input\n");
43 return -1;
44 }
45 host=argv[optind];
46
47 pid=getpid();
48
49 bzero(&s_action,sizeof(s_action));
50 s_action.sa_handler=sig_alrm;
51 s_action.sa_flags=SA_INTERRUPT;
52 sigaction(SIGALRM,&s_action,NULL);
53
54 // signal(SIGALRM,sig_alrm);
55 ai=host_serv(host,NULL,0,0);
56 inet_ntop(AF_INET,&((struct sockaddr_in*)(ai->ai_addr))->sin_addr,h,sizeof(h));
57 printf("traceroute to %s (%s): %d hops max, %d data bytes\n",
58 ai->ai_canonname?ai->ai_canonname:h,h,max_ttl,datalen);
59
60 if(ai->ai_family==AF_INET){
61 pr=&proto_v4;
62 }else{
63 printf("UNKNOW address family\n");
64 return -1;
65 }
66
67 pr->sasend=ai->ai_addr;
68 pr->sarecv=(struct sockaddr*)calloc(1,ai->ai_addrlen);
69 pr->salast=(struct sockaddr*)calloc(1,ai->ai_addrlen);
70 pr->sabind=(struct sockaddr*)calloc(1,ai->ai_addrlen);
71 pr->salen=ai->ai_addrlen;
72 traceloop();
73 exit(0);
74 }
traceloop.c
这里udp结构里的成员按照书上的写法会出现错误。linux里对udp的定义不一样。
1 //#define __FAVOR_BSD
2 #include"trace.h"
3 int gotalarm;
4 const char *icmpcode_v4(int code){
5 static char errbuf[100];
6 switch(code){
7 case 0:return("network unreachable");
8 case 1:return("host unreachable");
9 case 2:return("protocol unreachable");
10 case 3:return("port unreachable");
11 case 4:return("fragmentation required but DF bit set");
12 case 5:return("source route failed");
13 case 6:return("destination network unknown");
14 case 7:return("destination host unknown");
15 case 8:return("source host isolated(obsolete)");
16 case 9:return("destination network administartively prohibited");
17 case 10:return("destination host administartively prohibited");
18 case 11:return("network unreachable for TOS");
19 case 12:return("host unreachable for TOS");
20 case 13:return("communication error");
21 case 14:return("host recedenc violation");
22 case 15:return("precedence cutoff in effect");
23 default:sprintf(errbuf,"unknown code %d",code);
24 }
25 return errbuf;
26 }
27 void sig_alrm(int signo){
28 gotalarm=1;
29 return;
30 }
31 void tv_sub(struct timeval *out,struct timeval *in){
32 if((out->tv_usec-=in->tv_usec)<0){
33 --out->tv_sec;
34 out->tv_sec+=1000000;
35 }
36 out->tv_sec-=in->tv_sec;
37 }
38 void traceloop(void){
39 int seq,code,done;
40 double rtt;
41 struct rec *rec;
42 struct timeval tvrecv;
43 if((recvfd=socket(pr->sasend->sa_family,SOCK_RAW,pr->icmpproto))<0){
44 printf("recvfd:socket failed\n");
45 return;
46 }
47 setuid(getuid());
48 if((sendfd=socket(pr->sasend->sa_family,SOCK_DGRAM,0))<0){
49 printf("sendfd:socket failed\n");
50 return;
51 }
52
53 pr->sabind->sa_family=pr->sasend->sa_family;
54 sport=(getpid()&0xffff) | 0x8000;
55 ((struct sockaddr_in*)pr->sabind)->sin_port=htons(sport);
56
57 if(bind(sendfd,pr->sabind,pr->salen)<0){
58 printf("bind error\n");
59 return;
60 }
61
62 sig_alrm(SIGALRM);
63 seq=0;
64 done=0;
65 for(ttl=1;ttl<=max_ttl&&done==0;ttl++){
66 setsockopt(sendfd,pr->ttllevel,pr->ttloptname,&ttl,sizeof(int));//modify ttl
67 bzero(pr->salast,pr->salen);
68 printf("%2d ",ttl);
69 fflush(stdout);
70 for(probe=0;probe<nprobes;probe++){
71 /*
72 *these sendbuf is just
73 *used to exam if the received data is sended by our program
74 */
75 rec=(struct rec*)sendbuf;
76 rec->rec_seq=++seq;
77 rec->rec_ttl=ttl;
78
79 gettimeofday(&rec->rec_tv,NULL);
80 ((struct sockaddr_in*)pr->sasend)->sin_port=htons(dport+seq);
81 if(sendto(sendfd,sendbuf,datalen,0,pr->sasend,pr->salen)<0){//send to dest with ttl added
82 perror("bad sendto");
83 continue;
84 }
85
86 //if time_out print * else print info
87 if((code=(*pr->recv)(seq,&tvrecv))==-3){
88 printf(" *");
89 }else{
90 char str[NI_MAXHOST];
91 if(memcmp(pr->sarecv,pr->salast,pr->salen)!=0){
92 if(getnameinfo(pr->sarecv,pr->salen,str,sizeof(str),NULL,0,0)==0){
93 printf(" %s (%s)",str,inet_ntoa(((struct sockaddr_in*)pr->sarecv)->sin_addr));
94 }else{
95 printf(" %s",inet_ntoa(((struct sockaddr_in*)pr->sarecv)->sin_addr));
96 }
97 memcpy(pr->salast,pr->sarecv,pr->salen);
98 }
99 tv_sub(&tvrecv,&rec->rec_tv);
100 rtt=tvrecv.tv_sec*1000.0+tvrecv.tv_usec/1000;
101 printf(" %.3f ms",rtt);
102
103 if(code==-1){ //reach the dest
104 done++;
105 }else if(code>0){
106 printf(" (ICMP %s)",(*pr->icmpcode)(code));
107 }
108 }
109 fflush(stdout);
110 }
111 printf("\n");
112 }
113 }
114 int recv_v4(int seq,struct timeval *tv){
115 int hlen1,hlen2,icmplen,ret;
116 socklen_t len;
117 ssize_t n;
118 struct ip *ip,*hip;
119 struct icmp *icmp;
120 struct udphdr *udp;
121
122 gotalarm=0;
123 for(;;){
124 if(gotalarm){
125 return -3;
126 }
127 len=pr->salen;
128 alarm(3);
129 n=recvfrom(recvfd,recvbuf,sizeof(recvbuf),0,pr->sarecv,&len);//data len
130 if(n<0){
131 if(errno==EINTR){
132 continue;
133 }else{
134 printf("recvfrom error\n");
135 return 0;
136 }
137 }else{
138 //if recvfrom ok , close the alarm
139 alarm(0);
140 }
141
142 //read data
143 ip=(struct ip*)recvbuf;
144 hlen1=ip->ip_hl<<2;//ip len
145 icmp=(struct icmp*)(recvbuf+hlen1);
146 if((icmplen=n-hlen1)<8){
147 continue;
148 }
149 if(icmp->icmp_type==ICMP_TIMXCEED&&
150 icmp->icmp_code==ICMP_TIMXCEED_INTRANS){
151 if(icmplen<8+sizeof(struct ip)){
152 continue;
153 }
154 //get icmp data
155 hip=(struct ip*)(recvbuf+hlen1+8);
156 hlen2=hip->ip_hl<<2;
157 if(icmplen<8+hlen2+4){
158 continue;
159 }
160 udp=(struct udphdr *)(recvbuf+hlen1+8+hlen2);
161 if(hip->ip_p==IPPROTO_UDP&&
162 udp->source==htons(sport)&&
163 udp->dest==htons(dport+seq)){
164 ret=-2;
165 break;
166 }
167 }else if(icmp->icmp_type==ICMP_UNREACH){
168 if(icmplen<8+sizeof(struct ip))
169 continue;
170 hip=(struct ip*)(recvbuf+hlen1+8);
171 hlen2=hip->ip_hl<<2;
172 if(icmplen<8+hlen2+4)
173 continue;
174 udp=(struct udphdr*)(recvbuf+hlen1+8+hlen2);
175 if(hip->ip_p==IPPROTO_UDP&&
176 udp->source==htons(sport)&&
177 udp->dest==htons(dport+seq)){
178 if(icmp->icmp_code==ICMP_UNREACH_PORT)
179 ret=-1; //reach the destination
180 else
181 ret=icmp->icmp_code;
182 break;
183 }
184 }
185 }
186 gettimeofday(tv,NULL);
187 return ret;
188 }