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