From: Mandler <rcm@ssd.ray.com>
Newsgroups: comp.os.vxworks
Subject: re:  ping command available?
Date: Fri, 22 Jan 1993 11:03:48 -0500
Organization: Lawrence Berkeley Laboratory, Berkeley CA
NNTP-Posting-Host: 128.3.112.16
Originator: daemon@vxw.ee.lbl.gov

To thoff and other interested parties,
Here is a "quick and dirty" version of the ping program that we ported to
VxWorks.
    ______________________________________________________________
   |  Roland C Mandler,  Raytheon Submarine Signal Division       |
   |  1847 West Main Road,  Mail Stop 188                         |
   |  Portsmouth, RI  02871-1087  (401) 847-8000 (X4228)          |
   |                                                              |
   |      {uiucdcs,uunet}!rayssd!sgfb.ssd.ray.com!rcm             |
   |______________________________________________________________|


 /* Includes, defines and global variables used between functions.
 */
#include "vxWorks.h"
#include "mbuf.h"
#include "ctype.h"
#include "socket.h"
#include "if.h"
#include "ioLib.h"
#include "ioctl.h"
#include "iosLib.h"
#include "stdioLib.h"
#include "selectLib.h"
#include "sockLib.h"
#include "in_systm.h"
#include "in.h"
#include "ip.h"
#include "ip_icmp.h"
#include "taskLib.h"
#include "hostLib.h" 
#include "wdLib.h"
#include "errno.h"

extern int	errno;

/* defines */
/*
 * Beware that the outgoing packet starts with the ICMP header and
 * does not include the IP header (the kernel prepends that for us).
 * But, the received packet includes the IP header.
 */
#define	DEF_DATALEN	  56	/* default data area after ICMP header */
#ifndef	INADDR_NONE
#define	INADDR_NONE	0xffffffff	/* should be in <in.h> */
#endif
#ifndef	MAXHOSTNAMELEN
#define MAXHOSTNAMELEN	  64	/* should be defined in <param.h> */
#endif
#define MAXMSG             1    /* number of messages pipe will hold */
#define MAXPIPE            1    /* number of bytes in pipe message */
#define	MAXPACKET	4096	/* max packet size */
#define	MAXWAIT		  10	/* max time to wait for response, sec. */
				/* used only for final receive */
#define	NROUTES		9	/* number of record route slots */
#define PF_ICMP         1       /* ICMP protocol */
#define QUIET		2	/* quiet flag */
#define	RROUTE		8	/* record route flag */
#define	SIZE_ICMP_HDR	   8	/* 8-byte ICMP header */
#define	SIZE_TIME_DATA	   8	/* then the BSD timeval struct (ICMP "data") */

/* variables */
int		datalen;	/* size of data after the ICMP header */
				   /* may be 0 */
				   /* if >= SIZE_TIME_DATA, timing is done */
char		*destdotaddr;
char		*hostname;
short		ident;		/* masked process ID, to identify returns */
int		mytask;         /* full process ID */
int		npackets;	/* max # of packets to send; 0 if no limit */
int		nreceived;	/* # of packets we got back */
int		ntransmitted;	/* sequence # for outbound packets = #sent */
int             pingflags;
int		packsize;	/* size of ICMP packets to send */
				   /* this includes the 8-byte ICMP header */
char            pipecode;
int             pipefd; /* pipe file descriptor */
int             pipestat;
u_char		recvpack[MAXPACKET];	/* the received packet */
char rspace[3+4*NROUTES+1];	/* record route space */
long            sbytes;
u_char		sendpack[MAXPACKET];	/* the packet we send */
int		sockfd;	/* socket file descriptor */
int             time_start;
int             time_ret;
int		timing;		/* true if time-stamp in each packet */
int		tmin;		/* min round-trip time */
int		tmax;		/* max round-trip time */
long		tsum;		/* sum of all round-trip times, for average */
			/* above 3 times are in milliseconds */
int		verbose;	/* enables additional error messages */

/* structures */
struct fd_set   fdset; /*file descriptor bit mapping for select */
struct fd_set  *fdst;
struct sockaddr_in	dest;	/* who to ping */
WDOG_ID wdtmr;


