ICMP Echo program for 4.2BSD - "ping"

mogul at Shasta.ARPA mogul at Shasta.ARPA
Tue Mar 12 07:50:02 AEST 1985


#! /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!



More information about the Comp.sources.unix mailing list