/*
* 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 <van@helios.ee.lbl.gov>
*
* Totally rewritten by Eric Wassenaar, Nikhef-H, <e07@nikhef.nl>
*
* 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 <netinet/ip.h> 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 <stdio.h>
#include <errno.h>
#include <ctype.h>
#include <signal.h>
#include <setjmp.h>
#include <sys/types.h> /* not always automatically included */
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/file.h>
#include <sys/time.h>
#if defined(_AIX)
#include <sys/select.h> /* needed for fd_set */
#endif
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#if 0
#include <netinet/ip_var.h> /* only needed for MAX_IPOPTLEN */
#endif
#include <netinet/ip_icmp.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <netdb.h>
#undef NOERROR /* in <sys/streams.h> on solaris 2.x */
#include <arpa/nameser.h>
#include <resolv.h>
#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 <sysexits.h> */
#include "icmp.h" /* icmp types belong in <netinet/ip_icmp.h> */
#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 <sysexits.h> 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 <netinet/in.h>.
** 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("<option %d, length %d>\n", optval, optlen);
break;
}
/* end of options encountered */
if (optlen == 0)
break;
/* move to next option */
ipopt += optlen;
ipoptlen -= optlen;
}
}
#endif /*IP_OPTIONS*/
/*
** PRINT_ROUTE -- Print ip route data
** ----------------------------------
*/
#ifdef IP_OPTIONS
void
print_route(ipopt)
u_char *ipopt; /* start of current option */
{
int optval; /* option value */
int optlen; /* size of this option */
u_char *optptr; /* pointer to option data */
int offset; /* length of option data */
optval = ipopt[IPOPT_OPTVAL];
optlen = ipopt[IPOPT_OLEN];
offset = ipopt[IPOPT_OFFSET];
optptr = &ipopt[IPOPT_HDRLEN];
optlen -= IPOPT_HDRLEN;
while (offset > IPOPT_MINOFF)
{
struct in_addr inaddr;
bcopy((char *)optptr, (char *)&inaddr, INADDRSZ);
printf("\t%s\n", pr_addr(inaddr));
optptr += INADDRSZ;
offset -= INADDRSZ;
optlen -= INADDRSZ;
}
/* buffer full -- perhaps route not complete */
if (optval == IPOPT_RR && optlen == 0)
printf("\t%s\n", "...");
}
#endif /*IP_OPTIONS*/
/*
** PR_PORT -- Return a service port name
** -------------------------------------
**
** Returns:
** Pointer to static storage containing port name/number.
*/
char *
pr_port(protocol, port)
char *protocol; /* the protocol used */
u_short port; /* port number in network order */
{
struct servent *service;
static char buf[BUFSIZ];
service = getservbyport((int)port, protocol);
if (service != NULL)
(void) sprintf(buf, "%s", service->s_name);
else
(void) sprintf(buf, "%d", (int)ntohs(port));
return(buf);
}
/*
** PR_TYPE -- Return type of icmp input packet
** -------------------------------------------
**
** Returns:
** Pointer to name of packet type.
*/
#ifdef obsolete
char *
pr_type(type)
int type; /* type value of icmp packet */
{
static char *ttab[] =
{
/* 0 */ "echo reply",
/* 1 */ "ICMP 1",
/* 2 */ "ICMP 2",
/* 3 */ "destination unreachable",
/* 4 */ "source quench",
/* 5 */ "redirect route",
/* 6 */ "ICMP 6",
/* 7 */ "ICMP 7",
/* 8 */ "echo request",
/* 9 */ "router advertisement",
/* 10 */ "router solicitation",
/* 11 */ "time exceeded",
/* 12 */ "parameter problem",
/* 13 */ "timestamp request",
/* 14 */ "timestamp reply",
/* 15 */ "information request",
/* 16 */ "information reply",
/* 17 */ "address mask request",
/* 18 */ "address mask reply"
};
if (type < 0 || type > 18)
return("unknown icmp type");
return(ttab[type]);
}
#endif /*obsolete*/
/*
** PR_CODE -- Return code of icmp dest unreachable packet
** ------------------------------------------------------
**
** Returns:
** Pointer to description of packet code.
*/
#ifdef obsolete
char *
pr_code(code)
int code; /* code value of icmp packet */
{
static char *utab[] =
{
/* 0 */ "network unreachable",
/* 1 */ "host unreachable",
/* 2 */ "protocol unreachable",
/* 3 */ "port unreachable",
/* 4 */ "frag needed and DF set",
/* 5 */ "source route failed",
/* 6 */ "network unknown",
/* 7 */ "host unknown",
/* 8 */ "source host isolated",
/* 9 */ "network access prohibited",
/* 10 */ "host access prohibited",
/* 11 */ "network unreachable for TOS",
/* 12 */ "host unreachable for TOS",
/* 13 */ "access prohibited",
/* 14 */ "precedence violation",
/* 15 */ "precedence cutoff",
};
if (code < 0 || code > 15)
return("unknown icmp code");
return(utab[code]);
}
#endif /*obsolete*/
/*
** PRINT_FROM -- Print the address from which we got an input packet
** -----------------------------------------------------------------
**
** Additional data is displayed depending on command options.
*/
void
print_from(buf, cc, inaddr)
u_char *buf; /* address of input packet */
int cc; /* number of bytes received */
struct in_addr inaddr; /* internet address */
{
char *asnumber = NULL; /* set if AS-number available */
struct netent *np = NULL; /* set if network name available */
struct ip *ip;
int iphdrlen;
ip = (struct ip *)buf;
iphdrlen = ip->ip_hl << 2;
cc -= iphdrlen;
printf("%s", separator);
if (numeric)
printf(" %s", inet_ntoa(inaddr));
else
printf(" %s (%s)", inetname(inaddr), inet_ntoa(inaddr));
if (lookupnet)
np = getnetbyaddr(inet_netof(inaddr), AF_INET);
if (np != NULL)
printf(" (%s)", np->n_name);
if (lookupas)
{
(void) fflush(stdout);
synchronous = TRUE;
asnumber = as_lookup(inaddr, ashost, 0, keepopen);
synchronous = FALSE;
if (interrupted)
longjmp(interrupt_buf, 1);
}
if (asnumber != NULL)
printf(" [%s]", asnumber);
if (verbose)
printf (" %d bytes to %s", cc, inet_ntoa(ip->ip_dst));
}
/*
** INETNAME -- Return a host name or dotted quad
** ---------------------------------------------
**
** Returns:
** Pointer to static storage containing host/address string.
*/
char *
inetname(inaddr)
struct in_addr inaddr; /* IP address */
{
static char buf[MAXDNAME+1];
register char *host;
if (numeric || (inaddr.s_addr == INADDR_ANY))
host = NULL;
else
host = maphostbyaddr(inaddr);
if (host != NULL)
(void) strcpy(buf, host);
else
(void) strcpy(buf, inet_ntoa(inaddr));
return(buf);
}
/*
** PR_ADDR -- Return a host name and/or dotted quad
** ------------------------------------------------
**
** Returns:
** Pointer to static storage containing host/address string.
*/
char *
pr_addr(inaddr)
struct in_addr inaddr; /* IP address */
{
static char buf[MAXDNAME+19+1];
register char *host;
if (numeric || (inaddr.s_addr == INADDR_ANY))
host = NULL;
else
host = maphostbyaddr(inaddr);
if (host != NULL)
(void) sprintf(buf, "%s (%s)", host, inet_ntoa(inaddr));
else
(void) sprintf(buf, "%s", inet_ntoa(inaddr));
return(buf);
}
/*
** MAPHOSTBYADDR -- Map IP address to host name
** --------------------------------------------
**
** Returns:
** Pointer to string containing host name.
** NULL if address could not be resolved.
**
** The results are cached for subsequent retrieval.
*/
#define AHASHSIZE 0x2000
#define AHASHMASK 0x1fff
typedef struct addr_tab {
ipaddr_t *addrlist; /* list of IP addresses */
char **namelist; /* list of corresponding host names */
int addrcount; /* count of addresses on the list */
} addr_tab_t;
addr_tab_t addrtab[AHASHSIZE]; /* hash list of addresses and names */
char *
maphostbyaddr(inaddr)
struct in_addr inaddr; /* IP address to map */
{
ipaddr_t addr = inaddr.s_addr; /* address in network order */
register addr_tab_t *s;
register char *host;
register int i;
struct hostent *hp;
/* in case no mapping is desired */
if (numeric || (inaddr.s_addr == INADDR_ANY))
return(NULL);
/*
* Lookup the address in the appropriate hash list.
*/
s = &addrtab[ntohl(addr) & AHASHMASK];
for (i = 0; i < s->addrcount; i++)
if (s->addrlist[i] == addr)
return(s->namelist[i]);
/*
* Unknown address. Try to resolve it.
*/
hp = gethostbyaddr((char *)&inaddr, INADDRSZ, AF_INET);
if (hp != NULL)
host = maxstr(newstr(hp->h_name), MAXDNAME, FALSE);
else
host = NULL;
/*
* Put it on the hash list.
*/
s->addrlist = newlist(s->addrlist, s->addrcount+1, ipaddr_t);
s->namelist = newlist(s->namelist, s->addrcount+1, char *);
s->addrlist[s->addrcount] = addr;
s->namelist[s->addrcount] = host;
s->addrcount++;
return(host);
}
/*
** GETGATE -- Fetch internet address of host
** -----------------------------------------
**
** Returns:
** Internet address of given host.
** Aborts if address could not be determined.
**
** Only the first address as returned by the resolver is used.
** This address is used as loose source route gateway address.
**
** As a side effect, we will try to determine all its addresses
** and add them to the global address list ``gateaddr[]'' which
** has ``ngate'' entries.
*/
ipaddr_t
getgate(host)
char *host; /* host name or dotted quad */
{
register int i;
ipaddr_t addr;
struct in_addr inaddr;
struct hostent *hp;
/*
* Determine all addresses of the given host. Add original to the list.
*/
addr = inet_addr(host);
inaddr.s_addr = addr;
if (addr == NOT_DOTTED_QUAD)
{
hp = gethostbyname(host);
if (hp == NULL)
{
error("Unknown host %s", host);
exit(EX_NOHOST);
}
bcopy(hp->h_addr, (char *)&inaddr, INADDRSZ);
addr = inaddr.s_addr;
}
else
{
host = maphostbyaddr(inaddr);
if (host != NULL)
hp = gethostbyname(host);
else
hp = NULL;
if ((ngate < MAXGATE) && !gatewayaddr(inaddr))
gateaddr[ngate++] = inaddr.s_addr;
}
/*
* Append all found addresses to the global address list.
*/
for (i = 0; hp != NULL && hp->h_addr_list[i]; i++)
{
bcopy(hp->h_addr_list[i], (char *)&inaddr, INADDRSZ);
if ((ngate < MAXGATE) && !gatewayaddr(inaddr))
gateaddr[ngate++] = inaddr.s_addr;
}
/* prep the address cache */
for (i = 0; i < ngate; i++)
{
inaddr.s_addr = gateaddr[i];
(void) maphostbyaddr(inaddr);
}
return(addr);
}
/*
** GATEWAYADDR -- Check whether address belongs to known gateways
** --------------------------------------------------------------
**
** Returns:
** TRUE if the address is listed in the global table.
** FALSE otherwise.
*/
bool
gatewayaddr(inaddr)
struct in_addr inaddr; /* internet address to check */
{
register int i;
for (i = 0; i < ngate; i++)
{
if (inaddr.s_addr == gateaddr[i])
break;
}
return(i < ngate ? TRUE : FALSE);
}
/*
** IN_CHECKSUM -- Compute checksum for IP packets
** ----------------------------------------------
**
** The complete packet must have been constructed.
** The checksum field to be computed must be zero.
**
** Returns:
** Computed checksum.
*/
#ifdef obsolete
u_short
in_checksum(buf, len)
u_short *buf; /* start of packet */
int len; /* length of packet in bytes */
{
register u_short *w = buf; /* address of next 16-bit word */
register int nleft = len; /* remaining 16-bit words */
register int sum = 0; /* 32-bit accumulator */
u_short answer = 0;
/*
* Our algorithm is simple, using a 32 bit accumulator (sum),
* we add sequential 16 bit words to it, and at the end, fold back
* all the carry bits from the top 16 bits into the lower 16 bits.
*/
while (nleft > 1)
{
sum += *w++;
nleft -= 2;
}
if (nleft == 1)
{
*(u_char *)(&answer) = *(u_char *)w;
sum += answer;
}
/*
* Add back carry outs from top 16 bits to low 16 bits.
*/
sum = (sum >> 16) + (sum & 0xFFFF); /* add hi 16 to low 16 */
sum += (sum >> 16); /* add carry */
answer = ~sum; /* truncate to 16 bits */
return(answer);
}
#endif /*obsolete*/
/*
** IN_CHECKSUM -- Compute checksum for IP packets
** ----------------------------------------------
**
** The complete packet must have been constructed.
** The checksum field to be computed must be zero.
**
** Returns:
** Computed checksum.
*/
#ifdef notyet
u_short
in_checksum(buf, len)
u_short *buf; /* start of packet */
int len; /* length of packet in bytes */
{
register u_char *b = (u_char *)buf;
register int n = len; /* remaining 16-bit words */
register int sum = 0; /* 32-bit accumulator */
u_short answer;
/*
* Our algorithm is simple, using a 32 bit accumulator (sum),
* we add sequential 16 bit words to it, and at the end, fold back
* all the carry bits from the top 16 bits into the lower 16 bits.
*/
while (n > 1)
{
answer = (b[0] << 8) | b[1];
sum += answer;
b += 2; n -= 2;
}
if (n == 1)
{
answer = (b[0] << 8);
sum += answer;
}
/*
* Add back carry outs from top 16 bits to low 16 bits.
*/
sum = (sum >> 16) + (sum & 0xFFFF); /* add hi 16 to low 16 */
sum += (sum >> 16); /* add carry */
answer = ~sum & 0xFFFF; /* truncate to 16 bits */
answer = htons(answer); /* correct order */
return(answer);
}
#endif /*notyet*/
/*
** TVSUB -- Subtract two timeval structs
** -------------------------------------
**
** Returns:
** Time difference in micro(!)seconds.
**
** Side effects:
** The difference of the two timeval structs
** is stored back into the first.
**
** This implementation assumes that time_t is a signed value.
** On 32-bit machines this limits the range to ~35 minutes.
** That seems sufficient for most practical purposes.
** Note that tv_sec is an *un*signed entity on some platforms.
*/
time_t
tvsub(t2, t1)
struct timeval *t2; /* latest timeval */
struct timeval *t1; /* oldest timeval */
{
register time_t usec;
t2->tv_usec -= t1->tv_usec;
while (t2->tv_usec < 0)
{
t2->tv_usec += 1000000;
if (t2->tv_sec != 0)
t2->tv_sec--;
else
t2->tv_usec = 0;
}
if (t2->tv_sec < t1->tv_sec)
{
t2->tv_sec = 0;
t2->tv_usec = 0;
}
else
t2->tv_sec -= t1->tv_sec;
if (t2->tv_sec > MAXSECS)
{
t2->tv_sec = MAXSECS;
t2->tv_usec = 0;
}
usec = t2->tv_sec*1000000 + t2->tv_usec;
return(usec);
}
/*
** TVPRINT -- Convert time value to ascii string
** ---------------------------------------------
**
** Returns:
** Pointer to string in static storage.
**
** Output is in variable format, depending on the value.
** This avoids printing of non-significant digits.
*/
char *
tvprint(usec)
time_t usec; /* value to convert */
{
static char buf[30]; /* sufficient for 64-bit values */
time_t uval;
if (usec < 1000)
{
uval = usec;
(void) sprintf(buf, "%ld.%3.3ld", uval/1000, uval%1000);
}
else if (usec < 10000)
{
uval = (usec + 5)/10;
(void) sprintf(buf, "%ld.%2.2ld", uval/100, uval%100);
}
else if (usec < 100000)
{
uval = (usec + 50)/100;
(void) sprintf(buf, "%ld.%1.1ld", uval/10, uval%10);
}
else
{
uval = (usec + 500)/1000;
(void) sprintf(buf, "%ld", uval);
}
return(buf);
}
/*
** PRINT_STATS -- Print round-trip statistics
** ------------------------------------------
**
** Returns:
** None.
*/
time_t rttmin; /* minimum rtt value */
time_t rttmax; /* maximum rtt value */
double rttsum; /* accumulated rtt values */
double rttssq; /* sum of squared rtt values */
int nreceived = 0; /* number of valid replies received */
void
print_stats(ntransmitted)
int ntransmitted; /* number of probe packets sent */
{
double rttavg; /* average round-trip time */
double rttstd; /* rtt standard deviation */
if (ntransmitted > 0 && ntransmitted >= nreceived)
{
int missed = ntransmitted - nreceived;
double loss = 100 * (double)missed / (double)ntransmitted;
printf("%s", separator);
if (nreceived > 0)
{
rttavg = rttsum / nreceived;
rttstd = rttssq - (rttavg * rttsum);
rttstd = xsqrt(rttstd / nreceived);
printf(" %s", tvprint(rttmin));
printf("/%s", tvprint((time_t)rttavg));
printf("/%s", tvprint(rttmax));
printf(" (%s) ms ", tvprint((time_t)rttstd));
}
printf(" %d/%d pkts", nreceived, ntransmitted);
if (nreceived == 0)
printf(" (%d%% loss)", 100);
else
printf(" (%.2g%% loss)", loss);
}
}
/*
** RECORD_STATS -- Update round-trip statistics
** --------------------------------------------
**
** Returns:
** None.
*/
void
record_stats(rtt)
time_t rtt; /* round-trip value */
{
if (nreceived == 0)
{
rttmin = rtt;
rttmax = rtt;
rttsum = 0.0;
rttssq = 0.0;
}
nreceived++;
if (rtt < rttmin)
rttmin = rtt;
if (rtt > rttmax)
rttmax = rtt;
rttsum += (double)rtt;
rttssq += (double)rtt * (double)rtt;
}
/*
** CLEAR_STATS -- Clear out round-trip statistics
** ----------------------------------------------
**
** Returns:
** None.
*/
void
clear_stats()
{
nreceived = 0;
}
/*
** SELECT_MTU -- Select next lower mtu size from table
** ---------------------------------------------------
**
** Returns:
** New mtu size, if available.
** Old mtu size otherwise.
**
** The table of known mtu sizes is defined in "conf.h"
*/
int
select_mtu(len)
int len; /* current ip packet size */
{
register int i;
register int mtu;
for (i = 0; (mtu = mtusize[i]) > 0; i++)
{
if (mtu < len)
break;
}
if (mtu > 0)
len = mtu;
return(len);
}
/*
** REGULAR_TTL -- Check whether this is a known ttl value
** ------------------------------------------------------
**
** Returns:
** TRUE if this ttl belongs to the known values.
** FALSE otherwise.
**
** The known ttl values are defined in "conf.h"
*/
bool
regular_ttl(ttl)
int ttl; /* ttl value */
{
switch (ttl)
{
case MAX_START_TTL:
case NETWARE_TTL:
case OLD_PROTEON_TTL:
case NEW_PROTEON_TTL:
case OLD_BSD_TCP:
case NEW_BSD_TCP:
return(TRUE);
}
/* unknown value */
return(FALSE);
}
/*
** MAXSTR -- Ensure string does not exceed maximum size
** ----------------------------------------------------
**
** Returns:
** Pointer to the (possibly truncated) string.
**
** If necessary, a new string is allocated, and is then
** truncated, and the original string is left intact.
** Otherwise the original string is truncated in place.
**
*/
char *
maxstr(string, n, save)
char *string; /* the string to check */
int n; /* the maximum allowed size */
bool save; /* allocate new string, if set */
{
if (strlength(string) > n)
{
if (save)
string = newstr(string);
string[n] = '\0';
}
return(string);
}
/*
** XALLOC -- Allocate or reallocate additional memory
** --------------------------------------------------
**
** Returns:
** Pointer to (re)allocated buffer space.
** Aborts if the requested memory could not be obtained.
*/
ptr_t *
xalloc(buf, size)
register ptr_t *buf; /* current start of buffer space */
siz_t size; /* number of bytes to allocate */
{
if (buf == NULL)
buf = malloc(size);
else
buf = realloc(buf, size);
if (buf == NULL)
{
error("Out of memory");
exit(EX_OSERR);
}
return(buf);
}
/*
** ITOA -- Convert integer value to ascii string
** ---------------------------------------------
**
** Returns:
** Pointer to string in static storage.
*/
char *
itoa(n)
int n; /* value to convert */
{
static char buf[30]; /* sufficient for 64-bit values */
(void) sprintf(buf, "%d", n);
return(buf);
}
/*
** XSQRT -- Compute arithmetic square root
** ---------------------------------------
**
** Returns:
** Computed square root value.
**
** This is supplied to avoid linking with the -lm library.
** Several Newton-Raphson iterations are performed until
** the machine precision is hit.
*/
double
xsqrt(y)
double y;
{
double t, x;
if (y <= 0)
return(0);
x = (y < 1.0) ? 1.0 : y;
do {
t = x;
x = (t + (y/t))/2.0;
} while (0 < x && x < t);
return(x);
}
syntax highlighted by Code2HTML, v. 0.9.1