1: #ifndef lint
   2: static char sccsid[] = "@(#)ping.c	4.5 (Berkeley) 4/14/86";
   3: #endif
   4: 
   5: /*
   6:  *			P I N G . C
   7:  *
   8:  * Using the InterNet Control Message Protocol (ICMP) "ECHO" facility,
   9:  * measure round-trip-delays and packet loss across network paths.
  10:  *
  11:  * Author -
  12:  *	Mike Muuss
  13:  *	U. S. Army Ballistic Research Laboratory
  14:  *	December, 1983
  15:  * Modified at Uc Berkeley
  16:  *
  17:  * Status -
  18:  *	Public Domain.  Distribution Unlimited.
  19:  *
  20:  * Bugs -
  21:  *	More statistics could always be gathered.
  22:  *	This program has to run SUID to ROOT to access the ICMP socket.
  23:  */
  24: 
  25: #include <stdio.h>
  26: #include <errno.h>
  27: #include <sys/time.h>
  28: 
  29: #include <sys/param.h>
  30: #include <sys/socket.h>
  31: #include <sys/file.h>
  32: 
  33: #include <netinet/in_systm.h>
  34: #include <netinet/in.h>
  35: #include <netinet/ip.h>
  36: #include <netinet/ip_icmp.h>
  37: #include <netdb.h>
  38: 
  39: #define MAXWAIT     10  /* max time to wait for response, sec. */
  40: #define MAXPACKET   4096    /* max packet size */
  41: #ifndef MAXHOSTNAMELEN
  42: #define MAXHOSTNAMELEN  64
  43: #endif
  44: 
  45: int verbose;
  46: u_char  packet[MAXPACKET];
  47: int options;
  48: extern  int errno;
  49: 
  50: int s;          /* Socket file descriptor */
  51: struct hostent *hp; /* Pointer to host info */
  52: struct timezone tz; /* leftover */
  53: 
  54: struct sockaddr whereto;/* Who to ping */
  55: int datalen;        /* How much data */
  56: 
  57: char usage[] = "Usage:  ping [-drv] host [data size] [npackets]\n";
  58: 
  59: char *hostname;
  60: char hnamebuf[MAXHOSTNAMELEN];
  61: 
  62: int npackets;
  63: int ntransmitted = 0;       /* sequence # for outbound packets = #sent */
  64: int ident;
  65: 
  66: int nreceived = 0;      /* # of packets we got back */
  67: int timing = 0;
  68: int tmin = 999999999;
  69: int tmax = 0;
  70: int tsum = 0;           /* sum of all times, for doing average */
  71: int finish(), catcher();
  72: 
  73: /*
  74:  * 			M A I N
  75:  */
  76: main(argc, argv)
  77: char *argv[];
  78: {
  79:     struct sockaddr_in from;
  80:     char **av = argv;
  81:     struct sockaddr_in *to = (struct sockaddr_in *) &whereto;
  82:     int on = 1;
  83:     struct protoent *proto;
  84: 
  85:     argc--, av++;
  86:     while (argc > 0 && *av[0] == '-') {
  87:         while (*++av[0]) switch (*av[0]) {
  88:             case 'd':
  89:                 options |= SO_DEBUG;
  90:                 break;
  91:             case 'r':
  92:                 options |= SO_DONTROUTE;
  93:                 break;
  94:             case 'v':
  95:                 verbose++;
  96:                 break;
  97:         }
  98:         argc--, av++;
  99:     }
 100:     if( argc < 1)  {
 101:         printf(usage);
 102:         exit(1);
 103:     }
 104: 
 105:     bzero( (char *)&whereto, sizeof(struct sockaddr) );
 106:     to->sin_family = AF_INET;
 107:     to->sin_addr.s_addr = inet_addr(av[0]);
 108:     if (to->sin_addr.s_addr != -1) {
 109:         strcpy(hnamebuf, av[0]);
 110:         hostname = hnamebuf;
 111:     } else {
 112:         hp = gethostbyname(av[0]);
 113:         if (hp) {
 114:             to->sin_family = hp->h_addrtype;
 115:             bcopy(hp->h_addr, (caddr_t)&to->sin_addr, hp->h_length);
 116:             hostname = hp->h_name;
 117:         } else {
 118:             printf("%s: unknown host %s\n", argv[0], av[0]);
 119:             exit(1);
 120:         }
 121:     }
 122: 
 123:     if( argc >= 2 )
 124:         datalen = atoi( av[1] );
 125:     else
 126:         datalen = 64-8;
 127:     if (datalen > MAXPACKET) {
 128:         fprintf(stderr, "ping: packet size too large\n");
 129:         exit(1);
 130:     }
 131:     if (datalen >= sizeof(struct timeval))
 132:         timing = 1;
 133:     if (argc > 2)
 134:         npackets = atoi(av[2]);
 135: 
 136:     ident = getpid() & 0xFFFF;
 137: 
 138:     if ((proto = getprotobyname("icmp")) == NULL) {
 139:         fprintf(stderr, "icmp: unknown protocol\n");
 140:         exit(10);
 141:     }
 142:     if ((s = socket(AF_INET, SOCK_RAW, proto->p_proto)) < 0) {
 143:         perror("ping: socket");
 144:         exit(5);
 145:     }
 146:     if (options & SO_DEBUG)
 147:         setsockopt(s, SOL_SOCKET, SO_DEBUG, &on, sizeof(on));
 148:     if (options & SO_DONTROUTE)
 149:         setsockopt(s, SOL_SOCKET, SO_DONTROUTE, &on, sizeof(on));
 150: 
 151:     printf("PING %s: %d data bytes\n", hostname, datalen );
 152: 
 153:     setlinebuf( stdout );
 154: 
 155:     signal( SIGINT, finish );
 156:     signal(SIGALRM, catcher);
 157: 
 158:     catcher();  /* start things going */
 159: 
 160:     for (;;) {
 161:         int len = sizeof (packet);
 162:         int fromlen = sizeof (from);
 163:         int cc;
 164: 
 165:         if ( (cc=recvfrom(s, packet, len, 0, &from, &fromlen)) < 0) {
 166:             if( errno == EINTR )
 167:                 continue;
 168:             perror("ping: recvfrom");
 169:             continue;
 170:         }
 171:         pr_pack( packet, cc, &from );
 172:         if (npackets && nreceived >= npackets)
 173:             finish();
 174:     }
 175:     /*NOTREACHED*/
 176: }
 177: 
 178: /*
 179:  * 			C A T C H E R
 180:  *
 181:  * This routine causes another PING to be transmitted, and then
 182:  * schedules another SIGALRM for 1 second from now.
 183:  *
 184:  * Bug -
 185:  * 	Our sense of time will slowly skew (ie, packets will not be launched
 186:  * 	exactly at 1-second intervals).  This does not affect the quality
 187:  *	of the delay and loss statistics.
 188:  */
 189: catcher()
 190: {
 191:     int waittime;
 192: 
 193:     pinger();
 194:     if (npackets == 0 || ntransmitted < npackets)
 195:         alarm(1);
 196:     else {
 197:         if (nreceived) {
 198:             waittime = 2 * tmax / 1000;
 199:             if (waittime == 0)
 200:                 waittime = 1;
 201:         } else
 202:             waittime = MAXWAIT;
 203:         signal(SIGALRM, finish);
 204:         alarm(waittime);
 205:     }
 206: }
 207: 
 208: /*
 209:  * 			P I N G E R
 210:  *
 211:  * Compose and transmit an ICMP ECHO REQUEST packet.  The IP packet
 212:  * will be added on by the kernel.  The ID field is our UNIX process ID,
 213:  * and the sequence number is an ascending integer.  The first 8 bytes
 214:  * of the data portion are used to hold a UNIX "timeval" struct in VAX
 215:  * byte-order, to compute the round-trip time.
 216:  */
 217: pinger()
 218: {
 219:     static u_char outpack[MAXPACKET];
 220:     register struct icmp *icp = (struct icmp *) outpack;
 221:     int i, cc;
 222:     register struct timeval *tp = (struct timeval *) &outpack[8];
 223:     register u_char *datap = &outpack[8+sizeof(struct timeval)];
 224: 
 225:     icp->icmp_type = ICMP_ECHO;
 226:     icp->icmp_code = 0;
 227:     icp->icmp_cksum = 0;
 228:     icp->icmp_seq = ntransmitted++;
 229:     icp->icmp_id = ident;       /* ID */
 230: 
 231:     cc = datalen+8;         /* skips ICMP portion */
 232: 
 233:     if (timing)
 234:         gettimeofday( tp, &tz );
 235: 
 236:     for( i=8; i<datalen; i++)   /* skip 8 for time */
 237:         *datap++ = i;
 238: 
 239:     /* Compute ICMP checksum here */
 240:     icp->icmp_cksum = in_cksum( icp, cc );
 241: 
 242:     /* cc = sendto(s, msg, len, flags, to, tolen) */
 243:     i = sendto( s, outpack, cc, 0, &whereto, sizeof(struct sockaddr) );
 244: 
 245:     if( i < 0 || i != cc )  {
 246:         if( i<0 )  perror("sendto");
 247:         printf("ping: wrote %s %d chars, ret=%d\n",
 248:             hostname, cc, i );
 249:         fflush(stdout);
 250:     }
 251: }
 252: 
 253: /*
 254:  * 			P R _ T Y P E
 255:  *
 256:  * Convert an ICMP "type" field to a printable string.
 257:  */
 258: char *
 259: pr_type( t )
 260: register int t;
 261: {
 262:     static char *ttab[] = {
 263:         "Echo Reply",
 264:         "ICMP 1",
 265:         "ICMP 2",
 266:         "Dest Unreachable",
 267:         "Source Quence",
 268:         "Redirect",
 269:         "ICMP 6",
 270:         "ICMP 7",
 271:         "Echo",
 272:         "ICMP 9",
 273:         "ICMP 10",
 274:         "Time Exceeded",
 275:         "Parameter Problem",
 276:         "Timestamp",
 277:         "Timestamp Reply",
 278:         "Info Request",
 279:         "Info Reply"
 280:     };
 281: 
 282:     if( t < 0 || t > 16 )
 283:         return("OUT-OF-RANGE");
 284: 
 285:     return(ttab[t]);
 286: }
 287: 
 288: /*
 289:  *			P R _ P A C K
 290:  *
 291:  * Print out the packet, if it came from us.  This logic is necessary
 292:  * because ALL readers of the ICMP socket get a copy of ALL ICMP packets
 293:  * which arrive ('tis only fair).  This permits multiple copies of this
 294:  * program to be run without having intermingled output (or statistics!).
 295:  */
 296: pr_pack( buf, cc, from )
 297: char *buf;
 298: int cc;
 299: struct sockaddr_in *from;
 300: {
 301:     struct ip *ip;
 302:     register struct icmp *icp;
 303:     register long *lp = (long *) packet;
 304:     register int i;
 305:     struct timeval tv;
 306:     struct timeval *tp;
 307:     int hlen, triptime;
 308:     char *inet_ntoa();
 309: 
 310:     from->sin_addr.s_addr = ntohl( from->sin_addr.s_addr );
 311:     gettimeofday( &tv, &tz );
 312: 
 313:     ip = (struct ip *) buf;
 314:     hlen = ip->ip_hl << 2;
 315:     if (cc < hlen + ICMP_MINLEN) {
 316:         if (verbose)
 317:             printf("packet too short (%d bytes) from %s\n", cc,
 318:                 inet_ntoa(ntohl(from->sin_addr.s_addr)));
 319:         return;
 320:     }
 321:     cc -= hlen;
 322:     icp = (struct icmp *)(buf + hlen);
 323:     if( icp->icmp_type != ICMP_ECHOREPLY )  {
 324:         if (verbose) {
 325:             printf("%d bytes from %s: ", cc,
 326:                 inet_ntoa(ntohl(from->sin_addr.s_addr)));
 327:             printf("icmp_type=%d (%s)\n",
 328:                 icp->icmp_type, pr_type(icp->icmp_type) );
 329:             for( i=0; i<12; i++)
 330:                 printf("x%2.2x: x%8.8x\n", i*sizeof(long), *lp++ );
 331:             printf("icmp_code=%d\n", icp->icmp_code );
 332:         }
 333:         return;
 334:     }
 335:     if( icp->icmp_id != ident )
 336:         return;         /* 'Twas not our ECHO */
 337: 
 338:     tp = (struct timeval *)&icp->icmp_data[0];
 339:     printf("%d bytes from %s: ", cc,
 340:         inet_ntoa(ntohl(from->sin_addr.s_addr)));
 341:     printf("icmp_seq=%d. ", icp->icmp_seq );
 342:     if (timing) {
 343:         tvsub( &tv, tp );
 344:         triptime = tv.tv_sec*1000+(tv.tv_usec/1000);
 345:         printf("time=%d. ms\n", triptime );
 346:         tsum += triptime;
 347:         if( triptime < tmin )
 348:             tmin = triptime;
 349:         if( triptime > tmax )
 350:             tmax = triptime;
 351:     } else
 352:         putchar('\n');
 353:     nreceived++;
 354: }
 355: 
 356: 
 357: /*
 358:  *			I N _ C K S U M
 359:  *
 360:  * Checksum routine for Internet Protocol family headers (C Version)
 361:  *
 362:  */
 363: in_cksum(addr, len)
 364: u_short *addr;
 365: int len;
 366: {
 367:     register int nleft = len;
 368:     register u_short *w = addr;
 369:     register u_short answer;
 370:     register int sum = 0;
 371: 
 372:     /*
 373: 	 *  Our algorithm is simple, using a 32 bit accumulator (sum),
 374: 	 *  we add sequential 16 bit words to it, and at the end, fold
 375: 	 *  back all the carry bits from the top 16 bits into the lower
 376: 	 *  16 bits.
 377: 	 */
 378:     while( nleft > 1 )  {
 379:         sum += *w++;
 380:         nleft -= 2;
 381:     }
 382: 
 383:     /* mop up an odd byte, if necessary */
 384:     if( nleft == 1 )
 385:         sum += *(u_char *)w;
 386: 
 387:     /*
 388: 	 * add back carry outs from top 16 bits to low 16 bits
 389: 	 */
 390:     sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
 391:     sum += (sum >> 16);         /* add carry */
 392:     answer = ~sum;              /* truncate to 16 bits */
 393:     return (answer);
 394: }
 395: 
 396: /*
 397:  * 			T V S U B
 398:  *
 399:  * Subtract 2 timeval structs:  out = out - in.
 400:  *
 401:  * Out is assumed to be >= in.
 402:  */
 403: tvsub( out, in )
 404: register struct timeval *out, *in;
 405: {
 406:     if( (out->tv_usec -= in->tv_usec) < 0 )   {
 407:         out->tv_sec--;
 408:         out->tv_usec += 1000000;
 409:     }
 410:     out->tv_sec -= in->tv_sec;
 411: }
 412: 
 413: /*
 414:  *			F I N I S H
 415:  *
 416:  * Print out statistics, and give up.
 417:  * Heavily buffered STDIO is used here, so that all the statistics
 418:  * will be written with 1 sys-write call.  This is nice when more
 419:  * than one copy of the program is running on a terminal;  it prevents
 420:  * the statistics output from becomming intermingled.
 421:  */
 422: finish()
 423: {
 424:     printf("\n----%s PING Statistics----\n", hostname );
 425:     printf("%d packets transmitted, ", ntransmitted );
 426:     printf("%d packets received, ", nreceived );
 427:     if (ntransmitted)
 428:         printf("%d%% packet loss",
 429:         (int) (((ntransmitted-nreceived)*100) / ntransmitted ) );
 430:     printf("\n");
 431:     if (nreceived && timing)
 432:         printf("round-trip (ms)  min/avg/max = %d/%d/%d\n",
 433:         tmin,
 434:         tsum / nreceived,
 435:         tmax );
 436:     fflush(stdout);
 437:     exit(0);
 438: }

