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 | |
38dde0cd | 28 | static char sccsid[] = "@(#)ping.c 5.5 (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> | |
38dde0cd | 65 | #include <string.h> |
78288025 KB |
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 | ||
1070431c | 323 | if ((options & F_FLOOD) == 0) |
78288025 KB |
324 | catcher(); /* start things going */ |
325 | ||
326 | for (;;) { | |
327 | struct sockaddr_in from; | |
328 | register int cc; | |
329 | int fromlen; | |
63ed9e52 | 330 | |
78288025 | 331 | if (options & F_FLOOD) { |
63ed9e52 | 332 | pinger(); |
1070431c MK |
333 | timeout.tv_sec = 0; |
334 | timeout.tv_usec = 10000; | |
335 | fdmask = 1 << s; | |
336 | if (select(s + 1, (fd_set *)&fdmask, (fd_set *)NULL, | |
337 | (fd_set *)NULL, &timeout) < 1) | |
63ed9e52 KB |
338 | continue; |
339 | } | |
78288025 KB |
340 | fromlen = sizeof(from); |
341 | if ((cc = recvfrom(s, (char *)packet, packlen, 0, | |
342 | (struct sockaddr *)&from, &fromlen)) < 0) { | |
343 | if (errno == EINTR) | |
3310aa34 MK |
344 | continue; |
345 | perror("ping: recvfrom"); | |
346 | continue; | |
347 | } | |
78288025 | 348 | pr_pack((char *)packet, cc, &from); |
9a0e2985 | 349 | if (npackets && nreceived >= npackets) |
78288025 | 350 | break; |
3310aa34 | 351 | } |
78288025 KB |
352 | finish(); |
353 | /* NOTREACHED */ | |
3310aa34 MK |
354 | } |
355 | ||
356 | /* | |
78288025 KB |
357 | * catcher -- |
358 | * This routine causes another PING to be transmitted, and then | |
3310aa34 MK |
359 | * schedules another SIGALRM for 1 second from now. |
360 | * | |
78288025 KB |
361 | * bug -- |
362 | * Our sense of time will slowly skew (i.e., packets will not be | |
363 | * launched exactly at 1-second intervals). This does not affect the | |
364 | * quality of the delay and loss statistics. | |
3310aa34 MK |
365 | */ |
366 | catcher() | |
367 | { | |
9a0e2985 MK |
368 | int waittime; |
369 | ||
3310aa34 | 370 | pinger(); |
78288025 KB |
371 | (void)signal(SIGALRM, catcher); |
372 | if (!npackets || ntransmitted < npackets) | |
373 | alarm((u_int)interval); | |
9a0e2985 MK |
374 | else { |
375 | if (nreceived) { | |
376 | waittime = 2 * tmax / 1000; | |
78288025 | 377 | if (!waittime) |
9a0e2985 MK |
378 | waittime = 1; |
379 | } else | |
380 | waittime = MAXWAIT; | |
78288025 KB |
381 | (void)signal(SIGALRM, finish); |
382 | (void)alarm((u_int)waittime); | |
9a0e2985 | 383 | } |
3310aa34 MK |
384 | } |
385 | ||
386 | /* | |
78288025 KB |
387 | * pinger -- |
388 | * Compose and transmit an ICMP ECHO REQUEST packet. The IP packet | |
3310aa34 MK |
389 | * will be added on by the kernel. The ID field is our UNIX process ID, |
390 | * and the sequence number is an ascending integer. The first 8 bytes | |
391 | * of the data portion are used to hold a UNIX "timeval" struct in VAX | |
392 | * byte-order, to compute the round-trip time. | |
393 | */ | |
394 | pinger() | |
395 | { | |
78288025 KB |
396 | register struct icmp *icp; |
397 | register int cc; | |
398 | int i; | |
3310aa34 | 399 | |
78288025 | 400 | icp = (struct icmp *)outpack; |
3310aa34 MK |
401 | icp->icmp_type = ICMP_ECHO; |
402 | icp->icmp_code = 0; | |
403 | icp->icmp_cksum = 0; | |
f5d563c7 | 404 | icp->icmp_seq = ntransmitted++; |
78288025 | 405 | icp->icmp_id = ident; /* ID */ |
3310aa34 | 406 | |
78288025 | 407 | CLR(icp->icmp_seq % mx_dup_ck); |
3310aa34 | 408 | |
9a0e2985 | 409 | if (timing) |
78288025 KB |
410 | (void)gettimeofday((struct timeval *)&outpack[8], |
411 | (struct timezone *)NULL); | |
3310aa34 | 412 | |
78288025 | 413 | cc = datalen + 8; /* skips ICMP portion */ |
3310aa34 | 414 | |
78288025 KB |
415 | /* compute ICMP checksum here */ |
416 | icp->icmp_cksum = in_cksum((u_short *)icp, cc); | |
d93a36b6 | 417 | |
78288025 KB |
418 | i = sendto(s, (char *)outpack, cc, 0, &whereto, |
419 | sizeof(struct sockaddr)); | |
420 | ||
421 | if (i < 0 || i != cc) { | |
422 | if (i < 0) | |
423 | perror("ping: sendto"); | |
424 | (void)printf("ping: wrote %s %d chars, ret=%d\n", | |
425 | hostname, cc, i); | |
3310aa34 | 426 | } |
78288025 KB |
427 | if (options & F_FLOOD) |
428 | (void)write(STDOUT_FILENO, &DOT, 1); | |
3310aa34 MK |
429 | } |
430 | ||
431 | /* | |
78288025 KB |
432 | * pr_pack -- |
433 | * Print out the packet, if it came from us. This logic is necessary | |
3310aa34 MK |
434 | * because ALL readers of the ICMP socket get a copy of ALL ICMP packets |
435 | * which arrive ('tis only fair). This permits multiple copies of this | |
436 | * program to be run without having intermingled output (or statistics!). | |
437 | */ | |
78288025 KB |
438 | pr_pack(buf, cc, from) |
439 | char *buf; | |
440 | int cc; | |
441 | struct sockaddr_in *from; | |
3310aa34 | 442 | { |
824f4533 | 443 | register struct icmp *icp; |
78288025 | 444 | register u_long l; |
63ed9e52 KB |
445 | register int i, j; |
446 | register u_char *cp,*dp; | |
447 | static int old_rrlen; | |
448 | static char old_rr[MAX_IPOPTLEN]; | |
78288025 KB |
449 | struct ip *ip; |
450 | struct timeval tv, *tp; | |
451 | long triptime; | |
452 | int hlen, dupflag; | |
3310aa34 | 453 | |
78288025 | 454 | (void)gettimeofday(&tv, (struct timezone *)NULL); |
3310aa34 | 455 | |
63ed9e52 | 456 | /* Check the IP header */ |
78288025 | 457 | ip = (struct ip *)buf; |
824f4533 | 458 | hlen = ip->ip_hl << 2; |
78288025 KB |
459 | if (cc < hlen + ICMP_MINLEN) { |
460 | if (options & F_VERBOSE) | |
461 | (void)fprintf(stderr, | |
462 | "ping: packet too short (%d bytes) from %s\n", cc, | |
463 | inet_ntoa(*(struct in_addr *)&from->sin_addr.s_addr)); | |
824f4533 MK |
464 | return; |
465 | } | |
63ed9e52 KB |
466 | |
467 | /* Now the ICMP part */ | |
824f4533 MK |
468 | cc -= hlen; |
469 | icp = (struct icmp *)(buf + hlen); | |
78288025 KB |
470 | if (icp->icmp_type == ICMP_ECHOREPLY) { |
471 | if (icp->icmp_id != ident) | |
63ed9e52 | 472 | return; /* 'Twas not our ECHO */ |
78288025 | 473 | ++nreceived; |
63ed9e52 KB |
474 | if (timing) { |
475 | #ifndef icmp_data | |
476 | tp = (struct timeval *)&icp->icmp_ip; | |
477 | #else | |
78288025 | 478 | tp = (struct timeval *)icp->icmp_data; |
63ed9e52 | 479 | #endif |
78288025 KB |
480 | tvsub(&tv, tp); |
481 | triptime = tv.tv_sec * 1000 + (tv.tv_usec / 1000); | |
63ed9e52 | 482 | tsum += triptime; |
78288025 | 483 | if (triptime < tmin) |
63ed9e52 | 484 | tmin = triptime; |
78288025 | 485 | if (triptime > tmax) |
63ed9e52 | 486 | tmax = triptime; |
9a0e2985 | 487 | } |
63ed9e52 | 488 | |
78288025 KB |
489 | if (TST(icp->icmp_seq % mx_dup_ck)) { |
490 | ++nrepeats; | |
491 | --nreceived; | |
492 | dupflag = 1; | |
493 | } else { | |
494 | SET(icp->icmp_seq % mx_dup_ck); | |
495 | dupflag = 0; | |
63ed9e52 KB |
496 | } |
497 | ||
78288025 | 498 | if (options & F_QUIET) |
63ed9e52 KB |
499 | return; |
500 | ||
78288025 KB |
501 | if (options & F_FLOOD) |
502 | (void)write(STDOUT_FILENO, &BSPACE, 1); | |
503 | else { | |
504 | (void)printf("%d bytes from %s: icmp_seq=%u", cc, | |
505 | inet_ntoa(*(struct in_addr *)&from->sin_addr.s_addr), | |
506 | icp->icmp_seq); | |
507 | (void)printf(" ttl=%d", ip->ip_ttl); | |
63ed9e52 | 508 | if (timing) |
78288025 KB |
509 | (void)printf(" time=%ld ms", triptime); |
510 | if (dupflag) | |
511 | (void)printf(" (DUP!)"); | |
63ed9e52 KB |
512 | /* check the data */ |
513 | cp = (u_char*)&icp->icmp_data[8]; | |
78288025 KB |
514 | dp = &outpack[8 + sizeof(struct timeval)]; |
515 | for (i = 8; i < datalen; ++i, ++cp, ++dp) { | |
63ed9e52 | 516 | if (*cp != *dp) { |
78288025 KB |
517 | (void)printf("\nwrong data byte #%d should be 0x%x but was 0x%x", |
518 | i, *dp, *cp); | |
63ed9e52 | 519 | cp = (u_char*)&icp->icmp_data[0]; |
78288025 KB |
520 | for (i = 8; i < datalen; ++i, ++cp) { |
521 | if ((i % 32) == 8) | |
522 | (void)printf("\n\t"); | |
523 | (void)printf("%x ", *cp); | |
63ed9e52 KB |
524 | } |
525 | break; | |
526 | } | |
527 | } | |
63ed9e52 KB |
528 | } |
529 | } else { | |
530 | /* We've got something other than an ECHOREPLY */ | |
78288025 | 531 | if (!(options & F_VERBOSE)) |
63ed9e52 | 532 | return; |
78288025 KB |
533 | (void)printf("%d bytes from %s: ", cc, |
534 | pr_addr(from->sin_addr.s_addr)); | |
535 | pr_icmph(icp); | |
63ed9e52 KB |
536 | } |
537 | ||
538 | /* Display any IP options */ | |
539 | cp = (u_char *)buf + sizeof(struct ip); | |
78288025 KB |
540 | |
541 | /* ANSI C will force hlen to unsigned! */ | |
542 | for (; hlen > sizeof(struct ip); --hlen, ++cp) | |
543 | switch (*cp) { | |
63ed9e52 KB |
544 | case IPOPT_EOL: |
545 | hlen = 0; | |
546 | break; | |
547 | case IPOPT_LSRR: | |
78288025 | 548 | (void)printf("\nLSRR: "); |
63ed9e52 KB |
549 | hlen -= 2; |
550 | j = *++cp; | |
551 | ++cp; | |
78288025 KB |
552 | if (j > IPOPT_MINOFF) |
553 | for (;;) { | |
554 | l = *++cp; | |
555 | l = (l<<8) + *++cp; | |
556 | l = (l<<8) + *++cp; | |
557 | l = (l<<8) + *++cp; | |
558 | if (l == 0) | |
559 | (void)printf("\t0.0.0.0"); | |
63ed9e52 | 560 | else |
78288025 | 561 | (void)printf("\t%s", pr_addr(ntohl(l))); |
63ed9e52 KB |
562 | hlen -= 4; |
563 | j -= 4; | |
564 | if (j <= IPOPT_MINOFF) | |
565 | break; | |
78288025 | 566 | (void)putchar('\n'); |
63ed9e52 KB |
567 | } |
568 | break; | |
569 | case IPOPT_RR: | |
78288025 KB |
570 | j = *++cp; /* get length */ |
571 | i = *++cp; /* and pointer */ | |
63ed9e52 | 572 | hlen -= 2; |
78288025 KB |
573 | if (i > j) |
574 | i = j; | |
63ed9e52 KB |
575 | i -= IPOPT_MINOFF; |
576 | if (i <= 0) | |
577 | continue; | |
578 | if (i == old_rrlen | |
579 | && cp == (u_char *)buf + sizeof(struct ip) + 2 | |
580 | && !bcmp((char *)cp, old_rr, i) | |
78288025 KB |
581 | && !(options & F_FLOOD)) { |
582 | (void)printf("\t(same route)"); | |
583 | i = ((i + 3) / 4) * 4; | |
63ed9e52 KB |
584 | hlen -= i; |
585 | cp += i; | |
586 | break; | |
587 | } | |
588 | old_rrlen = i; | |
589 | bcopy((char *)cp, old_rr, i); | |
78288025 | 590 | (void)printf("\nRR: "); |
63ed9e52 KB |
591 | for (;;) { |
592 | l = *++cp; | |
593 | l = (l<<8) + *++cp; | |
594 | l = (l<<8) + *++cp; | |
595 | l = (l<<8) + *++cp; | |
596 | if (l == 0) | |
78288025 | 597 | (void)printf("\t0.0.0.0"); |
63ed9e52 | 598 | else |
78288025 | 599 | (void)printf("\t%s", pr_addr(ntohl(l))); |
63ed9e52 KB |
600 | hlen -= 4; |
601 | i -= 4; | |
602 | if (i <= 0) | |
603 | break; | |
78288025 | 604 | (void)putchar('\n'); |
63ed9e52 KB |
605 | } |
606 | break; | |
607 | case IPOPT_NOP: | |
78288025 | 608 | (void)printf("\nNOP"); |
63ed9e52 KB |
609 | break; |
610 | default: | |
78288025 | 611 | (void)printf("\nunknown option %x", *cp); |
63ed9e52 KB |
612 | break; |
613 | } | |
78288025 KB |
614 | if (!(options & F_FLOOD)) { |
615 | (void)putchar('\n'); | |
616 | (void)fflush(stdout); | |
3310aa34 | 617 | } |
3310aa34 MK |
618 | } |
619 | ||
3310aa34 | 620 | /* |
78288025 KB |
621 | * in_cksum -- |
622 | * Checksum routine for Internet Protocol family headers (C Version) | |
3310aa34 MK |
623 | */ |
624 | in_cksum(addr, len) | |
78288025 KB |
625 | u_short *addr; |
626 | int len; | |
3310aa34 | 627 | { |
8b11365c MK |
628 | register int nleft = len; |
629 | register u_short *w = addr; | |
8b11365c | 630 | register int sum = 0; |
63ed9e52 | 631 | u_short answer = 0; |
3310aa34 MK |
632 | |
633 | /* | |
78288025 KB |
634 | * Our algorithm is simple, using a 32 bit accumulator (sum), we add |
635 | * sequential 16 bit words to it, and at the end, fold back all the | |
636 | * carry bits from the top 16 bits into the lower 16 bits. | |
3310aa34 | 637 | */ |
78288025 | 638 | while (nleft > 1) { |
3310aa34 MK |
639 | sum += *w++; |
640 | nleft -= 2; | |
641 | } | |
8b11365c MK |
642 | |
643 | /* mop up an odd byte, if necessary */ | |
78288025 | 644 | if (nleft == 1) { |
63ed9e52 KB |
645 | *(u_char *)(&answer) = *(u_char *)w ; |
646 | sum += answer; | |
e03fd703 | 647 | } |
3310aa34 | 648 | |
78288025 | 649 | /* add back carry outs from top 16 bits to low 16 bits */ |
b55680d9 MK |
650 | sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ |
651 | sum += (sum >> 16); /* add carry */ | |
652 | answer = ~sum; /* truncate to 16 bits */ | |
78288025 | 653 | return(answer); |
3310aa34 MK |
654 | } |
655 | ||
656 | /* | |
78288025 KB |
657 | * tvsub -- |
658 | * Subtract 2 timeval structs: out = out - in. Out is assumed to | |
659 | * be >= in. | |
3310aa34 | 660 | */ |
78288025 KB |
661 | tvsub(out, in) |
662 | register struct timeval *out, *in; | |
3310aa34 | 663 | { |
78288025 KB |
664 | if ((out->tv_usec -= in->tv_usec) < 0) { |
665 | --out->tv_sec; | |
3310aa34 MK |
666 | out->tv_usec += 1000000; |
667 | } | |
668 | out->tv_sec -= in->tv_sec; | |
669 | } | |
670 | ||
671 | /* | |
78288025 KB |
672 | * finish -- |
673 | * Print out statistics, and give up. | |
3310aa34 MK |
674 | */ |
675 | finish() | |
676 | { | |
c19f4b1f | 677 | (void)signal(SIGINT, SIG_IGN); |
78288025 KB |
678 | (void)putchar('\n'); |
679 | (void)fflush(stdout); | |
680 | (void)printf("--- %s ping statistics ---\n", hostname); | |
681 | (void)printf("%ld packets transmitted, ", ntransmitted); | |
682 | (void)printf("%ld packets received, ", nreceived); | |
683 | if (nrepeats) | |
684 | (void)printf("+%ld duplicates, ", nrepeats); | |
63ed9e52 | 685 | if (ntransmitted) |
78288025 KB |
686 | if (nreceived > ntransmitted) |
687 | (void)printf("-- somebody's printing up packets!"); | |
d93a36b6 | 688 | else |
78288025 KB |
689 | (void)printf("%d%% packet loss", |
690 | (int) (((ntransmitted - nreceived) * 100) / | |
691 | ntransmitted)); | |
692 | (void)putchar('\n'); | |
9a0e2985 | 693 | if (nreceived && timing) |
78288025 KB |
694 | (void)printf("round-trip min/avg/max = %ld/%lu/%ld ms\n", |
695 | tmin, tsum / (nreceived + nrepeats), tmax); | |
3310aa34 MK |
696 | exit(0); |
697 | } | |
63ed9e52 | 698 | |
78288025 | 699 | #ifdef notdef |
63ed9e52 KB |
700 | static char *ttab[] = { |
701 | "Echo Reply", /* ip + seq + udata */ | |
702 | "Dest Unreachable", /* net, host, proto, port, frag, sr + IP */ | |
703 | "Source Quench", /* IP */ | |
704 | "Redirect", /* redirect type, gateway, + IP */ | |
705 | "Echo", | |
706 | "Time Exceeded", /* transit, frag reassem + IP */ | |
707 | "Parameter Problem", /* pointer + IP */ | |
708 | "Timestamp", /* id + seq + three timestamps */ | |
709 | "Timestamp Reply", /* " */ | |
710 | "Info Request", /* id + sq */ | |
711 | "Info Reply" /* " */ | |
712 | }; | |
78288025 | 713 | #endif |
63ed9e52 KB |
714 | |
715 | /* | |
78288025 KB |
716 | * pr_icmph -- |
717 | * Print a descriptive string about an ICMP header. | |
63ed9e52 | 718 | */ |
78288025 KB |
719 | pr_icmph(icp) |
720 | struct icmp *icp; | |
63ed9e52 | 721 | { |
78288025 | 722 | switch(icp->icmp_type) { |
63ed9e52 | 723 | case ICMP_ECHOREPLY: |
78288025 | 724 | (void)printf("Echo Reply\n"); |
63ed9e52 KB |
725 | /* XXX ID + Seq + Data */ |
726 | break; | |
727 | case ICMP_UNREACH: | |
78288025 | 728 | switch(icp->icmp_code) { |
63ed9e52 | 729 | case ICMP_UNREACH_NET: |
78288025 | 730 | (void)printf("Destination Net Unreachable\n"); |
63ed9e52 KB |
731 | break; |
732 | case ICMP_UNREACH_HOST: | |
78288025 | 733 | (void)printf("Destination Host Unreachable\n"); |
63ed9e52 KB |
734 | break; |
735 | case ICMP_UNREACH_PROTOCOL: | |
78288025 | 736 | (void)printf("Destination Protocol Unreachable\n"); |
63ed9e52 KB |
737 | break; |
738 | case ICMP_UNREACH_PORT: | |
78288025 | 739 | (void)printf("Destination Port Unreachable\n"); |
63ed9e52 KB |
740 | break; |
741 | case ICMP_UNREACH_NEEDFRAG: | |
78288025 | 742 | (void)printf("frag needed and DF set\n"); |
63ed9e52 KB |
743 | break; |
744 | case ICMP_UNREACH_SRCFAIL: | |
78288025 | 745 | (void)printf("Source Route Failed\n"); |
63ed9e52 KB |
746 | break; |
747 | default: | |
78288025 KB |
748 | (void)printf("Dest Unreachable, Bad Code: %d\n", |
749 | icp->icmp_code); | |
63ed9e52 KB |
750 | break; |
751 | } | |
752 | /* Print returned IP header information */ | |
753 | #ifndef icmp_data | |
78288025 | 754 | pr_retip(&icp->icmp_ip); |
63ed9e52 | 755 | #else |
78288025 | 756 | pr_retip((struct ip *)icp->icmp_data); |
63ed9e52 KB |
757 | #endif |
758 | break; | |
759 | case ICMP_SOURCEQUENCH: | |
78288025 | 760 | (void)printf("Source Quench\n"); |
63ed9e52 | 761 | #ifndef icmp_data |
78288025 | 762 | pr_retip(&icp->icmp_ip); |
63ed9e52 | 763 | #else |
78288025 | 764 | pr_retip((struct ip *)icp->icmp_data); |
63ed9e52 KB |
765 | #endif |
766 | break; | |
767 | case ICMP_REDIRECT: | |
78288025 | 768 | switch(icp->icmp_code) { |
63ed9e52 | 769 | case ICMP_REDIRECT_NET: |
78288025 | 770 | (void)printf("Redirect Network"); |
63ed9e52 KB |
771 | break; |
772 | case ICMP_REDIRECT_HOST: | |
78288025 | 773 | (void)printf("Redirect Host"); |
63ed9e52 KB |
774 | break; |
775 | case ICMP_REDIRECT_TOSNET: | |
78288025 | 776 | (void)printf("Redirect Type of Service and Network"); |
63ed9e52 KB |
777 | break; |
778 | case ICMP_REDIRECT_TOSHOST: | |
78288025 | 779 | (void)printf("Redirect Type of Service and Host"); |
63ed9e52 KB |
780 | break; |
781 | default: | |
78288025 | 782 | (void)printf("Redirect, Bad Code: %d", icp->icmp_code); |
63ed9e52 KB |
783 | break; |
784 | } | |
78288025 | 785 | (void)printf("(New addr: 0x%08lx)\n", icp->icmp_gwaddr.s_addr); |
63ed9e52 | 786 | #ifndef icmp_data |
78288025 | 787 | pr_retip(&icp->icmp_ip); |
63ed9e52 | 788 | #else |
78288025 | 789 | pr_retip((struct ip *)icp->icmp_data); |
63ed9e52 KB |
790 | #endif |
791 | break; | |
792 | case ICMP_ECHO: | |
78288025 | 793 | (void)printf("Echo Request\n"); |
63ed9e52 KB |
794 | /* XXX ID + Seq + Data */ |
795 | break; | |
796 | case ICMP_TIMXCEED: | |
78288025 | 797 | switch(icp->icmp_code) { |
63ed9e52 | 798 | case ICMP_TIMXCEED_INTRANS: |
78288025 | 799 | (void)printf("Time to live exceeded\n"); |
63ed9e52 KB |
800 | break; |
801 | case ICMP_TIMXCEED_REASS: | |
78288025 | 802 | (void)printf("Frag reassembly time exceeded\n"); |
63ed9e52 KB |
803 | break; |
804 | default: | |
78288025 KB |
805 | (void)printf("Time exceeded, Bad Code: %d\n", |
806 | icp->icmp_code); | |
63ed9e52 KB |
807 | break; |
808 | } | |
809 | #ifndef icmp_data | |
78288025 | 810 | pr_retip(&icp->icmp_ip); |
63ed9e52 | 811 | #else |
78288025 | 812 | pr_retip((struct ip *)icp->icmp_data); |
63ed9e52 KB |
813 | #endif |
814 | break; | |
815 | case ICMP_PARAMPROB: | |
78288025 KB |
816 | (void)printf("Parameter problem: pointer = 0x%02x\n", |
817 | icp->icmp_hun.ih_pptr); | |
63ed9e52 | 818 | #ifndef icmp_data |
78288025 | 819 | pr_retip(&icp->icmp_ip); |
63ed9e52 | 820 | #else |
78288025 | 821 | pr_retip((struct ip *)icp->icmp_data); |
63ed9e52 KB |
822 | #endif |
823 | break; | |
824 | case ICMP_TSTAMP: | |
78288025 | 825 | (void)printf("Timestamp\n"); |
63ed9e52 KB |
826 | /* XXX ID + Seq + 3 timestamps */ |
827 | break; | |
828 | case ICMP_TSTAMPREPLY: | |
78288025 | 829 | (void)printf("Timestamp Reply\n"); |
63ed9e52 KB |
830 | /* XXX ID + Seq + 3 timestamps */ |
831 | break; | |
832 | case ICMP_IREQ: | |
78288025 | 833 | (void)printf("Information Request\n"); |
63ed9e52 KB |
834 | /* XXX ID + Seq */ |
835 | break; | |
836 | case ICMP_IREQREPLY: | |
78288025 | 837 | (void)printf("Information Reply\n"); |
63ed9e52 KB |
838 | /* XXX ID + Seq */ |
839 | break; | |
840 | #ifdef ICMP_MASKREQ | |
841 | case ICMP_MASKREQ: | |
78288025 | 842 | (void)printf("Address Mask Request\n"); |
63ed9e52 KB |
843 | break; |
844 | #endif | |
845 | #ifdef ICMP_MASKREPLY | |
846 | case ICMP_MASKREPLY: | |
78288025 | 847 | (void)printf("Address Mask Reply\n"); |
63ed9e52 KB |
848 | break; |
849 | #endif | |
850 | default: | |
78288025 | 851 | (void)printf("Bad ICMP type: %d\n", icp->icmp_type); |
63ed9e52 KB |
852 | } |
853 | } | |
854 | ||
855 | /* | |
78288025 KB |
856 | * pr_iph -- |
857 | * Print an IP header with options. | |
63ed9e52 | 858 | */ |
78288025 KB |
859 | pr_iph(ip) |
860 | struct ip *ip; | |
63ed9e52 | 861 | { |
78288025 KB |
862 | int hlen; |
863 | u_char *cp; | |
63ed9e52 KB |
864 | |
865 | hlen = ip->ip_hl << 2; | |
78288025 KB |
866 | cp = (u_char *)ip + 20; /* point to options */ |
867 | ||
868 | (void)printf("Vr HL TOS Len ID Flg off TTL Pro cks Src Dst Data\n"); | |
869 | (void)printf(" %1x %1x %02x %04x %04x", | |
870 | ip->ip_v, ip->ip_hl, ip->ip_tos, ip->ip_len, ip->ip_id); | |
871 | (void)printf(" %1x %04x", ((ip->ip_off) & 0xe000) >> 13, | |
872 | (ip->ip_off) & 0x1fff); | |
873 | (void)printf(" %02x %02x %04x", ip->ip_ttl, ip->ip_p, ip->ip_sum); | |
874 | (void)printf(" %s ", inet_ntoa(*(struct in_addr *)&ip->ip_src.s_addr)); | |
875 | (void)printf(" %s ", inet_ntoa(*(struct in_addr *)&ip->ip_dst.s_addr)); | |
63ed9e52 | 876 | /* dump and option bytes */ |
78288025 KB |
877 | while (hlen-- > 20) { |
878 | (void)printf("%02x", *cp++); | |
63ed9e52 | 879 | } |
78288025 | 880 | (void)putchar('\n'); |
63ed9e52 KB |
881 | } |
882 | ||
883 | /* | |
78288025 KB |
884 | * pr_addr -- |
885 | * Return an ascii host address as a dotted quad and optionally with | |
886 | * a hostname. | |
63ed9e52 KB |
887 | */ |
888 | char * | |
78288025 KB |
889 | pr_addr(l) |
890 | u_long l; | |
63ed9e52 | 891 | { |
78288025 KB |
892 | struct hostent *hp; |
893 | static char buf[80]; | |
63ed9e52 | 894 | |
78288025 KB |
895 | if ((options & F_NUMERIC) || |
896 | !(hp = gethostbyaddr((char *)&l, 4, AF_INET))) | |
897 | (void)sprintf(buf, "%s", inet_ntoa(*(struct in_addr *)&l)); | |
63ed9e52 | 898 | else |
78288025 KB |
899 | (void)sprintf(buf, "%s (%s)", hp->h_name, |
900 | inet_ntoa(*(struct in_addr *)&l)); | |
901 | return(buf); | |
63ed9e52 KB |
902 | } |
903 | ||
904 | /* | |
78288025 KB |
905 | * pr_retip -- |
906 | * Dump some info on a returned (via ICMP) IP packet. | |
63ed9e52 | 907 | */ |
78288025 KB |
908 | pr_retip(ip) |
909 | struct ip *ip; | |
63ed9e52 | 910 | { |
78288025 KB |
911 | int hlen; |
912 | u_char *cp; | |
63ed9e52 | 913 | |
78288025 | 914 | pr_iph(ip); |
63ed9e52 | 915 | hlen = ip->ip_hl << 2; |
78288025 KB |
916 | cp = (u_char *)ip + hlen; |
917 | ||
918 | if (ip->ip_p == 6) | |
919 | (void)printf("TCP: from port %u, to port %u (decimal)\n", | |
920 | (*cp * 256 + *(cp + 1)), (*(cp + 2) * 256 + *(cp + 3))); | |
921 | else if (ip->ip_p == 17) | |
922 | (void)printf("UDP: from port %u, to port %u (decimal)\n", | |
923 | (*cp * 256 + *(cp + 1)), (*(cp + 2) * 256 + *(cp + 3))); | |
63ed9e52 KB |
924 | } |
925 | ||
926 | fill(bp, patp) | |
78288025 | 927 | char *bp, *patp; |
63ed9e52 | 928 | { |
78288025 KB |
929 | register int ii, jj, kk; |
930 | int pat[16]; | |
931 | char *cp; | |
932 | ||
933 | for (cp = patp; *cp; cp++) | |
934 | if (!isxdigit(*cp)) { | |
935 | (void)fprintf(stderr, | |
936 | "ping: patterns must be specified as hex digits.\n"); | |
937 | exit(1); | |
938 | } | |
939 | ii = sscanf(patp, | |
940 | "%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x", | |
941 | &pat[0], &pat[1], &pat[2], &pat[3], &pat[4], &pat[5], &pat[6], | |
942 | &pat[7], &pat[8], &pat[9], &pat[10], &pat[11], &pat[12], | |
943 | &pat[13], &pat[14], &pat[15]); | |
944 | ||
945 | if (ii > 0) | |
946 | for (kk = 0; kk <= MAXPACKET - (8 + ii); kk += ii) | |
947 | for (jj = 0; jj < ii; ++jj) | |
948 | bp[jj + kk] = pat[jj]; | |
949 | if (!(options & F_QUIET)) { | |
950 | (void)printf("PATTERN: 0x"); | |
951 | for (jj = 0; jj < ii; ++jj) | |
952 | (void)printf("%02x", bp[jj] & 0xFF); | |
953 | (void)printf("\n"); | |
954 | } | |
955 | } | |
63ed9e52 | 956 | |
78288025 KB |
957 | usage() |
958 | { | |
959 | (void)fprintf(stderr, | |
960 | "usage: ping [-Rdfnqrv] [-c count] [-i wait] [-l preload]\n\t[-p pattern] [-s packetsize] host\n"); | |
961 | exit(1); | |
63ed9e52 | 962 | } |