/* * Copyright (c) 1988, 1989, 1991, 1994, 1995, 1996 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that: (1) source code distributions * retain the above copyright notice and this paragraph in its entirety, (2) * distributions including binary code include the above copyright notice and * this paragraph in its entirety in the documentation or other materials * provided with the distribution, and (3) all advertising materials mentioning * features or use of this software display the following acknowledgement: * ``This product includes software developed by the University of California, * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of * the University nor the names of its contributors may be used to endorse * or promote products derived from this software without specific prior * written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ /* * This program comes from Van Jacobson * * Totally rewritten by Eric Wassenaar, Nikhef-H, * * The source of this particular version of the program is available * via anonymous ftp from machine 'ftp.nikhef.nl' [192.16.199.1] * in the directory '/pub/network' as 'traceroute.tar.Z' */ #ifndef lint static char Version[] = "@(#)traceroute.c e07@nikhef.nl (Eric Wassenaar) 991603"; #endif #if defined(apollo) && defined(lint) #define __attribute(x) #endif #if defined(__alpha) && defined(__osf__) && __GNUC__ #define __STDC__ 2 /* workaround for alpha bug */ #endif #undef obsolete /* old code left as a reminder */ #undef notyet /* new code for possible future use */ /* * traceroute host - trace the route ip packets follow going to "host". * * Attempt to trace the route an ip packet would follow to some * internet host. We find out intermediate hops by launching probe * packets with a small ttl (time to live) then listening for an * icmp "time exceeded" reply from a gateway. We start our probes * with a ttl of one and increase by one until we get an icmp "port * unreachable" (which means we got to "host") or hit a max (which * defaults to 30 hops & can be changed with the -m flag). Three * probes (change with -q flag) are sent at each ttl setting and a * line is printed showing the ttl, address of the gateway and * round trip time of each probe. If the probe answers come from * different gateways, the address of each responding system will * be printed. If there is no response within a 3 sec. timeout * interval (changed with the -w flag), a "*" is printed for that * probe. * If the -l flag is used the ttl from the icmp reply will * be printed, otherwise it will be printed only if it has an * unexpected value. * * Probe packets are UDP format. We don't want the destination * host to process them so the destination port is set to an * unlikely value (if some clod on the destination is using that * value, it can be changed with the -p flag). * * A sample use might be: * * [yak 71]% traceroute nis.nsf.net. * traceroute to nis.nsf.net (35.1.1.48), 30 hops max, 56 byte packet * 1 helios.ee.lbl.gov (128.3.112.1) 19 ms 19 ms 0 ms * 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 39 ms 19 ms * 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 39 ms 19 ms * 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 39 ms 40 ms 39 ms * 5 ccn-nerif22.Berkeley.EDU (128.32.168.22) 39 ms 39 ms 39 ms * 6 128.32.197.4 (128.32.197.4) 40 ms 59 ms 59 ms * 7 131.119.2.5 (131.119.2.5) 59 ms 59 ms 59 ms * 8 129.140.70.13 (129.140.70.13) 99 ms 99 ms 80 ms * 9 129.140.71.6 (129.140.71.6) 139 ms 239 ms 319 ms * 10 129.140.81.7 (129.140.81.7) 220 ms 199 ms 199 ms * 11 nic.merit.edu (35.1.1.48) 239 ms 239 ms 239 ms * * Note that lines 2 & 3 are the same. This is due to a buggy * kernel on the 2nd hop system -- lbl-csam.arpa -- that forwards * packets with a zero ttl. * * A more interesting example is: * * [yak 72]% traceroute allspice.lcs.mit.edu. * traceroute to allspice.lcs.mit.edu (18.26.0.115), 30 hops max * 1 helios.ee.lbl.gov (128.3.112.1) 0 ms 0 ms 0 ms * 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 19 ms 19 ms 19 ms * 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 19 ms 19 ms * 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 19 ms 39 ms 39 ms * 5 ccn-nerif22.Berkeley.EDU (128.32.168.22) 20 ms 39 ms 39 ms * 6 128.32.197.4 (128.32.197.4) 59 ms 119 ms 39 ms * 7 131.119.2.5 (131.119.2.5) 59 ms 59 ms 39 ms * 8 129.140.70.13 (129.140.70.13) 80 ms 79 ms 99 ms * 9 129.140.71.6 (129.140.71.6) 139 ms 139 ms 159 ms * 10 129.140.81.7 (129.140.81.7) 199 ms 180 ms 300 ms * 11 129.140.72.17 (129.140.72.17) 300 ms 239 ms 239 ms * 12 * * * * 13 128.121.54.72 (128.121.54.72) 259 ms 499 ms 279 ms * 14 * * * * 15 * * * * 16 * * * * 17 * * * * 18 ALLSPICE.LCS.MIT.EDU (18.26.0.115) 339 ms 279 ms 279 ms * * (I start to see why I'm having so much trouble with mail to * MIT.) Note that the gateways 12, 14, 15, 16 & 17 hops away * either don't send ICMP "time exceeded" messages or send them * with a ttl too small to reach us. 14 - 17 are running the * MIT C Gateway code that doesn't send "time exceeded"s. God * only knows what's going on with 12. * * The silent gateway 12 in the above may be the result of a bug in * the 4.[23]BSD network code (and its derivatives): 4.x (x <= 3) * sends an unreachable message using whatever ttl remains in the * original datagram. Since, for gateways, the remaining ttl is * zero, the icmp "time exceeded" is guaranteed to not make it back * to us. The behavior of this bug is slightly more interesting * when it appears on the destination system: * * 1 helios.ee.lbl.gov (128.3.112.1) 0 ms 0 ms 0 ms * 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 19 ms 39 ms * 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 19 ms 39 ms 19 ms * 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 39 ms 40 ms 19 ms * 5 ccn-nerif35.Berkeley.EDU (128.32.168.35) 39 ms 39 ms 39 ms * 6 csgw.Berkeley.EDU (128.32.133.254) 39 ms 59 ms 39 ms * 7 * * * * 8 * * * * 9 * * * * 10 * * * * 11 * * * * 12 * * * * 13 rip.Berkeley.EDU (128.32.131.22) 59 ms ! 39 ms ! 39 ms ! * * Notice that there are 12 "gateways" (13 is the final * destination) and exactly the last half of them are "missing". * What's really happening is that rip (a Sun-3 running Sun OS3.5) * is using the ttl from our arriving datagram as the ttl in its * icmp reply. So, the reply will time out on the return path * (with no notice sent to anyone since icmp's aren't sent for * icmp's) until we probe with a ttl that's at least twice the path * length. I.e., rip is really only 7 hops away. A reply that * returns with a ttl of 1 is a clue this problem exists. * Traceroute prints a "!" after the time if the ttl is <= 1. * Since vendors ship a lot of obsolete (DEC's Ultrix, Sun 3.x) or * non-standard (HPUX) software, expect to see this problem * frequently and/or take care picking the target host of your * probes. * * Other possible annotations after the time are !H, !N, !P (got a host, * network or protocol unreachable, respectively), !S or !F (source * route failed or fragmentation needed -- neither of these should * ever occur and the associated gateway is busted if you see one). If * almost all the probes result in some kind of unreachable, traceroute * will give up and exit. * * Still other annotations are (ttl="n") and (ttl="n"!), where "n" is the * ttl from the icmp reply packet we received. The first form will be * printed for every packet if you use the -l option. The second form * will be printed (with or without -l) if this ttl is an unexpected value. * We expect that the return path from the n'th hop will contain n hops, * otherwise we know the reply packet is coming back via a different path than * it went out on. Unfortunately, not everyone uses the same starting ttl * value for icmp messages. The common ones used by routers are 29 * (Proteon 8.1 and lower software), 59 (Proteon 8.2), 255 (cisco, BSD * since 4.3 tahoe). 30 and 60 are also often used by hosts, and probably * by some routers, because they were the BSD TCP ttl values. This makes * some "off by one" return paths hard to detect, you might try removing * OLD_BSD_TCP and NEW_BSD_TCP from the case statement if this annoys you. * If you are using the lsrr (-g) code with the -l code you will see many * bogus "!". * * With the -l option you will see the TTL value of the ICMP msg that * came back to you, as in: * * nettlerash> traceroute -l rip.berkeley.edu * traceroute to rip.berkeley.edu (128.32.131.22), 30 hops max, 40 byte packets * 1 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 0 ms (ttl=59) 10 ms (ttl=59) * 2 ccn-nerif35.Berkeley.EDU (128.32.168.35) 10 ms (ttl=58) 10 ms (ttl=58) * 3 csgw.Berkeley.EDU (128.32.133.254) 10 ms (ttl=253) 20 ms (ttl=253) * 4 rip.Berkeley.EDU (128.32.131.22) 30 ms (ttl=252) 30 ms (ttl=252) * * This shows that from nettlerash the route goes through two proteons (ttl= * 59, then 58), followed by a router using max_ttl to rip which also uses * max_ttl. (-l and printing ttl stuff added by cliff@berkeley.edu.) * * Notes * ----- * This program must be run by root or be setuid. (I suggest that * you *don't* make it setuid -- casual use could result in a lot * of unnecessary traffic on our poor, congested nets.) * * This program requires a kernel mod that does not appear in any * system available from Berkeley: A raw ip socket using proto * IPPROTO_RAW must interpret the data sent as an ip datagram (as * opposed to data to be wrapped in a ip datagram). See the README * file that came with the source to this program for a description * of the mods I made to /sys/netinet/raw_ip.c. Your mileage may * vary. But, again, ANY 4.x (x < 4) BSD KERNEL WILL HAVE TO BE * MODIFIED TO RUN THIS PROGRAM. * * The udp port usage may appear bizarre (well, ok, it is bizarre). * The problem is that an icmp message only contains 8 bytes of * data from the original datagram. 8 bytes is the size of a udp * header so, if we want to associate replies with the original * datagram, the necessary information must be encoded into the * udp header (the ip id could be used but there's no way to * interlock with the kernel's assignment of ip id's and, anyway, * it would have taken a lot more kernel hacking to allow this * code to set the ip id). So, to allow two or more users to * use traceroute simultaneously, we use this task's pid as the * source port (the high bit is set to move the port number out * of the "likely" range). To keep track of which probe is being * replied to (so times and/or hop counts don't get confused by a * reply that was delayed in transit), we increment the destination * port number before each probe. * * Don't use this as a coding example. I was trying to find a * routing problem and this code sort-of popped out after 48 hours * without sleep. I was amazed it ever compiled, much less ran. * * I stole the idea for this program from Steve Deering. Since * the first release, I've learned that had I attended the right * IETF working group meetings, I also could have stolen it from Guy * Almes or Matt Mathis. I don't know (or care) who came up with * the idea first. I envy the originators' perspicacity and I'm * glad they didn't keep the idea a secret. * * Tim Seaver, Ken Adelman and C. Philip Wood provided bug fixes and/or * enhancements to the original distribution. * * I've hacked up a round-trip-route version of this that works by * sending a loose-source-routed udp datagram through the destination * back to yourself. Unfortunately, SO many gateways botch source * routing, the thing is almost worthless. Maybe one day... * * -- Van Jacobson (van@helios.ee.lbl.gov) * Tue Dec 20 03:50:13 PST 1988 */ #include #include #include #include #include #include /* not always automatically included */ #include #include #include #include #if defined(_AIX) #include /* needed for fd_set */ #endif #include #include #include #if 0 #include /* only needed for MAX_IPOPTLEN */ #endif #include #include #include #include #undef NOERROR /* in on solaris 2.x */ #include #include #if defined(linux) #include "linux.h" /* special compatibility definitions */ #endif #include "port.h" /* various portability definitions */ #include "conf.h" /* various configuration definitions */ #include "exit.h" /* exit codes come from */ #include "icmp.h" /* icmp types belong in */ #ifndef MAXDNAME #define MAXDNAME 256 /* maximum length of domain name */ #endif #ifdef lint #define EXTERN #else #define EXTERN extern #endif EXTERN int errno; EXTERN res_state_t _res; /* defined in res_init.c */ extern char *version; /* program version number */ char **optargv = NULL; /* argument list including default options */ int optargc = 0; /* number of arguments in new argument list */ char *separator = " "; /* output field separator -- do not change */ /* * Probe packet structure. */ #define IPHDRSZ 20 /* actually sizeof(struct ip) */ #define UDPHDRSZ 8 /* actually sizeof(struct udphdr) */ struct ip ippkt; /* temporary storage */ struct udphdr udppkt; /* temporary storage */ struct probe /* format of a (udp) probe packet */ { struct ip ip; struct udphdr udp; struct timeval tv; /* time packet left */ u_char seq; /* sequence number of this packet */ u_char ttl; /* ttl packet left with */ }; #ifdef IP_MAXPACKET #define MAXPACKET IP_MAXPACKET /* max ip packet size */ #else #define MAXPACKET 65535 #endif #ifndef MAX_IPOPTLEN #define MAX_IPOPTLEN 40 /* max ip options buffer size */ #endif #define IPOPT_HDRLEN 3 /* actually IPOPT_MINOFF - 1 */ #define TIMLEN sizeof(struct timeval) #define PROBELEN (IPHDRSZ + UDPHDRSZ + TIMLEN + 2) #define MAXDATA (MAXPACKET - MAX_IPOPTLEN) #define MAXFILL (MAXDATA - PROBELEN) u_char opacket[MAXPACKET]; /* last output (udp) packet */ u_char ipacket[MAXPACKET]; /* last inbound (icmp) packet */ u_char *optbuf; /* ip options buffer */ int optlen = 0; /* size of ip options buffer */ /* * Socket assignments. */ int sendsock; /* send (udp) socket file descriptor */ int recvsock; /* receive (icmp) socket file descriptor */ int sockopts = 0; /* socket options */ #define SOCKOPT_DEBUG 0x0001 #define SOCKOPT_DONTROUTE 0x0010 struct sockaddr_in myaddr; /* our own source address */ struct sockaddr_in toaddr; /* address to try to reach */ struct sockaddr_in fromaddr; /* address we got response from */ struct sockaddr_in *me = (struct sockaddr_in *)&myaddr; struct sockaddr_in *to = (struct sockaddr_in *)&toaddr; struct sockaddr_in *from = (struct sockaddr_in *)&fromaddr; struct sockaddr *myaddr_sa = (struct sockaddr *)&myaddr; struct sockaddr *toaddr_sa = (struct sockaddr *)&toaddr; struct sockaddr *fromaddr_sa = (struct sockaddr *)&fromaddr; /* * IP header parameters. */ u_short ident; /* packet ident */ u_short port = 32768+666; /* -p starting udp destination port */ int tos = 0; /* -t type of service */ int datalen = PROBELEN; /* size of raw ip packet minus options */ int dontfrag = 0; /* -f prevent fragmentation if set to IP_DF */ int needfrag = 0; /* new mtu size if more fragmentation needed */ char *source = NULL; /* -s specific source of multi-homed sender */ /* * Special AS-number lookup parameters. */ char *ashost = NULL; /* -h explicit whois server for AS lookup */ bool keepopen = FALSE; /* -k keep connection to whois server open */ bool lookupas = FALSE; /* -A perform AS-number lookup */ bool lookupnet = FALSE; /* -N perform network name lookup */ /* * Internal control variables. */ int min_ttl = 1; /* -i initial ttl */ int max_ttl = DEF_MAXHOPS; /* -m max number of hops */ int nprobes = DEF_NPROBES; /* -q number of probe packets to send */ int maxquit = DEF_MAXQUIT; /* -Q quit after this consecutive timeouts */ int waitsecs = DEF_TIMEOUT; /* -w time to wait for response (seconds) */ int stopsecs = 0; /* -c time to wait between probes (seconds) */ bool verbose = FALSE; /* -v print additional information */ bool numeric = FALSE; /* -n print addresses numerically */ bool ttlflag = FALSE; /* -l print ttl values */ bool alladdr = FALSE; /* -a trace all addrs of destination host */ bool summary = FALSE; /* -S show per-hop summary statistics */ bool suppress = FALSE; /* -S suppress per-probe rtt and ttl report */ /* * Miscellaneous buffers. */ char hostnamebuf[MAXDNAME+1]; char *hostname; /* remote host to query about */ #define MAXADDRS 35 /* max address count from gethostnamadr.c */ ipaddr_t hostaddr[MAXADDRS]; /* multiple destination addresses */ int naddrs = 0; /* count of destination addresses */ #define MAXIPOPT 9 /* MAX_IPOPTLEN-IPOPT_MINOFF / INADDRSZ */ #define MAXLSRR (MAXIPOPT-1) /* leave room for destination address */ ipaddr_t lsrraddr[MAXLSRR]; /* loose source route addresses */ int nlsrr = 0; /* count of loose source route addresses */ #define MAXGATE (MAXLSRR*MAXADDRS) ipaddr_t gateaddr[MAXGATE]; /* all known gateway addresses */ int ngate = 0; /* count of all known gateway addresses */ /* * Miscellaneous definitions. */ #define NOT_DOTTED_QUAD ((ipaddr_t)-1) #define MAXINT8 255 #define MAXINT16 65535 #define MAXSECS 2146 /* 2147,483,647 usec */ /* * Internal flags to indicate ICMP packet type. * We cannot use the original type and code any more because of * overlapping range due to the increased number of subcodes. */ #define BAD_ICMP_PACKET -1 /* invalid ICMP reply packet */ #define TIMXCEED_INTRANS 0 /* got to intermediate host */ #define DEST_UNREACHABLE 1 /* generic unreachable indicator */ #define UNREACH_NET (DEST_UNREACHABLE + ICMP_UNREACH_NET) #define UNREACH_HOST (DEST_UNREACHABLE + ICMP_UNREACH_HOST) #define UNREACH_PROTOCOL (DEST_UNREACHABLE + ICMP_UNREACH_PROTOCOL) #define UNREACH_PORT (DEST_UNREACHABLE + ICMP_UNREACH_PORT) #define UNREACH_SRCFAIL (DEST_UNREACHABLE + ICMP_UNREACH_SRCFAIL) #define UNREACH_NEEDFRAG (DEST_UNREACHABLE + ICMP_UNREACH_NEEDFRAG) #define UNREACH_NET_UNKNOWN (DEST_UNREACHABLE + ICMP_UNREACH_NET_UNKNOWN) #define UNREACH_HOST_UNKNOWN (DEST_UNREACHABLE + ICMP_UNREACH_HOST_UNKNOWN) #define UNREACH_ISOLATED (DEST_UNREACHABLE + ICMP_UNREACH_ISOLATED) #define UNREACH_NET_PROHIB (DEST_UNREACHABLE + ICMP_UNREACH_NET_PROHIB) #define UNREACH_HOST_PROHIB (DEST_UNREACHABLE + ICMP_UNREACH_HOST_PROHIB) #define UNREACH_TOSNET (DEST_UNREACHABLE + ICMP_UNREACH_TOSNET) #define UNREACH_TOSHOST (DEST_UNREACHABLE + ICMP_UNREACH_TOSHOST) #define UNREACH_ADM_PROHIB (DEST_UNREACHABLE + ICMP_UNREACH_ADM_PROHIB) #define UNREACH_PREC_VIOL (DEST_UNREACHABLE + ICMP_UNREACH_PREC_VIOL) #define UNREACH_PREC_CUT (DEST_UNREACHABLE + ICMP_UNREACH_PREC_CUT) /* * Useful macro definitions. */ #include "defs.h" /* declaration of functions */ #define strlength(s) (int)strlen(s) #define is_space(c) (isascii(c) && isspace(c)) #define newlist(a,n,t) (t *)xalloc((ptr_t *)(a), (siz_t)((n)*sizeof(t))) #define newstruct(t) (t *)xalloc((ptr_t *)NULL, (siz_t)(sizeof(t))) #define newstring(s) (char *)xalloc((ptr_t *)NULL, (siz_t)(strlen(s)+1)) #define newstr(s) strcpy(newstring(s), s) #define xfree(a) (void) free((ptr_t *)(a)) #ifdef DEBUG #define assert(condition)\ {\ if (!(condition))\ {\ (void) fprintf(stderr, "assertion botch: ");\ (void) fprintf(stderr, "%s(%d): ", __FILE__, __LINE__);\ (void) fprintf(stderr, "%s\n", "condition");\ exit(EX_SOFTWARE);\ }\ } #else #define assert(condition) #endif char Usage[] = "\ Usage: %s [-g gateway] [-m maxhops] [-q nqueries] host [packetsize]\n\ Flags: [-a] [-f] [-i initial_ttl] [-l] [-n] [-Q maxquit] [-r] [-S] [-v]\n\ Other: [-c stoptime] [-p port] [-s source_addr] [-t tos] [-w waittime]\n\ ASnum: [-A] [-h server] [-k] [-N]\ "; /* ** MAIN -- Start of program traceroute ** ----------------------------------- ** ** Exits: ** Various possibilities from among which ** EX_SUCCESS The destination host has been reached ** EX_UNAVAILABLE Could not reach the destination host ** EX_USAGE Improper parameter/option specified */ int main(argc, argv) int argc; char *argv[]; { bool got_somewhere = FALSE; /* set if some address was reached */ char *program; /* name of this program */ ipaddr_t addr; struct in_addr inaddr; struct hostent *hp; register int i; register char *option; assert(sizeof(u_int) >= 4); /* probably paranoid */ #ifdef obsolete assert(sizeof(u_short) == 2); /* perhaps less paranoid */ assert(sizeof(ipaddr_t) == 4); /* but this is critical */ #endif /*obsolete*/ /* * Synchronize stdout and stderr in case output is redirected. */ linebufmode(stdout); /* * Initialize resolver. Shorter timeout values are set later. */ (void) res_init(); /* * Check command line options. * Interpolate default options and parameters. */ if (argc < 1 || argv[0] == NULL) exit(EX_USAGE); option = getenv("TRACEROUTE_DEFAULTS"); if (option != NULL) { set_defaults(option, argc, argv); argc = optargc; argv = optargv; } program = rindex(argv[0], '/'); if (program++ == NULL) program = argv[0]; while (argc > 1 && argv[1] != NULL && argv[1][0] == '-') { for (option = &argv[1][1]; *option != '\0'; option++) { switch (*option) { case 'A': /* lookup AS-number */ lookupas = TRUE; break; case 'a': /* probe all of multiple addresses */ alladdr = TRUE; break; case 'c': /* time to delay between probes to cisco */ stopsecs = getval(argv[2], "stoptime value", 0, MAXSECS); argc--, argv++; break; case 'd': /* socket level debugging */ sockopts |= SOCKOPT_DEBUG; break; case 'f': /* prevent fragmentation */ dontfrag = IP_DF; break; case 'g': /* loose source route gateway address */ if (argv[2] == NULL || argv[2][0] == '-') fatal("Missing gateway name"); if (nlsrr >= MAXLSRR) fatal("Maximum %s gateways", itoa(MAXLSRR)); lsrraddr[nlsrr++] = getgate(argv[2]); argc--, argv++; break; case 'h': /* explicit whois server host */ if (argv[2] == NULL || argv[2][0] == '-') fatal("Missing whois server"); ashost = argv[2]; argc--, argv++; break; case 'i': /* initial ttl value */ min_ttl = getval(argv[2], "initial ttl", 1, max_ttl); argc--, argv++; break; case 'k': /* keep whois connection open */ keepopen = TRUE; break; case 'l': /* list ttl values */ ttlflag = TRUE; break; case 'm': /* maximum hop (ttl) count */ max_ttl = getval(argv[2], "maxhops value", min_ttl, MAXINT8); argc--, argv++; break; case 'N': /* lookup network name */ lookupnet = TRUE; break; case 'n': /* print numeric addresses */ numeric = TRUE; break; case 'p': /* specific udp destination port number */ port = getval(argv[2], "port number", 1, MAXINT16); argc--, argv++; break; case 'Q': /* maximum number of consecutive timeouts */ maxquit = getval(argv[2], "maxquit value", 1, MAXINT16); argc--, argv++; break; case 'q': /* number of probe packets per hop */ nprobes = getval(argv[2], "nqueries value", 1, MAXINT16); argc--, argv++; break; case 'r': /* bypass normal routing */ sockopts |= SOCKOPT_DONTROUTE; break; case 'S': /* show per-hop summary statistics */ summary = TRUE; suppress = !suppress; break; case 's': /* explicit own source address */ if (argv[2] == NULL || argv[2][0] == '-') fatal("Missing source address"); source = argv[2]; argc--, argv++; break; case 't': /* type-of-service */ tos = getval(argv[2], "tos value", 0, MAXINT8); argc--, argv++; break; case 'v': /* verbose printout */ verbose = TRUE; break; case 'w': /* timeout to wait for replies */ waitsecs = getval(argv[2], "waittime value", 1, MAXSECS); argc--, argv++; break; case 'V': printf("%s\n", version); exit(EX_SUCCESS); default: fatal(Usage, program); } } argc--, argv++; } /* * Fetch (mandatory) remote host address(es) to probe. */ if (argc < 2 || argv[1] == NULL) fatal(Usage, program); hostname = argv[1]; addr = inet_addr(hostname); inaddr.s_addr = addr; if (addr == NOT_DOTTED_QUAD) { hp = gethostbyname(hostname); if (hp == NULL) { error("Unknown host %s", hostname); exit(EX_NOHOST); } hostname = strncpy(hostnamebuf, hp->h_name, MAXDNAME); hostname[MAXDNAME] = '\0'; for (i = 0; i < MAXADDRS && hp->h_addr_list[i]; i++) { bcopy(hp->h_addr_list[i], (char *)&inaddr, INADDRSZ); hostaddr[i] = inaddr.s_addr; } naddrs = i; /* prep the address cache */ for (i = 0; i < naddrs; i++) { inaddr.s_addr = hostaddr[i]; (void) maphostbyaddr(inaddr); } } else { hostname = strcpy(hostnamebuf, inetname(inaddr)); hostaddr[0] = addr; naddrs = 1; } /* * Check for optional ip size (minus options) of output packet. */ if (argc > 2 && argv[2] != NULL) datalen = getval(argv[2], "packet size", PROBELEN, MAXDATA); /* rest is undefined */ if (argc > 3) fatal(Usage, program); /* * Miscellaneous initialization. */ /* becomes the udp source port */ ident = (getpid() & 0xFFFF) | 0x8000; /* check the udp destination port */ if (((int)port + ((max_ttl - min_ttl + 1)*nprobes)) > MAXINT16) fatal("Destination port value overflow"); /* set shorter nameserver timeout */ _res.retry = DEF_RETRIES; /* number of datagram retries */ _res.retrans = DEF_RETRANS; /* timeout between retries */ /* allocate IP output socket and ICMP input socket */ check_proto(); get_socket(); /* * Define explicit source address in output packet, if specified. */ bzero((char *)&myaddr, sizeof(myaddr)); me->sin_family = AF_INET; me->sin_addr.s_addr = INADDR_ANY; me->sin_port = 0; if (source) { addr = inet_addr(source); if (addr == NOT_DOTTED_QUAD) fatal("Illegal source address %s", source); me->sin_addr.s_addr = addr; #ifndef IP_HDRINCL if (bind(sendsock, myaddr_sa, sizeof(myaddr)) < 0) { perror("bind"); exit(EX_OSERR); } #endif } /* * Start of main loop. */ /* don't need special privileges any more */ (void) setuid(getuid()); /* probe all addresses successively */ for (i = 0; i < naddrs; i++) { if (ping(hostaddr[i])) got_somewhere = TRUE; if (!alladdr) break; } /* indicate success or failure */ return(got_somewhere ? EX_SUCCESS : EX_UNAVAILABLE); /*NOTREACHED*/ } /* ** SET_DEFAULTS -- Interpolate default options and parameters in argv ** ------------------------------------------------------------------ ** ** The TRACEROUTE_DEFAULTS env variable gives customized options. ** ** Returns: ** None. ** ** Outputs: ** Creates ``optargv'' vector with ``optargc'' arguments. */ void set_defaults(option, argc, argv) char *option; /* option string */ int argc; /* original command line arg count */ char *argv[]; /* original command line arguments */ { register char *p, *q; register int i; /* * Allocate new argument vector. */ optargv = newlist(NULL, 2, char *); optargv[0] = argv[0]; optargc = 1; /* * Construct argument list from option string. */ for (q = newstr(option), p = q; *p != '\0'; p = q) { while (is_space(*p)) p++; if (*p == '\0') break; for (q = p; *q != '\0' && !is_space(*q); q++) continue; if (*q != '\0') *q++ = '\0'; optargv = newlist(optargv, optargc+2, char *); optargv[optargc] = p; optargc++; } /* * Append command line arguments. */ for (i = 1; i < argc && argv[i] != NULL; i++) { optargv = newlist(optargv, optargc+2, char *); optargv[optargc] = argv[i]; optargc++; } /* and terminate */ optargv[optargc] = NULL; } /* ** GETVAL -- Decode parameter value and perform range check ** -------------------------------------------------------- ** ** Returns: ** Parameter value if successfully decoded. ** Aborts in case of syntax or range errors. */ int getval(optstring, optname, minvalue, maxvalue) char *optstring; /* parameter from command line */ char *optname; /* descriptive name of option */ int minvalue; /* minimum value for option */ int maxvalue; /* maximum value for option */ { register int optvalue; if (optstring == NULL || optstring[0] == '-') fatal("Missing %s", optname); optvalue = atoi(optstring); if (optvalue == 0 && optstring[0] != '0') fatal("Invalid %s %s", optname, optstring); if (optvalue < minvalue) fatal("Minimum %s %s", optname, itoa(minvalue)); if (maxvalue > 0 && optvalue > maxvalue) fatal("Maximum %s %s", optname, itoa(maxvalue)); return(optvalue); } /* ** FATAL -- Abort program when illegal option encountered ** ------------------------------------------------------ ** ** Returns: ** Aborts after issuing error message. */ void /*VARARGS1*/ fatal(fmt, a, b, c, d) char *fmt; /* format of message */ char *a, *b, *c, *d; /* optional arguments */ { (void) fprintf(stderr, fmt, a, b, c, d); (void) fprintf(stderr, "\n"); exit(EX_USAGE); } /* ** ERROR -- Issue error message to error output ** -------------------------------------------- ** ** Returns: ** None. */ void /*VARARGS1*/ error(fmt, a, b, c, d) char *fmt; /* format of message */ char *a, *b, *c, *d; /* optional arguments */ { (void) fprintf(stderr, fmt, a, b, c, d); (void) fprintf(stderr, "\n"); } /* ** CHECK_PROTO -- Check protocol numbers ** ------------------------------------- ** ** traceroute uses protocol numbers as defined in . ** Verify whether they correspond to the values in /etc/protocols. ** This is probably rather paranoid. */ void check_proto() { struct protoent *proto; proto = getprotobyname("ip"); if (proto == NULL) { (void) fprintf(stderr, "ip: unknown protocol\n"); exit(EX_OSFILE); } if (proto->p_proto != IPPROTO_IP) { (void) fprintf(stderr, "ip protocol %d should be %d\n", proto->p_proto, IPPROTO_IP); exit(EX_CONFIG); } proto = getprotobyname("icmp"); if (proto == NULL) { (void) fprintf(stderr, "icmp: unknown protocol\n"); exit(EX_OSFILE); } if (proto->p_proto != IPPROTO_ICMP) { (void) fprintf(stderr, "icmp protocol %d should be %d\n", proto->p_proto, IPPROTO_ICMP); exit(EX_CONFIG); } proto = getprotobyname("tcp"); if (proto == NULL) { (void) fprintf(stderr, "tcp: unknown protocol\n"); exit(EX_OSFILE); } if (proto->p_proto != IPPROTO_TCP) { (void) fprintf(stderr, "tcp protocol %d should be %d\n", proto->p_proto, IPPROTO_TCP); exit(EX_CONFIG); } proto = getprotobyname("udp"); if (proto == NULL) { (void) fprintf(stderr, "udp: unknown protocol\n"); exit(EX_OSFILE); } if (proto->p_proto != IPPROTO_UDP) { (void) fprintf(stderr, "udp protocol %d should be %d\n", proto->p_proto, IPPROTO_UDP); exit(EX_CONFIG); } } /* ** GET_SOCKET -- Allocate and configure output and input socket ** ------------------------------------------------------------ ** ** A raw ip socket is allocated for sending the probe packets. ** A raw udp socket is allocated instead on those platforms ** which do not support raw ip socket manipulation. ** A raw icmp socket is allocated for receiving icmp messages. ** These sockets can be allocated only by root. ** Extra socket options are set as requested on the command line. ** ** Inputs: ** Global ``datalen'' contains the ip packet size. ** This is used to set the size of the socket buffer. ** ** Outputs: ** Global ``recvsock'' is the input socket descriptor. ** Global ``sendsock'' is the output socket descriptor. */ void get_socket() { int on = 1; /* * Allocate input socket to receive replies. */ recvsock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); if (recvsock < 0) { perror("icmp socket"); exit(EX_OSERR); } if (sockopts & SOCKOPT_DEBUG) (void) setsockopt(recvsock, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof(on)); if (sockopts & SOCKOPT_DONTROUTE) (void) setsockopt(recvsock, SOL_SOCKET, SO_DONTROUTE, (char *)&on, sizeof(on)); /* * Allocate output socket to send probe packets. */ #ifdef NO_RAW_IP sendsock = socket(AF_INET, SOCK_RAW, IPPROTO_UDP); if (sendsock < 0) { perror("udp socket"); exit(EX_OSERR); } #else sendsock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); if (sendsock < 0) { perror("raw socket"); exit(EX_OSERR); } #endif /*NO_RAW_IP*/ #ifdef SO_SNDBUF if (setsockopt(sendsock, SOL_SOCKET, SO_SNDBUF, (char *)&datalen, sizeof(datalen)) < 0) { perror("setsockopt: SO_SNDBUF"); exit(EX_OSERR); } #endif #ifdef IP_HDRINCL if (setsockopt(sendsock, IPPROTO_IP, IP_HDRINCL, (char *)&on, sizeof(on)) < 0) { perror("setsockopt: IP_HDRINCL"); exit(EX_OSERR); } #endif if (sockopts & SOCKOPT_DEBUG) (void) setsockopt(sendsock, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof(on)); if (sockopts & SOCKOPT_DONTROUTE) (void) setsockopt(sendsock, SOL_SOCKET, SO_DONTROUTE, (char *)&on, sizeof(on)); } /* ** SET_OPTIONS -- Initialize IP options ** ------------------------------------ ** ** Special IP options are set up as requested on the command line. ** This will extend the IP header with the IP options buffer. ** Not all platforms may support this, even if IP_OPTIONS exists. ** ** Inputs: ** ``toaddr'' contains final destination address. ** ``datalen'' contains the basic IP packet size. ** ** Outputs: ** Stores address of IP options buffer in ``optbuf''. ** Stores size of IP options buffer in ``optlen''. ** ** Side effects: ** Extends the size of the socket send buffer. */ #ifdef IP_OPTIONS void set_options(addrlist, naddr) ipaddr_t addrlist[]; /* list of gateway addresses */ int naddr; /* count of gateway addresses */ { static u_char ipoptbuf[MAX_IPOPTLEN]; u_char *ipopt = ipoptbuf; register int i; bzero((char *)ipoptbuf, sizeof(ipoptbuf)); /* make sure the remainder is 32-bit aligned */ ipopt[IPOPT_OPTVAL] = IPOPT_NOP; ipopt++; /* * Store the loose source route option addresses. */ ipopt[IPOPT_OPTVAL] = IPOPT_LSRR; ipopt[IPOPT_OLEN] = IPOPT_HDRLEN + (naddr + 1)*INADDRSZ; ipopt[IPOPT_OFFSET] = IPOPT_MINOFF; ipopt += IPOPT_HDRLEN; /* store intermediate gateway addresses */ for (i = 0; i < naddr; i++) { struct in_addr inaddr; inaddr.s_addr = addrlist[i]; bcopy((char *)&inaddr, (char *)ipopt, INADDRSZ); ipopt += INADDRSZ; } /* and the final destination */ bcopy((char *)&to->sin_addr, (char *)ipopt, INADDRSZ); ipopt += INADDRSZ; /* export start and size of options */ optbuf = ipoptbuf; optlen = ipopt - ipoptbuf; /* to keep certain platforms happy */ if (optlen < sizeof(ipoptbuf)) optlen = sizeof(ipoptbuf); /* * Announce the use of the ip options. They will be automatically * inserted when the packet is sent, but not on 4.4BSD platforms. */ #ifndef BSD44 if (setsockopt(sendsock, IPPROTO_IP, IP_OPTIONS, (char *)optbuf, optlen) < 0) { perror("setsockopt: IP_OPTIONS"); exit(EX_OSERR); } #endif /*BSD44*/ /* * Extend the size of the socket buffer to the full packet size. */ #ifdef SO_SNDBUF { int packetlen = datalen + optlen; if (setsockopt(sendsock, SOL_SOCKET, SO_SNDBUF, (char *)&packetlen, sizeof(packetlen)) < 0) { perror("setsockopt: SO_SNDBUF"); exit(EX_OSERR); } } #endif } #endif /*IP_OPTIONS*/ /* ** PING -- Probe given address of destination host ** ----------------------------------------------- ** ** Wrapper for trace_route() to hide interrupt handling. ** ** Returns: ** TRUE if the destination was reached. ** FALSE otherwise. */ bool interrupted = FALSE; /* set upon interrupt if synchronous */ bool synchronous = FALSE; /* perform synchronous interrupt handling */ static jmp_buf interrupt_buf; static sigtype_t /*ARGSUSED*/ interrupt(sig) int sig; { if (!synchronous) { longjmp(interrupt_buf, 1); /*NOTREACHED*/ } interrupted = TRUE; (void) signal(SIGINT, interrupt); sig_return(0); } bool ping(addr) ipaddr_t addr; /* address of destination */ { bool got_there; /* set if destination reached */ if (setjmp(interrupt_buf) != 0) { printf("\n"); (void) fprintf(stderr, "(interrupt)\n"); (void) signal(SIGINT, SIG_DFL); return(FALSE); } interrupted = FALSE; (void) signal(SIGINT, interrupt); got_there = trace_route(addr); if (interrupted) longjmp(interrupt_buf, 1); (void) signal(SIGINT, SIG_DFL); return(got_there); } /* ** TRACE_ROUTE -- Probe given address of destination host ** ------------------------------------------------------ ** ** Returns: ** TRUE if the destination was reached. ** FALSE otherwise. */ bool trace_route(addr) ipaddr_t addr; /* address of destination */ { int ttl; /* current hop count */ int probe; /* current probe number */ int seq = 0; /* packet sequence number */ int packetlen; /* total ip packet size */ bool got_there = FALSE; /* set if destination reached */ /* setup destination address */ bzero((char *)&toaddr, sizeof(toaddr)); to->sin_family = AF_INET; to->sin_addr.s_addr = addr; to->sin_port = 0; /* set ip options and compute options buffer size */ if (nlsrr > 0) { #ifdef IP_OPTIONS #ifdef BSD44 /* perform first hop routing */ addr = lsrraddr[0]; set_options(&lsrraddr[1], nlsrr - 1); #else /* store all gateway addresses */ set_options(lsrraddr, nlsrr); #endif /*BSD44*/ #else /*IP_OPTIONS*/ error("gateway source routing not supported"); nlsrr = 0; #endif /*IP_OPTIONS*/ } /* total ip packet size includes options */ packetlen = datalen + optlen; printf("traceroute to %s (%s)", hostname, inet_ntoa(to->sin_addr)); if (source) printf(" from %s", source); printf(": %d-%d hops, %d byte packets\n", min_ttl, max_ttl, packetlen); /* actual first hop may have been reset */ to->sin_addr.s_addr = addr; /* * All set. Start emitting probe packets. */ for (ttl = min_ttl; ttl <= max_ttl && !interrupted; ttl++) { ipaddr_t lastaddr = 0; /* address we got icmp response from */ int unreachable = 0; /* count of unreachable responses */ int timeouts = 0; /* count of receive timeouts */ int star = 0; /* count of consecutive timeouts */ int mtu = 0; /* set if forced to use new mtu size */ clear_stats(); printf("%2d", ttl); (void) fflush(stdout); for (probe = 0; probe < nprobes && star < maxquit; probe++) { register int cc; int icmpcode; time_t rtt; struct ip *ip; struct timeval sendtime, recvtime; struct timeval waittime, nexttime; /* initialize maximum timeout */ waittime.tv_sec = waitsecs; waittime.tv_usec = 0; /* * Send probe packet. Decrease packet size if it is too long. */ (void) gettimeofday(&sendtime, (struct timezone *)NULL); cc = send_probe(sendsock, ++seq, ttl, packetlen-optlen); if (cc == 0) mtu = select_mtu(packetlen); retry: /* * Wait for a reply packet or timeout, if successfully sent. * Delay printing the timeout marker until we have an address. */ (void) gettimeofday(&nexttime, (struct timezone *)NULL); if (cc > 0) cc = recv_reply(recvsock, &waittime); if (cc <= 0) { star++; timeouts++; continue; /* next probe */ } /* * Decrement waittime in case we are catching packets that * do not belong to us, e.g. because somebody is ping-ing. */ (void) gettimeofday(&recvtime, (struct timezone *)NULL); icmpcode = check_reply(ipacket, cc, seq); if (icmpcode == BAD_ICMP_PACKET) { (void) tvsub(&recvtime, &nexttime); (void) tvsub(&waittime, &recvtime); goto retry; /* not our reply */ } /* * Valid reply packet received. Compute rtt value. * Print remote address (if different from previous case). */ if (from->sin_addr.s_addr != lastaddr) { print_from(ipacket, cc, from->sin_addr); lastaddr = from->sin_addr.s_addr; } for (; star > 0; star--) printf("%s *", separator); rtt = tvsub(&recvtime, &sendtime); record_stats(rtt); if (!suppress) printf("%s %s ms", separator, tvprint(rtt)); /* * Check ttl for unexpected values. Print if necessary. */ ip = (struct ip *)ipacket; if (regular_ttl((int)ip->ip_ttl + (ttl - 1))) { if (!suppress && ttlflag) printf(" (ttl=%d)", (int)ip->ip_ttl); } else { if (!suppress && (ttlflag || (nlsrr == 0))) printf(" (ttl=%d!)", (int)ip->ip_ttl); } /* * Certain routers will erroneously report ICMP_UNREACH_PORT * if they are specified as loose source route gateway hosts. * We cheat and assume they are intermediate hosts. */ if (nlsrr > 0 && icmpcode == UNREACH_PORT) { if (gatewayaddr(from->sin_addr)) { printf(" !G"); icmpcode = TIMXCEED_INTRANS; } } /* * Certain target routers fail to report ICMP_UNREACH_PORT. */ if (from->sin_addr.s_addr == addr && nlsrr == 0) { if (icmpcode == TIMXCEED_INTRANS) { printf(" !A"); icmpcode = UNREACH_PORT; } } /* * Check icmp reply code for special cases. */ switch (icmpcode) { case TIMXCEED_INTRANS: /* intermediate host -- nothing to do */ break; case UNREACH_PORT: /* destination host -- normal case */ if (ip->ip_ttl <= 1) printf(" !"); got_there = TRUE; break; case UNREACH_PROTOCOL: printf(" !P"); got_there = TRUE; break; case UNREACH_SRCFAIL: printf(" !S"); unreachable++; break; case UNREACH_HOST: case UNREACH_HOST_UNKNOWN: case UNREACH_HOST_PROHIB: printf(" !H"); unreachable++; break; case UNREACH_NET: case UNREACH_NET_UNKNOWN: case UNREACH_NET_PROHIB: printf(" !N"); unreachable++; break; case UNREACH_ISOLATED: case UNREACH_ADM_PROHIB: case UNREACH_PREC_VIOL: case UNREACH_PREC_CUT: printf(" !U"); unreachable++; break; case UNREACH_TOSNET: case UNREACH_TOSHOST: printf(" !T"); unreachable++; break; case UNREACH_NEEDFRAG: mtu = needfrag; if (mtu > 0) printf(" !F=%d", mtu); else printf(" !F"); if (mtu == 0) mtu = select_mtu(packetlen); unreachable++; break; default: printf(" ?"); unreachable++; break; } (void) fflush(stdout); /* optionally pause between probes */ if (stopsecs != 0) (void) sleep((unsigned)stopsecs); } /* for probe < nprobes */ for (; star > 0; star--) printf("%s *", separator); if (summary) print_stats(probe); printf("\n"); /* * Check for special conditions after this series of probes. */ /* terminate if destination reached */ if (got_there) break; /* retry same ttl with shorter mtu size */ if ((mtu >= (PROBELEN + optlen)) && (mtu < packetlen)) { printf("(switching to new packet size %d)\n", mtu); packetlen = mtu; ttl--; continue; } /* abort if all probes yielded unreachable */ if (unreachable && (unreachable + timeouts >= probe)) break; } /* for ttl <= max_ttl */ /* indicate success or failure */ return(got_there); } /* ** SEND_PROBE -- Send out probe packet ** ----------------------------------- ** ** Inputs: ** Global ``ident'' contains our identification. ** Global ``port'' contains the base port offset. ** Global ``tos'' contains the type of service. ** Global ``dontfrag'' contains the don't fragment flag. ** Global ``toaddr'' contains destination address. ** Global ``myaddr'' contains own source address. ** Global ``optlen'' contains the size of the IP options. ** ** Returns: ** Number of bytes sent. ** 0 if packet size is too large. ** -1 in case of other errors. */ int send_probe(sock, seq, ttl, iplen) int sock; /* output socket descriptor */ int seq; /* sequence no. determines port */ int ttl; /* current ttl value */ int iplen; /* size of ip packet minus options */ { u_char *pkt = opacket; struct ip *ip = &ippkt; /* temporary ip header */ struct udphdr *udp = &udppkt; /* temporary udp header */ struct timeval tv; int iphdrlen = IPHDRSZ; /* size of ip header minus options */ int udplen = iplen - iphdrlen; /* size of udp packet */ register int n; /* * On traditional platforms, the kernel inserts the IP options. * On 4.4BSD-based platforms, we must construct them ourselves. */ #ifdef BSD44 iphdrlen += optlen; /* size of ip header plus options */ iplen += optlen; /* size of ip packet plus options */ #endif /*BSD44*/ /* * Fill in ip header. NOTE: certain fields are in machine byte order. * On traditional platforms, the kernel stores ip_v and ip_hl itself, * and properly computes ip_hl depending on the IP options. * The kernel always generates ip_id and ip_sum. */ ip->ip_v = (u_char) IPVERSION; /* version */ ip->ip_hl = (u_char) (iphdrlen >> 2); /* header length */ ip->ip_tos = (u_char) tos; /* type of service */ ip->ip_len = (short) iplen; /* total size */ ip->ip_id = (u_short) 0; /* identification */ ip->ip_off = (short) dontfrag; /* fragment offset */ ip->ip_ttl = (u_char) ttl; /* time-to-live */ ip->ip_p = (u_char) IPPROTO_UDP; /* protocol */ ip->ip_sum = (u_short) 0; /* checksum */ ip->ip_src = me->sin_addr; /* source address */ ip->ip_dst = to->sin_addr; /* destination address */ /* * Some platforms require the entire header to be in network byte order. */ #ifdef RAW_IP_NET_ORDER ip->ip_len = htons((u_short)ip->ip_len); ip->ip_off = htons((u_short)ip->ip_off); #endif /*RAW_IP_NET_ORDER*/ /* * Fill in udp header. There is no udp checksum. */ udp->uh_sport = htons(ident); /* source port */ udp->uh_dport = htons(port+seq); /* destination port */ udp->uh_ulen = htons((u_short)udplen); /* udp size */ udp->uh_sum = 0; /* checksum */ /* * Construct the output packet. */ bcopy((char *)ip, (char *)pkt, IPHDRSZ); pkt += IPHDRSZ; #ifdef BSD44 bcopy((char *)optbuf, (char *)pkt, optlen); pkt += optlen; #endif /*BSD44*/ bcopy((char *)udp, (char *)pkt, UDPHDRSZ); pkt += UDPHDRSZ; /* * Fill in remainder of output packet (actually unused). */ (void) gettimeofday(&tv, (struct timezone *)NULL); bcopy((char *)&tv, (char *)pkt, TIMLEN); pkt += TIMLEN; pkt[0] = (u_char)seq; pkt[1] = (u_char)ttl; /* * If we are sending only the udp packet instead of the ip packet, * we request an explicit ttl value for the resulting ip packet. * On new platforms we can also request an explicit tos value. */ #ifdef NO_RAW_IP if (setsockopt(sock, IPPROTO_IP, IP_TTL, (char *)&ttl, sizeof(ttl)) < 0) { perror("setsockopt: IP_TTL"); exit(EX_OSERR); } #ifdef IP_TOS if ((tos > 0) && setsockopt(sock, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(tos)) < 0) { perror("setsockopt: IP_TOS"); exit(EX_OSERR); } #endif #endif /*NO_RAW_IP*/ /* * Send out the probe packet. Either full raw ip, or only udp. */ #ifdef NO_RAW_IP /* skip the ip header */ udp = (struct udphdr *)&opacket[iphdrlen]; n = sendto(sock, (char *)udp, udplen, 0, toaddr_sa, sizeof(toaddr)); if (n > 0) n += iphdrlen; #else /*NO_RAW_IP*/ /* send raw ip packet */ ip = (struct ip *)opacket; n = sendto(sock, (char *)ip, iplen, 0, toaddr_sa, sizeof(toaddr)); #endif /*NO_RAW_IP*/ /* * Check for errors. Report packet too large for fragmentation. */ if (n < 0 || n != iplen) { #ifdef EMSGSIZE /* message too long */ if (n < 0 && errno == EMSGSIZE) return(0); #endif /*EMSGSIZE*/ #if defined(apollo) /* message too long */ if ((n < 0 && errno == EIO) && dontfrag) return(0); #endif printf("\n"); if (n < 0) perror("sendto"); else error("sendto: truncated packet to %s", hostname); /* failure */ return(-1); } /* successfully sent */ return(n); } /* ** RECV_REPLY -- Wait for arrival of input packet ** ---------------------------------------------- ** ** Outputs: ** Input packet is stored in global ``ipacket''. ** Reply address is stored in global ``fromaddr''. ** ** Returns: ** Number of bytes received. ** -1 in case of error or timeout. */ int recv_reply(sock, wait) int sock; /* input socket descriptor */ struct timeval *wait; /* timeout value */ { struct timeval timer; fd_set fds; int fromlen, len; register int n; /* avoid clobbering original timer */ timer.tv_sec = wait->tv_sec; timer.tv_usec = wait->tv_usec; rewait: /* FD_ZERO(&fds); */ bzero((char *)&fds, sizeof(fds)); FD_SET(sock, &fds); /* wait for arrival of input packet, or timeout */ n = select(FD_SETSIZE, &fds, (fd_set *)0, (fd_set *)0, &timer); if (n <= 0) { if (n < 0 && errno == EINTR) goto rewait; if (n == 0) errno = ETIMEDOUT; return(-1); } reread: /* fake an error if nothing was actually read */ len = sizeof(ipacket); fromlen = sizeof(fromaddr); n = recvfrom(sock, (char *)ipacket, len, 0, fromaddr_sa, &fromlen); if (n < 0 && errno == EINTR) goto reread; if (n == 0) errno = ECONNRESET; return(n); } /* ** CHECK_REPLY -- Check validity of received input packet ** ------------------------------------------------------ ** ** Inputs: ** Global ``ident'' contains our identification. ** Global ``port'' contains the base port offset. ** ** Returns: ** TIMXCEED_INTRANS if ttl dropped to zero (intermediate host). ** DEST_UNREACHABLE + subcode of ICMP_UNREACH if unreachable ** (got to the final destination if ICMP_UNREACH_PORT). ** BAD_ICMP_PACKET if not one of the above. ** ** Side effects: ** Sets ``needfrag'' if further fragmentation is needed ** and the new mtu size to use has been told. */ int check_reply(buf, cc, seq) u_char *buf; /* address of input packet */ int cc; /* total size of input packet */ int seq; /* sequence no. determines port */ { struct ip *ip; struct icmp *icp; struct udphdr *udp; int iphdrlen; int type; /* type of icmp input packet */ int code; /* subcode of type */ /* * The input packet contains the ip header in front of the icmp packet. * Make sure the packet contains the icmp header after the ip header. */ ip = (struct ip *)buf; iphdrlen = ip->ip_hl << 2; if (ip->ip_p != IPPROTO_ICMP) { if (verbose) { printf("\n%d bytes from %s: (not icmp)\n", cc, pr_addr(from->sin_addr)); /* dump the ip packet */ print_ippkt(ip, cc); } return(BAD_ICMP_PACKET); } if (cc < iphdrlen + ICMP_MINLEN) { if (verbose) { printf("\n%d bytes from %s: (too short)\n", cc, pr_addr(from->sin_addr)); /* dump the ip packet */ print_ippkt(ip, cc); } return(BAD_ICMP_PACKET); } /* move to the icmp packet */ icp = (struct icmp *)(buf + iphdrlen); cc -= iphdrlen; /* pickup type and its subcode */ type = icp->icmp_type; code = icp->icmp_code; /* * Make sure this is really our response packet. * The original ip packet is stored in the icmp message after the * icmp header, with the original udp header after the ip header. */ ip = &icp->icmp_ip; /* original ip packet */ iphdrlen = ip->ip_hl << 2; udp = (struct udphdr *)((u_char *)ip + iphdrlen); if (cc < (ICMP_MINLEN + iphdrlen + 2*INT16SZ)) goto bad; if (ip->ip_p != IPPROTO_UDP) goto bad; if (udp->uh_sport != htons(ident)) goto bad; if (udp->uh_dport != htons(port+seq)) goto bad; /* * Check for fragmentation problems. * This should happen only if the dontfrag option is set, and * we hit a machine which cannot handle the current mtu size. * Some of the more recent routers return the new mtu size. */ needfrag = 0; if (type == ICMP_UNREACH && code == ICMP_UNREACH_NEEDFRAG) needfrag = ntohl(icp->icmp_void) & 0xFFFF; /* * Check the type of the icmp packet. * We should get ICMP_TIMXCEED if we got to an intermediate host. * We get ICMP_UNREACH if the desired host is currently unreachable, * or if we got to the final destination itself. */ if (type == ICMP_TIMXCEED && code == ICMP_TIMXCEED_INTRANS) return(TIMXCEED_INTRANS); if (type == ICMP_UNREACH) return(DEST_UNREACHABLE + code); /* * Useless packet. Dump its contents if so requested. */ bad: if (verbose) { printf("\n%d bytes from %s: (bad packet) ", cc, pr_addr(from->sin_addr)); /* dump the icmp packet */ print_icmph(icp, cc); #ifdef obsolete printf("\n%d bytes from %s: icmp", cc, pr_addr(from->sin_addr)); printf(" type %d (%s)", type, pr_type(type)); if (type == ICMP_UNREACH) printf(" code %d (%s)", code, pr_code(code)); else printf(" code %d", code); printf("\n"); #endif /*obsolete*/ } return(BAD_ICMP_PACKET); } /* ** PRINT_ICMPH -- Print a descriptive string about an ICMP header ** -------------------------------------------------------------- */ void print_icmph(icp, cc) struct icmp *icp; /* icmp packet buffer */ int cc; /* size of icmp packet */ { /* check basic type and subcode */ switch (icp->icmp_type) { case ICMP_ECHOREPLY: printf("Echo reply\n"); /* XXX ID + Seq + Data */ break; case ICMP_UNREACH: switch (icp->icmp_code) { case ICMP_UNREACH_NET: printf("Network unreachable\n"); break; case ICMP_UNREACH_HOST: printf("Host unreachable\n"); break; case ICMP_UNREACH_PROTOCOL: printf("Protocol unreachable\n"); break; case ICMP_UNREACH_PORT: printf("Port unreachable\n"); break; case ICMP_UNREACH_NEEDFRAG: printf("Frag needed and DF set\n"); break; case ICMP_UNREACH_SRCFAIL: printf("Source route failed\n"); break; case ICMP_UNREACH_NET_UNKNOWN: printf("Network unknown\n"); break; case ICMP_UNREACH_HOST_UNKNOWN: printf("Host unknown\n"); break; case ICMP_UNREACH_ISOLATED: printf("Source host isolated\n"); break; case ICMP_UNREACH_NET_PROHIB: printf("Network access prohibited\n"); break; case ICMP_UNREACH_HOST_PROHIB: printf("Host access prohibited\n"); break; case ICMP_UNREACH_TOSNET: printf("Network unreachable for TOS\n"); break; case ICMP_UNREACH_TOSHOST: printf("Host unreachable for TOS\n"); break; case ICMP_UNREACH_ADM_PROHIB: printf("Access prohibited\n"); break; case ICMP_UNREACH_PREC_VIOL: printf("Precedence violation\n"); break; case ICMP_UNREACH_PREC_CUT: printf("Precedence cutoff\n"); break; default: printf("Destination unreachable, unknown code %d\n", icp->icmp_code); break; } print_ippkt((struct ip *)icp->icmp_data, cc - ICMP_MINLEN); break; case ICMP_SOURCEQUENCH: printf("Source quench\n"); print_ippkt((struct ip *)icp->icmp_data, cc - ICMP_MINLEN); break; case ICMP_REDIRECT: switch (icp->icmp_code) { case ICMP_REDIRECT_NET: printf("Redirect network"); break; case ICMP_REDIRECT_HOST: printf("Redirect host"); break; case ICMP_REDIRECT_TOSNET: printf("Redirect TOS and network"); break; case ICMP_REDIRECT_TOSHOST: printf("Redirect TOS and host"); break; default: printf("Redirect, unknown code %d", icp->icmp_code); break; } printf(" (new: %s)\n", inet_ntoa(icp->icmp_gwaddr)); print_ippkt((struct ip *)icp->icmp_data, cc - ICMP_MINLEN); break; case ICMP_ECHO: printf("Echo request\n"); /* XXX ID + Seq + Data */ break; case ICMP_ROUTERADVERT: printf("Router advertisement\n"); break; case ICMP_ROUTERSOLICIT: printf("Router solicitation\n"); break; case ICMP_TIMXCEED: switch (icp->icmp_code) { case ICMP_TIMXCEED_INTRANS: printf("Time to live exceeded\n"); break; case ICMP_TIMXCEED_REASS: printf("Frag reassembly time exceeded\n"); break; default: printf("Time exceeded, unknown code %d\n", icp->icmp_code); break; } print_ippkt((struct ip *)icp->icmp_data, cc - ICMP_MINLEN); break; case ICMP_PARAMPROB: printf("Parameter problem"); printf(" (pointer: 0x%02x)\n", icp->icmp_pptr); print_ippkt((struct ip *)icp->icmp_data, cc - ICMP_MINLEN); break; case ICMP_TSTAMP: printf("Timestamp request\n"); /* XXX ID + Seq + 3 timestamps */ break; case ICMP_TSTAMPREPLY: printf("Timestamp reply\n"); /* XXX ID + Seq + 3 timestamps */ break; case ICMP_IREQ: printf("Information request\n"); /* XXX ID + Seq */ break; case ICMP_IREQREPLY: printf("Information reply\n"); /* XXX ID + Seq */ break; case ICMP_MASKREQ: printf("Address mask request\n"); break; case ICMP_MASKREPLY: printf("Address mask reply\n"); break; default: printf("Unknown ICMP type %d\n", icp->icmp_type); break; } } /* ** PRINT_IPPKT -- Dump some info on a returned (via ICMP) IP packet ** ---------------------------------------------------------------- */ void print_ippkt(ip, cc) struct ip *ip; /* returned ip packet buffer */ int cc; /* size of ip packet */ { int iphdrlen; /* total size of ip header */ struct tcphdr *tcp; /* start of tcp packet */ struct udphdr *udp; /* start of udp packet */ struct icmp *icp; /* start of icmp packet */ /* silently discard too short packets */ if (cc < IPHDRSZ) return; /* * Print ip header itself. */ print_iphdr(ip, cc); /* * Plus extra info for certain protocols. */ iphdrlen = ip->ip_hl << 2; cc -= iphdrlen; if (ip->ip_p == IPPROTO_TCP && cc >= 2*INT16SZ) { tcp = (struct tcphdr *)((u_char *)ip + iphdrlen); printf("TCP: "); printf("from port %s", pr_port("tcp", tcp->th_sport)); printf(", to port %s", pr_port("tcp", tcp->th_dport)); printf("\n"); } else if (ip->ip_p == IPPROTO_UDP && cc >= 2*INT16SZ) { udp = (struct udphdr *)((u_char *)ip + iphdrlen); printf("UDP: "); printf("from port %s", pr_port("udp", udp->uh_sport)); printf(", to port %s", pr_port("udp", udp->uh_dport)); printf("\n"); } else if (ip->ip_p == IPPROTO_ICMP && cc >= ICMP_MINLEN) { icp = (struct icmp *)((u_char *)ip + iphdrlen); printf("ICMP: "); print_icmph(icp, cc); } } /* ** PRINT_IPHDR -- Print an IP header with options ** ---------------------------------------------- */ void print_iphdr(ip, cc) struct ip *ip; /* returned ip packet buffer */ int cc; /* size of ip packet */ { int iphdrlen; /* total size of ip header */ /* * Some platforms return the entire header in network byte order. */ #ifdef RAW_IP_NET_ORDER ip->ip_id = ntohs(ip->ip_id); ip->ip_sum = ntohs(ip->ip_sum); ip->ip_len = ntohs((u_short)ip->ip_len); ip->ip_off = ntohs((u_short)ip->ip_off); #endif /*RAW_IP_NET_ORDER*/ /* * Dump the ip header. */ printf("VR HL TOS LEN ID FLG OFF TTL PRO CKS SRC DST\n"); printf("%2d %2d", ip->ip_v, ip->ip_hl); printf(" %02x", ip->ip_tos); printf(" %4d", (int)ip->ip_len); printf(" %04x", ip->ip_id); printf(" %01x", ((ip->ip_off) & 0xE000) >> 13); printf(" %04x", ((ip->ip_off) & 0x1FFF)); printf(" %3d", (int)ip->ip_ttl); printf(" %3d", (int)ip->ip_p); printf(" %04x", ip->ip_sum); printf(" %-15s", inet_ntoa(ip->ip_src)); printf(" %-15s", inet_ntoa(ip->ip_dst)); printf("\n"); /* * Dump option bytes. */ iphdrlen = ip->ip_hl << 2; if (iphdrlen > IPHDRSZ && cc >= iphdrlen) { register int i; u_char *ipopt; /* address of options buffer */ int ipoptlen; /* total size of options buffer */ ipopt = (u_char *)ip + IPHDRSZ; ipoptlen = iphdrlen - IPHDRSZ; printf("IPOPT:"); for (i = 0; i < ipoptlen; i++) printf(" %02x", ipopt[i]); printf("\n"); #ifdef IP_OPTIONS print_options(ipopt, ipoptlen); #endif /*IP_OPTIONS*/ } } /* ** PRINT_OPTIONS -- Print ip options data ** -------------------------------------- */ #ifdef IP_OPTIONS void print_options(ipopt, ipoptlen) u_char *ipopt; /* address of options buffer */ int ipoptlen; /* total size of options buffer */ { int optval; /* option value */ int optlen; /* size of this option */ while (ipoptlen > 0) { optval = ipopt[IPOPT_OPTVAL]; optlen = ipopt[IPOPT_OLEN]; switch (optval) { case IPOPT_EOL: /* force end of options */ optlen = 0; break; case IPOPT_NOP: /* has no parameters */ optlen = 1; break; case IPOPT_RR: printf("<%s>\n", "recorded route"); print_route(ipopt); break; case IPOPT_LSRR: printf("<%s>\n", "loose source route"); print_route(ipopt); break; case IPOPT_SSRR: printf("<%s>\n", "strict source route"); print_route(ipopt); break; case IPOPT_TS: printf("<%s>\n", "time stamp"); break; case IPOPT_SECURITY: printf("<%s>\n", "security"); break; case IPOPT_SATID: printf("<%s>\n", "stream id"); break; default: printf("