ping.c revision 17724
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	if (tcgetattr (0, &ts) != -1) {
1803792Ssef		reset_kerninfo = !(ts.c_lflag & NOKERNINFO);
1813792Ssef		ts.c_lflag |= NOKERNINFO;
1823792Ssef		tcsetattr (0, TCSANOW, &ts);
1833792Ssef	}
1843792Ssef
1851558Srgrimes	datap = &outpack[8 + sizeof(struct timeval)];
18617724Sfenner	while ((ch = getopt(argc, argv, "QRc:dfh:i:l:np:qrs:v")) != EOF)
1871558Srgrimes		switch(ch) {
1881558Srgrimes		case 'c':
1891558Srgrimes			npackets = atoi(optarg);
1901558Srgrimes			if (npackets <= 0) {
1911558Srgrimes				(void)fprintf(stderr,
1921558Srgrimes				    "ping: bad number of packets to transmit.\n");
1931558Srgrimes				exit(1);
1941558Srgrimes			}
1951558Srgrimes			break;
1961558Srgrimes		case 'd':
1971558Srgrimes			options |= F_SO_DEBUG;
1981558Srgrimes			break;
1991558Srgrimes		case 'f':
2001558Srgrimes			if (getuid()) {
2011558Srgrimes				(void)fprintf(stderr,
2021558Srgrimes				    "ping: %s\n", strerror(EPERM));
2031558Srgrimes				exit(1);
2041558Srgrimes			}
2051558Srgrimes			options |= F_FLOOD;
2061558Srgrimes			setbuf(stdout, (char *)NULL);
2071558Srgrimes			break;
2081558Srgrimes		case 'i':		/* wait between sending packets */
2091558Srgrimes			interval = atoi(optarg);
2101558Srgrimes			if (interval <= 0) {
2111558Srgrimes				(void)fprintf(stderr,
2121558Srgrimes				    "ping: bad timing interval.\n");
2131558Srgrimes				exit(1);
2141558Srgrimes			}
2151558Srgrimes			options |= F_INTERVAL;
2161558Srgrimes			break;
2171558Srgrimes		case 'l':
2181558Srgrimes			preload = atoi(optarg);
2191558Srgrimes			if (preload < 0) {
2201558Srgrimes				(void)fprintf(stderr,
2211558Srgrimes				    "ping: bad preload value.\n");
2221558Srgrimes				exit(1);
2231558Srgrimes			}
2241558Srgrimes			break;
2251558Srgrimes		case 'n':
2261558Srgrimes			options |= F_NUMERIC;
2271558Srgrimes			break;
2281558Srgrimes		case 'p':		/* fill buffer with user pattern */
2291558Srgrimes			options |= F_PINGFILLED;
2301558Srgrimes			fill((char *)datap, optarg);
2311558Srgrimes				break;
23217724Sfenner		case 'Q':
23317724Sfenner			options |= F_QUIET2;
23417724Sfenner			break;
2351558Srgrimes		case 'q':
2361558Srgrimes			options |= F_QUIET;
2371558Srgrimes			break;
2381558Srgrimes		case 'R':
2391558Srgrimes			options |= F_RROUTE;
2401558Srgrimes			break;
2411558Srgrimes		case 'r':
2421558Srgrimes			options |= F_SO_DONTROUTE;
2431558Srgrimes			break;
2441558Srgrimes		case 's':		/* size of packet to send */
2451558Srgrimes			datalen = atoi(optarg);
2461558Srgrimes			if (datalen > MAXPACKET) {
2471558Srgrimes				(void)fprintf(stderr,
2481558Srgrimes				    "ping: packet size too large.\n");
2491558Srgrimes				exit(1);
2501558Srgrimes			}
2511558Srgrimes			if (datalen <= 0) {
2521558Srgrimes				(void)fprintf(stderr,
2531558Srgrimes				    "ping: illegal packet size.\n");
2541558Srgrimes				exit(1);
2551558Srgrimes			}
2561558Srgrimes			break;
2571558Srgrimes		case 'v':
2581558Srgrimes			options |= F_VERBOSE;
2591558Srgrimes			break;
2601558Srgrimes		default:
2611558Srgrimes			usage();
2621558Srgrimes		}
2631558Srgrimes	argc -= optind;
2641558Srgrimes	argv += optind;
2651558Srgrimes
2661558Srgrimes	if (argc != 1)
2671558Srgrimes		usage();
2681558Srgrimes	target = *argv;
2691558Srgrimes
2701558Srgrimes	bzero((char *)&whereto, sizeof(struct sockaddr));
2711558Srgrimes	to = (struct sockaddr_in *)&whereto;
2721558Srgrimes	to->sin_family = AF_INET;
2731558Srgrimes	to->sin_addr.s_addr = inet_addr(target);
2741558Srgrimes	if (to->sin_addr.s_addr != (u_int)-1)
2751558Srgrimes		hostname = target;
2761558Srgrimes	else {
2771558Srgrimes		hp = gethostbyname(target);
2781558Srgrimes		if (!hp) {
2791558Srgrimes			(void)fprintf(stderr,
2801558Srgrimes			    "ping: unknown host %s\n", target);
2811558Srgrimes			exit(1);
2821558Srgrimes		}
2831558Srgrimes		to->sin_family = hp->h_addrtype;
2841558Srgrimes		bcopy(hp->h_addr, (caddr_t)&to->sin_addr, hp->h_length);
2851558Srgrimes		(void)strncpy(hnamebuf, hp->h_name, sizeof(hnamebuf) - 1);
2861558Srgrimes		hostname = hnamebuf;
2871558Srgrimes	}
2881558Srgrimes
2891558Srgrimes	if (options & F_FLOOD && options & F_INTERVAL) {
2901558Srgrimes		(void)fprintf(stderr,
2911558Srgrimes		    "ping: -f and -i incompatible options.\n");
2921558Srgrimes		exit(1);
2931558Srgrimes	}
2941558Srgrimes
2951558Srgrimes	if (datalen >= sizeof(struct timeval))	/* can we time transfer */
2961558Srgrimes		timing = 1;
2971558Srgrimes	packlen = datalen + MAXIPLEN + MAXICMPLEN;
2981558Srgrimes	if (!(packet = (u_char *)malloc((u_int)packlen))) {
2991558Srgrimes		(void)fprintf(stderr, "ping: out of memory.\n");
3001558Srgrimes		exit(1);
3011558Srgrimes	}
3021558Srgrimes	if (!(options & F_PINGFILLED))
3031558Srgrimes		for (i = 8; i < datalen; ++i)
3041558Srgrimes			*datap++ = i;
3051558Srgrimes
3061558Srgrimes	ident = getpid() & 0xFFFF;
3071558Srgrimes
30817474Sfenner	if (!proto) {
3091558Srgrimes		(void)fprintf(stderr, "ping: unknown protocol icmp.\n");
3101558Srgrimes		exit(1);
3111558Srgrimes	}
31217474Sfenner	if (s < 0) {
31317474Sfenner		errno = sockerrno;
3141558Srgrimes		perror("ping: socket");
3151558Srgrimes		exit(1);
3161558Srgrimes	}
3171558Srgrimes	hold = 1;
3181558Srgrimes	if (options & F_SO_DEBUG)
3191558Srgrimes		(void)setsockopt(s, SOL_SOCKET, SO_DEBUG, (char *)&hold,
3201558Srgrimes		    sizeof(hold));
3211558Srgrimes	if (options & F_SO_DONTROUTE)
3221558Srgrimes		(void)setsockopt(s, SOL_SOCKET, SO_DONTROUTE, (char *)&hold,
3231558Srgrimes		    sizeof(hold));
3241558Srgrimes
3251558Srgrimes	/* record route option */
3261558Srgrimes	if (options & F_RROUTE) {
3271558Srgrimes#ifdef IP_OPTIONS
3281558Srgrimes		rspace[IPOPT_OPTVAL] = IPOPT_RR;
3291558Srgrimes		rspace[IPOPT_OLEN] = sizeof(rspace)-1;
3301558Srgrimes		rspace[IPOPT_OFFSET] = IPOPT_MINOFF;
3311558Srgrimes		if (setsockopt(s, IPPROTO_IP, IP_OPTIONS, rspace,
3321558Srgrimes		    sizeof(rspace)) < 0) {
3331558Srgrimes			perror("ping: record route");
3341558Srgrimes			exit(1);
3351558Srgrimes		}
3361558Srgrimes#else
3371558Srgrimes		(void)fprintf(stderr,
3381558Srgrimes		  "ping: record route not available in this implementation.\n");
3391558Srgrimes		exit(1);
3401558Srgrimes#endif /* IP_OPTIONS */
3411558Srgrimes	}
3421558Srgrimes
3431558Srgrimes	/*
3441558Srgrimes	 * When pinging the broadcast address, you can get a lot of answers.
3451558Srgrimes	 * Doing something so evil is useful if you are trying to stress the
3461558Srgrimes	 * ethernet, or just want to fill the arp cache to get some stuff for
3471558Srgrimes	 * /etc/ethers.
3481558Srgrimes	 */
3491558Srgrimes	hold = 48 * 1024;
3501558Srgrimes	(void)setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)&hold,
3511558Srgrimes	    sizeof(hold));
3521558Srgrimes
3531558Srgrimes	if (to->sin_family == AF_INET)
3541558Srgrimes		(void)printf("PING %s (%s): %d data bytes\n", hostname,
3551558Srgrimes		    inet_ntoa(*(struct in_addr *)&to->sin_addr.s_addr),
3561558Srgrimes		    datalen);
3571558Srgrimes	else
3581558Srgrimes		(void)printf("PING %s: %d data bytes\n", hostname, datalen);
3591558Srgrimes
3601558Srgrimes	(void)signal(SIGINT, finish);
3611558Srgrimes	(void)signal(SIGALRM, catcher);
3623792Ssef	(void)signal(SIGINFO, status);
3631558Srgrimes
3641558Srgrimes	while (preload--)		/* fire off them quickies */
3651558Srgrimes		pinger();
3661558Srgrimes
3671558Srgrimes	if ((options & F_FLOOD) == 0)
3681558Srgrimes		catcher();		/* start things going */
3691558Srgrimes
3701558Srgrimes	for (;;) {
3711558Srgrimes		struct sockaddr_in from;
3721558Srgrimes		register int cc;
3731558Srgrimes		int fromlen;
3741558Srgrimes
3751558Srgrimes		if (options & F_FLOOD) {
3761558Srgrimes			pinger();
3771558Srgrimes			timeout.tv_sec = 0;
3781558Srgrimes			timeout.tv_usec = 10000;
3791558Srgrimes			fdmask = 1 << s;
3801558Srgrimes			if (select(s + 1, (fd_set *)&fdmask, (fd_set *)NULL,
3811558Srgrimes			    (fd_set *)NULL, &timeout) < 1)
3821558Srgrimes				continue;
3831558Srgrimes		}
3841558Srgrimes		fromlen = sizeof(from);
3851558Srgrimes		if ((cc = recvfrom(s, (char *)packet, packlen, 0,
3861558Srgrimes		    (struct sockaddr *)&from, &fromlen)) < 0) {
3871558Srgrimes			if (errno == EINTR)
3881558Srgrimes				continue;
3891558Srgrimes			perror("ping: recvfrom");
3901558Srgrimes			continue;
3911558Srgrimes		}
3921558Srgrimes		pr_pack((char *)packet, cc, &from);
3931558Srgrimes		if (npackets && nreceived >= npackets)
3941558Srgrimes			break;
3951558Srgrimes	}
3961558Srgrimes	finish();
3971558Srgrimes	/* NOTREACHED */
3981558Srgrimes}
3991558Srgrimes
4001558Srgrimes/*
4011558Srgrimes * catcher --
4021558Srgrimes *	This routine causes another PING to be transmitted, and then
4031558Srgrimes * schedules another SIGALRM for 1 second from now.
4041558Srgrimes *
4051558Srgrimes * bug --
4061558Srgrimes *	Our sense of time will slowly skew (i.e., packets will not be
4071558Srgrimes * launched exactly at 1-second intervals).  This does not affect the
4081558Srgrimes * quality of the delay and loss statistics.
4091558Srgrimes */
4101558Srgrimesvoid
4111558Srgrimescatcher()
4121558Srgrimes{
4131558Srgrimes	int waittime;
4141558Srgrimes
4151558Srgrimes	pinger();
4161558Srgrimes	(void)signal(SIGALRM, catcher);
4171558Srgrimes	if (!npackets || ntransmitted < npackets)
4181558Srgrimes		alarm((u_int)interval);
4191558Srgrimes	else {
4201558Srgrimes		if (nreceived) {
4211558Srgrimes			waittime = 2 * tmax / 1000;
4221558Srgrimes			if (!waittime)
4231558Srgrimes				waittime = 1;
4241558Srgrimes		} else
4251558Srgrimes			waittime = MAXWAIT;
4261558Srgrimes		(void)signal(SIGALRM, finish);
4271558Srgrimes		(void)alarm((u_int)waittime);
4281558Srgrimes	}
4291558Srgrimes}
4301558Srgrimes
4311558Srgrimes/*
4321558Srgrimes * pinger --
4331558Srgrimes *	Compose and transmit an ICMP ECHO REQUEST packet.  The IP packet
4341558Srgrimes * will be added on by the kernel.  The ID field is our UNIX process ID,
4351558Srgrimes * and the sequence number is an ascending integer.  The first 8 bytes
43617724Sfenner * of the data portion are used to hold a UNIX "timeval" struct in host
4371558Srgrimes * byte-order, to compute the round-trip time.
4381558Srgrimes */
4391558Srgrimespinger()
4401558Srgrimes{
4411558Srgrimes	register struct icmp *icp;
4421558Srgrimes	register int cc;
4431558Srgrimes	int i;
4441558Srgrimes
4451558Srgrimes	icp = (struct icmp *)outpack;
4461558Srgrimes	icp->icmp_type = ICMP_ECHO;
4471558Srgrimes	icp->icmp_code = 0;
4481558Srgrimes	icp->icmp_cksum = 0;
4491558Srgrimes	icp->icmp_seq = ntransmitted++;
4501558Srgrimes	icp->icmp_id = ident;			/* ID */
4511558Srgrimes
4521558Srgrimes	CLR(icp->icmp_seq % mx_dup_ck);
4531558Srgrimes
4541558Srgrimes	if (timing)
4551558Srgrimes		(void)gettimeofday((struct timeval *)&outpack[8],
4561558Srgrimes		    (struct timezone *)NULL);
4571558Srgrimes
4581558Srgrimes	cc = datalen + 8;			/* skips ICMP portion */
4591558Srgrimes
4601558Srgrimes	/* compute ICMP checksum here */
4611558Srgrimes	icp->icmp_cksum = in_cksum((u_short *)icp, cc);
4621558Srgrimes
4631558Srgrimes	i = sendto(s, (char *)outpack, cc, 0, &whereto,
4641558Srgrimes	    sizeof(struct sockaddr));
4651558Srgrimes
4661558Srgrimes	if (i < 0 || i != cc)  {
4671558Srgrimes		if (i < 0)
4681558Srgrimes			perror("ping: sendto");
4691558Srgrimes		(void)printf("ping: wrote %s %d chars, ret=%d\n",
4701558Srgrimes		    hostname, cc, i);
4711558Srgrimes	}
4721558Srgrimes	if (!(options & F_QUIET) && options & F_FLOOD)
4731558Srgrimes		(void)write(STDOUT_FILENO, &DOT, 1);
4741558Srgrimes}
4751558Srgrimes
4761558Srgrimes/*
4771558Srgrimes * pr_pack --
4781558Srgrimes *	Print out the packet, if it came from us.  This logic is necessary
4791558Srgrimes * because ALL readers of the ICMP socket get a copy of ALL ICMP packets
4801558Srgrimes * which arrive ('tis only fair).  This permits multiple copies of this
4811558Srgrimes * program to be run without having intermingled output (or statistics!).
4821558Srgrimes */
4831558Srgrimespr_pack(buf, cc, from)
4841558Srgrimes	char *buf;
4851558Srgrimes	int cc;
4861558Srgrimes	struct sockaddr_in *from;
4871558Srgrimes{
4881558Srgrimes	register struct icmp *icp;
4891558Srgrimes	register u_long l;
4901558Srgrimes	register int i, j;
4911558Srgrimes	register u_char *cp,*dp;
4921558Srgrimes	static int old_rrlen;
4931558Srgrimes	static char old_rr[MAX_IPOPTLEN];
4941558Srgrimes	struct ip *ip;
4951558Srgrimes	struct timeval tv, *tp;
4961558Srgrimes	double triptime;
4971558Srgrimes	int hlen, dupflag;
4981558Srgrimes
4991558Srgrimes	(void)gettimeofday(&tv, (struct timezone *)NULL);
5001558Srgrimes
5011558Srgrimes	/* Check the IP header */
5021558Srgrimes	ip = (struct ip *)buf;
5031558Srgrimes	hlen = ip->ip_hl << 2;
5041558Srgrimes	if (cc < hlen + ICMP_MINLEN) {
5051558Srgrimes		if (options & F_VERBOSE)
5061558Srgrimes			(void)fprintf(stderr,
5071558Srgrimes			  "ping: packet too short (%d bytes) from %s\n", cc,
5081558Srgrimes			  inet_ntoa(*(struct in_addr *)&from->sin_addr.s_addr));
5091558Srgrimes		return;
5101558Srgrimes	}
5111558Srgrimes
5121558Srgrimes	/* Now the ICMP part */
5131558Srgrimes	cc -= hlen;
5141558Srgrimes	icp = (struct icmp *)(buf + hlen);
5151558Srgrimes	if (icp->icmp_type == ICMP_ECHOREPLY) {
5161558Srgrimes		if (icp->icmp_id != ident)
5171558Srgrimes			return;			/* 'Twas not our ECHO */
5181558Srgrimes		++nreceived;
5191558Srgrimes		if (timing) {
5201558Srgrimes#ifndef icmp_data
5211558Srgrimes			tp = (struct timeval *)&icp->icmp_ip;
5221558Srgrimes#else
5231558Srgrimes			tp = (struct timeval *)icp->icmp_data;
5241558Srgrimes#endif
5251558Srgrimes			tvsub(&tv, tp);
5261558Srgrimes			triptime = ((double)tv.tv_sec) * 1000.0 +
5271558Srgrimes			    ((double)tv.tv_usec) / 1000.0;
5281558Srgrimes			tsum += triptime;
5291558Srgrimes			if (triptime < tmin)
5301558Srgrimes				tmin = triptime;
5311558Srgrimes			if (triptime > tmax)
5321558Srgrimes				tmax = triptime;
5331558Srgrimes		}
5341558Srgrimes
5351558Srgrimes		if (TST(icp->icmp_seq % mx_dup_ck)) {
5361558Srgrimes			++nrepeats;
5371558Srgrimes			--nreceived;
5381558Srgrimes			dupflag = 1;
5391558Srgrimes		} else {
5401558Srgrimes			SET(icp->icmp_seq % mx_dup_ck);
5411558Srgrimes			dupflag = 0;
5421558Srgrimes		}
5431558Srgrimes
5441558Srgrimes		if (options & F_QUIET)
5451558Srgrimes			return;
5461558Srgrimes
5471558Srgrimes		if (options & F_FLOOD)
5481558Srgrimes			(void)write(STDOUT_FILENO, &BSPACE, 1);
5491558Srgrimes		else {
5501558Srgrimes			(void)printf("%d bytes from %s: icmp_seq=%u", cc,
5511558Srgrimes			   inet_ntoa(*(struct in_addr *)&from->sin_addr.s_addr),
5521558Srgrimes			   icp->icmp_seq);
5531558Srgrimes			(void)printf(" ttl=%d", ip->ip_ttl);
5541558Srgrimes			if (timing)
5551859Sdg				(void)printf(" time=%.3f ms", triptime);
5561558Srgrimes			if (dupflag)
5571558Srgrimes				(void)printf(" (DUP!)");
5581558Srgrimes			/* check the data */
5591558Srgrimes			cp = (u_char*)&icp->icmp_data[8];
5601558Srgrimes			dp = &outpack[8 + sizeof(struct timeval)];
5611558Srgrimes			for (i = 8; i < datalen; ++i, ++cp, ++dp) {
5621558Srgrimes				if (*cp != *dp) {
5631558Srgrimes	(void)printf("\nwrong data byte #%d should be 0x%x but was 0x%x",
5641558Srgrimes	    i, *dp, *cp);
5651558Srgrimes					cp = (u_char*)&icp->icmp_data[0];
5661558Srgrimes					for (i = 8; i < datalen; ++i, ++cp) {
5671558Srgrimes						if ((i % 32) == 8)
5681558Srgrimes							(void)printf("\n\t");
5691558Srgrimes						(void)printf("%x ", *cp);
5701558Srgrimes					}
5711558Srgrimes					break;
5721558Srgrimes				}
5731558Srgrimes			}
5741558Srgrimes		}
5751558Srgrimes	} else {
57617724Sfenner		/*
57717724Sfenner		 * We've got something other than an ECHOREPLY.
57817724Sfenner		 * See if it's a reply to something that we sent.
57917724Sfenner		 * We can compare IP destination, protocol,
58017724Sfenner		 * and ICMP type and ID.
58117724Sfenner		 */
58217724Sfenner#ifndef icmp_data
58317724Sfenner		struct ip *oip = &icp->icmp_ip;
58417724Sfenner#else
58517724Sfenner		struct ip *oip = (struct ip *)icp->icmp_data;
58617724Sfenner#endif
58717724Sfenner		struct icmp *oicmp = (struct icmp *)(oip + 1);
58817724Sfenner
58917724Sfenner		if ((options & F_VERBOSE) ||
59017724Sfenner		    (!(options & F_QUIET2) &&
59117724Sfenner		     (oip->ip_dst.s_addr ==
59217724Sfenner			 ((struct sockaddr_in *)&whereto)->sin_addr.s_addr) &&
59317724Sfenner		     (oip->ip_p == IPPROTO_ICMP) &&
59417724Sfenner		     (oicmp->icmp_type == ICMP_ECHO) &&
59517724Sfenner		     (oicmp->icmp_id == ident))) {
59617724Sfenner		    (void)printf("%d bytes from %s: ", cc,
59717724Sfenner			pr_addr(from->sin_addr.s_addr));
59817724Sfenner		    pr_icmph(icp);
59917724Sfenner		} else
60017724Sfenner		    return;
6011558Srgrimes	}
6021558Srgrimes
6031558Srgrimes	/* Display any IP options */
6041558Srgrimes	cp = (u_char *)buf + sizeof(struct ip);
6051558Srgrimes
6061558Srgrimes	for (; hlen > (int)sizeof(struct ip); --hlen, ++cp)
6071558Srgrimes		switch (*cp) {
6081558Srgrimes		case IPOPT_EOL:
6091558Srgrimes			hlen = 0;
6101558Srgrimes			break;
6111558Srgrimes		case IPOPT_LSRR:
6121558Srgrimes			(void)printf("\nLSRR: ");
6131558Srgrimes			hlen -= 2;
6141558Srgrimes			j = *++cp;
6151558Srgrimes			++cp;
6161558Srgrimes			if (j > IPOPT_MINOFF)
6171558Srgrimes				for (;;) {
6181558Srgrimes					l = *++cp;
6191558Srgrimes					l = (l<<8) + *++cp;
6201558Srgrimes					l = (l<<8) + *++cp;
6211558Srgrimes					l = (l<<8) + *++cp;
6221558Srgrimes					if (l == 0)
6231558Srgrimes						(void)printf("\t0.0.0.0");
6241558Srgrimes				else
6251558Srgrimes					(void)printf("\t%s", pr_addr(ntohl(l)));
6261558Srgrimes				hlen -= 4;
6271558Srgrimes				j -= 4;
6281558Srgrimes				if (j <= IPOPT_MINOFF)
6291558Srgrimes					break;
6301558Srgrimes				(void)putchar('\n');
6311558Srgrimes			}
6321558Srgrimes			break;
6331558Srgrimes		case IPOPT_RR:
6341558Srgrimes			j = *++cp;		/* get length */
6351558Srgrimes			i = *++cp;		/* and pointer */
6361558Srgrimes			hlen -= 2;
6371558Srgrimes			if (i > j)
6381558Srgrimes				i = j;
6391558Srgrimes			i -= IPOPT_MINOFF;
6401558Srgrimes			if (i <= 0)
6411558Srgrimes				continue;
6421558Srgrimes			if (i == old_rrlen
6431558Srgrimes			    && cp == (u_char *)buf + sizeof(struct ip) + 2
6441558Srgrimes			    && !bcmp((char *)cp, old_rr, i)
6451558Srgrimes			    && !(options & F_FLOOD)) {
6461558Srgrimes				(void)printf("\t(same route)");
6471558Srgrimes				i = ((i + 3) / 4) * 4;
6481558Srgrimes				hlen -= i;
6491558Srgrimes				cp += i;
6501558Srgrimes				break;
6511558Srgrimes			}
6521558Srgrimes			old_rrlen = i;
6531558Srgrimes			bcopy((char *)cp, old_rr, i);
6541558Srgrimes			(void)printf("\nRR: ");
6551558Srgrimes			for (;;) {
6561558Srgrimes				l = *++cp;
6571558Srgrimes				l = (l<<8) + *++cp;
6581558Srgrimes				l = (l<<8) + *++cp;
6591558Srgrimes				l = (l<<8) + *++cp;
6601558Srgrimes				if (l == 0)
6611558Srgrimes					(void)printf("\t0.0.0.0");
6621558Srgrimes				else
6631558Srgrimes					(void)printf("\t%s", pr_addr(ntohl(l)));
6641558Srgrimes				hlen -= 4;
6651558Srgrimes				i -= 4;
6661558Srgrimes				if (i <= 0)
6671558Srgrimes					break;
6681558Srgrimes				(void)putchar('\n');
6691558Srgrimes			}
6701558Srgrimes			break;
6711558Srgrimes		case IPOPT_NOP:
6721558Srgrimes			(void)printf("\nNOP");
6731558Srgrimes			break;
6741558Srgrimes		default:
6751558Srgrimes			(void)printf("\nunknown option %x", *cp);
6761558Srgrimes			break;
6771558Srgrimes		}
6781558Srgrimes	if (!(options & F_FLOOD)) {
6791558Srgrimes		(void)putchar('\n');
6801558Srgrimes		(void)fflush(stdout);
6811558Srgrimes	}
6821558Srgrimes}
6831558Srgrimes
6841558Srgrimes/*
6851558Srgrimes * in_cksum --
6861558Srgrimes *	Checksum routine for Internet Protocol family headers (C Version)
6871558Srgrimes */
6881558Srgrimesin_cksum(addr, len)
6891558Srgrimes	u_short *addr;
6901558Srgrimes	int len;
6911558Srgrimes{
6921558Srgrimes	register int nleft = len;
6931558Srgrimes	register u_short *w = addr;
6941558Srgrimes	register int sum = 0;
6951558Srgrimes	u_short answer = 0;
6961558Srgrimes
6971558Srgrimes	/*
6981558Srgrimes	 * Our algorithm is simple, using a 32 bit accumulator (sum), we add
6991558Srgrimes	 * sequential 16 bit words to it, and at the end, fold back all the
7001558Srgrimes	 * carry bits from the top 16 bits into the lower 16 bits.
7011558Srgrimes	 */
7021558Srgrimes	while (nleft > 1)  {
7031558Srgrimes		sum += *w++;
7041558Srgrimes		nleft -= 2;
7051558Srgrimes	}
7061558Srgrimes
7071558Srgrimes	/* mop up an odd byte, if necessary */
7081558Srgrimes	if (nleft == 1) {
7091558Srgrimes		*(u_char *)(&answer) = *(u_char *)w ;
7101558Srgrimes		sum += answer;
7111558Srgrimes	}
7121558Srgrimes
7131558Srgrimes	/* add back carry outs from top 16 bits to low 16 bits */
7141558Srgrimes	sum = (sum >> 16) + (sum & 0xffff);	/* add hi 16 to low 16 */
7151558Srgrimes	sum += (sum >> 16);			/* add carry */
7161558Srgrimes	answer = ~sum;				/* truncate to 16 bits */
7171558Srgrimes	return(answer);
7181558Srgrimes}
7191558Srgrimes
7201558Srgrimes/*
7211558Srgrimes * tvsub --
7221558Srgrimes *	Subtract 2 timeval structs:  out = out - in.  Out is assumed to
7231558Srgrimes * be >= in.
7241558Srgrimes */
7251558Srgrimestvsub(out, in)
7261558Srgrimes	register struct timeval *out, *in;
7271558Srgrimes{
7281558Srgrimes	if ((out->tv_usec -= in->tv_usec) < 0) {
7291558Srgrimes		--out->tv_sec;
7301558Srgrimes		out->tv_usec += 1000000;
7311558Srgrimes	}
7321558Srgrimes	out->tv_sec -= in->tv_sec;
7331558Srgrimes}
7341558Srgrimes
7351558Srgrimes/*
7363792Ssef * status --
7373792Ssef *	Print out statistics when SIGINFO is received.
7383792Ssef */
7393792Ssef
7403792Ssefvoid
7413792Ssefstatus()
7423792Ssef{
7433792Ssef	double temp_min = nreceived ? tmin : 0;
7443792Ssef	(void)fprintf(stderr, "%ld/%ld packets received (%ld%%) "
7453792Ssef		      "%.3f min / %.3f avg / %.3f max\n",
7468871Srgrimes		      nreceived, ntransmitted,
7478871Srgrimes		      (ntransmitted ?
7483792Ssef		       100 - (int) (((ntransmitted - nreceived) * 100)
7493792Ssef				    / ntransmitted)
7503792Ssef		       : 0),
7513792Ssef		      temp_min,
7523792Ssef		      ((nreceived + nrepeats) ?
7533792Ssef		       (tsum / (nreceived + nrepeats))/1000.0
7543792Ssef		       : tsum),
7553792Ssef		      tmax/ 1000.0);
7563792Ssef}
7573792Ssef
7583792Ssef/*
7591558Srgrimes * finish --
7601558Srgrimes *	Print out statistics, and give up.
7611558Srgrimes */
7621558Srgrimesvoid
7631558Srgrimesfinish()
7641558Srgrimes{
7651558Srgrimes	register int i;
7663792Ssef	struct termios ts;
7671558Srgrimes
7681558Srgrimes	(void)signal(SIGINT, SIG_IGN);
7691558Srgrimes	(void)putchar('\n');
7701558Srgrimes	(void)fflush(stdout);
7711558Srgrimes	(void)printf("--- %s ping statistics ---\n", hostname);
7721558Srgrimes	(void)printf("%ld packets transmitted, ", ntransmitted);
7731558Srgrimes	(void)printf("%ld packets received, ", nreceived);
7741558Srgrimes	if (nrepeats)
7751558Srgrimes		(void)printf("+%ld duplicates, ", nrepeats);
7761558Srgrimes	if (ntransmitted)
7771558Srgrimes		if (nreceived > ntransmitted)
7781558Srgrimes			(void)printf("-- somebody's printing up packets!");
7791558Srgrimes		else
7801558Srgrimes			(void)printf("%d%% packet loss",
7811558Srgrimes			    (int) (((ntransmitted - nreceived) * 100) /
7821558Srgrimes			    ntransmitted));
7831558Srgrimes	(void)putchar('\n');
7841558Srgrimes	if (nreceived && timing) {
7851558Srgrimes		/* Only display average to microseconds */
7861558Srgrimes		i = 1000.0 * tsum / (nreceived + nrepeats);
7871859Sdg		(void)printf("round-trip min/avg/max = %.3f/%.3f/%.3f ms\n",
7881558Srgrimes		    tmin, ((double)i) / 1000.0, tmax);
7891558Srgrimes	}
7903792Ssef	if (reset_kerninfo && tcgetattr (0, &ts) != -1) {
7913792Ssef		ts.c_lflag &= ~NOKERNINFO;
7923792Ssef		tcsetattr (0, TCSANOW, &ts);
7933792Ssef	}
7943792Ssef
7958871Srgrimes	if (nreceived)
7964862Sdg		exit(0);
7974862Sdg	else
7984862Sdg		exit(2);
7991558Srgrimes}
8001558Srgrimes
8011558Srgrimes#ifdef notdef
8021558Srgrimesstatic char *ttab[] = {
8031558Srgrimes	"Echo Reply",		/* ip + seq + udata */
8041558Srgrimes	"Dest Unreachable",	/* net, host, proto, port, frag, sr + IP */
8051558Srgrimes	"Source Quench",	/* IP */
8061558Srgrimes	"Redirect",		/* redirect type, gateway, + IP  */
8071558Srgrimes	"Echo",
8081558Srgrimes	"Time Exceeded",	/* transit, frag reassem + IP */
8091558Srgrimes	"Parameter Problem",	/* pointer + IP */
8101558Srgrimes	"Timestamp",		/* id + seq + three timestamps */
8111558Srgrimes	"Timestamp Reply",	/* " */
8121558Srgrimes	"Info Request",		/* id + sq */
8131558Srgrimes	"Info Reply"		/* " */
8141558Srgrimes};
8151558Srgrimes#endif
8161558Srgrimes
8171558Srgrimes/*
8181558Srgrimes * pr_icmph --
8191558Srgrimes *	Print a descriptive string about an ICMP header.
8201558Srgrimes */
8211558Srgrimespr_icmph(icp)
8221558Srgrimes	struct icmp *icp;
8231558Srgrimes{
8241558Srgrimes	switch(icp->icmp_type) {
8251558Srgrimes	case ICMP_ECHOREPLY:
8261558Srgrimes		(void)printf("Echo Reply\n");
8271558Srgrimes		/* XXX ID + Seq + Data */
8281558Srgrimes		break;
8291558Srgrimes	case ICMP_UNREACH:
8301558Srgrimes		switch(icp->icmp_code) {
8311558Srgrimes		case ICMP_UNREACH_NET:
8321558Srgrimes			(void)printf("Destination Net Unreachable\n");
8331558Srgrimes			break;
8341558Srgrimes		case ICMP_UNREACH_HOST:
8351558Srgrimes			(void)printf("Destination Host Unreachable\n");
8361558Srgrimes			break;
8371558Srgrimes		case ICMP_UNREACH_PROTOCOL:
8381558Srgrimes			(void)printf("Destination Protocol Unreachable\n");
8391558Srgrimes			break;
8401558Srgrimes		case ICMP_UNREACH_PORT:
8411558Srgrimes			(void)printf("Destination Port Unreachable\n");
8421558Srgrimes			break;
8431558Srgrimes		case ICMP_UNREACH_NEEDFRAG:
84417724Sfenner			(void)printf("frag needed and DF set (MTU %d)\n",
84517724Sfenner					icp->icmp_nextmtu);
8461558Srgrimes			break;
8471558Srgrimes		case ICMP_UNREACH_SRCFAIL:
8481558Srgrimes			(void)printf("Source Route Failed\n");
8491558Srgrimes			break;
85017724Sfenner		case ICMP_UNREACH_FILTER_PROHIB:
85117724Sfenner			(void)printf("Communication prohibited by filter\n");
85217724Sfenner			break;
8531558Srgrimes		default:
8541558Srgrimes			(void)printf("Dest Unreachable, Bad Code: %d\n",
8551558Srgrimes			    icp->icmp_code);
8561558Srgrimes			break;
8571558Srgrimes		}
8581558Srgrimes		/* Print returned IP header information */
8591558Srgrimes#ifndef icmp_data
8601558Srgrimes		pr_retip(&icp->icmp_ip);
8611558Srgrimes#else
8621558Srgrimes		pr_retip((struct ip *)icp->icmp_data);
8631558Srgrimes#endif
8641558Srgrimes		break;
8651558Srgrimes	case ICMP_SOURCEQUENCH:
8661558Srgrimes		(void)printf("Source Quench\n");
8671558Srgrimes#ifndef icmp_data
8681558Srgrimes		pr_retip(&icp->icmp_ip);
8691558Srgrimes#else
8701558Srgrimes		pr_retip((struct ip *)icp->icmp_data);
8711558Srgrimes#endif
8721558Srgrimes		break;
8731558Srgrimes	case ICMP_REDIRECT:
8741558Srgrimes		switch(icp->icmp_code) {
8751558Srgrimes		case ICMP_REDIRECT_NET:
8761558Srgrimes			(void)printf("Redirect Network");
8771558Srgrimes			break;
8781558Srgrimes		case ICMP_REDIRECT_HOST:
8791558Srgrimes			(void)printf("Redirect Host");
8801558Srgrimes			break;
8811558Srgrimes		case ICMP_REDIRECT_TOSNET:
8821558Srgrimes			(void)printf("Redirect Type of Service and Network");
8831558Srgrimes			break;
8841558Srgrimes		case ICMP_REDIRECT_TOSHOST:
8851558Srgrimes			(void)printf("Redirect Type of Service and Host");
8861558Srgrimes			break;
8871558Srgrimes		default:
8881558Srgrimes			(void)printf("Redirect, Bad Code: %d", icp->icmp_code);
8891558Srgrimes			break;
8901558Srgrimes		}
8911558Srgrimes		(void)printf("(New addr: 0x%08lx)\n", icp->icmp_gwaddr.s_addr);
8921558Srgrimes#ifndef icmp_data
8931558Srgrimes		pr_retip(&icp->icmp_ip);
8941558Srgrimes#else
8951558Srgrimes		pr_retip((struct ip *)icp->icmp_data);
8961558Srgrimes#endif
8971558Srgrimes		break;
8981558Srgrimes	case ICMP_ECHO:
8991558Srgrimes		(void)printf("Echo Request\n");
9001558Srgrimes		/* XXX ID + Seq + Data */
9011558Srgrimes		break;
9021558Srgrimes	case ICMP_TIMXCEED:
9031558Srgrimes		switch(icp->icmp_code) {
9041558Srgrimes		case ICMP_TIMXCEED_INTRANS:
9051558Srgrimes			(void)printf("Time to live exceeded\n");
9061558Srgrimes			break;
9071558Srgrimes		case ICMP_TIMXCEED_REASS:
9081558Srgrimes			(void)printf("Frag reassembly time exceeded\n");
9091558Srgrimes			break;
9101558Srgrimes		default:
9111558Srgrimes			(void)printf("Time exceeded, Bad Code: %d\n",
9121558Srgrimes			    icp->icmp_code);
9131558Srgrimes			break;
9141558Srgrimes		}
9151558Srgrimes#ifndef icmp_data
9161558Srgrimes		pr_retip(&icp->icmp_ip);
9171558Srgrimes#else
9181558Srgrimes		pr_retip((struct ip *)icp->icmp_data);
9191558Srgrimes#endif
9201558Srgrimes		break;
9211558Srgrimes	case ICMP_PARAMPROB:
9221558Srgrimes		(void)printf("Parameter problem: pointer = 0x%02x\n",
9231558Srgrimes		    icp->icmp_hun.ih_pptr);
9241558Srgrimes#ifndef icmp_data
9251558Srgrimes		pr_retip(&icp->icmp_ip);
9261558Srgrimes#else
9271558Srgrimes		pr_retip((struct ip *)icp->icmp_data);
9281558Srgrimes#endif
9291558Srgrimes		break;
9301558Srgrimes	case ICMP_TSTAMP:
9311558Srgrimes		(void)printf("Timestamp\n");
9321558Srgrimes		/* XXX ID + Seq + 3 timestamps */
9331558Srgrimes		break;
9341558Srgrimes	case ICMP_TSTAMPREPLY:
9351558Srgrimes		(void)printf("Timestamp Reply\n");
9361558Srgrimes		/* XXX ID + Seq + 3 timestamps */
9371558Srgrimes		break;
9381558Srgrimes	case ICMP_IREQ:
9391558Srgrimes		(void)printf("Information Request\n");
9401558Srgrimes		/* XXX ID + Seq */
9411558Srgrimes		break;
9421558Srgrimes	case ICMP_IREQREPLY:
9431558Srgrimes		(void)printf("Information Reply\n");
9441558Srgrimes		/* XXX ID + Seq */
9451558Srgrimes		break;
9461558Srgrimes	case ICMP_MASKREQ:
9471558Srgrimes		(void)printf("Address Mask Request\n");
9481558Srgrimes		break;
9491558Srgrimes	case ICMP_MASKREPLY:
9501558Srgrimes		(void)printf("Address Mask Reply\n");
9511558Srgrimes		break;
95217724Sfenner	case ICMP_ROUTERADVERT:
95317724Sfenner		(void)printf("Router Advertisement\n");
95417724Sfenner		break;
95517724Sfenner	case ICMP_ROUTERSOLICIT:
95617724Sfenner		(void)printf("Router Solicitation\n");
95717724Sfenner		break;
9581558Srgrimes	default:
9591558Srgrimes		(void)printf("Bad ICMP type: %d\n", icp->icmp_type);
9601558Srgrimes	}
9611558Srgrimes}
9621558Srgrimes
9631558Srgrimes/*
9641558Srgrimes * pr_iph --
9651558Srgrimes *	Print an IP header with options.
9661558Srgrimes */
9671558Srgrimespr_iph(ip)
9681558Srgrimes	struct ip *ip;
9691558Srgrimes{
9701558Srgrimes	int hlen;
9711558Srgrimes	u_char *cp;
9721558Srgrimes
9731558Srgrimes	hlen = ip->ip_hl << 2;
9741558Srgrimes	cp = (u_char *)ip + 20;		/* point to options */
9751558Srgrimes
97617724Sfenner	(void)printf("Vr HL TOS  Len   ID Flg  off TTL Pro  cks      Src      Dst\n");
9771558Srgrimes	(void)printf(" %1x  %1x  %02x %04x %04x",
97817724Sfenner	    ip->ip_v, ip->ip_hl, ip->ip_tos, ntohs(ip->ip_len),
97917724Sfenner	    ntohs(ip->ip_id));
98017724Sfenner	(void)printf("   %1x %04x", (ntohl(ip->ip_off) & 0xe000) >> 13,
98117724Sfenner	    ntohl(ip->ip_off) & 0x1fff);
98217724Sfenner	(void)printf("  %02x  %02x %04x", ip->ip_ttl, ip->ip_p,
98317724Sfenner							    ntohs(ip->ip_sum));
9841558Srgrimes	(void)printf(" %s ", inet_ntoa(*(struct in_addr *)&ip->ip_src.s_addr));
9851558Srgrimes	(void)printf(" %s ", inet_ntoa(*(struct in_addr *)&ip->ip_dst.s_addr));
98617724Sfenner	/* dump any option bytes */
9871558Srgrimes	while (hlen-- > 20) {
9881558Srgrimes		(void)printf("%02x", *cp++);
9891558Srgrimes	}
9901558Srgrimes	(void)putchar('\n');
9911558Srgrimes}
9921558Srgrimes
9931558Srgrimes/*
9941558Srgrimes * pr_addr --
9951558Srgrimes *	Return an ascii host address as a dotted quad and optionally with
9961558Srgrimes * a hostname.
9971558Srgrimes */
9981558Srgrimeschar *
9991558Srgrimespr_addr(l)
10001558Srgrimes	u_long l;
10011558Srgrimes{
10021558Srgrimes	struct hostent *hp;
10031558Srgrimes	static char buf[80];
10041558Srgrimes
10051558Srgrimes	if ((options & F_NUMERIC) ||
10061558Srgrimes	    !(hp = gethostbyaddr((char *)&l, 4, AF_INET)))
100717320Speter		(void)snprintf(buf, sizeof(buf), "%s",
100817320Speter		    inet_ntoa(*(struct in_addr *)&l));
10091558Srgrimes	else
101017320Speter		(void)snprintf(buf, sizeof(buf), "%s (%s)", hp->h_name,
10111558Srgrimes		    inet_ntoa(*(struct in_addr *)&l));
10121558Srgrimes	return(buf);
10131558Srgrimes}
10141558Srgrimes
10151558Srgrimes/*
10161558Srgrimes * pr_retip --
10171558Srgrimes *	Dump some info on a returned (via ICMP) IP packet.
10181558Srgrimes */
10191558Srgrimespr_retip(ip)
10201558Srgrimes	struct ip *ip;
10211558Srgrimes{
10221558Srgrimes	int hlen;
10231558Srgrimes	u_char *cp;
10241558Srgrimes
10251558Srgrimes	pr_iph(ip);
10261558Srgrimes	hlen = ip->ip_hl << 2;
10271558Srgrimes	cp = (u_char *)ip + hlen;
10281558Srgrimes
10291558Srgrimes	if (ip->ip_p == 6)
10301558Srgrimes		(void)printf("TCP: from port %u, to port %u (decimal)\n",
10311558Srgrimes		    (*cp * 256 + *(cp + 1)), (*(cp + 2) * 256 + *(cp + 3)));
10321558Srgrimes	else if (ip->ip_p == 17)
10331558Srgrimes		(void)printf("UDP: from port %u, to port %u (decimal)\n",
10341558Srgrimes			(*cp * 256 + *(cp + 1)), (*(cp + 2) * 256 + *(cp + 3)));
10351558Srgrimes}
10361558Srgrimes
10371558Srgrimesfill(bp, patp)
10381558Srgrimes	char *bp, *patp;
10391558Srgrimes{
10401558Srgrimes	register int ii, jj, kk;
10411558Srgrimes	int pat[16];
10421558Srgrimes	char *cp;
10431558Srgrimes
10441558Srgrimes	for (cp = patp; *cp; cp++)
10451558Srgrimes		if (!isxdigit(*cp)) {
10461558Srgrimes			(void)fprintf(stderr,
10471558Srgrimes			    "ping: patterns must be specified as hex digits.\n");
10481558Srgrimes			exit(1);
10491558Srgrimes		}
10501558Srgrimes	ii = sscanf(patp,
10511558Srgrimes	    "%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x",
10521558Srgrimes	    &pat[0], &pat[1], &pat[2], &pat[3], &pat[4], &pat[5], &pat[6],
10531558Srgrimes	    &pat[7], &pat[8], &pat[9], &pat[10], &pat[11], &pat[12],
10541558Srgrimes	    &pat[13], &pat[14], &pat[15]);
10551558Srgrimes
10561558Srgrimes	if (ii > 0)
10571558Srgrimes		for (kk = 0;
10581558Srgrimes		    kk <= MAXPACKET - (8 + sizeof(struct timeval) + ii);
10591558Srgrimes		    kk += ii)
10601558Srgrimes			for (jj = 0; jj < ii; ++jj)
10611558Srgrimes				bp[jj + kk] = pat[jj];
10621558Srgrimes	if (!(options & F_QUIET)) {
10631558Srgrimes		(void)printf("PATTERN: 0x");
10641558Srgrimes		for (jj = 0; jj < ii; ++jj)
10651558Srgrimes			(void)printf("%02x", bp[jj] & 0xFF);
10661558Srgrimes		(void)printf("\n");
10671558Srgrimes	}
10681558Srgrimes}
10691558Srgrimes
10701558Srgrimesusage()
10711558Srgrimes{
10721558Srgrimes	(void)fprintf(stderr,
10731558Srgrimes	    "usage: ping [-Rdfnqrv] [-c count] [-i wait] [-l preload]\n\t[-p pattern] [-s packetsize] host\n");
10741558Srgrimes	exit(1);
10751558Srgrimes}
1076