/* routines */
char		*inet_ntoa();	/* BSD library routine */
VOID             ping_task();
VOID             finish();	/* our function to finish up and exit */
VOID             stp();
VOID             timeout_hndl();
VOID             parseOptions();

/*
 * Copyright (c) 1987 Regents of the University of California.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms are permitted
 * provided that the above copyright notice and this paragraph are
 * duplicated in all such forms and that any documentation,
 * advertising materials, and other materials related to such
 * distribution and use acknowledge that the software was developed
 * by the University of California, Berkeley.  The name of the
 * University may not be used to endorse or promote products derived
 * from this software without specific prior written permission.
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

#ifndef lint
char copyright[] =
"@(#) Copyright (c) 1987 Regents of the University of California.\n\
 All rights reserved.\n";
#endif /* not lint */

#ifndef lint
static char sccsid[] = "@(#)ping.c	4.10 (Berkeley) 10/10/88";
#endif /* not lint */


/*
 *			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
 *
 * This version was modified to run under vxWorks by:
 *      Roland Mandler
 *      Raytheon Submarine Signal Division
 *      June, 1991
 *
 * 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.
 */

char usage[] =
"\nUsage: \
ping [-dnqrvRs] host [datasize [count]] \n\
\n\
    STANDARD OPTIONS: \n\
      -s = continuous    -d = debug    -q = quiet    -v = verbose \n\
      -R = record route  -r = route to interface \n\
\n\
";
char	hnamebuf[MAXHOSTNAMELEN];
char	*pname;

ping(strng)
char strng[];
{
        /* spawn off ping task - makes it easier to kill */
        taskSpawn(NULL, 1, VX_STDIO | VX_DEALLOC_STACK,20000,&ping_task,
                    strng,0,0,0,0,0,0,0,0,0);
}

