Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

ICMP Echo program for 4.2BSD - "ping"

0 views
Skip to first unread message

mo...@shasta.arpa

unread,
Mar 11, 1985, 4:50:02 PM3/11/85
to
#! /bin/sh
: This is a shar archive. Extract with sh, not csh.
echo x - README
cat > README << '17645!Funky!Stuff!'
"Ping" is used to send ICMP Echo Requests to an IP host. It was
originally written by Larry Allen of MIT, and has been improved
and debugged by Jeff Mogul at Stanford. It is quite useful
for checking to see if a host or gateway is up and functioning.
We use it all the time for debugging network problems.

You cannot run this program with an unmodified 4.2BSD kernel.
You must change /sys/netinet/in_proto.c to give user programs
access to ICMP packets:

*** in_proto.c.old Fri Jul 29 07:14:46 1983
--- in_proto.c Sat Dec 10 16:41:51 1983
***************
*** 46,54
0,
ip_init, 0, ip_slowtimo, ip_drain,
},
! { 0, PF_INET, IPPROTO_ICMP, 0,
! icmp_input, 0, 0, 0,
! 0,
0, 0, 0, 0,
},
{ SOCK_DGRAM, PF_INET, IPPROTO_UDP, PR_ATOMIC|PR_ADDR,

--- 46,54 -----
0,
ip_init, 0, ip_slowtimo, ip_drain,
},
! { SOCK_RAW, PF_INET, IPPROTO_ICMP, PR_ATOMIC|PR_ADDR,
! icmp_input, rip_output, 0, 0,
! raw_usrreq,
0, 0, 0, 0,
},
{ SOCK_DGRAM, PF_INET, IPPROTO_UDP, PR_ATOMIC|PR_ADDR,

Note: your line numbers may vary.

Also, the /sys/netinet/ip_icmp.c shipped with 4.2 had a number
of bugs, some of which cause Unix to crash. All have been
described on net.bugs.4bsd; if you use "ping" without having
fixed these bugs, beware!

"Ping" must be run by the super-user; the makefile included
installs it with the setuid bit set, so that anybody can use
it.
17645!Funky!Stuff!
echo x - ping.1
cat > ping.1 << '17645!Funky!Stuff!'
.TH PING 1
.SH NAME
ping \- IP/ICMP echo user program
.SH SYNOPSIS
.B ping [
\-cnnn
.B ] [
\-snnn
.B ] [
\-t
.B ] [
\-q
.B ]
hostname
.B [
...
.B ]
.SH DESCRIPTION
.I Ping
is used to send an ICMP (Internet Control Message Protocol) ``Echo Me''
packet to a host; it waits for a reply to see if the host responds.
Since every IP host is required to respond to ICMP packets,
this is a simple way to determine if a host is up.
.PP
If more than one host name argument is given, the hosts are pinged
in order.
.SH OPTIONS
The following options are recognized. Note that numeric arguments
follow the option flags immediately, without intervening spaces.
.IP "\-cnnn" 1i
For each host specified, send the echo
.I nnn
times. If
.I nnn
is zero or not given, then send the echo (practically) forever.
.IP "\-snnn" 1i
Make the packets
.I nnn
bytes long.
.IP "\-t" 1i
Print timing information, in milliseconds per round trip.
.IP "\-q" 1i
(Quiet mode) Don't print a line per packet, print summary line only.
.SH BUGS
By changing the default length you may create a situation where
Unix may send the echo packet but will drop the response, thus
confusing the issue.
.PP
Since the Unix hostname software is abysmally slow, it often
takes longer to look up the hostname than it does to exchange
packets. Specifying a host number (e.g., 10.1.0.11) works faster.
.PP
Timing resolution is 10 mSec; (un)fortunately round trip times
are usually longer than this.

17645!Funky!Stuff!
echo x - makefile
cat > makefile << '17645!Funky!Stuff!'
# makefile
# for ping (ICMP echo) program
#
# 14 December 1983 Jeffrey Mogul Stanford
#
DESTDIR= /bin
CFLAGS= -O

ping: ping.o resolve_host.o
cc -o ping ping.o resolve_host.o

install: ping
install -s ping $(DESTDIR)/ping
chown root $(DESTDIR)/ping
chmod u+s $(DESTDIR)/ping

clean:
rm -f *.o *.BAK *.CKP ping ping.shar a.out core

shar:
shar README ping.1 makefile ping.c resolve_host.c >ping.shar
17645!Funky!Stuff!
echo x - ping.c
cat > ping.c << '17645!Funky!Stuff!'
/*
* ping.c
*
* Send ICMP Echo Request packets.
*
* HISTORY:
* 7 March 1985 Jeffrey Mogul Stanford
* - Fixed sign-extension bug in non-VAX version of checksum
* routine.
* 27 February 1985 Jeffrey Mogul Stanford
* - Avoid all-zero ICMP messages (tickles TOPS-20 bug)
* (well, the bug is partly in 4.2BSD)
* - flush stdout after each host, for logging to disk
* 22 February 1985 Jeff Mogul Stanford
* - per request, added INTR trapping
* - -c0 means "nearly infinite count"
* 20 February 1985 Jeff Mogul Stanford
* - added -t (round-trip time), -q (quiet) options
* 14 December 1983 Jeffrey Mogul Stanford
* - added -c [count] and -s [packet size] options
*
* ? November 1983 Larry Allen MIT
* - Created.
*/

#include <stdio.h>
#include <sys/types.h>
#include <sys/time.h>
#include <errno.h>
#include <signal.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>

#define MAXPKTSIZ 576
#define DATASIZE sizeof(struct ip) + 16
#define TIMEOUT 5

#define MAXINT (((unsigned long)(-1))>>1)
/* works on a two's-complement machine */

struct pingpkt {
struct icmp p_hdr;
char p_data[DATASIZE];
} outpkt;
u_char inbuf[MAXPKTSIZ];
struct sockaddr_in sa; /* foreign sockaddr */
char *fname;

extern errno;

int quiet = 0;
int interrupted = 0;

main(argc, argv)
int argc;
char *argv[];
{
register int s;
register struct sockaddr_in *sap;
register struct protoent *pr;
register int i,j;
extern int alarm_int();
extern int intr_int();
extern struct sockaddr_in *resolve_host();
int packet_size = sizeof(outpkt);
int repeat_count = 1;
char *cmd_name = argv[0];
char *buffer;
int timed = 0;
double rtt; /* round-trip time in milliseconds */
double total_time;
double max_time;
double min_time;
int successes;

while ((argc > 2) && (argv[1][0] == '-')) {
switch (argv[1][1]) {
case 'c': /* -cnnn */
repeat_count = atoi(&argv[1][2]);
if (repeat_count == 0)
repeat_count = MAXINT;
break;
case 'q': /* -q */
quiet++;
break;
case 's': /* -snnn */
packet_size = atoi(&argv[1][2]);
if (packet_size < sizeof(long)) {
fprintf(stderr, "%s: -s%d is too short\n",
cmd_name, packet_size);
exit(1);
}
break;
case 't': /* -t */
timed++;
break;
default:
fprintf(stderr, "%s: Unknown flag -%c\n",
cmd_name, argv[1][1]);
Usage(cmd_name);
exit(1);
}
argc--;
argv++;
}

if (argc < 2) {
Usage(cmd_name);
exit (1);
}

signal (SIGALRM, alarm_int);

if ((pr = getprotobyname("icmp")) == NULL) {
perror("Can't resolve icmp protocol");
exit(1);
}

if ((buffer = (char *)malloc(packet_size + sizeof(outpkt))) == 0) {
perror("malloc failure");
exit(1);
}

for (i = 1; (i < argc) && (!interrupted) ; i++) {
fname = argv[i];

if ((sap = resolve_host(fname)) == NULL) {
printf("Don't know host %s.\n", fname);
continue;
}

sa = *sap; /* copy the beast */

if ((s = socket(sa.sin_family, SOCK_RAW, pr->p_proto)) < 0) {
perror("Can't open socket");
exit(1);
}


outpkt.p_hdr.icmp_type = ICMP_ECHO;
outpkt.p_hdr.icmp_code = 0;
#ifndef SEND_ZEROS
/*
* Some TOPS-20 systems can't handle all-zero ICMPs,
* so we set the ID and Sequence fields non-zero.
*/
outpkt.p_hdr.icmp_seq = 1;
outpkt.p_hdr.icmp_id = getpid()&0xFFFF;
#endif SEND_ZEROS

outpkt.p_hdr.icmp_cksum = 0;
outpkt.p_hdr.icmp_cksum =
~cksum (&outpkt, (sizeof (outpkt) + 1) >> 1);

if (connect(s, &sa, sizeof(sa)) < 0) {
perror("connect error");
exit(1);
}

bcopy(&outpkt, buffer, sizeof(outpkt));

total_time = 0.0;
max_time = 0.0;
min_time = TIMEOUT*2000.0; /* twice max possible */
successes = 0;

signal (SIGINT, intr_int);

for (j = 0; (j < repeat_count) && (!interrupted); j++) {
if (timed) {
if (DoPing(s, buffer, packet_size, &rtt)) {
total_time += rtt;
if (rtt > max_time)
max_time = rtt;
if (rtt < min_time)
min_time = rtt;
successes++;
}
}
else
if (DoPing(s, buffer, packet_size, 0))
successes++;
}

if ((repeat_count > 1) || quiet) {
/* print summary line only if it is interesting */
printf(" %s: %d/%d successes", fname,
successes, j);
if (successes)
printf(" (%.0f%%)",
(successes * 100.0)/(j + 0.0));
if (timed) {
if (successes) {
printf(": %.0f min, %.0f max, %.0f avg. mSec/pkt",
min_time, max_time,
total_time/(successes + 0.0));
}
}
printf("\n");
fflush(stdout);

signal (SIGINT, SIG_DFL);
}

close(s);
}
exit(0);
}

DoPing(s, buf, size, rttp)
int s;
char *buf;
int size;
double *rttp;
{
register struct icmp *picmp;
struct timeval start, finish;

if (rttp)
gettimeofday(&start, 0);

if (send(s, buf, size, 0) != size) {
perror("send error");
exit(1);
}

alarm(TIMEOUT);

for (;;) {
if (recv(s, inbuf, MAXPKTSIZ, 0) <= 0) {
if (errno == EINTR)
return(0);
perror("recv error");
exit(1);
}

picmp = (struct icmp *)inbuf;
if (picmp->icmp_type == ICMP_ECHOREPLY) {
alarm(0);
if (rttp) {
long rtt_usec;

gettimeofday(&finish, 0);

rtt_usec = finish.tv_usec - start.tv_usec;
rtt_usec += 1000000 *
(finish.tv_sec - start.tv_sec);
*rttp = (rtt_usec + 0.0)/1000.0;
if (!quiet)
printf ("%s responding, %.0f mSec.\n", fname,
*rttp);
}
else if (!quiet)
printf ("%s responding.\n", fname);
return(1);
}
}
}

int alarm_int (signo)
int signo;
{
if (!quiet)
printf("%s not responding\n", fname);
}

int intr_int (signo)
int signo;
{
if (!quiet)
printf("\n");
interrupted++;
}

int cksum (ibuf, ilen)

/* compute the 16-bit one's complement checksum of the specified
* buffer. The buffer length is specified in 16-bit words.
*/
short *ibuf;
int ilen;
{
register unsigned short *buf = (unsigned short *)ibuf;
register int len = ilen;
register int sum;

sum = 0;
while (len-- > 0) {
#ifdef VAX
asm ("addw2 (r11)+,r9");
asm ("adwc $0, r9");
#else
sum += *buf++; /* *buf unsigned, so no sign extension */
if (sum & 0x10000)
/* add 1 (end-around carry) and subtract the 0x10000 */
sum -= 0xFFFF;
#endif
}
return (sum & 0xFFFF);
}

Usage(us)
char *us;
{
fprintf(stderr,
"usage: %s [-cnnn] [-snnn] [-t] [-q] host [...]\n", us);
}
17645!Funky!Stuff!
echo x - resolve_host.c
cat > resolve_host.c << '17645!Funky!Stuff!'
/* resolve_host.c */

#include <stdio.h>
#include <sys/types.h>
#include <errno.h>
#include <signal.h>
#include <sys/socket.h>
#include <netdb.h>
#include <ctype.h>
#include <netinet/in.h>


struct sockaddr_in *
resolve_host(name)

/* Resolve the specified host name into an internet address. The "name" may
* be either a character string name, or an address in the form a.b.c.d where
* the pieces are octal, decimal, or hex numbers. Returns a pointer to a
* sockaddr_in struct (note this structure is statically allocated and must
* be copied), or NULL if the name is unknown.
*/

register char *name;
{
register struct hostent *fhost;
struct in_addr fadd;
static struct sockaddr_in sa;

if (!(isdigit(name[0])) && ((fhost = gethostbyname(name)) != NULL)) {
sa.sin_family = fhost->h_addrtype;
sa.sin_port = 0;
bcopy(fhost->h_addr, &sa.sin_addr, fhost->h_length);
} else {
fadd.s_addr = inet_addr(name);
if ((fadd.s_addr != -1) || !strcmp(name, "255.255.255.255")) {
sa.sin_family = AF_INET; /* grot */
sa.sin_port = 0;
sa.sin_addr.s_addr = fadd.s_addr;
} else
return(NULL);
}
return(&sa);
}
17645!Funky!Stuff!

0 new messages