OSDN Git Service

Fix no pic
[uclinux-h8/uClinux-dist.git] / user / iputils / tracepath6.c
1 /*
2  * tracepath6.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 <netinet/in.h>
17 #include <netinet/icmp6.h>
18
19 #include <linux/types.h>
20 #include <linux/errqueue.h>
21 #include <errno.h>
22 #include <string.h>
23 #include <netdb.h>
24 #include <resolv.h>
25 #include <sys/time.h>
26 #include <sys/uio.h>
27 #include <arpa/inet.h>
28
29 #ifndef SOL_IPV6
30 #define SOL_IPV6 IPPROTO_IPV6
31 #endif
32
33 int overhead = 48;
34 int mtu = 128000;
35 int hops_to = -1;
36 int hops_from = -1;
37 int no_resolve = 0;
38 int show_both = 0;
39 int mapped;
40
41 struct probehdr
42 {
43         __u32 ttl;
44         struct timeval tv;
45 };
46
47 void data_wait(int fd)
48 {
49         fd_set fds;
50         struct timeval tv;
51         FD_ZERO(&fds);
52         FD_SET(fd, &fds);
53         tv.tv_sec = 1;
54         tv.tv_usec = 0;
55         select(fd+1, &fds, NULL, NULL, &tv);
56 }
57
58 int recverr(int fd, int ttl)
59 {
60         int res;
61         struct probehdr rcvbuf;
62         char cbuf[512];
63         struct iovec  iov;
64         struct msghdr msg;
65         struct cmsghdr *cmsg;
66         struct sock_extended_err *e;
67         struct timeval tv;
68         struct timeval *rettv;
69         int rethops;
70         int sndhops;
71         int progress = -1;
72         int broken_router;
73
74 restart:
75         memset(&rcvbuf, -1, sizeof(rcvbuf));
76         iov.iov_base = &rcvbuf;
77         iov.iov_len = sizeof(rcvbuf);
78         msg.msg_name = NULL;
79         msg.msg_namelen = 0;
80         msg.msg_iov = &iov;
81         msg.msg_iovlen = 1;
82         msg.msg_flags = 0;
83         msg.msg_control = cbuf;
84         msg.msg_controllen = sizeof(cbuf);
85
86         gettimeofday(&tv, NULL);
87         res = recvmsg(fd, &msg, MSG_ERRQUEUE);
88         if (res < 0) {
89                 if (errno == EAGAIN)
90                         return progress;
91                 goto restart;
92         }
93
94         progress = 2;
95
96         rethops = -1;
97         sndhops = -1;
98         e = NULL;
99         rettv = NULL;
100         broken_router = 0;
101         if (res == sizeof(rcvbuf)) {
102                 if (rcvbuf.ttl == 0 || rcvbuf.tv.tv_sec == 0)
103                         broken_router = 1;
104
105                 sndhops = rcvbuf.ttl;
106                 rettv = &rcvbuf.tv;
107                 if (sndhops != ttl)
108                         progress = -1;
109         }
110         if (sndhops>0)
111                 printf("%2d:  ", sndhops);
112         else
113                 printf("%2d?: ", ttl);
114
115         for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
116                 if (cmsg->cmsg_level == SOL_IPV6) {
117                         switch(cmsg->cmsg_type) {
118                         case IPV6_RECVERR:
119                                 e = (struct sock_extended_err *)CMSG_DATA(cmsg);
120                                 break;
121                         case IPV6_HOPLIMIT:
122 #ifdef IPV6_2292HOPLIMIT
123                         case IPV6_2292HOPLIMIT:
124 #endif
125                                 rethops = *(int*)CMSG_DATA(cmsg);
126                                 break;
127                         }
128                 } else if (cmsg->cmsg_level == SOL_IP) {
129                         if (cmsg->cmsg_type == IP_TTL) {
130                                 rethops = *(__u8*)CMSG_DATA(cmsg);
131                         }
132                 }
133         }
134         if (e == NULL) {
135                 printf("no info\n");
136                 return 0;
137         }
138         if (e->ee_origin == SO_EE_ORIGIN_LOCAL)
139                 printf("%-32s ", "[LOCALHOST]");
140         else if (e->ee_origin == SO_EE_ORIGIN_ICMP6 ||
141                  e->ee_origin == SO_EE_ORIGIN_ICMP) {
142                 struct hostent * h = NULL;
143                 char abuf[128];
144                 struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)(e+1);
145                 struct sockaddr_in *sin = (struct sockaddr_in*)(e+1);
146
147                 if (!no_resolve)
148                         fflush(stdout);
149
150                 if (sin->sin_family == AF_INET6) {
151                         inet_ntop(AF_INET6, &sin6->sin6_addr, abuf, sizeof(abuf));
152                         if (!no_resolve)
153                                 h = gethostbyaddr((char *) &sin6->sin6_addr, sizeof(sin6->sin6_addr), AF_INET6);
154                 } else {
155                         inet_ntop(AF_INET, &sin->sin_addr, abuf, sizeof(abuf));
156                         if (!no_resolve)
157                                 h = gethostbyaddr((char *) &sin->sin_addr, sizeof(sin->sin_addr), AF_INET);
158                 }
159                 if (!no_resolve) {
160                         char fabuf[256];
161                         if (show_both) {
162                                 if (h)
163                                         snprintf(fabuf, sizeof(fabuf), "%s %s", h->h_name, abuf);
164                                 else
165                                         snprintf(fabuf, sizeof(fabuf), "%s", abuf);
166                         } else {
167                                 snprintf(fabuf, sizeof(fabuf), "%s", h ? h->h_name : abuf);
168                         }
169                         printf("%-40s ", fabuf);
170                 } else {
171                         printf("%-32s ", abuf);
172                 }
173         }
174
175         if (rethops>=0) {
176                 if (rethops<=64)
177                         rethops = 65-rethops;
178                 else if (rethops<=128)
179                         rethops = 129-rethops;
180                 else
181                         rethops = 256-rethops;
182                 if (sndhops>=0 && rethops != sndhops)
183                         printf("asymm %2d ", rethops);
184                 else if (sndhops<0 && rethops != ttl)
185                         printf("asymm %2d ", rethops);
186         }
187
188         if (rettv) {
189                 int diff = (tv.tv_sec-rettv->tv_sec)*1000000+(tv.tv_usec-rettv->tv_usec);
190                 printf("%3d.%03dms ", diff/1000, diff%1000);
191                 if (broken_router)
192                         printf("(This broken router returned corrupted payload) ");
193         }
194
195         switch (e->ee_errno) {
196         case ETIMEDOUT:
197                 printf("\n");
198                 break;
199         case EMSGSIZE:
200                 printf("pmtu %d\n", e->ee_info);
201                 mtu = e->ee_info;
202                 progress = 1;
203                 break;
204         case ECONNREFUSED:
205                 printf("reached\n");
206                 hops_to = sndhops<0 ? ttl : sndhops;
207                 hops_from = rethops;
208                 return 0;
209         case EPROTO:
210                 printf("!P\n");
211                 return 0;
212         case EHOSTUNREACH:
213                 if ((e->ee_origin == SO_EE_ORIGIN_ICMP &&
214                      e->ee_type == 11 &&
215                      e->ee_code == 0) ||
216                     (e->ee_origin == SO_EE_ORIGIN_ICMP6 &&
217                      e->ee_type == 3 &&
218                      e->ee_code == 0)) {
219                         printf("\n");
220                         break;
221                 }
222                 printf("!H\n");
223                 return 0;
224         case ENETUNREACH:
225                 printf("!N\n");
226                 return 0;
227         case EACCES:
228                 printf("!A\n");
229                 return 0;
230         default:
231                 printf("\n");
232                 errno = e->ee_errno;
233                 perror("NET ERROR");
234                 return 0;
235         }
236         goto restart;
237 }
238
239 int probe_ttl(int fd, int ttl)
240 {
241         int i;
242         char sndbuf[mtu];
243         struct probehdr *hdr = (struct probehdr*)sndbuf;
244
245 restart:
246
247         for (i=0; i<10; i++) {
248                 int res;
249
250                 hdr->ttl = ttl;
251                 gettimeofday(&hdr->tv, NULL);
252                 if (send(fd, sndbuf, mtu-overhead, 0) > 0)
253                         break;
254                 res = recverr(fd, ttl);
255                 if (res==0)
256                         return 0;
257                 if (res > 0)
258                         goto restart;
259         }
260
261         if (i<10) {
262                 int res;
263
264                 data_wait(fd);
265                 if (recv(fd, sndbuf, sizeof(sndbuf), MSG_DONTWAIT) > 0) {
266                         printf("%2d?: reply received 8)\n", ttl);
267                         return 0;
268                 }
269                 res = recverr(fd, ttl);
270                 if (res == 1)
271                         goto restart;
272                 return res;
273         }
274
275         printf("%2d:  send failed\n", ttl);
276         return 0;
277 }
278
279 static void usage(void) __attribute((noreturn));
280
281 static void usage(void)
282 {
283         fprintf(stderr, "Usage: tracepath6 [-n] [-b] <destination>[/<port>]\n");
284         exit(-1);
285 }
286
287
288 int main(int argc, char **argv)
289 {
290         int fd;
291         int on;
292         struct sockaddr_in6 sin;
293         int ttl;
294         char *p;
295         struct addrinfo hints, *ai, *ai0;
296         int ch;
297         int gai;
298         char pbuf[NI_MAXSERV];
299
300         while ((ch = getopt(argc, argv, "nbh?")) != EOF) {
301                 switch(ch) {
302                 case 'n':       
303                         no_resolve = 1;
304                         break;
305                 case 'b':       
306                         show_both = 1;
307                         break;
308                 default:
309                         usage();
310                 }
311         }
312
313         argc -= optind;
314         argv += optind;
315
316         if (argc != 1)
317                 usage();
318
319         memset(&sin, 0, sizeof(sin));
320         
321         p = strchr(argv[0], '/');
322         if (p) {
323                 *p = 0;
324                 sprintf(pbuf, "%u", (unsigned)atoi(p+1));
325         } else {
326                 sprintf(pbuf, "%u", (0x8000 | getpid()) & 0xffff);
327         }
328
329         memset(&hints, 0, sizeof(hints));
330         hints.ai_family = AF_INET6;
331         hints.ai_socktype = SOCK_DGRAM;
332         hints.ai_protocol = IPPROTO_UDP;
333         hints.ai_flags = no_resolve ? AI_NUMERICHOST : 0;
334         gai = getaddrinfo(argv[0], pbuf, &hints, &ai0);
335         if (gai) {
336                 herror("getaddrinfo");  /*XXX*/
337                 exit(1);
338         }
339
340         fd = -1;
341         for (ai = ai0; ai; ai = ai->ai_next) {
342                 fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
343                 if (fd < 0)
344                         continue;
345                 if (connect(fd, ai->ai_addr, ai->ai_addrlen) < 0) {
346                         close(fd);
347                         fd = -1;
348                         continue;
349                 }
350                 memcpy(&sin, ai->ai_addr, sizeof(sin));
351                 break;
352         }
353         if (fd < 0) {
354                 perror("socket/connect");
355                 exit(1);
356         }
357         freeaddrinfo(ai0);
358
359         if (!sin.sin6_addr.s6_addr32[0] && !sin.sin6_addr.s6_addr32[1]
360             && sin.sin6_addr.s6_addr32[2] == htonl(0xFFFF)) {
361                 mtu = 65535;
362                 overhead = 28;
363                 mapped = 1;
364         }
365
366         on = IPV6_PMTUDISC_DO;
367         if (setsockopt(fd, SOL_IPV6, IPV6_MTU_DISCOVER, &on, sizeof(on))) {
368                 perror("IPV6_MTU_DISCOVER");
369                 exit(1);
370         }
371         if (mapped && setsockopt(fd, SOL_IP, IP_MTU_DISCOVER, &on, sizeof(on))) {
372                 perror("IP_MTU_DISCOVER");
373                 exit(1);
374         }
375         on = 1;
376         if (setsockopt(fd, SOL_IPV6, IPV6_RECVERR, &on, sizeof(on))) {
377                 perror("IPV6_RECVERR");
378                 exit(1);
379         }
380         if (mapped && setsockopt(fd, SOL_IP, IP_RECVERR, &on, sizeof(on))) {
381                 perror("IP_RECVERR");
382                 exit(1);
383         }
384         if (
385 #ifdef IPV6_RECVHOPLIMIT
386             setsockopt(fd, SOL_IPV6, IPV6_HOPLIMIT, &on, sizeof(on)) &&
387             setsockopt(fd, SOL_IPV6, IPV6_2292HOPLIMIT, &on, sizeof(on))
388 #else
389             setsockopt(fd, SOL_IPV6, IPV6_HOPLIMIT, &on, sizeof(on))
390 #endif
391             ) {
392                 perror("IPV6_HOPLIMIT");
393                 exit(1);
394         }
395         if (mapped && setsockopt(fd, SOL_IP, IP_RECVTTL, &on, sizeof(on))) {
396                 perror("IP_RECVTTL");
397                 exit(1);
398         }
399
400         for (ttl=1; ttl<32; ttl++) {
401                 int res;
402                 int i;
403
404                 on = ttl;
405                 if (setsockopt(fd, SOL_IPV6, IPV6_UNICAST_HOPS, &on, sizeof(on))) {
406                         perror("IPV6_UNICAST_HOPS");
407                         exit(1);
408                 }
409                 if (mapped && setsockopt(fd, SOL_IP, IP_TTL, &on, sizeof(on))) {
410                         perror("IP_TTL");
411                         exit(1);
412                 }
413
414                 for (i=0; i<3; i++) {
415                         res = probe_ttl(fd, ttl);
416                         if (res == 0)
417                                 goto done;
418                         if (res > 0)
419                                 break;
420                 }
421
422                 if (res < 0)
423                         printf("%2d:  no reply\n", ttl);
424         }
425         printf("     Too many hops: pmtu %d\n", mtu);
426
427 done:
428         printf("     Resume: pmtu %d ", mtu);
429         if (hops_to>=0)
430                 printf("hops %d ", hops_to);
431         if (hops_from>=0)
432                 printf("back %d ", hops_from);
433         printf("\n");
434         exit(0);
435 }