Off by one error in max_leases sanity check. Bug 349, apparently.
[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 #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                                        /* last inbound (icmp) packet */
105 static u_char  packet[512] __attribute__ ((aligned));
106 static struct opacket  *outpacket;     /* last output (udp) packet */
107
108 static int s;                          /* receive (icmp) socket file descriptor */
109 static int sndsock;                    /* send (udp) socket file descriptor */
110
111 static struct sockaddr whereto;        /* Who to try to reach */
112 static int datalen;                    /* How much data */
113
114 static char *hostname;
115
116 static int max_ttl = 30;
117 static u_short ident;
118 static u_short port = 32768+666;       /* start udp dest port # for probe packets */
119
120 #ifdef CONFIG_FEATURE_TRACEROUTE_VERBOSE
121 static int verbose;
122 #endif
123 static int waittime = 5;               /* time to wait for response (in seconds) */
124 static int nflag;                      /* print addresses numerically */
125
126 /*
127  * Construct an Internet address representation.
128  * If the nflag has been supplied, give
129  * numeric value, otherwise try for symbolic name.
130  */
131 static inline void
132 inetname(struct sockaddr_in *from)
133 {
134         char *cp;
135         static char domain[MAXHOSTNAMELEN + 1];
136         char name[MAXHOSTNAMELEN + 1];
137         static int first = 1;
138         const char *ina;
139
140         if (first && !nflag) {
141                 first = 0;
142                 if (getdomainname(domain, MAXHOSTNAMELEN) != 0)
143                         domain[0] = 0;
144         }
145         cp = 0;
146         if (!nflag && from->sin_addr.s_addr != INADDR_ANY) {
147                 if(INET_rresolve(name, sizeof(name), from, 0x4000, 0xffffffff) >= 0) {
148                         if ((cp = strchr(name, '.')) &&
149                             !strcmp(cp + 1, domain))
150                                 *cp = 0;
151                         cp = (char *)name;
152                 }
153         }
154         ina = inet_ntoa(from->sin_addr);
155         if (nflag)
156                 printf(" %s", ina);
157         else
158                 printf(" %s (%s)", (cp ? cp : ina), ina);
159 }
160
161 static inline void
162 print(u_char *buf, int cc, struct sockaddr_in *from)
163 {
164         struct ip *ip;
165         int hlen;
166
167         ip = (struct ip *) buf;
168         hlen = ip->ip_hl << 2;
169         cc -= hlen;
170
171         inetname(from);
172 #ifdef CONFIG_FEATURE_TRACEROUTE_VERBOSE
173         if (verbose)
174                 printf (" %d bytes to %s", cc, inet_ntoa (ip->ip_dst));
175 #endif
176 }
177
178 static inline double
179 deltaT(struct timeval *t1p, struct timeval *t2p)
180 {
181         double dt;
182
183         dt = (double)(t2p->tv_sec - t1p->tv_sec) * 1000.0 +
184              (double)(t2p->tv_usec - t1p->tv_usec) / 1000.0;
185         return (dt);
186 }
187
188 static inline int
189 wait_for_reply(int sock, struct sockaddr_in *from, int reset_timer)
190 {
191         fd_set fds;
192         static struct timeval wait;
193         int cc = 0;
194         int fromlen = sizeof (*from);
195
196         FD_ZERO(&fds);
197         FD_SET(sock, &fds);
198         if (reset_timer) {
199                 /*
200                  * traceroute could hang if someone else has a ping
201                  * running and our ICMP reply gets dropped but we don't
202                  * realize it because we keep waking up to handle those
203                  * other ICMP packets that keep coming in.  To fix this,
204                  * "reset_timer" will only be true if the last packet that
205                  * came in was for us or if this is the first time we're
206                  * waiting for a reply since sending out a probe.  Note
207                  * that this takes advantage of the select() feature on
208                  * Linux where the remaining timeout is written to the
209                  * struct timeval area.
210                  */
211                 wait.tv_sec = waittime;
212                 wait.tv_usec = 0;
213         }
214
215         if (select(sock+1, &fds, (fd_set *)0, (fd_set *)0, &wait) > 0)
216                 cc=recvfrom(s, (char *)packet, sizeof(packet), 0,
217                             (struct sockaddr *)from, &fromlen);
218
219         return(cc);
220 }
221
222 #ifdef CONFIG_FEATURE_TRACEROUTE_VERBOSE
223 /*
224  * Convert an ICMP "type" field to a printable string.
225  */
226 static inline const char *
227 pr_type(u_char t)
228 {
229         static const char * const ttab[] = {
230         "Echo Reply",   "ICMP 1",       "ICMP 2",       "Dest Unreachable",
231         "Source Quench", "Redirect",    "ICMP 6",       "ICMP 7",
232         "Echo",         "ICMP 9",       "ICMP 10",      "Time Exceeded",
233         "Param Problem", "Timestamp",   "Timestamp Reply", "Info Request",
234         "Info Reply"
235         };
236
237         if(t > 16)
238                 return("OUT-OF-RANGE");
239
240         return(ttab[t]);
241 }
242 #endif
243
244 static inline int
245 packet_ok(u_char *buf, int cc, struct sockaddr_in *from, int seq)
246 {
247         struct icmp *icp;
248         u_char type, code;
249         int hlen;
250         struct ip *ip;
251
252         ip = (struct ip *) buf;
253         hlen = ip->ip_hl << 2;
254         if (cc < hlen + ICMP_MINLEN) {
255 #ifdef CONFIG_FEATURE_TRACEROUTE_VERBOSE
256                 if (verbose)
257                         printf("packet too short (%d bytes) from %s\n", cc,
258                                 inet_ntoa(from->sin_addr));
259 #endif
260                 return (0);
261         }
262         cc -= hlen;
263         icp = (struct icmp *)(buf + hlen);
264         type = icp->icmp_type; code = icp->icmp_code;
265         if ((type == ICMP_TIMXCEED && code == ICMP_TIMXCEED_INTRANS) ||
266             type == ICMP_UNREACH) {
267                 struct ip *hip;
268                 struct udphdr *up;
269
270                 hip = &icp->icmp_ip;
271                 hlen = hip->ip_hl << 2;
272                 up = (struct udphdr *)((u_char *)hip + hlen);
273                 if (hlen + 12 <= cc && hip->ip_p == IPPROTO_UDP &&
274                     up->source == htons(ident) &&
275                     up->dest == htons(port+seq))
276                         return (type == ICMP_TIMXCEED? -1 : code+1);
277         }
278 #ifdef CONFIG_FEATURE_TRACEROUTE_VERBOSE
279         if (verbose) {
280                 int i;
281                 u_long *lp = (u_long *)&icp->icmp_ip;
282
283                 printf("\n%d bytes from %s to %s: icmp type %d (%s) code %d\n",
284                         cc, inet_ntoa(from->sin_addr), inet_ntoa(ip->ip_dst),
285                         type, pr_type(type), icp->icmp_code);
286                 for (i = 4; i < cc ; i += sizeof(long))
287                         printf("%2d: x%8.8lx\n", i, *lp++);
288         }
289 #endif
290         return(0);
291 }
292
293 static void             /* not inline */
294 send_probe(int seq, int ttl)
295 {
296         struct opacket *op = outpacket;
297         struct ip *ip = &op->ip;
298         struct udphdr *up = &op->udp;
299         int i;
300         struct timezone tz;
301
302         ip->ip_off = 0;
303         ip->ip_hl = sizeof(*ip) >> 2;
304         ip->ip_p = IPPROTO_UDP;
305         ip->ip_len = datalen;
306         ip->ip_ttl = ttl;
307         ip->ip_v = IPVERSION;
308         ip->ip_id = htons(ident+seq);
309
310         up->source = htons(ident);
311         up->dest = htons(port+seq);
312         up->len = htons((u_short)(datalen - sizeof(struct ip)));
313         up->check = 0;
314
315         op->seq = seq;
316         op->ttl = ttl;
317         (void) gettimeofday(&op->tv, &tz);
318
319         i = sendto(sndsock, (char *)outpacket, datalen, 0, &whereto,
320                    sizeof(struct sockaddr));
321         if (i < 0 || i != datalen)  {
322                 if (i<0)
323                         perror("sendto");
324                 printf("traceroute: wrote %s %d chars, ret=%d\n", hostname,
325                         datalen, i);
326                 (void) fflush(stdout);
327         }
328 }
329
330
331 int
332 #ifndef CONFIG_TRACEROUTE
333 main(int argc, char *argv[])
334 #else
335 traceroute_main(int argc, char *argv[])
336 #endif
337 {
338         extern char *optarg;
339         extern int optind;
340         struct hostent *hp;
341         struct sockaddr_in from, *to;
342         int ch, i, on, probe, seq, tos, ttl;
343
344         int options = 0;                /* socket options */
345         char *source = 0;
346         int nprobes = 3;
347
348         on = 1;
349         seq = tos = 0;
350         to = (struct sockaddr_in *)&whereto;
351         while ((ch = getopt(argc, argv, "dm:np:q:rs:t:w:v")) != EOF)
352                 switch(ch) {
353                 case 'd':
354 #ifdef CONFIG_FEATURE_TRACEROUTE_SO_DEBUG
355                         options |= SO_DEBUG;
356 #endif
357                         break;
358                 case 'm':
359                         max_ttl = atoi(optarg);
360                         if (max_ttl <= 1)
361                                 bb_error_msg_and_die("max ttl must be >1.");
362                         break;
363                 case 'n':
364                         nflag++;
365                         break;
366                 case 'p':
367                         port = atoi(optarg);
368                         if (port < 1)
369                                 bb_error_msg_and_die("port must be >0.");
370                         break;
371                 case 'q':
372                         nprobes = atoi(optarg);
373                         if (nprobes < 1)
374                                 bb_error_msg_and_die("nprobes must be >0.");
375                         break;
376                 case 'r':
377                         options |= SO_DONTROUTE;
378                         break;
379                 case 's':
380                         /*
381                          * set the ip source address of the outbound
382                          * probe (e.g., on a multi-homed host).
383                          */
384                         source = optarg;
385                         break;
386                 case 't':
387                         tos = atoi(optarg);
388                         if (tos < 0 || tos > 255)
389                                 bb_error_msg_and_die("tos must be 0 to 255.");
390                         break;
391                 case 'v':
392 #ifdef CONFIG_FEATURE_TRACEROUTE_VERBOSE
393                         verbose++;
394 #endif
395                         break;
396                 case 'w':
397                         waittime = atoi(optarg);
398                         if (waittime <= 1)
399                                 bb_error_msg_and_die("wait must be >1 sec.");
400                         break;
401                 default:
402                         bb_show_usage();
403                 }
404         argc -= optind;
405         argv += optind;
406
407         if (argc < 1)
408                 bb_show_usage();
409
410         setlinebuf (stdout);
411
412         memset(&whereto, 0, sizeof(struct sockaddr));
413         hp = xgethostbyname(*argv);
414                         to->sin_family = hp->h_addrtype;
415         memcpy(&to->sin_addr, hp->h_addr, hp->h_length);
416                         hostname = (char *)hp->h_name;
417         if (*++argv)
418                 datalen = atoi(*argv);
419         if (datalen < 0 || datalen >= MAXPACKET - sizeof(struct opacket))
420                 bb_error_msg_and_die("packet size must be 0 <= s < %d.",
421                     (int)(MAXPACKET - sizeof(struct opacket)));
422         datalen += sizeof(struct opacket);
423         outpacket = (struct opacket *)xmalloc((unsigned)datalen);
424         memset(outpacket, 0, datalen);
425         outpacket->ip.ip_dst = to->sin_addr;
426         outpacket->ip.ip_tos = tos;
427         outpacket->ip.ip_v = IPVERSION;
428         outpacket->ip.ip_id = 0;
429
430         ident = (getpid() & 0xffff) | 0x8000;
431
432         if ((sndsock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0)
433                 bb_perror_msg_and_die(bb_msg_can_not_create_raw_socket);
434
435         s = create_icmp_socket();
436
437 #ifdef CONFIG_FEATURE_TRACEROUTE_SO_DEBUG
438         if (options & SO_DEBUG)
439                 (void) setsockopt(s, SOL_SOCKET, SO_DEBUG,
440                                   (char *)&on, sizeof(on));
441 #endif
442         if (options & SO_DONTROUTE)
443                 (void) setsockopt(s, SOL_SOCKET, SO_DONTROUTE,
444                                   (char *)&on, sizeof(on));
445 #ifdef SO_SNDBUF
446         if (setsockopt(sndsock, SOL_SOCKET, SO_SNDBUF, (char *)&datalen,
447                        sizeof(datalen)) < 0)
448                 bb_perror_msg_and_die("SO_SNDBUF");
449 #endif
450 #ifdef IP_HDRINCL
451         if (setsockopt(sndsock, IPPROTO_IP, IP_HDRINCL, (char *)&on,
452                        sizeof(on)) < 0)
453                 bb_perror_msg_and_die("IP_HDRINCL");
454 #endif
455 #ifdef CONFIG_FEATURE_TRACEROUTE_SO_DEBUG
456         if (options & SO_DEBUG)
457                 (void) setsockopt(sndsock, SOL_SOCKET, SO_DEBUG,
458                                   (char *)&on, sizeof(on));
459 #endif
460         if (options & SO_DONTROUTE)
461                 (void) setsockopt(sndsock, SOL_SOCKET, SO_DONTROUTE,
462                                   (char *)&on, sizeof(on));
463
464         if (source) {
465                 memset(&from, 0, sizeof(struct sockaddr));
466                 from.sin_family = AF_INET;
467                 from.sin_addr.s_addr = inet_addr(source);
468                 if (from.sin_addr.s_addr == -1)
469                         bb_error_msg_and_die("unknown host %s", source);
470                 outpacket->ip.ip_src = from.sin_addr;
471 #ifndef IP_HDRINCL
472                 if (bind(sndsock, (struct sockaddr *)&from, sizeof(from)) < 0)
473                         bb_perror_msg_and_die("bind");
474 #endif
475         }
476
477         fprintf(stderr, "traceroute to %s (%s)", hostname,
478                 inet_ntoa(to->sin_addr));
479         if (source)
480                 fprintf(stderr, " from %s", source);
481         fprintf(stderr, ", %d hops max, %d byte packets\n", max_ttl, datalen);
482
483         for (ttl = 1; ttl <= max_ttl; ++ttl) {
484                 u_long lastaddr = 0;
485                 int got_there = 0;
486                 int unreachable = 0;
487
488                 printf("%2d ", ttl);
489                 for (probe = 0; probe < nprobes; ++probe) {
490                         int cc, reset_timer;
491                         struct timeval t1, t2;
492                         struct timezone tz;
493                         struct ip *ip;
494
495                         (void) gettimeofday(&t1, &tz);
496                         send_probe(++seq, ttl);
497                         reset_timer = 1;
498                         while ((cc = wait_for_reply(s, &from, reset_timer)) != 0) {
499                                 (void) gettimeofday(&t2, &tz);
500                                 if ((i = packet_ok(packet, cc, &from, seq))) {
501                                         reset_timer = 1;
502                                         if (from.sin_addr.s_addr != lastaddr) {
503                                                 print(packet, cc, &from);
504                                                 lastaddr = from.sin_addr.s_addr;
505                                         }
506                                         printf("  %g ms", deltaT(&t1, &t2));
507                                         switch(i - 1) {
508                                         case ICMP_UNREACH_PORT:
509                                                 ip = (struct ip *)packet;
510                                                 if (ip->ip_ttl <= 1)
511                                                         printf(" !");
512                                                 ++got_there;
513                                                 break;
514                                         case ICMP_UNREACH_NET:
515                                                 ++unreachable;
516                                                 printf(" !N");
517                                                 break;
518                                         case ICMP_UNREACH_HOST:
519                                                 ++unreachable;
520                                                 printf(" !H");
521                                                 break;
522                                         case ICMP_UNREACH_PROTOCOL:
523                                                 ++got_there;
524                                                 printf(" !P");
525                                                 break;
526                                         case ICMP_UNREACH_NEEDFRAG:
527                                                 ++unreachable;
528                                                 printf(" !F");
529                                                 break;
530                                         case ICMP_UNREACH_SRCFAIL:
531                                                 ++unreachable;
532                                                 printf(" !S");
533                                                 break;
534                                         }
535                                         break;
536                                 } else
537                                         reset_timer = 0;
538                         }
539                         if (cc == 0)
540                                 printf(" *");
541                         (void) fflush(stdout);
542                 }
543                 putchar('\n');
544                 if (got_there || unreachable >= nprobes-1)
545                         return 0;
546         }
547
548         return 0;
549 }