Defined functions

catcher defined in line 189; used 3 times
finish defined in line 422; used 4 times
in_cksum defined in line 363; used 1 times
main defined in line 76; never used
pinger defined in line 217; used 1 times
pr_pack defined in line 296; used 1 times
pr_type defined in line 258; used 1 times
tvsub defined in line 403; used 1 times

Defined variables

datalen defined in line 55; used 7 times
hnamebuf defined in line 60; used 2 times
hostname defined in line 59; used 5 times
hp defined in line 51; used 6 times
ident defined in line 64; used 3 times
npackets defined in line 62; used 5 times
nreceived defined in line 66; used 7 times
ntransmitted defined in line 63; used 6 times
options defined in line 47; used 4 times
s defined in line 50; used 5 times
sccsid defined in line 2; never used
timing defined in line 67; used 4 times
tmax defined in line 69; used 4 times
tmin defined in line 68; used 3 times
tsum defined in line 70; used 2 times
tz defined in line 52; used 2 times
usage defined in line 57; used 1 times
verbose defined in line 45; used 3 times
whereto defined in line 54; used 3 times

Defined macros

MAXHOSTNAMELEN defined in line 42; used 2 times
MAXPACKET defined in line 40; used 3 times
MAXWAIT defined in line 39; used 1 times
Last modified: 1986-04-14
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 1502
Valid CSS Valid XHTML 1.0 Strict