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