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