blob: d1d59d5456c6e71da9ef9f58a6c850d5ad4150be [file] [log] [blame]
Erik Andersene49d5ec2000-02-08 19:58:47 +00001/* vi: set sw=4 ts=4: */
Eric Andersen485b9551999-12-07 23:14:59 +00002/*
Eric Andersen485b9551999-12-07 23:14:59 +00003 * Mini ping implementation for busybox
4 *
5 * Copyright (C) 1999 by Randolph Chung <tausq@debian.org>
6 *
Rob Landley42eddba2005-12-15 08:04:17 +00007 * Adapted from the ping in netkit-base 0.10:
Eric Andersen485b9551999-12-07 23:14:59 +00008 * Copyright (c) 1989 The Regents of the University of California.
Denis Vlasenko35d4da02007-01-22 14:04:27 +00009 * All rights reserved.
10 *
11 * This code is derived from software contributed to Berkeley by
12 * Mike Muuss.
Eric Andersen485b9551999-12-07 23:14:59 +000013 *
Denys Vlasenko0ef64bd2010-08-16 20:14:46 +020014 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
Eric Andersen485b9551999-12-07 23:14:59 +000015 */
Denis Vlasenkob9a279b2007-01-24 23:53:22 +000016/* 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 */
Joachim Nilsson714e2b72010-11-28 23:01:18 +010027//config:config PING
Denys Vlasenko4eed2c62017-07-18 22:01:24 +020028//config: bool "ping (9.5 kb)"
Joachim Nilsson714e2b72010-11-28 23:01:18 +010029//config: default y
Denys Vlasenkoe3b1a1f2011-02-26 22:24:08 +010030//config: select PLATFORM_LINUX
Joachim Nilsson714e2b72010-11-28 23:01:18 +010031//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020032//config: ping uses the ICMP protocol's mandatory ECHO_REQUEST datagram to
33//config: elicit an ICMP ECHO_RESPONSE from a host or gateway.
Joachim Nilsson714e2b72010-11-28 23:01:18 +010034//config:
35//config:config PING6
Denys Vlasenko4eed2c62017-07-18 22:01:24 +020036//config: bool "ping6 (10 kb)"
Joachim Nilsson714e2b72010-11-28 23:01:18 +010037//config: default y
Denys Vlasenko83423972016-11-23 09:25:57 +010038//config: depends on FEATURE_IPV6
Joachim Nilsson714e2b72010-11-28 23:01:18 +010039//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020040//config: Alias to "ping -6".
Joachim Nilsson714e2b72010-11-28 23:01:18 +010041//config:
42//config:config FEATURE_FANCY_PING
43//config: bool "Enable fancy ping output"
44//config: default y
Denys Vlasenko83423972016-11-23 09:25:57 +010045//config: depends on PING || PING6
Joachim Nilsson714e2b72010-11-28 23:01:18 +010046//config: help
Denys Vlasenkoa680f402017-07-21 11:58:43 +020047//config: With this option off, ping will say "HOST is alive!"
48//config: or terminate with SIGALRM in 5 seconds otherwise.
49//config: No command-line options will be recognized.
Joachim Nilsson714e2b72010-11-28 23:01:18 +010050
Denys Vlasenkob9f2d9f2011-01-18 13:58:01 +010051/* Needs socket(AF_INET, SOCK_RAW, IPPROTO_ICMP), therefore BB_SUID_MAYBE: */
52//applet:IF_PING(APPLET(ping, BB_DIR_BIN, BB_SUID_MAYBE))
53//applet:IF_PING6(APPLET(ping6, BB_DIR_BIN, BB_SUID_MAYBE))
Joachim Nilsson714e2b72010-11-28 23:01:18 +010054
55//kbuild:lib-$(CONFIG_PING) += ping.o
56//kbuild:lib-$(CONFIG_PING6) += ping.o
57
58//usage:#if !ENABLE_FEATURE_FANCY_PING
59//usage:# define ping_trivial_usage
60//usage: "HOST"
61//usage:# define ping_full_usage "\n\n"
62//usage: "Send ICMP ECHO_REQUEST packets to network hosts"
63//usage:# define ping6_trivial_usage
64//usage: "HOST"
65//usage:# define ping6_full_usage "\n\n"
66//usage: "Send ICMP ECHO_REQUEST packets to network hosts"
67//usage:#else
68//usage:# define ping_trivial_usage
69//usage: "[OPTIONS] HOST"
70//usage:# define ping_full_usage "\n\n"
71//usage: "Send ICMP ECHO_REQUEST packets to network hosts\n"
Denys Vlasenkoaf4a07a2013-03-15 00:11:35 +010072//usage: IF_PING6(
Joachim Nilsson714e2b72010-11-28 23:01:18 +010073//usage: "\n -4,-6 Force IP or IPv6 name resolution"
Denys Vlasenkoaf4a07a2013-03-15 00:11:35 +010074//usage: )
Joachim Nilsson714e2b72010-11-28 23:01:18 +010075//usage: "\n -c CNT Send only CNT pings"
Denys Vlasenkof3d705f2017-01-21 03:46:35 +010076//usage: "\n -s SIZE Send SIZE data bytes in packets (default 56)"
Joachim Nilsson714e2b72010-11-28 23:01:18 +010077//usage: "\n -t TTL Set TTL"
Denys Vlasenkof3d705f2017-01-21 03:46:35 +010078//usage: "\n -I IFACE/IP Source interface or IP address"
79//usage: "\n -W SEC Seconds to wait for the first response (default 10)"
Joachim Nilsson714e2b72010-11-28 23:01:18 +010080//usage: "\n (after all -c CNT packets are sent)"
81//usage: "\n -w SEC Seconds until ping exits (default:infinite)"
82//usage: "\n (can exit earlier with -c CNT)"
Florian Fainelli6ff05512014-08-27 16:01:25 +020083//usage: "\n -q Quiet, only display output at start"
Joachim Nilsson714e2b72010-11-28 23:01:18 +010084//usage: "\n and when finished"
Denys Vlasenko67d42df2017-07-21 16:07:13 +020085//usage: "\n -p HEXBYTE Pattern to use for payload"
Joachim Nilsson714e2b72010-11-28 23:01:18 +010086//usage:
87//usage:# define ping6_trivial_usage
88//usage: "[OPTIONS] HOST"
89//usage:# define ping6_full_usage "\n\n"
90//usage: "Send ICMP ECHO_REQUEST packets to network hosts\n"
Joachim Nilsson714e2b72010-11-28 23:01:18 +010091//usage: "\n -c CNT Send only CNT pings"
Denys Vlasenkof3d705f2017-01-21 03:46:35 +010092//usage: "\n -s SIZE Send SIZE data bytes in packets (default 56)"
93//usage: "\n -I IFACE/IP Source interface or IP address"
Florian Fainelli6ff05512014-08-27 16:01:25 +020094//usage: "\n -q Quiet, only display output at start"
Joachim Nilsson714e2b72010-11-28 23:01:18 +010095//usage: "\n and when finished"
Denys Vlasenko67d42df2017-07-21 16:07:13 +020096//usage: "\n -p HEXBYTE Pattern to use for payload"
Joachim Nilsson714e2b72010-11-28 23:01:18 +010097//usage:
98//usage:#endif
99//usage:
100//usage:#define ping_example_usage
101//usage: "$ ping localhost\n"
102//usage: "PING slag (127.0.0.1): 56 data bytes\n"
103//usage: "64 bytes from 127.0.0.1: icmp_seq=0 ttl=255 time=20.1 ms\n"
104//usage: "\n"
105//usage: "--- debian ping statistics ---\n"
106//usage: "1 packets transmitted, 1 packets received, 0% packet loss\n"
107//usage: "round-trip min/avg/max = 20.1/20.1/20.1 ms\n"
108//usage:#define ping6_example_usage
109//usage: "$ ping6 ip6-localhost\n"
110//usage: "PING ip6-localhost (::1): 56 data bytes\n"
111//usage: "64 bytes from ::1: icmp6_seq=0 ttl=64 time=20.1 ms\n"
112//usage: "\n"
113//usage: "--- ip6-localhost ping statistics ---\n"
114//usage: "1 packets transmitted, 1 packets received, 0% packet loss\n"
115//usage: "round-trip min/avg/max = 20.1/20.1/20.1 ms\n"
116
Denys Vlasenko0c4dbd42017-09-18 16:28:43 +0200117#include <net/if.h>
118#include <netinet/ip_icmp.h>
119#include "libbb.h"
120#include "common_bufsiz.h"
121
122#ifdef __BIONIC__
123/* should be in netinet/ip_icmp.h */
124# define ICMP_DEST_UNREACH 3 /* Destination Unreachable */
125# define ICMP_SOURCE_QUENCH 4 /* Source Quench */
126# define ICMP_REDIRECT 5 /* Redirect (change route) */
127# define ICMP_ECHO 8 /* Echo Request */
128# define ICMP_TIME_EXCEEDED 11 /* Time Exceeded */
129# define ICMP_PARAMETERPROB 12 /* Parameter Problem */
130# define ICMP_TIMESTAMP 13 /* Timestamp Request */
131# define ICMP_TIMESTAMPREPLY 14 /* Timestamp Reply */
132# define ICMP_INFO_REQUEST 15 /* Information Request */
133# define ICMP_INFO_REPLY 16 /* Information Reply */
134# define ICMP_ADDRESS 17 /* Address Mask Request */
135# define ICMP_ADDRESSREPLY 18 /* Address Mask Reply */
136#endif
137
James Clarke4d0971b2017-10-07 18:53:21 +0100138/* Some operating systems, like GNU/Hurd, don't define SOL_RAW, but do have
139 * IPPROTO_RAW. Since the IPPROTO definitions are also valid to use for
140 * setsockopt (and take the same value as their corresponding SOL definitions,
141 * if they exist), we can just fall back on IPPROTO_RAW. */
142#ifndef SOL_RAW
143# define SOL_RAW IPPROTO_RAW
144#endif
145
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000146#if ENABLE_PING6
Denys Vlasenkoad7d94b2009-11-20 18:12:12 +0100147# include <netinet/icmp6.h>
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000148/* I see RENUMBERED constants in bits/in.h - !!?
149 * What a fuck is going on with libc? Is it a glibc joke? */
Denys Vlasenkoad7d94b2009-11-20 18:12:12 +0100150# ifdef IPV6_2292HOPLIMIT
151# undef IPV6_HOPLIMIT
152# define IPV6_HOPLIMIT IPV6_2292HOPLIMIT
153# endif
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000154#endif
Eric Andersen485b9551999-12-07 23:14:59 +0000155
Rob Landleybc68cd12006-03-10 19:22:06 +0000156enum {
157 DEFDATALEN = 56,
158 MAXIPLEN = 60,
159 MAXICMPLEN = 76,
Rob Landleybc68cd12006-03-10 19:22:06 +0000160 MAX_DUP_CHK = (8 * 128),
161 MAXWAIT = 10,
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000162 PINGINTERVAL = 1, /* 1 second */
Denys Vlasenko59f46672013-06-28 00:30:46 +0200163 pingsock = 0,
Rob Landleybc68cd12006-03-10 19:22:06 +0000164};
Eric Andersen485b9551999-12-07 23:14:59 +0000165
Denys Vlasenko59f46672013-06-28 00:30:46 +0200166static void
167#if ENABLE_PING6
168create_icmp_socket(len_and_sockaddr *lsa)
169#else
170create_icmp_socket(void)
171#define create_icmp_socket(lsa) create_icmp_socket()
172#endif
173{
174 int sock;
175#if ENABLE_PING6
176 if (lsa->u.sa.sa_family == AF_INET6)
177 sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
178 else
179#endif
180 sock = socket(AF_INET, SOCK_RAW, 1); /* 1 == ICMP */
181 if (sock < 0) {
Denys Vlasenkof0058b12014-01-09 11:53:26 +0100182 if (errno == EPERM)
183 bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
184 bb_perror_msg_and_die(bb_msg_can_not_create_raw_socket);
Denys Vlasenko59f46672013-06-28 00:30:46 +0200185 }
186
187 xmove_fd(sock, pingsock);
188}
189
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000190#if !ENABLE_FEATURE_FANCY_PING
Denis Vlasenko4a5cf162006-11-20 00:48:22 +0000191
Denys Vlasenko9341fd22010-03-03 01:10:29 +0100192/* Simple version */
Denis Vlasenko4a5cf162006-11-20 00:48:22 +0000193
Denys Vlasenko9341fd22010-03-03 01:10:29 +0100194struct globals {
195 char *hostname;
196 char packet[DEFDATALEN + MAXIPLEN + MAXICMPLEN];
Jonas Danielsson4d5acd22016-06-23 18:26:32 +0200197 uint16_t myid;
Denys Vlasenko9341fd22010-03-03 01:10:29 +0100198} FIX_ALIASING;
Denys Vlasenkoe6a2f4c2016-04-21 16:26:30 +0200199#define G (*(struct globals*)bb_common_bufsiz1)
Denys Vlasenko47cfbf32016-04-21 18:18:48 +0200200#define INIT_G() do { setup_common_bufsiz(); } while (0)
Denis Vlasenkocb6874c2006-09-02 16:13:36 +0000201
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000202static void noresp(int ign UNUSED_PARAM)
Eric Andersenb5474c42002-03-20 11:59:28 +0000203{
Denys Vlasenko9341fd22010-03-03 01:10:29 +0100204 printf("No response from %s\n", G.hostname);
Eric Andersen4e486a52003-01-12 06:08:33 +0000205 exit(EXIT_FAILURE);
Eric Andersenb5474c42002-03-20 11:59:28 +0000206}
Eric Andersen19db07b1999-12-11 08:41:28 +0000207
Denis Vlasenkoe9356022007-01-29 18:03:54 +0000208static void ping4(len_and_sockaddr *lsa)
Eric Andersen19db07b1999-12-11 08:41:28 +0000209{
Erik Andersene49d5ec2000-02-08 19:58:47 +0000210 struct icmp *pkt;
Denys Vlasenko59f46672013-06-28 00:30:46 +0200211 int c;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000212
Denys Vlasenko9341fd22010-03-03 01:10:29 +0100213 pkt = (struct icmp *) G.packet;
Denys Vlasenko59f46672013-06-28 00:30:46 +0200214 /*memset(pkt, 0, sizeof(G.packet)); already is */
Erik Andersene49d5ec2000-02-08 19:58:47 +0000215 pkt->icmp_type = ICMP_ECHO;
Jonas Danielsson4d5acd22016-06-23 18:26:32 +0200216 pkt->icmp_id = G.myid;
Baruch Siache8f36332011-09-07 17:52:37 +0200217 pkt->icmp_cksum = inet_cksum((uint16_t *) pkt, sizeof(G.packet));
Erik Andersene49d5ec2000-02-08 19:58:47 +0000218
Denys Vlasenko9341fd22010-03-03 01:10:29 +0100219 xsendto(pingsock, G.packet, DEFDATALEN + ICMP_MINLEN, &lsa->u.sa, lsa->len);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000220
Erik Andersene49d5ec2000-02-08 19:58:47 +0000221 /* listen for replies */
222 while (1) {
Denys Vlasenko59f46672013-06-28 00:30:46 +0200223#if 0
Erik Andersene49d5ec2000-02-08 19:58:47 +0000224 struct sockaddr_in from;
Mike Frysinger03e827a2005-07-26 23:00:59 +0000225 socklen_t fromlen = sizeof(from);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000226
Denys Vlasenko9341fd22010-03-03 01:10:29 +0100227 c = recvfrom(pingsock, G.packet, sizeof(G.packet), 0,
Denis Vlasenko806116b2006-12-31 12:14:16 +0000228 (struct sockaddr *) &from, &fromlen);
Denys Vlasenko59f46672013-06-28 00:30:46 +0200229#else
230 c = recv(pingsock, G.packet, sizeof(G.packet), 0);
231#endif
Denis Vlasenko806116b2006-12-31 12:14:16 +0000232 if (c < 0) {
Denis Vlasenko44c2eb22007-01-08 23:55:33 +0000233 if (errno != EINTR)
234 bb_perror_msg("recvfrom");
Erik Andersene49d5ec2000-02-08 19:58:47 +0000235 continue;
236 }
237 if (c >= 76) { /* ip + icmp */
Denys Vlasenko9341fd22010-03-03 01:10:29 +0100238 struct iphdr *iphdr = (struct iphdr *) G.packet;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000239
Denys Vlasenko9341fd22010-03-03 01:10:29 +0100240 pkt = (struct icmp *) (G.packet + (iphdr->ihl << 2)); /* skip ip hdr */
Jonas Danielsson4d5acd22016-06-23 18:26:32 +0200241 if (pkt->icmp_id != G.myid)
242 continue; /* not our ping */
Erik Andersene49d5ec2000-02-08 19:58:47 +0000243 if (pkt->icmp_type == ICMP_ECHOREPLY)
244 break;
245 }
246 }
Eric Andersen19db07b1999-12-11 08:41:28 +0000247}
248
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000249#if ENABLE_PING6
250static void ping6(len_and_sockaddr *lsa)
251{
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000252 struct icmp6_hdr *pkt;
Denys Vlasenko59f46672013-06-28 00:30:46 +0200253 int c;
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000254 int sockopt;
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000255
Denys Vlasenko9341fd22010-03-03 01:10:29 +0100256 pkt = (struct icmp6_hdr *) G.packet;
Denys Vlasenko59f46672013-06-28 00:30:46 +0200257 /*memset(pkt, 0, sizeof(G.packet)); already is */
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000258 pkt->icmp6_type = ICMP6_ECHO_REQUEST;
Jonas Danielsson4d5acd22016-06-23 18:26:32 +0200259 pkt->icmp6_id = G.myid;
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000260
261 sockopt = offsetof(struct icmp6_hdr, icmp6_cksum);
Denys Vlasenkoc52cbea2015-08-24 19:48:03 +0200262 setsockopt_int(pingsock, SOL_RAW, IPV6_CHECKSUM, sockopt);
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000263
Denys Vlasenko9341fd22010-03-03 01:10:29 +0100264 xsendto(pingsock, G.packet, DEFDATALEN + sizeof(struct icmp6_hdr), &lsa->u.sa, lsa->len);
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000265
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000266 /* listen for replies */
267 while (1) {
Denys Vlasenko59f46672013-06-28 00:30:46 +0200268#if 0
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000269 struct sockaddr_in6 from;
270 socklen_t fromlen = sizeof(from);
271
Denys Vlasenko9341fd22010-03-03 01:10:29 +0100272 c = recvfrom(pingsock, G.packet, sizeof(G.packet), 0,
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000273 (struct sockaddr *) &from, &fromlen);
Denys Vlasenko59f46672013-06-28 00:30:46 +0200274#else
275 c = recv(pingsock, G.packet, sizeof(G.packet), 0);
276#endif
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000277 if (c < 0) {
278 if (errno != EINTR)
279 bb_perror_msg("recvfrom");
280 continue;
281 }
Denys Vlasenko59f46672013-06-28 00:30:46 +0200282 if (c >= ICMP_MINLEN) { /* icmp6_hdr */
Jonas Danielsson4d5acd22016-06-23 18:26:32 +0200283 if (pkt->icmp6_id != G.myid)
284 continue; /* not our ping */
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000285 if (pkt->icmp6_type == ICMP6_ECHO_REPLY)
286 break;
287 }
288 }
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000289}
290#endif
291
Denys Vlasenkoad7d94b2009-11-20 18:12:12 +0100292#if !ENABLE_PING6
293# define common_ping_main(af, argv) common_ping_main(argv)
294#endif
295static int common_ping_main(sa_family_t af, char **argv)
Eric Andersen19db07b1999-12-11 08:41:28 +0000296{
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000297 len_and_sockaddr *lsa;
Denis Vlasenko3483e852007-07-02 15:47:52 +0000298
Denys Vlasenko9341fd22010-03-03 01:10:29 +0100299 INIT_G();
300
Denys Vlasenkoad7d94b2009-11-20 18:12:12 +0100301#if ENABLE_PING6
Denis Vlasenko3483e852007-07-02 15:47:52 +0000302 while ((++argv)[0] && argv[0][0] == '-') {
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000303 if (argv[0][1] == '4') {
304 af = AF_INET;
305 continue;
306 }
307 if (argv[0][1] == '6') {
308 af = AF_INET6;
309 continue;
310 }
Manuel Novoa III cad53642003-03-19 09:13:01 +0000311 bb_show_usage();
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000312 }
313#else
314 argv++;
315#endif
316
Denys Vlasenko9341fd22010-03-03 01:10:29 +0100317 G.hostname = *argv;
318 if (!G.hostname)
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000319 bb_show_usage();
320
321#if ENABLE_PING6
Denys Vlasenko9341fd22010-03-03 01:10:29 +0100322 lsa = xhost_and_af2sockaddr(G.hostname, 0, af);
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000323#else
Denys Vlasenko9341fd22010-03-03 01:10:29 +0100324 lsa = xhost_and_af2sockaddr(G.hostname, 0, AF_INET);
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000325#endif
326 /* Set timer _after_ DNS resolution */
327 signal(SIGALRM, noresp);
328 alarm(5); /* give the host 5000ms to respond */
329
Denys Vlasenko59f46672013-06-28 00:30:46 +0200330 create_icmp_socket(lsa);
Jonas Danielsson4d5acd22016-06-23 18:26:32 +0200331 G.myid = (uint16_t) getpid();
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000332#if ENABLE_PING6
Bernhard Reutner-Fischer8c69afd2008-01-29 10:33:34 +0000333 if (lsa->u.sa.sa_family == AF_INET6)
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000334 ping6(lsa);
335 else
336#endif
Denis Vlasenkoe9356022007-01-29 18:03:54 +0000337 ping4(lsa);
Denys Vlasenkoa680f402017-07-21 11:58:43 +0200338 if (ENABLE_FEATURE_CLEAN_UP)
339 close(pingsock);
Denys Vlasenko9341fd22010-03-03 01:10:29 +0100340 printf("%s is alive!\n", G.hostname);
Matt Kraai3e856ce2000-12-01 02:55:13 +0000341 return EXIT_SUCCESS;
Eric Andersen19db07b1999-12-11 08:41:28 +0000342}
343
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000344
345#else /* FEATURE_FANCY_PING */
346
Denis Vlasenko4a5cf162006-11-20 00:48:22 +0000347
Denys Vlasenko9341fd22010-03-03 01:10:29 +0100348/* Full(er) version */
Denis Vlasenko4a5cf162006-11-20 00:48:22 +0000349
Denys Vlasenko22542ec2017-08-08 21:55:02 +0200350/* -c NUM, -t NUM, -w NUM, -W NUM */
351#define OPT_STRING "qvc:+s:t:+w:+W:+I:np:4"IF_PING6("6")
Denis Vlasenko4a5cf162006-11-20 00:48:22 +0000352enum {
353 OPT_QUIET = 1 << 0,
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000354 OPT_VERBOSE = 1 << 1,
355 OPT_c = 1 << 2,
356 OPT_s = 1 << 3,
Joachim Nilsson714e2b72010-11-28 23:01:18 +0100357 OPT_t = 1 << 4,
358 OPT_w = 1 << 5,
359 OPT_W = 1 << 6,
360 OPT_I = 1 << 7,
Denys Vlasenkoaf4a07a2013-03-15 00:11:35 +0100361 /*OPT_n = 1 << 8, - ignored */
Florian Fainelli6ff05512014-08-27 16:01:25 +0200362 OPT_p = 1 << 9,
363 OPT_IPV4 = 1 << 10,
364 OPT_IPV6 = (1 << 11) * ENABLE_PING6,
Denis Vlasenko4a5cf162006-11-20 00:48:22 +0000365};
366
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000367
Denis Vlasenko821cc252007-06-04 10:33:48 +0000368struct globals {
Denis Vlasenko1a7afb42007-10-19 21:39:25 +0000369 int if_index;
Denis Vlasenko59f502b2008-10-27 11:54:45 +0000370 char *str_I;
Denis Vlasenko821cc252007-06-04 10:33:48 +0000371 len_and_sockaddr *source_lsa;
372 unsigned datalen;
Denis Vlasenko59f502b2008-10-27 11:54:45 +0000373 unsigned pingcount; /* must be int-sized */
Joachim Nilsson714e2b72010-11-28 23:01:18 +0100374 unsigned opt_ttl;
Denis Vlasenko59f502b2008-10-27 11:54:45 +0000375 unsigned long ntransmitted, nreceived, nrepeats;
Denis Vlasenko821cc252007-06-04 10:33:48 +0000376 uint16_t myid;
Florian Fainelli6ff05512014-08-27 16:01:25 +0200377 uint8_t pattern;
Denis Vlasenko76791452007-06-18 08:55:57 +0000378 unsigned tmin, tmax; /* in us */
379 unsigned long long tsum; /* in us, sum of all times */
Denis Vlasenko90c31b32008-04-07 00:46:29 +0000380 unsigned deadline;
381 unsigned timeout;
382 unsigned total_secs;
Denys Vlasenko9341fd22010-03-03 01:10:29 +0100383 unsigned sizeof_rcv_packet;
384 char *rcv_packet; /* [datalen + MAXIPLEN + MAXICMPLEN] */
385 void *snd_packet; /* [datalen + ipv4/ipv6_const] */
Denis Vlasenko821cc252007-06-04 10:33:48 +0000386 const char *hostname;
387 const char *dotted;
388 union {
389 struct sockaddr sa;
390 struct sockaddr_in sin;
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000391#if ENABLE_PING6
Denis Vlasenko821cc252007-06-04 10:33:48 +0000392 struct sockaddr_in6 sin6;
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000393#endif
Denis Vlasenko821cc252007-06-04 10:33:48 +0000394 } pingaddr;
Denys Vlasenko26a7e2e2013-06-28 01:33:47 +0200395 unsigned char rcvd_tbl[MAX_DUP_CHK / 8];
Denys Vlasenko98a4c7c2010-02-04 15:00:15 +0100396} FIX_ALIASING;
Denys Vlasenkoe6a2f4c2016-04-21 16:26:30 +0200397#define G (*(struct globals*)bb_common_bufsiz1)
Denis Vlasenko821cc252007-06-04 10:33:48 +0000398#define if_index (G.if_index )
Denis Vlasenko1a7afb42007-10-19 21:39:25 +0000399#define source_lsa (G.source_lsa )
Denis Vlasenko59f502b2008-10-27 11:54:45 +0000400#define str_I (G.str_I )
Denis Vlasenko1a7afb42007-10-19 21:39:25 +0000401#define datalen (G.datalen )
Denis Vlasenko821cc252007-06-04 10:33:48 +0000402#define pingcount (G.pingcount )
Joachim Nilsson714e2b72010-11-28 23:01:18 +0100403#define opt_ttl (G.opt_ttl )
Denis Vlasenko821cc252007-06-04 10:33:48 +0000404#define myid (G.myid )
405#define tmin (G.tmin )
406#define tmax (G.tmax )
407#define tsum (G.tsum )
Denis Vlasenko90c31b32008-04-07 00:46:29 +0000408#define deadline (G.deadline )
409#define timeout (G.timeout )
410#define total_secs (G.total_secs )
Denis Vlasenko821cc252007-06-04 10:33:48 +0000411#define hostname (G.hostname )
412#define dotted (G.dotted )
413#define pingaddr (G.pingaddr )
414#define rcvd_tbl (G.rcvd_tbl )
Denis Vlasenko821cc252007-06-04 10:33:48 +0000415#define INIT_G() do { \
Denys Vlasenko47cfbf32016-04-21 18:18:48 +0200416 setup_common_bufsiz(); \
Denys Vlasenkoab3964d2015-10-13 14:50:20 +0200417 BUILD_BUG_ON(sizeof(G) > COMMON_BUFSIZE); \
Denis Vlasenko90c31b32008-04-07 00:46:29 +0000418 datalen = DEFDATALEN; \
419 timeout = MAXWAIT; \
Denis Vlasenko821cc252007-06-04 10:33:48 +0000420 tmin = UINT_MAX; \
421} while (0)
Eric Andersen19db07b1999-12-11 08:41:28 +0000422
Eric Andersen19db07b1999-12-11 08:41:28 +0000423
Denys Vlasenko26a7e2e2013-06-28 01:33:47 +0200424#define BYTE(bit) rcvd_tbl[(bit)>>3]
425#define MASK(bit) (1 << ((bit) & 7))
426#define SET(bit) (BYTE(bit) |= MASK(bit))
427#define CLR(bit) (BYTE(bit) &= (~MASK(bit)))
428#define TST(bit) (BYTE(bit) & MASK(bit))
Eric Andersen19db07b1999-12-11 08:41:28 +0000429
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000430static void print_stats_and_exit(int junk) NORETURN;
431static void print_stats_and_exit(int junk UNUSED_PARAM)
Erik Andersene49d5ec2000-02-08 19:58:47 +0000432{
Denys Vlasenkoaf4a07a2013-03-15 00:11:35 +0100433 unsigned long ul;
434 unsigned long nrecv;
435
Erik Andersene49d5ec2000-02-08 19:58:47 +0000436 signal(SIGINT, SIG_IGN);
437
Denys Vlasenkoaf4a07a2013-03-15 00:11:35 +0100438 nrecv = G.nreceived;
439 printf("\n--- %s ping statistics ---\n"
440 "%lu packets transmitted, "
441 "%lu packets received, ",
442 hostname, G.ntransmitted, nrecv
443 );
444 if (G.nrepeats)
445 printf("%lu duplicates, ", G.nrepeats);
446 ul = G.ntransmitted;
447 if (ul != 0)
448 ul = (ul - nrecv) * 100 / ul;
449 printf("%lu%% packet loss\n", ul);
Denis Vlasenko76791452007-06-18 08:55:57 +0000450 if (tmin != UINT_MAX) {
Denys Vlasenkoaf4a07a2013-03-15 00:11:35 +0100451 unsigned tavg = tsum / (nrecv + G.nrepeats);
Denis Vlasenko76791452007-06-18 08:55:57 +0000452 printf("round-trip min/avg/max = %u.%03u/%u.%03u/%u.%03u ms\n",
453 tmin / 1000, tmin % 1000,
454 tavg / 1000, tavg % 1000,
455 tmax / 1000, tmax % 1000);
456 }
Denis Vlasenko90c31b32008-04-07 00:46:29 +0000457 /* if condition is true, exit with 1 -- 'failure' */
Denys Vlasenkoaf4a07a2013-03-15 00:11:35 +0100458 exit(nrecv == 0 || (deadline && nrecv < pingcount));
Eric Andersen485b9551999-12-07 23:14:59 +0000459}
460
Denys Vlasenko281e7b82011-02-06 17:51:45 +0100461static void sendping_tail(void (*sp)(int), int size_pkt)
Denis Vlasenkoe9356022007-01-29 18:03:54 +0000462{
Denis Vlasenkoc8e99932007-02-09 18:14:42 +0000463 int sz;
464
Denys Vlasenkoaf4a07a2013-03-15 00:11:35 +0100465 CLR((uint16_t)G.ntransmitted % MAX_DUP_CHK);
466 G.ntransmitted++;
Denis Vlasenkoc8e99932007-02-09 18:14:42 +0000467
Denys Vlasenko281e7b82011-02-06 17:51:45 +0100468 size_pkt += datalen;
469
Denis Vlasenkoc8e99932007-02-09 18:14:42 +0000470 /* sizeof(pingaddr) can be larger than real sa size, but I think
471 * it doesn't matter */
Denys Vlasenko281e7b82011-02-06 17:51:45 +0100472 sz = xsendto(pingsock, G.snd_packet, size_pkt, &pingaddr.sa, sizeof(pingaddr));
Denis Vlasenkoc8e99932007-02-09 18:14:42 +0000473 if (sz != size_pkt)
Bernhard Reutner-Fischerb2908892007-04-12 11:34:39 +0000474 bb_error_msg_and_die(bb_msg_write_error);
Denis Vlasenkoe9356022007-01-29 18:03:54 +0000475
Denys Vlasenkoaf4a07a2013-03-15 00:11:35 +0100476 if (pingcount == 0 || deadline || G.ntransmitted < pingcount) {
Denis Vlasenko90c31b32008-04-07 00:46:29 +0000477 /* Didn't send all pings yet - schedule next in 1s */
478 signal(SIGALRM, sp);
479 if (deadline) {
480 total_secs += PINGINTERVAL;
481 if (total_secs >= deadline)
482 signal(SIGALRM, print_stats_and_exit);
483 }
Denis Vlasenkoe9356022007-01-29 18:03:54 +0000484 alarm(PINGINTERVAL);
Denis Vlasenko90c31b32008-04-07 00:46:29 +0000485 } else { /* -c NN, and all NN are sent (and no deadline) */
486 /* Wait for the last ping to come back.
487 * -W timeout: wait for a response in seconds.
Denys Vlasenko10ad6222017-04-17 16:13:32 +0200488 * Affects only timeout in absence of any responses,
Denis Vlasenko90c31b32008-04-07 00:46:29 +0000489 * otherwise ping waits for two RTTs. */
490 unsigned expire = timeout;
491
Denys Vlasenkoaf4a07a2013-03-15 00:11:35 +0100492 if (G.nreceived) {
Denis Vlasenko90c31b32008-04-07 00:46:29 +0000493 /* approx. 2*tmax, in seconds (2 RTT) */
494 expire = tmax / (512*1024);
495 if (expire == 0)
496 expire = 1;
497 }
498 signal(SIGALRM, print_stats_and_exit);
499 alarm(expire);
Denis Vlasenkoe9356022007-01-29 18:03:54 +0000500 }
501}
502
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000503static void sendping4(int junk UNUSED_PARAM)
Eric Andersen485b9551999-12-07 23:14:59 +0000504{
Denys Vlasenko9341fd22010-03-03 01:10:29 +0100505 struct icmp *pkt = G.snd_packet;
Eric Andersen485b9551999-12-07 23:14:59 +0000506
Florian Fainelli6ff05512014-08-27 16:01:25 +0200507 memset(pkt, G.pattern, datalen + ICMP_MINLEN + 4);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000508 pkt->icmp_type = ICMP_ECHO;
Denis Vlasenkob34266b2008-04-29 12:31:53 +0000509 /*pkt->icmp_code = 0;*/
Denys Vlasenko9341fd22010-03-03 01:10:29 +0100510 pkt->icmp_cksum = 0; /* cksum is calculated with this field set to 0 */
Denys Vlasenkoaf4a07a2013-03-15 00:11:35 +0100511 pkt->icmp_seq = htons(G.ntransmitted); /* don't ++ here, it can be a macro */
Erik Andersene49d5ec2000-02-08 19:58:47 +0000512 pkt->icmp_id = myid;
Denis Vlasenko7b72fc12007-06-16 13:37:59 +0000513
Denys Vlasenko9341fd22010-03-03 01:10:29 +0100514 /* If datalen < 4, we store timestamp _past_ the packet,
515 * but it's ok - we allocated 4 extra bytes in xzalloc() just in case.
516 */
Denis Vlasenko76791452007-06-18 08:55:57 +0000517 /*if (datalen >= 4)*/
Denys Vlasenko9341fd22010-03-03 01:10:29 +0100518 /* No hton: we'll read it back on the same machine */
Denis Vlasenko76791452007-06-18 08:55:57 +0000519 *(uint32_t*)&pkt->icmp_dun = monotonic_us();
Denis Vlasenko7b72fc12007-06-16 13:37:59 +0000520
Baruch Siache8f36332011-09-07 17:52:37 +0200521 pkt->icmp_cksum = inet_cksum((uint16_t *) pkt, datalen + ICMP_MINLEN);
Eric Andersen485b9551999-12-07 23:14:59 +0000522
Denys Vlasenko281e7b82011-02-06 17:51:45 +0100523 sendping_tail(sendping4, ICMP_MINLEN);
Eric Andersen485b9551999-12-07 23:14:59 +0000524}
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000525#if ENABLE_PING6
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000526static void sendping6(int junk UNUSED_PARAM)
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000527{
Denys Vlasenko1bb52a92011-02-05 03:58:43 +0100528 struct icmp6_hdr *pkt = G.snd_packet;
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000529
Florian Fainelli6ff05512014-08-27 16:01:25 +0200530 memset(pkt, G.pattern, datalen + sizeof(struct icmp6_hdr) + 4);
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000531 pkt->icmp6_type = ICMP6_ECHO_REQUEST;
Denis Vlasenkob34266b2008-04-29 12:31:53 +0000532 /*pkt->icmp6_code = 0;*/
533 /*pkt->icmp6_cksum = 0;*/
Denys Vlasenkoaf4a07a2013-03-15 00:11:35 +0100534 pkt->icmp6_seq = htons(G.ntransmitted); /* don't ++ here, it can be a macro */
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000535 pkt->icmp6_id = myid;
Denis Vlasenko7b72fc12007-06-16 13:37:59 +0000536
Denis Vlasenko76791452007-06-18 08:55:57 +0000537 /*if (datalen >= 4)*/
Denys Vlasenko5117eff2013-10-16 14:21:20 +0200538 *(bb__aliased_uint32_t*)(&pkt->icmp6_data8[4]) = monotonic_us();
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000539
Baruch Siache8f36332011-09-07 17:52:37 +0200540 //TODO? pkt->icmp_cksum = inet_cksum(...);
Denys Vlasenko9341fd22010-03-03 01:10:29 +0100541
Denys Vlasenko281e7b82011-02-06 17:51:45 +0100542 sendping_tail(sendping6, sizeof(struct icmp6_hdr));
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000543}
544#endif
Erik Andersene49d5ec2000-02-08 19:58:47 +0000545
Denis Vlasenko89ef65f2007-01-29 23:43:18 +0000546static const char *icmp_type_name(int id)
Erik Andersen227a59b2000-04-25 23:24:55 +0000547{
548 switch (id) {
Denis Vlasenko35d4da02007-01-22 14:04:27 +0000549 case ICMP_ECHOREPLY: return "Echo Reply";
550 case ICMP_DEST_UNREACH: return "Destination Unreachable";
551 case ICMP_SOURCE_QUENCH: return "Source Quench";
552 case ICMP_REDIRECT: return "Redirect (change route)";
553 case ICMP_ECHO: return "Echo Request";
554 case ICMP_TIME_EXCEEDED: return "Time Exceeded";
555 case ICMP_PARAMETERPROB: return "Parameter Problem";
556 case ICMP_TIMESTAMP: return "Timestamp Request";
557 case ICMP_TIMESTAMPREPLY: return "Timestamp Reply";
558 case ICMP_INFO_REQUEST: return "Information Request";
559 case ICMP_INFO_REPLY: return "Information Reply";
560 case ICMP_ADDRESS: return "Address Mask Request";
561 case ICMP_ADDRESSREPLY: return "Address Mask Reply";
562 default: return "unknown ICMP type";
Erik Andersen227a59b2000-04-25 23:24:55 +0000563 }
564}
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000565#if ENABLE_PING6
566/* RFC3542 changed some definitions from RFC2292 for no good reason, whee!
567 * the newer 3542 uses a MLD_ prefix where as 2292 uses ICMP6_ prefix */
568#ifndef MLD_LISTENER_QUERY
569# define MLD_LISTENER_QUERY ICMP6_MEMBERSHIP_QUERY
570#endif
571#ifndef MLD_LISTENER_REPORT
572# define MLD_LISTENER_REPORT ICMP6_MEMBERSHIP_REPORT
573#endif
574#ifndef MLD_LISTENER_REDUCTION
575# define MLD_LISTENER_REDUCTION ICMP6_MEMBERSHIP_REDUCTION
576#endif
Denis Vlasenko89ef65f2007-01-29 23:43:18 +0000577static const char *icmp6_type_name(int id)
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000578{
579 switch (id) {
580 case ICMP6_DST_UNREACH: return "Destination Unreachable";
581 case ICMP6_PACKET_TOO_BIG: return "Packet too big";
582 case ICMP6_TIME_EXCEEDED: return "Time Exceeded";
583 case ICMP6_PARAM_PROB: return "Parameter Problem";
584 case ICMP6_ECHO_REPLY: return "Echo Reply";
585 case ICMP6_ECHO_REQUEST: return "Echo Request";
586 case MLD_LISTENER_QUERY: return "Listener Query";
587 case MLD_LISTENER_REPORT: return "Listener Report";
588 case MLD_LISTENER_REDUCTION: return "Listener Reduction";
589 default: return "unknown ICMP type";
590 }
591}
592#endif
Erik Andersen227a59b2000-04-25 23:24:55 +0000593
Denis Vlasenko76791452007-06-18 08:55:57 +0000594static void unpack_tail(int sz, uint32_t *tp,
Denis Vlasenko19c238b2007-03-03 00:36:35 +0000595 const char *from_str,
596 uint16_t recv_seq, int ttl)
597{
Denys Vlasenko26a7e2e2013-06-28 01:33:47 +0200598 unsigned char *b, m;
Denis Vlasenko19c238b2007-03-03 00:36:35 +0000599 const char *dupmsg = " (DUP!)";
600 unsigned triptime = triptime; /* for gcc */
601
Denis Vlasenko19c238b2007-03-03 00:36:35 +0000602 if (tp) {
Denis Vlasenko76791452007-06-18 08:55:57 +0000603 /* (int32_t) cast is for hypothetical 64-bit unsigned */
604 /* (doesn't hurt 32-bit real-world anyway) */
605 triptime = (int32_t) ((uint32_t)monotonic_us() - *tp);
Denis Vlasenko19c238b2007-03-03 00:36:35 +0000606 tsum += triptime;
607 if (triptime < tmin)
608 tmin = triptime;
609 if (triptime > tmax)
610 tmax = triptime;
611 }
612
Denys Vlasenko26a7e2e2013-06-28 01:33:47 +0200613 b = &BYTE(recv_seq % MAX_DUP_CHK);
614 m = MASK(recv_seq % MAX_DUP_CHK);
615 /*if TST(recv_seq % MAX_DUP_CHK):*/
616 if (*b & m) {
Denys Vlasenkoaf4a07a2013-03-15 00:11:35 +0100617 ++G.nrepeats;
Denis Vlasenko19c238b2007-03-03 00:36:35 +0000618 } else {
Denys Vlasenko26a7e2e2013-06-28 01:33:47 +0200619 /*SET(recv_seq % MAX_DUP_CHK):*/
620 *b |= m;
621 ++G.nreceived;
Denis Vlasenko19c238b2007-03-03 00:36:35 +0000622 dupmsg += 7;
623 }
624
625 if (option_mask32 & OPT_QUIET)
626 return;
627
628 printf("%d bytes from %s: seq=%u ttl=%d", sz,
629 from_str, recv_seq, ttl);
630 if (tp)
Denis Vlasenko76791452007-06-18 08:55:57 +0000631 printf(" time=%u.%03u ms", triptime / 1000, triptime % 1000);
Denis Vlasenko19c238b2007-03-03 00:36:35 +0000632 puts(dupmsg);
Denys Vlasenko8131eea2009-11-02 14:19:51 +0100633 fflush_all();
Denis Vlasenko19c238b2007-03-03 00:36:35 +0000634}
Denis Vlasenkoe9356022007-01-29 18:03:54 +0000635static void unpack4(char *buf, int sz, struct sockaddr_in *from)
Eric Andersen485b9551999-12-07 23:14:59 +0000636{
Erik Andersene49d5ec2000-02-08 19:58:47 +0000637 struct icmp *icmppkt;
638 struct iphdr *iphdr;
Denis Vlasenko19c238b2007-03-03 00:36:35 +0000639 int hlen;
Eric Andersen485b9551999-12-07 23:14:59 +0000640
Erik Andersene49d5ec2000-02-08 19:58:47 +0000641 /* discard if too short */
Pavel Roskin0024abc2000-06-07 20:38:15 +0000642 if (sz < (datalen + ICMP_MINLEN))
Erik Andersene49d5ec2000-02-08 19:58:47 +0000643 return;
Eric Andersen485b9551999-12-07 23:14:59 +0000644
Denis Vlasenko2cbe6e62006-09-02 16:17:30 +0000645 /* check IP header */
646 iphdr = (struct iphdr *) buf;
647 hlen = iphdr->ihl << 2;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000648 sz -= hlen;
649 icmppkt = (struct icmp *) (buf + hlen);
Erik Andersen227a59b2000-04-25 23:24:55 +0000650 if (icmppkt->icmp_id != myid)
Denis Vlasenkocb6874c2006-09-02 16:13:36 +0000651 return; /* not our ping */
Erik Andersen227a59b2000-04-25 23:24:55 +0000652
Erik Andersene49d5ec2000-02-08 19:58:47 +0000653 if (icmppkt->icmp_type == ICMP_ECHOREPLY) {
Denis Vlasenko35d4da02007-01-22 14:04:27 +0000654 uint16_t recv_seq = ntohs(icmppkt->icmp_seq);
Denis Vlasenko76791452007-06-18 08:55:57 +0000655 uint32_t *tp = NULL;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000656
Denis Vlasenko76791452007-06-18 08:55:57 +0000657 if (sz >= ICMP_MINLEN + sizeof(uint32_t))
658 tp = (uint32_t *) icmppkt->icmp_data;
Denis Vlasenko19c238b2007-03-03 00:36:35 +0000659 unpack_tail(sz, tp,
660 inet_ntoa(*(struct in_addr *) &from->sin_addr.s_addr),
661 recv_seq, iphdr->ttl);
662 } else if (icmppkt->icmp_type != ICMP_ECHO) {
663 bb_error_msg("warning: got ICMP %d (%s)",
664 icmppkt->icmp_type,
665 icmp_type_name(icmppkt->icmp_type));
Denis Vlasenko35d4da02007-01-22 14:04:27 +0000666 }
Eric Andersen485b9551999-12-07 23:14:59 +0000667}
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000668#if ENABLE_PING6
Denys Vlasenko5126cf92011-09-11 20:27:28 +0200669static void unpack6(char *packet, int sz, struct sockaddr_in6 *from, int hoplimit)
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000670{
671 struct icmp6_hdr *icmppkt;
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000672 char buf[INET6_ADDRSTRLEN];
Eric Andersen485b9551999-12-07 23:14:59 +0000673
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000674 /* discard if too short */
675 if (sz < (datalen + sizeof(struct icmp6_hdr)))
676 return;
677
678 icmppkt = (struct icmp6_hdr *) packet;
679 if (icmppkt->icmp6_id != myid)
680 return; /* not our ping */
681
682 if (icmppkt->icmp6_type == ICMP6_ECHO_REPLY) {
683 uint16_t recv_seq = ntohs(icmppkt->icmp6_seq);
Denis Vlasenko76791452007-06-18 08:55:57 +0000684 uint32_t *tp = NULL;
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000685
Denis Vlasenko76791452007-06-18 08:55:57 +0000686 if (sz >= sizeof(struct icmp6_hdr) + sizeof(uint32_t))
687 tp = (uint32_t *) &icmppkt->icmp6_data8[4];
Denis Vlasenko19c238b2007-03-03 00:36:35 +0000688 unpack_tail(sz, tp,
Denys Vlasenko5126cf92011-09-11 20:27:28 +0200689 inet_ntop(AF_INET6, &from->sin6_addr,
Denis Vlasenko19c238b2007-03-03 00:36:35 +0000690 buf, sizeof(buf)),
691 recv_seq, hoplimit);
692 } else if (icmppkt->icmp6_type != ICMP6_ECHO_REQUEST) {
693 bb_error_msg("warning: got ICMP %d (%s)",
694 icmppkt->icmp6_type,
695 icmp6_type_name(icmppkt->icmp6_type));
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000696 }
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000697}
698#endif
699
Denis Vlasenkoe9356022007-01-29 18:03:54 +0000700static void ping4(len_and_sockaddr *lsa)
Eric Andersen485b9551999-12-07 23:14:59 +0000701{
Erik Andersene49d5ec2000-02-08 19:58:47 +0000702 int sockopt;
703
Bernhard Reutner-Fischer8c69afd2008-01-29 10:33:34 +0000704 pingaddr.sin = lsa->u.sin;
Denis Vlasenko5ad10482007-06-19 22:54:21 +0000705 if (source_lsa) {
706 if (setsockopt(pingsock, IPPROTO_IP, IP_MULTICAST_IF,
Bernhard Reutner-Fischer8c69afd2008-01-29 10:33:34 +0000707 &source_lsa->u.sa, source_lsa->len))
Denis Vlasenko5ad10482007-06-19 22:54:21 +0000708 bb_error_msg_and_die("can't set multicast source interface");
Bernhard Reutner-Fischer8c69afd2008-01-29 10:33:34 +0000709 xbind(pingsock, &source_lsa->u.sa, source_lsa->len);
Denis Vlasenko5ad10482007-06-19 22:54:21 +0000710 }
Denis Vlasenko2cbe6e62006-09-02 16:17:30 +0000711
Erik Andersene49d5ec2000-02-08 19:58:47 +0000712 /* enable broadcast pings */
Denis Vlasenko48237b02006-11-22 23:22:06 +0000713 setsockopt_broadcast(pingsock);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000714
Denis Vlasenko57707152008-08-24 00:02:18 +0000715 /* set recv buf (needed if we can get lots of responses: flood ping,
716 * broadcast ping etc) */
717 sockopt = (datalen * 2) + 7 * 1024; /* giving it a bit of extra room */
Denys Vlasenkoc52cbea2015-08-24 19:48:03 +0200718 setsockopt_SOL_SOCKET_int(pingsock, SO_RCVBUF, sockopt);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000719
Denys Vlasenko3c8799b2010-11-29 12:07:12 +0100720 if (opt_ttl != 0) {
Denys Vlasenkoc52cbea2015-08-24 19:48:03 +0200721 setsockopt_int(pingsock, IPPROTO_IP, IP_TTL, opt_ttl);
Denys Vlasenko10ad6222017-04-17 16:13:32 +0200722 /* above doesn't affect packets sent to bcast IP, so... */
Denys Vlasenkoc52cbea2015-08-24 19:48:03 +0200723 setsockopt_int(pingsock, IPPROTO_IP, IP_MULTICAST_TTL, opt_ttl);
Denys Vlasenko3c8799b2010-11-29 12:07:12 +0100724 }
Joachim Nilsson714e2b72010-11-28 23:01:18 +0100725
Denis Vlasenko90c31b32008-04-07 00:46:29 +0000726 signal(SIGINT, print_stats_and_exit);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000727
728 /* start the ping's going ... */
Denis Vlasenkoe9356022007-01-29 18:03:54 +0000729 sendping4(0);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000730
731 /* listen for replies */
732 while (1) {
733 struct sockaddr_in from;
Erik Andersen1d1d9502000-04-21 01:26:49 +0000734 socklen_t fromlen = (socklen_t) sizeof(from);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000735 int c;
736
Denys Vlasenko9341fd22010-03-03 01:10:29 +0100737 c = recvfrom(pingsock, G.rcv_packet, G.sizeof_rcv_packet, 0,
Denis Vlasenko44c2eb22007-01-08 23:55:33 +0000738 (struct sockaddr *) &from, &fromlen);
739 if (c < 0) {
740 if (errno != EINTR)
741 bb_perror_msg("recvfrom");
Erik Andersene49d5ec2000-02-08 19:58:47 +0000742 continue;
743 }
Denys Vlasenko9341fd22010-03-03 01:10:29 +0100744 unpack4(G.rcv_packet, c, &from);
Denys Vlasenkoaf4a07a2013-03-15 00:11:35 +0100745 if (pingcount && G.nreceived >= pingcount)
Erik Andersene49d5ec2000-02-08 19:58:47 +0000746 break;
747 }
Eric Andersen485b9551999-12-07 23:14:59 +0000748}
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000749#if ENABLE_PING6
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000750static void ping6(len_and_sockaddr *lsa)
751{
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000752 int sockopt;
753 struct msghdr msg;
754 struct sockaddr_in6 from;
755 struct iovec iov;
756 char control_buf[CMSG_SPACE(36)];
757
Bernhard Reutner-Fischer8c69afd2008-01-29 10:33:34 +0000758 pingaddr.sin6 = lsa->u.sin6;
Denis Vlasenko9ca26d32007-02-09 17:32:16 +0000759 if (source_lsa)
Bernhard Reutner-Fischer8c69afd2008-01-29 10:33:34 +0000760 xbind(pingsock, &source_lsa->u.sa, source_lsa->len);
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000761
762#ifdef ICMP6_FILTER
763 {
764 struct icmp6_filter filt;
765 if (!(option_mask32 & OPT_VERBOSE)) {
766 ICMP6_FILTER_SETBLOCKALL(&filt);
767 ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &filt);
768 } else {
769 ICMP6_FILTER_SETPASSALL(&filt);
770 }
771 if (setsockopt(pingsock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt,
Denys Vlasenko69675782013-01-14 01:34:48 +0100772 sizeof(filt)) < 0)
Denys Vlasenko2db782b2015-08-24 19:08:14 +0200773 bb_error_msg_and_die("setsockopt(%s)", "ICMP6_FILTER");
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000774 }
775#endif /*ICMP6_FILTER*/
776
777 /* enable broadcast pings */
778 setsockopt_broadcast(pingsock);
779
Denis Vlasenko57707152008-08-24 00:02:18 +0000780 /* set recv buf (needed if we can get lots of responses: flood ping,
781 * broadcast ping etc) */
782 sockopt = (datalen * 2) + 7 * 1024; /* giving it a bit of extra room */
Denys Vlasenkoc52cbea2015-08-24 19:48:03 +0200783 setsockopt_SOL_SOCKET_int(pingsock, SO_RCVBUF, sockopt);
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000784
785 sockopt = offsetof(struct icmp6_hdr, icmp6_cksum);
Denys Vlasenkoab3964d2015-10-13 14:50:20 +0200786 BUILD_BUG_ON(offsetof(struct icmp6_hdr, icmp6_cksum) != 2);
Denys Vlasenkoc52cbea2015-08-24 19:48:03 +0200787 setsockopt_int(pingsock, SOL_RAW, IPV6_CHECKSUM, sockopt);
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000788
789 /* request ttl info to be returned in ancillary data */
Denys Vlasenkoc52cbea2015-08-24 19:48:03 +0200790 setsockopt_1(pingsock, SOL_IPV6, IPV6_HOPLIMIT);
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000791
792 if (if_index)
Denis Vlasenkoaeb4bdd2007-01-25 00:00:02 +0000793 pingaddr.sin6.sin6_scope_id = if_index;
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000794
Denis Vlasenko90c31b32008-04-07 00:46:29 +0000795 signal(SIGINT, print_stats_and_exit);
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000796
797 /* start the ping's going ... */
798 sendping6(0);
799
800 /* listen for replies */
801 msg.msg_name = &from;
802 msg.msg_namelen = sizeof(from);
803 msg.msg_iov = &iov;
804 msg.msg_iovlen = 1;
805 msg.msg_control = control_buf;
Denys Vlasenko9341fd22010-03-03 01:10:29 +0100806 iov.iov_base = G.rcv_packet;
807 iov.iov_len = G.sizeof_rcv_packet;
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000808 while (1) {
809 int c;
810 struct cmsghdr *mp;
811 int hoplimit = -1;
812 msg.msg_controllen = sizeof(control_buf);
813
814 c = recvmsg(pingsock, &msg, 0);
815 if (c < 0) {
816 if (errno != EINTR)
817 bb_perror_msg("recvfrom");
818 continue;
819 }
820 for (mp = CMSG_FIRSTHDR(&msg); mp; mp = CMSG_NXTHDR(&msg, mp)) {
821 if (mp->cmsg_level == SOL_IPV6
822 && mp->cmsg_type == IPV6_HOPLIMIT
823 /* don't check len - we trust the kernel: */
824 /* && mp->cmsg_len >= CMSG_LEN(sizeof(int)) */
825 ) {
Denys Vlasenko57be1ee2009-11-26 15:26:31 +0100826 /*hoplimit = *(int*)CMSG_DATA(mp); - unaligned access */
827 move_from_unaligned_int(hoplimit, CMSG_DATA(mp));
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000828 }
829 }
Denys Vlasenko5126cf92011-09-11 20:27:28 +0200830 unpack6(G.rcv_packet, c, &from, hoplimit);
Denys Vlasenkoaf4a07a2013-03-15 00:11:35 +0100831 if (pingcount && G.nreceived >= pingcount)
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000832 break;
833 }
834}
835#endif
Eric Andersen485b9551999-12-07 23:14:59 +0000836
Denis Vlasenko9ca26d32007-02-09 17:32:16 +0000837static void ping(len_and_sockaddr *lsa)
Denis Vlasenko2cbe6e62006-09-02 16:17:30 +0000838{
Denis Vlasenko9ca26d32007-02-09 17:32:16 +0000839 printf("PING %s (%s)", hostname, dotted);
840 if (source_lsa) {
841 printf(" from %s",
Bernhard Reutner-Fischer8c69afd2008-01-29 10:33:34 +0000842 xmalloc_sockaddr2dotted_noport(&source_lsa->u.sa));
Denis Vlasenko2cbe6e62006-09-02 16:17:30 +0000843 }
Denis Vlasenko9ca26d32007-02-09 17:32:16 +0000844 printf(": %d data bytes\n", datalen);
845
Denys Vlasenko59f46672013-06-28 00:30:46 +0200846 create_icmp_socket(lsa);
847 /* untested whether "-I addr" really works for IPv6: */
848 if (str_I)
849 setsockopt_bindtodevice(pingsock, str_I);
850
Denys Vlasenko9341fd22010-03-03 01:10:29 +0100851 G.sizeof_rcv_packet = datalen + MAXIPLEN + MAXICMPLEN;
852 G.rcv_packet = xzalloc(G.sizeof_rcv_packet);
Denis Vlasenko9ca26d32007-02-09 17:32:16 +0000853#if ENABLE_PING6
Denys Vlasenko9341fd22010-03-03 01:10:29 +0100854 if (lsa->u.sa.sa_family == AF_INET6) {
855 /* +4 reserves a place for timestamp, which may end up sitting
856 * _after_ packet. Saves one if() - see sendping4/6() */
857 G.snd_packet = xzalloc(datalen + sizeof(struct icmp6_hdr) + 4);
Denis Vlasenko9ca26d32007-02-09 17:32:16 +0000858 ping6(lsa);
Denys Vlasenko9341fd22010-03-03 01:10:29 +0100859 } else
Denis Vlasenko9ca26d32007-02-09 17:32:16 +0000860#endif
Denys Vlasenko9341fd22010-03-03 01:10:29 +0100861 {
862 G.snd_packet = xzalloc(datalen + ICMP_MINLEN + 4);
Denis Vlasenko9ca26d32007-02-09 17:32:16 +0000863 ping4(lsa);
Denys Vlasenko9341fd22010-03-03 01:10:29 +0100864 }
Denis Vlasenko2cbe6e62006-09-02 16:17:30 +0000865}
866
Denys Vlasenkoad7d94b2009-11-20 18:12:12 +0100867static int common_ping_main(int opt, char **argv)
Eric Andersen485b9551999-12-07 23:14:59 +0000868{
Denis Vlasenkoaeb4bdd2007-01-25 00:00:02 +0000869 len_and_sockaddr *lsa;
Florian Fainelli6ff05512014-08-27 16:01:25 +0200870 char *str_s, *str_p;
Eric Andersen485b9551999-12-07 23:14:59 +0000871
Denis Vlasenko821cc252007-06-04 10:33:48 +0000872 INIT_G();
873
Denys Vlasenko22542ec2017-08-08 21:55:02 +0200874 opt |= getopt32(argv, "^"
875 OPT_STRING
876 /* exactly one arg; -v and -q don't mix */
877 "\0" "=1:q--v:v--q",
878 &pingcount, &str_s, &opt_ttl, &deadline, &timeout, &str_I, &str_p
879 );
Denis Vlasenko59f502b2008-10-27 11:54:45 +0000880 if (opt & OPT_s)
881 datalen = xatou16(str_s); // -s
882 if (opt & OPT_I) { // -I
883 if_index = if_nametoindex(str_I);
Denis Vlasenko9ca26d32007-02-09 17:32:16 +0000884 if (!if_index) {
885 /* TODO: I'm not sure it takes IPv6 unless in [XX:XX..] format */
Denis Vlasenko59f502b2008-10-27 11:54:45 +0000886 source_lsa = xdotted2sockaddr(str_I, 0);
887 str_I = NULL; /* don't try to bind to device later */
Denis Vlasenko9ca26d32007-02-09 17:32:16 +0000888 }
Erik Andersene49d5ec2000-02-08 19:58:47 +0000889 }
Florian Fainelli6ff05512014-08-27 16:01:25 +0200890 if (opt & OPT_p)
891 G.pattern = xstrtou_range(str_p, 16, 0, 255);
892
Denis Vlasenko1c9ad622007-05-27 00:53:41 +0000893 myid = (uint16_t) getpid();
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000894 hostname = argv[optind];
895#if ENABLE_PING6
Denis Vlasenko59f502b2008-10-27 11:54:45 +0000896 {
897 sa_family_t af = AF_UNSPEC;
898 if (opt & OPT_IPV4)
899 af = AF_INET;
900 if (opt & OPT_IPV6)
901 af = AF_INET6;
902 lsa = xhost_and_af2sockaddr(hostname, 0, af);
903 }
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000904#else
Denis Vlasenko42823d52007-02-04 02:39:08 +0000905 lsa = xhost_and_af2sockaddr(hostname, 0, AF_INET);
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000906#endif
Denis Vlasenko9ca26d32007-02-09 17:32:16 +0000907
Bernhard Reutner-Fischer8c69afd2008-01-29 10:33:34 +0000908 if (source_lsa && source_lsa->u.sa.sa_family != lsa->u.sa.sa_family)
Denis Vlasenko9ca26d32007-02-09 17:32:16 +0000909 /* leaking it here... */
910 source_lsa = NULL;
911
Bernhard Reutner-Fischer8c69afd2008-01-29 10:33:34 +0000912 dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
Denis Vlasenko9ca26d32007-02-09 17:32:16 +0000913 ping(lsa);
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +0000914 print_stats_and_exit(EXIT_SUCCESS);
Denis Vlasenko90c31b32008-04-07 00:46:29 +0000915 /*return EXIT_SUCCESS;*/
Eric Andersen485b9551999-12-07 23:14:59 +0000916}
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000917#endif /* FEATURE_FANCY_PING */
918
919
Denys Vlasenko83423972016-11-23 09:25:57 +0100920#if ENABLE_PING
Denys Vlasenkoad7d94b2009-11-20 18:12:12 +0100921int ping_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
922int ping_main(int argc UNUSED_PARAM, char **argv)
923{
Denys Vlasenko83423972016-11-23 09:25:57 +0100924# if !ENABLE_FEATURE_FANCY_PING
Denys Vlasenkoad7d94b2009-11-20 18:12:12 +0100925 return common_ping_main(AF_UNSPEC, argv);
Denys Vlasenko83423972016-11-23 09:25:57 +0100926# else
Denys Vlasenkoad7d94b2009-11-20 18:12:12 +0100927 return common_ping_main(0, argv);
Denys Vlasenko83423972016-11-23 09:25:57 +0100928# endif
Denys Vlasenkoad7d94b2009-11-20 18:12:12 +0100929}
Denys Vlasenko83423972016-11-23 09:25:57 +0100930#endif
Denys Vlasenkoad7d94b2009-11-20 18:12:12 +0100931
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000932#if ENABLE_PING6
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000933int ping6_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenko59f502b2008-10-27 11:54:45 +0000934int ping6_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000935{
Denys Vlasenkoad7d94b2009-11-20 18:12:12 +0100936# if !ENABLE_FEATURE_FANCY_PING
937 return common_ping_main(AF_INET6, argv);
938# else
939 return common_ping_main(OPT_IPV6, argv);
940# endif
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000941}
942#endif
943
944/* from ping6.c:
945 * Copyright (c) 1989 The Regents of the University of California.
946 * All rights reserved.
947 *
948 * This code is derived from software contributed to Berkeley by
949 * Mike Muuss.
950 *
951 * Redistribution and use in source and binary forms, with or without
952 * modification, are permitted provided that the following conditions
953 * are met:
954 * 1. Redistributions of source code must retain the above copyright
955 * notice, this list of conditions and the following disclaimer.
956 * 2. Redistributions in binary form must reproduce the above copyright
957 * notice, this list of conditions and the following disclaimer in the
958 * documentation and/or other materials provided with the distribution.
959 *
960 * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
961 * ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
962 *
963 * 4. Neither the name of the University nor the names of its contributors
964 * may be used to endorse or promote products derived from this software
965 * without specific prior written permission.
966 *
Denys Vlasenko95f79532017-08-02 14:26:33 +0200967 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND
Denis Vlasenkob9a279b2007-01-24 23:53:22 +0000968 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
969 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
970 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
971 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
972 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
973 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
974 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
975 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
976 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
977 * SUCH DAMAGE.
978 */