Commit | Line | Data |
---|---|---|
78288025 | 1 | /* |
e91396ee KB |
2 | * Copyright (c) 1989, 1993 |
3 | * The Regents of the University of California. All rights reserved. | |
78288025 KB |
4 | * |
5 | * This code is derived from software contributed to Berkeley by | |
6 | * Mike Muuss. | |
7 | * | |
70ab3c27 | 8 | * %sccs.include.redist.c% |
78288025 KB |
9 | */ |
10 | ||
11 | #ifndef lint | |
e91396ee KB |
12 | static char copyright[] = |
13 | "@(#) Copyright (c) 1989, 1993\n\ | |
14 | The Regents of the University of California. All rights reserved.\n"; | |
78288025 KB |
15 | #endif /* not lint */ |
16 | ||
17 | #ifndef lint | |
4d6bc69c | 18 | static char sccsid[] = "@(#)ping.c 8.3 (Berkeley) %G%"; |
78288025 KB |
19 | #endif /* not lint */ |
20 | ||
3310aa34 MK |
21 | /* |
22 | * P I N G . C | |
23 | * | |
24 | * Using the InterNet Control Message Protocol (ICMP) "ECHO" facility, | |
25 | * measure round-trip-delays and packet loss across network paths. | |
26 | * | |
27 | * Author - | |
28 | * Mike Muuss | |
29 | * U. S. Army Ballistic Research Laboratory | |
30 | * December, 1983 | |
3310aa34 MK |
31 | * |
32 | * Status - | |
33 | * Public Domain. Distribution Unlimited. | |
3310aa34 | 34 | * Bugs - |
3310aa34 MK |
35 | * More statistics could always be gathered. |
36 | * This program has to run SUID to ROOT to access the ICMP socket. | |
37 | */ | |
38 | ||
3310aa34 MK |
39 | #include <sys/param.h> |
40 | #include <sys/socket.h> | |
41 | #include <sys/file.h> | |
78288025 KB |
42 | #include <sys/time.h> |
43 | #include <sys/signal.h> | |
3310aa34 MK |
44 | |
45 | #include <netinet/in_systm.h> | |
46 | #include <netinet/in.h> | |
47 | #include <netinet/ip.h> | |
48 | #include <netinet/ip_icmp.h> | |
63ed9e52 | 49 | #include <netinet/ip_var.h> |
3310aa34 | 50 | #include <netdb.h> |
78288025 KB |
51 | #include <unistd.h> |
52 | #include <stdio.h> | |
53 | #include <ctype.h> | |
54 | #include <errno.h> | |
38dde0cd | 55 | #include <string.h> |
78288025 KB |
56 | |
57 | #define DEFDATALEN (64 - 8) /* default data length */ | |
58 | #define MAXIPLEN 60 | |
59 | #define MAXICMPLEN 76 | |
60 | #define MAXPACKET (65536 - 60 - 8)/* max packet size */ | |
61 | #define MAXWAIT 10 /* max seconds to wait for response */ | |
62 | #define NROUTES 9 /* number of record route slots */ | |
63 | ||
64 | #define A(bit) rcvd_tbl[(bit)>>3] /* identify byte in array */ | |
65 | #define B(bit) (1 << ((bit) & 0x07)) /* identify bit in byte */ | |
66 | #define SET(bit) (A(bit) |= B(bit)) | |
67 | #define CLR(bit) (A(bit) &= (~B(bit))) | |
68 | #define TST(bit) (A(bit) & B(bit)) | |
69 | ||
70 | /* various options */ | |
71 | int options; | |
72 | #define F_FLOOD 0x001 | |
73 | #define F_INTERVAL 0x002 | |
74 | #define F_NUMERIC 0x004 | |
75 | #define F_PINGFILLED 0x008 | |
76 | #define F_QUIET 0x010 | |
77 | #define F_RROUTE 0x020 | |
78 | #define F_SO_DEBUG 0x040 | |
79 | #define F_SO_DONTROUTE 0x080 | |
80 | #define F_VERBOSE 0x100 | |
3310aa34 | 81 | |
78288025 KB |
82 | /* |
83 | * MAX_DUP_CHK is the number of bits in received table, i.e. the maximum | |
84 | * number of received sequence numbers we can keep track of. Change 128 | |
85 | * to 8192 for complete accuracy... | |
86 | */ | |
87 | #define MAX_DUP_CHK (8 * 128) | |
88 | int mx_dup_ck = MAX_DUP_CHK; | |
89 | char rcvd_tbl[MAX_DUP_CHK / 8]; | |
90 | ||
91 | struct sockaddr whereto; /* who to ping */ | |
92 | int datalen = DEFDATALEN; | |
93 | int s; /* socket file descriptor */ | |
94 | u_char outpack[MAXPACKET]; | |
95 | char BSPACE = '\b'; /* characters written for flood */ | |
96 | char DOT = '.'; | |
97 | char *hostname; | |
98 | int ident; /* process id to identify our packets */ | |
3310aa34 | 99 | |
78288025 KB |
100 | /* counters */ |
101 | long npackets; /* max packets to transmit */ | |
102 | long nreceived; /* # of packets we got back */ | |
103 | long nrepeats; /* number of duplicates */ | |
104 | long ntransmitted; /* sequence # for outbound packets = #sent */ | |
105 | int interval = 1; /* interval between packets */ | |
3310aa34 | 106 | |
78288025 KB |
107 | /* timing */ |
108 | int timing; /* flag to do timing */ | |
7211977e KB |
109 | double tmin = 999999999.0; /* minimum round trip time */ |
110 | double tmax = 0.0; /* maximum round trip time */ | |
111 | double tsum = 0.0; /* sum of all times, for doing average */ | |
3310aa34 | 112 | |
896b9bf5 KB |
113 | char *pr_addr(); |
114 | void catcher(), finish(); | |
3310aa34 | 115 | |
3310aa34 | 116 | main(argc, argv) |
78288025 KB |
117 | int argc; |
118 | char **argv; | |
3310aa34 | 119 | { |
78288025 | 120 | extern int errno, optind; |
63ed9e52 | 121 | extern char *optarg; |
78288025 KB |
122 | struct timeval timeout; |
123 | struct hostent *hp; | |
124 | struct sockaddr_in *to; | |
125 | struct protoent *proto; | |
126 | register int i; | |
127 | int ch, fdmask, hold, packlen, preload; | |
128 | u_char *datap, *packet; | |
129 | char *target, hnamebuf[MAXHOSTNAMELEN], *malloc(); | |
130 | #ifdef IP_OPTIONS | |
131 | char rspace[3 + 4 * NROUTES + 1]; /* record route space */ | |
132 | #endif | |
63ed9e52 | 133 | |
78288025 KB |
134 | preload = 0; |
135 | datap = &outpack[8 + sizeof(struct timeval)]; | |
136 | while ((ch = getopt(argc, argv, "Rc:dfh:i:l:np:qrs:v")) != EOF) | |
137 | switch(ch) { | |
138 | case 'c': | |
139 | npackets = atoi(optarg); | |
140 | if (npackets <= 0) { | |
141 | (void)fprintf(stderr, | |
142 | "ping: bad number of packets to transmit.\n"); | |
143 | exit(1); | |
144 | } | |
145 | break; | |
146 | case 'd': | |
147 | options |= F_SO_DEBUG; | |
148 | break; | |
149 | case 'f': | |
150 | if (getuid()) { | |
151 | (void)fprintf(stderr, | |
73a887f0 | 152 | "ping: %s\n", strerror(EPERM)); |
78288025 KB |
153 | exit(1); |
154 | } | |
155 | options |= F_FLOOD; | |
156 | setbuf(stdout, (char *)NULL); | |
157 | break; | |
158 | case 'i': /* wait between sending packets */ | |
159 | interval = atoi(optarg); | |
160 | if (interval <= 0) { | |
161 | (void)fprintf(stderr, | |
162 | "ping: bad timing interval.\n"); | |
163 | exit(1); | |
164 | } | |
165 | options |= F_INTERVAL; | |
166 | break; | |
167 | case 'l': | |
168 | preload = atoi(optarg); | |
169 | if (preload < 0) { | |
170 | (void)fprintf(stderr, | |
171 | "ping: bad preload value.\n"); | |
172 | exit(1); | |
173 | } | |
174 | break; | |
175 | case 'n': | |
176 | options |= F_NUMERIC; | |
177 | break; | |
178 | case 'p': /* fill buffer with user pattern */ | |
179 | options |= F_PINGFILLED; | |
180 | fill((char *)datap, optarg); | |
9a0e2985 | 181 | break; |
78288025 KB |
182 | case 'q': |
183 | options |= F_QUIET; | |
184 | break; | |
185 | case 'R': | |
186 | options |= F_RROUTE; | |
187 | break; | |
188 | case 'r': | |
189 | options |= F_SO_DONTROUTE; | |
190 | break; | |
191 | case 's': /* size of packet to send */ | |
192 | datalen = atoi(optarg); | |
193 | if (datalen > MAXPACKET) { | |
194 | (void)fprintf(stderr, | |
195 | "ping: packet size too large.\n"); | |
196 | exit(1); | |
197 | } | |
198 | if (datalen <= 0) { | |
199 | (void)fprintf(stderr, | |
200 | "ping: illegal packet size.\n"); | |
63ed9e52 | 201 | exit(1); |
78288025 KB |
202 | } |
203 | break; | |
204 | case 'v': | |
205 | options |= F_VERBOSE; | |
206 | break; | |
207 | default: | |
208 | usage(); | |
9a0e2985 | 209 | } |
78288025 KB |
210 | argc -= optind; |
211 | argv += optind; | |
63ed9e52 | 212 | |
78288025 KB |
213 | if (argc != 1) |
214 | usage(); | |
215 | target = *argv; | |
3310aa34 | 216 | |
03fe9c29 | 217 | memset(&whereto, 0, sizeof(struct sockaddr)); |
78288025 | 218 | to = (struct sockaddr_in *)&whereto; |
8b11365c | 219 | to->sin_family = AF_INET; |
78288025 KB |
220 | to->sin_addr.s_addr = inet_addr(target); |
221 | if (to->sin_addr.s_addr != (u_int)-1) | |
222 | hostname = target; | |
223 | else { | |
224 | hp = gethostbyname(target); | |
225 | if (!hp) { | |
226 | (void)fprintf(stderr, | |
227 | "ping: unknown host %s\n", target); | |
8b11365c | 228 | exit(1); |
3310aa34 | 229 | } |
78288025 | 230 | to->sin_family = hp->h_addrtype; |
03fe9c29 | 231 | memmove(&to->sin_addr, hp->h_addr, hp->h_length); |
78288025 KB |
232 | (void)strncpy(hnamebuf, hp->h_name, sizeof(hnamebuf) - 1); |
233 | hostname = hnamebuf; | |
3310aa34 MK |
234 | } |
235 | ||
78288025 KB |
236 | if (options & F_FLOOD && options & F_INTERVAL) { |
237 | (void)fprintf(stderr, | |
238 | "ping: -f and -i incompatible options.\n"); | |
63ed9e52 KB |
239 | exit(1); |
240 | } | |
241 | ||
78288025 | 242 | if (datalen >= sizeof(struct timeval)) /* can we time transfer */ |
9a0e2985 | 243 | timing = 1; |
78288025 KB |
244 | packlen = datalen + MAXIPLEN + MAXICMPLEN; |
245 | if (!(packet = (u_char *)malloc((u_int)packlen))) { | |
246 | (void)fprintf(stderr, "ping: out of memory.\n"); | |
63ed9e52 KB |
247 | exit(1); |
248 | } | |
78288025 KB |
249 | if (!(options & F_PINGFILLED)) |
250 | for (i = 8; i < datalen; ++i) | |
251 | *datap++ = i; | |
3310aa34 MK |
252 | |
253 | ident = getpid() & 0xFFFF; | |
254 | ||
78288025 KB |
255 | if (!(proto = getprotobyname("icmp"))) { |
256 | (void)fprintf(stderr, "ping: unknown protocol icmp.\n"); | |
257 | exit(1); | |
8b11365c MK |
258 | } |
259 | if ((s = socket(AF_INET, SOCK_RAW, proto->p_proto)) < 0) { | |
3310aa34 | 260 | perror("ping: socket"); |
78288025 | 261 | exit(1); |
63ed9e52 | 262 | } |
78288025 KB |
263 | hold = 1; |
264 | if (options & F_SO_DEBUG) | |
265 | (void)setsockopt(s, SOL_SOCKET, SO_DEBUG, (char *)&hold, | |
266 | sizeof(hold)); | |
267 | if (options & F_SO_DONTROUTE) | |
268 | (void)setsockopt(s, SOL_SOCKET, SO_DONTROUTE, (char *)&hold, | |
269 | sizeof(hold)); | |
270 | ||
271 | /* record route option */ | |
272 | if (options & F_RROUTE) { | |
63ed9e52 KB |
273 | #ifdef IP_OPTIONS |
274 | rspace[IPOPT_OPTVAL] = IPOPT_RR; | |
275 | rspace[IPOPT_OLEN] = sizeof(rspace)-1; | |
276 | rspace[IPOPT_OFFSET] = IPOPT_MINOFF; | |
78288025 KB |
277 | if (setsockopt(s, IPPROTO_IP, IP_OPTIONS, rspace, |
278 | sizeof(rspace)) < 0) { | |
279 | perror("ping: record route"); | |
280 | exit(1); | |
63ed9e52 KB |
281 | } |
282 | #else | |
78288025 KB |
283 | (void)fprintf(stderr, |
284 | "ping: record route not available in this implementation.\n"); | |
285 | exit(1); | |
286 | #endif /* IP_OPTIONS */ | |
63ed9e52 | 287 | } |
3310aa34 | 288 | |
78288025 KB |
289 | /* |
290 | * When pinging the broadcast address, you can get a lot of answers. | |
291 | * Doing something so evil is useful if you are trying to stress the | |
292 | * ethernet, or just want to fill the arp cache to get some stuff for | |
293 | * /etc/ethers. | |
63ed9e52 | 294 | */ |
78288025 KB |
295 | hold = 48 * 1024; |
296 | (void)setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)&hold, | |
297 | sizeof(hold)); | |
298 | ||
299 | if (to->sin_family == AF_INET) | |
300 | (void)printf("PING %s (%s): %d data bytes\n", hostname, | |
301 | inet_ntoa(*(struct in_addr *)&to->sin_addr.s_addr), | |
302 | datalen); | |
303 | else | |
304 | (void)printf("PING %s: %d data bytes\n", hostname, datalen); | |
3310aa34 | 305 | |
c19f4b1f | 306 | (void)signal(SIGINT, finish); |
78288025 | 307 | (void)signal(SIGALRM, catcher); |
3310aa34 | 308 | |
78288025 | 309 | while (preload--) /* fire off them quickies */ |
63ed9e52 KB |
310 | pinger(); |
311 | ||
1070431c | 312 | if ((options & F_FLOOD) == 0) |
78288025 KB |
313 | catcher(); /* start things going */ |
314 | ||
315 | for (;;) { | |
316 | struct sockaddr_in from; | |
317 | register int cc; | |
318 | int fromlen; | |
63ed9e52 | 319 | |
78288025 | 320 | if (options & F_FLOOD) { |
63ed9e52 | 321 | pinger(); |
1070431c MK |
322 | timeout.tv_sec = 0; |
323 | timeout.tv_usec = 10000; | |
324 | fdmask = 1 << s; | |
325 | if (select(s + 1, (fd_set *)&fdmask, (fd_set *)NULL, | |
326 | (fd_set *)NULL, &timeout) < 1) | |
63ed9e52 KB |
327 | continue; |
328 | } | |
78288025 KB |
329 | fromlen = sizeof(from); |
330 | if ((cc = recvfrom(s, (char *)packet, packlen, 0, | |
331 | (struct sockaddr *)&from, &fromlen)) < 0) { | |
332 | if (errno == EINTR) | |
3310aa34 MK |
333 | continue; |
334 | perror("ping: recvfrom"); | |
335 | continue; | |
336 | } | |
78288025 | 337 | pr_pack((char *)packet, cc, &from); |
9a0e2985 | 338 | if (npackets && nreceived >= npackets) |
78288025 | 339 | break; |
3310aa34 | 340 | } |
78288025 KB |
341 | finish(); |
342 | /* NOTREACHED */ | |
3310aa34 MK |
343 | } |
344 | ||
345 | /* | |
78288025 KB |
346 | * catcher -- |
347 | * This routine causes another PING to be transmitted, and then | |
3310aa34 | 348 | * schedules another SIGALRM for 1 second from now. |
7211977e | 349 | * |
78288025 KB |
350 | * bug -- |
351 | * Our sense of time will slowly skew (i.e., packets will not be | |
352 | * launched exactly at 1-second intervals). This does not affect the | |
353 | * quality of the delay and loss statistics. | |
3310aa34 | 354 | */ |
896b9bf5 | 355 | void |
3310aa34 MK |
356 | catcher() |
357 | { | |
9a0e2985 MK |
358 | int waittime; |
359 | ||
3310aa34 | 360 | pinger(); |
78288025 KB |
361 | (void)signal(SIGALRM, catcher); |
362 | if (!npackets || ntransmitted < npackets) | |
363 | alarm((u_int)interval); | |
9a0e2985 MK |
364 | else { |
365 | if (nreceived) { | |
366 | waittime = 2 * tmax / 1000; | |
78288025 | 367 | if (!waittime) |
9a0e2985 MK |
368 | waittime = 1; |
369 | } else | |
370 | waittime = MAXWAIT; | |
78288025 KB |
371 | (void)signal(SIGALRM, finish); |
372 | (void)alarm((u_int)waittime); | |
9a0e2985 | 373 | } |
3310aa34 MK |
374 | } |
375 | ||
376 | /* | |
78288025 | 377 | * pinger -- |
7211977e | 378 | * Compose and transmit an ICMP ECHO REQUEST packet. The IP packet |
3310aa34 MK |
379 | * will be added on by the kernel. The ID field is our UNIX process ID, |
380 | * and the sequence number is an ascending integer. The first 8 bytes | |
381 | * of the data portion are used to hold a UNIX "timeval" struct in VAX | |
382 | * byte-order, to compute the round-trip time. | |
383 | */ | |
384 | pinger() | |
385 | { | |
78288025 KB |
386 | register struct icmp *icp; |
387 | register int cc; | |
388 | int i; | |
3310aa34 | 389 | |
78288025 | 390 | icp = (struct icmp *)outpack; |
3310aa34 MK |
391 | icp->icmp_type = ICMP_ECHO; |
392 | icp->icmp_code = 0; | |
393 | icp->icmp_cksum = 0; | |
f5d563c7 | 394 | icp->icmp_seq = ntransmitted++; |
78288025 | 395 | icp->icmp_id = ident; /* ID */ |
3310aa34 | 396 | |
78288025 | 397 | CLR(icp->icmp_seq % mx_dup_ck); |
3310aa34 | 398 | |
9a0e2985 | 399 | if (timing) |
78288025 KB |
400 | (void)gettimeofday((struct timeval *)&outpack[8], |
401 | (struct timezone *)NULL); | |
3310aa34 | 402 | |
78288025 | 403 | cc = datalen + 8; /* skips ICMP portion */ |
3310aa34 | 404 | |
78288025 KB |
405 | /* compute ICMP checksum here */ |
406 | icp->icmp_cksum = in_cksum((u_short *)icp, cc); | |
d93a36b6 | 407 | |
78288025 KB |
408 | i = sendto(s, (char *)outpack, cc, 0, &whereto, |
409 | sizeof(struct sockaddr)); | |
410 | ||
411 | if (i < 0 || i != cc) { | |
412 | if (i < 0) | |
413 | perror("ping: sendto"); | |
414 | (void)printf("ping: wrote %s %d chars, ret=%d\n", | |
415 | hostname, cc, i); | |
3310aa34 | 416 | } |
73a887f0 | 417 | if (!(options & F_QUIET) && options & F_FLOOD) |
78288025 | 418 | (void)write(STDOUT_FILENO, &DOT, 1); |
3310aa34 MK |
419 | } |
420 | ||
421 | /* | |
78288025 KB |
422 | * pr_pack -- |
423 | * Print out the packet, if it came from us. This logic is necessary | |
3310aa34 MK |
424 | * because ALL readers of the ICMP socket get a copy of ALL ICMP packets |
425 | * which arrive ('tis only fair). This permits multiple copies of this | |
426 | * program to be run without having intermingled output (or statistics!). | |
427 | */ | |
78288025 KB |
428 | pr_pack(buf, cc, from) |
429 | char *buf; | |
430 | int cc; | |
431 | struct sockaddr_in *from; | |
3310aa34 | 432 | { |
824f4533 | 433 | register struct icmp *icp; |
78288025 | 434 | register u_long l; |
63ed9e52 KB |
435 | register int i, j; |
436 | register u_char *cp,*dp; | |
437 | static int old_rrlen; | |
438 | static char old_rr[MAX_IPOPTLEN]; | |
78288025 KB |
439 | struct ip *ip; |
440 | struct timeval tv, *tp; | |
7211977e | 441 | double triptime; |
78288025 | 442 | int hlen, dupflag; |
3310aa34 | 443 | |
78288025 | 444 | (void)gettimeofday(&tv, (struct timezone *)NULL); |
3310aa34 | 445 | |
63ed9e52 | 446 | /* Check the IP header */ |
78288025 | 447 | ip = (struct ip *)buf; |
824f4533 | 448 | hlen = ip->ip_hl << 2; |
78288025 KB |
449 | if (cc < hlen + ICMP_MINLEN) { |
450 | if (options & F_VERBOSE) | |
451 | (void)fprintf(stderr, | |
452 | "ping: packet too short (%d bytes) from %s\n", cc, | |
453 | inet_ntoa(*(struct in_addr *)&from->sin_addr.s_addr)); | |
824f4533 MK |
454 | return; |
455 | } | |
63ed9e52 KB |
456 | |
457 | /* Now the ICMP part */ | |
824f4533 MK |
458 | cc -= hlen; |
459 | icp = (struct icmp *)(buf + hlen); | |
78288025 KB |
460 | if (icp->icmp_type == ICMP_ECHOREPLY) { |
461 | if (icp->icmp_id != ident) | |
63ed9e52 | 462 | return; /* 'Twas not our ECHO */ |
78288025 | 463 | ++nreceived; |
63ed9e52 KB |
464 | if (timing) { |
465 | #ifndef icmp_data | |
466 | tp = (struct timeval *)&icp->icmp_ip; | |
467 | #else | |
78288025 | 468 | tp = (struct timeval *)icp->icmp_data; |
63ed9e52 | 469 | #endif |
78288025 | 470 | tvsub(&tv, tp); |
7211977e KB |
471 | triptime = ((double)tv.tv_sec) * 1000.0 + |
472 | ((double)tv.tv_usec) / 1000.0; | |
63ed9e52 | 473 | tsum += triptime; |
78288025 | 474 | if (triptime < tmin) |
63ed9e52 | 475 | tmin = triptime; |
78288025 | 476 | if (triptime > tmax) |
63ed9e52 | 477 | tmax = triptime; |
9a0e2985 | 478 | } |
63ed9e52 | 479 | |
78288025 KB |
480 | if (TST(icp->icmp_seq % mx_dup_ck)) { |
481 | ++nrepeats; | |
482 | --nreceived; | |
483 | dupflag = 1; | |
484 | } else { | |
485 | SET(icp->icmp_seq % mx_dup_ck); | |
486 | dupflag = 0; | |
63ed9e52 KB |
487 | } |
488 | ||
78288025 | 489 | if (options & F_QUIET) |
63ed9e52 KB |
490 | return; |
491 | ||
78288025 KB |
492 | if (options & F_FLOOD) |
493 | (void)write(STDOUT_FILENO, &BSPACE, 1); | |
494 | else { | |
495 | (void)printf("%d bytes from %s: icmp_seq=%u", cc, | |
496 | inet_ntoa(*(struct in_addr *)&from->sin_addr.s_addr), | |
497 | icp->icmp_seq); | |
498 | (void)printf(" ttl=%d", ip->ip_ttl); | |
63ed9e52 | 499 | if (timing) |
7211977e | 500 | (void)printf(" time=%g ms", triptime); |
78288025 KB |
501 | if (dupflag) |
502 | (void)printf(" (DUP!)"); | |
63ed9e52 KB |
503 | /* check the data */ |
504 | cp = (u_char*)&icp->icmp_data[8]; | |
78288025 KB |
505 | dp = &outpack[8 + sizeof(struct timeval)]; |
506 | for (i = 8; i < datalen; ++i, ++cp, ++dp) { | |
63ed9e52 | 507 | if (*cp != *dp) { |
78288025 KB |
508 | (void)printf("\nwrong data byte #%d should be 0x%x but was 0x%x", |
509 | i, *dp, *cp); | |
63ed9e52 | 510 | cp = (u_char*)&icp->icmp_data[0]; |
78288025 KB |
511 | for (i = 8; i < datalen; ++i, ++cp) { |
512 | if ((i % 32) == 8) | |
513 | (void)printf("\n\t"); | |
514 | (void)printf("%x ", *cp); | |
63ed9e52 KB |
515 | } |
516 | break; | |
517 | } | |
518 | } | |
63ed9e52 KB |
519 | } |
520 | } else { | |
521 | /* We've got something other than an ECHOREPLY */ | |
78288025 | 522 | if (!(options & F_VERBOSE)) |
63ed9e52 | 523 | return; |
78288025 KB |
524 | (void)printf("%d bytes from %s: ", cc, |
525 | pr_addr(from->sin_addr.s_addr)); | |
526 | pr_icmph(icp); | |
63ed9e52 KB |
527 | } |
528 | ||
529 | /* Display any IP options */ | |
530 | cp = (u_char *)buf + sizeof(struct ip); | |
78288025 | 531 | |
a9334da6 | 532 | for (; hlen > (int)sizeof(struct ip); --hlen, ++cp) |
78288025 | 533 | switch (*cp) { |
63ed9e52 KB |
534 | case IPOPT_EOL: |
535 | hlen = 0; | |
536 | break; | |
537 | case IPOPT_LSRR: | |
78288025 | 538 | (void)printf("\nLSRR: "); |
63ed9e52 KB |
539 | hlen -= 2; |
540 | j = *++cp; | |
541 | ++cp; | |
78288025 KB |
542 | if (j > IPOPT_MINOFF) |
543 | for (;;) { | |
544 | l = *++cp; | |
545 | l = (l<<8) + *++cp; | |
546 | l = (l<<8) + *++cp; | |
547 | l = (l<<8) + *++cp; | |
548 | if (l == 0) | |
549 | (void)printf("\t0.0.0.0"); | |
63ed9e52 | 550 | else |
78288025 | 551 | (void)printf("\t%s", pr_addr(ntohl(l))); |
63ed9e52 KB |
552 | hlen -= 4; |
553 | j -= 4; | |
554 | if (j <= IPOPT_MINOFF) | |
555 | break; | |
78288025 | 556 | (void)putchar('\n'); |
63ed9e52 KB |
557 | } |
558 | break; | |
559 | case IPOPT_RR: | |
78288025 KB |
560 | j = *++cp; /* get length */ |
561 | i = *++cp; /* and pointer */ | |
63ed9e52 | 562 | hlen -= 2; |
78288025 KB |
563 | if (i > j) |
564 | i = j; | |
63ed9e52 KB |
565 | i -= IPOPT_MINOFF; |
566 | if (i <= 0) | |
567 | continue; | |
568 | if (i == old_rrlen | |
569 | && cp == (u_char *)buf + sizeof(struct ip) + 2 | |
4d6bc69c | 570 | && !memcmp(cp, old_rr, i) |
78288025 KB |
571 | && !(options & F_FLOOD)) { |
572 | (void)printf("\t(same route)"); | |
573 | i = ((i + 3) / 4) * 4; | |
63ed9e52 KB |
574 | hlen -= i; |
575 | cp += i; | |
576 | break; | |
577 | } | |
578 | old_rrlen = i; | |
03fe9c29 | 579 | memmove(old_rr, cp, i); |
78288025 | 580 | (void)printf("\nRR: "); |
63ed9e52 KB |
581 | for (;;) { |
582 | l = *++cp; | |
583 | l = (l<<8) + *++cp; | |
584 | l = (l<<8) + *++cp; | |
585 | l = (l<<8) + *++cp; | |
586 | if (l == 0) | |
78288025 | 587 | (void)printf("\t0.0.0.0"); |
63ed9e52 | 588 | else |
78288025 | 589 | (void)printf("\t%s", pr_addr(ntohl(l))); |
63ed9e52 KB |
590 | hlen -= 4; |
591 | i -= 4; | |
592 | if (i <= 0) | |
593 | break; | |
78288025 | 594 | (void)putchar('\n'); |
63ed9e52 KB |
595 | } |
596 | break; | |
597 | case IPOPT_NOP: | |
78288025 | 598 | (void)printf("\nNOP"); |
63ed9e52 KB |
599 | break; |
600 | default: | |
78288025 | 601 | (void)printf("\nunknown option %x", *cp); |
63ed9e52 KB |
602 | break; |
603 | } | |
78288025 KB |
604 | if (!(options & F_FLOOD)) { |
605 | (void)putchar('\n'); | |
606 | (void)fflush(stdout); | |
3310aa34 | 607 | } |
3310aa34 MK |
608 | } |
609 | ||
3310aa34 | 610 | /* |
78288025 KB |
611 | * in_cksum -- |
612 | * Checksum routine for Internet Protocol family headers (C Version) | |
3310aa34 MK |
613 | */ |
614 | in_cksum(addr, len) | |
78288025 KB |
615 | u_short *addr; |
616 | int len; | |
3310aa34 | 617 | { |
8b11365c MK |
618 | register int nleft = len; |
619 | register u_short *w = addr; | |
8b11365c | 620 | register int sum = 0; |
63ed9e52 | 621 | u_short answer = 0; |
3310aa34 MK |
622 | |
623 | /* | |
78288025 KB |
624 | * Our algorithm is simple, using a 32 bit accumulator (sum), we add |
625 | * sequential 16 bit words to it, and at the end, fold back all the | |
626 | * carry bits from the top 16 bits into the lower 16 bits. | |
3310aa34 | 627 | */ |
78288025 | 628 | while (nleft > 1) { |
3310aa34 MK |
629 | sum += *w++; |
630 | nleft -= 2; | |
631 | } | |
8b11365c MK |
632 | |
633 | /* mop up an odd byte, if necessary */ | |
78288025 | 634 | if (nleft == 1) { |
63ed9e52 KB |
635 | *(u_char *)(&answer) = *(u_char *)w ; |
636 | sum += answer; | |
e03fd703 | 637 | } |
3310aa34 | 638 | |
78288025 | 639 | /* add back carry outs from top 16 bits to low 16 bits */ |
b55680d9 MK |
640 | sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ |
641 | sum += (sum >> 16); /* add carry */ | |
642 | answer = ~sum; /* truncate to 16 bits */ | |
78288025 | 643 | return(answer); |
3310aa34 MK |
644 | } |
645 | ||
646 | /* | |
78288025 KB |
647 | * tvsub -- |
648 | * Subtract 2 timeval structs: out = out - in. Out is assumed to | |
649 | * be >= in. | |
3310aa34 | 650 | */ |
78288025 KB |
651 | tvsub(out, in) |
652 | register struct timeval *out, *in; | |
3310aa34 | 653 | { |
78288025 KB |
654 | if ((out->tv_usec -= in->tv_usec) < 0) { |
655 | --out->tv_sec; | |
3310aa34 MK |
656 | out->tv_usec += 1000000; |
657 | } | |
658 | out->tv_sec -= in->tv_sec; | |
659 | } | |
660 | ||
661 | /* | |
78288025 KB |
662 | * finish -- |
663 | * Print out statistics, and give up. | |
3310aa34 | 664 | */ |
896b9bf5 | 665 | void |
3310aa34 MK |
666 | finish() |
667 | { | |
7211977e KB |
668 | register int i; |
669 | ||
c19f4b1f | 670 | (void)signal(SIGINT, SIG_IGN); |
78288025 KB |
671 | (void)putchar('\n'); |
672 | (void)fflush(stdout); | |
673 | (void)printf("--- %s ping statistics ---\n", hostname); | |
674 | (void)printf("%ld packets transmitted, ", ntransmitted); | |
675 | (void)printf("%ld packets received, ", nreceived); | |
676 | if (nrepeats) | |
677 | (void)printf("+%ld duplicates, ", nrepeats); | |
63ed9e52 | 678 | if (ntransmitted) |
78288025 KB |
679 | if (nreceived > ntransmitted) |
680 | (void)printf("-- somebody's printing up packets!"); | |
d93a36b6 | 681 | else |
78288025 KB |
682 | (void)printf("%d%% packet loss", |
683 | (int) (((ntransmitted - nreceived) * 100) / | |
684 | ntransmitted)); | |
685 | (void)putchar('\n'); | |
7211977e KB |
686 | if (nreceived && timing) { |
687 | /* Only display average to microseconds */ | |
688 | i = 1000.0 * tsum / (nreceived + nrepeats); | |
689 | (void)printf("round-trip min/avg/max = %g/%g/%g ms\n", | |
690 | tmin, ((double)i) / 1000.0, tmax); | |
691 | } | |
3310aa34 MK |
692 | exit(0); |
693 | } | |
63ed9e52 | 694 | |
78288025 | 695 | #ifdef notdef |
63ed9e52 KB |
696 | static char *ttab[] = { |
697 | "Echo Reply", /* ip + seq + udata */ | |
698 | "Dest Unreachable", /* net, host, proto, port, frag, sr + IP */ | |
699 | "Source Quench", /* IP */ | |
700 | "Redirect", /* redirect type, gateway, + IP */ | |
701 | "Echo", | |
702 | "Time Exceeded", /* transit, frag reassem + IP */ | |
703 | "Parameter Problem", /* pointer + IP */ | |
704 | "Timestamp", /* id + seq + three timestamps */ | |
705 | "Timestamp Reply", /* " */ | |
706 | "Info Request", /* id + sq */ | |
707 | "Info Reply" /* " */ | |
708 | }; | |
78288025 | 709 | #endif |
63ed9e52 KB |
710 | |
711 | /* | |
78288025 KB |
712 | * pr_icmph -- |
713 | * Print a descriptive string about an ICMP header. | |
63ed9e52 | 714 | */ |
78288025 KB |
715 | pr_icmph(icp) |
716 | struct icmp *icp; | |
63ed9e52 | 717 | { |
78288025 | 718 | switch(icp->icmp_type) { |
63ed9e52 | 719 | case ICMP_ECHOREPLY: |
78288025 | 720 | (void)printf("Echo Reply\n"); |
63ed9e52 KB |
721 | /* XXX ID + Seq + Data */ |
722 | break; | |
723 | case ICMP_UNREACH: | |
78288025 | 724 | switch(icp->icmp_code) { |
63ed9e52 | 725 | case ICMP_UNREACH_NET: |
78288025 | 726 | (void)printf("Destination Net Unreachable\n"); |
63ed9e52 KB |
727 | break; |
728 | case ICMP_UNREACH_HOST: | |
78288025 | 729 | (void)printf("Destination Host Unreachable\n"); |
63ed9e52 KB |
730 | break; |
731 | case ICMP_UNREACH_PROTOCOL: | |
78288025 | 732 | (void)printf("Destination Protocol Unreachable\n"); |
63ed9e52 KB |
733 | break; |
734 | case ICMP_UNREACH_PORT: | |
78288025 | 735 | (void)printf("Destination Port Unreachable\n"); |
63ed9e52 KB |
736 | break; |
737 | case ICMP_UNREACH_NEEDFRAG: | |
78288025 | 738 | (void)printf("frag needed and DF set\n"); |
63ed9e52 KB |
739 | break; |
740 | case ICMP_UNREACH_SRCFAIL: | |
78288025 | 741 | (void)printf("Source Route Failed\n"); |
63ed9e52 KB |
742 | break; |
743 | default: | |
78288025 KB |
744 | (void)printf("Dest Unreachable, Bad Code: %d\n", |
745 | icp->icmp_code); | |
63ed9e52 KB |
746 | break; |
747 | } | |
748 | /* Print returned IP header information */ | |
749 | #ifndef icmp_data | |
78288025 | 750 | pr_retip(&icp->icmp_ip); |
63ed9e52 | 751 | #else |
78288025 | 752 | pr_retip((struct ip *)icp->icmp_data); |
63ed9e52 KB |
753 | #endif |
754 | break; | |
755 | case ICMP_SOURCEQUENCH: | |
78288025 | 756 | (void)printf("Source Quench\n"); |
63ed9e52 | 757 | #ifndef icmp_data |
78288025 | 758 | pr_retip(&icp->icmp_ip); |
63ed9e52 | 759 | #else |
78288025 | 760 | pr_retip((struct ip *)icp->icmp_data); |
63ed9e52 KB |
761 | #endif |
762 | break; | |
763 | case ICMP_REDIRECT: | |
78288025 | 764 | switch(icp->icmp_code) { |
63ed9e52 | 765 | case ICMP_REDIRECT_NET: |
78288025 | 766 | (void)printf("Redirect Network"); |
63ed9e52 KB |
767 | break; |
768 | case ICMP_REDIRECT_HOST: | |
78288025 | 769 | (void)printf("Redirect Host"); |
63ed9e52 KB |
770 | break; |
771 | case ICMP_REDIRECT_TOSNET: | |
78288025 | 772 | (void)printf("Redirect Type of Service and Network"); |
63ed9e52 KB |
773 | break; |
774 | case ICMP_REDIRECT_TOSHOST: | |
78288025 | 775 | (void)printf("Redirect Type of Service and Host"); |
63ed9e52 KB |
776 | break; |
777 | default: | |
78288025 | 778 | (void)printf("Redirect, Bad Code: %d", icp->icmp_code); |
63ed9e52 KB |
779 | break; |
780 | } | |
78288025 | 781 | (void)printf("(New addr: 0x%08lx)\n", icp->icmp_gwaddr.s_addr); |
63ed9e52 | 782 | #ifndef icmp_data |
78288025 | 783 | pr_retip(&icp->icmp_ip); |
63ed9e52 | 784 | #else |
78288025 | 785 | pr_retip((struct ip *)icp->icmp_data); |
63ed9e52 KB |
786 | #endif |
787 | break; | |
788 | case ICMP_ECHO: | |
78288025 | 789 | (void)printf("Echo Request\n"); |
63ed9e52 KB |
790 | /* XXX ID + Seq + Data */ |
791 | break; | |
792 | case ICMP_TIMXCEED: | |
78288025 | 793 | switch(icp->icmp_code) { |
63ed9e52 | 794 | case ICMP_TIMXCEED_INTRANS: |
78288025 | 795 | (void)printf("Time to live exceeded\n"); |
63ed9e52 KB |
796 | break; |
797 | case ICMP_TIMXCEED_REASS: | |
78288025 | 798 | (void)printf("Frag reassembly time exceeded\n"); |
63ed9e52 KB |
799 | break; |
800 | default: | |
78288025 KB |
801 | (void)printf("Time exceeded, Bad Code: %d\n", |
802 | icp->icmp_code); | |
63ed9e52 KB |
803 | break; |
804 | } | |
805 | #ifndef icmp_data | |
78288025 | 806 | pr_retip(&icp->icmp_ip); |
63ed9e52 | 807 | #else |
78288025 | 808 | pr_retip((struct ip *)icp->icmp_data); |
63ed9e52 KB |
809 | #endif |
810 | break; | |
811 | case ICMP_PARAMPROB: | |
78288025 KB |
812 | (void)printf("Parameter problem: pointer = 0x%02x\n", |
813 | icp->icmp_hun.ih_pptr); | |
63ed9e52 | 814 | #ifndef icmp_data |
78288025 | 815 | pr_retip(&icp->icmp_ip); |
63ed9e52 | 816 | #else |
78288025 | 817 | pr_retip((struct ip *)icp->icmp_data); |
63ed9e52 KB |
818 | #endif |
819 | break; | |
820 | case ICMP_TSTAMP: | |
78288025 | 821 | (void)printf("Timestamp\n"); |
63ed9e52 KB |
822 | /* XXX ID + Seq + 3 timestamps */ |
823 | break; | |
824 | case ICMP_TSTAMPREPLY: | |
78288025 | 825 | (void)printf("Timestamp Reply\n"); |
63ed9e52 KB |
826 | /* XXX ID + Seq + 3 timestamps */ |
827 | break; | |
828 | case ICMP_IREQ: | |
78288025 | 829 | (void)printf("Information Request\n"); |
63ed9e52 KB |
830 | /* XXX ID + Seq */ |
831 | break; | |
832 | case ICMP_IREQREPLY: | |
78288025 | 833 | (void)printf("Information Reply\n"); |
63ed9e52 KB |
834 | /* XXX ID + Seq */ |
835 | break; | |
836 | #ifdef ICMP_MASKREQ | |
837 | case ICMP_MASKREQ: | |
78288025 | 838 | (void)printf("Address Mask Request\n"); |
63ed9e52 KB |
839 | break; |
840 | #endif | |
841 | #ifdef ICMP_MASKREPLY | |
842 | case ICMP_MASKREPLY: | |
78288025 | 843 | (void)printf("Address Mask Reply\n"); |
63ed9e52 KB |
844 | break; |
845 | #endif | |
846 | default: | |
78288025 | 847 | (void)printf("Bad ICMP type: %d\n", icp->icmp_type); |
63ed9e52 KB |
848 | } |
849 | } | |
850 | ||
851 | /* | |
78288025 KB |
852 | * pr_iph -- |
853 | * Print an IP header with options. | |
63ed9e52 | 854 | */ |
78288025 KB |
855 | pr_iph(ip) |
856 | struct ip *ip; | |
63ed9e52 | 857 | { |
78288025 KB |
858 | int hlen; |
859 | u_char *cp; | |
63ed9e52 KB |
860 | |
861 | hlen = ip->ip_hl << 2; | |
78288025 KB |
862 | cp = (u_char *)ip + 20; /* point to options */ |
863 | ||
864 | (void)printf("Vr HL TOS Len ID Flg off TTL Pro cks Src Dst Data\n"); | |
865 | (void)printf(" %1x %1x %02x %04x %04x", | |
866 | ip->ip_v, ip->ip_hl, ip->ip_tos, ip->ip_len, ip->ip_id); | |
867 | (void)printf(" %1x %04x", ((ip->ip_off) & 0xe000) >> 13, | |
868 | (ip->ip_off) & 0x1fff); | |
869 | (void)printf(" %02x %02x %04x", ip->ip_ttl, ip->ip_p, ip->ip_sum); | |
870 | (void)printf(" %s ", inet_ntoa(*(struct in_addr *)&ip->ip_src.s_addr)); | |
871 | (void)printf(" %s ", inet_ntoa(*(struct in_addr *)&ip->ip_dst.s_addr)); | |
63ed9e52 | 872 | /* dump and option bytes */ |
78288025 KB |
873 | while (hlen-- > 20) { |
874 | (void)printf("%02x", *cp++); | |
63ed9e52 | 875 | } |
78288025 | 876 | (void)putchar('\n'); |
63ed9e52 KB |
877 | } |
878 | ||
879 | /* | |
78288025 KB |
880 | * pr_addr -- |
881 | * Return an ascii host address as a dotted quad and optionally with | |
882 | * a hostname. | |
63ed9e52 KB |
883 | */ |
884 | char * | |
78288025 KB |
885 | pr_addr(l) |
886 | u_long l; | |
63ed9e52 | 887 | { |
78288025 KB |
888 | struct hostent *hp; |
889 | static char buf[80]; | |
63ed9e52 | 890 | |
78288025 KB |
891 | if ((options & F_NUMERIC) || |
892 | !(hp = gethostbyaddr((char *)&l, 4, AF_INET))) | |
893 | (void)sprintf(buf, "%s", inet_ntoa(*(struct in_addr *)&l)); | |
63ed9e52 | 894 | else |
78288025 KB |
895 | (void)sprintf(buf, "%s (%s)", hp->h_name, |
896 | inet_ntoa(*(struct in_addr *)&l)); | |
897 | return(buf); | |
63ed9e52 KB |
898 | } |
899 | ||
900 | /* | |
78288025 KB |
901 | * pr_retip -- |
902 | * Dump some info on a returned (via ICMP) IP packet. | |
63ed9e52 | 903 | */ |
78288025 KB |
904 | pr_retip(ip) |
905 | struct ip *ip; | |
63ed9e52 | 906 | { |
78288025 KB |
907 | int hlen; |
908 | u_char *cp; | |
63ed9e52 | 909 | |
78288025 | 910 | pr_iph(ip); |
63ed9e52 | 911 | hlen = ip->ip_hl << 2; |
78288025 KB |
912 | cp = (u_char *)ip + hlen; |
913 | ||
914 | if (ip->ip_p == 6) | |
915 | (void)printf("TCP: from port %u, to port %u (decimal)\n", | |
916 | (*cp * 256 + *(cp + 1)), (*(cp + 2) * 256 + *(cp + 3))); | |
917 | else if (ip->ip_p == 17) | |
918 | (void)printf("UDP: from port %u, to port %u (decimal)\n", | |
919 | (*cp * 256 + *(cp + 1)), (*(cp + 2) * 256 + *(cp + 3))); | |
63ed9e52 KB |
920 | } |
921 | ||
922 | fill(bp, patp) | |
78288025 | 923 | char *bp, *patp; |
63ed9e52 | 924 | { |
78288025 KB |
925 | register int ii, jj, kk; |
926 | int pat[16]; | |
927 | char *cp; | |
928 | ||
929 | for (cp = patp; *cp; cp++) | |
930 | if (!isxdigit(*cp)) { | |
931 | (void)fprintf(stderr, | |
932 | "ping: patterns must be specified as hex digits.\n"); | |
933 | exit(1); | |
934 | } | |
935 | ii = sscanf(patp, | |
936 | "%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x", | |
937 | &pat[0], &pat[1], &pat[2], &pat[3], &pat[4], &pat[5], &pat[6], | |
938 | &pat[7], &pat[8], &pat[9], &pat[10], &pat[11], &pat[12], | |
939 | &pat[13], &pat[14], &pat[15]); | |
940 | ||
941 | if (ii > 0) | |
25efbec0 KB |
942 | for (kk = 0; |
943 | kk <= MAXPACKET - (8 + sizeof(struct timeval) + ii); | |
944 | kk += ii) | |
78288025 KB |
945 | for (jj = 0; jj < ii; ++jj) |
946 | bp[jj + kk] = pat[jj]; | |
947 | if (!(options & F_QUIET)) { | |
948 | (void)printf("PATTERN: 0x"); | |
949 | for (jj = 0; jj < ii; ++jj) | |
950 | (void)printf("%02x", bp[jj] & 0xFF); | |
951 | (void)printf("\n"); | |
952 | } | |
953 | } | |
63ed9e52 | 954 | |
78288025 KB |
955 | usage() |
956 | { | |
957 | (void)fprintf(stderr, | |
958 | "usage: ping [-Rdfnqrv] [-c count] [-i wait] [-l preload]\n\t[-p pattern] [-s packetsize] host\n"); | |
959 | exit(1); | |
63ed9e52 | 960 | } |