Fixed binary search: no more infinite loops when vendor is unknown.
[tangerine.git] / workbench / network / stacks / AROSTCP / C / ping.c
blob1aad253b2960535fe3096b8f9521285d52484a55
1 /*
2 * P I N G . C
4 * Using the InterNet Control Message Protocol (ICMP) "ECHO" facility,
5 * measure round-trip-delays and packet loss across network paths.
7 * Author -
8 * Mike Muuss
9 * U. S. Army Ballistic Research Laboratory
10 * December, 1983
11 * Pavel Fedin
12 * September, 2005
14 * Status -
15 * Public Domain. Distribution Unlimited.
16 * Bugs -
17 * More statistics could always be gathered.
20 /****** netutil.doc/ping ****************************************************
22 NAME
23 ping - send ICMP ECHO_REQUEST packets to network hosts
25 SYNOPSIS
26 ping [-dfnqrvR] [-c count] [-i wait] [-l preload] [-p pattern]
27 [-s packetsize] [-L [ hosts ]] host
29 DESCRIPTION
30 Ping uses the ICMP protocol's mandatory ECHO_REQUEST datagram to
31 elicit an ICMP ECHO_RESPONSE from a host or gateway. ECHO_REQUEST
32 datagrams (``pings'') have an IP and ICMP header, followed by a
33 ``struct timeval'' and then an arbitrary number of ``pad'' bytes
34 used to fill out the packet. The options are as follows: Other
35 options are:
37 -c count
38 Stop after sending (and receiving) count ECHO_RESPONSE
39 packets.
41 -d Set the SO_DEBUG option on the socket being used.
43 -f Flood ping. Outputs packets as fast as they come back or
44 one hundred times per second, whichever is more. For every
45 ECHO_REQUEST sent a period ``.'' is printed, while for ever
46 ECHO_REPLY received a backspace is printed. This provides a
47 rapid display of how many packets are being dropped. Only
48 the super-user may use this option. This can be very hard
49 on a network and should be used with caution.
51 -i wait
52 Wait wait seconds between sending each packet. The default
53 is to wait for one second between each packet. This option
54 is incompatible with the -f option.
56 -L [hosts]
57 Use loose routing IP option. Includes IPOPT_LSRR option in
58 the ECHO_REQUEST packet with all specified hosts in the
59 route. Many hosts wont support loose routing, such a host
60 can either ignore or return the loose routed ICMP packet in
61 the middle of the route.
63 -l preload
64 If preload is specified, ping sends that many packets as
65 fast as possible before falling into its normal mode of
66 behavior.
68 -n Numeric output only. No attempt will be made to lookup
69 symbolic names for host addresses.
71 -p pattern
72 You may specify up to 16 ``pad'' bytes to fill out the
73 packet you send. This is useful for diagnosing
74 data-dependent problems in a network. For example, ``-p
75 ff'' will cause the sent packet to be filled with all ones.
77 -q Quiet output. Nothing is displayed except the summary lines
78 at startup time and when finished.
80 -R Record route. Includes the RECORD_ROUTE option in the
81 ECHO_REQUEST packet and displays the route buffer on
82 returned packets. Note that the IP header is only large
83 enough for nine such routes. Many hosts ignore or discard
84 this option.
86 -r Bypass the normal routing tables and send directly to a host
87 on an attached network. If the host is not on a
88 directly-attached network, an error is returned. This
89 option can be used to ping a local host through an interface
90 that has no route through it.
92 -s packetsize
93 Specifies the number of data bytes to be sent. The default
94 is 56, which translates into 64 ICMP data bytes when
95 combined with the 8 bytes of ICMP header data.
97 -v Verbose output. ICMP packets other than ECHO_RESPONSE that
98 are received are listed.
100 When using ping for fault isolation, it should first be run on the
101 local host, to verify that the local network interface is up and
102 running. Then, hosts and gateways further and further away should
103 be ``pinged''. Round-trip times and packet loss statistics are
104 computed. If duplicate packets are received, they are not included
105 in the packet loss calculation, although the round trip time of
106 these packets is used in calculating the minimum/average/maximum
107 round-trip time numbers. When the specified number of packets have
108 been sent (and received) or if the program is terminated with a
109 SIGINT, a brief summary is displayed.
111 This program is intended for use in network testing, measurement and
112 management. Because of the load it can impose on the network, it is
113 unwise to use ping during normal operations or from automated
114 scripts.
116 ICMP PACKET DETAILS
117 An IP header without options is 20 bytes. An ICMP ECHO_REQUEST
118 packet contains an additional 8 bytes worth of ICMP header followed
119 by an arbitrary amount of data. When a packetsize is given, this
120 indicated the size of this extra piece of data (the default is 56).
121 Thus the amount of data received inside of an IP packet of type ICMP
122 ECHO_REPLY will always be 8 bytes more than the requested data space
123 (the ICMP header).
125 If the data space is at least eight bytes large, ping uses the first
126 eight bytes of this space to include a timestamp which it uses in
127 the computation of round trip times. If less than eight bytes of
128 pad are specified, no round trip times are given.
130 DUPLICATE AND DAMAGED PACKETS
131 Ping will report duplicate and damaged packets. Duplicate packets
132 should never occur, and seem to be caused by inappropriate
133 link-level retransmissions. Duplicates may occur in many situations
134 and are rarely (if ever) a good sign, although the presence of low
135 levels of duplicates may not always be cause for alarm.
137 Damaged packets are obviously serious cause for alarm and often
138 indicate broken hardware somewhere in the ping packet's path (in the
139 network or in the hosts).
141 TRYING DIFFERENT DATA PATTERNS
142 The (inter)network layer should never treat packets differently
143 depending on the data contained in the data portion. Unfortunately,
144 data-dependent problems have been known to sneak into networks and
145 remain undetected for long periods of time. In many cases the
146 particular pattern that will have problems is something that doesn't
147 have sufficient ``transitions'', such as all ones or all zeros, or a
148 pattern right at the edge, such as almost all zeros. It isn't
149 necessarily enough to specify a data pattern of all zeros (for
150 example) on the command line because the pattern that is of interest
151 is at the data link level, and the relationship between what you
152 type and what the controllers transmit can be complicated.
154 This means that if you have a data-dependent problem you will
155 probably have to do a lot of testing to find it. If you are lucky,
156 you may manage to find a file that either can't be sent across your
157 network or that takes much longer to transfer than other similar
158 length files. You can then examine this file for repeated patterns
159 that you can test using the -p option of ping.
161 TTL DETAILS
162 The TTL value of an IP packet represents the maximum number of IP
163 routers that the packet can go through before being thrown away. In
164 current practice you can expect each router in the Internet to
165 decrement the TTL field by exactly one.
167 The TCP/IP specification states that the TTL field for TCP packets
168 should be set to 60, but many systems use smaller values (4.3 BSD
169 uses 30, 4.2 used 15). AROSTCP normally uses TTL value 30.
171 The maximum possible value of this field is 255, and most systems
172 set the TTL field of ICMP ECHO_REQUEST packets to 255. This is why
173 you will find you can ``ping'' some hosts, but not reach them with
174 telnet or ftp.
176 In normal operation ping prints the ttl value from the packet it re-
177 ceives. When a remote system receives a ping packet, it can do one
178 of three things with the TTL field in its response:
180 · Not change it; this is what Berkeley Unix systems did before the
181 4.3BSD-Tahoe release. In this case the TTL value in the
182 received packet will be 255 minus the number of routers in the
183 round-trip path.
185 · Set it to 255; this is what AROSTCP and current (as of 1994)
186 Berkeley Unix systems do. In this case the TTL value in the
187 received packet will be 255 minus the number of routers in the
188 path from the remote system to the pinging host.
190 · Set it to some other value. Some machines use the same value
191 for ICMP packets that they use for TCP packets, for example
192 either 30 or 60. Others may use completely wild values.
194 LOOSE SOURCE ROUTING DETAILS
195 When a packet is routed with loose routing in IP, the destination
196 address of datagram is originally set to the first address in the
197 routing list. When the datagram reaches its destination, the
198 destination address is changed to the next address in the list and
199 the datagram is routed to that destination. After the whole routing
200 list is exhausted, the datagram is handled to upper-level protocols.
202 The loose routing options can be ignored by hosts between the
203 gateways in the loose routing list. However, if the host in the
204 list don't understand loose routing, it may think that the datagram
205 is destined to it and respond to it. Also, many hosts simply drop
206 the packets with IP options.
208 BUGS
209 Many Hosts and Gateways ignore the RECORD_ROUTE and
210 LOOSE_SOURCE_ROUTING options.
212 The maximum IP header length is too small for options like
213 RECORD_ROUTE to be completely useful. There's not much that that
214 can be done about this, however.
216 Flood pinging is not recommended in general, and flood pinging the
217 broadcast address should only be done under very controlled
218 conditions.
220 SEE ALSO
221 netstat, ifconfig
223 AUTHOR
224 Mike Muuss, U. S. Army Ballistic Research Laboratory, December, 1983
226 The ping command appeared in 4.3BSD.
228 The loose routing and working record route options were added by
229 Pekka Pessi, AmiTCP/IP Group, Helsinki Univ. of Technology.
231 *****************************************************************************
232 * */
234 #ifndef AMIGA
235 #define AMIGA
236 #endif
238 #include <proto/socket.h>
240 #define ioctl IoctlSocket
242 #include <sys/param.h>
243 #include <sys/socket.h>
244 #include <sys/time.h>
246 #include <fcntl.h>
247 #include <signal.h>
248 #if 0 /* NC */
249 #include <fcntl.h>
250 #include <signal.h>
251 /*#include <sys/file.h>*/
252 /*#include <sys/signal.h>*/
253 #endif
255 #include <netinet/in_systm.h>
256 #include <netinet/in.h>
257 #include <netinet/ip.h>
258 #include <netinet/ip_icmp.h>
259 #include <netinet/ip_var.h>
260 #include <netdb.h>
261 #include <arpa/inet.h>
262 #include <unistd.h>
263 #include <stdio.h>
264 #include <ctype.h>
265 #include <sys/errno.h>
266 #include <string.h>
267 #include <stdlib.h>
269 #define DEFDATALEN (64 - 8) /* default data length */
270 #define MAXIPLEN 60
271 #define MAXICMPLEN 76
272 #define MAXPACKET (65536 - 60 - 8)/* max packet size */
273 #define MAXWAIT 10 /* max seconds to wait for response */
274 #define NROUTES 9 /* number of record route slots */
276 #define A(bit) rcvd_tbl[(bit)>>3] /* identify byte in array */
277 #define B(bit) (1 << ((bit) & 0x07)) /* identify bit in byte */
278 #define SET(bit) (A(bit) |= B(bit))
279 #define CLR(bit) (A(bit) &= (~B(bit)))
280 #define TST(bit) (A(bit) & B(bit))
282 /* various options */
283 int options;
284 #define F_FLOOD 0x001
285 #define F_INTERVAL 0x002
286 #define F_NUMERIC 0x004
287 #define F_PINGFILLED 0x008
288 #define F_QUIET 0x010
289 #define F_RROUTE 0x020
290 #define F_SO_DEBUG 0x040
291 #define F_SO_DONTROUTE 0x080
292 #define F_VERBOSE 0x100
293 #define F_LOOSEROUTE 0x200
295 #ifndef LONG_MAX
296 #define LONG_MAX 0xffffffff
297 #endif
300 * MAX_DUP_CHK is the number of bits in received table, i.e. the maximum
301 * number of received sequence numbers we can keep track of. Change 128
302 * to 8192 for complete accuracy...
304 #define MAX_DUP_CHK (8 * 128)
305 int mx_dup_ck = MAX_DUP_CHK;
306 char rcvd_tbl[MAX_DUP_CHK / 8];
308 struct sockaddr whereto; /* who to ping */
309 int datalen = DEFDATALEN;
310 int s = -1; /* socket file descriptor */
311 u_char *outpack;
312 char BSPACE = '\b'; /* characters written for flood */
313 char DOT = '.';
314 char *hostname;
315 long ident; /* process id to identify our packets */
317 /* counters */
318 long npackets; /* max packets to transmit */
319 long nreceived; /* # of packets we got back */
320 long nrepeats; /* number of duplicates */
321 long ntransmitted; /* sequence # for outbound packets = #sent */
322 int interval = 1; /* interval between packets */
324 /* timing */
325 int timing; /* flag to do timing */
326 unsigned long tmin = LONG_MAX; /* minimum round trip time */
327 unsigned long tmax; /* maximum round trip time */
328 unsigned long tsum; /* sum of all times, for doing average */
330 char *pr_addr(u_long l);
331 void catcher(void), pinger(void), finish(void), usage(void);
332 void pr_pack(char *buf, int cc, struct sockaddr_in *from);
333 void pr_icmph(struct icmp *icp);
334 void pr_iph(struct ip *ip);
335 void pr_retip(struct ip *ip);
336 void fill(char *bp, char *patp);
337 int in_cksum(u_short *addr, int len);
338 void tvsub(register struct timeval *out, register struct timeval *in);
339 VOID CleanUpExit(LONG error);
341 #ifdef AMIGA
343 * Other Amiga dependent stuff
345 #ifdef __SASC
346 #include <clib/exec_protos.h>
347 #include <pragmas/exec_sysbase_pragmas.h>
348 extern struct ExecBase *SysBase;
349 #endif
350 /* Disable ^C signaling */
351 void __chkabort(void) {}
353 #include <dos/dos.h>
354 #include <devices/timer.h>
356 #include <proto/exec.h>
357 #include <proto/timer.h>
359 struct MsgPort *timerport = NULL;
360 struct timerequest *timermsg = NULL;
361 BOOL notopen = TRUE;
362 #define TimerBase (timermsg->tr_node.io_Device)
364 #define SOCKET_VERSION 3
365 struct Library *SocketBase;
366 const TEXT version[] = "$VER: ping 3.10 (14.10.2005)";
367 const TEXT socket_name[] = "bsdsocket.library";
369 void
370 clean_timer(void)
372 if (timermsg) {
373 if (!notopen) {
374 if (!CheckIO((struct IORequest*)timermsg)) {
375 AbortIO((struct IORequest*)timermsg);
376 WaitIO((struct IORequest*)timermsg);
378 CloseDevice((struct IORequest*)timermsg);
379 notopen = TRUE;
381 DeleteIORequest((APTR)timermsg);
382 timermsg = NULL;
384 if (timerport) {
385 DeleteMsgPort(timerport);
386 timerport = NULL;
389 #endif
391 int main(argc, argv)
392 int argc;
393 char **argv;
395 extern int errno, optind;
396 extern char *optarg;
397 struct timeval timeout;
398 struct hostent *hp;
399 struct sockaddr_in *to;
400 struct protoent *proto;
401 register int i;
402 int ch, fdmask, hold, packlen, preload;
403 u_char *datap, *packet;
404 char *target, hnamebuf[MAXHOSTNAMELEN];
405 #ifdef IP_OPTIONS
406 u_char rspace[3 + 4 * NROUTES + 1]; /* record route space */
407 #endif
408 #ifdef AMIGA
409 ULONG timermask;
410 #endif
412 SocketBase = OpenLibrary(socket_name, SOCKET_VERSION);
413 if(SocketBase == NULL) {
414 fprintf(stderr, "ping: cannot open bsdsocket.library version 3.\n");
415 return RETURN_FAIL;
417 SetErrnoPtr(&errno, sizeof(errno));
419 outpack = malloc(MAXPACKET);
420 if (outpack == NULL) {
421 perror("ping");
422 CleanUpExit(1);
424 preload = 0;
425 datap = &outpack[8 + sizeof(struct timeval)];
426 while ((ch = getopt(argc, argv, "LRc:dfh:i:l:np:qrs:v")) != EOF)
427 switch(ch) {
428 case 'c':
429 npackets = atoi(optarg);
430 if (npackets <= 0) {
431 (void)fprintf(stderr, "ping: bad number of packets to transmit.\n");
432 CleanUpExit(1);
434 break;
435 case 'd':
436 options |= F_SO_DEBUG;
437 break;
438 case 'f':
439 #ifndef AMIGA
440 if (getuid()) {
441 (void)fprintf(stderr, "ping: %s\n", strerror(EPERM));
442 CleanUpExit(1);
444 #endif
445 options |= F_FLOOD;
446 setbuf(stdout, (char *)NULL);
447 break;
448 case 'i': /* wait between sending packets */
449 interval = atoi(optarg);
450 if (interval <= 0) {
451 (void)fprintf(stderr, "ping: bad timing interval.\n");
452 CleanUpExit(1);
454 options |= F_INTERVAL;
455 break;
456 case 'L':
457 options |= F_LOOSEROUTE;
458 break;
459 case 'l':
460 preload = atoi(optarg);
461 if (preload < 0) {
462 (void)fprintf(stderr, "ping: bad preload value.\n");
463 CleanUpExit(1);
465 break;
466 case 'n':
467 options |= F_NUMERIC;
468 break;
469 case 'p': /* fill buffer with user pattern */
470 options |= F_PINGFILLED;
471 fill((char *)datap, optarg);
472 break;
473 case 'q':
474 options |= F_QUIET;
475 break;
476 case 'R':
477 options |= F_RROUTE;
478 break;
479 case 'r':
480 options |= F_SO_DONTROUTE;
481 break;
482 case 's': /* size of packet to send */
483 datalen = atoi(optarg);
484 if (datalen > MAXPACKET) {
485 (void)fprintf(stderr, "ping: packet size too large.\n");
486 CleanUpExit(1);
488 if (datalen <= 0) {
489 (void)fprintf(stderr, "ping: illegal packet size.\n");
490 CleanUpExit(1);
492 break;
493 case 'v':
494 options |= F_VERBOSE;
495 break;
496 default:
497 usage();
499 argc -= optind;
500 argv += optind;
502 if (argc < 1 || (options & F_LOOSEROUTE) == 0 && argc > 1)
503 usage();
505 if ((options & (F_LOOSEROUTE | F_RROUTE)) == (F_LOOSEROUTE | F_RROUTE)) {
506 fprintf(stderr, "ping: -L and -R options cannot be used concurrently\n");
507 CleanUpExit(1);
511 u_char *cp = rspace;
513 if (options & F_LOOSEROUTE) {
514 #ifdef IP_OPTIONS
515 rspace[IPOPT_OPTVAL] = IPOPT_LSRR;
516 rspace[IPOPT_OLEN] = 3;
517 rspace[IPOPT_OFFSET] = IPOPT_MINOFF;
518 cp = rspace + IPOPT_OFFSET + 1;
519 #else
520 (void)fprintf(stderr, "ping: source routing not "
521 "available in this implementation.\n");
522 CleanUpExit(1);
523 #endif /* IP_OPTIONS */
526 while (target = *argv++) {
527 bzero((char *)&whereto, sizeof(struct sockaddr));
528 to = (struct sockaddr_in *)&whereto;
529 #ifdef _SOCKADDR_LEN
530 to->sin_len = sizeof(*to);
531 #endif
532 to->sin_family = AF_INET;
533 to->sin_addr.s_addr = inet_addr(target);
534 if (to->sin_addr.s_addr != (u_int)-1)
535 hostname = target;
536 else {
537 hp = gethostbyname(target);
538 if (!hp) {
539 fprintf(stderr, "ping: unknown host %s\n", target);
540 CleanUpExit(1);
542 to->sin_family = hp->h_addrtype;
543 bcopy(hp->h_addr, (caddr_t)&to->sin_addr, hp->h_length);
544 (void)strncpy(hnamebuf, hp->h_name, sizeof(hnamebuf) - 1);
545 hostname = hnamebuf;
548 #ifdef IP_OPTIONS
549 if (options & F_LOOSEROUTE) {
550 if (rspace + sizeof(rspace) - 4 < cp) {
551 fprintf(stderr, "ping: too many hops for "
552 "source routing %s\n", target);
553 CleanUpExit(1);
555 *cp++ = (to->sin_addr.s_addr >> 24);
556 *cp++ = (to->sin_addr.s_addr >> 16);
557 *cp++ = (to->sin_addr.s_addr >> 8);
558 *cp++ = (to->sin_addr.s_addr >> 0);
559 rspace[IPOPT_OLEN] += 4;
561 #endif
565 if (options & F_FLOOD && options & F_INTERVAL) {
566 (void)fprintf(stderr, "ping: -f and -i incompatible options.\n");
567 CleanUpExit(1);
570 if (datalen >= sizeof(struct timeval)) /* can we time transfer */
571 timing = 1;
572 packlen = datalen + MAXIPLEN + MAXICMPLEN;
573 if (!(packet = (u_char *)malloc((u_int)packlen))) {
574 (void)fprintf(stderr, "ping: out of memory.\n");
575 CleanUpExit(1);
577 if (!(options & F_PINGFILLED))
578 for (i = 8; i < datalen; ++i)
579 *datap++ = i;
581 ident = getpid() & 0xFFFF;
583 if (!(proto = getprotobyname("icmp"))) {
584 (void)fprintf(stderr, "ping: unknown protocol icmp.\n");
585 CleanUpExit(1);
588 #ifdef AMIGA
589 atexit(clean_timer);
591 timerport = CreateMsgPort();
592 if (!timerport) {
593 (void)fprintf(stderr, "ping: could not create timer port.\n");
594 CleanUpExit(1);
596 timermask = 1<<timerport->mp_SigBit;
598 timermsg = (APTR)CreateIORequest(timerport, sizeof(*timermsg));
599 if (!timermsg) {
600 (void)fprintf(stderr, "ping: could not create timer message.\n");
601 CleanUpExit(1);
604 if (notopen = OpenDevice("timer.device",
605 #if (AROS_FLAVOUR & AROS_FLAVOUR_EMULATION)
606 // UNIT_MICROHZ isn't available on AROS/hosted
607 UNIT_VBLANK,
608 #else
609 UNIT_MICROHZ,
610 #endif
611 (struct IORequest *)timermsg, 0)) {
612 (void)fprintf(stderr, "ping: could not open timer device.\n");
613 CleanUpExit(1);
616 timermsg->tr_node.io_Command = TR_ADDREQUEST;
617 timermsg->tr_time.tv_secs = 1L;
618 timermsg->tr_time.tv_micro = 0L;
619 /* don't confuse CheckIO */
620 timermsg->tr_node.io_Message.mn_Node.ln_Type = NT_REPLYMSG;
621 SetSocketSignals(timermask | SIGBREAKF_CTRL_C, 0L, 0L);
622 #endif
624 if ((s = socket(AF_INET, SOCK_RAW, proto->p_proto)) < 0) {
625 perror("ping: socket");
626 CleanUpExit(1);
628 hold = 1;
629 if (options & F_SO_DEBUG)
630 (void)setsockopt(s, SOL_SOCKET, SO_DEBUG, (char *)&hold,
631 sizeof(hold));
632 if (options & F_SO_DONTROUTE)
633 (void)setsockopt(s, SOL_SOCKET, SO_DONTROUTE, (char *)&hold,
634 sizeof(hold));
636 #ifdef IP_OPTIONS
637 if (options & F_LOOSEROUTE) {
638 /* pad to long word */
639 rspace[rspace[IPOPT_OLEN]] = IPOPT_EOL;
640 if (setsockopt(s, IPPROTO_IP, IP_OPTIONS, rspace,
641 rspace[IPOPT_OLEN] + 1) < 0) {
642 perror("ping: source routing");
643 CleanUpExit(1);
646 #endif
648 /* record route option */
649 if (options & F_RROUTE) {
650 #ifdef IP_OPTIONS
651 rspace[IPOPT_OPTVAL] = IPOPT_RR;
652 rspace[IPOPT_OLEN] = sizeof(rspace)-1;
653 rspace[IPOPT_OFFSET] = IPOPT_MINOFF;
654 rspace[rspace[IPOPT_OLEN]] = IPOPT_EOL;
655 if (setsockopt(s, IPPROTO_IP, IP_OPTIONS, rspace,
656 sizeof(rspace)) < 0) {
657 perror("ping: record route");
658 CleanUpExit(1);
660 #else
661 (void)fprintf(stderr,
662 "ping: record route not available in this implementation.\n");
663 CleanUpExit(1);
664 #endif /* IP_OPTIONS */
668 * When pinging the broadcast address, you can get a lot of answers.
669 * Doing something so evil is useful if you are trying to stress the
670 * ethernet, or just want to fill the arp cache to get some stuff for
671 * /etc/ethers.
673 hold = 48 * 1024;
674 (void)setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)&hold,
675 sizeof(hold));
677 if (to->sin_family == AF_INET)
678 (void)printf("PING %s (%s): %d data bytes\n", hostname,
679 inet_ntoa(*(struct in_addr *)&to->sin_addr.s_addr),
680 datalen);
681 else
682 (void)printf("PING %s: %d data bytes\n", hostname, datalen);
684 #ifndef AMIGA
685 (void)signal(SIGINT, finish);
686 (void)signal(SIGALRM, catcher);
687 #endif
689 while (preload--) /* fire off them quickies */
690 pinger();
692 if ((options & F_FLOOD) == 0)
693 catcher(); /* start things going */
695 for (;;) {
696 struct sockaddr_in from;
697 register int cc;
698 int fromlen;
700 #ifdef AMIGA
701 /* Check for special signals */
702 ULONG sm = SetSignal(0L, timermask | SIGBREAKF_CTRL_C);
703 if (sm & SIGBREAKF_CTRL_C)
704 finish();
705 if (sm & timermask && GetMsg(timerport))
706 catcher();
707 #endif
709 if (options & F_FLOOD) {
710 pinger();
711 timeout.tv_sec = 0;
712 timeout.tv_usec = 10000;
713 fdmask = 1 << s;
714 if (select(s + 1, (fd_set *)&fdmask, (fd_set *)NULL,
715 (fd_set *)NULL, &timeout) < 1)
716 continue;
718 fromlen = sizeof(from);
719 if ((cc = recvfrom(s, (char *)packet, packlen, 0,
720 (struct sockaddr *)&from, &fromlen)) < 0) {
721 if (errno == EINTR)
722 continue;
723 perror("ping: recvfrom");
724 continue;
726 pr_pack((char *)packet, cc, &from);
727 if (npackets && nreceived >= npackets)
728 break;
730 finish();
731 /* NOTREACHED */
732 return 0;
736 * catcher --
737 * This routine causes another PING to be transmitted, and then
738 * schedules another SIGALRM for 1 second from now.
740 * bug --
741 * Our sense of time will slowly skew (i.e., packets will not be
742 * launched exactly at 1-second intervals). This does not affect the
743 * quality of the delay and loss statistics.
745 * notes --
746 * This routine uses timer.device in Amiga implementation instead
747 * of SIGALRM.
749 void
750 catcher()
752 #ifdef AMIGA
753 static int waittime = 0;
755 if (waittime)
756 finish();
758 pinger();
760 if (!npackets || ntransmitted < npackets) {
761 timermsg->tr_time.tv_sec = interval;
762 SendIO((struct IORequest*)timermsg);
763 } else {
764 if (nreceived) {
765 waittime = 2 * tmax / 1000;
766 if (!waittime)
767 waittime = 1;
768 } else
769 waittime = MAXWAIT;
770 timermsg->tr_time.tv_sec = waittime;
771 SendIO((struct IORequest*)timermsg);
773 #else
774 int waittime;
776 pinger();
777 (void)signal(SIGALRM, catcher);
778 if (!npackets || ntransmitted < npackets)
779 alarm((u_int)interval);
780 else {
781 if (nreceived) {
782 waittime = 2 * tmax / 1000;
783 if (!waittime)
784 waittime = 1;
785 } else
786 waittime = MAXWAIT;
787 (void)signal(SIGALRM, finish);
788 (void)alarm((u_int)waittime);
790 #endif
794 * pinger --
795 * Compose and transmit an ICMP ECHO REQUEST packet. The IP packet
796 * will be added on by the kernel. The ID field is our process ID,
797 * and the sequence number is an ascending integer. The first 8 bytes
798 * of the data portion are used to hold a UNIX "timeval" struct in native
799 * byte-order, to compute the round-trip time.
801 void
802 pinger()
804 register struct icmp *icp;
805 register int cc;
806 int i;
808 icp = (struct icmp *)outpack;
809 icp->icmp_type = ICMP_ECHO;
810 icp->icmp_code = 0;
811 icp->icmp_cksum = 0;
812 icp->icmp_seq = ntransmitted++;
813 icp->icmp_id = ident; /* ID */
815 CLR(icp->icmp_seq % mx_dup_ck);
817 if (timing)
818 #ifdef AMIGA
819 /* (void)ReadEClock((struct EClockVal *)&outpack[8]);*/ /* NC */
820 (void)GetSysTime((struct timeval *)&outpack[8]);
821 #else
822 (void)gettimeofday((struct timeval *)&outpack[8],
823 (struct timezone *)NULL);
824 #endif
825 cc = datalen + 8; /* skips ICMP portion */
827 /* compute ICMP checksum here */
828 icp->icmp_cksum = in_cksum((u_short *)icp, cc);
830 i = sendto(s, (char *)outpack, cc, 0, &whereto,
831 sizeof(struct sockaddr));
833 if (i < 0 || i != cc) {
834 if (i < 0)
835 perror("ping: sendto");
836 (void)printf("ping: wrote %s %d chars, ret=%d\n",
837 hostname, cc, i);
839 if (!(options & F_QUIET) && options & F_FLOOD)
840 (void)write(1, &DOT, 1);
844 * pr_pack --
845 * Print out the packet, if it came from us. This logic is necessary
846 * because ALL readers of the ICMP socket get a copy of ALL ICMP packets
847 * which arrive ('tis only fair). This permits multiple copies of this
848 * program to be run without having intermingled output (or statistics!).
850 void
851 pr_pack(buf, cc, from)
852 char *buf;
853 int cc;
854 struct sockaddr_in *from;
856 register struct icmp *icp;
857 register u_long l;
858 register int i, j;
859 register u_char *cp,*dp;
860 static int old_rrlen;
861 static char old_rr[MAX_IPOPTLEN];
862 struct ip *ip;
863 struct timeval tv, *tp;
864 long triptime;
865 int hlen, dupflag;
867 #ifdef AMIGA
868 /* ULONG efreq = ReadEClock((struct EClockVal *)&tv);*/ /* NC */
869 GetSysTime((struct timeval *)&tv);
870 #else
871 (void)gettimeofday(&tv, (struct timezone *)NULL);
872 #endif
873 /* Check the IP header */
874 ip = (struct ip *)buf;
875 hlen = ip->ip_hl << 2;
876 if (cc < hlen + ICMP_MINLEN) {
877 if (options & F_VERBOSE)
878 (void)fprintf(stderr,
879 "ping: packet too short (%d bytes) from %s\n", cc,
880 inet_ntoa(*(struct in_addr *)&from->sin_addr.s_addr));
881 return;
884 /* Now the ICMP part */
885 cc -= hlen;
886 icp = (struct icmp *)(buf + hlen);
887 if (icp->icmp_type == ICMP_ECHOREPLY) {
888 if (icp->icmp_id != ident)
889 return; /* 'Twas not our ECHO */
890 ++nreceived;
891 if (timing) {
892 #ifndef icmp_data
893 tp = (struct timeval *)&icp->icmp_ip;
894 #else
895 tp = (struct timeval *)icp->icmp_data;
896 #endif
897 #ifndef AMIGA /* NC */
898 /* EClockVal is actually an unsigned long long */
899 if (tv.tv_micro < tp->tv_micro)
900 tv.tv_sec--;
901 tv.tv_micro -= tp->tv_micro;
902 tv.tv_secs -= tp->tv_secs;
903 triptime = tv.tv_micro / (efreq / 1000);
904 if (tv.tv_secs)
905 triptime += tv.tv_sec * 250 * ((1<<30) / efreq);
906 #else
907 tvsub(&tv, tp);
908 triptime = tv.tv_sec * 1000 + (tv.tv_usec / 1000);
909 #endif
910 tsum += triptime;
911 if (triptime < tmin)
912 tmin = triptime;
913 if (triptime > tmax)
914 tmax = triptime;
917 if (TST(icp->icmp_seq % mx_dup_ck)) {
918 ++nrepeats;
919 --nreceived;
920 dupflag = 1;
921 } else {
922 SET(icp->icmp_seq % mx_dup_ck);
923 dupflag = 0;
926 if (options & F_QUIET)
927 return;
929 if (options & F_FLOOD)
930 (void)write(1, &BSPACE, 1);
931 else {
932 (void)printf("%d bytes from %s: icmp_seq=%u", cc,
933 inet_ntoa(*(struct in_addr *)&from->sin_addr.s_addr),
934 icp->icmp_seq);
935 (void)printf(" ttl=%d", ip->ip_ttl);
936 if (timing)
937 (void)printf(" time=%ld ms", triptime);
938 if (dupflag)
939 (void)printf(" (DUP!)");
940 /* check the data */
941 cp = (u_char*)&icp->icmp_data[8];
942 dp = &outpack[8 + sizeof(struct timeval)];
943 for (i = 8; i < datalen; ++i, ++cp, ++dp) {
944 if (*cp != *dp) {
945 (void)printf("\nwrong data byte #%d should be 0x%x but was 0x%x",
946 i, *dp, *cp);
947 cp = (u_char*)&icp->icmp_data[0];
948 for (i = 8; i < datalen; ++i, ++cp) {
949 if ((i % 32) == 8)
950 (void)printf("\n\t");
951 (void)printf("%x ", *cp);
953 break;
957 } else {
958 /* We've got something other than an ECHOREPLY */
959 if (!(options & F_VERBOSE))
960 return;
961 (void)printf("%d bytes from %s: ", cc,
962 pr_addr(from->sin_addr.s_addr));
963 pr_icmph(icp);
966 /* Display any IP options */
967 /* The LSRR and RR len handling was broken (?) //ppessi */
968 cp = (u_char *)buf + sizeof(struct ip);
970 for (; hlen > (int)sizeof(struct ip); --hlen, ++cp)
971 switch (*cp) {
972 case IPOPT_EOL:
973 hlen = 0;
974 break;
975 case IPOPT_LSRR:
976 case IPOPT_SSRR:
977 (void)printf(*cp == IPOPT_LSRR ?
978 "\nLSRR: " : "\nSSRR: ");
979 j = *++cp;
980 ++cp;
981 hlen -= j;
982 if (j > IPOPT_MINOFF)
983 for (;;) {
984 l = *++cp;
985 l = (l<<8) + *++cp;
986 l = (l<<8) + *++cp;
987 l = (l<<8) + *++cp;
988 if (l == 0)
989 (void)printf("\t0.0.0.0");
990 else
991 (void)printf("\t%s", pr_addr(ntohl(l)));
992 j -= 4;
993 if (j < IPOPT_MINOFF)
994 break;
995 (void)putchar('\n');
997 break;
998 case IPOPT_RR:
999 j = *++cp; /* get length */
1000 i = *++cp; /* and pointer */
1001 hlen -= j;
1002 if (i > j)
1003 i = j;
1004 i -= IPOPT_MINOFF;
1005 if (i <= 0)
1006 continue;
1007 if (i == old_rrlen
1008 && cp == (u_char *)buf + sizeof(struct ip) + 2
1009 && !bcmp((char *)cp, old_rr, i)
1010 && !(options & F_FLOOD)) {
1011 (void)printf("\t(same route)");
1012 i = ((i + 3) / 4) * 4;
1013 cp += i;
1014 break;
1016 old_rrlen = i;
1017 bcopy((char *)cp, old_rr, i);
1018 (void)printf("\nRR: ");
1019 for (;;) {
1020 l = *++cp;
1021 l = (l<<8) + *++cp;
1022 l = (l<<8) + *++cp;
1023 l = (l<<8) + *++cp;
1024 if (l == 0)
1025 (void)printf("\t0.0.0.0");
1026 else
1027 (void)printf("\t%s", pr_addr(ntohl(l)));
1028 i -= 4;
1029 if (i <= 0)
1030 break;
1031 (void)putchar('\n');
1033 break;
1034 case IPOPT_NOP:
1035 (void)printf("\nNOP");
1036 break;
1037 default:
1038 (void)printf("\nunknown option %x", *cp);
1039 break;
1041 if (!(options & F_FLOOD)) {
1042 (void)putchar('\n');
1043 (void)fflush(stdout);
1048 * in_cksum --
1049 * Checksum routine for Internet Protocol family headers (C Version)
1052 in_cksum(addr, len)
1053 u_short *addr;
1054 int len;
1056 register int nleft = len;
1057 register u_short *w = addr;
1058 register int sum = 0;
1059 u_short answer = 0;
1062 * Our algorithm is simple, using a 32 bit accumulator (sum), we add
1063 * sequential 16 bit words to it, and at the end, fold back all the
1064 * carry bits from the top 16 bits into the lower 16 bits.
1066 while (nleft > 1) {
1067 sum += *w++;
1068 nleft -= 2;
1071 /* mop up an odd byte, if necessary */
1072 if (nleft == 1) {
1073 *(u_char *)(&answer) = *(u_char *)w ;
1074 sum += answer;
1077 /* add back carry outs from top 16 bits to low 16 bits */
1078 sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
1079 sum += (sum >> 16); /* add carry */
1080 answer = ~sum; /* truncate to 16 bits */
1081 return(answer);
1085 * tvsub --
1086 * Subtract 2 timeval structs: out = out - in. Out is assumed to
1087 * be >= in.
1089 void
1090 tvsub(out, in)
1091 register struct timeval *out, *in;
1093 if ((out->tv_usec -= in->tv_usec) < 0) {
1094 --out->tv_sec;
1095 out->tv_usec += 1000000;
1097 out->tv_sec -= in->tv_sec;
1101 * finish --
1102 * Print out statistics, and give up.
1104 void
1105 finish()
1107 (void)signal(SIGINT, SIG_IGN);
1108 (void)putchar('\n');
1109 (void)fflush(stdout);
1110 (void)printf("--- %s ping statistics ---\n", hostname);
1111 (void)printf("%ld packets transmitted, ", ntransmitted);
1112 (void)printf("%ld packets received, ", nreceived);
1113 if (nrepeats)
1114 (void)printf("+%ld duplicates, ", nrepeats);
1115 if (ntransmitted)
1116 if (nreceived > ntransmitted)
1117 (void)printf("-- somebody's printing up packets!");
1118 else
1119 (void)printf("%d%% packet loss",
1120 (int) (((ntransmitted - nreceived) * 100) /
1121 ntransmitted));
1122 (void)putchar('\n');
1123 if (nreceived && timing)
1124 (void)printf("round-trip min/avg/max = %ld/%lu/%ld ms\n",
1125 tmin, tsum / (nreceived + nrepeats), tmax);
1126 CleanUpExit(0);
1129 #ifdef notdef
1130 static char *ttab[] = {
1131 "Echo Reply", /* ip + seq + udata */
1132 "Dest Unreachable", /* net, host, proto, port, frag, sr + IP */
1133 "Source Quench", /* IP */
1134 "Redirect", /* redirect type, gateway, + IP */
1135 "Echo",
1136 "Time Exceeded", /* transit, frag reassem + IP */
1137 "Parameter Problem", /* pointer + IP */
1138 "Timestamp", /* id + seq + three timestamps */
1139 "Timestamp Reply", /* " */
1140 "Info Request", /* id + sq */
1141 "Info Reply" /* " */
1143 #endif
1146 * pr_icmph --
1147 * Print a descriptive string about an ICMP header.
1149 void
1150 pr_icmph(icp)
1151 struct icmp *icp;
1153 switch(icp->icmp_type) {
1154 case ICMP_ECHOREPLY:
1155 (void)printf("Echo Reply\n");
1156 /* XXX ID + Seq + Data */
1157 break;
1158 case ICMP_UNREACH:
1159 switch(icp->icmp_code) {
1160 case ICMP_UNREACH_NET:
1161 (void)printf("Destination Net Unreachable\n");
1162 break;
1163 case ICMP_UNREACH_HOST:
1164 (void)printf("Destination Host Unreachable\n");
1165 break;
1166 case ICMP_UNREACH_PROTOCOL:
1167 (void)printf("Destination Protocol Unreachable\n");
1168 break;
1169 case ICMP_UNREACH_PORT:
1170 (void)printf("Destination Port Unreachable\n");
1171 break;
1172 case ICMP_UNREACH_NEEDFRAG:
1173 (void)printf("frag needed and DF set\n");
1174 break;
1175 case ICMP_UNREACH_SRCFAIL:
1176 (void)printf("Source Route Failed\n");
1177 break;
1178 default:
1179 (void)printf("Dest Unreachable, Bad Code: %d\n",
1180 icp->icmp_code);
1181 break;
1183 /* Print returned IP header information */
1184 #ifndef icmp_data
1185 pr_retip(&icp->icmp_ip);
1186 #else
1187 pr_retip((struct ip *)icp->icmp_data);
1188 #endif
1189 break;
1190 case ICMP_SOURCEQUENCH:
1191 (void)printf("Source Quench\n");
1192 #ifndef icmp_data
1193 pr_retip(&icp->icmp_ip);
1194 #else
1195 pr_retip((struct ip *)icp->icmp_data);
1196 #endif
1197 break;
1198 case ICMP_REDIRECT:
1199 switch(icp->icmp_code) {
1200 case ICMP_REDIRECT_NET:
1201 (void)printf("Redirect Network");
1202 break;
1203 case ICMP_REDIRECT_HOST:
1204 (void)printf("Redirect Host");
1205 break;
1206 case ICMP_REDIRECT_TOSNET:
1207 (void)printf("Redirect Type of Service and Network");
1208 break;
1209 case ICMP_REDIRECT_TOSHOST:
1210 (void)printf("Redirect Type of Service and Host");
1211 break;
1212 default:
1213 (void)printf("Redirect, Bad Code: %d", icp->icmp_code);
1214 break;
1216 (void)printf("(New addr: 0x%08lx)\n", icp->icmp_gwaddr.s_addr);
1217 #ifndef icmp_data
1218 pr_retip(&icp->icmp_ip);
1219 #else
1220 pr_retip((struct ip *)icp->icmp_data);
1221 #endif
1222 break;
1223 case ICMP_ECHO:
1224 (void)printf("Echo Request\n");
1225 /* XXX ID + Seq + Data */
1226 break;
1227 case ICMP_TIMXCEED:
1228 switch(icp->icmp_code) {
1229 case ICMP_TIMXCEED_INTRANS:
1230 (void)printf("Time to live exceeded\n");
1231 break;
1232 case ICMP_TIMXCEED_REASS:
1233 (void)printf("Frag reassembly time exceeded\n");
1234 break;
1235 default:
1236 (void)printf("Time exceeded, Bad Code: %d\n",
1237 icp->icmp_code);
1238 break;
1240 #ifndef icmp_data
1241 pr_retip(&icp->icmp_ip);
1242 #else
1243 pr_retip((struct ip *)icp->icmp_data);
1244 #endif
1245 break;
1246 case ICMP_PARAMPROB:
1247 (void)printf("Parameter problem: pointer = 0x%02x\n",
1248 icp->icmp_hun.ih_pptr);
1249 #ifndef icmp_data
1250 pr_retip(&icp->icmp_ip);
1251 #else
1252 pr_retip((struct ip *)icp->icmp_data);
1253 #endif
1254 break;
1255 case ICMP_TSTAMP:
1256 (void)printf("Timestamp\n");
1257 /* XXX ID + Seq + 3 timestamps */
1258 break;
1259 case ICMP_TSTAMPREPLY:
1260 (void)printf("Timestamp Reply\n");
1261 /* XXX ID + Seq + 3 timestamps */
1262 break;
1263 case ICMP_IREQ:
1264 (void)printf("Information Request\n");
1265 /* XXX ID + Seq */
1266 break;
1267 case ICMP_IREQREPLY:
1268 (void)printf("Information Reply\n");
1269 /* XXX ID + Seq */
1270 break;
1271 #ifdef ICMP_MASKREQ
1272 case ICMP_MASKREQ:
1273 (void)printf("Address Mask Request\n");
1274 break;
1275 #endif
1276 #ifdef ICMP_MASKREPLY
1277 case ICMP_MASKREPLY:
1278 (void)printf("Address Mask Reply\n");
1279 break;
1280 #endif
1281 default:
1282 (void)printf("Bad ICMP type: %d\n", icp->icmp_type);
1287 * pr_iph --
1288 * Print an IP header with options.
1290 void
1291 pr_iph(ip)
1292 struct ip *ip;
1294 int hlen;
1295 u_char *cp;
1297 hlen = ip->ip_hl << 2;
1298 cp = (u_char *)ip + 20; /* point to options */
1300 (void)printf("Vr HL TOS Len ID Flg off TTL Pro cks Src Dst Data\n");
1301 (void)printf(" %1x %1x %02x %04x %04x",
1302 ip->ip_v, ip->ip_hl, ip->ip_tos, ip->ip_len, ip->ip_id);
1303 (void)printf(" %1x %04x", ((ip->ip_off) & 0xe000) >> 13,
1304 (ip->ip_off) & 0x1fff);
1305 (void)printf(" %02x %02x %04x", ip->ip_ttl, ip->ip_p, ip->ip_sum);
1306 (void)printf(" %s ", inet_ntoa(*(struct in_addr *)&ip->ip_src.s_addr));
1307 (void)printf(" %s ", inet_ntoa(*(struct in_addr *)&ip->ip_dst.s_addr));
1308 /* dump and option bytes */
1309 while (hlen-- > 20) {
1310 (void)printf("%02x", *cp++);
1312 (void)putchar('\n');
1316 * pr_addr --
1317 * Return an ascii host address as a dotted quad and optionally with
1318 * a hostname.
1320 char *
1321 pr_addr(l)
1322 u_long l;
1324 struct hostent *hp;
1325 static char buf[80];
1326 union {
1327 struct in_addr addr;
1328 u_long l;
1329 } __tmp;
1331 __tmp.l = l;
1333 if ((options & F_NUMERIC) ||
1334 !(hp = gethostbyaddr((char *)&l, 4, AF_INET)))
1335 (void)sprintf(buf, "%s", inet_ntoa(__tmp.addr));
1336 else
1337 (void)sprintf(buf, "%s (%s)", hp->h_name,
1338 inet_ntoa(__tmp.addr));
1339 return(buf);
1343 * pr_retip --
1344 * Dump some info on a returned (via ICMP) IP packet.
1346 void
1347 pr_retip(ip)
1348 struct ip *ip;
1350 int hlen;
1351 u_char *cp;
1353 pr_iph(ip);
1354 hlen = ip->ip_hl << 2;
1355 cp = (u_char *)ip + hlen;
1357 if (ip->ip_p == 6)
1358 (void)printf("TCP: from port %u, to port %u (decimal)\n",
1359 (*cp * 256 + *(cp + 1)), (*(cp + 2) * 256 + *(cp + 3)));
1360 else if (ip->ip_p == 17)
1361 (void)printf("UDP: from port %u, to port %u (decimal)\n",
1362 (*cp * 256 + *(cp + 1)), (*(cp + 2) * 256 + *(cp + 3)));
1365 void
1366 fill(bp, patp)
1367 char *bp, *patp;
1369 register int ii, jj, kk;
1370 int pat[16];
1371 char *cp;
1373 for (cp = patp; *cp; cp++)
1374 if (!isxdigit(*cp)) {
1375 (void)fprintf(stderr,
1376 "ping: patterns must be specified as hex digits.\n");
1377 CleanUpExit(1);
1379 ii = sscanf(patp,
1380 "%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x",
1381 &pat[0], &pat[1], &pat[2], &pat[3], &pat[4], &pat[5], &pat[6],
1382 &pat[7], &pat[8], &pat[9], &pat[10], &pat[11], &pat[12],
1383 &pat[13], &pat[14], &pat[15]);
1385 if (ii > 0)
1386 for (kk = 0; kk <= MAXPACKET - (8 + ii); kk += ii)
1387 for (jj = 0; jj < ii; ++jj)
1388 bp[jj + kk] = pat[jj];
1389 if (!(options & F_QUIET)) {
1390 (void)printf("PATTERN: 0x");
1391 for (jj = 0; jj < ii; ++jj)
1392 (void)printf("%02x", bp[jj] & 0xFF);
1393 (void)printf("\n");
1397 void
1398 usage()
1400 (void)fprintf(stderr,
1401 "usage: ping [-Rdfnqrv] [-c count] [-i wait] [-l preload]\n"
1402 "\t[-p pattern] [-s packetsize] [-L [hosts]] host\n");
1403 CleanUpExit(1);
1406 VOID CleanUpExit(LONG error)
1408 CloseLibrary(SocketBase);
1409 exit(error);