VOID ping_task(strng)
char strng[];
{
	int sockoptions,on;
        int host;
        int pargc;
	int i, i1, i2, i3, i4;
	unsigned char ttl, loop;
	struct in_addr ifaddr;
        char *argvpar[11];
        char **pargv = argvpar;

        /* parse input string into argc, argv format */
        parseOptions(&pargc,pargv,strng);

	on = 1;
	pname = pargv[0];
	pargc--;
	pargv++;

        fdst = (struct fd_set *)&fdset;
	sockoptions = 0;
        pingflags = 0;
        ntransmitted = 0;
        nreceived = 0;
        verbose = 0;
        timing = 0;
        tmin = 0;
        tmax = 0;
        npackets = 1; /* default is send one ping */
        tmin = 0;
        tmax = 0;
        tsum = 0;
        wdtmr = wdCreate();
	while (pargc > 0 && *pargv[0] == '-') {
		while (*++pargv[0]) switch (*pargv[0]) {
			case 'd': /* debug on */
				sockoptions |= SO_DEBUG;
				break;
			case 'r': /* route to intfc */
				sockoptions |= SO_DONTROUTE;
				break;
			case 'v': /* verbose input error printout */
				verbose++;
				break;
                        case 's': /* continuous pinging at 1 Hz */
                                npackets = 0; /* 1 Hz continuous pinging */
                                printf("\n\nTYPE 'stp' TO ABORT \n\n");
                                break;
			case 'q': /* disables ping send/recv messages */
				pingflags |= QUIET;
				break;
			case 'R': /* record route */
				pingflags |= RROUTE;
				break;
			default:
				goto print_usage;
		}
		pargc--, pargv++;
	}
        if (pargc < 1) {
print_usage:	printf(usage);
		return;
	}

	/*
	 * Assume the host is specified by numbers (Internet dotted-decimal)
	 * and call inet_addr() to convert it.  If that doesn't work, then
	 * assume its a name and call hostGetByName() to look it up.
	 */

	bzero((char *) &dest, sizeof(dest));
	dest.sin_family = AF_INET;

	if ( (dest.sin_addr.s_addr = htons(inet_addr(pargv[0]))) != INADDR_NONE) {
		strcpy(hnamebuf, pargv[0]);
		hostname = hnamebuf;
		destdotaddr = NULL;
	} else {
		if ( (host = htons(hostGetByName(pargv[0]))) == NULL) {
			printf("host name error: %s ", pargv[0]);
		}
		dest.sin_family = AF_INET;
		dest.sin_addr.s_addr = host;
                strcpy(hnamebuf,pargv[0]);
		hostname = hnamebuf;
		destdotaddr = inet_ntoa(dest.sin_addr.s_addr);
				/* convert to dotted-decimal notation */
	}

	/*
	 * If the user specifies a size, that is the size of the data area
	 * following the ICMP header that is transmitted. 	 */

	if (pargc >= 2)
                sscanf(pargv[1],"%d",&datalen);
	else
		datalen = DEF_DATALEN;

	packsize = datalen + SIZE_ICMP_HDR;
	if (packsize > MAXPACKET)
		printf("packet size too large");
	if (datalen >= SIZE_TIME_DATA)
		timing = 1; 

	/*
	 * The user can specify the maximum number of packets to receive.
	 */

	if (pargc > 2) {
                sscanf(pargv[2],"%d",&npackets);
                printf("\n\nTYPE 'stp' TO ABORT \n\n");
        }

	/*
	 * Fetch our process ID.  We use that as the "ident" field
	 * in the ICMP header, to identify this process' packets.
	 * This allows multiple copies of ping to be running on a host
	 * at the same time.  This identifier is needed to separate
	 * the received ICMP packets (since all readers of an ICMP
	 * socket get all the received packets).
	 */

        mytask = taskIdSelf();
	ident = mytask & 0x7fff;

	/*
	 * Create the socket.
	 */

	if ( (sockfd = socket(AF_INET, SOCK_RAW, PF_ICMP)) < 0) 
		printf("can't create raw socket");
	if (sockoptions & SO_DEBUG)
		if (setsockopt(sockfd, SOL_SOCKET, SO_DEBUG, &on,
								sizeof(on)) < 0)
			printf("setsockopt SO_DEBUG error");
	if (sockoptions & SO_DONTROUTE)
		if (setsockopt(sockfd, SOL_SOCKET, SO_DONTROUTE, &on,
								sizeof(on)) < 0)
			printf("setsockopt SO_DONTROUTE error");
	/* Record Route option */
	if( pingflags & RROUTE ) {
#ifdef IP_OPTIONS
		rspace[IPOPT_OPTVAL] = IPOPT_RR;
		rspace[IPOPT_OLEN] = sizeof(rspace)-1;
		rspace[IPOPT_OFFSET] = IPOPT_MINOFF;
		if( setsockopt(sockfd, IPPROTO_IP, IP_OPTIONS, rspace, sizeof(rspace)) < 0 ) {
			printf( "Record route" );
			return;
		}
#else
		printf( "ping: record route not available on this machine.\n" );
		return;
#endif IP_OPTIONS
	}


        /* create a communication pipe */
        pipeDevCreate("ping_pipe",MAXMSG,MAXPIPE); 

        if ((pipefd = open("ping_pipe", O_RDWR, (int) NULL)) == ERROR)
        {
	    printf("error: can't open pipe.\n");
            return;
        }

        /* set up bit map to select on pipe or socket i/o */
        FD_SET(sockfd, fdst);
        FD_SET(pipefd, fdst);

	do_ping();	/* start the output going */
        return;
}

/* This routine is the watchdog timer handler. It writes a timeout indication
 * to the pipe, which is in turn handled within 'do_ping' */

VOID timeout_hndl()
{
    write(pipefd, "t", 1);
}


/* This routine controls ping origination and reception */

