www.usr.com/support/gpl/USR9113_release1.0.tar.gz
[bcm963xx.git] / userapps / opensource / busybox / networking / ping.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * $Id: ping.c,v 1.55 2003/07/22 08:56:51 andersen Exp $
4  * Mini ping implementation for busybox
5  *
6  * Copyright (C) 1999 by Randolph Chung <tausq@debian.org>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21  *
22  * This version of ping is adapted from the ping in netkit-base 0.10,
23  * which is:
24  *
25  * Copyright (c) 1989 The Regents of the University of California.
26  * All rights reserved.
27  *
28  * This code is derived from software contributed to Berkeley by
29  * Mike Muuss.
30  *
31  * Original copyright notice is retained at the end of this file.
32  */
33
34 #include <sys/param.h>
35 #include <sys/socket.h>
36 #include <sys/file.h>
37 #include <sys/time.h>
38 #include <sys/times.h>
39 #include <sys/signal.h>
40
41 #include <netinet/in.h>
42 #include <netinet/ip.h>
43 #include <netinet/ip_icmp.h>
44 #include <arpa/inet.h>
45 #include <netdb.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <errno.h>
49 #include <unistd.h>
50 #include <string.h>
51 #include <stdlib.h>
52 #include "busybox.h"
53
54
55 static const int DEFDATALEN = 56;
56 static const int MAXIPLEN = 60;
57 static const int MAXICMPLEN = 76;
58 static const int MAXPACKET = 65468;
59 #define MAX_DUP_CHK     (8 * 128)
60 static const int MAXWAIT = 10;
61 static const int PINGINTERVAL = 1;              /* second */
62
63 #define O_QUIET         (1 << 0)
64 #define O_LOG           2
65 #define PING_PID_FILE   "/var/pingPid"
66 #define A(bit)          rcvd_tbl[(bit)>>3]      /* identify byte in array */
67 #define B(bit)          (1 << ((bit) & 0x07))   /* identify bit in byte */
68 #define SET(bit)        (A(bit) |= B(bit))
69 #define CLR(bit)        (A(bit) &= (~B(bit)))
70 #define TST(bit)        (A(bit) & B(bit))
71
72 //BRCM begin
73 /* store ping statistics to a file */
74 FILE *pingFile=NULL;
75 FILE *pingPid=NULL;
76 //BRCM end
77
78 static void ping(const char *host);
79
80 /* common routines */
81 static int in_cksum(unsigned short *buf, int sz)
82 {
83         int nleft = sz;
84         int sum = 0;
85         unsigned short *w = buf;
86         unsigned short ans = 0;
87
88         while (nleft > 1) {
89                 sum += *w++;
90                 nleft -= 2;
91         }
92
93         if (nleft == 1) {
94                 *(unsigned char *) (&ans) = *(unsigned char *) w;
95                 sum += ans;
96         }
97
98         sum = (sum >> 16) + (sum & 0xFFFF);
99         sum += (sum >> 16);
100         ans = ~sum;
101         return (ans);
102 }
103
104 /* simple version */
105 #ifndef CONFIG_FEATURE_FANCY_PING
106 static char *hostname = NULL;
107 static void noresp(int ign)
108 {
109         printf("No response from %s\n", hostname);
110         exit(EXIT_FAILURE);
111 }
112
113 static void ping(const char *host)
114 {
115         struct hostent *h;
116         struct sockaddr_in pingaddr;
117         struct icmp *pkt;
118         int pingsock, c;
119         char packet[DEFDATALEN + MAXIPLEN + MAXICMPLEN];
120
121         pingsock = create_icmp_socket();
122
123         memset(&pingaddr, 0, sizeof(struct sockaddr_in));
124
125         pingaddr.sin_family = AF_INET;
126         h = xgethostbyname(host);
127         memcpy(&pingaddr.sin_addr, h->h_addr, sizeof(pingaddr.sin_addr));
128         hostname = h->h_name;
129
130         pkt = (struct icmp *) packet;
131         memset(pkt, 0, sizeof(packet));
132         pkt->icmp_type = ICMP_ECHO;
133         pkt->icmp_cksum = in_cksum((unsigned short *) pkt, sizeof(packet));
134
135         c = sendto(pingsock, packet, sizeof(packet), 0,
136                            (struct sockaddr *) &pingaddr, sizeof(struct sockaddr_in));
137
138         if (c < 0 || c != sizeof(packet))
139                 bb_perror_msg_and_die("sendto");
140
141         signal(SIGALRM, noresp);
142         alarm(5);                                       /* give the host 5000ms to respond */
143         /* listen for replies */
144         while (1) {
145                 struct sockaddr_in from;
146                 size_t fromlen = sizeof(from);
147
148                 if ((c = recvfrom(pingsock, packet, sizeof(packet), 0,
149                                                   (struct sockaddr *) &from, &fromlen)) < 0) {
150                         if (errno == EINTR)
151                                 continue;
152                         bb_perror_msg("recvfrom");
153                         continue;
154                 }
155                 if (c >= 76) {                  /* ip + icmp */
156                         struct iphdr *iphdr = (struct iphdr *) packet;
157
158                         pkt = (struct icmp *) (packet + (iphdr->ihl << 2));     /* skip ip hdr */
159                         if (pkt->icmp_type == ICMP_ECHOREPLY)
160                                 break;
161                 }
162         }
163         printf("%s is alive!\n", hostname);
164         return;
165 }
166
167 extern int ping_main(int argc, char **argv)
168 {
169         argc--;
170         argv++;
171         if (argc < 1)
172                 bb_show_usage();
173         ping(*argv);
174         return EXIT_SUCCESS;
175 }
176
177 #else /* ! CONFIG_FEATURE_FANCY_PING */
178 /* full(er) version */
179 static struct sockaddr_in pingaddr;
180 static int pingsock = -1;
181 static int datalen; /* intentionally uninitialized to work around gcc bug */
182
183 // brcm: changed default value of pingcount from 0 to 4.
184 static long ntransmitted, nreceived, nrepeats, pingcount=4;
185 static int myid, options;
186 static unsigned long tmin = ULONG_MAX, tmax, tsum;
187 static char rcvd_tbl[MAX_DUP_CHK / 8];
188
189 struct hostent *hostent;
190
191 static void sendping(int);
192 static void pingstats(int);
193 static void unpack(char *, int, struct sockaddr_in *);
194
195 static void logStat(int finish)
196 {
197   char ip[16];
198   struct in_addr addr;
199
200   pingFile = fopen ("/var/pingStats", "w");
201   if (pingFile == NULL)
202     return;
203
204   rewind(pingFile);
205   if ((finish) && (ntransmitted == 0))
206     fprintf(pingFile,"%d\n",-1);
207   else
208     fprintf(pingFile,"%d\n",finish);
209
210   memcpy(&addr, hostent->h_addr, sizeof(ip));
211   strncpy(ip,inet_ntoa(addr),16);
212   /* IP, sent, receive, lost, min, max, average */
213   if( nreceived )
214   fprintf(pingFile,"IP = %s Sent = %ld Receive = %ld Lost = %ld Min = %lu.%lu ms Max = %lu.%lu ms  Average = %lu.%lu ms \n",
215           ip, ntransmitted, nreceived, (ntransmitted-nreceived),
216           (tmin / 10), (tmin % 10),
217           (tmax / 10), (tmax % 10),
218           ((tsum / (nreceived + nrepeats)) / 10),
219           ((tsum / (nreceived + nrepeats)) % 10));
220   else
221   fprintf(pingFile,"IP = %s Sent = %ld Receive = %ld Lost = %ld Min = %lu.%lu ms Max = %lu.%lu ms  Average = %lu.%lu ms \n",
222           ip, ntransmitted, nreceived, (ntransmitted-nreceived),
223           (tmin / 10), (tmin % 10),
224           (tmin / 10), (tmin % 10),
225           (tmin / 10), (tmin % 10));
226   fclose(pingFile);
227 }
228
229 /**************************************************************************/
230
231 static void pingstats(int junk)
232 {
233         int status;
234
235         signal(SIGINT, SIG_IGN);
236         printf("\n--- %s ping statistics ---\n", hostent->h_name);
237         printf("%ld packets transmitted, ", ntransmitted);
238         printf("%ld packets received, ", nreceived);
239         if (nrepeats)
240                 printf("%ld duplicates, ", nrepeats);
241         if (ntransmitted)
242                 printf("%ld%% packet loss\n",
243                            (ntransmitted - nreceived) * 100 / ntransmitted);
244         if (nreceived)
245                 printf("round-trip min/avg/max = %lu.%lu/%lu.%lu/%lu.%lu ms\n",
246                            tmin / 10, tmin % 10,
247                            (tsum / (nreceived + nrepeats)) / 10,
248                            (tsum / (nreceived + nrepeats)) % 10, tmax / 10, tmax % 10);
249         if (nreceived != 0)
250                 status = EXIT_SUCCESS;
251         else
252                 status = EXIT_FAILURE;
253
254         //BRCM begin
255         if (options & O_LOG) {
256           logStat(1);
257           remove_file(PING_PID_FILE,FILEUTILS_FORCE);
258         }
259         //BRCM end
260
261         exit(status);
262 }
263
264 static void sendping(int junk)
265 {
266         struct icmp *pkt;
267         int i;
268         char packet[datalen + 8];
269
270         pkt = (struct icmp *) packet;
271
272         pkt->icmp_type = ICMP_ECHO;
273         pkt->icmp_code = 0;
274         pkt->icmp_cksum = 0;
275         pkt->icmp_seq = ntransmitted++;
276         pkt->icmp_id = myid;
277         CLR(pkt->icmp_seq % MAX_DUP_CHK);
278
279         gettimeofday((struct timeval *) &packet[8], NULL);
280         pkt->icmp_cksum = in_cksum((unsigned short *) pkt, sizeof(packet));
281
282         i = sendto(pingsock, packet, sizeof(packet), 0,
283                            (struct sockaddr *) &pingaddr, sizeof(struct sockaddr_in));
284
285         if (i < 0)
286                 bb_perror_msg_and_die("sendto");
287         else if ((size_t)i != sizeof(packet))
288                 bb_error_msg_and_die("ping wrote %d chars; %d expected", i,
289                            (int)sizeof(packet));
290
291         signal(SIGALRM, sendping);
292         if (pingcount == 0 || ntransmitted < pingcount) {       /* schedule next in 1s */
293                 alarm(PINGINTERVAL);
294         } else {                                        /* done, wait for the last ping to come back */
295                 /* todo, don't necessarily need to wait so long... */
296                 signal(SIGALRM, pingstats);
297                 alarm(MAXWAIT);
298         }
299 }
300
301 static char *icmp_type_name (int id)
302 {
303         switch (id) {
304         case ICMP_ECHOREPLY:            return "Echo Reply";
305         case ICMP_DEST_UNREACH:         return "Destination Unreachable";
306         case ICMP_SOURCE_QUENCH:        return "Source Quench";
307         case ICMP_REDIRECT:             return "Redirect (change route)";
308         case ICMP_ECHO:                         return "Echo Request";
309         case ICMP_TIME_EXCEEDED:        return "Time Exceeded";
310         case ICMP_PARAMETERPROB:        return "Parameter Problem";
311         case ICMP_TIMESTAMP:            return "Timestamp Request";
312         case ICMP_TIMESTAMPREPLY:       return "Timestamp Reply";
313         case ICMP_INFO_REQUEST:         return "Information Request";
314         case ICMP_INFO_REPLY:           return "Information Reply";
315         case ICMP_ADDRESS:                      return "Address Mask Request";
316         case ICMP_ADDRESSREPLY:         return "Address Mask Reply";
317         default:                                        return "unknown ICMP type";
318         }
319 }
320
321 static void unpack(char *buf, int sz, struct sockaddr_in *from)
322 {
323         struct icmp *icmppkt;
324         struct iphdr *iphdr;
325         struct timeval tv, *tp;
326         int hlen, dupflag;
327         unsigned long triptime;
328
329         gettimeofday(&tv, NULL);
330
331         /* check IP header */
332         iphdr = (struct iphdr *) buf;
333         hlen = iphdr->ihl << 2;
334         /* discard if too short */
335         if (sz < (datalen + ICMP_MINLEN))
336                 return;
337
338         sz -= hlen;
339         icmppkt = (struct icmp *) (buf + hlen);
340
341         if (icmppkt->icmp_id != myid)
342             return;                             /* not our ping */
343
344         if (icmppkt->icmp_type == ICMP_ECHOREPLY) {
345             ++nreceived;
346                 tp = (struct timeval *) icmppkt->icmp_data;
347
348                 if ((tv.tv_usec -= tp->tv_usec) < 0) {
349                         --tv.tv_sec;
350                         tv.tv_usec += 1000000;
351                 }
352                 tv.tv_sec -= tp->tv_sec;
353
354                 triptime = tv.tv_sec * 10000 + (tv.tv_usec / 100);
355                 tsum += triptime;
356                 if (triptime < tmin)
357                         tmin = triptime;
358                 if (triptime > tmax)
359                         tmax = triptime;
360
361                 if (TST(icmppkt->icmp_seq % MAX_DUP_CHK)) {
362                         ++nrepeats;
363                         --nreceived;
364                         dupflag = 1;
365                 } else {
366                         SET(icmppkt->icmp_seq % MAX_DUP_CHK);
367                         dupflag = 0;
368                 }
369                 //BRCM begin
370                 if (options & O_LOG) {
371                   logStat(0);
372                   usleep(1);
373                 } /* O_LOG */
374                 //BRCM end
375
376                 if (options & O_QUIET)
377                         return;
378
379                 /*brcm: changed display message to show actually receive data bytes length
380                   rather than ICMP packet length which is ICMP_MINLEN+dataLen */
381                 printf("%d bytes from %s: icmp_seq=%u", (sz-ICMP_MINLEN),
382                            inet_ntoa(*(struct in_addr *) &from->sin_addr.s_addr),
383                            icmppkt->icmp_seq);
384                 printf(" ttl=%d", iphdr->ttl);
385                 printf(" time=%lu.%lu ms", triptime / 10, triptime % 10);
386                 if (dupflag)
387                         printf(" (DUP!)");
388                 printf("\n");
389         } else
390                 if (icmppkt->icmp_type != ICMP_ECHO)
391                         bb_error_msg("Warning: Got ICMP %d (%s)",
392                                         icmppkt->icmp_type, icmp_type_name (icmppkt->icmp_type));
393 }
394
395 static void ping(const char *host)
396 {
397         char packet[datalen + MAXIPLEN + MAXICMPLEN];
398         int sockopt;
399
400         pingsock = create_icmp_socket();
401
402         memset(&pingaddr, 0, sizeof(struct sockaddr_in));
403
404         pingaddr.sin_family = AF_INET;
405         hostent = xgethostbyname(host);
406
407         if (hostent->h_addrtype != AF_INET)
408                 bb_error_msg_and_die("unknown address type; only AF_INET is currently supported.");
409
410         memcpy(&pingaddr.sin_addr, hostent->h_addr, sizeof(pingaddr.sin_addr));
411
412         /* enable broadcast pings */
413         sockopt = 1;
414         setsockopt(pingsock, SOL_SOCKET, SO_BROADCAST, (char *) &sockopt,
415                            sizeof(sockopt));
416
417         /* set recv buf for broadcast pings */
418         sockopt = 48 * 1024;
419         setsockopt(pingsock, SOL_SOCKET, SO_RCVBUF, (char *) &sockopt,
420                            sizeof(sockopt));
421
422         printf("PING %s (%s): %d data bytes\n",
423                    hostent->h_name,
424                    inet_ntoa(*(struct in_addr *) &pingaddr.sin_addr.s_addr),
425                    datalen);
426
427         signal(SIGINT, pingstats);
428
429         /* start the ping's going ... */
430         sendping(0);
431
432         /* listen for replies */
433         while (1) {
434                 struct sockaddr_in from;
435                 socklen_t fromlen = (socklen_t) sizeof(from);
436                 int c;
437
438                 if ((c = recvfrom(pingsock, packet, sizeof(packet), 0,
439                                                   (struct sockaddr *) &from, &fromlen)) < 0) {
440                         if (errno == EINTR)
441                                 continue;
442                         bb_perror_msg("recvfrom");
443                         continue;
444                 }
445                 unpack(packet, c, &from);
446                 if (pingcount > 0 && nreceived >= pingcount)
447                         break;
448         }
449         pingstats(0);
450 }
451
452 extern int ping_main(int argc, char **argv)
453 {
454   //        FILE *fd;
455         char *thisarg;
456
457         datalen = DEFDATALEN; /* initialized here rather than in global scope to work around gcc bug */
458
459         argc--;
460         argv++;
461         options = 0;
462         /* Parse any options */
463         while (argc >= 1 && **argv == '-') {
464                 thisarg = *argv;
465                 thisarg++;
466                 switch (*thisarg) {
467                 case 'q':
468                         options |= O_QUIET;
469                         break;
470                 case 'c':
471                         if (--argc <= 0)
472                                 bb_show_usage();
473                         argv++;
474                         pingcount = atoi(*argv);
475                         break;
476                 case 's':
477                         if (--argc <= 0)
478                                 bb_show_usage();
479                         argv++;
480                         datalen = atoi(*argv);
481                         break;
482                 //BRCM begin
483                 case 'l':
484                         if ((pingPid = fopen (PING_PID_FILE, "w")) != NULL) {
485                           fprintf(pingPid,"%d\n",getpid());
486                           (void)fclose(pingPid);
487                         }
488                         /* log statistics to file */
489                         options |= O_LOG;
490                   break;
491                   //BRCM end
492                 default:
493                         bb_show_usage();
494                 }
495                 argc--;
496                 argv++;
497         }
498         if (argc < 1)
499                 bb_show_usage();
500
501         myid = getpid() & 0xFFFF;
502         ping(*argv);
503
504         if (options & O_LOG) {
505           remove_file(PING_PID_FILE,FILEUTILS_FORCE);
506         }
507         return EXIT_SUCCESS;
508 }
509 #endif /* ! CONFIG_FEATURE_FANCY_PING */
510
511 /*
512  * Copyright (c) 1989 The Regents of the University of California.
513  * All rights reserved.
514  *
515  * This code is derived from software contributed to Berkeley by
516  * Mike Muuss.
517  *
518  * Redistribution and use in source and binary forms, with or without
519  * modification, are permitted provided that the following conditions
520  * are met:
521  * 1. Redistributions of source code must retain the above copyright
522  *    notice, this list of conditions and the following disclaimer.
523  * 2. Redistributions in binary form must reproduce the above copyright
524  *    notice, this list of conditions and the following disclaimer in the
525  *    documentation and/or other materials provided with the distribution.
526  *
527  * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
528  *              ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
529  *
530  * 4. Neither the name of the University nor the names of its contributors
531  *    may be used to endorse or promote products derived from this software
532  *    without specific prior written permission.
533  *
534  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
535  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
536  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
537  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
538  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
539  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
540  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
541  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
542  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
543  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
544  * SUCH DAMAGE.
545  */