Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 532 by niro, Sat Sep 1 22:45:15 2007 UTC revision 816 by niro, Fri Apr 24 18:33:46 2009 UTC
# Line 1  Line 1 
1  /* vi: set sw=4 ts=4: */  /* vi: set sw=4 ts=4: */
2  /*  /*
  * $Id: ping.c,v 1.1 2007-09-01 22:43:53 niro Exp $  
3   * Mini ping implementation for busybox   * Mini ping implementation for busybox
4   *   *
5   * Copyright (C) 1999 by Randolph Chung <tausq@debian.org>   * Copyright (C) 1999 by Randolph Chung <tausq@debian.org>
6   *   *
7   * Adapted from the ping in netkit-base 0.10:   * Adapted from the ping in netkit-base 0.10:
8   * Copyright (c) 1989 The Regents of the University of California.   * Copyright (c) 1989 The Regents of the University of California.
9   * Derived from software contributed to Berkeley by Mike Muuss.   * All rights reserved.
10     *
11     * This code is derived from software contributed to Berkeley by
12     * Mike Muuss.
13   *   *
14   * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.   * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
15   */   */
16    /* from ping6.c:
17     * Copyright (C) 1999 by Randolph Chung <tausq@debian.org>
18     *
19     * This version of ping is adapted from the ping in netkit-base 0.10,
20     * which is:
21     *
22     * Original copyright notice is retained at the end of this file.
23     *
24     * This version is an adaptation of ping.c from busybox.
25     * The code was modified by Bart Visscher <magick@linux-fan.com>
26     */
27    
28    #include <net/if.h>
29  #include <netinet/ip_icmp.h>  #include <netinet/ip_icmp.h>
30  #include "busybox.h"  #include "libbb.h"
31    
32    #if ENABLE_PING6
33    #include <netinet/icmp6.h>
34    /* I see RENUMBERED constants in bits/in.h - !!?
35     * What a fuck is going on with libc? Is it a glibc joke? */
36    #ifdef IPV6_2292HOPLIMIT
37    #undef IPV6_HOPLIMIT
38    #define IPV6_HOPLIMIT IPV6_2292HOPLIMIT
39    #endif
40    #endif
41    
42  enum {  enum {
43   DEFDATALEN = 56,   DEFDATALEN = 56,
# Line 22  enum { Line 46  enum {
46   MAXPACKET = 65468,   MAXPACKET = 65468,
47   MAX_DUP_CHK = (8 * 128),   MAX_DUP_CHK = (8 * 128),
48   MAXWAIT = 10,   MAXWAIT = 10,
49   PINGINTERVAL = 1 /* second */   PINGINTERVAL = 1, /* 1 second */
50  };  };
51    
 static void ping(const char *host);  
   
52  /* common routines */  /* common routines */
53    
54  static int in_cksum(unsigned short *buf, int sz)  static int in_cksum(unsigned short *buf, int sz)
# Line 52  static int in_cksum(unsigned short *buf, Line 74  static int in_cksum(unsigned short *buf,
74   return ans;   return ans;
75  }  }
76    
77  #ifndef CONFIG_FEATURE_FANCY_PING  #if !ENABLE_FEATURE_FANCY_PING
78    
79  /* simple version */  /* simple version */
80    
81  static char *hostname;  static char *hostname;
82    
83  static void noresp(int ign)  static void noresp(int ign UNUSED_PARAM)
84  {  {
85   printf("No response from %s\n", hostname);   printf("No response from %s\n", hostname);
86   exit(EXIT_FAILURE);   exit(EXIT_FAILURE);
87  }  }
88    
89  static void ping(const char *host)  static void ping4(len_and_sockaddr *lsa)
90  {  {
  struct hostent *h;  
91   struct sockaddr_in pingaddr;   struct sockaddr_in pingaddr;
92   struct icmp *pkt;   struct icmp *pkt;
93   int pingsock, c;   int pingsock, c;
94   char packet[DEFDATALEN + MAXIPLEN + MAXICMPLEN];   char packet[DEFDATALEN + MAXIPLEN + MAXICMPLEN];
95    
96   pingsock = create_icmp_socket();   pingsock = create_icmp_socket();
97     pingaddr = lsa->u.sin;
  memset(&pingaddr, 0, sizeof(struct sockaddr_in));  
   
  pingaddr.sin_family = AF_INET;  
  h = xgethostbyname(host);  
  memcpy(&pingaddr.sin_addr, h->h_addr, sizeof(pingaddr.sin_addr));  
  hostname = h->h_name;  
98    
99   pkt = (struct icmp *) packet;   pkt = (struct icmp *) packet;
100   memset(pkt, 0, sizeof(packet));   memset(pkt, 0, sizeof(packet));
101   pkt->icmp_type = ICMP_ECHO;   pkt->icmp_type = ICMP_ECHO;
102   pkt->icmp_cksum = in_cksum((unsigned short *) pkt, sizeof(packet));   pkt->icmp_cksum = in_cksum((unsigned short *) pkt, sizeof(packet));
103    
104   c = sendto(pingsock, packet, DEFDATALEN + ICMP_MINLEN, 0,   c = xsendto(pingsock, packet, DEFDATALEN + ICMP_MINLEN,
105     (struct sockaddr *) &pingaddr, sizeof(struct sockaddr_in));     (struct sockaddr *) &pingaddr, sizeof(pingaddr));
106    
  if (c < 0) {  
  if (ENABLE_FEATURE_CLEAN_UP) close(pingsock);  
  bb_perror_msg_and_die("sendto");  
  }  
   
  signal(SIGALRM, noresp);  
  alarm(5); /* give the host 5000ms to respond */  
107   /* listen for replies */   /* listen for replies */
108   while (1) {   while (1) {
109   struct sockaddr_in from;   struct sockaddr_in from;
# Line 116  static void ping(const char *host) Line 124  static void ping(const char *host)
124   break;   break;
125   }   }
126   }   }
127   if (ENABLE_FEATURE_CLEAN_UP) close(pingsock);   if (ENABLE_FEATURE_CLEAN_UP)
128   printf("%s is alive!\n", hostname);   close(pingsock);
129    }
130    
131    #if ENABLE_PING6
132    static void ping6(len_and_sockaddr *lsa)
133    {
134     struct sockaddr_in6 pingaddr;
135     struct icmp6_hdr *pkt;
136     int pingsock, c;
137     int sockopt;
138     char packet[DEFDATALEN + MAXIPLEN + MAXICMPLEN];
139    
140     pingsock = create_icmp6_socket();
141     pingaddr = lsa->u.sin6;
142    
143     pkt = (struct icmp6_hdr *) packet;
144     memset(pkt, 0, sizeof(packet));
145     pkt->icmp6_type = ICMP6_ECHO_REQUEST;
146    
147     sockopt = offsetof(struct icmp6_hdr, icmp6_cksum);
148     setsockopt(pingsock, SOL_RAW, IPV6_CHECKSUM, &sockopt, sizeof(sockopt));
149    
150     c = xsendto(pingsock, packet, DEFDATALEN + sizeof (struct icmp6_hdr),
151       (struct sockaddr *) &pingaddr, sizeof(pingaddr));
152    
153     /* listen for replies */
154     while (1) {
155     struct sockaddr_in6 from;
156     socklen_t fromlen = sizeof(from);
157    
158     c = recvfrom(pingsock, packet, sizeof(packet), 0,
159     (struct sockaddr *) &from, &fromlen);
160     if (c < 0) {
161     if (errno != EINTR)
162     bb_perror_msg("recvfrom");
163     continue;
164     }
165     if (c >= 8) { /* icmp6_hdr */
166     pkt = (struct icmp6_hdr *) packet;
167     if (pkt->icmp6_type == ICMP6_ECHO_REPLY)
168     break;
169     }
170     }
171     if (ENABLE_FEATURE_CLEAN_UP)
172     close(pingsock);
173  }  }
174    #endif
175    
176  int ping_main(int argc, char **argv)  int ping_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
177    int ping_main(int argc UNUSED_PARAM, char **argv)
178  {  {
179   argc--;   len_and_sockaddr *lsa;
180    #if ENABLE_PING6
181     sa_family_t af = AF_UNSPEC;
182    
183     while ((++argv)[0] && argv[0][0] == '-') {
184     if (argv[0][1] == '4') {
185     af = AF_INET;
186     continue;
187     }
188     if (argv[0][1] == '6') {
189     af = AF_INET6;
190     continue;
191     }
192     bb_show_usage();
193     }
194    #else
195   argv++;   argv++;
196   if (argc < 1)  #endif
197    
198     hostname = *argv;
199     if (!hostname)
200   bb_show_usage();   bb_show_usage();
201   ping(*argv);  
202    #if ENABLE_PING6
203     lsa = xhost_and_af2sockaddr(hostname, 0, af);
204    #else
205     lsa = xhost_and_af2sockaddr(hostname, 0, AF_INET);
206    #endif
207     /* Set timer _after_ DNS resolution */
208     signal(SIGALRM, noresp);
209     alarm(5); /* give the host 5000ms to respond */
210    
211    #if ENABLE_PING6
212     if (lsa->u.sa.sa_family == AF_INET6)
213     ping6(lsa);
214     else
215    #endif
216     ping4(lsa);
217     printf("%s is alive!\n", hostname);
218   return EXIT_SUCCESS;   return EXIT_SUCCESS;
219  }  }
220    
221  #else /* ! CONFIG_FEATURE_FANCY_PING */  
222    #else /* FEATURE_FANCY_PING */
223    
224    
225  /* full(er) version */  /* full(er) version */
226    
227  #define OPT_STRING "qc:s:I:"  #define OPT_STRING ("qvc:s:w:W:I:4" USE_PING6("6"))
228  enum {  enum {
229   OPT_QUIET = 1 << 0,   OPT_QUIET = 1 << 0,
230     OPT_VERBOSE = 1 << 1,
231     OPT_c = 1 << 2,
232     OPT_s = 1 << 3,
233     OPT_w = 1 << 4,
234     OPT_W = 1 << 5,
235     OPT_I = 1 << 6,
236     OPT_IPV4 = 1 << 7,
237     OPT_IPV6 = (1 << 8) * ENABLE_PING6,
238    };
239    
240    
241    struct globals {
242     int pingsock;
243     int if_index;
244     char *str_I;
245     len_and_sockaddr *source_lsa;
246     unsigned datalen;
247     unsigned pingcount; /* must be int-sized */
248     unsigned long ntransmitted, nreceived, nrepeats;
249     uint16_t myid;
250     unsigned tmin, tmax; /* in us */
251     unsigned long long tsum; /* in us, sum of all times */
252     unsigned deadline;
253     unsigned timeout;
254     unsigned total_secs;
255     const char *hostname;
256     const char *dotted;
257     union {
258     struct sockaddr sa;
259     struct sockaddr_in sin;
260    #if ENABLE_PING6
261     struct sockaddr_in6 sin6;
262    #endif
263     } pingaddr;
264     char rcvd_tbl[MAX_DUP_CHK / 8];
265  };  };
266    #define G (*(struct globals*)&bb_common_bufsiz1)
267    #define pingsock     (G.pingsock    )
268    #define if_index     (G.if_index    )
269    #define source_lsa   (G.source_lsa  )
270    #define str_I        (G.str_I       )
271    #define datalen      (G.datalen     )
272    #define ntransmitted (G.ntransmitted)
273    #define nreceived    (G.nreceived   )
274    #define nrepeats     (G.nrepeats    )
275    #define pingcount    (G.pingcount   )
276    #define myid         (G.myid        )
277    #define tmin         (G.tmin        )
278    #define tmax         (G.tmax        )
279    #define tsum         (G.tsum        )
280    #define deadline     (G.deadline    )
281    #define timeout      (G.timeout     )
282    #define total_secs   (G.total_secs  )
283    #define hostname     (G.hostname    )
284    #define dotted       (G.dotted      )
285    #define pingaddr     (G.pingaddr    )
286    #define rcvd_tbl     (G.rcvd_tbl    )
287    void BUG_ping_globals_too_big(void);
288    #define INIT_G() do { \
289     if (sizeof(G) > COMMON_BUFSIZE) \
290     BUG_ping_globals_too_big(); \
291     pingsock = -1; \
292     datalen = DEFDATALEN; \
293     timeout = MAXWAIT; \
294     tmin = UINT_MAX; \
295    } while (0)
296    
 static struct sockaddr_in pingaddr;  
 static struct sockaddr_in sourceaddr;  
 static int pingsock = -1;  
 static unsigned datalen; /* intentionally uninitialized to work around gcc bug */  
   
 static unsigned long ntransmitted, nreceived, nrepeats, pingcount;  
 static int myid;  
 static unsigned long tmin = ULONG_MAX, tmax, tsum;  
 static char rcvd_tbl[MAX_DUP_CHK / 8];  
   
 static struct hostent *hostent;  
   
 static void sendping(int);  
 static void pingstats(int);  
 static void unpack(char *, int, struct sockaddr_in *);  
297    
298  #define A(bit) rcvd_tbl[(bit)>>3] /* identify byte in array */  #define A(bit) rcvd_tbl[(bit)>>3] /* identify byte in array */
299  #define B(bit) (1 << ((bit) & 0x07)) /* identify bit in byte */  #define B(bit) (1 << ((bit) & 0x07)) /* identify bit in byte */
# Line 163  static void unpack(char *, int, struct s Line 303  static void unpack(char *, int, struct s
303    
304  /**************************************************************************/  /**************************************************************************/
305    
306  static void pingstats(int junk)  static void print_stats_and_exit(int junk) NORETURN;
307    static void print_stats_and_exit(int junk UNUSED_PARAM)
308  {  {
  int status;  
   
309   signal(SIGINT, SIG_IGN);   signal(SIGINT, SIG_IGN);
310    
311   printf("\n--- %s ping statistics ---\n", hostent->h_name);   printf("\n--- %s ping statistics ---\n", hostname);
312   printf("%lu packets transmitted, ", ntransmitted);   printf("%lu packets transmitted, ", ntransmitted);
313   printf("%lu packets received, ", nreceived);   printf("%lu packets received, ", nreceived);
314   if (nrepeats)   if (nrepeats)
315   printf("%lu duplicates, ", nrepeats);   printf("%lu duplicates, ", nrepeats);
316   if (ntransmitted)   if (ntransmitted)
317   printf("%lu%% packet loss\n",   ntransmitted = (ntransmitted - nreceived) * 100 / ntransmitted;
318     (ntransmitted - nreceived) * 100 / ntransmitted);   printf("%lu%% packet loss\n", ntransmitted);
319   if (nreceived)   if (tmin != UINT_MAX) {
320   printf("round-trip min/avg/max = %lu.%lu/%lu.%lu/%lu.%lu ms\n",   unsigned tavg = tsum / (nreceived + nrepeats);
321     tmin / 10, tmin % 10,   printf("round-trip min/avg/max = %u.%03u/%u.%03u/%u.%03u ms\n",
322     (tsum / (nreceived + nrepeats)) / 10,   tmin / 1000, tmin % 1000,
323     (tsum / (nreceived + nrepeats)) % 10, tmax / 10, tmax % 10);   tavg / 1000, tavg % 1000,
324   if (nreceived != 0)   tmax / 1000, tmax % 1000);
325   status = EXIT_SUCCESS;   }
326   else   /* if condition is true, exit with 1 -- 'failure' */
327   status = EXIT_FAILURE;   exit(nreceived == 0 || (deadline && nreceived < pingcount));
  exit(status);  
328  }  }
329    
330  static void sendping(int junk)  static void sendping_tail(void (*sp)(int), const void *pkt, int size_pkt)
331  {  {
332   struct icmp *pkt;   int sz;
  int i;  
  char packet[datalen + ICMP_MINLEN];  
333    
334   pkt = (struct icmp *) packet;   CLR((uint16_t)ntransmitted % MAX_DUP_CHK);
335     ntransmitted++;
336    
337     /* sizeof(pingaddr) can be larger than real sa size, but I think
338     * it doesn't matter */
339     sz = xsendto(pingsock, pkt, size_pkt, &pingaddr.sa, sizeof(pingaddr));
340     if (sz != size_pkt)
341     bb_error_msg_and_die(bb_msg_write_error);
342    
343     if (pingcount == 0 || deadline || ntransmitted < pingcount) {
344     /* Didn't send all pings yet - schedule next in 1s */
345     signal(SIGALRM, sp);
346     if (deadline) {
347     total_secs += PINGINTERVAL;
348     if (total_secs >= deadline)
349     signal(SIGALRM, print_stats_and_exit);
350     }
351     alarm(PINGINTERVAL);
352     } else { /* -c NN, and all NN are sent (and no deadline) */
353     /* Wait for the last ping to come back.
354     * -W timeout: wait for a response in seconds.
355     * Affects only timeout in absense of any responses,
356     * otherwise ping waits for two RTTs. */
357     unsigned expire = timeout;
358    
359     if (nreceived) {
360     /* approx. 2*tmax, in seconds (2 RTT) */
361     expire = tmax / (512*1024);
362     if (expire == 0)
363     expire = 1;
364     }
365     signal(SIGALRM, print_stats_and_exit);
366     alarm(expire);
367     }
368    }
369    
370    static void sendping4(int junk UNUSED_PARAM)
371    {
372     /* +4 reserves a place for timestamp, which may end up sitting
373     * *after* packet. Saves one if() */
374     struct icmp *pkt = alloca(datalen + ICMP_MINLEN + 4);
375    
376     memset(pkt, 0, datalen + ICMP_MINLEN + 4);
377   pkt->icmp_type = ICMP_ECHO;   pkt->icmp_type = ICMP_ECHO;
378   pkt->icmp_code = 0;   /*pkt->icmp_code = 0;*/
379   pkt->icmp_cksum = 0;   /*pkt->icmp_cksum = 0;*/
380   pkt->icmp_seq = htons(ntransmitted); /* don't ++ here, it can be a macro */   pkt->icmp_seq = htons(ntransmitted); /* don't ++ here, it can be a macro */
381   pkt->icmp_id = myid;   pkt->icmp_id = myid;
  CLR(ntohs(pkt->icmp_seq) % MAX_DUP_CHK);  
  ntransmitted++;  
382    
383   gettimeofday((struct timeval *) &pkt->icmp_dun, NULL);   /* We don't do hton, because we will read it back on the same machine */
384   pkt->icmp_cksum = in_cksum((unsigned short *) pkt, sizeof(packet));   /*if (datalen >= 4)*/
385     *(uint32_t*)&pkt->icmp_dun = monotonic_us();
386    
387   i = sendto(pingsock, packet, sizeof(packet), 0,   pkt->icmp_cksum = in_cksum((unsigned short *) pkt, datalen + ICMP_MINLEN);
    (struct sockaddr *) &pingaddr, sizeof(struct sockaddr_in));  
388    
389   if (i < 0)   sendping_tail(sendping4, pkt, datalen + ICMP_MINLEN);
390   bb_perror_msg_and_die("sendto");  }
391   if ((size_t)i != sizeof(packet))  #if ENABLE_PING6
392   bb_error_msg_and_die("ping wrote %d chars; %d expected", i,  static void sendping6(int junk UNUSED_PARAM)
393     (int)sizeof(packet));  {
394     struct icmp6_hdr *pkt = alloca(datalen + sizeof(struct icmp6_hdr) + 4);
395    
396   signal(SIGALRM, sendping);   memset(pkt, 0, datalen + sizeof(struct icmp6_hdr) + 4);
397   if (pingcount == 0 || ntransmitted < pingcount) { /* schedule next in 1s */   pkt->icmp6_type = ICMP6_ECHO_REQUEST;
398   alarm(PINGINTERVAL);   /*pkt->icmp6_code = 0;*/
399   } else { /* done, wait for the last ping to come back */   /*pkt->icmp6_cksum = 0;*/
400   /* todo, don't necessarily need to wait so long... */   pkt->icmp6_seq = htons(ntransmitted); /* don't ++ here, it can be a macro */
401   signal(SIGALRM, pingstats);   pkt->icmp6_id = myid;
402   alarm(MAXWAIT);  
403   }   /*if (datalen >= 4)*/
404     *(uint32_t*)(&pkt->icmp6_data8[4]) = monotonic_us();
405    
406     sendping_tail(sendping6, pkt, datalen + sizeof(struct icmp6_hdr));
407  }  }
408    #endif
409    
410  static char *icmp_type_name(int id)  static const char *icmp_type_name(int id)
411  {  {
412   switch (id) {   switch (id) {
413   case ICMP_ECHOREPLY: return "Echo Reply";   case ICMP_ECHOREPLY:      return "Echo Reply";
414   case ICMP_DEST_UNREACH: return "Destination Unreachable";   case ICMP_DEST_UNREACH:   return "Destination Unreachable";
415   case ICMP_SOURCE_QUENCH: return "Source Quench";   case ICMP_SOURCE_QUENCH:  return "Source Quench";
416   case ICMP_REDIRECT: return "Redirect (change route)";   case ICMP_REDIRECT:       return "Redirect (change route)";
417   case ICMP_ECHO: return "Echo Request";   case ICMP_ECHO:           return "Echo Request";
418   case ICMP_TIME_EXCEEDED: return "Time Exceeded";   case ICMP_TIME_EXCEEDED:  return "Time Exceeded";
419   case ICMP_PARAMETERPROB: return "Parameter Problem";   case ICMP_PARAMETERPROB:  return "Parameter Problem";
420   case ICMP_TIMESTAMP: return "Timestamp Request";   case ICMP_TIMESTAMP:      return "Timestamp Request";
421   case ICMP_TIMESTAMPREPLY: return "Timestamp Reply";   case ICMP_TIMESTAMPREPLY: return "Timestamp Reply";
422   case ICMP_INFO_REQUEST: return "Information Request";   case ICMP_INFO_REQUEST:   return "Information Request";
423   case ICMP_INFO_REPLY: return "Information Reply";   case ICMP_INFO_REPLY:     return "Information Reply";
424   case ICMP_ADDRESS: return "Address Mask Request";   case ICMP_ADDRESS:        return "Address Mask Request";
425   case ICMP_ADDRESSREPLY: return "Address Mask Reply";   case ICMP_ADDRESSREPLY:   return "Address Mask Reply";
426   default: return "unknown ICMP type";   default:                  return "unknown ICMP type";
427     }
428    }
429    #if ENABLE_PING6
430    /* RFC3542 changed some definitions from RFC2292 for no good reason, whee!
431     * the newer 3542 uses a MLD_ prefix where as 2292 uses ICMP6_ prefix */
432    #ifndef MLD_LISTENER_QUERY
433    # define MLD_LISTENER_QUERY ICMP6_MEMBERSHIP_QUERY
434    #endif
435    #ifndef MLD_LISTENER_REPORT
436    # define MLD_LISTENER_REPORT ICMP6_MEMBERSHIP_REPORT
437    #endif
438    #ifndef MLD_LISTENER_REDUCTION
439    # define MLD_LISTENER_REDUCTION ICMP6_MEMBERSHIP_REDUCTION
440    #endif
441    static const char *icmp6_type_name(int id)
442    {
443     switch (id) {
444     case ICMP6_DST_UNREACH:      return "Destination Unreachable";
445     case ICMP6_PACKET_TOO_BIG:   return "Packet too big";
446     case ICMP6_TIME_EXCEEDED:    return "Time Exceeded";
447     case ICMP6_PARAM_PROB:       return "Parameter Problem";
448     case ICMP6_ECHO_REPLY:       return "Echo Reply";
449     case ICMP6_ECHO_REQUEST:     return "Echo Request";
450     case MLD_LISTENER_QUERY:     return "Listener Query";
451     case MLD_LISTENER_REPORT:    return "Listener Report";
452     case MLD_LISTENER_REDUCTION: return "Listener Reduction";
453     default:                     return "unknown ICMP type";
454     }
455    }
456    #endif
457    
458    static void unpack_tail(int sz, uint32_t *tp,
459     const char *from_str,
460     uint16_t recv_seq, int ttl)
461    {
462     const char *dupmsg = " (DUP!)";
463     unsigned triptime = triptime; /* for gcc */
464    
465     ++nreceived;
466    
467     if (tp) {
468     /* (int32_t) cast is for hypothetical 64-bit unsigned */
469     /* (doesn't hurt 32-bit real-world anyway) */
470     triptime = (int32_t) ((uint32_t)monotonic_us() - *tp);
471     tsum += triptime;
472     if (triptime < tmin)
473     tmin = triptime;
474     if (triptime > tmax)
475     tmax = triptime;
476   }   }
 }  
477    
478  static void unpack(char *buf, int sz, struct sockaddr_in *from)   if (TST(recv_seq % MAX_DUP_CHK)) {
479     ++nrepeats;
480     --nreceived;
481     } else {
482     SET(recv_seq % MAX_DUP_CHK);
483     dupmsg += 7;
484     }
485    
486     if (option_mask32 & OPT_QUIET)
487     return;
488    
489     printf("%d bytes from %s: seq=%u ttl=%d", sz,
490     from_str, recv_seq, ttl);
491     if (tp)
492     printf(" time=%u.%03u ms", triptime / 1000, triptime % 1000);
493     puts(dupmsg);
494     fflush(stdout);
495    }
496    static void unpack4(char *buf, int sz, struct sockaddr_in *from)
497  {  {
498   struct icmp *icmppkt;   struct icmp *icmppkt;
499   struct iphdr *iphdr;   struct iphdr *iphdr;
500   struct timeval tv, *tp;   int hlen;
  int hlen, dupflag;  
  unsigned long triptime;  
   
  gettimeofday(&tv, NULL);  
501    
502   /* discard if too short */   /* discard if too short */
503   if (sz < (datalen + ICMP_MINLEN))   if (sz < (datalen + ICMP_MINLEN))
# Line 270  static void unpack(char *buf, int sz, st Line 512  static void unpack(char *buf, int sz, st
512   return; /* not our ping */   return; /* not our ping */
513    
514   if (icmppkt->icmp_type == ICMP_ECHOREPLY) {   if (icmppkt->icmp_type == ICMP_ECHOREPLY) {
515   u_int16_t recv_seq = ntohs(icmppkt->icmp_seq);   uint16_t recv_seq = ntohs(icmppkt->icmp_seq);
516   ++nreceived;   uint32_t *tp = NULL;
  tp = (struct timeval *) icmppkt->icmp_data;  
   
  if ((tv.tv_usec -= tp->tv_usec) < 0) {  
  --tv.tv_sec;  
  tv.tv_usec += 1000000;  
  }  
  tv.tv_sec -= tp->tv_sec;  
517    
518   triptime = tv.tv_sec * 10000 + (tv.tv_usec / 100);   if (sz >= ICMP_MINLEN + sizeof(uint32_t))
519   tsum += triptime;   tp = (uint32_t *) icmppkt->icmp_data;
520   if (triptime < tmin)   unpack_tail(sz, tp,
521   tmin = triptime;   inet_ntoa(*(struct in_addr *) &from->sin_addr.s_addr),
522   if (triptime > tmax)   recv_seq, iphdr->ttl);
523   tmax = triptime;   } else if (icmppkt->icmp_type != ICMP_ECHO) {
524     bb_error_msg("warning: got ICMP %d (%s)",
525     icmppkt->icmp_type,
526     icmp_type_name(icmppkt->icmp_type));
527     }
528    }
529    #if ENABLE_PING6
530    static void unpack6(char *packet, int sz, /*struct sockaddr_in6 *from,*/ int hoplimit)
531    {
532     struct icmp6_hdr *icmppkt;
533     char buf[INET6_ADDRSTRLEN];
534    
535   if (TST(recv_seq % MAX_DUP_CHK)) {   /* discard if too short */
536   ++nrepeats;   if (sz < (datalen + sizeof(struct icmp6_hdr)))
537   --nreceived;   return;
  dupflag = 1;  
  } else {  
  SET(recv_seq % MAX_DUP_CHK);  
  dupflag = 0;  
  }  
538    
539   if (option_mask32 & OPT_QUIET)   icmppkt = (struct icmp6_hdr *) packet;
540   return;   if (icmppkt->icmp6_id != myid)
541     return; /* not our ping */
542    
543   printf("%d bytes from %s: icmp_seq=%u", sz,   if (icmppkt->icmp6_type == ICMP6_ECHO_REPLY) {
544     inet_ntoa(*(struct in_addr *) &from->sin_addr.s_addr),   uint16_t recv_seq = ntohs(icmppkt->icmp6_seq);
545     recv_seq);   uint32_t *tp = NULL;
546   printf(" ttl=%d", iphdr->ttl);  
547   printf(" time=%lu.%lu ms", triptime / 10, triptime % 10);   if (sz >= sizeof(struct icmp6_hdr) + sizeof(uint32_t))
548   if (dupflag)   tp = (uint32_t *) &icmppkt->icmp6_data8[4];
549   printf(" (DUP!)");   unpack_tail(sz, tp,
550   puts("");   inet_ntop(AF_INET6, &pingaddr.sin6.sin6_addr,
551   } else   buf, sizeof(buf)),
552   if (icmppkt->icmp_type != ICMP_ECHO)   recv_seq, hoplimit);
553   bb_error_msg("warning: got ICMP %d (%s)",   } else if (icmppkt->icmp6_type != ICMP6_ECHO_REQUEST) {
554   icmppkt->icmp_type,   bb_error_msg("warning: got ICMP %d (%s)",
555   icmp_type_name(icmppkt->icmp_type));   icmppkt->icmp6_type,
556   fflush(stdout);   icmp6_type_name(icmppkt->icmp6_type));
557     }
558  }  }
559    #endif
560    
561  static void ping(const char *host)  static void ping4(len_and_sockaddr *lsa)
562  {  {
563   char packet[datalen + MAXIPLEN + MAXICMPLEN];   char packet[datalen + MAXIPLEN + MAXICMPLEN];
564   int sockopt;   int sockopt;
565    
566   pingsock = create_icmp_socket();   pingsock = create_icmp_socket();
567     pingaddr.sin = lsa->u.sin;
568   if (sourceaddr.sin_addr.s_addr) {   if (source_lsa) {
569   xbind(pingsock, (struct sockaddr*)&sourceaddr, sizeof(sourceaddr));   if (setsockopt(pingsock, IPPROTO_IP, IP_MULTICAST_IF,
570     &source_lsa->u.sa, source_lsa->len))
571     bb_error_msg_and_die("can't set multicast source interface");
572     xbind(pingsock, &source_lsa->u.sa, source_lsa->len);
573   }   }
574     if (str_I)
575   memset(&pingaddr, 0, sizeof(struct sockaddr_in));   setsockopt_bindtodevice(pingsock, str_I);
   
  pingaddr.sin_family = AF_INET;  
  hostent = xgethostbyname(host);  
  if (hostent->h_addrtype != AF_INET)  
  bb_error_msg_and_die("unknown address type; only AF_INET is currently supported");  
   
  memcpy(&pingaddr.sin_addr, hostent->h_addr, sizeof(pingaddr.sin_addr));  
576    
577   /* enable broadcast pings */   /* enable broadcast pings */
578   setsockopt_broadcast(pingsock);   setsockopt_broadcast(pingsock);
579    
580   /* set recv buf for broadcast pings */   /* set recv buf (needed if we can get lots of responses: flood ping,
581   sockopt = 48 * 1024;   * broadcast ping etc) */
582   setsockopt(pingsock, SOL_SOCKET, SO_RCVBUF, (char *) &sockopt,   sockopt = (datalen * 2) + 7 * 1024; /* giving it a bit of extra room */
583     sizeof(sockopt));   setsockopt(pingsock, SOL_SOCKET, SO_RCVBUF, &sockopt, sizeof(sockopt));
   
  printf("PING %s (%s)",  
  hostent->h_name,  
  inet_ntoa(*(struct in_addr *) &pingaddr.sin_addr.s_addr));  
  if (sourceaddr.sin_addr.s_addr) {  
  printf(" from %s",  
  inet_ntoa(*(struct in_addr *) &sourceaddr.sin_addr.s_addr));  
  }  
  printf(": %d data bytes\n", datalen);  
584    
585   signal(SIGINT, pingstats);   signal(SIGINT, print_stats_and_exit);
586    
587   /* start the ping's going ... */   /* start the ping's going ... */
588   sendping(0);   sendping4(0);
589    
590   /* listen for replies */   /* listen for replies */
591   while (1) {   while (1) {
# Line 370  static void ping(const char *host) Line 600  static void ping(const char *host)
600   bb_perror_msg("recvfrom");   bb_perror_msg("recvfrom");
601   continue;   continue;
602   }   }
603   unpack(packet, c, &from);   unpack4(packet, c, &from);
604   if (pingcount > 0 && nreceived >= pingcount)   if (pingcount && nreceived >= pingcount)
605   break;   break;
606   }   }
  pingstats(0);  
607  }  }
608    #if ENABLE_PING6
609    extern int BUG_bad_offsetof_icmp6_cksum(void);
610    static void ping6(len_and_sockaddr *lsa)
611    {
612     char packet[datalen + MAXIPLEN + MAXICMPLEN];
613     int sockopt;
614     struct msghdr msg;
615     struct sockaddr_in6 from;
616     struct iovec iov;
617     char control_buf[CMSG_SPACE(36)];
618    
619     pingsock = create_icmp6_socket();
620     pingaddr.sin6 = lsa->u.sin6;
621     /* untested whether "-I addr" really works for IPv6: */
622     if (source_lsa)
623     xbind(pingsock, &source_lsa->u.sa, source_lsa->len);
624     if (str_I)
625     setsockopt_bindtodevice(pingsock, str_I);
626    
627    #ifdef ICMP6_FILTER
628     {
629     struct icmp6_filter filt;
630     if (!(option_mask32 & OPT_VERBOSE)) {
631     ICMP6_FILTER_SETBLOCKALL(&filt);
632     ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &filt);
633     } else {
634     ICMP6_FILTER_SETPASSALL(&filt);
635     }
636     if (setsockopt(pingsock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt,
637       sizeof(filt)) < 0)
638     bb_error_msg_and_die("setsockopt(ICMP6_FILTER)");
639     }
640    #endif /*ICMP6_FILTER*/
641    
642  /* TODO: consolidate ether-wake.c, dnsd.c, ifupdown.c, nslookup.c   /* enable broadcast pings */
643   * versions of below thing. BTW we have far too many "%u.%u.%u.%u" too...   setsockopt_broadcast(pingsock);
644  */  
645  static int parse_nipquad(const char *str, struct sockaddr_in* addr)   /* set recv buf (needed if we can get lots of responses: flood ping,
646  {   * broadcast ping etc) */
647   char dummy;   sockopt = (datalen * 2) + 7 * 1024; /* giving it a bit of extra room */
648   unsigned i1, i2, i3, i4;   setsockopt(pingsock, SOL_SOCKET, SO_RCVBUF, &sockopt, sizeof(sockopt));
649   if (sscanf(str, "%u.%u.%u.%u%c",  
650     &i1, &i2, &i3, &i4, &dummy) == 4   sockopt = offsetof(struct icmp6_hdr, icmp6_cksum);
651   && ( (i1|i2|i3|i4) <= 0xff )   if (offsetof(struct icmp6_hdr, icmp6_cksum) != 2)
652   ) {   BUG_bad_offsetof_icmp6_cksum();
653   uint8_t* ptr = (uint8_t*)&addr->sin_addr;   setsockopt(pingsock, SOL_RAW, IPV6_CHECKSUM, &sockopt, sizeof(sockopt));
654   ptr[0] = i1;  
655   ptr[1] = i2;   /* request ttl info to be returned in ancillary data */
656   ptr[2] = i3;   setsockopt(pingsock, SOL_IPV6, IPV6_HOPLIMIT, &const_int_1, sizeof(const_int_1));
657   ptr[3] = i4;  
658   return 0;   if (if_index)
659   }   pingaddr.sin6.sin6_scope_id = if_index;
660   return 1; /* error */  
661  }   signal(SIGINT, print_stats_and_exit);
662    
663  int ping_main(int argc, char **argv)   /* start the ping's going ... */
664  {   sendping6(0);
665   char *opt_c, *opt_s, *opt_I;  
666     /* listen for replies */
667   datalen = DEFDATALEN; /* initialized here rather than in global scope to work around gcc bug */   msg.msg_name = &from;
668     msg.msg_namelen = sizeof(from);
669   /* exactly one argument needed */   msg.msg_iov = &iov;
670   opt_complementary = "=1";   msg.msg_iovlen = 1;
671   getopt32(argc, argv, OPT_STRING, &opt_c, &opt_s, &opt_I);   msg.msg_control = control_buf;
672   if (option_mask32 & 2) pingcount = xatoul(opt_c); // -c   iov.iov_base = packet;
673   if (option_mask32 & 4) datalen = xatou16(opt_s); // -s   iov.iov_len = sizeof(packet);
674   if (option_mask32 & 8) { // -I   while (1) {
675  /* TODO: ping6 accepts iface too:   int c;
676   if_index = if_nametoindex(*argv);   struct cmsghdr *mp;
677   if (!if_index) ...   int hoplimit = -1;
678  make it true for ping. */   msg.msg_controllen = sizeof(control_buf);
679   if (parse_nipquad(opt_I, &sourceaddr))  
680   bb_show_usage();   c = recvmsg(pingsock, &msg, 0);
681     if (c < 0) {
682     if (errno != EINTR)
683     bb_perror_msg("recvfrom");
684     continue;
685     }
686     for (mp = CMSG_FIRSTHDR(&msg); mp; mp = CMSG_NXTHDR(&msg, mp)) {
687     if (mp->cmsg_level == SOL_IPV6
688     && mp->cmsg_type == IPV6_HOPLIMIT
689     /* don't check len - we trust the kernel: */
690     /* && mp->cmsg_len >= CMSG_LEN(sizeof(int)) */
691     ) {
692     hoplimit = *(int*)CMSG_DATA(mp);
693     }
694     }
695     unpack6(packet, c, /*&from,*/ hoplimit);
696     if (pingcount && nreceived >= pingcount)
697     break;
698   }   }
699    }
700    #endif
701    
702   myid = (int16_t) getpid();  static void ping(len_and_sockaddr *lsa)
703   ping(argv[optind]);  {
704   return EXIT_SUCCESS;   printf("PING %s (%s)", hostname, dotted);
705     if (source_lsa) {
706     printf(" from %s",
707     xmalloc_sockaddr2dotted_noport(&source_lsa->u.sa));
708     }
709     printf(": %d data bytes\n", datalen);
710    
711    #if ENABLE_PING6
712     if (lsa->u.sa.sa_family == AF_INET6)
713     ping6(lsa);
714     else
715    #endif
716     ping4(lsa);
717    }
718    
719    int ping_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
720    int ping_main(int argc UNUSED_PARAM, char **argv)
721    {
722     len_and_sockaddr *lsa;
723     char *str_s;
724     int opt;
725    
726     INIT_G();
727    
728     /* exactly one argument needed; -v and -q don't mix; -c NUM, -w NUM, -W NUM */
729     opt_complementary = "=1:q--v:v--q:c+:w+:W+";
730     opt = getopt32(argv, OPT_STRING, &pingcount, &str_s, &deadline, &timeout, &str_I);
731     if (opt & OPT_s)
732     datalen = xatou16(str_s); // -s
733     if (opt & OPT_I) { // -I
734     if_index = if_nametoindex(str_I);
735     if (!if_index) {
736     /* TODO: I'm not sure it takes IPv6 unless in [XX:XX..] format */
737     source_lsa = xdotted2sockaddr(str_I, 0);
738     str_I = NULL; /* don't try to bind to device later */
739     }
740     }
741     myid = (uint16_t) getpid();
742     hostname = argv[optind];
743    #if ENABLE_PING6
744     {
745     sa_family_t af = AF_UNSPEC;
746     if (opt & OPT_IPV4)
747     af = AF_INET;
748     if (opt & OPT_IPV6)
749     af = AF_INET6;
750     lsa = xhost_and_af2sockaddr(hostname, 0, af);
751     }
752    #else
753     lsa = xhost_and_af2sockaddr(hostname, 0, AF_INET);
754    #endif
755    
756     if (source_lsa && source_lsa->u.sa.sa_family != lsa->u.sa.sa_family)
757     /* leaking it here... */
758     source_lsa = NULL;
759    
760     dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
761     ping(lsa);
762     print_stats_and_exit(EXIT_SUCCESS);
763     /*return EXIT_SUCCESS;*/
764    }
765    #endif /* FEATURE_FANCY_PING */
766    
767    
768    #if ENABLE_PING6
769    int ping6_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
770    int ping6_main(int argc UNUSED_PARAM, char **argv)
771    {
772     argv[0] = (char*)"-6";
773     return ping_main(0 /* argc+1 - but it's unused anyway */,
774     argv - 1);
775  }  }
776  #endif /* ! CONFIG_FEATURE_FANCY_PING */  #endif
777    
778    /* from ping6.c:
779     * Copyright (c) 1989 The Regents of the University of California.
780     * All rights reserved.
781     *
782     * This code is derived from software contributed to Berkeley by
783     * Mike Muuss.
784     *
785     * Redistribution and use in source and binary forms, with or without
786     * modification, are permitted provided that the following conditions
787     * are met:
788     * 1. Redistributions of source code must retain the above copyright
789     *    notice, this list of conditions and the following disclaimer.
790     * 2. Redistributions in binary form must reproduce the above copyright
791     *    notice, this list of conditions and the following disclaimer in the
792     *    documentation and/or other materials provided with the distribution.
793     *
794     * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
795     * ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
796     *
797     * 4. Neither the name of the University nor the names of its contributors
798     *    may be used to endorse or promote products derived from this software
799     *    without specific prior written permission.
800     *
801     * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
802     * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
803     * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
804     * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
805     * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
806     * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
807     * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
808     * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
809     * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
810     * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
811     * SUCH DAMAGE.
812     */

Legend:
Removed from v.532  
changed lines
  Added in v.816