OSDN Git Service

2013.10.24
[uclinux-h8/uClinux-dist.git] / user / iputils / tracepath.c
1 /*
2  * tracepath.c
3  *
4  *              This program is free software; you can redistribute it and/or
5  *              modify it under the terms of the GNU General Public License
6  *              as published by the Free Software Foundation; either version
7  *              2 of the License, or (at your option) any later version.
8  *
9  * Authors:     Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
10  */
11
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <unistd.h>
15 #include <sys/socket.h>
16 #include <linux/types.h>
17 #include <linux/errqueue.h>
18 #include <errno.h>
19 #include <string.h>
20 #include <netdb.h>
21 #include <netinet/in.h>
22 #include <resolv.h>
23 #include <sys/time.h>
24 #include <sys/uio.h>
25 #include <arpa/inet.h>
26
27 struct hhistory
28 {
29         int     hops;
30         struct timeval sendtime;
31 };
32
33 struct hhistory his[64];
34 int hisptr;
35
36 struct sockaddr_in target;
37 __u16 base_port;
38
39 const int overhead = 28;
40 int mtu = 65535;
41 int hops_to = -1;
42 int hops_from = -1;
43 int no_resolve = 0;
44
45 struct probehdr
46 {
47         __u32 ttl;
48         struct timeval tv;
49 };
50
51 void data_wait(int fd)
52 {
53         fd_set fds;
54         struct timeval tv;
55         FD_ZERO(&fds);
56         FD_SET(fd, &fds);
57         tv.tv_sec = 1;
58         tv.tv_usec = 0;
59         select(fd+1, &fds, NULL, NULL, &tv);
60 }
61
62 int recverr(int fd, int ttl)
63 {
64         int res;
65         struct probehdr rcvbuf;
66         char cbuf[512];
67         struct iovec  iov;
68         struct msghdr msg;
69         struct cmsghdr *cmsg;
70         struct sock_extended_err *e;
71         struct sockaddr_in addr;
72         struct timeval tv;
73         struct timeval *rettv;
74         int slot;
75         int rethops;
76         int sndhops;
77         int progress = -1;
78         int broken_router;
79         
80 restart:
81         memset(&rcvbuf, -1, sizeof(rcvbuf));
82         iov.iov_base = &rcvbuf;
83         iov.iov_len = sizeof(rcvbuf);
84         msg.msg_name = (__u8*)&addr;
85         msg.msg_namelen = sizeof(addr);
86         msg.msg_iov = &iov;
87         msg.msg_iovlen = 1;
88         msg.msg_flags = 0;
89         msg.msg_control = cbuf;
90         msg.msg_controllen = sizeof(cbuf);
91
92         gettimeofday(&tv, NULL);
93         res = recvmsg(fd, &msg, MSG_ERRQUEUE);
94         if (res < 0) {
95                 if (errno == EAGAIN)
96                         return progress;
97                 goto restart;
98         }
99
100         progress = mtu;
101
102         rethops = -1;
103         sndhops = -1;
104         e = NULL;
105         rettv = NULL;
106         slot = ntohs(addr.sin_port) - base_port;
107         if (slot>=0 && slot < 63 && his[slot].hops) {
108                 sndhops = his[slot].hops;
109                 rettv = &his[slot].sendtime;
110                 his[slot].hops = 0;
111         }
112         broken_router = 0;
113         if (res == sizeof(rcvbuf)) {
114                 if (rcvbuf.ttl == 0 || rcvbuf.tv.tv_sec == 0) {
115                         broken_router = 1;
116                 } else {
117                         sndhops = rcvbuf.ttl;
118                         rettv = &rcvbuf.tv;
119                 }
120         }
121
122         for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
123                 if (cmsg->cmsg_level == SOL_IP) {
124                         if (cmsg->cmsg_type == IP_RECVERR) {
125                                 e = (struct sock_extended_err *) CMSG_DATA(cmsg);
126                         } else if (cmsg->cmsg_type == IP_TTL) {
127                                 rethops = *(int*)CMSG_DATA(cmsg);
128                         } else { 
129                                 printf("cmsg:%d\n ", cmsg->cmsg_type); 
130                         }
131                 }
132         }
133         if (e == NULL) {
134                 printf("no info\n");
135                 return 0;
136         }
137         if (e->ee_origin == SO_EE_ORIGIN_LOCAL) {
138                 printf("%2d?: %-15s ", ttl, "[LOCALHOST]");
139         } else if (e->ee_origin == SO_EE_ORIGIN_ICMP) {
140                 char abuf[128];
141                 struct sockaddr_in *sin = (struct sockaddr_in*)(e+1);
142
143                 inet_ntop(AF_INET, &sin->sin_addr, abuf, sizeof(abuf));
144
145                 if (sndhops>0)
146                         printf("%2d:  ", sndhops);
147                 else
148                         printf("%2d?: ", ttl);
149
150                 if(!no_resolve) {
151                         char fabuf[256];
152                         struct hostent *h;
153                         fflush(stdout);
154                         h = gethostbyaddr((char *) &sin->sin_addr, sizeof(sin->sin_addr), AF_INET);
155                         snprintf(fabuf, sizeof(fabuf), "%s (%s)", h ? h->h_name : abuf, abuf);
156                         printf("%-52s ", fabuf);
157                 } else {
158                         printf("%-15s ", abuf);
159                 }
160         }
161
162         if (rethops>=0) {
163                 if (rethops<=64)
164                         rethops = 65-rethops;
165                 else if (rethops<=128)
166                         rethops = 129-rethops;
167                 else
168                         rethops = 256-rethops;
169                 if (sndhops>=0 && rethops != sndhops)
170                         printf("asymm %2d ", rethops);
171                 else if (sndhops<0 && rethops != ttl)
172                         printf("asymm %2d ", rethops);
173         }
174
175         if (rettv) {
176                 int diff = (tv.tv_sec-rettv->tv_sec)*1000000+(tv.tv_usec-rettv->tv_usec);
177                 printf("%3d.%03dms ", diff/1000, diff%1000);
178                 if (broken_router)
179                         printf("(This broken router returned corrupted payload) ");
180         }
181
182         switch (e->ee_errno) {
183         case ETIMEDOUT:
184                 printf("\n");
185                 break;
186         case EMSGSIZE:
187                 printf("pmtu %d\n", e->ee_info);
188                 mtu = e->ee_info;
189                 progress = mtu;
190                 break;
191         case ECONNREFUSED:
192                 printf("reached\n");
193                 hops_to = sndhops<0 ? ttl : sndhops;
194                 hops_from = rethops;
195                 return 0;
196         case EPROTO:
197                 printf("!P\n");
198                 return 0;
199         case EHOSTUNREACH:
200                 if (e->ee_origin == SO_EE_ORIGIN_ICMP &&
201                     e->ee_type == 11 &&
202                     e->ee_code == 0) {
203                         printf("\n");
204                         break;
205                 }
206                 printf("!H\n");
207                 return 0;
208         case ENETUNREACH:
209                 printf("!N\n");
210                 return 0;
211         case EACCES:
212                 printf("!A\n");
213                 return 0;
214         default:
215                 printf("\n");
216                 errno = e->ee_errno;
217                 perror("NET ERROR");
218                 return 0;
219         }
220         goto restart;
221 }
222
223 int probe_ttl(int fd, int ttl)
224 {
225         int i;
226         char sndbuf[mtu];
227         struct probehdr *hdr = (struct probehdr*)sndbuf;
228
229         memset(sndbuf,0,mtu);
230
231 restart:
232         for (i=0; i<10; i++) {
233                 int res;
234
235                 hdr->ttl = ttl;
236                 target.sin_port = htons(base_port + hisptr);
237                 gettimeofday(&hdr->tv, NULL);
238                 his[hisptr].hops = ttl;
239                 his[hisptr].sendtime = hdr->tv;
240                 if (sendto(fd, sndbuf, mtu-overhead, 0, (struct sockaddr*)&target, sizeof(target)) > 0)
241                         break;
242                 res = recverr(fd, ttl);
243                 his[hisptr].hops = 0;
244                 if (res==0)
245                         return 0;
246                 if (res > 0)
247                         goto restart;
248         }
249         hisptr = (hisptr + 1)&63;
250
251         if (i<10) {
252                 data_wait(fd);
253                 if (recv(fd, sndbuf, sizeof(sndbuf), MSG_DONTWAIT) > 0) {
254                         printf("%2d?: reply received 8)\n", ttl);
255                         return 0;
256                 }
257                 return recverr(fd, ttl);
258         }
259
260         printf("%2d:  send failed\n", ttl);
261         return 0;
262 }
263
264 static void usage(void) __attribute((noreturn));
265
266 static void usage(void)
267 {
268         fprintf(stderr, "Usage: tracepath [-n] <destination>[/<port>]\n");
269         exit(-1);
270 }
271
272 int
273 main(int argc, char **argv)
274 {
275         struct hostent *he;
276         int fd;
277         int on;
278         int ttl;
279         char *p;
280         int ch;
281
282         while ((ch = getopt(argc, argv, "nh?")) != EOF) {
283                 switch(ch) {
284                 case 'n':       
285                         no_resolve = 1;
286                         break;
287                 default:
288                         usage();
289                 }
290         }
291
292         argc -= optind;
293         argv += optind;
294
295         if (argc != 1)
296                 usage();
297
298
299         fd = socket(AF_INET, SOCK_DGRAM, 0);
300         if (fd < 0) {
301                 perror("socket");
302                 exit(1);
303         }
304         target.sin_family = AF_INET;
305
306         p = strchr(argv[0], '/');
307         if (p) {
308                 *p = 0;
309                 base_port = atoi(p+1);
310         } else
311                 base_port = 44444;
312         he = gethostbyname(argv[0]);
313         if (he == NULL) {
314                 herror("gethostbyname");
315                 exit(1);
316         }
317         memcpy(&target.sin_addr, he->h_addr, 4);
318
319         on = IP_PMTUDISC_DO;
320         if (setsockopt(fd, SOL_IP, IP_MTU_DISCOVER, &on, sizeof(on))) {
321                 perror("IP_MTU_DISCOVER");
322                 exit(1);
323         }
324         on = 1;
325         if (setsockopt(fd, SOL_IP, IP_RECVERR, &on, sizeof(on))) {
326                 perror("IP_RECVERR");
327                 exit(1);
328         }
329         if (setsockopt(fd, SOL_IP, IP_RECVTTL, &on, sizeof(on))) {
330                 perror("IP_RECVTTL");
331                 exit(1);
332         }
333
334         for (ttl=1; ttl<32; ttl++) {
335                 int res;
336                 int i;
337
338                 on = ttl;
339                 if (setsockopt(fd, SOL_IP, IP_TTL, &on, sizeof(on))) {
340                         perror("IP_TTL");
341                         exit(1);
342                 }
343
344                 for (i=0; i<3; i++) {
345                         res = probe_ttl(fd, ttl);
346                         if (res == 0)
347                                 goto done;
348                         if (res > 0)
349                                 break;
350                 }
351
352                 if (res < 0)
353                         printf("%2d:  no reply\n", ttl);
354         }
355         printf("     Too many hops: pmtu %d\n", mtu);
356 done:
357         printf("     Resume: pmtu %d ", mtu);
358         if (hops_to>=0)
359                 printf("hops %d ", hops_to);
360         if (hops_from>=0)
361                 printf("back %d ", hops_from);
362         printf("\n");
363         exit(0);
364 }