ba4f585dbf661e83baea02956ca2596af7542094
[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 {
190         len_and_sockaddr *lsa;
191 #if ENABLE_PING6
192         sa_family_t af = AF_UNSPEC;
193         while (++argv[0][0] == '-') {
194                 if (argv[0][1] == '4') {
195                         af = AF_INET;
196                         continue;
197                 }
198                 if (argv[0][1] == '6') {
199                         af = AF_INET6;
200                         continue;
201                 }
202                 bb_show_usage();
203         }
204 #else
205         argv++;
206 #endif
207
208         hostname = *argv;
209         if (!hostname)
210                 bb_show_usage();
211
212 #if ENABLE_PING6
213         lsa = host_and_af2sockaddr(hostname, 0, af);
214 #else
215         lsa = host_and_af2sockaddr(hostname, 0, AF_INET);
216 #endif
217         /* Set timer _after_ DNS resolution */
218         signal(SIGALRM, noresp);
219         alarm(5); /* give the host 5000ms to respond */
220
221 #if ENABLE_PING6
222         if (lsa->sa.sa_family == AF_INET6)
223                 ping6(lsa);
224         else
225 #endif
226                 ping4(lsa);
227         printf("%s is alive!\n", hostname);
228         return EXIT_SUCCESS;
229 }
230
231
232 #else /* FEATURE_FANCY_PING */
233
234
235 /* full(er) version */
236
237 #define OPT_STRING ("qvc:s:I:4" USE_PING6("6"))
238 enum {
239         OPT_QUIET = 1 << 0,
240         OPT_VERBOSE = 1 << 1,
241         OPT_c = 1 << 2,
242         OPT_s = 1 << 3,
243         OPT_I = 1 << 4,
244         OPT_IPV4 = 1 << 5,
245         OPT_IPV6 = (1 << 6) * ENABLE_PING6,
246 };
247
248
249 static union {
250         struct sockaddr sa;
251         struct sockaddr_in sin;
252 #if ENABLE_PING6
253         struct sockaddr_in6 sin6;
254 #endif
255 } pingaddr;
256 static struct sockaddr_in sourceaddr;
257 static int pingsock = -1;
258 static unsigned datalen; /* intentionally uninitialized to work around gcc bug */
259
260 static int if_index;
261
262 static unsigned long ntransmitted, nreceived, nrepeats, pingcount;
263 static int myid;
264 static unsigned long tmin = ULONG_MAX, tmax, tsum;
265 static char rcvd_tbl[MAX_DUP_CHK / 8];
266
267 static const char *hostname;
268 static const char *dotted;
269
270 #define A(bit)          rcvd_tbl[(bit)>>3]      /* identify byte in array */
271 #define B(bit)          (1 << ((bit) & 0x07))   /* identify bit in byte */
272 #define SET(bit)        (A(bit) |= B(bit))
273 #define CLR(bit)        (A(bit) &= (~B(bit)))
274 #define TST(bit)        (A(bit) & B(bit))
275
276 /**************************************************************************/
277
278 static void pingstats(int junk ATTRIBUTE_UNUSED)
279 {
280         int status;
281
282         signal(SIGINT, SIG_IGN);
283
284         printf("\n--- %s ping statistics ---\n", hostname);
285         printf("%lu packets transmitted, ", ntransmitted);
286         printf("%lu packets received, ", nreceived);
287         if (nrepeats)
288                 printf("%lu duplicates, ", nrepeats);
289         if (ntransmitted)
290                 ntransmitted = (ntransmitted - nreceived) * 100 / ntransmitted;
291         printf("%lu%% packet loss\n", ntransmitted);
292         if (nreceived)
293                 printf("round-trip min/avg/max = %lu.%lu/%lu.%lu/%lu.%lu ms\n",
294                         tmin / 10, tmin % 10,
295                         (tsum / (nreceived + nrepeats)) / 10,
296                         (tsum / (nreceived + nrepeats)) % 10, tmax / 10, tmax % 10);
297         if (nreceived != 0)
298                 status = EXIT_SUCCESS;
299         else
300                 status = EXIT_FAILURE;
301         exit(status);
302 }
303
304 static void sendping_tail(void (*sp)(int), int sz, int sizeof_packet)
305 {
306         if (sz < 0)
307                 bb_perror_msg_and_die("sendto");
308         if (sz != sizeof_packet)
309                 bb_error_msg_and_die("ping wrote %d chars; %d expected", sz,
310                         sizeof_packet);
311
312         signal(SIGALRM, sp);
313         if (pingcount == 0 || ntransmitted < pingcount) {       /* schedule next in 1s */
314                 alarm(PINGINTERVAL);
315         } else { /* done, wait for the last ping to come back */
316                 /* todo, don't necessarily need to wait so long... */
317                 signal(SIGALRM, pingstats);
318                 alarm(MAXWAIT);
319         }
320 }
321
322 static void sendping4(int junk ATTRIBUTE_UNUSED)
323 {
324         struct icmp *pkt;
325         int i;
326         char packet[datalen + ICMP_MINLEN];
327
328         pkt = (struct icmp *) packet;
329
330         pkt->icmp_type = ICMP_ECHO;
331         pkt->icmp_code = 0;
332         pkt->icmp_cksum = 0;
333         pkt->icmp_seq = htons(ntransmitted); /* don't ++ here, it can be a macro */
334         pkt->icmp_id = myid;
335         CLR((uint16_t)ntransmitted % MAX_DUP_CHK);
336         ntransmitted++;
337
338         gettimeofday((struct timeval *) &pkt->icmp_dun, NULL);
339         pkt->icmp_cksum = in_cksum((unsigned short *) pkt, sizeof(packet));
340
341         i = sendto(pingsock, packet, sizeof(packet), 0,
342                         &pingaddr.sa, sizeof(pingaddr.sin));
343
344         sendping_tail(sendping4, i, sizeof(packet));
345 }
346 #if ENABLE_PING6
347 static void sendping6(int junk ATTRIBUTE_UNUSED)
348 {
349         struct icmp6_hdr *pkt;
350         int i;
351         char packet[datalen + sizeof (struct icmp6_hdr)];
352
353         pkt = (struct icmp6_hdr *) packet;
354
355         pkt->icmp6_type = ICMP6_ECHO_REQUEST;
356         pkt->icmp6_code = 0;
357         pkt->icmp6_cksum = 0;
358         pkt->icmp6_seq = htons(ntransmitted); /* don't ++ here, it can be a macro */
359         pkt->icmp6_id = myid;
360         CLR((uint16_t)ntransmitted % MAX_DUP_CHK);
361         ntransmitted++;
362
363         gettimeofday((struct timeval *) &pkt->icmp6_data8[4], NULL);
364
365         i = sendto(pingsock, packet, sizeof(packet), 0,
366                         &pingaddr.sa, sizeof(pingaddr.sin6));
367
368         sendping_tail(sendping6, i, sizeof(packet));
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 unpack4(char *buf, int sz, struct sockaddr_in *from)
421 {
422         struct icmp *icmppkt;
423         struct iphdr *iphdr;
424         struct timeval tv, *tp;
425         int hlen, dupflag;
426         unsigned long triptime;
427
428         gettimeofday(&tv, NULL);
429
430         /* discard if too short */
431         if (sz < (datalen + ICMP_MINLEN))
432                 return;
433
434         /* check IP header */
435         iphdr = (struct iphdr *) buf;
436         hlen = iphdr->ihl << 2;
437         sz -= hlen;
438         icmppkt = (struct icmp *) (buf + hlen);
439         if (icmppkt->icmp_id != myid)
440                 return;                         /* not our ping */
441
442         if (icmppkt->icmp_type == ICMP_ECHOREPLY) {
443                 uint16_t recv_seq = ntohs(icmppkt->icmp_seq);
444                 ++nreceived;
445                 tp = (struct timeval *) icmppkt->icmp_data;
446
447                 if ((tv.tv_usec -= tp->tv_usec) < 0) {
448                         --tv.tv_sec;
449                         tv.tv_usec += 1000000;
450                 }
451                 tv.tv_sec -= tp->tv_sec;
452
453                 triptime = tv.tv_sec * 10000 + (tv.tv_usec / 100);
454                 tsum += triptime;
455                 if (triptime < tmin)
456                         tmin = triptime;
457                 if (triptime > tmax)
458                         tmax = triptime;
459
460                 if (TST(recv_seq % MAX_DUP_CHK)) {
461                         ++nrepeats;
462                         --nreceived;
463                         dupflag = 1;
464                 } else {
465                         SET(recv_seq % MAX_DUP_CHK);
466                         dupflag = 0;
467                 }
468
469                 if (option_mask32 & OPT_QUIET)
470                         return;
471
472                 printf("%d bytes from %s: icmp_seq=%u", sz,
473                            inet_ntoa(*(struct in_addr *) &from->sin_addr.s_addr),
474                            recv_seq);
475                 printf(" ttl=%d", iphdr->ttl);
476                 printf(" time=%lu.%lu ms", triptime / 10, triptime % 10);
477                 if (dupflag)
478                         printf(" (DUP!)");
479                 puts("");
480         } else {
481                 if (icmppkt->icmp_type != ICMP_ECHO)
482                         bb_error_msg("warning: got ICMP %d (%s)",
483                                         icmppkt->icmp_type,
484                                         icmp_type_name(icmppkt->icmp_type));
485         }
486         fflush(stdout);
487 }
488 #if ENABLE_PING6
489 static void unpack6(char *packet, int sz, struct sockaddr_in6 *from, int hoplimit)
490 {
491         struct icmp6_hdr *icmppkt;
492         struct timeval tv, *tp;
493         int dupflag;
494         unsigned long triptime;
495         char buf[INET6_ADDRSTRLEN];
496
497         gettimeofday(&tv, NULL);
498
499         /* discard if too short */
500         if (sz < (datalen + sizeof(struct icmp6_hdr)))
501                 return;
502
503         icmppkt = (struct icmp6_hdr *) packet;
504         if (icmppkt->icmp6_id != myid)
505                 return;                         /* not our ping */
506
507         if (icmppkt->icmp6_type == ICMP6_ECHO_REPLY) {
508                 uint16_t recv_seq = ntohs(icmppkt->icmp6_seq);
509                 ++nreceived;
510                 tp = (struct timeval *) &icmppkt->icmp6_data8[4];
511
512                 if ((tv.tv_usec -= tp->tv_usec) < 0) {
513                         --tv.tv_sec;
514                         tv.tv_usec += 1000000;
515                 }
516                 tv.tv_sec -= tp->tv_sec;
517
518                 triptime = tv.tv_sec * 10000 + (tv.tv_usec / 100);
519                 tsum += triptime;
520                 if (triptime < tmin)
521                         tmin = triptime;
522                 if (triptime > tmax)
523                         tmax = triptime;
524
525                 if (TST(recv_seq % MAX_DUP_CHK)) {
526                         ++nrepeats;
527                         --nreceived;
528                         dupflag = 1;
529                 } else {
530                         SET(recv_seq % MAX_DUP_CHK);
531                         dupflag = 0;
532                 }
533
534                 if (option_mask32 & OPT_QUIET)
535                         return;
536
537                 printf("%d bytes from %s: icmp6_seq=%u", sz,
538                            inet_ntop(AF_INET6, &pingaddr.sin6.sin6_addr,
539                                                  buf, sizeof(buf)),
540                            recv_seq);
541                 printf(" ttl=%d time=%lu.%lu ms", hoplimit,
542                            triptime / 10, triptime % 10);
543                 if (dupflag)
544                         printf(" (DUP!)");
545                 puts("");
546         } else {
547                 if (icmppkt->icmp6_type != ICMP6_ECHO_REQUEST)
548                         bb_error_msg("warning: got ICMP %d (%s)",
549                                         icmppkt->icmp6_type,
550                                         icmp6_type_name(icmppkt->icmp6_type));
551         }
552         fflush(stdout);
553 }
554 #endif
555
556 static void ping4(len_and_sockaddr *lsa)
557 {
558         char packet[datalen + MAXIPLEN + MAXICMPLEN];
559         int sockopt;
560
561         pingsock = create_icmp_socket();
562         pingaddr.sin = lsa->sin;
563         if (sourceaddr.sin_addr.s_addr) {
564                 xbind(pingsock, (struct sockaddr*)&sourceaddr, sizeof(sourceaddr));
565         }
566
567         /* enable broadcast pings */
568         setsockopt_broadcast(pingsock);
569
570         /* set recv buf for broadcast pings */
571         sockopt = 48 * 1024; /* explain why 48k? */
572         setsockopt(pingsock, SOL_SOCKET, SO_RCVBUF, &sockopt, sizeof(sockopt));
573
574         printf("PING %s (%s)", hostname, dotted);
575         if (sourceaddr.sin_addr.s_addr) {
576                 printf(" from %s",
577                         inet_ntoa(*(struct in_addr *) &sourceaddr.sin_addr.s_addr));
578         }
579         printf(": %d data bytes\n", datalen);
580
581         signal(SIGINT, pingstats);
582
583         /* start the ping's going ... */
584         sendping4(0);
585
586         /* listen for replies */
587         while (1) {
588                 struct sockaddr_in from;
589                 socklen_t fromlen = (socklen_t) sizeof(from);
590                 int c;
591
592                 c = recvfrom(pingsock, packet, sizeof(packet), 0,
593                                 (struct sockaddr *) &from, &fromlen);
594                 if (c < 0) {
595                         if (errno != EINTR)
596                                 bb_perror_msg("recvfrom");
597                         continue;
598                 }
599                 unpack4(packet, c, &from);
600                 if (pingcount > 0 && nreceived >= pingcount)
601                         break;
602         }
603 }
604 #if ENABLE_PING6
605 extern int BUG_bad_offsetof_icmp6_cksum(void);
606 static void ping6(len_and_sockaddr *lsa)
607 {
608         char packet[datalen + MAXIPLEN + MAXICMPLEN];
609         int sockopt;
610         struct msghdr msg;
611         struct sockaddr_in6 from;
612         struct iovec iov;
613         char control_buf[CMSG_SPACE(36)];
614
615         pingsock = create_icmp6_socket();
616         pingaddr.sin6 = lsa->sin6;
617         //if (sourceaddr.sin_addr.s_addr) {
618         //      xbind(pingsock, (struct sockaddr*)&sourceaddr, sizeof(sourceaddr));
619         //}
620
621 #ifdef ICMP6_FILTER
622         {
623                 struct icmp6_filter filt;
624                 if (!(option_mask32 & OPT_VERBOSE)) {
625                         ICMP6_FILTER_SETBLOCKALL(&filt);
626                         ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &filt);
627                 } else {
628                         ICMP6_FILTER_SETPASSALL(&filt);
629                 }
630                 if (setsockopt(pingsock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt,
631                                            sizeof(filt)) < 0)
632                         bb_error_msg_and_die("setsockopt(ICMP6_FILTER)");
633         }
634 #endif /*ICMP6_FILTER*/
635
636         /* enable broadcast pings */
637         setsockopt_broadcast(pingsock);
638
639         /* set recv buf for broadcast pings */
640         sockopt = 48 * 1024; /* explain why 48k? */
641         setsockopt(pingsock, SOL_SOCKET, SO_RCVBUF, &sockopt, sizeof(sockopt));
642
643         sockopt = offsetof(struct icmp6_hdr, icmp6_cksum);
644         if (offsetof(struct icmp6_hdr, icmp6_cksum) != 2)
645                 BUG_bad_offsetof_icmp6_cksum();
646         setsockopt(pingsock, SOL_RAW, IPV6_CHECKSUM, &sockopt, sizeof(sockopt));
647
648         /* request ttl info to be returned in ancillary data */
649         setsockopt(pingsock, SOL_IPV6, IPV6_HOPLIMIT, &const_int_1, sizeof(const_int_1));
650
651         if (if_index)
652                 pingaddr.sin6.sin6_scope_id = if_index;
653
654         printf("PING %s (%s): %d data bytes\n", hostname, dotted, datalen);
655
656         signal(SIGINT, pingstats);
657
658         /* start the ping's going ... */
659         sendping6(0);
660
661         /* listen for replies */
662         msg.msg_name = &from;
663         msg.msg_namelen = sizeof(from);
664         msg.msg_iov = &iov;
665         msg.msg_iovlen = 1;
666         msg.msg_control = control_buf;
667         iov.iov_base = packet;
668         iov.iov_len = sizeof(packet);
669         while (1) {
670                 int c;
671                 struct cmsghdr *mp;
672                 int hoplimit = -1;
673                 msg.msg_controllen = sizeof(control_buf);
674
675                 c = recvmsg(pingsock, &msg, 0);
676                 if (c < 0) {
677                         if (errno != EINTR)
678                                 bb_perror_msg("recvfrom");
679                         continue;
680                 }
681                 for (mp = CMSG_FIRSTHDR(&msg); mp; mp = CMSG_NXTHDR(&msg, mp)) {
682                         if (mp->cmsg_level == SOL_IPV6
683                          && mp->cmsg_type == IPV6_HOPLIMIT
684                          /* don't check len - we trust the kernel: */
685                          /* && mp->cmsg_len >= CMSG_LEN(sizeof(int)) */
686                         ) {
687                                 hoplimit = *(int*)CMSG_DATA(mp);
688                         }
689                 }
690                 unpack6(packet, c, &from, hoplimit);
691                 if (pingcount > 0 && nreceived >= pingcount)
692                         break;
693         }
694 }
695 #endif
696
697 /* TODO: consolidate ether-wake.c, dnsd.c, ifupdown.c, nslookup.c
698  * versions of below thing. BTW we have far too many "%u.%u.%u.%u" too...
699 */
700 static int parse_nipquad(const char *str, struct sockaddr_in* addr)
701 {
702         char dummy;
703         unsigned i1, i2, i3, i4;
704         if (sscanf(str, "%u.%u.%u.%u%c",
705                            &i1, &i2, &i3, &i4, &dummy) == 4
706         && ( (i1|i2|i3|i4) <= 0xff )
707         ) {
708                 uint8_t* ptr = (uint8_t*)&addr->sin_addr;
709                 ptr[0] = i1;
710                 ptr[1] = i2;
711                 ptr[2] = i3;
712                 ptr[3] = i4;
713                 return 0;
714         }
715         return 1; /* error */
716 }
717
718 int ping_main(int argc, char **argv)
719 {
720         len_and_sockaddr *lsa;
721         char *opt_c, *opt_s, *opt_I;
722         USE_PING6(sa_family_t af = AF_UNSPEC;)
723
724         datalen = DEFDATALEN; /* initialized here rather than in global scope to work around gcc bug */
725
726         /* exactly one argument needed, -v and -q don't mix */
727         opt_complementary = "=1:q--v:v--q";
728         getopt32(argc, argv, OPT_STRING, &opt_c, &opt_s, &opt_I);
729         if (option_mask32 & OPT_c) pingcount = xatoul(opt_c); // -c
730         if (option_mask32 & OPT_s) datalen = xatou16(opt_s); // -s
731         if (option_mask32 & OPT_I) { // -I
732                 if_index = if_nametoindex(opt_I);
733                 if (!if_index)
734                         if (parse_nipquad(opt_I, &sourceaddr))
735                                 bb_show_usage();
736         }
737         myid = (int16_t) getpid();
738         hostname = argv[optind];
739 #if ENABLE_PING6
740         if (option_mask32 & OPT_IPV4)
741                 af = AF_INET;
742         if (option_mask32 & OPT_IPV6)
743                 af = AF_INET6;
744         lsa = host_and_af2sockaddr(hostname, 0, af);
745 #else
746         lsa = host_and_af2sockaddr(hostname, 0, AF_INET);
747 #endif
748         dotted = xmalloc_sockaddr2dotted_noport(&lsa->sa, lsa->len);
749 #if ENABLE_PING6
750         if (lsa->sa.sa_family == AF_INET6)
751                 ping6(lsa);
752         else
753 #endif
754                 ping4(lsa);
755         pingstats(0);
756         return EXIT_SUCCESS;
757 }
758 #endif /* FEATURE_FANCY_PING */
759
760
761 #if ENABLE_PING6
762 int ping6_main(int argc, char **argv)
763 {
764         argv[0] = (char*)"-6";
765         return ping_main(argc + 1, argv - 1);
766 }
767 #endif
768
769 /* from ping6.c:
770  * Copyright (c) 1989 The Regents of the University of California.
771  * All rights reserved.
772  *
773  * This code is derived from software contributed to Berkeley by
774  * Mike Muuss.
775  *
776  * Redistribution and use in source and binary forms, with or without
777  * modification, are permitted provided that the following conditions
778  * are met:
779  * 1. Redistributions of source code must retain the above copyright
780  *    notice, this list of conditions and the following disclaimer.
781  * 2. Redistributions in binary form must reproduce the above copyright
782  *    notice, this list of conditions and the following disclaimer in the
783  *    documentation and/or other materials provided with the distribution.
784  *
785  * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
786  *              ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
787  *
788  * 4. Neither the name of the University nor the names of its contributors
789  *    may be used to endorse or promote products derived from this software
790  *    without specific prior written permission.
791  *
792  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
793  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
794  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
795  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
796  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
797  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
798  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
799  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
800  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
801  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
802  * SUCH DAMAGE.
803  */