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