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