introduce and use xdup2(int, int)
[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
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++;
196 #endif
197
198         hostname = *argv;
199         if (!hostname)
200                 bb_show_usage();
201
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->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;
219 }
220
221
222 #else /* FEATURE_FANCY_PING */
223
224
225 /* full(er) version */
226
227 #define OPT_STRING ("qvc:s:I:4" USE_PING6("6"))
228 enum {
229         OPT_QUIET = 1 << 0,
230         OPT_VERBOSE = 1 << 1,
231         OPT_c = 1 << 2,
232         OPT_s = 1 << 3,
233         OPT_I = 1 << 4,
234         OPT_IPV4 = 1 << 5,
235         OPT_IPV6 = (1 << 6) * ENABLE_PING6,
236 };
237
238
239 struct globals {
240         int pingsock;
241         len_and_sockaddr *source_lsa;
242         unsigned datalen;
243         int if_index;
244         unsigned long ntransmitted, nreceived, nrepeats, pingcount;
245         uint16_t myid;
246         unsigned tmin, tmax; /* in us */
247         unsigned long long tsum; /* in us, sum of all times */
248         const char *hostname;
249         const char *dotted;
250         union {
251                 struct sockaddr sa;
252                 struct sockaddr_in sin;
253 #if ENABLE_PING6
254                 struct sockaddr_in6 sin6;
255 #endif
256         } pingaddr;
257         char rcvd_tbl[MAX_DUP_CHK / 8];
258 };
259 #define G (*(struct globals*)&bb_common_bufsiz1)
260 #define pingsock     (G.pingsock    )
261 #define source_lsa   (G.source_lsa  )
262 #define datalen      (G.datalen     )
263 #define if_index     (G.if_index    )
264 #define ntransmitted (G.ntransmitted)
265 #define nreceived    (G.nreceived   )
266 #define nrepeats     (G.nrepeats    )
267 #define pingcount    (G.pingcount   )
268 #define myid         (G.myid        )
269 #define tmin         (G.tmin        )
270 #define tmax         (G.tmax        )
271 #define tsum         (G.tsum        )
272 #define hostname     (G.hostname    )
273 #define dotted       (G.dotted      )
274 #define pingaddr     (G.pingaddr    )
275 #define rcvd_tbl     (G.rcvd_tbl    )
276 void BUG_ping_globals_too_big(void);
277 #define INIT_G() do { \
278         if (sizeof(G) > COMMON_BUFSIZE) \
279                 BUG_ping_globals_too_big(); \
280         pingsock = -1; \
281         tmin = UINT_MAX; \
282 } while (0)
283
284
285 #define A(bit)          rcvd_tbl[(bit)>>3]      /* identify byte in array */
286 #define B(bit)          (1 << ((bit) & 0x07))   /* identify bit in byte */
287 #define SET(bit)        (A(bit) |= B(bit))
288 #define CLR(bit)        (A(bit) &= (~B(bit)))
289 #define TST(bit)        (A(bit) & B(bit))
290
291 /**************************************************************************/
292
293 static void pingstats(int junk ATTRIBUTE_UNUSED)
294 {
295         signal(SIGINT, SIG_IGN);
296
297         printf("\n--- %s ping statistics ---\n", hostname);
298         printf("%lu packets transmitted, ", ntransmitted);
299         printf("%lu packets received, ", nreceived);
300         if (nrepeats)
301                 printf("%lu duplicates, ", nrepeats);
302         if (ntransmitted)
303                 ntransmitted = (ntransmitted - nreceived) * 100 / ntransmitted;
304         printf("%lu%% packet loss\n", ntransmitted);
305         if (tmin != UINT_MAX) {
306                 unsigned tavg = tsum / (nreceived + nrepeats);
307                 printf("round-trip min/avg/max = %u.%03u/%u.%03u/%u.%03u ms\n",
308                         tmin / 1000, tmin % 1000,
309                         tavg / 1000, tavg % 1000,
310                         tmax / 1000, tmax % 1000);
311         }
312         exit(nreceived == 0); /* (nreceived == 0) is true (1) -- 'failure' */
313 }
314
315 static void sendping_tail(void (*sp)(int), const void *pkt, int size_pkt)
316 {
317         int sz;
318
319         CLR((uint16_t)ntransmitted % MAX_DUP_CHK);
320         ntransmitted++;
321
322         /* sizeof(pingaddr) can be larger than real sa size, but I think
323          * it doesn't matter */
324         sz = xsendto(pingsock, pkt, size_pkt, &pingaddr.sa, sizeof(pingaddr));
325         if (sz != size_pkt)
326                 bb_error_msg_and_die(bb_msg_write_error);
327
328         signal(SIGALRM, sp);
329         if (pingcount == 0 || ntransmitted < pingcount) { /* schedule next in 1s */
330                 alarm(PINGINTERVAL);
331         } else { /* done, wait for the last ping to come back */
332                 /* todo, don't necessarily need to wait so long... */
333                 signal(SIGALRM, pingstats);
334                 alarm(MAXWAIT);
335         }
336 }
337
338 static void sendping4(int junk ATTRIBUTE_UNUSED)
339 {
340         /* +4 reserves a place for timestamp, which may end up sitting
341          * *after* packet. Saves one if() */
342         struct icmp *pkt = alloca(datalen + ICMP_MINLEN + 4);
343
344         pkt->icmp_type = ICMP_ECHO;
345         pkt->icmp_code = 0;
346         pkt->icmp_cksum = 0;
347         pkt->icmp_seq = htons(ntransmitted); /* don't ++ here, it can be a macro */
348         pkt->icmp_id = myid;
349
350         /* We don't do hton, because we will read it back on the same machine */
351         /*if (datalen >= 4)*/
352                 *(uint32_t*)&pkt->icmp_dun = monotonic_us();
353
354         pkt->icmp_cksum = in_cksum((unsigned short *) pkt, datalen + ICMP_MINLEN);
355
356         sendping_tail(sendping4, pkt, datalen + ICMP_MINLEN);
357 }
358 #if ENABLE_PING6
359 static void sendping6(int junk ATTRIBUTE_UNUSED)
360 {
361         struct icmp6_hdr *pkt = alloca(datalen + sizeof(struct icmp6_hdr) + 4);
362
363         pkt->icmp6_type = ICMP6_ECHO_REQUEST;
364         pkt->icmp6_code = 0;
365         pkt->icmp6_cksum = 0;
366         pkt->icmp6_seq = htons(ntransmitted); /* don't ++ here, it can be a macro */
367         pkt->icmp6_id = myid;
368
369         /*if (datalen >= 4)*/
370                 *(uint32_t*)(&pkt->icmp6_data8[4]) = monotonic_us();
371
372         sendping_tail(sendping6, pkt, datalen + sizeof(struct icmp6_hdr));
373 }
374 #endif
375
376 static const char *icmp_type_name(int id)
377 {
378         switch (id) {
379         case ICMP_ECHOREPLY:      return "Echo Reply";
380         case ICMP_DEST_UNREACH:   return "Destination Unreachable";
381         case ICMP_SOURCE_QUENCH:  return "Source Quench";
382         case ICMP_REDIRECT:       return "Redirect (change route)";
383         case ICMP_ECHO:           return "Echo Request";
384         case ICMP_TIME_EXCEEDED:  return "Time Exceeded";
385         case ICMP_PARAMETERPROB:  return "Parameter Problem";
386         case ICMP_TIMESTAMP:      return "Timestamp Request";
387         case ICMP_TIMESTAMPREPLY: return "Timestamp Reply";
388         case ICMP_INFO_REQUEST:   return "Information Request";
389         case ICMP_INFO_REPLY:     return "Information Reply";
390         case ICMP_ADDRESS:        return "Address Mask Request";
391         case ICMP_ADDRESSREPLY:   return "Address Mask Reply";
392         default:                  return "unknown ICMP type";
393         }
394 }
395 #if ENABLE_PING6
396 /* RFC3542 changed some definitions from RFC2292 for no good reason, whee!
397  * the newer 3542 uses a MLD_ prefix where as 2292 uses ICMP6_ prefix */
398 #ifndef MLD_LISTENER_QUERY
399 # define MLD_LISTENER_QUERY ICMP6_MEMBERSHIP_QUERY
400 #endif
401 #ifndef MLD_LISTENER_REPORT
402 # define MLD_LISTENER_REPORT ICMP6_MEMBERSHIP_REPORT
403 #endif
404 #ifndef MLD_LISTENER_REDUCTION
405 # define MLD_LISTENER_REDUCTION ICMP6_MEMBERSHIP_REDUCTION
406 #endif
407 static const char *icmp6_type_name(int id)
408 {
409         switch (id) {
410         case ICMP6_DST_UNREACH:      return "Destination Unreachable";
411         case ICMP6_PACKET_TOO_BIG:   return "Packet too big";
412         case ICMP6_TIME_EXCEEDED:    return "Time Exceeded";
413         case ICMP6_PARAM_PROB:       return "Parameter Problem";
414         case ICMP6_ECHO_REPLY:       return "Echo Reply";
415         case ICMP6_ECHO_REQUEST:     return "Echo Request";
416         case MLD_LISTENER_QUERY:     return "Listener Query";
417         case MLD_LISTENER_REPORT:    return "Listener Report";
418         case MLD_LISTENER_REDUCTION: return "Listener Reduction";
419         default:                     return "unknown ICMP type";
420         }
421 }
422 #endif
423
424 static void unpack_tail(int sz, uint32_t *tp,
425                 const char *from_str,
426                 uint16_t recv_seq, int ttl)
427 {
428         const char *dupmsg = " (DUP!)";
429         unsigned triptime = triptime; /* for gcc */
430
431         ++nreceived;
432
433         if (tp) {
434                 /* (int32_t) cast is for hypothetical 64-bit unsigned */
435                 /* (doesn't hurt 32-bit real-world anyway) */
436                 triptime = (int32_t) ((uint32_t)monotonic_us() - *tp);
437                 tsum += triptime;
438                 if (triptime < tmin)
439                         tmin = triptime;
440                 if (triptime > tmax)
441                         tmax = triptime;
442         }
443
444         if (TST(recv_seq % MAX_DUP_CHK)) {
445                 ++nrepeats;
446                 --nreceived;
447         } else {
448                 SET(recv_seq % MAX_DUP_CHK);
449                 dupmsg += 7;
450         }
451
452         if (option_mask32 & OPT_QUIET)
453                 return;
454
455         printf("%d bytes from %s: seq=%u ttl=%d", sz,
456                 from_str, recv_seq, ttl);
457         if (tp)
458                 printf(" time=%u.%03u ms", triptime / 1000, triptime % 1000);
459         puts(dupmsg);
460         fflush(stdout);
461 }
462 static void unpack4(char *buf, int sz, struct sockaddr_in *from)
463 {
464         struct icmp *icmppkt;
465         struct iphdr *iphdr;
466         int hlen;
467
468         /* discard if too short */
469         if (sz < (datalen + ICMP_MINLEN))
470                 return;
471
472         /* check IP header */
473         iphdr = (struct iphdr *) buf;
474         hlen = iphdr->ihl << 2;
475         sz -= hlen;
476         icmppkt = (struct icmp *) (buf + hlen);
477         if (icmppkt->icmp_id != myid)
478                 return;                         /* not our ping */
479
480         if (icmppkt->icmp_type == ICMP_ECHOREPLY) {
481                 uint16_t recv_seq = ntohs(icmppkt->icmp_seq);
482                 uint32_t *tp = NULL;
483
484                 if (sz >= ICMP_MINLEN + sizeof(uint32_t))
485                         tp = (uint32_t *) icmppkt->icmp_data;
486                 unpack_tail(sz, tp,
487                         inet_ntoa(*(struct in_addr *) &from->sin_addr.s_addr),
488                         recv_seq, iphdr->ttl);
489         } else if (icmppkt->icmp_type != ICMP_ECHO) {
490                 bb_error_msg("warning: got ICMP %d (%s)",
491                                 icmppkt->icmp_type,
492                                 icmp_type_name(icmppkt->icmp_type));
493         }
494 }
495 #if ENABLE_PING6
496 static void unpack6(char *packet, int sz, struct sockaddr_in6 *from, int hoplimit)
497 {
498         struct icmp6_hdr *icmppkt;
499         char buf[INET6_ADDRSTRLEN];
500
501         /* discard if too short */
502         if (sz < (datalen + sizeof(struct icmp6_hdr)))
503                 return;
504
505         icmppkt = (struct icmp6_hdr *) packet;
506         if (icmppkt->icmp6_id != myid)
507                 return;                         /* not our ping */
508
509         if (icmppkt->icmp6_type == ICMP6_ECHO_REPLY) {
510                 uint16_t recv_seq = ntohs(icmppkt->icmp6_seq);
511                 uint32_t *tp = NULL;
512
513                 if (sz >= sizeof(struct icmp6_hdr) + sizeof(uint32_t))
514                         tp = (uint32_t *) &icmppkt->icmp6_data8[4];
515                 unpack_tail(sz, tp,
516                         inet_ntop(AF_INET6, &pingaddr.sin6.sin6_addr,
517                                         buf, sizeof(buf)),
518                         recv_seq, hoplimit);
519         } else if (icmppkt->icmp6_type != ICMP6_ECHO_REQUEST) {
520                 bb_error_msg("warning: got ICMP %d (%s)",
521                                 icmppkt->icmp6_type,
522                                 icmp6_type_name(icmppkt->icmp6_type));
523         }
524 }
525 #endif
526
527 static void ping4(len_and_sockaddr *lsa)
528 {
529         char packet[datalen + MAXIPLEN + MAXICMPLEN];
530         int sockopt;
531
532         pingsock = create_icmp_socket();
533         pingaddr.sin = lsa->sin;
534         if (source_lsa) {
535                 if (setsockopt(pingsock, IPPROTO_IP, IP_MULTICAST_IF,
536                                 &source_lsa->sa, source_lsa->len))
537                         bb_error_msg_and_die("can't set multicast source interface");
538                 xbind(pingsock, &source_lsa->sa, source_lsa->len);
539         }
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, &source_lsa->sa, source_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(&source_lsa->sa));
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                         source_lsa = xdotted2sockaddr(opt_I, 0);
700                 }
701         }
702         myid = (uint16_t) getpid();
703         hostname = argv[optind];
704 #if ENABLE_PING6
705         if (option_mask32 & OPT_IPV4)
706                 af = AF_INET;
707         if (option_mask32 & OPT_IPV6)
708                 af = AF_INET6;
709         lsa = xhost_and_af2sockaddr(hostname, 0, af);
710 #else
711         lsa = xhost_and_af2sockaddr(hostname, 0, AF_INET);
712 #endif
713
714         if (source_lsa && source_lsa->sa.sa_family != lsa->sa.sa_family)
715                 /* leaking it here... */
716                 source_lsa = NULL;
717
718         dotted = xmalloc_sockaddr2dotted_noport(&lsa->sa);
719         ping(lsa);
720         pingstats(0);
721         return EXIT_SUCCESS;
722 }
723 #endif /* FEATURE_FANCY_PING */
724
725
726 #if ENABLE_PING6
727 int ping6_main(int argc, char **argv);
728 int ping6_main(int argc, char **argv)
729 {
730         argv[0] = (char*)"-6";
731         return ping_main(argc + 1, argv - 1);
732 }
733 #endif
734
735 /* from ping6.c:
736  * Copyright (c) 1989 The Regents of the University of California.
737  * All rights reserved.
738  *
739  * This code is derived from software contributed to Berkeley by
740  * Mike Muuss.
741  *
742  * Redistribution and use in source and binary forms, with or without
743  * modification, are permitted provided that the following conditions
744  * are met:
745  * 1. Redistributions of source code must retain the above copyright
746  *    notice, this list of conditions and the following disclaimer.
747  * 2. Redistributions in binary form must reproduce the above copyright
748  *    notice, this list of conditions and the following disclaimer in the
749  *    documentation and/or other materials provided with the distribution.
750  *
751  * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
752  *              ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
753  *
754  * 4. Neither the name of the University nor the names of its contributors
755  *    may be used to endorse or promote products derived from this software
756  *    without specific prior written permission.
757  *
758  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
759  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
760  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
761  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
762  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
763  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
764  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
765  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
766  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
767  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
768  * SUCH DAMAGE.
769  */