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