add x to IPv6 functions which can die
[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 struct sockaddr_in sourceaddr;
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), int sz, int sizeof_packet)
306 {
307         if (sz < 0)
308                 bb_perror_msg_and_die("sendto");
309         if (sz != sizeof_packet)
310                 bb_error_msg_and_die("ping wrote %d chars; %d expected", sz,
311                         sizeof_packet);
312
313         signal(SIGALRM, sp);
314         if (pingcount == 0 || ntransmitted < pingcount) {       /* schedule next in 1s */
315                 alarm(PINGINTERVAL);
316         } else { /* done, wait for the last ping to come back */
317                 /* todo, don't necessarily need to wait so long... */
318                 signal(SIGALRM, pingstats);
319                 alarm(MAXWAIT);
320         }
321 }
322
323 static void sendping4(int junk ATTRIBUTE_UNUSED)
324 {
325         struct icmp *pkt;
326         int i;
327         char packet[datalen + ICMP_MINLEN];
328
329         pkt = (struct icmp *) packet;
330
331         pkt->icmp_type = ICMP_ECHO;
332         pkt->icmp_code = 0;
333         pkt->icmp_cksum = 0;
334         pkt->icmp_seq = htons(ntransmitted); /* don't ++ here, it can be a macro */
335         pkt->icmp_id = myid;
336         CLR((uint16_t)ntransmitted % MAX_DUP_CHK);
337         ntransmitted++;
338
339         gettimeofday((struct timeval *) &pkt->icmp_dun, NULL);
340         pkt->icmp_cksum = in_cksum((unsigned short *) pkt, sizeof(packet));
341
342         i = sendto(pingsock, packet, sizeof(packet), 0,
343                         &pingaddr.sa, sizeof(pingaddr.sin));
344
345         sendping_tail(sendping4, i, sizeof(packet));
346 }
347 #if ENABLE_PING6
348 static void sendping6(int junk ATTRIBUTE_UNUSED)
349 {
350         struct icmp6_hdr *pkt;
351         int i;
352         char packet[datalen + sizeof (struct icmp6_hdr)];
353
354         pkt = (struct icmp6_hdr *) packet;
355
356         pkt->icmp6_type = ICMP6_ECHO_REQUEST;
357         pkt->icmp6_code = 0;
358         pkt->icmp6_cksum = 0;
359         pkt->icmp6_seq = htons(ntransmitted); /* don't ++ here, it can be a macro */
360         pkt->icmp6_id = myid;
361         CLR((uint16_t)ntransmitted % MAX_DUP_CHK);
362         ntransmitted++;
363
364         gettimeofday((struct timeval *) &pkt->icmp6_data8[4], NULL);
365
366         i = sendto(pingsock, packet, sizeof(packet), 0,
367                         &pingaddr.sa, sizeof(pingaddr.sin6));
368
369         sendping_tail(sendping6, i, sizeof(packet));
370 }
371 #endif
372
373 static const char *icmp_type_name(int id)
374 {
375         switch (id) {
376         case ICMP_ECHOREPLY:      return "Echo Reply";
377         case ICMP_DEST_UNREACH:   return "Destination Unreachable";
378         case ICMP_SOURCE_QUENCH:  return "Source Quench";
379         case ICMP_REDIRECT:       return "Redirect (change route)";
380         case ICMP_ECHO:           return "Echo Request";
381         case ICMP_TIME_EXCEEDED:  return "Time Exceeded";
382         case ICMP_PARAMETERPROB:  return "Parameter Problem";
383         case ICMP_TIMESTAMP:      return "Timestamp Request";
384         case ICMP_TIMESTAMPREPLY: return "Timestamp Reply";
385         case ICMP_INFO_REQUEST:   return "Information Request";
386         case ICMP_INFO_REPLY:     return "Information Reply";
387         case ICMP_ADDRESS:        return "Address Mask Request";
388         case ICMP_ADDRESSREPLY:   return "Address Mask Reply";
389         default:                  return "unknown ICMP type";
390         }
391 }
392 #if ENABLE_PING6
393 /* RFC3542 changed some definitions from RFC2292 for no good reason, whee!
394  * the newer 3542 uses a MLD_ prefix where as 2292 uses ICMP6_ prefix */
395 #ifndef MLD_LISTENER_QUERY
396 # define MLD_LISTENER_QUERY ICMP6_MEMBERSHIP_QUERY
397 #endif
398 #ifndef MLD_LISTENER_REPORT
399 # define MLD_LISTENER_REPORT ICMP6_MEMBERSHIP_REPORT
400 #endif
401 #ifndef MLD_LISTENER_REDUCTION
402 # define MLD_LISTENER_REDUCTION ICMP6_MEMBERSHIP_REDUCTION
403 #endif
404 static const char *icmp6_type_name(int id)
405 {
406         switch (id) {
407         case ICMP6_DST_UNREACH:      return "Destination Unreachable";
408         case ICMP6_PACKET_TOO_BIG:   return "Packet too big";
409         case ICMP6_TIME_EXCEEDED:    return "Time Exceeded";
410         case ICMP6_PARAM_PROB:       return "Parameter Problem";
411         case ICMP6_ECHO_REPLY:       return "Echo Reply";
412         case ICMP6_ECHO_REQUEST:     return "Echo Request";
413         case MLD_LISTENER_QUERY:     return "Listener Query";
414         case MLD_LISTENER_REPORT:    return "Listener Report";
415         case MLD_LISTENER_REDUCTION: return "Listener Reduction";
416         default:                     return "unknown ICMP type";
417         }
418 }
419 #endif
420
421 static void unpack4(char *buf, int sz, struct sockaddr_in *from)
422 {
423         struct icmp *icmppkt;
424         struct iphdr *iphdr;
425         struct timeval tv, *tp;
426         int hlen, dupflag;
427         unsigned long triptime;
428
429         gettimeofday(&tv, NULL);
430
431         /* discard if too short */
432         if (sz < (datalen + ICMP_MINLEN))
433                 return;
434
435         /* check IP header */
436         iphdr = (struct iphdr *) buf;
437         hlen = iphdr->ihl << 2;
438         sz -= hlen;
439         icmppkt = (struct icmp *) (buf + hlen);
440         if (icmppkt->icmp_id != myid)
441                 return;                         /* not our ping */
442
443         if (icmppkt->icmp_type == ICMP_ECHOREPLY) {
444                 uint16_t recv_seq = ntohs(icmppkt->icmp_seq);
445                 ++nreceived;
446                 tp = (struct timeval *) icmppkt->icmp_data;
447
448                 if ((tv.tv_usec -= tp->tv_usec) < 0) {
449                         --tv.tv_sec;
450                         tv.tv_usec += 1000000;
451                 }
452                 tv.tv_sec -= tp->tv_sec;
453
454                 triptime = tv.tv_sec * 10000 + (tv.tv_usec / 100);
455                 tsum += triptime;
456                 if (triptime < tmin)
457                         tmin = triptime;
458                 if (triptime > tmax)
459                         tmax = triptime;
460
461                 if (TST(recv_seq % MAX_DUP_CHK)) {
462                         ++nrepeats;
463                         --nreceived;
464                         dupflag = 1;
465                 } else {
466                         SET(recv_seq % MAX_DUP_CHK);
467                         dupflag = 0;
468                 }
469
470                 if (option_mask32 & OPT_QUIET)
471                         return;
472
473                 printf("%d bytes from %s: icmp_seq=%u", sz,
474                            inet_ntoa(*(struct in_addr *) &from->sin_addr.s_addr),
475                            recv_seq);
476                 printf(" ttl=%d", iphdr->ttl);
477                 printf(" time=%lu.%lu ms", triptime / 10, triptime % 10);
478                 if (dupflag)
479                         printf(" (DUP!)");
480                 puts("");
481         } else {
482                 if (icmppkt->icmp_type != ICMP_ECHO)
483                         bb_error_msg("warning: got ICMP %d (%s)",
484                                         icmppkt->icmp_type,
485                                         icmp_type_name(icmppkt->icmp_type));
486         }
487         fflush(stdout);
488 }
489 #if ENABLE_PING6
490 static void unpack6(char *packet, int sz, struct sockaddr_in6 *from, int hoplimit)
491 {
492         struct icmp6_hdr *icmppkt;
493         struct timeval tv, *tp;
494         int dupflag;
495         unsigned long triptime;
496         char buf[INET6_ADDRSTRLEN];
497
498         gettimeofday(&tv, NULL);
499
500         /* discard if too short */
501         if (sz < (datalen + sizeof(struct icmp6_hdr)))
502                 return;
503
504         icmppkt = (struct icmp6_hdr *) packet;
505         if (icmppkt->icmp6_id != myid)
506                 return;                         /* not our ping */
507
508         if (icmppkt->icmp6_type == ICMP6_ECHO_REPLY) {
509                 uint16_t recv_seq = ntohs(icmppkt->icmp6_seq);
510                 ++nreceived;
511                 tp = (struct timeval *) &icmppkt->icmp6_data8[4];
512
513                 if ((tv.tv_usec -= tp->tv_usec) < 0) {
514                         --tv.tv_sec;
515                         tv.tv_usec += 1000000;
516                 }
517                 tv.tv_sec -= tp->tv_sec;
518
519                 triptime = tv.tv_sec * 10000 + (tv.tv_usec / 100);
520                 tsum += triptime;
521                 if (triptime < tmin)
522                         tmin = triptime;
523                 if (triptime > tmax)
524                         tmax = triptime;
525
526                 if (TST(recv_seq % MAX_DUP_CHK)) {
527                         ++nrepeats;
528                         --nreceived;
529                         dupflag = 1;
530                 } else {
531                         SET(recv_seq % MAX_DUP_CHK);
532                         dupflag = 0;
533                 }
534
535                 if (option_mask32 & OPT_QUIET)
536                         return;
537
538                 printf("%d bytes from %s: icmp6_seq=%u", sz,
539                            inet_ntop(AF_INET6, &pingaddr.sin6.sin6_addr,
540                                                  buf, sizeof(buf)),
541                            recv_seq);
542                 printf(" ttl=%d time=%lu.%lu ms", hoplimit,
543                            triptime / 10, triptime % 10);
544                 if (dupflag)
545                         printf(" (DUP!)");
546                 puts("");
547         } else {
548                 if (icmppkt->icmp6_type != ICMP6_ECHO_REQUEST)
549                         bb_error_msg("warning: got ICMP %d (%s)",
550                                         icmppkt->icmp6_type,
551                                         icmp6_type_name(icmppkt->icmp6_type));
552         }
553         fflush(stdout);
554 }
555 #endif
556
557 static void ping4(len_and_sockaddr *lsa)
558 {
559         char packet[datalen + MAXIPLEN + MAXICMPLEN];
560         int sockopt;
561
562         pingsock = create_icmp_socket();
563         pingaddr.sin = lsa->sin;
564         if (sourceaddr.sin_addr.s_addr) {
565                 xbind(pingsock, (struct sockaddr*)&sourceaddr, sizeof(sourceaddr));
566         }
567
568         /* enable broadcast pings */
569         setsockopt_broadcast(pingsock);
570
571         /* set recv buf for broadcast pings */
572         sockopt = 48 * 1024; /* explain why 48k? */
573         setsockopt(pingsock, SOL_SOCKET, SO_RCVBUF, &sockopt, sizeof(sockopt));
574
575         printf("PING %s (%s)", hostname, dotted);
576         if (sourceaddr.sin_addr.s_addr) {
577                 printf(" from %s",
578                         inet_ntoa(*(struct in_addr *) &sourceaddr.sin_addr.s_addr));
579         }
580         printf(": %d data bytes\n", datalen);
581
582         signal(SIGINT, pingstats);
583
584         /* start the ping's going ... */
585         sendping4(0);
586
587         /* listen for replies */
588         while (1) {
589                 struct sockaddr_in from;
590                 socklen_t fromlen = (socklen_t) sizeof(from);
591                 int c;
592
593                 c = recvfrom(pingsock, packet, sizeof(packet), 0,
594                                 (struct sockaddr *) &from, &fromlen);
595                 if (c < 0) {
596                         if (errno != EINTR)
597                                 bb_perror_msg("recvfrom");
598                         continue;
599                 }
600                 unpack4(packet, c, &from);
601                 if (pingcount > 0 && nreceived >= pingcount)
602                         break;
603         }
604 }
605 #if ENABLE_PING6
606 extern int BUG_bad_offsetof_icmp6_cksum(void);
607 static void ping6(len_and_sockaddr *lsa)
608 {
609         char packet[datalen + MAXIPLEN + MAXICMPLEN];
610         int sockopt;
611         struct msghdr msg;
612         struct sockaddr_in6 from;
613         struct iovec iov;
614         char control_buf[CMSG_SPACE(36)];
615
616         pingsock = create_icmp6_socket();
617         pingaddr.sin6 = lsa->sin6;
618         //if (sourceaddr.sin_addr.s_addr) {
619         //      xbind(pingsock, (struct sockaddr*)&sourceaddr, sizeof(sourceaddr));
620         //}
621
622 #ifdef ICMP6_FILTER
623         {
624                 struct icmp6_filter filt;
625                 if (!(option_mask32 & OPT_VERBOSE)) {
626                         ICMP6_FILTER_SETBLOCKALL(&filt);
627                         ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &filt);
628                 } else {
629                         ICMP6_FILTER_SETPASSALL(&filt);
630                 }
631                 if (setsockopt(pingsock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt,
632                                            sizeof(filt)) < 0)
633                         bb_error_msg_and_die("setsockopt(ICMP6_FILTER)");
634         }
635 #endif /*ICMP6_FILTER*/
636
637         /* enable broadcast pings */
638         setsockopt_broadcast(pingsock);
639
640         /* set recv buf for broadcast pings */
641         sockopt = 48 * 1024; /* explain why 48k? */
642         setsockopt(pingsock, SOL_SOCKET, SO_RCVBUF, &sockopt, sizeof(sockopt));
643
644         sockopt = offsetof(struct icmp6_hdr, icmp6_cksum);
645         if (offsetof(struct icmp6_hdr, icmp6_cksum) != 2)
646                 BUG_bad_offsetof_icmp6_cksum();
647         setsockopt(pingsock, SOL_RAW, IPV6_CHECKSUM, &sockopt, sizeof(sockopt));
648
649         /* request ttl info to be returned in ancillary data */
650         setsockopt(pingsock, SOL_IPV6, IPV6_HOPLIMIT, &const_int_1, sizeof(const_int_1));
651
652         if (if_index)
653                 pingaddr.sin6.sin6_scope_id = if_index;
654
655         printf("PING %s (%s): %d data bytes\n", hostname, dotted, datalen);
656
657         signal(SIGINT, pingstats);
658
659         /* start the ping's going ... */
660         sendping6(0);
661
662         /* listen for replies */
663         msg.msg_name = &from;
664         msg.msg_namelen = sizeof(from);
665         msg.msg_iov = &iov;
666         msg.msg_iovlen = 1;
667         msg.msg_control = control_buf;
668         iov.iov_base = packet;
669         iov.iov_len = sizeof(packet);
670         while (1) {
671                 int c;
672                 struct cmsghdr *mp;
673                 int hoplimit = -1;
674                 msg.msg_controllen = sizeof(control_buf);
675
676                 c = recvmsg(pingsock, &msg, 0);
677                 if (c < 0) {
678                         if (errno != EINTR)
679                                 bb_perror_msg("recvfrom");
680                         continue;
681                 }
682                 for (mp = CMSG_FIRSTHDR(&msg); mp; mp = CMSG_NXTHDR(&msg, mp)) {
683                         if (mp->cmsg_level == SOL_IPV6
684                          && mp->cmsg_type == IPV6_HOPLIMIT
685                          /* don't check len - we trust the kernel: */
686                          /* && mp->cmsg_len >= CMSG_LEN(sizeof(int)) */
687                         ) {
688                                 hoplimit = *(int*)CMSG_DATA(mp);
689                         }
690                 }
691                 unpack6(packet, c, &from, hoplimit);
692                 if (pingcount > 0 && nreceived >= pingcount)
693                         break;
694         }
695 }
696 #endif
697
698 /* TODO: consolidate ether-wake.c, dnsd.c, ifupdown.c, nslookup.c
699  * versions of below thing. BTW we have far too many "%u.%u.%u.%u" too...
700 */
701 static int parse_nipquad(const char *str, struct sockaddr_in* addr)
702 {
703         char dummy;
704         unsigned i1, i2, i3, i4;
705         if (sscanf(str, "%u.%u.%u.%u%c",
706                            &i1, &i2, &i3, &i4, &dummy) == 4
707         && ( (i1|i2|i3|i4) <= 0xff )
708         ) {
709                 uint8_t* ptr = (uint8_t*)&addr->sin_addr;
710                 ptr[0] = i1;
711                 ptr[1] = i2;
712                 ptr[2] = i3;
713                 ptr[3] = i4;
714                 return 0;
715         }
716         return 1; /* error */
717 }
718
719 int ping_main(int argc, char **argv);
720 int ping_main(int argc, char **argv)
721 {
722         len_and_sockaddr *lsa;
723         char *opt_c, *opt_s, *opt_I;
724         USE_PING6(sa_family_t af = AF_UNSPEC;)
725
726         datalen = DEFDATALEN; /* initialized here rather than in global scope to work around gcc bug */
727
728         /* exactly one argument needed, -v and -q don't mix */
729         opt_complementary = "=1:q--v:v--q";
730         getopt32(argc, argv, OPT_STRING, &opt_c, &opt_s, &opt_I);
731         if (option_mask32 & OPT_c) pingcount = xatoul(opt_c); // -c
732         if (option_mask32 & OPT_s) datalen = xatou16(opt_s); // -s
733         if (option_mask32 & OPT_I) { // -I
734                 if_index = if_nametoindex(opt_I);
735                 if (!if_index)
736                         if (parse_nipquad(opt_I, &sourceaddr))
737                                 bb_show_usage();
738         }
739         myid = (int16_t) getpid();
740         hostname = argv[optind];
741 #if ENABLE_PING6
742         if (option_mask32 & OPT_IPV4)
743                 af = AF_INET;
744         if (option_mask32 & OPT_IPV6)
745                 af = AF_INET6;
746         lsa = xhost_and_af2sockaddr(hostname, 0, af);
747 #else
748         lsa = xhost_and_af2sockaddr(hostname, 0, AF_INET);
749 #endif
750         dotted = xmalloc_sockaddr2dotted_noport(&lsa->sa, lsa->len);
751 #if ENABLE_PING6
752         if (lsa->sa.sa_family == AF_INET6)
753                 ping6(lsa);
754         else
755 #endif
756                 ping4(lsa);
757         pingstats(0);
758         return EXIT_SUCCESS;
759 }
760 #endif /* FEATURE_FANCY_PING */
761
762
763 #if ENABLE_PING6
764 int ping6_main(int argc, char **argv);
765 int ping6_main(int argc, char **argv)
766 {
767         argv[0] = (char*)"-6";
768         return ping_main(argc + 1, argv - 1);
769 }
770 #endif
771
772 /* from ping6.c:
773  * Copyright (c) 1989 The Regents of the University of California.
774  * All rights reserved.
775  *
776  * This code is derived from software contributed to Berkeley by
777  * Mike Muuss.
778  *
779  * Redistribution and use in source and binary forms, with or without
780  * modification, are permitted provided that the following conditions
781  * are met:
782  * 1. Redistributions of source code must retain the above copyright
783  *    notice, this list of conditions and the following disclaimer.
784  * 2. Redistributions in binary form must reproduce the above copyright
785  *    notice, this list of conditions and the following disclaimer in the
786  *    documentation and/or other materials provided with the distribution.
787  *
788  * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
789  *              ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
790  *
791  * 4. Neither the name of the University nor the names of its contributors
792  *    may be used to endorse or promote products derived from this software
793  *    without specific prior written permission.
794  *
795  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
796  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
797  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
798  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
799  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
800  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
801  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
802  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
803  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
804  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
805  * SUCH DAMAGE.
806  */