Skip to content

Instantly share code, notes, and snippets.

@qrush
Last active August 16, 2023 10:21
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save qrush/4491216 to your computer and use it in GitHub Desktop.
Save qrush/4491216 to your computer and use it in GitHub Desktop.
# This is a shell archive. Remove anything before this line, then
# unpack it by saving it in a file and typing "sh file". (Files
# unpacked will be owned by you and have default permissions.)
#
# This archive contains:
# Makefile ping.1 ping.c ping.shar newping.1 newping.c
echo x - Makefile
cat > "Makefile" << '//E*O*F Makefile//'
# Revised to compile under SunOS 4.1.x (no longer necessary to install
# kernel mods). 08/07/92 RJRJr This makefile is based on the original
# makefiles supplied with tcpdump and traceroute.
# Set DESTDIR to the directory in which the traceroute executable will be
# installed. /usr/etc is a good place to put a network debugging tool such
# as this.
DESTDIR= /usr/brl/sbin
MANDIR= /usr/brl/man/man1
# You shouldn't need to change anything below this line.
CC= cc
CFLAGS = -O
# At the moment, the INCL variable isn't really needed for anything.
INCL = -I.
LIBS =
# Script (or program) that returns the machine and os types,
# or just edit in the name yourself.
MD=`mdtype`
OS=`ostype`
# Explicitly define compiliation rule since SunOS 4's make doesn't like gcc.
# Also, gcc does not remove the .o before forking 'as', which can be a
# problem if you don't own the file but can write to the directory.
.c.o:
rm -f $@; $(CC) $(CFLAGS) -c $*.c
all: submake
ping: ping.o
$(CC) $(CFLAGS) $(INCL) -o ping ping.o $(LIBS)
submake:
-@dir=$(MD)-$(OS); set -x; \
if [ ! -d $$dir ]; then ${MAKE} ${MFLAGS} config; fi; \
if [ -n "`find Makefile -newer $$dir/Makefile -print`" ]; \
then ${MAKE} ${MFLAGS} config; fi; \
cd $$dir; ${MAKE} ${MFLAGS} ping
# N.B.- symbolic links are used in the subdirectory rather than VPATH
# because at least one Sun cc compiler puts the .o in the wrong place
# when using VPATH and it's almost impossible to get "make depend" to
# do the right thing.
config:
-@dir=$(MD)-$(OS); set -x; \
mkdir $$dir; chmod ug+w $$dir; ln -s ../ping.c $$dir; \
sed -e "/^all:/d" Makefile >$$dir/Makefile; \
chmod ug+w $$dir/Makefile; \
cd $$dir; ${MAKE} ${MFLAGS} depend
install: submake FRC
-@dir=$(MD)-$(OS); set -x; \
install -c -o root -g bin -m 4755 $$dir/ping ${DESTDIR}; \
install -c -o root -g staff -m 664 ping.1 ${MANDIR}
lint:
lint -hbxn $(INCL) ping.c | \
grep -v 'possible pointer alignment problem'
clean:
-@dir=$(MD)-$(OS); set -x; rm -rf $$dir
FRC:
depend:
cc -M ping.c | sed 's/\.o//' | \
awk ' { if ($$1 != prev) \
{ if (rec != "") print rec; rec = $$0; prev = $$1; } \
else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
else rec = rec " " $$2 } } \
END { print rec } ' >> makedep;
echo '/^# DO NOT DELETE THIS LINE/+2,$$d' >eddep;
echo '$$r makedep' >>eddep;
echo 'w' >>eddep;
cp Makefile Makefile.bak;
ed - Makefile < eddep;
rm eddep makedep;
echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile;
echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile;
echo '# see make depend above' >> Makefile;
# DO NOT DELETE THIS LINE -- make depend uses it
//E*O*F Makefile//
echo x - ping.1
cat > "ping.1" << '//E*O*F ping.1//'
.\" Copyright (c) 1985 Regents of the University of California.
.\" All rights reserved. The Berkeley software License Agreement
.\" specifies the terms and conditions for redistribution.
.\"
.\" @(#)ping.8 6.2 (Berkeley) 5/23/86
.\"
.TH PING 8 "May 23, 1986"
.UC 6
.SH NAME
ping \- send ICMP ECHO_REQUEST packets to network hosts
.SH SYNOPSIS
.B /etc/ping
[
.B \-dfqrv
]
.I host
[
.I packetsize
[
.I count
[
.I preload
]]]
.SH DESCRIPTION
The DARPA Internet is a large and complex aggregation of
network hardware, connected together by gateways.
Tracking a single-point hardware or software failure
can often be difficult.
.I Ping
utilizes the
ICMP protocol's mandatory ECHO_REQUEST datagram to elicit an
ICMP ECHO_RESPONSE from a host or gateway.
ECHO_REQUEST datagrams (``pings'') have an IP and ICMP header,
followed by a \fBstruct timeval\fR, and then an arbitrary number
of ``pad'' bytes used to fill out the packet.
Default datagram length is 64 bytes, but this may be changed
using the command-line option.
Other options are:
.TP
.B \-v
Verbose output. ICMP packets other than ECHO RESPONSE that are received
are listed.
.TP
.B \-q
Quiet output. Nothing is displayed except the summary line on termination.
.TP
.B \-f
Flood ping. Outputs packets as fast as they come back or one hundred times
per second, whichever is more. For every ECHO_REQUEST sent a period '.'
is printed, while for ever ECHO_REPLY received a backspace is printed.
This provides a rapid display of how many packets are being dropped.
.TP
.B \-r
Bypass the normal routing tables and send directly to a host on an attached
network.
If the host is not on a directly-attached network,
an error is returned.
This option can be used to ping a local host through an interface
that has no route through it (e.g., after the interface was dropped by
.IR routed (8C)).
.TP
.B \-d
Set the SO_DEBUG option on the socket being used.
.PP
When using \fIping\fR for fault isolation,
it should first be run on the local
host, to verify that the local network interface is up and
running.
Then, hosts and gateways further and further away
should be ``pinged''.
\fIPing\fR sends one datagram per second, and
prints one line of output for every ECHO_RESPONSE returned.
No output is produced if there is no response.
If an optional
.I count
is given, only that number of requests is sent.
Round-trip times and packet loss statistics are computed.
When all responses have been received or the program times out (with a
.I count
specified),
or if the program is terminated with a SIGINT, a brief
summary is displayed.
If
.I preload
is given,
.I ping
sends that many packets as rapidly as possible before
falling into its normal mode of behavior.
.PP
This program is intended for use in network testing, measurement
and management.
It should be used primarily for manual fault isolation.
Because of the load it could impose on the network,
it is unwise to use
.I ping
during normal operations or from automated scripts.
.SH DETAILS
For those that care. An IP header without options in 20 bytes.
An ICMP ECHO_REQUEST packet contains an additional 8 bytes worth
of ICMP header followed by an arbitrary amount of data. When a
.I packetsize
is given, this indicated the size of this extra blob of data (the
default is 56). Thus the amount of data received inside of an IP
packet of type ICMP ECHO_REPLY will always be 8 bytes more than
the requested data space (the ICMP header).
.PP
If the data space is at least eight bytes large,
.I ping
uses the first eight bytes of this space to include a timestamp which
it uses in the computation of round trip times. This explains why if
less than eight bytes of pad are requested, no round trip times are given.
.SH BUGS
Flood pinging the broadcast address is not recommended.
.SH AUTHOR
Mike Muuss
.SH SEE ALSO
netstat(1),
ifconfig(8C)
//E*O*F ping.1//
echo x - ping.c
cat > "ping.c" << '//E*O*F ping.c//'
/*
* P I N G . C
*
* 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
* Modified at Uc Berkeley
*
* Changed argument to inet_ntoa() to be struct in_addr instead of u_long
* DFM BRL 1992
*
* 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 <stdio.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/file.h>
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netdb.h>
#define MAXWAIT 10 /* max time to wait for response, sec. */
#define MAXPACKET 4096 /* max packet size */
#define VERBOSE 1 /* verbose flag */
#define QUIET 2 /* quiet flag */
#define FLOOD 4 /* floodping flag */
#ifndef MAXHOSTNAMELEN
#define MAXHOSTNAMELEN 64
#endif
u_char packet[MAXPACKET];
int i, pingflags, options;
extern int errno;
int s; /* Socket file descriptor */
struct hostent *hp; /* Pointer to host info */
struct timezone tz; /* leftover */
struct sockaddr whereto;/* Who to ping */
int datalen; /* How much data */
char usage[] =
"Usage: ping [-dfqrv] host [packetsize [count [preload]]]\n";
char *hostname;
char hnamebuf[MAXHOSTNAMELEN];
int npackets;
int preload = 0; /* number of packets to "preload" */
int ntransmitted = 0; /* sequence # for outbound packets = #sent */
int ident;
int nreceived = 0; /* # of packets we got back */
int timing = 0;
int tmin = 999999999;
int tmax = 0;
int tsum = 0; /* sum of all times, for doing average */
int finish(), catcher();
char *inet_ntoa();
/*
* M A I N
*/
main(argc, argv)
char *argv[];
{
struct sockaddr_in from;
char **av = argv;
struct sockaddr_in *to = (struct sockaddr_in *) &whereto;
int on = 1;
struct protoent *proto;
argc--, av++;
while (argc > 0 && *av[0] == '-') {
while (*++av[0]) switch (*av[0]) {
case 'd':
options |= SO_DEBUG;
break;
case 'r':
options |= SO_DONTROUTE;
break;
case 'v':
pingflags |= VERBOSE;
break;
case 'q':
pingflags |= QUIET;
break;
case 'f':
pingflags |= FLOOD;
break;
}
argc--, av++;
}
if(argc < 1 || argc > 4) {
printf(usage);
exit(1);
}
bzero((char *)&whereto, sizeof(struct sockaddr) );
to->sin_family = AF_INET;
to->sin_addr.s_addr = inet_addr(av[0]);
if(to->sin_addr.s_addr != (unsigned)-1) {
strcpy(hnamebuf, av[0]);
hostname = hnamebuf;
} else {
hp = gethostbyname(av[0]);
if (hp) {
to->sin_family = hp->h_addrtype;
bcopy(hp->h_addr, (caddr_t)&to->sin_addr, hp->h_length);
hostname = hp->h_name;
} else {
printf("%s: unknown host %s\n", argv[0], av[0]);
exit(1);
}
}
if( argc >= 2 )
datalen = atoi( av[1] );
else
datalen = 64-8;
if (datalen > MAXPACKET) {
fprintf(stderr, "ping: packet size too large\n");
exit(1);
}
if (datalen >= sizeof(struct timeval)) /* can we time 'em? */
timing = 1;
if (argc >= 3)
npackets = atoi(av[2]);
if (argc == 4)
preload = atoi(av[3]);
ident = getpid() & 0xFFFF;
if ((proto = getprotobyname("icmp")) == NULL) {
fprintf(stderr, "icmp: unknown protocol\n");
exit(10);
}
if ((s = socket(AF_INET, SOCK_RAW, proto->p_proto)) < 0) {
perror("ping: socket");
exit(5);
}
if (options & SO_DEBUG) {
if(pingflags & VERBOSE)
printf("...debug on.\n");
setsockopt(s, SOL_SOCKET, SO_DEBUG, &on, sizeof(on));
}
if (options & SO_DONTROUTE) {
if(pingflags & VERBOSE)
printf("...no routing.\n");
setsockopt(s, SOL_SOCKET, SO_DONTROUTE, &on, sizeof(on));
}
if(to->sin_family == AF_INET) {
printf("PING %s (%s): %d data bytes\n", hostname,
inet_ntoa(to->sin_addr), datalen); /* DFM */
} else {
printf("PING %s: %d data bytes\n", hostname, datalen );
}
setlinebuf( stdout );
signal( SIGINT, finish );
signal(SIGALRM, catcher);
/* fire off them quickies */
for(i=0; i < preload; i++)
pinger();
if(!(pingflags & FLOOD))
catcher(); /* start things going */
for (;;) {
int len = sizeof (packet);
int fromlen = sizeof (from);
int cc;
struct timeval timeout;
int fdmask = 1 << s;
timeout.tv_sec = 0;
timeout.tv_usec = 10000;
if(pingflags & FLOOD) {
pinger();
if( select(32, &fdmask, 0, 0, &timeout) == 0)
continue;
}
if ( (cc=recvfrom(s, packet, len, 0, &from, &fromlen)) < 0) {
if( errno == EINTR )
continue;
perror("ping: recvfrom");
continue;
}
pr_pack( packet, cc, &from );
if (npackets && nreceived >= npackets)
finish();
}
/*NOTREACHED*/
}
/*
* C A T C H E R
*
* This routine causes another PING to be transmitted, and then
* schedules another SIGALRM for 1 second from now.
*
* Bug -
* Our sense of time will slowly skew (ie, packets will not be launched
* exactly at 1-second intervals). This does not affect the quality
* of the delay and loss statistics.
*/
catcher()
{
int waittime;
pinger();
if (npackets == 0 || ntransmitted < npackets)
alarm(1);
else {
if (nreceived) {
waittime = 2 * tmax / 1000;
if (waittime == 0)
waittime = 1;
} else
waittime = MAXWAIT;
signal(SIGALRM, finish);
alarm(waittime);
}
}
/*
* P I N G E R
*
* Compose and transmit an ICMP ECHO REQUEST packet. The IP packet
* will be added on by the kernel. The ID field is our UNIX process ID,
* and the sequence number is an ascending integer. The first 8 bytes
* of the data portion are used to hold a UNIX "timeval" struct in VAX
* byte-order, to compute the round-trip time.
*/
pinger()
{
static u_char outpack[MAXPACKET];
register struct icmp *icp = (struct icmp *) outpack;
int i, cc;
register struct timeval *tp = (struct timeval *) &outpack[8];
register u_char *datap = &outpack[8+sizeof(struct timeval)];
icp->icmp_type = ICMP_ECHO;
icp->icmp_code = 0;
icp->icmp_cksum = 0;
icp->icmp_seq = ntransmitted++;
icp->icmp_id = ident; /* ID */
cc = datalen+8; /* skips ICMP portion */
if (timing)
gettimeofday( tp, &tz );
for( i=8; i<datalen; i++) /* skip 8 for time */
*datap++ = i;
/* Compute ICMP checksum here */
icp->icmp_cksum = in_cksum( icp, cc );
/* cc = sendto(s, msg, len, flags, to, tolen) */
i = sendto( s, outpack, cc, 0, &whereto, sizeof(struct sockaddr) );
if( i < 0 || i != cc ) {
if( i<0 ) perror("sendto");
printf("ping: wrote %s %d chars, ret=%d\n",
hostname, cc, i );
fflush(stdout);
}
if(pingflags == FLOOD) {
putchar('.');
fflush(stdout);
}
}
/*
* P R _ T Y P E
*
* Convert an ICMP "type" field to a printable string.
*/
char *
pr_type( t )
register int t;
{
static char *ttab[] = {
"Echo Reply",
"ICMP 1",
"ICMP 2",
"Dest Unreachable",
"Source Quench",
"Redirect",
"ICMP 6",
"ICMP 7",
"Echo",
"ICMP 9",
"ICMP 10",
"Time Exceeded",
"Parameter Problem",
"Timestamp",
"Timestamp Reply",
"Info Request",
"Info Reply"
};
if( t < 0 || t > 16 )
return("OUT-OF-RANGE");
return(ttab[t]);
}
/*
* P R _ P A C K
*
* Print out the packet, if it came from us. This logic is necessary
* because ALL readers of the ICMP socket get a copy of ALL ICMP packets
* which arrive ('tis only fair). This permits multiple copies of this
* program to be run without having intermingled output (or statistics!).
*/
pr_pack( buf, cc, from )
char *buf;
int cc;
struct sockaddr_in *from;
{
struct ip *ip;
register struct icmp *icp;
register long *lp = (long *) packet;
register int i;
struct timeval tv;
struct timeval *tp;
int hlen, triptime;
from->sin_addr.s_addr = ntohl( from->sin_addr.s_addr );
gettimeofday( &tv, &tz );
ip = (struct ip *) buf;
hlen = ip->ip_hl << 2;
if (cc < hlen + ICMP_MINLEN) {
if (pingflags & VERBOSE)
printf("packet too short (%d bytes) from %s\n", cc,
inet_ntoa(ntohl(from->sin_addr))); /* DFM */
return;
}
cc -= hlen;
icp = (struct icmp *)(buf + hlen);
if( (!(pingflags & QUIET)) && icp->icmp_type != ICMP_ECHOREPLY ) {
printf("%d bytes from %s: icmp_type=%d (%s) icmp_code=%d\n",
cc, inet_ntoa(ntohl(from->sin_addr)),
icp->icmp_type, pr_type(icp->icmp_type), icp->icmp_code);/*DFM*/
if (pingflags & VERBOSE) {
for( i=0; i<12; i++)
printf("x%2.2x: x%8.8x\n", i*sizeof(long),
*lp++);
}
return;
}
if( icp->icmp_id != ident )
return; /* 'Twas not our ECHO */
if (timing) {
tp = (struct timeval *)&icp->icmp_data[0];
tvsub( &tv, tp );
triptime = tv.tv_sec*1000+(tv.tv_usec/1000);
tsum += triptime;
if( triptime < tmin )
tmin = triptime;
if( triptime > tmax )
tmax = triptime;
}
if(!(pingflags & QUIET)) {
if(pingflags != FLOOD) {
printf("%d bytes from %s: icmp_seq=%d", cc,
inet_ntoa(from->sin_addr),
icp->icmp_seq ); /* DFM */
if (timing)
printf(" time=%d ms\n", triptime );
else
putchar('\n');
} else {
putchar('\b');
fflush(stdout);
}
}
nreceived++;
}
/*
* I N _ C K S U M
*
* Checksum routine for Internet Protocol family headers (C Version)
*
*/
in_cksum(addr, len)
u_short *addr;
int len;
{
register int nleft = len;
register u_short *w = addr;
register u_short answer;
register int sum = 0;
/*
* Our algorithm is simple, using a 32 bit accumulator (sum),
* we add sequential 16 bit words to it, and at the end, fold
* back all the carry bits from the top 16 bits into the lower
* 16 bits.
*/
while( nleft > 1 ) {
sum += *w++;
nleft -= 2;
}
/* mop up an odd byte, if necessary */
if( nleft == 1 ) {
u_short u = 0;
*(u_char *)(&u) = *(u_char *)w ;
sum += u;
}
/*
* add back carry outs from top 16 bits to low 16 bits
*/
sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
sum += (sum >> 16); /* add carry */
answer = ~sum; /* truncate to 16 bits */
return (answer);
}
/*
* T V S U B
*
* Subtract 2 timeval structs: out = out - in.
*
* Out is assumed to be >= in.
*/
tvsub( out, in )
register struct timeval *out, *in;
{
if( (out->tv_usec -= in->tv_usec) < 0 ) {
out->tv_sec--;
out->tv_usec += 1000000;
}
out->tv_sec -= in->tv_sec;
}
/*
* F I N I S H
*
* Print out statistics, and give up.
* Heavily buffered STDIO is used here, so that all the statistics
* will be written with 1 sys-write call. This is nice when more
* than one copy of the program is running on a terminal; it prevents
* the statistics output from becomming intermingled.
*/
finish()
{
putchar('\n');
fflush(stdout);
printf("\n----%s PING Statistics----\n", hostname );
printf("%d packets transmitted, ", ntransmitted );
printf("%d packets received, ", nreceived );
if (ntransmitted)
if( nreceived > ntransmitted)
printf("-- somebody's printing up packets!");
else
printf("%d%% packet loss",
(int) (((ntransmitted-nreceived)*100) /
ntransmitted));
printf("\n");
if (nreceived && timing)
printf("round-trip (ms) min/avg/max = %d/%d/%d\n",
tmin,
tsum / nreceived,
tmax );
fflush(stdout);
exit(0);
}
//E*O*F ping.c//
echo x - ping.shar
cat > "ping.shar" << '//E*O*F ping.shar//'
//E*O*F ping.shar//
echo x - newping.1
cat > "newping.1" << '//E*O*F newping.1//'
.\" Copyright (c) 1985 Regents of the University of California.
.\" All rights reserved. The Berkeley software License Agreement
.\" specifies the terms and conditions for redistribution.
.\"
.\" @(#)ping.8 6.2 (Berkeley) 5/23/86
.\"
.TH PING 8 "May 23, 1986"
.UC 6
.SH NAME
ping \- send ICMP ECHO_REQUEST packets to network hosts
.SH SYNOPSIS
.B /etc/ping
[
.B \-dfnqrvR
]
.I host
[
.I packetsize
[
.I count
[
.I preload
]]]
.SH DESCRIPTION
The DARPA Internet is a large and complex aggregation of
network hardware, connected together by gateways.
Tracking a single-point hardware or software failure
can often be difficult.
.I Ping
utilizes the
ICMP protocol's mandatory ECHO_REQUEST datagram to elicit an
ICMP ECHO_RESPONSE from a host or gateway.
ECHO_REQUEST datagrams (``pings'') have an IP and ICMP header,
followed by a \fBstruct timeval\fR, and then an arbitrary number
of ``pad'' bytes used to fill out the packet.
Default datagram length is 64 bytes, but this may be changed
using the command-line option.
Other options are:
.TP
.B \-v
Verbose output. ICMP packets other than ECHO RESPONSE that are received
are listed.
.TP
.B \-q
Quiet output. Nothing is displayed except the summary line on termination.
.TP
.B \-n
Numeric output only. No attempt will be made to lookup symbolic
names for host addresses. Useful if your nameserver if flakey
or for hosts not in the database.
.TP
.B \-f
Flood ping. Outputs packets as fast as they come back or one hundred times
per second, whichever is more. For every ECHO_REQUEST sent a period '.'
is printed, while for ever ECHO_REPLY received a backspace is printed.
This provides a rapid display of how many packets are being dropped.
.TP
.B \-R
Record Route. Includes the RECORD_ROUTE option in the ECHO_REQUEST
packet and displays the route buffer on returned packets. Note that
the IP header is only large enough for six such routes. Many hosts
ignore or discard this option.
.TP
.B \-r
Bypass the normal routing tables and send directly to a host on an attached
network.
If the host is not on a directly-attached network,
an error is returned.
This option can be used to ping a local host through an interface
that has no route through it (e.g., after the interface was dropped by
.IR routed (8C)).
.TP
.B \-d
Set the SO_DEBUG option on the socket being used.
.PP
When using \fIping\fR for fault isolation,
it should first be run on the local
host, to verify that the local network interface is up and
running.
Then, hosts and gateways further and further away
should be ``pinged''.
\fIPing\fR sends one datagram per second, and
prints one line of output for every ECHO_RESPONSE returned.
No output is produced if there is no response.
If an optional
.I count
is given, only that number of requests is sent.
Round-trip times and packet loss statistics are computed.
When all responses have been received or the program times out (with a
.I count
specified),
or if the program is terminated with a SIGINT, a brief
summary is displayed.
If
.I preload
is given,
.I ping
sends that many packets as fast as possible before
falling into its normal mode of behavior.
.PP
This program is intended for use in network testing, measurement
and management.
It should be used primarily for manual fault isolation.
Because of the load it could impose on the network,
it is unwise to use
.I ping
during normal operations or from automated scripts.
.SH DETAILS
For those that care. An IP header without options in 20 bytes.
An ICMP ECHO_REQUEST packet contains an additional 8 bytes worth
of ICMP header followed by an arbitrary amount of data. When a
.I packetsize
is given, this indicated the size of this extra blob of data (the
default is 56). Thus the amount of data received inside of an IP
packet of type ICMP ECHO_REPLY will always be 8 bytes more than
the requested data space (the ICMP header).
.PP
If the data space is at least eight bytes large,
.I ping
uses the first eight bytes of this space to include a timestamp which
it uses in the computation of round trip times. This explains why if
less than eight bytes of pad are requested, no round trip times are given.
.SH BUGS
Far too many Hosts and Gateways (including the core gateways) ignore the
RECORD_ROUTE option. To quote RFC 791, "What is optional is their
transmission in any particular datagram, not their implementation."
.PP
The maximum IP header length is too small for options like
RECORD_ROUTE to be completely useful. There's not much that
we can do about that however.
.PP
Flood pinging the broadcast address is not recommended.
.SH AUTHOR
Mike Muuss
.SH SEE ALSO
netstat(1),
ifconfig(8C)
//E*O*F newping.1//
echo x - newping.c
cat > "newping.c" << '//E*O*F newping.c//'
/*
* P I N G . C
*
* 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
* Modified at Uc Berkeley
* Record Route and verbose headers - Phil Dykstra, BRL, March 1988.
*
* 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 <stdio.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/file.h>
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netdb.h>
#define PING_MAXWAIT 10 /* max time to wait for response, sec. */
#define PING_MAXPACKET 4096 /* max packet size */
#define PING_NUMERIC 1 /* return dotted quads */
#define PING_VERBOSE 2 /* verbose flag */
#define PING_QUIET 4 /* quiet flag */
#define PING_DEBUG 8 /* turn on socket debugging */
#define PING_DONTROUTE 16 /* dont route pings*/
#define PING_RROUTE 32 /* record route flag */
#define PING_FLOOD 64 /* floodping flag */
#define PING_CISCO 128 /* cisco style ping */
#define PING_NROUTES 9 /* number of record route slots (9 max) */
#ifndef MAXHOSTNAMELEN
#define MAXHOSTNAMELEN 64
#endif
u_char packet[PING_MAXPACKET];
int i, pingflags;
extern int errno;
int s; /* Socket file descriptor */
struct hostent *hp; /* Pointer to host info */
struct timezone tz; /* leftover */
struct sockaddr whereto;/* Who to ping */
int datalen; /* How much data */
char usage[] =
"Usage: ping [-cdfnqrvR] host [packetsize [count [preload]]]\n";
char *hostname;
char hnamebuf[MAXHOSTNAMELEN];
int npackets;
int preload = 0; /* number of packets to "preload" */
int ntransmitted = 0; /* sequence # for outbound packets = #sent */
int ident;
int nreceived = 0; /* # of packets we got back */
int timing = 0;
int tmin = 999999999;
int tmax = 0;
int tsum = 0; /* sum of all times, for doing average */
int finish(), catcher();
char *inet_ntoa();
char *pr_addr();
char rspace[3+4*PING_NROUTES+1]; /* record route space */
/*
* M A I N
*/
main(argc, argv)
char *argv[];
{
char **av = argv;
int on = 1;
struct sockaddr_in pktaddr;
struct sockaddr_in *to = (struct sockaddr_in *) &whereto;
struct protoent *proto;
int maxpkt = sizeof (packet);
int addrlen = sizeof (pktaddr);
int rcvlen, nfds, fdmask;
struct timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = 10000;
argc--, av++;
while (argc > 0 && *av[0] == '-') {
while (*++av[0]) switch (*av[0]) {
case 'c':
pingflags |= PING_CISCO;
break;
case 'd':
pingflags |= PING_DEBUG;
break;
case 'f':
pingflags |= PING_FLOOD;
/* timeout.tv_usec = 0; */
break;
case 'n':
pingflags |= PING_NUMERIC;
break;
case 'q':
pingflags |= PING_QUIET;
break;
case 'r':
pingflags |= PING_DONTROUTE;
break;
case 'v':
pingflags |= PING_VERBOSE;
break;
case 'R':
pingflags |= PING_RROUTE;
break;
}
argc--, av++;
}
if(argc < 1 || argc > 4) {
printf(usage);
exit(1);
}
bzero((char *)&whereto, sizeof(struct sockaddr) );
to->sin_family = AF_INET;
to->sin_addr.s_addr = inet_addr(av[0]);
if(to->sin_addr.s_addr != (unsigned)-1) {
strcpy(hnamebuf, av[0]);
hostname = hnamebuf;
} else {
hp = gethostbyname(av[0]);
if (hp) {
to->sin_family = hp->h_addrtype;
bcopy(hp->h_addr, (caddr_t)&to->sin_addr, hp->h_length);
strncpy( hnamebuf, hp->h_name, sizeof(hnamebuf)-1 );
hostname = hnamebuf;
} else {
printf("%s: unknown host %s\n", argv[0], av[0]);
exit(1);
}
}
if( argc >= 2 )
datalen = atoi( av[1] );
else
datalen = 64-8;
if (datalen > PING_MAXPACKET) {
fprintf(stderr, "ping: packet size too large\n");
exit(1);
}
if (datalen >= sizeof(struct timeval)) /* can we time 'em? */
timing = 1;
if (argc >= 3)
npackets = atoi(av[2]);
if (argc == 4)
preload = atoi(av[3]);
ident = getpid() & 0xFFFF;
if ((proto = getprotobyname("icmp")) == NULL) {
fprintf(stderr, "icmp: unknown protocol\n");
exit(10);
}
if ((s = socket(AF_INET, SOCK_RAW, proto->p_proto)) < 0) {
perror("ping: socket");
exit(5);
}
fdmask = 1 << s;
nfds = s + 1;
if (pingflags & PING_DEBUG) {
setsockopt(s, SOL_SOCKET, SO_DEBUG, &on, sizeof(on));
}
if (pingflags & PING_DONTROUTE) {
setsockopt(s, SOL_SOCKET, SO_DONTROUTE, &on, sizeof(on));
}
/* Record Route option */
if( pingflags & PING_RROUTE ) {
#ifdef IP_OPTIONS
rspace[IPOPT_OPTVAL] = IPOPT_RR;
rspace[IPOPT_OLEN] = sizeof(rspace)-1;
rspace[IPOPT_OFFSET] = IPOPT_MINOFF;
if( setsockopt(s, IPPROTO_IP, IP_OPTIONS, rspace, sizeof(rspace)) < 0 ) {
perror( "Record route" );
exit( 42 );
}
#else /* IP_OPTIONS */
fprintf( stderr, "ping: record route not available on this machine.\n" );
exit( 42 );
#endif /* IP_OPTIONS */
}
if(to->sin_family == AF_INET) {
printf("PING %s (%s): %d data bytes\n", hostname,
inet_ntoa(to->sin_addr.s_addr), datalen);
} else {
printf("PING %s: %d data bytes\n", hostname, datalen );
}
setlinebuf( stdout );
signal( SIGINT, finish );
signal(SIGALRM, catcher);
/* fire off them quickies */
for(i=0; i < preload; i++)
pinger();
if(pingflags & PING_FLOOD) {
for (;;) {
pinger();
if( select(32, &fdmask, 0, 0, &timeout) == 0)
continue;
if((rcvlen = recvfrom(s, packet, maxpkt, 0,
&pktaddr, &addrlen)) < 0) {
if( errno != EINTR )
perror("\nping: recvfrom");
continue;
}
pr_pack( packet, rcvlen, &pktaddr );
if (npackets && nreceived >= npackets)
finish();
}
} else if(pingflags & PING_CISCO) {
for(;;) {
alarm(1);
pinger();
if((rcvlen = recvfrom(s, packet, maxpkt, 0,
&pktaddr, &addrlen)) < 0) {
if( errno != EINTR )
perror("\nping: recvfrom");
}
pr_pack( packet, rcvlen, &pktaddr );
if (npackets && nreceived >= npackets)
finish();
}
} else {
catcher(); /* start things going */
for (;;) {
if((rcvlen = recvfrom(s, packet, maxpkt, 0,
&pktaddr, &addrlen)) < 0) {
if( errno == EINTR )
continue;
perror("ping: recvfrom");
continue;
}
pr_pack( packet, rcvlen, &pktaddr );
if (npackets && nreceived >= npackets)
finish();
}
}
/*NOTREACHED*/
}
/*
* C A T C H E R
*
* This routine causes another PING to be transmitted, and then
* schedules another SIGALRM for 1 second from now.
*
* Bug -
* Our sense of time will slowly skew (ie, packets will not be launched
* exactly at 1-second intervals). This does not affect the quality
* of the delay and loss statistics.
*/
catcher()
{
int waittime;
if(pingflags & PING_CISCO) {
putchar('!');
fflush(stdout);
}
pinger();
if (npackets == 0 || ntransmitted < npackets)
alarm(1);
else {
if (nreceived) {
waittime = 2 * tmax / 1000;
if (waittime == 0)
waittime = 1;
} else
waittime = PING_MAXWAIT;
signal(SIGALRM, finish);
alarm(waittime);
}
}
/*
* P I N G E R
*
* Compose and transmit an ICMP ECHO REQUEST packet. The IP packet
* will be added on by the kernel. The ID field is our UNIX process ID,
* and the sequence number is an ascending integer. The first 8 bytes
* of the data portion are used to hold a UNIX "timeval" struct in VAX
* byte-order, to compute the round-trip time.
*/
pinger()
{
static u_char outpack[PING_MAXPACKET];
register struct icmp *icp = (struct icmp *) outpack;
int i, cc;
register struct timeval *tp = (struct timeval *) &outpack[8];
register u_char *datap = &outpack[8+sizeof(struct timeval)];
icp->icmp_type = ICMP_ECHO;
icp->icmp_code = 0;
icp->icmp_cksum = 0;
icp->icmp_seq = ntransmitted++;
icp->icmp_id = ident; /* ID */
cc = datalen+8; /* skips ICMP portion */
if (timing)
gettimeofday( tp, &tz );
for( i=8; i<datalen; i++) /* skip 8 for time */
*datap++ = i;
/* Compute ICMP checksum here */
icp->icmp_cksum = in_cksum( icp, cc );
/* cc = sendto(s, msg, len, flags, to, tolen) */
i = sendto( s, outpack, cc, 0, &whereto, sizeof(struct sockaddr) );
if( i < 0 || i != cc ) {
if( i<0 ) perror("sendto");
printf("ping: wrote %s %d chars, ret=%d\n",
hostname, cc, i );
fflush(stdout);
}
if(pingflags & PING_FLOOD) {
putchar('.');
fflush(stdout);
}
}
/*
* P R _ P A C K
*
* Print out the packet, if it came from us. This logic is necessary
* because ALL readers of the ICMP socket get a copy of ALL ICMP packets
* which arrive ('tis only fair). This permits multiple copies of this
* program to be run without having intermingled output (or statistics!).
*/
pr_pack( buf, cc, from )
char *buf;
int cc;
struct sockaddr_in *from;
{
struct ip *ip;
register struct icmp *icp;
register long *lp = (long *) packet;
register int i;
struct timeval tv;
struct timeval *tp;
int hlen, triptime;
from->sin_addr.s_addr = ntohl( from->sin_addr.s_addr );
gettimeofday( &tv, &tz );
/* Check the IP header */
ip = (struct ip *) buf;
hlen = ip->ip_hl << 2;
if( cc < hlen + ICMP_MINLEN ) {
if( pingflags & PING_VERBOSE )
printf("packet too short (%d bytes) from %s\n", cc,
inet_ntoa(ntohl(from->sin_addr.s_addr)));
return;
}
/* Now the ICMP part */
cc -= hlen;
icp = (struct icmp *)(buf + hlen);
if( icp->icmp_type == ICMP_ECHOREPLY ) {
if( icp->icmp_id != ident )
return; /* 'Twas not our ECHO */
nreceived++;
if (timing) {
tp = (struct timeval *)&icp->icmp_data[0];
tvsub( &tv, tp );
triptime = tv.tv_sec*1000+(tv.tv_usec/1000);
tsum += triptime;
if( triptime < tmin )
tmin = triptime;
if( triptime > tmax )
tmax = triptime;
}
if( pingflags & PING_QUIET)
return;
if( pingflags & PING_FLOOD) {
putchar('\b');
fflush(stdout);
} else if( pingflags & PING_CISCO) {
putchar('.');
fflush(stdout);
} else {
printf("%d bytes from %s: icmp_seq=%d", cc,
inet_ntoa(ntohl(from->sin_addr.s_addr)),
icp->icmp_seq );
if (timing)
printf(" time=%d ms\n", triptime );
else
putchar('\n');
}
} else {
/* We've got something other than an ECHOREPLY */
if( !(pingflags & PING_VERBOSE) )
return;
printf("%d bytes from %s: ",
cc, pr_addr(ntohl(from->sin_addr.s_addr)) );
pr_icmph( icp );
}
/* Display any IP options */
/* XXX - we should eventually do this for all packets with options */
if( hlen > 20 && icp->icmp_type == ICMP_ECHOREPLY ) {
unsigned char *cp;
/*printf("%d byte IP header:\n", hlen);*/
cp = (unsigned char *)buf + sizeof(struct ip) + 3;
for( i = 0; i < PING_NROUTES; i++ ) {
unsigned long l;
l = (*cp<<24) | (*(cp+1)<<16) | (*(cp+2)<<8) | *(cp+3);
/* give the nameserver a break! */
if( l == 0 )
printf("0.0.0.0\n");
else
printf("%s\n", pr_addr(ntohl(l)) );
cp += 4;
}
}
}
/*
* I N _ C K S U M
*
* Checksum routine for Internet Protocol family headers (C Version)
*
*/
in_cksum(addr, len)
u_short *addr;
int len;
{
register int nleft = len;
register u_short *w = addr;
register int sum = 0;
u_short answer = 0;
/*
* Our algorithm is simple, using a 32 bit accumulator (sum),
* we add sequential 16 bit words to it, and at the end, fold
* back all the carry bits from the top 16 bits into the lower
* 16 bits.
*/
while( nleft > 1 ) {
sum += *w++;
nleft -= 2;
}
/* mop up an odd byte, if necessary */
if( nleft == 1 ) {
*(u_char *)(&answer) = *(u_char *)w ;
sum += answer;
}
/*
* add back carry outs from top 16 bits to low 16 bits
*/
sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
sum += (sum >> 16); /* add possible carry */
answer = ~sum; /* ones complement & truncate to 16 bits */
return (answer);
}
/*
* T V S U B
*
* Subtract 2 timeval structs: out = out - in.
*
* Out is assumed to be >= in.
*/
tvsub( out, in )
register struct timeval *out, *in;
{
if( (out->tv_usec -= in->tv_usec) < 0 ) {
out->tv_sec--;
out->tv_usec += 1000000;
}
out->tv_sec -= in->tv_sec;
}
/*
* F I N I S H
*
* Print out statistics, and give up.
* Heavily buffered STDIO is used here, so that all the statistics
* will be written with 1 sys-write call. This is nice when more
* than one copy of the program is running on a terminal; it prevents
* the statistics output from becomming intermingled.
*/
finish()
{
putchar('\n');
fflush(stdout);
printf("\n----%s PING Statistics----\n", hostname );
printf("%d packets transmitted, %d packets received",
ntransmitted, nreceived);
if (ntransmitted)
if( nreceived > ntransmitted)
printf(" -- somebody's printing up packets!\n");
else
printf(", %d%% packet loss\n",
(int) (((ntransmitted-nreceived)*100) /
ntransmitted));
if (nreceived && timing)
printf("round-trip (ms) min/avg/max = %d/%d/%d\n",
tmin, tsum / nreceived, tmax);
fflush(stdout);
if (nreceived)
exit(0);
else
exit(1);
}
static char *ttab[] = {
"Echo Reply", /* ip + seq + udata */
"Dest Unreachable", /* net, host, proto, port, frag, sr + IP */
"Source Quench", /* IP */
"Redirect", /* redirect type, gateway, + IP */
"Echo",
"Time Exceeded", /* transit, frag reassem + IP */
"Parameter Problem", /* pointer + IP */
"Timestamp", /* id + seq + three timestamps */
"Timestamp Reply", /* " */
"Info Request", /* id + sq */
"Info Reply" /* " */
};
/*
* Print a descriptive string about an ICMP header.
*/
pr_icmph( icp )
struct icmp *icp;
{
switch( icp->icmp_type ) {
case ICMP_ECHOREPLY:
printf("Echo Reply\n");
/* XXX ID + Seq + Data */
break;
case ICMP_UNREACH:
switch( icp->icmp_code ) {
case ICMP_UNREACH_NET:
printf("Destination Network Unreachable\n");
break;
case ICMP_UNREACH_HOST:
printf("Destination Host Unreachable\n");
break;
case ICMP_UNREACH_PROTOCOL:
printf("Destination Protocol Unreachable\n");
break;
case ICMP_UNREACH_PORT:
printf("Destination Port Unreachable\n");
break;
case ICMP_UNREACH_NEEDFRAG:
printf("Fragmentation needed and DF set\n");
break;
case ICMP_UNREACH_SRCFAIL:
printf("Source Route Failed\n");
break;
default:
printf("Dest Unreachable, Bad Code: 0x%x\n", icp->icmp_code );
break;
}
/* Print returned IP header information */
pr_retip( icp->icmp_data );
break;
case ICMP_SOURCEQUENCH:
printf("Source Quench\n");
pr_retip( icp->icmp_data );
break;
case ICMP_REDIRECT:
switch( icp->icmp_code ) {
case ICMP_REDIRECT_NET:
printf("Network Redirect");
break;
case ICMP_REDIRECT_HOST:
printf("Host Redirect");
break;
case ICMP_REDIRECT_TOSNET:
printf("Type of Service and Network Redirect");
break;
case ICMP_REDIRECT_TOSHOST:
printf("Type of Service and Host Redirect");
break;
default:
printf("Redirect, Bad Code: 0x%x", icp->icmp_code );
break;
}
printf(" (New addr: 0x%08x)\n", icp->icmp_hun.ih_gwaddr );
pr_retip( icp->icmp_data );
break;
case ICMP_ECHO:
printf("Echo Request\n");
/* XXX ID + Seq + Data */
break;
case ICMP_TIMXCEED:
switch( icp->icmp_code ) {
case ICMP_TIMXCEED_INTRANS:
printf("Time to live exceeded in transit\n");
break;
case ICMP_TIMXCEED_REASS:
printf("Fragment reassembly time exceeded\n");
break;
default:
printf("Time exceeded, Bad Code: 0x%x\n", icp->icmp_code );
break;
}
pr_retip( icp->icmp_data );
break;
case ICMP_PARAMPROB:
switch( icp->icmp_code ) {
case 0:
printf("Parameter problem: error detected at byte 0x%02x\n",
icp->icmp_hun.ih_pptr );
default:
printf("Unspecified parameter problem\n");
}
pr_retip( icp->icmp_data );
break;
case ICMP_TSTAMP:
printf("Timestamp\n");
/* XXX ID + Seq + 3 timestamps */
break;
case ICMP_TSTAMPREPLY:
printf("Timestamp Reply\n");
/* XXX ID + Seq + 3 timestamps */
break;
case ICMP_IREQ:
printf("Information Request\n");
/* XXX ID + Seq */
break;
case ICMP_IREQREPLY:
printf("Information Reply\n");
/* XXX ID + Seq */
break;
case ICMP_MASKREQ:
printf("Address Mask Request\n");
break;
case ICMP_MASKREPLY:
printf("Address Mask Reply\n");
break;
default:
printf("Bad ICMP type: 0x%x\n", icp->icmp_type);
}
}
/*
* Print an IP header with options.
*/
pr_iph( ip )
struct ip *ip;
{
int hlen;
unsigned char *cp;
hlen = ip->ip_hl << 2;
cp = (unsigned char *)ip + 20; /* point to options */
printf("Vr HL TOS Len ID Flg Off TTL Pro Cksm Src Dst Data\n");
printf("%1x %1x %02x %04x %04x %1x %04x %02x %02x %04x %08x %08x",
ip->ip_v, ip->ip_hl, ip->ip_tos, ip->ip_len, ip->ip_id,
((ip->ip_off)&0xe000)>>13, (ip->ip_off)&0x1fff, ip->ip_ttl, ip->ip_p,
ip->ip_sum, ntohl(ip->ip_src.s_addr), ntohl(ip->ip_dst.s_addr));
/* dump and option bytes */
while( hlen-- > 20 ) {
printf( "%02x", *cp++ );
}
printf("\n");
}
/*
* Return an ascii host address
* as a dotted quad and optionally with a hostname
*/
char *
pr_addr( l )
unsigned long l;
{
struct hostent *hp;
static char buf[80];
if((pingflags & PING_NUMERIC) ||
(hp = gethostbyaddr(&l, 4, AF_INET)) == NULL )
sprintf( buf, "%s", inet_ntoa(l) );
else
sprintf( buf, "%s (%s)", hp->h_name, inet_ntoa(l) );
return( buf );
}
/*
* Dump some info on a returned (via ICMP) IP packet.
*/
pr_retip( ip )
struct ip *ip;
{
int hlen;
unsigned char *cp;
pr_iph( ip );
hlen = ip->ip_hl << 2;
cp = (unsigned char *)ip + hlen;
if( ip->ip_p == 6 ) {
printf( "TCP: from port %d, to port %d (decimal)\n",
(*cp*256+*(cp+1)), (*(cp+2)*256+*(cp+3)) );
} else if( ip->ip_p == 17 ) {
printf( "UDP: from port %d, to port %d (decimal)\n",
(*cp*256+*(cp+1)), (*(cp+2)*256+*(cp+3)) );
}
}
//E*O*F newping.c//
exit 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment