# BRCM_VERSION=3
[bcm963xx.git] / userapps / opensource / busybox / networking / traceroute.c
1 /*-
2  * Copyright (c) 1990, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Van Jacobson.
7  *
8  * Special for busybox ported by Vladimir Oleynik <dzo@simtreas.ru> 2001
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33
34 /*
35  * traceroute host  - trace the route ip packets follow going to "host".
36  * Notes
37  * -----
38  * This program must be run by root or be setuid.  (I suggest that
39  * you *don't* make it setuid -- casual use could result in a lot
40  * of unnecessary traffic on our poor, congested nets.)
41  *
42  * I stole the idea for this program from Steve Deering.  Since
43  * the first release, I've learned that had I attended the right
44  * IETF working group meetings, I also could have stolen it from Guy
45  * Almes or Matt Mathis.  I don't know (or care) who came up with
46  * the idea first.  I envy the originators' perspicacity and I'm
47  * glad they didn't keep the idea a secret.
48  *
49  * Tim Seaver, Ken Adelman and C. Philip Wood provided bug fixes and/or
50  * enhancements to the original distribution.
51  *
52  * I've hacked up a round-trip-route version of this that works by
53  * sending a loose-source-routed udp datagram through the destination
54  * back to yourself.  Unfortunately, SO many gateways botch source
55  * routing, the thing is almost worthless.  Maybe one day...
56  *
57  *  -- Van Jacobson (van@helios.ee.lbl.gov)
58  *     Tue Dec 20 03:50:13 PST 1988
59  */
60
61 #undef CONFIG_FEATURE_TRACEROUTE_VERBOSE
62 //#define CONFIG_FEATURE_TRACEROUTE_VERBOSE
63 #undef CONFIG_FEATURE_TRACEROUTE_SO_DEBUG   /* not in documentation man */
64
65 #include <stdio.h>
66 #include <errno.h>
67 #include <stdlib.h>
68 #include <string.h>
69 #include <unistd.h>
70 #include <sys/time.h>
71 #include "inet_common.h"
72 #include <netdb.h>
73 #include <endian.h>
74 #include <netinet/udp.h>
75 #include <netinet/ip.h>
76 #include <netinet/ip_icmp.h>
77
78
79 #define MAXPACKET       65535   /* max ip packet size */
80 #ifndef MAXHOSTNAMELEN
81 #define MAXHOSTNAMELEN  64
82 #endif
83
84 /*
85  * format of a (udp) probe packet.
86  */
87 struct opacket {
88         struct ip ip;
89         struct udphdr udp;
90         u_char seq;             /* sequence number of this packet */
91         u_char ttl;             /* ttl packet left with */
92         struct timeval tv;      /* time packet left */
93 };
94
95 /*
96  * Definitions for internet protocol version 4.
97  * Per RFC 791, September 1981.
98  */
99 #define IPVERSION       4
100
101
102 #include "busybox.h"
103
104 static u_char  packet[512];            /* last inbound (icmp) packet */
105 static struct opacket  *outpacket;     /* last output (udp) packet */
106
107 static int s;                          /* receive (icmp) socket file descriptor */
108 static int sndsock;                    /* send (udp) socket file descriptor */
109
110 static struct sockaddr whereto;        /* Who to try to reach */
111 static int datalen;                    /* How much data */
112
113 static char *hostname;
114
115 static int max_ttl = 30;
116 static u_short ident;
117 static u_short port = 32768+666;       /* start udp dest port # for probe packets */
118
119 #ifdef CONFIG_FEATURE_TRACEROUTE_VERBOSE
120 static int verbose;
121 #endif
122 static int waittime = 5;               /* time to wait for response (in seconds) */
123 static int nflag;                      /* print addresses numerically */
124
125 /*
126  * Construct an Internet address representation.
127  * If the nflag has been supplied, give
128  * numeric value, otherwise try for symbolic name.
129  */
130 static inline void
131 inetname(struct sockaddr_in *from)
132 {
133         char *cp;
134         static char domain[MAXHOSTNAMELEN + 1];
135         char name[MAXHOSTNAMELEN + 1];
136         static int first = 1;
137         const char *ina;
138
139         if (first && !nflag) {
140                 first = 0;
141                 if (getdomainname(domain, MAXHOSTNAMELEN) != 0)
142                         domain[0] = 0;
143         }
144         cp = 0;
145         if (!nflag && from->sin_addr.s_addr != INADDR_ANY) {
146                 if(INET_rresolve(name, sizeof(name), from, 0x4000, 0xffffffff) >= 0) {
147                         if ((cp = strchr(name, '.')) &&
148                             !strcmp(cp + 1, domain))
149                                 *cp = 0;
150                         cp = (char *)name;
151                 }
152         }
153         ina = inet_ntoa(from->sin_addr);
154         if (nflag)
155                 printf(" %s", ina);
156         else
157                 printf(" %s (%s)", (cp ? cp : ina), ina);
158 }
159
160 static inline void
161 print(u_char *buf, int cc, struct sockaddr_in *from)
162 {
163         struct ip *ip;
164         int hlen;
165
166         ip = (struct ip *) buf;
167         hlen = ip->ip_hl << 2;
168         cc -= hlen;
169
170         inetname(from);
171 #ifdef CONFIG_FEATURE_TRACEROUTE_VERBOSE
172         if (verbose)
173                 printf (" %d bytes to %s", cc, inet_ntoa (ip->ip_dst));
174 #endif
175 }
176
177 static inline double
178 deltaT(struct timeval *t1p, struct timeval *t2p)
179 {
180         double dt;
181
182         dt = (double)(t2p->tv_sec - t1p->tv_sec) * 1000.0 +
183              (double)(t2p->tv_usec - t1p->tv_usec) / 1000.0;
184         return (dt);
185 }
186
187 static inline int
188 wait_for_reply(int sock, struct sockaddr_in *from, int reset_timer)
189 {
190         fd_set fds;
191         static struct timeval wait;
192         int cc = 0;
193         int fromlen = sizeof (*from);
194
195         FD_ZERO(&fds);
196         FD_SET(sock, &fds);
197         if (reset_timer) {
198                 /*
199                  * traceroute could hang if someone else has a ping
200                  * running and our ICMP reply gets dropped but we don't
201                  * realize it because we keep waking up to handle those
202                  * other ICMP packets that keep coming in.  To fix this,
203                  * "reset_timer" will only be true if the last packet that
204                  * came in was for us or if this is the first time we're
205                  * waiting for a reply since sending out a probe.  Note
206                  * that this takes advantage of the select() feature on
207                  * Linux where the remaining timeout is written to the
208                  * struct timeval area.
209                  */
210                 wait.tv_sec = waittime;
211                 wait.tv_usec = 0;
212         }
213
214         if (select(sock+1, &fds, (fd_set *)0, (fd_set *)0, &wait) > 0)
215                 cc=recvfrom(s, (char *)packet, sizeof(packet), 0,
216                             (struct sockaddr *)from, &fromlen);
217
218         return(cc);
219 }
220
221 #ifdef CONFIG_FEATURE_TRACEROUTE_VERBOSE
222 /*
223  * Convert an ICMP "type" field to a printable string.
224  */
225 static inline const char *
226 pr_type(u_char t)
227 {
228         static const char * const ttab[] = {
229         "Echo Reply",   "ICMP 1",       "ICMP 2",       "Dest Unreachable",
230         "Source Quench", "Redirect",    "ICMP 6",       "ICMP 7",
231         "Echo",         "ICMP 9",       "ICMP 10",      "Time Exceeded",
232         "Param Problem", "Timestamp",   "Timestamp Reply", "Info Request",
233         "Info Reply"
234         };
235
236         if(t > 16)
237                 return("OUT-OF-RANGE");
238
239         return(ttab[t]);
240 }
241 #endif
242
243 static inline int
244 packet_ok(u_char *buf, int cc, struct sockaddr_in *from, int seq)
245 {
246         struct icmp *icp;
247         u_char type, code;
248         int hlen;
249         struct ip *ip;
250
251         ip = (struct ip *) buf;
252         hlen = ip->ip_hl << 2;
253         if (cc < hlen + ICMP_MINLEN) {
254 #ifdef CONFIG_FEATURE_TRACEROUTE_VERBOSE
255                 if (verbose)
256                         printf("packet too short (%d bytes) from %s\n", cc,
257                                 inet_ntoa(from->sin_addr));
258 #endif
259                 return (0);
260         }
261         cc -= hlen;
262         icp = (struct icmp *)(buf + hlen);
263         type = icp->icmp_type; code = icp->icmp_code;
264         if ((type == ICMP_TIMXCEED && code == ICMP_TIMXCEED_INTRANS) ||
265             type == ICMP_UNREACH) {
266                 struct ip *hip;
267                 struct udphdr *up;
268
269                 hip = &icp->icmp_ip;
270                 hlen = hip->ip_hl << 2;
271                 up = (struct udphdr *)((u_char *)hip + hlen);
272                 if (hlen + 12 <= cc && hip->ip_p == IPPROTO_UDP &&
273                     up->source == htons(ident) &&
274                     up->dest == htons(port+seq))
275                         return (type == ICMP_TIMXCEED? -1 : code+1);
276         }
277 #ifdef CONFIG_FEATURE_TRACEROUTE_VERBOSE
278         if (verbose) {
279                 int i;
280                 u_long *lp = (u_long *)&icp->icmp_ip;
281
282                 printf("\n%d bytes from %s to %s: icmp type %d (%s) code %d\n",
283                         cc, inet_ntoa(from->sin_addr), inet_ntoa(ip->ip_dst),
284                         type, pr_type(type), icp->icmp_code);
285                 for (i = 4; i < cc ; i += sizeof(long))
286                         printf("%2d: x%8.8lx\n", i, *lp++);
287         }
288 #endif
289         return(0);
290 }
291
292 static void             /* not inline */
293 send_probe(int seq, int ttl)
294 {
295         struct opacket *op = outpacket;
296         struct ip *ip = &op->ip;
297         struct udphdr *up = &op->udp;
298         int i;
299         struct timezone tz;
300
301         ip->ip_off = 0;
302         ip->ip_hl = sizeof(*ip) >> 2;
303         ip->ip_p = IPPROTO_UDP;
304         ip->ip_len = datalen;
305         ip->ip_ttl = ttl;
306         ip->ip_v = IPVERSION;
307         ip->ip_id = htons(ident+seq);
308
309         up->source = htons(ident);
310         up->dest = htons(port+seq);
311         up->len = htons((u_short)(datalen - sizeof(struct ip)));
312         up->check = 0;
313
314         op->seq = seq;
315         op->ttl = ttl;
316         (void) gettimeofday(&op->tv, &tz);
317
318         i = sendto(sndsock, (char *)outpacket, datalen, 0, &whereto,
319                    sizeof(struct sockaddr));
320         if (i < 0 || i != datalen)  {
321                 if (i<0)
322                         perror("sendto");
323                 printf("traceroute: wrote %s %d chars, ret=%d\n", hostname,
324                         datalen, i);
325                 (void) fflush(stdout);
326         }
327 }
328
329
330 int
331 #ifndef CONFIG_TRACEROUTE
332 main(int argc, char *argv[])
333 #else
334 traceroute_main(int argc, char *argv[])
335 #endif
336 {
337         extern char *optarg;
338         extern int optind;
339         struct hostent *hp;
340         struct sockaddr_in from, *to;
341         int ch, i, on, probe, seq, tos, ttl;
342
343         int options = 0;                /* socket options */
344         char *source = 0;
345         int nprobes = 3;
346
347         on = 1;
348         seq = tos = 0;
349         to = (struct sockaddr_in *)&whereto;
350         while ((ch = getopt(argc, argv, "dm:np:q:rs:t:w:v")) != EOF)
351                 switch(ch) {
352                 case 'd':
353 #ifdef CONFIG_FEATURE_TRACEROUTE_SO_DEBUG
354                         options |= SO_DEBUG;
355 #endif
356                         break;
357                 case 'm':
358                         max_ttl = atoi(optarg);
359                         if (max_ttl <= 1)
360                                 bb_error_msg_and_die("max ttl must be >1.");
361                         break;
362                 case 'n':
363                         nflag++;
364                         break;
365                 case 'p':
366                         port = atoi(optarg);
367                         if (port < 1)
368                                 bb_error_msg_and_die("port must be >0.");
369                         break;
370                 case 'q':
371                         nprobes = atoi(optarg);
372                         if (nprobes < 1)
373                                 bb_error_msg_and_die("nprobes must be >0.");
374                         break;
375                 case 'r':
376                         options |= SO_DONTROUTE;
377                         break;
378                 case 's':
379                         /*
380                          * set the ip source address of the outbound
381                          * probe (e.g., on a multi-homed host).
382                          */
383                         source = optarg;
384                         break;
385                 case 't':
386                         tos = atoi(optarg);
387                         if (tos < 0 || tos > 255)
388                                 bb_error_msg_and_die("tos must be 0 to 255.");
389                         break;
390                 case 'v':
391 #ifdef CONFIG_FEATURE_TRACEROUTE_VERBOSE
392                         verbose++;
393 #endif
394                         break;
395                 case 'w':
396                         waittime = atoi(optarg);
397                         if (waittime <= 1)
398                                 bb_error_msg_and_die("wait must be >1 sec.");
399                         break;
400                 default:
401                         bb_show_usage();
402                 }
403         argc -= optind;
404         argv += optind;
405
406         if (argc < 1)
407                 bb_show_usage();
408
409         setlinebuf (stdout);
410
411         memset(&whereto, 0, sizeof(struct sockaddr));
412         hp = xgethostbyname(*argv);
413                         to->sin_family = hp->h_addrtype;
414         memcpy(&to->sin_addr, hp->h_addr, hp->h_length);
415                         hostname = (char *)hp->h_name;
416         if (*++argv)
417                 datalen = atoi(*argv);
418         if (datalen < 0 || datalen >= MAXPACKET - sizeof(struct opacket))
419                 bb_error_msg_and_die("packet size must be 0 <= s < %d.",
420                     MAXPACKET - sizeof(struct opacket));
421         datalen += sizeof(struct opacket);
422         outpacket = (struct opacket *)xmalloc((unsigned)datalen);
423         memset(outpacket, 0, datalen);
424         outpacket->ip.ip_dst = to->sin_addr;
425         outpacket->ip.ip_tos = tos;
426         outpacket->ip.ip_v = IPVERSION;
427         outpacket->ip.ip_id = 0;
428
429         ident = (getpid() & 0xffff) | 0x8000;
430
431         if ((sndsock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0)
432                 bb_perror_msg_and_die(bb_msg_can_not_create_raw_socket);
433
434         s = create_icmp_socket();
435
436 #ifdef CONFIG_FEATURE_TRACEROUTE_SO_DEBUG
437         if (options & SO_DEBUG)
438                 (void) setsockopt(s, SOL_SOCKET, SO_DEBUG,
439                                   (char *)&on, sizeof(on));
440 #endif
441         if (options & SO_DONTROUTE)
442                 (void) setsockopt(s, SOL_SOCKET, SO_DONTROUTE,
443                                   (char *)&on, sizeof(on));
444 #ifdef SO_SNDBUF
445         if (setsockopt(sndsock, SOL_SOCKET, SO_SNDBUF, (char *)&datalen,
446                        sizeof(datalen)) < 0)
447                 bb_perror_msg_and_die("SO_SNDBUF");
448 #endif
449 #ifdef IP_HDRINCL
450         if (setsockopt(sndsock, IPPROTO_IP, IP_HDRINCL, (char *)&on,
451                        sizeof(on)) < 0)
452                 bb_perror_msg_and_die("IP_HDRINCL");
453 #endif
454 #ifdef CONFIG_FEATURE_TRACEROUTE_SO_DEBUG
455         if (options & SO_DEBUG)
456                 (void) setsockopt(sndsock, SOL_SOCKET, SO_DEBUG,
457                                   (char *)&on, sizeof(on));
458 #endif
459         if (options & SO_DONTROUTE)
460                 (void) setsockopt(sndsock, SOL_SOCKET, SO_DONTROUTE,
461                                   (char *)&on, sizeof(on));
462
463         if (source) {
464                 memset(&from, 0, sizeof(struct sockaddr));
465                 from.sin_family = AF_INET;
466                 from.sin_addr.s_addr = inet_addr(source);
467                 if (from.sin_addr.s_addr == -1)
468                         bb_error_msg_and_die("unknown host %s", source);
469                 outpacket->ip.ip_src = from.sin_addr;
470 #ifndef IP_HDRINCL
471                 if (bind(sndsock, (struct sockaddr *)&from, sizeof(from)) < 0)
472                         bb_perror_msg_and_die("bind");
473 #endif
474         }
475
476         fprintf(stderr, "traceroute to %s (%s)", hostname,
477                 inet_ntoa(to->sin_addr));
478         if (source)
479                 fprintf(stderr, " from %s", source);
480         fprintf(stderr, ", %d hops max, %d byte packets\n", max_ttl, datalen);
481
482         for (ttl = 1; ttl <= max_ttl; ++ttl) {
483                 u_long lastaddr = 0;
484                 int got_there = 0;
485                 int unreachable = 0;
486
487                 printf("%2d ", ttl);
488                 for (probe = 0; probe < nprobes; ++probe) {
489                         int cc, reset_timer;
490                         struct timeval t1, t2;
491                         struct timezone tz;
492                         struct ip *ip;
493
494                         (void) gettimeofday(&t1, &tz);
495                         send_probe(++seq, ttl);
496                         reset_timer = 1;
497                         while ((cc = wait_for_reply(s, &from, reset_timer)) != 0) {
498                                 (void) gettimeofday(&t2, &tz);
499                                 if ((i = packet_ok(packet, cc, &from, seq))) {
500                                         reset_timer = 1;
501                                         if (from.sin_addr.s_addr != lastaddr) {
502                                                 print(packet, cc, &from);
503                                                 lastaddr = from.sin_addr.s_addr;
504                                         }
505                                         printf("  %g ms", deltaT(&t1, &t2));
506                                         switch(i - 1) {
507                                         case ICMP_UNREACH_PORT:
508                                                 ip = (struct ip *)packet;
509                                                 if (ip->ip_ttl <= 1)
510                                                         printf(" !");
511                                                 ++got_there;
512                                                 break;
513                                         case ICMP_UNREACH_NET:
514                                                 ++unreachable;
515                                                 printf(" !N");
516                                                 break;
517                                         case ICMP_UNREACH_HOST:
518                                                 ++unreachable;
519                                                 printf(" !H");
520                                                 break;
521                                         case ICMP_UNREACH_PROTOCOL:
522                                                 ++got_there;
523                                                 printf(" !P");
524                                                 break;
525                                         case ICMP_UNREACH_NEEDFRAG:
526                                                 ++unreachable;
527                                                 printf(" !F");
528                                                 break;
529                                         case ICMP_UNREACH_SRCFAIL:
530                                                 ++unreachable;
531                                                 printf(" !S");
532                                                 break;
533                                         }
534                                         break;
535                                 } else
536                                         reset_timer = 0;
537                         }
538                         if (cc == 0)
539                                 printf(" *");
540                         (void) fflush(stdout);
541                 }
542                 putchar('\n');
543                 if (got_there || unreachable >= nprobes-1)
544                         return 0;
545         }
546
547         return 0;
548 }