blob: a3af5f698fe5c87e441ce33d3e5894b39a7ab748 [file] [log] [blame]
Eric Andersen5c58d282001-07-10 16:29:00 +00001/*-
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 *
Eric Andersen7467c8d2001-07-12 20:26:32 +00008 * Special for busybox ported by Vladimir Oleynik <dzo@simtreas.ru> 2001
Eric Andersen5c58d282001-07-10 16:29:00 +00009 * 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
Eric Andersen7467c8d2001-07-12 20:26:32 +000065#undef BB_FEATURE_TRACEROUTE_VERBOSE
66//#define BB_FEATURE_TRACEROUTE_VERBOSE
67#undef BB_FEATURE_TRACEROUTE_SO_DEBUG /* not in documentation man */
68
Eric Andersen5c58d282001-07-10 16:29:00 +000069#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 */
91struct 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
Eric Andersen5c58d282001-07-10 16:29:00 +0000108static u_char packet[512]; /* last inbound (icmp) packet */
109static struct opacket *outpacket; /* last output (udp) packet */
110
111static int s; /* receive (icmp) socket file descriptor */
112static int sndsock; /* send (udp) socket file descriptor */
113
114static struct sockaddr whereto; /* Who to try to reach */
115static int datalen; /* How much data */
116
117static char *hostname;
118
119static int max_ttl = 30;
120static u_short ident;
121static u_short port = 32768+666; /* start udp dest port # for probe packets */
122
Eric Andersen7467c8d2001-07-12 20:26:32 +0000123#ifdef BB_FEATURE_TRACEROUTE_VERBOSE
Eric Andersen5c58d282001-07-10 16:29:00 +0000124static int verbose;
Eric Andersen7467c8d2001-07-12 20:26:32 +0000125#endif
Eric Andersen5c58d282001-07-10 16:29:00 +0000126static int waittime = 5; /* time to wait for response (in seconds) */
127static int nflag; /* print addresses numerically */
128
Eric Andersen7467c8d2001-07-12 20:26:32 +0000129/*
130 * Construct an Internet address representation.
131 * If the nflag has been supplied, give
132 * numeric value, otherwise try for symbolic name.
133 */
Eric Andersen044228d2001-07-17 01:12:36 +0000134static inline void
135inetname(struct sockaddr_in *from)
Eric Andersen7467c8d2001-07-12 20:26:32 +0000136{
137 char *cp;
Eric Andersen7467c8d2001-07-12 20:26:32 +0000138 struct hostent *hp;
139 static char domain[MAXHOSTNAMELEN + 1];
140 static int first = 1;
Eric Andersen044228d2001-07-17 01:12:36 +0000141 const char *ina;
Eric Andersen7467c8d2001-07-12 20:26:32 +0000142
143 if (first && !nflag) {
144 first = 0;
145 if (gethostname(domain, MAXHOSTNAMELEN) == 0 &&
Eric Andersen044228d2001-07-17 01:12:36 +0000146 (cp = strchr(domain, '.')))
Eric Andersen7467c8d2001-07-12 20:26:32 +0000147 (void) strcpy(domain, cp + 1);
148 else
149 domain[0] = 0;
150 }
151 cp = 0;
Eric Andersen044228d2001-07-17 01:12:36 +0000152 if (!nflag && from->sin_addr.s_addr != INADDR_ANY) {
153 hp = gethostbyaddr((char *)&(from->sin_addr), sizeof (from->sin_addr), AF_INET);
Eric Andersen7467c8d2001-07-12 20:26:32 +0000154 if (hp) {
Eric Andersen044228d2001-07-17 01:12:36 +0000155 if ((cp = strchr(hp->h_name, '.')) &&
Eric Andersen7467c8d2001-07-12 20:26:32 +0000156 !strcmp(cp + 1, domain))
157 *cp = 0;
158 cp = (char *)hp->h_name;
159 }
160 }
Eric Andersen044228d2001-07-17 01:12:36 +0000161 ina = inet_ntoa(from->sin_addr);
162 if (nflag)
163 printf(" %s", ina);
164 else
165 printf(" %s (%s)", (cp ? cp : ina), ina);
Eric Andersen7467c8d2001-07-12 20:26:32 +0000166}
167
168static inline void
169print(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
Eric Andersen044228d2001-07-17 01:12:36 +0000178 inetname(from);
Eric Andersen7467c8d2001-07-12 20:26:32 +0000179#ifdef BB_FEATURE_TRACEROUTE_VERBOSE
180 if (verbose)
181 printf (" %d bytes to %s", cc, inet_ntoa (ip->ip_dst));
182#endif
183}
184
185static inline double
186deltaT(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
195static inline int
196wait_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 */
233static inline const char *
234pr_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
252static inline int
253packet_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
301static void /* not inline */
302send_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
Eric Andersen5c58d282001-07-10 16:29:00 +0000339int
340#ifndef BB_TRACEROUTE
341main(argc, argv)
342#else
343traceroute_main(argc, argv)
344#endif
345 int argc;
346 char *argv[];
347{
348 extern char *optarg;
349 extern int optind;
350 struct hostent *hp;
Eric Andersen5c58d282001-07-10 16:29:00 +0000351 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':
Eric Andersen7467c8d2001-07-12 20:26:32 +0000364#ifdef BB_FEATURE_TRACEROUTE_SO_DEBUG
Eric Andersen5c58d282001-07-10 16:29:00 +0000365 options |= SO_DEBUG;
Eric Andersen7467c8d2001-07-12 20:26:32 +0000366#endif
Eric Andersen5c58d282001-07-10 16:29:00 +0000367 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':
Eric Andersen7467c8d2001-07-12 20:26:32 +0000402#ifdef BB_FEATURE_TRACEROUTE_VERBOSE
Eric Andersen5c58d282001-07-10 16:29:00 +0000403 verbose++;
Eric Andersen7467c8d2001-07-12 20:26:32 +0000404#endif
Eric Andersen5c58d282001-07-10 16:29:00 +0000405 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
Eric Andersen7467c8d2001-07-12 20:26:32 +0000422 memset(&whereto, 0, sizeof(struct sockaddr));
423 hp = xgethostbyname(*argv);
Eric Andersen5c58d282001-07-10 16:29:00 +0000424 to->sin_family = hp->h_addrtype;
Eric Andersen7467c8d2001-07-12 20:26:32 +0000425 memcpy(&to->sin_addr, hp->h_addr, hp->h_length);
Eric Andersen5c58d282001-07-10 16:29:00 +0000426 hostname = (char *)hp->h_name;
Eric Andersen5c58d282001-07-10 16:29:00 +0000427 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);
Eric Andersen7467c8d2001-07-12 20:26:32 +0000434 memset(outpacket, 0, datalen);
Eric Andersen5c58d282001-07-10 16:29:00 +0000435 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
Eric Andersen7467c8d2001-07-12 20:26:32 +0000442 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
Eric Andersen5c58d282001-07-10 16:29:00 +0000448 if (options & SO_DEBUG)
449 (void) setsockopt(s, SOL_SOCKET, SO_DEBUG,
450 (char *)&on, sizeof(on));
Eric Andersen7467c8d2001-07-12 20:26:32 +0000451#endif
Eric Andersen5c58d282001-07-10 16:29:00 +0000452 if (options & SO_DONTROUTE)
453 (void) setsockopt(s, SOL_SOCKET, SO_DONTROUTE,
454 (char *)&on, sizeof(on));
Eric Andersen5c58d282001-07-10 16:29:00 +0000455#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
Eric Andersen7467c8d2001-07-12 20:26:32 +0000465#ifdef BB_FEATURE_TRACEROUTE_SO_DEBUG
Eric Andersen5c58d282001-07-10 16:29:00 +0000466 if (options & SO_DEBUG)
467 (void) setsockopt(sndsock, SOL_SOCKET, SO_DEBUG,
468 (char *)&on, sizeof(on));
Eric Andersen7467c8d2001-07-12 20:26:32 +0000469#endif
Eric Andersen5c58d282001-07-10 16:29:00 +0000470 if (options & SO_DONTROUTE)
471 (void) setsockopt(sndsock, SOL_SOCKET, SO_DONTROUTE,
472 (char *)&on, sizeof(on));
473
474 if (source) {
Eric Andersen7467c8d2001-07-12 20:26:32 +0000475 memset(&from, 0, sizeof(struct sockaddr));
Eric Andersen5c58d282001-07-10 16:29:00 +0000476 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);
Eric Andersen5c58d282001-07-10 16:29:00 +0000492
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:
Eric Andersen5c58d282001-07-10 16:29:00 +0000519 ip = (struct ip *)packet;
520 if (ip->ip_ttl <= 1)
521 printf(" !");
Eric Andersen5c58d282001-07-10 16:29:00 +0000522 ++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}