1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124#include <net/if.h>
125#if defined(__FreeBSD__)
126# include <netinet/in.h>
127# include <netinet/ip.h>
128#endif
129#include <netinet/ip_icmp.h>
130#include "libbb.h"
131#include "common_bufsiz.h"
132
133#ifdef __BIONIC__
134
135# define ICMP_DEST_UNREACH 3
136# define ICMP_SOURCE_QUENCH 4
137# define ICMP_REDIRECT 5
138# define ICMP_ECHO 8
139# define ICMP_TIME_EXCEEDED 11
140# define ICMP_PARAMETERPROB 12
141# define ICMP_TIMESTAMP 13
142# define ICMP_TIMESTAMPREPLY 14
143# define ICMP_INFO_REQUEST 15
144# define ICMP_INFO_REPLY 16
145# define ICMP_ADDRESS 17
146# define ICMP_ADDRESSREPLY 18
147#endif
148
149
150
151
152
153#ifndef SOL_RAW
154# define SOL_RAW IPPROTO_RAW
155#endif
156
157#if ENABLE_PING6
158# include <netinet/icmp6.h>
159
160
161# ifdef IPV6_2292HOPLIMIT
162# undef IPV6_HOPLIMIT
163# define IPV6_HOPLIMIT IPV6_2292HOPLIMIT
164# endif
165#endif
166
167#if defined(__FreeBSD__)
168
169
170
171
172
173
174
175
176
177
178
179
180struct iphdr {
181# if BYTE_ORDER == LITTLE_ENDIAN
182 unsigned int ihl:4;
183 unsigned int version:4;
184# elif BYTE_ORDER == BIG_ENDIAN
185 unsigned int version:4;
186 unsigned int ihl:4;
187# endif
188 uint8_t tos;
189 uint16_t tot_len;
190 uint16_t id;
191 uint16_t frag_off;
192 uint8_t ttl;
193 uint8_t protocol;
194 uint16_t check;
195 uint32_t saddr;
196 uint32_t daddr;
197
198};
199#endif
200
201enum {
202 DEFDATALEN = 56,
203 MAXIPLEN = 60,
204 MAXICMPLEN = 76,
205 MAX_DUP_CHK = (8 * 128),
206 MAXWAIT = 10,
207 PINGINTERVAL = 1,
208 pingsock = 0,
209};
210
211static void
212#if ENABLE_PING6
213create_icmp_socket(len_and_sockaddr *lsa)
214#else
215create_icmp_socket(void)
216#define create_icmp_socket(lsa) create_icmp_socket()
217#endif
218{
219 int sock;
220#if ENABLE_PING6
221 if (lsa->u.sa.sa_family == AF_INET6)
222 sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
223 else
224#endif
225 sock = socket(AF_INET, SOCK_RAW, 1);
226 if (sock < 0) {
227 if (errno == EPERM)
228 bb_simple_error_msg_and_die(bb_msg_perm_denied_are_you_root);
229 bb_simple_perror_msg_and_die(bb_msg_can_not_create_raw_socket);
230 }
231
232 xmove_fd(sock, pingsock);
233}
234
235#if !ENABLE_FEATURE_FANCY_PING
236
237
238
239struct globals {
240 char *hostname;
241 char packet[DEFDATALEN + MAXIPLEN + MAXICMPLEN];
242 uint16_t myid;
243} FIX_ALIASING;
244#define G (*(struct globals*)bb_common_bufsiz1)
245#define INIT_G() do { setup_common_bufsiz(); } while (0)
246
247static void noresp(int ign UNUSED_PARAM)
248{
249 printf("No response from %s\n", G.hostname);
250 exit(EXIT_FAILURE);
251}
252
253static void ping4(len_and_sockaddr *lsa)
254{
255 struct icmp *pkt;
256 int c;
257
258 pkt = (struct icmp *) G.packet;
259
260 pkt->icmp_type = ICMP_ECHO;
261 pkt->icmp_id = G.myid;
262 pkt->icmp_cksum = inet_cksum(pkt, sizeof(G.packet));
263
264 xsendto(pingsock, G.packet, DEFDATALEN + ICMP_MINLEN, &lsa->u.sa, lsa->len);
265
266
267 while (1) {
268#if 0
269 struct sockaddr_in from;
270 socklen_t fromlen = sizeof(from);
271
272 c = recvfrom(pingsock, G.packet, sizeof(G.packet), 0,
273 (struct sockaddr *) &from, &fromlen);
274#else
275 c = recv(pingsock, G.packet, sizeof(G.packet), 0);
276#endif
277 if (c < 0) {
278 if (errno != EINTR)
279 bb_simple_perror_msg("recvfrom");
280 continue;
281 }
282 if (c >= 76) {
283 struct iphdr *iphdr = (struct iphdr *) G.packet;
284
285 pkt = (struct icmp *) (G.packet + (iphdr->ihl << 2));
286 if (pkt->icmp_id != G.myid)
287 continue;
288 if (pkt->icmp_type == ICMP_ECHOREPLY)
289 break;
290 }
291 }
292}
293
294#if ENABLE_PING6
295static void ping6(len_and_sockaddr *lsa)
296{
297 struct icmp6_hdr *pkt;
298 int c;
299 int sockopt;
300
301 pkt = (struct icmp6_hdr *) G.packet;
302
303 pkt->icmp6_type = ICMP6_ECHO_REQUEST;
304 pkt->icmp6_id = G.myid;
305
306 sockopt = offsetof(struct icmp6_hdr, icmp6_cksum);
307 setsockopt_int(pingsock, SOL_RAW, IPV6_CHECKSUM, sockopt);
308
309 xsendto(pingsock, G.packet, DEFDATALEN + sizeof(struct icmp6_hdr), &lsa->u.sa, lsa->len);
310
311
312 while (1) {
313#if 0
314 struct sockaddr_in6 from;
315 socklen_t fromlen = sizeof(from);
316
317 c = recvfrom(pingsock, G.packet, sizeof(G.packet), 0,
318 (struct sockaddr *) &from, &fromlen);
319#else
320 c = recv(pingsock, G.packet, sizeof(G.packet), 0);
321#endif
322 if (c < 0) {
323 if (errno != EINTR)
324 bb_simple_perror_msg("recvfrom");
325 continue;
326 }
327 if (c >= ICMP_MINLEN) {
328 if (pkt->icmp6_id != G.myid)
329 continue;
330 if (pkt->icmp6_type == ICMP6_ECHO_REPLY)
331 break;
332 }
333 }
334}
335#endif
336
337#if !ENABLE_PING6
338# define common_ping_main(af, argv) common_ping_main(argv)
339#endif
340static int common_ping_main(sa_family_t af, char **argv)
341{
342 len_and_sockaddr *lsa;
343
344 INIT_G();
345
346#if ENABLE_PING6
347 while ((++argv)[0] && argv[0][0] == '-') {
348 if (argv[0][1] == '4') {
349 af = AF_INET;
350 continue;
351 }
352 if (argv[0][1] == '6') {
353 af = AF_INET6;
354 continue;
355 }
356 bb_show_usage();
357 }
358#else
359 argv++;
360#endif
361
362 G.hostname = *argv;
363 if (!G.hostname)
364 bb_show_usage();
365
366#if ENABLE_PING6
367 lsa = xhost_and_af2sockaddr(G.hostname, 0, af);
368#else
369 lsa = xhost_and_af2sockaddr(G.hostname, 0, AF_INET);
370#endif
371
372 signal(SIGALRM, noresp);
373 alarm(5);
374
375 create_icmp_socket(lsa);
376 G.myid = (uint16_t) getpid();
377
378
379
380
381 G.myid = htons(G.myid);
382#if ENABLE_PING6
383 if (lsa->u.sa.sa_family == AF_INET6)
384 ping6(lsa);
385 else
386#endif
387 ping4(lsa);
388 if (ENABLE_FEATURE_CLEAN_UP)
389 close(pingsock);
390 printf("%s is alive!\n", G.hostname);
391 return EXIT_SUCCESS;
392}
393
394
395#else
396
397
398
399
400
401#define OPT_STRING "qvAc:+s:t:+w:+W:+I:np:i:4"IF_PING6("6")
402enum {
403 OPT_QUIET = 1 << 0,
404 OPT_VERBOSE = 1 << 1,
405 OPT_A = 1 << 2,
406 OPT_c = 1 << 3,
407 OPT_s = 1 << 4,
408 OPT_t = 1 << 5,
409 OPT_w = 1 << 6,
410 OPT_W = 1 << 7,
411 OPT_I = 1 << 8,
412
413 OPT_p = 1 << 10,
414 OPT_i = 1 << 11,
415 OPT_IPV4 = 1 << 12,
416 OPT_IPV6 = (1 << 13) * ENABLE_PING6,
417};
418
419
420struct globals {
421 int if_index;
422 char *str_I;
423 len_and_sockaddr *source_lsa;
424 unsigned datalen;
425 unsigned pingcount;
426 unsigned opt_ttl;
427 unsigned long ntransmitted, nreceived, nrepeats;
428 uint16_t myid;
429 uint8_t pattern;
430 unsigned tmin, tmax;
431 unsigned long long tsum;
432 unsigned cur_us;
433 unsigned deadline_us;
434 unsigned interval_us;
435 unsigned timeout;
436 unsigned sizeof_rcv_packet;
437 char *rcv_packet;
438 void *snd_packet;
439 const char *hostname;
440 const char *dotted;
441 union {
442 struct sockaddr sa;
443 struct sockaddr_in sin;
444#if ENABLE_PING6
445 struct sockaddr_in6 sin6;
446#endif
447 } pingaddr;
448 unsigned char rcvd_tbl[MAX_DUP_CHK / 8];
449} FIX_ALIASING;
450#define G (*(struct globals*)bb_common_bufsiz1)
451#define if_index (G.if_index )
452#define source_lsa (G.source_lsa )
453#define str_I (G.str_I )
454#define datalen (G.datalen )
455#define pingcount (G.pingcount )
456#define opt_ttl (G.opt_ttl )
457#define myid (G.myid )
458#define tmin (G.tmin )
459#define tmax (G.tmax )
460#define tsum (G.tsum )
461#define timeout (G.timeout )
462#define hostname (G.hostname )
463#define dotted (G.dotted )
464#define pingaddr (G.pingaddr )
465#define rcvd_tbl (G.rcvd_tbl )
466#define INIT_G() do { \
467 setup_common_bufsiz(); \
468 BUILD_BUG_ON(sizeof(G) > COMMON_BUFSIZE); \
469 datalen = DEFDATALEN; \
470 timeout = MAXWAIT; \
471 tmin = UINT_MAX; \
472} while (0)
473
474
475#define BYTE(bit) rcvd_tbl[(bit)>>3]
476#define MASK(bit) (1 << ((bit) & 7))
477#define SET(bit) (BYTE(bit) |= MASK(bit))
478#define CLR(bit) (BYTE(bit) &= (~MASK(bit)))
479#define TST(bit) (BYTE(bit) & MASK(bit))
480
481static void print_stats_and_exit(int junk) NORETURN;
482static void print_stats_and_exit(int junk UNUSED_PARAM)
483{
484 unsigned long ul;
485 unsigned long nrecv;
486
487 signal(SIGINT, SIG_IGN);
488
489 nrecv = G.nreceived;
490 printf("\n--- %s ping statistics ---\n"
491 "%lu packets transmitted, "
492 "%lu packets received, ",
493 hostname, G.ntransmitted, nrecv
494 );
495 if (G.nrepeats)
496 printf("%lu duplicates, ", G.nrepeats);
497 ul = G.ntransmitted;
498 if (ul != 0)
499 ul = (ul - nrecv) * 100 / ul;
500 printf("%lu%% packet loss\n", ul);
501 if (tmin != UINT_MAX) {
502 unsigned tavg = tsum / (nrecv + G.nrepeats);
503 printf("round-trip min/avg/max = %u.%03u/%u.%03u/%u.%03u ms\n",
504 tmin / 1000, tmin % 1000,
505 tavg / 1000, tavg % 1000,
506 tmax / 1000, tmax % 1000);
507 }
508
509 exit(nrecv == 0 || (G.deadline_us && nrecv < pingcount));
510}
511
512static void sendping_tail(void (*sp)(int), int size_pkt)
513{
514 int sz;
515
516 if (G.deadline_us) {
517 unsigned n = G.cur_us - G.deadline_us;
518 if ((int)n >= 0)
519 print_stats_and_exit(0);
520 }
521
522 CLR((uint16_t)G.ntransmitted % MAX_DUP_CHK);
523 G.ntransmitted++;
524 size_pkt += datalen;
525
526
527
528 sz = xsendto(pingsock, G.snd_packet, size_pkt, &pingaddr.sa, sizeof(pingaddr));
529 if (sz != size_pkt)
530 bb_simple_error_msg_and_die(bb_msg_write_error);
531
532 if (pingcount == 0 || G.ntransmitted < pingcount) {
533
534 struct itimerval i;
535 signal(SIGALRM, sp);
536
537 i.it_interval.tv_sec = 0;
538 i.it_interval.tv_usec = 0;
539 i.it_value.tv_sec = G.interval_us / 1000000;
540 i.it_value.tv_usec = G.interval_us % 1000000;
541 setitimer(ITIMER_REAL, &i, NULL);
542 } else {
543
544
545
546
547 unsigned expire = timeout;
548
549 if (G.nreceived) {
550
551 expire = tmax / (512*1024);
552 if (expire == 0)
553 expire = 1;
554 }
555 signal(SIGALRM, print_stats_and_exit);
556 alarm(expire);
557 }
558}
559
560static void sendping4(int junk UNUSED_PARAM)
561{
562 struct icmp *pkt = G.snd_packet;
563
564 memset(pkt, G.pattern, datalen + ICMP_MINLEN + 4);
565 pkt->icmp_type = ICMP_ECHO;
566
567 pkt->icmp_cksum = 0;
568 pkt->icmp_seq = htons(G.ntransmitted);
569 pkt->icmp_id = myid;
570
571
572
573
574
575
576 *(uint32_t*)&pkt->icmp_dun = G.cur_us = monotonic_us();
577
578 pkt->icmp_cksum = inet_cksum(pkt, datalen + ICMP_MINLEN);
579
580 sendping_tail(sendping4, ICMP_MINLEN);
581}
582#if ENABLE_PING6
583static void sendping6(int junk UNUSED_PARAM)
584{
585 struct icmp6_hdr *pkt = G.snd_packet;
586
587 memset(pkt, G.pattern, datalen + sizeof(struct icmp6_hdr) + 4);
588 pkt->icmp6_type = ICMP6_ECHO_REQUEST;
589
590
591 pkt->icmp6_seq = htons(G.ntransmitted);
592 pkt->icmp6_id = myid;
593
594
595 *(bb__aliased_uint32_t*)(&pkt->icmp6_data8[4]) = G.cur_us = monotonic_us();
596
597
598
599 sendping_tail(sendping6, sizeof(struct icmp6_hdr));
600}
601#endif
602
603static const char *icmp_type_name(int id)
604{
605 switch (id) {
606 case ICMP_ECHOREPLY: return "Echo Reply";
607 case ICMP_DEST_UNREACH: return "Destination Unreachable";
608 case ICMP_SOURCE_QUENCH: return "Source Quench";
609 case ICMP_REDIRECT: return "Redirect (change route)";
610 case ICMP_ECHO: return "Echo Request";
611 case ICMP_TIME_EXCEEDED: return "Time Exceeded";
612 case ICMP_PARAMETERPROB: return "Parameter Problem";
613 case ICMP_TIMESTAMP: return "Timestamp Request";
614 case ICMP_TIMESTAMPREPLY: return "Timestamp Reply";
615 case ICMP_INFO_REQUEST: return "Information Request";
616 case ICMP_INFO_REPLY: return "Information Reply";
617 case ICMP_ADDRESS: return "Address Mask Request";
618 case ICMP_ADDRESSREPLY: return "Address Mask Reply";
619 default: return "unknown ICMP type";
620 }
621}
622#if ENABLE_PING6
623
624
625#ifndef MLD_LISTENER_QUERY
626# define MLD_LISTENER_QUERY ICMP6_MEMBERSHIP_QUERY
627#endif
628#ifndef MLD_LISTENER_REPORT
629# define MLD_LISTENER_REPORT ICMP6_MEMBERSHIP_REPORT
630#endif
631#ifndef MLD_LISTENER_REDUCTION
632# define MLD_LISTENER_REDUCTION ICMP6_MEMBERSHIP_REDUCTION
633#endif
634static const char *icmp6_type_name(int id)
635{
636 switch (id) {
637 case ICMP6_DST_UNREACH: return "Destination Unreachable";
638 case ICMP6_PACKET_TOO_BIG: return "Packet too big";
639 case ICMP6_TIME_EXCEEDED: return "Time Exceeded";
640 case ICMP6_PARAM_PROB: return "Parameter Problem";
641 case ICMP6_ECHO_REPLY: return "Echo Reply";
642 case ICMP6_ECHO_REQUEST: return "Echo Request";
643 case MLD_LISTENER_QUERY: return "Listener Query";
644 case MLD_LISTENER_REPORT: return "Listener Report";
645 case MLD_LISTENER_REDUCTION: return "Listener Reduction";
646 default: return "unknown ICMP type";
647 }
648}
649#endif
650
651static void unpack_tail(int sz, uint32_t *tp,
652 const char *from_str,
653 uint16_t recv_seq, int ttl)
654{
655 unsigned char *b, m;
656 const char *dupmsg = " (DUP!)";
657 unsigned triptime = triptime;
658
659 if (tp) {
660
661
662 triptime = (int32_t) ((uint32_t)monotonic_us() - *tp);
663 tsum += triptime;
664 if (triptime < tmin)
665 tmin = triptime;
666 if (triptime > tmax)
667 tmax = triptime;
668 }
669
670 b = &BYTE(recv_seq % MAX_DUP_CHK);
671 m = MASK(recv_seq % MAX_DUP_CHK);
672
673 if (*b & m) {
674 ++G.nrepeats;
675 } else {
676
677 *b |= m;
678 ++G.nreceived;
679 dupmsg += 7;
680 }
681
682 if (option_mask32 & OPT_QUIET)
683 return;
684
685 printf("%d bytes from %s: seq=%u ttl=%d", sz,
686 from_str, recv_seq, ttl);
687 if (tp)
688 printf(" time=%u.%03u ms", triptime / 1000, triptime % 1000);
689 puts(dupmsg);
690 fflush_all();
691}
692static int unpack4(char *buf, int sz, struct sockaddr_in *from)
693{
694 struct icmp *icmppkt;
695 struct iphdr *iphdr;
696 int hlen;
697
698
699 if (sz < (datalen + ICMP_MINLEN))
700 return 0;
701
702
703 iphdr = (struct iphdr *) buf;
704 hlen = iphdr->ihl << 2;
705 sz -= hlen;
706 icmppkt = (struct icmp *) (buf + hlen);
707 if (icmppkt->icmp_id != myid)
708 return 0;
709
710 if (icmppkt->icmp_type == ICMP_ECHOREPLY) {
711 uint16_t recv_seq = ntohs(icmppkt->icmp_seq);
712 uint32_t *tp = NULL;
713
714 if (sz >= ICMP_MINLEN + sizeof(uint32_t))
715 tp = (uint32_t *) icmppkt->icmp_data;
716 unpack_tail(sz, tp,
717 inet_ntoa(*(struct in_addr *) &from->sin_addr.s_addr),
718 recv_seq, iphdr->ttl);
719 return 1;
720 }
721 if (icmppkt->icmp_type != ICMP_ECHO) {
722 bb_error_msg("warning: got ICMP %d (%s)",
723 icmppkt->icmp_type,
724 icmp_type_name(icmppkt->icmp_type));
725 }
726 return 0;
727}
728#if ENABLE_PING6
729static int unpack6(char *packet, int sz, struct sockaddr_in6 *from, int hoplimit)
730{
731 struct icmp6_hdr *icmppkt;
732 char buf[INET6_ADDRSTRLEN];
733
734
735 if (sz < (datalen + sizeof(struct icmp6_hdr)))
736 return 0;
737
738 icmppkt = (struct icmp6_hdr *) packet;
739 if (icmppkt->icmp6_id != myid)
740 return 0;
741
742 if (icmppkt->icmp6_type == ICMP6_ECHO_REPLY) {
743 uint16_t recv_seq = ntohs(icmppkt->icmp6_seq);
744 uint32_t *tp = NULL;
745
746 if (sz >= sizeof(struct icmp6_hdr) + sizeof(uint32_t))
747 tp = (uint32_t *) &icmppkt->icmp6_data8[4];
748 unpack_tail(sz, tp,
749 inet_ntop(AF_INET6, &from->sin6_addr,
750 buf, sizeof(buf)),
751 recv_seq, hoplimit);
752 return 1;
753 }
754 if (icmppkt->icmp6_type != ICMP6_ECHO_REQUEST) {
755 bb_error_msg("warning: got ICMP %d (%s)",
756 icmppkt->icmp6_type,
757 icmp6_type_name(icmppkt->icmp6_type));
758 }
759 return 0;
760}
761#endif
762
763static void ping4(len_and_sockaddr *lsa)
764{
765 int sockopt;
766
767 pingaddr.sin = lsa->u.sin;
768 if (source_lsa) {
769 if (setsockopt(pingsock, IPPROTO_IP, IP_MULTICAST_IF,
770 &source_lsa->u.sa, source_lsa->len))
771 bb_simple_error_msg_and_die("can't set multicast source interface");
772 xbind(pingsock, &source_lsa->u.sa, source_lsa->len);
773 }
774
775
776 setsockopt_broadcast(pingsock);
777
778
779
780 sockopt = (datalen * 2) + 7 * 1024;
781 setsockopt_SOL_SOCKET_int(pingsock, SO_RCVBUF, sockopt);
782
783 if (opt_ttl != 0) {
784 setsockopt_int(pingsock, IPPROTO_IP, IP_TTL, opt_ttl);
785
786 setsockopt_int(pingsock, IPPROTO_IP, IP_MULTICAST_TTL, opt_ttl);
787 }
788
789 signal(SIGINT, print_stats_and_exit);
790
791
792 send_ping:
793 sendping4(0);
794
795
796 while (1) {
797 struct sockaddr_in from;
798 socklen_t fromlen = (socklen_t) sizeof(from);
799 int c;
800
801 c = recvfrom(pingsock, G.rcv_packet, G.sizeof_rcv_packet, 0,
802 (struct sockaddr *) &from, &fromlen);
803 if (c < 0) {
804 if (errno != EINTR)
805 bb_simple_perror_msg("recvfrom");
806 continue;
807 }
808 c = unpack4(G.rcv_packet, c, &from);
809 if (pingcount && G.nreceived >= pingcount)
810 break;
811 if (c && (option_mask32 & OPT_A)) {
812 goto send_ping;
813 }
814 }
815}
816#if ENABLE_PING6
817static void ping6(len_and_sockaddr *lsa)
818{
819 int sockopt;
820 struct msghdr msg;
821 struct sockaddr_in6 from;
822 struct iovec iov;
823 char control_buf[CMSG_SPACE(36)];
824
825 pingaddr.sin6 = lsa->u.sin6;
826 if (source_lsa)
827 xbind(pingsock, &source_lsa->u.sa, source_lsa->len);
828
829#ifdef ICMP6_FILTER
830 {
831 struct icmp6_filter filt;
832 if (!(option_mask32 & OPT_VERBOSE)) {
833 ICMP6_FILTER_SETBLOCKALL(&filt);
834 ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &filt);
835 } else {
836 ICMP6_FILTER_SETPASSALL(&filt);
837 }
838 if (setsockopt(pingsock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt,
839 sizeof(filt)) < 0)
840 bb_error_msg_and_die("setsockopt(%s)", "ICMP6_FILTER");
841 }
842#endif
843
844
845 setsockopt_broadcast(pingsock);
846
847
848
849 sockopt = (datalen * 2) + 7 * 1024;
850 setsockopt_SOL_SOCKET_int(pingsock, SO_RCVBUF, sockopt);
851
852 sockopt = offsetof(struct icmp6_hdr, icmp6_cksum);
853 BUILD_BUG_ON(offsetof(struct icmp6_hdr, icmp6_cksum) != 2);
854 setsockopt_int(pingsock, SOL_RAW, IPV6_CHECKSUM, sockopt);
855
856
857 setsockopt_1(pingsock, SOL_IPV6, IPV6_HOPLIMIT);
858
859 if (if_index)
860 pingaddr.sin6.sin6_scope_id = if_index;
861
862 signal(SIGINT, print_stats_and_exit);
863
864 msg.msg_name = &from;
865 msg.msg_namelen = sizeof(from);
866 msg.msg_iov = &iov;
867 msg.msg_iovlen = 1;
868 msg.msg_control = control_buf;
869 iov.iov_base = G.rcv_packet;
870 iov.iov_len = G.sizeof_rcv_packet;
871
872
873 send_ping:
874 sendping6(0);
875
876
877 while (1) {
878 int c;
879 struct cmsghdr *mp;
880 int hoplimit = -1;
881
882 msg.msg_controllen = sizeof(control_buf);
883 c = recvmsg(pingsock, &msg, 0);
884 if (c < 0) {
885 if (errno != EINTR)
886 bb_simple_perror_msg("recvfrom");
887 continue;
888 }
889 for (mp = CMSG_FIRSTHDR(&msg); mp; mp = CMSG_NXTHDR(&msg, mp)) {
890 if (mp->cmsg_level == SOL_IPV6
891 && mp->cmsg_type == IPV6_HOPLIMIT
892
893
894 ) {
895
896 move_from_unaligned_int(hoplimit, CMSG_DATA(mp));
897 }
898 }
899 c = unpack6(G.rcv_packet, c, &from, hoplimit);
900 if (pingcount && G.nreceived >= pingcount)
901 break;
902 if (c && (option_mask32 & OPT_A)) {
903 goto send_ping;
904 }
905 }
906}
907#endif
908
909static void ping(len_and_sockaddr *lsa)
910{
911 printf("PING %s (%s)", hostname, dotted);
912 if (source_lsa) {
913 printf(" from %s",
914 xmalloc_sockaddr2dotted_noport(&source_lsa->u.sa));
915 }
916 printf(": %d data bytes\n", datalen);
917
918 create_icmp_socket(lsa);
919
920 if (str_I)
921 setsockopt_bindtodevice(pingsock, str_I);
922
923 G.sizeof_rcv_packet = datalen + MAXIPLEN + MAXICMPLEN;
924 G.rcv_packet = xzalloc(G.sizeof_rcv_packet);
925#if ENABLE_PING6
926 if (lsa->u.sa.sa_family == AF_INET6) {
927
928
929 G.snd_packet = xzalloc(datalen + sizeof(struct icmp6_hdr) + 4);
930 ping6(lsa);
931 } else
932#endif
933 {
934 G.snd_packet = xzalloc(datalen + ICMP_MINLEN + 4);
935 ping4(lsa);
936 }
937}
938
939static int common_ping_main(int opt, char **argv)
940{
941 len_and_sockaddr *lsa;
942 char *str_s, *str_p;
943 char *str_i = (char*)"1";
944 duration_t interval;
945
946 INIT_G();
947
948 opt |= getopt32(argv, "^"
949 OPT_STRING
950
951 "\0" "=1:q--v:v--q",
952 &pingcount, &str_s, &opt_ttl, &G.deadline_us, &timeout, &str_I, &str_p, &str_i
953 );
954 if (opt & OPT_s)
955 datalen = xatou16(str_s);
956 if (opt & OPT_I) {
957 if_index = if_nametoindex(str_I);
958 if (!if_index) {
959
960 source_lsa = xdotted2sockaddr(str_I, 0);
961 str_I = NULL;
962 }
963 }
964 if (opt & OPT_p)
965 G.pattern = xstrtou_range(str_p, 16, 0, 255);
966 if (G.deadline_us) {
967 unsigned d = G.deadline_us < INT_MAX/1000000 ? G.deadline_us : INT_MAX/1000000;
968 G.deadline_us = 1 | ((d * 1000000) + monotonic_us());
969 }
970 interval = parse_duration_str(str_i);
971 if (interval > INT_MAX/1000000)
972 interval = INT_MAX/1000000;
973 G.interval_us = interval * 1000000;
974
975 myid = (uint16_t) getpid();
976
977
978
979
980 myid = htons(myid);
981 hostname = argv[optind];
982#if ENABLE_PING6
983 {
984 sa_family_t af = AF_UNSPEC;
985 if (opt & OPT_IPV4)
986 af = AF_INET;
987 if (opt & OPT_IPV6)
988 af = AF_INET6;
989 lsa = xhost_and_af2sockaddr(hostname, 0, af);
990 }
991#else
992 lsa = xhost_and_af2sockaddr(hostname, 0, AF_INET);
993#endif
994
995 if (source_lsa && source_lsa->u.sa.sa_family != lsa->u.sa.sa_family)
996
997 source_lsa = NULL;
998
999 dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
1000 ping(lsa);
1001 print_stats_and_exit(0);
1002
1003}
1004#endif
1005
1006
1007#if ENABLE_PING
1008int ping_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1009int ping_main(int argc UNUSED_PARAM, char **argv)
1010{
1011# if !ENABLE_FEATURE_FANCY_PING
1012 return common_ping_main(AF_UNSPEC, argv);
1013# else
1014 return common_ping_main(0, argv);
1015# endif
1016}
1017#endif
1018
1019#if ENABLE_PING6
1020int ping6_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1021int ping6_main(int argc UNUSED_PARAM, char **argv)
1022{
1023# if !ENABLE_FEATURE_FANCY_PING
1024 return common_ping_main(AF_INET6, argv);
1025# else
1026 return common_ping_main(OPT_IPV6, argv);
1027# endif
1028}
1029#endif
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066