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