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: }