Stuf
[oweals/busybox.git] / ping.c
1 /*
2  * $Id: ping.c,v 1.1 1999/12/07 23:14:59 andersen Exp $
3  * Mini ping implementation for busybox
4  *
5  * Copyright (C) 1999 by Randolph Chung <tausq@debian.org>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20  *
21  * This version of ping is adapted from the ping in netkit-base 0.10,
22  * which is:
23  *
24  * Copyright (c) 1989 The Regents of the University of California.
25  * All rights reserved.
26  *
27  * This code is derived from software contributed to Berkeley by
28  * Mike Muuss.
29  * 
30  * Original copyright notice is retained at the end of this file.
31  */
32
33 #include "internal.h"
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
50 #define DEFDATALEN      56
51 #define MAXIPLEN        60
52 #define MAXICMPLEN      76
53 #define MAXPACKET       65468
54 #define MAX_DUP_CHK     (8 * 128)
55 #define MAXWAIT         10
56
57 #define O_QUIET         (1 << 0)
58
59 #define A(bit)          rcvd_tbl[(bit)>>3]      /* identify byte in array */
60 #define B(bit)          (1 << ((bit) & 0x07))   /* identify bit in byte */
61 #define SET(bit)        (A(bit) |= B(bit))
62 #define CLR(bit)        (A(bit) &= (~B(bit)))
63 #define TST(bit)        (A(bit) & B(bit))
64
65 static const char* ping_usage = "ping [OPTION]... host\n\n"
66 "Options:\n"
67 "\t-q\t\tquiet\n"
68 "\t-c\t\tping count\n";
69
70 static char *hostname = NULL;
71 static struct sockaddr_in pingaddr;
72 static int pingsock = -1;
73 static long ntransmitted = 0, nreceived = 0, nrepeats = 0, pingcount = 0;
74 static int myid = 0, options = 0;
75 static unsigned long tmin = ULONG_MAX, tmax = 0, tsum = 0;
76 static char rcvd_tbl[MAX_DUP_CHK / 8];
77
78 static void pingstats(int);
79 static void sendping(int);
80 static void unpack(char *, int, struct sockaddr_in *);
81 static void ping(char *);
82 static int in_cksum(unsigned short *, int);
83
84 /**************************************************************************/
85
86 static int in_cksum(unsigned short *buf, int sz)
87 {
88     int nleft = sz;
89     int sum = 0;
90     unsigned short *w = buf;
91     unsigned short ans = 0;
92
93     while (nleft > 1) {
94         sum += *w++;
95         nleft -= 2;
96     }
97
98     if (nleft == 1) { 
99         *(unsigned char *)(&ans) = *(unsigned char *)w;
100         sum += ans;
101     }
102
103     sum = (sum >> 16) + (sum & 0xFFFF);
104     sum += (sum >> 16);
105     ans = ~sum;
106     return(ans);
107 }   
108
109 static void pingstats(int ign) {
110     signal(SIGINT, SIG_IGN);
111     
112     printf("\n--- %s ping statistics ---\n", hostname);
113     printf("%ld packets transmitted, ", ntransmitted);
114     printf("%ld packets received, ", nreceived);
115     if (nrepeats)
116         printf("%ld duplicates, ", nrepeats);
117     if (ntransmitted)
118         printf("%ld%% packet loss\n", 
119                (ntransmitted - nreceived)*100/ntransmitted);
120     if (nreceived)
121         printf("round-trip min/avg/max = %lu.%lu/%lu.%lu/%lu.%lu ms\n",
122                tmin/10, tmin%10, 
123                (tsum/(nreceived+nrepeats))/10,
124                (tsum/(nreceived+nrepeats))%10,
125                tmax/10, tmax%10);
126     exit(0);
127 }
128
129 static void sendping(int ign)
130 {
131     struct icmp *pkt;
132     int i;
133     char packet[DEFDATALEN + 8];
134
135     pkt = (struct icmp *)packet;
136     
137     pkt->icmp_type = ICMP_ECHO;
138     pkt->icmp_code = 0;
139     pkt->icmp_cksum = 0;
140     pkt->icmp_seq = ntransmitted++;
141     pkt->icmp_id = myid; 
142     CLR(pkt->icmp_seq % MAX_DUP_CHK);
143
144     gettimeofday((struct timeval *)&packet[8], NULL);
145     pkt->icmp_cksum = in_cksum((unsigned short *)pkt, sizeof(packet));
146     
147     i = sendto(pingsock, packet, sizeof(packet), 0, 
148                (struct sockaddr *)&pingaddr, sizeof(struct sockaddr_in));
149
150     if (i < 0 || i != sizeof(packet)) {
151         if (i < 0) perror("ping");
152         fprintf(stderr, "ping wrote %d chars; %d expected\n", i, sizeof(packet));
153         exit(1);
154     }
155
156     signal(SIGALRM, sendping);
157     if (pingcount == 0 || ntransmitted < pingcount) { /* schedule next */
158         alarm(1);
159     } else { /* done, wait for the last ping to come back */
160         /* todo, don't necessarily need to wait so long... */
161         signal(SIGALRM, pingstats);
162         alarm(MAXWAIT);
163     }
164 }
165     
166 static void unpack(char *buf, int sz, struct sockaddr_in *from)
167 {
168     struct icmp *icmppkt;
169     struct iphdr *iphdr;
170     struct timeval tv, *tp;
171     int hlen, dupflag;
172     unsigned long triptime;
173     
174     gettimeofday(&tv, NULL);
175     
176     /* check IP header */
177     iphdr = (struct iphdr *)buf;
178     hlen = iphdr->ihl << 2;
179     /* discard if too short */
180     if (sz < (DEFDATALEN + ICMP_MINLEN)) return;
181
182     sz -= hlen;
183     icmppkt = (struct icmp *)(buf + hlen);
184     
185     if (icmppkt->icmp_type == ICMP_ECHOREPLY) {
186         if (icmppkt->icmp_id != myid) return; /* not our ping */
187         ++nreceived;
188         tp = (struct timeval *)icmppkt->icmp_data;
189
190         if ((tv.tv_usec -= tp->tv_usec) < 0) {
191             --tv.tv_sec;
192             tv.tv_usec += 1000000;
193         }
194         tv.tv_sec -= tp->tv_sec;
195         
196         triptime = tv.tv_sec * 10000 + (tv.tv_usec / 100);
197         tsum += triptime;
198         if (triptime < tmin) tmin = triptime;
199         if (triptime > tmax) tmax = triptime;
200
201         if (TST(icmppkt->icmp_seq % MAX_DUP_CHK)) {
202             ++nrepeats;
203             --nreceived;
204             dupflag = 1;
205         } else {
206             SET(icmppkt->icmp_seq % MAX_DUP_CHK);
207             dupflag = 0;
208         }
209
210         if (options & O_QUIET) return;
211
212         printf("%d bytes from %s: icmp_seq=%u", sz, 
213                inet_ntoa(*(struct in_addr *)&from->sin_addr.s_addr),
214                icmppkt->icmp_seq);
215         printf(" ttl=%d", iphdr->ttl);
216         printf(" time=%lu.%lu ms", triptime/10, triptime%10);
217         if (dupflag) printf(" (DUP!)");
218         printf("\n");
219     } else {
220         fprintf(stderr, "Warning: unknown ICMP packet received (not echo-reply)\n");
221     }   
222 }
223
224 static void ping(char *host)
225 {
226     struct protoent *proto;
227     struct hostent *h;
228     char buf[MAXHOSTNAMELEN];
229     char packet[DEFDATALEN + MAXIPLEN + MAXICMPLEN];
230     int sockopt;
231     
232     if (!(proto = getprotobyname("icmp"))) {
233         fprintf(stderr, "ping: unknown protocol icmp\n");
234         exit(1);
235     }
236     if ((pingsock = socket(AF_INET, SOCK_RAW, proto->p_proto)) < 0) {
237         if (errno == EPERM) {
238             fprintf(stderr, "ping: permission denied. (are you root?)\n");
239         } else {
240             perror("ping");
241         }
242         exit(1);
243     }
244
245 #ifdef SUID_BUSYBOX
246     setuid(getuid());
247 #endif
248     
249     memset(&pingaddr, 0, sizeof(struct sockaddr_in));
250     pingaddr.sin_family = AF_INET;
251     if (inet_aton(host, &pingaddr.sin_addr)) {
252         hostname = host;
253     } else {    
254         if (!(h = gethostbyname(host))) {
255             fprintf(stderr, "ping: unknown host %s\n", host);
256             exit(1);
257         }
258
259         if (h->h_addrtype != AF_INET) {
260             fprintf(stderr, "ping: unknown address type; only AF_INET is currently supported.\n");
261             exit(1);
262         }
263         
264         pingaddr.sin_family = AF_INET; /* h->h_addrtype */
265         memcpy(&pingaddr.sin_addr, h->h_addr, sizeof(pingaddr.sin_addr)); 
266         strncpy(buf, h->h_name, sizeof(buf)-1);
267         hostname = buf;
268     }
269
270     /* enable broadcast pings */
271     sockopt = 1;
272     setsockopt(pingsock, SOL_SOCKET, SO_BROADCAST, (char *)&sockopt, sizeof(sockopt));
273     
274     /* set recv buf for broadcast pings */
275     sockopt = 48 * 1024;
276     setsockopt(pingsock, SOL_SOCKET, SO_RCVBUF, (char *)&sockopt, sizeof(sockopt));
277
278     printf("PING %s (%s): %d data bytes\n", 
279            hostname, inet_ntoa(*(struct in_addr *)&pingaddr.sin_addr.s_addr),
280            DEFDATALEN);
281
282     signal(SIGINT, pingstats);
283     
284     /* start the ping's going ... */
285     sendping(0);
286         
287     /* listen for replies */
288     while (1) {
289         struct sockaddr_in from;
290         size_t fromlen = sizeof(from);
291         int c;
292
293         if ((c = recvfrom(pingsock, packet, sizeof(packet), 0,
294                           (struct sockaddr *)&from, &fromlen)) < 0) {
295             if (errno == EINTR) continue;
296             perror("ping");
297             continue;
298         }
299         unpack(packet, c, &from);
300         if (pingcount > 0 && nreceived >= pingcount) break;     
301     }
302     pingstats(0);
303 }
304
305 extern int ping_main(int argc, char **argv)
306 {
307     argc--;
308     argv++;
309     options = 0;
310     /* Parse any options */
311     if (argc < 1) usage(ping_usage);
312
313     while (**argv == '-') {
314         while (*++(*argv))
315             switch (**argv) {
316             case 'c':
317                 argc--; argv++;
318                 if (argc < 1) usage(ping_usage);
319                 pingcount = atoi(*argv);
320                 break;
321             case 'q':
322                 options |= O_QUIET;
323                 break;
324             default:
325                 usage(ping_usage);
326             }
327         argc--;
328         argv++;
329     }
330
331     if (argc < 1) usage(ping_usage);
332
333     myid = getpid() & 0xFFFF;
334     ping(*(argv++));
335     exit( TRUE);
336 }
337
338 /*
339  * Copyright (c) 1989 The Regents of the University of California.
340  * All rights reserved.
341  *
342  * This code is derived from software contributed to Berkeley by
343  * Mike Muuss.
344  *
345  * Redistribution and use in source and binary forms, with or without
346  * modification, are permitted provided that the following conditions
347  * are met:
348  * 1. Redistributions of source code must retain the above copyright
349  *    notice, this list of conditions and the following disclaimer.
350  * 2. Redistributions in binary form must reproduce the above copyright
351  *    notice, this list of conditions and the following disclaimer in the
352  *    documentation and/or other materials provided with the distribution.
353  * 3. All advertising materials mentioning features or use of this software
354  *    must display the following acknowledgement:
355  *      This product includes software developed by the University of
356  *      California, Berkeley and its contributors.
357  * 4. Neither the name of the University nor the names of its contributors
358  *    may be used to endorse or promote products derived from this software
359  *    without specific prior written permission.
360  *
361  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
362  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
363  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
364  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
365  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
366  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
367  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
368  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
369  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
370  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
371  * SUCH DAMAGE.
372  */
373
374