blob: 106cf043b0ac2e7c314a5d394c1e8799fe8fee59 [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 */
134static inline char *
135inetname(struct in_addr in)
136{
137 char *cp;
138 static char line[50];
139 struct hostent *hp;
140 static char domain[MAXHOSTNAMELEN + 1];
141 static int first = 1;
142
143 if (first && !nflag) {
144 first = 0;
145 if (gethostname(domain, MAXHOSTNAMELEN) == 0 &&
146 (cp = index(domain, '.')))
147 (void) strcpy(domain, cp + 1);
148 else
149 domain[0] = 0;
150 }
151 cp = 0;
152 if (!nflag && in.s_addr != INADDR_ANY) {
153 hp = gethostbyaddr((char *)&in, sizeof (in), AF_INET);
154 if (hp) {
155 if ((cp = index(hp->h_name, '.')) &&
156 !strcmp(cp + 1, domain))
157 *cp = 0;
158 cp = (char *)hp->h_name;
159 }
160 }
161 if (cp)
162 (void) strcpy(line, cp);
163 else {
164 in.s_addr = ntohl(in.s_addr);
165 strcpy(line, inet_ntoa(in));
166 }
167 return (line);
168}
169
170static inline void
171print(u_char *buf, int cc, struct sockaddr_in *from)
172{
173 struct ip *ip;
174 int hlen;
175
176 ip = (struct ip *) buf;
177 hlen = ip->ip_hl << 2;
178 cc -= hlen;
179
180 if (nflag)
181 printf(" %s", inet_ntoa(from->sin_addr));
182 else
183 printf(" %s (%s)", inetname(from->sin_addr),
184 inet_ntoa(from->sin_addr));
185
186#ifdef BB_FEATURE_TRACEROUTE_VERBOSE
187 if (verbose)
188 printf (" %d bytes to %s", cc, inet_ntoa (ip->ip_dst));
189#endif
190}
191
192static inline double
193deltaT(struct timeval *t1p, struct timeval *t2p)
194{
195 double dt;
196
197 dt = (double)(t2p->tv_sec - t1p->tv_sec) * 1000.0 +
198 (double)(t2p->tv_usec - t1p->tv_usec) / 1000.0;
199 return (dt);
200}
201
202static inline int
203wait_for_reply(int sock, struct sockaddr_in *from, int reset_timer)
204{
205 fd_set fds;
206 static struct timeval wait;
207 int cc = 0;
208 int fromlen = sizeof (*from);
209
210 FD_ZERO(&fds);
211 FD_SET(sock, &fds);
212 if (reset_timer) {
213 /*
214 * traceroute could hang if someone else has a ping
215 * running and our ICMP reply gets dropped but we don't
216 * realize it because we keep waking up to handle those
217 * other ICMP packets that keep coming in. To fix this,
218 * "reset_timer" will only be true if the last packet that
219 * came in was for us or if this is the first time we're
220 * waiting for a reply since sending out a probe. Note
221 * that this takes advantage of the select() feature on
222 * Linux where the remaining timeout is written to the
223 * struct timeval area.
224 */
225 wait.tv_sec = waittime;
226 wait.tv_usec = 0;
227 }
228
229 if (select(sock+1, &fds, (fd_set *)0, (fd_set *)0, &wait) > 0)
230 cc=recvfrom(s, (char *)packet, sizeof(packet), 0,
231 (struct sockaddr *)from, &fromlen);
232
233 return(cc);
234}
235
236#ifdef BB_FEATURE_TRACEROUTE_VERBOSE
237/*
238 * Convert an ICMP "type" field to a printable string.
239 */
240static inline const char *
241pr_type(t)
242 u_char t;
243{
244 static const char * const ttab[] = {
245 "Echo Reply", "ICMP 1", "ICMP 2", "Dest Unreachable",
246 "Source Quench", "Redirect", "ICMP 6", "ICMP 7",
247 "Echo", "ICMP 9", "ICMP 10", "Time Exceeded",
248 "Param Problem", "Timestamp", "Timestamp Reply", "Info Request",
249 "Info Reply"
250 };
251
252 if(t > 16)
253 return("OUT-OF-RANGE");
254
255 return(ttab[t]);
256}
257#endif
258
259static inline int
260packet_ok(u_char *buf, int cc, struct sockaddr_in *from, int seq)
261{
262 struct icmp *icp;
263 u_char type, code;
264 int hlen;
265 struct ip *ip;
266
267 ip = (struct ip *) buf;
268 hlen = ip->ip_hl << 2;
269 if (cc < hlen + ICMP_MINLEN) {
270#ifdef BB_FEATURE_TRACEROUTE_VERBOSE
271 if (verbose)
272 printf("packet too short (%d bytes) from %s\n", cc,
273 inet_ntoa(from->sin_addr));
274#endif
275 return (0);
276 }
277 cc -= hlen;
278 icp = (struct icmp *)(buf + hlen);
279 type = icp->icmp_type; code = icp->icmp_code;
280 if ((type == ICMP_TIMXCEED && code == ICMP_TIMXCEED_INTRANS) ||
281 type == ICMP_UNREACH) {
282 struct ip *hip;
283 struct udphdr *up;
284
285 hip = &icp->icmp_ip;
286 hlen = hip->ip_hl << 2;
287 up = (struct udphdr *)((u_char *)hip + hlen);
288 if (hlen + 12 <= cc && hip->ip_p == IPPROTO_UDP &&
289 up->source == htons(ident) &&
290 up->dest == htons(port+seq))
291 return (type == ICMP_TIMXCEED? -1 : code+1);
292 }
293#ifdef BB_FEATURE_TRACEROUTE_VERBOSE
294 if (verbose) {
295 int i;
296 u_long *lp = (u_long *)&icp->icmp_ip;
297
298 printf("\n%d bytes from %s to %s: icmp type %d (%s) code %d\n",
299 cc, inet_ntoa(from->sin_addr), inet_ntoa(ip->ip_dst),
300 type, pr_type(type), icp->icmp_code);
301 for (i = 4; i < cc ; i += sizeof(long))
302 printf("%2d: x%8.8lx\n", i, *lp++);
303 }
304#endif
305 return(0);
306}
307
308static void /* not inline */
309send_probe(int seq, int ttl)
310{
311 struct opacket *op = outpacket;
312 struct ip *ip = &op->ip;
313 struct udphdr *up = &op->udp;
314 int i;
315 struct timezone tz;
316
317 ip->ip_off = 0;
318 ip->ip_hl = sizeof(*ip) >> 2;
319 ip->ip_p = IPPROTO_UDP;
320 ip->ip_len = datalen;
321 ip->ip_ttl = ttl;
322 ip->ip_v = IPVERSION;
323 ip->ip_id = htons(ident+seq);
324
325 up->source = htons(ident);
326 up->dest = htons(port+seq);
327 up->len = htons((u_short)(datalen - sizeof(struct ip)));
328 up->check = 0;
329
330 op->seq = seq;
331 op->ttl = ttl;
332 (void) gettimeofday(&op->tv, &tz);
333
334 i = sendto(sndsock, (char *)outpacket, datalen, 0, &whereto,
335 sizeof(struct sockaddr));
336 if (i < 0 || i != datalen) {
337 if (i<0)
338 perror("sendto");
339 printf("traceroute: wrote %s %d chars, ret=%d\n", hostname,
340 datalen, i);
341 (void) fflush(stdout);
342 }
343}
344
345
Eric Andersen5c58d282001-07-10 16:29:00 +0000346int
347#ifndef BB_TRACEROUTE
348main(argc, argv)
349#else
350traceroute_main(argc, argv)
351#endif
352 int argc;
353 char *argv[];
354{
355 extern char *optarg;
356 extern int optind;
357 struct hostent *hp;
Eric Andersen5c58d282001-07-10 16:29:00 +0000358 struct sockaddr_in from, *to;
359 int ch, i, on, probe, seq, tos, ttl;
360
361 int options = 0; /* socket options */
362 char *source = 0;
363 int nprobes = 3;
364
365 on = 1;
366 seq = tos = 0;
367 to = (struct sockaddr_in *)&whereto;
368 while ((ch = getopt(argc, argv, "dm:np:q:rs:t:w:v")) != EOF)
369 switch(ch) {
370 case 'd':
Eric Andersen7467c8d2001-07-12 20:26:32 +0000371#ifdef BB_FEATURE_TRACEROUTE_SO_DEBUG
Eric Andersen5c58d282001-07-10 16:29:00 +0000372 options |= SO_DEBUG;
Eric Andersen7467c8d2001-07-12 20:26:32 +0000373#endif
Eric Andersen5c58d282001-07-10 16:29:00 +0000374 break;
375 case 'm':
376 max_ttl = atoi(optarg);
377 if (max_ttl <= 1)
378 error_msg_and_die("max ttl must be >1.");
379 break;
380 case 'n':
381 nflag++;
382 break;
383 case 'p':
384 port = atoi(optarg);
385 if (port < 1)
386 error_msg_and_die("port must be >0.");
387 break;
388 case 'q':
389 nprobes = atoi(optarg);
390 if (nprobes < 1)
391 error_msg_and_die("nprobes must be >0.");
392 break;
393 case 'r':
394 options |= SO_DONTROUTE;
395 break;
396 case 's':
397 /*
398 * set the ip source address of the outbound
399 * probe (e.g., on a multi-homed host).
400 */
401 source = optarg;
402 break;
403 case 't':
404 tos = atoi(optarg);
405 if (tos < 0 || tos > 255)
406 error_msg_and_die("tos must be 0 to 255.");
407 break;
408 case 'v':
Eric Andersen7467c8d2001-07-12 20:26:32 +0000409#ifdef BB_FEATURE_TRACEROUTE_VERBOSE
Eric Andersen5c58d282001-07-10 16:29:00 +0000410 verbose++;
Eric Andersen7467c8d2001-07-12 20:26:32 +0000411#endif
Eric Andersen5c58d282001-07-10 16:29:00 +0000412 break;
413 case 'w':
414 waittime = atoi(optarg);
415 if (waittime <= 1)
416 error_msg_and_die("wait must be >1 sec.");
417 break;
418 default:
419 show_usage();
420 }
421 argc -= optind;
422 argv += optind;
423
424 if (argc < 1)
425 show_usage();
426
427 setlinebuf (stdout);
428
Eric Andersen7467c8d2001-07-12 20:26:32 +0000429 memset(&whereto, 0, sizeof(struct sockaddr));
430 hp = xgethostbyname(*argv);
Eric Andersen5c58d282001-07-10 16:29:00 +0000431 to->sin_family = hp->h_addrtype;
Eric Andersen7467c8d2001-07-12 20:26:32 +0000432 memcpy(&to->sin_addr, hp->h_addr, hp->h_length);
Eric Andersen5c58d282001-07-10 16:29:00 +0000433 hostname = (char *)hp->h_name;
Eric Andersen5c58d282001-07-10 16:29:00 +0000434 if (*++argv)
435 datalen = atoi(*argv);
436 if (datalen < 0 || datalen >= MAXPACKET - sizeof(struct opacket))
437 error_msg_and_die("packet size must be 0 <= s < %d.",
438 MAXPACKET - sizeof(struct opacket));
439 datalen += sizeof(struct opacket);
440 outpacket = (struct opacket *)xmalloc((unsigned)datalen);
Eric Andersen7467c8d2001-07-12 20:26:32 +0000441 memset(outpacket, 0, datalen);
Eric Andersen5c58d282001-07-10 16:29:00 +0000442 outpacket->ip.ip_dst = to->sin_addr;
443 outpacket->ip.ip_tos = tos;
444 outpacket->ip.ip_v = IPVERSION;
445 outpacket->ip.ip_id = 0;
446
447 ident = (getpid() & 0xffff) | 0x8000;
448
Eric Andersen7467c8d2001-07-12 20:26:32 +0000449 if ((sndsock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0)
450 perror_msg_and_die(can_not_create_raw_socket);
451
452 s = create_icmp_socket();
453
454#ifdef BB_FEATURE_TRACEROUTE_SO_DEBUG
Eric Andersen5c58d282001-07-10 16:29:00 +0000455 if (options & SO_DEBUG)
456 (void) setsockopt(s, SOL_SOCKET, SO_DEBUG,
457 (char *)&on, sizeof(on));
Eric Andersen7467c8d2001-07-12 20:26:32 +0000458#endif
Eric Andersen5c58d282001-07-10 16:29:00 +0000459 if (options & SO_DONTROUTE)
460 (void) setsockopt(s, SOL_SOCKET, SO_DONTROUTE,
461 (char *)&on, sizeof(on));
Eric Andersen5c58d282001-07-10 16:29:00 +0000462#ifdef SO_SNDBUF
463 if (setsockopt(sndsock, SOL_SOCKET, SO_SNDBUF, (char *)&datalen,
464 sizeof(datalen)) < 0)
465 perror_msg_and_die("SO_SNDBUF");
466#endif SO_SNDBUF
467#ifdef IP_HDRINCL
468 if (setsockopt(sndsock, IPPROTO_IP, IP_HDRINCL, (char *)&on,
469 sizeof(on)) < 0)
470 perror_msg_and_die("IP_HDRINCL");
471#endif IP_HDRINCL
Eric Andersen7467c8d2001-07-12 20:26:32 +0000472#ifdef BB_FEATURE_TRACEROUTE_SO_DEBUG
Eric Andersen5c58d282001-07-10 16:29:00 +0000473 if (options & SO_DEBUG)
474 (void) setsockopt(sndsock, SOL_SOCKET, SO_DEBUG,
475 (char *)&on, sizeof(on));
Eric Andersen7467c8d2001-07-12 20:26:32 +0000476#endif
Eric Andersen5c58d282001-07-10 16:29:00 +0000477 if (options & SO_DONTROUTE)
478 (void) setsockopt(sndsock, SOL_SOCKET, SO_DONTROUTE,
479 (char *)&on, sizeof(on));
480
481 if (source) {
Eric Andersen7467c8d2001-07-12 20:26:32 +0000482 memset(&from, 0, sizeof(struct sockaddr));
Eric Andersen5c58d282001-07-10 16:29:00 +0000483 from.sin_family = AF_INET;
484 from.sin_addr.s_addr = inet_addr(source);
485 if (from.sin_addr.s_addr == -1)
486 error_msg_and_die("unknown host %s", source);
487 outpacket->ip.ip_src = from.sin_addr;
488#ifndef IP_HDRINCL
489 if (bind(sndsock, (struct sockaddr *)&from, sizeof(from)) < 0)
490 perror_msg_and_die("bind");
491#endif IP_HDRINCL
492 }
493
494 fprintf(stderr, "traceroute to %s (%s)", hostname,
495 inet_ntoa(to->sin_addr));
496 if (source)
497 fprintf(stderr, " from %s", source);
498 fprintf(stderr, ", %d hops max, %d byte packets\n", max_ttl, datalen);
Eric Andersen5c58d282001-07-10 16:29:00 +0000499
500 for (ttl = 1; ttl <= max_ttl; ++ttl) {
501 u_long lastaddr = 0;
502 int got_there = 0;
503 int unreachable = 0;
504
505 printf("%2d ", ttl);
506 for (probe = 0; probe < nprobes; ++probe) {
507 int cc, reset_timer;
508 struct timeval t1, t2;
509 struct timezone tz;
510 struct ip *ip;
511
512 (void) gettimeofday(&t1, &tz);
513 send_probe(++seq, ttl);
514 reset_timer = 1;
515 while ((cc = wait_for_reply(s, &from, reset_timer)) != 0) {
516 (void) gettimeofday(&t2, &tz);
517 if ((i = packet_ok(packet, cc, &from, seq))) {
518 reset_timer = 1;
519 if (from.sin_addr.s_addr != lastaddr) {
520 print(packet, cc, &from);
521 lastaddr = from.sin_addr.s_addr;
522 }
523 printf(" %g ms", deltaT(&t1, &t2));
524 switch(i - 1) {
525 case ICMP_UNREACH_PORT:
Eric Andersen5c58d282001-07-10 16:29:00 +0000526 ip = (struct ip *)packet;
527 if (ip->ip_ttl <= 1)
528 printf(" !");
Eric Andersen5c58d282001-07-10 16:29:00 +0000529 ++got_there;
530 break;
531 case ICMP_UNREACH_NET:
532 ++unreachable;
533 printf(" !N");
534 break;
535 case ICMP_UNREACH_HOST:
536 ++unreachable;
537 printf(" !H");
538 break;
539 case ICMP_UNREACH_PROTOCOL:
540 ++got_there;
541 printf(" !P");
542 break;
543 case ICMP_UNREACH_NEEDFRAG:
544 ++unreachable;
545 printf(" !F");
546 break;
547 case ICMP_UNREACH_SRCFAIL:
548 ++unreachable;
549 printf(" !S");
550 break;
551 }
552 break;
553 } else
554 reset_timer = 0;
555 }
556 if (cc == 0)
557 printf(" *");
558 (void) fflush(stdout);
559 }
560 putchar('\n');
561 if (got_there || unreachable >= nprobes-1)
562 exit(0);
563 }
564
565 return 0;
566}