source: MondoRescue/branches/3.3/mindi-busybox/networking/ping.c@ 3734

Last change on this file since 3734 was 3621, checked in by Bruno Cornec, 10 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

File size: 27.1 KB
Line 
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 source tree.
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 "libbb.h"
31#include "common_bufsiz.h"
32
33#ifdef __BIONIC__
34/* should be in netinet/ip_icmp.h */
35# define ICMP_DEST_UNREACH 3 /* Destination Unreachable */
36# define ICMP_SOURCE_QUENCH 4 /* Source Quench */
37# define ICMP_REDIRECT 5 /* Redirect (change route) */
38# define ICMP_ECHO 8 /* Echo Request */
39# define ICMP_TIME_EXCEEDED 11 /* Time Exceeded */
40# define ICMP_PARAMETERPROB 12 /* Parameter Problem */
41# define ICMP_TIMESTAMP 13 /* Timestamp Request */
42# define ICMP_TIMESTAMPREPLY 14 /* Timestamp Reply */
43# define ICMP_INFO_REQUEST 15 /* Information Request */
44# define ICMP_INFO_REPLY 16 /* Information Reply */
45# define ICMP_ADDRESS 17 /* Address Mask Request */
46# define ICMP_ADDRESSREPLY 18 /* Address Mask Reply */
47#endif
48
49//config:config PING
50//config: bool "ping"
51//config: default y
52//config: select PLATFORM_LINUX
53//config: help
54//config: ping uses the ICMP protocol's mandatory ECHO_REQUEST datagram to
55//config: elicit an ICMP ECHO_RESPONSE from a host or gateway.
56//config:
57//config:config PING6
58//config: bool "ping6"
59//config: default y
60//config: depends on FEATURE_IPV6 && PING
61//config: help
62//config: This will give you a ping that can talk IPv6.
63//config:
64//config:config FEATURE_FANCY_PING
65//config: bool "Enable fancy ping output"
66//config: default y
67//config: depends on PING
68//config: help
69//config: Make the output from the ping applet include statistics, and at the
70//config: same time provide full support for ICMP packets.
71
72/* Needs socket(AF_INET, SOCK_RAW, IPPROTO_ICMP), therefore BB_SUID_MAYBE: */
73//applet:IF_PING(APPLET(ping, BB_DIR_BIN, BB_SUID_MAYBE))
74//applet:IF_PING6(APPLET(ping6, BB_DIR_BIN, BB_SUID_MAYBE))
75
76//kbuild:lib-$(CONFIG_PING) += ping.o
77//kbuild:lib-$(CONFIG_PING6) += ping.o
78
79//usage:#if !ENABLE_FEATURE_FANCY_PING
80//usage:# define ping_trivial_usage
81//usage: "HOST"
82//usage:# define ping_full_usage "\n\n"
83//usage: "Send ICMP ECHO_REQUEST packets to network hosts"
84//usage:# define ping6_trivial_usage
85//usage: "HOST"
86//usage:# define ping6_full_usage "\n\n"
87//usage: "Send ICMP ECHO_REQUEST packets to network hosts"
88//usage:#else
89//usage:# define ping_trivial_usage
90//usage: "[OPTIONS] HOST"
91//usage:# define ping_full_usage "\n\n"
92//usage: "Send ICMP ECHO_REQUEST packets to network hosts\n"
93//usage: IF_PING6(
94//usage: "\n -4,-6 Force IP or IPv6 name resolution"
95//usage: )
96//usage: "\n -c CNT Send only CNT pings"
97//usage: "\n -s SIZE Send SIZE data bytes in packets (default:56)"
98//usage: "\n -t TTL Set TTL"
99//usage: "\n -I IFACE/IP Use interface or IP address as source"
100//usage: "\n -W SEC Seconds to wait for the first response (default:10)"
101//usage: "\n (after all -c CNT packets are sent)"
102//usage: "\n -w SEC Seconds until ping exits (default:infinite)"
103//usage: "\n (can exit earlier with -c CNT)"
104//usage: "\n -q Quiet, only display output at start"
105//usage: "\n and when finished"
106//usage: "\n -p Pattern to use for payload"
107//usage:
108//usage:# define ping6_trivial_usage
109//usage: "[OPTIONS] HOST"
110//usage:# define ping6_full_usage "\n\n"
111//usage: "Send ICMP ECHO_REQUEST packets to network hosts\n"
112//usage: "\n -c CNT Send only CNT pings"
113//usage: "\n -s SIZE Send SIZE data bytes in packets (default:56)"
114//usage: "\n -I IFACE/IP Use interface or IP address as source"
115//usage: "\n -q Quiet, only display output at start"
116//usage: "\n and when finished"
117//usage: "\n -p Pattern to use for payload"
118//usage:
119//usage:#endif
120//usage:
121//usage:#define ping_example_usage
122//usage: "$ ping localhost\n"
123//usage: "PING slag (127.0.0.1): 56 data bytes\n"
124//usage: "64 bytes from 127.0.0.1: icmp_seq=0 ttl=255 time=20.1 ms\n"
125//usage: "\n"
126//usage: "--- debian ping statistics ---\n"
127//usage: "1 packets transmitted, 1 packets received, 0% packet loss\n"
128//usage: "round-trip min/avg/max = 20.1/20.1/20.1 ms\n"
129//usage:#define ping6_example_usage
130//usage: "$ ping6 ip6-localhost\n"
131//usage: "PING ip6-localhost (::1): 56 data bytes\n"
132//usage: "64 bytes from ::1: icmp6_seq=0 ttl=64 time=20.1 ms\n"
133//usage: "\n"
134//usage: "--- ip6-localhost ping statistics ---\n"
135//usage: "1 packets transmitted, 1 packets received, 0% packet loss\n"
136//usage: "round-trip min/avg/max = 20.1/20.1/20.1 ms\n"
137
138#if ENABLE_PING6
139# include <netinet/icmp6.h>
140/* I see RENUMBERED constants in bits/in.h - !!?
141 * What a fuck is going on with libc? Is it a glibc joke? */
142# ifdef IPV6_2292HOPLIMIT
143# undef IPV6_HOPLIMIT
144# define IPV6_HOPLIMIT IPV6_2292HOPLIMIT
145# endif
146#endif
147
148enum {
149 DEFDATALEN = 56,
150 MAXIPLEN = 60,
151 MAXICMPLEN = 76,
152 MAX_DUP_CHK = (8 * 128),
153 MAXWAIT = 10,
154 PINGINTERVAL = 1, /* 1 second */
155 pingsock = 0,
156};
157
158static void
159#if ENABLE_PING6
160create_icmp_socket(len_and_sockaddr *lsa)
161#else
162create_icmp_socket(void)
163#define create_icmp_socket(lsa) create_icmp_socket()
164#endif
165{
166 int sock;
167#if ENABLE_PING6
168 if (lsa->u.sa.sa_family == AF_INET6)
169 sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
170 else
171#endif
172 sock = socket(AF_INET, SOCK_RAW, 1); /* 1 == ICMP */
173 if (sock < 0) {
174 if (errno == EPERM)
175 bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
176 bb_perror_msg_and_die(bb_msg_can_not_create_raw_socket);
177 }
178
179 xmove_fd(sock, pingsock);
180}
181
182#if !ENABLE_FEATURE_FANCY_PING
183
184/* Simple version */
185
186struct globals {
187 char *hostname;
188 char packet[DEFDATALEN + MAXIPLEN + MAXICMPLEN];
189} FIX_ALIASING;
190#define G (*(struct globals*)bb_common_bufsiz1)
191#define INIT_G() do { setup_common_bufsiz(); } while (0)
192
193static void noresp(int ign UNUSED_PARAM)
194{
195 printf("No response from %s\n", G.hostname);
196 exit(EXIT_FAILURE);
197}
198
199static void ping4(len_and_sockaddr *lsa)
200{
201 struct icmp *pkt;
202 int c;
203
204 pkt = (struct icmp *) G.packet;
205 /*memset(pkt, 0, sizeof(G.packet)); already is */
206 pkt->icmp_type = ICMP_ECHO;
207 pkt->icmp_cksum = inet_cksum((uint16_t *) pkt, sizeof(G.packet));
208
209 xsendto(pingsock, G.packet, DEFDATALEN + ICMP_MINLEN, &lsa->u.sa, lsa->len);
210
211 /* listen for replies */
212 while (1) {
213#if 0
214 struct sockaddr_in from;
215 socklen_t fromlen = sizeof(from);
216
217 c = recvfrom(pingsock, G.packet, sizeof(G.packet), 0,
218 (struct sockaddr *) &from, &fromlen);
219#else
220 c = recv(pingsock, G.packet, sizeof(G.packet), 0);
221#endif
222 if (c < 0) {
223 if (errno != EINTR)
224 bb_perror_msg("recvfrom");
225 continue;
226 }
227 if (c >= 76) { /* ip + icmp */
228 struct iphdr *iphdr = (struct iphdr *) G.packet;
229
230 pkt = (struct icmp *) (G.packet + (iphdr->ihl << 2)); /* skip ip hdr */
231 if (pkt->icmp_type == ICMP_ECHOREPLY)
232 break;
233 }
234 }
235 if (ENABLE_FEATURE_CLEAN_UP)
236 close(pingsock);
237}
238
239#if ENABLE_PING6
240static void ping6(len_and_sockaddr *lsa)
241{
242 struct icmp6_hdr *pkt;
243 int c;
244 int sockopt;
245
246 pkt = (struct icmp6_hdr *) G.packet;
247 /*memset(pkt, 0, sizeof(G.packet)); already is */
248 pkt->icmp6_type = ICMP6_ECHO_REQUEST;
249
250 sockopt = offsetof(struct icmp6_hdr, icmp6_cksum);
251 setsockopt_int(pingsock, SOL_RAW, IPV6_CHECKSUM, sockopt);
252
253 xsendto(pingsock, G.packet, DEFDATALEN + sizeof(struct icmp6_hdr), &lsa->u.sa, lsa->len);
254
255 /* listen for replies */
256 while (1) {
257#if 0
258 struct sockaddr_in6 from;
259 socklen_t fromlen = sizeof(from);
260
261 c = recvfrom(pingsock, G.packet, sizeof(G.packet), 0,
262 (struct sockaddr *) &from, &fromlen);
263#else
264 c = recv(pingsock, G.packet, sizeof(G.packet), 0);
265#endif
266 if (c < 0) {
267 if (errno != EINTR)
268 bb_perror_msg("recvfrom");
269 continue;
270 }
271 if (c >= ICMP_MINLEN) { /* icmp6_hdr */
272 if (pkt->icmp6_type == ICMP6_ECHO_REPLY)
273 break;
274 }
275 }
276 if (ENABLE_FEATURE_CLEAN_UP)
277 close(pingsock);
278}
279#endif
280
281#if !ENABLE_PING6
282# define common_ping_main(af, argv) common_ping_main(argv)
283#endif
284static int common_ping_main(sa_family_t af, char **argv)
285{
286 len_and_sockaddr *lsa;
287
288 INIT_G();
289
290#if ENABLE_PING6
291 while ((++argv)[0] && argv[0][0] == '-') {
292 if (argv[0][1] == '4') {
293 af = AF_INET;
294 continue;
295 }
296 if (argv[0][1] == '6') {
297 af = AF_INET6;
298 continue;
299 }
300 bb_show_usage();
301 }
302#else
303 argv++;
304#endif
305
306 G.hostname = *argv;
307 if (!G.hostname)
308 bb_show_usage();
309
310#if ENABLE_PING6
311 lsa = xhost_and_af2sockaddr(G.hostname, 0, af);
312#else
313 lsa = xhost_and_af2sockaddr(G.hostname, 0, AF_INET);
314#endif
315 /* Set timer _after_ DNS resolution */
316 signal(SIGALRM, noresp);
317 alarm(5); /* give the host 5000ms to respond */
318
319 create_icmp_socket(lsa);
320#if ENABLE_PING6
321 if (lsa->u.sa.sa_family == AF_INET6)
322 ping6(lsa);
323 else
324#endif
325 ping4(lsa);
326 printf("%s is alive!\n", G.hostname);
327 return EXIT_SUCCESS;
328}
329
330
331#else /* FEATURE_FANCY_PING */
332
333
334/* Full(er) version */
335
336#define OPT_STRING ("qvc:s:t:w:W:I:np:4" IF_PING6("6"))
337enum {
338 OPT_QUIET = 1 << 0,
339 OPT_VERBOSE = 1 << 1,
340 OPT_c = 1 << 2,
341 OPT_s = 1 << 3,
342 OPT_t = 1 << 4,
343 OPT_w = 1 << 5,
344 OPT_W = 1 << 6,
345 OPT_I = 1 << 7,
346 /*OPT_n = 1 << 8, - ignored */
347 OPT_p = 1 << 9,
348 OPT_IPV4 = 1 << 10,
349 OPT_IPV6 = (1 << 11) * ENABLE_PING6,
350};
351
352
353struct globals {
354 int if_index;
355 char *str_I;
356 len_and_sockaddr *source_lsa;
357 unsigned datalen;
358 unsigned pingcount; /* must be int-sized */
359 unsigned opt_ttl;
360 unsigned long ntransmitted, nreceived, nrepeats;
361 uint16_t myid;
362 uint8_t pattern;
363 unsigned tmin, tmax; /* in us */
364 unsigned long long tsum; /* in us, sum of all times */
365 unsigned deadline;
366 unsigned timeout;
367 unsigned total_secs;
368 unsigned sizeof_rcv_packet;
369 char *rcv_packet; /* [datalen + MAXIPLEN + MAXICMPLEN] */
370 void *snd_packet; /* [datalen + ipv4/ipv6_const] */
371 const char *hostname;
372 const char *dotted;
373 union {
374 struct sockaddr sa;
375 struct sockaddr_in sin;
376#if ENABLE_PING6
377 struct sockaddr_in6 sin6;
378#endif
379 } pingaddr;
380 unsigned char rcvd_tbl[MAX_DUP_CHK / 8];
381} FIX_ALIASING;
382#define G (*(struct globals*)bb_common_bufsiz1)
383#define if_index (G.if_index )
384#define source_lsa (G.source_lsa )
385#define str_I (G.str_I )
386#define datalen (G.datalen )
387#define pingcount (G.pingcount )
388#define opt_ttl (G.opt_ttl )
389#define myid (G.myid )
390#define tmin (G.tmin )
391#define tmax (G.tmax )
392#define tsum (G.tsum )
393#define deadline (G.deadline )
394#define timeout (G.timeout )
395#define total_secs (G.total_secs )
396#define hostname (G.hostname )
397#define dotted (G.dotted )
398#define pingaddr (G.pingaddr )
399#define rcvd_tbl (G.rcvd_tbl )
400#define INIT_G() do { \
401 setup_common_bufsiz(); \
402 BUILD_BUG_ON(sizeof(G) > COMMON_BUFSIZE); \
403 datalen = DEFDATALEN; \
404 timeout = MAXWAIT; \
405 tmin = UINT_MAX; \
406} while (0)
407
408
409#define BYTE(bit) rcvd_tbl[(bit)>>3]
410#define MASK(bit) (1 << ((bit) & 7))
411#define SET(bit) (BYTE(bit) |= MASK(bit))
412#define CLR(bit) (BYTE(bit) &= (~MASK(bit)))
413#define TST(bit) (BYTE(bit) & MASK(bit))
414
415static void print_stats_and_exit(int junk) NORETURN;
416static void print_stats_and_exit(int junk UNUSED_PARAM)
417{
418 unsigned long ul;
419 unsigned long nrecv;
420
421 signal(SIGINT, SIG_IGN);
422
423 nrecv = G.nreceived;
424 printf("\n--- %s ping statistics ---\n"
425 "%lu packets transmitted, "
426 "%lu packets received, ",
427 hostname, G.ntransmitted, nrecv
428 );
429 if (G.nrepeats)
430 printf("%lu duplicates, ", G.nrepeats);
431 ul = G.ntransmitted;
432 if (ul != 0)
433 ul = (ul - nrecv) * 100 / ul;
434 printf("%lu%% packet loss\n", ul);
435 if (tmin != UINT_MAX) {
436 unsigned tavg = tsum / (nrecv + G.nrepeats);
437 printf("round-trip min/avg/max = %u.%03u/%u.%03u/%u.%03u ms\n",
438 tmin / 1000, tmin % 1000,
439 tavg / 1000, tavg % 1000,
440 tmax / 1000, tmax % 1000);
441 }
442 /* if condition is true, exit with 1 -- 'failure' */
443 exit(nrecv == 0 || (deadline && nrecv < pingcount));
444}
445
446static void sendping_tail(void (*sp)(int), int size_pkt)
447{
448 int sz;
449
450 CLR((uint16_t)G.ntransmitted % MAX_DUP_CHK);
451 G.ntransmitted++;
452
453 size_pkt += datalen;
454
455 /* sizeof(pingaddr) can be larger than real sa size, but I think
456 * it doesn't matter */
457 sz = xsendto(pingsock, G.snd_packet, size_pkt, &pingaddr.sa, sizeof(pingaddr));
458 if (sz != size_pkt)
459 bb_error_msg_and_die(bb_msg_write_error);
460
461 if (pingcount == 0 || deadline || G.ntransmitted < pingcount) {
462 /* Didn't send all pings yet - schedule next in 1s */
463 signal(SIGALRM, sp);
464 if (deadline) {
465 total_secs += PINGINTERVAL;
466 if (total_secs >= deadline)
467 signal(SIGALRM, print_stats_and_exit);
468 }
469 alarm(PINGINTERVAL);
470 } else { /* -c NN, and all NN are sent (and no deadline) */
471 /* Wait for the last ping to come back.
472 * -W timeout: wait for a response in seconds.
473 * Affects only timeout in absense of any responses,
474 * otherwise ping waits for two RTTs. */
475 unsigned expire = timeout;
476
477 if (G.nreceived) {
478 /* approx. 2*tmax, in seconds (2 RTT) */
479 expire = tmax / (512*1024);
480 if (expire == 0)
481 expire = 1;
482 }
483 signal(SIGALRM, print_stats_and_exit);
484 alarm(expire);
485 }
486}
487
488static void sendping4(int junk UNUSED_PARAM)
489{
490 struct icmp *pkt = G.snd_packet;
491
492 memset(pkt, G.pattern, datalen + ICMP_MINLEN + 4);
493 pkt->icmp_type = ICMP_ECHO;
494 /*pkt->icmp_code = 0;*/
495 pkt->icmp_cksum = 0; /* cksum is calculated with this field set to 0 */
496 pkt->icmp_seq = htons(G.ntransmitted); /* don't ++ here, it can be a macro */
497 pkt->icmp_id = myid;
498
499 /* If datalen < 4, we store timestamp _past_ the packet,
500 * but it's ok - we allocated 4 extra bytes in xzalloc() just in case.
501 */
502 /*if (datalen >= 4)*/
503 /* No hton: we'll read it back on the same machine */
504 *(uint32_t*)&pkt->icmp_dun = monotonic_us();
505
506 pkt->icmp_cksum = inet_cksum((uint16_t *) pkt, datalen + ICMP_MINLEN);
507
508 sendping_tail(sendping4, ICMP_MINLEN);
509}
510#if ENABLE_PING6
511static void sendping6(int junk UNUSED_PARAM)
512{
513 struct icmp6_hdr *pkt = G.snd_packet;
514
515 memset(pkt, G.pattern, datalen + sizeof(struct icmp6_hdr) + 4);
516 pkt->icmp6_type = ICMP6_ECHO_REQUEST;
517 /*pkt->icmp6_code = 0;*/
518 /*pkt->icmp6_cksum = 0;*/
519 pkt->icmp6_seq = htons(G.ntransmitted); /* don't ++ here, it can be a macro */
520 pkt->icmp6_id = myid;
521
522 /*if (datalen >= 4)*/
523 *(bb__aliased_uint32_t*)(&pkt->icmp6_data8[4]) = monotonic_us();
524
525 //TODO? pkt->icmp_cksum = inet_cksum(...);
526
527 sendping_tail(sendping6, sizeof(struct icmp6_hdr));
528}
529#endif
530
531static const char *icmp_type_name(int id)
532{
533 switch (id) {
534 case ICMP_ECHOREPLY: return "Echo Reply";
535 case ICMP_DEST_UNREACH: return "Destination Unreachable";
536 case ICMP_SOURCE_QUENCH: return "Source Quench";
537 case ICMP_REDIRECT: return "Redirect (change route)";
538 case ICMP_ECHO: return "Echo Request";
539 case ICMP_TIME_EXCEEDED: return "Time Exceeded";
540 case ICMP_PARAMETERPROB: return "Parameter Problem";
541 case ICMP_TIMESTAMP: return "Timestamp Request";
542 case ICMP_TIMESTAMPREPLY: return "Timestamp Reply";
543 case ICMP_INFO_REQUEST: return "Information Request";
544 case ICMP_INFO_REPLY: return "Information Reply";
545 case ICMP_ADDRESS: return "Address Mask Request";
546 case ICMP_ADDRESSREPLY: return "Address Mask Reply";
547 default: return "unknown ICMP type";
548 }
549}
550#if ENABLE_PING6
551/* RFC3542 changed some definitions from RFC2292 for no good reason, whee!
552 * the newer 3542 uses a MLD_ prefix where as 2292 uses ICMP6_ prefix */
553#ifndef MLD_LISTENER_QUERY
554# define MLD_LISTENER_QUERY ICMP6_MEMBERSHIP_QUERY
555#endif
556#ifndef MLD_LISTENER_REPORT
557# define MLD_LISTENER_REPORT ICMP6_MEMBERSHIP_REPORT
558#endif
559#ifndef MLD_LISTENER_REDUCTION
560# define MLD_LISTENER_REDUCTION ICMP6_MEMBERSHIP_REDUCTION
561#endif
562static const char *icmp6_type_name(int id)
563{
564 switch (id) {
565 case ICMP6_DST_UNREACH: return "Destination Unreachable";
566 case ICMP6_PACKET_TOO_BIG: return "Packet too big";
567 case ICMP6_TIME_EXCEEDED: return "Time Exceeded";
568 case ICMP6_PARAM_PROB: return "Parameter Problem";
569 case ICMP6_ECHO_REPLY: return "Echo Reply";
570 case ICMP6_ECHO_REQUEST: return "Echo Request";
571 case MLD_LISTENER_QUERY: return "Listener Query";
572 case MLD_LISTENER_REPORT: return "Listener Report";
573 case MLD_LISTENER_REDUCTION: return "Listener Reduction";
574 default: return "unknown ICMP type";
575 }
576}
577#endif
578
579static void unpack_tail(int sz, uint32_t *tp,
580 const char *from_str,
581 uint16_t recv_seq, int ttl)
582{
583 unsigned char *b, m;
584 const char *dupmsg = " (DUP!)";
585 unsigned triptime = triptime; /* for gcc */
586
587 if (tp) {
588 /* (int32_t) cast is for hypothetical 64-bit unsigned */
589 /* (doesn't hurt 32-bit real-world anyway) */
590 triptime = (int32_t) ((uint32_t)monotonic_us() - *tp);
591 tsum += triptime;
592 if (triptime < tmin)
593 tmin = triptime;
594 if (triptime > tmax)
595 tmax = triptime;
596 }
597
598 b = &BYTE(recv_seq % MAX_DUP_CHK);
599 m = MASK(recv_seq % MAX_DUP_CHK);
600 /*if TST(recv_seq % MAX_DUP_CHK):*/
601 if (*b & m) {
602 ++G.nrepeats;
603 } else {
604 /*SET(recv_seq % MAX_DUP_CHK):*/
605 *b |= m;
606 ++G.nreceived;
607 dupmsg += 7;
608 }
609
610 if (option_mask32 & OPT_QUIET)
611 return;
612
613 printf("%d bytes from %s: seq=%u ttl=%d", sz,
614 from_str, recv_seq, ttl);
615 if (tp)
616 printf(" time=%u.%03u ms", triptime / 1000, triptime % 1000);
617 puts(dupmsg);
618 fflush_all();
619}
620static void unpack4(char *buf, int sz, struct sockaddr_in *from)
621{
622 struct icmp *icmppkt;
623 struct iphdr *iphdr;
624 int hlen;
625
626 /* discard if too short */
627 if (sz < (datalen + ICMP_MINLEN))
628 return;
629
630 /* check IP header */
631 iphdr = (struct iphdr *) buf;
632 hlen = iphdr->ihl << 2;
633 sz -= hlen;
634 icmppkt = (struct icmp *) (buf + hlen);
635 if (icmppkt->icmp_id != myid)
636 return; /* not our ping */
637
638 if (icmppkt->icmp_type == ICMP_ECHOREPLY) {
639 uint16_t recv_seq = ntohs(icmppkt->icmp_seq);
640 uint32_t *tp = NULL;
641
642 if (sz >= ICMP_MINLEN + sizeof(uint32_t))
643 tp = (uint32_t *) icmppkt->icmp_data;
644 unpack_tail(sz, tp,
645 inet_ntoa(*(struct in_addr *) &from->sin_addr.s_addr),
646 recv_seq, iphdr->ttl);
647 } else if (icmppkt->icmp_type != ICMP_ECHO) {
648 bb_error_msg("warning: got ICMP %d (%s)",
649 icmppkt->icmp_type,
650 icmp_type_name(icmppkt->icmp_type));
651 }
652}
653#if ENABLE_PING6
654static void unpack6(char *packet, int sz, struct sockaddr_in6 *from, int hoplimit)
655{
656 struct icmp6_hdr *icmppkt;
657 char buf[INET6_ADDRSTRLEN];
658
659 /* discard if too short */
660 if (sz < (datalen + sizeof(struct icmp6_hdr)))
661 return;
662
663 icmppkt = (struct icmp6_hdr *) packet;
664 if (icmppkt->icmp6_id != myid)
665 return; /* not our ping */
666
667 if (icmppkt->icmp6_type == ICMP6_ECHO_REPLY) {
668 uint16_t recv_seq = ntohs(icmppkt->icmp6_seq);
669 uint32_t *tp = NULL;
670
671 if (sz >= sizeof(struct icmp6_hdr) + sizeof(uint32_t))
672 tp = (uint32_t *) &icmppkt->icmp6_data8[4];
673 unpack_tail(sz, tp,
674 inet_ntop(AF_INET6, &from->sin6_addr,
675 buf, sizeof(buf)),
676 recv_seq, hoplimit);
677 } else if (icmppkt->icmp6_type != ICMP6_ECHO_REQUEST) {
678 bb_error_msg("warning: got ICMP %d (%s)",
679 icmppkt->icmp6_type,
680 icmp6_type_name(icmppkt->icmp6_type));
681 }
682}
683#endif
684
685static void ping4(len_and_sockaddr *lsa)
686{
687 int sockopt;
688
689 pingaddr.sin = lsa->u.sin;
690 if (source_lsa) {
691 if (setsockopt(pingsock, IPPROTO_IP, IP_MULTICAST_IF,
692 &source_lsa->u.sa, source_lsa->len))
693 bb_error_msg_and_die("can't set multicast source interface");
694 xbind(pingsock, &source_lsa->u.sa, source_lsa->len);
695 }
696
697 /* enable broadcast pings */
698 setsockopt_broadcast(pingsock);
699
700 /* set recv buf (needed if we can get lots of responses: flood ping,
701 * broadcast ping etc) */
702 sockopt = (datalen * 2) + 7 * 1024; /* giving it a bit of extra room */
703 setsockopt_SOL_SOCKET_int(pingsock, SO_RCVBUF, sockopt);
704
705 if (opt_ttl != 0) {
706 setsockopt_int(pingsock, IPPROTO_IP, IP_TTL, opt_ttl);
707 /* above doesnt affect packets sent to bcast IP, so... */
708 setsockopt_int(pingsock, IPPROTO_IP, IP_MULTICAST_TTL, opt_ttl);
709 }
710
711 signal(SIGINT, print_stats_and_exit);
712
713 /* start the ping's going ... */
714 sendping4(0);
715
716 /* listen for replies */
717 while (1) {
718 struct sockaddr_in from;
719 socklen_t fromlen = (socklen_t) sizeof(from);
720 int c;
721
722 c = recvfrom(pingsock, G.rcv_packet, G.sizeof_rcv_packet, 0,
723 (struct sockaddr *) &from, &fromlen);
724 if (c < 0) {
725 if (errno != EINTR)
726 bb_perror_msg("recvfrom");
727 continue;
728 }
729 unpack4(G.rcv_packet, c, &from);
730 if (pingcount && G.nreceived >= pingcount)
731 break;
732 }
733}
734#if ENABLE_PING6
735static void ping6(len_and_sockaddr *lsa)
736{
737 int sockopt;
738 struct msghdr msg;
739 struct sockaddr_in6 from;
740 struct iovec iov;
741 char control_buf[CMSG_SPACE(36)];
742
743 pingaddr.sin6 = lsa->u.sin6;
744 if (source_lsa)
745 xbind(pingsock, &source_lsa->u.sa, source_lsa->len);
746
747#ifdef ICMP6_FILTER
748 {
749 struct icmp6_filter filt;
750 if (!(option_mask32 & OPT_VERBOSE)) {
751 ICMP6_FILTER_SETBLOCKALL(&filt);
752 ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &filt);
753 } else {
754 ICMP6_FILTER_SETPASSALL(&filt);
755 }
756 if (setsockopt(pingsock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt,
757 sizeof(filt)) < 0)
758 bb_error_msg_and_die("setsockopt(%s)", "ICMP6_FILTER");
759 }
760#endif /*ICMP6_FILTER*/
761
762 /* enable broadcast pings */
763 setsockopt_broadcast(pingsock);
764
765 /* set recv buf (needed if we can get lots of responses: flood ping,
766 * broadcast ping etc) */
767 sockopt = (datalen * 2) + 7 * 1024; /* giving it a bit of extra room */
768 setsockopt_SOL_SOCKET_int(pingsock, SO_RCVBUF, sockopt);
769
770 sockopt = offsetof(struct icmp6_hdr, icmp6_cksum);
771 BUILD_BUG_ON(offsetof(struct icmp6_hdr, icmp6_cksum) != 2);
772 setsockopt_int(pingsock, SOL_RAW, IPV6_CHECKSUM, sockopt);
773
774 /* request ttl info to be returned in ancillary data */
775 setsockopt_1(pingsock, SOL_IPV6, IPV6_HOPLIMIT);
776
777 if (if_index)
778 pingaddr.sin6.sin6_scope_id = if_index;
779
780 signal(SIGINT, print_stats_and_exit);
781
782 /* start the ping's going ... */
783 sendping6(0);
784
785 /* listen for replies */
786 msg.msg_name = &from;
787 msg.msg_namelen = sizeof(from);
788 msg.msg_iov = &iov;
789 msg.msg_iovlen = 1;
790 msg.msg_control = control_buf;
791 iov.iov_base = G.rcv_packet;
792 iov.iov_len = G.sizeof_rcv_packet;
793 while (1) {
794 int c;
795 struct cmsghdr *mp;
796 int hoplimit = -1;
797 msg.msg_controllen = sizeof(control_buf);
798
799 c = recvmsg(pingsock, &msg, 0);
800 if (c < 0) {
801 if (errno != EINTR)
802 bb_perror_msg("recvfrom");
803 continue;
804 }
805 for (mp = CMSG_FIRSTHDR(&msg); mp; mp = CMSG_NXTHDR(&msg, mp)) {
806 if (mp->cmsg_level == SOL_IPV6
807 && mp->cmsg_type == IPV6_HOPLIMIT
808 /* don't check len - we trust the kernel: */
809 /* && mp->cmsg_len >= CMSG_LEN(sizeof(int)) */
810 ) {
811 /*hoplimit = *(int*)CMSG_DATA(mp); - unaligned access */
812 move_from_unaligned_int(hoplimit, CMSG_DATA(mp));
813 }
814 }
815 unpack6(G.rcv_packet, c, &from, hoplimit);
816 if (pingcount && G.nreceived >= pingcount)
817 break;
818 }
819}
820#endif
821
822static void ping(len_and_sockaddr *lsa)
823{
824 printf("PING %s (%s)", hostname, dotted);
825 if (source_lsa) {
826 printf(" from %s",
827 xmalloc_sockaddr2dotted_noport(&source_lsa->u.sa));
828 }
829 printf(": %d data bytes\n", datalen);
830
831 create_icmp_socket(lsa);
832 /* untested whether "-I addr" really works for IPv6: */
833 if (str_I)
834 setsockopt_bindtodevice(pingsock, str_I);
835
836 G.sizeof_rcv_packet = datalen + MAXIPLEN + MAXICMPLEN;
837 G.rcv_packet = xzalloc(G.sizeof_rcv_packet);
838#if ENABLE_PING6
839 if (lsa->u.sa.sa_family == AF_INET6) {
840 /* +4 reserves a place for timestamp, which may end up sitting
841 * _after_ packet. Saves one if() - see sendping4/6() */
842 G.snd_packet = xzalloc(datalen + sizeof(struct icmp6_hdr) + 4);
843 ping6(lsa);
844 } else
845#endif
846 {
847 G.snd_packet = xzalloc(datalen + ICMP_MINLEN + 4);
848 ping4(lsa);
849 }
850}
851
852static int common_ping_main(int opt, char **argv)
853{
854 len_and_sockaddr *lsa;
855 char *str_s, *str_p;
856
857 INIT_G();
858
859 /* exactly one argument needed; -v and -q don't mix; -c NUM, -t NUM, -w NUM, -W NUM */
860 opt_complementary = "=1:q--v:v--q:c+:t+:w+:W+";
861 opt |= getopt32(argv, OPT_STRING, &pingcount, &str_s, &opt_ttl, &deadline, &timeout, &str_I, &str_p);
862 if (opt & OPT_s)
863 datalen = xatou16(str_s); // -s
864 if (opt & OPT_I) { // -I
865 if_index = if_nametoindex(str_I);
866 if (!if_index) {
867 /* TODO: I'm not sure it takes IPv6 unless in [XX:XX..] format */
868 source_lsa = xdotted2sockaddr(str_I, 0);
869 str_I = NULL; /* don't try to bind to device later */
870 }
871 }
872 if (opt & OPT_p)
873 G.pattern = xstrtou_range(str_p, 16, 0, 255);
874
875 myid = (uint16_t) getpid();
876 hostname = argv[optind];
877#if ENABLE_PING6
878 {
879 sa_family_t af = AF_UNSPEC;
880 if (opt & OPT_IPV4)
881 af = AF_INET;
882 if (opt & OPT_IPV6)
883 af = AF_INET6;
884 lsa = xhost_and_af2sockaddr(hostname, 0, af);
885 }
886#else
887 lsa = xhost_and_af2sockaddr(hostname, 0, AF_INET);
888#endif
889
890 if (source_lsa && source_lsa->u.sa.sa_family != lsa->u.sa.sa_family)
891 /* leaking it here... */
892 source_lsa = NULL;
893
894 dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
895 ping(lsa);
896 print_stats_and_exit(EXIT_SUCCESS);
897 /*return EXIT_SUCCESS;*/
898}
899#endif /* FEATURE_FANCY_PING */
900
901
902int ping_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
903int ping_main(int argc UNUSED_PARAM, char **argv)
904{
905#if !ENABLE_FEATURE_FANCY_PING
906 return common_ping_main(AF_UNSPEC, argv);
907#else
908 return common_ping_main(0, argv);
909#endif
910}
911
912#if ENABLE_PING6
913int ping6_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
914int ping6_main(int argc UNUSED_PARAM, char **argv)
915{
916# if !ENABLE_FEATURE_FANCY_PING
917 return common_ping_main(AF_INET6, argv);
918# else
919 return common_ping_main(OPT_IPV6, argv);
920# endif
921}
922#endif
923
924/* from ping6.c:
925 * Copyright (c) 1989 The Regents of the University of California.
926 * All rights reserved.
927 *
928 * This code is derived from software contributed to Berkeley by
929 * Mike Muuss.
930 *
931 * Redistribution and use in source and binary forms, with or without
932 * modification, are permitted provided that the following conditions
933 * are met:
934 * 1. Redistributions of source code must retain the above copyright
935 * notice, this list of conditions and the following disclaimer.
936 * 2. Redistributions in binary form must reproduce the above copyright
937 * notice, this list of conditions and the following disclaimer in the
938 * documentation and/or other materials provided with the distribution.
939 *
940 * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
941 * ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
942 *
943 * 4. Neither the name of the University nor the names of its contributors
944 * may be used to endorse or promote products derived from this software
945 * without specific prior written permission.
946 *
947 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
948 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
949 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
950 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
951 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
952 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
953 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
954 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
955 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
956 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
957 * SUCH DAMAGE.
958 */
Note: See TracBrowser for help on using the repository browser.