source: trunk/third/traceroute/traceroute.c @ 14140

Revision 14140, 35.0 KB checked in by danw, 25 years ago (diff)
kill savestr() and use strdup() instead, because the memory savestr() returns can't safely be free()ed. Fixes a SEGV under Solaris.
Line 
1/*
2 * Copyright (c) 1988, 1989, 1991, 1994, 1995, 1996, 1997
3 *      The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that: (1) source code distributions
7 * retain the above copyright notice and this paragraph in its entirety, (2)
8 * distributions including binary code include the above copyright notice and
9 * this paragraph in its entirety in the documentation or other materials
10 * provided with the distribution, and (3) all advertising materials mentioning
11 * features or use of this software display the following acknowledgement:
12 * ``This product includes software developed by the University of California,
13 * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
14 * the University nor the names of its contributors may be used to endorse
15 * or promote products derived from this software without specific prior
16 * written permission.
17 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
18 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
19 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
20 */
21
22#ifndef lint
23static const char copyright[] =
24    "@(#) Copyright (c) 1988, 1989, 1991, 1994, 1995, 1996, 1997\n\
25The Regents of the University of California.  All rights reserved.\n";
26static const char rcsid[] =
27    "@(#)$Header: /afs/dev.mit.edu/source/repository/third/traceroute/traceroute.c,v 1.4 1999-12-27 17:52:37 danw Exp $ (LBL)";
28#endif
29
30/*
31 * traceroute host  - trace the route ip packets follow going to "host".
32 *
33 * Attempt to trace the route an ip packet would follow to some
34 * internet host.  We find out intermediate hops by launching probe
35 * packets with a small ttl (time to live) then listening for an
36 * icmp "time exceeded" reply from a gateway.  We start our probes
37 * with a ttl of one and increase by one until we get an icmp "port
38 * unreachable" (which means we got to "host") or hit a max (which
39 * defaults to 30 hops & can be changed with the -m flag).  Three
40 * probes (change with -q flag) are sent at each ttl setting and a
41 * line is printed showing the ttl, address of the gateway and
42 * round trip time of each probe.  If the probe answers come from
43 * different gateways, the address of each responding system will
44 * be printed.  If there is no response within a 5 sec. timeout
45 * interval (changed with the -w flag), a "*" is printed for that
46 * probe.
47 *
48 * Probe packets are UDP format.  We don't want the destination
49 * host to process them so the destination port is set to an
50 * unlikely value (if some clod on the destination is using that
51 * value, it can be changed with the -p flag).
52 *
53 * A sample use might be:
54 *
55 *     [yak 71]% traceroute nis.nsf.net.
56 *     traceroute to nis.nsf.net (35.1.1.48), 30 hops max, 56 byte packet
57 *      1  helios.ee.lbl.gov (128.3.112.1)  19 ms  19 ms  0 ms
58 *      2  lilac-dmc.Berkeley.EDU (128.32.216.1)  39 ms  39 ms  19 ms
59 *      3  lilac-dmc.Berkeley.EDU (128.32.216.1)  39 ms  39 ms  19 ms
60 *      4  ccngw-ner-cc.Berkeley.EDU (128.32.136.23)  39 ms  40 ms  39 ms
61 *      5  ccn-nerif22.Berkeley.EDU (128.32.168.22)  39 ms  39 ms  39 ms
62 *      6  128.32.197.4 (128.32.197.4)  40 ms  59 ms  59 ms
63 *      7  131.119.2.5 (131.119.2.5)  59 ms  59 ms  59 ms
64 *      8  129.140.70.13 (129.140.70.13)  99 ms  99 ms  80 ms
65 *      9  129.140.71.6 (129.140.71.6)  139 ms  239 ms  319 ms
66 *     10  129.140.81.7 (129.140.81.7)  220 ms  199 ms  199 ms
67 *     11  nic.merit.edu (35.1.1.48)  239 ms  239 ms  239 ms
68 *
69 * Note that lines 2 & 3 are the same.  This is due to a buggy
70 * kernel on the 2nd hop system -- lbl-csam.arpa -- that forwards
71 * packets with a zero ttl.
72 *
73 * A more interesting example is:
74 *
75 *     [yak 72]% traceroute allspice.lcs.mit.edu.
76 *     traceroute to allspice.lcs.mit.edu (18.26.0.115), 30 hops max
77 *      1  helios.ee.lbl.gov (128.3.112.1)  0 ms  0 ms  0 ms
78 *      2  lilac-dmc.Berkeley.EDU (128.32.216.1)  19 ms  19 ms  19 ms
79 *      3  lilac-dmc.Berkeley.EDU (128.32.216.1)  39 ms  19 ms  19 ms
80 *      4  ccngw-ner-cc.Berkeley.EDU (128.32.136.23)  19 ms  39 ms  39 ms
81 *      5  ccn-nerif22.Berkeley.EDU (128.32.168.22)  20 ms  39 ms  39 ms
82 *      6  128.32.197.4 (128.32.197.4)  59 ms  119 ms  39 ms
83 *      7  131.119.2.5 (131.119.2.5)  59 ms  59 ms  39 ms
84 *      8  129.140.70.13 (129.140.70.13)  80 ms  79 ms  99 ms
85 *      9  129.140.71.6 (129.140.71.6)  139 ms  139 ms  159 ms
86 *     10  129.140.81.7 (129.140.81.7)  199 ms  180 ms  300 ms
87 *     11  129.140.72.17 (129.140.72.17)  300 ms  239 ms  239 ms
88 *     12  * * *
89 *     13  128.121.54.72 (128.121.54.72)  259 ms  499 ms  279 ms
90 *     14  * * *
91 *     15  * * *
92 *     16  * * *
93 *     17  * * *
94 *     18  ALLSPICE.LCS.MIT.EDU (18.26.0.115)  339 ms  279 ms  279 ms
95 *
96 * (I start to see why I'm having so much trouble with mail to
97 * MIT.)  Note that the gateways 12, 14, 15, 16 & 17 hops away
98 * either don't send ICMP "time exceeded" messages or send them
99 * with a ttl too small to reach us.  14 - 17 are running the
100 * MIT C Gateway code that doesn't send "time exceeded"s.  God
101 * only knows what's going on with 12.
102 *
103 * The silent gateway 12 in the above may be the result of a bug in
104 * the 4.[23]BSD network code (and its derivatives):  4.x (x <= 3)
105 * sends an unreachable message using whatever ttl remains in the
106 * original datagram.  Since, for gateways, the remaining ttl is
107 * zero, the icmp "time exceeded" is guaranteed to not make it back
108 * to us.  The behavior of this bug is slightly more interesting
109 * when it appears on the destination system:
110 *
111 *      1  helios.ee.lbl.gov (128.3.112.1)  0 ms  0 ms  0 ms
112 *      2  lilac-dmc.Berkeley.EDU (128.32.216.1)  39 ms  19 ms  39 ms
113 *      3  lilac-dmc.Berkeley.EDU (128.32.216.1)  19 ms  39 ms  19 ms
114 *      4  ccngw-ner-cc.Berkeley.EDU (128.32.136.23)  39 ms  40 ms  19 ms
115 *      5  ccn-nerif35.Berkeley.EDU (128.32.168.35)  39 ms  39 ms  39 ms
116 *      6  csgw.Berkeley.EDU (128.32.133.254)  39 ms  59 ms  39 ms
117 *      7  * * *
118 *      8  * * *
119 *      9  * * *
120 *     10  * * *
121 *     11  * * *
122 *     12  * * *
123 *     13  rip.Berkeley.EDU (128.32.131.22)  59 ms !  39 ms !  39 ms !
124 *
125 * Notice that there are 12 "gateways" (13 is the final
126 * destination) and exactly the last half of them are "missing".
127 * What's really happening is that rip (a Sun-3 running Sun OS3.5)
128 * is using the ttl from our arriving datagram as the ttl in its
129 * icmp reply.  So, the reply will time out on the return path
130 * (with no notice sent to anyone since icmp's aren't sent for
131 * icmp's) until we probe with a ttl that's at least twice the path
132 * length.  I.e., rip is really only 7 hops away.  A reply that
133 * returns with a ttl of 1 is a clue this problem exists.
134 * Traceroute prints a "!" after the time if the ttl is <= 1.
135 * Since vendors ship a lot of obsolete (DEC's Ultrix, Sun 3.x) or
136 * non-standard (HPUX) software, expect to see this problem
137 * frequently and/or take care picking the target host of your
138 * probes.
139 *
140 * Other possible annotations after the time are !H, !N, !P (got a host,
141 * network or protocol unreachable, respectively), !S or !F (source
142 * route failed or fragmentation needed -- neither of these should
143 * ever occur and the associated gateway is busted if you see one).  If
144 * almost all the probes result in some kind of unreachable, traceroute
145 * will give up and exit.
146 *
147 * Notes
148 * -----
149 * This program must be run by root or be setuid.  (I suggest that
150 * you *don't* make it setuid -- casual use could result in a lot
151 * of unnecessary traffic on our poor, congested nets.)
152 *
153 * This program requires a kernel mod that does not appear in any
154 * system available from Berkeley:  A raw ip socket using proto
155 * IPPROTO_RAW must interpret the data sent as an ip datagram (as
156 * opposed to data to be wrapped in a ip datagram).  See the README
157 * file that came with the source to this program for a description
158 * of the mods I made to /sys/netinet/raw_ip.c.  Your mileage may
159 * vary.  But, again, ANY 4.x (x < 4) BSD KERNEL WILL HAVE TO BE
160 * MODIFIED TO RUN THIS PROGRAM.
161 *
162 * The udp port usage may appear bizarre (well, ok, it is bizarre).
163 * The problem is that an icmp message only contains 8 bytes of
164 * data from the original datagram.  8 bytes is the size of a udp
165 * header so, if we want to associate replies with the original
166 * datagram, the necessary information must be encoded into the
167 * udp header (the ip id could be used but there's no way to
168 * interlock with the kernel's assignment of ip id's and, anyway,
169 * it would have taken a lot more kernel hacking to allow this
170 * code to set the ip id).  So, to allow two or more users to
171 * use traceroute simultaneously, we use this task's pid as the
172 * source port (the high bit is set to move the port number out
173 * of the "likely" range).  To keep track of which probe is being
174 * replied to (so times and/or hop counts don't get confused by a
175 * reply that was delayed in transit), we increment the destination
176 * port number before each probe.
177 *
178 * Don't use this as a coding example.  I was trying to find a
179 * routing problem and this code sort-of popped out after 48 hours
180 * without sleep.  I was amazed it ever compiled, much less ran.
181 *
182 * I stole the idea for this program from Steve Deering.  Since
183 * the first release, I've learned that had I attended the right
184 * IETF working group meetings, I also could have stolen it from Guy
185 * Almes or Matt Mathis.  I don't know (or care) who came up with
186 * the idea first.  I envy the originators' perspicacity and I'm
187 * glad they didn't keep the idea a secret.
188 *
189 * Tim Seaver, Ken Adelman and C. Philip Wood provided bug fixes and/or
190 * enhancements to the original distribution.
191 *
192 * I've hacked up a round-trip-route version of this that works by
193 * sending a loose-source-routed udp datagram through the destination
194 * back to yourself.  Unfortunately, SO many gateways botch source
195 * routing, the thing is almost worthless.  Maybe one day...
196 *
197 *  -- Van Jacobson (van@ee.lbl.gov)
198 *     Tue Dec 20 03:50:13 PST 1988
199 */
200
201#include <sys/param.h>
202#include <sys/file.h>
203#include <sys/ioctl.h>
204#ifdef HAVE_SYS_SELECT_H
205#include <sys/select.h>
206#endif
207#include <sys/socket.h>
208#include <sys/time.h>
209
210#include <netinet/in_systm.h>
211#include <netinet/in.h>
212#include <netinet/ip.h>
213#include <netinet/ip_var.h>
214#include <netinet/ip_icmp.h>
215#include <netinet/udp.h>
216#include <netinet/udp_var.h>
217
218#include <arpa/inet.h>
219
220#include <ctype.h>
221#include <errno.h>
222#ifdef HAVE_MALLOC_H
223#include <malloc.h>
224#endif
225#include <memory.h>
226#include <netdb.h>
227#include <stdio.h>
228#include <stdlib.h>
229#include <string.h>
230#include <unistd.h>
231
232#include "gnuc.h"
233#ifdef HAVE_OS_PROTO_H
234#include "os-proto.h"
235#endif
236
237#include "ifaddrlist.h"
238
239/* Maximum number of gateways (include room for one noop) */
240#define NGATEWAYS ((int)((MAX_IPOPTLEN - IPOPT_MINOFF - 1) / sizeof(u_int32_t)))
241
242#ifndef MAXHOSTNAMELEN
243#define MAXHOSTNAMELEN  64
244#endif
245
246#define Fprintf (void)fprintf
247#define Printf (void)printf
248
249/* Host name and address list */
250struct hostinfo {
251        char *name;
252        int n;
253        u_int32_t *addrs;
254};
255
256/* Data section of the probe packet */
257struct outdata {
258        u_char seq;             /* sequence number of this packet */
259        u_char ttl;             /* ttl packet left with */
260        struct timeval tv;      /* time packet left */
261};
262
263u_char  packet[512];            /* last inbound (icmp) packet */
264
265struct ip *outip;               /* last output (udp) packet */
266struct udphdr *outudp;          /* last output (udp) packet */
267struct outdata *outdata;        /* last output (udp) packet */
268
269struct icmp *outicmp;           /* last output (icmp) packet */
270
271/* loose source route gateway list (including room for final destination) */
272u_int32_t gwlist[NGATEWAYS + 1];
273
274int s;                          /* receive (icmp) socket file descriptor */
275int sndsock;                    /* send (udp/icmp) socket file descriptor */
276
277struct sockaddr whereto;        /* Who to try to reach */
278struct sockaddr_in wherefrom;   /* Who we are */
279int packlen;                    /* total length of packet */
280int minpacket;                  /* min ip packet size */
281int maxpacket = 32 * 1024;      /* max ip packet size */
282
283char *prog;
284char *source;
285char *hostname;
286char *device;
287
288int nprobes = 3;
289int max_ttl = 30;
290int first_ttl = 1;
291u_short ident;
292u_short port = 32768 + 666;     /* start udp dest port # for probe packets */
293
294int options;                    /* socket options */
295int verbose;
296int waittime = 5;               /* time to wait for response (in seconds) */
297int nflag;                      /* print addresses numerically */
298int useicmp;                    /* use icmp echo instead of udp packets */
299#ifdef CANT_HACK_CKSUM
300int docksum = 0;                /* don't calculate checksums */
301#else
302int docksum = 1;                /* calculate checksums */
303#endif
304int optlen;                     /* length of ip options */
305
306extern int optind;
307extern int opterr;
308extern char *optarg;
309
310/* Forwards */
311double  deltaT(struct timeval *, struct timeval *);
312void    freehostinfo(struct hostinfo *);
313void    getaddr(u_int32_t *, char *);
314struct  hostinfo *gethostinfo(char *);
315u_short in_cksum(u_short *, int);
316char    *inetname(struct in_addr);
317int     main(int, char **);
318int     packet_ok(u_char *, int, struct sockaddr_in *, int);
319char    *pr_type(u_char);
320void    print(u_char *, int, struct sockaddr_in *);
321void    send_probe(int, int, struct timeval *);
322void    setsin(struct sockaddr_in *, u_int32_t);
323int     str2val(const char *, const char *, int, int);
324void    tvsub(struct timeval *, struct timeval *);
325__dead  void usage(void);
326int     wait_for_reply(int, struct sockaddr_in *, struct timeval *);
327
328int
329main(int argc, char **argv)
330{
331        register int op, code, n;
332        register char *cp;
333        register u_char *outp;
334        register u_int32_t *ap;
335        register struct sockaddr_in *from = &wherefrom;
336        register struct sockaddr_in *to = (struct sockaddr_in *)&whereto;
337        register struct hostinfo *hi;
338        int on = 1;
339        register struct protoent *pe;
340        register int ttl, probe, i;
341        register int seq = 0;
342        int tos = 0, settos = 0;
343        register int lsrr = 0;
344        register u_short off = 0;
345        struct ifaddrlist *al;
346        char errbuf[132];
347
348        if ((cp = strrchr(argv[0], '/')) != NULL)
349                prog = cp + 1;
350        else
351                prog = argv[0];
352
353        opterr = 0;
354        while ((op = getopt(argc, argv, "dFInrvxf:g:i:m:p:q:s:t:w:")) != EOF)
355                switch (op) {
356
357                case 'd':
358                        options |= SO_DEBUG;
359                        break;
360
361                case 'f':
362                        first_ttl = str2val(optarg, "first ttl", 1, 255);
363                        break;
364
365                case 'F':
366                        off = IP_DF;
367                        break;
368
369                case 'g':
370                        if (lsrr >= NGATEWAYS) {
371                                Fprintf(stderr,
372                                    "%s: No more than %d gateways\n",
373                                    prog, NGATEWAYS);
374                                exit(1);
375                        }
376                        getaddr(gwlist + lsrr, optarg);
377                        ++lsrr;
378                        break;
379
380                case 'i':
381                        device = optarg;
382                        break;
383
384                case 'I':
385                        ++useicmp;
386                        break;
387
388                case 'm':
389                        max_ttl = str2val(optarg, "max ttl", 1, 255);
390                        break;
391
392                case 'n':
393                        ++nflag;
394                        break;
395
396                case 'p':
397                        port = str2val(optarg, "port", 1, -1);
398                        break;
399
400                case 'q':
401                        nprobes = str2val(optarg, "nprobes", 1, -1);
402                        break;
403
404                case 'r':
405                        options |= SO_DONTROUTE;
406                        break;
407
408                case 's':
409                        /*
410                         * set the ip source address of the outbound
411                         * probe (e.g., on a multi-homed host).
412                         */
413                        source = optarg;
414                        break;
415
416                case 't':
417                        tos = str2val(optarg, "tos", 0, 255);
418                        ++settos;
419                        break;
420
421                case 'v':
422                        ++verbose;
423                        break;
424
425                case 'x':
426                        docksum = (docksum == 0);
427                        break;
428
429                case 'w':
430                        waittime = str2val(optarg, "wait time", 2, 24 * 3600);
431                        break;
432
433                default:
434                        usage();
435                }
436
437        if (first_ttl > max_ttl) {
438                Fprintf(stderr,
439                    "%s: first ttl (%d) may not be greater than max ttl (%d)\n",
440                    prog, first_ttl, max_ttl);
441                exit(1);
442        }
443
444        if (!docksum)
445                Fprintf(stderr, "%s: Warning: ckecksums disabled\n", prog);
446
447        if (lsrr > 0)
448                optlen = (lsrr + 1) * sizeof(gwlist[0]);
449        minpacket = sizeof(*outip) + sizeof(*outdata) + optlen;
450        if (useicmp)
451                minpacket += 8;                 /* XXX magic number */
452        else
453                minpacket += sizeof(*outudp);
454        if (packlen == 0)
455                packlen = minpacket;            /* minimum sized packet */
456        else if (minpacket > packlen || packlen > maxpacket) {
457                Fprintf(stderr, "%s: packet size must be %d <= s <= %d\n",
458                    prog, minpacket, maxpacket);
459                exit(1);
460        }
461
462        /* Process destination and optional packet size */
463        switch (argc - optind) {
464
465        case 2:
466                packlen = str2val(argv[optind + 1],
467                    "packet length", minpacket, -1);
468                /* Fall through */
469
470        case 1:
471                hostname = argv[optind];
472                hi = gethostinfo(hostname);
473                setsin(to, hi->addrs[0]);
474                if (hi->n > 1)
475                        Fprintf(stderr,
476                    "%s: Warning: %s has multiple addresses; using %s\n",
477                                prog, hostname, inet_ntoa(to->sin_addr));
478                hostname = hi->name;
479                hi->name = NULL;
480                freehostinfo(hi);
481                break;
482
483        default:
484                usage();
485        }
486
487#ifdef HAVE_SETLINEBUF
488        setlinebuf (stdout);
489#else
490        setvbuf(stdout, NULL, _IOLBF, 0);
491#endif
492
493        outip = (struct ip *)malloc((unsigned)packlen);
494        if (outip == NULL) {
495                Fprintf(stderr, "%s: malloc: %s\n", prog, strerror(errno));
496                exit(1);
497        }
498        memset((char *)outip, 0, packlen);
499
500        outip->ip_v = IPVERSION;
501        if (settos)
502                outip->ip_tos = tos;
503#ifdef BYTESWAP_IP_LEN
504        outip->ip_len = htons(packlen);
505#else
506        outip->ip_len = packlen;
507#endif
508        outip->ip_off = off;
509        outp = (u_char *)(outip + 1);
510#ifdef HAVE_RAW_OPTIONS
511        if (lsrr > 0) {
512                register u_char *optlist;
513
514                optlist = outp;
515                outp += optlen;
516
517                /* final hop */
518                gwlist[lsrr] = to->sin_addr.s_addr;
519
520                outip->ip_dst.s_addr = gwlist[0];
521
522                /* force 4 byte alignment */
523                optlist[0] = IPOPT_NOP;
524                /* loose source route option */
525                optlist[1] = IPOPT_LSRR;
526                i = lsrr * sizeof(gwlist[0]);
527                optlist[2] = i + 3;
528                /* Pointer to LSRR addresses */
529                optlist[3] = IPOPT_MINOFF;
530                memcpy(optlist + 4, gwlist + 1, i);
531        } else
532#endif
533                outip->ip_dst = to->sin_addr;
534
535        outip->ip_hl = (outp - (u_char *)outip) >> 2;
536        ident = (getpid() & 0xffff) | 0x8000;
537        if (useicmp) {
538                outip->ip_p = IPPROTO_ICMP;
539
540                outicmp = (struct icmp *)outp;
541                outicmp->icmp_type = ICMP_ECHO;
542                outicmp->icmp_id = htons(ident);
543
544                outdata = (struct outdata *)(outp + 8); /* XXX magic number */
545        } else {
546                outip->ip_p = IPPROTO_UDP;
547
548                outudp = (struct udphdr *)outp;
549                outudp->uh_sport = htons(ident);
550                outudp->uh_ulen =
551                    htons((u_short)(packlen - (sizeof(*outip) + optlen)));
552                outdata = (struct outdata *)(outudp + 1);
553        }
554
555        cp = "icmp";
556        if ((pe = getprotobyname(cp)) == NULL) {
557                Fprintf(stderr, "%s: unknown protocol %s\n", prog, cp);
558                exit(1);
559        }
560        if ((s = socket(AF_INET, SOCK_RAW, pe->p_proto)) < 0) {
561                Fprintf(stderr, "%s: icmp socket: %s\n", prog, strerror(errno));
562                exit(1);
563        }
564        if (options & SO_DEBUG)
565                (void)setsockopt(s, SOL_SOCKET, SO_DEBUG, (char *)&on,
566                    sizeof(on));
567        if (options & SO_DONTROUTE)
568                (void)setsockopt(s, SOL_SOCKET, SO_DONTROUTE, (char *)&on,
569                    sizeof(on));
570
571#ifndef __hpux
572        sndsock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
573#else
574        sndsock = socket(AF_INET, SOCK_RAW,
575            useicmp ? IPPROTO_ICMP : IPPROTO_UDP);
576#endif
577        if (sndsock < 0) {
578                Fprintf(stderr, "%s: raw socket: %s\n", prog, strerror(errno));
579                exit(1);
580        }
581
582        /* Revert to non-privileged user after opening sockets */
583        setuid(getuid());
584
585#if defined(IP_OPTIONS) && !defined(HAVE_RAW_OPTIONS)
586        if (lsrr > 0) {
587                u_char optlist[MAX_IPOPTLEN];
588
589                cp = "ip";
590                if ((pe = getprotobyname(cp)) == NULL) {
591                        Fprintf(stderr, "%s: unknown protocol %s\n", prog, cp);
592                        exit(1);
593                }
594
595                /* final hop */
596                gwlist[lsrr] = to->sin_addr.s_addr;
597                ++lsrr;
598
599                /* force 4 byte alignment */
600                optlist[0] = IPOPT_NOP;
601                /* loose source route option */
602                optlist[1] = IPOPT_LSRR;
603                i = lsrr * sizeof(gwlist[0]);
604                optlist[2] = i + 3;
605                /* Pointer to LSRR addresses */
606                optlist[3] = IPOPT_MINOFF;
607                memcpy(optlist + 4, gwlist, i);
608
609                if ((setsockopt(sndsock, pe->p_proto, IP_OPTIONS, optlist,
610                    i + sizeof(gwlist[0]))) < 0) {
611                        Fprintf(stderr, "%s: IP_OPTIONS: %s\n",
612                            prog, strerror(errno));
613                        exit(1);
614                    }
615        }
616#endif
617
618#ifdef SO_SNDBUF
619        if (setsockopt(sndsock, SOL_SOCKET, SO_SNDBUF, (char *)&packlen,
620            sizeof(packlen)) < 0) {
621                Fprintf(stderr, "%s: SO_SNDBUF: %s\n", prog, strerror(errno));
622                exit(1);
623        }
624#endif
625#ifdef IP_HDRINCL
626        if (setsockopt(sndsock, IPPROTO_IP, IP_HDRINCL, (char *)&on,
627            sizeof(on)) < 0) {
628                Fprintf(stderr, "%s: IP_HDRINCL: %s\n", prog, strerror(errno));
629                exit(1);
630        }
631#else
632#ifdef IP_TOS
633        if (settos && setsockopt(sndsock, IPPROTO_IP, IP_TOS,
634            (char *)&tos, sizeof(tos)) < 0) {
635                Fprintf(stderr, "%s: setsockopt tos %d: %s\n",
636                    prog, tos, strerror(errno));
637                exit(1);
638        }
639#endif
640#endif
641        if (options & SO_DEBUG)
642                (void)setsockopt(sndsock, SOL_SOCKET, SO_DEBUG, (char *)&on,
643                    sizeof(on));
644        if (options & SO_DONTROUTE)
645                (void)setsockopt(sndsock, SOL_SOCKET, SO_DONTROUTE, (char *)&on,
646                    sizeof(on));
647
648        /* Get the interface address list */
649        n = ifaddrlist(&al, errbuf, sizeof errbuf);
650        if (n < 0) {
651                Fprintf(stderr, "%s: ifaddrlist: %s\n", prog, errbuf);
652                exit(1);
653        }
654        if (n == 0) {
655                Fprintf(stderr,
656                    "%s: Can't find any network interfaces\n", prog);
657                exit(1);
658        }
659
660        /* Look for a specific device */
661        if (device != NULL) {
662                for (i = n; i > 0; --i, ++al)
663                        if (strcmp(device, al->device) == 0)
664                                break;
665                if (i <= 0) {
666                        Fprintf(stderr, "%s: Can't find interface %s\n",
667                            prog, device);
668                        exit(1);
669                }
670        }
671
672        /* Determine our source address */
673        if (source == NULL) {
674                /*
675                 * If a device was specified, use the interface address.
676                 * Otherwise, use the first interface found.
677                 * Warn if there are more than one.
678                 */
679                setsin(from, al->addr);
680                if (n > 1 && device == NULL) {
681                        Fprintf(stderr,
682                    "%s: Warning: Multiple interfaces found; using %s @ %s\n",
683                            prog, inet_ntoa(from->sin_addr), al->device);
684                }
685        } else {
686                hi = gethostinfo(source);
687                source = hi->name;
688                hi->name = NULL;
689                if (device == NULL) {
690                        /*
691                         * Use the first interface found.
692                         * Warn if there are more than one.
693                         */
694                        setsin(from, hi->addrs[0]);
695                        if (hi->n > 1)
696                                Fprintf(stderr,
697                        "%s: Warning: %s has multiple addresses; using %s\n",
698                                    prog, source, inet_ntoa(from->sin_addr));
699                } else {
700                        /*
701                         * Make sure the source specified matches the
702                         * interface address.
703                         */
704                        for (i = hi->n, ap = hi->addrs; i > 0; --i, ++ap)
705                                if (*ap == al->addr)
706                                        break;
707                        if (i <= 0) {
708                                Fprintf(stderr,
709                                    "%s: %s is not on interface %s\n",
710                                    prog, source, device);
711                                exit(1);
712                        }
713                        setsin(from, *ap);
714                }
715                freehostinfo(hi);
716        }
717        outip->ip_src = from->sin_addr;
718#ifndef IP_HDRINCL
719        if (bind(sndsock, (struct sockaddr *)from, sizeof(*from)) < 0) {
720                Fprintf(stderr, "%s: bind: %s\n",
721                    prog, strerror(errno));
722                exit (1);
723        }
724#endif
725
726        Fprintf(stderr, "%s to %s (%s)",
727            prog, hostname, inet_ntoa(to->sin_addr));
728        if (source)
729                Fprintf(stderr, " from %s", source);
730        Fprintf(stderr, ", %d hops max, %d byte packets\n", max_ttl, packlen);
731        (void)fflush(stderr);
732
733        for (ttl = first_ttl; ttl <= max_ttl; ++ttl) {
734                u_int32_t lastaddr = 0;
735                int got_there = 0;
736                int unreachable = 0;
737
738                Printf("%2d ", ttl);
739                for (probe = 0; probe < nprobes; ++probe) {
740                        register int cc;
741                        struct timeval t1, t2;
742                        struct timezone tz;
743                        register struct ip *ip;
744
745                        (void)gettimeofday(&t1, &tz);
746                        send_probe(++seq, ttl, &t1);
747                        while ((cc = wait_for_reply(s, from, &t1)) != 0) {
748                                (void)gettimeofday(&t2, &tz);
749                                i = packet_ok(packet, cc, from, seq);
750                                /* Skip short packet */
751                                if (i == 0)
752                                        continue;
753                                if (from->sin_addr.s_addr != lastaddr) {
754                                        print(packet, cc, from);
755                                        lastaddr = from->sin_addr.s_addr;
756                                }
757                                Printf("  %.3f ms", deltaT(&t1, &t2));
758                                if (i == -2) {
759#ifndef ARCHAIC
760                                        ip = (struct ip *)packet;
761                                        if (ip->ip_ttl <= 1)
762                                                Printf(" !");
763#endif
764                                        ++got_there;
765                                        break;
766                                }
767                                /* time exceeded in transit */
768                                if (i == -1)
769                                        break;
770                                code = i - 1;
771                                switch (code) {
772
773                                case ICMP_UNREACH_PORT:
774#ifndef ARCHAIC
775                                        ip = (struct ip *)packet;
776                                        if (ip->ip_ttl <= 1)
777                                                Printf(" !");
778#endif
779                                        ++got_there;
780                                        break;
781
782                                case ICMP_UNREACH_NET:
783                                        ++unreachable;
784                                        Printf(" !N");
785                                        break;
786
787                                case ICMP_UNREACH_HOST:
788                                        ++unreachable;
789                                        Printf(" !H");
790                                        break;
791
792                                case ICMP_UNREACH_PROTOCOL:
793                                        ++got_there;
794                                        Printf(" !P");
795                                        break;
796
797                                case ICMP_UNREACH_NEEDFRAG:
798                                        ++unreachable;
799                                        Printf(" !F");
800                                        break;
801
802                                case ICMP_UNREACH_SRCFAIL:
803                                        ++unreachable;
804                                        Printf(" !S");
805                                        break;
806
807/* rfc1716 */
808#ifndef ICMP_UNREACH_FILTER_PROHIB
809#define ICMP_UNREACH_FILTER_PROHIB      13      /* admin prohibited filter */
810#endif
811                                case ICMP_UNREACH_FILTER_PROHIB:
812                                        ++unreachable;
813                                        Printf(" !X");
814                                        break;
815
816                                default:
817                                        ++unreachable;
818                                        Printf(" !<%d>", code);
819                                        break;
820                                }
821                                break;
822                        }
823                        if (cc == 0)
824                                Printf(" *");
825                        (void)fflush(stdout);
826                }
827                putchar('\n');
828                if (got_there ||
829                    (unreachable > 0 && unreachable >= nprobes - 1))
830                        break;
831        }
832        exit(0);
833}
834
835int
836wait_for_reply(register int sock, register struct sockaddr_in *fromp,
837    register struct timeval *tp)
838{
839        fd_set fds;
840        struct timeval now, wait;
841        struct timezone tz;
842        register int cc = 0;
843        int fromlen = sizeof(*fromp);
844        int retval;
845
846        FD_ZERO(&fds);
847        FD_SET(sock, &fds);
848
849        wait.tv_sec = tp->tv_sec + waittime;
850        wait.tv_usec = tp->tv_usec;
851        (void)gettimeofday(&now, &tz);
852        tvsub(&wait, &now);
853
854        retval = select(sock + 1, &fds, NULL, NULL, &wait);
855        if (retval < 0)  {
856                /* If we continue, we probably just flood the remote host. */
857                Fprintf(stderr, "%s: select: %s\n", prog, strerror(errno));
858                exit(1);
859        }
860        if (retval > 0)  {
861                cc = recvfrom(s, (char *)packet, sizeof(packet), 0,
862                            (struct sockaddr *)fromp, &fromlen);
863        }
864
865        return(cc);
866}
867
868void
869send_probe(register int seq, int ttl, register struct timeval *tp)
870{
871        register int cc;
872        register struct udpiphdr * ui;
873        struct ip tip;
874
875        outip->ip_ttl = ttl;
876#ifndef __hpux
877        outip->ip_id = htons(ident + seq);
878#endif
879
880        /*
881         * In most cases, the kernel will recalculate the ip checksum.
882         * But we must do it anyway so that the udp checksum comes out
883         * right.
884         */
885        if (docksum) {
886                outip->ip_sum =
887                    in_cksum((u_short *)outip, sizeof(*outip) + optlen);
888                if (outip->ip_sum == 0)
889                        outip->ip_sum = 0xffff;
890        }
891
892        /* Payload */
893        outdata->seq = seq;
894        outdata->ttl = ttl;
895        outdata->tv = *tp;
896
897        if (useicmp)
898                outicmp->icmp_seq = htons(seq);
899        else
900                outudp->uh_dport = htons(port + seq);
901
902        /* (We can only do the checksum if we know our ip address) */
903        if (docksum) {
904                if (useicmp) {
905                        outicmp->icmp_cksum = 0;
906                        outicmp->icmp_cksum = in_cksum((u_short *)outicmp,
907                            packlen - (sizeof(*outip) + optlen));
908                        if (outicmp->icmp_cksum == 0)
909                                outicmp->icmp_cksum = 0xffff;
910                } else {
911                        /* Checksum (must save and restore ip header) */
912                        tip = *outip;
913                        ui = (struct udpiphdr *)outip;
914                        ui->ui_next = 0;
915                        ui->ui_prev = 0;
916                        ui->ui_x1 = 0;
917                        ui->ui_len = outudp->uh_ulen;
918                        outudp->uh_sum = 0;
919                        outudp->uh_sum = in_cksum((u_short *)ui, packlen);
920                        if (outudp->uh_sum == 0)
921                                outudp->uh_sum = 0xffff;
922                        *outip = tip;
923                }
924        }
925
926        /* XXX undocumented debugging hack */
927        if (verbose > 1) {
928                register const u_short *sp;
929                register int nshorts, i;
930
931                sp = (u_short *)outip;
932                nshorts = (u_int)packlen / sizeof(u_short);
933                i = 0;
934                Printf("[ %d bytes", packlen);
935                while (--nshorts >= 0) {
936                        if ((i++ % 8) == 0)
937                                Printf("\n\t");
938                        Printf(" %04x", ntohs(*sp++));
939                }
940                if (packlen & 1) {
941                        if ((i % 8) == 0)
942                                Printf("\n\t");
943                        Printf(" %02x", *(u_char *)sp);
944                }
945                Printf("]\n");
946        }
947
948#if !defined(IP_HDRINCL) && defined(IP_TTL)
949        if (setsockopt(sndsock, IPPROTO_IP, IP_TTL,
950            (char *)&ttl, sizeof(ttl)) < 0) {
951                Fprintf(stderr, "%s: setsockopt ttl %d: %s\n",
952                    prog, ttl, strerror(errno));
953                exit(1);
954        }
955#endif
956
957#ifdef __hpux
958        cc = sendto(sndsock, useicmp ? (char *)outicmp : (char *)outudp,
959            packlen - (sizeof(*outip) + optlen), 0, &whereto, sizeof(whereto));
960        if (cc > 0)
961                cc += sizeof(*outip) + optlen;
962#else
963        cc = sendto(sndsock, (char *)outip,
964            packlen, 0, &whereto, sizeof(whereto));
965#endif
966        if (cc < 0 || cc != packlen)  {
967                if (cc < 0)
968                        Fprintf(stderr, "%s: sendto: %s\n",
969                            prog, strerror(errno));
970                Printf("%s: wrote %s %d chars, ret=%d\n",
971                    prog, hostname, packlen, cc);
972                (void)fflush(stdout);
973        }
974}
975
976double
977deltaT(struct timeval *t1p, struct timeval *t2p)
978{
979        register double dt;
980
981        dt = (double)(t2p->tv_sec - t1p->tv_sec) * 1000.0 +
982             (double)(t2p->tv_usec - t1p->tv_usec) / 1000.0;
983        return (dt);
984}
985
986/*
987 * Convert an ICMP "type" field to a printable string.
988 */
989char *
990pr_type(register u_char t)
991{
992        static char *ttab[] = {
993        "Echo Reply",   "ICMP 1",       "ICMP 2",       "Dest Unreachable",
994        "Source Quench", "Redirect",    "ICMP 6",       "ICMP 7",
995        "Echo",         "ICMP 9",       "ICMP 10",      "Time Exceeded",
996        "Param Problem", "Timestamp",   "Timestamp Reply", "Info Request",
997        "Info Reply"
998        };
999
1000        if (t > 16)
1001                return("OUT-OF-RANGE");
1002
1003        return(ttab[t]);
1004}
1005
1006int
1007packet_ok(register u_char *buf, int cc, register struct sockaddr_in *from,
1008    register int seq)
1009{
1010        register struct icmp *icp;
1011        register u_char type, code;
1012        register int hlen;
1013#ifndef ARCHAIC
1014        register struct ip *ip;
1015
1016        ip = (struct ip *) buf;
1017        hlen = ip->ip_hl << 2;
1018        if (cc < hlen + ICMP_MINLEN) {
1019                if (verbose)
1020                        Printf("packet too short (%d bytes) from %s\n", cc,
1021                                inet_ntoa(from->sin_addr));
1022                return (0);
1023        }
1024        cc -= hlen;
1025        icp = (struct icmp *)(buf + hlen);
1026#else
1027        icp = (struct icmp *)buf;
1028#endif
1029        type = icp->icmp_type;
1030        code = icp->icmp_code;
1031        if ((type == ICMP_TIMXCEED && code == ICMP_TIMXCEED_INTRANS) ||
1032            type == ICMP_UNREACH || type == ICMP_ECHOREPLY) {
1033                register struct ip *hip;
1034                register struct udphdr *up;
1035                register struct icmp *hicmp;
1036
1037                hip = &icp->icmp_ip;
1038                hlen = hip->ip_hl << 2;
1039                if (useicmp) {
1040                        /* XXX */
1041                        if (type == ICMP_ECHOREPLY &&
1042                            icp->icmp_id == htons(ident) &&
1043                            icp->icmp_seq == htons(seq))
1044                                return (-2);
1045
1046                        hicmp = (struct icmp *)((u_char *)hip + hlen);
1047                        /* XXX 8 is a magic number */
1048                        if (hlen + 8 <= cc &&
1049                            hip->ip_p == IPPROTO_ICMP &&
1050                            hicmp->icmp_id == htons(ident) &&
1051                            hicmp->icmp_seq == htons(seq))
1052                                return (type == ICMP_TIMXCEED ? -1 : code + 1);
1053                } else {
1054                        up = (struct udphdr *)((u_char *)hip + hlen);
1055                        /* XXX 8 is a magic number */
1056                        if (hlen + 12 <= cc &&
1057                            hip->ip_p == IPPROTO_UDP &&
1058                            up->uh_sport == htons(ident) &&
1059                            up->uh_dport == htons(port + seq))
1060                                return (type == ICMP_TIMXCEED ? -1 : code + 1);
1061                }
1062        }
1063#ifndef ARCHAIC
1064        if (verbose) {
1065                register int i;
1066                u_int32_t *lp = (u_int32_t *)&icp->icmp_ip;
1067
1068                Printf("\n%d bytes from %s to ", cc, inet_ntoa(from->sin_addr));
1069                Printf("%s: icmp type %d (%s) code %d\n",
1070                    inet_ntoa(ip->ip_dst), type, pr_type(type), icp->icmp_code);
1071                for (i = 4; i < cc ; i += sizeof(*lp))
1072                        Printf("%2d: x%8.8x\n", i, *lp++);
1073        }
1074#endif
1075        return(0);
1076}
1077
1078
1079void
1080print(register u_char *buf, register int cc, register struct sockaddr_in *from)
1081{
1082        register struct ip *ip;
1083        register int hlen;
1084
1085        ip = (struct ip *) buf;
1086        hlen = ip->ip_hl << 2;
1087        cc -= hlen;
1088
1089        if (nflag)
1090                Printf(" %s", inet_ntoa(from->sin_addr));
1091        else
1092                Printf(" %s (%s)", inetname(from->sin_addr),
1093                    inet_ntoa(from->sin_addr));
1094
1095        if (verbose)
1096                Printf(" %d bytes to %s", cc, inet_ntoa (ip->ip_dst));
1097}
1098
1099/*
1100 * Checksum routine for Internet Protocol family headers (C Version)
1101 */
1102u_short
1103in_cksum(register u_short *addr, register int len)
1104{
1105        register int nleft = len;
1106        register u_short *w = addr;
1107        register u_short answer;
1108        register int sum = 0;
1109
1110        /*
1111         *  Our algorithm is simple, using a 32 bit accumulator (sum),
1112         *  we add sequential 16 bit words to it, and at the end, fold
1113         *  back all the carry bits from the top 16 bits into the lower
1114         *  16 bits.
1115         */
1116        while (nleft > 1)  {
1117                sum += *w++;
1118                nleft -= 2;
1119        }
1120
1121        /* mop up an odd byte, if necessary */
1122        if (nleft == 1)
1123                sum += *(u_char *)w;
1124
1125        /*
1126         * add back carry outs from top 16 bits to low 16 bits
1127         */
1128        sum = (sum >> 16) + (sum & 0xffff);     /* add hi 16 to low 16 */
1129        sum += (sum >> 16);                     /* add carry */
1130        answer = ~sum;                          /* truncate to 16 bits */
1131        return (answer);
1132}
1133
1134/*
1135 * Subtract 2 timeval structs:  out = out - in.
1136 * Out is assumed to be >= in.
1137 */
1138void
1139tvsub(register struct timeval *out, register struct timeval *in)
1140{
1141
1142        if ((out->tv_usec -= in->tv_usec) < 0)   {
1143                --out->tv_sec;
1144                out->tv_usec += 1000000;
1145        }
1146        out->tv_sec -= in->tv_sec;
1147}
1148
1149/*
1150 * Construct an Internet address representation.
1151 * If the nflag has been supplied, give
1152 * numeric value, otherwise try for symbolic name.
1153 */
1154char *
1155inetname(struct in_addr in)
1156{
1157        register char *cp;
1158        register struct hostent *hp;
1159        static int first = 1;
1160        static char domain[MAXHOSTNAMELEN + 1], line[MAXHOSTNAMELEN + 1];
1161
1162        if (first && !nflag) {
1163                first = 0;
1164                if (gethostname(domain, MAXHOSTNAMELEN) == 0 &&
1165                    (cp = strchr(domain, '.')) != NULL) {
1166                        (void)strncpy(domain, cp + 1, sizeof(domain) - 1);
1167                        domain[sizeof(domain) - 1] = '\0';
1168                } else
1169                        domain[0] = '\0';
1170        }
1171        if (!nflag && in.s_addr != INADDR_ANY) {
1172                hp = gethostbyaddr((char *)&in, sizeof(in), AF_INET);
1173                if (hp != NULL) {
1174                        if ((cp = strchr(hp->h_name, '.')) != NULL &&
1175                            strcmp(cp + 1, domain) == 0)
1176                                *cp = '\0';
1177                        (void)strncpy(line, hp->h_name, sizeof(line) - 1);
1178                        line[sizeof(line) - 1] = '\0';
1179                        return (line);
1180                }
1181        }
1182        return (inet_ntoa(in));
1183}
1184
1185struct hostinfo *
1186gethostinfo(register char *hostname)
1187{
1188        register int n;
1189        register struct hostent *hp;
1190        register struct hostinfo *hi;
1191        register char **p;
1192        register u_int32_t addr, *ap;
1193
1194        hi = calloc(1, sizeof(*hi));
1195        if (hi == NULL) {
1196                Fprintf(stderr, "%s: calloc %s\n", prog, strerror(errno));
1197                exit(1);
1198        }
1199        addr = inet_addr(hostname);
1200        if ((int32_t)addr != -1) {
1201                hi->name = strdup(hostname);
1202                hi->n = 1;
1203                hi->addrs = calloc(1, sizeof(hi->addrs[0]));
1204                if (hi->addrs == NULL) {
1205                        Fprintf(stderr, "%s: calloc %s\n",
1206                            prog, strerror(errno));
1207                        exit(1);
1208                }
1209                hi->addrs[0] = addr;
1210                return (hi);
1211        }
1212
1213        hp = gethostbyname(hostname);
1214        if (hp == NULL) {
1215                Fprintf(stderr, "%s: unknown host %s\n", prog, hostname);
1216                exit(1);
1217        }
1218        if (hp->h_addrtype != AF_INET || hp->h_length != 4) {
1219                Fprintf(stderr, "%s: bad host %s\n", prog, hostname);
1220                exit(1);
1221        }
1222        hi->name = strdup(hp->h_name);
1223        for (n = 0, p = hp->h_addr_list; *p != NULL; ++n, ++p)
1224                continue;
1225        hi->n = n;
1226        hi->addrs = calloc(n, sizeof(hi->addrs[0]));
1227        if (hi->addrs == NULL) {
1228                Fprintf(stderr, "%s: calloc %s\n", prog, strerror(errno));
1229                exit(1);
1230        }
1231        for (ap = hi->addrs, p = hp->h_addr_list; *p != NULL; ++ap, ++p)
1232                memcpy(ap, *p, sizeof(*ap));
1233        return (hi);
1234}
1235
1236void
1237freehostinfo(register struct hostinfo *hi)
1238{
1239        if (hi->name != NULL) {
1240                free(hi->name);
1241                hi->name = NULL;
1242        }
1243        free((char *)hi->addrs);
1244        free((char *)hi);
1245}
1246
1247void
1248getaddr(register u_int32_t *ap, register char *hostname)
1249{
1250        register struct hostinfo *hi;
1251
1252        hi = gethostinfo(hostname);
1253        *ap = hi->addrs[0];
1254        freehostinfo(hi);
1255}
1256
1257void
1258setsin(register struct sockaddr_in *sin, register u_int32_t addr)
1259{
1260
1261        memset(sin, 0, sizeof(*sin));
1262#ifdef HAVE_SOCKADDR_SA_LEN
1263        sin->sin_len = sizeof(*sin);
1264#endif
1265        sin->sin_family = AF_INET;
1266        sin->sin_addr.s_addr = addr;
1267}
1268
1269/* String to value with optional min and max. Handles decimal and hex. */
1270int
1271str2val(register const char *str, register const char *what,
1272    register int mi, register int ma)
1273{
1274        register const char *cp;
1275        register int val;
1276        char *ep;
1277
1278        if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) {
1279                cp = str + 2;
1280                val = (int)strtol(cp, &ep, 16);
1281        } else
1282                val = (int)strtol(str, &ep, 10);
1283        if (*ep != '\0') {
1284                Fprintf(stderr, "%s: \"%s\" bad value for %s \n",
1285                    prog, str, what);
1286                exit(1);
1287        }
1288        if (val < mi && mi >= 0) {
1289                if (mi == 0)
1290                        Fprintf(stderr, "%s: %s must be >= %d\n",
1291                            prog, what, mi);
1292                else
1293                        Fprintf(stderr, "%s: %s must be > %d\n",
1294                            prog, what, mi - 1);
1295                exit(1);
1296        }
1297        if (val > ma && ma >= 0) {
1298                Fprintf(stderr, "%s: %s must be <= %d\n", prog, what, ma);
1299                exit(1);
1300        }
1301        return (val);
1302}
1303
1304__dead void
1305usage(void)
1306{
1307        extern char version[];
1308
1309        Fprintf(stderr, "Version %s\n", version);
1310        Fprintf(stderr, "Usage: %s [-dFInrvx] [-g gateway] [-i iface] \
1311[-f first_ttl] [-m max_ttl]\n\t[ -p port] [-q nqueries] [-s src_addr] [-t tos] \
1312[-w waittime]\n\thost [packetlen]\n",
1313            prog);
1314        exit(1);
1315}
Note: See TracBrowser for help on using the repository browser.