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