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