/* BSDI ping.c,v 2.3 1996/01/21 17:56:50 jch Exp */
/*
* Copyright (c) 1989, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Mike Muuss.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. 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 BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef lint
static char copyright[] =
"@(#) Copyright (c) 1989, 1993\n\
The Regents of the University of California. All rights reserved.\n";
#endif /* not lint */
#ifndef lint
static char sccsid[] = "@(#)ping.c 8.1 (Berkeley) 6/5/93";
#endif /* not lint */
/*
* Using the InterNet Control Message Protocol (ICMP) "ECHO" facility,
* measure round-trip-delays and packet loss across network paths.
*
* Author -
* Mike Muuss
* U. S. Army Ballistic Research Laboratory
* December, 1983
*
* Status -
* Public Domain. Distribution Unlimited.
* Bugs -
* More statistics could always be gathered.
* This program has to run SUID to ROOT to access the ICMP socket.
*/
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netinet/ip_var.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define DEFDATALEN (64 - 8) /* default data length */
#define MAXIPLEN 60
#define MAXICMPLEN 76
#define MAXPACKET (65536 - 60 - 8)/* max packet size */
#define NROUTES 9 /* number of record route slots */
#define A(bit) rcvd_tbl[(bit)>>3] /* identify byte in array */
#define B(bit) (1 << ((bit) & 0x07)) /* identify bit in byte */
#define SET(bit) (A(bit) |= B(bit))
#define CLR(bit) (A(bit) &= (~B(bit)))
#define TST(bit) (A(bit) & B(bit))
#define F_FLOOD 0x0001
#define F_INTERVAL 0x0002
#define F_NUMERIC 0x0004
#define F_PINGFILLED 0x0008
#define F_QUIET 0x0010
#define F_RROUTE 0x0020
#define F_SO_DEBUG 0x0040
#define F_SO_DONTROUTE 0x0080
#define F_VERBOSE 0x0100
u_int options;
/*
* MAX_DUP_CHK is the number of bits in received table, i.e. the maximum
* number of received sequence numbers we can keep track of. Change 128
* to 8192 for complete accuracy...
*/
#define MAX_DUP_CHK (8 * 8192)
int mx_dup_ck = MAX_DUP_CHK;
char rcvd_tbl[MAX_DUP_CHK / 8];
struct sockaddr whereto; /* who to ping */
int datalen = DEFDATALEN;
int s; /* socket file descriptor */
u_char outpack[MAXPACKET];
char BSPACE = '\b'; /* characters written for flood */
char DOT = '.';
char *hostname;
int ident; /* process id to identify our packets */
/* counters */
long npackets; /* max packets to transmit */
long nreceived; /* # of packets we got back */
long nrepeats; /* number of duplicates */
long ntransmitted; /* sequence # for outbound packets = #sent */
int interval = 1; /* interval between packets */
/* timing */
int timing; /* flag to do timing */
double tmin = 999999999.0; /* minimum round trip time */
double tmax = 0.0; /* maximum round trip time */
double tsum = 0.0; /* sum of all times, for doing average */
void fill __P((char *, char *));
u_short in_cksum __P((u_short *, int));
void onalrm __P((int));
void oninfo __P((int));
void onint __P((int));
void pinger __P((void));
char *pr_addr __P((u_long));
void pr_icmph __P((struct icmp *));
void pr_iph __P((struct ip *));
void pr_pack __P((char *, int, struct sockaddr_in *));
void pr_retip __P((struct ip *));
void summary __P((void));
void tvsub __P((struct timeval *, struct timeval *));
void usage __P((void));
int
main(argc, argv)
int argc;
char *argv[];
{
extern int errno, optind;
extern char *optarg;
struct hostent *hp;
struct itimerval itimer;
struct protoent *proto;
struct sockaddr_in *to, from;
struct timeval timeout;
fd_set fdset;
register int cc, i;
int ch, fromlen, hold, packlen, preload;
u_char *datap, *packet;
char *e, *target, hnamebuf[MAXHOSTNAMELEN];
#ifdef IP_OPTIONS
char rspace[3 + 4 * NROUTES + 1]; /* record route space */
#endif
preload = 0;
datap = &outpack[8 + sizeof(struct timeval)];
while ((ch = getopt(argc, argv, "c:dfi:l:np:qRrs:v")) != -1)
switch(ch) {
case 'c':
npackets = strtol(optarg, &e, 10);
if (npackets <= 0 || *optarg == '\0' || *e != '\0')
errx(1,
"illegal number of packets -- %s", optarg);
break;
case 'd':
options |= F_SO_DEBUG;
break;
case 'f':
if (getuid()) {
errno = EPERM;
err(1, NULL);
}
options |= F_FLOOD;
setbuf(stdout, (char *)NULL);
break;
case 'i': /* wait between sending packets */
interval = strtol(optarg, &e, 10);
if (interval <= 0 || *optarg == '\0' || *e != '\0')
errx(1,
"illegal timing interval -- %s", optarg);
options |= F_INTERVAL;
break;
case 'l':
preload = strtol(optarg, &e, 10);
if (preload < 0 || *optarg == '\0' || *e != '\0')
errx(1, "illegal preload value -- %s", optarg);
break;
case 'n':
options |= F_NUMERIC;
break;
case 'p': /* fill buffer with user pattern */
options |= F_PINGFILLED;
fill((char *)datap, optarg);
break;
case 'q':
options |= F_QUIET;
break;
case 'R':
options |= F_RROUTE;
break;
case 'r':
options |= F_SO_DONTROUTE;
break;
case 's': /* size of packet to send */
datalen = strtol(optarg, &e, 10);
if (datalen <= 0 || *optarg == '\0' || *e != '\0')
errx(1, "illegal datalen value -- %s", optarg);
if (datalen > MAXPACKET)
errx(1,
"datalen value too large, maximum is %d",
MAXPACKET);
break;
case 'v':
options |= F_VERBOSE;
break;
default:
usage();
}
argc -= optind;
argv += optind;
if (argc != 1)
usage();
target = *argv;
memset(&whereto, 0, sizeof(struct sockaddr));
to = (struct sockaddr_in *)&whereto;
to->sin_family = AF_INET;
to->sin_addr.s_addr = inet_addr(target);
if (to->sin_addr.s_addr != (u_int)-1)
hostname = target;
else {
hp = gethostbyname(target);
if (!hp)
errx(1, "unknown host %s", target);
to->sin_family = hp->h_addrtype;
memmove(&to->sin_addr, hp->h_addr, hp->h_length);
(void)strncpy(hnamebuf, hp->h_name, sizeof(hnamebuf) - 1);
hostname = hnamebuf;
}
if (options & F_FLOOD && options & F_INTERVAL)
errx(1, "-f and -i incompatible options");
i