blob: f4c67cf41a5947fd2c42d9dfe388b34cdaf04778 [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>
Eric Andersenaf6b40a2001-07-31 22:53:36 +000075#include <sys/types.h>
76#include <sys/socket.h>
Eric Andersen5c58d282001-07-10 16:29:00 +000077#include <netdb.h>
78#include <endian.h>
79#include <arpa/inet.h>
80#include <netinet/udp.h>
81#include <netinet/ip.h>
82#include <netinet/ip_icmp.h>
83
84
Eric Andersenaf6b40a2001-07-31 22:53:36 +000085
Eric Andersen5c58d282001-07-10 16:29:00 +000086#define MAXPACKET 65535 /* max ip packet size */
87#ifndef MAXHOSTNAMELEN
88#define MAXHOSTNAMELEN 64
89#endif
90
91/*
92 * format of a (udp) probe packet.
93 */
94struct opacket {
95 struct ip ip;
96 struct udphdr udp;
97 u_char seq; /* sequence number of this packet */
98 u_char ttl; /* ttl packet left with */
99 struct timeval tv; /* time packet left */
100};
101
102/*
103 * Definitions for internet protocol version 4.
104 * Per RFC 791, September 1981.
105 */
106#define IPVERSION 4
107
108
109#include "busybox.h"
110
Eric Andersen5c58d282001-07-10 16:29:00 +0000111static u_char packet[512]; /* last inbound (icmp) packet */
112static struct opacket *outpacket; /* last output (udp) packet */
113
114static int s; /* receive (icmp) socket file descriptor */
115static int sndsock; /* send (udp) socket file descriptor */
116
117static struct sockaddr whereto; /* Who to try to reach */
118static int datalen; /* How much data */
119
120static char *hostname;
121
122static int max_ttl = 30;
123static u_short ident;
124static u_short port = 32768+666; /* start udp dest port # for probe packets */
125
Eric Andersen7467c8d2001-07-12 20:26:32 +0000126#ifdef BB_FEATURE_TRACEROUTE_VERBOSE
Eric Andersen5c58d282001-07-10 16:29:00 +0000127static int verbose;
Eric Andersen7467c8d2001-07-12 20:26:32 +0000128#endif
Eric Andersen5c58d282001-07-10 16:29:00 +0000129static int waittime = 5; /* time to wait for response (in seconds) */
130static int nflag; /* print addresses numerically */
131
Eric Andersen7467c8d2001-07-12 20:26:32 +0000132/*
133 * Construct an Internet address representation.
134 * If the nflag has been supplied, give
135 * numeric value, otherwise try for symbolic name.
136 */
Eric Andersen044228d2001-07-17 01:12:36 +0000137static inline void
138inetname(struct sockaddr_in *from)
Eric Andersen7467c8d2001-07-12 20:26:32 +0000139{
140 char *cp;
Eric Andersen7467c8d2001-07-12 20:26:32 +0000141 struct hostent *hp;
142 static char domain[MAXHOSTNAMELEN + 1];
143 static int first = 1;
Eric Andersen044228d2001-07-17 01:12:36 +0000144 const char *ina;
Eric Andersen7467c8d2001-07-12 20:26:32 +0000145
146 if (first && !nflag) {
147 first = 0;
148 if (gethostname(domain, MAXHOSTNAMELEN) == 0 &&
Eric Andersen044228d2001-07-17 01:12:36 +0000149 (cp = strchr(domain, '.')))
Eric Andersen7467c8d2001-07-12 20:26:32 +0000150 (void) strcpy(domain, cp + 1);
151 else
152 domain[0] = 0;
153 }
154 cp = 0;
Eric Andersen044228d2001-07-17 01:12:36 +0000155 if (!nflag && from->sin_addr.s_addr != INADDR_ANY) {
156 hp = gethostbyaddr((char *)&(from->sin_addr), sizeof (from->sin_addr), AF_INET);
Eric Andersen7467c8d2001-07-12 20:26:32 +0000157 if (hp) {
Eric Andersen044228d2001-07-17 01:12:36 +0000158 if ((cp = strchr(hp->h_name, '.')) &&
Eric Andersen7467c8d2001-07-12 20:26:32 +0000159 !strcmp(cp + 1, domain))
160 *cp = 0;
161 cp = (char *)hp->h_name;
162 }
163 }
Eric Andersen044228d2001-07-17 01:12:36 +0000164 ina = inet_ntoa(from->sin_addr);
165 if (nflag)
166 printf(" %s", ina);
167 else
168 printf(" %s (%s)", (cp ? cp : ina), ina);
Eric Andersen7467c8d2001-07-12 20:26:32 +0000169}
170
171static inline void
172print(u_char *buf, int cc, struct sockaddr_in *from)
173{
174 struct ip *ip;
175 int hlen;
176
177 ip = (struct ip *) buf;
178 hlen = ip->ip_hl << 2;
179 cc -= hlen;
180
Eric Andersen044228d2001-07-17 01:12:36 +0000181 inetname(from);
Eric Andersen7467c8d2001-07-12 20:26:32 +0000182#ifdef BB_FEATURE_TRACEROUTE_VERBOSE
183 if (verbose)
184 printf (" %d bytes to %s", cc, inet_ntoa (ip->ip_dst));
185#endif
186}
187
188static inline double
189deltaT(struct timeval *t1p, struct timeval *t2p)
190{
191 double dt;
192
193 dt = (double)(t2p->tv_sec - t1p->tv_sec) * 1000.0 +
194 (double)(t2p->tv_usec - t1p->tv_usec) / 1000.0;
195 return (dt);
196}
197
198static inline int
199wait_for_reply(int sock, struct sockaddr_in *from, int reset_timer)
200{
201 fd_set fds;
202 static struct timeval wait;
203 int cc = 0;
204 int fromlen = sizeof (*from);
205
206 FD_ZERO(&fds);
207 FD_SET(sock, &fds);
208 if (reset_timer) {
209 /*
210 * traceroute could hang if someone else has a ping
211 * running and our ICMP reply gets dropped but we don't
212 * realize it because we keep waking up to handle those
213 * other ICMP packets that keep coming in. To fix this,
214 * "reset_timer" will only be true if the last packet that
215 * came in was for us or if this is the first time we're
216 * waiting for a reply since sending out a probe. Note
217 * that this takes advantage of the select() feature on
218 * Linux where the remaining timeout is written to the
219 * struct timeval area.
220 */
221 wait.tv_sec = waittime;
222 wait.tv_usec = 0;
223 }
224
225 if (select(sock+1, &fds, (fd_set *)0, (fd_set *)0, &wait) > 0)
226 cc=recvfrom(s, (char *)packet, sizeof(packet), 0,
227 (struct sockaddr *)from, &fromlen);
228
229 return(cc);
230}
231
232#ifdef BB_FEATURE_TRACEROUTE_VERBOSE
233/*
234 * Convert an ICMP "type" field to a printable string.
235 */
236static inline const char *
237pr_type(t)
238 u_char t;
239{
240 static const char * const ttab[] = {
241 "Echo Reply", "ICMP 1", "ICMP 2", "Dest Unreachable",
242 "Source Quench", "Redirect", "ICMP 6", "ICMP 7",
243 "Echo", "ICMP 9", "ICMP 10", "Time Exceeded",
244 "Param Problem", "Timestamp", "Timestamp Reply", "Info Request",
245 "Info Reply"
246 };
247
248 if(t > 16)
249 return("OUT-OF-RANGE");
250
251 return(ttab[t]);
252}
253#endif
254
255static inline int
256packet_ok(u_char *buf, int cc, struct sockaddr_in *from, int seq)
257{
258 struct icmp *icp;
259 u_char type, code;
260 int hlen;
261 struct ip *ip;
262
263 ip = (struct ip *) buf;
264 hlen = ip->ip_hl << 2;
265 if (cc < hlen + ICMP_MINLEN) {
266#ifdef BB_FEATURE_TRACEROUTE_VERBOSE
267 if (verbose)
268 printf("packet too short (%d bytes) from %s\n", cc,
269 inet_ntoa(from->sin_addr));
270#endif
271 return (0);
272 }
273 cc -= hlen;
274 icp = (struct icmp *)(buf + hlen);
275 type = icp->icmp_type; code = icp->icmp_code;
276 if ((type == ICMP_TIMXCEED && code == ICMP_TIMXCEED_INTRANS) ||
277 type == ICMP_UNREACH) {
278 struct ip *hip;
279 struct udphdr *up;
280
281 hip = &icp->icmp_ip;
282 hlen = hip->ip_hl << 2;
283 up = (struct udphdr *)((u_char *)hip + hlen);
284 if (hlen + 12 <= cc && hip->ip_p == IPPROTO_UDP &&
285 up->source == htons(ident) &&
286 up->dest == htons(port+seq))
287 return (type == ICMP_TIMXCEED? -1 : code+1);
288 }
289#ifdef BB_FEATURE_TRACEROUTE_VERBOSE
290 if (verbose) {
291 int i;
292 u_long *lp = (u_long *)&icp->icmp_ip;
293
294 printf("\n%d bytes from %s to %s: icmp type %d (%s) code %d\n",
295 cc, inet_ntoa(from->sin_addr), inet_ntoa(ip->ip_dst),
296 type, pr_type(type), icp->icmp_code);
297 for (i = 4; i < cc ; i += sizeof(long))
298 printf("%2d: x%8.8lx\n", i, *lp++);
299 }
300#endif
301 return(0);
302}
303
304static void /* not inline */
305send_probe(int seq, int ttl)
306{
307 struct opacket *op = outpacket;
308 struct ip *ip = &op->ip;
309 struct udphdr *up = &op->udp;
310 int i;
311 struct timezone tz;
312
313 ip->ip_off = 0;
314 ip->ip_hl = sizeof(*ip) >> 2;
315 ip->ip_p = IPPROTO_UDP;
316 ip->ip_len = datalen;
317 ip->ip_ttl = ttl;
318 ip->ip_v = IPVERSION;
319 ip->ip_id = htons(ident+seq);
320
321 up->source = htons(ident);
322 up->dest = htons(port+seq);
323 up->len = htons((u_short)(datalen - sizeof(struct ip)));
324 up->check = 0;
325
326 op->seq = seq;
327 op->ttl = ttl;
328 (void) gettimeofday(&op->tv, &tz);
329
330 i = sendto(sndsock, (char *)outpacket, datalen, 0, &whereto,
331 sizeof(struct sockaddr));
332 if (i < 0 || i != datalen) {
333 if (i<0)
334 perror("sendto");
335 printf("traceroute: wrote %s %d chars, ret=%d\n", hostname,
336 datalen, i);
337 (void) fflush(stdout);
338 }
339}
340
341
Eric Andersen5c58d282001-07-10 16:29:00 +0000342int
343#ifndef BB_TRACEROUTE
344main(argc, argv)
345#else
346traceroute_main(argc, argv)
347#endif
348 int argc;
349 char *argv[];
350{
351 extern char *optarg;
352 extern int optind;
353 struct hostent *hp;
Eric Andersen5c58d282001-07-10 16:29:00 +0000354 struct sockaddr_in from, *to;
355 int ch, i, on, probe, seq, tos, ttl;
356
357 int options = 0; /* socket options */
358 char *source = 0;
359 int nprobes = 3;
360
361 on = 1;
362 seq = tos = 0;
363 to = (struct sockaddr_in *)&whereto;
364 while ((ch = getopt(argc, argv, "dm:np:q:rs:t:w:v")) != EOF)
365 switch(ch) {
366 case 'd':
Eric Andersen7467c8d2001-07-12 20:26:32 +0000367#ifdef BB_FEATURE_TRACEROUTE_SO_DEBUG
Eric Andersen5c58d282001-07-10 16:29:00 +0000368 options |= SO_DEBUG;
Eric Andersen7467c8d2001-07-12 20:26:32 +0000369#endif
Eric Andersen5c58d282001-07-10 16:29:00 +0000370 break;
371 case 'm':
372 max_ttl = atoi(optarg);
373 if (max_ttl <= 1)
374 error_msg_and_die("max ttl must be >1.");
375 break;
376 case 'n':
377 nflag++;
378 break;
379 case 'p':
380 port = atoi(optarg);
381 if (port < 1)
382 error_msg_and_die("port must be >0.");
383 break;
384 case 'q':
385 nprobes = atoi(optarg);
386 if (nprobes < 1)
387 error_msg_and_die("nprobes must be >0.");
388 break;
389 case 'r':
390 options |= SO_DONTROUTE;
391 break;
392 case 's':
393 /*
394 * set the ip source address of the outbound
395 * probe (e.g., on a multi-homed host).
396 */
397 source = optarg;
398 break;
399 case 't':
400 tos = atoi(optarg);
401 if (tos < 0 || tos > 255)
402 error_msg_and_die("tos must be 0 to 255.");
403 break;
404 case 'v':
Eric Andersen7467c8d2001-07-12 20:26:32 +0000405#ifdef BB_FEATURE_TRACEROUTE_VERBOSE
Eric Andersen5c58d282001-07-10 16:29:00 +0000406 verbose++;
Eric Andersen7467c8d2001-07-12 20:26:32 +0000407#endif
Eric Andersen5c58d282001-07-10 16:29:00 +0000408 break;
409 case 'w':
410 waittime = atoi(optarg);
411 if (waittime <= 1)
412 error_msg_and_die("wait must be >1 sec.");
413 break;
414 default:
415 show_usage();
416 }
417 argc -= optind;
418 argv += optind;
419
420 if (argc < 1)
421 show_usage();
422
423 setlinebuf (stdout);
424
Eric Andersen7467c8d2001-07-12 20:26:32 +0000425 memset(&whereto, 0, sizeof(struct sockaddr));
426 hp = xgethostbyname(*argv);
Eric Andersen5c58d282001-07-10 16:29:00 +0000427 to->sin_family = hp->h_addrtype;
Eric Andersen7467c8d2001-07-12 20:26:32 +0000428 memcpy(&to->sin_addr, hp->h_addr, hp->h_length);
Eric Andersen5c58d282001-07-10 16:29:00 +0000429 hostname = (char *)hp->h_name;
Eric Andersen5c58d282001-07-10 16:29:00 +0000430 if (*++argv)
431 datalen = atoi(*argv);
432 if (datalen < 0 || datalen >= MAXPACKET - sizeof(struct opacket))
433 error_msg_and_die("packet size must be 0 <= s < %d.",
434 MAXPACKET - sizeof(struct opacket));
435 datalen += sizeof(struct opacket);
436 outpacket = (struct opacket *)xmalloc((unsigned)datalen);
Eric Andersen7467c8d2001-07-12 20:26:32 +0000437 memset(outpacket, 0, datalen);
Eric Andersen5c58d282001-07-10 16:29:00 +0000438 outpacket->ip.ip_dst = to->sin_addr;
439 outpacket->ip.ip_tos = tos;
440 outpacket->ip.ip_v = IPVERSION;
441 outpacket->ip.ip_id = 0;
442
443 ident = (getpid() & 0xffff) | 0x8000;
444
Eric Andersen7467c8d2001-07-12 20:26:32 +0000445 if ((sndsock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0)
446 perror_msg_and_die(can_not_create_raw_socket);
447
448 s = create_icmp_socket();
449
450#ifdef BB_FEATURE_TRACEROUTE_SO_DEBUG
Eric Andersen5c58d282001-07-10 16:29:00 +0000451 if (options & SO_DEBUG)
452 (void) setsockopt(s, SOL_SOCKET, SO_DEBUG,
453 (char *)&on, sizeof(on));
Eric Andersen7467c8d2001-07-12 20:26:32 +0000454#endif
Eric Andersen5c58d282001-07-10 16:29:00 +0000455 if (options & SO_DONTROUTE)
456 (void) setsockopt(s, SOL_SOCKET, SO_DONTROUTE,
457 (char *)&on, sizeof(on));
Eric Andersen5c58d282001-07-10 16:29:00 +0000458#ifdef SO_SNDBUF
459 if (setsockopt(sndsock, SOL_SOCKET, SO_SNDBUF, (char *)&datalen,
460 sizeof(datalen)) < 0)
461 perror_msg_and_die("SO_SNDBUF");
462#endif SO_SNDBUF
463#ifdef IP_HDRINCL
464 if (setsockopt(sndsock, IPPROTO_IP, IP_HDRINCL, (char *)&on,
465 sizeof(on)) < 0)
466 perror_msg_and_die("IP_HDRINCL");
467#endif IP_HDRINCL
Eric Andersen7467c8d2001-07-12 20:26:32 +0000468#ifdef BB_FEATURE_TRACEROUTE_SO_DEBUG
Eric Andersen5c58d282001-07-10 16:29:00 +0000469 if (options & SO_DEBUG)
470 (void) setsockopt(sndsock, SOL_SOCKET, SO_DEBUG,
471 (char *)&on, sizeof(on));
Eric Andersen7467c8d2001-07-12 20:26:32 +0000472#endif
Eric Andersen5c58d282001-07-10 16:29:00 +0000473 if (options & SO_DONTROUTE)
474 (void) setsockopt(sndsock, SOL_SOCKET, SO_DONTROUTE,
475 (char *)&on, sizeof(on));
476
477 if (source) {
Eric Andersen7467c8d2001-07-12 20:26:32 +0000478 memset(&from, 0, sizeof(struct sockaddr));
Eric Andersen5c58d282001-07-10 16:29:00 +0000479 from.sin_family = AF_INET;
480 from.sin_addr.s_addr = inet_addr(source);
481 if (from.sin_addr.s_addr == -1)
482 error_msg_and_die("unknown host %s", source);
483 outpacket->ip.ip_src = from.sin_addr;
484#ifndef IP_HDRINCL
485 if (bind(sndsock, (struct sockaddr *)&from, sizeof(from)) < 0)
486 perror_msg_and_die("bind");
487#endif IP_HDRINCL
488 }
489
490 fprintf(stderr, "traceroute to %s (%s)", hostname,
491 inet_ntoa(to->sin_addr));
492 if (source)
493 fprintf(stderr, " from %s", source);
494 fprintf(stderr, ", %d hops max, %d byte packets\n", max_ttl, datalen);
Eric Andersen5c58d282001-07-10 16:29:00 +0000495
496 for (ttl = 1; ttl <= max_ttl; ++ttl) {
497 u_long lastaddr = 0;
498 int got_there = 0;
499 int unreachable = 0;
500
501 printf("%2d ", ttl);
502 for (probe = 0; probe < nprobes; ++probe) {
503 int cc, reset_timer;
504 struct timeval t1, t2;
505 struct timezone tz;
506 struct ip *ip;
507
508 (void) gettimeofday(&t1, &tz);
509 send_probe(++seq, ttl);
510 reset_timer = 1;
511 while ((cc = wait_for_reply(s, &from, reset_timer)) != 0) {
512 (void) gettimeofday(&t2, &tz);
513 if ((i = packet_ok(packet, cc, &from, seq))) {
514 reset_timer = 1;
515 if (from.sin_addr.s_addr != lastaddr) {
516 print(packet, cc, &from);
517 lastaddr = from.sin_addr.s_addr;
518 }
519 printf(" %g ms", deltaT(&t1, &t2));
520 switch(i - 1) {
521 case ICMP_UNREACH_PORT:
Eric Andersen5c58d282001-07-10 16:29:00 +0000522 ip = (struct ip *)packet;
523 if (ip->ip_ttl <= 1)
524 printf(" !");
Eric Andersen5c58d282001-07-10 16:29:00 +0000525 ++got_there;
526 break;
527 case ICMP_UNREACH_NET:
528 ++unreachable;
529 printf(" !N");
530 break;
531 case ICMP_UNREACH_HOST:
532 ++unreachable;
533 printf(" !H");
534 break;
535 case ICMP_UNREACH_PROTOCOL:
536 ++got_there;
537 printf(" !P");
538 break;
539 case ICMP_UNREACH_NEEDFRAG:
540 ++unreachable;
541 printf(" !F");
542 break;
543 case ICMP_UNREACH_SRCFAIL:
544 ++unreachable;
545 printf(" !S");
546 break;
547 }
548 break;
549 } else
550 reset_timer = 0;
551 }
552 if (cc == 0)
553 printf(" *");
554 (void) fflush(stdout);
555 }
556 putchar('\n');
557 if (got_there || unreachable >= nprobes-1)
558 exit(0);
559 }
560
561 return 0;
562}