applying:
[oweals/busybox.git] / 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 #define MAXPACKET_ICMP  512
81 #ifndef MAXHOSTNAMELEN
82 #define MAXHOSTNAMELEN  64
83 #endif
84
85 /*
86  * format of a (udp) probe packet.
87  */
88 struct opacket {
89         struct ip ip;
90         struct udphdr udp;
91         u_char seq;             /* sequence number of this packet */
92         u_char ttl;             /* ttl packet left with */
93         struct timeval tv;      /* time packet left */
94 };
95
96 /*
97  * Definitions for internet protocol version 4.
98  * Per RFC 791, September 1981.
99  */
100 #define IPVERSION       4
101
102
103 #include "busybox.h"
104
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, u_char *packet, int size)
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, size, 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         u_char *packet;
343
344         int options = 0;                /* socket options */
345         char *source = 0;
346         int nprobes = 3;
347         packet = xmalloc (MAXPACKET_ICMP);
348
349         on = 1;
350         seq = tos = 0;
351         to = (struct sockaddr_in *)&whereto;
352         while ((ch = getopt(argc, argv, "dm:np:q:rs:t:w:v")) != EOF)
353                 switch(ch) {
354                 case 'd':
355 #ifdef CONFIG_FEATURE_TRACEROUTE_SO_DEBUG
356                         options |= SO_DEBUG;
357 #endif
358                         break;
359                 case 'm':
360                         max_ttl = atoi(optarg);
361                         if (max_ttl <= 1)
362                                 bb_error_msg_and_die("max ttl must be >1.");
363                         break;
364                 case 'n':
365                         nflag++;
366                         break;
367                 case 'p':
368                         port = atoi(optarg);
369                         if (port < 1)
370                                 bb_error_msg_and_die("port must be >0.");
371                         break;
372                 case 'q':
373                         nprobes = atoi(optarg);
374                         if (nprobes < 1)
375                                 bb_error_msg_and_die("nprobes must be >0.");
376                         break;
377                 case 'r':
378                         options |= SO_DONTROUTE;
379                         break;
380                 case 's':
381                         /*
382                          * set the ip source address of the outbound
383                          * probe (e.g., on a multi-homed host).
384                          */
385                         source = optarg;
386                         break;
387                 case 't':
388                         tos = atoi(optarg);
389                         if (tos < 0 || tos > 255)
390                                 bb_error_msg_and_die("tos must be 0 to 255.");
391                         break;
392                 case 'v':
393 #ifdef CONFIG_FEATURE_TRACEROUTE_VERBOSE
394                         verbose++;
395 #endif
396                         break;
397                 case 'w':
398                         waittime = atoi(optarg);
399                         if (waittime <= 1)
400                                 bb_error_msg_and_die("wait must be >1 sec.");
401                         break;
402                 default:
403                         bb_show_usage();
404                 }
405         argc -= optind;
406         argv += optind;
407
408         if (argc < 1)
409                 bb_show_usage();
410
411         setlinebuf (stdout);
412
413         memset(&whereto, 0, sizeof(struct sockaddr));
414         hp = xgethostbyname(*argv);
415                         to->sin_family = hp->h_addrtype;
416         memcpy(&to->sin_addr, hp->h_addr, hp->h_length);
417                         hostname = (char *)hp->h_name;
418         if (*++argv)
419                 datalen = atoi(*argv);
420         if (datalen < 0 || datalen >= MAXPACKET - sizeof(struct opacket))
421                 bb_error_msg_and_die("packet size must be 0 <= s < %d.",
422                     MAXPACKET - sizeof(struct opacket));
423         datalen += sizeof(struct opacket);
424         outpacket = (struct opacket *)xmalloc((unsigned)datalen);
425         memset(outpacket, 0, datalen);
426         outpacket->ip.ip_dst = to->sin_addr;
427         outpacket->ip.ip_tos = tos;
428         outpacket->ip.ip_v = IPVERSION;
429         outpacket->ip.ip_id = 0;
430
431         ident = (getpid() & 0xffff) | 0x8000;
432
433         if ((sndsock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0)
434                 bb_perror_msg_and_die(bb_msg_can_not_create_raw_socket);
435
436         s = create_icmp_socket();
437
438 #ifdef CONFIG_FEATURE_TRACEROUTE_SO_DEBUG
439         if (options & SO_DEBUG)
440                 (void) setsockopt(s, SOL_SOCKET, SO_DEBUG,
441                                   (char *)&on, sizeof(on));
442 #endif
443         if (options & SO_DONTROUTE)
444                 (void) setsockopt(s, SOL_SOCKET, SO_DONTROUTE,
445                                   (char *)&on, sizeof(on));
446 #ifdef SO_SNDBUF
447         if (setsockopt(sndsock, SOL_SOCKET, SO_SNDBUF, (char *)&datalen,
448                        sizeof(datalen)) < 0)
449                 bb_perror_msg_and_die("SO_SNDBUF");
450 #endif
451 #ifdef IP_HDRINCL
452         if (setsockopt(sndsock, IPPROTO_IP, IP_HDRINCL, (char *)&on,
453                        sizeof(on)) < 0)
454                 bb_perror_msg_and_die("IP_HDRINCL");
455 #endif
456 #ifdef CONFIG_FEATURE_TRACEROUTE_SO_DEBUG
457         if (options & SO_DEBUG)
458                 (void) setsockopt(sndsock, SOL_SOCKET, SO_DEBUG,
459                                   (char *)&on, sizeof(on));
460 #endif
461         if (options & SO_DONTROUTE)
462                 (void) setsockopt(sndsock, SOL_SOCKET, SO_DONTROUTE,
463                                   (char *)&on, sizeof(on));
464
465         if (source) {
466                 memset(&from, 0, sizeof(struct sockaddr));
467                 from.sin_family = AF_INET;
468                 from.sin_addr.s_addr = inet_addr(source);
469                 if (from.sin_addr.s_addr == -1)
470                         bb_error_msg_and_die("unknown host %s", source);
471                 outpacket->ip.ip_src = from.sin_addr;
472 #ifndef IP_HDRINCL
473                 if (bind(sndsock, (struct sockaddr *)&from, sizeof(from)) < 0)
474                         bb_perror_msg_and_die("bind");
475 #endif
476         }
477
478         fprintf(stderr, "traceroute to %s (%s)", hostname,
479                 inet_ntoa(to->sin_addr));
480         if (source)
481                 fprintf(stderr, " from %s", source);
482         fprintf(stderr, ", %d hops max, %d byte packets\n", max_ttl, datalen);
483
484         for (ttl = 1; ttl <= max_ttl; ++ttl) {
485                 u_long lastaddr = 0;
486                 int got_there = 0;
487                 int unreachable = 0;
488
489                 printf("%2d ", ttl);
490                 for (probe = 0; probe < nprobes; ++probe) {
491                         int cc, reset_timer;
492                         struct timeval t1, t2;
493                         struct timezone tz;
494                         struct ip *ip;
495
496                         (void) gettimeofday(&t1, &tz);
497                         send_probe(++seq, ttl);
498                         reset_timer = 1;
499                         while ((cc = wait_for_reply(s, &from, reset_timer, packet, MAXPACKET_ICMP)) != 0) {
500                                 (void) gettimeofday(&t2, &tz);
501                                 if ((i = packet_ok(packet, cc, &from, seq))) {
502                                         reset_timer = 1;
503                                         if (from.sin_addr.s_addr != lastaddr) {
504                                                 print(packet, cc, &from);
505                                                 lastaddr = from.sin_addr.s_addr;
506                                         }
507                                         printf("  %g ms", deltaT(&t1, &t2));
508                                         switch(i - 1) {
509                                         case ICMP_UNREACH_PORT:
510                                                 ip = (struct ip *)packet;
511                                                 if (ip->ip_ttl <= 1)
512                                                         printf(" !");
513                                                 ++got_there;
514                                                 break;
515                                         case ICMP_UNREACH_NET:
516                                                 ++unreachable;
517                                                 printf(" !N");
518                                                 break;
519                                         case ICMP_UNREACH_HOST:
520                                                 ++unreachable;
521                                                 printf(" !H");
522                                                 break;
523                                         case ICMP_UNREACH_PROTOCOL:
524                                                 ++got_there;
525                                                 printf(" !P");
526                                                 break;
527                                         case ICMP_UNREACH_NEEDFRAG:
528                                                 ++unreachable;
529                                                 printf(" !F");
530                                                 break;
531                                         case ICMP_UNREACH_SRCFAIL:
532                                                 ++unreachable;
533                                                 printf(" !S");
534                                                 break;
535                                         }
536                                         break;
537                                 } else
538                                         reset_timer = 0;
539                         }
540                         if (cc == 0)
541                                 printf(" *");
542                         (void) fflush(stdout);
543                 }
544                 putchar('\n');
545                 if (got_there || unreachable >= nprobes-1)
546                         return 0;
547         }
548
549         return 0;
550 }