|
|
1.1 ! root 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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.