BsAtHome writes in Bug 433:
[oweals/busybox.git] / networking / ping.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * $Id: ping.c,v 1.56 2004/03/15 08:28:48 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
65 #define A(bit)          rcvd_tbl[(bit)>>3]      /* identify byte in array */
66 #define B(bit)          (1 << ((bit) & 0x07))   /* identify bit in byte */
67 #define SET(bit)        (A(bit) |= B(bit))
68 #define CLR(bit)        (A(bit) &= (~B(bit)))
69 #define TST(bit)        (A(bit) & B(bit))
70
71 static void ping(const char *host);
72
73 /* common routines */
74 static int in_cksum(unsigned short *buf, int sz)
75 {
76         int nleft = sz;
77         int sum = 0;
78         unsigned short *w = buf;
79         unsigned short ans = 0;
80
81         while (nleft > 1) {
82                 sum += *w++;
83                 nleft -= 2;
84         }
85
86         if (nleft == 1) {
87                 *(unsigned char *) (&ans) = *(unsigned char *) w;
88                 sum += ans;
89         }
90
91         sum = (sum >> 16) + (sum & 0xFFFF);
92         sum += (sum >> 16);
93         ans = ~sum;
94         return (ans);
95 }
96
97 /* simple version */
98 #ifndef CONFIG_FEATURE_FANCY_PING
99 static char *hostname = NULL;
100 static void noresp(int ign)
101 {
102         printf("No response from %s\n", hostname);
103         exit(EXIT_FAILURE);
104 }
105
106 static void ping(const char *host)
107 {
108         struct hostent *h;
109         struct sockaddr_in pingaddr;
110         struct icmp *pkt;
111         int pingsock, c;
112         char packet[DEFDATALEN + MAXIPLEN + MAXICMPLEN];
113
114         pingsock = create_icmp_socket();
115
116         memset(&pingaddr, 0, sizeof(struct sockaddr_in));
117
118         pingaddr.sin_family = AF_INET;
119         h = xgethostbyname(host);
120         memcpy(&pingaddr.sin_addr, h->h_addr, sizeof(pingaddr.sin_addr));
121         hostname = h->h_name;
122
123         pkt = (struct icmp *) packet;
124         memset(pkt, 0, sizeof(packet));
125         pkt->icmp_type = ICMP_ECHO;
126         pkt->icmp_cksum = in_cksum((unsigned short *) pkt, sizeof(packet));
127
128         c = sendto(pingsock, packet, sizeof(packet), 0,
129                            (struct sockaddr *) &pingaddr, sizeof(struct sockaddr_in));
130
131         if (c < 0 || c != sizeof(packet))
132                 bb_perror_msg_and_die("sendto");
133
134         signal(SIGALRM, noresp);
135         alarm(5);                                       /* give the host 5000ms to respond */
136         /* listen for replies */
137         while (1) {
138                 struct sockaddr_in from;
139                 socklen_t fromlen = sizeof(from);
140
141                 if ((c = recvfrom(pingsock, packet, sizeof(packet), 0,
142                                                   (struct sockaddr *) &from, &fromlen)) < 0) {
143                         if (errno == EINTR)
144                                 continue;
145                         bb_perror_msg("recvfrom");
146                         continue;
147                 }
148                 if (c >= 76) {                  /* ip + icmp */
149                         struct iphdr *iphdr = (struct iphdr *) packet;
150
151                         pkt = (struct icmp *) (packet + (iphdr->ihl << 2));     /* skip ip hdr */
152                         if (pkt->icmp_type == ICMP_ECHOREPLY)
153                                 break;
154                 }
155         }
156         printf("%s is alive!\n", hostname);
157         return;
158 }
159
160 extern int ping_main(int argc, char **argv)
161 {
162         argc--;
163         argv++;
164         if (argc < 1)
165                 bb_show_usage();
166         ping(*argv);
167         return EXIT_SUCCESS;
168 }
169
170 #else /* ! CONFIG_FEATURE_FANCY_PING */
171 /* full(er) version */
172 static struct sockaddr_in pingaddr;
173 static int pingsock = -1;
174 static int datalen; /* intentionally uninitialized to work around gcc bug */
175
176 static long ntransmitted, nreceived, nrepeats, pingcount;
177 static int myid, options;
178 static unsigned long tmin = ULONG_MAX, tmax, tsum;
179 static char rcvd_tbl[MAX_DUP_CHK / 8];
180
181 #ifndef CONFIG_FEATURE_FANCY_PING6
182 static
183 #endif
184         struct hostent *hostent;
185
186 static void sendping(int);
187 static void pingstats(int);
188 static void unpack(char *, int, struct sockaddr_in *);
189
190 /**************************************************************************/
191
192 static void pingstats(int junk)
193 {
194         int status;
195
196         signal(SIGINT, SIG_IGN);
197
198         printf("\n--- %s ping statistics ---\n", hostent->h_name);
199         printf("%ld packets transmitted, ", ntransmitted);
200         printf("%ld packets received, ", nreceived);
201         if (nrepeats)
202                 printf("%ld duplicates, ", nrepeats);
203         if (ntransmitted)
204                 printf("%ld%% packet loss\n",
205                            (ntransmitted - nreceived) * 100 / ntransmitted);
206         if (nreceived)
207                 printf("round-trip min/avg/max = %lu.%lu/%lu.%lu/%lu.%lu ms\n",
208                            tmin / 10, tmin % 10,
209                            (tsum / (nreceived + nrepeats)) / 10,
210                            (tsum / (nreceived + nrepeats)) % 10, tmax / 10, tmax % 10);
211         if (nreceived != 0)
212                 status = EXIT_SUCCESS;
213         else
214                 status = EXIT_FAILURE;
215         exit(status);
216 }
217
218 static void sendping(int junk)
219 {
220         struct icmp *pkt;
221         int i;
222         char packet[datalen + 8];
223
224         pkt = (struct icmp *) packet;
225
226         pkt->icmp_type = ICMP_ECHO;
227         pkt->icmp_code = 0;
228         pkt->icmp_cksum = 0;
229         pkt->icmp_seq = htons(ntransmitted++);
230         pkt->icmp_id = myid;
231         CLR(ntohs(pkt->icmp_seq) % MAX_DUP_CHK);
232
233         gettimeofday((struct timeval *) &packet[8], NULL);
234         pkt->icmp_cksum = in_cksum((unsigned short *) pkt, sizeof(packet));
235
236         i = sendto(pingsock, packet, sizeof(packet), 0,
237                            (struct sockaddr *) &pingaddr, sizeof(struct sockaddr_in));
238
239         if (i < 0)
240                 bb_perror_msg_and_die("sendto");
241         else if ((size_t)i != sizeof(packet))
242                 bb_error_msg_and_die("ping wrote %d chars; %d expected", i,
243                            (int)sizeof(packet));
244
245         signal(SIGALRM, sendping);
246         if (pingcount == 0 || ntransmitted < pingcount) {       /* schedule next in 1s */
247                 alarm(PINGINTERVAL);
248         } else {                                        /* done, wait for the last ping to come back */
249                 /* todo, don't necessarily need to wait so long... */
250                 signal(SIGALRM, pingstats);
251                 alarm(MAXWAIT);
252         }
253 }
254
255 static char *icmp_type_name (int id)
256 {
257         switch (id) {
258         case ICMP_ECHOREPLY:            return "Echo Reply";
259         case ICMP_DEST_UNREACH:         return "Destination Unreachable";
260         case ICMP_SOURCE_QUENCH:        return "Source Quench";
261         case ICMP_REDIRECT:             return "Redirect (change route)";
262         case ICMP_ECHO:                         return "Echo Request";
263         case ICMP_TIME_EXCEEDED:        return "Time Exceeded";
264         case ICMP_PARAMETERPROB:        return "Parameter Problem";
265         case ICMP_TIMESTAMP:            return "Timestamp Request";
266         case ICMP_TIMESTAMPREPLY:       return "Timestamp Reply";
267         case ICMP_INFO_REQUEST:         return "Information Request";
268         case ICMP_INFO_REPLY:           return "Information Reply";
269         case ICMP_ADDRESS:                      return "Address Mask Request";
270         case ICMP_ADDRESSREPLY:         return "Address Mask Reply";
271         default:                                        return "unknown ICMP type";
272         }
273 }
274
275 static void unpack(char *buf, int sz, struct sockaddr_in *from)
276 {
277         struct icmp *icmppkt;
278         struct iphdr *iphdr;
279         struct timeval tv, *tp;
280         int hlen, dupflag;
281         unsigned long triptime;
282
283         gettimeofday(&tv, NULL);
284
285         /* check IP header */
286         iphdr = (struct iphdr *) buf;
287         hlen = iphdr->ihl << 2;
288         /* discard if too short */
289         if (sz < (datalen + ICMP_MINLEN))
290                 return;
291
292         sz -= hlen;
293         icmppkt = (struct icmp *) (buf + hlen);
294
295         if (icmppkt->icmp_id != myid)
296             return;                             /* not our ping */
297
298         if (icmppkt->icmp_type == ICMP_ECHOREPLY) {
299                 u_int16_t recv_seq = ntohs(icmppkt->icmp_seq);
300             ++nreceived;
301                 tp = (struct timeval *) icmppkt->icmp_data;
302
303                 if ((tv.tv_usec -= tp->tv_usec) < 0) {
304                         --tv.tv_sec;
305                         tv.tv_usec += 1000000;
306                 }
307                 tv.tv_sec -= tp->tv_sec;
308
309                 triptime = tv.tv_sec * 10000 + (tv.tv_usec / 100);
310                 tsum += triptime;
311                 if (triptime < tmin)
312                         tmin = triptime;
313                 if (triptime > tmax)
314                         tmax = triptime;
315
316                 if (TST(recv_seq % MAX_DUP_CHK)) {
317                         ++nrepeats;
318                         --nreceived;
319                         dupflag = 1;
320                 } else {
321                         SET(recv_seq % MAX_DUP_CHK);
322                         dupflag = 0;
323                 }
324
325                 if (options & O_QUIET)
326                         return;
327
328                 printf("%d bytes from %s: icmp_seq=%u", sz,
329                            inet_ntoa(*(struct in_addr *) &from->sin_addr.s_addr),
330                            recv_seq);
331                 printf(" ttl=%d", iphdr->ttl);
332                 printf(" time=%lu.%lu ms", triptime / 10, triptime % 10);
333                 if (dupflag)
334                         printf(" (DUP!)");
335                 printf("\n");
336         } else
337                 if (icmppkt->icmp_type != ICMP_ECHO)
338                         bb_error_msg("Warning: Got ICMP %d (%s)",
339                                         icmppkt->icmp_type, icmp_type_name (icmppkt->icmp_type));
340 }
341
342 static void ping(const char *host)
343 {
344         char packet[datalen + MAXIPLEN + MAXICMPLEN];
345         int sockopt;
346
347         pingsock = create_icmp_socket();
348
349         memset(&pingaddr, 0, sizeof(struct sockaddr_in));
350
351         pingaddr.sin_family = AF_INET;
352         hostent = xgethostbyname(host);
353         if (hostent->h_addrtype != AF_INET)
354                 bb_error_msg_and_die("unknown address type; only AF_INET is currently supported.");
355
356         memcpy(&pingaddr.sin_addr, hostent->h_addr, sizeof(pingaddr.sin_addr));
357
358         /* enable broadcast pings */
359         sockopt = 1;
360         setsockopt(pingsock, SOL_SOCKET, SO_BROADCAST, (char *) &sockopt,
361                            sizeof(sockopt));
362
363         /* set recv buf for broadcast pings */
364         sockopt = 48 * 1024;
365         setsockopt(pingsock, SOL_SOCKET, SO_RCVBUF, (char *) &sockopt,
366                            sizeof(sockopt));
367
368         printf("PING %s (%s): %d data bytes\n",
369                    hostent->h_name,
370                    inet_ntoa(*(struct in_addr *) &pingaddr.sin_addr.s_addr),
371                    datalen);
372
373         signal(SIGINT, pingstats);
374
375         /* start the ping's going ... */
376         sendping(0);
377
378         /* listen for replies */
379         while (1) {
380                 struct sockaddr_in from;
381                 socklen_t fromlen = (socklen_t) sizeof(from);
382                 int c;
383
384                 if ((c = recvfrom(pingsock, packet, sizeof(packet), 0,
385                                                   (struct sockaddr *) &from, &fromlen)) < 0) {
386                         if (errno == EINTR)
387                                 continue;
388                         bb_perror_msg("recvfrom");
389                         continue;
390                 }
391                 unpack(packet, c, &from);
392                 if (pingcount > 0 && nreceived >= pingcount)
393                         break;
394         }
395         pingstats(0);
396 }
397
398 extern int ping_main(int argc, char **argv)
399 {
400         char *thisarg;
401
402         datalen = DEFDATALEN; /* initialized here rather than in global scope to work around gcc bug */
403
404         argc--;
405         argv++;
406         options = 0;
407         /* Parse any options */
408         while (argc >= 1 && **argv == '-') {
409                 thisarg = *argv;
410                 thisarg++;
411                 switch (*thisarg) {
412                 case 'q':
413                         options |= O_QUIET;
414                         break;
415                 case 'c':
416                         if (--argc <= 0)
417                                 bb_show_usage();
418                         argv++;
419                         pingcount = atoi(*argv);
420                         break;
421                 case 's':
422                         if (--argc <= 0)
423                                 bb_show_usage();
424                         argv++;
425                         datalen = atoi(*argv);
426                         break;
427                 default:
428                         bb_show_usage();
429                 }
430                 argc--;
431                 argv++;
432         }
433         if (argc < 1)
434                 bb_show_usage();
435
436         myid = getpid() & 0xFFFF;
437         ping(*argv);
438         return EXIT_SUCCESS;
439 }
440 #endif /* ! CONFIG_FEATURE_FANCY_PING */
441
442 /*
443  * Copyright (c) 1989 The Regents of the University of California.
444  * All rights reserved.
445  *
446  * This code is derived from software contributed to Berkeley by
447  * Mike Muuss.
448  *
449  * Redistribution and use in source and binary forms, with or without
450  * modification, are permitted provided that the following conditions
451  * are met:
452  * 1. Redistributions of source code must retain the above copyright
453  *    notice, this list of conditions and the following disclaimer.
454  * 2. Redistributions in binary form must reproduce the above copyright
455  *    notice, this list of conditions and the following disclaimer in the
456  *    documentation and/or other materials provided with the distribution.
457  *
458  * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
459  *              ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
460  *
461  * 4. Neither the name of the University nor the names of its contributors
462  *    may be used to endorse or promote products derived from this software
463  *    without specific prior written permission.
464  *
465  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
466  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
467  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
468  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
469  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
470  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
471  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
472  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
473  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
474  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
475  * SUCH DAMAGE.
476  */