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