1: /* 2: * Copyright (c) 1989, 1993 3: * The Regents of the University of California. All rights reserved. 4: * 5: * This code is derived from software contributed to Berkeley by 6: * Mike Muuss. 7: * 8: * Redistribution and use in source and binary forms, with or without 9: * modification, are permitted provided that the following conditions 10: * are met: 11: * 1. Redistributions of source code must retain the above copyright 12: * notice, this list of conditions and the following disclaimer. 13: * 2. Redistributions in binary form must reproduce the above copyright 14: * notice, this list of conditions and the following disclaimer in the 15: * documentation and/or other materials provided with the distribution. 16: * 3. All advertising materials mentioning features or use of this software 17: * must display the following acknowledgement: 18: * This product includes software developed by the University of 19: * California, Berkeley and its contributors. 20: * 4. Neither the name of the University nor the names of its contributors 21: * may be used to endorse or promote products derived from this software 22: * without specific prior written permission. 23: * 24: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34: * SUCH DAMAGE. 35: */ 36: 37: #if !defined(lint) && defined(DOSCCS) 38: static char copyright[] = 39: "@(#) Copyright (c) 1989, 1993\n\ 40: The Regents of the University of California. All rights reserved.\n"; 41: 42: static char sccsid[] = "@(#)ping.c 8.1.2 (2.11BSD) 1996/1/18"; 43: #endif /* not lint */ 44: 45: /* 46: * P I N G . C 47: * 48: * Using the InterNet Control Message Protocol (ICMP) "ECHO" facility, 49: * measure round-trip-delays and packet loss across network paths. 50: * 51: * Author - 52: * Mike Muuss 53: * U. S. Army Ballistic Research Laboratory 54: * December, 1983 55: * 56: * Status - 57: * Public Domain. Distribution Unlimited. 58: * Bugs - 59: * More statistics could always be gathered. 60: * This program has to run SUID to ROOT to access the ICMP socket. 61: */ 62: 63: #include <sys/param.h> 64: #include <sys/socket.h> 65: #include <sys/file.h> 66: #include <sys/time.h> 67: #include <sys/signal.h> 68: 69: #include <netinet/in_systm.h> 70: #include <netinet/in.h> 71: #include <netinet/ip.h> 72: #include <netinet/ip_icmp.h> 73: #include <netinet/ip_var.h> 74: #include <arpa/inet.h> 75: #include <netdb.h> 76: #include <stdio.h> 77: #include <ctype.h> 78: #include <errno.h> 79: #include <string.h> 80: 81: #define DEFDATALEN (64 - 8) /* default data length */ 82: #define MAXIPLEN 60 83: #define MAXICMPLEN 76 84: #define MAXPACKET (4096 - 60 - 8) /* max packet size */ 85: #define MAXWAIT 10 /* max seconds to wait for response */ 86: #define NROUTES 9 /* number of record route slots */ 87: 88: #define A(bit) rcvd_tbl[(bit)>>3] /* identify byte in array */ 89: #define B(bit) (1 << ((bit) & 0x07)) /* identify bit in byte */ 90: #define SET(bit) (A(bit) |= B(bit)) 91: #define CLR(bit) (A(bit) &= (~B(bit))) 92: #define TST(bit) (A(bit) & B(bit)) 93: 94: /* various options */ 95: int options; 96: #define F_FLOOD 0x001 97: #define F_INTERVAL 0x002 98: #define F_NUMERIC 0x004 99: #define F_PINGFILLED 0x008 100: #define F_QUIET 0x010 101: #define F_RROUTE 0x020 102: #define F_SO_DEBUG 0x040 103: #define F_SO_DONTROUTE 0x080 104: #define F_VERBOSE 0x100 105: 106: /* 107: * MAX_DUP_CHK is the number of bits in received table, i.e. the maximum 108: * number of received sequence numbers we can keep track of. Change 512 109: * to 8192 for complete accuracy... 110: */ 111: #define MAX_DUP_CHK (8 * 512) 112: int mx_dup_ck = MAX_DUP_CHK; 113: char rcvd_tbl[MAX_DUP_CHK / 8]; 114: 115: struct sockaddr whereto; /* who to ping */ 116: int datalen = DEFDATALEN; 117: int s; /* socket file descriptor */ 118: u_char outpack[MAXPACKET]; 119: char BSPACE = '\b'; /* characters written for flood */ 120: char DOT = '.'; 121: char *hostname; 122: int ident; /* process id to identify our packets */ 123: 124: /* counters */ 125: long npackets; /* max packets to transmit */ 126: long nreceived; /* # of packets we got back */ 127: long nrepeats; /* number of duplicates */ 128: long ntransmitted; /* sequence # for outbound packets = #sent */ 129: int interval = 1; /* interval between packets */ 130: 131: /* timing */ 132: int timing; /* flag to do timing */ 133: double tmin = 999999999.0; /* minimum round trip time */ 134: double tmax = 0.0; /* maximum round trip time */ 135: double tsum = 0.0; /* sum of all times, for doing average */ 136: 137: char *pr_addr(); 138: void catcher(), finish(); 139: 140: main(argc, argv) 141: int argc; 142: char **argv; 143: { 144: extern int errno, optind; 145: extern char *optarg; 146: struct timeval timeout; 147: struct hostent *hp; 148: struct sockaddr_in *to; 149: struct protoent *proto; 150: register int i; 151: int ch, fdmask, hold, packlen, preload; 152: u_char *datap, *packet; 153: char *target, hnamebuf[MAXHOSTNAMELEN], *malloc(); 154: #ifdef IP_OPTIONS 155: char rspace[3 + 4 * NROUTES + 1]; /* record route space */ 156: #endif 157: 158: preload = 0; 159: datap = &outpack[8 + sizeof(struct timeval)]; 160: while ((ch = getopt(argc, argv, "Rc:dfh:i:l:np:qrs:v")) != EOF) 161: switch(ch) { 162: case 'c': 163: npackets = atoi(optarg); 164: if (npackets <= 0) { 165: (void)fprintf(stderr, 166: "ping: bad number of packets to transmit.\n"); 167: exit(1); 168: } 169: break; 170: case 'd': 171: options |= F_SO_DEBUG; 172: break; 173: case 'f': 174: if (getuid()) { 175: (void)fprintf(stderr, 176: "ping: %s\n", strerror(EPERM)); 177: exit(1); 178: } 179: options |= F_FLOOD; 180: setbuf(stdout, (char *)NULL); 181: break; 182: case 'i': /* wait between sending packets */ 183: interval = atoi(optarg); 184: if (interval <= 0) { 185: (void)fprintf(stderr, 186: "ping: bad timing interval.\n"); 187: exit(1); 188: } 189: options |= F_INTERVAL; 190: break; 191: case 'l': 192: preload = atoi(optarg); 193: if (preload < 0) { 194: (void)fprintf(stderr, 195: "ping: bad preload value.\n"); 196: exit(1); 197: } 198: break; 199: case 'n': 200: options |= F_NUMERIC; 201: break; 202: case 'p': /* fill buffer with user pattern */ 203: options |= F_PINGFILLED; 204: fill((char *)datap, optarg); 205: break; 206: case 'q': 207: options |= F_QUIET; 208: break; 209: case 'R': 210: options |= F_RROUTE; 211: break; 212: case 'r': 213: options |= F_SO_DONTROUTE; 214: break; 215: case 's': /* size of packet to send */ 216: datalen = atoi(optarg); 217: if (datalen > MAXPACKET) { 218: (void)fprintf(stderr, 219: "ping: packet size too large.\n"); 220: exit(1); 221: } 222: if (datalen <= 0) { 223: (void)fprintf(stderr, 224: "ping: illegal packet size.\n"); 225: exit(1); 226: } 227: break; 228: case 'v': 229: options |= F_VERBOSE; 230: break; 231: default: 232: usage(); 233: } 234: argc -= optind; 235: argv += optind; 236: 237: if (argc != 1) 238: usage(); 239: target = *argv; 240: 241: bzero((char *)&whereto, sizeof(struct sockaddr)); 242: to = (struct sockaddr_in *)&whereto; 243: to->sin_family = AF_INET; 244: to->sin_addr.s_addr = inet_addr(target); 245: if (to->sin_addr.s_addr != -1L) 246: hostname = target; 247: else { 248: hp = gethostbyname(target); 249: if (!hp) { 250: (void)fprintf(stderr, 251: "ping: unknown host %s\n", target); 252: exit(1); 253: } 254: to->sin_family = hp->h_addrtype; 255: bcopy(hp->h_addr, (caddr_t)&to->sin_addr, hp->h_length); 256: (void)strncpy(hnamebuf, hp->h_name, sizeof(hnamebuf) - 1); 257: hostname = hnamebuf; 258: } 259: 260: if (options & F_FLOOD && options & F_INTERVAL) { 261: (void)fprintf(stderr, 262: "ping: -f and -i incompatible options.\n"); 263: exit(1); 264: } 265: 266: if (datalen >= sizeof(struct timeval)) /* can we time transfer */ 267: timing = 1; 268: packlen = datalen + MAXIPLEN + MAXICMPLEN; 269: if (!(packet = (u_char *)malloc((u_int)packlen))) { 270: (void)fprintf(stderr, "ping: out of memory.\n"); 271: exit(1); 272: } 273: if (!(options & F_PINGFILLED)) 274: for (i = 8; i < datalen; ++i) 275: *datap++ = i; 276: 277: ident = getpid() & 0xFFFF; 278: 279: if (!(proto = getprotobyname("icmp"))) { 280: (void)fprintf(stderr, "ping: unknown protocol icmp.\n"); 281: exit(1); 282: } 283: if ((s = socket(AF_INET, SOCK_RAW, proto->p_proto)) < 0) { 284: perror("ping: socket"); 285: exit(1); 286: } 287: hold = 1; 288: if (options & F_SO_DEBUG) 289: (void)setsockopt(s, SOL_SOCKET, SO_DEBUG, (char *)&hold, 290: sizeof(hold)); 291: if (options & F_SO_DONTROUTE) 292: (void)setsockopt(s, SOL_SOCKET, SO_DONTROUTE, (char *)&hold, 293: sizeof(hold)); 294: 295: /* record route option */ 296: if (options & F_RROUTE) { 297: #ifdef IP_OPTIONS 298: rspace[IPOPT_OPTVAL] = IPOPT_RR; 299: rspace[IPOPT_OLEN] = sizeof(rspace)-1; 300: rspace[IPOPT_OFFSET] = IPOPT_MINOFF; 301: if (setsockopt(s, IPPROTO_IP, IP_OPTIONS, rspace, 302: sizeof(rspace)) < 0) { 303: perror("ping: record route"); 304: exit(1); 305: } 306: #else 307: (void)fprintf(stderr, 308: "ping: record route not available in this implementation.\n"); 309: exit(1); 310: #endif /* IP_OPTIONS */ 311: } 312: 313: /* 314: * When pinging the broadcast address, you can get a lot of answers. 315: * Doing something so evil is useful if you are trying to stress the 316: * ethernet, or just want to fill the arp cache to get some stuff for 317: * /etc/ethers. 318: */ 319: hold = 48 * 1024; 320: (void)setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)&hold, 321: sizeof(hold)); 322: 323: if (to->sin_family == AF_INET) 324: (void)printf("PING %s (%s): %d data bytes\n", hostname, 325: inet_ntoa(*(struct in_addr *)&to->sin_addr.s_addr), 326: datalen); 327: else 328: (void)printf("PING %s: %d data bytes\n", hostname, datalen); 329: 330: (void)signal(SIGINT, finish); 331: (void)signal(SIGALRM, catcher); 332: 333: while (preload--) /* fire off them quickies */ 334: pinger(); 335: 336: if ((options & F_FLOOD) == 0) 337: catcher(); /* start things going */ 338: 339: for (;;) { 340: struct sockaddr_in from; 341: register int cc; 342: int fromlen; 343: 344: if (options & F_FLOOD) { 345: pinger(); 346: timeout.tv_sec = 0; 347: timeout.tv_usec = 10000; 348: fdmask = 1 << s; 349: if (select(s + 1, (fd_set *)&fdmask, (fd_set *)NULL, 350: (fd_set *)NULL, &timeout) < 1) 351: continue; 352: } 353: fromlen = sizeof(from); 354: if ((cc = recvfrom(s, (char *)packet, packlen, 0, 355: (struct sockaddr *)&from, &fromlen)) < 0) { 356: if (errno == EINTR) 357: continue; 358: perror("ping: recvfrom"); 359: continue; 360: } 361: pr_pack((char *)packet, cc, &from); 362: if (npackets && nreceived >= npackets) 363: break; 364: } 365: finish(); 366: /* NOTREACHED */ 367: } 368: 369: /* 370: * catcher -- 371: * This routine causes another PING to be transmitted, and then 372: * schedules another SIGALRM for 1 second from now. 373: * 374: * bug -- 375: * Our sense of time will slowly skew (i.e., packets will not be 376: * launched exactly at 1-second intervals). This does not affect the 377: * quality of the delay and loss statistics. 378: */ 379: void 380: catcher() 381: { 382: int waittime; 383: 384: pinger(); 385: (void)signal(SIGALRM, catcher); 386: if (!npackets || ntransmitted < npackets) 387: alarm((u_int)interval); 388: else { 389: if (nreceived) { 390: waittime = 2 * tmax / 1000; 391: if (!waittime) 392: waittime = 1; 393: } else 394: waittime = MAXWAIT; 395: (void)signal(SIGALRM, finish); 396: (void)alarm((u_int)waittime); 397: } 398: } 399: 400: /* 401: * pinger -- 402: * Compose and transmit an ICMP ECHO REQUEST packet. The IP packet 403: * will be added on by the kernel. The ID field is our UNIX process ID, 404: * and the sequence number is an ascending integer. The first 8 bytes 405: * of the data portion are used to hold a UNIX "timeval" struct in VAX 406: * byte-order, to compute the round-trip time. 407: */ 408: pinger() 409: { 410: register struct icmp *icp; 411: register int cc; 412: int i; 413: 414: icp = (struct icmp *)outpack; 415: icp->icmp_type = ICMP_ECHO; 416: icp->icmp_code = 0; 417: icp->icmp_cksum = 0; 418: icp->icmp_seq = ntransmitted++; 419: icp->icmp_id = ident; /* ID */ 420: 421: CLR(icp->icmp_seq % mx_dup_ck); 422: 423: if (timing) 424: (void)gettimeofday((struct timeval *)&outpack[8], 425: (struct timezone *)NULL); 426: 427: cc = datalen + 8; /* skips ICMP portion */ 428: 429: /* compute ICMP checksum here */ 430: icp->icmp_cksum = in_cksum((u_short *)icp, cc); 431: 432: i = sendto(s, (char *)outpack, cc, 0, &whereto, 433: sizeof(struct sockaddr)); 434: 435: if (i < 0 || i != cc) { 436: if (i < 0) 437: perror("ping: sendto"); 438: (void)printf("ping: wrote %s %d chars, ret=%d\n", 439: hostname, cc, i); 440: } 441: if (!(options & F_QUIET) && options & F_FLOOD) 442: (void)write(fileno(stdout), &DOT, 1); 443: } 444: 445: /* 446: * pr_pack -- 447: * Print out the packet, if it came from us. This logic is necessary 448: * because ALL readers of the ICMP socket get a copy of ALL ICMP packets 449: * which arrive ('tis only fair). This permits multiple copies of this 450: * program to be run without having intermingled output (or statistics!). 451: */ 452: pr_pack(buf, cc, from) 453: char *buf; 454: int cc; 455: struct sockaddr_in *from; 456: { 457: register struct icmp *icp; 458: register u_long l; 459: register int i, j; 460: register u_char *cp,*dp; 461: static int old_rrlen; 462: static char old_rr[MAX_IPOPTLEN]; 463: struct ip *ip; 464: struct timeval tv, *tp; 465: double triptime; 466: int hlen, dupflag; 467: 468: (void)gettimeofday(&tv, (struct timezone *)NULL); 469: 470: /* Check the IP header */ 471: ip = (struct ip *)buf; 472: hlen = ip->ip_hl << 2; 473: if (cc < hlen + ICMP_MINLEN) { 474: if (options & F_VERBOSE) 475: (void)fprintf(stderr, 476: "ping: packet too short (%d bytes) from %s\n", cc, 477: inet_ntoa(*(struct in_addr *)&from->sin_addr.s_addr)); 478: return; 479: } 480: 481: /* Now the ICMP part */ 482: cc -= hlen; 483: icp = (struct icmp *)(buf + hlen); 484: if (icp->icmp_type == ICMP_ECHOREPLY) { 485: if (icp->icmp_id != ident) 486: return; /* 'Twas not our ECHO */ 487: ++nreceived; 488: if (timing) { 489: #ifndef icmp_data 490: tp = (struct timeval *)&icp->icmp_ip; 491: #else 492: tp = (struct timeval *)icp->icmp_data; 493: #endif 494: tvsub(&tv, tp); 495: triptime = ((double)tv.tv_sec) * 1000.0 + 496: ((double)tv.tv_usec) / 1000.0; 497: tsum += triptime; 498: if (triptime < tmin) 499: tmin = triptime; 500: if (triptime > tmax) 501: tmax = triptime; 502: } 503: 504: if (TST(icp->icmp_seq % mx_dup_ck)) { 505: ++nrepeats; 506: --nreceived; 507: dupflag = 1; 508: } else { 509: SET(icp->icmp_seq % mx_dup_ck); 510: dupflag = 0; 511: } 512: 513: if (options & F_QUIET) 514: return; 515: 516: if (options & F_FLOOD) 517: (void)write(fileno(stdout), &BSPACE, 1); 518: else { 519: (void)printf("%d bytes from %s: icmp_seq=%u", cc, 520: inet_ntoa(*(struct in_addr *)&from->sin_addr.s_addr), 521: icp->icmp_seq); 522: (void)printf(" ttl=%d", ip->ip_ttl); 523: if (timing) 524: (void)printf(" time=%g ms", triptime); 525: if (dupflag) 526: (void)printf(" (DUP!)"); 527: /* check the data */ 528: cp = (u_char*)&icp->icmp_data[8]; 529: dp = &outpack[8 + sizeof(struct timeval)]; 530: for (i = 8; i < datalen; ++i, ++cp, ++dp) { 531: if (*cp != *dp) { 532: (void)printf("\nwrong data byte #%d should be 0x%x but was 0x%x", 533: i, *dp, *cp); 534: cp = (u_char*)&icp->icmp_data[0]; 535: for (i = 8; i < datalen; ++i, ++cp) { 536: if ((i % 32) == 8) 537: (void)printf("\n\t"); 538: (void)printf("%x ", *cp); 539: } 540: break; 541: } 542: } 543: } 544: } else { 545: /* We've got something other than an ECHOREPLY */ 546: if (!(options & F_VERBOSE)) 547: return; 548: (void)printf("%d bytes from %s: ", cc, 549: pr_addr(from->sin_addr.s_addr)); 550: pr_icmph(icp); 551: } 552: 553: /* Display any IP options */ 554: cp = (u_char *)buf + sizeof(struct ip); 555: 556: for (; hlen > (int)sizeof(struct ip); --hlen, ++cp) 557: switch (*cp) { 558: case IPOPT_EOL: 559: hlen = 0; 560: break; 561: case IPOPT_LSRR: 562: (void)printf("\nLSRR: "); 563: hlen -= 2; 564: j = *++cp; 565: ++cp; 566: if (j > IPOPT_MINOFF) 567: for (;;) { 568: l = *++cp; 569: l = (l<<8) + *++cp; 570: l = (l<<8) + *++cp; 571: l = (l<<8) + *++cp; 572: if (l == 0) 573: (void)printf("\t0.0.0.0"); 574: else 575: (void)printf("\t%s", pr_addr(ntohl(l))); 576: hlen -= 4; 577: j -= 4; 578: if (j <= IPOPT_MINOFF) 579: break; 580: (void)putchar('\n'); 581: } 582: break; 583: case IPOPT_RR: 584: j = *++cp; /* get length */ 585: i = *++cp; /* and pointer */ 586: hlen -= 2; 587: if (i > j) 588: i = j; 589: i -= IPOPT_MINOFF; 590: if (i <= 0) 591: continue; 592: if (i == old_rrlen 593: && cp == (u_char *)buf + sizeof(struct ip) + 2 594: && !bcmp((char *)cp, old_rr, i) 595: && !(options & F_FLOOD)) { 596: (void)printf("\t(same route)"); 597: i = ((i + 3) / 4) * 4; 598: hlen -= i; 599: cp += i; 600: break; 601: } 602: old_rrlen = i; 603: bcopy((char *)cp, old_rr, i); 604: (void)printf("\nRR: "); 605: for (;;) { 606: l = *++cp; 607: l = (l<<8) + *++cp; 608: l = (l<<8) + *++cp; 609: l = (l<<8) + *++cp; 610: if (l == 0) 611: (void)printf("\t0.0.0.0"); 612: else 613: (void)printf("\t%s", pr_addr(ntohl(l))); 614: hlen -= 4; 615: i -= 4; 616: if (i <= 0) 617: break; 618: (void)putchar('\n'); 619: } 620: break; 621: case IPOPT_NOP: 622: (void)printf("\nNOP"); 623: break; 624: default: 625: (void)printf("\nunknown option %x", *cp); 626: break; 627: } 628: if (!(options & F_FLOOD)) { 629: (void)putchar('\n'); 630: (void)fflush(stdout); 631: } 632: } 633: 634: /* 635: * in_cksum -- 636: * Checksum routine for Internet Protocol family headers (C Version) 637: */ 638: in_cksum(addr, len) 639: u_short *addr; 640: int len; 641: { 642: register int nleft = len; 643: register u_short *w = addr; 644: u_short answer = 0; 645: long sum = 0; 646: 647: /* 648: * Our algorithm is simple, using a 32 bit accumulator (sum), we add 649: * sequential 16 bit words to it, and at the end, fold back all the 650: * carry bits from the top 16 bits into the lower 16 bits. 651: */ 652: while (nleft > 1) { 653: sum += *w++; 654: nleft -= 2; 655: } 656: 657: /* mop up an odd byte, if necessary */ 658: if (nleft == 1) { 659: *(u_char *)(&answer) = *(u_char *)w ; 660: sum += answer; 661: } 662: 663: /* add back carry outs from top 16 bits to low 16 bits */ 664: sum = (sum >> 16) + (sum & 0xffffL); /* add hi 16 to low 16 */ 665: sum += (sum >> 16); /* add carry */ 666: answer = ~sum; /* truncate to 16 bits */ 667: return(answer); 668: } 669: 670: /* 671: * tvsub -- 672: * Subtract 2 timeval structs: out = out - in. Out is assumed to 673: * be >= in. 674: */ 675: tvsub(out, in) 676: register struct timeval *out, *in; 677: { 678: if ((out->tv_usec -= in->tv_usec) < 0) { 679: --out->tv_sec; 680: out->tv_usec += 1000000; 681: } 682: out->tv_sec -= in->tv_sec; 683: } 684: 685: /* 686: * finish -- 687: * Print out statistics, and give up. 688: */ 689: void 690: finish() 691: { 692: register int i; 693: 694: (void)signal(SIGINT, SIG_IGN); 695: (void)putchar('\n'); 696: (void)fflush(stdout); 697: (void)printf("--- %s ping statistics ---\n", hostname); 698: (void)printf("%ld packets transmitted, ", ntransmitted); 699: (void)printf("%ld packets received, ", nreceived); 700: if (nrepeats) 701: (void)printf("+%ld duplicates, ", nrepeats); 702: if (ntransmitted) 703: if (nreceived > ntransmitted) 704: (void)printf("-- somebody's printing up packets!"); 705: else 706: (void)printf("%d%% packet loss", 707: (int) (((ntransmitted - nreceived) * 100) / 708: ntransmitted)); 709: (void)putchar('\n'); 710: if (nreceived && timing) { 711: /* Only display average to microseconds */ 712: i = 1000.0 * tsum / (nreceived + nrepeats); 713: (void)printf("round-trip min/avg/max = %g/%g/%g ms\n", 714: tmin, ((double)i) / 1000.0, tmax); 715: } 716: exit(0); 717: } 718: 719: #ifdef notdef 720: static char *ttab[] = { 721: "Echo Reply", /* ip + seq + udata */ 722: "Dest Unreachable", /* net, host, proto, port, frag, sr + IP */ 723: "Source Quench", /* IP */ 724: "Redirect", /* redirect type, gateway, + IP */ 725: "Echo", 726: "Time Exceeded", /* transit, frag reassem + IP */ 727: "Parameter Problem", /* pointer + IP */ 728: "Timestamp", /* id + seq + three timestamps */ 729: "Timestamp Reply", /* " */ 730: "Info Request", /* id + sq */ 731: "Info Reply" /* " */ 732: }; 733: #endif 734: 735: /* 736: * pr_icmph -- 737: * Print a descriptive string about an ICMP header. 738: */ 739: pr_icmph(icp) 740: struct icmp *icp; 741: { 742: switch(icp->icmp_type) { 743: case ICMP_ECHOREPLY: 744: (void)printf("Echo Reply\n"); 745: /* XXX ID + Seq + Data */ 746: break; 747: case ICMP_UNREACH: 748: switch(icp->icmp_code) { 749: case ICMP_UNREACH_NET: 750: (void)printf("Destination Net Unreachable\n"); 751: break; 752: case ICMP_UNREACH_HOST: 753: (void)printf("Destination Host Unreachable\n"); 754: break; 755: case ICMP_UNREACH_PROTOCOL: 756: (void)printf("Destination Protocol Unreachable\n"); 757: break; 758: case ICMP_UNREACH_PORT: 759: (void)printf("Destination Port Unreachable\n"); 760: break; 761: case ICMP_UNREACH_NEEDFRAG: 762: (void)printf("frag needed and DF set\n"); 763: break; 764: case ICMP_UNREACH_SRCFAIL: 765: (void)printf("Source Route Failed\n"); 766: break; 767: default: 768: (void)printf("Dest Unreachable, Bad Code: %d\n", 769: icp->icmp_code); 770: break; 771: } 772: /* Print returned IP header information */ 773: #ifndef icmp_data 774: pr_retip(&icp->icmp_ip); 775: #else 776: pr_retip((struct ip *)icp->icmp_data); 777: #endif 778: break; 779: case ICMP_SOURCEQUENCH: 780: (void)printf("Source Quench\n"); 781: #ifndef icmp_data 782: pr_retip(&icp->icmp_ip); 783: #else 784: pr_retip((struct ip *)icp->icmp_data); 785: #endif 786: break; 787: case ICMP_REDIRECT: 788: switch(icp->icmp_code) { 789: case ICMP_REDIRECT_NET: 790: (void)printf("Redirect Network"); 791: break; 792: case ICMP_REDIRECT_HOST: 793: (void)printf("Redirect Host"); 794: break; 795: case ICMP_REDIRECT_TOSNET: 796: (void)printf("Redirect Type of Service and Network"); 797: break; 798: case ICMP_REDIRECT_TOSHOST: 799: (void)printf("Redirect Type of Service and Host"); 800: break; 801: default: 802: (void)printf("Redirect, Bad Code: %d", icp->icmp_code); 803: break; 804: } 805: (void)printf("(New addr: 0x%08lx)\n", icp->icmp_gwaddr.s_addr); 806: #ifndef icmp_data 807: pr_retip(&icp->icmp_ip); 808: #else 809: pr_retip((struct ip *)icp->icmp_data); 810: #endif 811: break; 812: case ICMP_ECHO: 813: (void)printf("Echo Request\n"); 814: /* XXX ID + Seq + Data */ 815: break; 816: case ICMP_TIMXCEED: 817: switch(icp->icmp_code) { 818: case ICMP_TIMXCEED_INTRANS: 819: (void)printf("Time to live exceeded\n"); 820: break; 821: case ICMP_TIMXCEED_REASS: 822: (void)printf("Frag reassembly time exceeded\n"); 823: break; 824: default: 825: (void)printf("Time exceeded, Bad Code: %d\n", 826: icp->icmp_code); 827: break; 828: } 829: #ifndef icmp_data 830: pr_retip(&icp->icmp_ip); 831: #else 832: pr_retip((struct ip *)icp->icmp_data); 833: #endif 834: break; 835: case ICMP_PARAMPROB: 836: (void)printf("Parameter problem: pointer = 0x%02x\n", 837: icp->icmp_hun.ih_pptr); 838: #ifndef icmp_data 839: pr_retip(&icp->icmp_ip); 840: #else 841: pr_retip((struct ip *)icp->icmp_data); 842: #endif 843: break; 844: case ICMP_TSTAMP: 845: (void)printf("Timestamp\n"); 846: /* XXX ID + Seq + 3 timestamps */ 847: break; 848: case ICMP_TSTAMPREPLY: 849: (void)printf("Timestamp Reply\n"); 850: /* XXX ID + Seq + 3 timestamps */ 851: break; 852: case ICMP_IREQ: 853: (void)printf("Information Request\n"); 854: /* XXX ID + Seq */ 855: break; 856: case ICMP_IREQREPLY: 857: (void)printf("Information Reply\n"); 858: /* XXX ID + Seq */ 859: break; 860: #ifdef ICMP_MASKREQ 861: case ICMP_MASKREQ: 862: (void)printf("Address Mask Request\n"); 863: break; 864: #endif 865: #ifdef ICMP_MASKREPLY 866: case ICMP_MASKREPLY: 867: (void)printf("Address Mask Reply\n"); 868: break; 869: #endif 870: default: 871: (void)printf("Bad ICMP type: %d\n", icp->icmp_type); 872: } 873: } 874: 875: /* 876: * pr_iph -- 877: * Print an IP header with options. 878: */ 879: pr_iph(ip) 880: struct ip *ip; 881: { 882: int hlen; 883: u_char *cp; 884: 885: hlen = ip->ip_hl << 2; 886: cp = (u_char *)ip + 20; /* point to options */ 887: 888: (void)printf("Vr HL TOS Len ID Flg off TTL Pro cks Src Dst Data\n"); 889: (void)printf(" %1x %1x %02x %04x %04x", 890: ip->ip_v, ip->ip_hl, ip->ip_tos, ip->ip_len, ip->ip_id); 891: (void)printf(" %1x %04x", ((ip->ip_off) & 0xe000) >> 13, 892: (ip->ip_off) & 0x1fff); 893: (void)printf(" %02x %02x %04x", ip->ip_ttl, ip->ip_p, ip->ip_sum); 894: (void)printf(" %s ", inet_ntoa(*(struct in_addr *)&ip->ip_src.s_addr)); 895: (void)printf(" %s ", inet_ntoa(*(struct in_addr *)&ip->ip_dst.s_addr)); 896: /* dump and option bytes */ 897: while (hlen-- > 20) { 898: (void)printf("%02x", *cp++); 899: } 900: (void)putchar('\n'); 901: } 902: 903: /* 904: * pr_addr -- 905: * Return an ascii host address as a dotted quad and optionally with 906: * a hostname. 907: */ 908: char * 909: pr_addr(l) 910: u_long l; 911: { 912: struct hostent *hp; 913: static char buf[80]; 914: 915: if ((options & F_NUMERIC) || 916: !(hp = gethostbyaddr((char *)&l, 4, AF_INET))) 917: (void)sprintf(buf, "%s", inet_ntoa(*(struct in_addr *)&l)); 918: else 919: (void)sprintf(buf, "%s (%s)", hp->h_name, 920: inet_ntoa(*(struct in_addr *)&l)); 921: return(buf); 922: } 923: 924: /* 925: * pr_retip -- 926: * Dump some info on a returned (via ICMP) IP packet. 927: */ 928: pr_retip(ip) 929: struct ip *ip; 930: { 931: int hlen; 932: u_char *cp; 933: 934: pr_iph(ip); 935: hlen = ip->ip_hl << 2; 936: cp = (u_char *)ip + hlen; 937: 938: if (ip->ip_p == 6) 939: (void)printf("TCP: from port %u, to port %u (decimal)\n", 940: (*cp * 256 + *(cp + 1)), (*(cp + 2) * 256 + *(cp + 3))); 941: else if (ip->ip_p == 17) 942: (void)printf("UDP: from port %u, to port %u (decimal)\n", 943: (*cp * 256 + *(cp + 1)), (*(cp + 2) * 256 + *(cp + 3))); 944: } 945: 946: fill(bp, patp) 947: char *bp, *patp; 948: { 949: register int ii, jj, kk; 950: int pat[16]; 951: char *cp; 952: 953: for (cp = patp; *cp; cp++) 954: if (!isxdigit(*cp)) { 955: (void)fprintf(stderr, 956: "ping: patterns must be specified as hex digits.\n"); 957: exit(1); 958: } 959: ii = sscanf(patp, 960: "%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x", 961: &pat[0], &pat[1], &pat[2], &pat[3], &pat[4], &pat[5], &pat[6], 962: &pat[7], &pat[8], &pat[9], &pat[10], &pat[11], &pat[12], 963: &pat[13], &pat[14], &pat[15]); 964: 965: if (ii > 0) 966: for (kk = 0; 967: kk <= MAXPACKET - (8 + sizeof(struct timeval) + ii); 968: kk += ii) 969: for (jj = 0; jj < ii; ++jj) 970: bp[jj + kk] = pat[jj]; 971: if (!(options & F_QUIET)) { 972: (void)printf("PATTERN: 0x"); 973: for (jj = 0; jj < ii; ++jj) 974: (void)printf("%02x", bp[jj] & 0xFF); 975: (void)printf("\n"); 976: } 977: } 978: 979: usage() 980: { 981: (void)fprintf(stderr, 982: "usage: ping [-Rdfnqrv] [-c count] [-i wait] [-l preload]\n\t[-p pattern] [-s packetsize] host\n"); 983: exit(1); 984: }