ping.c revision 19864
11558Srgrimes/*
21558Srgrimes * Copyright (c) 1989, 1993
31558Srgrimes *	The Regents of the University of California.  All rights reserved.
41558Srgrimes *
51558Srgrimes * This code is derived from software contributed to Berkeley by
61558Srgrimes * Mike Muuss.
71558Srgrimes *
81558Srgrimes * Redistribution and use in source and binary forms, with or without
91558Srgrimes * modification, are permitted provided that the following conditions
101558Srgrimes * are met:
111558Srgrimes * 1. Redistributions of source code must retain the above copyright
121558Srgrimes *    notice, this list of conditions and the following disclaimer.
131558Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141558Srgrimes *    notice, this list of conditions and the following disclaimer in the
151558Srgrimes *    documentation and/or other materials provided with the distribution.
161558Srgrimes * 3. All advertising materials mentioning features or use of this software
171558Srgrimes *    must display the following acknowledgement:
181558Srgrimes *	This product includes software developed by the University of
191558Srgrimes *	California, Berkeley and its contributors.
201558Srgrimes * 4. Neither the name of the University nor the names of its contributors
211558Srgrimes *    may be used to endorse or promote products derived from this software
221558Srgrimes *    without specific prior written permission.
231558Srgrimes *
241558Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
251558Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
261558Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
271558Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
281558Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
291558Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
301558Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
311558Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
321558Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
331558Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
341558Srgrimes * SUCH DAMAGE.
351558Srgrimes */
361558Srgrimes
371558Srgrimes#ifndef lint
381558Srgrimesstatic char copyright[] =
391558Srgrimes"@(#) Copyright (c) 1989, 1993\n\
401558Srgrimes	The Regents of the University of California.  All rights reserved.\n";
411558Srgrimes#endif /* not lint */
421558Srgrimes
431558Srgrimes#ifndef lint
441558Srgrimesstatic char sccsid[] = "@(#)ping.c	8.1 (Berkeley) 6/5/93";
451558Srgrimes#endif /* not lint */
461558Srgrimes
471558Srgrimes/*
481558Srgrimes *			P I N G . C
491558Srgrimes *
501558Srgrimes * Using the InterNet Control Message Protocol (ICMP) "ECHO" facility,
511558Srgrimes * measure round-trip-delays and packet loss across network paths.
521558Srgrimes *
531558Srgrimes * Author -
541558Srgrimes *	Mike Muuss
551558Srgrimes *	U. S. Army Ballistic Research Laboratory
561558Srgrimes *	December, 1983
571558Srgrimes *
581558Srgrimes * Status -
591558Srgrimes *	Public Domain.  Distribution Unlimited.
601558Srgrimes * Bugs -
611558Srgrimes *	More statistics could always be gathered.
621558Srgrimes *	This program has to run SUID to ROOT to access the ICMP socket.
631558Srgrimes */
641558Srgrimes
651558Srgrimes#include <sys/param.h>
661558Srgrimes#include <sys/socket.h>
671558Srgrimes#include <sys/file.h>
681558Srgrimes#include <sys/time.h>
691558Srgrimes#include <sys/signal.h>
703792Ssef#include <termios.h>
711558Srgrimes
721558Srgrimes#include <netinet/in_systm.h>
731558Srgrimes#include <netinet/in.h>
741558Srgrimes#include <netinet/ip.h>
751558Srgrimes#include <netinet/ip_icmp.h>
761558Srgrimes#include <netinet/ip_var.h>
771558Srgrimes#include <netdb.h>
781558Srgrimes#include <unistd.h>
791558Srgrimes#include <stdio.h>
801558Srgrimes#include <ctype.h>
811558Srgrimes#include <errno.h>
821558Srgrimes#include <string.h>
831558Srgrimes
841558Srgrimes#define	DEFDATALEN	(64 - 8)	/* default data length */
851558Srgrimes#define	MAXIPLEN	60
861558Srgrimes#define	MAXICMPLEN	76
871558Srgrimes#define	MAXPACKET	(65536 - 60 - 8)/* max packet size */
881558Srgrimes#define	MAXWAIT		10		/* max seconds to wait for response */
891558Srgrimes#define	NROUTES		9		/* number of record route slots */
901558Srgrimes
911558Srgrimes#define	A(bit)		rcvd_tbl[(bit)>>3]	/* identify byte in array */
921558Srgrimes#define	B(bit)		(1 << ((bit) & 0x07))	/* identify bit in byte */
931558Srgrimes#define	SET(bit)	(A(bit) |= B(bit))
941558Srgrimes#define	CLR(bit)	(A(bit) &= (~B(bit)))
951558Srgrimes#define	TST(bit)	(A(bit) & B(bit))
961558Srgrimes
971558Srgrimes/* various options */
981558Srgrimesint options;
991558Srgrimes#define	F_FLOOD		0x001
1001558Srgrimes#define	F_INTERVAL	0x002
1011558Srgrimes#define	F_NUMERIC	0x004
1021558Srgrimes#define	F_PINGFILLED	0x008
1031558Srgrimes#define	F_QUIET		0x010
1041558Srgrimes#define	F_RROUTE	0x020
1051558Srgrimes#define	F_SO_DEBUG	0x040
1061558Srgrimes#define	F_SO_DONTROUTE	0x080
1071558Srgrimes#define	F_VERBOSE	0x100
10817724Sfenner#define	F_QUIET2	0x200
1091558Srgrimes
1101558Srgrimes/*
1111558Srgrimes * MAX_DUP_CHK is the number of bits in received table, i.e. the maximum
1121558Srgrimes * number of received sequence numbers we can keep track of.  Change 128
1131558Srgrimes * to 8192 for complete accuracy...
1141558Srgrimes */
1151558Srgrimes#define	MAX_DUP_CHK	(8 * 128)
1161558Srgrimesint mx_dup_ck = MAX_DUP_CHK;
1171558Srgrimeschar rcvd_tbl[MAX_DUP_CHK / 8];
1181558Srgrimes
1191558Srgrimesstruct sockaddr whereto;	/* who to ping */
1201558Srgrimesint datalen = DEFDATALEN;
1211558Srgrimesint s;				/* socket file descriptor */
1221558Srgrimesu_char outpack[MAXPACKET];
1231558Srgrimeschar BSPACE = '\b';		/* characters written for flood */
1241558Srgrimeschar DOT = '.';
1251558Srgrimeschar *hostname;
1261558Srgrimesint ident;			/* process id to identify our packets */
1271558Srgrimes
1281558Srgrimes/* counters */
1291558Srgrimeslong npackets;			/* max packets to transmit */
1301558Srgrimeslong nreceived;			/* # of packets we got back */
1311558Srgrimeslong nrepeats;			/* number of duplicates */
1321558Srgrimeslong ntransmitted;		/* sequence # for outbound packets = #sent */
1331558Srgrimesint interval = 1;		/* interval between packets */
1341558Srgrimes
1351558Srgrimes/* timing */
1361558Srgrimesint timing;			/* flag to do timing */
1371558Srgrimesdouble tmin = 999999999.0;	/* minimum round trip time */
1381558Srgrimesdouble tmax = 0.0;		/* maximum round trip time */
1391558Srgrimesdouble tsum = 0.0;		/* sum of all times, for doing average */
1401558Srgrimes
1413792Ssefint reset_kerninfo;
1423792Ssef
1431558Srgrimeschar *pr_addr();
1443792Ssefvoid catcher(), finish(), status();
1451558Srgrimes
1461558Srgrimesmain(argc, argv)
1471558Srgrimes	int argc;
1481558Srgrimes	char **argv;
1491558Srgrimes{
1501558Srgrimes	extern int errno, optind;
1511558Srgrimes	extern char *optarg;
1521558Srgrimes	struct timeval timeout;
1531558Srgrimes	struct hostent *hp;
1541558Srgrimes	struct sockaddr_in *to;
1551558Srgrimes	struct protoent *proto;
1563792Ssef	struct termios ts;
1571558Srgrimes	register int i;
15817474Sfenner	int ch, fdmask, hold, packlen, preload, sockerrno;
1591558Srgrimes	u_char *datap, *packet;
1601558Srgrimes	char *target, hnamebuf[MAXHOSTNAMELEN], *malloc();
1611558Srgrimes#ifdef IP_OPTIONS
1621558Srgrimes	char rspace[3 + 4 * NROUTES + 1];	/* record route space */
1631558Srgrimes#endif
1641558Srgrimes
16517474Sfenner	/*
16617474Sfenner	 * Do the stuff that we need root priv's for *first*, and
16717474Sfenner	 * then drop our setuid bit.  Save error reporting for
16817474Sfenner	 * after arg parsing.
16917474Sfenner	 */
17017474Sfenner	proto = getprotobyname("icmp");
17117474Sfenner	if (proto) {
17217474Sfenner		s = socket(AF_INET, SOCK_RAW, proto->p_proto);
17317474Sfenner		sockerrno = errno;
17417474Sfenner	}
17517474Sfenner
17617474Sfenner	setuid(getuid());
17717474Sfenner
1781558Srgrimes	preload = 0;
1793792Ssef
1801558Srgrimes	datap = &outpack[8 + sizeof(struct timeval)];
18117724Sfenner	while ((ch = getopt(argc, argv, "QRc:dfh:i:l:np:qrs:v")) != EOF)
1821558Srgrimes		switch(ch) {
1831558Srgrimes		case 'c':
1841558Srgrimes			npackets = atoi(optarg);
1851558Srgrimes			if (npackets <= 0) {
1861558Srgrimes				(void)fprintf(stderr,
1871558Srgrimes				    "ping: bad number of packets to transmit.\n");
1881558Srgrimes				exit(1);
1891558Srgrimes			}
1901558Srgrimes			break;
1911558Srgrimes		case 'd':
1921558Srgrimes			options |= F_SO_DEBUG;
1931558Srgrimes			break;
1941558Srgrimes		case 'f':
1951558Srgrimes			if (getuid()) {
1961558Srgrimes				(void)fprintf(stderr,
1971558Srgrimes				    "ping: %s\n", strerror(EPERM));
1981558Srgrimes				exit(1);
1991558Srgrimes			}
2001558Srgrimes			options |= F_FLOOD;
2011558Srgrimes			setbuf(stdout, (char *)NULL);
2021558Srgrimes			break;
2031558Srgrimes		case 'i':		/* wait between sending packets */
2041558Srgrimes			interval = atoi(optarg);
2051558Srgrimes			if (interval <= 0) {
2061558Srgrimes				(void)fprintf(stderr,
2071558Srgrimes				    "ping: bad timing interval.\n");
2081558Srgrimes				exit(1);
2091558Srgrimes			}
2101558Srgrimes			options |= F_INTERVAL;
2111558Srgrimes			break;
2121558Srgrimes		case 'l':
2131558Srgrimes			preload = atoi(optarg);
2141558Srgrimes			if (preload < 0) {
2151558Srgrimes				(void)fprintf(stderr,
2161558Srgrimes				    "ping: bad preload value.\n");
2171558Srgrimes				exit(1);
2181558Srgrimes			}
2191558Srgrimes			break;
2201558Srgrimes		case 'n':
2211558Srgrimes			options |= F_NUMERIC;
2221558Srgrimes			break;
2231558Srgrimes		case 'p':		/* fill buffer with user pattern */
2241558Srgrimes			options |= F_PINGFILLED;
2251558Srgrimes			fill((char *)datap, optarg);
2261558Srgrimes				break;
22717724Sfenner		case 'Q':
22817724Sfenner			options |= F_QUIET2;
22917724Sfenner			break;
2301558Srgrimes		case 'q':
2311558Srgrimes			options |= F_QUIET;
2321558Srgrimes			break;
2331558Srgrimes		case 'R':
2341558Srgrimes			options |= F_RROUTE;
2351558Srgrimes			break;
2361558Srgrimes		case 'r':
2371558Srgrimes			options |= F_SO_DONTROUTE;
2381558Srgrimes			break;
2391558Srgrimes		case 's':		/* size of packet to send */
2401558Srgrimes			datalen = atoi(optarg);
2411558Srgrimes			if (datalen > MAXPACKET) {
2421558Srgrimes				(void)fprintf(stderr,
2431558Srgrimes				    "ping: packet size too large.\n");
2441558Srgrimes				exit(1);
2451558Srgrimes			}
2461558Srgrimes			if (datalen <= 0) {
2471558Srgrimes				(void)fprintf(stderr,
2481558Srgrimes				    "ping: illegal packet size.\n");
2491558Srgrimes				exit(1);
2501558Srgrimes			}
2511558Srgrimes			break;
2521558Srgrimes		case 'v':
2531558Srgrimes			options |= F_VERBOSE;
2541558Srgrimes			break;
2551558Srgrimes		default:
2561558Srgrimes			usage();
2571558Srgrimes		}
2581558Srgrimes	argc -= optind;
2591558Srgrimes	argv += optind;
2601558Srgrimes
2611558Srgrimes	if (argc != 1)
2621558Srgrimes		usage();
2631558Srgrimes	target = *argv;
2641558Srgrimes
2651558Srgrimes	bzero((char *)&whereto, sizeof(struct sockaddr));
2661558Srgrimes	to = (struct sockaddr_in *)&whereto;
2671558Srgrimes	to->sin_family = AF_INET;
2681558Srgrimes	to->sin_addr.s_addr = inet_addr(target);
2691558Srgrimes	if (to->sin_addr.s_addr != (u_int)-1)
2701558Srgrimes		hostname = target;
2711558Srgrimes	else {
2721558Srgrimes		hp = gethostbyname(target);
2731558Srgrimes		if (!hp) {
2741558Srgrimes			(void)fprintf(stderr,
2751558Srgrimes			    "ping: unknown host %s\n", target);
2761558Srgrimes			exit(1);
2771558Srgrimes		}
2781558Srgrimes		to->sin_family = hp->h_addrtype;
2791558Srgrimes		bcopy(hp->h_addr, (caddr_t)&to->sin_addr, hp->h_length);
2801558Srgrimes		(void)strncpy(hnamebuf, hp->h_name, sizeof(hnamebuf) - 1);
2811558Srgrimes		hostname = hnamebuf;
2821558Srgrimes	}
2831558Srgrimes
2841558Srgrimes	if (options & F_FLOOD && options & F_INTERVAL) {
2851558Srgrimes		(void)fprintf(stderr,
2861558Srgrimes		    "ping: -f and -i incompatible options.\n");
2871558Srgrimes		exit(1);
2881558Srgrimes	}
2891558Srgrimes
2901558Srgrimes	if (datalen >= sizeof(struct timeval))	/* can we time transfer */
2911558Srgrimes		timing = 1;
2921558Srgrimes	packlen = datalen + MAXIPLEN + MAXICMPLEN;
2931558Srgrimes	if (!(packet = (u_char *)malloc((u_int)packlen))) {
2941558Srgrimes		(void)fprintf(stderr, "ping: out of memory.\n");
2951558Srgrimes		exit(1);
2961558Srgrimes	}
2971558Srgrimes	if (!(options & F_PINGFILLED))
2981558Srgrimes		for (i = 8; i < datalen; ++i)
2991558Srgrimes			*datap++ = i;
3001558Srgrimes
3011558Srgrimes	ident = getpid() & 0xFFFF;
3021558Srgrimes
30317474Sfenner	if (!proto) {
3041558Srgrimes		(void)fprintf(stderr, "ping: unknown protocol icmp.\n");
3051558Srgrimes		exit(1);
3061558Srgrimes	}
30717474Sfenner	if (s < 0) {
30817474Sfenner		errno = sockerrno;
3091558Srgrimes		perror("ping: socket");
3101558Srgrimes		exit(1);
3111558Srgrimes	}
3121558Srgrimes	hold = 1;
3131558Srgrimes	if (options & F_SO_DEBUG)
3141558Srgrimes		(void)setsockopt(s, SOL_SOCKET, SO_DEBUG, (char *)&hold,
3151558Srgrimes		    sizeof(hold));
3161558Srgrimes	if (options & F_SO_DONTROUTE)
3171558Srgrimes		(void)setsockopt(s, SOL_SOCKET, SO_DONTROUTE, (char *)&hold,
3181558Srgrimes		    sizeof(hold));
3191558Srgrimes
3201558Srgrimes	/* record route option */
3211558Srgrimes	if (options & F_RROUTE) {
3221558Srgrimes#ifdef IP_OPTIONS
3231558Srgrimes		rspace[IPOPT_OPTVAL] = IPOPT_RR;
3241558Srgrimes		rspace[IPOPT_OLEN] = sizeof(rspace)-1;
3251558Srgrimes		rspace[IPOPT_OFFSET] = IPOPT_MINOFF;
3261558Srgrimes		if (setsockopt(s, IPPROTO_IP, IP_OPTIONS, rspace,
3271558Srgrimes		    sizeof(rspace)) < 0) {
3281558Srgrimes			perror("ping: record route");
3291558Srgrimes			exit(1);
3301558Srgrimes		}
3311558Srgrimes#else
3321558Srgrimes		(void)fprintf(stderr,
3331558Srgrimes		  "ping: record route not available in this implementation.\n");
3341558Srgrimes		exit(1);
3351558Srgrimes#endif /* IP_OPTIONS */
3361558Srgrimes	}
3371558Srgrimes
3381558Srgrimes	/*
3391558Srgrimes	 * When pinging the broadcast address, you can get a lot of answers.
3401558Srgrimes	 * Doing something so evil is useful if you are trying to stress the
3411558Srgrimes	 * ethernet, or just want to fill the arp cache to get some stuff for
3421558Srgrimes	 * /etc/ethers.
3431558Srgrimes	 */
3441558Srgrimes	hold = 48 * 1024;
3451558Srgrimes	(void)setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)&hold,
3461558Srgrimes	    sizeof(hold));
3471558Srgrimes
3481558Srgrimes	if (to->sin_family == AF_INET)
3491558Srgrimes		(void)printf("PING %s (%s): %d data bytes\n", hostname,
3501558Srgrimes		    inet_ntoa(*(struct in_addr *)&to->sin_addr.s_addr),
3511558Srgrimes		    datalen);
3521558Srgrimes	else
3531558Srgrimes		(void)printf("PING %s: %d data bytes\n", hostname, datalen);
3541558Srgrimes
3551558Srgrimes	(void)signal(SIGINT, finish);
3561558Srgrimes	(void)signal(SIGALRM, catcher);
3573792Ssef	(void)signal(SIGINFO, status);
3581558Srgrimes
35919864Ssef	if (tcgetattr(STDOUT_FILENO, &ts) != -1) {
36019864Ssef		reset_kerninfo = !(ts.c_lflag & NOKERNINFO);
36119864Ssef		ts.c_lflag |= NOKERNINFO;
36219864Ssef		tcsetattr(STDOUT_FILENO, TCSANOW, &ts);
36319864Ssef	}
36419864Ssef
3651558Srgrimes	while (preload--)		/* fire off them quickies */
3661558Srgrimes		pinger();
3671558Srgrimes
3681558Srgrimes	if ((options & F_FLOOD) == 0)
3691558Srgrimes		catcher();		/* start things going */
3701558Srgrimes
3711558Srgrimes	for (;;) {
3721558Srgrimes		struct sockaddr_in from;
3731558Srgrimes		register int cc;
3741558Srgrimes		int fromlen;
3751558Srgrimes
3761558Srgrimes		if (options & F_FLOOD) {
3771558Srgrimes			pinger();
3781558Srgrimes			timeout.tv_sec = 0;
3791558Srgrimes			timeout.tv_usec = 10000;
3801558Srgrimes			fdmask = 1 << s;
3811558Srgrimes			if (select(s + 1, (fd_set *)&fdmask, (fd_set *)NULL,
3821558Srgrimes			    (fd_set *)NULL, &timeout) < 1)
3831558Srgrimes				continue;
3841558Srgrimes		}
3851558Srgrimes		fromlen = sizeof(from);
3861558Srgrimes		if ((cc = recvfrom(s, (char *)packet, packlen, 0,
3871558Srgrimes		    (struct sockaddr *)&from, &fromlen)) < 0) {
3881558Srgrimes			if (errno == EINTR)
3891558Srgrimes				continue;
3901558Srgrimes			perror("ping: recvfrom");
3911558Srgrimes			continue;
3921558Srgrimes		}
3931558Srgrimes		pr_pack((char *)packet, cc, &from);
3941558Srgrimes		if (npackets && nreceived >= npackets)
3951558Srgrimes			break;
3961558Srgrimes	}
3971558Srgrimes	finish();
3981558Srgrimes	/* NOTREACHED */
3991558Srgrimes}
4001558Srgrimes
4011558Srgrimes/*
4021558Srgrimes * catcher --
4031558Srgrimes *	This routine causes another PING to be transmitted, and then
4041558Srgrimes * schedules another SIGALRM for 1 second from now.
4051558Srgrimes *
4061558Srgrimes * bug --
4071558Srgrimes *	Our sense of time will slowly skew (i.e., packets will not be
4081558Srgrimes * launched exactly at 1-second intervals).  This does not affect the
4091558Srgrimes * quality of the delay and loss statistics.
4101558Srgrimes */
4111558Srgrimesvoid
4121558Srgrimescatcher()
4131558Srgrimes{
4141558Srgrimes	int waittime;
4151558Srgrimes
4161558Srgrimes	pinger();
4171558Srgrimes	(void)signal(SIGALRM, catcher);
4181558Srgrimes	if (!npackets || ntransmitted < npackets)
4191558Srgrimes		alarm((u_int)interval);
4201558Srgrimes	else {
4211558Srgrimes		if (nreceived) {
4221558Srgrimes			waittime = 2 * tmax / 1000;
4231558Srgrimes			if (!waittime)
4241558Srgrimes				waittime = 1;
4251558Srgrimes		} else
4261558Srgrimes			waittime = MAXWAIT;
4271558Srgrimes		(void)signal(SIGALRM, finish);
4281558Srgrimes		(void)alarm((u_int)waittime);
4291558Srgrimes	}
4301558Srgrimes}
4311558Srgrimes
4321558Srgrimes/*
4331558Srgrimes * pinger --
4341558Srgrimes *	Compose and transmit an ICMP ECHO REQUEST packet.  The IP packet
4351558Srgrimes * will be added on by the kernel.  The ID field is our UNIX process ID,
4361558Srgrimes * and the sequence number is an ascending integer.  The first 8 bytes
43717724Sfenner * of the data portion are used to hold a UNIX "timeval" struct in host
4381558Srgrimes * byte-order, to compute the round-trip time.
4391558Srgrimes */
4401558Srgrimespinger()
4411558Srgrimes{
4421558Srgrimes	register struct icmp *icp;
4431558Srgrimes	register int cc;
4441558Srgrimes	int i;
4451558Srgrimes
4461558Srgrimes	icp = (struct icmp *)outpack;
4471558Srgrimes	icp->icmp_type = ICMP_ECHO;
4481558Srgrimes	icp->icmp_code = 0;
4491558Srgrimes	icp->icmp_cksum = 0;
4501558Srgrimes	icp->icmp_seq = ntransmitted++;
4511558Srgrimes	icp->icmp_id = ident;			/* ID */
4521558Srgrimes
4531558Srgrimes	CLR(icp->icmp_seq % mx_dup_ck);
4541558Srgrimes
4551558Srgrimes	if (timing)
4561558Srgrimes		(void)gettimeofday((struct timeval *)&outpack[8],
4571558Srgrimes		    (struct timezone *)NULL);
4581558Srgrimes
4591558Srgrimes	cc = datalen + 8;			/* skips ICMP portion */
4601558Srgrimes
4611558Srgrimes	/* compute ICMP checksum here */
4621558Srgrimes	icp->icmp_cksum = in_cksum((u_short *)icp, cc);
4631558Srgrimes
4641558Srgrimes	i = sendto(s, (char *)outpack, cc, 0, &whereto,
4651558Srgrimes	    sizeof(struct sockaddr));
4661558Srgrimes
4671558Srgrimes	if (i < 0 || i != cc)  {
4681558Srgrimes		if (i < 0)
4691558Srgrimes			perror("ping: sendto");
4701558Srgrimes		(void)printf("ping: wrote %s %d chars, ret=%d\n",
4711558Srgrimes		    hostname, cc, i);
4721558Srgrimes	}
4731558Srgrimes	if (!(options & F_QUIET) && options & F_FLOOD)
4741558Srgrimes		(void)write(STDOUT_FILENO, &DOT, 1);
4751558Srgrimes}
4761558Srgrimes
4771558Srgrimes/*
4781558Srgrimes * pr_pack --
4791558Srgrimes *	Print out the packet, if it came from us.  This logic is necessary
4801558Srgrimes * because ALL readers of the ICMP socket get a copy of ALL ICMP packets
4811558Srgrimes * which arrive ('tis only fair).  This permits multiple copies of this
4821558Srgrimes * program to be run without having intermingled output (or statistics!).
4831558Srgrimes */
4841558Srgrimespr_pack(buf, cc, from)
4851558Srgrimes	char *buf;
4861558Srgrimes	int cc;
4871558Srgrimes	struct sockaddr_in *from;
4881558Srgrimes{
4891558Srgrimes	register struct icmp *icp;
4901558Srgrimes	register u_long l;
4911558Srgrimes	register int i, j;
4921558Srgrimes	register u_char *cp,*dp;
4931558Srgrimes	static int old_rrlen;
4941558Srgrimes	static char old_rr[MAX_IPOPTLEN];
4951558Srgrimes	struct ip *ip;
4961558Srgrimes	struct timeval tv, *tp;
4971558Srgrimes	double triptime;
4981558Srgrimes	int hlen, dupflag;
4991558Srgrimes
5001558Srgrimes	(void)gettimeofday(&tv, (struct timezone *)NULL);
5011558Srgrimes
5021558Srgrimes	/* Check the IP header */
5031558Srgrimes	ip = (struct ip *)buf;
5041558Srgrimes	hlen = ip->ip_hl << 2;
5051558Srgrimes	if (cc < hlen + ICMP_MINLEN) {
5061558Srgrimes		if (options & F_VERBOSE)
5071558Srgrimes			(void)fprintf(stderr,
5081558Srgrimes			  "ping: packet too short (%d bytes) from %s\n", cc,
5091558Srgrimes			  inet_ntoa(*(struct in_addr *)&from->sin_addr.s_addr));
5101558Srgrimes		return;
5111558Srgrimes	}
5121558Srgrimes
5131558Srgrimes	/* Now the ICMP part */
5141558Srgrimes	cc -= hlen;
5151558Srgrimes	icp = (struct icmp *)(buf + hlen);
5161558Srgrimes	if (icp->icmp_type == ICMP_ECHOREPLY) {
5171558Srgrimes		if (icp->icmp_id != ident)
5181558Srgrimes			return;			/* 'Twas not our ECHO */
5191558Srgrimes		++nreceived;
5201558Srgrimes		if (timing) {
5211558Srgrimes#ifndef icmp_data
5221558Srgrimes			tp = (struct timeval *)&icp->icmp_ip;
5231558Srgrimes#else
5241558Srgrimes			tp = (struct timeval *)icp->icmp_data;
5251558Srgrimes#endif
5261558Srgrimes			tvsub(&tv, tp);
5271558Srgrimes			triptime = ((double)tv.tv_sec) * 1000.0 +
5281558Srgrimes			    ((double)tv.tv_usec) / 1000.0;
5291558Srgrimes			tsum += triptime;
5301558Srgrimes			if (triptime < tmin)
5311558Srgrimes				tmin = triptime;
5321558Srgrimes			if (triptime > tmax)
5331558Srgrimes				tmax = triptime;
5341558Srgrimes		}
5351558Srgrimes
5361558Srgrimes		if (TST(icp->icmp_seq % mx_dup_ck)) {
5371558Srgrimes			++nrepeats;
5381558Srgrimes			--nreceived;
5391558Srgrimes			dupflag = 1;
5401558Srgrimes		} else {
5411558Srgrimes			SET(icp->icmp_seq % mx_dup_ck);
5421558Srgrimes			dupflag = 0;
5431558Srgrimes		}
5441558Srgrimes
5451558Srgrimes		if (options & F_QUIET)
5461558Srgrimes			return;
5471558Srgrimes
5481558Srgrimes		if (options & F_FLOOD)
5491558Srgrimes			(void)write(STDOUT_FILENO, &BSPACE, 1);
5501558Srgrimes		else {
5511558Srgrimes			(void)printf("%d bytes from %s: icmp_seq=%u", cc,
5521558Srgrimes			   inet_ntoa(*(struct in_addr *)&from->sin_addr.s_addr),
5531558Srgrimes			   icp->icmp_seq);
5541558Srgrimes			(void)printf(" ttl=%d", ip->ip_ttl);
5551558Srgrimes			if (timing)
5561859Sdg				(void)printf(" time=%.3f ms", triptime);
5571558Srgrimes			if (dupflag)
5581558Srgrimes				(void)printf(" (DUP!)");
5591558Srgrimes			/* check the data */
5601558Srgrimes			cp = (u_char*)&icp->icmp_data[8];
5611558Srgrimes			dp = &outpack[8 + sizeof(struct timeval)];
5621558Srgrimes			for (i = 8; i < datalen; ++i, ++cp, ++dp) {
5631558Srgrimes				if (*cp != *dp) {
5641558Srgrimes	(void)printf("\nwrong data byte #%d should be 0x%x but was 0x%x",
5651558Srgrimes	    i, *dp, *cp);
5661558Srgrimes					cp = (u_char*)&icp->icmp_data[0];
5671558Srgrimes					for (i = 8; i < datalen; ++i, ++cp) {
5681558Srgrimes						if ((i % 32) == 8)
5691558Srgrimes							(void)printf("\n\t");
5701558Srgrimes						(void)printf("%x ", *cp);
5711558Srgrimes					}
5721558Srgrimes					break;
5731558Srgrimes				}
5741558Srgrimes			}
5751558Srgrimes		}
5761558Srgrimes	} else {
57717724Sfenner		/*
57817724Sfenner		 * We've got something other than an ECHOREPLY.
57917724Sfenner		 * See if it's a reply to something that we sent.
58017724Sfenner		 * We can compare IP destination, protocol,
58117724Sfenner		 * and ICMP type and ID.
58217724Sfenner		 */
58317724Sfenner#ifndef icmp_data
58417724Sfenner		struct ip *oip = &icp->icmp_ip;
58517724Sfenner#else
58617724Sfenner		struct ip *oip = (struct ip *)icp->icmp_data;
58717724Sfenner#endif
58817724Sfenner		struct icmp *oicmp = (struct icmp *)(oip + 1);
58917724Sfenner
59017724Sfenner		if ((options & F_VERBOSE) ||
59117724Sfenner		    (!(options & F_QUIET2) &&
59217724Sfenner		     (oip->ip_dst.s_addr ==
59317724Sfenner			 ((struct sockaddr_in *)&whereto)->sin_addr.s_addr) &&
59417724Sfenner		     (oip->ip_p == IPPROTO_ICMP) &&
59517724Sfenner		     (oicmp->icmp_type == ICMP_ECHO) &&
59617724Sfenner		     (oicmp->icmp_id == ident))) {
59717724Sfenner		    (void)printf("%d bytes from %s: ", cc,
59817724Sfenner			pr_addr(from->sin_addr.s_addr));
59917724Sfenner		    pr_icmph(icp);
60017724Sfenner		} else
60117724Sfenner		    return;
6021558Srgrimes	}
6031558Srgrimes
6041558Srgrimes	/* Display any IP options */
6051558Srgrimes	cp = (u_char *)buf + sizeof(struct ip);
6061558Srgrimes
6071558Srgrimes	for (; hlen > (int)sizeof(struct ip); --hlen, ++cp)
6081558Srgrimes		switch (*cp) {
6091558Srgrimes		case IPOPT_EOL:
6101558Srgrimes			hlen = 0;
6111558Srgrimes			break;
6121558Srgrimes		case IPOPT_LSRR:
6131558Srgrimes			(void)printf("\nLSRR: ");
6141558Srgrimes			hlen -= 2;
6151558Srgrimes			j = *++cp;
6161558Srgrimes			++cp;
6171558Srgrimes			if (j > IPOPT_MINOFF)
6181558Srgrimes				for (;;) {
6191558Srgrimes					l = *++cp;
6201558Srgrimes					l = (l<<8) + *++cp;
6211558Srgrimes					l = (l<<8) + *++cp;
6221558Srgrimes					l = (l<<8) + *++cp;
6231558Srgrimes					if (l == 0)
6241558Srgrimes						(void)printf("\t0.0.0.0");
6251558Srgrimes				else
6261558Srgrimes					(void)printf("\t%s", pr_addr(ntohl(l)));
6271558Srgrimes				hlen -= 4;
6281558Srgrimes				j -= 4;
6291558Srgrimes				if (j <= IPOPT_MINOFF)
6301558Srgrimes					break;
6311558Srgrimes				(void)putchar('\n');
6321558Srgrimes			}
6331558Srgrimes			break;
6341558Srgrimes		case IPOPT_RR:
6351558Srgrimes			j = *++cp;		/* get length */
6361558Srgrimes			i = *++cp;		/* and pointer */
6371558Srgrimes			hlen -= 2;
6381558Srgrimes			if (i > j)
6391558Srgrimes				i = j;
6401558Srgrimes			i -= IPOPT_MINOFF;
6411558Srgrimes			if (i <= 0)
6421558Srgrimes				continue;
6431558Srgrimes			if (i == old_rrlen
6441558Srgrimes			    && cp == (u_char *)buf + sizeof(struct ip) + 2
6451558Srgrimes			    && !bcmp((char *)cp, old_rr, i)
6461558Srgrimes			    && !(options & F_FLOOD)) {
6471558Srgrimes				(void)printf("\t(same route)");
6481558Srgrimes				i = ((i + 3) / 4) * 4;
6491558Srgrimes				hlen -= i;
6501558Srgrimes				cp += i;
6511558Srgrimes				break;
6521558Srgrimes			}
6531558Srgrimes			old_rrlen = i;
6541558Srgrimes			bcopy((char *)cp, old_rr, i);
6551558Srgrimes			(void)printf("\nRR: ");
6561558Srgrimes			for (;;) {
6571558Srgrimes				l = *++cp;
6581558Srgrimes				l = (l<<8) + *++cp;
6591558Srgrimes				l = (l<<8) + *++cp;
6601558Srgrimes				l = (l<<8) + *++cp;
6611558Srgrimes				if (l == 0)
6621558Srgrimes					(void)printf("\t0.0.0.0");
6631558Srgrimes				else
6641558Srgrimes					(void)printf("\t%s", pr_addr(ntohl(l)));
6651558Srgrimes				hlen -= 4;
6661558Srgrimes				i -= 4;
6671558Srgrimes				if (i <= 0)
6681558Srgrimes					break;
6691558Srgrimes				(void)putchar('\n');
6701558Srgrimes			}
6711558Srgrimes			break;
6721558Srgrimes		case IPOPT_NOP:
6731558Srgrimes			(void)printf("\nNOP");
6741558Srgrimes			break;
6751558Srgrimes		default:
6761558Srgrimes			(void)printf("\nunknown option %x", *cp);
6771558Srgrimes			break;
6781558Srgrimes		}
6791558Srgrimes	if (!(options & F_FLOOD)) {
6801558Srgrimes		(void)putchar('\n');
6811558Srgrimes		(void)fflush(stdout);
6821558Srgrimes	}
6831558Srgrimes}
6841558Srgrimes
6851558Srgrimes/*
6861558Srgrimes * in_cksum --
6871558Srgrimes *	Checksum routine for Internet Protocol family headers (C Version)
6881558Srgrimes */
6891558Srgrimesin_cksum(addr, len)
6901558Srgrimes	u_short *addr;
6911558Srgrimes	int len;
6921558Srgrimes{
6931558Srgrimes	register int nleft = len;
6941558Srgrimes	register u_short *w = addr;
6951558Srgrimes	register int sum = 0;
6961558Srgrimes	u_short answer = 0;
6971558Srgrimes
6981558Srgrimes	/*
6991558Srgrimes	 * Our algorithm is simple, using a 32 bit accumulator (sum), we add
7001558Srgrimes	 * sequential 16 bit words to it, and at the end, fold back all the
7011558Srgrimes	 * carry bits from the top 16 bits into the lower 16 bits.
7021558Srgrimes	 */
7031558Srgrimes	while (nleft > 1)  {
7041558Srgrimes		sum += *w++;
7051558Srgrimes		nleft -= 2;
7061558Srgrimes	}
7071558Srgrimes
7081558Srgrimes	/* mop up an odd byte, if necessary */
7091558Srgrimes	if (nleft == 1) {
7101558Srgrimes		*(u_char *)(&answer) = *(u_char *)w ;
7111558Srgrimes		sum += answer;
7121558Srgrimes	}
7131558Srgrimes
7141558Srgrimes	/* add back carry outs from top 16 bits to low 16 bits */
7151558Srgrimes	sum = (sum >> 16) + (sum & 0xffff);	/* add hi 16 to low 16 */
7161558Srgrimes	sum += (sum >> 16);			/* add carry */
7171558Srgrimes	answer = ~sum;				/* truncate to 16 bits */
7181558Srgrimes	return(answer);
7191558Srgrimes}
7201558Srgrimes
7211558Srgrimes/*
7221558Srgrimes * tvsub --
7231558Srgrimes *	Subtract 2 timeval structs:  out = out - in.  Out is assumed to
7241558Srgrimes * be >= in.
7251558Srgrimes */
7261558Srgrimestvsub(out, in)
7271558Srgrimes	register struct timeval *out, *in;
7281558Srgrimes{
7291558Srgrimes	if ((out->tv_usec -= in->tv_usec) < 0) {
7301558Srgrimes		--out->tv_sec;
7311558Srgrimes		out->tv_usec += 1000000;
7321558Srgrimes	}
7331558Srgrimes	out->tv_sec -= in->tv_sec;
7341558Srgrimes}
7351558Srgrimes
7361558Srgrimes/*
7373792Ssef * status --
7383792Ssef *	Print out statistics when SIGINFO is received.
7393792Ssef */
7403792Ssef
7413792Ssefvoid
7423792Ssefstatus()
7433792Ssef{
7443792Ssef	double temp_min = nreceived ? tmin : 0;
7453792Ssef	(void)fprintf(stderr, "%ld/%ld packets received (%ld%%) "
7463792Ssef		      "%.3f min / %.3f avg / %.3f max\n",
7478871Srgrimes		      nreceived, ntransmitted,
7488871Srgrimes		      (ntransmitted ?
7493792Ssef		       100 - (int) (((ntransmitted - nreceived) * 100)
7503792Ssef				    / ntransmitted)
7513792Ssef		       : 0),
7523792Ssef		      temp_min,
7533792Ssef		      ((nreceived + nrepeats) ?
7543792Ssef		       (tsum / (nreceived + nrepeats))/1000.0
7553792Ssef		       : tsum),
7563792Ssef		      tmax/ 1000.0);
7573792Ssef}
7583792Ssef
7593792Ssef/*
7601558Srgrimes * finish --
7611558Srgrimes *	Print out statistics, and give up.
7621558Srgrimes */
7631558Srgrimesvoid
7641558Srgrimesfinish()
7651558Srgrimes{
7661558Srgrimes	register int i;
7673792Ssef	struct termios ts;
7681558Srgrimes
7691558Srgrimes	(void)signal(SIGINT, SIG_IGN);
7701558Srgrimes	(void)putchar('\n');
7711558Srgrimes	(void)fflush(stdout);
7721558Srgrimes	(void)printf("--- %s ping statistics ---\n", hostname);
7731558Srgrimes	(void)printf("%ld packets transmitted, ", ntransmitted);
7741558Srgrimes	(void)printf("%ld packets received, ", nreceived);
7751558Srgrimes	if (nrepeats)
7761558Srgrimes		(void)printf("+%ld duplicates, ", nrepeats);
7771558Srgrimes	if (ntransmitted)
7781558Srgrimes		if (nreceived > ntransmitted)
7791558Srgrimes			(void)printf("-- somebody's printing up packets!");
7801558Srgrimes		else
7811558Srgrimes			(void)printf("%d%% packet loss",
7821558Srgrimes			    (int) (((ntransmitted - nreceived) * 100) /
7831558Srgrimes			    ntransmitted));
7841558Srgrimes	(void)putchar('\n');
7851558Srgrimes	if (nreceived && timing) {
7861558Srgrimes		/* Only display average to microseconds */
7871558Srgrimes		i = 1000.0 * tsum / (nreceived + nrepeats);
7881859Sdg		(void)printf("round-trip min/avg/max = %.3f/%.3f/%.3f ms\n",
7891558Srgrimes		    tmin, ((double)i) / 1000.0, tmax);
7901558Srgrimes	}
79119395Sbde	if (reset_kerninfo && tcgetattr(STDOUT_FILENO, &ts) != -1) {
7923792Ssef		ts.c_lflag &= ~NOKERNINFO;
79319395Sbde		tcsetattr(STDOUT_FILENO, TCSANOW, &ts);
7943792Ssef	}
7953792Ssef
7968871Srgrimes	if (nreceived)
7974862Sdg		exit(0);
7984862Sdg	else
7994862Sdg		exit(2);
8001558Srgrimes}
8011558Srgrimes
8021558Srgrimes#ifdef notdef
8031558Srgrimesstatic char *ttab[] = {
8041558Srgrimes	"Echo Reply",		/* ip + seq + udata */
8051558Srgrimes	"Dest Unreachable",	/* net, host, proto, port, frag, sr + IP */
8061558Srgrimes	"Source Quench",	/* IP */
8071558Srgrimes	"Redirect",		/* redirect type, gateway, + IP  */
8081558Srgrimes	"Echo",
8091558Srgrimes	"Time Exceeded",	/* transit, frag reassem + IP */
8101558Srgrimes	"Parameter Problem",	/* pointer + IP */
8111558Srgrimes	"Timestamp",		/* id + seq + three timestamps */
8121558Srgrimes	"Timestamp Reply",	/* " */
8131558Srgrimes	"Info Request",		/* id + sq */
8141558Srgrimes	"Info Reply"		/* " */
8151558Srgrimes};
8161558Srgrimes#endif
8171558Srgrimes
8181558Srgrimes/*
8191558Srgrimes * pr_icmph --
8201558Srgrimes *	Print a descriptive string about an ICMP header.
8211558Srgrimes */
8221558Srgrimespr_icmph(icp)
8231558Srgrimes	struct icmp *icp;
8241558Srgrimes{
8251558Srgrimes	switch(icp->icmp_type) {
8261558Srgrimes	case ICMP_ECHOREPLY:
8271558Srgrimes		(void)printf("Echo Reply\n");
8281558Srgrimes		/* XXX ID + Seq + Data */
8291558Srgrimes		break;
8301558Srgrimes	case ICMP_UNREACH:
8311558Srgrimes		switch(icp->icmp_code) {
8321558Srgrimes		case ICMP_UNREACH_NET:
8331558Srgrimes			(void)printf("Destination Net Unreachable\n");
8341558Srgrimes			break;
8351558Srgrimes		case ICMP_UNREACH_HOST:
8361558Srgrimes			(void)printf("Destination Host Unreachable\n");
8371558Srgrimes			break;
8381558Srgrimes		case ICMP_UNREACH_PROTOCOL:
8391558Srgrimes			(void)printf("Destination Protocol Unreachable\n");
8401558Srgrimes			break;
8411558Srgrimes		case ICMP_UNREACH_PORT:
8421558Srgrimes			(void)printf("Destination Port Unreachable\n");
8431558Srgrimes			break;
8441558Srgrimes		case ICMP_UNREACH_NEEDFRAG:
84517724Sfenner			(void)printf("frag needed and DF set (MTU %d)\n",
84617724Sfenner					icp->icmp_nextmtu);
8471558Srgrimes			break;
8481558Srgrimes		case ICMP_UNREACH_SRCFAIL:
8491558Srgrimes			(void)printf("Source Route Failed\n");
8501558Srgrimes			break;
85117724Sfenner		case ICMP_UNREACH_FILTER_PROHIB:
85217724Sfenner			(void)printf("Communication prohibited by filter\n");
85317724Sfenner			break;
8541558Srgrimes		default:
8551558Srgrimes			(void)printf("Dest Unreachable, Bad Code: %d\n",
8561558Srgrimes			    icp->icmp_code);
8571558Srgrimes			break;
8581558Srgrimes		}
8591558Srgrimes		/* Print returned IP header information */
8601558Srgrimes#ifndef icmp_data
8611558Srgrimes		pr_retip(&icp->icmp_ip);
8621558Srgrimes#else
8631558Srgrimes		pr_retip((struct ip *)icp->icmp_data);
8641558Srgrimes#endif
8651558Srgrimes		break;
8661558Srgrimes	case ICMP_SOURCEQUENCH:
8671558Srgrimes		(void)printf("Source Quench\n");
8681558Srgrimes#ifndef icmp_data
8691558Srgrimes		pr_retip(&icp->icmp_ip);
8701558Srgrimes#else
8711558Srgrimes		pr_retip((struct ip *)icp->icmp_data);
8721558Srgrimes#endif
8731558Srgrimes		break;
8741558Srgrimes	case ICMP_REDIRECT:
8751558Srgrimes		switch(icp->icmp_code) {
8761558Srgrimes		case ICMP_REDIRECT_NET:
8771558Srgrimes			(void)printf("Redirect Network");
8781558Srgrimes			break;
8791558Srgrimes		case ICMP_REDIRECT_HOST:
8801558Srgrimes			(void)printf("Redirect Host");
8811558Srgrimes			break;
8821558Srgrimes		case ICMP_REDIRECT_TOSNET:
8831558Srgrimes			(void)printf("Redirect Type of Service and Network");
8841558Srgrimes			break;
8851558Srgrimes		case ICMP_REDIRECT_TOSHOST:
8861558Srgrimes			(void)printf("Redirect Type of Service and Host");
8871558Srgrimes			break;
8881558Srgrimes		default:
8891558Srgrimes			(void)printf("Redirect, Bad Code: %d", icp->icmp_code);
8901558Srgrimes			break;
8911558Srgrimes		}
8921558Srgrimes		(void)printf("(New addr: 0x%08lx)\n", icp->icmp_gwaddr.s_addr);
8931558Srgrimes#ifndef icmp_data
8941558Srgrimes		pr_retip(&icp->icmp_ip);
8951558Srgrimes#else
8961558Srgrimes		pr_retip((struct ip *)icp->icmp_data);
8971558Srgrimes#endif
8981558Srgrimes		break;
8991558Srgrimes	case ICMP_ECHO:
9001558Srgrimes		(void)printf("Echo Request\n");
9011558Srgrimes		/* XXX ID + Seq + Data */
9021558Srgrimes		break;
9031558Srgrimes	case ICMP_TIMXCEED:
9041558Srgrimes		switch(icp->icmp_code) {
9051558Srgrimes		case ICMP_TIMXCEED_INTRANS:
9061558Srgrimes			(void)printf("Time to live exceeded\n");
9071558Srgrimes			break;
9081558Srgrimes		case ICMP_TIMXCEED_REASS:
9091558Srgrimes			(void)printf("Frag reassembly time exceeded\n");
9101558Srgrimes			break;
9111558Srgrimes		default:
9121558Srgrimes			(void)printf("Time exceeded, Bad Code: %d\n",
9131558Srgrimes			    icp->icmp_code);
9141558Srgrimes			break;
9151558Srgrimes		}
9161558Srgrimes#ifndef icmp_data
9171558Srgrimes		pr_retip(&icp->icmp_ip);
9181558Srgrimes#else
9191558Srgrimes		pr_retip((struct ip *)icp->icmp_data);
9201558Srgrimes#endif
9211558Srgrimes		break;
9221558Srgrimes	case ICMP_PARAMPROB:
9231558Srgrimes		(void)printf("Parameter problem: pointer = 0x%02x\n",
9241558Srgrimes		    icp->icmp_hun.ih_pptr);
9251558Srgrimes#ifndef icmp_data
9261558Srgrimes		pr_retip(&icp->icmp_ip);
9271558Srgrimes#else
9281558Srgrimes		pr_retip((struct ip *)icp->icmp_data);
9291558Srgrimes#endif
9301558Srgrimes		break;
9311558Srgrimes	case ICMP_TSTAMP:
9321558Srgrimes		(void)printf("Timestamp\n");
9331558Srgrimes		/* XXX ID + Seq + 3 timestamps */
9341558Srgrimes		break;
9351558Srgrimes	case ICMP_TSTAMPREPLY:
9361558Srgrimes		(void)printf("Timestamp Reply\n");
9371558Srgrimes		/* XXX ID + Seq + 3 timestamps */
9381558Srgrimes		break;
9391558Srgrimes	case ICMP_IREQ:
9401558Srgrimes		(void)printf("Information Request\n");
9411558Srgrimes		/* XXX ID + Seq */
9421558Srgrimes		break;
9431558Srgrimes	case ICMP_IREQREPLY:
9441558Srgrimes		(void)printf("Information Reply\n");
9451558Srgrimes		/* XXX ID + Seq */
9461558Srgrimes		break;
9471558Srgrimes	case ICMP_MASKREQ:
9481558Srgrimes		(void)printf("Address Mask Request\n");
9491558Srgrimes		break;
9501558Srgrimes	case ICMP_MASKREPLY:
9511558Srgrimes		(void)printf("Address Mask Reply\n");
9521558Srgrimes		break;
95317724Sfenner	case ICMP_ROUTERADVERT:
95417724Sfenner		(void)printf("Router Advertisement\n");
95517724Sfenner		break;
95617724Sfenner	case ICMP_ROUTERSOLICIT:
95717724Sfenner		(void)printf("Router Solicitation\n");
95817724Sfenner		break;
9591558Srgrimes	default:
9601558Srgrimes		(void)printf("Bad ICMP type: %d\n", icp->icmp_type);
9611558Srgrimes	}
9621558Srgrimes}
9631558Srgrimes
9641558Srgrimes/*
9651558Srgrimes * pr_iph --
9661558Srgrimes *	Print an IP header with options.
9671558Srgrimes */
9681558Srgrimespr_iph(ip)
9691558Srgrimes	struct ip *ip;
9701558Srgrimes{
9711558Srgrimes	int hlen;
9721558Srgrimes	u_char *cp;
9731558Srgrimes
9741558Srgrimes	hlen = ip->ip_hl << 2;
9751558Srgrimes	cp = (u_char *)ip + 20;		/* point to options */
9761558Srgrimes
97717724Sfenner	(void)printf("Vr HL TOS  Len   ID Flg  off TTL Pro  cks      Src      Dst\n");
9781558Srgrimes	(void)printf(" %1x  %1x  %02x %04x %04x",
97917724Sfenner	    ip->ip_v, ip->ip_hl, ip->ip_tos, ntohs(ip->ip_len),
98017724Sfenner	    ntohs(ip->ip_id));
98117724Sfenner	(void)printf("   %1x %04x", (ntohl(ip->ip_off) & 0xe000) >> 13,
98217724Sfenner	    ntohl(ip->ip_off) & 0x1fff);
98317724Sfenner	(void)printf("  %02x  %02x %04x", ip->ip_ttl, ip->ip_p,
98417724Sfenner							    ntohs(ip->ip_sum));
9851558Srgrimes	(void)printf(" %s ", inet_ntoa(*(struct in_addr *)&ip->ip_src.s_addr));
9861558Srgrimes	(void)printf(" %s ", inet_ntoa(*(struct in_addr *)&ip->ip_dst.s_addr));
98717724Sfenner	/* dump any option bytes */
9881558Srgrimes	while (hlen-- > 20) {
9891558Srgrimes		(void)printf("%02x", *cp++);
9901558Srgrimes	}
9911558Srgrimes	(void)putchar('\n');
9921558Srgrimes}
9931558Srgrimes
9941558Srgrimes/*
9951558Srgrimes * pr_addr --
9961558Srgrimes *	Return an ascii host address as a dotted quad and optionally with
9971558Srgrimes * a hostname.
9981558Srgrimes */
9991558Srgrimeschar *
10001558Srgrimespr_addr(l)
10011558Srgrimes	u_long l;
10021558Srgrimes{
10031558Srgrimes	struct hostent *hp;
10041558Srgrimes	static char buf[80];
10051558Srgrimes
10061558Srgrimes	if ((options & F_NUMERIC) ||
10071558Srgrimes	    !(hp = gethostbyaddr((char *)&l, 4, AF_INET)))
100817320Speter		(void)snprintf(buf, sizeof(buf), "%s",
100917320Speter		    inet_ntoa(*(struct in_addr *)&l));
10101558Srgrimes	else
101117320Speter		(void)snprintf(buf, sizeof(buf), "%s (%s)", hp->h_name,
10121558Srgrimes		    inet_ntoa(*(struct in_addr *)&l));
10131558Srgrimes	return(buf);
10141558Srgrimes}
10151558Srgrimes
10161558Srgrimes/*
10171558Srgrimes * pr_retip --
10181558Srgrimes *	Dump some info on a returned (via ICMP) IP packet.
10191558Srgrimes */
10201558Srgrimespr_retip(ip)
10211558Srgrimes	struct ip *ip;
10221558Srgrimes{
10231558Srgrimes	int hlen;
10241558Srgrimes	u_char *cp;
10251558Srgrimes
10261558Srgrimes	pr_iph(ip);
10271558Srgrimes	hlen = ip->ip_hl << 2;
10281558Srgrimes	cp = (u_char *)ip + hlen;
10291558Srgrimes
10301558Srgrimes	if (ip->ip_p == 6)
10311558Srgrimes		(void)printf("TCP: from port %u, to port %u (decimal)\n",
10321558Srgrimes		    (*cp * 256 + *(cp + 1)), (*(cp + 2) * 256 + *(cp + 3)));
10331558Srgrimes	else if (ip->ip_p == 17)
10341558Srgrimes		(void)printf("UDP: from port %u, to port %u (decimal)\n",
10351558Srgrimes			(*cp * 256 + *(cp + 1)), (*(cp + 2) * 256 + *(cp + 3)));
10361558Srgrimes}
10371558Srgrimes
10381558Srgrimesfill(bp, patp)
10391558Srgrimes	char *bp, *patp;
10401558Srgrimes{
10411558Srgrimes	register int ii, jj, kk;
10421558Srgrimes	int pat[16];
10431558Srgrimes	char *cp;
10441558Srgrimes
10451558Srgrimes	for (cp = patp; *cp; cp++)
10461558Srgrimes		if (!isxdigit(*cp)) {
10471558Srgrimes			(void)fprintf(stderr,
10481558Srgrimes			    "ping: patterns must be specified as hex digits.\n");
10491558Srgrimes			exit(1);
10501558Srgrimes		}
10511558Srgrimes	ii = sscanf(patp,
10521558Srgrimes	    "%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x",
10531558Srgrimes	    &pat[0], &pat[1], &pat[2], &pat[3], &pat[4], &pat[5], &pat[6],
10541558Srgrimes	    &pat[7], &pat[8], &pat[9], &pat[10], &pat[11], &pat[12],
10551558Srgrimes	    &pat[13], &pat[14], &pat[15]);
10561558Srgrimes
10571558Srgrimes	if (ii > 0)
10581558Srgrimes		for (kk = 0;
10591558Srgrimes		    kk <= MAXPACKET - (8 + sizeof(struct timeval) + ii);
10601558Srgrimes		    kk += ii)
10611558Srgrimes			for (jj = 0; jj < ii; ++jj)
10621558Srgrimes				bp[jj + kk] = pat[jj];
10631558Srgrimes	if (!(options & F_QUIET)) {
10641558Srgrimes		(void)printf("PATTERN: 0x");
10651558Srgrimes		for (jj = 0; jj < ii; ++jj)
10661558Srgrimes			(void)printf("%02x", bp[jj] & 0xFF);
10671558Srgrimes		(void)printf("\n");
10681558Srgrimes	}
10691558Srgrimes}
10701558Srgrimes
10711558Srgrimesusage()
10721558Srgrimes{
10731558Srgrimes	(void)fprintf(stderr,
10741558Srgrimes	    "usage: ping [-Rdfnqrv] [-c count] [-i wait] [-l preload]\n\t[-p pattern] [-s packetsize] host\n");
10751558Srgrimes	exit(1);
10761558Srgrimes}
1077