aboutsummaryrefslogtreecommitdiff
path: root/busybox/networking/traceroute.c
diff options
context:
space:
mode:
Diffstat (limited to 'busybox/networking/traceroute.c')
-rw-r--r--busybox/networking/traceroute.c548
1 files changed, 548 insertions, 0 deletions
diff --git a/busybox/networking/traceroute.c b/busybox/networking/traceroute.c
new file mode 100644
index 000000000..44ffdf07e
--- /dev/null
+++ b/busybox/networking/traceroute.c
@@ -0,0 +1,548 @@
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 */
87struct 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
104static u_char packet[512]; /* last inbound (icmp) packet */
105static struct opacket *outpacket; /* last output (udp) packet */
106
107static int s; /* receive (icmp) socket file descriptor */
108static int sndsock; /* send (udp) socket file descriptor */
109
110static struct sockaddr whereto; /* Who to try to reach */
111static int datalen; /* How much data */
112
113static char *hostname;
114
115static int max_ttl = 30;
116static u_short ident;
117static u_short port = 32768+666; /* start udp dest port # for probe packets */
118
119#ifdef CONFIG_FEATURE_TRACEROUTE_VERBOSE
120static int verbose;
121#endif
122static int waittime = 5; /* time to wait for response (in seconds) */
123static 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 */
130static inline void
131inetname(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
160static inline void
161print(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
177static inline double
178deltaT(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
187static inline int
188wait_for_reply(int sock, struct sockaddr_in *from, int reset_timer)
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, sizeof(packet), 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 */
225static inline const char *
226pr_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
243static inline int
244packet_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
292static void /* not inline */
293send_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
330int
331#ifndef CONFIG_TRACEROUTE
332main(int argc, char *argv[])
333#else
334traceroute_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
343 int options = 0; /* socket options */
344 char *source = 0;
345 int nprobes = 3;
346
347 on = 1;
348 seq = tos = 0;
349 to = (struct sockaddr_in *)&whereto;
350 while ((ch = getopt(argc, argv, "dm:np:q:rs:t:w:v")) != EOF)
351 switch(ch) {
352 case 'd':
353#ifdef CONFIG_FEATURE_TRACEROUTE_SO_DEBUG
354 options |= SO_DEBUG;
355#endif
356 break;
357 case 'm':
358 max_ttl = atoi(optarg);
359 if (max_ttl <= 1)
360 bb_error_msg_and_die("max ttl must be >1.");
361 break;
362 case 'n':
363 nflag++;
364 break;
365 case 'p':
366 port = atoi(optarg);
367 if (port < 1)
368 bb_error_msg_and_die("port must be >0.");
369 break;
370 case 'q':
371 nprobes = atoi(optarg);
372 if (nprobes < 1)
373 bb_error_msg_and_die("nprobes must be >0.");
374 break;
375 case 'r':
376 options |= SO_DONTROUTE;
377 break;
378 case 's':
379 /*
380 * set the ip source address of the outbound
381 * probe (e.g., on a multi-homed host).
382 */
383 source = optarg;
384 break;
385 case 't':
386 tos = atoi(optarg);
387 if (tos < 0 || tos > 255)
388 bb_error_msg_and_die("tos must be 0 to 255.");
389 break;
390 case 'v':
391#ifdef CONFIG_FEATURE_TRACEROUTE_VERBOSE
392 verbose++;
393#endif
394 break;
395 case 'w':
396 waittime = atoi(optarg);
397 if (waittime <= 1)
398 bb_error_msg_and_die("wait must be >1 sec.");
399 break;
400 default:
401 bb_show_usage();
402 }
403 argc -= optind;
404 argv += optind;
405
406 if (argc < 1)
407 bb_show_usage();
408
409 setlinebuf (stdout);
410
411 memset(&whereto, 0, sizeof(struct sockaddr));
412 hp = xgethostbyname(*argv);
413 to->sin_family = hp->h_addrtype;
414 memcpy(&to->sin_addr, hp->h_addr, hp->h_length);
415 hostname = (char *)hp->h_name;
416 if (*++argv)
417 datalen = atoi(*argv);
418 if (datalen < 0 || datalen >= MAXPACKET - sizeof(struct opacket))
419 bb_error_msg_and_die("packet size must be 0 <= s < %d.",
420 MAXPACKET - sizeof(struct opacket));
421 datalen += sizeof(struct opacket);
422 outpacket = (struct opacket *)xmalloc((unsigned)datalen);
423 memset(outpacket, 0, datalen);
424 outpacket->ip.ip_dst = to->sin_addr;
425 outpacket->ip.ip_tos = tos;
426 outpacket->ip.ip_v = IPVERSION;
427 outpacket->ip.ip_id = 0;
428
429 ident = (getpid() & 0xffff) | 0x8000;
430
431 if ((sndsock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0)
432 bb_perror_msg_and_die(bb_msg_can_not_create_raw_socket);
433
434 s = create_icmp_socket();
435
436#ifdef CONFIG_FEATURE_TRACEROUTE_SO_DEBUG
437 if (options & SO_DEBUG)
438 (void) setsockopt(s, SOL_SOCKET, SO_DEBUG,
439 (char *)&on, sizeof(on));
440#endif
441 if (options & SO_DONTROUTE)
442 (void) setsockopt(s, SOL_SOCKET, SO_DONTROUTE,
443 (char *)&on, sizeof(on));
444#ifdef SO_SNDBUF
445 if (setsockopt(sndsock, SOL_SOCKET, SO_SNDBUF, (char *)&datalen,
446 sizeof(datalen)) < 0)
447 bb_perror_msg_and_die("SO_SNDBUF");
448#endif
449#ifdef IP_HDRINCL
450 if (setsockopt(sndsock, IPPROTO_IP, IP_HDRINCL, (char *)&on,
451 sizeof(on)) < 0)
452 bb_perror_msg_and_die("IP_HDRINCL");
453#endif
454#ifdef CONFIG_FEATURE_TRACEROUTE_SO_DEBUG
455 if (options & SO_DEBUG)
456 (void) setsockopt(sndsock, SOL_SOCKET, SO_DEBUG,
457 (char *)&on, sizeof(on));
458#endif
459 if (options & SO_DONTROUTE)
460 (void) setsockopt(sndsock, SOL_SOCKET, SO_DONTROUTE,
461 (char *)&on, sizeof(on));
462
463 if (source) {
464 memset(&from, 0, sizeof(struct sockaddr));
465 from.sin_family = AF_INET;
466 from.sin_addr.s_addr = inet_addr(source);
467 if (from.sin_addr.s_addr == -1)
468 bb_error_msg_and_die("unknown host %s", source);
469 outpacket->ip.ip_src = from.sin_addr;
470#ifndef IP_HDRINCL
471 if (bind(sndsock, (struct sockaddr *)&from, sizeof(from)) < 0)
472 bb_perror_msg_and_die("bind");
473#endif
474 }
475
476 fprintf(stderr, "traceroute to %s (%s)", hostname,
477 inet_ntoa(to->sin_addr));
478 if (source)
479 fprintf(stderr, " from %s", source);
480 fprintf(stderr, ", %d hops max, %d byte packets\n", max_ttl, datalen);
481
482 for (ttl = 1; ttl <= max_ttl; ++ttl) {
483 u_long lastaddr = 0;
484 int got_there = 0;
485 int unreachable = 0;
486
487 printf("%2d ", ttl);
488 for (probe = 0; probe < nprobes; ++probe) {
489 int cc, reset_timer;
490 struct timeval t1, t2;
491 struct timezone tz;
492 struct ip *ip;
493
494 (void) gettimeofday(&t1, &tz);
495 send_probe(++seq, ttl);
496 reset_timer = 1;
497 while ((cc = wait_for_reply(s, &from, reset_timer)) != 0) {
498 (void) gettimeofday(&t2, &tz);
499 if ((i = packet_ok(packet, cc, &from, seq))) {
500 reset_timer = 1;
501 if (from.sin_addr.s_addr != lastaddr) {
502 print(packet, cc, &from);
503 lastaddr = from.sin_addr.s_addr;
504 }
505 printf(" %g ms", deltaT(&t1, &t2));
506 switch(i - 1) {
507 case ICMP_UNREACH_PORT:
508 ip = (struct ip *)packet;
509 if (ip->ip_ttl <= 1)
510 printf(" !");
511 ++got_there;
512 break;
513 case ICMP_UNREACH_NET:
514 ++unreachable;
515 printf(" !N");
516 break;
517 case ICMP_UNREACH_HOST:
518 ++unreachable;
519 printf(" !H");
520 break;
521 case ICMP_UNREACH_PROTOCOL:
522 ++got_there;
523 printf(" !P");
524 break;
525 case ICMP_UNREACH_NEEDFRAG:
526 ++unreachable;
527 printf(" !F");
528 break;
529 case ICMP_UNREACH_SRCFAIL:
530 ++unreachable;
531 printf(" !S");
532 break;
533 }
534 break;
535 } else
536 reset_timer = 0;
537 }
538 if (cc == 0)
539 printf(" *");
540 (void) fflush(stdout);
541 }
542 putchar('\n');
543 if (got_there || unreachable >= nprobes-1)
544 return 0;
545 }
546
547 return 0;
548}