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