Check for SYS/GL during library init. Reason is that
[AROS.git] / workbench / network / common / C / ping.c
blob86933464278d72dc08eb67354a5284f474390a98
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 <aros/config.h>
240 #include <proto/socket.h>
242 #define ioctl IoctlSocket
244 #include <sys/param.h>
245 #include <sys/socket.h>
246 #include <sys/time.h>
248 #include <fcntl.h>
249 #include <signal.h>
250 #include <sys/file.h>
252 #include <netinet/in_systm.h>
253 #include <netinet/in.h>
254 #include <netinet/ip.h>
255 #include <netinet/ip_icmp.h>
256 #include <netinet/ip_var.h>
257 #include <netdb.h>
258 #include <arpa/inet.h>
259 #include <unistd.h>
260 #include <stdio.h>
261 #include <ctype.h>
262 #include <sys/errno.h>
263 #include <string.h>
264 #include <stdlib.h>
266 #define DEFDATALEN (64 - 8) /* default data length */
267 #define MAXIPLEN 60
268 #define MAXICMPLEN 76
269 #define MAXPACKET (65536 - 60 - 8)/* max packet size */
270 #define MAXWAIT 10 /* max seconds to wait for response */
271 #define NROUTES 9 /* number of record route slots */
273 #define A(bit) rcvd_tbl[(bit)>>3] /* identify byte in array */
274 #define B(bit) (1 << ((bit) & 0x07)) /* identify bit in byte */
275 #define SET(bit) (A(bit) |= B(bit))
276 #define CLR(bit) (A(bit) &= (~B(bit)))
277 #define TST(bit) (A(bit) & B(bit))
279 /* various options */
280 int options;
281 #define F_FLOOD 0x001
282 #define F_INTERVAL 0x002
283 #define F_NUMERIC 0x004
284 #define F_PINGFILLED 0x008
285 #define F_QUIET 0x010
286 #define F_RROUTE 0x020
287 #define F_SO_DEBUG 0x040
288 #define F_SO_DONTROUTE 0x080
289 #define F_VERBOSE 0x100
290 #define F_LOOSEROUTE 0x200
292 #ifndef LONG_MAX
293 #define LONG_MAX 0xffffffff
294 #endif
297 * MAX_DUP_CHK is the number of bits in received table, i.e. the maximum
298 * number of received sequence numbers we can keep track of. Change 128
299 * to 8192 for complete accuracy...
301 #define MAX_DUP_CHK (8 * 128)
302 int mx_dup_ck = MAX_DUP_CHK;
303 char rcvd_tbl[MAX_DUP_CHK / 8];
305 struct sockaddr whereto; /* who to ping */
306 int datalen = DEFDATALEN;
307 int s = -1; /* socket file descriptor */
308 u_char *outpack;
309 char BSPACE = '\b'; /* characters written for flood */
310 char DOT = '.';
311 char *hostname;
312 long ident; /* process id to identify our packets */
314 /* counters */
315 long npackets; /* max packets to transmit */
316 long nreceived; /* # of packets we got back */
317 long nrepeats; /* number of duplicates */
318 long ntransmitted; /* sequence # for outbound packets = #sent */
319 int interval = 1; /* interval between packets */
321 /* timing */
322 int timing; /* flag to do timing */
323 unsigned long tmin = LONG_MAX; /* minimum round trip time */
324 unsigned long tmax; /* maximum round trip time */
325 unsigned long tsum; /* sum of all times, for doing average */
327 char *pr_addr(u_long l);
328 void catcher(void), pinger(void), finish(void), usage(void);
329 void pr_pack(char *buf, int cc, struct sockaddr_in *from);
330 void pr_icmph(struct icmp *icp);
331 void pr_iph(struct ip *ip);
332 void pr_retip(struct ip *ip);
333 void fill(char *bp, char *patp);
334 int in_cksum(u_short *addr, int len);
335 void tvsub(register struct timeval *out, register struct timeval *in);
336 VOID CleanUpExit(LONG error);
338 #ifdef AMIGA
340 * Other Amiga dependent stuff
342 #ifdef __SASC
343 #include <clib/exec_protos.h>
344 #include <pragmas/exec_sysbase_pragmas.h>
345 extern struct ExecBase *SysBase;
346 #endif
347 /* Disable ^C signaling */
348 void __chkabort(void) {}
350 #include <dos/dos.h>
351 #include <devices/timer.h>
353 #include <proto/exec.h>
354 #include <proto/timer.h>
356 struct MsgPort *timerport = NULL;
357 struct timerequest *timermsg = NULL;
358 BOOL notopen = TRUE;
359 #define TimerBase (timermsg->tr_node.io_Device)
361 #define SOCKET_VERSION 3
362 struct Library *SocketBase;
363 const TEXT version[] = "$VER: ping 3.10 (14.10.2005)";
364 const TEXT socket_name[] = "bsdsocket.library";
366 void
367 clean_timer(void)
369 if (timermsg) {
370 if (!notopen) {
371 if (!CheckIO((struct IORequest*)timermsg)) {
372 AbortIO((struct IORequest*)timermsg);
373 WaitIO((struct IORequest*)timermsg);
375 CloseDevice((struct IORequest*)timermsg);
376 notopen = TRUE;
378 DeleteIORequest((APTR)timermsg);
379 timermsg = NULL;
381 if (timerport) {
382 DeleteMsgPort(timerport);
383 timerport = NULL;
386 #endif
388 int main(argc, argv)
389 int argc;
390 char **argv;
392 extern int optind;
393 extern char *optarg;
394 struct timeval timeout;
395 struct hostent *hp;
396 struct sockaddr_in *to;
397 struct protoent *proto;
398 register int i;
399 int ch, fdmask, hold, packlen, preload;
400 u_char *datap, *packet;
401 char *target, hnamebuf[MAXHOSTNAMELEN];
402 #ifdef IP_OPTIONS
403 u_char rspace[3 + 4 * NROUTES + 1]; /* record route space */
404 #endif
405 #ifdef AMIGA
406 ULONG timermask;
407 #endif
409 SocketBase = OpenLibrary(socket_name, SOCKET_VERSION);
410 if(SocketBase == NULL) {
411 fprintf(stderr, "ping: cannot open bsdsocket.library version 3.\n");
412 return RETURN_FAIL;
414 SetErrnoPtr(&errno, sizeof(errno));
416 outpack = malloc(MAXPACKET);
417 if (outpack == NULL) {
418 perror("ping");
419 CleanUpExit(1);
421 preload = 0;
422 datap = &outpack[8 + sizeof(struct timeval)];
423 while ((ch = getopt(argc, argv, "LRc:dfh:i:l:np:qrs:v")) != EOF)
424 switch(ch) {
425 case 'c':
426 npackets = atoi(optarg);
427 if (npackets <= 0) {
428 (void)fprintf(stderr, "ping: bad number of packets to transmit.\n");
429 CleanUpExit(1);
431 break;
432 case 'd':
433 options |= F_SO_DEBUG;
434 break;
435 case 'f':
436 #ifndef AMIGA
437 if (getuid()) {
438 (void)fprintf(stderr, "ping: %s\n", strerror(EPERM));
439 CleanUpExit(1);
441 #endif
442 options |= F_FLOOD;
443 setbuf(stdout, (char *)NULL);
444 break;
445 case 'i': /* wait between sending packets */
446 interval = atoi(optarg);
447 if (interval <= 0) {
448 (void)fprintf(stderr, "ping: bad timing interval.\n");
449 CleanUpExit(1);
451 options |= F_INTERVAL;
452 break;
453 case 'L':
454 options |= F_LOOSEROUTE;
455 break;
456 case 'l':
457 preload = atoi(optarg);
458 if (preload < 0) {
459 (void)fprintf(stderr, "ping: bad preload value.\n");
460 CleanUpExit(1);
462 break;
463 case 'n':
464 options |= F_NUMERIC;
465 break;
466 case 'p': /* fill buffer with user pattern */
467 options |= F_PINGFILLED;
468 fill((char *)datap, optarg);
469 break;
470 case 'q':
471 options |= F_QUIET;
472 break;
473 case 'R':
474 options |= F_RROUTE;
475 break;
476 case 'r':
477 options |= F_SO_DONTROUTE;
478 break;
479 case 's': /* size of packet to send */
480 datalen = atoi(optarg);
481 if (datalen > MAXPACKET) {
482 (void)fprintf(stderr, "ping: packet size too large.\n");
483 CleanUpExit(1);
485 if (datalen <= 0) {
486 (void)fprintf(stderr, "ping: illegal packet size.\n");
487 CleanUpExit(1);
489 break;
490 case 'v':
491 options |= F_VERBOSE;
492 break;
493 default:
494 usage();
496 argc -= optind;
497 argv += optind;
499 if (argc < 1 || (options & F_LOOSEROUTE) == 0 && argc > 1)
500 usage();
502 if ((options & (F_LOOSEROUTE | F_RROUTE)) == (F_LOOSEROUTE | F_RROUTE)) {
503 fprintf(stderr, "ping: -L and -R options cannot be used concurrently\n");
504 CleanUpExit(1);
508 u_char *cp = rspace;
510 if (options & F_LOOSEROUTE) {
511 #ifdef IP_OPTIONS
512 rspace[IPOPT_OPTVAL] = IPOPT_LSRR;
513 rspace[IPOPT_OLEN] = 3;
514 rspace[IPOPT_OFFSET] = IPOPT_MINOFF;
515 cp = rspace + IPOPT_OFFSET + 1;
516 #else
517 (void)fprintf(stderr, "ping: source routing not "
518 "available in this implementation.\n");
519 CleanUpExit(1);
520 #endif /* IP_OPTIONS */
523 while (target = *argv++) {
524 bzero((char *)&whereto, sizeof(struct sockaddr));
525 to = (struct sockaddr_in *)&whereto;
526 #ifdef _SOCKADDR_LEN
527 to->sin_len = sizeof(*to);
528 #endif
529 to->sin_family = AF_INET;
530 to->sin_addr.s_addr = inet_addr(target);
531 if (to->sin_addr.s_addr != (u_int)-1)
532 hostname = target;
533 else {
534 hp = gethostbyname(target);
535 if (!hp) {
536 fprintf(stderr, "ping: unknown host %s\n", target);
537 CleanUpExit(1);
539 to->sin_family = hp->h_addrtype;
540 bcopy(hp->h_addr, (caddr_t)&to->sin_addr, hp->h_length);
541 (void)strncpy(hnamebuf, hp->h_name, sizeof(hnamebuf) - 1);
542 hostname = hnamebuf;
545 #ifdef IP_OPTIONS
546 if (options & F_LOOSEROUTE) {
547 if (rspace + sizeof(rspace) - 4 < cp) {
548 fprintf(stderr, "ping: too many hops for "
549 "source routing %s\n", target);
550 CleanUpExit(1);
552 *cp++ = (to->sin_addr.s_addr >> 24);
553 *cp++ = (to->sin_addr.s_addr >> 16);
554 *cp++ = (to->sin_addr.s_addr >> 8);
555 *cp++ = (to->sin_addr.s_addr >> 0);
556 rspace[IPOPT_OLEN] += 4;
558 #endif
562 if (options & F_FLOOD && options & F_INTERVAL) {
563 (void)fprintf(stderr, "ping: -f and -i incompatible options.\n");
564 CleanUpExit(1);
567 if (datalen >= sizeof(struct timeval)) /* can we time transfer */
568 timing = 1;
569 packlen = datalen + MAXIPLEN + MAXICMPLEN;
570 if (!(packet = (u_char *)malloc((u_int)packlen))) {
571 (void)fprintf(stderr, "ping: out of memory.\n");
572 CleanUpExit(1);
574 if (!(options & F_PINGFILLED))
575 for (i = 8; i < datalen; ++i)
576 *datap++ = i;
578 ident = getpid() & 0xFFFF;
580 if (!(proto = getprotobyname("icmp"))) {
581 (void)fprintf(stderr, "ping: unknown protocol icmp.\n");
582 CleanUpExit(1);
585 #ifdef AMIGA
586 atexit(clean_timer);
588 timerport = CreateMsgPort();
589 if (!timerport) {
590 (void)fprintf(stderr, "ping: could not create timer port.\n");
591 CleanUpExit(1);
593 timermask = 1<<timerport->mp_SigBit;
595 timermsg = (APTR)CreateIORequest(timerport, sizeof(*timermsg));
596 if (!timermsg) {
597 (void)fprintf(stderr, "ping: could not create timer message.\n");
598 CleanUpExit(1);
601 if (notopen = OpenDevice("timer.device",
602 #if (AROS_FLAVOUR & AROS_FLAVOUR_EMULATION)
603 // UNIT_MICROHZ isn't available on AROS/hosted
604 UNIT_VBLANK,
605 #else
606 UNIT_MICROHZ,
607 #endif
608 (struct IORequest *)timermsg, 0)) {
609 (void)fprintf(stderr, "ping: could not open timer device.\n");
610 CleanUpExit(1);
613 timermsg->tr_node.io_Command = TR_ADDREQUEST;
614 timermsg->tr_time.tv_secs = 1L;
615 timermsg->tr_time.tv_micro = 0L;
616 /* don't confuse CheckIO */
617 timermsg->tr_node.io_Message.mn_Node.ln_Type = NT_REPLYMSG;
618 SetSocketSignals(timermask | SIGBREAKF_CTRL_C, 0L, 0L);
619 #endif
621 if ((s = socket(AF_INET, SOCK_RAW, proto->p_proto)) < 0) {
622 perror("ping: socket");
623 CleanUpExit(1);
625 hold = 1;
626 if (options & F_SO_DEBUG)
627 (void)setsockopt(s, SOL_SOCKET, SO_DEBUG, (char *)&hold,
628 sizeof(hold));
629 if (options & F_SO_DONTROUTE)
630 (void)setsockopt(s, SOL_SOCKET, SO_DONTROUTE, (char *)&hold,
631 sizeof(hold));
633 #ifdef IP_OPTIONS
634 if (options & F_LOOSEROUTE) {
635 /* pad to long word */
636 rspace[rspace[IPOPT_OLEN]] = IPOPT_EOL;
637 if (setsockopt(s, IPPROTO_IP, IP_OPTIONS, rspace,
638 rspace[IPOPT_OLEN] + 1) < 0) {
639 perror("ping: source routing");
640 CleanUpExit(1);
643 #endif
645 /* record route option */
646 if (options & F_RROUTE) {
647 #ifdef IP_OPTIONS
648 rspace[IPOPT_OPTVAL] = IPOPT_RR;
649 rspace[IPOPT_OLEN] = sizeof(rspace)-1;
650 rspace[IPOPT_OFFSET] = IPOPT_MINOFF;
651 rspace[rspace[IPOPT_OLEN]] = IPOPT_EOL;
652 if (setsockopt(s, IPPROTO_IP, IP_OPTIONS, rspace,
653 sizeof(rspace)) < 0) {
654 perror("ping: record route");
655 CleanUpExit(1);
657 #else
658 (void)fprintf(stderr,
659 "ping: record route not available in this implementation.\n");
660 CleanUpExit(1);
661 #endif /* IP_OPTIONS */
665 * When pinging the broadcast address, you can get a lot of answers.
666 * Doing something so evil is useful if you are trying to stress the
667 * ethernet, or just want to fill the arp cache to get some stuff for
668 * /etc/ethers.
670 hold = 48 * 1024;
671 (void)setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)&hold,
672 sizeof(hold));
674 if (to->sin_family == AF_INET)
675 (void)printf("PING %s (%s): %d data bytes\n", hostname,
676 inet_ntoa(*(struct in_addr *)&to->sin_addr.s_addr),
677 datalen);
678 else
679 (void)printf("PING %s: %d data bytes\n", hostname, datalen);
681 #ifndef AMIGA
682 (void)signal(SIGINT, finish);
683 (void)signal(SIGALRM, catcher);
684 #endif
686 while (preload--) /* fire off them quickies */
687 pinger();
689 if ((options & F_FLOOD) == 0)
690 catcher(); /* start things going */
692 for (;;) {
693 struct sockaddr_in from;
694 register int cc;
695 int fromlen;
697 #ifdef AMIGA
698 /* Check for special signals */
699 ULONG sm = SetSignal(0L, timermask | SIGBREAKF_CTRL_C);
700 if (sm & SIGBREAKF_CTRL_C)
701 finish();
702 if (sm & timermask && GetMsg(timerport))
703 catcher();
704 #endif
706 if (options & F_FLOOD) {
707 pinger();
708 timeout.tv_sec = 0;
709 timeout.tv_usec = 10000;
710 fdmask = 1 << s;
711 if (select(s + 1, (fd_set *)&fdmask, (fd_set *)NULL,
712 (fd_set *)NULL, &timeout) < 1)
713 continue;
715 fromlen = sizeof(from);
716 if ((cc = recvfrom(s, (char *)packet, packlen, 0,
717 (struct sockaddr *)&from, &fromlen)) < 0) {
718 if (errno == EINTR)
719 continue;
720 perror("ping: recvfrom");
721 continue;
723 pr_pack((char *)packet, cc, &from);
724 if (npackets && nreceived >= npackets)
725 break;
727 finish();
728 /* NOTREACHED */
729 return 0;
733 * catcher --
734 * This routine causes another PING to be transmitted, and then
735 * schedules another SIGALRM for 1 second from now.
737 * bug --
738 * Our sense of time will slowly skew (i.e., packets will not be
739 * launched exactly at 1-second intervals). This does not affect the
740 * quality of the delay and loss statistics.
742 * notes --
743 * This routine uses timer.device in Amiga implementation instead
744 * of SIGALRM.
746 void
747 catcher()
749 #ifdef AMIGA
750 static int waittime = 0;
752 if (waittime)
753 finish();
755 pinger();
757 if (!npackets || ntransmitted < npackets) {
758 timermsg->tr_time.tv_sec = interval;
759 SendIO((struct IORequest*)timermsg);
760 } else {
761 if (nreceived) {
762 waittime = 2 * tmax / 1000;
763 if (!waittime)
764 waittime = 1;
765 } else
766 waittime = MAXWAIT;
767 timermsg->tr_time.tv_sec = waittime;
768 SendIO((struct IORequest*)timermsg);
770 #else
771 int waittime;
773 pinger();
774 (void)signal(SIGALRM, catcher);
775 if (!npackets || ntransmitted < npackets)
776 alarm((u_int)interval);
777 else {
778 if (nreceived) {
779 waittime = 2 * tmax / 1000;
780 if (!waittime)
781 waittime = 1;
782 } else
783 waittime = MAXWAIT;
784 (void)signal(SIGALRM, finish);
785 (void)alarm((u_int)waittime);
787 #endif
791 * pinger --
792 * Compose and transmit an ICMP ECHO REQUEST packet. The IP packet
793 * will be added on by the kernel. The ID field is our process ID,
794 * and the sequence number is an ascending integer. The first 8 bytes
795 * of the data portion are used to hold a UNIX "timeval" struct in native
796 * byte-order, to compute the round-trip time.
798 void
799 pinger()
801 register struct icmp *icp;
802 register int cc;
803 int i;
805 icp = (struct icmp *)outpack;
806 icp->icmp_type = ICMP_ECHO;
807 icp->icmp_code = 0;
808 icp->icmp_cksum = 0;
809 icp->icmp_seq = htons(ntransmitted++);
810 icp->icmp_id = ident; /* ID */
812 CLR(icp->icmp_seq % mx_dup_ck);
814 if (timing)
815 #ifdef AMIGA
816 /* (void)ReadEClock((struct EClockVal *)&outpack[8]);*/ /* NC */
817 (void)GetSysTime((struct timeval *)&outpack[8]);
818 #else
819 (void)gettimeofday((struct timeval *)&outpack[8],
820 (struct timezone *)NULL);
821 #endif
822 cc = datalen + 8; /* skips ICMP portion */
824 /* compute ICMP checksum here */
825 icp->icmp_cksum = in_cksum((u_short *)icp, cc);
827 i = sendto(s, (char *)outpack, cc, 0, &whereto,
828 sizeof(struct sockaddr));
830 if (i < 0 || i != cc) {
831 if (i < 0)
832 perror("ping: sendto");
833 (void)printf("ping: wrote %s %d chars, ret=%d\n",
834 hostname, cc, i);
836 if (!(options & F_QUIET) && options & F_FLOOD)
837 (void)write(1, &DOT, 1);
841 * pr_pack --
842 * Print out the packet, if it came from us. This logic is necessary
843 * because ALL readers of the ICMP socket get a copy of ALL ICMP packets
844 * which arrive ('tis only fair). This permits multiple copies of this
845 * program to be run without having intermingled output (or statistics!).
847 void
848 pr_pack(buf, cc, from)
849 char *buf;
850 int cc;
851 struct sockaddr_in *from;
853 register struct icmp *icp;
854 register u_long l;
855 register int i, j;
856 register u_char *cp,*dp;
857 static int old_rrlen;
858 static char old_rr[MAX_IPOPTLEN];
859 struct ip *ip;
860 struct timeval tv, *tp;
861 long triptime;
862 int hlen, dupflag;
864 #ifdef AMIGA
865 /* ULONG efreq = ReadEClock((struct EClockVal *)&tv);*/ /* NC */
866 GetSysTime((struct timeval *)&tv);
867 #else
868 (void)gettimeofday(&tv, (struct timezone *)NULL);
869 #endif
870 /* Check the IP header */
871 ip = (struct ip *)buf;
872 hlen = ip->ip_hl << 2;
873 if (cc < hlen + ICMP_MINLEN) {
874 if (options & F_VERBOSE)
875 (void)fprintf(stderr,
876 "ping: packet too short (%d bytes) from %s\n", cc,
877 inet_ntoa(*(struct in_addr *)&from->sin_addr.s_addr));
878 return;
881 /* Now the ICMP part */
882 cc -= hlen;
883 icp = (struct icmp *)(buf + hlen);
884 if (icp->icmp_type == ICMP_ECHOREPLY) {
885 if (icp->icmp_id != ident)
886 return; /* 'Twas not our ECHO */
887 ++nreceived;
888 if (timing) {
889 #ifndef icmp_data
890 tp = (struct timeval *)&icp->icmp_ip;
891 #else
892 tp = (struct timeval *)icp->icmp_data;
893 #endif
894 #ifndef AMIGA /* NC */
895 /* EClockVal is actually an unsigned long long */
896 if (tv.tv_micro < tp->tv_micro)
897 tv.tv_sec--;
898 tv.tv_micro -= tp->tv_micro;
899 tv.tv_secs -= tp->tv_secs;
900 triptime = tv.tv_micro / (efreq / 1000);
901 if (tv.tv_secs)
902 triptime += tv.tv_sec * 250 * ((1<<30) / efreq);
903 #else
904 tvsub(&tv, tp);
905 triptime = tv.tv_sec * 1000 + (tv.tv_usec / 1000);
906 #endif
907 tsum += triptime;
908 if (triptime < tmin)
909 tmin = triptime;
910 if (triptime > tmax)
911 tmax = triptime;
914 if (TST(icp->icmp_seq % mx_dup_ck)) {
915 ++nrepeats;
916 --nreceived;
917 dupflag = 1;
918 } else {
919 SET(icp->icmp_seq % mx_dup_ck);
920 dupflag = 0;
923 if (options & F_QUIET)
924 return;
926 if (options & F_FLOOD)
927 (void)write(1, &BSPACE, 1);
928 else {
929 (void)printf("%d bytes from %s: icmp_seq=%u", cc,
930 inet_ntoa(*(struct in_addr *)&from->sin_addr.s_addr),
931 ntohs(icp->icmp_seq));
932 (void)printf(" ttl=%d", ip->ip_ttl);
933 if (timing)
934 (void)printf(" time=%ld ms", triptime);
935 if (dupflag)
936 (void)printf(" (DUP!)");
937 /* check the data */
938 cp = (u_char*)&icp->icmp_data[8];
939 dp = &outpack[8 + sizeof(struct timeval)];
940 for (i = 8; i < datalen; ++i, ++cp, ++dp) {
941 if (*cp != *dp) {
942 (void)printf("\nwrong data byte #%d should be 0x%x but was 0x%x",
943 i, *dp, *cp);
944 cp = (u_char*)&icp->icmp_data[0];
945 for (i = 8; i < datalen; ++i, ++cp) {
946 if ((i % 32) == 8)
947 (void)printf("\n\t");
948 (void)printf("%x ", *cp);
950 break;
954 } else {
955 /* We've got something other than an ECHOREPLY */
956 if (!(options & F_VERBOSE))
957 return;
958 (void)printf("%d bytes from %s: ", cc,
959 pr_addr(from->sin_addr.s_addr));
960 pr_icmph(icp);
963 /* Display any IP options */
964 /* The LSRR and RR len handling was broken (?) //ppessi */
965 cp = (u_char *)buf + sizeof(struct ip);
967 for (; hlen > (int)sizeof(struct ip); --hlen, ++cp)
968 switch (*cp) {
969 case IPOPT_EOL:
970 hlen = 0;
971 break;
972 case IPOPT_LSRR:
973 case IPOPT_SSRR:
974 (void)printf(*cp == IPOPT_LSRR ?
975 "\nLSRR: " : "\nSSRR: ");
976 j = *++cp;
977 ++cp;
978 hlen -= j;
979 if (j > IPOPT_MINOFF)
980 for (;;) {
981 l = *++cp;
982 l = (l<<8) + *++cp;
983 l = (l<<8) + *++cp;
984 l = (l<<8) + *++cp;
985 if (l == 0)
986 (void)printf("\t0.0.0.0");
987 else
988 (void)printf("\t%s", pr_addr(ntohl(l)));
989 j -= 4;
990 if (j < IPOPT_MINOFF)
991 break;
992 (void)putchar('\n');
994 break;
995 case IPOPT_RR:
996 j = *++cp; /* get length */
997 i = *++cp; /* and pointer */
998 hlen -= j;
999 if (i > j)
1000 i = j;
1001 i -= IPOPT_MINOFF;
1002 if (i <= 0)
1003 continue;
1004 if (i == old_rrlen
1005 && cp == (u_char *)buf + sizeof(struct ip) + 2
1006 && !bcmp((char *)cp, old_rr, i)
1007 && !(options & F_FLOOD)) {
1008 (void)printf("\t(same route)");
1009 i = ((i + 3) / 4) * 4;
1010 cp += i;
1011 break;
1013 old_rrlen = i;
1014 bcopy((char *)cp, old_rr, i);
1015 (void)printf("\nRR: ");
1016 for (;;) {
1017 l = *++cp;
1018 l = (l<<8) + *++cp;
1019 l = (l<<8) + *++cp;
1020 l = (l<<8) + *++cp;
1021 if (l == 0)
1022 (void)printf("\t0.0.0.0");
1023 else
1024 (void)printf("\t%s", pr_addr(ntohl(l)));
1025 i -= 4;
1026 if (i <= 0)
1027 break;
1028 (void)putchar('\n');
1030 break;
1031 case IPOPT_NOP:
1032 (void)printf("\nNOP");
1033 break;
1034 default:
1035 (void)printf("\nunknown option %x", *cp);
1036 break;
1038 if (!(options & F_FLOOD)) {
1039 (void)putchar('\n');
1040 (void)fflush(stdout);
1045 * in_cksum --
1046 * Checksum routine for Internet Protocol family headers (C Version)
1049 in_cksum(addr, len)
1050 u_short *addr;
1051 int len;
1053 register int nleft = len;
1054 register u_short *w = addr;
1055 register int sum = 0;
1056 u_short answer = 0;
1059 * Our algorithm is simple, using a 32 bit accumulator (sum), we add
1060 * sequential 16 bit words to it, and at the end, fold back all the
1061 * carry bits from the top 16 bits into the lower 16 bits.
1063 while (nleft > 1) {
1064 sum += *w++;
1065 nleft -= 2;
1068 /* mop up an odd byte, if necessary */
1069 if (nleft == 1) {
1070 *(u_char *)(&answer) = *(u_char *)w ;
1071 sum += answer;
1074 /* add back carry outs from top 16 bits to low 16 bits */
1075 sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
1076 sum += (sum >> 16); /* add carry */
1077 answer = ~sum; /* truncate to 16 bits */
1078 return(answer);
1082 * tvsub --
1083 * Subtract 2 timeval structs: out = out - in. Out is assumed to
1084 * be >= in.
1086 void
1087 tvsub(out, in)
1088 register struct timeval *out, *in;
1090 if ((out->tv_usec -= in->tv_usec) < 0) {
1091 --out->tv_sec;
1092 out->tv_usec += 1000000;
1094 out->tv_sec -= in->tv_sec;
1098 * finish --
1099 * Print out statistics, and give up.
1101 void
1102 finish()
1104 (void)signal(SIGINT, SIG_IGN);
1105 (void)putchar('\n');
1106 (void)fflush(stdout);
1107 (void)printf("--- %s ping statistics ---\n", hostname);
1108 (void)printf("%ld packets transmitted, ", ntransmitted);
1109 (void)printf("%ld packets received, ", nreceived);
1110 if (nrepeats)
1111 (void)printf("+%ld duplicates, ", nrepeats);
1112 if (ntransmitted)
1113 if (nreceived > ntransmitted)
1114 (void)printf("-- somebody's printing up packets!");
1115 else
1116 (void)printf("%d%% packet loss",
1117 (int) (((ntransmitted - nreceived) * 100) /
1118 ntransmitted));
1119 (void)putchar('\n');
1120 if (nreceived && timing)
1121 (void)printf("round-trip min/avg/max = %ld/%lu/%ld ms\n",
1122 tmin, tsum / (nreceived + nrepeats), tmax);
1123 CleanUpExit(0);
1126 #ifdef notdef
1127 static char *ttab[] = {
1128 "Echo Reply", /* ip + seq + udata */
1129 "Dest Unreachable", /* net, host, proto, port, frag, sr + IP */
1130 "Source Quench", /* IP */
1131 "Redirect", /* redirect type, gateway, + IP */
1132 "Echo",
1133 "Time Exceeded", /* transit, frag reassem + IP */
1134 "Parameter Problem", /* pointer + IP */
1135 "Timestamp", /* id + seq + three timestamps */
1136 "Timestamp Reply", /* " */
1137 "Info Request", /* id + sq */
1138 "Info Reply" /* " */
1140 #endif
1143 * pr_icmph --
1144 * Print a descriptive string about an ICMP header.
1146 void
1147 pr_icmph(icp)
1148 struct icmp *icp;
1150 switch(icp->icmp_type) {
1151 case ICMP_ECHOREPLY:
1152 (void)printf("Echo Reply\n");
1153 /* XXX ID + Seq + Data */
1154 break;
1155 case ICMP_UNREACH:
1156 switch(icp->icmp_code) {
1157 case ICMP_UNREACH_NET:
1158 (void)printf("Destination Net Unreachable\n");
1159 break;
1160 case ICMP_UNREACH_HOST:
1161 (void)printf("Destination Host Unreachable\n");
1162 break;
1163 case ICMP_UNREACH_PROTOCOL:
1164 (void)printf("Destination Protocol Unreachable\n");
1165 break;
1166 case ICMP_UNREACH_PORT:
1167 (void)printf("Destination Port Unreachable\n");
1168 break;
1169 case ICMP_UNREACH_NEEDFRAG:
1170 (void)printf("frag needed and DF set\n");
1171 break;
1172 case ICMP_UNREACH_SRCFAIL:
1173 (void)printf("Source Route Failed\n");
1174 break;
1175 default:
1176 (void)printf("Dest Unreachable, Bad Code: %d\n",
1177 icp->icmp_code);
1178 break;
1180 /* Print returned IP header information */
1181 #ifndef icmp_data
1182 pr_retip(&icp->icmp_ip);
1183 #else
1184 pr_retip((struct ip *)icp->icmp_data);
1185 #endif
1186 break;
1187 case ICMP_SOURCEQUENCH:
1188 (void)printf("Source Quench\n");
1189 #ifndef icmp_data
1190 pr_retip(&icp->icmp_ip);
1191 #else
1192 pr_retip((struct ip *)icp->icmp_data);
1193 #endif
1194 break;
1195 case ICMP_REDIRECT:
1196 switch(icp->icmp_code) {
1197 case ICMP_REDIRECT_NET:
1198 (void)printf("Redirect Network");
1199 break;
1200 case ICMP_REDIRECT_HOST:
1201 (void)printf("Redirect Host");
1202 break;
1203 case ICMP_REDIRECT_TOSNET:
1204 (void)printf("Redirect Type of Service and Network");
1205 break;
1206 case ICMP_REDIRECT_TOSHOST:
1207 (void)printf("Redirect Type of Service and Host");
1208 break;
1209 default:
1210 (void)printf("Redirect, Bad Code: %d", icp->icmp_code);
1211 break;
1213 (void)printf("(New addr: 0x%08lx)\n",
1214 (long unsigned)icp->icmp_gwaddr.s_addr);
1215 #ifndef icmp_data
1216 pr_retip(&icp->icmp_ip);
1217 #else
1218 pr_retip((struct ip *)icp->icmp_data);
1219 #endif
1220 break;
1221 case ICMP_ECHO:
1222 (void)printf("Echo Request\n");
1223 /* XXX ID + Seq + Data */
1224 break;
1225 case ICMP_TIMXCEED:
1226 switch(icp->icmp_code) {
1227 case ICMP_TIMXCEED_INTRANS:
1228 (void)printf("Time to live exceeded\n");
1229 break;
1230 case ICMP_TIMXCEED_REASS:
1231 (void)printf("Frag reassembly time exceeded\n");
1232 break;
1233 default:
1234 (void)printf("Time exceeded, Bad Code: %d\n",
1235 icp->icmp_code);
1236 break;
1238 #ifndef icmp_data
1239 pr_retip(&icp->icmp_ip);
1240 #else
1241 pr_retip((struct ip *)icp->icmp_data);
1242 #endif
1243 break;
1244 case ICMP_PARAMPROB:
1245 (void)printf("Parameter problem: pointer = 0x%02x\n",
1246 icp->icmp_hun.ih_pptr);
1247 #ifndef icmp_data
1248 pr_retip(&icp->icmp_ip);
1249 #else
1250 pr_retip((struct ip *)icp->icmp_data);
1251 #endif
1252 break;
1253 case ICMP_TSTAMP:
1254 (void)printf("Timestamp\n");
1255 /* XXX ID + Seq + 3 timestamps */
1256 break;
1257 case ICMP_TSTAMPREPLY:
1258 (void)printf("Timestamp Reply\n");
1259 /* XXX ID + Seq + 3 timestamps */
1260 break;
1261 case ICMP_IREQ:
1262 (void)printf("Information Request\n");
1263 /* XXX ID + Seq */
1264 break;
1265 case ICMP_IREQREPLY:
1266 (void)printf("Information Reply\n");
1267 /* XXX ID + Seq */
1268 break;
1269 #ifdef ICMP_MASKREQ
1270 case ICMP_MASKREQ:
1271 (void)printf("Address Mask Request\n");
1272 break;
1273 #endif
1274 #ifdef ICMP_MASKREPLY
1275 case ICMP_MASKREPLY:
1276 (void)printf("Address Mask Reply\n");
1277 break;
1278 #endif
1279 default:
1280 (void)printf("Bad ICMP type: %d\n", icp->icmp_type);
1285 * pr_iph --
1286 * Print an IP header with options.
1288 void
1289 pr_iph(ip)
1290 struct ip *ip;
1292 int hlen;
1293 u_char *cp;
1295 hlen = ip->ip_hl << 2;
1296 cp = (u_char *)ip + 20; /* point to options */
1298 (void)printf("Vr HL TOS Len ID Flg off TTL Pro cks Src Dst Data\n");
1299 (void)printf(" %1x %1x %02x %04x %04x",
1300 ip->ip_v, ip->ip_hl, ip->ip_tos, ip->ip_len, ip->ip_id);
1301 (void)printf(" %1x %04x", ((ip->ip_off) & 0xe000) >> 13,
1302 (ip->ip_off) & 0x1fff);
1303 (void)printf(" %02x %02x %04x", ip->ip_ttl, ip->ip_p, ip->ip_sum);
1304 (void)printf(" %s ", inet_ntoa(*(struct in_addr *)&ip->ip_src.s_addr));
1305 (void)printf(" %s ", inet_ntoa(*(struct in_addr *)&ip->ip_dst.s_addr));
1306 /* dump and option bytes */
1307 while (hlen-- > 20) {
1308 (void)printf("%02x", *cp++);
1310 (void)putchar('\n');
1314 * pr_addr --
1315 * Return an ascii host address as a dotted quad and optionally with
1316 * a hostname.
1318 char *
1319 pr_addr(l)
1320 u_long l;
1322 struct hostent *hp;
1323 static char buf[80];
1324 union {
1325 struct in_addr addr;
1326 u_long l;
1327 } __tmp;
1329 __tmp.l = l;
1331 if ((options & F_NUMERIC) ||
1332 !(hp = gethostbyaddr((char *)&l, 4, AF_INET)))
1333 (void)sprintf(buf, "%s", inet_ntoa(__tmp.addr));
1334 else
1335 (void)sprintf(buf, "%s (%s)", hp->h_name,
1336 inet_ntoa(__tmp.addr));
1337 return(buf);
1341 * pr_retip --
1342 * Dump some info on a returned (via ICMP) IP packet.
1344 void
1345 pr_retip(ip)
1346 struct ip *ip;
1348 int hlen;
1349 u_char *cp;
1351 pr_iph(ip);
1352 hlen = ip->ip_hl << 2;
1353 cp = (u_char *)ip + hlen;
1355 if (ip->ip_p == 6)
1356 (void)printf("TCP: from port %u, to port %u (decimal)\n",
1357 (*cp * 256 + *(cp + 1)), (*(cp + 2) * 256 + *(cp + 3)));
1358 else if (ip->ip_p == 17)
1359 (void)printf("UDP: from port %u, to port %u (decimal)\n",
1360 (*cp * 256 + *(cp + 1)), (*(cp + 2) * 256 + *(cp + 3)));
1363 void
1364 fill(bp, patp)
1365 char *bp, *patp;
1367 register int ii, jj, kk;
1368 int pat[16];
1369 char *cp;
1371 for (cp = patp; *cp; cp++)
1372 if (!isxdigit(*cp)) {
1373 (void)fprintf(stderr,
1374 "ping: patterns must be specified as hex digits.\n");
1375 CleanUpExit(1);
1377 ii = sscanf(patp,
1378 "%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x",
1379 &pat[0], &pat[1], &pat[2], &pat[3], &pat[4], &pat[5], &pat[6],
1380 &pat[7], &pat[8], &pat[9], &pat[10], &pat[11], &pat[12],
1381 &pat[13], &pat[14], &pat[15]);
1383 if (ii > 0)
1384 for (kk = 0; kk <= MAXPACKET - (8 + ii); kk += ii)
1385 for (jj = 0; jj < ii; ++jj)
1386 bp[jj + kk] = pat[jj];
1387 if (!(options & F_QUIET)) {
1388 (void)printf("PATTERN: 0x");
1389 for (jj = 0; jj < ii; ++jj)
1390 (void)printf("%02x", bp[jj] & 0xFF);
1391 (void)printf("\n");
1395 void
1396 usage()
1398 (void)fprintf(stderr,
1399 "usage: ping [-Rdfnqrv] [-c count] [-i wait] [-l preload]\n"
1400 "\t[-p pattern] [-s packetsize] [-L [hosts]] host\n");
1401 CleanUpExit(1);
1404 VOID CleanUpExit(LONG error)
1406 CloseLibrary(SocketBase);
1407 exit(error);