Commit | Line | Data |
---|---|---|
f773330d KB |
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 | |
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[] = | |
20 | "@(#) Copyright (c) 1987 Regents of the University of California.\n\ | |
21 | All rights reserved.\n"; | |
22 | #endif /* not lint */ | |
23 | ||
9a0e2985 | 24 | #ifndef lint |
55a48892 | 25 | static char sccsid[] = "@(#)ping.c 4.10 (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 MK |
86 | int npackets; |
87 | int ntransmitted = 0; /* sequence # for outbound packets = #sent */ | |
3310aa34 MK |
88 | int ident; |
89 | ||
90 | int nreceived = 0; /* # of packets we got back */ | |
9a0e2985 | 91 | int timing = 0; |
3310aa34 MK |
92 | int tmin = 999999999; |
93 | int tmax = 0; | |
94 | int tsum = 0; /* sum of all times, for doing average */ | |
9a0e2985 | 95 | int finish(), catcher(); |
3310aa34 MK |
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; | |
a4faa0ff | 105 | char *toaddr = NULL; |
3310aa34 | 106 | struct sockaddr_in *to = (struct sockaddr_in *) &whereto; |
9a0e2985 | 107 | int on = 1; |
8b11365c | 108 | struct protoent *proto; |
9a0e2985 MK |
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 | } | |
3310aa34 MK |
123 | argc--, av++; |
124 | } | |
9a0e2985 | 125 | if( argc < 1) { |
3310aa34 MK |
126 | printf(usage); |
127 | exit(1); | |
128 | } | |
129 | ||
130 | bzero( (char *)&whereto, sizeof(struct sockaddr) ); | |
8b11365c MK |
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; | |
3310aa34 | 136 | } else { |
8b11365c | 137 | hp = gethostbyname(av[0]); |
55a48892 KB |
138 | if (!hp) { |
139 | fprintf(stderr, "ping: %s: ", av[0]); | |
140 | herror((char *)NULL); | |
8b11365c | 141 | exit(1); |
3310aa34 | 142 | } |
55a48892 KB |
143 | to->sin_family = hp->h_addrtype; |
144 | bcopy(hp->h_addr, (caddr_t)&to->sin_addr, hp->h_length); | |
145 | hostname = hp->h_name; | |
146 | toaddr = inet_ntoa(to->sin_addr.s_addr); | |
3310aa34 MK |
147 | } |
148 | ||
9a0e2985 MK |
149 | if( argc >= 2 ) |
150 | datalen = atoi( av[1] ); | |
3310aa34 MK |
151 | else |
152 | datalen = 64-8; | |
8b11365c MK |
153 | if (datalen > MAXPACKET) { |
154 | fprintf(stderr, "ping: packet size too large\n"); | |
155 | exit(1); | |
156 | } | |
9a0e2985 MK |
157 | if (datalen >= sizeof(struct timeval)) |
158 | timing = 1; | |
159 | if (argc > 2) | |
160 | npackets = atoi(av[2]); | |
3310aa34 MK |
161 | |
162 | ident = getpid() & 0xFFFF; | |
163 | ||
8b11365c MK |
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) { | |
3310aa34 | 169 | perror("ping: socket"); |
9a0e2985 | 170 | exit(5); |
3310aa34 | 171 | } |
9a0e2985 MK |
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)); | |
3310aa34 | 176 | |
a4faa0ff MK |
177 | printf("PING %s", hostname); |
178 | if (toaddr) | |
179 | printf(" (%s)", toaddr); | |
180 | printf(": %d data bytes\n", datalen); | |
181 | ||
3310aa34 | 182 | |
8b11365c | 183 | setlinebuf( stdout ); |
3310aa34 MK |
184 | |
185 | signal( SIGINT, finish ); | |
9a0e2985 | 186 | signal(SIGALRM, catcher); |
3310aa34 MK |
187 | |
188 | catcher(); /* start things going */ | |
189 | ||
190 | for (;;) { | |
191 | int len = sizeof (packet); | |
192 | int fromlen = sizeof (from); | |
193 | int cc; | |
194 | ||
9a0e2985 | 195 | if ( (cc=recvfrom(s, packet, len, 0, &from, &fromlen)) < 0) { |
3310aa34 MK |
196 | if( errno == EINTR ) |
197 | continue; | |
198 | perror("ping: recvfrom"); | |
199 | continue; | |
200 | } | |
201 | pr_pack( packet, cc, &from ); | |
9a0e2985 MK |
202 | if (npackets && nreceived >= npackets) |
203 | finish(); | |
3310aa34 MK |
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 | { | |
9a0e2985 MK |
221 | int waittime; |
222 | ||
3310aa34 | 223 | pinger(); |
9a0e2985 MK |
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 | } | |
3310aa34 MK |
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 | { | |
8b11365c | 249 | static u_char outpack[MAXPACKET]; |
3310aa34 MK |
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 | ||
9a0e2985 MK |
263 | if (timing) |
264 | gettimeofday( tp, &tz ); | |
3310aa34 MK |
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 | */ | |
824f4533 MK |
326 | pr_pack( buf, cc, from ) |
327 | char *buf; | |
3310aa34 MK |
328 | int cc; |
329 | struct sockaddr_in *from; | |
330 | { | |
824f4533 MK |
331 | struct ip *ip; |
332 | register struct icmp *icp; | |
3310aa34 MK |
333 | register long *lp = (long *) packet; |
334 | register int i; | |
335 | struct timeval tv; | |
824f4533 MK |
336 | struct timeval *tp; |
337 | int hlen, triptime; | |
3310aa34 MK |
338 | |
339 | from->sin_addr.s_addr = ntohl( from->sin_addr.s_addr ); | |
340 | gettimeofday( &tv, &tz ); | |
341 | ||
824f4533 MK |
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); | |
3310aa34 | 352 | if( icp->icmp_type != ICMP_ECHOREPLY ) { |
9a0e2985 | 353 | if (verbose) { |
8b11365c MK |
354 | printf("%d bytes from %s: ", cc, |
355 | inet_ntoa(ntohl(from->sin_addr.s_addr))); | |
9a0e2985 MK |
356 | printf("icmp_type=%d (%s)\n", |
357 | icp->icmp_type, pr_type(icp->icmp_type) ); | |
358 | for( i=0; i<12; i++) | |
8b11365c | 359 | printf("x%2.2x: x%8.8x\n", i*sizeof(long), *lp++ ); |
9a0e2985 | 360 | printf("icmp_code=%d\n", icp->icmp_code ); |
9a0e2985 MK |
361 | } |
362 | return; | |
3310aa34 MK |
363 | } |
364 | if( icp->icmp_id != ident ) | |
365 | return; /* 'Twas not our ECHO */ | |
366 | ||
824f4533 | 367 | tp = (struct timeval *)&icp->icmp_data[0]; |
8b11365c MK |
368 | printf("%d bytes from %s: ", cc, |
369 | inet_ntoa(ntohl(from->sin_addr.s_addr))); | |
3310aa34 | 370 | printf("icmp_seq=%d. ", icp->icmp_seq ); |
9a0e2985 MK |
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'); | |
3310aa34 | 382 | nreceived++; |
3310aa34 MK |
383 | } |
384 | ||
385 | ||
386 | /* | |
387 | * I N _ C K S U M | |
388 | * | |
8b11365c | 389 | * Checksum routine for Internet Protocol family headers (C Version) |
3310aa34 | 390 | * |
3310aa34 MK |
391 | */ |
392 | in_cksum(addr, len) | |
393 | u_short *addr; | |
394 | int len; | |
395 | { | |
8b11365c MK |
396 | register int nleft = len; |
397 | register u_short *w = addr; | |
398 | register u_short answer; | |
399 | register int sum = 0; | |
e03fd703 | 400 | u_short odd_byte = 0; |
3310aa34 MK |
401 | |
402 | /* | |
8b11365c MK |
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. | |
3310aa34 | 407 | */ |
8b11365c | 408 | while( nleft > 1 ) { |
3310aa34 MK |
409 | sum += *w++; |
410 | nleft -= 2; | |
411 | } | |
8b11365c MK |
412 | |
413 | /* mop up an odd byte, if necessary */ | |
e03fd703 KB |
414 | if( nleft == 1 ) { |
415 | *(u_char *)(&odd_byte) = *(u_char *)w; | |
416 | sum += odd_byte; | |
417 | } | |
3310aa34 MK |
418 | |
419 | /* | |
8b11365c | 420 | * add back carry outs from top 16 bits to low 16 bits |
3310aa34 | 421 | */ |
b55680d9 MK |
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 */ | |
8b11365c | 425 | return (answer); |
3310aa34 MK |
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 | { | |
3310aa34 MK |
456 | printf("\n----%s PING Statistics----\n", hostname ); |
457 | printf("%d packets transmitted, ", ntransmitted ); | |
458 | printf("%d packets received, ", nreceived ); | |
9a0e2985 MK |
459 | if (ntransmitted) |
460 | printf("%d%% packet loss", | |
3310aa34 | 461 | (int) (((ntransmitted-nreceived)*100) / ntransmitted ) ); |
9a0e2985 MK |
462 | printf("\n"); |
463 | if (nreceived && timing) | |
464 | printf("round-trip (ms) min/avg/max = %d/%d/%d\n", | |
3310aa34 MK |
465 | tmin, |
466 | tsum / nreceived, | |
467 | tmax ); | |
468 | fflush(stdout); | |
469 | exit(0); | |
470 | } |