do_ping()
{
	int	finflag;
	register int		n;
	int			fromlen;
	struct sockaddr_in	from;
        int                     cont;

        for ( ; ; ) {

       	    send_ping();		/* first send a packet */

	    if (npackets != 0) 
		/*
		 * We've sent the specified number of packets.
		 * But, we can't just terminate, as there is at least one
		 * packet still to be received (the one we sent at the
		 * beginning of this function).
		 * Just wait 10 seconds.
		 */
                 wdStart (wdtmr, MAXWAIT * sysClkRateGet(), timeout_hndl);

            /* wait for either socket or pipe data availability */

            if ( select (32,&fdset,NULL,NULL,NULL) > 0 ) {
                if (FD_ISSET(pipefd,fdst)) {
                    pipestat = read (pipefd,&pipecode, MAXPIPE);
                    finish();
                    break; /* escape the loop and exit the program */
                }  /* pipe data pending */

                if (FD_ISSET(sockfd,fdst)) {

		    fromlen = sizeof(from);
		    if ( (n = recvfrom(sockfd, recvpack, sizeof(recvpack), 0,
				(struct sockaddr *) &from, &fromlen)) < 0) {
			    if (errno == EINTR)
				continue;	/* normal */
			    printf("recvfrom error");
                            break;
                    }
                    /* cancel 10 second timeout */
                    wdCancel(wdtmr);

                    /* check the receive packet */
		    pr_pack(recvpack, n, &from);

		    /*
		     * If we're only supposed to receive a certain number of
		     * packets, and we've reached the limit, stop.
		     */

		    if (npackets && (nreceived >= npackets)) {
			finish();
                        break;
                    }
                    /* wait 1 second before pinging again */
                    taskDelay(sysClkRateGet()); 

                } /* socket data pending */
            } /* select data available */
	} /* loop */
        return;
}
/*
 * Compose and transmit an ICMP ECHO REQUEST packet.  The IP header
 * will be prepended by the kernel.  The ID field is our process ID,
 * and the sequence number is an ascending integer.  The first 8 bytes
 * of the data portion are used to hold a BSD "timeval" struct in host
 * byte-order, to compute the round-trip time of each packet.
 */


send_ping()
{
	register int		i;
	register struct icmp	*icp;		/* ICMP header */
	register u_char		*uptr;		/* start of user data */

	/*
	 * Fill in the ICMP header.
	 */

	icp = (struct icmp *) sendpack;	/* pointer to ICMP header */
	icp->icmp_type  = ICMP_ECHO;
	icp->icmp_code  = 0;
	icp->icmp_cksum = 0;	     /* init to 0, then call in_cksum() below */
	icp->icmp_id    = ident;     /* our pid, to identify on return */
	icp->icmp_seq   = ntransmitted++;	/* sequence number */

	/*
	 * And fill in the remainder of the packet with the user data.
	 * We just set each byte of udata[i] to i (although this is
	 * not verified when the echoed packet is received back).
	 */

	uptr = &sendpack[SIZE_ICMP_HDR + SIZE_TIME_DATA];
	for (i = SIZE_TIME_DATA; i < datalen; i++)
		*uptr++ = i;

	/*
	 * Compute and store the ICMP checksum (now that we've filled
	 * in the entire ICMP packet).  The checksum includes the ICMP
	 * header, the time stamp, and our user data.
	 */

	icp->icmp_cksum = icmp_cksum(icp, packsize);

	/*
	 * Add the time stamp of when we sent it.
	 */

	if (timing)
                time_start = tickGet();

	/*
	 * Now send the datagram.
	 */

	i = sendto(sockfd, sendpack, packsize, 0,
			(struct sockaddr *) &dest, sizeof(dest));

       /* tell the user it went out */
        if ((pingflags & QUIET) == 0)
        {
	    printf("PING %s", hostname);
	    if (destdotaddr)
		printf(" (%s)", destdotaddr);
	    printf(": %d data bytes\n", datalen);
	    setlinebuf(stdout);		/* one line at a time */
        }

	if (i < 0 || i != packsize)  {
		if (i < 0)
			printf("sendto error");
		else
			printf("wrote %s %d bytes, return=%d",
						hostname, packsize, i);
	}
}
/*
 *			I C M P _ C K S U M
 *
 * Checksum routine for Internet Protocol family headers (C Version)
 *
 */
icmp_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 carry */
	answer = ~sum;				/* truncate to 16 bits */
	return (answer);
}


/*
 * 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;	/* ptr to start of IP header */
int			cc;	/* total size of received packet */
struct sockaddr_in	*from;	/* address of sender */
{
	int			i, iphdrlen, triptime;
	struct ip		*ip;		/* ptr to IP header */
	register struct icmp	*icp;		/* ptr to ICMP header */
	long			*lp;
	struct timeval		tv;
	char			*pr_type();

	from->sin_addr.s_addr = ntohl(from->sin_addr.s_addr);

	if (timing)
                time_ret = tickGet();

