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