[mod.computers.masscomp] ICMP network utility

masscomp-request@soma.UUCP (12/09/86)

The following is a handy networking utility that has been around for 
a long time, but may not have circulated much in Masscomp circles.
If you have a network, you may find this a useful program. It should
run on VAX, Masscomp and Sun for sure. If you find otherwise or if 
you know of more computers is works on, please advise me.
Stan, Moderator



#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
#	Makefile
#	ping.1n
#	ping.c
# This archive created: Mon Dec  8 16:08:28 1986
# By:	Stan Barber, Moderator (Masscomp Users' Society)
export PATH; PATH=/bin:/usr/bin:$PATH
echo shar: "extracting 'Makefile'" '(201 characters)'
if test -f 'Makefile'
then
	echo shar: "will not over-write existing file 'Makefile'"
else
sed 's/^	X//' << \SHAR_EOF > 'Makefile'
	XCC= ucb cc
	XCFLAGS= -O
	XDESTDIR= /usr/lbin
	X
	Xping: ping.c
	X	$(CC) $(CFLAGS) ping.c -o ping
	X
	Xinstall:ping
	X	cp ping $(DESTDIR)
	X	chown root $(DESTDIR)/ping
	X	chmod 4755 $(DESTDIR)/ping
	X
	Xclean:
	X	rm -f *.o ping
SHAR_EOF
if test 201 -ne "`wc -c < 'Makefile'`"
then
	echo shar: "error transmitting 'Makefile'" '(should have been 201 characters)'
fi
fi
echo shar: "extracting 'ping.1n'" '(1039 characters)'
if test -f 'ping.1n'
then
	echo shar: "will not over-write existing file 'ping.1n'"
else
sed 's/^	X//' << \SHAR_EOF > 'ping.1n'
	X.\" @(#)$Header$
	X.RL "LOCAL"
	X.TH PING 1N
	X.SH NAME
	Xping \- test ICMP echo packets
	X.SH SYNOPSIS
	X.B ping
	X.RB [\-d]
	Xhostname 
	X.B [data size]
	X.SH DESCRIPTION
	X.I ping\^
	Xallows testing of ICMP echo packets on a network. It is also useful
	Xfor quickly detecting if a host is down.
	X.PP
	XThe
	X.B \-d
	Xoption sets the 
	X.I SO_DEBUG
	Xflag in the
	X.I socket(2)
	Xstatement.
	X.PP
	X.I Hostname\^
	Xis the name of the host at which to direct the ICMP echo packet requests.
	XThe internet address in "dot" notation may be used as well as the 
	Xcommon name.
	XThe
	X.B "data size"
	Xfor ICMP packets may be set as well following the
	X.I hostname
	Xon the command line.
	X.PP
	X.SH FILES
	X/etc/net/hosts or /etc/hosts
	X.br
	X.SH DIAGNOSTICS
	X"unknown host" -- could not locate the hostname in the table
	X.br
	X"packet size too big" -- user-selected packet size exceeds system limits.
	X.br
	X.SH BUGS
	XMore statistics could always be gathered.
	X.br
	XThe program has to run SUID to 
	X.I root
	Xto access the ICMP socket.
	X.SH SEE ALSO
	X.I
	Xicmp(4n), socket(2), network(4)
	X.SH AUTHOR
	XMike Muuss, U.S. Army BRL
SHAR_EOF
if test 1039 -ne "`wc -c < 'ping.1n'`"
then
	echo shar: "error transmitting 'ping.1n'" '(should have been 1039 characters)'
fi
fi
echo shar: "extracting 'ping.c'" '(8538 characters)'
if test -f 'ping.c'
then
	echo shar: "will not over-write existing file 'ping.c'"
