1 /* vi: set sw=4 ts=4: */
3 * $Id: ping.c,v 1.20 2000/07/12 17:02:35 kraai Exp $
4 * Mini ping implementation for busybox
6 * Copyright (C) 1999 by Randolph Chung <tausq@debian.org>
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.
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.
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
22 * This version of ping is adapted from the ping in netkit-base 0.10,
25 * Copyright (c) 1989 The Regents of the University of California.
26 * All rights reserved.
28 * This code is derived from software contributed to Berkeley by
31 * Original copyright notice is retained at the end of this file.
35 #include <sys/param.h>
36 #include <sys/socket.h>
39 #include <sys/times.h>
40 #include <sys/signal.h>
42 #include <netinet/in.h>
43 #include <netinet/ip.h>
44 #include <netinet/ip_icmp.h>
45 #include <arpa/inet.h>
52 /* It turns out that libc5 doesn't have proper icmp support
53 * built into it header files, so we have to supplement it */
54 #if ! defined __GLIBC__ && ! defined __UCLIBC__
55 typedef unsigned int socklen_t;
57 #define ICMP_MINLEN 8 /* abs minimum */
62 u_int32_t ira_preference;
68 u_int8_t icmp_type; /* type of message, see below */
69 u_int8_t icmp_code; /* type sub code */
70 u_int16_t icmp_cksum; /* ones complement checksum of struct */
73 u_char ih_pptr; /* ICMP_PARAMPROB */
74 struct in_addr ih_gwaddr; /* gateway address */
75 struct ih_idseq /* echo datagram */
82 /* ICMP_UNREACH_NEEDFRAG -- Path MTU Discovery (RFC1191) */
86 u_int16_t ipm_nextmtu;
91 u_int8_t irt_num_addrs;
93 u_int16_t irt_lifetime;
96 #define icmp_pptr icmp_hun.ih_pptr
97 #define icmp_gwaddr icmp_hun.ih_gwaddr
98 #define icmp_id icmp_hun.ih_idseq.icd_id
99 #define icmp_seq icmp_hun.ih_idseq.icd_seq
100 #define icmp_void icmp_hun.ih_void
101 #define icmp_pmvoid icmp_hun.ih_pmtu.ipm_void
102 #define icmp_nextmtu icmp_hun.ih_pmtu.ipm_nextmtu
103 #define icmp_num_addrs icmp_hun.ih_rtradv.irt_num_addrs
104 #define icmp_wpa icmp_hun.ih_rtradv.irt_wpa
105 #define icmp_lifetime icmp_hun.ih_rtradv.irt_lifetime
117 /* options and then 64 bits of data */
119 struct icmp_ra_addr id_radv;
123 #define icmp_otime icmp_dun.id_ts.its_otime
124 #define icmp_rtime icmp_dun.id_ts.its_rtime
125 #define icmp_ttime icmp_dun.id_ts.its_ttime
126 #define icmp_ip icmp_dun.id_ip.idi_ip
127 #define icmp_radv icmp_dun.id_radv
128 #define icmp_mask icmp_dun.id_mask
129 #define icmp_data icmp_dun.id_data
133 #define DEFDATALEN 56
135 #define MAXICMPLEN 76
136 #define MAXPACKET 65468
137 #define MAX_DUP_CHK (8 * 128)
139 #define PINGINTERVAL 1 /* second */
141 #define O_QUIET (1 << 0)
143 #define A(bit) rcvd_tbl[(bit)>>3] /* identify byte in array */
144 #define B(bit) (1 << ((bit) & 0x07)) /* identify bit in byte */
145 #define SET(bit) (A(bit) |= B(bit))
146 #define CLR(bit) (A(bit) &= (~B(bit)))
147 #define TST(bit) (A(bit) & B(bit))
149 static void ping(const char *host);
151 /* common routines */
152 static int in_cksum(unsigned short *buf, int sz)
156 unsigned short *w = buf;
157 unsigned short ans = 0;
165 *(unsigned char *) (&ans) = *(unsigned char *) w;
169 sum = (sum >> 16) + (sum & 0xFFFF);
176 #ifdef BB_FEATURE_SIMPLE_PING
177 static const char *ping_usage = "ping host\n"
178 #ifndef BB_FEATURE_TRIVIAL_HELP
179 "\nSend ICMP ECHO_REQUEST packets to network hosts\n"
183 static char *hostname = NULL;
185 static void noresp(int ign)
187 printf("No response from %s\n", hostname);
191 static void ping(const char *host)
194 struct sockaddr_in pingaddr;
197 char packet[DEFDATALEN + MAXIPLEN + MAXICMPLEN];
199 if ((pingsock = socket(AF_INET, SOCK_RAW, 1)) < 0) { /* 1 == ICMP */
200 perror("ping: creating a raw socket");
204 /* drop root privs if running setuid */
207 memset(&pingaddr, 0, sizeof(struct sockaddr_in));
209 pingaddr.sin_family = AF_INET;
210 if (!(h = gethostbyname(host))) {
211 fprintf(stderr, "ping: unknown host %s\n", host);
214 memcpy(&pingaddr.sin_addr, h->h_addr, sizeof(pingaddr.sin_addr));
215 hostname = h->h_name;
217 pkt = (struct icmp *) packet;
218 memset(pkt, 0, sizeof(packet));
219 pkt->icmp_type = ICMP_ECHO;
220 pkt->icmp_cksum = in_cksum((unsigned short *) pkt, sizeof(packet));
222 c = sendto(pingsock, packet, sizeof(packet), 0,
223 (struct sockaddr *) &pingaddr, sizeof(struct sockaddr_in));
225 if (c < 0 || c != sizeof(packet)) {
227 perror("ping: sendto");
228 fprintf(stderr, "ping: write incomplete\n");
232 signal(SIGALRM, noresp);
233 alarm(5); /* give the host 5000ms to respond */
234 /* listen for replies */
236 struct sockaddr_in from;
237 size_t fromlen = sizeof(from);
239 if ((c = recvfrom(pingsock, packet, sizeof(packet), 0,
240 (struct sockaddr *) &from, &fromlen)) < 0) {
243 perror("ping: recvfrom");
246 if (c >= 76) { /* ip + icmp */
247 struct iphdr *iphdr = (struct iphdr *) packet;
249 pkt = (struct icmp *) (packet + (iphdr->ihl << 2)); /* skip ip hdr */
250 if (pkt->icmp_type == ICMP_ECHOREPLY)
254 printf("%s is alive!\n", hostname);
258 extern int ping_main(int argc, char **argv)
268 #else /* ! BB_FEATURE_SIMPLE_PING */
269 /* full(er) version */
270 static const char *ping_usage = "ping [OPTION]... host\n"
271 #ifndef BB_FEATURE_TRIVIAL_HELP
272 "\nSend ICMP ECHO_REQUEST packets to network hosts.\n\n"
274 "\t-c COUNT\tSend only COUNT pings.\n"
275 "\t-s SIZE\t\tSend SIZE data bytes in packets (default=56).\n"
276 "\t-q\t\tQuiet mode, only displays output at start\n"
277 "\t\t\tand when finished.\n"
281 static char *hostname = NULL;
282 static struct sockaddr_in pingaddr;
283 static int pingsock = -1;
284 static int datalen = DEFDATALEN;
286 static long ntransmitted = 0, nreceived = 0, nrepeats = 0, pingcount = 0;
287 static int myid = 0, options = 0;
288 static unsigned long tmin = ULONG_MAX, tmax = 0, tsum = 0;
289 static char rcvd_tbl[MAX_DUP_CHK / 8];
291 static void sendping(int);
292 static void pingstats(int);
293 static void unpack(char *, int, struct sockaddr_in *);
295 /**************************************************************************/
297 static void pingstats(int ign)
299 signal(SIGINT, SIG_IGN);
301 printf("\n--- %s ping statistics ---\n", hostname);
302 printf("%ld packets transmitted, ", ntransmitted);
303 printf("%ld packets received, ", nreceived);
305 printf("%ld duplicates, ", nrepeats);
307 printf("%ld%% packet loss\n",
308 (ntransmitted - nreceived) * 100 / ntransmitted);
310 printf("round-trip min/avg/max = %lu.%lu/%lu.%lu/%lu.%lu ms\n",
311 tmin / 10, tmin % 10,
312 (tsum / (nreceived + nrepeats)) / 10,
313 (tsum / (nreceived + nrepeats)) % 10, tmax / 10, tmax % 10);
317 static void sendping(int ign)
321 char packet[datalen + 8];
323 pkt = (struct icmp *) packet;
325 pkt->icmp_type = ICMP_ECHO;
328 pkt->icmp_seq = ntransmitted++;
330 CLR(pkt->icmp_seq % MAX_DUP_CHK);
332 gettimeofday((struct timeval *) &packet[8], NULL);
333 pkt->icmp_cksum = in_cksum((unsigned short *) pkt, sizeof(packet));
335 i = sendto(pingsock, packet, sizeof(packet), 0,
336 (struct sockaddr *) &pingaddr, sizeof(struct sockaddr_in));
339 fatalError("sendto: %s\n", strerror(errno));
340 else if (i != sizeof(packet))
341 fatalError("ping wrote %d chars; %d expected\n", i,
342 (int)sizeof(packet));
344 signal(SIGALRM, sendping);
345 if (pingcount == 0 || ntransmitted < pingcount) { /* schedule next in 1s */
347 } else { /* done, wait for the last ping to come back */
348 /* todo, don't necessarily need to wait so long... */
349 signal(SIGALRM, pingstats);
354 static char *icmp_type_name (int id)
357 case ICMP_ECHOREPLY: return "Echo Reply";
358 case ICMP_DEST_UNREACH: return "Destination Unreachable";
359 case ICMP_SOURCE_QUENCH: return "Source Quench";
360 case ICMP_REDIRECT: return "Redirect (change route)";
361 case ICMP_ECHO: return "Echo Request";
362 case ICMP_TIME_EXCEEDED: return "Time Exceeded";
363 case ICMP_PARAMETERPROB: return "Parameter Problem";
364 case ICMP_TIMESTAMP: return "Timestamp Request";
365 case ICMP_TIMESTAMPREPLY: return "Timestamp Reply";
366 case ICMP_INFO_REQUEST: return "Information Request";
367 case ICMP_INFO_REPLY: return "Information Reply";
368 case ICMP_ADDRESS: return "Address Mask Request";
369 case ICMP_ADDRESSREPLY: return "Address Mask Reply";
370 default: return "unknown ICMP type";
374 static void unpack(char *buf, int sz, struct sockaddr_in *from)
376 struct icmp *icmppkt;
378 struct timeval tv, *tp;
380 unsigned long triptime;
382 gettimeofday(&tv, NULL);
384 /* check IP header */
385 iphdr = (struct iphdr *) buf;
386 hlen = iphdr->ihl << 2;
387 /* discard if too short */
388 if (sz < (datalen + ICMP_MINLEN))
392 icmppkt = (struct icmp *) (buf + hlen);
394 if (icmppkt->icmp_id != myid)
395 return; /* not our ping */
397 if (icmppkt->icmp_type == ICMP_ECHOREPLY) {
399 tp = (struct timeval *) icmppkt->icmp_data;
401 if ((tv.tv_usec -= tp->tv_usec) < 0) {
403 tv.tv_usec += 1000000;
405 tv.tv_sec -= tp->tv_sec;
407 triptime = tv.tv_sec * 10000 + (tv.tv_usec / 100);
414 if (TST(icmppkt->icmp_seq % MAX_DUP_CHK)) {
419 SET(icmppkt->icmp_seq % MAX_DUP_CHK);
423 if (options & O_QUIET)
426 printf("%d bytes from %s: icmp_seq=%u", sz,
427 inet_ntoa(*(struct in_addr *) &from->sin_addr.s_addr),
429 printf(" ttl=%d", iphdr->ttl);
430 printf(" time=%lu.%lu ms", triptime / 10, triptime % 10);
435 if (icmppkt->icmp_type != ICMP_ECHO)
437 "Warning: Got ICMP %d (%s)\n",
438 icmppkt->icmp_type, icmp_type_name (icmppkt->icmp_type));
441 static void ping(const char *host)
443 struct protoent *proto;
445 char buf[MAXHOSTNAMELEN];
446 char packet[datalen + MAXIPLEN + MAXICMPLEN];
449 proto = getprotobyname("icmp");
450 /* if getprotobyname failed, just silently force
451 * proto->p_proto to have the correct value for "icmp" */
452 if ((pingsock = socket(AF_INET, SOCK_RAW,
453 (proto ? proto->p_proto : 1))) < 0) { /* 1 == ICMP */
454 if (errno == EPERM) {
455 fprintf(stderr, "ping: permission denied. (are you root?)\n");
457 perror("ping: creating a raw socket");
462 /* drop root privs if running setuid */
465 memset(&pingaddr, 0, sizeof(struct sockaddr_in));
467 pingaddr.sin_family = AF_INET;
468 if (!(h = gethostbyname(host))) {
469 fprintf(stderr, "ping: unknown host %s\n", host);
473 if (h->h_addrtype != AF_INET) {
475 "ping: unknown address type; only AF_INET is currently supported.\n");
479 pingaddr.sin_family = AF_INET; /* h->h_addrtype */
480 memcpy(&pingaddr.sin_addr, h->h_addr, sizeof(pingaddr.sin_addr));
481 strncpy(buf, h->h_name, sizeof(buf) - 1);
484 /* enable broadcast pings */
486 setsockopt(pingsock, SOL_SOCKET, SO_BROADCAST, (char *) &sockopt,
489 /* set recv buf for broadcast pings */
491 setsockopt(pingsock, SOL_SOCKET, SO_RCVBUF, (char *) &sockopt,
494 printf("PING %s (%s): %d data bytes\n",
496 inet_ntoa(*(struct in_addr *) &pingaddr.sin_addr.s_addr),
499 signal(SIGINT, pingstats);
501 /* start the ping's going ... */
504 /* listen for replies */
506 struct sockaddr_in from;
507 socklen_t fromlen = (socklen_t) sizeof(from);
510 if ((c = recvfrom(pingsock, packet, sizeof(packet), 0,
511 (struct sockaddr *) &from, &fromlen)) < 0) {
514 perror("ping: recvfrom");
517 unpack(packet, c, &from);
518 if (pingcount > 0 && nreceived >= pingcount)
524 extern int ping_main(int argc, char **argv)
531 /* Parse any options */
532 while (argc >= 1 && **argv == '-') {
543 pingcount = atoi(*argv);
549 datalen = atoi(*argv);
560 myid = getpid() & 0xFFFF;
564 #endif /* ! BB_FEATURE_SIMPLE_PING */
567 * Copyright (c) 1989 The Regents of the University of California.
568 * All rights reserved.
570 * This code is derived from software contributed to Berkeley by
573 * Redistribution and use in source and binary forms, with or without
574 * modification, are permitted provided that the following conditions
576 * 1. Redistributions of source code must retain the above copyright
577 * notice, this list of conditions and the following disclaimer.
578 * 2. Redistributions in binary form must reproduce the above copyright
579 * notice, this list of conditions and the following disclaimer in the
580 * documentation and/or other materials provided with the distribution.
581 * 3. All advertising materials mentioning features or use of this software
582 * must display the following acknowledgement:
583 * This product includes software developed by the University of
584 * California, Berkeley and its contributors.
585 * 4. Neither the name of the University nor the names of its contributors
586 * may be used to endorse or promote products derived from this software
587 * without specific prior written permission.
589 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
590 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
591 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
592 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
593 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
594 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
595 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
596 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
597 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
598 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF