e94b7914ffdfdeccfd412b49b7b4f1293582b8e4
[oweals/busybox.git] / networking / ping.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Mini ping implementation for busybox
4  *
5  * Copyright (C) 1999 by Randolph Chung <tausq@debian.org>
6  *
7  * Adapted from the ping in netkit-base 0.10:
8  * Copyright (c) 1989 The Regents of the University of California.
9  * 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.
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>
30 #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 {
43         DEFDATALEN = 56,
44         MAXIPLEN = 60,
45         MAXICMPLEN = 76,
46         MAXPACKET = 65468,
47         MAX_DUP_CHK = (8 * 128),
48         MAXWAIT = 10,
49         PINGINTERVAL = 1, /* 1 second */
50 };
51
52 /* common routines */
53
54 static int in_cksum(unsigned short *buf, int sz)
55 {
56         int nleft = sz;
57         int sum = 0;
58         unsigned short *w = buf;
59         unsigned short ans = 0;
60
61         while (nleft > 1) {
62                 sum += *w++;
63                 nleft -= 2;
64         }
65
66         if (nleft == 1) {
67                 *(unsigned char *) (&ans) = *(unsigned char *) w;
68                 sum += ans;
69         }
70
71         sum = (sum >> 16) + (sum & 0xFFFF);
72         sum += (sum >> 16);
73         ans = ~sum;
74         return ans;
75 }
76
77 #if !ENABLE_FEATURE_FANCY_PING
78
79 /* simple version */
80
81 static char *hostname;
82
83 static void noresp(int ign ATTRIBUTE_UNUSED)
84 {
85         printf("No response from %s\n", hostname);
86         exit(EXIT_FAILURE);
87 }
88
89 static void ping4(len_and_sockaddr *lsa)
90 {
91         struct sockaddr_in pingaddr;
92         struct icmp *pkt;
93         int pingsock, c;
94         char packet[DEFDATALEN + MAXIPLEN + MAXICMPLEN];
95
96         pingsock = create_icmp_socket();
97         pingaddr = lsa->sin;
98
99         pkt = (struct icmp *) packet;
100         memset(pkt, 0, sizeof(packet));
101         pkt->icmp_type = ICMP_ECHO;
102         pkt->icmp_cksum = in_cksum((unsigned short *) pkt, sizeof(packet));
103
104         c = xsendto(pingsock, packet, DEFDATALEN + ICMP_MINLEN,
105                            (struct sockaddr *) &pingaddr, sizeof(pingaddr));
106
107         /* listen for replies */
108         while (1) {
109                 struct sockaddr_in from;
110                 socklen_t fromlen = sizeof(from);
111
112                 c = recvfrom(pingsock, packet, sizeof(packet), 0,
113                                 (struct sockaddr *) &from, &fromlen);
114                 if (c < 0) {
115                         if (errno != EINTR)
116                                 bb_perror_msg("recvfrom");
117                         continue;
118                 }
119                 if (c >= 76) {                  /* ip + icmp */
120                         struct iphdr *iphdr = (struct iphdr *) packet;
121
122                         pkt = (struct icmp *) (packet + (iphdr->ihl << 2));     /* skip ip hdr */
123                         if (pkt->icmp_type == ICMP_ECHOREPLY)
124                                 break;
125                 }
126         }
127         if (ENABLE_FEATURE_CLEAN_UP)
128                 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->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);
177 int ping_main(int argc, char **argv)
178 {
179         len_and_sockaddr *lsa;
180 #if ENABLE_PING6
181         sa_family_t af = AF_UNSPEC;
182         while (++argv[0][0] == '-') {
183                 if (argv[0][1] == '4') {
184                         af = AF_INET;
185                         continue;
186                 }
187                 if (argv[0][1] == '6') {
188                         af = AF_INET6;
189                         continue;
190                 }
191                 bb_show_usage();
192         }
193 #else
194         argv++;
195 #endif
196
197         hostname = *argv;
198         if (!hostname)
199                 bb_show_usage();
200
201 #if ENABLE_PING6
202         lsa = xhost_and_af2sockaddr(hostname, 0, af);
203 #else
204         lsa = xhost_and_af2sockaddr(hostname, 0, AF_INET);
205 #endif
206         /* Set timer _after_ DNS resolution */
207         signal(SIGALRM, noresp);
208         alarm(5); /* give the host 5000ms to respond */
209
210 #if ENABLE_PING6
211         if (lsa->sa.sa_family == AF_INET6)
212                 ping6(lsa);
213         else
214 #endif
215                 ping4(lsa);
216         printf("%s is alive!\n", hostname);
217         return EXIT_SUCCESS;
218 }
219
220
221 #else /* FEATURE_FANCY_PING */
222
223
224 /* full(er) version */
225
226 #define OPT_STRING ("qvc:s:I:4" USE_PING6("6"))
227 enum {
228         OPT_QUIET = 1 << 0,
229         OPT_VERBOSE = 1 << 1,
230         OPT_c = 1 << 2,
231         OPT_s = 1 << 3,
232         OPT_I = 1 << 4,
233         OPT_IPV4 = 1 << 5,
234         OPT_IPV6 = (1 << 6) * ENABLE_PING6,
235 };
236
237
238 struct globals {
239         int pingsock;
240         len_and_sockaddr *source_lsa;
241         unsigned datalen;
242         int if_index;
243         unsigned long ntransmitted, nreceived, nrepeats, pingcount;
244         uint16_t myid;
245         unsigned tmin, tmax;
246         unsigned long tsum;
247         const char *hostname;
248         const char *dotted;
249         union {
250                 struct sockaddr sa;
251                 struct sockaddr_in sin;
252 #if ENABLE_PING6
253                 struct sockaddr_in6 sin6;
254 #endif
255         } pingaddr;
256         char rcvd_tbl[MAX_DUP_CHK / 8];
257 };
258 #define G (*(struct globals*)&bb_common_bufsiz1)
259 #define pingsock     (G.pingsock    )
260 #define source_lsa   (G.source_lsa  )
261 #define datalen      (G.datalen     )
262 #define if_index     (G.if_index    )
263 #define ntransmitted (G.ntransmitted)
264 #define nreceived    (G.nreceived   )
265 #define nrepeats     (G.nrepeats    )
266 #define pingcount    (G.pingcount   )
267 #define myid         (G.myid        )
268 #define tmin         (G.tmin        )
269 #define tmax         (G.tmax        )
270 #define tsum         (G.tsum        )
271 #define hostname     (G.hostname    )
272 #define dotted       (G.dotted      )
273 #define pingaddr     (G.pingaddr    )
274 #define rcvd_tbl     (G.rcvd_tbl    )
275 void BUG_ping_globals_too_big(void);
276 #define INIT_G() do { \
277         if (sizeof(G) > COMMON_BUFSIZE) \
278                 BUG_ping_globals_too_big(); \
279         pingsock = -1; \
280         tmin = UINT_MAX; \
281 } while (0)
282
283
284 #define A(bit)          rcvd_tbl[(bit)>>3]      /* identify byte in array */
285 #define B(bit)          (1 << ((bit) & 0x07))   /* identify bit in byte */
286 #define SET(bit)        (A(bit) |= B(bit))
287 #define CLR(bit)        (A(bit) &= (~B(bit)))
288 #define TST(bit)        (A(bit) & B(bit))
289
290 /**************************************************************************/
291
292 static void pingstats(int junk ATTRIBUTE_UNUSED)
293 {
294         signal(SIGINT, SIG_IGN);
295
296         printf("\n--- %s ping statistics ---\n", hostname);
297         printf("%lu packets transmitted, ", ntransmitted);
298         printf("%lu packets received, ", nreceived);
299         if (nrepeats)
300                 printf("%lu duplicates, ", nrepeats);
301         if (ntransmitted)
302                 ntransmitted = (ntransmitted - nreceived) * 100 / ntransmitted;
303         printf("%lu%% packet loss\n", ntransmitted);
304         if (tmin != UINT_MAX)
305                 printf("round-trip min/avg/max = %u.%u/%lu.%lu/%u.%u ms\n",
306                         tmin / 10, tmin % 10,
307                         (tsum / (nreceived + nrepeats)) / 10,
308                         (tsum / (nreceived + nrepeats)) % 10, tmax / 10, tmax % 10);
309         exit(nreceived == 0); /* (nreceived == 0) is true (1) -- 'failure' */
310 }
311
312 static void sendping_tail(void (*sp)(int), const void *pkt, int size_pkt)
313 {
314         int sz;
315
316         CLR((uint16_t)ntransmitted % MAX_DUP_CHK);
317         ntransmitted++;
318
319         /* sizeof(pingaddr) can be larger than real sa size, but I think
320          * it doesn't matter */
321         sz = xsendto(pingsock, pkt, size_pkt, &pingaddr.sa, sizeof(pingaddr));
322         if (sz != size_pkt)
323                 bb_error_msg_and_die(bb_msg_write_error);
324
325         signal(SIGALRM, sp);
326         if (pingcount == 0 || ntransmitted < pingcount) { /* schedule next in 1s */
327                 alarm(PINGINTERVAL);
328         } else { /* done, wait for the last ping to come back */
329                 /* todo, don't necessarily need to wait so long... */
330                 signal(SIGALRM, pingstats);
331                 alarm(MAXWAIT);
332         }
333 }
334
335 static void sendping4(int junk ATTRIBUTE_UNUSED)
336 {
337         struct icmp *pkt = alloca(datalen + ICMP_MINLEN);
338
339         pkt->icmp_type = ICMP_ECHO;
340         pkt->icmp_code = 0;
341         pkt->icmp_cksum = 0;
342         pkt->icmp_seq = htons(ntransmitted); /* don't ++ here, it can be a macro */
343         pkt->icmp_id = myid;
344
345 // I can't fucking believe someone thought it's okay to do it like this...
346 // where's hton? Where is a provision for different word size, structure padding, etc??
347 // FIXME!
348         gettimeofday((struct timeval *) &pkt->icmp_dun, NULL);
349
350         pkt->icmp_cksum = in_cksum((unsigned short *) pkt, datalen + ICMP_MINLEN);
351
352         sendping_tail(sendping4, pkt, datalen + ICMP_MINLEN);
353 }
354 #if ENABLE_PING6
355 static void sendping6(int junk ATTRIBUTE_UNUSED)
356 {
357         struct icmp6_hdr *pkt = alloca(datalen + sizeof(struct icmp6_hdr));
358
359         pkt->icmp6_type = ICMP6_ECHO_REQUEST;
360         pkt->icmp6_code = 0;
361         pkt->icmp6_cksum = 0;
362         pkt->icmp6_seq = htons(ntransmitted); /* don't ++ here, it can be a macro */
363         pkt->icmp6_id = myid;
364
365 // FIXME!
366         gettimeofday((struct timeval *) &pkt->icmp6_data8[4], NULL);
367
368         sendping_tail(sendping6, pkt, datalen + sizeof(struct icmp6_hdr));
369 }
370 #endif
371
372 static const char *icmp_type_name(int id)
373 {
374         switch (id) {
375         case ICMP_ECHOREPLY:      return "Echo Reply";
376         case ICMP_DEST_UNREACH:   return "Destination Unreachable";
377         case ICMP_SOURCE_QUENCH:  return "Source Quench";
378         case ICMP_REDIRECT:       return "Redirect (change route)";
379         case ICMP_ECHO:           return "Echo Request";
380         case ICMP_TIME_EXCEEDED:  return "Time Exceeded";
381         case ICMP_PARAMETERPROB:  return "Parameter Problem";
382         case ICMP_TIMESTAMP:      return "Timestamp Request";
383         case ICMP_TIMESTAMPREPLY: return "Timestamp Reply";
384         case ICMP_INFO_REQUEST:   return "Information Request";
385         case ICMP_INFO_REPLY:     return "Information Reply";
386         case ICMP_ADDRESS:        return "Address Mask Request";
387         case ICMP_ADDRESSREPLY:   return "Address Mask Reply";
388         default:                  return "unknown ICMP type";
389         }
390 }
391 #if ENABLE_PING6
392 /* RFC3542 changed some definitions from RFC2292 for no good reason, whee!
393  * the newer 3542 uses a MLD_ prefix where as 2292 uses ICMP6_ prefix */
394 #ifndef MLD_LISTENER_QUERY
395 # define MLD_LISTENER_QUERY ICMP6_MEMBERSHIP_QUERY
396 #endif
397 #ifndef MLD_LISTENER_REPORT
398 # define MLD_LISTENER_REPORT ICMP6_MEMBERSHIP_REPORT
399 #endif
400 #ifndef MLD_LISTENER_REDUCTION
401 # define MLD_LISTENER_REDUCTION ICMP6_MEMBERSHIP_REDUCTION
402 #endif
403 static const char *icmp6_type_name(int id)
404 {
405         switch (id) {
406         case ICMP6_DST_UNREACH:      return "Destination Unreachable";
407         case ICMP6_PACKET_TOO_BIG:   return "Packet too big";
408         case ICMP6_TIME_EXCEEDED:    return "Time Exceeded";
409         case ICMP6_PARAM_PROB:       return "Parameter Problem";
410         case ICMP6_ECHO_REPLY:       return "Echo Reply";
411         case ICMP6_ECHO_REQUEST:     return "Echo Request";
412         case MLD_LISTENER_QUERY:     return "Listener Query";
413         case MLD_LISTENER_REPORT:    return "Listener Report";
414         case MLD_LISTENER_REDUCTION: return "Listener Reduction";
415         default:                     return "unknown ICMP type";
416         }
417 }
418 #endif
419
420 static void unpack_tail(int sz, struct timeval *tp,
421                 const char *from_str,
422                 uint16_t recv_seq, int ttl)
423 {
424         const char *dupmsg = " (DUP!)";
425         unsigned triptime = triptime; /* for gcc */
426
427         ++nreceived;
428
429         if (tp) {
430                 struct timeval tv;
431
432                 gettimeofday(&tv, NULL);
433                 tv.tv_usec -= tp->tv_usec;
434                 if (tv.tv_usec < 0) {
435                         --tv.tv_sec;
436                         tv.tv_usec += 1000000;
437                 }
438                 tv.tv_sec -= tp->tv_sec;
439
440                 triptime = tv.tv_sec * 10000 + (tv.tv_usec / 100);
441                 tsum += triptime;
442                 if (triptime < tmin)
443                         tmin = triptime;
444                 if (triptime > tmax)
445                         tmax = triptime;
446         }
447
448         if (TST(recv_seq % MAX_DUP_CHK)) {
449                 ++nrepeats;
450                 --nreceived;
451         } else {
452                 SET(recv_seq % MAX_DUP_CHK);
453                 dupmsg += 7;
454         }
455
456         if (option_mask32 & OPT_QUIET)
457                 return;
458
459         printf("%d bytes from %s: seq=%u ttl=%d", sz,
460                 from_str, recv_seq, ttl);
461         if (tp)
462                 printf(" time=%u.%u ms", triptime / 10, triptime % 10);
463         puts(dupmsg);
464         fflush(stdout);
465 }
466 static void unpack4(char *buf, int sz, struct sockaddr_in *from)
467 {
468         struct icmp *icmppkt;
469         struct iphdr *iphdr;
470         int hlen;
471
472         /* discard if too short */
473         if (sz < (datalen + ICMP_MINLEN))
474                 return;
475
476         /* check IP header */
477         iphdr = (struct iphdr *) buf;
478         hlen = iphdr->ihl << 2;
479         sz -= hlen;
480         icmppkt = (struct icmp *) (buf + hlen);
481         if (icmppkt->icmp_id != myid)
482                 return;                         /* not our ping */
483
484         if (icmppkt->icmp_type == ICMP_ECHOREPLY) {
485                 uint16_t recv_seq = ntohs(icmppkt->icmp_seq);
486                 struct timeval *tp = NULL;
487
488                 if (sz >= ICMP_MINLEN + sizeof(struct timeval))
489                         tp = (struct timeval *) icmppkt->icmp_data;
490                 unpack_tail(sz, tp,
491                         inet_ntoa(*(struct in_addr *) &from->sin_addr.s_addr),
492                         recv_seq, iphdr->ttl);
493         } else if (icmppkt->icmp_type != ICMP_ECHO) {
494                 bb_error_msg("warning: got ICMP %d (%s)",
495                                 icmppkt->icmp_type,
496                                 icmp_type_name(icmppkt->icmp_type));
497         }
498 }
499 #if ENABLE_PING6
500 static void unpack6(char *packet, int sz, struct sockaddr_in6 *from, int hoplimit)
501 {
502         struct icmp6_hdr *icmppkt;
503         char buf[INET6_ADDRSTRLEN];
504
505         /* discard if too short */
506         if (sz < (datalen + sizeof(struct icmp6_hdr)))
507                 return;
508
509         icmppkt = (struct icmp6_hdr *) packet;
510         if (icmppkt->icmp6_id != myid)
511                 return;                         /* not our ping */
512
513         if (icmppkt->icmp6_type == ICMP6_ECHO_REPLY) {
514                 uint16_t recv_seq = ntohs(icmppkt->icmp6_seq);
515                 struct timeval *tp = NULL;
516
517                 if (sz >= sizeof(struct icmp6_hdr) + sizeof(struct timeval))
518                         tp = (struct timeval *) &icmppkt->icmp6_data8[4];
519                 unpack_tail(sz, tp,
520                         inet_ntop(AF_INET6, &pingaddr.sin6.sin6_addr,
521                                         buf, sizeof(buf)),
522                         recv_seq, hoplimit);
523         } else if (icmppkt->icmp6_type != ICMP6_ECHO_REQUEST) {
524                 bb_error_msg("warning: got ICMP %d (%s)",
525                                 icmppkt->icmp6_type,
526                                 icmp6_type_name(icmppkt->icmp6_type));
527         }
528 }
529 #endif
530
531 static void ping4(len_and_sockaddr *lsa)
532 {
533         char packet[datalen + MAXIPLEN + MAXICMPLEN];
534         int sockopt;
535
536         pingsock = create_icmp_socket();
537         pingaddr.sin = lsa->sin;
538         if (source_lsa)
539                 xbind(pingsock, &lsa->sa, lsa->len);
540
541         /* enable broadcast pings */
542         setsockopt_broadcast(pingsock);
543
544         /* set recv buf for broadcast pings */
545         sockopt = 48 * 1024; /* explain why 48k? */
546         setsockopt(pingsock, SOL_SOCKET, SO_RCVBUF, &sockopt, sizeof(sockopt));
547
548         signal(SIGINT, pingstats);
549
550         /* start the ping's going ... */
551         sendping4(0);
552
553         /* listen for replies */
554         while (1) {
555                 struct sockaddr_in from;
556                 socklen_t fromlen = (socklen_t) sizeof(from);
557                 int c;
558
559                 c = recvfrom(pingsock, packet, sizeof(packet), 0,
560                                 (struct sockaddr *) &from, &fromlen);
561                 if (c < 0) {
562                         if (errno != EINTR)
563                                 bb_perror_msg("recvfrom");
564                         continue;
565                 }
566                 unpack4(packet, c, &from);
567                 if (pingcount > 0 && nreceived >= pingcount)
568                         break;
569         }
570 }
571 #if ENABLE_PING6
572 extern int BUG_bad_offsetof_icmp6_cksum(void);
573 static void ping6(len_and_sockaddr *lsa)
574 {
575         char packet[datalen + MAXIPLEN + MAXICMPLEN];
576         int sockopt;
577         struct msghdr msg;
578         struct sockaddr_in6 from;
579         struct iovec iov;
580         char control_buf[CMSG_SPACE(36)];
581
582         pingsock = create_icmp6_socket();
583         pingaddr.sin6 = lsa->sin6;
584         /* untested whether "-I addr" really works for IPv6: */
585         if (source_lsa)
586                 xbind(pingsock, &lsa->sa, lsa->len);
587
588 #ifdef ICMP6_FILTER
589         {
590                 struct icmp6_filter filt;
591                 if (!(option_mask32 & OPT_VERBOSE)) {
592                         ICMP6_FILTER_SETBLOCKALL(&filt);
593                         ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &filt);
594                 } else {
595                         ICMP6_FILTER_SETPASSALL(&filt);
596                 }
597                 if (setsockopt(pingsock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt,
598                                            sizeof(filt)) < 0)
599                         bb_error_msg_and_die("setsockopt(ICMP6_FILTER)");
600         }
601 #endif /*ICMP6_FILTER*/
602
603         /* enable broadcast pings */
604         setsockopt_broadcast(pingsock);
605
606         /* set recv buf for broadcast pings */
607         sockopt = 48 * 1024; /* explain why 48k? */
608         setsockopt(pingsock, SOL_SOCKET, SO_RCVBUF, &sockopt, sizeof(sockopt));
609
610         sockopt = offsetof(struct icmp6_hdr, icmp6_cksum);
611         if (offsetof(struct icmp6_hdr, icmp6_cksum) != 2)
612                 BUG_bad_offsetof_icmp6_cksum();
613         setsockopt(pingsock, SOL_RAW, IPV6_CHECKSUM, &sockopt, sizeof(sockopt));
614
615         /* request ttl info to be returned in ancillary data */
616         setsockopt(pingsock, SOL_IPV6, IPV6_HOPLIMIT, &const_int_1, sizeof(const_int_1));
617
618         if (if_index)
619                 pingaddr.sin6.sin6_scope_id = if_index;
620
621         signal(SIGINT, pingstats);
622
623         /* start the ping's going ... */
624         sendping6(0);
625
626         /* listen for replies */
627         msg.msg_name = &from;
628         msg.msg_namelen = sizeof(from);
629         msg.msg_iov = &iov;
630         msg.msg_iovlen = 1;
631         msg.msg_control = control_buf;
632         iov.iov_base = packet;
633         iov.iov_len = sizeof(packet);
634         while (1) {
635                 int c;
636                 struct cmsghdr *mp;
637                 int hoplimit = -1;
638                 msg.msg_controllen = sizeof(control_buf);
639
640                 c = recvmsg(pingsock, &msg, 0);
641                 if (c < 0) {
642                         if (errno != EINTR)
643                                 bb_perror_msg("recvfrom");
644                         continue;
645                 }
646                 for (mp = CMSG_FIRSTHDR(&msg); mp; mp = CMSG_NXTHDR(&msg, mp)) {
647                         if (mp->cmsg_level == SOL_IPV6
648                          && mp->cmsg_type == IPV6_HOPLIMIT
649                          /* don't check len - we trust the kernel: */
650                          /* && mp->cmsg_len >= CMSG_LEN(sizeof(int)) */
651                         ) {
652                                 hoplimit = *(int*)CMSG_DATA(mp);
653                         }
654                 }
655                 unpack6(packet, c, &from, hoplimit);
656                 if (pingcount > 0 && nreceived >= pingcount)
657                         break;
658         }
659 }
660 #endif
661
662 static void ping(len_and_sockaddr *lsa)
663 {
664         printf("PING %s (%s)", hostname, dotted);
665         if (source_lsa) {
666                 printf(" from %s",
667                         xmalloc_sockaddr2dotted_noport(&lsa->sa, lsa->len));
668         }
669         printf(": %d data bytes\n", datalen);
670
671 #if ENABLE_PING6
672         if (lsa->sa.sa_family == AF_INET6)
673                 ping6(lsa);
674         else
675 #endif
676                 ping4(lsa);
677 }
678
679 int ping_main(int argc, char **argv);
680 int ping_main(int argc, char **argv)
681 {
682         len_and_sockaddr *lsa;
683         char *opt_c, *opt_s, *opt_I;
684         USE_PING6(sa_family_t af = AF_UNSPEC;)
685
686         INIT_G();
687
688         datalen = DEFDATALEN; /* initialized here rather than in global scope to work around gcc bug */
689
690         /* exactly one argument needed, -v and -q don't mix */
691         opt_complementary = "=1:q--v:v--q";
692         getopt32(argc, argv, OPT_STRING, &opt_c, &opt_s, &opt_I);
693         if (option_mask32 & OPT_c) pingcount = xatoul(opt_c); // -c
694         if (option_mask32 & OPT_s) datalen = xatou16(opt_s); // -s
695         if (option_mask32 & OPT_I) { // -I
696                 if_index = if_nametoindex(opt_I);
697                 if (!if_index) {
698                         /* TODO: I'm not sure it takes IPv6 unless in [XX:XX..] format */
699                         /* (ping doesn't support source IPv6 addresses yet anyway) */
700                         source_lsa = xdotted2sockaddr(opt_I, 0);
701                 }
702         }
703         myid = (uint16_t) getpid();
704         hostname = argv[optind];
705 #if ENABLE_PING6
706         if (option_mask32 & OPT_IPV4)
707                 af = AF_INET;
708         if (option_mask32 & OPT_IPV6)
709                 af = AF_INET6;
710         lsa = xhost_and_af2sockaddr(hostname, 0, af);
711 #else
712         lsa = xhost_and_af2sockaddr(hostname, 0, AF_INET);
713 #endif
714
715         if (source_lsa && source_lsa->sa.sa_family != lsa->sa.sa_family)
716                 /* leaking it here... */
717                 source_lsa = NULL;
718
719         dotted = xmalloc_sockaddr2dotted_noport(&lsa->sa, lsa->len);
720         ping(lsa);
721         pingstats(0);
722         return EXIT_SUCCESS;
723 }
724 #endif /* FEATURE_FANCY_PING */
725
726
727 #if ENABLE_PING6
728 int ping6_main(int argc, char **argv);
729 int ping6_main(int argc, char **argv)
730 {
731         argv[0] = (char*)"-6";
732         return ping_main(argc + 1, argv - 1);
733 }
734 #endif
735
736 /* from ping6.c:
737  * Copyright (c) 1989 The Regents of the University of California.
738  * All rights reserved.
739  *
740  * This code is derived from software contributed to Berkeley by
741  * Mike Muuss.
742  *
743  * Redistribution and use in source and binary forms, with or without
744  * modification, are permitted provided that the following conditions
745  * are met:
746  * 1. Redistributions of source code must retain the above copyright
747  *    notice, this list of conditions and the following disclaimer.
748  * 2. Redistributions in binary form must reproduce the above copyright
749  *    notice, this list of conditions and the following disclaimer in the
750  *    documentation and/or other materials provided with the distribution.
751  *
752  * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
753  *              ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
754  *
755  * 4. Neither the name of the University nor the names of its contributors
756  *    may be used to endorse or promote products derived from this software
757  *    without specific prior written permission.
758  *
759  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
760  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
761  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
762  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
763  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
764  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
765  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
766  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
767  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
768  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
769  * SUCH DAMAGE.
770  */