Magellan Linux

Annotation of /trunk/mkinitrd-magellan/busybox/networking/ping.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 532 - (hide annotations) (download)
Sat Sep 1 22:45:15 2007 UTC (16 years, 9 months ago) by niro
File MIME type: text/plain
File size: 10683 byte(s)
-import if magellan mkinitrd; it is a fork of redhats mkinitrd-5.0.8 with all magellan patches and features; deprecates magellan-src/mkinitrd

1 niro 532 /* vi: set sw=4 ts=4: */
2     /*
3     * $Id: ping.c,v 1.1 2007-09-01 22:43:53 niro Exp $
4     * Mini ping implementation for busybox
5     *
6     * Copyright (C) 1999 by Randolph Chung <tausq@debian.org>
7     *
8     * Adapted from the ping in netkit-base 0.10:
9     * Copyright (c) 1989 The Regents of the University of California.
10     * Derived from software contributed to Berkeley by Mike Muuss.
11     *
12     * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
13     */
14    
15     #include <netinet/ip_icmp.h>
16     #include "busybox.h"
17    
18     enum {
19     DEFDATALEN = 56,
20     MAXIPLEN = 60,
21     MAXICMPLEN = 76,
22     MAXPACKET = 65468,
23     MAX_DUP_CHK = (8 * 128),
24     MAXWAIT = 10,
25     PINGINTERVAL = 1 /* second */
26     };
27    
28     static void ping(const char *host);
29    
30     /* common routines */
31    
32     static int in_cksum(unsigned short *buf, int sz)
33     {
34     int nleft = sz;
35     int sum = 0;
36     unsigned short *w = buf;
37     unsigned short ans = 0;
38    
39     while (nleft > 1) {
40     sum += *w++;
41     nleft -= 2;
42     }
43    
44     if (nleft == 1) {
45     *(unsigned char *) (&ans) = *(unsigned char *) w;
46     sum += ans;
47     }
48    
49     sum = (sum >> 16) + (sum & 0xFFFF);
50     sum += (sum >> 16);
51     ans = ~sum;
52     return ans;
53     }
54    
55     #ifndef CONFIG_FEATURE_FANCY_PING
56    
57     /* simple version */
58    
59     static char *hostname;
60    
61     static void noresp(int ign)
62     {
63     printf("No response from %s\n", hostname);
64     exit(EXIT_FAILURE);
65     }
66    
67     static void ping(const char *host)
68     {
69     struct hostent *h;
70     struct sockaddr_in pingaddr;
71     struct icmp *pkt;
72     int pingsock, c;
73     char packet[DEFDATALEN + MAXIPLEN + MAXICMPLEN];
74    
75     pingsock = create_icmp_socket();
76    
77     memset(&pingaddr, 0, sizeof(struct sockaddr_in));
78    
79     pingaddr.sin_family = AF_INET;
80     h = xgethostbyname(host);
81     memcpy(&pingaddr.sin_addr, h->h_addr, sizeof(pingaddr.sin_addr));
82     hostname = h->h_name;
83    
84     pkt = (struct icmp *) packet;
85     memset(pkt, 0, sizeof(packet));
86     pkt->icmp_type = ICMP_ECHO;
87     pkt->icmp_cksum = in_cksum((unsigned short *) pkt, sizeof(packet));
88    
89     c = sendto(pingsock, packet, DEFDATALEN + ICMP_MINLEN, 0,
90     (struct sockaddr *) &pingaddr, sizeof(struct sockaddr_in));
91    
92     if (c < 0) {
93     if (ENABLE_FEATURE_CLEAN_UP) close(pingsock);
94     bb_perror_msg_and_die("sendto");
95     }
96    
97     signal(SIGALRM, noresp);
98     alarm(5); /* give the host 5000ms to respond */
99     /* listen for replies */
100     while (1) {
101     struct sockaddr_in from;
102     socklen_t fromlen = sizeof(from);
103    
104     c = recvfrom(pingsock, packet, sizeof(packet), 0,
105     (struct sockaddr *) &from, &fromlen);
106     if (c < 0) {
107     if (errno != EINTR)
108     bb_perror_msg("recvfrom");
109     continue;
110     }
111     if (c >= 76) { /* ip + icmp */
112     struct iphdr *iphdr = (struct iphdr *) packet;
113    
114     pkt = (struct icmp *) (packet + (iphdr->ihl << 2)); /* skip ip hdr */
115     if (pkt->icmp_type == ICMP_ECHOREPLY)
116     break;
117     }
118     }
119     if (ENABLE_FEATURE_CLEAN_UP) close(pingsock);
120     printf("%s is alive!\n", hostname);
121     }
122    
123     int ping_main(int argc, char **argv)
124     {
125     argc--;
126     argv++;
127     if (argc < 1)
128     bb_show_usage();
129     ping(*argv);
130     return EXIT_SUCCESS;
131     }
132    
133     #else /* ! CONFIG_FEATURE_FANCY_PING */
134    
135     /* full(er) version */
136    
137     #define OPT_STRING "qc:s:I:"
138     enum {
139     OPT_QUIET = 1 << 0,
140     };
141    
142     static struct sockaddr_in pingaddr;
143     static struct sockaddr_in sourceaddr;
144     static int pingsock = -1;
145     static unsigned datalen; /* intentionally uninitialized to work around gcc bug */
146    
147     static unsigned long ntransmitted, nreceived, nrepeats, pingcount;
148     static int myid;
149     static unsigned long tmin = ULONG_MAX, tmax, tsum;
150     static char rcvd_tbl[MAX_DUP_CHK / 8];
151    
152     static struct hostent *hostent;
153    
154     static void sendping(int);
155     static void pingstats(int);
156     static void unpack(char *, int, struct sockaddr_in *);
157    
158     #define A(bit) rcvd_tbl[(bit)>>3] /* identify byte in array */
159     #define B(bit) (1 << ((bit) & 0x07)) /* identify bit in byte */
160     #define SET(bit) (A(bit) |= B(bit))
161     #define CLR(bit) (A(bit) &= (~B(bit)))
162     #define TST(bit) (A(bit) & B(bit))
163    
164     /**************************************************************************/
165    
166     static void pingstats(int junk)
167     {
168     int status;
169    
170     signal(SIGINT, SIG_IGN);
171    
172     printf("\n--- %s ping statistics ---\n", hostent->h_name);
173     printf("%lu packets transmitted, ", ntransmitted);
174     printf("%lu packets received, ", nreceived);
175     if (nrepeats)
176     printf("%lu duplicates, ", nrepeats);
177     if (ntransmitted)
178     printf("%lu%% packet loss\n",
179     (ntransmitted - nreceived) * 100 / ntransmitted);
180     if (nreceived)
181     printf("round-trip min/avg/max = %lu.%lu/%lu.%lu/%lu.%lu ms\n",
182     tmin / 10, tmin % 10,
183     (tsum / (nreceived + nrepeats)) / 10,
184     (tsum / (nreceived + nrepeats)) % 10, tmax / 10, tmax % 10);
185     if (nreceived != 0)
186     status = EXIT_SUCCESS;
187     else
188     status = EXIT_FAILURE;
189     exit(status);
190     }
191    
192     static void sendping(int junk)
193     {
194     struct icmp *pkt;
195     int i;
196     char packet[datalen + ICMP_MINLEN];
197    
198     pkt = (struct icmp *) packet;
199    
200     pkt->icmp_type = ICMP_ECHO;
201     pkt->icmp_code = 0;
202     pkt->icmp_cksum = 0;
203     pkt->icmp_seq = htons(ntransmitted); /* don't ++ here, it can be a macro */
204     pkt->icmp_id = myid;
205     CLR(ntohs(pkt->icmp_seq) % MAX_DUP_CHK);
206     ntransmitted++;
207    
208     gettimeofday((struct timeval *) &pkt->icmp_dun, NULL);
209     pkt->icmp_cksum = in_cksum((unsigned short *) pkt, sizeof(packet));
210    
211     i = sendto(pingsock, packet, sizeof(packet), 0,
212     (struct sockaddr *) &pingaddr, sizeof(struct sockaddr_in));
213    
214     if (i < 0)
215     bb_perror_msg_and_die("sendto");
216     if ((size_t)i != sizeof(packet))
217     bb_error_msg_and_die("ping wrote %d chars; %d expected", i,
218     (int)sizeof(packet));
219    
220     signal(SIGALRM, sendping);
221     if (pingcount == 0 || ntransmitted < pingcount) { /* schedule next in 1s */
222     alarm(PINGINTERVAL);
223     } else { /* done, wait for the last ping to come back */
224     /* todo, don't necessarily need to wait so long... */
225     signal(SIGALRM, pingstats);
226     alarm(MAXWAIT);
227     }
228     }
229    
230     static char *icmp_type_name(int id)
231     {
232     switch (id) {
233     case ICMP_ECHOREPLY: return "Echo Reply";
234     case ICMP_DEST_UNREACH: return "Destination Unreachable";
235     case ICMP_SOURCE_QUENCH: return "Source Quench";
236     case ICMP_REDIRECT: return "Redirect (change route)";
237     case ICMP_ECHO: return "Echo Request";
238     case ICMP_TIME_EXCEEDED: return "Time Exceeded";
239     case ICMP_PARAMETERPROB: return "Parameter Problem";
240     case ICMP_TIMESTAMP: return "Timestamp Request";
241     case ICMP_TIMESTAMPREPLY: return "Timestamp Reply";
242     case ICMP_INFO_REQUEST: return "Information Request";
243     case ICMP_INFO_REPLY: return "Information Reply";
244     case ICMP_ADDRESS: return "Address Mask Request";
245     case ICMP_ADDRESSREPLY: return "Address Mask Reply";
246     default: return "unknown ICMP type";
247     }
248     }
249    
250     static void unpack(char *buf, int sz, struct sockaddr_in *from)
251     {
252     struct icmp *icmppkt;
253     struct iphdr *iphdr;
254     struct timeval tv, *tp;
255     int hlen, dupflag;
256     unsigned long triptime;
257    
258     gettimeofday(&tv, NULL);
259    
260     /* discard if too short */
261     if (sz < (datalen + ICMP_MINLEN))
262     return;
263    
264     /* check IP header */
265     iphdr = (struct iphdr *) buf;
266     hlen = iphdr->ihl << 2;
267     sz -= hlen;
268     icmppkt = (struct icmp *) (buf + hlen);
269     if (icmppkt->icmp_id != myid)
270     return; /* not our ping */
271    
272     if (icmppkt->icmp_type == ICMP_ECHOREPLY) {
273     u_int16_t recv_seq = ntohs(icmppkt->icmp_seq);
274     ++nreceived;
275     tp = (struct timeval *) icmppkt->icmp_data;
276    
277     if ((tv.tv_usec -= tp->tv_usec) < 0) {
278     --tv.tv_sec;
279     tv.tv_usec += 1000000;
280     }
281     tv.tv_sec -= tp->tv_sec;
282    
283     triptime = tv.tv_sec * 10000 + (tv.tv_usec / 100);
284     tsum += triptime;
285     if (triptime < tmin)
286     tmin = triptime;
287     if (triptime > tmax)
288     tmax = triptime;
289    
290     if (TST(recv_seq % MAX_DUP_CHK)) {
291     ++nrepeats;
292     --nreceived;
293     dupflag = 1;
294     } else {
295     SET(recv_seq % MAX_DUP_CHK);
296     dupflag = 0;
297     }
298    
299     if (option_mask32 & OPT_QUIET)
300     return;
301    
302     printf("%d bytes from %s: icmp_seq=%u", sz,
303     inet_ntoa(*(struct in_addr *) &from->sin_addr.s_addr),
304     recv_seq);
305     printf(" ttl=%d", iphdr->ttl);
306     printf(" time=%lu.%lu ms", triptime / 10, triptime % 10);
307     if (dupflag)
308     printf(" (DUP!)");
309     puts("");
310     } else
311     if (icmppkt->icmp_type != ICMP_ECHO)
312     bb_error_msg("warning: got ICMP %d (%s)",
313     icmppkt->icmp_type,
314     icmp_type_name(icmppkt->icmp_type));
315     fflush(stdout);
316     }
317    
318     static void ping(const char *host)
319     {
320     char packet[datalen + MAXIPLEN + MAXICMPLEN];
321     int sockopt;
322    
323     pingsock = create_icmp_socket();
324    
325     if (sourceaddr.sin_addr.s_addr) {
326     xbind(pingsock, (struct sockaddr*)&sourceaddr, sizeof(sourceaddr));
327     }
328    
329     memset(&pingaddr, 0, sizeof(struct sockaddr_in));
330    
331     pingaddr.sin_family = AF_INET;
332     hostent = xgethostbyname(host);
333     if (hostent->h_addrtype != AF_INET)
334     bb_error_msg_and_die("unknown address type; only AF_INET is currently supported");
335    
336     memcpy(&pingaddr.sin_addr, hostent->h_addr, sizeof(pingaddr.sin_addr));
337    
338     /* enable broadcast pings */
339     setsockopt_broadcast(pingsock);
340    
341     /* set recv buf for broadcast pings */
342     sockopt = 48 * 1024;
343     setsockopt(pingsock, SOL_SOCKET, SO_RCVBUF, (char *) &sockopt,
344     sizeof(sockopt));
345    
346     printf("PING %s (%s)",
347     hostent->h_name,
348     inet_ntoa(*(struct in_addr *) &pingaddr.sin_addr.s_addr));
349     if (sourceaddr.sin_addr.s_addr) {
350     printf(" from %s",
351     inet_ntoa(*(struct in_addr *) &sourceaddr.sin_addr.s_addr));
352     }
353     printf(": %d data bytes\n", datalen);
354    
355     signal(SIGINT, pingstats);
356    
357     /* start the ping's going ... */
358     sendping(0);
359    
360     /* listen for replies */
361     while (1) {
362     struct sockaddr_in from;
363     socklen_t fromlen = (socklen_t) sizeof(from);
364     int c;
365    
366     c = recvfrom(pingsock, packet, sizeof(packet), 0,
367     (struct sockaddr *) &from, &fromlen);
368     if (c < 0) {
369     if (errno != EINTR)
370     bb_perror_msg("recvfrom");
371     continue;
372     }
373     unpack(packet, c, &from);
374     if (pingcount > 0 && nreceived >= pingcount)
375     break;
376     }
377     pingstats(0);
378     }
379    
380     /* TODO: consolidate ether-wake.c, dnsd.c, ifupdown.c, nslookup.c
381     * versions of below thing. BTW we have far too many "%u.%u.%u.%u" too...
382     */
383     static int parse_nipquad(const char *str, struct sockaddr_in* addr)
384     {
385     char dummy;
386     unsigned i1, i2, i3, i4;
387     if (sscanf(str, "%u.%u.%u.%u%c",
388     &i1, &i2, &i3, &i4, &dummy) == 4
389     && ( (i1|i2|i3|i4) <= 0xff )
390     ) {
391     uint8_t* ptr = (uint8_t*)&addr->sin_addr;
392     ptr[0] = i1;
393     ptr[1] = i2;
394     ptr[2] = i3;
395     ptr[3] = i4;
396     return 0;
397     }
398     return 1; /* error */
399     }
400    
401     int ping_main(int argc, char **argv)
402     {
403     char *opt_c, *opt_s, *opt_I;
404    
405     datalen = DEFDATALEN; /* initialized here rather than in global scope to work around gcc bug */
406    
407     /* exactly one argument needed */
408     opt_complementary = "=1";
409     getopt32(argc, argv, OPT_STRING, &opt_c, &opt_s, &opt_I);
410     if (option_mask32 & 2) pingcount = xatoul(opt_c); // -c
411     if (option_mask32 & 4) datalen = xatou16(opt_s); // -s
412     if (option_mask32 & 8) { // -I
413     /* TODO: ping6 accepts iface too:
414     if_index = if_nametoindex(*argv);
415     if (!if_index) ...
416     make it true for ping. */
417     if (parse_nipquad(opt_I, &sourceaddr))
418     bb_show_usage();
419     }
420    
421     myid = (int16_t) getpid();
422     ping(argv[optind]);
423     return EXIT_SUCCESS;
424     }
425     #endif /* ! CONFIG_FEATURE_FANCY_PING */