	/*
	 * We have to look at the IP header, to get its length.
	 * We also verify that what follows the IP header contains at
	 * least an ICMP header (8 bytes minimum).
	 */

	ip = (struct ip *) buf;
	iphdrlen = ip->ip_hl << 2;	/* convert # 16-bit words to #bytes */
	if (cc < iphdrlen + ICMP_MINLEN) {
		if (verbose)
			printf("packet too short (%d bytes) from %s\n", cc,
				inet_ntoa(ntohl(from->sin_addr.s_addr)));
		return;
	}
	cc -= iphdrlen;

	icp = (struct icmp *)(buf + iphdrlen);
	if (icp->icmp_type != ICMP_ECHOREPLY) {
		/*
		 * The received ICMP packet is not an echo reply.
		 * If the verbose flag was set, we print the first 48 bytes
		 * of the received packet as 12 longs.
		 */

		if (verbose) {
			lp = (long *) buf;	/* to print 12 longs */
			printf("%d bytes from %s: ", cc,
				inet_ntoa(ntohl(from->sin_addr.s_addr)));
			printf("icmp_type=%d (%s)\n",
				icp->icmp_type, pr_type(icp->icmp_type));
			for (i = 0; i < 12; i++)
			    printf("x%2.2x: x%8.8x\n", i*sizeof(long), *lp++);
			printf("icmp_code=%d\n", icp->icmp_code);
		}
		return;
	}

	/*
	 * See if we sent the packet, and if not, just ignore it.
	 */

	if (icp->icmp_id != ident)
		return;

        if ((pingflags & QUIET) == 0)
        {
	    printf("%d bytes from %s: ", cc,
				     inet_ntoa(ntohl(from->sin_addr.s_addr)));
	    printf("ping # = %d. \n ", icp->icmp_seq);
        }
	if (timing) {
		/* Calculate the round-trip time, and update the min/avg/max */		
                triptime =(time_ret - time_start) * (1000 /sysClkRateGet());
		tsum += triptime;
		if (triptime < tmin)
			tmin = triptime;
		if (triptime > tmax)
			tmax = triptime;
	} 

	nreceived++;	/* only count echo reply packets that we sent */
}

/*
 * Convert an ICMP "type" field to a printable string.
 * This is called for ICMP packets that are received that are not
 * ICMP_ECHOREPLY packets.
 */

char *
pr_type(t)
register int t;
{
	static char	*ttab[] = {
		"Echo Reply",
		"ICMP 1",
		"ICMP 2",
		"Dest Unreachable",
		"Source Quence",
		"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]);
}

/*
 *	parseOptions.c
 * 	Generate Unix style (argc, argv) parameters from an incoming
 *	VxWorks command string.  Chops up the original string with
 *	nulls to make short strings and fills the argv array with pointers
 *	to the new strings.
 *
 *	maximum of 10 parameters should be defined. (argv[11] in caller).
 *	parameters should be seperated by blanks or tabs.
 */
VOID
parseOptions(argc, argv, inpStr)
	int	*argc;			/* argument count */
	char	*argv[];		/* argument pointers */
	char	*inpStr;		/* original input line */
{
	char	*cch;			/* current character */

	cch = inpStr;
	*argc = 1;			/* always at least 1 */
	
	while (*cch != '\0')
	{
		while (((*cch == ' ') || (*cch == '	')) && (*cch != '\0'))
			cch++;

              
                if (*cch != '\0')
                {
		    argv[*argc] = cch;
		    *argc += 1;

		    while ((*cch != ' ') && (*cch != '	') && 
                           (*cch != '\0'))
			    cch++;
                    if (*cch != '\0')
		        *cch++ = '\0';
                }
	}
}

/*
 * Print out statistics, and stop.
 */


VOID finish()
{
	printf("\n----%s PING Statistics----\n", hostname );
	printf("%d packets transmitted, ", ntransmitted );
	printf("%d packets received, ", nreceived );
	if (ntransmitted)
		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 );
        wdCancel(wdtmr);
        close(pipefd);
        close(sockfd);
        printf("PING done. Hit <return>.\n");
        taskDelete(mytask);
}
/* termination of continuous pinging */

VOID stp()
{
    finish();
}
Newsgroups: comp.os.vxworks