else
sed 's/^	X//' << \SHAR_EOF > 'ping.c'
	X
	X/*
	X *			P I N G . C
	X *
	X * Using the InterNet Control Message Protocol (ICMP) "ECHO" facility,
	X * measure round-trip-delays and packet loss across network paths.
	X *
	X * Author -
	X *	Mike Muuss
	X *	U. S. Army Ballistic Research Laboratory
	X *	December, 1983
	X *
	X * Target System -
	X *	4.2 BSD with MIT and BRL fixes to /sys/netinet/ip_icmp.c et.al.
	X *
	X * Status -
	X *	Public Domain.  Distribution Unlimited.
	X *
	X * Bugs -
	X *	Divide by zero if no packets return.
	X *	More statistics could always be gathered.
	X *	This program has to run SUID to ROOT to access the ICMP socket.
	X */
	X#ifndef lint
	Xstatic char RCSid[] = "@(#)$Header: ping.c,v 1.5 86/02/03 16:41:16 root Exp $ (BRL)";
	X#endif
	X
	X#include <stdio.h>
	X#include <errno.h>
	X#include <sys/time.h>
	X
	X#include <sys/param.h>
	X#include <sys/socket.h>
	X#include <sys/file.h>
	X
	X#include <netinet/in_systm.h>
	X#include <netinet/in.h>
	X#include <netinet/ip.h>
	X#include <netinet/ip_icmp.h>
	X#include <netdb.h>
	X
	X#define MAXPACKET	4096
	Xu_char	packet[MAXPACKET];
	Xint	options;
	Xextern	int errno;
	X
	Xint s;			/* Socket file descriptor */
	Xstruct hostent *hp;	/* Pointer to host info */
	Xstruct timezone tz;	/* leftover */
	X
	Xstruct sockaddr whereto;/* Who to ping */
	Xint datalen;		/* How much data */
	X
	Xchar usage[] = "Usage:  ping [-d] host [data size]\n";
	X
	Xchar *hostname;
	Xchar hnamebuf[64];
	X
	Xint ntransmitted = 1;		/* sequence # for outbound packets = #sent */
	Xint ident;
	X
	Xint nreceived = 0;		/* # of packets we got back */
	Xint timing = 0;
	Xint tmin = 999999999;
	Xint tmax = 0;
	Xint tsum = 0;			/* sum of all times, for doing average */
	Xint finish();
	X
	X/*
	X * 			M A I N
	X */
	Xmain(argc, argv)
	Xchar *argv[];
	X{
	X	struct sockaddr_in from;
	X	char **av = argv;
	X	struct sockaddr_in *to = (struct sockaddr_in *) &whereto;
	X
	X	if (argc > 0 && !strcmp(argv[1], "-d"))  {
	X		options |= SO_DEBUG;
	X		argc--, av++;
	X	}
	X	
	X	if( argc < 2 || argc > 3 )  {
	X		printf(usage);
	X		exit(1);
	X	}
	X
	X	bzero( (char *)&whereto, sizeof(struct sockaddr) );
	X	hp = gethostbyname(av[1]);
	X	if (hp) {
	X		to->sin_family = hp->h_addrtype;
	X		bcopy(hp->h_addr, (caddr_t)&to->sin_addr, hp->h_length);
	X		hostname = hp->h_name;
	X	} else {
	X		to->sin_family = AF_INET;
	X		to->sin_addr.s_addr = inet_addr(av[1]);
	X		if (to->sin_addr.s_addr == -1) {
	X			printf("ping: unknown host %s\n", av[1]);
	X			return;
	X		}
	X		strcpy(hnamebuf, argv[1]);
	X		hostname = hnamebuf;
	X	}
	X
	X	if( argc == 3 )
	X		datalen = atoi( argv[2] );
	X	else
	X		datalen = 64-8;
	X	if( datalen > MAXPACKET )  {
	X		printf("ping:  packet size too big\n");
	X		exit(1);
	X	}
	X	if (datalen >= sizeof(struct timeval))
	X		timing = 1;
	X
	X	ident = getpid() & 0xFFFF;
	X
	X	while ((s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) {
	X		perror("ping: socket");
	X		sleep(5);
	X	}
	X
	X	printf("PING %s: %d data bytes\n", hostname, datalen );
	X
	X	setlinebuf( stdout );
	X
	X	signal( SIGINT, finish );
	X
	X	catcher();	/* start things going */
	X
	X	for (;;) {
	X		int len = sizeof (packet);
	X		int fromlen = sizeof (from);
	X		int cc;
	X
	X		/* cc = recvfrom(s, buf, len, flags, from, fromlen) */
	X		if ( (cc=recvfrom(s, packet, len, 0, &from, &fromlen)) < 0) {
	X			if( errno == EINTR )
	X				continue;
	X			perror("ping: recvfrom");
	X			continue;
	X		}
	X		pr_pack( packet, cc, &from );
	X	}
	X	/*NOTREACHED*/
	X}
	X
	X/*
	X * 			C A T C H E R
	X * 
	X * This routine causes another PING to be transmitted, and then
	X * schedules another SIGALRM for 1 second from now.
	X * 
	X * Bug -
	X * 	Our sense of time will slowly skew (ie, packets will not be launched
	X * 	exactly at 1-second intervals).  This does not affect the quality
	X *	of the delay and loss statistics.
	X */
	Xcatcher()
	X{
	X	signal( SIGALRM, catcher );
	X	pinger();
	X	alarm(1);
	X}
	X
	X/*
	X * 			P I N G E R
	X * 
	X * Compose and transmit an ICMP ECHO REQUEST packet.  The IP packet
	X * will be added on by the kernel.  The ID field is our UNIX process ID,
	X * and the sequence number is an ascending integer.  The first 8 bytes
	X * of the data portion are used to hold a UNIX "timeval" struct in VAX
	X * byte-order, to compute the round-trip time.
	X */
	Xpinger()
	X{
	X	static u_char outpack[1024];
	X	register struct icmp *icp = (struct icmp *) outpack;
	X	int i, cc;
	X	register struct timeval *tp = (struct timeval *) &outpack[8];
	X	register u_char *datap = &outpack[8+sizeof(struct timeval)];
	X
	X	icp->icmp_type = ICMP_ECHO;
	X	icp->icmp_code = 0;
	X	icp->icmp_cksum = 0;
	X	icp->icmp_seq = ntransmitted++;
	X	icp->icmp_id = ident;		/* ID */
	X
	X	cc = datalen+8;			/* skips ICMP portion */
	X
	X	if( timing )
	X		gettimeofday( tp, &tz );
	X
	X	for( i=8; i<datalen; i++)	/* skip 8 for time */
	X		*datap++ = i;
	X
	X	/* Compute ICMP checksum here */
	X	icp->icmp_cksum = in_cksum( icp, cc );
	X
	X	/* cc = sendto(s, msg, len, flags, to, tolen) */
	X	i = sendto( s, outpack, cc, 0, &whereto, sizeof(struct sockaddr) );
	X
	X	if( i < 0 || i != cc )  {
	X		if( i<0 )  perror("sendto");
	X		printf("ping: wrote %s %d chars, ret=%d\n",
	X			hostname, cc, i );
	X		fflush(stdout);
	X	}
	X}
	X
	X/*
	X * 			P R _ T Y P E
	X *
	X * Convert an ICMP "type" field to a printable string.
	X */
	Xchar *
	Xpr_type( t )
	Xregister int t;
	X{
	X	static char *ttab[] = {
	X		"Echo Reply",
	X		"ICMP 1",
	X		"ICMP 2",
	X		"Dest Unreachable",
	X		"Source Quence",
	X		"Redirect",
	X		"ICMP 6",
	X		"ICMP 7",
	X		"Echo",
	X		"ICMP 9",
	X		"ICMP 10",
	X		"Time Exceeded",
	X		"Parameter Problem",
	X		"Timestamp",
	X		"Timestamp Reply",
	X		"Info Request",
	X		"Info Reply"
	X	};
	X
	X	if( t < 0 || t > 16 )
	X		return("OUT-OF-RANGE");
	X
	X	return(ttab[t]);
	X}
	X
	X/*
	X *			P R _ P A C K
	X *
	X * Print out the packet, if it came from us.  This logic is necessary
	X * because ALL readers of the ICMP socket get a copy of ALL ICMP packets
	X * which arrive ('tis only fair).  This permits multiple copies of this
	X * program to be run without having intermingled output (or statistics!).
	X */
	Xpr_pack( icp, cc, from )
	Xregister struct icmp *icp;
	Xint cc;
	Xstruct sockaddr_in *from;
	X{
	X	register long *lp = (long *) packet;
	X	register int i;
	X	struct timeval tv;
	X	struct timeval *tp = (struct timeval *) &packet[8];
	X	int triptime;
	X
	X	gettimeofday( &tv, &tz );
	X
	X	if( icp->icmp_type != ICMP_ECHOREPLY )  {
	X		char *inet_ntoa();
	X
	X		printf("%d bytes from %s: ", cc, inet_ntoa( from->sin_addr.s_addr) );
	X		printf("icmp_type=%d (%s)\n",
	X			icp->icmp_type, pr_type(icp->icmp_type) );
	X		for( i=0; i<12; i++)
	X			printf("x%2.2x: x%8.8x\n", i*sizeof(long), *lp++ );
	X		printf("icmp_code=%d\n", icp->icmp_code );
	X	}
	X	if( icp->icmp_id != ident )
	X		return;			/* 'Twas not our ECHO */
	X
	X	printf("%d bytes from %s: ", cc, inet_ntoa( from->sin_addr.s_addr) );
	X	printf("icmp_seq=%d. ", icp->icmp_seq );
	X	if (timing) {
	X		tvsub( &tv, tp );
	X		triptime = tv.tv_sec*1000+(tv.tv_usec/1000);
	X		printf("time=%d. ms\n", triptime );
	X		tsum += triptime;
	X		if( triptime < tmin )
	X			tmin = triptime;
	X		if( triptime > tmax )
	X			tmax = triptime;
	X	} else
	X		putchar('\n');
	X	nreceived++;
	X	fflush(stdout);
	X}
	X
	X
	X/*
	X *			I N _ C K S U M
	X *
	X * Checksum routine for Internet Protocol family headers (C Version)
	X *
	X */
	Xin_cksum(addr, len)
	Xu_short *addr;
	Xint len;
	X{
	X	register int nleft = len;
	X	register u_short *w = addr;
	X	register u_short answer;
	X	register int sum = 0;
	X
	X	/*
	X	 *  Our algorithm is simple, using a 32 bit accumulator (sum),
	X	 *  we add sequential 16 bit words to it, and at the end, fold
	X	 *  back all the carry bits from the top 16 bits into the lower
	X	 *  16 bits.
	X	 */
	X	while( nleft > 1 )  {
	X		sum += *w++;
	X		nleft -= 2;
	X	}
	X
	X	/* mop up an odd byte, if necessary */
	X	if( nleft == 1 )
	X		sum += *(u_char *)w;
	X
	X	/*
	X	 * add back carry outs from top 16 bits to low 16 bits
	X	 */
	X	sum += (sum >> 16);	/* add hi 16 to low 16 */
	X	answer = ~sum;		/* truncate to 16 bits */
	X	return (answer);
	X}
	X
	X/*
	X * 			T V S U B
	X * 
	X * Subtract 2 timeval structs:  out = out - in.
	X * 
	X * Out is assumed to be >= in.
	X */
	Xtvsub( out, in )
	Xregister struct timeval *out, *in;
	X{
	X	if( (out->tv_usec -= in->tv_usec) < 0 )   {
	X		out->tv_sec--;
	X		out->tv_usec += 1000000;
	X	}
	X	out->tv_sec -= in->tv_sec;
	X}
	X
	X/*
	X *			F I N I S H
	X *
	X * Print out statistics, and give up.
	X * Heavily buffered STDIO is used here, so that all the statistics
	X * will be written with 1 sys-write call.  This is nice when more
	X * than one copy of the program is running on a terminal;  it prevents
	X * the statistics output from becomming intermingled.
	X */
	Xfinish()
	X{
	X	ntransmitted--;		/* we will never hear the last one */
	X
	X	printf("\n----%s PING Statistics----\n", hostname );
	X	printf("%d packets transmitted, ", ntransmitted );
	X	printf("%d packets received, ", nreceived );
	X	if (ntransmitted)
	X	    printf("%d%% packet loss\n",
	X		(int) (((ntransmitted-nreceived)*100) / ntransmitted ) );
	X	if( nreceived && timing )
	X	    printf("round-trip (ms)  min/avg/max = %d/%d/%d\n",
	X		tmin,
	X		tsum / nreceived,
	X		tmax );
	X	fflush(stdout);
	X	exit(0);
	X}
	X
SHAR_EOF
if test 8538 -ne "`wc -c < 'ping.c'`"
then
	echo shar: "error transmitting 'ping.c'" '(should have been 8538 characters)'
fi
fi
exit 0
#	End of shell archive

guy@soma.UUCP (12/10/86)

> It should run on VAX, Masscomp and Sun for sure. If you find otherwise...

I found otherwise - or, more correctly, Doug Kingston found otherwise.  This
"ping" is based on a version prior to the one that came out in 4.3, but it
that version has the same bug Doug found in the 4.3 one; it doesn't work
with odd packet sizes on big-endian machines (like Suns and Masscomps).
Here's a "diff -c" listing of the changes needed to make it work with odd
packet sizes; however, if somebody has the 4.3 source they might want to
look at that version (and apply this fix, the 4.3 version still has that
bug).

[thanks! -- sob]

*** ping.c.BAK	Tue Dec  9 09:24:42 1986
--- ping.c	Tue Dec  9 09:28:46 1986
***************
*** 309,314 ****
--- 309,315 ----
  	register int nleft = len;
  	register u_short *w = addr;
  	register u_short answer;
+ 	u_short odd_byte = 0;
  	register int sum = 0;
  
  	/*
***************
*** 323,335 ****
  	}
  
  	/* mop up an odd byte, if necessary */
! 	if( nleft == 1 )
! 		sum += *(u_char *)w;
  
  	/*
  	 * add back carry outs from top 16 bits to low 16 bits
  	 */
! 	sum += (sum >> 16);	/* add hi 16 to low 16 */
  	answer = ~sum;		/* truncate to 16 bits */
  	return (answer);
  }
--- 324,339 ----
  	}
  
  	/* mop up an odd byte, if necessary */
! 	if( nleft == 1 ) {
! 		*(u_char *)(&odd_byte) = *(u_char *)w;
! 		sum += odd_byte;
! 	}
  
  	/*
  	 * 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);
  }