1 /* 2 * Copyright (c) 1987, 1988 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, 1988 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.11 (Berkeley) 05/16/89"; 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 burst = 1; 88 int ntransmitted = 0; /* sequence # for outbound packets = #sent */ 89 int ident; 90 91 int nreceived = 0; /* # of packets we got back */ 92 int timing = 0; 93 int tmin = 999999999; 94 int tmax = 0; 95 int tsum = 0; /* sum of all times, for doing average */ 96 int finish(), catcher(); 97 98 /* 99 * M A I N 100 */ 101 main(argc, argv) 102 char *argv[]; 103 { 104 struct sockaddr_in from; 105 char **av = argv; 106 char *toaddr = NULL; 107 struct sockaddr_in *to = (struct sockaddr_in *) &whereto; 108 int on = 1; 109 struct protoent *proto; 110 111 argc--, av++; 112 while (argc > 0 && *av[0] == '-') { 113 while (*++av[0]) switch (*av[0]) { 114 case 'd': 115 options |= SO_DEBUG; 116 break; 117 case 'r': 118 options |= SO_DONTROUTE; 119 break; 120 case 'v': 121 verbose++; 122 break; 123 } 124 argc--, av++; 125 } 126 if( argc < 1) { 127 printf(usage); 128 exit(1); 129 } 130 131 bzero( (char *)&whereto, sizeof(struct sockaddr) ); 132 to->sin_family = AF_INET; 133 to->sin_addr.s_addr = inet_addr(av[0]); 134 if (to->sin_addr.s_addr != -1) { 135 strcpy(hnamebuf, av[0]); 136 hostname = hnamebuf; 137 } else { 138 hp = gethostbyname(av[0]); 139 if (!hp) { 140 fprintf(stderr, "ping: %s: ", av[0]); 141 herror((char *)NULL); 142 exit(1); 143 } 144 to->sin_family = hp->h_addrtype; 145 bcopy(hp->h_addr, (caddr_t)&to->sin_addr, hp->h_length); 146 hostname = hp->h_name; 147 toaddr = inet_ntoa(to->sin_addr.s_addr); 148 } 149 150 if( argc >= 2 ) 151 datalen = atoi( av[1] ); 152 else 153 datalen = 64-8; 154 if (datalen > MAXPACKET) { 155 fprintf(stderr, "ping: packet size too large\n"); 156 exit(1); 157 } 158 if (datalen >= sizeof(struct timeval)) 159 timing = 1; 160 if (argc > 2) 161 npackets = atoi(av[2]); 162 163 ident = getpid() & 0xFFFF; 164 165 if ((proto = getprotobyname("icmp")) == NULL) { 166 fprintf(stderr, "icmp: unknown protocol\n"); 167 exit(10); 168 } 169 if ((s = socket(AF_INET, SOCK_RAW, proto->p_proto)) < 0) { 170 perror("ping: socket"); 171 exit(5); 172 } 173 if (options & SO_DEBUG) 174 setsockopt(s, SOL_SOCKET, SO_DEBUG, &on, sizeof(on)); 175 if (options & SO_DONTROUTE) 176 setsockopt(s, SOL_SOCKET, SO_DONTROUTE, &on, sizeof(on)); 177 178 printf("PING %s", hostname); 179 if (toaddr) 180 printf(" (%s)", toaddr); 181 printf(": %d data bytes\n", datalen); 182 183 184 setlinebuf( stdout ); 185 186 signal( SIGINT, finish ); 187 signal(SIGALRM, catcher); 188 189 catcher(); /* start things going */ 190 191 for (;;) { 192 int len = sizeof (packet); 193 int fromlen = sizeof (from); 194 int cc; 195 196 if ( (cc=recvfrom(s, packet, len, 0, &from, &fromlen)) < 0) { 197 if( errno == EINTR ) 198 continue; 199 perror("ping: recvfrom"); 200 continue; 201 } 202 pr_pack( packet, cc, &from ); 203 if (npackets && nreceived >= npackets) 204 finish(); 205 } 206 /*NOTREACHED*/ 207 } 208 209 /* 210 * C A T C H E R 211 * 212 * This routine causes another PING to be transmitted, and then 213 * schedules another SIGALRM for 1 second from now. 214 * 215 * Bug - 216 * Our sense of time will slowly skew (ie, packets will not be launched 217 * exactly at 1-second intervals). This does not affect the quality 218 * of the delay and loss statistics. 219 */ 220 catcher() 221 { 222 int waittime; 223 224 pinger(); 225 if (npackets == 0 || ntransmitted < npackets) 226 alarm(1); 227 else { 228 if (nreceived) { 229 waittime = 2 * tmax / 1000; 230 if (waittime == 0) 231 waittime = 1; 232 } else 233 waittime = MAXWAIT; 234 signal(SIGALRM, finish); 235 alarm(waittime); 236 } 237 } 238 239 /* 240 * P I N G E R 241 * 242 * Compose and transmit an ICMP ECHO REQUEST packet. The IP packet 243 * will be added on by the kernel. The ID field is our UNIX process ID, 244 * and the sequence number is an ascending integer. The first 8 bytes 245 * of the data portion are used to hold a UNIX "timeval" struct in VAX 246 * byte-order, to compute the round-trip time. 247 */ 248 pinger() 249 { 250 static u_char outpack[MAXPACKET]; 251 register struct icmp *icp = (struct icmp *) outpack; 252 int i, cc, n; 253 register struct timeval *tp = (struct timeval *) &outpack[8]; 254 register u_char *datap = &outpack[8+sizeof(struct timeval)]; 255 256 icp->icmp_type = ICMP_ECHO; 257 icp->icmp_code = 0; 258 icp->icmp_cksum = 0; 259 icp->icmp_seq = ntransmitted++; 260 icp->icmp_id = ident; /* ID */ 261 262 cc = datalen+8; /* skips ICMP portion */ 263 264 if (timing) 265 gettimeofday( tp, &tz ); 266 267 for( i=8; i<datalen; i++) /* skip 8 for time */ 268 *datap++ = i; 269 270 /* Compute ICMP checksum here */ 271 icp->icmp_cksum = in_cksum( icp, cc ); 272 273 /* cc = sendto(s, msg, len, flags, to, tolen) */ 274 for (n = 0; n < burst; n++) { 275 i = sendto(s, outpack, cc, 0, &whereto, sizeof(whereto)); 276 277 if( i < 0 || i != cc ) { 278 if( i<0 ) perror("sendto"); 279 printf("ping: wrote %s %d chars, ret=%d\n", 280 hostname, cc, i ); 281 fflush(stdout); 282 } 283 } 284 } 285 286 /* 287 * P R _ T Y P E 288 * 289 * Convert an ICMP "type" field to a printable string. 290 */ 291 char * 292 pr_type( t ) 293 register int t; 294 { 295 static char *ttab[] = { 296 "Echo Reply", 297 "ICMP 1", 298 "ICMP 2", 299 "Dest Unreachable", 300 "Source Quence", 301 "Redirect", 302 "ICMP 6", 303 "ICMP 7", 304 "Echo", 305 "ICMP 9", 306 "ICMP 10", 307 "Time Exceeded", 308 "Parameter Problem", 309 "Timestamp", 310 "Timestamp Reply", 311 "Info Request", 312 "Info Reply" 313 }; 314 315 if( t < 0 || t > 16 ) 316 return("OUT-OF-RANGE"); 317 318 return(ttab[t]); 319 } 320 321 /* 322 * P R _ P A C K 323 * 324 * Print out the packet, if it came from us. This logic is necessary 325 * because ALL readers of the ICMP socket get a copy of ALL ICMP packets 326 * which arrive ('tis only fair). This permits multiple copies of this 327 * program to be run without having intermingled output (or statistics!). 328 */ 329 pr_pack( buf, cc, from ) 330 char *buf; 331 int cc; 332 struct sockaddr_in *from; 333 { 334 struct ip *ip; 335 register struct icmp *icp; 336 register long *lp = (long *) packet; 337 register int i; 338 struct timeval tv; 339 struct timeval *tp; 340 int hlen, triptime; 341 342 from->sin_addr.s_addr = ntohl( from->sin_addr.s_addr ); 343 gettimeofday( &tv, &tz ); 344 345 ip = (struct ip *) buf; 346 hlen = ip->ip_hl << 2; 347 if (cc < hlen + ICMP_MINLEN) { 348 if (verbose) 349 printf("packet too short (%d bytes) from %s\n", cc, 350 inet_ntoa(ntohl(from->sin_addr.s_addr))); 351 return; 352 } 353 cc -= hlen; 354 icp = (struct icmp *)(buf + hlen); 355 if( icp->icmp_type != ICMP_ECHOREPLY ) { 356 if (verbose) { 357 printf("%d bytes from %s: ", cc, 358 inet_ntoa(ntohl(from->sin_addr.s_addr))); 359 printf("icmp_type=%d (%s)\n", 360 icp->icmp_type, pr_type(icp->icmp_type) ); 361 for( i=0; i<12; i++) 362 printf("x%2.2x: x%8.8x\n", i*sizeof(long), *lp++ ); 363 printf("icmp_code=%d\n", icp->icmp_code ); 364 } 365 return; 366 } 367 if( icp->icmp_id != ident ) 368 return; /* 'Twas not our ECHO */ 369 370 tp = (struct timeval *)&icp->icmp_data[0]; 371 printf("%d bytes from %s: ", cc, 372 inet_ntoa(ntohl(from->sin_addr.s_addr))); 373 printf("icmp_seq=%d. ", icp->icmp_seq ); 374 if (timing) { 375 tvsub( &tv, tp ); 376 triptime = tv.tv_sec*1000+(tv.tv_usec/1000); 377 printf("time=%d. ms\n", triptime ); 378 tsum += triptime; 379 if( triptime < tmin ) 380 tmin = triptime; 381 if( triptime > tmax ) 382 tmax = triptime; 383 } else 384 putchar('\n'); 385 nreceived++; 386 } 387 388 389 /* 390 * I N _ C K S U M 391 * 392 * Checksum routine for Internet Protocol family headers (C Version) 393 * 394 */ 395 in_cksum(addr, len) 396 u_short *addr; 397 int len; 398 { 399 register int nleft = len; 400 register u_short *w = addr; 401 register u_short answer; 402 register int sum = 0; 403 u_short odd_byte = 0; 404 405 /* 406 * Our algorithm is simple, using a 32 bit accumulator (sum), 407 * we add sequential 16 bit words to it, and at the end, fold 408 * back all the carry bits from the top 16 bits into the lower 409 * 16 bits. 410 */ 411 while( nleft > 1 ) { 412 sum += *w++; 413 nleft -= 2; 414 } 415 416 /* mop up an odd byte, if necessary */ 417 if( nleft == 1 ) { 418 *(u_char *)(&odd_byte) = *(u_char *)w; 419 sum += odd_byte; 420 } 421 422 /* 423 * add back carry outs from top 16 bits to low 16 bits 424 */ 425 sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ 426 sum += (sum >> 16); /* add carry */ 427 answer = ~sum; /* truncate to 16 bits */ 428 return (answer); 429 } 430 431 /* 432 * T V S U B 433 * 434 * Subtract 2 timeval structs: out = out - in. 435 * 436 * Out is assumed to be >= in. 437 */ 438 tvsub( out, in ) 439 register struct timeval *out, *in; 440 { 441 if( (out->tv_usec -= in->tv_usec) < 0 ) { 442 out->tv_sec--; 443 out->tv_usec += 1000000; 444 } 445 out->tv_sec -= in->tv_sec; 446 } 447 448 /* 449 * F I N I S H 450 * 451 * Print out statistics, and give up. 452 * Heavily buffered STDIO is used here, so that all the statistics 453 * will be written with 1 sys-write call. This is nice when more 454 * than one copy of the program is running on a terminal; it prevents 455 * the statistics output from becomming intermingled. 456 */ 457 finish() 458 { 459 printf("\n----%s PING Statistics----\n", hostname ); 460 printf("%d packets transmitted, ", ntransmitted ); 461 printf("%d packets received, ", nreceived ); 462 if (ntransmitted) { 463 if (nreceived <= ntransmitted) 464 printf("%d%% packet loss", 465 (int) (((ntransmitted-nreceived)*100) / ntransmitted)); 466 else 467 printf("%.2f responses per request", 468 (float) nreceived / (float) ntransmitted); 469 } 470 printf("\n"); 471 if (nreceived && timing) 472 printf("round-trip (ms) min/avg/max = %d/%d/%d\n", 473 tmin, 474 tsum / nreceived, 475 tmax ); 476 fflush(stdout); 477 exit(0); 478 } 479