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